summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Travis <travis@sgi.com>2008-12-31 17:34:16 -0800
committerIngo Molnar <mingo@elte.hu>2009-01-03 18:53:31 +0100
commit7eb19553369c46cc1fa64caf120cbcab1b597f7c (patch)
treeef1a3beae706b9497c845d0a2557ceb4d2754998
parent6092848a2a23b660150a38bc06f59d75838d70c8 (diff)
parent8c384cdee3e04d6194a2c2b192b624754f990835 (diff)
downloadlinux-stable-7eb19553369c46cc1fa64caf120cbcab1b597f7c.tar.gz
linux-stable-7eb19553369c46cc1fa64caf120cbcab1b597f7c.tar.bz2
linux-stable-7eb19553369c46cc1fa64caf120cbcab1b597f7c.zip
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux-2.6-cpumask into merge-rr-cpumask
Conflicts: arch/x86/kernel/io_apic.c kernel/rcuclassic.c kernel/sched.c kernel/time/tick-sched.c Signed-off-by: Mike Travis <travis@sgi.com> [ mingo@elte.hu: backmerged typo fix for io_apic.c ] Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--Documentation/DocBook/Makefile2
-rw-r--r--Documentation/DocBook/networking.tmpl3
-rw-r--r--Documentation/DocBook/wanbook.tmpl99
-rw-r--r--Documentation/RCU/00-INDEX2
-rw-r--r--Documentation/RCU/rculist_nulls.txt167
-rw-r--r--Documentation/RCU/trace.txt413
-rw-r--r--Documentation/arm/pxa/mfp.txt286
-rw-r--r--Documentation/block/biodoc.txt6
-rw-r--r--Documentation/cpu-freq/user-guide.txt6
-rw-r--r--Documentation/cputopology.txt48
-rw-r--r--Documentation/credentials.txt582
-rw-r--r--Documentation/dvb/technisat.txt69
-rw-r--r--Documentation/fb/pxafb.txt92
-rw-r--r--Documentation/feature-removal-schedule.txt19
-rw-r--r--Documentation/filesystems/proc.txt9
-rw-r--r--Documentation/filesystems/xfs.txt4
-rw-r--r--Documentation/kbuild/makefiles.txt14
-rw-r--r--Documentation/kernel-parameters.txt63
-rw-r--r--Documentation/lguest/lguest.c66
-rw-r--r--Documentation/networking/README.ipw22002
-rw-r--r--Documentation/networking/bonding.txt68
-rw-r--r--Documentation/networking/dccp.txt32
-rw-r--r--Documentation/networking/driver.txt2
-rw-r--r--Documentation/networking/generic-hdlc.txt8
-rw-r--r--Documentation/networking/ip-sysctl.txt6
-rw-r--r--Documentation/networking/mac80211_hwsim/README9
-rw-r--r--Documentation/networking/netdevices.txt2
-rw-r--r--Documentation/networking/regulatory.txt22
-rw-r--r--Documentation/nmi_watchdog.txt5
-rw-r--r--Documentation/powerpc/dts-bindings/fsl/tsec.txt12
-rw-r--r--Documentation/rfkill.txt20
-rw-r--r--Documentation/scheduler/sched-design-CFS.txt21
-rw-r--r--Documentation/scsi/cxgb3i.txt85
-rw-r--r--Documentation/sh/kgdb.txt179
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt328
-rw-r--r--Documentation/sound/alsa/HD-Audio-Models.txt348
-rw-r--r--Documentation/sound/alsa/HD-Audio.txt577
-rw-r--r--Documentation/sound/alsa/Procfile.txt10
-rw-r--r--Documentation/sound/alsa/soc/machine.txt8
-rw-r--r--Documentation/video4linux/API.html43
-rw-r--r--Documentation/video4linux/CARDLIST.bttv7
-rw-r--r--Documentation/video4linux/CARDLIST.cx238851
-rw-r--r--Documentation/video4linux/CARDLIST.cx885
-rw-r--r--Documentation/video4linux/CARDLIST.em28xx9
-rw-r--r--Documentation/video4linux/CARDLIST.saa71343
-rw-r--r--Documentation/video4linux/README.cx888
-rw-r--r--Documentation/video4linux/gspca.txt19
-rw-r--r--Documentation/video4linux/v4l2-framework.txt520
-rw-r--r--Documentation/x86/boot.txt6
-rw-r--r--Documentation/x86/pat.txt24
-rw-r--r--Documentation/x86/x86_64/boot-options.txt11
-rw-r--r--Documentation/x86/x86_64/mm.txt2
-rw-r--r--MAINTAINERS63
-rw-r--r--Makefile218
-rw-r--r--arch/Kconfig2
-rw-r--r--arch/alpha/include/asm/io.h3
-rw-r--r--arch/alpha/include/asm/topology.h17
-rw-r--r--arch/alpha/kernel/asm-offsets.c11
-rw-r--r--arch/alpha/kernel/entry.S10
-rw-r--r--arch/alpha/kernel/irq.c3
-rw-r--r--arch/alpha/kernel/setup.c5
-rw-r--r--arch/arm/Kconfig71
-rw-r--r--arch/arm/Makefile7
-rw-r--r--arch/arm/boot/compressed/Makefile4
-rw-r--r--arch/arm/boot/compressed/head-clps7500.S86
-rw-r--r--arch/arm/boot/compressed/head.S20
-rw-r--r--arch/arm/boot/compressed/misc.c2
-rw-r--r--arch/arm/common/Kconfig3
-rw-r--r--arch/arm/common/Makefile1
-rw-r--r--arch/arm/common/clkdev.c128
-rw-r--r--arch/arm/common/locomo.c1
-rw-r--r--arch/arm/common/vic.c8
-rw-r--r--arch/arm/configs/eseries_pxa_defconfig326
-rw-r--r--arch/arm/configs/h5000_defconfig996
-rw-r--r--arch/arm/configs/kirkwood_defconfig329
-rw-r--r--arch/arm/configs/ks8695_defconfig707
-rw-r--r--arch/arm/configs/mx31moboard_defconfig790
-rw-r--r--arch/arm/configs/mx31pdk_defconfig773
-rw-r--r--arch/arm/configs/neocore926_defconfig1302
-rw-r--r--arch/arm/configs/netx_defconfig6
-rw-r--r--arch/arm/configs/omap3_pandora_defconfig1409
-rw-r--r--arch/arm/configs/omap_ldp_defconfig148
-rw-r--r--arch/arm/configs/picotux200_defconfig6
-rw-r--r--arch/arm/configs/realview-smp_defconfig718
-rw-r--r--arch/arm/configs/realview_defconfig763
-rw-r--r--arch/arm/configs/s3c6400_defconfig845
-rw-r--r--arch/arm/configs/w90p910_defconfig626
-rw-r--r--arch/arm/include/asm/cacheflush.h37
-rw-r--r--arch/arm/include/asm/clkdev.h30
-rw-r--r--arch/arm/include/asm/dma-mapping.h4
-rw-r--r--arch/arm/include/asm/dma.h24
-rw-r--r--arch/arm/include/asm/hardware/iomd.h41
-rw-r--r--arch/arm/include/asm/hardware/vic.h10
-rw-r--r--arch/arm/include/asm/hwcap.h1
-rw-r--r--arch/arm/include/asm/io.h8
-rw-r--r--arch/arm/include/asm/irq.h4
-rw-r--r--arch/arm/include/asm/memory.h7
-rw-r--r--arch/arm/include/asm/mmu_context.h1
-rw-r--r--arch/arm/include/asm/mtd-xip.h1
-rw-r--r--arch/arm/include/asm/page.h32
-rw-r--r--arch/arm/include/asm/processor.h2
-rw-r--r--arch/arm/include/asm/setup.h6
-rw-r--r--arch/arm/include/asm/smp.h6
-rw-r--r--arch/arm/include/asm/string.h9
-rw-r--r--arch/arm/include/asm/system.h2
-rw-r--r--arch/arm/include/asm/uaccess.h5
-rw-r--r--arch/arm/kernel/armksyms.c1
-rw-r--r--arch/arm/kernel/ftrace.c2
-rw-r--r--arch/arm/kernel/head-common.S2
-rw-r--r--arch/arm/kernel/module.c4
-rw-r--r--arch/arm/kernel/setup.c57
-rw-r--r--arch/arm/kernel/smp.c4
-rw-r--r--arch/arm/kernel/thumbee.c2
-rw-r--r--arch/arm/kernel/vmlinux.lds.S2
-rw-r--r--arch/arm/lib/Makefile1
-rw-r--r--arch/arm/lib/memset.S2
-rw-r--r--arch/arm/mach-aaec2000/Makefile2
-rw-r--r--arch/arm/mach-aaec2000/clock.c99
-rw-r--r--arch/arm/mach-aaec2000/clock.h23
-rw-r--r--arch/arm/mach-aaec2000/core.c29
-rw-r--r--arch/arm/mach-aaec2000/include/mach/dma.h9
-rw-r--r--arch/arm/mach-aaec2000/include/mach/io.h6
-rw-r--r--arch/arm/mach-aaec2000/include/mach/memory.h3
-rw-r--r--arch/arm/mach-at91/Kconfig15
-rw-r--r--arch/arm/mach-at91/Makefile13
-rw-r--r--arch/arm/mach-at91/at91cap9.c8
-rw-r--r--arch/arm/mach-at91/at91cap9_devices.c36
-rw-r--r--arch/arm/mach-at91/at91rm9200_time.c9
-rw-r--r--arch/arm/mach-at91/at91sam9260_devices.c38
-rw-r--r--arch/arm/mach-at91/at91sam9261_devices.c17
-rw-r--r--arch/arm/mach-at91/at91sam9263_devices.c17
-rw-r--r--arch/arm/mach-at91/at91sam9rl_devices.c11
-rw-r--r--arch/arm/mach-at91/board-cam60.c30
-rw-r--r--arch/arm/mach-at91/board-cap9adk.c77
-rw-r--r--arch/arm/mach-at91/board-neocore926.c397
-rw-r--r--arch/arm/mach-at91/board-qil-a9260.c35
-rw-r--r--arch/arm/mach-at91/board-sam9-l9260.c35
-rw-r--r--arch/arm/mach-at91/board-sam9260ek.c37
-rw-r--r--arch/arm/mach-at91/board-sam9261ek.c80
-rw-r--r--arch/arm/mach-at91/board-sam9263ek.c36
-rw-r--r--arch/arm/mach-at91/board-sam9g20ek.c36
-rw-r--r--arch/arm/mach-at91/board-sam9rlek.c32
-rw-r--r--arch/arm/mach-at91/board-usb-a9260.c35
-rw-r--r--arch/arm/mach-at91/board-usb-a9263.c36
-rw-r--r--arch/arm/mach-at91/include/mach/at91_pmc.h7
-rw-r--r--arch/arm/mach-at91/include/mach/at91cap9.h4
-rw-r--r--arch/arm/mach-at91/include/mach/cpu.h15
-rw-r--r--arch/arm/mach-at91/include/mach/dma.h19
-rw-r--r--arch/arm/mach-at91/include/mach/io.h4
-rw-r--r--arch/arm/mach-at91/include/mach/memory.h11
-rw-r--r--arch/arm/mach-at91/sam9_smc.c47
-rw-r--r--arch/arm/mach-at91/sam9_smc.h33
-rw-r--r--arch/arm/mach-clps711x/include/mach/dma.h19
-rw-r--r--arch/arm/mach-clps711x/include/mach/io.h6
-rw-r--r--arch/arm/mach-clps711x/include/mach/memory.h20
-rw-r--r--arch/arm/mach-clps7500/Makefile11
-rw-r--r--arch/arm/mach-clps7500/Makefile.boot2
-rw-r--r--arch/arm/mach-clps7500/core.c395
-rw-r--r--arch/arm/mach-clps7500/include/mach/acornfb.h33
-rw-r--r--arch/arm/mach-clps7500/include/mach/debug-macro.S21
-rw-r--r--arch/arm/mach-clps7500/include/mach/dma.h21
-rw-r--r--arch/arm/mach-clps7500/include/mach/entry-macro.S16
-rw-r--r--arch/arm/mach-clps7500/include/mach/hardware.h67
-rw-r--r--arch/arm/mach-clps7500/include/mach/io.h255
-rw-r--r--arch/arm/mach-clps7500/include/mach/irq.h32
-rw-r--r--arch/arm/mach-clps7500/include/mach/irqs.h66
-rw-r--r--arch/arm/mach-clps7500/include/mach/memory.h43
-rw-r--r--arch/arm/mach-clps7500/include/mach/system.h23
-rw-r--r--arch/arm/mach-clps7500/include/mach/timex.h13
-rw-r--r--arch/arm/mach-clps7500/include/mach/uncompress.h35
-rw-r--r--arch/arm/mach-clps7500/include/mach/vmalloc.h4
-rw-r--r--arch/arm/mach-davinci/include/mach/dma.h16
-rw-r--r--arch/arm/mach-davinci/include/mach/io.h3
-rw-r--r--arch/arm/mach-davinci/include/mach/memory.h7
-rw-r--r--arch/arm/mach-davinci/include/mach/vmalloc.h1
-rw-r--r--arch/arm/mach-ebsa110/include/mach/dma.h11
-rw-r--r--arch/arm/mach-ebsa110/include/mach/memory.h7
-rw-r--r--arch/arm/mach-ep93xx/Kconfig6
-rw-r--r--arch/arm/mach-ep93xx/Makefile1
-rw-r--r--arch/arm/mach-ep93xx/adssphere.c7
-rw-r--r--arch/arm/mach-ep93xx/clock.c68
-rw-r--r--arch/arm/mach-ep93xx/core.c40
-rw-r--r--arch/arm/mach-ep93xx/edb9302.c7
-rw-r--r--arch/arm/mach-ep93xx/edb9302a.c9
-rw-r--r--arch/arm/mach-ep93xx/edb9307.c9
-rw-r--r--arch/arm/mach-ep93xx/edb9307a.c68
-rw-r--r--arch/arm/mach-ep93xx/edb9312.c9
-rw-r--r--arch/arm/mach-ep93xx/edb9315.c9
-rw-r--r--arch/arm/mach-ep93xx/edb9315a.c9
-rw-r--r--arch/arm/mach-ep93xx/gesbc9312.c7
-rw-r--r--arch/arm/mach-ep93xx/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-ep93xx/include/mach/dma.h3
-rw-r--r--arch/arm/mach-ep93xx/include/mach/gpio.h2
-rw-r--r--arch/arm/mach-ep93xx/include/mach/io.h4
-rw-r--r--arch/arm/mach-ep93xx/include/mach/memory.h4
-rw-r--r--arch/arm/mach-ep93xx/include/mach/platform.h1
-rw-r--r--arch/arm/mach-ep93xx/micro9.c89
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.c21
-rw-r--r--arch/arm/mach-footbridge/cats-hw.c1
-rw-r--r--arch/arm/mach-footbridge/common.c1
-rw-r--r--arch/arm/mach-footbridge/dc21285-timer.c1
-rw-r--r--arch/arm/mach-footbridge/dc21285.c1
-rw-r--r--arch/arm/mach-footbridge/dma.c1
-rw-r--r--arch/arm/mach-footbridge/ebsa285.c1
-rw-r--r--arch/arm/mach-footbridge/include/mach/dma.h25
-rw-r--r--arch/arm/mach-footbridge/include/mach/hardware.h14
-rw-r--r--arch/arm/mach-footbridge/include/mach/io.h3
-rw-r--r--arch/arm/mach-footbridge/include/mach/isa-dma.h25
-rw-r--r--arch/arm/mach-footbridge/include/mach/memory.h9
-rw-r--r--arch/arm/mach-footbridge/isa-irq.c1
-rw-r--r--arch/arm/mach-footbridge/netwinder-hw.c54
-rw-r--r--arch/arm/mach-footbridge/netwinder-leds.c7
-rw-r--r--arch/arm/mach-footbridge/personal.c1
-rw-r--r--arch/arm/mach-h720x/include/mach/dma.h26
-rw-r--r--arch/arm/mach-h720x/include/mach/io.h4
-rw-r--r--arch/arm/mach-h720x/include/mach/isa-dma.h19
-rw-r--r--arch/arm/mach-h720x/include/mach/memory.h20
-rw-r--r--arch/arm/mach-imx/dma.c7
-rw-r--r--arch/arm/mach-imx/include/mach/imx-dma.h12
-rw-r--r--arch/arm/mach-imx/include/mach/imxfb.h50
-rw-r--r--arch/arm/mach-imx/include/mach/io.h4
-rw-r--r--arch/arm/mach-imx/include/mach/memory.h10
-rw-r--r--arch/arm/mach-integrator/clock.c80
-rw-r--r--arch/arm/mach-integrator/clock.h25
-rw-r--r--arch/arm/mach-integrator/core.c35
-rw-r--r--arch/arm/mach-integrator/impd1.c26
-rw-r--r--arch/arm/mach-integrator/include/mach/clkdev.h25
-rw-r--r--arch/arm/mach-integrator/include/mach/dma.h19
-rw-r--r--arch/arm/mach-integrator/include/mach/memory.h13
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c18
-rw-r--r--arch/arm/mach-iop13xx/include/mach/dma.h3
-rw-r--r--arch/arm/mach-iop13xx/include/mach/memory.h16
-rw-r--r--arch/arm/mach-iop13xx/include/mach/timex.h2
-rw-r--r--arch/arm/mach-iop32x/include/mach/dma.h9
-rw-r--r--arch/arm/mach-iop32x/include/mach/io.h2
-rw-r--r--arch/arm/mach-iop32x/include/mach/memory.h13
-rw-r--r--arch/arm/mach-iop32x/include/mach/system.h3
-rw-r--r--arch/arm/mach-iop32x/include/mach/timex.h3
-rw-r--r--arch/arm/mach-iop33x/include/mach/dma.h9
-rw-r--r--arch/arm/mach-iop33x/include/mach/io.h2
-rw-r--r--arch/arm/mach-iop33x/include/mach/memory.h13
-rw-r--r--arch/arm/mach-iop33x/include/mach/system.h1
-rw-r--r--arch/arm/mach-iop33x/include/mach/timex.h3
-rw-r--r--arch/arm/mach-ixp2000/include/mach/dma.h9
-rw-r--r--arch/arm/mach-ixp2000/include/mach/memory.h7
-rw-r--r--arch/arm/mach-ixp23xx/include/mach/dma.h3
-rw-r--r--arch/arm/mach-ixp23xx/include/mach/io.h2
-rw-r--r--arch/arm/mach-ixp23xx/include/mach/memory.h13
-rw-r--r--arch/arm/mach-ixp4xx/fsg-setup.c9
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/dma.h21
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/io.h4
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/memory.h13
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/qmgr.h35
-rw-r--r--arch/arm/mach-ixp4xx/ixp4xx_qmgr.c44
-rw-r--r--arch/arm/mach-ixp4xx/nas100d-setup.c5
-rw-r--r--arch/arm/mach-ixp4xx/nslu2-setup.c5
-rw-r--r--arch/arm/mach-kirkwood/common.c59
-rw-r--r--arch/arm/mach-kirkwood/common.h1
-rw-r--r--arch/arm/mach-kirkwood/include/mach/dma.h1
-rw-r--r--arch/arm/mach-kirkwood/include/mach/gpio.h38
-rw-r--r--arch/arm/mach-kirkwood/include/mach/irqs.h5
-rw-r--r--arch/arm/mach-kirkwood/include/mach/kirkwood.h3
-rw-r--r--arch/arm/mach-kirkwood/include/mach/memory.h4
-rw-r--r--arch/arm/mach-kirkwood/irq.c35
-rw-r--r--arch/arm/mach-kirkwood/rd88f6281-setup.c16
-rw-r--r--arch/arm/mach-ks8695/Kconfig6
-rw-r--r--arch/arm/mach-ks8695/Makefile1
-rw-r--r--arch/arm/mach-ks8695/board-dsm320.c131
-rw-r--r--arch/arm/mach-ks8695/board-micrel.c3
-rw-r--r--arch/arm/mach-ks8695/devices.c38
-rw-r--r--arch/arm/mach-ks8695/gpio.c44
-rw-r--r--arch/arm/mach-ks8695/include/mach/dma.h17
-rw-r--r--arch/arm/mach-ks8695/include/mach/gpio.h49
-rw-r--r--arch/arm/mach-ks8695/include/mach/io.h4
-rw-r--r--arch/arm/mach-ks8695/include/mach/memory.h5
-rw-r--r--arch/arm/mach-l7200/include/mach/dma.h23
-rw-r--r--arch/arm/mach-l7200/include/mach/io.h10
-rw-r--r--arch/arm/mach-l7200/include/mach/memory.h3
-rw-r--r--arch/arm/mach-lh7a40x/clocks.c92
-rw-r--r--arch/arm/mach-lh7a40x/include/mach/io.h6
-rw-r--r--arch/arm/mach-lh7a40x/include/mach/memory.h10
-rw-r--r--arch/arm/mach-loki/include/mach/dma.h1
-rw-r--r--arch/arm/mach-loki/include/mach/memory.h4
-rw-r--r--arch/arm/mach-msm/include/mach/io.h6
-rw-r--r--arch/arm/mach-msm/include/mach/memory.h4
-rw-r--r--arch/arm/mach-mv78xx0/common.c1
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/dma.h1
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/gpio.h40
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/irqs.h4
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/memory.h4
-rw-r--r--arch/arm/mach-mv78xx0/include/mach/mv78xx0.h3
-rw-r--r--arch/arm/mach-mv78xx0/irq.c29
-rw-r--r--arch/arm/mach-mx1/Kconfig14
-rw-r--r--arch/arm/mach-mx1/Makefile10
-rw-r--r--arch/arm/mach-mx1/Makefile.boot4
-rw-r--r--arch/arm/mach-mx1/clock.c656
-rw-r--r--arch/arm/mach-mx1/crm_regs.h55
-rw-r--r--arch/arm/mach-mx1/devices.c260
-rw-r--r--arch/arm/mach-mx1/devices.h7
-rw-r--r--arch/arm/mach-mx1/generic.c43
-rw-r--r--arch/arm/mach-mx1/mx1ads.c148
-rw-r--r--arch/arm/mach-mx2/devices.c46
-rw-r--r--arch/arm/mach-mx2/devices.h3
-rw-r--r--arch/arm/mach-mx2/mx27ads.c61
-rw-r--r--arch/arm/mach-mx2/pcm038.c82
-rw-r--r--arch/arm/mach-mx3/Kconfig14
-rw-r--r--arch/arm/mach-mx3/Makefile2
-rw-r--r--arch/arm/mach-mx3/clock.c1
-rw-r--r--arch/arm/mach-mx3/devices.c40
-rw-r--r--arch/arm/mach-mx3/devices.h2
-rw-r--r--arch/arm/mach-mx3/iomux.c9
-rw-r--r--arch/arm/mach-mx3/mx31moboard.c141
-rw-r--r--arch/arm/mach-mx3/mx31pdk.c115
-rw-r--r--arch/arm/mach-mx3/pcm037.c70
-rw-r--r--arch/arm/mach-netx/fb.c7
-rw-r--r--arch/arm/mach-netx/include/mach/dma.h21
-rw-r--r--arch/arm/mach-netx/include/mach/io.h2
-rw-r--r--arch/arm/mach-netx/include/mach/memory.h10
-rw-r--r--arch/arm/mach-netx/include/mach/netx-regs.h22
-rw-r--r--arch/arm/mach-netx/time.c98
-rw-r--r--arch/arm/mach-netx/xc.c6
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/dma.h14
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/hardware.h2
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/io.h2
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/memory.h3
-rw-r--r--arch/arm/mach-omap1/Kconfig3
-rw-r--r--arch/arm/mach-omap1/board-fsample.c7
-rw-r--r--arch/arm/mach-omap1/board-h2-mmc.c89
-rw-r--r--arch/arm/mach-omap1/board-h2.c49
-rw-r--r--arch/arm/mach-omap1/board-h3-mmc.c100
-rw-r--r--arch/arm/mach-omap1/board-h3.c19
-rw-r--r--arch/arm/mach-omap1/board-innovator.c52
-rw-r--r--arch/arm/mach-omap1/board-nokia770.c86
-rw-r--r--arch/arm/mach-omap1/board-osk.c43
-rw-r--r--arch/arm/mach-omap1/board-palmte.c15
-rw-r--r--arch/arm/mach-omap1/board-palmtt.c2
-rw-r--r--arch/arm/mach-omap1/board-palmz71.c34
-rw-r--r--arch/arm/mach-omap1/board-perseus2.c7
-rw-r--r--arch/arm/mach-omap1/board-sx1-mmc.c80
-rw-r--r--arch/arm/mach-omap1/board-sx1.c21
-rw-r--r--arch/arm/mach-omap1/board-voiceblue.c59
-rw-r--r--arch/arm/mach-omap1/clock.h3
-rw-r--r--arch/arm/mach-omap1/devices.c90
-rw-r--r--arch/arm/mach-omap1/fpga.c6
-rw-r--r--arch/arm/mach-omap1/id.c53
-rw-r--r--arch/arm/mach-omap1/leds-h2p2-debug.c10
-rw-r--r--arch/arm/mach-omap1/leds-osk.c4
-rw-r--r--arch/arm/mach-omap1/leds.c8
-rw-r--r--arch/arm/mach-omap1/pm.c6
-rw-r--r--arch/arm/mach-omap1/serial.c10
-rw-r--r--arch/arm/mach-omap2/Kconfig4
-rw-r--r--arch/arm/mach-omap2/Makefile14
-rw-r--r--arch/arm/mach-omap2/board-2430sdp.c52
-rw-r--r--arch/arm/mach-omap2/board-apollon.c26
-rw-r--r--arch/arm/mach-omap2/board-generic.c11
-rw-r--r--arch/arm/mach-omap2/board-h4.c25
-rw-r--r--arch/arm/mach-omap2/board-ldp.c97
-rw-r--r--arch/arm/mach-omap2/board-omap3beagle.c90
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c212
-rw-r--r--arch/arm/mach-omap2/board-overo.c43
-rw-r--r--arch/arm/mach-omap2/clock24xx.h9
-rw-r--r--arch/arm/mach-omap2/clock34xx.c2
-rw-r--r--arch/arm/mach-omap2/clock34xx.h46
-rw-r--r--arch/arm/mach-omap2/devices.c169
-rw-r--r--arch/arm/mach-omap2/id.c305
-rw-r--r--arch/arm/mach-omap2/irq.c39
-rw-r--r--arch/arm/mach-omap2/mmc-twl4030.c408
-rw-r--r--arch/arm/mach-omap2/mmc-twl4030.h29
-rw-r--r--arch/arm/mach-omap2/mux.c44
-rw-r--r--arch/arm/mach-omap2/usb-tusb6010.c6
-rw-r--r--arch/arm/mach-orion5x/Makefile2
-rw-r--r--arch/arm/mach-orion5x/common.c1
-rw-r--r--arch/arm/mach-orion5x/common.h7
-rw-r--r--arch/arm/mach-orion5x/dns323-setup.c174
-rw-r--r--arch/arm/mach-orion5x/gpio.c231
-rw-r--r--arch/arm/mach-orion5x/include/mach/dma.h1
-rw-r--r--arch/arm/mach-orion5x/include/mach/gpio.h29
-rw-r--r--arch/arm/mach-orion5x/include/mach/io.h7
-rw-r--r--arch/arm/mach-orion5x/include/mach/irqs.h4
-rw-r--r--arch/arm/mach-orion5x/include/mach/memory.h4
-rw-r--r--arch/arm/mach-orion5x/include/mach/orion5x.h9
-rw-r--r--arch/arm/mach-orion5x/irq.c183
-rw-r--r--arch/arm/mach-orion5x/mpp.c6
-rw-r--r--arch/arm/mach-pnx4008/dma.c3
-rw-r--r--arch/arm/mach-pnx4008/include/mach/dma.h2
-rw-r--r--arch/arm/mach-pnx4008/include/mach/io.h4
-rw-r--r--arch/arm/mach-pnx4008/include/mach/memory.h5
-rw-r--r--arch/arm/mach-pxa/Kconfig44
-rw-r--r--arch/arm/mach-pxa/Makefile5
-rw-r--r--arch/arm/mach-pxa/am200epd.c19
-rw-r--r--arch/arm/mach-pxa/clock.c67
-rw-r--r--arch/arm/mach-pxa/clock.h59
-rw-r--r--arch/arm/mach-pxa/cm-x2xx.c29
-rw-r--r--arch/arm/mach-pxa/cm-x300.c5
-rw-r--r--arch/arm/mach-pxa/corgi.c32
-rw-r--r--arch/arm/mach-pxa/cpufreq-pxa2xx.c57
-rw-r--r--arch/arm/mach-pxa/devices.c95
-rw-r--r--arch/arm/mach-pxa/devices.h1
-rw-r--r--arch/arm/mach-pxa/dma.c2
-rw-r--r--arch/arm/mach-pxa/e330.c36
-rw-r--r--arch/arm/mach-pxa/e350.c36
-rw-r--r--arch/arm/mach-pxa/e400.c67
-rw-r--r--arch/arm/mach-pxa/e740.c46
-rw-r--r--arch/arm/mach-pxa/e750.c36
-rw-r--r--arch/arm/mach-pxa/e800.c30
-rw-r--r--arch/arm/mach-pxa/eseries.c124
-rw-r--r--arch/arm/mach-pxa/eseries.h12
-rw-r--r--arch/arm/mach-pxa/ezx.c701
-rw-r--r--arch/arm/mach-pxa/generic.c16
-rw-r--r--arch/arm/mach-pxa/gpio.c59
-rw-r--r--arch/arm/mach-pxa/gumstix.c22
-rw-r--r--arch/arm/mach-pxa/h5000.c200
-rw-r--r--arch/arm/mach-pxa/imote2.c575
-rw-r--r--arch/arm/mach-pxa/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-pxa/include/mach/dma.h4
-rw-r--r--arch/arm/mach-pxa/include/mach/eseries-gpio.h4
-rw-r--r--arch/arm/mach-pxa/include/mach/gumstix.h4
-rw-r--r--arch/arm/mach-pxa/include/mach/h5000.h113
-rw-r--r--arch/arm/mach-pxa/include/mach/hardware.h52
-rw-r--r--arch/arm/mach-pxa/include/mach/io.h6
-rw-r--r--arch/arm/mach-pxa/include/mach/littleton.h11
-rw-r--r--arch/arm/mach-pxa/include/mach/memory.h11
-rw-r--r--arch/arm/mach-pxa/include/mach/mfp-pxa25x.h31
-rw-r--r--arch/arm/mach-pxa/include/mach/mfp-pxa27x.h6
-rw-r--r--arch/arm/mach-pxa/include/mach/mfp-pxa930.h1
-rw-r--r--arch/arm/mach-pxa/include/mach/mioa701.h9
-rw-r--r--arch/arm/mach-pxa/include/mach/mtd-xip.h1
-rw-r--r--arch/arm/mach-pxa/include/mach/palmasoc.h13
-rw-r--r--arch/arm/mach-pxa/include/mach/pxa-regs.h507
-rw-r--r--arch/arm/mach-pxa/include/mach/pxa2xx-gpio.h5
-rw-r--r--arch/arm/mach-pxa/include/mach/pxa2xx-regs.h12
-rw-r--r--arch/arm/mach-pxa/include/mach/pxafb.h6
-rw-r--r--arch/arm/mach-pxa/include/mach/regs-ac97.h99
-rw-r--r--arch/arm/mach-pxa/include/mach/regs-lcd.h67
-rw-r--r--arch/arm/mach-pxa/include/mach/regs-uart.h143
-rw-r--r--arch/arm/mach-pxa/include/mach/timex.h8
-rw-r--r--arch/arm/mach-pxa/include/mach/uncompress.h4
-rw-r--r--arch/arm/mach-pxa/littleton.c75
-rw-r--r--arch/arm/mach-pxa/magician.c11
-rw-r--r--arch/arm/mach-pxa/mainstone.c4
-rw-r--r--arch/arm/mach-pxa/mfp-pxa2xx.c62
-rw-r--r--arch/arm/mach-pxa/mioa701.c203
-rw-r--r--arch/arm/mach-pxa/pcm990-baseboard.c10
-rw-r--r--arch/arm/mach-pxa/poodle.c32
-rw-r--r--arch/arm/mach-pxa/pwm.c2
-rw-r--r--arch/arm/mach-pxa/pxa25x.c89
-rw-r--r--arch/arm/mach-pxa/pxa27x.c115
-rw-r--r--arch/arm/mach-pxa/pxa300.c18
-rw-r--r--arch/arm/mach-pxa/pxa320.c8
-rw-r--r--arch/arm/mach-pxa/pxa3xx.c111
-rw-r--r--arch/arm/mach-pxa/saar.c421
-rw-r--r--arch/arm/mach-pxa/smemc.c2
-rw-r--r--arch/arm/mach-pxa/spitz.c36
-rw-r--r--arch/arm/mach-pxa/ssp.c2
-rw-r--r--arch/arm/mach-pxa/tavorevb.c412
-rw-r--r--arch/arm/mach-pxa/time.c11
-rw-r--r--arch/arm/mach-pxa/tosa.c74
-rw-r--r--arch/arm/mach-pxa/zylonite.c2
-rw-r--r--arch/arm/mach-pxa/zylonite_pxa320.c2
-rw-r--r--arch/arm/mach-realview/Kconfig30
-rw-r--r--arch/arm/mach-realview/Makefile1
-rw-r--r--arch/arm/mach-realview/Makefile.boot7
-rw-r--r--arch/arm/mach-realview/clock.c80
-rw-r--r--arch/arm/mach-realview/clock.h6
-rw-r--r--arch/arm/mach-realview/core.c141
-rw-r--r--arch/arm/mach-realview/core.h5
-rw-r--r--arch/arm/mach-realview/hotplug.c5
-rw-r--r--arch/arm/mach-realview/include/mach/board-eb.h18
-rw-r--r--arch/arm/mach-realview/include/mach/board-pb11mp.h3
-rw-r--r--arch/arm/mach-realview/include/mach/board-pba8.h152
-rw-r--r--arch/arm/mach-realview/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-realview/include/mach/debug-macro.S29
-rw-r--r--arch/arm/mach-realview/include/mach/dma.h20
-rw-r--r--arch/arm/mach-realview/include/mach/hardware.h9
-rw-r--r--arch/arm/mach-realview/include/mach/io.h9
-rw-r--r--arch/arm/mach-realview/include/mach/irqs.h1
-rw-r--r--arch/arm/mach-realview/include/mach/memory.h14
-rw-r--r--arch/arm/mach-realview/include/mach/uncompress.h3
-rw-r--r--arch/arm/mach-realview/include/mach/vmalloc.h2
-rw-r--r--arch/arm/mach-realview/localtimer.c44
-rw-r--r--arch/arm/mach-realview/platsmp.c37
-rw-r--r--arch/arm/mach-realview/realview_eb.c33
-rw-r--r--arch/arm/mach-realview/realview_pb1176.c13
-rw-r--r--arch/arm/mach-realview/realview_pb11mp.c16
-rw-r--r--arch/arm/mach-realview/realview_pba8.c300
-rw-r--r--arch/arm/mach-rpc/include/mach/dma.h33
-rw-r--r--arch/arm/mach-rpc/include/mach/io.h47
-rw-r--r--arch/arm/mach-rpc/include/mach/irqs.h1
-rw-r--r--arch/arm/mach-rpc/include/mach/isa-dma.h27
-rw-r--r--arch/arm/mach-rpc/include/mach/memory.h7
-rw-r--r--arch/arm/mach-s3c2400/include/mach/memory.h3
-rw-r--r--arch/arm/mach-s3c2410/Kconfig8
-rw-r--r--arch/arm/mach-s3c2410/Makefile1
-rw-r--r--arch/arm/mach-s3c2410/clock.c276
-rw-r--r--arch/arm/mach-s3c2410/dma.c5
-rw-r--r--arch/arm/mach-s3c2410/include/mach/dma.h23
-rw-r--r--arch/arm/mach-s3c2410/include/mach/gpio-core.h34
-rw-r--r--arch/arm/mach-s3c2410/include/mach/gpio.h6
-rw-r--r--arch/arm/mach-s3c2410/include/mach/irqs.h8
-rw-r--r--arch/arm/mach-s3c2410/include/mach/map.h83
-rw-r--r--arch/arm/mach-s3c2410/include/mach/memory.h3
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-clock.h33
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-gpio.h7
-rw-r--r--arch/arm/mach-s3c2410/include/mach/spi.h7
-rw-r--r--arch/arm/mach-s3c2410/include/mach/system-reset.h2
-rw-r--r--arch/arm/mach-s3c2410/include/mach/tick.h15
-rw-r--r--arch/arm/mach-s3c2410/include/mach/uncompress.h1
-rw-r--r--arch/arm/mach-s3c2410/include/mach/vmalloc.h20
-rw-r--r--arch/arm/mach-s3c2410/mach-amlm5900.c4
-rw-r--r--arch/arm/mach-s3c2410/mach-bast.c11
-rw-r--r--arch/arm/mach-s3c2410/mach-h1940.c14
-rw-r--r--arch/arm/mach-s3c2410/mach-n30.c11
-rw-r--r--arch/arm/mach-s3c2410/mach-otom.c4
-rw-r--r--arch/arm/mach-s3c2410/mach-qt2410.c8
-rw-r--r--arch/arm/mach-s3c2410/mach-smdk2410.c4
-rw-r--r--arch/arm/mach-s3c2410/mach-tct_hammer.c4
-rw-r--r--arch/arm/mach-s3c2410/mach-vr1000.c4
-rw-r--r--arch/arm/mach-s3c2410/s3c2410.c27
-rw-r--r--arch/arm/mach-s3c2412/Kconfig1
-rw-r--r--arch/arm/mach-s3c2412/clock.c7
-rw-r--r--arch/arm/mach-s3c2412/dma.c5
-rw-r--r--arch/arm/mach-s3c2412/mach-jive.c22
-rw-r--r--arch/arm/mach-s3c2412/mach-smdk2413.c6
-rw-r--r--arch/arm/mach-s3c2412/mach-vstms.c6
-rw-r--r--arch/arm/mach-s3c2412/s3c2412.c27
-rw-r--r--arch/arm/mach-s3c2440/Kconfig5
-rw-r--r--arch/arm/mach-s3c2440/dma.c5
-rw-r--r--arch/arm/mach-s3c2440/mach-anubis.c11
-rw-r--r--arch/arm/mach-s3c2440/mach-at2440evb.c51
-rw-r--r--arch/arm/mach-s3c2440/mach-nexcoder.c4
-rw-r--r--arch/arm/mach-s3c2440/mach-osiris.c7
-rw-r--r--arch/arm/mach-s3c2440/mach-rx3715.c4
-rw-r--r--arch/arm/mach-s3c2440/mach-smdk2440.c4
-rw-r--r--arch/arm/mach-s3c2442/Kconfig1
-rw-r--r--arch/arm/mach-s3c2443/Kconfig1
-rw-r--r--arch/arm/mach-s3c2443/clock.c53
-rw-r--r--arch/arm/mach-s3c2443/dma.c5
-rw-r--r--arch/arm/mach-s3c2443/mach-smdk2443.c6
-rw-r--r--arch/arm/mach-s3c2443/s3c2443.c3
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/debug-macro.S28
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/irqs.h115
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/map.h85
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/memory.h19
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/regs-clock.h88
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/regs-irq.h25
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/system.h25
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/tick.h15
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/timex.h18
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/vmalloc.h17
-rw-r--r--arch/arm/mach-s3c6400/Kconfig8
-rw-r--r--arch/arm/mach-s3c6400/Makefile15
-rw-r--r--arch/arm/mach-s3c6400/Makefile.boot2
-rw-r--r--arch/arm/mach-s3c6400/include/mach/debug-macro.S39
-rw-r--r--arch/arm/mach-s3c6400/include/mach/dma.h16
-rw-r--r--arch/arm/mach-s3c6400/include/mach/entry-macro.S44
-rw-r--r--arch/arm/mach-s3c6400/include/mach/gpio-core.h21
-rw-r--r--arch/arm/mach-s3c6400/include/mach/gpio.h96
-rw-r--r--arch/arm/mach-s3c6400/include/mach/hardware.h16
-rw-r--r--arch/arm/mach-s3c6400/include/mach/irqs.h20
-rw-r--r--arch/arm/mach-s3c6400/include/mach/map.h68
-rw-r--r--arch/arm/mach-s3c6400/include/mach/memory.h18
-rw-r--r--arch/arm/mach-s3c6400/include/mach/pwm-clock.h56
-rw-r--r--arch/arm/mach-s3c6400/include/mach/regs-fb.h259
-rw-r--r--arch/arm/mach-s3c6400/include/mach/regs-irq.h20
-rw-r--r--arch/arm/mach-s3c6400/include/mach/system.h24
-rw-r--r--arch/arm/mach-s3c6400/include/mach/tick.h29
-rw-r--r--arch/arm/mach-s3c6400/include/mach/uncompress.h28
-rw-r--r--arch/arm/mach-s3c6410/Kconfig62
-rw-r--r--arch/arm/mach-s3c6410/Makefile23
-rw-r--r--arch/arm/mach-s3c6410/cpu.c101
-rw-r--r--arch/arm/mach-s3c6410/mach-smdk6410.c185
-rw-r--r--arch/arm/mach-s3c6410/setup-sdhci.c102
-rw-r--r--arch/arm/mach-sa1100/clock.c100
-rw-r--r--arch/arm/mach-sa1100/collie.c29
-rw-r--r--arch/arm/mach-sa1100/collie_pm.c22
-rw-r--r--arch/arm/mach-sa1100/cpu-sa1100.c20
-rw-r--r--arch/arm/mach-sa1100/cpu-sa1110.c18
-rw-r--r--arch/arm/mach-sa1100/dma.c10
-rw-r--r--arch/arm/mach-sa1100/include/mach/h3600.h12
-rw-r--r--arch/arm/mach-sa1100/include/mach/hardware.h4
-rw-r--r--arch/arm/mach-sa1100/include/mach/io.h8
-rw-r--r--arch/arm/mach-sa1100/include/mach/memory.h13
-rw-r--r--arch/arm/mach-sa1100/include/mach/mtd-xip.h2
-rw-r--r--arch/arm/mach-sa1100/pleb.c10
-rw-r--r--arch/arm/mach-sa1100/shannon.c2
-rw-r--r--arch/arm/mach-sa1100/sleep.S52
-rw-r--r--arch/arm/mach-sa1100/time.c4
-rw-r--r--arch/arm/mach-shark/core.c2
-rw-r--r--arch/arm/mach-shark/include/mach/dma.h18
-rw-r--r--arch/arm/mach-shark/include/mach/hardware.h2
-rw-r--r--arch/arm/mach-shark/include/mach/io.h44
-rw-r--r--arch/arm/mach-shark/include/mach/isa-dma.h17
-rw-r--r--arch/arm/mach-shark/include/mach/memory.h4
-rw-r--r--arch/arm/mach-versatile/Kconfig2
-rw-r--r--arch/arm/mach-versatile/clock.c80
-rw-r--r--arch/arm/mach-versatile/clock.h7
-rw-r--r--arch/arm/mach-versatile/core.c56
-rw-r--r--arch/arm/mach-versatile/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-versatile/include/mach/dma.h20
-rw-r--r--arch/arm/mach-versatile/include/mach/io.h8
-rw-r--r--arch/arm/mach-versatile/include/mach/irqs.h86
-rw-r--r--arch/arm/mach-versatile/include/mach/memory.h10
-rw-r--r--arch/arm/mach-versatile/include/mach/platform.h58
-rw-r--r--arch/arm/mach-w90x900/Kconfig19
-rw-r--r--arch/arm/mach-w90x900/Makefile15
-rw-r--r--arch/arm/mach-w90x900/Makefile.boot3
-rw-r--r--arch/arm/mach-w90x900/cpu.h77
-rw-r--r--arch/arm/mach-w90x900/include/mach/entry-macro.S34
-rw-r--r--arch/arm/mach-w90x900/include/mach/hardware.h24
-rw-r--r--arch/arm/mach-w90x900/include/mach/io.h30
-rw-r--r--arch/arm/mach-w90x900/include/mach/irqs.h45
-rw-r--r--arch/arm/mach-w90x900/include/mach/map.h76
-rw-r--r--arch/arm/mach-w90x900/include/mach/memory.h23
-rw-r--r--arch/arm/mach-w90x900/include/mach/regs-irq.h51
-rw-r--r--arch/arm/mach-w90x900/include/mach/regs-serial.h59
-rw-r--r--arch/arm/mach-w90x900/include/mach/regs-timer.h42
-rw-r--r--arch/arm/mach-w90x900/include/mach/system.h28
-rw-r--r--arch/arm/mach-w90x900/include/mach/timex.h25
-rw-r--r--arch/arm/mach-w90x900/include/mach/uncompress.h40
-rw-r--r--arch/arm/mach-w90x900/include/mach/vmalloc.h23
-rw-r--r--arch/arm/mach-w90x900/irq.c76
-rw-r--r--arch/arm/mach-w90x900/mach-w90p910evb.c72
-rw-r--r--arch/arm/mach-w90x900/time.c80
-rw-r--r--arch/arm/mach-w90x900/w90p910.c134
-rw-r--r--arch/arm/mm/Kconfig67
-rw-r--r--arch/arm/mm/alignment.c1
-rw-r--r--arch/arm/mm/cache-v3.S1
-rw-r--r--arch/arm/mm/cache-v4.S1
-rw-r--r--arch/arm/mm/cache-v4wt.S1
-rw-r--r--arch/arm/mm/cache-v7.S2
-rw-r--r--arch/arm/mm/copypage-feroceon.S95
-rw-r--r--arch/arm/mm/copypage-feroceon.c111
-rw-r--r--arch/arm/mm/copypage-v3.S67
-rw-r--r--arch/arm/mm/copypage-v3.c81
-rw-r--r--arch/arm/mm/copypage-v4mc.c53
-rw-r--r--arch/arm/mm/copypage-v4wb.S79
-rw-r--r--arch/arm/mm/copypage-v4wb.c94
-rw-r--r--arch/arm/mm/copypage-v4wt.S73
-rw-r--r--arch/arm/mm/copypage-v4wt.c88
-rw-r--r--arch/arm/mm/copypage-v6.c84
-rw-r--r--arch/arm/mm/copypage-xsc3.S97
-rw-r--r--arch/arm/mm/copypage-xsc3.c113
-rw-r--r--arch/arm/mm/copypage-xscale.c47
-rw-r--r--arch/arm/mm/fault.c6
-rw-r--r--arch/arm/mm/init.c60
-rw-r--r--arch/arm/mm/mm.h4
-rw-r--r--arch/arm/mm/mmu.c128
-rw-r--r--arch/arm/mm/nommu.c23
-rw-r--r--arch/arm/mm/pgd.c2
-rw-r--r--arch/arm/mm/proc-syms.c4
-rw-r--r--arch/arm/mm/proc-v6.S2
-rw-r--r--arch/arm/mm/proc-v7.S19
-rw-r--r--arch/arm/mm/proc-xsc3.S25
-rw-r--r--arch/arm/plat-mxc/Kconfig9
-rw-r--r--arch/arm/plat-mxc/Makefile1
-rw-r--r--arch/arm/plat-mxc/dma-mx1-mx2.c14
-rw-r--r--arch/arm/plat-mxc/gpio.c6
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx27ads.h7
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31ads.h2
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31pdk.h19
-rw-r--r--arch/arm/plat-mxc/include/mach/debug-macro.S3
-rw-r--r--arch/arm/plat-mxc/include/mach/dma-mx1-mx2.h10
-rw-r--r--arch/arm/plat-mxc/include/mach/dma.h14
-rw-r--r--arch/arm/plat-mxc/include/mach/entry-macro.S2
-rw-r--r--arch/arm/plat-mxc/include/mach/gpio.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/hardware.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/io.h8
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h72
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mx3.h19
-rw-r--r--arch/arm/plat-mxc/include/mach/irqs.h32
-rw-r--r--arch/arm/plat-mxc/include/mach/memory.h21
-rw-r--r--arch/arm/plat-mxc/include/mach/mtd-xip.h34
-rw-r--r--arch/arm/plat-mxc/include/mach/mx1.h186
-rw-r--r--arch/arm/plat-mxc/include/mach/mx27.h15
-rw-r--r--arch/arm/plat-mxc/include/mach/mx31.h25
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_timer.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/timex.h8
-rw-r--r--arch/arm/plat-mxc/iomux-mx1-mx2.c37
-rw-r--r--arch/arm/plat-mxc/irq.c33
-rw-r--r--arch/arm/plat-omap/Kconfig2
-rw-r--r--arch/arm/plat-omap/debug-devices.c8
-rw-r--r--arch/arm/plat-omap/debug-leds.c10
-rw-r--r--arch/arm/plat-omap/devices.c225
-rw-r--r--arch/arm/plat-omap/dma.c17
-rw-r--r--arch/arm/plat-omap/dmtimer.c11
-rw-r--r--arch/arm/plat-omap/gpio.c127
-rw-r--r--arch/arm/plat-omap/i2c.c55
-rw-r--r--arch/arm/plat-omap/include/mach/board-apollon.h4
-rw-r--r--arch/arm/plat-omap/include/mach/board-h2.h6
-rw-r--r--arch/arm/plat-omap/include/mach/board-ldp.h5
-rw-r--r--arch/arm/plat-omap/include/mach/board.h22
-rw-r--r--arch/arm/plat-omap/include/mach/control.h17
-rw-r--r--arch/arm/plat-omap/include/mach/cpu.h82
-rw-r--r--arch/arm/plat-omap/include/mach/gpio.h31
-rw-r--r--arch/arm/plat-omap/include/mach/io.h6
-rw-r--r--arch/arm/plat-omap/include/mach/memory.h17
-rw-r--r--arch/arm/plat-omap/include/mach/mmc.h74
-rw-r--r--arch/arm/plat-omap/include/mach/mux.h41
-rw-r--r--arch/arm/plat-omap/sram.c3
-rw-r--r--arch/arm/plat-orion/Makefile2
-rw-r--r--arch/arm/plat-orion/gpio.c415
-rw-r--r--arch/arm/plat-orion/include/plat/ehci-orion.h8
-rw-r--r--arch/arm/plat-orion/include/plat/gpio.h39
-rw-r--r--arch/arm/plat-s3c/Kconfig95
-rw-r--r--arch/arm/plat-s3c/Makefile28
-rw-r--r--arch/arm/plat-s3c/clock.c368
-rw-r--r--arch/arm/plat-s3c/dev-fb.c72
-rw-r--r--arch/arm/plat-s3c/dev-hsmmc.c68
-rw-r--r--arch/arm/plat-s3c/dev-hsmmc1.c68
-rw-r--r--arch/arm/plat-s3c/dev-i2c0.c71
-rw-r--r--arch/arm/plat-s3c/dev-i2c1.c68
-rw-r--r--arch/arm/plat-s3c/gpio-config.c163
-rw-r--r--arch/arm/plat-s3c/gpio.c147
-rw-r--r--arch/arm/plat-s3c/include/mach/io.h18
-rw-r--r--arch/arm/plat-s3c/include/mach/timex.h (renamed from arch/arm/mach-s3c2410/include/mach/timex.h)0
-rw-r--r--arch/arm/plat-s3c/include/mach/vmalloc.h20
-rw-r--r--arch/arm/plat-s3c/include/plat/adc.h29
-rw-r--r--arch/arm/plat-s3c/include/plat/clock.h88
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu-freq.h94
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu.h71
-rw-r--r--arch/arm/plat-s3c/include/plat/debug-macro.S2
-rw-r--r--arch/arm/plat-s3c/include/plat/devs.h54
-rw-r--r--arch/arm/plat-s3c/include/plat/fb.h73
-rw-r--r--arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h176
-rw-r--r--arch/arm/plat-s3c/include/plat/gpio-cfg.h110
-rw-r--r--arch/arm/plat-s3c/include/plat/gpio-core.h77
-rw-r--r--arch/arm/plat-s3c/include/plat/iic-core.h35
-rw-r--r--arch/arm/plat-s3c/include/plat/iic.h57
-rw-r--r--arch/arm/plat-s3c/include/plat/map-base.h (renamed from arch/arm/plat-s3c/include/plat/map.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/nand.h (renamed from include/asm-arm/plat-s3c/nand.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-ac97.h (renamed from include/asm-arm/plat-s3c/regs-ac97.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-fb.h366
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-iic.h (renamed from include/asm-arm/plat-s3c/regs-iic.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-irqtype.h21
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-nand.h (renamed from include/asm-arm/plat-s3c/regs-nand.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-rtc.h (renamed from include/asm-arm/plat-s3c/regs-rtc.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-sdhci.h87
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-serial.h16
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-timer.h11
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-watchdog.h (renamed from include/asm-arm/plat-s3c/regs-watchdog.h)0
-rw-r--r--arch/arm/plat-s3c/include/plat/sdhci.h108
-rw-r--r--arch/arm/plat-s3c/include/plat/uncompress.h32
-rw-r--r--arch/arm/plat-s3c/init.c160
-rw-r--r--arch/arm/plat-s3c/pwm-clock.c463
-rw-r--r--arch/arm/plat-s3c/time.c285
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig61
-rw-r--r--arch/arm/plat-s3c24xx/Makefile16
-rw-r--r--arch/arm/plat-s3c24xx/adc.c372
-rw-r--r--arch/arm/plat-s3c24xx/clock-dclk.c194
-rw-r--r--arch/arm/plat-s3c24xx/clock.c493
-rw-r--r--arch/arm/plat-s3c24xx/common-smdk.c2
-rw-r--r--arch/arm/plat-s3c24xx/cpu.c142
-rw-r--r--arch/arm/plat-s3c24xx/devs.c103
-rw-r--r--arch/arm/plat-s3c24xx/dma.c18
-rw-r--r--arch/arm/plat-s3c24xx/gpiolib.c130
-rw-r--r--arch/arm/plat-s3c24xx/include/mach/pwm-clock.h55
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/clock.h64
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/cpu.h54
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/devs.h49
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/map.h99
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/mci.h (renamed from include/asm-arm/plat-s3c24xx/mci.h)0
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/pll.h37
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/regs-spi.h (renamed from include/asm-arm/plat-s3c24xx/regs-spi.h)0
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/regs-udc.h (renamed from include/asm-arm/plat-s3c24xx/regs-udc.h)0
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2400.h2
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2410.h2
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2412.h2
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2443.h2
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/udc.h (renamed from include/asm-arm/plat-s3c24xx/udc.h)0
-rw-r--r--arch/arm/plat-s3c24xx/irq.c1
-rw-r--r--arch/arm/plat-s3c24xx/pm.c5
-rw-r--r--arch/arm/plat-s3c24xx/pwm-clock.c437
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-clock.c277
-rw-r--r--arch/arm/plat-s3c24xx/s3c244x-clock.c5
-rw-r--r--arch/arm/plat-s3c24xx/s3c244x.c29
-rw-r--r--arch/arm/plat-s3c24xx/s3c244x.h2
-rw-r--r--arch/arm/plat-s3c24xx/setup-i2c.c25
-rw-r--r--arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c37
-rw-r--r--arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c37
-rw-r--r--arch/arm/plat-s3c24xx/time.c260
-rw-r--r--arch/arm/plat-s3c64xx/Kconfig62
-rw-r--r--arch/arm/plat-s3c64xx/Makefile31
-rw-r--r--arch/arm/plat-s3c64xx/clock.c281
-rw-r--r--arch/arm/plat-s3c64xx/cpu.c114
-rw-r--r--arch/arm/plat-s3c64xx/dev-uart.c176
-rw-r--r--arch/arm/plat-s3c64xx/gpiolib.c420
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-a.h48
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-b.h60
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-c.h53
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-d.h49
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-e.h44
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-f.h71
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-g.h42
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-h.h74
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-i.h40
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-j.h36
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-n.h54
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-o.h70
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-p.h69
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/gpio-bank-q.h46
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/irqs.h201
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/pll.h74
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/regs-clock.h224
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/regs-gpio.h35
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/regs-sys.h24
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/s3c6400.h35
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/s3c6410.h29
-rw-r--r--arch/arm/plat-s3c64xx/irq-eint.c202
-rw-r--r--arch/arm/plat-s3c64xx/irq.c257
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-clock.c655
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-init.c29
-rw-r--r--arch/arm/plat-s3c64xx/setup-fb-24bpp.c37
-rw-r--r--arch/arm/plat-s3c64xx/setup-i2c0.c31
-rw-r--r--arch/arm/plat-s3c64xx/setup-i2c1.c31
-rw-r--r--arch/arm/tools/mach-types101
-rw-r--r--arch/arm/vfp/vfp.h2
-rw-r--r--arch/arm/vfp/vfphw.S29
-rw-r--r--arch/arm/vfp/vfpmodule.c65
-rw-r--r--arch/avr32/boards/favr-32/flash.c2
-rw-r--r--arch/avr32/boards/favr-32/setup.c8
-rw-r--r--arch/avr32/boot/images/Makefile2
-rw-r--r--arch/avr32/configs/atstk1006_defconfig134
-rw-r--r--arch/avr32/include/asm/bitops.h5
-rw-r--r--arch/avr32/mach-at32ap/at32ap700x.c8
-rw-r--r--arch/blackfin/boot/Makefile2
-rw-r--r--arch/blackfin/include/asm/bitops.h1
-rw-r--r--arch/ia64/hp/sim/simeth.c25
-rw-r--r--arch/ia64/ia32/sys_ia32.c7
-rw-r--r--arch/ia64/include/asm/topology.h9
-rw-r--r--arch/ia64/kernel/acpi.c3
-rw-r--r--arch/ia64/kernel/iosapic.c23
-rw-r--r--arch/ia64/kernel/mca_drv.c2
-rw-r--r--arch/ia64/kernel/perfmon.c43
-rw-r--r--arch/ia64/kernel/signal.c4
-rw-r--r--arch/ia64/sn/kernel/sn2/sn_hwperf.c27
-rw-r--r--arch/m32r/kernel/smpboot.c2
-rw-r--r--arch/m68k/fpsp040/setox.S4
-rw-r--r--arch/m68k/mac/baboon.c42
-rw-r--r--arch/m68k/mac/config.c3
-rw-r--r--arch/m68k/mac/debug.c1
-rw-r--r--arch/m68k/mac/macints.c9
-rw-r--r--arch/m68k/mac/misc.c16
-rw-r--r--arch/m68k/mac/oss.c1
-rw-r--r--arch/m68k/mac/via.c80
-rw-r--r--arch/m68knommu/include/asm/bitops.h1
-rw-r--r--arch/mips/include/asm/byteorder.h5
-rw-r--r--arch/mips/include/asm/elf.h2
-rw-r--r--arch/mips/include/asm/mach-ip27/topology.h4
-rw-r--r--arch/mips/kernel/kspd.c4
-rw-r--r--arch/mips/kernel/mips-mt-fpaff.c5
-rw-r--r--arch/mips/kernel/vpe.c4
-rw-r--r--arch/parisc/include/asm/smp.h2
-rw-r--r--arch/parisc/include/asm/tlbflush.h5
-rw-r--r--arch/parisc/kernel/signal.c2
-rw-r--r--arch/powerpc/Kconfig64
-rw-r--r--arch/powerpc/Kconfig.debug9
-rw-r--r--arch/powerpc/Makefile1
-rw-r--r--arch/powerpc/boot/Makefile1
-rw-r--r--arch/powerpc/boot/devtree.c2
-rw-r--r--arch/powerpc/boot/dts/asp834x-redboot.dts20
-rw-r--r--arch/powerpc/boot/dts/bamboo.dts3
-rw-r--r--arch/powerpc/boot/dts/canyonlands.dts14
-rw-r--r--arch/powerpc/boot/dts/gef_sbc610.dts11
-rw-r--r--arch/powerpc/boot/dts/ksi8560.dts20
-rw-r--r--arch/powerpc/boot/dts/kuroboxHD.dts1
-rw-r--r--arch/powerpc/boot/dts/kuroboxHG.dts1
-rw-r--r--arch/powerpc/boot/dts/lite5200.dts1
-rw-r--r--arch/powerpc/boot/dts/lite5200b.dts1
-rw-r--r--arch/powerpc/boot/dts/motionpro.dts1
-rw-r--r--arch/powerpc/boot/dts/mpc8313erdb.dts20
-rw-r--r--arch/powerpc/boot/dts/mpc8315erdb.dts20
-rw-r--r--arch/powerpc/boot/dts/mpc8349emitx.dts19
-rw-r--r--arch/powerpc/boot/dts/mpc8349emitxgp.dts6
-rw-r--r--arch/powerpc/boot/dts/mpc834x_mds.dts19
-rw-r--r--arch/powerpc/boot/dts/mpc8377_mds.dts19
-rw-r--r--arch/powerpc/boot/dts/mpc8377_rdb.dts20
-rw-r--r--arch/powerpc/boot/dts/mpc8378_mds.dts19
-rw-r--r--arch/powerpc/boot/dts/mpc8378_rdb.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8379_mds.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8379_rdb.dts19
-rw-r--r--arch/powerpc/boot/dts/mpc8536ds.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8540ads.dts31
-rw-r--r--arch/powerpc/boot/dts/mpc8541cds.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8544ds.dts20
-rw-r--r--arch/powerpc/boot/dts/mpc8548cds.dts44
-rw-r--r--arch/powerpc/boot/dts/mpc8555cds.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8560ads.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8568mds.dts18
-rw-r--r--arch/powerpc/boot/dts/mpc8572ds.dts158
-rw-r--r--arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts483
-rw-r--r--arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts234
-rw-r--r--arch/powerpc/boot/dts/mpc8641_hpcn.dts45
-rw-r--r--arch/powerpc/boot/dts/pcm030.dts2
-rw-r--r--arch/powerpc/boot/dts/sbc8349.dts18
-rw-r--r--arch/powerpc/boot/dts/sbc8548.dts18
-rw-r--r--arch/powerpc/boot/dts/sbc8560.dts18
-rw-r--r--arch/powerpc/boot/dts/sbc8641d.dts44
-rw-r--r--arch/powerpc/boot/dts/stx_gp3_8560.dts18
-rw-r--r--arch/powerpc/boot/dts/tqm5200.dts1
-rw-r--r--arch/powerpc/boot/dts/tqm8540.dts28
-rw-r--r--arch/powerpc/boot/dts/tqm8541.dts18
-rw-r--r--arch/powerpc/boot/dts/tqm8548-bigflash.dts44
-rw-r--r--arch/powerpc/boot/dts/tqm8548.dts44
-rw-r--r--arch/powerpc/boot/dts/tqm8555.dts18
-rw-r--r--arch/powerpc/boot/dts/tqm8560.dts18
-rw-r--r--arch/powerpc/boot/libfdt-wrapper.c2
-rw-r--r--arch/powerpc/configs/86xx/gef_sbc610_defconfig8
-rw-r--r--arch/powerpc/configs/ppc44x_defconfig12
-rw-r--r--arch/powerpc/include/asm/atomic.h18
-rw-r--r--arch/powerpc/include/asm/bug.h11
-rw-r--r--arch/powerpc/include/asm/byteorder.h38
-rw-r--r--arch/powerpc/include/asm/cputable.h113
-rw-r--r--arch/powerpc/include/asm/dcr-native.h63
-rw-r--r--arch/powerpc/include/asm/dcr.h4
-rw-r--r--arch/powerpc/include/asm/device.h12
-rw-r--r--arch/powerpc/include/asm/dma-mapping.h156
-rw-r--r--arch/powerpc/include/asm/eeh.h8
-rw-r--r--arch/powerpc/include/asm/elf.h2
-rw-r--r--arch/powerpc/include/asm/feature-fixups.h30
-rw-r--r--arch/powerpc/include/asm/highmem.h23
-rw-r--r--arch/powerpc/include/asm/io.h7
-rw-r--r--arch/powerpc/include/asm/kdump.h13
-rw-r--r--arch/powerpc/include/asm/kexec.h15
-rw-r--r--arch/powerpc/include/asm/local.h4
-rw-r--r--arch/powerpc/include/asm/lppaca.h3
-rw-r--r--arch/powerpc/include/asm/mmu-40x.h5
-rw-r--r--arch/powerpc/include/asm/mmu-44x.h22
-rw-r--r--arch/powerpc/include/asm/mmu-8xx.h3
-rw-r--r--arch/powerpc/include/asm/mmu-fsl-booke.h7
-rw-r--r--arch/powerpc/include/asm/mmu.h57
-rw-r--r--arch/powerpc/include/asm/mmu_context.h257
-rw-r--r--arch/powerpc/include/asm/mpc52xx.h19
-rw-r--r--arch/powerpc/include/asm/mpc52xx_psc.h11
-rw-r--r--arch/powerpc/include/asm/mutex.h135
-rw-r--r--arch/powerpc/include/asm/page.h13
-rw-r--r--arch/powerpc/include/asm/page_32.h7
-rw-r--r--arch/powerpc/include/asm/pci-bridge.h30
-rw-r--r--arch/powerpc/include/asm/pci.h15
-rw-r--r--arch/powerpc/include/asm/pgalloc-32.h11
-rw-r--r--arch/powerpc/include/asm/pgalloc-64.h34
-rw-r--r--arch/powerpc/include/asm/pgalloc.h41
-rw-r--r--arch/powerpc/include/asm/pgtable-ppc32.h42
-rw-r--r--arch/powerpc/include/asm/pgtable-ppc64.h15
-rw-r--r--arch/powerpc/include/asm/pgtable.h26
-rw-r--r--arch/powerpc/include/asm/ppc_asm.h4
-rw-r--r--arch/powerpc/include/asm/processor.h8
-rw-r--r--arch/powerpc/include/asm/prom.h3
-rw-r--r--arch/powerpc/include/asm/ps3.h56
-rw-r--r--arch/powerpc/include/asm/ps3av.h4
-rw-r--r--arch/powerpc/include/asm/reg.h4
-rw-r--r--arch/powerpc/include/asm/rtas.h1
-rw-r--r--arch/powerpc/include/asm/sfp-machine.h58
-rw-r--r--arch/powerpc/include/asm/smp.h7
-rw-r--r--arch/powerpc/include/asm/spinlock.h2
-rw-r--r--arch/powerpc/include/asm/synch.h4
-rw-r--r--arch/powerpc/include/asm/system.h24
-rw-r--r--arch/powerpc/include/asm/time.h20
-rw-r--r--arch/powerpc/include/asm/tlbflush.h87
-rw-r--r--arch/powerpc/include/asm/topology.h12
-rw-r--r--arch/powerpc/include/asm/vdso_datapage.h3
-rw-r--r--arch/powerpc/kernel/Makefile4
-rw-r--r--arch/powerpc/kernel/asm-offsets.c6
-rw-r--r--arch/powerpc/kernel/cputable.c117
-rw-r--r--arch/powerpc/kernel/dma.c26
-rw-r--r--arch/powerpc/kernel/head_32.S31
-rw-r--r--arch/powerpc/kernel/head_44x.S34
-rw-r--r--arch/powerpc/kernel/head_fsl_booke.S107
-rw-r--r--arch/powerpc/kernel/ibmebus.c3
-rw-r--r--arch/powerpc/kernel/machine_kexec.c91
-rw-r--r--arch/powerpc/kernel/machine_kexec_64.c78
-rw-r--r--arch/powerpc/kernel/misc_32.S238
-rw-r--r--arch/powerpc/kernel/module.c6
-rw-r--r--arch/powerpc/kernel/of_device.c18
-rw-r--r--arch/powerpc/kernel/paca.c1
-rw-r--r--arch/powerpc/kernel/pci-common.c300
-rw-r--r--arch/powerpc/kernel/pci_32.c108
-rw-r--r--arch/powerpc/kernel/pci_64.c134
-rw-r--r--arch/powerpc/kernel/ppc_ksyms.c9
-rw-r--r--arch/powerpc/kernel/ppc_save_regs.S74
-rw-r--r--arch/powerpc/kernel/process.c4
-rw-r--r--arch/powerpc/kernel/prom.c47
-rw-r--r--arch/powerpc/kernel/prom_parse.c5
-rw-r--r--arch/powerpc/kernel/rtas.c26
-rw-r--r--arch/powerpc/kernel/rtas_pci.c48
-rw-r--r--arch/powerpc/kernel/setup_32.c15
-rw-r--r--arch/powerpc/kernel/setup_64.c5
-rw-r--r--arch/powerpc/kernel/smp-tbsync.c12
-rw-r--r--arch/powerpc/kernel/smp.c71
-rw-r--r--arch/powerpc/kernel/swsusp.c2
-rw-r--r--arch/powerpc/kernel/swsusp_32.S6
-rw-r--r--arch/powerpc/kernel/sysfs.c7
-rw-r--r--arch/powerpc/kernel/time.c36
-rw-r--r--arch/powerpc/kernel/traps.c62
-rw-r--r--arch/powerpc/kernel/vdso.c13
-rw-r--r--arch/powerpc/kernel/vdso32/gettimeofday.S208
-rw-r--r--arch/powerpc/kernel/vdso32/vdso32.lds.S3
-rw-r--r--arch/powerpc/kernel/vdso64/gettimeofday.S141
-rw-r--r--arch/powerpc/kernel/vdso64/vdso64.lds.S3
-rw-r--r--arch/powerpc/kernel/vio.c12
-rw-r--r--arch/powerpc/kernel/vmlinux.lds.S6
-rw-r--r--arch/powerpc/kvm/powerpc.c3
-rw-r--r--arch/powerpc/lib/copyuser_64.S17
-rw-r--r--arch/powerpc/lib/dma-noncoherent.c25
-rw-r--r--arch/powerpc/lib/memcpy_64.S16
-rw-r--r--arch/powerpc/math-emu/Makefile2
-rw-r--r--arch/powerpc/math-emu/fadd.c1
-rw-r--r--arch/powerpc/math-emu/fcmpo.c5
-rw-r--r--arch/powerpc/math-emu/fdiv.c9
-rw-r--r--arch/powerpc/math-emu/fdivs.c9
-rw-r--r--arch/powerpc/math-emu/fmadd.c5
-rw-r--r--arch/powerpc/math-emu/fmadds.c5
-rw-r--r--arch/powerpc/math-emu/fmsub.c5
-rw-r--r--arch/powerpc/math-emu/fmsubs.c5
-rw-r--r--arch/powerpc/math-emu/fmul.c3
-rw-r--r--arch/powerpc/math-emu/fmuls.c3
-rw-r--r--arch/powerpc/math-emu/fnmadd.c5
-rw-r--r--arch/powerpc/math-emu/fnmadds.c5
-rw-r--r--arch/powerpc/math-emu/fnmsub.c5
-rw-r--r--arch/powerpc/math-emu/fnmsubs.c5
-rw-r--r--arch/powerpc/math-emu/fsqrt.c5
-rw-r--r--arch/powerpc/math-emu/fsqrts.c5
-rw-r--r--arch/powerpc/math-emu/fsub.c3
-rw-r--r--arch/powerpc/math-emu/fsubs.c3
-rw-r--r--arch/powerpc/math-emu/math_efp.c720
-rw-r--r--arch/powerpc/mm/Makefile10
-rw-r--r--arch/powerpc/mm/fault.c16
-rw-r--r--arch/powerpc/mm/hash_low_32.S111
-rw-r--r--arch/powerpc/mm/hugetlbpage.c22
-rw-r--r--arch/powerpc/mm/init_32.c6
-rw-r--r--arch/powerpc/mm/mem.c6
-rw-r--r--arch/powerpc/mm/mmu_context_32.c84
-rw-r--r--arch/powerpc/mm/mmu_context_64.c70
-rw-r--r--arch/powerpc/mm/mmu_context_hash32.c103
-rw-r--r--arch/powerpc/mm/mmu_context_hash64.c78
-rw-r--r--arch/powerpc/mm/mmu_context_nohash.c397
-rw-r--r--arch/powerpc/mm/mmu_decl.h65
-rw-r--r--arch/powerpc/mm/pgtable.c117
-rw-r--r--arch/powerpc/mm/pgtable_32.c56
-rw-r--r--arch/powerpc/mm/ppc_mmu_32.c10
-rw-r--r--arch/powerpc/mm/tlb_32.c190
-rw-r--r--arch/powerpc/mm/tlb_64.c297
-rw-r--r--arch/powerpc/mm/tlb_hash32.c194
-rw-r--r--arch/powerpc/mm/tlb_hash64.c211
-rw-r--r--arch/powerpc/mm/tlb_nohash.c209
-rw-r--r--arch/powerpc/mm/tlb_nohash_low.S166
-rw-r--r--arch/powerpc/platforms/40x/ep405.c2
-rw-r--r--arch/powerpc/platforms/40x/kilauea.c2
-rw-r--r--arch/powerpc/platforms/40x/ppc40x_simple.c2
-rw-r--r--arch/powerpc/platforms/44x/ebony.c2
-rw-r--r--arch/powerpc/platforms/44x/ppc44x_simple.c2
-rw-r--r--arch/powerpc/platforms/44x/sam440ep.c2
-rw-r--r--arch/powerpc/platforms/52xx/lite5200_pm.c1
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pci.c2
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pic.c237
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pic.h53
-rw-r--r--arch/powerpc/platforms/52xx/mpc52xx_pm.c3
-rw-r--r--arch/powerpc/platforms/82xx/pq2.c2
-rw-r--r--arch/powerpc/platforms/85xx/Makefile2
-rw-r--r--arch/powerpc/platforms/85xx/mpc85xx_ds.c11
-rw-r--r--arch/powerpc/platforms/85xx/mpc85xx_mds.c8
-rw-r--r--arch/powerpc/platforms/85xx/smp.c104
-rw-r--r--arch/powerpc/platforms/86xx/Kconfig2
-rw-r--r--arch/powerpc/platforms/86xx/Makefile3
-rw-r--r--arch/powerpc/platforms/86xx/gef_gpio.c143
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype12
-rw-r--r--arch/powerpc/platforms/cell/Kconfig23
-rw-r--r--arch/powerpc/platforms/cell/Makefile17
-rw-r--r--arch/powerpc/platforms/cell/celleb_setup.c9
-rw-r--r--arch/powerpc/platforms/cell/iommu.c5
-rw-r--r--arch/powerpc/platforms/cell/qpace_setup.c152
-rw-r--r--arch/powerpc/platforms/cell/setup.c6
-rw-r--r--arch/powerpc/platforms/cell/spu_priv1_mmio.c6
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c27
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c8
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c4
-rw-r--r--arch/powerpc/platforms/chrp/pci.c3
-rw-r--r--arch/powerpc/platforms/embedded6xx/c2k.c6
-rw-r--r--arch/powerpc/platforms/embedded6xx/prpmc2800.c6
-rw-r--r--arch/powerpc/platforms/iseries/Kconfig1
-rw-r--r--arch/powerpc/platforms/maple/setup.c6
-rw-r--r--arch/powerpc/platforms/powermac/cpufreq_32.c2
-rw-r--r--arch/powerpc/platforms/powermac/pci.c6
-rw-r--r--arch/powerpc/platforms/powermac/setup.c10
-rw-r--r--arch/powerpc/platforms/powermac/sleep.S5
-rw-r--r--arch/powerpc/platforms/powermac/smp.c2
-rw-r--r--arch/powerpc/platforms/ps3/device-init.c33
-rw-r--r--arch/powerpc/platforms/ps3/mm.c2
-rw-r--r--arch/powerpc/platforms/ps3/setup.c8
-rw-r--r--arch/powerpc/platforms/ps3/system-bus.c38
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig2
-rw-r--r--arch/powerpc/platforms/pseries/cmm.c29
-rw-r--r--arch/powerpc/platforms/pseries/eeh.c44
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-cpu.c2
-rw-r--r--arch/powerpc/platforms/pseries/pci_dlpar.c163
-rw-r--r--arch/powerpc/platforms/pseries/phyp_dump.c5
-rw-r--r--arch/powerpc/platforms/pseries/rtasd.c4
-rw-r--r--arch/powerpc/platforms/pseries/xics.c43
-rw-r--r--arch/powerpc/sysdev/bestcomm/ata.c3
-rw-r--r--arch/powerpc/sysdev/bestcomm/ata.h19
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm.c7
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm.h61
-rw-r--r--arch/powerpc/sysdev/bestcomm/bestcomm_priv.h20
-rw-r--r--arch/powerpc/sysdev/dcr-low.S8
-rw-r--r--arch/powerpc/sysdev/dcr.c5
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c4
-rw-r--r--arch/powerpc/sysdev/fsl_soc.c241
-rw-r--r--arch/powerpc/sysdev/grackle.c2
-rw-r--r--arch/powerpc/sysdev/mpic.c32
-rw-r--r--arch/powerpc/sysdev/ppc4xx_pci.c306
-rw-r--r--arch/powerpc/sysdev/qe_lib/qe.c3
-rw-r--r--arch/powerpc/sysdev/qe_lib/ucc.c4
-rw-r--r--arch/powerpc/xmon/Makefile2
-rw-r--r--arch/powerpc/xmon/setjmp.S74
-rw-r--r--arch/powerpc/xmon/xmon.c5
-rw-r--r--arch/s390/Kconfig41
-rw-r--r--arch/s390/Makefile1
-rw-r--r--arch/s390/appldata/appldata.h4
-rw-r--r--arch/s390/appldata/appldata_base.c12
-rw-r--r--arch/s390/appldata/appldata_net_sum.c4
-rw-r--r--arch/s390/appldata/appldata_os.c21
-rw-r--r--arch/s390/crypto/aes_s390.c14
-rw-r--r--arch/s390/hypfs/hypfs_diag.c10
-rw-r--r--arch/s390/hypfs/inode.c18
-rw-r--r--arch/s390/include/asm/auxvec.h2
-rw-r--r--arch/s390/include/asm/bug.h5
-rw-r--r--arch/s390/include/asm/byteorder.h72
-rw-r--r--arch/s390/include/asm/elf.h16
-rw-r--r--arch/s390/include/asm/fcx.h4
-rw-r--r--arch/s390/include/asm/ftrace.h8
-rw-r--r--arch/s390/include/asm/isc.h1
-rw-r--r--arch/s390/include/asm/kvm_virtio.h4
-rw-r--r--arch/s390/include/asm/mmu.h1
-rw-r--r--arch/s390/include/asm/page.h2
-rw-r--r--arch/s390/include/asm/pgalloc.h5
-rw-r--r--arch/s390/include/asm/processor.h4
-rw-r--r--arch/s390/include/asm/qdio.h16
-rw-r--r--arch/s390/include/asm/sigp.h1
-rw-r--r--arch/s390/include/asm/smp.h5
-rw-r--r--arch/s390/include/asm/sysinfo.h11
-rw-r--r--arch/s390/include/asm/system.h24
-rw-r--r--arch/s390/include/asm/timer.h9
-rw-r--r--arch/s390/include/asm/topology.h2
-rw-r--r--arch/s390/include/asm/vdso.h39
-rw-r--r--arch/s390/kernel/Makefile18
-rw-r--r--arch/s390/kernel/asm-offsets.c15
-rw-r--r--arch/s390/kernel/compat_linux.c28
-rw-r--r--arch/s390/kernel/cpcmd.c7
-rw-r--r--arch/s390/kernel/debug.c39
-rw-r--r--arch/s390/kernel/entry.S58
-rw-r--r--arch/s390/kernel/entry64.S58
-rw-r--r--arch/s390/kernel/head.S49
-rw-r--r--arch/s390/kernel/head31.S28
-rw-r--r--arch/s390/kernel/head64.S24
-rw-r--r--arch/s390/kernel/mcount.S56
-rw-r--r--arch/s390/kernel/processor.c98
-rw-r--r--arch/s390/kernel/ptrace.c10
-rw-r--r--arch/s390/kernel/s390_ksyms.c5
-rw-r--r--arch/s390/kernel/setup.c185
-rw-r--r--arch/s390/kernel/smp.c201
-rw-r--r--arch/s390/kernel/time.c278
-rw-r--r--arch/s390/kernel/topology.c46
-rw-r--r--arch/s390/kernel/vdso.c234
-rw-r--r--arch/s390/kernel/vdso32/Makefile55
-rw-r--r--arch/s390/kernel/vdso32/clock_getres.S39
-rw-r--r--arch/s390/kernel/vdso32/clock_gettime.S128
-rw-r--r--arch/s390/kernel/vdso32/gettimeofday.S82
-rw-r--r--arch/s390/kernel/vdso32/note.S12
-rw-r--r--arch/s390/kernel/vdso32/vdso32.lds.S138
-rw-r--r--arch/s390/kernel/vdso32/vdso32_wrapper.S13
-rw-r--r--arch/s390/kernel/vdso64/Makefile55
-rw-r--r--arch/s390/kernel/vdso64/clock_getres.S39
-rw-r--r--arch/s390/kernel/vdso64/clock_gettime.S89
-rw-r--r--arch/s390/kernel/vdso64/gettimeofday.S56
-rw-r--r--arch/s390/kernel/vdso64/note.S12
-rw-r--r--arch/s390/kernel/vdso64/vdso64.lds.S138
-rw-r--r--arch/s390/kernel/vdso64/vdso64_wrapper.S13
-rw-r--r--arch/s390/kernel/vtime.c11
-rw-r--r--arch/s390/mm/extmem.c106
-rw-r--r--arch/s390/mm/pgtable.c4
-rw-r--r--arch/sh/Kconfig83
-rw-r--r--arch/sh/Kconfig.debug104
-rw-r--r--arch/sh/Makefile33
-rw-r--r--arch/sh/boards/Kconfig11
-rw-r--r--arch/sh/boards/Makefile1
-rw-r--r--arch/sh/boards/board-ap325rxa.c7
-rw-r--r--arch/sh/boards/board-rsk7203.c179
-rw-r--r--arch/sh/boards/board-shmin.c14
-rw-r--r--arch/sh/boards/mach-cayman/Makefile1
-rw-r--r--arch/sh/boards/mach-cayman/irq.c35
-rw-r--r--arch/sh/boards/mach-cayman/led.c51
-rw-r--r--arch/sh/boards/mach-dreamcast/irq.c192
-rw-r--r--arch/sh/boards/mach-dreamcast/setup.c5
-rw-r--r--arch/sh/boards/mach-edosk7705/Makefile1
-rw-r--r--arch/sh/boards/mach-edosk7705/io.c61
-rw-r--r--arch/sh/boards/mach-edosk7705/setup.c9
-rw-r--r--arch/sh/boards/mach-hp6xx/pm.c78
-rw-r--r--arch/sh/boards/mach-microdev/Makefile5
-rw-r--r--arch/sh/boards/mach-microdev/fdc37c93xapm.c160
-rw-r--r--arch/sh/boards/mach-microdev/irq.c36
-rw-r--r--arch/sh/boards/mach-microdev/led.c101
-rw-r--r--arch/sh/boards/mach-microdev/setup.c196
-rw-r--r--arch/sh/boards/mach-migor/setup.c21
-rw-r--r--arch/sh/boards/mach-rsk/Kconfig18
-rw-r--r--arch/sh/boards/mach-rsk/Makefile2
-rw-r--r--arch/sh/boards/mach-rsk/devices-rsk7203.c103
-rw-r--r--arch/sh/boards/mach-rsk/setup.c106
-rw-r--r--arch/sh/boards/mach-se/7343/Makefile2
-rw-r--r--arch/sh/boards/mach-se/7343/io.c273
-rw-r--r--arch/sh/boards/mach-se/7343/setup.c126
-rw-r--r--arch/sh/boards/mach-se/770x/setup.c4
-rw-r--r--arch/sh/boards/mach-se/7721/setup.c7
-rw-r--r--arch/sh/boards/mach-se/7722/setup.c10
-rw-r--r--arch/sh/boards/mach-sh03/setup.c59
-rw-r--r--arch/sh/boards/mach-systemh/irq.c37
-rw-r--r--arch/sh/cchips/hd6446x/hd64461.c115
-rw-r--r--arch/sh/configs/edosk7705_defconfig438
-rw-r--r--arch/sh/configs/rsk7201_defconfig703
-rw-r--r--arch/sh/configs/rsk7203_defconfig65
-rw-r--r--arch/sh/configs/rts7751r2dplus_qemu_defconfig949
-rw-r--r--arch/sh/configs/se7343_defconfig303
-rw-r--r--arch/sh/drivers/dma/Makefile1
-rw-r--r--arch/sh/drivers/dma/dma-isa.c106
-rw-r--r--arch/sh/drivers/dma/dma-sh.c3
-rw-r--r--arch/sh/drivers/dma/dma-sh.h1
-rw-r--r--arch/sh/drivers/pci/ops-sh03.c3
-rw-r--r--arch/sh/drivers/pci/pci-sh7780.c12
-rw-r--r--arch/sh/include/asm/addrspace.h11
-rw-r--r--arch/sh/include/asm/bitops-grb.h3
-rw-r--r--arch/sh/include/asm/bitops-irq.h91
-rw-r--r--arch/sh/include/asm/bitops-llsc.h2
-rw-r--r--arch/sh/include/asm/bitops-op32.h142
-rw-r--r--arch/sh/include/asm/bitops.h9
-rw-r--r--arch/sh/include/asm/bugs.h2
-rw-r--r--arch/sh/include/asm/elf.h4
-rw-r--r--arch/sh/include/asm/ftrace.h28
-rw-r--r--arch/sh/include/asm/io.h4
-rw-r--r--arch/sh/include/asm/kgdb.h66
-rw-r--r--arch/sh/include/asm/machvec.h5
-rw-r--r--arch/sh/include/asm/mmu_context.h4
-rw-r--r--arch/sh/include/asm/mutex-llsc.h112
-rw-r--r--arch/sh/include/asm/mutex.h5
-rw-r--r--arch/sh/include/asm/pm.h17
-rw-r--r--arch/sh/include/asm/processor.h5
-rw-r--r--arch/sh/include/asm/processor_32.h12
-rw-r--r--arch/sh/include/asm/processor_64.h4
-rw-r--r--arch/sh/include/asm/ptrace.h2
-rw-r--r--arch/sh/include/asm/sh_bios.h1
-rw-r--r--arch/sh/include/asm/string_64.h21
-rw-r--r--arch/sh/include/asm/syscall_32.h2
-rw-r--r--arch/sh/include/asm/syscall_64.h76
-rw-r--r--arch/sh/include/asm/system.h2
-rw-r--r--arch/sh/include/asm/topology.h1
-rw-r--r--arch/sh/include/asm/unaligned-sh4a.h258
-rw-r--r--arch/sh/include/asm/unaligned.h7
-rw-r--r--arch/sh/include/cpu-sh3/cpu/gpio.h14
-rw-r--r--arch/sh/include/mach-common/mach/edosk7705.h31
-rw-r--r--arch/sh/include/mach-se/mach/mrshpc.h52
-rw-r--r--arch/sh/include/mach-se/mach/se.h18
-rw-r--r--arch/sh/include/mach-se/mach/se7343.h9
-rw-r--r--arch/sh/kernel/Makefile_3218
-rw-r--r--arch/sh/kernel/Makefile_645
-rw-r--r--arch/sh/kernel/cf-enabler.c168
-rw-r--r--arch/sh/kernel/cpu/clock.c10
-rw-r--r--arch/sh/kernel/cpu/init.c2
-rw-r--r--arch/sh/kernel/cpu/sh2a/Makefile3
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7201.c85
-rw-r--r--arch/sh/kernel/cpu/sh2a/probe.c7
-rw-r--r--arch/sh/kernel/cpu/sh2a/setup-sh7201.c331
-rw-r--r--arch/sh/kernel/cpu/sh3/entry.S14
-rw-r--r--arch/sh/kernel/cpu/sh3/ex.S2
-rw-r--r--arch/sh/kernel/cpu/sh4/softfloat.c73
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7722.c310
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7343.c37
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7366.c19
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7722.c50
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7723.c19
-rw-r--r--arch/sh/kernel/debugtraps.S13
-rw-r--r--arch/sh/kernel/disassemble.c573
-rw-r--r--arch/sh/kernel/entry-common.S54
-rw-r--r--arch/sh/kernel/ftrace.c133
-rw-r--r--arch/sh/kernel/head_32.S12
-rw-r--r--arch/sh/kernel/idle.c81
-rw-r--r--arch/sh/kernel/kgdb.c285
-rw-r--r--arch/sh/kernel/kgdb_jmp.S33
-rw-r--r--arch/sh/kernel/kgdb_stub.c1052
-rw-r--r--arch/sh/kernel/pm.c88
-rw-r--r--arch/sh/kernel/process_32.c81
-rw-r--r--arch/sh/kernel/process_64.c111
-rw-r--r--arch/sh/kernel/ptrace_64.c186
-rw-r--r--arch/sh/kernel/setup.c1
-rw-r--r--arch/sh/kernel/sh_bios.c65
-rw-r--r--arch/sh/kernel/sh_ksyms_32.c37
-rw-r--r--arch/sh/kernel/sh_ksyms_64.c5
-rw-r--r--arch/sh/kernel/signal_32.c1
-rw-r--r--arch/sh/kernel/signal_64.c154
-rw-r--r--arch/sh/kernel/sys_sh.c92
-rw-r--r--arch/sh/kernel/time_32.c12
-rw-r--r--arch/sh/kernel/time_64.c5
-rw-r--r--arch/sh/kernel/timers/timer-mtu2.c5
-rw-r--r--arch/sh/kernel/traps_32.c36
-rw-r--r--arch/sh/kernel/vsyscall/vsyscall.c3
-rw-r--r--arch/sh/lib/Makefile16
-rw-r--r--arch/sh/lib/ashiftrt.S149
-rw-r--r--arch/sh/lib/ashldi3.c29
-rw-r--r--arch/sh/lib/ashlsi3.S193
-rw-r--r--arch/sh/lib/ashrdi3.c31
-rw-r--r--arch/sh/lib/ashrsi3.S185
-rw-r--r--arch/sh/lib/libgcc.h26
-rw-r--r--arch/sh/lib/lshrdi3.c29
-rw-r--r--arch/sh/lib/lshrsi3.S193
-rw-r--r--arch/sh/lib/mcount.S90
-rw-r--r--arch/sh/lib/movmem.S238
-rw-r--r--arch/sh/lib/udiv_qrnnd.S81
-rw-r--r--arch/sh/lib/udivsi3.S87
-rw-r--r--arch/sh/lib/udivsi3_i4i-Os.S149
-rw-r--r--arch/sh/lib/udivsi3_i4i.S666
-rw-r--r--arch/sh/lib64/Makefile8
-rw-r--r--arch/sh/lib64/c-checksum.c4
-rw-r--r--arch/sh/lib64/memcpy.S201
-rw-r--r--arch/sh/lib64/memcpy.c81
-rw-r--r--arch/sh/lib64/memset.S91
-rw-r--r--arch/sh/lib64/sdivsi3.S131
-rw-r--r--arch/sh/lib64/strcpy.S97
-rw-r--r--arch/sh/lib64/strlen.S33
-rw-r--r--arch/sh/lib64/udivdi3.S120
-rw-r--r--arch/sh/lib64/udivsi3.S59
-rw-r--r--arch/sh/mm/Makefile_321
-rw-r--r--arch/sh/mm/Makefile_641
-rw-r--r--arch/sh/mm/asids-debugfs.c79
-rw-r--r--arch/sh/mm/consistent.c7
-rw-r--r--arch/sh/mm/fault_32.c17
-rw-r--r--arch/sh/mm/ioremap_32.c3
-rw-r--r--arch/sh/mm/mmap.c94
-rw-r--r--arch/sh/oprofile/Makefile13
-rw-r--r--arch/sh/oprofile/backtrace.c114
-rw-r--r--arch/sh/oprofile/common.c150
-rw-r--r--arch/sh/oprofile/op_impl.h33
-rw-r--r--arch/sh/oprofile/op_model_null.c23
-rw-r--r--arch/sh/oprofile/op_model_sh7750.c172
-rw-r--r--arch/sh/tools/mach-types2
-rw-r--r--arch/sparc/Kconfig525
-rw-r--r--arch/sparc/Kconfig.debug26
-rw-r--r--arch/sparc/Makefile97
-rw-r--r--arch/sparc/boot/.gitignore8
-rw-r--r--arch/sparc/boot/Makefile38
-rw-r--r--arch/sparc/boot/piggyback_32.c (renamed from arch/sparc/boot/piggyback.c)0
-rw-r--r--arch/sparc/boot/piggyback_64.c (renamed from arch/sparc64/boot/piggyback.c)0
-rw-r--r--arch/sparc/configs/sparc32_defconfig (renamed from arch/sparc/defconfig)0
-rw-r--r--arch/sparc/configs/sparc64_defconfig (renamed from arch/sparc64/defconfig)0
-rw-r--r--arch/sparc/include/asm/Kbuild2
-rw-r--r--arch/sparc/include/asm/asm.h40
-rw-r--r--arch/sparc/include/asm/atomic_64.h7
-rw-r--r--arch/sparc/include/asm/bitops_64.h5
-rw-r--r--arch/sparc/include/asm/device.h12
-rw-r--r--arch/sparc/include/asm/hypervisor.h24
-rw-r--r--arch/sparc/include/asm/irq_32.h1
-rw-r--r--arch/sparc/include/asm/irq_64.h3
-rw-r--r--arch/sparc/include/asm/irqflags_64.h6
-rw-r--r--arch/sparc/include/asm/module.h32
-rw-r--r--arch/sparc/include/asm/module_32.h7
-rw-r--r--arch/sparc/include/asm/module_64.h7
-rw-r--r--arch/sparc/include/asm/openprom_32.h6
-rw-r--r--arch/sparc/include/asm/oplib_32.h8
-rw-r--r--arch/sparc/include/asm/pil.h9
-rw-r--r--arch/sparc/include/asm/ptrace_32.h10
-rw-r--r--arch/sparc/include/asm/ptrace_64.h9
-rw-r--r--arch/sparc/include/asm/scatterlist.h33
-rw-r--r--arch/sparc/include/asm/scatterlist_32.h26
-rw-r--r--arch/sparc/include/asm/scatterlist_64.h27
-rw-r--r--arch/sparc/include/asm/sections.h16
-rw-r--r--arch/sparc/include/asm/sections_32.h6
-rw-r--r--arch/sparc/include/asm/sections_64.h9
-rw-r--r--arch/sparc/include/asm/spinlock_64.h31
-rw-r--r--arch/sparc/include/asm/spitfire.h4
-rw-r--r--arch/sparc/include/asm/system_32.h5
-rw-r--r--arch/sparc/include/asm/system_64.h46
-rw-r--r--arch/sparc/include/asm/topology_64.h13
-rw-r--r--arch/sparc/include/asm/tsb.h6
-rw-r--r--arch/sparc/include/asm/ttable.h15
-rw-r--r--arch/sparc/include/asm/unistd.h446
-rw-r--r--arch/sparc/include/asm/unistd_32.h385
-rw-r--r--arch/sparc/include/asm/unistd_64.h380
-rw-r--r--arch/sparc/kernel/.gitignore1
-rw-r--r--arch/sparc/kernel/Makefile117
-rw-r--r--arch/sparc/kernel/asm-offsets.c19
-rw-r--r--arch/sparc/kernel/audit.c (renamed from arch/sparc64/kernel/audit.c)0
-rw-r--r--arch/sparc/kernel/auxio_32.c (renamed from arch/sparc/kernel/auxio.c)0
-rw-r--r--arch/sparc/kernel/auxio_64.c149
-rw-r--r--arch/sparc/kernel/central.c (renamed from arch/sparc64/kernel/central.c)0
-rw-r--r--arch/sparc/kernel/cherrs.S579
-rw-r--r--arch/sparc/kernel/chmc.c (renamed from arch/sparc64/kernel/chmc.c)0
-rw-r--r--arch/sparc/kernel/compat_audit.c43
-rw-r--r--arch/sparc/kernel/cpu.c413
-rw-r--r--arch/sparc/kernel/devices.c2
-rw-r--r--arch/sparc/kernel/ds.c (renamed from arch/sparc64/kernel/ds.c)0
-rw-r--r--arch/sparc/kernel/dtlb_miss.S (renamed from arch/sparc64/kernel/dtlb_miss.S)0
-rw-r--r--arch/sparc/kernel/dtlb_prot.S (renamed from arch/sparc64/kernel/dtlb_prot.S)0
-rw-r--r--arch/sparc/kernel/ebus.c (renamed from arch/sparc64/kernel/ebus.c)0
-rw-r--r--arch/sparc/kernel/entry.h229
-rw-r--r--arch/sparc/kernel/etrap_32.S (renamed from arch/sparc/kernel/etrap.S)0
-rw-r--r--arch/sparc/kernel/etrap_64.S236
-rw-r--r--arch/sparc/kernel/fpu_traps.S (renamed from arch/sparc64/kernel/fpu_traps.S)0
-rw-r--r--arch/sparc/kernel/ftrace.c (renamed from arch/sparc64/kernel/ftrace.c)0
-rw-r--r--arch/sparc/kernel/getsetcc.S (renamed from arch/sparc64/kernel/getsetcc.S)0
-rw-r--r--arch/sparc/kernel/head.S1295
-rw-r--r--arch/sparc/kernel/head_32.S1295
-rw-r--r--arch/sparc/kernel/head_64.S900
-rw-r--r--arch/sparc/kernel/helpers.S (renamed from arch/sparc64/kernel/helpers.S)0
-rw-r--r--arch/sparc/kernel/hvapi.c (renamed from arch/sparc64/kernel/hvapi.c)0
-rw-r--r--arch/sparc/kernel/hvcalls.S800
-rw-r--r--arch/sparc/kernel/hvtramp.S140
-rw-r--r--arch/sparc/kernel/idprom.c67
-rw-r--r--arch/sparc/kernel/init_task.c3
-rw-r--r--arch/sparc/kernel/iommu.c (renamed from arch/sparc64/kernel/iommu.c)0
-rw-r--r--arch/sparc/kernel/iommu_common.h (renamed from arch/sparc64/kernel/iommu_common.h)0
-rw-r--r--arch/sparc/kernel/ioport.c4
-rw-r--r--arch/sparc/kernel/irq.c674
-rw-r--r--arch/sparc/kernel/irq_32.c675
-rw-r--r--arch/sparc/kernel/irq_64.c1104
-rw-r--r--arch/sparc/kernel/itlb_miss.S (renamed from arch/sparc64/kernel/itlb_miss.S)0
-rw-r--r--arch/sparc/kernel/ivec.S (renamed from arch/sparc64/kernel/ivec.S)0
-rw-r--r--arch/sparc/kernel/kernel.h31
-rw-r--r--arch/sparc/kernel/kgdb_32.c (renamed from arch/sparc/kernel/kgdb.c)0
-rw-r--r--arch/sparc/kernel/kgdb_64.c (renamed from arch/sparc64/kernel/kgdb.c)0
-rw-r--r--arch/sparc/kernel/kprobes.c (renamed from arch/sparc64/kernel/kprobes.c)0
-rw-r--r--arch/sparc/kernel/kstack.h (renamed from arch/sparc64/kernel/kstack.h)0
-rw-r--r--arch/sparc/kernel/ktlb.S (renamed from arch/sparc64/kernel/ktlb.S)0
-rw-r--r--arch/sparc/kernel/ldc.c (renamed from arch/sparc64/kernel/ldc.c)0
-rw-r--r--arch/sparc/kernel/mdesc.c917
-rw-r--r--arch/sparc/kernel/misctrap.S (renamed from arch/sparc64/kernel/misctrap.S)0
-rw-r--r--arch/sparc/kernel/module.c140
-rw-r--r--arch/sparc/kernel/muldiv.c5
-rw-r--r--arch/sparc/kernel/of_device_32.c (renamed from arch/sparc/kernel/of_device.c)0
-rw-r--r--arch/sparc/kernel/of_device_64.c898
-rw-r--r--arch/sparc/kernel/pci.c (renamed from arch/sparc64/kernel/pci.c)0
-rw-r--r--arch/sparc/kernel/pci_common.c (renamed from arch/sparc64/kernel/pci_common.c)0
-rw-r--r--arch/sparc/kernel/pci_fire.c (renamed from arch/sparc64/kernel/pci_fire.c)0
-rw-r--r--arch/sparc/kernel/pci_impl.h (renamed from arch/sparc64/kernel/pci_impl.h)0
-rw-r--r--arch/sparc/kernel/pci_msi.c447
-rw-r--r--arch/sparc/kernel/pci_psycho.c (renamed from arch/sparc64/kernel/pci_psycho.c)0
-rw-r--r--arch/sparc/kernel/pci_sabre.c (renamed from arch/sparc64/kernel/pci_sabre.c)0
-rw-r--r--arch/sparc/kernel/pci_schizo.c (renamed from arch/sparc64/kernel/pci_schizo.c)0
-rw-r--r--arch/sparc/kernel/pci_sun4v.c (renamed from arch/sparc64/kernel/pci_sun4v.c)0
-rw-r--r--arch/sparc/kernel/pci_sun4v.h (renamed from arch/sparc64/kernel/pci_sun4v.h)0
-rw-r--r--arch/sparc/kernel/pci_sun4v_asm.S (renamed from arch/sparc64/kernel/pci_sun4v_asm.S)0
-rw-r--r--arch/sparc/kernel/pcic.c2
-rw-r--r--arch/sparc/kernel/pmc.c18
-rw-r--r--arch/sparc/kernel/power.c (renamed from arch/sparc64/kernel/power.c)0
-rw-r--r--arch/sparc/kernel/process.c709
-rw-r--r--arch/sparc/kernel/process_32.c707
-rw-r--r--arch/sparc/kernel/process_64.c (renamed from arch/sparc64/kernel/process.c)0
-rw-r--r--arch/sparc/kernel/prom.c566
-rw-r--r--arch/sparc/kernel/prom.h29
-rw-r--r--arch/sparc/kernel/prom_32.c295
-rw-r--r--arch/sparc/kernel/prom_64.c571
-rw-r--r--arch/sparc/kernel/prom_common.c326
-rw-r--r--arch/sparc/kernel/prom_irqtrans.c842
-rw-r--r--arch/sparc/kernel/psycho_common.c (renamed from arch/sparc64/kernel/psycho_common.c)0
-rw-r--r--arch/sparc/kernel/psycho_common.h (renamed from arch/sparc64/kernel/psycho_common.h)0
-rw-r--r--arch/sparc/kernel/ptrace_32.c (renamed from arch/sparc/kernel/ptrace.c)0
-rw-r--r--arch/sparc/kernel/ptrace_64.c (renamed from arch/sparc64/kernel/ptrace.c)0
-rw-r--r--arch/sparc/kernel/reboot.c (renamed from arch/sparc64/kernel/reboot.c)0
-rw-r--r--arch/sparc/kernel/rtrap_32.S (renamed from arch/sparc/kernel/rtrap.S)0
-rw-r--r--arch/sparc/kernel/rtrap_64.S450
-rw-r--r--arch/sparc/kernel/sbus.c (renamed from arch/sparc64/kernel/sbus.c)0
-rw-r--r--arch/sparc/kernel/setup.c423
-rw-r--r--arch/sparc/kernel/setup_32.c422
-rw-r--r--arch/sparc/kernel/setup_64.c429
-rw-r--r--arch/sparc/kernel/signal32.c (renamed from arch/sparc64/kernel/signal32.c)0
-rw-r--r--arch/sparc/kernel/signal_32.c (renamed from arch/sparc/kernel/signal.c)0
-rw-r--r--arch/sparc/kernel/signal_64.c (renamed from arch/sparc64/kernel/signal.c)0
-rw-r--r--arch/sparc/kernel/smp_32.c (renamed from arch/sparc/kernel/smp.c)0
-rw-r--r--arch/sparc/kernel/smp_64.c1408
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c257
-rw-r--r--arch/sparc/kernel/sparc_ksyms_32.c253
-rw-r--r--arch/sparc/kernel/sparc_ksyms_64.c284
-rw-r--r--arch/sparc/kernel/spiterrs.S245
-rw-r--r--arch/sparc/kernel/sstate.c (renamed from arch/sparc64/kernel/sstate.c)0
-rw-r--r--arch/sparc/kernel/stacktrace.c64
-rw-r--r--arch/sparc/kernel/starfire.c (renamed from arch/sparc64/kernel/starfire.c)0
-rw-r--r--arch/sparc/kernel/sun4c_irq.c2
-rw-r--r--arch/sparc/kernel/sun4d_irq.c3
-rw-r--r--arch/sparc/kernel/sun4m_irq.c2
-rw-r--r--arch/sparc/kernel/sun4v_ivec.S341
-rw-r--r--arch/sparc/kernel/sun4v_tlb_miss.S (renamed from arch/sparc64/kernel/sun4v_tlb_miss.S)0
-rw-r--r--arch/sparc/kernel/sys32.S (renamed from arch/sparc64/kernel/sys32.S)0
-rw-r--r--arch/sparc/kernel/sys_sparc32.c (renamed from arch/sparc64/kernel/sys_sparc32.c)0
-rw-r--r--arch/sparc/kernel/sys_sparc_32.c (renamed from arch/sparc/kernel/sys_sparc.c)0
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c (renamed from arch/sparc64/kernel/sys_sparc.c)0
-rw-r--r--arch/sparc/kernel/syscalls.S (renamed from arch/sparc64/kernel/syscalls.S)0
-rw-r--r--arch/sparc/kernel/sysfs.c314
-rw-r--r--arch/sparc/kernel/systbls.h (renamed from arch/sparc64/kernel/systbls.h)0
-rw-r--r--arch/sparc/kernel/systbls_32.S (renamed from arch/sparc/kernel/systbls.S)0
-rw-r--r--arch/sparc/kernel/systbls_64.S (renamed from arch/sparc64/kernel/systbls.S)0
-rw-r--r--arch/sparc/kernel/time_32.c (renamed from arch/sparc/kernel/time.c)0
-rw-r--r--arch/sparc/kernel/time_64.c (renamed from arch/sparc64/kernel/time.c)0
-rw-r--r--arch/sparc/kernel/trampoline_32.S (renamed from arch/sparc/kernel/trampoline.S)0
-rw-r--r--arch/sparc/kernel/trampoline_64.S417
-rw-r--r--arch/sparc/kernel/traps.c490
-rw-r--r--arch/sparc/kernel/traps_32.c462
-rw-r--r--arch/sparc/kernel/traps_64.c2600
-rw-r--r--arch/sparc/kernel/tsb.S552
-rw-r--r--arch/sparc/kernel/ttable.S266
-rw-r--r--arch/sparc/kernel/una_asm_32.S (renamed from arch/sparc/kernel/una_asm.S)0
-rw-r--r--arch/sparc/kernel/una_asm_64.S (renamed from arch/sparc64/kernel/una_asm.S)0
-rw-r--r--arch/sparc/kernel/unaligned_32.c (renamed from arch/sparc/kernel/unaligned.c)0
-rw-r--r--arch/sparc/kernel/unaligned_64.c (renamed from arch/sparc64/kernel/unaligned.c)0
-rw-r--r--arch/sparc/kernel/us2e_cpufreq.c (renamed from arch/sparc64/kernel/us2e_cpufreq.c)0
-rw-r--r--arch/sparc/kernel/us3_cpufreq.c (renamed from arch/sparc64/kernel/us3_cpufreq.c)0
-rw-r--r--arch/sparc/kernel/utrap.S (renamed from arch/sparc64/kernel/utrap.S)0
-rw-r--r--arch/sparc/kernel/vio.c (renamed from arch/sparc64/kernel/vio.c)0
-rw-r--r--arch/sparc/kernel/viohs.c (renamed from arch/sparc64/kernel/viohs.c)0
-rw-r--r--arch/sparc/kernel/visemul.c (renamed from arch/sparc64/kernel/visemul.c)0
-rw-r--r--arch/sparc/kernel/vmlinux.lds.S109
-rw-r--r--arch/sparc/kernel/winfixup.S (renamed from arch/sparc64/kernel/winfixup.S)0
-rw-r--r--arch/sparc/lib/GENbzero.S (renamed from arch/sparc64/lib/GENbzero.S)0
-rw-r--r--arch/sparc/lib/GENcopy_from_user.S (renamed from arch/sparc64/lib/GENcopy_from_user.S)0
-rw-r--r--arch/sparc/lib/GENcopy_to_user.S (renamed from arch/sparc64/lib/GENcopy_to_user.S)0
-rw-r--r--arch/sparc/lib/GENmemcpy.S (renamed from arch/sparc64/lib/GENmemcpy.S)0
-rw-r--r--arch/sparc/lib/GENpage.S (renamed from arch/sparc64/lib/GENpage.S)0
-rw-r--r--arch/sparc/lib/GENpatch.S (renamed from arch/sparc64/lib/GENpatch.S)0
-rw-r--r--arch/sparc/lib/Makefile47
-rw-r--r--arch/sparc/lib/NG2copy_from_user.S (renamed from arch/sparc64/lib/NG2copy_from_user.S)0
-rw-r--r--arch/sparc/lib/NG2copy_to_user.S (renamed from arch/sparc64/lib/NG2copy_to_user.S)0
-rw-r--r--arch/sparc/lib/NG2memcpy.S (renamed from arch/sparc64/lib/NG2memcpy.S)0
-rw-r--r--arch/sparc/lib/NG2page.S (renamed from arch/sparc64/lib/NG2page.S)0
-rw-r--r--arch/sparc/lib/NG2patch.S (renamed from arch/sparc64/lib/NG2patch.S)0
-rw-r--r--arch/sparc/lib/NGbzero.S (renamed from arch/sparc64/lib/NGbzero.S)0
-rw-r--r--arch/sparc/lib/NGcopy_from_user.S (renamed from arch/sparc64/lib/NGcopy_from_user.S)0
-rw-r--r--arch/sparc/lib/NGcopy_to_user.S (renamed from arch/sparc64/lib/NGcopy_to_user.S)0
-rw-r--r--arch/sparc/lib/NGmemcpy.S (renamed from arch/sparc64/lib/NGmemcpy.S)0
-rw-r--r--arch/sparc/lib/NGpage.S (renamed from arch/sparc64/lib/NGpage.S)0
-rw-r--r--arch/sparc/lib/NGpatch.S (renamed from arch/sparc64/lib/NGpatch.S)0
-rw-r--r--arch/sparc/lib/PeeCeeI.c (renamed from arch/sparc64/lib/PeeCeeI.c)0
-rw-r--r--arch/sparc/lib/U1copy_from_user.S (renamed from arch/sparc64/lib/U1copy_from_user.S)0
-rw-r--r--arch/sparc/lib/U1copy_to_user.S (renamed from arch/sparc64/lib/U1copy_to_user.S)0
-rw-r--r--arch/sparc/lib/U1memcpy.S (renamed from arch/sparc64/lib/U1memcpy.S)0
-rw-r--r--arch/sparc/lib/U3copy_from_user.S (renamed from arch/sparc64/lib/U3copy_from_user.S)0
-rw-r--r--arch/sparc/lib/U3copy_to_user.S (renamed from arch/sparc64/lib/U3copy_to_user.S)0
-rw-r--r--arch/sparc/lib/U3memcpy.S (renamed from arch/sparc64/lib/U3memcpy.S)0
-rw-r--r--arch/sparc/lib/U3patch.S (renamed from arch/sparc64/lib/U3patch.S)0
-rw-r--r--arch/sparc/lib/VISsave.S (renamed from arch/sparc64/lib/VISsave.S)0
-rw-r--r--arch/sparc/lib/atomic_32.S (renamed from arch/sparc/lib/atomic.S)0
-rw-r--r--arch/sparc/lib/atomic_64.S138
-rw-r--r--arch/sparc/lib/bitops.S141
-rw-r--r--arch/sparc/lib/bzero.S (renamed from arch/sparc64/lib/bzero.S)0
-rw-r--r--arch/sparc/lib/checksum_32.S (renamed from arch/sparc/lib/checksum.S)0
-rw-r--r--arch/sparc/lib/checksum_64.S (renamed from arch/sparc64/lib/checksum.S)0
-rw-r--r--arch/sparc/lib/clear_page.S (renamed from arch/sparc64/lib/clear_page.S)0
-rw-r--r--arch/sparc/lib/copy_in_user.S (renamed from arch/sparc64/lib/copy_in_user.S)0
-rw-r--r--arch/sparc/lib/copy_page.S (renamed from arch/sparc64/lib/copy_page.S)0
-rw-r--r--arch/sparc/lib/csum_copy.S (renamed from arch/sparc64/lib/csum_copy.S)0
-rw-r--r--arch/sparc/lib/csum_copy_from_user.S (renamed from arch/sparc64/lib/csum_copy_from_user.S)0
-rw-r--r--arch/sparc/lib/csum_copy_to_user.S (renamed from arch/sparc64/lib/csum_copy_to_user.S)0
-rw-r--r--arch/sparc/lib/ipcsum.S (renamed from arch/sparc64/lib/ipcsum.S)0
-rw-r--r--arch/sparc/lib/mcount.S (renamed from arch/sparc64/lib/mcount.S)0
-rw-r--r--arch/sparc/lib/memcmp.S329
-rw-r--r--arch/sparc/lib/memmove.S (renamed from arch/sparc64/lib/memmove.S)0
-rw-r--r--arch/sparc/lib/memscan_32.S (renamed from arch/sparc/lib/memscan.S)0
-rw-r--r--arch/sparc/lib/memscan_64.S (renamed from arch/sparc64/lib/memscan.S)0
-rw-r--r--arch/sparc/lib/rwsem_32.S (renamed from arch/sparc/lib/rwsem.S)0
-rw-r--r--arch/sparc/lib/rwsem_64.S163
-rw-r--r--arch/sparc/lib/strlen.S79
-rw-r--r--arch/sparc/lib/strlen_user_32.S (renamed from arch/sparc/lib/strlen_user.S)0
-rw-r--r--arch/sparc/lib/strlen_user_64.S (renamed from arch/sparc64/lib/strlen_user.S)0
-rw-r--r--arch/sparc/lib/strncmp_32.S (renamed from arch/sparc/lib/strncmp.S)0
-rw-r--r--arch/sparc/lib/strncmp_64.S (renamed from arch/sparc64/lib/strncmp.S)0
-rw-r--r--arch/sparc/lib/strncpy_from_user_32.S (renamed from arch/sparc/lib/strncpy_from_user.S)0
-rw-r--r--arch/sparc/lib/strncpy_from_user_64.S (renamed from arch/sparc64/lib/strncpy_from_user.S)0
-rw-r--r--arch/sparc/lib/user_fixup.c (renamed from arch/sparc64/lib/user_fixup.c)0
-rw-r--r--arch/sparc/lib/xor.S (renamed from arch/sparc64/lib/xor.S)0
-rw-r--r--arch/sparc/math-emu/Makefile6
-rw-r--r--arch/sparc/math-emu/ashldi3.S36
-rw-r--r--arch/sparc/math-emu/math.c511
-rw-r--r--arch/sparc/math-emu/math_32.c511
-rw-r--r--arch/sparc/math-emu/math_64.c513
-rw-r--r--arch/sparc/math-emu/sfp-util_32.h (renamed from arch/sparc/math-emu/sfp-util.h)0
-rw-r--r--arch/sparc/math-emu/sfp-util_64.h (renamed from arch/sparc64/math-emu/sfp-util.h)0
-rw-r--r--arch/sparc/mm/Makefile24
-rw-r--r--arch/sparc/mm/fault_32.c (renamed from arch/sparc/mm/fault.c)0
-rw-r--r--arch/sparc/mm/fault_64.c (renamed from arch/sparc64/mm/fault.c)0
-rw-r--r--arch/sparc/mm/generic_32.c (renamed from arch/sparc/mm/generic.c)0
-rw-r--r--arch/sparc/mm/generic_64.c (renamed from arch/sparc64/mm/generic.c)0
-rw-r--r--arch/sparc/mm/hugetlbpage.c (renamed from arch/sparc64/mm/hugetlbpage.c)0
-rw-r--r--arch/sparc/mm/init.c524
-rw-r--r--arch/sparc/mm/init_32.c524
-rw-r--r--arch/sparc/mm/init_64.c2360
-rw-r--r--arch/sparc/mm/init_64.h (renamed from arch/sparc64/mm/init.h)0
-rw-r--r--arch/sparc/mm/io-unit.c8
-rw-r--r--arch/sparc/mm/iommu.c16
-rw-r--r--arch/sparc/mm/srmmu.c25
-rw-r--r--arch/sparc/mm/sun4c.c12
-rw-r--r--arch/sparc/mm/tlb.c (renamed from arch/sparc64/mm/tlb.c)0
-rw-r--r--arch/sparc/mm/tsb.c503
-rw-r--r--arch/sparc/mm/ultra.S767
-rw-r--r--arch/sparc/oprofile/init.c229
-rw-r--r--arch/sparc/prom/Makefile19
-rw-r--r--arch/sparc/prom/bootstr_32.c (renamed from arch/sparc/prom/bootstr.c)0
-rw-r--r--arch/sparc/prom/bootstr_64.c (renamed from arch/sparc64/prom/bootstr.c)0
-rw-r--r--arch/sparc/prom/cif.S (renamed from arch/sparc64/prom/cif.S)0
-rw-r--r--arch/sparc/prom/console_32.c (renamed from arch/sparc/prom/console.c)0
-rw-r--r--arch/sparc/prom/console_64.c (renamed from arch/sparc64/prom/console.c)0
-rw-r--r--arch/sparc/prom/devops_32.c (renamed from arch/sparc/prom/devops.c)0
-rw-r--r--arch/sparc/prom/devops_64.c (renamed from arch/sparc64/prom/devops.c)0
-rw-r--r--arch/sparc/prom/init_32.c (renamed from arch/sparc/prom/init.c)0
-rw-r--r--arch/sparc/prom/init_64.c (renamed from arch/sparc64/prom/init.c)0
-rw-r--r--arch/sparc/prom/misc.c131
-rw-r--r--arch/sparc/prom/misc_32.c129
-rw-r--r--arch/sparc/prom/misc_64.c (renamed from arch/sparc64/prom/misc.c)0
-rw-r--r--arch/sparc/prom/p1275.c (renamed from arch/sparc64/prom/p1275.c)0
-rw-r--r--arch/sparc/prom/printf.c5
-rw-r--r--arch/sparc/prom/tree.c346
-rw-r--r--arch/sparc/prom/tree_32.c346
-rw-r--r--arch/sparc/prom/tree_64.c (renamed from arch/sparc64/prom/tree.c)0
-rw-r--r--arch/sparc64/Kconfig433
-rw-r--r--arch/sparc64/Kconfig.debug44
-rw-r--r--arch/sparc64/Makefile48
-rw-r--r--arch/sparc64/boot/.gitignore4
-rw-r--r--arch/sparc64/boot/Makefile33
-rw-r--r--arch/sparc64/kernel/Makefile36
-rw-r--r--arch/sparc64/kernel/asm-offsets.c1
-rw-r--r--arch/sparc64/kernel/auxio.c167
-rw-r--r--arch/sparc64/kernel/cherrs.S579
-rw-r--r--arch/sparc64/kernel/compat_audit.c42
-rw-r--r--arch/sparc64/kernel/cpu.c166
-rw-r--r--arch/sparc64/kernel/entry.h195
-rw-r--r--arch/sparc64/kernel/etrap.S236
-rw-r--r--arch/sparc64/kernel/head.S900
-rw-r--r--arch/sparc64/kernel/hvcalls.S768
-rw-r--r--arch/sparc64/kernel/hvtramp.S139
-rw-r--r--arch/sparc64/kernel/idprom.c49
-rw-r--r--arch/sparc64/kernel/init_task.c35
-rw-r--r--arch/sparc64/kernel/irq.c1041
-rw-r--r--arch/sparc64/kernel/mdesc.c916
-rw-r--r--arch/sparc64/kernel/module.c213
-rw-r--r--arch/sparc64/kernel/of_device.c898
-rw-r--r--arch/sparc64/kernel/pci_msi.c447
-rw-r--r--arch/sparc64/kernel/prom.c1684
-rw-r--r--arch/sparc64/kernel/rtrap.S438
-rw-r--r--arch/sparc64/kernel/setup.c428
-rw-r--r--arch/sparc64/kernel/smp.c1409
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c283
-rw-r--r--arch/sparc64/kernel/spiterrs.S245
-rw-r--r--arch/sparc64/kernel/stacktrace.c50
-rw-r--r--arch/sparc64/kernel/sun4v_ivec.S341
-rw-r--r--arch/sparc64/kernel/sysfs.c313
-rw-r--r--arch/sparc64/kernel/trampoline.S421
-rw-r--r--arch/sparc64/kernel/traps.c2601
-rw-r--r--arch/sparc64/kernel/tsb.S552
-rw-r--r--arch/sparc64/kernel/ttable.S266
-rw-r--r--arch/sparc64/kernel/vmlinux.lds.S147
-rw-r--r--arch/sparc64/lib/Makefile23
-rw-r--r--arch/sparc64/lib/atomic.S164
-rw-r--r--arch/sparc64/lib/bitops.S165
-rw-r--r--arch/sparc64/lib/iomap.c48
-rw-r--r--arch/sparc64/lib/memcmp.S28
-rw-r--r--arch/sparc64/lib/rwsem.S170
-rw-r--r--arch/sparc64/lib/strlen.S80
-rw-r--r--arch/sparc64/math-emu/Makefile7
-rw-r--r--arch/sparc64/math-emu/math.c513
-rw-r--r--arch/sparc64/mm/Makefile9
-rw-r--r--arch/sparc64/mm/init.c2362
-rw-r--r--arch/sparc64/mm/tsb.c498
-rw-r--r--arch/sparc64/mm/ultra.S769
-rw-r--r--arch/sparc64/oprofile/Makefile9
-rw-r--r--arch/sparc64/oprofile/init.c23
-rw-r--r--arch/sparc64/prom/Makefile9
-rw-r--r--arch/sparc64/prom/printf.c47
-rw-r--r--arch/um/drivers/daemon_kern.c2
-rw-r--r--arch/um/drivers/mcast_kern.c2
-rw-r--r--arch/um/drivers/mconsole_kern.c3
-rw-r--r--arch/um/drivers/net_kern.c29
-rw-r--r--arch/um/drivers/pcap_kern.c2
-rw-r--r--arch/um/drivers/slip_kern.c2
-rw-r--r--arch/um/drivers/slirp_kern.c2
-rw-r--r--arch/um/drivers/vde_kern.c2
-rw-r--r--arch/um/os-Linux/drivers/ethertap_kern.c2
-rw-r--r--arch/um/os-Linux/drivers/tuntap_kern.c2
-rw-r--r--arch/x86/Kconfig88
-rw-r--r--arch/x86/Kconfig.debug20
-rw-r--r--arch/x86/boot/video-vga.c4
-rw-r--r--arch/x86/boot/video.c2
-rw-r--r--arch/x86/configs/i386_defconfig4
-rw-r--r--arch/x86/configs/x86_64_defconfig4
-rw-r--r--arch/x86/crypto/crc32c-intel.c121
-rw-r--r--arch/x86/ia32/ia32_aout.c2
-rw-r--r--arch/x86/ia32/ia32_signal.c109
-rw-r--r--arch/x86/include/asm/bitops.h10
-rw-r--r--arch/x86/include/asm/bug.h2
-rw-r--r--arch/x86/include/asm/byteorder.h74
-rw-r--r--arch/x86/include/asm/cpufeature.h5
-rw-r--r--arch/x86/include/asm/dma-mapping.h4
-rw-r--r--arch/x86/include/asm/ds.h248
-rw-r--r--arch/x86/include/asm/dwarf2.h97
-rw-r--r--arch/x86/include/asm/elf.h2
-rw-r--r--arch/x86/include/asm/ftrace.h29
-rw-r--r--arch/x86/include/asm/gart.h33
-rw-r--r--arch/x86/include/asm/hardirq_32.h2
-rw-r--r--arch/x86/include/asm/hardirq_64.h2
-rw-r--r--arch/x86/include/asm/hw_irq.h4
-rw-r--r--arch/x86/include/asm/hypervisor.h26
-rw-r--r--arch/x86/include/asm/ia32.h18
-rw-r--r--arch/x86/include/asm/idle.h5
-rw-r--r--arch/x86/include/asm/io.h37
-rw-r--r--arch/x86/include/asm/io_64.h2
-rw-r--r--arch/x86/include/asm/io_apic.h10
-rw-r--r--arch/x86/include/asm/iommu.h35
-rw-r--r--arch/x86/include/asm/irq.h4
-rw-r--r--arch/x86/include/asm/irq_regs_32.h2
-rw-r--r--arch/x86/include/asm/linkage.h60
-rw-r--r--arch/x86/include/asm/mmu_context_32.h13
-rw-r--r--arch/x86/include/asm/msr-index.h2
-rw-r--r--arch/x86/include/asm/msr.h15
-rw-r--r--arch/x86/include/asm/pci.h14
-rw-r--r--arch/x86/include/asm/pci_64.h1
-rw-r--r--arch/x86/include/asm/pgtable-2level.h50
-rw-r--r--arch/x86/include/asm/pgtable-3level.h1
-rw-r--r--arch/x86/include/asm/pgtable.h28
-rw-r--r--arch/x86/include/asm/pgtable_32.h9
-rw-r--r--arch/x86/include/asm/pgtable_64.h28
-rw-r--r--arch/x86/include/asm/prctl.h3
-rw-r--r--arch/x86/include/asm/processor.h17
-rw-r--r--arch/x86/include/asm/ptrace.h43
-rw-r--r--arch/x86/include/asm/setup.h4
-rw-r--r--arch/x86/include/asm/sigframe.h70
-rw-r--r--arch/x86/include/asm/signal.h6
-rw-r--r--arch/x86/include/asm/sparsemem.h2
-rw-r--r--arch/x86/include/asm/syscalls.h16
-rw-r--r--arch/x86/include/asm/system.h4
-rw-r--r--arch/x86/include/asm/thread_info.h7
-rw-r--r--arch/x86/include/asm/topology.h36
-rw-r--r--arch/x86/include/asm/trampoline.h7
-rw-r--r--arch/x86/include/asm/traps.h11
-rw-r--r--arch/x86/include/asm/tsc.h8
-rw-r--r--arch/x86/include/asm/uaccess.h4
-rw-r--r--arch/x86/include/asm/uv/bios.h34
-rw-r--r--arch/x86/include/asm/uv/uv_hub.h103
-rw-r--r--arch/x86/include/asm/vmware.h27
-rw-r--r--arch/x86/include/asm/xen/hypercall.h6
-rw-r--r--arch/x86/include/asm/xen/hypervisor.h39
-rw-r--r--arch/x86/include/asm/xen/page.h5
-rw-r--r--arch/x86/kernel/Makefile8
-rw-r--r--arch/x86/kernel/amd_iommu.c6
-rw-r--r--arch/x86/kernel/amd_iommu_init.c8
-rw-r--r--arch/x86/kernel/aperture_64.c5
-rw-r--r--arch/x86/kernel/apic.c22
-rw-r--r--arch/x86/kernel/asm-offsets_32.c2
-rw-r--r--arch/x86/kernel/asm-offsets_64.c4
-rw-r--r--arch/x86/kernel/bios_uv.c58
-rw-r--r--arch/x86/kernel/check.c161
-rw-r--r--arch/x86/kernel/cpu/Makefile6
-rw-r--r--arch/x86/kernel/cpu/addon_cpuid_features.c8
-rw-r--r--arch/x86/kernel/cpu/amd.c9
-rw-r--r--arch/x86/kernel/cpu/common.c8
-rw-r--r--arch/x86/kernel/cpu/hypervisor.c58
-rw-r--r--arch/x86/kernel/cpu/intel.c22
-rw-r--r--arch/x86/kernel/cpu/intel_cacheinfo.c17
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_64.c3
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_amd_64.c2
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_intel_64.c2
-rw-r--r--arch/x86/kernel/cpu/mtrr/main.c346
-rw-r--r--arch/x86/kernel/cpu/vmware.c112
-rw-r--r--arch/x86/kernel/ds.c899
-rw-r--r--arch/x86/kernel/e820.c16
-rw-r--r--arch/x86/kernel/early-quirks.c1
-rw-r--r--arch/x86/kernel/early_printk.c47
-rw-r--r--arch/x86/kernel/entry_32.S477
-rw-r--r--arch/x86/kernel/entry_64.S1417
-rw-r--r--arch/x86/kernel/genx2apic_uv_x.c111
-rw-r--r--arch/x86/kernel/head.c1
-rw-r--r--arch/x86/kernel/head32.c3
-rw-r--r--arch/x86/kernel/head64.c3
-rw-r--r--arch/x86/kernel/hpet.c11
-rw-r--r--arch/x86/kernel/init_task.c1
-rw-r--r--arch/x86/kernel/io_apic.c5
-rw-r--r--arch/x86/kernel/irq_64.c27
-rw-r--r--arch/x86/kernel/irqinit_32.c2
-rw-r--r--arch/x86/kernel/irqinit_64.c66
-rw-r--r--arch/x86/kernel/microcode_amd.c232
-rw-r--r--arch/x86/kernel/microcode_core.c25
-rw-r--r--arch/x86/kernel/microcode_intel.c8
-rw-r--r--arch/x86/kernel/nmi.c15
-rw-r--r--arch/x86/kernel/pci-dma.c24
-rw-r--r--arch/x86/kernel/pci-gart_64.c4
-rw-r--r--arch/x86/kernel/pci-swiotlb_64.c29
-rw-r--r--arch/x86/kernel/process.c3
-rw-r--r--arch/x86/kernel/process_32.c63
-rw-r--r--arch/x86/kernel/process_64.c54
-rw-r--r--arch/x86/kernel/ptrace.c464
-rw-r--r--arch/x86/kernel/quirks.c2
-rw-r--r--arch/x86/kernel/setup.c162
-rw-r--r--arch/x86/kernel/setup_percpu.c8
-rw-r--r--arch/x86/kernel/sigframe.h42
-rw-r--r--arch/x86/kernel/signal.c909
-rw-r--r--arch/x86/kernel/signal_32.c716
-rw-r--r--arch/x86/kernel/signal_64.c516
-rw-r--r--arch/x86/kernel/smp.c18
-rw-r--r--arch/x86/kernel/smpboot.c19
-rw-r--r--arch/x86/kernel/time_32.c2
-rw-r--r--arch/x86/kernel/time_64.c6
-rw-r--r--arch/x86/kernel/tlb_32.c13
-rw-r--r--arch/x86/kernel/tlb_64.c2
-rw-r--r--arch/x86/kernel/tlb_uv.c4
-rw-r--r--arch/x86/kernel/trampoline.c19
-rw-r--r--arch/x86/kernel/traps.c38
-rw-r--r--arch/x86/kernel/tsc.c42
-rw-r--r--arch/x86/kernel/tsc_sync.c8
-rw-r--r--arch/x86/kernel/vmi_32.c119
-rw-r--r--arch/x86/kernel/vmlinux_32.lds.S1
-rw-r--r--arch/x86/kernel/vmlinux_64.lds.S1
-rw-r--r--arch/x86/kernel/vsyscall_64.c9
-rw-r--r--arch/x86/lguest/boot.c3
-rw-r--r--arch/x86/lguest/i386_head.S15
-rw-r--r--arch/x86/mm/fault.c2
-rw-r--r--arch/x86/mm/init_32.c33
-rw-r--r--arch/x86/mm/init_64.c2
-rw-r--r--arch/x86/mm/ioremap.c3
-rw-r--r--arch/x86/mm/pat.c236
-rw-r--r--arch/x86/oprofile/op_model_amd.c89
-rw-r--r--arch/x86/pci/common.c17
-rw-r--r--arch/x86/scripts/strip-symbols1
-rw-r--r--arch/x86/vdso/vdso32-setup.c2
-rw-r--r--arch/x86/vdso/vma.c2
-rw-r--r--arch/x86/xen/enlighten.c17
-rw-r--r--arch/x86/xen/mmu.c17
-rw-r--r--arch/x86/xen/multicalls.c2
-rw-r--r--arch/x86/xen/setup.c9
-rw-r--r--arch/xtensa/platforms/iss/network.c21
-rw-r--r--block/Kconfig23
-rw-r--r--block/as-iosched.c10
-rw-r--r--block/blk-barrier.c120
-rw-r--r--block/blk-core.c63
-rw-r--r--block/blk-settings.c6
-rw-r--r--block/blk-softirq.c2
-rw-r--r--block/blk-sysfs.c7
-rw-r--r--block/blk-tag.c1
-rw-r--r--block/blk-timeout.c21
-rw-r--r--block/blk.h4
-rw-r--r--block/cfq-iosched.c26
-rw-r--r--block/compat_ioctl.c2
-rw-r--r--block/deadline-iosched.c6
-rw-r--r--block/elevator.c73
-rw-r--r--block/genhd.c23
-rw-r--r--block/ioctl.c2
-rw-r--r--block/noop-iosched.c2
-rw-r--r--block/scsi_ioctl.c4
-rw-r--r--crypto/Kconfig29
-rw-r--r--crypto/Makefile1
-rw-r--r--crypto/aes_generic.c1145
-rw-r--r--crypto/ahash.c38
-rw-r--r--crypto/ansi_cprng.c35
-rw-r--r--crypto/api.c121
-rw-r--r--crypto/async_tx/async_xor.c11
-rw-r--r--crypto/authenc.c3
-rw-r--r--crypto/camellia.c84
-rw-r--r--crypto/crc32c.c290
-rw-r--r--crypto/crypto_null.c64
-rw-r--r--crypto/des_generic.c5
-rw-r--r--crypto/fcrypt.c8
-rw-r--r--crypto/hmac.c10
-rw-r--r--crypto/internal.h2
-rw-r--r--crypto/md4.c56
-rw-r--r--crypto/md5.c50
-rw-r--r--crypto/michael_mic.c72
-rw-r--r--crypto/proc.c20
-rw-r--r--crypto/rmd128.c61
-rw-r--r--crypto/rmd160.c61
-rw-r--r--crypto/rmd256.c61
-rw-r--r--crypto/rmd320.c61
-rw-r--r--crypto/salsa20_generic.c75
-rw-r--r--crypto/sha1_generic.c56
-rw-r--r--crypto/sha256_generic.c104
-rw-r--r--crypto/sha512_generic.c127
-rw-r--r--crypto/shash.c508
-rw-r--r--crypto/testmgr.c76
-rw-r--r--crypto/testmgr.h2
-rw-r--r--crypto/tgr192.c135
-rw-r--r--crypto/wp512.c121
-rw-r--r--drivers/acpi/bus.c8
-rw-r--r--drivers/acpi/pci_irq.c56
-rw-r--r--drivers/acpi/processor_idle.c6
-rw-r--r--drivers/acpi/utilities/utglobal.c2
-rw-r--r--drivers/ata/Kconfig3
-rw-r--r--drivers/ata/ahci.c8
-rw-r--r--drivers/ata/ata_generic.c5
-rw-r--r--drivers/ata/ata_piix.c21
-rw-r--r--drivers/ata/libata-acpi.c19
-rw-r--r--drivers/ata/libata-core.c204
-rw-r--r--drivers/ata/libata-eh.c116
-rw-r--r--drivers/ata/libata-pmp.c22
-rw-r--r--drivers/ata/libata-scsi.c26
-rw-r--r--drivers/ata/pata_bf54x.c1
-rw-r--r--drivers/ata/pata_it821x.c34
-rw-r--r--drivers/ata/pata_ixp4xx_cf.c14
-rw-r--r--drivers/ata/pata_legacy.c18
-rw-r--r--drivers/ata/pata_mpc52xx.c546
-rw-r--r--drivers/ata/pata_oldpiix.c1
-rw-r--r--drivers/ata/pata_pdc2027x.c29
-rw-r--r--drivers/ata/pata_platform.c14
-rw-r--r--drivers/ata/pata_radisys.c1
-rw-r--r--drivers/ata/pata_rz1000.c16
-rw-r--r--drivers/ata/pata_scc.c1
-rw-r--r--drivers/ata/pata_serverworks.c1
-rw-r--r--drivers/ata/pata_sis.c1
-rw-r--r--drivers/ata/sata_mv.c1
-rw-r--r--drivers/ata/sata_sil.c98
-rw-r--r--drivers/atm/Kconfig6
-rw-r--r--drivers/atm/Makefile1
-rw-r--r--drivers/atm/solos-pci.c790
-rw-r--r--drivers/base/cpu.c44
-rw-r--r--drivers/block/aoe/aoe.h1
-rw-r--r--drivers/block/aoe/aoeblk.c2
-rw-r--r--drivers/block/aoe/aoecmd.c22
-rw-r--r--drivers/block/aoe/aoenet.c11
-rw-r--r--drivers/block/cciss.c93
-rw-r--r--drivers/block/cciss.h4
-rw-r--r--drivers/block/cciss_cmd.h3
-rw-r--r--drivers/block/loop.c45
-rw-r--r--drivers/block/nbd.c10
-rw-r--r--drivers/block/virtio_blk.c43
-rw-r--r--drivers/block/xen-blkfront.c8
-rw-r--r--drivers/bluetooth/Kconfig20
-rw-r--r--drivers/bluetooth/Makefile1
-rw-r--r--drivers/bluetooth/bcm203x.c9
-rw-r--r--drivers/bluetooth/bfusb.c11
-rw-r--r--drivers/bluetooth/bpa10x.c7
-rw-r--r--drivers/bluetooth/bt3c_cs.c8
-rw-r--r--drivers/bluetooth/btsdio.c6
-rw-r--r--drivers/bluetooth/btusb.c229
-rw-r--r--drivers/bluetooth/hci_bcsp.c5
-rw-r--r--drivers/bluetooth/hci_h4.c5
-rw-r--r--drivers/bluetooth/hci_ldisc.c9
-rw-r--r--drivers/bluetooth/hci_usb.c1136
-rw-r--r--drivers/bluetooth/hci_usb.h129
-rw-r--r--drivers/bluetooth/hci_vhci.c5
-rw-r--r--drivers/cdrom/cdrom.c703
-rw-r--r--drivers/char/Kconfig16
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/agp/intel-agp.c11
-rw-r--r--drivers/char/bsr.c84
-rw-r--r--drivers/char/ds1620.c25
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/char/hvc_console.c14
-rw-r--r--drivers/char/hvc_console.h2
-rw-r--r--drivers/char/hvc_iseries.c4
-rw-r--r--drivers/char/hvc_iucv.c850
-rw-r--r--drivers/char/hvc_udbg.c96
-rw-r--r--drivers/char/hvc_vio.c4
-rw-r--r--drivers/char/hvcs.c2
-rw-r--r--drivers/char/hvsi.c2
-rw-r--r--drivers/char/nvram.c264
-rw-r--r--drivers/char/nwflash.c8
-rw-r--r--drivers/char/tty_audit.c76
-rw-r--r--drivers/char/tty_io.c1
-rw-r--r--drivers/char/virtio_console.c30
-rw-r--r--drivers/clocksource/acpi_pm.c10
-rw-r--r--drivers/connector/cn_proc.c16
-rw-r--r--drivers/crypto/hifn_795x.c494
-rw-r--r--drivers/crypto/padlock-aes.c52
-rw-r--r--drivers/crypto/talitos.c153
-rw-r--r--drivers/crypto/talitos.h85
-rw-r--r--drivers/dma/dmaengine.c3
-rw-r--r--drivers/dma/ioat_dma.c5
-rw-r--r--drivers/dma/iop-adma.c16
-rw-r--r--drivers/dma/mv_xor.c15
-rw-r--r--drivers/edac/Kconfig2
-rw-r--r--drivers/edac/edac_device.c12
-rw-r--r--drivers/firmware/dmi_scan.c27
-rw-r--r--drivers/firmware/iscsi_ibft.c7
-rw-r--r--drivers/gpu/drm/Kconfig17
-rw-r--r--drivers/gpu/drm/Makefile3
-rw-r--r--drivers/gpu/drm/drm_auth.c29
-rw-r--r--drivers/gpu/drm/drm_bufs.c27
-rw-r--r--drivers/gpu/drm/drm_context.c10
-rw-r--r--drivers/gpu/drm/drm_crtc.c2446
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c826
-rw-r--r--drivers/gpu/drm/drm_drv.c95
-rw-r--r--drivers/gpu/drm/drm_edid.c732
-rw-r--r--drivers/gpu/drm/drm_fops.c223
-rw-r--r--drivers/gpu/drm/drm_gem.c111
-rw-r--r--drivers/gpu/drm/drm_hashtab.c2
-rw-r--r--drivers/gpu/drm/drm_ioctl.c61
-rw-r--r--drivers/gpu/drm/drm_irq.c73
-rw-r--r--drivers/gpu/drm/drm_lock.c42
-rw-r--r--drivers/gpu/drm/drm_mm.c1
-rw-r--r--drivers/gpu/drm/drm_modes.c576
-rw-r--r--drivers/gpu/drm/drm_proc.c71
-rw-r--r--drivers/gpu/drm/drm_stub.c142
-rw-r--r--drivers/gpu/drm/drm_sysfs.c329
-rw-r--r--drivers/gpu/drm/drm_vm.c7
-rw-r--r--drivers/gpu/drm/i915/Makefile17
-rw-r--r--drivers/gpu/drm/i915/dvo.h157
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7017.c454
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c368
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c442
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c302
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c335
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c348
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c42
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h95
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c637
-rw-r--r--drivers/gpu/drm/i915/i915_gem_proc.c34
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c1
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c51
-rw-r--r--drivers/gpu/drm/i915/i915_mem.c3
-rw-r--r--drivers/gpu/drm/i915/i915_opregion.c4
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h20
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c193
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h405
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c284
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1618
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h146
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c495
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c925
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c184
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c525
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c83
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c1128
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo_regs.h327
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c1725
-rw-r--r--drivers/gpu/drm/radeon/r300_cmdbuf.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c73
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h14
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c166
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c4
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c73
-rw-r--r--drivers/ide/Kconfig10
-rw-r--r--drivers/ide/Makefile3
-rw-r--r--drivers/ide/cmd64x.c4
-rw-r--r--drivers/ide/cs5530.c3
-rw-r--r--drivers/ide/cy82c693.c1
-rw-r--r--drivers/ide/gayle.c6
-rw-r--r--drivers/ide/hpt366.c8
-rw-r--r--drivers/ide/ide-acpi.c4
-rw-r--r--drivers/ide/ide-cd.c142
-rw-r--r--drivers/ide/ide-cd.h2
-rw-r--r--drivers/ide/ide-dma-sff.c54
-rw-r--r--drivers/ide/ide-io.c292
-rw-r--r--drivers/ide/ide-ioctls.c5
-rw-r--r--drivers/ide/ide-iops.c48
-rw-r--r--drivers/ide/ide-legacy.c58
-rw-r--r--drivers/ide/ide-lib.c105
-rw-r--r--drivers/ide/ide-park.c16
-rw-r--r--drivers/ide/ide-pm.c235
-rw-r--r--drivers/ide/ide-probe.c166
-rw-r--r--drivers/ide/ide-proc.c29
-rw-r--r--drivers/ide/ide.c89
-rw-r--r--drivers/ide/ide_arm.c11
-rw-r--r--drivers/ide/macide.c1
-rw-r--r--drivers/ide/pdc202xx_old.c9
-rw-r--r--drivers/ide/rz1000.c36
-rw-r--r--drivers/ide/sc1200.c3
-rw-r--r--drivers/ide/trm290.c4
-rw-r--r--drivers/ide/tx4938ide.c4
-rw-r--r--drivers/ide/tx4939ide.c10
-rw-r--r--drivers/ide/umc8672.c11
-rw-r--r--drivers/infiniband/Kconfig1
-rw-r--r--drivers/infiniband/core/addr.c229
-rw-r--r--drivers/infiniband/core/cma.c86
-rw-r--r--drivers/infiniband/core/sysfs.c10
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c8
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h7
-rw-r--r--drivers/infiniband/hw/ehca/ehca_eq.c2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c17
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c17
-rw-r--r--drivers/infiniband/hw/ehca/ehca_qp.c12
-rw-r--r--drivers/infiniband/hw/ehca/ehca_reqs.c13
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c49
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c38
-rw-r--r--drivers/infiniband/hw/ipath/ipath_fs.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c61
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba7220.c83
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h15
-rw-r--r--drivers/infiniband/hw/ipath/ipath_keys.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mad.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_qp.c32
-rw-r--r--drivers/infiniband/hw/ipath/ipath_rc.c5
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sdma.c21
-rw-r--r--drivers/infiniband/hw/ipath/ipath_stats.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ud.c19
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h1
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c14
-rw-r--r--drivers/infiniband/hw/mlx4/main.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mcg.c23
-rw-r--r--drivers/infiniband/hw/nes/nes.c17
-rw-r--r--drivers/infiniband/hw/nes/nes.h18
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c310
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.h14
-rw-r--r--drivers/infiniband/hw/nes/nes_hw.c44
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c13
-rw-r--r--drivers/infiniband/hw/nes/nes_utils.c14
-rw-r--r--drivers/infiniband/hw/nes/nes_verbs.c45
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h23
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c8
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c6
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c25
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c70
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c48
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h3
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c137
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c9
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c31
-rw-r--r--drivers/input/keyboard/omap-keypad.c8
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c2
-rw-r--r--drivers/input/keyboard/sh_keysc.c28
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/touchscreen/ads7846.c4
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c2
-rw-r--r--drivers/isdn/capi/capifs.c4
-rw-r--r--drivers/isdn/gigaset/asyncdata.c18
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c13
-rw-r--r--drivers/isdn/gigaset/common.c21
-rw-r--r--drivers/isdn/gigaset/ev-layer.c42
-rw-r--r--drivers/isdn/gigaset/gigaset.h20
-rw-r--r--drivers/isdn/gigaset/i4l.c4
-rw-r--r--drivers/isdn/gigaset/interface.c52
-rw-r--r--drivers/isdn/gigaset/isocdata.c6
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c32
-rw-r--r--drivers/isdn/gigaset/usb-gigaset.c14
-rw-r--r--drivers/isdn/hardware/eicon/di.c28
-rw-r--r--drivers/isdn/hardware/eicon/message.c60
-rw-r--r--drivers/isdn/hardware/mISDN/hfc_multi.h4
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c67
-rw-r--r--drivers/isdn/hysdn/hysdn_net.c6
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c6
-rw-r--r--drivers/isdn/i4l/isdn_concap.c4
-rw-r--r--drivers/isdn/i4l/isdn_net.c224
-rw-r--r--drivers/isdn/i4l/isdn_net.h50
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c32
-rw-r--r--drivers/isdn/mISDN/core.c8
-rw-r--r--drivers/isdn/mISDN/dsp_audio.c2
-rw-r--r--drivers/isdn/mISDN/dsp_cmx.c8
-rw-r--r--drivers/isdn/mISDN/dsp_core.c4
-rw-r--r--drivers/isdn/mISDN/dsp_hwec.c2
-rw-r--r--drivers/isdn/mISDN/dsp_pipeline.c2
-rw-r--r--drivers/isdn/mISDN/dsp_tones.c92
-rw-r--r--drivers/isdn/mISDN/l1oip_codec.c1
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c22
-rw-r--r--drivers/isdn/mISDN/layer1.c3
-rw-r--r--drivers/isdn/mISDN/layer2.c16
-rw-r--r--drivers/isdn/mISDN/socket.c2
-rw-r--r--drivers/isdn/mISDN/stack.c2
-rw-r--r--drivers/isdn/mISDN/tei.c4
-rw-r--r--drivers/isdn/mISDN/timerdev.c9
-rw-r--r--drivers/lguest/lg.h2
-rw-r--r--drivers/lguest/lguest_device.c8
-rw-r--r--drivers/lguest/lguest_user.c13
-rw-r--r--drivers/lguest/page_tables.c72
-rw-r--r--drivers/macintosh/via-cuda.c1
-rw-r--r--drivers/macintosh/via-macii.c1
-rw-r--r--drivers/macintosh/via-maciisi.c1
-rw-r--r--drivers/macintosh/via-pmu.c4
-rw-r--r--drivers/macintosh/via-pmu68k.c1
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c5
-rw-r--r--drivers/md/bitmap.c22
-rw-r--r--drivers/md/dm-crypt.c2
-rw-r--r--drivers/md/dm-io.c2
-rw-r--r--drivers/md/dm.c2
-rw-r--r--drivers/media/common/ir-keymaps.c93
-rw-r--r--drivers/media/common/saa7146_fops.c2
-rw-r--r--drivers/media/common/saa7146_video.c10
-rw-r--r--drivers/media/common/tuners/mxl5005s.c6
-rw-r--r--drivers/media/common/tuners/tda827x.c15
-rw-r--r--drivers/media/common/tuners/tda8290.c63
-rw-r--r--drivers/media/common/tuners/tda9887.c5
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c35
-rw-r--r--drivers/media/common/tuners/xc5000.c7
-rw-r--r--drivers/media/dvb/Kconfig13
-rw-r--r--drivers/media/dvb/b2c2/Kconfig3
-rw-r--r--drivers/media/dvb/b2c2/flexcop-fe-tuner.c2
-rw-r--r--drivers/media/dvb/b2c2/flexcop-i2c.c6
-rw-r--r--drivers/media/dvb/b2c2/flexcop.c2
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig2
-rw-r--r--drivers/media/dvb/bt8xx/dst.c4
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c6
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c77
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h134
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c37
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c71
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h1
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig46
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c33
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h140
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c2
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2-core.c3
-rw-r--r--drivers/media/dvb/dvb-usb/cinergyT2.h10
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c6
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c5
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h1
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c15
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk-fe.c140
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c16
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.h1
-rw-r--r--drivers/media/dvb/dvb-usb/usb-urb.c3
-rw-r--r--drivers/media/dvb/frontends/Kconfig53
-rw-r--r--drivers/media/dvb/frontends/Makefile10
-rw-r--r--drivers/media/dvb/frontends/af9013.c14
-rw-r--r--drivers/media/dvb/frontends/cx24113.c616
-rw-r--r--drivers/media/dvb/frontends/cx24113.h11
-rw-r--r--drivers/media/dvb/frontends/cx24116.c43
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h9
-rw-r--r--drivers/media/dvb/frontends/drx397xD.c12
-rw-r--r--drivers/media/dvb/frontends/drx397xD_fw.h4
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c2
-rw-r--r--drivers/media/dvb/frontends/lgdt3304.c378
-rw-r--r--drivers/media/dvb/frontends/lgdt3304.h45
-rw-r--r--drivers/media/dvb/frontends/s5h1411.c3
-rw-r--r--drivers/media/dvb/frontends/s921_core.c216
-rw-r--r--drivers/media/dvb/frontends/s921_core.h114
-rw-r--r--drivers/media/dvb/frontends/s921_module.c190
-rw-r--r--drivers/media/dvb/frontends/s921_module.h49
-rw-r--r--drivers/media/dvb/frontends/si21xx.c1
-rw-r--r--drivers/media/dvb/frontends/stb0899_algo.c1519
-rw-r--r--drivers/media/dvb/frontends/stb0899_cfg.h287
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c1684
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.h162
-rw-r--r--drivers/media/dvb/frontends/stb0899_priv.h267
-rw-r--r--drivers/media/dvb/frontends/stb0899_reg.h2027
-rw-r--r--drivers/media/dvb/frontends/stb6100.c545
-rw-r--r--drivers/media/dvb/frontends/stb6100.h115
-rw-r--r--drivers/media/dvb/frontends/stb6100_cfg.h108
-rw-r--r--drivers/media/dvb/frontends/tda8261.c230
-rw-r--r--drivers/media/dvb/frontends/tda8261.h55
-rw-r--r--drivers/media/dvb/frontends/tda8261_cfg.h84
-rw-r--r--drivers/media/dvb/frontends/zl10353.c3
-rw-r--r--drivers/media/dvb/pluto2/pluto2.c3
-rw-r--r--drivers/media/dvb/siano/sms-cards.c110
-rw-r--r--drivers/media/dvb/siano/sms-cards.h13
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c78
-rw-r--r--drivers/media/dvb/siano/smscoreapi.h36
-rw-r--r--drivers/media/dvb/siano/smsdvb.c56
-rw-r--r--drivers/media/dvb/siano/smsusb.c45
-rw-r--r--drivers/media/dvb/ttpci/Kconfig6
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c298
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c311
-rw-r--r--drivers/media/dvb/ttpci/budget.h1
-rw-r--r--drivers/media/radio/dsbr100.c381
-rw-r--r--drivers/media/radio/radio-aimslab.c2
-rw-r--r--drivers/media/radio/radio-cadet.c2
-rw-r--r--drivers/media/radio/radio-gemtek.c2
-rw-r--r--drivers/media/radio/radio-mr800.c123
-rw-r--r--drivers/media/radio/radio-rtrack2.c2
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/video/Kconfig50
-rw-r--r--drivers/media/video/Makefile12
-rw-r--r--drivers/media/video/arv.c5
-rw-r--r--drivers/media/video/bt8xx/bt832.c274
-rw-r--r--drivers/media/video/bt8xx/bt832.h305
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c72
-rw-r--r--drivers/media/video/bt8xx/bttv-gpio.c7
-rw-r--r--drivers/media/video/bt8xx/bttv.h10
-rw-r--r--drivers/media/video/bt8xx/bttvp.h2
-rw-r--r--drivers/media/video/bw-qcam.c5
-rw-r--r--drivers/media/video/c-qcam.c7
-rw-r--r--drivers/media/video/compat_ioctl32.c935
-rw-r--r--drivers/media/video/cpia.c9
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c2
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c2
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c15
-rw-r--r--drivers/media/video/cs5345.c227
-rw-r--r--drivers/media/video/cs53l32a.c188
-rw-r--r--drivers/media/video/cx18/Kconfig2
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c231
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c106
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h5
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c28
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-cards.c9
-rw-r--r--drivers/media/video/cx18/cx18-cards.h6
-rw-r--r--drivers/media/video/cx18/cx18-controls.c5
-rw-r--r--drivers/media/video/cx18/cx18-driver.c284
-rw-r--r--drivers/media/video/cx18/cx18-driver.h78
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c59
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h1
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c38
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c229
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c23
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h1
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c31
-rw-r--r--drivers/media/video/cx18/cx18-io.c198
-rw-r--r--drivers/media/video/cx18/cx18-io.h326
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c12
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h1
-rw-r--r--drivers/media/video/cx18/cx18-irq.c163
-rw-r--r--drivers/media/video/cx18/cx18-irq.h4
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c527
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h29
-rw-r--r--drivers/media/video/cx18/cx18-queue.c118
-rw-r--r--drivers/media/video/cx18/cx18-queue.h22
-rw-r--r--drivers/media/video/cx18/cx18-scb.c2
-rw-r--r--drivers/media/video/cx18/cx18-scb.h9
-rw-r--r--drivers/media/video/cx18/cx18-streams.c140
-rw-r--r--drivers/media/video/cx18/cx18-streams.h5
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c5
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h6
-rw-r--r--drivers/media/video/cx23885/Kconfig4
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c12
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c1
-rw-r--r--drivers/media/video/cx23885/cx23885.h1
-rw-r--r--drivers/media/video/cx25840/Kconfig2
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c14
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c447
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h7
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c2
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c2
-rw-r--r--drivers/media/video/cx88/Kconfig2
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c3
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c16
-rw-r--r--drivers/media/video/cx88/cx88-cards.c86
-rw-r--r--drivers/media/video/cx88/cx88-core.c3
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c230
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c4
-rw-r--r--drivers/media/video/cx88/cx88.h14
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c21
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c1464
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c628
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c14
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c49
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c311
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h153
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c850
-rw-r--r--drivers/media/video/em28xx/em28xx.h148
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2
-rw-r--r--drivers/media/video/gspca/Kconfig23
-rw-r--r--drivers/media/video/gspca/Makefile4
-rw-r--r--drivers/media/video/gspca/conex.c2
-rw-r--r--drivers/media/video/gspca/etoms.c4
-rw-r--r--drivers/media/video/gspca/finepix.c5
-rw-r--r--drivers/media/video/gspca/gspca.c214
-rw-r--r--drivers/media/video/gspca/gspca.h25
-rw-r--r--drivers/media/video/gspca/m5602/m5602_bridge.h119
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c100
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.c135
-rw-r--r--drivers/media/video/gspca/m5602/m5602_mt9m111.h14
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.c316
-rw-r--r--drivers/media/video/gspca/m5602/m5602_ov9650.h195
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.c166
-rw-r--r--drivers/media/video/gspca/m5602/m5602_po1030.h10
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.c235
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k4aa.h47
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.c213
-rw-r--r--drivers/media/video/gspca/m5602/m5602_s5k83a.h25
-rw-r--r--drivers/media/video/gspca/m5602/m5602_sensor.h14
-rw-r--r--drivers/media/video/gspca/mars.c4
-rw-r--r--drivers/media/video/gspca/ov519.c172
-rw-r--r--drivers/media/video/gspca/ov534.c601
-rw-r--r--drivers/media/video/gspca/pac207.c8
-rw-r--r--drivers/media/video/gspca/pac7311.c5
-rw-r--r--drivers/media/video/gspca/sonixb.c25
-rw-r--r--drivers/media/video/gspca/sonixj.c508
-rw-r--r--drivers/media/video/gspca/spca500.c8
-rw-r--r--drivers/media/video/gspca/spca501.c148
-rw-r--r--drivers/media/video/gspca/spca505.c2
-rw-r--r--drivers/media/video/gspca/spca506.c2
-rw-r--r--drivers/media/video/gspca/spca508.c2
-rw-r--r--drivers/media/video/gspca/spca561.c522
-rw-r--r--drivers/media/video/gspca/stk014.c8
-rw-r--r--drivers/media/video/gspca/stv06xx/Kconfig9
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile9
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c522
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h107
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c535
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h263
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c430
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h275
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_sensor.h92
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c251
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h315
-rw-r--r--drivers/media/video/gspca/sunplus.c6
-rw-r--r--drivers/media/video/gspca/t613.c4
-rw-r--r--drivers/media/video/gspca/tv8532.c142
-rw-r--r--drivers/media/video/gspca/vc032x.c819
-rw-r--r--drivers/media/video/gspca/zc3xx-reg.h8
-rw-r--r--drivers/media/video/gspca/zc3xx.c1012
-rw-r--r--drivers/media/video/ir-kbd-i2c.c6
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c214
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h52
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c44
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c324
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.h3
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c314
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h13
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c73
-rw-r--r--drivers/media/video/ivtv/ivtv-routing.c12
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c15
-rw-r--r--drivers/media/video/ivtv/ivtv-vbi.c17
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c91
-rw-r--r--drivers/media/video/m52790.c176
-rw-r--r--drivers/media/video/msp3400-driver.c402
-rw-r--r--drivers/media/video/msp3400-driver.h7
-rw-r--r--drivers/media/video/msp3400-kthreads.c34
-rw-r--r--drivers/media/video/mt9m001.c60
-rw-r--r--drivers/media/video/mt9m111.c121
-rw-r--r--drivers/media/video/mt9t031.c736
-rw-r--r--drivers/media/video/mt9v022.c46
-rw-r--r--drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--drivers/media/video/omap24xxcam.c1908
-rw-r--r--drivers/media/video/omap24xxcam.h593
-rw-r--r--drivers/media/video/ov511.c5
-rw-r--r--drivers/media/video/ov772x.c1012
-rw-r--r--drivers/media/video/pms.c9
-rw-r--r--drivers/media/video/pvrusb2/Kconfig2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c13
-rw-r--r--drivers/media/video/pwc/pwc-if.c2
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c3
-rw-r--r--drivers/media/video/pwc/pwc.h3
-rw-r--r--drivers/media/video/pxa_camera.c545
-rw-r--r--drivers/media/video/pxa_camera.h95
-rw-r--r--drivers/media/video/saa5246a.c7
-rw-r--r--drivers/media/video/saa5249.c7
-rw-r--r--drivers/media/video/saa7115.c763
-rw-r--r--drivers/media/video/saa7127.c421
-rw-r--r--drivers/media/video/saa7134/Kconfig4
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c52
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c24
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c14
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h3
-rw-r--r--drivers/media/video/saa717x.c610
-rw-r--r--drivers/media/video/se401.c5
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c329
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c2
-rw-r--r--drivers/media/video/sn9c102/sn9c102_devtable.h8
-rw-r--r--drivers/media/video/soc_camera.c389
-rw-r--r--drivers/media/video/soc_camera_platform.c17
-rw-r--r--drivers/media/video/stk-webcam.c27
-rw-r--r--drivers/media/video/stv680.c5
-rw-r--r--drivers/media/video/tda7432.c252
-rw-r--r--drivers/media/video/tda9840.c188
-rw-r--r--drivers/media/video/tda9875.c348
-rw-r--r--drivers/media/video/tea6415c.c49
-rw-r--r--drivers/media/video/tea6420.c49
-rw-r--r--drivers/media/video/tlv320aic23b.c141
-rw-r--r--drivers/media/video/tuner-core.c397
-rw-r--r--drivers/media/video/tvaudio.c707
-rw-r--r--drivers/media/video/tvp514x.c1569
-rw-r--r--drivers/media/video/tvp514x_regs.h297
-rw-r--r--drivers/media/video/tvp5150.c827
-rw-r--r--drivers/media/video/tw9910.c951
-rw-r--r--drivers/media/video/upd64031a.c193
-rw-r--r--drivers/media/video/upd64083.c166
-rw-r--r--drivers/media/video/usbvideo/ibmcam.c4
-rw-r--r--drivers/media/video/usbvideo/konicawc.c4
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c9
-rw-r--r--drivers/media/video/usbvideo/ultracam.c4
-rw-r--r--drivers/media/video/usbvideo/usbvideo.c7
-rw-r--r--drivers/media/video/usbvideo/vicam.c3
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c11
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c143
-rw-r--r--drivers/media/video/uvc/uvc_driver.c332
-rw-r--r--drivers/media/video/uvc/uvc_queue.c23
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c115
-rw-r--r--drivers/media/video/uvc/uvc_video.c214
-rw-r--r--drivers/media/video/uvc/uvcvideo.h34
-rw-r--r--drivers/media/video/v4l2-common.c203
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c1082
-rw-r--r--drivers/media/video/v4l2-dev.c365
-rw-r--r--drivers/media/video/v4l2-device.c86
-rw-r--r--drivers/media/video/v4l2-ioctl.c98
-rw-r--r--drivers/media/video/v4l2-subdev.c110
-rw-r--r--drivers/media/video/vino.c5
-rw-r--r--drivers/media/video/vp27smpx.c126
-rw-r--r--drivers/media/video/w9966.c5
-rw-r--r--drivers/media/video/wm8739.c188
-rw-r--r--drivers/media/video/wm8775.c221
-rw-r--r--drivers/media/video/zc0301/zc0301_core.c2
-rw-r--r--drivers/media/video/zoran/zoran_card.c6
-rw-r--r--drivers/media/video/zoran/zoran_driver.c8
-rw-r--r--drivers/message/fusion/mptbase.c15
-rw-r--r--drivers/message/fusion/mptlan.c22
-rw-r--r--drivers/message/fusion/mptlan.h2
-rw-r--r--drivers/message/fusion/mptscsih.c3
-rw-r--r--drivers/mfd/asic3.c6
-rw-r--r--drivers/mfd/mcp-core.c2
-rw-r--r--drivers/mfd/mcp-sa11x0.c2
-rw-r--r--drivers/mfd/ucb1x00-assabet.c2
-rw-r--r--drivers/mfd/ucb1x00-core.c2
-rw-r--r--drivers/mfd/ucb1x00-ts.c2
-rw-r--r--drivers/misc/sgi-gru/gruprocfs.c1
-rw-r--r--drivers/misc/sgi-xp/xp.h7
-rw-r--r--drivers/misc/sgi-xp/xp_main.c7
-rw-r--r--drivers/misc/sgi-xp/xp_sn2.c34
-rw-r--r--drivers/misc/sgi-xp/xp_uv.c70
-rw-r--r--drivers/misc/sgi-xp/xpc.h12
-rw-r--r--drivers/misc/sgi-xp/xpc_sn2.c15
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c290
-rw-r--r--drivers/misc/sgi-xp/xpnet.c13
-rw-r--r--drivers/mmc/host/imxmmc.c459
-rw-r--r--drivers/mmc/host/imxmmc.h37
-rw-r--r--drivers/mmc/host/mmci.c2
-rw-r--r--drivers/mmc/host/omap.c7
-rw-r--r--drivers/mmc/host/pxamci.c7
-rw-r--r--drivers/mmc/host/s3cmci.c2
-rw-r--r--drivers/mtd/maps/dc21285.c7
-rw-r--r--drivers/mtd/maps/ixp2000.c2
-rw-r--r--drivers/mtd/maps/ixp4xx.c2
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c4
-rw-r--r--drivers/mtd/nand/s3c2410.c8
-rw-r--r--drivers/mtd/onenand/omap2.c10
-rw-r--r--drivers/net/3c501.c3
-rw-r--r--drivers/net/3c501.h2
-rw-r--r--drivers/net/3c503.c21
-rw-r--r--drivers/net/3c505.c51
-rw-r--r--drivers/net/3c507.c15
-rw-r--r--drivers/net/3c509.c6
-rw-r--r--drivers/net/3c515.c13
-rw-r--r--drivers/net/3c523.c46
-rw-r--r--drivers/net/3c527.c4
-rw-r--r--drivers/net/3c59x.c13
-rw-r--r--drivers/net/7990.c1
-rw-r--r--drivers/net/8139cp.c47
-rw-r--r--drivers/net/8139too.c57
-rw-r--r--drivers/net/82596.c48
-rw-r--r--drivers/net/8390.c50
-rw-r--r--drivers/net/8390.h15
-rw-r--r--drivers/net/8390p.c50
-rw-r--r--drivers/net/Kconfig71
-rw-r--r--drivers/net/Makefile11
-rw-r--r--drivers/net/a2065.c5
-rw-r--r--drivers/net/ac3200.c5
-rw-r--r--drivers/net/acenic.c37
-rw-r--r--drivers/net/amd8111e.c12
-rw-r--r--drivers/net/apne.c29
-rw-r--r--drivers/net/appletalk/cops.c9
-rw-r--r--drivers/net/appletalk/ipddp.c6
-rw-r--r--drivers/net/appletalk/ltpc.c13
-rw-r--r--drivers/net/arcnet/arc-rawmode.c5
-rw-r--r--drivers/net/arcnet/arc-rimi.c16
-rw-r--r--drivers/net/arcnet/arcnet.c32
-rw-r--r--drivers/net/arcnet/capmode.c9
-rw-r--r--drivers/net/arcnet/com20020-isa.c4
-rw-r--r--drivers/net/arcnet/com20020-pci.c2
-rw-r--r--drivers/net/arcnet/com20020.c10
-rw-r--r--drivers/net/arcnet/com90io.c4
-rw-r--r--drivers/net/arcnet/com90xx.c12
-rw-r--r--drivers/net/arcnet/rfc1051.c9
-rw-r--r--drivers/net/arcnet/rfc1201.c14
-rw-r--r--drivers/net/ariadne.c21
-rw-r--r--drivers/net/arm/Kconfig10
-rw-r--r--drivers/net/arm/Makefile3
-rw-r--r--drivers/net/arm/am79c961a.c7
-rw-r--r--drivers/net/arm/at91_ether.c11
-rw-r--r--drivers/net/arm/ep93xx_eth.c10
-rw-r--r--drivers/net/arm/ether1.c5
-rw-r--r--drivers/net/arm/ether3.c5
-rw-r--r--drivers/net/arm/etherh.c24
-rw-r--r--drivers/net/arm/ixp4xx_eth.c346
-rw-r--r--drivers/net/arm/ks8695net.c1675
-rw-r--r--drivers/net/arm/ks8695net.h107
-rw-r--r--drivers/net/at1700.c16
-rw-r--r--drivers/net/atarilance.c52
-rw-r--r--drivers/net/atl1e/atl1e_main.c47
-rw-r--r--drivers/net/atlx/atl1.c102
-rw-r--r--drivers/net/atlx/atl1.h4
-rw-r--r--drivers/net/atlx/atl2.c101
-rw-r--r--drivers/net/atlx/atl2.h1
-rw-r--r--drivers/net/atlx/atlx.c13
-rw-r--r--drivers/net/atp.c29
-rw-r--r--drivers/net/au1000_eth.c50
-rw-r--r--drivers/net/ax88796.c9
-rw-r--r--drivers/net/b44.c13
-rw-r--r--drivers/net/bfin_mac.c1
-rw-r--r--drivers/net/bmac.c7
-rw-r--r--drivers/net/bnx2.c100
-rw-r--r--drivers/net/bnx2.h42
-rw-r--r--drivers/net/bnx2x_link.c72
-rw-r--r--drivers/net/bnx2x_link.h10
-rw-r--r--drivers/net/bnx2x_main.c95
-rw-r--r--drivers/net/bonding/Makefile3
-rw-r--r--drivers/net/bonding/bond_3ad.c667
-rw-r--r--drivers/net/bonding/bond_3ad.h59
-rw-r--r--drivers/net/bonding/bond_alb.c37
-rw-r--r--drivers/net/bonding/bond_ipv6.c216
-rw-r--r--drivers/net/bonding/bond_main.c477
-rw-r--r--drivers/net/bonding/bond_sysfs.c160
-rw-r--r--drivers/net/bonding/bonding.h68
-rw-r--r--drivers/net/can/vcan.c24
-rw-r--r--drivers/net/cassini.c16
-rw-r--r--drivers/net/chelsio/cxgb2.c94
-rw-r--r--drivers/net/chelsio/sge.c10
-rw-r--r--drivers/net/cpmac.c15
-rw-r--r--drivers/net/cris/eth_v10.c4
-rw-r--r--drivers/net/cs89x0.c22
-rw-r--r--drivers/net/cxgb3/adapter.h5
-rw-r--r--drivers/net/cxgb3/common.h4
-rw-r--r--drivers/net/cxgb3/cxgb3_ctl_defs.h17
-rw-r--r--drivers/net/cxgb3/cxgb3_main.c130
-rw-r--r--drivers/net/cxgb3/cxgb3_offload.c76
-rw-r--r--drivers/net/cxgb3/sge.c105
-rw-r--r--drivers/net/cxgb3/t3_hw.c26
-rw-r--r--drivers/net/cxgb3/version.h2
-rw-r--r--drivers/net/cxgb3/vsc8211.c2
-rw-r--r--drivers/net/de600.c4
-rw-r--r--drivers/net/de620.c26
-rw-r--r--drivers/net/declance.c7
-rw-r--r--drivers/net/defxx.c27
-rw-r--r--drivers/net/depca.c54
-rw-r--r--drivers/net/dl2k.c28
-rw-r--r--drivers/net/dm9000.c40
-rw-r--r--drivers/net/dummy.c15
-rw-r--r--drivers/net/e100.c68
-rw-r--r--drivers/net/e1000/e1000.h1
-rw-r--r--drivers/net/e1000/e1000_main.c107
-rw-r--r--drivers/net/e1000e/82571.c19
-rw-r--r--drivers/net/e1000e/defines.h8
-rw-r--r--drivers/net/e1000e/e1000.h2
-rw-r--r--drivers/net/e1000e/es2lan.c194
-rw-r--r--drivers/net/e1000e/ethtool.c82
-rw-r--r--drivers/net/e1000e/hw.h8
-rw-r--r--drivers/net/e1000e/ich8lan.c151
-rw-r--r--drivers/net/e1000e/lib.c80
-rw-r--r--drivers/net/e1000e/netdev.c131
-rw-r--r--drivers/net/e1000e/phy.c12
-rw-r--r--drivers/net/e2100.c24
-rw-r--r--drivers/net/eepro.c10
-rw-r--r--drivers/net/eepro100.c2401
-rw-r--r--drivers/net/eexpress.c3
-rw-r--r--drivers/net/eexpress.h56
-rw-r--r--drivers/net/ehea/ehea.h2
-rw-r--r--drivers/net/ehea/ehea_main.c9
-rw-r--r--drivers/net/ehea/ehea_qmr.c18
-rw-r--r--drivers/net/enc28j60.c51
-rw-r--r--drivers/net/enic/cq_desc.h5
-rw-r--r--drivers/net/enic/enic.h2
-rw-r--r--drivers/net/enic/enic_main.c86
-rw-r--r--drivers/net/enic/enic_res.c7
-rw-r--r--drivers/net/enic/enic_res.h4
-rw-r--r--drivers/net/enic/vnic_dev.c70
-rw-r--r--drivers/net/enic/vnic_devcmd.h19
-rw-r--r--drivers/net/enic/vnic_intr.h2
-rw-r--r--drivers/net/enic/vnic_resource.h2
-rw-r--r--drivers/net/enic/vnic_rq.h9
-rw-r--r--drivers/net/enic/vnic_rss.h13
-rw-r--r--drivers/net/enic/vnic_wq.h9
-rw-r--r--drivers/net/epic100.c56
-rw-r--r--drivers/net/eql.c12
-rw-r--r--drivers/net/es3210.c34
-rw-r--r--drivers/net/eth16i.c15
-rw-r--r--drivers/net/ewrk3.c18
-rw-r--r--drivers/net/fealnx.c6
-rw-r--r--drivers/net/fec.c6
-rw-r--r--drivers/net/fec_mpc52xx.c3
-rw-r--r--drivers/net/fec_mpc52xx_phy.c5
-rw-r--r--drivers/net/forcedeth.c90
-rw-r--r--drivers/net/fs_enet/fs_enet-main.c9
-rw-r--r--drivers/net/gianfar.c861
-rw-r--r--drivers/net/gianfar.h78
-rw-r--r--drivers/net/gianfar_ethtool.c89
-rw-r--r--drivers/net/gianfar_mii.c212
-rw-r--r--drivers/net/gianfar_mii.h2
-rw-r--r--drivers/net/hamachi.c27
-rw-r--r--drivers/net/hamradio/6pack.c1
-rw-r--r--drivers/net/hamradio/baycom_epp.c1
-rw-r--r--drivers/net/hamradio/bpqether.c8
-rw-r--r--drivers/net/hamradio/dmascc.c13
-rw-r--r--drivers/net/hamradio/hdlcdrv.c1
-rw-r--r--drivers/net/hamradio/mkiss.c4
-rw-r--r--drivers/net/hamradio/scc.c13
-rw-r--r--drivers/net/hamradio/yam.c1
-rw-r--r--drivers/net/hp-plus.c27
-rw-r--r--drivers/net/hp.c25
-rw-r--r--drivers/net/hp100.c24
-rw-r--r--drivers/net/hydra.c28
-rw-r--r--drivers/net/ibm_newemac/core.c11
-rw-r--r--drivers/net/ibmlana.c6
-rw-r--r--drivers/net/ibmveth.c48
-rw-r--r--drivers/net/ifb.c13
-rw-r--r--drivers/net/igb/e1000_defines.h7
-rw-r--r--drivers/net/igb/e1000_mac.c25
-rw-r--r--drivers/net/igb/e1000_regs.h4
-rw-r--r--drivers/net/igb/igb.h44
-rw-r--r--drivers/net/igb/igb_ethtool.c131
-rw-r--r--drivers/net/igb/igb_main.c403
-rw-r--r--drivers/net/ioc3-eth.c13
-rw-r--r--drivers/net/ipg.c9
-rw-r--r--drivers/net/irda/ali-ircc.c17
-rw-r--r--drivers/net/irda/au1k_ir.c1
-rw-r--r--drivers/net/irda/donauboe.c8
-rw-r--r--drivers/net/irda/irda-usb.c15
-rw-r--r--drivers/net/irda/irtty-sir.c7
-rw-r--r--drivers/net/irda/kingsun-sir.c1
-rw-r--r--drivers/net/irda/ks959-sir.c1
-rw-r--r--drivers/net/irda/ksdazzle-sir.c1
-rw-r--r--drivers/net/irda/ma600-sir.c2
-rw-r--r--drivers/net/irda/mcs7780.c1
-rw-r--r--drivers/net/irda/nsc-ircc.c17
-rw-r--r--drivers/net/irda/pxaficp_ir.c50
-rw-r--r--drivers/net/irda/sa1100_ir.c30
-rw-r--r--drivers/net/irda/sir_dev.c12
-rw-r--r--drivers/net/irda/smsc-ircc2.c6
-rw-r--r--drivers/net/irda/stir4200.c1
-rw-r--r--drivers/net/irda/via-ircc.c16
-rw-r--r--drivers/net/irda/vlsi_ir.c37
-rw-r--r--drivers/net/irda/w83977af_ir.c29
-rw-r--r--drivers/net/isa-skeleton.c16
-rw-r--r--drivers/net/iseries_veth.c14
-rw-r--r--drivers/net/ixgb/ixgb_main.c58
-rw-r--r--drivers/net/ixgbe/Makefile2
-rw-r--r--drivers/net/ixgbe/ixgbe.h32
-rw-r--r--drivers/net/ixgbe/ixgbe_82598.c192
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb.c332
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb.h184
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb_82598.c398
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb_82598.h94
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb_nl.c641
-rw-r--r--drivers/net/ixgbe/ixgbe_ethtool.c134
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c485
-rw-r--r--drivers/net/ixgbe/ixgbe_phy.c326
-rw-r--r--drivers/net/ixgbe/ixgbe_phy.h25
-rw-r--r--drivers/net/ixgbe/ixgbe_type.h28
-rw-r--r--drivers/net/ixp2000/ixpdev.c6
-rw-r--r--drivers/net/jazzsonic.c4
-rw-r--r--drivers/net/jme.c71
-rw-r--r--drivers/net/jme.h36
-rw-r--r--drivers/net/korina.c5
-rw-r--r--drivers/net/lance.c32
-rw-r--r--drivers/net/lib82596.c14
-rw-r--r--drivers/net/lib8390.c20
-rw-r--r--drivers/net/lne390.c29
-rw-r--r--drivers/net/loopback.c29
-rw-r--r--drivers/net/lp486e.c29
-rw-r--r--drivers/net/mac8390.c22
-rw-r--r--drivers/net/mac89x0.c16
-rw-r--r--drivers/net/macb.c18
-rw-r--r--drivers/net/mace.c34
-rw-r--r--drivers/net/macmace.c6
-rw-r--r--drivers/net/macsonic.c9
-rw-r--r--drivers/net/macvlan.c49
-rw-r--r--drivers/net/meth.c4
-rw-r--r--drivers/net/mlx4/cq.c11
-rw-r--r--drivers/net/mlx4/en_cq.c13
-rw-r--r--drivers/net/mlx4/en_main.c11
-rw-r--r--drivers/net/mlx4/en_netdev.c56
-rw-r--r--drivers/net/mlx4/en_params.c107
-rw-r--r--drivers/net/mlx4/en_rx.c9
-rw-r--r--drivers/net/mlx4/en_tx.c29
-rw-r--r--drivers/net/mlx4/eq.c121
-rw-r--r--drivers/net/mlx4/main.c53
-rw-r--r--drivers/net/mlx4/mcg.c25
-rw-r--r--drivers/net/mlx4/mlx4.h14
-rw-r--r--drivers/net/mlx4/mlx4_en.h18
-rw-r--r--drivers/net/mlx4/profile.c4
-rw-r--r--drivers/net/mv643xx_eth.c525
-rw-r--r--drivers/net/mvme147.c9
-rw-r--r--drivers/net/myri10ge/myri10ge.c71
-rw-r--r--drivers/net/myri10ge/myri10ge_mcp.h175
-rw-r--r--drivers/net/myri10ge/myri10ge_mcp_gen_header.h2
-rw-r--r--drivers/net/myri_sbus.c27
-rw-r--r--drivers/net/natsemi.c12
-rw-r--r--drivers/net/ne-h8300.c30
-rw-r--r--drivers/net/ne.c29
-rw-r--r--drivers/net/ne2.c29
-rw-r--r--drivers/net/ne2k-pci.c28
-rw-r--r--drivers/net/ne3210.c31
-rw-r--r--drivers/net/netconsole.c9
-rw-r--r--drivers/net/netx-eth.c1
-rw-r--r--drivers/net/netxen/netxen_nic_ethtool.c10
-rw-r--r--drivers/net/netxen/netxen_nic_hw.c6
-rw-r--r--drivers/net/netxen/netxen_nic_init.c2
-rw-r--r--drivers/net/netxen/netxen_nic_main.c57
-rw-r--r--drivers/net/netxen/netxen_nic_niu.c7
-rw-r--r--drivers/net/ni5010.c15
-rw-r--r--drivers/net/ni52.c43
-rw-r--r--drivers/net/ni65.c39
-rw-r--r--drivers/net/niu.c145
-rw-r--r--drivers/net/niu.h2
-rw-r--r--drivers/net/ns83820.c28
-rw-r--r--drivers/net/pasemi_mac.c16
-rw-r--r--drivers/net/pasemi_mac_ethtool.c4
-rw-r--r--drivers/net/pci-skeleton.c8
-rw-r--r--drivers/net/pcmcia/3c574_cs.c6
-rw-r--r--drivers/net/pcmcia/3c589_cs.c6
-rw-r--r--drivers/net/pcmcia/axnet_cs.c22
-rw-r--r--drivers/net/pcmcia/com20020_cs.c6
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c79
-rw-r--r--drivers/net/pcmcia/nmclan_cs.c6
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c3
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c5
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c6
-rw-r--r--drivers/net/pcnet32.c10
-rw-r--r--drivers/net/phy/Kconfig23
-rw-r--r--drivers/net/phy/Makefile5
-rw-r--r--drivers/net/phy/broadcom.c216
-rw-r--r--drivers/net/phy/et1011c.c113
-rw-r--r--drivers/net/phy/mdio-gpio.c296
-rw-r--r--drivers/net/phy/mdio-ofgpio.c204
-rw-r--r--drivers/net/phy/mdio_bus.c39
-rw-r--r--drivers/net/phy/national.c155
-rw-r--r--drivers/net/phy/phy.c2
-rw-r--r--drivers/net/phy/phy_device.c54
-rw-r--r--drivers/net/phy/smsc.c28
-rw-r--r--drivers/net/phy/ste10Xp.c137
-rw-r--r--drivers/net/plip.c35
-rw-r--r--drivers/net/ppp_async.c6
-rw-r--r--drivers/net/ppp_generic.c234
-rw-r--r--drivers/net/ppp_synctty.c6
-rw-r--r--drivers/net/pppoe.c5
-rw-r--r--drivers/net/pppol2tp.c94
-rw-r--r--drivers/net/ps3_gelic_net.c6
-rw-r--r--drivers/net/ps3_gelic_wireless.c37
-rw-r--r--drivers/net/ps3_gelic_wireless.h4
-rw-r--r--drivers/net/qla3xxx.c42
-rw-r--r--drivers/net/qlge/qlge_ethtool.c2
-rw-r--r--drivers/net/qlge/qlge_main.c79
-rw-r--r--drivers/net/qlge/qlge_mpi.c2
-rw-r--r--drivers/net/r6040.c51
-rw-r--r--drivers/net/r8169.c57
-rw-r--r--drivers/net/rionet.c31
-rw-r--r--drivers/net/rrunner.c25
-rw-r--r--drivers/net/s2io.c146
-rw-r--r--drivers/net/sb1000.c1
-rw-r--r--drivers/net/sb1250-mac.c11
-rw-r--r--drivers/net/sc92031.c30
-rw-r--r--drivers/net/seeq8005.c13
-rw-r--r--drivers/net/sfc/Kconfig8
-rw-r--r--drivers/net/sfc/Makefile5
-rw-r--r--drivers/net/sfc/boards.c206
-rw-r--r--drivers/net/sfc/boards.h14
-rw-r--r--drivers/net/sfc/efx.c449
-rw-r--r--drivers/net/sfc/efx.h12
-rw-r--r--drivers/net/sfc/enum.h36
-rw-r--r--drivers/net/sfc/ethtool.c175
-rw-r--r--drivers/net/sfc/falcon.c480
-rw-r--r--drivers/net/sfc/falcon.h3
-rw-r--r--drivers/net/sfc/falcon_gmac.c229
-rw-r--r--drivers/net/sfc/falcon_hwdefs.h161
-rw-r--r--drivers/net/sfc/falcon_xmac.c261
-rw-r--r--drivers/net/sfc/gmii.h137
-rw-r--r--drivers/net/sfc/mac.h16
-rw-r--r--drivers/net/sfc/mdio_10g.c483
-rw-r--r--drivers/net/sfc/mdio_10g.h63
-rw-r--r--drivers/net/sfc/mtd.c268
-rw-r--r--drivers/net/sfc/net_driver.h131
-rw-r--r--drivers/net/sfc/phy.h7
-rw-r--r--drivers/net/sfc/rx.c2
-rw-r--r--drivers/net/sfc/selftest.c145
-rw-r--r--drivers/net/sfc/selftest.h14
-rw-r--r--drivers/net/sfc/sfe4001.c225
-rw-r--r--drivers/net/sfc/spi.h34
-rw-r--r--drivers/net/sfc/tenxpress.c796
-rw-r--r--drivers/net/sfc/workarounds.h12
-rw-r--r--drivers/net/sfc/xfp_phy.c29
-rw-r--r--drivers/net/sgiseeq.c7
-rw-r--r--drivers/net/sh_eth.c3
-rw-r--r--drivers/net/sis190.c7
-rw-r--r--drivers/net/sis900.c99
-rw-r--r--drivers/net/skfp/skfddi.c27
-rw-r--r--drivers/net/skge.c103
-rw-r--r--drivers/net/sky2.c56
-rw-r--r--drivers/net/slip.c12
-rw-r--r--drivers/net/smc-mca.c27
-rw-r--r--drivers/net/smc-ultra.c27
-rw-r--r--drivers/net/smc-ultra32.c5
-rw-r--r--drivers/net/smc911x.c16
-rw-r--r--drivers/net/smc911x.h3
-rw-r--r--drivers/net/smc9194.c13
-rw-r--r--drivers/net/smc91x.c42
-rw-r--r--drivers/net/smc91x.h61
-rw-r--r--drivers/net/smsc911x.c2071
-rw-r--r--drivers/net/smsc911x.h390
-rw-r--r--drivers/net/smsc9420.c1744
-rw-r--r--drivers/net/smsc9420.h275
-rw-r--r--drivers/net/sonic.c1
-rw-r--r--drivers/net/sonic.h20
-rw-r--r--drivers/net/spider_net.c18
-rw-r--r--drivers/net/spider_net_ethtool.c8
-rw-r--r--drivers/net/starfire.c29
-rw-r--r--drivers/net/stnic.c25
-rw-r--r--drivers/net/sun3_82586.c40
-rw-r--r--drivers/net/sun3lance.c10
-rw-r--r--drivers/net/sunbmac.c22
-rw-r--r--drivers/net/sundance.c6
-rw-r--r--drivers/net/sungem.c65
-rw-r--r--drivers/net/sunhme.c31
-rw-r--r--drivers/net/sunlance.c7
-rw-r--r--drivers/net/sunqe.c15
-rw-r--r--drivers/net/sunvnet.c5
-rw-r--r--drivers/net/tc35815.c49
-rw-r--r--drivers/net/tehuti.c73
-rw-r--r--drivers/net/tg3.c1068
-rw-r--r--drivers/net/tg3.h188
-rw-r--r--drivers/net/tlan.c214
-rw-r--r--drivers/net/tokenring/3c359.c13
-rw-r--r--drivers/net/tokenring/Kconfig2
-rw-r--r--drivers/net/tokenring/abyss.c4
-rw-r--r--drivers/net/tokenring/ibmtr.c15
-rw-r--r--drivers/net/tokenring/lanstreamer.c36
-rw-r--r--drivers/net/tokenring/madgemc.c12
-rw-r--r--drivers/net/tokenring/olympic.c52
-rw-r--r--drivers/net/tokenring/proteon.c7
-rw-r--r--drivers/net/tokenring/skisa.c7
-rw-r--r--drivers/net/tokenring/smctr.c2
-rw-r--r--drivers/net/tokenring/tms380tr.c15
-rw-r--r--drivers/net/tokenring/tmspci.c5
-rw-r--r--drivers/net/tsi108_eth.c12
-rw-r--r--drivers/net/tulip/de2104x.c54
-rw-r--r--drivers/net/tulip/de4x5.c17
-rw-r--r--drivers/net/tulip/dmfe.c7
-rw-r--r--drivers/net/tulip/eeprom.c2
-rw-r--r--drivers/net/tulip/interrupt.c10
-rw-r--r--drivers/net/tulip/tulip_core.c15
-rw-r--r--drivers/net/tulip/uli526x.c8
-rw-r--r--drivers/net/tulip/winbond-840.c16
-rw-r--r--drivers/net/tulip/xircom_cb.c4
-rw-r--r--drivers/net/tun.c41
-rw-r--r--drivers/net/typhoon.c15
-rw-r--r--drivers/net/ucc_geth.c408
-rw-r--r--drivers/net/ucc_geth.h1
-rw-r--r--drivers/net/usb/asix.c5
-rw-r--r--drivers/net/usb/catc.c43
-rw-r--r--drivers/net/usb/dm9601.c5
-rw-r--r--drivers/net/usb/hso.c36
-rw-r--r--drivers/net/usb/kaweth.c17
-rw-r--r--drivers/net/usb/mcs7830.c9
-rw-r--r--drivers/net/usb/pegasus.c74
-rw-r--r--drivers/net/usb/rtl8150.c38
-rw-r--r--drivers/net/usb/smsc95xx.c110
-rw-r--r--drivers/net/usb/usbnet.c5
-rw-r--r--drivers/net/veth.c30
-rw-r--r--drivers/net/via-rhine.c47
-rw-r--r--drivers/net/via-velocity.c28
-rw-r--r--drivers/net/virtio_net.c217
-rw-r--r--drivers/net/wan/Kconfig9
-rw-r--r--drivers/net/wan/Makefile3
-rw-r--r--drivers/net/wan/c101.c6
-rw-r--r--drivers/net/wan/cosa.c1
-rw-r--r--drivers/net/wan/cycx_x25.c91
-rw-r--r--drivers/net/wan/dlci.c37
-rw-r--r--drivers/net/wan/dscc4.c4
-rw-r--r--drivers/net/wan/farsync.c2
-rw-r--r--drivers/net/wan/hd64570.c723
-rw-r--r--drivers/net/wan/hd64572.c640
-rw-r--r--drivers/net/wan/hd6457x.c868
-rw-r--r--drivers/net/wan/hdlc_fr.c10
-rw-r--r--drivers/net/wan/hdlc_ppp.c649
-rw-r--r--drivers/net/wan/hostess_sv11.c1
-rw-r--r--drivers/net/wan/ixp4xx_hss.c1325
-rw-r--r--drivers/net/wan/lapbether.c3
-rw-r--r--drivers/net/wan/lmc/lmc_main.c1
-rw-r--r--drivers/net/wan/lmc/lmc_proto.c1
-rw-r--r--drivers/net/wan/n2.c9
-rw-r--r--drivers/net/wan/pc300_drv.c22
-rw-r--r--drivers/net/wan/pc300too.c121
-rw-r--r--drivers/net/wan/pci200syn.c79
-rw-r--r--drivers/net/wan/sbni.c101
-rw-r--r--drivers/net/wan/sdla.c48
-rw-r--r--drivers/net/wan/sealevel.c1
-rw-r--r--drivers/net/wan/syncppp.c1480
-rw-r--r--drivers/net/wan/wanxl.c9
-rw-r--r--drivers/net/wan/x25_asy.c52
-rw-r--r--drivers/net/wan/z85230.c12
-rw-r--r--drivers/net/wd.c29
-rw-r--r--drivers/net/wireless/Kconfig163
-rw-r--r--drivers/net/wireless/Makefile22
-rw-r--r--drivers/net/wireless/adm8211.c52
-rw-r--r--drivers/net/wireless/adm8211.h2
-rw-r--r--drivers/net/wireless/airo.c243
-rw-r--r--drivers/net/wireless/airport.c286
-rw-r--r--drivers/net/wireless/arlan-main.c27
-rw-r--r--drivers/net/wireless/ath5k/ath5k.h102
-rw-r--r--drivers/net/wireless/ath5k/attach.c14
-rw-r--r--drivers/net/wireless/ath5k/base.c191
-rw-r--r--drivers/net/wireless/ath5k/desc.c4
-rw-r--r--drivers/net/wireless/ath5k/dma.c190
-rw-r--r--drivers/net/wireless/ath5k/eeprom.c1194
-rw-r--r--drivers/net/wireless/ath5k/eeprom.h253
-rw-r--r--drivers/net/wireless/ath5k/initvals.c8
-rw-r--r--drivers/net/wireless/ath5k/pcu.c233
-rw-r--r--drivers/net/wireless/ath5k/phy.c9
-rw-r--r--drivers/net/wireless/ath5k/qcu.c37
-rw-r--r--drivers/net/wireless/ath5k/reg.h16
-rw-r--r--drivers/net/wireless/ath5k/reset.c9
-rw-r--r--drivers/net/wireless/ath9k/Kconfig11
-rw-r--r--drivers/net/wireless/ath9k/Makefile9
-rw-r--r--drivers/net/wireless/ath9k/ani.c852
-rw-r--r--drivers/net/wireless/ath9k/ath9k.h432
-rw-r--r--drivers/net/wireless/ath9k/beacon.c319
-rw-r--r--drivers/net/wireless/ath9k/calib.c1021
-rw-r--r--drivers/net/wireless/ath9k/core.c1886
-rw-r--r--drivers/net/wireless/ath9k/core.h926
-rw-r--r--drivers/net/wireless/ath9k/debug.c262
-rw-r--r--drivers/net/wireless/ath9k/eeprom.c2824
-rw-r--r--drivers/net/wireless/ath9k/hw.c8490
-rw-r--r--drivers/net/wireless/ath9k/hw.h165
-rw-r--r--drivers/net/wireless/ath9k/initvals.h1938
-rw-r--r--drivers/net/wireless/ath9k/mac.c946
-rw-r--r--drivers/net/wireless/ath9k/main.c2353
-rw-r--r--drivers/net/wireless/ath9k/phy.c24
-rw-r--r--drivers/net/wireless/ath9k/phy.h5
-rw-r--r--drivers/net/wireless/ath9k/rc.c1832
-rw-r--r--drivers/net/wireless/ath9k/rc.h220
-rw-r--r--drivers/net/wireless/ath9k/recv.c1242
-rw-r--r--drivers/net/wireless/ath9k/reg.h109
-rw-r--r--drivers/net/wireless/ath9k/regd.c85
-rw-r--r--drivers/net/wireless/ath9k/regd.h2
-rw-r--r--drivers/net/wireless/ath9k/xmit.c1532
-rw-r--r--drivers/net/wireless/atmel.c79
-rw-r--r--drivers/net/wireless/b43/b43.h1
-rw-r--r--drivers/net/wireless/b43/debugfs.c1
-rw-r--r--drivers/net/wireless/b43/debugfs.h1
-rw-r--r--drivers/net/wireless/b43/dma.c4
-rw-r--r--drivers/net/wireless/b43/main.c280
-rw-r--r--drivers/net/wireless/b43/phy_a.c4
-rw-r--r--drivers/net/wireless/b43/phy_common.c18
-rw-r--r--drivers/net/wireless/b43/phy_g.c28
-rw-r--r--drivers/net/wireless/b43/pio.c3
-rw-r--r--drivers/net/wireless/b43/xmit.c64
-rw-r--r--drivers/net/wireless/b43/xmit.h5
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h5
-rw-r--r--drivers/net/wireless/b43legacy/debugfs.c2
-rw-r--r--drivers/net/wireless/b43legacy/dma.c48
-rw-r--r--drivers/net/wireless/b43legacy/main.c211
-rw-r--r--drivers/net/wireless/b43legacy/phy.c6
-rw-r--r--drivers/net/wireless/b43legacy/pio.c31
-rw-r--r--drivers/net/wireless/b43legacy/xmit.c26
-rw-r--r--drivers/net/wireless/b43legacy/xmit.h2
-rw-r--r--drivers/net/wireless/hostap/Kconfig13
-rw-r--r--drivers/net/wireless/hostap/hostap.h2
-rw-r--r--drivers/net/wireless/hostap/hostap_80211.h2
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_rx.c72
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_tx.c23
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.c253
-rw-r--r--drivers/net/wireless/hostap/hostap_ap.h8
-rw-r--r--drivers/net/wireless/hostap/hostap_common.h13
-rw-r--r--drivers/net/wireless/hostap/hostap_hw.c71
-rw-r--r--drivers/net/wireless/hostap/hostap_info.c16
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c157
-rw-r--r--drivers/net/wireless/hostap/hostap_main.c45
-rw-r--r--drivers/net/wireless/hostap/hostap_pci.c2
-rw-r--r--drivers/net/wireless/hostap/hostap_proc.c35
-rw-r--r--drivers/net/wireless/hostap/hostap_wlan.h8
-rw-r--r--drivers/net/wireless/ipw2100.c8654
-rw-r--r--drivers/net/wireless/ipw2200.c12008
-rw-r--r--drivers/net/wireless/ipw2200.h2010
-rw-r--r--drivers/net/wireless/ipw2x00/Kconfig191
-rw-r--r--drivers/net/wireless/ipw2x00/Makefile14
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c8649
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.h (renamed from drivers/net/wireless/ipw2100.h)0
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c11986
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.h2012
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_geo.c (renamed from net/ieee80211/ieee80211_geo.c)0
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_module.c293
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_rx.c1799
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_tx.c546
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_wx.c760
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig2
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-commands.h148
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-core.h28
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-debug.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-hw.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-io.h24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-led.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-led.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-rs.c223
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-rs.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c125
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.h40
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965-hw.h203
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c251
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000-hw.h64
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c440
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hcmd-check.c108
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.c179
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.h22
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c1241
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.c19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h536
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c335
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h78
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h33
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c83
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h183
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.c50
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.h15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h173
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-hcmd.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-helpers.h111
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h37
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.c38
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rfkill.c12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rfkill.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c230
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c131
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-spectrum.c198
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-spectrum.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c259
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.h16
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c438
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c751
-rw-r--r--drivers/net/wireless/libertas/assoc.c69
-rw-r--r--drivers/net/wireless/libertas/cmd.c32
-rw-r--r--drivers/net/wireless/libertas/cmd.h3
-rw-r--r--drivers/net/wireless/libertas/debugfs.c11
-rw-r--r--drivers/net/wireless/libertas/decl.h4
-rw-r--r--drivers/net/wireless/libertas/defs.h14
-rw-r--r--drivers/net/wireless/libertas/dev.h7
-rw-r--r--drivers/net/wireless/libertas/ethtool.c14
-rw-r--r--drivers/net/wireless/libertas/host.h9
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h26
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c3
-rw-r--r--drivers/net/wireless/libertas/if_usb.c7
-rw-r--r--drivers/net/wireless/libertas/main.c135
-rw-r--r--drivers/net/wireless/libertas/persistcfg.c18
-rw-r--r--drivers/net/wireless/libertas/radiotap.h3
-rw-r--r--drivers/net/wireless/libertas/scan.c105
-rw-r--r--drivers/net/wireless/libertas/scan.h4
-rw-r--r--drivers/net/wireless/libertas/tx.c2
-rw-r--r--drivers/net/wireless/libertas/types.h5
-rw-r--r--drivers/net/wireless/libertas/wext.c80
-rw-r--r--drivers/net/wireless/libertas_tf/cmd.c5
-rw-r--r--drivers/net/wireless/libertas_tf/main.c8
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c294
-rw-r--r--drivers/net/wireless/netwave_cs.c6
-rw-r--r--drivers/net/wireless/orinoco.c6021
-rw-r--r--drivers/net/wireless/orinoco.h213
-rw-r--r--drivers/net/wireless/orinoco/Makefile12
-rw-r--r--drivers/net/wireless/orinoco/airport.c286
-rw-r--r--drivers/net/wireless/orinoco/hermes.c (renamed from drivers/net/wireless/hermes.c)0
-rw-r--r--drivers/net/wireless/orinoco/hermes.h (renamed from drivers/net/wireless/hermes.h)0
-rw-r--r--drivers/net/wireless/orinoco/hermes_dld.c (renamed from drivers/net/wireless/hermes_dld.c)0
-rw-r--r--drivers/net/wireless/orinoco/hermes_dld.h (renamed from drivers/net/wireless/hermes_dld.h)0
-rw-r--r--drivers/net/wireless/orinoco/hermes_rid.h (renamed from drivers/net/wireless/hermes_rid.h)0
-rw-r--r--drivers/net/wireless/orinoco/orinoco.c6130
-rw-r--r--drivers/net/wireless/orinoco/orinoco.h222
-rw-r--r--drivers/net/wireless/orinoco/orinoco_cs.c537
-rw-r--r--drivers/net/wireless/orinoco/orinoco_nortel.c (renamed from drivers/net/wireless/orinoco_nortel.c)0
-rw-r--r--drivers/net/wireless/orinoco/orinoco_pci.c (renamed from drivers/net/wireless/orinoco_pci.c)0
-rw-r--r--drivers/net/wireless/orinoco/orinoco_pci.h (renamed from drivers/net/wireless/orinoco_pci.h)0
-rw-r--r--drivers/net/wireless/orinoco/orinoco_plx.c (renamed from drivers/net/wireless/orinoco_plx.c)0
-rw-r--r--drivers/net/wireless/orinoco/orinoco_tmd.c (renamed from drivers/net/wireless/orinoco_tmd.c)0
-rw-r--r--drivers/net/wireless/orinoco/spectrum_cs.c530
-rw-r--r--drivers/net/wireless/orinoco_cs.c533
-rw-r--r--drivers/net/wireless/p54/p54.h74
-rw-r--r--drivers/net/wireless/p54/p54common.c1620
-rw-r--r--drivers/net/wireless/p54/p54common.h349
-rw-r--r--drivers/net/wireless/p54/p54pci.c70
-rw-r--r--drivers/net/wireless/p54/p54pci.h2
-rw-r--r--drivers/net/wireless/p54/p54usb.c261
-rw-r--r--drivers/net/wireless/p54/p54usb.h1
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c24
-rw-r--r--drivers/net/wireless/prism54/islpci_hotplug.c8
-rw-r--r--drivers/net/wireless/ray_cs.c12
-rw-r--r--drivers/net/wireless/rndis_wlan.c95
-rw-r--r--drivers/net/wireless/rt2x00/Kconfig1
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c373
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c407
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c443
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.h5
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h113
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00config.c202
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00crypto.c79
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c47
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.h13
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c86
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00leds.c88
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h33
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c56
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.c41
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.h37
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c94
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h47
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c145
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.h141
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c469
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c689
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.h2
-rw-r--r--drivers/net/wireless/rtl8180_dev.c1081
-rw-r--r--drivers/net/wireless/rtl8180_rtl8225.c780
-rw-r--r--drivers/net/wireless/rtl8187.h221
-rw-r--r--drivers/net/wireless/rtl8187_dev.c1261
-rw-r--r--drivers/net/wireless/rtl8187_rtl8225.c992
-rw-r--r--drivers/net/wireless/rtl818x.h231
-rw-r--r--drivers/net/wireless/rtl818x/Makefile7
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180.h (renamed from drivers/net/wireless/rtl8180.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_dev.c1093
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_grf5101.c (renamed from drivers/net/wireless/rtl8180_grf5101.c)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_grf5101.h (renamed from drivers/net/wireless/rtl8180_grf5101.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_max2820.c (renamed from drivers/net/wireless/rtl8180_max2820.c)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_max2820.h (renamed from drivers/net/wireless/rtl8180_max2820.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_rtl8225.c788
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_rtl8225.h (renamed from drivers/net/wireless/rtl8180_rtl8225.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_sa2400.c (renamed from drivers/net/wireless/rtl8180_sa2400.c)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_sa2400.h (renamed from drivers/net/wireless/rtl8180_sa2400.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187.h227
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c1496
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_rtl8225.c971
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_rtl8225.h (renamed from drivers/net/wireless/rtl8187_rtl8225.h)0
-rw-r--r--drivers/net/wireless/rtl818x/rtl818x.h232
-rw-r--r--drivers/net/wireless/spectrum_cs.c507
-rw-r--r--drivers/net/wireless/strip.c22
-rw-r--r--drivers/net/wireless/wavelan.c122
-rw-r--r--drivers/net/wireless/wavelan_cs.c39
-rw-r--r--drivers/net/wireless/wl3501.h4
-rw-r--r--drivers/net/wireless/wl3501_cs.c11
-rw-r--r--drivers/net/wireless/zd1201.c131
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c4
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c44
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c5
-rw-r--r--drivers/net/xen-netfront.c9
-rw-r--r--drivers/net/xtsonic.c6
-rw-r--r--drivers/net/yellowfin.c42
-rw-r--r--drivers/net/znet.c28
-rw-r--r--drivers/net/zorro8390.c29
-rw-r--r--drivers/of/base.c68
-rw-r--r--drivers/of/gpio.c70
-rw-r--r--drivers/of/of_i2c.c11
-rw-r--r--drivers/oprofile/buffer_sync.c117
-rw-r--r--drivers/oprofile/cpu_buffer.c197
-rw-r--r--drivers/oprofile/cpu_buffer.h69
-rw-r--r--drivers/oprofile/oprofile_files.c15
-rw-r--r--drivers/parisc/led.c4
-rw-r--r--drivers/pci/hotplug/acpiphp.h2
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c2
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c4
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c5
-rw-r--r--drivers/pci/hotplug/pciehp_core.c23
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c69
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c2
-rw-r--r--drivers/pci/quirks.c170
-rw-r--r--drivers/pcmcia/Kconfig2
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/pxa2xx_e740.c176
-rw-r--r--drivers/pnp/pnpbios/bioscalls.c2
-rw-r--r--drivers/ps3/ps3av.c20
-rw-r--r--drivers/ps3/ps3av_cmd.c4
-rw-r--r--drivers/rapidio/rio-scan.c8
-rw-r--r--drivers/rtc/rtc-at91sam9.c1
-rw-r--r--drivers/rtc/rtc-isl1208.c7
-rw-r--r--drivers/rtc/rtc-s3c.c2
-rw-r--r--drivers/rtc/rtc-sa1100.c12
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/block/dasd_devmap.c19
-rw-r--r--drivers/s390/block/dasd_eckd.c2
-rw-r--r--drivers/s390/block/dasd_proc.c28
-rw-r--r--drivers/s390/block/dcssblk.c77
-rw-r--r--drivers/s390/block/xpram.c41
-rw-r--r--drivers/s390/char/monreader.c41
-rw-r--r--drivers/s390/char/monwriter.c5
-rw-r--r--drivers/s390/char/sclp_cmd.c29
-rw-r--r--drivers/s390/char/sclp_config.c10
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c12
-rw-r--r--drivers/s390/char/sclp_sdias.c18
-rw-r--r--drivers/s390/char/sclp_vt220.c33
-rw-r--r--drivers/s390/char/vmcp.c11
-rw-r--r--drivers/s390/char/vmlogrdr.c26
-rw-r--r--drivers/s390/char/vmur.c15
-rw-r--r--drivers/s390/char/zcore.c14
-rw-r--r--drivers/s390/cio/blacklist.c14
-rw-r--r--drivers/s390/cio/ccwgroup.c26
-rw-r--r--drivers/s390/cio/chsc.c8
-rw-r--r--drivers/s390/cio/chsc_sch.c2
-rw-r--r--drivers/s390/cio/cio.c245
-rw-r--r--drivers/s390/cio/cio.h18
-rw-r--r--drivers/s390/cio/cmf.c63
-rw-r--r--drivers/s390/cio/css.c12
-rw-r--r--drivers/s390/cio/device.c237
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c46
-rw-r--r--drivers/s390/cio/device_pgid.c2
-rw-r--r--drivers/s390/cio/device_status.c4
-rw-r--r--drivers/s390/cio/qdio.h33
-rw-r--r--drivers/s390/cio/qdio_debug.c104
-rw-r--r--drivers/s390/cio/qdio_debug.h112
-rw-r--r--drivers/s390/cio/qdio_main.c648
-rw-r--r--drivers/s390/cio/qdio_perf.c8
-rw-r--r--drivers/s390/cio/qdio_perf.h5
-rw-r--r--drivers/s390/cio/qdio_setup.c145
-rw-r--r--drivers/s390/cio/qdio_thinint.c29
-rw-r--r--drivers/s390/crypto/ap_bus.c212
-rw-r--r--drivers/s390/crypto/ap_bus.h6
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c10
-rw-r--r--drivers/s390/kvm/kvm_virtio.c34
-rw-r--r--drivers/s390/net/ctcm_fsms.c46
-rw-r--r--drivers/s390/net/ctcm_main.c72
-rw-r--r--drivers/s390/net/ctcm_main.h6
-rw-r--r--drivers/s390/net/ctcm_mpc.c15
-rw-r--r--drivers/s390/net/ctcm_sysfs.c3
-rw-r--r--drivers/s390/net/lcs.c104
-rw-r--r--drivers/s390/net/netiucv.c64
-rw-r--r--drivers/s390/net/qeth_core.h9
-rw-r--r--drivers/s390/net/qeth_core_main.c176
-rw-r--r--drivers/s390/net/qeth_core_offl.c8
-rw-r--r--drivers/s390/net/qeth_l2_main.c55
-rw-r--r--drivers/s390/net/qeth_l3_main.c211
-rw-r--r--drivers/s390/scsi/zfcp_aux.c130
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c9
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c20
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c15
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h1
-rw-r--r--drivers/s390/scsi/zfcp_def.h34
-rw-r--r--drivers/s390/scsi/zfcp_erp.c20
-rw-r--r--drivers/s390/scsi/zfcp_fc.c91
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c80
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h2
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c7
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c3
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c3
-rw-r--r--drivers/s390/sysinfo.c127
-rw-r--r--drivers/scsi/Kconfig22
-rw-r--r--drivers/scsi/Makefile5
-rw-r--r--drivers/scsi/NCR5380.c2
-rw-r--r--drivers/scsi/a100u2w.c2
-rw-r--r--drivers/scsi/aacraid/aachba.c2
-rw-r--r--drivers/scsi/aacraid/commctrl.c14
-rw-r--r--drivers/scsi/aacraid/comminit.c2
-rw-r--r--drivers/scsi/aacraid/commsup.c2
-rw-r--r--drivers/scsi/aacraid/dpcsup.c2
-rw-r--r--drivers/scsi/aacraid/linit.c6
-rw-r--r--drivers/scsi/aacraid/rkt.c2
-rw-r--r--drivers/scsi/aacraid/rx.c2
-rw-r--r--drivers/scsi/aacraid/sa.c2
-rw-r--r--drivers/scsi/advansys.c3
-rw-r--r--drivers/scsi/aha1740.c2
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c8
-rw-r--r--drivers/scsi/atp870u.c4
-rw-r--r--drivers/scsi/ch.c2
-rw-r--r--drivers/scsi/cxgb3i/Kbuild4
-rw-r--r--drivers/scsi/cxgb3i/Kconfig7
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i.h139
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_ddp.c770
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_ddp.h306
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_init.c107
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c951
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.c1810
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_offload.h231
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.c402
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_pdu.h59
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c18
-rw-r--r--drivers/scsi/eata.c15
-rw-r--r--drivers/scsi/eata_pio.c4
-rw-r--r--drivers/scsi/esp_scsi.c6
-rw-r--r--drivers/scsi/fcoe/Makefile8
-rw-r--r--drivers/scsi/fcoe/fc_transport_fcoe.c446
-rw-r--r--drivers/scsi/fcoe/fcoe_sw.c494
-rw-r--r--drivers/scsi/fcoe/libfcoe.c1510
-rw-r--r--drivers/scsi/fdomain.c2
-rw-r--r--drivers/scsi/ibmmca.c2
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c293
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h32
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c4
-rw-r--r--drivers/scsi/ibmvscsi/ibmvstgt.c16
-rw-r--r--drivers/scsi/ide-scsi.c32
-rw-r--r--drivers/scsi/in2000.c2
-rw-r--r--drivers/scsi/initio.c2
-rw-r--r--drivers/scsi/initio.h2
-rw-r--r--drivers/scsi/ipr.c6
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/iscsi_tcp.c1625
-rw-r--r--drivers/scsi/iscsi_tcp.h88
-rw-r--r--drivers/scsi/libfc/Makefile12
-rw-r--r--drivers/scsi/libfc/fc_disc.c845
-rw-r--r--drivers/scsi/libfc/fc_elsct.c71
-rw-r--r--drivers/scsi/libfc/fc_exch.c1970
-rw-r--r--drivers/scsi/libfc/fc_fcp.c2131
-rw-r--r--drivers/scsi/libfc/fc_frame.c89
-rw-r--r--drivers/scsi/libfc/fc_lport.c1604
-rw-r--r--drivers/scsi/libfc/fc_rport.c1291
-rw-r--r--drivers/scsi/libiscsi.c247
-rw-r--r--drivers/scsi/libiscsi_tcp.c1163
-rw-r--r--drivers/scsi/lpfc/lpfc.h22
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c169
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h15
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c17
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c164
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c160
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c69
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h249
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c685
-rw-r--r--drivers/scsi/lpfc/lpfc_logmsg.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c54
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h30
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1235
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c159
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c28
-rw-r--r--drivers/scsi/mac_esp.c100
-rw-r--r--drivers/scsi/mac_scsi.c1
-rw-r--r--drivers/scsi/nsp32.c3
-rw-r--r--drivers/scsi/qla1280.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c328
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c96
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h15
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h584
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c21
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h14
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h56
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c481
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c1251
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h45
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c358
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c840
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c886
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c516
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c1471
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c554
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h4
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c3
-rw-r--r--drivers/scsi/qlogicfas408.c2
-rw-r--r--drivers/scsi/scsi_error.c6
-rw-r--r--drivers/scsi/scsi_ioctl.c6
-rw-r--r--drivers/scsi/scsi_lib.c151
-rw-r--r--drivers/scsi/scsi_scan.c17
-rw-r--r--drivers/scsi/scsi_transport_fc.c13
-rw-r--r--drivers/scsi/scsi_transport_spi.c5
-rw-r--r--drivers/scsi/sd.c11
-rw-r--r--drivers/scsi/ses.c9
-rw-r--r--drivers/scsi/sr.c4
-rw-r--r--drivers/scsi/sr_ioctl.c2
-rw-r--r--drivers/scsi/st.c245
-rw-r--r--drivers/scsi/stex.c3
-rw-r--r--drivers/scsi/sym53c416.c2
-rw-r--r--drivers/scsi/tmscsim.c3
-rw-r--r--drivers/scsi/u14-34f.c3
-rw-r--r--drivers/scsi/wd7000.c4
-rw-r--r--drivers/serial/Kconfig25
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/amba-pl011.c2
-rw-r--r--drivers/serial/imx.c8
-rw-r--r--drivers/serial/mpc52xx_uart.c74
-rw-r--r--drivers/serial/pmac_zilog.c27
-rw-r--r--drivers/serial/pxa.c3
-rw-r--r--drivers/serial/s3c24a0.c118
-rw-r--r--drivers/serial/s3c6400.c151
-rw-r--r--drivers/serial/samsung.c184
-rw-r--r--drivers/serial/samsung.h9
-rw-r--r--drivers/serial/serial_lh7a40x.c3
-rw-r--r--drivers/serial/sh-sci.c563
-rw-r--r--drivers/serial/sh-sci.h45
-rw-r--r--drivers/spi/pxa2xx_spi.c2
-rw-r--r--drivers/spi/spi_s3c24xx.c2
-rw-r--r--drivers/ssb/main.c11
-rw-r--r--drivers/ssb/pcihost_wrapper.c2
-rw-r--r--drivers/staging/slicoss/slicoss.c7
-rw-r--r--drivers/staging/winbond/linux/wbusb.c6
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c20
-rw-r--r--drivers/staging/wlan-ng/p80211wext.c58
-rw-r--r--drivers/usb/atm/cxacru.c4
-rw-r--r--drivers/usb/atm/usbatm.c5
-rw-r--r--drivers/usb/core/devio.c10
-rw-r--r--drivers/usb/core/inode.c4
-rw-r--r--drivers/usb/gadget/f_phonet.c621
-rw-r--r--drivers/usb/gadget/m66592-udc.c34
-rw-r--r--drivers/usb/gadget/m66592-udc.h27
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c2
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c2
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c4
-rw-r--r--drivers/usb/gadget/u_ether.c10
-rw-r--r--drivers/usb/gadget/u_phonet.h21
-rw-r--r--drivers/usb/host/ehci-orion.c17
-rw-r--r--drivers/usb/host/ohci-omap.c8
-rw-r--r--drivers/usb/host/ohci-pxa27x.c2
-rw-r--r--drivers/usb/host/r8a66597-hcd.c34
-rw-r--r--drivers/usb/host/r8a66597.h8
-rw-r--r--drivers/uwb/wlp/eda.c38
-rw-r--r--drivers/video/Kconfig40
-rw-r--r--drivers/video/amba-clcd.c4
-rw-r--r--drivers/video/console/fbcon.c78
-rw-r--r--drivers/video/console/vgacon.c17
-rw-r--r--drivers/video/controlfb.c4
-rw-r--r--drivers/video/cyber2000fb.c2
-rw-r--r--drivers/video/fb_defio.c27
-rw-r--r--drivers/video/imxfb.c468
-rw-r--r--drivers/video/imxfb.h73
-rw-r--r--drivers/video/macfb.c33
-rw-r--r--drivers/video/ps3fb.c23
-rw-r--r--drivers/video/pxafb.c981
-rw-r--r--drivers/video/pxafb.h82
-rw-r--r--drivers/video/sa1100fb.c2
-rw-r--r--drivers/video/sh7760fb.c86
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c198
-rw-r--r--drivers/virtio/virtio.c2
-rw-r--r--drivers/virtio/virtio_balloon.c13
-rw-r--r--drivers/virtio/virtio_pci.c43
-rw-r--r--drivers/virtio/virtio_ring.c3
-rw-r--r--drivers/w1/w1_io.c4
-rw-r--r--drivers/watchdog/s3c2410_wdt.c2
-rw-r--r--drivers/watchdog/sa1100_wdt.c12
-rw-r--r--drivers/xen/balloon.c4
-rw-r--r--drivers/xen/events.c5
-rw-r--r--drivers/xen/features.c6
-rw-r--r--drivers/xen/grant-table.c1
-rw-r--r--firmware/Makefile19
-rw-r--r--firmware/WHENCE11
-rw-r--r--firmware/cxgb3/t3b_psram-1.1.0.bin.ihex162
-rw-r--r--firmware/cxgb3/t3c_psram-1.1.0.bin.ihex162
-rw-r--r--firmware/cxgb3/t3fw-7.0.0.bin.ihex1881
-rw-r--r--fs/9p/fid.c6
-rw-r--r--fs/9p/v9fs.c2
-rw-r--r--fs/9p/vfs_dentry.c9
-rw-r--r--fs/9p/vfs_inode.c10
-rw-r--r--fs/9p/vfs_super.c4
-rw-r--r--fs/affs/inode.c4
-rw-r--r--fs/affs/super.c4
-rw-r--r--fs/afs/proc.c4
-rw-r--r--fs/afs/server.c9
-rw-r--r--fs/aio.c100
-rw-r--r--fs/anon_inodes.c4
-rw-r--r--fs/attr.c4
-rw-r--r--fs/autofs/inode.c4
-rw-r--r--fs/autofs4/dev-ioctl.c3
-rw-r--r--fs/autofs4/inode.c4
-rw-r--r--fs/autofs4/waitq.c4
-rw-r--r--fs/bfs/dir.c4
-rw-r--r--fs/binfmt_aout.c2
-rw-r--r--fs/binfmt_elf.c22
-rw-r--r--fs/binfmt_elf_fdpic.c19
-rw-r--r--fs/binfmt_flat.c2
-rw-r--r--fs/binfmt_som.c2
-rw-r--r--fs/bio-integrity.c2
-rw-r--r--fs/bio.c320
-rw-r--r--fs/buffer.c19
-rw-r--r--fs/cifs/AUTHORS2
-rw-r--r--fs/cifs/CHANGES9
-rw-r--r--fs/cifs/README12
-rw-r--r--fs/cifs/cifs_dfs_ref.c48
-rw-r--r--fs/cifs/cifs_fs_sb.h7
-rw-r--r--fs/cifs/cifs_spnego.c6
-rw-r--r--fs/cifs/cifsencrypt.c30
-rw-r--r--fs/cifs/cifsencrypt.h3
-rw-r--r--fs/cifs/cifsfs.c75
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h15
-rw-r--r--fs/cifs/cifspdu.h2
-rw-r--r--fs/cifs/cifsproto.h5
-rw-r--r--fs/cifs/cifssmb.c49
-rw-r--r--fs/cifs/connect.c685
-rw-r--r--fs/cifs/dir.c21
-rw-r--r--fs/cifs/file.c25
-rw-r--r--fs/cifs/inode.c66
-rw-r--r--fs/cifs/ioctl.c2
-rw-r--r--fs/cifs/misc.c13
-rw-r--r--fs/cifs/sess.c5
-rw-r--r--fs/cifs/smbdes.c5
-rw-r--r--fs/cifs/smbencrypt.c9
-rw-r--r--fs/cifs/transport.c378
-rw-r--r--fs/coda/cache.c6
-rw-r--r--fs/coda/file.c3
-rw-r--r--fs/coda/upcall.c2
-rw-r--r--fs/compat.c42
-rw-r--r--fs/devpts/inode.c4
-rw-r--r--fs/dlm/netlink.c2
-rw-r--r--fs/dquot.c4
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h3
-rw-r--r--fs/ecryptfs/kthread.c9
-rw-r--r--fs/ecryptfs/main.c3
-rw-r--r--fs/ecryptfs/messaging.c27
-rw-r--r--fs/ecryptfs/miscdev.c27
-rw-r--r--fs/exec.c191
-rw-r--r--fs/exportfs/expfs.c4
-rw-r--r--fs/ext2/balloc.c2
-rw-r--r--fs/ext2/ialloc.c4
-rw-r--r--fs/ext3/balloc.c2
-rw-r--r--fs/ext3/ialloc.c4
-rw-r--r--fs/ext4/balloc.c2
-rw-r--r--fs/ext4/ialloc.c4
-rw-r--r--fs/ext4/super.c8
-rw-r--r--fs/fat/file.c2
-rw-r--r--fs/fat/inode.c4
-rw-r--r--fs/fcntl.c18
-rw-r--r--fs/file_table.c10
-rw-r--r--fs/fuse/dev.c4
-rw-r--r--fs/fuse/dir.c23
-rw-r--r--fs/gfs2/inode.c10
-rw-r--r--fs/hfs/inode.c4
-rw-r--r--fs/hfs/super.c4
-rw-r--r--fs/hfsplus/inode.c4
-rw-r--r--fs/hfsplus/options.c4
-rw-r--r--fs/hpfs/namei.c24
-rw-r--r--fs/hpfs/super.c4
-rw-r--r--fs/hppfs/hppfs.c6
-rw-r--r--fs/hugetlbfs/inode.c21
-rw-r--r--fs/inode.c209
-rw-r--r--fs/inotify_user.c2
-rw-r--r--fs/internal.h6
-rw-r--r--fs/ioprio.c18
-rw-r--r--fs/jfs/inode.c8
-rw-r--r--fs/jfs/jfs_inode.c4
-rw-r--r--fs/lockd/clntlock.c23
-rw-r--r--fs/lockd/host.c18
-rw-r--r--fs/lockd/mon.c2
-rw-r--r--fs/lockd/svc.c6
-rw-r--r--fs/locks.c2
-rw-r--r--fs/minix/bitmap.c4
-rw-r--r--fs/namei.c10
-rw-r--r--fs/namespace.c2
-rw-r--r--fs/ncpfs/ioctl.c91
-rw-r--r--fs/nfs/callback.c36
-rw-r--r--fs/nfs/client.c95
-rw-r--r--fs/nfs/delegation.c260
-rw-r--r--fs/nfs/delegation.h33
-rw-r--r--fs/nfs/dir.c24
-rw-r--r--fs/nfs/inode.c13
-rw-r--r--fs/nfs/internal.h14
-rw-r--r--fs/nfs/mount_clnt.c34
-rw-r--r--fs/nfs/nfs4_fs.h32
-rw-r--r--fs/nfs/nfs4proc.c431
-rw-r--r--fs/nfs/nfs4renewd.c22
-rw-r--r--fs/nfs/nfs4state.c415
-rw-r--r--fs/nfs/nfs4xdr.c1235
-rw-r--r--fs/nfs/nfsroot.c33
-rw-r--r--fs/nfs/read.c6
-rw-r--r--fs/nfs/super.c50
-rw-r--r--fs/nfs_common/nfsacl.c4
-rw-r--r--fs/nfsctl.c5
-rw-r--r--fs/nfsd/auth.c95
-rw-r--r--fs/nfsd/nfs4callback.c9
-rw-r--r--fs/nfsd/nfs4recover.c72
-rw-r--r--fs/nfsd/nfs4state.c16
-rw-r--r--fs/nfsd/nfsctl.c2
-rw-r--r--fs/nfsd/nfsfh.c11
-rw-r--r--fs/nfsd/vfs.c9
-rw-r--r--fs/ocfs2/cluster/netdebug.c8
-rw-r--r--fs/ocfs2/cluster/nodemanager.c2
-rw-r--r--fs/ocfs2/cluster/tcp.c29
-rw-r--r--fs/ocfs2/dlm/dlmfs.c8
-rw-r--r--fs/ocfs2/namei.c4
-rw-r--r--fs/omfs/inode.c8
-rw-r--r--fs/open.c59
-rw-r--r--fs/pipe.c4
-rw-r--r--fs/posix_acl.c4
-rw-r--r--fs/proc/array.c32
-rw-r--r--fs/proc/base.c36
-rw-r--r--fs/proc/proc_devtree.c3
-rw-r--r--fs/quota.c4
-rw-r--r--fs/ramfs/inode.c4
-rw-r--r--fs/reiserfs/namei.c4
-rw-r--r--fs/seq_file.c3
-rw-r--r--fs/smbfs/dir.c3
-rw-r--r--fs/smbfs/inode.c2
-rw-r--r--fs/smbfs/proc.c2
-rw-r--r--fs/super.c2
-rw-r--r--fs/sysv/ialloc.c4
-rw-r--r--fs/ubifs/budget.c2
-rw-r--r--fs/ubifs/dir.c4
-rw-r--r--fs/udf/ialloc.c4
-rw-r--r--fs/udf/namei.c2
-rw-r--r--fs/ufs/ialloc.c4
-rw-r--r--fs/xfs/Makefile6
-rw-r--r--fs/xfs/linux-2.6/sv.h22
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c66
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.h3
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c87
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.h30
-rw-r--r--fs/xfs/linux-2.6/xfs_cred.h12
-rw-r--r--fs/xfs/linux-2.6/xfs_export.c1
-rw-r--r--fs/xfs/linux-2.6/xfs_file.c189
-rw-r--r--fs/xfs/linux-2.6/xfs_fs_subr.c23
-rw-r--r--fs/xfs/linux-2.6/xfs_globals.c8
-rw-r--r--fs/xfs/linux-2.6/xfs_globals.h1
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl.c226
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl.h82
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl32.c849
-rw-r--r--fs/xfs/linux-2.6/xfs_ioctl32.h214
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.c122
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.h1
-rw-r--r--fs/xfs/linux-2.6/xfs_linux.h13
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.c50
-rw-r--r--fs/xfs/linux-2.6/xfs_stats.c6
-rw-r--r--fs/xfs/linux-2.6/xfs_stats.h65
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c884
-rw-r--r--fs/xfs/linux-2.6/xfs_super.h15
-rw-r--r--fs/xfs/linux-2.6/xfs_sync.c762
-rw-r--r--fs/xfs/linux-2.6/xfs_sync.h55
-rw-r--r--fs/xfs/linux-2.6/xfs_sysctl.c11
-rw-r--r--fs/xfs/linux-2.6/xfs_sysctl.h3
-rw-r--r--fs/xfs/linux-2.6/xfs_vfs.h77
-rw-r--r--fs/xfs/linux-2.6/xfs_vnode.c145
-rw-r--r--fs/xfs/linux-2.6/xfs_vnode.h72
-rw-r--r--fs/xfs/quota/xfs_dquot.c39
-rw-r--r--fs/xfs/quota/xfs_dquot.h4
-rw-r--r--fs/xfs/quota/xfs_dquot_item.c45
-rw-r--r--fs/xfs/quota/xfs_qm.c57
-rw-r--r--fs/xfs/quota/xfs_qm.h3
-rw-r--r--fs/xfs/quota/xfs_qm_bhv.c5
-rw-r--r--fs/xfs/quota/xfs_qm_syscalls.c151
-rw-r--r--fs/xfs/support/debug.c39
-rw-r--r--fs/xfs/support/debug.h2
-rw-r--r--fs/xfs/support/ktrace.c9
-rw-r--r--fs/xfs/xfs.h2
-rw-r--r--fs/xfs/xfs_acl.c8
-rw-r--r--fs/xfs/xfs_ag.h15
-rw-r--r--fs/xfs/xfs_alloc.c264
-rw-r--r--fs/xfs/xfs_alloc.h27
-rw-r--r--fs/xfs/xfs_alloc_btree.c2387
-rw-r--r--fs/xfs/xfs_alloc_btree.h107
-rw-r--r--fs/xfs/xfs_arch.h39
-rw-r--r--fs/xfs/xfs_bit.h3
-rw-r--r--fs/xfs/xfs_bmap.c410
-rw-r--r--fs/xfs/xfs_bmap.h72
-rw-r--r--fs/xfs/xfs_bmap_btree.c2617
-rw-r--r--fs/xfs/xfs_bmap_btree.h171
-rw-r--r--fs/xfs/xfs_btree.c3596
-rw-r--r--fs/xfs/xfs_btree.h392
-rw-r--r--fs/xfs/xfs_btree_trace.c249
-rw-r--r--fs/xfs/xfs_btree_trace.h116
-rw-r--r--fs/xfs/xfs_buf_item.c45
-rw-r--r--fs/xfs/xfs_clnt.h105
-rw-r--r--fs/xfs/xfs_da_btree.h24
-rw-r--r--fs/xfs/xfs_dfrag.c8
-rw-r--r--fs/xfs/xfs_dfrag.h2
-rw-r--r--fs/xfs/xfs_dinode.h148
-rw-r--r--fs/xfs/xfs_dir2_sf.h7
-rw-r--r--fs/xfs/xfs_dmops.c5
-rw-r--r--fs/xfs/xfs_error.c15
-rw-r--r--fs/xfs/xfs_error.h12
-rw-r--r--fs/xfs/xfs_extfree_item.c45
-rw-r--r--fs/xfs/xfs_fs.h22
-rw-r--r--fs/xfs/xfs_fsops.c30
-rw-r--r--fs/xfs/xfs_ialloc.c449
-rw-r--r--fs/xfs/xfs_ialloc.h31
-rw-r--r--fs/xfs/xfs_ialloc_btree.c2193
-rw-r--r--fs/xfs/xfs_ialloc_btree.h111
-rw-r--r--fs/xfs/xfs_iget.c735
-rw-r--r--fs/xfs/xfs_imap.h40
-rw-r--r--fs/xfs/xfs_inode.c587
-rw-r--r--fs/xfs/xfs_inode.h377
-rw-r--r--fs/xfs/xfs_inode_item.c45
-rw-r--r--fs/xfs/xfs_inode_item.h41
-rw-r--r--fs/xfs/xfs_iomap.c28
-rw-r--r--fs/xfs/xfs_itable.c102
-rw-r--r--fs/xfs/xfs_itable.h14
-rw-r--r--fs/xfs/xfs_log.c81
-rw-r--r--fs/xfs/xfs_log.h4
-rw-r--r--fs/xfs/xfs_log_priv.h48
-rw-r--r--fs/xfs/xfs_log_recover.c416
-rw-r--r--fs/xfs/xfs_mount.c81
-rw-r--r--fs/xfs/xfs_mount.h73
-rw-r--r--fs/xfs/xfs_qmops.c5
-rw-r--r--fs/xfs/xfs_quota.h8
-rw-r--r--fs/xfs/xfs_rename.c61
-rw-r--r--fs/xfs/xfs_rtalloc.c41
-rw-r--r--fs/xfs/xfs_rw.c2
-rw-r--r--fs/xfs/xfs_sb.h167
-rw-r--r--fs/xfs/xfs_trans.c22
-rw-r--r--fs/xfs/xfs_trans.h322
-rw-r--r--fs/xfs/xfs_trans_ail.c362
-rw-r--r--fs/xfs/xfs_trans_buf.c7
-rw-r--r--fs/xfs/xfs_trans_inode.c30
-rw-r--r--fs/xfs/xfs_trans_item.c10
-rw-r--r--fs/xfs/xfs_trans_priv.h98
-rw-r--r--fs/xfs/xfs_utils.c12
-rw-r--r--fs/xfs/xfs_vfsops.c757
-rw-r--r--fs/xfs/xfs_vfsops.h16
-rw-r--r--fs/xfs/xfs_vnodeops.c354
-rw-r--r--fs/xfs/xfs_vnodeops.h16
-rw-r--r--include/acpi/acoutput.h2
-rw-r--r--include/acpi/platform/aclinux.h6
-rw-r--r--include/asm-arm/plat-s3c/iic.h33
-rw-r--r--include/asm-generic/bug.h15
-rw-r--r--include/asm-generic/memory_model.h2
-rw-r--r--include/asm-generic/pgtable.h50
-rw-r--r--include/asm-generic/vmlinux.lds.h10
-rw-r--r--include/asm-m68k/bitops.h5
-rw-r--r--include/asm-m68k/byteorder.h16
-rw-r--r--include/asm-m68k/machw.h22
-rw-r--r--include/crypto/aes.h8
-rw-r--r--include/crypto/algapi.h16
-rw-r--r--include/crypto/hash.h125
-rw-r--r--include/crypto/internal/hash.h16
-rw-r--r--include/drm/Kbuild2
-rw-r--r--include/drm/drm.h24
-rw-r--r--include/drm/drmP.h108
-rw-r--r--include/drm/drm_crtc.h733
-rw-r--r--include/drm/drm_crtc_helper.h124
-rw-r--r--include/drm/drm_edid.h202
-rw-r--r--include/drm/drm_mode.h271
-rw-r--r--include/drm/drm_sarea.h6
-rw-r--r--include/drm/i915_drm.h41
-rw-r--r--include/keys/keyring-type.h31
-rw-r--r--include/linux/aio.h5
-rw-r--r--include/linux/atm.h17
-rw-r--r--include/linux/atmdev.h15
-rw-r--r--include/linux/audit.h26
-rw-r--r--include/linux/binfmts.h16
-rw-r--r--include/linux/bio.h26
-rw-r--r--include/linux/bitmap.h35
-rw-r--r--include/linux/bitops.h13
-rw-r--r--include/linux/blkdev.h52
-rw-r--r--include/linux/bottom_half.h1
-rw-r--r--include/linux/buffer_head.h1
-rw-r--r--include/linux/capability.h25
-rw-r--r--include/linux/cgroup_subsys.h6
-rw-r--r--include/linux/console.h4
-rw-r--r--include/linux/cpumask.h221
-rw-r--r--include/linux/crc32c.h6
-rw-r--r--include/linux/cred.h342
-rw-r--r--include/linux/crypto.h10
-rw-r--r--include/linux/dcbnl.h340
-rw-r--r--include/linux/dccp.h42
-rw-r--r--include/linux/dmi.h5
-rw-r--r--include/linux/elevator.h8
-rw-r--r--include/linux/etherdevice.h46
-rw-r--r--include/linux/ethtool.h2
-rw-r--r--include/linux/fault-inject.h9
-rw-r--r--include/linux/fb.h2
-rw-r--r--include/linux/fddidevice.h1
-rw-r--r--include/linux/filter.h3
-rw-r--r--include/linux/fs.h18
-rw-r--r--include/linux/fsl_devices.h18
-rw-r--r--include/linux/ftrace.h21
-rw-r--r--include/linux/futex.h3
-rw-r--r--include/linux/genhd.h1
-rw-r--r--include/linux/hardirq.h13
-rw-r--r--include/linux/hdlc.h4
-rw-r--r--include/linux/hippidevice.h4
-rw-r--r--include/linux/hrtimer.h34
-rw-r--r--include/linux/ide.h37
-rw-r--r--include/linux/ieee80211.h212
-rw-r--r--include/linux/if.h1
-rw-r--r--include/linux/if_arp.h3
-rw-r--r--include/linux/in.h4
-rw-r--r--include/linux/init_task.h14
-rw-r--r--include/linux/input.h2
-rw-r--r--include/linux/interrupt.h5
-rw-r--r--include/linux/ipv6.h1
-rw-r--r--include/linux/irq.h7
-rw-r--r--include/linux/jiffies.h10
-rw-r--r--include/linux/kernel.h12
-rw-r--r--include/linux/key-ui.h66
-rw-r--r--include/linux/key.h32
-rw-r--r--include/linux/keyctl.h4
-rw-r--r--include/linux/lguest_launcher.h6
-rw-r--r--include/linux/libata.h71
-rw-r--r--include/linux/linkage.h8
-rw-r--r--include/linux/list_nulls.h94
-rw-r--r--include/linux/lockd/bind.h1
-rw-r--r--include/linux/lockd/lockd.h4
-rw-r--r--include/linux/lockdep.h14
-rw-r--r--include/linux/mdio-gpio.h25
-rw-r--r--include/linux/mfd/wm8350/audio.h38
-rw-r--r--include/linux/mii.h33
-rw-r--r--include/linux/mlx4/device.h4
-rw-r--r--include/linux/mm.h21
-rw-r--r--include/linux/mm_types.h5
-rw-r--r--include/linux/mroute6.h26
-rw-r--r--include/linux/netdevice.h400
-rw-r--r--include/linux/netfilter/nfnetlink_conntrack.h1
-rw-r--r--include/linux/netfilter_bridge/ebtables.h3
-rw-r--r--include/linux/netfilter_ipv4/ipt_policy.h2
-rw-r--r--include/linux/netfilter_ipv6/ip6t_policy.h2
-rw-r--r--include/linux/netlink.h3
-rw-r--r--include/linux/netpoll.h5
-rw-r--r--include/linux/nfs_fs.h17
-rw-r--r--include/linux/nfs_fs_sb.h6
-rw-r--r--include/linux/nfs_mount.h3
-rw-r--r--include/linux/nfs_xdr.h7
-rw-r--r--include/linux/nfsd/state.h2
-rw-r--r--include/linux/nl80211.h223
-rw-r--r--include/linux/nsproxy.h1
-rw-r--r--include/linux/of.h6
-rw-r--r--include/linux/of_gpio.h44
-rw-r--r--include/linux/of_platform.h3
-rw-r--r--include/linux/oprofile.h3
-rw-r--r--include/linux/pci.h6
-rw-r--r--include/linux/pci_ids.h5
-rw-r--r--include/linux/phy.h2
-rw-r--r--include/linux/pkt_cls.h14
-rw-r--r--include/linux/pkt_sched.h16
-rw-r--r--include/linux/posix-timers.h6
-rw-r--r--include/linux/ptrace.h22
-rw-r--r--include/linux/rcuclassic.h4
-rw-r--r--include/linux/rculist_nulls.h110
-rw-r--r--include/linux/rcupdate.h10
-rw-r--r--include/linux/rcutree.h329
-rw-r--r--include/linux/rfkill.h8
-rw-r--r--include/linux/ring_buffer.h2
-rw-r--r--include/linux/rio_drv.h4
-rw-r--r--include/linux/rtnetlink.h5
-rw-r--r--include/linux/sched.h69
-rw-r--r--include/linux/securebits.h2
-rw-r--r--include/linux/security.h344
-rw-r--r--include/linux/seq_file.h7
-rw-r--r--include/linux/serial_core.h2
-rw-r--r--include/linux/skbuff.h54
-rw-r--r--include/linux/slab.h10
-rw-r--r--include/linux/smp.h18
-rw-r--r--include/linux/smsc911x.h47
-rw-r--r--include/linux/snmp.h3
-rw-r--r--include/linux/stop_machine.h6
-rw-r--r--include/linux/sunrpc/clnt.h2
-rw-r--r--include/linux/sunrpc/rpc_pipe_fs.h1
-rw-r--r--include/linux/sunrpc/svc_xprt.h8
-rw-r--r--include/linux/sunrpc/svcauth_gss.h1
-rw-r--r--include/linux/sunrpc/xdr.h15
-rw-r--r--include/linux/sunrpc/xprt.h3
-rw-r--r--include/linux/swiotlb.h22
-rw-r--r--include/linux/threads.h16
-rw-r--r--include/linux/tick.h4
-rw-r--r--include/linux/timex.h73
-rw-r--r--include/linux/tty.h4
-rw-r--r--include/linux/types.h11
-rw-r--r--include/linux/user_namespace.h13
-rw-r--r--include/linux/videodev2.h14
-rw-r--r--include/linux/virtio_balloon.h3
-rw-r--r--include/linux/virtio_console.h11
-rw-r--r--include/linux/virtio_net.h9
-rw-r--r--include/linux/virtio_pci.h8
-rw-r--r--include/linux/virtio_ring.h13
-rw-r--r--include/linux/xfrm.h14
-rw-r--r--include/media/i2c-addr.h2
-rw-r--r--include/media/ir-common.h2
-rw-r--r--include/media/ov772x.h21
-rw-r--r--include/media/saa7146_vv.h3
-rw-r--r--include/media/soc_camera.h111
-rw-r--r--include/media/tvp514x.h118
-rw-r--r--include/media/tw9910.h39
-rw-r--r--include/media/v4l2-chip-ident.h10
-rw-r--r--include/media/v4l2-common.h41
-rw-r--r--include/media/v4l2-dev.h51
-rw-r--r--include/media/v4l2-device.h109
-rw-r--r--include/media/v4l2-int-device.h6
-rw-r--r--include/media/v4l2-ioctl.h14
-rw-r--r--include/media/v4l2-subdev.h189
-rw-r--r--include/net/bluetooth/bluetooth.h4
-rw-r--r--include/net/bluetooth/hci.h2
-rw-r--r--include/net/cfg80211.h142
-rw-r--r--include/net/checksum.h2
-rw-r--r--include/net/dcbnl.h53
-rw-r--r--include/net/dn.h8
-rw-r--r--include/net/dn_fib.h6
-rw-r--r--include/net/dst.h39
-rw-r--r--include/net/flow.h9
-rw-r--r--include/net/gen_stats.h3
-rw-r--r--include/net/ieee80211.h148
-rw-r--r--include/net/ieee80211_crypt.h108
-rw-r--r--include/net/ieee80211_radiotap.h15
-rw-r--r--include/net/inet_hashtables.h85
-rw-r--r--include/net/inet_timewait_sock.h10
-rw-r--r--include/net/ip.h3
-rw-r--r--include/net/ip_vs.h17
-rw-r--r--include/net/irda/irda_device.h4
-rw-r--r--include/net/iucv/iucv.h45
-rw-r--r--include/net/lib80211.h129
-rw-r--r--include/net/mac80211.h446
-rw-r--r--include/net/ndisc.h14
-rw-r--r--include/net/neighbour.h15
-rw-r--r--include/net/net_namespace.h22
-rw-r--r--include/net/netfilter/nf_conntrack.h5
-rw-r--r--include/net/netfilter/nf_conntrack_ecache.h57
-rw-r--r--include/net/netfilter/nf_conntrack_expect.h2
-rw-r--r--include/net/netfilter/nf_conntrack_helper.h5
-rw-r--r--include/net/netfilter/nf_conntrack_l4proto.h2
-rw-r--r--include/net/netfilter/nf_conntrack_tuple.h12
-rw-r--r--include/net/netfilter/nfnetlink_log.h14
-rw-r--r--include/net/netlink.h24
-rw-r--r--include/net/netns/ipv4.h2
-rw-r--r--include/net/netns/ipv6.h12
-rw-r--r--include/net/netns/mib.h3
-rw-r--r--include/net/netns/x_tables.h5
-rw-r--r--include/net/netns/xfrm.h56
-rw-r--r--include/net/phonet/pep.h2
-rw-r--r--include/net/phonet/phonet.h2
-rw-r--r--include/net/phonet/pn_dev.h2
-rw-r--r--include/net/pkt_cls.h2
-rw-r--r--include/net/protocol.h3
-rw-r--r--include/net/sch_generic.h38
-rw-r--r--include/net/scm.h4
-rw-r--r--include/net/sctp/sctp.h9
-rw-r--r--include/net/sctp/user.h2
-rw-r--r--include/net/sock.h86
-rw-r--r--include/net/syncppp.h102
-rw-r--r--include/net/tcp.h20
-rw-r--r--include/net/udp.h25
-rw-r--r--include/net/udplite.h2
-rw-r--r--include/net/wireless.h90
-rw-r--r--include/net/xfrm.h110
-rw-r--r--include/scsi/fc/fc_els.h816
-rw-r--r--include/scsi/fc/fc_encaps.h138
-rw-r--r--include/scsi/fc/fc_fc2.h124
-rw-r--r--include/scsi/fc/fc_fcoe.h114
-rw-r--r--include/scsi/fc/fc_fcp.h199
-rw-r--r--include/scsi/fc/fc_fs.h340
-rw-r--r--include/scsi/fc/fc_gs.h93
-rw-r--r--include/scsi/fc/fc_ns.h159
-rw-r--r--include/scsi/fc_encode.h309
-rw-r--r--include/scsi/fc_frame.h242
-rw-r--r--include/scsi/fc_transport_fcoe.h54
-rw-r--r--include/scsi/iscsi_if.h7
-rw-r--r--include/scsi/libfc.h938
-rw-r--r--include/scsi/libfcoe.h176
-rw-r--r--include/scsi/libiscsi.h39
-rw-r--r--include/scsi/libiscsi_tcp.h132
-rw-r--r--include/scsi/scsi_device.h7
-rw-r--r--include/scsi/scsi_transport_iscsi.h12
-rw-r--r--include/sound/ac97_codec.h2
-rw-r--r--include/sound/asound.h1
-rw-r--r--include/sound/core.h28
-rw-r--r--include/sound/info.h106
-rw-r--r--include/sound/jack.h2
-rw-r--r--include/sound/l3.h18
-rw-r--r--include/sound/s3c24xx_uda134x.h14
-rw-r--r--include/sound/soc-dai.h231
-rw-r--r--include/sound/soc-dapm.h2
-rw-r--r--include/sound/soc.h206
-rw-r--r--include/sound/uda134x.h26
-rw-r--r--include/sound/version.h2
-rw-r--r--include/trace/sched.h12
-rw-r--r--include/video/sh_mobile_lcdc.h1
-rw-r--r--include/xen/interface/event_channel.h2
-rw-r--r--init/Kconfig93
-rw-r--r--init/main.c14
-rw-r--r--ipc/mqueue.c19
-rw-r--r--ipc/shm.c9
-rw-r--r--ipc/util.c18
-rw-r--r--kernel/Kconfig.preempt25
-rw-r--r--kernel/Makefile8
-rw-r--r--kernel/acct.c7
-rw-r--r--kernel/auditsc.c255
-rw-r--r--kernel/capability.c288
-rw-r--r--kernel/cgroup.c25
-rw-r--r--kernel/compat.c49
-rw-r--r--kernel/cpu.c144
-rw-r--r--kernel/cred-internals.h21
-rw-r--r--kernel/cred.c588
-rw-r--r--kernel/delayacct.c2
-rw-r--r--kernel/exit.c25
-rw-r--r--kernel/fork.c68
-rw-r--r--kernel/futex.c81
-rw-r--r--kernel/futex_compat.c7
-rw-r--r--kernel/hrtimer.c331
-rw-r--r--kernel/irq/chip.c1
-rw-r--r--kernel/irq/handle.c5
-rw-r--r--kernel/irq/manage.c38
-rw-r--r--kernel/irq/numa_migrate.c11
-rw-r--r--kernel/irq/proc.c32
-rw-r--r--kernel/kallsyms.c16
-rw-r--r--kernel/kexec.c2
-rw-r--r--kernel/kmod.c30
-rw-r--r--kernel/lockdep.c27
-rw-r--r--kernel/nsproxy.c15
-rw-r--r--kernel/panic.c32
-rw-r--r--kernel/posix-timers.c46
-rw-r--r--kernel/power/poweroff.c2
-rw-r--r--kernel/printk.c2
-rw-r--r--kernel/profile.c38
-rw-r--r--kernel/ptrace.c41
-rw-r--r--kernel/rcuclassic.c29
-rw-r--r--kernel/rcupreempt.c29
-rw-r--r--kernel/rcupreempt_trace.c10
-rw-r--r--kernel/rcutorture.c93
-rw-r--r--kernel/rcutree.c1535
-rw-r--r--kernel/rcutree_trace.c271
-rw-r--r--kernel/resource.c9
-rw-r--r--kernel/sched.c115
-rw-r--r--kernel/sched_fair.c9
-rw-r--r--kernel/sched_rt.c2
-rw-r--r--kernel/sched_stats.h5
-rw-r--r--kernel/signal.c60
-rw-r--r--kernel/smp.c145
-rw-r--r--kernel/softirq.c21
-rw-r--r--kernel/softlockup.c10
-rw-r--r--kernel/stacktrace.c11
-rw-r--r--kernel/stop_machine.c8
-rw-r--r--kernel/sys.c586
-rw-r--r--kernel/sysctl.c26
-rw-r--r--kernel/sysctl_check.c1
-rw-r--r--kernel/taskstats.c39
-rw-r--r--kernel/time/clocksource.c9
-rw-r--r--kernel/time/ntp.c4
-rw-r--r--kernel/time/tick-broadcast.c115
-rw-r--r--kernel/time/tick-common.c6
-rw-r--r--kernel/time/tick-sched.c44
-rw-r--r--kernel/timer.c8
-rw-r--r--kernel/trace/Kconfig17
-rw-r--r--kernel/trace/Makefile2
-rw-r--r--kernel/trace/ftrace.c15
-rw-r--r--kernel/trace/ring_buffer.c103
-rw-r--r--kernel/trace/trace.c156
-rw-r--r--kernel/trace/trace.h20
-rw-r--r--kernel/trace/trace_boot.c14
-rw-r--r--kernel/trace/trace_bts.c276
-rw-r--r--kernel/trace/trace_functions.c14
-rw-r--r--kernel/trace/trace_functions_graph.c68
-rw-r--r--kernel/trace/trace_hw_branches.c195
-rw-r--r--kernel/trace/trace_mmiotrace.c6
-rw-r--r--kernel/trace/trace_power.c2
-rw-r--r--kernel/trace/trace_sched_switch.c17
-rw-r--r--kernel/trace/trace_sched_wakeup.c2
-rw-r--r--kernel/trace/trace_stack.c49
-rw-r--r--kernel/trace/trace_sysprof.c26
-rw-r--r--kernel/tsacct.c6
-rw-r--r--kernel/uid16.c31
-rw-r--r--kernel/user.c96
-rw-r--r--kernel/user_namespace.c65
-rw-r--r--kernel/workqueue.c34
-rw-r--r--lib/Kconfig10
-rw-r--r--lib/Kconfig.debug22
-rw-r--r--lib/Makefile3
-rw-r--r--lib/bug.c19
-rw-r--r--lib/cpumask.c62
-rw-r--r--lib/debugobjects.c4
-rw-r--r--lib/find_last_bit.c45
-rw-r--r--lib/is_single_threaded.c45
-rw-r--r--lib/libcrc32c.c182
-rw-r--r--lib/swiotlb.c255
-rw-r--r--lib/vsprintf.c77
-rw-r--r--mm/Makefile1
-rw-r--r--mm/bounce.c9
-rw-r--r--mm/failslab.c59
-rw-r--r--mm/memory.c70
-rw-r--r--mm/mempolicy.c9
-rw-r--r--mm/migrate.c9
-rw-r--r--mm/mlock.c45
-rw-r--r--mm/oom_kill.c12
-rw-r--r--mm/pdflush.c16
-rw-r--r--mm/shmem.c8
-rw-r--r--mm/slab.c91
-rw-r--r--mm/slub.c118
-rw-r--r--mm/swapfile.c9
-rw-r--r--mm/vmscan.c4
-rw-r--r--mm/vmstat.c4
-rw-r--r--net/802/fddi.c8
-rw-r--r--net/802/hippi.c14
-rw-r--r--net/802/tr.c24
-rw-r--r--net/8021q/vlan.c26
-rw-r--r--net/8021q/vlan.h6
-rw-r--r--net/8021q/vlan_core.c2
-rw-r--r--net/8021q/vlan_dev.c76
-rw-r--r--net/9p/client.c2
-rw-r--r--net/9p/trans_rdma.c2
-rw-r--r--net/Kconfig7
-rw-r--r--net/Makefile4
-rw-r--r--net/appletalk/aarp.c3
-rw-r--r--net/appletalk/ddp.c5
-rw-r--r--net/appletalk/sysctl_net_atalk.c14
-rw-r--r--net/atm/atm_sysfs.c2
-rw-r--r--net/atm/br2684.c7
-rw-r--r--net/atm/clip.c4
-rw-r--r--net/atm/common.h1
-rw-r--r--net/atm/ioctl.c49
-rw-r--r--net/atm/lec.c60
-rw-r--r--net/atm/mpc.c40
-rw-r--r--net/atm/mpoa_caches.c18
-rw-r--r--net/atm/pvc.c3
-rw-r--r--net/atm/resources.c88
-rw-r--r--net/atm/resources.h2
-rw-r--r--net/atm/svc.c19
-rw-r--r--net/ax25/af_ax25.c2
-rw-r--r--net/ax25/ax25_in.c41
-rw-r--r--net/ax25/ax25_route.c2
-rw-r--r--net/ax25/sysctl_net_ax25.c58
-rw-r--r--net/bluetooth/af_bluetooth.c50
-rw-r--r--net/bluetooth/bnep/bnep.h2
-rw-r--r--net/bluetooth/bnep/core.c8
-rw-r--r--net/bluetooth/bnep/netdev.c11
-rw-r--r--net/bluetooth/bnep/sock.c5
-rw-r--r--net/bluetooth/cmtp/capi.c5
-rw-r--r--net/bluetooth/cmtp/core.c5
-rw-r--r--net/bluetooth/cmtp/sock.c5
-rw-r--r--net/bluetooth/hci_conn.c5
-rw-r--r--net/bluetooth/hci_core.c11
-rw-r--r--net/bluetooth/hci_event.c5
-rw-r--r--net/bluetooth/hci_sock.c5
-rw-r--r--net/bluetooth/hci_sysfs.c12
-rw-r--r--net/bluetooth/hidp/core.c5
-rw-r--r--net/bluetooth/hidp/sock.c5
-rw-r--r--net/bluetooth/l2cap.c5
-rw-r--r--net/bluetooth/rfcomm/core.c7
-rw-r--r--net/bluetooth/rfcomm/sock.c9
-rw-r--r--net/bluetooth/rfcomm/tty.c48
-rw-r--r--net/bluetooth/sco.c5
-rw-r--r--net/bridge/br_device.c20
-rw-r--r--net/bridge/br_if.c4
-rw-r--r--net/bridge/br_netfilter.c13
-rw-r--r--net/bridge/br_sysfs_br.c2
-rw-r--r--net/bridge/netfilter/ebt_log.c18
-rw-r--r--net/bridge/netfilter/ebtable_broute.c26
-rw-r--r--net/bridge/netfilter/ebtable_filter.c41
-rw-r--r--net/bridge/netfilter/ebtable_nat.c38
-rw-r--r--net/bridge/netfilter/ebtables.c52
-rw-r--r--net/can/raw.c22
-rw-r--r--net/core/datagram.c5
-rw-r--r--net/core/dev.c472
-rw-r--r--net/core/dst.c6
-rw-r--r--net/core/ethtool.c53
-rw-r--r--net/core/fib_rules.c7
-rw-r--r--net/core/filter.c19
-rw-r--r--net/core/flow.c6
-rw-r--r--net/core/gen_estimator.c97
-rw-r--r--net/core/neighbour.c77
-rw-r--r--net/core/net-sysfs.c15
-rw-r--r--net/core/net_namespace.c2
-rw-r--r--net/core/netpoll.c20
-rw-r--r--net/core/pktgen.c42
-rw-r--r--net/core/rtnetlink.c15
-rw-r--r--net/core/scm.c10
-rw-r--r--net/core/skbuff.c277
-rw-r--r--net/core/sock.c46
-rw-r--r--net/core/sysctl_net_core.c68
-rw-r--r--net/dcb/Kconfig22
-rw-r--r--net/dcb/Makefile1
-rw-r--r--net/dcb/dcbnl.c1122
-rw-r--r--net/dccp/ackvec.c9
-rw-r--r--net/dccp/ackvec.h5
-rw-r--r--net/dccp/ccid.c62
-rw-r--r--net/dccp/ccid.h26
-rw-r--r--net/dccp/ccids/ccid2.c6
-rw-r--r--net/dccp/dccp.h17
-rw-r--r--net/dccp/diag.c11
-rw-r--r--net/dccp/feat.c1458
-rw-r--r--net/dccp/feat.h130
-rw-r--r--net/dccp/input.c44
-rw-r--r--net/dccp/ipv4.c13
-rw-r--r--net/dccp/ipv6.c15
-rw-r--r--net/dccp/minisocks.c54
-rw-r--r--net/dccp/options.c229
-rw-r--r--net/dccp/output.c19
-rw-r--r--net/dccp/probe.c19
-rw-r--r--net/dccp/proto.c225
-rw-r--r--net/dccp/sysctl.c21
-rw-r--r--net/dccp/timer.c12
-rw-r--r--net/decnet/af_decnet.c62
-rw-r--r--net/decnet/dn_dev.c22
-rw-r--r--net/decnet/dn_neigh.c18
-rw-r--r--net/decnet/dn_nsp_in.c28
-rw-r--r--net/decnet/dn_nsp_out.c23
-rw-r--r--net/decnet/dn_route.c29
-rw-r--r--net/decnet/dn_table.c2
-rw-r--r--net/decnet/sysctl_net_decnet.c48
-rw-r--r--net/dsa/mv88e6060.c6
-rw-r--r--net/dsa/mv88e6123_61_65.c4
-rw-r--r--net/dsa/mv88e6131.c4
-rw-r--r--net/dsa/mv88e6xxx.c2
-rw-r--r--net/dsa/slave.c2
-rw-r--r--net/dsa/tag_dsa.c1
-rw-r--r--net/dsa/tag_edsa.c1
-rw-r--r--net/dsa/tag_trailer.c1
-rw-r--r--net/ethernet/eth.c19
-rw-r--r--net/ieee80211/Kconfig73
-rw-r--r--net/ieee80211/Makefile12
-rw-r--r--net/ieee80211/ieee80211_crypt.c206
-rw-r--r--net/ieee80211/ieee80211_crypt_ccmp.c493
-rw-r--r--net/ieee80211/ieee80211_crypt_tkip.c787
-rw-r--r--net/ieee80211/ieee80211_crypt_wep.c295
-rw-r--r--net/ieee80211/ieee80211_module.c338
-rw-r--r--net/ieee80211/ieee80211_rx.c1831
-rw-r--r--net/ieee80211/ieee80211_tx.c545
-rw-r--r--net/ieee80211/ieee80211_wx.c760
-rw-r--r--net/ipv4/af_inet.c116
-rw-r--r--net/ipv4/ah4.c10
-rw-r--r--net/ipv4/arp.c36
-rw-r--r--net/ipv4/cipso_ipv4.c1
-rw-r--r--net/ipv4/devinet.c19
-rw-r--r--net/ipv4/esp4.c8
-rw-r--r--net/ipv4/fib_frontend.c10
-rw-r--r--net/ipv4/fib_hash.c12
-rw-r--r--net/ipv4/fib_semantics.c8
-rw-r--r--net/ipv4/fib_trie.c6
-rw-r--r--net/ipv4/icmp.c39
-rw-r--r--net/ipv4/igmp.c95
-rw-r--r--net/ipv4/inet_connection_sock.c29
-rw-r--r--net/ipv4/inet_diag.c31
-rw-r--r--net/ipv4/inet_hashtables.c277
-rw-r--r--net/ipv4/inet_lro.c4
-rw-r--r--net/ipv4/inet_timewait_sock.c48
-rw-r--r--net/ipv4/inetpeer.c2
-rw-r--r--net/ipv4/ip_forward.c2
-rw-r--r--net/ipv4/ip_fragment.c21
-rw-r--r--net/ipv4/ip_gre.c58
-rw-r--r--net/ipv4/ip_input.c10
-rw-r--r--net/ipv4/ip_output.c24
-rw-r--r--net/ipv4/ip_sockglue.c72
-rw-r--r--net/ipv4/ipcomp.c10
-rw-r--r--net/ipv4/ipconfig.c40
-rw-r--r--net/ipv4/ipip.c37
-rw-r--r--net/ipv4/ipmr.c280
-rw-r--r--net/ipv4/netfilter.c7
-rw-r--r--net/ipv4/netfilter/arp_tables.c16
-rw-r--r--net/ipv4/netfilter/arptable_filter.c12
-rw-r--r--net/ipv4/netfilter/ip_tables.c12
-rw-r--r--net/ipv4/netfilter/ipt_CLUSTERIP.c9
-rw-r--r--net/ipv4/netfilter/ipt_LOG.c11
-rw-r--r--net/ipv4/netfilter/ipt_addrtype.c16
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c27
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c4
-rw-r--r--net/ipv4/netfilter/nf_conntrack_proto_icmp.c4
-rw-r--r--net/ipv4/netfilter/nf_nat_h323.c58
-rw-r--r--net/ipv4/netfilter/nf_nat_irc.c4
-rw-r--r--net/ipv4/netfilter/nf_nat_rule.c27
-rw-r--r--net/ipv4/netfilter/nf_nat_sip.c18
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic.c9
-rw-r--r--net/ipv4/proc.c15
-rw-r--r--net/ipv4/raw.c10
-rw-r--r--net/ipv4/route.c231
-rw-r--r--net/ipv4/sysctl_net_ipv4.c188
-rw-r--r--net/ipv4/tcp.c131
-rw-r--r--net/ipv4/tcp_cubic.c120
-rw-r--r--net/ipv4/tcp_diag.c2
-rw-r--r--net/ipv4/tcp_input.c511
-rw-r--r--net/ipv4/tcp_ipv4.c140
-rw-r--r--net/ipv4/tcp_minisocks.c2
-rw-r--r--net/ipv4/tcp_output.c219
-rw-r--r--net/ipv4/tcp_probe.c7
-rw-r--r--net/ipv4/tcp_timer.c14
-rw-r--r--net/ipv4/tcp_yeah.c4
-rw-r--r--net/ipv4/udp.c271
-rw-r--r--net/ipv4/udp_impl.h4
-rw-r--r--net/ipv4/udplite.c14
-rw-r--r--net/ipv4/xfrm4_input.c4
-rw-r--r--net/ipv4/xfrm4_policy.c15
-rw-r--r--net/ipv4/xfrm4_state.c2
-rw-r--r--net/ipv6/addrconf.c80
-rw-r--r--net/ipv6/addrlabel.c34
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/ah6.c9
-rw-r--r--net/ipv6/anycast.c6
-rw-r--r--net/ipv6/datagram.c3
-rw-r--r--net/ipv6/esp6.c7
-rw-r--r--net/ipv6/exthdrs.c2
-rw-r--r--net/ipv6/icmp.c21
-rw-r--r--net/ipv6/inet6_connection_sock.c2
-rw-r--r--net/ipv6/inet6_hashtables.c182
-rw-r--r--net/ipv6/ip6_flowlabel.c8
-rw-r--r--net/ipv6/ip6_output.c5
-rw-r--r--net/ipv6/ip6_tunnel.c37
-rw-r--r--net/ipv6/ip6mr.c490
-rw-r--r--net/ipv6/ipcomp6.c10
-rw-r--r--net/ipv6/ipv6_sockglue.c34
-rw-r--r--net/ipv6/mcast.c41
-rw-r--r--net/ipv6/mip6.c3
-rw-r--r--net/ipv6/ndisc.c105
-rw-r--r--net/ipv6/netfilter.c7
-rw-r--r--net/ipv6/netfilter/ip6t_LOG.c11
-rw-r--r--net/ipv6/netfilter/ip6t_REJECT.c2
-rw-r--r--net/ipv6/netfilter/ip6table_filter.c17
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c5
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c2
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c6
-rw-r--r--net/ipv6/raw.c3
-rw-r--r--net/ipv6/reassembly.c12
-rw-r--r--net/ipv6/route.c47
-rw-r--r--net/ipv6/sit.c35
-rw-r--r--net/ipv6/syncookies.c2
-rw-r--r--net/ipv6/sysctl_net_ipv6.c4
-rw-r--r--net/ipv6/tcp_ipv6.c31
-rw-r--r--net/ipv6/udp.c151
-rw-r--r--net/ipv6/udp_impl.h4
-rw-r--r--net/ipv6/udplite.c9
-rw-r--r--net/ipv6/xfrm6_input.c9
-rw-r--r--net/ipv6/xfrm6_policy.c22
-rw-r--r--net/ipv6/xfrm6_state.c2
-rw-r--r--net/ipv6/xfrm6_tunnel.c33
-rw-r--r--net/ipx/sysctl_net_ipx.c2
-rw-r--r--net/irda/irlan/irlan_client.c4
-rw-r--r--net/irda/irlan/irlan_common.c2
-rw-r--r--net/irda/irlap_frame.c17
-rw-r--r--net/irda/irsysctl.c50
-rw-r--r--net/irda/irttp.c14
-rw-r--r--net/irda/timer.c2
-rw-r--r--net/iucv/af_iucv.c19
-rw-r--r--net/iucv/iucv.c161
-rw-r--r--net/key/af_key.c217
-rw-r--r--net/llc/af_llc.c1
-rw-r--r--net/llc/llc_proc.c3
-rw-r--r--net/llc/sysctl_net_llc.c20
-rw-r--r--net/mac80211/Kconfig8
-rw-r--r--net/mac80211/Makefile2
-rw-r--r--net/mac80211/cfg.c194
-rw-r--r--net/mac80211/debugfs.c12
-rw-r--r--net/mac80211/debugfs_key.c4
-rw-r--r--net/mac80211/debugfs_netdev.c117
-rw-r--r--net/mac80211/debugfs_sta.c84
-rw-r--r--net/mac80211/event.c5
-rw-r--r--net/mac80211/ht.c265
-rw-r--r--net/mac80211/ieee80211_i.h90
-rw-r--r--net/mac80211/iface.c54
-rw-r--r--net/mac80211/key.c10
-rw-r--r--net/mac80211/main.c284
-rw-r--r--net/mac80211/mesh.c4
-rw-r--r--net/mac80211/mesh.h5
-rw-r--r--net/mac80211/mesh_hwmp.c7
-rw-r--r--net/mac80211/mesh_plink.c49
-rw-r--r--net/mac80211/mlme.c606
-rw-r--r--net/mac80211/rate.c52
-rw-r--r--net/mac80211/rate.h11
-rw-r--r--net/mac80211/rc80211_minstrel.c109
-rw-r--r--net/mac80211/rc80211_minstrel.h2
-rw-r--r--net/mac80211/rc80211_pid.h3
-rw-r--r--net/mac80211/rc80211_pid_algo.c38
-rw-r--r--net/mac80211/rc80211_pid_debugfs.c5
-rw-r--r--net/mac80211/rx.c174
-rw-r--r--net/mac80211/scan.c24
-rw-r--r--net/mac80211/sta_info.c37
-rw-r--r--net/mac80211/sta_info.h45
-rw-r--r--net/mac80211/tkip.c10
-rw-r--r--net/mac80211/tx.c462
-rw-r--r--net/mac80211/util.c107
-rw-r--r--net/mac80211/wep.c33
-rw-r--r--net/mac80211/wep.h2
-rw-r--r--net/mac80211/wext.c257
-rw-r--r--net/mac80211/wme.c32
-rw-r--r--net/mac80211/wpa.c32
-rw-r--r--net/netfilter/Kconfig5
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c20
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c20
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c82
-rw-r--r--net/netfilter/ipvs/ip_vs_dh.c34
-rw-r--r--net/netfilter/ipvs/ip_vs_ftp.c13
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c71
-rw-r--r--net/netfilter/ipvs/ip_vs_lblcr.c98
-rw-r--r--net/netfilter/ipvs/ip_vs_lc.c17
-rw-r--r--net/netfilter/ipvs/ip_vs_nq.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_proto.c38
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_ah_esp.c10
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_tcp.c8
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_udp.c8
-rw-r--r--net/netfilter/ipvs/ip_vs_rr.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_sed.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_sh.c35
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_wlc.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_wrr.c11
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c34
-rw-r--r--net/netfilter/nf_conntrack_acct.c2
-rw-r--r--net/netfilter/nf_conntrack_amanda.c1
-rw-r--r--net/netfilter/nf_conntrack_core.c61
-rw-r--r--net/netfilter/nf_conntrack_ecache.c14
-rw-r--r--net/netfilter/nf_conntrack_expect.c43
-rw-r--r--net/netfilter/nf_conntrack_ftp.c24
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c13
-rw-r--r--net/netfilter/nf_conntrack_helper.c32
-rw-r--r--net/netfilter/nf_conntrack_irc.c14
-rw-r--r--net/netfilter/nf_conntrack_netbios_ns.c1
-rw-r--r--net/netfilter/nf_conntrack_netlink.c201
-rw-r--r--net/netfilter/nf_conntrack_pptp.c1
-rw-r--r--net/netfilter/nf_conntrack_proto_generic.c4
-rw-r--r--net/netfilter/nf_conntrack_proto_gre.c2
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c30
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c50
-rw-r--r--net/netfilter/nf_conntrack_proto_udp.c8
-rw-r--r--net/netfilter/nf_conntrack_proto_udplite.c4
-rw-r--r--net/netfilter/nf_conntrack_sane.c1
-rw-r--r--net/netfilter/nf_conntrack_sip.c1
-rw-r--r--net/netfilter/nf_conntrack_standalone.c20
-rw-r--r--net/netfilter/nf_conntrack_tftp.c1
-rw-r--r--net/netfilter/nfnetlink_log.c9
-rw-r--r--net/netfilter/xt_NFLOG.c5
-rw-r--r--net/netfilter/xt_dccp.c16
-rw-r--r--net/netfilter/xt_hashlimit.c14
-rw-r--r--net/netfilter/xt_iprange.c36
-rw-r--r--net/netfilter/xt_owner.c16
-rw-r--r--net/netfilter/xt_recent.c32
-rw-r--r--net/netlabel/netlabel_addrlist.c4
-rw-r--r--net/netlabel/netlabel_mgmt.c1
-rw-r--r--net/netlink/af_netlink.c7
-rw-r--r--net/netlink/attr.c8
-rw-r--r--net/netrom/af_netrom.c4
-rw-r--r--net/netrom/sysctl_net_netrom.c48
-rw-r--r--net/packet/af_packet.c2
-rw-r--r--net/phonet/af_phonet.c8
-rw-r--r--net/phonet/pep-gprs.c218
-rw-r--r--net/phonet/pep.c38
-rw-r--r--net/phonet/pn_dev.c6
-rw-r--r--net/phonet/socket.c13
-rw-r--r--net/phonet/sysctl.c4
-rw-r--r--net/rfkill/rfkill-input.c337
-rw-r--r--net/rfkill/rfkill-input.h3
-rw-r--r--net/rfkill/rfkill.c183
-rw-r--r--net/rose/af_rose.c4
-rw-r--r--net/rose/sysctl_net_rose.c40
-rw-r--r--net/rxrpc/af_rxrpc.c4
-rw-r--r--net/rxrpc/ar-connection.c2
-rw-r--r--net/rxrpc/ar-connevent.c2
-rw-r--r--net/rxrpc/ar-error.c3
-rw-r--r--net/rxrpc/ar-key.c6
-rw-r--r--net/rxrpc/ar-local.c16
-rw-r--r--net/rxrpc/ar-peer.c8
-rw-r--r--net/rxrpc/ar-proc.c16
-rw-r--r--net/rxrpc/ar-security.c2
-rw-r--r--net/rxrpc/ar-transport.c12
-rw-r--r--net/rxrpc/rxkad.c2
-rw-r--r--net/sched/Kconfig19
-rw-r--r--net/sched/Makefile2
-rw-r--r--net/sched/act_api.c18
-rw-r--r--net/sched/act_gact.c4
-rw-r--r--net/sched/act_ipt.c4
-rw-r--r--net/sched/act_mirred.c4
-rw-r--r--net/sched/act_nat.c4
-rw-r--r--net/sched/act_pedit.c4
-rw-r--r--net/sched/act_police.c33
-rw-r--r--net/sched/act_simple.c4
-rw-r--r--net/sched/act_skbedit.c4
-rw-r--r--net/sched/cls_api.c3
-rw-r--r--net/sched/cls_basic.c2
-rw-r--r--net/sched/cls_cgroup.c293
-rw-r--r--net/sched/cls_flow.c4
-rw-r--r--net/sched/cls_fw.c2
-rw-r--r--net/sched/cls_route.c2
-rw-r--r--net/sched/cls_tcindex.c6
-rw-r--r--net/sched/cls_u32.c11
-rw-r--r--net/sched/ematch.c18
-rw-r--r--net/sched/sch_api.c50
-rw-r--r--net/sched/sch_atm.c36
-rw-r--r--net/sched/sch_blackhole.c1
-rw-r--r--net/sched/sch_cbq.c76
-rw-r--r--net/sched/sch_drr.c519
-rw-r--r--net/sched/sch_dsmark.c22
-rw-r--r--net/sched/sch_fifo.c4
-rw-r--r--net/sched/sch_generic.c40
-rw-r--r--net/sched/sch_gred.c22
-rw-r--r--net/sched/sch_hfsc.c64
-rw-r--r--net/sched/sch_htb.c171
-rw-r--r--net/sched/sch_multiq.c82
-rw-r--r--net/sched/sch_netem.c160
-rw-r--r--net/sched/sch_prio.c50
-rw-r--r--net/sched/sch_red.c33
-rw-r--r--net/sched/sch_sfq.c71
-rw-r--r--net/sched/sch_tbf.c44
-rw-r--r--net/sched/sch_teql.c19
-rw-r--r--net/sctp/ipv6.c35
-rw-r--r--net/sctp/protocol.c29
-rw-r--r--net/sctp/sm_statefuns.c24
-rw-r--r--net/sctp/socket.c206
-rw-r--r--net/sctp/sysctl.c82
-rw-r--r--net/socket.c6
-rw-r--r--net/sunrpc/auth.c20
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c295
-rw-r--r--net/sunrpc/auth_gss/gss_generic_token.c6
-rw-r--r--net/sunrpc/auth_gss/gss_mech_switch.c18
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c28
-rw-r--r--net/sunrpc/clnt.c24
-rw-r--r--net/sunrpc/rpc_pipe.c42
-rw-r--r--net/sunrpc/rpcb_clnt.c17
-rw-r--r--net/sunrpc/svcauth_unix.c24
-rw-r--r--net/sunrpc/svcsock.c6
-rw-r--r--net/sunrpc/xdr.c50
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c4
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c2
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c16
-rw-r--r--net/sunrpc/xprtrdma/transport.c14
-rw-r--r--net/sunrpc/xprtrdma/verbs.c16
-rw-r--r--net/sunrpc/xprtsock.c41
-rw-r--r--net/tipc/eth_media.c3
-rw-r--r--net/tipc/name_table.c2
-rw-r--r--net/unix/af_unix.c211
-rw-r--r--net/unix/garbage.c12
-rw-r--r--net/unix/sysctl_net_unix.c3
-rw-r--r--net/wanrouter/wanmain.c36
-rw-r--r--net/wireless/Kconfig30
-rw-r--r--net/wireless/Makefile7
-rw-r--r--net/wireless/core.c14
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/lib80211.c284
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c492
-rw-r--r--net/wireless/lib80211_crypt_tkip.c784
-rw-r--r--net/wireless/lib80211_crypt_wep.c296
-rw-r--r--net/wireless/nl80211.c417
-rw-r--r--net/wireless/reg.c1005
-rw-r--r--net/wireless/reg.h33
-rw-r--r--net/wireless/sysfs.c25
-rw-r--r--net/wireless/util.c19
-rw-r--r--net/wireless/wext-compat.c139
-rw-r--r--net/wireless/wext.c2
-rw-r--r--net/x25/sysctl_net_x25.c22
-rw-r--r--net/xfrm/Makefile4
-rw-r--r--net/xfrm/xfrm_input.c25
-rw-r--r--net/xfrm/xfrm_output.c18
-rw-r--r--net/xfrm/xfrm_policy.c511
-rw-r--r--net/xfrm/xfrm_proc.c41
-rw-r--r--net/xfrm/xfrm_state.c407
-rw-r--r--net/xfrm/xfrm_sysctl.c85
-rw-r--r--net/xfrm/xfrm_user.c249
-rw-r--r--scripts/Kbuild.include22
-rw-r--r--scripts/Makefile.build67
-rw-r--r--scripts/Makefile.lib15
-rw-r--r--scripts/Makefile.modinst3
-rwxr-xr-xscripts/decodecode32
-rwxr-xr-xscripts/extract-ikconfig8
-rw-r--r--scripts/genksyms/genksyms.c277
-rw-r--r--scripts/genksyms/genksyms.h7
-rw-r--r--scripts/genksyms/keywords.c_shipped189
-rw-r--r--scripts/genksyms/keywords.gperf2
-rwxr-xr-xscripts/headerdep.pl193
-rw-r--r--scripts/kallsyms.c21
-rw-r--r--scripts/kconfig/lxdialog/check-lxdialog.sh2
-rwxr-xr-xscripts/kernel-doc67
-rwxr-xr-xscripts/mkcompile_h6
-rw-r--r--scripts/mkmakefile4
-rw-r--r--scripts/mksysmap7
-rw-r--r--scripts/package/Makefile9
-rwxr-xr-xscripts/recordmcount.pl1
-rwxr-xr-xscripts/setlocalversion7
-rw-r--r--scripts/strip-symbols22
-rwxr-xr-xscripts/tags.sh160
-rw-r--r--security/capability.c58
-rw-r--r--security/commoncap.c830
-rw-r--r--security/keys/internal.h49
-rw-r--r--security/keys/key.c25
-rw-r--r--security/keys/keyctl.c210
-rw-r--r--security/keys/keyring.c15
-rw-r--r--security/keys/permission.c29
-rw-r--r--security/keys/proc.c8
-rw-r--r--security/keys/process_keys.c469
-rw-r--r--security/keys/request_key.c135
-rw-r--r--security/keys/request_key_auth.c46
-rw-r--r--security/root_plug.c13
-rw-r--r--security/security.c103
-rw-r--r--security/selinux/avc.c4
-rw-r--r--security/selinux/exports.c8
-rw-r--r--security/selinux/hooks.c1258
-rw-r--r--security/selinux/include/av_perm_to_string.h2
-rw-r--r--security/selinux/include/av_permissions.h2
-rw-r--r--security/selinux/include/class_to_string.h5
-rw-r--r--security/selinux/include/flask.h1
-rw-r--r--security/selinux/include/objsec.h11
-rw-r--r--security/selinux/nlmsgtab.c3
-rw-r--r--security/selinux/selinuxfs.c15
-rw-r--r--security/selinux/xfrm.c6
-rw-r--r--security/smack/smack_access.c4
-rw-r--r--security/smack/smack_lsm.c176
-rw-r--r--security/smack/smackfs.c26
-rw-r--r--sound/ac97_bus.c1
-rw-r--r--sound/aoa/codecs/Makefile4
-rw-r--r--sound/aoa/codecs/onyx.c1118
-rw-r--r--sound/aoa/codecs/onyx.h (renamed from sound/aoa/codecs/snd-aoa-codec-onyx.h)0
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-onyx.c1118
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-tas.c1012
-rw-r--r--sound/aoa/codecs/snd-aoa-codec-toonie.c150
-rw-r--r--sound/aoa/codecs/tas-basstreble.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h)0
-rw-r--r--sound/aoa/codecs/tas-gain-table.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h)0
-rw-r--r--sound/aoa/codecs/tas.c1012
-rw-r--r--sound/aoa/codecs/tas.h (renamed from sound/aoa/codecs/snd-aoa-codec-tas.h)0
-rw-r--r--sound/aoa/codecs/toonie.c150
-rw-r--r--sound/aoa/core/Makefile8
-rw-r--r--sound/aoa/core/alsa.c99
-rw-r--r--sound/aoa/core/alsa.h (renamed from sound/aoa/core/snd-aoa-alsa.h)0
-rw-r--r--sound/aoa/core/core.c162
-rw-r--r--sound/aoa/core/gpio-feature.c408
-rw-r--r--sound/aoa/core/gpio-pmf.c (renamed from sound/aoa/core/snd-aoa-gpio-pmf.c)0
-rw-r--r--sound/aoa/core/snd-aoa-alsa.c99
-rw-r--r--sound/aoa/core/snd-aoa-core.c162
-rw-r--r--sound/aoa/core/snd-aoa-gpio-feature.c408
-rw-r--r--sound/aoa/fabrics/Makefile2
-rw-r--r--sound/aoa/fabrics/layout.c1120
-rw-r--r--sound/aoa/fabrics/snd-aoa-fabric-layout.c1120
-rw-r--r--sound/aoa/soundbus/i2sbus/Makefile2
-rw-r--r--sound/aoa/soundbus/i2sbus/control.c (renamed from sound/aoa/soundbus/i2sbus/i2sbus-control.c)0
-rw-r--r--sound/aoa/soundbus/i2sbus/core.c450
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus-core.c450
-rw-r--r--sound/aoa/soundbus/i2sbus/i2sbus.h2
-rw-r--r--sound/aoa/soundbus/i2sbus/interface.h (renamed from sound/aoa/soundbus/i2sbus/i2sbus-interface.h)0
-rw-r--r--sound/aoa/soundbus/i2sbus/pcm.c (renamed from sound/aoa/soundbus/i2sbus/i2sbus-pcm.c)0
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c2
-rw-r--r--sound/arm/pxa2xx-ac97.c1
-rw-r--r--sound/arm/pxa2xx-pcm.h2
-rw-r--r--sound/core/Kconfig21
-rw-r--r--sound/core/Makefile2
-rw-r--r--sound/core/device.c4
-rw-r--r--sound/core/hrtimer.c154
-rw-r--r--sound/core/info.c17
-rw-r--r--sound/core/init.c71
-rw-r--r--sound/core/jack.c15
-rw-r--r--sound/core/rawmidi.c4
-rw-r--r--sound/core/rtctimer.c2
-rw-r--r--sound/core/seq/seq.c4
-rw-r--r--sound/core/timer.c2
-rw-r--r--sound/drivers/Kconfig2
-rw-r--r--sound/drivers/pcsp/pcsp.c7
-rw-r--r--sound/drivers/pcsp/pcsp.h3
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c168
-rw-r--r--sound/drivers/vx/vx_core.c2
-rw-r--r--sound/drivers/vx/vx_pcm.c2
-rw-r--r--sound/i2c/other/tea575x-tuner.c22
-rw-r--r--sound/isa/sb/sb8.c4
-rw-r--r--sound/oss/waveartist.c8
-rw-r--r--sound/pci/Kconfig127
-rw-r--r--sound/pci/ac97/ac97_codec.c2
-rw-r--r--sound/pci/ac97/ac97_patch.c7
-rw-r--r--sound/pci/ca0106/ca0106.h30
-rw-r--r--sound/pci/ca0106/ca0106_main.c550
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c263
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c5
-rw-r--r--sound/pci/cs5535audio/Makefile3
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c12
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h39
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c179
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c15
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c494
-rw-r--r--sound/pci/emu10k1/emumixer.c46
-rw-r--r--sound/pci/es1968.c2
-rw-r--r--sound/pci/hda/Kconfig188
-rw-r--r--sound/pci/hda/Makefile75
-rw-r--r--sound/pci/hda/hda_beep.c2
-rw-r--r--sound/pci/hda/hda_codec.c781
-rw-r--r--sound/pci/hda/hda_codec.h107
-rw-r--r--sound/pci/hda/hda_eld.c590
-rw-r--r--sound/pci/hda/hda_generic.c21
-rw-r--r--sound/pci/hda/hda_hwdep.c234
-rw-r--r--sound/pci/hda/hda_intel.c348
-rw-r--r--sound/pci/hda/hda_local.h100
-rw-r--r--sound/pci/hda/hda_patch.h22
-rw-r--r--sound/pci/hda/hda_proc.c91
-rw-r--r--sound/pci/hda/patch_analog.c132
-rw-r--r--sound/pci/hda/patch_atihdmi.c40
-rw-r--r--sound/pci/hda/patch_cmedia.c27
-rw-r--r--sound/pci/hda/patch_conexant.c39
-rw-r--r--sound/pci/hda/patch_intelhdmi.c711
-rw-r--r--sound/pci/hda/patch_nvhdmi.c32
-rw-r--r--sound/pci/hda/patch_realtek.c1663
-rw-r--r--sound/pci/hda/patch_si3054.c35
-rw-r--r--sound/pci/hda/patch_sigmatel.c1331
-rw-r--r--sound/pci/hda/patch_via.c171
-rw-r--r--sound/pci/ice1712/ice1724.c23
-rw-r--r--sound/pci/maestro3.c2
-rw-r--r--sound/pci/mixart/mixart.c4
-rw-r--r--sound/pci/mixart/mixart_core.c2
-rw-r--r--sound/pci/oxygen/oxygen.c4
-rw-r--r--sound/pci/pcxhr/Makefile2
-rw-r--r--sound/pci/pcxhr/pcxhr.c558
-rw-r--r--sound/pci/pcxhr/pcxhr.h76
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c293
-rw-r--r--sound/pci/pcxhr/pcxhr_core.h5
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c158
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.c820
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.h56
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c556
-rw-r--r--sound/pci/riptide/riptide.c4
-rw-r--r--sound/pci/rme9652/hdsp.c4
-rw-r--r--sound/pci/rme9652/hdspm.c4
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c2
-rw-r--r--sound/ppc/pmac.c2
-rw-r--r--sound/ppc/tumbler.c3
-rw-r--r--sound/soc/Kconfig13
-rw-r--r--sound/soc/Makefile12
-rw-r--r--sound/soc/at32/Kconfig34
-rw-r--r--sound/soc/at32/Makefile11
-rw-r--r--sound/soc/at32/at32-pcm.c492
-rw-r--r--sound/soc/at32/at32-pcm.h79
-rw-r--r--sound/soc/at32/at32-ssc.c849
-rw-r--r--sound/soc/at32/at32-ssc.h59
-rw-r--r--sound/soc/at32/playpaq_wm8510.c513
-rw-r--r--sound/soc/at91/Kconfig10
-rw-r--r--sound/soc/at91/Makefile6
-rw-r--r--sound/soc/at91/at91-pcm.c434
-rw-r--r--sound/soc/at91/at91-pcm.h72
-rw-r--r--sound/soc/at91/at91-ssc.c791
-rw-r--r--sound/soc/at91/at91-ssc.h27
-rw-r--r--sound/soc/atmel/Kconfig43
-rw-r--r--sound/soc/atmel/Makefile15
-rw-r--r--sound/soc/atmel/atmel-pcm.c494
-rw-r--r--sound/soc/atmel/atmel-pcm.h86
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c790
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h121
-rw-r--r--sound/soc/atmel/playpaq_wm8510.c512
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c328
-rw-r--r--sound/soc/au1x/dbdma2.c3
-rw-r--r--sound/soc/au1x/psc-ac97.c16
-rw-r--r--sound/soc/au1x/psc-i2s.c18
-rw-r--r--sound/soc/au1x/sample-ac97.c4
-rw-r--r--sound/soc/blackfin/Kconfig22
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c113
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c178
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h35
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c8
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c10
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c12
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c31
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h2
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c14
-rw-r--r--sound/soc/codecs/Kconfig69
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/ac97.c7
-rw-r--r--sound/soc/codecs/ad1980.c24
-rw-r--r--sound/soc/codecs/ad73311.c18
-rw-r--r--sound/soc/codecs/ak4535.c19
-rw-r--r--sound/soc/codecs/cs4270.c38
-rw-r--r--sound/soc/codecs/l3.c91
-rw-r--r--sound/soc/codecs/pcm3008.c212
-rw-r--r--sound/soc/codecs/pcm3008.h25
-rw-r--r--sound/soc/codecs/ssm2602.c57
-rw-r--r--sound/soc/codecs/tlv320aic23.c262
-rw-r--r--sound/soc/codecs/tlv320aic26.c22
-rw-r--r--sound/soc/codecs/tlv320aic3x.c166
-rw-r--r--sound/soc/codecs/tlv320aic3x.h60
-rw-r--r--sound/soc/codecs/twl4030.c1317
-rw-r--r--sound/soc/codecs/twl4030.h219
-rw-r--r--sound/soc/codecs/uda134x.c668
-rw-r--r--sound/soc/codecs/uda134x.h36
-rw-r--r--sound/soc/codecs/uda1380.c29
-rw-r--r--sound/soc/codecs/wm8350.c1583
-rw-r--r--sound/soc/codecs/wm8350.h20
-rw-r--r--sound/soc/codecs/wm8510.c19
-rw-r--r--sound/soc/codecs/wm8580.c134
-rw-r--r--sound/soc/codecs/wm8580.h1
-rw-r--r--sound/soc/codecs/wm8728.c585
-rw-r--r--sound/soc/codecs/wm8728.h30
-rw-r--r--sound/soc/codecs/wm8731.c25
-rw-r--r--sound/soc/codecs/wm8750.c19
-rw-r--r--sound/soc/codecs/wm8753.c39
-rw-r--r--sound/soc/codecs/wm8900.c262
-rw-r--r--sound/soc/codecs/wm8900.h6
-rw-r--r--sound/soc/codecs/wm8903.c268
-rw-r--r--sound/soc/codecs/wm8903.h5
-rw-r--r--sound/soc/codecs/wm8971.c19
-rw-r--r--sound/soc/codecs/wm8990.c43
-rw-r--r--sound/soc/codecs/wm8990.h4
-rw-r--r--sound/soc/codecs/wm9712.c12
-rw-r--r--sound/soc/codecs/wm9713.c46
-rw-r--r--sound/soc/davinci/Kconfig10
-rw-r--r--sound/soc/davinci/Makefile2
-rw-r--r--sound/soc/davinci/davinci-evm.c14
-rw-r--r--sound/soc/davinci/davinci-i2s.c257
-rw-r--r--sound/soc/davinci/davinci-pcm.c30
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c157
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl_dma.c14
-rw-r--r--sound/soc/fsl/fsl_ssi.c24
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c22
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c8
-rw-r--r--sound/soc/fsl/soc-of-simple.c12
-rw-r--r--sound/soc/omap/Kconfig35
-rw-r--r--sound/soc/omap/Makefile8
-rw-r--r--sound/soc/omap/n810.c10
-rw-r--r--sound/soc/omap/omap-mcbsp.c61
-rw-r--r--sound/soc/omap/omap-pcm.c16
-rw-r--r--sound/soc/omap/omap2evm.c151
-rw-r--r--sound/soc/omap/omap3beagle.c149
-rw-r--r--sound/soc/omap/omap3pandora.c311
-rw-r--r--sound/soc/omap/osk5912.c10
-rw-r--r--sound/soc/omap/overo.c148
-rw-r--r--sound/soc/omap/sdp3430.c152
-rw-r--r--sound/soc/pxa/Kconfig22
-rw-r--r--sound/soc/pxa/Makefile6
-rw-r--r--sound/soc/pxa/corgi.c12
-rw-r--r--sound/soc/pxa/e800_wm9712.c8
-rw-r--r--sound/soc/pxa/em-x270.c7
-rw-r--r--sound/soc/pxa/palm27x.c269
-rw-r--r--sound/soc/pxa/poodle.c6
-rw-r--r--sound/soc/pxa/pxa-ssp.c931
-rw-r--r--sound/soc/pxa/pxa-ssp.h47
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c34
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c35
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c14
-rw-r--r--sound/soc/pxa/spitz.c6
-rw-r--r--sound/soc/pxa/tosa.c38
-rw-r--r--sound/soc/pxa/zylonite.c219
-rw-r--r--sound/soc/s3c24xx/Kconfig5
-rw-r--r--sound/soc/s3c24xx/Makefile2
-rw-r--r--sound/soc/s3c24xx/ln2440sbc_alc650.c8
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c9
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c38
-rw-r--r--sound/soc/s3c24xx/s3c2443-ac97.c32
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c35
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c12
-rw-r--r--sound/soc/s3c24xx/s3c24xx_uda134x.c373
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c8
-rw-r--r--sound/soc/sh/dma-sh7760.c12
-rw-r--r--sound/soc/sh/hac.c19
-rw-r--r--sound/soc/sh/sh7760-ac97.c6
-rw-r--r--sound/soc/sh/ssi.c30
-rw-r--r--sound/soc/soc-core.c840
-rw-r--r--sound/soc/soc-dapm.c82
-rw-r--r--sound/sound_core.c1
-rw-r--r--sound/usb/caiaq/caiaq-control.c73
-rw-r--r--sound/usb/caiaq/caiaq-device.c2
-rw-r--r--sound/usb/usbmidi.c2
-rw-r--r--sound/usb/usx2y/usb_stream.c3
-rw-r--r--usr/gen_init_cpio.c28
4887 files changed, 350582 insertions, 200255 deletions
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 9b1f6ca100d1..0a08126d3094 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -6,7 +6,7 @@
# To add a new book the only step required is to add the book to the
# list of DOCBOOKS.
-DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml \
+DOCBOOKS := z8530book.xml mcabook.xml \
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
procfs-guide.xml writing_usb_driver.xml networking.xml \
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
diff --git a/Documentation/DocBook/networking.tmpl b/Documentation/DocBook/networking.tmpl
index f24f9e85e4ae..627707a3cb9d 100644
--- a/Documentation/DocBook/networking.tmpl
+++ b/Documentation/DocBook/networking.tmpl
@@ -98,9 +98,6 @@
X!Enet/core/wireless.c
</sect1>
-->
- <sect1><title>Synchronous PPP</title>
-!Edrivers/net/wan/syncppp.c
- </sect1>
</chapter>
</book>
diff --git a/Documentation/DocBook/wanbook.tmpl b/Documentation/DocBook/wanbook.tmpl
deleted file mode 100644
index 8c93db122f04..000000000000
--- a/Documentation/DocBook/wanbook.tmpl
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-
-<book id="WANGuide">
- <bookinfo>
- <title>Synchronous PPP and Cisco HDLC Programming Guide</title>
-
- <authorgroup>
- <author>
- <firstname>Alan</firstname>
- <surname>Cox</surname>
- <affiliation>
- <address>
- <email>alan@lxorguk.ukuu.org.uk</email>
- </address>
- </affiliation>
- </author>
- </authorgroup>
-
- <copyright>
- <year>2000</year>
- <holder>Alan Cox</holder>
- </copyright>
-
- <legalnotice>
- <para>
- This documentation is free software; you can redistribute
- 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.
- </para>
-
- <para>
- This program is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- </para>
-
- <para>
- You should have received a copy of the GNU General Public
- License along with this program; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- </para>
-
- <para>
- For more details see the file COPYING in the source
- distribution of Linux.
- </para>
- </legalnotice>
- </bookinfo>
-
-<toc></toc>
-
- <chapter id="intro">
- <title>Introduction</title>
- <para>
- The syncppp drivers in Linux provide a fairly complete
- implementation of Cisco HDLC and a minimal implementation of
- PPP. The longer term goal is to switch the PPP layer to the
- generic PPP interface that is new in Linux 2.3.x. The API should
- remain unchanged when this is done, but support will then be
- available for IPX, compression and other PPP features
- </para>
- </chapter>
- <chapter id="bugs">
- <title>Known Bugs And Assumptions</title>
- <para>
- <variablelist>
- <varlistentry><term>PPP is minimal</term>
- <listitem>
- <para>
- The current PPP implementation is very basic, although sufficient
- for most wan usages.
- </para>
- </listitem></varlistentry>
-
- <varlistentry><term>Cisco HDLC Quirks</term>
- <listitem>
- <para>
- Currently we do not end all packets with the correct Cisco multicast
- or unicast flags. Nothing appears to mind too much but this should
- be corrected.
- </para>
- </listitem></varlistentry>
- </variablelist>
-
- </para>
- </chapter>
-
- <chapter id="pubfunctions">
- <title>Public Functions Provided</title>
-!Edrivers/net/wan/syncppp.c
- </chapter>
-
-</book>
diff --git a/Documentation/RCU/00-INDEX b/Documentation/RCU/00-INDEX
index 461481dfb7c3..7dc0695a8f90 100644
--- a/Documentation/RCU/00-INDEX
+++ b/Documentation/RCU/00-INDEX
@@ -16,6 +16,8 @@ RTFP.txt
- List of RCU papers (bibliography) going back to 1980.
torture.txt
- RCU Torture Test Operation (CONFIG_RCU_TORTURE_TEST)
+trace.txt
+ - CONFIG_RCU_TRACE debugfs files and formats
UP.txt
- RCU on Uniprocessor Systems
whatisRCU.txt
diff --git a/Documentation/RCU/rculist_nulls.txt b/Documentation/RCU/rculist_nulls.txt
new file mode 100644
index 000000000000..239f542d48ba
--- /dev/null
+++ b/Documentation/RCU/rculist_nulls.txt
@@ -0,0 +1,167 @@
+Using hlist_nulls to protect read-mostly linked lists and
+objects using SLAB_DESTROY_BY_RCU allocations.
+
+Please read the basics in Documentation/RCU/listRCU.txt
+
+Using special makers (called 'nulls') is a convenient way
+to solve following problem :
+
+A typical RCU linked list managing objects which are
+allocated with SLAB_DESTROY_BY_RCU kmem_cache can
+use following algos :
+
+1) Lookup algo
+--------------
+rcu_read_lock()
+begin:
+obj = lockless_lookup(key);
+if (obj) {
+ if (!try_get_ref(obj)) // might fail for free objects
+ goto begin;
+ /*
+ * Because a writer could delete object, and a writer could
+ * reuse these object before the RCU grace period, we
+ * must check key after geting the reference on object
+ */
+ if (obj->key != key) { // not the object we expected
+ put_ref(obj);
+ goto begin;
+ }
+}
+rcu_read_unlock();
+
+Beware that lockless_lookup(key) cannot use traditional hlist_for_each_entry_rcu()
+but a version with an additional memory barrier (smp_rmb())
+
+lockless_lookup(key)
+{
+ struct hlist_node *node, *next;
+ for (pos = rcu_dereference((head)->first);
+ pos && ({ next = pos->next; smp_rmb(); prefetch(next); 1; }) &&
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; });
+ pos = rcu_dereference(next))
+ if (obj->key == key)
+ return obj;
+ return NULL;
+
+And note the traditional hlist_for_each_entry_rcu() misses this smp_rmb() :
+
+ struct hlist_node *node;
+ for (pos = rcu_dereference((head)->first);
+ pos && ({ prefetch(pos->next); 1; }) &&
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; });
+ pos = rcu_dereference(pos->next))
+ if (obj->key == key)
+ return obj;
+ return NULL;
+}
+
+Quoting Corey Minyard :
+
+"If the object is moved from one list to another list in-between the
+ time the hash is calculated and the next field is accessed, and the
+ object has moved to the end of a new list, the traversal will not
+ complete properly on the list it should have, since the object will
+ be on the end of the new list and there's not a way to tell it's on a
+ new list and restart the list traversal. I think that this can be
+ solved by pre-fetching the "next" field (with proper barriers) before
+ checking the key."
+
+2) Insert algo :
+----------------
+
+We need to make sure a reader cannot read the new 'obj->obj_next' value
+and previous value of 'obj->key'. Or else, an item could be deleted
+from a chain, and inserted into another chain. If new chain was empty
+before the move, 'next' pointer is NULL, and lockless reader can
+not detect it missed following items in original chain.
+
+/*
+ * Please note that new inserts are done at the head of list,
+ * not in the middle or end.
+ */
+obj = kmem_cache_alloc(...);
+lock_chain(); // typically a spin_lock()
+obj->key = key;
+atomic_inc(&obj->refcnt);
+/*
+ * we need to make sure obj->key is updated before obj->next
+ */
+smp_wmb();
+hlist_add_head_rcu(&obj->obj_node, list);
+unlock_chain(); // typically a spin_unlock()
+
+
+3) Remove algo
+--------------
+Nothing special here, we can use a standard RCU hlist deletion.
+But thanks to SLAB_DESTROY_BY_RCU, beware a deleted object can be reused
+very very fast (before the end of RCU grace period)
+
+if (put_last_reference_on(obj) {
+ lock_chain(); // typically a spin_lock()
+ hlist_del_init_rcu(&obj->obj_node);
+ unlock_chain(); // typically a spin_unlock()
+ kmem_cache_free(cachep, obj);
+}
+
+
+
+--------------------------------------------------------------------------
+With hlist_nulls we can avoid extra smp_rmb() in lockless_lookup()
+and extra smp_wmb() in insert function.
+
+For example, if we choose to store the slot number as the 'nulls'
+end-of-list marker for each slot of the hash table, we can detect
+a race (some writer did a delete and/or a move of an object
+to another chain) checking the final 'nulls' value if
+the lookup met the end of chain. If final 'nulls' value
+is not the slot number, then we must restart the lookup at
+the begining. If the object was moved to same chain,
+then the reader doesnt care : It might eventually
+scan the list again without harm.
+
+
+1) lookup algo
+
+ head = &table[slot];
+ rcu_read_lock();
+begin:
+ hlist_nulls_for_each_entry_rcu(obj, node, head, member) {
+ if (obj->key == key) {
+ if (!try_get_ref(obj)) // might fail for free objects
+ goto begin;
+ if (obj->key != key) { // not the object we expected
+ put_ref(obj);
+ goto begin;
+ }
+ goto out;
+ }
+/*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != slot)
+ goto begin;
+ obj = NULL;
+
+out:
+ rcu_read_unlock();
+
+2) Insert function :
+--------------------
+
+/*
+ * Please note that new inserts are done at the head of list,
+ * not in the middle or end.
+ */
+obj = kmem_cache_alloc(cachep);
+lock_chain(); // typically a spin_lock()
+obj->key = key;
+atomic_set(&obj->refcnt, 1);
+/*
+ * insert obj in RCU way (readers might be traversing chain)
+ */
+hlist_nulls_add_head_rcu(&obj->obj_node, list);
+unlock_chain(); // typically a spin_unlock()
diff --git a/Documentation/RCU/trace.txt b/Documentation/RCU/trace.txt
new file mode 100644
index 000000000000..068848240a8b
--- /dev/null
+++ b/Documentation/RCU/trace.txt
@@ -0,0 +1,413 @@
+CONFIG_RCU_TRACE debugfs Files and Formats
+
+
+The rcupreempt and rcutree implementations of RCU provide debugfs trace
+output that summarizes counters and state. This information is useful for
+debugging RCU itself, and can sometimes also help to debug abuses of RCU.
+Note that the rcuclassic implementation of RCU does not provide debugfs
+trace output.
+
+The following sections describe the debugfs files and formats for
+preemptable RCU (rcupreempt) and hierarchical RCU (rcutree).
+
+
+Preemptable RCU debugfs Files and Formats
+
+This implementation of RCU provides three debugfs files under the
+top-level directory RCU: rcu/rcuctrs (which displays the per-CPU
+counters used by preemptable RCU) rcu/rcugp (which displays grace-period
+counters), and rcu/rcustats (which internal counters for debugging RCU).
+
+The output of "cat rcu/rcuctrs" looks as follows:
+
+CPU last cur F M
+ 0 5 -5 0 0
+ 1 -1 0 0 0
+ 2 0 1 0 0
+ 3 0 1 0 0
+ 4 0 1 0 0
+ 5 0 1 0 0
+ 6 0 2 0 0
+ 7 0 -1 0 0
+ 8 0 1 0 0
+ggp = 26226, state = waitzero
+
+The per-CPU fields are as follows:
+
+o "CPU" gives the CPU number. Offline CPUs are not displayed.
+
+o "last" gives the value of the counter that is being decremented
+ for the current grace period phase. In the example above,
+ the counters sum to 4, indicating that there are still four
+ RCU read-side critical sections still running that started
+ before the last counter flip.
+
+o "cur" gives the value of the counter that is currently being
+ both incremented (by rcu_read_lock()) and decremented (by
+ rcu_read_unlock()). In the example above, the counters sum to
+ 1, indicating that there is only one RCU read-side critical section
+ still running that started after the last counter flip.
+
+o "F" indicates whether RCU is waiting for this CPU to acknowledge
+ a counter flip. In the above example, RCU is not waiting on any,
+ which is consistent with the state being "waitzero" rather than
+ "waitack".
+
+o "M" indicates whether RCU is waiting for this CPU to execute a
+ memory barrier. In the above example, RCU is not waiting on any,
+ which is consistent with the state being "waitzero" rather than
+ "waitmb".
+
+o "ggp" is the global grace-period counter.
+
+o "state" is the RCU state, which can be one of the following:
+
+ o "idle": there is no grace period in progress.
+
+ o "waitack": RCU just incremented the global grace-period
+ counter, which has the effect of reversing the roles of
+ the "last" and "cur" counters above, and is waiting for
+ all the CPUs to acknowledge the flip. Once the flip has
+ been acknowledged, CPUs will no longer be incrementing
+ what are now the "last" counters, so that their sum will
+ decrease monotonically down to zero.
+
+ o "waitzero": RCU is waiting for the sum of the "last" counters
+ to decrease to zero.
+
+ o "waitmb": RCU is waiting for each CPU to execute a memory
+ barrier, which ensures that instructions from a given CPU's
+ last RCU read-side critical section cannot be reordered
+ with instructions following the memory-barrier instruction.
+
+The output of "cat rcu/rcugp" looks as follows:
+
+oldggp=48870 newggp=48873
+
+Note that reading from this file provokes a synchronize_rcu(). The
+"oldggp" value is that of "ggp" from rcu/rcuctrs above, taken before
+executing the synchronize_rcu(), and the "newggp" value is also the
+"ggp" value, but taken after the synchronize_rcu() command returns.
+
+
+The output of "cat rcu/rcugp" looks as follows:
+
+na=1337955 nl=40 wa=1337915 wl=44 da=1337871 dl=0 dr=1337871 di=1337871
+1=50989 e1=6138 i1=49722 ie1=82 g1=49640 a1=315203 ae1=265563 a2=49640
+z1=1401244 ze1=1351605 z2=49639 m1=5661253 me1=5611614 m2=49639
+
+These are counters tracking internal preemptable-RCU events, however,
+some of them may be useful for debugging algorithms using RCU. In
+particular, the "nl", "wl", and "dl" values track the number of RCU
+callbacks in various states. The fields are as follows:
+
+o "na" is the total number of RCU callbacks that have been enqueued
+ since boot.
+
+o "nl" is the number of RCU callbacks waiting for the previous
+ grace period to end so that they can start waiting on the next
+ grace period.
+
+o "wa" is the total number of RCU callbacks that have started waiting
+ for a grace period since boot. "na" should be roughly equal to
+ "nl" plus "wa".
+
+o "wl" is the number of RCU callbacks currently waiting for their
+ grace period to end.
+
+o "da" is the total number of RCU callbacks whose grace periods
+ have completed since boot. "wa" should be roughly equal to
+ "wl" plus "da".
+
+o "dr" is the total number of RCU callbacks that have been removed
+ from the list of callbacks ready to invoke. "dr" should be roughly
+ equal to "da".
+
+o "di" is the total number of RCU callbacks that have been invoked
+ since boot. "di" should be roughly equal to "da", though some
+ early versions of preemptable RCU had a bug so that only the
+ last CPU's count of invocations was displayed, rather than the
+ sum of all CPU's counts.
+
+o "1" is the number of calls to rcu_try_flip(). This should be
+ roughly equal to the sum of "e1", "i1", "a1", "z1", and "m1"
+ described below. In other words, the number of times that
+ the state machine is visited should be equal to the sum of the
+ number of times that each state is visited plus the number of
+ times that the state-machine lock acquisition failed.
+
+o "e1" is the number of times that rcu_try_flip() was unable to
+ acquire the fliplock.
+
+o "i1" is the number of calls to rcu_try_flip_idle().
+
+o "ie1" is the number of times rcu_try_flip_idle() exited early
+ due to the calling CPU having no work for RCU.
+
+o "g1" is the number of times that rcu_try_flip_idle() decided
+ to start a new grace period. "i1" should be roughly equal to
+ "ie1" plus "g1".
+
+o "a1" is the number of calls to rcu_try_flip_waitack().
+
+o "ae1" is the number of times that rcu_try_flip_waitack() found
+ that at least one CPU had not yet acknowledge the new grace period
+ (AKA "counter flip").
+
+o "a2" is the number of time rcu_try_flip_waitack() found that
+ all CPUs had acknowledged. "a1" should be roughly equal to
+ "ae1" plus "a2". (This particular output was collected on
+ a 128-CPU machine, hence the smaller-than-usual fraction of
+ calls to rcu_try_flip_waitack() finding all CPUs having already
+ acknowledged.)
+
+o "z1" is the number of calls to rcu_try_flip_waitzero().
+
+o "ze1" is the number of times that rcu_try_flip_waitzero() found
+ that not all of the old RCU read-side critical sections had
+ completed.
+
+o "z2" is the number of times that rcu_try_flip_waitzero() finds
+ the sum of the counters equal to zero, in other words, that
+ all of the old RCU read-side critical sections had completed.
+ The value of "z1" should be roughly equal to "ze1" plus
+ "z2".
+
+o "m1" is the number of calls to rcu_try_flip_waitmb().
+
+o "me1" is the number of times that rcu_try_flip_waitmb() finds
+ that at least one CPU has not yet executed a memory barrier.
+
+o "m2" is the number of times that rcu_try_flip_waitmb() finds that
+ all CPUs have executed a memory barrier.
+
+
+Hierarchical RCU debugfs Files and Formats
+
+This implementation of RCU provides three debugfs files under the
+top-level directory RCU: rcu/rcudata (which displays fields in struct
+rcu_data), rcu/rcugp (which displays grace-period counters), and
+rcu/rcuhier (which displays the struct rcu_node hierarchy).
+
+The output of "cat rcu/rcudata" looks as follows:
+
+rcu:
+ 0 c=4011 g=4012 pq=1 pqc=4011 qp=0 rpfq=1 rp=3c2a dt=23301/73 dn=2 df=1882 of=0 ri=2126 ql=2 b=10
+ 1 c=4011 g=4012 pq=1 pqc=4011 qp=0 rpfq=3 rp=39a6 dt=78073/1 dn=2 df=1402 of=0 ri=1875 ql=46 b=10
+ 2 c=4010 g=4010 pq=1 pqc=4010 qp=0 rpfq=-5 rp=1d12 dt=16646/0 dn=2 df=3140 of=0 ri=2080 ql=0 b=10
+ 3 c=4012 g=4013 pq=1 pqc=4012 qp=1 rpfq=3 rp=2b50 dt=21159/1 dn=2 df=2230 of=0 ri=1923 ql=72 b=10
+ 4 c=4012 g=4013 pq=1 pqc=4012 qp=1 rpfq=3 rp=1644 dt=5783/1 dn=2 df=3348 of=0 ri=2805 ql=7 b=10
+ 5 c=4012 g=4013 pq=0 pqc=4011 qp=1 rpfq=3 rp=1aac dt=5879/1 dn=2 df=3140 of=0 ri=2066 ql=10 b=10
+ 6 c=4012 g=4013 pq=1 pqc=4012 qp=1 rpfq=3 rp=ed8 dt=5847/1 dn=2 df=3797 of=0 ri=1266 ql=10 b=10
+ 7 c=4012 g=4013 pq=1 pqc=4012 qp=1 rpfq=3 rp=1fa2 dt=6199/1 dn=2 df=2795 of=0 ri=2162 ql=28 b=10
+rcu_bh:
+ 0 c=-268 g=-268 pq=1 pqc=-268 qp=0 rpfq=-145 rp=21d6 dt=23301/73 dn=2 df=0 of=0 ri=0 ql=0 b=10
+ 1 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-170 rp=20ce dt=78073/1 dn=2 df=26 of=0 ri=5 ql=0 b=10
+ 2 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-83 rp=fbd dt=16646/0 dn=2 df=28 of=0 ri=4 ql=0 b=10
+ 3 c=-268 g=-268 pq=1 pqc=-268 qp=0 rpfq=-105 rp=178c dt=21159/1 dn=2 df=28 of=0 ri=2 ql=0 b=10
+ 4 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-30 rp=b54 dt=5783/1 dn=2 df=32 of=0 ri=0 ql=0 b=10
+ 5 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-29 rp=df5 dt=5879/1 dn=2 df=30 of=0 ri=3 ql=0 b=10
+ 6 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-28 rp=788 dt=5847/1 dn=2 df=32 of=0 ri=0 ql=0 b=10
+ 7 c=-268 g=-268 pq=1 pqc=-268 qp=1 rpfq=-53 rp=1098 dt=6199/1 dn=2 df=30 of=0 ri=3 ql=0 b=10
+
+The first section lists the rcu_data structures for rcu, the second for
+rcu_bh. Each section has one line per CPU, or eight for this 8-CPU system.
+The fields are as follows:
+
+o The number at the beginning of each line is the CPU number.
+ CPUs numbers followed by an exclamation mark are offline,
+ but have been online at least once since boot. There will be
+ no output for CPUs that have never been online, which can be
+ a good thing in the surprisingly common case where NR_CPUS is
+ substantially larger than the number of actual CPUs.
+
+o "c" is the count of grace periods that this CPU believes have
+ completed. CPUs in dynticks idle mode may lag quite a ways
+ behind, for example, CPU 4 under "rcu" above, which has slept
+ through the past 25 RCU grace periods. It is not unusual to
+ see CPUs lagging by thousands of grace periods.
+
+o "g" is the count of grace periods that this CPU believes have
+ started. Again, CPUs in dynticks idle mode may lag behind.
+ If the "c" and "g" values are equal, this CPU has already
+ reported a quiescent state for the last RCU grace period that
+ it is aware of, otherwise, the CPU believes that it owes RCU a
+ quiescent state.
+
+o "pq" indicates that this CPU has passed through a quiescent state
+ for the current grace period. It is possible for "pq" to be
+ "1" and "c" different than "g", which indicates that although
+ the CPU has passed through a quiescent state, either (1) this
+ CPU has not yet reported that fact, (2) some other CPU has not
+ yet reported for this grace period, or (3) both.
+
+o "pqc" indicates which grace period the last-observed quiescent
+ state for this CPU corresponds to. This is important for handling
+ the race between CPU 0 reporting an extended dynticks-idle
+ quiescent state for CPU 1 and CPU 1 suddenly waking up and
+ reporting its own quiescent state. If CPU 1 was the last CPU
+ for the current grace period, then the CPU that loses this race
+ will attempt to incorrectly mark CPU 1 as having checked in for
+ the next grace period!
+
+o "qp" indicates that RCU still expects a quiescent state from
+ this CPU.
+
+o "rpfq" is the number of rcu_pending() calls on this CPU required
+ to induce this CPU to invoke force_quiescent_state().
+
+o "rp" is low-order four hex digits of the count of how many times
+ rcu_pending() has been invoked on this CPU.
+
+o "dt" is the current value of the dyntick counter that is incremented
+ when entering or leaving dynticks idle state, either by the
+ scheduler or by irq. The number after the "/" is the interrupt
+ nesting depth when in dyntick-idle state, or one greater than
+ the interrupt-nesting depth otherwise.
+
+ This field is displayed only for CONFIG_NO_HZ kernels.
+
+o "dn" is the current value of the dyntick counter that is incremented
+ when entering or leaving dynticks idle state via NMI. If both
+ the "dt" and "dn" values are even, then this CPU is in dynticks
+ idle mode and may be ignored by RCU. If either of these two
+ counters is odd, then RCU must be alert to the possibility of
+ an RCU read-side critical section running on this CPU.
+
+ This field is displayed only for CONFIG_NO_HZ kernels.
+
+o "df" is the number of times that some other CPU has forced a
+ quiescent state on behalf of this CPU due to this CPU being in
+ dynticks-idle state.
+
+ This field is displayed only for CONFIG_NO_HZ kernels.
+
+o "of" is the number of times that some other CPU has forced a
+ quiescent state on behalf of this CPU due to this CPU being
+ offline. In a perfect world, this might neve happen, but it
+ turns out that offlining and onlining a CPU can take several grace
+ periods, and so there is likely to be an extended period of time
+ when RCU believes that the CPU is online when it really is not.
+ Please note that erring in the other direction (RCU believing a
+ CPU is offline when it is really alive and kicking) is a fatal
+ error, so it makes sense to err conservatively.
+
+o "ri" is the number of times that RCU has seen fit to send a
+ reschedule IPI to this CPU in order to get it to report a
+ quiescent state.
+
+o "ql" is the number of RCU callbacks currently residing on
+ this CPU. This is the total number of callbacks, regardless
+ of what state they are in (new, waiting for grace period to
+ start, waiting for grace period to end, ready to invoke).
+
+o "b" is the batch limit for this CPU. If more than this number
+ of RCU callbacks is ready to invoke, then the remainder will
+ be deferred.
+
+
+The output of "cat rcu/rcugp" looks as follows:
+
+rcu: completed=33062 gpnum=33063
+rcu_bh: completed=464 gpnum=464
+
+Again, this output is for both "rcu" and "rcu_bh". The fields are
+taken from the rcu_state structure, and are as follows:
+
+o "completed" is the number of grace periods that have completed.
+ It is comparable to the "c" field from rcu/rcudata in that a
+ CPU whose "c" field matches the value of "completed" is aware
+ that the corresponding RCU grace period has completed.
+
+o "gpnum" is the number of grace periods that have started. It is
+ comparable to the "g" field from rcu/rcudata in that a CPU
+ whose "g" field matches the value of "gpnum" is aware that the
+ corresponding RCU grace period has started.
+
+ If these two fields are equal (as they are for "rcu_bh" above),
+ then there is no grace period in progress, in other words, RCU
+ is idle. On the other hand, if the two fields differ (as they
+ do for "rcu" above), then an RCU grace period is in progress.
+
+
+The output of "cat rcu/rcuhier" looks as follows, with very long lines:
+
+c=6902 g=6903 s=2 jfq=3 j=72c7 nfqs=13142/nfqsng=0(13142) fqlh=6
+1/1 0:127 ^0
+3/3 0:35 ^0 0/0 36:71 ^1 0/0 72:107 ^2 0/0 108:127 ^3
+3/3f 0:5 ^0 2/3 6:11 ^1 0/0 12:17 ^2 0/0 18:23 ^3 0/0 24:29 ^4 0/0 30:35 ^5 0/0 36:41 ^0 0/0 42:47 ^1 0/0 48:53 ^2 0/0 54:59 ^3 0/0 60:65 ^4 0/0 66:71 ^5 0/0 72:77 ^0 0/0 78:83 ^1 0/0 84:89 ^2 0/0 90:95 ^3 0/0 96:101 ^4 0/0 102:107 ^5 0/0 108:113 ^0 0/0 114:119 ^1 0/0 120:125 ^2 0/0 126:127 ^3
+rcu_bh:
+c=-226 g=-226 s=1 jfq=-5701 j=72c7 nfqs=88/nfqsng=0(88) fqlh=0
+0/1 0:127 ^0
+0/3 0:35 ^0 0/0 36:71 ^1 0/0 72:107 ^2 0/0 108:127 ^3
+0/3f 0:5 ^0 0/3 6:11 ^1 0/0 12:17 ^2 0/0 18:23 ^3 0/0 24:29 ^4 0/0 30:35 ^5 0/0 36:41 ^0 0/0 42:47 ^1 0/0 48:53 ^2 0/0 54:59 ^3 0/0 60:65 ^4 0/0 66:71 ^5 0/0 72:77 ^0 0/0 78:83 ^1 0/0 84:89 ^2 0/0 90:95 ^3 0/0 96:101 ^4 0/0 102:107 ^5 0/0 108:113 ^0 0/0 114:119 ^1 0/0 120:125 ^2 0/0 126:127 ^3
+
+This is once again split into "rcu" and "rcu_bh" portions. The fields are
+as follows:
+
+o "c" is exactly the same as "completed" under rcu/rcugp.
+
+o "g" is exactly the same as "gpnum" under rcu/rcugp.
+
+o "s" is the "signaled" state that drives force_quiescent_state()'s
+ state machine.
+
+o "jfq" is the number of jiffies remaining for this grace period
+ before force_quiescent_state() is invoked to help push things
+ along. Note that CPUs in dyntick-idle mode thoughout the grace
+ period will not report on their own, but rather must be check by
+ some other CPU via force_quiescent_state().
+
+o "j" is the low-order four hex digits of the jiffies counter.
+ Yes, Paul did run into a number of problems that turned out to
+ be due to the jiffies counter no longer counting. Why do you ask?
+
+o "nfqs" is the number of calls to force_quiescent_state() since
+ boot.
+
+o "nfqsng" is the number of useless calls to force_quiescent_state(),
+ where there wasn't actually a grace period active. This can
+ happen due to races. The number in parentheses is the difference
+ between "nfqs" and "nfqsng", or the number of times that
+ force_quiescent_state() actually did some real work.
+
+o "fqlh" is the number of calls to force_quiescent_state() that
+ exited immediately (without even being counted in nfqs above)
+ due to contention on ->fqslock.
+
+o Each element of the form "1/1 0:127 ^0" represents one struct
+ rcu_node. Each line represents one level of the hierarchy, from
+ root to leaves. It is best to think of the rcu_data structures
+ as forming yet another level after the leaves. Note that there
+ might be either one, two, or three levels of rcu_node structures,
+ depending on the relationship between CONFIG_RCU_FANOUT and
+ CONFIG_NR_CPUS.
+
+ o The numbers separated by the "/" are the qsmask followed
+ by the qsmaskinit. The qsmask will have one bit
+ set for each entity in the next lower level that
+ has not yet checked in for the current grace period.
+ The qsmaskinit will have one bit for each entity that is
+ currently expected to check in during each grace period.
+ The value of qsmaskinit is assigned to that of qsmask
+ at the beginning of each grace period.
+
+ For example, for "rcu", the qsmask of the first entry
+ of the lowest level is 0x14, meaning that we are still
+ waiting for CPUs 2 and 4 to check in for the current
+ grace period.
+
+ o The numbers separated by the ":" are the range of CPUs
+ served by this struct rcu_node. This can be helpful
+ in working out how the hierarchy is wired together.
+
+ For example, the first entry at the lowest level shows
+ "0:5", indicating that it covers CPUs 0 through 5.
+
+ o The number after the "^" indicates the bit in the
+ next higher level rcu_node structure that this
+ rcu_node structure corresponds to.
+
+ For example, the first entry at the lowest level shows
+ "^0", indicating that it corresponds to bit zero in
+ the first entry at the middle level.
diff --git a/Documentation/arm/pxa/mfp.txt b/Documentation/arm/pxa/mfp.txt
new file mode 100644
index 000000000000..a179e5bc02c9
--- /dev/null
+++ b/Documentation/arm/pxa/mfp.txt
@@ -0,0 +1,286 @@
+ MFP Configuration for PXA2xx/PXA3xx Processors
+
+ Eric Miao <eric.miao@marvell.com>
+
+MFP stands for Multi-Function Pin, which is the pin-mux logic on PXA3xx and
+later PXA series processors. This document describes the existing MFP API,
+and how board/platform driver authors could make use of it.
+
+ Basic Concept
+===============
+
+Unlike the GPIO alternate function settings on PXA25x and PXA27x, a new MFP
+mechanism is introduced from PXA3xx to completely move the pin-mux functions
+out of the GPIO controller. In addition to pin-mux configurations, the MFP
+also controls the low power state, driving strength, pull-up/down and event
+detection of each pin. Below is a diagram of internal connections between
+the MFP logic and the remaining SoC peripherals:
+
+ +--------+
+ | |--(GPIO19)--+
+ | GPIO | |
+ | |--(GPIO...) |
+ +--------+ |
+ | +---------+
+ +--------+ +------>| |
+ | PWM2 |--(PWM_OUT)-------->| MFP |
+ +--------+ +------>| |-------> to external PAD
+ | +---->| |
+ +--------+ | | +-->| |
+ | SSP2 |---(TXD)----+ | | +---------+
+ +--------+ | |
+ | |
+ +--------+ | |
+ | Keypad |--(MKOUT4)----+ |
+ +--------+ |
+ |
+ +--------+ |
+ | UART2 |---(TXD)--------+
+ +--------+
+
+NOTE: the external pad is named as MFP_PIN_GPIO19, it doesn't necessarily
+mean it's dedicated for GPIO19, only as a hint that internally this pin
+can be routed from GPIO19 of the GPIO controller.
+
+To better understand the change from PXA25x/PXA27x GPIO alternate function
+to this new MFP mechanism, here are several key points:
+
+ 1. GPIO controller on PXA3xx is now a dedicated controller, same as other
+ internal controllers like PWM, SSP and UART, with 128 internal signals
+ which can be routed to external through one or more MFPs (e.g. GPIO<0>
+ can be routed through either MFP_PIN_GPIO0 as well as MFP_PIN_GPIO0_2,
+ see arch/arm/mach-pxa/mach/include/mfp-pxa300.h)
+
+ 2. Alternate function configuration is removed from this GPIO controller,
+ the remaining functions are pure GPIO-specific, i.e.
+
+ - GPIO signal level control
+ - GPIO direction control
+ - GPIO level change detection
+
+ 3. Low power state for each pin is now controlled by MFP, this means the
+ PGSRx registers on PXA2xx are now useless on PXA3xx
+
+ 4. Wakeup detection is now controlled by MFP, PWER does not control the
+ wakeup from GPIO(s) any more, depending on the sleeping state, ADxER
+ (as defined in pxa3xx-regs.h) controls the wakeup from MFP
+
+NOTE: with such a clear separation of MFP and GPIO, by GPIO<xx> we normally
+mean it is a GPIO signal, and by MFP<xxx> or pin xxx, we mean a physical
+pad (or ball).
+
+ MFP API Usage
+===============
+
+For board code writers, here are some guidelines:
+
+1. include ONE of the following header files in your <board>.c:
+
+ - #include <mach/mfp-pxa25x.h>
+ - #include <mach/mfp-pxa27x.h>
+ - #include <mach/mfp-pxa300.h>
+ - #include <mach/mfp-pxa320.h>
+ - #include <mach/mfp-pxa930.h>
+
+ NOTE: only one file in your <board>.c, depending on the processors used,
+ because pin configuration definitions may conflict in these file (i.e.
+ same name, different meaning and settings on different processors). E.g.
+ for zylonite platform, which support both PXA300/PXA310 and PXA320, two
+ separate files are introduced: zylonite_pxa300.c and zylonite_pxa320.c
+ (in addition to handle MFP configuration differences, they also handle
+ the other differences between the two combinations).
+
+ NOTE: PXA300 and PXA310 are almost identical in pin configurations (with
+ PXA310 supporting some additional ones), thus the difference is actually
+ covered in a single mfp-pxa300.h.
+
+2. prepare an array for the initial pin configurations, e.g.:
+
+ static unsigned long mainstone_pin_config[] __initdata = {
+ /* Chip Select */
+ GPIO15_nCS_1,
+
+ /* LCD - 16bpp Active TFT */
+ GPIOxx_TFT_LCD_16BPP,
+ GPIO16_PWM0_OUT, /* Backlight */
+
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO112_MMC_CMD,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+
+ ...
+
+ /* GPIO */
+ GPIO1_GPIO | WAKEUP_ON_EDGE_BOTH,
+ };
+
+ a) once the pin configurations are passed to pxa{2xx,3xx}_mfp_config(),
+ and written to the actual registers, they are useless and may discard,
+ adding '__initdata' will help save some additional bytes here.
+
+ b) when there is only one possible pin configurations for a component,
+ some simplified definitions can be used, e.g. GPIOxx_TFT_LCD_16BPP on
+ PXA25x and PXA27x processors
+
+ c) if by board design, a pin can be configured to wake up the system
+ from low power state, it can be 'OR'ed with any of:
+
+ WAKEUP_ON_EDGE_BOTH
+ WAKEUP_ON_EDGE_RISE
+ WAKEUP_ON_EDGE_FALL
+ WAKEUP_ON_LEVEL_HIGH - specifically for enabling of keypad GPIOs,
+
+ to indicate that this pin has the capability of wake-up the system,
+ and on which edge(s). This, however, doesn't necessarily mean the
+ pin _will_ wakeup the system, it will only when set_irq_wake() is
+ invoked with the corresponding GPIO IRQ (GPIO_IRQ(xx) or gpio_to_irq())
+ and eventually calls gpio_set_wake() for the actual register setting.
+
+ d) although PXA3xx MFP supports edge detection on each pin, the
+ internal logic will only wakeup the system when those specific bits
+ in ADxER registers are set, which can be well mapped to the
+ corresponding peripheral, thus set_irq_wake() can be called with
+ the peripheral IRQ to enable the wakeup.
+
+
+ MFP on PXA3xx
+===============
+
+Every external I/O pad on PXA3xx (excluding those for special purpose) has
+one MFP logic associated, and is controlled by one MFP register (MFPR).
+
+The MFPR has the following bit definitions (for PXA300/PXA310/PXA320):
+
+ 31 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ +-------------------------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ | RESERVED |PS|PU|PD| DRIVE |SS|SD|SO|EC|EF|ER|--| AF_SEL |
+ +-------------------------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+ Bit 3: RESERVED
+ Bit 4: EDGE_RISE_EN - enable detection of rising edge on this pin
+ Bit 5: EDGE_FALL_EN - enable detection of falling edge on this pin
+ Bit 6: EDGE_CLEAR - disable edge detection on this pin
+ Bit 7: SLEEP_OE_N - enable outputs during low power modes
+ Bit 8: SLEEP_DATA - output data on the pin during low power modes
+ Bit 9: SLEEP_SEL - selection control for low power modes signals
+ Bit 13: PULLDOWN_EN - enable the internal pull-down resistor on this pin
+ Bit 14: PULLUP_EN - enable the internal pull-up resistor on this pin
+ Bit 15: PULL_SEL - pull state controlled by selected alternate function
+ (0) or by PULL{UP,DOWN}_EN bits (1)
+
+ Bit 0 - 2: AF_SEL - alternate function selection, 8 possibilities, from 0-7
+ Bit 10-12: DRIVE - drive strength and slew rate
+ 0b000 - fast 1mA
+ 0b001 - fast 2mA
+ 0b002 - fast 3mA
+ 0b003 - fast 4mA
+ 0b004 - slow 6mA
+ 0b005 - fast 6mA
+ 0b006 - slow 10mA
+ 0b007 - fast 10mA
+
+ MFP Design for PXA2xx/PXA3xx
+==============================
+
+Due to the difference of pin-mux handling between PXA2xx and PXA3xx, a unified
+MFP API is introduced to cover both series of processors.
+
+The basic idea of this design is to introduce definitions for all possible pin
+configurations, these definitions are processor and platform independent, and
+the actual API invoked to convert these definitions into register settings and
+make them effective there-after.
+
+ Files Involved
+ --------------
+
+ - arch/arm/mach-pxa/include/mach/mfp.h
+
+ for
+ 1. Unified pin definitions - enum constants for all configurable pins
+ 2. processor-neutral bit definitions for a possible MFP configuration
+
+ - arch/arm/mach-pxa/include/mach/mfp-pxa3xx.h
+
+ for PXA3xx specific MFPR register bit definitions and PXA3xx common pin
+ configurations
+
+ - arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h
+
+ for PXA2xx specific definitions and PXA25x/PXA27x common pin configurations
+
+ - arch/arm/mach-pxa/include/mach/mfp-pxa25x.h
+ arch/arm/mach-pxa/include/mach/mfp-pxa27x.h
+ arch/arm/mach-pxa/include/mach/mfp-pxa300.h
+ arch/arm/mach-pxa/include/mach/mfp-pxa320.h
+ arch/arm/mach-pxa/include/mach/mfp-pxa930.h
+
+ for processor specific definitions
+
+ - arch/arm/mach-pxa/mfp-pxa3xx.c
+ - arch/arm/mach-pxa/mfp-pxa2xx.c
+
+ for implementation of the pin configuration to take effect for the actual
+ processor.
+
+ Pin Configuration
+ -----------------
+
+ The following comments are copied from mfp.h (see the actual source code
+ for most updated info)
+
+ /*
+ * a possible MFP configuration is represented by a 32-bit integer
+ *
+ * bit 0.. 9 - MFP Pin Number (1024 Pins Maximum)
+ * bit 10..12 - Alternate Function Selection
+ * bit 13..15 - Drive Strength
+ * bit 16..18 - Low Power Mode State
+ * bit 19..20 - Low Power Mode Edge Detection
+ * bit 21..22 - Run Mode Pull State
+ *
+ * to facilitate the definition, the following macros are provided
+ *
+ * MFP_CFG_DEFAULT - default MFP configuration value, with
+ * alternate function = 0,
+ * drive strength = fast 3mA (MFP_DS03X)
+ * low power mode = default
+ * edge detection = none
+ *
+ * MFP_CFG - default MFPR value with alternate function
+ * MFP_CFG_DRV - default MFPR value with alternate function and
+ * pin drive strength
+ * MFP_CFG_LPM - default MFPR value with alternate function and
+ * low power mode
+ * MFP_CFG_X - default MFPR value with alternate function,
+ * pin drive strength and low power mode
+ */
+
+ Examples of pin configurations are:
+
+ #define GPIO94_SSP3_RXD MFP_CFG_X(GPIO94, AF1, DS08X, FLOAT)
+
+ which reads GPIO94 can be configured as SSP3_RXD, with alternate function
+ selection of 1, driving strength of 0b101, and a float state in low power
+ modes.
+
+ NOTE: this is the default setting of this pin being configured as SSP3_RXD
+ which can be modified a bit in board code, though it is not recommended to
+ do so, simply because this default setting is usually carefully encoded,
+ and is supposed to work in most cases.
+
+ Register Settings
+ -----------------
+
+ Register settings on PXA3xx for a pin configuration is actually very
+ straight-forward, most bits can be converted directly into MFPR value
+ in a easier way. Two sets of MFPR values are calculated: the run-time
+ ones and the low power mode ones, to allow different settings.
+
+ The conversion from a generic pin configuration to the actual register
+ settings on PXA2xx is a bit complicated: many registers are involved,
+ including GAFRx, GPDRx, PGSRx, PWER, PKWR, PFER and PRER. Please see
+ mfp-pxa2xx.c for how the conversion is made.
diff --git a/Documentation/block/biodoc.txt b/Documentation/block/biodoc.txt
index 4dbb8be1c991..3c5434c83daf 100644
--- a/Documentation/block/biodoc.txt
+++ b/Documentation/block/biodoc.txt
@@ -914,7 +914,7 @@ I/O scheduler, a.k.a. elevator, is implemented in two layers. Generic dispatch
queue and specific I/O schedulers. Unless stated otherwise, elevator is used
to refer to both parts and I/O scheduler to specific I/O schedulers.
-Block layer implements generic dispatch queue in ll_rw_blk.c and elevator.c.
+Block layer implements generic dispatch queue in block/*.c.
The generic dispatch queue is responsible for properly ordering barrier
requests, requeueing, handling non-fs requests and all other subtleties.
@@ -926,8 +926,8 @@ be built inside the kernel. Each queue can choose different one and can also
change to another one dynamically.
A block layer call to the i/o scheduler follows the convention elv_xxx(). This
-calls elevator_xxx_fn in the elevator switch (drivers/block/elevator.c). Oh,
-xxx and xxx might not match exactly, but use your imagination. If an elevator
+calls elevator_xxx_fn in the elevator switch (block/elevator.c). Oh, xxx
+and xxx might not match exactly, but use your imagination. If an elevator
doesn't implement a function, the switch does nothing or some minimal house
keeping work.
diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt
index 4f3f3840320e..e3443ddcfb89 100644
--- a/Documentation/cpu-freq/user-guide.txt
+++ b/Documentation/cpu-freq/user-guide.txt
@@ -93,10 +93,8 @@ Several "PowerBook" and "iBook2" notebooks are supported.
1.5 SuperH
----------
-The following SuperH processors are supported by cpufreq:
-
-SH-3
-SH-4
+All SuperH processors supporting rate rounding through the clock
+framework are supported by cpufreq.
1.6 Blackfin
------------
diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt
index bd699da24666..45932ec21cee 100644
--- a/Documentation/cputopology.txt
+++ b/Documentation/cputopology.txt
@@ -31,3 +31,51 @@ not defined by include/asm-XXX/topology.h:
2) core_id: 0
3) thread_siblings: just the given CPU
4) core_siblings: just the given CPU
+
+Additionally, cpu topology information is provided under
+/sys/devices/system/cpu and includes these files. The internal
+source for the output is in brackets ("[]").
+
+ kernel_max: the maximum cpu index allowed by the kernel configuration.
+ [NR_CPUS-1]
+
+ offline: cpus that are not online because they have been
+ HOTPLUGGED off (see cpu-hotplug.txt) or exceed the limit
+ of cpus allowed by the kernel configuration (kernel_max
+ above). [~cpu_online_mask + cpus >= NR_CPUS]
+
+ online: cpus that are online and being scheduled [cpu_online_mask]
+
+ possible: cpus that have been allocated resources and can be
+ brought online if they are present. [cpu_possible_mask]
+
+ present: cpus that have been identified as being present in the
+ system. [cpu_present_mask]
+
+The format for the above output is compatible with cpulist_parse()
+[see <linux/cpumask.h>]. Some examples follow.
+
+In this example, there are 64 cpus in the system but cpus 32-63 exceed
+the kernel max which is limited to 0..31 by the NR_CPUS config option
+being 32. Note also that cpus 2 and 4-31 are not online but could be
+brought online as they are both present and possible.
+
+ kernel_max: 31
+ offline: 2,4-31,32-63
+ online: 0-1,3
+ possible: 0-31
+ present: 0-31
+
+In this example, the NR_CPUS config option is 128, but the kernel was
+started with possible_cpus=144. There are 4 cpus in the system and cpu2
+was manually taken offline (and is the only cpu that can be brought
+online.)
+
+ kernel_max: 127
+ offline: 2,4-127,128-143
+ online: 0-1,3
+ possible: 0-127
+ present: 0-3
+
+See cpu-hotplug.txt for the possible_cpus=NUM kernel start parameter
+as well as more information on the various cpumask's.
diff --git a/Documentation/credentials.txt b/Documentation/credentials.txt
new file mode 100644
index 000000000000..df03169782ea
--- /dev/null
+++ b/Documentation/credentials.txt
@@ -0,0 +1,582 @@
+ ====================
+ CREDENTIALS IN LINUX
+ ====================
+
+By: David Howells <dhowells@redhat.com>
+
+Contents:
+
+ (*) Overview.
+
+ (*) Types of credentials.
+
+ (*) File markings.
+
+ (*) Task credentials.
+
+ - Immutable credentials.
+ - Accessing task credentials.
+ - Accessing another task's credentials.
+ - Altering credentials.
+ - Managing credentials.
+
+ (*) Open file credentials.
+
+ (*) Overriding the VFS's use of credentials.
+
+
+========
+OVERVIEW
+========
+
+There are several parts to the security check performed by Linux when one
+object acts upon another:
+
+ (1) Objects.
+
+ Objects are things in the system that may be acted upon directly by
+ userspace programs. Linux has a variety of actionable objects, including:
+
+ - Tasks
+ - Files/inodes
+ - Sockets
+ - Message queues
+ - Shared memory segments
+ - Semaphores
+ - Keys
+
+ As a part of the description of all these objects there is a set of
+ credentials. What's in the set depends on the type of object.
+
+ (2) Object ownership.
+
+ Amongst the credentials of most objects, there will be a subset that
+ indicates the ownership of that object. This is used for resource
+ accounting and limitation (disk quotas and task rlimits for example).
+
+ In a standard UNIX filesystem, for instance, this will be defined by the
+ UID marked on the inode.
+
+ (3) The objective context.
+
+ Also amongst the credentials of those objects, there will be a subset that
+ indicates the 'objective context' of that object. This may or may not be
+ the same set as in (2) - in standard UNIX files, for instance, this is the
+ defined by the UID and the GID marked on the inode.
+
+ The objective context is used as part of the security calculation that is
+ carried out when an object is acted upon.
+
+ (4) Subjects.
+
+ A subject is an object that is acting upon another object.
+
+ Most of the objects in the system are inactive: they don't act on other
+ objects within the system. Processes/tasks are the obvious exception:
+ they do stuff; they access and manipulate things.
+
+ Objects other than tasks may under some circumstances also be subjects.
+ For instance an open file may send SIGIO to a task using the UID and EUID
+ given to it by a task that called fcntl(F_SETOWN) upon it. In this case,
+ the file struct will have a subjective context too.
+
+ (5) The subjective context.
+
+ A subject has an additional interpretation of its credentials. A subset
+ of its credentials forms the 'subjective context'. The subjective context
+ is used as part of the security calculation that is carried out when a
+ subject acts.
+
+ A Linux task, for example, has the FSUID, FSGID and the supplementary
+ group list for when it is acting upon a file - which are quite separate
+ from the real UID and GID that normally form the objective context of the
+ task.
+
+ (6) Actions.
+
+ Linux has a number of actions available that a subject may perform upon an
+ object. The set of actions available depends on the nature of the subject
+ and the object.
+
+ Actions include reading, writing, creating and deleting files; forking or
+ signalling and tracing tasks.
+
+ (7) Rules, access control lists and security calculations.
+
+ When a subject acts upon an object, a security calculation is made. This
+ involves taking the subjective context, the objective context and the
+ action, and searching one or more sets of rules to see whether the subject
+ is granted or denied permission to act in the desired manner on the
+ object, given those contexts.
+
+ There are two main sources of rules:
+
+ (a) Discretionary access control (DAC):
+
+ Sometimes the object will include sets of rules as part of its
+ description. This is an 'Access Control List' or 'ACL'. A Linux
+ file may supply more than one ACL.
+
+ A traditional UNIX file, for example, includes a permissions mask that
+ is an abbreviated ACL with three fixed classes of subject ('user',
+ 'group' and 'other'), each of which may be granted certain privileges
+ ('read', 'write' and 'execute' - whatever those map to for the object
+ in question). UNIX file permissions do not allow the arbitrary
+ specification of subjects, however, and so are of limited use.
+
+ A Linux file might also sport a POSIX ACL. This is a list of rules
+ that grants various permissions to arbitrary subjects.
+
+ (b) Mandatory access control (MAC):
+
+ The system as a whole may have one or more sets of rules that get
+ applied to all subjects and objects, regardless of their source.
+ SELinux and Smack are examples of this.
+
+ In the case of SELinux and Smack, each object is given a label as part
+ of its credentials. When an action is requested, they take the
+ subject label, the object label and the action and look for a rule
+ that says that this action is either granted or denied.
+
+
+====================
+TYPES OF CREDENTIALS
+====================
+
+The Linux kernel supports the following types of credentials:
+
+ (1) Traditional UNIX credentials.
+
+ Real User ID
+ Real Group ID
+
+ The UID and GID are carried by most, if not all, Linux objects, even if in
+ some cases it has to be invented (FAT or CIFS files for example, which are
+ derived from Windows). These (mostly) define the objective context of
+ that object, with tasks being slightly different in some cases.
+
+ Effective, Saved and FS User ID
+ Effective, Saved and FS Group ID
+ Supplementary groups
+
+ These are additional credentials used by tasks only. Usually, an
+ EUID/EGID/GROUPS will be used as the subjective context, and real UID/GID
+ will be used as the objective. For tasks, it should be noted that this is
+ not always true.
+
+ (2) Capabilities.
+
+ Set of permitted capabilities
+ Set of inheritable capabilities
+ Set of effective capabilities
+ Capability bounding set
+
+ These are only carried by tasks. They indicate superior capabilities
+ granted piecemeal to a task that an ordinary task wouldn't otherwise have.
+ These are manipulated implicitly by changes to the traditional UNIX
+ credentials, but can also be manipulated directly by the capset() system
+ call.
+
+ The permitted capabilities are those caps that the process might grant
+ itself to its effective or permitted sets through capset(). This
+ inheritable set might also be so constrained.
+
+ The effective capabilities are the ones that a task is actually allowed to
+ make use of itself.
+
+ The inheritable capabilities are the ones that may get passed across
+ execve().
+
+ The bounding set limits the capabilities that may be inherited across
+ execve(), especially when a binary is executed that will execute as UID 0.
+
+ (3) Secure management flags (securebits).
+
+ These are only carried by tasks. These govern the way the above
+ credentials are manipulated and inherited over certain operations such as
+ execve(). They aren't used directly as objective or subjective
+ credentials.
+
+ (4) Keys and keyrings.
+
+ These are only carried by tasks. They carry and cache security tokens
+ that don't fit into the other standard UNIX credentials. They are for
+ making such things as network filesystem keys available to the file
+ accesses performed by processes, without the necessity of ordinary
+ programs having to know about security details involved.
+
+ Keyrings are a special type of key. They carry sets of other keys and can
+ be searched for the desired key. Each process may subscribe to a number
+ of keyrings:
+
+ Per-thread keying
+ Per-process keyring
+ Per-session keyring
+
+ When a process accesses a key, if not already present, it will normally be
+ cached on one of these keyrings for future accesses to find.
+
+ For more information on using keys, see Documentation/keys.txt.
+
+ (5) LSM
+
+ The Linux Security Module allows extra controls to be placed over the
+ operations that a task may do. Currently Linux supports two main
+ alternate LSM options: SELinux and Smack.
+
+ Both work by labelling the objects in a system and then applying sets of
+ rules (policies) that say what operations a task with one label may do to
+ an object with another label.
+
+ (6) AF_KEY
+
+ This is a socket-based approach to credential management for networking
+ stacks [RFC 2367]. It isn't discussed by this document as it doesn't
+ interact directly with task and file credentials; rather it keeps system
+ level credentials.
+
+
+When a file is opened, part of the opening task's subjective context is
+recorded in the file struct created. This allows operations using that file
+struct to use those credentials instead of the subjective context of the task
+that issued the operation. An example of this would be a file opened on a
+network filesystem where the credentials of the opened file should be presented
+to the server, regardless of who is actually doing a read or a write upon it.
+
+
+=============
+FILE MARKINGS
+=============
+
+Files on disk or obtained over the network may have annotations that form the
+objective security context of that file. Depending on the type of filesystem,
+this may include one or more of the following:
+
+ (*) UNIX UID, GID, mode;
+
+ (*) Windows user ID;
+
+ (*) Access control list;
+
+ (*) LSM security label;
+
+ (*) UNIX exec privilege escalation bits (SUID/SGID);
+
+ (*) File capabilities exec privilege escalation bits.
+
+These are compared to the task's subjective security context, and certain
+operations allowed or disallowed as a result. In the case of execve(), the
+privilege escalation bits come into play, and may allow the resulting process
+extra privileges, based on the annotations on the executable file.
+
+
+================
+TASK CREDENTIALS
+================
+
+In Linux, all of a task's credentials are held in (uid, gid) or through
+(groups, keys, LSM security) a refcounted structure of type 'struct cred'.
+Each task points to its credentials by a pointer called 'cred' in its
+task_struct.
+
+Once a set of credentials has been prepared and committed, it may not be
+changed, barring the following exceptions:
+
+ (1) its reference count may be changed;
+
+ (2) the reference count on the group_info struct it points to may be changed;
+
+ (3) the reference count on the security data it points to may be changed;
+
+ (4) the reference count on any keyrings it points to may be changed;
+
+ (5) any keyrings it points to may be revoked, expired or have their security
+ attributes changed; and
+
+ (6) the contents of any keyrings to which it points may be changed (the whole
+ point of keyrings being a shared set of credentials, modifiable by anyone
+ with appropriate access).
+
+To alter anything in the cred struct, the copy-and-replace principle must be
+adhered to. First take a copy, then alter the copy and then use RCU to change
+the task pointer to make it point to the new copy. There are wrappers to aid
+with this (see below).
+
+A task may only alter its _own_ credentials; it is no longer permitted for a
+task to alter another's credentials. This means the capset() system call is no
+longer permitted to take any PID other than the one of the current process.
+Also keyctl_instantiate() and keyctl_negate() functions no longer permit
+attachment to process-specific keyrings in the requesting process as the
+instantiating process may need to create them.
+
+
+IMMUTABLE CREDENTIALS
+---------------------
+
+Once a set of credentials has been made public (by calling commit_creds() for
+example), it must be considered immutable, barring two exceptions:
+
+ (1) The reference count may be altered.
+
+ (2) Whilst the keyring subscriptions of a set of credentials may not be
+ changed, the keyrings subscribed to may have their contents altered.
+
+To catch accidental credential alteration at compile time, struct task_struct
+has _const_ pointers to its credential sets, as does struct file. Furthermore,
+certain functions such as get_cred() and put_cred() operate on const pointers,
+thus rendering casts unnecessary, but require to temporarily ditch the const
+qualification to be able to alter the reference count.
+
+
+ACCESSING TASK CREDENTIALS
+--------------------------
+
+A task being able to alter only its own credentials permits the current process
+to read or replace its own credentials without the need for any form of locking
+- which simplifies things greatly. It can just call:
+
+ const struct cred *current_cred()
+
+to get a pointer to its credentials structure, and it doesn't have to release
+it afterwards.
+
+There are convenience wrappers for retrieving specific aspects of a task's
+credentials (the value is simply returned in each case):
+
+ uid_t current_uid(void) Current's real UID
+ gid_t current_gid(void) Current's real GID
+ uid_t current_euid(void) Current's effective UID
+ gid_t current_egid(void) Current's effective GID
+ uid_t current_fsuid(void) Current's file access UID
+ gid_t current_fsgid(void) Current's file access GID
+ kernel_cap_t current_cap(void) Current's effective capabilities
+ void *current_security(void) Current's LSM security pointer
+ struct user_struct *current_user(void) Current's user account
+
+There are also convenience wrappers for retrieving specific associated pairs of
+a task's credentials:
+
+ void current_uid_gid(uid_t *, gid_t *);
+ void current_euid_egid(uid_t *, gid_t *);
+ void current_fsuid_fsgid(uid_t *, gid_t *);
+
+which return these pairs of values through their arguments after retrieving
+them from the current task's credentials.
+
+
+In addition, there is a function for obtaining a reference on the current
+process's current set of credentials:
+
+ const struct cred *get_current_cred(void);
+
+and functions for getting references to one of the credentials that don't
+actually live in struct cred:
+
+ struct user_struct *get_current_user(void);
+ struct group_info *get_current_groups(void);
+
+which get references to the current process's user accounting structure and
+supplementary groups list respectively.
+
+Once a reference has been obtained, it must be released with put_cred(),
+free_uid() or put_group_info() as appropriate.
+
+
+ACCESSING ANOTHER TASK'S CREDENTIALS
+------------------------------------
+
+Whilst a task may access its own credentials without the need for locking, the
+same is not true of a task wanting to access another task's credentials. It
+must use the RCU read lock and rcu_dereference().
+
+The rcu_dereference() is wrapped by:
+
+ const struct cred *__task_cred(struct task_struct *task);
+
+This should be used inside the RCU read lock, as in the following example:
+
+ void foo(struct task_struct *t, struct foo_data *f)
+ {
+ const struct cred *tcred;
+ ...
+ rcu_read_lock();
+ tcred = __task_cred(t);
+ f->uid = tcred->uid;
+ f->gid = tcred->gid;
+ f->groups = get_group_info(tcred->groups);
+ rcu_read_unlock();
+ ...
+ }
+
+A function need not get RCU read lock to use __task_cred() if it is holding a
+spinlock at the time as this implicitly holds the RCU read lock.
+
+Should it be necessary to hold another task's credentials for a long period of
+time, and possibly to sleep whilst doing so, then the caller should get a
+reference on them using:
+
+ const struct cred *get_task_cred(struct task_struct *task);
+
+This does all the RCU magic inside of it. The caller must call put_cred() on
+the credentials so obtained when they're finished with.
+
+There are a couple of convenience functions to access bits of another task's
+credentials, hiding the RCU magic from the caller:
+
+ uid_t task_uid(task) Task's real UID
+ uid_t task_euid(task) Task's effective UID
+
+If the caller is holding a spinlock or the RCU read lock at the time anyway,
+then:
+
+ __task_cred(task)->uid
+ __task_cred(task)->euid
+
+should be used instead. Similarly, if multiple aspects of a task's credentials
+need to be accessed, RCU read lock or a spinlock should be used, __task_cred()
+called, the result stored in a temporary pointer and then the credential
+aspects called from that before dropping the lock. This prevents the
+potentially expensive RCU magic from being invoked multiple times.
+
+Should some other single aspect of another task's credentials need to be
+accessed, then this can be used:
+
+ task_cred_xxx(task, member)
+
+where 'member' is a non-pointer member of the cred struct. For instance:
+
+ uid_t task_cred_xxx(task, suid);
+
+will retrieve 'struct cred::suid' from the task, doing the appropriate RCU
+magic. This may not be used for pointer members as what they point to may
+disappear the moment the RCU read lock is dropped.
+
+
+ALTERING CREDENTIALS
+--------------------
+
+As previously mentioned, a task may only alter its own credentials, and may not
+alter those of another task. This means that it doesn't need to use any
+locking to alter its own credentials.
+
+To alter the current process's credentials, a function should first prepare a
+new set of credentials by calling:
+
+ struct cred *prepare_creds(void);
+
+this locks current->cred_replace_mutex and then allocates and constructs a
+duplicate of the current process's credentials, returning with the mutex still
+held if successful. It returns NULL if not successful (out of memory).
+
+The mutex prevents ptrace() from altering the ptrace state of a process whilst
+security checks on credentials construction and changing is taking place as
+the ptrace state may alter the outcome, particularly in the case of execve().
+
+The new credentials set should be altered appropriately, and any security
+checks and hooks done. Both the current and the proposed sets of credentials
+are available for this purpose as current_cred() will return the current set
+still at this point.
+
+
+When the credential set is ready, it should be committed to the current process
+by calling:
+
+ int commit_creds(struct cred *new);
+
+This will alter various aspects of the credentials and the process, giving the
+LSM a chance to do likewise, then it will use rcu_assign_pointer() to actually
+commit the new credentials to current->cred, it will release
+current->cred_replace_mutex to allow ptrace() to take place, and it will notify
+the scheduler and others of the changes.
+
+This function is guaranteed to return 0, so that it can be tail-called at the
+end of such functions as sys_setresuid().
+
+Note that this function consumes the caller's reference to the new credentials.
+The caller should _not_ call put_cred() on the new credentials afterwards.
+
+Furthermore, once this function has been called on a new set of credentials,
+those credentials may _not_ be changed further.
+
+
+Should the security checks fail or some other error occur after prepare_creds()
+has been called, then the following function should be invoked:
+
+ void abort_creds(struct cred *new);
+
+This releases the lock on current->cred_replace_mutex that prepare_creds() got
+and then releases the new credentials.
+
+
+A typical credentials alteration function would look something like this:
+
+ int alter_suid(uid_t suid)
+ {
+ struct cred *new;
+ int ret;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ new->suid = suid;
+ ret = security_alter_suid(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
+ }
+
+ return commit_creds(new);
+ }
+
+
+MANAGING CREDENTIALS
+--------------------
+
+There are some functions to help manage credentials:
+
+ (*) void put_cred(const struct cred *cred);
+
+ This releases a reference to the given set of credentials. If the
+ reference count reaches zero, the credentials will be scheduled for
+ destruction by the RCU system.
+
+ (*) const struct cred *get_cred(const struct cred *cred);
+
+ This gets a reference on a live set of credentials, returning a pointer to
+ that set of credentials.
+
+ (*) struct cred *get_new_cred(struct cred *cred);
+
+ This gets a reference on a set of credentials that is under construction
+ and is thus still mutable, returning a pointer to that set of credentials.
+
+
+=====================
+OPEN FILE CREDENTIALS
+=====================
+
+When a new file is opened, a reference is obtained on the opening task's
+credentials and this is attached to the file struct as 'f_cred' in place of
+'f_uid' and 'f_gid'. Code that used to access file->f_uid and file->f_gid
+should now access file->f_cred->fsuid and file->f_cred->fsgid.
+
+It is safe to access f_cred without the use of RCU or locking because the
+pointer will not change over the lifetime of the file struct, and nor will the
+contents of the cred struct pointed to, barring the exceptions listed above
+(see the Task Credentials section).
+
+
+=======================================
+OVERRIDING THE VFS'S USE OF CREDENTIALS
+=======================================
+
+Under some circumstances it is desirable to override the credentials used by
+the VFS, and that can be done by calling into such as vfs_mkdir() with a
+different set of credentials. This is done in the following places:
+
+ (*) sys_faccessat().
+
+ (*) do_coredump().
+
+ (*) nfs4recover.c.
diff --git a/Documentation/dvb/technisat.txt b/Documentation/dvb/technisat.txt
new file mode 100644
index 000000000000..cdf6ee4b2da1
--- /dev/null
+++ b/Documentation/dvb/technisat.txt
@@ -0,0 +1,69 @@
+How to set up the Technisat devices
+===================================
+
+1) Find out what device you have
+================================
+
+First start your linux box with a shipped kernel:
+lspci -vvv for a PCI device (lsusb -vvv for an USB device) will show you for example:
+02:0b.0 Network controller: Techsan Electronics Co Ltd B2C2 FlexCopII DVB chip / Technisat SkyStar2 DVB card (rev 02)
+
+dmesg | grep frontend may show you for example:
+DVB: registering frontend 0 (Conexant CX24123/CX24109)...
+
+2) Kernel compilation:
+======================
+
+If the Technisat is the only TV device in your box get rid of unnecessary modules and check this one:
+"Multimedia devices" => "Customise analog and hybrid tuner modules to build"
+In this directory uncheck every driver which is activated there.
+
+Then please activate:
+2a) Main module part:
+
+a.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters"
+b.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC PCI" in case of a PCI card OR
+c.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC USB" in case of an USB 1.1 adapter
+d.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Enable debug for the B2C2 FlexCop drivers"
+Notice: d.) is helpful for troubleshooting
+
+2b) Frontend module part:
+
+1.) Revision 2.3:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink VP310/MT312/ZL10313 based"
+
+2.) Revision 2.6:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0299 based"
+
+3.) Revision 2.7:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "Samsung S5H1420 based"
+c.)"Multimedia devices" => "Customise DVB frontends" => "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
+d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller"
+
+4.) Revision 2.8:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
+c.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24123 based"
+d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller"
+
+5.) DVB-T card:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink MT352 based"
+
+6.) DVB-C card:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0297 based"
+
+7.) ATSC card 1st generation:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "Broadcom BCM3510"
+
+8.) ATSC card 2nd generation:
+a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build"
+b.)"Multimedia devices" => "Customise DVB frontends" => "NxtWave Communications NXT2002/NXT2004 based"
+c.)"Multimedia devices" => "Customise DVB frontends" => "LG Electronics LGDT3302/LGDT3303 based"
+
+Author: Uwe Bugla <uwe.bugla@gmx.de> December 2008
diff --git a/Documentation/fb/pxafb.txt b/Documentation/fb/pxafb.txt
index db9b8500b43b..d143a0a749f9 100644
--- a/Documentation/fb/pxafb.txt
+++ b/Documentation/fb/pxafb.txt
@@ -5,9 +5,13 @@ The driver supports the following options, either via
options=<OPTIONS> when modular or video=pxafb:<OPTIONS> when built in.
For example:
- modprobe pxafb options=mode:640x480-8,passive
+ modprobe pxafb options=vmem:2M,mode:640x480-8,passive
or on the kernel command line
- video=pxafb:mode:640x480-8,passive
+ video=pxafb:vmem:2M,mode:640x480-8,passive
+
+vmem: VIDEO_MEM_SIZE
+ Amount of video memory to allocate (can be suffixed with K or M
+ for kilobytes or megabytes)
mode:XRESxYRES[-BPP]
XRES == LCCR1_PPL + 1
@@ -52,3 +56,87 @@ outputen:POLARITY
pixclockpol:POLARITY
pixel clock polarity
0 => falling edge, 1 => rising edge
+
+
+Overlay Support for PXA27x and later LCD controllers
+====================================================
+
+ PXA27x and later processors support overlay1 and overlay2 on-top of the
+ base framebuffer (although under-neath the base is also possible). They
+ support palette and no-palette RGB formats, as well as YUV formats (only
+ available on overlay2). These overlays have dedicated DMA channels and
+ behave in a similar way as a framebuffer.
+
+ However, there are some differences between these overlay framebuffers
+ and normal framebuffers, as listed below:
+
+ 1. overlay can start at a 32-bit word aligned position within the base
+ framebuffer, which means they have a start (x, y). This information
+ is encoded into var->nonstd (no, var->xoffset and var->yoffset are
+ not for such purpose).
+
+ 2. overlay framebuffer is allocated dynamically according to specified
+ 'struct fb_var_screeninfo', the amount is decided by:
+
+ var->xres_virtual * var->yres_virtual * bpp
+
+ bpp = 16 -- for RGB565 or RGBT555
+ = 24 -- for YUV444 packed
+ = 24 -- for YUV444 planar
+ = 16 -- for YUV422 planar (1 pixel = 1 Y + 1/2 Cb + 1/2 Cr)
+ = 12 -- for YUV420 planar (1 pixel = 1 Y + 1/4 Cb + 1/4 Cr)
+
+ NOTE:
+
+ a. overlay does not support panning in x-direction, thus
+ var->xres_virtual will always be equal to var->xres
+
+ b. line length of overlay(s) must be on a 32-bit word boundary,
+ for YUV planar modes, it is a requirement for the component
+ with minimum bits per pixel, e.g. for YUV420, Cr component
+ for one pixel is actually 2-bits, it means the line length
+ should be a multiple of 16-pixels
+
+ c. starting horizontal position (XPOS) should start on a 32-bit
+ word boundary, otherwise the fb_check_var() will just fail.
+
+ d. the rectangle of the overlay should be within the base plane,
+ otherwise fail
+
+ Applications should follow the sequence below to operate an overlay
+ framebuffer:
+
+ a. open("/dev/fb[1-2]", ...)
+ b. ioctl(fd, FBIOGET_VSCREENINFO, ...)
+ c. modify 'var' with desired parameters:
+ 1) var->xres and var->yres
+ 2) larger var->yres_virtual if more memory is required,
+ usually for double-buffering
+ 3) var->nonstd for starting (x, y) and color format
+ 4) var->{red, green, blue, transp} if RGB mode is to be used
+ d. ioctl(fd, FBIOPUT_VSCREENINFO, ...)
+ e. ioctl(fd, FBIOGET_FSCREENINFO, ...)
+ f. mmap
+ g. ...
+
+ 3. for YUV planar formats, these are actually not supported within the
+ framebuffer framework, application has to take care of the offsets
+ and lengths of each component within the framebuffer.
+
+ 4. var->nonstd is used to pass starting (x, y) position and color format,
+ the detailed bit fields are shown below:
+
+ 31 23 20 10 0
+ +-----------------+---+----------+----------+
+ | ... unused ... |FOR| XPOS | YPOS |
+ +-----------------+---+----------+----------+
+
+ FOR - color format, as defined by OVERLAY_FORMAT_* in pxafb.h
+ 0 - RGB
+ 1 - YUV444 PACKED
+ 2 - YUV444 PLANAR
+ 3 - YUV422 PLANAR
+ 4 - YUR420 PLANAR
+
+ XPOS - starting horizontal position
+ YPOS - starting vertical position
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index c28a2ac88f9d..dc7c681e532c 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -120,13 +120,6 @@ Who: Christoph Hellwig <hch@lst.de>
---------------------------
-What: eepro100 network driver
-When: January 2007
-Why: replaced by the e100 driver
-Who: Adrian Bunk <bunk@stusta.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.
@@ -244,18 +237,6 @@ Who: Michael Buesch <mb@bu3sch.de>
---------------------------
-What: init_mm export
-When: 2.6.26
-Why: Not used in-tree. The current out-of-tree users used it to
- work around problems in the CPA code which should be resolved
- by now. One usecase was described to provide verification code
- of the CPA operation. That's a good idea in general, but such
- code / infrastructure should be in the kernel and not in some
- out-of-tree driver.
-Who: Thomas Gleixner <tglx@linutronix.de>
-
-----------------------------
-
What: usedac i386 kernel parameter
When: 2.6.27
Why: replaced by allowdac and no dac combination
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index bb1b0dd3bfcb..71df353e367c 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -1339,10 +1339,13 @@ nmi_watchdog
Enables/Disables the NMI watchdog on x86 systems. When the value is non-zero
the NMI watchdog is enabled and will continuously test all online cpus to
-determine whether or not they are still functioning properly.
+determine whether or not they are still functioning properly. Currently,
+passing "nmi_watchdog=" parameter at boot time is required for this function
+to work.
-Because the NMI watchdog shares registers with oprofile, by disabling the NMI
-watchdog, oprofile may have more registers to utilize.
+If LAPIC NMI watchdog method is in use (nmi_watchdog=2 kernel parameter), the
+NMI watchdog shares registers with oprofile. By disabling the NMI watchdog,
+oprofile may have more registers to utilize.
msgmni
------
diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt
index 0a1668ba2600..9878f50d6ed6 100644
--- a/Documentation/filesystems/xfs.txt
+++ b/Documentation/filesystems/xfs.txt
@@ -229,10 +229,6 @@ The following sysctls are available for the XFS filesystem:
ISGID bit is cleared if the irix_sgid_inherit compatibility sysctl
is set.
- fs.xfs.restrict_chown (Min: 0 Default: 1 Max: 1)
- Controls whether unprivileged users can use chown to "give away"
- a file to another user.
-
fs.xfs.inherit_sync (Min: 0 Default: 1 Max: 1)
Setting this to "1" will cause the "sync" flag set
by the xfs_io(8) chattr command on a directory to be
diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt
index 7a7753321a26..51104f9194a5 100644
--- a/Documentation/kbuild/makefiles.txt
+++ b/Documentation/kbuild/makefiles.txt
@@ -383,6 +383,20 @@ more details, with real examples.
to prerequisites are referenced with $(src) (because they are not
generated files).
+ $(kecho)
+ echoing information to user in a rule is often a good practice
+ but when execution "make -s" one does not expect to see any output
+ except for warnings/errors.
+ To support this kbuild define $(kecho) which will echo out the
+ text following $(kecho) to stdout except if "make -s" is used.
+
+ Example:
+ #arch/blackfin/boot/Makefile
+ $(obj)/vmImage: $(obj)/vmlinux.gz
+ $(call if_changed,uimage)
+ @$(kecho) 'Kernel: $@ is ready'
+
+
--- 3.11 $(CC) support functions
The kernel may be built with several different versions of
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2919a2e91938..a2d8805c03d5 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -89,6 +89,7 @@ parameter is applicable:
SPARC Sparc architecture is enabled.
SWSUSP Software suspend (hibernation) is enabled.
SUSPEND System suspend states are enabled.
+ FTRACE Function tracing enabled.
TS Appropriate touchscreen support is enabled.
USB USB support is enabled.
USBHID USB Human Interface Device support is enabled.
@@ -220,14 +221,17 @@ and is between 256 and 4096 characters. It is defined in the file
Bits in debug_level correspond to a level in
ACPI_DEBUG_PRINT statements, e.g.,
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ...
- See Documentation/acpi/debug.txt for more information
- about debug layers and levels.
+ The debug_level mask defaults to "info". See
+ Documentation/acpi/debug.txt for more information about
+ debug layers and levels.
+ Enable processor driver info messages:
+ acpi.debug_layer=0x20000000
+ Enable PCI/PCI interrupt routing info messages:
+ acpi.debug_layer=0x400000
Enable AML "Debug" output, i.e., stores to the Debug
object while interpreting AML:
acpi.debug_layer=0xffffffff acpi.debug_level=0x2
- Enable PCI/PCI interrupt routing info messages:
- acpi.debug_layer=0x400000 acpi.debug_level=0x4
Enable all messages related to ACPI hardware:
acpi.debug_layer=0x2 acpi.debug_level=0xffffffff
@@ -819,6 +823,9 @@ and is between 256 and 4096 characters. It is defined in the file
hlt [BUGS=ARM,SH]
+ hvc_iucv= [S390] Number of z/VM IUCV Hypervisor console (HVC)
+ back-ends. Valid parameters: 0..8
+
i8042.debug [HW] Toggle i8042 debug mode
i8042.direct [HW] Put keyboard port into non-translated mode
i8042.dumbkbd [HW] Pretend that controller can only read data from
@@ -1401,7 +1408,20 @@ and is between 256 and 4096 characters. It is defined in the file
when a NMI is triggered.
Format: [state][,regs][,debounce][,die]
- nmi_watchdog= [KNL,BUGS=X86-32] Debugging features for SMP kernels
+ nmi_watchdog= [KNL,BUGS=X86-32,X86-64] Debugging features for SMP kernels
+ Format: [panic,][num]
+ Valid num: 0,1,2
+ 0 - turn nmi_watchdog off
+ 1 - use the IO-APIC timer for the NMI watchdog
+ 2 - use the local APIC for the NMI watchdog using
+ a performance counter. Note: This will use one performance
+ counter and the local APIC's performance vector.
+ When panic is specified panic when an NMI watchdog timeout occurs.
+ This is useful when you use a panic=... timeout and need the box
+ quickly up again.
+ Instead of 1 and 2 it is possible to use the following
+ symbolic names: lapic and ioapic
+ Example: nmi_watchdog=2 or nmi_watchdog=panic,lapic
no387 [BUGS=X86-32] Tells the kernel to use the 387 maths
emulation library even if a 387 maths coprocessor
@@ -1457,6 +1477,10 @@ and is between 256 and 4096 characters. It is defined in the file
instruction doesn't work correctly and not to
use it.
+ no_file_caps Tells the kernel not to honor file capabilities. The
+ only way then for a file to be executed with privilege
+ is to be setuid root or executed by root.
+
nohalt [IA-64] Tells the kernel not to use the power saving
function PAL_HALT_LIGHT when idle. This increases
power-consumption. On the positive side, it reduces
@@ -1634,6 +1658,17 @@ and is between 256 and 4096 characters. It is defined in the file
nomsi [MSI] If the PCI_MSI kernel config parameter is
enabled, this kernel boot option can be used to
disable the use of MSI interrupts system-wide.
+ noioapicquirk [APIC] Disable all boot interrupt quirks.
+ Safety option to keep boot IRQs enabled. This
+ should never be necessary.
+ ioapicreroute [APIC] Enable rerouting of boot IRQs to the
+ primary IO-APIC for bridges that cannot disable
+ boot IRQs. This fixes a source of spurious IRQs
+ when the system masks IRQs.
+ noioapicreroute [APIC] Disable workaround that uses the
+ boot IRQ equivalent of an IRQ that connects to
+ a chipset where boot IRQs cannot be disabled.
+ The opposite of ioapicreroute.
biosirq [X86-32] Use PCI BIOS calls to get the interrupt
routing table. These calls are known to be buggy
on several machines and they hang the machine
@@ -2173,6 +2208,9 @@ and is between 256 and 4096 characters. It is defined in the file
st= [HW,SCSI] SCSI tape parameters (buffers, etc.)
See Documentation/scsi/st.txt.
+ stacktrace [FTRACE]
+ Enabled the stack tracer on boot up.
+
sti= [PARISC,HW]
Format: <num>
Set the STI (builtin display/keyboard on the HP-PARISC
@@ -2257,12 +2295,27 @@ and is between 256 and 4096 characters. It is defined in the file
See comment before function dc390_setup() in
drivers/scsi/tmscsim.c.
+ topology= [S390]
+ Format: {off | on}
+ Specify if the kernel should make use of the cpu
+ topology informations if the hardware supports these.
+ The scheduler will make use of these informations and
+ e.g. base its process migration decisions on it.
+ Default is off.
+
tp720= [HW,PS2]
trix= [HW,OSS] MediaTrix AudioTrix Pro
Format:
<io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
+ tsc= Disable clocksource-must-verify flag for TSC.
+ Format: <string>
+ [x86] reliable: mark tsc clocksource as reliable, this
+ disables clocksource verification at runtime.
+ Used to enable high-resolution timer mode on older
+ hardware, and in virtualized environment.
+
turbografx.map[2|3]= [HW,JOY]
TurboGraFX parallel port interface
Format:
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c
index 804520633fcf..f2dbbf3bdeab 100644
--- a/Documentation/lguest/lguest.c
+++ b/Documentation/lguest/lguest.c
@@ -481,51 +481,6 @@ static unsigned long load_initrd(const char *name, unsigned long mem)
/* We return the initrd size. */
return len;
}
-
-/* Once we know how much memory we have we can construct simple linear page
- * tables which set virtual == physical which will get the Guest far enough
- * into the boot to create its own.
- *
- * We lay them out of the way, just below the initrd (which is why we need to
- * know its size here). */
-static unsigned long setup_pagetables(unsigned long mem,
- unsigned long initrd_size)
-{
- unsigned long *pgdir, *linear;
- unsigned int mapped_pages, i, linear_pages;
- unsigned int ptes_per_page = getpagesize()/sizeof(void *);
-
- mapped_pages = mem/getpagesize();
-
- /* Each PTE page can map ptes_per_page pages: how many do we need? */
- linear_pages = (mapped_pages + ptes_per_page-1)/ptes_per_page;
-
- /* We put the toplevel page directory page at the top of memory. */
- pgdir = from_guest_phys(mem) - initrd_size - getpagesize();
-
- /* Now we use the next linear_pages pages as pte pages */
- linear = (void *)pgdir - linear_pages*getpagesize();
-
- /* Linear mapping is easy: put every page's address into the mapping in
- * order. PAGE_PRESENT contains the flags Present, Writable and
- * Executable. */
- for (i = 0; i < mapped_pages; i++)
- linear[i] = ((i * getpagesize()) | PAGE_PRESENT);
-
- /* The top level points to the linear page table pages above. */
- for (i = 0; i < mapped_pages; i += ptes_per_page) {
- pgdir[i/ptes_per_page]
- = ((to_guest_phys(linear) + i*sizeof(void *))
- | PAGE_PRESENT);
- }
-
- verbose("Linear mapping of %u pages in %u pte pages at %#lx\n",
- mapped_pages, linear_pages, to_guest_phys(linear));
-
- /* We return the top level (guest-physical) address: the kernel needs
- * to know where it is. */
- return to_guest_phys(pgdir);
-}
/*:*/
/* Simple routine to roll all the commandline arguments together with spaces
@@ -548,13 +503,13 @@ static void concat(char *dst, char *args[])
/*L:185 This is where we actually tell the kernel to initialize the Guest. We
* saw the arguments it expects when we looked at initialize() in lguest_user.c:
- * the base of Guest "physical" memory, the top physical page to allow, the
- * top level pagetable and the entry point for the Guest. */
-static int tell_kernel(unsigned long pgdir, unsigned long start)
+ * the base of Guest "physical" memory, the top physical page to allow and the
+ * entry point for the Guest. */
+static int tell_kernel(unsigned long start)
{
unsigned long args[] = { LHREQ_INITIALIZE,
(unsigned long)guest_base,
- guest_limit / getpagesize(), pgdir, start };
+ guest_limit / getpagesize(), start };
int fd;
verbose("Guest: %p - %p (%#lx)\n",
@@ -1030,7 +985,7 @@ static void update_device_status(struct device *dev)
/* Zero out the virtqueues. */
for (vq = dev->vq; vq; vq = vq->next) {
memset(vq->vring.desc, 0,
- vring_size(vq->config.num, getpagesize()));
+ vring_size(vq->config.num, LGUEST_VRING_ALIGN));
lg_last_avail(vq) = 0;
}
} else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
@@ -1211,7 +1166,7 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
void *p;
/* First we need some memory for this virtqueue. */
- pages = (vring_size(num_descs, getpagesize()) + getpagesize() - 1)
+ pages = (vring_size(num_descs, LGUEST_VRING_ALIGN) + getpagesize() - 1)
/ getpagesize();
p = get_pages(pages);
@@ -1228,7 +1183,7 @@ static void add_virtqueue(struct device *dev, unsigned int num_descs,
vq->config.pfn = to_guest_phys(p) / getpagesize();
/* Initialize the vring. */
- vring_init(&vq->vring, num_descs, p, getpagesize());
+ vring_init(&vq->vring, num_descs, p, LGUEST_VRING_ALIGN);
/* Append virtqueue to this device's descriptor. We use
* device_config() to get the end of the device's current virtqueues;
@@ -1941,7 +1896,7 @@ int main(int argc, char *argv[])
{
/* Memory, top-level pagetable, code startpoint and size of the
* (optional) initrd. */
- unsigned long mem = 0, pgdir, start, initrd_size = 0;
+ unsigned long mem = 0, start, initrd_size = 0;
/* Two temporaries and the /dev/lguest file descriptor. */
int i, c, lguest_fd;
/* The boot information for the Guest. */
@@ -2040,9 +1995,6 @@ int main(int argc, char *argv[])
boot->hdr.type_of_loader = 0xFF;
}
- /* Set up the initial linear pagetables, starting below the initrd. */
- pgdir = setup_pagetables(mem, initrd_size);
-
/* The Linux boot header contains an "E820" memory map: ours is a
* simple, single region. */
boot->e820_entries = 1;
@@ -2064,7 +2016,7 @@ int main(int argc, char *argv[])
/* We tell the kernel to initialize the Guest: this returns the open
* /dev/lguest file descriptor. */
- lguest_fd = tell_kernel(pgdir, start);
+ lguest_fd = tell_kernel(start);
/* We clone off a thread, which wakes the Launcher whenever one of the
* input file descriptors needs attention. We call this the Waker, and
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
index 4f2a40f1dbc6..80c728522c4c 100644
--- a/Documentation/networking/README.ipw2200
+++ b/Documentation/networking/README.ipw2200
@@ -147,7 +147,7 @@ Where the supported parameter are:
driver. If disabled, the driver will not attempt to scan
for and associate to a network until it has been configured with
one or more properties for the target network, for example configuring
- the network SSID. Default is 1 (auto-associate)
+ the network SSID. Default is 0 (do not auto-associate)
Example: % modprobe ipw2200 associate=0
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt
index 688dfe1e6b70..5ede7473b425 100644
--- a/Documentation/networking/bonding.txt
+++ b/Documentation/networking/bonding.txt
@@ -194,6 +194,48 @@ or, for backwards compatibility, the option value. E.g.,
The parameters are as follows:
+ad_select
+
+ Specifies the 802.3ad aggregation selection logic to use. The
+ possible values and their effects are:
+
+ stable or 0
+
+ The active aggregator is chosen by largest aggregate
+ bandwidth.
+
+ Reselection of the active aggregator occurs only when all
+ slaves of the active aggregator are down or the active
+ aggregator has no slaves.
+
+ This is the default value.
+
+ bandwidth or 1
+
+ The active aggregator is chosen by largest aggregate
+ bandwidth. Reselection occurs if:
+
+ - A slave is added to or removed from the bond
+
+ - Any slave's link state changes
+
+ - Any slave's 802.3ad association state changes
+
+ - The bond's adminstrative state changes to up
+
+ count or 2
+
+ The active aggregator is chosen by the largest number of
+ ports (slaves). Reselection occurs as described under the
+ "bandwidth" setting, above.
+
+ The bandwidth and count selection policies permit failover of
+ 802.3ad aggregations when partial failure of the active aggregator
+ occurs. This keeps the aggregator with the highest availability
+ (either in bandwidth or in number of ports) active at all times.
+
+ This option was added in bonding version 3.4.0.
+
arp_interval
Specifies the ARP link monitoring frequency in milliseconds.
@@ -551,6 +593,16 @@ num_grat_arp
affects only the active-backup mode. This option was added for
bonding version 3.3.0.
+num_unsol_na
+
+ Specifies the number of unsolicited IPv6 Neighbor Advertisements
+ to be issued after a failover event. One unsolicited NA is issued
+ immediately after the failover.
+
+ The valid range is 0 - 255; the default value is 1. This option
+ affects only the active-backup mode. This option was added for
+ bonding version 3.4.0.
+
primary
A string (eth0, eth2, etc) specifying which slave is the
@@ -922,17 +974,19 @@ USERCTL=no
NETMASK, NETWORK and BROADCAST) to match your network configuration.
For later versions of initscripts, such as that found with Fedora
-7 and Red Hat Enterprise Linux version 5 (or later), it is possible, and,
-indeed, preferable, to specify the bonding options in the ifcfg-bond0
+7 (or later) and Red Hat Enterprise Linux version 5 (or later), it is possible,
+and, indeed, preferable, to specify the bonding options in the ifcfg-bond0
file, e.g. a line of the format:
-BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=+192.168.1.254"
+BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=192.168.1.254"
will configure the bond with the specified options. The options
specified in BONDING_OPTS are identical to the bonding module parameters
-except for the arp_ip_target field. Each target should be included as a
-separate option and should be preceded by a '+' to indicate it should be
-added to the list of queried targets, e.g.,
+except for the arp_ip_target field when using versions of initscripts older
+than and 8.57 (Fedora 8) and 8.45.19 (Red Hat Enterprise Linux 5.2). When
+using older versions each target should be included as a separate option and
+should be preceded by a '+' to indicate it should be added to the list of
+queried targets, e.g.,
arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
@@ -940,7 +994,7 @@ added to the list of queried targets, e.g.,
options via BONDING_OPTS, it is not necessary to edit /etc/modules.conf or
/etc/modprobe.conf.
- For older versions of initscripts that do not support
+ For even older versions of initscripts that do not support
BONDING_OPTS, it is necessary to edit /etc/modules.conf (or
/etc/modprobe.conf, depending upon your distro) to load the bonding module
with your desired options when the bond0 interface is brought up. The
diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt
index 39131a3c78f8..7a3bb1abb830 100644
--- a/Documentation/networking/dccp.txt
+++ b/Documentation/networking/dccp.txt
@@ -57,6 +57,24 @@ can be set before calling bind().
DCCP_SOCKOPT_GET_CUR_MPS is read-only and retrieves the current maximum packet
size (application payload size) in bytes, see RFC 4340, section 14.
+DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs
+supported by the endpoint (see include/linux/dccp.h for symbolic constants).
+The caller needs to provide a sufficiently large (> 2) array of type uint8_t.
+
+DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same
+time, combining the operation of the next two socket options. This option is
+preferrable over the latter two, since often applications will use the same
+type of CCID for both directions; and mixed use of CCIDs is not currently well
+understood. This socket option takes as argument at least one uint8_t value, or
+an array of uint8_t values, which must match available CCIDS (see above). CCIDs
+must be registered on the socket before calling connect() or listen().
+
+DCCP_SOCKOPT_TX_CCID is read/write. It returns the current CCID (if set) or sets
+the preference list for the TX CCID, using the same format as DCCP_SOCKOPT_CCID.
+Please note that the getsockopt argument type here is `int', not uint8_t.
+
+DCCP_SOCKOPT_RX_CCID is analogous to DCCP_SOCKOPT_TX_CCID, but for the RX CCID.
+
DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold
timewait state when closing the connection (RFC 4340, 8.3). The usual case is
that the closing server sends a CloseReq, whereupon the client holds timewait
@@ -115,20 +133,12 @@ retries2
importance for retransmitted acknowledgments and feature negotiation,
data packets are never retransmitted. Analogue of tcp_retries2.
-send_ndp = 1
- Whether or not to send NDP count options (sec. 7.7.2).
-
-send_ackvec = 1
- Whether or not to send Ack Vector options (sec. 11.5).
-
-ack_ratio = 2
- The default Ack Ratio (sec. 11.3) to use.
-
tx_ccid = 2
- Default CCID for the sender-receiver half-connection.
+ Default CCID for the sender-receiver half-connection. Depending on the
+ choice of CCID, the Send Ack Vector feature is enabled automatically.
rx_ccid = 2
- Default CCID for the receiver-sender half-connection.
+ Default CCID for the receiver-sender half-connection; see tx_ccid.
seq_window = 100
The initial sequence window (sec. 7.5.2).
diff --git a/Documentation/networking/driver.txt b/Documentation/networking/driver.txt
index ea72d2e66ca8..03283daa64fe 100644
--- a/Documentation/networking/driver.txt
+++ b/Documentation/networking/driver.txt
@@ -13,7 +13,7 @@ Transmit path guidelines:
static int drv_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- struct drv *dp = dev->priv;
+ struct drv *dp = netdev_priv(dev);
lock_tx(dp);
...
diff --git a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt
index 31bc8b759b75..4eb3cc40b702 100644
--- a/Documentation/networking/generic-hdlc.txt
+++ b/Documentation/networking/generic-hdlc.txt
@@ -3,15 +3,15 @@ Krzysztof Halasa <khc@pm.waw.pl>
Generic HDLC layer currently supports:
-1. Frame Relay (ANSI, CCITT, Cisco and no LMI).
+1. Frame Relay (ANSI, CCITT, Cisco and no LMI)
- Normal (routed) and Ethernet-bridged (Ethernet device emulation)
interfaces can share a single PVC.
- ARP support (no InARP support in the kernel - there is an
experimental InARP user-space daemon available on:
http://www.kernel.org/pub/linux/utils/net/hdlc/).
-2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
-3. Cisco HDLC.
-4. PPP (uses syncppp.c).
+2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation
+3. Cisco HDLC
+4. PPP
5. X.25 (uses X.25 routines).
Generic HDLC is a protocol driver only - it needs a low-level driver
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index d84932650fd3..c7712787933c 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -27,6 +27,12 @@ min_adv_mss - INTEGER
The advertised MSS depends on the first hop route MTU, but will
never be lower than this setting.
+rt_cache_rebuild_count - INTEGER
+ The per net-namespace route cache emergency rebuild threshold.
+ Any net-namespace having its route cache rebuilt due to
+ a hash bucket chain being too long more than this many times
+ will have its route caching disabled
+
IP Fragmentation:
ipfrag_high_thresh - INTEGER
diff --git a/Documentation/networking/mac80211_hwsim/README b/Documentation/networking/mac80211_hwsim/README
index 2ff8ccb8dc37..24ac91d56698 100644
--- a/Documentation/networking/mac80211_hwsim/README
+++ b/Documentation/networking/mac80211_hwsim/README
@@ -50,10 +50,6 @@ associates with the AP. hostapd and wpa_supplicant are used to take
care of WPA2-PSK authentication. In addition, hostapd is also
processing access point side of association.
-Please note that the current Linux kernel does not enable AP mode, so a
-simple patch is needed to enable AP mode selection:
-http://johannes.sipsolutions.net/patches/kernel/all/LATEST/006-allow-ap-vlan-modes.patch
-
# Build mac80211_hwsim as part of kernel configuration
@@ -65,3 +61,8 @@ hostapd hostapd.conf
# Run wpa_supplicant (station) for wlan1
wpa_supplicant -Dwext -iwlan1 -c wpa_supplicant.conf
+
+
+More test cases are available in hostap.git:
+git://w1.fi/srv/git/hostap.git and mac80211_hwsim/tests subdirectory
+(http://w1.fi/gitweb/gitweb.cgi?p=hostap.git;a=tree;f=mac80211_hwsim/tests)
diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt
index d0f71fc7f782..a2ab6a0b116d 100644
--- a/Documentation/networking/netdevices.txt
+++ b/Documentation/networking/netdevices.txt
@@ -18,7 +18,7 @@ There are routines in net_init.c to handle the common cases of
alloc_etherdev, alloc_netdev. These reserve extra space for driver
private data which gets freed when the network device is freed. If
separately allocated data is attached to the network device
-(dev->priv) then it is up to the module exit handler to free that.
+(netdev_priv(dev)) then it is up to the module exit handler to free that.
MTU
===
diff --git a/Documentation/networking/regulatory.txt b/Documentation/networking/regulatory.txt
index a96989a8ff35..dcf31648414a 100644
--- a/Documentation/networking/regulatory.txt
+++ b/Documentation/networking/regulatory.txt
@@ -131,11 +131,13 @@ are expected to do this during initialization.
r = zd_reg2alpha2(mac->regdomain, alpha2);
if (!r)
- regulatory_hint(hw->wiphy, alpha2, NULL);
+ regulatory_hint(hw->wiphy, alpha2);
Example code - drivers providing a built in regulatory domain:
--------------------------------------------------------------
+[NOTE: This API is not currently available, it can be added when required]
+
If you have regulatory information you can obtain from your
driver and you *need* to use this we let you build a regulatory domain
structure and pass it to the wireless core. To do this you should
@@ -167,7 +169,6 @@ struct ieee80211_regdomain mydriver_jp_regdom = {
Then in some part of your code after your wiphy has been registered:
- int r;
struct ieee80211_regdomain *rd;
int size_of_regd;
int num_rules = mydriver_jp_regdom.n_reg_rules;
@@ -178,17 +179,12 @@ Then in some part of your code after your wiphy has been registered:
rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd)
- return -ENOMEM;
+ return -ENOMEM;
memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain));
- for (i=0; i < num_rules; i++) {
- memcpy(&rd->reg_rules[i], &mydriver_jp_regdom.reg_rules[i],
- sizeof(struct ieee80211_reg_rule));
- }
- r = regulatory_hint(hw->wiphy, NULL, rd);
- if (r) {
- kfree(rd);
- return r;
- }
-
+ for (i=0; i < num_rules; i++)
+ memcpy(&rd->reg_rules[i],
+ &mydriver_jp_regdom.reg_rules[i],
+ sizeof(struct ieee80211_reg_rule));
+ regulatory_struct_hint(rd);
diff --git a/Documentation/nmi_watchdog.txt b/Documentation/nmi_watchdog.txt
index 90aa4531cb67..bf9f80a98282 100644
--- a/Documentation/nmi_watchdog.txt
+++ b/Documentation/nmi_watchdog.txt
@@ -69,6 +69,11 @@ to the overall system performance.
On x86 nmi_watchdog is disabled by default so you have to enable it with
a boot time parameter.
+It's possible to disable the NMI watchdog in run-time by writing "0" to
+/proc/sys/kernel/nmi_watchdog. Writing "1" to the same file will re-enable
+the NMI watchdog. Notice that you still need to use "nmi_watchdog=" parameter
+at boot time.
+
NOTE: In kernels prior to 2.4.2-ac18 the NMI-oopser is enabled unconditionally
on x86 SMP boxes.
diff --git a/Documentation/powerpc/dts-bindings/fsl/tsec.txt b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
index cf55fa4112d2..7fa4b27574b5 100644
--- a/Documentation/powerpc/dts-bindings/fsl/tsec.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/tsec.txt
@@ -2,8 +2,8 @@
The MDIO is a bus to which the PHY devices are connected. For each
device that exists on this bus, a child node should be created. See
-the definition of the PHY node below for an example of how to define
-a PHY.
+the definition of the PHY node in booting-without-of.txt for an example
+of how to define a PHY.
Required properties:
- reg : Offset and length of the register set for the device
@@ -21,6 +21,14 @@ Example:
};
};
+* TBI Internal MDIO bus
+
+As of this writing, every tsec is associated with an internal TBI PHY.
+This PHY is accessed through the local MDIO bus. These buses are defined
+similarly to the mdio buses, except they are compatible with "fsl,gianfar-tbi".
+The TBI PHYs underneath them are similar to normal PHYs, but the reg property
+is considered instructive, rather than descriptive. The reg property should
+be chosen so it doesn't interfere with other PHYs on the bus.
* Gianfar-compatible ethernet nodes
diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt
index b65f0799df48..4d3ee317a4a3 100644
--- a/Documentation/rfkill.txt
+++ b/Documentation/rfkill.txt
@@ -191,12 +191,20 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input):
to tell the devices registered with the rfkill class to change
their state (i.e. translates the input layer event into real
action).
+
* rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0
(power off all transmitters) in a special way: it ignores any
overrides and local state cache and forces all transmitters to the
RFKILL_STATE_SOFT_BLOCKED state (including those which are already
- supposed to be BLOCKED). Note that the opposite event (power on all
- transmitters) is handled normally.
+ supposed to be BLOCKED).
+ * rfkill EPO will remain active until rfkill-input receives an
+ EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters
+ are locked in the blocked state (rfkill will refuse to unblock them).
+ * rfkill-input implements different policies that the user can
+ select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill,
+ and either do nothing (leave transmitters blocked, but now unlocked),
+ restore the transmitters to their state before the EPO, or unblock
+ them all.
Userspace uevent handler or kernel platform-specific drivers hooked to the
rfkill notifier chain:
@@ -331,11 +339,9 @@ class to get a sysfs interface :-)
correct event for your switch/button. These events are emergency power-off
events when they are trying to turn the transmitters off. An example of an
input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill
-switch in a laptop which is NOT a hotkey, but a real switch that kills radios
-in hardware, even if the O.S. has gone to lunch. An example of an input device
-which SHOULD NOT generate *_RFKILL_ALL events by default, is any sort of hot
-key that does nothing by itself, as well as any hot key that is type-specific
-(e.g. the one for WLAN).
+switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch.
+An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by
+default, is any sort of hot key that is type-specific (e.g. the one for WLAN).
3.1 Guidelines for wireless device drivers
diff --git a/Documentation/scheduler/sched-design-CFS.txt b/Documentation/scheduler/sched-design-CFS.txt
index eb471c7a905e..8398ca4ff4ed 100644
--- a/Documentation/scheduler/sched-design-CFS.txt
+++ b/Documentation/scheduler/sched-design-CFS.txt
@@ -273,3 +273,24 @@ task groups and modify their CPU share using the "cgroups" pseudo filesystem.
# #Launch gmplayer (or your favourite movie player)
# echo <movie_player_pid> > multimedia/tasks
+
+8. Implementation note: user namespaces
+
+User namespaces are intended to be hierarchical. But they are currently
+only partially implemented. Each of those has ramifications for CFS.
+
+First, since user namespaces are hierarchical, the /sys/kernel/uids
+presentation is inadequate. Eventually we will likely want to use sysfs
+tagging to provide private views of /sys/kernel/uids within each user
+namespace.
+
+Second, the hierarchical nature is intended to support completely
+unprivileged use of user namespaces. So if using user groups, then
+we want the users in a user namespace to be children of the user
+who created it.
+
+That is currently unimplemented. So instead, every user in a new
+user namespace will receive 1024 shares just like any user in the
+initial user namespace. Note that at the moment creation of a new
+user namespace requires each of CAP_SYS_ADMIN, CAP_SETUID, and
+CAP_SETGID.
diff --git a/Documentation/scsi/cxgb3i.txt b/Documentation/scsi/cxgb3i.txt
new file mode 100644
index 000000000000..8141fa01978e
--- /dev/null
+++ b/Documentation/scsi/cxgb3i.txt
@@ -0,0 +1,85 @@
+Chelsio S3 iSCSI Driver for Linux
+
+Introduction
+============
+
+The Chelsio T3 ASIC based Adapters (S310, S320, S302, S304, Mezz cards, etc.
+series of products) supports iSCSI acceleration and iSCSI Direct Data Placement
+(DDP) where the hardware handles the expensive byte touching operations, such
+as CRC computation and verification, and direct DMA to the final host memory
+destination:
+
+ - iSCSI PDU digest generation and verification
+
+ On transmitting, Chelsio S3 h/w computes and inserts the Header and
+ Data digest into the PDUs.
+ On receiving, Chelsio S3 h/w computes and verifies the Header and
+ Data digest of the PDUs.
+
+ - Direct Data Placement (DDP)
+
+ S3 h/w can directly place the iSCSI Data-In or Data-Out PDU's
+ payload into pre-posted final destination host-memory buffers based
+ on the Initiator Task Tag (ITT) in Data-In or Target Task Tag (TTT)
+ in Data-Out PDUs.
+
+ - PDU Transmit and Recovery
+
+ On transmitting, S3 h/w accepts the complete PDU (header + data)
+ from the host driver, computes and inserts the digests, decomposes
+ the PDU into multiple TCP segments if necessary, and transmit all
+ the TCP segments onto the wire. It handles TCP retransmission if
+ needed.
+
+ On receving, S3 h/w recovers the iSCSI PDU by reassembling TCP
+ segments, separating the header and data, calculating and verifying
+ the digests, then forwards the header to the host. The payload data,
+ if possible, will be directly placed into the pre-posted host DDP
+ buffer. Otherwise, the payload data will be sent to the host too.
+
+The cxgb3i driver interfaces with open-iscsi initiator and provides the iSCSI
+acceleration through Chelsio hardware wherever applicable.
+
+Using the cxgb3i Driver
+=======================
+
+The following steps need to be taken to accelerates the open-iscsi initiator:
+
+1. Load the cxgb3i driver: "modprobe cxgb3i"
+
+ The cxgb3i module registers a new transport class "cxgb3i" with open-iscsi.
+
+ * in the case of recompiling the kernel, the cxgb3i selection is located at
+ Device Drivers
+ SCSI device support --->
+ [*] SCSI low-level drivers --->
+ <M> Chelsio S3xx iSCSI support
+
+2. Create an interface file located under /etc/iscsi/ifaces/ for the new
+ transport class "cxgb3i".
+
+ The content of the file should be in the following format:
+ iface.transport_name = cxgb3i
+ iface.net_ifacename = <ethX>
+ iface.ipaddress = <iscsi ip address>
+
+ * if iface.ipaddress is specified, <iscsi ip address> needs to be either the
+ same as the ethX's ip address or an address on the same subnet. Make
+ sure the ip address is unique in the network.
+
+3. edit /etc/iscsi/iscsid.conf
+ The default setting for MaxRecvDataSegmentLength (131072) is too big,
+ replace "node.conn[0].iscsi.MaxRecvDataSegmentLength" to be a value no
+ bigger than 15360 (for example 8192):
+
+ node.conn[0].iscsi.MaxRecvDataSegmentLength = 8192
+
+ * The login would fail for a normal session if MaxRecvDataSegmentLength is
+ too big. A error message in the format of
+ "cxgb3i: ERR! MaxRecvSegmentLength <X> too big. Need to be <= <Y>."
+ would be logged to dmesg.
+
+4. To direct open-iscsi traffic to go through cxgb3i's accelerated path,
+ "-I <iface file name>" option needs to be specified with most of the
+ iscsiadm command. <iface file name> is the transport interface file created
+ in step 2.
diff --git a/Documentation/sh/kgdb.txt b/Documentation/sh/kgdb.txt
deleted file mode 100644
index 05b4ba89d28c..000000000000
--- a/Documentation/sh/kgdb.txt
+++ /dev/null
@@ -1,179 +0,0 @@
-
-This file describes the configuration and behavior of KGDB for the SH
-kernel. Based on a description from Henry Bell <henry.bell@st.com>, it
-has been modified to account for quirks in the current implementation.
-
-Version
-=======
-
-This version of KGDB was written for 2.4.xx kernels for the SH architecture.
-Further documentation is available from the linux-sh project website.
-
-
-Debugging Setup: Host
-======================
-
-The two machines will be connected together via a serial line - this
-should be a null modem cable i.e. with a twist.
-
-On your DEVELOPMENT machine, go to your kernel source directory and
-build the kernel, enabling KGDB support in the "kernel hacking" section.
-This includes the KGDB code, and also makes the kernel be compiled with
-the "-g" option set -- necessary for debugging.
-
-To install this new kernel, use the following installation procedure.
-
-Decide on which tty port you want the machines to communicate, then
-cable them up back-to-back using the null modem. On the DEVELOPMENT
-machine, you may wish to create an initialization file called .gdbinit
-(in the kernel source directory or in your home directory) to execute
-commonly-used commands at startup.
-
-A minimal .gdbinit might look like this:
-
- file vmlinux
- set remotebaud 115200
- target remote /dev/ttyS0
-
-Change the "target" definition so that it specifies the tty port that
-you intend to use. Change the "remotebaud" definition to match the
-data rate that you are going to use for the com line (115200 is the
-default).
-
-Debugging Setup: Target
-========================
-
-By default, the KGDB stub will communicate with the host GDB using
-ttySC1 at 115200 baud, 8 databits, no parity; these defaults can be
-changed in the kernel configuration. As the kernel starts up, KGDB will
-initialize so that breakpoints, kernel segfaults, and so forth will
-generally enter the debugger.
-
-This behavior can be modified by including the "kgdb" option in the
-kernel command line; this option has the general form:
-
- kgdb=<ttyspec>,<action>
-
-The <ttyspec> indicates the port to use, and can optionally specify
-baud, parity and databits -- e.g. "ttySC0,9600N8" or "ttySC1,19200".
-
-The <action> can be "halt" or "disabled". The "halt" action enters the
-debugger via a breakpoint as soon as kgdb is initialized; the "disabled"
-action causes kgdb to ignore kernel segfaults and such until explicitly
-entered by a breakpoint in the code or by external action (sysrq or NMI).
-
-(Both <ttyspec> and <action> can appear alone, w/o the separating comma.)
-
-For example, if you wish to debug early in kernel startup code, you
-might specify the halt option:
-
- kgdb=halt
-
-Boot the TARGET machine, which will appear to hang.
-
-On your DEVELOPMENT machine, cd to the source directory and run the gdb
-program. (This is likely to be a cross GDB which runs on your host but
-is built for an SH target.) If everything is working correctly you
-should see gdb print out a few lines indicating that a breakpoint has
-been taken. It will actually show a line of code in the target kernel
-inside the gdbstub activation code.
-
-NOTE: BE SURE TO TERMINATE OR SUSPEND any other host application which
-may be using the same serial port (for example, a terminal emulator you
-have been using to connect to the target boot code.) Otherwise, data
-from the target may not all get to GDB!
-
-You can now use whatever gdb commands you like to set breakpoints.
-Enter "continue" to start your target machine executing again. At this
-point the target system will run at full speed until it encounters
-your breakpoint or gets a segment violation in the kernel, or whatever.
-
-Serial Ports: KGDB, Console
-============================
-
-This version of KGDB may not gracefully handle conflict with other
-drivers in the kernel using the same port. If KGDB is configured on the
-same port (and with the same parameters) as the kernel console, or if
-CONFIG_SH_KGDB_CONSOLE is configured, things should be fine (though in
-some cases console messages may appear twice through GDB). But if the
-KGDB port is not the kernel console and used by another serial driver
-which assumes different serial parameters (e.g. baud rate) KGDB may not
-recover.
-
-Also, when KGDB is entered via sysrq-g (requires CONFIG_KGDB_SYSRQ) and
-the kgdb port uses the same port as the console, detaching GDB will not
-restore the console to working order without the port being re-opened.
-
-Another serious consequence of this is that GDB currently CANNOT break
-into KGDB externally (e.g. via ^C or <BREAK>); unless a breakpoint or
-error is encountered, the only way to enter KGDB after the initial halt
-(see above) is via NMI (CONFIG_KGDB_NMI) or sysrq-g (CONFIG_KGDB_SYSRQ).
-
-Code is included for the basic Hitachi Solution Engine boards to allow
-the use of ttyS0 for KGDB if desired; this is less robust, but may be
-useful in some cases. (This cannot be selected using the config file,
-but only through the kernel command line, e.g. "kgdb=ttyS0", though the
-configured defaults for baud rate etc. still apply if not overridden.)
-
-If gdbstub Does Not Work
-========================
-
-If it doesn't work, you will have to troubleshoot it. Do the easy
-things first like double checking your cabling and data rates. You
-might try some non-kernel based programs to see if the back-to-back
-connection works properly. Just something simple like cat /etc/hosts
-/dev/ttyS0 on one machine and cat /dev/ttyS0 on the other will tell you
-if you can send data from one machine to the other. There is no point
-in tearing out your hair in the kernel if the line doesn't work.
-
-If you need to debug the GDB/KGDB communication itself, the gdb commands
-"set debug remote 1" and "set debug serial 1" may be useful, but be
-warned: they produce a lot of output.
-
-Threads
-=======
-
-Each process in a target machine is seen as a gdb thread. gdb thread related
-commands (info threads, thread n) can be used. CONFIG_KGDB_THREAD must
-be defined for this to work.
-
-In this version, kgdb reports PID_MAX (32768) as the process ID for the
-idle process (pid 0), since GDB does not accept 0 as an ID.
-
-Detaching (exiting KGDB)
-=========================
-
-There are two ways to resume full-speed target execution: "continue" and
-"detach". With "continue", GDB inserts any specified breakpoints in the
-target code and resumes execution; the target is still in "gdb mode".
-If a breakpoint or other debug event (e.g. NMI) happens, the target
-halts and communicates with GDB again, which is waiting for it.
-
-With "detach", GDB does *not* insert any breakpoints; target execution
-is resumed and GDB stops communicating (does not wait for the target).
-In this case, the target is no longer in "gdb mode" -- for example,
-console messages no longer get sent separately to the KGDB port, or
-encapsulated for GDB. If a debug event (e.g. NMI) occurs, the target
-will re-enter "gdb mode" and will display this fact on the console; you
-must give a new "target remote" command to gdb.
-
-NOTE: TO AVOID LOSSING CONSOLE MESSAGES IN CASE THE KERNEL CONSOLE AND
-KGDB USING THE SAME PORT, THE TARGET WAITS FOR ANY INPUT CHARACTER ON
-THE KGDB PORT AFTER A DETACH COMMAND. For example, after the detach you
-could start a terminal emulator on the same host port and enter a <cr>;
-however, this program must then be terminated or suspended in order to
-use GBD again if KGDB is re-entered.
-
-
-Acknowledgements
-================
-
-This code was mostly generated by Henry Bell <henry.bell@st.com>;
-largely from KGDB by Amit S. Kale <akale@veritas.com> - extracts from
-code by Glenn Engel, Jim Kingdon, David Grothe <dave@gcom.com>, Tigran
-Aivazian <tigran@sco.com>, William Gatliff <bgat@open-widgets.com>, Ben
-Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com> are also
-included.
-
-Jeremy Siegel
-<jsiegel@mvista.com>
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 3cd2ad958176..841a9365d5fd 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -757,6 +757,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
model - force the model name
position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
+ probe_only - Only probing and no codec initialization (default=off);
+ Useful to check the initial codec status for debugging
bdl_pos_adj - Specifies the DMA IRQ timing delay in samples.
Passing -1 will make the driver to choose the appropriate
value based on the controller chip.
@@ -772,325 +774,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
This module supports multiple cards and autoprobe.
+ See Documentation/sound/alsa/HD-Audio.txt for more details about
+ HD-audio driver.
+
Each codec may have a model table for different configurations.
If your machine isn't listed there, the default (usually minimal)
configuration is set up. You can pass "model=<name>" option to
specify a certain model in such a case. There are different
- models depending on the codec chip.
-
- Model name Description
- ---------- -----------
- ALC880
- 3stack 3-jack in back and a headphone out
- 3stack-digout 3-jack in back, a HP out and a SPDIF out
- 5stack 5-jack in back, 2-jack in front
- 5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
- 6stack 6-jack in back, 2-jack in front
- 6stack-digout 6-jack with a SPDIF out
- w810 3-jack
- z71v 3-jack (HP shared SPDIF)
- asus 3-jack (ASUS Mobo)
- asus-w1v ASUS W1V
- asus-dig ASUS with SPDIF out
- asus-dig2 ASUS with SPDIF out (using GPIO2)
- uniwill 3-jack
- fujitsu Fujitsu Laptops (Pi1536)
- F1734 2-jack
- lg LG laptop (m1 express dual)
- lg-lw LG LW20/LW25 laptop
- tcl TCL S700
- clevo Clevo laptops (m520G, m665n)
- medion Medion Rim 2150
- test for testing/debugging purpose, almost all controls can be
- adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
- auto auto-config reading BIOS (default)
-
- ALC260
- hp HP machines
- hp-3013 HP machines (3013-variant)
- hp-dc7600 HP DC7600
- fujitsu Fujitsu S7020
- acer Acer TravelMate
- will Will laptops (PB V7900)
- replacer Replacer 672V
- basic fixed pin assignment (old default model)
- test for testing/debugging purpose, almost all controls can
- adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
- auto auto-config reading BIOS (default)
-
- ALC262
- fujitsu Fujitsu Laptop
- hp-bpc HP xw4400/6400/8400/9400 laptops
- hp-bpc-d7000 HP BPC D7000
- hp-tc-t5735 HP Thin Client T5735
- hp-rp5700 HP RP5700
- benq Benq ED8
- benq-t31 Benq T31
- hippo Hippo (ATI) with jack detection, Sony UX-90s
- hippo_1 Hippo (Benq) with jack detection
- sony-assamd Sony ASSAMD
- toshiba-s06 Toshiba S06
- toshiba-rx1 Toshiba RX1
- ultra Samsung Q1 Ultra Vista model
- lenovo-3000 Lenovo 3000 y410
- nec NEC Versa S9100
- basic fixed pin assignment w/o SPDIF
- auto auto-config reading BIOS (default)
-
- ALC267/268
- quanta-il1 Quanta IL1 mini-notebook
- 3stack 3-stack model
- toshiba Toshiba A205
- acer Acer laptops
- acer-aspire Acer Aspire One
- dell Dell OEM laptops (Vostro 1200)
- zepto Zepto laptops
- test for testing/debugging purpose, almost all controls can
- adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
- auto auto-config reading BIOS (default)
-
- ALC269
- basic Basic preset
- quanta Quanta FL1
- eeepc-p703 ASUS Eeepc P703 P900A
- eeepc-p901 ASUS Eeepc P901 S101
-
- ALC662/663
- 3stack-dig 3-stack (2-channel) with SPDIF
- 3stack-6ch 3-stack (6-channel)
- 3stack-6ch-dig 3-stack (6-channel) with SPDIF
- 6stack-dig 6-stack with SPDIF
- lenovo-101e Lenovo laptop
- eeepc-p701 ASUS Eeepc P701
- eeepc-ep20 ASUS Eeepc EP20
- ecs ECS/Foxconn mobo
- m51va ASUS M51VA
- g71v ASUS G71V
- h13 ASUS H13
- g50v ASUS G50V
- asus-mode1 ASUS
- asus-mode2 ASUS
- asus-mode3 ASUS
- asus-mode4 ASUS
- asus-mode5 ASUS
- asus-mode6 ASUS
- auto auto-config reading BIOS (default)
-
- ALC882/885
- 3stack-dig 3-jack with SPDIF I/O
- 6stack-dig 6-jack digital with SPDIF I/O
- arima Arima W820Di1
- targa Targa T8, MSI-1049 T8
- asus-a7j ASUS A7J
- asus-a7m ASUS A7M
- macpro MacPro support
- mbp3 Macbook Pro rev3
- imac24 iMac 24'' with jack detection
- w2jc ASUS W2JC
- auto auto-config reading BIOS (default)
-
- ALC883/888
- 3stack-dig 3-jack with SPDIF I/O
- 6stack-dig 6-jack digital with SPDIF I/O
- 3stack-6ch 3-jack 6-channel
- 3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
- 6stack-dig-demo 6-jack digital for Intel demo board
- acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
- acer-aspire Acer Aspire 9810
- medion Medion Laptops
- medion-md2 Medion MD2
- targa-dig Targa/MSI
- targa-2ch-dig Targs/MSI with 2-channel
- laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
- lenovo-101e Lenovo 101E
- lenovo-nb0763 Lenovo NB0763
- lenovo-ms7195-dig Lenovo MS7195
- lenovo-sky Lenovo Sky
- haier-w66 Haier W66
- 3stack-hp HP machines with 3stack (Lucknow, Samba boards)
- 6stack-dell Dell machines with 6stack (Inspiron 530)
- mitac Mitac 8252D
- clevo-m720 Clevo M720 laptop series
- fujitsu-pi2515 Fujitsu AMILO Pi2515
- 3stack-6ch-intel Intel DG33* boards
- auto auto-config reading BIOS (default)
-
- ALC861/660
- 3stack 3-jack
- 3stack-dig 3-jack with SPDIF I/O
- 6stack-dig 6-jack with SPDIF I/O
- 3stack-660 3-jack (for ALC660)
- uniwill-m31 Uniwill M31 laptop
- toshiba Toshiba laptop support
- asus Asus laptop support
- asus-laptop ASUS F2/F3 laptops
- auto auto-config reading BIOS (default)
-
- ALC861VD/660VD
- 3stack 3-jack
- 3stack-dig 3-jack with SPDIF OUT
- 6stack-dig 6-jack with SPDIF OUT
- 3stack-660 3-jack (for ALC660VD)
- 3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
- lenovo Lenovo 3000 C200
- dallas Dallas laptops
- hp HP TX1000
- auto auto-config reading BIOS (default)
-
- CMI9880
- minimal 3-jack in back
- min_fp 3-jack in back, 2-jack in front
- full 6-jack in back, 2-jack in front
- full_dig 6-jack in back, 2-jack in front, SPDIF I/O
- allout 5-jack in back, 2-jack in front, SPDIF out
- auto auto-config reading BIOS (default)
-
- AD1882 / AD1882A
- 3stack 3-stack mode (default)
- 6stack 6-stack mode
-
- AD1884A / AD1883 / AD1984A / AD1984B
- desktop 3-stack desktop (default)
- laptop laptop with HP jack sensing
- mobile mobile devices with HP jack sensing
- thinkpad Lenovo Thinkpad X300
-
- AD1884
- N/A
-
- AD1981
- basic 3-jack (default)
- hp HP nx6320
- thinkpad Lenovo Thinkpad T60/X60/Z60
- toshiba Toshiba U205
-
- AD1983
- N/A
-
- AD1984
- basic default configuration
- thinkpad Lenovo Thinkpad T61/X61
- dell Dell T3400
-
- AD1986A
- 6stack 6-jack, separate surrounds (default)
- 3stack 3-stack, shared surrounds
- laptop 2-channel only (FSC V2060, Samsung M50)
- laptop-eapd 2-channel with EAPD (Samsung R65, ASUS A6J)
- laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
- ultra 2-channel with EAPD (Samsung Ultra tablet PC)
-
- AD1988/AD1988B/AD1989A/AD1989B
- 6stack 6-jack
- 6stack-dig ditto with SPDIF
- 3stack 3-jack
- 3stack-dig ditto with SPDIF
- laptop 3-jack with hp-jack automute
- laptop-dig ditto with SPDIF
- auto auto-config reading BIOS (default)
-
- Conexant 5045
- laptop-hpsense Laptop with HP sense (old model laptop)
- laptop-micsense Laptop with Mic sense (old model fujitsu)
- laptop-hpmicsense Laptop with HP and Mic senses
- benq Benq R55E
- test for testing/debugging purpose, almost all controls
- can be adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
-
- Conexant 5047
- laptop Basic Laptop config
- laptop-hp Laptop config for some HP models (subdevice 30A5)
- laptop-eapd Laptop config with EAPD support
- test for testing/debugging purpose, almost all controls
- can be adjusted. Appearing only when compiled with
- $CONFIG_SND_DEBUG=y
-
- Conexant 5051
- laptop Basic Laptop config (default)
- hp HP Spartan laptop
-
- STAC9200
- ref Reference board
- dell-d21 Dell (unknown)
- dell-d22 Dell (unknown)
- dell-d23 Dell (unknown)
- dell-m21 Dell Inspiron 630m, Dell Inspiron 640m
- dell-m22 Dell Latitude D620, Dell Latitude D820
- dell-m23 Dell XPS M1710, Dell Precision M90
- dell-m24 Dell Latitude 120L
- dell-m25 Dell Inspiron E1505n
- dell-m26 Dell Inspiron 1501
- dell-m27 Dell Inspiron E1705/9400
- gateway Gateway laptops with EAPD control
- panasonic Panasonic CF-74
-
- STAC9205/9254
- ref Reference board
- dell-m42 Dell (unknown)
- dell-m43 Dell Precision
- dell-m44 Dell Inspiron
-
- STAC9220/9221
- ref Reference board
- 3stack D945 3stack
- 5stack D945 5stack + SPDIF
- intel-mac-v1 Intel Mac Type 1
- intel-mac-v2 Intel Mac Type 2
- intel-mac-v3 Intel Mac Type 3
- intel-mac-v4 Intel Mac Type 4
- intel-mac-v5 Intel Mac Type 5
- intel-mac-auto Intel Mac (detect type according to subsystem id)
- macmini Intel Mac Mini (equivalent with type 3)
- macbook Intel Mac Book (eq. type 5)
- macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
- macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
- imac-intel Intel iMac (eq. type 2)
- imac-intel-20 Intel iMac (newer version) (eq. type 3)
- dell-d81 Dell (unknown)
- dell-d82 Dell (unknown)
- dell-m81 Dell (unknown)
- dell-m82 Dell XPS M1210
-
- STAC9202/9250/9251
- ref Reference board, base config
- m2-2 Some Gateway MX series laptops
- m6 Some Gateway NX series laptops
- pa6 Gateway NX860 series
-
- STAC9227/9228/9229/927x
- ref Reference board
- 3stack D965 3stack
- 5stack D965 5stack + SPDIF
- dell-3stack Dell Dimension E520
- dell-bios Fixes with Dell BIOS setup
-
- STAC92HD71B*
- ref Reference board
- dell-m4-1 Dell desktops
- dell-m4-2 Dell desktops
- dell-m4-3 Dell desktops
-
- STAC92HD73*
- ref Reference board
- dell-m6-amic Dell desktops/laptops with analog mics
- dell-m6-dmic Dell desktops/laptops with digital mics
- dell-m6 Dell desktops/laptops with both type of mics
-
- STAC9872
- vaio Setup for VAIO FE550G/SZ110
- vaio-ar Setup for VAIO AR
+ models depending on the codec chip. The list of available models
+ is found in HD-Audio-Models.txt
The model name "genric" is treated as a special case. When this
model is given, the driver uses the generic codec parser without
"codec-patch". It's sometimes good for testing and debugging.
If the default configuration doesn't work and one of the above
- matches with your device, report it together with the PCI
- subsystem ID (output of "lspci -nv") to ALSA BTS or alsa-devel
+ matches with your device, report it together with alsa-info.sh
+ output (with --no-upload option) to kernel bugzilla or alsa-devel
ML (see the section "Links and Addresses").
power_save and power_save_controller options are for power-saving
@@ -1650,7 +1350,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
* AuzenTech X-Meridian
* Bgears b-Enspirer
* Club3D Theatron DTS
- * HT-Omega Claro
+ * HT-Omega Claro (plus)
+ * HT-Omega Claro halo (XT)
* Razer Barracuda AC-1
* Sondigo Inferno
@@ -2407,8 +2108,11 @@ Links and Addresses
ALSA project homepage
http://www.alsa-project.org
- ALSA Bug Tracking System
- https://bugtrack.alsa-project.org/bugs/
+ Kernel Bugzilla
+ http://bugzilla.kernel.org/
ALSA Developers ML
mailto:alsa-devel@alsa-project.org
+
+ alsa-info.sh script
+ http://www.alsa-project.org/alsa-info.sh
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
new file mode 100644
index 000000000000..4b7ac21ea9eb
--- /dev/null
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -0,0 +1,348 @@
+ Model name Description
+ ---------- -----------
+ALC880
+======
+ 3stack 3-jack in back and a headphone out
+ 3stack-digout 3-jack in back, a HP out and a SPDIF out
+ 5stack 5-jack in back, 2-jack in front
+ 5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
+ 6stack 6-jack in back, 2-jack in front
+ 6stack-digout 6-jack with a SPDIF out
+ w810 3-jack
+ z71v 3-jack (HP shared SPDIF)
+ asus 3-jack (ASUS Mobo)
+ asus-w1v ASUS W1V
+ asus-dig ASUS with SPDIF out
+ asus-dig2 ASUS with SPDIF out (using GPIO2)
+ uniwill 3-jack
+ fujitsu Fujitsu Laptops (Pi1536)
+ F1734 2-jack
+ lg LG laptop (m1 express dual)
+ lg-lw LG LW20/LW25 laptop
+ tcl TCL S700
+ clevo Clevo laptops (m520G, m665n)
+ medion Medion Rim 2150
+ test for testing/debugging purpose, almost all controls can be
+ adjusted. Appearing only when compiled with
+ $CONFIG_SND_DEBUG=y
+ auto auto-config reading BIOS (default)
+
+ALC260
+======
+ hp HP machines
+ hp-3013 HP machines (3013-variant)
+ hp-dc7600 HP DC7600
+ fujitsu Fujitsu S7020
+ acer Acer TravelMate
+ will Will laptops (PB V7900)
+ replacer Replacer 672V
+ basic fixed pin assignment (old default model)
+ test for testing/debugging purpose, almost all controls can
+ adjusted. Appearing only when compiled with
+ $CONFIG_SND_DEBUG=y
+ auto auto-config reading BIOS (default)
+
+ALC262
+======
+ fujitsu Fujitsu Laptop
+ hp-bpc HP xw4400/6400/8400/9400 laptops
+ hp-bpc-d7000 HP BPC D7000
+ hp-tc-t5735 HP Thin Client T5735
+ hp-rp5700 HP RP5700
+ benq Benq ED8
+ benq-t31 Benq T31
+ hippo Hippo (ATI) with jack detection, Sony UX-90s
+ hippo_1 Hippo (Benq) with jack detection
+ sony-assamd Sony ASSAMD
+ toshiba-s06 Toshiba S06
+ toshiba-rx1 Toshiba RX1
+ ultra Samsung Q1 Ultra Vista model
+ lenovo-3000 Lenovo 3000 y410
+ nec NEC Versa S9100
+ basic fixed pin assignment w/o SPDIF
+ auto auto-config reading BIOS (default)
+
+ALC267/268
+==========
+ quanta-il1 Quanta IL1 mini-notebook
+ 3stack 3-stack model
+ toshiba Toshiba A205
+ acer Acer laptops
+ acer-dmic Acer laptops with digital-mic
+ acer-aspire Acer Aspire One
+ dell Dell OEM laptops (Vostro 1200)
+ zepto Zepto laptops
+ test for testing/debugging purpose, almost all controls can
+ adjusted. Appearing only when compiled with
+ $CONFIG_SND_DEBUG=y
+ auto auto-config reading BIOS (default)
+
+ALC269
+======
+ basic Basic preset
+ quanta Quanta FL1
+ eeepc-p703 ASUS Eeepc P703 P900A
+ eeepc-p901 ASUS Eeepc P901 S101
+ fujitsu FSC Amilo
+ auto auto-config reading BIOS (default)
+
+ALC662/663
+==========
+ 3stack-dig 3-stack (2-channel) with SPDIF
+ 3stack-6ch 3-stack (6-channel)
+ 3stack-6ch-dig 3-stack (6-channel) with SPDIF
+ 6stack-dig 6-stack with SPDIF
+ lenovo-101e Lenovo laptop
+ eeepc-p701 ASUS Eeepc P701
+ eeepc-ep20 ASUS Eeepc EP20
+ ecs ECS/Foxconn mobo
+ m51va ASUS M51VA
+ g71v ASUS G71V
+ h13 ASUS H13
+ g50v ASUS G50V
+ asus-mode1 ASUS
+ asus-mode2 ASUS
+ asus-mode3 ASUS
+ asus-mode4 ASUS
+ asus-mode5 ASUS
+ asus-mode6 ASUS
+ auto auto-config reading BIOS (default)
+
+ALC882/885
+==========
+ 3stack-dig 3-jack with SPDIF I/O
+ 6stack-dig 6-jack digital with SPDIF I/O
+ arima Arima W820Di1
+ targa Targa T8, MSI-1049 T8
+ asus-a7j ASUS A7J
+ asus-a7m ASUS A7M
+ macpro MacPro support
+ mbp3 Macbook Pro rev3
+ imac24 iMac 24'' with jack detection
+ w2jc ASUS W2JC
+ auto auto-config reading BIOS (default)
+
+ALC883/888
+==========
+ 3stack-dig 3-jack with SPDIF I/O
+ 6stack-dig 6-jack digital with SPDIF I/O
+ 3stack-6ch 3-jack 6-channel
+ 3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
+ 6stack-dig-demo 6-jack digital for Intel demo board
+ acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
+ acer-aspire Acer Aspire 9810
+ acer-aspire-4930g Acer Aspire 4930G
+ medion Medion Laptops
+ medion-md2 Medion MD2
+ targa-dig Targa/MSI
+ targa-2ch-dig Targs/MSI with 2-channel
+ laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
+ lenovo-101e Lenovo 101E
+ lenovo-nb0763 Lenovo NB0763
+ lenovo-ms7195-dig Lenovo MS7195
+ lenovo-sky Lenovo Sky
+ haier-w66 Haier W66
+ 3stack-hp HP machines with 3stack (Lucknow, Samba boards)
+ 6stack-dell Dell machines with 6stack (Inspiron 530)
+ mitac Mitac 8252D
+ clevo-m720 Clevo M720 laptop series
+ fujitsu-pi2515 Fujitsu AMILO Pi2515
+ fujitsu-xa3530 Fujitsu AMILO XA3530
+ 3stack-6ch-intel Intel DG33* boards
+ auto auto-config reading BIOS (default)
+
+ALC861/660
+==========
+ 3stack 3-jack
+ 3stack-dig 3-jack with SPDIF I/O
+ 6stack-dig 6-jack with SPDIF I/O
+ 3stack-660 3-jack (for ALC660)
+ uniwill-m31 Uniwill M31 laptop
+ toshiba Toshiba laptop support
+ asus Asus laptop support
+ asus-laptop ASUS F2/F3 laptops
+ auto auto-config reading BIOS (default)
+
+ALC861VD/660VD
+==============
+ 3stack 3-jack
+ 3stack-dig 3-jack with SPDIF OUT
+ 6stack-dig 6-jack with SPDIF OUT
+ 3stack-660 3-jack (for ALC660VD)
+ 3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
+ lenovo Lenovo 3000 C200
+ dallas Dallas laptops
+ hp HP TX1000
+ asus-v1s ASUS V1Sn
+ auto auto-config reading BIOS (default)
+
+CMI9880
+=======
+ minimal 3-jack in back
+ min_fp 3-jack in back, 2-jack in front
+ full 6-jack in back, 2-jack in front
+ full_dig 6-jack in back, 2-jack in front, SPDIF I/O
+ allout 5-jack in back, 2-jack in front, SPDIF out
+ auto auto-config reading BIOS (default)
+
+AD1882 / AD1882A
+================
+ 3stack 3-stack mode (default)
+ 6stack 6-stack mode
+
+AD1884A / AD1883 / AD1984A / AD1984B
+====================================
+ desktop 3-stack desktop (default)
+ laptop laptop with HP jack sensing
+ mobile mobile devices with HP jack sensing
+ thinkpad Lenovo Thinkpad X300
+
+AD1884
+======
+ N/A
+
+AD1981
+======
+ basic 3-jack (default)
+ hp HP nx6320
+ thinkpad Lenovo Thinkpad T60/X60/Z60
+ toshiba Toshiba U205
+
+AD1983
+======
+ N/A
+
+AD1984
+======
+ basic default configuration
+ thinkpad Lenovo Thinkpad T61/X61
+ dell Dell T3400
+
+AD1986A
+=======
+ 6stack 6-jack, separate surrounds (default)
+ 3stack 3-stack, shared surrounds
+ laptop 2-channel only (FSC V2060, Samsung M50)
+ laptop-eapd 2-channel with EAPD (ASUS A6J)
+ laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
+ ultra 2-channel with EAPD (Samsung Ultra tablet PC)
+ samsung 2-channel with EAPD (Samsung R65)
+
+AD1988/AD1988B/AD1989A/AD1989B
+==============================
+ 6stack 6-jack
+ 6stack-dig ditto with SPDIF
+ 3stack 3-jack
+ 3stack-dig ditto with SPDIF
+ laptop 3-jack with hp-jack automute
+ laptop-dig ditto with SPDIF
+ auto auto-config reading BIOS (default)
+
+Conexant 5045
+=============
+ laptop-hpsense Laptop with HP sense (old model laptop)
+ laptop-micsense Laptop with Mic sense (old model fujitsu)
+ laptop-hpmicsense Laptop with HP and Mic senses
+ benq Benq R55E
+ test for testing/debugging purpose, almost all controls
+ can be adjusted. Appearing only when compiled with
+ $CONFIG_SND_DEBUG=y
+
+Conexant 5047
+=============
+ laptop Basic Laptop config
+ laptop-hp Laptop config for some HP models (subdevice 30A5)
+ laptop-eapd Laptop config with EAPD support
+ test for testing/debugging purpose, almost all controls
+ can be adjusted. Appearing only when compiled with
+ $CONFIG_SND_DEBUG=y
+
+Conexant 5051
+=============
+ laptop Basic Laptop config (default)
+ hp HP Spartan laptop
+
+STAC9200
+========
+ ref Reference board
+ dell-d21 Dell (unknown)
+ dell-d22 Dell (unknown)
+ dell-d23 Dell (unknown)
+ dell-m21 Dell Inspiron 630m, Dell Inspiron 640m
+ dell-m22 Dell Latitude D620, Dell Latitude D820
+ dell-m23 Dell XPS M1710, Dell Precision M90
+ dell-m24 Dell Latitude 120L
+ dell-m25 Dell Inspiron E1505n
+ dell-m26 Dell Inspiron 1501
+ dell-m27 Dell Inspiron E1705/9400
+ gateway Gateway laptops with EAPD control
+ panasonic Panasonic CF-74
+
+STAC9205/9254
+=============
+ ref Reference board
+ dell-m42 Dell (unknown)
+ dell-m43 Dell Precision
+ dell-m44 Dell Inspiron
+
+STAC9220/9221
+=============
+ ref Reference board
+ 3stack D945 3stack
+ 5stack D945 5stack + SPDIF
+ intel-mac-v1 Intel Mac Type 1
+ intel-mac-v2 Intel Mac Type 2
+ intel-mac-v3 Intel Mac Type 3
+ intel-mac-v4 Intel Mac Type 4
+ intel-mac-v5 Intel Mac Type 5
+ intel-mac-auto Intel Mac (detect type according to subsystem id)
+ macmini Intel Mac Mini (equivalent with type 3)
+ macbook Intel Mac Book (eq. type 5)
+ macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
+ macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
+ imac-intel Intel iMac (eq. type 2)
+ imac-intel-20 Intel iMac (newer version) (eq. type 3)
+ dell-d81 Dell (unknown)
+ dell-d82 Dell (unknown)
+ dell-m81 Dell (unknown)
+ dell-m82 Dell XPS M1210
+
+STAC9202/9250/9251
+==================
+ ref Reference board, base config
+ m2-2 Some Gateway MX series laptops
+ m6 Some Gateway NX series laptops
+ pa6 Gateway NX860 series
+
+STAC9227/9228/9229/927x
+=======================
+ ref Reference board
+ ref-no-jd Reference board without HP/Mic jack detection
+ 3stack D965 3stack
+ 5stack D965 5stack + SPDIF
+ dell-3stack Dell Dimension E520
+ dell-bios Fixes with Dell BIOS setup
+
+STAC92HD71B*
+============
+ ref Reference board
+ dell-m4-1 Dell desktops
+ dell-m4-2 Dell desktops
+ dell-m4-3 Dell desktops
+
+STAC92HD73*
+===========
+ ref Reference board
+ no-jd BIOS setup but without jack-detection
+ dell-m6-amic Dell desktops/laptops with analog mics
+ dell-m6-dmic Dell desktops/laptops with digital mics
+ dell-m6 Dell desktops/laptops with both type of mics
+
+STAC92HD83*
+===========
+ ref Reference board
+
+STAC9872
+========
+ vaio Setup for VAIO FE550G/SZ110
+ vaio-ar Setup for VAIO AR
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
new file mode 100644
index 000000000000..8d68fff71839
--- /dev/null
+++ b/Documentation/sound/alsa/HD-Audio.txt
@@ -0,0 +1,577 @@
+MORE NOTES ON HD-AUDIO DRIVER
+=============================
+ Takashi Iwai <tiwai@suse.de>
+
+
+GENERAL
+-------
+
+HD-audio is the new standard on-board audio component on modern PCs
+after AC97. Although Linux has been supporting HD-audio since long
+time ago, there are often problems with new machines. A part of the
+problem is broken BIOS, and the rest is the driver implementation.
+This document explains the brief trouble-shooting and debugging
+methods for the HD-audio hardware.
+
+The HD-audio component consists of two parts: the controller chip and
+the codec chips on the HD-audio bus. Linux provides a single driver
+for all controllers, snd-hda-intel. Although the driver name contains
+a word of a well-known harware vendor, it's not specific to it but for
+all controller chips by other companies. Since the HD-audio
+controllers are supposed to be compatible, the single snd-hda-driver
+should work in most cases. But, not surprisingly, there are known
+bugs and issues specific to each controller type. The snd-hda-intel
+driver has a bunch of workarounds for these as described below.
+
+A controller may have multiple codecs. Usually you have one audio
+codec and optionally one modem codec. In theory, there might be
+multiple audio codecs, e.g. for analog and digital outputs, and the
+driver might not work properly because of conflict of mixer elements.
+This should be fixed in future if such hardware really exists.
+
+The snd-hda-intel driver has several different codec parsers depending
+on the codec. It has a generic parser as a fallback, but this
+functionality is fairly limited until now. Instead of the generic
+parser, usually the codec-specific parser (coded in patch_*.c) is used
+for the codec-specific implementations. The details about the
+codec-specific problems are explained in the later sections.
+
+If you are interested in the deep debugging of HD-audio, read the
+HD-audio specification at first. The specification is found on
+Intel's web page, for example:
+
+- http://www.intel.com/standards/hdaudio/
+
+
+HD-AUDIO CONTROLLER
+-------------------
+
+DMA-Position Problem
+~~~~~~~~~~~~~~~~~~~~
+The most common problem of the controller is the inaccurate DMA
+pointer reporting. The DMA pointer for playback and capture can be
+read in two ways, either via a LPIB register or via a position-buffer
+map. As default the driver tries to read from the io-mapped
+position-buffer, and falls back to LPIB if the position-buffer appears
+dead. However, this detection isn't perfect on some devices. In such
+a case, you can change the default method via `position_fix` option.
+
+`position_fix=1` means to use LPIB method explicitly.
+`position_fix=2` means to use the position-buffer. 0 is the default
+value, the automatic check and fallback to LPIB as described in the
+above. If you get a problem of repeated sounds, this option might
+help.
+
+In addition to that, every controller is known to be broken regarding
+the wake-up timing. It wakes up a few samples before actually
+processing the data on the buffer. This caused a lot of problems, for
+example, with ALSA dmix or JACK. Since 2.6.27 kernel, the driver puts
+an artificial delay to the wake up timing. This delay is controlled
+via `bdl_pos_adj` option.
+
+When `bdl_pos_adj` is a negative value (as default), it's assigned to
+an appropriate value depending on the controller chip. For Intel
+chips, it'd be 1 while it'd be 32 for others. Usually this works.
+Only in case it doesn't work and you get warning messages, you should
+change this parameter to other values.
+
+
+Codec-Probing Problem
+~~~~~~~~~~~~~~~~~~~~~
+A less often but a more severe problem is the codec probing. When
+BIOS reports the available codec slots wrongly, the driver gets
+confused and tries to access the non-existing codec slot. This often
+results in the total screw-up, and destructs the further communication
+with the codec chips. The symptom appears usually as error messages
+like:
+------------------------------------------------------------------------
+ hda_intel: azx_get_response timeout, switching to polling mode:
+ last cmd=0x12345678
+ hda_intel: azx_get_response timeout, switching to single_cmd mode:
+ last cmd=0x12345678
+------------------------------------------------------------------------
+
+The first line is a warning, and this is usually relatively harmless.
+It means that the codec response isn't notified via an IRQ. The
+driver uses explicit polling method to read the response. It gives
+very slight CPU overhead, but you'd unlikely notice it.
+
+The second line is, however, a fatal error. If this happens, usually
+it means that something is really wrong. Most likely you are
+accessing a non-existing codec slot.
+
+Thus, if the second error message appears, try to narrow the probed
+codec slots via `probe_mask` option. It's a bitmask, and each bit
+corresponds to the codec slot. For example, to probe only the first
+slot, pass `probe_mask=1`. For the first and the third slots, pass
+`probe_mask=5` (where 5 = 1 | 4), and so on.
+
+Since 2.6.29 kernel, the driver has a more robust probing method, so
+this error might happen rarely, though.
+
+
+Interrupt Handling
+~~~~~~~~~~~~~~~~~~
+In rare but some cases, the interrupt isn't properly handled as
+default. You would notice this by the DMA transfer error reported by
+ALSA PCM core, for example. Using MSI might help in such a case.
+Pass `enable_msi=1` option for enabling MSI.
+
+
+HD-AUDIO CODEC
+--------------
+
+Model Option
+~~~~~~~~~~~~
+The most common problem regarding the HD-audio driver is the
+unsupported codec features or the mismatched device configuration.
+Most of codec-specific code has several preset models, either to
+override the BIOS setup or to provide more comprehensive features.
+
+The driver checks PCI SSID and looks through the static configuration
+table until any matching entry is found. If you have a new machine,
+you may see a message like below:
+------------------------------------------------------------------------
+ hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
+------------------------------------------------------------------------
+Even if you see such a message, DON'T PANIC. Take a deep breath and
+keep your towel. First of all, it's an informational message, no
+warning, no error. This means that the PCI SSID of your device isn't
+listed in the known preset model (white-)list. But, this doesn't mean
+that the driver is broken. Many codec-drivers provide the automatic
+configuration mechanism based on the BIOS setup.
+
+The HD-audio codec has usually "pin" widgets, and BIOS sets the default
+configuration of each pin, which indicates the location, the
+connection type, the jack color, etc. The HD-audio driver can guess
+the right connection judging from these default configuration values.
+However -- some codec-support codes, such as patch_analog.c, don't
+support the automatic probing (yet as of 2.6.28). And, BIOS is often,
+yes, pretty often broken. It sets up wrong values and screws up the
+driver.
+
+The preset model is provided basically to overcome such a situation.
+When the matching preset model is found in the white-list, the driver
+assumes the static configuration of that preset and builds the mixer
+elements and PCM streams based on the static information. Thus, if
+you have a newer machine with a slightly different PCI SSID from the
+existing one, you may have a good chance to re-use the same model.
+You can pass the `model` option to specify the preset model instead of
+PCI SSID look-up.
+
+What `model` option values are available depends on the codec chip.
+Check your codec chip from the codec proc file (see "Codec Proc-File"
+section below). It will show the vendor/product name of your codec
+chip. Then, see Documentation/sound/alsa/HD-Audio-Modelstxt file,
+the section of HD-audio driver. You can find a list of codecs
+and `model` options belonging to each codec. For example, for Realtek
+ALC262 codec chip, pass `model=ultra` for devices that are compatible
+with Samsung Q1 Ultra.
+
+Thus, the first thing you can do for any brand-new, unsupported and
+non-working HD-audio hardware is to check HD-audio codec and several
+different `model` option values. If you have a luck, some of them
+might suit with your device well.
+
+Some codecs such as ALC880 have a special model option `model=test`.
+This configures the driver to provide as many mixer controls as
+possible for every single pin feature except for the unsolicited
+events (and maybe some other specials). Adjust each mixer element and
+try the I/O in the way of trial-and-error until figuring out the whole
+I/O pin mappings.
+
+Note that `model=generic` has a special meaning. It means to use the
+generic parser regardless of the codec. Usually the codec-specific
+parser is much better than the generic parser (as now). Thus this
+option is more about the debugging purpose.
+
+
+Speaker and Headphone Output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+One of the most frequent (and obvious) bugs with HD-audio is the
+silent output from either or both of a built-in speaker and a
+headphone jack. In general, you should try a headphone output at
+first. A speaker output often requires more additional controls like
+the external amplifier bits. Thus a headphone output has a slightly
+better chance.
+
+Before making a bug report, double-check whether the mixer is set up
+correctly. The recent version of snd-hda-intel driver provides mostly
+"Master" volume control as well as "Front" volume (where Front
+indicates the front-channels). In addition, there can be individual
+"Headphone" and "Speaker" controls.
+
+Ditto for the speaker output. There can be "External Amplifier"
+switch on some codecs. Turn on this if present.
+
+Another related problem is the automatic mute of speaker output by
+headphone plugging. This feature is implemented in most cases, but
+not on every preset model or codec-support code.
+
+In anyway, try a different model option if you have such a problem.
+Some other models may match better and give you more matching
+functionality. If none of the available models works, send a bug
+report. See the bug report section for details.
+
+If you are masochistic enough to debug the driver problem, note the
+following:
+
+- The speaker (and the headphone, too) output often requires the
+ external amplifier. This can be set usually via EAPD verb or a
+ certain GPIO. If the codec pin supports EAPD, you have a better
+ chance via SET_EAPD_BTL verb (0x70c). On others, GPIO pin (mostly
+ it's either GPIO0 or GPIO1) may turn on/off EAPD.
+- Some Realtek codecs require special vendor-specific coefficients to
+ turn on the amplifier. See patch_realtek.c.
+- IDT codecs may have extra power-enable/disable controls on each
+ analog pin. See patch_sigmatel.c.
+- Very rare but some devices don't accept the pin-detection verb until
+ triggered. Issuing GET_PIN_SENSE verb (0xf09) may result in the
+ codec-communication stall. Some examples are found in
+ patch_realtek.c.
+
+
+Capture Problems
+~~~~~~~~~~~~~~~~
+The capture problems are often because of missing setups of mixers.
+Thus, before submitting a bug report, make sure that you set up the
+mixer correctly. For example, both "Capture Volume" and "Capture
+Switch" have to be set properly in addition to the right "Capture
+Source" or "Input Source" selection. Some devices have "Mic Boost"
+volume or switch.
+
+When the PCM device is opened via "default" PCM (without pulse-audio
+plugin), you'll likely have "Digital Capture Volume" control as well.
+This is provided for the extra gain/attenuation of the signal in
+software, especially for the inputs without the hardware volume
+control such as digital microphones. Unless really needed, this
+should be set to exactly 50%, corresponding to 0dB -- neither extra
+gain nor attenuation. When you use "hw" PCM, i.e., a raw access PCM,
+this control will have no influence, though.
+
+It's known that some codecs / devices have fairly bad analog circuits,
+and the recorded sound contains a certain DC-offset. This is no bug
+of the driver.
+
+Most of modern laptops have no analog CD-input connection. Thus, the
+recording from CD input won't work in many cases although the driver
+provides it as the capture source. Use CDDA instead.
+
+The automatic switching of the built-in and external mic per plugging
+is implemented on some codec models but not on every model. Partly
+because of my laziness but mostly lack of testers. Feel free to
+submit the improvement patch to the author.
+
+
+Direct Debugging
+~~~~~~~~~~~~~~~~
+If no model option gives you a better result, and you are a tough guy
+to fight against evil, try debugging via hitting the raw HD-audio
+codec verbs to the device. Some tools are available: hda-emu and
+hda-analyzer. The detailed description is found in the sections
+below. You'd need to enable hwdep for using these tools. See "Kernel
+Configuration" section.
+
+
+OTHER ISSUES
+------------
+
+Kernel Configuration
+~~~~~~~~~~~~~~~~~~~~
+In general, I recommend you to enable the sound debug option,
+`CONFIG_SND_DEBUG=y`, no matter whether you are debugging or not.
+This enables snd_printd() macro and others, and you'll get additional
+kernel messages at probing.
+
+In addition, you can enable `CONFIG_SND_DEBUG_VERBOSE=y`. But this
+will give you far more messages. Thus turn this on only when you are
+sure to want it.
+
+Don't forget to turn on the appropriate `CONFIG_SND_HDA_CODEC_*`
+options. Note that each of them corresponds to the codec chip, not
+the controller chip. Thus, even if lspci shows the Nvidia controller,
+you may need to choose the option for other vendors. If you are
+unsure, just select all yes.
+
+`CONFIG_SND_HDA_HWDEP` is a useful option for debugging the driver.
+When this is enabled, the driver creates hardware-dependent devices
+(one per each codec), and you have a raw access to the device via
+these device files. For example, `hwC0D2` will be created for the
+codec slot #2 of the first card (#0). For debug-tools such as
+hda-verb and hda-analyzer, the hwdep device has to be enabled.
+Thus, it'd be better to turn this on always.
+
+`CONFIG_SND_HDA_RECONFIG` is a new option, and this depends on the
+hwdep option above. When enabled, you'll have some sysfs files under
+the corresponding hwdep directory. See "HD-audio reconfiguration"
+section below.
+
+`CONFIG_SND_HDA_POWER_SAVE` option enables the power-saving feature.
+See "Power-saving" section below.
+
+
+Codec Proc-File
+~~~~~~~~~~~~~~~
+The codec proc-file is a treasure-chest for debugging HD-audio.
+It shows most of useful information of each codec widget.
+
+The proc file is located in /proc/asound/card*/codec#*, one file per
+each codec slot. You can know the codec vendor, product id and
+names, the type of each widget, capabilities and so on.
+This file, however, doesn't show the jack sensing state, so far. This
+is because the jack-sensing might be depending on the trigger state.
+
+This file will be picked up by the debug tools, and also it can be fed
+to the emulator as the primary codec information. See the debug tools
+section below.
+
+This proc file can be also used to check whether the generic parser is
+used. When the generic parser is used, the vendor/product ID name
+will appear as "Realtek ID 0262", instead of "Realtek ALC262".
+
+
+HD-Audio Reconfiguration
+~~~~~~~~~~~~~~~~~~~~~~~~
+This is an experimental feature to allow you re-configure the HD-audio
+codec dynamically without reloading the driver. The following sysfs
+files are available under each codec-hwdep device directory (e.g.
+/sys/class/sound/hwC0D0):
+
+vendor_id::
+ Shows the 32bit codec vendor-id hex number. You can change the
+ vendor-id value by writing to this file.
+subsystem_id::
+ Shows the 32bit codec subsystem-id hex number. You can change the
+ subsystem-id value by writing to this file.
+revision_id::
+ Shows the 32bit codec revision-id hex number. You can change the
+ revision-id value by writing to this file.
+afg::
+ Shows the AFG ID. This is read-only.
+mfg::
+ Shows the MFG ID. This is read-only.
+name::
+ Shows the codec name string. Can be changed by writing to this
+ file.
+modelname::
+ Shows the currently set `model` option. Can be changed by writing
+ to this file.
+init_verbs::
+ The extra verbs to execute at initialization. You can add a verb by
+ writing to this file. Pass tree numbers, nid, verb and parameter.
+hints::
+ Shows hint strings for codec parsers for any use. Right now it's
+ not used.
+reconfig::
+ Triggers the codec re-configuration. When any value is written to
+ this file, the driver re-initialize and parses the codec tree
+ again. All the changes done by the sysfs entries above are taken
+ into account.
+clear::
+ Resets the codec, removes the mixer elements and PCM stuff of the
+ specified codec, and clear all init verbs and hints.
+
+
+Power-Saving
+~~~~~~~~~~~~
+The power-saving is a kind of auto-suspend of the device. When the
+device is inactive for a certain time, the device is automatically
+turned off to save the power. The time to go down is specified via
+`power_save` module option, and this option can be changed dynamically
+via sysfs.
+
+The power-saving won't work when the analog loopback is enabled on
+some codecs. Make sure that you mute all unneeded signal routes when
+you want the power-saving.
+
+The power-saving feature might cause audible click noises at each
+power-down/up depending on the device. Some of them might be
+solvable, but some are hard, I'm afraid. Some distros such as
+openSUSE enables the power-saving feature automatically when the power
+cable is unplugged. Thus, if you hear noises, suspect first the
+power-saving. See /sys/module/snd_hda_intel/parameters/power_save to
+check the current value. If it's non-zero, the feature is turned on.
+
+
+Development Tree
+~~~~~~~~~~~~~~~~
+The latest development codes for HD-audio are found on sound git tree:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git
+
+The master branch or for-next branches can be used as the main
+development branches in general while the HD-audio specific patches
+are committed in topic/hda branch.
+
+If you are using the latest Linus tree, it'd be better to pull the
+above GIT tree onto it. If you are using the older kernels, an easy
+way to try the latest ALSA code is to build from the snapshot
+tarball. There are daily tarballs and the latest snapshot tarball.
+All can be built just like normal alsa-driver release packages, that
+is, installed via the usual spells: configure, make and make
+install(-modules). See INSTALL in the package. The snapshot tarballs
+are found at:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/
+
+
+Sending a Bug Report
+~~~~~~~~~~~~~~~~~~~~
+If any model or module options don't work for your device, it's time
+to send a bug report to the developers. Give the following in your
+bug report:
+
+- Hardware vendor, product and model names
+- Kernel version (and ALSA-driver version if you built externally)
+- `alsa-info.sh` output; run with `--no-upload` option. See the
+ section below about alsa-info
+
+If it's a regression, at best, send alsa-info outputs of both working
+and non-working kernels. This is really helpful because we can
+compare the codec registers directly.
+
+Send a bug report either the followings:
+
+kernel-bugzilla::
+ http://bugme.linux-foundation.org/
+alsa-devel ML::
+ alsa-devel@alsa-project.org
+
+
+DEBUG TOOLS
+-----------
+
+This section describes some tools available for debugging HD-audio
+problems.
+
+alsa-info
+~~~~~~~~~
+The script `alsa-info.sh` is a very useful tool to gather the audio
+device information. You can fetch the latest version from:
+
+- http://www.alsa-project.org/alsa-info.sh
+
+Run this script as root, and it will gather the important information
+such as the module lists, module parameters, proc file contents
+including the codec proc files, mixer outputs and the control
+elements. As default, it will store the information onto a web server
+on alsa-project.org. But, if you send a bug report, it'd be better to
+run with `--no-upload` option, and attach the generated file.
+
+There are some other useful options. See `--help` option output for
+details.
+
+
+hda-verb
+~~~~~~~~
+hda-verb is a tiny program that allows you to access the HD-audio
+codec directly. You can execute a raw HD-audio codec verb with this.
+This program accesses the hwdep device, thus you need to enable the
+kernel config `CONFIG_SND_HDA_HWDEP=y` beforehand.
+
+The hda-verb program takes four arguments: the hwdep device file, the
+widget NID, the verb and the parameter. When you access to the codec
+on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
+argument, typically. (However, the real path name depends on the
+system.)
+
+The second parameter is the widget number-id to access. The third
+parameter can be either a hex/digit number or a string corresponding
+to a verb. Similarly, the last parameter is the value to write, or
+can be a string for the parameter type.
+
+------------------------------------------------------------------------
+ % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
+ nid = 0x12, verb = 0x701, param = 0x2
+ value = 0x0
+
+ % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
+ nid = 0x0, verb = 0xf00, param = 0x0
+ value = 0x10ec0262
+
+ % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
+ nid = 0x2, verb = 0x300, param = 0xb080
+ value = 0x0
+------------------------------------------------------------------------
+
+Although you can issue any verbs with this program, the driver state
+won't be always updated. For example, the volume values are usually
+cached in the driver, and thus changing the widget amp value directly
+via hda-verb won't change the mixer value.
+
+The hda-verb program is found in the ftp directory:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+Also a git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
+
+See README file in the tarball for more details about hda-verb
+program.
+
+
+hda-analyzer
+~~~~~~~~~~~~
+hda-analyzer provides a graphical interface to access the raw HD-audio
+control, based on pyGTK2 binding. It's a more powerful version of
+hda-verb. The program gives you an easy-to-use GUI stuff for showing
+the widget information and adjusting the amp values, as well as the
+proc-compatible output.
+
+The hda-analyzer is a part of alsa.git repository in
+alsa-project.org:
+
+- http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
+
+
+Codecgraph
+~~~~~~~~~~
+Codecgraph is a utility program to generate a graph and visualizes the
+codec-node connection of a codec chip. It's especially useful when
+you analyze or debug a codec without a proper datasheet. The program
+parses the given codec proc file and converts to SVG via graphiz
+program.
+
+The tarball and GIT trees are found in the web page at:
+
+- http://helllabs.org/codecgraph/
+
+
+hda-emu
+~~~~~~~
+hda-emu is an HD-audio emulator. The main purpose of this program is
+to debug an HD-audio codec without the real hardware. Thus, it
+doesn't emulate the behavior with the real audio I/O, but it just
+dumps the codec register changes and the ALSA-driver internal changes
+at probing and operating the HD-audio driver.
+
+The program requires a codec proc-file to simulate. Get a proc file
+for the target codec beforehand, or pick up an example codec from the
+codec proc collections in the tarball. Then, run the program with the
+proc file, and the hda-emu program will start parsing the codec file
+and simulates the HD-audio driver:
+
+------------------------------------------------------------------------
+ % hda-emu codecs/stac9200-dell-d820-laptop
+ # Parsing..
+ hda_codec: Unknown model for STAC9200, using BIOS defaults
+ hda_codec: pin nid 08 bios pin config 40c003fa
+ ....
+------------------------------------------------------------------------
+
+The program gives you only a very dumb command-line interface. You
+can get a proc-file dump at the current state, get a list of control
+(mixer) elements, set/get the control element value, simulate the PCM
+operation, the jack plugging simulation, etc.
+
+The package is found in:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+A git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
+
+See README file in the tarball for more details about hda-emu
+program.
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt
index f738b296440a..bba2dbb79d81 100644
--- a/Documentation/sound/alsa/Procfile.txt
+++ b/Documentation/sound/alsa/Procfile.txt
@@ -153,6 +153,16 @@ card*/codec#*
Shows the general codec information and the attribute of each
widget node.
+card*/eld#*
+ Available for HDMI or DisplayPort interfaces.
+ Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
+ and describes its audio capabilities and configurations.
+
+ Some ELD fields may be modified by doing `echo name hex_value > eld#*`.
+ Only do this if you are sure the HDMI sink provided value is wrong.
+ And if that makes your HDMI audio work, please report to us so that we
+ can fix it in future kernel releases.
+
Sequencer Information
---------------------
diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt
index f370e7db86af..bab7711ce963 100644
--- a/Documentation/sound/alsa/soc/machine.txt
+++ b/Documentation/sound/alsa/soc/machine.txt
@@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
the following struct:-
/* SoC machine */
-struct snd_soc_machine {
+struct snd_soc_card {
char *name;
int (*probe)(struct platform_device *pdev);
@@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
.ops = &corgi_ops,
};
-struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
+struct snd_soc_card then sets up the machine with it's DAIs. e.g.
/* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
.name = "Corgi",
.dai_link = &corgi_dai,
.num_links = 1,
@@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
/* corgi audio subsystem */
static struct snd_soc_device corgi_snd_devdata = {
- .machine = &snd_soc_machine_corgi,
+ .machine = &snd_soc_corgi,
.platform = &pxa2xx_soc_platform,
.codec_dev = &soc_codec_dev_wm8731,
.codec_data = &corgi_wm8731_setup,
diff --git a/Documentation/video4linux/API.html b/Documentation/video4linux/API.html
index afbe9ae7ee96..d749d41f647b 100644
--- a/Documentation/video4linux/API.html
+++ b/Documentation/video4linux/API.html
@@ -1,16 +1,27 @@
-<TITLE>V4L API</TITLE>
-<H1>Video For Linux APIs</H1>
-<table border=0>
-<tr>
-<td>
-<A HREF=http://www.linuxtv.org/downloads/video4linux/API/V4L1_API.html>
-V4L original API</a>
-</td><td>
-Obsoleted by V4L2 API
-</td></tr><tr><td>
-<A HREF=http://www.linuxtv.org/downloads/video4linux/API/V4L2_API>
-V4L2 API</a>
-</td><td>
-Should be used for new projects
-</td></tr>
-</table>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <meta content="text/html;charset=ISO-8859-2" http-equiv="Content-Type" />
+ <title>V4L API</title>
+ </head>
+ <body>
+ <h1>Video For Linux APIs</h1>
+ <table border="0">
+ <tr>
+ <td>
+ <a href="http://www.linuxtv.org/downloads/video4linux/API/V4L1_API.html">V4L original API</a>
+ </td>
+ <td>
+ Obsoleted by V4L2 API
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="http://www.linuxtv.org/downloads/video4linux/API/V4L2_API">V4L2 API</a>
+ </td>
+ <td>Should be used for new projects
+ </td>
+ </tr>
+ </table>
+ </body>
+</html>
diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
index 60ba66836038..0d93fa1ac25e 100644
--- a/Documentation/video4linux/CARDLIST.bttv
+++ b/Documentation/video4linux/CARDLIST.bttv
@@ -104,8 +104,8 @@
103 -> Grand X-Guard / Trust 814PCI [0304:0102]
104 -> Nebula Electronics DigiTV [0071:0101]
105 -> ProVideo PV143 [aa00:1430,aa00:1431,aa00:1432,aa00:1433,aa03:1433]
-106 -> PHYTEC VD-009-X1 MiniDIN (bt878)
-107 -> PHYTEC VD-009-X1 Combi (bt878)
+106 -> PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)
+107 -> PHYTEC VD-009-X1 VD-011 Combi (bt878)
108 -> PHYTEC VD-009 MiniDIN (bt878)
109 -> PHYTEC VD-009 Combi (bt878)
110 -> IVC-100 [ff00:a132]
@@ -151,3 +151,6 @@
150 -> Geovision GV-600 [008a:763c]
151 -> Kozumi KTV-01C
152 -> Encore ENL TV-FM-2 [1000:1801]
+153 -> PHYTEC VD-012 (bt878)
+154 -> PHYTEC VD-012-X1 (bt878)
+155 -> PHYTEC VD-012-X2 (bt878)
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 64823ccacd69..35ea130e9898 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -11,3 +11,4 @@
10 -> DViCO FusionHDTV7 Dual Express [18ac:d618]
11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78]
12 -> Leadtek Winfast PxDVR3200 H [107d:6681]
+ 13 -> Compro VideoMate E650F [185b:e800]
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index a5227e308f4a..0d08f1edcf6d 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -2,7 +2,7 @@
1 -> Hauppauge WinTV 34xxx models [0070:3400,0070:3401]
2 -> GDI Black Gold [14c7:0106,14c7:0107]
3 -> PixelView [1554:4811]
- 4 -> ATI TV Wonder Pro [1002:00f8]
+ 4 -> ATI TV Wonder Pro [1002:00f8,1002:00f9]
5 -> Leadtek Winfast 2000XP Expert [107d:6611,107d:6613]
6 -> AverTV Studio 303 (M126) [1461:000b]
7 -> MSI TV-@nywhere Master [1462:8606]
@@ -74,3 +74,6 @@
73 -> TeVii S420 DVB-S [d420:9022]
74 -> Prolink Pixelview Global Extreme [1554:4976]
75 -> PROF 7300 DVB-S/S2 [B033:3033]
+ 76 -> SATTRADE ST4200 DVB-S/S2 [b200:4200]
+ 77 -> TBS 8910 DVB-S [8910:8888]
+ 78 -> Prof 6200 DVB-S [b022:3022]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 187cc48d0924..75bded8a4aa2 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -1,5 +1,5 @@
0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800]
- 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
+ 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036]
3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208]
4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201]
@@ -12,9 +12,9 @@
11 -> Terratec Hybrid XS (em2880) [0ccd:0042]
12 -> Kworld PVR TV 2800 RF (em2820/em2840)
13 -> Terratec Prodigy XS (em2880) [0ccd:0047]
- 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) [eb1a:2821]
+ 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
15 -> V-Gear PocketTV (em2800)
- 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b,2040:651f]
+ 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b]
17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227]
18 -> Hauppauge WinTV HVR 900 (R2) (em2880) [2040:6502]
19 -> PointNix Intra-Oral Camera (em2860)
@@ -27,7 +27,6 @@
26 -> Hercules Smart TV USB 2.0 (em2820/em2840)
27 -> Pinnacle PCTV USB 2 (Philips FM1216ME) (em2820/em2840)
28 -> Leadtek Winfast USB II Deluxe (em2820/em2840)
- 29 -> Pinnacle Dazzle DVC 100 (em2820/em2840)
30 -> Videology 20K14XUSB USB2.0 (em2820/em2840)
31 -> Usbgear VD204v9 (em2821)
32 -> Supercomp USB 2.0 TV (em2821)
@@ -57,3 +56,5 @@
56 -> Pinnacle Hybrid Pro (2) (em2882) [2304:0226]
57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316]
58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041]
+ 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f]
+ 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840)
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index dc67eef38ff9..335aef4dcaeb 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -10,7 +10,7 @@
9 -> Medion 5044
10 -> Kworld/KuroutoShikou SAA7130-TVPCI
11 -> Terratec Cinergy 600 TV [153b:1143]
- 12 -> Medion 7134 [16be:0003]
+ 12 -> Medion 7134 [16be:0003,16be:5000]
13 -> Typhoon TV+Radio 90031
14 -> ELSA EX-VISION 300TV [1048:226b]
15 -> ELSA EX-VISION 500TV [1048:226a]
@@ -151,3 +151,4 @@
150 -> Zogis Real Angel 220
151 -> ADS Tech Instant HDTV [1421:0380]
152 -> Asus Tiger Rev:1.00 [1043:4857]
+153 -> Kworld Plus TV Analog Lite PCI [17de:7128]
diff --git a/Documentation/video4linux/README.cx88 b/Documentation/video4linux/README.cx88
index 166d5960b1a9..35fae23f883b 100644
--- a/Documentation/video4linux/README.cx88
+++ b/Documentation/video4linux/README.cx88
@@ -1,4 +1,3 @@
-
cx8800 release notes
====================
@@ -10,21 +9,20 @@ current status
video
- Basically works.
- - Some minor image quality glitches.
- - For now only capture, overlay support isn't completed yet.
+ - For now, only capture and read(). Overlay isn't supported.
audio
- The chip specs for the on-chip TV sound decoder are next
to useless :-/
- Neverless the builtin TV sound decoder starts working now,
- at least for PAL-BG. Other TV norms need other code ...
+ at least for some standards.
FOR ANY REPORTS ON THIS PLEASE MENTION THE TV NORM YOU ARE
USING.
- Most tuner chips do provide mono sound, which may or may not
be useable depending on the board design. With the Hauppauge
cards it works, so there is mono sound available as fallback.
- audio data dma (i.e. recording without loopback cable to the
- sound card) should be possible, but there is no code yet ...
+ sound card) is supported via cx88-alsa.
vbi
- Code present. Works for NTSC closed caption. PAL and other
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 004818fab040..1c58a7630146 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -50,9 +50,14 @@ ov519 045e:028c Micro$oft xbox cam
spca508 0461:0815 Micro Innovation IC200
sunplus 0461:0821 Fujifilm MV-1
zc3xx 0461:0a00 MicroInnovation WebCam320
+stv06xx 046d:0840 QuickCam Express
+stv06xx 046d:0850 LEGO cam / QuickCam Web
+stv06xx 046d:0870 Dexxa WebCam USB
spca500 046d:0890 Logitech QuickCam traveler
vc032x 046d:0892 Logitech Orbicam
vc032x 046d:0896 Logitech Orbicam
+vc032x 046d:0897 Logitech QuickCam for Dell notebooks
+zc3xx 046d:089d Logitech QuickCam E2500
zc3xx 046d:08a0 Logitech QC IM
zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound
zc3xx 046d:08a2 Labtec Webcam Pro
@@ -169,6 +174,9 @@ spca500 06bd:0404 Agfa CL20
spca500 06be:0800 Optimedia
sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom
spca506 06e1:a190 ADS Instant VCD
+ov534 06f8:3002 Hercules Blog Webcam
+ov534 06f8:3003 Hercules Dualpix HD Weblog
+sonixj 06f8:3004 Hercules Classic Silver
spca508 0733:0110 ViewQuest VQ110
spca508 0130:0130 Clone Digital Webcam 11043
spca501 0733:0401 Intel Create and Share
@@ -199,7 +207,8 @@ sunplus 08ca:2050 Medion MD 41437
sunplus 08ca:2060 Aiptek PocketDV5300
tv8532 0923:010f ICM532 cams
mars 093a:050f Mars-Semi Pc-Camera
-pac207 093a:2460 PAC207 Qtec Webcam 100
+pac207 093a:2460 Qtec Webcam 100
+pac207 093a:2461 HP Webcam
pac207 093a:2463 Philips SPC 220 NC
pac207 093a:2464 Labtec Webcam 1200
pac207 093a:2468 PAC207
@@ -213,10 +222,13 @@ pac7311 093a:2603 PAC7312
pac7311 093a:2608 Trust WB-3300p
pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350
pac7311 093a:260f SnakeCam
+pac7311 093a:2620 Apollo AC-905
pac7311 093a:2621 PAC731x
+pac7311 093a:2622 Genius Eye 312
pac7311 093a:2624 PAC7302
pac7311 093a:2626 Labtec 2200
pac7311 093a:262a Webcam 300k
+pac7311 093a:262c Philips SPC 230 NC
zc3xx 0ac8:0302 Z-star Vimicro zc0302
vc032x 0ac8:0321 Vimicro generic vc0321
vc032x 0ac8:0323 Vimicro Vc0323
@@ -249,11 +261,13 @@ sonixj 0c45:60c0 Sangha Sn535
sonixj 0c45:60ec SN9C105+MO4000
sonixj 0c45:60fb Surfer NoName
sonixj 0c45:60fc LG-LIC300
+sonixj 0c45:60fe Microdia Audio
sonixj 0c45:6128 Microdia/Sonix SNP325
sonixj 0c45:612a Avant Camera
sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix
sonixj 0c45:6130 Sonix Pccam
sonixj 0c45:6138 Sn9c120 Mo4000
+sonixj 0c45:613a Microdia Sonix PC Camera
sonixj 0c45:613b Surfer SN-206
sonixj 0c45:613c Sonix Pccam168
sonixj 0c45:6143 Sonix Pccam168
@@ -263,6 +277,9 @@ etoms 102c:6251 Qcam xxxxxx VGA
zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128
spca561 10fd:7e50 FlyCam Usb 100
zc3xx 10fd:8050 Typhoon Webshot II USB 300k
+ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201)
+pac207 145f:013a Trust WB-1300N
+vc032x 15b8:6002 HP 2.0 Megapixel rz406aa
spca501 1776:501c Arowana 300K CMOS Camera
t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops
vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
new file mode 100644
index 000000000000..eeae76c22a93
--- /dev/null
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -0,0 +1,520 @@
+Overview of the V4L2 driver framework
+=====================================
+
+This text documents the various structures provided by the V4L2 framework and
+their relationships.
+
+
+Introduction
+------------
+
+The V4L2 drivers tend to be very complex due to the complexity of the
+hardware: most devices have multiple ICs, export multiple device nodes in
+/dev, and create also non-V4L2 devices such as DVB, ALSA, FB, I2C and input
+(IR) devices.
+
+Especially the fact that V4L2 drivers have to setup supporting ICs to
+do audio/video muxing/encoding/decoding makes it more complex than most.
+Usually these ICs are connected to the main bridge driver through one or
+more I2C busses, but other busses can also be used. Such devices are
+called 'sub-devices'.
+
+For a long time the framework was limited to the video_device struct for
+creating V4L device nodes and video_buf for handling the video buffers
+(note that this document does not discuss the video_buf framework).
+
+This meant that all drivers had to do the setup of device instances and
+connecting to sub-devices themselves. Some of this is quite complicated
+to do right and many drivers never did do it correctly.
+
+There is also a lot of common code that could never be refactored due to
+the lack of a framework.
+
+So this framework sets up the basic building blocks that all drivers
+need and this same framework should make it much easier to refactor
+common code into utility functions shared by all drivers.
+
+
+Structure of a driver
+---------------------
+
+All drivers have the following structure:
+
+1) A struct for each device instance containing the device state.
+
+2) A way of initializing and commanding sub-devices (if any).
+
+3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and
+ /dev/vtxX) and keeping track of device-node specific data.
+
+4) Filehandle-specific structs containing per-filehandle data.
+
+This is a rough schematic of how it all relates:
+
+ device instances
+ |
+ +-sub-device instances
+ |
+ \-V4L2 device nodes
+ |
+ \-filehandle instances
+
+
+Structure of the framework
+--------------------------
+
+The framework closely resembles the driver structure: it has a v4l2_device
+struct for the device instance data, a v4l2_subdev struct to refer to
+sub-device instances, the video_device struct stores V4L2 device node data
+and in the future a v4l2_fh struct will keep track of filehandle instances
+(this is not yet implemented).
+
+
+struct v4l2_device
+------------------
+
+Each device instance is represented by a struct v4l2_device (v4l2-device.h).
+Very simple devices can just allocate this struct, but most of the time you
+would embed this struct inside a larger struct.
+
+You must register the device instance:
+
+ v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
+
+Registration will initialize the v4l2_device struct and link dev->driver_data
+to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from
+dev (driver name followed by the bus_id, to be precise). You may change the
+name after registration if you want.
+
+The first 'dev' argument is normally the struct device pointer of a pci_dev,
+usb_device or platform_device.
+
+You unregister with:
+
+ v4l2_device_unregister(struct v4l2_device *v4l2_dev);
+
+Unregistering will also automatically unregister all subdevs from the device.
+
+Sometimes you need to iterate over all devices registered by a specific
+driver. This is usually the case if multiple device drivers use the same
+hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv
+hardware. The same is true for alsa drivers for example.
+
+You can iterate over all registered devices as follows:
+
+static int callback(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+
+ /* test if this device was inited */
+ if (v4l2_dev == NULL)
+ return 0;
+ ...
+ return 0;
+}
+
+int iterate(void *p)
+{
+ struct device_driver *drv;
+ int err;
+
+ /* Find driver 'ivtv' on the PCI bus.
+ pci_bus_type is a global. For USB busses use usb_bus_type. */
+ drv = driver_find("ivtv", &pci_bus_type);
+ /* iterate over all ivtv device instances */
+ err = driver_for_each_device(drv, NULL, p, callback);
+ put_driver(drv);
+ return err;
+}
+
+Sometimes you need to keep a running counter of the device instance. This is
+commonly used to map a device instance to an index of a module option array.
+
+The recommended approach is as follows:
+
+static atomic_t drv_instance = ATOMIC_INIT(0);
+
+static int __devinit drv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ ...
+ state->instance = atomic_inc_return(&drv_instance) - 1;
+}
+
+
+struct v4l2_subdev
+------------------
+
+Many drivers need to communicate with sub-devices. These devices can do all
+sort of tasks, but most commonly they handle audio and/or video muxing,
+encoding or decoding. For webcams common sub-devices are sensors and camera
+controllers.
+
+Usually these are I2C devices, but not necessarily. In order to provide the
+driver with a consistent interface to these sub-devices the v4l2_subdev struct
+(v4l2-subdev.h) was created.
+
+Each sub-device driver must have a v4l2_subdev struct. This struct can be
+stand-alone for simple sub-devices or it might be embedded in a larger struct
+if more state information needs to be stored. Usually there is a low-level
+device struct (e.g. i2c_client) that contains the device data as setup
+by the kernel. It is recommended to store that pointer in the private
+data of v4l2_subdev using v4l2_set_subdevdata(). That makes it easy to go
+from a v4l2_subdev to the actual low-level bus-specific device data.
+
+You also need a way to go from the low-level struct to v4l2_subdev. For the
+common i2c_client struct the i2c_set_clientdata() call is used to store a
+v4l2_subdev pointer, for other busses you may have to use other methods.
+
+From the bridge driver perspective you load the sub-device module and somehow
+obtain the v4l2_subdev pointer. For i2c devices this is easy: you call
+i2c_get_clientdata(). For other busses something similar needs to be done.
+Helper functions exists for sub-devices on an I2C bus that do most of this
+tricky work for you.
+
+Each v4l2_subdev contains function pointers that sub-device drivers can
+implement (or leave NULL if it is not applicable). Since sub-devices can do
+so many different things and you do not want to end up with a huge ops struct
+of which only a handful of ops are commonly implemented, the function pointers
+are sorted according to category and each category has its own ops struct.
+
+The top-level ops struct contains pointers to the category ops structs, which
+may be NULL if the subdev driver does not support anything from that category.
+
+It looks like this:
+
+struct v4l2_subdev_core_ops {
+ int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_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_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;
+};
+
+The core ops are common to all subdevs, the other categories are implemented
+depending on the sub-device. E.g. a video device is unlikely to support the
+audio ops and vice versa.
+
+This setup limits the number of function pointers while still making it easy
+to add new ops and categories.
+
+A sub-device driver initializes the v4l2_subdev struct using:
+
+ v4l2_subdev_init(subdev, &ops);
+
+Afterwards you need to initialize subdev->name with a unique name and set the
+module owner. This is done for you if you use the i2c helper functions.
+
+A device (bridge) driver needs to register the v4l2_subdev with the
+v4l2_device:
+
+ int err = v4l2_device_register_subdev(device, subdev);
+
+This can fail if the subdev module disappeared before it could be registered.
+After this function was called successfully the subdev->dev field points to
+the v4l2_device.
+
+You can unregister a sub-device using:
+
+ v4l2_device_unregister_subdev(subdev);
+
+Afterwards the subdev module can be unloaded and subdev->dev == NULL.
+
+You can call an ops function either directly:
+
+ err = subdev->ops->core->g_chip_ident(subdev, &chip);
+
+but it is better and easier to use this macro:
+
+ err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip);
+
+The macro will to the right NULL pointer checks and returns -ENODEV if subdev
+is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is
+NULL, or the actual result of the subdev->ops->core->g_chip_ident ops.
+
+It is also possible to call all or a subset of the sub-devices:
+
+ v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip);
+
+Any subdev that does not support this ops is skipped and error results are
+ignored. If you want to check for errors use this:
+
+ err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip);
+
+Any error except -ENOIOCTLCMD will exit the loop with that error. If no
+errors (except -ENOIOCTLCMD) occured, then 0 is returned.
+
+The second argument to both calls is a group ID. If 0, then all subdevs are
+called. If non-zero, then only those whose group ID match that value will
+be called. Before a bridge driver registers a subdev it can set subdev->grp_id
+to whatever value it wants (it's 0 by default). This value is owned by the
+bridge driver and the sub-device driver will never modify or use it.
+
+The group ID gives the bridge driver more control how callbacks are called.
+For example, there may be multiple audio chips on a board, each capable of
+changing the volume. But usually only one will actually be used when the
+user want to change the volume. You can set the group ID for that subdev to
+e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling
+v4l2_device_call_all(). That ensures that it will only go to the subdev
+that needs it.
+
+The advantage of using v4l2_subdev is that it is a generic struct and does
+not contain any knowledge about the underlying hardware. So a driver might
+contain several subdevs that use an I2C bus, but also a subdev that is
+controlled through GPIO pins. This distinction is only relevant when setting
+up the device, but once the subdev is registered it is completely transparent.
+
+
+I2C sub-device drivers
+----------------------
+
+Since these drivers are so common, special helper functions are available to
+ease the use of these drivers (v4l2-common.h).
+
+The recommended method of adding v4l2_subdev support to an I2C driver is to
+embed the v4l2_subdev struct into the state struct that is created for each
+I2C device instance. Very simple devices have no state struct and in that case
+you can just create a v4l2_subdev directly.
+
+A typical state struct would look like this (where 'chipname' is replaced by
+the name of the chip):
+
+struct chipname_state {
+ struct v4l2_subdev sd;
+ ... /* additional state fields */
+};
+
+Initialize the v4l2_subdev struct as follows:
+
+ v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
+
+This function will fill in all the fields of v4l2_subdev and ensure that the
+v4l2_subdev and i2c_client both point to one another.
+
+You should also add a helper inline function to go from a v4l2_subdev pointer
+to a chipname_state struct:
+
+static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct chipname_state, sd);
+}
+
+Use this to go from the v4l2_subdev struct to the i2c_client struct:
+
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+And this to go from an i2c_client to a v4l2_subdev struct:
+
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+Finally you need to make a command function to make driver->command()
+call the right subdev_ops functions:
+
+static int subdev_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+If driver->command is never used then you can leave this out. Eventually the
+driver->command usage should be removed from v4l.
+
+Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback
+is called. This will unregister the sub-device from the bridge driver. It is
+safe to call this even if the sub-device was never registered.
+
+
+The bridge driver also has some helper functions it can use:
+
+struct v4l2_subdev *sd = v4l2_i2c_new_subdev(adapter, "module_foo", "chipid", 0x36);
+
+This loads the given module (can be NULL if no module needs to be loaded) and
+calls i2c_new_device() with the given i2c_adapter and chip/address arguments.
+If all goes well, then it registers the subdev with the v4l2_device. It gets
+the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure
+that adapdata is set to v4l2_device when you setup the i2c_adapter in your
+driver.
+
+You can also use v4l2_i2c_new_probed_subdev() which is very similar to
+v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses
+that it should probe. Internally it calls i2c_new_probed_device().
+
+Both functions return NULL if something went wrong.
+
+
+struct video_device
+-------------------
+
+The actual device nodes in the /dev directory are created using the
+video_device struct (v4l2-dev.h). This struct can either be allocated
+dynamically or embedded in a larger struct.
+
+To allocate it dynamically use:
+
+ struct video_device *vdev = video_device_alloc();
+
+ if (vdev == NULL)
+ return -ENOMEM;
+
+ vdev->release = video_device_release;
+
+If you embed it in a larger struct, then you must set the release()
+callback to your own function:
+
+ struct video_device *vdev = &my_vdev->vdev;
+
+ vdev->release = my_vdev_release;
+
+The release callback must be set and it is called when the last user
+of the video device exits.
+
+The default video_device_release() callback just calls kfree to free the
+allocated memory.
+
+You should also set these fields:
+
+- parent: set to the parent device (same device as was used to register
+ v4l2_device).
+- name: set to something descriptive and unique.
+- fops: set to the file_operations struct.
+- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
+ (highly recommended to use this and it might become compulsory in the
+ future!), then set this to your v4l2_ioctl_ops struct.
+
+If you use v4l2_ioctl_ops, then you should set .unlocked_ioctl to
+__video_ioctl2 or .ioctl to video_ioctl2 in your file_operations struct.
+
+
+video_device registration
+-------------------------
+
+Next you register the video device: this will create the character device
+for you.
+
+ err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (err) {
+ video_device_release(vdev); // or kfree(my_vdev);
+ return err;
+ }
+
+Which device is registered depends on the type argument. The following
+types exist:
+
+VFL_TYPE_GRABBER: videoX for video input/output devices
+VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext)
+VFL_TYPE_RADIO: radioX for radio tuners
+VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use)
+
+The last argument gives you a certain amount of control over the device
+kernel number used (i.e. the X in videoX). Normally you will pass -1 to
+let the v4l2 framework pick the first free number. But if a driver creates
+many devices, then it can be useful to have different video devices in
+separate ranges. For example, video capture devices start at 0, video
+output devices start at 16.
+
+So you can use the last argument to specify a minimum kernel number and
+the v4l2 framework will try to pick the first free number that is equal
+or higher to what you passed. If that fails, then it will just pick the
+first free number.
+
+Whenever a device node is created some attributes are also created for you.
+If you look in /sys/class/video4linux you see the devices. Go into e.g.
+video0 and you will see 'name' and 'index' attributes. The 'name' attribute
+is the 'name' field of the video_device struct. The 'index' attribute is
+a device node index that can be assigned by the driver, or that is calculated
+for you.
+
+If you call video_register_device(), then the index is just increased by
+1 for each device node you register. The first video device node you register
+always starts off with 0.
+
+Alternatively you can call video_register_device_index() which is identical
+to video_register_device(), but with an extra index argument. Here you can
+pass a specific index value (between 0 and 31) that should be used.
+
+Users can setup udev rules that utilize the index attribute to make fancy
+device names (e.g. 'mpegX' for MPEG video capture device nodes).
+
+After the device was successfully registered, then you can use these fields:
+
+- vfl_type: the device type passed to video_register_device.
+- minor: the assigned device minor number.
+- num: the device kernel number (i.e. the X in videoX).
+- index: the device index number (calculated or set explicitly using
+ video_register_device_index).
+
+If the registration failed, then you need to call video_device_release()
+to free the allocated video_device struct, or free your own struct if the
+video_device was embedded in it. The vdev->release() callback will never
+be called if the registration failed, nor should you ever attempt to
+unregister the device if the registration failed.
+
+
+video_device cleanup
+--------------------
+
+When the video device nodes have to be removed, either during the unload
+of the driver or because the USB device was disconnected, then you should
+unregister them:
+
+ video_unregister_device(vdev);
+
+This will remove the device nodes from sysfs (causing udev to remove them
+from /dev).
+
+After video_unregister_device() returns no new opens can be done.
+
+However, in the case of USB devices some application might still have one
+of these device nodes open. You should block all new accesses to read,
+write, poll, etc. except possibly for certain ioctl operations like
+queueing buffers.
+
+When the last user of the video device node exits, then the vdev->release()
+callback is called and you can do the final cleanup there.
+
+
+video_device helper functions
+-----------------------------
+
+There are a few useful helper functions:
+
+You can set/get driver private data in the video_device struct using:
+
+void *video_get_drvdata(struct video_device *dev);
+void video_set_drvdata(struct video_device *dev, void *data);
+
+Note that you can safely call video_set_drvdata() before calling
+video_register_device().
+
+And this function:
+
+struct video_device *video_devdata(struct file *file);
+
+returns the video_device belonging to the file struct.
+
+The final helper function combines video_get_drvdata with
+video_devdata:
+
+void *video_drvdata(struct file *file);
+
+You can go from a video_device struct to the v4l2_device struct using:
+
+struct v4l2_device *v4l2_dev = dev_get_drvdata(vdev->parent);
+
diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt
index 83c0033ee9e0..fcdc62b3c3d8 100644
--- a/Documentation/x86/boot.txt
+++ b/Documentation/x86/boot.txt
@@ -349,7 +349,7 @@ Protocol: 2.00+
3 SYSLINUX
4 EtherBoot
5 ELILO
- 7 GRuB
+ 7 GRUB
8 U-BOOT
9 Xen
A Gujin
@@ -537,8 +537,8 @@ Type: read
Offset/size: 0x248/4
Protocol: 2.08+
- If non-zero then this field contains the offset from the end of the
- real-mode code to the payload.
+ If non-zero then this field contains the offset from the beginning
+ of the protected-mode code to the payload.
The payload may be compressed. The format of both the compressed and
uncompressed data should be determined using the standard magic
diff --git a/Documentation/x86/pat.txt b/Documentation/x86/pat.txt
index c93ff5f4c0dd..cf08c9fff3cd 100644
--- a/Documentation/x86/pat.txt
+++ b/Documentation/x86/pat.txt
@@ -80,6 +80,30 @@ pci proc | -- | -- | WC |
| | | |
-------------------------------------------------------------------
+Advanced APIs for drivers
+-------------------------
+A. Exporting pages to users with remap_pfn_range, io_remap_pfn_range,
+vm_insert_pfn
+
+Drivers wanting to export some pages to userspace do it by using mmap
+interface and a combination of
+1) pgprot_noncached()
+2) io_remap_pfn_range() or remap_pfn_range() or vm_insert_pfn()
+
+With PAT support, a new API pgprot_writecombine is being added. So, drivers can
+continue to use the above sequence, with either pgprot_noncached() or
+pgprot_writecombine() in step 1, followed by step 2.
+
+In addition, step 2 internally tracks the region as UC or WC in memtype
+list in order to ensure no conflicting mapping.
+
+Note that this set of APIs only works with IO (non RAM) regions. If driver
+wants to export a RAM region, it has to do set_memory_uc() or set_memory_wc()
+as step 0 above and also track the usage of those pages and use set_memory_wb()
+before the page is freed to free pool.
+
+
+
Notes:
-- in the above table mean "Not suggested usage for the API". Some of the --'s
diff --git a/Documentation/x86/x86_64/boot-options.txt b/Documentation/x86/x86_64/boot-options.txt
index f6d561a1a9b2..34c13040a718 100644
--- a/Documentation/x86/x86_64/boot-options.txt
+++ b/Documentation/x86/x86_64/boot-options.txt
@@ -79,17 +79,6 @@ Timing
Report when timer interrupts are lost because some code turned off
interrupts for too long.
- nmi_watchdog=NUMBER[,panic]
- NUMBER can be:
- 0 don't use an NMI watchdog
- 1 use the IO-APIC timer for the NMI watchdog
- 2 use the local APIC for the NMI watchdog using a performance counter. Note
- This will use one performance counter and the local APIC's performance
- vector.
- When panic is specified panic when an NMI watchdog timeout occurs.
- This is useful when you use a panic=... timeout and need the box
- quickly up again.
-
nohpet
Don't use the HPET timer.
diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt
index efce75097369..29b52b14d0b4 100644
--- a/Documentation/x86/x86_64/mm.txt
+++ b/Documentation/x86/x86_64/mm.txt
@@ -6,7 +6,7 @@ Virtual memory map with 4 level page tables:
0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm
hole caused by [48:63] sign extension
ffff800000000000 - ffff80ffffffffff (=40 bits) guard hole
-ffff810000000000 - ffffc0ffffffffff (=46 bits) direct mapping of all phys. memory
+ffff880000000000 - ffffc0ffffffffff (=57 TB) direct mapping of all phys. memory
ffffc10000000000 - ffffc1ffffffffff (=40 bits) hole
ffffc20000000000 - ffffe1ffffffffff (=45 bits) vmalloc/ioremap space
ffffe20000000000 - ffffe2ffffffffff (=40 bits) virtual memory map (1TB)
diff --git a/MAINTAINERS b/MAINTAINERS
index 09ed704f4dda..ceb32ee51f9d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -742,7 +742,7 @@ M: jirislaby@gmail.com
P: Nick Kossifidis
M: mickflemm@gmail.com
P: Luis R. Rodriguez
-M: mcgrof@gmail.com
+M: lrodriguez@atheros.com
P: Bob Copeland
M: me@bobcopeland.com
L: linux-wireless@vger.kernel.org
@@ -1607,11 +1607,6 @@ L: acpi4asus-user@lists.sourceforge.net
W: http://sourceforge.net/projects/acpi4asus
S: Maintained
-EEPRO100 NETWORK DRIVER
-P: Andrey V. Savochkin
-M: saw@saw.sw.com.sg
-S: Maintained
-
EFS FILESYSTEM
W: http://aeschi.ch.eu.org/efs/
S: Orphan
@@ -1760,6 +1755,13 @@ L: linuxppc-dev@ozlabs.org
L: linux-i2c@vger.kernel.org
S: Maintained
+FREESCALE IMX / MXC FRAMEBUFFER DRIVER
+P: Sascha Hauer
+M: kernel@pengutronix.de
+L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
+L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
+S: Maintained
+
FREESCALE SOC FS_ENET DRIVER
P: Pantelis Antoniou
M: pantelis.antoniou@gmail.com
@@ -1849,7 +1851,7 @@ P: Haavard Skinnemoen
M: hskinnemoen@atmel.com
S: Supported
-GENERIC HDLC DRIVER, N2, C101, PCI200SYN and WANXL DRIVERS
+GENERIC HDLC (WAN) DRIVERS
P: Krzysztof Halasa
M: khc@pm.waw.pl
W: http://www.kernel.org/pub/linux/utils/net/hdlc/
@@ -2191,9 +2193,9 @@ S: Supported
INOTIFY
P: John McCutchan
-M: ttb@tentacle.dhs.org
+M: john@johnmccutchan.com
P: Robert Love
-M: rml@novell.com
+M: rlove@rlove.org
L: linux-kernel@vger.kernel.org
S: Maintained
@@ -2248,6 +2250,11 @@ M: dan.j.williams@intel.com
L: linux-kernel@vger.kernel.org
S: Supported
+INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT
+P: Krzysztof Halasa
+M: khc@pm.waw.pl
+S: Maintained
+
INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT
P: Deepak Saxena
M: dsaxena@plexity.net
@@ -3614,16 +3621,26 @@ L: linux-hams@vger.kernel.org
W: http://www.linux-ax25.org/
S: Maintained
-RTL818X WIRELESS DRIVER
-P: Michael Wu
-M: flamingice@sourmilk.net
-P: Andrea Merello
-M: andreamrl@tiscali.it
+RTL8180 WIRELESS DRIVER
+P: John W. Linville
+M: linville@tuxdriver.com
L: linux-wireless@vger.kernel.org
W: http://linuxwireless.org/
-T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git
+T: git kernel.org:/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
+RTL8187 WIRELESS DRIVER
+P: Herton Ronaldo Krzesinski
+M: herton@mandriva.com.br
+P: Hin-Tak Leung
+M htl10@users.sourceforge.net
+P: Larry Finger
+M: Larry.Finger@lwfinger.net
+L: linux-wireless@vger.kernel.org
+W: http://linuxwireless.org/
+T: git kernel.org:/pub/scm/linux/kernel/git/linville/wireless-testing.git
+S: Maintained
+
S3 SAVAGE FRAMEBUFFER DRIVER
P: Antonino Daplas
M: adaplas@gmail.com
@@ -3913,6 +3930,18 @@ M: mhoffman@lightlink.com
L: lm-sensors@lm-sensors.org
S: Maintained
+SMSC911x ETHERNET DRIVER
+P: Steve Glendinning
+M: steve.glendinning@smsc.com
+L: netdev@vger.kernel.org
+S: Supported
+
+SMSC9420 PCI ETHERNET DRIVER
+P: Steve Glendinning
+M: steve.glendinning@smsc.com
+L: netdev@vger.kernel.org
+S: Supported
+
SMX UIO Interface
P: Ben Nizette
M: bn@niasdigital.com
@@ -3977,7 +4006,7 @@ M: tiwai@suse.de
L: alsa-devel@alsa-project.org (subscribers-only)
S: Maintained
-SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
P: Liam Girdwood
M: lrg@slimlogic.co.uk
P: Mark Brown
@@ -4529,7 +4558,7 @@ S: Maintained
USB VIDEO CLASS
P: Laurent Pinchart
M: laurent.pinchart@skynet.be
-L: linux-uvc-devel@lists.berlios.de
+L: linux-uvc-devel@lists.berlios.de (subscribers-only)
L: video4linux-list@redhat.com
W: http://linux-uvc.berlios.de
S: Maintained
diff --git a/Makefile b/Makefile
index 6c2f51b70454..d13a9694e159 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 28
-EXTRAVERSION = -rc8
+EXTRAVERSION =
NAME = Erotic Pickled Herring
# *DOCUMENTATION*
@@ -205,13 +205,14 @@ ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
-# Where to locate arch specific headers
+# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc64)
- hdr-arch := sparc
-else
- hdr-arch := $(SRCARCH)
+ SRCARCH := sparc
endif
+# Where to locate arch specific headers
+hdr-arch := $(SRCARCH)
+
KCONFIG_CONFIG ?= .config
# SHELL used by kbuild
@@ -336,7 +337,7 @@ LINUXINCLUDE := -Iinclude \
-I$(srctree)/arch/$(hdr-arch)/include \
-include include/linux/autoconf.h
-KBUILD_CPPFLAGS := -D__KERNEL__ $(LINUXINCLUDE)
+KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
@@ -439,7 +440,11 @@ ifeq ($(config-targets),1)
include $(srctree)/arch/$(SRCARCH)/Makefile
export KBUILD_DEFCONFIG KBUILD_KCONFIG
-config %config: scripts_basic outputmakefile FORCE
+config: scripts_basic outputmakefile FORCE
+ $(Q)mkdir -p include/linux include/config
+ $(Q)$(MAKE) $(build)=scripts/kconfig $@
+
+%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
@@ -600,20 +605,25 @@ export INSTALL_PATH ?= /boot
MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
export MODLIB
-#
-# INSTALL_MOD_STRIP, if defined, will cause modules to be
-# stripped after they are installed. If INSTALL_MOD_STRIP is '1', then
-# the default option --strip-debug will be used. Otherwise,
-# INSTALL_MOD_STRIP will used as the options to the strip command.
+strip-symbols := $(srctree)/scripts/strip-symbols \
+ $(wildcard $(srctree)/arch/$(ARCH)/scripts/strip-symbols)
+#
+# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped while
+# they get installed. If INSTALL_MOD_STRIP is '1', then the default
+# options (see below) will be used. Otherwise, INSTALL_MOD_STRIP will
+# be used as the option(s) to the objcopy command.
ifdef INSTALL_MOD_STRIP
ifeq ($(INSTALL_MOD_STRIP),1)
-mod_strip_cmd = $(STRIP) --strip-debug
+mod_strip_cmd = $(OBJCOPY) --strip-debug
+ifeq ($(CONFIG_KALLSYMS_ALL),$(CONFIG_KALLSYMS_STRIP_GENERATED))
+mod_strip_cmd += --wildcard $(addprefix --strip-symbols ,$(strip-symbols))
+endif
else
-mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP)
+mod_strip_cmd = $(OBJCOPY) $(INSTALL_MOD_STRIP)
endif # INSTALL_MOD_STRIP=1
else
-mod_strip_cmd = true
+mod_strip_cmd = false
endif # INSTALL_MOD_STRIP
export mod_strip_cmd
@@ -743,6 +753,7 @@ last_kallsyms := 2
endif
kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
+kallsyms.h := $(wildcard include/config/kallsyms/*.h) $(wildcard include/config/kallsyms/*/*.h)
define verify_kallsyms
$(Q)$(if $($(quiet)cmd_sysmap), \
@@ -767,24 +778,41 @@ endef
# Generate .S file with all kernel symbols
quiet_cmd_kallsyms = KSYM $@
- cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \
- $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@
+ cmd_kallsyms = { test $* -eq 0 || $(NM) -n $<; } \
+ | $(KALLSYMS) $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) >$@
+
+quiet_cmd_kstrip = STRIP $@
+ cmd_kstrip = $(OBJCOPY) --wildcard $(addprefix --strip$(if $(CONFIG_RELOCATABLE),-unneeded)-symbols ,$(filter %/scripts/strip-symbols,$^)) $< $@
-.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
+$(foreach n,0 1 2 3,.tmp_kallsyms$(n).o): KBUILD_AFLAGS += -Wa,--strip-local-absolute
+$(foreach n,0 1 2 3,.tmp_kallsyms$(n).o): %.o: %.S scripts FORCE
$(call if_changed_dep,as_o_S)
-.tmp_kallsyms%.S: .tmp_vmlinux% $(KALLSYMS)
+ifeq ($(CONFIG_KALLSYMS_STRIP_GENERATED),y)
+strip-ext := .stripped
+endif
+
+.tmp_kallsyms%.S: .tmp_vmlinux%$(strip-ext) $(KALLSYMS) $(kallsyms.h)
$(call cmd,kallsyms)
+# make -jN seems to have problems with intermediate files, see bug #3330.
+.SECONDARY: $(foreach n,1 2 3,.tmp_vmlinux$(n).stripped)
+.tmp_vmlinux%.stripped: .tmp_vmlinux% $(strip-symbols) $(kallsyms.h)
+ $(call cmd,kstrip)
+
+ifneq ($(CONFIG_DEBUG_INFO),y)
+.tmp_vmlinux%: LDFLAGS_vmlinux += -S
+endif
# .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version
-.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE
- $(call if_changed_rule,ksym_ld)
+.tmp_vmlinux%: $(vmlinux-lds) $(vmlinux-all) FORCE
+ $(if $(filter 1,$*),$(call if_changed_rule,ksym_ld),$(call if_changed,vmlinux__))
-.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE
- $(call if_changed,vmlinux__)
+.tmp_vmlinux0$(strip-ext):
+ $(Q)echo "placeholder" >$@
-.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE
- $(call if_changed,vmlinux__)
+.tmp_vmlinux1: .tmp_kallsyms0.o
+.tmp_vmlinux2: .tmp_kallsyms1.o
+.tmp_vmlinux3: .tmp_kallsyms2.o
# Needs to visit scripts/ before $(KALLSYMS) can be used.
$(KALLSYMS): scripts ;
@@ -926,7 +954,7 @@ PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
# 2) Create the include2 directory, used for the second asm symlink
prepare3: include/config/kernel.release
ifneq ($(KBUILD_SRC),)
- @echo ' Using $(srctree) as source for kernel'
+ @$(kecho) ' Using $(srctree) as source for kernel'
$(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \
echo " $(srctree) is not clean, please run 'make mrproper'";\
echo " in the '$(srctree)' directory.";\
@@ -983,7 +1011,7 @@ endef
# directory for generated filesas used by some architectures.
define create-symlink
if [ ! -L include/asm ]; then \
- echo ' SYMLINK $@ -> include/asm-$(SRCARCH)'; \
+ $(kecho) ' SYMLINK $@ -> include/asm-$(SRCARCH)'; \
if [ ! -d include/asm-$(SRCARCH) ]; then \
mkdir -p include/asm-$(SRCARCH); \
fi; \
@@ -1022,6 +1050,10 @@ include/linux/version.h: $(srctree)/Makefile FORCE
include/linux/utsrelease.h: include/config/kernel.release FORCE
$(call filechk,utsrelease.h)
+PHONY += headerdep
+headerdep:
+ $(Q)find include/ -name '*.h' | xargs --max-args 1 scripts/headerdep.pl
+
# ---------------------------------------------------------------------------
PHONY += depend dep
@@ -1096,7 +1128,7 @@ all: modules
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux)
$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
- @echo ' Building modules, stage 2.';
+ @$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
@@ -1270,7 +1302,8 @@ help:
@echo ' versioncheck - Sanity check on version.h usage'
@echo ' includecheck - Check for duplicate included header files'
@echo ' export_report - List the usages of all exported symbols'
- @echo ' headers_check - Sanity check on exported headers'; \
+ @echo ' headers_check - Sanity check on exported headers'
+ @echo ' headerdep - Detect inclusion cycles in headers'; \
echo ''
@echo 'Kernel packaging:'
@$(MAKE) $(build)=$(package-dir) help
@@ -1360,7 +1393,7 @@ $(module-dirs): crmodverdir $(objtree)/Module.symvers
$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
modules: $(module-dirs)
- @echo ' Building modules, stage 2.';
+ @$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
PHONY += modules_install
@@ -1409,123 +1442,12 @@ endif # KBUILD_EXTMOD
# Generate tags for editors
# ---------------------------------------------------------------------------
+quiet_cmd_tags = GEN $@
+ cmd_tags = $(CONFIG_SHELL) $(srctree)/scripts/tags.sh $@
-#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set
-#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file.
-#Adding $(srctree) adds about 20M on i386 to the size of the output file!
-
-ifeq ($(src),$(obj))
-__srctree =
-else
-__srctree = $(srctree)/
-endif
-
-ifeq ($(ALLSOURCE_ARCHS),)
-ifeq ($(ARCH),um)
-ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH)
-else
-ALLINCLUDE_ARCHS := $(SRCARCH)
-endif
-else
-#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour.
-ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS)
-endif
-
-ALLSOURCE_ARCHS := $(SRCARCH)
-
-define find-sources
- ( for arch in $(ALLSOURCE_ARCHS) ; do \
- find $(__srctree)arch/$${arch} $(RCS_FIND_IGNORE) \
- -wholename $(__srctree)arch/$${arch}/include/asm -type d -prune \
- -o -name $1 -print; \
- done ; \
- find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \
- -name $1 -print; \
- find $(__srctree)include $(RCS_FIND_IGNORE) \
- \( -name config -o -name 'asm-*' \) -prune \
- -o -name $1 -print; \
- for arch in $(ALLINCLUDE_ARCHS) ; do \
- test -e $(__srctree)include/asm-$${arch} && \
- find $(__srctree)include/asm-$${arch} $(RCS_FIND_IGNORE) \
- -name $1 -print; \
- test -e $(__srctree)arch/$${arch}/include/asm && \
- find $(__srctree)arch/$${arch}/include/asm $(RCS_FIND_IGNORE) \
- -name $1 -print; \
- done ; \
- find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
- -name $1 -print; \
- find $(__srctree) $(RCS_FIND_IGNORE) \
- \( -name include -o -name arch -o -name '.tmp_*' \) -prune -o \
- -name $1 -print; \
- )
-endef
-
-define all-sources
- $(call find-sources,'*.[chS]')
-endef
-define all-kconfigs
- $(call find-sources,'Kconfig*')
-endef
-define all-defconfigs
- $(call find-sources,'defconfig')
-endef
-
-define xtags
- if $1 --version 2>&1 | grep -iq exuberant; then \
- $(all-sources) | xargs $1 -a \
- -I __initdata,__exitdata,__acquires,__releases \
- -I __read_mostly,____cacheline_aligned,____cacheline_aligned_in_smp,____cacheline_internodealigned_in_smp \
- -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \
- --extra=+f --c-kinds=+px \
- --regex-asm='/^ENTRY\(([^)]*)\).*/\1/'; \
- $(all-kconfigs) | xargs $1 -a \
- --langdef=kconfig \
- --language-force=kconfig \
- --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/'; \
- $(all-defconfigs) | xargs -r $1 -a \
- --langdef=dotconfig \
- --language-force=dotconfig \
- --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'; \
- elif $1 --version 2>&1 | grep -iq emacs; then \
- $(all-sources) | xargs $1 -a; \
- $(all-kconfigs) | xargs $1 -a \
- --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'; \
- $(all-defconfigs) | xargs -r $1 -a \
- --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'; \
- else \
- $(all-sources) | xargs $1 -a; \
- fi
-endef
-
-quiet_cmd_cscope-file = FILELST cscope.files
- cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files
-
-quiet_cmd_cscope = MAKE cscope.out
- cmd_cscope = cscope -b -f cscope.out
-
-cscope: FORCE
- $(call cmd,cscope-file)
- $(call cmd,cscope)
-
-quiet_cmd_TAGS = MAKE $@
-define cmd_TAGS
- rm -f $@; \
- $(call xtags,etags)
-endef
-
-TAGS: FORCE
- $(call cmd,TAGS)
-
-quiet_cmd_tags = MAKE $@
-define cmd_tags
- rm -f $@; \
- $(call xtags,ctags)
-endef
-
-tags: FORCE
+tags TAGS cscope: FORCE
$(call cmd,tags)
-
# Scripts to check various things for consistency
# ---------------------------------------------------------------------------
@@ -1604,7 +1526,11 @@ endif
$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
# Modules
-/ %/: prepare scripts FORCE
+/: prepare scripts FORCE
+ $(cmd_crmodverdir)
+ $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
+ $(build)=$(build-dir)
+%/: prepare scripts FORCE
$(cmd_crmodverdir)
$(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \
$(build)=$(build-dir)
@@ -1638,7 +1564,7 @@ cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) \
$(if $(KBUILD_MODULES),; rm -f $(MODVERDIR)/*)
a_flags = -Wp,-MD,$(depfile) $(KBUILD_AFLAGS) $(AFLAGS_KERNEL) \
- $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
+ $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(KBUILD_CPPFLAGS) \
$(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(basetarget).o)
quiet_cmd_as_o_S = AS $@
diff --git a/arch/Kconfig b/arch/Kconfig
index 471e72dbaf8b..2e13aa261929 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -6,6 +6,8 @@ config OPROFILE
tristate "OProfile system profiling (EXPERIMENTAL)"
depends on PROFILING
depends on HAVE_OPROFILE
+ select TRACING
+ select RING_BUFFER
help
OProfile is a profiling system capable of profiling the
whole system, include the kernel, kernel modules, libraries,
diff --git a/arch/alpha/include/asm/io.h b/arch/alpha/include/asm/io.h
index e971ab000f95..eda9b909aa05 100644
--- a/arch/alpha/include/asm/io.h
+++ b/arch/alpha/include/asm/io.h
@@ -96,9 +96,6 @@ static inline dma_addr_t __deprecated isa_page_to_bus(struct page *page)
return page_to_phys(page);
}
-/* This depends on working iommu. */
-#define BIO_VMERGE_BOUNDARY (alpha_mv.mv_pci_tbi ? PAGE_SIZE : 0)
-
/* Maximum PIO space address supported? */
#define IO_SPACE_LIMIT 0xffff
diff --git a/arch/alpha/include/asm/topology.h b/arch/alpha/include/asm/topology.h
index 149532e162c4..b4f284c72ff3 100644
--- a/arch/alpha/include/asm/topology.h
+++ b/arch/alpha/include/asm/topology.h
@@ -39,7 +39,24 @@ static inline cpumask_t node_to_cpumask(int node)
return node_cpu_mask;
}
+extern struct cpumask node_to_cpumask_map[];
+/* FIXME: This is dumb, recalculating every time. But simple. */
+static const struct cpumask *cpumask_of_node(int node)
+{
+ int cpu;
+
+ cpumask_clear(&node_to_cpumask_map[node]);
+
+ for_each_online_cpu(cpu) {
+ if (cpu_to_node(cpu) == node)
+ cpumask_set_cpu(cpu, node_to_cpumask_map[node]);
+ }
+
+ return &node_to_cpumask_map[node];
+}
+
#define pcibus_to_cpumask(bus) (cpu_online_map)
+#define cpumask_of_pcibus(bus) (cpu_online_mask)
#endif /* !CONFIG_NUMA */
# include <asm-generic/topology.h>
diff --git a/arch/alpha/kernel/asm-offsets.c b/arch/alpha/kernel/asm-offsets.c
index 4b18cd94d59d..6ff8886e7e22 100644
--- a/arch/alpha/kernel/asm-offsets.c
+++ b/arch/alpha/kernel/asm-offsets.c
@@ -19,15 +19,18 @@ void foo(void)
BLANK();
DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked));
- DEFINE(TASK_UID, offsetof(struct task_struct, uid));
- DEFINE(TASK_EUID, offsetof(struct task_struct, euid));
- DEFINE(TASK_GID, offsetof(struct task_struct, gid));
- DEFINE(TASK_EGID, offsetof(struct task_struct, egid));
+ DEFINE(TASK_CRED, offsetof(struct task_struct, cred));
DEFINE(TASK_REAL_PARENT, offsetof(struct task_struct, real_parent));
DEFINE(TASK_GROUP_LEADER, offsetof(struct task_struct, group_leader));
DEFINE(TASK_TGID, offsetof(struct task_struct, tgid));
BLANK();
+ DEFINE(CRED_UID, offsetof(struct cred, uid));
+ DEFINE(CRED_EUID, offsetof(struct cred, euid));
+ DEFINE(CRED_GID, offsetof(struct cred, gid));
+ DEFINE(CRED_EGID, offsetof(struct cred, egid));
+ BLANK();
+
DEFINE(SIZEOF_PT_REGS, sizeof(struct pt_regs));
DEFINE(PT_PTRACED, PT_PTRACED);
DEFINE(CLONE_VM, CLONE_VM);
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 5fc61e281ac7..f77345bc66a9 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -850,8 +850,9 @@ osf_getpriority:
sys_getxuid:
.prologue 0
ldq $2, TI_TASK($8)
- ldl $0, TASK_UID($2)
- ldl $1, TASK_EUID($2)
+ ldq $3, TASK_CRED($2)
+ ldl $0, CRED_UID($3)
+ ldl $1, CRED_EUID($3)
stq $1, 80($sp)
ret
.end sys_getxuid
@@ -862,8 +863,9 @@ sys_getxuid:
sys_getxgid:
.prologue 0
ldq $2, TI_TASK($8)
- ldl $0, TASK_GID($2)
- ldl $1, TASK_EGID($2)
+ ldq $3, TASK_CRED($2)
+ ldl $0, CRED_GID($3)
+ ldl $1, CRED_EGID($3)
stq $1, 80($sp)
ret
.end sys_getxgid
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index d0f1620007f7..703731accda6 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -50,7 +50,8 @@ int irq_select_affinity(unsigned int irq)
if (!irq_desc[irq].chip->set_affinity || irq_user_affinity[irq])
return 1;
- while (!cpu_possible(cpu) || !cpu_isset(cpu, irq_default_affinity))
+ while (!cpu_possible(cpu) ||
+ !cpumask_test_cpu(cpu, irq_default_affinity))
cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0);
last_cpu = cpu;
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index a449e999027c..02bee6983ce2 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -79,6 +79,11 @@ int alpha_l3_cacheshape;
unsigned long alpha_verbose_mcheck = CONFIG_VERBOSE_MCHECK_ON;
#endif
+#ifdef CONFIG_NUMA
+struct cpumask node_to_cpumask_map[MAX_NUMNODES] __read_mostly;
+EXPORT_SYMBOL(node_to_cpumask_map);
+#endif
+
/* Which processor we booted from. */
int boot_cpuid;
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9722f8bb506c..d6ebe39934b5 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -156,7 +156,6 @@ config ARCH_MTD_XIP
bool
config GENERIC_HARDIRQS_NO__DO_IRQ
- bool
def_bool y
if OPROFILE
@@ -201,6 +200,7 @@ choice
config ARCH_AAEC2000
bool "Agilent AAEC-2000 based"
+ select CPU_ARM920T
select ARM_AMBA
select HAVE_CLK
help
@@ -210,6 +210,7 @@ config ARCH_INTEGRATOR
bool "ARM Ltd. Integrator family"
select ARM_AMBA
select HAVE_CLK
+ select COMMON_CLKDEV
select ICST525
help
Support for ARM's Integrator platform.
@@ -218,6 +219,7 @@ config ARCH_REALVIEW
bool "ARM Ltd. RealView family"
select ARM_AMBA
select HAVE_CLK
+ select COMMON_CLKDEV
select ICST307
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -229,6 +231,7 @@ config ARCH_VERSATILE
select ARM_AMBA
select ARM_VIC
select HAVE_CLK
+ select COMMON_CLKDEV
select ICST307
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -243,22 +246,15 @@ config ARCH_AT91
This enables support for systems based on the Atmel AT91RM9200,
AT91SAM9 and AT91CAP9 processors.
-config ARCH_CLPS7500
- bool "Cirrus CL-PS7500FE"
- select TIMER_ACORN
- select ISA
- select NO_IOPORT
- select ARCH_SPARSEMEM_ENABLE
- help
- Support for the Cirrus Logic PS7500FE system-on-a-chip.
-
config ARCH_CLPS711X
bool "Cirrus Logic CLPS711x/EP721x-based"
+ select CPU_ARM720T
help
Support for Cirrus Logic 711x/721x based boards.
config ARCH_EBSA110
bool "EBSA-110"
+ select CPU_SA110
select ISA
select NO_IOPORT
help
@@ -269,16 +265,19 @@ config ARCH_EBSA110
config ARCH_EP93XX
bool "EP93xx-based"
+ select CPU_ARM920T
select ARM_AMBA
select ARM_VIC
select GENERIC_GPIO
select HAVE_CLK
+ select COMMON_CLKDEV
select ARCH_REQUIRE_GPIOLIB
help
This enables support for the Cirrus EP93xx series of CPUs.
config ARCH_FOOTBRIDGE
bool "FootBridge"
+ select CPU_SA110
select FOOTBRIDGE
help
Support for systems based on the DC21285 companion chip
@@ -286,18 +285,23 @@ config ARCH_FOOTBRIDGE
config ARCH_NETX
bool "Hilscher NetX based"
+ select CPU_ARM926T
select ARM_VIC
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_TIME
help
This enables support for systems based on the Hilscher NetX Soc
config ARCH_H720X
bool "Hynix HMS720x-based"
+ select CPU_ARM720T
select ISA_DMA_API
help
This enables support for systems based on the Hynix HMS720x
config ARCH_IMX
bool "IMX"
+ select CPU_ARM920T
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -307,6 +311,7 @@ config ARCH_IMX
config ARCH_IOP13XX
bool "IOP13xx-based"
depends on MMU
+ select CPU_XSC3
select PLAT_IOP
select PCI
select ARCH_SUPPORTS_MSI
@@ -317,6 +322,7 @@ config ARCH_IOP13XX
config ARCH_IOP32X
bool "IOP32x-based"
depends on MMU
+ select CPU_XSCALE
select PLAT_IOP
select PCI
select GENERIC_GPIO
@@ -328,6 +334,7 @@ config ARCH_IOP32X
config ARCH_IOP33X
bool "IOP33x-based"
depends on MMU
+ select CPU_XSCALE
select PLAT_IOP
select PCI
select GENERIC_GPIO
@@ -338,6 +345,7 @@ config ARCH_IOP33X
config ARCH_IXP23XX
bool "IXP23XX-based"
depends on MMU
+ select CPU_XSC3
select PCI
help
Support for Intel's IXP23xx (XScale) family of processors.
@@ -345,6 +353,7 @@ config ARCH_IXP23XX
config ARCH_IXP2000
bool "IXP2400/2800-based"
depends on MMU
+ select CPU_XSCALE
select PCI
help
Support for Intel's IXP2400/2800 (XScale) family of processors.
@@ -352,6 +361,7 @@ config ARCH_IXP2000
config ARCH_IXP4XX
bool "IXP4xx-based"
depends on MMU
+ select CPU_XSCALE
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -361,6 +371,7 @@ config ARCH_IXP4XX
config ARCH_L7200
bool "LinkUp-L7200"
+ select CPU_ARM720T
select FIQ
help
Say Y here if you intend to run this kernel on a LinkUp Systems
@@ -374,7 +385,9 @@ config ARCH_L7200
config ARCH_KIRKWOOD
bool "Marvell Kirkwood"
+ select CPU_FEROCEON
select PCI
+ select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
select PLAT_ORION
@@ -384,13 +397,16 @@ config ARCH_KIRKWOOD
config ARCH_KS8695
bool "Micrel/Kendin KS8695"
+ select CPU_ARM922T
select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
help
Support for Micrel/Kendin KS8695 "Centaur" (ARM922T) based
System-on-Chip devices.
config ARCH_NS9XXX
bool "NetSilicon NS9xxx"
+ select CPU_ARM926T
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -403,6 +419,7 @@ config ARCH_NS9XXX
config ARCH_LOKI
bool "Marvell Loki (88RC8480)"
+ select CPU_FEROCEON
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
select PLAT_ORION
@@ -411,7 +428,9 @@ config ARCH_LOKI
config ARCH_MV78XX0
bool "Marvell MV78xx0"
+ select CPU_FEROCEON
select PCI
+ select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
select PLAT_ORION
@@ -432,6 +451,7 @@ config ARCH_MXC
config ARCH_ORION5X
bool "Marvell Orion"
depends on MMU
+ select CPU_FEROCEON
select PCI
select GENERIC_GPIO
select GENERIC_TIME
@@ -444,6 +464,7 @@ config ARCH_ORION5X
config ARCH_PNX4008
bool "Philips Nexperia PNX4008 Mobile"
+ select CPU_ARM926T
select HAVE_CLK
help
This enables support for Philips PNX4008 mobile platform.
@@ -454,6 +475,7 @@ config ARCH_PXA
select ARCH_MTD_XIP
select GENERIC_GPIO
select HAVE_CLK
+ select COMMON_CLKDEV
select ARCH_REQUIRE_GPIOLIB
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -477,6 +499,7 @@ config ARCH_RPC
config ARCH_SA1100
bool "SA1100-based"
+ select CPU_SA1100
select ISA
select ARCH_SPARSEMEM_ENABLE
select ARCH_MTD_XIP
@@ -498,8 +521,16 @@ config ARCH_S3C2410
BAST (<http://www.simtec.co.uk/products/EB110ITX/>), the IPAQ 1940 or
the Samsung SMDK2410 development board (and derivatives).
+config ARCH_S3C64XX
+ bool "Samsung S3C64XX"
+ select GENERIC_GPIO
+ select HAVE_CLK
+ help
+ Samsung S3C64XX series based systems
+
config ARCH_SHARK
bool "Shark"
+ select CPU_SA110
select ISA
select ISA_DMA
select ZONE_DMA
@@ -510,6 +541,7 @@ config ARCH_SHARK
config ARCH_LH7A40X
bool "Sharp LH7A40X"
+ select CPU_ARM922T
select ARCH_DISCONTIGMEM_ENABLE if !LH7A40X_CONTIGMEM
select ARCH_SPARSEMEM_ENABLE if !LH7A40X_CONTIGMEM
help
@@ -520,6 +552,7 @@ config ARCH_LH7A40X
config ARCH_DAVINCI
bool "TI DaVinci"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
select GENERIC_GPIO
@@ -541,6 +574,7 @@ config ARCH_OMAP
config ARCH_MSM
bool "Qualcomm MSM"
+ select CPU_V6
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
help
@@ -549,6 +583,13 @@ config ARCH_MSM
interface to the ARM9 modem processor which runs the baseband stack
and controls some vital subsystems (clock and power control, etc).
+config ARCH_W90X900
+ bool "Nuvoton W90X900 CPU"
+ select CPU_ARM926T
+ help
+ Support for Nuvoton (Winbond logic dept.) ARM9 processor,You
+ can login www.mcuos.com or www.nuvoton.com to know more.
+
endchoice
source "arch/arm/mach-clps711x/Kconfig"
@@ -590,6 +631,7 @@ source "arch/arm/mach-orion5x/Kconfig"
source "arch/arm/mach-kirkwood/Kconfig"
source "arch/arm/plat-s3c24xx/Kconfig"
+source "arch/arm/plat-s3c64xx/Kconfig"
source "arch/arm/plat-s3c/Kconfig"
if ARCH_S3C2410
@@ -601,6 +643,11 @@ source "arch/arm/mach-s3c2442/Kconfig"
source "arch/arm/mach-s3c2443/Kconfig"
endif
+if ARCH_S3C64XX
+source "arch/arm/mach-s3c6400/Kconfig"
+source "arch/arm/mach-s3c6410/Kconfig"
+endif
+
source "arch/arm/mach-lh7a40x/Kconfig"
source "arch/arm/mach-imx/Kconfig"
@@ -627,6 +674,8 @@ source "arch/arm/mach-ks8695/Kconfig"
source "arch/arm/mach-msm/Kconfig"
+source "arch/arm/mach-w90x900/Kconfig"
+
# Definitions to make life easier
config ARCH_ACORN
bool
@@ -781,7 +830,7 @@ config HOTPLUG_CPU
config LOCAL_TIMERS
bool "Use local timer interrupts"
- depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP)
+ depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
default y
help
Enable support for local timers on SMP platforms, rather then the
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index bd6e28115ebb..24e0f0187697 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -76,6 +76,7 @@ tune-$(CONFIG_CPU_SA110) :=-mtune=strongarm110
tune-$(CONFIG_CPU_SA1100) :=-mtune=strongarm1100
tune-$(CONFIG_CPU_XSCALE) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale
tune-$(CONFIG_CPU_XSC3) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale
+tune-$(CONFIG_CPU_FEROCEON) :=$(call cc-option,-mtune=marvell-f,-mtune=xscale)
tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm)
ifeq ($(CONFIG_AEABI),y)
@@ -96,7 +97,6 @@ textofs-y := 0x00008000
machine-$(CONFIG_ARCH_RPC) := rpc
machine-$(CONFIG_ARCH_EBSA110) := ebsa110
- machine-$(CONFIG_ARCH_CLPS7500) := clps7500
machine-$(CONFIG_FOOTBRIDGE) := footbridge
machine-$(CONFIG_ARCH_SHARK) := shark
machine-$(CONFIG_ARCH_SA1100) := sa1100
@@ -121,7 +121,10 @@ endif
machine-$(CONFIG_ARCH_OMAP3) := omap2
plat-$(CONFIG_ARCH_OMAP) := omap
machine-$(CONFIG_ARCH_S3C2410) := s3c2410 s3c2400 s3c2412 s3c2440 s3c2442 s3c2443
+ machine-$(CONFIG_ARCH_S3C24A0) := s3c24a0
plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx s3c
+ machine-$(CONFIG_ARCH_S3C64XX) := s3c6400 s3c6410
+ plat-$(CONFIG_PLAT_S3C64XX) := s3c64xx s3c
machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x
machine-$(CONFIG_ARCH_VERSATILE) := versatile
machine-$(CONFIG_ARCH_IMX) := imx
@@ -139,11 +142,13 @@ endif
plat-$(CONFIG_ARCH_MXC) := mxc
machine-$(CONFIG_ARCH_MX2) := mx2
machine-$(CONFIG_ARCH_MX3) := mx3
+ machine-$(CONFIG_ARCH_MX1) := mx1
machine-$(CONFIG_ARCH_ORION5X) := orion5x
plat-$(CONFIG_PLAT_ORION) := orion
machine-$(CONFIG_ARCH_MSM) := msm
machine-$(CONFIG_ARCH_LOKI) := loki
machine-$(CONFIG_ARCH_MV78XX0) := mv78xx0
+ machine-$(CONFIG_ARCH_W90X900) := w90x900
ifeq ($(CONFIG_ARCH_EBSA110),y)
# This is what happens if you forget the IOCS16 line.
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index c47f2a3f8f8f..fbe5eef1f6c9 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -23,10 +23,6 @@ ifeq ($(CONFIG_ARCH_L7200),y)
OBJS += head-l7200.o
endif
-ifeq ($(CONFIG_ARCH_CLPS7500),y)
-HEAD = head-clps7500.o
-endif
-
ifeq ($(CONFIG_ARCH_P720T),y)
# Borrow this code from SA1100
OBJS += head-sa1100.o
diff --git a/arch/arm/boot/compressed/head-clps7500.S b/arch/arm/boot/compressed/head-clps7500.S
deleted file mode 100644
index 4f3c78ac30a0..000000000000
--- a/arch/arm/boot/compressed/head-clps7500.S
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * linux/arch/arm/boot/compressed/head-clps7500.S
- *
- * Copyright (C) 1999, 2000, 2001 Nexus Electronics Ltd
- */
-
-
- /* There are three different ways the kernel can be
- booted on a 7500 system: from Angel (loaded in RAM), from
- 16-bit ROM or from 32-bit Flash. Luckily, a single kernel
- image does for them all. */
- /* This branch is taken if the CPU memory width matches the
- actual device in use. The default at power on is 16 bits
- so we must be prepared for a mismatch. */
- .section ".start", "ax"
-2:
- b 1f
- .word 0xffff
- .word 0xb632 @ mov r11, #0x03200000
- .word 0xe3a0
- .word 0x0000 @ mov r0, #0
- .word 0xe3a0
- .word 0x0080 @ strb r0, [r11, #0x80]
- .word 0xe5cb
- .word 0xf000 @ mov pc, #0
- .word 0xe3a0
-1:
- adr r1, 2b
- teq r1, #0
- bne .Langel
- /* This is a direct-from-ROM boot. Copy the kernel into
- RAM and run it there. */
- mov r0, #0x30
- mcr p15, 0, r0, c1, c0, 0
- mov r0, #0x13
- msr cpsr_cxsf, r0
- mov r12, #0x03000000 @ point to LEDs
- orr r12, r12, #0x00020000
- orr r12, r12, #0xba00
- mov r0, #0x5500
- str r0, [r12]
- mov r0, #0x10000000
- orr r0, r0, #0x8000
- mov r4, r0
- ldr r2, =_end
-2:
- ldr r3, [r1], #4
- str r3, [r0], #4
- teq r0, r2
- bne 2b
- mov r0, #0xff00
- str r0, [r12]
-1:
- mov r12, #0x03000000 @ point to LEDs
- orr r12, r12, #0x00020000
- orr r12, r12, #0xba00
- mov r0, #0xfe00
- str r0, [r12]
-
- adr lr, 1f
- mov r0, #0
- mov r1, #14 /* MACH_TYPE_CLPS7500 */
- mov pc, lr
-.Langel:
-#ifdef CONFIG_ANGELBOOT
- /* Call Angel to switch into SVC mode. */
- mov r0, #0x17
- swi 0x123456
-#endif
- /* Ensure all interrupts are off and MMU disabled */
- mrs r0, cpsr
- orr r0, r0, #0xc0
- msr cpsr_cxsf, r0
-
- adr lr, 1b
- orr lr, lr, #0x10000000
- mov r0, #0x30 @ MMU off
- mcr p15, 0, r0, c1, c0, 0
- mov r0, r0
- mov pc, lr
-
- .ltorg
-
-1:
-/* And the rest */
-#include "head.S"
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 84a1e0496a3c..77d614232d81 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -624,6 +624,12 @@ proc_types:
b __armv4_mmu_cache_off
b __armv4_mmu_cache_flush
+ .word 0x56056930
+ .word 0xff0ffff0 @ PXA935
+ b __armv4_mmu_cache_on
+ b __armv4_mmu_cache_off
+ b __armv4_mmu_cache_flush
+
.word 0x56050000 @ Feroceon
.word 0xff0f0000
b __armv4_mmu_cache_on
@@ -717,6 +723,9 @@ __armv7_mmu_cache_off:
bl __armv7_mmu_cache_flush
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 @ invalidate whole TLB
+ mcr p15, 0, r0, c7, c5, 6 @ invalidate BTC
+ mcr p15, 0, r0, c7, c10, 4 @ DSB
+ mcr p15, 0, r0, c7, c5, 4 @ ISB
mov pc, r12
__arm6_mmu_cache_off:
@@ -778,12 +787,13 @@ __armv6_mmu_cache_flush:
__armv7_mmu_cache_flush:
mrc p15, 0, r10, c0, c1, 5 @ read ID_MMFR1
tst r10, #0xf << 16 @ hierarchical cache (ARMv7)
- beq hierarchical
mov r10, #0
+ beq hierarchical
mcr p15, 0, r10, c7, c14, 0 @ clean+invalidate D
b iflush
hierarchical:
- stmfd sp!, {r0-r5, r7, r9-r11}
+ mcr p15, 0, r10, c7, c10, 5 @ DMB
+ stmfd sp!, {r0-r5, r7, r9, r11}
mrc p15, 1, r0, c0, c0, 1 @ read clidr
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
@@ -820,12 +830,14 @@ skip:
cmp r3, r10
bgt loop1
finished:
+ ldmfd sp!, {r0-r5, r7, r9, r11}
mov r10, #0 @ swith back to cache level 0
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
- ldmfd sp!, {r0-r5, r7, r9-r11}
iflush:
+ mcr p15, 0, r10, c7, c10, 4 @ DSB
mcr p15, 0, r10, c7, c5, 0 @ invalidate I+BTB
- mcr p15, 0, r10, c7, c10, 4 @ drain WB
+ mcr p15, 0, r10, c7, c10, 4 @ DSB
+ mcr p15, 0, r10, c7, c5, 4 @ ISB
mov pc, lr
__armv5tej_mmu_cache_flush:
diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c
index 65ce8fff29db..3fc08413fff0 100644
--- a/arch/arm/boot/compressed/misc.c
+++ b/arch/arm/boot/compressed/misc.c
@@ -86,6 +86,8 @@ static void putstr(const char *ptr)
#define __ptr_t void *
+#define memzero(s,n) __memzero(s,n)
+
/*
* Optimised C version of memzero for the ARM.
*/
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
index 86b5e6982660..a2cd9beaf37d 100644
--- a/arch/arm/common/Kconfig
+++ b/arch/arm/common/Kconfig
@@ -33,3 +33,6 @@ config SHARPSL_PM
config SHARP_SCOOP
bool
+
+config COMMON_CLKDEV
+ bool
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 325e4b6a6afb..7cb7961d81cb 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
obj-$(CONFIG_ARCH_IXP2000) += uengine.o
obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
+obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
new file mode 100644
index 000000000000..17a17b49a45b
--- /dev/null
+++ b/arch/arm/common/clkdev.c
@@ -0,0 +1,128 @@
+/*
+ * arch/arm/common/clkdev.c
+ *
+ * Copyright (C) 2008 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.
+ *
+ * Helper for the clk API to assist looking up a struct clk.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+
+#include <asm/clkdev.h>
+#include <mach/clkdev.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+static struct clk *clk_find(const char *dev_id, const char *con_id)
+{
+ struct clk_lookup *p;
+ struct clk *clk = NULL;
+ int match, best = 0;
+
+ list_for_each_entry(p, &clocks, node) {
+ if ((p->dev_id && !dev_id) || (p->con_id && !con_id))
+ continue;
+ match = 0;
+ if (p->dev_id)
+ match += 2 * (strcmp(p->dev_id, dev_id) == 0);
+ if (p->con_id)
+ match += 1 * (strcmp(p->con_id, con_id) == 0);
+ if (match == 0)
+ continue;
+
+ if (match > best) {
+ clk = p->clk;
+ best = match;
+ }
+ }
+ return clk;
+}
+
+struct clk *clk_get(struct device *dev, const char *con_id)
+{
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct clk *clk;
+
+ mutex_lock(&clocks_mutex);
+ clk = clk_find(dev_id, con_id);
+ if (clk && !__clk_get(clk))
+ clk = NULL;
+ mutex_unlock(&clocks_mutex);
+
+ return clk ? clk : ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ __clk_put(clk);
+}
+EXPORT_SYMBOL(clk_put);
+
+void clkdev_add(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_add_tail(&cl->node, &clocks);
+ mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL(clkdev_add);
+
+#define MAX_DEV_ID 20
+#define MAX_CON_ID 16
+
+struct clk_lookup_alloc {
+ struct clk_lookup cl;
+ char dev_id[MAX_DEV_ID];
+ char con_id[MAX_CON_ID];
+};
+
+struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
+ const char *dev_fmt, ...)
+{
+ struct clk_lookup_alloc *cla;
+
+ cla = kzalloc(sizeof(*cla), GFP_KERNEL);
+ if (!cla)
+ return NULL;
+
+ cla->cl.clk = clk;
+ if (con_id) {
+ strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
+ cla->cl.con_id = cla->con_id;
+ }
+
+ if (dev_fmt) {
+ va_list ap;
+
+ va_start(ap, dev_fmt);
+ vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
+ cla->cl.dev_id = cla->dev_id;
+ va_end(ap);
+ }
+
+ return &cla->cl;
+}
+EXPORT_SYMBOL(clkdev_alloc);
+
+/*
+ * clkdev_drop - remove a clock dynamically allocated
+ */
+void clkdev_drop(struct clk_lookup *cl)
+{
+ mutex_lock(&clocks_mutex);
+ list_del(&cl->node);
+ mutex_unlock(&clocks_mutex);
+ kfree(cl);
+}
+EXPORT_SYMBOL(clkdev_drop);
diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c
index 7c6b4b99a2df..2293f0ce061e 100644
--- a/arch/arm/common/locomo.c
+++ b/arch/arm/common/locomo.c
@@ -1108,6 +1108,7 @@ void locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf)
locomo_writel(bpwf | LOCOMO_ALC_EN, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
spin_unlock_irqrestore(&lchip->lock, flags);
}
+EXPORT_SYMBOL(locomo_frontlight_set);
/*
* LoCoMo "Register Access Bus."
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
index f1e4b8f60cab..ecf0bfbab107 100644
--- a/arch/arm/common/vic.c
+++ b/arch/arm/common/vic.c
@@ -69,12 +69,12 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
/*
* Make sure we clear all existing interrupts
*/
- writel(0, base + VIC_VECT_ADDR);
+ writel(0, base + VIC_PL190_VECT_ADDR);
for (i = 0; i < 19; i++) {
unsigned int value;
- value = readl(base + VIC_VECT_ADDR);
- writel(value, base + VIC_VECT_ADDR);
+ value = readl(base + VIC_PL190_VECT_ADDR);
+ writel(value, base + VIC_PL190_VECT_ADDR);
}
for (i = 0; i < 16; i++) {
@@ -82,7 +82,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
writel(VIC_VECT_CNTL_ENABLE | i, reg);
}
- writel(32, base + VIC_DEF_VECT_ADDR);
+ writel(32, base + VIC_PL190_DEF_VECT_ADDR);
for (i = 0; i < 32; i++) {
unsigned int irq = irq_start + i;
diff --git a/arch/arm/configs/eseries_pxa_defconfig b/arch/arm/configs/eseries_pxa_defconfig
index 2307587a38a9..b6c5cbbf4c85 100644
--- a/arch/arm/configs/eseries_pxa_defconfig
+++ b/arch/arm/configs/eseries_pxa_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.26
-# Sat Jul 26 22:28:46 2008
+# Linux kernel version: 2.6.28-rc8
+# Wed Dec 24 23:35:45 2008
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -22,8 +22,6 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_ARCH_SUPPORTS_AOUT=y
-CONFIG_ZONE_DMA=y
CONFIG_ARCH_MTD_XIP=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
@@ -48,8 +46,7 @@ CONFIG_SYSVIPC_SYSCTL=y
CONFIG_LOG_BUF_SHIFT=14
# CONFIG_CGROUPS is not set
# CONFIG_GROUP_SCHED is not set
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
# CONFIG_RELAY is not set
# CONFIG_NAMESPACES is not set
# CONFIG_BLK_DEV_INITRD is not set
@@ -58,13 +55,12 @@ CONFIG_SYSCTL=y
CONFIG_EMBEDDED=y
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
# CONFIG_KALLSYMS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
-CONFIG_COMPAT_BRK=y
+# CONFIG_COMPAT_BRK is not set
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_ANON_INODES=y
@@ -73,6 +69,7 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
@@ -80,15 +77,10 @@ CONFIG_SLAB=y
# CONFIG_PROFILING is not set
# CONFIG_MARKERS is not set
CONFIG_HAVE_OPROFILE=y
-# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
-# CONFIG_HAVE_IOREMAP_PROT is not set
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
-# CONFIG_HAVE_ARCH_TRACEHOOK is not set
-# CONFIG_HAVE_DMA_ATTRS is not set
-# CONFIG_USE_GENERIC_SMP_HELPERS is not set
CONFIG_HAVE_CLK=y
-CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
@@ -112,14 +104,15 @@ CONFIG_BLOCK=y
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
-CONFIG_IOSCHED_DEADLINE=y
-CONFIG_IOSCHED_CFQ=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
CONFIG_DEFAULT_AS=y
# CONFIG_DEFAULT_DEADLINE is not set
# CONFIG_DEFAULT_CFQ is not set
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="anticipatory"
CONFIG_CLASSIC_RCU=y
+CONFIG_FREEZER=y
#
# System Type
@@ -129,7 +122,6 @@ CONFIG_CLASSIC_RCU=y
# CONFIG_ARCH_REALVIEW is not set
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_AT91 is not set
-# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
@@ -160,7 +152,8 @@ CONFIG_ARCH_PXA=y
# CONFIG_ARCH_LH7A40X is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM7X00A is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_W90X900 is not set
#
# Intel PXA2xx/PXA3xx Implementations
@@ -169,8 +162,10 @@ CONFIG_ARCH_PXA=y
# CONFIG_ARCH_LUBBOCK is not set
# CONFIG_MACH_LOGICPD_PXA270 is not set
# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_MACH_MP900C is not set
# CONFIG_ARCH_PXA_IDP is not set
# CONFIG_PXA_SHARPSL is not set
+# CONFIG_ARCH_VIPER is not set
CONFIG_ARCH_PXA_ESERIES=y
CONFIG_MACH_E330=y
CONFIG_MACH_E350=y
@@ -178,7 +173,8 @@ CONFIG_MACH_E740=y
CONFIG_MACH_E750=y
CONFIG_MACH_E400=y
CONFIG_MACH_E800=y
-# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_TRIZEPS_PXA is not set
+# CONFIG_MACH_H5000 is not set
# CONFIG_MACH_EM_X270 is not set
# CONFIG_MACH_COLIBRI is not set
# CONFIG_MACH_ZYLONITE is not set
@@ -186,12 +182,15 @@ CONFIG_MACH_E800=y
# CONFIG_MACH_TAVOREVB is not set
# CONFIG_MACH_SAAR is not set
# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_CM_X300 is not set
# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_MIOA701 is not set
# CONFIG_MACH_PCM027 is not set
# CONFIG_ARCH_PXA_PALM is not set
# CONFIG_PXA_EZX is not set
CONFIG_PXA25x=y
# CONFIG_PXA_PWM is not set
+CONFIG_PXA_HAVE_BOARD_IRQS=y
#
# Boot options
@@ -222,6 +221,7 @@ CONFIG_CPU_CP15_MMU=y
# CONFIG_OUTER_CACHE is not set
CONFIG_IWMMXT=y
CONFIG_XSCALE_PMU=y
+CONFIG_COMMON_CLKDEV=y
#
# Bus support
@@ -237,6 +237,7 @@ CONFIG_PCMCIA_IOCTL=y
#
# PC-card bridges
#
+CONFIG_PCMCIA_PXA2XX=m
#
# Kernel Features
@@ -245,25 +246,30 @@ CONFIG_TICK_ONESHOT=y
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
# CONFIG_PREEMPT is not set
CONFIG_HZ=100
CONFIG_AEABI=y
CONFIG_OABI_COMPAT=y
-# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
-# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4096
# CONFIG_RESOURCES_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=1
-CONFIG_BOUNCE=y
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
CONFIG_ALIGNMENT_TRAP=y
#
@@ -277,9 +283,10 @@ CONFIG_KEXEC=y
CONFIG_ATAGS_PROC=y
#
-# CPU Frequency scaling
+# CPU Power Management
#
# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
#
# Floating point emulation
@@ -296,6 +303,8 @@ CONFIG_FPE_NWFPE=y
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
CONFIG_BINFMT_MISC=y
@@ -309,10 +318,6 @@ CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
# CONFIG_APM_EMULATION is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
-
-#
-# Networking
-#
CONFIG_NET=y
#
@@ -339,7 +344,7 @@ CONFIG_IP_FIB_HASH=y
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
-CONFIG_INET_TUNNEL=y
+# CONFIG_INET_TUNNEL is not set
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_XFRM_MODE_BEET=y
@@ -350,25 +355,7 @@ CONFIG_INET_TCP_DIAG=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
-CONFIG_IPV6=y
-# CONFIG_IPV6_PRIVACY is not set
-# CONFIG_IPV6_ROUTER_PREF is not set
-# CONFIG_IPV6_OPTIMISTIC_DAD is not set
-# CONFIG_INET6_AH is not set
-# CONFIG_INET6_ESP is not set
-# CONFIG_INET6_IPCOMP is not set
-# CONFIG_IPV6_MIP6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
-CONFIG_INET6_XFRM_MODE_TRANSPORT=y
-CONFIG_INET6_XFRM_MODE_TUNNEL=y
-CONFIG_INET6_XFRM_MODE_BEET=y
-# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
-CONFIG_IPV6_SIT=y
-CONFIG_IPV6_NDISC_NODETYPE=y
-# CONFIG_IPV6_TUNNEL is not set
-# CONFIG_IPV6_MULTIPLE_TABLES is not set
-# CONFIG_IPV6_MROUTE is not set
+# CONFIG_IPV6 is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
@@ -376,6 +363,7 @@ CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
@@ -421,25 +409,18 @@ CONFIG_IRDA_FAST_RR=y
#
# Dongle support
#
-# CONFIG_KINGSUN_DONGLE is not set
-# CONFIG_KSDAZZLE_DONGLE is not set
-# CONFIG_KS959_DONGLE is not set
#
# FIR device drivers
#
-# CONFIG_USB_IRDA is not set
-# CONFIG_SIGMATEL_FIR is not set
CONFIG_PXA_FICP=y
-# CONFIG_MCS_FIR is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
-
-#
-# Wireless
-#
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
CONFIG_CFG80211=m
CONFIG_NL80211=y
+CONFIG_WIRELESS_OLD_REGULATORY=y
CONFIG_WIRELESS_EXT=y
CONFIG_WIRELESS_EXT_SYSFS=y
CONFIG_MAC80211=m
@@ -448,7 +429,9 @@ CONFIG_MAC80211=m
# Rate control algorithm selection
#
CONFIG_MAC80211_RC_PID=y
+# CONFIG_MAC80211_RC_MINSTREL is not set
CONFIG_MAC80211_RC_DEFAULT_PID=y
+# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set
CONFIG_MAC80211_RC_DEFAULT="pid"
# CONFIG_MAC80211_MESH is not set
# CONFIG_MAC80211_LEDS is not set
@@ -539,11 +522,12 @@ CONFIG_MTD_NAND=m
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_NAND_ECC_SMC is not set
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
CONFIG_MTD_NAND_IDS=m
# CONFIG_MTD_NAND_DISKONCHIP is not set
# CONFIG_MTD_NAND_SHARPSL is not set
+CONFIG_MTD_NAND_TMIO=m
# CONFIG_MTD_NAND_PLATFORM is not set
-# CONFIG_MTD_ALAUDA is not set
# CONFIG_MTD_ONENAND is not set
#
@@ -556,13 +540,13 @@ CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_LOOP=m
# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
-# CONFIG_BLK_DEV_UB is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
CONFIG_MISC_DEVICES=y
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -632,37 +616,25 @@ CONFIG_NETDEVICES=y
CONFIG_WLAN_80211=y
# CONFIG_PCMCIA_RAYCS is not set
# CONFIG_LIBERTAS is not set
+# CONFIG_LIBERTAS_THINFIRM is not set
CONFIG_HERMES=m
CONFIG_PCMCIA_HERMES=m
# CONFIG_PCMCIA_SPECTRUM is not set
# CONFIG_ATMEL is not set
# CONFIG_AIRO_CS is not set
# CONFIG_PCMCIA_WL3501 is not set
-# CONFIG_USB_ZD1201 is not set
-# CONFIG_USB_NET_RNDIS_WLAN is not set
-# CONFIG_RTL8187 is not set
# CONFIG_MAC80211_HWSIM is not set
# CONFIG_P54_COMMON is not set
# CONFIG_IWLWIFI_LEDS is not set
# CONFIG_HOSTAP is not set
# CONFIG_B43 is not set
# CONFIG_B43LEGACY is not set
-# CONFIG_ZD1211RW is not set
# CONFIG_RT2X00 is not set
-
-#
-# USB Network Adapters
-#
-# CONFIG_USB_CATC is not set
-# CONFIG_USB_KAWETH is not set
-# CONFIG_USB_PEGASUS is not set
-# CONFIG_USB_RTL8150 is not set
-# CONFIG_USB_USBNET is not set
CONFIG_NET_PCMCIA=y
# CONFIG_PCMCIA_3C589 is not set
# CONFIG_PCMCIA_3C574 is not set
# CONFIG_PCMCIA_FMVJ18X is not set
-CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_PCNET is not set
# CONFIG_PCMCIA_NMCLAN is not set
# CONFIG_PCMCIA_SMC91C92 is not set
# CONFIG_PCMCIA_XIRC2PS is not set
@@ -714,13 +686,11 @@ CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
-# CONFIG_TOUCHSCREEN_UCB1400 is not set
CONFIG_TOUCHSCREEN_WM97XX=m
CONFIG_TOUCHSCREEN_WM9705=y
CONFIG_TOUCHSCREEN_WM9712=y
CONFIG_TOUCHSCREEN_WM9713=y
# CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE is not set
-# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_INPUT_MISC is not set
@@ -749,11 +719,13 @@ CONFIG_DEVKMEM=y
#
# Non-8250 serial port support
#
-# CONFIG_SERIAL_PXA is not set
+CONFIG_SERIAL_PXA=y
+# CONFIG_SERIAL_PXA_CONSOLE is not set
+CONFIG_SERIAL_CORE=y
CONFIG_UNIX98_PTYS=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_IPMI_HANDLER is not set
-CONFIG_HW_RANDOM=m
+# CONFIG_HW_RANDOM is not set
# CONFIG_NVRAM is not set
# CONFIG_R3964 is not set
@@ -773,6 +745,10 @@ CONFIG_GPIOLIB=y
# CONFIG_GPIO_SYSFS is not set
#
+# Memory mapped GPIO expanders:
+#
+
+#
# I2C GPIO expanders:
#
@@ -786,12 +762,14 @@ CONFIG_GPIOLIB=y
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
@@ -799,8 +777,13 @@ CONFIG_SSB_POSSIBLE=y
#
CONFIG_MFD_CORE=y
# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_EGPIO is not set
# CONFIG_HTC_PASIC3 is not set
+# CONFIG_UCB1400_CORE is not set
+CONFIG_MFD_TMIO=y
+CONFIG_MFD_T7L66XB=y
+CONFIG_MFD_TC6387XB=y
CONFIG_MFD_TC6393XB=y
#
@@ -827,6 +810,7 @@ CONFIG_MFD_TC6393XB=y
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
@@ -851,8 +835,10 @@ CONFIG_FB_PXA=y
# CONFIG_FB_PXA_PARAMETERS is not set
# CONFIG_FB_MBX is not set
CONFIG_FB_W100=y
-# CONFIG_FB_AM200EPD is not set
+# CONFIG_FB_TMIO is not set
# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
# CONFIG_LCD_ILI9320 is not set
@@ -886,6 +872,7 @@ CONFIG_FONT_MINI_4x6=y
# CONFIG_FONT_10x18 is not set
# CONFIG_LOGO is not set
CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
CONFIG_SND=m
CONFIG_SND_TIMER=m
CONFIG_SND_PCM=m
@@ -899,14 +886,18 @@ CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
CONFIG_SND_VERBOSE_PRINTK=y
# CONFIG_SND_DEBUG is not set
+CONFIG_SND_VMASTER=y
+CONFIG_SND_AC97_CODEC=m
CONFIG_SND_DRIVERS=y
# CONFIG_SND_DUMMY is not set
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
CONFIG_SND_ARM=y
+CONFIG_SND_PXA2XX_LIB=m
+CONFIG_SND_PXA2XX_LIB_AC97=y
# CONFIG_SND_PXA2XX_AC97 is not set
-# CONFIG_SND_USB is not set
# CONFIG_SND_PCMCIA is not set
CONFIG_SND_SOC=m
CONFIG_SND_SOC_AC97_BUS=y
@@ -920,133 +911,19 @@ CONFIG_HID_SUPPORT=y
CONFIG_HID=y
# CONFIG_HID_DEBUG is not set
# CONFIG_HIDRAW is not set
+# CONFIG_HID_PID is not set
#
-# USB Input Devices
+# Special HID drivers
#
-CONFIG_USB_HID=m
-# CONFIG_USB_HIDINPUT_POWERBOOK is not set
-# CONFIG_HID_FF is not set
-# CONFIG_USB_HIDDEV is not set
-
-#
-# USB HID Boot Protocol drivers
-#
-# CONFIG_USB_KBD is not set
-# CONFIG_USB_MOUSE is not set
-CONFIG_USB_SUPPORT=y
-CONFIG_USB_ARCH_HAS_HCD=y
-# CONFIG_USB_ARCH_HAS_OHCI is not set
-# CONFIG_USB_ARCH_HAS_EHCI is not set
-CONFIG_USB=m
-# CONFIG_USB_DEBUG is not set
-# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
-
-#
-# Miscellaneous USB options
-#
-# CONFIG_USB_DEVICEFS is not set
-CONFIG_USB_DEVICE_CLASS=y
-# CONFIG_USB_DYNAMIC_MINORS is not set
-# CONFIG_USB_SUSPEND is not set
-# CONFIG_USB_OTG is not set
-# CONFIG_USB_OTG_WHITELIST is not set
-# CONFIG_USB_OTG_BLACKLIST_HUB is not set
-
-#
-# USB Host Controller Drivers
-#
-# CONFIG_USB_C67X00_HCD is not set
-# CONFIG_USB_ISP116X_HCD is not set
-# CONFIG_USB_ISP1760_HCD is not set
-# CONFIG_USB_SL811_HCD is not set
-# CONFIG_USB_R8A66597_HCD is not set
-
-#
-# USB Device Class drivers
-#
-# CONFIG_USB_ACM is not set
-# CONFIG_USB_PRINTER is not set
-# CONFIG_USB_WDM is not set
-
-#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
-#
-
-#
-# may also be needed; see USB_STORAGE Help for more information
-#
-# CONFIG_USB_STORAGE is not set
-# CONFIG_USB_LIBUSUAL is not set
-
-#
-# USB Imaging devices
-#
-# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_MICROTEK is not set
-CONFIG_USB_MON=y
-
-#
-# USB port drivers
-#
-# CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
-# CONFIG_USB_EMI62 is not set
-# CONFIG_USB_EMI26 is not set
-# CONFIG_USB_ADUTUX is not set
-# CONFIG_USB_AUERSWALD is not set
-# CONFIG_USB_RIO500 is not set
-# CONFIG_USB_LEGOTOWER is not set
-# CONFIG_USB_LCD is not set
-# CONFIG_USB_BERRY_CHARGE is not set
-# CONFIG_USB_LED is not set
-# CONFIG_USB_CYPRESS_CY7C63 is not set
-# CONFIG_USB_CYTHERM is not set
-# CONFIG_USB_PHIDGET is not set
-# CONFIG_USB_IDMOUSE is not set
-# CONFIG_USB_FTDI_ELAN is not set
-# CONFIG_USB_APPLEDISPLAY is not set
-# CONFIG_USB_LD is not set
-# CONFIG_USB_TRANCEVIBRATOR is not set
-# CONFIG_USB_IOWARRIOR is not set
-# CONFIG_USB_ISIGHTFW is not set
-CONFIG_USB_GADGET=y
-# CONFIG_USB_GADGET_DEBUG_FILES is not set
-CONFIG_USB_GADGET_SELECTED=y
-# CONFIG_USB_GADGET_AMD5536UDC is not set
-# CONFIG_USB_GADGET_ATMEL_USBA is not set
-# CONFIG_USB_GADGET_FSL_USB2 is not set
-# CONFIG_USB_GADGET_NET2280 is not set
-CONFIG_USB_GADGET_PXA25X=y
-CONFIG_USB_PXA25X=y
-CONFIG_USB_PXA25X_SMALL=y
-# CONFIG_USB_GADGET_M66592 is not set
-# CONFIG_USB_GADGET_PXA27X is not set
-# CONFIG_USB_GADGET_GOKU is not set
-# CONFIG_USB_GADGET_LH7A40X is not set
-# CONFIG_USB_GADGET_OMAP is not set
-# CONFIG_USB_GADGET_S3C2410 is not set
-# CONFIG_USB_GADGET_AT91 is not set
-# CONFIG_USB_GADGET_DUMMY_HCD is not set
-# CONFIG_USB_GADGET_DUALSPEED is not set
-# CONFIG_USB_ZERO is not set
-CONFIG_USB_ETH=m
-# CONFIG_USB_ETH_RNDIS is not set
-# CONFIG_USB_GADGETFS is not set
-# CONFIG_USB_FILE_STORAGE is not set
-# CONFIG_USB_G_SERIAL is not set
-# CONFIG_USB_MIDI_GADGET is not set
-# CONFIG_USB_G_PRINTER is not set
-# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_HID_COMPAT=y
+# CONFIG_USB_SUPPORT is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
CONFIG_MMC_UNSAFE_RESUME=y
#
-# MMC/SD Card Drivers
+# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_BOUNCE=y
@@ -1054,14 +931,18 @@ CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_MMC_TEST is not set
#
-# MMC/SD Host Controller Drivers
+# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_PXA is not set
# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_TMIO=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
# CONFIG_NEW_LEDS is not set
CONFIG_RTC_LIB=y
# CONFIG_RTC_CLASS is not set
# CONFIG_DMADEVICES is not set
+# CONFIG_REGULATOR is not set
# CONFIG_UIO is not set
#
@@ -1070,11 +951,17 @@ CONFIG_RTC_LIB=y
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_EXT2_FS_XIP is not set
-# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4DEV_FS is not set
+CONFIG_EXT3_FS=m
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=m
+CONFIG_FS_MBCACHE=m
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
# CONFIG_OCFS2_FS is not set
CONFIG_DNOTIFY=y
@@ -1106,6 +993,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
@@ -1142,6 +1030,7 @@ CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
@@ -1228,13 +1117,15 @@ CONFIG_FRAME_WARN=1024
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_DEBUG_MEMORY_INIT is not set
CONFIG_FRAME_POINTER=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_LATENCYTOP is not set
-CONFIG_HAVE_FTRACE=y
-CONFIG_HAVE_DYNAMIC_FTRACE=y
-# CONFIG_FTRACE is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_DEBUG_USER is not set
@@ -1244,15 +1135,23 @@ CONFIG_HAVE_ARCH_KGDB=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_CRYPTO=y
#
# Crypto core or helper
#
+# CONFIG_CRYPTO_FIPS is not set
CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_ALGAPI2=m
+CONFIG_CRYPTO_AEAD2=m
CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_BLKCIPHER2=m
+CONFIG_CRYPTO_HASH2=m
+CONFIG_CRYPTO_RNG2=m
CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_MANAGER2=m
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
@@ -1324,14 +1223,17 @@ CONFIG_CRYPTO_ARC4=m
#
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_HW=y
#
# Library routines
#
CONFIG_BITREVERSE=y
-# CONFIG_GENERIC_FIND_FIRST_BIT is not set
-# CONFIG_GENERIC_FIND_NEXT_BIT is not set
CONFIG_CRC_CCITT=y
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
diff --git a/arch/arm/configs/h5000_defconfig b/arch/arm/configs/h5000_defconfig
new file mode 100644
index 000000000000..649baa370495
--- /dev/null
+++ b/arch/arm/configs/h5000_defconfig
@@ -0,0 +1,996 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.27-rc6
+# Tue Sep 16 16:13:48 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_CGROUPS is not set
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+# CONFIG_UID16 is not set
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
+# CONFIG_HAVE_IOREMAP_PROT is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_ARCH_TRACEHOOK is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+# CONFIG_USE_GENERIC_SMP_HELPERS is not set
+CONFIG_HAVE_CLK=y
+# CONFIG_PROC_PAGE_MONITOR is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_ARCH_PXA_ESERIES is not set
+CONFIG_MACH_H5000=y
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_TAVOREVB is not set
+# CONFIG_MACH_SAAR is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_ARCH_PXA_PALM is not set
+# CONFIG_PXA_EZX is not set
+CONFIG_PXA25x=y
+# CONFIG_PXA_PWM is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+# CONFIG_IWMMXT is not set
+CONFIG_XSCALE_PMU=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="keepinitrd"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_APM_EMULATION=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+# CONFIG_MTD_CHAR is not set
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x8000000
+CONFIG_MTD_PHYSMAP_LEN=0x0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PXA2XX is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_SHARP_SL is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_NETDEVICES is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_APMPOWER is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# I2C GPIO expanders:
+#
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+CONFIG_USB_GADGET_PXA25X=y
+CONFIG_USB_PXA25X=y
+CONFIG_USB_PXA25X_SMALL=y
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+# CONFIG_USB_ETH_RNDIS is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_MMC is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+# CONFIG_DMADEVICES is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/kirkwood_defconfig b/arch/arm/configs/kirkwood_defconfig
index e3357ba10f1f..ab8b1e0d0dac 100644
--- a/arch/arm/configs/kirkwood_defconfig
+++ b/arch/arm/configs/kirkwood_defconfig
@@ -1,11 +1,11 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.26-rc5
-# Sun Jun 22 15:51:25 2008
+# Linux kernel version: 2.6.28-rc7
+# Thu Dec 4 15:27:39 2008
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
-CONFIG_GENERIC_GPIO=y
+# CONFIG_GENERIC_GPIO is not set
CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
@@ -22,8 +22,7 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_ARCH_SUPPORTS_AOUT=y
-CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
@@ -49,14 +48,17 @@ CONFIG_LOG_BUF_SHIFT=14
# CONFIG_GROUP_SCHED is not set
# CONFIG_SYSFS_DEPRECATED_V2 is not set
# CONFIG_RELAY is not set
-# CONFIG_NAMESPACES is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
# CONFIG_BLK_DEV_INITRD is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
-CONFIG_EMBEDDED=y
+# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -73,9 +75,12 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
+CONFIG_PCI_QUIRKS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
# CONFIG_SLOB is not set
CONFIG_PROFILING=y
# CONFIG_MARKERS is not set
@@ -85,8 +90,7 @@ CONFIG_KPROBES=y
CONFIG_KRETPROBES=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
-# CONFIG_HAVE_DMA_ATTRS is not set
-CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
@@ -97,12 +101,13 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
-# CONFIG_KMOD is not set
+CONFIG_KMOD=y
CONFIG_BLOCK=y
# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
# CONFIG_LSF is not set
# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
#
# IO Schedulers
@@ -117,6 +122,7 @@ CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"
CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
#
# System Type
@@ -128,7 +134,6 @@ CONFIG_CLASSIC_RCU=y
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_CO285 is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
@@ -158,7 +163,7 @@ CONFIG_ARCH_KIRKWOOD=y
# CONFIG_ARCH_LH7A40X is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM7X00A is not set
+# CONFIG_ARCH_MSM is not set
#
# Marvell Kirkwood Implementations
@@ -199,6 +204,7 @@ CONFIG_ARM_THUMB=y
# CONFIG_CPU_DCACHE_DISABLE is not set
CONFIG_OUTER_CACHE=y
CONFIG_CACHE_FEROCEON_L2=y
+# CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH is not set
#
# Bus support
@@ -217,25 +223,30 @@ CONFIG_TICK_ONESHOT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PREEMPT=y
CONFIG_HZ=100
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
-# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
-# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4096
# CONFIG_RESOURCES_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=1
-CONFIG_BOUNCE=y
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
CONFIG_ALIGNMENT_TRAP=y
#
@@ -248,6 +259,11 @@ CONFIG_CMDLINE=""
# CONFIG_KEXEC is not set
#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
# Floating point emulation
#
@@ -260,6 +276,8 @@ CONFIG_CMDLINE=""
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set
@@ -268,10 +286,6 @@ CONFIG_BINFMT_ELF=y
#
# CONFIG_PM is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
-
-#
-# Networking
-#
CONFIG_NET=y
#
@@ -322,6 +336,15 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
+CONFIG_NET_DSA=y
+# CONFIG_NET_DSA_TAG_DSA is not set
+CONFIG_NET_DSA_TAG_EDSA=y
+# CONFIG_NET_DSA_TAG_TRAILER is not set
+CONFIG_NET_DSA_MV88E6XXX=y
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+CONFIG_NET_DSA_MV88E6123_61_65=y
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
@@ -343,12 +366,12 @@ CONFIG_NET_PKTGEN=m
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
-
-#
-# Wireless
-#
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
# CONFIG_MAC80211 is not set
# CONFIG_IEEE80211 is not set
# CONFIG_RFKILL is not set
@@ -365,6 +388,8 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
@@ -453,7 +478,7 @@ CONFIG_M25PXX_USE_FAST_READ=y
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_NAND_ECC_SMC is not set
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
CONFIG_MTD_NAND_IDS=y
@@ -561,6 +586,7 @@ CONFIG_SCSI_LOWLEVEL=y
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
# CONFIG_SCSI_SRP is not set
+# CONFIG_SCSI_DH is not set
CONFIG_ATA=y
# CONFIG_ATA_NONSTANDARD is not set
CONFIG_SATA_PMP=y
@@ -619,7 +645,6 @@ CONFIG_SATA_MV=y
# CONFIG_PATA_SIS is not set
# CONFIG_PATA_VIA is not set
# CONFIG_PATA_WINBOND is not set
-# CONFIG_PATA_PLATFORM is not set
# CONFIG_PATA_SCH is not set
# CONFIG_MD is not set
# CONFIG_FUSION is not set
@@ -627,11 +652,14 @@ CONFIG_SATA_MV=y
#
# IEEE 1394 (FireWire) support
#
+
+#
+# Enable only one of the two stacks, unless you know what you are doing
+#
# CONFIG_FIREWIRE is not set
# CONFIG_IEEE1394 is not set
# CONFIG_I2O is not set
CONFIG_NETDEVICES=y
-# CONFIG_NETDEVICES_MULTIQUEUE is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -639,7 +667,23 @@ CONFIG_NETDEVICES=y
# CONFIG_TUN is not set
# CONFIG_VETH is not set
# CONFIG_ARCNET is not set
-# CONFIG_PHYLIB is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+CONFIG_MARVELL_PHY=y
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
# CONFIG_AX88796 is not set
@@ -650,12 +694,16 @@ CONFIG_MII=y
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
# CONFIG_NET_TULIP is not set
# CONFIG_HP100 is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_AMD8111_ETH is not set
@@ -676,14 +724,12 @@ CONFIG_NET_PCI=y
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_SC92031 is not set
+# CONFIG_ATL2 is not set
CONFIG_NETDEV_1000=y
# CONFIG_ACENIC is not set
# CONFIG_DL2K is not set
-CONFIG_E1000=y
-CONFIG_E1000_NAPI=y
-# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_E1000 is not set
# CONFIG_E1000E is not set
-# CONFIG_E1000E_ENABLED is not set
# CONFIG_IP1000 is not set
# CONFIG_IGB is not set
# CONFIG_NS83820 is not set
@@ -699,6 +745,8 @@ CONFIG_E1000_NAPI=y
CONFIG_MV643XX_ETH=y
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
+# CONFIG_ATL1E is not set
+# CONFIG_JME is not set
# CONFIG_NETDEV_10000 is not set
# CONFIG_TR is not set
@@ -765,7 +813,11 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
#
# Character devices
#
-# CONFIG_VT is not set
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
# CONFIG_DEVKMEM is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_NOZOMI is not set
@@ -775,7 +827,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
#
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
-# CONFIG_SERIAL_8250_PCI is not set
+CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
# CONFIG_SERIAL_8250_EXTENDED is not set
@@ -800,44 +852,64 @@ CONFIG_DEVPORT=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
#
# I2C Hardware Bus support
#
+
+#
+# PC SMBus host controller drivers
+#
# CONFIG_I2C_ALI1535 is not set
# CONFIG_I2C_ALI1563 is not set
# CONFIG_I2C_ALI15X3 is not set
# CONFIG_I2C_AMD756 is not set
# CONFIG_I2C_AMD8111 is not set
-# CONFIG_I2C_GPIO is not set
# CONFIG_I2C_I801 is not set
-# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_ISCH is not set
# CONFIG_I2C_PIIX4 is not set
# CONFIG_I2C_NFORCE2 is not set
-# CONFIG_I2C_OCORES is not set
-# CONFIG_I2C_PARPORT_LIGHT is not set
-# CONFIG_I2C_PROSAVAGE is not set
-# CONFIG_I2C_SAVAGE4 is not set
-# CONFIG_I2C_SIMTEC is not set
# CONFIG_I2C_SIS5595 is not set
# CONFIG_I2C_SIS630 is not set
# CONFIG_I2C_SIS96X is not set
-# CONFIG_I2C_TAOS_EVM is not set
-# CONFIG_I2C_STUB is not set
-# CONFIG_I2C_TINY_USB is not set
# CONFIG_I2C_VIA is not set
# CONFIG_I2C_VIAPRO is not set
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_MV64XXX=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Graphics adapter I2C/DDC channel drivers
+#
# CONFIG_I2C_VOODOO3 is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
# CONFIG_I2C_PCA_PLATFORM is not set
-CONFIG_I2C_MV64XXX=y
+# CONFIG_I2C_STUB is not set
#
# Miscellaneous I2C Chip support
#
# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
# CONFIG_SENSORS_EEPROM is not set
# CONFIG_SENSORS_PCF8574 is not set
# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
# CONFIG_SENSORS_PCF8591 is not set
# CONFIG_SENSORS_MAX6875 is not set
# CONFIG_SENSORS_TSL2550 is not set
@@ -864,20 +936,26 @@ CONFIG_SPI_ORION=y
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
# Multifunction device drivers
#
+# CONFIG_MFD_CORE is not set
# CONFIG_MFD_SM501 is not set
-# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
#
# Multimedia devices
@@ -910,8 +988,10 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_DISPLAY_SUPPORT is not set
#
-# Sound
+# Console display driver support
#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
# CONFIG_SOUND is not set
CONFIG_HID_SUPPORT=y
CONFIG_HID=y
@@ -922,9 +1002,36 @@ CONFIG_HID=y
# USB Input Devices
#
CONFIG_USB_HID=y
-# CONFIG_USB_HIDINPUT_POWERBOOK is not set
-# CONFIG_HID_FF is not set
+# CONFIG_HID_PID is not set
# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_BRIGHT=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DELL=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF is not set
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
@@ -940,8 +1047,9 @@ CONFIG_USB_DEVICEFS=y
CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_OTG is not set
-# CONFIG_USB_OTG_WHITELIST is not set
-# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
#
# USB Host Controller Drivers
@@ -956,20 +1064,23 @@ CONFIG_USB_EHCI_TT_NEWSCHED=y
# CONFIG_USB_UHCI_HCD is not set
# CONFIG_USB_SL811_HCD is not set
# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_WHCI_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
-CONFIG_USB_PRINTER=y
+CONFIG_USB_PRINTER=m
# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
#
#
-# may also be needed; see USB_STORAGE Help for more information
+# see USB_STORAGE Help for more information
#
CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_DEBUG is not set
@@ -992,7 +1103,6 @@ CONFIG_USB_STORAGE_JUMPSHOT=y
#
# CONFIG_USB_MDC800 is not set
# CONFIG_USB_MICROTEK is not set
-# CONFIG_USB_MON is not set
#
# USB port drivers
@@ -1005,7 +1115,7 @@ CONFIG_USB_STORAGE_JUMPSHOT=y
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
-# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_SEVSEG is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
@@ -1023,8 +1133,12 @@ CONFIG_USB_STORAGE_JUMPSHOT=y
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
# CONFIG_USB_GADGET is not set
+# CONFIG_UWB is not set
# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
CONFIG_NEW_LEDS=y
# CONFIG_LEDS_CLASS is not set
@@ -1038,6 +1152,8 @@ CONFIG_NEW_LEDS=y
# CONFIG_LEDS_TRIGGERS is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set
#
@@ -1056,7 +1172,6 @@ CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_DRV_DS1374 is not set
# CONFIG_RTC_DRV_DS1672 is not set
# CONFIG_RTC_DRV_MAX6900 is not set
-CONFIG_RTC_DRV_MV=y
# CONFIG_RTC_DRV_RS5C372 is not set
# CONFIG_RTC_DRV_ISL1208 is not set
# CONFIG_RTC_DRV_X1205 is not set
@@ -1064,29 +1179,39 @@ CONFIG_RTC_DRV_MV=y
# CONFIG_RTC_DRV_PCF8583 is not set
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
#
# SPI RTC drivers
#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
# CONFIG_RTC_DRV_MAX6902 is not set
# CONFIG_RTC_DRV_R9701 is not set
# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
#
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_V3020 is not set
#
# on-CPU RTC drivers
#
+CONFIG_RTC_DRV_MV=y
CONFIG_DMADEVICES=y
#
@@ -1099,6 +1224,8 @@ CONFIG_DMA_ENGINE=y
# DMA Clients
#
# CONFIG_NET_DMA is not set
+# CONFIG_DMATEST is not set
+# CONFIG_REGULATOR is not set
# CONFIG_UIO is not set
#
@@ -1109,11 +1236,12 @@ CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XIP is not set
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
-# CONFIG_EXT4DEV_FS is not set
+# CONFIG_EXT4_FS is not set
CONFIG_JBD=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
CONFIG_XFS_FS=y
# CONFIG_XFS_QUOTA is not set
# CONFIG_XFS_POSIX_ACL is not set
@@ -1131,7 +1259,7 @@ CONFIG_INOTIFY_USER=y
#
# CD-ROM/DVD Filesystems
#
-CONFIG_ISO9660_FS=y
+CONFIG_ISO9660_FS=m
CONFIG_JOLIET=y
# CONFIG_ZISOFS is not set
CONFIG_UDF_FS=m
@@ -1140,9 +1268,9 @@ CONFIG_UDF_NLS=y
#
# DOS/FAT/NT Filesystems
#
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_NTFS_FS is not set
@@ -1152,6 +1280,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
@@ -1182,6 +1311,7 @@ CONFIG_JFFS2_RTIME=y
CONFIG_CRAMFS=y
# CONFIG_VXFS_FS is not set
# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
@@ -1192,13 +1322,13 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
# CONFIG_NFS_V4 is not set
-# CONFIG_NFSD is not set
CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_SUNRPC_REGISTER_V4 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
@@ -1210,24 +1340,8 @@ CONFIG_SUNRPC=y
#
# Partition Types
#
-CONFIG_PARTITION_ADVANCED=y
-# CONFIG_ACORN_PARTITION is not set
-# CONFIG_OSF_PARTITION is not set
-# CONFIG_AMIGA_PARTITION is not set
-# CONFIG_ATARI_PARTITION is not set
-# CONFIG_MAC_PARTITION is not set
+# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
-# CONFIG_BSD_DISKLABEL is not set
-# CONFIG_MINIX_SUBPARTITION is not set
-# CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_UNIXWARE_DISKLABEL is not set
-# CONFIG_LDM_PARTITION is not set
-# CONFIG_SGI_PARTITION is not set
-# CONFIG_ULTRIX_PARTITION is not set
-# CONFIG_SUN_PARTITION is not set
-# CONFIG_KARMA_PARTITION is not set
-# CONFIG_EFI_PARTITION is not set
-# CONFIG_SYSV68_PARTITION is not set
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
@@ -1284,11 +1398,14 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_TIMER_STATS is not set
# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_DEBUG_SLAB is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
# CONFIG_DEBUG_PREEMPT is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
@@ -1300,21 +1417,40 @@ CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_KOBJECT is not set
-# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_DEBUG_INFO=y
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
# CONFIG_DEBUG_SG is not set
CONFIG_FRAME_POINTER=y
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_KPROBES_SANITY_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
# CONFIG_DEBUG_STACK_USAGE is not set
@@ -1326,6 +1462,7 @@ CONFIG_DEBUG_LL=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_ASYNC_CORE=y
CONFIG_CRYPTO=y
@@ -1333,8 +1470,12 @@ CONFIG_CRYPTO=y
#
# Crypto core or helper
#
+# CONFIG_CRYPTO_FIPS is not set
CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_AEAD=m
CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_RNG=m
CONFIG_CRYPTO_MANAGER=m
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
@@ -1373,6 +1514,10 @@ CONFIG_CRYPTO_PCBC=m
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
@@ -1403,6 +1548,11 @@ CONFIG_CRYPTO_PCBC=m
#
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_HW=y
# CONFIG_CRYPTO_DEV_HIFN_795X is not set
@@ -1410,10 +1560,9 @@ CONFIG_CRYPTO_HW=y
# Library routines
#
CONFIG_BITREVERSE=y
-# CONFIG_GENERIC_FIND_FIRST_BIT is not set
-# CONFIG_GENERIC_FIND_NEXT_BIT is not set
CONFIG_CRC_CCITT=y
CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
CONFIG_CRC_ITU_T=m
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
diff --git a/arch/arm/configs/ks8695_defconfig b/arch/arm/configs/ks8695_defconfig
index 6077f2cb88e4..d25c41bab06c 100644
--- a/arch/arm/configs/ks8695_defconfig
+++ b/arch/arm/configs/ks8695_defconfig
@@ -1,39 +1,67 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.17-rc4
-# Thu May 25 15:42:51 2006
+# Linux kernel version: 2.6.27-simtec-micrel1
+# Fri Dec 5 10:30:27 2008
#
CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+# CONFIG_GENERIC_TIME is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
#
-# Code maturity level options
+# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_SYSCTL=y
+# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
-CONFIG_UID16=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
@@ -41,30 +69,50 @@ CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
+# CONFIG_HAVE_IOREMAP_PROT is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_ARCH_TRACEHOOK is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+# CONFIG_USE_GENERIC_SMP_HELPERS is not set
+# CONFIG_HAVE_CLK is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
-# CONFIG_SLOB is not set
-CONFIG_OBSOLETE_INTERMODULE=y
-
-#
-# Loadable module support
-#
CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_KMOD=y
-
-#
-# Block layer
-#
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
#
# IO Schedulers
@@ -78,60 +126,77 @@ CONFIG_DEFAULT_AS=y
# CONFIG_DEFAULT_CFQ is not set
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
#
# System Type
#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_CO285 is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
-# CONFIG_ARCH_INTEGRATOR is not set
-# CONFIG_ARCH_IOP3XX is not set
-# CONFIG_ARCH_IXP4XX is not set
-# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+CONFIG_ARCH_KS8695=y
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_VERSATILE is not set
-# CONFIG_ARCH_REALVIEW is not set
-# CONFIG_ARCH_IMX is not set
-# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_AAEC2000 is not set
-# CONFIG_ARCH_AT91 is not set
-CONFIG_ARCH_KS8695=y
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
#
# Kendin/Micrel KS8695 Implementations
#
CONFIG_MACH_KS8695=y
-# CONFIG_MACH_DSM320 is not set
-# CONFIG_MACH_CM4002 is not set
-# CONFIG_MACH_CM4008 is not set
-# CONFIG_MACH_CM40xx is not set
-# CONFIG_MACH_LITE300 is not set
-# CONFIG_MACH_SE4200 is not set
-# CONFIG_MACH_MANGA_KS8695 is not set
+CONFIG_MACH_DSM320=y
#
# Processor Type
#
CONFIG_CPU_32=y
CONFIG_CPU_ARM922T=y
-CONFIG_CPU_32v4=y
+CONFIG_CPU_32v4T=y
CONFIG_CPU_ABRT_EV4T=y
+CONFIG_CPU_PABRT_NOIFAR=y
CONFIG_CPU_CACHE_V4WT=y
CONFIG_CPU_CACHE_VIVT=y
CONFIG_CPU_COPY_V4WB=y
CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
#
# Processor Features
@@ -140,16 +205,16 @@ CONFIG_CPU_TLB_V4WBI=y
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_OUTER_CACHE is not set
#
# Bus support
#
CONFIG_PCI=y
+CONFIG_PCI_SYSCALL=y
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCI_LEGACY=y
CONFIG_PCI_DEBUG=y
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
CONFIG_PCCARD=y
# CONFIG_PCMCIA_DEBUG is not set
CONFIG_PCMCIA=y
@@ -173,9 +238,12 @@ CONFIG_PCCARD_NONSTATIC=y
#
# Kernel Features
#
+# CONFIG_TICK_ONESHOT is not set
# CONFIG_PREEMPT is not set
CONFIG_HZ=100
-# CONFIG_AEABI is not set
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
@@ -184,7 +252,14 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
CONFIG_ALIGNMENT_TRAP=y
#
@@ -194,6 +269,7 @@ CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x20410000,3145728 root=/dev/ram0 rw"
# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
#
# Floating point emulation
@@ -202,8 +278,7 @@ CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x20410000,3145728 root=/dev
#
# At least one emulation must be selected
#
-CONFIG_FPE_NWFPE=y
-# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_NWFPE is not set
# CONFIG_FPE_FASTFPE is not set
#
@@ -212,34 +287,33 @@ CONFIG_FPE_NWFPE=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set
-# CONFIG_ARTHUR is not set
#
# Power management options
#
# CONFIG_PM is not set
-# CONFIG_APM is not set
-
-#
-# Networking
-#
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y
#
# Networking options
#
-# CONFIG_NETDEBUG is not set
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
CONFIG_IP_FIB_HASH=y
CONFIG_IP_PNP=y
-# CONFIG_IP_PNP_DHCP is not set
-CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
@@ -250,28 +324,21 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
-CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
-
-#
-# DCCP Configuration (EXPERIMENTAL)
-#
# CONFIG_IP_DCCP is not set
-
-#
-# SCTP Configuration (EXPERIMENTAL)
-#
# CONFIG_IP_SCTP is not set
-
-#
-# TIPC Configuration (EXPERIMENTAL)
-#
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
@@ -282,13 +349,8 @@ CONFIG_TCP_CONG_BIC=y
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
-# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-
-#
-# QoS and/or fair queueing
-#
# CONFIG_NET_SCHED is not set
#
@@ -296,9 +358,21 @@ CONFIG_TCP_CONG_BIC=y
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
#
# Device Drivers
@@ -307,36 +381,40 @@ CONFIG_TCP_CONG_BIC=y
#
# Generic Driver Options
#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
-
-#
-# Connector - unified userspace <-> kernelspace linker
-#
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
-
-#
-# Memory Technology Devices (MTD)
-#
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_CONCAT is not set
CONFIG_MTD_PARTITIONS=y
-# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
CONFIG_MTD_CMDLINE_PARTS=y
# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
#
# RAM/ROM/Flash chip drivers
@@ -355,22 +433,25 @@ CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
-# CONFIG_MTD_CFI_INTELEXT is not set
-CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
#
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
-# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x8000000
+CONFIG_MTD_PHYSMAP_LEN=0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=4
# CONFIG_MTD_ARM_INTEGRATOR is not set
# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
# CONFIG_MTD_PLATRAM is not set
#
@@ -388,29 +469,15 @@ CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-
-#
-# NAND Flash Device Drivers
-#
# CONFIG_MTD_NAND is not set
-
-#
-# OneNAND Flash Device Drivers
-#
# CONFIG_MTD_ONENAND is not set
#
-# Parallel port support
+# UBI - Unsorted block images
#
+# CONFIG_MTD_UBI is not set
# CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-
-#
-# Block devices
-#
+CONFIG_BLK_DEV=y
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
@@ -422,13 +489,17 @@ CONFIG_MTD_CFI_UTIL=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
-
-#
-# ATA/ATAPI/MFM/RLL support
-#
+CONFIG_MISC_DEVICES=y
+# CONFIG_PHANTOM is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_HP_ILO is not set
+CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
#
@@ -436,127 +507,85 @@ CONFIG_BLK_DEV_INITRD=y
#
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
# CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
# CONFIG_FUSION is not set
#
# IEEE 1394 (FireWire) support
#
-# CONFIG_IEEE1394 is not set
#
-# I2O device support
+# Enable only one of the two stacks, unless you know what you are doing
#
+# CONFIG_FIREWIRE is not set
+# CONFIG_IEEE1394 is not set
# CONFIG_I2O is not set
-
-#
-# Network device support
-#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
-
-#
-# ARCnet devices
-#
+# CONFIG_VETH is not set
# CONFIG_ARCNET is not set
-
-#
-# PHY device support
-#
# CONFIG_PHYLIB is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
CONFIG_NET_ETHERNET=y
-# CONFIG_MII is not set
-CONFIG_ARM_KS8695_ETHER=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNGEM is not set
# CONFIG_CASSINI is not set
# CONFIG_NET_VENDOR_3COM is not set
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
-
-#
-# Tulip family network device support
-#
# CONFIG_NET_TULIP is not set
# CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_NET_PCI is not set
-
-#
-# Ethernet (1000 Mbit)
-#
-# CONFIG_ACENIC is not set
-# CONFIG_DL2K is not set
-# CONFIG_E1000 is not set
-# CONFIG_NS83820 is not set
-# CONFIG_HAMACHI is not set
-# CONFIG_YELLOWFIN is not set
-# CONFIG_R8169 is not set
-# CONFIG_SIS190 is not set
-# CONFIG_SKGE is not set
-# CONFIG_SKY2 is not set
-# CONFIG_SK98LIN is not set
-# CONFIG_TIGON3 is not set
-# CONFIG_BNX2 is not set
-
-#
-# Ethernet (10000 Mbit)
-#
-# CONFIG_CHELSIO_T1 is not set
-# CONFIG_IXGB is not set
-# CONFIG_S2IO is not set
-
-#
-# Token Ring devices
-#
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
# CONFIG_TR is not set
#
-# Wireless LAN (non-hamradio)
-#
-# CONFIG_NET_RADIO is not set
-
-#
-# PCMCIA network device support
-#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_WL3501 is not set
+CONFIG_PRISM54=m
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_HOSTAP is not set
# CONFIG_NET_PCMCIA is not set
-
-#
-# Wan interfaces
-#
# CONFIG_WAN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
-# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
# CONFIG_ISDN is not set
#
# Input device support
#
CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
#
# Userland interfaces
@@ -566,7 +595,6 @@ CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
@@ -576,6 +604,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
@@ -589,9 +618,13 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# Character devices
#
CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_NOZOMI is not set
#
# Serial drivers
@@ -609,132 +642,113 @@ CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
-
-#
-# IPMI
-#
# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
+CONFIG_HW_RANDOM=m
# CONFIG_NVRAM is not set
-# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
#
-# Ftape, the floppy tape device driver
-#
-# CONFIG_DRM is not set
-
-#
# PCMCIA character devices
#
# CONFIG_SYNCLINK_CS is not set
# CONFIG_CARDMAN_4000 is not set
# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
# CONFIG_RAW_DRIVER is not set
-
-#
-# TPM devices
-#
# CONFIG_TCG_TPM is not set
-# CONFIG_TELCLOCK is not set
-
-#
-# I2C support
-#
+CONFIG_DEVPORT=y
# CONFIG_I2C is not set
-
-#
-# SPI support
-#
# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
-
-#
-# Dallas's 1-wire bus
-#
# CONFIG_W1 is not set
-
-#
-# Hardware Monitoring support
-#
+# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
-# CONFIG_HWMON_VID is not set
-
-#
-# Misc devices
-#
+# CONFIG_WATCHDOG is not set
#
-# LED devices
+# Sonics Silicon Backplane
#
-# CONFIG_NEW_LEDS is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
#
-# LED drivers
+# Multifunction device drivers
#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
#
-# LED Triggers
+# Multimedia devices
#
#
-# Multimedia devices
+# Multimedia core support
#
# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
#
-# Digital Video Broadcasting Devices
+# Multimedia drivers
#
-# CONFIG_DVB is not set
+# CONFIG_DAB is not set
#
# Graphics support
#
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
#
-# Console display driver support
+# Display device support
#
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_DUMMY_CONSOLE=y
+# CONFIG_DISPLAY_SUPPORT is not set
#
-# Sound
+# Console display driver support
#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
# CONFIG_SOUND is not set
-
-#
-# USB support
-#
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+CONFIG_HID_DEBUG=y
+# CONFIG_HIDRAW is not set
+CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
# CONFIG_USB is not set
#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# Enable Host or Gadget support to see Inventra options
#
#
-# USB Gadget Support
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
#
# CONFIG_USB_GADGET is not set
-
-#
-# MMC/SD Card support
-#
# CONFIG_MMC is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
#
-# Real Time Clock
+# Voltage and Current regulators
#
-CONFIG_RTC_LIB=y
-# CONFIG_RTC_CLASS is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
#
# File systems
@@ -743,16 +757,16 @@ CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_EXT2_FS_XIP is not set
# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
# CONFIG_XFS_FS is not set
# CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set
@@ -774,10 +788,11 @@ CONFIG_DNOTIFY=y
# Pseudo filesystems
#
CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set
#
@@ -790,67 +805,113 @@ CONFIG_RAMFS=y
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
-# CONFIG_JFFS_FS is not set
-# CONFIG_JFFS2_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
CONFIG_CRAMFS=y
# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
-# CONFIG_NFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
-# CONFIG_9P_FS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
# CONFIG_NLS is not set
-
-#
-# Profiling support
-#
-# CONFIG_PROFILING is not set
+# CONFIG_DLM is not set
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
-CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
-CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
-# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
CONFIG_FRAME_POINTER=y
-# CONFIG_UNWIND_INFO is not set
-CONFIG_FORCED_INLINING=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
CONFIG_DEBUG_USER=y
# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
CONFIG_DEBUG_LL=y
# CONFIG_DEBUG_ICEDCC is not set
@@ -859,21 +920,103 @@ CONFIG_DEBUG_LL=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
#
-# Cryptographic options
+# Ciphers
#
-# CONFIG_CRYPTO is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
#
-# Hardware crypto devices
+# Compression
#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
#
# Library routines
#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/mx31moboard_defconfig b/arch/arm/configs/mx31moboard_defconfig
new file mode 100644
index 000000000000..e90f86d6deef
--- /dev/null
+++ b/arch/arm/configs/mx31moboard_defconfig
@@ -0,0 +1,790 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.27-rc5
+# Fri Oct 24 11:41:22 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
+# CONFIG_HAVE_IOREMAP_PROT is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_ARCH_TRACEHOOK is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+# CONFIG_USE_GENERIC_SMP_HELPERS is not set
+# CONFIG_HAVE_CLK is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX2 is not set
+CONFIG_ARCH_MX3=y
+
+#
+# MX3 Options
+#
+# CONFIG_MACH_MX31ADS is not set
+# CONFIG_MACH_PCM037 is not set
+# CONFIG_MACH_MX31LITE is not set
+CONFIG_MACH_MX31MOBOARD=y
+# CONFIG_MXC_IRQ_PRIOR is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V6=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=m
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+CONFIG_MTD_REDBOOT_PARTS_READONLY=y
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+# CONFIG_MTD_CFI_I2 is not set
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x0
+CONFIG_MTD_PHYSMAP_LEN=0x0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+CONFIG_SMC91X=y
+# CONFIG_DM9000 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# I2C GPIO expanders:
+#
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/mx31pdk_defconfig b/arch/arm/configs/mx31pdk_defconfig
new file mode 100644
index 000000000000..95ffc0db95a0
--- /dev/null
+++ b/arch/arm/configs/mx31pdk_defconfig
@@ -0,0 +1,773 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc2
+# Sun Oct 26 15:55:29 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+# CONFIG_EXPERIMENTAL is not set
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_MODULES is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX2 is not set
+CONFIG_ARCH_MX3=y
+
+#
+# MX3 Options
+#
+# CONFIG_MACH_MX31ADS is not set
+# CONFIG_MACH_PCM037 is not set
+# CONFIG_MACH_MX31LITE is not set
+CONFIG_MACH_MX31_3DS=y
+# CONFIG_MXC_IRQ_PRIOR is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V6=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_UNEVICTABLE_LRU is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+CONFIG_IPV6=y
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+CONFIG_IPV6_SIT=y
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_IMX=y
+CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+
+#
+# I2C GPIO expanders:
+#
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_WM8400 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_PCBC is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC32 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/neocore926_defconfig b/arch/arm/configs/neocore926_defconfig
new file mode 100644
index 000000000000..325f1e105f69
--- /dev/null
+++ b/arch/arm/configs/neocore926_defconfig
@@ -0,0 +1,1302 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.27-rc1
+# Tue Jul 29 10:46:54 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
+# CONFIG_HAVE_IOREMAP_PROT is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_ARCH_TRACEHOOK is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+# CONFIG_USE_GENERIC_SMP_HELPERS is not set
+CONFIG_HAVE_CLK=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+CONFIG_ARCH_AT91=y
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Atmel AT91 System-on-Chip
+#
+# CONFIG_ARCH_AT91RM9200 is not set
+# CONFIG_ARCH_AT91SAM9260 is not set
+# CONFIG_ARCH_AT91SAM9261 is not set
+CONFIG_ARCH_AT91SAM9263=y
+# CONFIG_ARCH_AT91SAM9RL is not set
+# CONFIG_ARCH_AT91SAM9G20 is not set
+# CONFIG_ARCH_AT91CAP9 is not set
+# CONFIG_ARCH_AT91X40 is not set
+CONFIG_AT91_PMC_UNIT=y
+
+#
+# AT91SAM9263 Board Type
+#
+# CONFIG_MACH_AT91SAM9263EK is not set
+# CONFIG_MACH_USB_A9263 is not set
+CONFIG_MACH_NEOCORE926=y
+
+#
+# AT91 Board Options
+#
+CONFIG_MTD_AT91_DATAFLASH_CARD=y
+
+#
+# AT91 Feature Selections
+#
+# CONFIG_AT91_PROGRAMMABLE_CLOCKS is not set
+CONFIG_AT91_TIMER_HZ=100
+CONFIG_AT91_EARLY_DBGU=y
+# CONFIG_AT91_EARLY_USART0 is not set
+# CONFIG_AT91_EARLY_USART1 is not set
+# CONFIG_AT91_EARLY_USART2 is not set
+# CONFIG_AT91_EARLY_USART3 is not set
+# CONFIG_AT91_EARLY_USART4 is not set
+# CONFIG_AT91_EARLY_USART5 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+# CONFIG_AEABI is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=y
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+CONFIG_NFTL=y
+CONFIG_NFTL_RW=y
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+CONFIG_MTD_BLOCK2MTD=y
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+CONFIG_MTD_NAND_ECC_SMC=y
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_ATMEL=y
+CONFIG_MTD_NAND_ATMEL_ECC_HW=y
+# CONFIG_MTD_NAND_ATMEL_ECC_SOFT is not set
+# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_PLATFORM=y
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+CONFIG_ATMEL_PWM=y
+CONFIG_ATMEL_TCLIB=y
+CONFIG_ATMEL_TCB_CLKSRC=y
+CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+# CONFIG_BLK_DEV_SD is not set
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+CONFIG_MACB=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+# CONFIG_SERIAL_ATMEL_PDC is not set
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_ATMEL=y
+# CONFIG_SPI_BITBANG is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_ATMEL=y
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_ATMEL_LCDC=y
+# CONFIG_BACKLIGHT_ATMEL_PWM is not set
+# CONFIG_BACKLIGHT_CORGI is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+CONFIG_HID_DEBUG=y
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=y
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_AT91=y
+# CONFIG_MMC_SPI is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+CONFIG_JFFS2_FS_WBUF_VERIFY=y
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_LATENCYTOP is not set
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/netx_defconfig b/arch/arm/configs/netx_defconfig
index 0884f2370c3a..61d0fc5b2417 100644
--- a/arch/arm/configs/netx_defconfig
+++ b/arch/arm/configs/netx_defconfig
@@ -728,9 +728,9 @@ CONFIG_RTC_CLASS=m
#
# RTC interfaces
#
-CONFIG_RTC_INTF_SYSFS=m
-CONFIG_RTC_INTF_PROC=m
-CONFIG_RTC_INTF_DEV=m
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
#
# RTC drivers
diff --git a/arch/arm/configs/omap3_pandora_defconfig b/arch/arm/configs/omap3_pandora_defconfig
new file mode 100644
index 000000000000..09543f4de5bc
--- /dev/null
+++ b/arch/arm/configs/omap3_pandora_defconfig
@@ -0,0 +1,1409 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc7
+# Fri Dec 5 11:54:09 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+CONFIG_ARCH_OMAP=y
+# CONFIG_ARCH_MSM is not set
+
+#
+# TI OMAP Implementations
+#
+CONFIG_ARCH_OMAP_OTG=y
+# CONFIG_ARCH_OMAP1 is not set
+# CONFIG_ARCH_OMAP2 is not set
+CONFIG_ARCH_OMAP3=y
+
+#
+# OMAP Feature Selections
+#
+# CONFIG_OMAP_DEBUG_POWERDOMAIN is not set
+# CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set
+# CONFIG_OMAP_RESET_CLOCKS is not set
+# CONFIG_OMAP_MUX is not set
+CONFIG_OMAP_MCBSP=y
+# CONFIG_OMAP_MPU_TIMER is not set
+CONFIG_OMAP_32K_TIMER=y
+CONFIG_OMAP_32K_TIMER_HZ=128
+CONFIG_OMAP_DM_TIMER=y
+# CONFIG_OMAP_LL_DEBUG_UART1 is not set
+# CONFIG_OMAP_LL_DEBUG_UART2 is not set
+CONFIG_OMAP_LL_DEBUG_UART3=y
+CONFIG_ARCH_OMAP34XX=y
+CONFIG_ARCH_OMAP3430=y
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP3_BEAGLE is not set
+# CONFIG_MACH_OMAP_LDP is not set
+# CONFIG_MACH_OVERO is not set
+CONFIG_MACH_OMAP3_PANDORA=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_IFAR=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+CONFIG_ARM_THUMBEE=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_HAS_TLS_REG=y
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=128
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=" debug "
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ADS7846=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_OMAP=y
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_ISP1301_OMAP is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_BITBANG is not set
+CONFIG_SPI_OMAP24XX=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+CONFIG_GPIO_TWL4030=y
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+CONFIG_TWL4030_CORE=y
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_COMPAT is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_BRIGHT is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DELL is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_SOC=y
+
+#
+# OMAP 343x high speed USB support
+#
+CONFIG_USB_MUSB_HOST=y
+# CONFIG_USB_MUSB_PERIPHERAL is not set
+# CONFIG_USB_MUSB_OTG is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+CONFIG_USB_MUSB_HDRC_HCD=y
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_INVENTRA_DMA=y
+# CONFIG_USB_TI_CPPI_DMA is not set
+# CONFIG_USB_MUSB_DEBUG is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+CONFIG_USB_GADGET_OMAP=y
+CONFIG_USB_OMAP=y
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_OMAP is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+CONFIG_RTC_DRV_TWL4030=y
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_DMADEVICES is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_QUOTA=y
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+CONFIG_PRINT_QUOTA_WARNING=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/omap_ldp_defconfig b/arch/arm/configs/omap_ldp_defconfig
index 948a212fb1cc..b77d054169ee 100644
--- a/arch/arm/configs/omap_ldp_defconfig
+++ b/arch/arm/configs/omap_ldp_defconfig
@@ -316,7 +316,82 @@ CONFIG_BINFMT_MISC=y
#
# CONFIG_PM is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
-# CONFIG_NET is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
#
# Device Drivers
@@ -332,6 +407,8 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
# CONFIG_MTD is not set
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
@@ -390,6 +467,54 @@ CONFIG_SCSI_LOWLEVEL=y
# CONFIG_SCSI_DH is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+CONFIG_SMC911X=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
#
# Input device support
@@ -816,6 +941,27 @@ CONFIG_TMPFS=y
# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
#
# Partition Types
diff --git a/arch/arm/configs/picotux200_defconfig b/arch/arm/configs/picotux200_defconfig
index 14826f0dabde..59e4463c2da2 100644
--- a/arch/arm/configs/picotux200_defconfig
+++ b/arch/arm/configs/picotux200_defconfig
@@ -1069,9 +1069,9 @@ CONFIG_RTC_CLASS=m
#
# RTC interfaces
#
-CONFIG_RTC_INTF_SYSFS=m
-CONFIG_RTC_INTF_PROC=m
-CONFIG_RTC_INTF_DEV=m
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
#
diff --git a/arch/arm/configs/realview-smp_defconfig b/arch/arm/configs/realview-smp_defconfig
index 0c09b23167ec..cd29824d791c 100644
--- a/arch/arm/configs/realview-smp_defconfig
+++ b/arch/arm/configs/realview-smp_defconfig
@@ -1,84 +1,111 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.19-rc3
-# Wed Oct 25 14:12:00 2006
+# Linux kernel version: 2.6.28-rc2
+# Mon Nov 10 14:41:47 2008
#
CONFIG_ARM=y
-# CONFIG_GENERIC_TIME is not set
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
#
-# Code maturity level options
+# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
-# CONFIG_IPC_NS is not set
+CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
-# CONFIG_UTS_NS is not set
# CONFIG_AUDIT is not set
# CONFIG_IKCONFIG is not set
-# CONFIG_CPUSETS is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
-CONFIG_INITRAMFS_SOURCE=""
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
-# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
-CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
CONFIG_SHMEM=y
-CONFIG_SLAB=y
+CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
-# CONFIG_SLOB is not set
-
-#
-# Loadable module support
-#
CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
-# CONFIG_KMOD is not set
+CONFIG_KMOD=y
CONFIG_STOP_MACHINE=y
-
-#
-# Block layer
-#
CONFIG_BLOCK=y
+# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
#
# IO Schedulers
@@ -92,6 +119,8 @@ CONFIG_DEFAULT_DEADLINE=y
# CONFIG_DEFAULT_CFQ is not set
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="deadline"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
#
# System Type
@@ -103,19 +132,26 @@ CONFIG_ARCH_REALVIEW=y
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_CO285 is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
-# CONFIG_ARCH_IXP4XX is not set
-# CONFIG_ARCH_IXP2000 is not set
# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_RPC is not set
@@ -123,13 +159,29 @@ CONFIG_ARCH_REALVIEW=y
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
#
# RealView platform type
#
CONFIG_MACH_REALVIEW_EB=y
-CONFIG_REALVIEW_MPCORE=y
+# CONFIG_REALVIEW_EB_A9MP is not set
+CONFIG_REALVIEW_EB_ARM11MP=y
+# CONFIG_REALVIEW_EB_ARM11MP_REVB is not set
+CONFIG_MACH_REALVIEW_PB11MP=y
+# CONFIG_MACH_REALVIEW_PB1176 is not set
+# CONFIG_MACH_REALVIEW_PBA8 is not set
+CONFIG_REALVIEW_HIGH_PHYS_OFFSET=y
#
# Processor Type
@@ -138,12 +190,15 @@ CONFIG_CPU_32=y
# CONFIG_CPU_ARM926T is not set
CONFIG_CPU_V6=y
CONFIG_CPU_32v6K=y
+# CONFIG_CPU_V7 is not set
CONFIG_CPU_32v6=y
CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
CONFIG_CPU_CACHE_V6=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y
@@ -153,9 +208,10 @@ CONFIG_CPU_CP15_MMU=y
CONFIG_ARM_THUMB=y
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
-# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
# CONFIG_CPU_BPREDICT_DISABLE is not set
CONFIG_HAS_TLS_REG=y
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
CONFIG_ARM_GIC=y
CONFIG_ICST307=y
@@ -163,32 +219,44 @@ CONFIG_ICST307=y
# Bus support
#
CONFIG_ARM_AMBA=y
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
# CONFIG_PCCARD is not set
#
# Kernel Features
#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_SMP=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_NR_CPUS=4
CONFIG_HOTPLUG_CPU=y
CONFIG_LOCAL_TIMERS=y
# CONFIG_PREEMPT is not set
CONFIG_HZ=100
-# CONFIG_AEABI is not set
-# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
CONFIG_ALIGNMENT_TRAP=y
#
@@ -198,6 +266,12 @@ CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=ttyAMA0 mem=128M"
# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
#
# Floating point emulation
@@ -206,8 +280,7 @@ CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=tt
#
# At least one emulation must be selected
#
-CONFIG_FPE_NWFPE=y
-# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_NWFPE is not set
# CONFIG_FPE_FASTFPE is not set
CONFIG_VFP=y
@@ -215,28 +288,29 @@ CONFIG_VFP=y
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set
-# CONFIG_ARTHUR is not set
#
# Power management options
#
# CONFIG_PM is not set
-# CONFIG_APM is not set
-
-#
-# Networking
-#
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y
#
# Networking options
#
-# CONFIG_NETDEBUG is not set
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
@@ -255,36 +329,25 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
-
-#
-# DCCP Configuration (EXPERIMENTAL)
-#
# CONFIG_IP_DCCP is not set
-
-#
-# SCTP Configuration (EXPERIMENTAL)
-#
# CONFIG_IP_SCTP is not set
-
-#
-# TIPC Configuration (EXPERIMENTAL)
-#
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
@@ -294,10 +357,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-
-#
-# QoS and/or fair queueing
-#
# CONFIG_NET_SCHED is not set
#
@@ -305,9 +364,14 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
-# CONFIG_IEEE80211 is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
#
# Device Drivers
@@ -316,38 +380,37 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
#
# Generic Driver Options
#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_FW_LOADER is not set
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
-
-#
-# Connector - unified userspace <-> kernelspace linker
-#
# CONFIG_CONNECTOR is not set
-
-#
-# Memory Technology Devices (MTD)
-#
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
-# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_CONCAT=y
CONFIG_MTD_PARTITIONS=y
# CONFIG_MTD_REDBOOT_PARTS is not set
CONFIG_MTD_CMDLINE_PARTS=y
# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
# CONFIG_RFD_FTL is not set
# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
#
# RAM/ROM/Flash chip drivers
@@ -373,7 +436,6 @@ CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
#
# Mapping drivers for chip access
@@ -397,115 +459,73 @@ CONFIG_MTD_ARM_INTEGRATOR=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-
-#
-# NAND Flash Device Drivers
-#
# CONFIG_MTD_NAND is not set
-
-#
-# OneNAND Flash Device Drivers
-#
# CONFIG_MTD_ONENAND is not set
#
-# Parallel port support
+# UBI - Unsorted block images
#
+# CONFIG_MTD_UBI is not set
# CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-
-#
-# Block devices
-#
+CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_RAM is not set
-CONFIG_BLK_DEV_INITRD=y
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
#
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
# CONFIG_SCSI_NETLINK is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
+# CONFIG_ATA is not set
# CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
-# CONFIG_FUSION is not set
-
-#
-# IEEE 1394 (FireWire) support
-#
-
-#
-# I2O device support
-#
-
-#
-# Network device support
-#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
-
-#
-# PHY device support
-#
+# CONFIG_VETH is not set
# CONFIG_PHYLIB is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
+# CONFIG_AX88796 is not set
CONFIG_SMC91X=y
# CONFIG_DM9000 is not set
-
-#
-# Ethernet (1000 Mbit)
-#
-
-#
-# Ethernet (10000 Mbit)
-#
-
-#
-# Token Ring devices
-#
-
-#
-# Wireless LAN (non-hamradio)
-#
-# CONFIG_NET_RADIO is not set
-
-#
-# Wan interfaces
-#
+CONFIG_SMC911X=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
# CONFIG_WAN is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
-# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
# CONFIG_ISDN is not set
#
@@ -513,6 +533,7 @@ CONFIG_SMC91X=y
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
#
# Userland interfaces
@@ -522,7 +543,6 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
@@ -538,9 +558,16 @@ CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_STOWAWAY is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
@@ -558,9 +585,11 @@ CONFIG_SERIO_LIBPS2=y
# Character devices
#
CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
# CONFIG_SERIAL_NONSTANDARD is not set
#
@@ -579,97 +608,91 @@ CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=16
-
-#
-# IPMI
-#
# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_NVRAM is not set
-# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
-
-#
-# Ftape, the floppy tape device driver
-#
# CONFIG_RAW_DRIVER is not set
-
-#
-# TPM devices
-#
# CONFIG_TCG_TPM is not set
-
-#
-# I2C support
-#
# CONFIG_I2C is not set
-
-#
-# SPI support
-#
# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
-
-#
-# Dallas's 1-wire bus
-#
# CONFIG_W1 is not set
-
-#
-# Hardware Monitoring support
-#
+# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
-# CONFIG_HWMON_VID is not set
-
-#
-# Misc devices
-#
-# CONFIG_SGI_IOC4 is not set
-# CONFIG_TIFM_CORE is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
#
-# LED devices
+# Sonics Silicon Backplane
#
-# CONFIG_NEW_LEDS is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
#
-# LED drivers
+# Multifunction device drivers
#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_WM8400 is not set
#
-# LED Triggers
+# Multimedia devices
#
#
-# Multimedia devices
+# Multimedia core support
#
# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
#
-# Digital Video Broadcasting Devices
+# Multimedia drivers
#
-# CONFIG_DVB is not set
+# CONFIG_DAB is not set
#
# Graphics support
#
-# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_BACKLIGHT is not set
# CONFIG_FB_MODE_HELPERS is not set
# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
CONFIG_FB_ARMCLCD=y
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
#
# Console display driver support
@@ -677,28 +700,17 @@ CONFIG_FB_ARMCLCD=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
# CONFIG_FONTS is not set
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
-
-#
-# Logo configuration
-#
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
CONFIG_LOGO_LINUX_CLUT224=y
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
-
-#
-# Sound
-#
CONFIG_SOUND=y
-
-#
-# Advanced Linux Sound Architecture
-#
+CONFIG_SOUND_OSS_CORE=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
@@ -712,100 +724,65 @@ CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
-
-#
-# Generic devices
-#
-CONFIG_SND_AC97_CODEC=m
-CONFIG_SND_AC97_BUS=m
-# CONFIG_SND_DUMMY is not set
-# CONFIG_SND_MTPAV is not set
-# CONFIG_SND_SERIAL_U16550 is not set
-# CONFIG_SND_MPU401 is not set
-
-#
-# ALSA ARM devices
-#
-CONFIG_SND_ARMAACI=m
-
-#
-# Open Sound System
-#
+CONFIG_SND_VMASTER=y
+CONFIG_SND_AC97_CODEC=y
+# CONFIG_SND_DRIVERS is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_ARMAACI=y
+# CONFIG_SND_SOC is not set
# CONFIG_SOUND_PRIME is not set
-
-#
-# USB support
-#
-CONFIG_USB_ARCH_HAS_HCD=y
-# CONFIG_USB_ARCH_HAS_OHCI is not set
-# CONFIG_USB_ARCH_HAS_EHCI is not set
-# CONFIG_USB is not set
-
-#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
-#
-
-#
-# USB Gadget Support
-#
-# CONFIG_USB_GADGET is not set
-
-#
-# MMC/SD Card support
-#
+CONFIG_AC97_BUS=y
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
-CONFIG_MMC_BLOCK=y
-CONFIG_MMC_ARMMMCI=y
-# CONFIG_MMC_TIFM_SD is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
#
-# Real Time Clock
+# MMC/SD/SDIO Card Drivers
#
-CONFIG_RTC_LIB=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_HCTOSYS=y
-CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
-# CONFIG_RTC_DEBUG is not set
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
#
-# RTC interfaces
+# MMC/SD/SDIO Host Controller Drivers
#
-CONFIG_RTC_INTF_SYSFS=y
-CONFIG_RTC_INTF_PROC=y
-CONFIG_RTC_INTF_DEV=y
-CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+CONFIG_MMC_ARMMMCI=y
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
#
-# RTC drivers
+# Voltage and Current regulators
#
-# CONFIG_RTC_DRV_DS1553 is not set
-# CONFIG_RTC_DRV_DS1742 is not set
-# CONFIG_RTC_DRV_M48T86 is not set
-CONFIG_RTC_DRV_PL031=y
-# CONFIG_RTC_DRV_TEST is not set
-# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
#
# File systems
#
-CONFIG_EXT2_FS=y
-# CONFIG_EXT2_FS_XATTR is not set
-# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4DEV_FS is not set
+# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
-# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
-# CONFIG_INOTIFY_USER is not set
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set
@@ -831,11 +808,11 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set
#
@@ -848,29 +825,28 @@ CONFIG_RAMFS=y
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
-# CONFIG_JFFS_FS is not set
# CONFIG_JFFS2_FS is not set
CONFIG_CRAMFS=y
# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
+CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
# CONFIG_NFS_V4 is not set
-# CONFIG_NFS_DIRECTIO is not set
-# CONFIG_NFSD is not set
CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
@@ -878,17 +854,12 @@ CONFIG_SUNRPC=y
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
-# CONFIG_9P_FS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
@@ -929,64 +900,177 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
-
-#
-# Profiling support
-#
-# CONFIG_PROFILING is not set
+# CONFIG_DLM is not set
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
-# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
CONFIG_MAGIC_SYSRQ=y
# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
-CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_RWSEMS=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
-# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
CONFIG_FRAME_POINTER=y
-# CONFIG_UNWIND_INFO is not set
-CONFIG_FORCED_INLINING=y
-# CONFIG_HEADERS_CHECK is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
-CONFIG_DEBUG_LL=y
-# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_LL is not set
#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
#
-# Cryptographic options
+# Random Number Generation
#
-# CONFIG_CRYPTO is not set
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
#
# Library routines
#
+CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/realview_defconfig b/arch/arm/configs/realview_defconfig
index 907e54344dad..7e253f58ed18 100644
--- a/arch/arm/configs/realview_defconfig
+++ b/arch/arm/configs/realview_defconfig
@@ -1,105 +1,204 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.14-rc2
-# Thu Sep 29 14:50:10 2005
+# Linux kernel version: 2.6.28-rc2
+# Mon Nov 10 14:39:48 2008
#
CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
-CONFIG_UID16=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
#
-# Code maturity level options
+# General setup
#
-# CONFIG_EXPERIMENTAL is not set
-CONFIG_CLEAN_COMPILE=y
+CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_SYSCTL=y
+# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
-CONFIG_HOTPLUG=y
-CONFIG_KOBJECT_UEVENT=y
# CONFIG_IKCONFIG is not set
-CONFIG_INITRAMFS_SOURCE=""
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
CONFIG_EPOLL=y
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
CONFIG_SHMEM=y
-CONFIG_CC_ALIGN_FUNCTIONS=0
-CONFIG_CC_ALIGN_LABELS=0
-CONFIG_CC_ALIGN_LOOPS=0
-CONFIG_CC_ALIGN_JUMPS=0
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
-
-#
-# Loadable module support
-#
CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
CONFIG_MODULE_UNLOAD=y
-CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
-# CONFIG_KMOD is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="deadline"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
#
# System Type
#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+CONFIG_ARCH_REALVIEW=y
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_CO285 is not set
# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
-# CONFIG_ARCH_INTEGRATOR is not set
-# CONFIG_ARCH_IOP3XX is not set
-# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_VERSATILE is not set
-CONFIG_ARCH_REALVIEW=y
-# CONFIG_ARCH_IMX is not set
-# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_MSM is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
#
# RealView platform type
#
CONFIG_MACH_REALVIEW_EB=y
+# CONFIG_REALVIEW_EB_A9MP is not set
+CONFIG_REALVIEW_EB_ARM11MP=y
+# CONFIG_REALVIEW_EB_ARM11MP_REVB is not set
+CONFIG_MACH_REALVIEW_PB11MP=y
+CONFIG_MACH_REALVIEW_PB1176=y
+# CONFIG_MACH_REALVIEW_PBA8 is not set
#
# Processor Type
#
CONFIG_CPU_32=y
-CONFIG_CPU_ARM926T=y
-# CONFIG_CPU_V6 is not set
-CONFIG_CPU_32v5=y
-CONFIG_CPU_ABRT_EV5TJ=y
-CONFIG_CPU_CACHE_VIVT=y
-CONFIG_CPU_COPY_V4WB=y
-CONFIG_CPU_TLB_V4WBI=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+# CONFIG_CPU_V7 is not set
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V6=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
#
# Processor Features
@@ -107,8 +206,9 @@ CONFIG_CPU_TLB_V4WBI=y
CONFIG_ARM_THUMB=y
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
-# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
-# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
CONFIG_ARM_GIC=y
CONFIG_ICST307=y
@@ -116,20 +216,41 @@ CONFIG_ICST307=y
# Bus support
#
CONFIG_ARM_AMBA=y
-CONFIG_ISA_DMA_API=y
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
# CONFIG_PCCARD is not set
#
# Kernel Features
#
-# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+# CONFIG_SMP is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
CONFIG_ALIGNMENT_TRAP=y
#
@@ -139,6 +260,12 @@ CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=ttyAMA0 mem=128M"
# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
#
# Floating point emulation
@@ -147,26 +274,24 @@ CONFIG_CMDLINE="root=/dev/nfs nfsroot=10.1.69.3:/work/nfsroot ip=dhcp console=tt
#
# At least one emulation must be selected
#
-CONFIG_FPE_NWFPE=y
-# CONFIG_FPE_NWFPE_XP is not set
-# CONFIG_VFP is not set
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_VFP=y
#
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set
-# CONFIG_ARTHUR is not set
#
# Power management options
#
# CONFIG_PM is not set
-
-#
-# Networking
-#
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y
#
@@ -175,6 +300,11 @@ CONFIG_NET=y
CONFIG_PACKET=y
# CONFIG_PACKET_MMAP is not set
CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
@@ -186,34 +316,56 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
-CONFIG_TCP_CONG_BIC=y
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
# CONFIG_NET_SCHED is not set
-# CONFIG_NET_CLS_ROUTE is not set
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
-# CONFIG_IEEE80211 is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
#
# Device Drivers
@@ -222,30 +374,37 @@ CONFIG_TCP_CONG_BIC=y
#
# Generic Driver Options
#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_FW_LOADER is not set
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
-
-#
-# Memory Technology Devices (MTD)
-#
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
-# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_CONCAT=y
CONFIG_MTD_PARTITIONS=y
# CONFIG_MTD_REDBOOT_PARTS is not set
CONFIG_MTD_CMDLINE_PARTS=y
# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
#
# RAM/ROM/Flash chip drivers
@@ -266,7 +425,6 @@ CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
-CONFIG_MTD_CFI_AMDSTD_RETRY=0
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
@@ -279,7 +437,6 @@ CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
# CONFIG_MTD_PHYSMAP is not set
CONFIG_MTD_ARM_INTEGRATOR=y
-# CONFIG_MTD_EDB7312 is not set
# CONFIG_MTD_PLATRAM is not set
#
@@ -288,7 +445,7 @@ CONFIG_MTD_ARM_INTEGRATOR=y
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
-# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_BLOCK2MTD is not set
#
# Disk-On-Chip Device Drivers
@@ -296,121 +453,81 @@ CONFIG_MTD_ARM_INTEGRATOR=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-
-#
-# NAND Flash Device Drivers
-#
# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
#
-# Parallel port support
+# UBI - Unsorted block images
#
+# CONFIG_MTD_UBI is not set
# CONFIG_PARPORT is not set
-
-#
-# Plug and Play support
-#
-
-#
-# Block devices
-#
+CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_RAM is not set
-CONFIG_BLK_DEV_RAM_COUNT=16
# CONFIG_CDROM_PKTCDVD is not set
-
-#
-# IO Schedulers
-#
-CONFIG_IOSCHED_NOOP=y
-# CONFIG_IOSCHED_AS is not set
-CONFIG_IOSCHED_DEADLINE=y
-# CONFIG_IOSCHED_CFQ is not set
# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
#
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
# CONFIG_MD is not set
-
-#
-# Fusion MPT device support
-#
-# CONFIG_FUSION is not set
-
-#
-# IEEE 1394 (FireWire) support
-#
-
-#
-# I2O device support
-#
-
-#
-# Network device support
-#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
-
-#
-# PHY device support
-#
+# CONFIG_VETH is not set
# CONFIG_PHYLIB is not set
-
-#
-# Ethernet (10 or 100Mbit)
-#
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
+# CONFIG_AX88796 is not set
CONFIG_SMC91X=y
# CONFIG_DM9000 is not set
-
-#
-# Ethernet (1000 Mbit)
-#
-
-#
-# Ethernet (10000 Mbit)
-#
-
-#
-# Token Ring devices
-#
-
-#
-# Wireless LAN (non-hamradio)
-#
-# CONFIG_NET_RADIO is not set
-
-#
-# Wan interfaces
-#
+CONFIG_SMC911X=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
# CONFIG_WAN is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
-
-#
-# ISDN subsystem
-#
# CONFIG_ISDN is not set
#
# Input device support
#
CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
#
# Userland interfaces
@@ -420,7 +537,6 @@ CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
@@ -433,11 +549,19 @@ CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
@@ -455,8 +579,11 @@ CONFIG_SERIO_LIBPS2=y
# Character devices
#
CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
# CONFIG_SERIAL_NONSTANDARD is not set
#
@@ -475,73 +602,91 @@ CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=16
-
-#
-# IPMI
-#
# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
+# CONFIG_HW_RANDOM is not set
# CONFIG_NVRAM is not set
-# CONFIG_RTC is not set
-# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
-
-#
-# Ftape, the floppy tape device driver
-#
# CONFIG_RAW_DRIVER is not set
-
-#
-# TPM devices
-#
-
-#
-# I2C support
-#
+# CONFIG_TCG_TPM is not set
# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
#
-# Hardware Monitoring support
+# Sonics Silicon Backplane
#
-# CONFIG_HWMON is not set
-# CONFIG_HWMON_VID is not set
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
#
-# Misc devices
+# Multifunction device drivers
#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_WM8400 is not set
#
-# Multimedia Capabilities Port drivers
+# Multimedia devices
#
#
-# Multimedia devices
+# Multimedia core support
#
# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
#
-# Digital Video Broadcasting Devices
+# Multimedia drivers
#
-# CONFIG_DVB is not set
+# CONFIG_DAB is not set
#
# Graphics support
#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
-CONFIG_FB_SOFT_CURSOR=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
# CONFIG_FB_MODE_HELPERS is not set
# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
CONFIG_FB_ARMCLCD=y
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
#
# Console display driver support
@@ -549,27 +694,17 @@ CONFIG_FB_ARMCLCD=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
# CONFIG_FONTS is not set
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
-
-#
-# Logo configuration
-#
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
CONFIG_LOGO_LINUX_CLUT224=y
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
-
-#
-# Sound
-#
CONFIG_SOUND=y
-
-#
-# Advanced Linux Sound Architecture
-#
+CONFIG_SOUND_OSS_CORE=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
@@ -577,59 +712,71 @@ CONFIG_SND_PCM=y
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
-
-#
-# Generic devices
-#
-# CONFIG_SND_DUMMY is not set
-# CONFIG_SND_MTPAV is not set
-# CONFIG_SND_SERIAL_U16550 is not set
-# CONFIG_SND_MPU401 is not set
-
-#
-# ALSA ARM devices
-#
-# CONFIG_SND_ARMAACI is not set
-
-#
-# Open Sound System
-#
+CONFIG_SND_VMASTER=y
+CONFIG_SND_AC97_CODEC=y
+# CONFIG_SND_DRIVERS is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_ARMAACI=y
+# CONFIG_SND_SOC is not set
# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
#
-# USB support
+# MMC/SD/SDIO Card Drivers
#
-CONFIG_USB_ARCH_HAS_HCD=y
-# CONFIG_USB_ARCH_HAS_OHCI is not set
-# CONFIG_USB is not set
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
#
-# USB Gadget Support
+# MMC/SD/SDIO Host Controller Drivers
#
-# CONFIG_USB_GADGET is not set
+CONFIG_MMC_ARMMMCI=y
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
#
-# MMC/SD Card support
+# Voltage and Current regulators
#
-# CONFIG_MMC is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
#
# File systems
#
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
-# CONFIG_JBD is not set
+# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set
@@ -654,51 +801,59 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# Pseudo filesystems
#
CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLB_PAGE is not set
-CONFIG_RAMFS=y
-# CONFIG_RELAYFS_FS is not set
+# CONFIG_CONFIGFS_FS is not set
#
# Miscellaneous filesystems
#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
-# CONFIG_JFFS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
# CONFIG_JFFS2_FS is not set
CONFIG_CRAMFS=y
# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
+CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
-# CONFIG_NFSD is not set
+# CONFIG_NFS_V4 is not set
CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
@@ -739,26 +894,71 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
-CONFIG_DEBUG_KERNEL=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
CONFIG_MAGIC_SYSRQ=y
-CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
-# CONFIG_DEBUG_FS is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_LL is not set
#
@@ -766,21 +966,106 @@ CONFIG_DEBUG_ERRORS=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
#
-# Cryptographic options
+# Compression
#
-# CONFIG_CRYPTO is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
#
-# Hardware crypto devices
+# Random Number Generation
#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
#
# Library routines
#
+CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/s3c6400_defconfig b/arch/arm/configs/s3c6400_defconfig
new file mode 100644
index 000000000000..cf3c1b5d7048
--- /dev/null
+++ b/arch/arm/configs/s3c6400_defconfig
@@ -0,0 +1,845 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc3
+# Mon Nov 3 10:10:30 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+# CONFIG_GENERIC_TIME is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
+CONFIG_MMU=y
+CONFIG_NO_IOPORT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+CONFIG_LSF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+CONFIG_ARCH_S3C64XX=y
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+CONFIG_PLAT_S3C64XX=y
+CONFIG_CPU_S3C6400_INIT=y
+CONFIG_CPU_S3C6400_CLOCK=y
+CONFIG_S3C64XX_SETUP_I2C0=y
+CONFIG_S3C64XX_SETUP_I2C1=y
+CONFIG_PLAT_S3C=y
+
+#
+# Boot options
+#
+CONFIG_S3C_BOOT_ERROR_RESET=y
+
+#
+# Power management
+#
+CONFIG_S3C_LOWLEVEL_UART_PORT=0
+CONFIG_S3C_GPIO_SPACE=0
+CONFIG_S3C_GPIO_TRACK=y
+CONFIG_S3C_GPIO_PULL_UPDOWN=y
+CONFIG_S3C_GPIO_CFG_S3C24XX=y
+CONFIG_S3C_GPIO_CFG_S3C64XX=y
+CONFIG_S3C_DEV_HSMMC=y
+CONFIG_S3C_DEV_HSMMC1=y
+CONFIG_S3C_DEV_I2C1=y
+CONFIG_CPU_S3C6410=y
+CONFIG_S3C6410_SETUP_SDHCI=y
+CONFIG_MACH_SMDK6410=y
+CONFIG_SMDK6410_SD_CH0=y
+# CONFIG_SMDK6410_SD_CH1 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_V6=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V6=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_ARM_VIC=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_CMDLINE="console=ttySAC0,115200 root=/dev/ram init=/bin/bash initrd=0x51000000,4M"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_VFP=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_UARTS=4
+# CONFIG_SERIAL_SAMSUNG_DEBUG is not set
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_SERIAL_S3C6400=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_S3C2410=y
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+CONFIG_AT24=y
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7473 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+CONFIG_HID_DEBUG=y
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_PID is not set
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_COMPAT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+
+#
+# Enable Host or Gadget support to see Inventra options
+#
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+# CONFIG_USB_GADGET is not set
+CONFIG_MMC=y
+CONFIG_MMC_DEBUG=y
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=y
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_S3C=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_DEBUG_LL=y
+# CONFIG_DEBUG_ICEDCC is not set
+CONFIG_DEBUG_S3C_PORT=y
+CONFIG_DEBUG_S3C_UART=0
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/w90p910_defconfig b/arch/arm/configs/w90p910_defconfig
new file mode 100644
index 000000000000..56bda7c6d670
--- /dev/null
+++ b/arch/arm/configs/w90p910_defconfig
@@ -0,0 +1,626 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.27-rc8-git8
+# Sat Nov 15 10:05:00 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+# CONFIG_GENERIC_TIME is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+CONFIG_USER_NS=y
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
+# CONFIG_HAVE_IOREMAP_PROT is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_ARCH_TRACEHOOK is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+# CONFIG_USE_GENERIC_SMP_HELPERS is not set
+# CONFIG_HAVE_CLK is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_MODULES is not set
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_LSF=y
+CONFIG_BLK_DEV_BSG=y
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+CONFIG_ARCH_W90X900=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+CONFIG_CPU_W90P910=y
+
+#
+# W90P910 Machines
+#
+CONFIG_MACH_W90P910EVB=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_CMDLINE="root=/dev/ram0 console=ttyS0,115200n8 initrd=0xa00000,4000000 mem=64M"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_W90X900=y
+# CONFIG_SERIAL_W90X900_PORT1 is not set
+# CONFIG_SERIAL_W90X900_PORT2 is not set
+# CONFIG_SERIAL_W90X900_PORT3 is not set
+# CONFIG_SERIAL_W90X900_PORT4 is not set
+CONFIG_SERIAL_W90X900_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_FTRACE is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC32 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index de6c59f814a1..6cbd8fdc9f1f 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -10,11 +10,11 @@
#ifndef _ASMARM_CACHEFLUSH_H
#define _ASMARM_CACHEFLUSH_H
-#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/glue.h>
#include <asm/shmparam.h>
+#include <asm/cachetype.h>
#define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT)
@@ -296,16 +296,6 @@ static inline void outer_flush_range(unsigned long start, unsigned long end)
#endif
/*
- * flush_cache_vmap() is used when creating mappings (eg, via vmap,
- * vmalloc, ioremap etc) in kernel space for pages. 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.
- */
-#define flush_cache_vmap(start, end) flush_cache_all()
-#define flush_cache_vunmap(start, end) flush_cache_all()
-
-/*
* 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.
@@ -444,4 +434,29 @@ static inline void flush_ioremap_region(unsigned long phys, void __iomem *virt,
dmac_inv_range(start, start + size);
}
+/*
+ * 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)
+{
+ if (!cache_is_vipt_nonaliasing())
+ flush_cache_all();
+ else
+ /*
+ * 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)
+{
+ if (!cache_is_vipt_nonaliasing())
+ flush_cache_all();
+}
+
#endif
diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h
new file mode 100644
index 000000000000..b6ec7c627b39
--- /dev/null
+++ b/arch/arm/include/asm/clkdev.h
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/include/asm/clkdev.h
+ *
+ * Copyright (C) 2008 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.
+ *
+ * Helper for the clk API to assist looking up a struct clk.
+ */
+#ifndef __ASM_CLKDEV_H
+#define __ASM_CLKDEV_H
+
+struct clk;
+
+struct clk_lookup {
+ struct list_head node;
+ const char *dev_id;
+ const char *con_id;
+ struct clk *clk;
+};
+
+struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
+ const char *dev_fmt, ...);
+
+void clkdev_add(struct clk_lookup *cl);
+void clkdev_drop(struct clk_lookup *cl);
+
+#endif
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 4ed149cbb32a..22cb14ec3438 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -69,7 +69,9 @@ extern void dma_cache_maint(const void *kaddr, size_t size, int rw);
*/
static inline int dma_supported(struct device *dev, u64 mask)
{
- return dev->dma_mask && *dev->dma_mask != 0;
+ if (mask < ISA_DMA_THRESHOLD)
+ return 0;
+ return 1;
}
static inline int dma_set_mask(struct device *dev, u64 dma_mask)
diff --git a/arch/arm/include/asm/dma.h b/arch/arm/include/asm/dma.h
index 75154b193117..df5638f3643a 100644
--- a/arch/arm/include/asm/dma.h
+++ b/arch/arm/include/asm/dma.h
@@ -1,12 +1,7 @@
#ifndef __ASM_ARM_DMA_H
#define __ASM_ARM_DMA_H
-typedef unsigned int dmach_t;
-
-#include <linux/spinlock.h>
-#include <asm/system.h>
-#include <asm/scatterlist.h>
-#include <mach/dma.h>
+#include <asm/memory.h>
/*
* This is the maximum virtual address which can be DMA'd from.
@@ -15,6 +10,19 @@ typedef unsigned int dmach_t;
#define MAX_DMA_ADDRESS 0xffffffff
#endif
+#ifdef CONFIG_ISA_DMA_API
+/*
+ * This is used to support drivers written for the x86 ISA DMA API.
+ * It should not be re-used except for that purpose.
+ */
+#include <linux/spinlock.h>
+#include <asm/system.h>
+#include <asm/scatterlist.h>
+
+typedef unsigned int dmach_t;
+
+#include <mach/isa-dma.h>
+
/*
* DMA modes
*/
@@ -140,4 +148,6 @@ extern int isa_dma_bridge_buggy;
#define isa_dma_bridge_buggy (0)
#endif
-#endif /* _ARM_DMA_H */
+#endif /* CONFIG_ISA_DMA_API */
+
+#endif /* __ASM_ARM_DMA_H */
diff --git a/arch/arm/include/asm/hardware/iomd.h b/arch/arm/include/asm/hardware/iomd.h
index 9c5afbd71a69..f9ee69e4f53e 100644
--- a/arch/arm/include/asm/hardware/iomd.h
+++ b/arch/arm/include/asm/hardware/iomd.h
@@ -32,19 +32,11 @@
#define IOMD_KARTRX (0x004)
#define IOMD_KCTRL (0x008)
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_IOLINES (0x00C)
-#endif
-
#define IOMD_IRQSTATA (0x010)
#define IOMD_IRQREQA (0x014)
#define IOMD_IRQCLRA (0x014)
#define IOMD_IRQMASKA (0x018)
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_SUSMODE (0x01C)
-#endif
-
#define IOMD_IRQSTATB (0x020)
#define IOMD_IRQREQB (0x024)
#define IOMD_IRQMASKB (0x028)
@@ -53,10 +45,6 @@
#define IOMD_FIQREQ (0x034)
#define IOMD_FIQMASK (0x038)
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_CLKCTL (0x03C)
-#endif
-
#define IOMD_T0CNTL (0x040)
#define IOMD_T0LTCHL (0x040)
#define IOMD_T0CNTH (0x044)
@@ -71,18 +59,6 @@
#define IOMD_T1GO (0x058)
#define IOMD_T1LATCH (0x05c)
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_IRQSTATC (0x060)
-#define IOMD_IRQREQC (0x064)
-#define IOMD_IRQMASKC (0x068)
-
-#define IOMD_VIDMUX (0x06c)
-
-#define IOMD_IRQSTATD (0x070)
-#define IOMD_IRQREQD (0x074)
-#define IOMD_IRQMASKD (0x078)
-#endif
-
#define IOMD_ROMCR0 (0x080)
#define IOMD_ROMCR1 (0x084)
#ifdef CONFIG_ARCH_RPC
@@ -100,11 +76,6 @@
#define IOMD_MOUSEY (0x0A4)
#endif
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_MSEDAT (0x0A8)
-#define IOMD_MSECTL (0x0Ac)
-#endif
-
#ifdef CONFIG_ARCH_RPC
#define IOMD_DMATCR (0x0C0)
#endif
@@ -113,18 +84,6 @@
#ifdef CONFIG_ARCH_RPC
#define IOMD_DMAEXT (0x0CC)
#endif
-#ifdef CONFIG_ARCH_CLPS7500
-#define IOMD_ASTCR (0x0CC)
-#define IOMD_DRAMCR (0x0D0)
-#define IOMD_SELFREF (0x0D4)
-#define IOMD_ATODICR (0x0E0)
-#define IOMD_ATODSR (0x0E4)
-#define IOMD_ATODCC (0x0E8)
-#define IOMD_ATODCNT1 (0x0EC)
-#define IOMD_ATODCNT2 (0x0F0)
-#define IOMD_ATODCNT3 (0x0F4)
-#define IOMD_ATODCNT4 (0x0F8)
-#endif
#ifdef CONFIG_ARCH_RPC
#define DMA_EXT_IO0 1
diff --git a/arch/arm/include/asm/hardware/vic.h b/arch/arm/include/asm/hardware/vic.h
index 263f2c362a30..f87328d4a180 100644
--- a/arch/arm/include/asm/hardware/vic.h
+++ b/arch/arm/include/asm/hardware/vic.h
@@ -29,15 +29,17 @@
#define VIC_INT_SOFT 0x18
#define VIC_INT_SOFT_CLEAR 0x1c
#define VIC_PROTECT 0x20
-#define VIC_VECT_ADDR 0x30
-#define VIC_DEF_VECT_ADDR 0x34
+#define VIC_PL190_VECT_ADDR 0x30 /* PL190 only */
+#define VIC_PL190_DEF_VECT_ADDR 0x34 /* PL190 only */
-#define VIC_VECT_ADDR0 0x100 /* 0 to 15 */
-#define VIC_VECT_CNTL0 0x200 /* 0 to 15 */
+#define VIC_VECT_ADDR0 0x100 /* 0 to 15 (0..31 PL192) */
+#define VIC_VECT_CNTL0 0x200 /* 0 to 15 (0..31 PL192) */
#define VIC_ITCR 0x300 /* VIC test control register */
#define VIC_VECT_CNTL_ENABLE (1 << 5)
+#define VIC_PL192_VECT_ADDR 0xF00
+
#ifndef __ASSEMBLY__
void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources);
#endif
diff --git a/arch/arm/include/asm/hwcap.h b/arch/arm/include/asm/hwcap.h
index 81f4c899a555..bda489f9f017 100644
--- a/arch/arm/include/asm/hwcap.h
+++ b/arch/arm/include/asm/hwcap.h
@@ -16,6 +16,7 @@
#define HWCAP_IWMMXT 512
#define HWCAP_CRUNCH 1024
#define HWCAP_THUMBEE 2048
+#define HWCAP_NEON 4096
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
/*
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index a8094451be57..d2a59cfc30ce 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -80,6 +80,14 @@ extern void __iounmap(volatile void __iomem *addr);
extern void __readwrite_bug(const char *fn);
/*
+ * A typesafe __io() helper
+ */
+static inline void __iomem *__typesafe_io(unsigned long addr)
+{
+ return (void __iomem *)addr;
+}
+
+/*
* Now, pick up the machine-defined IO definitions
*/
#include <mach/io.h>
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index a0009aa5d157..328f14a8b790 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -7,10 +7,6 @@
#define irq_canonicalize(i) (i)
#endif
-#ifndef NR_IRQS
-#define NR_IRQS 128
-#endif
-
/*
* Use this value to indicate lack of interrupt
* capability
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 77764301844b..0202a7c20e62 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -112,10 +112,8 @@
* private definitions which should NOT be used outside memory.h
* files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
*/
-#ifndef __virt_to_phys
#define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
-#endif
/*
* Convert a physical address to a Page Frame Number and back
@@ -180,6 +178,11 @@ static inline void *phys_to_virt(unsigned long x)
* memory. Use of these is *deprecated* (and that doesn't mean
* use the __ prefixed forms instead.) See dma-mapping.h.
*/
+#ifndef __virt_to_bus
+#define __virt_to_bus __virt_to_phys
+#define __bus_to_virt __phys_to_virt
+#endif
+
static inline __deprecated unsigned long virt_to_bus(void *x)
{
return __virt_to_bus((unsigned long)x);
diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h
index 0559f37c2a27..263fed05ea33 100644
--- a/arch/arm/include/asm/mmu_context.h
+++ b/arch/arm/include/asm/mmu_context.h
@@ -14,6 +14,7 @@
#define __ASM_ARM_MMU_CONTEXT_H
#include <linux/compiler.h>
+#include <linux/sched.h>
#include <asm/cacheflush.h>
#include <asm/cachetype.h>
#include <asm/proc-fns.h>
diff --git a/arch/arm/include/asm/mtd-xip.h b/arch/arm/include/asm/mtd-xip.h
index d8fbe2d9b8b9..d79d66d2cf71 100644
--- a/arch/arm/include/asm/mtd-xip.h
+++ b/arch/arm/include/asm/mtd-xip.h
@@ -15,7 +15,6 @@
#ifndef __ARM_MTD_XIP_H__
#define __ARM_MTD_XIP_H__
-#include <mach/hardware.h>
#include <mach/mtd-xip.h>
/* fill instruction prefetch */
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index bed1c0a00368..f341c9dbd662 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -108,32 +108,38 @@
#error Unknown user operations model
#endif
+struct page;
+
struct cpu_user_fns {
- void (*cpu_clear_user_page)(void *p, unsigned long user);
- void (*cpu_copy_user_page)(void *to, const void *from,
- unsigned long user);
+ void (*cpu_clear_user_highpage)(struct page *page, unsigned long vaddr);
+ void (*cpu_copy_user_highpage)(struct page *to, struct page *from,
+ unsigned long vaddr);
};
#ifdef MULTI_USER
extern struct cpu_user_fns cpu_user;
-#define __cpu_clear_user_page cpu_user.cpu_clear_user_page
-#define __cpu_copy_user_page cpu_user.cpu_copy_user_page
+#define __cpu_clear_user_highpage cpu_user.cpu_clear_user_highpage
+#define __cpu_copy_user_highpage cpu_user.cpu_copy_user_highpage
#else
-#define __cpu_clear_user_page __glue(_USER,_clear_user_page)
-#define __cpu_copy_user_page __glue(_USER,_copy_user_page)
+#define __cpu_clear_user_highpage __glue(_USER,_clear_user_highpage)
+#define __cpu_copy_user_highpage __glue(_USER,_copy_user_highpage)
-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 __cpu_clear_user_highpage(struct page *page, unsigned long vaddr);
+extern void __cpu_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr);
#endif
-#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)
+#define clear_user_highpage(page,vaddr) \
+ __cpu_clear_user_highpage(page, vaddr)
+
+#define __HAVE_ARCH_COPY_USER_HIGHPAGE
+#define copy_user_highpage(to,from,vaddr,vma) \
+ __cpu_copy_user_highpage(to, from, vaddr)
-#define clear_page(page) memzero((void *)(page), PAGE_SIZE)
+#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
extern void copy_page(void *to, const void *from);
#undef STRICT_MM_TYPECHECKS
diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
index 6ff33790f47b..1845892260e7 100644
--- a/arch/arm/include/asm/processor.h
+++ b/arch/arm/include/asm/processor.h
@@ -64,7 +64,7 @@ struct thread_struct {
({ \
unsigned long *stack = (unsigned long *)sp; \
set_fs(USER_DS); \
- memzero(regs->uregs, sizeof(regs->uregs)); \
+ memset(regs->uregs, 0, sizeof(regs->uregs)); \
if (current->personality & ADDR_LIMIT_32BIT) \
regs->ARM_cpsr = USR_MODE; \
else \
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index a65413ba121d..f2cd18a0932b 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -209,9 +209,11 @@ struct meminfo {
struct membank bank[NR_BANKS];
};
+extern struct meminfo meminfo;
+
#define for_each_nodebank(iter,mi,no) \
- for (iter = 0; iter < mi->nr_banks; iter++) \
- if (mi->bank[iter].node == no)
+ for (iter = 0; iter < (mi)->nr_banks; iter++) \
+ if ((mi)->bank[iter].node == no)
#define bank_pfn_start(bank) __phys_to_pfn((bank)->start)
#define bank_pfn_end(bank) __phys_to_pfn((bank)->start + (bank)->size)
diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h
index 727b5c042e52..fad70da5911d 100644
--- a/arch/arm/include/asm/smp.h
+++ b/arch/arm/include/asm/smp.h
@@ -114,7 +114,7 @@ extern void local_timer_interrupt(void);
/*
* Stop a local timer interrupt.
*/
-extern void local_timer_stop(unsigned int cpu);
+extern void local_timer_stop(void);
/*
* Platform provides this to acknowledge a local timer IRQ
@@ -123,7 +123,7 @@ extern int local_timer_ack(void);
#else
-static inline void local_timer_stop(unsigned int cpu)
+static inline void local_timer_stop(void)
{
}
@@ -132,7 +132,7 @@ static inline void local_timer_stop(unsigned int cpu)
/*
* Setup a local timer interrupt for a CPU.
*/
-extern void local_timer_setup(unsigned int cpu);
+extern void local_timer_setup(void);
/*
* show local interrupt info
diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index e50c4a39b699..cf4f3aad0fc1 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -21,7 +21,6 @@ extern void * memmove(void *, const void *, __kernel_size_t);
#define __HAVE_ARCH_MEMCHR
extern void * memchr(const void *, int, __kernel_size_t);
-#define __HAVE_ARCH_MEMZERO
#define __HAVE_ARCH_MEMSET
extern void * memset(void *, int, __kernel_size_t);
@@ -39,12 +38,4 @@ extern void __memzero(void *ptr, __kernel_size_t n);
(__p); \
})
-#define memzero(p,n) \
- ({ \
- void *__p = (p); size_t __n = n; \
- if ((__n) != 0) \
- __memzero((__p),(__n)); \
- (__p); \
- })
-
#endif
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 568020b34e3e..811be55f338e 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -3,8 +3,6 @@
#ifdef __KERNEL__
-#include <asm/memory.h>
-
#define CPU_ARCH_UNKNOWN 0
#define CPU_ARCH_ARMv3 1
#define CPU_ARCH_ARMv4 2
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index e98ec60b3400..7897464e0c24 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -11,7 +11,8 @@
/*
* User space memory access functions
*/
-#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/thread_info.h>
#include <asm/errno.h>
#include <asm/memory.h>
#include <asm/domain.h>
@@ -400,7 +401,7 @@ static inline unsigned long __must_check copy_from_user(void *to, const void __u
if (access_ok(VERIFY_READ, from, n))
n = __copy_from_user(to, from, n);
else /* security hole - plug it */
- memzero(to, n);
+ memset(to, 0, n);
return n;
}
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 23af3c972c9a..531e1860e546 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
+#include <linux/sched.h>
#include <linux/string.h>
#include <linux/cryptohash.h>
#include <linux/delay.h>
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index 6c90479e8974..c63842766229 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -95,7 +95,7 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
return ret;
}
-/* run from kstop_machine */
+/* run from ftrace_init with irqs disabled */
int __init ftrace_dyn_arch_init(void *data)
{
ftrace_mcount_set(data);
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index bde52df1c668..991952c644d1 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -18,7 +18,7 @@
__switch_data:
.long __mmap_switched
.long __data_loc @ r4
- .long __data_start @ r5
+ .long _data @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index b8d965dcd6fd..dab48f27263f 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -21,6 +21,7 @@
#include <linux/string.h>
#include <asm/pgtable.h>
+#include <asm/sections.h>
#ifdef CONFIG_XIP_KERNEL
/*
@@ -29,9 +30,8 @@
* MODULES_VADDR is redefined here and not in asm/memory.h to avoid
* recompiling the whole kernel when CONFIG_XIP_KERNEL is turned on/off.
*/
-extern void _etext;
#undef MODULES_VADDR
-#define MODULES_VADDR (((unsigned long)&_etext + ~PGDIR_MASK) & PGDIR_MASK)
+#define MODULES_VADDR (((unsigned long)_etext + ~PGDIR_MASK) & PGDIR_MASK)
#endif
#ifdef CONFIG_MMU
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 1f1eecca7f55..7049815d66d5 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -29,6 +29,7 @@
#include <asm/cputype.h>
#include <asm/elf.h>
#include <asm/procinfo.h>
+#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/cacheflush.h>
@@ -59,9 +60,8 @@ static int __init fpe_setup(char *line)
__setup("fpe=", fpe_setup);
#endif
-extern void paging_init(struct meminfo *, struct machine_desc *desc);
+extern void paging_init(struct machine_desc *desc);
extern void reboot_setup(char *str);
-extern void _text, _etext, __data_start, _edata, _end;
unsigned int processor_id;
EXPORT_SYMBOL(processor_id);
@@ -112,7 +112,6 @@ static struct stack stacks[NR_CPUS];
char elf_platform[ELF_PLATFORM_SIZE];
EXPORT_SYMBOL(elf_platform);
-static struct meminfo meminfo __initdata = { 0, };
static const char *cpu_name;
static const char *machine_name;
static char __initdata command_line[COMMAND_LINE_SIZE];
@@ -367,21 +366,34 @@ static struct machine_desc * __init setup_machine(unsigned int nr)
return list;
}
-static void __init arm_add_memory(unsigned long start, unsigned long size)
+static int __init arm_add_memory(unsigned long start, unsigned long size)
{
- struct membank *bank;
+ struct membank *bank = &meminfo.bank[meminfo.nr_banks];
+
+ if (meminfo.nr_banks >= NR_BANKS) {
+ printk(KERN_CRIT "NR_BANKS too low, "
+ "ignoring memory at %#lx\n", start);
+ return -EINVAL;
+ }
/*
* Ensure that start/size are aligned to a page boundary.
* Size is appropriately rounded down, start is rounded up.
*/
size -= start & ~PAGE_MASK;
-
- bank = &meminfo.bank[meminfo.nr_banks++];
-
bank->start = PAGE_ALIGN(start);
bank->size = size & PAGE_MASK;
bank->node = PHYS_TO_NID(start);
+
+ /*
+ * Check whether this memory region has non-zero size or
+ * invalid node number.
+ */
+ if (bank->size == 0 || bank->node >= MAX_NUMNODES)
+ return -EINVAL;
+
+ meminfo.nr_banks++;
+ return 0;
}
/*
@@ -472,10 +484,10 @@ request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
struct resource *res;
int i;
- kernel_code.start = virt_to_phys(&_text);
- kernel_code.end = virt_to_phys(&_etext - 1);
- kernel_data.start = virt_to_phys(&__data_start);
- kernel_data.end = virt_to_phys(&_end - 1);
+ kernel_code.start = virt_to_phys(_text);
+ kernel_code.end = virt_to_phys(_etext - 1);
+ kernel_data.start = virt_to_phys(_data);
+ kernel_data.end = virt_to_phys(_end - 1);
for (i = 0; i < mi->nr_banks; i++) {
if (mi->bank[i].size == 0)
@@ -539,14 +551,7 @@ __tagtable(ATAG_CORE, parse_tag_core);
static int __init parse_tag_mem32(const struct tag *tag)
{
- if (meminfo.nr_banks >= NR_BANKS) {
- printk(KERN_WARNING
- "Ignoring memory bank 0x%08x size %dKB\n",
- tag->u.mem.start, tag->u.mem.size / 1024);
- return -EINVAL;
- }
- arm_add_memory(tag->u.mem.start, tag->u.mem.size);
- return 0;
+ return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
}
__tagtable(ATAG_MEM, parse_tag_mem32);
@@ -710,15 +715,15 @@ void __init setup_arch(char **cmdline_p)
parse_tags(tags);
}
- 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;
+ 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;
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from);
- paging_init(&meminfo, mdesc);
+ paging_init(mdesc);
request_standard_resources(&meminfo, mdesc);
#ifdef CONFIG_SMP
@@ -772,6 +777,8 @@ static const char *hwcap_str[] = {
"java",
"iwmmxt",
"crunch",
+ "thumbee",
+ "neon",
NULL
};
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index bd905c0a7365..55fa7ff96a3e 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -171,7 +171,7 @@ int __cpuexit __cpu_disable(void)
/*
* Stop the local timer for this CPU.
*/
- local_timer_stop(cpu);
+ local_timer_stop();
/*
* Flush user cache and TLB mappings, and then remove this CPU
@@ -274,7 +274,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
/*
* Setup local timer for this CPU.
*/
- local_timer_setup(cpu);
+ local_timer_setup();
calibrate_delay();
diff --git a/arch/arm/kernel/thumbee.c b/arch/arm/kernel/thumbee.c
index df3f6b7ebcea..9cb7aaca159f 100644
--- a/arch/arm/kernel/thumbee.c
+++ b/arch/arm/kernel/thumbee.c
@@ -25,7 +25,7 @@
/*
* Access to the ThumbEE Handler Base register
*/
-static inline unsigned long teehbr_read()
+static inline unsigned long teehbr_read(void)
{
unsigned long v;
asm("mrc p14, 6, %0, c1, c0, 0\n" : "=r" (v));
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 4898bdcfe7dd..00216071eaf7 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -119,7 +119,7 @@ SECTIONS
#endif
.data : AT(__data_loc) {
- __data_start = .; /* address in memory */
+ _data = .; /* address in memory */
/*
* first, the init task union, aligned
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 30351cd4560d..866f84a586ff 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -38,7 +38,6 @@ else
endif
lib-$(CONFIG_ARCH_RPC) += ecard.o io-acorn.o floppydma.o
-lib-$(CONFIG_ARCH_CLPS7500) += io-acorn.o
lib-$(CONFIG_ARCH_L7200) += io-acorn.o
lib-$(CONFIG_ARCH_SHARK) += io-shark.o
diff --git a/arch/arm/lib/memset.S b/arch/arm/lib/memset.S
index 761eefa76243..650d5923ab83 100644
--- a/arch/arm/lib/memset.S
+++ b/arch/arm/lib/memset.S
@@ -25,7 +25,7 @@
add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3))
/*
* The pointer is now aligned and the length is adjusted. Try doing the
- * memzero again.
+ * memset again.
*/
ENTRY(memset)
diff --git a/arch/arm/mach-aaec2000/Makefile b/arch/arm/mach-aaec2000/Makefile
index a8e462f58bc9..20ec83896c37 100644
--- a/arch/arm/mach-aaec2000/Makefile
+++ b/arch/arm/mach-aaec2000/Makefile
@@ -3,7 +3,7 @@
#
# Common support (must be linked before board specific support)
-obj-y += core.o clock.o
+obj-y += core.o
# Specific board support
obj-$(CONFIG_MACH_AAED2000) += aaed2000.o
diff --git a/arch/arm/mach-aaec2000/clock.c b/arch/arm/mach-aaec2000/clock.c
deleted file mode 100644
index e10ee158d720..000000000000
--- a/arch/arm/mach-aaec2000/clock.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * linux/arch/arm/mach-aaec2000/clock.c
- *
- * Copyright (C) 2005 Nicolas Bellido Y Ortega
- *
- * Based on linux/arch/arm/mach-integrator/clock.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/string.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-
-#include "clock.h"
-
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- mutex_unlock(&clocks_mutex);
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
- module_put(clk->owner);
-}
-EXPORT_SYMBOL(clk_put);
-
-int clk_enable(struct clk *clk)
-{
- return 0;
-}
-EXPORT_SYMBOL(clk_enable);
-
-void clk_disable(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_disable);
-
-unsigned long clk_get_rate(struct clk *clk)
-{
- return clk->rate;
-}
-EXPORT_SYMBOL(clk_get_rate);
-
-long clk_round_rate(struct clk *clk, unsigned long rate)
-{
- return rate;
-}
-EXPORT_SYMBOL(clk_round_rate);
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
- return 0;
-}
-EXPORT_SYMBOL(clk_set_rate);
-
-int clk_register(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init(void)
-{
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-aaec2000/clock.h b/arch/arm/mach-aaec2000/clock.h
deleted file mode 100644
index d4bb74ff613f..000000000000
--- a/arch/arm/mach-aaec2000/clock.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * linux/arch/arm/mach-aaec2000/clock.h
- *
- * Copyright (C) 2005 Nicolas Bellido Y Ortega
- *
- * Based on linux/arch/arm/mach-integrator/clock.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-struct module;
-
-struct clk {
- struct list_head node;
- unsigned long rate;
- struct module *owner;
- const char *name;
- void *data;
-};
-
-int clk_register(struct clk *clk);
-void clk_unregister(struct clk *clk);
diff --git a/arch/arm/mach-aaec2000/core.c b/arch/arm/mach-aaec2000/core.c
index dfb26bc23d1a..50e13965dfed 100644
--- a/arch/arm/mach-aaec2000/core.c
+++ b/arch/arm/mach-aaec2000/core.c
@@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <linux/timex.h>
#include <linux/signal.h>
+#include <linux/clk.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -30,7 +31,6 @@
#include <asm/mach/map.h>
#include "core.h"
-#include "clock.h"
/*
* Common I/O mapping:
@@ -229,9 +229,28 @@ static struct amba_device *amba_devs[] __initdata = {
&clcd_device,
};
-static struct clk aaec2000_clcd_clk = {
- .name = "CLCDCLK",
-};
+void clk_disable(struct clk *clk)
+{
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ return dev && strcmp(dev_name(dev), "mb:16") == 0 ? NULL : ERR_PTR(-ENOENT);
+}
+
+void clk_put(struct clk *clk)
+{
+}
void __init aaec2000_set_clcd_plat_data(struct aaec2000_clcd_info *clcd)
{
@@ -265,8 +284,6 @@ static int __init aaec2000_init(void)
{
int i;
- clk_register(&aaec2000_clcd_clk);
-
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
diff --git a/arch/arm/mach-aaec2000/include/mach/dma.h b/arch/arm/mach-aaec2000/include/mach/dma.h
deleted file mode 100644
index 2da846c72fe7..000000000000
--- a/arch/arm/mach-aaec2000/include/mach/dma.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-aaec2000/include/mach/dma.h
- *
- * Copyright (c) 2005 Nicolas Bellido Y Ortega
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
diff --git a/arch/arm/mach-aaec2000/include/mach/io.h b/arch/arm/mach-aaec2000/include/mach/io.h
index c87c24de1110..ab4fe5d20eaf 100644
--- a/arch/arm/mach-aaec2000/include/mach/io.h
+++ b/arch/arm/mach-aaec2000/include/mach/io.h
@@ -6,15 +6,13 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
/*
* We don't actually have real ISA nor PCI buses, but there is so many
* drivers out there that might just work if we fake them...
*/
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-aaec2000/include/mach/memory.h b/arch/arm/mach-aaec2000/include/mach/memory.h
index 56ae900a482e..c00822543d9f 100644
--- a/arch/arm/mach-aaec2000/include/mach/memory.h
+++ b/arch/arm/mach-aaec2000/include/mach/memory.h
@@ -14,9 +14,6 @@
#define PHYS_OFFSET UL(0xf0000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
/*
* The nodes are the followings:
*
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 5aafb2e2ca7a..323b47f2b52f 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -7,36 +7,43 @@ choice
config ARCH_AT91RM9200
bool "AT91RM9200"
+ select CPU_ARM920T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91SAM9260
bool "AT91SAM9260 or AT91SAM9XE"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91SAM9261
bool "AT91SAM9261"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91SAM9263
bool "AT91SAM9263"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91SAM9RL
bool "AT91SAM9RL"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91SAM9G20
bool "AT91SAM9G20"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
config ARCH_AT91CAP9
bool "AT91CAP9"
+ select CPU_ARM926T
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -235,6 +242,12 @@ config MACH_USB_A9263
Select this if you are using a Calao Systems USB-A9263.
<http://www.calao-systems.com>
+config MACH_NEOCORE926
+ bool "Adeneo NEOCORE926"
+ depends on ARCH_AT91SAM9263
+ help
+ Select this if you are using the Adeneo Neocore 926 board.
+
endif
# ----------------------------------------------------------
@@ -302,7 +315,7 @@ comment "AT91 Board Options"
config MTD_AT91_DATAFLASH_CARD
bool "Enable DataFlash Card support"
- depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK)
+ depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK || MACH_NEOCORE926)
help
Enable support for the DataFlash card.
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index cca612d97ca2..c69ff237fd14 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -11,12 +11,12 @@ obj-$(CONFIG_AT91_PMC_UNIT) += clock.o
# CPU-specific support
obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o
-obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o
+obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o
# AT91RM9200 board-specific support
@@ -47,6 +47,7 @@ obj-$(CONFIG_MACH_AT91SAM9261EK) += board-sam9261ek.o
# AT91SAM9263 board-specific support
obj-$(CONFIG_MACH_AT91SAM9263EK) += board-sam9263ek.o
obj-$(CONFIG_MACH_USB_A9263) += board-usb-a9263.o
+obj-$(CONFIG_MACH_NEOCORE926) += board-neocore926.o
# AT91SAM9RL board-specific support
obj-$(CONFIG_MACH_AT91SAM9RLEK) += board-sam9rlek.o
diff --git a/arch/arm/mach-at91/at91cap9.c b/arch/arm/mach-at91/at91cap9.c
index 0fc0adaebd58..0a38c69fdbc4 100644
--- a/arch/arm/mach-at91/at91cap9.c
+++ b/arch/arm/mach-at91/at91cap9.c
@@ -17,6 +17,8 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+
+#include <mach/cpu.h>
#include <mach/at91cap9.h>
#include <mach/at91_pmc.h>
#include <mach/at91_rstc.h>
@@ -317,6 +319,12 @@ void __init at91cap9_initialize(unsigned long main_clock)
/* Register GPIO subsystem */
at91_gpio_init(at91cap9_gpio, 4);
+
+ /* Remember the silicon revision */
+ if (cpu_is_at91cap9_revB())
+ system_rev = 0xB;
+ else if (cpu_is_at91cap9_revC())
+ system_rev = 0xC;
}
/* --------------------------------------------------------------------
diff --git a/arch/arm/mach-at91/at91cap9_devices.c b/arch/arm/mach-at91/at91cap9_devices.c
index 5ebd4273d353..9eca2209cde6 100644
--- a/arch/arm/mach-at91/at91cap9_devices.c
+++ b/arch/arm/mach-at91/at91cap9_devices.c
@@ -13,6 +13,7 @@
*/
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
@@ -21,6 +22,7 @@
#include <video/atmel_lcdc.h>
#include <mach/board.h>
+#include <mach/cpu.h>
#include <mach/gpio.h>
#include <mach/at91cap9.h>
#include <mach/at91cap9_matrix.h>
@@ -69,6 +71,9 @@ void __init at91_add_device_usbh(struct at91_usbh_data *data)
if (!data)
return;
+ if (cpu_is_at91cap9_revB())
+ set_irq_type(AT91CAP9_ID_UHP, IRQ_TYPE_LEVEL_HIGH);
+
/* Enable VBus control for UHP ports */
for (i = 0; i < data->ports; i++) {
if (data->vbus_pin[i])
@@ -151,8 +156,13 @@ static struct platform_device at91_usba_udc_device = {
void __init at91_add_device_usba(struct usba_platform_data *data)
{
- at91_sys_write(AT91_MATRIX_UDPHS, AT91_MATRIX_SELECT_UDPHS |
- AT91_MATRIX_UDPHS_BYPASS_LOCK);
+ if (cpu_is_at91cap9_revB()) {
+ set_irq_type(AT91CAP9_ID_UDPHS, IRQ_TYPE_LEVEL_HIGH);
+ at91_sys_write(AT91_MATRIX_UDPHS, AT91_MATRIX_SELECT_UDPHS |
+ AT91_MATRIX_UDPHS_BYPASS_LOCK);
+ }
+ else
+ at91_sys_write(AT91_MATRIX_UDPHS, AT91_MATRIX_SELECT_UDPHS);
/*
* Invalid pins are 0 on AT91, but the usba driver is shared
@@ -406,28 +416,13 @@ static struct platform_device at91cap9_nand_device = {
void __init at91_add_device_nand(struct atmel_nand_data *data)
{
- unsigned long csa, mode;
+ unsigned long csa;
if (!data)
return;
csa = at91_sys_read(AT91_MATRIX_EBICSA);
- at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_EBI_CS3A_SMC_SMARTMEDIA | AT91_MATRIX_EBI_VDDIOMSEL_3_3V);
-
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(2) | AT91_SMC_NCS_WRSETUP_(1)
- | AT91_SMC_NRDSETUP_(2) | AT91_SMC_NCS_RDSETUP_(1));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(4) | AT91_SMC_NCS_WRPULSE_(6)
- | AT91_SMC_NRDPULSE_(4) | AT91_SMC_NCS_RDPULSE_(6));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(8) | AT91_SMC_NRDCYCLE_(8));
-
- if (data->bus_width_16)
- mode = AT91_SMC_DBW_16;
- else
- mode = AT91_SMC_DBW_8;
- at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(1));
+ at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_EBI_CS3A_SMC_SMARTMEDIA);
/* enable pin */
if (data->enable_pin)
@@ -865,6 +860,9 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
if (!data)
return;
+ if (cpu_is_at91cap9_revB())
+ set_irq_type(AT91CAP9_ID_LCDC, IRQ_TYPE_LEVEL_HIGH);
+
at91_set_A_periph(AT91_PIN_PC1, 0); /* LCDHSYNC */
at91_set_A_periph(AT91_PIN_PC2, 0); /* LCDDOTCK */
at91_set_A_periph(AT91_PIN_PC3, 0); /* LCDDEN */
diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c
index 72f51d39202c..1ff1bda0a894 100644
--- a/arch/arm/mach-at91/at91rm9200_time.c
+++ b/arch/arm/mach-at91/at91rm9200_time.c
@@ -141,6 +141,15 @@ clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev)
/* Use "raw" primitives so we behave correctly on RT kernels. */
raw_local_irq_save(flags);
+ /*
+ * According to Thomas Gleixner irqs are already disabled here. Simply
+ * removing raw_local_irq_save above (and the matching
+ * raw_local_irq_restore) was not accepted. See
+ * http://thread.gmane.org/gmane.linux.ports.arm.kernel/41174
+ * So for now (2008-11-20) just warn once if irqs were not disabled ...
+ */
+ WARN_ON_ONCE(!raw_irqs_disabled_flags(flags));
+
/* The alarm IRQ uses absolute time (now+delta), not the relative
* time (delta) in our calling convention. Like all clockevents
* using such "match" hardware, we have a race to defend against.
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 7774d17dde74..fdde1ea21b07 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -313,7 +313,7 @@ static struct platform_device at91sam9260_nand_device = {
void __init at91_add_device_nand(struct atmel_nand_data *data)
{
- unsigned long csa, mode;
+ unsigned long csa;
if (!data)
return;
@@ -321,42 +321,6 @@ void __init at91_add_device_nand(struct atmel_nand_data *data)
csa = at91_sys_read(AT91_MATRIX_EBICSA);
at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_CS3A_SMC_SMARTMEDIA);
- if (cpu_is_at91sam9260()) {
- /* Timing for sam9260 */
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(1) | AT91_SMC_NCS_WRSETUP_(0)
- | AT91_SMC_NRDSETUP_(1) | AT91_SMC_NCS_RDSETUP_(0));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3)
- | AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5));
-
- if (data->bus_width_16)
- mode = AT91_SMC_DBW_16;
- else
- mode = AT91_SMC_DBW_8;
- at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2));
- }
-
- if (cpu_is_at91sam9g20()) {
- /* Timing for sam9g20 */
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(2) | AT91_SMC_NCS_WRSETUP_(0)
- | AT91_SMC_NRDSETUP_(2) | AT91_SMC_NCS_RDSETUP_(0));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(4) | AT91_SMC_NCS_WRPULSE_(4)
- | AT91_SMC_NRDPULSE_(4) | AT91_SMC_NCS_RDPULSE_(4));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(7) | AT91_SMC_NRDCYCLE_(7));
-
- if (data->bus_width_16)
- mode = AT91_SMC_DBW_16;
- else
- mode = AT91_SMC_DBW_8;
- at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(3));
- }
-
/* enable pin */
if (data->enable_pin)
at91_set_gpio_output(data->enable_pin, 1);
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 6b89172310c7..17289756f80f 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -223,7 +223,7 @@ static struct platform_device atmel_nand_device = {
void __init at91_add_device_nand(struct atmel_nand_data *data)
{
- unsigned long csa, mode;
+ unsigned long csa;
if (!data)
return;
@@ -231,21 +231,6 @@ void __init at91_add_device_nand(struct atmel_nand_data *data)
csa = at91_sys_read(AT91_MATRIX_EBICSA);
at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_CS3A_SMC_SMARTMEDIA);
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(1) | AT91_SMC_NCS_WRSETUP_(0)
- | AT91_SMC_NRDSETUP_(1) | AT91_SMC_NCS_RDSETUP_(0));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3)
- | AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5));
-
- if (data->bus_width_16)
- mode = AT91_SMC_DBW_16;
- else
- mode = AT91_SMC_DBW_8;
- at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2));
-
/* enable pin */
if (data->enable_pin)
at91_set_gpio_output(data->enable_pin, 1);
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 8b884083f76d..b753cb879d8e 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -382,7 +382,7 @@ static struct platform_device at91sam9263_nand_device = {
void __init at91_add_device_nand(struct atmel_nand_data *data)
{
- unsigned long csa, mode;
+ unsigned long csa;
if (!data)
return;
@@ -390,21 +390,6 @@ void __init at91_add_device_nand(struct atmel_nand_data *data)
csa = at91_sys_read(AT91_MATRIX_EBI0CSA);
at91_sys_write(AT91_MATRIX_EBI0CSA, csa | AT91_MATRIX_EBI0_CS3A_SMC_SMARTMEDIA);
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(1) | AT91_SMC_NCS_WRSETUP_(0)
- | AT91_SMC_NRDSETUP_(1) | AT91_SMC_NCS_RDSETUP_(0));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3)
- | AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5));
-
- if (data->bus_width_16)
- mode = AT91_SMC_DBW_16;
- else
- mode = AT91_SMC_DBW_8;
- at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2));
-
/* enable pin */
if (data->enable_pin)
at91_set_gpio_output(data->enable_pin, 1);
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index 87deb1e1b529..145324f4ec56 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -232,17 +232,6 @@ void __init at91_add_device_nand(struct atmel_nand_data *data)
csa = at91_sys_read(AT91_MATRIX_EBICSA);
at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_CS3A_SMC_SMARTMEDIA);
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(1) | AT91_SMC_NCS_WRSETUP_(0)
- | AT91_SMC_NRDSETUP_(1) | AT91_SMC_NCS_RDSETUP_(0));
-
- at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3)
- | AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3));
-
- at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5));
-
- at91_sys_write(AT91_SMC_MODE(3), AT91_SMC_DBW_8 | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2));
-
/* enable pin */
if (data->enable_pin)
at91_set_gpio_output(data->enable_pin, 1);
diff --git a/arch/arm/mach-at91/board-cam60.c b/arch/arm/mach-at91/board-cam60.c
index cdddca54b938..d3ba29c5d8c8 100644
--- a/arch/arm/mach-at91/board-cam60.c
+++ b/arch/arm/mach-at91/board-cam60.c
@@ -39,7 +39,9 @@
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -151,6 +153,32 @@ static struct atmel_nand_data __initdata cam60_nand_data = {
.partition_info = nand_partitions,
};
+static struct sam9_smc_config __initdata cam60_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init cam60_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &cam60_nand_smc_config);
+
+ at91_add_device_nand(&cam60_nand_data);
+}
+
static void __init cam60_board_init(void)
{
@@ -165,7 +193,7 @@ static void __init cam60_board_init(void)
at91_set_gpio_output(AT91_PIN_PB18, 1);
at91_add_device_usbh(&cam60_usbh_data);
/* NAND */
- at91_add_device_nand(&cam60_nand_data);
+ cam60_add_device_nand();
}
MACHINE_START(CAM60, "KwikByte CAM60")
diff --git a/arch/arm/mach-at91/board-cap9adk.c b/arch/arm/mach-at91/board-cap9adk.c
index 201b89392dcc..83a1a0fef47b 100644
--- a/arch/arm/mach-at91/board-cap9adk.c
+++ b/arch/arm/mach-at91/board-cap9adk.c
@@ -36,17 +36,16 @@
#include <mach/hardware.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
-#include <asm/irq.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/at91cap9_matrix.h>
#include <mach/at91sam9_smc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -195,6 +194,43 @@ static struct atmel_nand_data __initdata cap9adk_nand_data = {
#endif
};
+static struct sam9_smc_config __initdata cap9adk_nand_smc_config = {
+ .ncs_read_setup = 1,
+ .nrd_setup = 2,
+ .ncs_write_setup = 1,
+ .nwe_setup = 2,
+
+ .ncs_read_pulse = 6,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 6,
+ .nwe_pulse = 4,
+
+ .read_cycle = 8,
+ .write_cycle = 8,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 1,
+};
+
+static void __init cap9adk_add_device_nand(void)
+{
+ unsigned long csa;
+
+ csa = at91_sys_read(AT91_MATRIX_EBICSA);
+ at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_EBI_VDDIOMSEL_3_3V);
+
+ /* setup bus-width (8 or 16) */
+ if (cap9adk_nand_data.bus_width_16)
+ cap9adk_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ cap9adk_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &cap9adk_nand_smc_config);
+
+ at91_add_device_nand(&cap9adk_nand_data);
+}
+
/*
* NOR flash
@@ -234,6 +270,24 @@ static struct platform_device cap9adk_nor_flash = {
.num_resources = ARRAY_SIZE(nor_flash_resources),
};
+static struct sam9_smc_config __initdata cap9adk_nor_smc_config = {
+ .ncs_read_setup = 2,
+ .nrd_setup = 4,
+ .ncs_write_setup = 2,
+ .nwe_setup = 4,
+
+ .ncs_read_pulse = 10,
+ .nrd_pulse = 8,
+ .ncs_write_pulse = 10,
+ .nwe_pulse = 8,
+
+ .read_cycle = 16,
+ .write_cycle = 16,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE | AT91_SMC_DBW_16,
+ .tdf_cycles = 1,
+};
+
static __init void cap9adk_add_device_nor(void)
{
unsigned long csa;
@@ -241,18 +295,8 @@ static __init void cap9adk_add_device_nor(void)
csa = at91_sys_read(AT91_MATRIX_EBICSA);
at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_EBI_VDDIOMSEL_3_3V);
- /* set the bus interface characteristics */
- at91_sys_write(AT91_SMC_SETUP(0), AT91_SMC_NWESETUP_(4) | AT91_SMC_NCS_WRSETUP_(2)
- | AT91_SMC_NRDSETUP_(4) | AT91_SMC_NCS_RDSETUP_(2));
-
- at91_sys_write(AT91_SMC_PULSE(0), AT91_SMC_NWEPULSE_(8) | AT91_SMC_NCS_WRPULSE_(10)
- | AT91_SMC_NRDPULSE_(8) | AT91_SMC_NCS_RDPULSE_(10));
-
- at91_sys_write(AT91_SMC_CYCLE(0), AT91_SMC_NWECYCLE_(16) | AT91_SMC_NRDCYCLE_(16));
-
- at91_sys_write(AT91_SMC_MODE(0), AT91_SMC_READMODE | AT91_SMC_WRITEMODE
- | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE
- | AT91_SMC_DBW_16 | AT91_SMC_TDF_(1));
+ /* configure chip-select 0 (NOR) */
+ sam9_smc_configure(0, &cap9adk_nor_smc_config);
platform_device_register(&cap9adk_nor_flash);
}
@@ -330,10 +374,8 @@ static void __init cap9adk_board_init(void)
/* Serial */
at91_add_device_serial();
/* USB Host */
- set_irq_type(AT91CAP9_ID_UHP, IRQ_TYPE_LEVEL_HIGH);
at91_add_device_usbh(&cap9adk_usbh_data);
/* USB HS */
- set_irq_type(AT91CAP9_ID_UDPHS, IRQ_TYPE_LEVEL_HIGH);
at91_add_device_usba(&cap9adk_usba_udc_data);
/* SPI */
at91_add_device_spi(cap9adk_spi_devices, ARRAY_SIZE(cap9adk_spi_devices));
@@ -344,13 +386,12 @@ static void __init cap9adk_board_init(void)
/* Ethernet */
at91_add_device_eth(&cap9adk_macb_data);
/* NAND */
- at91_add_device_nand(&cap9adk_nand_data);
+ cap9adk_add_device_nand();
/* NOR Flash */
cap9adk_add_device_nor();
/* I2C */
at91_add_device_i2c(NULL, 0);
/* LCD Controller */
- set_irq_type(AT91CAP9_ID_LCDC, IRQ_TYPE_LEVEL_HIGH);
at91_add_device_lcdc(&cap9adk_lcdc_data);
/* AC97 */
at91_add_device_ac97(&cap9adk_ac97_data);
diff --git a/arch/arm/mach-at91/board-neocore926.c b/arch/arm/mach-at91/board-neocore926.c
new file mode 100644
index 000000000000..9ba7ba2cc3b1
--- /dev/null
+++ b/arch/arm/mach-at91/board-neocore926.c
@@ -0,0 +1,397 @@
+/*
+ * linux/arch/arm/mach-at91/board-neocore926.c
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2007 Atmel Corporation
+ * Copyright (C) 2008 ADENEO.
+ *
+ * This program is free software; you can redistribute 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/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/fb.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+
+#include <video/atmel_lcdc.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+
+#include "sam9_smc.h"
+#include "generic.h"
+
+
+static void __init neocore926_map_io(void)
+{
+ /* Initialize processor: 20 MHz crystal */
+ at91sam9263_initialize(20000000);
+
+ /* DGBU on ttyS0. (Rx & Tx only) */
+ at91_register_uart(0, 0, 0);
+
+ /* USART0 on ttyS1. (Rx, Tx, RTS, CTS) */
+ at91_register_uart(AT91SAM9263_ID_US0, 1, ATMEL_UART_CTS | ATMEL_UART_RTS);
+
+ /* set serial console to ttyS0 (ie, DBGU) */
+ at91_set_serial_console(0);
+}
+
+static void __init neocore926_init_irq(void)
+{
+ at91sam9263_init_interrupts(NULL);
+}
+
+
+/*
+ * USB Host port
+ */
+static struct at91_usbh_data __initdata neocore926_usbh_data = {
+ .ports = 2,
+ .vbus_pin = { AT91_PIN_PA24, AT91_PIN_PA21 },
+};
+
+/*
+ * USB Device port
+ */
+static struct at91_udc_data __initdata neocore926_udc_data = {
+ .vbus_pin = AT91_PIN_PA25,
+ .pullup_pin = 0, /* pull-up driven by UDC */
+};
+
+
+/*
+ * ADS7846 Touchscreen
+ */
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+static int ads7843_pendown_state(void)
+{
+ return !at91_get_gpio_value(AT91_PIN_PA15); /* Touchscreen PENIRQ */
+}
+
+static struct ads7846_platform_data ads_info = {
+ .model = 7843,
+ .x_min = 150,
+ .x_max = 3830,
+ .y_min = 190,
+ .y_max = 3830,
+ .vref_delay_usecs = 100,
+ .x_plate_ohms = 450,
+ .y_plate_ohms = 250,
+ .pressure_max = 15000,
+ .debounce_max = 1,
+ .debounce_rep = 0,
+ .debounce_tol = (~0),
+ .get_pendown_state = ads7843_pendown_state,
+};
+
+static void __init neocore926_add_device_ts(void)
+{
+ at91_set_B_periph(AT91_PIN_PA15, 1); /* External IRQ1, with pullup */
+ at91_set_gpio_input(AT91_PIN_PC13, 1); /* Touchscreen BUSY signal */
+}
+#else
+static void __init neocore926_add_device_ts(void) {}
+#endif
+
+/*
+ * SPI devices.
+ */
+static struct spi_board_info neocore926_spi_devices[] = {
+#if defined(CONFIG_MTD_AT91_DATAFLASH_CARD)
+ { /* DataFlash card */
+ .modalias = "mtd_dataflash",
+ .chip_select = 0,
+ .max_speed_hz = 15 * 1000 * 1000,
+ .bus_num = 0,
+ },
+#endif
+#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+ {
+ .modalias = "ads7846",
+ .chip_select = 1,
+ .max_speed_hz = 125000 * 16,
+ .bus_num = 0,
+ .platform_data = &ads_info,
+ .irq = AT91SAM9263_ID_IRQ1,
+ },
+#endif
+};
+
+
+/*
+ * MCI (SD/MMC)
+ */
+static struct at91_mmc_data __initdata neocore926_mmc_data = {
+ .wire4 = 1,
+ .det_pin = AT91_PIN_PE18,
+ .wp_pin = AT91_PIN_PE19,
+};
+
+
+/*
+ * MACB Ethernet device
+ */
+static struct at91_eth_data __initdata neocore926_macb_data = {
+ .phy_irq_pin = AT91_PIN_PE31,
+ .is_rmii = 1,
+};
+
+
+/*
+ * NAND flash
+ */
+static struct mtd_partition __initdata neocore926_nand_partition[] = {
+ {
+ .name = "Linux Kernel", /* "Partition 1", */
+ .offset = 0,
+ .size = SZ_8M,
+ },
+ {
+ .name = "Filesystem", /* "Partition 2", */
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = SZ_32M,
+ },
+ {
+ .name = "Free", /* "Partition 3", */
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct mtd_partition * __init nand_partitions(int size, int *num_partitions)
+{
+ *num_partitions = ARRAY_SIZE(neocore926_nand_partition);
+ return neocore926_nand_partition;
+}
+
+static struct atmel_nand_data __initdata neocore926_nand_data = {
+ .ale = 21,
+ .cle = 22,
+ .rdy_pin = AT91_PIN_PB19,
+ .rdy_pin_active_low = 1,
+ .enable_pin = AT91_PIN_PD15,
+ .partition_info = nand_partitions,
+};
+
+static struct sam9_smc_config __initdata neocore926_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 4,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 4,
+ .nwe_pulse = 4,
+
+ .read_cycle = 6,
+ .write_cycle = 6,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init neocore926_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &neocore926_nand_smc_config);
+
+ at91_add_device_nand(&neocore926_nand_data);
+}
+
+
+/*
+ * LCD Controller
+ */
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static struct fb_videomode at91_tft_vga_modes[] = {
+ {
+ .name = "TX09D50VM1CCA @ 60",
+ .refresh = 60,
+ .xres = 240, .yres = 320,
+ .pixclock = KHZ2PICOS(5000),
+
+ .left_margin = 1, .right_margin = 33,
+ .upper_margin = 1, .lower_margin = 0,
+ .hsync_len = 5, .vsync_len = 1,
+
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+};
+
+static struct fb_monspecs at91fb_default_monspecs = {
+ .manufacturer = "HIT",
+ .monitor = "TX09D70VM1CCA",
+
+ .modedb = at91_tft_vga_modes,
+ .modedb_len = ARRAY_SIZE(at91_tft_vga_modes),
+ .hfmin = 15000,
+ .hfmax = 64000,
+ .vfmin = 50,
+ .vfmax = 150,
+};
+
+#define AT91SAM9263_DEFAULT_LCDCON2 (ATMEL_LCDC_MEMOR_LITTLE \
+ | ATMEL_LCDC_DISTYPE_TFT \
+ | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE)
+
+static void at91_lcdc_power_control(int on)
+{
+ at91_set_gpio_value(AT91_PIN_PA30, on);
+}
+
+/* Driver datas */
+static struct atmel_lcdfb_info __initdata neocore926_lcdc_data = {
+ .lcdcon_is_backlight = true,
+ .default_bpp = 16,
+ .default_dmacon = ATMEL_LCDC_DMAEN,
+ .default_lcdcon2 = AT91SAM9263_DEFAULT_LCDCON2,
+ .default_monspecs = &at91fb_default_monspecs,
+ .atmel_lcdfb_power_control = at91_lcdc_power_control,
+ .guard_time = 1,
+ .lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB555,
+};
+
+#else
+static struct atmel_lcdfb_info __initdata neocore926_lcdc_data;
+#endif
+
+
+/*
+ * GPIO Buttons
+ */
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+static struct gpio_keys_button neocore926_buttons[] = {
+ { /* BP1, "leftclic" */
+ .code = BTN_LEFT,
+ .gpio = AT91_PIN_PC5,
+ .active_low = 1,
+ .desc = "left_click",
+ .wakeup = 1,
+ },
+ { /* BP2, "rightclic" */
+ .code = BTN_RIGHT,
+ .gpio = AT91_PIN_PC4,
+ .active_low = 1,
+ .desc = "right_click",
+ .wakeup = 1,
+ },
+};
+
+static struct gpio_keys_platform_data neocore926_button_data = {
+ .buttons = neocore926_buttons,
+ .nbuttons = ARRAY_SIZE(neocore926_buttons),
+};
+
+static struct platform_device neocore926_button_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .platform_data = &neocore926_button_data,
+ }
+};
+
+static void __init neocore926_add_device_buttons(void)
+{
+ at91_set_GPIO_periph(AT91_PIN_PC5, 0); /* left button */
+ at91_set_deglitch(AT91_PIN_PC5, 1);
+ at91_set_GPIO_periph(AT91_PIN_PC4, 0); /* right button */
+ at91_set_deglitch(AT91_PIN_PC4, 1);
+
+ platform_device_register(&neocore926_button_device);
+}
+#else
+static void __init neocore926_add_device_buttons(void) {}
+#endif
+
+
+/*
+ * AC97
+ */
+static struct atmel_ac97_data neocore926_ac97_data = {
+ .reset_pin = AT91_PIN_PA13,
+};
+
+
+static void __init neocore926_board_init(void)
+{
+ /* Serial */
+ at91_add_device_serial();
+
+ /* USB Host */
+ at91_add_device_usbh(&neocore926_usbh_data);
+
+ /* USB Device */
+ at91_add_device_udc(&neocore926_udc_data);
+
+ /* SPI */
+ at91_set_gpio_output(AT91_PIN_PE20, 1); /* select spi0 clock */
+ at91_add_device_spi(neocore926_spi_devices, ARRAY_SIZE(neocore926_spi_devices));
+
+ /* Touchscreen */
+ neocore926_add_device_ts();
+
+ /* MMC */
+ at91_add_device_mmc(1, &neocore926_mmc_data);
+
+ /* Ethernet */
+ at91_add_device_eth(&neocore926_macb_data);
+
+ /* NAND */
+ neocore926_add_device_nand();
+
+ /* I2C */
+ at91_add_device_i2c(NULL, 0);
+
+ /* LCD Controller */
+ at91_add_device_lcdc(&neocore926_lcdc_data);
+
+ /* Push Buttons */
+ neocore926_add_device_buttons();
+
+ /* AC97 */
+ at91_add_device_ac97(&neocore926_ac97_data);
+}
+
+MACHINE_START(NEOCORE926, "ADENEO NEOCORE 926")
+ /* Maintainer: ADENEO */
+ .phys_io = AT91_BASE_SYS,
+ .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
+ .boot_params = AT91_SDRAM_BASE + 0x100,
+ .timer = &at91sam926x_timer,
+ .map_io = neocore926_map_io,
+ .init_irq = neocore926_init_irq,
+ .init_machine = neocore926_board_init,
+MACHINE_END
diff --git a/arch/arm/mach-at91/board-qil-a9260.c b/arch/arm/mach-at91/board-qil-a9260.c
index cfb4571a2e27..4cff9a7e61d2 100644
--- a/arch/arm/mach-at91/board-qil-a9260.c
+++ b/arch/arm/mach-at91/board-qil-a9260.c
@@ -41,8 +41,10 @@
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -147,13 +149,34 @@ static struct atmel_nand_data __initdata ek_nand_data = {
.rdy_pin = AT91_PIN_PC13,
.enable_pin = AT91_PIN_PC14,
.partition_info = nand_partitions,
-#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
- .bus_width_16 = 1,
-#else
- .bus_width_16 = 0,
-#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* MCI (SD/MMC)
*/
@@ -227,7 +250,7 @@ static void __init ek_board_init(void)
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* I2C */
at91_add_device_i2c(NULL, 0);
/* Ethernet */
diff --git a/arch/arm/mach-at91/board-sam9-l9260.c b/arch/arm/mach-at91/board-sam9-l9260.c
index 99bb4cc23a09..b48346977534 100644
--- a/arch/arm/mach-at91/board-sam9-l9260.c
+++ b/arch/arm/mach-at91/board-sam9-l9260.c
@@ -38,7 +38,9 @@
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -148,13 +150,34 @@ static struct atmel_nand_data __initdata ek_nand_data = {
.rdy_pin = AT91_PIN_PC13,
.enable_pin = AT91_PIN_PC14,
.partition_info = nand_partitions,
-#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
- .bus_width_16 = 1,
-#else
- .bus_width_16 = 0,
-#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* MCI (SD/MMC)
@@ -178,7 +201,7 @@ static void __init ek_board_init(void)
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c
index b49eb6e4918a..93a0f8b100eb 100644
--- a/arch/arm/mach-at91/board-sam9260ek.c
+++ b/arch/arm/mach-at91/board-sam9260ek.c
@@ -42,7 +42,10 @@
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -195,6 +198,38 @@ static struct atmel_nand_data __initdata ek_nand_data = {
#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* setup bus-width (8 or 16) */
+ if (ek_nand_data.bus_width_16)
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* MCI (SD/MMC)
@@ -303,7 +338,7 @@ static void __init ek_board_init(void)
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c
index 4977409d4fc6..d5266da55311 100644
--- a/arch/arm/mach-at91/board-sam9261ek.c
+++ b/arch/arm/mach-at91/board-sam9261ek.c
@@ -47,7 +47,9 @@
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/at91sam9_smc.h>
+#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -76,7 +78,7 @@ static void __init ek_init_irq(void)
* DM9000 ethernet device
*/
#if defined(CONFIG_DM9000)
-static struct resource at91sam9261_dm9000_resource[] = {
+static struct resource dm9000_resource[] = {
[0] = {
.start = AT91_CHIPSELECT_2,
.end = AT91_CHIPSELECT_2 + 3,
@@ -98,27 +100,42 @@ static struct dm9000_plat_data dm9000_platdata = {
.flags = DM9000_PLATF_16BITONLY,
};
-static struct platform_device at91sam9261_dm9000_device = {
+static struct platform_device dm9000_device = {
.name = "dm9000",
.id = 0,
- .num_resources = ARRAY_SIZE(at91sam9261_dm9000_resource),
- .resource = at91sam9261_dm9000_resource,
+ .num_resources = ARRAY_SIZE(dm9000_resource),
+ .resource = dm9000_resource,
.dev = {
.platform_data = &dm9000_platdata,
}
};
+/*
+ * SMC timings for the DM9000.
+ * Note: These timings were calculated for MASTER_CLOCK = 100000000 according to the DM9000 timings.
+ */
+static struct sam9_smc_config __initdata dm9000_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 2,
+ .ncs_write_setup = 0,
+ .nwe_setup = 2,
+
+ .ncs_read_pulse = 8,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 8,
+ .nwe_pulse = 4,
+
+ .read_cycle = 16,
+ .write_cycle = 16,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE | AT91_SMC_DBW_16,
+ .tdf_cycles = 1,
+};
+
static void __init ek_add_device_dm9000(void)
{
- /*
- * Configure Chip-Select 2 on SMC for the DM9000.
- * Note: These timings were calculated for MASTER_CLOCK = 100000000
- * according to the DM9000 timings.
- */
- at91_sys_write(AT91_SMC_SETUP(2), AT91_SMC_NWESETUP_(2) | AT91_SMC_NCS_WRSETUP_(0) | AT91_SMC_NRDSETUP_(2) | AT91_SMC_NCS_RDSETUP_(0));
- at91_sys_write(AT91_SMC_PULSE(2), AT91_SMC_NWEPULSE_(4) | AT91_SMC_NCS_WRPULSE_(8) | AT91_SMC_NRDPULSE_(4) | AT91_SMC_NCS_RDPULSE_(8));
- at91_sys_write(AT91_SMC_CYCLE(2), AT91_SMC_NWECYCLE_(16) | AT91_SMC_NRDCYCLE_(16));
- at91_sys_write(AT91_SMC_MODE(2), AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE | AT91_SMC_DBW_16 | AT91_SMC_TDF_(1));
+ /* Configure chip-select 2 (DM9000) */
+ sam9_smc_configure(2, &dm9000_smc_config);
/* Configure Reset signal as output */
at91_set_gpio_output(AT91_PIN_PC10, 0);
@@ -126,7 +143,7 @@ static void __init ek_add_device_dm9000(void)
/* Configure Interrupt pin as input, no pull-up */
at91_set_gpio_input(AT91_PIN_PC11, 0);
- platform_device_register(&at91sam9261_dm9000_device);
+ platform_device_register(&dm9000_device);
}
#else
static void __init ek_add_device_dm9000(void) {}
@@ -197,6 +214,39 @@ static struct atmel_nand_data __initdata ek_nand_data = {
#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* setup bus-width (8 or 16) */
+ if (ek_nand_data.bus_width_16)
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
+
/*
* ADS7846 Touchscreen
*/
@@ -525,7 +575,7 @@ static void __init ek_board_init(void)
/* I2C */
at91_add_device_i2c(NULL, 0);
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* DM9000 ethernet */
ek_add_device_dm9000();
diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c
index 8354015c6a23..57d52528f224 100644
--- a/arch/arm/mach-at91/board-sam9263ek.c
+++ b/arch/arm/mach-at91/board-sam9263ek.c
@@ -46,7 +46,9 @@
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/at91sam9_smc.h>
+#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -203,6 +205,38 @@ static struct atmel_nand_data __initdata ek_nand_data = {
#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* setup bus-width (8 or 16) */
+ if (ek_nand_data.bus_width_16)
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* I2C devices
@@ -385,7 +419,7 @@ static void __init ek_board_init(void)
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* I2C */
at91_add_device_i2c(ek_i2c_devices, ARRAY_SIZE(ek_i2c_devices));
/* LCD Controller */
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index b588ead14d68..81439fe6fb3d 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -37,7 +37,9 @@
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -156,6 +158,38 @@ static struct atmel_nand_data __initdata ek_nand_data = {
#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 2,
+ .ncs_write_setup = 0,
+ .nwe_setup = 2,
+
+ .ncs_read_pulse = 4,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 4,
+ .nwe_pulse = 4,
+
+ .read_cycle = 7,
+ .write_cycle = 7,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 3,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* setup bus-width (8 or 16) */
+ if (ek_nand_data.bus_width_16)
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* MCI (SD/MMC)
@@ -195,7 +229,7 @@ static void __init ek_board_init(void)
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c
index 270851864308..9b937ee4815a 100644
--- a/arch/arm/mach-at91/board-sam9rlek.c
+++ b/arch/arm/mach-at91/board-sam9rlek.c
@@ -29,8 +29,9 @@
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/gpio.h>
-#include <mach/at91sam9_smc.h>
+#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -103,9 +104,34 @@ static struct atmel_nand_data __initdata ek_nand_data = {
.rdy_pin = AT91_PIN_PD17,
.enable_pin = AT91_PIN_PB6,
.partition_info = nand_partitions,
- .bus_width_16 = 0,
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* SPI devices
@@ -188,7 +214,7 @@ static void __init ek_board_init(void)
/* I2C */
at91_add_device_i2c(NULL, 0);
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* MMC */
diff --git a/arch/arm/mach-at91/board-usb-a9260.c b/arch/arm/mach-at91/board-usb-a9260.c
index 7c350357333a..d13304c0bc45 100644
--- a/arch/arm/mach-at91/board-usb-a9260.c
+++ b/arch/arm/mach-at91/board-usb-a9260.c
@@ -41,8 +41,10 @@
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -121,13 +123,34 @@ static struct atmel_nand_data __initdata ek_nand_data = {
.rdy_pin = AT91_PIN_PC13,
.enable_pin = AT91_PIN_PC14,
.partition_info = nand_partitions,
-#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
- .bus_width_16 = 1,
-#else
- .bus_width_16 = 0,
-#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
/*
* GPIO Buttons
*/
@@ -189,7 +212,7 @@ static void __init ek_board_init(void)
/* USB Device */
at91_add_device_udc(&ek_udc_data);
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* I2C */
at91_add_device_i2c(NULL, 0);
/* Ethernet */
diff --git a/arch/arm/mach-at91/board-usb-a9263.c b/arch/arm/mach-at91/board-usb-a9263.c
index 391b566c4571..d96405b7d578 100644
--- a/arch/arm/mach-at91/board-usb-a9263.c
+++ b/arch/arm/mach-at91/board-usb-a9263.c
@@ -40,8 +40,10 @@
#include <mach/hardware.h>
#include <mach/board.h>
#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
#include <mach/at91_shdwc.h>
+#include "sam9_smc.h"
#include "generic.h"
@@ -134,13 +136,35 @@ static struct atmel_nand_data __initdata ek_nand_data = {
.rdy_pin = AT91_PIN_PA22,
.enable_pin = AT91_PIN_PD15,
.partition_info = nand_partitions,
-#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
- .bus_width_16 = 1,
-#else
- .bus_width_16 = 0,
-#endif
};
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
+
/*
* GPIO Buttons
*/
@@ -206,7 +230,7 @@ static void __init ek_board_init(void)
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* NAND */
- at91_add_device_nand(&ek_nand_data);
+ ek_add_device_nand();
/* I2C */
at91_add_device_i2c(NULL, 0);
/* Push Buttons */
diff --git a/arch/arm/mach-at91/include/mach/at91_pmc.h b/arch/arm/mach-at91/include/mach/at91_pmc.h
index 2e3f2894b704..9561e33b8a9a 100644
--- a/arch/arm/mach-at91/include/mach/at91_pmc.h
+++ b/arch/arm/mach-at91/include/mach/at91_pmc.h
@@ -23,6 +23,7 @@
#define AT91_PMC_PCK (1 << 0) /* Processor Clock */
#define AT91RM9200_PMC_UDP (1 << 1) /* USB Devcice Port Clock [AT91RM9200 only] */
#define AT91RM9200_PMC_MCKUDP (1 << 2) /* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */
+#define AT91CAP9_PMC_DDR (1 << 2) /* DDR Clock [AT91CAP9 revC only] */
#define AT91RM9200_PMC_UHP (1 << 4) /* USB Host Port Clock [AT91RM9200 only] */
#define AT91SAM926x_PMC_UHP (1 << 6) /* USB Host Port Clock [AT91SAM926x only] */
#define AT91CAP9_PMC_UHP (1 << 6) /* USB Host Port Clock [AT91CAP9 only] */
@@ -102,10 +103,16 @@
#define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */
#define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */
#define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [AT91CAP9 only] */
+#define AT91_PMC_OSCSEL (1 << 7) /* Slow Clock Oscillator [AT91CAP9 revC only] */
#define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */
#define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */
#define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */
#define AT91_PMC_PCK3RDY (1 << 11) /* Programmable Clock 3 */
#define AT91_PMC_IMR (AT91_PMC + 0x6c) /* Interrupt Mask Register */
+#define AT91_PMC_PROT (AT91_PMC + 0xe4) /* Protect Register [AT91CAP9 revC only] */
+#define AT91_PMC_PROTKEY 0x504d4301 /* Activation Code */
+
+#define AT91_PMC_VER (AT91_PMC + 0xfc) /* PMC Module Version [AT91CAP9 only] */
+
#endif
diff --git a/arch/arm/mach-at91/include/mach/at91cap9.h b/arch/arm/mach-at91/include/mach/at91cap9.h
index 4a4b64135a92..d8c1ededaa75 100644
--- a/arch/arm/mach-at91/include/mach/at91cap9.h
+++ b/arch/arm/mach-at91/include/mach/at91cap9.h
@@ -101,7 +101,9 @@
#define AT91_RTT (0xfffffd20 - AT91_BASE_SYS)
#define AT91_PIT (0xfffffd30 - AT91_BASE_SYS)
#define AT91_WDT (0xfffffd40 - AT91_BASE_SYS)
-#define AT91_GPBR (0xfffffd50 - AT91_BASE_SYS)
+#define AT91_GPBR (cpu_is_at91cap9_revB() ? \
+ (0xfffffd50 - AT91_BASE_SYS) : \
+ (0xfffffd60 - AT91_BASE_SYS))
#define AT91_USART0 AT91CAP9_BASE_US0
#define AT91_USART1 AT91CAP9_BASE_US1
diff --git a/arch/arm/mach-at91/include/mach/cpu.h b/arch/arm/mach-at91/include/mach/cpu.h
index dbfd9f73f80b..c554c3e4d553 100644
--- a/arch/arm/mach-at91/include/mach/cpu.h
+++ b/arch/arm/mach-at91/include/mach/cpu.h
@@ -49,6 +49,17 @@ static inline unsigned long at91_arch_identify(void)
return (at91_sys_read(AT91_DBGU_CIDR) & AT91_CIDR_ARCH);
}
+#ifdef CONFIG_ARCH_AT91CAP9
+#include <mach/at91_pmc.h>
+
+#define ARCH_REVISION_CAP9_B 0x399
+#define ARCH_REVISION_CAP9_C 0x601
+
+static inline unsigned long at91cap9_rev_identify(void)
+{
+ return (at91_sys_read(AT91_PMC_VER));
+}
+#endif
#ifdef CONFIG_ARCH_AT91RM9200
#define cpu_is_at91rm9200() (at91_cpu_identify() == ARCH_ID_AT91RM9200)
@@ -90,8 +101,12 @@ static inline unsigned long at91_arch_identify(void)
#ifdef CONFIG_ARCH_AT91CAP9
#define cpu_is_at91cap9() (at91_cpu_identify() == ARCH_ID_AT91CAP9)
+#define cpu_is_at91cap9_revB() (at91cap9_rev_identify() == ARCH_REVISION_CAP9_B)
+#define cpu_is_at91cap9_revC() (at91cap9_rev_identify() == ARCH_REVISION_CAP9_C)
#else
#define cpu_is_at91cap9() (0)
+#define cpu_is_at91cap9_revB() (0)
+#define cpu_is_at91cap9_revC() (0)
#endif
/*
diff --git a/arch/arm/mach-at91/include/mach/dma.h b/arch/arm/mach-at91/include/mach/dma.h
deleted file mode 100644
index e4f90c177616..000000000000
--- a/arch/arm/mach-at91/include/mach/dma.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-at91/include/mach/dma.h
- *
- * Copyright (C) 2003 SAN People
- *
- * This program is free software; you can redistribute 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
- */
diff --git a/arch/arm/mach-at91/include/mach/io.h b/arch/arm/mach-at91/include/mach/io.h
index 1611bd03f528..0b0cccc46e68 100644
--- a/arch/arm/mach-at91/include/mach/io.h
+++ b/arch/arm/mach-at91/include/mach/io.h
@@ -23,8 +23,8 @@
#define IO_SPACE_LIMIT 0xFFFFFFFF
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#ifndef __ASSEMBLY__
diff --git a/arch/arm/mach-at91/include/mach/memory.h b/arch/arm/mach-at91/include/mach/memory.h
index 9dd1b8c79b08..14f4ef4b6a9e 100644
--- a/arch/arm/mach-at91/include/mach/memory.h
+++ b/arch/arm/mach-at91/include/mach/memory.h
@@ -25,15 +25,4 @@
#define PHYS_OFFSET (AT91_SDRAM_BASE)
-
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-at91/sam9_smc.c b/arch/arm/mach-at91/sam9_smc.c
new file mode 100644
index 000000000000..5eab6aa621d0
--- /dev/null
+++ b/arch/arm/mach-at91/sam9_smc.c
@@ -0,0 +1,47 @@
+/*
+ * linux/arch/arm/mach-at91/sam9_smc.c
+ *
+ * Copyright (C) 2008 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/module.h>
+#include <linux/io.h>
+
+#include <mach/at91sam9_smc.h>
+
+#include "sam9_smc.h"
+
+void __init sam9_smc_configure(int cs, struct sam9_smc_config* config)
+{
+ /* Setup register */
+ at91_sys_write(AT91_SMC_SETUP(cs),
+ AT91_SMC_NWESETUP_(config->nwe_setup)
+ | AT91_SMC_NCS_WRSETUP_(config->ncs_write_setup)
+ | AT91_SMC_NRDSETUP_(config->nrd_setup)
+ | AT91_SMC_NCS_RDSETUP_(config->ncs_read_setup)
+ );
+
+ /* Pulse register */
+ at91_sys_write(AT91_SMC_PULSE(cs),
+ AT91_SMC_NWEPULSE_(config->nwe_pulse)
+ | AT91_SMC_NCS_WRPULSE_(config->ncs_write_pulse)
+ | AT91_SMC_NRDPULSE_(config->nrd_pulse)
+ | AT91_SMC_NCS_RDPULSE_(config->ncs_read_pulse)
+ );
+
+ /* Cycle register */
+ at91_sys_write(AT91_SMC_CYCLE(cs),
+ AT91_SMC_NWECYCLE_(config->write_cycle)
+ | AT91_SMC_NRDCYCLE_(config->read_cycle)
+ );
+
+ /* Mode register */
+ at91_sys_write(AT91_SMC_MODE(cs),
+ config->mode
+ | AT91_SMC_TDF_(config->tdf_cycles)
+ );
+}
diff --git a/arch/arm/mach-at91/sam9_smc.h b/arch/arm/mach-at91/sam9_smc.h
new file mode 100644
index 000000000000..bf72cfb3455b
--- /dev/null
+++ b/arch/arm/mach-at91/sam9_smc.h
@@ -0,0 +1,33 @@
+/*
+ * linux/arch/arm/mach-at91/sam9_smc.
+ *
+ * Copyright (C) 2008 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.
+ */
+
+struct sam9_smc_config {
+ /* Setup register */
+ u8 ncs_read_setup;
+ u8 nrd_setup;
+ u8 ncs_write_setup;
+ u8 nwe_setup;
+
+ /* Pulse register */
+ u8 ncs_read_pulse;
+ u8 nrd_pulse;
+ u8 ncs_write_pulse;
+ u8 nwe_pulse;
+
+ /* Cycle register */
+ u16 read_cycle;
+ u16 write_cycle;
+
+ /* Mode register */
+ u32 mode;
+ u8 tdf_cycles:4;
+};
+
+extern void __init sam9_smc_configure(int cs, struct sam9_smc_config* config);
diff --git a/arch/arm/mach-clps711x/include/mach/dma.h b/arch/arm/mach-clps711x/include/mach/dma.h
deleted file mode 100644
index 0d620e869536..000000000000
--- a/arch/arm/mach-clps711x/include/mach/dma.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-clps711x/include/mach/dma.h
- *
- * Copyright (C) 1997,1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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
- */
diff --git a/arch/arm/mach-clps711x/include/mach/io.h b/arch/arm/mach-clps711x/include/mach/io.h
index 4c8440087679..2e0b3ced8f07 100644
--- a/arch/arm/mach-clps711x/include/mach/io.h
+++ b/arch/arm/mach-clps711x/include/mach/io.h
@@ -20,12 +20,10 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
/*
* We don't support ins[lb]/outs[lb]. Make them fault.
diff --git a/arch/arm/mach-clps711x/include/mach/memory.h b/arch/arm/mach-clps711x/include/mach/memory.h
index 98ec30c97bbe..e522b20bcbc2 100644
--- a/arch/arm/mach-clps711x/include/mach/memory.h
+++ b/arch/arm/mach-clps711x/include/mach/memory.h
@@ -26,25 +26,7 @@
*/
#define PHYS_OFFSET UL(0xc0000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-
-#if defined(CONFIG_ARCH_CDB89712)
-
-#define __virt_to_bus(x) (x)
-#define __bus_to_virt(x) (x)
-
-#elif defined (CONFIG_ARCH_AUTCPU12)
-
-#define __virt_to_bus(x) (x)
-#define __bus_to_virt(x) (x)
-
-#else
+#if !defined(CONFIG_ARCH_CDB89712) && !defined (CONFIG_ARCH_AUTCPU12)
#define __virt_to_bus(x) ((x) - PAGE_OFFSET)
#define __bus_to_virt(x) ((x) + PAGE_OFFSET)
diff --git a/arch/arm/mach-clps7500/Makefile b/arch/arm/mach-clps7500/Makefile
deleted file mode 100644
index 4bd8ebd70e7b..000000000000
--- a/arch/arm/mach-clps7500/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Makefile for the linux kernel.
-#
-
-# Object file lists.
-
-obj-y := core.o
-obj-m :=
-obj-n :=
-obj- :=
-
diff --git a/arch/arm/mach-clps7500/Makefile.boot b/arch/arm/mach-clps7500/Makefile.boot
deleted file mode 100644
index fe16506c1540..000000000000
--- a/arch/arm/mach-clps7500/Makefile.boot
+++ /dev/null
@@ -1,2 +0,0 @@
- zreladdr-y := 0x10008000
-
diff --git a/arch/arm/mach-clps7500/core.c b/arch/arm/mach-clps7500/core.c
deleted file mode 100644
index 7e247c04d41c..000000000000
--- a/arch/arm/mach-clps7500/core.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * linux/arch/arm/mach-clps7500/core.c
- *
- * Copyright (C) 1998 Russell King
- * Copyright (C) 1999 Nexus Electronics Ltd
- *
- * Extra MM routines for CL7500 architecture
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/list.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/serial_8250.h>
-#include <linux/io.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
-#include <asm/mach/time.h>
-
-#include <mach/hardware.h>
-#include <asm/hardware/iomd.h>
-#include <asm/irq.h>
-#include <asm/mach-types.h>
-
-unsigned int vram_size;
-
-static void cl7500_ack_irq_a(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << irq;
- val = iomd_readb(IOMD_IRQMASKA);
- iomd_writeb(val & ~mask, IOMD_IRQMASKA);
- iomd_writeb(mask, IOMD_IRQCLRA);
-}
-
-static void cl7500_mask_irq_a(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << irq;
- val = iomd_readb(IOMD_IRQMASKA);
- iomd_writeb(val & ~mask, IOMD_IRQMASKA);
-}
-
-static void cl7500_unmask_irq_a(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << irq;
- val = iomd_readb(IOMD_IRQMASKA);
- iomd_writeb(val | mask, IOMD_IRQMASKA);
-}
-
-static struct irq_chip clps7500_a_chip = {
- .ack = cl7500_ack_irq_a,
- .mask = cl7500_mask_irq_a,
- .unmask = cl7500_unmask_irq_a,
-};
-
-static void cl7500_mask_irq_b(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKB);
- iomd_writeb(val & ~mask, IOMD_IRQMASKB);
-}
-
-static void cl7500_unmask_irq_b(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKB);
- iomd_writeb(val | mask, IOMD_IRQMASKB);
-}
-
-static struct irq_chip clps7500_b_chip = {
- .ack = cl7500_mask_irq_b,
- .mask = cl7500_mask_irq_b,
- .unmask = cl7500_unmask_irq_b,
-};
-
-static void cl7500_mask_irq_c(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKC);
- iomd_writeb(val & ~mask, IOMD_IRQMASKC);
-}
-
-static void cl7500_unmask_irq_c(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKC);
- iomd_writeb(val | mask, IOMD_IRQMASKC);
-}
-
-static struct irq_chip clps7500_c_chip = {
- .ack = cl7500_mask_irq_c,
- .mask = cl7500_mask_irq_c,
- .unmask = cl7500_unmask_irq_c,
-};
-
-static void cl7500_mask_irq_d(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKD);
- iomd_writeb(val & ~mask, IOMD_IRQMASKD);
-}
-
-static void cl7500_unmask_irq_d(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_IRQMASKD);
- iomd_writeb(val | mask, IOMD_IRQMASKD);
-}
-
-static struct irq_chip clps7500_d_chip = {
- .ack = cl7500_mask_irq_d,
- .mask = cl7500_mask_irq_d,
- .unmask = cl7500_unmask_irq_d,
-};
-
-static void cl7500_mask_irq_dma(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_DMAMASK);
- iomd_writeb(val & ~mask, IOMD_DMAMASK);
-}
-
-static void cl7500_unmask_irq_dma(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_DMAMASK);
- iomd_writeb(val | mask, IOMD_DMAMASK);
-}
-
-static struct irq_chip clps7500_dma_chip = {
- .ack = cl7500_mask_irq_dma,
- .mask = cl7500_mask_irq_dma,
- .unmask = cl7500_unmask_irq_dma,
-};
-
-static void cl7500_mask_irq_fiq(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_FIQMASK);
- iomd_writeb(val & ~mask, IOMD_FIQMASK);
-}
-
-static void cl7500_unmask_irq_fiq(unsigned int irq)
-{
- unsigned int val, mask;
-
- mask = 1 << (irq & 7);
- val = iomd_readb(IOMD_FIQMASK);
- iomd_writeb(val | mask, IOMD_FIQMASK);
-}
-
-static struct irq_chip clps7500_fiq_chip = {
- .ack = cl7500_mask_irq_fiq,
- .mask = cl7500_mask_irq_fiq,
- .unmask = cl7500_unmask_irq_fiq,
-};
-
-static void cl7500_no_action(unsigned int irq)
-{
-}
-
-static struct irq_chip clps7500_no_chip = {
- .ack = cl7500_no_action,
- .mask = cl7500_no_action,
- .unmask = cl7500_no_action,
-};
-
-static struct irqaction irq_isa = {
- .handler = no_action,
- .mask = CPU_MASK_NONE,
- .name = "isa",
-};
-
-static void __init clps7500_init_irq(void)
-{
- unsigned int irq, flags;
-
- iomd_writeb(0, IOMD_IRQMASKA);
- iomd_writeb(0, IOMD_IRQMASKB);
- iomd_writeb(0, IOMD_FIQMASK);
- iomd_writeb(0, IOMD_DMAMASK);
-
- for (irq = 0; irq < NR_IRQS; irq++) {
- flags = IRQF_VALID;
-
- if (irq <= 6 || (irq >= 9 && irq <= 15) ||
- (irq >= 48 && irq <= 55))
- flags |= IRQF_PROBE;
-
- switch (irq) {
- case 0 ... 7:
- set_irq_chip(irq, &clps7500_a_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 8 ... 15:
- set_irq_chip(irq, &clps7500_b_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 16 ... 22:
- set_irq_chip(irq, &clps7500_dma_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 24 ... 31:
- set_irq_chip(irq, &clps7500_c_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 40 ... 47:
- set_irq_chip(irq, &clps7500_d_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 48 ... 55:
- set_irq_chip(irq, &clps7500_no_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
-
- case 64 ... 72:
- set_irq_chip(irq, &clps7500_fiq_chip);
- set_irq_handler(irq, handle_level_irq);
- set_irq_flags(irq, flags);
- break;
- }
- }
-
- setup_irq(IRQ_ISA, &irq_isa);
-}
-
-static struct map_desc cl7500_io_desc[] __initdata = {
- { /* IO space */
- .virtual = (unsigned long)IO_BASE,
- .pfn = __phys_to_pfn(IO_START),
- .length = IO_SIZE,
- .type = MT_DEVICE
- }, { /* ISA space */
- .virtual = ISA_BASE,
- .pfn = __phys_to_pfn(ISA_START),
- .length = ISA_SIZE,
- .type = MT_DEVICE
- }, { /* Flash */
- .virtual = CLPS7500_FLASH_BASE,
- .pfn = __phys_to_pfn(CLPS7500_FLASH_START),
- .length = CLPS7500_FLASH_SIZE,
- .type = MT_DEVICE
- }, { /* LED */
- .virtual = LED_BASE,
- .pfn = __phys_to_pfn(LED_START),
- .length = LED_SIZE,
- .type = MT_DEVICE
- }
-};
-
-static void __init clps7500_map_io(void)
-{
- iotable_init(cl7500_io_desc, ARRAY_SIZE(cl7500_io_desc));
-}
-
-extern void ioctime_init(void);
-extern unsigned long ioc_timer_gettimeoffset(void);
-
-static irqreturn_t
-clps7500_timer_interrupt(int irq, void *dev_id)
-{
- timer_tick();
-
- /* Why not using do_leds interface?? */
- {
- /* Twinkle the lights. */
- static int count, state = 0xff00;
- if (count-- == 0) {
- state ^= 0x100;
- count = 25;
- *((volatile unsigned int *)LED_ADDRESS) = state;
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static struct irqaction clps7500_timer_irq = {
- .name = "CLPS7500 Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = clps7500_timer_interrupt,
-};
-
-/*
- * Set up timer interrupt.
- */
-static void __init clps7500_timer_init(void)
-{
- ioctime_init();
- setup_irq(IRQ_TIMER, &clps7500_timer_irq);
-}
-
-static struct sys_timer clps7500_timer = {
- .init = clps7500_timer_init,
- .offset = ioc_timer_gettimeoffset,
-};
-
-static struct plat_serial8250_port serial_platform_data[] = {
- {
- .mapbase = 0x03010fe0,
- .irq = 10,
- .uartclk = 1843200,
- .regshift = 2,
- .iotype = UPIO_MEM,
- .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_SKIP_TEST,
- },
- {
- .mapbase = 0x03010be0,
- .irq = 0,
- .uartclk = 1843200,
- .regshift = 2,
- .iotype = UPIO_MEM,
- .flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_SKIP_TEST,
- },
- {
- .iobase = ISASLOT_IO + 0x2e8,
- .irq = 41,
- .uartclk = 1843200,
- .regshift = 0,
- .iotype = UPIO_PORT,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
- },
- {
- .iobase = ISASLOT_IO + 0x3e8,
- .irq = 40,
- .uartclk = 1843200,
- .regshift = 0,
- .iotype = UPIO_PORT,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
- },
- { },
-};
-
-static struct platform_device serial_device = {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM,
- .dev = {
- .platform_data = serial_platform_data,
- },
-};
-
-static void __init clps7500_init(void)
-{
- platform_device_register(&serial_device);
-}
-
-MACHINE_START(CLPS7500, "CL-PS7500")
- /* Maintainer: Philip Blundell */
- .phys_io = 0x03000000,
- .io_pg_offst = ((0xe0000000) >> 18) & 0xfffc,
- .map_io = clps7500_map_io,
- .init_irq = clps7500_init_irq,
- .init_machine = clps7500_init,
- .timer = &clps7500_timer,
-MACHINE_END
-
diff --git a/arch/arm/mach-clps7500/include/mach/acornfb.h b/arch/arm/mach-clps7500/include/mach/acornfb.h
deleted file mode 100644
index aea6330c9745..000000000000
--- a/arch/arm/mach-clps7500/include/mach/acornfb.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#define acornfb_valid_pixrate(var) (var->pixclock >= 39325 && var->pixclock <= 40119)
-
-static inline void
-acornfb_vidc20_find_rates(struct vidc_timing *vidc,
- struct fb_var_screeninfo *var)
-{
- u_int bandwidth;
-
- vidc->control |= VIDC20_CTRL_PIX_CK;
-
- /* Calculate bandwidth */
- bandwidth = var->pixclock * 8 / var->bits_per_pixel;
-
- /* Encode bandwidth as VIDC20 setting */
- if (bandwidth > 16667*2)
- vidc->control |= VIDC20_CTRL_FIFO_16;
- else if (bandwidth > 13333*2)
- vidc->control |= VIDC20_CTRL_FIFO_20;
- else if (bandwidth > 11111*2)
- vidc->control |= VIDC20_CTRL_FIFO_24;
- else
- vidc->control |= VIDC20_CTRL_FIFO_28;
-
- vidc->pll_ctl = 0x2020;
-}
-
-#ifdef CONFIG_CHRONTEL_7003
-#define acornfb_default_control() VIDC20_CTRL_PIX_HCLK
-#else
-#define acornfb_default_control() VIDC20_CTRL_PIX_VCLK
-#endif
-
-#define acornfb_default_econtrol() VIDC20_ECTL_DAC | VIDC20_ECTL_REG(3) | VIDC20_ECTL_ECK
diff --git a/arch/arm/mach-clps7500/include/mach/debug-macro.S b/arch/arm/mach-clps7500/include/mach/debug-macro.S
deleted file mode 100644
index af4104e7e84a..000000000000
--- a/arch/arm/mach-clps7500/include/mach/debug-macro.S
+++ /dev/null
@@ -1,21 +0,0 @@
-/* arch/arm/mach-clps7500/include/mach/debug-macro.S
- *
- * Debugging macro include header
- *
- * Copyright (C) 1994-1999 Russell King
- * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
-*/
-
- .macro addruart,rx
- mov \rx, #0xe0000000
- orr \rx, \rx, #0x00010000
- orr \rx, \rx, #0x00000be0
- .endm
-
-#define UART_SHIFT 2
-#include <asm/hardware/debug-8250.S>
diff --git a/arch/arm/mach-clps7500/include/mach/dma.h b/arch/arm/mach-clps7500/include/mach/dma.h
deleted file mode 100644
index 63fcde505498..000000000000
--- a/arch/arm/mach-clps7500/include/mach/dma.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/dma.h
- *
- * Copyright (C) 1999 Nexus Electronics Ltd.
- */
-
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/* DMA is not yet implemented! It should be the same as acorn, copy over.. */
-
-/*
- * This is the maximum DMA address that can be DMAd to.
- * There should not be more than (0xd0000000 - 0xc0000000)
- * bytes of RAM.
- */
-#define MAX_DMA_ADDRESS 0xd0000000
-
-#define DMA_S0 0
-
-#endif /* _ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-clps7500/include/mach/entry-macro.S b/arch/arm/mach-clps7500/include/mach/entry-macro.S
deleted file mode 100644
index 4e7e54144093..000000000000
--- a/arch/arm/mach-clps7500/include/mach/entry-macro.S
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <mach/hardware.h>
-#include <asm/hardware/entry-macro-iomd.S>
-
- .equ ioc_base_high, IOC_BASE & 0xff000000
- .equ ioc_base_low, IOC_BASE & 0x00ff0000
-
- .macro get_irqnr_preamble, base, tmp
- mov \base, #ioc_base_high @ point at IOC
- .if ioc_base_low
- orr \base, \base, #ioc_base_low
- .endif
- .endm
-
- .macro arch_ret_to_user, tmp1, tmp2
- .endm
-
diff --git a/arch/arm/mach-clps7500/include/mach/hardware.h b/arch/arm/mach-clps7500/include/mach/hardware.h
deleted file mode 100644
index a6ad1d44badf..000000000000
--- a/arch/arm/mach-clps7500/include/mach/hardware.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/hardware.h
- *
- * Copyright (C) 1996-1999 Russell King.
- * Copyright (C) 1999 Nexus Electronics Ltd.
- *
- * This file contains the hardware definitions of the
- * CL7500 evaluation board.
- */
-#ifndef __ASM_ARCH_HARDWARE_H
-#define __ASM_ARCH_HARDWARE_H
-
-#include <mach/memory.h>
-#include <asm/hardware/iomd.h>
-
-#ifdef __ASSEMBLY__
-#define IOMEM(x) x
-#else
-#define IOMEM(x) ((void __iomem *)(x))
-#endif
-
-/*
- * What hardware must be present
- */
-#define HAS_IOMD
-#define HAS_VIDC20
-
-/* Hardware addresses of major areas.
- * *_START is the physical address
- * *_SIZE is the size of the region
- * *_BASE is the virtual address
- */
-
-#define IO_START 0x03000000 /* I/O */
-#define IO_SIZE 0x01000000
-#define IO_BASE IOMEM(0xe0000000)
-
-#define ISA_START 0x0c000000 /* ISA */
-#define ISA_SIZE 0x00010000
-#define ISA_BASE 0xe1000000
-
-#define CLPS7500_FLASH_START 0x01000000 /* XXX */
-#define CLPS7500_FLASH_SIZE 0x01000000
-#define CLPS7500_FLASH_BASE 0xe2000000
-
-#define LED_START 0x0302B000
-#define LED_SIZE 0x00001000
-#define LED_BASE 0xe3000000
-#define LED_ADDRESS (LED_BASE + 0xa00)
-
-/* Let's define SCREEN_START for CL7500, even though it's a lie. */
-#define SCREEN_START 0x02000000 /* VRAM */
-#define SCREEN_END 0xdfc00000
-#define SCREEN_BASE 0xdf800000
-
-#define VIDC_BASE (void __iomem *)0xe0400000
-#define IOMD_BASE IOMEM(0xe0200000)
-#define IOC_BASE IOMEM(0xe0200000)
-#define FLOPPYDMA_BASE IOMEM(0xe002a000)
-#define PCIO_BASE IOMEM(0xe0010000)
-
-#define vidc_writel(val) __raw_writel(val, VIDC_BASE)
-
-/* in/out bias for the ISA slot region */
-#define ISASLOT_IO 0x80400000
-
-#endif
diff --git a/arch/arm/mach-clps7500/include/mach/io.h b/arch/arm/mach-clps7500/include/mach/io.h
deleted file mode 100644
index 2ff2860889ed..000000000000
--- a/arch/arm/mach-clps7500/include/mach/io.h
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/io.h
- * from arch/arm/mach-rpc/include/mach/io.h
- *
- * Copyright (C) 1997 Russell King
- *
- * Modifications:
- * 06-Dec-1997 RMK Created.
- */
-#ifndef __ASM_ARM_ARCH_IO_H
-#define __ASM_ARM_ARCH_IO_H
-
-#include <mach/hardware.h>
-
-#define IO_SPACE_LIMIT 0xffffffff
-
-/*
- * GCC is totally crap at loading/storing data. We try to persuade it
- * to do the right thing by using these whereever possible instead of
- * the above.
- */
-#define __arch_base_getb(b,o) \
- ({ \
- unsigned int v, r = (b); \
- __asm__ __volatile__( \
- "ldrb %0, [%1, %2]" \
- : "=r" (v) \
- : "r" (r), "Ir" (o)); \
- v; \
- })
-
-#define __arch_base_getl(b,o) \
- ({ \
- unsigned int v, r = (b); \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2]" \
- : "=r" (v) \
- : "r" (r), "Ir" (o)); \
- v; \
- })
-
-#define __arch_base_putb(v,b,o) \
- ({ \
- unsigned int r = (b); \
- __asm__ __volatile__( \
- "strb %0, [%1, %2]" \
- : \
- : "r" (v), "r" (r), "Ir" (o)); \
- })
-
-#define __arch_base_putl(v,b,o) \
- ({ \
- unsigned int r = (b); \
- __asm__ __volatile__( \
- "str %0, [%1, %2]" \
- : \
- : "r" (v), "r" (r), "Ir" (o)); \
- })
-
-/*
- * We use two different types of addressing - PC style addresses, and ARM
- * addresses. PC style accesses the PC hardware with the normal PC IO
- * addresses, eg 0x3f8 for serial#1. ARM addresses are 0x80000000+
- * and are translated to the start of IO. Note that all addresses are
- * shifted left!
- */
-#define __PORT_PCIO(x) (!((x) & 0x80000000))
-
-/*
- * Dynamic IO functions - let the compiler
- * optimize the expressions
- */
-static inline void __outb (unsigned int value, unsigned int port)
-{
- unsigned long temp;
- __asm__ __volatile__(
- "tst %2, #0x80000000\n\t"
- "mov %0, %4\n\t"
- "addeq %0, %0, %3\n\t"
- "strb %1, [%0, %2, lsl #2] @ outb"
- : "=&r" (temp)
- : "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)
- : "cc");
-}
-
-static inline void __outw (unsigned int value, unsigned int port)
-{
- unsigned long temp;
- __asm__ __volatile__(
- "tst %2, #0x80000000\n\t"
- "mov %0, %4\n\t"
- "addeq %0, %0, %3\n\t"
- "str %1, [%0, %2, lsl #2] @ outw"
- : "=&r" (temp)
- : "r" (value|value<<16), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)
- : "cc");
-}
-
-static inline void __outl (unsigned int value, unsigned int port)
-{
- unsigned long temp;
- __asm__ __volatile__(
- "tst %2, #0x80000000\n\t"
- "mov %0, %4\n\t"
- "addeq %0, %0, %3\n\t"
- "str %1, [%0, %2, lsl #2] @ outl"
- : "=&r" (temp)
- : "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE)
- : "cc");
-}
-
-#define DECLARE_DYN_IN(sz,fnsuffix,instr) \
-static inline unsigned sz __in##fnsuffix (unsigned int port) \
-{ \
- unsigned long temp, value; \
- __asm__ __volatile__( \
- "tst %2, #0x80000000\n\t" \
- "mov %0, %4\n\t" \
- "addeq %0, %0, %3\n\t" \
- "ldr" instr " %1, [%0, %2, lsl #2] @ in" #fnsuffix \
- : "=&r" (temp), "=r" (value) \
- : "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) \
- : "cc"); \
- return (unsigned sz)value; \
-}
-
-static inline unsigned int __ioaddr (unsigned int port) \
-{ \
- if (__PORT_PCIO(port)) \
- return (unsigned int)(PCIO_BASE + (port << 2)); \
- else \
- return (unsigned int)(IO_BASE + (port << 2)); \
-}
-
-#define DECLARE_IO(sz,fnsuffix,instr) \
- DECLARE_DYN_IN(sz,fnsuffix,instr)
-
-DECLARE_IO(char,b,"b")
-DECLARE_IO(short,w,"")
-DECLARE_IO(int,l,"")
-
-#undef DECLARE_IO
-#undef DECLARE_DYN_IN
-
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port) \
-({ \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "strb %0, [%1, %2] @ outbc" \
- : : "r" (value), "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "strb %0, [%1, %2] @ outbc" \
- : : "r" (value), "r" (IO_BASE), "r" ((port) << 2)); \
-})
-
-#define __inbc(port) \
-({ \
- unsigned char result; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "ldrb %0, [%1, %2] @ inbc" \
- : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "ldrb %0, [%1, %2] @ inbc" \
- : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \
- result; \
-})
-
-#define __outwc(value,port) \
-({ \
- unsigned long v = value; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "str %0, [%1, %2] @ outwc" \
- : : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "str %0, [%1, %2] @ outwc" \
- : : "r" (v|v<<16), "r" (IO_BASE), "r" ((port) << 2)); \
-})
-
-#define __inwc(port) \
-({ \
- unsigned short result; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2] @ inwc" \
- : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2] @ inwc" \
- : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \
- result & 0xffff; \
-})
-
-#define __outlc(value,port) \
-({ \
- unsigned long v = value; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "str %0, [%1, %2] @ outlc" \
- : : "r" (v), "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "str %0, [%1, %2] @ outlc" \
- : : "r" (v), "r" (IO_BASE), "r" ((port) << 2)); \
-})
-
-#define __inlc(port) \
-({ \
- unsigned long result; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2] @ inlc" \
- : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port) << 2)); \
- else \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2] @ inlc" \
- : "=r" (result) : "r" (IO_BASE), "r" ((port) << 2)); \
- result; \
-})
-
-#define __ioaddrc(port) \
- (__PORT_PCIO((port)) ? PCIO_BASE + ((port) << 2) : IO_BASE + ((port) << 2))
-
-#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p))
-#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p))
-#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p))
-#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p))
-#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p))
-#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p))
-#define __ioaddr(p) (__builtin_constant_p((p)) ? __ioaddr(p) : __ioaddrc(p))
-/* the following macro is deprecated */
-#define ioaddr(port) __ioaddr((port))
-
-#define insb(p,d,l) __raw_readsb(__ioaddr(p),d,l)
-#define insw(p,d,l) __raw_readsw(__ioaddr(p),d,l)
-
-#define outsb(p,d,l) __raw_writesb(__ioaddr(p),d,l)
-#define outsw(p,d,l) __raw_writesw(__ioaddr(p),d,l)
-
-/*
- * 1:1 mapping for ioremapped regions.
- */
-#define __mem_pci(x) (x)
-
-#endif
diff --git a/arch/arm/mach-clps7500/include/mach/irq.h b/arch/arm/mach-clps7500/include/mach/irq.h
deleted file mode 100644
index d02fcf28ee05..000000000000
--- a/arch/arm/mach-clps7500/include/mach/irq.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/irq.h
- *
- * Copyright (C) 1996 Russell King
- * Copyright (C) 1999, 2001 Nexus Electronics Ltd.
- *
- * Changelog:
- * 10-10-1996 RMK Brought up to date with arch-sa110eval
- * 22-08-1998 RMK Restructured IRQ routines
- * 11-08-1999 PJB Created ARM7500 version, derived from RiscPC code
- */
-
-#include <linux/io.h>
-#include <asm/hardware/iomd.h>
-
-static inline int fixup_irq(unsigned int irq)
-{
- if (irq == IRQ_ISA) {
- int isabits = *((volatile unsigned int *)0xe002b700);
- if (isabits == 0) {
- printk("Spurious ISA IRQ!\n");
- return irq;
- }
- irq = IRQ_ISA_BASE;
- while (!(isabits & 1)) {
- irq++;
- isabits >>= 1;
- }
- }
-
- return irq;
-}
diff --git a/arch/arm/mach-clps7500/include/mach/irqs.h b/arch/arm/mach-clps7500/include/mach/irqs.h
deleted file mode 100644
index bee66b487f59..000000000000
--- a/arch/arm/mach-clps7500/include/mach/irqs.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/irqs.h
- *
- * Copyright (C) 1999 Nexus Electronics Ltd
- */
-
-#define IRQ_INT2 0
-#define IRQ_INT1 2
-#define IRQ_VSYNCPULSE 3
-#define IRQ_POWERON 4
-#define IRQ_TIMER0 5
-#define IRQ_TIMER1 6
-#define IRQ_FORCE 7
-#define IRQ_INT8 8
-#define IRQ_ISA 9
-#define IRQ_INT6 10
-#define IRQ_INT5 11
-#define IRQ_INT4 12
-#define IRQ_INT3 13
-#define IRQ_KEYBOARDTX 14
-#define IRQ_KEYBOARDRX 15
-
-#define IRQ_DMA0 16
-#define IRQ_DMA1 17
-#define IRQ_DMA2 18
-#define IRQ_DMA3 19
-#define IRQ_DMAS0 20
-#define IRQ_DMAS1 21
-
-#define IRQ_IOP0 24
-#define IRQ_IOP1 25
-#define IRQ_IOP2 26
-#define IRQ_IOP3 27
-#define IRQ_IOP4 28
-#define IRQ_IOP5 29
-#define IRQ_IOP6 30
-#define IRQ_IOP7 31
-
-#define IRQ_MOUSERX 40
-#define IRQ_MOUSETX 41
-#define IRQ_ADC 42
-#define IRQ_EVENT1 43
-#define IRQ_EVENT2 44
-
-#define IRQ_ISA_BASE 48
-#define IRQ_ISA_3 48
-#define IRQ_ISA_4 49
-#define IRQ_ISA_5 50
-#define IRQ_ISA_7 51
-#define IRQ_ISA_9 52
-#define IRQ_ISA_10 53
-#define IRQ_ISA_11 54
-#define IRQ_ISA_14 55
-
-#define FIQ_INT9 0
-#define FIQ_INT5 1
-#define FIQ_INT6 4
-#define FIQ_INT8 6
-#define FIQ_FORCE 7
-
-/*
- * This is the offset of the FIQ "IRQ" numbers
- */
-#define FIQ_START 64
-
-#define IRQ_TIMER IRQ_TIMER0
diff --git a/arch/arm/mach-clps7500/include/mach/memory.h b/arch/arm/mach-clps7500/include/mach/memory.h
deleted file mode 100644
index 87b32db470c8..000000000000
--- a/arch/arm/mach-clps7500/include/mach/memory.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/memory.h
- *
- * Copyright (c) 1996,1997,1998 Russell King.
- *
- * Changelog:
- * 20-Oct-1996 RMK Created
- * 31-Dec-1997 RMK Fixed definitions to reduce warnings
- * 11-Jan-1998 RMK Uninlined to reduce hits on cache
- * 08-Feb-1998 RMK Added __virt_to_bus and __bus_to_virt
- * 21-Mar-1999 RMK Renamed to memory.h
- * RMK Added TASK_SIZE and PAGE_OFFSET
- */
-#ifndef __ASM_ARCH_MEMORY_H
-#define __ASM_ARCH_MEMORY_H
-
-/*
- * Physical DRAM offset.
- */
-#define PHYS_OFFSET UL(0x10000000)
-
-/*
- * These are exactly the same on the RiscPC as the
- * physical memory view.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-/*
- * Cache flushing area - ROM
- */
-#define FLUSH_BASE_PHYS 0x00000000
-#define FLUSH_BASE 0xdf000000
-
-/*
- * Sparsemem support. Each section is a maximum of 64MB. The sections
- * are offset by 128MB and can cover 128MB, so that gives us a maximum
- * of 29 physmem bits.
- */
-#define MAX_PHYSMEM_BITS 29
-#define SECTION_SIZE_BITS 26
-
-#endif
diff --git a/arch/arm/mach-clps7500/include/mach/system.h b/arch/arm/mach-clps7500/include/mach/system.h
deleted file mode 100644
index 6d325fbe8b08..000000000000
--- a/arch/arm/mach-clps7500/include/mach/system.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/system.h
- *
- * Copyright (c) 1999 Nexus Electronics Ltd.
- */
-#ifndef __ASM_ARCH_SYSTEM_H
-#define __ASM_ARCH_SYSTEM_H
-
-#include <linux/io.h>
-#include <asm/hardware/iomd.h>
-
-static inline void arch_idle(void)
-{
- iomd_writeb(0, IOMD_SUSMODE);
-}
-
-#define arch_reset(mode) \
- do { \
- iomd_writeb(0, IOMD_ROMCR0); \
- cpu_reset(0); \
- } while (0)
-
-#endif
diff --git a/arch/arm/mach-clps7500/include/mach/timex.h b/arch/arm/mach-clps7500/include/mach/timex.h
deleted file mode 100644
index dfaa9b425757..000000000000
--- a/arch/arm/mach-clps7500/include/mach/timex.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/timex.h
- *
- * CL7500 architecture timex specifications
- *
- * Copyright (C) 1999 Nexus Electronics Ltd
- */
-
-/*
- * On the ARM7500, the clock ticks at 2MHz.
- */
-#define CLOCK_TICK_RATE 2000000
-
diff --git a/arch/arm/mach-clps7500/include/mach/uncompress.h b/arch/arm/mach-clps7500/include/mach/uncompress.h
deleted file mode 100644
index d7d0af4b49fc..000000000000
--- a/arch/arm/mach-clps7500/include/mach/uncompress.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/uncompress.h
- *
- * Copyright (C) 1999, 2000 Nexus Electronics Ltd.
- */
-#define BASE 0x03010000
-#define SERBASE (BASE + (0x2f8 << 2))
-
-static inline void putc(char c)
-{
- while (!(*((volatile unsigned int *)(SERBASE + 0x14)) & 0x20))
- barrier();
-
- *((volatile unsigned int *)(SERBASE)) = c;
-}
-
-static inline void flush(void)
-{
-}
-
-static __inline__ void arch_decomp_setup(void)
-{
- int baud = 3686400 / (9600 * 32);
-
- *((volatile unsigned int *)(SERBASE + 0xC)) = 0x80;
- *((volatile unsigned int *)(SERBASE + 0x0)) = baud & 0xff;
- *((volatile unsigned int *)(SERBASE + 0x4)) = (baud & 0xff00) >> 8;
- *((volatile unsigned int *)(SERBASE + 0xC)) = 3; /* 8 bits */
- *((volatile unsigned int *)(SERBASE + 0x10)) = 3; /* DTR, RTS */
-}
-
-/*
- * nothing to do
- */
-#define arch_decomp_wdog()
diff --git a/arch/arm/mach-clps7500/include/mach/vmalloc.h b/arch/arm/mach-clps7500/include/mach/vmalloc.h
deleted file mode 100644
index 8fc5406d1b6d..000000000000
--- a/arch/arm/mach-clps7500/include/mach/vmalloc.h
+++ /dev/null
@@ -1,4 +0,0 @@
-/*
- * arch/arm/mach-clps7500/include/mach/vmalloc.h
- */
-#define VMALLOC_END (PAGE_OFFSET + 0x1c000000)
diff --git a/arch/arm/mach-davinci/include/mach/dma.h b/arch/arm/mach-davinci/include/mach/dma.h
deleted file mode 100644
index 8e2f2d0ba667..000000000000
--- a/arch/arm/mach-davinci/include/mach/dma.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * DaVinci DMA definitions
- *
- * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
- *
- * 2007 (c) MontaVista Software, Inc. This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-#define MAX_DMA_ADDRESS 0xffffffff
-
-#endif /* __ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-davinci/include/mach/io.h b/arch/arm/mach-davinci/include/mach/io.h
index b78ee9140496..a48795fd2417 100644
--- a/arch/arm/mach-davinci/include/mach/io.h
+++ b/arch/arm/mach-davinci/include/mach/io.h
@@ -29,8 +29,7 @@
* We don't actually have real ISA nor PCI buses, but there is so many
* drivers out there that might just work if we fake them...
*/
-#define PCIO_BASE 0
-#define __io(a) ((void __iomem *)(PCIO_BASE + (a)))
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#define __mem_isa(a) (a)
diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h
index dd1625c23cf4..86c25c7f3ce3 100644
--- a/arch/arm/mach-davinci/include/mach/memory.h
+++ b/arch/arm/mach-davinci/include/mach/memory.h
@@ -52,13 +52,8 @@ __arch_adjust_zones(int node, unsigned long *size, unsigned long *holes)
if ((meminfo.bank[0].size >> 20) > 128) __arch_adjust_zones(node, zone_size, holes)
#define ISA_DMA_THRESHOLD (PHYS_OFFSET + (128<<20) - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + (128<<20))
#endif
-/*
- * Bus address is physical address
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif /* __ASM_ARCH_MEMORY_H */
diff --git a/arch/arm/mach-davinci/include/mach/vmalloc.h b/arch/arm/mach-davinci/include/mach/vmalloc.h
index b98bd9e92fd6..ad51625b6609 100644
--- a/arch/arm/mach-davinci/include/mach/vmalloc.h
+++ b/arch/arm/mach-davinci/include/mach/vmalloc.h
@@ -8,7 +8,6 @@
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
-#include <asm/memory.h>
#include <mach/io.h>
/* Allow vmalloc range until the IO virtual range minus a 2M "hole" */
diff --git a/arch/arm/mach-ebsa110/include/mach/dma.h b/arch/arm/mach-ebsa110/include/mach/dma.h
deleted file mode 100644
index 780a04c8bbe9..000000000000
--- a/arch/arm/mach-ebsa110/include/mach/dma.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * arch/arm/mach-ebsa110/include/mach/dma.h
- *
- * Copyright (C) 1997,1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * EBSA110 DMA definitions
- */
diff --git a/arch/arm/mach-ebsa110/include/mach/memory.h b/arch/arm/mach-ebsa110/include/mach/memory.h
index eea4b75b657b..0ca66d080c69 100644
--- a/arch/arm/mach-ebsa110/include/mach/memory.h
+++ b/arch/arm/mach-ebsa110/include/mach/memory.h
@@ -22,13 +22,6 @@
#define PHYS_OFFSET UL(0x00000000)
/*
- * We keep this 1:1 so that we don't interfere
- * with the PCMCIA memory regions
- */
-#define __virt_to_bus(x) (x)
-#define __bus_to_virt(x) (x)
-
-/*
* Cache flushing area - SRAM
*/
#define FLUSH_BASE_PHYS 0x40000000
diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig
index 5a1b8c05c958..56bddcef6905 100644
--- a/arch/arm/mach-ep93xx/Kconfig
+++ b/arch/arm/mach-ep93xx/Kconfig
@@ -33,6 +33,12 @@ config MACH_EDB9307
Say 'Y' here if you want your kernel to support the Cirrus
Logic EDB9307 Evaluation Board.
+config MACH_EDB9307A
+ bool "Support Cirrus Logic EDB9307A"
+ help
+ Say 'Y' here if you want your kernel to support the Cirrus
+ Logic EDB9307A Evaluation Board.
+
config MACH_EDB9312
bool "Support Cirrus Logic EDB9312"
help
diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
index c1252ca9648e..944e42d51646 100644
--- a/arch/arm/mach-ep93xx/Makefile
+++ b/arch/arm/mach-ep93xx/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_MACH_ADSSPHERE) += adssphere.o
obj-$(CONFIG_MACH_EDB9302) += edb9302.o
obj-$(CONFIG_MACH_EDB9302A) += edb9302a.o
obj-$(CONFIG_MACH_EDB9307) += edb9307.o
+obj-$(CONFIG_MACH_EDB9307A) += edb9307a.o
obj-$(CONFIG_MACH_EDB9312) += edb9312.o
obj-$(CONFIG_MACH_EDB9315) += edb9315.o
obj-$(CONFIG_MACH_EDB9315A) += edb9315a.o
diff --git a/arch/arm/mach-ep93xx/adssphere.c b/arch/arm/mach-ep93xx/adssphere.c
index 561db73ec1ae..3fbd9b0fbe24 100644
--- a/arch/arm/mach-ep93xx/adssphere.c
+++ b/arch/arm/mach-ep93xx/adssphere.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data adssphere_flash_data = {
};
static struct resource adssphere_flash_resource = {
- .start = 0x60000000,
- .end = 0x61ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_32M - 1,
.flags = IORESOURCE_MEM,
};
@@ -59,7 +60,7 @@ MACHINE_START(ADSSPHERE, "ADS Sphere board")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 8c9f2491dccc..96049283a10a 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -16,11 +16,12 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/io.h>
+
+#include <asm/clkdev.h>
#include <asm/div64.h>
#include <mach/hardware.h>
struct clk {
- char *name;
unsigned long rate;
int users;
u32 enable_reg;
@@ -28,53 +29,33 @@ struct clk {
};
static struct clk clk_uart = {
- .name = "UARTCLK",
.rate = 14745600,
};
-static struct clk clk_pll1 = {
- .name = "pll1",
-};
-static struct clk clk_f = {
- .name = "fclk",
-};
-static struct clk clk_h = {
- .name = "hclk",
-};
-static struct clk clk_p = {
- .name = "pclk",
-};
-static struct clk clk_pll2 = {
- .name = "pll2",
-};
+static struct clk clk_pll1;
+static struct clk clk_f;
+static struct clk clk_h;
+static struct clk clk_p;
+static struct clk clk_pll2;
static struct clk clk_usb_host = {
- .name = "usb_host",
.enable_reg = EP93XX_SYSCON_CLOCK_CONTROL,
.enable_mask = EP93XX_SYSCON_CLOCK_USH_EN,
};
-
-static struct clk *clocks[] = {
- &clk_uart,
- &clk_pll1,
- &clk_f,
- &clk_h,
- &clk_p,
- &clk_pll2,
- &clk_usb_host,
+#define INIT_CK(dev,con,ck) \
+ { .dev_id = dev, .con_id = con, .clk = ck }
+
+static struct clk_lookup clocks[] = {
+ INIT_CK("apb:uart1", NULL, &clk_uart),
+ INIT_CK("apb:uart2", NULL, &clk_uart),
+ INIT_CK("apb:uart3", NULL, &clk_uart),
+ INIT_CK(NULL, "pll1", &clk_pll1),
+ INIT_CK(NULL, "fclk", &clk_f),
+ INIT_CK(NULL, "hclk", &clk_h),
+ INIT_CK(NULL, "pclk", &clk_p),
+ INIT_CK(NULL, "pll2", &clk_pll2),
+ INIT_CK(NULL, "usb_host", &clk_usb_host),
};
-struct clk *clk_get(struct device *dev, const char *id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(clocks); i++) {
- if (!strcmp(clocks[i]->name, id))
- return clocks[i];
- }
-
- return ERR_PTR(-ENOENT);
-}
-EXPORT_SYMBOL(clk_get);
int clk_enable(struct clk *clk)
{
@@ -106,12 +87,6 @@ unsigned long clk_get_rate(struct clk *clk)
}
EXPORT_SYMBOL(clk_get_rate);
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
-
static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
@@ -138,6 +113,7 @@ static unsigned long calc_pll_rate(u32 config_word)
static int __init ep93xx_clock_init(void)
{
u32 value;
+ int i;
value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1);
if (!(value & 0x00800000)) { /* PLL1 bypassed? */
@@ -165,6 +141,8 @@ static int __init ep93xx_clock_init(void)
clk_f.rate / 1000000, clk_h.rate / 1000000,
clk_p.rate / 1000000);
+ for (i = 0; i < ARRAY_SIZE(clocks); i++)
+ clkdev_add(&clocks[i]);
return 0;
}
arch_initcall(ep93xx_clock_init);
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 48345fb34613..4781f323703b 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -34,6 +34,8 @@
#include <linux/amba/bus.h>
#include <linux/amba/serial.h>
#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
#include <asm/types.h>
#include <asm/setup.h>
@@ -153,12 +155,14 @@ static unsigned char gpio_int_unmasked[3];
static unsigned char gpio_int_enabled[3];
static unsigned char gpio_int_type1[3];
static unsigned char gpio_int_type2[3];
+static unsigned char gpio_int_debouce[3];
/* Port ordering is: A B F */
static const u8 int_type1_register_offset[3] = { 0x90, 0xac, 0x4c };
static const u8 int_type2_register_offset[3] = { 0x94, 0xb0, 0x50 };
static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 };
+static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 };
void ep93xx_gpio_update_int_params(unsigned port)
{
@@ -181,6 +185,22 @@ void ep93xx_gpio_int_mask(unsigned line)
gpio_int_unmasked[line >> 3] &= ~(1 << (line & 7));
}
+void ep93xx_gpio_int_debounce(unsigned int irq, int enable)
+{
+ int line = irq_to_gpio(irq);
+ int port = line >> 3;
+ int port_mask = 1 << (line & 7);
+
+ if (enable)
+ gpio_int_debouce[port] |= port_mask;
+ else
+ gpio_int_debouce[port] &= ~port_mask;
+
+ __raw_writeb(gpio_int_debouce[port],
+ EP93XX_GPIO_REG(int_debounce_register_offset[port]));
+}
+EXPORT_SYMBOL(ep93xx_gpio_int_debounce);
+
/*************************************************************************
* EP93xx IRQ handling
*************************************************************************/
@@ -497,6 +517,26 @@ void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr)
platform_device_register(&ep93xx_eth_device);
}
+static struct i2c_gpio_platform_data ep93xx_i2c_data = {
+ .sda_pin = EP93XX_GPIO_LINE_EEDAT,
+ .sda_is_open_drain = 0,
+ .scl_pin = EP93XX_GPIO_LINE_EECLK,
+ .scl_is_open_drain = 0,
+ .udelay = 2,
+};
+
+static struct platform_device ep93xx_i2c_device = {
+ .name = "i2c-gpio",
+ .id = 0,
+ .dev.platform_data = &ep93xx_i2c_data,
+};
+
+void __init ep93xx_register_i2c(struct i2c_board_info *devices, int num)
+{
+ i2c_register_board_info(0, devices, num);
+ platform_device_register(&ep93xx_i2c_device);
+}
+
extern void ep93xx_gpio_init(void);
void __init ep93xx_init_devices(void)
diff --git a/arch/arm/mach-ep93xx/edb9302.c b/arch/arm/mach-ep93xx/edb9302.c
index e4add5bdccfd..8bf8d7c78f1a 100644
--- a/arch/arm/mach-ep93xx/edb9302.c
+++ b/arch/arm/mach-ep93xx/edb9302.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data edb9302_flash_data = {
};
static struct resource edb9302_flash_resource = {
- .start = 0x60000000,
- .end = 0x60ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
.flags = IORESOURCE_MEM,
};
@@ -59,7 +60,7 @@ MACHINE_START(EDB9302, "Cirrus Logic EDB9302 Evaluation Board")
/* Maintainer: George Kashperko <george@chas.com.ua> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/edb9302a.c b/arch/arm/mach-ep93xx/edb9302a.c
index 02c4405afed7..a352c57c7b46 100644
--- a/arch/arm/mach-ep93xx/edb9302a.c
+++ b/arch/arm/mach-ep93xx/edb9302a.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data edb9302a_flash_data = {
};
static struct resource edb9302a_flash_resource = {
- .start = 0x60000000,
- .end = 0x60ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
.flags = IORESOURCE_MEM,
};
@@ -44,7 +45,7 @@ static struct platform_device edb9302a_flash = {
};
static struct ep93xx_eth_data edb9302a_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init edb9302a_init_machine(void)
@@ -59,7 +60,7 @@ MACHINE_START(EDB9302A, "Cirrus Logic EDB9302A Evaluation Board")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0xc0000100,
+ .boot_params = EP93XX_SDCE0_PHYS_BASE + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/edb9307.c b/arch/arm/mach-ep93xx/edb9307.c
index 040edbd2ea05..5ab22f63a4eb 100644
--- a/arch/arm/mach-ep93xx/edb9307.c
+++ b/arch/arm/mach-ep93xx/edb9307.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data edb9307_flash_data = {
};
static struct resource edb9307_flash_resource = {
- .start = 0x60000000,
- .end = 0x61ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_32M - 1,
.flags = IORESOURCE_MEM,
};
@@ -44,7 +45,7 @@ static struct platform_device edb9307_flash = {
};
static struct ep93xx_eth_data edb9307_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init edb9307_init_machine(void)
@@ -59,7 +60,7 @@ MACHINE_START(EDB9307, "Cirrus Logic EDB9307 Evaluation Board")
/* Maintainer: Herbert Valerio Riedel <hvr@gnu.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/edb9307a.c b/arch/arm/mach-ep93xx/edb9307a.c
new file mode 100644
index 000000000000..5b5c22b681be
--- /dev/null
+++ b/arch/arm/mach-ep93xx/edb9307a.c
@@ -0,0 +1,68 @@
+/*
+ * arch/arm/mach-ep93xx/edb9307a.c
+ * Cirrus Logic EDB9307A support.
+ *
+ * Copyright (C) 2008 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+static struct physmap_flash_data edb9307a_flash_data = {
+ .width = 2,
+};
+
+static struct resource edb9307a_flash_resource = {
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device edb9307a_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &edb9307a_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &edb9307a_flash_resource,
+};
+
+static struct ep93xx_eth_data edb9307a_eth_data = {
+ .phy_id = 1,
+};
+
+static void __init edb9307a_init_machine(void)
+{
+ ep93xx_init_devices();
+ platform_device_register(&edb9307a_flash);
+
+ ep93xx_register_eth(&edb9307a_eth_data, 1);
+}
+
+MACHINE_START(EDB9307A, "Cirrus Logic EDB9307A Evaluation Board")
+ /* Maintainer: H Hartley Sweeten <hsweeten@visionengravers.com> */
+ .phys_io = EP93XX_APB_PHYS_BASE,
+ .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
+ .boot_params = EP93XX_SDCE0_PHYS_BASE + 0x100,
+ .map_io = ep93xx_map_io,
+ .init_irq = ep93xx_init_irq,
+ .timer = &ep93xx_timer,
+ .init_machine = edb9307a_init_machine,
+MACHINE_END
diff --git a/arch/arm/mach-ep93xx/edb9312.c b/arch/arm/mach-ep93xx/edb9312.c
index 6853e302bc3a..d7179f66d804 100644
--- a/arch/arm/mach-ep93xx/edb9312.c
+++ b/arch/arm/mach-ep93xx/edb9312.c
@@ -20,6 +20,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -29,8 +30,8 @@ static struct physmap_flash_data edb9312_flash_data = {
};
static struct resource edb9312_flash_resource = {
- .start = 0x60000000,
- .end = 0x61ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_32M - 1,
.flags = IORESOURCE_MEM,
};
@@ -45,7 +46,7 @@ static struct platform_device edb9312_flash = {
};
static struct ep93xx_eth_data edb9312_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init edb9312_init_machine(void)
@@ -60,7 +61,7 @@ MACHINE_START(EDB9312, "Cirrus Logic EDB9312 Evaluation Board")
/* Maintainer: Toufeeq Hussain <toufeeq_hussain@infosys.com> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/edb9315.c b/arch/arm/mach-ep93xx/edb9315.c
index 9469b350d253..025af6eaca10 100644
--- a/arch/arm/mach-ep93xx/edb9315.c
+++ b/arch/arm/mach-ep93xx/edb9315.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data edb9315_flash_data = {
};
static struct resource edb9315_flash_resource = {
- .start = 0x60000000,
- .end = 0x61ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_32M - 1,
.flags = IORESOURCE_MEM,
};
@@ -44,7 +45,7 @@ static struct platform_device edb9315_flash = {
};
static struct ep93xx_eth_data edb9315_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init edb9315_init_machine(void)
@@ -59,7 +60,7 @@ MACHINE_START(EDB9315, "Cirrus Logic EDB9315 Evaluation Board")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/edb9315a.c b/arch/arm/mach-ep93xx/edb9315a.c
index 584457ce7c80..4c9cc8a39f5c 100644
--- a/arch/arm/mach-ep93xx/edb9315a.c
+++ b/arch/arm/mach-ep93xx/edb9315a.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data edb9315a_flash_data = {
};
static struct resource edb9315a_flash_resource = {
- .start = 0x60000000,
- .end = 0x60ffffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
.flags = IORESOURCE_MEM,
};
@@ -44,7 +45,7 @@ static struct platform_device edb9315a_flash = {
};
static struct ep93xx_eth_data edb9315a_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init edb9315a_init_machine(void)
@@ -59,7 +60,7 @@ MACHINE_START(EDB9315A, "Cirrus Logic EDB9315A Evaluation Board")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0xc0000100,
+ .boot_params = EP93XX_SDCE0_PHYS_BASE + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/gesbc9312.c b/arch/arm/mach-ep93xx/gesbc9312.c
index 035b24e31b64..3bad500b71b6 100644
--- a/arch/arm/mach-ep93xx/gesbc9312.c
+++ b/arch/arm/mach-ep93xx/gesbc9312.c
@@ -19,6 +19,7 @@
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -28,8 +29,8 @@ static struct physmap_flash_data gesbc9312_flash_data = {
};
static struct resource gesbc9312_flash_resource = {
- .start = 0x60000000,
- .end = 0x607fffff,
+ .start = EP93XX_CS6_PHYS_BASE,
+ .end = EP93XX_CS6_PHYS_BASE + SZ_8M - 1,
.flags = IORESOURCE_MEM,
};
@@ -59,7 +60,7 @@ MACHINE_START(GESBC9312, "Glomation GESBC-9312-sx")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ep93xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-ep93xx/include/mach/clkdev.h b/arch/arm/mach-ep93xx/include/mach/clkdev.h
new file mode 100644
index 000000000000..04b37a89801c
--- /dev/null
+++ b/arch/arm/mach-ep93xx/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-ep93xx/include/mach/dma.h b/arch/arm/mach-ep93xx/include/mach/dma.h
deleted file mode 100644
index d0fa9656e92f..000000000000
--- a/arch/arm/mach-ep93xx/include/mach/dma.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/*
- * arch/arm/mach-ep93xx/include/mach/dma.h
- */
diff --git a/arch/arm/mach-ep93xx/include/mach/gpio.h b/arch/arm/mach-ep93xx/include/mach/gpio.h
index f7020414c5df..0a1498ae899a 100644
--- a/arch/arm/mach-ep93xx/include/mach/gpio.h
+++ b/arch/arm/mach-ep93xx/include/mach/gpio.h
@@ -99,6 +99,8 @@
/* maximum value for irq capable line identifiers */
#define EP93XX_GPIO_LINE_MAX_IRQ EP93XX_GPIO_LINE_F(7)
+extern void ep93xx_gpio_int_debounce(unsigned int irq, int enable);
+
/* new generic GPIO API - see Documentation/gpio.txt */
#include <asm-generic/gpio.h>
diff --git a/arch/arm/mach-ep93xx/include/mach/io.h b/arch/arm/mach-ep93xx/include/mach/io.h
index 1ab9a90ad339..fd5f081cc8b7 100644
--- a/arch/arm/mach-ep93xx/include/mach/io.h
+++ b/arch/arm/mach-ep93xx/include/mach/io.h
@@ -4,5 +4,5 @@
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(p) ((void __iomem *)(p))
-#define __mem_pci(p) (p)
+#define __io(p) __typesafe_io(p)
+#define __mem_pci(p) (p)
diff --git a/arch/arm/mach-ep93xx/include/mach/memory.h b/arch/arm/mach-ep93xx/include/mach/memory.h
index f1b633590752..5c80c3c8158d 100644
--- a/arch/arm/mach-ep93xx/include/mach/memory.h
+++ b/arch/arm/mach-ep93xx/include/mach/memory.h
@@ -7,8 +7,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __bus_to_virt(x) __phys_to_virt(x)
-#define __virt_to_bus(x) __virt_to_phys(x)
-
-
#endif
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index db2489d3bda7..88f7e88f152f 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -14,6 +14,7 @@ void ep93xx_map_io(void);
void ep93xx_init_irq(void);
void ep93xx_init_time(unsigned long);
void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
+void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
void ep93xx_init_devices(void);
extern struct sys_timer ep93xx_timer;
diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c
index c2197236b632..15d6815d78c4 100644
--- a/arch/arm/mach-ep93xx/micro9.c
+++ b/arch/arm/mach-ep93xx/micro9.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <linux/mtd/physmap.h>
#include <mach/hardware.h>
@@ -25,7 +26,7 @@
#include <asm/mach-types.h>
static struct ep93xx_eth_data micro9_eth_data = {
- .phy_id = 0x1f,
+ .phy_id = 0x1f,
};
static void __init micro9_init(void)
@@ -38,46 +39,46 @@ static void __init micro9_init(void)
*/
#ifdef CONFIG_MACH_MICRO9H
static struct physmap_flash_data micro9h_flash_data = {
- .width = 4,
+ .width = 4,
};
static struct resource micro9h_flash_resource = {
- .start = 0x10000000,
- .end = 0x13ffffff,
- .flags = IORESOURCE_MEM,
+ .start = EP93XX_CS1_PHYS_BASE,
+ .end = EP93XX_CS1_PHYS_BASE + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
};
static struct platform_device micro9h_flash = {
- .name = "physmap-flash",
- .id = 0,
- .dev = {
- .platform_data = &micro9h_flash_data,
- },
- .num_resources = 1,
- .resource = &micro9h_flash_resource,
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &micro9h_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &micro9h_flash_resource,
};
static void __init micro9h_init(void)
{
- platform_device_register(&micro9h_flash);
+ platform_device_register(&micro9h_flash);
}
static void __init micro9h_init_machine(void)
{
- ep93xx_init_devices();
- micro9_init();
- micro9h_init();
+ ep93xx_init_devices();
+ micro9_init();
+ micro9h_init();
}
MACHINE_START(MICRO9, "Contec Hypercontrol Micro9-H")
- /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
- .phys_io = EP93XX_APB_PHYS_BASE,
- .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
- .map_io = ep93xx_map_io,
- .init_irq = ep93xx_init_irq,
- .timer = &ep93xx_timer,
- .init_machine = micro9h_init_machine,
+ /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
+ .phys_io = EP93XX_APB_PHYS_BASE,
+ .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
+ .map_io = ep93xx_map_io,
+ .init_irq = ep93xx_init_irq,
+ .timer = &ep93xx_timer,
+ .init_machine = micro9h_init_machine,
MACHINE_END
#endif
@@ -87,19 +88,19 @@ MACHINE_END
#ifdef CONFIG_MACH_MICRO9M
static void __init micro9m_init_machine(void)
{
- ep93xx_init_devices();
- micro9_init();
+ ep93xx_init_devices();
+ micro9_init();
}
MACHINE_START(MICRO9M, "Contec Hypercontrol Micro9-M")
- /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
- .phys_io = EP93XX_APB_PHYS_BASE,
- .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
- .map_io = ep93xx_map_io,
- .init_irq = ep93xx_init_irq,
- .timer = &ep93xx_timer,
- .init_machine = micro9m_init_machine,
+ /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
+ .phys_io = EP93XX_APB_PHYS_BASE,
+ .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
+ .map_io = ep93xx_map_io,
+ .init_irq = ep93xx_init_irq,
+ .timer = &ep93xx_timer,
+ .init_machine = micro9m_init_machine,
MACHINE_END
#endif
@@ -109,19 +110,19 @@ MACHINE_END
#ifdef CONFIG_MACH_MICRO9L
static void __init micro9l_init_machine(void)
{
- ep93xx_init_devices();
- micro9_init();
+ ep93xx_init_devices();
+ micro9_init();
}
MACHINE_START(MICRO9L, "Contec Hypercontrol Micro9-L")
- /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
- .phys_io = EP93XX_APB_PHYS_BASE,
- .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
- .map_io = ep93xx_map_io,
- .init_irq = ep93xx_init_irq,
- .timer = &ep93xx_timer,
- .init_machine = micro9l_init_machine,
+ /* Maintainer: Manfred Gruber <manfred.gruber@contec.at> */
+ .phys_io = EP93XX_APB_PHYS_BASE,
+ .io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
+ .map_io = ep93xx_map_io,
+ .init_irq = ep93xx_init_irq,
+ .timer = &ep93xx_timer,
+ .init_machine = micro9l_init_machine,
MACHINE_END
#endif
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index b4aa4c054276..7ee024d34829 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/m48t86.h>
#include <linux/io.h>
+#include <linux/i2c.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -117,7 +118,7 @@ static struct physmap_flash_data ts72xx_flash_data = {
static struct resource ts72xx_flash_resource = {
.start = TS72XX_NOR_PHYS_BASE,
- .end = TS72XX_NOR_PHYS_BASE + 0x00ffffff,
+ .end = TS72XX_NOR_PHYS_BASE + SZ_16M - 1,
.flags = IORESOURCE_MEM,
};
@@ -144,21 +145,21 @@ static void ts72xx_rtc_writebyte(unsigned char value, unsigned long addr)
}
static struct m48t86_ops ts72xx_rtc_ops = {
- .readbyte = ts72xx_rtc_readbyte,
- .writebyte = ts72xx_rtc_writebyte,
+ .readbyte = ts72xx_rtc_readbyte,
+ .writebyte = ts72xx_rtc_writebyte,
};
static struct platform_device ts72xx_rtc_device = {
- .name = "rtc-m48t86",
- .id = -1,
- .dev = {
- .platform_data = &ts72xx_rtc_ops,
+ .name = "rtc-m48t86",
+ .id = -1,
+ .dev = {
+ .platform_data = &ts72xx_rtc_ops,
},
- .num_resources = 0,
+ .num_resources = 0,
};
static struct ep93xx_eth_data ts72xx_eth_data = {
- .phy_id = 1,
+ .phy_id = 1,
};
static void __init ts72xx_init_machine(void)
@@ -175,7 +176,7 @@ MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC")
/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
.phys_io = EP93XX_APB_PHYS_BASE,
.io_pg_offst = ((EP93XX_APB_VIRT_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = EP93XX_SDCE3_PHYS_BASE_SYNC + 0x100,
.map_io = ts72xx_map_io,
.init_irq = ep93xx_init_irq,
.timer = &ep93xx_timer,
diff --git a/arch/arm/mach-footbridge/cats-hw.c b/arch/arm/mach-footbridge/cats-hw.c
index 6a5b437ab86f..1b996b26d2e0 100644
--- a/arch/arm/mach-footbridge/cats-hw.c
+++ b/arch/arm/mach-footbridge/cats-hw.c
@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/screen_info.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
diff --git a/arch/arm/mach-footbridge/common.c b/arch/arm/mach-footbridge/common.c
index 818014e09f4a..36ff06d4df15 100644
--- a/arch/arm/mach-footbridge/common.c
+++ b/arch/arm/mach-footbridge/common.c
@@ -14,6 +14,7 @@
#include <linux/list.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/pgtable.h>
#include <asm/page.h>
diff --git a/arch/arm/mach-footbridge/dc21285-timer.c b/arch/arm/mach-footbridge/dc21285-timer.c
index b2a21189dd81..da35bc5c5ccc 100644
--- a/arch/arm/mach-footbridge/dc21285-timer.c
+++ b/arch/arm/mach-footbridge/dc21285-timer.c
@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/spinlock.h>
#include <asm/irq.h>
diff --git a/arch/arm/mach-footbridge/dc21285.c b/arch/arm/mach-footbridge/dc21285.c
index d4c1e526f59c..133086019e3e 100644
--- a/arch/arm/mach-footbridge/dc21285.c
+++ b/arch/arm/mach-footbridge/dc21285.c
@@ -17,6 +17,7 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/irq.h>
#include <asm/system.h>
diff --git a/arch/arm/mach-footbridge/dma.c b/arch/arm/mach-footbridge/dma.c
index b653e9cfa3f7..4f3506346969 100644
--- a/arch/arm/mach-footbridge/dma.c
+++ b/arch/arm/mach-footbridge/dma.c
@@ -12,6 +12,7 @@
*/
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/dma.h>
#include <asm/scatterlist.h>
diff --git a/arch/arm/mach-footbridge/ebsa285.c b/arch/arm/mach-footbridge/ebsa285.c
index b1d3bf20a41e..30040fd588cc 100644
--- a/arch/arm/mach-footbridge/ebsa285.c
+++ b/arch/arm/mach-footbridge/ebsa285.c
@@ -4,6 +4,7 @@
* EBSA285 machine fixup
*/
#include <linux/init.h>
+#include <linux/spinlock.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
diff --git a/arch/arm/mach-footbridge/include/mach/dma.h b/arch/arm/mach-footbridge/include/mach/dma.h
deleted file mode 100644
index 62afd213effb..000000000000
--- a/arch/arm/mach-footbridge/include/mach/dma.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * arch/arm/mach-footbridge/include/mach/dma.h
- *
- * Architecture DMA routines
- *
- * Copyright (C) 1998,1999 Russell King
- * Copyright (C) 1998,1999 Philip Blundell
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/*
- * The 21285 has two internal DMA channels; we call these 8 and 9.
- * On CATS hardware we have an additional eight ISA dma channels
- * numbered 0..7.
- */
-#define _ISA_DMA(x) (0+(x))
-#define _DC21285_DMA(x) (8+(x))
-
-#define MAX_DMA_CHANNELS 10
-
-#define DMA_FLOPPY _ISA_DMA(2)
-#define DMA_ISA_CASCADE _ISA_DMA(4)
-
-#endif /* _ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-footbridge/include/mach/hardware.h b/arch/arm/mach-footbridge/include/mach/hardware.h
index ffaea90486f9..51dd902043ad 100644
--- a/arch/arm/mach-footbridge/include/mach/hardware.h
+++ b/arch/arm/mach-footbridge/include/mach/hardware.h
@@ -12,8 +12,6 @@
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H
-#include <mach/memory.h>
-
/* Virtual Physical Size
* 0xff800000 0x40000000 1MB X-Bus
* 0xff000000 0x7c000000 1MB PCI I/O space
@@ -28,9 +26,6 @@
#define XBUS_SIZE 0x00100000
#define XBUS_BASE 0xff800000
-#define PCIO_SIZE 0x00100000
-#define PCIO_BASE 0xff000000
-
#define ARMCSR_SIZE 0x00100000
#define ARMCSR_BASE 0xfe000000
@@ -91,10 +86,11 @@
#define CPLD_FLASH_WR_ENABLE 1
#ifndef __ASSEMBLY__
-extern void gpio_modify_op(int mask, int set);
-extern void gpio_modify_io(int mask, int in);
-extern int gpio_read(void);
-extern void cpld_modify(int mask, int set);
+extern spinlock_t nw_gpio_lock;
+extern void nw_gpio_modify_op(unsigned int mask, unsigned int set);
+extern void nw_gpio_modify_io(unsigned int mask, unsigned int in);
+extern unsigned int nw_gpio_read(void);
+extern void nw_cpld_modify(unsigned int mask, unsigned int set);
#endif
#define pcibios_assign_all_busses() 1
diff --git a/arch/arm/mach-footbridge/include/mach/io.h b/arch/arm/mach-footbridge/include/mach/io.h
index a7b066239996..101a4fe90bde 100644
--- a/arch/arm/mach-footbridge/include/mach/io.h
+++ b/arch/arm/mach-footbridge/include/mach/io.h
@@ -14,7 +14,8 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
+#define PCIO_SIZE 0x00100000
+#define PCIO_BASE 0xff000000
#define IO_SPACE_LIMIT 0xffff
diff --git a/arch/arm/mach-footbridge/include/mach/isa-dma.h b/arch/arm/mach-footbridge/include/mach/isa-dma.h
new file mode 100644
index 000000000000..5bd4a0d338a8
--- /dev/null
+++ b/arch/arm/mach-footbridge/include/mach/isa-dma.h
@@ -0,0 +1,25 @@
+/*
+ * arch/arm/mach-footbridge/include/mach/isa-dma.h
+ *
+ * Architecture DMA routines
+ *
+ * Copyright (C) 1998,1999 Russell King
+ * Copyright (C) 1998,1999 Philip Blundell
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+/*
+ * The 21285 has two internal DMA channels; we call these 8 and 9.
+ * On CATS hardware we have an additional eight ISA dma channels
+ * numbered 0..7.
+ */
+#define _ISA_DMA(x) (0+(x))
+#define _DC21285_DMA(x) (8+(x))
+
+#define MAX_DMA_CHANNELS 10
+
+#define DMA_FLOPPY _ISA_DMA(2)
+#define DMA_ISA_CASCADE _ISA_DMA(4)
+
+#endif /* _ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-footbridge/include/mach/memory.h b/arch/arm/mach-footbridge/include/mach/memory.h
index 6ae2f1a07ab9..cb16e59d87b6 100644
--- a/arch/arm/mach-footbridge/include/mach/memory.h
+++ b/arch/arm/mach-footbridge/include/mach/memory.h
@@ -30,9 +30,18 @@
extern unsigned long __virt_to_bus(unsigned long);
extern unsigned long __bus_to_virt(unsigned long);
#endif
+#define __virt_to_bus __virt_to_bus
+#define __bus_to_virt __bus_to_virt
#elif defined(CONFIG_FOOTBRIDGE_HOST)
+/*
+ * The footbridge is programmed to expose the system RAM at the corresponding
+ * address. So, if PAGE_OFFSET is 0xc0000000, RAM appears at 0xe0000000.
+ * If 0x80000000, then its exposed at 0xa0000000 on the bus. etc.
+ * The only requirement is that the RAM isn't placed at bus address 0 which
+ * would clash with VGA cards.
+ */
#define __virt_to_bus(x) ((x) - 0xe0000000)
#define __bus_to_virt(x) ((x) + 0xe0000000)
diff --git a/arch/arm/mach-footbridge/isa-irq.c b/arch/arm/mach-footbridge/isa-irq.c
index 54fec9ae28b9..9ee80a211d3c 100644
--- a/arch/arm/mach-footbridge/isa-irq.c
+++ b/arch/arm/mach-footbridge/isa-irq.c
@@ -19,6 +19,7 @@
#include <linux/list.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/mach/irq.h>
diff --git a/arch/arm/mach-footbridge/netwinder-hw.c b/arch/arm/mach-footbridge/netwinder-hw.c
index 00b0ddcac283..ac7ffa6fc413 100644
--- a/arch/arm/mach-footbridge/netwinder-hw.c
+++ b/arch/arm/mach-footbridge/netwinder-hw.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/hardware/dec21285.h>
#include <asm/leds.h>
@@ -67,13 +68,14 @@ static inline void wb977_ww(int reg, int val)
/*
* This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE
*/
-DEFINE_SPINLOCK(gpio_lock);
+DEFINE_SPINLOCK(nw_gpio_lock);
+EXPORT_SYMBOL(nw_gpio_lock);
static unsigned int current_gpio_op;
static unsigned int current_gpio_io;
static unsigned int current_cpld;
-void gpio_modify_op(int mask, int set)
+void nw_gpio_modify_op(unsigned int mask, unsigned int set)
{
unsigned int new_gpio, changed;
@@ -86,6 +88,7 @@ void gpio_modify_op(int mask, int set)
if (changed & 0xff00)
outb(new_gpio >> 8, GP2_IO_BASE);
}
+EXPORT_SYMBOL(nw_gpio_modify_op);
static inline void __gpio_modify_io(int mask, int in)
{
@@ -118,7 +121,7 @@ static inline void __gpio_modify_io(int mask, int in)
}
}
-void gpio_modify_io(int mask, int in)
+void nw_gpio_modify_io(unsigned int mask, unsigned int in)
{
/* Open up the SuperIO chip */
wb977_open();
@@ -128,11 +131,13 @@ void gpio_modify_io(int mask, int in)
/* Close up the EFER gate */
wb977_close();
}
+EXPORT_SYMBOL(nw_gpio_modify_io);
-int gpio_read(void)
+unsigned int nw_gpio_read(void)
{
return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;
}
+EXPORT_SYMBOL(nw_gpio_read);
/*
* Initialise the Winbond W83977F global registers
@@ -322,9 +327,9 @@ static inline void wb977_init_gpio(void)
/*
* Set Group1/Group2 outputs
*/
- spin_lock_irqsave(&gpio_lock, flags);
- gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
/*
@@ -359,34 +364,35 @@ static void __init wb977_init(void)
wb977_close();
}
-void cpld_modify(int mask, int set)
+void nw_cpld_modify(unsigned int mask, unsigned int set)
{
int msk;
current_cpld = (current_cpld & ~mask) | set;
- gpio_modify_io(GPIO_DATA | GPIO_IOCLK | GPIO_IOLOAD, 0);
- gpio_modify_op(GPIO_IOLOAD, 0);
+ nw_gpio_modify_io(GPIO_DATA | GPIO_IOCLK | GPIO_IOLOAD, 0);
+ nw_gpio_modify_op(GPIO_IOLOAD, 0);
for (msk = 8; msk; msk >>= 1) {
int bit = current_cpld & msk;
- gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
- gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
+ nw_gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
+ nw_gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
}
- gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
- gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
- gpio_modify_op(GPIO_IOLOAD, 0);
+ nw_gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
+ nw_gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
+ nw_gpio_modify_op(GPIO_IOLOAD, 0);
}
+EXPORT_SYMBOL(nw_cpld_modify);
static void __init cpld_init(void)
{
unsigned long flags;
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(-1, CPLD_UNMUTE | CPLD_7111_DISABLE);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(-1, CPLD_UNMUTE | CPLD_7111_DISABLE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
static unsigned char rwa_unlock[] __initdata =
@@ -596,12 +602,6 @@ static void __init rwa010_init(void)
rwa010_soundblaster_reset();
}
-EXPORT_SYMBOL(gpio_lock);
-EXPORT_SYMBOL(gpio_modify_op);
-EXPORT_SYMBOL(gpio_modify_io);
-EXPORT_SYMBOL(cpld_modify);
-EXPORT_SYMBOL(gpio_read);
-
/*
* Initialise any other hardware after we've got the PCI bus
* initialised. We may need the PCI bus to talk to this other
@@ -616,9 +616,9 @@ static int __init nw_hw_init(void)
cpld_init();
rwa010_init();
- spin_lock_irqsave(&gpio_lock, flags);
- gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
return 0;
}
diff --git a/arch/arm/mach-footbridge/netwinder-leds.c b/arch/arm/mach-footbridge/netwinder-leds.c
index d91a4f4a32dc..00269fe0be8a 100644
--- a/arch/arm/mach-footbridge/netwinder-leds.c
+++ b/arch/arm/mach-footbridge/netwinder-leds.c
@@ -32,7 +32,6 @@ static char led_state;
static char hw_led_state;
static DEFINE_SPINLOCK(leds_lock);
-extern spinlock_t gpio_lock;
static void netwinder_leds_event(led_event_t evt)
{
@@ -121,9 +120,9 @@ static void netwinder_leds_event(led_event_t evt)
spin_unlock_irqrestore(&leds_lock, flags);
if (led_state & LED_STATE_ENABLED) {
- spin_lock_irqsave(&gpio_lock, flags);
- gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
}
diff --git a/arch/arm/mach-footbridge/personal.c b/arch/arm/mach-footbridge/personal.c
index c4f843fc099d..e2c9f0690b16 100644
--- a/arch/arm/mach-footbridge/personal.c
+++ b/arch/arm/mach-footbridge/personal.c
@@ -4,6 +4,7 @@
* Personal server (Skiff) machine fixup
*/
#include <linux/init.h>
+#include <linux/spinlock.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
diff --git a/arch/arm/mach-h720x/include/mach/dma.h b/arch/arm/mach-h720x/include/mach/dma.h
deleted file mode 100644
index 0a9d86ee84fe..000000000000
--- a/arch/arm/mach-h720x/include/mach/dma.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * arch/arm/mach-h720x/include/mach/dma.h
- *
- * Architecture DMA routes
- *
- * Copyright (C) 1997.1998 Russell King
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/*
- * This is the maximum DMA address that can be DMAd to.
- * There should not be more than (0xd0000000 - 0xc0000000)
- * bytes of RAM.
- */
-#define MAX_DMA_ADDRESS 0xd0000000
-
-#if defined (CONFIG_CPU_H7201)
-#define MAX_DMA_CHANNELS 3
-#elif defined (CONFIG_CPU_H7202)
-#define MAX_DMA_CHANNELS 4
-#else
-#error processor definition missmatch
-#endif
-
-#endif /* __ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-h720x/include/mach/io.h b/arch/arm/mach-h720x/include/mach/io.h
index 1dab74ce88c6..2c8659c21a93 100644
--- a/arch/arm/mach-h720x/include/mach/io.h
+++ b/arch/arm/mach-h720x/include/mach/io.h
@@ -14,11 +14,9 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-h720x/include/mach/isa-dma.h b/arch/arm/mach-h720x/include/mach/isa-dma.h
new file mode 100644
index 000000000000..3eafb3f163c0
--- /dev/null
+++ b/arch/arm/mach-h720x/include/mach/isa-dma.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/mach-h720x/include/mach/isa-dma.h
+ *
+ * Architecture DMA routes
+ *
+ * Copyright (C) 1997.1998 Russell King
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+#if defined (CONFIG_CPU_H7201)
+#define MAX_DMA_CHANNELS 3
+#elif defined (CONFIG_CPU_H7202)
+#define MAX_DMA_CHANNELS 4
+#else
+#error processor definition missmatch
+#endif
+
+#endif /* __ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-h720x/include/mach/memory.h b/arch/arm/mach-h720x/include/mach/memory.h
index cb26f49cc4e1..ef4c1e26f18e 100644
--- a/arch/arm/mach-h720x/include/mach/memory.h
+++ b/arch/arm/mach-h720x/include/mach/memory.h
@@ -7,23 +7,13 @@
#ifndef __ASM_ARCH_MEMORY_H
#define __ASM_ARCH_MEMORY_H
-/*
- * Page offset:
- * ( 0xc0000000UL )
- */
#define PHYS_OFFSET UL(0x40000000)
-
/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- *
- * There is something to do here later !, Mar 2000, Jungjun Kim
+ * This is the maximum DMA address that can be DMAd to.
+ * There should not be more than (0xd0000000 - 0xc0000000)
+ * bytes of RAM.
*/
-
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
+#define ISA_DMA_THRESHOLD (PHYS_OFFSET + SZ_256M - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_256M)
#endif
diff --git a/arch/arm/mach-imx/dma.c b/arch/arm/mach-imx/dma.c
index c10810c936b3..1536583eece0 100644
--- a/arch/arm/mach-imx/dma.c
+++ b/arch/arm/mach-imx/dma.c
@@ -28,10 +28,11 @@
#include <linux/interrupt.h>
#include <linux/errno.h>
+#include <asm/scatterlist.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/imx-dma.h>
struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS];
@@ -138,7 +139,7 @@ imx_dma_setup_sg_base(imx_dmach_t dma_ch,
int
imx_dma_setup_single(imx_dmach_t dma_ch, dma_addr_t dma_address,
unsigned int dma_length, unsigned int dev_addr,
- dmamode_t dmamode)
+ unsigned int dmamode)
{
struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];
@@ -223,7 +224,7 @@ imx_dma_setup_single(imx_dmach_t dma_ch, dma_addr_t dma_address,
int
imx_dma_setup_sg(imx_dmach_t dma_ch,
struct scatterlist *sg, unsigned int sgcount, unsigned int dma_length,
- unsigned int dev_addr, dmamode_t dmamode)
+ unsigned int dev_addr, unsigned int dmamode)
{
int res;
struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];
diff --git a/arch/arm/mach-imx/include/mach/imx-dma.h b/arch/arm/mach-imx/include/mach/imx-dma.h
index 44d89c35539a..bbe54df7f0de 100644
--- a/arch/arm/mach-imx/include/mach/imx-dma.h
+++ b/arch/arm/mach-imx/include/mach/imx-dma.h
@@ -18,7 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/dma.h>
+#include <mach/dma.h>
#ifndef __ASM_ARCH_IMX_DMA_H
#define __ASM_ARCH_IMX_DMA_H
@@ -48,7 +48,7 @@ struct imx_dma_channel {
void (*irq_handler) (int, void *);
void (*err_handler) (int, void *, int errcode);
void *data;
- dmamode_t dma_mode;
+ unsigned int dma_mode;
struct scatterlist *sg;
unsigned int sgbc;
unsigned int sgcount;
@@ -66,14 +66,18 @@ extern struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS];
/* The type to distinguish channel numbers parameter from ordinal int type */
typedef int imx_dmach_t;
+#define DMA_MODE_READ 0
+#define DMA_MODE_WRITE 1
+#define DMA_MODE_MASK 1
+
int
imx_dma_setup_single(imx_dmach_t dma_ch, dma_addr_t dma_address,
- unsigned int dma_length, unsigned int dev_addr, dmamode_t dmamode);
+ unsigned int dma_length, unsigned int dev_addr, unsigned int dmamode);
int
imx_dma_setup_sg(imx_dmach_t dma_ch,
struct scatterlist *sg, unsigned int sgcount, unsigned int dma_length,
- unsigned int dev_addr, dmamode_t dmamode);
+ unsigned int dev_addr, unsigned int dmamode);
int
imx_dma_setup_handlers(imx_dmach_t dma_ch,
diff --git a/arch/arm/mach-imx/include/mach/imxfb.h b/arch/arm/mach-imx/include/mach/imxfb.h
index 3ed9ec8b9f00..870d0d939616 100644
--- a/arch/arm/mach-imx/include/mach/imxfb.h
+++ b/arch/arm/mach-imx/include/mach/imxfb.h
@@ -1,7 +1,52 @@
/*
* This structure describes the machine which we are running on.
*/
-struct imxfb_mach_info {
+
+#define PCR_TFT (1 << 31)
+#define PCR_COLOR (1 << 30)
+#define PCR_PBSIZ_1 (0 << 28)
+#define PCR_PBSIZ_2 (1 << 28)
+#define PCR_PBSIZ_4 (2 << 28)
+#define PCR_PBSIZ_8 (3 << 28)
+#define PCR_BPIX_1 (0 << 25)
+#define PCR_BPIX_2 (1 << 25)
+#define PCR_BPIX_4 (2 << 25)
+#define PCR_BPIX_8 (3 << 25)
+#define PCR_BPIX_12 (4 << 25)
+#define PCR_BPIX_16 (4 << 25)
+#define PCR_PIXPOL (1 << 24)
+#define PCR_FLMPOL (1 << 23)
+#define PCR_LPPOL (1 << 22)
+#define PCR_CLKPOL (1 << 21)
+#define PCR_OEPOL (1 << 20)
+#define PCR_SCLKIDLE (1 << 19)
+#define PCR_END_SEL (1 << 18)
+#define PCR_END_BYTE_SWAP (1 << 17)
+#define PCR_REV_VS (1 << 16)
+#define PCR_ACD_SEL (1 << 15)
+#define PCR_ACD(x) (((x) & 0x7f) << 8)
+#define PCR_SCLK_SEL (1 << 7)
+#define PCR_SHARP (1 << 6)
+#define PCR_PCD(x) ((x) & 0x3f)
+
+#define PWMR_CLS(x) (((x) & 0x1ff) << 16)
+#define PWMR_LDMSK (1 << 15)
+#define PWMR_SCR1 (1 << 10)
+#define PWMR_SCR0 (1 << 9)
+#define PWMR_CC_EN (1 << 8)
+#define PWMR_PW(x) ((x) & 0xff)
+
+#define LSCR1_PS_RISE_DELAY(x) (((x) & 0x7f) << 26)
+#define LSCR1_CLS_RISE_DELAY(x) (((x) & 0x3f) << 16)
+#define LSCR1_REV_TOGGLE_DELAY(x) (((x) & 0xf) << 8)
+#define LSCR1_GRAY2(x) (((x) & 0xf) << 4)
+#define LSCR1_GRAY1(x) (((x) & 0xf))
+
+#define DMACR_BURST (1 << 31)
+#define DMACR_HM(x) (((x) & 0xf) << 16)
+#define DMACR_TM(x) ((x) & 0xf)
+
+struct imx_fb_platform_data {
u_long pixclock;
u_short xres;
@@ -34,4 +79,5 @@ struct imxfb_mach_info {
void (*lcd_power)(int);
void (*backlight_power)(int);
};
-void set_imx_fb_info(struct imxfb_mach_info *hard_imx_fb_info);
+
+void set_imx_fb_info(struct imx_fb_platform_data *);
diff --git a/arch/arm/mach-imx/include/mach/io.h b/arch/arm/mach-imx/include/mach/io.h
index c50c5fa6fb81..9e197ae4590f 100644
--- a/arch/arm/mach-imx/include/mach/io.h
+++ b/arch/arm/mach-imx/include/mach/io.h
@@ -20,11 +20,9 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-imx/include/mach/memory.h b/arch/arm/mach-imx/include/mach/memory.h
index 5c453063c0ed..a93df7cba694 100644
--- a/arch/arm/mach-imx/include/mach/memory.h
+++ b/arch/arm/mach-imx/include/mach/memory.h
@@ -23,14 +23,4 @@
#define PHYS_OFFSET UL(0x08000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) (x - PAGE_OFFSET + PHYS_OFFSET)
-#define __bus_to_virt(x) (x - PHYS_OFFSET + PAGE_OFFSET)
-
#endif
diff --git a/arch/arm/mach-integrator/clock.c b/arch/arm/mach-integrator/clock.c
index 8d761fdd2ecd..989ecf5f5c46 100644
--- a/arch/arm/mach-integrator/clock.c
+++ b/arch/arm/mach-integrator/clock.c
@@ -10,42 +10,12 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/string.h>
#include <linux/clk.h>
#include <linux/mutex.h>
-#include <asm/hardware/icst525.h>
-
-#include "clock.h"
-
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- mutex_unlock(&clocks_mutex);
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
- module_put(clk->owner);
-}
-EXPORT_SYMBOL(clk_put);
+#include <asm/clkdev.h>
+#include <mach/clkdev.h>
int clk_enable(struct clk *clk)
{
@@ -67,7 +37,6 @@ EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
struct icst525_vco vco;
-
vco = icst525_khz_to_vco(clk->params, rate / 1000);
return icst525_khz(clk->params, vco) * 1000;
}
@@ -76,56 +45,15 @@ EXPORT_SYMBOL(clk_round_rate);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EIO;
+
if (clk->setvco) {
struct icst525_vco vco;
vco = icst525_khz_to_vco(clk->params, rate / 1000);
clk->rate = icst525_khz(clk->params, vco) * 1000;
-
- printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
- clk->name, vco.s, vco.r, vco.v);
-
clk->setvco(clk, vco);
ret = 0;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL(clk_set_rate);
-
-/*
- * These are fixed clocks.
- */
-static struct clk kmi_clk = {
- .name = "KMIREFCLK",
- .rate = 24000000,
-};
-
-static struct clk uart_clk = {
- .name = "UARTCLK",
- .rate = 14745600,
-};
-
-int clk_register(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init(void)
-{
- clk_register(&kmi_clk);
- clk_register(&uart_clk);
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-integrator/clock.h b/arch/arm/mach-integrator/clock.h
index 09e6328ceba9..e69de29bb2d1 100644
--- a/arch/arm/mach-integrator/clock.h
+++ b/arch/arm/mach-integrator/clock.h
@@ -1,25 +0,0 @@
-/*
- * linux/arch/arm/mach-integrator/clock.h
- *
- * Copyright (C) 2004 ARM Limited.
- * Written by Deep Blue Solutions 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.
- */
-struct module;
-struct icst525_params;
-
-struct clk {
- struct list_head node;
- unsigned long rate;
- struct module *owner;
- const char *name;
- const struct icst525_params *params;
- void *data;
- void (*setvco)(struct clk *, struct icst525_vco vco);
-};
-
-int clk_register(struct clk *clk);
-void clk_unregister(struct clk *clk);
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
index 595b7392ee4e..c89c949b4d45 100644
--- a/arch/arm/mach-integrator/core.c
+++ b/arch/arm/mach-integrator/core.c
@@ -21,6 +21,8 @@
#include <linux/amba/serial.h>
#include <linux/io.h>
+#include <asm/clkdev.h>
+#include <mach/clkdev.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/hardware/arm_timer.h>
@@ -108,10 +110,43 @@ static struct amba_device *amba_devs[] __initdata = {
&kmi1_device,
};
+/*
+ * These are fixed clocks.
+ */
+static struct clk clk24mhz = {
+ .rate = 24000000,
+};
+
+static struct clk uartclk = {
+ .rate = 14745600,
+};
+
+static struct clk_lookup lookups[] __initdata = {
+ { /* UART0 */
+ .dev_id = "mb:16",
+ .clk = &uartclk,
+ }, { /* UART1 */
+ .dev_id = "mb:17",
+ .clk = &uartclk,
+ }, { /* KMI0 */
+ .dev_id = "mb:18",
+ .clk = &clk24mhz,
+ }, { /* KMI1 */
+ .dev_id = "mb:19",
+ .clk = &clk24mhz,
+ }, { /* MMCI - IntegratorCP */
+ .dev_id = "mb:1c",
+ .clk = &uartclk,
+ }
+};
+
static int __init integrator_init(void)
{
int i;
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
+
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c
index 172299a78302..0058c937719e 100644
--- a/arch/arm/mach-integrator/impd1.c
+++ b/arch/arm/mach-integrator/impd1.c
@@ -22,13 +22,13 @@
#include <linux/amba/clcd.h>
#include <linux/io.h>
+#include <asm/clkdev.h>
+#include <mach/clkdev.h>
#include <asm/hardware/icst525.h>
#include <mach/lm.h>
#include <mach/impd1.h>
#include <asm/sizes.h>
-#include "clock.h"
-
static int module_id;
module_param_named(lmid, module_id, int, 0444);
@@ -37,6 +37,7 @@ MODULE_PARM_DESC(lmid, "logic module stack position");
struct impd1_module {
void __iomem *base;
struct clk vcos[2];
+ struct clk_lookup *clks[3];
};
static const struct icst525_params impd1_vco_params = {
@@ -339,9 +340,8 @@ static struct impd1_device impd1_devs[] = {
}
};
-static const char *impd1_vconames[2] = {
- "CLCDCLK",
- "AUXVCO2",
+static struct clk fixed_14745600 = {
+ .rate = 14745600,
};
static int impd1_probe(struct lm_device *dev)
@@ -374,14 +374,20 @@ static int impd1_probe(struct lm_device *dev)
for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {
impd1->vcos[i].owner = THIS_MODULE,
- impd1->vcos[i].name = impd1_vconames[i],
impd1->vcos[i].params = &impd1_vco_params,
impd1->vcos[i].data = impd1,
impd1->vcos[i].setvco = impd1_setvco;
-
- clk_register(&impd1->vcos[i]);
}
+ impd1->clks[0] = clkdev_alloc(&impd1->vcos[0], NULL, "lm%x:01000",
+ dev->id);
+ impd1->clks[1] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00100",
+ dev->id);
+ impd1->clks[2] = clkdev_alloc(&fixed_14745600, NULL, "lm%x:00200",
+ dev->id);
+ for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
+ clkdev_add(impd1->clks[i]);
+
for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
struct impd1_device *idev = impd1_devs + i;
struct amba_device *d;
@@ -434,8 +440,8 @@ static void impd1_remove(struct lm_device *dev)
device_for_each_child(&dev->dev, NULL, impd1_remove_one);
- for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++)
- clk_unregister(&impd1->vcos[i]);
+ for (i = 0; i < ARRAY_SIZE(impd1->clks); i++)
+ clkdev_drop(impd1->clks[i]);
lm_set_drvdata(dev, NULL);
diff --git a/arch/arm/mach-integrator/include/mach/clkdev.h b/arch/arm/mach-integrator/include/mach/clkdev.h
new file mode 100644
index 000000000000..9293e410832a
--- /dev/null
+++ b/arch/arm/mach-integrator/include/mach/clkdev.h
@@ -0,0 +1,25 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#include <linux/module.h>
+#include <asm/hardware/icst525.h>
+
+struct clk {
+ unsigned long rate;
+ struct module *owner;
+ const struct icst525_params *params;
+ void *data;
+ void (*setvco)(struct clk *, struct icst525_vco vco);
+};
+
+static inline int __clk_get(struct clk *clk)
+{
+ return try_module_get(clk->owner);
+}
+
+static inline void __clk_put(struct clk *clk)
+{
+ module_put(clk->owner);
+}
+
+#endif
diff --git a/arch/arm/mach-integrator/include/mach/dma.h b/arch/arm/mach-integrator/include/mach/dma.h
deleted file mode 100644
index fbebe85a2db7..000000000000
--- a/arch/arm/mach-integrator/include/mach/dma.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * arch/arm/mach-integrator/include/mach/dma.h
- *
- * Copyright (C) 1997,1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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
- */
diff --git a/arch/arm/mach-integrator/include/mach/memory.h b/arch/arm/mach-integrator/include/mach/memory.h
index be7e63c21d25..2b2e7a110724 100644
--- a/arch/arm/mach-integrator/include/mach/memory.h
+++ b/arch/arm/mach-integrator/include/mach/memory.h
@@ -24,16 +24,9 @@
* Physical DRAM offset.
*/
#define PHYS_OFFSET UL(0x00000000)
-#define BUS_OFFSET UL(0x80000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) (x - PAGE_OFFSET + BUS_OFFSET)
-#define __bus_to_virt(x) (x - BUS_OFFSET + PAGE_OFFSET)
+#define BUS_OFFSET UL(0x80000000)
+#define __virt_to_bus(x) ((x) - PAGE_OFFSET + BUS_OFFSET)
+#define __bus_to_virt(x) ((x) - BUS_OFFSET + PAGE_OFFSET)
#endif
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index 88026ccd5ac9..427c2d8dc123 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -21,6 +21,8 @@
#include <linux/amba/clcd.h>
#include <linux/io.h>
+#include <asm/clkdev.h>
+#include <mach/clkdev.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/setup.h>
@@ -38,7 +40,6 @@
#include <asm/mach/time.h>
#include "common.h"
-#include "clock.h"
#define INTCP_PA_MMC_BASE 0x1c000000
#define INTCP_PA_AACI_BASE 0x1d000000
@@ -289,15 +290,16 @@ static void cp_auxvco_set(struct clk *clk, struct icst525_vco vco)
writel(0, CM_LOCK);
}
-static struct clk cp_clcd_clk = {
- .name = "CLCDCLK",
+static struct clk cp_auxclk = {
.params = &cp_auxvco_params,
.setvco = cp_auxvco_set,
};
-static struct clk cp_mmci_clk = {
- .name = "MCLK",
- .rate = 14745600,
+static struct clk_lookup cp_lookups[] = {
+ { /* CLCD */
+ .dev_id = "mb:c0",
+ .clk = &cp_auxclk,
+ },
};
/*
@@ -554,8 +556,8 @@ static void __init intcp_init(void)
{
int i;
- clk_register(&cp_clcd_clk);
- clk_register(&cp_mmci_clk);
+ for (i = 0; i < ARRAY_SIZE(cp_lookups); i++)
+ clkdev_add(&cp_lookups[i]);
platform_add_devices(intcp_devs, ARRAY_SIZE(intcp_devs));
diff --git a/arch/arm/mach-iop13xx/include/mach/dma.h b/arch/arm/mach-iop13xx/include/mach/dma.h
deleted file mode 100644
index d79846fbb394..000000000000
--- a/arch/arm/mach-iop13xx/include/mach/dma.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifndef _IOP13XX_DMA_H
-#define _IOP13XX_DMA_H
-#endif
diff --git a/arch/arm/mach-iop13xx/include/mach/memory.h b/arch/arm/mach-iop13xx/include/mach/memory.h
index b82602d529bf..e012bf13c955 100644
--- a/arch/arm/mach-iop13xx/include/mach/memory.h
+++ b/arch/arm/mach-iop13xx/include/mach/memory.h
@@ -16,18 +16,6 @@
#define IOP13XX_PMMR_P_START (IOP13XX_PMMR_PHYS_MEM_BASE)
#define IOP13XX_PMMR_P_END (IOP13XX_PMMR_PHYS_MEM_BASE + IOP13XX_PMMR_SIZE)
-/*
- * Virtual view <-> PCI DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-
-/* RAM has 1:1 mapping on the PCIe/x Busses */
-#define __virt_to_bus(x) (__virt_to_phys(x))
-#define __bus_to_virt(x) (__phys_to_virt(x))
-
static inline dma_addr_t __virt_to_lbus(unsigned long x)
{
return x + IOP13XX_PMMR_PHYS_MEM_BASE - IOP13XX_PMMR_VIRT_MEM_BASE;
@@ -55,7 +43,7 @@ static inline unsigned long __lbus_to_virt(dma_addr_t x)
if (is_lbus_device(dev) && __is_lbus_dma(__dma)) \
__virt = __lbus_to_virt(__dma); \
else \
- __virt = __bus_to_virt(__dma); \
+ __virt = __phys_to_virt(__dma); \
(void *)__virt; \
})
@@ -66,7 +54,7 @@ static inline unsigned long __lbus_to_virt(dma_addr_t x)
if (is_lbus_device(dev) && __is_lbus_virt(__virt)) \
__dma = __virt_to_lbus(__virt); \
else \
- __dma = __virt_to_bus(__virt); \
+ __dma = __virt_to_phys(__virt); \
__dma; \
})
diff --git a/arch/arm/mach-iop13xx/include/mach/timex.h b/arch/arm/mach-iop13xx/include/mach/timex.h
index 5b1f1c8a8270..45fb2745bb54 100644
--- a/arch/arm/mach-iop13xx/include/mach/timex.h
+++ b/arch/arm/mach-iop13xx/include/mach/timex.h
@@ -1,3 +1 @@
-#include <mach/hardware.h>
-
#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-iop32x/include/mach/dma.h b/arch/arm/mach-iop32x/include/mach/dma.h
deleted file mode 100644
index f8bd817f205d..000000000000
--- a/arch/arm/mach-iop32x/include/mach/dma.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-iop32x/include/mach/dma.h
- *
- * Copyright (C) 2004 Intel Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
diff --git a/arch/arm/mach-iop32x/include/mach/io.h b/arch/arm/mach-iop32x/include/mach/io.h
index ce54705ba3d4..339e5854728b 100644
--- a/arch/arm/mach-iop32x/include/mach/io.h
+++ b/arch/arm/mach-iop32x/include/mach/io.h
@@ -11,7 +11,7 @@
#ifndef __IO_H
#define __IO_H
-#include <mach/hardware.h>
+#include <asm/hardware/iop3xx.h>
extern void __iomem *__iop3xx_ioremap(unsigned long cookie, size_t size,
unsigned int mtype);
diff --git a/arch/arm/mach-iop32x/include/mach/memory.h b/arch/arm/mach-iop32x/include/mach/memory.h
index 42cd4bf3148c..c30f6450ad50 100644
--- a/arch/arm/mach-iop32x/include/mach/memory.h
+++ b/arch/arm/mach-iop32x/include/mach/memory.h
@@ -5,22 +5,9 @@
#ifndef __MEMORY_H
#define __MEMORY_H
-#include <mach/hardware.h>
-
/*
* Physical DRAM offset.
*/
#define PHYS_OFFSET UL(0xa0000000)
-/*
- * Virtual view <-> PCI DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) (__virt_to_phys(x))
-#define __bus_to_virt(x) (__phys_to_virt(x))
-
-
#endif
diff --git a/arch/arm/mach-iop32x/include/mach/system.h b/arch/arm/mach-iop32x/include/mach/system.h
index 20f923e54f46..32d9e5b0a28d 100644
--- a/arch/arm/mach-iop32x/include/mach/system.h
+++ b/arch/arm/mach-iop32x/include/mach/system.h
@@ -7,8 +7,9 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
#include <asm/mach-types.h>
+#include <asm/hardware/iop3xx.h>
+#include <mach/n2100.h>
static inline void arch_idle(void)
{
diff --git a/arch/arm/mach-iop32x/include/mach/timex.h b/arch/arm/mach-iop32x/include/mach/timex.h
index a541afced3cb..7262ab81419d 100644
--- a/arch/arm/mach-iop32x/include/mach/timex.h
+++ b/arch/arm/mach-iop32x/include/mach/timex.h
@@ -3,7 +3,4 @@
*
* IOP32x architecture timex specifications
*/
-
-#include <mach/hardware.h>
-
#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-iop33x/include/mach/dma.h b/arch/arm/mach-iop33x/include/mach/dma.h
deleted file mode 100644
index d8b42232931d..000000000000
--- a/arch/arm/mach-iop33x/include/mach/dma.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-iop33x/include/mach/dma.h
- *
- * Copyright (C) 2004 Intel Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
diff --git a/arch/arm/mach-iop33x/include/mach/io.h b/arch/arm/mach-iop33x/include/mach/io.h
index 158874631217..e99a7ed6d050 100644
--- a/arch/arm/mach-iop33x/include/mach/io.h
+++ b/arch/arm/mach-iop33x/include/mach/io.h
@@ -11,7 +11,7 @@
#ifndef __IO_H
#define __IO_H
-#include <mach/hardware.h>
+#include <asm/hardware/iop3xx.h>
extern void __iomem *__iop3xx_ioremap(unsigned long cookie, size_t size,
unsigned int mtype);
diff --git a/arch/arm/mach-iop33x/include/mach/memory.h b/arch/arm/mach-iop33x/include/mach/memory.h
index 2cef0bbb354f..a30a96aa6d2d 100644
--- a/arch/arm/mach-iop33x/include/mach/memory.h
+++ b/arch/arm/mach-iop33x/include/mach/memory.h
@@ -5,22 +5,9 @@
#ifndef __MEMORY_H
#define __MEMORY_H
-#include <mach/hardware.h>
-
/*
* Physical DRAM offset.
*/
#define PHYS_OFFSET UL(0x00000000)
-/*
- * Virtual view <-> PCI DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) (__virt_to_phys(x))
-#define __bus_to_virt(x) (__phys_to_virt(x))
-
-
#endif
diff --git a/arch/arm/mach-iop33x/include/mach/system.h b/arch/arm/mach-iop33x/include/mach/system.h
index 7bf3bfb49446..0cb3ad862acd 100644
--- a/arch/arm/mach-iop33x/include/mach/system.h
+++ b/arch/arm/mach-iop33x/include/mach/system.h
@@ -7,6 +7,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <asm/hardware/iop3xx.h>
static inline void arch_idle(void)
{
diff --git a/arch/arm/mach-iop33x/include/mach/timex.h b/arch/arm/mach-iop33x/include/mach/timex.h
index c75760844d49..54c589091d6e 100644
--- a/arch/arm/mach-iop33x/include/mach/timex.h
+++ b/arch/arm/mach-iop33x/include/mach/timex.h
@@ -3,7 +3,4 @@
*
* IOP3xx architecture timex specifications
*/
-
-#include <mach/hardware.h>
-
#define CLOCK_TICK_RATE (100 * HZ)
diff --git a/arch/arm/mach-ixp2000/include/mach/dma.h b/arch/arm/mach-ixp2000/include/mach/dma.h
deleted file mode 100644
index 26063d60f622..000000000000
--- a/arch/arm/mach-ixp2000/include/mach/dma.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * arch/arm/mach-ixp2000/include/mach/dma.h
- *
- * Copyright (C) 2002 Intel Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
diff --git a/arch/arm/mach-ixp2000/include/mach/memory.h b/arch/arm/mach-ixp2000/include/mach/memory.h
index 241529a7c52d..aee7eb8a71b2 100644
--- a/arch/arm/mach-ixp2000/include/mach/memory.h
+++ b/arch/arm/mach-ixp2000/include/mach/memory.h
@@ -15,13 +15,6 @@
#define PHYS_OFFSET UL(0x00000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
#include <mach/ixp2000-regs.h>
#define __virt_to_bus(v) \
diff --git a/arch/arm/mach-ixp23xx/include/mach/dma.h b/arch/arm/mach-ixp23xx/include/mach/dma.h
deleted file mode 100644
index 8886544b93f7..000000000000
--- a/arch/arm/mach-ixp23xx/include/mach/dma.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/*
- * arch/arm/mach-ixp23xx/include/mach/dma.h
- */
diff --git a/arch/arm/mach-ixp23xx/include/mach/io.h b/arch/arm/mach-ixp23xx/include/mach/io.h
index 305ea1808c71..fd9ef8e519f7 100644
--- a/arch/arm/mach-ixp23xx/include/mach/io.h
+++ b/arch/arm/mach-ixp23xx/include/mach/io.h
@@ -20,8 +20,6 @@
#define __io(p) ((void __iomem*)((p) + IXP23XX_PCI_IO_VIRT))
#define __mem_pci(a) (a)
-#include <linux/kernel.h> /* For BUG */
-
static inline void __iomem *
ixp23xx_ioremap(unsigned long addr, unsigned long size, unsigned int mtype)
{
diff --git a/arch/arm/mach-ixp23xx/include/mach/memory.h b/arch/arm/mach-ixp23xx/include/mach/memory.h
index 9d40115f7ebe..fdd138706c70 100644
--- a/arch/arm/mach-ixp23xx/include/mach/memory.h
+++ b/arch/arm/mach-ixp23xx/include/mach/memory.h
@@ -19,16 +19,6 @@
*/
#define PHYS_OFFSET (0x00000000)
-
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#ifndef __ASSEMBLY__
-
#define __virt_to_bus(v) \
({ unsigned int ret; \
ret = ((__virt_to_phys(v) - 0x00000000) + \
@@ -43,6 +33,3 @@
#define arch_is_coherent() 1
#endif
-
-
-#endif
diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c
index e7c6386782ed..5add22fc9899 100644
--- a/arch/arm/mach-ixp4xx/fsg-setup.c
+++ b/arch/arm/mach-ixp4xx/fsg-setup.c
@@ -177,7 +177,6 @@ static irqreturn_t fsg_reset_handler(int irq, void *dev_id)
static void __init fsg_init(void)
{
- DECLARE_MAC_BUF(mac_buf);
uint8_t __iomem *f;
ixp4xx_sys_init();
@@ -256,10 +255,10 @@ static void __init fsg_init(void)
#endif
iounmap(f);
}
- printk(KERN_INFO "FSG: Using MAC address %s for port 0\n",
- print_mac(mac_buf, fsg_plat_eth[0].hwaddr));
- printk(KERN_INFO "FSG: Using MAC address %s for port 1\n",
- print_mac(mac_buf, fsg_plat_eth[1].hwaddr));
+ printk(KERN_INFO "FSG: Using MAC address %pM for port 0\n",
+ fsg_plat_eth[0].hwaddr);
+ printk(KERN_INFO "FSG: Using MAC address %pM for port 1\n",
+ fsg_plat_eth[1].hwaddr);
}
diff --git a/arch/arm/mach-ixp4xx/include/mach/dma.h b/arch/arm/mach-ixp4xx/include/mach/dma.h
deleted file mode 100644
index 00c5070c0201..000000000000
--- a/arch/arm/mach-ixp4xx/include/mach/dma.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * arch/arm/mach-ixp4xx/include/mach/dma.h
- *
- * Copyright (C) 2001-2004 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.
- *
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-#include <linux/device.h>
-#include <asm/page.h>
-#include <asm/sizes.h>
-#include <mach/hardware.h>
-
-#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_64M)
-
-#endif /* _ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-ixp4xx/include/mach/io.h b/arch/arm/mach-ixp4xx/include/mach/io.h
index 319948e31bec..ce63048d45eb 100644
--- a/arch/arm/mach-ixp4xx/include/mach/io.h
+++ b/arch/arm/mach-ixp4xx/include/mach/io.h
@@ -49,8 +49,6 @@ extern int ixp4xx_pci_write(u32 addr, u32 cmd, u32 data);
#else
-#include <linux/mm.h>
-
/*
* In the case of using indirect PCI, we simply return the actual PCI
* address and our read/write implementation use that to drive the
@@ -241,7 +239,7 @@ __ixp4xx_readsl(const volatile void __iomem *bus_addr, u32 *vaddr, u32 count)
#ifndef CONFIG_PCI
-#define __io(v) v
+#define __io(v) __typesafe_io(v)
#else
diff --git a/arch/arm/mach-ixp4xx/include/mach/memory.h b/arch/arm/mach-ixp4xx/include/mach/memory.h
index c4d2830ac987..98f5e5e20980 100644
--- a/arch/arm/mach-ixp4xx/include/mach/memory.h
+++ b/arch/arm/mach-ixp4xx/include/mach/memory.h
@@ -22,19 +22,8 @@ void ixp4xx_adjust_zones(int node, unsigned long *size, unsigned long *holes);
ixp4xx_adjust_zones(node, size, holes)
#define ISA_DMA_THRESHOLD (SZ_64M - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_64M)
#endif
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- *
- * These are dummies for now.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-ixp4xx/include/mach/qmgr.h b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
index 1e52b95cede5..0cbe6ceb67c5 100644
--- a/arch/arm/mach-ixp4xx/include/mach/qmgr.h
+++ b/arch/arm/mach-ixp4xx/include/mach/qmgr.h
@@ -12,6 +12,8 @@
#include <linux/io.h>
#include <linux/kernel.h>
+#define DEBUG_QMGR 0
+
#define HALF_QUEUES 32
#define QUEUES 64 /* only 32 lower queues currently supported */
#define MAX_QUEUE_LENGTH 4 /* in dwords */
@@ -61,22 +63,51 @@ void qmgr_enable_irq(unsigned int queue);
void qmgr_disable_irq(unsigned int queue);
/* request_ and release_queue() must be called from non-IRQ context */
+
+#if DEBUG_QMGR
+extern char qmgr_queue_descs[QUEUES][32];
+
int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
unsigned int nearly_empty_watermark,
- unsigned int nearly_full_watermark);
+ unsigned int nearly_full_watermark,
+ const char *desc_format, const char* name);
+#else
+int __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+ unsigned int nearly_empty_watermark,
+ unsigned int nearly_full_watermark);
+#define qmgr_request_queue(queue, len, nearly_empty_watermark, \
+ nearly_full_watermark, desc_format, name) \
+ __qmgr_request_queue(queue, len, nearly_empty_watermark, \
+ nearly_full_watermark)
+#endif
+
void qmgr_release_queue(unsigned int queue);
static inline void qmgr_put_entry(unsigned int queue, u32 val)
{
extern struct qmgr_regs __iomem *qmgr_regs;
+#if DEBUG_QMGR
+ BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+ printk(KERN_DEBUG "Queue %s(%i) put %X\n",
+ qmgr_queue_descs[queue], queue, val);
+#endif
__raw_writel(val, &qmgr_regs->acc[queue][0]);
}
static inline u32 qmgr_get_entry(unsigned int queue)
{
+ u32 val;
extern struct qmgr_regs __iomem *qmgr_regs;
- return __raw_readl(&qmgr_regs->acc[queue][0]);
+ val = __raw_readl(&qmgr_regs->acc[queue][0]);
+#if DEBUG_QMGR
+ BUG_ON(!qmgr_queue_descs[queue]); /* not yet requested */
+
+ printk(KERN_DEBUG "Queue %s(%i) get %X\n",
+ qmgr_queue_descs[queue], queue, val);
+#endif
+ return val;
}
static inline int qmgr_get_stat1(unsigned int queue)
diff --git a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
index c6cb069a5a83..bfddc73d0a20 100644
--- a/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
+++ b/arch/arm/mach-ixp4xx/ixp4xx_qmgr.c
@@ -14,8 +14,6 @@
#include <linux/module.h>
#include <mach/qmgr.h>
-#define DEBUG 0
-
struct qmgr_regs __iomem *qmgr_regs;
static struct resource *mem_res;
static spinlock_t qmgr_lock;
@@ -23,6 +21,10 @@ static u32 used_sram_bitmap[4]; /* 128 16-dword pages */
static void (*irq_handlers[HALF_QUEUES])(void *pdev);
static void *irq_pdevs[HALF_QUEUES];
+#if DEBUG_QMGR
+char qmgr_queue_descs[QUEUES][32];
+#endif
+
void qmgr_set_irq(unsigned int queue, int src,
void (*handler)(void *pdev), void *pdev)
{
@@ -70,6 +72,7 @@ void qmgr_disable_irq(unsigned int queue)
spin_lock_irqsave(&qmgr_lock, flags);
__raw_writel(__raw_readl(&qmgr_regs->irqen[0]) & ~(1 << queue),
&qmgr_regs->irqen[0]);
+ __raw_writel(1 << queue, &qmgr_regs->irqstat[0]); /* clear */
spin_unlock_irqrestore(&qmgr_lock, flags);
}
@@ -81,9 +84,16 @@ static inline void shift_mask(u32 *mask)
mask[0] <<= 1;
}
+#if DEBUG_QMGR
int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
unsigned int nearly_empty_watermark,
- unsigned int nearly_full_watermark)
+ unsigned int nearly_full_watermark,
+ const char *desc_format, const char* name)
+#else
+int __qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
+ unsigned int nearly_empty_watermark,
+ unsigned int nearly_full_watermark)
+#endif
{
u32 cfg, addr = 0, mask[4]; /* in 16-dwords */
int err;
@@ -151,12 +161,13 @@ int qmgr_request_queue(unsigned int queue, unsigned int len /* dwords */,
used_sram_bitmap[2] |= mask[2];
used_sram_bitmap[3] |= mask[3];
__raw_writel(cfg | (addr << 14), &qmgr_regs->sram[queue]);
- spin_unlock_irq(&qmgr_lock);
-
-#if DEBUG
- printk(KERN_DEBUG "qmgr: requested queue %i, addr = 0x%02X\n",
- queue, addr);
+#if DEBUG_QMGR
+ snprintf(qmgr_queue_descs[queue], sizeof(qmgr_queue_descs[0]),
+ desc_format, name);
+ printk(KERN_DEBUG "qmgr: requested queue %s(%i) addr = 0x%02X\n",
+ qmgr_queue_descs[queue], queue, addr);
#endif
+ spin_unlock_irq(&qmgr_lock);
return 0;
err:
@@ -189,6 +200,11 @@ void qmgr_release_queue(unsigned int queue)
while (addr--)
shift_mask(mask);
+#if DEBUG_QMGR
+ printk(KERN_DEBUG "qmgr: releasing queue %s(%i)\n",
+ qmgr_queue_descs[queue], queue);
+ qmgr_queue_descs[queue][0] = '\x0';
+#endif
__raw_writel(0, &qmgr_regs->sram[queue]);
used_sram_bitmap[0] &= ~mask[0];
@@ -199,9 +215,10 @@ void qmgr_release_queue(unsigned int queue)
spin_unlock_irq(&qmgr_lock);
module_put(THIS_MODULE);
-#if DEBUG
- printk(KERN_DEBUG "qmgr: released queue %i\n", queue);
-#endif
+
+ while ((addr = qmgr_get_entry(queue)))
+ printk(KERN_ERR "qmgr: released queue %i not empty: 0x%08X\n",
+ queue, addr);
}
static int qmgr_init(void)
@@ -272,5 +289,10 @@ EXPORT_SYMBOL(qmgr_regs);
EXPORT_SYMBOL(qmgr_set_irq);
EXPORT_SYMBOL(qmgr_enable_irq);
EXPORT_SYMBOL(qmgr_disable_irq);
+#if DEBUG_QMGR
+EXPORT_SYMBOL(qmgr_queue_descs);
EXPORT_SYMBOL(qmgr_request_queue);
+#else
+EXPORT_SYMBOL(__qmgr_request_queue);
+#endif
EXPORT_SYMBOL(qmgr_release_queue);
diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c
index 0acd95ecf27e..921c947b5b6b 100644
--- a/arch/arm/mach-ixp4xx/nas100d-setup.c
+++ b/arch/arm/mach-ixp4xx/nas100d-setup.c
@@ -231,7 +231,6 @@ static irqreturn_t nas100d_reset_handler(int irq, void *dev_id)
static void __init nas100d_init(void)
{
- DECLARE_MAC_BUF(mac_buf);
uint8_t __iomem *f;
int i;
@@ -294,8 +293,8 @@ static void __init nas100d_init(void)
#endif
iounmap(f);
}
- printk(KERN_INFO "NAS100D: Using MAC address %s for port 0\n",
- print_mac(mac_buf, nas100d_plat_eth[0].hwaddr));
+ printk(KERN_INFO "NAS100D: Using MAC address %pM for port 0\n",
+ nas100d_plat_eth[0].hwaddr);
}
diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c
index bc9d920ae54f..ff6a08d02cc4 100644
--- a/arch/arm/mach-ixp4xx/nslu2-setup.c
+++ b/arch/arm/mach-ixp4xx/nslu2-setup.c
@@ -220,7 +220,6 @@ static struct sys_timer nslu2_timer = {
static void __init nslu2_init(void)
{
- DECLARE_MAC_BUF(mac_buf);
uint8_t __iomem *f;
int i;
@@ -275,8 +274,8 @@ static void __init nslu2_init(void)
#endif
iounmap(f);
}
- printk(KERN_INFO "NSLU2: Using MAC address %s for port 0\n",
- print_mac(mac_buf, nslu2_plat_eth[0].hwaddr));
+ printk(KERN_INFO "NSLU2: Using MAC address %pM for port 0\n",
+ nslu2_plat_eth[0].hwaddr);
}
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index 0bb1fbd84ccb..7b8ef97fb501 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -57,6 +57,7 @@ void __init kirkwood_map_io(void)
****************************************************************************/
static struct orion_ehci_data kirkwood_ehci_data = {
.dram = &kirkwood_mbus_dram_info,
+ .phy_version = EHCI_PHY_NA,
};
static u64 ehci_dmamask = 0xffffffffUL;
@@ -153,6 +154,64 @@ void __init kirkwood_ge00_init(struct mv643xx_eth_platform_data *eth_data)
/*****************************************************************************
+ * GE01
+ ****************************************************************************/
+struct mv643xx_eth_shared_platform_data kirkwood_ge01_shared_data = {
+ .dram = &kirkwood_mbus_dram_info,
+ .shared_smi = &kirkwood_ge00_shared,
+};
+
+static struct resource kirkwood_ge01_shared_resources[] = {
+ {
+ .name = "ge01 base",
+ .start = GE01_PHYS_BASE + 0x2000,
+ .end = GE01_PHYS_BASE + 0x3fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .name = "ge01 err irq",
+ .start = IRQ_KIRKWOOD_GE01_ERR,
+ .end = IRQ_KIRKWOOD_GE01_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device kirkwood_ge01_shared = {
+ .name = MV643XX_ETH_SHARED_NAME,
+ .id = 1,
+ .dev = {
+ .platform_data = &kirkwood_ge01_shared_data,
+ },
+ .num_resources = ARRAY_SIZE(kirkwood_ge01_shared_resources),
+ .resource = kirkwood_ge01_shared_resources,
+};
+
+static struct resource kirkwood_ge01_resources[] = {
+ {
+ .name = "ge01 irq",
+ .start = IRQ_KIRKWOOD_GE01_SUM,
+ .end = IRQ_KIRKWOOD_GE01_SUM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device kirkwood_ge01 = {
+ .name = MV643XX_ETH_NAME,
+ .id = 1,
+ .num_resources = 1,
+ .resource = kirkwood_ge01_resources,
+};
+
+void __init kirkwood_ge01_init(struct mv643xx_eth_platform_data *eth_data)
+{
+ eth_data->shared = &kirkwood_ge01_shared;
+ kirkwood_ge01.dev.platform_data = eth_data;
+
+ platform_device_register(&kirkwood_ge01_shared);
+ platform_device_register(&kirkwood_ge01);
+}
+
+
+/*****************************************************************************
* Ethernet switch
****************************************************************************/
static struct resource kirkwood_switch_resources[] = {
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
index 5774632a67e3..fe367c18e722 100644
--- a/arch/arm/mach-kirkwood/common.h
+++ b/arch/arm/mach-kirkwood/common.h
@@ -30,6 +30,7 @@ void kirkwood_pcie_id(u32 *dev, u32 *rev);
void kirkwood_ehci_init(void);
void kirkwood_ge00_init(struct mv643xx_eth_platform_data *eth_data);
+void kirkwood_ge01_init(struct mv643xx_eth_platform_data *eth_data);
void kirkwood_ge00_switch_init(struct dsa_platform_data *d, int irq);
void kirkwood_pcie_init(void);
void kirkwood_rtc_init(void);
diff --git a/arch/arm/mach-kirkwood/include/mach/dma.h b/arch/arm/mach-kirkwood/include/mach/dma.h
deleted file mode 100644
index 40a8c178f10d..000000000000
--- a/arch/arm/mach-kirkwood/include/mach/dma.h
+++ /dev/null
@@ -1 +0,0 @@
-/* empty */
diff --git a/arch/arm/mach-kirkwood/include/mach/gpio.h b/arch/arm/mach-kirkwood/include/mach/gpio.h
new file mode 100644
index 000000000000..81b335eb62ec
--- /dev/null
+++ b/arch/arm/mach-kirkwood/include/mach/gpio.h
@@ -0,0 +1,38 @@
+/*
+ * arch/asm-arm/mach-kirkwood/include/mach/gpio.h
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ASM_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H
+
+#include <mach/irqs.h>
+#include <plat/gpio.h>
+#include <asm-generic/gpio.h> /* cansleep wrappers */
+
+#define GPIO_MAX 50
+#define GPIO_OFF(pin) (((pin) >> 5) ? 0x0140 : 0x0100)
+#define GPIO_OUT(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x00)
+#define GPIO_IO_CONF(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x04)
+#define GPIO_BLINK_EN(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x08)
+#define GPIO_IN_POL(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x0c)
+#define GPIO_DATA_IN(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x10)
+#define GPIO_EDGE_CAUSE(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x14)
+#define GPIO_EDGE_MASK(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x18)
+#define GPIO_LEVEL_MASK(pin) (DEV_BUS_VIRT_BASE + GPIO_OFF(pin) + 0x1c)
+
+static inline int gpio_to_irq(int pin)
+{
+ return pin + IRQ_KIRKWOOD_GPIO_START;
+}
+
+static inline int irq_to_gpio(int irq)
+{
+ return irq - IRQ_KIRKWOOD_GPIO_START;
+}
+
+
+#endif
diff --git a/arch/arm/mach-kirkwood/include/mach/irqs.h b/arch/arm/mach-kirkwood/include/mach/irqs.h
index ffab89f21c11..f00a0a45a67e 100644
--- a/arch/arm/mach-kirkwood/include/mach/irqs.h
+++ b/arch/arm/mach-kirkwood/include/mach/irqs.h
@@ -11,8 +11,6 @@
#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H
-#include "kirkwood.h" /* need GPIO_MAX */
-
/*
* Low Interrupt Controller
*/
@@ -51,12 +49,13 @@
#define IRQ_KIRKWOOD_GPIO_HIGH_8_15 40
#define IRQ_KIRKWOOD_GPIO_HIGH_16_23 41
#define IRQ_KIRKWOOD_GE00_ERR 46
+#define IRQ_KIRKWOOD_GE01_ERR 47
/*
* KIRKWOOD General Purpose Pins
*/
#define IRQ_KIRKWOOD_GPIO_START 64
-#define NR_GPIO_IRQS GPIO_MAX
+#define NR_GPIO_IRQS 50
#define NR_IRQS (IRQ_KIRKWOOD_GPIO_START + NR_GPIO_IRQS)
diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
index eae42406fd86..ada480c0e197 100644
--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
@@ -117,7 +117,4 @@
#define SATA_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x80000)
-#define GPIO_MAX 50
-
-
#endif
diff --git a/arch/arm/mach-kirkwood/include/mach/memory.h b/arch/arm/mach-kirkwood/include/mach/memory.h
index b5fb34bdccd5..45431e131465 100644
--- a/arch/arm/mach-kirkwood/include/mach/memory.h
+++ b/arch/arm/mach-kirkwood/include/mach/memory.h
@@ -7,8 +7,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-
#endif
diff --git a/arch/arm/mach-kirkwood/irq.c b/arch/arm/mach-kirkwood/irq.c
index 5790643ffe07..efb86b700276 100644
--- a/arch/arm/mach-kirkwood/irq.c
+++ b/arch/arm/mach-kirkwood/irq.c
@@ -13,10 +13,45 @@
#include <linux/irq.h>
#include <linux/io.h>
#include <plat/irq.h>
+#include <asm/gpio.h>
#include "common.h"
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ BUG_ON(irq < IRQ_KIRKWOOD_GPIO_LOW_0_7);
+ BUG_ON(irq > IRQ_KIRKWOOD_GPIO_HIGH_16_23);
+
+ orion_gpio_irq_handler((irq - IRQ_KIRKWOOD_GPIO_LOW_0_7) << 3);
+}
+
void __init kirkwood_init_irq(void)
{
+ int i;
+
orion_irq_init(0, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF));
orion_irq_init(32, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF));
+
+ /*
+ * Mask and clear GPIO IRQ interrupts.
+ */
+ writel(0, GPIO_LEVEL_MASK(0));
+ writel(0, GPIO_EDGE_MASK(0));
+ writel(0, GPIO_EDGE_CAUSE(0));
+ writel(0, GPIO_LEVEL_MASK(32));
+ writel(0, GPIO_EDGE_MASK(32));
+ writel(0, GPIO_EDGE_CAUSE(32));
+
+ for (i = IRQ_KIRKWOOD_GPIO_START; i < NR_IRQS; i++) {
+ set_irq_chip(i, &orion_gpio_irq_level_chip);
+ set_irq_handler(i, handle_level_irq);
+ irq_desc[i].status |= IRQ_LEVEL;
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_0_7, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_8_15, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_16_23, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_24_31, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_0_7, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_8_15, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_16_23, gpio_irq_handler);
}
diff --git a/arch/arm/mach-kirkwood/rd88f6281-setup.c b/arch/arm/mach-kirkwood/rd88f6281-setup.c
index 175054abd630..9a0e905d10cd 100644
--- a/arch/arm/mach-kirkwood/rd88f6281-setup.c
+++ b/arch/arm/mach-kirkwood/rd88f6281-setup.c
@@ -80,24 +80,38 @@ static struct dsa_platform_data rd88f6281_switch_data = {
.port_names[1] = "lan2",
.port_names[2] = "lan3",
.port_names[3] = "lan4",
- .port_names[4] = "wan",
.port_names[5] = "cpu",
};
+static struct mv643xx_eth_platform_data rd88f6281_ge01_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(11),
+};
+
static struct mv_sata_platform_data rd88f6281_sata_data = {
.n_ports = 2,
};
static void __init rd88f6281_init(void)
{
+ u32 dev, rev;
+
/*
* Basic setup. Needs to be called early.
*/
kirkwood_init();
kirkwood_ehci_init();
+
kirkwood_ge00_init(&rd88f6281_ge00_data);
+ kirkwood_pcie_id(&dev, &rev);
+ if (rev == MV88F6281_REV_A0) {
+ rd88f6281_switch_data.sw_addr = 10;
+ kirkwood_ge01_init(&rd88f6281_ge01_data);
+ } else {
+ rd88f6281_switch_data.port_names[4] = "wan";
+ }
kirkwood_ge00_switch_init(&rd88f6281_switch_data, NO_IRQ);
+
kirkwood_rtc_init();
kirkwood_sata_init(&rd88f6281_sata_data);
kirkwood_uart0_init();
diff --git a/arch/arm/mach-ks8695/Kconfig b/arch/arm/mach-ks8695/Kconfig
index ce1cf8de2b4d..2754daabda55 100644
--- a/arch/arm/mach-ks8695/Kconfig
+++ b/arch/arm/mach-ks8695/Kconfig
@@ -8,6 +8,12 @@ config MACH_KS8695
Say 'Y' here if you want your kernel to run on the original
Kendin-Micrel KS8695 development board.
+config MACH_DSM320
+ bool "DSM-320 Wireless Media Player"
+ help
+ Say 'Y' here if you want your kernel to run on the D-Link
+ DSM-320 Wireless Media Player.
+
endmenu
endif
diff --git a/arch/arm/mach-ks8695/Makefile b/arch/arm/mach-ks8695/Makefile
index ade42b73afbb..f735d2cc0294 100644
--- a/arch/arm/mach-ks8695/Makefile
+++ b/arch/arm/mach-ks8695/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_LEDS) += leds.o
# Board-specific support
obj-$(CONFIG_MACH_KS8695) += board-micrel.o
+obj-$(CONFIG_MACH_DSM320) += board-dsm320.o
diff --git a/arch/arm/mach-ks8695/board-dsm320.c b/arch/arm/mach-ks8695/board-dsm320.c
new file mode 100644
index 000000000000..521ff0789f39
--- /dev/null
+++ b/arch/arm/mach-ks8695/board-dsm320.c
@@ -0,0 +1,131 @@
+/*
+ * arch/arm/mach-ks8695/board-dsm320.c
+ *
+ * DSM-320 D-Link Wireless Media Player, board support.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.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 <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/devices.h>
+#include <mach/gpio.h>
+
+#include "generic.h"
+
+#ifdef CONFIG_PCI
+static int dsm320_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ switch (slot) {
+ case 0:
+ /* PCI-AHB bridge? */
+ return KS8695_IRQ_EXTERN0;
+ case 18:
+ /* Mini PCI slot */
+ return KS8695_IRQ_EXTERN2;
+ case 20:
+ /* RealMAGIC chip */
+ return KS8695_IRQ_EXTERN0;
+ }
+ BUG();
+}
+
+static struct ks8695_pci_cfg __initdata dsm320_pci = {
+ .mode = KS8695_MODE_MINIPCI,
+ .map_irq = dsm320_pci_map_irq,
+};
+
+static void __init dsm320_register_pci(void)
+{
+ /* Initialise the GPIO lines for interrupt mode */
+ /* RealMAGIC */
+ ks8695_gpio_interrupt(KS8695_GPIO_0, IRQ_TYPE_LEVEL_LOW);
+ /* MiniPCI Slot */
+ ks8695_gpio_interrupt(KS8695_GPIO_2, IRQ_TYPE_LEVEL_LOW);
+
+ ks8695_init_pci(&dsm320_pci);
+}
+
+#else
+static inline void __init dsm320_register_pci(void) { }
+#endif
+
+static struct physmap_flash_data dsm320_nor_pdata = {
+ .width = 4,
+ .nr_parts = 0,
+};
+
+static struct resource dsm320_nor_resource[] = {
+ [0] = {
+ .start = SZ_32M, /* We expect the bootloader to map
+ * the flash here.
+ */
+ .end = SZ_32M + SZ_4M - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device dsm320_device_nor = {
+ .name = "physmap-flash",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(dsm320_nor_resource),
+ .resource = dsm320_nor_resource,
+ .dev = {
+ .platform_data = &dsm320_nor_pdata,
+ },
+};
+
+void __init dsm320_register_nor(void)
+{
+ int ret;
+
+ ret = platform_device_register(&dsm320_device_nor);
+ if (ret < 0)
+ printk(KERN_ERR "failed to register physmap-flash device\n");
+}
+
+static void __init dsm320_init(void)
+{
+ /* GPIO registration */
+ ks8695_register_gpios();
+
+ /* PCI registration */
+ dsm320_register_pci();
+
+ /* Network device */
+ ks8695_add_device_lan(); /* eth0 = LAN */
+
+ /* NOR devices */
+ dsm320_register_nor();
+}
+
+MACHINE_START(DSM320, "D-Link DSM-320 Wireless Media Player")
+ /* Maintainer: Simtec Electronics. */
+ .phys_io = KS8695_IO_PA,
+ .io_pg_offst = (KS8695_IO_VA >> 18) & 0xfffc,
+ .boot_params = KS8695_SDRAM_PA + 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = dsm320_init,
+ .timer = &ks8695_timer,
+MACHINE_END
diff --git a/arch/arm/mach-ks8695/board-micrel.c b/arch/arm/mach-ks8695/board-micrel.c
index 0468e93b7d3b..8ceaf5ac6e2c 100644
--- a/arch/arm/mach-ks8695/board-micrel.c
+++ b/arch/arm/mach-ks8695/board-micrel.c
@@ -18,6 +18,7 @@
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
+#include <mach/gpio.h>
#include <mach/devices.h>
#include "generic.h"
@@ -39,6 +40,8 @@ static void __init micrel_init(void)
{
printk(KERN_INFO "Micrel KS8695 Development Board initializing\n");
+ ks8695_register_gpios();
+
#ifdef CONFIG_PCI
ks8695_init_pci(&micrel_pci);
#endif
diff --git a/arch/arm/mach-ks8695/devices.c b/arch/arm/mach-ks8695/devices.c
index 4bd251482c8f..36ab0fd3d9b6 100644
--- a/arch/arm/mach-ks8695/devices.c
+++ b/arch/arm/mach-ks8695/devices.c
@@ -25,19 +25,20 @@
#include <mach/regs-wan.h>
#include <mach/regs-lan.h>
#include <mach/regs-hpna.h>
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
/* --------------------------------------------------------------------
* Ethernet
* -------------------------------------------------------------------- */
-#if defined(CONFIG_ARM_KS8695_ETHER) || defined(CONFIG_ARM_KS8695_ETHER_MODULE)
static u64 eth_dmamask = 0xffffffffUL;
static struct resource ks8695_wan_resources[] = {
[0] = {
- .start = KS8695_WAN_VA,
- .end = KS8695_WAN_VA + 0x00ff,
+ .start = KS8695_WAN_PA,
+ .end = KS8695_WAN_PA + 0x00ff,
.flags = IORESOURCE_MEM,
},
[1] = {
@@ -58,6 +59,12 @@ static struct resource ks8695_wan_resources[] = {
.end = KS8695_IRQ_WAN_LINK,
.flags = IORESOURCE_IRQ,
},
+ [4] = {
+ .name = "WAN PHY",
+ .start = KS8695_MISC_PA,
+ .end = KS8695_MISC_PA + 0x1f,
+ .flags = IORESOURCE_MEM,
+ },
};
static struct platform_device ks8695_wan_device = {
@@ -74,8 +81,8 @@ static struct platform_device ks8695_wan_device = {
static struct resource ks8695_lan_resources[] = {
[0] = {
- .start = KS8695_LAN_VA,
- .end = KS8695_LAN_VA + 0x00ff,
+ .start = KS8695_LAN_PA,
+ .end = KS8695_LAN_PA + 0x00ff,
.flags = IORESOURCE_MEM,
},
[1] = {
@@ -90,6 +97,12 @@ static struct resource ks8695_lan_resources[] = {
.end = KS8695_IRQ_LAN_TX_STATUS,
.flags = IORESOURCE_IRQ,
},
+ [3] = {
+ .name = "LAN SWITCH",
+ .start = KS8695_SWITCH_PA,
+ .end = KS8695_SWITCH_PA + 0x4f,
+ .flags = IORESOURCE_MEM,
+ },
};
static struct platform_device ks8695_lan_device = {
@@ -106,8 +119,8 @@ static struct platform_device ks8695_lan_device = {
static struct resource ks8695_hpna_resources[] = {
[0] = {
- .start = KS8695_HPNA_VA,
- .end = KS8695_HPNA_VA + 0x00ff,
+ .start = KS8695_HPNA_PA,
+ .end = KS8695_HPNA_PA + 0x00ff,
.flags = IORESOURCE_MEM,
},
[1] = {
@@ -149,18 +162,12 @@ void __init ks8696_add_device_hpna(void)
{
platform_device_register(&ks8695_hpna_device);
}
-#else
-void __init ks8695_add_device_wan(void) {}
-void __init ks8695_add_device_lan(void) {}
-void __init ks8696_add_device_hpna(void) {}
-#endif
/* --------------------------------------------------------------------
* Watchdog
* -------------------------------------------------------------------- */
-#if defined(CONFIG_KS8695_WATCHDOG) || defined(CONFIG_KS8695_WATCHDOG_MODULE)
static struct platform_device ks8695_wdt_device = {
.name = "ks8695_wdt",
.id = -1,
@@ -171,9 +178,6 @@ static void __init ks8695_add_device_watchdog(void)
{
platform_device_register(&ks8695_wdt_device);
}
-#else
-static void __init ks8695_add_device_watchdog(void) {}
-#endif
/* --------------------------------------------------------------------
@@ -190,7 +194,7 @@ void __init ks8695_init_leds(u8 cpu_led, u8 timer_led)
gpio_direction_output(cpu_led, 1);
gpio_direction_output(timer_led, 1);
- ks8695_leds_cpu = cpu_led;
+ ks8695_leds_cpu = cpu_led;
ks8695_leds_timer = timer_led;
}
#else
diff --git a/arch/arm/mach-ks8695/gpio.c b/arch/arm/mach-ks8695/gpio.c
index 9aecf0c4b8b1..55fbf7111a5b 100644
--- a/arch/arm/mach-ks8695/gpio.c
+++ b/arch/arm/mach-ks8695/gpio.c
@@ -2,6 +2,8 @@
* arch/arm/mach-ks8695/gpio.c
*
* Copyright (C) 2006 Andrew Victor
+ * Updated to GPIOLIB, Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -35,7 +37,7 @@
* Configure a GPIO line for either GPIO function, or its internal
* function (Interrupt, Timer, etc).
*/
-static void __init_or_module ks8695_gpio_mode(unsigned int pin, short gpio)
+static void ks8695_gpio_mode(unsigned int pin, short gpio)
{
unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN };
unsigned long x, flags;
@@ -61,7 +63,7 @@ static unsigned short gpio_irq[] = { KS8695_IRQ_EXTERN0, KS8695_IRQ_EXTERN1, KS8
/*
* Configure GPIO pin as external interrupt source.
*/
-int __init_or_module ks8695_gpio_interrupt(unsigned int pin, unsigned int type)
+int ks8695_gpio_interrupt(unsigned int pin, unsigned int type)
{
unsigned long x, flags;
@@ -94,7 +96,7 @@ EXPORT_SYMBOL(ks8695_gpio_interrupt);
/*
* Configure the GPIO line as an input.
*/
-int __init_or_module gpio_direction_input(unsigned int pin)
+static int ks8695_gpio_direction_input(struct gpio_chip *gc, unsigned int pin)
{
unsigned long x, flags;
@@ -115,13 +117,13 @@ int __init_or_module gpio_direction_input(unsigned int pin)
return 0;
}
-EXPORT_SYMBOL(gpio_direction_input);
/*
* Configure the GPIO line as an output, with default state.
*/
-int __init_or_module gpio_direction_output(unsigned int pin, unsigned int state)
+static int ks8695_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int pin, int state)
{
unsigned long x, flags;
@@ -150,13 +152,13 @@ int __init_or_module gpio_direction_output(unsigned int pin, unsigned int state)
return 0;
}
-EXPORT_SYMBOL(gpio_direction_output);
/*
* Set the state of an output GPIO line.
*/
-void gpio_set_value(unsigned int pin, unsigned int state)
+static void ks8695_gpio_set_value(struct gpio_chip *gc,
+ unsigned int pin, int state)
{
unsigned long x, flags;
@@ -175,13 +177,12 @@ void gpio_set_value(unsigned int pin, unsigned int state)
local_irq_restore(flags);
}
-EXPORT_SYMBOL(gpio_set_value);
/*
* Read the state of a GPIO line.
*/
-int gpio_get_value(unsigned int pin)
+static int ks8695_gpio_get_value(struct gpio_chip *gc, unsigned int pin)
{
unsigned long x;
@@ -191,21 +192,18 @@ int gpio_get_value(unsigned int pin)
x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD);
return (x & IOPD(pin)) != 0;
}
-EXPORT_SYMBOL(gpio_get_value);
/*
* Map GPIO line to IRQ number.
*/
-int gpio_to_irq(unsigned int pin)
+static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin)
{
if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */
return -EINVAL;
return gpio_irq[pin];
}
-EXPORT_SYMBOL(gpio_to_irq);
-
/*
* Map IRQ number to GPIO line.
@@ -219,6 +217,26 @@ int irq_to_gpio(unsigned int irq)
}
EXPORT_SYMBOL(irq_to_gpio);
+/* GPIOLIB interface */
+
+static struct gpio_chip ks8695_gpio_chip = {
+ .label = "KS8695",
+ .direction_input = ks8695_gpio_direction_input,
+ .direction_output = ks8695_gpio_direction_output,
+ .get = ks8695_gpio_get_value,
+ .set = ks8695_gpio_set_value,
+ .to_irq = ks8695_gpio_to_irq,
+ .base = 0,
+ .ngpio = 16,
+ .can_sleep = 0,
+};
+
+/* Register the GPIOs */
+void ks8695_register_gpios(void)
+{
+ if (gpiochip_add(&ks8695_gpio_chip))
+ printk(KERN_ERR "Unable to register core GPIOs\n");
+}
/* .... Debug interface ..................................................... */
diff --git a/arch/arm/mach-ks8695/include/mach/dma.h b/arch/arm/mach-ks8695/include/mach/dma.h
deleted file mode 100644
index 561206280089..000000000000
--- a/arch/arm/mach-ks8695/include/mach/dma.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * arch/arm/mach-ks8695/include/mach/dma.h
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU 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
- */
diff --git a/arch/arm/mach-ks8695/include/mach/gpio.h b/arch/arm/mach-ks8695/include/mach/gpio.h
index d4af5c335f16..86312d476bc6 100644
--- a/arch/arm/mach-ks8695/include/mach/gpio.h
+++ b/arch/arm/mach-ks8695/include/mach/gpio.h
@@ -30,53 +30,28 @@
#define KS8695_GPIO_14 14
#define KS8695_GPIO_15 15
-
/*
* Configure GPIO pin as external interrupt source.
*/
-int __init_or_module ks8695_gpio_interrupt(unsigned int pin, unsigned int type);
-
-/*
- * Configure the GPIO line as an input.
- */
-int __init_or_module gpio_direction_input(unsigned int pin);
-
-/*
- * Configure the GPIO line as an output, with default state.
- */
-int __init_or_module gpio_direction_output(unsigned int pin, unsigned int state);
-
-/*
- * Set the state of an output GPIO line.
- */
-void gpio_set_value(unsigned int pin, unsigned int state);
-
-/*
- * Read the state of a GPIO line.
- */
-int gpio_get_value(unsigned int pin);
-
-/*
- * Map GPIO line to IRQ number.
- */
-int gpio_to_irq(unsigned int pin);
+extern int ks8695_gpio_interrupt(unsigned int pin, unsigned int type);
/*
* Map IRQ number to GPIO line.
*/
-int irq_to_gpio(unsigned int irq);
-
+extern int irq_to_gpio(unsigned int irq);
#include <asm-generic/gpio.h>
-static inline int gpio_request(unsigned int pin, const char *label)
-{
- return 0;
-}
+/* If it turns out that we need to optimise GPIO access for the
+ * Micrel's GPIOs, then these can be changed to check their argument
+ * directly as static inlines. However for now it's probably not
+ * worthwhile.
+ */
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_to_irq __gpio_to_irq
-static inline void gpio_free(unsigned int pin)
-{
- might_sleep();
-}
+/* Register the GPIOs */
+extern void ks8695_register_gpios(void);
#endif
diff --git a/arch/arm/mach-ks8695/include/mach/io.h b/arch/arm/mach-ks8695/include/mach/io.h
index f364f24ffe1e..a7a63ac3ba4e 100644
--- a/arch/arm/mach-ks8695/include/mach/io.h
+++ b/arch/arm/mach-ks8695/include/mach/io.h
@@ -13,7 +13,7 @@
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-ks8695/include/mach/memory.h b/arch/arm/mach-ks8695/include/mach/memory.h
index 8fbc4c76c38b..6d5887cf5742 100644
--- a/arch/arm/mach-ks8695/include/mach/memory.h
+++ b/arch/arm/mach-ks8695/include/mach/memory.h
@@ -37,11 +37,6 @@ extern struct bus_type platform_bus_type;
(dma_addr_t)__virt_to_phys(x) : (dma_addr_t)__virt_to_bus(x); })
#define __arch_page_to_dma(dev, x) __arch_virt_to_dma(dev, page_address(x))
-#else
-
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
#endif
diff --git a/arch/arm/mach-l7200/include/mach/dma.h b/arch/arm/mach-l7200/include/mach/dma.h
deleted file mode 100644
index c7e48bd4590c..000000000000
--- a/arch/arm/mach-l7200/include/mach/dma.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/arm/mach-l7200/include/mach/dma.h
- *
- * Copyright (C) 2000 Steve Hill (sjhill@cotw.com)
- *
- * Changelog:
- * 08-29-2000 SJH Created
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/* DMA is not yet implemented! It should be the same as acorn, copy over.. */
-
-/*
- * This is the maximum DMA address that can be DMAd to.
- * There should not be more than (0xd0000000 - 0xc0000000)
- * bytes of RAM.
- */
-#define MAX_DMA_ADDRESS 0xd0000000
-
-#define DMA_S0 0
-
-#endif /* _ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-l7200/include/mach/io.h b/arch/arm/mach-l7200/include/mach/io.h
index d432ba9e5dff..a770a89fb708 100644
--- a/arch/arm/mach-l7200/include/mach/io.h
+++ b/arch/arm/mach-l7200/include/mach/io.h
@@ -10,18 +10,12 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
/*
* There are not real ISA nor PCI buses, so we fake it.
*/
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-#define __io(a) __io(a)
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-l7200/include/mach/memory.h b/arch/arm/mach-l7200/include/mach/memory.h
index f338cf3ffd93..9fb40ed2f03b 100644
--- a/arch/arm/mach-l7200/include/mach/memory.h
+++ b/arch/arm/mach-l7200/include/mach/memory.h
@@ -17,9 +17,6 @@
*/
#define PHYS_OFFSET UL(0xf0000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
/*
* Cache flushing area - ROM
*/
diff --git a/arch/arm/mach-lh7a40x/clocks.c b/arch/arm/mach-lh7a40x/clocks.c
index 4fb23ac6b5ac..6182f5410b4d 100644
--- a/arch/arm/mach-lh7a40x/clocks.c
+++ b/arch/arm/mach-lh7a40x/clocks.c
@@ -14,21 +14,14 @@
#include <linux/err.h>
struct module;
-struct icst525_params;
struct clk {
struct list_head node;
unsigned long rate;
struct module *owner;
const char *name;
-// void *data;
-// const struct icst525_params *params;
-// void (*setvco)(struct clk *, struct icst525_vco vco);
};
-int clk_register(struct clk *clk);
-void clk_unregister(struct clk *clk);
-
/* ----- */
#define MAINDIV1(c) (((c) >> 7) & 0x0f)
@@ -79,31 +72,15 @@ unsigned int pclkfreq_get (void)
/* ----- */
-static LIST_HEAD(clocks);
-static DECLARE_MUTEX(clocks_sem);
-
struct clk *clk_get (struct device *dev, const char *id)
{
- struct clk *p;
- struct clk *clk = ERR_PTR(-ENOENT);
-
- down (&clocks_sem);
- list_for_each_entry (p, &clocks, node) {
- if (strcmp (id, p->name) == 0
- && try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- up (&clocks_sem);
-
- return clk;
+ return dev && strcmp(dev_name(dev), "cldc-lh7a40x") == 0
+ ? NULL : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);
void clk_put (struct clk *clk)
{
- module_put(clk->owner);
}
EXPORT_SYMBOL(clk_put);
@@ -118,20 +95,9 @@ void clk_disable (struct clk *clk)
}
EXPORT_SYMBOL(clk_disable);
-int clk_use (struct clk *clk)
-{
- return 0;
-}
-EXPORT_SYMBOL(clk_use);
-
-void clk_unuse (struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_unuse);
-
unsigned long clk_get_rate (struct clk *clk)
{
- return clk->rate;
+ return 0;
}
EXPORT_SYMBOL(clk_get_rate);
@@ -143,56 +109,6 @@ EXPORT_SYMBOL(clk_round_rate);
int clk_set_rate (struct clk *clk, unsigned long rate)
{
- int ret = -EIO;
- return ret;
+ return -EIO;
}
EXPORT_SYMBOL(clk_set_rate);
-
-#if 0
-/*
- * These are fixed clocks.
- */
-static struct clk kmi_clk = {
- .name = "KMIREFCLK",
- .rate = 24000000,
-};
-
-static struct clk uart_clk = {
- .name = "UARTCLK",
- .rate = 24000000,
-};
-
-static struct clk mmci_clk = {
- .name = "MCLK",
- .rate = 33000000,
-};
-#endif
-
-static struct clk clcd_clk = {
- .name = "CLCDCLK",
- .rate = 0,
-};
-
-int clk_register (struct clk *clk)
-{
- down (&clocks_sem);
- list_add (&clk->node, &clocks);
- up (&clocks_sem);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister (struct clk *clk)
-{
- down (&clocks_sem);
- list_del (&clk->node);
- up (&clocks_sem);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init (void)
-{
- clk_register(&clcd_clk);
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-lh7a40x/include/mach/io.h b/arch/arm/mach-lh7a40x/include/mach/io.h
index 031d26f9163c..6ece45911cbc 100644
--- a/arch/arm/mach-lh7a40x/include/mach/io.h
+++ b/arch/arm/mach-lh7a40x/include/mach/io.h
@@ -11,12 +11,10 @@
#ifndef __ASM_ARCH_IO_H
#define __ASM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
/* No ISA or PCI bus on this machine. */
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif /* __ASM_ARCH_IO_H */
diff --git a/arch/arm/mach-lh7a40x/include/mach/memory.h b/arch/arm/mach-lh7a40x/include/mach/memory.h
index 1da14ff66c93..189d20e543e7 100644
--- a/arch/arm/mach-lh7a40x/include/mach/memory.h
+++ b/arch/arm/mach-lh7a40x/include/mach/memory.h
@@ -19,16 +19,6 @@
*/
#define PHYS_OFFSET UL(0xc0000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#ifdef CONFIG_DISCONTIGMEM
/*
diff --git a/arch/arm/mach-loki/include/mach/dma.h b/arch/arm/mach-loki/include/mach/dma.h
deleted file mode 100644
index 40a8c178f10d..000000000000
--- a/arch/arm/mach-loki/include/mach/dma.h
+++ /dev/null
@@ -1 +0,0 @@
-/* empty */
diff --git a/arch/arm/mach-loki/include/mach/memory.h b/arch/arm/mach-loki/include/mach/memory.h
index a39533ab489d..2ed7e6e732c2 100644
--- a/arch/arm/mach-loki/include/mach/memory.h
+++ b/arch/arm/mach-loki/include/mach/memory.h
@@ -7,8 +7,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/io.h b/arch/arm/mach-msm/include/mach/io.h
index c6a2feb268b0..aab964591db4 100644
--- a/arch/arm/mach-msm/include/mach/io.h
+++ b/arch/arm/mach-msm/include/mach/io.h
@@ -23,11 +23,7 @@
void __iomem *__msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype);
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-#define __io(a) __io(a)
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index 63fd47f2e62e..f4698baec976 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -19,9 +19,5 @@
/* physical offset of RAM */
#define PHYS_OFFSET UL(0x10000000)
-/* bus address and physical addresses are identical */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c
index 238a2f8c2d52..b0e4e0d8f506 100644
--- a/arch/arm/mach-mv78xx0/common.c
+++ b/arch/arm/mach-mv78xx0/common.c
@@ -167,6 +167,7 @@ void __init mv78xx0_map_io(void)
****************************************************************************/
static struct orion_ehci_data mv78xx0_ehci_data = {
.dram = &mv78xx0_mbus_dram_info,
+ .phy_version = EHCI_PHY_NA,
};
static u64 ehci_dmamask = 0xffffffffUL;
diff --git a/arch/arm/mach-mv78xx0/include/mach/dma.h b/arch/arm/mach-mv78xx0/include/mach/dma.h
deleted file mode 100644
index 40a8c178f10d..000000000000
--- a/arch/arm/mach-mv78xx0/include/mach/dma.h
+++ /dev/null
@@ -1 +0,0 @@
-/* empty */
diff --git a/arch/arm/mach-mv78xx0/include/mach/gpio.h b/arch/arm/mach-mv78xx0/include/mach/gpio.h
new file mode 100644
index 000000000000..d9d1535ea100
--- /dev/null
+++ b/arch/arm/mach-mv78xx0/include/mach/gpio.h
@@ -0,0 +1,40 @@
+/*
+ * arch/asm-arm/mach-mv78xx0/include/mach/gpio.h
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ASM_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H
+
+#include <mach/irqs.h>
+#include <plat/gpio.h>
+#include <asm-generic/gpio.h> /* cansleep wrappers */
+
+extern int mv78xx0_core_index(void);
+
+#define GPIO_MAX 32
+#define GPIO_OUT(pin) (DEV_BUS_VIRT_BASE + 0x0100)
+#define GPIO_IO_CONF(pin) (DEV_BUS_VIRT_BASE + 0x0104)
+#define GPIO_BLINK_EN(pin) (DEV_BUS_VIRT_BASE + 0x0108)
+#define GPIO_IN_POL(pin) (DEV_BUS_VIRT_BASE + 0x010c)
+#define GPIO_DATA_IN(pin) (DEV_BUS_VIRT_BASE + 0x0110)
+#define GPIO_EDGE_CAUSE(pin) (DEV_BUS_VIRT_BASE + 0x0114)
+#define GPIO_MASK_OFF (mv78xx0_core_index() ? 0x18 : 0)
+#define GPIO_EDGE_MASK(pin) (DEV_BUS_VIRT_BASE + 0x0118 + GPIO_MASK_OFF)
+#define GPIO_LEVEL_MASK(pin) (DEV_BUS_VIRT_BASE + 0x011c + GPIO_MASK_OFF)
+
+static inline int gpio_to_irq(int pin)
+{
+ return pin + IRQ_MV78XX0_GPIO_START;
+}
+
+static inline int irq_to_gpio(int irq)
+{
+ return irq - IRQ_MV78XX0_GPIO_START;
+}
+
+
+#endif
diff --git a/arch/arm/mach-mv78xx0/include/mach/irqs.h b/arch/arm/mach-mv78xx0/include/mach/irqs.h
index bebc330281ec..fa1d422196c2 100644
--- a/arch/arm/mach-mv78xx0/include/mach/irqs.h
+++ b/arch/arm/mach-mv78xx0/include/mach/irqs.h
@@ -11,8 +11,6 @@
#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H
-#include "mv78xx0.h" /* need GPIO_MAX */
-
/*
* MV78xx0 Low Interrupt Controller
*/
@@ -88,7 +86,7 @@
* MV78XX0 General Purpose Pins
*/
#define IRQ_MV78XX0_GPIO_START 96
-#define NR_GPIO_IRQS GPIO_MAX
+#define NR_GPIO_IRQS 32
#define NR_IRQS (IRQ_MV78XX0_GPIO_START + NR_GPIO_IRQS)
diff --git a/arch/arm/mach-mv78xx0/include/mach/memory.h b/arch/arm/mach-mv78xx0/include/mach/memory.h
index 9e47a140ff7a..e663042d307f 100644
--- a/arch/arm/mach-mv78xx0/include/mach/memory.h
+++ b/arch/arm/mach-mv78xx0/include/mach/memory.h
@@ -7,8 +7,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-
#endif
diff --git a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
index ee9c5593ee92..e930ea5330a2 100644
--- a/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
+++ b/arch/arm/mach-mv78xx0/include/mach/mv78xx0.h
@@ -122,7 +122,4 @@
#define SATA_PHYS_BASE (MV78XX0_REGS_PHYS_BASE | 0xa0000)
-#define GPIO_MAX 32
-
-
#endif
diff --git a/arch/arm/mach-mv78xx0/irq.c b/arch/arm/mach-mv78xx0/irq.c
index 503e5d195ae5..e273418797b4 100644
--- a/arch/arm/mach-mv78xx0/irq.c
+++ b/arch/arm/mach-mv78xx0/irq.c
@@ -11,13 +11,42 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/irq.h>
+#include <asm/gpio.h>
#include <mach/mv78xx0.h>
#include <plat/irq.h>
#include "common.h"
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ BUG_ON(irq < IRQ_MV78XX0_GPIO_0_7 || irq > IRQ_MV78XX0_GPIO_24_31);
+
+ orion_gpio_irq_handler((irq - IRQ_MV78XX0_GPIO_0_7) << 3);
+}
+
void __init mv78xx0_init_irq(void)
{
+ int i;
+
orion_irq_init(0, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_LOW_OFF));
orion_irq_init(32, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_HIGH_OFF));
orion_irq_init(64, (void __iomem *)(IRQ_VIRT_BASE + IRQ_MASK_ERR_OFF));
+
+ /*
+ * Mask and clear GPIO IRQ interrupts.
+ */
+ writel(0, GPIO_LEVEL_MASK(0));
+ writel(0, GPIO_EDGE_MASK(0));
+ writel(0, GPIO_EDGE_CAUSE(0));
+
+ for (i = IRQ_MV78XX0_GPIO_START; i < NR_IRQS; i++) {
+ set_irq_chip(i, &orion_gpio_irq_level_chip);
+ set_irq_handler(i, handle_level_irq);
+ irq_desc[i].status |= IRQ_LEVEL;
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_chained_handler(IRQ_MV78XX0_GPIO_0_7, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_MV78XX0_GPIO_8_15, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_MV78XX0_GPIO_16_23, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_MV78XX0_GPIO_24_31, gpio_irq_handler);
}
diff --git a/arch/arm/mach-mx1/Kconfig b/arch/arm/mach-mx1/Kconfig
new file mode 100644
index 000000000000..2b59fc74784f
--- /dev/null
+++ b/arch/arm/mach-mx1/Kconfig
@@ -0,0 +1,14 @@
+if ARCH_MX1
+
+comment "MX1 Platforms"
+
+config MACH_MXLADS
+ bool
+
+config ARCH_MX1ADS
+ bool "MX1ADS platform"
+ select MACH_MXLADS
+ help
+ Say Y here if you are using Motorola MX1ADS/MXLADS boards
+
+endif
diff --git a/arch/arm/mach-mx1/Makefile b/arch/arm/mach-mx1/Makefile
new file mode 100644
index 000000000000..b969719011fa
--- /dev/null
+++ b/arch/arm/mach-mx1/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y += generic.o clock.o devices.o
+
+# Specific board support
+obj-$(CONFIG_ARCH_MX1ADS) += mx1ads.o
diff --git a/arch/arm/mach-mx1/Makefile.boot b/arch/arm/mach-mx1/Makefile.boot
new file mode 100644
index 000000000000..8ed1492288a2
--- /dev/null
+++ b/arch/arm/mach-mx1/Makefile.boot
@@ -0,0 +1,4 @@
+ zreladdr-y := 0x08008000
+params_phys-y := 0x08000100
+initrd_phys-y := 0x08800000
+
diff --git a/arch/arm/mach-mx1/clock.c b/arch/arm/mach-mx1/clock.c
new file mode 100644
index 000000000000..4bcd1ece55f5
--- /dev/null
+++ b/arch/arm/mach-mx1/clock.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/math64.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include "crm_regs.h"
+
+static int _clk_enable(struct clk *clk)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_can_use_parent(const struct clk *clk_arr[], unsigned int size,
+ struct clk *parent)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (parent == clk_arr[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static unsigned long
+_clk_simple_round_rate(struct clk *clk, unsigned long rate, unsigned int limit)
+{
+ int div;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+ if (parent_rate % rate)
+ div++;
+
+ if (div > limit)
+ div = limit;
+
+ return parent_rate / div;
+}
+
+static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk->parent->round_rate(clk->parent, rate);
+}
+
+static int _clk_parent_set_rate(struct clk *clk, unsigned long rate)
+{
+ return clk->parent->set_rate(clk->parent, rate);
+}
+
+/*
+ * get the system pll clock in Hz
+ *
+ * mfi + mfn / (mfd +1)
+ * f = 2 * f_ref * --------------------
+ * pd + 1
+ */
+static unsigned long mx1_decode_pll(unsigned int pll, u32 f_ref)
+{
+ unsigned long long ll;
+ unsigned long quot;
+
+ u32 mfi = (pll >> 10) & 0xf;
+ u32 mfn = pll & 0x3ff;
+ u32 mfd = (pll >> 16) & 0x3ff;
+ u32 pd = (pll >> 26) & 0xf;
+
+ mfi = mfi <= 5 ? 5 : mfi;
+
+ ll = 2 * (unsigned long long)f_ref *
+ ((mfi << 16) + (mfn << 16) / (mfd + 1));
+ quot = (pd + 1) * (1 << 16);
+ ll += quot / 2;
+ do_div(ll, quot);
+ return (unsigned long)ll;
+}
+
+static unsigned long clk16m_get_rate(struct clk *clk)
+{
+ return 16000000;
+}
+
+static struct clk clk16m = {
+ .name = "CLK16M",
+ .get_rate = clk16m_get_rate,
+ .enable = _clk_enable,
+ .enable_reg = CCM_CSCR,
+ .enable_shift = CCM_CSCR_OSC_EN_SHIFT,
+ .disable = _clk_disable,
+};
+
+/* in Hz */
+static unsigned long clk32_rate;
+
+static unsigned long clk32_get_rate(struct clk *clk)
+{
+ return clk32_rate;
+}
+
+static struct clk clk32 = {
+ .name = "CLK32",
+ .get_rate = clk32_get_rate,
+};
+
+static unsigned long clk32_premult_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) * 512;
+}
+
+static struct clk clk32_premult = {
+ .name = "CLK32_premultiplier",
+ .parent = &clk32,
+ .get_rate = clk32_premult_get_rate,
+};
+
+static const struct clk *prem_clk_clocks[] = {
+ &clk32_premult,
+ &clk16m,
+};
+
+static int prem_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int i;
+ unsigned int reg = __raw_readl(CCM_CSCR);
+
+ i = _clk_can_use_parent(prem_clk_clocks, ARRAY_SIZE(prem_clk_clocks),
+ parent);
+
+ switch (i) {
+ case 0:
+ reg &= ~CCM_CSCR_SYSTEM_SEL;
+ break;
+ case 1:
+ reg |= CCM_CSCR_SYSTEM_SEL;
+ break;
+ default:
+ return i;
+ }
+
+ __raw_writel(reg, CCM_CSCR);
+
+ return 0;
+}
+
+static struct clk prem_clk = {
+ .name = "prem_clk",
+ .set_parent = prem_clk_set_parent,
+};
+
+static unsigned long system_clk_get_rate(struct clk *clk)
+{
+ return mx1_decode_pll(__raw_readl(CCM_SPCTL0),
+ clk_get_rate(clk->parent));
+}
+
+static struct clk system_clk = {
+ .name = "system_clk",
+ .parent = &prem_clk,
+ .get_rate = system_clk_get_rate,
+};
+
+static unsigned long mcu_clk_get_rate(struct clk *clk)
+{
+ return mx1_decode_pll(__raw_readl(CCM_MPCTL0),
+ clk_get_rate(clk->parent));
+}
+
+static struct clk mcu_clk = {
+ .name = "mcu_clk",
+ .parent = &clk32_premult,
+ .get_rate = mcu_clk_get_rate,
+};
+
+static unsigned long fclk_get_rate(struct clk *clk)
+{
+ unsigned long fclk = clk_get_rate(clk->parent);
+
+ if (__raw_readl(CCM_CSCR) & CCM_CSCR_PRESC)
+ fclk /= 2;
+
+ return fclk;
+}
+
+static struct clk fclk = {
+ .name = "fclk",
+ .parent = &mcu_clk,
+ .get_rate = fclk_get_rate,
+};
+
+/*
+ * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
+ */
+static unsigned long hclk_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / (((__raw_readl(CCM_CSCR) &
+ CCM_CSCR_BCLK_MASK) >> CCM_CSCR_BCLK_OFFSET) + 1);
+}
+
+static unsigned long hclk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return _clk_simple_round_rate(clk, rate, 16);
+}
+
+static int hclk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int div;
+ unsigned int reg;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+
+ if (div > 16 || div < 1 || ((parent_rate / div) != rate))
+ return -EINVAL;
+
+ div--;
+
+ reg = __raw_readl(CCM_CSCR);
+ reg &= ~CCM_CSCR_BCLK_MASK;
+ reg |= div << CCM_CSCR_BCLK_OFFSET;
+ __raw_writel(reg, CCM_CSCR);
+
+ return 0;
+}
+
+static struct clk hclk = {
+ .name = "hclk",
+ .parent = &system_clk,
+ .get_rate = hclk_get_rate,
+ .round_rate = hclk_round_rate,
+ .set_rate = hclk_set_rate,
+};
+
+static unsigned long clk48m_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / (((__raw_readl(CCM_CSCR) &
+ CCM_CSCR_USB_MASK) >> CCM_CSCR_USB_OFFSET) + 1);
+}
+
+static unsigned long clk48m_round_rate(struct clk *clk, unsigned long rate)
+{
+ return _clk_simple_round_rate(clk, rate, 8);
+}
+
+static int clk48m_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int div;
+ unsigned int reg;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+
+ if (div > 8 || div < 1 || ((parent_rate / div) != rate))
+ return -EINVAL;
+
+ div--;
+
+ reg = __raw_readl(CCM_CSCR);
+ reg &= ~CCM_CSCR_USB_MASK;
+ reg |= div << CCM_CSCR_USB_OFFSET;
+ __raw_writel(reg, CCM_CSCR);
+
+ return 0;
+}
+
+static struct clk clk48m = {
+ .name = "CLK48M",
+ .parent = &system_clk,
+ .get_rate = clk48m_get_rate,
+ .round_rate = clk48m_round_rate,
+ .set_rate = clk48m_set_rate,
+};
+
+/*
+ * get peripheral clock 1 ( UART[12], Timer[12], PWM )
+ */
+static unsigned long perclk1_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
+ CCM_PCDR_PCLK1_MASK) >> CCM_PCDR_PCLK1_OFFSET) + 1);
+}
+
+static unsigned long perclk1_round_rate(struct clk *clk, unsigned long rate)
+{
+ return _clk_simple_round_rate(clk, rate, 16);
+}
+
+static int perclk1_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int div;
+ unsigned int reg;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+
+ if (div > 16 || div < 1 || ((parent_rate / div) != rate))
+ return -EINVAL;
+
+ div--;
+
+ reg = __raw_readl(CCM_PCDR);
+ reg &= ~CCM_PCDR_PCLK1_MASK;
+ reg |= div << CCM_PCDR_PCLK1_OFFSET;
+ __raw_writel(reg, CCM_PCDR);
+
+ return 0;
+}
+
+/*
+ * get peripheral clock 2 ( LCD, SD, SPI[12] )
+ */
+static unsigned long perclk2_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
+ CCM_PCDR_PCLK2_MASK) >> CCM_PCDR_PCLK2_OFFSET) + 1);
+}
+
+static unsigned long perclk2_round_rate(struct clk *clk, unsigned long rate)
+{
+ return _clk_simple_round_rate(clk, rate, 16);
+}
+
+static int perclk2_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int div;
+ unsigned int reg;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+
+ if (div > 16 || div < 1 || ((parent_rate / div) != rate))
+ return -EINVAL;
+
+ div--;
+
+ reg = __raw_readl(CCM_PCDR);
+ reg &= ~CCM_PCDR_PCLK2_MASK;
+ reg |= div << CCM_PCDR_PCLK2_OFFSET;
+ __raw_writel(reg, CCM_PCDR);
+
+ return 0;
+}
+
+/*
+ * get peripheral clock 3 ( SSI )
+ */
+static unsigned long perclk3_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / (((__raw_readl(CCM_PCDR) &
+ CCM_PCDR_PCLK3_MASK) >> CCM_PCDR_PCLK3_OFFSET) + 1);
+}
+
+static unsigned long perclk3_round_rate(struct clk *clk, unsigned long rate)
+{
+ return _clk_simple_round_rate(clk, rate, 128);
+}
+
+static int perclk3_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int div;
+ unsigned int reg;
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(clk->parent);
+
+ div = parent_rate / rate;
+
+ if (div > 128 || div < 1 || ((parent_rate / div) != rate))
+ return -EINVAL;
+
+ div--;
+
+ reg = __raw_readl(CCM_PCDR);
+ reg &= ~CCM_PCDR_PCLK3_MASK;
+ reg |= div << CCM_PCDR_PCLK3_OFFSET;
+ __raw_writel(reg, CCM_PCDR);
+
+ return 0;
+}
+
+static struct clk perclk[] = {
+ {
+ .name = "perclk",
+ .id = 0,
+ .parent = &system_clk,
+ .get_rate = perclk1_get_rate,
+ .round_rate = perclk1_round_rate,
+ .set_rate = perclk1_set_rate,
+ }, {
+ .name = "perclk",
+ .id = 1,
+ .parent = &system_clk,
+ .get_rate = perclk2_get_rate,
+ .round_rate = perclk2_round_rate,
+ .set_rate = perclk2_set_rate,
+ }, {
+ .name = "perclk",
+ .id = 2,
+ .parent = &system_clk,
+ .get_rate = perclk3_get_rate,
+ .round_rate = perclk3_round_rate,
+ .set_rate = perclk3_set_rate,
+ }
+};
+
+static const struct clk *clko_clocks[] = {
+ &perclk[0],
+ &hclk,
+ &clk48m,
+ &clk16m,
+ &prem_clk,
+ &fclk,
+};
+
+static int clko_set_parent(struct clk *clk, struct clk *parent)
+{
+ int i;
+ unsigned int reg;
+
+ i = _clk_can_use_parent(clko_clocks, ARRAY_SIZE(clko_clocks), parent);
+ if (i < 0)
+ return i;
+
+ reg = __raw_readl(CCM_CSCR) & ~CCM_CSCR_CLKO_MASK;
+ reg |= i << CCM_CSCR_CLKO_OFFSET;
+ __raw_writel(reg, CCM_CSCR);
+
+ if (clko_clocks[i]->set_rate && clko_clocks[i]->round_rate) {
+ clk->set_rate = _clk_parent_set_rate;
+ clk->round_rate = _clk_parent_round_rate;
+ } else {
+ clk->set_rate = NULL;
+ clk->round_rate = NULL;
+ }
+
+ return 0;
+}
+
+static struct clk clko_clk = {
+ .name = "clko_clk",
+ .set_parent = clko_set_parent,
+};
+
+static struct clk dma_clk = {
+ .name = "dma_clk",
+ .parent = &hclk,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = SCM_GCCR,
+ .enable_shift = SCM_GCCR_DMA_CLK_EN_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk csi_clk = {
+ .name = "csi_clk",
+ .parent = &hclk,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = SCM_GCCR,
+ .enable_shift = SCM_GCCR_CSI_CLK_EN_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk mma_clk = {
+ .name = "mma_clk",
+ .parent = &hclk,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = SCM_GCCR,
+ .enable_shift = SCM_GCCR_MMA_CLK_EN_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk usbd_clk = {
+ .name = "usbd_clk",
+ .parent = &clk48m,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = SCM_GCCR,
+ .enable_shift = SCM_GCCR_USBD_CLK_EN_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk gpt_clk = {
+ .name = "gpt_clk",
+ .parent = &perclk[0],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk uart_clk = {
+ .name = "uart_clk",
+ .parent = &perclk[0],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk i2c_clk = {
+ .name = "i2c_clk",
+ .parent = &hclk,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk spi_clk = {
+ .name = "spi_clk",
+ .parent = &perclk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk sdhc_clk = {
+ .name = "sdhc_clk",
+ .parent = &perclk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk lcdc_clk = {
+ .name = "lcdc_clk",
+ .parent = &perclk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk mshc_clk = {
+ .name = "mshc_clk",
+ .parent = &hclk,
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk ssi_clk = {
+ .name = "ssi_clk",
+ .parent = &perclk[2],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,
+};
+
+static struct clk rtc_clk = {
+ .name = "rtc_clk",
+ .parent = &clk32,
+};
+
+static struct clk *mxc_clks[] = {
+ &clk16m,
+ &clk32,
+ &clk32_premult,
+ &prem_clk,
+ &system_clk,
+ &mcu_clk,
+ &fclk,
+ &hclk,
+ &clk48m,
+ &perclk[0],
+ &perclk[1],
+ &perclk[2],
+ &clko_clk,
+ &dma_clk,
+ &csi_clk,
+ &mma_clk,
+ &usbd_clk,
+ &gpt_clk,
+ &uart_clk,
+ &i2c_clk,
+ &spi_clk,
+ &sdhc_clk,
+ &lcdc_clk,
+ &mshc_clk,
+ &ssi_clk,
+ &rtc_clk,
+};
+
+int __init mxc_clocks_init(unsigned long fref)
+{
+ struct clk **clkp;
+ unsigned int reg;
+
+ /* disable clocks we are able to */
+ __raw_writel(0, SCM_GCCR);
+
+ clk32_rate = fref;
+ reg = __raw_readl(CCM_CSCR);
+
+ /* detect clock reference for system PLL */
+ if (reg & CCM_CSCR_SYSTEM_SEL) {
+ prem_clk.parent = &clk16m;
+ } else {
+ /* ensure that oscillator is disabled */
+ reg &= ~(1 << CCM_CSCR_OSC_EN_SHIFT);
+ __raw_writel(reg, CCM_CSCR);
+ prem_clk.parent = &clk32_premult;
+ }
+
+ /* detect reference for CLKO */
+ reg = (reg & CCM_CSCR_CLKO_MASK) >> CCM_CSCR_CLKO_OFFSET;
+ clko_clk.parent = (struct clk *)clko_clocks[reg];
+
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
+ clk_register(*clkp);
+
+ clk_enable(&hclk);
+ clk_enable(&fclk);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx1/crm_regs.h b/arch/arm/mach-mx1/crm_regs.h
new file mode 100644
index 000000000000..22e866ff0c09
--- /dev/null
+++ b/arch/arm/mach-mx1/crm_regs.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (c) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#ifndef __ARCH_ARM_MACH_MX1_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX1_CRM_REGS_H__
+
+#define CCM_BASE IO_ADDRESS(CCM_BASE_ADDR)
+#define SCM_BASE IO_ADDRESS(SCM_BASE_ADDR)
+
+/* CCM register addresses */
+#define CCM_CSCR (CCM_BASE + 0x0)
+#define CCM_MPCTL0 (CCM_BASE + 0x4)
+#define CCM_MPCTL1 (CCM_BASE + 0x8)
+#define CCM_SPCTL0 (CCM_BASE + 0xC)
+#define CCM_SPCTL1 (CCM_BASE + 0x10)
+#define CCM_PCDR (CCM_BASE + 0x20)
+
+#define CCM_CSCR_CLKO_OFFSET 29
+#define CCM_CSCR_CLKO_MASK (0x7 << 29)
+#define CCM_CSCR_USB_OFFSET 26
+#define CCM_CSCR_USB_MASK (0x7 << 26)
+#define CCM_CSCR_SPLL_RESTART (1 << 22)
+#define CCM_CSCR_MPLL_RESTART (1 << 21)
+#define CCM_CSCR_OSC_EN_SHIFT 17
+#define CCM_CSCR_SYSTEM_SEL (1 << 16)
+#define CCM_CSCR_BCLK_OFFSET 10
+#define CCM_CSCR_BCLK_MASK (0xF << 10)
+#define CCM_CSCR_PRESC (1 << 15)
+#define CCM_CSCR_SPEN (1 << 1)
+#define CCM_CSCR_MPEN (1 << 0)
+
+#define CCM_PCDR_PCLK3_OFFSET 16
+#define CCM_PCDR_PCLK3_MASK (0x7F << 16)
+#define CCM_PCDR_PCLK2_OFFSET 4
+#define CCM_PCDR_PCLK2_MASK (0xF << 4)
+#define CCM_PCDR_PCLK1_OFFSET 0
+#define CCM_PCDR_PCLK1_MASK 0xF
+
+/* SCM register addresses */
+#define SCM_SIDR (SCM_BASE + 0x0)
+#define SCM_FMCR (SCM_BASE + 0x4)
+#define SCM_GPCR (SCM_BASE + 0x8)
+#define SCM_GCCR (SCM_BASE + 0xC)
+
+#define SCM_GCCR_DMA_CLK_EN_OFFSET 3
+#define SCM_GCCR_CSI_CLK_EN_OFFSET 2
+#define SCM_GCCR_MMA_CLK_EN_OFFSET 1
+#define SCM_GCCR_USBD_CLK_EN_OFFSET 0
+
+#endif /* __ARCH_ARM_MACH_MX2_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx1/devices.c b/arch/arm/mach-mx1/devices.c
new file mode 100644
index 000000000000..686d8d2dbb24
--- /dev/null
+++ b/arch/arm/mach-mx1/devices.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
+ * Copyright (c) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (c) 2008 Darius Augulis <darius.augulis@teltonika.lt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <mach/hardware.h>
+
+static struct resource imx_csi_resources[] = {
+ [0] = {
+ .start = 0x00224000,
+ .end = 0x00224010,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = CSI_INT,
+ .end = CSI_INT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 imx_csi_dmamask = 0xffffffffUL;
+
+struct platform_device imx_csi_device = {
+ .name = "imx-csi",
+ .id = 0, /* This is used to put cameras on this interface */
+ .dev = {
+ .dma_mask = &imx_csi_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = imx_csi_resources,
+ .num_resources = ARRAY_SIZE(imx_csi_resources),
+};
+
+static struct resource imx_i2c_resources[] = {
+ [0] = {
+ .start = 0x00217000,
+ .end = 0x00217010,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = I2C_INT,
+ .end = I2C_INT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_i2c_device = {
+ .name = "imx-i2c",
+ .id = 0,
+ .resource = imx_i2c_resources,
+ .num_resources = ARRAY_SIZE(imx_i2c_resources),
+};
+
+static struct resource imx_uart1_resources[] = {
+ [0] = {
+ .start = UART1_BASE_ADDR,
+ .end = UART1_BASE_ADDR + 0xD0,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = UART1_MINT_RX,
+ .end = UART1_MINT_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = UART1_MINT_TX,
+ .end = UART1_MINT_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = UART1_MINT_RTS,
+ .end = UART1_MINT_RTS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_uart1_device = {
+ .name = "imx-uart",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(imx_uart1_resources),
+ .resource = imx_uart1_resources,
+};
+
+static struct resource imx_uart2_resources[] = {
+ [0] = {
+ .start = UART2_BASE_ADDR,
+ .end = UART2_BASE_ADDR + 0xD0,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = UART2_MINT_RX,
+ .end = UART2_MINT_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = UART2_MINT_TX,
+ .end = UART2_MINT_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = UART2_MINT_RTS,
+ .end = UART2_MINT_RTS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_uart2_device = {
+ .name = "imx-uart",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(imx_uart2_resources),
+ .resource = imx_uart2_resources,
+};
+
+static struct resource imx_rtc_resources[] = {
+ [0] = {
+ .start = 0x00204000,
+ .end = 0x00204024,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = RTC_INT,
+ .end = RTC_INT,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = RTC_SAMINT,
+ .end = RTC_SAMINT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_rtc_device = {
+ .name = "rtc-imx",
+ .id = 0,
+ .resource = imx_rtc_resources,
+ .num_resources = ARRAY_SIZE(imx_rtc_resources),
+};
+
+static struct resource imx_wdt_resources[] = {
+ [0] = {
+ .start = 0x00201000,
+ .end = 0x00201008,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = WDT_INT,
+ .end = WDT_INT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_wdt_device = {
+ .name = "imx-wdt",
+ .id = 0,
+ .resource = imx_wdt_resources,
+ .num_resources = ARRAY_SIZE(imx_wdt_resources),
+};
+
+static struct resource imx_usb_resources[] = {
+ [0] = {
+ .start = 0x00212000,
+ .end = 0x00212148,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = USBD_INT0,
+ .end = USBD_INT0,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = USBD_INT1,
+ .end = USBD_INT1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = USBD_INT2,
+ .end = USBD_INT2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [4] = {
+ .start = USBD_INT3,
+ .end = USBD_INT3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [5] = {
+ .start = USBD_INT4,
+ .end = USBD_INT4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [6] = {
+ .start = USBD_INT5,
+ .end = USBD_INT5,
+ .flags = IORESOURCE_IRQ,
+ },
+ [7] = {
+ .start = USBD_INT6,
+ .end = USBD_INT6,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_usb_device = {
+ .name = "imx_udc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(imx_usb_resources),
+ .resource = imx_usb_resources,
+};
+
+/* GPIO port description */
+static struct mxc_gpio_port imx_gpio_ports[] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR),
+ .irq = GPIO_INT_PORTA,
+ .virtual_irq_start = MXC_GPIO_IRQ_START
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x100),
+ .irq = GPIO_INT_PORTB,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x200),
+ .irq = GPIO_INT_PORTC,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64
+ },
+ [3] = {
+ .chip.label = "gpio-3",
+ .base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x300),
+ .irq = GPIO_INT_PORTD,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 96
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
+}
diff --git a/arch/arm/mach-mx1/devices.h b/arch/arm/mach-mx1/devices.h
new file mode 100644
index 000000000000..0da5d7cce3a2
--- /dev/null
+++ b/arch/arm/mach-mx1/devices.h
@@ -0,0 +1,7 @@
+extern struct platform_device imx_csi_device;
+extern struct platform_device imx_i2c_device;
+extern struct platform_device imx_uart1_device;
+extern struct platform_device imx_uart2_device;
+extern struct platform_device imx_rtc_device;
+extern struct platform_device imx_wdt_device;
+extern struct platform_device imx_usb_device;
diff --git a/arch/arm/mach-mx1/generic.c b/arch/arm/mach-mx1/generic.c
new file mode 100644
index 000000000000..0dec6f300ffc
--- /dev/null
+++ b/arch/arm/mach-mx1/generic.c
@@ -0,0 +1,43 @@
+/*
+ * author: Sascha Hauer
+ * Created: april 20th, 2004
+ * Copyright: Synertronixx GmbH
+ *
+ * Common code for i.MX machines
+ *
+ * This program is free software; you can redistribute 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 <asm/mach/map.h>
+
+#include <mach/hardware.h>
+
+static struct map_desc imx_io_desc[] __initdata = {
+ {
+ .virtual = IMX_IO_BASE,
+ .pfn = __phys_to_pfn(IMX_IO_PHYS),
+ .length = IMX_IO_SIZE,
+ .type = MT_DEVICE
+ }
+};
+
+void __init mxc_map_io(void)
+{
+ iotable_init(imx_io_desc, ARRAY_SIZE(imx_io_desc));
+}
diff --git a/arch/arm/mach-mx1/mx1ads.c b/arch/arm/mach-mx1/mx1ads.c
new file mode 100644
index 000000000000..2e4b185fe4a9
--- /dev/null
+++ b/arch/arm/mach-mx1/mx1ads.c
@@ -0,0 +1,148 @@
+/*
+ * arch/arm/mach-imx/mx1ads.c
+ *
+ * Initially based on:
+ * linux-2.6.7-imx/arch/arm/mach-imx/scb9328.c
+ * Copyright (c) 2004 Sascha Hauer <sascha@saschahauer.de>
+ *
+ * 2004 (c) MontaVista Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+#include <mach/hardware.h>
+#include <mach/common.h>
+#include <mach/imx-uart.h>
+#include <mach/iomux-mx1-mx2.h>
+#include "devices.h"
+
+/*
+ * UARTs platform data
+ */
+static int mxc_uart1_pins[] = {
+ PC9_PF_UART1_CTS,
+ PC10_PF_UART1_RTS,
+ PC11_PF_UART1_TXD,
+ PC12_PF_UART1_RXD,
+};
+
+static int uart1_mxc_init(struct platform_device *pdev)
+{
+ return mxc_gpio_setup_multiple_pins(mxc_uart1_pins,
+ ARRAY_SIZE(mxc_uart1_pins), "UART1");
+}
+
+static int uart1_mxc_exit(struct platform_device *pdev)
+{
+ mxc_gpio_release_multiple_pins(mxc_uart1_pins,
+ ARRAY_SIZE(mxc_uart1_pins));
+ return 0;
+}
+
+static int mxc_uart2_pins[] = {
+ PB28_PF_UART2_CTS,
+ PB29_PF_UART2_RTS,
+ PB30_PF_UART2_TXD,
+ PB31_PF_UART2_RXD,
+};
+
+static int uart2_mxc_init(struct platform_device *pdev)
+{
+ return mxc_gpio_setup_multiple_pins(mxc_uart2_pins,
+ ARRAY_SIZE(mxc_uart2_pins), "UART2");
+}
+
+static int uart2_mxc_exit(struct platform_device *pdev)
+{
+ mxc_gpio_release_multiple_pins(mxc_uart2_pins,
+ ARRAY_SIZE(mxc_uart2_pins));
+ return 0;
+}
+
+static struct imxuart_platform_data uart_pdata[] = {
+ {
+ .init = uart1_mxc_init,
+ .exit = uart1_mxc_exit,
+ .flags = IMXUART_HAVE_RTSCTS,
+ }, {
+ .init = uart2_mxc_init,
+ .exit = uart2_mxc_exit,
+ .flags = IMXUART_HAVE_RTSCTS,
+ },
+};
+
+/*
+ * Physmap flash
+ */
+
+static struct physmap_flash_data mx1ads_flash_data = {
+ .width = 4, /* bankwidth in bytes */
+};
+
+static struct resource flash_resource = {
+ .start = IMX_CS0_PHYS,
+ .end = IMX_CS0_PHYS + SZ_32M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = &flash_resource,
+ .num_resources = 1,
+};
+
+/*
+ * Board init
+ */
+static void __init mx1ads_init(void)
+{
+ /* UART */
+ mxc_register_device(&imx_uart1_device, &uart_pdata[0]);
+ mxc_register_device(&imx_uart2_device, &uart_pdata[1]);
+
+ /* Physmap flash */
+ mxc_register_device(&flash_device, &mx1ads_flash_data);
+}
+
+static void __init mx1ads_timer_init(void)
+{
+ mxc_clocks_init(32000);
+ mxc_timer_init("gpt_clk");
+}
+
+struct sys_timer mx1ads_timer = {
+ .init = mx1ads_timer_init,
+};
+
+MACHINE_START(MX1ADS, "Freescale MX1ADS")
+ /* Maintainer: Sascha Hauer, Pengutronix */
+ .phys_io = IMX_IO_PHYS,
+ .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .timer = &mx1ads_timer,
+ .init_machine = mx1ads_init,
+MACHINE_END
+
+MACHINE_START(MXLADS, "Freescale MXLADS")
+ .phys_io = IMX_IO_PHYS,
+ .io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .timer = &mx1ads_timer,
+ .init_machine = mx1ads_init,
+MACHINE_END
diff --git a/arch/arm/mach-mx2/devices.c b/arch/arm/mach-mx2/devices.c
index bd0559d5933e..af121f5ab710 100644
--- a/arch/arm/mach-mx2/devices.c
+++ b/arch/arm/mach-mx2/devices.c
@@ -190,38 +190,72 @@ struct platform_device mxc_wdt = {
.resource = mxc_wdt_resources,
};
+static struct resource mxc_w1_master_resources[] = {
+ {
+ .start = OWIRE_BASE_ADDR,
+ .end = OWIRE_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device mxc_w1_master_device = {
+ .name = "mxc_w1",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_w1_master_resources),
+ .resource = mxc_w1_master_resources,
+};
+
+static struct resource mxc_nand_resources[] = {
+ {
+ .start = NFC_BASE_ADDR,
+ .end = NFC_BASE_ADDR + 0xfff,
+ .flags = IORESOURCE_MEM
+ }, {
+ .start = MXC_INT_NANDFC,
+ .end = MXC_INT_NANDFC,
+ .flags = IORESOURCE_IRQ
+ },
+};
+
+struct platform_device mxc_nand_device = {
+ .name = "mxc_nand",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_nand_resources),
+ .resource = mxc_nand_resources,
+};
+
/* GPIO port description */
static struct mxc_gpio_port imx_gpio_ports[] = {
[0] = {
.chip.label = "gpio-0",
.irq = MXC_INT_GPIO,
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 0),
- .virtual_irq_start = MXC_MAX_INT_LINES,
+ .virtual_irq_start = MXC_GPIO_IRQ_START,
},
[1] = {
.chip.label = "gpio-1",
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 1),
- .virtual_irq_start = MXC_MAX_INT_LINES + 32,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
},
[2] = {
.chip.label = "gpio-2",
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 2),
- .virtual_irq_start = MXC_MAX_INT_LINES + 64,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
},
[3] = {
.chip.label = "gpio-3",
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 3),
- .virtual_irq_start = MXC_MAX_INT_LINES + 96,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 96,
},
[4] = {
.chip.label = "gpio-4",
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 4),
- .virtual_irq_start = MXC_MAX_INT_LINES + 128,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 128,
},
[5] = {
.chip.label = "gpio-5",
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 5),
- .virtual_irq_start = MXC_MAX_INT_LINES + 160,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 160,
}
};
diff --git a/arch/arm/mach-mx2/devices.h b/arch/arm/mach-mx2/devices.h
index c77a4b8f73b4..1e8cb577a642 100644
--- a/arch/arm/mach-mx2/devices.h
+++ b/arch/arm/mach-mx2/devices.h
@@ -12,4 +12,5 @@ extern struct platform_device mxc_uart_device2;
extern struct platform_device mxc_uart_device3;
extern struct platform_device mxc_uart_device4;
extern struct platform_device mxc_uart_device5;
-
+extern struct platform_device mxc_w1_master_device;
+extern struct platform_device mxc_nand_device;
diff --git a/arch/arm/mach-mx2/mx27ads.c b/arch/arm/mach-mx2/mx27ads.c
index 56e22d3ca075..2b5c67f54571 100644
--- a/arch/arm/mach-mx2/mx27ads.c
+++ b/arch/arm/mach-mx2/mx27ads.c
@@ -68,15 +68,14 @@ static int mxc_uart0_pins[] = {
static int uart_mxc_port0_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart0_pins,
- ARRAY_SIZE(mxc_uart0_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART0");
+ ARRAY_SIZE(mxc_uart0_pins), "UART0");
}
static int uart_mxc_port0_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart0_pins,
- ARRAY_SIZE(mxc_uart0_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART0");
+ mxc_gpio_release_multiple_pins(mxc_uart0_pins,
+ ARRAY_SIZE(mxc_uart0_pins));
+ return 0;
}
static int mxc_uart1_pins[] = {
@@ -89,15 +88,14 @@ static int mxc_uart1_pins[] = {
static int uart_mxc_port1_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart1_pins,
- ARRAY_SIZE(mxc_uart1_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART1");
+ ARRAY_SIZE(mxc_uart1_pins), "UART1");
}
static int uart_mxc_port1_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart1_pins,
- ARRAY_SIZE(mxc_uart1_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART1");
+ mxc_gpio_release_multiple_pins(mxc_uart1_pins,
+ ARRAY_SIZE(mxc_uart1_pins));
+ return 0;
}
static int mxc_uart2_pins[] = {
@@ -110,15 +108,14 @@ static int mxc_uart2_pins[] = {
static int uart_mxc_port2_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart2_pins,
- ARRAY_SIZE(mxc_uart2_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART2");
+ ARRAY_SIZE(mxc_uart2_pins), "UART2");
}
static int uart_mxc_port2_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart2_pins,
- ARRAY_SIZE(mxc_uart2_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART2");
+ mxc_gpio_release_multiple_pins(mxc_uart2_pins,
+ ARRAY_SIZE(mxc_uart2_pins));
+ return 0;
}
static int mxc_uart3_pins[] = {
@@ -131,15 +128,13 @@ static int mxc_uart3_pins[] = {
static int uart_mxc_port3_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart3_pins,
- ARRAY_SIZE(mxc_uart3_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART3");
+ ARRAY_SIZE(mxc_uart3_pins), "UART3");
}
static int uart_mxc_port3_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart3_pins,
- ARRAY_SIZE(mxc_uart3_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART3");
+ mxc_gpio_release_multiple_pins(mxc_uart3_pins,
+ ARRAY_SIZE(mxc_uart3_pins));
}
static int mxc_uart4_pins[] = {
@@ -152,15 +147,14 @@ static int mxc_uart4_pins[] = {
static int uart_mxc_port4_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart4_pins,
- ARRAY_SIZE(mxc_uart4_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART4");
+ ARRAY_SIZE(mxc_uart4_pins), "UART4");
}
static int uart_mxc_port4_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart4_pins,
- ARRAY_SIZE(mxc_uart4_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART4");
+ mxc_gpio_release_multiple_pins(mxc_uart4_pins,
+ ARRAY_SIZE(mxc_uart4_pins));
+ return 0;
}
static int mxc_uart5_pins[] = {
@@ -173,15 +167,14 @@ static int mxc_uart5_pins[] = {
static int uart_mxc_port5_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart5_pins,
- ARRAY_SIZE(mxc_uart5_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART5");
+ ARRAY_SIZE(mxc_uart5_pins), "UART5");
}
static int uart_mxc_port5_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart5_pins,
- ARRAY_SIZE(mxc_uart5_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART5");
+ mxc_gpio_release_multiple_pins(mxc_uart5_pins,
+ ARRAY_SIZE(mxc_uart5_pins));
+ return 0;
}
static struct platform_device *platform_devices[] __initdata = {
@@ -212,15 +205,13 @@ static int mxc_fec_pins[] = {
static void gpio_fec_active(void)
{
mxc_gpio_setup_multiple_pins(mxc_fec_pins,
- ARRAY_SIZE(mxc_fec_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "FEC");
+ ARRAY_SIZE(mxc_fec_pins), "FEC");
}
static void gpio_fec_inactive(void)
{
- mxc_gpio_setup_multiple_pins(mxc_fec_pins,
- ARRAY_SIZE(mxc_fec_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "FEC");
+ mxc_gpio_release_multiple_pins(mxc_fec_pins,
+ ARRAY_SIZE(mxc_fec_pins));
}
static struct imxuart_platform_data uart_pdata[] = {
diff --git a/arch/arm/mach-mx2/pcm038.c b/arch/arm/mach-mx2/pcm038.c
index 7f55746e2591..dfd4156da7d5 100644
--- a/arch/arm/mach-mx2/pcm038.c
+++ b/arch/arm/mach-mx2/pcm038.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
+#include <linux/mtd/plat-ram.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
#include <mach/common.h>
@@ -27,10 +28,36 @@
#include <asm/mach/time.h>
#include <mach/imx-uart.h>
#include <mach/board-pcm038.h>
+#include <mach/mxc_nand.h>
#include "devices.h"
/*
+ * Phytec's PCM038 comes with 2MiB battery buffered SRAM,
+ * 16 bit width
+ */
+
+static struct platdata_mtd_ram pcm038_sram_data = {
+ .bankwidth = 2,
+};
+
+static struct resource pcm038_sram_resource = {
+ .start = CS1_BASE_ADDR,
+ .end = CS1_BASE_ADDR + 512 * 1024 - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device pcm038_sram_mtd_device = {
+ .name = "mtd-ram",
+ .id = 0,
+ .dev = {
+ .platform_data = &pcm038_sram_data,
+ },
+ .num_resources = 1,
+ .resource = &pcm038_sram_resource,
+};
+
+/*
* Phytec's phyCORE-i.MX27 comes with 32MiB flash,
* 16 bit width
*/
@@ -64,15 +91,14 @@ static int mxc_uart0_pins[] = {
static int uart_mxc_port0_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart0_pins,
- ARRAY_SIZE(mxc_uart0_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART0");
+ ARRAY_SIZE(mxc_uart0_pins), "UART0");
}
static int uart_mxc_port0_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart0_pins,
- ARRAY_SIZE(mxc_uart0_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART0");
+ mxc_gpio_release_multiple_pins(mxc_uart0_pins,
+ ARRAY_SIZE(mxc_uart0_pins));
+ return 0;
}
static int mxc_uart1_pins[] = {
@@ -85,15 +111,14 @@ static int mxc_uart1_pins[] = {
static int uart_mxc_port1_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart1_pins,
- ARRAY_SIZE(mxc_uart1_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART1");
+ ARRAY_SIZE(mxc_uart1_pins), "UART1");
}
static int uart_mxc_port1_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart1_pins,
- ARRAY_SIZE(mxc_uart1_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART1");
+ mxc_gpio_release_multiple_pins(mxc_uart1_pins,
+ ARRAY_SIZE(mxc_uart1_pins));
+ return 0;
}
static int mxc_uart2_pins[] = { PE10_PF_UART3_CTS,
@@ -104,15 +129,14 @@ static int mxc_uart2_pins[] = { PE10_PF_UART3_CTS,
static int uart_mxc_port2_init(struct platform_device *pdev)
{
return mxc_gpio_setup_multiple_pins(mxc_uart2_pins,
- ARRAY_SIZE(mxc_uart2_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "UART2");
+ ARRAY_SIZE(mxc_uart2_pins), "UART2");
}
static int uart_mxc_port2_exit(struct platform_device *pdev)
{
- return mxc_gpio_setup_multiple_pins(mxc_uart2_pins,
- ARRAY_SIZE(mxc_uart2_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "UART2");
+ mxc_gpio_release_multiple_pins(mxc_uart2_pins,
+ ARRAY_SIZE(mxc_uart2_pins));
+ return 0;
}
static struct imxuart_platform_data uart_pdata[] = {
@@ -155,29 +179,47 @@ static int mxc_fec_pins[] = {
static void gpio_fec_active(void)
{
mxc_gpio_setup_multiple_pins(mxc_fec_pins,
- ARRAY_SIZE(mxc_fec_pins),
- MXC_GPIO_ALLOC_MODE_NORMAL, "FEC");
+ ARRAY_SIZE(mxc_fec_pins), "FEC");
}
static void gpio_fec_inactive(void)
{
- mxc_gpio_setup_multiple_pins(mxc_fec_pins,
- ARRAY_SIZE(mxc_fec_pins),
- MXC_GPIO_ALLOC_MODE_RELEASE, "FEC");
+ mxc_gpio_release_multiple_pins(mxc_fec_pins,
+ ARRAY_SIZE(mxc_fec_pins));
}
+static struct mxc_nand_platform_data pcm038_nand_board_info = {
+ .width = 1,
+ .hw_ecc = 1,
+};
+
static struct platform_device *platform_devices[] __initdata = {
&pcm038_nor_mtd_device,
+ &mxc_w1_master_device,
+ &pcm038_sram_mtd_device,
};
+/* On pcm038 there's a sram attached to CS1, we enable the chipselect here and
+ * setup other stuffs to access the sram. */
+static void __init pcm038_init_sram(void)
+{
+ __raw_writel(0x0000d843, CSCR_U(1));
+ __raw_writel(0x22252521, CSCR_L(1));
+ __raw_writel(0x22220a00, CSCR_A(1));
+}
+
static void __init pcm038_init(void)
{
gpio_fec_active();
+ pcm038_init_sram();
mxc_register_device(&mxc_uart_device0, &uart_pdata[0]);
mxc_register_device(&mxc_uart_device1, &uart_pdata[1]);
mxc_register_device(&mxc_uart_device2, &uart_pdata[2]);
+ mxc_gpio_mode(PE16_AF_RTCK); /* OWIRE */
+ mxc_register_device(&mxc_nand_device, &pcm038_nand_board_info);
+
platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
#ifdef CONFIG_MACH_PCM970_BASEBOARD
diff --git a/arch/arm/mach-mx3/Kconfig b/arch/arm/mach-mx3/Kconfig
index db9431dee1b4..e79659e8176e 100644
--- a/arch/arm/mach-mx3/Kconfig
+++ b/arch/arm/mach-mx3/Kconfig
@@ -21,5 +21,19 @@ config MACH_MX31LITE
Include support for MX31 LITEKIT platform. This includes specific
configurations for the board and its peripherals.
+config MACH_MX31_3DS
+ bool "Support MX31PDK (3DS)"
+ default n
+ help
+ Include support for MX31PDK (3DS) platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MACH_MX31MOBOARD
+ bool "Support mx31moboard platforms (EPFL Mobots group)"
+ default n
+ help
+ Include support for mx31moboard platform. This includes specific
+ configurations for the board and its peripherals.
+
endmenu
diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile
index 8b21abb71fb0..5a151540fe83 100644
--- a/arch/arm/mach-mx3/Makefile
+++ b/arch/arm/mach-mx3/Makefile
@@ -8,3 +8,5 @@ obj-y := mm.o clock.o devices.o iomux.o
obj-$(CONFIG_MACH_MX31ADS) += mx31ads.o
obj-$(CONFIG_MACH_MX31LITE) += mx31lite.o
obj-$(CONFIG_MACH_PCM037) += pcm037.o
+obj-$(CONFIG_MACH_MX31_3DS) += mx31pdk.o
+obj-$(CONFIG_MACH_MX31MOBOARD) += mx31moboard.o
diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c
index 9f14a871ee7c..b1746aae1f89 100644
--- a/arch/arm/mach-mx3/clock.c
+++ b/arch/arm/mach-mx3/clock.c
@@ -24,6 +24,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <mach/clock.h>
+#include <mach/hardware.h>
#include <asm/div64.h>
#include "crm_regs.h"
diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c
index a6bdcc07f3c9..1d46cb4adf96 100644
--- a/arch/arm/mach-mx3/devices.c
+++ b/arch/arm/mach-mx3/devices.c
@@ -125,19 +125,19 @@ static struct mxc_gpio_port imx_gpio_ports[] = {
.chip.label = "gpio-0",
.base = IO_ADDRESS(GPIO1_BASE_ADDR),
.irq = MXC_INT_GPIO1,
- .virtual_irq_start = MXC_GPIO_INT_BASE
+ .virtual_irq_start = MXC_GPIO_IRQ_START,
},
[1] = {
.chip.label = "gpio-1",
.base = IO_ADDRESS(GPIO2_BASE_ADDR),
.irq = MXC_INT_GPIO2,
- .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
},
[2] = {
.chip.label = "gpio-2",
.base = IO_ADDRESS(GPIO3_BASE_ADDR),
.irq = MXC_INT_GPIO3,
- .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
}
};
@@ -145,3 +145,37 @@ int __init mxc_register_gpios(void)
{
return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
}
+
+static struct resource mxc_w1_master_resources[] = {
+ {
+ .start = OWIRE_BASE_ADDR,
+ .end = OWIRE_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device mxc_w1_master_device = {
+ .name = "mxc_w1",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_w1_master_resources),
+ .resource = mxc_w1_master_resources,
+};
+
+static struct resource mxc_nand_resources[] = {
+ {
+ .start = NFC_BASE_ADDR,
+ .end = NFC_BASE_ADDR + 0xfff,
+ .flags = IORESOURCE_MEM
+ }, {
+ .start = MXC_INT_NANDFC,
+ .end = MXC_INT_NANDFC,
+ .flags = IORESOURCE_IRQ
+ },
+};
+
+struct platform_device mxc_nand_device = {
+ .name = "mxc_nand",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_nand_resources),
+ .resource = mxc_nand_resources,
+};
diff --git a/arch/arm/mach-mx3/devices.h b/arch/arm/mach-mx3/devices.h
index 4dc03f9e6001..9949ef4e0694 100644
--- a/arch/arm/mach-mx3/devices.h
+++ b/arch/arm/mach-mx3/devices.h
@@ -4,3 +4,5 @@ extern struct platform_device mxc_uart_device1;
extern struct platform_device mxc_uart_device2;
extern struct platform_device mxc_uart_device3;
extern struct platform_device mxc_uart_device4;
+extern struct platform_device mxc_w1_master_device;
+extern struct platform_device mxc_nand_device;
diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c
index 6e664be8cc13..7a5088b519a8 100644
--- a/arch/arm/mach-mx3/iomux.c
+++ b/arch/arm/mach-mx3/iomux.c
@@ -74,17 +74,18 @@ void mxc_iomux_set_pad(enum iomux_pins pin, u32 config)
u32 field, l;
void __iomem *reg;
- reg = IOMUXSW_PAD_CTL + (pin + 2) / 3;
+ pin &= IOMUX_PADNUM_MASK;
+ reg = IOMUXSW_PAD_CTL + (pin + 2) / 3 * 4;
field = (pin + 2) % 3;
- pr_debug("%s: reg offset = 0x%x field = %d\n",
+ pr_debug("%s: reg offset = 0x%x, field = %d\n",
__func__, (pin + 2) / 3, field);
spin_lock(&gpio_mux_lock);
l = __raw_readl(reg);
- l &= ~(0x1ff << (field * 9));
- l |= config << (field * 9);
+ l &= ~(0x1ff << (field * 10));
+ l |= config << (field * 10);
__raw_writel(l, reg);
spin_unlock(&gpio_mux_lock);
diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c
new file mode 100644
index 000000000000..c29098af7394
--- /dev/null
+++ b/arch/arm/mach-mx3/mx31moboard.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2008 Valentin Longchamp, EPFL Mobots group
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/types.h>
+#include <linux/init.h>
+
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/partitions.h>
+#include <linux/memory.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+#include <mach/common.h>
+#include <mach/imx-uart.h>
+#include <mach/iomux-mx3.h>
+
+#include "devices.h"
+
+static struct physmap_flash_data mx31moboard_flash_data = {
+ .width = 2,
+};
+
+static struct resource mx31moboard_flash_resource = {
+ .start = 0xa0000000,
+ .end = 0xa1ffffff,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device mx31moboard_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &mx31moboard_flash_data,
+ },
+ .resource = &mx31moboard_flash_resource,
+ .num_resources = 1,
+};
+
+static struct imxuart_platform_data uart_pdata = {
+ .flags = IMXUART_HAVE_RTSCTS,
+};
+
+static struct platform_device *devices[] __initdata = {
+ &mx31moboard_flash,
+};
+
+/*
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+
+ mxc_iomux_mode(MX31_PIN_CTS1__CTS1);
+ mxc_iomux_mode(MX31_PIN_RTS1__RTS1);
+ mxc_iomux_mode(MX31_PIN_TXD1__TXD1);
+ mxc_iomux_mode(MX31_PIN_RXD1__RXD1);
+
+ mxc_register_device(&mxc_uart_device0, &uart_pdata);
+
+ mxc_iomux_mode(MX31_PIN_CTS2__CTS2);
+ mxc_iomux_mode(MX31_PIN_RTS2__RTS2);
+ mxc_iomux_mode(MX31_PIN_TXD2__TXD2);
+ mxc_iomux_mode(MX31_PIN_RXD2__RXD2);
+
+ mxc_register_device(&mxc_uart_device1, &uart_pdata);
+
+ mxc_iomux_mode(MX31_PIN_PC_RST__CTS5);
+ mxc_iomux_mode(MX31_PIN_PC_VS2__RTS5);
+ mxc_iomux_mode(MX31_PIN_PC_BVD2__TXD5);
+ mxc_iomux_mode(MX31_PIN_PC_BVD1__RXD5);
+
+ mxc_register_device(&mxc_uart_device4, &uart_pdata);
+}
+
+/*
+ * This structure defines static mappings for the mx31moboard.
+ */
+static struct map_desc mx31moboard_io_desc[] __initdata = {
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ }, {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = AIPS2_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ },
+};
+
+/*
+ * Set up static virtual mappings.
+ */
+void __init mx31moboard_map_io(void)
+{
+ mxc_map_io();
+ iotable_init(mx31moboard_io_desc, ARRAY_SIZE(mx31moboard_io_desc));
+}
+
+static void __init mx31moboard_timer_init(void)
+{
+ mxc_clocks_init(26000000);
+ mxc_timer_init("ipg_clk.0");
+}
+
+struct sys_timer mx31moboard_timer = {
+ .init = mx31moboard_timer_init,
+};
+
+MACHINE_START(MX31MOBOARD, "EPFL Mobots mx31moboard")
+ /* Maintainer: Valentin Longchamp, EPFL Mobots group */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mx31moboard_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mx31moboard_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-mx3/mx31pdk.c b/arch/arm/mach-mx3/mx31pdk.c
new file mode 100644
index 000000000000..d464d068a4a6
--- /dev/null
+++ b/arch/arm/mach-mx3/mx31pdk.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2008 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/memory.h>
+#include <asm/mach/map.h>
+#include <mach/common.h>
+#include <mach/board-mx31pdk.h>
+#include <mach/imx-uart.h>
+#include <mach/iomux-mx3.h>
+#include "devices.h"
+
+/*!
+ * @file mx31pdk.c
+ *
+ * @brief This file contains the board-specific initialization routines.
+ *
+ * @ingroup System
+ */
+
+static struct imxuart_platform_data uart_pdata = {
+ .flags = IMXUART_HAVE_RTSCTS,
+};
+
+static inline void mxc_init_imx_uart(void)
+{
+ mxc_iomux_mode(MX31_PIN_CTS1__CTS1);
+ mxc_iomux_mode(MX31_PIN_RTS1__RTS1);
+ mxc_iomux_mode(MX31_PIN_TXD1__TXD1);
+ mxc_iomux_mode(MX31_PIN_RXD1__RXD1);
+
+ mxc_register_device(&mxc_uart_device0, &uart_pdata);
+}
+
+/*!
+ * This structure defines static mappings for the i.MX31PDK board.
+ */
+static struct map_desc mx31pdk_io_desc[] __initdata = {
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ }, {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = AIPS2_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ },
+};
+
+/*!
+ * Set up static virtual mappings.
+ */
+static void __init mx31pdk_map_io(void)
+{
+ mxc_map_io();
+ iotable_init(mx31pdk_io_desc, ARRAY_SIZE(mx31pdk_io_desc));
+}
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_init_imx_uart();
+}
+
+static void __init mx31pdk_timer_init(void)
+{
+ mxc_clocks_init(26000000);
+ mxc_timer_init("ipg_clk.0");
+}
+
+static struct sys_timer mx31pdk_timer = {
+ .init = mx31pdk_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros defined in arch.h in order to
+ * initialize __mach_desc_MX31PDK data structure.
+ */
+MACHINE_START(MX31_3DS, "Freescale MX31PDK (3DS)")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mx31pdk_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mx31pdk_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx3/pcm037.c b/arch/arm/mach-mx3/pcm037.c
index 843f68c8ead1..8cea82587222 100644
--- a/arch/arm/mach-mx3/pcm037.c
+++ b/arch/arm/mach-mx3/pcm037.c
@@ -21,7 +21,11 @@
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
+#include <linux/mtd/plat-ram.h>
#include <linux/memory.h>
+#include <linux/gpio.h>
+#include <linux/smc911x.h>
+#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
@@ -32,6 +36,7 @@
#include <mach/imx-uart.h>
#include <mach/iomux-mx3.h>
#include <mach/board-pcm037.h>
+#include <mach/mxc_nand.h>
#include "devices.h"
@@ -59,8 +64,63 @@ static struct imxuart_platform_data uart_pdata = {
.flags = IMXUART_HAVE_RTSCTS,
};
+static struct resource smc911x_resources[] = {
+ [0] = {
+ .start = CS1_BASE_ADDR + 0x300,
+ .end = CS1_BASE_ADDR + 0x300 + SZ_64K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1),
+ .end = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smc911x_platdata smc911x_info = {
+ .flags = SMC911X_USE_32BIT,
+ .irq_flags = IRQF_SHARED | IRQF_TRIGGER_LOW,
+};
+
+static struct platform_device pcm037_eth = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc911x_resources),
+ .resource = smc911x_resources,
+ .dev = {
+ .platform_data = &smc911x_info,
+ },
+};
+
+static struct platdata_mtd_ram pcm038_sram_data = {
+ .bankwidth = 2,
+};
+
+static struct resource pcm038_sram_resource = {
+ .start = CS4_BASE_ADDR,
+ .end = CS4_BASE_ADDR + 512 * 1024 - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device pcm037_sram_device = {
+ .name = "mtd-ram",
+ .id = 0,
+ .dev = {
+ .platform_data = &pcm038_sram_data,
+ },
+ .num_resources = 1,
+ .resource = &pcm038_sram_resource,
+};
+
+static struct mxc_nand_platform_data pcm037_nand_board_info = {
+ .width = 1,
+ .hw_ecc = 1,
+};
+
static struct platform_device *devices[] __initdata = {
&pcm037_flash,
+ &pcm037_eth,
+ &pcm037_sram_device,
};
/*
@@ -81,6 +141,16 @@ static void __init mxc_board_init(void)
mxc_iomux_mode(MX31_PIN_CSPI3_MISO__TXD3);
mxc_register_device(&mxc_uart_device2, &uart_pdata);
+
+ mxc_iomux_mode(MX31_PIN_BATT_LINE__OWIRE);
+ mxc_register_device(&mxc_w1_master_device, NULL);
+
+ /* SMSC9215 IRQ pin */
+ mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO3_1, IOMUX_CONFIG_GPIO));
+ if (!gpio_request(MX31_PIN_GPIO3_1, "pcm037-eth"))
+ gpio_direction_input(MX31_PIN_GPIO3_1);
+
+ mxc_register_device(&mxc_nand_device, &pcm037_nand_board_info);
}
/*
diff --git a/arch/arm/mach-netx/fb.c b/arch/arm/mach-netx/fb.c
index 24c79650f9f3..8f1f992f002e 100644
--- a/arch/arm/mach-netx/fb.c
+++ b/arch/arm/mach-netx/fb.c
@@ -22,14 +22,11 @@
#include <linux/dma-mapping.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
+#include <linux/err.h>
#include <mach/netx-regs.h>
#include <mach/hardware.h>
-struct clk {};
-
-static struct clk fb_clk;
-
static struct clcd_panel *netx_panel;
void netx_clcd_enable(struct clcd_fb *fb)
@@ -85,7 +82,7 @@ int clk_enable(struct clk *clk)
struct clk *clk_get(struct device *dev, const char *id)
{
- return &fb_clk;
+ return dev && strcmp(dev_name(dev), "fb") == 0 ? NULL : ERR_PTR(-ENOENT);
}
void clk_put(struct clk *clk)
diff --git a/arch/arm/mach-netx/include/mach/dma.h b/arch/arm/mach-netx/include/mach/dma.h
deleted file mode 100644
index 690b3ebc43ac..000000000000
--- a/arch/arm/mach-netx/include/mach/dma.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * arch/arm/mach-netx/include/mach/dma.h
- *
- * Copyright (C) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#define MAX_DMA_CHANNELS 0
-#define MAX_DMA_ADDRESS ~0
diff --git a/arch/arm/mach-netx/include/mach/io.h b/arch/arm/mach-netx/include/mach/io.h
index 468b92a82585..c3921cb3b6a6 100644
--- a/arch/arm/mach-netx/include/mach/io.h
+++ b/arch/arm/mach-netx/include/mach/io.h
@@ -22,7 +22,7 @@
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-netx/include/mach/memory.h b/arch/arm/mach-netx/include/mach/memory.h
index 53745a1378de..9a363f297f90 100644
--- a/arch/arm/mach-netx/include/mach/memory.h
+++ b/arch/arm/mach-netx/include/mach/memory.h
@@ -22,15 +22,5 @@
#define PHYS_OFFSET UL(0x80000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-netx/include/mach/netx-regs.h b/arch/arm/mach-netx/include/mach/netx-regs.h
index 5104a00d40f4..08c60ff227be 100644
--- a/arch/arm/mach-netx/include/mach/netx-regs.h
+++ b/arch/arm/mach-netx/include/mach/netx-regs.h
@@ -328,6 +328,28 @@
#define NETX_PFIFO_FILL_LEVEL(pfifo) NETX_PFIFO_REG(0x180 + ((pfifo)<<2))
#define NETX_PFIFO_XPEC_ISR(xpec) NETX_PFIFO_REG(0x400 + ((xpec) << 2))
+
+/*******************************
+ * Memory Controller *
+ *******************************/
+
+/* Registers */
+#define NETX_MEMCR_REG(ofs) __io(NETX_VA_MEMCR + (ofs))
+#define NETX_MEMCR_SRAM_CTRL(cs) NETX_MEMCR_REG(0x0 + 4 * (cs)) /* SRAM for CS 0..2 */
+#define NETX_MEMCR_SDRAM_CFG_CTRL NETX_MEMCR_REG(0x40)
+#define NETX_MEMCR_SDRAM_TIMING_CTRL NETX_MEMCR_REG(0x44)
+#define NETX_MEMCR_SDRAM_MODE NETX_MEMCR_REG(0x48)
+#define NETX_MEMCR_SDRAM_EXT_MODE NETX_MEMCR_REG(0x4c)
+#define NETX_MEMCR_PRIO_TIMESLOT_CTRL NETX_MEMCR_REG(0x80)
+#define NETX_MEMCR_PRIO_ACCESS_CTRL NETX_MEMCR_REG(0x84)
+
+/* Bits */
+#define NETX_MEMCR_SRAM_CTRL_WIDTHEXTMEM(x) (((x) & 0x3) << 24)
+#define NETX_MEMCR_SRAM_CTRL_WSPOSTPAUSEEXTMEM(x) (((x) & 0x3) << 16)
+#define NETX_MEMCR_SRAM_CTRL_WSPREPASEEXTMEM(x) (((x) & 0x3) << 8)
+#define NETX_MEMCR_SRAM_CTRL_WSEXTMEM(x) (((x) & 0x1f) << 0)
+
+
/*******************************
* Dual Port Memory *
*******************************/
diff --git a/arch/arm/mach-netx/time.c b/arch/arm/mach-netx/time.c
index 7c540c1f01fa..d51d627ce7cf 100644
--- a/arch/arm/mach-netx/time.c
+++ b/arch/arm/mach-netx/time.c
@@ -21,43 +21,100 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/mach/time.h>
#include <mach/netx-regs.h>
+#define TIMER_CLOCKEVENT 0
+#define TIMER_CLOCKSOURCE 1
+
+static void netx_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ u32 tmode;
+
+ /* disable timer */
+ writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ writel(LATCH, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
+ tmode = NETX_GPIO_COUNTER_CTRL_RST_EN |
+ NETX_GPIO_COUNTER_CTRL_IRQ_EN |
+ NETX_GPIO_COUNTER_CTRL_RUN;
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ writel(0, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKEVENT));
+ tmode = NETX_GPIO_COUNTER_CTRL_IRQ_EN |
+ NETX_GPIO_COUNTER_CTRL_RUN;
+ break;
+
+ default:
+ WARN(1, "%s: unhandled mode %d\n", __func__, mode);
+ /* fall through */
+
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_RESUME:
+ tmode = 0;
+ break;
+ }
+
+ writel(tmode, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKEVENT));
+}
+
+static int netx_set_next_event(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ writel(0 - evt, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKEVENT));
+ return 0;
+}
+
+static struct clock_event_device netx_clockevent = {
+ .name = "netx-timer" __stringify(TIMER_CLOCKEVENT),
+ .shift = 32,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_next_event = netx_set_next_event,
+ .set_mode = netx_set_mode,
+};
+
/*
* IRQ handler for the timer
*/
static irqreturn_t
netx_timer_interrupt(int irq, void *dev_id)
{
- timer_tick();
+ struct clock_event_device *evt = &netx_clockevent;
/* acknowledge interrupt */
writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
+ evt->event_handler(evt);
+
return IRQ_HANDLED;
}
static struct irqaction netx_timer_irq = {
- .name = "NetX Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = netx_timer_interrupt,
+ .name = "NetX Timer Tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = netx_timer_interrupt,
};
cycle_t netx_get_cycles(void)
{
- return readl(NETX_GPIO_COUNTER_CURRENT(1));
+ return readl(NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE));
}
static struct clocksource clocksource_netx = {
- .name = "netx_timer",
+ .name = "netx_timer",
.rating = 200,
.read = netx_get_cycles,
.mask = CLOCKSOURCE_MASK(32),
- .shift = 20,
+ .shift = 20,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
@@ -77,24 +134,37 @@ static void __init netx_timer_init(void)
/* acknowledge interrupt */
writel(COUNTER_BIT(0), NETX_GPIO_IRQ);
- /* Enable the interrupt in the specific timer register and start timer */
+ /* Enable the interrupt in the specific timer
+ * register and start timer
+ */
writel(COUNTER_BIT(0), NETX_GPIO_IRQ_ENABLE);
writel(NETX_GPIO_COUNTER_CTRL_IRQ_EN | NETX_GPIO_COUNTER_CTRL_RUN,
- NETX_GPIO_COUNTER_CTRL(0));
+ NETX_GPIO_COUNTER_CTRL(0));
setup_irq(NETX_IRQ_TIMER0, &netx_timer_irq);
/* Setup timer one for clocksource */
- writel(0, NETX_GPIO_COUNTER_CTRL(1));
- writel(0, NETX_GPIO_COUNTER_CURRENT(1));
- writel(0xFFFFFFFF, NETX_GPIO_COUNTER_MAX(1));
+ writel(0, NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE));
+ writel(0, NETX_GPIO_COUNTER_CURRENT(TIMER_CLOCKSOURCE));
+ writel(0xffffffff, NETX_GPIO_COUNTER_MAX(TIMER_CLOCKSOURCE));
- writel(NETX_GPIO_COUNTER_CTRL_RUN,
- NETX_GPIO_COUNTER_CTRL(1));
+ writel(NETX_GPIO_COUNTER_CTRL_RUN,
+ NETX_GPIO_COUNTER_CTRL(TIMER_CLOCKSOURCE));
clocksource_netx.mult =
clocksource_hz2mult(CLOCK_TICK_RATE, clocksource_netx.shift);
clocksource_register(&clocksource_netx);
+
+ netx_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
+ netx_clockevent.shift);
+ netx_clockevent.max_delta_ns =
+ clockevent_delta2ns(0xfffffffe, &netx_clockevent);
+ /* with max_delta_ns >= delta2ns(0x800) the system currently runs fine.
+ * Adding some safety ... */
+ netx_clockevent.min_delta_ns =
+ clockevent_delta2ns(0xa00, &netx_clockevent);
+ netx_clockevent.cpumask = cpumask_of_cpu(0);
+ clockevents_register_device(&netx_clockevent);
}
struct sys_timer netx_timer = {
diff --git a/arch/arm/mach-netx/xc.c b/arch/arm/mach-netx/xc.c
index 32eabf5dfa4f..8fc6205dc3a5 100644
--- a/arch/arm/mach-netx/xc.c
+++ b/arch/arm/mach-netx/xc.c
@@ -92,10 +92,10 @@ static int xc_check_ptr(struct xc *x, unsigned long adr, unsigned int size)
return -1;
}
-static int xc_patch(struct xc *x, void *patch, int count)
+static int xc_patch(struct xc *x, const void *patch, int count)
{
unsigned int val, adr;
- unsigned int *data = patch;
+ const unsigned int *data = patch;
int i;
for (i = 0; i < count; i++) {
@@ -117,7 +117,7 @@ int xc_request_firmware(struct xc *x)
struct fw_header *head;
unsigned int size;
int i;
- void *src;
+ const void *src;
unsigned long dst;
sprintf(name, "xc%d.bin", x->no);
diff --git a/arch/arm/mach-ns9xxx/include/mach/dma.h b/arch/arm/mach-ns9xxx/include/mach/dma.h
deleted file mode 100644
index 3f50d8c9e5c7..000000000000
--- a/arch/arm/mach-ns9xxx/include/mach/dma.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * arch/arm/mach-ns9xxx/include/mach/dma.h
- *
- * Copyright (C) 2006 by Digi International 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 __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-#endif /* ifndef __ASM_ARCH_DMA_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/hardware.h b/arch/arm/mach-ns9xxx/include/mach/hardware.h
index 6dbb2030f563..76631128e11c 100644
--- a/arch/arm/mach-ns9xxx/include/mach/hardware.h
+++ b/arch/arm/mach-ns9xxx/include/mach/hardware.h
@@ -11,8 +11,6 @@
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H
-#include <asm/memory.h>
-
/*
* NetSilicon NS9xxx internal mapping:
*
diff --git a/arch/arm/mach-ns9xxx/include/mach/io.h b/arch/arm/mach-ns9xxx/include/mach/io.h
index 027bf649645a..f08451d2e1bc 100644
--- a/arch/arm/mach-ns9xxx/include/mach/io.h
+++ b/arch/arm/mach-ns9xxx/include/mach/io.h
@@ -13,7 +13,7 @@
#define IO_SPACE_LIMIT 0xffffffff /* XXX */
-#define __io(a) ((void __iomem *)(a))
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#define __mem_isa(a) (IO_BASE + (a))
diff --git a/arch/arm/mach-ns9xxx/include/mach/memory.h b/arch/arm/mach-ns9xxx/include/mach/memory.h
index 649ee6235b94..6107193adbfe 100644
--- a/arch/arm/mach-ns9xxx/include/mach/memory.h
+++ b/arch/arm/mach-ns9xxx/include/mach/memory.h
@@ -21,7 +21,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig
index 79f0b1f8497b..10a301e32434 100644
--- a/arch/arm/mach-omap1/Kconfig
+++ b/arch/arm/mach-omap1/Kconfig
@@ -4,16 +4,19 @@ comment "OMAP Core Type"
config ARCH_OMAP730
depends on ARCH_OMAP1
bool "OMAP730 Based System"
+ select CPU_ARM926T
select ARCH_OMAP_OTG
config ARCH_OMAP15XX
depends on ARCH_OMAP1
default y
bool "OMAP15xx Based System"
+ select CPU_ARM925T
config ARCH_OMAP16XX
depends on ARCH_OMAP1
bool "OMAP16xx Based System"
+ select CPU_ARM926T
select ARCH_OMAP_OTG
comment "OMAP Board Type"
diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c
index db789461fca4..30308294e7c1 100644
--- a/arch/arm/mach-omap1/board-fsample.c
+++ b/arch/arm/mach-omap1/board-fsample.c
@@ -205,7 +205,7 @@ static struct platform_device *devices[] __initdata = {
static int nand_dev_ready(struct omap_nand_platform_data *data)
{
- return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN);
+ return gpio_get_value(P2_NAND_RB_GPIO_PIN);
}
static struct omap_uart_config fsample_uart_config __initdata = {
@@ -223,8 +223,9 @@ static struct omap_board_config_kernel fsample_config[] = {
static void __init omap_fsample_init(void)
{
- if (!(omap_request_gpio(P2_NAND_RB_GPIO_PIN)))
- nand_data.dev_ready = nand_dev_ready;
+ if (gpio_request(P2_NAND_RB_GPIO_PIN, "NAND ready") < 0)
+ BUG();
+ nand_data.dev_ready = nand_dev_ready;
omap_cfg_reg(L3_1610_FLASH_CS2B_OE);
omap_cfg_reg(M8_1610_FLASH_CS2B_WE);
diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
index ab9ee5820c48..409fa56d0a87 100644
--- a/arch/arm/mach-omap1/board-h2-mmc.c
+++ b/arch/arm/mach-omap1/board-h2-mmc.c
@@ -12,90 +12,68 @@
* published by the Free Software Foundation.
*/
+#include <linux/platform_device.h>
+
+#include <linux/i2c/tps65010.h>
+
#include <mach/mmc.h>
#include <mach/gpio.h>
-#ifdef CONFIG_MMC_OMAP
-static int slot_cover_open;
-static struct device *mmc_device;
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
-static int h2_mmc_set_power(struct device *dev, int slot, int power_on,
+static int mmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
- power_on ? "on" : "off", vdd);
-#endif
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
+ if (power_on)
+ gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 1);
+ else
+ gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 0);
return 0;
}
-static int h2_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+static int mmc_late_init(struct device *dev)
{
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1,
- bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
-#endif
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
+ int ret;
- return 0;
-}
+ ret = gpio_request(H2_TPS_GPIO_MMC_PWR_EN, "MMC power");
+ if (ret < 0)
+ return ret;
-static int h2_mmc_get_cover_state(struct device *dev, int slot)
-{
- BUG_ON(slot != 0);
-
- return slot_cover_open;
-}
-
-void h2_mmc_slot_cover_handler(void *arg, int state)
-{
- if (mmc_device == NULL)
- return;
-
- slot_cover_open = state;
- omap_mmc_notify_cover_event(mmc_device, 0, state);
-}
-
-static int h2_mmc_late_init(struct device *dev)
-{
- int ret = 0;
-
- mmc_device = dev;
+ gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 0);
return ret;
}
-static void h2_mmc_cleanup(struct device *dev)
+static void mmc_shutdown(struct device *dev)
{
+ gpio_free(H2_TPS_GPIO_MMC_PWR_EN);
}
-static struct omap_mmc_platform_data h2_mmc_data = {
+/*
+ * H2 could use the following functions tested:
+ * - mmc_get_cover_state that uses OMAP_MPUIO(1)
+ * - mmc_get_wp that uses OMAP_MPUIO(3)
+ */
+static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 1,
- .switch_slot = NULL,
- .init = h2_mmc_late_init,
- .cleanup = h2_mmc_cleanup,
+ .init = mmc_late_init,
+ .shutdown = mmc_shutdown,
+ .dma_mask = 0xffffffff,
.slots[0] = {
- .set_power = h2_mmc_set_power,
- .set_bus_mode = h2_mmc_set_bus_mode,
- .get_ro = NULL,
- .get_cover_state = h2_mmc_get_cover_state,
+ .set_power = mmc_set_power,
.ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 |
MMC_VDD_32_33 | MMC_VDD_33_34,
.name = "mmcblk",
},
};
+static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC];
+
void __init h2_mmc_init(void)
{
- omap_set_mmc_info(1, &h2_mmc_data);
+ mmc_data[0] = &mmc1_data;
+ omap1_init_mmc(mmc_data, OMAP16XX_NR_MMC);
}
#else
@@ -104,7 +82,4 @@ void __init h2_mmc_init(void)
{
}
-void h2_mmc_slot_cover_handler(void *arg, int state)
-{
-}
#endif
diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c
index 3b65914b9141..b240c5f861da 100644
--- a/arch/arm/mach-omap1/board-h2.c
+++ b/arch/arm/mach-omap1/board-h2.c
@@ -250,11 +250,8 @@ static struct platform_device h2_kp_device = {
#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE)
static int h2_transceiver_mode(struct device *dev, int state)
{
- if (state & IR_SIRMODE)
- omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 0);
- else /* MIR/FIR */
- omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 1);
-
+ /* SIR when low, else MIR/FIR when HIGH */
+ gpio_set_value(H2_IRDA_FIRSEL_GPIO_PIN, !(state & IR_SIRMODE));
return 0;
}
#endif
@@ -342,16 +339,31 @@ static struct platform_device *h2_devices[] __initdata = {
static void __init h2_init_smc91x(void)
{
- if ((omap_request_gpio(0)) < 0) {
+ if (gpio_request(0, "SMC91x irq") < 0) {
printk("Error requesting gpio 0 for smc91x irq\n");
return;
}
}
+static int tps_setup(struct i2c_client *client, void *context)
+{
+ tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V |
+ TPS_LDO1_ENABLE | TPS_VLDO1_3_0V);
+
+ return 0;
+}
+
+static struct tps65010_board tps_board = {
+ .base = H2_TPS_GPIO_BASE,
+ .outmask = 0x0f,
+ .setup = tps_setup,
+};
+
static struct i2c_board_info __initdata h2_i2c_board_info[] = {
{
I2C_BOARD_INFO("tps65010", 0x48),
.irq = OMAP_GPIO_IRQ(58),
+ .platform_data = &tps_board,
}, {
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq = OMAP_GPIO_IRQ(2),
@@ -381,15 +393,6 @@ static struct omap_usb_config h2_usb_config __initdata = {
.pins[1] = 3,
};
-static struct omap_mmc_config h2_mmc_config __initdata = {
- .mmc[0] = {
- .enabled = 1,
- .wire4 = 1,
- },
-};
-
-extern struct omap_mmc_platform_data h2_mmc_data;
-
static struct omap_uart_config h2_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
@@ -400,7 +403,6 @@ static struct omap_lcd_config h2_lcd_config __initdata = {
static struct omap_board_config_kernel h2_config[] __initdata = {
{ OMAP_TAG_USB, &h2_usb_config },
- { OMAP_TAG_MMC, &h2_mmc_config },
{ OMAP_TAG_UART, &h2_uart_config },
{ OMAP_TAG_LCD, &h2_lcd_config },
};
@@ -409,7 +411,7 @@ static struct omap_board_config_kernel h2_config[] __initdata = {
static int h2_nand_dev_ready(struct omap_nand_platform_data *data)
{
- return omap_get_gpio_datain(H2_NAND_RB_GPIO_PIN);
+ return gpio_get_value(H2_NAND_RB_GPIO_PIN);
}
static void __init h2_init(void)
@@ -428,8 +430,9 @@ static void __init h2_init(void)
h2_nand_resource.end = h2_nand_resource.start = OMAP_CS2B_PHYS;
h2_nand_resource.end += SZ_4K - 1;
- if (!(omap_request_gpio(H2_NAND_RB_GPIO_PIN)))
- h2_nand_data.dev_ready = h2_nand_dev_ready;
+ if (gpio_request(H2_NAND_RB_GPIO_PIN, "NAND ready") < 0)
+ BUG();
+ gpio_direction_input(H2_NAND_RB_GPIO_PIN);
omap_cfg_reg(L3_1610_FLASH_CS2B_OE);
omap_cfg_reg(M8_1610_FLASH_CS2B_WE);
@@ -441,10 +444,10 @@ static void __init h2_init(void)
/* Irda */
#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE)
omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, FUNC_MUX_CTRL_A);
- if (!(omap_request_gpio(H2_IRDA_FIRSEL_GPIO_PIN))) {
- omap_set_gpio_direction(H2_IRDA_FIRSEL_GPIO_PIN, 0);
- h2_irda_data.transceiver_mode = h2_transceiver_mode;
- }
+ if (gpio_request(H2_IRDA_FIRSEL_GPIO_PIN, "IRDA mode") < 0)
+ BUG();
+ gpio_direction_output(H2_IRDA_FIRSEL_GPIO_PIN, 0);
+ h2_irda_data.transceiver_mode = h2_transceiver_mode;
#endif
platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices));
diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
index 36085819098c..fdfe793d56f2 100644
--- a/arch/arm/mach-omap1/board-h3-mmc.c
+++ b/arch/arm/mach-omap1/board-h3-mmc.c
@@ -12,94 +12,55 @@
* published by the Free Software Foundation.
*/
+#include <linux/platform_device.h>
+
+#include <linux/i2c/tps65010.h>
+
#include <mach/mmc.h>
#include <mach/gpio.h>
-#ifdef CONFIG_MMC_OMAP
-static int slot_cover_open;
-static struct device *mmc_device;
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
-static int h3_mmc_set_power(struct device *dev, int slot, int power_on,
+static int mmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
- power_on ? "on" : "off", vdd);
-#endif
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
+ if (power_on)
+ gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 1);
+ else
+ gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 0);
return 0;
}
-static int h3_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
-{
- int ret = 0;
-
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1,
- bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
-#endif
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
-
- /* Treated on upper level */
-
- return bus_mode;
-}
-
-static int h3_mmc_get_cover_state(struct device *dev, int slot)
-{
- BUG_ON(slot != 0);
-
- return slot_cover_open;
-}
-
-void h3_mmc_slot_cover_handler(void *arg, int state)
-{
- if (mmc_device == NULL)
- return;
-
- slot_cover_open = state;
- omap_mmc_notify_cover_event(mmc_device, 0, state);
-}
-
-static int h3_mmc_late_init(struct device *dev)
-{
- int ret = 0;
-
- mmc_device = dev;
-
- return ret;
-}
-
-static void h3_mmc_cleanup(struct device *dev)
-{
-}
-
-static struct omap_mmc_platform_data h3_mmc_data = {
+/*
+ * H3 could use the following functions tested:
+ * - mmc_get_cover_state that uses OMAP_MPUIO(1)
+ * - mmc_get_wp that maybe uses OMAP_MPUIO(3)
+ */
+static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 1,
- .switch_slot = NULL,
- .init = h3_mmc_late_init,
- .cleanup = h3_mmc_cleanup,
+ .dma_mask = 0xffffffff,
.slots[0] = {
- .set_power = h3_mmc_set_power,
- .set_bus_mode = h3_mmc_set_bus_mode,
- .get_ro = NULL,
- .get_cover_state = h3_mmc_get_cover_state,
+ .set_power = mmc_set_power,
.ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 |
MMC_VDD_32_33 | MMC_VDD_33_34,
.name = "mmcblk",
},
};
+static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC];
+
void __init h3_mmc_init(void)
{
- omap_set_mmc_info(1, &h3_mmc_data);
+ int ret;
+
+ ret = gpio_request(H3_TPS_GPIO_MMC_PWR_EN, "MMC power");
+ if (ret < 0)
+ return;
+ gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 0);
+
+ mmc_data[0] = &mmc1_data;
+ omap1_init_mmc(mmc_data, OMAP16XX_NR_MMC);
}
#else
@@ -108,7 +69,4 @@ void __init h3_mmc_init(void)
{
}
-void h3_mmc_slot_cover_handler(void *arg, int state)
-{
-}
#endif
diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c
index adfcd7b51393..5157eea9be35 100644
--- a/arch/arm/mach-omap1/board-h3.c
+++ b/arch/arm/mach-omap1/board-h3.c
@@ -447,15 +447,6 @@ static struct omap_usb_config h3_usb_config __initdata = {
.pins[1] = 3,
};
-static struct omap_mmc_config h3_mmc_config __initdata = {
- .mmc[0] = {
- .enabled = 1,
- .wire4 = 1,
- },
-};
-
-extern struct omap_mmc_platform_data h3_mmc_data;
-
static struct omap_uart_config h3_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
@@ -466,7 +457,6 @@ static struct omap_lcd_config h3_lcd_config __initdata = {
static struct omap_board_config_kernel h3_config[] __initdata = {
{ OMAP_TAG_USB, &h3_usb_config },
- { OMAP_TAG_MMC, &h3_mmc_config },
{ OMAP_TAG_UART, &h3_uart_config },
{ OMAP_TAG_LCD, &h3_lcd_config },
};
@@ -498,7 +488,7 @@ static struct omap_gpio_switch h3_gpio_switches[] __initdata = {
static int nand_dev_ready(struct omap_nand_platform_data *data)
{
- return omap_get_gpio_datain(H3_NAND_RB_GPIO_PIN);
+ return gpio_get_value(H3_NAND_RB_GPIO_PIN);
}
static void __init h3_init(void)
@@ -516,8 +506,9 @@ static void __init h3_init(void)
nand_resource.end = nand_resource.start = OMAP_CS2B_PHYS;
nand_resource.end += SZ_4K - 1;
- if (!(omap_request_gpio(H3_NAND_RB_GPIO_PIN)))
- nand_data.dev_ready = nand_dev_ready;
+ if (gpio_request(H3_NAND_RB_GPIO_PIN, "NAND ready") < 0)
+ BUG();
+ nand_data.dev_ready = nand_dev_ready;
/* GPIO10 Func_MUX_CTRL reg bit 29:27, Configure V2 to mode1 as GPIO */
/* GPIO10 pullup/down register, Enable pullup on GPIO10 */
@@ -537,7 +528,7 @@ static void __init h3_init(void)
static void __init h3_init_smc91x(void)
{
omap_cfg_reg(W15_1710_GPIO40);
- if (omap_request_gpio(40) < 0) {
+ if (gpio_request(40, "SMC91x irq") < 0) {
printk("Error requesting gpio 40 for smc91x irq\n");
return;
}
diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c
index cbc11be5cd2a..af2fb9070083 100644
--- a/arch/arm/mach-omap1/board-innovator.c
+++ b/arch/arm/mach-omap1/board-innovator.c
@@ -39,6 +39,7 @@
#include <mach/common.h>
#include <mach/mcbsp.h>
#include <mach/omap-alsa.h>
+#include <mach/mmc.h>
static int innovator_keymap[] = {
KEY(0, 0, KEY_F1),
@@ -301,7 +302,7 @@ static void __init innovator_init_smc91x(void)
OMAP1510_FPGA_RST);
udelay(750);
} else {
- if ((omap_request_gpio(0)) < 0) {
+ if (gpio_request(0, "SMC91x irq") < 0) {
printk("Error requesting gpio 0 for smc91x irq\n");
return;
}
@@ -360,16 +361,49 @@ static struct omap_lcd_config innovator1610_lcd_config __initdata = {
};
#endif
-static struct omap_mmc_config innovator_mmc_config __initdata = {
- .mmc [0] = {
- .enabled = 1,
- .wire4 = 1,
- .wp_pin = OMAP_MPUIO(3),
- .power_pin = -1, /* FPGA F3 UIO42 */
- .switch_pin = -1, /* FPGA F4 UIO43 */
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
+
+static int mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ if (power_on)
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3),
+ OMAP1510_FPGA_POWER);
+ else
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3),
+ OMAP1510_FPGA_POWER);
+
+ return 0;
+}
+
+/*
+ * Innovator could use the following functions tested:
+ * - mmc_get_wp that uses OMAP_MPUIO(3)
+ * - mmc_get_cover_state that uses FPGA F4 UIO43
+ */
+static struct omap_mmc_platform_data mmc1_data = {
+ .nr_slots = 1,
+ .slots[0] = {
+ .set_power = mmc_set_power,
+ .wires = 4,
+ .name = "mmcblk",
},
};
+static struct omap_mmc_platform_data *mmc_data[OMAP16XX_NR_MMC];
+
+void __init innovator_mmc_init(void)
+{
+ mmc_data[0] = &mmc1_data;
+ omap1_init_mmc(mmc_data, OMAP15XX_NR_MMC);
+}
+
+#else
+static inline void innovator_mmc_init(void)
+{
+}
+#endif
+
static struct omap_uart_config innovator_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
@@ -377,7 +411,6 @@ static struct omap_uart_config innovator_uart_config __initdata = {
static struct omap_board_config_kernel innovator_config[] = {
{ OMAP_TAG_USB, NULL },
{ OMAP_TAG_LCD, NULL },
- { OMAP_TAG_MMC, &innovator_mmc_config },
{ OMAP_TAG_UART, &innovator_uart_config },
};
@@ -412,6 +445,7 @@ static void __init innovator_init(void)
omap_board_config_size = ARRAY_SIZE(innovator_config);
omap_serial_init();
omap_register_i2c_bus(1, 100, NULL, 0);
+ innovator_mmc_init();
}
static void __init innovator_map_io(void)
diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index 38d9783ac6d6..4970c402a594 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -35,6 +35,7 @@
#include <mach/aic23.h>
#include <mach/omapfb.h>
#include <mach/lcd_mipid.h>
+#include <mach/mmc.h>
#define ADS7846_PENDOWN_GPIO 15
@@ -102,7 +103,7 @@ static void mipid_shutdown(struct mipid_platform_data *pdata)
{
if (pdata->nreset_gpio != -1) {
printk(KERN_INFO "shutdown LCD\n");
- omap_set_gpio_dataout(pdata->nreset_gpio, 0);
+ gpio_set_value(pdata->nreset_gpio, 0);
msleep(120);
}
}
@@ -124,13 +125,13 @@ static void mipid_dev_init(void)
static void ads7846_dev_init(void)
{
- if (omap_request_gpio(ADS7846_PENDOWN_GPIO) < 0)
+ if (gpio_request(ADS7846_PENDOWN_GPIO, "ADS7846 pendown") < 0)
printk(KERN_ERR "can't get ads7846 pen down GPIO\n");
}
static int ads7846_get_pendown_state(void)
{
- return !omap_get_gpio_datain(ADS7846_PENDOWN_GPIO);
+ return !gpio_get_value(ADS7846_PENDOWN_GPIO);
}
static struct ads7846_platform_data nokia770_ads7846_platform_data __initdata = {
@@ -173,26 +174,68 @@ static struct omap_usb_config nokia770_usb_config __initdata = {
.pins[0] = 6,
};
-static struct omap_mmc_config nokia770_mmc_config __initdata = {
- .mmc[0] = {
- .enabled = 0,
- .wire4 = 0,
- .wp_pin = -1,
- .power_pin = -1,
- .switch_pin = -1,
- },
- .mmc[1] = {
- .enabled = 0,
- .wire4 = 0,
- .wp_pin = -1,
- .power_pin = -1,
- .switch_pin = -1,
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
+
+#define NOKIA770_GPIO_MMC_POWER 41
+#define NOKIA770_GPIO_MMC_SWITCH 23
+
+static int nokia770_mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ if (power_on)
+ gpio_set_value(NOKIA770_GPIO_MMC_POWER, 1);
+ else
+ gpio_set_value(NOKIA770_GPIO_MMC_POWER, 0);
+
+ return 0;
+}
+
+static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
+{
+ return gpio_get_value(NOKIA770_GPIO_MMC_SWITCH);
+}
+
+static struct omap_mmc_platform_data nokia770_mmc2_data = {
+ .nr_slots = 1,
+ .dma_mask = 0xffffffff,
+ .slots[0] = {
+ .set_power = nokia770_mmc_set_power,
+ .get_cover_state = nokia770_mmc_get_cover_state,
+ .name = "mmcblk",
},
};
+static struct omap_mmc_platform_data *nokia770_mmc_data[OMAP16XX_NR_MMC];
+
+static void __init nokia770_mmc_init(void)
+{
+ int ret;
+
+ ret = gpio_request(NOKIA770_GPIO_MMC_POWER, "MMC power");
+ if (ret < 0)
+ return;
+ gpio_direction_output(NOKIA770_GPIO_MMC_POWER, 0);
+
+ ret = gpio_request(NOKIA770_GPIO_MMC_SWITCH, "MMC cover");
+ if (ret < 0) {
+ gpio_free(NOKIA770_GPIO_MMC_POWER);
+ return;
+ }
+ gpio_direction_input(NOKIA770_GPIO_MMC_SWITCH);
+
+ /* Only the second MMC controller is used */
+ nokia770_mmc_data[1] = &nokia770_mmc2_data;
+ omap1_init_mmc(nokia770_mmc_data, OMAP16XX_NR_MMC);
+}
+
+#else
+static inline void nokia770_mmc_init(void)
+{
+}
+#endif
+
static struct omap_board_config_kernel nokia770_config[] __initdata = {
{ OMAP_TAG_USB, NULL },
- { OMAP_TAG_MMC, &nokia770_mmc_config },
};
#if defined(CONFIG_OMAP_DSP)
@@ -228,9 +271,9 @@ static void nokia770_audio_pwr_up(void)
/* Turn on codec */
aic23_power_up();
- if (omap_get_gpio_datain(HEADPHONE_GPIO))
+ if (gpio_get_value(HEADPHONE_GPIO))
/* HP not connected, turn on amplifier */
- omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 1);
+ gpio_set_value(AMPLIFIER_CTRL_GPIO, 1);
else
/* HP connected, do not turn on amplifier */
printk("HP connected\n");
@@ -250,7 +293,7 @@ static DECLARE_DELAYED_WORK(codec_power_down_work, codec_delayed_power_down);
static void nokia770_audio_pwr_down(void)
{
/* Turn off amplifier */
- omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 0);
+ gpio_set_value(AMPLIFIER_CTRL_GPIO, 0);
/* Turn off codec: schedule delayed work */
schedule_delayed_work(&codec_power_down_work, HZ / 20); /* 50ms */
@@ -335,6 +378,7 @@ static void __init omap_nokia770_init(void)
omap_dsp_init();
ads7846_dev_init();
mipid_dev_init();
+ nokia770_mmc_init();
}
static void __init omap_nokia770_map_io(void)
diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c
index 3e766e49f7cc..ff9e67baa5c9 100644
--- a/arch/arm/mach-omap1/board-osk.c
+++ b/arch/arm/mach-omap1/board-osk.c
@@ -188,7 +188,8 @@ static struct gpio_led tps_leds[] = {
/* NOTE: D9 and D2 have hardware blink support.
* Also, D9 requires non-battery power.
*/
- { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9", },
+ { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9",
+ .default_trigger = "ide-disk", },
{ .gpio = OSK_TPS_GPIO_LED_D2, .name = "d2", },
{ .gpio = OSK_TPS_GPIO_LED_D3, .name = "d3", .active_low = 1,
.default_trigger = "heartbeat", },
@@ -260,7 +261,6 @@ static struct i2c_board_info __initdata osk_i2c_board_info[] = {
},
/* TODO when driver support is ready:
* - aic23 audio chip at 0x1a
- * - on Mistral, 24c04 eeprom at 0x50
* - optionally on Mistral, ov9640 camera sensor at 0x30
*/
};
@@ -288,7 +288,7 @@ static void __init osk_init_cf(void)
return;
}
/* the CF I/O IRQ is really active-low */
- set_irq_type(OMAP_GPIO_IRQ(62), IRQ_TYPE_EDGE_FALLING);
+ set_irq_type(gpio_to_irq(62), IRQ_TYPE_EDGE_FALLING);
}
static void __init osk_init_irq(void)
@@ -337,11 +337,28 @@ static struct omap_board_config_kernel osk_config[] __initdata = {
#ifdef CONFIG_OMAP_OSK_MISTRAL
#include <linux/input.h>
+#include <linux/i2c/at24.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <mach/keypad.h>
+static struct at24_platform_data at24c04 = {
+ .byte_len = SZ_4K / 8,
+ .page_size = 16,
+};
+
+static struct i2c_board_info __initdata mistral_i2c_board_info[] = {
+ {
+ /* NOTE: powered from LCD supply */
+ I2C_BOARD_INFO("24c04", 0x50),
+ .platform_data = &at24c04,
+ },
+ /* TODO when driver support is ready:
+ * - optionally ov9640 camera sensor at 0x30
+ */
+};
+
static const int osk_keymap[] = {
/* KEY(col, row, code) */
KEY(0, 0, KEY_F1), /* SW4 */
@@ -483,23 +500,30 @@ static void __init osk_mistral_init(void)
omap_cfg_reg(P20_1610_GPIO4); /* PENIRQ */
gpio_request(4, "ts_int");
gpio_direction_input(4);
- set_irq_type(OMAP_GPIO_IRQ(4), IRQ_TYPE_EDGE_FALLING);
+ set_irq_type(gpio_to_irq(4), IRQ_TYPE_EDGE_FALLING);
spi_register_board_info(mistral_boardinfo,
ARRAY_SIZE(mistral_boardinfo));
- /* the sideways button (SW1) is for use as a "wakeup" button */
+ /* the sideways button (SW1) is for use as a "wakeup" button
+ *
+ * NOTE: The Mistral board has the wakeup button (SW1) wired
+ * to the LCD 3.3V rail, which is powered down during suspend.
+ * To allow this button to wake up the omap, work around this
+ * HW bug by rewiring SW1 to use the main 3.3V rail.
+ */
omap_cfg_reg(N15_1610_MPUIO2);
if (gpio_request(OMAP_MPUIO(2), "wakeup") == 0) {
int ret = 0;
+ int irq = gpio_to_irq(OMAP_MPUIO(2));
gpio_direction_input(OMAP_MPUIO(2));
- set_irq_type(OMAP_GPIO_IRQ(OMAP_MPUIO(2)), IRQ_TYPE_EDGE_RISING);
+ set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
#ifdef CONFIG_PM
/* share the IRQ in case someone wants to use the
* button for more than wakeup from system sleep.
*/
- ret = request_irq(OMAP_GPIO_IRQ(OMAP_MPUIO(2)),
+ ret = request_irq(irq,
&osk_mistral_wake_interrupt,
IRQF_SHARED, "mistral_wakeup",
&osk_mistral_wake_interrupt);
@@ -508,7 +532,7 @@ static void __init osk_mistral_init(void)
printk(KERN_ERR "OSK+Mistral: no wakeup irq, %d?\n",
ret);
} else
- enable_irq_wake(OMAP_GPIO_IRQ(OMAP_MPUIO(2)));
+ enable_irq_wake(irq);
#endif
} else
printk(KERN_ERR "OSK+Mistral: wakeup button is awol\n");
@@ -520,6 +544,9 @@ static void __init osk_mistral_init(void)
if (gpio_request(2, "lcd_pwr") == 0)
gpio_direction_output(2, 1);
+ i2c_register_board_info(1, mistral_i2c_board_info,
+ ARRAY_SIZE(mistral_i2c_board_info));
+
platform_add_devices(mistral_devices, ARRAY_SIZE(mistral_devices));
}
#else
diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c
index b58043644a6f..75e32d35afd9 100644
--- a/arch/arm/mach-omap1/board-palmte.c
+++ b/arch/arm/mach-omap1/board-palmte.c
@@ -255,7 +255,7 @@ static void palmte_get_power_status(struct apm_power_info *info, int *battery)
{
int charging, batt, hi, lo, mid;
- charging = !omap_get_gpio_datain(PALMTE_DC_GPIO);
+ charging = !gpio_get_value(PALMTE_DC_GPIO);
batt = battery[0];
if (charging)
batt -= 60;
@@ -316,7 +316,6 @@ static void palmte_get_power_status(struct apm_power_info *info, int *battery)
static struct omap_board_config_kernel palmte_config[] __initdata = {
{ OMAP_TAG_USB, &palmte_usb_config },
- { OMAP_TAG_MMC, &palmte_mmc_config },
{ OMAP_TAG_LCD, &palmte_lcd_config },
{ OMAP_TAG_UART, &palmte_uart_config },
};
@@ -335,11 +334,11 @@ static void palmte_headphones_detect(void *data, int state)
{
if (state) {
/* Headphones connected, disable speaker */
- omap_set_gpio_dataout(PALMTE_SPEAKER_GPIO, 0);
+ gpio_set_value(PALMTE_SPEAKER_GPIO, 0);
printk(KERN_INFO "PM: speaker off\n");
} else {
/* Headphones unplugged, re-enable speaker */
- omap_set_gpio_dataout(PALMTE_SPEAKER_GPIO, 1);
+ gpio_set_value(PALMTE_SPEAKER_GPIO, 1);
printk(KERN_INFO "PM: speaker on\n");
}
}
@@ -347,18 +346,18 @@ static void palmte_headphones_detect(void *data, int state)
static void __init palmte_misc_gpio_setup(void)
{
/* Set TSC2102 PINTDAV pin as input (used by TSC2102 driver) */
- if (omap_request_gpio(PALMTE_PINTDAV_GPIO)) {
+ if (gpio_request(PALMTE_PINTDAV_GPIO, "TSC2102 PINTDAV") < 0) {
printk(KERN_ERR "Could not reserve PINTDAV GPIO!\n");
return;
}
- omap_set_gpio_direction(PALMTE_PINTDAV_GPIO, 1);
+ gpio_direction_input(PALMTE_PINTDAV_GPIO);
/* Set USB-or-DC-IN pin as input (unused) */
- if (omap_request_gpio(PALMTE_USB_OR_DC_GPIO)) {
+ if (gpio_request(PALMTE_USB_OR_DC_GPIO, "USB/DC-IN") < 0) {
printk(KERN_ERR "Could not reserve cable signal GPIO!\n");
return;
}
- omap_set_gpio_direction(PALMTE_USB_OR_DC_GPIO, 1);
+ gpio_direction_input(PALMTE_USB_OR_DC_GPIO);
}
static void __init omap_palmte_init(void)
diff --git a/arch/arm/mach-omap1/board-palmtt.c b/arch/arm/mach-omap1/board-palmtt.c
index 40f9860a09df..5c001afe8062 100644
--- a/arch/arm/mach-omap1/board-palmtt.c
+++ b/arch/arm/mach-omap1/board-palmtt.c
@@ -268,7 +268,7 @@ static struct platform_device *palmtt_devices[] __initdata = {
static int palmtt_get_pendown_state(void)
{
- return !omap_get_gpio_datain(6);
+ return !gpio_get_value(6);
}
static const struct ads7846_platform_data palmtt_ts_info = {
diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c
index e719294250b1..cc05257eb1cd 100644
--- a/arch/arm/mach-omap1/board-palmz71.c
+++ b/arch/arm/mach-omap1/board-palmz71.c
@@ -239,7 +239,7 @@ static struct platform_device *devices[] __initdata = {
static int
palmz71_get_pendown_state(void)
{
- return !omap_get_gpio_datain(PALMZ71_PENIRQ_GPIO);
+ return !gpio_get_value(PALMZ71_PENIRQ_GPIO);
}
static const struct ads7846_platform_data palmz71_ts_info = {
@@ -267,16 +267,6 @@ static struct omap_usb_config palmz71_usb_config __initdata = {
.pins[0] = 2,
};
-static struct omap_mmc_config palmz71_mmc_config __initdata = {
- .mmc[0] = {
- .enabled = 1,
- .wire4 = 0,
- .wp_pin = PALMZ71_MMC_WP_GPIO,
- .power_pin = -1,
- .switch_pin = PALMZ71_MMC_IN_GPIO,
- },
-};
-
static struct omap_lcd_config palmz71_lcd_config __initdata = {
.ctrl_name = "internal",
};
@@ -287,7 +277,6 @@ static struct omap_uart_config palmz71_uart_config __initdata = {
static struct omap_board_config_kernel palmz71_config[] __initdata = {
{OMAP_TAG_USB, &palmz71_usb_config},
- {OMAP_TAG_MMC, &palmz71_mmc_config},
{OMAP_TAG_LCD, &palmz71_lcd_config},
{OMAP_TAG_UART, &palmz71_uart_config},
};
@@ -295,13 +284,13 @@ static struct omap_board_config_kernel palmz71_config[] __initdata = {
static irqreturn_t
palmz71_powercable(int irq, void *dev_id)
{
- if (omap_get_gpio_datain(PALMZ71_USBDETECT_GPIO)) {
+ if (gpio_get_value(PALMZ71_USBDETECT_GPIO)) {
printk(KERN_INFO "PM: Power cable connected\n");
- set_irq_type(OMAP_GPIO_IRQ(PALMZ71_USBDETECT_GPIO),
+ set_irq_type(gpio_to_irq(PALMZ71_USBDETECT_GPIO),
IRQ_TYPE_EDGE_FALLING);
} else {
printk(KERN_INFO "PM: Power cable disconnected\n");
- set_irq_type(OMAP_GPIO_IRQ(PALMZ71_USBDETECT_GPIO),
+ set_irq_type(gpio_to_irq(PALMZ71_USBDETECT_GPIO),
IRQ_TYPE_EDGE_RISING);
}
return IRQ_HANDLED;
@@ -323,29 +312,28 @@ palmz71_gpio_setup(int early)
{
if (early) {
/* Only set GPIO1 so we have a working serial */
- omap_set_gpio_dataout(1, 1);
- omap_set_gpio_direction(1, 0);
+ gpio_direction_output(1, 1);
} else {
/* Set MMC/SD host WP pin as input */
- if (omap_request_gpio(PALMZ71_MMC_WP_GPIO)) {
+ if (gpio_request(PALMZ71_MMC_WP_GPIO, "MMC WP") < 0) {
printk(KERN_ERR "Could not reserve WP GPIO!\n");
return;
}
- omap_set_gpio_direction(PALMZ71_MMC_WP_GPIO, 1);
+ gpio_direction_input(PALMZ71_MMC_WP_GPIO);
/* Monitor the Power-cable-connected signal */
- if (omap_request_gpio(PALMZ71_USBDETECT_GPIO)) {
+ if (gpio_request(PALMZ71_USBDETECT_GPIO, "USB detect") < 0) {
printk(KERN_ERR
"Could not reserve cable signal GPIO!\n");
return;
}
- omap_set_gpio_direction(PALMZ71_USBDETECT_GPIO, 1);
- if (request_irq(OMAP_GPIO_IRQ(PALMZ71_USBDETECT_GPIO),
+ gpio_direction_input(PALMZ71_USBDETECT_GPIO);
+ if (request_irq(gpio_to_irq(PALMZ71_USBDETECT_GPIO),
palmz71_powercable, IRQF_SAMPLE_RANDOM,
"palmz71-cable", 0))
printk(KERN_ERR
"IRQ request for power cable failed!\n");
- palmz71_powercable(OMAP_GPIO_IRQ(PALMZ71_USBDETECT_GPIO), 0);
+ palmz71_powercable(gpio_to_irq(PALMZ71_USBDETECT_GPIO), 0);
}
}
diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c
index b715917bfdaf..3b9f907aa899 100644
--- a/arch/arm/mach-omap1/board-perseus2.c
+++ b/arch/arm/mach-omap1/board-perseus2.c
@@ -205,7 +205,7 @@ static struct platform_device *devices[] __initdata = {
static int nand_dev_ready(struct omap_nand_platform_data *data)
{
- return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN);
+ return gpio_get_value(P2_NAND_RB_GPIO_PIN);
}
static struct omap_uart_config perseus2_uart_config __initdata = {
@@ -223,8 +223,9 @@ static struct omap_board_config_kernel perseus2_config[] __initdata = {
static void __init omap_perseus2_init(void)
{
- if (!(omap_request_gpio(P2_NAND_RB_GPIO_PIN)))
- nand_data.dev_ready = nand_dev_ready;
+ if (gpio_request(P2_NAND_RB_GPIO_PIN, "NAND ready") < 0)
+ BUG();
+ nand_data.dev_ready = nand_dev_ready;
omap_cfg_reg(L3_1610_FLASH_CS2B_OE);
omap_cfg_reg(M8_1610_FLASH_CS2B_WE);
diff --git a/arch/arm/mach-omap1/board-sx1-mmc.c b/arch/arm/mach-omap1/board-sx1-mmc.c
index 0be4ebaa2842..66a4d7d5255d 100644
--- a/arch/arm/mach-omap1/board-sx1-mmc.c
+++ b/arch/arm/mach-omap1/board-sx1-mmc.c
@@ -12,30 +12,20 @@
* published by the Free Software Foundation.
*/
+#include <linux/platform_device.h>
+
#include <mach/hardware.h>
#include <mach/mmc.h>
#include <mach/gpio.h>
-#ifdef CONFIG_MMC_OMAP
-static int slot_cover_open;
-static struct device *mmc_device;
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
-static int sx1_mmc_set_power(struct device *dev, int slot, int power_on,
+static int mmc_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
int err;
u8 dat = 0;
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
- power_on ? "on" : "off", vdd);
-#endif
-
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
-
err = sx1_i2c_read_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, &dat);
if (err < 0)
return err;
@@ -48,68 +38,23 @@ static int sx1_mmc_set_power(struct device *dev, int slot, int power_on,
return sx1_i2c_write_byte(SOFIA_I2C_ADDR, SOFIA_POWER1_REG, dat);
}
-static int sx1_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
-{
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1,
- bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
-#endif
- if (slot != 0) {
- dev_err(dev, "No such slot %d\n", slot + 1);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int sx1_mmc_get_cover_state(struct device *dev, int slot)
-{
- BUG_ON(slot != 0);
-
- return slot_cover_open;
-}
-
-void sx1_mmc_slot_cover_handler(void *arg, int state)
-{
- if (mmc_device == NULL)
- return;
-
- slot_cover_open = state;
- omap_mmc_notify_cover_event(mmc_device, 0, state);
-}
-
-static int sx1_mmc_late_init(struct device *dev)
-{
- int ret = 0;
-
- mmc_device = dev;
-
- return ret;
-}
-
-static void sx1_mmc_cleanup(struct device *dev)
-{
-}
-
-static struct omap_mmc_platform_data sx1_mmc_data = {
+/* Cover switch is at OMAP_MPUIO(3) */
+static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 1,
- .switch_slot = NULL,
- .init = sx1_mmc_late_init,
- .cleanup = sx1_mmc_cleanup,
.slots[0] = {
- .set_power = sx1_mmc_set_power,
- .set_bus_mode = sx1_mmc_set_bus_mode,
- .get_ro = NULL,
- .get_cover_state = sx1_mmc_get_cover_state,
+ .set_power = mmc_set_power,
.ocr_mask = MMC_VDD_28_29 | MMC_VDD_30_31 |
MMC_VDD_32_33 | MMC_VDD_33_34,
.name = "mmcblk",
},
};
+static struct omap_mmc_platform_data *mmc_data[OMAP15XX_NR_MMC];
+
void __init sx1_mmc_init(void)
{
- omap_set_mmc_info(1, &sx1_mmc_data);
+ mmc_data[0] = &mmc1_data;
+ omap1_init_mmc(mmc_data, OMAP15XX_NR_MMC);
}
#else
@@ -118,7 +63,4 @@ void __init sx1_mmc_init(void)
{
}
-void sx1_mmc_slot_cover_handler(void *arg, int state)
-{
-}
#endif
diff --git a/arch/arm/mach-omap1/board-sx1.c b/arch/arm/mach-omap1/board-sx1.c
index 130bcc6fd082..8171fe0ca082 100644
--- a/arch/arm/mach-omap1/board-sx1.c
+++ b/arch/arm/mach-omap1/board-sx1.c
@@ -378,15 +378,6 @@ static struct omap_usb_config sx1_usb_config __initdata = {
.pins[2] = 0,
};
-/*----------- MMC -------------------------*/
-
-static struct omap_mmc_config sx1_mmc_config __initdata = {
- .mmc [0] = {
- .enabled = 1,
- .wire4 = 0,
- },
-};
-
/*----------- LCD -------------------------*/
static struct platform_device sx1_lcd_device = {
@@ -414,7 +405,6 @@ static struct omap_uart_config sx1_uart_config __initdata = {
static struct omap_board_config_kernel sx1_config[] __initdata = {
{ OMAP_TAG_USB, &sx1_usb_config },
- { OMAP_TAG_MMC, &sx1_mmc_config },
{ OMAP_TAG_LCD, &sx1_lcd_config },
{ OMAP_TAG_UART, &sx1_uart_config },
};
@@ -436,14 +426,9 @@ static void __init omap_sx1_init(void)
omap_request_gpio(1); /* A_IRDA_OFF */
omap_request_gpio(11); /* A_SWITCH */
omap_request_gpio(15); /* A_USB_ON */
- omap_set_gpio_direction(1, 0);/* gpio1 -> output */
- omap_set_gpio_direction(11, 0);/* gpio11 -> output */
- omap_set_gpio_direction(15, 0);/* gpio15 -> output */
- /* set GPIO data */
- omap_set_gpio_dataout(1, 1);/*A_IRDA_OFF = 1 */
- omap_set_gpio_dataout(11, 0);/*A_SWITCH = 0 */
- omap_set_gpio_dataout(15, 0);/*A_USB_ON = 0 */
-
+ gpio_direction_output(1, 1); /*A_IRDA_OFF = 1 */
+ gpio_direction_output(11, 0); /*A_SWITCH = 0 */
+ gpio_direction_output(15, 0); /*A_USB_ON = 0 */
}
/*----------------------------------------*/
static void __init omap_sx1_init_irq(void)
diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c
index 45a01311669a..c224f3c64235 100644
--- a/arch/arm/mach-omap1/board-voiceblue.c
+++ b/arch/arm/mach-omap1/board-voiceblue.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/notifier.h>
@@ -140,21 +141,12 @@ static struct omap_usb_config voiceblue_usb_config __initdata = {
.pins[2] = 6,
};
-static struct omap_mmc_config voiceblue_mmc_config __initdata = {
- .mmc[0] = {
- .enabled = 1,
- .power_pin = 2,
- .switch_pin = -1,
- },
-};
-
static struct omap_uart_config voiceblue_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
static struct omap_board_config_kernel voiceblue_config[] = {
{ OMAP_TAG_USB, &voiceblue_usb_config },
- { OMAP_TAG_MMC, &voiceblue_mmc_config },
{ OMAP_TAG_UART, &voiceblue_uart_config },
};
@@ -168,29 +160,27 @@ static void __init voiceblue_init_irq(void)
static void __init voiceblue_init(void)
{
/* Watchdog */
- omap_request_gpio(0);
+ gpio_request(0, "Watchdog");
/* smc91x reset */
- omap_request_gpio(7);
- omap_set_gpio_direction(7, 0);
- omap_set_gpio_dataout(7, 1);
+ gpio_request(7, "SMC91x reset");
+ gpio_direction_output(7, 1);
udelay(2); /* wait at least 100ns */
- omap_set_gpio_dataout(7, 0);
+ gpio_set_value(7, 0);
mdelay(50); /* 50ms until PHY ready */
/* smc91x interrupt pin */
- omap_request_gpio(8);
+ gpio_request(8, "SMC91x irq");
/* 16C554 reset*/
- omap_request_gpio(6);
- omap_set_gpio_direction(6, 0);
- omap_set_gpio_dataout(6, 0);
+ gpio_request(6, "16C554 reset");
+ gpio_direction_output(6, 0);
/* 16C554 interrupt pins */
- omap_request_gpio(12);
- omap_request_gpio(13);
- omap_request_gpio(14);
- omap_request_gpio(15);
- set_irq_type(OMAP_GPIO_IRQ(12), IRQ_TYPE_EDGE_RISING);
- set_irq_type(OMAP_GPIO_IRQ(13), IRQ_TYPE_EDGE_RISING);
- set_irq_type(OMAP_GPIO_IRQ(14), IRQ_TYPE_EDGE_RISING);
- set_irq_type(OMAP_GPIO_IRQ(15), IRQ_TYPE_EDGE_RISING);
+ gpio_request(12, "16C554 irq");
+ gpio_request(13, "16C554 irq");
+ gpio_request(14, "16C554 irq");
+ gpio_request(15, "16C554 irq");
+ set_irq_type(gpio_to_irq(12), IRQ_TYPE_EDGE_RISING);
+ set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING);
+ set_irq_type(gpio_to_irq(14), IRQ_TYPE_EDGE_RISING);
+ set_irq_type(gpio_to_irq(15), IRQ_TYPE_EDGE_RISING);
platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices));
omap_board_config = voiceblue_config;
@@ -244,19 +234,18 @@ static int wdt_gpio_state;
void voiceblue_wdt_enable(void)
{
- omap_set_gpio_direction(0, 0);
- omap_set_gpio_dataout(0, 0);
- omap_set_gpio_dataout(0, 1);
- omap_set_gpio_dataout(0, 0);
+ gpio_direction_output(0, 0);
+ gpio_set_value(0, 1);
+ gpio_set_value(0, 0);
wdt_gpio_state = 0;
}
void voiceblue_wdt_disable(void)
{
- omap_set_gpio_dataout(0, 0);
- omap_set_gpio_dataout(0, 1);
- omap_set_gpio_dataout(0, 0);
- omap_set_gpio_direction(0, 1);
+ gpio_set_value(0, 0);
+ gpio_set_value(0, 1);
+ gpio_set_value(0, 0);
+ gpio_direction_input(0);
}
void voiceblue_wdt_ping(void)
@@ -265,7 +254,7 @@ void voiceblue_wdt_ping(void)
return;
wdt_gpio_state = !wdt_gpio_state;
- omap_set_gpio_dataout(0, wdt_gpio_state);
+ gpio_set_value(0, wdt_gpio_state);
}
void voiceblue_reset(void)
diff --git a/arch/arm/mach-omap1/clock.h b/arch/arm/mach-omap1/clock.h
index 5635b511ab6f..c1dcdf18d8dd 100644
--- a/arch/arm/mach-omap1/clock.h
+++ b/arch/arm/mach-omap1/clock.h
@@ -705,7 +705,6 @@ static struct clk bclk_16xx = {
static struct clk mmc1_ck = {
.name = "mmc_ck",
- .id = 1,
/* Functional clock is direct from ULPD, interface clock is ARMPER */
.parent = &armper_ck.clk,
.rate = 48000000,
@@ -720,7 +719,7 @@ static struct clk mmc1_ck = {
static struct clk mmc2_ck = {
.name = "mmc_ck",
- .id = 2,
+ .id = 1,
/* Functional clock is direct from ULPD, interface clock is ARMPER */
.parent = &armper_ck.clk,
.rate = 48000000,
diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c
index e382b438c64e..77382d8b6b2f 100644
--- a/arch/arm/mach-omap1/devices.c
+++ b/arch/arm/mach-omap1/devices.c
@@ -22,6 +22,7 @@
#include <mach/board.h>
#include <mach/mux.h>
#include <mach/gpio.h>
+#include <mach/mmc.h>
/*-------------------------------------------------------------------------*/
@@ -99,6 +100,95 @@ static inline void omap_init_mbox(void)
static inline void omap_init_mbox(void) { }
#endif
+/*-------------------------------------------------------------------------*/
+
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
+
+static inline void omap1_mmc_mux(struct omap_mmc_platform_data *mmc_controller,
+ int controller_nr)
+{
+ if (controller_nr == 0) {
+ omap_cfg_reg(MMC_CMD);
+ omap_cfg_reg(MMC_CLK);
+ omap_cfg_reg(MMC_DAT0);
+ if (cpu_is_omap1710()) {
+ omap_cfg_reg(M15_1710_MMC_CLKI);
+ omap_cfg_reg(P19_1710_MMC_CMDDIR);
+ omap_cfg_reg(P20_1710_MMC_DATDIR0);
+ }
+ if (mmc_controller->slots[0].wires == 4) {
+ omap_cfg_reg(MMC_DAT1);
+ /* NOTE: DAT2 can be on W10 (here) or M15 */
+ if (!mmc_controller->slots[0].nomux)
+ omap_cfg_reg(MMC_DAT2);
+ omap_cfg_reg(MMC_DAT3);
+ }
+ }
+
+ /* Block 2 is on newer chips, and has many pinout options */
+ if (cpu_is_omap16xx() && controller_nr == 1) {
+ if (!mmc_controller->slots[1].nomux) {
+ omap_cfg_reg(Y8_1610_MMC2_CMD);
+ omap_cfg_reg(Y10_1610_MMC2_CLK);
+ omap_cfg_reg(R18_1610_MMC2_CLKIN);
+ omap_cfg_reg(W8_1610_MMC2_DAT0);
+ if (mmc_controller->slots[1].wires == 4) {
+ omap_cfg_reg(V8_1610_MMC2_DAT1);
+ omap_cfg_reg(W15_1610_MMC2_DAT2);
+ omap_cfg_reg(R10_1610_MMC2_DAT3);
+ }
+
+ /* These are needed for the level shifter */
+ omap_cfg_reg(V9_1610_MMC2_CMDDIR);
+ omap_cfg_reg(V5_1610_MMC2_DATDIR0);
+ omap_cfg_reg(W19_1610_MMC2_DATDIR1);
+ }
+
+ /* Feedback clock must be set on OMAP-1710 MMC2 */
+ if (cpu_is_omap1710())
+ omap_writel(omap_readl(MOD_CONF_CTRL_1) | (1 << 24),
+ MOD_CONF_CTRL_1);
+ }
+}
+
+void __init omap1_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers)
+{
+ int i;
+
+ for (i = 0; i < nr_controllers; i++) {
+ unsigned long base, size;
+ unsigned int irq = 0;
+
+ if (!mmc_data[i])
+ continue;
+
+ omap1_mmc_mux(mmc_data[i], i);
+
+ switch (i) {
+ case 0:
+ base = OMAP1_MMC1_BASE;
+ irq = INT_MMC;
+ break;
+ case 1:
+ if (!cpu_is_omap16xx())
+ return;
+ base = OMAP1_MMC2_BASE;
+ irq = INT_1610_MMC2;
+ break;
+ default:
+ continue;
+ }
+ size = OMAP1_MMC_SIZE;
+
+ omap_mmc_add(i, base, size, irq, mmc_data[i]);
+ };
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
#if defined(CONFIG_OMAP_STI)
#define OMAP1_STI_BASE 0xfffea000
diff --git a/arch/arm/mach-omap1/fpga.c b/arch/arm/mach-omap1/fpga.c
index 04995381aa5c..4f2b8a7adb19 100644
--- a/arch/arm/mach-omap1/fpga.c
+++ b/arch/arm/mach-omap1/fpga.c
@@ -177,9 +177,9 @@ void omap1510_fpga_init_irq(void)
* NOTE: For general GPIO/MPUIO access and interrupts, please see
* gpio.[ch]
*/
- omap_request_gpio(13);
- omap_set_gpio_direction(13, 1);
- set_irq_type(OMAP_GPIO_IRQ(13), IRQ_TYPE_EDGE_RISING);
+ gpio_request(13, "FPGA irq");
+ gpio_direction_input(13);
+ set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING);
set_irq_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux);
}
diff --git a/arch/arm/mach-omap1/id.c b/arch/arm/mach-omap1/id.c
index 13083d7e692d..89bb8756f450 100644
--- a/arch/arm/mach-omap1/id.c
+++ b/arch/arm/mach-omap1/id.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <mach/cpu.h>
#define OMAP_DIE_ID_0 0xfffe1800
#define OMAP_DIE_ID_1 0xfffe1804
@@ -30,6 +31,8 @@ struct omap_id {
u32 type; /* Cpu id bits [31:08], cpu class bits [07:00] */
};
+static unsigned int omap_revision;
+
/* Register values to detect the OMAP version */
static struct omap_id omap_ids[] __initdata = {
{ .jtag_id = 0xb574, .die_rev = 0x2, .omap_id = 0x03310315, .type = 0x03100000},
@@ -53,6 +56,12 @@ static struct omap_id omap_ids[] __initdata = {
{ .jtag_id = 0xb5f7, .die_rev = 0x2, .omap_id = 0x03330100, .type = 0x17100000},
};
+unsigned int omap_rev(void)
+{
+ return omap_revision;
+}
+EXPORT_SYMBOL(omap_rev);
+
/*
* Get OMAP type from PROD_ID.
* 1710 has the PROD_ID in bits 15:00, not in 16:01 as documented in TRM.
@@ -121,17 +130,18 @@ void __init omap_check_revision(void)
omap_id = omap_readl(OMAP32_ID_0);
#ifdef DEBUG
- printk("OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0));
- printk("OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n",
+ printk(KERN_DEBUG "OMAP_DIE_ID_0: 0x%08x\n", omap_readl(OMAP_DIE_ID_0));
+ printk(KERN_DEBUG "OMAP_DIE_ID_1: 0x%08x DIE_REV: %i\n",
omap_readl(OMAP_DIE_ID_1),
(omap_readl(OMAP_DIE_ID_1) >> 17) & 0xf);
- printk("OMAP_PRODUCTION_ID_0: 0x%08x\n", omap_readl(OMAP_PRODUCTION_ID_0));
- printk("OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n",
+ printk(KERN_DEBUG "OMAP_PRODUCTION_ID_0: 0x%08x\n",
+ omap_readl(OMAP_PRODUCTION_ID_0));
+ printk(KERN_DEBUG "OMAP_PRODUCTION_ID_1: 0x%08x JTAG_ID: 0x%04x\n",
omap_readl(OMAP_PRODUCTION_ID_1),
omap_readl(OMAP_PRODUCTION_ID_1) & 0xffff);
- printk("OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0));
- printk("OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1));
- printk("JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev);
+ printk(KERN_DEBUG "OMAP32_ID_0: 0x%08x\n", omap_readl(OMAP32_ID_0));
+ printk(KERN_DEBUG "OMAP32_ID_1: 0x%08x\n", omap_readl(OMAP32_ID_1));
+ printk(KERN_DEBUG "JTAG_ID: 0x%04x DIE_REV: %i\n", jtag_id, die_rev);
#endif
system_serial_high = omap_readl(OMAP_DIE_ID_0);
@@ -140,7 +150,7 @@ void __init omap_check_revision(void)
/* First check only the major version in a safe way */
for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
if (jtag_id == (omap_ids[i].jtag_id)) {
- system_rev = omap_ids[i].type;
+ omap_revision = omap_ids[i].type;
break;
}
}
@@ -148,7 +158,7 @@ void __init omap_check_revision(void)
/* Check if we can find the die revision */
for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
if (jtag_id == omap_ids[i].jtag_id && die_rev == omap_ids[i].die_rev) {
- system_rev = omap_ids[i].type;
+ omap_revision = omap_ids[i].type;
break;
}
}
@@ -158,38 +168,35 @@ void __init omap_check_revision(void)
if (jtag_id == omap_ids[i].jtag_id
&& die_rev == omap_ids[i].die_rev
&& omap_id == omap_ids[i].omap_id) {
- system_rev = omap_ids[i].type;
+ omap_revision = omap_ids[i].type;
break;
}
}
/* Add the cpu class info (7xx, 15xx, 16xx, 24xx) */
- cpu_type = system_rev >> 24;
+ cpu_type = omap_revision >> 24;
switch (cpu_type) {
case 0x07:
- system_rev |= 0x07;
+ omap_revision |= 0x07;
break;
case 0x03:
case 0x15:
- system_rev |= 0x15;
+ omap_revision |= 0x15;
break;
case 0x16:
case 0x17:
- system_rev |= 0x16;
- break;
- case 0x24:
- system_rev |= 0x24;
+ omap_revision |= 0x16;
break;
default:
- printk("Unknown OMAP cpu type: 0x%02x\n", cpu_type);
+ printk(KERN_INFO "Unknown OMAP cpu type: 0x%02x\n", cpu_type);
}
- printk("OMAP%04x", system_rev >> 16);
- if ((system_rev >> 8) & 0xff)
- printk("%x", (system_rev >> 8) & 0xff);
- printk(" revision %i handled as %02xxx id: %08x%08x\n",
- die_rev, system_rev & 0xff, system_serial_low,
+ printk(KERN_INFO "OMAP%04x", omap_revision >> 16);
+ if ((omap_revision >> 8) & 0xff)
+ printk(KERN_INFO "%x", (omap_revision >> 8) & 0xff);
+ printk(KERN_INFO " revision %i handled as %02xxx id: %08x%08x\n",
+ die_rev, omap_revision & 0xff, system_serial_low,
system_serial_high);
}
diff --git a/arch/arm/mach-omap1/leds-h2p2-debug.c b/arch/arm/mach-omap1/leds-h2p2-debug.c
index 71fe2cc7f7cf..17c9d0e04216 100644
--- a/arch/arm/mach-omap1/leds-h2p2-debug.c
+++ b/arch/arm/mach-omap1/leds-h2p2-debug.c
@@ -65,8 +65,8 @@ void h2p2_dbg_leds_event(led_event_t evt)
/* all leds off during suspend or shutdown */
if (! machine_is_omap_perseus2()) {
- omap_set_gpio_dataout(GPIO_TIMER, 0);
- omap_set_gpio_dataout(GPIO_IDLE, 0);
+ gpio_set_value(GPIO_TIMER, 0);
+ gpio_set_value(GPIO_IDLE, 0);
}
__raw_writew(~0, &fpga->leds);
@@ -94,7 +94,7 @@ void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2())
hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
else {
- omap_set_gpio_dataout(GPIO_TIMER, led_state & LED_TIMER_ON);
+ gpio_set_value(GPIO_TIMER, led_state & LED_TIMER_ON);
goto done;
}
@@ -106,7 +106,7 @@ void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2())
hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
else {
- omap_set_gpio_dataout(GPIO_IDLE, 1);
+ gpio_set_value(GPIO_IDLE, 1);
goto done;
}
@@ -116,7 +116,7 @@ void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2())
hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
else {
- omap_set_gpio_dataout(GPIO_IDLE, 0);
+ gpio_set_value(GPIO_IDLE, 0);
goto done;
}
diff --git a/arch/arm/mach-omap1/leds-osk.c b/arch/arm/mach-omap1/leds-osk.c
index 98e789622dfd..499d7ad8697d 100644
--- a/arch/arm/mach-omap1/leds-osk.c
+++ b/arch/arm/mach-omap1/leds-osk.c
@@ -44,8 +44,8 @@ static void mistral_setled(void)
green = 1;
/* else both sides are disabled */
- omap_set_gpio_dataout(GPIO_LED_GREEN, green);
- omap_set_gpio_dataout(GPIO_LED_RED, red);
+ gpio_set_value(GPIO_LED_GREEN, green);
+ gpio_set_value(GPIO_LED_RED, red);
}
#endif
diff --git a/arch/arm/mach-omap1/leds.c b/arch/arm/mach-omap1/leds.c
index 6cdad93c4a00..8cbf2562dcaa 100644
--- a/arch/arm/mach-omap1/leds.c
+++ b/arch/arm/mach-omap1/leds.c
@@ -47,14 +47,14 @@ omap_leds_init(void)
* that's a different kind of LED (just one color at a time).
*/
omap_cfg_reg(P18_1610_GPIO3);
- if (omap_request_gpio(3) == 0)
- omap_set_gpio_direction(3, 0);
+ 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 (omap_request_gpio(OMAP_MPUIO(4)) == 0)
- omap_set_gpio_direction(OMAP_MPUIO(4), 0);
+ 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");
}
diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c
index 770d256c790b..9774c1f5311e 100644
--- a/arch/arm/mach-omap1/pm.c
+++ b/arch/arm/mach-omap1/pm.c
@@ -226,7 +226,8 @@ void omap_pm_suspend(void)
{
unsigned long arg0 = 0, arg1 = 0;
- printk("PM: OMAP%x is trying to enter deep sleep...\n", system_rev);
+ printk(KERN_INFO "PM: OMAP%x is trying to enter deep sleep...\n",
+ omap_rev());
omap_serial_wake_trigger(1);
@@ -421,7 +422,8 @@ void omap_pm_suspend(void)
omap_serial_wake_trigger(0);
- printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev);
+ printk(KERN_INFO "PM: OMAP%x is re-starting from deep sleep...\n",
+ omap_rev());
}
#if defined(DEBUG) && defined(CONFIG_PROC_FS)
diff --git a/arch/arm/mach-omap1/serial.c b/arch/arm/mach-omap1/serial.c
index 528691d5cb51..0002084e0655 100644
--- a/arch/arm/mach-omap1/serial.c
+++ b/arch/arm/mach-omap1/serial.c
@@ -244,22 +244,22 @@ static void __init omap_serial_set_port_wakeup(int gpio_nr)
{
int ret;
- ret = omap_request_gpio(gpio_nr);
+ ret = gpio_request(gpio_nr, "UART wake");
if (ret < 0) {
printk(KERN_ERR "Could not request UART wake GPIO: %i\n",
gpio_nr);
return;
}
- omap_set_gpio_direction(gpio_nr, 1);
- ret = request_irq(OMAP_GPIO_IRQ(gpio_nr), &omap_serial_wake_interrupt,
+ gpio_direction_input(gpio_nr);
+ ret = request_irq(gpio_to_irq(gpio_nr), &omap_serial_wake_interrupt,
IRQF_TRIGGER_RISING, "serial wakeup", NULL);
if (ret) {
- omap_free_gpio(gpio_nr);
+ gpio_free(gpio_nr);
printk(KERN_ERR "No interrupt for UART wake GPIO: %i\n",
gpio_nr);
return;
}
- enable_irq_wake(OMAP_GPIO_IRQ(gpio_nr));
+ enable_irq_wake(gpio_to_irq(gpio_nr));
}
static int __init omap_serial_wakeup_init(void)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 4832fcc7d04a..3754b79092ab 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -55,3 +55,7 @@ config MACH_OMAP_LDP
config MACH_OVERO
bool "Gumstix Overo board"
depends on ARCH_OMAP3 && ARCH_OMAP34XX
+
+config MACH_OMAP3_PANDORA
+ bool "OMAP3 Pandora"
+ depends on ARCH_OMAP3 && ARCH_OMAP34XX \ No newline at end of file
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index c69392372c99..bbd12bc10fdc 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -27,9 +27,15 @@ obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o
# Specific board support
obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o
obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o
-obj-$(CONFIG_MACH_OMAP_2430SDP) += board-2430sdp.o
+obj-$(CONFIG_MACH_OMAP_2430SDP) += board-2430sdp.o \
+ mmc-twl4030.o
obj-$(CONFIG_MACH_OMAP_APOLLON) += board-apollon.o
-obj-$(CONFIG_MACH_OMAP3_BEAGLE) += board-omap3beagle.o
-obj-$(CONFIG_MACH_OMAP_LDP) += board-ldp.o
-obj-$(CONFIG_MACH_OVERO) += board-overo.o
+obj-$(CONFIG_MACH_OMAP3_BEAGLE) += board-omap3beagle.o \
+ mmc-twl4030.o
+obj-$(CONFIG_MACH_OMAP_LDP) += board-ldp.o \
+ mmc-twl4030.o
+obj-$(CONFIG_MACH_OVERO) += board-overo.o \
+ mmc-twl4030.o
+obj-$(CONFIG_MACH_OMAP3_PANDORA) += board-omap3pandora.o \
+ mmc-twl4030.o
diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c
index 24688efaa445..83fa37211d77 100644
--- a/arch/arm/mach-omap2/board-2430sdp.c
+++ b/arch/arm/mach-omap2/board-2430sdp.c
@@ -19,6 +19,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
+#include <linux/i2c/twl4030.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
@@ -35,6 +36,7 @@
#include <mach/common.h>
#include <mach/gpmc.h>
+#include "mmc-twl4030.h"
#define SDP2430_FLASH_CS 0
#define SDP2430_SMC91X_CS 5
@@ -168,13 +170,13 @@ static inline void __init sdp2430_init_smc91x(void)
sdp2430_smc91x_resources[0].end = cs_mem_base + 0x30f;
udelay(100);
- if (omap_request_gpio(OMAP24XX_ETHR_GPIO_IRQ) < 0) {
+ if (gpio_request(OMAP24XX_ETHR_GPIO_IRQ, "SMC91x irq") < 0) {
printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
OMAP24XX_ETHR_GPIO_IRQ);
gpmc_cs_free(eth_cs);
goto out;
}
- omap_set_gpio_direction(OMAP24XX_ETHR_GPIO_IRQ, 1);
+ gpio_direction_input(OMAP24XX_ETHR_GPIO_IRQ);
out:
clk_disable(gpmc_fck);
@@ -197,12 +199,58 @@ static struct omap_board_config_kernel sdp2430_config[] = {
{OMAP_TAG_UART, &sdp2430_uart_config},
};
+
+static struct twl4030_gpio_platform_data sdp2430_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+};
+
+static struct twl4030_platform_data sdp2430_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+
+ /* platform_data for children goes here */
+ .gpio = &sdp2430_gpio_data,
+};
+
+static struct i2c_board_info __initdata sdp2430_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl4030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_24XX_SYS_NIRQ,
+ .platform_data = &sdp2430_twldata,
+ },
+};
+
+static int __init omap2430_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 400, NULL, 0);
+ omap_register_i2c_bus(2, 2600, sdp2430_i2c_boardinfo,
+ ARRAY_SIZE(sdp2430_i2c_boardinfo));
+ return 0;
+}
+
+static struct twl4030_hsmmc_info mmc[] __initdata = {
+ {
+ .mmc = 1,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = -EINVAL,
+ .ext_clock = 1,
+ },
+ {} /* Terminator */
+};
+
static void __init omap_2430sdp_init(void)
{
+ omap2430_i2c_init();
+
platform_add_devices(sdp2430_devices, ARRAY_SIZE(sdp2430_devices));
omap_board_config = sdp2430_config;
omap_board_config_size = ARRAY_SIZE(sdp2430_config);
omap_serial_init();
+ twl4030_mmc_init(mmc);
}
static void __init omap_2430sdp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c
index 989ad152d7f8..bf1e5d32c2a3 100644
--- a/arch/arm/mach-omap2/board-apollon.c
+++ b/arch/arm/mach-omap2/board-apollon.c
@@ -236,13 +236,13 @@ static inline void __init apollon_init_smc91x(void)
udelay(100);
omap_cfg_reg(W4__24XX_GPIO74);
- if (omap_request_gpio(APOLLON_ETHR_GPIO_IRQ) < 0) {
+ if (gpio_request(APOLLON_ETHR_GPIO_IRQ, "SMC91x irq") < 0) {
printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
APOLLON_ETHR_GPIO_IRQ);
gpmc_cs_free(APOLLON_ETH_CS);
goto out;
}
- omap_set_gpio_direction(APOLLON_ETHR_GPIO_IRQ, 1);
+ gpio_direction_input(APOLLON_ETHR_GPIO_IRQ);
out:
clk_disable(gpmc_fck);
@@ -261,16 +261,6 @@ static struct omap_uart_config apollon_uart_config __initdata = {
.enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2),
};
-static struct omap_mmc_config apollon_mmc_config __initdata = {
- .mmc [0] = {
- .enabled = 1,
- .wire4 = 1,
- .wp_pin = -1,
- .power_pin = -1,
- .switch_pin = -1,
- },
-};
-
static struct omap_usb_config apollon_usb_config __initdata = {
.register_dev = 1,
.hmc_mode = 0x14, /* 0:dev 1:host1 2:disable */
@@ -284,7 +274,6 @@ static struct omap_lcd_config apollon_lcd_config __initdata = {
static struct omap_board_config_kernel apollon_config[] = {
{ OMAP_TAG_UART, &apollon_uart_config },
- { OMAP_TAG_MMC, &apollon_mmc_config },
{ OMAP_TAG_USB, &apollon_usb_config },
{ OMAP_TAG_LCD, &apollon_lcd_config },
};
@@ -327,15 +316,15 @@ static void __init apollon_sw_init(void)
/* Enter SW - Y11 */
omap_cfg_reg(Y11_242X_GPIO16);
omap_request_gpio(SW_ENTER_GPIO16);
- omap_set_gpio_direction(SW_ENTER_GPIO16, 1);
+ gpio_direction_input(SW_ENTER_GPIO16);
/* Up SW - AA12 */
omap_cfg_reg(AA12_242X_GPIO17);
omap_request_gpio(SW_UP_GPIO17);
- omap_set_gpio_direction(SW_UP_GPIO17, 1);
+ gpio_direction_input(SW_UP_GPIO17);
/* Down SW - AA8 */
omap_cfg_reg(AA8_242X_GPIO58);
omap_request_gpio(SW_DOWN_GPIO58);
- omap_set_gpio_direction(SW_DOWN_GPIO58, 1);
+ gpio_direction_input(SW_DOWN_GPIO58);
set_irq_type(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), IRQ_TYPE_EDGE_RISING);
if (request_irq(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), &apollon_sw_interrupt,
@@ -359,9 +348,8 @@ static void __init apollon_usb_init(void)
/* USB device */
/* DEVICE_SUSPEND */
omap_cfg_reg(P21_242X_GPIO12);
- omap_request_gpio(12);
- omap_set_gpio_direction(12, 0); /* OUT */
- omap_set_gpio_dataout(12, 0);
+ gpio_request(12, "USB suspend");
+ gpio_direction_output(12, 0);
}
static void __init omap_apollon_init(void)
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 9ba097868e72..3b34c20d1df4 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -41,19 +41,8 @@ static struct omap_uart_config generic_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
-static struct omap_mmc_config generic_mmc_config __initdata = {
- .mmc [0] = {
- .enabled = 0,
- .wire4 = 0,
- .wp_pin = -1,
- .power_pin = -1,
- .switch_pin = -1,
- },
-};
-
static struct omap_board_config_kernel generic_config[] = {
{ OMAP_TAG_UART, &generic_uart_config },
- { OMAP_TAG_MMC, &generic_mmc_config },
};
static void __init omap_generic_init(void)
diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c
index 2fef2c845083..5e9b14675b1e 100644
--- a/arch/arm/mach-omap2/board-h4.c
+++ b/arch/arm/mach-omap2/board-h4.c
@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
#include <linux/input.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -372,31 +373,33 @@ static struct omap_uart_config h4_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
-static struct omap_mmc_config h4_mmc_config __initdata = {
- .mmc [0] = {
- .enabled = 1,
- .wire4 = 1,
- .wp_pin = -1,
- .power_pin = -1,
- .switch_pin = -1,
- },
-};
-
static struct omap_lcd_config h4_lcd_config __initdata = {
.ctrl_name = "internal",
};
static struct omap_board_config_kernel h4_config[] = {
{ OMAP_TAG_UART, &h4_uart_config },
- { OMAP_TAG_MMC, &h4_mmc_config },
{ OMAP_TAG_LCD, &h4_lcd_config },
};
+static struct at24_platform_data m24c01 = {
+ .byte_len = SZ_1K / 8,
+ .page_size = 16,
+};
+
static struct i2c_board_info __initdata h4_i2c_board_info[] = {
{
I2C_BOARD_INFO("isp1301_omap", 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
+ { /* EEPROM on mainboard */
+ I2C_BOARD_INFO("24c01", 0x52),
+ .platform_data = &m24c01,
+ },
+ { /* EEPROM on cpu card */
+ I2C_BOARD_INFO("24c01", 0x57),
+ .platform_data = &m24c01,
+ },
};
static void __init omap_h4_init(void)
diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c
index 1ea59986aa7a..aa6972781e4a 100644
--- a/arch/arm/mach-omap2/board-ldp.c
+++ b/arch/arm/mach-omap2/board-ldp.c
@@ -21,6 +21,7 @@
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
+#include <linux/i2c/twl4030.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
@@ -38,11 +39,69 @@
#include <asm/delay.h>
#include <mach/control.h>
+#include "mmc-twl4030.h"
+
+#define SDP3430_SMC91X_CS 3
+
+static struct resource ldp_smc911x_resources[] = {
+ [0] = {
+ .start = OMAP34XX_ETHR_START,
+ .end = OMAP34XX_ETHR_START + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
+ },
+};
+
+static struct platform_device ldp_smc911x_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ldp_smc911x_resources),
+ .resource = ldp_smc911x_resources,
+};
+
+static struct platform_device *ldp_devices[] __initdata = {
+ &ldp_smc911x_device,
+};
+
+static inline void __init ldp_init_smc911x(void)
+{
+ int eth_cs;
+ unsigned long cs_mem_base;
+ int eth_gpio = 0;
+
+ eth_cs = LDP_SMC911X_CS;
+
+ if (gpmc_cs_request(eth_cs, SZ_16M, &cs_mem_base) < 0) {
+ printk(KERN_ERR "Failed to request GPMC mem for smc911x\n");
+ return;
+ }
+
+ ldp_smc911x_resources[0].start = cs_mem_base + 0x0;
+ ldp_smc911x_resources[0].end = cs_mem_base + 0xf;
+ udelay(100);
+
+ eth_gpio = LDP_SMC911X_GPIO;
+
+ ldp_smc911x_resources[1].start = OMAP_GPIO_IRQ(eth_gpio);
+
+ if (omap_request_gpio(eth_gpio) < 0) {
+ printk(KERN_ERR "Failed to request GPIO%d for smc911x IRQ\n",
+ eth_gpio);
+ return;
+ }
+ gpio_direction_input(eth_gpio);
+}
+
static void __init omap_ldp_init_irq(void)
{
omap2_init_common_hw();
omap_init_irq();
omap_gpio_init();
+ ldp_init_smc911x();
}
static struct omap_uart_config ldp_uart_config __initdata = {
@@ -53,20 +112,56 @@ static struct omap_board_config_kernel ldp_config[] __initdata = {
{ OMAP_TAG_UART, &ldp_uart_config },
};
+static struct twl4030_gpio_platform_data ldp_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+};
+
+static struct twl4030_platform_data ldp_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+
+ /* platform_data for children goes here */
+ .gpio = &ldp_gpio_data,
+};
+
+static struct i2c_board_info __initdata ldp_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl4030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_34XX_SYS_NIRQ,
+ .platform_data = &ldp_twldata,
+ },
+};
+
static int __init omap_i2c_init(void)
{
- omap_register_i2c_bus(1, 2600, NULL, 0);
+ omap_register_i2c_bus(1, 2600, ldp_i2c_boardinfo,
+ ARRAY_SIZE(ldp_i2c_boardinfo));
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
}
+static struct twl4030_hsmmc_info mmc[] __initdata = {
+ {
+ .mmc = 1,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = -EINVAL,
+ },
+ {} /* Terminator */
+};
+
static void __init omap_ldp_init(void)
{
omap_i2c_init();
+ platform_add_devices(ldp_devices, ARRAY_SIZE(ldp_devices));
omap_board_config = ldp_config;
omap_board_config_size = ARRAY_SIZE(ldp_config);
omap_serial_init();
+ twl4030_mmc_init(mmc);
}
static void __init omap_ldp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index baa79674e9d5..9e5ada01b5fa 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -38,7 +38,9 @@
#include <mach/common.h>
#include <mach/gpmc.h>
#include <mach/nand.h>
+#include <mach/mux.h>
+#include "mmc-twl4030.h"
#define GPMC_CS0_BASE 0x60
#define GPMC_CS_SIZE 0x30
@@ -103,6 +105,78 @@ static struct omap_uart_config omap3_beagle_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
+static struct twl4030_hsmmc_info mmc[] = {
+ {
+ .mmc = 1,
+ .wires = 8,
+ .gpio_wp = 29,
+ },
+ {} /* Terminator */
+};
+
+static struct gpio_led gpio_leds[];
+
+static int beagle_twl_gpio_setup(struct device *dev,
+ unsigned gpio, unsigned ngpio)
+{
+ /* gpio + 0 is "mmc0_cd" (input/IRQ) */
+
+ /* REVISIT: need ehci-omap hooks for external VBUS
+ * power switch and overcurrent detect
+ */
+
+ gpio_request(gpio + 1, "EHCI_nOC");
+ gpio_direction_input(gpio + 1);
+
+ /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */
+ gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR");
+ gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1);
+
+ /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */
+ gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
+
+ return 0;
+}
+
+static struct twl4030_gpio_platform_data beagle_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+ .use_leds = true,
+ .pullups = BIT(1),
+ .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13)
+ | BIT(15) | BIT(16) | BIT(17),
+ .setup = beagle_twl_gpio_setup,
+};
+
+static struct twl4030_platform_data beagle_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+
+ /* platform_data for children goes here */
+ .gpio = &beagle_gpio_data,
+};
+
+static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl4030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_34XX_SYS_NIRQ,
+ .platform_data = &beagle_twldata,
+ },
+};
+
+static int __init omap3_beagle_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo,
+ ARRAY_SIZE(beagle_i2c_boardinfo));
+#ifdef CONFIG_I2C2_OMAP_BEAGLE
+ omap_register_i2c_bus(2, 400, NULL, 0);
+#endif
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
static void __init omap3_beagle_init_irq(void)
{
omap2_init_common_hw();
@@ -130,6 +204,11 @@ static struct gpio_led gpio_leds[] = {
.default_trigger = "mmc0",
.gpio = 149,
},
+ {
+ .name = "beagleboard::pmu_stat",
+ .gpio = -EINVAL, /* gets replaced */
+ .active_low = true,
+ },
};
static struct gpio_led_platform_data gpio_led_info = {
@@ -218,11 +297,22 @@ static void __init omap3beagle_flash_init(void)
static void __init omap3_beagle_init(void)
{
+ omap3_beagle_i2c_init();
platform_add_devices(omap3_beagle_devices,
ARRAY_SIZE(omap3_beagle_devices));
omap_board_config = omap3_beagle_config;
omap_board_config_size = ARRAY_SIZE(omap3_beagle_config);
omap_serial_init();
+
+ omap_cfg_reg(AH8_34XX_GPIO29);
+ mmc[0].gpio_cd = gpio + 0;
+ twl4030_mmc_init(mmc);
+
+ omap_cfg_reg(J25_34XX_GPIO170);
+ gpio_request(170, "DVI_nPD");
+ /* REVISIT leave DVI powered down until it's needed ... */
+ gpio_direction_output(170, true);
+
omap3beagle_flash_init();
}
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
new file mode 100644
index 000000000000..b3196107afdb
--- /dev/null
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -0,0 +1,212 @@
+/*
+ * board-omap3pandora.c (Pandora Handheld Console)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/i2c/twl4030.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/board.h>
+#include <mach/common.h>
+#include <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/mcspi.h>
+
+#include "mmc-twl4030.h"
+
+#define OMAP3_PANDORA_TS_GPIO 94
+
+static struct twl4030_hsmmc_info omap3pandora_mmc[] = {
+ {
+ .mmc = 1,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = 126,
+ .ext_clock = 0,
+ },
+ {
+ .mmc = 2,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = 127,
+ .ext_clock = 1,
+ },
+ {} /* Terminator */
+};
+
+static struct omap_uart_config omap3pandora_uart_config __initdata = {
+ .enabled_uarts = (1 << 2), /* UART3 */
+};
+
+static int omap3pandora_twl_gpio_setup(struct device *dev,
+ unsigned gpio, unsigned ngpio)
+{
+ /* gpio + {0,1} is "mmc{0,1}_cd" (input/IRQ) */
+ omap3pandora_mmc[0].gpio_cd = gpio + 0;
+ omap3pandora_mmc[1].gpio_cd = gpio + 1;
+ twl4030_mmc_init(omap3pandora_mmc);
+
+ return 0;
+}
+
+static struct twl4030_gpio_platform_data omap3pandora_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+ .setup = omap3pandora_twl_gpio_setup,
+};
+
+static struct twl4030_usb_data omap3pandora_usb_data = {
+ .usb_mode = T2_USB_MODE_ULPI,
+};
+
+static struct twl4030_platform_data omap3pandora_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+ .gpio = &omap3pandora_gpio_data,
+ .usb = &omap3pandora_usb_data,
+};
+
+static struct i2c_board_info __initdata omap3pandora_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("tps65950", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_34XX_SYS_NIRQ,
+ .platform_data = &omap3pandora_twldata,
+ },
+};
+
+static int __init omap3pandora_i2c_init(void)
+{
+ omap_register_i2c_bus(1, 2600, omap3pandora_i2c_boardinfo,
+ ARRAY_SIZE(omap3pandora_i2c_boardinfo));
+ /* i2c2 pins are not connected */
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ return 0;
+}
+
+static void __init omap3pandora_init_irq(void)
+{
+ omap2_init_common_hw();
+ omap_init_irq();
+ omap_gpio_init();
+}
+
+static void __init omap3pandora_ads7846_init(void)
+{
+ int gpio = OMAP3_PANDORA_TS_GPIO;
+ int ret;
+
+ ret = gpio_request(gpio, "ads7846_pen_down");
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to request GPIO %d for "
+ "ads7846 pen down IRQ\n", gpio);
+ return;
+ }
+
+ gpio_direction_input(gpio);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+ return !gpio_get_value(OMAP3_PANDORA_TS_GPIO);
+}
+
+static struct ads7846_platform_data ads7846_config = {
+ .x_max = 0x0fff,
+ .y_max = 0x0fff,
+ .x_plate_ohms = 180,
+ .pressure_max = 255,
+ .debounce_max = 10,
+ .debounce_tol = 3,
+ .debounce_rep = 1,
+ .get_pendown_state = ads7846_get_pendown_state,
+ .keep_vref_on = 1,
+};
+
+static struct omap2_mcspi_device_config ads7846_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+static struct spi_board_info omap3pandora_spi_board_info[] __initdata = {
+ {
+ .modalias = "ads7846",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .controller_data = &ads7846_mcspi_config,
+ .irq = OMAP_GPIO_IRQ(OMAP3_PANDORA_TS_GPIO),
+ .platform_data = &ads7846_config,
+ }
+};
+
+static struct platform_device omap3pandora_lcd_device = {
+ .name = "pandora_lcd",
+ .id = -1,
+};
+
+static struct omap_lcd_config omap3pandora_lcd_config __initdata = {
+ .ctrl_name = "internal",
+};
+
+static struct omap_board_config_kernel omap3pandora_config[] __initdata = {
+ { OMAP_TAG_UART, &omap3pandora_uart_config },
+ { OMAP_TAG_LCD, &omap3pandora_lcd_config },
+};
+
+static struct platform_device *omap3pandora_devices[] __initdata = {
+ &omap3pandora_lcd_device,
+};
+
+static void __init omap3pandora_init(void)
+{
+ omap3pandora_i2c_init();
+ platform_add_devices(omap3pandora_devices,
+ ARRAY_SIZE(omap3pandora_devices));
+ omap_board_config = omap3pandora_config;
+ omap_board_config_size = ARRAY_SIZE(omap3pandora_config);
+ omap_serial_init();
+ spi_register_board_info(omap3pandora_spi_board_info,
+ ARRAY_SIZE(omap3pandora_spi_board_info));
+ omap3pandora_ads7846_init();
+}
+
+static void __init omap3pandora_map_io(void)
+{
+ omap2_set_globals_343x();
+ omap2_map_common_io();
+}
+
+MACHINE_START(OMAP3_PANDORA, "Pandora Handheld Console")
+ .phys_io = 0x48000000,
+ .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc,
+ .boot_params = 0x80000100,
+ .map_io = omap3pandora_map_io,
+ .init_irq = omap3pandora_init_irq,
+ .init_machine = omap3pandora_init,
+ .timer = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index e09aa59a399c..82b3dc557c96 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -26,6 +26,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -44,6 +45,8 @@
#include <mach/hardware.h>
#include <mach/nand.h>
+#include "mmc-twl4030.h"
+
#define NAND_BLOCK_SIZE SZ_128K
#define GPMC_CS0_BASE 0x60
#define GPMC_CS_SIZE 0x30
@@ -139,8 +142,31 @@ static struct omap_uart_config overo_uart_config __initdata = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
+static struct twl4030_gpio_platform_data overo_gpio_data = {
+ .gpio_base = OMAP_MAX_GPIO_LINES,
+ .irq_base = TWL4030_GPIO_IRQ_BASE,
+ .irq_end = TWL4030_GPIO_IRQ_END,
+};
+
+static struct twl4030_platform_data overo_twldata = {
+ .irq_base = TWL4030_IRQ_BASE,
+ .irq_end = TWL4030_IRQ_END,
+ .gpio = &overo_gpio_data,
+};
+
+static struct i2c_board_info __initdata overo_i2c_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl4030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = INT_34XX_SYS_NIRQ,
+ .platform_data = &overo_twldata,
+ },
+};
+
static int __init overo_i2c_init(void)
{
+ omap_register_i2c_bus(1, 2600, overo_i2c_boardinfo,
+ ARRAY_SIZE(overo_i2c_boardinfo));
/* i2c2 pins are used for gpio */
omap_register_i2c_bus(3, 400, NULL, 0);
return 0;
@@ -171,6 +197,22 @@ static struct platform_device *overo_devices[] __initdata = {
&overo_lcd_device,
};
+static struct twl4030_hsmmc_info mmc[] __initdata = {
+ {
+ .mmc = 1,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = -EINVAL,
+ },
+ {
+ .mmc = 2,
+ .wires = 4,
+ .gpio_cd = -EINVAL,
+ .gpio_wp = -EINVAL,
+ },
+ {} /* Terminator */
+};
+
static void __init overo_init(void)
{
overo_i2c_init();
@@ -178,6 +220,7 @@ static void __init overo_init(void)
omap_board_config = overo_config;
omap_board_config_size = ARRAY_SIZE(overo_config);
omap_serial_init();
+ twl4030_mmc_init(mmc);
overo_flash_init();
if ((gpio_request(OVERO_GPIO_W2W_NRESET,
diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h
index 242a19d86ccd..ff6cd14d254d 100644
--- a/arch/arm/mach-omap2/clock24xx.h
+++ b/arch/arm/mach-omap2/clock24xx.h
@@ -2522,7 +2522,6 @@ static struct clk usbhs_ick = {
static struct clk mmchs1_ick = {
.name = "mmchs_ick",
- .id = 1,
.parent = &l4_ck,
.flags = CLOCK_IN_OMAP243X,
.clkdm_name = "core_l4_clkdm",
@@ -2533,7 +2532,6 @@ static struct clk mmchs1_ick = {
static struct clk mmchs1_fck = {
.name = "mmchs_fck",
- .id = 1,
.parent = &func_96m_ck,
.flags = CLOCK_IN_OMAP243X,
.clkdm_name = "core_l3_clkdm",
@@ -2544,7 +2542,7 @@ static struct clk mmchs1_fck = {
static struct clk mmchs2_ick = {
.name = "mmchs_ick",
- .id = 2,
+ .id = 1,
.parent = &l4_ck,
.flags = CLOCK_IN_OMAP243X,
.clkdm_name = "core_l4_clkdm",
@@ -2555,7 +2553,7 @@ static struct clk mmchs2_ick = {
static struct clk mmchs2_fck = {
.name = "mmchs_fck",
- .id = 2,
+ .id = 1,
.parent = &func_96m_ck,
.flags = CLOCK_IN_OMAP243X,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, OMAP24XX_CM_FCLKEN2),
@@ -2595,7 +2593,6 @@ static struct clk mdm_intc_ick = {
static struct clk mmchsdb1_fck = {
.name = "mmchsdb_fck",
- .id = 1,
.parent = &func_32k_ck,
.flags = CLOCK_IN_OMAP243X,
.clkdm_name = "core_l4_clkdm",
@@ -2606,7 +2603,7 @@ static struct clk mmchsdb1_fck = {
static struct clk mmchsdb2_fck = {
.name = "mmchsdb_fck",
- .id = 2,
+ .id = 1,
.parent = &func_32k_ck,
.flags = CLOCK_IN_OMAP243X,
.clkdm_name = "core_l4_clkdm",
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c
index 084e11082f80..31bb7010bd48 100644
--- a/arch/arm/mach-omap2/clock34xx.c
+++ b/arch/arm/mach-omap2/clock34xx.c
@@ -475,7 +475,7 @@ int __init omap2_clk_init(void)
* Update this if there are further clock changes between ES2
* and production parts
*/
- if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0)) {
+ if (omap_rev() == OMAP3430_REV_ES1_0) {
/* No 3430ES1-only rates exist, so no RATE_IN_3430ES1 */
cpu_clkflg |= CLOCK_IN_OMAP3430ES1;
} else {
diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h
index c38a8a09692f..a826094d89b5 100644
--- a/arch/arm/mach-omap2/clock34xx.h
+++ b/arch/arm/mach-omap2/clock34xx.h
@@ -1374,7 +1374,7 @@ static struct clk core_96m_fck = {
static struct clk mmchs3_fck = {
.name = "mmchs_fck",
- .id = 3,
+ .id = 2,
.parent = &core_96m_fck,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1),
.enable_bit = OMAP3430ES2_EN_MMC3_SHIFT,
@@ -1385,7 +1385,7 @@ static struct clk mmchs3_fck = {
static struct clk mmchs2_fck = {
.name = "mmchs_fck",
- .id = 2,
+ .id = 1,
.parent = &core_96m_fck,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1),
.enable_bit = OMAP3430_EN_MMC2_SHIFT,
@@ -1406,7 +1406,6 @@ static struct clk mspro_fck = {
static struct clk mmchs1_fck = {
.name = "mmchs_fck",
- .id = 1,
.parent = &core_96m_fck,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1),
.enable_bit = OMAP3430_EN_MMC1_SHIFT,
@@ -1722,7 +1721,7 @@ static struct clk usbtll_ick = {
static struct clk mmchs3_ick = {
.name = "mmchs_ick",
- .id = 3,
+ .id = 2,
.parent = &core_l4_ick,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
.enable_bit = OMAP3430ES2_EN_MMC3_SHIFT,
@@ -1774,7 +1773,7 @@ static struct clk des2_ick = {
static struct clk mmchs2_ick = {
.name = "mmchs_ick",
- .id = 2,
+ .id = 1,
.parent = &core_l4_ick,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
.enable_bit = OMAP3430_EN_MMC2_SHIFT,
@@ -1785,7 +1784,6 @@ static struct clk mmchs2_ick = {
static struct clk mmchs1_ick = {
.name = "mmchs_ick",
- .id = 1,
.parent = &core_l4_ick,
.enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1),
.enable_bit = OMAP3430_EN_MMC1_SHIFT,
@@ -2280,8 +2278,8 @@ static struct clk wkup_32k_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio1_fck = {
- .name = "gpio1_fck",
+static struct clk gpio1_dbck = {
+ .name = "gpio1_dbck",
.parent = &wkup_32k_fck,
.enable_reg = OMAP_CM_REGADDR(WKUP_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO1_SHIFT,
@@ -2527,8 +2525,8 @@ static struct clk per_32k_alwon_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio6_fck = {
- .name = "gpio6_fck",
+static struct clk gpio6_dbck = {
+ .name = "gpio6_dbck",
.parent = &per_32k_alwon_fck,
.enable_reg = OMAP_CM_REGADDR(OMAP3430_PER_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO6_SHIFT,
@@ -2537,8 +2535,8 @@ static struct clk gpio6_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio5_fck = {
- .name = "gpio5_fck",
+static struct clk gpio5_dbck = {
+ .name = "gpio5_dbck",
.parent = &per_32k_alwon_fck,
.enable_reg = OMAP_CM_REGADDR(OMAP3430_PER_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO5_SHIFT,
@@ -2547,8 +2545,8 @@ static struct clk gpio5_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio4_fck = {
- .name = "gpio4_fck",
+static struct clk gpio4_dbck = {
+ .name = "gpio4_dbck",
.parent = &per_32k_alwon_fck,
.enable_reg = OMAP_CM_REGADDR(OMAP3430_PER_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO4_SHIFT,
@@ -2557,8 +2555,8 @@ static struct clk gpio4_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio3_fck = {
- .name = "gpio3_fck",
+static struct clk gpio3_dbck = {
+ .name = "gpio3_dbck",
.parent = &per_32k_alwon_fck,
.enable_reg = OMAP_CM_REGADDR(OMAP3430_PER_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO3_SHIFT,
@@ -2567,8 +2565,8 @@ static struct clk gpio3_fck = {
.recalc = &followparent_recalc,
};
-static struct clk gpio2_fck = {
- .name = "gpio2_fck",
+static struct clk gpio2_dbck = {
+ .name = "gpio2_dbck",
.parent = &per_32k_alwon_fck,
.enable_reg = OMAP_CM_REGADDR(OMAP3430_PER_MOD, CM_FCLKEN),
.enable_bit = OMAP3430_EN_GPIO2_SHIFT,
@@ -3170,7 +3168,7 @@ static struct clk *onchip_34xx_clks[] __initdata = {
&usim_fck,
&gpt1_fck,
&wkup_32k_fck,
- &gpio1_fck,
+ &gpio1_dbck,
&wdt2_fck,
&wkup_l4_ick,
&usim_ick,
@@ -3192,11 +3190,11 @@ static struct clk *onchip_34xx_clks[] __initdata = {
&gpt8_fck,
&gpt9_fck,
&per_32k_alwon_fck,
- &gpio6_fck,
- &gpio5_fck,
- &gpio4_fck,
- &gpio3_fck,
- &gpio2_fck,
+ &gpio6_dbck,
+ &gpio5_dbck,
+ &gpio4_dbck,
+ &gpio3_dbck,
+ &gpio2_dbck,
&wdt3_fck,
&per_l4_ick,
&gpio6_ick,
diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 90af2ac469aa..9d7216ff6c9f 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -14,16 +14,19 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
+#include <mach/control.h>
#include <mach/tc.h>
#include <mach/board.h>
#include <mach/mux.h>
#include <mach/gpio.h>
#include <mach/eac.h>
+#include <mach/mmc.h>
#if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE)
#define OMAP2_MBOX_BASE IO_ADDRESS(OMAP24XX_MAILBOX_BASE)
@@ -295,6 +298,171 @@ static void omap_init_sha1_md5(void)
static inline void omap_init_sha1_md5(void) { }
#endif
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_ARCH_OMAP3
+
+#define MMCHS_SYSCONFIG 0x0010
+#define MMCHS_SYSCONFIG_SWRESET (1 << 1)
+#define MMCHS_SYSSTATUS 0x0014
+#define MMCHS_SYSSTATUS_RESETDONE (1 << 0)
+
+static struct platform_device dummy_pdev = {
+ .dev = {
+ .bus = &platform_bus_type,
+ },
+};
+
+/**
+ * omap_hsmmc_reset() - Full reset of each HS-MMC controller
+ *
+ * Ensure that each MMC controller is fully reset. Controllers
+ * left in an unknown state (by bootloader) may prevent retention
+ * or OFF-mode. This is especially important in cases where the
+ * MMC driver is not enabled, _or_ built as a module.
+ *
+ * In order for reset to work, interface, functional and debounce
+ * clocks must be enabled. The debounce clock comes from func_32k_clk
+ * and is not under SW control, so we only enable i- and f-clocks.
+ **/
+static void __init omap_hsmmc_reset(void)
+{
+ u32 i, nr_controllers = cpu_is_omap34xx() ? OMAP34XX_NR_MMC :
+ OMAP24XX_NR_MMC;
+
+ for (i = 0; i < nr_controllers; i++) {
+ u32 v, base = 0;
+ struct clk *iclk, *fclk;
+ struct device *dev = &dummy_pdev.dev;
+
+ switch (i) {
+ case 0:
+ base = OMAP2_MMC1_BASE;
+ break;
+ case 1:
+ base = OMAP2_MMC2_BASE;
+ break;
+ case 2:
+ base = OMAP3_MMC3_BASE;
+ break;
+ }
+
+ dummy_pdev.id = i;
+ iclk = clk_get(dev, "mmchs_ick");
+ if (iclk && clk_enable(iclk))
+ iclk = NULL;
+
+ fclk = clk_get(dev, "mmchs_fck");
+ if (fclk && clk_enable(fclk))
+ fclk = NULL;
+
+ if (!iclk || !fclk) {
+ printk(KERN_WARNING
+ "%s: Unable to enable clocks for MMC%d, "
+ "cannot reset.\n", __func__, i);
+ break;
+ }
+
+ omap_writel(MMCHS_SYSCONFIG_SWRESET, base + MMCHS_SYSCONFIG);
+ v = omap_readl(base + MMCHS_SYSSTATUS);
+ while (!(omap_readl(base + MMCHS_SYSSTATUS) &
+ MMCHS_SYSSTATUS_RESETDONE))
+ cpu_relax();
+
+ if (fclk) {
+ clk_disable(fclk);
+ clk_put(fclk);
+ }
+ if (iclk) {
+ clk_disable(iclk);
+ clk_put(iclk);
+ }
+ }
+}
+#else
+static inline void omap_hsmmc_reset(void) {}
+#endif
+
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
+ defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
+
+static inline void omap2_mmc_mux(struct omap_mmc_platform_data *mmc_controller,
+ int controller_nr)
+{
+ if (cpu_is_omap2420() && controller_nr == 0) {
+ omap_cfg_reg(H18_24XX_MMC_CMD);
+ omap_cfg_reg(H15_24XX_MMC_CLKI);
+ omap_cfg_reg(G19_24XX_MMC_CLKO);
+ omap_cfg_reg(F20_24XX_MMC_DAT0);
+ omap_cfg_reg(F19_24XX_MMC_DAT_DIR0);
+ omap_cfg_reg(G18_24XX_MMC_CMD_DIR);
+ if (mmc_controller->slots[0].wires == 4) {
+ omap_cfg_reg(H14_24XX_MMC_DAT1);
+ omap_cfg_reg(E19_24XX_MMC_DAT2);
+ omap_cfg_reg(D19_24XX_MMC_DAT3);
+ omap_cfg_reg(E20_24XX_MMC_DAT_DIR1);
+ omap_cfg_reg(F18_24XX_MMC_DAT_DIR2);
+ omap_cfg_reg(E18_24XX_MMC_DAT_DIR3);
+ }
+
+ /*
+ * Use internal loop-back in MMC/SDIO Module Input Clock
+ * selection
+ */
+ if (mmc_controller->slots[0].internal_clock) {
+ u32 v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
+ v |= (1 << 24);
+ omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0);
+ }
+ }
+}
+
+void __init omap2_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers)
+{
+ int i;
+
+ for (i = 0; i < nr_controllers; i++) {
+ unsigned long base, size;
+ unsigned int irq = 0;
+
+ if (!mmc_data[i])
+ continue;
+
+ omap2_mmc_mux(mmc_data[i], i);
+
+ switch (i) {
+ case 0:
+ base = OMAP2_MMC1_BASE;
+ irq = INT_24XX_MMC_IRQ;
+ break;
+ case 1:
+ base = OMAP2_MMC2_BASE;
+ irq = INT_24XX_MMC2_IRQ;
+ break;
+ case 2:
+ if (!cpu_is_omap34xx())
+ return;
+ base = OMAP3_MMC3_BASE;
+ irq = INT_34XX_MMC3_IRQ;
+ break;
+ default:
+ continue;
+ }
+
+ if (cpu_is_omap2420())
+ size = OMAP2420_MMC_SIZE;
+ else
+ size = HSMMC_SIZE;
+
+ omap_mmc_add(i, base, size, irq, mmc_data[i]);
+ };
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
#if defined(CONFIG_HDQ_MASTER_OMAP) || defined(CONFIG_HDQ_MASTER_OMAP_MODULE)
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
#define OMAP_HDQ_BASE 0x480B2000
@@ -334,6 +502,7 @@ static int __init omap2_init_devices(void)
/* please keep these calls, and their implementations above,
* in alphabetical order so they're easier to sort through.
*/
+ omap_hsmmc_reset();
omap_init_mbox();
omap_init_mcspi();
omap_hdq_init();
diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
index bf45ff39a7b5..b0f8e7d62798 100644
--- a/arch/arm/mach-omap2/id.c
+++ b/arch/arm/mach-omap2/id.c
@@ -22,40 +22,15 @@
#include <mach/control.h>
#include <mach/cpu.h>
-static u32 class;
-static void __iomem *tap_base;
-static u16 tap_prod_id;
-
-#define OMAP_TAP_IDCODE 0x0204
-#define OMAP_TAP_DIE_ID_0 0x0218
-#define OMAP_TAP_DIE_ID_1 0x021C
-#define OMAP_TAP_DIE_ID_2 0x0220
-#define OMAP_TAP_DIE_ID_3 0x0224
-
-/* system_rev fields for OMAP2 processors:
- * CPU id bits [31:16],
- * CPU device type [15:12], (unprg,normal,POP)
- * CPU revision [11:08]
- * CPU class bits [07:00]
- */
-
-struct omap_id {
- u16 hawkeye; /* Silicon type (Hawkeye id) */
- u8 dev; /* Device type from production_id reg */
- u32 type; /* combined type id copied to system_rev */
-};
+static struct omap_chip_id omap_chip;
+static unsigned int omap_revision;
-/* Register values to detect the OMAP version */
-static struct omap_id omap_ids[] __initdata = {
- { .hawkeye = 0xb5d9, .dev = 0x0, .type = 0x24200000 },
- { .hawkeye = 0xb5d9, .dev = 0x1, .type = 0x24201000 },
- { .hawkeye = 0xb5d9, .dev = 0x2, .type = 0x24202000 },
- { .hawkeye = 0xb5d9, .dev = 0x4, .type = 0x24220000 },
- { .hawkeye = 0xb5d9, .dev = 0x8, .type = 0x24230000 },
- { .hawkeye = 0xb68a, .dev = 0x0, .type = 0x24300000 },
-};
-static struct omap_chip_id omap_chip;
+unsigned int omap_rev(void)
+{
+ return omap_revision;
+}
+EXPORT_SYMBOL(omap_rev);
/**
* omap_chip_is - test whether currently running OMAP matches a chip type
@@ -70,135 +45,41 @@ int omap_chip_is(struct omap_chip_id oci)
}
EXPORT_SYMBOL(omap_chip_is);
-static u32 __init read_tap_reg(int reg)
-{
- unsigned int regval = 0;
- u32 cpuid;
-
- /* Reading the IDCODE register on 3430 ES1 results in a
- * data abort as the register is not exposed on the OCP
- * Hence reading the Cortex Rev
- */
- cpuid = read_cpuid(CPUID_ID);
-
- /* If the processor type is Cortex-A8 and the revision is 0x0
- * it means its Cortex r0p0 which is 3430 ES1
- */
- if ((((cpuid >> 4) & 0xFFF) == 0xC08) && ((cpuid & 0xF) == 0x0)) {
-
- if (reg == tap_prod_id) {
- regval = 0x000F00F0;
- goto out;
- }
-
- switch (reg) {
- case OMAP_TAP_IDCODE : regval = 0x0B7AE02F; break;
- /* Making DevType as 0xF in ES1 to differ from ES2 */
- case OMAP_TAP_DIE_ID_0: regval = 0x01000000; break;
- case OMAP_TAP_DIE_ID_1: regval = 0x1012d687; break;
- case OMAP_TAP_DIE_ID_2: regval = 0x00000000; break;
- case OMAP_TAP_DIE_ID_3: regval = 0x2d2c0000; break;
- }
- } else
- regval = __raw_readl(tap_base + reg);
-
-out:
- return regval;
-
-}
+/*----------------------------------------------------------------------------*/
-/*
- * _set_system_rev - set the system_rev global based on current OMAP chip type
- *
- * Set the system_rev global. This is primarily used by the cpu_is_omapxxxx()
- * macros.
- */
-static void __init _set_system_rev(u32 type, u8 rev)
-{
- u32 i, ctrl_status;
-
- /*
- * system_rev encoding is as follows
- * system_rev & 0xff000000 -> Omap Class (24xx/34xx)
- * system_rev & 0xfff00000 -> Omap Sub Class (242x/343x)
- * system_rev & 0xffff0000 -> Omap type (2420/2422/2423/2430/3430)
- * system_rev & 0x0000f000 -> Silicon revision (ES1, ES2 )
- * system_rev & 0x00000700 -> Device Type ( EMU/HS/GP/BAD )
- * system_rev & 0x000000c0 -> IDCODE revision[6:7]
- * system_rev & 0x0000003f -> sys_boot[0:5]
- */
- /* Embedding the ES revision info in type field */
- system_rev = type;
- /* Also add IDCODE revision info only two lower bits */
- system_rev |= ((rev & 0x3) << 6);
-
- /* Add in the device type and sys_boot fields (see above) */
- if (cpu_is_omap24xx()) {
- i = OMAP24XX_CONTROL_STATUS;
- } else if (cpu_is_omap343x()) {
- i = OMAP343X_CONTROL_STATUS;
- } else {
- printk(KERN_ERR "id: unknown CPU type\n");
- BUG();
- }
- ctrl_status = omap_ctrl_readl(i);
- system_rev |= (ctrl_status & (OMAP2_SYSBOOT_5_MASK |
- OMAP2_SYSBOOT_4_MASK |
- OMAP2_SYSBOOT_3_MASK |
- OMAP2_SYSBOOT_2_MASK |
- OMAP2_SYSBOOT_1_MASK |
- OMAP2_SYSBOOT_0_MASK));
- system_rev |= (ctrl_status & OMAP2_DEVICETYPE_MASK);
-}
-
-
-/*
- * _set_omap_chip - set the omap_chip global based on OMAP chip type
- *
- * Build the omap_chip bits. This variable is used by powerdomain and
- * clockdomain code to indicate whether structures are applicable for
- * the current OMAP chip type by ANDing it against a 'platform' bitfield
- * in the structure.
- */
-static void __init _set_omap_chip(void)
-{
- if (cpu_is_omap343x()) {
-
- omap_chip.oc = CHIP_IS_OMAP3430;
- if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0))
- omap_chip.oc |= CHIP_IS_OMAP3430ES1;
- else if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0))
- omap_chip.oc |= CHIP_IS_OMAP3430ES2;
-
- } else if (cpu_is_omap243x()) {
-
- /* Currently only supports 2430ES2.1 and 2430-all */
- omap_chip.oc |= CHIP_IS_OMAP2430;
-
- } else if (cpu_is_omap242x()) {
-
- /* Currently only supports 2420ES2.1.1 and 2420-all */
- omap_chip.oc |= CHIP_IS_OMAP2420;
+#define OMAP_TAP_IDCODE 0x0204
+#define OMAP_TAP_DIE_ID_0 0x0218
+#define OMAP_TAP_DIE_ID_1 0x021C
+#define OMAP_TAP_DIE_ID_2 0x0220
+#define OMAP_TAP_DIE_ID_3 0x0224
- } else {
+#define read_tap_reg(reg) __raw_readl(tap_base + (reg))
- /* Current CPU not supported by this code. */
- printk(KERN_WARNING "OMAP chip type code does not yet support "
- "this CPU type.\n");
- WARN_ON(1);
+struct omap_id {
+ u16 hawkeye; /* Silicon type (Hawkeye id) */
+ u8 dev; /* Device type from production_id reg */
+ u32 type; /* Combined type id copied to omap_revision */
+};
- }
+/* Register values to detect the OMAP version */
+static struct omap_id omap_ids[] __initdata = {
+ { .hawkeye = 0xb5d9, .dev = 0x0, .type = 0x24200024 },
+ { .hawkeye = 0xb5d9, .dev = 0x1, .type = 0x24201024 },
+ { .hawkeye = 0xb5d9, .dev = 0x2, .type = 0x24202024 },
+ { .hawkeye = 0xb5d9, .dev = 0x4, .type = 0x24220024 },
+ { .hawkeye = 0xb5d9, .dev = 0x8, .type = 0x24230024 },
+ { .hawkeye = 0xb68a, .dev = 0x0, .type = 0x24300024 },
+};
-}
+static void __iomem *tap_base;
+static u16 tap_prod_id;
-void __init omap2_check_revision(void)
+void __init omap24xx_check_revision(void)
{
int i, j;
- u32 idcode;
- u32 prod_id;
+ u32 idcode, prod_id;
u16 hawkeye;
- u8 dev_type;
- u8 rev;
+ u8 dev_type, rev;
idcode = read_tap_reg(OMAP_TAP_IDCODE);
prod_id = read_tap_reg(tap_prod_id);
@@ -220,18 +101,6 @@ void __init omap2_check_revision(void)
pr_debug("OMAP_TAP_PROD_ID_0: 0x%08x DEV_TYPE: %i\n",
prod_id, dev_type);
- /*
- * Detection for 34xx ES2.0 and above can be done with just
- * hawkeye and rev. See TRM 1.5.2 Device Identification.
- * Note that rev cannot be used directly as ES1.0 uses value 0.
- */
- if (hawkeye == 0xb7ae) {
- system_rev = 0x34300000 | ((1 + rev) << 12);
- pr_info("OMAP%04x ES2.%i\n", system_rev >> 16, rev);
- _set_omap_chip();
- return;
- }
-
/* Check hawkeye ids */
for (i = 0; i < ARRAY_SIZE(omap_ids); i++) {
if (hawkeye == omap_ids[i].hawkeye)
@@ -255,23 +124,115 @@ void __init omap2_check_revision(void)
j = i;
}
- _set_system_rev(omap_ids[j].type, rev);
+ pr_info("OMAP%04x", omap_rev() >> 16);
+ if ((omap_rev() >> 8) & 0x0f)
+ pr_info("ES%x", (omap_rev() >> 12) & 0xf);
+ pr_info("\n");
+}
- _set_omap_chip();
+void __init omap34xx_check_revision(void)
+{
+ u32 cpuid, idcode;
+ u16 hawkeye;
+ u8 rev;
+ char *rev_name = "ES1.0";
- pr_info("OMAP%04x", system_rev >> 16);
- if ((system_rev >> 8) & 0x0f)
- pr_info("ES%x", (system_rev >> 12) & 0xf);
- pr_info("\n");
+ /*
+ * We cannot access revision registers on ES1.0.
+ * If the processor type is Cortex-A8 and the revision is 0x0
+ * it means its Cortex r0p0 which is 3430 ES1.0.
+ */
+ cpuid = read_cpuid(CPUID_ID);
+ if ((((cpuid >> 4) & 0xfff) == 0xc08) && ((cpuid & 0xf) == 0x0)) {
+ omap_revision = OMAP3430_REV_ES1_0;
+ goto out;
+ }
+ /*
+ * Detection for 34xx ES2.0 and above can be done with just
+ * hawkeye and rev. See TRM 1.5.2 Device Identification.
+ * Note that rev does not map directly to our defined processor
+ * revision numbers as ES1.0 uses value 0.
+ */
+ idcode = read_tap_reg(OMAP_TAP_IDCODE);
+ hawkeye = (idcode >> 12) & 0xffff;
+ rev = (idcode >> 28) & 0xff;
+
+ if (hawkeye == 0xb7ae) {
+ switch (rev) {
+ case 0:
+ omap_revision = OMAP3430_REV_ES2_0;
+ rev_name = "ES2.0";
+ break;
+ case 2:
+ omap_revision = OMAP3430_REV_ES2_1;
+ rev_name = "ES2.1";
+ break;
+ case 3:
+ omap_revision = OMAP3430_REV_ES3_0;
+ rev_name = "ES3.0";
+ break;
+ default:
+ /* Use the latest known revision as default */
+ omap_revision = OMAP3430_REV_ES3_0;
+ rev_name = "Unknown revision\n";
+ }
+ }
+
+out:
+ pr_info("OMAP%04x %s\n", omap_rev() >> 16, rev_name);
}
+/*
+ * Try to detect the exact revision of the omap we're running on
+ */
+void __init omap2_check_revision(void)
+{
+ /*
+ * At this point we have an idea about the processor revision set
+ * earlier with omap2_set_globals_tap().
+ */
+ if (cpu_is_omap24xx())
+ omap24xx_check_revision();
+ else if (cpu_is_omap34xx())
+ omap34xx_check_revision();
+ else
+ pr_err("OMAP revision unknown, please fix!\n");
+
+ /*
+ * OK, now we know the exact revision. Initialize omap_chip bits
+ * for powerdowmain and clockdomain code.
+ */
+ if (cpu_is_omap243x()) {
+ /* Currently only supports 2430ES2.1 and 2430-all */
+ omap_chip.oc |= CHIP_IS_OMAP2430;
+ } else if (cpu_is_omap242x()) {
+ /* Currently only supports 2420ES2.1.1 and 2420-all */
+ omap_chip.oc |= CHIP_IS_OMAP2420;
+ } else if (cpu_is_omap343x()) {
+ omap_chip.oc = CHIP_IS_OMAP3430;
+ if (omap_rev() == OMAP3430_REV_ES1_0)
+ omap_chip.oc |= CHIP_IS_OMAP3430ES1;
+ else if (omap_rev() > OMAP3430_REV_ES1_0)
+ omap_chip.oc |= CHIP_IS_OMAP3430ES2;
+ } else {
+ pr_err("Uninitialized omap_chip, please fix!\n");
+ }
+}
+
+/*
+ * Set up things for map_io and processor detection later on. Gets called
+ * pretty much first thing from board init. For multi-omap, this gets
+ * cpu_is_omapxxxx() working accurately enough for map_io. Then we'll try to
+ * detect the exact revision later on in omap2_detect_revision() once map_io
+ * is done.
+ */
void __init omap2_set_globals_tap(struct omap_globals *omap2_globals)
{
- class = omap2_globals->class;
+ omap_revision = omap2_globals->class;
tap_base = omap2_globals->tap;
- if (class == 0x3430)
+ if (cpu_is_omap34xx())
tap_prod_id = 0x0210;
else
tap_prod_id = 0x0208;
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index c40fc378a251..636e2821af7d 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -23,6 +23,7 @@
#define INTC_REVISION 0x0000
#define INTC_SYSCONFIG 0x0010
#define INTC_SYSSTATUS 0x0014
+#define INTC_SIR 0x0040
#define INTC_CONTROL 0x0048
#define INTC_MIR_CLEAR0 0x0088
#define INTC_MIR_SET0 0x008c
@@ -60,6 +61,30 @@ static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg)
return __raw_readl(bank->base_reg + reg);
}
+static int previous_irq;
+
+/*
+ * On 34xx we can get occasional spurious interrupts if the ack from
+ * an interrupt handler does not get posted before we unmask. Warn about
+ * the interrupt handlers that need to flush posted writes.
+ */
+static int omap_check_spurious(unsigned int irq)
+{
+ u32 sir, spurious;
+
+ sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
+ spurious = sir >> 6;
+
+ if (spurious > 1) {
+ printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
+ "posted write for irq %i\n",
+ irq, sir, previous_irq);
+ return spurious;
+ }
+
+ return 0;
+}
+
/* XXX: FIQ and additional INTC support (only MPU at the moment) */
static void omap_ack_irq(unsigned int irq)
{
@@ -70,6 +95,20 @@ static void omap_mask_irq(unsigned int irq)
{
int offset = irq & (~(IRQ_BITS_PER_REG - 1));
+ if (cpu_is_omap34xx()) {
+ int spurious = 0;
+
+ /*
+ * INT_34XX_GPT12_IRQ is also the spurious irq. Maybe because
+ * it is the highest irq number?
+ */
+ if (irq == INT_34XX_GPT12_IRQ)
+ spurious = omap_check_spurious(irq);
+
+ if (!spurious)
+ previous_irq = irq;
+ }
+
irq &= (IRQ_BITS_PER_REG - 1);
intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_SET0 + offset);
diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
new file mode 100644
index 000000000000..437f52073f6e
--- /dev/null
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -0,0 +1,408 @@
+/*
+ * linux/arch/arm/mach-omap2/mmc-twl4030.c
+ *
+ * Copyright (C) 2007-2008 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/hardware.h>
+#include <mach/control.h>
+#include <mach/mmc.h>
+#include <mach/board.h>
+
+#include "mmc-twl4030.h"
+
+#if defined(CONFIG_TWL4030_CORE) && \
+ (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
+
+#define LDO_CLR 0x00
+#define VSEL_S2_CLR 0x40
+
+#define VMMC1_DEV_GRP 0x27
+#define VMMC1_CLR 0x00
+#define VMMC1_315V 0x03
+#define VMMC1_300V 0x02
+#define VMMC1_285V 0x01
+#define VMMC1_185V 0x00
+#define VMMC1_DEDICATED 0x2A
+
+#define VMMC2_DEV_GRP 0x2B
+#define VMMC2_CLR 0x40
+#define VMMC2_315V 0x0c
+#define VMMC2_300V 0x0b
+#define VMMC2_285V 0x0a
+#define VMMC2_260V 0x08
+#define VMMC2_185V 0x06
+#define VMMC2_DEDICATED 0x2E
+
+#define VMMC_DEV_GRP_P1 0x20
+
+static u16 control_pbias_offset;
+static u16 control_devconf1_offset;
+
+#define HSMMC_NAME_LEN 9
+
+static struct twl_mmc_controller {
+ struct omap_mmc_platform_data *mmc;
+ u8 twl_vmmc_dev_grp;
+ u8 twl_mmc_dedicated;
+ char name[HSMMC_NAME_LEN];
+} hsmmc[] = {
+ {
+ .twl_vmmc_dev_grp = VMMC1_DEV_GRP,
+ .twl_mmc_dedicated = VMMC1_DEDICATED,
+ },
+ {
+ .twl_vmmc_dev_grp = VMMC2_DEV_GRP,
+ .twl_mmc_dedicated = VMMC2_DEDICATED,
+ },
+};
+
+static int twl_mmc_card_detect(int irq)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
+ struct omap_mmc_platform_data *mmc;
+
+ mmc = hsmmc[i].mmc;
+ if (!mmc)
+ continue;
+ if (irq != mmc->slots[0].card_detect_irq)
+ continue;
+
+ /* NOTE: assumes card detect signal is active-low */
+ return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
+ }
+ return -ENOSYS;
+}
+
+static int twl_mmc_get_ro(struct device *dev, int slot)
+{
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ /* NOTE: assumes write protect signal is active-high */
+ return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
+}
+
+/*
+ * MMC Slot Initialization.
+ */
+static int twl_mmc_late_init(struct device *dev)
+{
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+ int ret = 0;
+ int i;
+
+ ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
+ if (ret)
+ goto done;
+ ret = gpio_direction_input(mmc->slots[0].switch_pin);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
+ if (hsmmc[i].name == mmc->slots[0].name) {
+ hsmmc[i].mmc = mmc;
+ break;
+ }
+ }
+
+ return 0;
+
+err:
+ gpio_free(mmc->slots[0].switch_pin);
+done:
+ mmc->slots[0].card_detect_irq = 0;
+ mmc->slots[0].card_detect = NULL;
+
+ dev_err(dev, "err %d configuring card detect\n", ret);
+ return ret;
+}
+
+static void twl_mmc_cleanup(struct device *dev)
+{
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ gpio_free(mmc->slots[0].switch_pin);
+}
+
+#ifdef CONFIG_PM
+
+static int twl_mmc_suspend(struct device *dev, int slot)
+{
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ disable_irq(mmc->slots[0].card_detect_irq);
+ return 0;
+}
+
+static int twl_mmc_resume(struct device *dev, int slot)
+{
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ enable_irq(mmc->slots[0].card_detect_irq);
+ return 0;
+}
+
+#else
+#define twl_mmc_suspend NULL
+#define twl_mmc_resume NULL
+#endif
+
+/*
+ * Sets the MMC voltage in twl4030
+ */
+static int twl_mmc_set_voltage(struct twl_mmc_controller *c, int vdd)
+{
+ int ret;
+ u8 vmmc, dev_grp_val;
+
+ switch (1 << vdd) {
+ case MMC_VDD_35_36:
+ case MMC_VDD_34_35:
+ case MMC_VDD_33_34:
+ case MMC_VDD_32_33:
+ case MMC_VDD_31_32:
+ case MMC_VDD_30_31:
+ if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP)
+ vmmc = VMMC1_315V;
+ else
+ vmmc = VMMC2_315V;
+ break;
+ case MMC_VDD_29_30:
+ if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP)
+ vmmc = VMMC1_315V;
+ else
+ vmmc = VMMC2_300V;
+ break;
+ case MMC_VDD_27_28:
+ case MMC_VDD_26_27:
+ if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP)
+ vmmc = VMMC1_285V;
+ else
+ vmmc = VMMC2_285V;
+ break;
+ case MMC_VDD_25_26:
+ case MMC_VDD_24_25:
+ case MMC_VDD_23_24:
+ case MMC_VDD_22_23:
+ case MMC_VDD_21_22:
+ case MMC_VDD_20_21:
+ if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP)
+ vmmc = VMMC1_285V;
+ else
+ vmmc = VMMC2_260V;
+ break;
+ case MMC_VDD_165_195:
+ if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP)
+ vmmc = VMMC1_185V;
+ else
+ vmmc = VMMC2_185V;
+ break;
+ default:
+ vmmc = 0;
+ break;
+ }
+
+ if (vmmc)
+ dev_grp_val = VMMC_DEV_GRP_P1; /* Power up */
+ else
+ dev_grp_val = LDO_CLR; /* Power down */
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ dev_grp_val, c->twl_vmmc_dev_grp);
+ if (ret)
+ return ret;
+
+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+ vmmc, c->twl_mmc_dedicated);
+
+ return ret;
+}
+
+static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ u32 reg;
+ int ret = 0;
+ struct twl_mmc_controller *c = &hsmmc[0];
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ if (power_on) {
+ if (cpu_is_omap2430()) {
+ reg = omap_ctrl_readl(OMAP243X_CONTROL_DEVCONF1);
+ if ((1 << vdd) >= MMC_VDD_30_31)
+ reg |= OMAP243X_MMC1_ACTIVE_OVERWRITE;
+ else
+ reg &= ~OMAP243X_MMC1_ACTIVE_OVERWRITE;
+ omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1);
+ }
+
+ if (mmc->slots[0].internal_clock) {
+ reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
+ reg |= OMAP2_MMCSDIO1ADPCLKISEL;
+ omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0);
+ }
+
+ reg = omap_ctrl_readl(control_pbias_offset);
+ reg |= OMAP2_PBIASSPEEDCTRL0;
+ reg &= ~OMAP2_PBIASLITEPWRDNZ0;
+ omap_ctrl_writel(reg, control_pbias_offset);
+
+ ret = twl_mmc_set_voltage(c, vdd);
+
+ /* 100ms delay required for PBIAS configuration */
+ msleep(100);
+ reg = omap_ctrl_readl(control_pbias_offset);
+ reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0);
+ if ((1 << vdd) <= MMC_VDD_165_195)
+ reg &= ~OMAP2_PBIASLITEVMODE0;
+ else
+ reg |= OMAP2_PBIASLITEVMODE0;
+ omap_ctrl_writel(reg, control_pbias_offset);
+ } else {
+ reg = omap_ctrl_readl(control_pbias_offset);
+ reg &= ~OMAP2_PBIASLITEPWRDNZ0;
+ omap_ctrl_writel(reg, control_pbias_offset);
+
+ ret = twl_mmc_set_voltage(c, 0);
+
+ /* 100ms delay required for PBIAS configuration */
+ msleep(100);
+ reg = omap_ctrl_readl(control_pbias_offset);
+ reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 |
+ OMAP2_PBIASLITEVMODE0);
+ omap_ctrl_writel(reg, control_pbias_offset);
+ }
+
+ return ret;
+}
+
+static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vdd)
+{
+ int ret;
+ struct twl_mmc_controller *c = &hsmmc[1];
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+
+ if (power_on) {
+ if (mmc->slots[0].internal_clock) {
+ u32 reg;
+
+ reg = omap_ctrl_readl(control_devconf1_offset);
+ reg |= OMAP2_MMCSDIO2ADPCLKISEL;
+ omap_ctrl_writel(reg, control_devconf1_offset);
+ }
+ ret = twl_mmc_set_voltage(c, vdd);
+ } else {
+ ret = twl_mmc_set_voltage(c, 0);
+ }
+
+ return ret;
+}
+
+static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
+
+void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
+{
+ struct twl4030_hsmmc_info *c;
+ int nr_hsmmc = ARRAY_SIZE(hsmmc_data);
+
+ if (cpu_is_omap2430()) {
+ control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE;
+ control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1;
+ nr_hsmmc = 2;
+ } else {
+ control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE;
+ control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1;
+ }
+
+ for (c = controllers; c->mmc; c++) {
+ struct twl_mmc_controller *twl = hsmmc + c->mmc - 1;
+ struct omap_mmc_platform_data *mmc = hsmmc_data[c->mmc - 1];
+
+ if (!c->mmc || c->mmc > nr_hsmmc) {
+ pr_debug("MMC%d: no such controller\n", c->mmc);
+ continue;
+ }
+ if (mmc) {
+ pr_debug("MMC%d: already configured\n", c->mmc);
+ continue;
+ }
+
+ mmc = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL);
+ if (!mmc) {
+ pr_err("Cannot allocate memory for mmc device!\n");
+ return;
+ }
+
+ sprintf(twl->name, "mmc%islot%i", c->mmc, 1);
+ mmc->slots[0].name = twl->name;
+ mmc->nr_slots = 1;
+ mmc->slots[0].ocr_mask = MMC_VDD_165_195 |
+ MMC_VDD_26_27 | MMC_VDD_27_28 |
+ MMC_VDD_29_30 |
+ MMC_VDD_30_31 | MMC_VDD_31_32;
+ mmc->slots[0].wires = c->wires;
+ mmc->slots[0].internal_clock = !c->ext_clock;
+ mmc->dma_mask = 0xffffffff;
+
+ /* note: twl4030 card detect GPIOs normally switch VMMCx ... */
+ if (gpio_is_valid(c->gpio_cd)) {
+ mmc->init = twl_mmc_late_init;
+ mmc->cleanup = twl_mmc_cleanup;
+ mmc->suspend = twl_mmc_suspend;
+ mmc->resume = twl_mmc_resume;
+
+ mmc->slots[0].switch_pin = c->gpio_cd;
+ mmc->slots[0].card_detect_irq = gpio_to_irq(c->gpio_cd);
+ mmc->slots[0].card_detect = twl_mmc_card_detect;
+ } else
+ mmc->slots[0].switch_pin = -EINVAL;
+
+ /* write protect normally uses an OMAP gpio */
+ if (gpio_is_valid(c->gpio_wp)) {
+ gpio_request(c->gpio_wp, "mmc_wp");
+ gpio_direction_input(c->gpio_wp);
+
+ mmc->slots[0].gpio_wp = c->gpio_wp;
+ mmc->slots[0].get_ro = twl_mmc_get_ro;
+ } else
+ mmc->slots[0].gpio_wp = -EINVAL;
+
+ /* NOTE: we assume OMAP's MMC1 and MMC2 use
+ * the TWL4030's VMMC1 and VMMC2, respectively;
+ * and that OMAP's MMC3 isn't used.
+ */
+
+ switch (c->mmc) {
+ case 1:
+ mmc->slots[0].set_power = twl_mmc1_set_power;
+ break;
+ case 2:
+ mmc->slots[0].set_power = twl_mmc2_set_power;
+ break;
+ default:
+ pr_err("MMC%d configuration not supported!\n", c->mmc);
+ continue;
+ }
+ hsmmc_data[c->mmc - 1] = mmc;
+ }
+
+ omap2_init_mmc(hsmmc_data, OMAP34XX_NR_MMC);
+}
+
+#endif
diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h
new file mode 100644
index 000000000000..e1c8076400ca
--- /dev/null
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -0,0 +1,29 @@
+/*
+ * MMC definitions for OMAP2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct twl4030_hsmmc_info {
+ u8 mmc; /* controller 1/2/3 */
+ u8 wires; /* 1/4/8 wires */
+ int gpio_cd; /* or -EINVAL */
+ int gpio_wp; /* or -EINVAL */
+ int ext_clock:1; /* use external pin for input clock */
+};
+
+#if defined(CONFIG_TWL4030_CORE) && \
+ (defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
+ defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
+
+void twl4030_mmc_init(struct twl4030_hsmmc_info *);
+
+#else
+
+static inline void twl4030_mmc_init(struct twl4030_hsmmc_info *info)
+{
+}
+
+#endif
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index b1393673d95d..dacb41f130c0 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -203,6 +203,15 @@ MUX_CFG_24XX("AE9_2430_USB0HS_NXT", 0x13D, 0, 0, 0, 1)
MUX_CFG_24XX("AC7_2430_USB0HS_DATA7", 0x13E, 0, 0, 0, 1)
/* 2430 McBSP */
+MUX_CFG_24XX("AD6_2430_MCBSP_CLKS", 0x011E, 0, 0, 0, 1)
+
+MUX_CFG_24XX("AB2_2430_MCBSP1_CLKR", 0x011A, 0, 0, 0, 1)
+MUX_CFG_24XX("AD5_2430_MCBSP1_FSR", 0x011B, 0, 0, 0, 1)
+MUX_CFG_24XX("AA1_2430_MCBSP1_DX", 0x011C, 0, 0, 0, 1)
+MUX_CFG_24XX("AF3_2430_MCBSP1_DR", 0x011D, 0, 0, 0, 1)
+MUX_CFG_24XX("AB3_2430_MCBSP1_FSX", 0x011F, 0, 0, 0, 1)
+MUX_CFG_24XX("Y9_2430_MCBSP1_CLKX", 0x0120, 0, 0, 0, 1)
+
MUX_CFG_24XX("AC10_2430_MCBSP2_FSX", 0x012E, 1, 0, 0, 1)
MUX_CFG_24XX("AD16_2430_MCBSP2_CLX", 0x012F, 1, 0, 0, 1)
MUX_CFG_24XX("AE13_2430_MCBSP2_DX", 0x0130, 1, 0, 0, 1)
@@ -211,6 +220,31 @@ MUX_CFG_24XX("AC10_2430_MCBSP2_FSX_OFF",0x012E, 0, 0, 0, 1)
MUX_CFG_24XX("AD16_2430_MCBSP2_CLX_OFF",0x012F, 0, 0, 0, 1)
MUX_CFG_24XX("AE13_2430_MCBSP2_DX_OFF", 0x0130, 0, 0, 0, 1)
MUX_CFG_24XX("AD13_2430_MCBSP2_DR_OFF", 0x0131, 0, 0, 0, 1)
+
+MUX_CFG_24XX("AC9_2430_MCBSP3_CLKX", 0x0103, 0, 0, 0, 1)
+MUX_CFG_24XX("AE4_2430_MCBSP3_FSX", 0x0104, 0, 0, 0, 1)
+MUX_CFG_24XX("AE2_2430_MCBSP3_DR", 0x0105, 0, 0, 0, 1)
+MUX_CFG_24XX("AF4_2430_MCBSP3_DX", 0x0106, 0, 0, 0, 1)
+
+MUX_CFG_24XX("N3_2430_MCBSP4_CLKX", 0x010B, 1, 0, 0, 1)
+MUX_CFG_24XX("AD23_2430_MCBSP4_DR", 0x010C, 1, 0, 0, 1)
+MUX_CFG_24XX("AB25_2430_MCBSP4_DX", 0x010D, 1, 0, 0, 1)
+MUX_CFG_24XX("AC25_2430_MCBSP4_FSX", 0x010E, 1, 0, 0, 1)
+
+MUX_CFG_24XX("AE16_2430_MCBSP5_CLKX", 0x00ED, 1, 0, 0, 1)
+MUX_CFG_24XX("AF12_2430_MCBSP5_FSX", 0x00ED, 1, 0, 0, 1)
+MUX_CFG_24XX("K7_2430_MCBSP5_DX", 0x00EF, 1, 0, 0, 1)
+MUX_CFG_24XX("M1_2430_MCBSP5_DR", 0x00F0, 1, 0, 0, 1)
+
+/* 2430 MCSPI1 */
+MUX_CFG_24XX("Y18_2430_MCSPI1_CLK", 0x010F, 0, 0, 0, 1)
+MUX_CFG_24XX("AD15_2430_MCSPI1_SIMO", 0x0110, 0, 0, 0, 1)
+MUX_CFG_24XX("AE17_2430_MCSPI1_SOMI", 0x0111, 0, 0, 0, 1)
+MUX_CFG_24XX("U1_2430_MCSPI1_CS0", 0x0112, 0, 0, 0, 1)
+
+/* Touchscreen GPIO */
+MUX_CFG_24XX("AF19_2430_GPIO_85", 0x0113, 3, 0, 0, 1)
+
};
#define OMAP24XX_PINS_SZ ARRAY_SIZE(omap24xx_pins)
@@ -417,6 +451,14 @@ MUX_CFG_34XX("AD2_3430_USB3FS_PHY_MM3_TXDAT", 0x188,
MUX_CFG_34XX("AC1_3430_USB3FS_PHY_MM3_TXEN_N", 0x18a,
OMAP34XX_MUX_MODE6 | OMAP34XX_PIN_OUTPUT)
+
+/* 34XX GPIO - bidirectional, unless the name has an "_OUT" suffix.
+ * No internal pullup/pulldown without "_UP" or "_DOWN" suffix.
+ */
+MUX_CFG_34XX("AH8_34XX_GPIO29", 0x5fa,
+ OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT)
+MUX_CFG_34XX("J25_34XX_GPIO170", 0x1c6,
+ OMAP34XX_MUX_MODE4 | OMAP34XX_PIN_INPUT)
};
#define OMAP34XX_PINS_SZ ARRAY_SIZE(omap34xx_pins)
@@ -452,7 +494,7 @@ static void __init_or_module omap2_cfg_debug(const struct pin_config *cfg, u16 r
#endif
#ifdef CONFIG_ARCH_OMAP24XX
-int __init_or_module omap24xx_cfg_reg(const struct pin_config *cfg)
+static int __init_or_module omap24xx_cfg_reg(const struct pin_config *cfg)
{
static DEFINE_SPINLOCK(mux_spin_lock);
unsigned long flags;
diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c
index 10ef464d6be7..15e509013def 100644
--- a/arch/arm/mach-omap2/usb-tusb6010.c
+++ b/arch/arm/mach-omap2/usb-tusb6010.c
@@ -12,11 +12,11 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/usb/musb.h>
#include <mach/gpmc.h>
-#include <mach/gpio.h>
#include <mach/mux.h>
@@ -292,12 +292,12 @@ tusb6010_setup_interface(struct musb_hdrc_platform_data *data,
);
/* IRQ */
- status = omap_request_gpio(irq);
+ status = gpio_request(irq, "TUSB6010 irq");
if (status < 0) {
printk(error, 3, status);
return status;
}
- omap_set_gpio_direction(irq, 1);
+ gpio_direction_input(irq);
tusb_resources[2].start = irq + IH_GPIO_BASE;
/* set up memory timings ... can speed them up later */
diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile
index 3d4a1bc12355..edc38e2c856f 100644
--- a/arch/arm/mach-orion5x/Makefile
+++ b/arch/arm/mach-orion5x/Makefile
@@ -1,4 +1,4 @@
-obj-y += common.o addr-map.o pci.o gpio.o irq.o mpp.o
+obj-y += common.o addr-map.o pci.o irq.o mpp.o
obj-$(CONFIG_MACH_DB88F5281) += db88f5281-setup.o
obj-$(CONFIG_MACH_RD88F5182) += rd88f5182-setup.o
obj-$(CONFIG_MACH_KUROBOX_PRO) += kurobox_pro-setup.o
diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c
index 437065c25c9c..0a623379789f 100644
--- a/arch/arm/mach-orion5x/common.c
+++ b/arch/arm/mach-orion5x/common.c
@@ -72,6 +72,7 @@ void __init orion5x_map_io(void)
****************************************************************************/
static struct orion_ehci_data orion5x_ehci_data = {
.dram = &orion5x_mbus_dram_info,
+ .phy_version = EHCI_PHY_ORION,
};
static u64 ehci_dmamask = 0xffffffffUL;
diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h
index a000c7c6ee96..798b9a5e3da9 100644
--- a/arch/arm/mach-orion5x/common.h
+++ b/arch/arm/mach-orion5x/common.h
@@ -51,13 +51,6 @@ int orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys);
struct pci_bus *orion5x_pci_sys_scan_bus(int nr, struct pci_sys_data *sys);
int orion5x_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin);
-/*
- * Valid GPIO pins according to MPP setup, used by machine-setup.
- * (/mach-orion/gpio.c).
- */
-void orion5x_gpio_set_valid(unsigned pin, int valid);
-void gpio_display(void); /* debug */
-
struct machine_desc;
struct meminfo;
struct tag;
diff --git a/arch/arm/mach-orion5x/dns323-setup.c b/arch/arm/mach-orion5x/dns323-setup.c
index 3e66098340a5..0722d6510df1 100644
--- a/arch/arm/mach-orion5x/dns323-setup.c
+++ b/arch/arm/mach-orion5x/dns323-setup.c
@@ -21,6 +21,7 @@
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <linux/i2c.h>
+#include <linux/ata_platform.h>
#include <asm/mach-types.h>
#include <asm/gpio.h>
#include <asm/mach/arch.h>
@@ -64,9 +65,21 @@ static struct hw_pci dns323_pci __initdata = {
.map_irq = dns323_pci_map_irq,
};
+static int __init dns323_dev_id(void)
+{
+ u32 dev, rev;
+
+ orion5x_pcie_id(&dev, &rev);
+
+ return dev;
+}
+
static int __init dns323_pci_init(void)
{
- if (machine_is_dns323())
+ /* The 5182 doesn't really use it's PCI bus, and initialising PCI
+ * gets in the way of initialising the SATA controller.
+ */
+ if (machine_is_dns323() && dns323_dev_id() != MV88F5182_DEV_ID)
pci_common_init(&dns323_pci);
return 0;
@@ -75,14 +88,6 @@ static int __init dns323_pci_init(void)
subsys_initcall(dns323_pci_init);
/****************************************************************************
- * Ethernet
- */
-
-static struct mv643xx_eth_platform_data dns323_eth_data = {
- .phy_addr = MV643XX_ETH_PHY_ADDR(8),
-};
-
-/****************************************************************************
* 8MiB NOR flash (Spansion S29GL064M90TFIR4)
*
* Layout as used by D-Link:
@@ -143,6 +148,90 @@ static struct platform_device dns323_nor_flash = {
};
/****************************************************************************
+ * Ethernet
+ */
+
+static struct mv643xx_eth_platform_data dns323_eth_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+/* dns323_parse_hex_*() taken from tsx09-common.c; should a common copy of these
+ * functions be kept somewhere?
+ */
+static int __init dns323_parse_hex_nibble(char n)
+{
+ if (n >= '0' && n <= '9')
+ return n - '0';
+
+ if (n >= 'A' && n <= 'F')
+ return n - 'A' + 10;
+
+ if (n >= 'a' && n <= 'f')
+ return n - 'a' + 10;
+
+ return -1;
+}
+
+static int __init dns323_parse_hex_byte(const char *b)
+{
+ int hi;
+ int lo;
+
+ hi = dns323_parse_hex_nibble(b[0]);
+ lo = dns323_parse_hex_nibble(b[1]);
+
+ if (hi < 0 || lo < 0)
+ return -1;
+
+ return (hi << 4) | lo;
+}
+
+static int __init dns323_read_mac_addr(void)
+{
+ u_int8_t addr[6];
+ int i;
+ char *mac_page;
+
+ /* MAC address is stored as a regular ol' string in /dev/mtdblock4
+ * (0x007d0000-0x00800000) starting at offset 196480 (0x2ff80).
+ */
+ mac_page = ioremap(DNS323_NOR_BOOT_BASE + 0x7d0000 + 196480, 1024);
+ if (!mac_page)
+ return -ENOMEM;
+
+ /* Sanity check the string we're looking at */
+ for (i = 0; i < 5; i++) {
+ if (*(mac_page + (i * 3) + 2) != ':') {
+ goto error_fail;
+ }
+ }
+
+ for (i = 0; i < 6; i++) {
+ int byte;
+
+ byte = dns323_parse_hex_byte(mac_page + (i * 3));
+ if (byte < 0) {
+ goto error_fail;
+ }
+
+ addr[i] = byte;
+ }
+
+ iounmap(mac_page);
+ printk("DNS323: Found ethernet MAC address: ");
+ for (i = 0; i < 6; i++)
+ printk("%.2x%s", addr[i], (i < 5) ? ":" : ".\n");
+
+ memcpy(dns323_eth_data.mac_addr, addr, 6);
+
+ return 0;
+
+error_fail:
+ iounmap(mac_page);
+ return -EINVAL;
+}
+
+/****************************************************************************
* GPIO LEDs (simple - doesn't use hardware blinking support)
*/
@@ -207,10 +296,17 @@ static struct platform_device dns323_button_device = {
},
};
+/*****************************************************************************
+ * SATA
+ */
+static struct mv_sata_platform_data dns323_sata_data = {
+ .n_ports = 2,
+};
+
/****************************************************************************
* General Setup
*/
-static struct orion5x_mpp_mode dns323_mpp_modes[] __initdata = {
+static struct orion5x_mpp_mode dns323_mv88f5181_mpp_modes[] __initdata = {
{ 0, MPP_PCIE_RST_OUTn },
{ 1, MPP_GPIO }, /* right amber LED (sata ch0) */
{ 2, MPP_GPIO }, /* left amber LED (sata ch1) */
@@ -234,6 +330,30 @@ static struct orion5x_mpp_mode dns323_mpp_modes[] __initdata = {
{ -1 },
};
+static struct orion5x_mpp_mode dns323_mv88f5182_mpp_modes[] __initdata = {
+ { 0, MPP_UNUSED },
+ { 1, MPP_GPIO }, /* right amber LED (sata ch0) */
+ { 2, MPP_GPIO }, /* left amber LED (sata ch1) */
+ { 3, MPP_UNUSED },
+ { 4, MPP_GPIO }, /* power button LED */
+ { 5, MPP_GPIO }, /* power button LED */
+ { 6, MPP_GPIO }, /* GMT G751-2f overtemp */
+ { 7, MPP_GPIO }, /* M41T80 nIRQ/OUT/SQW */
+ { 8, MPP_GPIO }, /* triggers power off */
+ { 9, MPP_GPIO }, /* power button switch */
+ { 10, MPP_GPIO }, /* reset button switch */
+ { 11, MPP_UNUSED },
+ { 12, MPP_SATA_LED },
+ { 13, MPP_SATA_LED },
+ { 14, MPP_SATA_LED },
+ { 15, MPP_SATA_LED },
+ { 16, MPP_UNUSED },
+ { 17, MPP_UNUSED },
+ { 18, MPP_UNUSED },
+ { 19, MPP_UNUSED },
+ { -1 },
+};
+
/*
* On the DNS-323 the following devices are attached via I2C:
*
@@ -264,16 +384,15 @@ static void __init dns323_init(void)
/* Setup basic Orion functions. Need to be called early. */
orion5x_init();
- orion5x_mpp_conf(dns323_mpp_modes);
- writel(0, MPP_DEV_CTRL); /* DEV_D[31:16] */
-
- /*
- * Configure peripherals.
+ /* Just to be tricky, the 5182 has a completely different
+ * set of MPP modes to the 5181.
*/
- orion5x_ehci0_init();
- orion5x_eth_init(&dns323_eth_data);
- orion5x_i2c_init();
- orion5x_uart0_init();
+ if (dns323_dev_id() == MV88F5182_DEV_ID)
+ orion5x_mpp_conf(dns323_mv88f5182_mpp_modes);
+ else {
+ orion5x_mpp_conf(dns323_mv88f5181_mpp_modes);
+ writel(0, MPP_DEV_CTRL); /* DEV_D[31:16] */
+ }
/* setup flash mapping
* CS3 holds a 8 MB Spansion S29GL064M90TFIR4
@@ -288,6 +407,23 @@ static void __init dns323_init(void)
i2c_register_board_info(0, dns323_i2c_devices,
ARRAY_SIZE(dns323_i2c_devices));
+ /*
+ * Configure peripherals.
+ */
+ if (dns323_read_mac_addr() < 0)
+ printk("DNS323: Failed to read MAC address\n");
+
+ orion5x_ehci0_init();
+ orion5x_eth_init(&dns323_eth_data);
+ orion5x_i2c_init();
+ orion5x_uart0_init();
+
+ /* The 5182 has it's SATA controller on-chip, and needs it's own little
+ * init routine.
+ */
+ if (dns323_dev_id() == MV88F5182_DEV_ID)
+ orion5x_sata_init(&dns323_sata_data);
+
/* register dns323 specific power-off method */
if (gpio_request(DNS323_GPIO_POWER_OFF, "POWEROFF") != 0 ||
gpio_direction_output(DNS323_GPIO_POWER_OFF, 0) != 0)
diff --git a/arch/arm/mach-orion5x/gpio.c b/arch/arm/mach-orion5x/gpio.c
deleted file mode 100644
index f99d08811e5a..000000000000
--- a/arch/arm/mach-orion5x/gpio.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * arch/arm/mach-orion5x/gpio.c
- *
- * GPIO functions for Marvell Orion System On Chip
- *
- * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <asm/gpio.h>
-#include <mach/orion5x.h>
-#include "common.h"
-
-static DEFINE_SPINLOCK(gpio_lock);
-static unsigned long gpio_valid[BITS_TO_LONGS(GPIO_MAX)];
-static const char *gpio_label[GPIO_MAX]; /* non null for allocated GPIOs */
-
-void __init orion5x_gpio_set_valid(unsigned pin, int valid)
-{
- if (valid)
- __set_bit(pin, gpio_valid);
- else
- __clear_bit(pin, gpio_valid);
-}
-
-/*
- * GENERIC_GPIO primitives
- */
-int gpio_direction_input(unsigned pin)
-{
- unsigned long flags;
-
- if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
- pr_debug("%s: invalid GPIO %d\n", __func__, pin);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- /*
- * Some callers might have not used the gpio_request(),
- * so flag this pin as requested now.
- */
- if (!gpio_label[pin])
- gpio_label[pin] = "?";
-
- orion5x_setbits(GPIO_IO_CONF, 1 << pin);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
- return 0;
-}
-EXPORT_SYMBOL(gpio_direction_input);
-
-int gpio_direction_output(unsigned pin, int value)
-{
- unsigned long flags;
- int mask;
-
- if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
- pr_debug("%s: invalid GPIO %d\n", __func__, pin);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- /*
- * Some callers might have not used the gpio_request(),
- * so flag this pin as requested now.
- */
- if (!gpio_label[pin])
- gpio_label[pin] = "?";
-
- mask = 1 << pin;
- orion5x_clrbits(GPIO_BLINK_EN, mask);
- if (value)
- orion5x_setbits(GPIO_OUT, mask);
- else
- orion5x_clrbits(GPIO_OUT, mask);
- orion5x_clrbits(GPIO_IO_CONF, mask);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
- return 0;
-}
-EXPORT_SYMBOL(gpio_direction_output);
-
-int gpio_get_value(unsigned pin)
-{
- int val, mask = 1 << pin;
-
- if (readl(GPIO_IO_CONF) & mask)
- val = readl(GPIO_DATA_IN) ^ readl(GPIO_IN_POL);
- else
- val = readl(GPIO_OUT);
-
- return val & mask;
-}
-EXPORT_SYMBOL(gpio_get_value);
-
-void gpio_set_value(unsigned pin, int value)
-{
- unsigned long flags;
- int mask = 1 << pin;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- orion5x_clrbits(GPIO_BLINK_EN, mask);
- if (value)
- orion5x_setbits(GPIO_OUT, mask);
- else
- orion5x_clrbits(GPIO_OUT, mask);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-}
-EXPORT_SYMBOL(gpio_set_value);
-
-void orion5x_gpio_set_blink(unsigned pin, int blink)
-{
- unsigned long flags;
- int mask = 1 << pin;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- orion5x_clrbits(GPIO_OUT, mask);
- if (blink)
- orion5x_setbits(GPIO_BLINK_EN, mask);
- else
- orion5x_clrbits(GPIO_BLINK_EN, mask);
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-}
-EXPORT_SYMBOL(orion5x_gpio_set_blink);
-
-int gpio_request(unsigned pin, const char *label)
-{
- int ret = 0;
- unsigned long flags;
-
- if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
- pr_debug("%s: invalid GPIO %d\n", __func__, pin);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- if (gpio_label[pin]) {
- pr_debug("%s: GPIO %d already used as %s\n",
- __func__, pin, gpio_label[pin]);
- ret = -EBUSY;
- } else
- gpio_label[pin] = label ? label : "?";
-
- spin_unlock_irqrestore(&gpio_lock, flags);
- return ret;
-}
-EXPORT_SYMBOL(gpio_request);
-
-void gpio_free(unsigned pin)
-{
- might_sleep();
-
- if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
- pr_debug("%s: invalid GPIO %d\n", __func__, pin);
- return;
- }
-
- if (!gpio_label[pin])
- pr_warning("%s: GPIO %d already freed\n", __func__, pin);
- else
- gpio_label[pin] = NULL;
-}
-EXPORT_SYMBOL(gpio_free);
-
-/* Debug helper */
-void gpio_display(void)
-{
- int i;
-
- for (i = 0; i < GPIO_MAX; i++) {
- printk(KERN_DEBUG "Pin-%d: ", i);
-
- if (!test_bit(i, gpio_valid)) {
- printk("non-GPIO\n");
- } else if (!gpio_label[i]) {
- printk("GPIO, free\n");
- } else {
- printk("GPIO, used by %s, ", gpio_label[i]);
- if (readl(GPIO_IO_CONF) & (1 << i)) {
- printk("input, active %s, level %s, edge %s\n",
- ((readl(GPIO_IN_POL) >> i) & 1) ? "low" : "high",
- ((readl(GPIO_LEVEL_MASK) >> i) & 1) ? "enabled" : "masked",
- ((readl(GPIO_EDGE_MASK) >> i) & 1) ? "enabled" : "masked");
- } else {
- printk("output, val=%d\n", (readl(GPIO_OUT) >> i) & 1);
- }
- }
- }
-
- printk(KERN_DEBUG "MPP_0_7_CTRL (0x%08x) = 0x%08x\n",
- MPP_0_7_CTRL, readl(MPP_0_7_CTRL));
- printk(KERN_DEBUG "MPP_8_15_CTRL (0x%08x) = 0x%08x\n",
- MPP_8_15_CTRL, readl(MPP_8_15_CTRL));
- printk(KERN_DEBUG "MPP_16_19_CTRL (0x%08x) = 0x%08x\n",
- MPP_16_19_CTRL, readl(MPP_16_19_CTRL));
- printk(KERN_DEBUG "MPP_DEV_CTRL (0x%08x) = 0x%08x\n",
- MPP_DEV_CTRL, readl(MPP_DEV_CTRL));
- printk(KERN_DEBUG "GPIO_OUT (0x%08x) = 0x%08x\n",
- GPIO_OUT, readl(GPIO_OUT));
- printk(KERN_DEBUG "GPIO_IO_CONF (0x%08x) = 0x%08x\n",
- GPIO_IO_CONF, readl(GPIO_IO_CONF));
- printk(KERN_DEBUG "GPIO_BLINK_EN (0x%08x) = 0x%08x\n",
- GPIO_BLINK_EN, readl(GPIO_BLINK_EN));
- printk(KERN_DEBUG "GPIO_IN_POL (0x%08x) = 0x%08x\n",
- GPIO_IN_POL, readl(GPIO_IN_POL));
- printk(KERN_DEBUG "GPIO_DATA_IN (0x%08x) = 0x%08x\n",
- GPIO_DATA_IN, readl(GPIO_DATA_IN));
- printk(KERN_DEBUG "GPIO_LEVEL_MASK (0x%08x) = 0x%08x\n",
- GPIO_LEVEL_MASK, readl(GPIO_LEVEL_MASK));
- printk(KERN_DEBUG "GPIO_EDGE_CAUSE (0x%08x) = 0x%08x\n",
- GPIO_EDGE_CAUSE, readl(GPIO_EDGE_CAUSE));
- printk(KERN_DEBUG "GPIO_EDGE_MASK (0x%08x) = 0x%08x\n",
- GPIO_EDGE_MASK, readl(GPIO_EDGE_MASK));
-}
diff --git a/arch/arm/mach-orion5x/include/mach/dma.h b/arch/arm/mach-orion5x/include/mach/dma.h
deleted file mode 100644
index 40a8c178f10d..000000000000
--- a/arch/arm/mach-orion5x/include/mach/dma.h
+++ /dev/null
@@ -1 +0,0 @@
-/* empty */
diff --git a/arch/arm/mach-orion5x/include/mach/gpio.h b/arch/arm/mach-orion5x/include/mach/gpio.h
index 65dc136a86f7..d8182e87ac16 100644
--- a/arch/arm/mach-orion5x/include/mach/gpio.h
+++ b/arch/arm/mach-orion5x/include/mach/gpio.h
@@ -2,18 +2,26 @@
* arch/arm/mach-orion5x/include/mach/gpio.h
*
* This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
+ * License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
-extern int gpio_request(unsigned pin, const char *label);
-extern void gpio_free(unsigned pin);
-extern int gpio_direction_input(unsigned pin);
-extern int gpio_direction_output(unsigned pin, int value);
-extern int gpio_get_value(unsigned pin);
-extern void gpio_set_value(unsigned pin, int value);
-extern void orion5x_gpio_set_blink(unsigned pin, int blink);
-extern void gpio_display(void); /* debug */
+#ifndef __ASM_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H
+
+#include <mach/irqs.h>
+#include <plat/gpio.h>
+#include <asm-generic/gpio.h> /* cansleep wrappers */
+
+#define GPIO_MAX 32
+#define GPIO_OUT(pin) ORION5X_DEV_BUS_REG(0x100)
+#define GPIO_IO_CONF(pin) ORION5X_DEV_BUS_REG(0x104)
+#define GPIO_BLINK_EN(pin) ORION5X_DEV_BUS_REG(0x108)
+#define GPIO_IN_POL(pin) ORION5X_DEV_BUS_REG(0x10c)
+#define GPIO_DATA_IN(pin) ORION5X_DEV_BUS_REG(0x110)
+#define GPIO_EDGE_CAUSE(pin) ORION5X_DEV_BUS_REG(0x114)
+#define GPIO_EDGE_MASK(pin) ORION5X_DEV_BUS_REG(0x118)
+#define GPIO_LEVEL_MASK(pin) ORION5X_DEV_BUS_REG(0x11c)
static inline int gpio_to_irq(int pin)
{
@@ -25,4 +33,5 @@ static inline int irq_to_gpio(int irq)
return irq - IRQ_ORION5X_GPIO_START;
}
-#include <asm-generic/gpio.h> /* cansleep wrappers */
+
+#endif
diff --git a/arch/arm/mach-orion5x/include/mach/io.h b/arch/arm/mach-orion5x/include/mach/io.h
index f24b2513f7f3..c47b033bd999 100644
--- a/arch/arm/mach-orion5x/include/mach/io.h
+++ b/arch/arm/mach-orion5x/include/mach/io.h
@@ -38,14 +38,9 @@ __arch_iounmap(void __iomem *addr)
__iounmap(addr);
}
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-
#define __arch_ioremap(p, s, m) __arch_ioremap(p, s, m)
#define __arch_iounmap(a) __arch_iounmap(a)
-#define __io(a) __io(a)
+#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
diff --git a/arch/arm/mach-orion5x/include/mach/irqs.h b/arch/arm/mach-orion5x/include/mach/irqs.h
index d5b0fbf6b965..a6fa9d8f12d8 100644
--- a/arch/arm/mach-orion5x/include/mach/irqs.h
+++ b/arch/arm/mach-orion5x/include/mach/irqs.h
@@ -13,8 +13,6 @@
#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H
-#include "orion5x.h" /* need GPIO_MAX */
-
/*
* Orion Main Interrupt Controller
*/
@@ -54,7 +52,7 @@
* Orion General Purpose Pins
*/
#define IRQ_ORION5X_GPIO_START 32
-#define NR_GPIO_IRQS GPIO_MAX
+#define NR_GPIO_IRQS 32
#define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS)
diff --git a/arch/arm/mach-orion5x/include/mach/memory.h b/arch/arm/mach-orion5x/include/mach/memory.h
index 54dd76b013f2..52a2955d0f87 100644
--- a/arch/arm/mach-orion5x/include/mach/memory.h
+++ b/arch/arm/mach-orion5x/include/mach/memory.h
@@ -9,8 +9,4 @@
#define PHYS_OFFSET UL(0x00000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-
#endif
diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h
index 9f5ce1ce5840..67bda31406dd 100644
--- a/arch/arm/mach-orion5x/include/mach/orion5x.h
+++ b/arch/arm/mach-orion5x/include/mach/orion5x.h
@@ -134,14 +134,6 @@
#define MPP_16_19_CTRL ORION5X_DEV_BUS_REG(0x050)
#define MPP_DEV_CTRL ORION5X_DEV_BUS_REG(0x008)
#define MPP_RESET_SAMPLE ORION5X_DEV_BUS_REG(0x010)
-#define GPIO_OUT ORION5X_DEV_BUS_REG(0x100)
-#define GPIO_IO_CONF ORION5X_DEV_BUS_REG(0x104)
-#define GPIO_BLINK_EN ORION5X_DEV_BUS_REG(0x108)
-#define GPIO_IN_POL ORION5X_DEV_BUS_REG(0x10c)
-#define GPIO_DATA_IN ORION5X_DEV_BUS_REG(0x110)
-#define GPIO_EDGE_CAUSE ORION5X_DEV_BUS_REG(0x114)
-#define GPIO_EDGE_MASK ORION5X_DEV_BUS_REG(0x118)
-#define GPIO_LEVEL_MASK ORION5X_DEV_BUS_REG(0x11c)
#define DEV_BANK_0_PARAM ORION5X_DEV_BUS_REG(0x45c)
#define DEV_BANK_1_PARAM ORION5X_DEV_BUS_REG(0x460)
#define DEV_BANK_2_PARAM ORION5X_DEV_BUS_REG(0x464)
@@ -149,7 +141,6 @@
#define DEV_BUS_CTRL ORION5X_DEV_BUS_REG(0x4c0)
#define DEV_BUS_INT_CAUSE ORION5X_DEV_BUS_REG(0x4d0)
#define DEV_BUS_INT_MASK ORION5X_DEV_BUS_REG(0x4d4)
-#define GPIO_MAX 32
/***************************************************************************
* Orion CPU Bridge Registers
diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c
index 632a36f5cf14..0caae43301e5 100644
--- a/arch/arm/mach-orion5x/irq.c
+++ b/arch/arm/mach-orion5x/irq.c
@@ -19,193 +19,38 @@
#include <plat/irq.h>
#include "common.h"
-/*****************************************************************************
- * Orion GPIO IRQ
- *
- * GPIO_IN_POL register controlls whether GPIO_DATA_IN will hold the same
- * value of the line or the opposite value.
- *
- * Level IRQ handlers: DATA_IN is used directly as cause register.
- * Interrupt are masked by LEVEL_MASK registers.
- * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE.
- * Interrupt are masked by EDGE_MASK registers.
- * Both-edge handlers: Similar to regular Edge handlers, but also swaps
- * the polarity to catch the next line transaction.
- * This is a race condition that might not perfectly
- * work on some use cases.
- *
- * Every eight GPIO lines are grouped (OR'ed) before going up to main
- * cause register.
- *
- * EDGE cause mask
- * data-in /--------| |-----| |----\
- * -----| |----- ---- to main cause reg
- * X \----------------| |----/
- * polarity LEVEL mask
- *
- ****************************************************************************/
-static void orion5x_gpio_irq_ack(u32 irq)
-{
- int pin = irq_to_gpio(irq);
- if (irq_desc[irq].status & IRQ_LEVEL)
- /*
- * Mask bit for level interrupt
- */
- orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin);
- else
- /*
- * Clear casue bit for egde interrupt
- */
- orion5x_clrbits(GPIO_EDGE_CAUSE, 1 << pin);
-}
-
-static void orion5x_gpio_irq_mask(u32 irq)
-{
- int pin = irq_to_gpio(irq);
- if (irq_desc[irq].status & IRQ_LEVEL)
- orion5x_clrbits(GPIO_LEVEL_MASK, 1 << pin);
- else
- orion5x_clrbits(GPIO_EDGE_MASK, 1 << pin);
-}
-
-static void orion5x_gpio_irq_unmask(u32 irq)
+static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
- int pin = irq_to_gpio(irq);
- if (irq_desc[irq].status & IRQ_LEVEL)
- orion5x_setbits(GPIO_LEVEL_MASK, 1 << pin);
- else
- orion5x_setbits(GPIO_EDGE_MASK, 1 << pin);
-}
-
-static int orion5x_gpio_set_irq_type(u32 irq, u32 type)
-{
- int pin = irq_to_gpio(irq);
- struct irq_desc *desc;
-
- if ((readl(GPIO_IO_CONF) & (1 << pin)) == 0) {
- printk(KERN_ERR "orion5x_gpio_set_irq_type failed "
- "(irq %d, pin %d).\n", irq, pin);
- return -EINVAL;
- }
-
- desc = irq_desc + irq;
-
- switch (type) {
- case IRQ_TYPE_LEVEL_HIGH:
- desc->handle_irq = handle_level_irq;
- desc->status |= IRQ_LEVEL;
- orion5x_clrbits(GPIO_IN_POL, (1 << pin));
- break;
- case IRQ_TYPE_LEVEL_LOW:
- desc->handle_irq = handle_level_irq;
- desc->status |= IRQ_LEVEL;
- orion5x_setbits(GPIO_IN_POL, (1 << pin));
- break;
- case IRQ_TYPE_EDGE_RISING:
- desc->handle_irq = handle_edge_irq;
- desc->status &= ~IRQ_LEVEL;
- orion5x_clrbits(GPIO_IN_POL, (1 << pin));
- break;
- case IRQ_TYPE_EDGE_FALLING:
- desc->handle_irq = handle_edge_irq;
- desc->status &= ~IRQ_LEVEL;
- orion5x_setbits(GPIO_IN_POL, (1 << pin));
- break;
- case IRQ_TYPE_EDGE_BOTH:
- desc->handle_irq = handle_edge_irq;
- desc->status &= ~IRQ_LEVEL;
- /*
- * set initial polarity based on current input level
- */
- if ((readl(GPIO_IN_POL) ^ readl(GPIO_DATA_IN))
- & (1 << pin))
- orion5x_setbits(GPIO_IN_POL, (1 << pin)); /* falling */
- else
- orion5x_clrbits(GPIO_IN_POL, (1 << pin)); /* rising */
-
- break;
- default:
- printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type);
- return -EINVAL;
- }
-
- desc->status &= ~IRQ_TYPE_SENSE_MASK;
- desc->status |= type & IRQ_TYPE_SENSE_MASK;
-
- return 0;
-}
-
-static struct irq_chip orion5x_gpio_irq_chip = {
- .name = "Orion-IRQ-GPIO",
- .ack = orion5x_gpio_irq_ack,
- .mask = orion5x_gpio_irq_mask,
- .unmask = orion5x_gpio_irq_unmask,
- .set_type = orion5x_gpio_set_irq_type,
-};
-
-static void orion5x_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- u32 cause, offs, pin;
-
BUG_ON(irq < IRQ_ORION5X_GPIO_0_7 || irq > IRQ_ORION5X_GPIO_24_31);
- offs = (irq - IRQ_ORION5X_GPIO_0_7) * 8;
- cause = (readl(GPIO_DATA_IN) & readl(GPIO_LEVEL_MASK)) |
- (readl(GPIO_EDGE_CAUSE) & readl(GPIO_EDGE_MASK));
- for (pin = offs; pin < offs + 8; pin++) {
- if (cause & (1 << pin)) {
- irq = gpio_to_irq(pin);
- desc = irq_desc + irq;
- if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
- /* Swap polarity (race with GPIO line) */
- u32 polarity = readl(GPIO_IN_POL);
- polarity ^= 1 << pin;
- writel(polarity, GPIO_IN_POL);
- }
- generic_handle_irq(irq);
- }
- }
+ orion_gpio_irq_handler((irq - IRQ_ORION5X_GPIO_0_7) << 3);
}
-static void __init orion5x_init_gpio_irq(void)
+void __init orion5x_init_irq(void)
{
int i;
- struct irq_desc *desc;
+
+ orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK);
/*
* Mask and clear GPIO IRQ interrupts
*/
- writel(0x0, GPIO_LEVEL_MASK);
- writel(0x0, GPIO_EDGE_MASK);
- writel(0x0, GPIO_EDGE_CAUSE);
+ writel(0x0, GPIO_LEVEL_MASK(0));
+ writel(0x0, GPIO_EDGE_MASK(0));
+ writel(0x0, GPIO_EDGE_CAUSE(0));
/*
* Register chained level handlers for GPIO IRQs by default.
* User can use set_type() if he wants to use edge types handlers.
*/
for (i = IRQ_ORION5X_GPIO_START; i < NR_IRQS; i++) {
- set_irq_chip(i, &orion5x_gpio_irq_chip);
+ set_irq_chip(i, &orion_gpio_irq_level_chip);
set_irq_handler(i, handle_level_irq);
- desc = irq_desc + i;
- desc->status |= IRQ_LEVEL;
+ irq_desc[i].status |= IRQ_LEVEL;
set_irq_flags(i, IRQF_VALID);
}
- set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, orion5x_gpio_irq_handler);
- set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, orion5x_gpio_irq_handler);
- set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, orion5x_gpio_irq_handler);
- set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, orion5x_gpio_irq_handler);
-}
-
-/*****************************************************************************
- * Orion Main IRQ
- ****************************************************************************/
-static void __init orion5x_init_main_irq(void)
-{
- orion_irq_init(0, (void __iomem *)MAIN_IRQ_MASK);
-}
-
-void __init orion5x_init_irq(void)
-{
- orion5x_init_main_irq();
- orion5x_init_gpio_irq();
+ set_irq_chained_handler(IRQ_ORION5X_GPIO_0_7, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_ORION5X_GPIO_8_15, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_ORION5X_GPIO_16_23, gpio_irq_handler);
+ set_irq_chained_handler(IRQ_ORION5X_GPIO_24_31, gpio_irq_handler);
}
diff --git a/arch/arm/mach-orion5x/mpp.c b/arch/arm/mach-orion5x/mpp.c
index 640ea2a3fc6c..e23a3f91d6c6 100644
--- a/arch/arm/mach-orion5x/mpp.c
+++ b/arch/arm/mach-orion5x/mpp.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/mbus.h>
#include <linux/io.h>
+#include <asm/gpio.h>
#include <mach/hardware.h>
#include "common.h"
#include "mpp.h"
@@ -152,7 +153,10 @@ void __init orion5x_mpp_conf(struct orion5x_mpp_mode *mode)
*reg &= ~(0xf << shift);
*reg |= (num_type & 0xf) << shift;
- orion5x_gpio_set_valid(mode->mpp, !!(mode->type == MPP_GPIO));
+ if (mode->type == MPP_UNUSED && (mode->mpp < 16 || is_5182()))
+ orion_gpio_set_unused(mode->mpp);
+
+ orion_gpio_set_valid(mode->mpp, !!(mode->type == MPP_GPIO));
mode++;
}
diff --git a/arch/arm/mach-pnx4008/dma.c b/arch/arm/mach-pnx4008/dma.c
index ac2f70eddb9e..425f7188505e 100644
--- a/arch/arm/mach-pnx4008/dma.c
+++ b/arch/arm/mach-pnx4008/dma.c
@@ -25,9 +25,8 @@
#include <asm/system.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <asm/dma-mapping.h>
-#include <asm/mach/dma.h>
#include <mach/clock.h>
static struct dma_channel {
diff --git a/arch/arm/mach-pnx4008/include/mach/dma.h b/arch/arm/mach-pnx4008/include/mach/dma.h
index 5442d04fc575..f094bf8bfb18 100644
--- a/arch/arm/mach-pnx4008/include/mach/dma.h
+++ b/arch/arm/mach-pnx4008/include/mach/dma.h
@@ -16,8 +16,6 @@
#include "platform.h"
-#define MAX_DMA_ADDRESS 0xffffffff
-
#define MAX_DMA_CHANNELS 8
#define DMAC_BASE IO_ADDRESS(PNX4008_DMA_CONFIG_BASE)
diff --git a/arch/arm/mach-pnx4008/include/mach/io.h b/arch/arm/mach-pnx4008/include/mach/io.h
index c6206f25839d..cbf0904540ea 100644
--- a/arch/arm/mach-pnx4008/include/mach/io.h
+++ b/arch/arm/mach-pnx4008/include/mach/io.h
@@ -15,7 +15,7 @@
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-pnx4008/include/mach/memory.h b/arch/arm/mach-pnx4008/include/mach/memory.h
index 5789a2d16f5a..0e8770081058 100644
--- a/arch/arm/mach-pnx4008/include/mach/memory.h
+++ b/arch/arm/mach-pnx4008/include/mach/memory.h
@@ -16,9 +16,6 @@
/*
* Physical DRAM offset.
*/
-#define PHYS_OFFSET (0x80000000)
-
-#define __virt_to_bus(x) ((x) - PAGE_OFFSET + PHYS_OFFSET)
-#define __bus_to_virt(x) ((x) + PAGE_OFFSET - PHYS_OFFSET)
+#define PHYS_OFFSET UL(0x80000000)
#endif
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index a062235e83a8..8eea7306f29b 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -19,20 +19,34 @@ config CPU_PXA320
config CPU_PXA930
bool "PXA930 (codename Tavor-P)"
+config CPU_PXA935
+ bool "PXA935 (codename Tavor-P65)"
+
endmenu
endif
config ARCH_GUMSTIX
- bool "Gumstix XScale boards"
+ bool "Gumstix XScale 255 boards"
+ select PXA25x
help
- Say Y here if you intend to run this kernel on a
- Gumstix Full Function Minature Computer.
+ Say Y here if you intend to run this kernel on
+ Basix, Connex, ws-200ax, ws-400ax systems
-config MACH_GUMSTIX_F
- bool "Basix, Connex, ws-200ax, ws-400ax systems"
+choice
+ prompt "Gumstix Carrier/Expansion Board"
depends on ARCH_GUMSTIX
- select PXA25x
+
+config GUMSTIX_AM200EPD
+ bool "Enable AM200EPD board support"
+
+endchoice
+
+config MACH_INTELMOTE2
+ bool "Intel Mote 2 Platform"
+ select PXA27x
+ select IWMMXT
+ select PXA_HAVE_BOARD_IRQS
config ARCH_LUBBOCK
bool "Intel DBPXA250 Development Platform"
@@ -199,6 +213,10 @@ config MACH_E800
config TRIZEPS_PXA
bool "PXA based Keith und Koep Trizeps DIMM-Modules"
+config MACH_H5000
+ bool "HP iPAQ h5000"
+ select PXA25x
+
config MACH_TRIZEPS4
bool "Keith und Koep Trizeps4 DIMM-Module"
depends on TRIZEPS_PXA
@@ -283,7 +301,6 @@ config MACH_MIOA701
bool "Mitac Mio A701 Support"
select PXA27x
select IWMMXT
- select LEDS_GPIO
select HAVE_PWM
select GPIO_SYSFS
help
@@ -342,10 +359,6 @@ config PCM990_DISPLAY_NONE
endchoice
-config MACH_AM200EPD
- depends on MACH_GUMSTIX_F
- bool "Enable AM200EPD board support"
-
config PXA_EZX
bool "Motorola EZX Platform"
select PXA27x
@@ -386,16 +399,25 @@ endmenu
config PXA25x
bool
+ select CPU_XSCALE
help
Select code specific to PXA21x/25x/26x variants
config PXA27x
bool
+ select CPU_XSCALE
help
Select code specific to PXA27x variants
+config CPU_PXA26x
+ bool
+ select PXA25x
+ help
+ Select code specific to PXA26x (codename Dalhart)
+
config PXA3xx
bool
+ select CPU_XSC3
help
Select code specific to PXA3xx variants
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index d64c68b232e3..7b28bb561d63 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -27,7 +27,7 @@ obj-$(CONFIG_CPU_PXA930) += pxa930.o
# Specific board support
obj-$(CONFIG_ARCH_GUMSTIX) += gumstix.o
-obj-$(CONFIG_MACH_AM200EPD) += am200epd.o
+obj-$(CONFIG_GUMSTIX_AM200EPD) += am200epd.o
obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o
obj-$(CONFIG_MACH_LOGICPD_PXA270) += lpd270.o
obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o
@@ -35,6 +35,7 @@ obj-$(CONFIG_MACH_MP900C) += mp900.o
obj-$(CONFIG_ARCH_PXA_IDP) += idp.o
obj-$(CONFIG_MACH_TRIZEPS4) += trizeps4.o
obj-$(CONFIG_MACH_COLIBRI) += colibri.o
+obj-$(CONFIG_MACH_H5000) += h5000.o
obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o sharpsl_pm.o corgi_pm.o
obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o sharpsl_pm.o spitz_pm.o
obj-$(CONFIG_CORGI_SSP_DEPRECATED) += corgi_ssp.o corgi_lcd.o
@@ -69,6 +70,8 @@ obj-$(CONFIG_MACH_ARMCORE) += cm-x2xx.o cm-x255.o cm-x270.o
obj-$(CONFIG_MACH_CM_X300) += cm-x300.o
obj-$(CONFIG_PXA_EZX) += ezx.o
+obj-$(CONFIG_MACH_INTELMOTE2) += imote2.o
+
# Support for blinky lights
led-y := leds.o
led-$(CONFIG_ARCH_LUBBOCK) += leds-lubbock.o
diff --git a/arch/arm/mach-pxa/am200epd.c b/arch/arm/mach-pxa/am200epd.c
index b965085a37b9..77ee80e5e47b 100644
--- a/arch/arm/mach-pxa/am200epd.c
+++ b/arch/arm/mach-pxa/am200epd.c
@@ -30,8 +30,12 @@
#include <linux/irq.h>
#include <linux/gpio.h>
+#include <mach/gumstix.h>
+#include <mach/mfp-pxa25x.h>
#include <mach/pxafb.h>
+#include "generic.h"
+
#include <video/metronomefb.h>
static unsigned int panel_type = 6;
@@ -331,7 +335,16 @@ static struct metronome_board am200_board = {
.cleanup = am200_cleanup,
};
-static int __init am200_init(void)
+static unsigned long am200_pin_config[] __initdata = {
+ GPIO51_GPIO,
+ GPIO49_GPIO,
+ GPIO48_GPIO,
+ GPIO32_GPIO,
+ GPIO17_GPIO,
+ GPIO16_GPIO,
+};
+
+int __init am200_init(void)
{
int ret;
@@ -339,6 +352,8 @@ static int __init am200_init(void)
* creation events */
fb_register_client(&am200_fb_notif);
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(am200_pin_config));
+
/* request our platform independent driver */
request_module("metronomefb");
@@ -367,8 +382,6 @@ static int __init am200_init(void)
module_param(panel_type, uint, 0);
MODULE_PARM_DESC(panel_type, "Select the panel type: 6, 8, 97");
-module_init(am200_init);
-
MODULE_DESCRIPTION("board driver for am200 metronome epd kit");
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c
index ca8e20538157..40b774084514 100644
--- a/arch/arm/mach-pxa/clock.c
+++ b/arch/arm/mach-pxa/clock.c
@@ -12,53 +12,16 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <asm/clkdev.h>
#include <mach/pxa2xx-regs.h>
-#include <mach/pxa2xx-gpio.h>
#include <mach/hardware.h>
#include "devices.h"
#include "generic.h"
#include "clock.h"
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
static DEFINE_SPINLOCK(clocks_lock);
-static struct clk *clk_lookup(struct device *dev, const char *id)
-{
- struct clk *p;
-
- list_for_each_entry(p, &clocks, node)
- if (strcmp(id, p->name) == 0 && p->dev == dev)
- return p;
-
- return NULL;
-}
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- p = clk_lookup(dev, id);
- if (!p)
- p = clk_lookup(NULL, id);
- if (p)
- clk = p;
- mutex_unlock(&clocks_mutex);
-
- if (!IS_ERR(clk) && clk->ops == NULL)
- clk = clk->other;
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
int clk_enable(struct clk *clk)
{
unsigned long flags;
@@ -116,37 +79,27 @@ const struct clkops clk_cken_ops = {
.disable = clk_cken_disable,
};
-void clks_register(struct clk *clks, size_t num)
+void clks_register(struct clk_lookup *clks, size_t num)
{
int i;
- mutex_lock(&clocks_mutex);
for (i = 0; i < num; i++)
- list_add(&clks[i].node, &clocks);
- mutex_unlock(&clocks_mutex);
+ clkdev_add(&clks[i]);
}
int clk_add_alias(char *alias, struct device *alias_dev, char *id,
struct device *dev)
{
- struct clk *r = clk_lookup(dev, id);
- struct clk *new;
+ struct clk *r = clk_get(dev, id);
+ struct clk_lookup *l;
if (!r)
return -ENODEV;
- new = kzalloc(sizeof(struct clk), GFP_KERNEL);
-
- if (!new)
- return -ENOMEM;
-
- new->name = alias;
- new->dev = alias_dev;
- new->other = r;
-
- mutex_lock(&clocks_mutex);
- list_add(&new->node, &clocks);
- mutex_unlock(&clocks_mutex);
-
+ l = clkdev_alloc(r, alias, alias_dev ? dev_name(alias_dev) : NULL);
+ clk_put(r);
+ if (!l)
+ return -ENODEV;
+ clkdev_add(l);
return 0;
}
diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
index 73be795fe3bf..4e9c613c6767 100644
--- a/arch/arm/mach-pxa/clock.h
+++ b/arch/arm/mach-pxa/clock.h
@@ -1,6 +1,4 @@
-#include <linux/list.h>
-
-struct clk;
+#include <asm/clkdev.h>
struct clkops {
void (*enable)(struct clk *);
@@ -9,9 +7,6 @@ struct clkops {
};
struct clk {
- struct list_head node;
- const char *name;
- struct device *dev;
const struct clkops *ops;
unsigned long rate;
unsigned int cken;
@@ -20,41 +15,31 @@ struct clk {
struct clk *other;
};
-#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \
+#define INIT_CLKREG(_clk,_devname,_conname) \
{ \
- .name = _name, \
- .dev = _dev, \
+ .clk = _clk, \
+ .dev_id = _devname, \
+ .con_id = _conname, \
+ }
+
+#define DEFINE_CKEN(_name, _cken, _rate, _delay) \
+struct clk clk_##_name = { \
.ops = &clk_cken_ops, \
.rate = _rate, \
.cken = CKEN_##_cken, \
.delay = _delay, \
}
-#define INIT_CK(_name, _cken, _ops, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
+#define DEFINE_CK(_name, _cken, _ops) \
+struct clk clk_##_name = { \
.ops = _ops, \
.cken = CKEN_##_cken, \
}
-/*
- * This is a placeholder to alias one clock device+name pair
- * to another struct clk.
- */
-#define INIT_CKOTHER(_name, _other, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
- .other = _other, \
- }
-
-#define INIT_CLK(_name, _ops, _rate, _delay, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
- .ops = _ops, \
- .rate = _rate, \
+#define DEFINE_CLK(_name, _ops, _rate, _delay) \
+struct clk clk_##_name = { \
+ .ops = _ops, \
+ .rate = _rate, \
.delay = _delay, \
}
@@ -64,20 +49,16 @@ void clk_cken_enable(struct clk *clk);
void clk_cken_disable(struct clk *clk);
#ifdef CONFIG_PXA3xx
-#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
+#define DEFINE_PXA3_CKEN(_name, _cken, _rate, _delay) \
+struct clk clk_##_name = { \
.ops = &clk_pxa3xx_cken_ops, \
.rate = _rate, \
.cken = CKEN_##_cken, \
.delay = _delay, \
}
-#define PXA3xx_CK(_name, _cken, _ops, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
+#define DEFINE_PXA3_CK(_name, _cken, _ops) \
+struct clk clk_##_name = { \
.ops = _ops, \
.cken = CKEN_##_cken, \
}
@@ -87,7 +68,7 @@ extern void clk_pxa3xx_cken_enable(struct clk *);
extern void clk_pxa3xx_cken_disable(struct clk *);
#endif
-void clks_register(struct clk *clks, size_t num);
+void clks_register(struct clk_lookup *clks, size_t num);
int clk_add_alias(char *alias, struct device *alias_dev, char *id,
struct device *dev);
diff --git a/arch/arm/mach-pxa/cm-x2xx.c b/arch/arm/mach-pxa/cm-x2xx.c
index 0b3ce3b6d896..d99fd9e4d888 100644
--- a/arch/arm/mach-pxa/cm-x2xx.c
+++ b/arch/arm/mach-pxa/cm-x2xx.c
@@ -210,10 +210,8 @@ static struct pxafb_mode_info generic_stn_320x240_mode = {
static struct pxafb_mach_info generic_stn_320x240 = {
.modes = &generic_stn_320x240_mode,
.num_modes = 1,
- .lccr0 = 0,
- .lccr3 = (LCCR3_PixClkDiv(0x03) |
- LCCR3_Acb(0xff) |
- LCCR3_PCP),
+ .lcd_conn = LCD_COLOR_STN_8BPP | LCD_PCLK_EDGE_FALL |\
+ LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
@@ -236,10 +234,8 @@ static struct pxafb_mode_info generic_tft_640x480_mode = {
static struct pxafb_mach_info generic_tft_640x480 = {
.modes = &generic_tft_640x480_mode,
.num_modes = 1,
- .lccr0 = (LCCR0_PAS),
- .lccr3 = (LCCR3_PixClkDiv(0x01) |
- LCCR3_Acb(0xff) |
- LCCR3_PCP),
+ .lcd_conn = LCD_COLOR_TFT_8BPP | LCD_PCLK_EDGE_FALL |\
+ LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
@@ -263,9 +259,7 @@ static struct pxafb_mode_info generic_crt_640x480_mode = {
static struct pxafb_mach_info generic_crt_640x480 = {
.modes = &generic_crt_640x480_mode,
.num_modes = 1,
- .lccr0 = (LCCR0_PAS),
- .lccr3 = (LCCR3_PixClkDiv(0x01) |
- LCCR3_Acb(0xff)),
+ .lcd_conn = LCD_COLOR_TFT_8BPP | LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
@@ -289,9 +283,7 @@ static struct pxafb_mode_info generic_crt_800x600_mode = {
static struct pxafb_mach_info generic_crt_800x600 = {
.modes = &generic_crt_800x600_mode,
.num_modes = 1,
- .lccr0 = (LCCR0_PAS),
- .lccr3 = (LCCR3_PixClkDiv(0x02) |
- LCCR3_Acb(0xff)),
+ .lcd_conn = LCD_COLOR_TFT_8BPP | LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
@@ -314,10 +306,7 @@ static struct pxafb_mode_info generic_tft_320x240_mode = {
static struct pxafb_mach_info generic_tft_320x240 = {
.modes = &generic_tft_320x240_mode,
.num_modes = 1,
- .lccr0 = (LCCR0_PAS),
- .lccr3 = (LCCR3_PixClkDiv(0x06) |
- LCCR3_Acb(0xff) |
- LCCR3_PCP),
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
@@ -341,9 +330,7 @@ static struct pxafb_mode_info generic_stn_640x480_mode = {
static struct pxafb_mach_info generic_stn_640x480 = {
.modes = &generic_stn_640x480_mode,
.num_modes = 1,
- .lccr0 = 0,
- .lccr3 = (LCCR3_PixClkDiv(0x02) |
- LCCR3_Acb(0xff)),
+ .lcd_conn = LCD_COLOR_STN_8BPP | LCD_AC_BIAS_FREQ(0xff),
.cmap_inverse = 0,
.cmap_static = 0,
};
diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c
index deb46cd144bf..ff0c577cd1ac 100644
--- a/arch/arm/mach-pxa/cm-x300.c
+++ b/arch/arm/mach-pxa/cm-x300.c
@@ -31,7 +31,6 @@
#include <mach/mfp-pxa300.h>
#include <mach/hardware.h>
-#include <mach/gpio.h>
#include <mach/pxafb.h>
#include <mach/mmc.h>
#include <mach/ohci.h>
@@ -137,6 +136,10 @@ static mfp_cfg_t cm_x300_mfp_cfg[] __initdata = {
GPIO82_GPIO | MFP_PULL_HIGH, /* MMC CD */
GPIO85_GPIO, /* MMC WP */
GPIO99_GPIO, /* Ethernet IRQ */
+
+ /* Standard I2C */
+ GPIO21_I2C_SCL,
+ GPIO22_I2C_SDA,
};
#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c
index 65558d6aa220..c5e28a46b292 100644
--- a/arch/arm/mach-pxa/corgi.c
+++ b/arch/arm/mach-pxa/corgi.c
@@ -19,6 +19,7 @@
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/mmc/host.h>
+#include <linux/mtd/physmap.h>
#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/backlight.h>
@@ -541,11 +542,42 @@ err_free_1:
static inline void corgi_init_spi(void) {}
#endif
+static struct mtd_partition sharpsl_rom_parts[] = {
+ {
+ .name ="Boot PROM Filesystem",
+ .offset = 0x00120000,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data sharpsl_rom_data = {
+ .width = 2,
+ .nr_parts = ARRAY_SIZE(sharpsl_rom_parts),
+ .parts = sharpsl_rom_parts,
+};
+
+static struct resource sharpsl_rom_resources[] = {
+ {
+ .start = 0x00000000,
+ .end = 0x007fffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sharpsl_rom_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = sharpsl_rom_resources,
+ .num_resources = ARRAY_SIZE(sharpsl_rom_resources),
+ .dev.platform_data = &sharpsl_rom_data,
+};
+
static struct platform_device *devices[] __initdata = {
&corgiscoop_device,
&corgifb_device,
&corgikbd_device,
&corgiled_device,
+ &sharpsl_rom_device,
};
static void corgi_poweroff(void)
diff --git a/arch/arm/mach-pxa/cpufreq-pxa2xx.c b/arch/arm/mach-pxa/cpufreq-pxa2xx.c
index 1f272ea83f36..771dd4eac935 100644
--- a/arch/arm/mach-pxa/cpufreq-pxa2xx.c
+++ b/arch/arm/mach-pxa/cpufreq-pxa2xx.c
@@ -64,7 +64,7 @@ typedef struct {
/* Define the refresh period in mSec for the SDRAM and the number of rows */
#define SDRAM_TREF 64 /* standard 64ms SDRAM */
-#define SDRAM_ROWS 4096 /* 64MB=8192 32MB=4096 */
+static unsigned int sdram_rows;
#define CCLKCFG_TURBO 0x1
#define CCLKCFG_FCS 0x2
@@ -73,6 +73,9 @@ typedef struct {
#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2)
#define MDREFR_DRI_MASK 0xFFF
+#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3)
+#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3)
+
/*
* PXA255 definitions
*/
@@ -109,6 +112,10 @@ static struct cpufreq_frequency_table
static struct cpufreq_frequency_table
pxa255_turbo_freq_table[NUM_PXA25x_TURBO_FREQS+1];
+static unsigned int pxa255_turbo_table;
+module_param(pxa255_turbo_table, uint, 0);
+MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table, !0 = turbo table)");
+
/*
* PXA270 definitions
*
@@ -158,22 +165,16 @@ static struct cpufreq_frequency_table
extern unsigned get_clk_frequency_khz(int info);
-static void find_freq_tables(struct cpufreq_policy *policy,
- struct cpufreq_frequency_table **freq_table,
+static void find_freq_tables(struct cpufreq_frequency_table **freq_table,
pxa_freqs_t **pxa_freqs)
{
if (cpu_is_pxa25x()) {
- if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
+ if (!pxa255_turbo_table) {
*pxa_freqs = pxa255_run_freqs;
*freq_table = pxa255_run_freq_table;
- } else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) {
+ } else {
*pxa_freqs = pxa255_turbo_freqs;
*freq_table = pxa255_turbo_freq_table;
- } else {
- printk("CPU PXA: Unknown policy found. "
- "Using CPUFREQ_POLICY_PERFORMANCE\n");
- *pxa_freqs = pxa255_run_freqs;
- *freq_table = pxa255_run_freq_table;
}
}
if (cpu_is_pxa27x()) {
@@ -194,14 +195,28 @@ static void pxa27x_guess_max_freq(void)
}
}
+static void init_sdram_rows(void)
+{
+ uint32_t mdcnfg = MDCNFG;
+ unsigned int drac2 = 0, drac0 = 0;
+
+ if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3))
+ drac2 = MDCNFG_DRAC2(mdcnfg);
+
+ if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1))
+ drac0 = MDCNFG_DRAC0(mdcnfg);
+
+ sdram_rows = 1 << (11 + max(drac0, drac2));
+}
+
static u32 mdrefr_dri(unsigned int freq)
{
u32 dri = 0;
if (cpu_is_pxa25x())
- dri = ((freq * SDRAM_TREF) / (SDRAM_ROWS * 32));
+ dri = ((freq * SDRAM_TREF) / (sdram_rows * 32));
if (cpu_is_pxa27x())
- dri = ((freq * SDRAM_TREF) / (SDRAM_ROWS - 31)) / 32;
+ dri = ((freq * SDRAM_TREF) / (sdram_rows - 31)) / 32;
return dri;
}
@@ -212,7 +227,7 @@ static int pxa_verify_policy(struct cpufreq_policy *policy)
pxa_freqs_t *pxa_freqs;
int ret;
- find_freq_tables(policy, &pxa_freqs_table, &pxa_freqs);
+ find_freq_tables(&pxa_freqs_table, &pxa_freqs);
ret = cpufreq_frequency_table_verify(policy, pxa_freqs_table);
if (freq_debug)
@@ -240,7 +255,7 @@ static int pxa_set_target(struct cpufreq_policy *policy,
unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg;
/* Get the current policy */
- find_freq_tables(policy, &pxa_freqs_table, &pxa_freq_settings);
+ find_freq_tables(&pxa_freqs_table, &pxa_freq_settings);
/* Lookup the next frequency */
if (cpufreq_frequency_table_target(policy, pxa_freqs_table,
@@ -329,11 +344,15 @@ static __init int pxa_cpufreq_init(struct cpufreq_policy *policy)
{
int i;
unsigned int freq;
+ struct cpufreq_frequency_table *pxa255_freq_table;
+ pxa_freqs_t *pxa255_freqs;
/* try to guess pxa27x cpu */
if (cpu_is_pxa27x())
pxa27x_guess_max_freq();
+ init_sdram_rows();
+
/* set default policy and cpuinfo */
policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */
policy->cur = get_clk_frequency_khz(0); /* current freq */
@@ -354,6 +373,8 @@ static __init int pxa_cpufreq_init(struct cpufreq_policy *policy)
}
pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END;
+ pxa255_turbo_table = !!pxa255_turbo_table;
+
/* Generate the pxa27x cpufreq_frequency_table struct */
for (i = 0; i < NUM_PXA27x_FREQS; i++) {
freq = pxa27x_freqs[i].khz;
@@ -368,8 +389,12 @@ static __init int pxa_cpufreq_init(struct cpufreq_policy *policy)
* Set the policy's minimum and maximum frequencies from the tables
* just constructed. This sets cpuinfo.mxx_freq, min and max.
*/
- if (cpu_is_pxa25x())
- cpufreq_frequency_table_cpuinfo(policy, pxa255_run_freq_table);
+ if (cpu_is_pxa25x()) {
+ find_freq_tables(&pxa255_freq_table, &pxa255_freqs);
+ pr_info("PXA255 cpufreq using %s frequency table\n",
+ pxa255_turbo_table ? "turbo" : "run");
+ cpufreq_frequency_table_cpuinfo(policy, pxa255_freq_table);
+ }
else if (cpu_is_pxa27x())
cpufreq_frequency_table_cpuinfo(policy, pxa27x_freq_table);
diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
index 35736fc08634..e16f8e3d58d3 100644
--- a/arch/arm/mach-pxa/devices.c
+++ b/arch/arm/mach-pxa/devices.c
@@ -4,13 +4,12 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
-#include <mach/gpio.h>
+#include <mach/pxa-regs.h>
#include <mach/udc.h>
#include <mach/pxafb.h>
#include <mach/mmc.h>
#include <mach/irda.h>
#include <mach/i2c.h>
-#include <mach/mfp-pxa27x.h>
#include <mach/ohci.h>
#include <mach/pxa27x_keypad.h>
#include <mach/pxa2xx_spi.h>
@@ -156,8 +155,8 @@ void __init set_pxa_fb_parent(struct device *parent_dev)
static struct resource pxa_resource_ffuart[] = {
{
- .start = __PREG(FFUART),
- .end = __PREG(FFUART) + 35,
+ .start = 0x40100000,
+ .end = 0x40100023,
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_FFUART,
@@ -175,8 +174,8 @@ struct platform_device pxa_device_ffuart= {
static struct resource pxa_resource_btuart[] = {
{
- .start = __PREG(BTUART),
- .end = __PREG(BTUART) + 35,
+ .start = 0x40200000,
+ .end = 0x40200023,
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_BTUART,
@@ -194,8 +193,8 @@ struct platform_device pxa_device_btuart = {
static struct resource pxa_resource_stuart[] = {
{
- .start = __PREG(STUART),
- .end = __PREG(STUART) + 35,
+ .start = 0x40700000,
+ .end = 0x40700023,
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_STUART,
@@ -213,8 +212,8 @@ struct platform_device pxa_device_stuart = {
static struct resource pxa_resource_hwuart[] = {
{
- .start = __PREG(HWUART),
- .end = __PREG(HWUART) + 47,
+ .start = 0x41600000,
+ .end = 0x4160002F,
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_HWUART,
@@ -249,18 +248,53 @@ struct platform_device pxa_device_i2c = {
.num_resources = ARRAY_SIZE(pxai2c_resources),
};
-static unsigned long pxa27x_i2c_mfp_cfg[] = {
- GPIO117_I2C_SCL,
- GPIO118_I2C_SDA,
-};
-
void __init pxa_set_i2c_info(struct i2c_pxa_platform_data *info)
{
- if (cpu_is_pxa27x())
- pxa2xx_mfp_config(ARRAY_AND_SIZE(pxa27x_i2c_mfp_cfg));
pxa_register_device(&pxa_device_i2c, info);
}
+#ifdef CONFIG_PXA27x
+static struct resource pxa27x_resources_i2c_power[] = {
+ {
+ .start = 0x40f00180,
+ .end = 0x40f001a3,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_PWRI2C,
+ .end = IRQ_PWRI2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device pxa27x_device_i2c_power = {
+ .name = "pxa2xx-i2c",
+ .id = 1,
+ .resource = pxa27x_resources_i2c_power,
+ .num_resources = ARRAY_SIZE(pxa27x_resources_i2c_power),
+};
+#endif
+
+#ifdef CONFIG_PXA3xx
+static struct resource pxa3xx_resources_i2c_power[] = {
+ {
+ .start = 0x40f500c0,
+ .end = 0x40f500d3,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_PWRI2C,
+ .end = IRQ_PWRI2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device pxa3xx_device_i2c_power = {
+ .name = "pxa2xx-i2c",
+ .id = 1,
+ .resource = pxa3xx_resources_i2c_power,
+ .num_resources = ARRAY_SIZE(pxa3xx_resources_i2c_power),
+};
+#endif
+
static struct resource pxai2s_resources[] = {
{
.start = 0x40400000,
@@ -296,11 +330,36 @@ void __init pxa_set_ficp_info(struct pxaficp_platform_data *info)
pxa_register_device(&pxa_device_ficp, info);
}
-struct platform_device pxa_device_rtc = {
+static struct resource pxa_rtc_resources[] = {
+ [0] = {
+ .start = 0x40900000,
+ .end = 0x40900000 + 0x3b,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_RTC1Hz,
+ .end = IRQ_RTC1Hz,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_RTCAlrm,
+ .end = IRQ_RTCAlrm,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device sa1100_device_rtc = {
.name = "sa1100-rtc",
.id = -1,
};
+struct platform_device pxa_device_rtc = {
+ .name = "pxa-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pxa_rtc_resources),
+ .resource = pxa_rtc_resources,
+};
+
static struct resource pxa_ac97_resources[] = {
[0] = {
.start = 0x40500000,
diff --git a/arch/arm/mach-pxa/devices.h b/arch/arm/mach-pxa/devices.h
index bb04af4b0aa3..ecc24a4dca6d 100644
--- a/arch/arm/mach-pxa/devices.h
+++ b/arch/arm/mach-pxa/devices.h
@@ -11,6 +11,7 @@ extern struct platform_device pxa_device_hwuart;
extern struct platform_device pxa_device_i2c;
extern struct platform_device pxa_device_i2s;
extern struct platform_device pxa_device_ficp;
+extern struct platform_device sa1100_device_rtc;
extern struct platform_device pxa_device_rtc;
extern struct platform_device pxa_device_ac97;
diff --git a/arch/arm/mach-pxa/dma.c b/arch/arm/mach-pxa/dma.c
index c0be17e0ab82..b1514fb20d3a 100644
--- a/arch/arm/mach-pxa/dma.c
+++ b/arch/arm/mach-pxa/dma.c
@@ -21,7 +21,7 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/pxa-regs.h>
diff --git a/arch/arm/mach-pxa/e330.c b/arch/arm/mach-pxa/e330.c
index d488eded2058..1bd7f740427c 100644
--- a/arch/arm/mach-pxa/e330.c
+++ b/arch/arm/mach-pxa/e330.c
@@ -1,5 +1,5 @@
/*
- * Hardware definitions for the Toshiba eseries PDAs
+ * Hardware definitions for the Toshiba e330 PDAs
*
* Copyright (c) 2003 Ian Molton <spyro@f2s.com>
*
@@ -12,6 +12,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tc6387xb.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
@@ -19,13 +22,44 @@
#include <mach/mfp-pxa25x.h>
#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/eseries-gpio.h>
#include <mach/udc.h>
#include "generic.h"
#include "eseries.h"
+#include "clock.h"
+
+/* -------------------- e330 tc6387xb parameters -------------------- */
+
+static struct tc6387xb_platform_data e330_tc6387xb_info = {
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e330_tc6387xb_device = {
+ .name = "tc6387xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e330_tc6387xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* --------------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+ &e330_tc6387xb_device,
+};
static void __init e330_init(void)
{
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
+ platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e7xx_udc_mach_info);
}
diff --git a/arch/arm/mach-pxa/e350.c b/arch/arm/mach-pxa/e350.c
index 8ecbc5479828..251129391d7d 100644
--- a/arch/arm/mach-pxa/e350.c
+++ b/arch/arm/mach-pxa/e350.c
@@ -1,5 +1,5 @@
/*
- * Hardware definitions for the Toshiba eseries PDAs
+ * Hardware definitions for the Toshiba e350 PDAs
*
* Copyright (c) 2003 Ian Molton <spyro@f2s.com>
*
@@ -12,20 +12,54 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/t7l66xb.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
#include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
#include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
#include <mach/udc.h>
#include "generic.h"
#include "eseries.h"
+#include "clock.h"
+
+/* -------------------- e350 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e350_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e350_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e350_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+ &e350_t7l66xb_device,
+};
static void __init e350_init(void)
{
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
+ platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e7xx_udc_mach_info);
}
diff --git a/arch/arm/mach-pxa/e400.c b/arch/arm/mach-pxa/e400.c
index 544bbaa20621..bed0336aca3d 100644
--- a/arch/arm/mach-pxa/e400.c
+++ b/arch/arm/mach-pxa/e400.c
@@ -12,20 +12,26 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/t7l66xb.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
-#include <mach/pxa-regs.h>
#include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
#include <mach/hardware.h>
-
+#include <mach/eseries-gpio.h>
#include <mach/pxafb.h>
#include <mach/udc.h>
#include "generic.h"
#include "eseries.h"
+#include "clock.h"
/* ------------------------ E400 LCD definitions ------------------------ */
@@ -46,7 +52,7 @@ static struct pxafb_mode_info e400_pxafb_mode_info = {
static struct pxafb_mach_info e400_pxafb_mach_info = {
.modes = &e400_pxafb_mode_info,
.num_modes = 1,
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+ .lcd_conn = LCD_COLOR_TFT_16BPP,
.lccr3 = 0,
.pxafb_backlight_power = NULL,
};
@@ -65,7 +71,10 @@ static unsigned long e400_pin_config[] __initdata = {
GPIO42_BTUART_RXD,
GPIO43_BTUART_TXD,
GPIO44_BTUART_CTS,
- GPIO45_GPIO, /* Used by TMIO for #SUSPEND */
+
+ /* TMIO controller */
+ GPIO19_GPIO, /* t7l66xb #PCLR */
+ GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
/* wakeup */
GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
@@ -73,10 +82,60 @@ static unsigned long e400_pin_config[] __initdata = {
/* ---------------------------------------------------------------------- */
+static struct mtd_partition partition_a = {
+ .name = "Internal NAND flash",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr e400_t7l66xb_nand_bbt = {
+ .options = 0,
+ .offs = 4,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+static struct tmio_nand_data e400_t7l66xb_nand_config = {
+ .num_partitions = 1,
+ .partition = &partition_a,
+ .badblock_pattern = &e400_t7l66xb_nand_bbt,
+};
+
+static struct t7l66xb_platform_data e400_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+
+ .nand_data = &e400_t7l66xb_nand_config,
+};
+
+static struct platform_device e400_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e400_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+ &e400_t7l66xb_device,
+};
+
static void __init e400_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(e400_pin_config));
+ /* Fixme - e400 may have a switched clock */
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
set_pxa_fb_info(&e400_pxafb_mach_info);
+ platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e7xx_udc_mach_info);
}
diff --git a/arch/arm/mach-pxa/e740.c b/arch/arm/mach-pxa/e740.c
index c57a15b37f0d..b00d670b2ea6 100644
--- a/arch/arm/mach-pxa/e740.c
+++ b/arch/arm/mach-pxa/e740.c
@@ -15,6 +15,8 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/mfd/t7l66xb.h>
#include <video/w100fb.h>
@@ -23,12 +25,16 @@
#include <asm/mach-types.h>
#include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
#include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
#include <mach/udc.h>
+#include <mach/irda.h>
#include "generic.h"
#include "eseries.h"
-
+#include "clock.h"
+#include "devices.h"
/* ------------------------ e740 video support --------------------------- */
@@ -116,7 +122,17 @@ static unsigned long e740_pin_config[] __initdata = {
GPIO42_BTUART_RXD,
GPIO43_BTUART_TXD,
GPIO44_BTUART_CTS,
- GPIO45_GPIO, /* Used by TMIO for #SUSPEND */
+
+ /* TMIO controller */
+ GPIO19_GPIO, /* t7l66xb #PCLR */
+ GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
+
+ /* UDC */
+ GPIO13_GPIO,
+ GPIO3_GPIO,
+
+ /* IrDA */
+ GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
/* PC Card */
GPIO8_GPIO, /* CD0 */
@@ -142,17 +158,43 @@ static unsigned long e740_pin_config[] __initdata = {
GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
};
+/* -------------------- e740 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e740_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e740_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e740_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
/* ----------------------------------------------------------------------- */
static struct platform_device *devices[] __initdata = {
&e740_fb_device,
+ &e740_t7l66xb_device,
};
static void __init e740_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(e740_pin_config));
+ eseries_register_clks();
+ clk_add_alias("CLK_CK48M", &e740_t7l66xb_device.dev,
+ "UDCCLK", &pxa25x_device_udc.dev),
+ eseries_get_tmio_gpios();
platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e7xx_udc_mach_info);
+ e7xx_irda_init();
+ pxa_set_ficp_info(&e7xx_ficp_platform_data);
}
MACHINE_START(E740, "Toshiba e740")
diff --git a/arch/arm/mach-pxa/e750.c b/arch/arm/mach-pxa/e750.c
index 640e738b85df..84d7c1aac58d 100644
--- a/arch/arm/mach-pxa/e750.c
+++ b/arch/arm/mach-pxa/e750.c
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
+#include <linux/mfd/tc6393xb.h>
#include <video/w100fb.h>
@@ -23,11 +24,15 @@
#include <asm/mach-types.h>
#include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
#include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
#include <mach/udc.h>
+#include <mach/irda.h>
#include "generic.h"
#include "eseries.h"
+#include "clock.h"
/* ---------------------- E750 LCD definitions -------------------- */
@@ -100,16 +105,45 @@ static struct platform_device e750_fb_device = {
.resource = e750_fb_resources,
};
-/* ----------------------------------------------------------------------- */
+/* ----------------- e750 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e750_tc6393xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .scr_pll2cr = 0x0cc1,
+ .scr_gper = 0,
+ .gpio_base = -1,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+};
+
+static struct platform_device e750_tc6393xb_device = {
+ .name = "tc6393xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e750_tc6393xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ------------------------------------------------------------- */
static struct platform_device *devices[] __initdata = {
&e750_fb_device,
+ &e750_tc6393xb_device,
};
static void __init e750_init(void)
{
+ clk_add_alias("CLK_CK3P6MI", &e750_tc6393xb_device.dev,
+ "GPIO11_CLK", NULL),
+ eseries_get_tmio_gpios();
platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e7xx_udc_mach_info);
+ e7xx_irda_init();
+ pxa_set_ficp_info(&e7xx_ficp_platform_data);
}
MACHINE_START(E750, "Toshiba e750")
diff --git a/arch/arm/mach-pxa/e800.c b/arch/arm/mach-pxa/e800.c
index a293e09bfe25..9a86a426f924 100644
--- a/arch/arm/mach-pxa/e800.c
+++ b/arch/arm/mach-pxa/e800.c
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
+#include <linux/mfd/tc6393xb.h>
#include <video/w100fb.h>
@@ -23,12 +24,14 @@
#include <asm/mach-types.h>
#include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
#include <mach/hardware.h>
#include <mach/eseries-gpio.h>
#include <mach/udc.h>
#include "generic.h"
#include "eseries.h"
+#include "clock.h"
/* ------------------------ e800 LCD definitions ------------------------- */
@@ -160,14 +163,41 @@ static struct pxa2xx_udc_mach_info e800_udc_mach_info = {
.gpio_pullup_inverted = 1
};
+/* ----------------- e800 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e800_tc6393xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .scr_pll2cr = 0x0cc1,
+ .scr_gper = 0,
+ .gpio_base = -1,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+};
+
+static struct platform_device e800_tc6393xb_device = {
+ .name = "tc6393xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e800_tc6393xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
/* ----------------------------------------------------------------------- */
static struct platform_device *devices[] __initdata = {
&e800_fb_device,
+ &e800_tc6393xb_device,
};
static void __init e800_init(void)
{
+ clk_add_alias("CLK_CK3P6MI", &e800_tc6393xb_device.dev,
+ "GPIO11_CLK", NULL),
+ eseries_get_tmio_gpios();
platform_add_devices(devices, ARRAY_SIZE(devices));
pxa_set_udc_info(&e800_udc_mach_info);
}
diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c
index d28849b50a14..dfce7d5b659e 100644
--- a/arch/arm/mach-pxa/eseries.c
+++ b/arch/arm/mach-pxa/eseries.c
@@ -12,6 +12,9 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
@@ -21,8 +24,10 @@
#include <mach/hardware.h>
#include <mach/eseries-gpio.h>
#include <mach/udc.h>
+#include <mach/irda.h>
#include "generic.h"
+#include "clock.h"
/* Only e800 has 128MB RAM */
void __init eseries_fixup(struct machine_desc *desc,
@@ -43,3 +48,122 @@ struct pxa2xx_udc_mach_info e7xx_udc_mach_info = {
.gpio_pullup_inverted = 1
};
+static void e7xx_irda_transceiver_mode(struct device *dev, int mode)
+{
+ if (mode & IR_OFF) {
+ gpio_set_value(GPIO_E7XX_IR_OFF, 1);
+ pxa2xx_transceiver_mode(dev, mode);
+ } else {
+ pxa2xx_transceiver_mode(dev, mode);
+ gpio_set_value(GPIO_E7XX_IR_OFF, 0);
+ }
+}
+
+int e7xx_irda_init(void)
+{
+ int ret;
+
+ ret = gpio_request(GPIO_E7XX_IR_OFF, "IrDA power");
+ if (ret)
+ goto out;
+
+ ret = gpio_direction_output(GPIO_E7XX_IR_OFF, 0);
+ if (ret)
+ goto out;
+
+ e7xx_irda_transceiver_mode(NULL, IR_SIRMODE | IR_OFF);
+out:
+ return ret;
+}
+
+static void e7xx_irda_shutdown(struct device *dev)
+{
+ e7xx_irda_transceiver_mode(dev, IR_SIRMODE | IR_OFF);
+ gpio_free(GPIO_E7XX_IR_OFF);
+}
+
+struct pxaficp_platform_data e7xx_ficp_platform_data = {
+ .transceiver_cap = IR_SIRMODE | IR_OFF,
+ .transceiver_mode = e7xx_irda_transceiver_mode,
+ .shutdown = e7xx_irda_shutdown,
+};
+
+int eseries_tmio_enable(struct platform_device *dev)
+{
+ /* Reset - bring SUSPEND high before PCLR */
+ gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+ gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 0);
+ msleep(1);
+ gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 1);
+ msleep(1);
+ gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 1);
+ msleep(1);
+ return 0;
+}
+
+int eseries_tmio_disable(struct platform_device *dev)
+{
+ gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+ gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 0);
+ return 0;
+}
+
+int eseries_tmio_suspend(struct platform_device *dev)
+{
+ gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+ return 0;
+}
+
+int eseries_tmio_resume(struct platform_device *dev)
+{
+ gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 1);
+ msleep(1);
+ return 0;
+}
+
+void eseries_get_tmio_gpios(void)
+{
+ gpio_request(GPIO_ESERIES_TMIO_SUSPEND, NULL);
+ gpio_request(GPIO_ESERIES_TMIO_PCLR, NULL);
+ gpio_direction_output(GPIO_ESERIES_TMIO_SUSPEND, 0);
+ gpio_direction_output(GPIO_ESERIES_TMIO_PCLR, 0);
+}
+
+/* TMIO controller uses the same resources on all e-series machines. */
+struct resource eseries_tmio_resources[] = {
+ [0] = {
+ .start = PXA_CS4_PHYS,
+ .end = PXA_CS4_PHYS + 0x1fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .end = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* Some e-series hardware cannot control the 32K clock */
+static void clk_32k_dummy(struct clk *clk)
+{
+}
+
+static const struct clkops clk_32k_dummy_ops = {
+ .enable = clk_32k_dummy,
+ .disable = clk_32k_dummy,
+};
+
+static struct clk tmio_dummy_clk = {
+ .ops = &clk_32k_dummy_ops,
+ .rate = 32768,
+};
+
+static struct clk_lookup eseries_clkregs[] = {
+ INIT_CLKREG(&tmio_dummy_clk, NULL, "CLK_CK32K"),
+};
+
+void eseries_register_clks(void)
+{
+ clks_register(eseries_clkregs, ARRAY_SIZE(eseries_clkregs));
+}
+
diff --git a/arch/arm/mach-pxa/eseries.h b/arch/arm/mach-pxa/eseries.h
index a83f88d4b6ad..5930f5e2a123 100644
--- a/arch/arm/mach-pxa/eseries.h
+++ b/arch/arm/mach-pxa/eseries.h
@@ -2,3 +2,15 @@ void __init eseries_fixup(struct machine_desc *desc,
struct tag *tags, char **cmdline, struct meminfo *mi);
extern struct pxa2xx_udc_mach_info e7xx_udc_mach_info;
+extern struct pxaficp_platform_data e7xx_ficp_platform_data;
+extern int e7xx_irda_init(void);
+
+extern int eseries_tmio_enable(struct platform_device *dev);
+extern int eseries_tmio_disable(struct platform_device *dev);
+extern int eseries_tmio_suspend(struct platform_device *dev);
+extern int eseries_tmio_resume(struct platform_device *dev);
+extern void eseries_get_tmio_gpios(void);
+extern struct resource eseries_tmio_resources[];
+extern struct platform_device e300_tc6387xb_device;
+extern void eseries_register_clks(void);
+
diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c
index cc3d850cc0b6..df5f822f3b6c 100644
--- a/arch/arm/mach-pxa/ezx.c
+++ b/arch/arm/mach-pxa/ezx.c
@@ -16,11 +16,14 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/pwm_backlight.h>
+#include <linux/input.h>
#include <asm/setup.h>
#include <mach/pxafb.h>
#include <mach/ohci.h>
#include <mach/i2c.h>
+#include <mach/hardware.h>
+#include <mach/pxa27x_keypad.h>
#include <mach/mfp-pxa27x.h>
#include <mach/pxa-regs.h>
@@ -101,120 +104,732 @@ static unsigned long ezx_pin_config[] __initdata = {
GPIO44_BTUART_CTS,
GPIO45_BTUART_RTS,
- /* STUART */
- GPIO46_STUART_RXD,
- GPIO47_STUART_TXD,
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
- /* For A780 support (connected with Neptune GSM chip) */
- GPIO30_USB_P3_2, /* ICL_TXENB */
- GPIO31_USB_P3_6, /* ICL_VPOUT */
- GPIO90_USB_P3_5, /* ICL_VPIN */
- GPIO91_USB_P3_1, /* ICL_XRXD */
- GPIO56_USB_P3_4, /* ICL_VMOUT */
- GPIO113_USB_P3_3, /* /ICL_VMIN */
+ /* PCAP SSP */
+ GPIO29_SSP1_SCLK,
+ GPIO25_SSP1_TXD,
+ GPIO26_SSP1_RXD,
+ GPIO24_GPIO, /* pcap chip select */
+ GPIO1_GPIO, /* pcap interrupt */
+ GPIO4_GPIO, /* WDI_AP */
+ GPIO55_GPIO, /* SYS_RESTART */
+
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+ GPIO112_MMC_CMD,
+ GPIO11_GPIO, /* mmc detect */
+
+ /* usb to external transceiver */
+ GPIO34_USB_P2_2,
+ GPIO35_USB_P2_1,
+ GPIO36_USB_P2_4,
+ GPIO39_USB_P2_6,
+ GPIO40_USB_P2_5,
+ GPIO53_USB_P2_3,
+
+ /* usb to Neptune GSM chip */
+ GPIO30_USB_P3_2,
+ GPIO31_USB_P3_6,
+ GPIO90_USB_P3_5,
+ GPIO91_USB_P3_1,
+ GPIO56_USB_P3_4,
+ GPIO113_USB_P3_3,
+};
+
+#if defined(CONFIG_MACH_EZX_A780) || defined(CONFIG_MACH_EZX_E680)
+static unsigned long gen1_pin_config[] __initdata = {
+ /* flip / lockswitch */
+ GPIO12_GPIO,
+
+ /* bluetooth (bcm2035) */
+ GPIO14_GPIO | WAKEUP_ON_LEVEL_HIGH, /* HOSTWAKE */
+ GPIO48_GPIO, /* RESET */
+ GPIO28_GPIO, /* WAKEUP */
+
+ /* Neptune handshake */
+ GPIO0_GPIO | WAKEUP_ON_LEVEL_HIGH, /* BP_RDY */
+ GPIO57_GPIO, /* AP_RDY */
+ GPIO13_GPIO | WAKEUP_ON_LEVEL_HIGH, /* WDI */
+ GPIO3_GPIO | WAKEUP_ON_LEVEL_HIGH, /* WDI2 */
+ GPIO82_GPIO, /* RESET */
+ GPIO99_GPIO, /* TC_MM_EN */
+
+ /* sound */
+ GPIO52_SSP3_SCLK,
+ GPIO83_SSP3_SFRM,
+ GPIO81_SSP3_TXD,
+ GPIO89_SSP3_RXD,
+
+ /* ssp2 pins to in */
+ GPIO22_GPIO, /* SSP2_SCLK */
+ GPIO37_GPIO, /* SSP2_SFRM */
+ GPIO38_GPIO, /* SSP2_TXD */
+ GPIO88_GPIO, /* SSP2_RXD */
+
+ /* camera */
+ GPIO23_CIF_MCLK,
+ GPIO54_CIF_PCLK,
+ GPIO85_CIF_LV,
+ GPIO84_CIF_FV,
+ GPIO27_CIF_DD_0,
+ GPIO114_CIF_DD_1,
+ GPIO51_CIF_DD_2,
+ GPIO115_CIF_DD_3,
+ GPIO95_CIF_DD_4,
+ GPIO94_CIF_DD_5,
+ GPIO17_CIF_DD_6,
+ GPIO108_CIF_DD_7,
+ GPIO50_GPIO, /* CAM_EN */
+ GPIO19_GPIO, /* CAM_RST */
+
+ /* EMU */
+ GPIO120_GPIO, /* EMU_MUX1 */
+ GPIO119_GPIO, /* EMU_MUX2 */
+ GPIO86_GPIO, /* SNP_INT_CTL */
+ GPIO87_GPIO, /* SNP_INT_IN */
+};
+#endif
+
+#if defined(CONFIG_MACH_EZX_A1200) || defined(CONFIG_MACH_EZX_A910) || \
+ defined(CONFIG_MACH_EZX_E2) || defined(CONFIG_MACH_EZX_E6)
+static unsigned long gen2_pin_config[] __initdata = {
+ /* flip / lockswitch */
+ GPIO15_GPIO,
+
+ /* EOC */
+ GPIO10_GPIO,
+
+ /* bluetooth (bcm2045) */
+ GPIO13_GPIO | WAKEUP_ON_LEVEL_HIGH, /* HOSTWAKE */
+ GPIO37_GPIO, /* RESET */
+ GPIO57_GPIO, /* WAKEUP */
+
+ /* Neptune handshake */
+ GPIO0_GPIO | WAKEUP_ON_LEVEL_HIGH, /* BP_RDY */
+ GPIO96_GPIO, /* AP_RDY */
+ GPIO3_GPIO | WAKEUP_ON_LEVEL_HIGH, /* WDI */
+ GPIO116_GPIO, /* RESET */
+ GPIO41_GPIO, /* BP_FLASH */
+
+ /* sound */
+ GPIO52_SSP3_SCLK,
+ GPIO83_SSP3_SFRM,
+ GPIO81_SSP3_TXD,
+ GPIO82_SSP3_RXD,
+
+ /* ssp2 pins to in */
+ GPIO22_GPIO, /* SSP2_SCLK */
+ GPIO14_GPIO, /* SSP2_SFRM */
+ GPIO38_GPIO, /* SSP2_TXD */
+ GPIO88_GPIO, /* SSP2_RXD */
+
+ /* camera */
+ GPIO23_CIF_MCLK,
+ GPIO54_CIF_PCLK,
+ GPIO85_CIF_LV,
+ GPIO84_CIF_FV,
+ GPIO27_CIF_DD_0,
+ GPIO114_CIF_DD_1,
+ GPIO51_CIF_DD_2,
+ GPIO115_CIF_DD_3,
+ GPIO95_CIF_DD_4,
+ GPIO48_CIF_DD_5,
+ GPIO93_CIF_DD_6,
+ GPIO12_CIF_DD_7,
+ GPIO50_GPIO, /* CAM_EN */
+ GPIO28_GPIO, /* CAM_RST */
+ GPIO17_GPIO, /* CAM_FLASH */
+};
+#endif
+
+#ifdef CONFIG_MACH_EZX_A780
+static unsigned long a780_pin_config[] __initdata = {
+ /* keypad */
+ GPIO93_KP_DKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_MKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_MKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+ GPIO107_KP_MKOUT_4,
+
+ /* attenuate sound */
+ GPIO96_GPIO,
+};
+#endif
+
+#ifdef CONFIG_MACH_EZX_E680
+static unsigned long e680_pin_config[] __initdata = {
+ /* keypad */
+ GPIO93_KP_DKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO96_KP_DKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_DKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_DKIN_5 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+
+ /* MIDI */
+ GPIO79_GPIO, /* VA_SEL_BUL */
+ GPIO80_GPIO, /* FLT_SEL_BUL */
+ GPIO78_GPIO, /* MIDI_RESET */
+ GPIO33_GPIO, /* MIDI_CS */
+ GPIO15_GPIO, /* MIDI_IRQ */
+ GPIO49_GPIO, /* MIDI_NPWE */
+ GPIO18_GPIO, /* MIDI_RDY */
+
+ /* leds */
+ GPIO46_GPIO,
+ GPIO47_GPIO,
+};
+#endif
+
+#ifdef CONFIG_MACH_EZX_A1200
+static unsigned long a1200_pin_config[] __initdata = {
+ /* keypad */
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_MKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_MKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+ GPIO107_KP_MKOUT_4,
+ GPIO108_KP_MKOUT_5,
+};
+#endif
+
+#ifdef CONFIG_MACH_EZX_A910
+static unsigned long a910_pin_config[] __initdata = {
+ /* keypad */
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_MKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_MKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+ GPIO107_KP_MKOUT_4,
+ GPIO108_KP_MKOUT_5,
+
+ /* WLAN */
+ GPIO89_GPIO, /* RESET */
+ GPIO33_GPIO, /* WAKEUP */
+ GPIO94_GPIO | WAKEUP_ON_LEVEL_HIGH, /* HOSTWAKE */
+
+ /* MMC CS */
+ GPIO20_GPIO,
+};
+#endif
+
+#ifdef CONFIG_MACH_EZX_E2
+static unsigned long e2_pin_config[] __initdata = {
+ /* keypad */
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_MKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_MKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+ GPIO107_KP_MKOUT_4,
+ GPIO108_KP_MKOUT_5,
};
+#endif
+
+#ifdef CONFIG_MACH_EZX_E6
+static unsigned long e6_pin_config[] __initdata = {
+ /* keypad */
+ GPIO100_KP_MKIN_0 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO101_KP_MKIN_1 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO102_KP_MKIN_2 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO97_KP_MKIN_3 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO98_KP_MKIN_4 | WAKEUP_ON_LEVEL_HIGH,
+ GPIO103_KP_MKOUT_0,
+ GPIO104_KP_MKOUT_1,
+ GPIO105_KP_MKOUT_2,
+ GPIO106_KP_MKOUT_3,
+ GPIO107_KP_MKOUT_4,
+ GPIO108_KP_MKOUT_5,
+};
+#endif
+
+/* KEYPAD */
+#ifdef CONFIG_MACH_EZX_A780
+static unsigned int a780_key_map[] = {
+ KEY(0, 0, KEY_SEND),
+ KEY(0, 1, KEY_BACK),
+ KEY(0, 2, KEY_END),
+ KEY(0, 3, KEY_PAGEUP),
+ KEY(0, 4, KEY_UP),
+
+ KEY(1, 0, KEY_NUMERIC_1),
+ KEY(1, 1, KEY_NUMERIC_2),
+ KEY(1, 2, KEY_NUMERIC_3),
+ KEY(1, 3, KEY_SELECT),
+ KEY(1, 4, KEY_KPENTER),
+
+ KEY(2, 0, KEY_NUMERIC_4),
+ KEY(2, 1, KEY_NUMERIC_5),
+ KEY(2, 2, KEY_NUMERIC_6),
+ KEY(2, 3, KEY_RECORD),
+ KEY(2, 4, KEY_LEFT),
+
+ KEY(3, 0, KEY_NUMERIC_7),
+ KEY(3, 1, KEY_NUMERIC_8),
+ KEY(3, 2, KEY_NUMERIC_9),
+ KEY(3, 3, KEY_HOME),
+ KEY(3, 4, KEY_RIGHT),
+
+ KEY(4, 0, KEY_NUMERIC_STAR),
+ KEY(4, 1, KEY_NUMERIC_0),
+ KEY(4, 2, KEY_NUMERIC_POUND),
+ KEY(4, 3, KEY_PAGEDOWN),
+ KEY(4, 4, KEY_DOWN),
+};
+
+static struct pxa27x_keypad_platform_data a780_keypad_platform_data = {
+ .matrix_key_rows = 5,
+ .matrix_key_cols = 5,
+ .matrix_key_map = a780_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(a780_key_map),
+
+ .direct_key_map = { KEY_CAMERA },
+ .direct_key_num = 1,
+
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_A780 */
+
+#ifdef CONFIG_MACH_EZX_E680
+static unsigned int e680_key_map[] = {
+ KEY(0, 0, KEY_UP),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_RESERVED),
+ KEY(0, 3, KEY_SEND),
+
+ KEY(1, 0, KEY_DOWN),
+ KEY(1, 1, KEY_LEFT),
+ KEY(1, 2, KEY_PAGEUP),
+ KEY(1, 3, KEY_PAGEDOWN),
+
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_RESERVED),
+ KEY(2, 2, KEY_RESERVED),
+ KEY(2, 3, KEY_KPENTER),
+};
+
+static struct pxa27x_keypad_platform_data e680_keypad_platform_data = {
+ .matrix_key_rows = 3,
+ .matrix_key_cols = 4,
+ .matrix_key_map = e680_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(e680_key_map),
+
+ .direct_key_map = {
+ KEY_CAMERA,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_F1,
+ KEY_CANCEL,
+ KEY_F2,
+ },
+ .direct_key_num = 6,
+
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_E680 */
+
+#ifdef CONFIG_MACH_EZX_A1200
+static unsigned int a1200_key_map[] = {
+ KEY(0, 0, KEY_RESERVED),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_PAGEDOWN),
+ KEY(0, 3, KEY_RESERVED),
+ KEY(0, 4, KEY_RESERVED),
+ KEY(0, 5, KEY_RESERVED),
+
+ KEY(1, 0, KEY_RESERVED),
+ KEY(1, 1, KEY_DOWN),
+ KEY(1, 2, KEY_CAMERA),
+ KEY(1, 3, KEY_RESERVED),
+ KEY(1, 4, KEY_RESERVED),
+ KEY(1, 5, KEY_RESERVED),
+
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_KPENTER),
+ KEY(2, 2, KEY_RECORD),
+ KEY(2, 3, KEY_RESERVED),
+ KEY(2, 4, KEY_RESERVED),
+ KEY(2, 5, KEY_SELECT),
+
+ KEY(3, 0, KEY_RESERVED),
+ KEY(3, 1, KEY_UP),
+ KEY(3, 2, KEY_SEND),
+ KEY(3, 3, KEY_RESERVED),
+ KEY(3, 4, KEY_RESERVED),
+ KEY(3, 5, KEY_RESERVED),
+
+ KEY(4, 0, KEY_RESERVED),
+ KEY(4, 1, KEY_LEFT),
+ KEY(4, 2, KEY_PAGEUP),
+ KEY(4, 3, KEY_RESERVED),
+ KEY(4, 4, KEY_RESERVED),
+ KEY(4, 5, KEY_RESERVED),
+};
+
+static struct pxa27x_keypad_platform_data a1200_keypad_platform_data = {
+ .matrix_key_rows = 5,
+ .matrix_key_cols = 6,
+ .matrix_key_map = a1200_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(a1200_key_map),
+
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_A1200 */
+
+#ifdef CONFIG_MACH_EZX_E6
+static unsigned int e6_key_map[] = {
+ KEY(0, 0, KEY_RESERVED),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_PAGEDOWN),
+ KEY(0, 3, KEY_RESERVED),
+ KEY(0, 4, KEY_RESERVED),
+ KEY(0, 5, KEY_NEXTSONG),
+
+ KEY(1, 0, KEY_RESERVED),
+ KEY(1, 1, KEY_DOWN),
+ KEY(1, 2, KEY_PROG1),
+ KEY(1, 3, KEY_RESERVED),
+ KEY(1, 4, KEY_RESERVED),
+ KEY(1, 5, KEY_RESERVED),
+
+ KEY(2, 0, KEY_RESERVED),
+ KEY(2, 1, KEY_ENTER),
+ KEY(2, 2, KEY_CAMERA),
+ KEY(2, 3, KEY_RESERVED),
+ KEY(2, 4, KEY_RESERVED),
+ KEY(2, 5, KEY_WWW),
+
+ KEY(3, 0, KEY_RESERVED),
+ KEY(3, 1, KEY_UP),
+ KEY(3, 2, KEY_SEND),
+ KEY(3, 3, KEY_RESERVED),
+ KEY(3, 4, KEY_RESERVED),
+ KEY(3, 5, KEY_PLAYPAUSE),
+
+ KEY(4, 0, KEY_RESERVED),
+ KEY(4, 1, KEY_LEFT),
+ KEY(4, 2, KEY_PAGEUP),
+ KEY(4, 3, KEY_RESERVED),
+ KEY(4, 4, KEY_RESERVED),
+ KEY(4, 5, KEY_PREVIOUSSONG),
+};
+
+static struct pxa27x_keypad_platform_data e6_keypad_platform_data = {
+ .matrix_key_rows = 5,
+ .matrix_key_cols = 6,
+ .matrix_key_map = e6_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(e6_key_map),
-static void __init ezx_init(void)
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_E6 */
+
+#ifdef CONFIG_MACH_EZX_A910
+static unsigned int a910_key_map[] = {
+ KEY(0, 0, KEY_NUMERIC_6),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_PAGEDOWN),
+ KEY(0, 3, KEY_KPENTER),
+ KEY(0, 4, KEY_NUMERIC_5),
+ KEY(0, 5, KEY_CAMERA),
+
+ KEY(1, 0, KEY_NUMERIC_8),
+ KEY(1, 1, KEY_DOWN),
+ KEY(1, 2, KEY_RESERVED),
+ KEY(1, 3, KEY_F1), /* Left SoftKey */
+ KEY(1, 4, KEY_NUMERIC_STAR),
+ KEY(1, 5, KEY_RESERVED),
+
+ KEY(2, 0, KEY_NUMERIC_7),
+ KEY(2, 1, KEY_NUMERIC_9),
+ KEY(2, 2, KEY_RECORD),
+ KEY(2, 3, KEY_F2), /* Right SoftKey */
+ KEY(2, 4, KEY_BACK),
+ KEY(2, 5, KEY_SELECT),
+
+ KEY(3, 0, KEY_NUMERIC_2),
+ KEY(3, 1, KEY_UP),
+ KEY(3, 2, KEY_SEND),
+ KEY(3, 3, KEY_NUMERIC_0),
+ KEY(3, 4, KEY_NUMERIC_1),
+ KEY(3, 5, KEY_RECORD),
+
+ KEY(4, 0, KEY_NUMERIC_4),
+ KEY(4, 1, KEY_LEFT),
+ KEY(4, 2, KEY_PAGEUP),
+ KEY(4, 3, KEY_NUMERIC_POUND),
+ KEY(4, 4, KEY_NUMERIC_3),
+ KEY(4, 5, KEY_RESERVED),
+};
+
+static struct pxa27x_keypad_platform_data a910_keypad_platform_data = {
+ .matrix_key_rows = 5,
+ .matrix_key_cols = 6,
+ .matrix_key_map = a910_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(a910_key_map),
+
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_A910 */
+
+#ifdef CONFIG_MACH_EZX_E2
+static unsigned int e2_key_map[] = {
+ KEY(0, 0, KEY_NUMERIC_6),
+ KEY(0, 1, KEY_RIGHT),
+ KEY(0, 2, KEY_NUMERIC_9),
+ KEY(0, 3, KEY_NEXTSONG),
+ KEY(0, 4, KEY_NUMERIC_5),
+ KEY(0, 5, KEY_F1), /* Left SoftKey */
+
+ KEY(1, 0, KEY_NUMERIC_8),
+ KEY(1, 1, KEY_DOWN),
+ KEY(1, 2, KEY_RESERVED),
+ KEY(1, 3, KEY_PAGEUP),
+ KEY(1, 4, KEY_NUMERIC_STAR),
+ KEY(1, 5, KEY_F2), /* Right SoftKey */
+
+ KEY(2, 0, KEY_NUMERIC_7),
+ KEY(2, 1, KEY_KPENTER),
+ KEY(2, 2, KEY_RECORD),
+ KEY(2, 3, KEY_PAGEDOWN),
+ KEY(2, 4, KEY_BACK),
+ KEY(2, 5, KEY_NUMERIC_0),
+
+ KEY(3, 0, KEY_NUMERIC_2),
+ KEY(3, 1, KEY_UP),
+ KEY(3, 2, KEY_SEND),
+ KEY(3, 3, KEY_PLAYPAUSE),
+ KEY(3, 4, KEY_NUMERIC_1),
+ KEY(3, 5, KEY_SOUND), /* Music SoftKey */
+
+ KEY(4, 0, KEY_NUMERIC_4),
+ KEY(4, 1, KEY_LEFT),
+ KEY(4, 2, KEY_NUMERIC_POUND),
+ KEY(4, 3, KEY_PREVIOUSSONG),
+ KEY(4, 4, KEY_NUMERIC_3),
+ KEY(4, 5, KEY_RESERVED),
+};
+
+static struct pxa27x_keypad_platform_data e2_keypad_platform_data = {
+ .matrix_key_rows = 5,
+ .matrix_key_cols = 6,
+ .matrix_key_map = e2_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(e2_key_map),
+
+ .debounce_interval = 30,
+};
+#endif /* CONFIG_MACH_EZX_E2 */
+
+#ifdef CONFIG_MACH_EZX_A780
+static void __init a780_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen1_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(a780_pin_config));
+
pxa_set_i2c_info(NULL);
- if (machine_is_ezx_a780() || machine_is_ezx_e680())
- set_pxa_fb_info(&ezx_fb_info_1);
- else
- set_pxa_fb_info(&ezx_fb_info_2);
- platform_add_devices(devices, ARRAY_SIZE(devices));
-}
+ set_pxa_fb_info(&ezx_fb_info_1);
-static void __init ezx_fixup(struct machine_desc *desc, struct tag *tags,
- char **cmdline, struct meminfo *mi)
-{
- /* We have two ram chips. First one with 32MB at 0xA0000000 and a second
- * 16MB one at 0xAC000000
- */
- mi->nr_banks = 2;
- mi->bank[0].start = 0xa0000000;
- mi->bank[0].node = 0;
- mi->bank[0].size = (32*1024*1024);
- mi->bank[1].start = 0xac000000;
- mi->bank[1].node = 1;
- mi->bank[1].size = (16*1024*1024);
+ pxa_set_keypad_info(&a780_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
}
-#ifdef CONFIG_MACH_EZX_A780
MACHINE_START(EZX_A780, "Motorola EZX A780")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = a780_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_EZX_E680
+static struct i2c_board_info __initdata e680_i2c_board_info[] = {
+ { I2C_BOARD_INFO("tea5767", 0x81) },
+};
+
+static void __init e680_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen1_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e680_pin_config));
+
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(e680_i2c_board_info));
+
+ set_pxa_fb_info(&ezx_fb_info_1);
+
+ pxa_set_keypad_info(&e680_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
MACHINE_START(EZX_E680, "Motorola EZX E680")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = e680_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_EZX_A1200
+static struct i2c_board_info __initdata a1200_i2c_board_info[] = {
+ { I2C_BOARD_INFO("tea5767", 0x81) },
+};
+
+static void __init a1200_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen2_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(a1200_pin_config));
+
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(a1200_i2c_board_info));
+
+ set_pxa_fb_info(&ezx_fb_info_2);
+
+ pxa_set_keypad_info(&a1200_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
MACHINE_START(EZX_A1200, "Motorola EZX A1200")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = a1200_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_EZX_A910
+static void __init a910_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen2_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(a910_pin_config));
+
+ pxa_set_i2c_info(NULL);
+
+ set_pxa_fb_info(&ezx_fb_info_2);
+
+ pxa_set_keypad_info(&a910_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
MACHINE_START(EZX_A910, "Motorola EZX A910")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = a910_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_EZX_E6
+static struct i2c_board_info __initdata e6_i2c_board_info[] = {
+ { I2C_BOARD_INFO("tea5767", 0x81) },
+};
+
+static void __init e6_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen2_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e6_pin_config));
+
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(e6_i2c_board_info));
+
+ set_pxa_fb_info(&ezx_fb_info_2);
+
+ pxa_set_keypad_info(&e6_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
MACHINE_START(EZX_E6, "Motorola EZX E6")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = e6_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_EZX_E2
+static struct i2c_board_info __initdata e2_i2c_board_info[] = {
+ { I2C_BOARD_INFO("tea5767", 0x81) },
+};
+
+static void __init e2_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(ezx_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(gen2_pin_config));
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e2_pin_config));
+
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(e2_i2c_board_info));
+
+ set_pxa_fb_info(&ezx_fb_info_2);
+
+ pxa_set_keypad_info(&e2_keypad_platform_data);
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
MACHINE_START(EZX_E2, "Motorola EZX E2")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = ezx_fixup,
.boot_params = 0xa0000100,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = &ezx_init,
+ .init_machine = e2_init,
MACHINE_END
#endif
diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
index 85ed0b33331f..0ccc91c92c44 100644
--- a/arch/arm/mach-pxa/generic.c
+++ b/arch/arm/mach-pxa/generic.c
@@ -24,6 +24,7 @@
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/mach/map.h>
+#include <asm/mach-types.h>
#include <mach/pxa-regs.h>
#include <mach/reset.h>
@@ -39,6 +40,21 @@ void clear_reset_status(unsigned int mask)
pxa3xx_clear_reset_status(mask);
}
+unsigned long get_clock_tick_rate(void)
+{
+ unsigned long clock_tick_rate;
+
+ if (cpu_is_pxa25x())
+ clock_tick_rate = 3686400;
+ else if (machine_is_mainstone())
+ clock_tick_rate = 3249600;
+ else
+ clock_tick_rate = 3250000;
+
+ return clock_tick_rate;
+}
+EXPORT_SYMBOL(get_clock_tick_rate);
+
/*
* Get the clock frequency as reflected by CCCR and the turbo flag.
* We assume these values have been applied via a fcs.
diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c
index 14930cf8be7b..5fec1e479cb3 100644
--- a/arch/arm/mach-pxa/gpio.c
+++ b/arch/arm/mach-pxa/gpio.c
@@ -25,6 +25,18 @@
#include "generic.h"
+#define GPIO0_BASE ((void __iomem *)io_p2v(0x40E00000))
+#define GPIO1_BASE ((void __iomem *)io_p2v(0x40E00004))
+#define GPIO2_BASE ((void __iomem *)io_p2v(0x40E00008))
+#define GPIO3_BASE ((void __iomem *)io_p2v(0x40E00100))
+
+#define GPLR_OFFSET 0x00
+#define GPDR_OFFSET 0x0C
+#define GPSR_OFFSET 0x18
+#define GPCR_OFFSET 0x24
+#define GRER_OFFSET 0x30
+#define GFER_OFFSET 0x3C
+#define GEDR_OFFSET 0x48
struct pxa_gpio_chip {
struct gpio_chip chip;
@@ -33,6 +45,18 @@ struct pxa_gpio_chip {
int pxa_last_gpio;
+#ifdef CONFIG_CPU_PXA26x
+/* GPIO86/87/88/89 on PXA26x have their direction bits in GPDR2 inverted,
+ * as well as their Alternate Function value being '1' for GPIO in GAFRx.
+ */
+static int __gpio_is_inverted(unsigned gpio)
+{
+ return cpu_is_pxa25x() && gpio > 85;
+}
+#else
+#define __gpio_is_inverted(gpio) (0)
+#endif
+
/*
* Configure pins for GPIO or other functions
*/
@@ -75,7 +99,10 @@ static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
gpdr = pxa->regbase + GPDR_OFFSET;
local_irq_save(flags);
value = __raw_readl(gpdr);
- value &= ~mask;
+ if (__gpio_is_inverted(chip->base + offset))
+ value |= mask;
+ else
+ value &= ~mask;
__raw_writel(value, gpdr);
local_irq_restore(flags);
@@ -97,7 +124,10 @@ static int pxa_gpio_direction_output(struct gpio_chip *chip,
gpdr = pxa->regbase + GPDR_OFFSET;
local_irq_save(flags);
tmp = __raw_readl(gpdr);
- tmp |= mask;
+ if (__gpio_is_inverted(chip->base + offset))
+ tmp &= ~mask;
+ else
+ tmp |= mask;
__raw_writel(tmp, gpdr);
local_irq_restore(flags);
@@ -173,10 +203,17 @@ static unsigned long GPIO_IRQ_mask[4];
*/
static int __gpio_is_occupied(unsigned gpio)
{
- if (cpu_is_pxa25x() || cpu_is_pxa27x())
- return GAFR(gpio) & (0x3 << (((gpio) & 0xf) * 2));
- else
- return 0;
+ if (cpu_is_pxa27x() || cpu_is_pxa25x()) {
+ int af = (GAFR(gpio) >> ((gpio & 0xf) * 2)) & 0x3;
+ int dir = GPDR(gpio) & GPIO_bit(gpio);
+
+ if (__gpio_is_inverted(gpio))
+ return af != 1 || dir == 0;
+ else
+ return af != 0 || dir != 0;
+ }
+
+ return 0;
}
static int pxa_gpio_irq_type(unsigned int irq, unsigned int type)
@@ -190,9 +227,8 @@ static int pxa_gpio_irq_type(unsigned int irq, unsigned int type)
/* Don't mess with enabled GPIOs using preconfigured edges or
* GPIOs set to alternate function or to output during probe
*/
- if ((GPIO_IRQ_rising_edge[idx] |
- GPIO_IRQ_falling_edge[idx] |
- GPDR(gpio)) & GPIO_bit(gpio))
+ if ((GPIO_IRQ_rising_edge[idx] & GPIO_bit(gpio)) ||
+ (GPIO_IRQ_falling_edge[idx] & GPIO_bit(gpio)))
return 0;
if (__gpio_is_occupied(gpio))
@@ -201,7 +237,10 @@ static int pxa_gpio_irq_type(unsigned int irq, unsigned int type)
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
}
- GPDR(gpio) &= ~GPIO_bit(gpio);
+ if (__gpio_is_inverted(gpio))
+ GPDR(gpio) |= GPIO_bit(gpio);
+ else
+ GPDR(gpio) &= ~GPIO_bit(gpio);
if (type & IRQ_TYPE_EDGE_RISING)
__set_bit(gpio, GPIO_IRQ_rising_edge);
diff --git a/arch/arm/mach-pxa/gumstix.c b/arch/arm/mach-pxa/gumstix.c
index d8962a0fb98d..e296ce11658c 100644
--- a/arch/arm/mach-pxa/gumstix.c
+++ b/arch/arm/mach-pxa/gumstix.c
@@ -184,15 +184,22 @@ static unsigned long gumstix_pin_config[] __initdata = {
GPIO6_MMC_CLK,
GPIO53_MMC_CLK,
GPIO8_MMC_CS0,
- /* these are used by AM200EPD */
- GPIO51_GPIO,
- GPIO49_GPIO,
- GPIO48_GPIO,
- GPIO32_GPIO,
- GPIO17_GPIO,
- GPIO16_GPIO,
};
+int __attribute__((weak)) am200_init(void)
+{
+ return 0;
+}
+
+static void __init carrier_board_init(void)
+{
+ /*
+ * put carrier/expansion board init here if
+ * they cannot be detected programatically
+ */
+ am200_init();
+}
+
static void __init gumstix_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(gumstix_pin_config));
@@ -201,6 +208,7 @@ static void __init gumstix_init(void)
gumstix_udc_init();
gumstix_mmc_init();
(void) platform_add_devices(devices, ARRAY_SIZE(devices));
+ carrier_board_init();
}
MACHINE_START(GUMSTIX, "Gumstix")
diff --git a/arch/arm/mach-pxa/h5000.c b/arch/arm/mach-pxa/h5000.c
new file mode 100644
index 000000000000..da6e4422c0f3
--- /dev/null
+++ b/arch/arm/mach-pxa/h5000.c
@@ -0,0 +1,200 @@
+/*
+ * Hardware definitions for HP iPAQ h5xxx Handheld Computers
+ *
+ * Copyright 2000-2003 Hewlett-Packard Company.
+ * Copyright 2002 Jamey Hicks <jamey.hicks@hp.com>
+ * Copyright 2004-2005 Phil Blundell <pb@handhelds.org>
+ * Copyright 2007-2008 Anton Vorontsov <cbouatmailru@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.
+ *
+ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Author: Jamey Hicks.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/h5000.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-regs.h>
+#include <mach/mfp-pxa25x.h>
+#include <mach/udc.h>
+#include "generic.h"
+
+/*
+ * Flash
+ */
+
+static struct mtd_partition h5000_flash0_partitions[] = {
+ {
+ .name = "bootldr",
+ .size = 0x00040000,
+ .offset = 0,
+ .mask_flags = MTD_WRITEABLE,
+ },
+ {
+ .name = "root",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ },
+};
+
+static struct mtd_partition h5000_flash1_partitions[] = {
+ {
+ .name = "second root",
+ .size = SZ_16M - 0x00040000,
+ .offset = 0,
+ },
+ {
+ .name = "asset",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE,
+ },
+};
+
+static struct physmap_flash_data h5000_flash0_data = {
+ .width = 4,
+ .parts = h5000_flash0_partitions,
+ .nr_parts = ARRAY_SIZE(h5000_flash0_partitions),
+};
+
+static struct physmap_flash_data h5000_flash1_data = {
+ .width = 4,
+ .parts = h5000_flash1_partitions,
+ .nr_parts = ARRAY_SIZE(h5000_flash1_partitions),
+};
+
+static struct resource h5000_flash0_resources = {
+ .start = PXA_CS0_PHYS,
+ .end = PXA_CS0_PHYS + SZ_32M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT,
+};
+
+static struct resource h5000_flash1_resources = {
+ .start = PXA_CS0_PHYS + SZ_32M,
+ .end = PXA_CS0_PHYS + SZ_32M + SZ_16M - 1,
+ .flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT,
+};
+
+static struct platform_device h5000_flash[] = {
+ {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = &h5000_flash0_resources,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &h5000_flash0_data,
+ },
+ },
+ {
+ .name = "physmap-flash",
+ .id = 1,
+ .resource = &h5000_flash1_resources,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &h5000_flash1_data,
+ },
+ },
+};
+
+/*
+ * USB Device Controller
+ */
+
+static struct pxa2xx_udc_mach_info h5000_udc_mach_info __initdata = {
+ .gpio_pullup = H5000_GPIO_USB_PULLUP,
+};
+
+/*
+ * GPIO setup
+ */
+
+static unsigned long h5000_pin_config[] __initdata = {
+ /* Crystal and Clock Signals */
+ GPIO12_32KHz,
+
+ /* SDRAM and Static Memory I/O Signals */
+ GPIO15_nCS_1,
+ GPIO78_nCS_2,
+ GPIO79_nCS_3,
+ GPIO80_nCS_4,
+
+ /* FFUART */
+ GPIO34_FFUART_RXD,
+ GPIO35_FFUART_CTS,
+ GPIO36_FFUART_DCD,
+ GPIO37_FFUART_DSR,
+ GPIO38_FFUART_RI,
+ GPIO39_FFUART_TXD,
+ GPIO40_FFUART_DTR,
+ GPIO41_FFUART_RTS,
+
+ /* BTUART */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+ GPIO45_BTUART_RTS,
+
+ /* SSP1 */
+ GPIO23_SSP1_SCLK,
+ GPIO25_SSP1_TXD,
+ GPIO26_SSP1_RXD,
+};
+
+/*
+ * Localbus setup:
+ * CS0: Flash;
+ * CS1: MediaQ chip, select 16-bit bus and vlio;
+ * CS5: SAMCOP.
+ */
+
+static void fix_msc(void)
+{
+ MSC0 = 0x129c24f2;
+ MSC1 = 0x7ff424fa;
+ MSC2 = 0x7ff47ff4;
+
+ MDREFR |= 0x02080000;
+}
+
+/*
+ * Platform devices
+ */
+
+static struct platform_device *devices[] __initdata = {
+ &h5000_flash[0],
+ &h5000_flash[1],
+};
+
+static void __init h5000_init(void)
+{
+ fix_msc();
+
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(h5000_pin_config));
+ pxa_set_udc_info(&h5000_udc_mach_info);
+ platform_add_devices(ARRAY_AND_SIZE(devices));
+}
+
+MACHINE_START(H5400, "HP iPAQ H5000")
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = h5000_init,
+MACHINE_END
diff --git a/arch/arm/mach-pxa/imote2.c b/arch/arm/mach-pxa/imote2.c
new file mode 100644
index 000000000000..364c5e271330
--- /dev/null
+++ b/arch/arm/mach-pxa/imote2.c
@@ -0,0 +1,575 @@
+/*
+ * linux/arch/arm/mach-pxa/imote2.c
+ *
+ * Author: Ed C. Epp
+ * Created: Nov 05, 2002
+ * Copyright: Intel Corp.
+ *
+ * Modified 2008: Jonathan Cameron
+ *
+ * The Imote2 is a wireless sensor node platform sold
+ * by Crossbow (www.xbow.com).
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/mfd/da903x.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+
+#include <mach/i2c.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-regs.h>
+#include <mach/mfp-pxa27x.h>
+#include <mach/regs-ssp.h>
+#include <mach/udc.h>
+#include <mach/mmc.h>
+#include <mach/pxa2xx_spi.h>
+#include <mach/pxa27x-udc.h>
+
+#include "devices.h"
+#include "generic.h"
+
+static unsigned long imote2_pin_config[] __initdata = {
+
+ /* Device Identification for wakeup*/
+ GPIO102_GPIO,
+
+ /* Button */
+ GPIO91_GPIO,
+
+ /* DA9030 */
+ GPIO1_GPIO,
+
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO112_MMC_CMD,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+
+ /* 802.15.4 radio - driver out of mainline */
+ GPIO22_GPIO, /* CC_RSTN */
+ GPIO114_GPIO, /* CC_FIFO */
+ GPIO116_GPIO, /* CC_CCA */
+ GPIO0_GPIO, /* CC_FIFOP */
+ GPIO16_GPIO, /* CCSFD */
+ GPIO39_GPIO, /* CSn */
+ GPIO115_GPIO, /* Power enable */
+
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
+
+ /* SSP 3 - 802.15.4 radio */
+ GPIO39_GPIO, /* Chip Select */
+ GPIO34_SSP3_SCLK,
+ GPIO35_SSP3_TXD,
+ GPIO41_SSP3_RXD,
+
+ /* SSP 2 - to daughter boards */
+ GPIO37_GPIO, /* Chip Select */
+ GPIO36_SSP2_SCLK,
+ GPIO38_SSP2_TXD,
+ GPIO11_SSP2_RXD,
+
+ /* SSP 1 - to daughter boards */
+ GPIO24_GPIO, /* Chip Select */
+ GPIO23_SSP1_SCLK,
+ GPIO25_SSP1_TXD,
+ GPIO26_SSP1_RXD,
+
+ /* BTUART Basic Connector*/
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+ GPIO45_BTUART_RTS,
+
+ /* STUART Serial console via debug board*/
+ GPIO46_STUART_RXD,
+ GPIO47_STUART_TXD,
+
+ /* Basic sensor board */
+ GPIO96_GPIO, /* accelerometer interrupt */
+ GPIO99_GPIO, /* ADC interrupt */
+
+ /* Connector pins specified as gpios */
+ GPIO94_GPIO, /* large basic connector pin 14 */
+ GPIO10_GPIO, /* large basic connector pin 23 */
+
+ /* LEDS */
+ GPIO103_GPIO, /* red led */
+ GPIO104_GPIO, /* green led */
+ GPIO105_GPIO, /* blue led */
+};
+
+static struct gpio_led imote2_led_pins[] = {
+ {
+ .name = "imote2:red",
+ .gpio = 103,
+ .active_low = 1,
+ }, {
+ .name = "imote2:green",
+ .gpio = 104,
+ .active_low = 1,
+ }, {
+ .name = "imote2:blue",
+ .gpio = 105,
+ .active_low = 1,
+ },
+};
+
+static struct gpio_led_platform_data imote2_led_data = {
+ .num_leds = ARRAY_SIZE(imote2_led_pins),
+ .leds = imote2_led_pins,
+};
+
+static struct platform_device imote2_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &imote2_led_data,
+ },
+};
+
+/* Reverse engineered partly from Platformx drivers */
+enum imote2_ldos{
+ vcc_vref,
+ vcc_cc2420,
+ vcc_mica,
+ vcc_bt,
+ /* The two voltages available to sensor boards */
+ vcc_sensor_1_8,
+ vcc_sensor_3,
+
+ vcc_sram_ext, /* directly connected to the pxa271 */
+ vcc_pxa_pll,
+ vcc_pxa_usim, /* Reference voltage for certain gpios */
+ vcc_pxa_mem,
+ vcc_pxa_flash,
+ vcc_pxa_core, /*Dc-Dc buck not yet supported */
+ vcc_lcd,
+ vcc_bb,
+ vcc_bbio,
+ vcc_io, /* cc2420 802.15.4 radio and pxa vcc_io ?*/
+};
+
+/* The values of the various regulator constraints are obviously dependent
+ * on exactly what is wired to each ldo. Unfortunately this information is
+ * not generally available. More information has been requested from Xbow
+ * but as of yet they haven't been forthcoming.
+ *
+ * Some of these are clearly Stargate 2 related (no way of plugging
+ * in an lcd on the IM2 for example!).
+ */
+static struct regulator_init_data imote2_ldo_init_data[] = {
+ [vcc_bbio] = {
+ .constraints = { /* board default 1.8V */
+ .name = "vcc_bbio",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ },
+ },
+ [vcc_bb] = {
+ .constraints = { /* board default 2.8V */
+ .name = "vcc_bb",
+ .min_uV = 2700000,
+ .max_uV = 3000000,
+ },
+ },
+ [vcc_pxa_flash] = {
+ .constraints = {/* default is 1.8V */
+ .name = "vcc_pxa_flash",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ },
+ },
+ [vcc_cc2420] = { /* also vcc_io */
+ .constraints = {
+ /* board default is 2.8V */
+ .name = "vcc_cc2420",
+ .min_uV = 2700000,
+ .max_uV = 3300000,
+ },
+ },
+ [vcc_vref] = { /* Reference for what? */
+ .constraints = { /* default 1.8V */
+ .name = "vcc_vref",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ },
+ },
+ [vcc_sram_ext] = {
+ .constraints = { /* default 2.8V */
+ .name = "vcc_sram_ext",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ },
+ },
+ [vcc_mica] = {
+ .constraints = { /* default 2.8V */
+ .name = "vcc_mica",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ },
+ },
+ [vcc_bt] = {
+ .constraints = { /* default 2.8V */
+ .name = "vcc_bt",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ },
+ },
+ [vcc_lcd] = {
+ .constraints = { /* default 2.8V */
+ .name = "vcc_lcd",
+ .min_uV = 2700000,
+ .max_uV = 3300000,
+ },
+ },
+ [vcc_io] = { /* Same or higher than everything
+ * bar vccbat and vccusb */
+ .constraints = { /* default 2.8V */
+ .name = "vcc_io",
+ .min_uV = 2692000,
+ .max_uV = 3300000,
+ },
+ },
+ [vcc_sensor_1_8] = {
+ .constraints = { /* default 1.8V */
+ .name = "vcc_sensor_1_8",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ },
+ },
+ [vcc_sensor_3] = { /* curiously default 2.8V */
+ .constraints = {
+ .name = "vcc_sensor_3",
+ .min_uV = 2800000,
+ .max_uV = 3000000,
+ },
+ },
+ [vcc_pxa_pll] = { /* 1.17V - 1.43V, default 1.3V*/
+ .constraints = {
+ .name = "vcc_pxa_pll",
+ .min_uV = 1170000,
+ .max_uV = 1430000,
+ },
+ },
+ [vcc_pxa_usim] = {
+ .constraints = { /* default 1.8V */
+ .name = "vcc_pxa_usim",
+ .min_uV = 1710000,
+ .max_uV = 2160000,
+ },
+ },
+ [vcc_pxa_mem] = {
+ .constraints = { /* default 1.8V */
+ .name = "vcc_pxa_mem",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ },
+ },
+};
+
+static struct da903x_subdev_info imote2_da9030_subdevs[] = {
+ {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO2,
+ .platform_data = &imote2_ldo_init_data[vcc_bbio],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO3,
+ .platform_data = &imote2_ldo_init_data[vcc_bb],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO4,
+ .platform_data = &imote2_ldo_init_data[vcc_pxa_flash],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO5,
+ .platform_data = &imote2_ldo_init_data[vcc_cc2420],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO6,
+ .platform_data = &imote2_ldo_init_data[vcc_vref],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO7,
+ .platform_data = &imote2_ldo_init_data[vcc_sram_ext],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO8,
+ .platform_data = &imote2_ldo_init_data[vcc_mica],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO9,
+ .platform_data = &imote2_ldo_init_data[vcc_bt],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO10,
+ .platform_data = &imote2_ldo_init_data[vcc_sensor_1_8],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO11,
+ .platform_data = &imote2_ldo_init_data[vcc_sensor_3],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO12,
+ .platform_data = &imote2_ldo_init_data[vcc_lcd],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO15,
+ .platform_data = &imote2_ldo_init_data[vcc_pxa_pll],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO17,
+ .platform_data = &imote2_ldo_init_data[vcc_pxa_usim],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO18,
+ .platform_data = &imote2_ldo_init_data[vcc_io],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO19,
+ .platform_data = &imote2_ldo_init_data[vcc_pxa_mem],
+ },
+};
+
+static struct da903x_platform_data imote2_da9030_pdata = {
+ .num_subdevs = ARRAY_SIZE(imote2_da9030_subdevs),
+ .subdevs = imote2_da9030_subdevs,
+};
+
+/* As the the imote2 doesn't currently have a conventional SD slot
+ * there is no option to hotplug cards, making all this rather simple
+ */
+static int imote2_mci_get_ro(struct device *dev)
+{
+ return 0;
+}
+
+/* Rather simple case as hotplugging not possible */
+static struct pxamci_platform_data imote2_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, /* default anyway */
+ .get_ro = imote2_mci_get_ro,
+};
+
+static struct mtd_partition imote2flash_partitions[] = {
+ {
+ .name = "Bootloader",
+ .size = 0x00040000,
+ .offset = 0,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "Kernel",
+ .size = 0x00200000,
+ .offset = 0x00040000,
+ .mask_flags = 0,
+ }, {
+ .name = "Filesystem",
+ .size = 0x01DC0000,
+ .offset = 0x00240000,
+ .mask_flags = 0,
+ },
+};
+
+static struct resource flash_resources = {
+ .start = PXA_CS0_PHYS,
+ .end = PXA_CS0_PHYS + SZ_32M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct flash_platform_data imote2_flash_data = {
+ .map_name = "cfi_probe",
+ .parts = imote2flash_partitions,
+ .nr_parts = ARRAY_SIZE(imote2flash_partitions),
+ .name = "PXA27xOnChipROM",
+ .width = 2,
+};
+
+static struct platform_device imote2_flash_device = {
+ .name = "pxa2xx-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &imote2_flash_data,
+ },
+ .resource = &flash_resources,
+ .num_resources = 1,
+};
+
+/* Some of the drivers here are out of kernel at the moment (parts of IIO)
+ * and it may be a while before they are in the mainline.
+ */
+static struct i2c_board_info __initdata imote2_i2c_board_info[] = {
+ { /* UCAM sensor board */
+ .type = "max1238",
+ .addr = 0x35,
+ }, { /* ITS400 Sensor board only */
+ .type = "max1363",
+ .addr = 0x34,
+ /* Through a nand gate - Also beware, on V2 sensor board the
+ * pull up resistors are missing.
+ */
+ .irq = IRQ_GPIO(99),
+ }, { /* ITS400 Sensor board only */
+ .type = "tsl2561",
+ .addr = 0x49,
+ /* Through a nand gate - Also beware, on V2 sensor board the
+ * pull up resistors are missing.
+ */
+ .irq = IRQ_GPIO(99),
+ }, { /* ITS400 Sensor board only */
+ .type = "tmp175",
+ .addr = 0x4A,
+ .irq = IRQ_GPIO(96),
+ },
+};
+
+static struct i2c_board_info __initdata imote2_pwr_i2c_board_info[] = {
+ {
+ .type = "da9030",
+ .addr = 0x49,
+ .platform_data = &imote2_da9030_pdata,
+ .irq = gpio_to_irq(1),
+ },
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_0_info = {
+ .num_chipselect = 1,
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_1_info = {
+ .num_chipselect = 1,
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_2_info = {
+ .num_chipselect = 1,
+};
+
+/* Patch posted by Eric Miao <eric.miao@marvell.com> will remove
+ * the need for these functions.
+ */
+static void spi1control(u32 command)
+{
+ gpio_set_value(24, command & PXA2XX_CS_ASSERT ? 0 : 1);
+};
+
+static void spi3control(u32 command)
+{
+ gpio_set_value(39, command & PXA2XX_CS_ASSERT ? 0 : 1);
+};
+
+static struct pxa2xx_spi_chip staccel_chip_info = {
+ .tx_threshold = 8,
+ .rx_threshold = 8,
+ .dma_burst_size = 8,
+ .timeout = 235,
+ .cs_control = spi1control,
+};
+
+static struct pxa2xx_spi_chip cc2420_info = {
+ .tx_threshold = 8,
+ .rx_threshold = 8,
+ .dma_burst_size = 8,
+ .timeout = 235,
+ .cs_control = spi3control,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+ { /* Driver in IIO */
+ .modalias = "lis3l02dq",
+ .max_speed_hz = 8000000,/* 8MHz max spi frequency at 3V */
+ .bus_num = 1,
+ .chip_select = 0,
+ .controller_data = &staccel_chip_info,
+ .irq = IRQ_GPIO(96),
+ }, { /* Driver out of kernel as it needs considerable rewriting */
+ .modalias = "cc2420",
+ .max_speed_hz = 6500000,
+ .bus_num = 3,
+ .chip_select = 0,
+ .controller_data = &cc2420_info,
+ },
+};
+
+static void im2_udc_command(int cmd)
+{
+ switch (cmd) {
+ case PXA2XX_UDC_CMD_CONNECT:
+ UP2OCR |= UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
+ break;
+ case PXA2XX_UDC_CMD_DISCONNECT:
+ UP2OCR &= ~(UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE);
+ break;
+ }
+}
+
+static struct pxa2xx_udc_mach_info imote2_udc_info __initdata = {
+ .udc_command = im2_udc_command,
+};
+
+static struct platform_device *imote2_devices[] = {
+ &imote2_flash_device,
+ &imote2_leds,
+};
+
+static struct i2c_pxa_platform_data i2c_pwr_pdata = {
+ .fast_mode = 1,
+};
+
+static struct i2c_pxa_platform_data i2c_pdata = {
+ .fast_mode = 1,
+};
+
+static void __init imote2_init(void)
+{
+
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(imote2_pin_config));
+ /* SPI chip select directions - all other directions should
+ * be handled by drivers.*/
+ gpio_direction_output(37, 0);
+ gpio_direction_output(24, 0);
+ gpio_direction_output(39, 0);
+
+ platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices));
+
+ pxa2xx_set_spi_info(1, &pxa_ssp_master_0_info);
+ pxa2xx_set_spi_info(2, &pxa_ssp_master_1_info);
+ pxa2xx_set_spi_info(3, &pxa_ssp_master_2_info);
+
+ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+
+ i2c_register_board_info(0, imote2_i2c_board_info,
+ ARRAY_SIZE(imote2_i2c_board_info));
+ i2c_register_board_info(1, imote2_pwr_i2c_board_info,
+ ARRAY_SIZE(imote2_pwr_i2c_board_info));
+
+ pxa27x_set_i2c_power_info(&i2c_pwr_pdata);
+ pxa_set_i2c_info(&i2c_pdata);
+
+ pxa_set_mci_info(&imote2_mci_platform_data);
+ pxa_set_udc_info(&imote2_udc_info);
+}
+
+MACHINE_START(INTELMOTE2, "IMOTE 2")
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa27x_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = imote2_init,
+ .boot_params = 0xA0000100,
+MACHINE_END
diff --git a/arch/arm/mach-pxa/include/mach/clkdev.h b/arch/arm/mach-pxa/include/mach/clkdev.h
new file mode 100644
index 000000000000..04b37a89801c
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-pxa/include/mach/dma.h b/arch/arm/mach-pxa/include/mach/dma.h
index 955bfe606067..7804637a6df3 100644
--- a/arch/arm/mach-pxa/include/mach/dma.h
+++ b/arch/arm/mach-pxa/include/mach/dma.h
@@ -30,10 +30,6 @@ typedef enum {
DMA_PRIO_LOW = 2
} pxa_dma_prio;
-#if defined(CONFIG_MACH_ARMCORE) && defined(CONFIG_PCI)
-#define HAVE_ARCH_PCI_SET_DMA_MASK 1
-#endif
-
/*
* DMA registration
*/
diff --git a/arch/arm/mach-pxa/include/mach/eseries-gpio.h b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
index 4c90b1310270..efbd2aa9ecec 100644
--- a/arch/arm/mach-pxa/include/mach/eseries-gpio.h
+++ b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
@@ -43,8 +43,10 @@
#define GPIO_E800_PCMCIA_PWR1 73
/* e7xx IrDA power control */
-#define GPIO_E7XX_IR_ON 38
+#define GPIO_E7XX_IR_OFF 38
/* ASIC related GPIOs */
#define GPIO_ESERIES_TMIO_IRQ 5
+#define GPIO_ESERIES_TMIO_PCLR 19
+#define GPIO_ESERIES_TMIO_SUSPEND 45
#define GPIO_E800_ANGELX_IRQ 8
diff --git a/arch/arm/mach-pxa/include/mach/gumstix.h b/arch/arm/mach-pxa/include/mach/gumstix.h
index 42ee1956750e..099f54a41de4 100644
--- a/arch/arm/mach-pxa/include/mach/gumstix.h
+++ b/arch/arm/mach-pxa/include/mach/gumstix.h
@@ -94,3 +94,7 @@ has detected a cable insertion; driven low otherwise. */
#define GPIO26_PRDY_nBSY_MD (GPIO26_PRDY_nBSY | GPIO_IN)
#define GPIO27_PRDY_nBSY_MD (GPIO27_PRDY_nBSY | GPIO_IN)
#define GPIO36_nCD_MD (GPIO36_nCD | GPIO_IN)
+
+/* for expansion boards that can't be programatically detected */
+extern int am200_init(void);
+
diff --git a/arch/arm/mach-pxa/include/mach/h5000.h b/arch/arm/mach-pxa/include/mach/h5000.h
new file mode 100644
index 000000000000..2a5ae3802787
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/h5000.h
@@ -0,0 +1,113 @@
+/*
+ * Hardware definitions for HP iPAQ h5xxx Handheld Computers
+ *
+ * Copyright(20)02 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Author: Jamey Hicks
+ */
+
+#ifndef __ASM_ARCH_H5000_H
+#define __ASM_ARCH_H5000_H
+
+#include <mach/mfp-pxa25x.h>
+
+/*
+ * CPU GPIOs
+ */
+
+#define H5000_GPIO_POWER_BUTTON (0)
+#define H5000_GPIO_RESET_BUTTON_N (1)
+#define H5000_GPIO_OPT_INT (2)
+#define H5000_GPIO_BACKUP_POWER (3)
+#define H5000_GPIO_ACTION_BUTTON (4)
+#define H5000_GPIO_COM_DCD_SOMETHING (5) /* what is this really ? */
+/* 6 not connected */
+#define H5000_GPIO_RESET_BUTTON_AGAIN_N (7) /* connected to gpio 1 as well */
+/* 8 not connected */
+#define H5000_GPIO_RSO_N (9) /* reset output from max1702 which regulates 3.3 and 2.5 */
+#define H5000_GPIO_ASIC_INT_N (10) /* from companion asic */
+#define H5000_GPIO_BT_ENV_0 (11) /* to LMX9814, set to 1 according to regdump */
+/*(12) not connected */
+#define H5000_GPIO_BT_ENV_1 (13) /* to LMX9814, set to 1 according to regdump */
+#define H5000_GPIO_BT_WU (14) /* from LMX9814, Defined as HOST_WAKEUP in the LMX9820 data sheet */
+/*(15) is CS1# */
+/*(16) not connected */
+/*(17) not connected */
+/*(18) is pcmcia ready */
+/*(19) is dreq1 */
+/*(20) is dreq0 */
+#define H5000_GPIO_OE_RD_NWR (21) /* output enable on rd/nwr signal to companion asic */
+/*(22) is not connected */
+#define H5000_GPIO_OPT_SPI_CLK (23) /* to extension pack */
+#define H5000_GPIO_OPT_SPI_CS_N (24) /* to extension pack */
+#define H5000_GPIO_OPT_SPI_DOUT (25) /* to extension pack */
+#define H5000_GPIO_OPT_SPI_DIN (26) /* to extension pack */
+/*(27) not connected */
+#define H5000_GPIO_I2S_BITCLK (28) /* connected to AC97 codec */
+#define H5000_GPIO_I2S_DATAOUT (29) /* connected to AC97 codec */
+#define H5000_GPIO_I2S_DATAIN (30) /* connected to AC97 codec */
+#define H5000_GPIO_I2S_LRCLK (31) /* connected to AC97 codec */
+#define H5000_GPIO_I2S_SYSCLK (32) /* connected to AC97 codec */
+/*(33) is CS5# */
+#define H5000_GPIO_COM_RXD (34) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_CTS (35) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_DCD (36) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_DSR (37) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_RI (38) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_TXD (39) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_DTR (40) /* connected to cradle/cable connector */
+#define H5000_GPIO_COM_RTS (41) /* connected to cradle/cable connector */
+
+#define H5000_GPIO_BT_RXD (42) /* connected to BT (LMX9814) */
+#define H5000_GPIO_BT_TXD (43) /* connected to BT (LMX9814) */
+#define H5000_GPIO_BT_CTS (44) /* connected to BT (LMX9814) */
+#define H5000_GPIO_BT_RTS (45) /* connected to BT (LMX9814) */
+
+#define H5000_GPIO_IRDA_RXD (46)
+#define H5000_GPIO_IRDA_TXD (47)
+
+#define H5000_GPIO_POE_N (48) /* used for pcmcia */
+#define H5000_GPIO_PWE_N (49) /* used for pcmcia */
+#define H5000_GPIO_PIOR_N (50) /* used for pcmcia */
+#define H5000_GPIO_PIOW_N (51) /* used for pcmcia */
+#define H5000_GPIO_PCE1_N (52) /* used for pcmcia */
+#define H5000_GPIO_PCE2_N (53) /* used for pcmcia */
+#define H5000_GPIO_PSKTSEL (54) /* used for pcmcia */
+#define H5000_GPIO_PREG_N (55) /* used for pcmcia */
+#define H5000_GPIO_PWAIT_N (56) /* used for pcmcia */
+#define H5000_GPIO_IOIS16_N (57) /* used for pcmcia */
+
+#define H5000_GPIO_IRDA_SD (58) /* to hsdl3002 sd */
+/*(59) not connected */
+#define H5000_GPIO_POWER_SD_N (60) /* controls power to SD */
+#define H5000_GPIO_POWER_RS232_N (61) /* inverted FORCEON to rs232 transceiver */
+#define H5000_GPIO_POWER_ACCEL_N (62) /* controls power to accel */
+/*(63) is not connected */
+#define H5000_GPIO_OPT_NVRAM (64) /* controls power to expansion pack */
+#define H5000_GPIO_CHG_EN (65) /* to sc801 en */
+#define H5000_GPIO_USB_PULLUP (66) /* USB d+ pullup via 1.5K resistor */
+#define H5000_GPIO_BT_2V8_N (67) /* 2.8V used by bluetooth */
+#define H5000_GPIO_EXT_CHG_RATE (68) /* enables external charging rate */
+/*(69) is not connected */
+#define H5000_GPIO_CIR_RESET (70) /* consumer IR reset */
+#define H5000_GPIO_POWER_LIGHT_SENSOR_N (71)
+#define H5000_GPIO_BT_M_RESET (72)
+#define H5000_GPIO_STD_CHG_RATE (73)
+#define H5000_GPIO_SD_WP_N (74)
+#define H5000_GPIO_MOTOR_ON_N (75) /* external pullup on this */
+#define H5000_GPIO_HEADPHONE_DETECT (76)
+#define H5000_GPIO_USB_CHG_RATE (77) /* select rate for charging via usb */
+/*(78) is CS2# */
+/*(79) is CS3# */
+/*(80) is CS4# */
+
+#endif /* __ASM_ARCH_H5000_H */
diff --git a/arch/arm/mach-pxa/include/mach/hardware.h b/arch/arm/mach-pxa/include/mach/hardware.h
index a582a6d9b92b..16ab79547dae 100644
--- a/arch/arm/mach-pxa/include/mach/hardware.h
+++ b/arch/arm/mach-pxa/include/mach/hardware.h
@@ -102,6 +102,9 @@
* PXA930 B0 0x69056835 0x5E643013
* PXA930 B1 0x69056837 0x7E643013
* PXA930 B2 0x69056838 0x8E643013
+ *
+ * PXA935 A0 0x56056931 0x1E653013
+ * PXA935 B0 0x56056936 0x6E653013
*/
#ifdef CONFIG_PXA25x
#define __cpu_is_pxa210(id) \
@@ -178,12 +181,22 @@
#define __cpu_is_pxa930(id) \
({ \
unsigned int _id = (id) >> 4 & 0xfff; \
- _id == 0x683; \
+ _id == 0x683; \
})
#else
#define __cpu_is_pxa930(id) (0)
#endif
+#ifdef CONFIG_CPU_PXA935
+#define __cpu_is_pxa935(id) \
+ ({ \
+ unsigned int _id = (id) >> 4 & 0xfff; \
+ _id == 0x693; \
+ })
+#else
+#define __cpu_is_pxa935(id) (0)
+#endif
+
#define cpu_is_pxa210() \
({ \
__cpu_is_pxa210(read_cpuid_id()); \
@@ -204,8 +217,6 @@
__cpu_is_pxa25x(read_cpuid_id()); \
})
-extern int cpu_is_pxa26x(void);
-
#define cpu_is_pxa27x() \
({ \
__cpu_is_pxa27x(read_cpuid_id()); \
@@ -232,6 +243,12 @@ extern int cpu_is_pxa26x(void);
__cpu_is_pxa930(id); \
})
+#define cpu_is_pxa935() \
+ ({ \
+ unsigned int id = read_cpuid(CPUID_ID); \
+ __cpu_is_pxa935(id); \
+ })
+
/*
* CPUID Core Generation Bit
* <= 0x2 for pxa21x/pxa25x/pxa26x/pxa27x
@@ -249,6 +266,12 @@ extern int cpu_is_pxa26x(void);
_id == 0x3; \
})
+#define __cpu_is_pxa9xx(id) \
+ ({ \
+ unsigned int _id = (id) >> 4 & 0xfff; \
+ _id == 0x683 || _id == 0x693; \
+ })
+
#define cpu_is_pxa2xx() \
({ \
__cpu_is_pxa2xx(read_cpuid_id()); \
@@ -259,32 +282,25 @@ extern int cpu_is_pxa26x(void);
__cpu_is_pxa3xx(read_cpuid_id()); \
})
-/*
- * Handy routine to set GPIO alternate functions
- */
-extern int pxa_gpio_mode( int gpio_mode );
-
-/*
- * Return GPIO level, nonzero means high, zero is low
- */
-extern int pxa_gpio_get_value(unsigned gpio);
-
-/*
- * Set output GPIO level
- */
-extern void pxa_gpio_set_value(unsigned gpio, int value);
-
+#define cpu_is_pxa9xx() \
+ ({ \
+ __cpu_is_pxa9xx(read_cpuid_id()); \
+ })
/*
* return current memory and LCD clock frequency in units of 10kHz
*/
extern unsigned int get_memclk_frequency_10khz(void);
+/* return the clock tick rate of the OS timer */
+extern unsigned long get_clock_tick_rate(void);
#endif
#if defined(CONFIG_MACH_ARMCORE) && defined(CONFIG_PCI)
#define PCIBIOS_MIN_IO 0
#define PCIBIOS_MIN_MEM 0
#define pcibios_assign_all_busses() 1
+#define HAVE_ARCH_PCI_SET_DMA_MASK 1
#endif
+
#endif /* _ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-pxa/include/mach/io.h b/arch/arm/mach-pxa/include/mach/io.h
index 600fd4f76603..262691fb97d8 100644
--- a/arch/arm/mach-pxa/include/mach/io.h
+++ b/arch/arm/mach-pxa/include/mach/io.h
@@ -6,15 +6,13 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
-
#define IO_SPACE_LIMIT 0xffffffff
/*
* We don't actually have real ISA nor PCI buses, but there is so many
* drivers out there that might just work if we fake them...
*/
-#define __io(a) ((void __iomem *)(a))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-pxa/include/mach/littleton.h b/arch/arm/mach-pxa/include/mach/littleton.h
index 5c4e320c1437..6c9b21c51322 100644
--- a/arch/arm/mach-pxa/include/mach/littleton.h
+++ b/arch/arm/mach-pxa/include/mach/littleton.h
@@ -1,8 +1,13 @@
-#ifndef __ASM_ARCH_ZYLONITE_H
-#define __ASM_ARCH_ZYLONITE_H
+#ifndef __ASM_ARCH_LITTLETON_H
+#define __ASM_ARCH_LITTLETON_H
+
+#include <mach/gpio.h>
#define LITTLETON_ETH_PHYS 0x30000000
#define LITTLETON_GPIO_LCD_CS (17)
-#endif /* __ASM_ARCH_ZYLONITE_H */
+#define EXT0_GPIO_BASE (NR_BUILTIN_GPIO)
+#define EXT0_GPIO(x) (EXT0_GPIO_BASE + (x))
+
+#endif /* __ASM_ARCH_LITTLETON_H */
diff --git a/arch/arm/mach-pxa/include/mach/memory.h b/arch/arm/mach-pxa/include/mach/memory.h
index 59aef89808d6..f626730ee42e 100644
--- a/arch/arm/mach-pxa/include/mach/memory.h
+++ b/arch/arm/mach-pxa/include/mach/memory.h
@@ -18,16 +18,6 @@
#define PHYS_OFFSET UL(0xa0000000)
/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-/*
* The nodes are matched with the physical SDRAM banks as follows:
*
* node 0: 0xa0000000-0xa3ffffff --> 0xc0000000-0xc3ffffff
@@ -47,6 +37,7 @@ void cmx2xx_pci_adjust_zones(int node, unsigned long *size,
cmx2xx_pci_adjust_zones(node, size, holes)
#define ISA_DMA_THRESHOLD (PHYS_OFFSET + SZ_64M - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_64M)
#endif
#endif
diff --git a/arch/arm/mach-pxa/include/mach/mfp-pxa25x.h b/arch/arm/mach-pxa/include/mach/mfp-pxa25x.h
index 617cab2cc8d0..a72869b73ee3 100644
--- a/arch/arm/mach-pxa/include/mach/mfp-pxa25x.h
+++ b/arch/arm/mach-pxa/include/mach/mfp-pxa25x.h
@@ -158,4 +158,35 @@
#define GPIO76_LCD_PCLK MFP_CFG_OUT(GPIO76, AF2, DRIVE_LOW)
#define GPIO77_LCD_BIAS MFP_CFG_OUT(GPIO77, AF2, DRIVE_LOW)
+#ifdef CONFIG_CPU_PXA26x
+/* GPIO */
+#define GPIO85_GPIO MFP_CFG_IN(GPIO85, AF0)
+#define GPIO86_GPIO MFP_CFG_IN(GPIO86, AF1)
+#define GPIO87_GPIO MFP_CFG_IN(GPIO87, AF1)
+#define GPIO88_GPIO MFP_CFG_IN(GPIO88, AF1)
+#define GPIO89_GPIO MFP_CFG_IN(GPIO89, AF1)
+
+/* SDRAM */
+#define GPIO86_nSDCS2 MFP_CFG_OUT(GPIO86, AF0, DRIVE_HIGH)
+#define GPIO87_nSDCS3 MFP_CFG_OUT(GPIO87, AF0, DRIVE_HIGH)
+#define GPIO88_RDnWR MFP_CFG_OUT(GPIO88, AF0, DRIVE_HIGH)
+#define GPIO89_nACRESET MFP_CFG_OUT(GPIO89, AF0, DRIVE_HIGH)
+
+/* USB */
+#define GPIO9_USB_RCV MFP_CFG_IN(GPIO9, AF1)
+#define GPIO32_USB_VP MFP_CFG_IN(GPIO32, AF2)
+#define GPIO34_USB_VM MFP_CFG_IN(GPIO34, AF2)
+#define GPIO39_USB_VPO MFP_CFG_OUT(GPIO39, AF3, DRIVE_LOW)
+#define GPIO56_USB_VMO MFP_CFG_OUT(GPIO56, AF1, DRIVE_LOW)
+#define GPIO57_USB_nOE MFP_CFG_OUT(GPIO57, AF1, DRIVE_HIGH)
+
+/* ASSP */
+#define GPIO28_ASSP_BITCLK_IN MFP_CFG_IN(GPIO28, AF3)
+#define GPIO28_ASSP_BITCLK_OUT MFP_CFG_OUT(GPIO28, AF3, DRIVE_LOW)
+#define GPIO29_ASSP_RXD MFP_CFG_IN(GPIO29, AF3)
+#define GPIO30_ASSP_TXD MFP_CFG_OUT(GPIO30, AF3, DRIVE_LOW)
+#define GPIO31_ASSP_SFRM_IN MFP_CFG_IN(GPIO31, AF1)
+#define GPIO31_ASSP_SFRM_OUT MFP_CFG_OUT(GPIO31, AF3, DRIVE_LOW)
+#endif
+
#endif /* __ASM_ARCH_MFP_PXA25X_H */
diff --git a/arch/arm/mach-pxa/include/mach/mfp-pxa27x.h b/arch/arm/mach-pxa/include/mach/mfp-pxa27x.h
index 122bdbd53182..da4f85a4f990 100644
--- a/arch/arm/mach-pxa/include/mach/mfp-pxa27x.h
+++ b/arch/arm/mach-pxa/include/mach/mfp-pxa27x.h
@@ -11,6 +11,12 @@
#include <mach/mfp.h>
#include <mach/mfp-pxa2xx.h>
+/* Note: GPIO3/GPIO4 will be driven by Power I2C when PCFR/PI2C_EN
+ * bit is set, regardless of the GPIO configuration
+ */
+#define GPIO3_GPIO MFP_CFG_IN(GPIO3, AF0)
+#define GPIO4_GPIO MFP_CFG_IN(GPIO4, AF0)
+
/* GPIO */
#define GPIO85_GPIO MFP_CFG_IN(GPIO85, AF0)
#define GPIO86_GPIO MFP_CFG_IN(GPIO86, AF0)
diff --git a/arch/arm/mach-pxa/include/mach/mfp-pxa930.h b/arch/arm/mach-pxa/include/mach/mfp-pxa930.h
index fabd9b4df827..fa73f56a1372 100644
--- a/arch/arm/mach-pxa/include/mach/mfp-pxa930.h
+++ b/arch/arm/mach-pxa/include/mach/mfp-pxa930.h
@@ -421,6 +421,7 @@
#define GPIO20_PWM0 MFP_CFG_LPM(GPIO20, AF2, PULL_LOW)
#define GPIO21_PWM2 MFP_CFG_LPM(GPIO21, AF3, PULL_LOW)
#define GPIO22_PWM3 MFP_CFG_LPM(GPIO22, AF3, PULL_LOW)
+#define GPIO32_PWM0 MFP_CFG_LPM(GPIO32, AF4, PULL_LOW)
/* CIR */
#define GPIO46_CIR_OUT MFP_CFG(GPIO46, AF1)
diff --git a/arch/arm/mach-pxa/include/mach/mioa701.h b/arch/arm/mach-pxa/include/mach/mioa701.h
index 8483cb511831..02868447b0b1 100644
--- a/arch/arm/mach-pxa/include/mach/mioa701.h
+++ b/arch/arm/mach-pxa/include/mach/mioa701.h
@@ -10,12 +10,14 @@
(MFP_PIN(pin) | MFP_##af | MFP_DIR_OUT | MFP_LPM_##state))
/* Global GPIOs */
-#define GPIO9_CHARGE_nEN 9
+#define GPIO9_CHARGE_EN 9
#define GPIO18_POWEROFF 18
#define GPIO87_LCD_POWER 87
+#define GPIO96_AC_DETECT 96
+#define GPIO80_MAYBE_CHARGE_VDROP 80 /* Drop of 88mV */
/* USB */
-#define GPIO13_USB_DETECT 13
+#define GPIO13_nUSB_DETECT 13
#define GPIO22_USB_ENABLE 22
/* SDIO bits */
@@ -24,7 +26,10 @@
#define GPIO91_SDIO_EN 91
/* Bluetooth */
+#define GPIO14_BT_nACTIVITY 14
#define GPIO83_BT_ON 83
+#define GPIO77_BT_UNKNOWN1 77
+#define GPIO86_BT_MAYBE_nRESET 86
/* GPS */
#define GPIO23_GPS_UNKNOWN1 23
diff --git a/arch/arm/mach-pxa/include/mach/mtd-xip.h b/arch/arm/mach-pxa/include/mach/mtd-xip.h
index 4d452fcb1508..cfca8155be72 100644
--- a/arch/arm/mach-pxa/include/mach/mtd-xip.h
+++ b/arch/arm/mach-pxa/include/mach/mtd-xip.h
@@ -15,6 +15,7 @@
#ifndef __ARCH_PXA_MTD_XIP_H__
#define __ARCH_PXA_MTD_XIP_H__
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#define xip_irqpending() (ICIP & ICMR)
diff --git a/arch/arm/mach-pxa/include/mach/palmasoc.h b/arch/arm/mach-pxa/include/mach/palmasoc.h
new file mode 100644
index 000000000000..6c4b1f7de20a
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/palmasoc.h
@@ -0,0 +1,13 @@
+#ifndef _INCLUDE_PALMASOC_H_
+#define _INCLUDE_PALMASOC_H_
+struct palm27x_asoc_info {
+ int jack_gpio;
+};
+
+#ifdef CONFIG_SND_PXA2XX_SOC_PALM27X
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data);
+#else
+static inline void palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) {}
+#endif
+
+#endif
diff --git a/arch/arm/mach-pxa/include/mach/pxa-regs.h b/arch/arm/mach-pxa/include/mach/pxa-regs.h
index 15295d960000..31d615aa7723 100644
--- a/arch/arm/mach-pxa/include/mach/pxa-regs.h
+++ b/arch/arm/mach-pxa/include/mach/pxa-regs.h
@@ -13,6 +13,7 @@
#ifndef __PXA_REGS_H
#define __PXA_REGS_H
+#include <mach/hardware.h>
/*
* PXA Chip selects
@@ -123,298 +124,6 @@
#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */
#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */
-
-/*
- * UARTs
- */
-
-/* Full Function UART (FFUART) */
-#define FFUART FFRBR
-#define FFRBR __REG(0x40100000) /* Receive Buffer Register (read only) */
-#define FFTHR __REG(0x40100000) /* Transmit Holding Register (write only) */
-#define FFIER __REG(0x40100004) /* Interrupt Enable Register (read/write) */
-#define FFIIR __REG(0x40100008) /* Interrupt ID Register (read only) */
-#define FFFCR __REG(0x40100008) /* FIFO Control Register (write only) */
-#define FFLCR __REG(0x4010000C) /* Line Control Register (read/write) */
-#define FFMCR __REG(0x40100010) /* Modem Control Register (read/write) */
-#define FFLSR __REG(0x40100014) /* Line Status Register (read only) */
-#define FFMSR __REG(0x40100018) /* Modem Status Register (read only) */
-#define FFSPR __REG(0x4010001C) /* Scratch Pad Register (read/write) */
-#define FFISR __REG(0x40100020) /* Infrared Selection Register (read/write) */
-#define FFDLL __REG(0x40100000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
-#define FFDLH __REG(0x40100004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
-
-/* Bluetooth UART (BTUART) */
-#define BTUART BTRBR
-#define BTRBR __REG(0x40200000) /* Receive Buffer Register (read only) */
-#define BTTHR __REG(0x40200000) /* Transmit Holding Register (write only) */
-#define BTIER __REG(0x40200004) /* Interrupt Enable Register (read/write) */
-#define BTIIR __REG(0x40200008) /* Interrupt ID Register (read only) */
-#define BTFCR __REG(0x40200008) /* FIFO Control Register (write only) */
-#define BTLCR __REG(0x4020000C) /* Line Control Register (read/write) */
-#define BTMCR __REG(0x40200010) /* Modem Control Register (read/write) */
-#define BTLSR __REG(0x40200014) /* Line Status Register (read only) */
-#define BTMSR __REG(0x40200018) /* Modem Status Register (read only) */
-#define BTSPR __REG(0x4020001C) /* Scratch Pad Register (read/write) */
-#define BTISR __REG(0x40200020) /* Infrared Selection Register (read/write) */
-#define BTDLL __REG(0x40200000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
-#define BTDLH __REG(0x40200004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
-
-/* Standard UART (STUART) */
-#define STUART STRBR
-#define STRBR __REG(0x40700000) /* Receive Buffer Register (read only) */
-#define STTHR __REG(0x40700000) /* Transmit Holding Register (write only) */
-#define STIER __REG(0x40700004) /* Interrupt Enable Register (read/write) */
-#define STIIR __REG(0x40700008) /* Interrupt ID Register (read only) */
-#define STFCR __REG(0x40700008) /* FIFO Control Register (write only) */
-#define STLCR __REG(0x4070000C) /* Line Control Register (read/write) */
-#define STMCR __REG(0x40700010) /* Modem Control Register (read/write) */
-#define STLSR __REG(0x40700014) /* Line Status Register (read only) */
-#define STMSR __REG(0x40700018) /* Reserved */
-#define STSPR __REG(0x4070001C) /* Scratch Pad Register (read/write) */
-#define STISR __REG(0x40700020) /* Infrared Selection Register (read/write) */
-#define STDLL __REG(0x40700000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
-#define STDLH __REG(0x40700004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
-
-/* Hardware UART (HWUART) */
-#define HWUART HWRBR
-#define HWRBR __REG(0x41600000) /* Receive Buffer Register (read only) */
-#define HWTHR __REG(0x41600000) /* Transmit Holding Register (write only) */
-#define HWIER __REG(0x41600004) /* Interrupt Enable Register (read/write) */
-#define HWIIR __REG(0x41600008) /* Interrupt ID Register (read only) */
-#define HWFCR __REG(0x41600008) /* FIFO Control Register (write only) */
-#define HWLCR __REG(0x4160000C) /* Line Control Register (read/write) */
-#define HWMCR __REG(0x41600010) /* Modem Control Register (read/write) */
-#define HWLSR __REG(0x41600014) /* Line Status Register (read only) */
-#define HWMSR __REG(0x41600018) /* Modem Status Register (read only) */
-#define HWSPR __REG(0x4160001C) /* Scratch Pad Register (read/write) */
-#define HWISR __REG(0x41600020) /* Infrared Selection Register (read/write) */
-#define HWFOR __REG(0x41600024) /* Receive FIFO Occupancy Register (read only) */
-#define HWABR __REG(0x41600028) /* Auto-Baud Control Register (read/write) */
-#define HWACR __REG(0x4160002C) /* Auto-Baud Count Register (read only) */
-#define HWDLL __REG(0x41600000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
-#define HWDLH __REG(0x41600004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
-
-#define IER_DMAE (1 << 7) /* DMA Requests Enable */
-#define IER_UUE (1 << 6) /* UART Unit Enable */
-#define IER_NRZE (1 << 5) /* NRZ coding Enable */
-#define IER_RTIOE (1 << 4) /* Receiver Time Out Interrupt Enable */
-#define IER_MIE (1 << 3) /* Modem Interrupt Enable */
-#define IER_RLSE (1 << 2) /* Receiver Line Status Interrupt Enable */
-#define IER_TIE (1 << 1) /* Transmit Data request Interrupt Enable */
-#define IER_RAVIE (1 << 0) /* Receiver Data Available Interrupt Enable */
-
-#define IIR_FIFOES1 (1 << 7) /* FIFO Mode Enable Status */
-#define IIR_FIFOES0 (1 << 6) /* FIFO Mode Enable Status */
-#define IIR_TOD (1 << 3) /* Time Out Detected */
-#define IIR_IID2 (1 << 2) /* Interrupt Source Encoded */
-#define IIR_IID1 (1 << 1) /* Interrupt Source Encoded */
-#define IIR_IP (1 << 0) /* Interrupt Pending (active low) */
-
-#define FCR_ITL2 (1 << 7) /* Interrupt Trigger Level */
-#define FCR_ITL1 (1 << 6) /* Interrupt Trigger Level */
-#define FCR_RESETTF (1 << 2) /* Reset Transmitter FIFO */
-#define FCR_RESETRF (1 << 1) /* Reset Receiver FIFO */
-#define FCR_TRFIFOE (1 << 0) /* Transmit and Receive FIFO Enable */
-#define FCR_ITL_1 (0)
-#define FCR_ITL_8 (FCR_ITL1)
-#define FCR_ITL_16 (FCR_ITL2)
-#define FCR_ITL_32 (FCR_ITL2|FCR_ITL1)
-
-#define LCR_DLAB (1 << 7) /* Divisor Latch Access Bit */
-#define LCR_SB (1 << 6) /* Set Break */
-#define LCR_STKYP (1 << 5) /* Sticky Parity */
-#define LCR_EPS (1 << 4) /* Even Parity Select */
-#define LCR_PEN (1 << 3) /* Parity Enable */
-#define LCR_STB (1 << 2) /* Stop Bit */
-#define LCR_WLS1 (1 << 1) /* Word Length Select */
-#define LCR_WLS0 (1 << 0) /* Word Length Select */
-
-#define LSR_FIFOE (1 << 7) /* FIFO Error Status */
-#define LSR_TEMT (1 << 6) /* Transmitter Empty */
-#define LSR_TDRQ (1 << 5) /* Transmit Data Request */
-#define LSR_BI (1 << 4) /* Break Interrupt */
-#define LSR_FE (1 << 3) /* Framing Error */
-#define LSR_PE (1 << 2) /* Parity Error */
-#define LSR_OE (1 << 1) /* Overrun Error */
-#define LSR_DR (1 << 0) /* Data Ready */
-
-#define MCR_LOOP (1 << 4)
-#define MCR_OUT2 (1 << 3) /* force MSR_DCD in loopback mode */
-#define MCR_OUT1 (1 << 2) /* force MSR_RI in loopback mode */
-#define MCR_RTS (1 << 1) /* Request to Send */
-#define MCR_DTR (1 << 0) /* Data Terminal Ready */
-
-#define MSR_DCD (1 << 7) /* Data Carrier Detect */
-#define MSR_RI (1 << 6) /* Ring Indicator */
-#define MSR_DSR (1 << 5) /* Data Set Ready */
-#define MSR_CTS (1 << 4) /* Clear To Send */
-#define MSR_DDCD (1 << 3) /* Delta Data Carrier Detect */
-#define MSR_TERI (1 << 2) /* Trailing Edge Ring Indicator */
-#define MSR_DDSR (1 << 1) /* Delta Data Set Ready */
-#define MSR_DCTS (1 << 0) /* Delta Clear To Send */
-
-/*
- * IrSR (Infrared Selection Register)
- */
-#define STISR_RXPL (1 << 4) /* Receive Data Polarity */
-#define STISR_TXPL (1 << 3) /* Transmit Data Polarity */
-#define STISR_XMODE (1 << 2) /* Transmit Pulse Width Select */
-#define STISR_RCVEIR (1 << 1) /* Receiver SIR Enable */
-#define STISR_XMITIR (1 << 0) /* Transmitter SIR Enable */
-
-
-/*
- * I2C registers - moved into drivers/i2c/busses/i2c-pxa.c
- */
-
-/*
- * Serial Audio Controller - moved into sound/soc/pxa/pxa2xx-i2s.c
- */
-
-/*
- * AC97 Controller registers
- */
-
-#define POCR __REG(0x40500000) /* PCM Out Control Register */
-#define POCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
-#define POCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
-
-#define PICR __REG(0x40500004) /* PCM In Control Register */
-#define PICR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
-#define PICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
-
-#define MCCR __REG(0x40500008) /* Mic In Control Register */
-#define MCCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
-#define MCCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
-
-#define GCR __REG(0x4050000C) /* Global Control Register */
-#ifdef CONFIG_PXA3xx
-#define GCR_CLKBPB (1 << 31) /* Internal clock enable */
-#endif
-#define GCR_nDMAEN (1 << 24) /* non DMA Enable */
-#define GCR_CDONE_IE (1 << 19) /* Command Done Interrupt Enable */
-#define GCR_SDONE_IE (1 << 18) /* Status Done Interrupt Enable */
-#define GCR_SECRDY_IEN (1 << 9) /* Secondary Ready Interrupt Enable */
-#define GCR_PRIRDY_IEN (1 << 8) /* Primary Ready Interrupt Enable */
-#define GCR_SECRES_IEN (1 << 5) /* Secondary Resume Interrupt Enable */
-#define GCR_PRIRES_IEN (1 << 4) /* Primary Resume Interrupt Enable */
-#define GCR_ACLINK_OFF (1 << 3) /* AC-link Shut Off */
-#define GCR_WARM_RST (1 << 2) /* AC97 Warm Reset */
-#define GCR_COLD_RST (1 << 1) /* AC'97 Cold Reset (0 = active) */
-#define GCR_GIE (1 << 0) /* Codec GPI Interrupt Enable */
-
-#define POSR __REG(0x40500010) /* PCM Out Status Register */
-#define POSR_FIFOE (1 << 4) /* FIFO error */
-#define POSR_FSR (1 << 2) /* FIFO Service Request */
-
-#define PISR __REG(0x40500014) /* PCM In Status Register */
-#define PISR_FIFOE (1 << 4) /* FIFO error */
-#define PISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
-#define PISR_FSR (1 << 2) /* FIFO Service Request */
-
-#define MCSR __REG(0x40500018) /* Mic In Status Register */
-#define MCSR_FIFOE (1 << 4) /* FIFO error */
-#define MCSR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
-#define MCSR_FSR (1 << 2) /* FIFO Service Request */
-
-#define GSR __REG(0x4050001C) /* Global Status Register */
-#define GSR_CDONE (1 << 19) /* Command Done */
-#define GSR_SDONE (1 << 18) /* Status Done */
-#define GSR_RDCS (1 << 15) /* Read Completion Status */
-#define GSR_BIT3SLT12 (1 << 14) /* Bit 3 of slot 12 */
-#define GSR_BIT2SLT12 (1 << 13) /* Bit 2 of slot 12 */
-#define GSR_BIT1SLT12 (1 << 12) /* Bit 1 of slot 12 */
-#define GSR_SECRES (1 << 11) /* Secondary Resume Interrupt */
-#define GSR_PRIRES (1 << 10) /* Primary Resume Interrupt */
-#define GSR_SCR (1 << 9) /* Secondary Codec Ready */
-#define GSR_PCR (1 << 8) /* Primary Codec Ready */
-#define GSR_MCINT (1 << 7) /* Mic In Interrupt */
-#define GSR_POINT (1 << 6) /* PCM Out Interrupt */
-#define GSR_PIINT (1 << 5) /* PCM In Interrupt */
-#define GSR_ACOFFD (1 << 3) /* AC-link Shut Off Done */
-#define GSR_MOINT (1 << 2) /* Modem Out Interrupt */
-#define GSR_MIINT (1 << 1) /* Modem In Interrupt */
-#define GSR_GSCI (1 << 0) /* Codec GPI Status Change Interrupt */
-
-#define CAR __REG(0x40500020) /* CODEC Access Register */
-#define CAR_CAIP (1 << 0) /* Codec Access In Progress */
-
-#define PCDR __REG(0x40500040) /* PCM FIFO Data Register */
-#define MCDR __REG(0x40500060) /* Mic-in FIFO Data Register */
-
-#define MOCR __REG(0x40500100) /* Modem Out Control Register */
-#define MOCR_FEIE (1 << 3) /* FIFO Error */
-#define MOCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
-
-#define MICR __REG(0x40500108) /* Modem In Control Register */
-#define MICR_FEIE (1 << 3) /* FIFO Error */
-#define MICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
-
-#define MOSR __REG(0x40500110) /* Modem Out Status Register */
-#define MOSR_FIFOE (1 << 4) /* FIFO error */
-#define MOSR_FSR (1 << 2) /* FIFO Service Request */
-
-#define MISR __REG(0x40500118) /* Modem In Status Register */
-#define MISR_FIFOE (1 << 4) /* FIFO error */
-#define MISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
-#define MISR_FSR (1 << 2) /* FIFO Service Request */
-
-#define MODR __REG(0x40500140) /* Modem FIFO Data Register */
-
-#define PAC_REG_BASE __REG(0x40500200) /* Primary Audio Codec */
-#define SAC_REG_BASE __REG(0x40500300) /* Secondary Audio Codec */
-#define PMC_REG_BASE __REG(0x40500400) /* Primary Modem Codec */
-#define SMC_REG_BASE __REG(0x40500500) /* Secondary Modem Codec */
-
-
-/*
- * Fast Infrared Communication Port
- */
-
-#define FICP __REG(0x40800000) /* Start of FICP area */
-#define ICCR0 __REG(0x40800000) /* ICP Control Register 0 */
-#define ICCR1 __REG(0x40800004) /* ICP Control Register 1 */
-#define ICCR2 __REG(0x40800008) /* ICP Control Register 2 */
-#define ICDR __REG(0x4080000c) /* ICP Data Register */
-#define ICSR0 __REG(0x40800014) /* ICP Status Register 0 */
-#define ICSR1 __REG(0x40800018) /* ICP Status Register 1 */
-
-#define ICCR0_AME (1 << 7) /* Address match enable */
-#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */
-#define ICCR0_RIE (1 << 5) /* Recieve FIFO interrupt enable */
-#define ICCR0_RXE (1 << 4) /* Receive enable */
-#define ICCR0_TXE (1 << 3) /* Transmit enable */
-#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */
-#define ICCR0_LBM (1 << 1) /* Loopback mode */
-#define ICCR0_ITR (1 << 0) /* IrDA transmission */
-
-#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */
-#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */
-#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */
-#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */
-#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */
-#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */
-
-#ifdef CONFIG_PXA27x
-#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */
-#endif
-#define ICSR0_FRE (1 << 5) /* Framing error */
-#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */
-#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */
-#define ICSR0_RAB (1 << 2) /* Receiver abort */
-#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */
-#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */
-
-#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */
-#define ICSR1_CRE (1 << 5) /* CRC error */
-#define ICSR1_EOF (1 << 4) /* End of frame */
-#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */
-#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */
-#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */
-#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */
-
-
/*
* Real Time Clock
*/
@@ -463,19 +172,6 @@
/*
- * Pulse Width Modulator
- */
-
-#define PWM_CTRL0 __REG(0x40B00000) /* PWM 0 Control Register */
-#define PWM_PWDUTY0 __REG(0x40B00004) /* PWM 0 Duty Cycle Register */
-#define PWM_PERVAL0 __REG(0x40B00008) /* PWM 0 Period Control Register */
-
-#define PWM_CTRL1 __REG(0x40C00000) /* PWM 1Control Register */
-#define PWM_PWDUTY1 __REG(0x40C00004) /* PWM 1 Duty Cycle Register */
-#define PWM_PERVAL1 __REG(0x40C00008) /* PWM 1 Period Control Register */
-
-
-/*
* Interrupt Controller
*/
@@ -496,19 +192,6 @@
* General Purpose I/O
*/
-#define GPIO0_BASE ((void __iomem *)io_p2v(0x40E00000))
-#define GPIO1_BASE ((void __iomem *)io_p2v(0x40E00004))
-#define GPIO2_BASE ((void __iomem *)io_p2v(0x40E00008))
-#define GPIO3_BASE ((void __iomem *)io_p2v(0x40E00100))
-
-#define GPLR_OFFSET 0x00
-#define GPDR_OFFSET 0x0C
-#define GPSR_OFFSET 0x18
-#define GPCR_OFFSET 0x24
-#define GRER_OFFSET 0x30
-#define GFER_OFFSET 0x3C
-#define GEDR_OFFSET 0x48
-
#define GPLR0 __REG(0x40E00000) /* GPIO Pin-Level Register GPIO<31:0> */
#define GPLR1 __REG(0x40E00004) /* GPIO Pin-Level Register GPIO<63:32> */
#define GPLR2 __REG(0x40E00008) /* GPIO Pin-Level Register GPIO<80:64> */
@@ -558,10 +241,6 @@
#define GPIO_bit(x) (1 << ((x) & 0x1f))
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
-
-/* Interrupt Controller */
-
#define _GPLR(x) __REG2(0x40E00000, ((x) & 0x60) >> 3)
#define _GPDR(x) __REG2(0x40E0000C, ((x) & 0x60) >> 3)
#define _GPSR(x) __REG2(0x40E00018, ((x) & 0x60) >> 3)
@@ -580,189 +259,5 @@
#define GEDR(x) (*((((x) & 0x7f) < 96) ? &_GEDR(x) : &GEDR3))
#define GAFR(x) (*((((x) & 0x7f) < 96) ? &_GAFR(x) : \
((((x) & 0x7f) < 112) ? &GAFR3_L : &GAFR3_U)))
-#else
-
-#define GPLR(x) __REG2(0x40E00000, ((x) & 0x60) >> 3)
-#define GPDR(x) __REG2(0x40E0000C, ((x) & 0x60) >> 3)
-#define GPSR(x) __REG2(0x40E00018, ((x) & 0x60) >> 3)
-#define GPCR(x) __REG2(0x40E00024, ((x) & 0x60) >> 3)
-#define GRER(x) __REG2(0x40E00030, ((x) & 0x60) >> 3)
-#define GFER(x) __REG2(0x40E0003C, ((x) & 0x60) >> 3)
-#define GEDR(x) __REG2(0x40E00048, ((x) & 0x60) >> 3)
-#define GAFR(x) __REG2(0x40E00054, ((x) & 0x70) >> 2)
-
-#endif
-
-/*
- * Power Manager - see pxa2xx-regs.h
- */
-
-/*
- * SSP Serial Port Registers - see arch/arm/mach-pxa/include/mach/regs-ssp.h
- */
-
-/*
- * MultiMediaCard (MMC) controller - see drivers/mmc/host/pxamci.h
- */
-
-/*
- * Core Clock - see arch/arm/mach-pxa/include/mach/pxa2xx-regs.h
- */
-
-#ifdef CONFIG_PXA27x
-
-/* Camera Interface */
-#define CICR0 __REG(0x50000000)
-#define CICR1 __REG(0x50000004)
-#define CICR2 __REG(0x50000008)
-#define CICR3 __REG(0x5000000C)
-#define CICR4 __REG(0x50000010)
-#define CISR __REG(0x50000014)
-#define CIFR __REG(0x50000018)
-#define CITOR __REG(0x5000001C)
-#define CIBR0 __REG(0x50000028)
-#define CIBR1 __REG(0x50000030)
-#define CIBR2 __REG(0x50000038)
-
-#define CICR0_DMAEN (1 << 31) /* DMA request enable */
-#define CICR0_PAR_EN (1 << 30) /* Parity enable */
-#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
-#define CICR0_ENB (1 << 28) /* Camera interface enable */
-#define CICR0_DIS (1 << 27) /* Camera interface disable */
-#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
-#define CICR0_TOM (1 << 9) /* Time-out mask */
-#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
-#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
-#define CICR0_EOLM (1 << 6) /* End-of-line mask */
-#define CICR0_PERRM (1 << 5) /* Parity-error mask */
-#define CICR0_QDM (1 << 4) /* Quick-disable mask */
-#define CICR0_CDM (1 << 3) /* Disable-done mask */
-#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
-#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
-#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
-
-#define CICR1_TBIT (1 << 31) /* Transparency bit */
-#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
-#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
-#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
-#define CICR1_RGB_F (1 << 11) /* RGB format */
-#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
-#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
-#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
-#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
-#define CICR1_DW (0x7 << 0) /* Data width mask */
-
-#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
- wait count mask */
-#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
- wait count mask */
-#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
-#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
- wait count mask */
-#define CICR2_FSW (0x7 << 0) /* Frame stabilization
- wait count mask */
-
-#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
- wait count mask */
-#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
- wait count mask */
-#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
-#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
- wait count mask */
-#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
-
-#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
-#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
-#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
-#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
-#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
-#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
-#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
-#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
-
-#define CISR_FTO (1 << 15) /* FIFO time-out */
-#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
-#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
-#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
-#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
-#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
-#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
-#define CISR_EOL (1 << 8) /* End of line */
-#define CISR_PAR_ERR (1 << 7) /* Parity error */
-#define CISR_CQD (1 << 6) /* Camera interface quick disable */
-#define CISR_CDD (1 << 5) /* Camera interface disable done */
-#define CISR_SOF (1 << 4) /* Start of frame */
-#define CISR_EOF (1 << 3) /* End of frame */
-#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
-#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
-#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
-
-#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
-#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
-#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
-#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
-#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
-#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
-#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
-#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
-
-#define SRAM_SIZE 0x40000 /* 4x64K */
-
-#define SRAM_MEM_PHYS 0x5C000000
-
-#define IMPMCR __REG(0x58000000) /* IM Power Management Control Reg */
-#define IMPMSR __REG(0x58000008) /* IM Power Management Status Reg */
-
-#define IMPMCR_PC3 (0x3 << 22) /* Bank 3 Power Control */
-#define IMPMCR_PC3_RUN_MODE (0x0 << 22) /* Run mode */
-#define IMPMCR_PC3_STANDBY_MODE (0x1 << 22) /* Standby mode */
-#define IMPMCR_PC3_AUTO_MODE (0x3 << 22) /* Automatically controlled */
-
-#define IMPMCR_PC2 (0x3 << 20) /* Bank 2 Power Control */
-#define IMPMCR_PC2_RUN_MODE (0x0 << 20) /* Run mode */
-#define IMPMCR_PC2_STANDBY_MODE (0x1 << 20) /* Standby mode */
-#define IMPMCR_PC2_AUTO_MODE (0x3 << 20) /* Automatically controlled */
-
-#define IMPMCR_PC1 (0x3 << 18) /* Bank 1 Power Control */
-#define IMPMCR_PC1_RUN_MODE (0x0 << 18) /* Run mode */
-#define IMPMCR_PC1_STANDBY_MODE (0x1 << 18) /* Standby mode */
-#define IMPMCR_PC1_AUTO_MODE (0x3 << 18) /* Automatically controlled */
-
-#define IMPMCR_PC0 (0x3 << 16) /* Bank 0 Power Control */
-#define IMPMCR_PC0_RUN_MODE (0x0 << 16) /* Run mode */
-#define IMPMCR_PC0_STANDBY_MODE (0x1 << 16) /* Standby mode */
-#define IMPMCR_PC0_AUTO_MODE (0x3 << 16) /* Automatically controlled */
-
-#define IMPMCR_AW3 (1 << 11) /* Bank 3 Automatic Wake-up enable */
-#define IMPMCR_AW2 (1 << 10) /* Bank 2 Automatic Wake-up enable */
-#define IMPMCR_AW1 (1 << 9) /* Bank 1 Automatic Wake-up enable */
-#define IMPMCR_AW0 (1 << 8) /* Bank 0 Automatic Wake-up enable */
-
-#define IMPMCR_DST (0xFF << 0) /* Delay Standby Time, ms */
-
-#define IMPMSR_PS3 (0x3 << 6) /* Bank 3 Power Status: */
-#define IMPMSR_PS3_RUN_MODE (0x0 << 6) /* Run mode */
-#define IMPMSR_PS3_STANDBY_MODE (0x1 << 6) /* Standby mode */
-
-#define IMPMSR_PS2 (0x3 << 4) /* Bank 2 Power Status: */
-#define IMPMSR_PS2_RUN_MODE (0x0 << 4) /* Run mode */
-#define IMPMSR_PS2_STANDBY_MODE (0x1 << 4) /* Standby mode */
-
-#define IMPMSR_PS1 (0x3 << 2) /* Bank 1 Power Status: */
-#define IMPMSR_PS1_RUN_MODE (0x0 << 2) /* Run mode */
-#define IMPMSR_PS1_STANDBY_MODE (0x1 << 2) /* Standby mode */
-
-#define IMPMSR_PS0 (0x3 << 0) /* Bank 0 Power Status: */
-#define IMPMSR_PS0_RUN_MODE (0x0 << 0) /* Run mode */
-#define IMPMSR_PS0_STANDBY_MODE (0x1 << 0) /* Standby mode */
-
-#endif
-
-/* PWRMODE register M field values */
-
-#define PWRMODE_IDLE 0x1
-#define PWRMODE_STANDBY 0x2
-#define PWRMODE_SLEEP 0x3
-#define PWRMODE_DEEPSLEEP 0x7
#endif
diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx-gpio.h b/arch/arm/mach-pxa/include/mach/pxa2xx-gpio.h
index 6ef1dd09970b..d83393e25273 100644
--- a/arch/arm/mach-pxa/include/mach/pxa2xx-gpio.h
+++ b/arch/arm/mach-pxa/include/mach/pxa2xx-gpio.h
@@ -365,4 +365,9 @@
#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_IN)
#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN)
+/*
+ * Handy routine to set GPIO alternate functions
+ */
+extern int pxa_gpio_mode( int gpio_mode );
+
#endif /* __ASM_ARCH_PXA2XX_GPIO_H */
diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h
index 806ecfea44bf..77102d695cc7 100644
--- a/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h
+++ b/arch/arm/mach-pxa/include/mach/pxa2xx-regs.h
@@ -49,6 +49,11 @@
#define MECR_NOS (1 << 0) /* Number Of Sockets: 0 -> 1 sock, 1 -> 2 sock */
#define MECR_CIT (1 << 1) /* Card Is There: 0 -> no card, 1 -> card inserted */
+#define MDCNFG_DE0 (1 << 0) /* SDRAM Bank 0 Enable */
+#define MDCNFG_DE1 (1 << 1) /* SDRAM Bank 1 Enable */
+#define MDCNFG_DE2 (1 << 16) /* SDRAM Bank 2 Enable */
+#define MDCNFG_DE3 (1 << 17) /* SDRAM Bank 3 Enable */
+
#define MDREFR_K0DB4 (1 << 29) /* SDCLK0 Divide by 4 Control/Status */
#define MDREFR_K2FREE (1 << 25) /* SDRAM Free-Running Control */
#define MDREFR_K1FREE (1 << 24) /* SDRAM Free-Running Control */
@@ -243,4 +248,11 @@
#define OSCC_OON (1 << 1) /* 32.768kHz OON (write-once only bit) */
#define OSCC_OOK (1 << 0) /* 32.768kHz OOK (read-only bit) */
+/* PWRMODE register M field values */
+
+#define PWRMODE_IDLE 0x1
+#define PWRMODE_STANDBY 0x2
+#define PWRMODE_SLEEP 0x3
+#define PWRMODE_DEEPSLEEP 0x7
+
#endif
diff --git a/arch/arm/mach-pxa/include/mach/pxafb.h b/arch/arm/mach-pxa/include/mach/pxafb.h
index cbda4d35c421..6932720ba04e 100644
--- a/arch/arm/mach-pxa/include/mach/pxafb.h
+++ b/arch/arm/mach-pxa/include/mach/pxafb.h
@@ -48,6 +48,7 @@
#define LCD_MONO_DSTN_8BPP ((8 << 4) | LCD_TYPE_MONO_DSTN)
#define LCD_COLOR_STN_8BPP ((8 << 4) | LCD_TYPE_COLOR_STN)
#define LCD_COLOR_DSTN_16BPP ((16 << 4) | LCD_TYPE_COLOR_DSTN)
+#define LCD_COLOR_TFT_8BPP ((8 << 4) | LCD_TYPE_COLOR_TFT)
#define LCD_COLOR_TFT_16BPP ((16 << 4) | LCD_TYPE_COLOR_TFT)
#define LCD_COLOR_TFT_18BPP ((18 << 4) | LCD_TYPE_COLOR_TFT)
#define LCD_SMART_PANEL_8BPP ((8 << 4) | LCD_TYPE_SMART_PANEL)
@@ -94,6 +95,10 @@ struct pxafb_mode_info {
* in pxa27x and pxa3xx, initialize them to the same value or
* the larger one will be used
* 3. same to {rd,wr}_pulse_width
+ *
+ * 4. LCD_PCLK_EDGE_{RISE,FALL} controls the L_PCLK_WR polarity
+ * 5. sync & FB_SYNC_HOR_HIGH_ACT controls the L_LCLK_A0
+ * 6. sync & FB_SYNC_VERT_HIGH_ACT controls the L_LCLK_RD
*/
unsigned a0csrd_set_hld; /* A0 and CS Setup/Hold Time before/after L_FCLK_RD */
unsigned a0cswr_set_hld; /* A0 and CS Setup/Hold Time before/after L_PCLK_WR */
@@ -108,6 +113,7 @@ struct pxafb_mach_info {
unsigned int num_modes;
unsigned int lcd_conn;
+ unsigned long video_mem_size;
u_int fixed_modes:1,
cmap_inverse:1,
diff --git a/arch/arm/mach-pxa/include/mach/regs-ac97.h b/arch/arm/mach-pxa/include/mach/regs-ac97.h
new file mode 100644
index 000000000000..e41b9d202b8c
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/regs-ac97.h
@@ -0,0 +1,99 @@
+#ifndef __ASM_ARCH_REGS_AC97_H
+#define __ASM_ARCH_REGS_AC97_H
+
+/*
+ * AC97 Controller registers
+ */
+
+#define POCR __REG(0x40500000) /* PCM Out Control Register */
+#define POCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define POCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define PICR __REG(0x40500004) /* PCM In Control Register */
+#define PICR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define PICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MCCR __REG(0x40500008) /* Mic In Control Register */
+#define MCCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define MCCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define GCR __REG(0x4050000C) /* Global Control Register */
+#ifdef CONFIG_PXA3xx
+#define GCR_CLKBPB (1 << 31) /* Internal clock enable */
+#endif
+#define GCR_nDMAEN (1 << 24) /* non DMA Enable */
+#define GCR_CDONE_IE (1 << 19) /* Command Done Interrupt Enable */
+#define GCR_SDONE_IE (1 << 18) /* Status Done Interrupt Enable */
+#define GCR_SECRDY_IEN (1 << 9) /* Secondary Ready Interrupt Enable */
+#define GCR_PRIRDY_IEN (1 << 8) /* Primary Ready Interrupt Enable */
+#define GCR_SECRES_IEN (1 << 5) /* Secondary Resume Interrupt Enable */
+#define GCR_PRIRES_IEN (1 << 4) /* Primary Resume Interrupt Enable */
+#define GCR_ACLINK_OFF (1 << 3) /* AC-link Shut Off */
+#define GCR_WARM_RST (1 << 2) /* AC97 Warm Reset */
+#define GCR_COLD_RST (1 << 1) /* AC'97 Cold Reset (0 = active) */
+#define GCR_GIE (1 << 0) /* Codec GPI Interrupt Enable */
+
+#define POSR __REG(0x40500010) /* PCM Out Status Register */
+#define POSR_FIFOE (1 << 4) /* FIFO error */
+#define POSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define PISR __REG(0x40500014) /* PCM In Status Register */
+#define PISR_FIFOE (1 << 4) /* FIFO error */
+#define PISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define PISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MCSR __REG(0x40500018) /* Mic In Status Register */
+#define MCSR_FIFOE (1 << 4) /* FIFO error */
+#define MCSR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MCSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define GSR __REG(0x4050001C) /* Global Status Register */
+#define GSR_CDONE (1 << 19) /* Command Done */
+#define GSR_SDONE (1 << 18) /* Status Done */
+#define GSR_RDCS (1 << 15) /* Read Completion Status */
+#define GSR_BIT3SLT12 (1 << 14) /* Bit 3 of slot 12 */
+#define GSR_BIT2SLT12 (1 << 13) /* Bit 2 of slot 12 */
+#define GSR_BIT1SLT12 (1 << 12) /* Bit 1 of slot 12 */
+#define GSR_SECRES (1 << 11) /* Secondary Resume Interrupt */
+#define GSR_PRIRES (1 << 10) /* Primary Resume Interrupt */
+#define GSR_SCR (1 << 9) /* Secondary Codec Ready */
+#define GSR_PCR (1 << 8) /* Primary Codec Ready */
+#define GSR_MCINT (1 << 7) /* Mic In Interrupt */
+#define GSR_POINT (1 << 6) /* PCM Out Interrupt */
+#define GSR_PIINT (1 << 5) /* PCM In Interrupt */
+#define GSR_ACOFFD (1 << 3) /* AC-link Shut Off Done */
+#define GSR_MOINT (1 << 2) /* Modem Out Interrupt */
+#define GSR_MIINT (1 << 1) /* Modem In Interrupt */
+#define GSR_GSCI (1 << 0) /* Codec GPI Status Change Interrupt */
+
+#define CAR __REG(0x40500020) /* CODEC Access Register */
+#define CAR_CAIP (1 << 0) /* Codec Access In Progress */
+
+#define PCDR __REG(0x40500040) /* PCM FIFO Data Register */
+#define MCDR __REG(0x40500060) /* Mic-in FIFO Data Register */
+
+#define MOCR __REG(0x40500100) /* Modem Out Control Register */
+#define MOCR_FEIE (1 << 3) /* FIFO Error */
+#define MOCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MICR __REG(0x40500108) /* Modem In Control Register */
+#define MICR_FEIE (1 << 3) /* FIFO Error */
+#define MICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MOSR __REG(0x40500110) /* Modem Out Status Register */
+#define MOSR_FIFOE (1 << 4) /* FIFO error */
+#define MOSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MISR __REG(0x40500118) /* Modem In Status Register */
+#define MISR_FIFOE (1 << 4) /* FIFO error */
+#define MISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MODR __REG(0x40500140) /* Modem FIFO Data Register */
+
+#define PAC_REG_BASE __REG(0x40500200) /* Primary Audio Codec */
+#define SAC_REG_BASE __REG(0x40500300) /* Secondary Audio Codec */
+#define PMC_REG_BASE __REG(0x40500400) /* Primary Modem Codec */
+#define SMC_REG_BASE __REG(0x40500500) /* Secondary Modem Codec */
+
+#endif /* __ASM_ARCH_REGS_AC97_H */
diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h
index c689c4ea769c..f82dcea792d9 100644
--- a/arch/arm/mach-pxa/include/mach/regs-lcd.h
+++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h
@@ -12,27 +12,29 @@
#define LCCR3 (0x00C) /* LCD Controller Control Register 3 */
#define LCCR4 (0x010) /* LCD Controller Control Register 4 */
#define LCCR5 (0x014) /* LCD Controller Control Register 5 */
-#define DFBR0 (0x020) /* DMA Channel 0 Frame Branch Register */
-#define DFBR1 (0x024) /* DMA Channel 1 Frame Branch Register */
-#define LCSR (0x038) /* LCD Controller Status Register */
+#define LCSR (0x038) /* LCD Controller Status Register 0 */
+#define LCSR1 (0x034) /* LCD Controller Status Register 1 */
#define LIIDR (0x03C) /* LCD Controller Interrupt ID Register */
#define TMEDRGBR (0x040) /* TMED RGB Seed Register */
#define TMEDCR (0x044) /* TMED Control Register */
+#define FBR0 (0x020) /* DMA Channel 0 Frame Branch Register */
+#define FBR1 (0x024) /* DMA Channel 1 Frame Branch Register */
+#define FBR2 (0x028) /* DMA Channel 2 Frame Branch Register */
+#define FBR3 (0x02C) /* DMA Channel 2 Frame Branch Register */
+#define FBR4 (0x030) /* DMA Channel 2 Frame Branch Register */
+#define FBR5 (0x110) /* DMA Channel 2 Frame Branch Register */
+#define FBR6 (0x114) /* DMA Channel 2 Frame Branch Register */
+
+#define OVL1C1 (0x050) /* Overlay 1 Control Register 1 */
+#define OVL1C2 (0x060) /* Overlay 1 Control Register 2 */
+#define OVL2C1 (0x070) /* Overlay 2 Control Register 1 */
+#define OVL2C2 (0x080) /* Overlay 2 Control Register 2 */
+
#define CMDCR (0x100) /* Command Control Register */
#define PRSR (0x104) /* Panel Read Status Register */
-#define LCCR3_1BPP (0 << 24)
-#define LCCR3_2BPP (1 << 24)
-#define LCCR3_4BPP (2 << 24)
-#define LCCR3_8BPP (3 << 24)
-#define LCCR3_16BPP (4 << 24)
-#define LCCR3_18BPP (5 << 24)
-#define LCCR3_18BPP_P (6 << 24)
-#define LCCR3_19BPP (7 << 24)
-#define LCCR3_19BPP_P (1 << 29)
-#define LCCR3_24BPP ((1 << 29) | (1 << 24))
-#define LCCR3_25BPP ((1 << 29) | (2 << 24))
+#define LCCR3_BPP(x) ((((x) & 0x7) << 24) | (((x) & 0x8) ? (1 << 29) : 0))
#define LCCR3_PDFOR_0 (0 << 30)
#define LCCR3_PDFOR_1 (1 << 30)
@@ -42,19 +44,16 @@
#define LCCR4_PAL_FOR_0 (0 << 15)
#define LCCR4_PAL_FOR_1 (1 << 15)
#define LCCR4_PAL_FOR_2 (2 << 15)
+#define LCCR4_PAL_FOR_3 (3 << 15)
#define LCCR4_PAL_FOR_MASK (3 << 15)
#define FDADR0 (0x200) /* DMA Channel 0 Frame Descriptor Address Register */
-#define FSADR0 (0x204) /* DMA Channel 0 Frame Source Address Register */
-#define FIDR0 (0x208) /* DMA Channel 0 Frame ID Register */
-#define LDCMD0 (0x20C) /* DMA Channel 0 Command Register */
#define FDADR1 (0x210) /* DMA Channel 1 Frame Descriptor Address Register */
-#define FSADR1 (0x214) /* DMA Channel 1 Frame Source Address Register */
-#define FIDR1 (0x218) /* DMA Channel 1 Frame ID Register */
-#define LDCMD1 (0x21C) /* DMA Channel 1 Command Register */
+#define FDADR2 (0x220) /* DMA Channel 2 Frame Descriptor Address Register */
+#define FDADR3 (0x230) /* DMA Channel 3 Frame Descriptor Address Register */
+#define FDADR4 (0x240) /* DMA Channel 4 Frame Descriptor Address Register */
+#define FDADR5 (0x250) /* DMA Channel 5 Frame Descriptor Address Register */
#define FDADR6 (0x260) /* DMA Channel 6 Frame Descriptor Address Register */
-#define FSADR6 (0x264) /* DMA Channel 6 Frame Source Address Register */
-#define FIDR6 (0x268) /* DMA Channel 6 Frame ID Register */
#define LCCR0_ENB (1 << 0) /* LCD Controller enable */
#define LCCR0_CMS (1 << 1) /* Color/Monochrome Display Select */
@@ -126,9 +125,6 @@
#define LCCR3_PCD Fld (8, 0) /* Pixel Clock Divisor */
#define LCCR3_PixClkDiv(Div) (((Div) << FShft (LCCR3_PCD)))
-#define LCCR3_BPP Fld (3, 24) /* Bit Per Pixel */
-#define LCCR3_Bpp(Bpp) (((Bpp) << FShft (LCCR3_BPP)))
-
#define LCCR3_ACB Fld (8, 8) /* AC Bias */
#define LCCR3_Acb(Acb) (((Acb) << FShft (LCCR3_ACB)))
@@ -157,8 +153,22 @@
#define LCSR_RD_ST (1 << 11) /* read status */
#define LCSR_CMD_INT (1 << 12) /* command interrupt */
+#define LCSR1_IU(x) (1 << ((x) + 23)) /* Input FIFO underrun */
+#define LCSR1_BS(x) (1 << ((x) + 15)) /* Branch Status */
+#define LCSR1_EOF(x) (1 << ((x) + 7)) /* End of Frame Status */
+#define LCSR1_SOF(x) (1 << ((x) - 1)) /* Start of Frame Status */
+
#define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */
+/* overlay control registers */
+#define OVLxC1_PPL(x) ((((x) - 1) & 0x3ff) << 0) /* Pixels Per Line */
+#define OVLxC1_LPO(x) ((((x) - 1) & 0x3ff) << 10) /* Number of Lines */
+#define OVLxC1_BPP(x) (((x) & 0xf) << 20) /* Bits Per Pixel */
+#define OVLxC1_OEN (1 << 31) /* Enable bit for Overlay */
+#define OVLxC2_XPOS(x) (((x) & 0x3ff) << 0) /* Horizontal Position */
+#define OVLxC2_YPOS(x) (((x) & 0x3ff) << 10) /* Vertical Position */
+#define OVL2C2_PFOR(x) (((x) & 0x7) << 20) /* Pixel Format */
+
/* smartpanel related */
#define PRSR_DATA(x) ((x) & 0xff) /* Panel Data */
#define PRSR_A0 (1 << 8) /* Read Data Source */
@@ -177,4 +187,11 @@
#define SMART_CMD(x) (SMART_CMD_WRITE_COMMAND | ((x) & 0xff))
#define SMART_DAT(x) (SMART_CMD_WRITE_DATA | ((x) & 0xff))
+
+/* SMART_DELAY() is introduced for software controlled delay primitive which
+ * can be inserted between command sequences, unused command 0x6 is used here
+ * and delay ranges from 0ms ~ 255ms
+ */
+#define SMART_CMD_DELAY (0x6 << 9)
+#define SMART_DELAY(ms) (SMART_CMD_DELAY | ((ms) & 0xff))
#endif /* __ASM_ARCH_REGS_LCD_H */
diff --git a/arch/arm/mach-pxa/include/mach/regs-uart.h b/arch/arm/mach-pxa/include/mach/regs-uart.h
new file mode 100644
index 000000000000..55aeb7fb72f6
--- /dev/null
+++ b/arch/arm/mach-pxa/include/mach/regs-uart.h
@@ -0,0 +1,143 @@
+#ifndef __ASM_ARCH_REGS_UART_H
+#define __ASM_ARCH_REGS_UART_H
+
+/*
+ * UARTs
+ */
+
+/* Full Function UART (FFUART) */
+#define FFUART FFRBR
+#define FFRBR __REG(0x40100000) /* Receive Buffer Register (read only) */
+#define FFTHR __REG(0x40100000) /* Transmit Holding Register (write only) */
+#define FFIER __REG(0x40100004) /* Interrupt Enable Register (read/write) */
+#define FFIIR __REG(0x40100008) /* Interrupt ID Register (read only) */
+#define FFFCR __REG(0x40100008) /* FIFO Control Register (write only) */
+#define FFLCR __REG(0x4010000C) /* Line Control Register (read/write) */
+#define FFMCR __REG(0x40100010) /* Modem Control Register (read/write) */
+#define FFLSR __REG(0x40100014) /* Line Status Register (read only) */
+#define FFMSR __REG(0x40100018) /* Modem Status Register (read only) */
+#define FFSPR __REG(0x4010001C) /* Scratch Pad Register (read/write) */
+#define FFISR __REG(0x40100020) /* Infrared Selection Register (read/write) */
+#define FFDLL __REG(0x40100000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define FFDLH __REG(0x40100004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
+/* Bluetooth UART (BTUART) */
+#define BTUART BTRBR
+#define BTRBR __REG(0x40200000) /* Receive Buffer Register (read only) */
+#define BTTHR __REG(0x40200000) /* Transmit Holding Register (write only) */
+#define BTIER __REG(0x40200004) /* Interrupt Enable Register (read/write) */
+#define BTIIR __REG(0x40200008) /* Interrupt ID Register (read only) */
+#define BTFCR __REG(0x40200008) /* FIFO Control Register (write only) */
+#define BTLCR __REG(0x4020000C) /* Line Control Register (read/write) */
+#define BTMCR __REG(0x40200010) /* Modem Control Register (read/write) */
+#define BTLSR __REG(0x40200014) /* Line Status Register (read only) */
+#define BTMSR __REG(0x40200018) /* Modem Status Register (read only) */
+#define BTSPR __REG(0x4020001C) /* Scratch Pad Register (read/write) */
+#define BTISR __REG(0x40200020) /* Infrared Selection Register (read/write) */
+#define BTDLL __REG(0x40200000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define BTDLH __REG(0x40200004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
+/* Standard UART (STUART) */
+#define STUART STRBR
+#define STRBR __REG(0x40700000) /* Receive Buffer Register (read only) */
+#define STTHR __REG(0x40700000) /* Transmit Holding Register (write only) */
+#define STIER __REG(0x40700004) /* Interrupt Enable Register (read/write) */
+#define STIIR __REG(0x40700008) /* Interrupt ID Register (read only) */
+#define STFCR __REG(0x40700008) /* FIFO Control Register (write only) */
+#define STLCR __REG(0x4070000C) /* Line Control Register (read/write) */
+#define STMCR __REG(0x40700010) /* Modem Control Register (read/write) */
+#define STLSR __REG(0x40700014) /* Line Status Register (read only) */
+#define STMSR __REG(0x40700018) /* Reserved */
+#define STSPR __REG(0x4070001C) /* Scratch Pad Register (read/write) */
+#define STISR __REG(0x40700020) /* Infrared Selection Register (read/write) */
+#define STDLL __REG(0x40700000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define STDLH __REG(0x40700004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
+/* Hardware UART (HWUART) */
+#define HWUART HWRBR
+#define HWRBR __REG(0x41600000) /* Receive Buffer Register (read only) */
+#define HWTHR __REG(0x41600000) /* Transmit Holding Register (write only) */
+#define HWIER __REG(0x41600004) /* Interrupt Enable Register (read/write) */
+#define HWIIR __REG(0x41600008) /* Interrupt ID Register (read only) */
+#define HWFCR __REG(0x41600008) /* FIFO Control Register (write only) */
+#define HWLCR __REG(0x4160000C) /* Line Control Register (read/write) */
+#define HWMCR __REG(0x41600010) /* Modem Control Register (read/write) */
+#define HWLSR __REG(0x41600014) /* Line Status Register (read only) */
+#define HWMSR __REG(0x41600018) /* Modem Status Register (read only) */
+#define HWSPR __REG(0x4160001C) /* Scratch Pad Register (read/write) */
+#define HWISR __REG(0x41600020) /* Infrared Selection Register (read/write) */
+#define HWFOR __REG(0x41600024) /* Receive FIFO Occupancy Register (read only) */
+#define HWABR __REG(0x41600028) /* Auto-Baud Control Register (read/write) */
+#define HWACR __REG(0x4160002C) /* Auto-Baud Count Register (read only) */
+#define HWDLL __REG(0x41600000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */
+#define HWDLH __REG(0x41600004) /* Divisor Latch High Register (DLAB = 1) (read/write) */
+
+#define IER_DMAE (1 << 7) /* DMA Requests Enable */
+#define IER_UUE (1 << 6) /* UART Unit Enable */
+#define IER_NRZE (1 << 5) /* NRZ coding Enable */
+#define IER_RTIOE (1 << 4) /* Receiver Time Out Interrupt Enable */
+#define IER_MIE (1 << 3) /* Modem Interrupt Enable */
+#define IER_RLSE (1 << 2) /* Receiver Line Status Interrupt Enable */
+#define IER_TIE (1 << 1) /* Transmit Data request Interrupt Enable */
+#define IER_RAVIE (1 << 0) /* Receiver Data Available Interrupt Enable */
+
+#define IIR_FIFOES1 (1 << 7) /* FIFO Mode Enable Status */
+#define IIR_FIFOES0 (1 << 6) /* FIFO Mode Enable Status */
+#define IIR_TOD (1 << 3) /* Time Out Detected */
+#define IIR_IID2 (1 << 2) /* Interrupt Source Encoded */
+#define IIR_IID1 (1 << 1) /* Interrupt Source Encoded */
+#define IIR_IP (1 << 0) /* Interrupt Pending (active low) */
+
+#define FCR_ITL2 (1 << 7) /* Interrupt Trigger Level */
+#define FCR_ITL1 (1 << 6) /* Interrupt Trigger Level */
+#define FCR_RESETTF (1 << 2) /* Reset Transmitter FIFO */
+#define FCR_RESETRF (1 << 1) /* Reset Receiver FIFO */
+#define FCR_TRFIFOE (1 << 0) /* Transmit and Receive FIFO Enable */
+#define FCR_ITL_1 (0)
+#define FCR_ITL_8 (FCR_ITL1)
+#define FCR_ITL_16 (FCR_ITL2)
+#define FCR_ITL_32 (FCR_ITL2|FCR_ITL1)
+
+#define LCR_DLAB (1 << 7) /* Divisor Latch Access Bit */
+#define LCR_SB (1 << 6) /* Set Break */
+#define LCR_STKYP (1 << 5) /* Sticky Parity */
+#define LCR_EPS (1 << 4) /* Even Parity Select */
+#define LCR_PEN (1 << 3) /* Parity Enable */
+#define LCR_STB (1 << 2) /* Stop Bit */
+#define LCR_WLS1 (1 << 1) /* Word Length Select */
+#define LCR_WLS0 (1 << 0) /* Word Length Select */
+
+#define LSR_FIFOE (1 << 7) /* FIFO Error Status */
+#define LSR_TEMT (1 << 6) /* Transmitter Empty */
+#define LSR_TDRQ (1 << 5) /* Transmit Data Request */
+#define LSR_BI (1 << 4) /* Break Interrupt */
+#define LSR_FE (1 << 3) /* Framing Error */
+#define LSR_PE (1 << 2) /* Parity Error */
+#define LSR_OE (1 << 1) /* Overrun Error */
+#define LSR_DR (1 << 0) /* Data Ready */
+
+#define MCR_LOOP (1 << 4)
+#define MCR_OUT2 (1 << 3) /* force MSR_DCD in loopback mode */
+#define MCR_OUT1 (1 << 2) /* force MSR_RI in loopback mode */
+#define MCR_RTS (1 << 1) /* Request to Send */
+#define MCR_DTR (1 << 0) /* Data Terminal Ready */
+
+#define MSR_DCD (1 << 7) /* Data Carrier Detect */
+#define MSR_RI (1 << 6) /* Ring Indicator */
+#define MSR_DSR (1 << 5) /* Data Set Ready */
+#define MSR_CTS (1 << 4) /* Clear To Send */
+#define MSR_DDCD (1 << 3) /* Delta Data Carrier Detect */
+#define MSR_TERI (1 << 2) /* Trailing Edge Ring Indicator */
+#define MSR_DDSR (1 << 1) /* Delta Data Set Ready */
+#define MSR_DCTS (1 << 0) /* Delta Clear To Send */
+
+/*
+ * IrSR (Infrared Selection Register)
+ */
+#define STISR_RXPL (1 << 4) /* Receive Data Polarity */
+#define STISR_TXPL (1 << 3) /* Transmit Data Polarity */
+#define STISR_XMODE (1 << 2) /* Transmit Pulse Width Select */
+#define STISR_RCVEIR (1 << 1) /* Receiver SIR Enable */
+#define STISR_XMITIR (1 << 0) /* Transmitter SIR Enable */
+
+#endif /* __ASM_ARCH_REGS_UART_H */
diff --git a/arch/arm/mach-pxa/include/mach/timex.h b/arch/arm/mach-pxa/include/mach/timex.h
index b05fc6683c47..af6760a50e1a 100644
--- a/arch/arm/mach-pxa/include/mach/timex.h
+++ b/arch/arm/mach-pxa/include/mach/timex.h
@@ -10,6 +10,14 @@
* published by the Free Software Foundation.
*/
+/* Various drivers are still using the constant of CLOCK_TICK_RATE, for
+ * those drivers to at least work, the definition is provided here.
+ *
+ * NOTE: this is no longer accurate when multiple processors and boards
+ * are selected, newer drivers should not depend on this any more. Use
+ * either the clocksource/clockevent or get this at run-time by calling
+ * get_clock_tick_rate() (as defined in generic.c).
+ */
#if defined(CONFIG_PXA25x)
/* PXA250/210 timer base */
diff --git a/arch/arm/mach-pxa/include/mach/uncompress.h b/arch/arm/mach-pxa/include/mach/uncompress.h
index 21e3e890af98..f4b029c03957 100644
--- a/arch/arm/mach-pxa/include/mach/uncompress.h
+++ b/arch/arm/mach-pxa/include/mach/uncompress.h
@@ -10,7 +10,7 @@
*/
#include <linux/serial_reg.h>
-#include <mach/pxa-regs.h>
+#include <mach/regs-uart.h>
#include <asm/mach-types.h>
#define __REG(x) ((volatile unsigned long *)x)
@@ -35,7 +35,7 @@ static inline void flush(void)
static inline void arch_decomp_setup(void)
{
- if (machine_is_littleton())
+ if (machine_is_littleton() || machine_is_intelmote2())
UART = STUART;
}
diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c
index b4d00aba0e31..31da7f3c06f6 100644
--- a/arch/arm/mach-pxa/littleton.c
+++ b/arch/arm/mach-pxa/littleton.c
@@ -20,8 +20,13 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/smc91x.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mfd/da903x.h>
+#include <linux/i2c/max732x.h>
#include <asm/types.h>
#include <asm/setup.h>
@@ -36,10 +41,10 @@
#include <mach/pxa-regs.h>
#include <mach/mfp-pxa300.h>
-#include <mach/gpio.h>
#include <mach/pxafb.h>
#include <mach/ssp.h>
#include <mach/pxa2xx_spi.h>
+#include <mach/i2c.h>
#include <mach/pxa27x_keypad.h>
#include <mach/pxa3xx_nand.h>
#include <mach/littleton.h>
@@ -314,6 +319,73 @@ static void __init littleton_init_nand(void)
static inline void littleton_init_nand(void) {}
#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
+static struct led_info littleton_da9034_leds[] = {
+ [0] = {
+ .name = "littleton:keypad1",
+ .flags = DA9034_LED_RAMP,
+ },
+ [1] = {
+ .name = "littleton:keypad2",
+ .flags = DA9034_LED_RAMP,
+ },
+ [2] = {
+ .name = "littleton:vibra",
+ .flags = 0,
+ },
+};
+
+static struct da903x_subdev_info littleton_da9034_subdevs[] = {
+ {
+ .name = "da903x-led",
+ .id = DA9034_ID_LED_1,
+ .platform_data = &littleton_da9034_leds[0],
+ }, {
+ .name = "da903x-led",
+ .id = DA9034_ID_LED_2,
+ .platform_data = &littleton_da9034_leds[1],
+ }, {
+ .name = "da903x-led",
+ .id = DA9034_ID_VIBRA,
+ .platform_data = &littleton_da9034_leds[2],
+ }, {
+ .name = "da903x-backlight",
+ .id = DA9034_ID_WLED,
+ },
+};
+
+static struct da903x_platform_data littleton_da9034_info = {
+ .num_subdevs = ARRAY_SIZE(littleton_da9034_subdevs),
+ .subdevs = littleton_da9034_subdevs,
+};
+
+static struct max732x_platform_data littleton_max7320_info = {
+ .gpio_base = EXT0_GPIO_BASE,
+};
+
+static struct i2c_board_info littleton_i2c_info[] = {
+ [0] = {
+ .type = "da9034",
+ .addr = 0x34,
+ .platform_data = &littleton_da9034_info,
+ .irq = gpio_to_irq(mfp_to_gpio(MFP_PIN_GPIO18)),
+ },
+ [1] = {
+ .type = "max7320",
+ .addr = 0x50,
+ .platform_data = &littleton_max7320_info,
+ },
+};
+
+static void __init littleton_init_i2c(void)
+{
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(littleton_i2c_info));
+}
+#else
+static inline void littleton_init_i2c(void) {}
+#endif /* CONFIG_I2C_PXA || CONFIG_I2C_PXA_MODULE */
+
static void __init littleton_init(void)
{
/* initialize MFP configurations */
@@ -326,6 +398,7 @@ static void __init littleton_init(void)
platform_device_register(&smc91x_device);
littleton_init_spi();
+ littleton_init_i2c();
littleton_init_lcd();
littleton_init_keypad();
littleton_init_nand();
diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c
index 519138bc5f85..21b821e1a60d 100644
--- a/arch/arm/mach-pxa/magician.c
+++ b/arch/arm/mach-pxa/magician.c
@@ -123,6 +123,10 @@ static unsigned long magician_pin_config[] __initdata = {
GPIO107_GPIO, /* DS1WM_IRQ */
GPIO108_GPIO, /* GSM_READY */
GPIO115_GPIO, /* nPEN_IRQ */
+
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
};
/*
@@ -332,8 +336,7 @@ static struct pxafb_mach_info toppoly_info = {
.modes = toppoly_modes,
.num_modes = 1,
.fixed_modes = 1,
- .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_PixRsEdg,
+ .lcd_conn = LCD_COLOR_TFT_16BPP,
.pxafb_lcd_power = toppoly_lcd_power,
};
@@ -341,8 +344,8 @@ static struct pxafb_mach_info samsung_info = {
.modes = samsung_modes,
.num_modes = 1,
.fixed_modes = 1,
- .lccr0 = LCCR0_LDDALT | LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
- .lccr3 = LCCR3_PixFlEdg,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL |\
+ LCD_ALTERNATE_MAPPING,
.pxafb_lcd_power = samsung_lcd_power,
};
diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c
index f2c7ad8f2b6b..5f224968043c 100644
--- a/arch/arm/mach-pxa/mainstone.c
+++ b/arch/arm/mach-pxa/mainstone.c
@@ -128,6 +128,10 @@ static unsigned long mainstone_pin_config[] = {
GPIO108_KP_MKOUT_5,
GPIO96_KP_MKOUT_6,
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
+
/* GPIO */
GPIO1_GPIO | WAKEUP_ON_EDGE_BOTH,
};
diff --git a/arch/arm/mach-pxa/mfp-pxa2xx.c b/arch/arm/mach-pxa/mfp-pxa2xx.c
index 2061c00c8ead..33626de8cbf6 100644
--- a/arch/arm/mach-pxa/mfp-pxa2xx.c
+++ b/arch/arm/mach-pxa/mfp-pxa2xx.c
@@ -38,12 +38,13 @@ struct gpio_desc {
unsigned valid : 1;
unsigned can_wakeup : 1;
unsigned keypad_gpio : 1;
+ unsigned dir_inverted : 1;
unsigned int mask; /* bit mask in PWER or PKWR */
+ unsigned int mux_mask; /* bit mask of muxed gpio bits, 0 if no mux */
unsigned long config;
};
static struct gpio_desc gpio_desc[MFP_PIN_GPIO127 + 1];
-static int gpio_nr;
static unsigned long gpdr_lpm[4];
@@ -54,7 +55,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
int uorl = !!(gpio & 0x10); /* GAFRx_U or GAFRx_L ? */
int shft = (gpio & 0xf) << 1;
int fn = MFP_AF(c);
- int dir = c & MFP_DIR_OUT;
+ int is_out = (c & MFP_DIR_OUT) ? 1 : 0;
if (fn > 3)
return -EINVAL;
@@ -68,7 +69,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
else
GAFR_U(bank) = gafr;
- if (dir == MFP_DIR_OUT)
+ if (is_out ^ gpio_desc[gpio].dir_inverted)
GPDR(gpio) |= mask;
else
GPDR(gpio) &= ~mask;
@@ -77,11 +78,11 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
switch (c & MFP_LPM_STATE_MASK) {
case MFP_LPM_DRIVE_HIGH:
PGSR(bank) |= mask;
- dir = MFP_DIR_OUT;
+ is_out = 1;
break;
case MFP_LPM_DRIVE_LOW:
PGSR(bank) &= ~mask;
- dir = MFP_DIR_OUT;
+ is_out = 1;
break;
case MFP_LPM_DEFAULT:
break;
@@ -92,7 +93,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
break;
}
- if (dir == MFP_DIR_OUT)
+ if (is_out ^ gpio_desc[gpio].dir_inverted)
gpdr_lpm[bank] |= mask;
else
gpdr_lpm[bank] &= ~mask;
@@ -106,7 +107,7 @@ static int __mfp_config_gpio(unsigned gpio, unsigned long c)
return -EINVAL;
}
- if ((c & MFP_LPM_CAN_WAKEUP) && (dir == MFP_DIR_OUT)) {
+ if ((c & MFP_LPM_CAN_WAKEUP) && is_out) {
pr_warning("%s: output GPIO%d unable to wakeup\n",
__func__, gpio);
return -EINVAL;
@@ -169,7 +170,7 @@ void pxa2xx_mfp_set_lpm(int mfp, unsigned long lpm)
int gpio_set_wake(unsigned int gpio, unsigned int on)
{
struct gpio_desc *d;
- unsigned long c;
+ unsigned long c, mux_taken;
if (gpio > mfp_to_gpio(MFP_PIN_GPIO127))
return -EINVAL;
@@ -183,9 +184,13 @@ int gpio_set_wake(unsigned int gpio, unsigned int on)
if (d->keypad_gpio)
return -EINVAL;
+ mux_taken = (PWER & d->mux_mask) & (~d->mask);
+ if (on && mux_taken)
+ return -EBUSY;
+
if (d->can_wakeup && (c & MFP_LPM_CAN_WAKEUP)) {
if (on) {
- PWER |= d->mask;
+ PWER = (PWER & ~d->mux_mask) | d->mask;
if (c & MFP_LPM_EDGE_RISE)
PRER |= d->mask;
@@ -210,7 +215,7 @@ static void __init pxa25x_mfp_init(void)
{
int i;
- for (i = 0; i <= 84; i++)
+ for (i = 0; i <= pxa_last_gpio; i++)
gpio_desc[i].valid = 1;
for (i = 0; i <= 15; i++) {
@@ -218,7 +223,11 @@ static void __init pxa25x_mfp_init(void)
gpio_desc[i].mask = GPIO_bit(i);
}
- gpio_nr = 85;
+ /* PXA26x has additional 4 GPIOs (86/87/88/89) which has the
+ * direction bit inverted in GPDR2. See PXA26x DM 4.1.1.
+ */
+ for (i = 86; i <= pxa_last_gpio; i++)
+ gpio_desc[i].dir_inverted = 1;
}
#else
static inline void pxa25x_mfp_init(void) {}
@@ -251,11 +260,27 @@ int keypad_set_wake(unsigned int on)
return 0;
}
+#define PWER_WEMUX2_GPIO38 (1 << 16)
+#define PWER_WEMUX2_GPIO53 (2 << 16)
+#define PWER_WEMUX2_GPIO40 (3 << 16)
+#define PWER_WEMUX2_GPIO36 (4 << 16)
+#define PWER_WEMUX2_MASK (7 << 16)
+#define PWER_WEMUX3_GPIO31 (1 << 19)
+#define PWER_WEMUX3_GPIO113 (2 << 19)
+#define PWER_WEMUX3_MASK (3 << 19)
+
+#define INIT_GPIO_DESC_MUXED(mux, gpio) \
+do { \
+ gpio_desc[(gpio)].can_wakeup = 1; \
+ gpio_desc[(gpio)].mask = PWER_ ## mux ## _GPIO ##gpio; \
+ gpio_desc[(gpio)].mux_mask = PWER_ ## mux ## _MASK; \
+} while (0)
+
static void __init pxa27x_mfp_init(void)
{
int i, gpio;
- for (i = 0; i <= 120; i++) {
+ for (i = 0; i <= pxa_last_gpio; i++) {
/* skip GPIO2, 5, 6, 7, 8, they are not
* valid pins allow configuration
*/
@@ -286,7 +311,12 @@ static void __init pxa27x_mfp_init(void)
gpio_desc[35].can_wakeup = 1;
gpio_desc[35].mask = PWER_WE35;
- gpio_nr = 121;
+ INIT_GPIO_DESC_MUXED(WEMUX3, 31);
+ INIT_GPIO_DESC_MUXED(WEMUX3, 113);
+ INIT_GPIO_DESC_MUXED(WEMUX2, 38);
+ INIT_GPIO_DESC_MUXED(WEMUX2, 53);
+ INIT_GPIO_DESC_MUXED(WEMUX2, 40);
+ INIT_GPIO_DESC_MUXED(WEMUX2, 36);
}
#else
static inline void pxa27x_mfp_init(void) {}
@@ -300,7 +330,7 @@ static int pxa2xx_mfp_suspend(struct sys_device *d, pm_message_t state)
{
int i;
- for (i = 0; i <= gpio_to_bank(gpio_nr); i++) {
+ for (i = 0; i <= gpio_to_bank(pxa_last_gpio); i++) {
saved_gafr[0][i] = GAFR_L(i);
saved_gafr[1][i] = GAFR_U(i);
@@ -315,7 +345,7 @@ static int pxa2xx_mfp_resume(struct sys_device *d)
{
int i;
- for (i = 0; i <= gpio_to_bank(gpio_nr); i++) {
+ for (i = 0; i <= gpio_to_bank(pxa_last_gpio); i++) {
GAFR_L(i) = saved_gafr[0][i];
GAFR_U(i) = saved_gafr[1][i];
GPDR(i * 32) = saved_gpdr[i];
@@ -348,7 +378,7 @@ static int __init pxa2xx_mfp_init(void)
pxa27x_mfp_init();
/* initialize gafr_run[], pgsr_lpm[] from existing values */
- for (i = 0; i <= gpio_to_bank(gpio_nr); i++)
+ for (i = 0; i <= gpio_to_bank(pxa_last_gpio); i++)
gpdr_lpm[i] = GPDR(i * 32);
return sysdev_class_register(&pxa2xx_mfp_sysclass);
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c
index 782903fe9c6c..2b427e015b6f 100644
--- a/arch/arm/mach-pxa/mioa701.c
+++ b/arch/arm/mach-pxa/mioa701.c
@@ -34,7 +34,7 @@
#include <linux/irq.h>
#include <linux/pda_power.h>
#include <linux/power_supply.h>
-#include <linux/wm97xx.h>
+#include <linux/wm97xx_batt.h>
#include <linux/mtd/physmap.h>
#include <asm/mach-types.h>
@@ -46,6 +46,9 @@
#include <mach/mmc.h>
#include <mach/udc.h>
#include <mach/pxa27x-udc.h>
+#include <mach/i2c.h>
+#include <mach/camera.h>
+#include <media/soc_camera.h>
#include <mach/mioa701.h>
@@ -54,10 +57,11 @@
static unsigned long mioa701_pin_config[] = {
/* Mio global */
- MIO_CFG_OUT(GPIO9_CHARGE_nEN, AF0, DRIVE_LOW),
+ MIO_CFG_OUT(GPIO9_CHARGE_EN, AF0, DRIVE_LOW),
MIO_CFG_OUT(GPIO18_POWEROFF, AF0, DRIVE_LOW),
MFP_CFG_OUT(GPIO3, AF0, DRIVE_HIGH),
MFP_CFG_OUT(GPIO4, AF0, DRIVE_HIGH),
+ MIO_CFG_IN(GPIO80_MAYBE_CHARGE_VDROP, AF0),
/* Backlight PWM 0 */
GPIO16_PWM0_OUT,
@@ -74,7 +78,7 @@ static unsigned long mioa701_pin_config[] = {
MIO_CFG_OUT(GPIO91_SDIO_EN, AF0, DRIVE_LOW),
/* USB */
- MIO_CFG_IN(GPIO13_USB_DETECT, AF0),
+ MIO_CFG_IN(GPIO13_nUSB_DETECT, AF0),
MIO_CFG_OUT(GPIO22_USB_ENABLE, AF0, DRIVE_LOW),
/* LCD */
@@ -98,12 +102,29 @@ static unsigned long mioa701_pin_config[] = {
GPIO75_LCD_LCLK,
GPIO76_LCD_PCLK,
+ /* QCI */
+ GPIO12_CIF_DD_7,
+ GPIO17_CIF_DD_6,
+ GPIO50_CIF_DD_3,
+ GPIO51_CIF_DD_2,
+ GPIO52_CIF_DD_4,
+ GPIO53_CIF_MCLK,
+ GPIO54_CIF_PCLK,
+ GPIO55_CIF_DD_1,
+ GPIO81_CIF_DD_0,
+ GPIO82_CIF_DD_5,
+ GPIO84_CIF_FV,
+ GPIO85_CIF_LV,
+
/* Bluetooth */
+ MIO_CFG_IN(GPIO14_BT_nACTIVITY, AF0),
GPIO44_BTUART_CTS,
GPIO42_BTUART_RXD,
GPIO45_BTUART_RTS,
GPIO43_BTUART_TXD,
MIO_CFG_OUT(GPIO83_BT_ON, AF0, DRIVE_LOW),
+ MIO_CFG_OUT(GPIO77_BT_UNKNOWN1, AF0, DRIVE_HIGH),
+ MIO_CFG_OUT(GPIO86_BT_MAYBE_nRESET, AF0, DRIVE_HIGH),
/* GPS */
MIO_CFG_OUT(GPIO23_GPS_UNKNOWN1, AF0, DRIVE_LOW),
@@ -151,16 +172,16 @@ static unsigned long mioa701_pin_config[] = {
GPIO104_KP_MKOUT_1,
GPIO105_KP_MKOUT_2,
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
+
/* Unknown */
- MFP_CFG_IN(GPIO14, AF0),
MFP_CFG_IN(GPIO20, AF0),
MFP_CFG_IN(GPIO21, AF0),
MFP_CFG_IN(GPIO33, AF0),
MFP_CFG_OUT(GPIO49, AF0, DRIVE_HIGH),
MFP_CFG_OUT(GPIO57, AF0, DRIVE_HIGH),
- MFP_CFG_OUT(GPIO77, AF0, DRIVE_HIGH),
- MFP_CFG_IN(GPIO80, AF0),
- MFP_CFG_OUT(GPIO86, AF0, DRIVE_HIGH),
MFP_CFG_IN(GPIO96, AF0),
MFP_CFG_OUT(GPIO116, AF0, DRIVE_HIGH),
};
@@ -407,7 +428,7 @@ static void udc_power_command(int cmd)
static int is_usb_connected(void)
{
- return !!gpio_get_value(GPIO13_USB_DETECT);
+ return !gpio_get_value(GPIO13_nUSB_DETECT);
}
static struct pxa2xx_udc_mach_info mioa701_udc_info = {
@@ -659,13 +680,19 @@ static char *supplicants[] = {
"mioa701_battery"
};
+static int is_ac_connected(void)
+{
+ return gpio_get_value(GPIO96_AC_DETECT);
+}
+
static void mioa701_set_charge(int flags)
{
- gpio_set_value(GPIO9_CHARGE_nEN, !flags);
+ gpio_set_value(GPIO9_CHARGE_EN, (flags == PDA_POWER_CHARGE_USB));
}
static struct pda_power_pdata power_pdata = {
- .is_ac_online = is_usb_connected,
+ .is_ac_online = is_ac_connected,
+ .is_usb_online = is_usb_connected,
.set_charge = mioa701_set_charge,
.supplied_to = supplicants,
.num_supplicants = ARRAY_SIZE(supplicants),
@@ -674,8 +701,15 @@ static struct pda_power_pdata power_pdata = {
static struct resource power_resources[] = {
[0] = {
.name = "ac",
- .start = gpio_to_irq(GPIO13_USB_DETECT),
- .end = gpio_to_irq(GPIO13_USB_DETECT),
+ .start = gpio_to_irq(GPIO96_AC_DETECT),
+ .end = gpio_to_irq(GPIO96_AC_DETECT),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE |
+ IORESOURCE_IRQ_LOWEDGE,
+ },
+ [1] = {
+ .name = "usb",
+ .start = gpio_to_irq(GPIO13_nUSB_DETECT),
+ .end = gpio_to_irq(GPIO13_nUSB_DETECT),
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE |
IORESOURCE_IRQ_LOWEDGE,
},
@@ -691,120 +725,43 @@ static struct platform_device power_dev = {
},
};
-#if defined(CONFIG_PDA_POWER) && defined(CONFIG_TOUCHSCREEN_WM97XX)
-static struct wm97xx *battery_wm;
-
-static enum power_supply_property battery_props[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, /* Necessary for apm */
+static struct wm97xx_batt_info mioa701_battery_data = {
+ .batt_aux = WM97XX_AUX_ID1,
+ .temp_aux = -1,
+ .charge_gpio = -1,
+ .min_voltage = 0xc00,
+ .max_voltage = 0xfc0,
+ .batt_tech = POWER_SUPPLY_TECHNOLOGY_LION,
+ .batt_div = 1,
+ .batt_mult = 1,
+ .batt_name = "mioa701_battery",
};
-static int get_battery_voltage(void)
-{
- int adc = -1;
-
- if (battery_wm)
- adc = wm97xx_read_aux_adc(battery_wm, WM97XX_AUX_ID1);
- return adc;
-}
-
-static int get_battery_status(struct power_supply *b)
-{
- int status;
-
- if (is_usb_connected())
- status = POWER_SUPPLY_STATUS_CHARGING;
- else
- status = POWER_SUPPLY_STATUS_DISCHARGING;
-
- return status;
-}
-
-static int get_property(struct power_supply *b,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- int rc = 0;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = get_battery_status(b);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = 0xfd0;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
- val->intval = 0xc00;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_battery_voltage();
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = 100;
- break;
- default:
- val->intval = -1;
- rc = -1;
- }
-
- return rc;
+/*
+ * Camera interface
+ */
+struct pxacamera_platform_data mioa701_pxacamera_platform_data = {
+ .flags = PXA_CAMERA_MASTER | PXA_CAMERA_DATAWIDTH_8 |
+ PXA_CAMERA_PCLK_EN | PXA_CAMERA_MCLK_EN,
+ .mclk_10khz = 5000,
};
-static struct power_supply battery_ps = {
- .name = "mioa701_battery",
- .type = POWER_SUPPLY_TYPE_BATTERY,
- .get_property = get_property,
- .properties = battery_props,
- .num_properties = ARRAY_SIZE(battery_props),
+static struct soc_camera_link iclink = {
+ .bus_id = 0, /* Must match id in pxa27x_device_camera in device.c */
};
-static int battery_probe(struct platform_device *pdev)
-{
- struct wm97xx *wm = platform_get_drvdata(pdev);
- int rc;
-
- battery_wm = wm;
-
- rc = power_supply_register(NULL, &battery_ps);
- if (rc)
- dev_err(&pdev->dev,
- "Could not register mioa701 battery -> %d\n", rc);
- return rc;
-}
-
-static int battery_remove(struct platform_device *pdev)
-{
- battery_wm = NULL;
- return 0;
-}
-
-static struct platform_driver mioa701_battery_driver = {
- .driver = {
- .name = "wm97xx-battery",
+/* Board I2C devices. */
+static struct i2c_board_info __initdata mioa701_i2c_devices[] = {
+ {
+ /* Must initialize before the camera(s) */
+ I2C_BOARD_INFO("mt9m111", 0x5d),
+ .platform_data = &iclink,
},
- .probe = battery_probe,
- .remove = battery_remove
};
-static int __init mioa701_battery_init(void)
-{
- int rc;
-
- rc = platform_driver_register(&mioa701_battery_driver);
- if (rc)
- printk(KERN_ERR "Could not register mioa701 battery driver\n");
- return rc;
-}
-
-#else
-static int __init mioa701_battery_init(void)
-{
- return 0;
-}
-#endif
+struct i2c_pxa_platform_data i2c_pdata = {
+ .fast_mode = 1,
+};
/*
* Mio global
@@ -851,17 +808,17 @@ static void mioa701_machine_exit(void);
static void mioa701_poweroff(void)
{
mioa701_machine_exit();
- gpio_set_value(GPIO18_POWEROFF, 1);
+ arm_machine_restart('s');
}
static void mioa701_restart(char c)
{
mioa701_machine_exit();
- arm_machine_restart(c);
+ arm_machine_restart('s');
}
struct gpio_ress global_gpios[] = {
- MIO_GPIO_OUT(GPIO9_CHARGE_nEN, 1, "Charger enable"),
+ MIO_GPIO_OUT(GPIO9_CHARGE_EN, 1, "Charger enable"),
MIO_GPIO_OUT(GPIO18_POWEROFF, 0, "Power Off"),
MIO_GPIO_OUT(GPIO87_LCD_POWER, 0, "LCD Power")
};
@@ -879,12 +836,16 @@ static void __init mioa701_machine_init(void)
set_pxa_fb_info(&mioa701_pxafb_info);
pxa_set_mci_info(&mioa701_mci_info);
pxa_set_keypad_info(&mioa701_keypad_info);
+ wm97xx_bat_set_pdata(&mioa701_battery_data);
udc_init();
pm_power_off = mioa701_poweroff;
arm_pm_restart = mioa701_restart;
platform_add_devices(devices, ARRAY_SIZE(devices));
gsm_init();
- mioa701_battery_init();
+
+ pxa_set_i2c_info(&i2c_pdata);
+ pxa_set_camera_info(&mioa701_pxacamera_platform_data);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(mioa701_i2c_devices));
}
static void mioa701_machine_exit(void)
diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c
index b36cec5c9eed..34841c72815f 100644
--- a/arch/arm/mach-pxa/pcm990-baseboard.c
+++ b/arch/arm/mach-pxa/pcm990-baseboard.c
@@ -55,6 +55,10 @@ static unsigned long pcm990_pin_config[] __initdata = {
GPIO89_USBH1_PEN,
/* PWM0 */
GPIO16_PWM0_OUT,
+
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
};
/*
@@ -100,8 +104,7 @@ static struct pxafb_mode_info fb_info_sharp_lq084v1dg21 = {
static struct pxafb_mach_info pcm990_fbinfo __initdata = {
.modes = &fb_info_sharp_lq084v1dg21,
.num_modes = 1,
- .lccr0 = LCCR0_PAS,
- .lccr3 = LCCR3_PCP,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
.pxafb_lcd_power = pcm990_lcd_power,
};
#elif defined(CONFIG_PCM990_DISPLAY_NEC)
@@ -123,8 +126,7 @@ struct pxafb_mode_info fb_info_nec_nl6448bc20_18d = {
static struct pxafb_mach_info pcm990_fbinfo __initdata = {
.modes = &fb_info_nec_nl6448bc20_18d,
.num_modes = 1,
- .lccr0 = LCCR0_Act,
- .lccr3 = LCCR3_PixFlEdg,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
.pxafb_lcd_power = pcm990_lcd_power,
};
#endif
diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c
index 2e3bd8b1523b..ae88855bf974 100644
--- a/arch/arm/mach-pxa/poodle.c
+++ b/arch/arm/mach-pxa/poodle.c
@@ -20,6 +20,7 @@
#include <linux/fb.h>
#include <linux/pm.h>
#include <linux/delay.h>
+#include <linux/mtd/physmap.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
@@ -413,9 +414,40 @@ static struct pxafb_mach_info poodle_fb_info = {
.lcd_conn = LCD_COLOR_TFT_16BPP,
};
+static struct mtd_partition sharpsl_rom_parts[] = {
+ {
+ .name ="Boot PROM Filesystem",
+ .offset = 0x00120000,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data sharpsl_rom_data = {
+ .width = 2,
+ .nr_parts = ARRAY_SIZE(sharpsl_rom_parts),
+ .parts = sharpsl_rom_parts,
+};
+
+static struct resource sharpsl_rom_resources[] = {
+ {
+ .start = 0x00000000,
+ .end = 0x007fffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sharpsl_rom_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = sharpsl_rom_resources,
+ .num_resources = ARRAY_SIZE(sharpsl_rom_resources),
+ .dev.platform_data = &sharpsl_rom_data,
+};
+
static struct platform_device *devices[] __initdata = {
&poodle_locomo_device,
&poodle_scoop_device,
+ &sharpsl_rom_device,
};
static void poodle_poweroff(void)
diff --git a/arch/arm/mach-pxa/pwm.c b/arch/arm/mach-pxa/pwm.c
index 74e2ead8cee8..3ca7ffc6904b 100644
--- a/arch/arm/mach-pxa/pwm.c
+++ b/arch/arm/mach-pxa/pwm.c
@@ -173,7 +173,7 @@ static struct pwm_device *pwm_probe(struct platform_device *pdev,
return ERR_PTR(-ENOMEM);
}
- pwm->clk = clk_get(&pdev->dev, "PWMCLK");
+ pwm->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(pwm->clk)) {
ret = PTR_ERR(pwm->clk);
goto err_free;
diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
index 25d17a1dab78..6c57522e2469 100644
--- a/arch/arm/mach-pxa/pxa25x.c
+++ b/arch/arm/mach-pxa/pxa25x.c
@@ -36,12 +36,6 @@
#include "devices.h"
#include "clock.h"
-int cpu_is_pxa26x(void)
-{
- return cpu_is_pxa250() && ((BOOT_DEF & 0x8) == 0);
-}
-EXPORT_SYMBOL_GPL(cpu_is_pxa26x);
-
/*
* Various clock factors driven by the CCCR register.
*/
@@ -167,36 +161,51 @@ static const struct clkops clk_pxa25x_gpio11_ops = {
* 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz
* 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly)
*/
-static struct clk pxa25x_hwuart_clk =
- INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev)
-;
+static DEFINE_CKEN(pxa25x_hwuart, HWUART, 14745600, 1);
+
+static struct clk_lookup pxa25x_hwuart_clkreg =
+ INIT_CLKREG(&clk_pxa25x_hwuart, "pxa2xx-uart.3", NULL);
/*
* PXA 2xx clock declarations.
*/
-static struct clk pxa25x_clks[] = {
- INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev),
- INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev),
- INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
- INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL),
- INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa25x_device_udc.dev),
- INIT_CLK("GPIO11_CLK", &clk_pxa25x_gpio11_ops, 3686400, 0, NULL),
- INIT_CLK("GPIO12_CLK", &clk_pxa25x_gpio12_ops, 32768, 0, NULL),
- INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev),
- INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev),
-
- INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev),
- INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev),
- INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev),
- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, &pxa25x_device_pwm0.dev),
- INIT_CKEN("PWMCLK", PWM1, 3686400, 0, &pxa25x_device_pwm1.dev),
-
- INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
-
- /*
- INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
- */
- INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
+static DEFINE_CK(pxa25x_lcd, LCD, &clk_pxa25x_lcd_ops);
+static DEFINE_CKEN(pxa25x_ffuart, FFUART, 14745600, 1);
+static DEFINE_CKEN(pxa25x_btuart, BTUART, 14745600, 1);
+static DEFINE_CKEN(pxa25x_stuart, STUART, 14745600, 1);
+static DEFINE_CKEN(pxa25x_usb, USB, 47923000, 5);
+static DEFINE_CLK(pxa25x_gpio11, &clk_pxa25x_gpio11_ops, 3686400, 0);
+static DEFINE_CLK(pxa25x_gpio12, &clk_pxa25x_gpio12_ops, 32768, 0);
+static DEFINE_CKEN(pxa25x_mmc, MMC, 19169000, 0);
+static DEFINE_CKEN(pxa25x_i2c, I2C, 31949000, 0);
+static DEFINE_CKEN(pxa25x_ssp, SSP, 3686400, 0);
+static DEFINE_CKEN(pxa25x_nssp, NSSP, 3686400, 0);
+static DEFINE_CKEN(pxa25x_assp, ASSP, 3686400, 0);
+static DEFINE_CKEN(pxa25x_pwm0, PWM0, 3686400, 0);
+static DEFINE_CKEN(pxa25x_pwm1, PWM1, 3686400, 0);
+static DEFINE_CKEN(pxa25x_ac97, AC97, 24576000, 0);
+static DEFINE_CKEN(pxa25x_i2s, I2S, 14745600, 0);
+static DEFINE_CKEN(pxa25x_ficp, FICP, 47923000, 0);
+
+static struct clk_lookup pxa25x_clkregs[] = {
+ INIT_CLKREG(&clk_pxa25x_lcd, "pxa2xx-fb", NULL),
+ INIT_CLKREG(&clk_pxa25x_ffuart, "pxa2xx-uart.0", NULL),
+ INIT_CLKREG(&clk_pxa25x_btuart, "pxa2xx-uart.1", NULL),
+ INIT_CLKREG(&clk_pxa25x_stuart, "pxa2xx-uart.2", NULL),
+ INIT_CLKREG(&clk_pxa25x_usb, "pxa25x-udc", NULL),
+ INIT_CLKREG(&clk_pxa25x_mmc, "pxa2xx-mci.0", NULL),
+ INIT_CLKREG(&clk_pxa25x_i2c, "pxa2xx-i2c.0", NULL),
+ INIT_CLKREG(&clk_pxa25x_ssp, "pxa25x-ssp.0", NULL),
+ INIT_CLKREG(&clk_pxa25x_nssp, "pxa25x-nssp.1", NULL),
+ INIT_CLKREG(&clk_pxa25x_assp, "pxa25x-nssp.2", NULL),
+ INIT_CLKREG(&clk_pxa25x_pwm0, "pxa25x-pwm.0", NULL),
+ INIT_CLKREG(&clk_pxa25x_pwm1, "pxa25x-pwm.1", NULL),
+ INIT_CLKREG(&clk_pxa25x_i2s, "pxa2xx-i2s", NULL),
+ INIT_CLKREG(&clk_pxa25x_stuart, "pxa2xx-ir", "UARTCLK"),
+ INIT_CLKREG(&clk_pxa25x_ficp, "pxa2xx-ir", "FICPCLK"),
+ INIT_CLKREG(&clk_pxa25x_ac97, NULL, "AC97CLK"),
+ INIT_CLKREG(&clk_pxa25x_gpio11, NULL, "GPIO11_CLK"),
+ INIT_CLKREG(&clk_pxa25x_gpio12, NULL, "GPIO12_CLK"),
};
#ifdef CONFIG_PM
@@ -304,13 +313,21 @@ void __init pxa25x_init_irq(void)
pxa_init_gpio(85, pxa25x_set_wake);
}
+#ifdef CONFIG_CPU_PXA26x
+void __init pxa26x_init_irq(void)
+{
+ pxa_init_irq(32, pxa25x_set_wake);
+ pxa_init_gpio(90, pxa25x_set_wake);
+}
+#endif
+
static struct platform_device *pxa25x_devices[] __initdata = {
&pxa25x_device_udc,
&pxa_device_ffuart,
&pxa_device_btuart,
&pxa_device_stuart,
&pxa_device_i2s,
- &pxa_device_rtc,
+ &sa1100_device_rtc,
&pxa25x_device_ssp,
&pxa25x_device_nssp,
&pxa25x_device_assp,
@@ -336,7 +353,7 @@ static int __init pxa25x_init(void)
reset_status = RCSR;
- clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
+ clks_register(pxa25x_clkregs, ARRAY_SIZE(pxa25x_clkregs));
if ((ret = pxa_init_dma(16)))
return ret;
@@ -356,8 +373,8 @@ static int __init pxa25x_init(void)
}
/* Only add HWUART for PXA255/26x; PXA210/250 do not have it. */
- if (cpu_is_pxa255() || cpu_is_pxa26x()) {
- clks_register(&pxa25x_hwuart_clk, 1);
+ if (cpu_is_pxa255()) {
+ clks_register(&pxa25x_hwuart_clkreg, 1);
ret = platform_device_register(&pxa_device_hwuart);
}
diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index 3e4ab2279c99..411bec54fdc4 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -144,40 +144,59 @@ static const struct clkops clk_pxa27x_lcd_ops = {
.getrate = clk_pxa27x_lcd_getrate,
};
-static struct clk pxa27x_clks[] = {
- INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_ops, &pxa_device_fb.dev),
- INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops, NULL),
-
- INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
- INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
- INIT_CKEN("UARTCLK", STUART, 14857000, 1, NULL),
-
- INIT_CKEN("I2SCLK", I2S, 14682000, 0, &pxa_device_i2s.dev),
- INIT_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev),
- INIT_CKEN("UDCCLK", USB, 48000000, 5, &pxa27x_device_udc.dev),
- INIT_CKEN("MMCCLK", MMC, 19500000, 0, &pxa_device_mci.dev),
- INIT_CKEN("FICPCLK", FICP, 48000000, 0, &pxa_device_ficp.dev),
-
- INIT_CKEN("USBCLK", USBHOST, 48000000, 0, &pxa27x_device_ohci.dev),
- INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev),
- INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, &pxa27x_device_keypad.dev),
-
- INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
- INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
- INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
- INIT_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
- INIT_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
-
- INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
- INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0, NULL),
-
- /*
- INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL),
- INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL),
- INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL),
- INIT_CKEN("IMCLK", IM, 0, 0, NULL),
- INIT_CKEN("MEMCLK", MEMC, 0, 0, NULL),
- */
+static DEFINE_CK(pxa27x_lcd, LCD, &clk_pxa27x_lcd_ops);
+static DEFINE_CK(pxa27x_camera, CAMERA, &clk_pxa27x_lcd_ops);
+static DEFINE_CKEN(pxa27x_ffuart, FFUART, 14857000, 1);
+static DEFINE_CKEN(pxa27x_btuart, BTUART, 14857000, 1);
+static DEFINE_CKEN(pxa27x_stuart, STUART, 14857000, 1);
+static DEFINE_CKEN(pxa27x_i2s, I2S, 14682000, 0);
+static DEFINE_CKEN(pxa27x_i2c, I2C, 32842000, 0);
+static DEFINE_CKEN(pxa27x_usb, USB, 48000000, 5);
+static DEFINE_CKEN(pxa27x_mmc, MMC, 19500000, 0);
+static DEFINE_CKEN(pxa27x_ficp, FICP, 48000000, 0);
+static DEFINE_CKEN(pxa27x_usbhost, USBHOST, 48000000, 0);
+static DEFINE_CKEN(pxa27x_pwri2c, PWRI2C, 13000000, 0);
+static DEFINE_CKEN(pxa27x_keypad, KEYPAD, 32768, 0);
+static DEFINE_CKEN(pxa27x_ssp1, SSP1, 13000000, 0);
+static DEFINE_CKEN(pxa27x_ssp2, SSP2, 13000000, 0);
+static DEFINE_CKEN(pxa27x_ssp3, SSP3, 13000000, 0);
+static DEFINE_CKEN(pxa27x_pwm0, PWM0, 13000000, 0);
+static DEFINE_CKEN(pxa27x_pwm1, PWM1, 13000000, 0);
+static DEFINE_CKEN(pxa27x_ac97, AC97, 24576000, 0);
+static DEFINE_CKEN(pxa27x_ac97conf, AC97CONF, 24576000, 0);
+static DEFINE_CKEN(pxa27x_msl, MSL, 48000000, 0);
+static DEFINE_CKEN(pxa27x_usim, USIM, 48000000, 0);
+static DEFINE_CKEN(pxa27x_memstk, MEMSTK, 19500000, 0);
+static DEFINE_CKEN(pxa27x_im, IM, 0, 0);
+static DEFINE_CKEN(pxa27x_memc, MEMC, 0, 0);
+
+static struct clk_lookup pxa27x_clkregs[] = {
+ INIT_CLKREG(&clk_pxa27x_lcd, "pxa2xx-fb", NULL),
+ INIT_CLKREG(&clk_pxa27x_camera, "pxa27x-camera.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_ffuart, "pxa2xx-uart.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_btuart, "pxa2xx-uart.1", NULL),
+ INIT_CLKREG(&clk_pxa27x_stuart, "pxa2xx-uart.2", NULL),
+ INIT_CLKREG(&clk_pxa27x_i2s, "pxa2xx-i2s", NULL),
+ INIT_CLKREG(&clk_pxa27x_i2c, "pxa2xx-i2c.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_usb, "pxa27x-udc", NULL),
+ INIT_CLKREG(&clk_pxa27x_mmc, "pxa2xx-mci.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_stuart, "pxa2xx-ir", "UARTCLK"),
+ INIT_CLKREG(&clk_pxa27x_ficp, "pxa2xx-ir", "FICPCLK"),
+ INIT_CLKREG(&clk_pxa27x_usbhost, "pxa27x-ohci", NULL),
+ INIT_CLKREG(&clk_pxa27x_pwri2c, "pxa2xx-i2c.1", NULL),
+ INIT_CLKREG(&clk_pxa27x_keypad, "pxa27x-keypad", NULL),
+ INIT_CLKREG(&clk_pxa27x_ssp1, "pxa27x-ssp.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_ssp2, "pxa27x-ssp.1", NULL),
+ INIT_CLKREG(&clk_pxa27x_ssp3, "pxa27x-ssp.2", NULL),
+ INIT_CLKREG(&clk_pxa27x_pwm0, "pxa27x-pwm.0", NULL),
+ INIT_CLKREG(&clk_pxa27x_pwm1, "pxa27x-pwm.1", NULL),
+ INIT_CLKREG(&clk_pxa27x_ac97, NULL, "AC97CLK"),
+ INIT_CLKREG(&clk_pxa27x_ac97conf, NULL, "AC97CONFCLK"),
+ INIT_CLKREG(&clk_pxa27x_msl, NULL, "MSLCLK"),
+ INIT_CLKREG(&clk_pxa27x_usim, NULL, "USIMCLK"),
+ INIT_CLKREG(&clk_pxa27x_memstk, NULL, "MSTKCLK"),
+ INIT_CLKREG(&clk_pxa27x_im, NULL, "IMCLK"),
+ INIT_CLKREG(&clk_pxa27x_memc, NULL, "MEMCLK"),
};
#ifdef CONFIG_PM
@@ -313,38 +332,18 @@ static int pxa27x_set_wake(unsigned int irq, unsigned int on)
void __init pxa27x_init_irq(void)
{
pxa_init_irq(34, pxa27x_set_wake);
- pxa_init_gpio(128, pxa27x_set_wake);
+ pxa_init_gpio(121, pxa27x_set_wake);
}
/*
* device registration specific to PXA27x.
*/
-
-static struct resource i2c_power_resources[] = {
- {
- .start = 0x40f00180,
- .end = 0x40f001a3,
- .flags = IORESOURCE_MEM,
- }, {
- .start = IRQ_PWRI2C,
- .end = IRQ_PWRI2C,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device pxa27x_device_i2c_power = {
- .name = "pxa2xx-i2c",
- .id = 1,
- .resource = i2c_power_resources,
- .num_resources = ARRAY_SIZE(i2c_power_resources),
-};
-
void __init pxa27x_set_i2c_power_info(struct i2c_pxa_platform_data *info)
{
local_irq_disable();
PCFR |= PCFR_PI2CEN;
local_irq_enable();
- pxa27x_device_i2c_power.dev.platform_data = info;
+ pxa_register_device(&pxa27x_device_i2c_power, info);
}
static struct platform_device *devices[] __initdata = {
@@ -353,8 +352,8 @@ static struct platform_device *devices[] __initdata = {
&pxa_device_btuart,
&pxa_device_stuart,
&pxa_device_i2s,
+ &sa1100_device_rtc,
&pxa_device_rtc,
- &pxa27x_device_i2c_power,
&pxa27x_device_ssp1,
&pxa27x_device_ssp2,
&pxa27x_device_ssp3,
@@ -380,7 +379,7 @@ static int __init pxa27x_init(void)
reset_status = RCSR;
- clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
+ clks_register(pxa27x_clkregs, ARRAY_SIZE(pxa27x_clkregs));
if ((ret = pxa_init_dma(32)))
return ret;
diff --git a/arch/arm/mach-pxa/pxa300.c b/arch/arm/mach-pxa/pxa300.c
index 9adc7fc4618a..f735e58e6669 100644
--- a/arch/arm/mach-pxa/pxa300.c
+++ b/arch/arm/mach-pxa/pxa300.c
@@ -85,14 +85,16 @@ static struct pxa3xx_mfp_addr_map pxa310_mfp_addr_map[] __initdata = {
MFP_ADDR_END,
};
-static struct clk common_clks[] = {
- PXA3xx_CKEN("NANDCLK", NAND, 156000000, 0, &pxa3xx_device_nand.dev),
+static DEFINE_PXA3_CKEN(common_nand, NAND, 156000000, 0);
+
+static struct clk_lookup common_clkregs[] = {
+ INIT_CLKREG(&clk_common_nand, "pxa3xx-nand", "NANDCLK"),
};
-static struct clk pxa310_clks[] = {
-#ifdef CONFIG_CPU_PXA310
- PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev),
-#endif
+static DEFINE_PXA3_CKEN(pxa310_mmc3, MMC3, 19500000, 0);
+
+static struct clk_lookup pxa310_clkregs[] = {
+ INIT_CLKREG(&clk_pxa310_mmc3, "pxa2xx-mci.2", "MMCCLK"),
};
static int __init pxa300_init(void)
@@ -100,12 +102,12 @@ static int __init pxa300_init(void)
if (cpu_is_pxa300() || cpu_is_pxa310()) {
pxa3xx_init_mfp();
pxa3xx_mfp_init_addr(pxa300_mfp_addr_map);
- clks_register(ARRAY_AND_SIZE(common_clks));
+ clks_register(ARRAY_AND_SIZE(common_clkregs));
}
if (cpu_is_pxa310()) {
pxa3xx_mfp_init_addr(pxa310_mfp_addr_map);
- clks_register(ARRAY_AND_SIZE(pxa310_clks));
+ clks_register(ARRAY_AND_SIZE(pxa310_clkregs));
}
return 0;
diff --git a/arch/arm/mach-pxa/pxa320.c b/arch/arm/mach-pxa/pxa320.c
index 016eb18f01a3..effe408c186f 100644
--- a/arch/arm/mach-pxa/pxa320.c
+++ b/arch/arm/mach-pxa/pxa320.c
@@ -80,8 +80,10 @@ static struct pxa3xx_mfp_addr_map pxa320_mfp_addr_map[] __initdata = {
MFP_ADDR_END,
};
-static struct clk pxa320_clks[] = {
- PXA3xx_CKEN("NANDCLK", NAND, 104000000, 0, &pxa3xx_device_nand.dev),
+static DEFINE_PXA3_CKEN(pxa320_nand, NAND, 104000000, 0);
+
+static struct clk_lookup pxa320_clkregs[] = {
+ INIT_CLKREG(&clk_pxa320_nand, "pxa3xx-nand", "NANDCLK"),
};
static int __init pxa320_init(void)
@@ -89,7 +91,7 @@ static int __init pxa320_init(void)
if (cpu_is_pxa320()) {
pxa3xx_init_mfp();
pxa3xx_mfp_init_addr(pxa320_mfp_addr_map);
- clks_register(ARRAY_AND_SIZE(pxa320_clks));
+ clks_register(ARRAY_AND_SIZE(pxa320_clkregs));
}
return 0;
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index b3cd5d0b0f35..490893824e78 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -29,6 +29,7 @@
#include <mach/pm.h>
#include <mach/dma.h>
#include <mach/ssp.h>
+#include <mach/i2c.h>
#include "generic.h"
#include "devices.h"
@@ -216,43 +217,58 @@ static const struct clkops clk_dummy_ops = {
.disable = clk_dummy_disable,
};
-static struct clk pxa3xx_clks[] = {
- {
- .name = "CLK_POUT",
- .ops = &clk_pout_ops,
- .rate = 13000000,
- .delay = 70,
- },
-
- /* Power I2C clock is always on */
- {
- .name = "I2CCLK",
- .ops = &clk_dummy_ops,
- .dev = &pxa3xx_device_i2c_power.dev,
- },
-
- PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev),
- PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL),
- PXA3xx_CK("AC97CLK", AC97, &clk_pxa3xx_ac97_ops, NULL),
-
- PXA3xx_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
- PXA3xx_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
- PXA3xx_CKEN("UARTCLK", STUART, 14857000, 1, NULL),
-
- PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev),
- PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa27x_device_udc.dev),
- PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev),
- PXA3xx_CKEN("KBDCLK", KEYPAD, 32768, 0, &pxa27x_device_keypad.dev),
+static struct clk clk_pxa3xx_pout = {
+ .ops = &clk_pout_ops,
+ .rate = 13000000,
+ .delay = 70,
+};
- PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev),
- PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev),
- PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev),
- PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev),
- PXA3xx_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
- PXA3xx_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
+static struct clk clk_dummy = {
+ .ops = &clk_dummy_ops,
+};
- PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev),
- PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev),
+static DEFINE_PXA3_CK(pxa3xx_lcd, LCD, &clk_pxa3xx_hsio_ops);
+static DEFINE_PXA3_CK(pxa3xx_camera, CAMERA, &clk_pxa3xx_hsio_ops);
+static DEFINE_PXA3_CK(pxa3xx_ac97, AC97, &clk_pxa3xx_ac97_ops);
+static DEFINE_PXA3_CKEN(pxa3xx_ffuart, FFUART, 14857000, 1);
+static DEFINE_PXA3_CKEN(pxa3xx_btuart, BTUART, 14857000, 1);
+static DEFINE_PXA3_CKEN(pxa3xx_stuart, STUART, 14857000, 1);
+static DEFINE_PXA3_CKEN(pxa3xx_i2c, I2C, 32842000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_udc, UDC, 48000000, 5);
+static DEFINE_PXA3_CKEN(pxa3xx_usbh, USBH, 48000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_keypad, KEYPAD, 32768, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_ssp1, SSP1, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_ssp2, SSP2, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_ssp3, SSP3, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_ssp4, SSP4, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_pwm0, PWM0, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_pwm1, PWM1, 13000000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_mmc1, MMC1, 19500000, 0);
+static DEFINE_PXA3_CKEN(pxa3xx_mmc2, MMC2, 19500000, 0);
+
+static struct clk_lookup pxa3xx_clkregs[] = {
+ INIT_CLKREG(&clk_pxa3xx_pout, NULL, "CLK_POUT"),
+ /* Power I2C clock is always on */
+ INIT_CLKREG(&clk_dummy, "pxa2xx-i2c.1", NULL),
+ INIT_CLKREG(&clk_pxa3xx_lcd, "pxa2xx-fb", NULL),
+ INIT_CLKREG(&clk_pxa3xx_camera, NULL, "CAMCLK"),
+ INIT_CLKREG(&clk_pxa3xx_ac97, NULL, "AC97CLK"),
+ INIT_CLKREG(&clk_pxa3xx_ffuart, "pxa2xx-uart.0", NULL),
+ INIT_CLKREG(&clk_pxa3xx_btuart, "pxa2xx-uart.1", NULL),
+ INIT_CLKREG(&clk_pxa3xx_stuart, "pxa2xx-uart.2", NULL),
+ INIT_CLKREG(&clk_pxa3xx_stuart, "pxa2xx-ir", "UARTCLK"),
+ INIT_CLKREG(&clk_pxa3xx_i2c, "pxa2xx-i2c.0", NULL),
+ INIT_CLKREG(&clk_pxa3xx_udc, "pxa27x-udc", NULL),
+ INIT_CLKREG(&clk_pxa3xx_usbh, "pxa27x-ohci", NULL),
+ INIT_CLKREG(&clk_pxa3xx_keypad, "pxa27x-keypad", NULL),
+ INIT_CLKREG(&clk_pxa3xx_ssp1, "pxa27x-ssp.0", NULL),
+ INIT_CLKREG(&clk_pxa3xx_ssp2, "pxa27x-ssp.1", NULL),
+ INIT_CLKREG(&clk_pxa3xx_ssp3, "pxa27x-ssp.2", NULL),
+ INIT_CLKREG(&clk_pxa3xx_ssp4, "pxa27x-ssp.3", NULL),
+ INIT_CLKREG(&clk_pxa3xx_pwm0, "pxa27x-pwm.0", NULL),
+ INIT_CLKREG(&clk_pxa3xx_pwm1, "pxa27x-pwm.1", NULL),
+ INIT_CLKREG(&clk_pxa3xx_mmc1, "pxa2xx-mci.0", NULL),
+ INIT_CLKREG(&clk_pxa3xx_mmc2, "pxa2xx-mci.1", NULL),
};
#ifdef CONFIG_PM
@@ -529,28 +545,9 @@ void __init pxa3xx_init_irq(void)
* device registration specific to PXA3xx.
*/
-static struct resource i2c_power_resources[] = {
- {
- .start = 0x40f500c0,
- .end = 0x40f500d3,
- .flags = IORESOURCE_MEM,
- }, {
- .start = IRQ_PWRI2C,
- .end = IRQ_PWRI2C,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device pxa3xx_device_i2c_power = {
- .name = "pxa2xx-i2c",
- .id = 1,
- .resource = i2c_power_resources,
- .num_resources = ARRAY_SIZE(i2c_power_resources),
-};
-
void __init pxa3xx_set_i2c_power_info(struct i2c_pxa_platform_data *info)
{
- pxa3xx_device_i2c_power.dev.platform_data = info;
+ pxa_register_device(&pxa3xx_device_i2c_power, info);
}
static struct platform_device *devices[] __initdata = {
@@ -559,6 +556,7 @@ static struct platform_device *devices[] __initdata = {
&pxa_device_btuart,
&pxa_device_stuart,
&pxa_device_i2s,
+ &sa1100_device_rtc,
&pxa_device_rtc,
&pxa27x_device_ssp1,
&pxa27x_device_ssp2,
@@ -566,7 +564,6 @@ static struct platform_device *devices[] __initdata = {
&pxa3xx_device_ssp4,
&pxa27x_device_pwm0,
&pxa27x_device_pwm1,
- &pxa3xx_device_i2c_power,
};
static struct sys_device pxa3xx_sysdev[] = {
@@ -595,7 +592,7 @@ static int __init pxa3xx_init(void)
*/
ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S);
- clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks));
+ clks_register(pxa3xx_clkregs, ARRAY_SIZE(pxa3xx_clkregs));
if ((ret = pxa_init_dma(32)))
return ret;
diff --git a/arch/arm/mach-pxa/saar.c b/arch/arm/mach-pxa/saar.c
index e7ea91ce7f02..5d02a7325586 100644
--- a/arch/arm/mach-pxa/saar.c
+++ b/arch/arm/mach-pxa/saar.c
@@ -17,19 +17,44 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
#include <linux/smc91x.h>
+#include <linux/mfd/da903x.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/hardware.h>
#include <mach/pxa3xx-regs.h>
#include <mach/mfp-pxa930.h>
+#include <mach/i2c.h>
+#include <mach/regs-lcd.h>
+#include <mach/pxafb.h>
#include "devices.h"
#include "generic.h"
+#define GPIO_LCD_RESET (16)
+
/* SAAR MFP configurations */
static mfp_cfg_t saar_mfp_cfg[] __initdata = {
+ /* LCD */
+ GPIO23_LCD_DD0,
+ GPIO24_LCD_DD1,
+ GPIO25_LCD_DD2,
+ GPIO26_LCD_DD3,
+ GPIO27_LCD_DD4,
+ GPIO28_LCD_DD5,
+ GPIO29_LCD_DD6,
+ GPIO44_LCD_DD7,
+ GPIO21_LCD_CS,
+ GPIO22_LCD_VSYNC,
+ GPIO17_LCD_FCLK_RD,
+ GPIO18_LCD_LCLK_A0,
+ GPIO19_LCD_PCLK_WR,
+ GPIO16_GPIO, /* LCD reset */
+
/* Ethernet */
DF_nCS1_nCS3,
GPIO97_GPIO,
@@ -64,12 +89,408 @@ static struct platform_device smc91x_device = {
},
};
+#if defined(CONFIG_FB_PXA) || (CONFIG_FB_PXA_MODULE)
+static uint16_t lcd_power_on[] = {
+ /* single frame */
+ SMART_CMD_NOOP,
+ SMART_CMD(0x00),
+ SMART_DELAY(0),
+
+ SMART_CMD_NOOP,
+ SMART_CMD(0x00),
+ SMART_DELAY(0),
+
+ SMART_CMD_NOOP,
+ SMART_CMD(0x00),
+ SMART_DELAY(0),
+
+ SMART_CMD_NOOP,
+ SMART_CMD(0x00),
+ SMART_DELAY(10),
+
+ /* calibration control */
+ SMART_CMD(0x00),
+ SMART_CMD(0xA4),
+ SMART_DAT(0x80),
+ SMART_DAT(0x01),
+ SMART_DELAY(150),
+
+ /*Power-On Init sequence*/
+ SMART_CMD(0x00), /* output ctrl */
+ SMART_CMD(0x01),
+ SMART_DAT(0x01),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* wave ctrl */
+ SMART_CMD(0x02),
+ SMART_DAT(0x07),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x03), /* entry mode */
+ SMART_DAT(0xD0),
+ SMART_DAT(0x30),
+ SMART_CMD(0x00),
+ SMART_CMD(0x08), /* display ctrl 2 */
+ SMART_DAT(0x08),
+ SMART_DAT(0x08),
+ SMART_CMD(0x00),
+ SMART_CMD(0x09), /* display ctrl 3 */
+ SMART_DAT(0x04),
+ SMART_DAT(0x2F),
+ SMART_CMD(0x00),
+ SMART_CMD(0x0A), /* display ctrl 4 */
+ SMART_DAT(0x00),
+ SMART_DAT(0x08),
+ SMART_CMD(0x00),
+ SMART_CMD(0x0D), /* Frame Marker position */
+ SMART_DAT(0x00),
+ SMART_DAT(0x08),
+ SMART_CMD(0x00),
+ SMART_CMD(0x60), /* Driver output control */
+ SMART_DAT(0x27),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x61), /* Base image display control */
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_CMD(0x00),
+ SMART_CMD(0x30), /* Y settings 30h-3Dh */
+ SMART_DAT(0x07),
+ SMART_DAT(0x07),
+ SMART_CMD(0x00),
+ SMART_CMD(0x31),
+ SMART_DAT(0x00),
+ SMART_DAT(0x07),
+ SMART_CMD(0x00),
+ SMART_CMD(0x32), /* Timing(3), ASW HOLD=0.5CLK */
+ SMART_DAT(0x04),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x33), /* Timing(4), CKV ST=0CLK, CKV ED=1CLK */
+ SMART_DAT(0x03),
+ SMART_DAT(0x03),
+ SMART_CMD(0x00),
+ SMART_CMD(0x34),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x35),
+ SMART_DAT(0x02),
+ SMART_DAT(0x05),
+ SMART_CMD(0x00),
+ SMART_CMD(0x36),
+ SMART_DAT(0x1F),
+ SMART_DAT(0x1F),
+ SMART_CMD(0x00),
+ SMART_CMD(0x37),
+ SMART_DAT(0x07),
+ SMART_DAT(0x07),
+ SMART_CMD(0x00),
+ SMART_CMD(0x38),
+ SMART_DAT(0x00),
+ SMART_DAT(0x07),
+ SMART_CMD(0x00),
+ SMART_CMD(0x39),
+ SMART_DAT(0x04),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x3A),
+ SMART_DAT(0x03),
+ SMART_DAT(0x03),
+ SMART_CMD(0x00),
+ SMART_CMD(0x3B),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x3C),
+ SMART_DAT(0x02),
+ SMART_DAT(0x05),
+ SMART_CMD(0x00),
+ SMART_CMD(0x3D),
+ SMART_DAT(0x1F),
+ SMART_DAT(0x1F),
+ SMART_CMD(0x00), /* Display control 1 */
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_CMD(0x00), /* Power control 5 */
+ SMART_CMD(0x17),
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_CMD(0x00), /* Power control 1 */
+ SMART_CMD(0x10),
+ SMART_DAT(0x10),
+ SMART_DAT(0xB0),
+ SMART_CMD(0x00), /* Power control 2 */
+ SMART_CMD(0x11),
+ SMART_DAT(0x01),
+ SMART_DAT(0x30),
+ SMART_CMD(0x00), /* Power control 3 */
+ SMART_CMD(0x12),
+ SMART_DAT(0x01),
+ SMART_DAT(0x9E),
+ SMART_CMD(0x00), /* Power control 4 */
+ SMART_CMD(0x13),
+ SMART_DAT(0x17),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* Power control 3 */
+ SMART_CMD(0x12),
+ SMART_DAT(0x01),
+ SMART_DAT(0xBE),
+ SMART_DELAY(100),
+
+ /* display mode : 240*320 */
+ SMART_CMD(0x00), /* RAM address set(H) 0*/
+ SMART_CMD(0x20),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* RAM address set(V) 4*/
+ SMART_CMD(0x21),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* Start of Window RAM address set(H) 8*/
+ SMART_CMD(0x50),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* End of Window RAM address set(H) 12*/
+ SMART_CMD(0x51),
+ SMART_DAT(0x00),
+ SMART_DAT(0xEF),
+ SMART_CMD(0x00), /* Start of Window RAM address set(V) 16*/
+ SMART_CMD(0x52),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* End of Window RAM address set(V) 20*/
+ SMART_CMD(0x53),
+ SMART_DAT(0x01),
+ SMART_DAT(0x3F),
+ SMART_CMD(0x00), /* Panel interface control 1 */
+ SMART_CMD(0x90),
+ SMART_DAT(0x00),
+ SMART_DAT(0x1A),
+ SMART_CMD(0x00), /* Panel interface control 2 */
+ SMART_CMD(0x92),
+ SMART_DAT(0x04),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* Panel interface control 3 */
+ SMART_CMD(0x93),
+ SMART_DAT(0x00),
+ SMART_DAT(0x05),
+ SMART_DELAY(20),
+};
+
+static uint16_t lcd_panel_on[] = {
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x21),
+ SMART_DELAY(1),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x61),
+ SMART_DELAY(100),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x01),
+ SMART_DAT(0x73),
+ SMART_DELAY(1),
+};
+
+static uint16_t lcd_panel_off[] = {
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x72),
+ SMART_DELAY(40),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_DELAY(1),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_DELAY(1),
+};
+
+static uint16_t lcd_power_off[] = {
+ SMART_CMD(0x00),
+ SMART_CMD(0x10),
+ SMART_DAT(0x00),
+ SMART_DAT(0x80),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x11),
+ SMART_DAT(0x01),
+ SMART_DAT(0x60),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x12),
+ SMART_DAT(0x01),
+ SMART_DAT(0xAE),
+ SMART_DELAY(40),
+
+ SMART_CMD(0x00),
+ SMART_CMD(0x10),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+};
+
+static uint16_t update_framedata[] = {
+ /* set display ram: 240*320 */
+ SMART_CMD(0x00), /* RAM address set(H) 0*/
+ SMART_CMD(0x20),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* RAM address set(V) 4*/
+ SMART_CMD(0x21),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* Start of Window RAM address set(H) 8 */
+ SMART_CMD(0x50),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* End of Window RAM address set(H) 12 */
+ SMART_CMD(0x51),
+ SMART_DAT(0x00),
+ SMART_DAT(0xEF),
+ SMART_CMD(0x00), /* Start of Window RAM address set(V) 16 */
+ SMART_CMD(0x52),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00), /* End of Window RAM address set(V) 20 */
+ SMART_CMD(0x53),
+ SMART_DAT(0x01),
+ SMART_DAT(0x3F),
+
+ /* wait for vsync cmd before transferring frame data */
+ SMART_CMD_WAIT_FOR_VSYNC,
+
+ /* write ram */
+ SMART_CMD(0x00),
+ SMART_CMD(0x22),
+
+ /* write frame data */
+ SMART_CMD_WRITE_FRAME,
+};
+
+static void ltm022a97a_lcd_power(int on, struct fb_var_screeninfo *var)
+{
+ static int pin_requested = 0;
+ struct fb_info *info = container_of(var, struct fb_info, var);
+ int err;
+
+ if (!pin_requested) {
+ err = gpio_request(GPIO_LCD_RESET, "lcd reset");
+ if (err) {
+ pr_err("failed to request gpio for LCD reset\n");
+ return;
+ }
+
+ gpio_direction_output(GPIO_LCD_RESET, 0);
+ pin_requested = 1;
+ }
+
+ if (on) {
+ gpio_set_value(GPIO_LCD_RESET, 0); msleep(100);
+ gpio_set_value(GPIO_LCD_RESET, 1); msleep(10);
+
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(lcd_power_on));
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(lcd_panel_on));
+ } else {
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(lcd_panel_off));
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(lcd_power_off));
+ }
+
+ err = pxafb_smart_flush(info);
+ if (err)
+ pr_err("%s: timed out\n", __func__);
+}
+
+static void ltm022a97a_update(struct fb_info *info)
+{
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(update_framedata));
+ pxafb_smart_flush(info);
+}
+
+static struct pxafb_mode_info toshiba_ltm022a97a_modes[] = {
+ [0] = {
+ .xres = 240,
+ .yres = 320,
+ .bpp = 16,
+ .a0csrd_set_hld = 30,
+ .a0cswr_set_hld = 30,
+ .wr_pulse_width = 30,
+ .rd_pulse_width = 30,
+ .op_hold_time = 30,
+ .cmd_inh_time = 60,
+
+ /* L_LCLK_A0 and L_LCLK_RD active low */
+ .sync = FB_SYNC_HOR_HIGH_ACT |
+ FB_SYNC_VERT_HIGH_ACT,
+ },
+};
+
+static struct pxafb_mach_info saar_lcd_info = {
+ .modes = toshiba_ltm022a97a_modes,
+ .num_modes = 1,
+ .lcd_conn = LCD_SMART_PANEL_8BPP | LCD_PCLK_EDGE_FALL,
+ .pxafb_lcd_power = ltm022a97a_lcd_power,
+ .smart_update = ltm022a97a_update,
+};
+
+static void __init saar_init_lcd(void)
+{
+ set_pxa_fb_info(&saar_lcd_info);
+}
+#else
+static inline void saar_init_lcd(void) {}
+#endif
+
+#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
+static struct da903x_subdev_info saar_da9034_subdevs[] = {
+ [0] = {
+ .name = "da903x-backlight",
+ .id = DA9034_ID_WLED,
+ },
+};
+
+static struct da903x_platform_data saar_da9034_info = {
+ .num_subdevs = ARRAY_SIZE(saar_da9034_subdevs),
+ .subdevs = saar_da9034_subdevs,
+};
+
+static struct i2c_board_info saar_i2c_info[] = {
+ [0] = {
+ .type = "da9034",
+ .addr = 0x34,
+ .platform_data = &saar_da9034_info,
+ .irq = gpio_to_irq(mfp_to_gpio(MFP_PIN_GPIO83)),
+ },
+};
+
+static void __init saar_init_i2c(void)
+{
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info));
+}
+#else
+static inline void saar_init_i2c(void) {}
+#endif
static void __init saar_init(void)
{
/* initialize MFP configurations */
pxa3xx_mfp_config(ARRAY_AND_SIZE(saar_mfp_cfg));
platform_device_register(&smc91x_device);
+
+ saar_init_i2c();
+ saar_init_lcd();
}
MACHINE_START(SAAR, "PXA930 Handheld Platform (aka SAAR)")
diff --git a/arch/arm/mach-pxa/smemc.c b/arch/arm/mach-pxa/smemc.c
index ad346addc028..d6f6904132a6 100644
--- a/arch/arm/mach-pxa/smemc.c
+++ b/arch/arm/mach-pxa/smemc.c
@@ -8,6 +8,8 @@
#include <linux/io.h>
#include <linux/sysdev.h>
+#include <mach/hardware.h>
+
#define SMEMC_PHYS_BASE (0x4A000000)
#define SMEMC_PHYS_SIZE (0x90)
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c
index 3be76ee2bdbf..7299d87a1cb3 100644
--- a/arch/arm/mach-pxa/spitz.c
+++ b/arch/arm/mach-pxa/spitz.c
@@ -22,6 +22,7 @@
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/mmc/host.h>
+#include <linux/mtd/physmap.h>
#include <linux/pm.h>
#include <linux/backlight.h>
#include <linux/io.h>
@@ -122,6 +123,10 @@ static unsigned long spitz_pin_config[] __initdata = {
GPIO105_GPIO, /* SPITZ_GPIO_CF_IRQ */
GPIO106_GPIO, /* SPITZ_GPIO_CF2_IRQ */
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
+
GPIO1_GPIO | WAKEUP_ON_EDGE_RISE,
};
@@ -609,10 +614,41 @@ static struct pxafb_mach_info spitz_pxafb_info = {
};
+static struct mtd_partition sharpsl_rom_parts[] = {
+ {
+ .name ="Boot PROM Filesystem",
+ .offset = 0x00140000,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data sharpsl_rom_data = {
+ .width = 2,
+ .nr_parts = ARRAY_SIZE(sharpsl_rom_parts),
+ .parts = sharpsl_rom_parts,
+};
+
+static struct resource sharpsl_rom_resources[] = {
+ {
+ .start = 0x00000000,
+ .end = 0x007fffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sharpsl_rom_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = sharpsl_rom_resources,
+ .num_resources = ARRAY_SIZE(sharpsl_rom_resources),
+ .dev.platform_data = &sharpsl_rom_data,
+};
+
static struct platform_device *devices[] __initdata = {
&spitzscoop_device,
&spitzkbd_device,
&spitzled_device,
+ &sharpsl_rom_device,
};
static void spitz_poweroff(void)
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c
index 2c31ec725688..6f42004db3ed 100644
--- a/arch/arm/mach-pxa/ssp.c
+++ b/arch/arm/mach-pxa/ssp.c
@@ -356,7 +356,7 @@ static int __devinit ssp_probe(struct platform_device *pdev, int type)
}
ssp->pdev = pdev;
- ssp->clk = clk_get(&pdev->dev, "SSPCLK");
+ ssp->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(ssp->clk)) {
ret = PTR_ERR(ssp->clk);
goto err_free;
diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c
index 589d32b4fc46..58ef08a5224b 100644
--- a/arch/arm/mach-pxa/tavorevb.c
+++ b/arch/arm/mach-pxa/tavorevb.c
@@ -18,12 +18,15 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/smc91x.h>
+#include <linux/pwm_backlight.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/hardware.h>
#include <mach/pxa3xx-regs.h>
#include <mach/mfp-pxa930.h>
+#include <mach/pxafb.h>
+#include <mach/pxa27x_keypad.h>
#include "devices.h"
#include "generic.h"
@@ -33,6 +36,45 @@ static mfp_cfg_t tavorevb_mfp_cfg[] __initdata = {
/* Ethernet */
DF_nCS1_nCS3,
GPIO47_GPIO,
+
+ /* LCD */
+ GPIO23_LCD_DD0,
+ GPIO24_LCD_DD1,
+ GPIO25_LCD_DD2,
+ GPIO26_LCD_DD3,
+ GPIO27_LCD_DD4,
+ GPIO28_LCD_DD5,
+ GPIO29_LCD_DD6,
+ GPIO44_LCD_DD7,
+ GPIO21_LCD_CS,
+ GPIO22_LCD_CS2,
+
+ GPIO17_LCD_FCLK_RD,
+ GPIO18_LCD_LCLK_A0,
+ GPIO19_LCD_PCLK_WR,
+
+ /* LCD Backlight */
+ GPIO43_PWM3, /* primary backlight */
+ GPIO32_PWM0, /* secondary backlight */
+
+ /* Keypad */
+ GPIO0_KP_MKIN_0,
+ GPIO2_KP_MKIN_1,
+ GPIO4_KP_MKIN_2,
+ GPIO6_KP_MKIN_3,
+ GPIO8_KP_MKIN_4,
+ GPIO10_KP_MKIN_5,
+ GPIO12_KP_MKIN_6,
+ GPIO1_KP_MKOUT_0,
+ GPIO3_KP_MKOUT_1,
+ GPIO5_KP_MKOUT_2,
+ GPIO7_KP_MKOUT_3,
+ GPIO9_KP_MKOUT_4,
+ GPIO11_KP_MKOUT_5,
+ GPIO13_KP_MKOUT_6,
+
+ GPIO14_KP_DKIN_2,
+ GPIO15_KP_DKIN_3,
};
#define TAVOREVB_ETH_PHYS (0x14000000)
@@ -64,12 +106,382 @@ static struct platform_device smc91x_device = {
},
};
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
+static unsigned int tavorevb_matrix_key_map[] = {
+ /* KEY(row, col, key_code) */
+ KEY(0, 4, KEY_A), KEY(0, 5, KEY_B), KEY(0, 6, KEY_C),
+ KEY(1, 4, KEY_E), KEY(1, 5, KEY_F), KEY(1, 6, KEY_G),
+ KEY(2, 4, KEY_I), KEY(2, 5, KEY_J), KEY(2, 6, KEY_K),
+ KEY(3, 4, KEY_M), KEY(3, 5, KEY_N), KEY(3, 6, KEY_O),
+ KEY(4, 5, KEY_R), KEY(4, 6, KEY_S),
+ KEY(5, 4, KEY_U), KEY(5, 4, KEY_V), KEY(5, 6, KEY_W),
+
+ KEY(6, 4, KEY_Y), KEY(6, 5, KEY_Z),
+
+ KEY(0, 3, KEY_0), KEY(2, 0, KEY_1), KEY(2, 1, KEY_2), KEY(2, 2, KEY_3),
+ KEY(2, 3, KEY_4), KEY(1, 0, KEY_5), KEY(1, 1, KEY_6), KEY(1, 2, KEY_7),
+ KEY(1, 3, KEY_8), KEY(0, 2, KEY_9),
+
+ KEY(6, 6, KEY_SPACE),
+ KEY(0, 0, KEY_KPASTERISK), /* * */
+ KEY(0, 1, KEY_KPDOT), /* # */
+
+ KEY(4, 1, KEY_UP),
+ KEY(4, 3, KEY_DOWN),
+ KEY(4, 0, KEY_LEFT),
+ KEY(4, 2, KEY_RIGHT),
+ KEY(6, 0, KEY_HOME),
+ KEY(3, 2, KEY_END),
+ KEY(6, 1, KEY_DELETE),
+ KEY(5, 2, KEY_BACK),
+ KEY(6, 3, KEY_CAPSLOCK), /* KEY_LEFTSHIFT), */
+
+ KEY(4, 4, KEY_ENTER), /* scroll push */
+ KEY(6, 2, KEY_ENTER), /* keypad action */
+
+ KEY(3, 1, KEY_SEND),
+ KEY(5, 3, KEY_RECORD),
+ KEY(5, 0, KEY_VOLUMEUP),
+ KEY(5, 1, KEY_VOLUMEDOWN),
+
+ KEY(3, 0, KEY_F22), /* soft1 */
+ KEY(3, 3, KEY_F23), /* soft2 */
+};
+
+static struct pxa27x_keypad_platform_data tavorevb_keypad_info = {
+ .matrix_key_rows = 7,
+ .matrix_key_cols = 7,
+ .matrix_key_map = tavorevb_matrix_key_map,
+ .matrix_key_map_size = ARRAY_SIZE(tavorevb_matrix_key_map),
+ .debounce_interval = 30,
+};
+
+static void __init tavorevb_init_keypad(void)
+{
+ pxa_set_keypad_info(&tavorevb_keypad_info);
+}
+#else
+static inline void tavorevb_init_keypad(void) {}
+#endif /* CONFIG_KEYBOARD_PXA27x || CONFIG_KEYBOARD_PXA27x_MODULE */
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct platform_pwm_backlight_data tavorevb_backlight_data[] = {
+ [0] = {
+ /* primary backlight */
+ .pwm_id = 2,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 100000,
+ },
+ [1] = {
+ /* secondary backlight */
+ .pwm_id = 0,
+ .max_brightness = 100,
+ .dft_brightness = 100,
+ .pwm_period_ns = 100000,
+ },
+};
+
+static struct platform_device tavorevb_backlight_devices[] = {
+ [0] = {
+ .name = "pwm-backlight",
+ .id = 0,
+ .dev = {
+ .platform_data = &tavorevb_backlight_data[0],
+ },
+ },
+ [1] = {
+ .name = "pwm-backlight",
+ .id = 1,
+ .dev = {
+ .platform_data = &tavorevb_backlight_data[1],
+ },
+ },
+};
+
+static uint16_t panel_init[] = {
+ /* DSTB OUT */
+ SMART_CMD(0x00),
+ SMART_CMD_NOOP,
+ SMART_DELAY(1),
+
+ SMART_CMD(0x00),
+ SMART_CMD_NOOP,
+ SMART_DELAY(1),
+
+ SMART_CMD(0x00),
+ SMART_CMD_NOOP,
+ SMART_DELAY(1),
+
+ /* STB OUT */
+ SMART_CMD(0x00),
+ SMART_CMD(0x1D),
+ SMART_DAT(0x00),
+ SMART_DAT(0x05),
+ SMART_DELAY(1),
+
+ /* P-ON Init sequence */
+ SMART_CMD(0x00), /* OSC ON */
+ SMART_CMD(0x00),
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_CMD(0x00),
+ SMART_CMD(0x01), /* SOURCE DRIVER SHIFT DIRECTION and display RAM setting */
+ SMART_DAT(0x01),
+ SMART_DAT(0x27),
+ SMART_CMD(0x00),
+ SMART_CMD(0x02), /* LINE INV */
+ SMART_DAT(0x02),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x03), /* IF mode(1) */
+ SMART_DAT(0x01), /* 8bit smart mode(8-8),high speed write mode */
+ SMART_DAT(0x30),
+ SMART_CMD(0x07),
+ SMART_CMD(0x00), /* RAM Write Mode */
+ SMART_DAT(0x00),
+ SMART_DAT(0x03),
+ SMART_CMD(0x00),
+
+ /* DISPLAY Setting, 262K, fixed(NO scroll), no split screen */
+ SMART_CMD(0x07),
+ SMART_DAT(0x40), /* 16/18/19 BPP */
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x08), /* BP, FP Seting, BP=2H, FP=3H */
+ SMART_DAT(0x03),
+ SMART_DAT(0x02),
+ SMART_CMD(0x00),
+ SMART_CMD(0x0C), /* IF mode(2), using internal clock & MPU */
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x0D), /* Frame setting, 1Min. Frequence, 16CLK */
+ SMART_DAT(0x00),
+ SMART_DAT(0x10),
+ SMART_CMD(0x00),
+ SMART_CMD(0x12), /* Timing(1),ASW W=4CLK, ASW ST=1CLK */
+ SMART_DAT(0x03),
+ SMART_DAT(0x02),
+ SMART_CMD(0x00),
+ SMART_CMD(0x13), /* Timing(2),OEV ST=0.5CLK, OEV ED=1CLK */
+ SMART_DAT(0x01),
+ SMART_DAT(0x02),
+ SMART_CMD(0x00),
+ SMART_CMD(0x14), /* Timing(3), ASW HOLD=0.5CLK */
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x15), /* Timing(4), CKV ST=0CLK, CKV ED=1CLK */
+ SMART_DAT(0x20),
+ SMART_DAT(0x00),
+ SMART_CMD(0x00),
+ SMART_CMD(0x1C),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x03),
+ SMART_CMD(0x00),
+ SMART_DAT(0x04),
+ SMART_DAT(0x03),
+ SMART_CMD(0x03),
+ SMART_CMD(0x01),
+ SMART_DAT(0x03),
+ SMART_DAT(0x04),
+ SMART_CMD(0x03),
+ SMART_CMD(0x02),
+ SMART_DAT(0x04),
+ SMART_DAT(0x03),
+ SMART_CMD(0x03),
+ SMART_CMD(0x03),
+ SMART_DAT(0x03),
+ SMART_DAT(0x03),
+ SMART_CMD(0x03),
+ SMART_CMD(0x04),
+ SMART_DAT(0x01),
+ SMART_DAT(0x01),
+ SMART_CMD(0x03),
+ SMART_CMD(0x05),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x04),
+ SMART_CMD(0x02),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x04),
+ SMART_CMD(0x03),
+ SMART_DAT(0x01),
+ SMART_DAT(0x3F),
+ SMART_DELAY(0),
+
+ /* DISP RAM setting: 240*320 */
+ SMART_CMD(0x04), /* HADDR, START 0 */
+ SMART_CMD(0x06),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00), /* x1,3 */
+ SMART_CMD(0x04), /* HADDR, END 4 */
+ SMART_CMD(0x07),
+ SMART_DAT(0x00),
+ SMART_DAT(0xEF), /* x2, 7 */
+ SMART_CMD(0x04), /* VADDR, START 8 */
+ SMART_CMD(0x08),
+ SMART_DAT(0x00), /* y1, 10 */
+ SMART_DAT(0x00), /* y1, 11 */
+ SMART_CMD(0x04), /* VADDR, END 12 */
+ SMART_CMD(0x09),
+ SMART_DAT(0x01), /* y2, 14 */
+ SMART_DAT(0x3F), /* y2, 15 */
+ SMART_CMD(0x02), /* RAM ADDR SETTING 16 */
+ SMART_CMD(0x00),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00), /* x1, 19 */
+ SMART_CMD(0x02), /* RAM ADDR SETTING 20 */
+ SMART_CMD(0x01),
+ SMART_DAT(0x00), /* y1, 22 */
+ SMART_DAT(0x00), /* y1, 23 */
+};
+
+static uint16_t panel_on[] = {
+ /* Power-IC ON */
+ SMART_CMD(0x01),
+ SMART_CMD(0x02),
+ SMART_DAT(0x07),
+ SMART_DAT(0x7D),
+ SMART_CMD(0x01),
+ SMART_CMD(0x03),
+ SMART_DAT(0x00),
+ SMART_DAT(0x05),
+ SMART_CMD(0x01),
+ SMART_CMD(0x04),
+ SMART_DAT(0x00),
+ SMART_DAT(0x00),
+ SMART_CMD(0x01),
+ SMART_CMD(0x05),
+ SMART_DAT(0x00),
+ SMART_DAT(0x15),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xC0),
+ SMART_DAT(0x10),
+ SMART_DELAY(30),
+
+ /* DISP ON */
+ SMART_CMD(0x01),
+ SMART_CMD(0x01),
+ SMART_DAT(0x00),
+ SMART_DAT(0x01),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xFF),
+ SMART_DAT(0xFE),
+ SMART_DELAY(150),
+};
+
+static uint16_t panel_off[] = {
+ SMART_CMD(0x00),
+ SMART_CMD(0x1E),
+ SMART_DAT(0x00),
+ SMART_DAT(0x0A),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xFF),
+ SMART_DAT(0xEE),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xF8),
+ SMART_DAT(0x12),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xE8),
+ SMART_DAT(0x11),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0xC0),
+ SMART_DAT(0x11),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0x40),
+ SMART_DAT(0x11),
+ SMART_CMD(0x01),
+ SMART_CMD(0x00),
+ SMART_DAT(0x00),
+ SMART_DAT(0x10),
+};
+
+static uint16_t update_framedata[] = {
+ /* write ram */
+ SMART_CMD(0x02),
+ SMART_CMD(0x02),
+
+ /* write frame data */
+ SMART_CMD_WRITE_FRAME,
+};
+
+static void ltm020d550_lcd_power(int on, struct fb_var_screeninfo *var)
+{
+ struct fb_info *info = container_of(var, struct fb_info, var);
+
+ if (on) {
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_init));
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_on));
+ } else {
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_off));
+ }
+
+ if (pxafb_smart_flush(info))
+ pr_err("%s: timed out\n", __func__);
+}
+
+static void ltm020d550_update(struct fb_info *info)
+{
+ pxafb_smart_queue(info, ARRAY_AND_SIZE(update_framedata));
+ pxafb_smart_flush(info);
+}
+
+static struct pxafb_mode_info toshiba_ltm020d550_modes[] = {
+ [0] = {
+ .xres = 240,
+ .yres = 320,
+ .bpp = 16,
+ .a0csrd_set_hld = 30,
+ .a0cswr_set_hld = 30,
+ .wr_pulse_width = 30,
+ .rd_pulse_width = 170,
+ .op_hold_time = 30,
+ .cmd_inh_time = 60,
+
+ /* L_LCLK_A0 and L_LCLK_RD active low */
+ .sync = FB_SYNC_HOR_HIGH_ACT |
+ FB_SYNC_VERT_HIGH_ACT,
+ },
+};
+
+static struct pxafb_mach_info tavorevb_lcd_info = {
+ .modes = toshiba_ltm020d550_modes,
+ .num_modes = 1,
+ .lcd_conn = LCD_SMART_PANEL_8BPP | LCD_PCLK_EDGE_FALL,
+ .pxafb_lcd_power = ltm020d550_lcd_power,
+ .smart_update = ltm020d550_update,
+};
+
+static void __init tavorevb_init_lcd(void)
+{
+ platform_device_register(&tavorevb_backlight_devices[0]);
+ platform_device_register(&tavorevb_backlight_devices[1]);
+ set_pxa_fb_info(&tavorevb_lcd_info);
+}
+#else
+static inline void tavorevb_init_lcd(void) {}
+#endif /* CONFIG_FB_PXA || CONFIG_FB_PXA_MODULE */
+
static void __init tavorevb_init(void)
{
/* initialize MFP configurations */
pxa3xx_mfp_config(ARRAY_AND_SIZE(tavorevb_mfp_cfg));
platform_device_register(&smc91x_device);
+
+ tavorevb_init_lcd();
+ tavorevb_init_keypad();
}
MACHINE_START(TAVOREVB, "PXA930 Evaluation Board (aka TavorEVB)")
diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c
index bf3c9a4aad50..95656a72268d 100644
--- a/arch/arm/mach-pxa/time.c
+++ b/arch/arm/mach-pxa/time.c
@@ -22,8 +22,8 @@
#include <asm/div64.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
-#include <asm/mach-types.h>
/*
* This is PXA's sched_clock implementation. This has a resolution
@@ -149,18 +149,11 @@ static struct irqaction pxa_ost0_irq = {
static void __init pxa_timer_init(void)
{
- unsigned long clock_tick_rate;
+ unsigned long clock_tick_rate = get_clock_tick_rate();
OIER = 0;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
- if (cpu_is_pxa25x())
- clock_tick_rate = 3686400;
- else if (machine_is_mainstone())
- clock_tick_rate = 3249600;
- else
- clock_tick_rate = 3250000;
-
set_oscr2ns_scale(clock_tick_rate);
ckevt_pxa_osmr0.mult =
diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
index 224897a67d15..3332e5d0356c 100644
--- a/arch/arm/mach-pxa/tosa.c
+++ b/arch/arm/mach-pxa/tosa.c
@@ -25,6 +25,7 @@
#include <linux/mfd/tmio.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
#include <linux/pm.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
@@ -733,6 +734,45 @@ static void tosa_tc6393xb_teardown(struct platform_device *dev)
gpio_free(TOSA_GPIO_CARD_VCC_ON);
}
+#ifdef CONFIG_MFD_TC6393XB
+static struct fb_videomode tosa_tc6393xb_lcd_mode[] = {
+ {
+ .xres = 480,
+ .yres = 640,
+ .pixclock = 0x002cdf00,/* PLL divisor */
+ .left_margin = 0x004c,
+ .right_margin = 0x005b,
+ .upper_margin = 0x0001,
+ .lower_margin = 0x000d,
+ .hsync_len = 0x0002,
+ .vsync_len = 0x0001,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },{
+ .xres = 240,
+ .yres = 320,
+ .pixclock = 0x00e7f203,/* PLL divisor */
+ .left_margin = 0x0024,
+ .right_margin = 0x002f,
+ .upper_margin = 0x0001,
+ .lower_margin = 0x000d,
+ .hsync_len = 0x0002,
+ .vsync_len = 0x0001,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ }
+};
+
+static struct tmio_fb_data tosa_tc6393xb_fb_config = {
+ .lcd_set_power = tc6393xb_lcd_set_power,
+ .lcd_mode = tc6393xb_lcd_mode,
+ .num_modes = ARRAY_SIZE(tosa_tc6393xb_lcd_mode),
+ .modes = &tosa_tc6393xb_lcd_mode[0],
+ .height = 82,
+ .width = 60,
+};
+#endif
+
static struct tc6393xb_platform_data tosa_tc6393xb_data = {
.scr_pll2cr = 0x0cc1,
.scr_gper = 0x3300,
@@ -748,6 +788,9 @@ static struct tc6393xb_platform_data tosa_tc6393xb_data = {
.resume = tosa_tc6393xb_resume,
.nand_data = &tosa_tc6393xb_nand_config,
+#ifdef CONFIG_MFD_TC6393XB
+ .fb_data = &tosa_tc6393xb_fb_config,
+#endif
.resume_restore = 1,
};
@@ -789,6 +832,36 @@ static struct spi_board_info spi_board_info[] __initdata = {
},
};
+static struct mtd_partition sharpsl_rom_parts[] = {
+ {
+ .name ="Boot PROM Filesystem",
+ .offset = 0x00160000,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data sharpsl_rom_data = {
+ .width = 2,
+ .nr_parts = ARRAY_SIZE(sharpsl_rom_parts),
+ .parts = sharpsl_rom_parts,
+};
+
+static struct resource sharpsl_rom_resources[] = {
+ {
+ .start = 0x00000000,
+ .end = 0x007fffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sharpsl_rom_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = sharpsl_rom_resources,
+ .num_resources = ARRAY_SIZE(sharpsl_rom_resources),
+ .dev.platform_data = &sharpsl_rom_data,
+};
+
static struct platform_device *devices[] __initdata = {
&tosascoop_device,
&tosascoop_jc_device,
@@ -798,6 +871,7 @@ static struct platform_device *devices[] __initdata = {
&tosa_gpio_keys_device,
&tosaled_device,
&tosa_bt_device,
+ &sharpsl_rom_device,
};
static void tosa_poweroff(void)
diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c
index 813804433466..218d2001f1df 100644
--- a/arch/arm/mach-pxa/zylonite.c
+++ b/arch/arm/mach-pxa/zylonite.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/pwm_backlight.h>
#include <linux/smc91x.h>
@@ -25,7 +26,6 @@
#include <asm/mach/arch.h>
#include <mach/hardware.h>
#include <mach/audio.h>
-#include <mach/gpio.h>
#include <mach/pxafb.h>
#include <mach/zylonite.h>
#include <mach/mmc.h>
diff --git a/arch/arm/mach-pxa/zylonite_pxa320.c b/arch/arm/mach-pxa/zylonite_pxa320.c
index 0f244744daae..28e4e623780b 100644
--- a/arch/arm/mach-pxa/zylonite_pxa320.c
+++ b/arch/arm/mach-pxa/zylonite_pxa320.c
@@ -16,8 +16,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/gpio.h>
-#include <mach/gpio.h>
#include <mach/mfp-pxa320.h>
#include <mach/zylonite.h>
diff --git a/arch/arm/mach-realview/Kconfig b/arch/arm/mach-realview/Kconfig
index 5ccde7cf39e8..ad911854eb4c 100644
--- a/arch/arm/mach-realview/Kconfig
+++ b/arch/arm/mach-realview/Kconfig
@@ -7,9 +7,17 @@ config MACH_REALVIEW_EB
help
Include support for the ARM(R) RealView Emulation Baseboard platform.
+config REALVIEW_EB_A9MP
+ bool "Support Multicore Cortex-A9"
+ depends on MACH_REALVIEW_EB
+ select CPU_V7
+ help
+ Enable support for the Cortex-A9MPCore tile on the Realview platform.
+
config REALVIEW_EB_ARM11MP
bool "Support ARM11MPCore tile"
depends on MACH_REALVIEW_EB
+ select CPU_V6
help
Enable support for the ARM11MPCore tile on the Realview platform.
@@ -25,6 +33,7 @@ config REALVIEW_EB_ARM11MP_REVB
config MACH_REALVIEW_PB11MP
bool "Support RealView/PB11MPCore platform"
+ select CPU_V6
select ARM_GIC
help
Include support for the ARM(R) RealView MPCore Platform Baseboard.
@@ -33,8 +42,29 @@ config MACH_REALVIEW_PB11MP
config MACH_REALVIEW_PB1176
bool "Support RealView/PB1176 platform"
+ select CPU_V6
select ARM_GIC
help
Include support for the ARM(R) RealView ARM1176 Platform Baseboard.
+config MACH_REALVIEW_PBA8
+ bool "Support RealView/PB-A8 platform"
+ select CPU_V7
+ select ARM_GIC
+ help
+ Include support for the ARM(R) RealView Cortex-A8 Platform Baseboard.
+ PB-A8 is a platform with an on-board Cortex-A8 and has support for
+ PCI-E and Compact Flash.
+
+config REALVIEW_HIGH_PHYS_OFFSET
+ bool "High physical base address for the RealView platform"
+ depends on !MACH_REALVIEW_PB1176
+ default y
+ help
+ RealView boards other than PB1176 have the RAM available at
+ 0x70000000, 256MB of which being mirrored at 0x00000000. If
+ the board supports 512MB of RAM, this option allows the
+ memory to be accessed contiguously at the high physical
+ offset.
+
endmenu
diff --git a/arch/arm/mach-realview/Makefile b/arch/arm/mach-realview/Makefile
index d2ae077431dd..7bea8ffc4b59 100644
--- a/arch/arm/mach-realview/Makefile
+++ b/arch/arm/mach-realview/Makefile
@@ -6,5 +6,6 @@ obj-y := core.o clock.o
obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o
obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o
obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o
+obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-realview/Makefile.boot b/arch/arm/mach-realview/Makefile.boot
index c7e75acfe6c9..d97e003d3df4 100644
--- a/arch/arm/mach-realview/Makefile.boot
+++ b/arch/arm/mach-realview/Makefile.boot
@@ -1,4 +1,9 @@
+ifeq ($(CONFIG_REALVIEW_HIGH_PHYS_OFFSET),y)
+ zreladdr-y := 0x70008000
+params_phys-y := 0x70000100
+initrd_phys-y := 0x70800000
+else
zreladdr-y := 0x00008000
params_phys-y := 0x00000100
initrd_phys-y := 0x00800000
-
+endif
diff --git a/arch/arm/mach-realview/clock.c b/arch/arm/mach-realview/clock.c
index 3347c4236a60..a7043115de72 100644
--- a/arch/arm/mach-realview/clock.c
+++ b/arch/arm/mach-realview/clock.c
@@ -10,9 +10,11 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/string.h>
#include <linux/clk.h>
#include <linux/mutex.h>
@@ -20,32 +22,6 @@
#include "clock.h"
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- mutex_unlock(&clocks_mutex);
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
- module_put(clk->owner);
-}
-EXPORT_SYMBOL(clk_put);
-
int clk_enable(struct clk *clk)
{
return 0;
@@ -65,7 +41,9 @@ EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
- return rate;
+ struct icst307_vco vco;
+ vco = icst307_khz_to_vco(clk->params, rate / 1000);
+ return icst307_khz(clk->params, vco) * 1000;
}
EXPORT_SYMBOL(clk_round_rate);
@@ -78,57 +56,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
vco = icst307_khz_to_vco(clk->params, rate / 1000);
clk->rate = icst307_khz(clk->params, vco) * 1000;
-
- printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
- clk->name, vco.s, vco.r, vco.v);
-
clk->setvco(clk, vco);
ret = 0;
}
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
-
-/*
- * These are fixed clocks.
- */
-static struct clk kmi_clk = {
- .name = "KMIREFCLK",
- .rate = 24000000,
-};
-
-static struct clk uart_clk = {
- .name = "UARTCLK",
- .rate = 24000000,
-};
-
-static struct clk mmci_clk = {
- .name = "MCLK",
- .rate = 24000000,
-};
-
-int clk_register(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init(void)
-{
- clk_register(&kmi_clk);
- clk_register(&uart_clk);
- clk_register(&mmci_clk);
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-realview/clock.h b/arch/arm/mach-realview/clock.h
index dadba695e181..ebbb0f06b600 100644
--- a/arch/arm/mach-realview/clock.h
+++ b/arch/arm/mach-realview/clock.h
@@ -12,14 +12,8 @@ struct module;
struct icst307_params;
struct clk {
- struct list_head node;
unsigned long rate;
- struct module *owner;
- const char *name;
const struct icst307_params *params;
void *data;
void (*setvco)(struct clk *, struct icst307_vco vco);
};
-
-int clk_register(struct clk *clk);
-void clk_unregister(struct clk *clk);
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index b07cb9b7adb1..bd2aa4f16141 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -28,11 +28,14 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/io.h>
+#include <linux/smc911x.h>
+#include <asm/clkdev.h>
#include <asm/system.h>
#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/icst307.h>
@@ -49,7 +52,7 @@
#define REALVIEW_REFCOUNTER (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_24MHz_OFFSET)
-/* used by entry-macro.S */
+/* used by entry-macro.S and platsmp.c */
void __iomem *gic_cpu_base_addr;
/*
@@ -124,6 +127,29 @@ int realview_flash_register(struct resource *res, u32 num)
return platform_device_register(&realview_flash_device);
}
+static struct smc911x_platdata realview_smc911x_platdata = {
+ .flags = SMC911X_USE_32BIT,
+ .irq_flags = IRQF_SHARED,
+ .irq_polarity = 1,
+};
+
+static struct platform_device realview_eth_device = {
+ .name = "smc911x",
+ .id = 0,
+ .num_resources = 2,
+};
+
+int realview_eth_register(const char *name, struct resource *res)
+{
+ if (name)
+ realview_eth_device.name = name;
+ realview_eth_device.resource = res;
+ if (strcmp(realview_eth_device.name, "smc911x") == 0)
+ realview_eth_device.dev.platform_data = &realview_smc911x_platdata;
+
+ return platform_device_register(&realview_eth_device);
+}
+
static struct resource realview_i2c_resource = {
.start = REALVIEW_I2C_BASE,
.end = REALVIEW_I2C_BASE + SZ_4K - 1,
@@ -177,9 +203,14 @@ static const struct icst307_params realview_oscvco_params = {
static void realview_oscvco_set(struct clk *clk, struct icst307_vco vco)
{
void __iomem *sys_lock = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LOCK_OFFSET;
- void __iomem *sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC4_OFFSET;
+ void __iomem *sys_osc;
u32 val;
+ if (machine_is_realview_pb1176())
+ sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC0_OFFSET;
+ else
+ sys_osc = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_OSC4_OFFSET;
+
val = readl(sys_osc) & ~0x7ffff;
val |= vco.v | (vco.r << 9) | (vco.s << 16);
@@ -188,13 +219,60 @@ static void realview_oscvco_set(struct clk *clk, struct icst307_vco vco)
writel(0, sys_lock);
}
-struct clk realview_clcd_clk = {
- .name = "CLCDCLK",
+static struct clk oscvco_clk = {
.params = &realview_oscvco_params,
.setvco = realview_oscvco_set,
};
/*
+ * These are fixed clocks.
+ */
+static struct clk ref24_clk = {
+ .rate = 24000000,
+};
+
+static struct clk_lookup lookups[] = {
+ { /* UART0 */
+ .dev_id = "dev:f1",
+ .clk = &ref24_clk,
+ }, { /* UART1 */
+ .dev_id = "dev:f2",
+ .clk = &ref24_clk,
+ }, { /* UART2 */
+ .dev_id = "dev:f3",
+ .clk = &ref24_clk,
+ }, { /* UART3 */
+ .dev_id = "fpga:09",
+ .clk = &ref24_clk,
+ }, { /* KMI0 */
+ .dev_id = "fpga:06",
+ .clk = &ref24_clk,
+ }, { /* KMI1 */
+ .dev_id = "fpga:07",
+ .clk = &ref24_clk,
+ }, { /* MMC0 */
+ .dev_id = "fpga:05",
+ .clk = &ref24_clk,
+ }, { /* EB:CLCD */
+ .dev_id = "dev:20",
+ .clk = &oscvco_clk,
+ }, { /* PB:CLCD */
+ .dev_id = "issp:20",
+ .clk = &oscvco_clk,
+ }
+};
+
+static int __init clk_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
+ return 0;
+}
+arch_initcall(clk_init);
+
+/*
* CLCD support.
*/
#define SYS_CLCD_NLCDIOON (1 << 2)
@@ -226,7 +304,30 @@ static struct clcd_panel vga = {
.width = -1,
.height = -1,
.tim2 = TIM2_BCD | TIM2_IPC,
- .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
+ .bpp = 16,
+};
+
+static struct clcd_panel xvga = {
+ .mode = {
+ .name = "XVGA",
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 768,
+ .pixclock = 15748,
+ .left_margin = 152,
+ .right_margin = 48,
+ .upper_margin = 23,
+ .lower_margin = 3,
+ .hsync_len = 104,
+ .vsync_len = 4,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+ .width = -1,
+ .height = -1,
+ .tim2 = TIM2_BCD | TIM2_IPC,
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
.bpp = 16,
};
@@ -249,7 +350,7 @@ static struct clcd_panel sanyo_3_8_in = {
.width = -1,
.height = -1,
.tim2 = TIM2_BCD,
- .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
.bpp = 16,
};
@@ -272,7 +373,7 @@ static struct clcd_panel sanyo_2_5_in = {
.width = -1,
.height = -1,
.tim2 = TIM2_IVS | TIM2_IHS | TIM2_IPC,
- .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
.bpp = 16,
};
@@ -295,7 +396,7 @@ static struct clcd_panel epson_2_2_in = {
.width = -1,
.height = -1,
.tim2 = TIM2_BCD | TIM2_IPC,
- .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1),
+ .cntl = CNTL_LCDTFT | CNTL_BGR | CNTL_LCDVCOMP(1),
.bpp = 16,
};
@@ -308,9 +409,15 @@ static struct clcd_panel epson_2_2_in = {
static struct clcd_panel *realview_clcd_panel(void)
{
void __iomem *sys_clcd = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_CLCD_OFFSET;
- struct clcd_panel *panel = &vga;
+ struct clcd_panel *vga_panel;
+ struct clcd_panel *panel;
u32 val;
+ if (machine_is_realview_eb())
+ vga_panel = &vga;
+ else
+ vga_panel = &xvga;
+
val = readl(sys_clcd) & SYS_CLCD_ID_MASK;
if (val == SYS_CLCD_ID_SANYO_3_8)
panel = &sanyo_3_8_in;
@@ -319,11 +426,11 @@ static struct clcd_panel *realview_clcd_panel(void)
else if (val == SYS_CLCD_ID_EPSON_2_2)
panel = &epson_2_2_in;
else if (val == SYS_CLCD_ID_VGA)
- panel = &vga;
+ panel = vga_panel;
else {
printk(KERN_ERR "CLCD: unknown LCD panel ID 0x%08x, using VGA\n",
val);
- panel = &vga;
+ panel = vga_panel;
}
return panel;
@@ -358,12 +465,18 @@ static void realview_clcd_enable(struct clcd_fb *fb)
writel(val, sys_clcd);
}
-static unsigned long framesize = SZ_1M;
-
static int realview_clcd_setup(struct clcd_fb *fb)
{
+ unsigned long framesize;
dma_addr_t dma;
+ if (machine_is_realview_eb())
+ /* VGA, 16bpp */
+ framesize = 640 * 480 * 2;
+ else
+ /* XVGA, 16bpp */
+ framesize = 1024 * 768 * 2;
+
fb->panel = realview_clcd_panel();
fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
@@ -588,7 +701,7 @@ void __init realview_timer_init(unsigned int timer_irq)
* The dummy clock device has to be registered before the main device
* so that the latter will broadcast the clock events
*/
- local_timer_setup(smp_processor_id());
+ local_timer_setup();
#endif
/*
diff --git a/arch/arm/mach-realview/core.h b/arch/arm/mach-realview/core.h
index 3cea92c70d8f..63be2abdc19c 100644
--- a/arch/arm/mach-realview/core.h
+++ b/arch/arm/mach-realview/core.h
@@ -48,12 +48,10 @@ extern struct platform_device realview_flash_device;
extern struct platform_device realview_i2c_device;
extern struct mmc_platform_data realview_mmc0_plat_data;
extern struct mmc_platform_data realview_mmc1_plat_data;
-extern struct clk realview_clcd_clk;
extern struct clcd_board clcd_plat_data;
extern void __iomem *gic_cpu_base_addr;
#ifdef CONFIG_LOCAL_TIMERS
-extern void __iomem *twd_base_addr;
-extern unsigned int twd_size;
+extern void __iomem *twd_base;
#endif
extern void __iomem *timer0_va_base;
extern void __iomem *timer1_va_base;
@@ -63,5 +61,6 @@ 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);
#endif
diff --git a/arch/arm/mach-realview/hotplug.c b/arch/arm/mach-realview/hotplug.c
index 09748cbcd10e..be048e3e8799 100644
--- a/arch/arm/mach-realview/hotplug.c
+++ b/arch/arm/mach-realview/hotplug.c
@@ -13,6 +13,8 @@
#include <linux/smp.h>
#include <linux/completion.h>
+#include <asm/cacheflush.h>
+
extern volatile int pen_release;
static DECLARE_COMPLETION(cpu_killed);
@@ -21,7 +23,8 @@ static inline void cpu_enter_lowpower(void)
{
unsigned int v;
- asm volatile( "mcr p15, 0, %1, c7, c14, 0\n"
+ flush_cache_all();
+ asm volatile(
" mcr p15, 0, %1, c7, c5, 0\n"
" mcr p15, 0, %1, c7, c10, 4\n"
/*
diff --git a/arch/arm/mach-realview/include/mach/board-eb.h b/arch/arm/mach-realview/include/mach/board-eb.h
index 8d699fd324d0..268d7701fa9b 100644
--- a/arch/arm/mach-realview/include/mach/board-eb.h
+++ b/arch/arm/mach-realview/include/mach/board-eb.h
@@ -49,16 +49,14 @@
#ifdef CONFIG_REALVIEW_EB_ARM11MP_REVB
#define REALVIEW_EB11MP_SCU_BASE 0x10100000 /* SCU registers */
#define REALVIEW_EB11MP_GIC_CPU_BASE 0x10100100 /* Generic interrupt controller CPU interface */
-#define REALVIEW_EB11MP_TWD_BASE 0x10100700
-#define REALVIEW_EB11MP_TWD_SIZE 0x00000100
+#define REALVIEW_EB11MP_TWD_BASE 0x10100600
#define REALVIEW_EB11MP_GIC_DIST_BASE 0x10101000 /* Generic interrupt controller distributor */
#define REALVIEW_EB11MP_L220_BASE 0x10102000 /* L220 registers */
#define REALVIEW_EB11MP_SYS_PLD_CTRL1 0xD8 /* Register offset for MPCore sysctl */
#else
#define REALVIEW_EB11MP_SCU_BASE 0x1F000000 /* SCU registers */
#define REALVIEW_EB11MP_GIC_CPU_BASE 0x1F000100 /* Generic interrupt controller CPU interface */
-#define REALVIEW_EB11MP_TWD_BASE 0x1F000700
-#define REALVIEW_EB11MP_TWD_SIZE 0x00000100
+#define REALVIEW_EB11MP_TWD_BASE 0x1F000600
#define REALVIEW_EB11MP_GIC_DIST_BASE 0x1F001000 /* Generic interrupt controller distributor */
#define REALVIEW_EB11MP_L220_BASE 0x1F002000 /* L220 registers */
#define REALVIEW_EB11MP_SYS_PLD_CTRL1 0x74 /* Register offset for MPCore sysctl */
@@ -163,7 +161,7 @@
#define NR_IRQS NR_IRQS_EB
#endif
-#if defined(CONFIG_REALVIEW_EB_ARM11MP) \
+#if defined(CONFIG_REALVIEW_EB_ARM11MP) || defined(CONFIG_REALVIEW_EB_A9MP) \
&& (!defined(MAX_GIC_NR) || (MAX_GIC_NR < NR_GIC_EB11MP))
#undef MAX_GIC_NR
#define MAX_GIC_NR NR_GIC_EB11MP
@@ -177,6 +175,7 @@
#define REALVIEW_EB_PROC_ARM9 0x02000000
#define REALVIEW_EB_PROC_ARM11 0x04000000
#define REALVIEW_EB_PROC_ARM11MP 0x06000000
+#define REALVIEW_EB_PROC_A9MP 0x0C000000
#define check_eb_proc(proc_type) \
((readl(__io_address(REALVIEW_SYS_PROCID)) & REALVIEW_EB_PROC_MASK) \
@@ -188,4 +187,13 @@
#define core_tile_eb11mp() 0
#endif
+#ifdef CONFIG_REALVIEW_EB_A9MP
+#define core_tile_a9mp() check_eb_proc(REALVIEW_EB_PROC_A9MP)
+#else
+#define core_tile_a9mp() 0
+#endif
+
+#define machine_is_realview_eb_mp() \
+ (machine_is_realview_eb() && (core_tile_eb11mp() || core_tile_a9mp()))
+
#endif /* __ASM_ARCH_BOARD_EB_H */
diff --git a/arch/arm/mach-realview/include/mach/board-pb11mp.h b/arch/arm/mach-realview/include/mach/board-pb11mp.h
index ecd80e58631e..53ea0e7a1267 100644
--- a/arch/arm/mach-realview/include/mach/board-pb11mp.h
+++ b/arch/arm/mach-realview/include/mach/board-pb11mp.h
@@ -77,8 +77,7 @@
*/
#define REALVIEW_TC11MP_SCU_BASE 0x1F000000 /* IRQ, Test chip */
#define REALVIEW_TC11MP_GIC_CPU_BASE 0x1F000100 /* Test chip interrupt controller CPU interface */
-#define REALVIEW_TC11MP_TWD_BASE 0x1F000700
-#define REALVIEW_TC11MP_TWD_SIZE 0x00000100
+#define REALVIEW_TC11MP_TWD_BASE 0x1F000600
#define REALVIEW_TC11MP_GIC_DIST_BASE 0x1F001000 /* Test chip interrupt controller distributor */
#define REALVIEW_TC11MP_L220_BASE 0x1F002000 /* L220 registers */
diff --git a/arch/arm/mach-realview/include/mach/board-pba8.h b/arch/arm/mach-realview/include/mach/board-pba8.h
new file mode 100644
index 000000000000..c8bed8f58bab
--- /dev/null
+++ b/arch/arm/mach-realview/include/mach/board-pba8.h
@@ -0,0 +1,152 @@
+/*
+ * include/asm-arm/arch-realview/board-pba8.h
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef __ASM_ARCH_BOARD_PBA8_H
+#define __ASM_ARCH_BOARD_PBA8_H
+
+#include <mach/platform.h>
+
+/*
+ * Peripheral addresses
+ */
+#define REALVIEW_PBA8_UART0_BASE 0x10009000 /* UART 0 */
+#define REALVIEW_PBA8_UART1_BASE 0x1000A000 /* UART 1 */
+#define REALVIEW_PBA8_UART2_BASE 0x1000B000 /* UART 2 */
+#define REALVIEW_PBA8_UART3_BASE 0x1000C000 /* UART 3 */
+#define REALVIEW_PBA8_SSP_BASE 0x1000D000 /* Synchronous Serial Port */
+#define REALVIEW_PBA8_WATCHDOG0_BASE 0x1000F000 /* Watchdog 0 */
+#define REALVIEW_PBA8_WATCHDOG_BASE 0x10010000 /* watchdog interface */
+#define REALVIEW_PBA8_TIMER0_1_BASE 0x10011000 /* Timer 0 and 1 */
+#define REALVIEW_PBA8_TIMER2_3_BASE 0x10012000 /* Timer 2 and 3 */
+#define REALVIEW_PBA8_GPIO0_BASE 0x10013000 /* GPIO port 0 */
+#define REALVIEW_PBA8_RTC_BASE 0x10017000 /* Real Time Clock */
+#define REALVIEW_PBA8_TIMER4_5_BASE 0x10018000 /* Timer 4/5 */
+#define REALVIEW_PBA8_TIMER6_7_BASE 0x10019000 /* Timer 6/7 */
+#define REALVIEW_PBA8_SCTL_BASE 0x1001A000 /* System Controller */
+#define REALVIEW_PBA8_CLCD_BASE 0x10020000 /* CLCD */
+#define REALVIEW_PBA8_ONB_SRAM_BASE 0x10060000 /* On-board SRAM */
+#define REALVIEW_PBA8_DMC_BASE 0x100E0000 /* DMC configuration */
+#define REALVIEW_PBA8_SMC_BASE 0x100E1000 /* SMC configuration */
+#define REALVIEW_PBA8_CAN_BASE 0x100E2000 /* CAN bus */
+#define REALVIEW_PBA8_CF_BASE 0x18000000 /* Compact flash */
+#define REALVIEW_PBA8_CF_MEM_BASE 0x18003000 /* SMC for Compact flash */
+#define REALVIEW_PBA8_GIC_CPU_BASE 0x1E000000 /* Generic interrupt controller CPU interface */
+#define REALVIEW_PBA8_FLASH0_BASE 0x40000000
+#define REALVIEW_PBA8_FLASH0_SIZE SZ_64M
+#define REALVIEW_PBA8_FLASH1_BASE 0x44000000
+#define REALVIEW_PBA8_FLASH1_SIZE SZ_64M
+#define REALVIEW_PBA8_ETH_BASE 0x4E000000 /* Ethernet */
+#define REALVIEW_PBA8_USB_BASE 0x4F000000 /* USB */
+#define REALVIEW_PBA8_GIC_DIST_BASE 0x1E001000 /* Generic interrupt controller distributor */
+#define REALVIEW_PBA8_LT_BASE 0xC0000000 /* Logic Tile expansion */
+#define REALVIEW_PBA8_SDRAM6_BASE 0x70000000 /* SDRAM bank 6 256MB */
+#define REALVIEW_PBA8_SDRAM7_BASE 0x80000000 /* SDRAM bank 7 256MB */
+
+#define REALVIEW_PBA8_SYS_PLD_CTRL1 0x74
+
+/*
+ * PBA8 PCI regions
+ */
+#define REALVIEW_PBA8_PCI_BASE 0x90040000 /* PCI-X Unit base */
+#define REALVIEW_PBA8_PCI_IO_BASE 0x90050000 /* IO Region on AHB */
+#define REALVIEW_PBA8_PCI_MEM_BASE 0xA0000000 /* MEM Region on AHB */
+
+#define REALVIEW_PBA8_PCI_BASE_SIZE 0x10000 /* 16 Kb */
+#define REALVIEW_PBA8_PCI_IO_SIZE 0x1000 /* 4 Kb */
+#define REALVIEW_PBA8_PCI_MEM_SIZE 0x20000000 /* 512 MB */
+
+/*
+ * Irqs
+ */
+#define IRQ_PBA8_GIC_START 32
+
+/* L220
+#define IRQ_PBA8_L220_EVENT (IRQ_PBA8_GIC_START + 29)
+#define IRQ_PBA8_L220_SLAVE (IRQ_PBA8_GIC_START + 30)
+#define IRQ_PBA8_L220_DECODE (IRQ_PBA8_GIC_START + 31)
+*/
+
+/*
+ * PB-A8 on-board gic irq sources
+ */
+#define IRQ_PBA8_WATCHDOG (IRQ_PBA8_GIC_START + 0) /* Watchdog timer */
+#define IRQ_PBA8_SOFT (IRQ_PBA8_GIC_START + 1) /* Software interrupt */
+#define IRQ_PBA8_COMMRx (IRQ_PBA8_GIC_START + 2) /* Debug Comm Rx interrupt */
+#define IRQ_PBA8_COMMTx (IRQ_PBA8_GIC_START + 3) /* Debug Comm Tx interrupt */
+#define IRQ_PBA8_TIMER0_1 (IRQ_PBA8_GIC_START + 4) /* Timer 0/1 (default timer) */
+#define IRQ_PBA8_TIMER2_3 (IRQ_PBA8_GIC_START + 5) /* Timer 2/3 */
+#define IRQ_PBA8_GPIO0 (IRQ_PBA8_GIC_START + 6) /* GPIO 0 */
+#define IRQ_PBA8_GPIO1 (IRQ_PBA8_GIC_START + 7) /* GPIO 1 */
+#define IRQ_PBA8_GPIO2 (IRQ_PBA8_GIC_START + 8) /* GPIO 2 */
+ /* 9 reserved */
+#define IRQ_PBA8_RTC (IRQ_PBA8_GIC_START + 10) /* Real Time Clock */
+#define IRQ_PBA8_SSP (IRQ_PBA8_GIC_START + 11) /* Synchronous Serial Port */
+#define IRQ_PBA8_UART0 (IRQ_PBA8_GIC_START + 12) /* UART 0 on development chip */
+#define IRQ_PBA8_UART1 (IRQ_PBA8_GIC_START + 13) /* UART 1 on development chip */
+#define IRQ_PBA8_UART2 (IRQ_PBA8_GIC_START + 14) /* UART 2 on development chip */
+#define IRQ_PBA8_UART3 (IRQ_PBA8_GIC_START + 15) /* UART 3 on development chip */
+#define IRQ_PBA8_SCI (IRQ_PBA8_GIC_START + 16) /* Smart Card Interface */
+#define IRQ_PBA8_MMCI0A (IRQ_PBA8_GIC_START + 17) /* Multimedia Card 0A */
+#define IRQ_PBA8_MMCI0B (IRQ_PBA8_GIC_START + 18) /* Multimedia Card 0B */
+#define IRQ_PBA8_AACI (IRQ_PBA8_GIC_START + 19) /* Audio Codec */
+#define IRQ_PBA8_KMI0 (IRQ_PBA8_GIC_START + 20) /* Keyboard/Mouse port 0 */
+#define IRQ_PBA8_KMI1 (IRQ_PBA8_GIC_START + 21) /* Keyboard/Mouse port 1 */
+#define IRQ_PBA8_CHARLCD (IRQ_PBA8_GIC_START + 22) /* Character LCD */
+#define IRQ_PBA8_CLCD (IRQ_PBA8_GIC_START + 23) /* CLCD controller */
+#define IRQ_PBA8_DMAC (IRQ_PBA8_GIC_START + 24) /* DMA controller */
+#define IRQ_PBA8_PWRFAIL (IRQ_PBA8_GIC_START + 25) /* Power failure */
+#define IRQ_PBA8_PISMO (IRQ_PBA8_GIC_START + 26) /* PISMO interface */
+#define IRQ_PBA8_DoC (IRQ_PBA8_GIC_START + 27) /* Disk on Chip memory controller */
+#define IRQ_PBA8_ETH (IRQ_PBA8_GIC_START + 28) /* Ethernet controller */
+#define IRQ_PBA8_USB (IRQ_PBA8_GIC_START + 29) /* USB controller */
+#define IRQ_PBA8_TSPEN (IRQ_PBA8_GIC_START + 30) /* Touchscreen pen */
+#define IRQ_PBA8_TSKPAD (IRQ_PBA8_GIC_START + 31) /* Touchscreen keypad */
+
+/* ... */
+#define IRQ_PBA8_PCI0 (IRQ_PBA8_GIC_START + 50)
+#define IRQ_PBA8_PCI1 (IRQ_PBA8_GIC_START + 51)
+#define IRQ_PBA8_PCI2 (IRQ_PBA8_GIC_START + 52)
+#define IRQ_PBA8_PCI3 (IRQ_PBA8_GIC_START + 53)
+
+#define IRQ_PBA8_SMC -1
+#define IRQ_PBA8_SCTL -1
+
+#define NR_GIC_PBA8 1
+
+/*
+ * Only define NR_IRQS if less than NR_IRQS_PBA8
+ */
+#define NR_IRQS_PBA8 (IRQ_PBA8_GIC_START + 64)
+
+#if defined(CONFIG_MACH_REALVIEW_PBA8)
+
+#if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_PBA8)
+#undef NR_IRQS
+#define NR_IRQS NR_IRQS_PBA8
+#endif
+
+#if !defined(MAX_GIC_NR) || (MAX_GIC_NR < NR_GIC_PBA8)
+#undef MAX_GIC_NR
+#define MAX_GIC_NR NR_GIC_PBA8
+#endif
+
+#endif /* CONFIG_MACH_REALVIEW_PBA8 */
+
+#endif /* __ASM_ARCH_BOARD_PBA8_H */
diff --git a/arch/arm/mach-realview/include/mach/clkdev.h b/arch/arm/mach-realview/include/mach/clkdev.h
new file mode 100644
index 000000000000..04b37a89801c
--- /dev/null
+++ b/arch/arm/mach-realview/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-realview/include/mach/debug-macro.S b/arch/arm/mach-realview/include/mach/debug-macro.S
index 7196bcadff0c..92dbcb9e1792 100644
--- a/arch/arm/mach-realview/include/mach/debug-macro.S
+++ b/arch/arm/mach-realview/include/mach/debug-macro.S
@@ -8,15 +8,36 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
-*/
+ */
+
+#if defined(CONFIG_MACH_REALVIEW_EB) || \
+ defined(CONFIG_MACH_REALVIEW_PB11MP) || \
+ defined(CONFIG_MACH_REALVIEW_PBA8)
+#ifndef DEBUG_LL_UART_OFFSET
+#define DEBUG_LL_UART_OFFSET 0x00009000
+#elif DEBUG_LL_UART_OFFSET != 0x00009000
+#warning "DEBUG_LL_UART_OFFSET already defined to a different value"
+#endif
+#endif
+
+#ifdef CONFIG_MACH_REALVIEW_PB1176
+#ifndef DEBUG_LL_UART_OFFSET
+#define DEBUG_LL_UART_OFFSET 0x0010c000
+#elif DEBUG_LL_UART_OFFSET != 0x0010c000
+#warning "DEBUG_LL_UART_OFFSET already defined to a different value"
+#endif
+#endif
+
+#ifndef DEBUG_LL_UART_OFFSET
+#error "Unknown RealView platform"
+#endif
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0x10000000
- movne \rx, #0xf0000000 @ virtual base
- orr \rx, \rx, #0x00009000
+ movne \rx, #0xfb000000 @ virtual base
+ orr \rx, \rx, #DEBUG_LL_UART_OFFSET
.endm
#include <asm/hardware/debug-pl01x.S>
diff --git a/arch/arm/mach-realview/include/mach/dma.h b/arch/arm/mach-realview/include/mach/dma.h
deleted file mode 100644
index f1a5a1a10952..000000000000
--- a/arch/arm/mach-realview/include/mach/dma.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/mach-realview/include/mach/dma.h
- *
- * Copyright (C) 2003 ARM Limited.
- * Copyright (C) 1997,1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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
- */
diff --git a/arch/arm/mach-realview/include/mach/hardware.h b/arch/arm/mach-realview/include/mach/hardware.h
index 79a93b3dfca9..b42c14f89acb 100644
--- a/arch/arm/mach-realview/include/mach/hardware.h
+++ b/arch/arm/mach-realview/include/mach/hardware.h
@@ -25,7 +25,14 @@
#include <asm/sizes.h>
/* macro to get at IO space when running virtually */
-#define IO_ADDRESS(x) (((x) & 0x0fffffff) + 0xf0000000)
+/*
+ * Statically mapped addresses:
+ *
+ * 10xx xxxx -> fbxx xxxx
+ * 1exx xxxx -> fdxx xxxx
+ * 1fxx xxxx -> fexx xxxx
+ */
+#define IO_ADDRESS(x) (((x) & 0x03ffffff) + 0xfb000000)
#define __io_address(n) __io(IO_ADDRESS(n))
#endif
diff --git a/arch/arm/mach-realview/include/mach/io.h b/arch/arm/mach-realview/include/mach/io.h
index aa069424d310..f05bcdf605d8 100644
--- a/arch/arm/mach-realview/include/mach/io.h
+++ b/arch/arm/mach-realview/include/mach/io.h
@@ -22,12 +22,7 @@
#define IO_SPACE_LIMIT 0xffffffff
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-
-#define __io(a) __io(a)
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-realview/include/mach/irqs.h b/arch/arm/mach-realview/include/mach/irqs.h
index 02a918529db3..fe5cb987aa21 100644
--- a/arch/arm/mach-realview/include/mach/irqs.h
+++ b/arch/arm/mach-realview/include/mach/irqs.h
@@ -25,6 +25,7 @@
#include <mach/board-eb.h>
#include <mach/board-pb11mp.h>
#include <mach/board-pb1176.h>
+#include <mach/board-pba8.h>
#define IRQ_LOCALTIMER 29
#define IRQ_LOCALWDOG 30
diff --git a/arch/arm/mach-realview/include/mach/memory.h b/arch/arm/mach-realview/include/mach/memory.h
index 0e673483a141..293c30025e7e 100644
--- a/arch/arm/mach-realview/include/mach/memory.h
+++ b/arch/arm/mach-realview/include/mach/memory.h
@@ -23,16 +23,10 @@
/*
* Physical DRAM offset.
*/
+#ifdef CONFIG_REALVIEW_HIGH_PHYS_OFFSET
+#define PHYS_OFFSET UL(0x70000000)
+#else
#define PHYS_OFFSET UL(0x00000000)
-
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) ((x) - PAGE_OFFSET)
-#define __bus_to_virt(x) ((x) + PAGE_OFFSET)
+#endif
#endif
diff --git a/arch/arm/mach-realview/include/mach/uncompress.h b/arch/arm/mach-realview/include/mach/uncompress.h
index 79f50f218e77..415d634d52ab 100644
--- a/arch/arm/mach-realview/include/mach/uncompress.h
+++ b/arch/arm/mach-realview/include/mach/uncompress.h
@@ -23,6 +23,7 @@
#include <mach/board-eb.h>
#include <mach/board-pb11mp.h>
#include <mach/board-pb1176.h>
+#include <mach/board-pba8.h>
#define AMBA_UART_DR(base) (*(volatile unsigned char *)((base) + 0x00))
#define AMBA_UART_LCRH(base) (*(volatile unsigned char *)((base) + 0x2c))
@@ -40,6 +41,8 @@ static inline unsigned long get_uart_base(void)
return REALVIEW_PB11MP_UART0_BASE;
else if (machine_is_realview_pb1176())
return REALVIEW_PB1176_UART0_BASE;
+ else if (machine_is_realview_pba8())
+ return REALVIEW_PBA8_UART0_BASE;
else
return 0;
}
diff --git a/arch/arm/mach-realview/include/mach/vmalloc.h b/arch/arm/mach-realview/include/mach/vmalloc.h
index 48cbcc873db2..fe0de1b507ac 100644
--- a/arch/arm/mach-realview/include/mach/vmalloc.h
+++ b/arch/arm/mach-realview/include/mach/vmalloc.h
@@ -18,4 +18,4 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#define VMALLOC_END (PAGE_OFFSET + 0x18000000)
+#define VMALLOC_END 0xf8000000
diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c
index 504961ef343c..67d6d9cc68b2 100644
--- a/arch/arm/mach-realview/localtimer.c
+++ b/arch/arm/mach-realview/localtimer.c
@@ -38,18 +38,14 @@ void local_timer_interrupt(void)
#ifdef CONFIG_LOCAL_TIMERS
-#define TWD_BASE(cpu) (twd_base_addr + (cpu) * twd_size)
-
/* set up by the platform code */
-void __iomem *twd_base_addr;
-unsigned int twd_size;
+void __iomem *twd_base;
static unsigned long mpcore_timer_rate;
static void local_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
{
- void __iomem *base = TWD_BASE(smp_processor_id());
unsigned long ctrl;
switch(mode) {
@@ -68,17 +64,16 @@ static void local_timer_set_mode(enum clock_event_mode mode,
ctrl = 0;
}
- __raw_writel(ctrl, base + TWD_TIMER_CONTROL);
+ __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
}
static int local_timer_set_next_event(unsigned long evt,
struct clock_event_device *unused)
{
- void __iomem *base = TWD_BASE(smp_processor_id());
- unsigned long ctrl = __raw_readl(base + TWD_TIMER_CONTROL);
+ unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
- __raw_writel(evt, base + TWD_TIMER_COUNTER);
- __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, base + TWD_TIMER_CONTROL);
+ __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
+ __raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
return 0;
}
@@ -91,19 +86,16 @@ static int local_timer_set_next_event(unsigned long evt,
*/
int local_timer_ack(void)
{
- void __iomem *base = TWD_BASE(smp_processor_id());
-
- if (__raw_readl(base + TWD_TIMER_INTSTAT)) {
- __raw_writel(1, base + TWD_TIMER_INTSTAT);
+ if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
+ __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
return 1;
}
return 0;
}
-static void __cpuinit twd_calibrate_rate(unsigned int cpu)
+static void __cpuinit twd_calibrate_rate(void)
{
- void __iomem *base = TWD_BASE(cpu);
unsigned long load, count;
u64 waitjiffies;
@@ -124,15 +116,15 @@ static void __cpuinit twd_calibrate_rate(unsigned int cpu)
waitjiffies += 5;
/* enable, no interrupt or reload */
- __raw_writel(0x1, base + TWD_TIMER_CONTROL);
+ __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
/* maximum value */
- __raw_writel(0xFFFFFFFFU, base + TWD_TIMER_COUNTER);
+ __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
while (get_jiffies_64() < waitjiffies)
udelay(10);
- count = __raw_readl(base + TWD_TIMER_COUNTER);
+ count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
@@ -142,18 +134,19 @@ static void __cpuinit twd_calibrate_rate(unsigned int cpu)
load = mpcore_timer_rate / HZ;
- __raw_writel(load, base + TWD_TIMER_LOAD);
+ __raw_writel(load, twd_base + TWD_TIMER_LOAD);
}
/*
* Setup the local clock events for a CPU.
*/
-void __cpuinit local_timer_setup(unsigned int cpu)
+void __cpuinit local_timer_setup(void)
{
+ unsigned int cpu = smp_processor_id();
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
unsigned long flags;
- twd_calibrate_rate(cpu);
+ twd_calibrate_rate();
clk->name = "local_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
@@ -178,9 +171,9 @@ void __cpuinit local_timer_setup(unsigned int cpu)
/*
* take a local timer down
*/
-void __cpuexit local_timer_stop(unsigned int cpu)
+void __cpuexit local_timer_stop(void)
{
- __raw_writel(0, TWD_BASE(cpu) + TWD_TIMER_CONTROL);
+ __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
}
#else /* CONFIG_LOCAL_TIMERS */
@@ -190,8 +183,9 @@ static void dummy_timer_set_mode(enum clock_event_mode mode,
{
}
-void __cpuinit local_timer_setup(unsigned int cpu)
+void __cpuinit local_timer_setup(void)
{
+ unsigned int cpu = smp_processor_id();
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
clk->name = "dummy_timer";
diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c
index e102aeb0f76e..8fce85f33033 100644
--- a/arch/arm/mach-realview/platsmp.c
+++ b/arch/arm/mach-realview/platsmp.c
@@ -23,6 +23,8 @@
#include <mach/board-pb11mp.h>
#include <mach/scu.h>
+#include "core.h"
+
extern void realview_secondary_startup(void);
/*
@@ -31,15 +33,20 @@ extern void realview_secondary_startup(void);
*/
volatile int __cpuinitdata pen_release = -1;
+static void __iomem *scu_base_addr(void)
+{
+ if (machine_is_realview_eb_mp())
+ return __io_address(REALVIEW_EB11MP_SCU_BASE);
+ else if (machine_is_realview_pb11mp())
+ return __io_address(REALVIEW_TC11MP_SCU_BASE);
+ else
+ return (void __iomem *)0;
+}
+
static unsigned int __init get_core_count(void)
{
unsigned int ncores;
- void __iomem *scu_base = 0;
-
- if (machine_is_realview_eb() && core_tile_eb11mp())
- scu_base = __io_address(REALVIEW_EB11MP_SCU_BASE);
- else if (machine_is_realview_pb11mp())
- scu_base = __io_address(REALVIEW_TC11MP_SCU_BASE);
+ void __iomem *scu_base = scu_base_addr();
if (scu_base) {
ncores = __raw_readl(scu_base + SCU_CONFIG);
@@ -56,14 +63,7 @@ static unsigned int __init get_core_count(void)
static void scu_enable(void)
{
u32 scu_ctrl;
- void __iomem *scu_base;
-
- if (machine_is_realview_eb() && core_tile_eb11mp())
- scu_base = __io_address(REALVIEW_EB11MP_SCU_BASE);
- else if (machine_is_realview_pb11mp())
- scu_base = __io_address(REALVIEW_TC11MP_SCU_BASE);
- else
- BUG();
+ void __iomem *scu_base = scu_base_addr();
scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
scu_ctrl |= 1;
@@ -88,10 +88,7 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
* core (e.g. timer irq), then they will not have been enabled
* for us: do so
*/
- if (machine_is_realview_eb() && core_tile_eb11mp())
- gic_cpu_init(0, __io_address(REALVIEW_EB11MP_GIC_CPU_BASE));
- else if (machine_is_realview_pb11mp())
- gic_cpu_init(0, __io_address(REALVIEW_TC11MP_GIC_CPU_BASE));
+ gic_cpu_init(0, gic_cpu_base_addr);
/*
* let the primary processor know we're out of the
@@ -232,9 +229,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
* dummy (!CONFIG_LOCAL_TIMERS), it was already registers in
* realview_timer_init
*/
- if ((machine_is_realview_eb() && core_tile_eb11mp()) ||
- machine_is_realview_pb11mp())
- local_timer_setup(cpu);
+ local_timer_setup();
#endif
/*
diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c
index eb829eb1ebe2..bed39ed97613 100644
--- a/arch/arm/mach-realview/realview_eb.c
+++ b/arch/arm/mach-realview/realview_eb.c
@@ -108,7 +108,7 @@ static struct map_desc realview_eb11mp_io_desc[] __initdata = {
static void __init realview_eb_map_io(void)
{
iotable_init(realview_eb_io_desc, ARRAY_SIZE(realview_eb_io_desc));
- if (core_tile_eb11mp())
+ if (core_tile_eb11mp() || core_tile_a9mp())
iotable_init(realview_eb11mp_io_desc, ARRAY_SIZE(realview_eb11mp_io_desc));
}
@@ -242,12 +242,6 @@ static struct resource realview_eb_eth_resources[] = {
},
};
-static struct platform_device realview_eb_eth_device = {
- .id = 0,
- .num_resources = ARRAY_SIZE(realview_eb_eth_resources),
- .resource = realview_eb_eth_resources,
-};
-
/*
* Detect and register the correct Ethernet device. RealView/EB rev D
* platforms use the newer SMSC LAN9118 Ethernet chip
@@ -255,26 +249,24 @@ static struct platform_device realview_eb_eth_device = {
static int eth_device_register(void)
{
void __iomem *eth_addr = ioremap(REALVIEW_EB_ETH_BASE, SZ_4K);
+ const char *name = NULL;
u32 idrev;
if (!eth_addr)
return -ENOMEM;
idrev = readl(eth_addr + 0x50);
- if ((idrev & 0xFFFF0000) == 0x01180000)
- /* SMSC LAN9118 chip present */
- realview_eb_eth_device.name = "smc911x";
- else
- /* SMSC 91C111 chip present */
- realview_eb_eth_device.name = "smc91x";
+ if ((idrev & 0xFFFF0000) != 0x01180000)
+ /* SMSC LAN9118 not present, use LAN91C111 instead */
+ name = "smc91x";
iounmap(eth_addr);
- return platform_device_register(&realview_eb_eth_device);
+ return realview_eth_register(name, realview_eb_eth_resources);
}
static void __init gic_init_irq(void)
{
- if (core_tile_eb11mp()) {
+ if (core_tile_eb11mp() || core_tile_a9mp()) {
unsigned int pldctrl;
/* new irq mode */
@@ -342,10 +334,9 @@ static void __init realview_eb_timer_init(void)
timer2_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_EB_TIMER2_3_BASE) + 0x20;
- if (core_tile_eb11mp()) {
+ if (core_tile_eb11mp() || core_tile_a9mp()) {
#ifdef CONFIG_LOCAL_TIMERS
- twd_base_addr = __io_address(REALVIEW_EB11MP_TWD_BASE);
- twd_size = REALVIEW_EB11MP_TWD_SIZE;
+ twd_base = __io_address(REALVIEW_EB11MP_TWD_BASE);
#endif
timer_irq = IRQ_EB11MP_TIMER0_1;
} else
@@ -362,7 +353,7 @@ static void __init realview_eb_init(void)
{
int i;
- if (core_tile_eb11mp()) {
+ if (core_tile_eb11mp() || core_tile_a9mp()) {
realview_eb11mp_fixup();
#ifdef CONFIG_CACHE_L2X0
@@ -372,8 +363,6 @@ static void __init realview_eb_init(void)
#endif
}
- clk_register(&realview_clcd_clk);
-
realview_flash_register(&realview_eb_flash_resource, 1);
platform_device_register(&realview_i2c_device);
eth_device_register();
@@ -392,7 +381,7 @@ MACHINE_START(REALVIEW_EB, "ARM-RealView EB")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.phys_io = REALVIEW_EB_UART0_BASE,
.io_pg_offst = (IO_ADDRESS(REALVIEW_EB_UART0_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = PHYS_OFFSET + 0x00000100,
.map_io = realview_eb_map_io,
.init_irq = gic_init_irq,
.timer = &realview_eb_timer,
diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c
index cccdb3eb90fe..8f0683c22140 100644
--- a/arch/arm/mach-realview/realview_pb1176.c
+++ b/arch/arm/mach-realview/realview_pb1176.c
@@ -222,13 +222,6 @@ static struct resource realview_pb1176_smsc911x_resources[] = {
},
};
-static struct platform_device realview_pb1176_smsc911x_device = {
- .name = "smc911x",
- .id = 0,
- .num_resources = ARRAY_SIZE(realview_pb1176_smsc911x_resources),
- .resource = realview_pb1176_smsc911x_resources,
-};
-
static void __init gic_init_irq(void)
{
/* ARM1176 DevChip GIC, primary */
@@ -265,10 +258,8 @@ static void __init realview_pb1176_init(void)
l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff);
#endif
- clk_register(&realview_clcd_clk);
-
realview_flash_register(&realview_pb1176_flash_resource, 1);
- platform_device_register(&realview_pb1176_smsc911x_device);
+ realview_eth_register(NULL, realview_pb1176_smsc911x_resources);
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
@@ -284,7 +275,7 @@ MACHINE_START(REALVIEW_PB1176, "ARM-RealView PB1176")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.phys_io = REALVIEW_PB1176_UART0_BASE,
.io_pg_offst = (IO_ADDRESS(REALVIEW_PB1176_UART0_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = PHYS_OFFSET + 0x00000100,
.map_io = realview_pb1176_map_io,
.init_irq = gic_init_irq,
.timer = &realview_pb1176_timer,
diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c
index 8b863148ec18..3ebdb2dadd6f 100644
--- a/arch/arm/mach-realview/realview_pb11mp.c
+++ b/arch/arm/mach-realview/realview_pb11mp.c
@@ -230,13 +230,6 @@ static struct resource realview_pb11mp_smsc911x_resources[] = {
},
};
-static struct platform_device realview_pb11mp_smsc911x_device = {
- .name = "smc911x",
- .id = 0,
- .num_resources = ARRAY_SIZE(realview_pb11mp_smsc911x_resources),
- .resource = realview_pb11mp_smsc911x_resources,
-};
-
struct resource realview_pb11mp_cf_resources[] = {
[0] = {
.start = REALVIEW_PB11MP_CF_BASE,
@@ -292,8 +285,7 @@ static void __init realview_pb11mp_timer_init(void)
timer3_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE) + 0x20;
#ifdef CONFIG_LOCAL_TIMERS
- twd_base_addr = __io_address(REALVIEW_TC11MP_TWD_BASE);
- twd_size = REALVIEW_TC11MP_TWD_SIZE;
+ twd_base = __io_address(REALVIEW_TC11MP_TWD_BASE);
#endif
realview_timer_init(IRQ_TC11MP_TIMER0_1);
}
@@ -312,11 +304,9 @@ static void __init realview_pb11mp_init(void)
l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff);
#endif
- clk_register(&realview_clcd_clk);
-
realview_flash_register(realview_pb11mp_flash_resource,
ARRAY_SIZE(realview_pb11mp_flash_resource));
- platform_device_register(&realview_pb11mp_smsc911x_device);
+ realview_eth_register(NULL, realview_pb11mp_smsc911x_resources);
platform_device_register(&realview_i2c_device);
platform_device_register(&realview_pb11mp_cf_device);
@@ -334,7 +324,7 @@ MACHINE_START(REALVIEW_PB11MP, "ARM-RealView PB11MPCore")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.phys_io = REALVIEW_PB11MP_UART0_BASE,
.io_pg_offst = (IO_ADDRESS(REALVIEW_PB11MP_UART0_BASE) >> 18) & 0xfffc,
- .boot_params = 0x00000100,
+ .boot_params = PHYS_OFFSET + 0x00000100,
.map_io = realview_pb11mp_map_io,
.init_irq = gic_init_irq,
.timer = &realview_pb11mp_timer,
diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c
new file mode 100644
index 000000000000..34c94435d2d8
--- /dev/null
+++ b/arch/arm/mach-realview/realview_pba8.c
@@ -0,0 +1,300 @@
+/*
+ * linux/arch/arm/mach-realview/realview_pba8.c
+ *
+ * Copyright (C) 2008 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
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/gic.h>
+#include <asm/hardware/icst307.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/mmc.h>
+#include <asm/mach/time.h>
+
+#include <mach/hardware.h>
+#include <mach/board-pba8.h>
+#include <mach/irqs.h>
+
+#include "core.h"
+#include "clock.h"
+
+static struct map_desc realview_pba8_io_desc[] __initdata = {
+ {
+ .virtual = IO_ADDRESS(REALVIEW_SYS_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_SYS_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = IO_ADDRESS(REALVIEW_PBA8_GIC_CPU_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_GIC_CPU_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = IO_ADDRESS(REALVIEW_PBA8_GIC_DIST_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_GIC_DIST_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = IO_ADDRESS(REALVIEW_SCTL_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_SCTL_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = IO_ADDRESS(REALVIEW_PBA8_TIMER0_1_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_TIMER0_1_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = IO_ADDRESS(REALVIEW_PBA8_TIMER2_3_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_TIMER2_3_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ },
+#ifdef CONFIG_PCI
+ {
+ .virtual = PCIX_UNIT_BASE,
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_PCI_BASE),
+ .length = REALVIEW_PBA8_PCI_BASE_SIZE,
+ .type = MT_DEVICE
+ },
+#endif
+#ifdef CONFIG_DEBUG_LL
+ {
+ .virtual = IO_ADDRESS(REALVIEW_PBA8_UART0_BASE),
+ .pfn = __phys_to_pfn(REALVIEW_PBA8_UART0_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ },
+#endif
+};
+
+static void __init realview_pba8_map_io(void)
+{
+ iotable_init(realview_pba8_io_desc, ARRAY_SIZE(realview_pba8_io_desc));
+}
+
+/*
+ * RealView PBA8Core AMBA devices
+ */
+
+#define GPIO2_IRQ { IRQ_PBA8_GPIO2, NO_IRQ }
+#define GPIO2_DMA { 0, 0 }
+#define GPIO3_IRQ { IRQ_PBA8_GPIO3, NO_IRQ }
+#define GPIO3_DMA { 0, 0 }
+#define AACI_IRQ { IRQ_PBA8_AACI, NO_IRQ }
+#define AACI_DMA { 0x80, 0x81 }
+#define MMCI0_IRQ { IRQ_PBA8_MMCI0A, IRQ_PBA8_MMCI0B }
+#define MMCI0_DMA { 0x84, 0 }
+#define KMI0_IRQ { IRQ_PBA8_KMI0, NO_IRQ }
+#define KMI0_DMA { 0, 0 }
+#define KMI1_IRQ { IRQ_PBA8_KMI1, NO_IRQ }
+#define KMI1_DMA { 0, 0 }
+#define PBA8_SMC_IRQ { NO_IRQ, NO_IRQ }
+#define PBA8_SMC_DMA { 0, 0 }
+#define MPMC_IRQ { NO_IRQ, NO_IRQ }
+#define MPMC_DMA { 0, 0 }
+#define PBA8_CLCD_IRQ { IRQ_PBA8_CLCD, NO_IRQ }
+#define PBA8_CLCD_DMA { 0, 0 }
+#define DMAC_IRQ { IRQ_PBA8_DMAC, NO_IRQ }
+#define DMAC_DMA { 0, 0 }
+#define SCTL_IRQ { NO_IRQ, NO_IRQ }
+#define SCTL_DMA { 0, 0 }
+#define PBA8_WATCHDOG_IRQ { IRQ_PBA8_WATCHDOG, NO_IRQ }
+#define PBA8_WATCHDOG_DMA { 0, 0 }
+#define PBA8_GPIO0_IRQ { IRQ_PBA8_GPIO0, NO_IRQ }
+#define PBA8_GPIO0_DMA { 0, 0 }
+#define GPIO1_IRQ { IRQ_PBA8_GPIO1, NO_IRQ }
+#define GPIO1_DMA { 0, 0 }
+#define PBA8_RTC_IRQ { IRQ_PBA8_RTC, NO_IRQ }
+#define PBA8_RTC_DMA { 0, 0 }
+#define SCI_IRQ { IRQ_PBA8_SCI, NO_IRQ }
+#define SCI_DMA { 7, 6 }
+#define PBA8_UART0_IRQ { IRQ_PBA8_UART0, NO_IRQ }
+#define PBA8_UART0_DMA { 15, 14 }
+#define PBA8_UART1_IRQ { IRQ_PBA8_UART1, NO_IRQ }
+#define PBA8_UART1_DMA { 13, 12 }
+#define PBA8_UART2_IRQ { IRQ_PBA8_UART2, NO_IRQ }
+#define PBA8_UART2_DMA { 11, 10 }
+#define PBA8_UART3_IRQ { IRQ_PBA8_UART3, NO_IRQ }
+#define PBA8_UART3_DMA { 0x86, 0x87 }
+#define PBA8_SSP_IRQ { IRQ_PBA8_SSP, NO_IRQ }
+#define PBA8_SSP_DMA { 9, 8 }
+
+/* FPGA Primecells */
+AMBA_DEVICE(aaci, "fpga:04", AACI, NULL);
+AMBA_DEVICE(mmc0, "fpga:05", MMCI0, &realview_mmc0_plat_data);
+AMBA_DEVICE(kmi0, "fpga:06", KMI0, NULL);
+AMBA_DEVICE(kmi1, "fpga:07", KMI1, NULL);
+AMBA_DEVICE(uart3, "fpga:09", PBA8_UART3, NULL);
+
+/* DevChip Primecells */
+AMBA_DEVICE(smc, "dev:00", PBA8_SMC, NULL);
+AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
+AMBA_DEVICE(wdog, "dev:e1", PBA8_WATCHDOG, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", PBA8_GPIO0, NULL);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(rtc, "dev:e8", PBA8_RTC, NULL);
+AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
+AMBA_DEVICE(uart0, "dev:f1", PBA8_UART0, NULL);
+AMBA_DEVICE(uart1, "dev:f2", PBA8_UART1, NULL);
+AMBA_DEVICE(uart2, "dev:f3", PBA8_UART2, NULL);
+AMBA_DEVICE(ssp0, "dev:f4", PBA8_SSP, NULL);
+
+/* Primecells on the NEC ISSP chip */
+AMBA_DEVICE(clcd, "issp:20", PBA8_CLCD, &clcd_plat_data);
+AMBA_DEVICE(dmac, "issp:30", DMAC, NULL);
+
+static struct amba_device *amba_devs[] __initdata = {
+ &dmac_device,
+ &uart0_device,
+ &uart1_device,
+ &uart2_device,
+ &uart3_device,
+ &smc_device,
+ &clcd_device,
+ &sctl_device,
+ &wdog_device,
+ &gpio0_device,
+ &gpio1_device,
+ &gpio2_device,
+ &rtc_device,
+ &sci0_device,
+ &ssp0_device,
+ &aaci_device,
+ &mmc0_device,
+ &kmi0_device,
+ &kmi1_device,
+};
+
+/*
+ * RealView PB-A8 platform devices
+ */
+static struct resource realview_pba8_flash_resource[] = {
+ [0] = {
+ .start = REALVIEW_PBA8_FLASH0_BASE,
+ .end = REALVIEW_PBA8_FLASH0_BASE + REALVIEW_PBA8_FLASH0_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = REALVIEW_PBA8_FLASH1_BASE,
+ .end = REALVIEW_PBA8_FLASH1_BASE + REALVIEW_PBA8_FLASH1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource realview_pba8_smsc911x_resources[] = {
+ [0] = {
+ .start = REALVIEW_PBA8_ETH_BASE,
+ .end = REALVIEW_PBA8_ETH_BASE + SZ_64K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_PBA8_ETH,
+ .end = IRQ_PBA8_ETH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct resource realview_pba8_cf_resources[] = {
+ [0] = {
+ .start = REALVIEW_PBA8_CF_BASE,
+ .end = REALVIEW_PBA8_CF_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = REALVIEW_PBA8_CF_MEM_BASE,
+ .end = REALVIEW_PBA8_CF_MEM_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = -1, /* FIXME: Find correct irq */
+ .end = -1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device realview_pba8_cf_device = {
+ .name = "compactflash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(realview_pba8_cf_resources),
+ .resource = realview_pba8_cf_resources,
+};
+
+static void __init gic_init_irq(void)
+{
+ /* ARM PB-A8 on-board GIC */
+ gic_cpu_base_addr = __io_address(REALVIEW_PBA8_GIC_CPU_BASE);
+ gic_dist_init(0, __io_address(REALVIEW_PBA8_GIC_DIST_BASE), IRQ_PBA8_GIC_START);
+ gic_cpu_init(0, __io_address(REALVIEW_PBA8_GIC_CPU_BASE));
+}
+
+static void __init realview_pba8_timer_init(void)
+{
+ timer0_va_base = __io_address(REALVIEW_PBA8_TIMER0_1_BASE);
+ timer1_va_base = __io_address(REALVIEW_PBA8_TIMER0_1_BASE) + 0x20;
+ timer2_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE);
+ timer3_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE) + 0x20;
+
+ realview_timer_init(IRQ_PBA8_TIMER0_1);
+}
+
+static struct sys_timer realview_pba8_timer = {
+ .init = realview_pba8_timer_init,
+};
+
+static void __init realview_pba8_init(void)
+{
+ int i;
+
+ realview_flash_register(realview_pba8_flash_resource,
+ ARRAY_SIZE(realview_pba8_flash_resource));
+ realview_eth_register(NULL, realview_pba8_smsc911x_resources);
+ platform_device_register(&realview_i2c_device);
+ platform_device_register(&realview_pba8_cf_device);
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+ 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")
+ /* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
+ .phys_io = REALVIEW_PBA8_UART0_BASE,
+ .io_pg_offst = (IO_ADDRESS(REALVIEW_PBA8_UART0_BASE) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x00000100,
+ .map_io = realview_pba8_map_io,
+ .init_irq = gic_init_irq,
+ .timer = &realview_pba8_timer,
+ .init_machine = realview_pba8_init,
+MACHINE_END
diff --git a/arch/arm/mach-rpc/include/mach/dma.h b/arch/arm/mach-rpc/include/mach/dma.h
deleted file mode 100644
index 360b56f8f29f..000000000000
--- a/arch/arm/mach-rpc/include/mach/dma.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * arch/arm/mach-rpc/include/mach/dma.h
- *
- * Copyright (C) 1997 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.
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/*
- * This is the maximum DMA address that can be DMAd to.
- * There should not be more than (0xd0000000 - 0xc0000000)
- * bytes of RAM.
- */
-#define MAX_DMA_ADDRESS 0xd0000000
-#define MAX_DMA_CHANNELS 8
-
-#define DMA_0 0
-#define DMA_1 1
-#define DMA_2 2
-#define DMA_3 3
-#define DMA_S0 4
-#define DMA_S1 5
-#define DMA_VIRTUAL_FLOPPY 6
-#define DMA_VIRTUAL_SOUND 7
-
-#define DMA_FLOPPY DMA_VIRTUAL_FLOPPY
-
-#endif /* _ASM_ARCH_DMA_H */
-
diff --git a/arch/arm/mach-rpc/include/mach/io.h b/arch/arm/mach-rpc/include/mach/io.h
index 9f0553b7ec28..20da7f486e51 100644
--- a/arch/arm/mach-rpc/include/mach/io.h
+++ b/arch/arm/mach-rpc/include/mach/io.h
@@ -18,49 +18,6 @@
#define IO_SPACE_LIMIT 0xffffffff
/*
- * GCC is totally crap at loading/storing data. We try to persuade it
- * to do the right thing by using these whereever possible instead of
- * the above.
- */
-#define __arch_base_getb(b,o) \
- ({ \
- unsigned int __v, __r = (b); \
- __asm__ __volatile__( \
- "ldrb %0, [%1, %2]" \
- : "=r" (__v) \
- : "r" (__r), "Ir" (o)); \
- __v; \
- })
-
-#define __arch_base_getl(b,o) \
- ({ \
- unsigned int __v, __r = (b); \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2]" \
- : "=r" (__v) \
- : "r" (__r), "Ir" (o)); \
- __v; \
- })
-
-#define __arch_base_putb(v,b,o) \
- ({ \
- unsigned int __r = (b); \
- __asm__ __volatile__( \
- "strb %0, [%1, %2]" \
- : \
- : "r" (v), "r" (__r), "Ir" (o));\
- })
-
-#define __arch_base_putl(v,b,o) \
- ({ \
- unsigned int __r = (b); \
- __asm__ __volatile__( \
- "str %0, [%1, %2]" \
- : \
- : "r" (v), "r" (__r), "Ir" (o));\
- })
-
-/*
* We use two different types of addressing - PC style addresses, and ARM
* addresses. PC style accesses the PC hardware with the normal PC IO
* addresses, eg 0x3f8 for serial#1. ARM addresses are 0x80000000+
@@ -232,15 +189,13 @@ DECLARE_IO(int,l,"")
result; \
})
-#define __ioaddrc(port) __ioaddr(port)
-
#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p))
#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p))
#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p))
#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p))
#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p))
#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p))
-#define __ioaddr(p) (__builtin_constant_p((p)) ? __ioaddr(p) : __ioaddrc(p))
+
/* the following macro is deprecated */
#define ioaddr(port) ((unsigned long)__ioaddr((port)))
diff --git a/arch/arm/mach-rpc/include/mach/irqs.h b/arch/arm/mach-rpc/include/mach/irqs.h
index 4ce6ca97f669..3d2037496e38 100644
--- a/arch/arm/mach-rpc/include/mach/irqs.h
+++ b/arch/arm/mach-rpc/include/mach/irqs.h
@@ -44,3 +44,4 @@
#define IRQ_TIMER IRQ_TIMER0
+#define NR_IRQS 128
diff --git a/arch/arm/mach-rpc/include/mach/isa-dma.h b/arch/arm/mach-rpc/include/mach/isa-dma.h
new file mode 100644
index 000000000000..bad720548587
--- /dev/null
+++ b/arch/arm/mach-rpc/include/mach/isa-dma.h
@@ -0,0 +1,27 @@
+/*
+ * arch/arm/mach-rpc/include/mach/isa-dma.h
+ *
+ * Copyright (C) 1997 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.
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+#define MAX_DMA_CHANNELS 8
+
+#define DMA_0 0
+#define DMA_1 1
+#define DMA_2 2
+#define DMA_3 3
+#define DMA_S0 4
+#define DMA_S1 5
+#define DMA_VIRTUAL_FLOPPY 6
+#define DMA_VIRTUAL_SOUND 7
+
+#define DMA_FLOPPY DMA_VIRTUAL_FLOPPY
+
+#endif /* _ASM_ARCH_DMA_H */
+
diff --git a/arch/arm/mach-rpc/include/mach/memory.h b/arch/arm/mach-rpc/include/mach/memory.h
index 9bf7e43e2863..78191bf25192 100644
--- a/arch/arm/mach-rpc/include/mach/memory.h
+++ b/arch/arm/mach-rpc/include/mach/memory.h
@@ -24,13 +24,6 @@
#define PHYS_OFFSET UL(0x10000000)
/*
- * These are exactly the same on the RiscPC as the
- * physical memory view.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-/*
* Cache flushing area - ROM
*/
#define FLUSH_BASE_PHYS 0x00000000
diff --git a/arch/arm/mach-s3c2400/include/mach/memory.h b/arch/arm/mach-s3c2400/include/mach/memory.h
index 8f4878e4f591..cf5901ffd385 100644
--- a/arch/arm/mach-s3c2400/include/mach/memory.h
+++ b/arch/arm/mach-s3c2400/include/mach/memory.h
@@ -17,7 +17,4 @@
#define PHYS_OFFSET UL(0x0C000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig
index 99fdc736698c..63a30d1dd425 100644
--- a/arch/arm/mach-s3c2410/Kconfig
+++ b/arch/arm/mach-s3c2410/Kconfig
@@ -7,6 +7,7 @@
config CPU_S3C2410
bool
depends on ARCH_S3C2410
+ select CPU_ARM920T
select S3C2410_CLOCK
select S3C2410_GPIO
select CPU_LLSERIAL_S3C2410
@@ -32,11 +33,6 @@ config S3C2410_GPIO
help
GPIO code for S3C2410 and similar processors
-config S3C2410_CLOCK
- bool
- help
- Clock code for the S3C2410, and similar processors
-
config SIMTEC_NOR
bool
help
@@ -84,6 +80,7 @@ config ARCH_BAST
select PM_SIMTEC if PM
select SIMTEC_NOR
select MACH_BAST_IDE
+ select S3C24XX_DCLK
select ISA
help
Say Y here if you are using the Simtec Electronics EB2410ITX
@@ -121,6 +118,7 @@ config MACH_TCT_HAMMER
config MACH_VR1000
bool "Thorcom VR1000"
select PM_SIMTEC if PM
+ select S3C24XX_DCLK
select SIMTEC_NOR
select MACH_BAST_IDE
select CPU_S3C2410
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile
index 00f31f8c4e78..fca02f82711c 100644
--- a/arch/arm/mach-s3c2410/Makefile
+++ b/arch/arm/mach-s3c2410/Makefile
@@ -15,7 +15,6 @@ obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o
obj-$(CONFIG_S3C2410_GPIO) += gpio.o
-obj-$(CONFIG_S3C2410_CLOCK) += clock.o
# Machine support
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c
deleted file mode 100644
index 4e07943c1e29..000000000000
--- a/arch/arm/mach-s3c2410/clock.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/* linux/arch/arm/mach-s3c2410/clock.c
- *
- * Copyright (c) 2006 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C2410,S3C2440,S3C2442 Clock control support
- *
- * This program is free software; you can redistribute 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/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/sysdev.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-#include <linux/serial_core.h>
-#include <linux/io.h>
-
-#include <asm/mach/map.h>
-
-#include <mach/hardware.h>
-
-#include <plat/regs-serial.h>
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-
-#include <plat/s3c2410.h>
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-int s3c2410_clkcon_enable(struct clk *clk, int enable)
-{
- unsigned int clocks = clk->ctrlbit;
- unsigned long clkcon;
-
- clkcon = __raw_readl(S3C2410_CLKCON);
-
- if (enable)
- clkcon |= clocks;
- else
- clkcon &= ~clocks;
-
- /* ensure none of the special function bits set */
- clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
-
- __raw_writel(clkcon, S3C2410_CLKCON);
-
- return 0;
-}
-
-static int s3c2410_upll_enable(struct clk *clk, int enable)
-{
- unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
- unsigned long orig = clkslow;
-
- if (enable)
- clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
- else
- clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
-
- __raw_writel(clkslow, S3C2410_CLKSLOW);
-
- /* if we started the UPLL, then allow to settle */
-
- if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
- udelay(200);
-
- return 0;
-}
-
-/* standard clock definitions */
-
-static struct clk init_clocks_disable[] = {
- {
- .name = "nand",
- .id = -1,
- .parent = &clk_h,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_NAND,
- }, {
- .name = "sdi",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_SDI,
- }, {
- .name = "adc",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_ADC,
- }, {
- .name = "i2c",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_IIC,
- }, {
- .name = "iis",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_IIS,
- }, {
- .name = "spi",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_SPI,
- }
-};
-
-static struct clk init_clocks[] = {
- {
- .name = "lcd",
- .id = -1,
- .parent = &clk_h,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_LCDC,
- }, {
- .name = "gpio",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_GPIO,
- }, {
- .name = "usb-host",
- .id = -1,
- .parent = &clk_h,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_USBH,
- }, {
- .name = "usb-device",
- .id = -1,
- .parent = &clk_h,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_USBD,
- }, {
- .name = "timers",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_PWMT,
- }, {
- .name = "uart",
- .id = 0,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_UART0,
- }, {
- .name = "uart",
- .id = 1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_UART1,
- }, {
- .name = "uart",
- .id = 2,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_UART2,
- }, {
- .name = "rtc",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c2410_clkcon_enable,
- .ctrlbit = S3C2410_CLKCON_RTC,
- }, {
- .name = "watchdog",
- .id = -1,
- .parent = &clk_p,
- .ctrlbit = 0,
- }, {
- .name = "usb-bus-host",
- .id = -1,
- .parent = &clk_usb_bus,
- }, {
- .name = "usb-bus-gadget",
- .id = -1,
- .parent = &clk_usb_bus,
- },
-};
-
-/* s3c2410_baseclk_add()
- *
- * Add all the clocks used by the s3c2410 or compatible CPUs
- * such as the S3C2440 and S3C2442.
- *
- * We cannot use a system device as we are needed before any
- * of the init-calls that initialise the devices are actually
- * done.
-*/
-
-int __init s3c2410_baseclk_add(void)
-{
- unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
- unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
- struct clk *clkp;
- struct clk *xtal;
- int ret;
- int ptr;
-
- clk_upll.enable = s3c2410_upll_enable;
-
- if (s3c24xx_register_clock(&clk_usb_bus) < 0)
- printk(KERN_ERR "failed to register usb bus clock\n");
-
- /* register clocks from clock array */
-
- clkp = init_clocks;
- for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
- /* ensure that we note the clock state */
-
- clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
-
- ret = s3c24xx_register_clock(clkp);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register clock %s (%d)\n",
- clkp->name, ret);
- }
- }
-
- /* We must be careful disabling the clocks we are not intending to
- * be using at boot time, as subsystems such as the LCD which do
- * their own DMA requests to the bus can cause the system to lockup
- * if they where in the middle of requesting bus access.
- *
- * Disabling the LCD clock if the LCD is active is very dangerous,
- * and therefore the bootloader should be careful to not enable
- * the LCD clock if it is not needed.
- */
-
- /* install (and disable) the clocks we do not need immediately */
-
- clkp = init_clocks_disable;
- for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
-
- ret = s3c24xx_register_clock(clkp);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register clock %s (%d)\n",
- clkp->name, ret);
- }
-
- s3c2410_clkcon_enable(clkp, 0);
- }
-
- /* show the clock-slow value */
-
- xtal = clk_get(NULL, "xtal");
-
- printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
- print_mhz(clk_get_rate(xtal) /
- ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
- (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
- (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
- (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
-
- return 0;
-}
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c
index 7d914a470b6c..552b4c778fdc 100644
--- a/arch/arm/mach-s3c2410/dma.c
+++ b/arch/arm/mach-s3c2410/dma.c
@@ -17,7 +17,6 @@
#include <linux/sysdev.h>
#include <linux/serial_core.h>
-#include <asm/dma.h>
#include <mach/dma.h>
#include <plat/cpu.h>
@@ -25,12 +24,12 @@
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-ac97.h>
+#include <plat/regs-ac97.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {
[DMACH_XD0] = {
diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h
index 891b53cd69b8..13358ce2128c 100644
--- a/arch/arm/mach-s3c2410/include/mach/dma.h
+++ b/arch/arm/mach-s3c2410/include/mach/dma.h
@@ -16,11 +16,6 @@
#include <linux/sysdev.h>
#include <mach/hardware.h>
-/*
- * This is the maximum DMA address(physical address) that can be DMAd to.
- *
- */
-#define MAX_DMA_ADDRESS 0x40000000
#define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */
/* We use `virtual` dma channels to hide the fact we have only a limited
@@ -254,7 +249,7 @@ typedef unsigned long dma_device_t;
* request a dma channel exclusivley
*/
-extern int s3c2410_dma_request(dmach_t channel,
+extern int s3c2410_dma_request(unsigned int channel,
struct s3c2410_dma_client *, void *dev);
@@ -263,14 +258,14 @@ extern int s3c2410_dma_request(dmach_t channel,
* change the state of the dma channel
*/
-extern int s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op);
+extern int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op);
/* s3c2410_dma_setflags
*
* set the channel's flags to a given state
*/
-extern int s3c2410_dma_setflags(dmach_t channel,
+extern int s3c2410_dma_setflags(unsigned int channel,
unsigned int flags);
/* s3c2410_dma_free
@@ -278,7 +273,7 @@ extern int s3c2410_dma_setflags(dmach_t channel,
* free the dma channel (will also abort any outstanding operations)
*/
-extern int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *);
+extern int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *);
/* s3c2410_dma_enqueue
*
@@ -287,7 +282,7 @@ extern int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *);
* drained before the buffer is given to the DMA system.
*/
-extern int s3c2410_dma_enqueue(dmach_t channel, void *id,
+extern int s3c2410_dma_enqueue(unsigned int channel, void *id,
dma_addr_t data, int size);
/* s3c2410_dma_config
@@ -295,7 +290,7 @@ extern int s3c2410_dma_enqueue(dmach_t channel, void *id,
* configure the dma channel
*/
-extern int s3c2410_dma_config(dmach_t channel, int xferunit, int dcon);
+extern int s3c2410_dma_config(unsigned int channel, int xferunit, int dcon);
/* s3c2410_dma_devconfig
*
@@ -310,11 +305,11 @@ extern int s3c2410_dma_devconfig(int channel, enum s3c2410_dmasrc source,
* get the position that the dma transfer is currently at
*/
-extern int s3c2410_dma_getposition(dmach_t channel,
+extern int s3c2410_dma_getposition(unsigned int channel,
dma_addr_t *src, dma_addr_t *dest);
-extern int s3c2410_dma_set_opfn(dmach_t, s3c2410_dma_opfn_t rtn);
-extern int s3c2410_dma_set_buffdone_fn(dmach_t, s3c2410_dma_cbfn_t rtn);
+extern int s3c2410_dma_set_opfn(unsigned int, s3c2410_dma_opfn_t rtn);
+extern int s3c2410_dma_set_buffdone_fn(unsigned int, s3c2410_dma_cbfn_t rtn);
/* DMA Register definitions */
diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-core.h b/arch/arm/mach-s3c2410/include/mach/gpio-core.h
new file mode 100644
index 000000000000..6c9fbb99ef14
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/gpio-core.h
@@ -0,0 +1,34 @@
+/* arch/arm/mach-s3c24100/include/mach/gpio-core.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C2410 - GPIO core support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_GPIO_CORE_H
+#define __ASM_ARCH_GPIO_CORE_H __FILE__
+
+#include <plat/gpio-core.h>
+#include <mach/regs-gpio.h>
+
+extern struct s3c_gpio_chip s3c24xx_gpios[];
+
+static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)
+{
+ struct s3c_gpio_chip *chip;
+
+ if (pin > S3C2410_GPG10)
+ return NULL;
+
+ chip = &s3c24xx_gpios[pin/32];
+ return (S3C2410_GPIO_OFFSET(pin) > chip->chip.ngpio) ? chip : NULL;
+}
+
+#endif /* __ASM_ARCH_GPIO_CORE_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/gpio.h b/arch/arm/mach-s3c2410/include/mach/gpio.h
index 3b52b86498a6..e0349af8a483 100644
--- a/arch/arm/mach-s3c2410/include/mach/gpio.h
+++ b/arch/arm/mach-s3c2410/include/mach/gpio.h
@@ -15,4 +15,10 @@
#define gpio_set_value __gpio_set_value
#define gpio_cansleep __gpio_cansleep
+/* some boards require extra gpio capacity to support external
+ * devices that need GPIO.
+ */
+
+#define ARCH_NR_GPIOS (256 + CONFIG_S3C24XX_GPIO_EXTRA)
+
#include <asm-generic/gpio.h>
diff --git a/arch/arm/mach-s3c2410/include/mach/irqs.h b/arch/arm/mach-s3c2410/include/mach/irqs.h
index 950c71bf1489..9565903d490b 100644
--- a/arch/arm/mach-s3c2410/include/mach/irqs.h
+++ b/arch/arm/mach-s3c2410/include/mach/irqs.h
@@ -134,6 +134,8 @@
#define IRQ_S3C2443_HSMMC S3C2410_IRQ(20) /* IRQ_SDI */
#define IRQ_S3C2443_NAND S3C2410_IRQ(24) /* reserved */
+#define IRQ_HSMMC0 IRQ_S3C2443_HSMMC
+
#define IRQ_S3C2443_LCD1 S3C2410_IRQSUB(14)
#define IRQ_S3C2443_LCD2 S3C2410_IRQSUB(15)
#define IRQ_S3C2443_LCD3 S3C2410_IRQSUB(16)
@@ -160,6 +162,12 @@
#define NR_IRQS (IRQ_S3C2440_AC97+1)
#endif
+/* compatibility define. */
+#define IRQ_UART3 IRQ_S3C2443_UART3
+#define IRQ_S3CUART_RX3 IRQ_S3C2443_RX3
+#define IRQ_S3CUART_TX3 IRQ_S3C2443_TX3
+#define IRQ_S3CUART_ERR3 IRQ_S3C2443_ERR3
+
/* Our FIQs are routable from IRQ_EINT0 to IRQ_ADCPARENT */
#define FIQ_START IRQ_EINT0
diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 23c470c2e5b1..255fdfeaf957 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -13,34 +13,20 @@
#ifndef __ASM_ARCH_MAP_H
#define __ASM_ARCH_MAP_H
+#include <plat/map-base.h>
#include <plat/map.h>
#define S3C2410_ADDR(x) S3C_ADDR(x)
-/* interrupt controller is the first thing we put in, to make
- * the assembly code for the irq detection easier
- */
-#define S3C24XX_VA_IRQ S3C_VA_IRQ
-#define S3C2410_PA_IRQ (0x4A000000)
-#define S3C24XX_SZ_IRQ SZ_1M
-
-/* memory controller registers */
-#define S3C24XX_VA_MEMCTRL S3C_VA_MEM
-#define S3C2410_PA_MEMCTRL (0x48000000)
-#define S3C24XX_SZ_MEMCTRL SZ_1M
-
/* USB host controller */
#define S3C2410_PA_USBHOST (0x49000000)
-#define S3C24XX_SZ_USBHOST SZ_1M
/* DMA controller */
#define S3C2410_PA_DMA (0x4B000000)
#define S3C24XX_SZ_DMA SZ_1M
/* Clock and Power management */
-#define S3C24XX_VA_CLKPWR S3C_VA_SYS
#define S3C2410_PA_CLKPWR (0x4C000000)
-#define S3C24XX_SZ_CLKPWR SZ_1M
/* LCD controller */
#define S3C2410_PA_LCD (0x4D000000)
@@ -48,48 +34,12 @@
/* NAND flash controller */
#define S3C2410_PA_NAND (0x4E000000)
-#define S3C24XX_SZ_NAND SZ_1M
-
-/* UARTs */
-#define S3C24XX_VA_UART S3C_VA_UART
-#define S3C2410_PA_UART (0x50000000)
-#define S3C24XX_SZ_UART SZ_1M
-
-/* Timers */
-#define S3C24XX_VA_TIMER S3C_VA_TIMER
-#define S3C2410_PA_TIMER (0x51000000)
-#define S3C24XX_SZ_TIMER SZ_1M
-
-/* USB Device port */
-#define S3C2410_PA_USBDEV (0x52000000)
-#define S3C24XX_SZ_USBDEV SZ_1M
-
-/* Watchdog */
-#define S3C24XX_VA_WATCHDOG S3C_VA_WATCHDOG
-#define S3C2410_PA_WATCHDOG (0x53000000)
-#define S3C24XX_SZ_WATCHDOG SZ_1M
/* IIC hardware controller */
#define S3C2410_PA_IIC (0x54000000)
-#define S3C24XX_SZ_IIC SZ_1M
/* IIS controller */
#define S3C2410_PA_IIS (0x55000000)
-#define S3C24XX_SZ_IIS SZ_1M
-
-/* GPIO ports */
-
-/* the calculation for the VA of this must ensure that
- * it is the same distance apart from the UART in the
- * phsyical address space, as the initial mapping for the IO
- * is done as a 1:1 maping. This puts it (currently) at
- * 0xFA800000, which is not in the way of any current mapping
- * by the base system.
-*/
-
-#define S3C2410_PA_GPIO (0x56000000)
-#define S3C24XX_VA_GPIO ((S3C2410_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
-#define S3C24XX_SZ_GPIO SZ_1M
/* RTC */
#define S3C2410_PA_RTC (0x57000000)
@@ -97,15 +47,12 @@
/* ADC */
#define S3C2410_PA_ADC (0x58000000)
-#define S3C24XX_SZ_ADC SZ_1M
/* SPI */
#define S3C2410_PA_SPI (0x59000000)
-#define S3C24XX_SZ_SPI SZ_1M
/* SDI */
#define S3C2410_PA_SDI (0x5A000000)
-#define S3C24XX_SZ_SDI SZ_1M
/* CAMIF */
#define S3C2440_PA_CAMIF (0x4F000000)
@@ -120,13 +67,6 @@
#define S3C2443_PA_HSMMC (0x4A800000)
#define S3C2443_SZ_HSMMC (256)
-/* ISA style IO, for each machine to sort out mappings for, if it
- * implements it. We reserve two 16M regions for ISA.
- */
-
-#define S3C24XX_VA_ISA_WORD S3C2410_ADDR(0x02000000)
-#define S3C24XX_VA_ISA_BYTE S3C2410_ADDR(0x03000000)
-
/* physical addresses of all the chip-select areas */
#define S3C2410_CS0 (0x00000000)
@@ -152,27 +92,16 @@
#define S3C24XX_PA_TIMER S3C2410_PA_TIMER
#define S3C24XX_PA_USBDEV S3C2410_PA_USBDEV
#define S3C24XX_PA_WATCHDOG S3C2410_PA_WATCHDOG
-#define S3C24XX_PA_IIC S3C2410_PA_IIC
#define S3C24XX_PA_IIS S3C2410_PA_IIS
#define S3C24XX_PA_GPIO S3C2410_PA_GPIO
#define S3C24XX_PA_RTC S3C2410_PA_RTC
#define S3C24XX_PA_ADC S3C2410_PA_ADC
#define S3C24XX_PA_SPI S3C2410_PA_SPI
+#define S3C24XX_PA_SDI S3C2410_PA_SDI
+#define S3C24XX_PA_NAND S3C2410_PA_NAND
-/* deal with the registers that move under the 2412/2413 */
-
-#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
-#ifndef __ASSEMBLY__
-extern void __iomem *s3c24xx_va_gpio2;
-#endif
-#ifdef CONFIG_CPU_S3C2412_ONLY
-#define S3C24XX_VA_GPIO2 (S3C24XX_VA_GPIO + 0x10)
-#else
-#define S3C24XX_VA_GPIO2 s3c24xx_va_gpio2
-#endif
-#else
-#define s3c24xx_va_gpio2 S3C24XX_VA_GPIO
-#define S3C24XX_VA_GPIO2 S3C24XX_VA_GPIO
-#endif
+#define S3C_PA_IIC S3C2410_PA_IIC
+#define S3C_PA_UART S3C24XX_PA_UART
+#define S3C_PA_HSMMC0 S3C2443_PA_HSMMC
#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/memory.h b/arch/arm/mach-s3c2410/include/mach/memory.h
index 93782628a786..6f1e5871ae4b 100644
--- a/arch/arm/mach-s3c2410/include/mach/memory.h
+++ b/arch/arm/mach-s3c2410/include/mach/memory.h
@@ -13,7 +13,4 @@
#define PHYS_OFFSET UL(0x30000000)
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
#endif
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-clock.h
index b3f90aa78076..2a5d90e957fb 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-clock.h
@@ -42,13 +42,6 @@
#define S3C2410_CLKCON_IIS (1<<17)
#define S3C2410_CLKCON_SPI (1<<18)
-#define S3C2410_PLLCON_MDIVSHIFT 12
-#define S3C2410_PLLCON_PDIVSHIFT 4
-#define S3C2410_PLLCON_SDIVSHIFT 0
-#define S3C2410_PLLCON_MDIVMASK ((1<<(1+(19-12)))-1)
-#define S3C2410_PLLCON_PDIVMASK ((1<<5)-1)
-#define S3C2410_PLLCON_SDIVMASK 3
-
/* DCLKCON register addresses in gpio.h */
#define S3C2410_DCLKCON_DCLK0EN (1<<0)
@@ -76,32 +69,6 @@
#define S3C2410_CLKSLOW_SLOWVAL(x) (x)
#define S3C2410_CLKSLOW_GET_SLOWVAL(x) ((x) & 7)
-#ifndef __ASSEMBLY__
-
-#include <asm/div64.h>
-
-static inline unsigned int
-s3c2410_get_pll(unsigned int pllval, unsigned int baseclk)
-{
- unsigned int mdiv, pdiv, sdiv;
- uint64_t fvco;
-
- mdiv = pllval >> S3C2410_PLLCON_MDIVSHIFT;
- pdiv = pllval >> S3C2410_PLLCON_PDIVSHIFT;
- sdiv = pllval >> S3C2410_PLLCON_SDIVSHIFT;
-
- mdiv &= S3C2410_PLLCON_MDIVMASK;
- pdiv &= S3C2410_PLLCON_PDIVMASK;
- sdiv &= S3C2410_PLLCON_SDIVMASK;
-
- fvco = (uint64_t)baseclk * (mdiv + 8);
- do_div(fvco, (pdiv + 2) << sdiv);
-
- return (unsigned int)fvco;
-}
-
-#endif /* __ASSEMBLY__ */
-
#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442)
/* extra registers */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
index 528080ceac44..321077613067 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
@@ -1053,13 +1053,6 @@
#define S3C24XX_EXTINT1 S3C24XX_GPIOREG2(0x8C)
#define S3C24XX_EXTINT2 S3C24XX_GPIOREG2(0x90)
-/* values for S3C2410_EXTINT0/1/2 */
-#define S3C2410_EXTINT_LOWLEV (0x00)
-#define S3C2410_EXTINT_HILEV (0x01)
-#define S3C2410_EXTINT_FALLEDGE (0x02)
-#define S3C2410_EXTINT_RISEEDGE (0x04)
-#define S3C2410_EXTINT_BOTHEDGE (0x06)
-
/* interrupt filtering conrrol for EINT16..EINT23 */
#define S3C2410_EINFLT0 S3C2410_GPIOREG(0x94)
#define S3C2410_EINFLT1 S3C2410_GPIOREG(0x98)
diff --git a/arch/arm/mach-s3c2410/include/mach/spi.h b/arch/arm/mach-s3c2410/include/mach/spi.h
index 46d46f5b99f2..774f3adfe8ad 100644
--- a/arch/arm/mach-s3c2410/include/mach/spi.h
+++ b/arch/arm/mach-s3c2410/include/mach/spi.h
@@ -22,5 +22,12 @@ struct s3c2410_spi_info {
void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol);
};
+/* Standard setup / suspend routines for SPI GPIO pins. */
+
+extern void s3c24xx_spi_gpiocfg_bus0_gpe11_12_13(struct s3c2410_spi_info *spi,
+ int enable);
+
+extern void s3c24xx_spi_gpiocfg_bus1_gpg5_6_7(struct s3c2410_spi_info *spi,
+ int enable);
#endif /* __ASM_ARCH_SPI_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/system-reset.h b/arch/arm/mach-s3c2410/include/mach/system-reset.h
index 43535a0e7186..7613d0a384ba 100644
--- a/arch/arm/mach-s3c2410/include/mach/system-reset.h
+++ b/arch/arm/mach-s3c2410/include/mach/system-reset.h
@@ -13,7 +13,7 @@
#include <mach/hardware.h>
#include <linux/io.h>
-#include <asm/plat-s3c/regs-watchdog.h>
+#include <plat/regs-watchdog.h>
#include <mach/regs-clock.h>
#include <linux/clk.h>
diff --git a/arch/arm/mach-s3c2410/include/mach/tick.h b/arch/arm/mach-s3c2410/include/mach/tick.h
new file mode 100644
index 000000000000..544da41979db
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/tick.h
@@ -0,0 +1,15 @@
+/* linux/arch/arm/mach-s3c2410/include/mach/tick.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C2410 - timer tick support
+ */
+
+#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0))
+
+static inline int s3c24xx_ostimer_pending(void)
+{
+ return __raw_readl(S3C2410_SRCPND) & SRCPND_TIMER4;
+}
diff --git a/arch/arm/mach-s3c2410/include/mach/uncompress.h b/arch/arm/mach-s3c2410/include/mach/uncompress.h
index ab39491beee2..c9432103750d 100644
--- a/arch/arm/mach-s3c2410/include/mach/uncompress.h
+++ b/arch/arm/mach-s3c2410/include/mach/uncompress.h
@@ -1,3 +1,4 @@
+
/* arch/arm/mach-s3c2410/include/mach/uncompress.h
*
* Copyright (c) 2003, 2007 Simtec Electronics
diff --git a/arch/arm/mach-s3c2410/include/mach/vmalloc.h b/arch/arm/mach-s3c2410/include/mach/vmalloc.h
deleted file mode 100644
index 315b0078a34d..000000000000
--- a/arch/arm/mach-s3c2410/include/mach/vmalloc.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/vmalloc.h
- *
- * from arch/arm/mach-iop3xx/include/mach/vmalloc.h
- *
- * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * S3C2410 vmalloc definition
-*/
-
-#ifndef __ASM_ARCH_VMALLOC_H
-#define __ASM_ARCH_VMALLOC_H
-
-#define VMALLOC_END (0xE0000000)
-
-#endif /* __ASM_ARCH_VMALLOC_H */
diff --git a/arch/arm/mach-s3c2410/mach-amlm5900.c b/arch/arm/mach-s3c2410/mach-amlm5900.c
index d061fea01900..6d6995afeb43 100644
--- a/arch/arm/mach-s3c2410/mach-amlm5900.c
+++ b/arch/arm/mach-s3c2410/mach-amlm5900.c
@@ -52,6 +52,7 @@
#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
+#include <plat/iic.h>
#include <plat/devs.h>
#include <plat/cpu.h>
@@ -150,7 +151,7 @@ static struct platform_device *amlm5900_devices[] __initdata = {
#endif
&s3c_device_adc,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_usbgadget,
@@ -233,6 +234,7 @@ static void __init amlm5900_init(void)
#ifdef CONFIG_FB_S3C2410
s3c24xx_fb_set_platdata(&amlm5900_fb_info);
#endif
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(amlm5900_devices, ARRAY_SIZE(amlm5900_devices));
}
diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c
index 8db9c700e3c2..01bd76725b92 100644
--- a/arch/arm/mach-s3c2410/mach-bast.c
+++ b/arch/arm/mach-s3c2410/mach-bast.c
@@ -44,8 +44,8 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
-#include <asm/plat-s3c/nand.h>
-#include <asm/plat-s3c/iic.h>
+#include <plat/nand.h>
+#include <plat/iic.h>
#include <mach/fb.h>
#include <linux/mtd/mtd.h>
@@ -406,7 +406,7 @@ static struct platform_device bast_sio = {
* standard 100KHz i2c bus frequency
*/
-static struct s3c2410_platform_i2c bast_i2c_info = {
+static struct s3c2410_platform_i2c __initdata bast_i2c_info = {
.flags = 0,
.slave_addr = 0x10,
.bus_freq = 100*1000,
@@ -553,7 +553,7 @@ static struct platform_device *bast_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_nand,
&bast_device_dm9k,
@@ -588,7 +588,8 @@ static void __init bast_map_io(void)
s3c24xx_register_clocks(bast_clocks, ARRAY_SIZE(bast_clocks));
s3c_device_nand.dev.platform_data = &bast_nand_info;
- s3c_device_i2c.dev.platform_data = &bast_i2c_info;
+
+ s3c_i2c0_set_platdata(&bast_i2c_info);
s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc));
s3c24xx_init_clocks(0);
diff --git a/arch/arm/mach-s3c2410/mach-h1940.c b/arch/arm/mach-s3c2410/mach-h1940.c
index 98716d0108e9..821a1668c3ac 100644
--- a/arch/arm/mach-s3c2410/mach-h1940.c
+++ b/arch/arm/mach-s3c2410/mach-h1940.c
@@ -38,11 +38,13 @@
#include <mach/h1940.h>
#include <mach/h1940-latch.h>
#include <mach/fb.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/udc.h>
+#include <plat/iic.h>
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/pll.h>
#include <plat/pm.h>
static struct map_desc h1940_iodesc[] __initdata = {
@@ -183,7 +185,7 @@ static struct platform_device *h1940_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_usbgadget,
&s3c_device_leds,
@@ -215,6 +217,7 @@ static void __init h1940_init(void)
s3c24xx_fb_set_platdata(&h1940_fb_info);
s3c24xx_udc_set_platdata(&h1940_udc_cfg);
+ s3c_i2c0_set_platdata(NULL);
/* Turn off suspend on both USB ports, and switch the
* selectable USB port to USB device mode. */
@@ -223,10 +226,9 @@ static void __init h1940_init(void)
S3C2410_MISCCR_USBSUSPND0 |
S3C2410_MISCCR_USBSUSPND1, 0x0);
- tmp = (
- 0x78 << S3C2410_PLLCON_MDIVSHIFT)
- | (0x02 << S3C2410_PLLCON_PDIVSHIFT)
- | (0x03 << S3C2410_PLLCON_SDIVSHIFT);
+ tmp = (0x78 << S3C24XX_PLLCON_MDIVSHIFT)
+ | (0x02 << S3C24XX_PLLCON_PDIVSHIFT)
+ | (0x03 << S3C24XX_PLLCON_SDIVSHIFT);
writel(tmp, S3C2410_UPLLCON);
platform_add_devices(h1940_devices, ARRAY_SIZE(h1940_devices));
diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c
index 82505517846c..05a5e877b49b 100644
--- a/arch/arm/mach-s3c2410/mach-n30.c
+++ b/arch/arm/mach-s3c2410/mach-n30.c
@@ -17,7 +17,6 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/delay.h>
#include <linux/gpio_keys.h>
#include <linux/init.h>
#include <linux/input.h>
@@ -40,14 +39,14 @@
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
-#include <asm/plat-s3c/iic.h>
+#include <plat/iic.h>
#include <plat/regs-serial.h>
#include <plat/clock.h>
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/s3c2410.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/udc.h>
static struct map_desc n30_iodesc[] __initdata = {
/* nothing here yet */
@@ -320,7 +319,7 @@ static struct s3c2410fb_mach_info n30_fb_info __initdata = {
static struct platform_device *n30_devices[] __initdata = {
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_usb,
&s3c_device_usbgadget,
@@ -332,7 +331,7 @@ static struct platform_device *n30_devices[] __initdata = {
static struct platform_device *n35_devices[] __initdata = {
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_usbgadget,
&n35_button_device,
@@ -501,7 +500,7 @@ static void __init n30_init_irq(void)
static void __init n30_init(void)
{
s3c24xx_fb_set_platdata(&n30_fb_info);
- s3c_device_i2c.dev.platform_data = &n30_i2ccfg;
+ s3c_device_i2c0.dev.platform_data = &n30_i2ccfg;
s3c24xx_udc_set_platdata(&n30_udc_cfg);
/* Turn off suspend on both USB ports, and switch the
diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c
index d8255cf87e44..f6c7261a4a12 100644
--- a/arch/arm/mach-s3c2410/mach-otom.c
+++ b/arch/arm/mach-s3c2410/mach-otom.c
@@ -35,6 +35,7 @@
#include <plat/s3c2410.h>
#include <plat/clock.h>
#include <plat/devs.h>
+#include <plat/iic.h>
#include <plat/cpu.h>
static struct map_desc otom11_iodesc[] __initdata = {
@@ -94,7 +95,7 @@ static struct platform_device *otom11_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&otom_device_nor,
@@ -109,6 +110,7 @@ static void __init otom11_map_io(void)
static void __init otom11_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(otom11_devices, ARRAY_SIZE(otom11_devices));
}
diff --git a/arch/arm/mach-s3c2410/mach-qt2410.c b/arch/arm/mach-s3c2410/mach-qt2410.c
index 661807e14e8a..9678a53ceeb1 100644
--- a/arch/arm/mach-s3c2410/mach-qt2410.c
+++ b/arch/arm/mach-s3c2410/mach-qt2410.c
@@ -50,10 +50,11 @@
#include <mach/leds-gpio.h>
#include <plat/regs-serial.h>
#include <mach/fb.h>
-#include <asm/plat-s3c/nand.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/nand.h>
+#include <plat/udc.h>
#include <mach/spi.h>
#include <mach/spi-gpio.h>
+#include <plat/iic.h>
#include <plat/common-smdk.h>
#include <plat/devs.h>
@@ -247,7 +248,7 @@ static struct platform_device *qt2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_sdi,
&s3c_device_usbgadget,
@@ -349,6 +350,7 @@ static void __init qt2410_machine_init(void)
s3c2410_gpio_setpin(S3C2410_GPB0, 1);
s3c24xx_udc_set_platdata(&qt2410_udc_cfg);
+ s3c_i2c0_set_platdata(NULL);
s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c
index 152527bb2872..c49126ccb1d5 100644
--- a/arch/arm/mach-s3c2410/mach-smdk2410.c
+++ b/arch/arm/mach-s3c2410/mach-smdk2410.c
@@ -47,6 +47,7 @@
#include <asm/mach-types.h>
#include <plat/regs-serial.h>
+#include <plat/iic.h>
#include <plat/devs.h>
#include <plat/cpu.h>
@@ -89,7 +90,7 @@ static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
};
@@ -102,6 +103,7 @@ static void __init smdk2410_map_io(void)
static void __init smdk2410_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
smdk_machine_init();
}
diff --git a/arch/arm/mach-s3c2410/mach-tct_hammer.c b/arch/arm/mach-s3c2410/mach-tct_hammer.c
index 309dcf4c870a..8fdb0430bd48 100644
--- a/arch/arm/mach-s3c2410/mach-tct_hammer.c
+++ b/arch/arm/mach-s3c2410/mach-tct_hammer.c
@@ -45,6 +45,7 @@
#include <asm/mach-types.h>
#include <plat/regs-serial.h>
+#include <plat/iic.h>
#include <plat/devs.h>
#include <plat/cpu.h>
@@ -127,7 +128,7 @@ static struct s3c2410_uartcfg tct_hammer_uartcfgs[] = {
static struct platform_device *tct_hammer_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_usbgadget,
@@ -146,6 +147,7 @@ static void __init tct_hammer_map_io(void)
static void __init tct_hammer_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(tct_hammer_devices, ARRAY_SIZE(tct_hammer_devices));
}
diff --git a/arch/arm/mach-s3c2410/mach-vr1000.c b/arch/arm/mach-s3c2410/mach-vr1000.c
index 941353af16dc..61a1ea9c5c5c 100644
--- a/arch/arm/mach-s3c2410/mach-vr1000.c
+++ b/arch/arm/mach-s3c2410/mach-vr1000.c
@@ -47,6 +47,7 @@
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/iic.h>
#include "usb-simtec.h"
#include "nor-simtec.h"
@@ -334,7 +335,7 @@ static struct platform_device *vr1000_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_adc,
&serial_device,
&vr1000_dm9k0,
@@ -384,6 +385,7 @@ static void __init vr1000_map_io(void)
static void __init vr1000_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(vr1000_devices, ARRAY_SIZE(vr1000_devices));
i2c_register_board_info(0, vr1000_i2c_devs,
diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c
index ac79b536c4c3..feb141b1f915 100644
--- a/arch/arm/mach-s3c2410/s3c2410.c
+++ b/arch/arm/mach-s3c2410/s3c2410.c
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/sysdev.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
@@ -28,6 +29,8 @@
#include <mach/hardware.h>
#include <asm/irq.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
@@ -35,6 +38,7 @@
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/clock.h>
+#include <plat/pll.h>
/* Initial IO mappings */
@@ -59,25 +63,28 @@ void __init s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no)
* machine specific initialisation.
*/
-void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size)
+void __init s3c2410_map_io(void)
{
- /* register our io-tables */
-
iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc));
- iotable_init(mach_desc, mach_size);
}
-void __init s3c2410_init_clocks(int xtal)
+void __init_or_cpufreq s3c2410_setup_clocks(void)
{
+ struct clk *xtal_clk;
unsigned long tmp;
+ unsigned long xtal;
unsigned long fclk;
unsigned long hclk;
unsigned long pclk;
+ xtal_clk = clk_get(NULL, "xtal");
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
/* now we've got our machine bits initialised, work out what
* clocks we've got */
- fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal);
+ fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal);
tmp = __raw_readl(S3C2410_CLKDIVN);
@@ -95,7 +102,13 @@ void __init s3c2410_init_clocks(int xtal)
* console to use them
*/
- s3c24xx_setup_clocks(xtal, fclk, hclk, pclk);
+ s3c24xx_setup_clocks(fclk, hclk, pclk);
+}
+
+void __init s3c2410_init_clocks(int xtal)
+{
+ s3c24xx_register_baseclocks(xtal);
+ s3c2410_setup_clocks();
s3c2410_baseclk_add();
}
diff --git a/arch/arm/mach-s3c2412/Kconfig b/arch/arm/mach-s3c2412/Kconfig
index c59a9d2ee9a6..ca99564ae4b5 100644
--- a/arch/arm/mach-s3c2412/Kconfig
+++ b/arch/arm/mach-s3c2412/Kconfig
@@ -7,6 +7,7 @@
config CPU_S3C2412
bool
depends on ARCH_S3C2410
+ select CPU_ARM926T
select CPU_LLSERIAL_S3C2440
select S3C2412_PM if PM
select S3C2412_DMA if S3C2410_DMA
diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c
index 96d9eb15424f..a037df5e1c2d 100644
--- a/arch/arm/mach-s3c2412/clock.c
+++ b/arch/arm/mach-s3c2412/clock.c
@@ -93,12 +93,6 @@ static int s3c2412_upll_enable(struct clk *clk, int enable)
/* clock selections */
-/* CPU EXTCLK input */
-static struct clk clk_ext = {
- .name = "extclk",
- .id = -1,
-};
-
static struct clk clk_erefclk = {
.name = "erefclk",
.id = -1,
@@ -773,5 +767,6 @@ int __init s3c2412_baseclk_add(void)
s3c2412_clkcon_enable(clkp, 0);
}
+ s3c_pwmclk_init();
return 0;
}
diff --git a/arch/arm/mach-s3c2412/dma.c b/arch/arm/mach-s3c2412/dma.c
index ba0591e71f32..919856c9433f 100644
--- a/arch/arm/mach-s3c2412/dma.c
+++ b/arch/arm/mach-s3c2412/dma.c
@@ -18,7 +18,6 @@
#include <linux/serial_core.h>
#include <linux/io.h>
-#include <asm/dma.h>
#include <mach/dma.h>
#include <plat/dma.h>
@@ -26,13 +25,13 @@
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-ac97.h>
+#include <plat/regs-ac97.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
#include <asm/plat-s3c24xx/regs-iis.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#define MAP(x) { (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID }
diff --git a/arch/arm/mach-s3c2412/mach-jive.c b/arch/arm/mach-s3c2412/mach-jive.c
index b08f18c8c47a..ecddbbb34832 100644
--- a/arch/arm/mach-s3c2412/mach-jive.c
+++ b/arch/arm/mach-s3c2412/mach-jive.c
@@ -17,7 +17,6 @@
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/sysdev.h>
-#include <linux/delay.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
@@ -31,8 +30,8 @@
#include <asm/mach/irq.h>
#include <plat/regs-serial.h>
-#include <asm/plat-s3c/nand.h>
-#include <asm/plat-s3c/iic.h>
+#include <plat/nand.h>
+#include <plat/iic.h>
#include <mach/regs-power.h>
#include <mach/regs-gpio.h>
@@ -52,7 +51,8 @@
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/pm.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/udc.h>
+#include <plat/iic.h>
static struct map_desc jive_iodesc[] __initdata = {
};
@@ -398,11 +398,12 @@ static struct s3c2410_spigpio_info jive_lcd_spi = {
.bus_num = 1,
.pin_clk = S3C2410_GPG8,
.pin_mosi = S3C2410_GPB8,
+ .num_chipselect = 1,
.chip_select = jive_lcd_spi_chipselect,
};
static struct platform_device jive_device_lcdspi = {
- .name = "s3c24xx-spi-gpio",
+ .name = "spi_s3c24xx_gpio",
.id = 1,
.num_resources = 0,
.dev.platform_data = &jive_lcd_spi,
@@ -419,11 +420,12 @@ static struct s3c2410_spigpio_info jive_wm8750_spi = {
.bus_num = 2,
.pin_clk = S3C2410_GPB4,
.pin_mosi = S3C2410_GPB9,
+ .num_chipselect = 1,
.chip_select = jive_wm8750_chipselect,
};
static struct platform_device jive_device_wm8750 = {
- .name = "s3c24xx-spi-gpio",
+ .name = "spi_s3c24xx_gpio",
.id = 2,
.num_resources = 0,
.dev.platform_data = &jive_wm8750_spi,
@@ -450,14 +452,14 @@ static struct spi_board_info __initdata jive_spi_devs[] = {
/* I2C bus and device configuration. */
-static struct s3c2410_platform_i2c jive_i2c_cfg = {
+static struct s3c2410_platform_i2c jive_i2c_cfg __initdata = {
.max_freq = 80 * 1000,
.bus_freq = 50 * 1000,
.flags = S3C_IICFLG_FILTER,
.sda_delay = 2,
};
-static struct i2c_board_info jive_i2c_devs[] = {
+static struct i2c_board_info jive_i2c_devs[] __initdata = {
[0] = {
I2C_BOARD_INFO("lis302dl", 0x1c),
.irq = IRQ_EINT14,
@@ -470,7 +472,7 @@ static struct platform_device *jive_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_lcd,
&jive_device_lcdspi,
&jive_device_wm8750,
@@ -663,7 +665,7 @@ static void __init jive_machine_init(void)
spi_register_board_info(jive_spi_devs, ARRAY_SIZE(jive_spi_devs));
- s3c_device_i2c.dev.platform_data = &jive_i2c_cfg;
+ s3c_i2c0_set_platdata(&jive_i2c_cfg);
i2c_register_board_info(0, jive_i2c_devs, ARRAY_SIZE(jive_i2c_devs));
pm_power_off = jive_power_off;
diff --git a/arch/arm/mach-s3c2412/mach-smdk2413.c b/arch/arm/mach-s3c2412/mach-smdk2413.c
index c719b5a740a9..eba66aa6bd20 100644
--- a/arch/arm/mach-s3c2412/mach-smdk2413.c
+++ b/arch/arm/mach-s3c2412/mach-smdk2413.c
@@ -37,7 +37,8 @@
#include <mach/regs-lcd.h>
#include <mach/idle.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/udc.h>
+#include <plat/iic.h>
#include <mach/fb.h>
#include <plat/s3c2410.h>
@@ -105,7 +106,7 @@ static struct platform_device *smdk2413_devices[] __initdata = {
&s3c_device_usb,
//&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_usbgadget,
};
@@ -142,6 +143,7 @@ static void __init smdk2413_machine_init(void)
s3c24xx_udc_set_platdata(&smdk2413_udc_cfg);
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2413_devices, ARRAY_SIZE(smdk2413_devices));
smdk_machine_init();
diff --git a/arch/arm/mach-s3c2412/mach-vstms.c b/arch/arm/mach-s3c2412/mach-vstms.c
index 4cfa19ad9be0..11e8ad49fc7b 100644
--- a/arch/arm/mach-s3c2412/mach-vstms.c
+++ b/arch/arm/mach-s3c2412/mach-vstms.c
@@ -39,7 +39,8 @@
#include <mach/idle.h>
#include <mach/fb.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/iic.h>
+#include <plat/nand.h>
#include <plat/s3c2410.h>
#include <plat/s3c2412.h>
@@ -122,7 +123,7 @@ static struct s3c2410_platform_nand vstms_nand_info = {
static struct platform_device *vstms_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c_device_nand,
@@ -151,6 +152,7 @@ static void __init vstms_map_io(void)
static void __init vstms_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(vstms_devices, ARRAY_SIZE(vstms_devices));
}
diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c
index 313759c3da69..5b5aba69ec3f 100644
--- a/arch/arm/mach-s3c2412/s3c2412.c
+++ b/arch/arm/mach-s3c2412/s3c2412.c
@@ -16,6 +16,7 @@
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/sysdev.h>
#include <linux/serial_core.h>
@@ -33,13 +34,15 @@
#include <mach/reset.h>
#include <mach/idle.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
#include <mach/regs-power.h>
#include <mach/regs-gpio.h>
#include <mach/regs-gpioj.h>
#include <mach/regs-dsc.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#include <mach/regs-s3c2412.h>
#include <plat/s3c2412.h>
@@ -47,6 +50,7 @@
#include <plat/devs.h>
#include <plat/clock.h>
#include <plat/pm.h>
+#include <plat/pll.h>
#ifndef CONFIG_CPU_S3C2412_ONLY
void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO;
@@ -136,7 +140,7 @@ static void s3c2412_hard_reset(void)
* machine specific initialisation.
*/
-void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size)
+void __init s3c2412_map_io(void)
{
/* move base of IO */
@@ -153,20 +157,25 @@ void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size)
/* register our io-tables */
iotable_init(s3c2412_iodesc, ARRAY_SIZE(s3c2412_iodesc));
- iotable_init(mach_desc, mach_size);
}
-void __init s3c2412_init_clocks(int xtal)
+void __init_or_cpufreq s3c2412_setup_clocks(void)
{
+ struct clk *xtal_clk;
unsigned long tmp;
+ unsigned long xtal;
unsigned long fclk;
unsigned long hclk;
unsigned long pclk;
+ xtal_clk = clk_get(NULL, "xtal");
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
/* now we've got our machine bits initialised, work out what
* clocks we've got */
- fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2);
+ fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal * 2);
clk_mpll.rate = fclk;
@@ -183,11 +192,17 @@ void __init s3c2412_init_clocks(int xtal)
printk("S3C2412: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
+ s3c24xx_setup_clocks(fclk, hclk, pclk);
+}
+
+void __init s3c2412_init_clocks(int xtal)
+{
/* initialise the clocks here, to allow other things like the
* console to use them
*/
- s3c24xx_setup_clocks(xtal, fclk, hclk, pclk);
+ s3c24xx_register_baseclocks(xtal);
+ s3c2412_setup_clocks();
s3c2412_baseclk_add();
}
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
index 25de042ab996..cde5ae9a4340 100644
--- a/arch/arm/mach-s3c2440/Kconfig
+++ b/arch/arm/mach-s3c2440/Kconfig
@@ -7,6 +7,7 @@
config CPU_S3C2440
bool
depends on ARCH_S3C2410
+ select CPU_ARM920T
select S3C2410_CLOCK
select S3C2410_PM if PM
select S3C2410_GPIO
@@ -28,8 +29,10 @@ menu "S3C2440 Machines"
config MACH_ANUBIS
bool "Simtec Electronics ANUBIS"
select CPU_S3C2440
+ select S3C24XX_DCLK
select PM_SIMTEC if PM
select HAVE_PATA_PLATFORM
+ select S3C24XX_GPIO_EXTRA64
help
Say Y here if you are using the Simtec Electronics ANUBIS
development system
@@ -37,7 +40,9 @@ config MACH_ANUBIS
config MACH_OSIRIS
bool "Simtec IM2440D20 (OSIRIS) module"
select CPU_S3C2440
+ select S3C24XX_DCLK
select PM_SIMTEC if PM
+ select S3C24XX_GPIO_EXTRA128
help
Say Y here if you are using the Simtec IM2440D20 module, also
known as the Osiris.
diff --git a/arch/arm/mach-s3c2440/dma.c b/arch/arm/mach-s3c2440/dma.c
index 32303f6a8321..5b5ee0b8f4e0 100644
--- a/arch/arm/mach-s3c2440/dma.c
+++ b/arch/arm/mach-s3c2440/dma.c
@@ -17,7 +17,6 @@
#include <linux/sysdev.h>
#include <linux/serial_core.h>
-#include <asm/dma.h>
#include <mach/dma.h>
#include <plat/dma.h>
@@ -25,12 +24,12 @@
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-ac97.h>
+#include <plat/regs-ac97.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
[DMACH_XD0] = {
diff --git a/arch/arm/mach-s3c2440/mach-anubis.c b/arch/arm/mach-s3c2440/mach-anubis.c
index e2beca470484..b05d56e230a1 100644
--- a/arch/arm/mach-s3c2440/mach-anubis.c
+++ b/arch/arm/mach-s3c2440/mach-anubis.c
@@ -39,7 +39,8 @@
#include <mach/regs-gpio.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/nand.h>
+#include <plat/iic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -366,6 +367,8 @@ static struct sm501_initdata anubis_sm501_initdata = {
.mask = 0,
},
+ .devices = SM501_USE_GPIO,
+
/* set the SDRAM and bus clocks */
.mclk = 72 * MHZ,
.m1xclk = 144 * MHZ,
@@ -373,10 +376,12 @@ static struct sm501_initdata anubis_sm501_initdata = {
static struct sm501_platdata_gpio_i2c anubis_sm501_gpio_i2c[] = {
[0] = {
+ .bus_num = 1,
.pin_scl = 44,
.pin_sda = 45,
},
[1] = {
+ .bus_num = 2,
.pin_scl = 40,
.pin_sda = 41,
},
@@ -384,6 +389,7 @@ static struct sm501_platdata_gpio_i2c anubis_sm501_gpio_i2c[] = {
static struct sm501_platdata anubis_sm501_platdata = {
.init = &anubis_sm501_initdata,
+ .gpio_base = -1,
.gpio_i2c = anubis_sm501_gpio_i2c,
.gpio_i2c_nr = ARRAY_SIZE(anubis_sm501_gpio_i2c),
};
@@ -404,7 +410,7 @@ static struct platform_device *anubis_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_wdt,
&s3c_device_adc,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_nand,
&anubis_device_ide0,
@@ -468,6 +474,7 @@ static void __init anubis_map_io(void)
static void __init anubis_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(anubis_devices, ARRAY_SIZE(anubis_devices));
i2c_register_board_info(0, anubis_i2c_devs,
diff --git a/arch/arm/mach-s3c2440/mach-at2440evb.c b/arch/arm/mach-s3c2440/mach-at2440evb.c
index 66876c6f2f1c..0a6d0a5d961b 100644
--- a/arch/arm/mach-s3c2440/mach-at2440evb.c
+++ b/arch/arm/mach-s3c2440/mach-at2440evb.c
@@ -28,6 +28,7 @@
#include <asm/mach/irq.h>
#include <mach/hardware.h>
+#include <mach/fb.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
@@ -35,7 +36,8 @@
#include <mach/regs-gpio.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/nand.h>
+#include <plat/iic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -45,6 +47,7 @@
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <asm/plat-s3c24xx/mci.h>
static struct map_desc at2440evb_iodesc[] __initdata = {
/* Nothing here */
@@ -162,19 +165,60 @@ static struct platform_device at2440evb_device_eth = {
},
};
+static struct s3c24xx_mci_pdata at2440evb_mci_pdata = {
+ .gpio_detect = S3C2410_GPG10,
+};
+
+/* 7" LCD panel */
+
+static struct s3c2410fb_display at2440evb_lcd_cfg __initdata = {
+
+ .lcdcon5 = S3C2410_LCDCON5_FRM565 |
+ S3C2410_LCDCON5_INVVLINE |
+ S3C2410_LCDCON5_INVVFRAME |
+ S3C2410_LCDCON5_PWREN |
+ S3C2410_LCDCON5_HWSWP,
+
+ .type = S3C2410_LCDCON1_TFT,
+
+ .width = 800,
+ .height = 480,
+
+ .pixclock = 33333, /* HCLK 60 MHz, divisor 2 */
+ .xres = 800,
+ .yres = 480,
+ .bpp = 16,
+ .left_margin = 88,
+ .right_margin = 40,
+ .hsync_len = 128,
+ .upper_margin = 32,
+ .lower_margin = 11,
+ .vsync_len = 2,
+};
+
+static struct s3c2410fb_mach_info at2440evb_fb_info __initdata = {
+ .displays = &at2440evb_lcd_cfg,
+ .num_displays = 1,
+ .default_display = 0,
+};
+
static struct platform_device *at2440evb_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_wdt,
&s3c_device_adc,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_nand,
+ &s3c_device_sdi,
+ &s3c_device_lcd,
&at2440evb_device_eth,
};
static void __init at2440evb_map_io(void)
{
s3c_device_nand.dev.platform_data = &at2440evb_nand_info;
+ s3c_device_sdi.name = "s3c2440-sdi";
+ s3c_device_sdi.dev.platform_data = &at2440evb_mci_pdata;
s3c24xx_init_io(at2440evb_iodesc, ARRAY_SIZE(at2440evb_iodesc));
s3c24xx_init_clocks(16934400);
@@ -183,6 +227,9 @@ static void __init at2440evb_map_io(void)
static void __init at2440evb_init(void)
{
+ s3c24xx_fb_set_platdata(&at2440evb_fb_info);
+ s3c_i2c0_set_platdata(NULL);
+
platform_add_devices(at2440evb_devices, ARRAY_SIZE(at2440evb_devices));
}
diff --git a/arch/arm/mach-s3c2440/mach-nexcoder.c b/arch/arm/mach-s3c2440/mach-nexcoder.c
index a546307fd53d..7aeaa972d7f5 100644
--- a/arch/arm/mach-s3c2440/mach-nexcoder.c
+++ b/arch/arm/mach-s3c2440/mach-nexcoder.c
@@ -37,6 +37,7 @@
//#include <asm/debug-ll.h>
#include <mach/regs-gpio.h>
#include <plat/regs-serial.h>
+#include <plat/iic.h>
#include <plat/s3c2410.h>
#include <plat/s3c2440.h>
@@ -107,7 +108,7 @@ static struct platform_device *nexcoder_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c_device_camif,
@@ -142,6 +143,7 @@ static void __init nexcoder_map_io(void)
static void __init nexcoder_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(nexcoder_devices, ARRAY_SIZE(nexcoder_devices));
};
diff --git a/arch/arm/mach-s3c2440/mach-osiris.c b/arch/arm/mach-s3c2440/mach-osiris.c
index 2361d606abc5..41a00f57e5da 100644
--- a/arch/arm/mach-s3c2440/mach-osiris.c
+++ b/arch/arm/mach-s3c2440/mach-osiris.c
@@ -37,7 +37,8 @@
#include <mach/regs-gpio.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/nand.h>
+#include <plat/iic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -335,7 +336,7 @@ static struct i2c_board_info osiris_i2c_devs[] __initdata = {
/* Standard Osiris devices */
static struct platform_device *osiris_devices[] __initdata = {
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_wdt,
&s3c_device_nand,
&osiris_pcmcia,
@@ -398,6 +399,8 @@ static void __init osiris_init(void)
sysdev_class_register(&osiris_pm_sysclass);
sysdev_register(&osiris_pm_sysdev);
+ s3c_i2c0_set_platdata(NULL);
+
i2c_register_board_info(0, osiris_i2c_devs,
ARRAY_SIZE(osiris_i2c_devs));
diff --git a/arch/arm/mach-s3c2440/mach-rx3715.c b/arch/arm/mach-s3c2440/mach-rx3715.c
index 4d14c7cff892..12d378f84ad2 100644
--- a/arch/arm/mach-s3c2440/mach-rx3715.c
+++ b/arch/arm/mach-s3c2440/mach-rx3715.c
@@ -42,7 +42,7 @@
#include <mach/regs-lcd.h>
#include <mach/h1940.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/nand.h>
#include <mach/fb.h>
#include <plat/clock.h>
@@ -179,7 +179,7 @@ static struct platform_device *rx3715_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_nand,
};
diff --git a/arch/arm/mach-s3c2440/mach-smdk2440.c b/arch/arm/mach-s3c2440/mach-smdk2440.c
index fefeaaa4155f..db6eafbd4d90 100644
--- a/arch/arm/mach-s3c2440/mach-smdk2440.c
+++ b/arch/arm/mach-s3c2440/mach-smdk2440.c
@@ -37,6 +37,7 @@
#include <mach/idle.h>
#include <mach/fb.h>
+#include <plat/iic.h>
#include <plat/s3c2410.h>
#include <plat/s3c2440.h>
@@ -152,7 +153,7 @@ static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
- &s3c_device_i2c,
+ &s3c_device_i2c0,
&s3c_device_iis,
};
@@ -166,6 +167,7 @@ static void __init smdk2440_map_io(void)
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
diff --git a/arch/arm/mach-s3c2442/Kconfig b/arch/arm/mach-s3c2442/Kconfig
index 26d131a77074..b289d198020e 100644
--- a/arch/arm/mach-s3c2442/Kconfig
+++ b/arch/arm/mach-s3c2442/Kconfig
@@ -7,6 +7,7 @@
config CPU_S3C2442
bool
depends on ARCH_S3C2410
+ select CPU_ARM920T
select S3C2410_CLOCK
select S3C2410_GPIO
select S3C2410_PM if PM
diff --git a/arch/arm/mach-s3c2443/Kconfig b/arch/arm/mach-s3c2443/Kconfig
index 14252f573754..212141baebec 100644
--- a/arch/arm/mach-s3c2443/Kconfig
+++ b/arch/arm/mach-s3c2443/Kconfig
@@ -24,6 +24,7 @@ config MACH_SMDK2443
bool "SMDK2443"
select CPU_S3C2443
select MACH_SMDK
+ select S3C_DEV_HSMMC
help
Say Y here if you are using an SMDK2443
diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c
index f854e7385e3c..2785d69c95b0 100644
--- a/arch/arm/mach-s3c2443/clock.c
+++ b/arch/arm/mach-s3c2443/clock.c
@@ -29,7 +29,6 @@
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/mutex.h>
-#include <linux/delay.h>
#include <linux/serial_core.h>
#include <linux/io.h>
@@ -39,6 +38,8 @@
#include <mach/regs-s3c2443-clock.h>
+#include <plat/cpu-freq.h>
+
#include <plat/s3c2443.h>
#include <plat/clock.h>
#include <plat/cpu.h>
@@ -145,12 +146,6 @@ static unsigned long s3c2443_roundrate_clksrc256(struct clk *clk,
/* clock selections */
-/* CPU EXTCLK input */
-static struct clk clk_ext = {
- .name = "ext",
- .id = -1,
-};
-
static struct clk clk_mpllref = {
.name = "mpllref",
.parent = &clk_xtal,
@@ -165,14 +160,6 @@ static struct clk clk_mpll = {
};
#endif
-static struct clk clk_epllref;
-
-static struct clk clk_epll = {
- .name = "epll",
- .parent = &clk_epllref,
- .id = -1,
-};
-
static struct clk clk_i2s_ext = {
.name = "i2s-ext",
.id = -1,
@@ -1011,22 +998,20 @@ static struct clk *clks[] __initdata = {
&clk_prediv,
};
-void __init s3c2443_init_clocks(int xtal)
+void __init_or_cpufreq s3c2443_setup_clocks(void)
{
- unsigned long epllcon = __raw_readl(S3C2443_EPLLCON);
unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON);
unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0);
+ struct clk *xtal_clk;
+ unsigned long xtal;
unsigned long pll;
unsigned long fclk;
unsigned long hclk;
unsigned long pclk;
- struct clk *clkp;
- int ret;
- int ptr;
- /* s3c2443 parents h and p clocks from prediv */
- clk_h.parent = &clk_prediv;
- clk_p.parent = &clk_prediv;
+ xtal_clk = clk_get(NULL, "xtal");
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
pll = s3c2443_get_mpll(mpllcon, xtal);
clk_msysclk.rate = pll;
@@ -1036,13 +1021,29 @@ void __init s3c2443_init_clocks(int xtal)
hclk /= s3c2443_get_hdiv(clkdiv0);
pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1);
- s3c24xx_setup_clocks(xtal, fclk, hclk, pclk);
+ s3c24xx_setup_clocks(fclk, hclk, pclk);
printk("S3C2443: mpll %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n",
(mpllcon & S3C2443_PLLCON_OFF) ? "off":"on",
print_mhz(pll), print_mhz(fclk),
print_mhz(hclk), print_mhz(pclk));
+ s3c24xx_setup_clocks(fclk, hclk, pclk);
+}
+
+void __init s3c2443_init_clocks(int xtal)
+{
+ struct clk *clkp;
+ unsigned long epllcon = __raw_readl(S3C2443_EPLLCON);
+ int ret;
+ int ptr;
+
+ /* s3c2443 parents h and p clocks from prediv */
+ clk_h.parent = &clk_prediv;
+ clk_p.parent = &clk_prediv;
+
+ s3c24xx_register_baseclocks(xtal);
+ s3c2443_setup_clocks();
s3c2443_clk_initparents();
for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
@@ -1056,7 +1057,7 @@ void __init s3c2443_init_clocks(int xtal)
}
clk_epll.rate = s3c2443_get_epll(epllcon, xtal);
-
+ clk_epll.parent = &clk_epllref;
clk_usb_bus.parent = &clk_usb_bus_host;
/* ensure usb bus clock is within correct rate of 48MHz */
@@ -1105,4 +1106,6 @@ void __init s3c2443_init_clocks(int xtal)
(clkp->enable)(clkp, 0);
}
+
+ s3c_pwmclk_init();
}
diff --git a/arch/arm/mach-s3c2443/dma.c b/arch/arm/mach-s3c2443/dma.c
index f73ccb25ff94..2a58a4d5aa5a 100644
--- a/arch/arm/mach-s3c2443/dma.c
+++ b/arch/arm/mach-s3c2443/dma.c
@@ -18,7 +18,6 @@
#include <linux/serial_core.h>
#include <linux/io.h>
-#include <asm/dma.h>
#include <mach/dma.h>
#include <plat/dma.h>
@@ -26,12 +25,12 @@
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-ac97.h>
+#include <plat/regs-ac97.h>
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#define MAP(x) { \
[0] = (x) | DMA_CH_VALID, \
diff --git a/arch/arm/mach-s3c2443/mach-smdk2443.c b/arch/arm/mach-s3c2443/mach-smdk2443.c
index a7fe65f3dcc1..039a46243105 100644
--- a/arch/arm/mach-s3c2443/mach-smdk2443.c
+++ b/arch/arm/mach-s3c2443/mach-smdk2443.c
@@ -37,6 +37,7 @@
#include <mach/idle.h>
#include <mach/fb.h>
+#include <plat/iic.h>
#include <plat/s3c2410.h>
#include <plat/s3c2440.h>
@@ -103,8 +104,8 @@ static struct s3c2410_uartcfg smdk2443_uartcfgs[] __initdata = {
static struct platform_device *smdk2443_devices[] __initdata = {
&s3c_device_wdt,
- &s3c_device_i2c,
- &s3c_device_hsmmc,
+ &s3c_device_i2c0,
+ &s3c_device_hsmmc0,
};
static void __init smdk2443_map_io(void)
@@ -116,6 +117,7 @@ static void __init smdk2443_map_io(void)
static void __init smdk2443_machine_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2443_devices, ARRAY_SIZE(smdk2443_devices));
smdk_machine_init();
}
diff --git a/arch/arm/mach-s3c2443/s3c2443.c b/arch/arm/mach-s3c2443/s3c2443.c
index bbeddf9ddcb1..ce2ec3298930 100644
--- a/arch/arm/mach-s3c2443/s3c2443.c
+++ b/arch/arm/mach-s3c2443/s3c2443.c
@@ -81,10 +81,9 @@ void __init s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no)
* machine specific initialisation.
*/
-void __init s3c2443_map_io(struct map_desc *mach_desc, int mach_size)
+void __init s3c2443_map_io(void)
{
iotable_init(s3c2443_iodesc, ARRAY_SIZE(s3c2443_iodesc));
- iotable_init(mach_desc, mach_size);
}
/* need to register class before we actually register the device, and
diff --git a/arch/arm/mach-s3c24a0/include/mach/debug-macro.S b/arch/arm/mach-s3c24a0/include/mach/debug-macro.S
new file mode 100644
index 000000000000..f0ef0ab475f6
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/debug-macro.S
@@ -0,0 +1,28 @@
+/* arch/arm/mach-s3c2410/include/mach/debug-macro.S
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* pull in the relevant register and map files. */
+
+#include <mach/map.h>
+#include <plat/regs-serial.h>
+
+ .macro addruart, rx
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1
+ ldreq \rx, = S3C24XX_PA_UART
+ ldrne \rx, = S3C24XX_VA_UART
+#if CONFIG_DEBUG_S3C_UART != 0
+ add \rx, \rx, #(S3C2410_UART1_OFF * CONFIG_DEBUG_S3C_UART)
+#endif
+ .endm
+
+/* include the reset of the code which will do the work, we're only
+ * compiling for a single cpu processor type so the default of s3c2440
+ * will be fine with us.
+ */
+
+#include <plat/debug-macro.S>
diff --git a/arch/arm/mach-s3c24a0/include/mach/irqs.h b/arch/arm/mach-s3c24a0/include/mach/irqs.h
new file mode 100644
index 000000000000..ae8c0e359783
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/irqs.h
@@ -0,0 +1,115 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/irqs.h
+ *
+ * Copyright (c) 2003-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+
+#ifndef __ASM_ARCH_24A0_IRQS_H
+#define __ASM_ARCH_24A0_IRQS_H __FILE__
+
+#define IRQ_EINT0t2 S3C2410_IRQ(0) /* 16 */
+/* for generic entry-macro.S */
+#define IRQ_EINT0 IRQ_EINT0t2
+
+#define IRQ_EINT3t6 S3C2410_IRQ(1)
+#define IRQ_EINT7t10 S3C2410_IRQ(2)
+#define IRQ_EINT11t14 S3C2410_IRQ(3)
+#define IRQ_EINT15t18 S3C2410_IRQ(4) /* 20 */
+#define IRQ_TICK S3C2410_IRQ(5)
+#define IRQ_DCTQ S3C2410_IRQ(6)
+#define IRQ_MC S3C2410_IRQ(7)
+#define IRQ_ME S3C2410_IRQ(8) /* 24 */
+#define IRQ_KEYPAD S3C2410_IRQ(9)
+#define IRQ_TIMER0 S3C2410_IRQ(10)
+#define IRQ_TIMER1 S3C2410_IRQ(11)
+#define IRQ_TIMER2 S3C2410_IRQ(12)
+#define IRQ_TIMER3_4 S3C2410_IRQ(13)
+#define IRQ_OS_TIMER IRQ_TIMER3_4
+#define IRQ_LCD S3C2410_IRQ(14)
+#define IRQ_CAM_C S3C2410_IRQ(15)
+#define IRQ_WDT_BATFLT S3C2410_IRQ(16) /* 32 */
+#define IRQ_UART0 S3C2410_IRQ(17)
+#define IRQ_CAM_P S3C2410_IRQ(18)
+#define IRQ_MODEM S3C2410_IRQ(19)
+#define IRQ_DMA S3C2410_IRQ(20)
+#define IRQ_SDI S3C2410_IRQ(21)
+#define IRQ_SPI0 S3C2410_IRQ(22)
+#define IRQ_UART1 S3C2410_IRQ(23)
+#define IRQ_AC97_NFLASH S3C2410_IRQ(24) /* 40 */
+#define IRQ_USBD S3C2410_IRQ(25)
+#define IRQ_USBH S3C2410_IRQ(26)
+#define IRQ_IIC S3C2410_IRQ(27)
+#define IRQ_IRDA_MSTICK S3C2410_IRQ(28) /* 44 */
+#define IRQ_VLX_SPI1 S3C2410_IRQ(29)
+#define IRQ_RTC S3C2410_IRQ(30) /* 46 */
+#define IRQ_ADC_PEN S3C2410_IRQ(31)
+
+/* interrupts generated from the external interrupts sources */
+#define IRQ_EINT00 S3C2410_IRQ(32) /* 48 */
+#define IRQ_EINT1 S3C2410_IRQ(33)
+#define IRQ_EINT2 S3C2410_IRQ(34)
+#define IRQ_EINT3 S3C2410_IRQ(35)
+#define IRQ_EINT4 S3C2410_IRQ(36)
+#define IRQ_EINT5 S3C2410_IRQ(37)
+#define IRQ_EINT6 S3C2410_IRQ(38)
+#define IRQ_EINT7 S3C2410_IRQ(39)
+#define IRQ_EINT8 S3C2410_IRQ(40)
+#define IRQ_EINT9 S3C2410_IRQ(41)
+#define IRQ_EINT10 S3C2410_IRQ(42)
+#define IRQ_EINT11 S3C2410_IRQ(43)
+#define IRQ_EINT12 S3C2410_IRQ(44)
+#define IRQ_EINT13 S3C2410_IRQ(45)
+#define IRQ_EINT14 S3C2410_IRQ(46)
+#define IRQ_EINT15 S3C2410_IRQ(47)
+#define IRQ_EINT16 S3C2410_IRQ(48)
+#define IRQ_EINT17 S3C2410_IRQ(49)
+#define IRQ_EINT18 S3C2410_IRQ(50)
+
+/* SUB IRQS */
+#define IRQ_S3CUART_RX0 S3C2410_IRQ(51) /* 67 */
+#define IRQ_S3CUART_TX0 S3C2410_IRQ(52)
+#define IRQ_S3CUART_ERR0 S3C2410_IRQ(53)
+
+#define IRQ_S3CUART_RX1 S3C2410_IRQ(54)
+#define IRQ_S3CUART_TX1 S3C2410_IRQ(55)
+#define IRQ_S3CUART_ERR1 S3C2410_IRQ(56)
+
+#define IRQ_S3CUART_RX2 (0x0)
+#define IRQ_S3CUART_TX2 (0x0)
+#define IRQ_S3CUART_ERR2 (0x0)
+
+
+#define IRQ_IRDA S3C2410_IRQ(57)
+#define IRQ_MSTICK S3C2410_IRQ(58)
+#define IRQ_RESERVED0 S3C2410_IRQ(59)
+#define IRQ_RESERVED1 S3C2410_IRQ(60)
+#define IRQ_RESERVED2 S3C2410_IRQ(61)
+#define IRQ_TIMER3 S3C2410_IRQ(62)
+#define IRQ_TIMER4 S3C2410_IRQ(63)
+#define IRQ_WDT S3C2410_IRQ(64)
+#define IRQ_BATFLT S3C2410_IRQ(65)
+#define IRQ_POST S3C2410_IRQ(66)
+#define IRQ_DISP_FIFO S3C2410_IRQ(67)
+#define IRQ_PENUP S3C2410_IRQ(68)
+#define IRQ_PENDN S3C2410_IRQ(69)
+#define IRQ_ADC S3C2410_IRQ(70)
+#define IRQ_DISP_FRAME S3C2410_IRQ(71)
+#define IRQ_NFLASH S3C2410_IRQ(72)
+#define IRQ_AC97 S3C2410_IRQ(73)
+#define IRQ_SPI1 S3C2410_IRQ(74)
+#define IRQ_VLX S3C2410_IRQ(75)
+#define IRQ_DMA0 S3C2410_IRQ(76)
+#define IRQ_DMA1 S3C2410_IRQ(77)
+#define IRQ_DMA2 S3C2410_IRQ(78)
+#define IRQ_DMA3 S3C2410_IRQ(79)
+
+#define IRQ_TC (0x0)
+
+#define NR_IRQS (IRQ_DMA3+1)
+
+#endif /* __ASM_ARCH_24A0_IRQS_H */
diff --git a/arch/arm/mach-s3c24a0/include/mach/map.h b/arch/arm/mach-s3c24a0/include/mach/map.h
new file mode 100644
index 000000000000..a01132717e34
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/map.h
@@ -0,0 +1,85 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/map.h
+ *
+ * Copyright 2003,2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24A0 - Memory map definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_24A0_MAP_H
+#define __ASM_ARCH_24A0_MAP_H __FILE__
+
+#include <plat/map-base.h>
+#include <plat/map.h>
+
+#define S3C24A0_PA_IO_BASE (0x40000000)
+#define S3C24A0_PA_CLKPWR (0x40000000)
+#define S3C24A0_PA_IRQ (0x40200000)
+#define S3C24A0_PA_DMA (0x40400000)
+#define S3C24A0_PA_MEMCTRL (0x40C00000)
+#define S3C24A0_PA_NAND (0x40C00000)
+#define S3C24A0_PA_SROM (0x40C20000)
+#define S3C24A0_PA_SDRAM (0x40C40000)
+#define S3C24A0_PA_BUSM (0x40CE0000)
+#define S3C24A0_PA_USBHOST (0x41000000)
+#define S3C24A0_PA_MODEMIF (0x41180000)
+#define S3C24A0_PA_IRDA (0x41800000)
+#define S3C24A0_PA_TIMER (0x44000000)
+#define S3C24A0_PA_WATCHDOG (0x44100000)
+#define S3C24A0_PA_RTC (0x44200000)
+#define S3C24A0_PA_UART (0x44400000)
+#define S3C24A0_PA_UART0 (S3C24A0_PA_UART)
+#define S3C24A0_PA_UART1 (S3C24A0_PA_UART + 0x4000)
+#define S3C24A0_PA_SPI (0x44500000)
+#define S3C24A0_PA_IIC (0x44600000)
+#define S3C24A0_PA_IIS (0x44700000)
+#define S3C24A0_PA_GPIO (0x44800000)
+#define S3C24A0_PA_KEYIF (0x44900000)
+#define S3C24A0_PA_USBDEV (0x44A00000)
+#define S3C24A0_PA_AC97 (0x45000000)
+#define S3C24A0_PA_ADC (0x45800000)
+#define S3C24A0_PA_SDI (0x46000000)
+#define S3C24A0_PA_MS (0x46100000)
+#define S3C24A0_PA_LCD (0x4A000000)
+#define S3C24A0_PA_VPOST (0x4A100000)
+
+/* physical addresses of all the chip-select areas */
+
+#define S3C24A0_CS0 (0x00000000)
+#define S3C24A0_CS1 (0x04000000)
+#define S3C24A0_CS2 (0x08000000)
+#define S3C24A0_CS3 (0x0C000000)
+#define S3C24A0_CS4 (0x10000000)
+#define S3C24A0_CS5 (0x40000000)
+
+#define S3C24A0_SDRAM_PA (S3C24A0_CS4)
+
+/* Use a single interface for common resources between S3C24XX cpus */
+
+#define S3C24XX_PA_IRQ S3C24A0_PA_IRQ
+#define S3C24XX_PA_MEMCTRL S3C24A0_PA_MEMCTRL
+#define S3C24XX_PA_USBHOST S3C24A0_PA_USBHOST
+#define S3C24XX_PA_DMA S3C24A0_PA_DMA
+#define S3C24XX_PA_CLKPWR S3C24A0_PA_CLKPWR
+#define S3C24XX_PA_LCD S3C24A0_PA_LCD
+#define S3C24XX_PA_UART S3C24A0_PA_UART
+#define S3C24XX_PA_TIMER S3C24A0_PA_TIMER
+#define S3C24XX_PA_USBDEV S3C24A0_PA_USBDEV
+#define S3C24XX_PA_WATCHDOG S3C24A0_PA_WATCHDOG
+#define S3C24XX_PA_IIS S3C24A0_PA_IIS
+#define S3C24XX_PA_GPIO S3C24A0_PA_GPIO
+#define S3C24XX_PA_RTC S3C24A0_PA_RTC
+#define S3C24XX_PA_ADC S3C24A0_PA_ADC
+#define S3C24XX_PA_SPI S3C24A0_PA_SPI
+#define S3C24XX_PA_SDI S3C24A0_PA_SDI
+#define S3C24XX_PA_NAND S3C24A0_PA_NAND
+
+#define S3C_PA_UART S3C24A0_PA_UART
+#define S3C_PA_IIC S3C24A0_PA_IIC
+
+#endif /* __ASM_ARCH_24A0_MAP_H */
diff --git a/arch/arm/mach-s3c24a0/include/mach/memory.h b/arch/arm/mach-s3c24a0/include/mach/memory.h
new file mode 100644
index 000000000000..585211ca0187
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/memory.h
@@ -0,0 +1,19 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/memory.h
+ * from linux/include/asm-arm/arch-rpc/memory.h
+ *
+ * Copyright (C) 1996,1997,1998 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_24A0_MEMORY_H
+#define __ASM_ARCH_24A0_MEMORY_H __FILE__
+
+#define PHYS_OFFSET UL(0x10000000)
+
+#define __virt_to_bus(x) __virt_to_phys(x)
+#define __bus_to_virt(x) __phys_to_virt(x)
+
+#endif
diff --git a/arch/arm/mach-s3c24a0/include/mach/regs-clock.h b/arch/arm/mach-s3c24a0/include/mach/regs-clock.h
new file mode 100644
index 000000000000..af2abd756c30
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/regs-clock.h
@@ -0,0 +1,88 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/regs-clock.h
+ *
+ * Copyright (c) 2003,2004,2005,2006 Simtec Electronics <linux@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C24A0 clock register definitions
+*/
+
+#ifndef __ASM_ARCH_24A0_REGS_CLOCK_H
+#define __ASM_ARCH_24A0_REGS_CLOCK_H __FILE__
+
+#define S3C24A0_MPLLCON S3C2410_CLKREG(0x10)
+#define S3C24A0_UPLLCON S3C2410_CLKREG(0x14)
+#define S3C24A0_CLKCON S3C2410_CLKREG(0x20)
+#define S3C24A0_CLKSRC S3C2410_CLKREG(0x24)
+#define S3C24A0_CLKDIVN S3C2410_CLKREG(0x28)
+
+/* CLKCON register bits */
+
+#define S3C24A0_CLKCON_VLX (1<<29)
+#define S3C24A0_CLKCON_VPOST (1<<28)
+#define S3C24A0_CLKCON_WDT (1<<27) /* reserved */
+#define S3C24A0_CLKCON_MPEGDCTQ (1<<26)
+#define S3C24A0_CLKCON_VPOSTIF (1<<25)
+#define S3C24A0_CLKCON_MPEG4IF (1<<24)
+#define S3C24A0_CLKCON_CAM_UPLL (1<<23)
+#define S3C24A0_CLKCON_LCDC (1<<22)
+#define S3C24A0_CLKCON_CAM_HCLK (1<<21)
+#define S3C24A0_CLKCON_MPEG4 (1<<20)
+#define S3C24A0_CLKCON_KEYPAD (1<<19)
+#define S3C24A0_CLKCON_ADC (1<<18)
+#define S3C24A0_CLKCON_SDI (1<<17)
+#define S3C24A0_CLKCON_MS (1<<16) /* memory stick */
+#define S3C24A0_CLKCON_USBD (1<<15)
+#define S3C24A0_CLKCON_GPIO (1<<14)
+#define S3C24A0_CLKCON_IIS (1<<13)
+#define S3C24A0_CLKCON_IIC (1<<12)
+#define S3C24A0_CLKCON_SPI (1<<11)
+#define S3C24A0_CLKCON_UART1 (1<<10)
+#define S3C24A0_CLKCON_UART0 (1<<9)
+#define S3C24A0_CLKCON_PWMT (1<<8)
+#define S3C24A0_CLKCON_USBH (1<<7)
+#define S3C24A0_CLKCON_AC97 (1<<6)
+#define S3C24A0_CLKCON_IrDA (1<<4)
+#define S3C24A0_CLKCON_IDLE (1<<2)
+#define S3C24A0_CLKCON_MON (1<<1)
+#define S3C24A0_CLKCON_STOP (1<<0)
+
+/* CLKSRC register bits */
+
+#define S3C24A0_CLKSRC_OSC (1<<8) /* CLKSRC */
+#define S3C24A0_CLKSRC_UPLL (1<<7)
+#define S3C24A0_CLKSRC_MPLL (1<<5)
+#define S3C24A0_CLKSRC_EXT (1<<4)
+
+/* Use a single interface with the common code, for s3c24xx */
+
+#define S3C2410_MPLLCON S3C24A0_MPLLCON
+#define S3C2410_UPLLCON S3C24A0_UPLLCON
+#define S3C2410_CLKCON S3C24A0_CLKCON
+#define S3C2410_CLKSLOW S3C24A0_CLKSRC
+#define S3C2410_CLKDIVN S3C24A0_CLKDIVN
+
+#define S3C2410_CLKCON_IDLE S3C24A0_CLKCON_IDLE
+#define S3C2410_CLKCON_POWER S3C24A0_CLKCON_STOP
+#define S3C2410_CLKCON_LCDC S3C24A0_CLKCON_LCDC
+#define S3C2410_CLKCON_USBH S3C24A0_CLKCON_USBH
+#define S3C2410_CLKCON_USBD S3C24A0_CLKCON_USBD
+#define S3C2410_CLKCON_PWMT S3C24A0_CLKCON_PWMT
+#define S3C2410_CLKCON_SDI S3C24A0_CLKCON_SDI
+#define S3C2410_CLKCON_UART0 S3C24A0_CLKCON_UART0
+#define S3C2410_CLKCON_UART1 S3C24A0_CLKCON_UART1
+#define S3C2410_CLKCON_GPIO S3C24A0_CLKCON_GPIO
+#define S3C2410_CLKCON_ADC S3C24A0_CLKCON_ADC
+#define S3C2410_CLKCON_IIC S3C24A0_CLKCON_IIC
+#define S3C2410_CLKCON_IIS S3C24A0_CLKCON_IIS
+#define S3C2410_CLKCON_SPI S3C24A0_CLKCON_SPI
+
+#define S3C2410_CLKSLOW_UCLK_OFF S3C24A0_CLKSRC_UPLL
+#define S3C2410_CLKSLOW_MPLL_OFF S3C24A0_CLKSRC_MPLL
+#define S3C2410_CLKSLOW_SLOW (0xFF)
+#define S3C2410_CLKSLOW_GET_SLOWVAL(x) (0x1)
+
+#endif /* __ASM_ARCH_24A0_REGS_CLOCK_H */
diff --git a/arch/arm/mach-s3c24a0/include/mach/regs-irq.h b/arch/arm/mach-s3c24a0/include/mach/regs-irq.h
new file mode 100644
index 000000000000..6086f6f189eb
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/regs-irq.h
@@ -0,0 +1,25 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/regs-irq.h
+ *
+ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
+ * http://www.simtec.co.uk/products/SWLINUX/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+
+#ifndef ___ASM_ARCH_24A0_REGS_IRQ_H
+#define ___ASM_ARCH_24A0_REGS_IRQ_H __FILE__
+
+
+#define S3C2410_EINTMASK S3C2410_EINTREG(0x034)
+#define S3C2410_EINTPEND S3C2410_EINTREG(0X038)
+
+#define S3C24XX_EINTMASK S3C24XX_EINTREG(0x034)
+#define S3C24XX_EINTPEND S3C24XX_EINTREG(0X038)
+
+#endif /* __ASM_ARCH_24A0_REGS_IRQ_H */
+
+
+
diff --git a/arch/arm/mach-s3c24a0/include/mach/system.h b/arch/arm/mach-s3c24a0/include/mach/system.h
new file mode 100644
index 000000000000..bd1bd1957656
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/system.h
@@ -0,0 +1,25 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/system.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24A0 - System function defines and includes
+ *
+ * This program is free software; you can 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 <mach/hardware.h>
+#include <asm/io.h>
+
+#include <mach/map.h>
+
+static void arch_idle(void)
+{
+ /* currently no specific idle support. */
+}
+
+void (*s3c24xx_reset_hook)(void);
+
+#include <asm/plat-s3c24xx/system-reset.h>
diff --git a/arch/arm/mach-s3c24a0/include/mach/tick.h b/arch/arm/mach-s3c24a0/include/mach/tick.h
new file mode 100644
index 000000000000..9dea8ba6fb72
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/tick.h
@@ -0,0 +1,15 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/tick.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C24A0 - timer tick support
+ */
+
+#define SUBSRC_TIMER4 (1 << (IRQ_TIMER4 - IRQ_S3CUART_RX0))
+
+static inline int s3c24xx_ostimer_pending(void)
+{
+ return __raw_readl(S3C2410_SUBSRCPND) & SUBSRC_TIMER4;
+}
diff --git a/arch/arm/mach-s3c24a0/include/mach/timex.h b/arch/arm/mach-s3c24a0/include/mach/timex.h
new file mode 100644
index 000000000000..98573424a016
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/timex.h
@@ -0,0 +1,18 @@
+/* linux/arch/arm/mach-s3c24a0/include/mach/timex.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 - time parameters
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_TIMEX_H
+#define __ASM_ARCH_TIMEX_H
+
+#define CLOCK_TICK_RATE 12000000
+
+#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-s3c24a0/include/mach/vmalloc.h b/arch/arm/mach-s3c24a0/include/mach/vmalloc.h
new file mode 100644
index 000000000000..4d4fe4849589
--- /dev/null
+++ b/arch/arm/mach-s3c24a0/include/mach/vmalloc.h
@@ -0,0 +1,17 @@
+/* linux/include/asm-arm/arch-s3c24ao/vmalloc.h
+ *
+ * Copyright 2008 Simtec Electronics <linux@simtec.co.uk>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C24A0 vmalloc definition
+*/
+
+#ifndef __ASM_ARCH_VMALLOC_H
+#define __ASM_ARCH_VMALLOC_H
+
+#define VMALLOC_END (0xE0000000)
+
+#endif /* __ASM_ARCH_VMALLOC_H */
diff --git a/arch/arm/mach-s3c6400/Kconfig b/arch/arm/mach-s3c6400/Kconfig
new file mode 100644
index 000000000000..6da82b5c09ba
--- /dev/null
+++ b/arch/arm/mach-s3c6400/Kconfig
@@ -0,0 +1,8 @@
+# arch/arm/mach-s3c6400/Kconfig
+#
+# Copyright 2008 Openmoko, Inc.
+# Simtec Electronics, Ben Dooks <ben@simtec.co.uk>
+#
+# Licensed under GPLv2
+
+# Currently nothing here, this will be added later
diff --git a/arch/arm/mach-s3c6400/Makefile b/arch/arm/mach-s3c6400/Makefile
new file mode 100644
index 000000000000..8f397db25b87
--- /dev/null
+++ b/arch/arm/mach-s3c6400/Makefile
@@ -0,0 +1,15 @@
+# arch/arm/mach-s3c6400/Makefile
+#
+# Copyright 2008 Openmoko, Inc.
+# Copyright 2008 Simtec Electronics
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Core support for S3C6400 system
+
+obj-n += blank.o
diff --git a/arch/arm/mach-s3c6400/Makefile.boot b/arch/arm/mach-s3c6400/Makefile.boot
new file mode 100644
index 000000000000..ba41fdc0a586
--- /dev/null
+++ b/arch/arm/mach-s3c6400/Makefile.boot
@@ -0,0 +1,2 @@
+ zreladdr-y := 0x50008000
+params_phys-y := 0x50000100
diff --git a/arch/arm/mach-s3c6400/include/mach/debug-macro.S b/arch/arm/mach-s3c6400/include/mach/debug-macro.S
new file mode 100644
index 000000000000..b18ac5266dfc
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/debug-macro.S
@@ -0,0 +1,39 @@
+/* arch/arm/mach-s3c6400/include/mach/debug-macro.S
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* pull in the relevant register and map files. */
+
+#include <mach/map.h>
+#include <plat/regs-serial.h>
+
+ /* note, for the boot process to work we have to keep the UART
+ * virtual address aligned to an 1MiB boundary for the L1
+ * mapping the head code makes. We keep the UART virtual address
+ * aligned and add in the offset when we load the value here.
+ */
+
+ .macro addruart, rx
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1
+ ldreq \rx, = S3C_PA_UART
+ ldrne \rx, = (S3C_VA_UART + S3C_PA_UART & 0xfffff)
+#if CONFIG_DEBUG_S3C_UART != 0
+ add \rx, \rx, #(0x400 * CONFIG_DEBUG_S3C_UART)
+#endif
+ .endm
+
+/* include the reset of the code which will do the work, we're only
+ * compiling for a single cpu processor type so the default of s3c2440
+ * will be fine with us.
+ */
+
+#include <plat/debug-macro.S>
diff --git a/arch/arm/mach-s3c6400/include/mach/dma.h b/arch/arm/mach-s3c6400/include/mach/dma.h
new file mode 100644
index 000000000000..9771ac2cb07e
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/dma.h
@@ -0,0 +1,16 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/dma.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 - DMA support
+ */
+
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H __FILE__
+
+/* currently nothing here, placeholder */
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/entry-macro.S b/arch/arm/mach-s3c6400/include/mach/entry-macro.S
new file mode 100644
index 000000000000..fbd90d2cf355
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/entry-macro.S
@@ -0,0 +1,44 @@
+/* arch/arm/mach-s3c6400/include/mach/entry-macro.S
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Low-level IRQ helper macros for the Samsung S3C64XX series
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+*/
+
+#include <asm/hardware/vic.h>
+#include <mach/map.h>
+#include <plat/irqs.h>
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ ldr \base, =S3C_VA_VIC0
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ @ check the vic0
+ mov \irqnr, # S3C_IRQ_OFFSET + 31
+ ldr \irqstat, [ \base, # VIC_IRQ_STATUS ]
+ teq \irqstat, #0
+
+ @ otherwise try vic1
+ addeq \tmp, \base, #(S3C_VA_VIC1 - S3C_VA_VIC0)
+ addeq \irqnr, \irqnr, #32
+ ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ]
+ teqeq \irqstat, #0
+
+ clzne \irqstat, \irqstat
+ subne \irqnr, \irqnr, \irqstat
+ .endm
diff --git a/arch/arm/mach-s3c6400/include/mach/gpio-core.h b/arch/arm/mach-s3c6400/include/mach/gpio-core.h
new file mode 100644
index 000000000000..d89aae68b0a5
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/gpio-core.h
@@ -0,0 +1,21 @@
+/* arch/arm/mach-s3c6400/include/mach/gpio-core.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - GPIO core support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_GPIO_CORE_H
+#define __ASM_ARCH_GPIO_CORE_H __FILE__
+
+/* currently we just include the platform support */
+#include <plat/gpio-core.h>
+
+#endif /* __ASM_ARCH_GPIO_CORE_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/gpio.h b/arch/arm/mach-s3c6400/include/mach/gpio.h
new file mode 100644
index 000000000000..e8e35e8fe731
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/gpio.h
@@ -0,0 +1,96 @@
+/* arch/arm/mach-s3c6400/include/mach/gpio.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C6400 - GPIO lib support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+/* GPIO bank sizes */
+#define S3C64XX_GPIO_A_NR (8)
+#define S3C64XX_GPIO_B_NR (7)
+#define S3C64XX_GPIO_C_NR (8)
+#define S3C64XX_GPIO_D_NR (5)
+#define S3C64XX_GPIO_E_NR (5)
+#define S3C64XX_GPIO_F_NR (16)
+#define S3C64XX_GPIO_G_NR (7)
+#define S3C64XX_GPIO_H_NR (10)
+#define S3C64XX_GPIO_I_NR (16)
+#define S3C64XX_GPIO_J_NR (12)
+#define S3C64XX_GPIO_K_NR (16)
+#define S3C64XX_GPIO_L_NR (15)
+#define S3C64XX_GPIO_M_NR (6)
+#define S3C64XX_GPIO_N_NR (16)
+#define S3C64XX_GPIO_O_NR (16)
+#define S3C64XX_GPIO_P_NR (15)
+#define S3C64XX_GPIO_Q_NR (9)
+
+/* GPIO bank numbes */
+
+/* CONFIG_S3C_GPIO_SPACE allows the user to select extra
+ * space for debugging purposes so that any accidental
+ * change from one gpio bank to another can be caught.
+*/
+
+#define S3C64XX_GPIO_NEXT(__gpio) \
+ ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
+
+enum s3c_gpio_number {
+ S3C64XX_GPIO_A_START = 0,
+ S3C64XX_GPIO_B_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_A),
+ S3C64XX_GPIO_C_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_B),
+ S3C64XX_GPIO_D_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_C),
+ S3C64XX_GPIO_E_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_D),
+ S3C64XX_GPIO_F_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_E),
+ S3C64XX_GPIO_G_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_F),
+ S3C64XX_GPIO_H_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_G),
+ S3C64XX_GPIO_I_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_H),
+ S3C64XX_GPIO_J_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_I),
+ S3C64XX_GPIO_K_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_J),
+ S3C64XX_GPIO_L_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_K),
+ S3C64XX_GPIO_M_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_L),
+ S3C64XX_GPIO_N_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_M),
+ S3C64XX_GPIO_O_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_N),
+ S3C64XX_GPIO_P_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_O),
+ S3C64XX_GPIO_Q_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_P),
+};
+
+/* S3C64XX GPIO number definitions. */
+
+#define S3C64XX_GPA(_nr) (S3C64XX_GPIO_A_START + (_nr))
+#define S3C64XX_GPB(_nr) (S3C64XX_GPIO_B_START + (_nr))
+#define S3C64XX_GPC(_nr) (S3C64XX_GPIO_C_START + (_nr))
+#define S3C64XX_GPD(_nr) (S3C64XX_GPIO_D_START + (_nr))
+#define S3C64XX_GPE(_nr) (S3C64XX_GPIO_E_START + (_nr))
+#define S3C64XX_GPF(_nr) (S3C64XX_GPIO_F_START + (_nr))
+#define S3C64XX_GPG(_nr) (S3C64XX_GPIO_G_START + (_nr))
+#define S3C64XX_GPH(_nr) (S3C64XX_GPIO_H_START + (_nr))
+#define S3C64XX_GPI(_nr) (S3C64XX_GPIO_I_START + (_nr))
+#define S3C64XX_GPJ(_nr) (S3C64XX_GPIO_J_START + (_nr))
+#define S3C64XX_GPK(_nr) (S3C64XX_GPIO_K_START + (_nr))
+#define S3C64XX_GPL(_nr) (S3C64XX_GPIO_L_START + (_nr))
+#define S3C64XX_GPM(_nr) (S3C64XX_GPIO_M_START + (_nr))
+#define S3C64XX_GPN(_nr) (S3C64XX_GPIO_N_START + (_nr))
+#define S3C64XX_GPO(_nr) (S3C64XX_GPIO_O_START + (_nr))
+#define S3C64XX_GPP(_nr) (S3C64XX_GPIO_P_START + (_nr))
+#define S3C64XX_GPQ(_nr) (S3C64XX_GPIO_Q_START + (_nr))
+
+/* the end of the S3C64XX specific gpios */
+#define S3C64XX_GPIO_END (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1)
+#define S3C_GPIO_END S3C64XX_GPIO_END
+
+/* define the number of gpios we need to the one after the GPQ() range */
+#define ARCH_NR_GPIOS (S3C64XX_GPQ(S3C64XX_GPIO_Q_NR) + 1)
+
+#include <asm-generic/gpio.h>
diff --git a/arch/arm/mach-s3c6400/include/mach/hardware.h b/arch/arm/mach-s3c6400/include/mach/hardware.h
new file mode 100644
index 000000000000..862d033e57a4
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/hardware.h
@@ -0,0 +1,16 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/hardware.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 - Hardware support
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H __FILE__
+
+/* currently nothing here, placeholder */
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/irqs.h b/arch/arm/mach-s3c6400/include/mach/irqs.h
new file mode 100644
index 000000000000..b38c47cffc28
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/irqs.h
@@ -0,0 +1,20 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/irqs.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 - IRQ definitions
+ */
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H __FILE__
+
+#ifndef __ASM_ARM_IRQ_H
+#error "Do not include this directly, instead #include <asm/irq.h>"
+#endif
+
+#include <plat/irqs.h>
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h
new file mode 100644
index 000000000000..cff27d813fc6
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/map.h
@@ -0,0 +1,68 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/map.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C64XX - Memory map definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_MAP_H
+#define __ASM_ARCH_MAP_H __FILE__
+
+#include <plat/map-base.h>
+
+/* HSMMC units */
+#define S3C64XX_PA_HSMMC(x) (0x7C200000 + ((x) * 0x100000))
+#define S3C64XX_PA_HSMMC0 S3C64XX_PA_HSMMC(0)
+#define S3C64XX_PA_HSMMC1 S3C64XX_PA_HSMMC(1)
+#define S3C64XX_PA_HSMMC2 S3C64XX_PA_HSMMC(2)
+
+#define S3C_PA_UART (0x7F005000)
+#define S3C_PA_UART0 (S3C_PA_UART + 0x00)
+#define S3C_PA_UART1 (S3C_PA_UART + 0x400)
+#define S3C_PA_UART2 (S3C_PA_UART + 0x800)
+#define S3C_PA_UART3 (S3C_PA_UART + 0xC00)
+#define S3C_UART_OFFSET (0x400)
+
+/* See notes on UART VA mapping in debug-macro.S */
+#define S3C_VA_UARTx(x) (S3C_VA_UART + (S3C_PA_UART & 0xfffff) + ((x) * S3C_UART_OFFSET))
+
+#define S3C_VA_UART0 S3C_VA_UARTx(0)
+#define S3C_VA_UART1 S3C_VA_UARTx(1)
+#define S3C_VA_UART2 S3C_VA_UARTx(2)
+#define S3C_VA_UART3 S3C_VA_UARTx(3)
+
+#define S3C64XX_PA_FB (0x77100000)
+#define S3C64XX_PA_SYSCON (0x7E00F000)
+#define S3C64XX_PA_TIMER (0x7F006000)
+#define S3C64XX_PA_IIC0 (0x7F004000)
+#define S3C64XX_PA_IIC1 (0x7F00F000)
+
+#define S3C64XX_PA_GPIO (0x7F008000)
+#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)
+#define S3C64XX_SZ_GPIO SZ_4K
+
+#define S3C64XX_PA_SDRAM (0x50000000)
+#define S3C64XX_PA_VIC0 (0x71200000)
+#define S3C64XX_PA_VIC1 (0x71300000)
+
+/* place VICs close together */
+#define S3C_VA_VIC0 (S3C_VA_IRQ + 0x00)
+#define S3C_VA_VIC1 (S3C_VA_IRQ + 0x10000)
+
+/* compatibiltiy defines. */
+#define S3C_PA_TIMER S3C64XX_PA_TIMER
+#define S3C_PA_HSMMC0 S3C64XX_PA_HSMMC0
+#define S3C_PA_HSMMC1 S3C64XX_PA_HSMMC1
+#define S3C_PA_HSMMC2 S3C64XX_PA_HSMMC2
+#define S3C_PA_IIC S3C64XX_PA_IIC0
+#define S3C_PA_IIC1 S3C64XX_PA_IIC1
+#define S3C_PA_FB S3C64XX_PA_FB
+
+#endif /* __ASM_ARCH_6400_MAP_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/memory.h b/arch/arm/mach-s3c6400/include/mach/memory.h
new file mode 100644
index 000000000000..a3ac84a65480
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/memory.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-s3c6400/include/mach/memory.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#define PHYS_OFFSET UL(0x50000000)
+
+#endif
diff --git a/arch/arm/mach-s3c6400/include/mach/pwm-clock.h b/arch/arm/mach-s3c6400/include/mach/pwm-clock.h
new file mode 100644
index 000000000000..b25bedee0d52
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/pwm-clock.h
@@ -0,0 +1,56 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/pwm-clock.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64xx - pwm clock and timer support
+ */
+
+/**
+ * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
+ * @tcfg: The timer TCFG1 register bits shifted down to 0.
+ *
+ * Return true if the given configuration from TCFG1 is a TCLK instead
+ * any of the TDIV clocks.
+ */
+static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
+{
+ return tcfg >= S3C64XX_TCFG1_MUX_TCLK;
+}
+
+/**
+ * tcfg_to_divisor() - convert tcfg1 setting to a divisor
+ * @tcfg1: The tcfg1 setting, shifted down.
+ *
+ * Get the divisor value for the given tcfg1 setting. We assume the
+ * caller has already checked to see if this is not a TCLK source.
+ */
+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
+{
+ return 1 << tcfg1;
+}
+
+/**
+ * pwm_tdiv_has_div1() - does the tdiv setting have a /1
+ *
+ * Return true if we have a /1 in the tdiv setting.
+ */
+static inline unsigned int pwm_tdiv_has_div1(void)
+{
+ return 1;
+}
+
+/**
+ * pwm_tdiv_div_bits() - calculate TCFG1 divisor value.
+ * @div: The divisor to calculate the bit information for.
+ *
+ * Turn a divisor into the necessary bit field for TCFG1.
+ */
+static inline unsigned long pwm_tdiv_div_bits(unsigned int div)
+{
+ return ilog2(div);
+}
+
+#define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK
diff --git a/arch/arm/mach-s3c6400/include/mach/regs-fb.h b/arch/arm/mach-s3c6400/include/mach/regs-fb.h
new file mode 100644
index 000000000000..47019795ce06
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/regs-fb.h
@@ -0,0 +1,259 @@
+/* arch/arm/mach-s3c6400/include/mach/regs-fb.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C64XX - new-style framebuffer register definitions
+ *
+ * This is the register set for the new style framebuffer interface
+ * found from the S3C2443 onwards and specifically the S3C64XX series
+ * S3C6400 and S3C6410.
+ *
+ * The file contains the cpu specific items which change between whichever
+ * architecture is selected. See <plat/regs-fb.h> for the core definitions
+ * that are the same.
+ *
+ * This program is free software; you can 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 the core definitions here, in case we really do need to
+ * override them at a later date.
+*/
+
+#include <plat/regs-fb.h>
+
+#define S3C_FB_MAX_WIN (5) /* number of hardware windows available. */
+#define VIDCON1_FSTATUS_EVEN (1 << 15)
+
+/* Video timing controls */
+#define VIDTCON0 (0x10)
+#define VIDTCON1 (0x14)
+#define VIDTCON2 (0x18)
+
+/* Window position controls */
+
+#define WINCON(_win) (0x20 + ((_win) * 4))
+
+/* OSD1 and OSD4 do not have register D */
+
+#define VIDOSD_A(_win) (0x40 + ((_win) * 16))
+#define VIDOSD_B(_win) (0x44 + ((_win) * 16))
+#define VIDOSD_C(_win) (0x48 + ((_win) * 16))
+#define VIDOSD_D(_win) (0x4C + ((_win) * 16))
+
+/* Video buffer addresses */
+
+#define VIDW_BUF_START(_buff) (0xA0 + ((_buff) * 8))
+#define VIDW_BUF_START1(_buff) (0xA4 + ((_buff) * 8))
+#define VIDW_BUF_END(_buff) (0xD0 + ((_buff) * 8))
+#define VIDW_BUF_END1(_buff) (0xD4 + ((_buff) * 8))
+#define VIDW_BUF_SIZE(_buff) (0x100 + ((_buff) * 4))
+
+#define VIDINTCON0 (0x130)
+
+#define WxKEYCONy(_win, _con) ((0x140 + ((_win) * 8)) + ((_con) * 4))
+
+/* WINCONx */
+
+#define WINCONx_CSCWIDTH_MASK (0x3 << 26)
+#define WINCONx_CSCWIDTH_SHIFT (26)
+#define WINCONx_CSCWIDTH_WIDE (0x0 << 26)
+#define WINCONx_CSCWIDTH_NARROW (0x3 << 26)
+
+#define WINCONx_ENLOCAL (1 << 22)
+#define WINCONx_BUFSTATUS (1 << 21)
+#define WINCONx_BUFSEL (1 << 20)
+#define WINCONx_BUFAUTOEN (1 << 19)
+#define WINCONx_YCbCr (1 << 13)
+
+#define WINCON1_LOCALSEL_CAMIF (1 << 23)
+
+#define WINCON2_LOCALSEL_CAMIF (1 << 23)
+#define WINCON2_BLD_PIX (1 << 6)
+
+#define WINCON2_ALPHA_SEL (1 << 1)
+#define WINCON2_BPPMODE_MASK (0xf << 2)
+#define WINCON2_BPPMODE_SHIFT (2)
+#define WINCON2_BPPMODE_1BPP (0x0 << 2)
+#define WINCON2_BPPMODE_2BPP (0x1 << 2)
+#define WINCON2_BPPMODE_4BPP (0x2 << 2)
+#define WINCON2_BPPMODE_8BPP_1232 (0x4 << 2)
+#define WINCON2_BPPMODE_16BPP_565 (0x5 << 2)
+#define WINCON2_BPPMODE_16BPP_A1555 (0x6 << 2)
+#define WINCON2_BPPMODE_16BPP_I1555 (0x7 << 2)
+#define WINCON2_BPPMODE_18BPP_666 (0x8 << 2)
+#define WINCON2_BPPMODE_18BPP_A1665 (0x9 << 2)
+#define WINCON2_BPPMODE_19BPP_A1666 (0xa << 2)
+#define WINCON2_BPPMODE_24BPP_888 (0xb << 2)
+#define WINCON2_BPPMODE_24BPP_A1887 (0xc << 2)
+#define WINCON2_BPPMODE_25BPP_A1888 (0xd << 2)
+#define WINCON2_BPPMODE_28BPP_A4888 (0xd << 2)
+
+#define WINCON3_BLD_PIX (1 << 6)
+
+#define WINCON3_ALPHA_SEL (1 << 1)
+#define WINCON3_BPPMODE_MASK (0xf << 2)
+#define WINCON3_BPPMODE_SHIFT (2)
+#define WINCON3_BPPMODE_1BPP (0x0 << 2)
+#define WINCON3_BPPMODE_2BPP (0x1 << 2)
+#define WINCON3_BPPMODE_4BPP (0x2 << 2)
+#define WINCON3_BPPMODE_16BPP_565 (0x5 << 2)
+#define WINCON3_BPPMODE_16BPP_A1555 (0x6 << 2)
+#define WINCON3_BPPMODE_16BPP_I1555 (0x7 << 2)
+#define WINCON3_BPPMODE_18BPP_666 (0x8 << 2)
+#define WINCON3_BPPMODE_18BPP_A1665 (0x9 << 2)
+#define WINCON3_BPPMODE_19BPP_A1666 (0xa << 2)
+#define WINCON3_BPPMODE_24BPP_888 (0xb << 2)
+#define WINCON3_BPPMODE_24BPP_A1887 (0xc << 2)
+#define WINCON3_BPPMODE_25BPP_A1888 (0xd << 2)
+#define WINCON3_BPPMODE_28BPP_A4888 (0xd << 2)
+
+#define VIDINTCON0_FIFIOSEL_WINDOW2 (0x10 << 5)
+#define VIDINTCON0_FIFIOSEL_WINDOW3 (0x20 << 5)
+#define VIDINTCON0_FIFIOSEL_WINDOW4 (0x40 << 5)
+
+#define DITHMODE (0x170)
+#define WINxMAP(_win) (0x180 + ((_win) * 4))
+
+
+#define DITHMODE_R_POS_MASK (0x3 << 5)
+#define DITHMODE_R_POS_SHIFT (5)
+#define DITHMODE_R_POS_8BIT (0x0 << 5)
+#define DITHMODE_R_POS_6BIT (0x1 << 5)
+#define DITHMODE_R_POS_5BIT (0x2 << 5)
+
+#define DITHMODE_G_POS_MASK (0x3 << 3)
+#define DITHMODE_G_POS_SHIFT (3)
+#define DITHMODE_G_POS_8BIT (0x0 << 3)
+#define DITHMODE_G_POS_6BIT (0x1 << 3)
+#define DITHMODE_G_POS_5BIT (0x2 << 3)
+
+#define DITHMODE_B_POS_MASK (0x3 << 1)
+#define DITHMODE_B_POS_SHIFT (1)
+#define DITHMODE_B_POS_8BIT (0x0 << 1)
+#define DITHMODE_B_POS_6BIT (0x1 << 1)
+#define DITHMODE_B_POS_5BIT (0x2 << 1)
+
+#define DITHMODE_DITH_EN (1 << 0)
+
+#define WPALCON (0x1A0)
+
+#define WPALCON_W4PAL_16BPP_A555 (1 << 8)
+#define WPALCON_W3PAL_16BPP_A555 (1 << 7)
+#define WPALCON_W2PAL_16BPP_A555 (1 << 6)
+
+/* Palette registers */
+
+#define WIN2_PAL(_entry) (0x300 + ((_entry) * 2))
+#define WIN3_PAL(_entry) (0x320 + ((_entry) * 2))
+#define WIN4_PAL(_entry) (0x340 + ((_entry) * 2))
+#define WIN0_PAL(_entry) (0x400 + ((_entry) * 4))
+#define WIN1_PAL(_entry) (0x800 + ((_entry) * 4))
+
+/* system specific implementation code for palette sizes, and other
+ * information that changes depending on which architecture is being
+ * compiled.
+*/
+
+/* return true if window _win has OSD register D */
+#define s3c_fb_has_osd_d(_win) ((_win) != 4 && (_win) != 0)
+
+static inline unsigned int s3c_fb_win_pal_size(unsigned int win)
+{
+ if (win < 2)
+ return 256;
+ if (win < 4)
+ return 16;
+ if (win == 4)
+ return 4;
+
+ BUG(); /* shouldn't get here */
+}
+
+static inline int s3c_fb_validate_win_bpp(unsigned int win, unsigned int bpp)
+{
+ /* all windows can do 1/2 bpp */
+
+ if ((bpp == 25 || bpp == 19) && win == 0)
+ return 0; /* win 0 does not have 19 or 25bpp modes */
+
+ if (bpp == 4 && win == 4)
+ return 0;
+
+ if (bpp == 8 && (win >= 3))
+ return 0; /* win 3/4 cannot do 8bpp in any mode */
+
+ return 1;
+}
+
+static inline unsigned int s3c_fb_pal_reg(unsigned int window, int reg)
+{
+ switch (window) {
+ case 0: return WIN0_PAL(reg);
+ case 1: return WIN1_PAL(reg);
+ case 2: return WIN2_PAL(reg);
+ case 3: return WIN3_PAL(reg);
+ case 4: return WIN4_PAL(reg);
+ }
+
+ BUG();
+}
+
+static inline int s3c_fb_pal_is16(unsigned int window)
+{
+ return window > 1;
+}
+
+struct s3c_fb_palette {
+ struct fb_bitfield r;
+ struct fb_bitfield g;
+ struct fb_bitfield b;
+ struct fb_bitfield a;
+};
+
+static inline void s3c_fb_init_palette(unsigned int window,
+ struct s3c_fb_palette *palette)
+{
+ if (window < 2) {
+ /* Windows 0/1 are 8/8/8 or A/8/8/8 */
+ palette->r.offset = 16;
+ palette->r.length = 8;
+ palette->g.offset = 8;
+ palette->g.length = 8;
+ palette->b.offset = 0;
+ palette->b.length = 8;
+ } else {
+ /* currently we assume RGB 5/6/5 */
+ palette->r.offset = 11;
+ palette->r.length = 5;
+ palette->g.offset = 5;
+ palette->g.length = 6;
+ palette->b.offset = 0;
+ palette->b.length = 5;
+ }
+}
+
+/* Notes on per-window bpp settings
+ *
+ * Value Win0 Win1 Win2 Win3 Win 4
+ * 0000 1(P) 1(P) 1(P) 1(P) 1(P)
+ * 0001 2(P) 2(P) 2(P) 2(P) 2(P)
+ * 0010 4(P) 4(P) 4(P) 4(P) -none-
+ * 0011 8(P) 8(P) -none- -none- -none-
+ * 0100 -none- 8(A232) 8(A232) -none- -none-
+ * 0101 16(565) 16(565) 16(565) 16(565) 16(565)
+ * 0110 -none- 16(A555) 16(A555) 16(A555) 16(A555)
+ * 0111 16(I555) 16(I565) 16(I555) 16(I555) 16(I555)
+ * 1000 18(666) 18(666) 18(666) 18(666) 18(666)
+ * 1001 -none- 18(A665) 18(A665) 18(A665) 16(A665)
+ * 1010 -none- 19(A666) 19(A666) 19(A666) 19(A666)
+ * 1011 24(888) 24(888) 24(888) 24(888) 24(888)
+ * 1100 -none- 24(A887) 24(A887) 24(A887) 24(A887)
+ * 1101 -none- 25(A888) 25(A888) 25(A888) 25(A888)
+ * 1110 -none- -none- -none- -none- -none-
+ * 1111 -none- -none- -none- -none- -none-
+*/
diff --git a/arch/arm/mach-s3c6400/include/mach/regs-irq.h b/arch/arm/mach-s3c6400/include/mach/regs-irq.h
new file mode 100644
index 000000000000..bcce68a0bb75
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/regs-irq.h
@@ -0,0 +1,20 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/regs-irq.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C64XX - IRQ register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_IRQ_H
+#define __ASM_ARCH_REGS_IRQ_H __FILE__
+
+#include <asm/hardware/vic.h>
+
+#endif /* __ASM_ARCH_6400_REGS_IRQ_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/system.h b/arch/arm/mach-s3c6400/include/mach/system.h
new file mode 100644
index 000000000000..652bbc403f0b
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/system.h
@@ -0,0 +1,24 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/system.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 - system implementation
+ */
+
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H __FILE__
+
+static void arch_idle(void)
+{
+ /* nothing here yet */
+}
+
+static void arch_reset(char mode)
+{
+ /* nothing here yet */
+}
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/tick.h b/arch/arm/mach-s3c6400/include/mach/tick.h
new file mode 100644
index 000000000000..d9c0dc7014ec
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/tick.h
@@ -0,0 +1,29 @@
+/* linux/arch/arm/mach-s3c6400/include/mach/tick.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C64XX - Timer tick support definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_TICK_H
+#define __ASM_ARCH_TICK_H __FILE__
+
+/* note, the timer interrutps turn up in 2 places, the vic and then
+ * the timer block. We take the VIC as the base at the moment.
+ */
+static inline u32 s3c24xx_ostimer_pending(void)
+{
+ u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS);
+ return pend & 1 << (IRQ_TIMER4_VIC - S3C64XX_IRQ_VIC0(0));
+}
+
+#define TICK_MAX (0xffffffff)
+
+#endif /* __ASM_ARCH_6400_TICK_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/uncompress.h b/arch/arm/mach-s3c6400/include/mach/uncompress.h
new file mode 100644
index 000000000000..c6a82a20bf2a
--- /dev/null
+++ b/arch/arm/mach-s3c6400/include/mach/uncompress.h
@@ -0,0 +1,28 @@
+/* arch/arm/mach-s3c6400/include/mach/uncompress.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C6400 - uncompress code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+#include <mach/map.h>
+#include <plat/uncompress.h>
+
+static void arch_detect_cpu(void)
+{
+ /* we do not need to do any cpu detection here at the moment. */
+ fifo_mask = S3C2440_UFSTAT_TXMASK;
+ fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
+}
+
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s3c6410/Kconfig b/arch/arm/mach-s3c6410/Kconfig
new file mode 100644
index 000000000000..1d5010070027
--- /dev/null
+++ b/arch/arm/mach-s3c6410/Kconfig
@@ -0,0 +1,62 @@
+# arch/arm/mach-s3c6410/Kconfig
+#
+# Copyright 2008 Openmoko, Inc.
+# Copyright 2008 Simtec Electronics
+#
+# Licensed under GPLv2
+
+# Configuration options for the S3C6410 CPU
+
+config CPU_S3C6410
+ bool
+ select CPU_S3C6400_INIT
+ select CPU_S3C6400_CLOCK
+ help
+ Enable S3C6410 CPU support
+
+config S3C6410_SETUP_SDHCI
+ bool
+ help
+ Internal helper functions for S3C6410 based SDHCI systems
+
+config MACH_SMDK6410
+ bool "SMDK6410"
+ select CPU_S3C6410
+ select S3C_DEV_HSMMC
+ select S3C_DEV_HSMMC1
+ select S3C_DEV_I2C1
+ select S3C_DEV_FB
+ select S3C6410_SETUP_SDHCI
+ select S3C64XX_SETUP_I2C1
+ select S3C64XX_SETUP_FB_24BPP
+ help
+ Machine support for the Samsung SMDK6410
+
+# At least some of the SMDK6410s were shipped with the card detect
+# for the MMC/SD slots connected to the same input. This means that
+# either the boards need to be altered to have channel0 to an alternate
+# configuration or that only one slot can be used.
+
+choice
+ prompt "SMDK6410 MMC/SD slot setup"
+ depends on MACH_SMDK6410
+
+config SMDK6410_SD_CH0
+ bool "Use channel 0 only"
+ depends on MACH_SMDK6410
+ help
+ Select CON7 (channel 0) as the MMC/SD slot, as
+ at least some SMDK6410 boards come with the
+ resistors fitted so that the card detects for
+ channels 0 and 1 are the same.
+
+config SMDK6410_SD_CH1
+ bool "Use channel 1 only"
+ depends on MACH_SMDK6410
+ help
+ Select CON6 (channel 1) as the MMC/SD slot, as
+ at least some SMDK6410 boards come with the
+ resistors fitted so that the card detects for
+ channels 0 and 1 are the same.
+
+endchoice
diff --git a/arch/arm/mach-s3c6410/Makefile b/arch/arm/mach-s3c6410/Makefile
new file mode 100644
index 000000000000..2cd4f189036b
--- /dev/null
+++ b/arch/arm/mach-s3c6410/Makefile
@@ -0,0 +1,23 @@
+# arch/arm/plat-s3c6410/Makefile
+#
+# Copyright 2008 Openmoko, Inc.
+# Copyright 2008 Simtec Electronics
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Core support for S3C6410 system
+
+obj-$(CONFIG_CPU_S3C6410) += cpu.o
+
+# Helper and device support
+
+obj-$(CONFIG_S3C6410_SETUP_SDHCI) += setup-sdhci.o
+
+# machine support
+
+obj-$(CONFIG_MACH_SMDK6410) += mach-smdk6410.o
diff --git a/arch/arm/mach-s3c6410/cpu.c b/arch/arm/mach-s3c6410/cpu.c
new file mode 100644
index 000000000000..6a73ca6b7a3a
--- /dev/null
+++ b/arch/arm/mach-s3c6410/cpu.c
@@ -0,0 +1,101 @@
+/* linux/arch/arm/mach-s3c6410/cpu.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/sysdev.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <plat/cpu-freq.h>
+#include <plat/regs-serial.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+#include <plat/sdhci.h>
+#include <plat/iic-core.h>
+#include <plat/s3c6400.h>
+#include <plat/s3c6410.h>
+
+/* Initial IO mappings */
+
+static struct map_desc s3c6410_iodesc[] __initdata = {
+};
+
+/* s3c6410_map_io
+ *
+ * register the standard cpu IO areas
+*/
+
+void __init s3c6410_map_io(void)
+{
+ iotable_init(s3c6410_iodesc, ARRAY_SIZE(s3c6410_iodesc));
+
+ /* initialise device information early */
+ s3c6410_default_sdhci0();
+ s3c6410_default_sdhci1();
+
+ /* the i2c devices are directly compatible with s3c2440 */
+ s3c_i2c0_setname("s3c2440-i2c");
+ s3c_i2c1_setname("s3c2440-i2c");
+}
+
+void __init s3c6410_init_clocks(int xtal)
+{
+ printk(KERN_DEBUG "%s: initialising clocks\n", __func__);
+ s3c24xx_register_baseclocks(xtal);
+ s3c64xx_register_clocks();
+ s3c6400_register_clocks();
+ s3c6400_setup_clocks();
+}
+
+void __init s3c6410_init_irq(void)
+{
+ /* VIC0 is missing IRQ7, VIC1 is fully populated. */
+ s3c64xx_init_irq(~0 & ~(1 << 7), ~0);
+}
+
+struct sysdev_class s3c6410_sysclass = {
+ .name = "s3c6410-core",
+};
+
+static struct sys_device s3c6410_sysdev = {
+ .cls = &s3c6410_sysclass,
+};
+
+static int __init s3c6410_core_init(void)
+{
+ return sysdev_class_register(&s3c6410_sysclass);
+}
+
+core_initcall(s3c6410_core_init);
+
+int __init s3c6410_init(void)
+{
+ printk("S3C6410: Initialising architecture\n");
+
+ return sysdev_register(&s3c6410_sysdev);
+}
diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c
new file mode 100644
index 000000000000..3c4d47145c83
--- /dev/null
+++ b/arch/arm/mach-s3c6410/mach-smdk6410.c
@@ -0,0 +1,185 @@
+/* linux/arch/arm/mach-s3c6410/mach-smdk6410.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <video/platform_lcd.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-fb.h>
+#include <mach/map.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <plat/regs-serial.h>
+#include <plat/iic.h>
+#include <plat/fb.h>
+
+#include <plat/s3c6410.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
+#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
+#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
+
+static struct s3c2410_uartcfg smdk6410_uartcfgs[] __initdata = {
+ [0] = {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+ [1] = {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+};
+
+/* framebuffer and LCD setup. */
+
+/* GPF15 = LCD backlight control
+ * GPF13 => Panel power
+ * GPN5 = LCD nRESET signal
+ * PWM_TOUT1 => backlight brightness
+ */
+
+static void smdk6410_lcd_power_set(struct plat_lcd_data *pd,
+ unsigned int power)
+{
+ if (power) {
+ gpio_direction_output(S3C64XX_GPF(13), 1);
+ gpio_direction_output(S3C64XX_GPF(15), 1);
+
+ /* fire nRESET on power up */
+ gpio_direction_output(S3C64XX_GPN(5), 0);
+ msleep(10);
+ gpio_direction_output(S3C64XX_GPN(5), 1);
+ msleep(1);
+ } else {
+ gpio_direction_output(S3C64XX_GPF(15), 0);
+ gpio_direction_output(S3C64XX_GPF(13), 0);
+ }
+}
+
+static struct plat_lcd_data smdk6410_lcd_power_data = {
+ .set_power = smdk6410_lcd_power_set,
+};
+
+static struct platform_device smdk6410_lcd_powerdev = {
+ .name = "platform-lcd",
+ .dev.parent = &s3c_device_fb.dev,
+ .dev.platform_data = &smdk6410_lcd_power_data,
+};
+
+static struct s3c_fb_pd_win smdk6410_fb_win0 = {
+ /* this is to ensure we use win0 */
+ .win_mode = {
+ .pixclock = 41094,
+ .left_margin = 8,
+ .right_margin = 13,
+ .upper_margin = 7,
+ .lower_margin = 5,
+ .hsync_len = 3,
+ .vsync_len = 1,
+ .xres = 800,
+ .yres = 480,
+ },
+ .max_bpp = 32,
+ .default_bpp = 16,
+};
+
+/* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
+static struct s3c_fb_platdata smdk6410_lcd_pdata __initdata = {
+ .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
+ .win[0] = &smdk6410_fb_win0,
+ .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
+ .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
+};
+
+struct map_desc smdk6410_iodesc[] = {};
+
+static struct platform_device *smdk6410_devices[] __initdata = {
+#ifdef CONFIG_SMDK6410_SD_CH0
+ &s3c_device_hsmmc0,
+#endif
+#ifdef CONFIG_SMDK6410_SD_CH1
+ &s3c_device_hsmmc1,
+#endif
+ &s3c_device_i2c0,
+ &s3c_device_i2c1,
+ &s3c_device_fb,
+ &smdk6410_lcd_powerdev,
+};
+
+static struct i2c_board_info i2c_devs0[] __initdata = {
+ { I2C_BOARD_INFO("24c08", 0x50), },
+ { I2C_BOARD_INFO("WM8580", 0X1b), },
+};
+
+static struct i2c_board_info i2c_devs1[] __initdata = {
+ { I2C_BOARD_INFO("24c128", 0x57), }, /* Samsung S524AD0XD1 */
+};
+
+static void __init smdk6410_map_io(void)
+{
+ s3c64xx_init_io(smdk6410_iodesc, ARRAY_SIZE(smdk6410_iodesc));
+ s3c24xx_init_clocks(12000000);
+ s3c24xx_init_uarts(smdk6410_uartcfgs, ARRAY_SIZE(smdk6410_uartcfgs));
+}
+
+static void __init smdk6410_machine_init(void)
+{
+ s3c_i2c0_set_platdata(NULL);
+ s3c_i2c1_set_platdata(NULL);
+ s3c_fb_set_platdata(&smdk6410_lcd_pdata);
+
+ i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
+ i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
+
+ platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
+}
+
+MACHINE_START(SMDK6410, "SMDK6410")
+ /* Maintainer: Ben Dooks <ben@fluff.org> */
+ .phys_io = S3C_PA_UART & 0xfff00000,
+ .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S3C64XX_PA_SDRAM + 0x100,
+
+ .init_irq = s3c6410_init_irq,
+ .map_io = smdk6410_map_io,
+ .init_machine = smdk6410_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c6410/setup-sdhci.c b/arch/arm/mach-s3c6410/setup-sdhci.c
new file mode 100644
index 000000000000..0b5788bd5985
--- /dev/null
+++ b/arch/arm/mach-s3c6410/setup-sdhci.c
@@ -0,0 +1,102 @@
+/* linux/arch/arm/mach-s3c6410/setup-sdhci.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6410 - Helper functions for settign up SDHCI device(s) (HSMMC)
+ *
+ * This program is free software; you can 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/platform_device.h>
+#include <linux/io.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+#include <plat/regs-sdhci.h>
+#include <plat/sdhci.h>
+
+/* clock sources for the mmc bus clock, order as for the ctrl2[5..4] */
+
+char *s3c6410_hsmmc_clksrcs[4] = {
+ [0] = "hsmmc",
+ [1] = "hsmmc",
+ [2] = "mmc_bus",
+ /* [3] = "48m", - note not succesfully used yet */
+};
+
+void s3c6410_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width)
+{
+ unsigned int gpio;
+ unsigned int end;
+
+ end = S3C64XX_GPG(2 + width);
+
+ /* Set all the necessary GPG pins to special-function 0 */
+ for (gpio = S3C64XX_GPG(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(2));
+}
+
+void s3c6410_setup_sdhci0_cfg_card(struct platform_device *dev,
+ void __iomem *r,
+ struct mmc_ios *ios,
+ struct mmc_card *card)
+{
+ u32 ctrl2, ctrl3;
+
+ /* don't need to alter anything acording to card-type */
+
+ writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA, r + S3C64XX_SDHCI_CONTROL4);
+
+ ctrl2 = readl(r + S3C_SDHCI_CONTROL2);
+ ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+ ctrl2 |= (S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR |
+ S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK |
+ S3C_SDHCI_CTRL2_ENFBCLKRX |
+ S3C_SDHCI_CTRL2_DFCNT_NONE |
+ S3C_SDHCI_CTRL2_ENCLKOUTHOLD);
+
+ if (ios->clock < 25 * 1000000)
+ ctrl3 = (S3C_SDHCI_CTRL3_FCSEL3 |
+ S3C_SDHCI_CTRL3_FCSEL2 |
+ S3C_SDHCI_CTRL3_FCSEL1 |
+ S3C_SDHCI_CTRL3_FCSEL0);
+ else
+ ctrl3 = (S3C_SDHCI_CTRL3_FCSEL1 | S3C_SDHCI_CTRL3_FCSEL0);
+
+ printk(KERN_INFO "%s: CTRL 2=%08x, 3=%08x\n", __func__, ctrl2, ctrl3);
+ writel(ctrl2, r + S3C_SDHCI_CONTROL2);
+ writel(ctrl3, r + S3C_SDHCI_CONTROL3);
+}
+
+void s3c6410_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width)
+{
+ unsigned int gpio;
+ unsigned int end;
+
+ end = S3C64XX_GPH(2 + width);
+
+ /* Set all the necessary GPG pins to special-function 0 */
+ for (gpio = S3C64XX_GPH(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(3));
+}
diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c
index 43c30f84abf2..dab3c6347a8f 100644
--- a/arch/arm/mach-sa1100/clock.c
+++ b/arch/arm/mach-sa1100/clock.c
@@ -3,6 +3,7 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -14,36 +15,39 @@
#include <mach/hardware.h>
/*
- * Very simple clock implementation - we only have one clock to
- * deal with at the moment, so we only match using the "name".
+ * Very simple clock implementation - we only have one clock to deal with.
*/
struct clk {
- struct list_head node;
- unsigned long rate;
- const char *name;
unsigned int enabled;
- void (*enable)(void);
- void (*disable)(void);
};
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
+static void clk_gpio27_enable(void)
+{
+ /*
+ * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
+ * (SA-1110 Developer's Manual, section 9.1.2.1)
+ */
+ GAFR |= GPIO_32_768kHz;
+ GPDR |= GPIO_32_768kHz;
+ TUCR = TUCR_3_6864MHz;
+}
+
+static void clk_gpio27_disable(void)
+{
+ TUCR = 0;
+ GPDR &= ~GPIO_32_768kHz;
+ GAFR &= ~GPIO_32_768kHz;
+}
+
+static struct clk clk_gpio27;
+
static DEFINE_SPINLOCK(clocks_lock);
struct clk *clk_get(struct device *dev, const char *id)
{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0) {
- clk = p;
- break;
- }
- }
- mutex_unlock(&clocks_mutex);
+ const char *devname = dev_name(dev);
- return clk;
+ return strcmp(devname, "sa1111.0") ? ERR_PTR(-ENOENT) : &clk_gpio27;
}
EXPORT_SYMBOL(clk_get);
@@ -58,7 +62,7 @@ int clk_enable(struct clk *clk)
spin_lock_irqsave(&clocks_lock, flags);
if (clk->enabled++ == 0)
- clk->enable();
+ clk_gpio27_enable();
spin_unlock_irqrestore(&clocks_lock, flags);
return 0;
}
@@ -72,63 +76,13 @@ void clk_disable(struct clk *clk)
spin_lock_irqsave(&clocks_lock, flags);
if (--clk->enabled == 0)
- clk->disable();
+ clk_gpio27_disable();
spin_unlock_irqrestore(&clocks_lock, flags);
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
- return clk->rate;
+ return 3686400;
}
EXPORT_SYMBOL(clk_get_rate);
-
-
-static void clk_gpio27_enable(void)
-{
- /*
- * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
- * (SA-1110 Developer's Manual, section 9.1.2.1)
- */
- GAFR |= GPIO_32_768kHz;
- GPDR |= GPIO_32_768kHz;
- TUCR = TUCR_3_6864MHz;
-}
-
-static void clk_gpio27_disable(void)
-{
- TUCR = 0;
- GPDR &= ~GPIO_32_768kHz;
- GAFR &= ~GPIO_32_768kHz;
-}
-
-static struct clk clk_gpio27 = {
- .name = "SA1111_CLK",
- .rate = 3686400,
- .enable = clk_gpio27_enable,
- .disable = clk_gpio27_disable,
-};
-
-int clk_register(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init(void)
-{
- clk_register(&clk_gpio27);
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c
index fe289997cfaf..2052eb88c961 100644
--- a/arch/arm/mach-sa1100/collie.c
+++ b/arch/arm/mach-sa1100/collie.c
@@ -68,23 +68,22 @@ struct platform_device colliescoop_device = {
};
static struct scoop_pcmcia_dev collie_pcmcia_scoop[] = {
-{
- .dev = &colliescoop_device.dev,
- .irq = COLLIE_IRQ_GPIO_CF_IRQ,
- .cd_irq = COLLIE_IRQ_GPIO_CF_CD,
- .cd_irq_str = "PCMCIA0 CD",
-},
+ {
+ .dev = &colliescoop_device.dev,
+ .irq = COLLIE_IRQ_GPIO_CF_IRQ,
+ .cd_irq = COLLIE_IRQ_GPIO_CF_CD,
+ .cd_irq_str = "PCMCIA0 CD",
+ },
};
static struct scoop_pcmcia_config collie_pcmcia_config = {
- .devs = &collie_pcmcia_scoop[0],
- .num_devs = 1,
+ .devs = &collie_pcmcia_scoop[0],
+ .num_devs = 1,
};
-
static struct mcp_plat_data collie_mcp_data = {
- .mccr0 = MCCR0_ADM | MCCR0_ExtClk,
- .sclk_rate = 9216000,
+ .mccr0 = MCCR0_ADM | MCCR0_ExtClk,
+ .sclk_rate = 9216000,
};
#ifdef CONFIG_SHARP_LOCOMO
@@ -95,14 +94,14 @@ struct platform_device collie_locomo_device;
static void collie_uart_set_mctrl(struct uart_port *port, u_int mctrl)
{
- if (mctrl & TIOCM_RTS)
+ if (mctrl & TIOCM_RTS)
locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 0);
- else
+ else
locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_RTS, 1);
- if (mctrl & TIOCM_DTR)
+ if (mctrl & TIOCM_DTR)
locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 0);
- else
+ else
locomo_gpio_write(&collie_locomo_device.dev, LOCOMO_GPIO_DTR, 1);
}
diff --git a/arch/arm/mach-sa1100/collie_pm.c b/arch/arm/mach-sa1100/collie_pm.c
index b1161fc80602..b39307f26b52 100644
--- a/arch/arm/mach-sa1100/collie_pm.c
+++ b/arch/arm/mach-sa1100/collie_pm.c
@@ -26,7 +26,7 @@
#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/hardware/scoop.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/collie.h>
#include <asm/mach/sharpsl_param.h>
#include <asm/hardware/sharpsl_pm.h>
@@ -263,24 +263,24 @@ static int __init collie_pm_ucb_add(struct ucb1x00_dev *pdev)
}
static struct ucb1x00_driver collie_pm_ucb_driver = {
- .add = collie_pm_ucb_add,
+ .add = collie_pm_ucb_add,
};
static struct platform_device *collie_pm_device;
static int __init collie_pm_init(void)
{
- int ret;
+ int ret;
- collie_pm_device = platform_device_alloc("sharpsl-pm", -1);
- if (!collie_pm_device)
- return -ENOMEM;
+ collie_pm_device = platform_device_alloc("sharpsl-pm", -1);
+ if (!collie_pm_device)
+ return -ENOMEM;
- collie_pm_device->dev.platform_data = &collie_pm_machinfo;
- ret = platform_device_add(collie_pm_device);
+ collie_pm_device->dev.platform_data = &collie_pm_machinfo;
+ ret = platform_device_add(collie_pm_device);
- if (ret)
- platform_device_put(collie_pm_device);
+ if (ret)
+ platform_device_put(collie_pm_device);
if (!ret)
ret = ucb1x00_register_driver(&collie_pm_ucb_driver);
@@ -291,7 +291,7 @@ static int __init collie_pm_init(void)
static void __exit collie_pm_exit(void)
{
ucb1x00_unregister_driver(&collie_pm_ucb_driver);
- platform_device_unregister(collie_pm_device);
+ platform_device_unregister(collie_pm_device);
}
module_init(collie_pm_init);
diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c
index 244d5956312c..ef817876a5d6 100644
--- a/arch/arm/mach-sa1100/cpu-sa1100.c
+++ b/arch/arm/mach-sa1100/cpu-sa1100.c
@@ -3,17 +3,17 @@
*
* Copyright (C) 2000 2001, The Delft University of Technology
*
- * Authors:
+ * Authors:
* - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
* - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
* - major rewrite for linux-2.3.99
- * - rewritten for the more generic power management scheme in
+ * - rewritten for the more generic power management scheme in
* linux-2.4.5-rmk1
*
* This software has been developed while working on the LART
* computing board (http://www.lartmaker.nl/), which is
* sponsored by the Mobile Multi-media Communications
- * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications
+ * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications
* (http://www.ubicom.tudelft.nl/) projects.
*
* The authors can be reached at:
@@ -36,7 +36,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU 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
@@ -44,7 +44,7 @@
*
* Theory of operations
* ====================
- *
+ *
* Clock scaling can be used to lower the power consumption of the CPU
* core. This will give you a somewhat longer running time.
*
@@ -58,11 +58,11 @@
* MDCNFG 0xA0000000 DRAM config
* MDCAS0 0xA0000004 Access waveform
* MDCAS1 0xA0000008 Access waveform
- * MDCAS2 0xA000000C Access waveform
+ * MDCAS2 0xA000000C Access waveform
*
* Care must be taken to change the DRAM parameters the correct way,
* because otherwise the DRAM becomes unusable and the kernel will
- * crash.
+ * crash.
*
* The simple solution to avoid a kernel crash is to put the actual
* clock change in ROM and jump to that code from the kernel. The main
@@ -75,7 +75,7 @@
* as long as all re-configuration steps yield a valid DRAM
* configuration. The advantages are clear: it will run on all SA-1100
* platforms, and the code is very simple.
- *
+ *
* If you really want to understand what is going on in
* sa1100_update_dram_timings(), you'll have to read sections 8.2,
* 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
@@ -97,7 +97,7 @@
typedef struct {
int speed;
u32 mdcnfg;
- u32 mdcas0;
+ u32 mdcas0;
u32 mdcas1;
u32 mdcas2;
} sa1100_dram_regs_t;
@@ -147,7 +147,7 @@ static void sa1100_update_dram_timings(int current_speed, int new_speed)
/* No risk, no fun: run with interrupts on! */
if (new_speed > current_speed) {
/* We're going FASTER, so first relax the memory
- * timings before changing the core frequency
+ * timings before changing the core frequency
*/
/* Half the memory access clock */
diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c
index 3e4fb214eada..63b32b68b296 100644
--- a/arch/arm/mach-sa1100/cpu-sa1110.c
+++ b/arch/arm/mach-sa1100/cpu-sa1110.c
@@ -81,14 +81,14 @@ static struct sdram_params sdram_tbl[] __initdata = {
.twr = 9,
.refresh = 64000,
.cas_latency = 3,
- }, { /* Samsung K4S281632B-1H */
- .name = "K4S281632B-1H",
- .rows = 12,
- .tck = 10,
- .trp = 20,
- .twr = 10,
- .refresh = 64000,
- .cas_latency = 3,
+ }, { /* Samsung K4S281632B-1H */
+ .name = "K4S281632B-1H",
+ .rows = 12,
+ .tck = 10,
+ .trp = 20,
+ .twr = 10,
+ .refresh = 64000,
+ .cas_latency = 3,
}, { /* Samsung KM416S4030CT */
.name = "KM416S4030CT",
.rows = 13,
@@ -220,7 +220,7 @@ sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
}
/*
- * Ok, set the CPU frequency.
+ * Ok, set the CPU frequency.
*/
static int sa1110_target(struct cpufreq_policy *policy,
unsigned int target_freq,
diff --git a/arch/arm/mach-sa1100/dma.c b/arch/arm/mach-sa1100/dma.c
index f990a3e85846..95f9c5a6d6d5 100644
--- a/arch/arm/mach-sa1100/dma.c
+++ b/arch/arm/mach-sa1100/dma.c
@@ -19,7 +19,7 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#undef DEBUG
@@ -113,10 +113,10 @@ int sa1100_request_dma (dma_device_t device, const char *device_id,
}
}
if (!err) {
- if (dma)
- dma->device = device;
- else
- err = -ENOSR;
+ if (dma)
+ dma->device = device;
+ else
+ err = -ENOSR;
}
spin_unlock(&dma_list_lock);
if (err)
diff --git a/arch/arm/mach-sa1100/include/mach/h3600.h b/arch/arm/mach-sa1100/include/mach/h3600.h
index 3ca0ecf095e6..9cc47fddb335 100644
--- a/arch/arm/mach-sa1100/include/mach/h3600.h
+++ b/arch/arm/mach-sa1100/include/mach/h3600.h
@@ -32,14 +32,14 @@ typedef int __bitwise pm_request_t;
#define machine_is_h3xxx() (machine_is_h3100() || machine_is_h3600() || machine_is_h3800())
/* Physical memory regions corresponding to chip selects */
-#define H3600_EGPIO_PHYS (SA1100_CS5_PHYS + 0x01000000)
-#define H3600_BANK_2_PHYS SA1100_CS2_PHYS
-#define H3600_BANK_4_PHYS SA1100_CS4_PHYS
+#define H3600_EGPIO_PHYS (SA1100_CS5_PHYS + 0x01000000)
+#define H3600_BANK_2_PHYS SA1100_CS2_PHYS
+#define H3600_BANK_4_PHYS SA1100_CS4_PHYS
/* Virtual memory regions corresponding to chip selects 2 & 4 (used on sleeves) */
-#define H3600_EGPIO_VIRT 0xf0000000
-#define H3600_BANK_2_VIRT 0xf1000000
-#define H3600_BANK_4_VIRT 0xf3800000
+#define H3600_EGPIO_VIRT 0xf0000000
+#define H3600_BANK_2_VIRT 0xf1000000
+#define H3600_BANK_4_VIRT 0xf3800000
/*
Machine-independent GPIO definitions
diff --git a/arch/arm/mach-sa1100/include/mach/hardware.h b/arch/arm/mach-sa1100/include/mach/hardware.h
index b70846c096aa..60711822b125 100644
--- a/arch/arm/mach-sa1100/include/mach/hardware.h
+++ b/arch/arm/mach-sa1100/include/mach/hardware.h
@@ -59,6 +59,10 @@
# define __REG(x) (*((volatile unsigned long *)io_p2v(x)))
# define __PREG(x) (io_v2p((unsigned long)&(x)))
+static inline unsigned long get_clock_tick_rate(void)
+{
+ return 3686400;
+}
#else
# define __REG(x) io_p2v(x)
diff --git a/arch/arm/mach-sa1100/include/mach/io.h b/arch/arm/mach-sa1100/include/mach/io.h
index 0c070a6149bc..d8b43f3dcd2d 100644
--- a/arch/arm/mach-sa1100/include/mach/io.h
+++ b/arch/arm/mach-sa1100/include/mach/io.h
@@ -16,11 +16,7 @@
* We don't actually have real ISA nor PCI buses, but there is so many
* drivers out there that might just work if we fake them...
*/
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-#define __io(a) __io(a)
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-sa1100/include/mach/memory.h b/arch/arm/mach-sa1100/include/mach/memory.h
index 1c127b68581d..e9f8eed900f5 100644
--- a/arch/arm/mach-sa1100/include/mach/memory.h
+++ b/arch/arm/mach-sa1100/include/mach/memory.h
@@ -23,23 +23,12 @@ void sa1111_adjust_zones(int node, unsigned long *size, unsigned long *holes);
sa1111_adjust_zones(node, size, holes)
#define ISA_DMA_THRESHOLD (PHYS_OFFSET + SZ_1M - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_1M)
#endif
#endif
/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- *
- * On the SA1100, bus addresses are equivalent to physical addresses.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-/*
* Because of the wide memory address space between physical RAM banks on the
* SA1100, it's much convenient to use Linux's SparseMEM support to implement
* our memory map representation. Assuming all memory nodes have equal access
diff --git a/arch/arm/mach-sa1100/include/mach/mtd-xip.h b/arch/arm/mach-sa1100/include/mach/mtd-xip.h
index eaa09e86ad16..b3d684098fbf 100644
--- a/arch/arm/mach-sa1100/include/mach/mtd-xip.h
+++ b/arch/arm/mach-sa1100/include/mach/mtd-xip.h
@@ -15,6 +15,8 @@
#ifndef __ARCH_SA1100_MTD_XIP_H__
#define __ARCH_SA1100_MTD_XIP_H__
+#include <mach/hardware.h>
+
#define xip_irqpending() (ICIP & ICMR)
/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c
index e45d3a1890bc..e1458bc1868e 100644
--- a/arch/arm/mach-sa1100/pleb.c
+++ b/arch/arm/mach-sa1100/pleb.c
@@ -122,12 +122,12 @@ static void __init pleb_map_io(void)
sa1100_map_io();
sa1100_register_uart(0, 3);
- sa1100_register_uart(1, 1);
+ sa1100_register_uart(1, 1);
- GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
- GPDR |= GPIO_UART_TXD;
- GPDR &= ~GPIO_UART_RXD;
- PPAR |= PPAR_UPR;
+ GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
+ GPDR |= GPIO_UART_TXD;
+ GPDR &= ~GPIO_UART_RXD;
+ PPAR |= PPAR_UPR;
/*
* Fix expansion memory timing for network card
diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c
index 9ccdd09cf69f..ddd917d1083d 100644
--- a/arch/arm/mach-sa1100/shannon.c
+++ b/arch/arm/mach-sa1100/shannon.c
@@ -33,7 +33,7 @@ static struct mtd_partition shannon_partitions[] = {
.offset = MTDPART_OFS_APPEND,
.size = 0xe0000
},
- {
+ {
.name = "initrd",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
diff --git a/arch/arm/mach-sa1100/sleep.S b/arch/arm/mach-sa1100/sleep.S
index 171441f96710..80f31bad707c 100644
--- a/arch/arm/mach-sa1100/sleep.S
+++ b/arch/arm/mach-sa1100/sleep.S
@@ -100,36 +100,36 @@ ENTRY(sa1100_cpu_suspend)
ldr r1, =MSC1
ldr r2, =MSC2
- ldr r3, [r0]
- bic r3, r3, #FMsk(MSC_RT)
- bic r3, r3, #FMsk(MSC_RT)<<16
+ ldr r3, [r0]
+ bic r3, r3, #FMsk(MSC_RT)
+ bic r3, r3, #FMsk(MSC_RT)<<16
- ldr r4, [r1]
- bic r4, r4, #FMsk(MSC_RT)
- bic r4, r4, #FMsk(MSC_RT)<<16
+ ldr r4, [r1]
+ bic r4, r4, #FMsk(MSC_RT)
+ bic r4, r4, #FMsk(MSC_RT)<<16
- ldr r5, [r2]
- bic r5, r5, #FMsk(MSC_RT)
- bic r5, r5, #FMsk(MSC_RT)<<16
+ ldr r5, [r2]
+ bic r5, r5, #FMsk(MSC_RT)
+ bic r5, r5, #FMsk(MSC_RT)<<16
- ldr r6, =MDREFR
+ ldr r6, =MDREFR
- ldr r7, [r6]
- bic r7, r7, #0x0000FF00
- bic r7, r7, #0x000000F0
- orr r8, r7, #MDREFR_SLFRSH
+ ldr r7, [r6]
+bic r7, r7, #0x0000FF00
+bic r7, r7, #0x000000F0
+orr r8, r7, #MDREFR_SLFRSH
- ldr r9, =MDCNFG
- ldr r10, [r9]
- bic r10, r10, #(MDCNFG_DE0+MDCNFG_DE1)
- bic r10, r10, #(MDCNFG_DE2+MDCNFG_DE3)
+ ldr r9, =MDCNFG
+ ldr r10, [r9]
+ bic r10, r10, #(MDCNFG_DE0+MDCNFG_DE1)
+ bic r10, r10, #(MDCNFG_DE2+MDCNFG_DE3)
- bic r11, r8, #MDREFR_SLFRSH
- bic r11, r11, #MDREFR_E1PIN
+ bic r11, r8, #MDREFR_SLFRSH
+ bic r11, r11, #MDREFR_E1PIN
- ldr r12, =PMCR
+ ldr r12, =PMCR
- mov r13, #PMCR_SF
+ mov r13, #PMCR_SF
b sa1110_sdram_controller_fix
@@ -188,10 +188,10 @@ ENTRY(sa1100_cpu_resume)
mcr p15, 0, r1, c8, c7, 0 @ flush I+D TLBs
mcr p15, 0, r1, c7, c7, 0 @ flush I&D cache
mcr p15, 0, r1, c9, c0, 0 @ invalidate RB
- mcr p15, 0, r1, c9, c0, 5 @ allow user space to use RB
+ mcr p15, 0, r1, c9, c0, 5 @ allow user space to use RB
- mcr p15, 0, r4, c3, c0, 0 @ domain ID
- mcr p15, 0, r5, c2, c0, 0 @ translation table base addr
+ mcr p15, 0, r4, c3, c0, 0 @ domain ID
+ mcr p15, 0, r5, c2, c0, 0 @ translation table base addr
mcr p15, 0, r6, c13, c0, 0 @ PID
b resume_turn_on_mmu @ cache align execution
@@ -209,7 +209,7 @@ sleep_save_sp:
.text
resume_after_mmu:
- mcr p15, 0, r1, c15, c1, 2 @ enable clock switching
+ mcr p15, 0, r1, c15, c1, 2 @ enable clock switching
ldmfd sp!, {r4 - r12, pc} @ return to caller
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c
index 1cac4ac0b4b8..711c0295c66f 100644
--- a/arch/arm/mach-sa1100/time.c
+++ b/arch/arm/mach-sa1100/time.c
@@ -2,8 +2,8 @@
* linux/arch/arm/mach-sa1100/time.c
*
* Copyright (C) 1998 Deborah Wallach.
- * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com>
- *
+ * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com>
+ *
* 2000/03/29 (C) Nicolas Pitre <nico@cam.org>
* Rewritten: big cleanup, much simpler, better HZ accuracy.
*
diff --git a/arch/arm/mach-shark/core.c b/arch/arm/mach-shark/core.c
index a9400d984451..a23fd3d0163a 100644
--- a/arch/arm/mach-shark/core.c
+++ b/arch/arm/mach-shark/core.c
@@ -16,6 +16,8 @@
#include <asm/leds.h>
#include <asm/param.h>
+#include <mach/hardware.h>
+
#include <asm/mach/map.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
diff --git a/arch/arm/mach-shark/include/mach/dma.h b/arch/arm/mach-shark/include/mach/dma.h
deleted file mode 100644
index c0a29bd2a74f..000000000000
--- a/arch/arm/mach-shark/include/mach/dma.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * arch/arm/mach-shark/include/mach/dma.h
- *
- * by Alexander Schulz
- */
-#ifndef __ASM_ARCH_DMA_H
-#define __ASM_ARCH_DMA_H
-
-/* Use only the lowest 4MB, nothing else works.
- * The rest is not DMAable. See dev / .properties
- * in OpenFirmware.
- */
-#define MAX_DMA_ADDRESS 0xC0400000
-#define MAX_DMA_CHANNELS 8
-#define DMA_ISA_CASCADE 4
-
-#endif /* _ASM_ARCH_DMA_H */
-
diff --git a/arch/arm/mach-shark/include/mach/hardware.h b/arch/arm/mach-shark/include/mach/hardware.h
index cb0ee2943c1a..01bf76099ce5 100644
--- a/arch/arm/mach-shark/include/mach/hardware.h
+++ b/arch/arm/mach-shark/include/mach/hardware.h
@@ -28,8 +28,6 @@
#define ROMCARD_SIZE 0x08000000
#define ROMCARD_START 0x10000000
-#define PCIO_BASE 0xe0000000
-
/* defines for the Framebuffer */
#define FB_START 0x06000000
diff --git a/arch/arm/mach-shark/include/mach/io.h b/arch/arm/mach-shark/include/mach/io.h
index 92475922c068..c5cee829fc87 100644
--- a/arch/arm/mach-shark/include/mach/io.h
+++ b/arch/arm/mach-shark/include/mach/io.h
@@ -11,46 +11,10 @@
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
-#include <mach/hardware.h>
+#define PCIO_BASE 0xe0000000
+#define IO_SPACE_LIMIT 0xffffffff
-#define IO_SPACE_LIMIT 0xffffffff
-
-/*
- * We use two different types of addressing - PC style addresses, and ARM
- * addresses. PC style accesses the PC hardware with the normal PC IO
- * addresses, eg 0x3f8 for serial#1. ARM addresses are 0x80000000+
- * and are translated to the start of IO.
- */
-#define __PORT_PCIO(x) (!((x) & 0x80000000))
-
-#define __io(a) ((void __iomem *)(PCIO_BASE + (a)))
-
-
-static inline unsigned int __ioaddr (unsigned int port) \
-{ \
- if (__PORT_PCIO(port)) \
- return (unsigned int)(PCIO_BASE + (port)); \
- else \
- return (unsigned int)(IO_BASE + (port)); \
-}
-
-#define __mem_pci(addr) (addr)
-
-/*
- * Translated address IO functions
- *
- * IO address has already been translated to a virtual address
- */
-#define outb_t(v,p) \
- (*(volatile unsigned char *)(p) = (v))
-
-#define inb_t(p) \
- (*(volatile unsigned char *)(p))
-
-#define outl_t(v,p) \
- (*(volatile unsigned long *)(p) = (v))
-
-#define inl_t(p) \
- (*(volatile unsigned long *)(p))
+#define __io(a) ((void __iomem *)(PCIO_BASE + (a)))
+#define __mem_pci(addr) (addr)
#endif
diff --git a/arch/arm/mach-shark/include/mach/isa-dma.h b/arch/arm/mach-shark/include/mach/isa-dma.h
new file mode 100644
index 000000000000..864298ff3927
--- /dev/null
+++ b/arch/arm/mach-shark/include/mach/isa-dma.h
@@ -0,0 +1,17 @@
+/*
+ * arch/arm/mach-shark/include/mach/isa-dma.h
+ *
+ * by Alexander Schulz
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+/* Use only the lowest 4MB, nothing else works.
+ * The rest is not DMAable. See dev / .properties
+ * in OpenFirmware.
+ */
+#define MAX_DMA_CHANNELS 8
+#define DMA_ISA_CASCADE 4
+
+#endif /* _ASM_ARCH_DMA_H */
+
diff --git a/arch/arm/mach-shark/include/mach/memory.h b/arch/arm/mach-shark/include/mach/memory.h
index b7874ad9f9f6..c5ab038925d6 100644
--- a/arch/arm/mach-shark/include/mach/memory.h
+++ b/arch/arm/mach-shark/include/mach/memory.h
@@ -33,12 +33,10 @@ static inline void __arch_adjust_zones(int node, unsigned long *zone_size, unsig
__arch_adjust_zones(node, size, holes)
#define ISA_DMA_THRESHOLD (PHYS_OFFSET + SZ_4M - 1)
+#define MAX_DMA_ADDRESS (PAGE_OFFSET + SZ_4M)
#endif
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
/*
* Cache flushing area
*/
diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
index 95096afd5271..c781f30c8368 100644
--- a/arch/arm/mach-versatile/Kconfig
+++ b/arch/arm/mach-versatile/Kconfig
@@ -3,12 +3,14 @@ menu "Versatile platform type"
config ARCH_VERSATILE_PB
bool "Support Versatile/PB platform"
+ select CPU_ARM926T
default y
help
Include support for the ARM(R) Versatile/PB platform.
config MACH_VERSATILE_AB
bool "Support Versatile/AB platform"
+ select CPU_ARM926T
help
Include support for the ARM(R) Versatile/AP platform.
diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c
index 58937f1fb38c..c50a44ea7ee6 100644
--- a/arch/arm/mach-versatile/clock.c
+++ b/arch/arm/mach-versatile/clock.c
@@ -10,6 +10,7 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -17,36 +18,11 @@
#include <linux/clk.h>
#include <linux/mutex.h>
+#include <asm/clkdev.h>
#include <asm/hardware/icst307.h>
#include "clock.h"
-static LIST_HEAD(clocks);
-static DEFINE_MUTEX(clocks_mutex);
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- mutex_unlock(&clocks_mutex);
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-void clk_put(struct clk *clk)
-{
- module_put(clk->owner);
-}
-EXPORT_SYMBOL(clk_put);
-
int clk_enable(struct clk *clk)
{
return 0;
@@ -66,7 +42,9 @@ EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
- return rate;
+ struct icst307_vco vco;
+ vco = icst307_khz_to_vco(clk->params, rate / 1000);
+ return icst307_khz(clk->params, vco) * 1000;
}
EXPORT_SYMBOL(clk_round_rate);
@@ -79,57 +57,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
vco = icst307_khz_to_vco(clk->params, rate / 1000);
clk->rate = icst307_khz(clk->params, vco) * 1000;
-
- printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
- clk->name, vco.s, vco.r, vco.v);
-
clk->setvco(clk, vco);
ret = 0;
}
return ret;
}
EXPORT_SYMBOL(clk_set_rate);
-
-/*
- * These are fixed clocks.
- */
-static struct clk kmi_clk = {
- .name = "KMIREFCLK",
- .rate = 24000000,
-};
-
-static struct clk uart_clk = {
- .name = "UARTCLK",
- .rate = 24000000,
-};
-
-static struct clk mmci_clk = {
- .name = "MCLK",
- .rate = 24000000,
-};
-
-int clk_register(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-void clk_unregister(struct clk *clk)
-{
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-static int __init clk_init(void)
-{
- clk_register(&kmi_clk);
- clk_register(&uart_clk);
- clk_register(&mmci_clk);
- return 0;
-}
-arch_initcall(clk_init);
diff --git a/arch/arm/mach-versatile/clock.h b/arch/arm/mach-versatile/clock.h
index 8b0b61dd17e4..03468fdc3e58 100644
--- a/arch/arm/mach-versatile/clock.h
+++ b/arch/arm/mach-versatile/clock.h
@@ -12,14 +12,9 @@ struct module;
struct icst307_params;
struct clk {
- struct list_head node;
unsigned long rate;
- struct module *owner;
- const char *name;
const struct icst307_params *params;
+ u32 oscoff;
void *data;
void (*setvco)(struct clk *, struct icst307_vco vco);
};
-
-int clk_register(struct clk *clk);
-void clk_unregister(struct clk *clk);
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index a3f1933434e2..1c43494f5c42 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -31,6 +31,7 @@
#include <linux/cnt32_to_63.h>
#include <linux/io.h>
+#include <asm/clkdev.h>
#include <asm/system.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -373,22 +374,60 @@ static const struct icst307_params versatile_oscvco_params = {
static void versatile_oscvco_set(struct clk *clk, struct icst307_vco vco)
{
- void __iomem *sys_lock = __io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_LOCK_OFFSET;
- void __iomem *sys_osc = __io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_OSCCLCD_OFFSET;
+ void __iomem *sys = __io_address(VERSATILE_SYS_BASE);
+ void __iomem *sys_lock = sys + VERSATILE_SYS_LOCK_OFFSET;
u32 val;
- val = readl(sys_osc) & ~0x7ffff;
+ val = readl(sys + clk->oscoff) & ~0x7ffff;
val |= vco.v | (vco.r << 9) | (vco.s << 16);
writel(0xa05f, sys_lock);
- writel(val, sys_osc);
+ writel(val, sys + clk->oscoff);
writel(0, sys_lock);
}
-static struct clk versatile_clcd_clk = {
- .name = "CLCDCLK",
+static struct clk osc4_clk = {
.params = &versatile_oscvco_params,
- .setvco = versatile_oscvco_set,
+ .oscoff = VERSATILE_SYS_OSCCLCD_OFFSET,
+ .setvco = versatile_oscvco_set,
+};
+
+/*
+ * These are fixed clocks.
+ */
+static struct clk ref24_clk = {
+ .rate = 24000000,
+};
+
+static struct clk_lookup lookups[] __initdata = {
+ { /* UART0 */
+ .dev_id = "dev:f1",
+ .clk = &ref24_clk,
+ }, { /* UART1 */
+ .dev_id = "dev:f2",
+ .clk = &ref24_clk,
+ }, { /* UART2 */
+ .dev_id = "dev:f3",
+ .clk = &ref24_clk,
+ }, { /* UART3 */
+ .dev_id = "fpga:09",
+ .clk = &ref24_clk,
+ }, { /* KMI0 */
+ .dev_id = "fpga:06",
+ .clk = &ref24_clk,
+ }, { /* KMI1 */
+ .dev_id = "fpga:07",
+ .clk = &ref24_clk,
+ }, { /* MMC0 */
+ .dev_id = "fpga:05",
+ .clk = &ref24_clk,
+ }, { /* MMC1 */
+ .dev_id = "fpga:0b",
+ .clk = &ref24_clk,
+ }, { /* CLCD */
+ .dev_id = "dev:20",
+ .clk = &osc4_clk,
+ }
};
/*
@@ -786,7 +825,8 @@ void __init versatile_init(void)
{
int i;
- clk_register(&versatile_clcd_clk);
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
platform_device_register(&versatile_flash_device);
platform_device_register(&versatile_i2c_device);
diff --git a/arch/arm/mach-versatile/include/mach/clkdev.h b/arch/arm/mach-versatile/include/mach/clkdev.h
new file mode 100644
index 000000000000..04b37a89801c
--- /dev/null
+++ b/arch/arm/mach-versatile/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-versatile/include/mach/dma.h b/arch/arm/mach-versatile/include/mach/dma.h
deleted file mode 100644
index 0aabf12c8834..000000000000
--- a/arch/arm/mach-versatile/include/mach/dma.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/mach-versatile/include/mach/dma.h
- *
- * Copyright (C) 2003 ARM Limited.
- * Copyright (C) 1997,1998 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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
- */
diff --git a/arch/arm/mach-versatile/include/mach/io.h b/arch/arm/mach-versatile/include/mach/io.h
index c0b9dd1d0257..f067c14c7182 100644
--- a/arch/arm/mach-versatile/include/mach/io.h
+++ b/arch/arm/mach-versatile/include/mach/io.h
@@ -22,11 +22,7 @@
#define IO_SPACE_LIMIT 0xffffffff
-static inline void __iomem *__io(unsigned long addr)
-{
- return (void __iomem *)addr;
-}
-#define __io(a) __io(a)
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/mach-versatile/include/mach/irqs.h b/arch/arm/mach-versatile/include/mach/irqs.h
index 216a1312e62e..9bfdb30e1f3f 100644
--- a/arch/arm/mach-versatile/include/mach/irqs.h
+++ b/arch/arm/mach-versatile/include/mach/irqs.h
@@ -60,39 +60,6 @@
#define IRQ_VICSOURCE31 (IRQ_VIC_START + INT_VICSOURCE31)
#define IRQ_VIC_END (IRQ_VIC_START + 31)
-#define IRQMASK_WDOGINT INTMASK_WDOGINT
-#define IRQMASK_SOFTINT INTMASK_SOFTINT
-#define IRQMASK_COMMRx INTMASK_COMMRx
-#define IRQMASK_COMMTx INTMASK_COMMTx
-#define IRQMASK_TIMERINT0_1 INTMASK_TIMERINT0_1
-#define IRQMASK_TIMERINT2_3 INTMASK_TIMERINT2_3
-#define IRQMASK_GPIOINT0 INTMASK_GPIOINT0
-#define IRQMASK_GPIOINT1 INTMASK_GPIOINT1
-#define IRQMASK_GPIOINT2 INTMASK_GPIOINT2
-#define IRQMASK_GPIOINT3 INTMASK_GPIOINT3
-#define IRQMASK_RTCINT INTMASK_RTCINT
-#define IRQMASK_SSPINT INTMASK_SSPINT
-#define IRQMASK_UARTINT0 INTMASK_UARTINT0
-#define IRQMASK_UARTINT1 INTMASK_UARTINT1
-#define IRQMASK_UARTINT2 INTMASK_UARTINT2
-#define IRQMASK_SCIINT INTMASK_SCIINT
-#define IRQMASK_CLCDINT INTMASK_CLCDINT
-#define IRQMASK_DMAINT INTMASK_DMAINT
-#define IRQMASK_PWRFAILINT INTMASK_PWRFAILINT
-#define IRQMASK_MBXINT INTMASK_MBXINT
-#define IRQMASK_GNDINT INTMASK_GNDINT
-#define IRQMASK_VICSOURCE21 INTMASK_VICSOURCE21
-#define IRQMASK_VICSOURCE22 INTMASK_VICSOURCE22
-#define IRQMASK_VICSOURCE23 INTMASK_VICSOURCE23
-#define IRQMASK_VICSOURCE24 INTMASK_VICSOURCE24
-#define IRQMASK_VICSOURCE25 INTMASK_VICSOURCE25
-#define IRQMASK_VICSOURCE26 INTMASK_VICSOURCE26
-#define IRQMASK_VICSOURCE27 INTMASK_VICSOURCE27
-#define IRQMASK_VICSOURCE28 INTMASK_VICSOURCE28
-#define IRQMASK_VICSOURCE29 INTMASK_VICSOURCE29
-#define IRQMASK_VICSOURCE30 INTMASK_VICSOURCE30
-#define IRQMASK_VICSOURCE31 INTMASK_VICSOURCE31
-
/*
* FIQ interrupts definitions are the same as the INT definitions.
*/
@@ -130,39 +97,6 @@
#define FIQ_VICSOURCE31 INT_VICSOURCE31
-#define FIQMASK_WDOGINT INTMASK_WDOGINT
-#define FIQMASK_SOFTINT INTMASK_SOFTINT
-#define FIQMASK_COMMRx INTMASK_COMMRx
-#define FIQMASK_COMMTx INTMASK_COMMTx
-#define FIQMASK_TIMERINT0_1 INTMASK_TIMERINT0_1
-#define FIQMASK_TIMERINT2_3 INTMASK_TIMERINT2_3
-#define FIQMASK_GPIOINT0 INTMASK_GPIOINT0
-#define FIQMASK_GPIOINT1 INTMASK_GPIOINT1
-#define FIQMASK_GPIOINT2 INTMASK_GPIOINT2
-#define FIQMASK_GPIOINT3 INTMASK_GPIOINT3
-#define FIQMASK_RTCINT INTMASK_RTCINT
-#define FIQMASK_SSPINT INTMASK_SSPINT
-#define FIQMASK_UARTINT0 INTMASK_UARTINT0
-#define FIQMASK_UARTINT1 INTMASK_UARTINT1
-#define FIQMASK_UARTINT2 INTMASK_UARTINT2
-#define FIQMASK_SCIINT INTMASK_SCIINT
-#define FIQMASK_CLCDINT INTMASK_CLCDINT
-#define FIQMASK_DMAINT INTMASK_DMAINT
-#define FIQMASK_PWRFAILINT INTMASK_PWRFAILINT
-#define FIQMASK_MBXINT INTMASK_MBXINT
-#define FIQMASK_GNDINT INTMASK_GNDINT
-#define FIQMASK_VICSOURCE21 INTMASK_VICSOURCE21
-#define FIQMASK_VICSOURCE22 INTMASK_VICSOURCE22
-#define FIQMASK_VICSOURCE23 INTMASK_VICSOURCE23
-#define FIQMASK_VICSOURCE24 INTMASK_VICSOURCE24
-#define FIQMASK_VICSOURCE25 INTMASK_VICSOURCE25
-#define FIQMASK_VICSOURCE26 INTMASK_VICSOURCE26
-#define FIQMASK_VICSOURCE27 INTMASK_VICSOURCE27
-#define FIQMASK_VICSOURCE28 INTMASK_VICSOURCE28
-#define FIQMASK_VICSOURCE29 INTMASK_VICSOURCE29
-#define FIQMASK_VICSOURCE30 INTMASK_VICSOURCE30
-#define FIQMASK_VICSOURCE31 INTMASK_VICSOURCE31
-
/*
* Secondary interrupt controller
*/
@@ -188,24 +122,4 @@
#define IRQ_SIC_PCI3 (IRQ_SIC_START + SIC_INT_PCI3)
#define IRQ_SIC_END 63
-#define SIC_IRQMASK_MMCI0B SIC_INTMASK_MMCI0B
-#define SIC_IRQMASK_MMCI1B SIC_INTMASK_MMCI1B
-#define SIC_IRQMASK_KMI0 SIC_INTMASK_KMI0
-#define SIC_IRQMASK_KMI1 SIC_INTMASK_KMI1
-#define SIC_IRQMASK_SCI3 SIC_INTMASK_SCI3
-#define SIC_IRQMASK_UART3 SIC_INTMASK_UART3
-#define SIC_IRQMASK_CLCD SIC_INTMASK_CLCD
-#define SIC_IRQMASK_TOUCH SIC_INTMASK_TOUCH
-#define SIC_IRQMASK_KEYPAD SIC_INTMASK_KEYPAD
-#define SIC_IRQMASK_DoC SIC_INTMASK_DoC
-#define SIC_IRQMASK_MMCI0A SIC_INTMASK_MMCI0A
-#define SIC_IRQMASK_MMCI1A SIC_INTMASK_MMCI1A
-#define SIC_IRQMASK_AACI SIC_INTMASK_AACI
-#define SIC_IRQMASK_ETH SIC_INTMASK_ETH
-#define SIC_IRQMASK_USB SIC_INTMASK_USB
-#define SIC_IRQMASK_PCI0 SIC_INTMASK_PCI0
-#define SIC_IRQMASK_PCI1 SIC_INTMASK_PCI1
-#define SIC_IRQMASK_PCI2 SIC_INTMASK_PCI2
-#define SIC_IRQMASK_PCI3 SIC_INTMASK_PCI3
-
#define NR_IRQS 64
diff --git a/arch/arm/mach-versatile/include/mach/memory.h b/arch/arm/mach-versatile/include/mach/memory.h
index b6315c0602ac..79aeab86b903 100644
--- a/arch/arm/mach-versatile/include/mach/memory.h
+++ b/arch/arm/mach-versatile/include/mach/memory.h
@@ -25,14 +25,4 @@
*/
#define PHYS_OFFSET UL(0x00000000)
-/*
- * Virtual view <-> DMA view memory address translations
- * virt_to_bus: Used to translate the virtual address to an
- * address suitable to be passed to set_dma_addr
- * bus_to_virt: Used to convert an address for DMA operations
- * to an address that the kernel can use.
- */
-#define __virt_to_bus(x) ((x) - PAGE_OFFSET)
-#define __bus_to_virt(x) ((x) + PAGE_OFFSET)
-
#endif
diff --git a/arch/arm/mach-versatile/include/mach/platform.h b/arch/arm/mach-versatile/include/mach/platform.h
index f91ba930ca8a..83207395191a 100644
--- a/arch/arm/mach-versatile/include/mach/platform.h
+++ b/arch/arm/mach-versatile/include/mach/platform.h
@@ -347,44 +347,6 @@
#define INT_VICSOURCE30 30 /* PCI 3 */
#define INT_VICSOURCE31 31 /* SIC source */
-/*
- * Interrupt bit positions
- *
- */
-#define INTMASK_WDOGINT (1 << INT_WDOGINT)
-#define INTMASK_SOFTINT (1 << INT_SOFTINT)
-#define INTMASK_COMMRx (1 << INT_COMMRx)
-#define INTMASK_COMMTx (1 << INT_COMMTx)
-#define INTMASK_TIMERINT0_1 (1 << INT_TIMERINT0_1)
-#define INTMASK_TIMERINT2_3 (1 << INT_TIMERINT2_3)
-#define INTMASK_GPIOINT0 (1 << INT_GPIOINT0)
-#define INTMASK_GPIOINT1 (1 << INT_GPIOINT1)
-#define INTMASK_GPIOINT2 (1 << INT_GPIOINT2)
-#define INTMASK_GPIOINT3 (1 << INT_GPIOINT3)
-#define INTMASK_RTCINT (1 << INT_RTCINT)
-#define INTMASK_SSPINT (1 << INT_SSPINT)
-#define INTMASK_UARTINT0 (1 << INT_UARTINT0)
-#define INTMASK_UARTINT1 (1 << INT_UARTINT1)
-#define INTMASK_UARTINT2 (1 << INT_UARTINT2)
-#define INTMASK_SCIINT (1 << INT_SCIINT)
-#define INTMASK_CLCDINT (1 << INT_CLCDINT)
-#define INTMASK_DMAINT (1 << INT_DMAINT)
-#define INTMASK_PWRFAILINT (1 << INT_PWRFAILINT)
-#define INTMASK_MBXINT (1 << INT_MBXINT)
-#define INTMASK_GNDINT (1 << INT_GNDINT)
-#define INTMASK_VICSOURCE21 (1 << INT_VICSOURCE21)
-#define INTMASK_VICSOURCE22 (1 << INT_VICSOURCE22)
-#define INTMASK_VICSOURCE23 (1 << INT_VICSOURCE23)
-#define INTMASK_VICSOURCE24 (1 << INT_VICSOURCE24)
-#define INTMASK_VICSOURCE25 (1 << INT_VICSOURCE25)
-#define INTMASK_VICSOURCE26 (1 << INT_VICSOURCE26)
-#define INTMASK_VICSOURCE27 (1 << INT_VICSOURCE27)
-#define INTMASK_VICSOURCE28 (1 << INT_VICSOURCE28)
-#define INTMASK_VICSOURCE29 (1 << INT_VICSOURCE29)
-#define INTMASK_VICSOURCE30 (1 << INT_VICSOURCE30)
-#define INTMASK_VICSOURCE31 (1 << INT_VICSOURCE31)
-
-
#define VERSATILE_SC_VALID_INT 0x003FFFFF
#define MAXIRQNUM 31
@@ -417,26 +379,6 @@
#define SIC_INT_PCI3 30
-#define SIC_INTMASK_MMCI0B (1 << SIC_INT_MMCI0B)
-#define SIC_INTMASK_MMCI1B (1 << SIC_INT_MMCI1B)
-#define SIC_INTMASK_KMI0 (1 << SIC_INT_KMI0)
-#define SIC_INTMASK_KMI1 (1 << SIC_INT_KMI1)
-#define SIC_INTMASK_SCI3 (1 << SIC_INT_SCI3)
-#define SIC_INTMASK_UART3 (1 << SIC_INT_UART3)
-#define SIC_INTMASK_CLCD (1 << SIC_INT_CLCD)
-#define SIC_INTMASK_TOUCH (1 << SIC_INT_TOUCH)
-#define SIC_INTMASK_KEYPAD (1 << SIC_INT_KEYPAD)
-#define SIC_INTMASK_DoC (1 << SIC_INT_DoC)
-#define SIC_INTMASK_MMCI0A (1 << SIC_INT_MMCI0A)
-#define SIC_INTMASK_MMCI1A (1 << SIC_INT_MMCI1A)
-#define SIC_INTMASK_AACI (1 << SIC_INT_AACI)
-#define SIC_INTMASK_ETH (1 << SIC_INT_ETH)
-#define SIC_INTMASK_USB (1 << SIC_INT_USB)
-#define SIC_INTMASK_PCI0 (1 << SIC_INT_PCI0)
-#define SIC_INTMASK_PCI1 (1 << SIC_INT_PCI1)
-#define SIC_INTMASK_PCI2 (1 << SIC_INT_PCI2)
-#define SIC_INTMASK_PCI3 (1 << SIC_INT_PCI3)
-
/*
* Clean base - dummy
*
diff --git a/arch/arm/mach-w90x900/Kconfig b/arch/arm/mach-w90x900/Kconfig
new file mode 100644
index 000000000000..8e4178fe5ec2
--- /dev/null
+++ b/arch/arm/mach-w90x900/Kconfig
@@ -0,0 +1,19 @@
+if ARCH_W90X900
+
+config CPU_W90P910
+ bool
+ help
+ Support for W90P910 of Nuvoton W90X900 CPUs.
+
+menu "W90P910 Machines"
+
+config MACH_W90P910EVB
+ bool "Nuvoton W90P910 Evaluation Board"
+ default y
+ select CPU_W90P910
+ help
+ Say Y here if you are using the Nuvoton W90P910EVB
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-w90x900/Makefile b/arch/arm/mach-w90x900/Makefile
new file mode 100644
index 000000000000..0c0c1d63f1c7
--- /dev/null
+++ b/arch/arm/mach-w90x900/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := irq.o time.o
+
+# W90X900 CPU support files
+
+obj-$(CONFIG_CPU_W90P910) += w90p910.o
+
+# machine support
+
+obj-$(CONFIG_MACH_W90P910EVB) += mach-w90p910evb.o
diff --git a/arch/arm/mach-w90x900/Makefile.boot b/arch/arm/mach-w90x900/Makefile.boot
new file mode 100644
index 000000000000..a057b546b6e5
--- /dev/null
+++ b/arch/arm/mach-w90x900/Makefile.boot
@@ -0,0 +1,3 @@
+zreladdr-y := 0x00008000
+params_phys-y := 0x00000100
+
diff --git a/arch/arm/mach-w90x900/cpu.h b/arch/arm/mach-w90x900/cpu.h
new file mode 100644
index 000000000000..40ff40845df0
--- /dev/null
+++ b/arch/arm/mach-w90x900/cpu.h
@@ -0,0 +1,77 @@
+/*
+ * arch/arm/mach-w90x900/cpu.h
+ *
+ * Based on linux/include/asm-arm/plat-s3c24xx/cpu.h by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Header file for W90X900 CPU support
+ *
+ * Wan ZongShun <mcuos.com@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.
+ *
+ */
+
+#define IODESC_ENT(y) \
+{ \
+ .virtual = (unsigned long)W90X900_VA_##y, \
+ .pfn = __phys_to_pfn(W90X900_PA_##y), \
+ .length = W90X900_SZ_##y, \
+ .type = MT_DEVICE, \
+}
+
+/*Cpu identifier register*/
+
+#define W90X900PDID W90X900_VA_GCR
+#define W90P910_CPUID 0x02900910
+#define W90P920_CPUID 0x02900920
+#define W90P950_CPUID 0x02900950
+#define W90N960_CPUID 0x02900960
+
+struct w90x900_uartcfg;
+struct map_desc;
+struct sys_timer;
+
+/* core initialisation functions */
+
+extern void w90x900_init_irq(void);
+extern void w90p910_init_io(struct map_desc *mach_desc, int size);
+extern void w90p910_init_uarts(struct w90x900_uartcfg *cfg, int no);
+extern void w90p910_init_clocks(int xtal);
+extern void w90p910_map_io(struct map_desc *mach_desc, int size);
+extern struct sys_timer w90x900_timer;
+
+#define W90X900_RES(name) \
+struct resource w90x900_##name##_resource[] = { \
+ [0] = { \
+ .start = name##_PA, \
+ .end = name##_PA + 0x0ff, \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ [1] = { \
+ .start = IRQ_##name, \
+ .end = IRQ_##name, \
+ .flags = IORESOURCE_IRQ, \
+ } \
+}
+
+#define W90X900_DEVICE(devname, regname, devid, platdevname) \
+struct platform_device w90x900_##devname = { \
+ .name = platdevname, \
+ .id = devid, \
+ .num_resources = ARRAY_SIZE(w90x900_##regname##_resource), \
+ .resource = w90x900_##regname##_resource, \
+}
+
+#define W90X900_UARTCFG(port, flag, uc, ulc, ufc) \
+{ \
+ .hwport = port, \
+ .flags = flag, \
+ .ucon = uc, \
+ .ulcon = ulc, \
+ .ufcon = ufc, \
+}
diff --git a/arch/arm/mach-w90x900/include/mach/entry-macro.S b/arch/arm/mach-w90x900/include/mach/entry-macro.S
new file mode 100644
index 000000000000..d39aca5be9ee
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/entry-macro.S
@@ -0,0 +1,34 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for W90P910-based platforms
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ */
+
+#include <mach/hardware.h>
+#include <mach/regs-irq.h>
+
+ .macro get_irqnr_preamble, base, tmp
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ mov \base, #AIC_BA
+
+ ldr \irqnr, [ \base, #AIC_IPER]
+ ldr \irqnr, [ \base, #AIC_ISNR]
+ cmp \irqnr, #0
+
+ .endm
+
+ /* currently don't need an disable_fiq macro */
+
+ .macro disable_fiq
+ .endm
diff --git a/arch/arm/mach-w90x900/include/mach/hardware.h b/arch/arm/mach-w90x900/include/mach/hardware.h
new file mode 100644
index 000000000000..fe3c6265a466
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/hardware.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/hardware.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/hardware.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#include <asm/sizes.h>
+#include <mach/map.h>
+
+#endif /* __ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-w90x900/include/mach/io.h b/arch/arm/mach-w90x900/include/mach/io.h
new file mode 100644
index 000000000000..d96ab99df05b
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/io.h
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/io.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/io.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+/*
+ * 1:1 mapping for ioremapped regions.
+ */
+
+#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+
+#endif
diff --git a/arch/arm/mach-w90x900/include/mach/irqs.h b/arch/arm/mach-w90x900/include/mach/irqs.h
new file mode 100644
index 000000000000..1c583f9cbcde
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/irqs.h
@@ -0,0 +1,45 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/irqs.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/irqs.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H
+
+/*
+ * we keep the first set of CPU IRQs out of the range of
+ * the ISA space, so that the PC104 has them to itself
+ * and we don't end up having to do horrible things to the
+ * standard ISA drivers....
+ *
+ */
+
+#define W90X900_IRQ(x) (x)
+
+/* Main cpu interrupts */
+
+#define IRQ_WDT W90X900_IRQ(1)
+#define IRQ_UART0 W90X900_IRQ(7)
+#define IRQ_UART1 W90X900_IRQ(8)
+#define IRQ_UART2 W90X900_IRQ(9)
+#define IRQ_UART3 W90X900_IRQ(10)
+#define IRQ_UART4 W90X900_IRQ(11)
+#define IRQ_TIMER0 W90X900_IRQ(12)
+#define IRQ_TIMER1 W90X900_IRQ(13)
+#define IRQ_T_INT_GROUP W90X900_IRQ(14)
+#define IRQ_ADC W90X900_IRQ(31)
+#define NR_IRQS (IRQ_ADC+1)
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-w90x900/include/mach/map.h b/arch/arm/mach-w90x900/include/mach/map.h
new file mode 100644
index 000000000000..79320ebe614b
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/map.h
@@ -0,0 +1,76 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/map.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/map.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_MAP_H
+#define __ASM_ARCH_MAP_H
+
+#ifndef __ASSEMBLY__
+#define W90X900_ADDR(x) ((void __iomem *)(0xF0000000 + (x)))
+#else
+#define W90X900_ADDR(x) (0xF0000000 + (x))
+#endif
+
+#define AHB_IO_BASE 0xB0000000
+#define APB_IO_BASE 0xB8000000
+#define CLOCKPW_BASE (APB_IO_BASE+0x200)
+#define AIC_IO_BASE (APB_IO_BASE+0x2000)
+#define TIMER_IO_BASE (APB_IO_BASE+0x1000)
+
+/*
+ * interrupt controller is the first thing we put in, to make
+ * the assembly code for the irq detection easier
+ */
+
+#define W90X900_VA_IRQ W90X900_ADDR(0x00000000)
+#define W90X900_PA_IRQ (0xB8002000)
+#define W90X900_SZ_IRQ SZ_4K
+
+#define W90X900_VA_GCR W90X900_ADDR(0x08002000)
+#define W90X900_PA_GCR (0xB0000000)
+#define W90X900_SZ_GCR SZ_4K
+
+/* Clock and Power management */
+
+#define W90X900_VA_CLKPWR (W90X900_VA_GCR+0x200)
+#define W90X900_PA_CLKPWR (0xB0000200)
+#define W90X900_SZ_CLKPWR SZ_4K
+
+/* EBI management */
+
+#define W90X900_VA_EBI W90X900_ADDR(0x00001000)
+#define W90X900_PA_EBI (0xB0001000)
+#define W90X900_SZ_EBI SZ_4K
+
+/* UARTs */
+
+#define W90X900_VA_UART W90X900_ADDR(0x08000000)
+#define W90X900_PA_UART (0xB8000000)
+#define W90X900_SZ_UART SZ_4K
+
+/* Timers */
+
+#define W90X900_VA_TIMER W90X900_ADDR(0x08001000)
+#define W90X900_PA_TIMER (0xB8001000)
+#define W90X900_SZ_TIMER SZ_4K
+
+/* GPIO ports */
+
+#define W90X900_VA_GPIO W90X900_ADDR(0x08003000)
+#define W90X900_PA_GPIO (0xB8003000)
+#define W90X900_SZ_GPIO SZ_4K
+
+#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/mach-w90x900/include/mach/memory.h b/arch/arm/mach-w90x900/include/mach/memory.h
new file mode 100644
index 000000000000..971b80702c27
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/memory.h
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/memory.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/memory.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#define PHYS_OFFSET UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-w90x900/include/mach/regs-irq.h b/arch/arm/mach-w90x900/include/mach/regs-irq.h
new file mode 100644
index 000000000000..8a3185fbc9cf
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/regs-irq.h
@@ -0,0 +1,51 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/regs-irq.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/regs-irq.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef ___ASM_ARCH_REGS_IRQ_H
+#define ___ASM_ARCH_REGS_IRQ_H
+
+/* Advance Interrupt Controller (AIC) Registers */
+
+#define AIC_BA W90X900_VA_IRQ
+
+#define REG_AIC_IRQSC (AIC_BA+0x80)
+#define REG_AIC_GEN (AIC_BA+0x84)
+#define REG_AIC_GASR (AIC_BA+0x88)
+#define REG_AIC_GSCR (AIC_BA+0x8C)
+#define REG_AIC_IRSR (AIC_BA+0x100)
+#define REG_AIC_IASR (AIC_BA+0x104)
+#define REG_AIC_ISR (AIC_BA+0x108)
+#define REG_AIC_IPER (AIC_BA+0x10C)
+#define REG_AIC_ISNR (AIC_BA+0x110)
+#define REG_AIC_IMR (AIC_BA+0x114)
+#define REG_AIC_OISR (AIC_BA+0x118)
+#define REG_AIC_MECR (AIC_BA+0x120)
+#define REG_AIC_MDCR (AIC_BA+0x124)
+#define REG_AIC_SSCR (AIC_BA+0x128)
+#define REG_AIC_SCCR (AIC_BA+0x12C)
+#define REG_AIC_EOSCR (AIC_BA+0x130)
+#define AIC_IPER (0x10C)
+#define AIC_ISNR (0x110)
+
+/*16-18 bits of REG_AIC_GEN define irq(2-4) group*/
+
+#define TIMER2_IRQ (1 << 16)
+#define TIMER3_IRQ (1 << 17)
+#define TIMER4_IRQ (1 << 18)
+#define TIME_GROUP_IRQ (TIMER2_IRQ|TIMER3_IRQ|TIMER4_IRQ)
+
+#endif /* ___ASM_ARCH_REGS_IRQ_H */
diff --git a/arch/arm/mach-w90x900/include/mach/regs-serial.h b/arch/arm/mach-w90x900/include/mach/regs-serial.h
new file mode 100644
index 000000000000..f08fa0d75e11
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/regs-serial.h
@@ -0,0 +1,59 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/regs-serial.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/regs-serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARM_REGS_SERIAL_H
+#define __ASM_ARM_REGS_SERIAL_H
+
+#define UART0_BA W90X900_VA_UART
+#define UART1_BA (W90X900_VA_UART+0x100)
+#define UART2_BA (W90X900_VA_UART+0x200)
+#define UART3_BA (W90X900_VA_UART+0x300)
+#define UART4_BA (W90X900_VA_UART+0x400)
+
+#define UART0_PA W90X900_PA_UART
+#define UART1_PA (W90X900_PA_UART+0x100)
+#define UART2_PA (W90X900_PA_UART+0x200)
+#define UART3_PA (W90X900_PA_UART+0x300)
+#define UART4_PA (W90X900_PA_UART+0x400)
+
+#ifndef __ASSEMBLY__
+
+struct w90x900_uart_clksrc {
+ const char *name;
+ unsigned int divisor;
+ unsigned int min_baud;
+ unsigned int max_baud;
+};
+
+struct w90x900_uartcfg {
+ unsigned char hwport;
+ unsigned char unused;
+ unsigned short flags;
+ unsigned long uart_flags;
+
+ unsigned long ucon;
+ unsigned long ulcon;
+ unsigned long ufcon;
+
+ struct w90x900_uart_clksrc *clocks;
+ unsigned int clocks_size;
+};
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_REGS_SERIAL_H */
+
diff --git a/arch/arm/mach-w90x900/include/mach/regs-timer.h b/arch/arm/mach-w90x900/include/mach/regs-timer.h
new file mode 100644
index 000000000000..8f390620c0e4
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/regs-timer.h
@@ -0,0 +1,42 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/regs-timer.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/regs-timer.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_REGS_TIMER_H
+#define __ASM_ARCH_REGS_TIMER_H
+
+/* Timer Registers */
+
+#define TMR_BA W90X900_VA_TIMER
+#define REG_TCSR0 (TMR_BA+0x00)
+#define REG_TCSR1 (TMR_BA+0x04)
+#define REG_TICR0 (TMR_BA+0x08)
+#define REG_TICR1 (TMR_BA+0x0C)
+#define REG_TDR0 (TMR_BA+0x10)
+#define REG_TDR1 (TMR_BA+0x14)
+#define REG_TISR (TMR_BA+0x18)
+#define REG_WTCR (TMR_BA+0x1C)
+#define REG_TCSR2 (TMR_BA+0x20)
+#define REG_TCSR3 (TMR_BA+0x24)
+#define REG_TICR2 (TMR_BA+0x28)
+#define REG_TICR3 (TMR_BA+0x2C)
+#define REG_TDR2 (TMR_BA+0x30)
+#define REG_TDR3 (TMR_BA+0x34)
+#define REG_TCSR4 (TMR_BA+0x40)
+#define REG_TICR4 (TMR_BA+0x48)
+#define REG_TDR4 (TMR_BA+0x50)
+
+#endif /* __ASM_ARCH_REGS_TIMER_H */
diff --git a/arch/arm/mach-w90x900/include/mach/system.h b/arch/arm/mach-w90x900/include/mach/system.h
new file mode 100644
index 000000000000..93753f922618
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/system.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/system.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/system.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <asm/proc-fns.h>
+
+static void arch_idle(void)
+{
+}
+
+static void arch_reset(char mode)
+{
+ cpu_reset(0);
+}
+
diff --git a/arch/arm/mach-w90x900/include/mach/timex.h b/arch/arm/mach-w90x900/include/mach/timex.h
new file mode 100644
index 000000000000..164dce0b64db
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/timex.h
@@ -0,0 +1,25 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/timex.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/timex.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_TIMEX_H
+#define __ASM_ARCH_TIMEX_H
+
+/* CLOCK_TICK_RATE Now, I don't use it. */
+
+#define CLOCK_TICK_RATE 15000000
+
+#endif /* __ASM_ARCH_TIMEX_H */
diff --git a/arch/arm/mach-w90x900/include/mach/uncompress.h b/arch/arm/mach-w90x900/include/mach/uncompress.h
new file mode 100644
index 000000000000..050d9fe5ae1b
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/uncompress.h
@@ -0,0 +1,40 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/uncompress.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/uncompress.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+/* Defines for UART registers */
+
+#include <mach/regs-serial.h>
+#include <mach/map.h>
+
+#define arch_decomp_wdog()
+
+static void putc(int ch)
+{
+}
+
+static inline void flush(void)
+{
+}
+
+static void arch_decomp_setup(void)
+{
+}
+
+#endif/* __ASM_W90X900_UNCOMPRESS_H */
diff --git a/arch/arm/mach-w90x900/include/mach/vmalloc.h b/arch/arm/mach-w90x900/include/mach/vmalloc.h
new file mode 100644
index 000000000000..2f9dfb928533
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/vmalloc.h
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/vmalloc.h
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/vmalloc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef __ASM_ARCH_VMALLOC_H
+#define __ASM_ARCH_VMALLOC_H
+
+#define VMALLOC_END (0xE0000000)
+
+#endif /* __ASM_ARCH_VMALLOC_H */
diff --git a/arch/arm/mach-w90x900/irq.c b/arch/arm/mach-w90x900/irq.c
new file mode 100644
index 000000000000..0b4fc194729c
--- /dev/null
+++ b/arch/arm/mach-w90x900/irq.c
@@ -0,0 +1,76 @@
+/*
+ * linux/arch/arm/mach-w90x900/irq.c
+ *
+ * based on linux/arch/arm/plat-s3c24xx/irq.c by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/ptrace.h>
+#include <linux/sysdev.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-irq.h>
+
+static void w90x900_irq_mask(unsigned int irq)
+{
+ __raw_writel(1 << irq, REG_AIC_MDCR);
+}
+
+/*
+ * By the w90p910 spec,any irq,only write 1
+ * to REG_AIC_EOSCR for ACK
+ */
+
+static void w90x900_irq_ack(unsigned int irq)
+{
+ __raw_writel(0x01, REG_AIC_EOSCR);
+}
+
+static void w90x900_irq_unmask(unsigned int irq)
+{
+ unsigned long mask;
+
+ if (irq == IRQ_T_INT_GROUP) {
+ mask = __raw_readl(REG_AIC_GEN);
+ __raw_writel(TIME_GROUP_IRQ | mask, REG_AIC_GEN);
+ __raw_writel(1 << IRQ_T_INT_GROUP, REG_AIC_MECR);
+ }
+ __raw_writel(1 << irq, REG_AIC_MECR);
+}
+
+static struct irq_chip w90x900_irq_chip = {
+ .ack = w90x900_irq_ack,
+ .mask = w90x900_irq_mask,
+ .unmask = w90x900_irq_unmask,
+};
+
+void __init w90x900_init_irq(void)
+{
+ int irqno;
+
+ __raw_writel(0xFFFFFFFE, REG_AIC_MDCR);
+
+ for (irqno = IRQ_WDT; irqno <= IRQ_ADC; irqno++) {
+ set_irq_chip(irqno, &w90x900_irq_chip);
+ set_irq_handler(irqno, handle_level_irq);
+ set_irq_flags(irqno, IRQF_VALID);
+ }
+}
diff --git a/arch/arm/mach-w90x900/mach-w90p910evb.c b/arch/arm/mach-w90x900/mach-w90p910evb.c
new file mode 100644
index 000000000000..9307a2475438
--- /dev/null
+++ b/arch/arm/mach-w90x900/mach-w90p910evb.c
@@ -0,0 +1,72 @@
+/*
+ * linux/arch/arm/mach-w90x900/mach-w90p910evb.c
+ *
+ * Based on mach-s3c2410/mach-smdk2410.c by Jonas Dietsche
+ *
+ * Copyright (C) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/regs-serial.h>
+
+#include "cpu.h"
+
+static struct map_desc w90p910_iodesc[] __initdata = {
+};
+
+static struct w90x900_uartcfg w90p910_uartcfgs[] = {
+ W90X900_UARTCFG(0, 0, 0, 0, 0),
+ W90X900_UARTCFG(1, 0, 0, 0, 0),
+ W90X900_UARTCFG(2, 0, 0, 0, 0),
+ W90X900_UARTCFG(3, 0, 0, 0, 0),
+ W90X900_UARTCFG(4, 0, 0, 0, 0),
+};
+
+/*Here should be your evb resourse,such as LCD*/
+
+static struct platform_device *w90p910evb_dev[] __initdata = {
+};
+
+static void __init w90p910evb_map_io(void)
+{
+ w90p910_map_io(w90p910_iodesc, ARRAY_SIZE(w90p910_iodesc));
+ w90p910_init_clocks(0);
+ w90p910_init_uarts(w90p910_uartcfgs, ARRAY_SIZE(w90p910_uartcfgs));
+}
+
+static void __init w90p910evb_init(void)
+{
+ platform_add_devices(w90p910evb_dev, ARRAY_SIZE(w90p910evb_dev));
+}
+
+MACHINE_START(W90P910EVB, "W90P910EVB")
+ /* Maintainer: Wan ZongShun */
+ .phys_io = W90X900_PA_UART,
+ .io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,
+ .boot_params = 0,
+ .map_io = w90p910evb_map_io,
+ .init_irq = w90x900_init_irq,
+ .init_machine = w90p910evb_init,
+ .timer = &w90x900_timer,
+MACHINE_END
diff --git a/arch/arm/mach-w90x900/time.c b/arch/arm/mach-w90x900/time.c
new file mode 100644
index 000000000000..3a69e381f316
--- /dev/null
+++ b/arch/arm/mach-w90x900/time.c
@@ -0,0 +1,80 @@
+/*
+ * linux/arch/arm/mach-w90x900/time.c
+ *
+ * Based on linux/arch/arm/plat-s3c24xx/time.c by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+
+#include <mach/system.h>
+#include <mach/map.h>
+#include <mach/regs-timer.h>
+
+static unsigned long w90x900_gettimeoffset(void)
+{
+ return 0;
+}
+
+/*IRQ handler for the timer*/
+
+static irqreturn_t
+w90x900_timer_interrupt(int irq, void *dev_id)
+{
+ timer_tick();
+ __raw_writel(0x01, REG_TISR); /* clear TIF0 */
+ return IRQ_HANDLED;
+}
+
+static struct irqaction w90x900_timer_irq = {
+ .name = "w90x900 Timer Tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = w90x900_timer_interrupt,
+};
+
+/*Set up timer reg.*/
+
+static void w90x900_timer_setup(void)
+{
+ __raw_writel(0, REG_TCSR0);
+ __raw_writel(0, REG_TCSR1);
+ __raw_writel(0, REG_TCSR2);
+ __raw_writel(0, REG_TCSR3);
+ __raw_writel(0, REG_TCSR4);
+ __raw_writel(0x1F, REG_TISR);
+ __raw_writel(15000000/(100 * 100), REG_TICR0);
+ __raw_writel(0x68000063, REG_TCSR0);
+}
+
+static void __init w90x900_timer_init(void)
+{
+ w90x900_timer_setup();
+ setup_irq(IRQ_TIMER0, &w90x900_timer_irq);
+}
+
+struct sys_timer w90x900_timer = {
+ .init = w90x900_timer_init,
+ .offset = w90x900_gettimeoffset,
+ .resume = w90x900_timer_setup
+};
diff --git a/arch/arm/mach-w90x900/w90p910.c b/arch/arm/mach-w90x900/w90p910.c
new file mode 100644
index 000000000000..aa783bc94310
--- /dev/null
+++ b/arch/arm/mach-w90x900/w90p910.c
@@ -0,0 +1,134 @@
+/*
+ * linux/arch/arm/mach-w90x900/w90p910.c
+ *
+ * Based on linux/arch/arm/plat-s3c24xx/s3c244x.c by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ * All rights reserved.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * W90P910 cpu support
+ *
+ * This program is free software; you can redistribute 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/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-serial.h>
+
+#include "cpu.h"
+
+/*W90P910 has five uarts*/
+
+#define MAX_UART_COUNT 5
+static int uart_count;
+static struct platform_device *uart_devs[MAX_UART_COUNT-1];
+
+/* Initial IO mappings */
+
+static struct map_desc w90p910_iodesc[] __initdata = {
+ IODESC_ENT(IRQ),
+ IODESC_ENT(GCR),
+ IODESC_ENT(UART),
+ IODESC_ENT(TIMER),
+ IODESC_ENT(EBI),
+ /*IODESC_ENT(LCD),*/
+};
+
+/*Init the dev resource*/
+
+static W90X900_RES(UART0);
+static W90X900_RES(UART1);
+static W90X900_RES(UART2);
+static W90X900_RES(UART3);
+static W90X900_RES(UART4);
+static W90X900_DEVICE(uart0, UART0, 0, "w90x900-uart");
+static W90X900_DEVICE(uart1, UART1, 1, "w90x900-uart");
+static W90X900_DEVICE(uart2, UART2, 2, "w90x900-uart");
+static W90X900_DEVICE(uart3, UART3, 3, "w90x900-uart");
+static W90X900_DEVICE(uart4, UART4, 4, "w90x900-uart");
+
+static struct platform_device *uart_devices[] __initdata = {
+ &w90x900_uart0,
+ &w90x900_uart1,
+ &w90x900_uart2,
+ &w90x900_uart3,
+ &w90x900_uart4
+};
+
+/*Init W90P910 uart device*/
+
+void __init w90p910_init_uarts(struct w90x900_uartcfg *cfg, int no)
+{
+ struct platform_device *platdev;
+ int uart, uartdev;
+
+ /*By min() to judge count of uart be used indeed*/
+
+ uartdev = ARRAY_SIZE(uart_devices);
+ no = min(uartdev, no);
+
+ for (uart = 0; uart < no; uart++, cfg++) {
+ if (cfg->hwport != uart)
+ printk(KERN_ERR "w90x900_uartcfg[%d] error\n", uart);
+ platdev = uart_devices[cfg->hwport];
+ uart_devs[uart] = platdev;
+ platdev->dev.platform_data = cfg;
+ }
+ uart_count = uart;
+}
+
+/*Init W90P910 evb io*/
+
+void __init w90p910_map_io(struct map_desc *mach_desc, int mach_size)
+{
+ unsigned long idcode = 0x0;
+
+ iotable_init(w90p910_iodesc, ARRAY_SIZE(w90p910_iodesc));
+
+ idcode = __raw_readl(W90X900PDID);
+ if (idcode != W90P910_CPUID)
+ printk(KERN_ERR "CPU type 0x%08lx is not W90P910\n", idcode);
+}
+
+/*Init W90P910 clock*/
+
+void __init w90p910_init_clocks(int xtal)
+{
+}
+
+static int __init w90p910_init_cpu(void)
+{
+ return 0;
+}
+
+static int __init w90x900_arch_init(void)
+{
+ int ret;
+
+ ret = w90p910_init_cpu();
+ if (ret != 0)
+ return ret;
+
+ return platform_add_devices(uart_devs, uart_count);
+
+}
+arch_initcall(w90x900_arch_init);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index ab5f7a21350b..d490f3773c01 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -10,8 +10,7 @@ config CPU_32
# ARM610
config CPU_ARM610
- bool "Support ARM610 processor"
- depends on ARCH_RPC
+ bool "Support ARM610 processor" if ARCH_RPC
select CPU_32v3
select CPU_CACHE_V3
select CPU_CACHE_VIVT
@@ -43,8 +42,7 @@ config CPU_ARM7TDMI
# ARM710
config CPU_ARM710
- bool "Support ARM710 processor" if !ARCH_CLPS7500 && ARCH_RPC
- default y if ARCH_CLPS7500
+ bool "Support ARM710 processor" if ARCH_RPC
select CPU_32v3
select CPU_CACHE_V3
select CPU_CACHE_VIVT
@@ -63,8 +61,7 @@ config CPU_ARM710
# ARM720T
config CPU_ARM720T
- bool "Support ARM720T processor" if !ARCH_CLPS711X && !ARCH_L7200 && !ARCH_CDB89712 && ARCH_INTEGRATOR
- default y if ARCH_CLPS711X || ARCH_L7200 || ARCH_CDB89712 || ARCH_H720X
+ bool "Support ARM720T processor" if ARCH_INTEGRATOR
select CPU_32v4T
select CPU_ABRT_LV4T
select CPU_PABRT_NOIFAR
@@ -114,9 +111,7 @@ config CPU_ARM9TDMI
# ARM920T
config CPU_ARM920T
- bool "Support ARM920T processor"
- depends on ARCH_EP93XX || ARCH_INTEGRATOR || CPU_S3C2410 || CPU_S3C2440 || CPU_S3C2442 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200
- default y if CPU_S3C2410 || CPU_S3C2440 || CPU_S3C2442 || ARCH_AT91RM9200
+ bool "Support ARM920T processor" if ARCH_INTEGRATOR
select CPU_32v4T
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -138,8 +133,6 @@ config CPU_ARM920T
# ARM922T
config CPU_ARM922T
bool "Support ARM922T processor" if ARCH_INTEGRATOR
- depends on ARCH_LH7A40X || ARCH_INTEGRATOR || ARCH_KS8695
- default y if ARCH_LH7A40X || ARCH_KS8695
select CPU_32v4T
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -159,8 +152,6 @@ config CPU_ARM922T
# ARM925T
config CPU_ARM925T
bool "Support ARM925T processor" if ARCH_OMAP1
- depends on ARCH_OMAP15XX
- default y if ARCH_OMAP15XX
select CPU_32v4T
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -179,22 +170,7 @@ config CPU_ARM925T
# ARM926T
config CPU_ARM926T
- bool "Support ARM926T processor"
- depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || \
- MACH_VERSATILE_AB || ARCH_OMAP730 || \
- ARCH_OMAP16XX || MACH_REALVIEW_EB || \
- ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || \
- ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || \
- ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || \
- ARCH_AT91SAM9G20 || ARCH_AT91CAP9 || \
- ARCH_NS9XXX || ARCH_DAVINCI || ARCH_MX2
- default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || \
- ARCH_OMAP730 || ARCH_OMAP16XX || \
- ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || \
- ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || \
- ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || \
- ARCH_AT91SAM9G20 || ARCH_AT91CAP9 || \
- ARCH_NS9XXX || ARCH_DAVINCI || ARCH_MX2
+ bool "Support ARM926T processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB
select CPU_32v5
select CPU_ABRT_EV5TJ
select CPU_PABRT_NOIFAR
@@ -247,8 +223,7 @@ config CPU_ARM946E
# ARM1020 - needs validating
config CPU_ARM1020
- bool "Support ARM1020T (rev 0) processor"
- depends on ARCH_INTEGRATOR
+ bool "Support ARM1020T (rev 0) processor" if ARCH_INTEGRATOR
select CPU_32v5
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -266,8 +241,7 @@ config CPU_ARM1020
# ARM1020E - needs validating
config CPU_ARM1020E
- bool "Support ARM1020E processor"
- depends on ARCH_INTEGRATOR
+ bool "Support ARM1020E processor" if ARCH_INTEGRATOR
select CPU_32v5
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -280,8 +254,7 @@ config CPU_ARM1020E
# ARM1022E
config CPU_ARM1022
- bool "Support ARM1022E processor"
- depends on ARCH_INTEGRATOR
+ bool "Support ARM1022E processor" if ARCH_INTEGRATOR
select CPU_32v5
select CPU_ABRT_EV4T
select CPU_PABRT_NOIFAR
@@ -299,8 +272,7 @@ config CPU_ARM1022
# ARM1026EJ-S
config CPU_ARM1026
- bool "Support ARM1026EJ-S processor"
- depends on ARCH_INTEGRATOR
+ bool "Support ARM1026EJ-S processor" if ARCH_INTEGRATOR
select CPU_32v5
select CPU_ABRT_EV5T # But need Jazelle, but EV5TJ ignores bit 10
select CPU_PABRT_NOIFAR
@@ -317,8 +289,7 @@ config CPU_ARM1026
# SA110
config CPU_SA110
- bool "Support StrongARM(R) SA-110 processor" if !ARCH_EBSA110 && !FOOTBRIDGE && !ARCH_TBOX && !ARCH_SHARK && !ARCH_NEXUSPCI && ARCH_RPC
- default y if ARCH_EBSA110 || FOOTBRIDGE || ARCH_TBOX || ARCH_SHARK || ARCH_NEXUSPCI
+ bool "Support StrongARM(R) SA-110 processor" if ARCH_RPC
select CPU_32v3 if ARCH_RPC
select CPU_32v4 if !ARCH_RPC
select CPU_ABRT_EV4
@@ -340,8 +311,6 @@ config CPU_SA110
# SA1100
config CPU_SA1100
bool
- depends on ARCH_SA1100
- default y
select CPU_32v4
select CPU_ABRT_EV4
select CPU_PABRT_NOIFAR
@@ -353,8 +322,6 @@ config CPU_SA1100
# XScale
config CPU_XSCALE
bool
- depends on ARCH_IOP32X || ARCH_IOP33X || PXA25x || PXA27x || ARCH_IXP4XX || ARCH_IXP2000
- default y
select CPU_32v5
select CPU_ABRT_EV5T
select CPU_PABRT_NOIFAR
@@ -365,8 +332,6 @@ config CPU_XSCALE
# XScale Core Version 3
config CPU_XSC3
bool
- depends on ARCH_IXP23XX || ARCH_IOP13XX || PXA3xx
- default y
select CPU_32v5
select CPU_ABRT_EV5T
select CPU_PABRT_NOIFAR
@@ -378,8 +343,6 @@ config CPU_XSC3
# Feroceon
config CPU_FEROCEON
bool
- depends on ARCH_ORION5X || ARCH_LOKI || ARCH_KIRKWOOD || ARCH_MV78XX0
- default y
select CPU_32v5
select CPU_ABRT_EV5T
select CPU_PABRT_NOIFAR
@@ -399,10 +362,7 @@ config CPU_FEROCEON_OLD_ID
# ARMv6
config CPU_V6
- bool "Support ARM V6 processor"
- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_MSM || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
- default y if ARCH_MX3
- default y if ARCH_MSM
+ bool "Support ARM V6 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB
select CPU_32v6
select CPU_ABRT_EV6
select CPU_PABRT_NOIFAR
@@ -427,8 +387,7 @@ config CPU_32v6K
# ARMv7
config CPU_V7
- bool "Support ARM V7 processor"
- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP3
+ bool "Support ARM V7 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB
select CPU_32v6K
select CPU_32v7
select CPU_ABRT_EV7
@@ -745,7 +704,7 @@ config CACHE_FEROCEON_L2_WRITETHROUGH
config CACHE_L2X0
bool "Enable the L2x0 outer cache controller"
- depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
+ depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || REALVIEW_EB_A9MP
default y
select OUTER_CACHE
help
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 2d5884ce0435..3a398befed41 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -17,6 +17,7 @@
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
+#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S
index 3b3639eb7ca5..8a4abebc478a 100644
--- a/arch/arm/mm/cache-v3.S
+++ b/arch/arm/mm/cache-v3.S
@@ -9,7 +9,6 @@
*/
#include <linux/linkage.h>
#include <linux/init.h>
-#include <mach/hardware.h>
#include <asm/page.h>
#include "proc-macros.S"
diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S
index 5786adf10040..3668611cb400 100644
--- a/arch/arm/mm/cache-v4.S
+++ b/arch/arm/mm/cache-v4.S
@@ -9,7 +9,6 @@
*/
#include <linux/linkage.h>
#include <linux/init.h>
-#include <mach/hardware.h>
#include <asm/page.h>
#include "proc-macros.S"
diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S
index 51a9b0b273b6..c54fa2cc40e6 100644
--- a/arch/arm/mm/cache-v4wt.S
+++ b/arch/arm/mm/cache-v4wt.S
@@ -13,7 +13,6 @@
*/
#include <linux/linkage.h>
#include <linux/init.h>
-#include <mach/hardware.h>
#include <asm/page.h>
#include "proc-macros.S"
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index d19c2bec2b1f..be93ff02a98d 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -26,6 +26,7 @@
* - mm - mm_struct describing address space
*/
ENTRY(v7_flush_dcache_all)
+ dmb @ ensure ordering with previous memory accesses
mrc p15, 1, r0, c0, c0, 1 @ read clidr
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
@@ -64,6 +65,7 @@ skip:
finished:
mov r10, #0 @ swith back to cache level 0
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
+ dsb
isb
mov pc, lr
ENDPROC(v7_flush_dcache_all)
diff --git a/arch/arm/mm/copypage-feroceon.S b/arch/arm/mm/copypage-feroceon.S
deleted file mode 100644
index 7eb0d320d240..000000000000
--- a/arch/arm/mm/copypage-feroceon.S
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * linux/arch/arm/lib/copypage-feroceon.S
- *
- * Copyright (C) 2008 Marvell Semiconductors
- *
- * This program is free software; you can 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 handles copy_user_page and clear_user_page on Feroceon
- * more optimally than the generic implementations.
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/asm-offsets.h>
-
- .text
- .align 5
-
-ENTRY(feroceon_copy_user_page)
- stmfd sp!, {r4-r9, lr}
- mov ip, #PAGE_SZ
-1: mov lr, r1
- ldmia r1!, {r2 - r9}
- pld [lr, #32]
- pld [lr, #64]
- pld [lr, #96]
- pld [lr, #128]
- pld [lr, #160]
- pld [lr, #192]
- pld [lr, #224]
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- ldmia r1!, {r2 - r9}
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- stmia r0, {r2 - r9}
- subs ip, ip, #(32 * 8)
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- bne 1b
- mcr p15, 0, ip, c7, c10, 4 @ drain WB
- ldmfd sp!, {r4-r9, pc}
-
- .align 5
-
-ENTRY(feroceon_clear_user_page)
- stmfd sp!, {r4-r7, lr}
- mov r1, #PAGE_SZ/32
- mov r2, #0
- mov r3, #0
- mov r4, #0
- mov r5, #0
- mov r6, #0
- mov r7, #0
- mov ip, #0
- mov lr, #0
-1: stmia r0, {r2-r7, ip, lr}
- subs r1, r1, #1
- mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line
- add r0, r0, #32
- bne 1b
- mcr p15, 0, r1, c7, c10, 4 @ drain WB
- ldmfd sp!, {r4-r7, pc}
-
- __INITDATA
-
- .type feroceon_user_fns, #object
-ENTRY(feroceon_user_fns)
- .long feroceon_clear_user_page
- .long feroceon_copy_user_page
- .size feroceon_user_fns, . - feroceon_user_fns
diff --git a/arch/arm/mm/copypage-feroceon.c b/arch/arm/mm/copypage-feroceon.c
new file mode 100644
index 000000000000..c3ba6a94da0c
--- /dev/null
+++ b/arch/arm/mm/copypage-feroceon.c
@@ -0,0 +1,111 @@
+/*
+ * linux/arch/arm/mm/copypage-feroceon.S
+ *
+ * Copyright (C) 2008 Marvell Semiconductors
+ *
+ * This program is free software; you can 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 handles copy_user_highpage and clear_user_page on Feroceon
+ * more optimally than the generic implementations.
+ */
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+static void __attribute__((naked))
+feroceon_copy_user_page(void *kto, const void *kfrom)
+{
+ asm("\
+ stmfd sp!, {r4-r9, lr} \n\
+ mov ip, %0 \n\
+1: mov lr, r1 \n\
+ ldmia r1!, {r2 - r9} \n\
+ pld [lr, #32] \n\
+ pld [lr, #64] \n\
+ pld [lr, #96] \n\
+ pld [lr, #128] \n\
+ pld [lr, #160] \n\
+ pld [lr, #192] \n\
+ pld [lr, #224] \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ ldmia r1!, {r2 - r9} \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ stmia r0, {r2 - r9} \n\
+ subs ip, ip, #(32 * 8) \n\
+ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D line\n\
+ add r0, r0, #32 \n\
+ bne 1b \n\
+ mcr p15, 0, ip, c7, c10, 4 @ drain WB\n\
+ ldmfd sp!, {r4-r9, pc}"
+ :
+ : "I" (PAGE_SIZE));
+}
+
+void feroceon_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
+{
+ void *kto, *kfrom;
+
+ kto = kmap_atomic(to, KM_USER0);
+ kfrom = kmap_atomic(from, KM_USER1);
+ feroceon_copy_user_page(kto, kfrom);
+ kunmap_atomic(kfrom, KM_USER1);
+ kunmap_atomic(kto, KM_USER0);
+}
+
+void feroceon_clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile ("\
+ mov r1, %2 \n\
+ mov r2, #0 \n\
+ mov r3, #0 \n\
+ mov r4, #0 \n\
+ mov r5, #0 \n\
+ mov r6, #0 \n\
+ mov r7, #0 \n\
+ mov ip, #0 \n\
+ mov lr, #0 \n\
+1: stmia %0, {r2-r7, ip, lr} \n\
+ subs r1, r1, #1 \n\
+ mcr p15, 0, %0, c7, c14, 1 @ clean and invalidate D line\n\
+ add %0, %0, #32 \n\
+ bne 1b \n\
+ mcr p15, 0, r1, c7, c10, 4 @ drain WB"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 32)
+ : "r1", "r2", "r3", "r4", "r5", "r6", "r7", "ip", "lr");
+ kunmap_atomic(kaddr, KM_USER0);
+}
+
+struct cpu_user_fns feroceon_user_fns __initdata = {
+ .cpu_clear_user_highpage = feroceon_clear_user_highpage,
+ .cpu_copy_user_highpage = feroceon_copy_user_highpage,
+};
+
diff --git a/arch/arm/mm/copypage-v3.S b/arch/arm/mm/copypage-v3.S
deleted file mode 100644
index 2ee394b11bcb..000000000000
--- a/arch/arm/mm/copypage-v3.S
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * linux/arch/arm/lib/copypage.S
- *
- * Copyright (C) 1995-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.
- *
- * ASM optimised string functions
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/assembler.h>
-#include <asm/asm-offsets.h>
-
- .text
- .align 5
-/*
- * ARMv3 optimised copy_user_page
- *
- * FIXME: do we need to handle cache stuff...
- */
-ENTRY(v3_copy_user_page)
- stmfd sp!, {r4, lr} @ 2
- mov r2, #PAGE_SZ/64 @ 1
- ldmia r1!, {r3, r4, ip, lr} @ 4+1
-1: stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4+1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4+1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4
- subs r2, r2, #1 @ 1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmneia r1!, {r3, r4, ip, lr} @ 4
- bne 1b @ 1
- ldmfd sp!, {r4, pc} @ 3
-
- .align 5
-/*
- * ARMv3 optimised clear_user_page
- *
- * FIXME: do we need to handle cache stuff...
- */
-ENTRY(v3_clear_user_page)
- str lr, [sp, #-4]!
- mov r1, #PAGE_SZ/64 @ 1
- mov r2, #0 @ 1
- mov r3, #0 @ 1
- mov ip, #0 @ 1
- mov lr, #0 @ 1
-1: stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- subs r1, r1, #1 @ 1
- bne 1b @ 1
- ldr pc, [sp], #4
-
- __INITDATA
-
- .type v3_user_fns, #object
-ENTRY(v3_user_fns)
- .long v3_clear_user_page
- .long v3_copy_user_page
- .size v3_user_fns, . - v3_user_fns
diff --git a/arch/arm/mm/copypage-v3.c b/arch/arm/mm/copypage-v3.c
new file mode 100644
index 000000000000..70ed96c8af8e
--- /dev/null
+++ b/arch/arm/mm/copypage-v3.c
@@ -0,0 +1,81 @@
+/*
+ * linux/arch/arm/mm/copypage-v3.c
+ *
+ * Copyright (C) 1995-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.
+ */
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+/*
+ * ARMv3 optimised copy_user_highpage
+ *
+ * FIXME: do we need to handle cache stuff...
+ */
+static void __attribute__((naked))
+v3_copy_user_page(void *kto, const void *kfrom)
+{
+ asm("\n\
+ stmfd sp!, {r4, lr} @ 2\n\
+ mov r2, %2 @ 1\n\
+ ldmia %0!, {r3, r4, ip, lr} @ 4+1\n\
+1: stmia %1!, {r3, r4, ip, lr} @ 4\n\
+ ldmia %0!, {r3, r4, ip, lr} @ 4+1\n\
+ stmia %1!, {r3, r4, ip, lr} @ 4\n\
+ ldmia %0!, {r3, r4, ip, lr} @ 4+1\n\
+ stmia %1!, {r3, r4, ip, lr} @ 4\n\
+ ldmia %0!, {r3, r4, ip, lr} @ 4\n\
+ subs r2, r2, #1 @ 1\n\
+ stmia %1!, {r3, r4, ip, lr} @ 4\n\
+ ldmneia %0!, {r3, r4, ip, lr} @ 4\n\
+ bne 1b @ 1\n\
+ ldmfd sp!, {r4, pc} @ 3"
+ :
+ : "r" (kfrom), "r" (kto), "I" (PAGE_SIZE / 64));
+}
+
+void v3_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
+{
+ void *kto, *kfrom;
+
+ kto = kmap_atomic(to, KM_USER0);
+ kfrom = kmap_atomic(from, KM_USER1);
+ v3_copy_user_page(kto, kfrom);
+ kunmap_atomic(kfrom, KM_USER1);
+ kunmap_atomic(kto, KM_USER0);
+}
+
+/*
+ * ARMv3 optimised clear_user_page
+ *
+ * FIXME: do we need to handle cache stuff...
+ */
+void v3_clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile("\n\
+ mov r1, %2 @ 1\n\
+ mov r2, #0 @ 1\n\
+ mov r3, #0 @ 1\n\
+ mov ip, #0 @ 1\n\
+ mov lr, #0 @ 1\n\
+1: stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ subs r1, r1, #1 @ 1\n\
+ bne 1b @ 1"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 64)
+ : "r1", "r2", "r3", "ip", "lr");
+ kunmap_atomic(kaddr, KM_USER0);
+}
+
+struct cpu_user_fns v3_user_fns __initdata = {
+ .cpu_clear_user_highpage = v3_clear_user_highpage,
+ .cpu_copy_user_highpage = v3_copy_user_highpage,
+};
diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c
index 8d33e2549344..bdb5fd983b15 100644
--- a/arch/arm/mm/copypage-v4mc.c
+++ b/arch/arm/mm/copypage-v4mc.c
@@ -15,8 +15,8 @@
*/
#include <linux/init.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
-#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
@@ -33,7 +33,7 @@
static DEFINE_SPINLOCK(minicache_lock);
/*
- * ARMv4 mini-dcache optimised copy_user_page
+ * ARMv4 mini-dcache optimised copy_user_highpage
*
* We flush the destination cache lines just before we write the data into the
* corresponding address. Since the Dcache is read-allocate, this removes the
@@ -42,7 +42,7 @@ static DEFINE_SPINLOCK(minicache_lock);
*
* Note: We rely on all ARMv4 processors implementing the "invalidate D line"
* instruction. If your processor does not supply this, you have to write your
- * own copy_user_page that does the right thing.
+ * own copy_user_highpage that does the right thing.
*/
static void __attribute__((naked))
mc_copy_user_page(void *from, void *to)
@@ -68,50 +68,53 @@ mc_copy_user_page(void *from, void *to)
: "r" (from), "r" (to), "I" (PAGE_SIZE / 64));
}
-void v4_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+void v4_mc_copy_user_highpage(struct page *from, struct page *to,
+ unsigned long vaddr)
{
- struct page *page = virt_to_page(kfrom);
+ void *kto = kmap_atomic(to, KM_USER1);
- if (test_and_clear_bit(PG_dcache_dirty, &page->flags))
- __flush_dcache_page(page_mapping(page), page);
+ if (test_and_clear_bit(PG_dcache_dirty, &from->flags))
+ __flush_dcache_page(page_mapping(from), from);
spin_lock(&minicache_lock);
- set_pte_ext(TOP_PTE(0xffff8000), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot), 0);
+ set_pte_ext(TOP_PTE(0xffff8000), pfn_pte(page_to_pfn(from), minicache_pgprot), 0);
flush_tlb_kernel_page(0xffff8000);
mc_copy_user_page((void *)0xffff8000, kto);
spin_unlock(&minicache_lock);
+
+ kunmap_atomic(kto, KM_USER1);
}
/*
* ARMv4 optimised clear_user_page
*/
-void __attribute__((naked))
-v4_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+void v4_mc_clear_user_highpage(struct page *page, unsigned long vaddr)
{
- asm volatile(
- "str lr, [sp, #-4]!\n\
- mov r1, %0 @ 1\n\
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile("\
+ mov r1, %2 @ 1\n\
mov r2, #0 @ 1\n\
mov r3, #0 @ 1\n\
mov ip, #0 @ 1\n\
mov lr, #0 @ 1\n\
-1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
- stmia r0!, {r2, r3, ip, lr} @ 4\n\
- stmia r0!, {r2, r3, ip, lr} @ 4\n\
- mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
- stmia r0!, {r2, r3, ip, lr} @ 4\n\
- stmia r0!, {r2, r3, ip, lr} @ 4\n\
+1: mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
subs r1, r1, #1 @ 1\n\
- bne 1b @ 1\n\
- ldr pc, [sp], #4"
- :
- : "I" (PAGE_SIZE / 64));
+ bne 1b @ 1"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 64)
+ : "r1", "r2", "r3", "ip", "lr");
+ kunmap_atomic(kaddr, KM_USER0);
}
struct cpu_user_fns v4_mc_user_fns __initdata = {
- .cpu_clear_user_page = v4_mc_clear_user_page,
- .cpu_copy_user_page = v4_mc_copy_user_page,
+ .cpu_clear_user_highpage = v4_mc_clear_user_highpage,
+ .cpu_copy_user_highpage = v4_mc_copy_user_highpage,
};
diff --git a/arch/arm/mm/copypage-v4wb.S b/arch/arm/mm/copypage-v4wb.S
deleted file mode 100644
index 83117354b1cd..000000000000
--- a/arch/arm/mm/copypage-v4wb.S
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * linux/arch/arm/lib/copypage.S
- *
- * Copyright (C) 1995-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.
- *
- * ASM optimised string functions
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/asm-offsets.h>
-
- .text
- .align 5
-/*
- * ARMv4 optimised copy_user_page
- *
- * We flush the destination cache lines just before we write the data into the
- * corresponding address. Since the Dcache is read-allocate, this removes the
- * Dcache aliasing issue. The writes will be forwarded to the write buffer,
- * and merged as appropriate.
- *
- * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
- * instruction. If your processor does not supply this, you have to write your
- * own copy_user_page that does the right thing.
- */
-ENTRY(v4wb_copy_user_page)
- stmfd sp!, {r4, lr} @ 2
- mov r2, #PAGE_SZ/64 @ 1
- ldmia r1!, {r3, r4, ip, lr} @ 4
-1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4+1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4
- mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4
- subs r2, r2, #1 @ 1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmneia r1!, {r3, r4, ip, lr} @ 4
- bne 1b @ 1
- mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB
- ldmfd sp!, {r4, pc} @ 3
-
- .align 5
-/*
- * ARMv4 optimised clear_user_page
- *
- * Same story as above.
- */
-ENTRY(v4wb_clear_user_page)
- str lr, [sp, #-4]!
- mov r1, #PAGE_SZ/64 @ 1
- mov r2, #0 @ 1
- mov r3, #0 @ 1
- mov ip, #0 @ 1
- mov lr, #0 @ 1
-1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- subs r1, r1, #1 @ 1
- bne 1b @ 1
- mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB
- ldr pc, [sp], #4
-
- __INITDATA
-
- .type v4wb_user_fns, #object
-ENTRY(v4wb_user_fns)
- .long v4wb_clear_user_page
- .long v4wb_copy_user_page
- .size v4wb_user_fns, . - v4wb_user_fns
diff --git a/arch/arm/mm/copypage-v4wb.c b/arch/arm/mm/copypage-v4wb.c
new file mode 100644
index 000000000000..3ec93dab7656
--- /dev/null
+++ b/arch/arm/mm/copypage-v4wb.c
@@ -0,0 +1,94 @@
+/*
+ * linux/arch/arm/mm/copypage-v4wb.c
+ *
+ * Copyright (C) 1995-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.
+ */
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+/*
+ * ARMv4 optimised copy_user_highpage
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address. Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue. The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ *
+ * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
+ * instruction. If your processor does not supply this, you have to write your
+ * own copy_user_highpage that does the right thing.
+ */
+static void __attribute__((naked))
+v4wb_copy_user_page(void *kto, const void *kfrom)
+{
+ asm("\
+ stmfd sp!, {r4, lr} @ 2\n\
+ mov r2, %0 @ 1\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4+1\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+ mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+ subs r2, r2, #1 @ 1\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmneia r1!, {r3, r4, ip, lr} @ 4\n\
+ bne 1b @ 1\n\
+ mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB\n\
+ ldmfd sp!, {r4, pc} @ 3"
+ :
+ : "I" (PAGE_SIZE / 64));
+}
+
+void v4wb_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
+{
+ void *kto, *kfrom;
+
+ kto = kmap_atomic(to, KM_USER0);
+ kfrom = kmap_atomic(from, KM_USER1);
+ v4wb_copy_user_page(kto, kfrom);
+ kunmap_atomic(kfrom, KM_USER1);
+ kunmap_atomic(kto, KM_USER0);
+}
+
+/*
+ * ARMv4 optimised clear_user_page
+ *
+ * Same story as above.
+ */
+void v4wb_clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile("\
+ mov r1, %2 @ 1\n\
+ mov r2, #0 @ 1\n\
+ mov r3, #0 @ 1\n\
+ mov ip, #0 @ 1\n\
+ mov lr, #0 @ 1\n\
+1: mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ mcr p15, 0, %0, c7, c6, 1 @ 1 invalidate D line\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ subs r1, r1, #1 @ 1\n\
+ bne 1b @ 1\n\
+ mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 64)
+ : "r1", "r2", "r3", "ip", "lr");
+ kunmap_atomic(kaddr, KM_USER0);
+}
+
+struct cpu_user_fns v4wb_user_fns __initdata = {
+ .cpu_clear_user_highpage = v4wb_clear_user_highpage,
+ .cpu_copy_user_highpage = v4wb_copy_user_highpage,
+};
diff --git a/arch/arm/mm/copypage-v4wt.S b/arch/arm/mm/copypage-v4wt.S
deleted file mode 100644
index e1f2af28d549..000000000000
--- a/arch/arm/mm/copypage-v4wt.S
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * linux/arch/arm/lib/copypage-v4.S
- *
- * Copyright (C) 1995-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.
- *
- * ASM optimised string functions
- *
- * This is for CPUs with a writethrough cache and 'flush ID cache' is
- * the only supported cache operation.
- */
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/asm-offsets.h>
-
- .text
- .align 5
-/*
- * ARMv4 optimised copy_user_page
- *
- * Since we have writethrough caches, we don't have to worry about
- * dirty data in the cache. However, we do have to ensure that
- * subsequent reads are up to date.
- */
-ENTRY(v4wt_copy_user_page)
- stmfd sp!, {r4, lr} @ 2
- mov r2, #PAGE_SZ/64 @ 1
- ldmia r1!, {r3, r4, ip, lr} @ 4
-1: stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4+1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmia r1!, {r3, r4, ip, lr} @ 4
- subs r2, r2, #1 @ 1
- stmia r0!, {r3, r4, ip, lr} @ 4
- ldmneia r1!, {r3, r4, ip, lr} @ 4
- bne 1b @ 1
- mcr p15, 0, r2, c7, c7, 0 @ flush ID cache
- ldmfd sp!, {r4, pc} @ 3
-
- .align 5
-/*
- * ARMv4 optimised clear_user_page
- *
- * Same story as above.
- */
-ENTRY(v4wt_clear_user_page)
- str lr, [sp, #-4]!
- mov r1, #PAGE_SZ/64 @ 1
- mov r2, #0 @ 1
- mov r3, #0 @ 1
- mov ip, #0 @ 1
- mov lr, #0 @ 1
-1: stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- stmia r0!, {r2, r3, ip, lr} @ 4
- subs r1, r1, #1 @ 1
- bne 1b @ 1
- mcr p15, 0, r2, c7, c7, 0 @ flush ID cache
- ldr pc, [sp], #4
-
- __INITDATA
-
- .type v4wt_user_fns, #object
-ENTRY(v4wt_user_fns)
- .long v4wt_clear_user_page
- .long v4wt_copy_user_page
- .size v4wt_user_fns, . - v4wt_user_fns
diff --git a/arch/arm/mm/copypage-v4wt.c b/arch/arm/mm/copypage-v4wt.c
new file mode 100644
index 000000000000..0f1188efae45
--- /dev/null
+++ b/arch/arm/mm/copypage-v4wt.c
@@ -0,0 +1,88 @@
+/*
+ * linux/arch/arm/mm/copypage-v4wt.S
+ *
+ * Copyright (C) 1995-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.
+ *
+ * This is for CPUs with a writethrough cache and 'flush ID cache' is
+ * the only supported cache operation.
+ */
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+/*
+ * ARMv4 optimised copy_user_highpage
+ *
+ * Since we have writethrough caches, we don't have to worry about
+ * dirty data in the cache. However, we do have to ensure that
+ * subsequent reads are up to date.
+ */
+static void __attribute__((naked))
+v4wt_copy_user_page(void *kto, const void *kfrom)
+{
+ asm("\
+ stmfd sp!, {r4, lr} @ 2\n\
+ mov r2, %0 @ 1\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+1: stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4+1\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmia r1!, {r3, r4, ip, lr} @ 4\n\
+ subs r2, r2, #1 @ 1\n\
+ stmia r0!, {r3, r4, ip, lr} @ 4\n\
+ ldmneia r1!, {r3, r4, ip, lr} @ 4\n\
+ bne 1b @ 1\n\
+ mcr p15, 0, r2, c7, c7, 0 @ flush ID cache\n\
+ ldmfd sp!, {r4, pc} @ 3"
+ :
+ : "I" (PAGE_SIZE / 64));
+}
+
+void v4wt_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
+{
+ void *kto, *kfrom;
+
+ kto = kmap_atomic(to, KM_USER0);
+ kfrom = kmap_atomic(from, KM_USER1);
+ v4wt_copy_user_page(kto, kfrom);
+ kunmap_atomic(kfrom, KM_USER1);
+ kunmap_atomic(kto, KM_USER0);
+}
+
+/*
+ * ARMv4 optimised clear_user_page
+ *
+ * Same story as above.
+ */
+void v4wt_clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile("\
+ mov r1, %2 @ 1\n\
+ mov r2, #0 @ 1\n\
+ mov r3, #0 @ 1\n\
+ mov ip, #0 @ 1\n\
+ mov lr, #0 @ 1\n\
+1: stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ stmia %0!, {r2, r3, ip, lr} @ 4\n\
+ subs r1, r1, #1 @ 1\n\
+ bne 1b @ 1\n\
+ mcr p15, 0, r2, c7, c7, 0 @ flush ID cache"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 64)
+ : "r1", "r2", "r3", "ip", "lr");
+ kunmap_atomic(kaddr, KM_USER0);
+}
+
+struct cpu_user_fns v4wt_user_fns __initdata = {
+ .cpu_clear_user_highpage = v4wt_clear_user_highpage,
+ .cpu_copy_user_highpage = v4wt_copy_user_highpage,
+};
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c
index 0e21c0767580..4127a7bddfe5 100644
--- a/arch/arm/mm/copypage-v6.c
+++ b/arch/arm/mm/copypage-v6.c
@@ -10,8 +10,8 @@
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
-#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/shmparam.h>
#include <asm/tlbflush.h>
@@ -33,41 +33,56 @@ static DEFINE_SPINLOCK(v6_lock);
* Copy the user page. No aliasing to deal with so we can just
* attack the kernel's existing mapping of these pages.
*/
-static void v6_copy_user_page_nonaliasing(void *kto, const void *kfrom, unsigned long vaddr)
+static void v6_copy_user_highpage_nonaliasing(struct page *to,
+ struct page *from, unsigned long vaddr)
{
+ void *kto, *kfrom;
+
+ kfrom = kmap_atomic(from, KM_USER0);
+ kto = kmap_atomic(to, KM_USER1);
copy_page(kto, kfrom);
+ kunmap_atomic(kto, KM_USER1);
+ kunmap_atomic(kfrom, KM_USER0);
}
/*
* Clear the user page. No aliasing to deal with so we can just
* attack the kernel's existing mapping of this page.
*/
-static void v6_clear_user_page_nonaliasing(void *kaddr, unsigned long vaddr)
+static void v6_clear_user_highpage_nonaliasing(struct page *page, unsigned long vaddr)
{
+ void *kaddr = kmap_atomic(page, KM_USER0);
clear_page(kaddr);
+ kunmap_atomic(kaddr, KM_USER0);
}
/*
- * Copy the page, taking account of the cache colour.
+ * Discard data in the kernel mapping for the new page.
+ * FIXME: needs this MCRR to be supported.
*/
-static void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned long vaddr)
+static void discard_old_kernel_data(void *kto)
{
- unsigned int offset = CACHE_COLOUR(vaddr);
- unsigned long from, to;
- struct page *page = virt_to_page(kfrom);
-
- if (test_and_clear_bit(PG_dcache_dirty, &page->flags))
- __flush_dcache_page(page_mapping(page), page);
-
- /*
- * Discard data in the kernel mapping for the new page.
- * FIXME: needs this MCRR to be supported.
- */
__asm__("mcrr p15, 0, %1, %0, c6 @ 0xec401f06"
:
: "r" (kto),
"r" ((unsigned long)kto + PAGE_SIZE - L1_CACHE_BYTES)
: "cc");
+}
+
+/*
+ * Copy the page, taking account of the cache colour.
+ */
+static void v6_copy_user_highpage_aliasing(struct page *to,
+ struct page *from, unsigned long vaddr)
+{
+ unsigned int offset = CACHE_COLOUR(vaddr);
+ unsigned long kfrom, kto;
+
+ if (test_and_clear_bit(PG_dcache_dirty, &from->flags))
+ __flush_dcache_page(page_mapping(from), from);
+
+ /* FIXME: not highmem safe */
+ discard_old_kernel_data(page_address(to));
/*
* Now copy the page using the same cache colour as the
@@ -75,16 +90,16 @@ static void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned lo
*/
spin_lock(&v6_lock);
- set_pte_ext(TOP_PTE(from_address) + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, PAGE_KERNEL), 0);
- set_pte_ext(TOP_PTE(to_address) + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, PAGE_KERNEL), 0);
+ set_pte_ext(TOP_PTE(from_address) + offset, pfn_pte(page_to_pfn(from), PAGE_KERNEL), 0);
+ set_pte_ext(TOP_PTE(to_address) + offset, pfn_pte(page_to_pfn(to), PAGE_KERNEL), 0);
- from = from_address + (offset << PAGE_SHIFT);
- to = to_address + (offset << PAGE_SHIFT);
+ kfrom = from_address + (offset << PAGE_SHIFT);
+ kto = to_address + (offset << PAGE_SHIFT);
- flush_tlb_kernel_page(from);
- flush_tlb_kernel_page(to);
+ flush_tlb_kernel_page(kfrom);
+ flush_tlb_kernel_page(kto);
- copy_page((void *)to, (void *)from);
+ copy_page((void *)kto, (void *)kfrom);
spin_unlock(&v6_lock);
}
@@ -94,20 +109,13 @@ static void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned lo
* so remap the kernel page into the same cache colour as the user
* page.
*/
-static void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr)
+static void v6_clear_user_highpage_aliasing(struct page *page, unsigned long vaddr)
{
unsigned int offset = CACHE_COLOUR(vaddr);
unsigned long to = to_address + (offset << PAGE_SHIFT);
- /*
- * Discard data in the kernel mapping for the new page
- * FIXME: needs this MCRR to be supported.
- */
- __asm__("mcrr p15, 0, %1, %0, c6 @ 0xec401f06"
- :
- : "r" (kaddr),
- "r" ((unsigned long)kaddr + PAGE_SIZE - L1_CACHE_BYTES)
- : "cc");
+ /* FIXME: not highmem safe */
+ discard_old_kernel_data(page_address(page));
/*
* Now clear the page using the same cache colour as
@@ -115,7 +123,7 @@ static void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr)
*/
spin_lock(&v6_lock);
- set_pte_ext(TOP_PTE(to_address) + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, PAGE_KERNEL), 0);
+ set_pte_ext(TOP_PTE(to_address) + offset, pfn_pte(page_to_pfn(page), PAGE_KERNEL), 0);
flush_tlb_kernel_page(to);
clear_page((void *)to);
@@ -123,15 +131,15 @@ static void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr)
}
struct cpu_user_fns v6_user_fns __initdata = {
- .cpu_clear_user_page = v6_clear_user_page_nonaliasing,
- .cpu_copy_user_page = v6_copy_user_page_nonaliasing,
+ .cpu_clear_user_highpage = v6_clear_user_highpage_nonaliasing,
+ .cpu_copy_user_highpage = v6_copy_user_highpage_nonaliasing,
};
static int __init v6_userpage_init(void)
{
if (cache_is_vipt_aliasing()) {
- cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing;
- cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing;
+ cpu_user.cpu_clear_user_highpage = v6_clear_user_highpage_aliasing;
+ cpu_user.cpu_copy_user_highpage = v6_copy_user_highpage_aliasing;
}
return 0;
diff --git a/arch/arm/mm/copypage-xsc3.S b/arch/arm/mm/copypage-xsc3.S
deleted file mode 100644
index 9a2cb4332b4c..000000000000
--- a/arch/arm/mm/copypage-xsc3.S
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * linux/arch/arm/lib/copypage-xsc3.S
- *
- * Copyright (C) 2004 Intel Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Adapted for 3rd gen XScale core, no more mini-dcache
- * Author: Matt Gilbert (matthew.m.gilbert@intel.com)
- */
-
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <asm/asm-offsets.h>
-
-/*
- * General note:
- * We don't really want write-allocate cache behaviour for these functions
- * since that will just eat through 8K of the cache.
- */
-
- .text
- .align 5
-/*
- * XSC3 optimised copy_user_page
- * r0 = destination
- * r1 = source
- * r2 = virtual user address of ultimate destination page
- *
- * The source page may have some clean entries in the cache already, but we
- * can safely ignore them - break_cow() will flush them out of the cache
- * if we eventually end up using our copied page.
- *
- */
-ENTRY(xsc3_mc_copy_user_page)
- stmfd sp!, {r4, r5, lr}
- mov lr, #PAGE_SZ/64-1
-
- pld [r1, #0]
- pld [r1, #32]
-1: pld [r1, #64]
- pld [r1, #96]
-
-2: ldrd r2, [r1], #8
- mov ip, r0
- ldrd r4, [r1], #8
- mcr p15, 0, ip, c7, c6, 1 @ invalidate
- strd r2, [r0], #8
- ldrd r2, [r1], #8
- strd r4, [r0], #8
- ldrd r4, [r1], #8
- strd r2, [r0], #8
- strd r4, [r0], #8
- ldrd r2, [r1], #8
- mov ip, r0
- ldrd r4, [r1], #8
- mcr p15, 0, ip, c7, c6, 1 @ invalidate
- strd r2, [r0], #8
- ldrd r2, [r1], #8
- subs lr, lr, #1
- strd r4, [r0], #8
- ldrd r4, [r1], #8
- strd r2, [r0], #8
- strd r4, [r0], #8
- bgt 1b
- beq 2b
-
- ldmfd sp!, {r4, r5, pc}
-
- .align 5
-/*
- * XScale optimised clear_user_page
- * r0 = destination
- * r1 = virtual user address of ultimate destination page
- */
-ENTRY(xsc3_mc_clear_user_page)
- mov r1, #PAGE_SZ/32
- mov r2, #0
- mov r3, #0
-1: mcr p15, 0, r0, c7, c6, 1 @ invalidate line
- strd r2, [r0], #8
- strd r2, [r0], #8
- strd r2, [r0], #8
- strd r2, [r0], #8
- subs r1, r1, #1
- bne 1b
- mov pc, lr
-
- __INITDATA
-
- .type xsc3_mc_user_fns, #object
-ENTRY(xsc3_mc_user_fns)
- .long xsc3_mc_clear_user_page
- .long xsc3_mc_copy_user_page
- .size xsc3_mc_user_fns, . - xsc3_mc_user_fns
diff --git a/arch/arm/mm/copypage-xsc3.c b/arch/arm/mm/copypage-xsc3.c
new file mode 100644
index 000000000000..39a994542cad
--- /dev/null
+++ b/arch/arm/mm/copypage-xsc3.c
@@ -0,0 +1,113 @@
+/*
+ * linux/arch/arm/mm/copypage-xsc3.S
+ *
+ * Copyright (C) 2004 Intel Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Adapted for 3rd gen XScale core, no more mini-dcache
+ * Author: Matt Gilbert (matthew.m.gilbert@intel.com)
+ */
+#include <linux/init.h>
+#include <linux/highmem.h>
+
+/*
+ * General note:
+ * We don't really want write-allocate cache behaviour for these functions
+ * since that will just eat through 8K of the cache.
+ */
+
+/*
+ * XSC3 optimised copy_user_highpage
+ * r0 = destination
+ * r1 = source
+ *
+ * The source page may have some clean entries in the cache already, but we
+ * can safely ignore them - break_cow() will flush them out of the cache
+ * if we eventually end up using our copied page.
+ *
+ */
+static void __attribute__((naked))
+xsc3_mc_copy_user_page(void *kto, const void *kfrom)
+{
+ asm("\
+ stmfd sp!, {r4, r5, lr} \n\
+ mov lr, %0 \n\
+ \n\
+ pld [r1, #0] \n\
+ pld [r1, #32] \n\
+1: pld [r1, #64] \n\
+ pld [r1, #96] \n\
+ \n\
+2: ldrd r2, [r1], #8 \n\
+ mov ip, r0 \n\
+ ldrd r4, [r1], #8 \n\
+ mcr p15, 0, ip, c7, c6, 1 @ invalidate\n\
+ strd r2, [r0], #8 \n\
+ ldrd r2, [r1], #8 \n\
+ strd r4, [r0], #8 \n\
+ ldrd r4, [r1], #8 \n\
+ strd r2, [r0], #8 \n\
+ strd r4, [r0], #8 \n\
+ ldrd r2, [r1], #8 \n\
+ mov ip, r0 \n\
+ ldrd r4, [r1], #8 \n\
+ mcr p15, 0, ip, c7, c6, 1 @ invalidate\n\
+ strd r2, [r0], #8 \n\
+ ldrd r2, [r1], #8 \n\
+ subs lr, lr, #1 \n\
+ strd r4, [r0], #8 \n\
+ ldrd r4, [r1], #8 \n\
+ strd r2, [r0], #8 \n\
+ strd r4, [r0], #8 \n\
+ bgt 1b \n\
+ beq 2b \n\
+ \n\
+ ldmfd sp!, {r4, r5, pc}"
+ :
+ : "I" (PAGE_SIZE / 64 - 1));
+}
+
+void xsc3_mc_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
+{
+ void *kto, *kfrom;
+
+ kto = kmap_atomic(to, KM_USER0);
+ kfrom = kmap_atomic(from, KM_USER1);
+ xsc3_mc_copy_user_page(kto, kfrom);
+ kunmap_atomic(kfrom, KM_USER1);
+ kunmap_atomic(kto, KM_USER0);
+}
+
+/*
+ * XScale optimised clear_user_page
+ * r0 = destination
+ * r1 = virtual user address of ultimate destination page
+ */
+void xsc3_mc_clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
+ asm volatile ("\
+ mov r1, %2 \n\
+ mov r2, #0 \n\
+ mov r3, #0 \n\
+1: mcr p15, 0, %0, c7, c6, 1 @ invalidate line\n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
+ subs r1, r1, #1 \n\
+ bne 1b"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 32)
+ : "r1", "r2", "r3");
+ kunmap_atomic(kaddr, KM_USER0);
+}
+
+struct cpu_user_fns xsc3_mc_user_fns __initdata = {
+ .cpu_clear_user_highpage = xsc3_mc_clear_user_highpage,
+ .cpu_copy_user_highpage = xsc3_mc_copy_user_highpage,
+};
diff --git a/arch/arm/mm/copypage-xscale.c b/arch/arm/mm/copypage-xscale.c
index bad49331bbf9..d18f2397ee2d 100644
--- a/arch/arm/mm/copypage-xscale.c
+++ b/arch/arm/mm/copypage-xscale.c
@@ -15,8 +15,8 @@
*/
#include <linux/init.h>
#include <linux/mm.h>
+#include <linux/highmem.h>
-#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
@@ -35,7 +35,7 @@
static DEFINE_SPINLOCK(minicache_lock);
/*
- * XScale mini-dcache optimised copy_user_page
+ * XScale mini-dcache optimised copy_user_highpage
*
* We flush the destination cache lines just before we write the data into the
* corresponding address. Since the Dcache is read-allocate, this removes the
@@ -90,48 +90,53 @@ mc_copy_user_page(void *from, void *to)
: "r" (from), "r" (to), "I" (PAGE_SIZE / 64 - 1));
}
-void xscale_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+void xscale_mc_copy_user_highpage(struct page *to, struct page *from,
+ unsigned long vaddr)
{
- struct page *page = virt_to_page(kfrom);
+ void *kto = kmap_atomic(to, KM_USER1);
- if (test_and_clear_bit(PG_dcache_dirty, &page->flags))
- __flush_dcache_page(page_mapping(page), page);
+ if (test_and_clear_bit(PG_dcache_dirty, &from->flags))
+ __flush_dcache_page(page_mapping(from), from);
spin_lock(&minicache_lock);
- set_pte_ext(TOP_PTE(COPYPAGE_MINICACHE), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot), 0);
+ set_pte_ext(TOP_PTE(COPYPAGE_MINICACHE), pfn_pte(page_to_pfn(from), minicache_pgprot), 0);
flush_tlb_kernel_page(COPYPAGE_MINICACHE);
mc_copy_user_page((void *)COPYPAGE_MINICACHE, kto);
spin_unlock(&minicache_lock);
+
+ kunmap_atomic(kto, KM_USER1);
}
/*
* XScale optimised clear_user_page
*/
-void __attribute__((naked))
-xscale_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+void
+xscale_mc_clear_user_highpage(struct page *page, unsigned long vaddr)
{
+ void *ptr, *kaddr = kmap_atomic(page, KM_USER0);
asm volatile(
- "mov r1, %0 \n\
+ "mov r1, %2 \n\
mov r2, #0 \n\
mov r3, #0 \n\
-1: mov ip, r0 \n\
- strd r2, [r0], #8 \n\
- strd r2, [r0], #8 \n\
- strd r2, [r0], #8 \n\
- strd r2, [r0], #8 \n\
+1: mov ip, %0 \n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
+ strd r2, [%0], #8 \n\
mcr p15, 0, ip, c7, c10, 1 @ clean D line\n\
subs r1, r1, #1 \n\
mcr p15, 0, ip, c7, c6, 1 @ invalidate D line\n\
- bne 1b \n\
- mov pc, lr"
- :
- : "I" (PAGE_SIZE / 32));
+ bne 1b"
+ : "=r" (ptr)
+ : "0" (kaddr), "I" (PAGE_SIZE / 32)
+ : "r1", "r2", "r3", "ip");
+ kunmap_atomic(kaddr, KM_USER0);
}
struct cpu_user_fns xscale_mc_user_fns __initdata = {
- .cpu_clear_user_page = xscale_mc_clear_user_page,
- .cpu_copy_user_page = xscale_mc_copy_user_page,
+ .cpu_clear_user_highpage = xscale_mc_clear_user_highpage,
+ .cpu_copy_user_highpage = xscale_mc_copy_user_highpage,
};
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 22c9530e91e2..0455557a2899 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
+#include <linux/page-flags.h>
#include <asm/system.h>
#include <asm/pgtable.h>
@@ -84,13 +85,14 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
break;
}
-#ifndef CONFIG_HIGHMEM
/* We must not map this if we have highmem enabled */
+ if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT)))
+ break;
+
pte = pte_offset_map(pmd, addr);
printk(", *pte=%08lx", pte_val(*pte));
printk(", *ppte=%08lx", pte_val(pte[-PTRS_PER_PTE]));
pte_unmap(pte);
-#endif
} while(0);
printk("\n");
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 82c4b4217989..34df4d9d03a6 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -17,6 +17,7 @@
#include <linux/initrd.h>
#include <asm/mach-types.h>
+#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/sizes.h>
#include <asm/tlb.h>
@@ -64,10 +65,11 @@ static int __init parse_tag_initrd2(const struct tag *tag)
__tagtable(ATAG_INITRD2, parse_tag_initrd2);
/*
- * This is used to pass memory configuration data from paging_init
- * to mem_init, and by show_mem() to skip holes in the memory map.
+ * This keeps memory configuration data used by a couple memory
+ * initialization functions, as well as show_mem() for the skipping
+ * of holes in the memory map. It is populated by arm_add_memory().
*/
-static struct meminfo meminfo = { 0, };
+struct meminfo meminfo;
void show_mem(void)
{
@@ -128,7 +130,7 @@ find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages)
{
unsigned int start_pfn, i, bootmap_pfn;
- start_pfn = PAGE_ALIGN(__pa(&_end)) >> PAGE_SHIFT;
+ start_pfn = PAGE_ALIGN(__pa(_end)) >> PAGE_SHIFT;
bootmap_pfn = 0;
for_each_nodebank(i, mi, node) {
@@ -331,13 +333,12 @@ static void __init bootmem_free_node(int node, struct meminfo *mi)
free_area_init_node(node, zone_size, start_pfn, zhole_size);
}
-void __init bootmem_init(struct meminfo *mi)
+void __init bootmem_init(void)
{
+ struct meminfo *mi = &meminfo;
unsigned long memend_pfn = 0;
int node, initrd_node;
- memcpy(&meminfo, mi, sizeof(meminfo));
-
/*
* Locate which node contains the ramdisk image, if any.
*/
@@ -394,20 +395,22 @@ void __init bootmem_init(struct meminfo *mi)
max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;
}
-static inline void free_area(unsigned long addr, unsigned long end, char *s)
+static inline int free_area(unsigned long pfn, unsigned long end, char *s)
{
- unsigned int size = (end - addr) >> 10;
+ unsigned int pages = 0, size = (end - pfn) << (PAGE_SHIFT - 10);
- for (; addr < end; addr += PAGE_SIZE) {
- struct page *page = virt_to_page(addr);
+ for (; pfn < end; pfn++) {
+ struct page *page = pfn_to_page(pfn);
ClearPageReserved(page);
init_page_count(page);
- free_page(addr);
- totalram_pages++;
+ __free_page(page);
+ pages++;
}
if (size && s)
printk(KERN_INFO "Freeing %s memory: %dK\n", s, size);
+
+ return pages;
}
static inline void
@@ -478,13 +481,9 @@ static void __init free_unused_memmap_node(int node, struct meminfo *mi)
*/
void __init mem_init(void)
{
- unsigned int codepages, datapages, initpages;
+ unsigned int codesize, datasize, initsize;
int i, node;
- codepages = &_etext - &_text;
- datapages = &_end - &__data_start;
- initpages = &__init_end - &__init_begin;
-
#ifndef CONFIG_DISCONTIGMEM
max_mapnr = virt_to_page(high_memory) - mem_map;
#endif
@@ -501,7 +500,8 @@ void __init mem_init(void)
#ifdef CONFIG_SA1111
/* now that our DMA memory is actually so designated, we can free it */
- free_area(PAGE_OFFSET, (unsigned long)swapper_pg_dir, NULL);
+ totalram_pages += free_area(PHYS_PFN_OFFSET,
+ __phys_to_pfn(__pa(swapper_pg_dir)), NULL);
#endif
/*
@@ -509,18 +509,21 @@ void __init mem_init(void)
* real number of pages we have in this system
*/
printk(KERN_INFO "Memory:");
-
num_physpages = 0;
for (i = 0; i < meminfo.nr_banks; i++) {
num_physpages += bank_pfn_size(&meminfo.bank[i]);
printk(" %ldMB", bank_phys_size(&meminfo.bank[i]) >> 20);
}
-
printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT));
+
+ codesize = _etext - _text;
+ datasize = _end - _data;
+ initsize = __init_end - __init_begin;
+
printk(KERN_NOTICE "Memory: %luKB available (%dK code, "
"%dK data, %dK init)\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
- codepages >> 10, datapages >> 10, initpages >> 10);
+ codesize >> 10, datasize >> 10, initsize >> 10);
if (PAGE_SIZE >= 16384 && num_physpages <= 128) {
extern int sysctl_overcommit_memory;
@@ -535,11 +538,10 @@ void __init mem_init(void)
void free_initmem(void)
{
- if (!machine_is_integrator() && !machine_is_cintegrator()) {
- free_area((unsigned long)(&__init_begin),
- (unsigned long)(&__init_end),
- "init");
- }
+ if (!machine_is_integrator() && !machine_is_cintegrator())
+ totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)),
+ __phys_to_pfn(__pa(__init_end)),
+ "init");
}
#ifdef CONFIG_BLK_DEV_INITRD
@@ -549,7 +551,9 @@ static int keep_initrd;
void free_initrd_mem(unsigned long start, unsigned long end)
{
if (!keep_initrd)
- free_area(start, end, "initrd");
+ totalram_pages += free_area(__phys_to_pfn(__pa(start)),
+ __phys_to_pfn(__pa(end)),
+ "initrd");
}
static int __init keepinitrd_setup(char *__unused)
diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h
index 5d9f53907b4e..95bbe112965e 100644
--- a/arch/arm/mm/mm.h
+++ b/arch/arm/mm/mm.h
@@ -32,7 +32,5 @@ struct meminfo;
struct pglist_data;
void __init create_mapping(struct map_desc *md);
-void __init bootmem_init(struct meminfo *mi);
+void __init bootmem_init(void);
void reserve_node_zero(struct pglist_data *pgdat);
-
-extern void _text, _stext, _etext, __data_start, _end, __init_begin, __init_end;
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 7f36c825718d..9b36c5cb5e9f 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -17,6 +17,7 @@
#include <asm/cputype.h>
#include <asm/mach-types.h>
+#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/sizes.h>
#include <asm/tlb.h>
@@ -646,61 +647,79 @@ static void __init early_vmalloc(char **arg)
"vmalloc area too small, limiting to %luMB\n",
vmalloc_reserve >> 20);
}
+
+ if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) {
+ vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M);
+ printk(KERN_WARNING
+ "vmalloc area is too big, limiting to %luMB\n",
+ vmalloc_reserve >> 20);
+ }
}
__early_param("vmalloc=", early_vmalloc);
#define VMALLOC_MIN (void *)(VMALLOC_END - vmalloc_reserve)
-static int __init check_membank_valid(struct membank *mb)
+static void __init sanity_check_meminfo(void)
{
- /*
- * Check whether this memory region has non-zero size or
- * invalid node number.
- */
- if (mb->size == 0 || mb->node >= MAX_NUMNODES)
- return 0;
-
- /*
- * Check whether this memory region would entirely overlap
- * the vmalloc area.
- */
- if (phys_to_virt(mb->start) >= VMALLOC_MIN) {
- printk(KERN_NOTICE "Ignoring RAM at %.8lx-%.8lx "
- "(vmalloc region overlap).\n",
- mb->start, mb->start + mb->size - 1);
- return 0;
- }
-
- /*
- * Check whether this memory region would partially overlap
- * the vmalloc area.
- */
- if (phys_to_virt(mb->start + mb->size) < phys_to_virt(mb->start) ||
- phys_to_virt(mb->start + mb->size) > VMALLOC_MIN) {
- unsigned long newsize = VMALLOC_MIN - phys_to_virt(mb->start);
-
- printk(KERN_NOTICE "Truncating RAM at %.8lx-%.8lx "
- "to -%.8lx (vmalloc region overlap).\n",
- mb->start, mb->start + mb->size - 1,
- mb->start + newsize - 1);
- mb->size = newsize;
- }
+ int i, j;
- return 1;
-}
+ for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
+ struct membank *bank = &meminfo.bank[j];
+ *bank = meminfo.bank[i];
-static void __init sanity_check_meminfo(struct meminfo *mi)
-{
- int i, j;
+#ifdef CONFIG_HIGHMEM
+ /*
+ * Split those memory banks which are partially overlapping
+ * the vmalloc area greatly simplifying things later.
+ */
+ if (__va(bank->start) < VMALLOC_MIN &&
+ bank->size > VMALLOC_MIN - __va(bank->start)) {
+ if (meminfo.nr_banks >= NR_BANKS) {
+ printk(KERN_CRIT "NR_BANKS too low, "
+ "ignoring high memory\n");
+ } else {
+ memmove(bank + 1, bank,
+ (meminfo.nr_banks - i) * sizeof(*bank));
+ meminfo.nr_banks++;
+ i++;
+ bank[1].size -= VMALLOC_MIN - __va(bank->start);
+ bank[1].start = __pa(VMALLOC_MIN - 1) + 1;
+ j++;
+ }
+ bank->size = VMALLOC_MIN - __va(bank->start);
+ }
+#else
+ /*
+ * Check whether this memory bank would entirely overlap
+ * the vmalloc area.
+ */
+ if (__va(bank->start) >= VMALLOC_MIN) {
+ printk(KERN_NOTICE "Ignoring RAM at %.8lx-%.8lx "
+ "(vmalloc region overlap).\n",
+ bank->start, bank->start + bank->size - 1);
+ continue;
+ }
- for (i = 0, j = 0; i < mi->nr_banks; i++) {
- if (check_membank_valid(&mi->bank[i]))
- mi->bank[j++] = mi->bank[i];
+ /*
+ * Check whether this memory bank would partially overlap
+ * the vmalloc area.
+ */
+ if (__va(bank->start + bank->size) > VMALLOC_MIN ||
+ __va(bank->start + bank->size) < __va(bank->start)) {
+ unsigned long newsize = VMALLOC_MIN - __va(bank->start);
+ printk(KERN_NOTICE "Truncating RAM at %.8lx-%.8lx "
+ "to -%.8lx (vmalloc region overlap).\n",
+ bank->start, bank->start + bank->size - 1,
+ bank->start + newsize - 1);
+ bank->size = newsize;
+ }
+#endif
+ j++;
}
- mi->nr_banks = j;
+ meminfo.nr_banks = j;
}
-static inline void prepare_page_table(struct meminfo *mi)
+static inline void prepare_page_table(void)
{
unsigned long addr;
@@ -712,7 +731,7 @@ static inline void prepare_page_table(struct meminfo *mi)
#ifdef CONFIG_XIP_KERNEL
/* The XIP kernel is mapped in the module area -- skip over it */
- addr = ((unsigned long)&_etext + PGDIR_SIZE - 1) & PGDIR_MASK;
+ addr = ((unsigned long)_etext + PGDIR_SIZE - 1) & PGDIR_MASK;
#endif
for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
@@ -721,7 +740,7 @@ static inline void prepare_page_table(struct meminfo *mi)
* Clear out all the kernel space mappings, except for the first
* memory bank, up to the end of the vmalloc region.
*/
- for (addr = __phys_to_virt(mi->bank[0].start + mi->bank[0].size);
+ for (addr = __phys_to_virt(bank_phys_end(&meminfo.bank[0]));
addr < VMALLOC_END; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
}
@@ -738,10 +757,10 @@ void __init reserve_node_zero(pg_data_t *pgdat)
* Note that this can only be in node 0.
*/
#ifdef CONFIG_XIP_KERNEL
- reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start,
+ reserve_bootmem_node(pgdat, __pa(_data), _end - _data,
BOOTMEM_DEFAULT);
#else
- reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext,
+ reserve_bootmem_node(pgdat, __pa(_stext), _end - _stext,
BOOTMEM_DEFAULT);
#endif
@@ -808,7 +827,6 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
* Allocate the vector page early.
*/
vectors = alloc_bootmem_low_pages(PAGE_SIZE);
- BUG_ON(!vectors);
for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
pmd_clear(pmd_off_k(addr));
@@ -820,7 +838,7 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
#ifdef CONFIG_XIP_KERNEL
map.pfn = __phys_to_pfn(CONFIG_XIP_PHYS_ADDR & SECTION_MASK);
map.virtual = MODULES_VADDR;
- map.length = ((unsigned long)&_etext - map.virtual + ~SECTION_MASK) & SECTION_MASK;
+ map.length = ((unsigned long)_etext - map.virtual + ~SECTION_MASK) & SECTION_MASK;
map.type = MT_ROM;
create_mapping(&map);
#endif
@@ -880,23 +898,23 @@ static void __init devicemaps_init(struct machine_desc *mdesc)
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
*/
-void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
+void __init paging_init(struct machine_desc *mdesc)
{
void *zero_page;
build_mem_type_table();
- sanity_check_meminfo(mi);
- prepare_page_table(mi);
- bootmem_init(mi);
+ sanity_check_meminfo();
+ prepare_page_table();
+ bootmem_init();
devicemaps_init(mdesc);
top_pmd = pmd_off_k(0xffff0000);
/*
- * allocate the zero page. Note that we count on this going ok.
+ * allocate the zero page. Note that this always succeeds and
+ * returns a zeroed result.
*/
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
- memzero(zero_page, PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page);
}
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c
index 07b62b238979..ad7bacc693b2 100644
--- a/arch/arm/mm/nommu.c
+++ b/arch/arm/mm/nommu.c
@@ -10,6 +10,7 @@
#include <linux/io.h>
#include <asm/cacheflush.h>
+#include <asm/sections.h>
#include <asm/page.h>
#include <asm/mach/arch.h>
@@ -25,10 +26,10 @@ void __init reserve_node_zero(pg_data_t *pgdat)
* Note that this can only be in node 0.
*/
#ifdef CONFIG_XIP_KERNEL
- reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start,
+ reserve_bootmem_node(pgdat, __pa(_data), _end - _data,
BOOTMEM_DEFAULT);
#else
- reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext,
+ reserve_bootmem_node(pgdat, __pa(_stext), _end - _stext,
BOOTMEM_DEFAULT);
#endif
@@ -41,27 +42,13 @@ void __init reserve_node_zero(pg_data_t *pgdat)
BOOTMEM_DEFAULT);
}
-static void __init sanity_check_meminfo(struct meminfo *mi)
-{
- int i, j;
-
- for (i = 0, j = 0; i < mi->nr_banks; i++) {
- struct membank *mb = &mi->bank[i];
-
- if (mb->size != 0 && mb->node < MAX_NUMNODES)
- mi->bank[j++] = mi->bank[i];
- }
- mi->nr_banks = j;
-}
-
/*
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
*/
-void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
+void __init paging_init(struct machine_desc *mdesc)
{
- sanity_check_meminfo(mi);
- bootmem_init(mi);
+ bootmem_init();
}
/*
diff --git a/arch/arm/mm/pgd.c b/arch/arm/mm/pgd.c
index e0f19ab91163..2690146161ba 100644
--- a/arch/arm/mm/pgd.c
+++ b/arch/arm/mm/pgd.c
@@ -31,7 +31,7 @@ pgd_t *get_pgd_slow(struct mm_struct *mm)
if (!new_pgd)
goto no_pgd;
- memzero(new_pgd, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));
+ memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));
/*
* Copy over the kernel and IO PGD entries
diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c
index 2b5ba396e3a6..4ad3bf291ad3 100644
--- a/arch/arm/mm/proc-syms.c
+++ b/arch/arm/mm/proc-syms.c
@@ -33,8 +33,8 @@ EXPORT_SYMBOL(cpu_cache);
#ifdef CONFIG_MMU
#ifndef MULTI_USER
-EXPORT_SYMBOL(__cpu_clear_user_page);
-EXPORT_SYMBOL(__cpu_copy_user_page);
+EXPORT_SYMBOL(__cpu_clear_user_highpage);
+EXPORT_SYMBOL(__cpu_copy_user_highpage);
#else
EXPORT_SYMBOL(cpu_user);
#endif
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index 294943b85973..f0cc599facb7 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -71,6 +71,8 @@ ENTRY(cpu_v6_reset)
* IRQs are already disabled.
*/
ENTRY(cpu_v6_do_idle)
+ mov r1, #0
+ mcr p15, 0, r1, c7, c10, 4 @ DWB - WFI may enter a low-power mode
mcr p15, 0, r1, c7, c0, 4 @ wait for interrupt
mov pc, lr
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 4d3c0a73e7fb..d1ebec42521d 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -20,9 +20,17 @@
#define TTB_C (1 << 0)
#define TTB_S (1 << 1)
+#define TTB_RGN_NC (0 << 3)
+#define TTB_RGN_OC_WBWA (1 << 3)
#define TTB_RGN_OC_WT (2 << 3)
#define TTB_RGN_OC_WB (3 << 3)
+#ifndef CONFIG_SMP
+#define TTB_FLAGS TTB_C|TTB_RGN_OC_WB @ mark PTWs cacheable, outer WB
+#else
+#define TTB_FLAGS TTB_C|TTB_S|TTB_RGN_OC_WBWA @ mark PTWs cacheable and shared, outer WBWA
+#endif
+
ENTRY(cpu_v7_proc_init)
mov pc, lr
ENDPROC(cpu_v7_proc_init)
@@ -55,6 +63,7 @@ ENDPROC(cpu_v7_reset)
* IRQs are already disabled.
*/
ENTRY(cpu_v7_do_idle)
+ dsb @ WFI may enter a low-power mode
wfi
mov pc, lr
ENDPROC(cpu_v7_do_idle)
@@ -85,7 +94,7 @@ ENTRY(cpu_v7_switch_mm)
#ifdef CONFIG_MMU
mov r2, #0
ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id
- orr r0, r0, #TTB_RGN_OC_WB @ mark PTWs outer cacheable, WB
+ orr r0, r0, #TTB_FLAGS
mcr p15, 0, r2, c13, c0, 1 @ set reserved context ID
isb
1: mcr p15, 0, r0, c2, c0, 0 @ set TTB 0
@@ -162,6 +171,11 @@ cpu_v7_name:
* - cache type register is implemented
*/
__v7_setup:
+#ifdef CONFIG_SMP
+ mrc p15, 0, r0, c1, c0, 1 @ Enable SMP/nAMP mode
+ orr r0, r0, #(0x1 << 6)
+ mcr p15, 0, r0, c1, c0, 1
+#endif
adr r12, __v7_setup_stack @ the local stack
stmia r12, {r0-r5, r7, r9, r11, lr}
bl v7_flush_dcache_all
@@ -174,8 +188,7 @@ __v7_setup:
#ifdef CONFIG_MMU
mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs
mcr p15, 0, r10, c2, c0, 2 @ TTB control register
- orr r4, r4, #TTB_RGN_OC_WB @ mark PTWs outer cacheable, WB
- mcr p15, 0, r4, c2, c0, 0 @ load TTB0
+ orr r4, r4, #TTB_FLAGS
mcr p15, 0, r4, c2, c0, 1 @ load TTB1
mov r10, #0x1f @ domains 0, 1 = manager
mcr p15, 0, r10, c3, c0, 0 @ load domain access register
diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S
index 8f6cf56c11c0..33515c214b92 100644
--- a/arch/arm/mm/proc-xsc3.S
+++ b/arch/arm/mm/proc-xsc3.S
@@ -481,3 +481,28 @@ __xsc3_proc_info:
.long xsc3_mc_user_fns
.long xsc3_cache_fns
.size __xsc3_proc_info, . - __xsc3_proc_info
+
+/* Note: PXA935 changed its implementor ID from Intel to Marvell */
+
+ .type __xsc3_pxa935_proc_info,#object
+__xsc3_pxa935_proc_info:
+ .long 0x56056000
+ .long 0xffffe000
+ .long PMD_TYPE_SECT | \
+ PMD_SECT_BUFFERABLE | \
+ PMD_SECT_CACHEABLE | \
+ PMD_SECT_AP_WRITE | \
+ PMD_SECT_AP_READ
+ .long PMD_TYPE_SECT | \
+ PMD_SECT_AP_WRITE | \
+ PMD_SECT_AP_READ
+ b __xsc3_setup
+ .long cpu_arch_name
+ .long cpu_elf_name
+ .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP
+ .long cpu_xsc3_name
+ .long xsc3_processor_functions
+ .long v4wbi_tlb_fns
+ .long xsc3_mc_user_fns
+ .long xsc3_cache_fns
+ .size __xsc3_pxa935_proc_info, . - __xsc3_pxa935_proc_info
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index b2a7e3fad117..9cc2b16fdf79 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -6,18 +6,27 @@ choice
prompt "MXC/iMX Base Type"
default ARCH_MX3
+config ARCH_MX1
+ bool "MX1-based"
+ select CPU_ARM920T
+ help
+ This enables support for systems based on the Freescale i.MX1 family
+
config ARCH_MX2
bool "MX2-based"
+ select CPU_ARM926T
help
This enables support for systems based on the Freescale i.MX2 family
config ARCH_MX3
bool "MX3-based"
+ select CPU_V6
help
This enables support for systems based on the Freescale i.MX3 family
endchoice
+source "arch/arm/mach-mx1/Kconfig"
source "arch/arm/mach-mx2/Kconfig"
source "arch/arm/mach-mx3/Kconfig"
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 067556f7c91f..db74a929179d 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -5,4 +5,5 @@
# Common support
obj-y := irq.o clock.o gpio.o time.o devices.o
+obj-$(CONFIG_ARCH_MX1) += iomux-mx1-mx2.o dma-mx1-mx2.o
obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o
diff --git a/arch/arm/plat-mxc/dma-mx1-mx2.c b/arch/arm/plat-mxc/dma-mx1-mx2.c
index b296f19fd89a..2905ec758758 100644
--- a/arch/arm/plat-mxc/dma-mx1-mx2.c
+++ b/arch/arm/plat-mxc/dma-mx1-mx2.c
@@ -34,7 +34,6 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
#include <mach/dma-mx1-mx2.h>
#define DMA_DCR 0x00 /* Control Register */
@@ -114,7 +113,7 @@ struct imx_dma_channel {
void (*err_handler) (int, void *, int errcode);
void (*prog_handler) (int, void *, struct scatterlist *);
void *data;
- dmamode_t dma_mode;
+ unsigned int dma_mode;
struct scatterlist *sg;
unsigned int resbytes;
int dma_num;
@@ -193,7 +192,7 @@ static inline int imx_dma_sg_next(int channel, struct scatterlist *sg)
int
imx_dma_setup_single(int channel, dma_addr_t dma_address,
unsigned int dma_length, unsigned int dev_addr,
- dmamode_t dmamode)
+ unsigned int dmamode)
{
struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
@@ -288,7 +287,7 @@ int
imx_dma_setup_sg(int channel,
struct scatterlist *sg, unsigned int sgcount,
unsigned int dma_length, unsigned int dev_addr,
- dmamode_t dmamode)
+ unsigned int dmamode)
{
struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
@@ -512,6 +511,7 @@ void imx_dma_disable(int channel)
}
EXPORT_SYMBOL(imx_dma_disable);
+#ifdef CONFIG_ARCH_MX2
static void imx_dma_watchdog(unsigned long chno)
{
struct imx_dma_channel *imxdma = &imx_dma_channels[chno];
@@ -523,6 +523,7 @@ static void imx_dma_watchdog(unsigned long chno)
if (imxdma->err_handler)
imxdma->err_handler(chno, imxdma->data, IMX_DMA_ERR_TIMEOUT);
}
+#endif
static irqreturn_t dma_err_handler(int irq, void *dev_id)
{
@@ -675,7 +676,7 @@ int imx_dma_request(int channel, const char *name)
{
struct imx_dma_channel *imxdma = &imx_dma_channels[channel];
unsigned long flags;
- int ret;
+ int ret = 0;
/* basic sanity checks */
if (!name)
@@ -697,6 +698,7 @@ int imx_dma_request(int channel, const char *name)
ret = request_irq(MXC_INT_DMACH0 + channel, dma_irq_handler, 0, "DMA",
NULL);
if (ret) {
+ local_irq_restore(flags);
printk(KERN_CRIT "Can't register IRQ %d for DMA channel %d\n",
MXC_INT_DMACH0 + channel, channel);
return ret;
@@ -713,7 +715,7 @@ int imx_dma_request(int channel, const char *name)
imxdma->sg = NULL;
local_irq_restore(flags);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(imx_dma_request);
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c
index de5c4747453f..ccbd94adc668 100644
--- a/arch/arm/plat-mxc/gpio.c
+++ b/arch/arm/plat-mxc/gpio.c
@@ -115,8 +115,8 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
}
}
-#ifdef CONFIG_ARCH_MX3
-/* MX3 has one interrupt *per* gpio port */
+#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1)
+/* MX1 and MX3 has one interrupt *per* gpio port */
static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
@@ -237,7 +237,7 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
/* its a serious configuration bug when it fails */
BUG_ON( gpiochip_add(&port[i].chip) < 0 );
-#ifdef CONFIG_ARCH_MX3
+#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1)
/* setup one handler for each entry */
set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);
set_irq_data(port[i].irq, &port[i]);
diff --git a/arch/arm/plat-mxc/include/mach/board-mx27ads.h b/arch/arm/plat-mxc/include/mach/board-mx27ads.h
index 61e66dac90ef..8f34a05afc87 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx27ads.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx27ads.h
@@ -15,7 +15,7 @@
#define __ASM_ARCH_MXC_BOARD_MX27ADS_H__
/* external interrupt multiplexer */
-#define MXC_EXP_IO_BASE (MXC_GPIO_BASE + MXC_MAX_GPIO_LINES)
+#define MXC_EXP_IO_BASE (MXC_BOARD_IRQ_START)
#define MXC_VIRTUAL_INTS_BASE (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES)
#define MXC_SDIO1_CARD_IRQ MXC_VIRTUAL_INTS_BASE
@@ -28,11 +28,6 @@
/*
* MXC UART EVB board level configurations
*/
-
-#define MXC_LL_EXTUART_PADDR (CS4_BASE_ADDR + 0x20000)
-#define MXC_LL_EXTUART_VADDR (CS4_BASE_ADDR_VIRT + 0x20000)
-#define MXC_LL_EXTUART_16BIT_BUS
-
#define MXC_LL_UART_PADDR UART1_BASE_ADDR
#define MXC_LL_UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR)
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31ads.h b/arch/arm/plat-mxc/include/mach/board-mx31ads.h
index 745b48864f93..451d510d08c3 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31ads.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31ads.h
@@ -90,7 +90,7 @@
#define PBC_INTMASK_CLEAR_REG (PBC_INTMASK_CLEAR + PBC_BASE_ADDRESS)
#define EXPIO_PARENT_INT IOMUX_TO_IRQ(MX31_PIN_GPIO1_4)
-#define MXC_EXP_IO_BASE (MXC_MAX_INT_LINES + MXC_MAX_GPIO_LINES)
+#define MXC_EXP_IO_BASE (MXC_BOARD_IRQ_START)
#define MXC_IRQ_TO_EXPIO(irq) ((irq) - MXC_EXP_IO_BASE)
#define EXPIO_INT_LOW_BAT (MXC_EXP_IO_BASE + 0)
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31pdk.h b/arch/arm/plat-mxc/include/mach/board-mx31pdk.h
new file mode 100644
index 000000000000..2b6b316d0f51
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/board-mx31pdk.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2008 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 __ASM_ARCH_MXC_BOARD_MX31PDK_H__
+#define __ASM_ARCH_MXC_BOARD_MX31PDK_H__
+
+/* mandatory for CONFIG_LL_DEBUG */
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#endif /* __ASM_ARCH_MXC_BOARD_MX31PDK_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/debug-macro.S b/arch/arm/plat-mxc/include/mach/debug-macro.S
index b9907bebba3b..602768b427e2 100644
--- a/arch/arm/plat-mxc/include/mach/debug-macro.S
+++ b/arch/arm/plat-mxc/include/mach/debug-macro.S
@@ -28,6 +28,9 @@
#ifdef CONFIG_MACH_PCM038
#include <mach/board-pcm038.h>
#endif
+#ifdef CONFIG_MACH_MX31_3DS
+#include <mach/board-mx31pdk.h>
+#endif
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
diff --git a/arch/arm/plat-mxc/include/mach/dma-mx1-mx2.h b/arch/arm/plat-mxc/include/mach/dma-mx1-mx2.h
index e85fd946116c..b3876cc238ca 100644
--- a/arch/arm/plat-mxc/include/mach/dma-mx1-mx2.h
+++ b/arch/arm/plat-mxc/include/mach/dma-mx1-mx2.h
@@ -22,13 +22,15 @@
* MA 02110-1301, USA.
*/
-#include <asm/dma.h>
-
#ifndef __ASM_ARCH_MXC_DMA_H
#define __ASM_ARCH_MXC_DMA_H
#define IMX_DMA_CHANNELS 16
+#define DMA_MODE_READ 0
+#define DMA_MODE_WRITE 1
+#define DMA_MODE_MASK 1
+
#define DMA_BASE IO_ADDRESS(DMA_BASE_ADDR)
#define IMX_DMA_MEMSIZE_32 (0 << 4)
@@ -54,12 +56,12 @@ imx_dma_config_burstlen(int channel, unsigned int burstlen);
int
imx_dma_setup_single(int channel, dma_addr_t dma_address,
unsigned int dma_length, unsigned int dev_addr,
- dmamode_t dmamode);
+ unsigned int dmamode);
int
imx_dma_setup_sg(int channel, struct scatterlist *sg,
unsigned int sgcount, unsigned int dma_length,
- unsigned int dev_addr, dmamode_t dmamode);
+ unsigned int dev_addr, unsigned int dmamode);
int
imx_dma_setup_handlers(int channel,
diff --git a/arch/arm/plat-mxc/include/mach/dma.h b/arch/arm/plat-mxc/include/mach/dma.h
deleted file mode 100644
index c822d569a05e..000000000000
--- a/arch/arm/plat-mxc/include/mach/dma.h
+++ /dev/null
@@ -1,14 +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 __ASM_ARCH_MXC_DMA_H__
-#define __ASM_ARCH_MXC_DMA_H__
-
-#endif
diff --git a/arch/arm/plat-mxc/include/mach/entry-macro.S b/arch/arm/plat-mxc/include/mach/entry-macro.S
index 11632028f7d1..5f01d60da845 100644
--- a/arch/arm/plat-mxc/include/mach/entry-macro.S
+++ b/arch/arm/plat-mxc/include/mach/entry-macro.S
@@ -9,6 +9,8 @@
* published by the Free Software Foundation.
*/
+#include <mach/hardware.h>
+
#define AVIC_NIMASK 0x04
@ this macro disables fast irq (not implemented)
diff --git a/arch/arm/plat-mxc/include/mach/gpio.h b/arch/arm/plat-mxc/include/mach/gpio.h
index 65eedc0d196f..ea509f1090fb 100644
--- a/arch/arm/plat-mxc/include/mach/gpio.h
+++ b/arch/arm/plat-mxc/include/mach/gpio.h
@@ -27,8 +27,8 @@
#define gpio_set_value __gpio_set_value
#define gpio_cansleep __gpio_cansleep
-#define gpio_to_irq(gpio) (MXC_MAX_INT_LINES + (gpio))
-#define irq_to_gpio(irq) ((irq) - MXC_MAX_INT_LINES)
+#define gpio_to_irq(gpio) (MXC_GPIO_IRQ_START + (gpio))
+#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START)
struct mxc_gpio_port {
void __iomem *base;
diff --git a/arch/arm/plat-mxc/include/mach/hardware.h b/arch/arm/plat-mxc/include/mach/hardware.h
index 3caadeeda701..a612d8bb73c8 100644
--- a/arch/arm/plat-mxc/include/mach/hardware.h
+++ b/arch/arm/plat-mxc/include/mach/hardware.h
@@ -32,6 +32,10 @@
# endif
#endif
+#ifdef CONFIG_ARCH_MX1
+# include <mach/mx1.h>
+#endif
+
#include <mach/mxc.h>
#endif /* __ASM_ARCH_MXC_HARDWARE_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/io.h b/arch/arm/plat-mxc/include/mach/io.h
index 5d4cb1196441..b4f2de769466 100644
--- a/arch/arm/plat-mxc/include/mach/io.h
+++ b/arch/arm/plat-mxc/include/mach/io.h
@@ -25,8 +25,8 @@ __mx3_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
/* Access all peripherals below 0x80000000 as nonshared device
* but leave l2cc alone.
*/
- if ((phys_addr < 0x80000000) && ((phys_addr < L2CC_BASE_ADDR) ||
- (phys_addr >= L2CC_BASE_ADDR + L2CC_SIZE)))
+ if ((phys_addr < 0x80000000) && ((phys_addr < 0x30000000) ||
+ (phys_addr >= 0x30000000 + SZ_1M)))
mtype = MT_DEVICE_NONSHARED;
}
@@ -35,8 +35,8 @@ __mx3_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
#endif
/* io address mapping macro */
-#define __io(a) ((void __iomem *)(a))
+#define __io(a) __typesafe_io(a)
-#define __mem_pci(a) (a)
+#define __mem_pci(a) (a)
#endif
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h b/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h
index 3d09bfd6c53d..95a383be628e 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx1-mx2.h
@@ -21,12 +21,6 @@
#include <linux/io.h>
-#define MXC_GPIO_ALLOC_MODE_NORMAL 0
-#define MXC_GPIO_ALLOC_MODE_NO_ALLOC 1
-#define MXC_GPIO_ALLOC_MODE_TRY_ALLOC 2
-#define MXC_GPIO_ALLOC_MODE_ALLOC_ONLY 4
-#define MXC_GPIO_ALLOC_MODE_RELEASE 8
-
/*
* GPIO Module and I/O Multiplexer
* x = 0..3 for reg_A, reg_B, reg_C, reg_D
@@ -103,7 +97,8 @@
extern void mxc_gpio_mode(int gpio_mode);
extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
- int alloc_mode, const char *label);
+ const char *label);
+extern void mxc_gpio_release_multiple_pins(const int *pin_list, int count);
/*-------------------------------------------------------------------------*/
@@ -113,9 +108,9 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
* missing on some (many) pins
*/
#ifdef CONFIG_ARCH_MX1
-#define PA0_AIN_SPI2_CLK (GPIO_GIUS | GPIO_PORTA | GPIO_OUT | 0)
+#define PA0_AIN_SPI2_CLK (GPIO_PORTA | GPIO_OUT | 0)
#define PA0_AF_ETMTRACESYNC (GPIO_PORTA | GPIO_AF | 0)
-#define PA1_AOUT_SPI2_RXD (GPIO_GIUS | GPIO_PORTA | GPIO_IN | 1)
+#define PA1_AOUT_SPI2_RXD (GPIO_PORTA | GPIO_IN | 1)
#define PA1_PF_TIN (GPIO_PORTA | GPIO_PF | 1)
#define PA2_PF_PWM0 (GPIO_PORTA | GPIO_OUT | GPIO_PF | 2)
#define PA3_PF_CSI_MCLK (GPIO_PORTA | GPIO_PF | 3)
@@ -133,7 +128,7 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PA15_PF_I2C_SDA (GPIO_PORTA | GPIO_OUT | GPIO_PF | 15)
#define PA16_PF_I2C_SCL (GPIO_PORTA | GPIO_OUT | GPIO_PF | 16)
#define PA17_AF_ETMTRACEPKT4 (GPIO_PORTA | GPIO_AF | 17)
-#define PA17_AIN_SPI2_SS (GPIO_GIUS | GPIO_PORTA | GPIO_OUT | 17)
+#define PA17_AIN_SPI2_SS (GPIO_PORTA | GPIO_OUT | 17)
#define PA18_AF_ETMTRACEPKT5 (GPIO_PORTA | GPIO_AF | 18)
#define PA19_AF_ETMTRACEPKT6 (GPIO_PORTA | GPIO_AF | 19)
#define PA20_AF_ETMTRACEPKT7 (GPIO_PORTA | GPIO_AF | 20)
@@ -201,27 +196,27 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PC15_PF_SPI1_SS (GPIO_PORTC | GPIO_PF | 15)
#define PC16_PF_SPI1_MISO (GPIO_PORTC | GPIO_PF | 16)
#define PC17_PF_SPI1_MOSI (GPIO_PORTC | GPIO_PF | 17)
-#define PC24_BIN_UART3_RI (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 24)
-#define PC25_BIN_UART3_DSR (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 25)
-#define PC26_AOUT_UART3_DTR (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 26)
-#define PC27_BIN_UART3_DCD (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 27)
-#define PC28_BIN_UART3_CTS (GPIO_GIUS | GPIO_PORTC | GPIO_OUT | GPIO_BIN | 28)
-#define PC29_AOUT_UART3_RTS (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 29)
-#define PC30_BIN_UART3_TX (GPIO_GIUS | GPIO_PORTC | GPIO_BIN | 30)
-#define PC31_AOUT_UART3_RX (GPIO_GIUS | GPIO_PORTC | GPIO_IN | 31)
+#define PC24_BIN_UART3_RI (GPIO_PORTC | GPIO_OUT | GPIO_BIN | 24)
+#define PC25_BIN_UART3_DSR (GPIO_PORTC | GPIO_OUT | GPIO_BIN | 25)
+#define PC26_AOUT_UART3_DTR (GPIO_PORTC | GPIO_IN | 26)
+#define PC27_BIN_UART3_DCD (GPIO_PORTC | GPIO_OUT | GPIO_BIN | 27)
+#define PC28_BIN_UART3_CTS (GPIO_PORTC | GPIO_OUT | GPIO_BIN | 28)
+#define PC29_AOUT_UART3_RTS (GPIO_PORTC | GPIO_IN | 29)
+#define PC30_BIN_UART3_TX (GPIO_PORTC | GPIO_BIN | 30)
+#define PC31_AOUT_UART3_RX (GPIO_PORTC | GPIO_IN | 31)
#define PD6_PF_LSCLK (GPIO_PORTD | GPIO_OUT | GPIO_PF | 6)
#define PD7_PF_REV (GPIO_PORTD | GPIO_PF | 7)
-#define PD7_AF_UART2_DTR (GPIO_GIUS | GPIO_PORTD | GPIO_IN | GPIO_AF | 7)
-#define PD7_AIN_SPI2_SCLK (GPIO_GIUS | GPIO_PORTD | GPIO_AIN | 7)
+#define PD7_AF_UART2_DTR (GPIO_PORTD | GPIO_IN | GPIO_AF | 7)
+#define PD7_AIN_SPI2_SCLK (GPIO_PORTD | GPIO_AIN | 7)
#define PD8_PF_CLS (GPIO_PORTD | GPIO_PF | 8)
#define PD8_AF_UART2_DCD (GPIO_PORTD | GPIO_OUT | GPIO_AF | 8)
-#define PD8_AIN_SPI2_SS (GPIO_GIUS | GPIO_PORTD | GPIO_AIN | 8)
+#define PD8_AIN_SPI2_SS (GPIO_PORTD | GPIO_AIN | 8)
#define PD9_PF_PS (GPIO_PORTD | GPIO_PF | 9)
#define PD9_AF_UART2_RI (GPIO_PORTD | GPIO_OUT | GPIO_AF | 9)
-#define PD9_AOUT_SPI2_RXD (GPIO_GIUS | GPIO_PORTD | GPIO_IN | 9)
+#define PD9_AOUT_SPI2_RXD (GPIO_PORTD | GPIO_IN | 9)
#define PD10_PF_SPL_SPR (GPIO_PORTD | GPIO_OUT | GPIO_PF | 10)
#define PD10_AF_UART2_DSR (GPIO_PORTD | GPIO_OUT | GPIO_AF | 10)
-#define PD10_AIN_SPI2_TXD (GPIO_GIUS | GPIO_PORTD | GPIO_OUT | 10)
+#define PD10_AIN_SPI2_TXD (GPIO_PORTD | GPIO_OUT | 10)
#define PD11_PF_CONTRAST (GPIO_PORTD | GPIO_OUT | GPIO_PF | 11)
#define PD12_PF_ACD_OE (GPIO_PORTD | GPIO_OUT | GPIO_PF | 12)
#define PD13_PF_LP_HSYNC (GPIO_PORTD | GPIO_OUT | GPIO_PF | 13)
@@ -243,7 +238,7 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PD29_PF_LD14 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 29)
#define PD30_PF_LD15 (GPIO_PORTD | GPIO_OUT | GPIO_PF | 30)
#define PD31_PF_TMR2OUT (GPIO_PORTD | GPIO_PF | 31)
-#define PD31_BIN_SPI2_TXD (GPIO_GIUS | GPIO_PORTD | GPIO_BIN | 31)
+#define PD31_BIN_SPI2_TXD (GPIO_PORTD | GPIO_BIN | 31)
#endif
#ifdef CONFIG_ARCH_MX2
@@ -279,6 +274,12 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PA29_PF_VSYNC (GPIO_PORTA | GPIO_OUT | GPIO_PF | 29)
#define PA30_PF_CONTRAST (GPIO_PORTA | GPIO_OUT | GPIO_PF | 30)
#define PA31_PF_OE_ACD (GPIO_PORTA | GPIO_OUT | GPIO_PF | 31)
+#define PB4_PF_SD2_D0 (GPIO_PORTB | GPIO_PF | 4)
+#define PB5_PF_SD2_D1 (GPIO_PORTB | GPIO_PF | 5)
+#define PB6_PF_SD2_D2 (GPIO_PORTB | GPIO_PF | 6)
+#define PB7_PF_SD2_D3 (GPIO_PORTB | GPIO_PF | 7)
+#define PB8_PF_SD2_CMD (GPIO_PORTB | GPIO_PF | 8)
+#define PB9_PF_SD2_CLK (GPIO_PORTB | GPIO_PF | 9)
#define PB10_PF_CSI_D0 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 10)
#define PB10_AF_UART6_TXD (GPIO_PORTB | GPIO_OUT | GPIO_AF | 10)
#define PB11_PF_CSI_D1 (GPIO_PORTB | GPIO_OUT | GPIO_PF | 11)
@@ -315,6 +316,13 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PB31_AF_UART4_RXD (GPIO_PORTB | GPIO_IN | GPIO_AF | 31)
#define PC5_PF_I2C2_SDA (GPIO_PORTC | GPIO_IN | GPIO_PF | 5)
#define PC6_PF_I2C2_SCL (GPIO_PORTC | GPIO_IN | GPIO_PF | 6)
+#define PC7_PF_USBOTG_DATA5 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 7)
+#define PC8_PF_USBOTG_DATA6 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 8)
+#define PC9_PF_USBOTG_DATA0 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 9)
+#define PC10_PF_USBOTG_DATA2 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 10)
+#define PC11_PF_USBOTG_DATA1 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 11)
+#define PC12_PF_USBOTG_DATA4 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 12)
+#define PC13_PF_USBOTG_DATA3 (GPIO_PORTC | GPIO_OUT | GPIO_PF | 13)
#define PC16_PF_SSI4_FS (GPIO_PORTC | GPIO_IN | GPIO_PF | 16)
#define PC17_PF_SSI4_RXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 17)
#define PC18_PF_SSI4_TXD (GPIO_PORTC | GPIO_IN | GPIO_PF | 18)
@@ -365,6 +373,9 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PD30_PF_CSPI1_MISO (GPIO_PORTD | GPIO_IN | GPIO_PF | 30)
#define PD31_PF_CSPI1_MOSI (GPIO_PORTD | GPIO_OUT | GPIO_PF | 31)
#define PF23_AIN_FEC_TX_EN (GPIO_PORTF | GPIO_OUT | GPIO_AIN | 23)
+#define PE0_PF_USBOTG_NXT (GPIO_PORTE | GPIO_OUT | GPIO_PF | 0)
+#define PE1_PF_USBOTG_STP (GPIO_PORTE | GPIO_OUT | GPIO_PF | 1)
+#define PE2_PF_USBOTG_DIR (GPIO_PORTE | GPIO_OUT | GPIO_PF | 2)
#define PE3_PF_UART2_CTS (GPIO_PORTE | GPIO_OUT | GPIO_PF | 3)
#define PE4_PF_UART2_RTS (GPIO_PORTE | GPIO_IN | GPIO_PF | 4)
#define PE6_PF_UART2_TXD (GPIO_PORTE | GPIO_OUT | GPIO_PF | 6)
@@ -379,18 +390,27 @@ extern int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
#define PE15_PF_UART1_RTS (GPIO_PORTE | GPIO_IN | GPIO_PF | 15)
#define PE16_AF_RTCK (GPIO_PORTE | GPIO_OUT | GPIO_AF | 16)
#define PE16_PF_RTCK (GPIO_PORTE | GPIO_OUT | GPIO_PF | 16)
+#define PE18_PF_SDHC1_D0 (GPIO_PORTE | GPIO_PF | 18)
#define PE18_AF_CSPI3_MISO (GPIO_PORTE | GPIO_IN | GPIO_AF | 18)
+#define PE19_PF_SDHC1_D1 (GPIO_PORTE | GPIO_PF | 19)
+#define PE20_PF_SDHC1_D2 (GPIO_PORTE | GPIO_PF | 20)
+#define PE21_PF_SDHC1_D3 (GPIO_PORTE | GPIO_PF | 21)
#define PE21_AF_CSPI3_SS (GPIO_PORTE | GPIO_OUT | GPIO_AF | 21)
+#define PE22_PF_SDHC1_CMD (GPIO_PORTE | GPIO_PF | 22)
#define PE22_AF_CSPI3_MOSI (GPIO_PORTE | GPIO_OUT | GPIO_AF | 22)
+#define PE22_PF_SDHC1_CLK (GPIO_PORTE | GPIO_PF | 23)
#define PE23_AF_CSPI3_SCLK (GPIO_PORTE | GPIO_OUT | GPIO_AF | 23)
+#define PE24_PF_USBOTG_CLK (GPIO_PORTE | GPIO_OUT | GPIO_PF | 24)
+#define PE25_PF_USBOTG_DATA7 (GPIO_PORTE | GPIO_OUT | GPIO_PF | 25)
#endif
/* decode irq number to use with IMR(x), ISR(x) and friends */
-#define IRQ_TO_REG(irq) ((irq - MXC_MAX_INT_LINES) >> 5)
+#define IRQ_TO_REG(irq) ((irq - MXC_INTERNAL_IRQS) >> 5)
-#define IRQ_GPIOA(x) (MXC_MAX_INT_LINES + x)
+#define IRQ_GPIOA(x) (MXC_GPIO_IRQ_START + x)
#define IRQ_GPIOB(x) (IRQ_GPIOA(32) + x)
#define IRQ_GPIOC(x) (IRQ_GPIOB(32) + x)
#define IRQ_GPIOD(x) (IRQ_GPIOC(32) + x)
+#define IRQ_GPIOE(x) (IRQ_GPIOD(32) + x)
#endif /* _MXC_GPIO_MX1_MX2_H */
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx3.h b/arch/arm/plat-mxc/include/mach/iomux-mx3.h
index c9f39c2fb8c6..c9198c0aea18 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx3.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx3.h
@@ -141,7 +141,7 @@ void mxc_iomux_set_gpr(enum iomux_gp_func, bool);
((iomux_pin & IOMUX_GPIONUM_MASK) >> IOMUX_GPIONUM_SHIFT)
#define IOMUX_TO_IRQ(iomux_pin) \
(((iomux_pin & IOMUX_GPIONUM_MASK) >> IOMUX_GPIONUM_SHIFT) + \
- MXC_GPIO_INT_BASE)
+ MXC_GPIO_IRQ_START)
/*
* This enumeration is constructed based on the Section
@@ -491,6 +491,14 @@ enum iomux_pins {
#define MX31_PIN_RTS1__RTS1 IOMUX_MODE(MX31_PIN_RTS1, IOMUX_CONFIG_FUNC)
#define MX31_PIN_TXD1__TXD1 IOMUX_MODE(MX31_PIN_TXD1, IOMUX_CONFIG_FUNC)
#define MX31_PIN_RXD1__RXD1 IOMUX_MODE(MX31_PIN_RXD1, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_CTS2__CTS2 IOMUX_MODE(MX31_PIN_CTS2, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_RTS2__RTS2 IOMUX_MODE(MX31_PIN_RTS2, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_TXD2__TXD2 IOMUX_MODE(MX31_PIN_TXD2, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_RXD2__RXD2 IOMUX_MODE(MX31_PIN_RXD2, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_PC_RST__CTS5 IOMUX_MODE(MX31_PIN_PC_RST, IOMUX_CONFIG_ALT2)
+#define MX31_PIN_PC_VS2__RTS5 IOMUX_MODE(MX31_PIN_PC_VS2, IOMUX_CONFIG_ALT2)
+#define MX31_PIN_PC_BVD2__TXD5 IOMUX_MODE(MX31_PIN_PC_BVD2, IOMUX_CONFIG_ALT2)
+#define MX31_PIN_PC_BVD1__RXD5 IOMUX_MODE(MX31_PIN_PC_BVD1, IOMUX_CONFIG_ALT2)
#define MX31_PIN_CSPI1_MOSI__MOSI IOMUX_MODE(MX31_PIN_CSPI1_MOSI, IOMUX_CONFIG_FUNC)
#define MX31_PIN_CSPI1_MISO__MISO IOMUX_MODE(MX31_PIN_CSPI1_MISO, IOMUX_CONFIG_FUNC)
#define MX31_PIN_CSPI1_SCLK__SCLK IOMUX_MODE(MX31_PIN_CSPI1_SCLK, IOMUX_CONFIG_FUNC)
@@ -509,6 +517,15 @@ enum iomux_pins {
#define MX31_PIN_CSPI3_MISO__MISO IOMUX_MODE(MX31_PIN_CSPI3_MISO, IOMUX_CONFIG_FUNC)
#define MX31_PIN_CSPI3_SCLK__SCLK IOMUX_MODE(MX31_PIN_CSPI3_SCLK, IOMUX_CONFIG_FUNC)
#define MX31_PIN_CSPI3_SPI_RDY__SPI_RDY IOMUX_MODE(MX31_PIN_CSPI3_SPI_RDY, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_BATT_LINE__OWIRE IOMUX_MODE(MX31_PIN_BATT_LINE, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_CS4__CS4 IOMUX_MODE(MX31_PIN_CS4, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_DATA3__SD1_DATA3 IOMUX_MODE(MX31_PIN_SD1_DATA3, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_DATA2__SD1_DATA2 IOMUX_MODE(MX31_PIN_SD1_DATA2, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_DATA1__SD1_DATA1 IOMUX_MODE(MX31_PIN_SD1_DATA1, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_DATA0__SD1_DATA0 IOMUX_MODE(MX31_PIN_SD1_DATA0, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_CLK__SD1_CLK IOMUX_MODE(MX31_PIN_SD1_CLK, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_SD1_CMD__SD1_CMD IOMUX_MODE(MX31_PIN_SD1_CMD, IOMUX_CONFIG_FUNC)
+
/*XXX: The SS0, SS1, SS2, SS3 lines of spi3 are multiplexed by cspi2_ss0, cspi2_ss1, cspi1_ss0
* cspi1_ss1*/
diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h
index b55bba35e18a..e06d3cb0ee11 100644
--- a/arch/arm/plat-mxc/include/mach/irqs.h
+++ b/arch/arm/plat-mxc/include/mach/irqs.h
@@ -11,7 +11,37 @@
#ifndef __ASM_ARCH_MXC_IRQS_H__
#define __ASM_ARCH_MXC_IRQS_H__
-#include <mach/hardware.h>
+/*
+ * So far all i.MX SoCs have 64 internal interrupts
+ */
+#define MXC_INTERNAL_IRQS 64
+
+#define MXC_GPIO_IRQ_START MXC_INTERNAL_IRQS
+
+#if defined CONFIG_ARCH_MX1
+#define MXC_GPIO_IRQS (32 * 4)
+#elif defined CONFIG_ARCH_MX2
+#define MXC_GPIO_IRQS (32 * 6)
+#elif defined CONFIG_ARCH_MX3
+#define MXC_GPIO_IRQS (32 * 3)
+#endif
+
+/*
+ * 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 MXC_BOARD_IRQS, but keep it
+ * within sensible limits.
+ */
+#define MXC_BOARD_IRQ_START (MXC_INTERNAL_IRQS + MXC_GPIO_IRQS)
+#define MXC_BOARD_IRQS 16
+
+#define NR_IRQS (MXC_BOARD_IRQ_START + MXC_BOARD_IRQS)
+
extern void imx_irq_set_priority(unsigned char irq, unsigned char prio);
+/* all normal IRQs can be FIQs */
+#define FIQ_START 0
+/* switch betwean IRQ and FIQ */
+extern int mxc_set_irq_fiq(unsigned int irq, unsigned int type);
+
#endif /* __ASM_ARCH_MXC_IRQS_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/memory.h b/arch/arm/plat-mxc/include/mach/memory.h
index d7a8d3ebed57..0b808399097f 100644
--- a/arch/arm/plat-mxc/include/mach/memory.h
+++ b/arch/arm/plat-mxc/include/mach/memory.h
@@ -11,19 +11,12 @@
#ifndef __ASM_ARCH_MXC_MEMORY_H__
#define __ASM_ARCH_MXC_MEMORY_H__
-#include <mach/hardware.h>
-
-/*
- * Virtual view <-> DMA view memory address translations
- * This macro is used to translate the virtual address to an address
- * suitable to be passed to set_dma_addr()
- */
-#define __virt_to_bus(a) __virt_to_phys(a)
-
-/*
- * Used to convert an address for DMA operations to an address that the
- * kernel can use.
- */
-#define __bus_to_virt(a) __phys_to_virt(a)
+#if defined CONFIG_ARCH_MX1
+#define PHYS_OFFSET UL(0x08000000)
+#elif defined CONFIG_ARCH_MX2
+#define PHYS_OFFSET UL(0xA0000000)
+#elif defined CONFIG_ARCH_MX3
+#define PHYS_OFFSET UL(0x80000000)
+#endif
#endif /* __ASM_ARCH_MXC_MEMORY_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mtd-xip.h b/arch/arm/plat-mxc/include/mach/mtd-xip.h
new file mode 100644
index 000000000000..1ab1bba5688d
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mtd-xip.h
@@ -0,0 +1,34 @@
+/*
+ * MTD primitives for XIP support. Architecture specific functions
+ *
+ * Do not include this file directly. It's included from linux/mtd/xip.h
+ *
+ * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>, Teltonika, 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 <mach/mxc_timer.h>
+
+#ifndef __ARCH_IMX_MTD_XIP_H__
+#define __ARCH_IMX_MTD_XIP_H__
+
+#ifdef CONFIG_ARCH_MX1
+/* AITC registers */
+#define AITC_BASE IO_ADDRESS(AVIC_BASE_ADDR)
+#define NIPNDH (AITC_BASE + 0x58)
+#define NIPNDL (AITC_BASE + 0x5C)
+#define INTENABLEH (AITC_BASE + 0x10)
+#define INTENABLEL (AITC_BASE + 0x14)
+/* MTD macros */
+#define xip_irqpending() ((__raw_readl(INTENABLEH) & __raw_readl(NIPNDH)) \
+ || (__raw_readl(INTENABLEL) & __raw_readl(NIPNDL)))
+#define xip_currtime() (__raw_readl(TIMER_BASE + MXC_TCN))
+#define xip_elapsed_since(x) (signed)((__raw_readl(TIMER_BASE + MXC_TCN) - (x)) / 96)
+#define xip_cpu_idle() asm volatile ("mcr p15, 0, %0, c7, c0, 4" :: "r" (0))
+#endif /* CONFIG_ARCH_MX1 */
+
+#endif /* __ARCH_IMX_MTD_XIP_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx1.h b/arch/arm/plat-mxc/include/mach/mx1.h
new file mode 100644
index 000000000000..b92e02324d8e
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx1.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 1997,1998 Russell King
+ * Copyright (C) 1999 ARM Limited
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (c) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_ARCH_MXC_MX1_H__
+#define __ASM_ARCH_MXC_MX1_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+#include <mach/vmalloc.h>
+
+/*
+ * Memory map
+ */
+#define IMX_IO_PHYS 0x00200000
+#define IMX_IO_SIZE 0x00100000
+#define IMX_IO_BASE VMALLOC_END
+
+#define IMX_CS0_PHYS 0x10000000
+#define IMX_CS0_SIZE 0x02000000
+
+#define IMX_CS1_PHYS 0x12000000
+#define IMX_CS1_SIZE 0x01000000
+
+#define IMX_CS2_PHYS 0x13000000
+#define IMX_CS2_SIZE 0x01000000
+
+#define IMX_CS3_PHYS 0x14000000
+#define IMX_CS3_SIZE 0x01000000
+
+#define IMX_CS4_PHYS 0x15000000
+#define IMX_CS4_SIZE 0x01000000
+
+#define IMX_CS5_PHYS 0x16000000
+#define IMX_CS5_SIZE 0x01000000
+
+/*
+ * Register BASEs, based on OFFSETs
+ */
+#define AIPI1_BASE_ADDR (0x00000 + IMX_IO_PHYS)
+#define WDT_BASE_ADDR (0x01000 + IMX_IO_PHYS)
+#define TIM1_BASE_ADDR (0x02000 + IMX_IO_PHYS)
+#define TIM2_BASE_ADDR (0x03000 + IMX_IO_PHYS)
+#define RTC_BASE_ADDR (0x04000 + IMX_IO_PHYS)
+#define LCDC_BASE_ADDR (0x05000 + IMX_IO_PHYS)
+#define UART1_BASE_ADDR (0x06000 + IMX_IO_PHYS)
+#define UART2_BASE_ADDR (0x07000 + IMX_IO_PHYS)
+#define PWM_BASE_ADDR (0x08000 + IMX_IO_PHYS)
+#define DMA_BASE_ADDR (0x09000 + IMX_IO_PHYS)
+#define AIPI2_BASE_ADDR (0x10000 + IMX_IO_PHYS)
+#define SIM_BASE_ADDR (0x11000 + IMX_IO_PHYS)
+#define USBD_BASE_ADDR (0x12000 + IMX_IO_PHYS)
+#define SPI1_BASE_ADDR (0x13000 + IMX_IO_PHYS)
+#define MMC_BASE_ADDR (0x14000 + IMX_IO_PHYS)
+#define ASP_BASE_ADDR (0x15000 + IMX_IO_PHYS)
+#define BTA_BASE_ADDR (0x16000 + IMX_IO_PHYS)
+#define I2C_BASE_ADDR (0x17000 + IMX_IO_PHYS)
+#define SSI_BASE_ADDR (0x18000 + IMX_IO_PHYS)
+#define SPI2_BASE_ADDR (0x19000 + IMX_IO_PHYS)
+#define MSHC_BASE_ADDR (0x1A000 + IMX_IO_PHYS)
+#define CCM_BASE_ADDR (0x1B000 + IMX_IO_PHYS)
+#define SCM_BASE_ADDR (0x1B804 + IMX_IO_PHYS)
+#define GPIO_BASE_ADDR (0x1C000 + IMX_IO_PHYS)
+#define EIM_BASE_ADDR (0x20000 + IMX_IO_PHYS)
+#define SDRAMC_BASE_ADDR (0x21000 + IMX_IO_PHYS)
+#define MMA_BASE_ADDR (0x22000 + IMX_IO_PHYS)
+#define AVIC_BASE_ADDR (0x23000 + IMX_IO_PHYS)
+#define CSI_BASE_ADDR (0x24000 + IMX_IO_PHYS)
+
+/* macro to get at IO space when running virtually */
+#define IO_ADDRESS(x) ((x) - IMX_IO_PHYS + IMX_IO_BASE)
+
+/* define macros needed for entry-macro.S */
+#define AVIC_IO_ADDRESS(x) IO_ADDRESS(x)
+
+/* fixed interrput numbers */
+#define INT_SOFTINT 0
+#define CSI_INT 6
+#define DSPA_MAC_INT 7
+#define DSPA_INT 8
+#define COMP_INT 9
+#define MSHC_XINT 10
+#define GPIO_INT_PORTA 11
+#define GPIO_INT_PORTB 12
+#define GPIO_INT_PORTC 13
+#define LCDC_INT 14
+#define SIM_INT 15
+#define SIM_DATA_INT 16
+#define RTC_INT 17
+#define RTC_SAMINT 18
+#define UART2_MINT_PFERR 19
+#define UART2_MINT_RTS 20
+#define UART2_MINT_DTR 21
+#define UART2_MINT_UARTC 22
+#define UART2_MINT_TX 23
+#define UART2_MINT_RX 24
+#define UART1_MINT_PFERR 25
+#define UART1_MINT_RTS 26
+#define UART1_MINT_DTR 27
+#define UART1_MINT_UARTC 28
+#define UART1_MINT_TX 29
+#define UART1_MINT_RX 30
+#define VOICE_DAC_INT 31
+#define VOICE_ADC_INT 32
+#define PEN_DATA_INT 33
+#define PWM_INT 34
+#define SDHC_INT 35
+#define I2C_INT 39
+#define CSPI_INT 41
+#define SSI_TX_INT 42
+#define SSI_TX_ERR_INT 43
+#define SSI_RX_INT 44
+#define SSI_RX_ERR_INT 45
+#define TOUCH_INT 46
+#define USBD_INT0 47
+#define USBD_INT1 48
+#define USBD_INT2 49
+#define USBD_INT3 50
+#define USBD_INT4 51
+#define USBD_INT5 52
+#define USBD_INT6 53
+#define BTSYS_INT 55
+#define BTTIM_INT 56
+#define BTWUI_INT 57
+#define TIM2_INT 58
+#define TIM1_INT 59
+#define DMA_ERR 60
+#define DMA_INT 61
+#define GPIO_INT_PORTD 62
+#define WDT_INT 63
+
+/* gpio and gpio based interrupt handling */
+#define GPIO_DR 0x1C
+#define GPIO_GDIR 0x00
+#define GPIO_PSR 0x24
+#define GPIO_ICR1 0x28
+#define GPIO_ICR2 0x2C
+#define GPIO_IMR 0x30
+#define GPIO_ISR 0x34
+#define GPIO_INT_LOW_LEV 0x3
+#define GPIO_INT_HIGH_LEV 0x2
+#define GPIO_INT_RISE_EDGE 0x0
+#define GPIO_INT_FALL_EDGE 0x1
+#define GPIO_INT_NONE 0x4
+
+/* DMA */
+#define DMA_REQ_UART3_T 2
+#define DMA_REQ_UART3_R 3
+#define DMA_REQ_SSI2_T 4
+#define DMA_REQ_SSI2_R 5
+#define DMA_REQ_CSI_STAT 6
+#define DMA_REQ_CSI_R 7
+#define DMA_REQ_MSHC 8
+#define DMA_REQ_DSPA_DCT_DOUT 9
+#define DMA_REQ_DSPA_DCT_DIN 10
+#define DMA_REQ_DSPA_MAC 11
+#define DMA_REQ_EXT 12
+#define DMA_REQ_SDHC 13
+#define DMA_REQ_SPI1_R 14
+#define DMA_REQ_SPI1_T 15
+#define DMA_REQ_SSI_T 16
+#define DMA_REQ_SSI_R 17
+#define DMA_REQ_ASP_DAC 18
+#define DMA_REQ_ASP_ADC 19
+#define DMA_REQ_USP_EP(x) (20 + (x))
+#define DMA_REQ_SPI2_R 26
+#define DMA_REQ_SPI2_T 27
+#define DMA_REQ_UART2_T 28
+#define DMA_REQ_UART2_R 29
+#define DMA_REQ_UART1_T 30
+#define DMA_REQ_UART1_R 31
+
+/* mandatory for CONFIG_LL_DEBUG */
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR IO_ADDRESS(UART1_BASE_ADDR)
+
+#endif /* __ASM_ARCH_MXC_MX1_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h
index a86db64744a1..0313be720552 100644
--- a/arch/arm/plat-mxc/include/mach/mx27.h
+++ b/arch/arm/plat-mxc/include/mach/mx27.h
@@ -72,7 +72,8 @@
/* for mx27*/
#define OTG_BASE_ADDR USBOTG_BASE_ADDR
#define SAHARA_BASE_ADDR (AIPI_BASE_ADDR + 0x25000)
-#define EMMA_BASE_ADDR (AIPI_BASE_ADDR + 0x26400)
+#define EMMA_PP_BASE_ADDR (AIPI_BASE_ADDR + 0x26000)
+#define EMMA_PRP_BASE_ADDR (AIPI_BASE_ADDR + 0x26400)
#define CCM_BASE_ADDR (AIPI_BASE_ADDR + 0x27000)
#define SYSCTRL_BASE_ADDR (AIPI_BASE_ADDR + 0x27800)
#define IIM_BASE_ADDR (AIPI_BASE_ADDR + 0x28000)
@@ -288,16 +289,4 @@ extern int mx27_revision(void);
/* this CPU supports up to 192 GPIOs (don't forget the baseboard!) */
#define ARCH_NR_GPIOS (192 + 16)
-/* OS clock tick rate */
-#define CLOCK_TICK_RATE 13300000
-
-/* Start of RAM */
-#define PHYS_OFFSET SDRAM_BASE_ADDR
-
-/* max interrupt lines count */
-#define NR_IRQS 256
-
-/* count of internal interrupt sources */
-#define MXC_MAX_INT_LINES 64
-
#endif /* __ASM_ARCH_MXC_MX27_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h
index 0536f8917bc0..de026654b00e 100644
--- a/arch/arm/plat-mxc/include/mach/mx31.h
+++ b/arch/arm/plat-mxc/include/mach/mx31.h
@@ -15,11 +15,6 @@
#error "Do not include directly."
#endif
-/*!
- * defines the hardware clock tick rate
- */
-#define CLOCK_TICK_RATE 16625000
-
/*
* MX31 memory map:
*
@@ -244,9 +239,6 @@
#define PCMCIA_IO_ADDRESS(x) \
(((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
-/* Start of physical RAM - On many MX31 platforms, this is the first SDRAM bank (CSD0) */
-#define PHYS_OFFSET CSD0_BASE_ADDR
-
/*
* Interrupt numbers
*/
@@ -315,23 +307,6 @@
#define MXC_INT_EXT_WDOG 62
#define MXC_INT_EXT_TV 63
-#define MXC_MAX_INT_LINES 64
-
-#define MXC_GPIO_INT_BASE MXC_MAX_INT_LINES
-#define MXC_MAX_GPIO_LINES (GPIO_NUM_PIN * GPIO_PORT_NUM)
-#define MXC_MAX_VIRTUAL_INTS 16
-
-#define NR_IRQS (MXC_MAX_INT_LINES + MXC_MAX_GPIO_LINES + MXC_MAX_VIRTUAL_INTS)
-
-/*!
- * Number of GPIO port as defined in the IC Spec
- */
-#define GPIO_PORT_NUM 3
-/*!
- * Number of GPIO pins per port
- */
-#define GPIO_NUM_PIN 32
-
#define PROD_SIGNATURE 0x1 /* For MX31 */
/* silicon revisions specific to i.MX31 */
diff --git a/arch/arm/plat-mxc/include/mach/mxc_timer.h b/arch/arm/plat-mxc/include/mach/mxc_timer.h
index 130aebfbe168..6c19a134744b 100644
--- a/arch/arm/plat-mxc/include/mach/mxc_timer.h
+++ b/arch/arm/plat-mxc/include/mach/mxc_timer.h
@@ -26,7 +26,7 @@
#include <linux/clk.h>
#include <mach/hardware.h>
-#ifdef CONFIG_ARCH_IMX
+#ifdef CONFIG_ARCH_MX1
#define TIMER_BASE IO_ADDRESS(TIM1_BASE_ADDR)
#define TIMER_INTERRUPT TIM1_INT
@@ -65,7 +65,7 @@ static void gpt_irq_acknowledge(void)
{
__raw_writel(0, TIMER_BASE + MXC_TSTAT);
}
-#endif /* CONFIG_ARCH_IMX */
+#endif /* CONFIG_ARCH_MX1 */
#ifdef CONFIG_ARCH_MX2
#define TIMER_BASE IO_ADDRESS(GPT1_BASE_ADDR)
diff --git a/arch/arm/plat-mxc/include/mach/timex.h b/arch/arm/plat-mxc/include/mach/timex.h
index 0b0af0253e91..07b4a73c9d2f 100644
--- a/arch/arm/plat-mxc/include/mach/timex.h
+++ b/arch/arm/plat-mxc/include/mach/timex.h
@@ -20,6 +20,12 @@
#ifndef __ASM_ARCH_MXC_TIMEX_H__
#define __ASM_ARCH_MXC_TIMEX_H__
-#include <mach/hardware.h> /* for CLOCK_TICK_RATE */
+#if defined CONFIG_ARCH_MX1
+#define CLOCK_TICK_RATE 16000000
+#elif defined CONFIG_ARCH_MX2
+#define CLOCK_TICK_RATE 13300000
+#elif defined CONFIG_ARCH_MX3
+#define CLOCK_TICK_RATE 16625000
+#endif
#endif /* __ASM_ARCH_MXC_TIMEX_H__ */
diff --git a/arch/arm/plat-mxc/iomux-mx1-mx2.c b/arch/arm/plat-mxc/iomux-mx1-mx2.c
index d97387aa9a42..df6f18395686 100644
--- a/arch/arm/plat-mxc/iomux-mx1-mx2.c
+++ b/arch/arm/plat-mxc/iomux-mx1-mx2.c
@@ -110,12 +110,13 @@ void mxc_gpio_mode(int gpio_mode)
EXPORT_SYMBOL(mxc_gpio_mode);
int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
- int alloc_mode, const char *label)
+ const char *label)
{
const int *p = pin_list;
int i;
unsigned gpio;
unsigned mode;
+ int ret = -EINVAL;
for (i = 0; i < count; i++) {
gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK);
@@ -124,33 +125,33 @@ int mxc_gpio_setup_multiple_pins(const int *pin_list, unsigned count,
if (gpio >= (GPIO_PORT_MAX + 1) * 32)
goto setup_error;
- if (alloc_mode & MXC_GPIO_ALLOC_MODE_RELEASE)
- gpio_free(gpio);
- else if (!(alloc_mode & MXC_GPIO_ALLOC_MODE_NO_ALLOC))
- if (gpio_request(gpio, label)
- && !(alloc_mode & MXC_GPIO_ALLOC_MODE_TRY_ALLOC))
- goto setup_error;
+ ret = gpio_request(gpio, label);
+ if (ret)
+ goto setup_error;
- if (!(alloc_mode & (MXC_GPIO_ALLOC_MODE_ALLOC_ONLY |
- MXC_GPIO_ALLOC_MODE_RELEASE)))
- mxc_gpio_mode(gpio | mode);
+ mxc_gpio_mode(gpio | mode);
p++;
}
return 0;
setup_error:
- if (alloc_mode & (MXC_GPIO_ALLOC_MODE_NO_ALLOC |
- MXC_GPIO_ALLOC_MODE_TRY_ALLOC))
- return -EINVAL;
+ mxc_gpio_release_multiple_pins(pin_list, i);
+ return ret;
+}
+EXPORT_SYMBOL(mxc_gpio_setup_multiple_pins);
- while (p != pin_list) {
- p--;
- gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK);
+void mxc_gpio_release_multiple_pins(const int *pin_list, int count)
+{
+ const int *p = pin_list;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ unsigned gpio = *p & (GPIO_PIN_MASK | GPIO_PORT_MASK);
gpio_free(gpio);
+ p++;
}
- return -EINVAL;
}
-EXPORT_SYMBOL(mxc_gpio_setup_multiple_pins);
+EXPORT_SYMBOL(mxc_gpio_release_multiple_pins);
diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c
index d862c9e5f8db..6e7578a3514b 100644
--- a/arch/arm/plat-mxc/irq.c
+++ b/arch/arm/plat-mxc/irq.c
@@ -17,9 +17,12 @@
* MA 02110-1301, USA.
*/
+#include <linux/module.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <mach/common.h>
+#include <asm/mach/irq.h>
+#include <mach/hardware.h>
#define AVIC_BASE IO_ADDRESS(AVIC_BASE_ADDR)
#define AVIC_INTCNTL (AVIC_BASE + 0x00) /* int control reg */
@@ -65,6 +68,28 @@ void imx_irq_set_priority(unsigned char irq, unsigned char prio)
EXPORT_SYMBOL(imx_irq_set_priority);
#endif
+#ifdef CONFIG_FIQ
+int mxc_set_irq_fiq(unsigned int irq, unsigned int type)
+{
+ unsigned int irqt;
+
+ if (irq >= MXC_INTERNAL_IRQS)
+ return -EINVAL;
+
+ if (irq < MXC_INTERNAL_IRQS / 2) {
+ irqt = __raw_readl(AVIC_INTTYPEL) & ~(1 << irq);
+ __raw_writel(irqt | (!!type << irq), AVIC_INTTYPEL);
+ } else {
+ irq -= MXC_INTERNAL_IRQS / 2;
+ irqt = __raw_readl(AVIC_INTTYPEH) & ~(1 << irq);
+ __raw_writel(irqt | (!!type << irq), AVIC_INTTYPEH);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_set_irq_fiq);
+#endif /* CONFIG_FIQ */
+
/* Disable interrupt number "irq" in the AVIC */
static void mxc_mask_irq(unsigned int irq)
{
@@ -91,7 +116,6 @@ static struct irq_chip mxc_avic_chip = {
void __init mxc_init_irq(void)
{
int i;
- u32 reg;
/* put the AVIC into the reset value with
* all interrupts disabled
@@ -106,7 +130,7 @@ void __init mxc_init_irq(void)
/* all IRQ no FIQ */
__raw_writel(0, AVIC_INTTYPEH);
__raw_writel(0, AVIC_INTTYPEL);
- for (i = 0; i < MXC_MAX_INT_LINES; i++) {
+ for (i = 0; i < MXC_INTERNAL_IRQS; i++) {
set_irq_chip(i, &mxc_avic_chip);
set_irq_handler(i, handle_level_irq);
set_irq_flags(i, IRQF_VALID);
@@ -119,5 +143,10 @@ void __init mxc_init_irq(void)
/* init architectures chained interrupt handler */
mxc_register_gpios();
+#ifdef CONFIG_FIQ
+ /* Initialize FIQ */
+ init_FIQ();
+#endif
+
printk(KERN_INFO "MXC IRQ initialized\n");
}
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index a94f0c44ebc8..46d3b0b9ce69 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -14,9 +14,11 @@ config ARCH_OMAP1
config ARCH_OMAP2
bool "TI OMAP2"
+ select CPU_V6
config ARCH_OMAP3
bool "TI OMAP3"
+ select CPU_V7
endchoice
diff --git a/arch/arm/plat-omap/debug-devices.c b/arch/arm/plat-omap/debug-devices.c
index e31154b15d9e..f6684832ca8f 100644
--- a/arch/arm/plat-omap/debug-devices.c
+++ b/arch/arm/plat-omap/debug-devices.c
@@ -69,15 +69,15 @@ int __init debug_card_init(u32 addr, unsigned gpio)
smc91x_resources[0].start = addr + 0x300;
smc91x_resources[0].end = addr + 0x30f;
- smc91x_resources[1].start = OMAP_GPIO_IRQ(gpio);
- smc91x_resources[1].end = OMAP_GPIO_IRQ(gpio);
+ smc91x_resources[1].start = gpio_to_irq(gpio);
+ smc91x_resources[1].end = gpio_to_irq(gpio);
- status = omap_request_gpio(gpio);
+ status = gpio_request(gpio, "SMC91x irq");
if (status < 0) {
printk(KERN_ERR "GPIO%d unavailable for smc91x IRQ\n", gpio);
return status;
}
- omap_set_gpio_direction(gpio, 1);
+ gpio_direction_input(gpio);
led_resources[0].start = addr;
led_resources[0].end = addr + SZ_4K - 1;
diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c
index 2f4c0cabfd34..be4eefda4767 100644
--- a/arch/arm/plat-omap/debug-leds.c
+++ b/arch/arm/plat-omap/debug-leds.c
@@ -83,8 +83,8 @@ static void h2p2_dbg_leds_event(led_event_t evt)
/* all leds off during suspend or shutdown */
if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) {
- omap_set_gpio_dataout(GPIO_TIMER, 0);
- omap_set_gpio_dataout(GPIO_IDLE, 0);
+ gpio_set_value(GPIO_TIMER, 0);
+ gpio_set_value(GPIO_IDLE, 0);
}
__raw_writew(~0, &fpga->leds);
@@ -107,7 +107,7 @@ static void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2() || machine_is_omap_h4())
hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
else {
- omap_set_gpio_dataout(GPIO_TIMER,
+ gpio_set_value(GPIO_TIMER,
led_state & LED_TIMER_ON);
goto done;
}
@@ -121,7 +121,7 @@ static void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2() || machine_is_omap_h4())
hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
else {
- omap_set_gpio_dataout(GPIO_IDLE, 1);
+ gpio_set_value(GPIO_IDLE, 1);
goto done;
}
@@ -131,7 +131,7 @@ static void h2p2_dbg_leds_event(led_event_t evt)
if (machine_is_omap_perseus2() || machine_is_omap_h4())
hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
else {
- omap_set_gpio_dataout(GPIO_IDLE, 0);
+ gpio_set_value(GPIO_IDLE, 0);
goto done;
}
diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c
index 0cb2b22388e9..ac15c23fd5da 100644
--- a/arch/arm/plat-omap/devices.c
+++ b/arch/arm/plat-omap/devices.c
@@ -192,202 +192,48 @@ void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
/*-------------------------------------------------------------------------*/
-#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
-#define OMAP_MMC1_BASE 0x4809c000
-#define OMAP_MMC1_END (OMAP_MMC1_BASE + 0x1fc)
-#define OMAP_MMC1_INT INT_24XX_MMC_IRQ
+#define OMAP_MMC_NR_RES 2
-#define OMAP_MMC2_BASE 0x480b4000
-#define OMAP_MMC2_END (OMAP_MMC2_BASE + 0x1fc)
-#define OMAP_MMC2_INT INT_24XX_MMC2_IRQ
-
-#else
-
-#define OMAP_MMC1_BASE 0xfffb7800
-#define OMAP_MMC1_END (OMAP_MMC1_BASE + 0x7f)
-#define OMAP_MMC1_INT INT_MMC
-
-#define OMAP_MMC2_BASE 0xfffb7c00 /* omap16xx only */
-#define OMAP_MMC2_END (OMAP_MMC2_BASE + 0x7f)
-#define OMAP_MMC2_INT INT_1610_MMC2
-
-#endif
-
-static struct omap_mmc_platform_data mmc1_data;
-
-static u64 mmc1_dmamask = 0xffffffff;
-
-static struct resource mmc1_resources[] = {
- {
- .start = OMAP_MMC1_BASE,
- .end = OMAP_MMC1_END,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = OMAP_MMC1_INT,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device mmc_omap_device1 = {
- .name = "mmci-omap",
- .id = 1,
- .dev = {
- .dma_mask = &mmc1_dmamask,
- .platform_data = &mmc1_data,
- },
- .num_resources = ARRAY_SIZE(mmc1_resources),
- .resource = mmc1_resources,
-};
-
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2430) || \
- defined(CONFIG_ARCH_OMAP34XX)
-
-static struct omap_mmc_platform_data mmc2_data;
-
-static u64 mmc2_dmamask = 0xffffffff;
-
-static struct resource mmc2_resources[] = {
- {
- .start = OMAP_MMC2_BASE,
- .end = OMAP_MMC2_END,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = OMAP_MMC2_INT,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device mmc_omap_device2 = {
- .name = "mmci-omap",
- .id = 2,
- .dev = {
- .dma_mask = &mmc2_dmamask,
- .platform_data = &mmc2_data,
- },
- .num_resources = ARRAY_SIZE(mmc2_resources),
- .resource = mmc2_resources,
-};
-#endif
-
-static inline void omap_init_mmc_conf(const struct omap_mmc_config *mmc_conf)
-{
- if (cpu_is_omap2430() || cpu_is_omap34xx())
- return;
-
- if (mmc_conf->mmc[0].enabled) {
- if (cpu_is_omap24xx()) {
- omap_cfg_reg(H18_24XX_MMC_CMD);
- omap_cfg_reg(H15_24XX_MMC_CLKI);
- omap_cfg_reg(G19_24XX_MMC_CLKO);
- omap_cfg_reg(F20_24XX_MMC_DAT0);
- omap_cfg_reg(F19_24XX_MMC_DAT_DIR0);
- omap_cfg_reg(G18_24XX_MMC_CMD_DIR);
- } else {
- omap_cfg_reg(MMC_CMD);
- omap_cfg_reg(MMC_CLK);
- omap_cfg_reg(MMC_DAT0);
- if (cpu_is_omap1710()) {
- omap_cfg_reg(M15_1710_MMC_CLKI);
- omap_cfg_reg(P19_1710_MMC_CMDDIR);
- omap_cfg_reg(P20_1710_MMC_DATDIR0);
- }
- }
- if (mmc_conf->mmc[0].wire4) {
- if (cpu_is_omap24xx()) {
- omap_cfg_reg(H14_24XX_MMC_DAT1);
- omap_cfg_reg(E19_24XX_MMC_DAT2);
- omap_cfg_reg(D19_24XX_MMC_DAT3);
- omap_cfg_reg(E20_24XX_MMC_DAT_DIR1);
- omap_cfg_reg(F18_24XX_MMC_DAT_DIR2);
- omap_cfg_reg(E18_24XX_MMC_DAT_DIR3);
- } else {
- omap_cfg_reg(MMC_DAT1);
- /* NOTE: DAT2 can be on W10 (here) or M15 */
- if (!mmc_conf->mmc[0].nomux)
- omap_cfg_reg(MMC_DAT2);
- omap_cfg_reg(MMC_DAT3);
- }
- }
- }
-
-#ifdef CONFIG_ARCH_OMAP16XX
- /* block 2 is on newer chips, and has many pinout options */
- if (mmc_conf->mmc[1].enabled) {
- if (!mmc_conf->mmc[1].nomux) {
- omap_cfg_reg(Y8_1610_MMC2_CMD);
- omap_cfg_reg(Y10_1610_MMC2_CLK);
- omap_cfg_reg(R18_1610_MMC2_CLKIN);
- omap_cfg_reg(W8_1610_MMC2_DAT0);
- if (mmc_conf->mmc[1].wire4) {
- omap_cfg_reg(V8_1610_MMC2_DAT1);
- omap_cfg_reg(W15_1610_MMC2_DAT2);
- omap_cfg_reg(R10_1610_MMC2_DAT3);
- }
-
- /* These are needed for the level shifter */
- omap_cfg_reg(V9_1610_MMC2_CMDDIR);
- omap_cfg_reg(V5_1610_MMC2_DATDIR0);
- omap_cfg_reg(W19_1610_MMC2_DATDIR1);
- }
-
- /* Feedback clock must be set on OMAP-1710 MMC2 */
- if (cpu_is_omap1710())
- omap_writel(omap_readl(MOD_CONF_CTRL_1) | (1 << 24),
- MOD_CONF_CTRL_1);
- }
-#endif
-}
-
-static void __init omap_init_mmc(void)
+/*
+ * Register MMC devices. Called from mach-omap1 and mach-omap2 device init.
+ */
+int __init omap_mmc_add(int id, unsigned long base, unsigned long size,
+ unsigned int irq, struct omap_mmc_platform_data *data)
{
- const struct omap_mmc_config *mmc_conf;
-
- /* NOTE: assumes MMC was never (wrongly) enabled */
- mmc_conf = omap_get_config(OMAP_TAG_MMC, struct omap_mmc_config);
- if (!mmc_conf)
- return;
-
- omap_init_mmc_conf(mmc_conf);
-
- if (mmc_conf->mmc[0].enabled) {
- mmc1_data.conf = mmc_conf->mmc[0];
- (void) platform_device_register(&mmc_omap_device1);
- }
-
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2430) || \
- defined(CONFIG_ARCH_OMAP34XX)
- if (mmc_conf->mmc[1].enabled) {
- mmc2_data.conf = mmc_conf->mmc[1];
- (void) platform_device_register(&mmc_omap_device2);
- }
-#endif
-}
+ struct platform_device *pdev;
+ struct resource res[OMAP_MMC_NR_RES];
+ int ret;
+
+ pdev = platform_device_alloc("mmci-omap", id);
+ if (!pdev)
+ return -ENOMEM;
+
+ memset(res, 0, OMAP_MMC_NR_RES * sizeof(struct resource));
+ res[0].start = base;
+ res[0].end = base + size - 1;
+ res[0].flags = IORESOURCE_MEM;
+ res[1].start = res[1].end = irq;
+ res[1].flags = IORESOURCE_IRQ;
+
+ ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
+ if (ret == 0)
+ ret = platform_device_add_data(pdev, data, sizeof(*data));
+ if (ret)
+ goto fail;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto fail;
+ return 0;
-void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info)
-{
- switch (host) {
- case 1:
- mmc1_data = *info;
- break;
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2430) || \
- defined(CONFIG_ARCH_OMAP34XX)
- case 2:
- mmc2_data = *info;
- break;
-#endif
- default:
- BUG();
- }
+fail:
+ platform_device_put(pdev);
+ return ret;
}
-#else
-static inline void omap_init_mmc(void) {}
-void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info) {}
#endif
/*-------------------------------------------------------------------------*/
@@ -532,7 +378,6 @@ static int __init omap_init_devices(void)
*/
omap_init_dsp();
omap_init_kp();
- omap_init_mmc();
omap_init_uwire();
omap_init_wdt();
omap_init_rng();
diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 50f8b4ad9a09..692d2b495af3 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -29,7 +29,7 @@
#include <asm/system.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/tc.h>
@@ -1848,9 +1848,22 @@ static int omap2_dma_handle_ch(int ch)
printk(KERN_INFO
"DMA synchronization event drop occurred with device "
"%d\n", dma_chan[ch].dev_id);
- if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ))
+ if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ)) {
printk(KERN_INFO "DMA transaction error with device %d\n",
dma_chan[ch].dev_id);
+ if (cpu_class_is_omap2()) {
+ /* Errata: sDMA Channel is not disabled
+ * after a transaction error. So we explicitely
+ * disable the channel
+ */
+ u32 ccr;
+
+ ccr = dma_read(CCR(ch));
+ ccr &= ~OMAP_DMA_CCR_EN;
+ dma_write(ccr, CCR(ch));
+ dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
+ }
+ }
if (unlikely(status & OMAP2_DMA_SECURE_ERR_IRQ))
printk(KERN_INFO "DMA secure error with device %d\n",
dma_chan[ch].dev_id);
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 963c31cd1541..e4f0ce04ba92 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -539,10 +539,6 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
- /* REVISIT: hw feature, ttgr overtaking tldr? */
- while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)))
- cpu_relax();
-
omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
}
@@ -553,14 +549,15 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
u32 l;
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
- if (autoreload)
+ if (autoreload) {
l |= OMAP_TIMER_CTRL_AR;
- else
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
+ } else {
l &= ~OMAP_TIMER_CTRL_AR;
+ }
l |= OMAP_TIMER_CTRL_ST;
omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, load);
- omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
}
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c
index 424049d83fbe..07b6968a7d16 100644
--- a/arch/arm/plat-omap/gpio.c
+++ b/arch/arm/plat-omap/gpio.c
@@ -152,6 +152,7 @@ struct gpio_bank {
u32 level_mask;
spinlock_t lock;
struct gpio_chip chip;
+ struct clk *dbck;
};
#define METHOD_MPUIO 0
@@ -244,6 +245,8 @@ static inline struct gpio_bank *get_gpio_bank(int gpio)
return &gpio_bank[gpio >> 5];
if (cpu_is_omap34xx())
return &gpio_bank[gpio >> 5];
+ BUG();
+ return NULL;
}
static inline int get_gpio_index(int gpio)
@@ -332,19 +335,6 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
__raw_writel(l, reg);
}
-void omap_set_gpio_direction(int gpio, int is_input)
-{
- struct gpio_bank *bank;
- unsigned long flags;
-
- if (check_gpio(gpio) < 0)
- return;
- bank = get_gpio_bank(gpio);
- spin_lock_irqsave(&bank->lock, flags);
- _set_gpio_direction(bank, get_gpio_index(gpio), is_input);
- spin_unlock_irqrestore(&bank->lock, flags);
-}
-
static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
{
void __iomem *reg = bank->base;
@@ -406,20 +396,7 @@ static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
__raw_writel(l, reg);
}
-void omap_set_gpio_dataout(int gpio, int enable)
-{
- struct gpio_bank *bank;
- unsigned long flags;
-
- if (check_gpio(gpio) < 0)
- return;
- bank = get_gpio_bank(gpio);
- spin_lock_irqsave(&bank->lock, flags);
- _set_gpio_dataout(bank, get_gpio_index(gpio), enable);
- spin_unlock_irqrestore(&bank->lock, flags);
-}
-
-int omap_get_gpio_datain(int gpio)
+static int __omap_get_gpio_datain(int gpio)
{
struct gpio_bank *bank;
void __iomem *reg;
@@ -473,6 +450,7 @@ void omap_set_gpio_debounce(int gpio, int enable)
{
struct gpio_bank *bank;
void __iomem *reg;
+ unsigned long flags;
u32 val, l = 1 << get_gpio_index(gpio);
if (cpu_class_is_omap1())
@@ -480,16 +458,28 @@ void omap_set_gpio_debounce(int gpio, int enable)
bank = get_gpio_bank(gpio);
reg = bank->base;
-
reg += OMAP24XX_GPIO_DEBOUNCE_EN;
+
+ spin_lock_irqsave(&bank->lock, flags);
val = __raw_readl(reg);
- if (enable)
+ if (enable && !(val & l))
val |= l;
- else
+ else if (!enable && (val & l))
val &= ~l;
+ else
+ goto done;
+
+ if (cpu_is_omap34xx()) {
+ if (enable)
+ clk_enable(bank->dbck);
+ else
+ clk_disable(bank->dbck);
+ }
__raw_writel(val, reg);
+done:
+ spin_unlock_irqrestore(&bank->lock, flags);
}
EXPORT_SYMBOL(omap_set_gpio_debounce);
@@ -906,26 +896,17 @@ static int gpio_wake_enable(unsigned int irq, unsigned int enable)
return retval;
}
-int omap_request_gpio(int gpio)
+static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
{
- struct gpio_bank *bank;
+ struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
unsigned long flags;
- int status;
-
- if (check_gpio(gpio) < 0)
- return -EINVAL;
- status = gpio_request(gpio, NULL);
- if (status < 0)
- return status;
-
- bank = get_gpio_bank(gpio);
spin_lock_irqsave(&bank->lock, flags);
/* Set trigger to none. You need to enable the desired trigger with
* request_irq() or set_irq_type().
*/
- _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE);
+ _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
#ifdef CONFIG_ARCH_OMAP15XX
if (bank->method == METHOD_GPIO_1510) {
@@ -933,7 +914,7 @@ int omap_request_gpio(int gpio)
/* Claim the pin for MPU */
reg = bank->base + OMAP1510_GPIO_PIN_CONTROL;
- __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg);
+ __raw_writel(__raw_readl(reg) | (1 << offset), reg);
}
#endif
spin_unlock_irqrestore(&bank->lock, flags);
@@ -941,39 +922,28 @@ int omap_request_gpio(int gpio)
return 0;
}
-void omap_free_gpio(int gpio)
+static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
{
- struct gpio_bank *bank;
+ struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
unsigned long flags;
- if (check_gpio(gpio) < 0)
- return;
- bank = get_gpio_bank(gpio);
spin_lock_irqsave(&bank->lock, flags);
- if (unlikely(!gpiochip_is_requested(&bank->chip,
- get_gpio_index(gpio)))) {
- spin_unlock_irqrestore(&bank->lock, flags);
- printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio);
- dump_stack();
- return;
- }
#ifdef CONFIG_ARCH_OMAP16XX
if (bank->method == METHOD_GPIO_1610) {
/* Disable wake-up during idle for dynamic tick */
void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
- __raw_writel(1 << get_gpio_index(gpio), reg);
+ __raw_writel(1 << offset, reg);
}
#endif
#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
if (bank->method == METHOD_GPIO_24XX) {
/* Disable wake-up during idle for dynamic tick */
void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
- __raw_writel(1 << get_gpio_index(gpio), reg);
+ __raw_writel(1 << offset, reg);
}
#endif
- _reset_gpio(bank, gpio);
+ _reset_gpio(bank, bank->chip.base + offset);
spin_unlock_irqrestore(&bank->lock, flags);
- gpio_free(gpio);
}
/*
@@ -1252,7 +1222,7 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset)
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
- return omap_get_gpio_datain(chip->base + offset);
+ return __omap_get_gpio_datain(chip->base + offset);
}
static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
@@ -1279,6 +1249,14 @@ static void gpio_set(struct gpio_chip *chip, unsigned offset, int value)
spin_unlock_irqrestore(&bank->lock, flags);
}
+static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_bank *bank;
+
+ bank = container_of(chip, struct gpio_bank, chip);
+ return bank->virtual_irq_start + offset;
+}
+
/*---------------------------------------------------------------------*/
static int initialized;
@@ -1296,7 +1274,6 @@ static struct clk * gpio5_fck;
#endif
#if defined(CONFIG_ARCH_OMAP3)
-static struct clk *gpio_fclks[OMAP34XX_NR_GPIOS];
static struct clk *gpio_iclks[OMAP34XX_NR_GPIOS];
#endif
@@ -1310,9 +1287,7 @@ static int __init _omap_gpio_init(void)
int i;
int gpio = 0;
struct gpio_bank *bank;
-#if defined(CONFIG_ARCH_OMAP3)
char clk_name[11];
-#endif
initialized = 1;
@@ -1367,12 +1342,6 @@ static int __init _omap_gpio_init(void)
printk(KERN_ERR "Could not get %s\n", clk_name);
else
clk_enable(gpio_iclks[i]);
- sprintf(clk_name, "gpio%d_fck", i + 1);
- gpio_fclks[i] = clk_get(NULL, clk_name);
- if (IS_ERR(gpio_fclks[i]))
- printk(KERN_ERR "Could not get %s\n", clk_name);
- else
- clk_enable(gpio_fclks[i]);
}
}
#endif
@@ -1479,10 +1448,13 @@ static int __init _omap_gpio_init(void)
/* REVISIT eventually switch from OMAP-specific gpio structs
* over to the generic ones
*/
+ bank->chip.request = omap_gpio_request;
+ bank->chip.free = omap_gpio_free;
bank->chip.direction_input = gpio_input;
bank->chip.get = gpio_get;
bank->chip.direction_output = gpio_output;
bank->chip.set = gpio_set;
+ bank->chip.to_irq = gpio_2irq;
if (bank_is_mpuio(bank)) {
bank->chip.label = "mpuio";
#ifdef CONFIG_ARCH_OMAP16XX
@@ -1511,6 +1483,13 @@ static int __init _omap_gpio_init(void)
}
set_irq_chained_handler(bank->irq, gpio_irq_handler);
set_irq_data(bank->irq, bank);
+
+ if (cpu_is_omap34xx()) {
+ sprintf(clk_name, "gpio%d_dbck", i + 1);
+ bank->dbck = clk_get(NULL, clk_name);
+ if (IS_ERR(bank->dbck))
+ printk(KERN_ERR "Could not get %s\n", clk_name);
+ }
}
/* Enable system clock for GPIO module.
@@ -1739,12 +1718,6 @@ static int __init omap_gpio_sysinit(void)
return ret;
}
-EXPORT_SYMBOL(omap_request_gpio);
-EXPORT_SYMBOL(omap_free_gpio);
-EXPORT_SYMBOL(omap_set_gpio_direction);
-EXPORT_SYMBOL(omap_set_gpio_dataout);
-EXPORT_SYMBOL(omap_get_gpio_datain);
-
arch_initcall(omap_gpio_sysinit);
@@ -1801,14 +1774,14 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
continue;
irq = bank->virtual_irq_start + j;
- value = omap_get_gpio_datain(gpio);
+ value = gpio_get_value(gpio);
is_in = gpio_is_input(bank, mask);
if (bank_is_mpuio(bank))
seq_printf(s, "MPUIO %2d ", j);
else
seq_printf(s, "GPIO %3d ", gpio);
- seq_printf(s, "(%10s): %s %s",
+ seq_printf(s, "(%-20.20s): %s %s",
label,
is_in ? "in " : "out",
value ? "hi" : "lo");
diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
index 0e6d147ab6f8..89a6ab0b7db8 100644
--- a/arch/arm/plat-omap/i2c.c
+++ b/arch/arm/plat-omap/i2c.c
@@ -79,26 +79,43 @@ static struct platform_device omap_i2c_devices[] = {
#endif
};
-static void __init omap_i2c_mux_pins(int bus_id)
+#if defined(CONFIG_ARCH_OMAP24XX)
+static const int omap24xx_pins[][2] = {
+ { M19_24XX_I2C1_SCL, L15_24XX_I2C1_SDA },
+ { J15_24XX_I2C2_SCL, H19_24XX_I2C2_SDA },
+};
+#else
+static const int omap24xx_pins[][2] = {};
+#endif
+#if defined(CONFIG_ARCH_OMAP34XX)
+static const int omap34xx_pins[][2] = {
+ { K21_34XX_I2C1_SCL, J21_34XX_I2C1_SDA},
+ { AF15_34XX_I2C2_SCL, AE15_34XX_I2C2_SDA},
+ { AF14_34XX_I2C3_SCL, AG14_34XX_I2C3_SDA},
+};
+#else
+static const int omap34xx_pins[][2] = {};
+#endif
+
+static void __init omap_i2c_mux_pins(int bus)
{
- /* TODO: Muxing for OMAP3 */
- switch (bus_id) {
- case 1:
- if (cpu_class_is_omap1()) {
- omap_cfg_reg(I2C_SCL);
- omap_cfg_reg(I2C_SDA);
- } else if (cpu_is_omap24xx()) {
- omap_cfg_reg(M19_24XX_I2C1_SCL);
- omap_cfg_reg(L15_24XX_I2C1_SDA);
- }
- break;
- case 2:
- if (cpu_is_omap24xx()) {
- omap_cfg_reg(J15_24XX_I2C2_SCL);
- omap_cfg_reg(H19_24XX_I2C2_SDA);
- }
- break;
+ int scl, sda;
+
+ if (cpu_class_is_omap1()) {
+ scl = I2C_SCL;
+ sda = I2C_SDA;
+ } else if (cpu_is_omap24xx()) {
+ scl = omap24xx_pins[bus][0];
+ sda = omap24xx_pins[bus][1];
+ } else if (cpu_is_omap34xx()) {
+ scl = omap34xx_pins[bus][0];
+ sda = omap34xx_pins[bus][1];
+ } else {
+ return;
}
+
+ omap_cfg_reg(sda);
+ omap_cfg_reg(scl);
}
int __init omap_register_i2c_bus(int bus_id, u32 clkrate,
@@ -142,6 +159,6 @@ int __init omap_register_i2c_bus(int bus_id, u32 clkrate,
res[1].start = irq;
}
- omap_i2c_mux_pins(bus_id);
+ omap_i2c_mux_pins(bus_id - 1);
return platform_device_register(pdev);
}
diff --git a/arch/arm/plat-omap/include/mach/board-apollon.h b/arch/arm/plat-omap/include/mach/board-apollon.h
index 731c858cf3fe..61bd5e8f09b1 100644
--- a/arch/arm/plat-omap/include/mach/board-apollon.h
+++ b/arch/arm/plat-omap/include/mach/board-apollon.h
@@ -29,12 +29,14 @@
#ifndef __ASM_ARCH_OMAP_APOLLON_H
#define __ASM_ARCH_OMAP_APOLLON_H
+#include <mach/cpu.h>
+
extern void apollon_mmc_init(void);
static inline int apollon_plus(void)
{
/* The apollon plus has IDCODE revision 5 */
- return system_rev & 0xc0;
+ return omap_rev() & 0xc0;
}
/* Placeholder for APOLLON specific defines */
diff --git a/arch/arm/plat-omap/include/mach/board-h2.h b/arch/arm/plat-omap/include/mach/board-h2.h
index 2a050e9be65f..15531c8dc0e6 100644
--- a/arch/arm/plat-omap/include/mach/board-h2.h
+++ b/arch/arm/plat-omap/include/mach/board-h2.h
@@ -29,13 +29,13 @@
#ifndef __ASM_ARCH_OMAP_H2_H
#define __ASM_ARCH_OMAP_H2_H
-/* Placeholder for H2 specific defines */
-
/* At OMAP1610 Innovator the Ethernet is directly connected to CS1 */
#define OMAP1610_ETHR_START 0x04000300
+#define H2_TPS_GPIO_BASE (OMAP_MAX_GPIO_LINES + 16 /* MPUIO */)
+# define H2_TPS_GPIO_MMC_PWR_EN (H2_TPS_GPIO_BASE + 3)
+
extern void h2_mmc_init(void);
-extern void h2_mmc_slot_cover_handler(void *arg, int state);
#endif /* __ASM_ARCH_OMAP_H2_H */
diff --git a/arch/arm/plat-omap/include/mach/board-ldp.h b/arch/arm/plat-omap/include/mach/board-ldp.h
index 66e2746c04ca..f23399665212 100644
--- a/arch/arm/plat-omap/include/mach/board-ldp.h
+++ b/arch/arm/plat-omap/include/mach/board-ldp.h
@@ -32,5 +32,8 @@
extern void twl4030_bci_battery_init(void);
#define TWL4030_IRQNUM INT_34XX_SYS_NIRQ
-
+#define LDP_SMC911X_CS 1
+#define LDP_SMC911X_GPIO 152
+#define DEBUG_BASE 0x08000000
+#define OMAP34XX_ETHR_START DEBUG_BASE
#endif /* __ASM_ARCH_OMAP_LDP_H */
diff --git a/arch/arm/plat-omap/include/mach/board.h b/arch/arm/plat-omap/include/mach/board.h
index c23c12ccb353..9466772fc7c8 100644
--- a/arch/arm/plat-omap/include/mach/board.h
+++ b/arch/arm/plat-omap/include/mach/board.h
@@ -16,7 +16,6 @@
/* Different peripheral ids */
#define OMAP_TAG_CLOCK 0x4f01
-#define OMAP_TAG_MMC 0x4f02
#define OMAP_TAG_SERIAL_CONSOLE 0x4f03
#define OMAP_TAG_USB 0x4f04
#define OMAP_TAG_LCD 0x4f05
@@ -35,27 +34,6 @@ struct omap_clock_config {
u8 system_clock_type;
};
-struct omap_mmc_conf {
- unsigned enabled:1;
- /* nomux means "standard" muxing is wrong on this board, and that
- * board-specific code handled it before common init logic.
- */
- unsigned nomux:1;
- /* switch pin can be for card detect (default) or card cover */
- unsigned cover:1;
- /* 4 wire signaling is optional, and is only used for SD/SDIO */
- unsigned wire4:1;
- /* use the internal clock */
- unsigned internal_clock:1;
- s16 power_pin;
- s16 switch_pin;
- s16 wp_pin;
-};
-
-struct omap_mmc_config {
- struct omap_mmc_conf mmc[2];
-};
-
struct omap_serial_console_config {
u8 console_uart;
u32 console_speed;
diff --git a/arch/arm/plat-omap/include/mach/control.h b/arch/arm/plat-omap/include/mach/control.h
index dc9886760577..269147f3836f 100644
--- a/arch/arm/plat-omap/include/mach/control.h
+++ b/arch/arm/plat-omap/include/mach/control.h
@@ -74,6 +74,7 @@
#define OMAP243X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190)
#define OMAP243X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194)
#define OMAP243X_CONTROL_IVA2_GEMCFG (OMAP2_CONTROL_GENERAL + 0x0198)
+#define OMAP243X_CONTROL_PBIAS_LITE (OMAP2_CONTROL_GENERAL + 0x0230)
/* 24xx-only CONTROL_GENERAL register offsets */
#define OMAP24XX_CONTROL_DEBOBS (OMAP2_CONTROL_GENERAL + 0x0000)
@@ -140,6 +141,7 @@
#define OMAP343X_CONTROL_TEST_KEY_13 (OMAP2_CONTROL_GENERAL + 0x00fc)
#define OMAP343X_CONTROL_IVA2_BOOTADDR (OMAP2_CONTROL_GENERAL + 0x0190)
#define OMAP343X_CONTROL_IVA2_BOOTMOD (OMAP2_CONTROL_GENERAL + 0x0194)
+#define OMAP343X_CONTROL_PBIAS_LITE (OMAP2_CONTROL_GENERAL + 0x02b0)
#define OMAP343X_CONTROL_TEMP_SENSOR (OMAP2_CONTROL_GENERAL + 0x02b4)
/*
@@ -154,11 +156,14 @@
* and the security mode (secure, non-secure, don't care)
*/
/* CONTROL_DEVCONF0 bits */
+#define OMAP2_MMCSDIO1ADPCLKISEL (1 << 24) /* MMC1 loop back clock */
#define OMAP24XX_USBSTANDBYCTRL (1 << 15)
#define OMAP2_MCBSP2_CLKS_MASK (1 << 6)
#define OMAP2_MCBSP1_CLKS_MASK (1 << 2)
/* CONTROL_DEVCONF1 bits */
+#define OMAP243X_MMC1_ACTIVE_OVERWRITE (1 << 31)
+#define OMAP2_MMCSDIO2ADPCLKISEL (1 << 6) /* MMC2 loop back clock */
#define OMAP2_MCBSP5_CLKS_MASK (1 << 4) /* > 242x */
#define OMAP2_MCBSP4_CLKS_MASK (1 << 2) /* > 242x */
#define OMAP2_MCBSP3_CLKS_MASK (1 << 0) /* > 242x */
@@ -172,6 +177,18 @@
#define OMAP2_SYSBOOT_1_MASK (1 << 1)
#define OMAP2_SYSBOOT_0_MASK (1 << 0)
+/* CONTROL_PBIAS_LITE bits */
+#define OMAP343X_PBIASLITESUPPLY_HIGH1 (1 << 15)
+#define OMAP343X_PBIASLITEVMODEERROR1 (1 << 11)
+#define OMAP343X_PBIASSPEEDCTRL1 (1 << 10)
+#define OMAP343X_PBIASLITEPWRDNZ1 (1 << 9)
+#define OMAP343X_PBIASLITEVMODE1 (1 << 8)
+#define OMAP343X_PBIASLITESUPPLY_HIGH0 (1 << 7)
+#define OMAP343X_PBIASLITEVMODEERROR0 (1 << 3)
+#define OMAP2_PBIASSPEEDCTRL0 (1 << 2)
+#define OMAP2_PBIASLITEPWRDNZ0 (1 << 1)
+#define OMAP2_PBIASLITEVMODE0 (1 << 0)
+
#ifndef __ASSEMBLY__
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
extern void __iomem *omap_ctrl_base_get(void);
diff --git a/arch/arm/plat-omap/include/mach/cpu.h b/arch/arm/plat-omap/include/mach/cpu.h
index e0464187209d..b2062f1175de 100644
--- a/arch/arm/plat-omap/include/mach/cpu.h
+++ b/arch/arm/plat-omap/include/mach/cpu.h
@@ -28,13 +28,18 @@
struct omap_chip_id {
u8 oc;
+ u8 type;
};
#define OMAP_CHIP_INIT(x) { .oc = x }
-extern unsigned int system_rev;
-
-#define omap2_cpu_rev() ((system_rev >> 12) & 0x0f)
+/*
+ * omap_rev bits:
+ * CPU id bits (0730, 1510, 1710, 2422...) [31:16]
+ * CPU revision (See _REV_ defined in cpu.h) [15:08]
+ * CPU class bits (15xx, 16xx, 24xx, 34xx...) [07:00]
+ */
+unsigned int omap_rev(void);
/*
* Test if multicore OMAP support is needed
@@ -108,7 +113,7 @@ extern unsigned int system_rev;
* cpu_is_omap243x(): True for OMAP2430
* cpu_is_omap343x(): True for OMAP3430
*/
-#define GET_OMAP_CLASS ((system_rev >> 24) & 0xff)
+#define GET_OMAP_CLASS (omap_rev() & 0xff)
#define IS_OMAP_CLASS(class, id) \
static inline int is_omap ##class (void) \
@@ -116,7 +121,7 @@ static inline int is_omap ##class (void) \
return (GET_OMAP_CLASS == (id)) ? 1 : 0; \
}
-#define GET_OMAP_SUBCLASS ((system_rev >> 20) & 0x0fff)
+#define GET_OMAP_SUBCLASS ((omap_rev() >> 20) & 0x0fff)
#define IS_OMAP_SUBCLASS(subclass, id) \
static inline int is_omap ##subclass (void) \
@@ -226,7 +231,7 @@ IS_OMAP_SUBCLASS(343x, 0x343)
* cpu_is_omap2430(): True for OMAP2430
* cpu_is_omap3430(): True for OMAP3430
*/
-#define GET_OMAP_TYPE ((system_rev >> 16) & 0xffff)
+#define GET_OMAP_TYPE ((omap_rev() >> 16) & 0xffff)
#define IS_OMAP_TYPE(type, id) \
static inline int is_omap ##type (void) \
@@ -320,44 +325,20 @@ IS_OMAP_TYPE(3430, 0x3430)
#define cpu_class_is_omap2() (cpu_is_omap24xx() || cpu_is_omap34xx())
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
-/*
- * Macros to detect silicon revision of OMAP2/3 processors.
- * is_sil_rev_greater_than: true if passed cpu type & its rev is greater.
- * is_sil_rev_lesser_than: true if passed cpu type & its rev is lesser.
- * is_sil_rev_equal_to: true if passed cpu type & its rev is equal.
- * get_sil_rev: return the silicon rev value.
- */
-#define get_sil_omap_type(rev) ((rev & 0xffff0000) >> 16)
-#define get_sil_revision(rev) ((rev & 0x0000f000) >> 12)
-#define is_sil_rev_greater_than(rev) \
- ((get_sil_omap_type(system_rev) == get_sil_omap_type(rev)) && \
- (get_sil_revision(system_rev) > get_sil_revision(rev)))
+/* Various silicon revisions for omap2 */
+#define OMAP242X_CLASS 0x24200024
+#define OMAP2420_REV_ES1_0 0x24200024
+#define OMAP2420_REV_ES2_0 0x24201024
-#define is_sil_rev_less_than(rev) \
- ((get_sil_omap_type(system_rev) == get_sil_omap_type(rev)) && \
- (get_sil_revision(system_rev) < get_sil_revision(rev)))
+#define OMAP243X_CLASS 0x24300024
+#define OMAP2430_REV_ES1_0 0x24300024
-#define is_sil_rev_equal_to(rev) \
- ((get_sil_omap_type(system_rev) == get_sil_omap_type(rev)) && \
- (get_sil_revision(system_rev) == get_sil_revision(rev)))
-
-#define get_sil_rev() \
- get_sil_revision(system_rev)
-
-/* Various silicon macros defined here */
-#define OMAP242X_CLASS 0x24200000
-#define OMAP2420_REV_ES1_0 0x24200000
-#define OMAP2420_REV_ES2_0 0x24201000
-
-#define OMAP243X_CLASS 0x24300000
-#define OMAP2430_REV_ES1_0 0x24300000
-
-#define OMAP343X_CLASS 0x34300000
-#define OMAP3430_REV_ES1_0 0x34300000
-#define OMAP3430_REV_ES2_0 0x34301000
-#define OMAP3430_REV_ES2_1 0x34302000
-#define OMAP3430_REV_ES2_2 0x34303000
+#define OMAP343X_CLASS 0x34300034
+#define OMAP3430_REV_ES1_0 0x34300034
+#define OMAP3430_REV_ES2_0 0x34301034
+#define OMAP3430_REV_ES2_1 0x34302034
+#define OMAP3430_REV_ES3_0 0x34303034
/*
* omap_chip bits
@@ -382,23 +363,16 @@ IS_OMAP_TYPE(3430, 0x3430)
#define CHIP_IS_OMAP24XX (CHIP_IS_OMAP2420 | CHIP_IS_OMAP2430)
int omap_chip_is(struct omap_chip_id oci);
-
+int omap_type(void);
/*
* Macro to detect device type i.e. EMU/HS/TST/GP/BAD
*/
-#define DEVICE_TYPE_TEST 0
-#define DEVICE_TYPE_EMU 1
-#define DEVICE_TYPE_SEC 2
-#define DEVICE_TYPE_GP 3
-#define DEVICE_TYPE_BAD 4
-
-#define get_device_type() ((system_rev & 0x700) >> 8)
-#define is_device_type_test() (get_device_type() == DEVICE_TYPE_TEST)
-#define is_device_type_emu() (get_device_type() == DEVICE_TYPE_EMU)
-#define is_device_type_sec() (get_device_type() == DEVICE_TYPE_SEC)
-#define is_device_type_gp() (get_device_type() == DEVICE_TYPE_GP)
-#define is_device_type_bad() (get_device_type() == DEVICE_TYPE_BAD)
+#define OMAP2_DEVICE_TYPE_TEST 0
+#define OMAP2_DEVICE_TYPE_EMU 1
+#define OMAP2_DEVICE_TYPE_SEC 2
+#define OMAP2_DEVICE_TYPE_GP 3
+#define OMAP2_DEVICE_TYPE_BAD 4
void omap2_check_revision(void);
diff --git a/arch/arm/plat-omap/include/mach/gpio.h b/arch/arm/plat-omap/include/mach/gpio.h
index 98e9008b7e9d..04e68e88f134 100644
--- a/arch/arm/plat-omap/include/mach/gpio.h
+++ b/arch/arm/plat-omap/include/mach/gpio.h
@@ -71,11 +71,6 @@
IH_GPIO_BASE + (nr))
extern int omap_gpio_init(void); /* Call from board init only */
-extern int omap_request_gpio(int gpio);
-extern void omap_free_gpio(int gpio);
-extern void omap_set_gpio_direction(int gpio, int is_input);
-extern void omap_set_gpio_dataout(int gpio, int enable);
-extern int omap_get_gpio_datain(int gpio);
extern void omap2_gpio_prepare_for_retention(void);
extern void omap2_gpio_resume_after_retention(void);
extern void omap_set_gpio_debounce(int gpio, int enable);
@@ -92,6 +87,16 @@ extern void omap_set_gpio_debounce_time(int gpio, int enable);
#include <linux/errno.h>
#include <asm-generic/gpio.h>
+static inline int omap_request_gpio(int gpio)
+{
+ return gpio_request(gpio, "FIXME");
+}
+
+static inline void omap_free_gpio(int gpio)
+{
+ gpio_free(gpio);
+}
+
static inline int gpio_get_value(unsigned gpio)
{
return __gpio_get_value(gpio);
@@ -109,16 +114,24 @@ static inline int gpio_cansleep(unsigned gpio)
static inline int gpio_to_irq(unsigned gpio)
{
- if (gpio < (OMAP_MAX_GPIO_LINES + 16))
- return OMAP_GPIO_IRQ(gpio);
- return -EINVAL;
+ return __gpio_to_irq(gpio);
}
static inline int irq_to_gpio(unsigned irq)
{
+ int tmp;
+
+ /* omap1 SOC mpuio */
if (cpu_class_is_omap1() && (irq < (IH_MPUIO_BASE + 16)))
return (irq - IH_MPUIO_BASE) + OMAP_MAX_GPIO_LINES;
- return irq - IH_GPIO_BASE;
+
+ /* SOC gpio */
+ tmp = irq - IH_GPIO_BASE;
+ if (tmp < OMAP_MAX_GPIO_LINES)
+ return tmp;
+
+ /* we don't supply reverse mappings for non-SOC gpios */
+ return -EIO;
}
#endif
diff --git a/arch/arm/plat-omap/include/mach/io.h b/arch/arm/plat-omap/include/mach/io.h
index adc83b7b8205..d92bf7964481 100644
--- a/arch/arm/plat-omap/include/mach/io.h
+++ b/arch/arm/plat-omap/include/mach/io.h
@@ -42,8 +42,8 @@
* We don't actually have real ISA nor PCI buses, but there is so many
* drivers out there that might just work if we fake them...
*/
-#define __io(a) ((void __iomem *)(PCIO_BASE + (a)))
-#define __mem_pci(a) (a)
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
/*
* ----------------------------------------------------------------------------
@@ -51,8 +51,6 @@
* ----------------------------------------------------------------------------
*/
-#define PCIO_BASE 0
-
#if defined(CONFIG_ARCH_OMAP1)
#define IO_PHYS 0xFFFB0000
diff --git a/arch/arm/plat-omap/include/mach/memory.h b/arch/arm/plat-omap/include/mach/memory.h
index d40cac60b959..211c9f6619e9 100644
--- a/arch/arm/plat-omap/include/mach/memory.h
+++ b/arch/arm/plat-omap/include/mach/memory.h
@@ -43,18 +43,7 @@
#endif
/*
- * Conversion between SDRAM and fake PCI bus, used by USB
- * NOTE: Physical address must be converted to Local Bus address
- * on OMAP-1510 only
- */
-
-/*
* Bus address is physical address, except for OMAP-1510 Local Bus.
- */
-#define __virt_to_bus(x) __virt_to_phys(x)
-#define __bus_to_virt(x) __phys_to_virt(x)
-
-/*
* OMAP-1510 bus address is translated into a Local Bus address if the
* OMAP bus type is lbus. We do the address translation based on the
* device overriding the defaults used in the dma-mapping API.
@@ -74,16 +63,16 @@
#define __arch_page_to_dma(dev, page) ({is_lbus_device(dev) ? \
(dma_addr_t)virt_to_lbus(page_address(page)) : \
- (dma_addr_t)__virt_to_bus(page_address(page));})
+ (dma_addr_t)__virt_to_phys(page_address(page));})
#define __arch_dma_to_virt(dev, addr) ({ (void *) (is_lbus_device(dev) ? \
lbus_to_virt(addr) : \
- __bus_to_virt(addr)); })
+ __phys_to_virt(addr)); })
#define __arch_virt_to_dma(dev, addr) ({ unsigned long __addr = (unsigned long)(addr); \
(dma_addr_t) (is_lbus_device(dev) ? \
virt_to_lbus(__addr) : \
- __virt_to_bus(__addr)); })
+ __virt_to_phys(__addr)); })
#endif /* CONFIG_ARCH_OMAP15XX */
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index fc15d13058fc..031250f02805 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -17,12 +17,28 @@
#include <mach/board.h>
+#define OMAP15XX_NR_MMC 1
+#define OMAP16XX_NR_MMC 2
+#define OMAP1_MMC_SIZE 0x080
+#define OMAP1_MMC1_BASE 0xfffb7800
+#define OMAP1_MMC2_BASE 0xfffb7c00 /* omap16xx only */
+
+#define OMAP24XX_NR_MMC 2
+#define OMAP34XX_NR_MMC 3
+#define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE
+#define HSMMC_SIZE 0x200
+#define OMAP2_MMC1_BASE 0x4809c000
+#define OMAP2_MMC2_BASE 0x480b4000
+#define OMAP3_MMC3_BASE 0x480ad000
+#define HSMMC3 (1 << 2)
+#define HSMMC2 (1 << 1)
+#define HSMMC1 (1 << 0)
+
#define OMAP_MMC_MAX_SLOTS 2
struct omap_mmc_platform_data {
- struct omap_mmc_conf conf;
- /* number of slots on board */
+ /* number of slots per controller */
unsigned nr_slots:2;
/* set if your board has components or wiring that limits the
@@ -41,7 +57,31 @@ struct omap_mmc_platform_data {
int (*suspend)(struct device *dev, int slot);
int (*resume)(struct device *dev, int slot);
+ u64 dma_mask;
+
struct omap_mmc_slot_data {
+
+ /* 4 wire signaling is optional, and is used for SD/SDIO/HSMMC;
+ * 8 wire signaling is also optional, and is used with HSMMC
+ */
+ u8 wires;
+
+ /*
+ * nomux means "standard" muxing is wrong on this board, and
+ * that board-specific code handled it before common init logic.
+ */
+ unsigned nomux:1;
+
+ /* switch pin can be for card detect (default) or card cover */
+ unsigned cover:1;
+
+ /* use the internal clock */
+ unsigned internal_clock:1;
+ s16 power_pin;
+
+ int switch_pin; /* gpio (card detect) */
+ int gpio_wp; /* gpio (write protect) */
+
int (* set_bus_mode)(struct device *dev, int slot, int bus_mode);
int (* set_power)(struct device *dev, int slot, int power_on, int vdd);
int (* get_ro)(struct device *dev, int slot);
@@ -49,8 +89,8 @@ struct omap_mmc_platform_data {
/* return MMC cover switch state, can be NULL if not supported.
*
* possible return values:
- * 0 - open
- * 1 - closed
+ * 0 - closed
+ * 1 - open
*/
int (* get_cover_state)(struct device *dev, int slot);
@@ -66,9 +106,31 @@ struct omap_mmc_platform_data {
} slots[OMAP_MMC_MAX_SLOTS];
};
-extern void omap_set_mmc_info(int host, const struct omap_mmc_platform_data *info);
-
/* called from board-specific card detection service routine */
extern void omap_mmc_notify_cover_event(struct device *dev, int slot, int is_closed);
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
+ defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
+void omap1_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers);
+void omap2_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers);
+int omap_mmc_add(int id, unsigned long base, unsigned long size,
+ unsigned int irq, struct omap_mmc_platform_data *data);
+#else
+static inline void omap1_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers)
+{
+}
+static inline void omap2_init_mmc(struct omap_mmc_platform_data **mmc_data,
+ int nr_controllers)
+{
+}
+static inline int omap_mmc_add(int id, unsigned long base, unsigned long size,
+ unsigned int irq, struct omap_mmc_platform_data *data)
+{
+ return 0;
+}
+
+#endif
#endif
diff --git a/arch/arm/plat-omap/include/mach/mux.h b/arch/arm/plat-omap/include/mach/mux.h
index 6bbf1789bed5..f4362b8682c7 100644
--- a/arch/arm/plat-omap/include/mach/mux.h
+++ b/arch/arm/plat-omap/include/mach/mux.h
@@ -632,6 +632,15 @@ enum omap24xx_index {
AC7_2430_USB0HS_DATA7,
/* 2430 McBSP */
+ AD6_2430_MCBSP_CLKS,
+
+ AB2_2430_MCBSP1_CLKR,
+ AD5_2430_MCBSP1_FSR,
+ AA1_2430_MCBSP1_DX,
+ AF3_2430_MCBSP1_DR,
+ AB3_2430_MCBSP1_FSX,
+ Y9_2430_MCBSP1_CLKX,
+
AC10_2430_MCBSP2_FSX,
AD16_2430_MCBSP2_CLX,
AE13_2430_MCBSP2_DX,
@@ -641,6 +650,30 @@ enum omap24xx_index {
AE13_2430_MCBSP2_DX_OFF,
AD13_2430_MCBSP2_DR_OFF,
+ AC9_2430_MCBSP3_CLKX,
+ AE4_2430_MCBSP3_FSX,
+ AE2_2430_MCBSP3_DR,
+ AF4_2430_MCBSP3_DX,
+
+ N3_2430_MCBSP4_CLKX,
+ AD23_2430_MCBSP4_DR,
+ AB25_2430_MCBSP4_DX,
+ AC25_2430_MCBSP4_FSX,
+
+ AE16_2430_MCBSP5_CLKX,
+ AF12_2430_MCBSP5_FSX,
+ K7_2430_MCBSP5_DX,
+ M1_2430_MCBSP5_DR,
+
+ /* 2430 McSPI*/
+ Y18_2430_MCSPI1_CLK,
+ AD15_2430_MCSPI1_SIMO,
+ AE17_2430_MCSPI1_SOMI,
+ U1_2430_MCSPI1_CS0,
+
+ /* Touchscreen GPIO */
+ AF19_2430_GPIO_85,
+
};
enum omap34xx_index {
@@ -749,6 +782,14 @@ enum omap34xx_index {
AD2_3430_USB3FS_PHY_MM3_TXDAT,
AC1_3430_USB3FS_PHY_MM3_TXEN_N,
+ /* 34xx GPIO
+ * - normally these are bidirectional, no internal pullup/pulldown
+ * - "_UP" suffix (GPIO3_UP) if internal pullup is configured
+ * - "_DOWN" suffix (GPIO3_DOWN) with internal pulldown
+ * - "_OUT" suffix (GPIO3_OUT) for output-only pins (unlike 24xx)
+ */
+ AH8_34XX_GPIO29,
+ J25_34XX_GPIO170,
};
struct omap_mux_cfg {
diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c
index dcd9d16da2e9..be7bcaf2b832 100644
--- a/arch/arm/plat-omap/sram.c
+++ b/arch/arm/plat-omap/sram.c
@@ -24,6 +24,7 @@
#include <mach/sram.h>
#include <mach/board.h>
+#include <mach/cpu.h>
#include <mach/control.h>
@@ -87,7 +88,7 @@ static int is_sram_locked(void)
int type = 0;
if (cpu_is_omap242x())
- type = system_rev & OMAP2_DEVICETYPE_MASK;
+ type = omap_rev() & OMAP2_DEVICETYPE_MASK;
if (type == GP_DEVICE) {
/* RAMFW: R/W access to all initiators for all qualifier sets */
diff --git a/arch/arm/plat-orion/Makefile b/arch/arm/plat-orion/Makefile
index 198f3dde2be3..56021a72e10c 100644
--- a/arch/arm/plat-orion/Makefile
+++ b/arch/arm/plat-orion/Makefile
@@ -6,3 +6,5 @@ obj-y := irq.o pcie.o time.o
obj-m :=
obj-n :=
obj- :=
+
+obj-$(CONFIG_GENERIC_GPIO) += gpio.o
diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
new file mode 100644
index 000000000000..967186425ca1
--- /dev/null
+++ b/arch/arm/plat-orion/gpio.c
@@ -0,0 +1,415 @@
+/*
+ * arch/arm/plat-orion/gpio.c
+ *
+ * Marvell Orion SoC GPIO handling.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <asm/gpio.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+static const char *gpio_label[GPIO_MAX]; /* non null for allocated GPIOs */
+static unsigned long gpio_valid[BITS_TO_LONGS(GPIO_MAX)];
+
+static inline void __set_direction(unsigned pin, int input)
+{
+ u32 u;
+
+ u = readl(GPIO_IO_CONF(pin));
+ if (input)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_IO_CONF(pin));
+}
+
+static void __set_level(unsigned pin, int high)
+{
+ u32 u;
+
+ u = readl(GPIO_OUT(pin));
+ if (high)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_OUT(pin));
+}
+
+
+/*
+ * GENERIC_GPIO primitives.
+ */
+int gpio_direction_input(unsigned pin)
+{
+ unsigned long flags;
+
+ if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
+ pr_debug("%s: invalid GPIO %d\n", __func__, pin);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /*
+ * Some callers might not have used gpio_request(),
+ * so flag this pin as requested now.
+ */
+ if (gpio_label[pin] == NULL)
+ gpio_label[pin] = "?";
+
+ /*
+ * Configure GPIO direction.
+ */
+ __set_direction(pin, 1);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_direction_input);
+
+int gpio_direction_output(unsigned pin, int value)
+{
+ unsigned long flags;
+ u32 u;
+
+ if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
+ pr_debug("%s: invalid GPIO %d\n", __func__, pin);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /*
+ * Some callers might not have used gpio_request(),
+ * so flag this pin as requested now.
+ */
+ if (gpio_label[pin] == NULL)
+ gpio_label[pin] = "?";
+
+ /*
+ * Disable blinking.
+ */
+ u = readl(GPIO_BLINK_EN(pin));
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_BLINK_EN(pin));
+
+ /*
+ * Configure GPIO output value.
+ */
+ __set_level(pin, value);
+
+ /*
+ * Configure GPIO direction.
+ */
+ __set_direction(pin, 0);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_direction_output);
+
+int gpio_get_value(unsigned pin)
+{
+ int val;
+
+ if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)))
+ val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin));
+ else
+ val = readl(GPIO_OUT(pin));
+
+ return (val >> (pin & 31)) & 1;
+}
+EXPORT_SYMBOL(gpio_get_value);
+
+void gpio_set_value(unsigned pin, int value)
+{
+ unsigned long flags;
+ u32 u;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /*
+ * Disable blinking.
+ */
+ u = readl(GPIO_BLINK_EN(pin));
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_BLINK_EN(pin));
+
+ /*
+ * Configure GPIO output value.
+ */
+ __set_level(pin, value);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL(gpio_set_value);
+
+int gpio_request(unsigned pin, const char *label)
+{
+ unsigned long flags;
+ int ret;
+
+ if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
+ pr_debug("%s: invalid GPIO %d\n", __func__, pin);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ if (gpio_label[pin] == NULL) {
+ gpio_label[pin] = label ? label : "?";
+ ret = 0;
+ } else {
+ pr_debug("%s: GPIO %d already used as %s\n",
+ __func__, pin, gpio_label[pin]);
+ ret = -EBUSY;
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(gpio_request);
+
+void gpio_free(unsigned pin)
+{
+ if (pin >= GPIO_MAX || !test_bit(pin, gpio_valid)) {
+ pr_debug("%s: invalid GPIO %d\n", __func__, pin);
+ return;
+ }
+
+ if (gpio_label[pin] == NULL)
+ pr_warning("%s: GPIO %d already freed\n", __func__, pin);
+ else
+ gpio_label[pin] = NULL;
+}
+EXPORT_SYMBOL(gpio_free);
+
+
+/*
+ * Orion-specific GPIO API extensions.
+ */
+void __init orion_gpio_set_unused(unsigned pin)
+{
+ /*
+ * Configure as output, drive low.
+ */
+ __set_level(pin, 0);
+ __set_direction(pin, 0);
+}
+
+void __init orion_gpio_set_valid(unsigned pin, int valid)
+{
+ if (valid)
+ __set_bit(pin, gpio_valid);
+ else
+ __clear_bit(pin, gpio_valid);
+}
+
+void orion_gpio_set_blink(unsigned pin, int blink)
+{
+ unsigned long flags;
+ u32 u;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ /*
+ * Set output value to zero.
+ */
+ __set_level(pin, 0);
+
+ u = readl(GPIO_BLINK_EN(pin));
+ if (blink)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_BLINK_EN(pin));
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL(orion_gpio_set_blink);
+
+
+/*****************************************************************************
+ * Orion GPIO IRQ
+ *
+ * GPIO_IN_POL register controls whether GPIO_DATA_IN will hold the same
+ * value of the line or the opposite value.
+ *
+ * Level IRQ handlers: DATA_IN is used directly as cause register.
+ * Interrupt are masked by LEVEL_MASK registers.
+ * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE.
+ * Interrupt are masked by EDGE_MASK registers.
+ * Both-edge handlers: Similar to regular Edge handlers, but also swaps
+ * the polarity to catch the next line transaction.
+ * This is a race condition that might not perfectly
+ * work on some use cases.
+ *
+ * Every eight GPIO lines are grouped (OR'ed) before going up to main
+ * cause register.
+ *
+ * EDGE cause mask
+ * data-in /--------| |-----| |----\
+ * -----| |----- ---- to main cause reg
+ * X \----------------| |----/
+ * polarity LEVEL mask
+ *
+ ****************************************************************************/
+static void gpio_irq_edge_ack(u32 irq)
+{
+ int pin = irq_to_gpio(irq);
+
+ writel(~(1 << (pin & 31)), GPIO_EDGE_CAUSE(pin));
+}
+
+static void gpio_irq_edge_mask(u32 irq)
+{
+ int pin = irq_to_gpio(irq);
+ u32 u;
+
+ u = readl(GPIO_EDGE_MASK(pin));
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_EDGE_MASK(pin));
+}
+
+static void gpio_irq_edge_unmask(u32 irq)
+{
+ int pin = irq_to_gpio(irq);
+ u32 u;
+
+ u = readl(GPIO_EDGE_MASK(pin));
+ u |= 1 << (pin & 31);
+ writel(u, GPIO_EDGE_MASK(pin));
+}
+
+static void gpio_irq_level_mask(u32 irq)
+{
+ int pin = irq_to_gpio(irq);
+ u32 u;
+
+ u = readl(GPIO_LEVEL_MASK(pin));
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_LEVEL_MASK(pin));
+}
+
+static void gpio_irq_level_unmask(u32 irq)
+{
+ int pin = irq_to_gpio(irq);
+ u32 u;
+
+ u = readl(GPIO_LEVEL_MASK(pin));
+ u |= 1 << (pin & 31);
+ writel(u, GPIO_LEVEL_MASK(pin));
+}
+
+static int gpio_irq_set_type(u32 irq, u32 type)
+{
+ int pin = irq_to_gpio(irq);
+ struct irq_desc *desc;
+ u32 u;
+
+ u = readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31));
+ if (!u) {
+ printk(KERN_ERR "orion gpio_irq_set_type failed "
+ "(irq %d, pin %d).\n", irq, pin);
+ return -EINVAL;
+ }
+
+ desc = irq_desc + irq;
+
+ /*
+ * Set edge/level type.
+ */
+ if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
+ desc->chip = &orion_gpio_irq_edge_chip;
+ } else if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+ desc->chip = &orion_gpio_irq_level_chip;
+ } else {
+ printk(KERN_ERR "failed to set irq=%d (type=%d)\n", irq, type);
+ return -EINVAL;
+ }
+
+ /*
+ * Configure interrupt polarity.
+ */
+ if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH) {
+ u = readl(GPIO_IN_POL(pin));
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_IN_POL(pin));
+ } else if (type == IRQ_TYPE_EDGE_FALLING || type == IRQ_TYPE_LEVEL_LOW) {
+ u = readl(GPIO_IN_POL(pin));
+ u |= 1 << (pin & 31);
+ writel(u, GPIO_IN_POL(pin));
+ } else if (type == IRQ_TYPE_EDGE_BOTH) {
+ u32 v;
+
+ v = readl(GPIO_IN_POL(pin)) ^ readl(GPIO_DATA_IN(pin));
+
+ /*
+ * set initial polarity based on current input level
+ */
+ u = readl(GPIO_IN_POL(pin));
+ if (v & (1 << (pin & 31)))
+ u |= 1 << (pin & 31); /* falling */
+ else
+ u &= ~(1 << (pin & 31)); /* rising */
+ writel(u, GPIO_IN_POL(pin));
+ }
+
+ desc->status = (desc->status & ~IRQ_TYPE_SENSE_MASK) | type;
+
+ return 0;
+}
+
+struct irq_chip orion_gpio_irq_edge_chip = {
+ .name = "orion_gpio_irq_edge",
+ .ack = gpio_irq_edge_ack,
+ .mask = gpio_irq_edge_mask,
+ .unmask = gpio_irq_edge_unmask,
+ .set_type = gpio_irq_set_type,
+};
+
+struct irq_chip orion_gpio_irq_level_chip = {
+ .name = "orion_gpio_irq_level",
+ .mask = gpio_irq_level_mask,
+ .mask_ack = gpio_irq_level_mask,
+ .unmask = gpio_irq_level_unmask,
+ .set_type = gpio_irq_set_type,
+};
+
+void orion_gpio_irq_handler(int pinoff)
+{
+ u32 cause;
+ int pin;
+
+ cause = readl(GPIO_DATA_IN(pinoff)) & readl(GPIO_LEVEL_MASK(pinoff));
+ cause |= readl(GPIO_EDGE_CAUSE(pinoff)) & readl(GPIO_EDGE_MASK(pinoff));
+
+ for (pin = pinoff; pin < pinoff + 8; pin++) {
+ int irq = gpio_to_irq(pin);
+ struct irq_desc *desc = irq_desc + irq;
+
+ if (!(cause & (1 << (pin & 31))))
+ continue;
+
+ if ((desc->status & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
+ /* Swap polarity (race with GPIO line) */
+ u32 polarity;
+
+ polarity = readl(GPIO_IN_POL(pin));
+ polarity ^= 1 << (pin & 31);
+ writel(polarity, GPIO_IN_POL(pin));
+ }
+ desc_handle_irq(irq, desc);
+ }
+}
diff --git a/arch/arm/plat-orion/include/plat/ehci-orion.h b/arch/arm/plat-orion/include/plat/ehci-orion.h
index 64343051095a..4ec668e77460 100644
--- a/arch/arm/plat-orion/include/plat/ehci-orion.h
+++ b/arch/arm/plat-orion/include/plat/ehci-orion.h
@@ -11,8 +11,16 @@
#include <linux/mbus.h>
+enum orion_ehci_phy_ver {
+ EHCI_PHY_ORION,
+ EHCI_PHY_DD,
+ EHCI_PHY_KW,
+ EHCI_PHY_NA,
+};
+
struct orion_ehci_data {
struct mbus_dram_target_info *dram;
+ enum orion_ehci_phy_ver phy_version;
};
diff --git a/arch/arm/plat-orion/include/plat/gpio.h b/arch/arm/plat-orion/include/plat/gpio.h
new file mode 100644
index 000000000000..54deaf274b52
--- /dev/null
+++ b/arch/arm/plat-orion/include/plat/gpio.h
@@ -0,0 +1,39 @@
+/*
+ * arch/arm/plat-orion/include/plat/gpio.h
+ *
+ * Marvell Orion SoC GPIO handling.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_GPIO_H
+#define __PLAT_GPIO_H
+
+/*
+ * GENERIC_GPIO primitives.
+ */
+int gpio_request(unsigned pin, const char *label);
+void gpio_free(unsigned pin);
+int gpio_direction_input(unsigned pin);
+int gpio_direction_output(unsigned pin, int value);
+int gpio_get_value(unsigned pin);
+void gpio_set_value(unsigned pin, int value);
+
+/*
+ * Orion-specific GPIO API extensions.
+ */
+void orion_gpio_set_unused(unsigned pin);
+void orion_gpio_set_valid(unsigned pin, int valid);
+void orion_gpio_set_blink(unsigned pin, int blink);
+
+/*
+ * GPIO interrupt handling.
+ */
+extern struct irq_chip orion_gpio_irq_edge_chip;
+extern struct irq_chip orion_gpio_irq_level_chip;
+void orion_gpio_irq_handler(int irqoff);
+
+
+#endif
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig
index 31656c33e05e..de9383814e5e 100644
--- a/arch/arm/plat-s3c/Kconfig
+++ b/arch/arm/plat-s3c/Kconfig
@@ -6,34 +6,32 @@
config PLAT_S3C
bool
- depends on ARCH_S3C2410
- default y if ARCH_S3C2410
+ depends on ARCH_S3C2410 || ARCH_S3C24A0 || ARCH_S3C64XX
+ default y
select NO_IOPORT
help
Base platform code for any Samsung S3C device
# low-level serial option nodes
+if PLAT_S3C
+
config CPU_LLSERIAL_S3C2410_ONLY
bool
- depends on ARCH_S3C2410
default y if CPU_LLSERIAL_S3C2410 && !CPU_LLSERIAL_S3C2440
config CPU_LLSERIAL_S3C2440_ONLY
bool
- depends on ARCH_S3C2410
default y if CPU_LLSERIAL_S3C2440 && !CPU_LLSERIAL_S3C2410
config CPU_LLSERIAL_S3C2410
bool
- depends on ARCH_S3C2410
help
Selected if there is an S3C2410 (or register compatible) serial
low-level implementation needed
config CPU_LLSERIAL_S3C2440
bool
- depends on ARCH_S3C2410
help
Selected if there is an S3C2440 (or register compatible) serial
low-level implementation needed
@@ -44,7 +42,7 @@ comment "Boot options"
config S3C_BOOT_WATCHDOG
bool "S3C Initialisation watchdog"
- depends on PLAT_S3C && S3C2410_WATCHDOG
+ depends on S3C2410_WATCHDOG
help
Say y to enable the watchdog during the kernel decompression
stage. If the kernel fails to uncompress, then the watchdog
@@ -52,16 +50,22 @@ config S3C_BOOT_WATCHDOG
config S3C_BOOT_ERROR_RESET
bool "S3C Reboot on decompression error"
- depends on PLAT_S3C
help
Say y here to use the watchdog to reset the system if the
kernel decompressor detects an error during decompression.
+config S3C_BOOT_UART_FORCE_FIFO
+ bool "Force UART FIFO on during boot process"
+ default y
+ help
+ Say Y here to force the UART FIFOs on during the kernel
+ uncompressor
+
comment "Power management"
config S3C2410_PM_DEBUG
bool "S3C2410 PM Suspend debug"
- depends on PLAT_S3C && PM
+ depends on PM
help
Say Y here if you want verbose debugging from the PM Suspend and
Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt>
@@ -69,7 +73,7 @@ config S3C2410_PM_DEBUG
config S3C2410_PM_CHECK
bool "S3C2410 PM Suspend Memory CRC"
- depends on PLAT_S3C && PM && CRC32
+ depends on PM && CRC32
help
Enable the PM code's memory area checksum over sleep. This option
will generate CRCs of all blocks of memory, and store them before
@@ -83,7 +87,7 @@ config S3C2410_PM_CHECK
config S3C2410_PM_CHECK_CHUNKSIZE
int "S3C2410 PM Suspend CRC Chunksize (KiB)"
- depends on PLAT_S3C && PM && S3C2410_PM_CHECK
+ depends on PM && S3C2410_PM_CHECK
default 64
help
Set the chunksize in Kilobytes of the CRC for checking memory
@@ -95,10 +99,77 @@ config S3C2410_PM_CHECK_CHUNKSIZE
config S3C_LOWLEVEL_UART_PORT
int "S3C UART to use for low-level messages"
- depends on PLAT_S3C
default 0
help
Choice of which UART port to use for the low-level messages,
such as the `Uncompressing...` at start time. The value of
this configuration should be between zero and two. The port
must have been initialised by the boot-loader before use.
+
+# options for gpiolib support
+
+config S3C_GPIO_SPACE
+ int "Space between gpio banks"
+ default 0
+ help
+ Add a number of spare GPIO entries between each bank for debugging
+ purposes. This allows any problems where an counter overflows from
+ one bank to another to be caught, at the expense of using a little
+ more memory.
+
+config S3C_GPIO_TRACK
+ bool
+ help
+ Internal configuration option to enable the s3c specific gpio
+ chip tracking if the platform requires it.
+
+config S3C_GPIO_PULL_UPDOWN
+ bool
+ help
+ Internal configuration to enable the correct GPIO pull helper
+
+config S3C_GPIO_PULL_DOWN
+ bool
+ help
+ Internal configuration to enable the correct GPIO pull helper
+
+config S3C_GPIO_PULL_UP
+ bool
+ help
+ Internal configuration to enable the correct GPIO pull helper
+
+config S3C_GPIO_CFG_S3C24XX
+ bool
+ help
+ Internal configuration to enable S3C24XX style GPIO configuration
+ functions.
+
+config S3C_GPIO_CFG_S3C64XX
+ bool
+ help
+ Internal configuration to enable S3C64XX style GPIO configuration
+ functions.
+
+# device definitions to compile in
+
+config S3C_DEV_HSMMC
+ bool
+ help
+ Compile in platform device definitions for HSMMC code
+
+config S3C_DEV_HSMMC1
+ bool
+ help
+ Compile in platform device definitions for HSMMC channel 1
+
+config S3C_DEV_I2C1
+ bool
+ help
+ Compile in platform device definitions for I2C channel 1
+
+config S3C_DEV_FB
+ bool
+ help
+ Compile in platform device definition for framebuffer
+
+endif
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile
index f03d7b35ba37..39195f972d5e 100644
--- a/arch/arm/plat-s3c/Makefile
+++ b/arch/arm/plat-s3c/Makefile
@@ -1,3 +1,27 @@
-# dummy makefile, currently just including asm/arm/plat-s3c/include/plat
+# arch/arm/plat-s3c/Makefile
+#
+# Copyright 2008 Simtec Electronics
+#
+# Licensed under GPLv2
-obj-n := dummy.o
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Core support for all Samsung SoCs
+
+obj-y += init.o
+obj-y += time.o
+obj-y += clock.o
+obj-y += pwm-clock.o
+obj-y += gpio.o
+obj-y += gpio-config.o
+
+# devices
+
+obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o
+obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o
+obj-y += dev-i2c0.o
+obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o
+obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o
diff --git a/arch/arm/plat-s3c/clock.c b/arch/arm/plat-s3c/clock.c
new file mode 100644
index 000000000000..b6be76e2fe51
--- /dev/null
+++ b/arch/arm/plat-s3c/clock.c
@@ -0,0 +1,368 @@
+/* linux/arch/arm/plat-s3c24xx/clock.c
+ *
+ * Copyright (c) 2004-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX Core clock control support
+ *
+ * Based on, and code from linux/arch/arm/mach-versatile/clock.c
+ **
+ ** Copyright (C) 2004 ARM Limited.
+ ** Written by Deep Blue Solutions Limited.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <plat/cpu-freq.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+/* clock information */
+
+static LIST_HEAD(clocks);
+
+/* We originally used an mutex here, but some contexts (see resume)
+ * are calling functions such as clk_set_parent() with IRQs disabled
+ * causing an BUG to be triggered.
+ */
+DEFINE_SPINLOCK(clocks_lock);
+
+/* enable and disable calls for use with the clk struct */
+
+static int clk_null_enable(struct clk *clk, int enable)
+{
+ return 0;
+}
+
+/* Clock API calls */
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk *p;
+ struct clk *clk = ERR_PTR(-ENOENT);
+ int idno;
+
+ if (dev == NULL || dev->bus != &platform_bus_type)
+ idno = -1;
+ else
+ idno = to_platform_device(dev)->id;
+
+ spin_lock(&clocks_lock);
+
+ list_for_each_entry(p, &clocks, list) {
+ if (p->id == idno &&
+ strcmp(id, p->name) == 0 &&
+ try_module_get(p->owner)) {
+ clk = p;
+ break;
+ }
+ }
+
+ /* check for the case where a device was supplied, but the
+ * clock that was being searched for is not device specific */
+
+ if (IS_ERR(clk)) {
+ list_for_each_entry(p, &clocks, list) {
+ if (p->id == -1 && strcmp(id, p->name) == 0 &&
+ try_module_get(p->owner)) {
+ clk = p;
+ break;
+ }
+ }
+ }
+
+ spin_unlock(&clocks_lock);
+ return clk;
+}
+
+void clk_put(struct clk *clk)
+{
+ module_put(clk->owner);
+}
+
+int clk_enable(struct clk *clk)
+{
+ if (IS_ERR(clk) || clk == NULL)
+ return -EINVAL;
+
+ clk_enable(clk->parent);
+
+ spin_lock(&clocks_lock);
+
+ if ((clk->usage++) == 0)
+ (clk->enable)(clk, 1);
+
+ spin_unlock(&clocks_lock);
+ return 0;
+}
+
+void clk_disable(struct clk *clk)
+{
+ if (IS_ERR(clk) || clk == NULL)
+ return;
+
+ spin_lock(&clocks_lock);
+
+ if ((--clk->usage) == 0)
+ (clk->enable)(clk, 0);
+
+ spin_unlock(&clocks_lock);
+ clk_disable(clk->parent);
+}
+
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (IS_ERR(clk))
+ return 0;
+
+ if (clk->rate != 0)
+ return clk->rate;
+
+ if (clk->get_rate != NULL)
+ return (clk->get_rate)(clk);
+
+ if (clk->parent != NULL)
+ return clk_get_rate(clk->parent);
+
+ return clk->rate;
+}
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (!IS_ERR(clk) && clk->round_rate)
+ return (clk->round_rate)(clk, rate);
+
+ return rate;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret;
+
+ if (IS_ERR(clk))
+ return -EINVAL;
+
+ /* We do not default just do a clk->rate = rate as
+ * the clock may have been made this way by choice.
+ */
+
+ WARN_ON(clk->set_rate == NULL);
+
+ if (clk->set_rate == NULL)
+ return -EINVAL;
+
+ spin_lock(&clocks_lock);
+ ret = (clk->set_rate)(clk, rate);
+ spin_unlock(&clocks_lock);
+
+ return ret;
+}
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = 0;
+
+ if (IS_ERR(clk))
+ return -EINVAL;
+
+ spin_lock(&clocks_lock);
+
+ if (clk->set_parent)
+ ret = (clk->set_parent)(clk, parent);
+
+ spin_unlock(&clocks_lock);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(clk_get);
+EXPORT_SYMBOL(clk_put);
+EXPORT_SYMBOL(clk_enable);
+EXPORT_SYMBOL(clk_disable);
+EXPORT_SYMBOL(clk_get_rate);
+EXPORT_SYMBOL(clk_round_rate);
+EXPORT_SYMBOL(clk_set_rate);
+EXPORT_SYMBOL(clk_get_parent);
+EXPORT_SYMBOL(clk_set_parent);
+
+/* base clocks */
+
+static int clk_default_setrate(struct clk *clk, unsigned long rate)
+{
+ clk->rate = rate;
+ return 0;
+}
+
+struct clk clk_xtal = {
+ .name = "xtal",
+ .id = -1,
+ .rate = 0,
+ .parent = NULL,
+ .ctrlbit = 0,
+};
+
+struct clk clk_ext = {
+ .name = "ext",
+ .id = -1,
+};
+
+struct clk clk_epll = {
+ .name = "epll",
+ .id = -1,
+};
+
+struct clk clk_mpll = {
+ .name = "mpll",
+ .id = -1,
+ .set_rate = clk_default_setrate,
+};
+
+struct clk clk_upll = {
+ .name = "upll",
+ .id = -1,
+ .parent = NULL,
+ .ctrlbit = 0,
+};
+
+struct clk clk_f = {
+ .name = "fclk",
+ .id = -1,
+ .rate = 0,
+ .parent = &clk_mpll,
+ .ctrlbit = 0,
+ .set_rate = clk_default_setrate,
+};
+
+struct clk clk_h = {
+ .name = "hclk",
+ .id = -1,
+ .rate = 0,
+ .parent = NULL,
+ .ctrlbit = 0,
+ .set_rate = clk_default_setrate,
+};
+
+struct clk clk_p = {
+ .name = "pclk",
+ .id = -1,
+ .rate = 0,
+ .parent = NULL,
+ .ctrlbit = 0,
+ .set_rate = clk_default_setrate,
+};
+
+struct clk clk_usb_bus = {
+ .name = "usb-bus",
+ .id = -1,
+ .rate = 0,
+ .parent = &clk_upll,
+};
+
+
+
+struct clk s3c24xx_uclk = {
+ .name = "uclk",
+ .id = -1,
+};
+
+/* initialise the clock system */
+
+int s3c24xx_register_clock(struct clk *clk)
+{
+ clk->owner = THIS_MODULE;
+
+ if (clk->enable == NULL)
+ clk->enable = clk_null_enable;
+
+ /* add to the list of available clocks */
+
+ /* Quick check to see if this clock has already been registered. */
+ BUG_ON(clk->list.prev != clk->list.next);
+
+ spin_lock(&clocks_lock);
+ list_add(&clk->list, &clocks);
+ spin_unlock(&clocks_lock);
+
+ return 0;
+}
+
+int s3c24xx_register_clocks(struct clk **clks, int nr_clks)
+{
+ int fails = 0;
+
+ for (; nr_clks > 0; nr_clks--, clks++) {
+ if (s3c24xx_register_clock(*clks) < 0)
+ fails++;
+ }
+
+ return fails;
+}
+
+/* initalise all the clocks */
+
+int __init s3c24xx_register_baseclocks(unsigned long xtal)
+{
+ printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");
+
+ clk_xtal.rate = xtal;
+
+ /* register our clocks */
+
+ if (s3c24xx_register_clock(&clk_xtal) < 0)
+ printk(KERN_ERR "failed to register master xtal\n");
+
+ if (s3c24xx_register_clock(&clk_mpll) < 0)
+ printk(KERN_ERR "failed to register mpll clock\n");
+
+ if (s3c24xx_register_clock(&clk_upll) < 0)
+ printk(KERN_ERR "failed to register upll clock\n");
+
+ if (s3c24xx_register_clock(&clk_f) < 0)
+ printk(KERN_ERR "failed to register cpu fclk\n");
+
+ if (s3c24xx_register_clock(&clk_h) < 0)
+ printk(KERN_ERR "failed to register cpu hclk\n");
+
+ if (s3c24xx_register_clock(&clk_p) < 0)
+ printk(KERN_ERR "failed to register cpu pclk\n");
+
+ return 0;
+}
+
diff --git a/arch/arm/plat-s3c/dev-fb.c b/arch/arm/plat-s3c/dev-fb.c
new file mode 100644
index 000000000000..0454b8ec02e2
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-fb.c
@@ -0,0 +1,72 @@
+/* linux/arch/arm/plat-s3c/dev-fb.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series device definition for framebuffer device
+ *
+ * This program is free software; you can 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/string.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+
+#include <mach/map.h>
+#include <mach/regs-fb.h>
+
+#include <plat/fb.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+static struct resource s3c_fb_resource[] = {
+ [0] = {
+ .start = S3C_PA_FB,
+ .end = S3C_PA_FB + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_LCD_VSYNC,
+ .end = IRQ_LCD_VSYNC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_LCD_FIFO,
+ .end = IRQ_LCD_FIFO,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = IRQ_LCD_SYSTEM,
+ .end = IRQ_LCD_SYSTEM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s3c_device_fb = {
+ .name = "s3c-fb",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s3c_fb_resource),
+ .resource = s3c_fb_resource,
+ .dev.dma_mask = &s3c_device_fb.dev.coherent_dma_mask,
+ .dev.coherent_dma_mask = 0xffffffffUL,
+};
+
+void __init s3c_fb_set_platdata(struct s3c_fb_platdata *pd)
+{
+ struct s3c_fb_platdata *npd;
+
+ if (!pd) {
+ printk(KERN_ERR "%s: no platform data\n", __func__);
+ return;
+ }
+
+ npd = kmemdup(pd, sizeof(struct s3c_fb_platdata), GFP_KERNEL);
+ if (!npd)
+ printk(KERN_ERR "%s: no memory for platform data\n", __func__);
+
+ s3c_device_fb.dev.platform_data = npd;
+}
diff --git a/arch/arm/plat-s3c/dev-hsmmc.c b/arch/arm/plat-s3c/dev-hsmmc.c
new file mode 100644
index 000000000000..4c05b39810e2
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-hsmmc.c
@@ -0,0 +1,68 @@
+/* linux/arch/arm/plat-s3c/dev-hsmmc.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series device definition for hsmmc devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+
+#include <mach/map.h>
+#include <plat/sdhci.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+#define S3C_SZ_HSMMC (0x1000)
+
+static struct resource s3c_hsmmc_resource[] = {
+ [0] = {
+ .start = S3C_PA_HSMMC0,
+ .end = S3C_PA_HSMMC0 + S3C_SZ_HSMMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_HSMMC0,
+ .end = IRQ_HSMMC0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 s3c_device_hsmmc_dmamask = 0xffffffffUL;
+
+struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = {
+ .max_width = 4,
+ .host_caps = (MMC_CAP_4_BIT_DATA |
+ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED),
+};
+
+struct platform_device s3c_device_hsmmc0 = {
+ .name = "s3c-sdhci",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c_hsmmc_resource),
+ .resource = s3c_hsmmc_resource,
+ .dev = {
+ .dma_mask = &s3c_device_hsmmc_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &s3c_hsmmc0_def_platdata,
+ },
+};
+
+void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd)
+{
+ struct s3c_sdhci_platdata *set = &s3c_hsmmc0_def_platdata;
+
+ set->max_width = pd->max_width;
+
+ if (pd->cfg_gpio)
+ set->cfg_gpio = pd->cfg_gpio;
+ if (pd->cfg_card)
+ set->cfg_card = pd->cfg_card;
+}
diff --git a/arch/arm/plat-s3c/dev-hsmmc1.c b/arch/arm/plat-s3c/dev-hsmmc1.c
new file mode 100644
index 000000000000..e49bc4cd0ee6
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-hsmmc1.c
@@ -0,0 +1,68 @@
+/* linux/arch/arm/plat-s3c/dev-hsmmc1.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series device definition for hsmmc device 1
+ *
+ * This program is free software; you can 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/platform_device.h>
+#include <linux/mmc/host.h>
+
+#include <mach/map.h>
+#include <plat/sdhci.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+#define S3C_SZ_HSMMC (0x1000)
+
+static struct resource s3c_hsmmc1_resource[] = {
+ [0] = {
+ .start = S3C_PA_HSMMC1,
+ .end = S3C_PA_HSMMC1 + S3C_SZ_HSMMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_HSMMC1,
+ .end = IRQ_HSMMC1,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 s3c_device_hsmmc1_dmamask = 0xffffffffUL;
+
+struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata = {
+ .max_width = 4,
+ .host_caps = (MMC_CAP_4_BIT_DATA |
+ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED),
+};
+
+struct platform_device s3c_device_hsmmc1 = {
+ .name = "s3c-sdhci",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource),
+ .resource = s3c_hsmmc1_resource,
+ .dev = {
+ .dma_mask = &s3c_device_hsmmc1_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &s3c_hsmmc1_def_platdata,
+ },
+};
+
+void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd)
+{
+ struct s3c_sdhci_platdata *set = &s3c_hsmmc1_def_platdata;
+
+ set->max_width = pd->max_width;
+
+ if (pd->cfg_gpio)
+ set->cfg_gpio = pd->cfg_gpio;
+ if (pd->cfg_card)
+ set->cfg_card = pd->cfg_card;
+}
diff --git a/arch/arm/plat-s3c/dev-i2c0.c b/arch/arm/plat-s3c/dev-i2c0.c
new file mode 100644
index 000000000000..2c0128c77c6e
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-i2c0.c
@@ -0,0 +1,71 @@
+/* linux/arch/arm/plat-s3c/dev-i2c0.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series device definition for i2c device 0
+ *
+ * This program is free software; you can 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/string.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+
+#include <plat/regs-iic.h>
+#include <plat/iic.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+static struct resource s3c_i2c_resource[] = {
+ [0] = {
+ .start = S3C_PA_IIC,
+ .end = S3C_PA_IIC + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_IIC,
+ .end = IRQ_IIC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s3c_device_i2c0 = {
+ .name = "s3c2410-i2c",
+#ifdef CONFIG_S3C_DEV_I2C1
+ .id = 0,
+#else
+ .id = -1,
+#endif
+ .num_resources = ARRAY_SIZE(s3c_i2c_resource),
+ .resource = s3c_i2c_resource,
+};
+
+static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
+ .flags = 0,
+ .slave_addr = 0x10,
+ .bus_freq = 100*1000,
+ .max_freq = 400*1000,
+ .sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
+};
+
+void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
+{
+ struct s3c2410_platform_i2c *npd;
+
+ if (!pd)
+ pd = &default_i2c_data0;
+
+ npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
+ if (!npd)
+ printk(KERN_ERR "%s: no memory for platform data\n", __func__);
+ else if (!npd->cfg_gpio)
+ npd->cfg_gpio = s3c_i2c0_cfg_gpio;
+
+ s3c_device_i2c0.dev.platform_data = npd;
+}
diff --git a/arch/arm/plat-s3c/dev-i2c1.c b/arch/arm/plat-s3c/dev-i2c1.c
new file mode 100644
index 000000000000..9658fb0aec95
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-i2c1.c
@@ -0,0 +1,68 @@
+/* linux/arch/arm/plat-s3c/dev-i2c1.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series device definition for i2c device 1
+ *
+ * This program is free software; you can 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/string.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+
+#include <plat/regs-iic.h>
+#include <plat/iic.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+static struct resource s3c_i2c_resource[] = {
+ [0] = {
+ .start = S3C_PA_IIC1,
+ .end = S3C_PA_IIC1 + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_IIC1,
+ .end = IRQ_IIC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s3c_device_i2c1 = {
+ .name = "s3c2410-i2c",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(s3c_i2c_resource),
+ .resource = s3c_i2c_resource,
+};
+
+static struct s3c2410_platform_i2c default_i2c_data1 __initdata = {
+ .flags = 0,
+ .bus_num = 1,
+ .slave_addr = 0x10,
+ .bus_freq = 100*1000,
+ .max_freq = 400*1000,
+ .sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
+};
+
+void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd)
+{
+ struct s3c2410_platform_i2c *npd;
+
+ if (!pd)
+ pd = &default_i2c_data1;
+
+ npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
+ if (!npd)
+ printk(KERN_ERR "%s: no memory for platform data\n", __func__);
+ else if (!npd->cfg_gpio)
+ npd->cfg_gpio = s3c_i2c1_cfg_gpio;
+
+ s3c_device_i2c1.dev.platform_data = npd;
+}
diff --git a/arch/arm/plat-s3c/gpio-config.c b/arch/arm/plat-s3c/gpio-config.c
new file mode 100644
index 000000000000..7642b975a998
--- /dev/null
+++ b/arch/arm/plat-s3c/gpio-config.c
@@ -0,0 +1,163 @@
+/* linux/arch/arm/plat-s3c/gpio-config.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series GPIO configuration core
+ *
+ * This program is free software; you can 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/gpio.h>
+#include <linux/io.h>
+
+#include <mach/gpio-core.h>
+#include <plat/gpio-cfg.h>
+#include <plat/gpio-cfg-helpers.h>
+
+int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
+{
+ struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset;
+ int ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ local_irq_save(flags);
+ ret = s3c_gpio_do_setcfg(chip, offset, config);
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull)
+{
+ struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);
+ unsigned long flags;
+ int offset, ret;
+
+ if (!chip)
+ return -EINVAL;
+
+ offset = pin - chip->chip.base;
+
+ local_irq_save(flags);
+ ret = s3c_gpio_do_setpull(chip, offset, pull);
+ local_irq_restore(flags);
+
+ return ret;
+}
+
+#ifdef CONFIG_S3C_GPIO_CFG_S3C24XX
+int s3c_gpio_setcfg_s3c24xx_banka(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off;
+ u32 con;
+
+ if (s3c_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+
+ /* Map output to 0, and SFN2 to 1 */
+ cfg -= 1;
+ if (cfg > 1)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x1 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+
+int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = off * 2;
+ u32 con;
+
+ if (s3c_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ if (cfg > 3)
+ return -EINVAL;
+
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0x3 << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_S3C_GPIO_CFG_S3C64XX
+int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg)
+{
+ void __iomem *reg = chip->base;
+ unsigned int shift = (off & 7) * 4;
+ u32 con;
+
+ if (off < 8 && chip->chip.ngpio >= 8)
+ reg -= 4;
+
+ if (s3c_gpio_is_cfg_special(cfg)) {
+ cfg &= 0xf;
+ cfg <<= shift;
+ }
+
+ con = __raw_readl(reg);
+ con &= ~(0xf << shift);
+ con |= cfg;
+ __raw_writel(con, reg);
+
+ return 0;
+}
+#endif /* CONFIG_S3C_GPIO_CFG_S3C64XX */
+
+#ifdef CONFIG_S3C_GPIO_PULL_UPDOWN
+int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup;
+
+ pup = __raw_readl(reg);
+ pup &= ~(3 << shift);
+ pup |= pull << shift;
+ __raw_writel(pup, reg);
+
+ return 0;
+}
+
+s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip,
+ unsigned int off)
+{
+ void __iomem *reg = chip->base + 0x08;
+ int shift = off * 2;
+ u32 pup = __raw_readl(reg);
+
+ pup >>= shift;
+ pup &= 0x3;
+ return (__force s3c_gpio_pull_t)pup;
+}
+#endif
diff --git a/arch/arm/plat-s3c/gpio.c b/arch/arm/plat-s3c/gpio.c
new file mode 100644
index 000000000000..d71dd6d9ce5c
--- /dev/null
+++ b/arch/arm/plat-s3c/gpio.c
@@ -0,0 +1,147 @@
+/* linux/arch/arm/plat-s3c/gpio.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series GPIO core
+ *
+ * This program is free software; you can 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/io.h>
+#include <linux/gpio.h>
+
+#include <plat/gpio-core.h>
+
+#ifdef CONFIG_S3C_GPIO_TRACK
+struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];
+
+static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip)
+{
+ unsigned int gpn;
+ int i;
+
+ gpn = chip->chip.base;
+ for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
+ BUG_ON(gpn > ARRAY_SIZE(s3c_gpios));
+ s3c_gpios[gpn] = chip;
+ }
+}
+#endif /* CONFIG_S3C_GPIO_TRACK */
+
+/* Default routines for controlling GPIO, based on the original S3C24XX
+ * GPIO functions which deal with the case where each gpio bank of the
+ * chip is as following:
+ *
+ * base + 0x00: Control register, 2 bits per gpio
+ * gpio n: 2 bits starting at (2*n)
+ * 00 = input, 01 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+*/
+
+static int s3c_gpiolib_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long con;
+
+ local_irq_save(flags);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+
+ __raw_writel(con, base + 0x00);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+static int s3c_gpiolib_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+ unsigned long con;
+
+ local_irq_save(flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ con = __raw_readl(base + 0x00);
+ con &= ~(3 << (offset * 2));
+ con |= 1 << (offset * 2);
+
+ __raw_writel(con, base + 0x00);
+ __raw_writel(dat, base + 0x04);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+static void s3c_gpiolib_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long flags;
+ unsigned long dat;
+
+ local_irq_save(flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= ~(1 << offset);
+ if (value)
+ dat |= 1 << offset;
+ __raw_writel(dat, base + 0x04);
+
+ local_irq_restore(flags);
+}
+
+static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ unsigned long val;
+
+ val = __raw_readl(ourchip->base + 0x04);
+ val >>= offset;
+ val &= 1;
+
+ return val;
+}
+
+__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
+{
+ struct gpio_chip *gc = &chip->chip;
+ int ret;
+
+ BUG_ON(!chip->base);
+ BUG_ON(!gc->label);
+ BUG_ON(!gc->ngpio);
+
+ if (!gc->direction_input)
+ gc->direction_input = s3c_gpiolib_input;
+ if (!gc->direction_output)
+ gc->direction_output = s3c_gpiolib_output;
+ if (!gc->set)
+ gc->set = s3c_gpiolib_set;
+ if (!gc->get)
+ gc->get = s3c_gpiolib_get;
+
+ /* gpiochip_add() prints own failure message on error. */
+ ret = gpiochip_add(gc);
+ if (ret >= 0)
+ s3c_gpiolib_track(chip);
+}
diff --git a/arch/arm/plat-s3c/include/mach/io.h b/arch/arm/plat-s3c/include/mach/io.h
new file mode 100644
index 000000000000..f6a53631b665
--- /dev/null
+++ b/arch/arm/plat-s3c/include/mach/io.h
@@ -0,0 +1,18 @@
+/* arch/arm/plat-s3c/include/mach/io.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben-linux@fluff.org>
+ *
+ * Default IO routines for plat-s3c based systems, such as S3C24A0
+ */
+
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+/* No current ISA/PCI bus support. */
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
+
+#define IO_SPACE_LIMIT (0xFFFFFFFF)
+
+#endif
diff --git a/arch/arm/mach-s3c2410/include/mach/timex.h b/arch/arm/plat-s3c/include/mach/timex.h
index 2a425ed0a7e0..2a425ed0a7e0 100644
--- a/arch/arm/mach-s3c2410/include/mach/timex.h
+++ b/arch/arm/plat-s3c/include/mach/timex.h
diff --git a/arch/arm/plat-s3c/include/mach/vmalloc.h b/arch/arm/plat-s3c/include/mach/vmalloc.h
new file mode 100644
index 000000000000..bfd2ca6e3074
--- /dev/null
+++ b/arch/arm/plat-s3c/include/mach/vmalloc.h
@@ -0,0 +1,20 @@
+/* arch/arm/plat-s3c/include/mach/vmalloc.h
+ *
+ * from arch/arm/mach-iop3xx/include/mach/vmalloc.h
+ *
+ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
+ * http://www.simtec.co.uk/products/SWLINUX/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C2410 vmalloc definition
+*/
+
+#ifndef __ASM_ARCH_VMALLOC_H
+#define __ASM_ARCH_VMALLOC_H
+
+#define VMALLOC_END (0xE0000000)
+
+#endif /* __ASM_ARCH_VMALLOC_H */
diff --git a/arch/arm/plat-s3c/include/plat/adc.h b/arch/arm/plat-s3c/include/plat/adc.h
new file mode 100644
index 000000000000..43df2a404b0b
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/adc.h
@@ -0,0 +1,29 @@
+/* arch/arm/plat-s3c/include/plat/adc.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simnte.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX ADC driver information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_ADC_H
+#define __ASM_PLAT_ADC_H __FILE__
+
+struct s3c_adc_client;
+
+extern int s3c_adc_start(struct s3c_adc_client *client,
+ unsigned int channel, unsigned int nr_samples);
+
+extern struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
+ void (*select)(unsigned selected),
+ void (*conv)(unsigned d0, unsigned d1),
+ unsigned int is_ts);
+
+extern void s3c_adc_release(struct s3c_adc_client *client);
+
+#endif /* __ASM_PLAT_ADC_H */
diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h
new file mode 100644
index 000000000000..a10622eed43a
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/clock.h
@@ -0,0 +1,88 @@
+/* linux/arch/arm/plat-s3c/include/plat/clock.h
+ *
+ * Copyright (c) 2004-2005 Simtec Electronics
+ * http://www.simtec.co.uk/products/SWLINUX/
+ * Written by Ben Dooks, <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/spinlock.h>
+
+struct clk {
+ struct list_head list;
+ struct module *owner;
+ struct clk *parent;
+ const char *name;
+ int id;
+ int usage;
+ unsigned long rate;
+ unsigned long ctrlbit;
+
+ int (*enable)(struct clk *, int enable);
+ int (*set_rate)(struct clk *c, unsigned long rate);
+ unsigned long (*get_rate)(struct clk *c);
+ unsigned long (*round_rate)(struct clk *c, unsigned long rate);
+ int (*set_parent)(struct clk *c, struct clk *parent);
+};
+
+/* other clocks which may be registered by board support */
+
+extern struct clk s3c24xx_dclk0;
+extern struct clk s3c24xx_dclk1;
+extern struct clk s3c24xx_clkout0;
+extern struct clk s3c24xx_clkout1;
+extern struct clk s3c24xx_uclk;
+
+extern struct clk clk_usb_bus;
+
+/* core clock support */
+
+extern struct clk clk_f;
+extern struct clk clk_h;
+extern struct clk clk_p;
+extern struct clk clk_mpll;
+extern struct clk clk_upll;
+extern struct clk clk_epll;
+extern struct clk clk_xtal;
+extern struct clk clk_ext;
+
+/* S3C64XX specific clocks */
+extern struct clk clk_27m;
+extern struct clk clk_48m;
+
+/* exports for arch/arm/mach-s3c2410
+ *
+ * Please DO NOT use these outside of arch/arm/mach-s3c2410
+*/
+
+extern spinlock_t clocks_lock;
+
+extern int s3c2410_clkcon_enable(struct clk *clk, int enable);
+
+extern int s3c24xx_register_clock(struct clk *clk);
+extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks);
+
+extern int s3c24xx_register_baseclocks(unsigned long xtal);
+
+extern void s3c64xx_register_clocks(void);
+
+extern void s3c24xx_setup_clocks(unsigned long fclk,
+ unsigned long hclk,
+ unsigned long pclk);
+
+extern void s3c2410_setup_clocks(void);
+extern void s3c2412_setup_clocks(void);
+extern void s3c244x_setup_clocks(void);
+extern void s3c2443_setup_clocks(void);
+
+/* S3C64XX specific functions and clocks */
+
+extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable);
+
+/* Init for pwm clock code */
+
+extern void s3c_pwmclk_init(void);
+
diff --git a/arch/arm/plat-s3c/include/plat/cpu-freq.h b/arch/arm/plat-s3c/include/plat/cpu-freq.h
new file mode 100644
index 000000000000..c86a13307e90
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/cpu-freq.h
@@ -0,0 +1,94 @@
+/* arch/arm/plat-s3c/include/plat/cpu-freq.h
+ *
+ * Copyright (c) 2006,2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C CPU frequency scaling support - driver and board
+ *
+ * This program is free software; you can 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/cpufreq.h>
+
+struct s3c_cpufreq_info;
+struct s3c_cpufreq_board;
+struct s3c_iotimings;
+
+struct s3c_freq {
+ unsigned long fclk;
+ unsigned long armclk;
+ unsigned long hclk_tns; /* in 10ths of ns */
+ unsigned long hclk;
+ unsigned long pclk;
+};
+
+/* wrapper 'struct cpufreq_freqs' so that any drivers receiving the
+ * notification can use this information that is not provided by just
+ * having the core frequency alone.
+ */
+
+struct s3c_cpufreq_freqs {
+ struct cpufreq_freqs freqs;
+ struct s3c_freq old;
+ struct s3c_freq new;
+};
+
+#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs)
+
+struct s3c_clkdivs {
+ int p_divisor; /* fclk / pclk */
+ int h_divisor; /* fclk / hclk */
+ int arm_divisor; /* not all cpus have this. */
+ unsigned char dvs; /* using dvs mode to arm. */
+};
+
+#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s))
+
+struct s3c_pllval {
+ unsigned long freq;
+ unsigned long pll_reg;
+};
+
+struct s3c_cpufreq_config {
+ struct s3c_freq freq;
+ struct s3c_pllval pll;
+ struct s3c_clkdivs divs;
+ struct s3c_cpufreq_info *info; /* for core, not drivers */
+ struct s3c_cpufreq_board *board;
+};
+
+/* s3c_cpufreq_board
+ *
+ * per-board configuraton information, such as memory refresh and
+ * how to initialise IO timings.
+ */
+struct s3c_cpufreq_board {
+ unsigned int refresh; /* refresh period in ns */
+ unsigned int auto_io:1; /* automatically init io timings. */
+ unsigned int need_io:1; /* set if needs io timing support. */
+
+ /* any non-zero field in here is taken as an upper limit. */
+ struct s3c_freq max; /* frequency limits */
+};
+
+/* Things depending on frequency scaling. */
+#ifdef CONFIG_CPU_FREQ_S3C
+#define __init_or_cpufreq
+#else
+#define __init_or_cpufreq __init
+#endif
+
+/* Board functions */
+
+#ifdef CONFIG_CPU_FREQ_S3C
+extern int s3c_cpufreq_setboard(struct s3c_cpufreq_board *board);
+#else
+
+static inline int s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
+{
+ return 0;
+}
+#endif /* CONFIG_CPU_FREQ_S3C */
diff --git a/arch/arm/plat-s3c/include/plat/cpu.h b/arch/arm/plat-s3c/include/plat/cpu.h
new file mode 100644
index 000000000000..e62ae0fcfe56
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/cpu.h
@@ -0,0 +1,71 @@
+/* linux/arch/arm/plat-s3c/include/plat/cpu.h
+ *
+ * Copyright (c) 2004-2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Header file for S3C24XX CPU support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* todo - fix when rmk changes iodescs to use `void __iomem *` */
+
+#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE }
+
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
+
+#define print_mhz(m) ((m) / MHZ), (((m) / 1000) % 1000)
+
+/* forward declaration */
+struct s3c24xx_uart_resources;
+struct platform_device;
+struct s3c2410_uartcfg;
+struct map_desc;
+
+/* per-cpu initialisation function table. */
+
+struct cpu_table {
+ unsigned long idcode;
+ unsigned long idmask;
+ void (*map_io)(void);
+ void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no);
+ void (*init_clocks)(int xtal);
+ int (*init)(void);
+ const char *name;
+};
+
+extern void s3c_init_cpu(unsigned long idcode,
+ struct cpu_table *cpus, unsigned int cputab_size);
+
+/* core initialisation functions */
+
+extern void s3c24xx_init_irq(void);
+extern void s3c64xx_init_irq(u32 vic0, u32 vic1);
+
+extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
+extern void s3c64xx_init_io(struct map_desc *mach_desc, int size);
+
+extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no);
+
+extern void s3c24xx_init_clocks(int xtal);
+
+extern void s3c24xx_init_uartdevs(char *name,
+ struct s3c24xx_uart_resources *res,
+ struct s3c2410_uartcfg *cfg, int no);
+
+/* timer for 2410/2440 */
+
+struct sys_timer;
+extern struct sys_timer s3c24xx_timer;
+
+/* system device classes */
+
+extern struct sysdev_class s3c2410_sysclass;
+extern struct sysdev_class s3c2412_sysclass;
+extern struct sysdev_class s3c2440_sysclass;
+extern struct sysdev_class s3c2442_sysclass;
+extern struct sysdev_class s3c2443_sysclass;
diff --git a/arch/arm/plat-s3c/include/plat/debug-macro.S b/arch/arm/plat-s3c/include/plat/debug-macro.S
index 4aa7e2e6c001..3634d4e3708b 100644
--- a/arch/arm/plat-s3c/include/plat/debug-macro.S
+++ b/arch/arm/plat-s3c/include/plat/debug-macro.S
@@ -20,7 +20,7 @@
.endm
#ifndef fifo_level
-#define fifo_level fifo_level_s3c2410
+#define fifo_level fifo_level_s3c2440
#endif
.macro fifo_full_s3c2440 rd, rx
diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h
new file mode 100644
index 000000000000..6b1b5231511c
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -0,0 +1,54 @@
+/* linux/include/asm-arm/plat-s3c24xx/devs.h
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Header file for s3c2410 standard platform devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+#include <linux/platform_device.h>
+
+struct s3c24xx_uart_resources {
+ struct resource *resources;
+ unsigned long nr_resources;
+};
+
+extern struct s3c24xx_uart_resources s3c2410_uart_resources[];
+extern struct s3c24xx_uart_resources s3c64xx_uart_resources[];
+
+extern struct platform_device *s3c24xx_uart_devs[];
+extern struct platform_device *s3c24xx_uart_src[];
+
+extern struct platform_device s3c_device_timer[];
+
+extern struct platform_device s3c_device_fb;
+extern struct platform_device s3c_device_usb;
+extern struct platform_device s3c_device_lcd;
+extern struct platform_device s3c_device_wdt;
+extern struct platform_device s3c_device_i2c0;
+extern struct platform_device s3c_device_i2c1;
+extern struct platform_device s3c_device_iis;
+extern struct platform_device s3c_device_rtc;
+extern struct platform_device s3c_device_adc;
+extern struct platform_device s3c_device_sdi;
+extern struct platform_device s3c_device_hsmmc0;
+extern struct platform_device s3c_device_hsmmc1;
+extern struct platform_device s3c_device_hsmmc2;
+
+extern struct platform_device s3c_device_spi0;
+extern struct platform_device s3c_device_spi1;
+
+extern struct platform_device s3c_device_nand;
+
+extern struct platform_device s3c_device_usbgadget;
+
+/* s3c2440 specific devices */
+
+#ifdef CONFIG_CPU_S3C2440
+
+extern struct platform_device s3c_device_camif;
+
+#endif
diff --git a/arch/arm/plat-s3c/include/plat/fb.h b/arch/arm/plat-s3c/include/plat/fb.h
new file mode 100644
index 000000000000..214ff561b0dd
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/fb.h
@@ -0,0 +1,73 @@
+/* linux/arch/arm/plat-s3c/include/plat/fb.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - FB platform data definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_S3C_FB_H
+#define __PLAT_S3C_FB_H __FILE__
+
+/**
+ * struct s3c_fb_pd_win - per window setup data
+ * @win_mode: The display parameters to initialise (not for window 0)
+ * @virtual_x: The virtual X size.
+ * @virtual_y: The virtual Y size.
+ */
+struct s3c_fb_pd_win {
+ struct fb_videomode win_mode;
+
+ unsigned short default_bpp;
+ unsigned short max_bpp;
+ unsigned short virtual_x;
+ unsigned short virtual_y;
+};
+
+/**
+ * struct s3c_fb_platdata - S3C driver platform specific information
+ * @setup_gpio: Setup the external GPIO pins to the right state to transfer
+ * the data from the display system to the connected display
+ * device.
+ * @vidcon0: The base vidcon0 values to control the panel data format.
+ * @vidcon1: The base vidcon1 values to control the panel data output.
+ * @win: The setup data for each hardware window, or NULL for unused.
+ * @display_mode: The LCD output display mode.
+ *
+ * The platform data supplies the video driver with all the information
+ * it requires to work with the display(s) attached to the machine. It
+ * controls the initial mode, the number of display windows (0 is always
+ * the base framebuffer) that are initialised etc.
+ *
+ */
+struct s3c_fb_platdata {
+ void (*setup_gpio)(void);
+
+ struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN];
+
+ u32 vidcon0;
+ u32 vidcon1;
+};
+
+/**
+ * s3c_fb_set_platdata() - Setup the FB device with platform data.
+ * @pd: The platform data to set. The data is copied from the passed structure
+ * so the machine data can mark the data __initdata so that any unused
+ * machines will end up dumping their data at runtime.
+ */
+extern void s3c_fb_set_platdata(struct s3c_fb_platdata *pd);
+
+/**
+ * s3c64xx_fb_gpio_setup_24bpp() - S3C64XX setup function for 24bpp LCD
+ *
+ * Initialise the GPIO for an 24bpp LCD display on the RGB interface.
+ */
+extern void s3c64xx_fb_gpio_setup_24bpp(void);
+
+#endif /* __PLAT_S3C_FB_H */
diff --git a/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h b/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h
new file mode 100644
index 000000000000..652e2bbdaa20
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/gpio-cfg-helpers.h
@@ -0,0 +1,176 @@
+/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg-helper.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - GPIO pin configuration helper definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* This is meant for core cpu support, machine or other driver files
+ * should not be including this header.
+ */
+
+#ifndef __PLAT_GPIO_CFG_HELPERS_H
+#define __PLAT_GPIO_CFG_HELPERS_H __FILE__
+
+/* As a note, all gpio configuration functions are entered exclusively, either
+ * with the relevant lock held or the system prevented from doing anything else
+ * by disabling interrupts.
+*/
+
+static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int config)
+{
+ return (chip->config->set_config)(chip, off, config);
+}
+
+static inline int s3c_gpio_do_setpull(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull)
+{
+ return (chip->config->set_pull)(chip, off, pull);
+}
+
+/**
+ * s3c_gpio_setcfg_s3c24xx - S3C24XX style GPIO configuration.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has two bits of configuration per gpio, which have the following
+ * functions:
+ * 00 = input
+ * 01 = output
+ * 1x = special function
+*/
+extern int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg);
+
+/**
+ * s3c_gpio_setcfg_s3c24xx_a - S3C24XX style GPIO configuration (Bank A)
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register
+ * has one bit of configuration for the gpio, where setting the bit
+ * means the pin is in special function mode and unset means output.
+*/
+extern int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg);
+
+/**
+ * s3c_gpio_setcfg_s3c64xx_4bit - S3C64XX 4bit single register GPIO config.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @cfg: The configuration value to set.
+ *
+ * This helper deal with the GPIO cases where the control register has 4 bits
+ * of control per GPIO, generally in the form of:
+ * 0000 = Input
+ * 0001 = Output
+ * others = Special functions (dependant on bank)
+ *
+ * Note, since the code to deal with the case where there are two control
+ * registers instead of one, we do not have a seperate set of functions for
+ * each case.
+*/
+extern int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip,
+ unsigned int off, unsigned int cfg);
+
+
+/* Pull-{up,down} resistor controls.
+ *
+ * S3C2410,S3C2440,S3C24A0 = Pull-UP,
+ * S3C2412,S3C2413 = Pull-Down
+ * S3C6400,S3C6410 = Pull-Both [None,Down,Up,Undef]
+ * S3C2443 = Pull-Both [not same as S3C6400]
+ */
+
+/**
+ * s3c_gpio_setpull_1up() - Pull configuration for choice of up or none.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @param: pull: The pull mode being requested.
+ *
+ * This is a helper function for the case where we have GPIOs with one
+ * bit configuring the presence of a pull-up resistor.
+ */
+extern int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull);
+
+/**
+ * s3c_gpio_setpull_1down() - Pull configuration for choice of down or none
+ * @chip: The gpio chip that is being configured
+ * @off: The offset for the GPIO being configured
+ * @param: pull: The pull mode being requested
+ *
+ * This is a helper function for the case where we have GPIOs with one
+ * bit configuring the presence of a pull-down resistor.
+ */
+extern int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull);
+
+/**
+ * s3c_gpio_setpull_upown() - Pull configuration for choice of up, down or none
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @param: pull: The pull mode being requested.
+ *
+ * This is a helper function for the case where we have GPIOs with two
+ * bits configuring the presence of a pull resistor, in the following
+ * order:
+ * 00 = No pull resistor connected
+ * 01 = Pull-up resistor connected
+ * 10 = Pull-down resistor connected
+ */
+extern int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull);
+
+
+/**
+ * s3c_gpio_getpull_updown() - Get configuration for choice of up, down or none
+ * @chip: The gpio chip that the GPIO pin belongs to
+ * @off: The offset to the pin to get the configuration of.
+ *
+ * This helper function reads the state of the pull-{up,down} resistor for the
+ * given GPIO in the same case as s3c_gpio_setpull_upown.
+*/
+extern s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip,
+ unsigned int off);
+
+/**
+ * s3c_gpio_setpull_s3c2443() - Pull configuration for s3c2443.
+ * @chip: The gpio chip that is being configured.
+ * @off: The offset for the GPIO being configured.
+ * @param: pull: The pull mode being requested.
+ *
+ * This is a helper function for the case where we have GPIOs with two
+ * bits configuring the presence of a pull resistor, in the following
+ * order:
+ * 00 = Pull-up resistor connected
+ * 10 = Pull-down resistor connected
+ * x1 = No pull up resistor
+ */
+extern int s3c_gpio_setpull_s3c2443(struct s3c_gpio_chip *chip,
+ unsigned int off, s3c_gpio_pull_t pull);
+
+/**
+ * s3c_gpio_getpull_s3c2443() - Get configuration for s3c2443 pull resistors
+ * @chip: The gpio chip that the GPIO pin belongs to.
+ * @off: The offset to the pin to get the configuration of.
+ *
+ * This helper function reads the state of the pull-{up,down} resistor for the
+ * given GPIO in the same case as s3c_gpio_setpull_upown.
+*/
+extern s3c_gpio_pull_t s3c_gpio_getpull_s3c24xx(struct s3c_gpio_chip *chip,
+ unsigned int off);
+
+#endif /* __PLAT_GPIO_CFG_HELPERS_H */
+
diff --git a/arch/arm/plat-s3c/include/plat/gpio-cfg.h b/arch/arm/plat-s3c/include/plat/gpio-cfg.h
new file mode 100644
index 000000000000..29cd6a86cade
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/gpio-cfg.h
@@ -0,0 +1,110 @@
+/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - GPIO pin configuration
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* This file contains the necessary definitions to get the basic gpio
+ * pin configuration done such as setting a pin to input or output or
+ * changing the pull-{up,down} configurations.
+ */
+
+/* Note, this interface is being added to the s3c64xx arch first and will
+ * be added to the s3c24xx systems later.
+ */
+
+#ifndef __PLAT_GPIO_CFG_H
+#define __PLAT_GPIO_CFG_H __FILE__
+
+typedef unsigned int __bitwise__ s3c_gpio_pull_t;
+
+/* forward declaration if gpio-core.h hasn't been included */
+struct s3c_gpio_chip;
+
+/**
+ * struct s3c_gpio_cfg GPIO configuration
+ * @cfg_eint: Configuration setting when used for external interrupt source
+ * @get_pull: Read the current pull configuration for the GPIO
+ * @set_pull: Set the current pull configuraiton for the GPIO
+ * @set_config: Set the current configuration for the GPIO
+ * @get_config: Read the current configuration for the GPIO
+ *
+ * Each chip can have more than one type of GPIO bank available and some
+ * have different capabilites even when they have the same control register
+ * layouts. Provide an point to vector control routine and provide any
+ * per-bank configuration information that other systems such as the
+ * external interrupt code will need.
+ */
+struct s3c_gpio_cfg {
+ unsigned int cfg_eint;
+
+ s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
+ int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
+ s3c_gpio_pull_t pull);
+
+ unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs);
+ int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
+ unsigned config);
+};
+
+#define S3C_GPIO_SPECIAL_MARK (0xfffffff0)
+#define S3C_GPIO_SPECIAL(x) (S3C_GPIO_SPECIAL_MARK | (x))
+
+/* Defines for generic pin configurations */
+#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0))
+#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1))
+#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x))
+
+#define s3c_gpio_is_cfg_special(_cfg) \
+ (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK)
+
+/**
+ * s3c_gpio_cfgpin() - Change the GPIO function of a pin.
+ * @pin pin The pin number to configure.
+ * @pin to The configuration for the pin's function.
+ *
+ * Configure which function is actually connected to the external
+ * pin, such as an gpio input, output or some form of special function
+ * connected to an internal peripheral block.
+ */
+extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);
+
+/* Define values for the pull-{up,down} available for each gpio pin.
+ *
+ * These values control the state of the weak pull-{up,down} resistors
+ * available on most pins on the S3C series. Not all chips support both
+ * up or down settings, and it may be dependant on the chip that is being
+ * used to whether the particular mode is available.
+ */
+#define S3C_GPIO_PULL_NONE ((__force s3c_gpio_pull_t)0x00)
+#define S3C_GPIO_PULL_DOWN ((__force s3c_gpio_pull_t)0x01)
+#define S3C_GPIO_PULL_UP ((__force s3c_gpio_pull_t)0x02)
+
+/**
+ * s3c_gpio_setpull() - set the state of a gpio pin pull resistor
+ * @pin: The pin number to configure the pull resistor.
+ * @pull: The configuration for the pull resistor.
+ *
+ * This function sets the state of the pull-{up,down} resistor for the
+ * specified pin. It will return 0 if successfull, or a negative error
+ * code if the pin cannot support the requested pull setting.
+*/
+extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);
+
+/**
+ * s3c_gpio_getpull() - get the pull resistor state of a gpio pin
+ * @pin: The pin number to get the settings for
+ *
+ * Read the pull resistor value for the specified pin.
+*/
+extern s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin);
+
+#endif /* __PLAT_GPIO_CFG_H */
diff --git a/arch/arm/plat-s3c/include/plat/gpio-core.h b/arch/arm/plat-s3c/include/plat/gpio-core.h
new file mode 100644
index 000000000000..2fc60a580ac8
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/gpio-core.h
@@ -0,0 +1,77 @@
+/* linux/arch/arm/plat-s3c/include/plat/gpio-core.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - GPIO core
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* Define the core gpiolib support functions that the s3c platforms may
+ * need to extend or change depending on the hardware and the s3c chip
+ * selected at build or found at run time.
+ *
+ * These definitions are not intended for driver inclusion, there is
+ * nothing here that should not live outside the platform and core
+ * specific code.
+*/
+
+struct s3c_gpio_cfg;
+
+/**
+ * struct s3c_gpio_chip - wrapper for specific implementation of gpio
+ * @chip: The chip structure to be exported via gpiolib.
+ * @base: The base pointer to the gpio configuration registers.
+ * @config: special function and pull-resistor control information.
+ *
+ * This wrapper provides the necessary information for the Samsung
+ * specific gpios being registered with gpiolib.
+ */
+struct s3c_gpio_chip {
+ struct gpio_chip chip;
+ struct s3c_gpio_cfg *config;
+ void __iomem *base;
+};
+
+static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc)
+{
+ return container_of(gpc, struct s3c_gpio_chip, chip);
+}
+
+/** s3c_gpiolib_add() - add the s3c specific version of a gpio_chip.
+ * @chip: The chip to register
+ *
+ * This is a wrapper to gpiochip_add() that takes our specific gpio chip
+ * information and makes the necessary alterations for the platform and
+ * notes the information for use with the configuration systems and any
+ * other parts of the system.
+ */
+extern void s3c_gpiolib_add(struct s3c_gpio_chip *chip);
+
+/* CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios
+ * for use with the configuration calls, and other parts of the s3c gpiolib
+ * support code.
+ *
+ * Not all s3c support code will need this, as some configurations of cpu
+ * may only support one or two different configuration options and have an
+ * easy gpio to s3c_gpio_chip mapping function. If this is the case, then
+ * the machine support file should provide its own s3c_gpiolib_getchip()
+ * and any other necessary functions.
+ */
+
+#ifdef CONFIG_S3C_GPIO_TRACK
+extern struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];
+
+static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip)
+{
+ return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL;
+}
+#else
+/* machine specific code should provide s3c_gpiolib_getchip */
+
+static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { }
+#endif
diff --git a/arch/arm/plat-s3c/include/plat/iic-core.h b/arch/arm/plat-s3c/include/plat/iic-core.h
new file mode 100644
index 000000000000..36397ca20962
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/iic-core.h
@@ -0,0 +1,35 @@
+/* arch/arm/mach-s3c2410/include/mach/iic-core.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C - I2C Controller core functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_IIC_CORE_H
+#define __ASM_ARCH_IIC_CORE_H __FILE__
+
+/* These functions are only for use with the core support code, such as
+ * the cpu specific initialisation code
+ */
+
+/* re-define device name depending on support. */
+static inline void s3c_i2c0_setname(char *name)
+{
+ /* currently this device is always compiled in */
+ s3c_device_i2c0.name = name;
+}
+
+static inline void s3c_i2c1_setname(char *name)
+{
+#ifdef CONFIG_S3C_DEV_I2C1
+ s3c_device_i2c1.name = name;
+#endif
+}
+
+#endif /* __ASM_ARCH_IIC_H */
diff --git a/arch/arm/plat-s3c/include/plat/iic.h b/arch/arm/plat-s3c/include/plat/iic.h
new file mode 100644
index 000000000000..dc1dfcb9bc6c
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/iic.h
@@ -0,0 +1,57 @@
+/* arch/arm/mach-s3c2410/include/mach/iic.h
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 - I2C Controller platfrom_device info
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_IIC_H
+#define __ASM_ARCH_IIC_H __FILE__
+
+#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */
+
+/* Notes:
+ * 1) All frequencies are expressed in Hz
+ * 2) A value of zero is `do not care`
+*/
+
+struct s3c2410_platform_i2c {
+ int bus_num; /* bus number to use */
+ unsigned int flags;
+ unsigned int slave_addr; /* slave address for controller */
+ unsigned long bus_freq; /* standard bus frequency */
+ unsigned long max_freq; /* max frequency for the bus */
+ unsigned long min_freq; /* min frequency for the bus */
+ unsigned int sda_delay; /* pclks (s3c2440 only) */
+
+ void (*cfg_gpio)(struct platform_device *dev);
+};
+
+/**
+ * s3c_i2c0_set_platdata - set platform data for i2c0 device
+ * @i2c: The platform data to set, or NULL for default data.
+ *
+ * Register the given platform data for use with the i2c0 device. This
+ * call copies the platform data, so the caller can use __initdata for
+ * their copy.
+ *
+ * This call will set cfg_gpio if is null to the default platform
+ * implementation.
+ *
+ * Any user of s3c_device_i2c0 should call this, even if it is with
+ * NULL to ensure that the device is given the default platform data
+ * as the driver will no longer carry defaults.
+ */
+extern void s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *i2c);
+extern void s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *i2c);
+
+/* defined by architecture to configure gpio */
+extern void s3c_i2c0_cfg_gpio(struct platform_device *dev);
+extern void s3c_i2c1_cfg_gpio(struct platform_device *dev);
+
+#endif /* __ASM_ARCH_IIC_H */
diff --git a/arch/arm/plat-s3c/include/plat/map.h b/arch/arm/plat-s3c/include/plat/map-base.h
index b84289d32a54..b84289d32a54 100644
--- a/arch/arm/plat-s3c/include/plat/map.h
+++ b/arch/arm/plat-s3c/include/plat/map-base.h
diff --git a/include/asm-arm/plat-s3c/nand.h b/arch/arm/plat-s3c/include/plat/nand.h
index f4dcd14af059..f4dcd14af059 100644
--- a/include/asm-arm/plat-s3c/nand.h
+++ b/arch/arm/plat-s3c/include/plat/nand.h
diff --git a/include/asm-arm/plat-s3c/regs-ac97.h b/arch/arm/plat-s3c/include/plat/regs-ac97.h
index c3878f7acb83..c3878f7acb83 100644
--- a/include/asm-arm/plat-s3c/regs-ac97.h
+++ b/arch/arm/plat-s3c/include/plat/regs-ac97.h
diff --git a/arch/arm/plat-s3c/include/plat/regs-fb.h b/arch/arm/plat-s3c/include/plat/regs-fb.h
new file mode 100644
index 000000000000..e9ee599d430e
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/regs-fb.h
@@ -0,0 +1,366 @@
+/* arch/arm/plat-s3c/include/plat/regs-fb.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - new-style framebuffer register definitions
+ *
+ * This is the register set for the new style framebuffer interface
+ * found from the S3C2443 onwards into the S3C2416, S3C2450 and the
+ * S3C64XX series such as the S3C6400 and S3C6410.
+ *
+ * The file does not contain the cpu specific items which are based on
+ * whichever architecture is selected, it only contains the core of the
+ * register set. See <mach/regs-fb.h> to get the specifics.
+ *
+ * Note, we changed to using regs-fb.h as it avoids any clashes with
+ * the original regs-lcd.h so out of the way of regs-lcd.h as well as
+ * indicating the newer block is much more than just an LCD interface.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* Please do not include this file directly, use <mach/regs-fb.h> to
+ * ensure all the localised SoC support is included as necessary.
+*/
+
+/* VIDCON0 */
+
+#define VIDCON0 (0x00)
+#define VIDCON0_INTERLACE (1 << 29)
+#define VIDCON0_VIDOUT_MASK (0x3 << 26)
+#define VIDCON0_VIDOUT_SHIFT (26)
+#define VIDCON0_VIDOUT_RGB (0x0 << 26)
+#define VIDCON0_VIDOUT_TV (0x1 << 26)
+#define VIDCON0_VIDOUT_I80_LDI0 (0x2 << 26)
+#define VIDCON0_VIDOUT_I80_LDI1 (0x3 << 26)
+
+#define VIDCON0_L1_DATA_MASK (0x7 << 23)
+#define VIDCON0_L1_DATA_SHIFT (23)
+#define VIDCON0_L1_DATA_16BPP (0x0 << 23)
+#define VIDCON0_L1_DATA_18BPP16 (0x1 << 23)
+#define VIDCON0_L1_DATA_18BPP9 (0x2 << 23)
+#define VIDCON0_L1_DATA_24BPP (0x3 << 23)
+#define VIDCON0_L1_DATA_18BPP (0x4 << 23)
+#define VIDCON0_L1_DATA_16BPP8 (0x5 << 23)
+
+#define VIDCON0_L0_DATA_MASK (0x7 << 20)
+#define VIDCON0_L0_DATA_SHIFT (20)
+#define VIDCON0_L0_DATA_16BPP (0x0 << 20)
+#define VIDCON0_L0_DATA_18BPP16 (0x1 << 20)
+#define VIDCON0_L0_DATA_18BPP9 (0x2 << 20)
+#define VIDCON0_L0_DATA_24BPP (0x3 << 20)
+#define VIDCON0_L0_DATA_18BPP (0x4 << 20)
+#define VIDCON0_L0_DATA_16BPP8 (0x5 << 20)
+
+#define VIDCON0_PNRMODE_MASK (0x3 << 17)
+#define VIDCON0_PNRMODE_SHIFT (17)
+#define VIDCON0_PNRMODE_RGB (0x0 << 17)
+#define VIDCON0_PNRMODE_BGR (0x1 << 17)
+#define VIDCON0_PNRMODE_SERIAL_RGB (0x2 << 17)
+#define VIDCON0_PNRMODE_SERIAL_BGR (0x3 << 17)
+
+#define VIDCON0_CLKVALUP (1 << 16)
+#define VIDCON0_CLKVAL_F_MASK (0xff << 6)
+#define VIDCON0_CLKVAL_F_SHIFT (6)
+#define VIDCON0_CLKVAL_F_LIMIT (0xff)
+#define VIDCON0_CLKVAL_F(_x) ((_x) << 6)
+#define VIDCON0_VLCKFREE (1 << 5)
+#define VIDCON0_CLKDIR (1 << 4)
+
+#define VIDCON0_CLKSEL_MASK (0x3 << 2)
+#define VIDCON0_CLKSEL_SHIFT (2)
+#define VIDCON0_CLKSEL_HCLK (0x0 << 2)
+#define VIDCON0_CLKSEL_LCD (0x1 << 2)
+#define VIDCON0_CLKSEL_27M (0x3 << 2)
+
+#define VIDCON0_ENVID (1 << 1)
+#define VIDCON0_ENVID_F (1 << 0)
+
+#define VIDCON1 (0x04)
+#define VIDCON1_LINECNT_MASK (0x7ff << 16)
+#define VIDCON1_LINECNT_SHIFT (16)
+#define VIDCON1_LINECNT_GET(_v) (((_v) >> 16) & 0x7ff)
+#define VIDCON1_VSTATUS_MASK (0x3 << 13)
+#define VIDCON1_VSTATUS_SHIFT (13)
+#define VIDCON1_VSTATUS_VSYNC (0x0 << 13)
+#define VIDCON1_VSTATUS_BACKPORCH (0x1 << 13)
+#define VIDCON1_VSTATUS_ACTIVE (0x2 << 13)
+#define VIDCON1_VSTATUS_FRONTPORCH (0x0 << 13)
+
+#define VIDCON1_INV_VCLK (1 << 7)
+#define VIDCON1_INV_HSYNC (1 << 6)
+#define VIDCON1_INV_VSYNC (1 << 5)
+#define VIDCON1_INV_VDEN (1 << 4)
+
+/* VIDCON2 */
+
+#define VIDCON2 (0x08)
+#define VIDCON2_EN601 (1 << 23)
+#define VIDCON2_TVFMTSEL_SW (1 << 14)
+
+#define VIDCON2_TVFMTSEL1_MASK (0x3 << 12)
+#define VIDCON2_TVFMTSEL1_SHIFT (12)
+#define VIDCON2_TVFMTSEL1_RGB (0x0 << 12)
+#define VIDCON2_TVFMTSEL1_YUV422 (0x1 << 12)
+#define VIDCON2_TVFMTSEL1_YUV444 (0x2 << 12)
+
+#define VIDCON2_ORGYCbCr (1 << 8)
+#define VIDCON2_YUVORDCrCb (1 << 7)
+
+/* VIDTCON0 */
+
+#define VIDTCON0_VBPDE_MASK (0xff << 24)
+#define VIDTCON0_VBPDE_SHIFT (24)
+#define VIDTCON0_VBPDE_LIMIT (0xff)
+#define VIDTCON0_VBPDE(_x) ((_x) << 24)
+
+#define VIDTCON0_VBPD_MASK (0xff << 16)
+#define VIDTCON0_VBPD_SHIFT (16)
+#define VIDTCON0_VBPD_LIMIT (0xff)
+#define VIDTCON0_VBPD(_x) ((_x) << 16)
+
+#define VIDTCON0_VFPD_MASK (0xff << 8)
+#define VIDTCON0_VFPD_SHIFT (8)
+#define VIDTCON0_VFPD_LIMIT (0xff)
+#define VIDTCON0_VFPD(_x) ((_x) << 8)
+
+#define VIDTCON0_VSPW_MASK (0xff << 0)
+#define VIDTCON0_VSPW_SHIFT (0)
+#define VIDTCON0_VSPW_LIMIT (0xff)
+#define VIDTCON0_VSPW(_x) ((_x) << 0)
+
+/* VIDTCON1 */
+
+#define VIDTCON1_VFPDE_MASK (0xff << 24)
+#define VIDTCON1_VFPDE_SHIFT (24)
+#define VIDTCON1_VFPDE_LIMIT (0xff)
+#define VIDTCON1_VFPDE(_x) ((_x) << 24)
+
+#define VIDTCON1_HBPD_MASK (0xff << 16)
+#define VIDTCON1_HBPD_SHIFT (16)
+#define VIDTCON1_HBPD_LIMIT (0xff)
+#define VIDTCON1_HBPD(_x) ((_x) << 16)
+
+#define VIDTCON1_HFPD_MASK (0xff << 8)
+#define VIDTCON1_HFPD_SHIFT (8)
+#define VIDTCON1_HFPD_LIMIT (0xff)
+#define VIDTCON1_HFPD(_x) ((_x) << 8)
+
+#define VIDTCON1_HSPW_MASK (0xff << 0)
+#define VIDTCON1_HSPW_SHIFT (0)
+#define VIDTCON1_HSPW_LIMIT (0xff)
+#define VIDTCON1_HSPW(_x) ((_x) << 0)
+
+#define VIDTCON2 (0x18)
+#define VIDTCON2_LINEVAL_MASK (0x7ff << 11)
+#define VIDTCON2_LINEVAL_SHIFT (11)
+#define VIDTCON2_LINEVAL_LIMIT (0x7ff)
+#define VIDTCON2_LINEVAL(_x) ((_x) << 11)
+
+#define VIDTCON2_HOZVAL_MASK (0x7ff << 0)
+#define VIDTCON2_HOZVAL_SHIFT (0)
+#define VIDTCON2_HOZVAL_LIMIT (0x7ff)
+#define VIDTCON2_HOZVAL(_x) ((_x) << 0)
+
+/* WINCONx */
+
+
+#define WINCONx_BITSWP (1 << 18)
+#define WINCONx_BYTSWP (1 << 17)
+#define WINCONx_HAWSWP (1 << 16)
+#define WINCONx_BURSTLEN_MASK (0x3 << 9)
+#define WINCONx_BURSTLEN_SHIFT (9)
+#define WINCONx_BURSTLEN_16WORD (0x0 << 9)
+#define WINCONx_BURSTLEN_8WORD (0x1 << 9)
+#define WINCONx_BURSTLEN_4WORD (0x2 << 9)
+
+#define WINCONx_ENWIN (1 << 0)
+#define WINCON0_BPPMODE_MASK (0xf << 2)
+#define WINCON0_BPPMODE_SHIFT (2)
+#define WINCON0_BPPMODE_1BPP (0x0 << 2)
+#define WINCON0_BPPMODE_2BPP (0x1 << 2)
+#define WINCON0_BPPMODE_4BPP (0x2 << 2)
+#define WINCON0_BPPMODE_8BPP_PALETTE (0x3 << 2)
+#define WINCON0_BPPMODE_16BPP_565 (0x5 << 2)
+#define WINCON0_BPPMODE_16BPP_1555 (0x7 << 2)
+#define WINCON0_BPPMODE_18BPP_666 (0x8 << 2)
+#define WINCON0_BPPMODE_24BPP_888 (0xb << 2)
+
+#define WINCON1_BLD_PIX (1 << 6)
+
+#define WINCON1_ALPHA_SEL (1 << 1)
+#define WINCON1_BPPMODE_MASK (0xf << 2)
+#define WINCON1_BPPMODE_SHIFT (2)
+#define WINCON1_BPPMODE_1BPP (0x0 << 2)
+#define WINCON1_BPPMODE_2BPP (0x1 << 2)
+#define WINCON1_BPPMODE_4BPP (0x2 << 2)
+#define WINCON1_BPPMODE_8BPP_PALETTE (0x3 << 2)
+#define WINCON1_BPPMODE_8BPP_1232 (0x4 << 2)
+#define WINCON1_BPPMODE_16BPP_565 (0x5 << 2)
+#define WINCON1_BPPMODE_16BPP_A1555 (0x6 << 2)
+#define WINCON1_BPPMODE_16BPP_I1555 (0x7 << 2)
+#define WINCON1_BPPMODE_18BPP_666 (0x8 << 2)
+#define WINCON1_BPPMODE_18BPP_A1665 (0x9 << 2)
+#define WINCON1_BPPMODE_19BPP_A1666 (0xa << 2)
+#define WINCON1_BPPMODE_24BPP_888 (0xb << 2)
+#define WINCON1_BPPMODE_24BPP_A1887 (0xc << 2)
+#define WINCON1_BPPMODE_25BPP_A1888 (0xd << 2)
+#define WINCON1_BPPMODE_28BPP_A4888 (0xd << 2)
+
+
+#define VIDOSDxA_TOPLEFT_X_MASK (0x7ff << 11)
+#define VIDOSDxA_TOPLEFT_X_SHIFT (11)
+#define VIDOSDxA_TOPLEFT_X_LIMIT (0x7ff)
+#define VIDOSDxA_TOPLEFT_X(_x) ((_x) << 11)
+
+#define VIDOSDxA_TOPLEFT_Y_MASK (0x7ff << 0)
+#define VIDOSDxA_TOPLEFT_Y_SHIFT (0)
+#define VIDOSDxA_TOPLEFT_Y_LIMIT (0x7ff)
+#define VIDOSDxA_TOPLEFT_Y(_x) ((_x) << 0)
+
+#define VIDOSDxB_BOTRIGHT_X_MASK (0x7ff << 11)
+#define VIDOSDxB_BOTRIGHT_X_SHIFT (11)
+#define VIDOSDxB_BOTRIGHT_X_LIMIT (0x7ff)
+#define VIDOSDxB_BOTRIGHT_X(_x) ((_x) << 11)
+
+#define VIDOSDxB_BOTRIGHT_Y_MASK (0x7ff << 0)
+#define VIDOSDxB_BOTRIGHT_Y_SHIFT (0)
+#define VIDOSDxB_BOTRIGHT_Y_LIMIT (0x7ff)
+#define VIDOSDxB_BOTRIGHT_Y(_x) ((_x) << 0)
+
+/* For VIDOSD[1..4]C */
+#define VIDISD14C_ALPHA0_R(_x) ((_x) << 20)
+#define VIDISD14C_ALPHA0_G_MASK (0xf << 16)
+#define VIDISD14C_ALPHA0_G_SHIFT (16)
+#define VIDISD14C_ALPHA0_G_LIMIT (0xf)
+#define VIDISD14C_ALPHA0_G(_x) ((_x) << 16)
+#define VIDISD14C_ALPHA0_B_MASK (0xf << 12)
+#define VIDISD14C_ALPHA0_B_SHIFT (12)
+#define VIDISD14C_ALPHA0_B_LIMIT (0xf)
+#define VIDISD14C_ALPHA0_B(_x) ((_x) << 12)
+#define VIDISD14C_ALPHA1_R_MASK (0xf << 8)
+#define VIDISD14C_ALPHA1_R_SHIFT (8)
+#define VIDISD14C_ALPHA1_R_LIMIT (0xf)
+#define VIDISD14C_ALPHA1_R(_x) ((_x) << 8)
+#define VIDISD14C_ALPHA1_G_MASK (0xf << 4)
+#define VIDISD14C_ALPHA1_G_SHIFT (4)
+#define VIDISD14C_ALPHA1_G_LIMIT (0xf)
+#define VIDISD14C_ALPHA1_G(_x) ((_x) << 4)
+#define VIDISD14C_ALPHA1_B_MASK (0xf << 0)
+#define VIDISD14C_ALPHA1_B_SHIFT (0)
+#define VIDISD14C_ALPHA1_B_LIMIT (0xf)
+#define VIDISD14C_ALPHA1_B(_x) ((_x) << 0)
+
+/* Video buffer addresses */
+#define VIDW_BUF_START(_buff) (0xA0 + ((_buff) * 8))
+#define VIDW_BUF_START1(_buff) (0xA4 + ((_buff) * 8))
+#define VIDW_BUF_END(_buff) (0xD0 + ((_buff) * 8))
+#define VIDW_BUF_END1(_buff) (0xD4 + ((_buff) * 8))
+#define VIDW_BUF_SIZE(_buff) (0x100 + ((_buff) * 4))
+
+#define VIDW_BUF_SIZE_OFFSET_MASK (0x1fff << 13)
+#define VIDW_BUF_SIZE_OFFSET_SHIFT (13)
+#define VIDW_BUF_SIZE_OFFSET_LIMIT (0x1fff)
+#define VIDW_BUF_SIZE_OFFSET(_x) ((_x) << 13)
+
+#define VIDW_BUF_SIZE_PAGEWIDTH_MASK (0x1fff << 0)
+#define VIDW_BUF_SIZE_PAGEWIDTH_SHIFT (0)
+#define VIDW_BUF_SIZE_PAGEWIDTH_LIMIT (0x1fff)
+#define VIDW_BUF_SIZE_PAGEWIDTH(_x) ((_x) << 0)
+
+/* Interrupt controls and status */
+
+#define VIDINTCON0_FIFOINTERVAL_MASK (0x3f << 20)
+#define VIDINTCON0_FIFOINTERVAL_SHIFT (20)
+#define VIDINTCON0_FIFOINTERVAL_LIMIT (0x3f)
+#define VIDINTCON0_FIFOINTERVAL(_x) ((_x) << 20)
+
+#define VIDINTCON0_INT_SYSMAINCON (1 << 19)
+#define VIDINTCON0_INT_SYSSUBCON (1 << 18)
+#define VIDINTCON0_INT_I80IFDONE (1 << 17)
+
+#define VIDINTCON0_FRAMESEL0_MASK (0x3 << 15)
+#define VIDINTCON0_FRAMESEL0_SHIFT (15)
+#define VIDINTCON0_FRAMESEL0_BACKPORCH (0x0 << 15)
+#define VIDINTCON0_FRAMESEL0_VSYNC (0x1 << 15)
+#define VIDINTCON0_FRAMESEL0_ACTIVE (0x2 << 15)
+#define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15)
+
+#define VIDINTCON0_FRAMESEL1 (1 << 14)
+#define VIDINTCON0_FRAMESEL1_NONE (0x0 << 14)
+#define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 14)
+#define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 14)
+#define VIDINTCON0_FRAMESEL1_FRONTPORCH (0x3 << 14)
+
+#define VIDINTCON0_INT_FRAME (1 << 12)
+#define VIDINTCON0_FIFIOSEL_MASK (0x7f << 5)
+#define VIDINTCON0_FIFIOSEL_SHIFT (5)
+#define VIDINTCON0_FIFIOSEL_WINDOW0 (0x1 << 5)
+#define VIDINTCON0_FIFIOSEL_WINDOW1 (0x2 << 5)
+
+#define VIDINTCON0_FIFOLEVEL_MASK (0x7 << 2)
+#define VIDINTCON0_FIFOLEVEL_SHIFT (2)
+#define VIDINTCON0_FIFOLEVEL_TO25PC (0x0 << 2)
+#define VIDINTCON0_FIFOLEVEL_TO50PC (0x1 << 2)
+#define VIDINTCON0_FIFOLEVEL_TO75PC (0x2 << 2)
+#define VIDINTCON0_FIFOLEVEL_EMPTY (0x3 << 2)
+#define VIDINTCON0_FIFOLEVEL_FULL (0x4 << 2)
+
+#define VIDINTCON0_INT_FIFO_MASK (0x3 << 0)
+#define VIDINTCON0_INT_FIFO_SHIFT (0)
+#define VIDINTCON0_INT_ENABLE (1 << 0)
+
+#define VIDINTCON1 (0x134)
+#define VIDINTCON1_INT_I180 (1 << 2)
+#define VIDINTCON1_INT_FRAME (1 << 1)
+#define VIDINTCON1_INT_FIFO (1 << 0)
+
+/* Window colour-key control registers */
+
+#define WxKEYCON0_KEYBL_EN (1 << 26)
+#define WxKEYCON0_KEYEN_F (1 << 25)
+#define WxKEYCON0_DIRCON (1 << 24)
+#define WxKEYCON0_COMPKEY_MASK (0xffffff << 0)
+#define WxKEYCON0_COMPKEY_SHIFT (0)
+#define WxKEYCON0_COMPKEY_LIMIT (0xffffff)
+#define WxKEYCON0_COMPKEY(_x) ((_x) << 0)
+#define WxKEYCON1_COLVAL_MASK (0xffffff << 0)
+#define WxKEYCON1_COLVAL_SHIFT (0)
+#define WxKEYCON1_COLVAL_LIMIT (0xffffff)
+#define WxKEYCON1_COLVAL(_x) ((_x) << 0)
+
+
+/* Window blanking (MAP) */
+
+#define WINxMAP_MAP (1 << 24)
+#define WINxMAP_MAP_COLOUR_MASK (0xffffff << 0)
+#define WINxMAP_MAP_COLOUR_SHIFT (0)
+#define WINxMAP_MAP_COLOUR_LIMIT (0xffffff)
+#define WINxMAP_MAP_COLOUR(_x) ((_x) << 0)
+
+#define WPALCON_PAL_UPDATE (1 << 9)
+#define WPALCON_W1PAL_MASK (0x7 << 3)
+#define WPALCON_W1PAL_SHIFT (3)
+#define WPALCON_W1PAL_25BPP_A888 (0x0 << 3)
+#define WPALCON_W1PAL_24BPP (0x1 << 3)
+#define WPALCON_W1PAL_19BPP_A666 (0x2 << 3)
+#define WPALCON_W1PAL_18BPP_A665 (0x3 << 3)
+#define WPALCON_W1PAL_18BPP (0x4 << 3)
+#define WPALCON_W1PAL_16BPP_A555 (0x5 << 3)
+#define WPALCON_W1PAL_16BPP_565 (0x6 << 3)
+
+#define WPALCON_W0PAL_MASK (0x7 << 0)
+#define WPALCON_W0PAL_SHIFT (0)
+#define WPALCON_W0PAL_25BPP_A888 (0x0 << 0)
+#define WPALCON_W0PAL_24BPP (0x1 << 0)
+#define WPALCON_W0PAL_19BPP_A666 (0x2 << 0)
+#define WPALCON_W0PAL_18BPP_A665 (0x3 << 0)
+#define WPALCON_W0PAL_18BPP (0x4 << 0)
+#define WPALCON_W0PAL_16BPP_A555 (0x5 << 0)
+#define WPALCON_W0PAL_16BPP_565 (0x6 << 0)
+
diff --git a/include/asm-arm/plat-s3c/regs-iic.h b/arch/arm/plat-s3c/include/plat/regs-iic.h
index 2f7c17de8ac8..2f7c17de8ac8 100644
--- a/include/asm-arm/plat-s3c/regs-iic.h
+++ b/arch/arm/plat-s3c/include/plat/regs-iic.h
diff --git a/arch/arm/plat-s3c/include/plat/regs-irqtype.h b/arch/arm/plat-s3c/include/plat/regs-irqtype.h
new file mode 100644
index 000000000000..c63cd3fc5ad3
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/regs-irqtype.h
@@ -0,0 +1,21 @@
+/* arch/arm/plat-s3c/include/plat/regs-irqtype.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C - IRQ detection types.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* values for S3C2410_EXTINT0/1/2 and other cpus in the series, including
+ * the S3C64XX
+*/
+#define S3C2410_EXTINT_LOWLEV (0x00)
+#define S3C2410_EXTINT_HILEV (0x01)
+#define S3C2410_EXTINT_FALLEDGE (0x02)
+#define S3C2410_EXTINT_RISEEDGE (0x04)
+#define S3C2410_EXTINT_BOTHEDGE (0x06)
diff --git a/include/asm-arm/plat-s3c/regs-nand.h b/arch/arm/plat-s3c/include/plat/regs-nand.h
index b2caa4bca270..b2caa4bca270 100644
--- a/include/asm-arm/plat-s3c/regs-nand.h
+++ b/arch/arm/plat-s3c/include/plat/regs-nand.h
diff --git a/include/asm-arm/plat-s3c/regs-rtc.h b/arch/arm/plat-s3c/include/plat/regs-rtc.h
index d5837cf8e402..d5837cf8e402 100644
--- a/include/asm-arm/plat-s3c/regs-rtc.h
+++ b/arch/arm/plat-s3c/include/plat/regs-rtc.h
diff --git a/arch/arm/plat-s3c/include/plat/regs-sdhci.h b/arch/arm/plat-s3c/include/plat/regs-sdhci.h
new file mode 100644
index 000000000000..e34049ad44cc
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/regs-sdhci.h
@@ -0,0 +1,87 @@
+/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - SDHCI (HSMMC) register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_S3C_SDHCI_REGS_H
+#define __PLAT_S3C_SDHCI_REGS_H __FILE__
+
+#define S3C_SDHCI_CONTROL2 (0x80)
+#define S3C_SDHCI_CONTROL3 (0x84)
+#define S3C64XX_SDHCI_CONTROL4 (0x8C)
+
+#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31)
+#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30)
+#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29)
+#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28)
+
+#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24)
+#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24)
+#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24)
+
+#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16)
+#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16)
+#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16)
+
+#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15)
+#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14)
+#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13)
+#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12)
+#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11)
+
+#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9)
+#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9)
+#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9)
+
+#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8)
+#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7)
+#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6)
+#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4)
+#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4)
+#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3)
+#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1)
+#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0)
+
+#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31)
+#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23)
+#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15)
+#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7)
+
+#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24)
+#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24)
+#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24)
+
+#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16)
+#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16)
+#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16)
+
+#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8)
+#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8)
+#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8)
+
+#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0)
+#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0)
+#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0)
+
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16)
+#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16)
+
+#define S3C64XX_SDHCI_CONTROL4_BUSY (1)
+
+#endif /* __PLAT_S3C_SDHCI_REGS_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-serial.h b/arch/arm/plat-s3c/include/plat/regs-serial.h
index a0daa647b92c..487d7d2a7e1d 100644
--- a/arch/arm/plat-s3c/include/plat/regs-serial.h
+++ b/arch/arm/plat-s3c/include/plat/regs-serial.h
@@ -77,6 +77,12 @@
#define S3C2440_UCON_FCLK (3<<10)
#define S3C2443_UCON_EPLL (3<<10)
+#define S3C6400_UCON_CLKMASK (3<<10)
+#define S3C6400_UCON_PCLK (0<<10)
+#define S3C6400_UCON_PCLK2 (2<<10)
+#define S3C6400_UCON_UCLK0 (1<<10)
+#define S3C6400_UCON_UCLK1 (3<<10)
+
#define S3C2440_UCON2_FCLK_EN (1<<15)
#define S3C2440_UCON0_DIVMASK (15 << 12)
#define S3C2440_UCON1_DIVMASK (15 << 12)
@@ -149,6 +155,14 @@
#define S3C2410_UFSTAT_RXMASK (15<<0)
#define S3C2410_UFSTAT_RXSHIFT (0)
+/* UFSTAT S3C24A0 */
+#define S3C24A0_UFSTAT_TXFULL (1 << 14)
+#define S3C24A0_UFSTAT_RXFULL (1 << 6)
+#define S3C24A0_UFSTAT_TXMASK (63 << 8)
+#define S3C24A0_UFSTAT_TXSHIFT (8)
+#define S3C24A0_UFSTAT_RXMASK (63)
+#define S3C24A0_UFSTAT_RXSHIFT (0)
+
/* UFSTAT S3C2443 same as S3C2440 */
#define S3C2440_UFSTAT_TXFULL (1<<14)
#define S3C2440_UFSTAT_RXFULL (1<<6)
@@ -224,7 +238,7 @@ struct s3c2410_uartcfg {
* or platform_add_device() before the console_initcall()
*/
-extern struct platform_device *s3c24xx_uart_devs[3];
+extern struct platform_device *s3c24xx_uart_devs[4];
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm/plat-s3c/include/plat/regs-timer.h b/arch/arm/plat-s3c/include/plat/regs-timer.h
index cc0eedd53e38..d097d92f8cc7 100644
--- a/arch/arm/plat-s3c/include/plat/regs-timer.h
+++ b/arch/arm/plat-s3c/include/plat/regs-timer.h
@@ -10,7 +10,6 @@
* S3C2410 Timer configuration
*/
-
#ifndef __ASM_ARCH_REGS_TIMER_H
#define __ASM_ARCH_REGS_TIMER_H
@@ -21,6 +20,8 @@
#define S3C2410_TCFG1 S3C_TIMERREG(0x04)
#define S3C2410_TCON S3C_TIMERREG(0x08)
+#define S3C64XX_TINT_CSTAT S3C_TIMERREG(0x44)
+
#define S3C2410_TCFG_PRESCALER0_MASK (255<<0)
#define S3C2410_TCFG_PRESCALER1_MASK (255<<8)
#define S3C2410_TCFG_PRESCALER1_SHIFT (8)
@@ -72,6 +73,14 @@
#define S3C2410_TCFG1_MUX_TCLK (4<<0)
#define S3C2410_TCFG1_MUX_MASK (15<<0)
+#define S3C64XX_TCFG1_MUX_DIV1 (0<<0)
+#define S3C64XX_TCFG1_MUX_DIV2 (1<<0)
+#define S3C64XX_TCFG1_MUX_DIV4 (2<<0)
+#define S3C64XX_TCFG1_MUX_DIV8 (3<<0)
+#define S3C64XX_TCFG1_MUX_DIV16 (4<<0)
+#define S3C64XX_TCFG1_MUX_TCLK (5<<0) /* 3 sets of TCLK */
+#define S3C64XX_TCFG1_MUX_MASK (15<<0)
+
#define S3C2410_TCFG1_SHIFT(x) ((x) * 4)
/* for each timer, we have an count buffer, an compare buffer and
diff --git a/include/asm-arm/plat-s3c/regs-watchdog.h b/arch/arm/plat-s3c/include/plat/regs-watchdog.h
index 4938492470f7..4938492470f7 100644
--- a/include/asm-arm/plat-s3c/regs-watchdog.h
+++ b/arch/arm/plat-s3c/include/plat/regs-watchdog.h
diff --git a/arch/arm/plat-s3c/include/plat/sdhci.h b/arch/arm/plat-s3c/include/plat/sdhci.h
new file mode 100644
index 000000000000..c4ca3920ca4b
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/sdhci.h
@@ -0,0 +1,108 @@
+/* linux/arch/arm/plat-s3c/include/plat/sdhci.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Platform - SDHCI (HSMMC) platform data definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_S3C_SDHCI_H
+#define __PLAT_S3C_SDHCI_H __FILE__
+
+struct platform_device;
+struct mmc_host;
+struct mmc_card;
+struct mmc_ios;
+
+/**
+ * struct s3c_sdhci_platdata() - Platform device data for Samsung SDHCI
+ * @max_width: The maximum number of data bits supported.
+ * @host_caps: Standard MMC host capabilities bit field.
+ * @cfg_gpio: Configure the GPIO for a specific card bit-width
+ * @cfg_card: Configure the interface for a specific card and speed. This
+ * is necessary the controllers and/or GPIO blocks require the
+ * changing of driver-strength and other controls dependant on
+ * the card and speed of operation.
+ *
+ * Initialisation data specific to either the machine or the platform
+ * for the device driver to use or call-back when configuring gpio or
+ * card speed information.
+*/
+struct s3c_sdhci_platdata {
+ unsigned int max_width;
+ unsigned int host_caps;
+
+ char **clocks; /* set of clock sources */
+
+ void (*cfg_gpio)(struct platform_device *dev, int width);
+ void (*cfg_card)(struct platform_device *dev,
+ void __iomem *regbase,
+ struct mmc_ios *ios,
+ struct mmc_card *card);
+};
+
+/**
+ * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device.
+ * @pd: Platform data to register to device.
+ *
+ * Register the given platform data for use withe S3C SDHCI device.
+ * The call will copy the platform data, so the board definitions can
+ * make the structure itself __initdata.
+ */
+extern void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd);
+extern void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd);
+
+/* Default platform data, exported so that per-cpu initialisation can
+ * set the correct one when there are more than one cpu type selected.
+*/
+
+extern struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata;
+extern struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata;
+
+/* Helper function availablity */
+
+#ifdef CONFIG_S3C6410_SETUP_SDHCI
+extern char *s3c6410_hsmmc_clksrcs[4];
+
+extern void s3c6410_setup_sdhci0_cfg_gpio(struct platform_device *, int w);
+extern void s3c6410_setup_sdhci1_cfg_gpio(struct platform_device *, int w);
+
+extern void s3c6410_setup_sdhci0_cfg_card(struct platform_device *dev,
+ void __iomem *r,
+ struct mmc_ios *ios,
+ struct mmc_card *card);
+
+#ifdef CONFIG_S3C_DEV_HSMMC
+static inline void s3c6410_default_sdhci0(void)
+{
+ s3c_hsmmc0_def_platdata.clocks = s3c6410_hsmmc_clksrcs;
+ s3c_hsmmc0_def_platdata.cfg_gpio = s3c6410_setup_sdhci0_cfg_gpio;
+ s3c_hsmmc0_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card;
+}
+#else
+static inline void s3c6410_default_sdhci0(void) { }
+#endif /* CONFIG_S3C_DEV_HSMMC */
+
+#ifdef CONFIG_S3C_DEV_HSMMC1
+static inline void s3c6410_default_sdhci1(void)
+{
+ s3c_hsmmc1_def_platdata.clocks = s3c6410_hsmmc_clksrcs;
+ s3c_hsmmc1_def_platdata.cfg_gpio = s3c6410_setup_sdhci1_cfg_gpio;
+ s3c_hsmmc1_def_platdata.cfg_card = s3c6410_setup_sdhci0_cfg_card;
+}
+#else
+static inline void s3c6410_default_sdhci1(void) { }
+#endif /* CONFIG_S3C_DEV_HSMMC1 */
+
+#else
+static inline void s3c6410_default_sdhci0(void) { }
+static inline void s3c6410_default_sdhci1(void) { }
+#endif /* CONFIG_S3C6410_SETUP_SDHCI */
+
+#endif /* __PLAT_S3C_SDHCI_H */
diff --git a/arch/arm/plat-s3c/include/plat/uncompress.h b/arch/arm/plat-s3c/include/plat/uncompress.h
index 4df006b9cc10..6061de87f225 100644
--- a/arch/arm/plat-s3c/include/plat/uncompress.h
+++ b/arch/arm/plat-s3c/include/plat/uncompress.h
@@ -28,7 +28,7 @@ static void arch_detect_cpu(void);
/* defines for UART registers */
#include <plat/regs-serial.h>
-#include <asm/plat-s3c/regs-watchdog.h>
+#include <plat/regs-watchdog.h>
/* working in physical space... */
#undef S3C2410_WDOGREG
@@ -37,7 +37,7 @@ static void arch_detect_cpu(void);
/* how many bytes we allow into the FIFO at a time in FIFO mode */
#define FIFO_MAX (14)
-#define uart_base S3C24XX_PA_UART + (0x4000*CONFIG_S3C_LOWLEVEL_UART_PORT)
+#define uart_base S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT)
static __inline__ void
uart_wr(unsigned int reg, unsigned int val)
@@ -139,6 +139,28 @@ static void arch_decomp_error(const char *x)
static void error(char *err);
+#ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO
+static inline void arch_enable_uart_fifo(void)
+{
+ u32 fifocon = uart_rd(S3C2410_UFCON);
+
+ if (!(fifocon & S3C2410_UFCON_FIFOMODE)) {
+ fifocon |= S3C2410_UFCON_RESETBOTH;
+ uart_wr(S3C2410_UFCON, fifocon);
+
+ /* wait for fifo reset to complete */
+ while (1) {
+ fifocon = uart_rd(S3C2410_UFCON);
+ if (!(fifocon & S3C2410_UFCON_RESETBOTH))
+ break;
+ }
+ }
+}
+#else
+#define arch_enable_uart_fifo() do { } while(0)
+#endif
+
+
static void
arch_decomp_setup(void)
{
@@ -149,6 +171,12 @@ arch_decomp_setup(void)
arch_detect_cpu();
arch_decomp_wdog_start();
+
+ /* Enable the UART FIFOs if they where not enabled and our
+ * configuration says we should turn them on.
+ */
+
+ arch_enable_uart_fifo();
}
diff --git a/arch/arm/plat-s3c/init.c b/arch/arm/plat-s3c/init.c
new file mode 100644
index 000000000000..6790edfaca6f
--- /dev/null
+++ b/arch/arm/plat-s3c/init.c
@@ -0,0 +1,160 @@
+/* linux/arch/arm/plat-s3c/init.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C series CPU initialisation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+#include <plat/regs-serial.h>
+
+static struct cpu_table *cpu;
+
+static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
+ struct cpu_table *tab,
+ unsigned int count)
+{
+ for (; count != 0; count--, tab++) {
+ if ((idcode & tab->idmask) == tab->idcode)
+ return tab;
+ }
+
+ return NULL;
+}
+
+void __init s3c_init_cpu(unsigned long idcode,
+ struct cpu_table *cputab, unsigned int cputab_size)
+{
+ cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);
+
+ if (cpu == NULL) {
+ printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
+ panic("Unknown S3C24XX CPU");
+ }
+
+ printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
+
+ if (cpu->map_io == NULL || cpu->init == NULL) {
+ printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
+ panic("Unsupported Samsung CPU");
+ }
+
+ cpu->map_io();
+}
+
+/* s3c24xx_init_clocks
+ *
+ * Initialise the clock subsystem and associated information from the
+ * given master crystal value.
+ *
+ * xtal = 0 -> use default PLL crystal value (normally 12MHz)
+ * != 0 -> PLL crystal value in Hz
+*/
+
+void __init s3c24xx_init_clocks(int xtal)
+{
+ if (xtal == 0)
+ xtal = 12*1000*1000;
+
+ if (cpu == NULL)
+ panic("s3c24xx_init_clocks: no cpu setup?\n");
+
+ if (cpu->init_clocks == NULL)
+ panic("s3c24xx_init_clocks: cpu has no clock init\n");
+ else
+ (cpu->init_clocks)(xtal);
+}
+
+/* uart management */
+
+static int nr_uarts __initdata = 0;
+
+static struct s3c2410_uartcfg uart_cfgs[CONFIG_SERIAL_SAMSUNG_UARTS];
+
+/* s3c24xx_init_uartdevs
+ *
+ * copy the specified platform data and configuration into our central
+ * set of devices, before the data is thrown away after the init process.
+ *
+ * This also fills in the array passed to the serial driver for the
+ * early initialisation of the console.
+*/
+
+void __init s3c24xx_init_uartdevs(char *name,
+ struct s3c24xx_uart_resources *res,
+ struct s3c2410_uartcfg *cfg, int no)
+{
+ struct platform_device *platdev;
+ struct s3c2410_uartcfg *cfgptr = uart_cfgs;
+ struct s3c24xx_uart_resources *resp;
+ int uart;
+
+ memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
+
+ for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
+ platdev = s3c24xx_uart_src[cfgptr->hwport];
+
+ resp = res + cfgptr->hwport;
+
+ s3c24xx_uart_devs[uart] = platdev;
+
+ platdev->name = name;
+ platdev->resource = resp->resources;
+ platdev->num_resources = resp->nr_resources;
+
+ platdev->dev.platform_data = cfgptr;
+ }
+
+ nr_uarts = no;
+}
+
+void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+ if (cpu == NULL)
+ return;
+
+ if (cpu->init_uarts == NULL) {
+ printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
+ } else
+ (cpu->init_uarts)(cfg, no);
+}
+
+static int __init s3c_arch_init(void)
+{
+ int ret;
+
+ // do the correct init for cpu
+
+ if (cpu == NULL)
+ panic("s3c_arch_init: NULL cpu\n");
+
+ ret = (cpu->init)();
+ if (ret != 0)
+ return ret;
+
+ ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
+ return ret;
+}
+
+arch_initcall(s3c_arch_init);
diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c
new file mode 100644
index 000000000000..a318215ab535
--- /dev/null
+++ b/arch/arm/plat-s3c/pwm-clock.c
@@ -0,0 +1,463 @@
+/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ * Copyright (c) 2007, 2008 Ben Dooks
+ * Ben Dooks <ben-linux@fluff.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.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/log2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+#include <asm/irq.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+#include <plat/regs-timer.h>
+#include <mach/pwm-clock.h>
+
+/* Each of the timers 0 through 5 go through the following
+ * clock tree, with the inputs depending on the timers.
+ *
+ * pclk ---- [ prescaler 0 ] -+---> timer 0
+ * +---> timer 1
+ *
+ * pclk ---- [ prescaler 1 ] -+---> timer 2
+ * +---> timer 3
+ * \---> timer 4
+ *
+ * Which are fed into the timers as so:
+ *
+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\
+ * [mux] -> timer 0
+ * tclk 0 ------------------------------/
+ *
+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\
+ * [mux] -> timer 1
+ * tclk 0 ------------------------------/
+ *
+ *
+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\
+ * [mux] -> timer 2
+ * tclk 1 ------------------------------/
+ *
+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\
+ * [mux] -> timer 3
+ * tclk 1 ------------------------------/
+ *
+ * prescaled 1 ---- [ div 2,4,8, 16 ] --\
+ * [mux] -> timer 4
+ * tclk 1 ------------------------------/
+ *
+ * Since the mux and the divider are tied together in the
+ * same register space, it is impossible to set the parent
+ * and the rate at the same time. To avoid this, we add an
+ * intermediate 'prescaled-and-divided' clock to select
+ * as the parent for the timer input clock called tdiv.
+ *
+ * prescaled clk --> pwm-tdiv ---\
+ * [ mux ] --> timer X
+ * tclk -------------------------/
+*/
+
+static struct clk clk_timer_scaler[];
+
+static unsigned long clk_pwm_scaler_get_rate(struct clk *clk)
+{
+ unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+ if (clk == &clk_timer_scaler[1]) {
+ tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
+ tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
+ } else {
+ tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
+ }
+
+ return clk_get_rate(clk->parent) / (tcfg0 + 1);
+}
+
+static unsigned long clk_pwm_scaler_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long parent_rate = clk_get_rate(clk->parent);
+ unsigned long divisor = parent_rate / rate;
+
+ if (divisor > 256)
+ divisor = 256;
+ else if (divisor < 2)
+ divisor = 2;
+
+ return parent_rate / divisor;
+}
+
+static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long round = clk_pwm_scaler_round_rate(clk, rate);
+ unsigned long tcfg0;
+ unsigned long divisor;
+ unsigned long flags;
+
+ divisor = clk_get_rate(clk->parent) / round;
+ divisor--;
+
+ local_irq_save(flags);
+ tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+ if (clk == &clk_timer_scaler[1]) {
+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
+ tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT;
+ } else {
+ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
+ tcfg0 |= divisor;
+ }
+
+ __raw_writel(tcfg0, S3C2410_TCFG0);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static struct clk clk_timer_scaler[] = {
+ [0] = {
+ .name = "pwm-scaler0",
+ .id = -1,
+ .get_rate = clk_pwm_scaler_get_rate,
+ .set_rate = clk_pwm_scaler_set_rate,
+ .round_rate = clk_pwm_scaler_round_rate,
+ },
+ [1] = {
+ .name = "pwm-scaler1",
+ .id = -1,
+ .get_rate = clk_pwm_scaler_get_rate,
+ .set_rate = clk_pwm_scaler_set_rate,
+ .round_rate = clk_pwm_scaler_round_rate,
+ },
+};
+
+static struct clk clk_timer_tclk[] = {
+ [0] = {
+ .name = "pwm-tclk0",
+ .id = -1,
+ },
+ [1] = {
+ .name = "pwm-tclk1",
+ .id = -1,
+ },
+};
+
+struct pwm_tdiv_clk {
+ struct clk clk;
+ unsigned int divisor;
+};
+
+static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
+{
+ return container_of(clk, struct pwm_tdiv_clk, clk);
+}
+
+static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
+{
+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
+ unsigned int divisor;
+
+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
+
+ if (pwm_cfg_src_is_tclk(tcfg1))
+ divisor = to_tdiv(clk)->divisor;
+ else
+ divisor = tcfg_to_divisor(tcfg1);
+
+ return clk_get_rate(clk->parent) / divisor;
+}
+
+static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long parent_rate;
+ unsigned long divisor;
+
+ parent_rate = clk_get_rate(clk->parent);
+ divisor = parent_rate / rate;
+
+ if (divisor <= 1 && pwm_tdiv_has_div1())
+ divisor = 1;
+ else if (divisor <= 2)
+ divisor = 2;
+ else if (divisor <= 4)
+ divisor = 4;
+ else if (divisor <= 8)
+ divisor = 8;
+ else
+ divisor = 16;
+
+ return parent_rate / divisor;
+}
+
+static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
+{
+ return pwm_tdiv_div_bits(divclk->divisor);
+}
+
+static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
+{
+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
+ unsigned long bits = clk_pwm_tdiv_bits(divclk);
+ unsigned long flags;
+ unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id);
+
+ local_irq_save(flags);
+
+ tcfg1 = __raw_readl(S3C2410_TCFG1);
+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
+ tcfg1 |= bits << shift;
+ __raw_writel(tcfg1, S3C2410_TCFG1);
+
+ local_irq_restore(flags);
+}
+
+static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct pwm_tdiv_clk *divclk = to_tdiv(clk);
+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
+ unsigned long parent_rate = clk_get_rate(clk->parent);
+ unsigned long divisor;
+
+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
+
+ rate = clk_round_rate(clk, rate);
+ divisor = parent_rate / rate;
+
+ if (divisor > 16)
+ return -EINVAL;
+
+ divclk->divisor = divisor;
+
+ /* Update the current MUX settings if we are currently
+ * selected as the clock source for this clock. */
+
+ if (!pwm_cfg_src_is_tclk(tcfg1))
+ clk_pwm_tdiv_update(divclk);
+
+ return 0;
+}
+
+static struct pwm_tdiv_clk clk_timer_tdiv[] = {
+ [0] = {
+ .clk = {
+ .name = "pwm-tdiv",
+ .parent = &clk_timer_scaler[0],
+ .get_rate = clk_pwm_tdiv_get_rate,
+ .set_rate = clk_pwm_tdiv_set_rate,
+ .round_rate = clk_pwm_tdiv_round_rate,
+ },
+ },
+ [1] = {
+ .clk = {
+ .name = "pwm-tdiv",
+ .parent = &clk_timer_scaler[0],
+ .get_rate = clk_pwm_tdiv_get_rate,
+ .set_rate = clk_pwm_tdiv_set_rate,
+ .round_rate = clk_pwm_tdiv_round_rate,
+ }
+ },
+ [2] = {
+ .clk = {
+ .name = "pwm-tdiv",
+ .parent = &clk_timer_scaler[1],
+ .get_rate = clk_pwm_tdiv_get_rate,
+ .set_rate = clk_pwm_tdiv_set_rate,
+ .round_rate = clk_pwm_tdiv_round_rate,
+ },
+ },
+ [3] = {
+ .clk = {
+ .name = "pwm-tdiv",
+ .parent = &clk_timer_scaler[1],
+ .get_rate = clk_pwm_tdiv_get_rate,
+ .set_rate = clk_pwm_tdiv_set_rate,
+ .round_rate = clk_pwm_tdiv_round_rate,
+ },
+ },
+ [4] = {
+ .clk = {
+ .name = "pwm-tdiv",
+ .parent = &clk_timer_scaler[1],
+ .get_rate = clk_pwm_tdiv_get_rate,
+ .set_rate = clk_pwm_tdiv_set_rate,
+ .round_rate = clk_pwm_tdiv_round_rate,
+ },
+ },
+};
+
+static int __init clk_pwm_tdiv_register(unsigned int id)
+{
+ struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
+
+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
+
+ divclk->clk.id = id;
+ divclk->divisor = tcfg_to_divisor(tcfg1);
+
+ return s3c24xx_register_clock(&divclk->clk);
+}
+
+static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
+{
+ return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
+}
+
+static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
+{
+ return &clk_timer_tdiv[id].clk;
+}
+
+static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned int id = clk->id;
+ unsigned long tcfg1;
+ unsigned long flags;
+ unsigned long bits;
+ unsigned long shift = S3C2410_TCFG1_SHIFT(id);
+
+ if (parent == s3c24xx_pwmclk_tclk(id))
+ bits = S3C_TCFG1_MUX_TCLK << shift;
+ else if (parent == s3c24xx_pwmclk_tdiv(id))
+ bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ local_irq_save(flags);
+
+ tcfg1 = __raw_readl(S3C2410_TCFG1);
+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
+ __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static struct clk clk_tin[] = {
+ [0] = {
+ .name = "pwm-tin",
+ .id = 0,
+ .set_parent = clk_pwm_tin_set_parent,
+ },
+ [1] = {
+ .name = "pwm-tin",
+ .id = 1,
+ .set_parent = clk_pwm_tin_set_parent,
+ },
+ [2] = {
+ .name = "pwm-tin",
+ .id = 2,
+ .set_parent = clk_pwm_tin_set_parent,
+ },
+ [3] = {
+ .name = "pwm-tin",
+ .id = 3,
+ .set_parent = clk_pwm_tin_set_parent,
+ },
+ [4] = {
+ .name = "pwm-tin",
+ .id = 4,
+ .set_parent = clk_pwm_tin_set_parent,
+ },
+};
+
+static __init int clk_pwm_tin_register(struct clk *pwm)
+{
+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
+ unsigned int id = pwm->id;
+
+ struct clk *parent;
+ int ret;
+
+ ret = s3c24xx_register_clock(pwm);
+ if (ret < 0)
+ return ret;
+
+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
+
+ if (pwm_cfg_src_is_tclk(tcfg1))
+ parent = s3c24xx_pwmclk_tclk(id);
+ else
+ parent = s3c24xx_pwmclk_tdiv(id);
+
+ return clk_set_parent(pwm, parent);
+}
+
+/**
+ * s3c_pwmclk_init() - initialise pwm clocks
+ *
+ * Initialise and register the clocks which provide the inputs for the
+ * pwm timer blocks.
+ *
+ * Note, this call is required by the time core, so must be called after
+ * the base clocks are added and before any of the initcalls are run.
+ */
+__init void s3c_pwmclk_init(void)
+{
+ struct clk *clk_timers;
+ unsigned int clk;
+ int ret;
+
+ clk_timers = clk_get(NULL, "timers");
+ if (IS_ERR(clk_timers)) {
+ printk(KERN_ERR "%s: no parent clock\n", __func__);
+ return;
+ }
+
+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
+ clk_timer_scaler[clk].parent = clk_timers;
+ ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
+ if (ret < 0) {
+ printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
+ return;
+ }
+ }
+
+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
+ ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
+ if (ret < 0) {
+ printk(KERN_ERR "error adding pww tclk%d\n", clk);
+ return;
+ }
+ }
+
+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
+ ret = clk_pwm_tdiv_register(clk);
+ if (ret < 0) {
+ printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
+ return;
+ }
+ }
+
+ for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
+ ret = clk_pwm_tin_register(&clk_tin[clk]);
+ if (ret < 0) {
+ printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
+ return;
+ }
+ }
+}
diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c
new file mode 100644
index 000000000000..3b27b29da478
--- /dev/null
+++ b/arch/arm/plat-s3c/time.c
@@ -0,0 +1,285 @@
+/* linux/arch/arm/plat-s3c24xx/time.c
+ *
+ * Copyright (C) 2003-2005 Simtec Electronics
+ * Ben Dooks, <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <asm/system.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+
+#include <asm/irq.h>
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <mach/regs-irq.h>
+#include <asm/mach/time.h>
+#include <mach/tick.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+static unsigned long timer_startval;
+static unsigned long timer_usec_ticks;
+
+#ifndef TICK_MAX
+#define TICK_MAX (0xffff)
+#endif
+
+#define TIMER_USEC_SHIFT 16
+
+/* we use the shifted arithmetic to work out the ratio of timer ticks
+ * to usecs, as often the peripheral clock is not a nice even multiple
+ * of 1MHz.
+ *
+ * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok
+ * for the current HZ value of 200 without producing overflows.
+ *
+ * Original patch by Dimitry Andric, updated by Ben Dooks
+*/
+
+
+/* timer_mask_usec_ticks
+ *
+ * given a clock and divisor, make the value to pass into timer_ticks_to_usec
+ * to scale the ticks into usecs
+*/
+
+static inline unsigned long
+timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk)
+{
+ unsigned long den = pclk / 1000;
+
+ return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den;
+}
+
+/* timer_ticks_to_usec
+ *
+ * convert timer ticks to usec.
+*/
+
+static inline unsigned long timer_ticks_to_usec(unsigned long ticks)
+{
+ unsigned long res;
+
+ res = ticks * timer_usec_ticks;
+ res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */
+
+ return res >> TIMER_USEC_SHIFT;
+}
+
+/***
+ * Returns microsecond since last clock interrupt. Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ * IRQs are disabled before entering here from do_gettimeofday()
+ */
+
+static unsigned long s3c2410_gettimeoffset (void)
+{
+ unsigned long tdone;
+ unsigned long tval;
+
+ /* work out how many ticks have gone since last timer interrupt */
+
+ tval = __raw_readl(S3C2410_TCNTO(4));
+ tdone = timer_startval - tval;
+
+ /* check to see if there is an interrupt pending */
+
+ if (s3c24xx_ostimer_pending()) {
+ /* re-read the timer, and try and fix up for the missed
+ * interrupt. Note, the interrupt may go off before the
+ * timer has re-loaded from wrapping.
+ */
+
+ tval = __raw_readl(S3C2410_TCNTO(4));
+ tdone = timer_startval - tval;
+
+ if (tval != 0)
+ tdone += timer_startval;
+ }
+
+ return timer_ticks_to_usec(tdone);
+}
+
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t
+s3c2410_timer_interrupt(int irq, void *dev_id)
+{
+ timer_tick();
+ return IRQ_HANDLED;
+}
+
+static struct irqaction s3c2410_timer_irq = {
+ .name = "S3C2410 Timer Tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = s3c2410_timer_interrupt,
+};
+
+#define use_tclk1_12() ( \
+ machine_is_bast() || \
+ machine_is_vr1000() || \
+ machine_is_anubis() || \
+ machine_is_osiris())
+
+static struct clk *tin;
+static struct clk *tdiv;
+static struct clk *timerclk;
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ *
+ * Currently we only use timer4, as it is the only timer which has no
+ * other function that can be exploited externally
+ */
+static void s3c2410_timer_setup (void)
+{
+ unsigned long tcon;
+ unsigned long tcnt;
+ unsigned long tcfg1;
+ unsigned long tcfg0;
+
+ tcnt = TICK_MAX; /* default value for tcnt */
+
+ /* configure the system for whichever machine is in use */
+
+ if (use_tclk1_12()) {
+ /* timer is at 12MHz, scaler is 1 */
+ timer_usec_ticks = timer_mask_usec_ticks(1, 12000000);
+ tcnt = 12000000 / HZ;
+
+ tcfg1 = __raw_readl(S3C2410_TCFG1);
+ tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
+ tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1;
+ __raw_writel(tcfg1, S3C2410_TCFG1);
+ } else {
+ unsigned long pclk;
+ struct clk *tscaler;
+
+ /* for the h1940 (and others), we use the pclk from the core
+ * to generate the timer values. since values around 50 to
+ * 70MHz are not values we can directly generate the timer
+ * value from, we need to pre-scale and divide before using it.
+ *
+ * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz
+ * (8.45 ticks per usec)
+ */
+
+ pclk = clk_get_rate(timerclk);
+
+ /* configure clock tick */
+
+ timer_usec_ticks = timer_mask_usec_ticks(6, pclk);
+
+ tscaler = clk_get_parent(tdiv);
+
+ clk_set_rate(tscaler, pclk / 3);
+ clk_set_rate(tdiv, pclk / 6);
+ clk_set_parent(tin, tdiv);
+
+ tcnt = clk_get_rate(tin) / HZ;
+ }
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcfg0 = __raw_readl(S3C2410_TCFG0);
+ tcfg1 = __raw_readl(S3C2410_TCFG1);
+
+ /* timers reload after counting zero, so reduce the count by 1 */
+
+ tcnt--;
+
+ printk(KERN_DEBUG "timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n",
+ tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks);
+
+ /* check to see if timer is within 16bit range... */
+ if (tcnt > TICK_MAX) {
+ panic("setup_timer: HZ is too small, cannot configure timer!");
+ return;
+ }
+
+ __raw_writel(tcfg1, S3C2410_TCFG1);
+ __raw_writel(tcfg0, S3C2410_TCFG0);
+
+ timer_startval = tcnt;
+ __raw_writel(tcnt, S3C2410_TCNTB(4));
+
+ /* ensure timer is stopped... */
+
+ tcon &= ~(7<<20);
+ tcon |= S3C2410_TCON_T4RELOAD;
+ tcon |= S3C2410_TCON_T4MANUALUPD;
+
+ __raw_writel(tcon, S3C2410_TCON);
+ __raw_writel(tcnt, S3C2410_TCNTB(4));
+ __raw_writel(tcnt, S3C2410_TCMPB(4));
+
+ /* start the timer running */
+ tcon |= S3C2410_TCON_T4START;
+ tcon &= ~S3C2410_TCON_T4MANUALUPD;
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+static void __init s3c2410_timer_resources(void)
+{
+ struct platform_device tmpdev;
+
+ tmpdev.dev.bus = &platform_bus_type;
+ tmpdev.id = 4;
+
+ timerclk = clk_get(NULL, "timers");
+ if (IS_ERR(timerclk))
+ panic("failed to get clock for system timer");
+
+ clk_enable(timerclk);
+
+ if (!use_tclk1_12()) {
+ tin = clk_get(&tmpdev.dev, "pwm-tin");
+ if (IS_ERR(tin))
+ panic("failed to get pwm-tin clock for system timer");
+
+ tdiv = clk_get(&tmpdev.dev, "pwm-tdiv");
+ if (IS_ERR(tdiv))
+ panic("failed to get pwm-tdiv clock for system timer");
+ }
+
+ clk_enable(tin);
+}
+
+static void __init s3c2410_timer_init(void)
+{
+ s3c2410_timer_resources();
+ s3c2410_timer_setup();
+ setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
+}
+
+struct sys_timer s3c24xx_timer = {
+ .init = s3c2410_timer_init,
+ .offset = s3c2410_gettimeoffset,
+ .resume = s3c2410_timer_setup
+};
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index 0af3872fb763..2c8a2f5d75ff 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -6,8 +6,8 @@
config PLAT_S3C24XX
bool
- depends on ARCH_S3C2410
- default y if ARCH_S3C2410
+ depends on ARCH_S3C2410 || ARCH_S3C24A0
+ default y
select NO_IOPORT
select ARCH_REQUIRE_GPIOLIB
help
@@ -15,6 +15,19 @@ config PLAT_S3C24XX
if PLAT_S3C24XX
+# code that is shared between a number of the s3c24xx implementations
+
+config S3C2410_CLOCK
+ bool
+ help
+ Clock code for the S3C2410, and similar processors which
+ is currently includes the S3C2410, S3C2440, S3C2442.
+
+config S3C24XX_DCLK
+ bool
+ help
+ Clock code for supporting DCLK/CLKOUT on S3C24XX architectures
+
config CPU_S3C244X
bool
depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442)
@@ -28,6 +41,27 @@ config S3C24XX_PWM
Support for exporting the PWM timer blocks via the pwm device
system.
+
+# gpio configurations
+
+config S3C24XX_GPIO_EXTRA
+ int
+ default 128 if S3C24XX_GPIO_EXTRA128
+ default 64 if S3C24XX_GPIO_EXTRA64
+ default 0
+
+config S3C24XX_GPIO_EXTRA64
+ bool
+ help
+ Add an extra 64 gpio numbers to the available GPIO pool. This is
+ available for boards that need extra gpios for external devices.
+
+config S3C24XX_GPIO_EXTRA128
+ bool
+ help
+ Add an extra 128 gpio numbers to the available GPIO pool. This is
+ available for boards that need extra gpios for external devices.
+
config PM_SIMTEC
bool
help
@@ -49,6 +83,29 @@ config S3C2410_DMA_DEBUG
Enable debugging output for the DMA code. This option sends info
to the kernel log, at priority KERN_DEBUG.
+config S3C24XX_ADC
+ bool "ADC common driver support"
+ help
+ Core support for the ADC block found in the S3C24XX SoC systems
+ for drivers such as the touchscreen and hwmon to use to share
+ this resource.
+
+# SPI default pin configuration code
+
+config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13
+ bool
+ help
+ SPI GPIO configuration code for BUS0 when connected to
+ GPE11, GPE12 and GPE13.
+
+config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7
+ bool
+ help
+ SPI GPIO configuration code for BUS 1 when connected to
+ GPG5, GPG6 and GPG7.
+
+# common code for s3c24xx based machines, such as the SMDKs.
+
config MACH_SMDK
bool
help
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index d82767b2b833..1e0767b266b8 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -17,9 +17,8 @@ obj-y += irq.o
obj-y += devs.o
obj-y += gpio.o
obj-y += gpiolib.o
-obj-y += time.o
obj-y += clock.o
-obj-y += pwm-clock.o
+obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o
# Architecture dependant builds
@@ -30,5 +29,18 @@ obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_PM) += sleep.o
obj-$(CONFIG_HAVE_PWM) += pwm.o
+obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o
obj-$(CONFIG_S3C2410_DMA) += dma.o
+obj-$(CONFIG_S3C24XX_ADC) += adc.o
+
+# device specific setup and/or initialisation
+obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o
+
+# SPI gpio central GPIO functions
+
+obj-$(CONFIG_S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13) += spi-bus0-gpe11_12_13.o
+obj-$(CONFIG_S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7) += spi-bus1-gpg5_6_7.o
+
+# machine common support
+
obj-$(CONFIG_MACH_SMDK) += common-smdk.o
diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c
new file mode 100644
index 000000000000..9a5c767e0a42
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/adc.c
@@ -0,0 +1,372 @@
+/* arch/arm/plat-s3c24xx/adc.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
+ *
+ * S3C24XX ADC device core
+ *
+ * This program is free software; you can redistribute 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.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <plat/regs-adc.h>
+#include <plat/adc.h>
+
+/* This driver is designed to control the usage of the ADC block between
+ * the touchscreen and any other drivers that may need to use it, such as
+ * the hwmon driver.
+ *
+ * Priority will be given to the touchscreen driver, but as this itself is
+ * rate limited it should not starve other requests which are processed in
+ * order that they are received.
+ *
+ * Each user registers to get a client block which uniquely identifies it
+ * and stores information such as the necessary functions to callback when
+ * action is required.
+ */
+
+struct s3c_adc_client {
+ struct platform_device *pdev;
+ struct list_head pend;
+
+ unsigned int nr_samples;
+ unsigned char is_ts;
+ unsigned char channel;
+
+ void (*select_cb)(unsigned selected);
+ void (*convert_cb)(unsigned val1, unsigned val2);
+};
+
+struct adc_device {
+ struct platform_device *pdev;
+ struct platform_device *owner;
+ struct clk *clk;
+ struct s3c_adc_client *cur;
+ struct s3c_adc_client *ts_pend;
+ void __iomem *regs;
+
+ unsigned int prescale;
+
+ int irq;
+};
+
+static struct adc_device *adc_dev;
+
+static LIST_HEAD(adc_pending);
+
+#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
+
+static inline void s3c_adc_convert(struct adc_device *adc)
+{
+ unsigned con = readl(adc->regs + S3C2410_ADCCON);
+
+ con |= S3C2410_ADCCON_ENABLE_START;
+ writel(con, adc->regs + S3C2410_ADCCON);
+}
+
+static inline void s3c_adc_select(struct adc_device *adc,
+ struct s3c_adc_client *client)
+{
+ unsigned con = readl(adc->regs + S3C2410_ADCCON);
+
+ client->select_cb(1);
+
+ con &= ~S3C2410_ADCCON_MUXMASK;
+ con &= ~S3C2410_ADCCON_STDBM;
+ con &= ~S3C2410_ADCCON_STARTMASK;
+
+ if (!client->is_ts)
+ con |= S3C2410_ADCCON_SELMUX(client->channel);
+
+ writel(con, adc->regs + S3C2410_ADCCON);
+}
+
+static void s3c_adc_dbgshow(struct adc_device *adc)
+{
+ adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",
+ readl(adc->regs + S3C2410_ADCCON),
+ readl(adc->regs + S3C2410_ADCTSC),
+ readl(adc->regs + S3C2410_ADCDLY));
+}
+
+void s3c_adc_try(struct adc_device *adc)
+{
+ struct s3c_adc_client *next = adc->ts_pend;
+
+ if (!next && !list_empty(&adc_pending)) {
+ next = list_first_entry(&adc_pending,
+ struct s3c_adc_client, pend);
+ list_del(&next->pend);
+ } else
+ adc->ts_pend = NULL;
+
+ if (next) {
+ adc_dbg(adc, "new client is %p\n", next);
+ adc->cur = next;
+ s3c_adc_select(adc, next);
+ s3c_adc_convert(adc);
+ s3c_adc_dbgshow(adc);
+ }
+}
+
+int s3c_adc_start(struct s3c_adc_client *client,
+ unsigned int channel, unsigned int nr_samples)
+{
+ struct adc_device *adc = adc_dev;
+ unsigned long flags;
+
+ if (!adc) {
+ printk(KERN_ERR "%s: failed to find adc\n", __func__);
+ return -EINVAL;
+ }
+
+ if (client->is_ts && adc->ts_pend)
+ return -EAGAIN;
+
+ local_irq_save(flags);
+
+ client->channel = channel;
+ client->nr_samples = nr_samples;
+
+ if (client->is_ts)
+ adc->ts_pend = client;
+ else
+ list_add_tail(&client->pend, &adc_pending);
+
+ if (!adc->cur)
+ s3c_adc_try(adc);
+ local_irq_restore(flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(s3c_adc_start);
+
+static void s3c_adc_default_select(unsigned select)
+{
+}
+
+struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
+ void (*select)(unsigned int selected),
+ void (*conv)(unsigned d0, unsigned d1),
+ unsigned int is_ts)
+{
+ struct s3c_adc_client *client;
+
+ WARN_ON(!pdev);
+ WARN_ON(!conv);
+
+ if (!select)
+ select = s3c_adc_default_select;
+
+ if (!conv || !pdev)
+ return ERR_PTR(-EINVAL);
+
+ client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
+ if (!client) {
+ dev_err(&pdev->dev, "no memory for adc client\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ client->pdev = pdev;
+ client->is_ts = is_ts;
+ client->select_cb = select;
+ client->convert_cb = conv;
+
+ return client;
+}
+EXPORT_SYMBOL_GPL(s3c_adc_register);
+
+void s3c_adc_release(struct s3c_adc_client *client)
+{
+ /* We should really check that nothing is in progress. */
+ kfree(client);
+}
+EXPORT_SYMBOL_GPL(s3c_adc_release);
+
+static irqreturn_t s3c_adc_irq(int irq, void *pw)
+{
+ struct adc_device *adc = pw;
+ struct s3c_adc_client *client = adc->cur;
+ unsigned long flags;
+ unsigned data0, data1;
+
+ if (!client) {
+ dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
+ return IRQ_HANDLED;
+ }
+
+ data0 = readl(adc->regs + S3C2410_ADCDAT0);
+ data1 = readl(adc->regs + S3C2410_ADCDAT1);
+ adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
+
+ (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff);
+
+ if (--client->nr_samples > 0) {
+ /* fire another conversion for this */
+
+ client->select_cb(1);
+ s3c_adc_convert(adc);
+ } else {
+ local_irq_save(flags);
+ (client->select_cb)(0);
+ adc->cur = NULL;
+
+ s3c_adc_try(adc);
+ local_irq_restore(flags);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int s3c_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct adc_device *adc;
+ struct resource *regs;
+ int ret;
+
+ adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);
+ if (adc == NULL) {
+ dev_err(dev, "failed to allocate adc_device\n");
+ return -ENOMEM;
+ }
+
+ adc->pdev = pdev;
+ adc->prescale = S3C2410_ADCCON_PRSCVL(49);
+
+ adc->irq = platform_get_irq(pdev, 1);
+ if (adc->irq <= 0) {
+ dev_err(dev, "failed to get adc irq\n");
+ ret = -ENOENT;
+ goto err_alloc;
+ }
+
+ ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc);
+ if (ret < 0) {
+ dev_err(dev, "failed to attach adc irq\n");
+ goto err_alloc;
+ }
+
+ adc->clk = clk_get(dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ dev_err(dev, "failed to get adc clock\n");
+ ret = PTR_ERR(adc->clk);
+ goto err_irq;
+ }
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_err(dev, "failed to find registers\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+
+ adc->regs = ioremap(regs->start, resource_size(regs));
+ if (!adc->regs) {
+ dev_err(dev, "failed to map registers\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+
+ clk_enable(adc->clk);
+
+ writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
+ adc->regs + S3C2410_ADCCON);
+
+ dev_info(dev, "attached adc driver\n");
+
+ platform_set_drvdata(pdev, adc);
+ adc_dev = adc;
+
+ return 0;
+
+ err_clk:
+ clk_put(adc->clk);
+
+ err_irq:
+ free_irq(adc->irq, adc);
+
+ err_alloc:
+ kfree(adc);
+ return ret;
+}
+
+static int s3c_adc_remove(struct platform_device *pdev)
+{
+ struct adc_device *adc = platform_get_drvdata(pdev);
+
+ iounmap(adc->regs);
+ free_irq(adc->irq, adc);
+ clk_disable(adc->clk);
+ clk_put(adc->clk);
+ kfree(adc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct adc_device *adc = platform_get_drvdata(pdev);
+ u32 con;
+
+ con = readl(adc->regs + S3C2410_ADCCON);
+ con |= S3C2410_ADCCON_STDBM;
+ writel(con, adc->regs + S3C2410_ADCCON);
+
+ clk_disable(adc->clk);
+
+ return 0;
+}
+
+static int s3c_adc_resume(struct platform_device *pdev)
+{
+ struct adc_device *adc = platform_get_drvdata(pdev);
+
+ clk_enable(adc->clk);
+
+ writel(adc->prescale | S3C2410_ADCCON_PRSCEN,
+ adc->regs + S3C2410_ADCCON);
+
+ return 0;
+}
+
+#else
+#define s3c_adc_suspend NULL
+#define s3c_adc_resume NULL
+#endif
+
+static struct platform_driver s3c_adc_driver = {
+ .driver = {
+ .name = "s3c24xx-adc",
+ .owner = THIS_MODULE,
+ },
+ .probe = s3c_adc_probe,
+ .remove = __devexit_p(s3c_adc_remove),
+ .suspend = s3c_adc_suspend,
+ .resume = s3c_adc_resume,
+};
+
+static int __init adc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&s3c_adc_driver);
+ if (ret)
+ printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
+
+ return ret;
+}
+
+arch_initcall(adc_init);
diff --git a/arch/arm/plat-s3c24xx/clock-dclk.c b/arch/arm/plat-s3c24xx/clock-dclk.c
new file mode 100644
index 000000000000..5b75a797b5ab
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/clock-dclk.c
@@ -0,0 +1,194 @@
+/* linux/arch/arm/plat-s3c24xx/clock-dclk.c
+ *
+ * Copyright (c) 2004,2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C24XX - definitions for DCLK and CLKOUT registers
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+/* clocks that could be registered by external code */
+
+static int s3c24xx_dclk_enable(struct clk *clk, int enable)
+{
+ unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON);
+
+ if (enable)
+ dclkcon |= clk->ctrlbit;
+ else
+ dclkcon &= ~clk->ctrlbit;
+
+ __raw_writel(dclkcon, S3C24XX_DCLKCON);
+
+ return 0;
+}
+
+static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
+{
+ unsigned long dclkcon;
+ unsigned int uclk;
+
+ if (parent == &clk_upll)
+ uclk = 1;
+ else if (parent == &clk_p)
+ uclk = 0;
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ dclkcon = __raw_readl(S3C24XX_DCLKCON);
+
+ if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) {
+ if (uclk)
+ dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK;
+ else
+ dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK;
+ } else {
+ if (uclk)
+ dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK;
+ else
+ dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK;
+ }
+
+ __raw_writel(dclkcon, S3C24XX_DCLKCON);
+
+ return 0;
+}
+static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate)
+{
+ unsigned long div;
+
+ if ((rate == 0) || !clk->parent)
+ return 0;
+
+ div = clk_get_rate(clk->parent) / rate;
+ if (div < 2)
+ div = 2;
+ else if (div > 16)
+ div = 16;
+
+ return div;
+}
+
+static unsigned long s3c24xx_round_dclk_rate(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long div = s3c24xx_calc_div(clk, rate);
+
+ if (div == 0)
+ return 0;
+
+ return clk_get_rate(clk->parent) / div;
+}
+
+static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long mask, data, div = s3c24xx_calc_div(clk, rate);
+
+ if (div == 0)
+ return -EINVAL;
+
+ if (clk == &s3c24xx_dclk0) {
+ mask = S3C2410_DCLKCON_DCLK0_DIV_MASK |
+ S3C2410_DCLKCON_DCLK0_CMP_MASK;
+ data = S3C2410_DCLKCON_DCLK0_DIV(div) |
+ S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2);
+ } else if (clk == &s3c24xx_dclk1) {
+ mask = S3C2410_DCLKCON_DCLK1_DIV_MASK |
+ S3C2410_DCLKCON_DCLK1_CMP_MASK;
+ data = S3C2410_DCLKCON_DCLK1_DIV(div) |
+ S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2);
+ } else
+ return -EINVAL;
+
+ clk->rate = clk_get_rate(clk->parent) / div;
+ __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data),
+ S3C24XX_DCLKCON);
+ return clk->rate;
+}
+static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
+{
+ unsigned long mask;
+ unsigned long source;
+
+ /* calculate the MISCCR setting for the clock */
+
+ if (parent == &clk_xtal)
+ source = S3C2410_MISCCR_CLK0_MPLL;
+ else if (parent == &clk_upll)
+ source = S3C2410_MISCCR_CLK0_UPLL;
+ else if (parent == &clk_f)
+ source = S3C2410_MISCCR_CLK0_FCLK;
+ else if (parent == &clk_h)
+ source = S3C2410_MISCCR_CLK0_HCLK;
+ else if (parent == &clk_p)
+ source = S3C2410_MISCCR_CLK0_PCLK;
+ else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0)
+ source = S3C2410_MISCCR_CLK0_DCLK0;
+ else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1)
+ source = S3C2410_MISCCR_CLK0_DCLK0;
+ else
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ if (clk == &s3c24xx_clkout0)
+ mask = S3C2410_MISCCR_CLK0_MASK;
+ else {
+ source <<= 4;
+ mask = S3C2410_MISCCR_CLK1_MASK;
+ }
+
+ s3c2410_modify_misccr(mask, source);
+ return 0;
+}
+
+/* external clock definitions */
+
+struct clk s3c24xx_dclk0 = {
+ .name = "dclk0",
+ .id = -1,
+ .ctrlbit = S3C2410_DCLKCON_DCLK0EN,
+ .enable = s3c24xx_dclk_enable,
+ .set_parent = s3c24xx_dclk_setparent,
+ .set_rate = s3c24xx_set_dclk_rate,
+ .round_rate = s3c24xx_round_dclk_rate,
+};
+
+struct clk s3c24xx_dclk1 = {
+ .name = "dclk1",
+ .id = -1,
+ .ctrlbit = S3C2410_DCLKCON_DCLK1EN,
+ .enable = s3c24xx_dclk_enable,
+ .set_parent = s3c24xx_dclk_setparent,
+ .set_rate = s3c24xx_set_dclk_rate,
+ .round_rate = s3c24xx_round_dclk_rate,
+};
+
+struct clk s3c24xx_clkout0 = {
+ .name = "clkout0",
+ .id = -1,
+ .set_parent = s3c24xx_clkout_setparent,
+};
+
+struct clk s3c24xx_clkout1 = {
+ .name = "clkout1",
+ .id = -1,
+ .set_parent = s3c24xx_clkout_setparent,
+};
diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c
index a005ddbd9ef3..8474d05274bd 100644
--- a/arch/arm/plat-s3c24xx/clock.c
+++ b/arch/arm/plat-s3c24xx/clock.c
@@ -27,18 +27,8 @@
*/
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#include <linux/sysdev.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <mach/hardware.h>
@@ -47,490 +37,23 @@
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
+#include <plat/cpu-freq.h>
+
#include <plat/clock.h>
#include <plat/cpu.h>
-
-/* clock information */
-
-static LIST_HEAD(clocks);
-
-DEFINE_MUTEX(clocks_mutex);
-
-/* enable and disable calls for use with the clk struct */
-
-static int clk_null_enable(struct clk *clk, int enable)
-{
- return 0;
-}
-
-/* Clock API calls */
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p;
- struct clk *clk = ERR_PTR(-ENOENT);
- int idno;
-
- if (dev == NULL || dev->bus != &platform_bus_type)
- idno = -1;
- else
- idno = to_platform_device(dev)->id;
-
- mutex_lock(&clocks_mutex);
-
- list_for_each_entry(p, &clocks, list) {
- if (p->id == idno &&
- strcmp(id, p->name) == 0 &&
- try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
-
- /* check for the case where a device was supplied, but the
- * clock that was being searched for is not device specific */
-
- if (IS_ERR(clk)) {
- list_for_each_entry(p, &clocks, list) {
- if (p->id == -1 && strcmp(id, p->name) == 0 &&
- try_module_get(p->owner)) {
- clk = p;
- break;
- }
- }
- }
-
- mutex_unlock(&clocks_mutex);
- return clk;
-}
-
-void clk_put(struct clk *clk)
-{
- module_put(clk->owner);
-}
-
-int clk_enable(struct clk *clk)
-{
- if (IS_ERR(clk) || clk == NULL)
- return -EINVAL;
-
- clk_enable(clk->parent);
-
- mutex_lock(&clocks_mutex);
-
- if ((clk->usage++) == 0)
- (clk->enable)(clk, 1);
-
- mutex_unlock(&clocks_mutex);
- return 0;
-}
-
-void clk_disable(struct clk *clk)
-{
- if (IS_ERR(clk) || clk == NULL)
- return;
-
- mutex_lock(&clocks_mutex);
-
- if ((--clk->usage) == 0)
- (clk->enable)(clk, 0);
-
- mutex_unlock(&clocks_mutex);
- clk_disable(clk->parent);
-}
-
-
-unsigned long clk_get_rate(struct clk *clk)
-{
- if (IS_ERR(clk))
- return 0;
-
- if (clk->rate != 0)
- return clk->rate;
-
- if (clk->get_rate != NULL)
- return (clk->get_rate)(clk);
-
- if (clk->parent != NULL)
- return clk_get_rate(clk->parent);
-
- return clk->rate;
-}
-
-long clk_round_rate(struct clk *clk, unsigned long rate)
-{
- if (!IS_ERR(clk) && clk->round_rate)
- return (clk->round_rate)(clk, rate);
-
- return rate;
-}
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
- int ret;
-
- if (IS_ERR(clk))
- return -EINVAL;
-
- /* We do not default just do a clk->rate = rate as
- * the clock may have been made this way by choice.
- */
-
- WARN_ON(clk->set_rate == NULL);
-
- if (clk->set_rate == NULL)
- return -EINVAL;
-
- mutex_lock(&clocks_mutex);
- ret = (clk->set_rate)(clk, rate);
- mutex_unlock(&clocks_mutex);
-
- return ret;
-}
-
-struct clk *clk_get_parent(struct clk *clk)
-{
- return clk->parent;
-}
-
-int clk_set_parent(struct clk *clk, struct clk *parent)
-{
- int ret = 0;
-
- if (IS_ERR(clk))
- return -EINVAL;
-
- mutex_lock(&clocks_mutex);
-
- if (clk->set_parent)
- ret = (clk->set_parent)(clk, parent);
-
- mutex_unlock(&clocks_mutex);
-
- return ret;
-}
-
-EXPORT_SYMBOL(clk_get);
-EXPORT_SYMBOL(clk_put);
-EXPORT_SYMBOL(clk_enable);
-EXPORT_SYMBOL(clk_disable);
-EXPORT_SYMBOL(clk_get_rate);
-EXPORT_SYMBOL(clk_round_rate);
-EXPORT_SYMBOL(clk_set_rate);
-EXPORT_SYMBOL(clk_get_parent);
-EXPORT_SYMBOL(clk_set_parent);
-
-/* base clocks */
-
-static int clk_default_setrate(struct clk *clk, unsigned long rate)
-{
- clk->rate = rate;
- return 0;
-}
-
-struct clk clk_xtal = {
- .name = "xtal",
- .id = -1,
- .rate = 0,
- .parent = NULL,
- .ctrlbit = 0,
-};
-
-struct clk clk_mpll = {
- .name = "mpll",
- .id = -1,
- .set_rate = clk_default_setrate,
-};
-
-struct clk clk_upll = {
- .name = "upll",
- .id = -1,
- .parent = NULL,
- .ctrlbit = 0,
-};
-
-struct clk clk_f = {
- .name = "fclk",
- .id = -1,
- .rate = 0,
- .parent = &clk_mpll,
- .ctrlbit = 0,
- .set_rate = clk_default_setrate,
-};
-
-struct clk clk_h = {
- .name = "hclk",
- .id = -1,
- .rate = 0,
- .parent = NULL,
- .ctrlbit = 0,
- .set_rate = clk_default_setrate,
-};
-
-struct clk clk_p = {
- .name = "pclk",
- .id = -1,
- .rate = 0,
- .parent = NULL,
- .ctrlbit = 0,
- .set_rate = clk_default_setrate,
-};
-
-struct clk clk_usb_bus = {
- .name = "usb-bus",
- .id = -1,
- .rate = 0,
- .parent = &clk_upll,
-};
-
-/* clocks that could be registered by external code */
-
-static int s3c24xx_dclk_enable(struct clk *clk, int enable)
-{
- unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON);
-
- if (enable)
- dclkcon |= clk->ctrlbit;
- else
- dclkcon &= ~clk->ctrlbit;
-
- __raw_writel(dclkcon, S3C24XX_DCLKCON);
-
- return 0;
-}
-
-static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
-{
- unsigned long dclkcon;
- unsigned int uclk;
-
- if (parent == &clk_upll)
- uclk = 1;
- else if (parent == &clk_p)
- uclk = 0;
- else
- return -EINVAL;
-
- clk->parent = parent;
-
- dclkcon = __raw_readl(S3C24XX_DCLKCON);
-
- if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) {
- if (uclk)
- dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK;
- else
- dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK;
- } else {
- if (uclk)
- dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK;
- else
- dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK;
- }
-
- __raw_writel(dclkcon, S3C24XX_DCLKCON);
-
- return 0;
-}
-
-static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate)
-{
- unsigned long div;
-
- if ((rate == 0) || !clk->parent)
- return 0;
-
- div = clk_get_rate(clk->parent) / rate;
- if (div < 2)
- div = 2;
- else if (div > 16)
- div = 16;
-
- return div;
-}
-
-static unsigned long s3c24xx_round_dclk_rate(struct clk *clk,
- unsigned long rate)
-{
- unsigned long div = s3c24xx_calc_div(clk, rate);
-
- if (div == 0)
- return 0;
-
- return clk_get_rate(clk->parent) / div;
-}
-
-static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate)
-{
- unsigned long mask, data, div = s3c24xx_calc_div(clk, rate);
-
- if (div == 0)
- return -EINVAL;
-
- if (clk == &s3c24xx_dclk0) {
- mask = S3C2410_DCLKCON_DCLK0_DIV_MASK |
- S3C2410_DCLKCON_DCLK0_CMP_MASK;
- data = S3C2410_DCLKCON_DCLK0_DIV(div) |
- S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2);
- } else if (clk == &s3c24xx_dclk1) {
- mask = S3C2410_DCLKCON_DCLK1_DIV_MASK |
- S3C2410_DCLKCON_DCLK1_CMP_MASK;
- data = S3C2410_DCLKCON_DCLK1_DIV(div) |
- S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2);
- } else
- return -EINVAL;
-
- clk->rate = clk_get_rate(clk->parent) / div;
- __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data),
- S3C24XX_DCLKCON);
- return clk->rate;
-}
-
-static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
-{
- unsigned long mask;
- unsigned long source;
-
- /* calculate the MISCCR setting for the clock */
-
- if (parent == &clk_xtal)
- source = S3C2410_MISCCR_CLK0_MPLL;
- else if (parent == &clk_upll)
- source = S3C2410_MISCCR_CLK0_UPLL;
- else if (parent == &clk_f)
- source = S3C2410_MISCCR_CLK0_FCLK;
- else if (parent == &clk_h)
- source = S3C2410_MISCCR_CLK0_HCLK;
- else if (parent == &clk_p)
- source = S3C2410_MISCCR_CLK0_PCLK;
- else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0)
- source = S3C2410_MISCCR_CLK0_DCLK0;
- else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1)
- source = S3C2410_MISCCR_CLK0_DCLK0;
- else
- return -EINVAL;
-
- clk->parent = parent;
-
- if (clk == &s3c24xx_clkout0)
- mask = S3C2410_MISCCR_CLK0_MASK;
- else {
- source <<= 4;
- mask = S3C2410_MISCCR_CLK1_MASK;
- }
-
- s3c2410_modify_misccr(mask, source);
- return 0;
-}
-
-/* external clock definitions */
-
-struct clk s3c24xx_dclk0 = {
- .name = "dclk0",
- .id = -1,
- .ctrlbit = S3C2410_DCLKCON_DCLK0EN,
- .enable = s3c24xx_dclk_enable,
- .set_parent = s3c24xx_dclk_setparent,
- .set_rate = s3c24xx_set_dclk_rate,
- .round_rate = s3c24xx_round_dclk_rate,
-};
-
-struct clk s3c24xx_dclk1 = {
- .name = "dclk1",
- .id = -1,
- .ctrlbit = S3C2410_DCLKCON_DCLK1EN,
- .enable = s3c24xx_dclk_enable,
- .set_parent = s3c24xx_dclk_setparent,
- .set_rate = s3c24xx_set_dclk_rate,
- .round_rate = s3c24xx_round_dclk_rate,
-};
-
-struct clk s3c24xx_clkout0 = {
- .name = "clkout0",
- .id = -1,
- .set_parent = s3c24xx_clkout_setparent,
-};
-
-struct clk s3c24xx_clkout1 = {
- .name = "clkout1",
- .id = -1,
- .set_parent = s3c24xx_clkout_setparent,
-};
-
-struct clk s3c24xx_uclk = {
- .name = "uclk",
- .id = -1,
-};
-
-/* initialise the clock system */
-
-int s3c24xx_register_clock(struct clk *clk)
-{
- clk->owner = THIS_MODULE;
-
- if (clk->enable == NULL)
- clk->enable = clk_null_enable;
-
- /* add to the list of available clocks */
-
- mutex_lock(&clocks_mutex);
- list_add(&clk->list, &clocks);
- mutex_unlock(&clocks_mutex);
-
- return 0;
-}
-
-int s3c24xx_register_clocks(struct clk **clks, int nr_clks)
-{
- int fails = 0;
-
- for (; nr_clks > 0; nr_clks--, clks++) {
- if (s3c24xx_register_clock(*clks) < 0)
- fails++;
- }
-
- return fails;
-}
+#include <plat/pll.h>
/* initalise all the clocks */
-int __init s3c24xx_setup_clocks(unsigned long xtal,
- unsigned long fclk,
- unsigned long hclk,
- unsigned long pclk)
+void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk,
+ unsigned long hclk,
+ unsigned long pclk)
{
- printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");
-
- /* initialise the main system clocks */
-
- clk_xtal.rate = xtal;
- clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal);
+ clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON),
+ clk_xtal.rate);
clk_mpll.rate = fclk;
clk_h.rate = hclk;
clk_p.rate = pclk;
clk_f.rate = fclk;
-
- /* assume uart clocks are correctly setup */
-
- /* register our clocks */
-
- if (s3c24xx_register_clock(&clk_xtal) < 0)
- printk(KERN_ERR "failed to register master xtal\n");
-
- if (s3c24xx_register_clock(&clk_mpll) < 0)
- printk(KERN_ERR "failed to register mpll clock\n");
-
- if (s3c24xx_register_clock(&clk_upll) < 0)
- printk(KERN_ERR "failed to register upll clock\n");
-
- if (s3c24xx_register_clock(&clk_f) < 0)
- printk(KERN_ERR "failed to register cpu fclk\n");
-
- if (s3c24xx_register_clock(&clk_h) < 0)
- printk(KERN_ERR "failed to register cpu hclk\n");
-
- if (s3c24xx_register_clock(&clk_p) < 0)
- printk(KERN_ERR "failed to register cpu pclk\n");
-
- return 0;
}
diff --git a/arch/arm/plat-s3c24xx/common-smdk.c b/arch/arm/plat-s3c24xx/common-smdk.c
index 3098736c65d9..3d4837021ac7 100644
--- a/arch/arm/plat-s3c24xx/common-smdk.c
+++ b/arch/arm/plat-s3c24xx/common-smdk.c
@@ -38,7 +38,7 @@
#include <mach/regs-gpio.h>
#include <mach/leds-gpio.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/nand.h>
#include <plat/common-smdk.h>
#include <plat/devs.h>
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c
index 22a329513c0f..542062f8cbc1 100644
--- a/arch/arm/plat-s3c24xx/cpu.c
+++ b/arch/arm/plat-s3c24xx/cpu.c
@@ -30,7 +30,6 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/delay.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -55,16 +54,6 @@
#include <plat/s3c2442.h>
#include <plat/s3c2443.h>
-struct cpu_table {
- unsigned long idcode;
- unsigned long idmask;
- void (*map_io)(struct map_desc *mach_desc, int size);
- void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no);
- void (*init_clocks)(int xtal);
- int (*init)(void);
- const char *name;
-};
-
/* table of supported CPUs */
static const char name_s3c2400[] = "S3C2400";
@@ -169,23 +158,7 @@ static struct map_desc s3c_iodesc[] __initdata = {
IODESC_ENT(UART)
};
-static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode)
-{
- struct cpu_table *tab;
- int count;
-
- tab = cpu_ids;
- for (count = 0; count < ARRAY_SIZE(cpu_ids); count++, tab++) {
- if ((idcode & tab->idmask) == tab->idcode)
- return tab;
- }
-
- return NULL;
-}
-
-/* cpu information */
-
-static struct cpu_table *cpu;
+/* read cpu identificaiton code */
static unsigned long s3c24xx_read_idcode_v5(void)
{
@@ -231,6 +204,7 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
unsigned long idcode = 0x0;
/* initialise the io descriptors we need for initialisation */
+ iotable_init(mach_desc, size);
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
if (cpu_architecture() >= CPU_ARCH_ARMv5) {
@@ -239,117 +213,7 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
idcode = s3c24xx_read_idcode_v4();
}
- cpu = s3c_lookup_cpu(idcode);
-
- if (cpu == NULL) {
- printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
- panic("Unknown S3C24XX CPU");
- }
-
- printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
-
- if (cpu->map_io == NULL || cpu->init == NULL) {
- printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
- panic("Unsupported S3C24XX CPU");
- }
-
arm_pm_restart = s3c24xx_pm_restart;
- (cpu->map_io)(mach_desc, size);
-}
-
-/* s3c24xx_init_clocks
- *
- * Initialise the clock subsystem and associated information from the
- * given master crystal value.
- *
- * xtal = 0 -> use default PLL crystal value (normally 12MHz)
- * != 0 -> PLL crystal value in Hz
-*/
-
-void __init s3c24xx_init_clocks(int xtal)
-{
- if (xtal == 0)
- xtal = 12*1000*1000;
-
- if (cpu == NULL)
- panic("s3c24xx_init_clocks: no cpu setup?\n");
-
- if (cpu->init_clocks == NULL)
- panic("s3c24xx_init_clocks: cpu has no clock init\n");
- else
- (cpu->init_clocks)(xtal);
+ s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
}
-
-/* uart management */
-
-static int nr_uarts __initdata = 0;
-
-static struct s3c2410_uartcfg uart_cfgs[3];
-
-/* s3c24xx_init_uartdevs
- *
- * copy the specified platform data and configuration into our central
- * set of devices, before the data is thrown away after the init process.
- *
- * This also fills in the array passed to the serial driver for the
- * early initialisation of the console.
-*/
-
-void __init s3c24xx_init_uartdevs(char *name,
- struct s3c24xx_uart_resources *res,
- struct s3c2410_uartcfg *cfg, int no)
-{
- struct platform_device *platdev;
- struct s3c2410_uartcfg *cfgptr = uart_cfgs;
- struct s3c24xx_uart_resources *resp;
- int uart;
-
- memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
-
- for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
- platdev = s3c24xx_uart_src[cfgptr->hwport];
-
- resp = res + cfgptr->hwport;
-
- s3c24xx_uart_devs[uart] = platdev;
-
- platdev->name = name;
- platdev->resource = resp->resources;
- platdev->num_resources = resp->nr_resources;
-
- platdev->dev.platform_data = cfgptr;
- }
-
- nr_uarts = no;
-}
-
-void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
-{
- if (cpu == NULL)
- return;
-
- if (cpu->init_uarts == NULL) {
- printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
- } else
- (cpu->init_uarts)(cfg, no);
-}
-
-static int __init s3c_arch_init(void)
-{
- int ret;
-
- // do the correct init for cpu
-
- if (cpu == NULL)
- panic("s3c_arch_init: NULL cpu\n");
-
- ret = (cpu->init)();
- if (ret != 0)
- return ret;
-
- ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
- return ret;
-}
-
-arch_initcall(s3c_arch_init);
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index e93f8bf6d338..16ac01d9b8ab 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -29,11 +29,11 @@
#include <asm/irq.h>
#include <plat/regs-serial.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/udc.h>
#include <plat/devs.h>
#include <plat/cpu.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
/* Serial port registrations */
@@ -76,6 +76,19 @@ static struct resource s3c2410_uart2_resource[] = {
}
};
+static struct resource s3c2410_uart3_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_UART3,
+ .end = S3C2443_PA_UART3 + 0x3fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX3,
+ .end = IRQ_S3CUART_ERR3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
[0] = {
.resources = s3c2410_uart0_resource,
@@ -89,6 +102,10 @@ struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
.resources = s3c2410_uart2_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),
},
+ [3] = {
+ .resources = s3c2410_uart3_resource,
+ .nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),
+ },
};
/* yart devices */
@@ -105,13 +122,18 @@ static struct platform_device s3c24xx_uart_device2 = {
.id = 2,
};
-struct platform_device *s3c24xx_uart_src[3] = {
+static struct platform_device s3c24xx_uart_device3 = {
+ .id = 3,
+};
+
+struct platform_device *s3c24xx_uart_src[4] = {
&s3c24xx_uart_device0,
&s3c24xx_uart_device1,
&s3c24xx_uart_device2,
+ &s3c24xx_uart_device3,
};
-struct platform_device *s3c24xx_uart_devs[3] = {
+struct platform_device *s3c24xx_uart_devs[4] = {
};
/* USB Host Controller */
@@ -192,8 +214,8 @@ void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
static struct resource s3c_nand_resource[] = {
[0] = {
- .start = S3C2410_PA_NAND,
- .end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
+ .start = S3C24XX_PA_NAND,
+ .end = S3C24XX_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
}
};
@@ -271,31 +293,6 @@ struct platform_device s3c_device_wdt = {
EXPORT_SYMBOL(s3c_device_wdt);
-/* I2C */
-
-static struct resource s3c_i2c_resource[] = {
- [0] = {
- .start = S3C24XX_PA_IIC,
- .end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_IIC,
- .end = IRQ_IIC,
- .flags = IORESOURCE_IRQ,
- }
-
-};
-
-struct platform_device s3c_device_i2c = {
- .name = "s3c2410-i2c",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_i2c_resource),
- .resource = s3c_i2c_resource,
-};
-
-EXPORT_SYMBOL(s3c_device_i2c);
-
/* IIS */
static struct resource s3c_iis_resource[] = {
@@ -372,18 +369,26 @@ static struct resource s3c_adc_resource[] = {
};
struct platform_device s3c_device_adc = {
- .name = "s3c2410-adc",
+ .name = "s3c24xx-adc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_adc_resource),
.resource = s3c_adc_resource,
};
+/* HWMON */
+
+struct platform_device s3c_device_hwmon = {
+ .name = "s3c24xx-hwmon",
+ .id = -1,
+ .dev.parent = &s3c_device_adc.dev,
+};
+
/* SDI */
static struct resource s3c_sdi_resource[] = {
[0] = {
- .start = S3C2410_PA_SDI,
- .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
+ .start = S3C24XX_PA_SDI,
+ .end = S3C24XX_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
@@ -403,36 +408,6 @@ struct platform_device s3c_device_sdi = {
EXPORT_SYMBOL(s3c_device_sdi);
-/* High-speed MMC/SD */
-
-static struct resource s3c_hsmmc_resource[] = {
- [0] = {
- .start = S3C2443_PA_HSMMC,
- .end = S3C2443_PA_HSMMC + S3C2443_SZ_HSMMC - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_S3C2443_HSMMC,
- .end = IRQ_S3C2443_HSMMC,
- .flags = IORESOURCE_IRQ,
- }
-};
-
-static u64 s3c_device_hsmmc_dmamask = 0xffffffffUL;
-
-struct platform_device s3c_device_hsmmc = {
- .name = "s3c-sdhci",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_hsmmc_resource),
- .resource = s3c_hsmmc_resource,
- .dev = {
- .dma_mask = &s3c_device_hsmmc_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
-};
-
-
-
/* SPI (0) */
static struct resource s3c_spi0_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c
index 1baf941d1930..aee2aeb46c60 100644
--- a/arch/arm/plat-s3c24xx/dma.c
+++ b/arch/arm/plat-s3c24xx/dma.c
@@ -25,15 +25,13 @@
#include <linux/sysdev.h>
#include <linux/slab.h>
#include <linux/errno.h>
-#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
-#include <asm/mach/dma.h>
#include <mach/map.h>
#include <plat/dma.h>
@@ -804,7 +802,7 @@ EXPORT_SYMBOL(s3c2410_dma_request);
* allowed to go through.
*/
-int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client)
+int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
unsigned long flags;
@@ -995,7 +993,7 @@ static int s3c2410_dma_started(struct s3c2410_dma_chan *chan)
}
int
-s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op)
+s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
@@ -1043,7 +1041,7 @@ EXPORT_SYMBOL(s3c2410_dma_ctrl);
* dcon: base value of the DCONx register
*/
-int s3c2410_dma_config(dmach_t channel,
+int s3c2410_dma_config(unsigned int channel,
int xferunit,
int dcon)
{
@@ -1092,7 +1090,7 @@ int s3c2410_dma_config(dmach_t channel,
EXPORT_SYMBOL(s3c2410_dma_config);
-int s3c2410_dma_setflags(dmach_t channel, unsigned int flags)
+int s3c2410_dma_setflags(unsigned int channel, unsigned int flags)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
@@ -1113,7 +1111,7 @@ EXPORT_SYMBOL(s3c2410_dma_setflags);
* irq?
*/
-int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn)
+int s3c2410_dma_set_opfn(unsigned int channel, s3c2410_dma_opfn_t rtn)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
@@ -1129,7 +1127,7 @@ int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn)
EXPORT_SYMBOL(s3c2410_dma_set_opfn);
-int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
+int s3c2410_dma_set_buffdone_fn(unsigned int channel, s3c2410_dma_cbfn_t rtn)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
@@ -1219,7 +1217,7 @@ EXPORT_SYMBOL(s3c2410_dma_devconfig);
* returns the current transfer points for the dma source and destination
*/
-int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst)
+int s3c2410_dma_getposition(unsigned int channel, dma_addr_t *src, dma_addr_t *dst)
{
struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c
index 3caec6bad3eb..f95c6c9d9f1a 100644
--- a/arch/arm/plat-s3c24xx/gpiolib.c
+++ b/arch/arm/plat-s3c24xx/gpiolib.c
@@ -19,104 +19,12 @@
#include <linux/io.h>
#include <linux/gpio.h>
+#include <plat/gpio-core.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
-struct s3c24xx_gpio_chip {
- struct gpio_chip chip;
- void __iomem *base;
-};
-
-static inline struct s3c24xx_gpio_chip *to_s3c_chip(struct gpio_chip *gpc)
-{
- return container_of(gpc, struct s3c24xx_gpio_chip, chip);
-}
-
-/* these routines are exported for use by other parts of the platform
- * and system support, but are not intended to be used directly by the
- * drivers themsevles.
- */
-
-static int s3c24xx_gpiolib_input(struct gpio_chip *chip, unsigned offset)
-{
- struct s3c24xx_gpio_chip *ourchip = to_s3c_chip(chip);
- void __iomem *base = ourchip->base;
- unsigned long flags;
- unsigned long con;
-
- local_irq_save(flags);
-
- con = __raw_readl(base + 0x00);
- con &= ~(3 << (offset * 2));
- con |= (S3C2410_GPIO_OUTPUT & 0xf) << (offset * 2);
-
- __raw_writel(con, base + 0x00);
-
- local_irq_restore(flags);
- return 0;
-}
-
-static int s3c24xx_gpiolib_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct s3c24xx_gpio_chip *ourchip = to_s3c_chip(chip);
- void __iomem *base = ourchip->base;
- unsigned long flags;
- unsigned long dat;
- unsigned long con;
-
- local_irq_save(flags);
-
- dat = __raw_readl(base + 0x04);
- dat &= ~(1 << offset);
- if (value)
- dat |= 1 << offset;
- __raw_writel(dat, base + 0x04);
-
- con = __raw_readl(base + 0x00);
- con &= ~(3 << (offset * 2));
- con |= (S3C2410_GPIO_OUTPUT & 0xf) << (offset * 2);
-
- __raw_writel(con, base + 0x00);
- __raw_writel(dat, base + 0x04);
-
- local_irq_restore(flags);
- return 0;
-}
-
-static void s3c24xx_gpiolib_set(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct s3c24xx_gpio_chip *ourchip = to_s3c_chip(chip);
- void __iomem *base = ourchip->base;
- unsigned long flags;
- unsigned long dat;
-
- local_irq_save(flags);
-
- dat = __raw_readl(base + 0x04);
- dat &= ~(1 << offset);
- if (value)
- dat |= 1 << offset;
- __raw_writel(dat, base + 0x04);
-
- local_irq_restore(flags);
-}
-
-static int s3c24xx_gpiolib_get(struct gpio_chip *chip, unsigned offset)
-{
- struct s3c24xx_gpio_chip *ourchip = to_s3c_chip(chip);
- unsigned long val;
-
- val = __raw_readl(ourchip->base + 0x04);
- val >>= offset;
- val &= 1;
-
- return val;
-}
-
static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset)
{
return -EINVAL;
@@ -125,7 +33,7 @@ static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset)
static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip,
unsigned offset, int value)
{
- struct s3c24xx_gpio_chip *ourchip = to_s3c_chip(chip);
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
void __iomem *base = ourchip->base;
unsigned long flags;
unsigned long dat;
@@ -151,7 +59,7 @@ static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip,
return 0;
}
-static struct s3c24xx_gpio_chip gpios[] = {
+struct s3c_gpio_chip s3c24xx_gpios[] = {
[0] = {
.base = S3C24XX_GPIO_BASE(S3C2410_GPA0),
.chip = {
@@ -161,8 +69,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.ngpio = 24,
.direction_input = s3c24xx_gpiolib_banka_input,
.direction_output = s3c24xx_gpiolib_banka_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[1] = {
@@ -172,10 +78,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.owner = THIS_MODULE,
.label = "GPIOB",
.ngpio = 16,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[2] = {
@@ -185,10 +87,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.owner = THIS_MODULE,
.label = "GPIOC",
.ngpio = 16,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[3] = {
@@ -198,10 +96,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.owner = THIS_MODULE,
.label = "GPIOD",
.ngpio = 16,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[4] = {
@@ -211,10 +105,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.label = "GPIOE",
.owner = THIS_MODULE,
.ngpio = 16,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[5] = {
@@ -224,10 +114,6 @@ static struct s3c24xx_gpio_chip gpios[] = {
.owner = THIS_MODULE,
.label = "GPIOF",
.ngpio = 8,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
[6] = {
@@ -237,21 +123,17 @@ static struct s3c24xx_gpio_chip gpios[] = {
.owner = THIS_MODULE,
.label = "GPIOG",
.ngpio = 10,
- .direction_input = s3c24xx_gpiolib_input,
- .direction_output = s3c24xx_gpiolib_output,
- .set = s3c24xx_gpiolib_set,
- .get = s3c24xx_gpiolib_get,
},
},
};
static __init int s3c24xx_gpiolib_init(void)
{
- struct s3c24xx_gpio_chip *chip = gpios;
+ struct s3c_gpio_chip *chip = s3c24xx_gpios;
int gpn;
- for (gpn = 0; gpn < ARRAY_SIZE(gpios); gpn++, chip++)
- gpiochip_add(&chip->chip);
+ for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++)
+ s3c_gpiolib_add(chip);
return 0;
}
diff --git a/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
new file mode 100644
index 000000000000..a087de21bc20
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
@@ -0,0 +1,55 @@
+/* linux/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C24xx - pwm clock and timer support
+ */
+
+/**
+ * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
+ * @cfg: The timer TCFG1 register bits shifted down to 0.
+ *
+ * Return true if the given configuration from TCFG1 is a TCLK instead
+ * any of the TDIV clocks.
+ */
+static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
+{
+ return tcfg == S3C2410_TCFG1_MUX_TCLK;
+}
+
+/**
+ * tcfg_to_divisor() - convert tcfg1 setting to a divisor
+ * @tcfg1: The tcfg1 setting, shifted down.
+ *
+ * Get the divisor value for the given tcfg1 setting. We assume the
+ * caller has already checked to see if this is not a TCLK source.
+ */
+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
+{
+ return 1 << (1 + tcfg1);
+}
+
+/**
+ * pwm_tdiv_has_div1() - does the tdiv setting have a /1
+ *
+ * Return true if we have a /1 in the tdiv setting.
+ */
+static inline unsigned int pwm_tdiv_has_div1(void)
+{
+ return 0;
+}
+
+/**
+ * pwm_tdiv_div_bits() - calculate TCFG1 divisor value.
+ * @div: The divisor to calculate the bit information for.
+ *
+ * Turn a divisor into the necessary bit field for TCFG1.
+ */
+static inline unsigned long pwm_tdiv_div_bits(unsigned int div)
+{
+ return ilog2(div) - 1;
+}
+
+#define S3C_TCFG1_MUX_TCLK S3C2410_TCFG1_MUX_TCLK
diff --git a/arch/arm/plat-s3c24xx/include/plat/clock.h b/arch/arm/plat-s3c24xx/include/plat/clock.h
deleted file mode 100644
index 235b753cd877..000000000000
--- a/arch/arm/plat-s3c24xx/include/plat/clock.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* linux/include/asm-arm/plat-s3c24xx/clock.h
- * linux/arch/arm/mach-s3c2410/clock.h
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- * http://www.simtec.co.uk/products/SWLINUX/
- * Written by Ben Dooks, <ben@simtec.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-struct clk {
- struct list_head list;
- struct module *owner;
- struct clk *parent;
- const char *name;
- int id;
- int usage;
- unsigned long rate;
- unsigned long ctrlbit;
-
- int (*enable)(struct clk *, int enable);
- int (*set_rate)(struct clk *c, unsigned long rate);
- unsigned long (*get_rate)(struct clk *c);
- unsigned long (*round_rate)(struct clk *c, unsigned long rate);
- int (*set_parent)(struct clk *c, struct clk *parent);
-};
-
-/* other clocks which may be registered by board support */
-
-extern struct clk s3c24xx_dclk0;
-extern struct clk s3c24xx_dclk1;
-extern struct clk s3c24xx_clkout0;
-extern struct clk s3c24xx_clkout1;
-extern struct clk s3c24xx_uclk;
-
-extern struct clk clk_usb_bus;
-
-/* core clock support */
-
-extern struct clk clk_f;
-extern struct clk clk_h;
-extern struct clk clk_p;
-extern struct clk clk_mpll;
-extern struct clk clk_upll;
-extern struct clk clk_xtal;
-
-/* exports for arch/arm/mach-s3c2410
- *
- * Please DO NOT use these outside of arch/arm/mach-s3c2410
-*/
-
-extern struct mutex clocks_mutex;
-
-extern int s3c2410_clkcon_enable(struct clk *clk, int enable);
-
-extern int s3c24xx_register_clock(struct clk *clk);
-extern int s3c24xx_register_clocks(struct clk **clk, int nr_clks);
-
-extern int s3c24xx_setup_clocks(unsigned long xtal,
- unsigned long fclk,
- unsigned long hclk,
- unsigned long pclk);
diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu.h b/arch/arm/plat-s3c24xx/include/plat/cpu.h
deleted file mode 100644
index 23e420e8bd5b..000000000000
--- a/arch/arm/plat-s3c24xx/include/plat/cpu.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* linux/include/asm-arm/plat-s3c24xx/cpu.h
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Header file for S3C24XX CPU support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-/* todo - fix when rmk changes iodescs to use `void __iomem *` */
-
-#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, __phys_to_pfn(S3C24XX_PA_##x), S3C24XX_SZ_##x, MT_DEVICE }
-
-#ifndef MHZ
-#define MHZ (1000*1000)
-#endif
-
-#define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000)
-
-/* forward declaration */
-struct s3c24xx_uart_resources;
-struct platform_device;
-struct s3c2410_uartcfg;
-struct map_desc;
-
-/* core initialisation functions */
-
-extern void s3c24xx_init_irq(void);
-
-extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
-
-extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no);
-
-extern void s3c24xx_init_clocks(int xtal);
-
-extern void s3c24xx_init_uartdevs(char *name,
- struct s3c24xx_uart_resources *res,
- struct s3c2410_uartcfg *cfg, int no);
-
-/* timer for 2410/2440 */
-
-struct sys_timer;
-extern struct sys_timer s3c24xx_timer;
-
-/* system device classes */
-
-extern struct sysdev_class s3c2410_sysclass;
-extern struct sysdev_class s3c2412_sysclass;
-extern struct sysdev_class s3c2440_sysclass;
-extern struct sysdev_class s3c2442_sysclass;
-extern struct sysdev_class s3c2443_sysclass;
diff --git a/arch/arm/plat-s3c24xx/include/plat/devs.h b/arch/arm/plat-s3c24xx/include/plat/devs.h
deleted file mode 100644
index badaac9d64a8..000000000000
--- a/arch/arm/plat-s3c24xx/include/plat/devs.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* linux/include/asm-arm/plat-s3c24xx/devs.h
- *
- * Copyright (c) 2004 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * Header file for s3c2410 standard platform devices
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-#include <linux/platform_device.h>
-
-struct s3c24xx_uart_resources {
- struct resource *resources;
- unsigned long nr_resources;
-};
-
-extern struct s3c24xx_uart_resources s3c2410_uart_resources[];
-
-extern struct platform_device *s3c24xx_uart_devs[];
-extern struct platform_device *s3c24xx_uart_src[];
-
-extern struct platform_device s3c_device_timer[];
-
-extern struct platform_device s3c_device_usb;
-extern struct platform_device s3c_device_lcd;
-extern struct platform_device s3c_device_wdt;
-extern struct platform_device s3c_device_i2c;
-extern struct platform_device s3c_device_iis;
-extern struct platform_device s3c_device_rtc;
-extern struct platform_device s3c_device_adc;
-extern struct platform_device s3c_device_sdi;
-extern struct platform_device s3c_device_hsmmc;
-
-extern struct platform_device s3c_device_spi0;
-extern struct platform_device s3c_device_spi1;
-
-extern struct platform_device s3c_device_nand;
-
-extern struct platform_device s3c_device_usbgadget;
-
-/* s3c2440 specific devices */
-
-#ifdef CONFIG_CPU_S3C2440
-
-extern struct platform_device s3c_device_camif;
-
-#endif
diff --git a/arch/arm/plat-s3c24xx/include/plat/map.h b/arch/arm/plat-s3c24xx/include/plat/map.h
new file mode 100644
index 000000000000..fef8ea8b8e1e
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/map.h
@@ -0,0 +1,99 @@
+/* linux/include/asm-arm/plat-s3c24xx/map.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX - Memory map definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_S3C24XX_MAP_H
+#define __ASM_PLAT_S3C24XX_MAP_H
+
+/* interrupt controller is the first thing we put in, to make
+ * the assembly code for the irq detection easier
+ */
+#define S3C24XX_VA_IRQ S3C_VA_IRQ
+#define S3C2410_PA_IRQ (0x4A000000)
+#define S3C24XX_SZ_IRQ SZ_1M
+
+/* memory controller registers */
+#define S3C24XX_VA_MEMCTRL S3C_VA_MEM
+#define S3C2410_PA_MEMCTRL (0x48000000)
+#define S3C24XX_SZ_MEMCTRL SZ_1M
+
+/* UARTs */
+#define S3C24XX_VA_UART S3C_VA_UART
+#define S3C2410_PA_UART (0x50000000)
+#define S3C24XX_SZ_UART SZ_1M
+#define S3C_UART_OFFSET (0x4000)
+
+/* Timers */
+#define S3C24XX_VA_TIMER S3C_VA_TIMER
+#define S3C2410_PA_TIMER (0x51000000)
+#define S3C24XX_SZ_TIMER SZ_1M
+
+/* Clock and Power management */
+#define S3C24XX_VA_CLKPWR S3C_VA_SYS
+#define S3C24XX_SZ_CLKPWR SZ_1M
+
+/* USB Device port */
+#define S3C2410_PA_USBDEV (0x52000000)
+#define S3C24XX_SZ_USBDEV SZ_1M
+
+/* Watchdog */
+#define S3C24XX_VA_WATCHDOG S3C_VA_WATCHDOG
+#define S3C2410_PA_WATCHDOG (0x53000000)
+#define S3C24XX_SZ_WATCHDOG SZ_1M
+
+/* Standard size definitions for peripheral blocks. */
+
+#define S3C24XX_SZ_IIS SZ_1M
+#define S3C24XX_SZ_ADC SZ_1M
+#define S3C24XX_SZ_SPI SZ_1M
+#define S3C24XX_SZ_SDI SZ_1M
+#define S3C24XX_SZ_NAND SZ_1M
+#define S3C24XX_SZ_USBHOST SZ_1M
+
+/* GPIO ports */
+
+/* the calculation for the VA of this must ensure that
+ * it is the same distance apart from the UART in the
+ * phsyical address space, as the initial mapping for the IO
+ * is done as a 1:1 maping. This puts it (currently) at
+ * 0xFA800000, which is not in the way of any current mapping
+ * by the base system.
+*/
+
+#define S3C2410_PA_GPIO (0x56000000)
+#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
+#define S3C24XX_SZ_GPIO SZ_1M
+
+
+/* ISA style IO, for each machine to sort out mappings for, if it
+ * implements it. We reserve two 16M regions for ISA.
+ */
+
+#define S3C24XX_VA_ISA_WORD S3C2410_ADDR(0x02000000)
+#define S3C24XX_VA_ISA_BYTE S3C2410_ADDR(0x03000000)
+
+/* deal with the registers that move under the 2412/2413 */
+
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#ifndef __ASSEMBLY__
+extern void __iomem *s3c24xx_va_gpio2;
+#endif
+#ifdef CONFIG_CPU_S3C2412_ONLY
+#define S3C24XX_VA_GPIO2 (S3C24XX_VA_GPIO + 0x10)
+#else
+#define S3C24XX_VA_GPIO2 s3c24xx_va_gpio2
+#endif
+#else
+#define s3c24xx_va_gpio2 S3C24XX_VA_GPIO
+#define S3C24XX_VA_GPIO2 S3C24XX_VA_GPIO
+#endif
+
+#endif /* __ASM_PLAT_S3C24XX_MAP_H */
diff --git a/include/asm-arm/plat-s3c24xx/mci.h b/arch/arm/plat-s3c24xx/include/plat/mci.h
index 2d0852ac3b27..2d0852ac3b27 100644
--- a/include/asm-arm/plat-s3c24xx/mci.h
+++ b/arch/arm/plat-s3c24xx/include/plat/mci.h
diff --git a/arch/arm/plat-s3c24xx/include/plat/pll.h b/arch/arm/plat-s3c24xx/include/plat/pll.h
new file mode 100644
index 000000000000..7ea8bffa7a9c
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/pll.h
@@ -0,0 +1,37 @@
+/* linux/arch/arm/plat-s3c24xx/include/plat/pll.h
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C24xx - common pll registers and code
+ */
+
+#define S3C24XX_PLLCON_MDIVSHIFT 12
+#define S3C24XX_PLLCON_PDIVSHIFT 4
+#define S3C24XX_PLLCON_SDIVSHIFT 0
+#define S3C24XX_PLLCON_MDIVMASK ((1<<(1+(19-12)))-1)
+#define S3C24XX_PLLCON_PDIVMASK ((1<<5)-1)
+#define S3C24XX_PLLCON_SDIVMASK 3
+
+#include <asm/div64.h>
+
+static inline unsigned int
+s3c24xx_get_pll(unsigned int pllval, unsigned int baseclk)
+{
+ unsigned int mdiv, pdiv, sdiv;
+ uint64_t fvco;
+
+ mdiv = pllval >> S3C24XX_PLLCON_MDIVSHIFT;
+ pdiv = pllval >> S3C24XX_PLLCON_PDIVSHIFT;
+ sdiv = pllval >> S3C24XX_PLLCON_SDIVSHIFT;
+
+ mdiv &= S3C24XX_PLLCON_MDIVMASK;
+ pdiv &= S3C24XX_PLLCON_PDIVMASK;
+ sdiv &= S3C24XX_PLLCON_SDIVMASK;
+
+ fvco = (uint64_t)baseclk * (mdiv + 8);
+ do_div(fvco, (pdiv + 2) << sdiv);
+
+ return (unsigned int)fvco;
+}
diff --git a/include/asm-arm/plat-s3c24xx/regs-spi.h b/arch/arm/plat-s3c24xx/include/plat/regs-spi.h
index 2b35479ee35c..2b35479ee35c 100644
--- a/include/asm-arm/plat-s3c24xx/regs-spi.h
+++ b/arch/arm/plat-s3c24xx/include/plat/regs-spi.h
diff --git a/include/asm-arm/plat-s3c24xx/regs-udc.h b/arch/arm/plat-s3c24xx/include/plat/regs-udc.h
index f0dd4a41b37b..f0dd4a41b37b 100644
--- a/include/asm-arm/plat-s3c24xx/regs-udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/regs-udc.h
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2400.h b/arch/arm/plat-s3c24xx/include/plat/s3c2400.h
index 3a5a16821af8..b3feaea5c70b 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2400.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2400.h
@@ -17,7 +17,7 @@
extern int s3c2400_init(void);
-extern void s3c2400_map_io(struct map_desc *mach_desc, int size);
+extern void s3c2400_map_io(void);
extern void s3c2400_init_uarts(struct s3c2410_uartcfg *cfg, int no);
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
index 3cd1ec677b3f..a9ac9e29759e 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
@@ -15,7 +15,7 @@
extern int s3c2410_init(void);
-extern void s3c2410_map_io(struct map_desc *mach_desc, int size);
+extern void s3c2410_map_io(void);
extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no);
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2412.h b/arch/arm/plat-s3c24xx/include/plat/s3c2412.h
index 3ec97685e781..bb15d3b68be5 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2412.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2412.h
@@ -14,7 +14,7 @@
extern int s3c2412_init(void);
-extern void s3c2412_map_io(struct map_desc *mach_desc, int size);
+extern void s3c2412_map_io(void);
extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no);
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2443.h b/arch/arm/plat-s3c24xx/include/plat/s3c2443.h
index 11d83b5c84e6..815b107ed890 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2443.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2443.h
@@ -16,7 +16,7 @@ struct s3c2410_uartcfg;
extern int s3c2443_init(void);
-extern void s3c2443_map_io(struct map_desc *mach_desc, int size);
+extern void s3c2443_map_io(void);
extern void s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no);
diff --git a/include/asm-arm/plat-s3c24xx/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 546bb4008f49..546bb4008f49 100644
--- a/include/asm-arm/plat-s3c24xx/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
index 963f7a4f26f2..0192ecdc1442 100644
--- a/arch/arm/plat-s3c24xx/irq.c
+++ b/arch/arm/plat-s3c24xx/irq.c
@@ -62,6 +62,7 @@
#include <asm/mach/irq.h>
+#include <plat/regs-irqtype.h>
#include <mach/regs-irq.h>
#include <mach/regs-gpio.h>
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c
index 8efb57ad5019..34ef18e5b2a1 100644
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -33,7 +33,6 @@
#include <linux/interrupt.h>
#include <linux/crc32.h>
#include <linux/ioport.h>
-#include <linux/delay.h>
#include <linux/serial_core.h>
#include <linux/io.h>
@@ -76,11 +75,13 @@ static struct sleep_save core_save[] = {
SAVE_ITEM(S3C2410_BANKCON4),
SAVE_ITEM(S3C2410_BANKCON5),
+#ifndef CONFIG_CPU_FREQ
SAVE_ITEM(S3C2410_CLKDIVN),
SAVE_ITEM(S3C2410_MPLLCON),
+ SAVE_ITEM(S3C2410_REFRESH),
+#endif
SAVE_ITEM(S3C2410_UPLLCON),
SAVE_ITEM(S3C2410_CLKSLOW),
- SAVE_ITEM(S3C2410_REFRESH),
};
static struct gpio_sleep {
diff --git a/arch/arm/plat-s3c24xx/pwm-clock.c b/arch/arm/plat-s3c24xx/pwm-clock.c
deleted file mode 100644
index 3fad68a1e6bc..000000000000
--- a/arch/arm/plat-s3c24xx/pwm-clock.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
- *
- * Copyright (c) 2007 Simtec Electronics
- * Copyright (c) 2007, 2008 Ben Dooks
- * Ben Dooks <ben-linux@fluff.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.
-*/
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/irq.h>
-
-#include <mach/regs-clock.h>
-#include <mach/regs-gpio.h>
-
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-#include <plat/regs-timer.h>
-
-/* Each of the timers 0 through 5 go through the following
- * clock tree, with the inputs depending on the timers.
- *
- * pclk ---- [ prescaler 0 ] -+---> timer 0
- * +---> timer 1
- *
- * pclk ---- [ prescaler 1 ] -+---> timer 2
- * +---> timer 3
- * \---> timer 4
- *
- * Which are fed into the timers as so:
- *
- * prescaled 0 ---- [ div 2,4,8,16 ] ---\
- * [mux] -> timer 0
- * tclk 0 ------------------------------/
- *
- * prescaled 0 ---- [ div 2,4,8,16 ] ---\
- * [mux] -> timer 1
- * tclk 0 ------------------------------/
- *
- *
- * prescaled 1 ---- [ div 2,4,8,16 ] ---\
- * [mux] -> timer 2
- * tclk 1 ------------------------------/
- *
- * prescaled 1 ---- [ div 2,4,8,16 ] ---\
- * [mux] -> timer 3
- * tclk 1 ------------------------------/
- *
- * prescaled 1 ---- [ div 2,4,8, 16 ] --\
- * [mux] -> timer 4
- * tclk 1 ------------------------------/
- *
- * Since the mux and the divider are tied together in the
- * same register space, it is impossible to set the parent
- * and the rate at the same time. To avoid this, we add an
- * intermediate 'prescaled-and-divided' clock to select
- * as the parent for the timer input clock called tdiv.
- *
- * prescaled clk --> pwm-tdiv ---\
- * [ mux ] --> timer X
- * tclk -------------------------/
-*/
-
-static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
-{
- unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
-
- if (clk->id == 1) {
- tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
- tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
- } else {
- tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
- }
-
- return clk_get_rate(clk->parent) / (tcfg0 + 1);
-}
-
-/* TODO - add set rate calls. */
-
-static struct clk clk_timer_scaler[] = {
- [0] = {
- .name = "pwm-scaler0",
- .id = -1,
- .get_rate = clk_pwm_scaler_getrate,
- },
- [1] = {
- .name = "pwm-scaler1",
- .id = -1,
- .get_rate = clk_pwm_scaler_getrate,
- },
-};
-
-static struct clk clk_timer_tclk[] = {
- [0] = {
- .name = "pwm-tclk0",
- .id = -1,
- },
- [1] = {
- .name = "pwm-tclk1",
- .id = -1,
- },
-};
-
-struct pwm_tdiv_clk {
- struct clk clk;
- unsigned int divisor;
-};
-
-static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
-{
- return container_of(clk, struct pwm_tdiv_clk, clk);
-}
-
-static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
-{
- return 1 << (1 + tcfg1);
-}
-
-static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
-{
- unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
- unsigned int divisor;
-
- tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
- tcfg1 &= S3C2410_TCFG1_MUX_MASK;
-
- if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
- divisor = to_tdiv(clk)->divisor;
- else
- divisor = tcfg_to_divisor(tcfg1);
-
- return clk_get_rate(clk->parent) / divisor;
-}
-
-static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
- unsigned long rate)
-{
- unsigned long parent_rate;
- unsigned long divisor;
-
- parent_rate = clk_get_rate(clk->parent);
- divisor = parent_rate / rate;
-
- if (divisor <= 2)
- divisor = 2;
- else if (divisor <= 4)
- divisor = 4;
- else if (divisor <= 8)
- divisor = 8;
- else
- divisor = 16;
-
- return parent_rate / divisor;
-}
-
-static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
-{
- unsigned long bits;
-
- switch (divclk->divisor) {
- case 2:
- bits = S3C2410_TCFG1_MUX_DIV2;
- break;
- case 4:
- bits = S3C2410_TCFG1_MUX_DIV4;
- break;
- case 8:
- bits = S3C2410_TCFG1_MUX_DIV8;
- break;
- case 16:
- default:
- bits = S3C2410_TCFG1_MUX_DIV16;
- break;
- }
-
- return bits;
-}
-
-static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
-{
- unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
- unsigned long bits = clk_pwm_tdiv_bits(divclk);
- unsigned long flags;
- unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id);
-
- local_irq_save(flags);
-
- tcfg1 = __raw_readl(S3C2410_TCFG1);
- tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
- tcfg1 |= bits << shift;
- __raw_writel(tcfg1, S3C2410_TCFG1);
-
- local_irq_restore(flags);
-}
-
-static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
-{
- struct pwm_tdiv_clk *divclk = to_tdiv(clk);
- unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
- unsigned long parent_rate = clk_get_rate(clk->parent);
- unsigned long divisor;
-
- tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
- tcfg1 &= S3C2410_TCFG1_MUX_MASK;
-
- rate = clk_round_rate(clk, rate);
- divisor = parent_rate / rate;
-
- if (divisor > 16)
- return -EINVAL;
-
- divclk->divisor = divisor;
-
- /* Update the current MUX settings if we are currently
- * selected as the clock source for this clock. */
-
- if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
- clk_pwm_tdiv_update(divclk);
-
- return 0;
-}
-
-static struct pwm_tdiv_clk clk_timer_tdiv[] = {
- [0] = {
- .clk = {
- .name = "pwm-tdiv",
- .parent = &clk_timer_scaler[0],
- .get_rate = clk_pwm_tdiv_get_rate,
- .set_rate = clk_pwm_tdiv_set_rate,
- .round_rate = clk_pwm_tdiv_round_rate,
- },
- },
- [1] = {
- .clk = {
- .name = "pwm-tdiv",
- .parent = &clk_timer_scaler[0],
- .get_rate = clk_pwm_tdiv_get_rate,
- .set_rate = clk_pwm_tdiv_set_rate,
- .round_rate = clk_pwm_tdiv_round_rate,
- }
- },
- [2] = {
- .clk = {
- .name = "pwm-tdiv",
- .parent = &clk_timer_scaler[1],
- .get_rate = clk_pwm_tdiv_get_rate,
- .set_rate = clk_pwm_tdiv_set_rate,
- .round_rate = clk_pwm_tdiv_round_rate,
- },
- },
- [3] = {
- .clk = {
- .name = "pwm-tdiv",
- .parent = &clk_timer_scaler[1],
- .get_rate = clk_pwm_tdiv_get_rate,
- .set_rate = clk_pwm_tdiv_set_rate,
- .round_rate = clk_pwm_tdiv_round_rate,
- },
- },
- [4] = {
- .clk = {
- .name = "pwm-tdiv",
- .parent = &clk_timer_scaler[1],
- .get_rate = clk_pwm_tdiv_get_rate,
- .set_rate = clk_pwm_tdiv_set_rate,
- .round_rate = clk_pwm_tdiv_round_rate,
- },
- },
-};
-
-static int __init clk_pwm_tdiv_register(unsigned int id)
-{
- struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
- unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
-
- tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
- tcfg1 &= S3C2410_TCFG1_MUX_MASK;
-
- divclk->clk.id = id;
- divclk->divisor = tcfg_to_divisor(tcfg1);
-
- return s3c24xx_register_clock(&divclk->clk);
-}
-
-static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
-{
- return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
-}
-
-static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
-{
- return &clk_timer_tdiv[id].clk;
-}
-
-static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
-{
- unsigned int id = clk->id;
- unsigned long tcfg1;
- unsigned long flags;
- unsigned long bits;
- unsigned long shift = S3C2410_TCFG1_SHIFT(id);
-
- if (parent == s3c24xx_pwmclk_tclk(id))
- bits = S3C2410_TCFG1_MUX_TCLK << shift;
- else if (parent == s3c24xx_pwmclk_tdiv(id))
- bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
- else
- return -EINVAL;
-
- clk->parent = parent;
-
- local_irq_save(flags);
-
- tcfg1 = __raw_readl(S3C2410_TCFG1);
- tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
- __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
-
- local_irq_restore(flags);
-
- return 0;
-}
-
-static struct clk clk_tin[] = {
- [0] = {
- .name = "pwm-tin",
- .id = 0,
- .set_parent = clk_pwm_tin_set_parent,
- },
- [1] = {
- .name = "pwm-tin",
- .id = 1,
- .set_parent = clk_pwm_tin_set_parent,
- },
- [2] = {
- .name = "pwm-tin",
- .id = 2,
- .set_parent = clk_pwm_tin_set_parent,
- },
- [3] = {
- .name = "pwm-tin",
- .id = 3,
- .set_parent = clk_pwm_tin_set_parent,
- },
- [4] = {
- .name = "pwm-tin",
- .id = 4,
- .set_parent = clk_pwm_tin_set_parent,
- },
-};
-
-static __init int clk_pwm_tin_register(struct clk *pwm)
-{
- unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
- unsigned int id = pwm->id;
-
- struct clk *parent;
- int ret;
-
- ret = s3c24xx_register_clock(pwm);
- if (ret < 0)
- return ret;
-
- tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
- tcfg1 &= S3C2410_TCFG1_MUX_MASK;
-
- if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
- parent = s3c24xx_pwmclk_tclk(id);
- else
- parent = s3c24xx_pwmclk_tdiv(id);
-
- return clk_set_parent(pwm, parent);
-}
-
-static __init int s3c24xx_pwmclk_init(void)
-{
- struct clk *clk_timers;
- unsigned int clk;
- int ret;
-
- clk_timers = clk_get(NULL, "timers");
- if (IS_ERR(clk_timers)) {
- printk(KERN_ERR "%s: no parent clock\n", __func__);
- return -EINVAL;
- }
-
- for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
- clk_timer_scaler[clk].parent = clk_timers;
- ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
- if (ret < 0) {
- printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
- goto err;
- }
- }
-
- for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
- ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
- if (ret < 0) {
- printk(KERN_ERR "error adding pww tclk%d\n", clk);
- goto err;
- }
- }
-
- for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
- ret = clk_pwm_tdiv_register(clk);
- if (ret < 0) {
- printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
- goto err;
- }
- }
-
- for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
- ret = clk_pwm_tin_register(&clk_tin[clk]);
- if (ret < 0) {
- printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
- goto err;
- }
- }
-
- return 0;
-
- err:
- return ret;
-}
-
-arch_initcall(s3c24xx_pwmclk_init);
diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c
new file mode 100644
index 000000000000..b61bdb793734
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c
@@ -0,0 +1,277 @@
+/* linux/arch/arm/mach-s3c2410/clock.c
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410,S3C2440,S3C2442 Clock control support
+ *
+ * This program is free software; you can redistribute 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/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+
+#include <plat/s3c2410.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+
+int s3c2410_clkcon_enable(struct clk *clk, int enable)
+{
+ unsigned int clocks = clk->ctrlbit;
+ unsigned long clkcon;
+
+ clkcon = __raw_readl(S3C2410_CLKCON);
+
+ if (enable)
+ clkcon |= clocks;
+ else
+ clkcon &= ~clocks;
+
+ /* ensure none of the special function bits set */
+ clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
+
+ __raw_writel(clkcon, S3C2410_CLKCON);
+
+ return 0;
+}
+
+static int s3c2410_upll_enable(struct clk *clk, int enable)
+{
+ unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
+ unsigned long orig = clkslow;
+
+ if (enable)
+ clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
+ else
+ clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
+
+ __raw_writel(clkslow, S3C2410_CLKSLOW);
+
+ /* if we started the UPLL, then allow to settle */
+
+ if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
+ udelay(200);
+
+ return 0;
+}
+
+/* standard clock definitions */
+
+static struct clk init_clocks_disable[] = {
+ {
+ .name = "nand",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_NAND,
+ }, {
+ .name = "sdi",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_SDI,
+ }, {
+ .name = "adc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_ADC,
+ }, {
+ .name = "i2c",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_IIC,
+ }, {
+ .name = "iis",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_IIS,
+ }, {
+ .name = "spi",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_SPI,
+ }
+};
+
+static struct clk init_clocks[] = {
+ {
+ .name = "lcd",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_LCDC,
+ }, {
+ .name = "gpio",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_GPIO,
+ }, {
+ .name = "usb-host",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_USBH,
+ }, {
+ .name = "usb-device",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_USBD,
+ }, {
+ .name = "timers",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_PWMT,
+ }, {
+ .name = "uart",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_UART0,
+ }, {
+ .name = "uart",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_UART1,
+ }, {
+ .name = "uart",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_UART2,
+ }, {
+ .name = "rtc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c2410_clkcon_enable,
+ .ctrlbit = S3C2410_CLKCON_RTC,
+ }, {
+ .name = "watchdog",
+ .id = -1,
+ .parent = &clk_p,
+ .ctrlbit = 0,
+ }, {
+ .name = "usb-bus-host",
+ .id = -1,
+ .parent = &clk_usb_bus,
+ }, {
+ .name = "usb-bus-gadget",
+ .id = -1,
+ .parent = &clk_usb_bus,
+ },
+};
+
+/* s3c2410_baseclk_add()
+ *
+ * Add all the clocks used by the s3c2410 or compatible CPUs
+ * such as the S3C2440 and S3C2442.
+ *
+ * We cannot use a system device as we are needed before any
+ * of the init-calls that initialise the devices are actually
+ * done.
+*/
+
+int __init s3c2410_baseclk_add(void)
+{
+ unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
+ unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
+ struct clk *clkp;
+ struct clk *xtal;
+ int ret;
+ int ptr;
+
+ clk_upll.enable = s3c2410_upll_enable;
+
+ if (s3c24xx_register_clock(&clk_usb_bus) < 0)
+ printk(KERN_ERR "failed to register usb bus clock\n");
+
+ /* register clocks from clock array */
+
+ clkp = init_clocks;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
+ /* ensure that we note the clock state */
+
+ clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
+
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ }
+
+ /* We must be careful disabling the clocks we are not intending to
+ * be using at boot time, as subsystems such as the LCD which do
+ * their own DMA requests to the bus can cause the system to lockup
+ * if they where in the middle of requesting bus access.
+ *
+ * Disabling the LCD clock if the LCD is active is very dangerous,
+ * and therefore the bootloader should be careful to not enable
+ * the LCD clock if it is not needed.
+ */
+
+ /* install (and disable) the clocks we do not need immediately */
+
+ clkp = init_clocks_disable;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
+
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+
+ s3c2410_clkcon_enable(clkp, 0);
+ }
+
+ /* show the clock-slow value */
+
+ xtal = clk_get(NULL, "xtal");
+
+ printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
+ print_mhz(clk_get_rate(xtal) /
+ ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
+ (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
+ (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
+ (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
+
+ s3c_pwmclk_init();
+ return 0;
+}
diff --git a/arch/arm/plat-s3c24xx/s3c244x-clock.c b/arch/arm/plat-s3c24xx/s3c244x-clock.c
index 7c09773ff9fc..dde41f171aff 100644
--- a/arch/arm/plat-s3c24xx/s3c244x-clock.c
+++ b/arch/arm/plat-s3c24xx/s3c244x-clock.c
@@ -31,7 +31,6 @@
#include <linux/sysdev.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <linux/io.h>
@@ -102,13 +101,13 @@ static int s3c244x_clk_add(struct sys_device *sysdev)
if (clk_get_rate(clock_upll) > (94 * MHZ)) {
clk_usb_bus.rate = clk_get_rate(clock_upll) / 2;
- mutex_lock(&clocks_mutex);
+ spin_lock(&clocks_lock);
clkdivn = __raw_readl(S3C2410_CLKDIVN);
clkdivn |= S3C2440_CLKDIVN_UCLK;
__raw_writel(clkdivn, S3C2410_CLKDIVN);
- mutex_unlock(&clocks_mutex);
+ spin_unlock(&clocks_lock);
}
return 0;
diff --git a/arch/arm/plat-s3c24xx/s3c244x.c b/arch/arm/plat-s3c24xx/s3c244x.c
index c0344fac4a94..c1de6bb0101b 100644
--- a/arch/arm/plat-s3c24xx/s3c244x.c
+++ b/arch/arm/plat-s3c24xx/s3c244x.c
@@ -29,6 +29,8 @@
#include <mach/hardware.h>
#include <asm/irq.h>
+#include <plat/cpu-freq.h>
+
#include <mach/regs-clock.h>
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
@@ -42,6 +44,7 @@
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/pm.h>
+#include <plat/pll.h>
static struct map_desc s3c244x_iodesc[] __initdata = {
IODESC_ENT(CLKPWR),
@@ -56,32 +59,34 @@ void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no)
s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no);
}
-void __init s3c244x_map_io(struct map_desc *mach_desc, int size)
+void __init s3c244x_map_io(void)
{
/* register our io-tables */
iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
- iotable_init(mach_desc, size);
/* rename any peripherals used differing from the s3c2410 */
s3c_device_sdi.name = "s3c2440-sdi";
- s3c_device_i2c.name = "s3c2440-i2c";
+ s3c_device_i2c0.name = "s3c2440-i2c";
s3c_device_nand.name = "s3c2440-nand";
s3c_device_usbgadget.name = "s3c2440-usbgadget";
}
-void __init s3c244x_init_clocks(int xtal)
+void __init_or_cpufreq s3c244x_setup_clocks(void)
{
+ struct clk *xtal_clk;
unsigned long clkdiv;
unsigned long camdiv;
+ unsigned long xtal;
unsigned long hclk, fclk, pclk;
int hdiv = 1;
- /* now we've got our machine bits initialised, work out what
- * clocks we've got */
+ xtal_clk = clk_get(NULL, "xtal");
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
- fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;
+ fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;
clkdiv = __raw_readl(S3C2410_CLKDIVN);
camdiv = __raw_readl(S3C2440_CAMDIVN);
@@ -107,18 +112,24 @@ void __init s3c244x_init_clocks(int xtal)
}
hclk = fclk / hdiv;
- pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1);
+ pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);
/* print brief summary of clocks, etc */
printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
+ s3c24xx_setup_clocks(fclk, hclk, pclk);
+}
+
+void __init s3c244x_init_clocks(int xtal)
+{
/* initialise the clocks here, to allow other things like the
* console to use them, and to add new ones after the initialisation
*/
- s3c24xx_setup_clocks(xtal, fclk, hclk, pclk);
+ s3c24xx_register_baseclocks(xtal);
+ s3c244x_setup_clocks();
s3c2410_baseclk_add();
}
diff --git a/arch/arm/plat-s3c24xx/s3c244x.h b/arch/arm/plat-s3c24xx/s3c244x.h
index f8ed17676a35..6aab5eaae2b4 100644
--- a/arch/arm/plat-s3c24xx/s3c244x.h
+++ b/arch/arm/plat-s3c24xx/s3c244x.h
@@ -12,7 +12,7 @@
#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442)
-extern void s3c244x_map_io(struct map_desc *mach_desc, int size);
+extern void s3c244x_map_io(void);
extern void s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no);
diff --git a/arch/arm/plat-s3c24xx/setup-i2c.c b/arch/arm/plat-s3c24xx/setup-i2c.c
new file mode 100644
index 000000000000..d62b7e7fb355
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/setup-i2c.c
@@ -0,0 +1,25 @@
+/* linux/arch/arm/plat-s3c24xx/setup-i2c.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX Base setup for i2c device
+ *
+ * This program is free software; you can 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>
+
+struct platform_device;
+
+#include <plat/iic.h>
+#include <mach/hardware.h>
+#include <mach/regs-gpio.h>
+
+void s3c_i2c0_cfg_gpio(struct platform_device *dev)
+{
+ s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
+ s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
+}
diff --git a/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c b/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c
new file mode 100644
index 000000000000..8b403cbb53d2
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c
@@ -0,0 +1,37 @@
+/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpe11_12_13.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - gpio configuration for bus 0 on gpe11,12,13
+ *
+ * This program is free software; you can redistribute 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.
+*/
+
+#include <linux/kernel.h>
+
+#include <mach/hardware.h>
+
+#include <mach/spi.h>
+#include <mach/regs-gpio.h>
+
+void s3c24xx_spi_gpiocfg_bus0_gpe11_12_13(struct s3c2410_spi_info *spi,
+ int enable)
+{
+ if (enable) {
+ s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
+ s3c2410_gpio_pullup(S3C2410_GPE11, 0);
+ s3c2410_gpio_pullup(S3C2410_GPE13, 0);
+ } else {
+ s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPIO_INPUT);
+ s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPIO_INPUT);
+ s3c2410_gpio_pullup(S3C2410_GPE11, 1);
+ s3c2410_gpio_pullup(S3C2410_GPE12, 1);
+ s3c2410_gpio_pullup(S3C2410_GPE13, 1);
+ }
+}
diff --git a/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c b/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c
new file mode 100644
index 000000000000..8fccd4e549f0
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/spi-bus1-gpg5_6_7.c
@@ -0,0 +1,37 @@
+/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpg5_6_7.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - gpio configuration for bus 1 on gpg5,6,7
+ *
+ * This program is free software; you can redistribute 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.
+*/
+
+#include <linux/kernel.h>
+
+#include <mach/hardware.h>
+
+#include <mach/spi.h>
+#include <mach/regs-gpio.h>
+
+void s3c24xx_spi_gpiocfg_bus1_gpg5_6_7(struct s3c2410_spi_info *spi,
+ int enable)
+{
+ if (enable) {
+ s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPG7_SPICLK1);
+ s3c2410_gpio_cfgpin(S3C2410_GPG6, S3C2410_GPG6_SPIMOSI1);
+ s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPG5_SPIMISO1);
+ s3c2410_gpio_pullup(S3C2410_GPG5, 0);
+ s3c2410_gpio_pullup(S3C2410_GPG6, 0);
+ } else {
+ s3c2410_gpio_cfgpin(S3C2410_GPG7, S3C2410_GPIO_INPUT);
+ s3c2410_gpio_cfgpin(S3C2410_GPG5, S3C2410_GPIO_INPUT);
+ s3c2410_gpio_pullup(S3C2410_GPG5, 1);
+ s3c2410_gpio_pullup(S3C2410_GPG6, 1);
+ s3c2410_gpio_pullup(S3C2410_GPG7, 1);
+ }
+}
diff --git a/arch/arm/plat-s3c24xx/time.c b/arch/arm/plat-s3c24xx/time.c
deleted file mode 100644
index c51916236ac0..000000000000
--- a/arch/arm/plat-s3c24xx/time.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/* linux/arch/arm/plat-s3c24xx/time.c
- *
- * Copyright (C) 2003-2005 Simtec Electronics
- * Ben Dooks, <ben@simtec.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 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/sched.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-
-#include <asm/system.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include <asm/irq.h>
-#include <mach/map.h>
-#include <plat/regs-timer.h>
-#include <mach/regs-irq.h>
-#include <asm/mach/time.h>
-
-#include <plat/clock.h>
-#include <plat/cpu.h>
-
-static unsigned long timer_startval;
-static unsigned long timer_usec_ticks;
-
-#define TIMER_USEC_SHIFT 16
-
-/* we use the shifted arithmetic to work out the ratio of timer ticks
- * to usecs, as often the peripheral clock is not a nice even multiple
- * of 1MHz.
- *
- * shift of 14 and 15 are too low for the 12MHz, 16 seems to be ok
- * for the current HZ value of 200 without producing overflows.
- *
- * Original patch by Dimitry Andric, updated by Ben Dooks
-*/
-
-
-/* timer_mask_usec_ticks
- *
- * given a clock and divisor, make the value to pass into timer_ticks_to_usec
- * to scale the ticks into usecs
-*/
-
-static inline unsigned long
-timer_mask_usec_ticks(unsigned long scaler, unsigned long pclk)
-{
- unsigned long den = pclk / 1000;
-
- return ((1000 << TIMER_USEC_SHIFT) * scaler + (den >> 1)) / den;
-}
-
-/* timer_ticks_to_usec
- *
- * convert timer ticks to usec.
-*/
-
-static inline unsigned long timer_ticks_to_usec(unsigned long ticks)
-{
- unsigned long res;
-
- res = ticks * timer_usec_ticks;
- res += 1 << (TIMER_USEC_SHIFT - 4); /* round up slightly */
-
- return res >> TIMER_USEC_SHIFT;
-}
-
-/***
- * Returns microsecond since last clock interrupt. Note that interrupts
- * will have been disabled by do_gettimeoffset()
- * IRQs are disabled before entering here from do_gettimeofday()
- */
-
-#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0))
-
-static unsigned long s3c2410_gettimeoffset (void)
-{
- unsigned long tdone;
- unsigned long irqpend;
- unsigned long tval;
-
- /* work out how many ticks have gone since last timer interrupt */
-
- tval = __raw_readl(S3C2410_TCNTO(4));
- tdone = timer_startval - tval;
-
- /* check to see if there is an interrupt pending */
-
- irqpend = __raw_readl(S3C2410_SRCPND);
- if (irqpend & SRCPND_TIMER4) {
- /* re-read the timer, and try and fix up for the missed
- * interrupt. Note, the interrupt may go off before the
- * timer has re-loaded from wrapping.
- */
-
- tval = __raw_readl(S3C2410_TCNTO(4));
- tdone = timer_startval - tval;
-
- if (tval != 0)
- tdone += timer_startval;
- }
-
- return timer_ticks_to_usec(tdone);
-}
-
-
-/*
- * IRQ handler for the timer
- */
-static irqreturn_t
-s3c2410_timer_interrupt(int irq, void *dev_id)
-{
- timer_tick();
- return IRQ_HANDLED;
-}
-
-static struct irqaction s3c2410_timer_irq = {
- .name = "S3C2410 Timer Tick",
- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = s3c2410_timer_interrupt,
-};
-
-#define use_tclk1_12() ( \
- machine_is_bast() || \
- machine_is_vr1000() || \
- machine_is_anubis() || \
- machine_is_osiris() )
-
-/*
- * Set up timer interrupt, and return the current time in seconds.
- *
- * Currently we only use timer4, as it is the only timer which has no
- * other function that can be exploited externally
- */
-static void s3c2410_timer_setup (void)
-{
- unsigned long tcon;
- unsigned long tcnt;
- unsigned long tcfg1;
- unsigned long tcfg0;
-
- tcnt = 0xffff; /* default value for tcnt */
-
- /* read the current timer configuration bits */
-
- tcon = __raw_readl(S3C2410_TCON);
- tcfg1 = __raw_readl(S3C2410_TCFG1);
- tcfg0 = __raw_readl(S3C2410_TCFG0);
-
- /* configure the system for whichever machine is in use */
-
- if (use_tclk1_12()) {
- /* timer is at 12MHz, scaler is 1 */
- timer_usec_ticks = timer_mask_usec_ticks(1, 12000000);
- tcnt = 12000000 / HZ;
-
- tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
- tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1;
- } else {
- unsigned long pclk;
- struct clk *clk;
-
- /* for the h1940 (and others), we use the pclk from the core
- * to generate the timer values. since values around 50 to
- * 70MHz are not values we can directly generate the timer
- * value from, we need to pre-scale and divide before using it.
- *
- * for instance, using 50.7MHz and dividing by 6 gives 8.45MHz
- * (8.45 ticks per usec)
- */
-
- /* this is used as default if no other timer can be found */
-
- clk = clk_get(NULL, "timers");
- if (IS_ERR(clk))
- panic("failed to get clock for system timer");
-
- clk_enable(clk);
-
- pclk = clk_get_rate(clk);
-
- /* configure clock tick */
-
- timer_usec_ticks = timer_mask_usec_ticks(6, pclk);
-
- tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
- tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
-
- tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
- tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT;
-
- tcnt = (pclk / 6) / HZ;
- }
-
- /* timers reload after counting zero, so reduce the count by 1 */
-
- tcnt--;
-
- printk("timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx, usec %08lx\n",
- tcon, tcnt, tcfg0, tcfg1, timer_usec_ticks);
-
- /* check to see if timer is within 16bit range... */
- if (tcnt > 0xffff) {
- panic("setup_timer: HZ is too small, cannot configure timer!");
- return;
- }
-
- __raw_writel(tcfg1, S3C2410_TCFG1);
- __raw_writel(tcfg0, S3C2410_TCFG0);
-
- timer_startval = tcnt;
- __raw_writel(tcnt, S3C2410_TCNTB(4));
-
- /* ensure timer is stopped... */
-
- tcon &= ~(7<<20);
- tcon |= S3C2410_TCON_T4RELOAD;
- tcon |= S3C2410_TCON_T4MANUALUPD;
-
- __raw_writel(tcon, S3C2410_TCON);
- __raw_writel(tcnt, S3C2410_TCNTB(4));
- __raw_writel(tcnt, S3C2410_TCMPB(4));
-
- /* start the timer running */
- tcon |= S3C2410_TCON_T4START;
- tcon &= ~S3C2410_TCON_T4MANUALUPD;
- __raw_writel(tcon, S3C2410_TCON);
-}
-
-static void __init s3c2410_timer_init (void)
-{
- s3c2410_timer_setup();
- setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
-}
-
-struct sys_timer s3c24xx_timer = {
- .init = s3c2410_timer_init,
- .offset = s3c2410_gettimeoffset,
- .resume = s3c2410_timer_setup
-};
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig
new file mode 100644
index 000000000000..54375a00a7d2
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/Kconfig
@@ -0,0 +1,62 @@
+# arch/arm/plat-s3c64xx/Kconfig
+#
+# Copyright 2008 Openmoko, Inc.
+# Copyright 2008 Simtec Electronics
+# Ben Dooks <ben@simtec.co.uk>
+#
+# Licensed under GPLv2
+
+config PLAT_S3C64XX
+ bool
+ depends on ARCH_S3C64XX
+ default y
+ select CPU_V6
+ select PLAT_S3C
+ select ARM_VIC
+ select NO_IOPORT
+ select ARCH_REQUIRE_GPIOLIB
+ select S3C_GPIO_TRACK
+ select S3C_GPIO_PULL_UPDOWN
+ select S3C_GPIO_CFG_S3C24XX
+ select S3C_GPIO_CFG_S3C64XX
+ help
+ Base platform code for any Samsung S3C64XX device
+
+if PLAT_S3C64XX
+
+# Configuration options shared by all S3C64XX implementations
+
+config CPU_S3C6400_INIT
+ bool
+ help
+ Common initialisation code for the S3C6400 that is shared
+ by other CPUs in the series, such as the S3C6410.
+
+config CPU_S3C6400_CLOCK
+ bool
+ help
+ Common clock support code for the S3C6400 that is shared
+ by other CPUs in the series, such as the S3C6410.
+
+# platform specific device setup
+
+config S3C64XX_SETUP_I2C0
+ bool
+ default y
+ help
+ Common setup code for i2c bus 0.
+
+ Note, currently since i2c0 is always compiled, this setup helper
+ is always compiled with it.
+
+config S3C64XX_SETUP_I2C1
+ bool
+ help
+ Common setup code for i2c bus 1.
+
+config S3C64XX_SETUP_FB_24BPP
+ bool
+ help
+ Common setup code for S3C64XX with an 24bpp RGB display helper.
+
+endif
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile
new file mode 100644
index 000000000000..2e6d79bf8f33
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/Makefile
@@ -0,0 +1,31 @@
+# arch/arm/plat-s3c64xx/Makefile
+#
+# Copyright 2008 Openmoko, Inc.
+# Copyright 2008 Simtec Electronics
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n := dummy.o
+obj- :=
+
+# Core files
+
+obj-y += dev-uart.o
+obj-y += cpu.o
+obj-y += irq.o
+obj-y += irq-eint.o
+obj-y += clock.o
+obj-y += gpiolib.o
+
+# CPU support
+
+obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o
+obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o
+
+# Device setup
+
+obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
+obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o
+obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c
new file mode 100644
index 000000000000..136c982c68e1
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/clock.c
@@ -0,0 +1,281 @@
+/* linux/arch/arm/plat-s3c64xx/clock.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX Base clock support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/regs-sys.h>
+#include <plat/regs-clock.h>
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+struct clk clk_27m = {
+ .name = "clk_27m",
+ .id = -1,
+ .rate = 27000000,
+};
+
+static int clk_48m_ctrl(struct clk *clk, int enable)
+{
+ unsigned long flags;
+ u32 val;
+
+ /* can't rely on clock lock, this register has other usages */
+ local_irq_save(flags);
+
+ val = __raw_readl(S3C64XX_OTHERS);
+ if (enable)
+ val |= S3C64XX_OTHERS_USBMASK;
+ else
+ val &= ~S3C64XX_OTHERS_USBMASK;
+
+ __raw_writel(val, S3C64XX_OTHERS);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+struct clk clk_48m = {
+ .name = "clk_48m",
+ .id = -1,
+ .rate = 48000000,
+ .enable = clk_48m_ctrl,
+};
+
+static int inline s3c64xx_gate(void __iomem *reg,
+ struct clk *clk,
+ int enable)
+{
+ unsigned int ctrlbit = clk->ctrlbit;
+ u32 con;
+
+ con = __raw_readl(reg);
+
+ if (enable)
+ con |= ctrlbit;
+ else
+ con &= ~ctrlbit;
+
+ __raw_writel(con, reg);
+ return 0;
+}
+
+static int s3c64xx_pclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_PCLK_GATE, clk, enable);
+}
+
+static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
+}
+
+int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
+{
+ return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
+}
+
+static struct clk init_clocks_disable[] = {
+ {
+ .name = "nand",
+ .id = -1,
+ .parent = &clk_h,
+ }, {
+ .name = "adc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_TSADC,
+ }, {
+ .name = "i2c",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIC,
+ }, {
+ .name = "iis",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIS0,
+ }, {
+ .name = "iis",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_IIS1,
+ }, {
+ .name = "spi",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_SPI0,
+ }, {
+ .name = "spi",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_SPI1,
+ }, {
+ .name = "48m",
+ .id = 0,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC0_48,
+ }, {
+ .name = "48m",
+ .id = 1,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC1_48,
+ }, {
+ .name = "48m",
+ .id = 2,
+ .parent = &clk_48m,
+ .enable = s3c64xx_sclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC2_48,
+ },
+};
+
+static struct clk init_clocks[] = {
+ {
+ .name = "lcd",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_LCD,
+ }, {
+ .name = "gpio",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_GPIO,
+ }, {
+ .name = "usb-host",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_SCLK_UHOST,
+ }, {
+ .name = "hsmmc",
+ .id = 0,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC0,
+ }, {
+ .name = "hsmmc",
+ .id = 1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC1,
+ }, {
+ .name = "hsmmc",
+ .id = 2,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_HSMMC2,
+ }, {
+ .name = "timers",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_PWM,
+ }, {
+ .name = "uart",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART0,
+ }, {
+ .name = "uart",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART1,
+ }, {
+ .name = "uart",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART2,
+ }, {
+ .name = "uart",
+ .id = 3,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_UART3,
+ }, {
+ .name = "rtc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_RTC,
+ }, {
+ .name = "watchdog",
+ .id = -1,
+ .parent = &clk_p,
+ .ctrlbit = S3C_CLKCON_PCLK_WDT,
+ }, {
+ .name = "ac97",
+ .id = -1,
+ .parent = &clk_p,
+ .ctrlbit = S3C_CLKCON_PCLK_AC97,
+ }
+};
+
+static struct clk *clks[] __initdata = {
+ &clk_ext,
+ &clk_epll,
+ &clk_27m,
+ &clk_48m,
+};
+
+void s3c64xx_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
+
+ clkp = init_clocks;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ }
+
+ clkp = init_clocks_disable;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
+
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+
+ (clkp->enable)(clkp, 0);
+ }
+
+ s3c_pwmclk_init();
+}
diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c
new file mode 100644
index 000000000000..fbde183a4560
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/cpu.c
@@ -0,0 +1,114 @@
+/* linux/arch/arm/plat-s3c64xx/cpu.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX CPU Support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/regs-serial.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+#include <plat/s3c6400.h>
+#include <plat/s3c6410.h>
+
+/* table of supported CPUs */
+
+static const char name_s3c6400[] = "S3C6400";
+static const char name_s3c6410[] = "S3C6410";
+
+static struct cpu_table cpu_ids[] __initdata = {
+ {
+ .idcode = 0x36400000,
+ .idmask = 0xfffff000,
+ .map_io = s3c6400_map_io,
+ .init_clocks = s3c6400_init_clocks,
+ .init_uarts = s3c6400_init_uarts,
+ .init = s3c6400_init,
+ .name = name_s3c6400,
+ }, {
+ .idcode = 0x36410100,
+ .idmask = 0xffffff00,
+ .map_io = s3c6410_map_io,
+ .init_clocks = s3c6410_init_clocks,
+ .init_uarts = s3c6410_init_uarts,
+ .init = s3c6410_init,
+ .name = name_s3c6410,
+ },
+};
+
+/* minimal IO mapping */
+
+/* see notes on uart map in arch/arm/mach-s3c6400/include/mach/debug-macro.S */
+#define UART_OFFS (S3C_PA_UART & 0xfffff)
+
+static struct map_desc s3c_iodesc[] __initdata = {
+ {
+ .virtual = (unsigned long)S3C_VA_SYS,
+ .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS),
+ .pfn = __phys_to_pfn(S3C_PA_UART),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_VIC0,
+ .pfn = __phys_to_pfn(S3C64XX_PA_VIC0),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_VIC1,
+ .pfn = __phys_to_pfn(S3C64XX_PA_VIC1),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_TIMER,
+ .pfn = __phys_to_pfn(S3C_PA_TIMER),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C64XX_VA_GPIO,
+ .pfn = __phys_to_pfn(S3C64XX_PA_GPIO),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ },
+};
+
+/* read cpu identification code */
+
+void __init s3c64xx_init_io(struct map_desc *mach_desc, int size)
+{
+ unsigned long idcode;
+
+ /* initialise the io descriptors we need for initialisation */
+ iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
+ iotable_init(mach_desc, size);
+
+ idcode = __raw_readl(S3C_VA_SYS + 0x118);
+ s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
+}
diff --git a/arch/arm/plat-s3c64xx/dev-uart.c b/arch/arm/plat-s3c64xx/dev-uart.c
new file mode 100644
index 000000000000..62c11a6fc7ba
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/dev-uart.c
@@ -0,0 +1,176 @@
+/* linux/arch/arm/plat-s3c64xx/dev-uart.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Base S3C64XX UART resource and device definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/devs.h>
+
+/* Serial port registrations */
+
+/* 64xx uarts are closer together */
+
+static struct resource s3c64xx_uart0_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART0,
+ .end = S3C_PA_UART0 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX0,
+ .end = IRQ_S3CUART_RX0,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX0,
+ .end = IRQ_S3CUART_TX0,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR0,
+ .end = IRQ_S3CUART_ERR0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource s3c64xx_uart1_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART1,
+ .end = S3C_PA_UART1 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX1,
+ .end = IRQ_S3CUART_RX1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX1,
+ .end = IRQ_S3CUART_TX1,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR1,
+ .end = IRQ_S3CUART_ERR1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource s3c6xx_uart2_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART2,
+ .end = S3C_PA_UART2 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX2,
+ .end = IRQ_S3CUART_RX2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX2,
+ .end = IRQ_S3CUART_TX2,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR2,
+ .end = IRQ_S3CUART_ERR2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource s3c64xx_uart3_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART3,
+ .end = S3C_PA_UART3 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX3,
+ .end = IRQ_S3CUART_RX3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX3,
+ .end = IRQ_S3CUART_TX3,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR3,
+ .end = IRQ_S3CUART_ERR3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+struct s3c24xx_uart_resources s3c64xx_uart_resources[] __initdata = {
+ [0] = {
+ .resources = s3c64xx_uart0_resource,
+ .nr_resources = ARRAY_SIZE(s3c64xx_uart0_resource),
+ },
+ [1] = {
+ .resources = s3c64xx_uart1_resource,
+ .nr_resources = ARRAY_SIZE(s3c64xx_uart1_resource),
+ },
+ [2] = {
+ .resources = s3c6xx_uart2_resource,
+ .nr_resources = ARRAY_SIZE(s3c6xx_uart2_resource),
+ },
+ [3] = {
+ .resources = s3c64xx_uart3_resource,
+ .nr_resources = ARRAY_SIZE(s3c64xx_uart3_resource),
+ },
+};
+
+/* uart devices */
+
+static struct platform_device s3c24xx_uart_device0 = {
+ .id = 0,
+};
+
+static struct platform_device s3c24xx_uart_device1 = {
+ .id = 1,
+};
+
+static struct platform_device s3c24xx_uart_device2 = {
+ .id = 2,
+};
+
+static struct platform_device s3c24xx_uart_device3 = {
+ .id = 3,
+};
+
+struct platform_device *s3c24xx_uart_src[4] = {
+ &s3c24xx_uart_device0,
+ &s3c24xx_uart_device1,
+ &s3c24xx_uart_device2,
+ &s3c24xx_uart_device3,
+};
+
+struct platform_device *s3c24xx_uart_devs[4] = {
+};
+
diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c
new file mode 100644
index 000000000000..cc62941d7b5c
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/gpiolib.c
@@ -0,0 +1,420 @@
+/* arch/arm/plat-s3c64xx/gpiolib.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - GPIOlib support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/map.h>
+#include <mach/gpio.h>
+#include <mach/gpio-core.h>
+
+#include <plat/gpio-cfg.h>
+#include <plat/gpio-cfg-helpers.h>
+#include <plat/regs-gpio.h>
+
+/* GPIO bank summary:
+ *
+ * Bank GPIOs Style SlpCon ExtInt Group
+ * A 8 4Bit Yes 1
+ * B 7 4Bit Yes 1
+ * C 8 4Bit Yes 2
+ * D 5 4Bit Yes 3
+ * E 5 4Bit Yes None
+ * F 16 2Bit Yes 4 [1]
+ * G 7 4Bit Yes 5
+ * H 10 4Bit[2] Yes 6
+ * I 16 2Bit Yes None
+ * J 12 2Bit Yes None
+ * K 16 4Bit[2] No None
+ * L 15 4Bit[2] No None
+ * M 6 4Bit No IRQ_EINT
+ * N 16 2Bit No IRQ_EINT
+ * O 16 2Bit Yes 7
+ * P 15 2Bit Yes 8
+ * Q 9 2Bit Yes 9
+ *
+ * [1] BANKF pins 14,15 do not form part of the external interrupt sources
+ * [2] BANK has two control registers, GPxCON0 and GPxCON1
+ */
+
+#define OFF_GPCON (0x00)
+#define OFF_GPDAT (0x04)
+
+#define con_4bit_shift(__off) ((__off) * 4)
+
+#if 1
+#define gpio_dbg(x...) do { } while(0)
+#else
+#define gpio_dbg(x...) printk(KERN_DEBUG ## x)
+#endif
+
+/* The s3c64xx_gpiolib_4bit routines are to control the gpio banks where
+ * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
+ * following example:
+ *
+ * base + 0x00: Control register, 4 bits per gpio
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * Note, since the data register is one bit per gpio and is at base + 0x4
+ * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of
+ * the output.
+*/
+
+static int s3c64xx_gpiolib_4bit_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+
+ con = __raw_readl(base + OFF_GPCON);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, base + OFF_GPCON);
+
+ gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);
+
+ return 0;
+}
+
+static int s3c64xx_gpiolib_4bit_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ unsigned long con;
+ unsigned long dat;
+
+ con = __raw_readl(base + OFF_GPCON);
+ con &= ~(0xf << con_4bit_shift(offset));
+ con |= 0x1 << con_4bit_shift(offset);
+
+ dat = __raw_readl(base + OFF_GPDAT);
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + OFF_GPDAT);
+ __raw_writel(con, base + OFF_GPCON);
+ __raw_writel(dat, base + OFF_GPDAT);
+
+ gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+/* The next set of routines are for the case where the GPIO configuration
+ * registers are 4 bits per GPIO but there is more than one register (the
+ * bank has more than 8 GPIOs.
+ *
+ * This case is the similar to the 4 bit case, but the registers are as
+ * follows:
+ *
+ * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs)
+ * gpio n: 4 bits starting at (4*n)
+ * 0000 = input, 0001 = output, others mean special-function
+ * base + 0x08: Data register, 1 bit per gpio
+ * bit n: data bit n
+ *
+ * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we
+ * store the 'base + 0x4' address so that these routines see the data
+ * register at ourchip->base + 0x04.
+*/
+
+static int s3c64xx_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+
+ if (offset > 7)
+ offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(offset));
+ __raw_writel(con, regcon);
+
+ gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con);
+
+ return 0;
+
+}
+
+static int s3c64xx_gpiolib_4bit2_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
+ void __iomem *base = ourchip->base;
+ void __iomem *regcon = base;
+ unsigned long con;
+ unsigned long dat;
+
+ if (offset > 7)
+ offset -= 8;
+ else
+ regcon -= 4;
+
+ con = __raw_readl(regcon);
+ con &= ~(0xf << con_4bit_shift(offset));
+ con |= 0x1 << con_4bit_shift(offset);
+
+ dat = __raw_readl(base + OFF_GPDAT);
+ if (value)
+ dat |= 1 << offset;
+ else
+ dat &= ~(1 << offset);
+
+ __raw_writel(dat, base + OFF_GPDAT);
+ __raw_writel(con, regcon);
+ __raw_writel(dat, base + OFF_GPDAT);
+
+ gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
+
+ return 0;
+}
+
+static struct s3c_gpio_cfg gpio_4bit_cfg_noint = {
+ .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = {
+ .cfg_eint = 7,
+ .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = {
+ .cfg_eint = 3,
+ .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_chip gpio_4bit[] = {
+ {
+ .base = S3C64XX_GPA_BASE,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPA(0),
+ .ngpio = S3C64XX_GPIO_A_NR,
+ .label = "GPA",
+ },
+ }, {
+ .base = S3C64XX_GPB_BASE,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPB(0),
+ .ngpio = S3C64XX_GPIO_B_NR,
+ .label = "GPB",
+ },
+ }, {
+ .base = S3C64XX_GPC_BASE,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPC(0),
+ .ngpio = S3C64XX_GPIO_C_NR,
+ .label = "GPC",
+ },
+ }, {
+ .base = S3C64XX_GPD_BASE,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPD(0),
+ .ngpio = S3C64XX_GPIO_D_NR,
+ .label = "GPD",
+ },
+ }, {
+ .base = S3C64XX_GPE_BASE,
+ .config = &gpio_4bit_cfg_noint,
+ .chip = {
+ .base = S3C64XX_GPE(0),
+ .ngpio = S3C64XX_GPIO_E_NR,
+ .label = "GPE",
+ },
+ }, {
+ .base = S3C64XX_GPG_BASE,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPG(0),
+ .ngpio = S3C64XX_GPIO_G_NR,
+ .label = "GPG",
+ },
+ }, {
+ .base = S3C64XX_GPM_BASE,
+ .config = &gpio_4bit_cfg_eint0011,
+ .chip = {
+ .base = S3C64XX_GPM(0),
+ .ngpio = S3C64XX_GPIO_M_NR,
+ .label = "GPM",
+ },
+ },
+};
+
+static struct s3c_gpio_chip gpio_4bit2[] = {
+ {
+ .base = S3C64XX_GPH_BASE + 0x4,
+ .config = &gpio_4bit_cfg_eint0111,
+ .chip = {
+ .base = S3C64XX_GPH(0),
+ .ngpio = S3C64XX_GPIO_H_NR,
+ .label = "GPH",
+ },
+ }, {
+ .base = S3C64XX_GPK_BASE + 0x4,
+ .config = &gpio_4bit_cfg_noint,
+ .chip = {
+ .base = S3C64XX_GPK(0),
+ .ngpio = S3C64XX_GPIO_K_NR,
+ .label = "GPK",
+ },
+ }, {
+ .base = S3C64XX_GPL_BASE + 0x4,
+ .config = &gpio_4bit_cfg_eint0011,
+ .chip = {
+ .base = S3C64XX_GPL(0),
+ .ngpio = S3C64XX_GPIO_L_NR,
+ .label = "GPL",
+ },
+ },
+};
+
+static struct s3c_gpio_cfg gpio_2bit_cfg_noint = {
+ .set_config = s3c_gpio_setcfg_s3c24xx,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = {
+ .cfg_eint = 2,
+ .set_config = s3c_gpio_setcfg_s3c24xx,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = {
+ .cfg_eint = 3,
+ .set_config = s3c_gpio_setcfg_s3c24xx,
+ .set_pull = s3c_gpio_setpull_updown,
+ .get_pull = s3c_gpio_getpull_updown,
+};
+
+static struct s3c_gpio_chip gpio_2bit[] = {
+ {
+ .base = S3C64XX_GPF_BASE,
+ .config = &gpio_2bit_cfg_eint11,
+ .chip = {
+ .base = S3C64XX_GPF(0),
+ .ngpio = S3C64XX_GPIO_F_NR,
+ .label = "GPF",
+ },
+ }, {
+ .base = S3C64XX_GPI_BASE,
+ .config = &gpio_2bit_cfg_noint,
+ .chip = {
+ .base = S3C64XX_GPI(0),
+ .ngpio = S3C64XX_GPIO_I_NR,
+ .label = "GPI",
+ },
+ }, {
+ .base = S3C64XX_GPJ_BASE,
+ .config = &gpio_2bit_cfg_noint,
+ .chip = {
+ .base = S3C64XX_GPJ(0),
+ .ngpio = S3C64XX_GPIO_J_NR,
+ .label = "GPJ",
+ },
+ }, {
+ .base = S3C64XX_GPN_BASE,
+ .config = &gpio_2bit_cfg_eint10,
+ .chip = {
+ .base = S3C64XX_GPN(0),
+ .ngpio = S3C64XX_GPIO_N_NR,
+ .label = "GPN",
+ },
+ }, {
+ .base = S3C64XX_GPO_BASE,
+ .config = &gpio_2bit_cfg_eint11,
+ .chip = {
+ .base = S3C64XX_GPO(0),
+ .ngpio = S3C64XX_GPIO_O_NR,
+ .label = "GPO",
+ },
+ }, {
+ .base = S3C64XX_GPP_BASE,
+ .config = &gpio_2bit_cfg_eint11,
+ .chip = {
+ .base = S3C64XX_GPP(0),
+ .ngpio = S3C64XX_GPIO_P_NR,
+ .label = "GPP",
+ },
+ }, {
+ .base = S3C64XX_GPQ_BASE,
+ .config = &gpio_2bit_cfg_eint11,
+ .chip = {
+ .base = S3C64XX_GPQ(0),
+ .ngpio = S3C64XX_GPIO_Q_NR,
+ .label = "GPQ",
+ },
+ },
+};
+
+static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
+{
+ chip->chip.direction_input = s3c64xx_gpiolib_4bit_input;
+ chip->chip.direction_output = s3c64xx_gpiolib_4bit_output;
+}
+
+static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip)
+{
+ chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input;
+ chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output;
+}
+
+static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips,
+ int nr_chips,
+ void (*fn)(struct s3c_gpio_chip *))
+{
+ for (; nr_chips > 0; nr_chips--, chips++) {
+ if (fn)
+ (fn)(chips);
+ s3c_gpiolib_add(chips);
+ }
+}
+
+static __init int s3c64xx_gpiolib_init(void)
+{
+ s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit),
+ s3c64xx_gpiolib_add_4bit);
+
+ s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2),
+ s3c64xx_gpiolib_add_4bit2);
+
+ s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), NULL);
+
+ return 0;
+}
+
+arch_initcall(s3c64xx_gpiolib_init);
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-a.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-a.h
new file mode 100644
index 000000000000..9aa0e427d113
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-a.h
@@ -0,0 +1,48 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-a.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank A register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPACON (S3C64XX_GPA_BASE + 0x00)
+#define S3C64XX_GPADAT (S3C64XX_GPA_BASE + 0x04)
+#define S3C64XX_GPAPUD (S3C64XX_GPA_BASE + 0x08)
+#define S3C64XX_GPACONSLP (S3C64XX_GPA_BASE + 0x0c)
+#define S3C64XX_GPAPUDSLP (S3C64XX_GPA_BASE + 0x10)
+
+#define S3C64XX_GPA_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPA_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPA_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPA0_UART_RXD0 (0x02 << 0)
+#define S3C64XX_GPA0_EINT_G1_0 (0x07 << 0)
+
+#define S3C64XX_GPA1_UART_TXD0 (0x02 << 4)
+#define S3C64XX_GPA1_EINT_G1_1 (0x07 << 4)
+
+#define S3C64XX_GPA2_UART_nCTS0 (0x02 << 8)
+#define S3C64XX_GPA2_EINT_G1_2 (0x07 << 8)
+
+#define S3C64XX_GPA3_UART_nRTS0 (0x02 << 12)
+#define S3C64XX_GPA3_EINT_G1_3 (0x07 << 12)
+
+#define S3C64XX_GPA4_UART_RXD1 (0x02 << 16)
+#define S3C64XX_GPA4_EINT_G1_4 (0x07 << 16)
+
+#define S3C64XX_GPA5_UART_TXD1 (0x02 << 20)
+#define S3C64XX_GPA5_EINT_G1_5 (0x07 << 20)
+
+#define S3C64XX_GPA6_UART_nCTS1 (0x02 << 24)
+#define S3C64XX_GPA6_EINT_G1_6 (0x07 << 24)
+
+#define S3C64XX_GPA7_UART_nRTS1 (0x02 << 28)
+#define S3C64XX_GPA7_EINT_G1_7 (0x07 << 28)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-b.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-b.h
new file mode 100644
index 000000000000..3933adb4d50a
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-b.h
@@ -0,0 +1,60 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-b.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank B register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPBCON (S3C64XX_GPB_BASE + 0x00)
+#define S3C64XX_GPBDAT (S3C64XX_GPB_BASE + 0x04)
+#define S3C64XX_GPBPUD (S3C64XX_GPB_BASE + 0x08)
+#define S3C64XX_GPBCONSLP (S3C64XX_GPB_BASE + 0x0c)
+#define S3C64XX_GPBPUDSLP (S3C64XX_GPB_BASE + 0x10)
+
+#define S3C64XX_GPB_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPB_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPB_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPB0_UART_RXD2 (0x02 << 0)
+#define S3C64XX_GPB0_EXTDMA_REQ (0x03 << 0)
+#define S3C64XX_GPB0_IrDA_RXD (0x04 << 0)
+#define S3C64XX_GPB0_ADDR_CF0 (0x05 << 0)
+#define S3C64XX_GPB0_EINT_G1_8 (0x07 << 0)
+
+#define S3C64XX_GPB1_UART_TXD2 (0x02 << 4)
+#define S3C64XX_GPB1_EXTDMA_ACK (0x03 << 4)
+#define S3C64XX_GPB1_IrDA_TXD (0x04 << 4)
+#define S3C64XX_GPB1_ADDR_CF1 (0x05 << 4)
+#define S3C64XX_GPB1_EINT_G1_9 (0x07 << 4)
+
+#define S3C64XX_GPB2_UART_RXD3 (0x02 << 8)
+#define S3C64XX_GPB2_IrDA_RXD (0x03 << 8)
+#define S3C64XX_GPB2_EXTDMA_REQ (0x04 << 8)
+#define S3C64XX_GPB2_ADDR_CF2 (0x05 << 8)
+#define S3C64XX_GPB2_I2C_SCL1 (0x06 << 8)
+#define S3C64XX_GPB2_EINT_G1_10 (0x07 << 8)
+
+#define S3C64XX_GPB3_UART_TXD3 (0x02 << 12)
+#define S3C64XX_GPB3_IrDA_TXD (0x03 << 12)
+#define S3C64XX_GPB3_EXTDMA_ACK (0x04 << 12)
+#define S3C64XX_GPB3_I2C_SDA1 (0x06 << 12)
+#define S3C64XX_GPB3_EINT_G1_11 (0x07 << 12)
+
+#define S3C64XX_GPB4_IrDA_SDBW (0x02 << 16)
+#define S3C64XX_GPB4_CAM_FIELD (0x03 << 16)
+#define S3C64XX_GPB4_CF_DATA_DIR (0x04 << 16)
+#define S3C64XX_GPB4_EINT_G1_12 (0x07 << 16)
+
+#define S3C64XX_GPB5_I2C_SCL0 (0x02 << 20)
+#define S3C64XX_GPB5_EINT_G1_13 (0x07 << 20)
+
+#define S3C64XX_GPB6_I2C_SDA0 (0x02 << 24)
+#define S3C64XX_GPB6_EINT_G1_14 (0x07 << 24)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-c.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-c.h
new file mode 100644
index 000000000000..c47daf7e2723
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-c.h
@@ -0,0 +1,53 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-c.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank C register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPCCON (S3C64XX_GPC_BASE + 0x00)
+#define S3C64XX_GPCDAT (S3C64XX_GPC_BASE + 0x04)
+#define S3C64XX_GPCPUD (S3C64XX_GPC_BASE + 0x08)
+#define S3C64XX_GPCCONSLP (S3C64XX_GPC_BASE + 0x0c)
+#define S3C64XX_GPCPUDSLP (S3C64XX_GPC_BASE + 0x10)
+
+#define S3C64XX_GPC_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPC_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPC_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPC0_SPI_MISO0 (0x02 << 0)
+#define S3C64XX_GPC0_EINT_G2_0 (0x07 << 0)
+
+#define S3C64XX_GPC1_SPI_CLKO (0x02 << 4)
+#define S3C64XX_GPC1_EINT_G2_1 (0x07 << 4)
+
+#define S3C64XX_GPC2_SPI_MOSIO (0x02 << 8)
+#define S3C64XX_GPC2_EINT_G2_2 (0x07 << 8)
+
+#define S3C64XX_GPC3_SPI_nCSO (0x02 << 12)
+#define S3C64XX_GPC3_EINT_G2_3 (0x07 << 12)
+
+#define S3C64XX_GPC4_SPI_MISO1 (0x02 << 16)
+#define S3C64XX_GPC4_MMC2_CMD (0x03 << 16)
+#define S3C64XX_GPC4_I2S0_V40_DO (0x05 << 16)
+#define S3C64XX_GPC4_EINT_G2_4 (0x07 << 16)
+
+#define S3C64XX_GPC5_SPI_CLK1 (0x02 << 20)
+#define S3C64XX_GPC5_MMC2_CLK (0x03 << 20)
+#define S3C64XX_GPC5_I2S1_V40_DO (0x05 << 20)
+#define S3C64XX_GPC5_EINT_G2_5 (0x07 << 20)
+
+#define S3C64XX_GPC6_SPI_MOSI1 (0x02 << 24)
+#define S3C64XX_GPC6_EINT_G2_6 (0x07 << 24)
+
+#define S3C64XX_GPC7_SPI_nCS1 (0x02 << 28)
+#define S3C64XX_GPC7_I2S2_V40_DO (0x05 << 28)
+#define S3C64XX_GPC7_EINT_G2_7 (0x07 << 28)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-d.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-d.h
new file mode 100644
index 000000000000..6fe4a49c26f0
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-d.h
@@ -0,0 +1,49 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-d.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank D register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPDCON (S3C64XX_GPD_BASE + 0x00)
+#define S3C64XX_GPDDAT (S3C64XX_GPD_BASE + 0x04)
+#define S3C64XX_GPDPUD (S3C64XX_GPD_BASE + 0x08)
+#define S3C64XX_GPDCONSLP (S3C64XX_GPD_BASE + 0x0c)
+#define S3C64XX_GPDPUDSLP (S3C64XX_GPD_BASE + 0x10)
+
+#define S3C64XX_GPD_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPD_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPD_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPD0_PCM0_SCLK (0x02 << 0)
+#define S3C64XX_GPD0_I2S0_CLK (0x03 << 0)
+#define S3C64XX_GPD0_AC97_BITCLK (0x04 << 0)
+#define S3C64XX_GPD0_EINT_G3_0 (0x07 << 0)
+
+#define S3C64XX_GPD1_PCM0_EXTCLK (0x02 << 4)
+#define S3C64XX_GPD1_I2S0_CDCLK (0x03 << 4)
+#define S3C64XX_GPD1_AC97_nRESET (0x04 << 4)
+#define S3C64XX_GPD1_EINT_G3_1 (0x07 << 4)
+
+#define S3C64XX_GPD2_PCM0_FSYNC (0x02 << 8)
+#define S3C64XX_GPD2_I2S0_LRCLK (0x03 << 8)
+#define S3C64XX_GPD2_AC97_SYNC (0x04 << 8)
+#define S3C64XX_GPD2_EINT_G3_2 (0x07 << 8)
+
+#define S3C64XX_GPD3_PCM0_SIN (0x02 << 12)
+#define S3C64XX_GPD3_I2S0_DI (0x03 << 12)
+#define S3C64XX_GPD3_AC97_SDI (0x04 << 12)
+#define S3C64XX_GPD3_EINT_G3_3 (0x07 << 12)
+
+#define S3C64XX_GPD4_PCM0_SOUT (0x02 << 16)
+#define S3C64XX_GPD4_I2S0_D0 (0x03 << 16)
+#define S3C64XX_GPD4_AC97_SDO (0x04 << 16)
+#define S3C64XX_GPD4_EINT_G3_4 (0x07 << 16)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-e.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-e.h
new file mode 100644
index 000000000000..7fcf3d8e0a48
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-e.h
@@ -0,0 +1,44 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-e.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank E register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPECON (S3C64XX_GPE_BASE + 0x00)
+#define S3C64XX_GPEDAT (S3C64XX_GPE_BASE + 0x04)
+#define S3C64XX_GPEPUD (S3C64XX_GPE_BASE + 0x08)
+#define S3C64XX_GPECONSLP (S3C64XX_GPE_BASE + 0x0c)
+#define S3C64XX_GPEPUDSLP (S3C64XX_GPE_BASE + 0x10)
+
+#define S3C64XX_GPE_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPE_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPE_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPE0_PCM1_SCLK (0x02 << 0)
+#define S3C64XX_GPE0_I2S1_CLK (0x03 << 0)
+#define S3C64XX_GPE0_AC97_BITCLK (0x04 << 0)
+
+#define S3C64XX_GPE1_PCM1_EXTCLK (0x02 << 4)
+#define S3C64XX_GPE1_I2S1_CDCLK (0x03 << 4)
+#define S3C64XX_GPE1_AC97_nRESET (0x04 << 4)
+
+#define S3C64XX_GPE2_PCM1_FSYNC (0x02 << 8)
+#define S3C64XX_GPE2_I2S1_LRCLK (0x03 << 8)
+#define S3C64XX_GPE2_AC97_SYNC (0x04 << 8)
+
+#define S3C64XX_GPE3_PCM1_SIN (0x02 << 12)
+#define S3C64XX_GPE3_I2S1_DI (0x03 << 12)
+#define S3C64XX_GPE3_AC97_SDI (0x04 << 12)
+
+#define S3C64XX_GPE4_PCM1_SOUT (0x02 << 16)
+#define S3C64XX_GPE4_I2S1_D0 (0x03 << 16)
+#define S3C64XX_GPE4_AC97_SDO (0x04 << 16)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-f.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-f.h
new file mode 100644
index 000000000000..f3faff974a18
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-f.h
@@ -0,0 +1,71 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-f.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank F register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPFCON (S3C64XX_GPF_BASE + 0x00)
+#define S3C64XX_GPFDAT (S3C64XX_GPF_BASE + 0x04)
+#define S3C64XX_GPFPUD (S3C64XX_GPF_BASE + 0x08)
+#define S3C64XX_GPFCONSLP (S3C64XX_GPF_BASE + 0x0c)
+#define S3C64XX_GPFPUDSLP (S3C64XX_GPF_BASE + 0x10)
+
+#define S3C64XX_GPF_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPF_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPF_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPF0_CAMIF_CLK (0x02 << 0)
+#define S3C64XX_GPF0_EINT_G4_0 (0x03 << 0)
+
+#define S3C64XX_GPF1_CAMIF_HREF (0x02 << 2)
+#define S3C64XX_GPF1_EINT_G4_1 (0x03 << 2)
+
+#define S3C64XX_GPF2_CAMIF_PCLK (0x02 << 4)
+#define S3C64XX_GPF2_EINT_G4_2 (0x03 << 4)
+
+#define S3C64XX_GPF3_CAMIF_nRST (0x02 << 6)
+#define S3C64XX_GPF3_EINT_G4_3 (0x03 << 6)
+
+#define S3C64XX_GPF4_CAMIF_VSYNC (0x02 << 8)
+#define S3C64XX_GPF4_EINT_G4_4 (0x03 << 8)
+
+#define S3C64XX_GPF5_CAMIF_YDATA0 (0x02 << 10)
+#define S3C64XX_GPF5_EINT_G4_5 (0x03 << 10)
+
+#define S3C64XX_GPF6_CAMIF_YDATA1 (0x02 << 12)
+#define S3C64XX_GPF6_EINT_G4_6 (0x03 << 12)
+
+#define S3C64XX_GPF7_CAMIF_YDATA2 (0x02 << 14)
+#define S3C64XX_GPF7_EINT_G4_7 (0x03 << 14)
+
+#define S3C64XX_GPF8_CAMIF_YDATA3 (0x02 << 16)
+#define S3C64XX_GPF8_EINT_G4_8 (0x03 << 16)
+
+#define S3C64XX_GPF9_CAMIF_YDATA4 (0x02 << 18)
+#define S3C64XX_GPF9_EINT_G4_9 (0x03 << 18)
+
+#define S3C64XX_GPF10_CAMIF_YDATA5 (0x02 << 20)
+#define S3C64XX_GPF10_EINT_G4_10 (0x03 << 20)
+
+#define S3C64XX_GPF11_CAMIF_YDATA6 (0x02 << 22)
+#define S3C64XX_GPF11_EINT_G4_11 (0x03 << 22)
+
+#define S3C64XX_GPF12_CAMIF_YDATA7 (0x02 << 24)
+#define S3C64XX_GPF12_EINT_G4_12 (0x03 << 24)
+
+#define S3C64XX_GPF13_PWM_ECLK (0x02 << 26)
+#define S3C64XX_GPF13_EINT_G4_13 (0x03 << 26)
+
+#define S3C64XX_GPF14_PWM_TOUT0 (0x02 << 28)
+#define S3C64XX_GPF14_CLKOUT0 (0x03 << 28)
+
+#define S3C64XX_GPF15_PWM_TOUT1 (0x02 << 30)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-g.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-g.h
new file mode 100644
index 000000000000..35bbd2378e55
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-g.h
@@ -0,0 +1,42 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-g.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank G register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPGCON (S3C64XX_GPG_BASE + 0x00)
+#define S3C64XX_GPGDAT (S3C64XX_GPG_BASE + 0x04)
+#define S3C64XX_GPGPUD (S3C64XX_GPG_BASE + 0x08)
+#define S3C64XX_GPGCONSLP (S3C64XX_GPG_BASE + 0x0c)
+#define S3C64XX_GPGPUDSLP (S3C64XX_GPG_BASE + 0x10)
+
+#define S3C64XX_GPG_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPG_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPG_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPG0_MMC0_CLK (0x02 << 0)
+#define S3C64XX_GPG0_EINT_G5_0 (0x07 << 0)
+
+#define S3C64XX_GPG1_MMC0_CMD (0x02 << 4)
+#define S3C64XX_GPG1_EINT_G5_1 (0x07 << 4)
+
+#define S3C64XX_GPG2_MMC0_DATA0 (0x02 << 8)
+#define S3C64XX_GPG2_EINT_G5_2 (0x07 << 8)
+
+#define S3C64XX_GPG3_MMC0_DATA1 (0x02 << 12)
+#define S3C64XX_GPG3_EINT_G5_3 (0x07 << 12)
+
+#define S3C64XX_GPG4_MMC0_DATA2 (0x02 << 16)
+#define S3C64XX_GPG4_EINT_G5_4 (0x07 << 16)
+
+#define S3C64XX_GPG5_MMC0_DATA3 (0x02 << 20)
+#define S3C64XX_GPG5_EINT_G5_5 (0x07 << 20)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-h.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-h.h
new file mode 100644
index 000000000000..81549516572f
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-h.h
@@ -0,0 +1,74 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-h.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank H register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPHCON0 (S3C64XX_GPH_BASE + 0x00)
+#define S3C64XX_GPHCON1 (S3C64XX_GPH_BASE + 0x04)
+#define S3C64XX_GPHDAT (S3C64XX_GPH_BASE + 0x08)
+#define S3C64XX_GPHPUD (S3C64XX_GPH_BASE + 0x0c)
+#define S3C64XX_GPHCONSLP (S3C64XX_GPH_BASE + 0x10)
+#define S3C64XX_GPHPUDSLP (S3C64XX_GPH_BASE + 0x14)
+
+#define S3C64XX_GPH_CONMASK(__gpio) (0xf << ((__gpio) * 4))
+#define S3C64XX_GPH_INPUT(__gpio) (0x0 << ((__gpio) * 4))
+#define S3C64XX_GPH_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
+
+#define S3C64XX_GPH0_MMC1_CLK (0x02 << 0)
+#define S3C64XX_GPH0_KP_COL0 (0x04 << 0)
+#define S3C64XX_GPH0_EINT_G6_0 (0x07 << 0)
+
+#define S3C64XX_GPH1_MMC1_CMD (0x02 << 4)
+#define S3C64XX_GPH1_KP_COL1 (0x04 << 4)
+#define S3C64XX_GPH1_EINT_G6_1 (0x07 << 4)
+
+#define S3C64XX_GPH2_MMC1_DATA0 (0x02 << 8)
+#define S3C64XX_GPH2_KP_COL2 (0x04 << 8)
+#define S3C64XX_GPH2_EINT_G6_2 (0x07 << 8)
+
+#define S3C64XX_GPH3_MMC1_DATA1 (0x02 << 12)
+#define S3C64XX_GPH3_KP_COL3 (0x04 << 12)
+#define S3C64XX_GPH3_EINT_G6_3 (0x07 << 12)
+
+#define S3C64XX_GPH4_MMC1_DATA2 (0x02 << 16)
+#define S3C64XX_GPH4_KP_COL4 (0x04 << 16)
+#define S3C64XX_GPH4_EINT_G6_4 (0x07 << 16)
+
+#define S3C64XX_GPH5_MMC1_DATA3 (0x02 << 20)
+#define S3C64XX_GPH5_KP_COL5 (0x04 << 20)
+#define S3C64XX_GPH5_EINT_G6_5 (0x07 << 20)
+
+#define S3C64XX_GPH6_MMC1_DATA4 (0x02 << 24)
+#define S3C64XX_GPH6_MMC2_DATA0 (0x03 << 24)
+#define S3C64XX_GPH6_KP_COL6 (0x04 << 24)
+#define S3C64XX_GPH6_I2S_V40_BCLK (0x05 << 24)
+#define S3C64XX_GPH6_ADDR_CF0 (0x06 << 24)
+#define S3C64XX_GPH6_EINT_G6_6 (0x07 << 24)
+
+#define S3C64XX_GPH7_MMC1_DATA5 (0x02 << 28)
+#define S3C64XX_GPH7_MMC2_DATA1 (0x03 << 28)
+#define S3C64XX_GPH7_KP_COL7 (0x04 << 28)
+#define S3C64XX_GPH7_I2S_V40_CDCLK (0x05 << 28)
+#define S3C64XX_GPH7_ADDR_CF1 (0x06 << 28)
+#define S3C64XX_GPH7_EINT_G6_7 (0x07 << 28)
+
+#define S3C64XX_GPH8_MMC1_DATA6 (0x02 << 32)
+#define S3C64XX_GPH8_MMC2_DATA2 (0x03 << 32)
+#define S3C64XX_GPH8_I2S_V40_LRCLK (0x05 << 32)
+#define S3C64XX_GPH8_ADDR_CF2 (0x06 << 32)
+#define S3C64XX_GPH8_EINT_G6_8 (0x07 << 32)
+
+#define S3C64XX_GPH9_MMC1_DATA7 (0x02 << 36)
+#define S3C64XX_GPH9_MMC2_DATA3 (0x03 << 36)
+#define S3C64XX_GPH9_I2S_V40_DI (0x05 << 36)
+#define S3C64XX_GPH9_EINT_G6_9 (0x07 << 36)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-i.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-i.h
new file mode 100644
index 000000000000..ce9ebe335566
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-i.h
@@ -0,0 +1,40 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-i.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank I register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPICON (S3C64XX_GPI_BASE + 0x00)
+#define S3C64XX_GPIDAT (S3C64XX_GPI_BASE + 0x04)
+#define S3C64XX_GPIPUD (S3C64XX_GPI_BASE + 0x08)
+#define S3C64XX_GPICONSLP (S3C64XX_GPI_BASE + 0x0c)
+#define S3C64XX_GPIPUDSLP (S3C64XX_GPI_BASE + 0x10)
+
+#define S3C64XX_GPI_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPI_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPI_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPI0_VD0 (0x02 << 0)
+#define S3C64XX_GPI1_VD1 (0x02 << 2)
+#define S3C64XX_GPI2_VD2 (0x02 << 4)
+#define S3C64XX_GPI3_VD3 (0x02 << 6)
+#define S3C64XX_GPI4_VD4 (0x02 << 8)
+#define S3C64XX_GPI5_VD5 (0x02 << 10)
+#define S3C64XX_GPI6_VD6 (0x02 << 12)
+#define S3C64XX_GPI7_VD7 (0x02 << 14)
+#define S3C64XX_GPI8_VD8 (0x02 << 16)
+#define S3C64XX_GPI9_VD9 (0x02 << 18)
+#define S3C64XX_GPI10_VD10 (0x02 << 20)
+#define S3C64XX_GPI11_VD11 (0x02 << 22)
+#define S3C64XX_GPI12_VD12 (0x02 << 24)
+#define S3C64XX_GPI13_VD13 (0x02 << 26)
+#define S3C64XX_GPI14_VD14 (0x02 << 28)
+#define S3C64XX_GPI15_VD15 (0x02 << 30)
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-j.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-j.h
new file mode 100644
index 000000000000..21a906299d30
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-j.h
@@ -0,0 +1,36 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-j.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank J register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPJCON (S3C64XX_GPJ_BASE + 0x00)
+#define S3C64XX_GPJDAT (S3C64XX_GPJ_BASE + 0x04)
+#define S3C64XX_GPJPUD (S3C64XX_GPJ_BASE + 0x08)
+#define S3C64XX_GPJCONSLP (S3C64XX_GPJ_BASE + 0x0c)
+#define S3C64XX_GPJPUDSLP (S3C64XX_GPJ_BASE + 0x10)
+
+#define S3C64XX_GPJ_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPJ_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPJ_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPJ0_VD16 (0x02 << 0)
+#define S3C64XX_GPJ1_VD17 (0x02 << 2)
+#define S3C64XX_GPJ2_VD18 (0x02 << 4)
+#define S3C64XX_GPJ3_VD19 (0x02 << 6)
+#define S3C64XX_GPJ4_VD20 (0x02 << 8)
+#define S3C64XX_GPJ5_VD21 (0x02 << 10)
+#define S3C64XX_GPJ6_VD22 (0x02 << 12)
+#define S3C64XX_GPJ7_VD23 (0x02 << 14)
+#define S3C64XX_GPJ8_LCD_HSYNC (0x02 << 16)
+#define S3C64XX_GPJ9_LCD_VSYNC (0x02 << 18)
+#define S3C64XX_GPJ10_LCD_VDEN (0x02 << 20)
+#define S3C64XX_GPJ11_LCD_VCLK (0x02 << 22)
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-n.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-n.h
new file mode 100644
index 000000000000..569e76120881
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-n.h
@@ -0,0 +1,54 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-n.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank N register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPNCON (S3C64XX_GPN_BASE + 0x00)
+#define S3C64XX_GPNDAT (S3C64XX_GPN_BASE + 0x04)
+#define S3C64XX_GPNPUD (S3C64XX_GPN_BASE + 0x08)
+
+#define S3C64XX_GPN_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPN_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPN_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPN0_EINT0 (0x02 << 0)
+#define S3C64XX_GPN0_KP_ROW0 (0x03 << 0)
+
+#define S3C64XX_GPN1_EINT1 (0x02 << 2)
+#define S3C64XX_GPN1_KP_ROW1 (0x03 << 2)
+
+#define S3C64XX_GPN2_EINT2 (0x02 << 4)
+#define S3C64XX_GPN2_KP_ROW2 (0x03 << 4)
+
+#define S3C64XX_GPN3_EINT3 (0x02 << 6)
+#define S3C64XX_GPN3_KP_ROW3 (0x03 << 6)
+
+#define S3C64XX_GPN4_EINT4 (0x02 << 8)
+#define S3C64XX_GPN4_KP_ROW4 (0x03 << 8)
+
+#define S3C64XX_GPN5_EINT5 (0x02 << 10)
+#define S3C64XX_GPN5_KP_ROW5 (0x03 << 10)
+
+#define S3C64XX_GPN6_EINT6 (0x02 << 12)
+#define S3C64XX_GPN6_KP_ROW6 (0x03 << 12)
+
+#define S3C64XX_GPN7_EINT7 (0x02 << 14)
+#define S3C64XX_GPN7_KP_ROW7 (0x03 << 14)
+
+#define S3C64XX_GPN8_EINT8 (0x02 << 16)
+#define S3C64XX_GPN9_EINT9 (0x02 << 18)
+#define S3C64XX_GPN10_EINT10 (0x02 << 20)
+#define S3C64XX_GPN11_EINT11 (0x02 << 22)
+#define S3C64XX_GPN12_EINT12 (0x02 << 24)
+#define S3C64XX_GPN13_EINT13 (0x02 << 26)
+#define S3C64XX_GPN14_EINT14 (0x02 << 28)
+#define S3C64XX_GPN15_EINT15 (0x02 << 30)
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-o.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-o.h
new file mode 100644
index 000000000000..b09e12954b57
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-o.h
@@ -0,0 +1,70 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-o.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank O register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPOCON (S3C64XX_GPO_BASE + 0x00)
+#define S3C64XX_GPODAT (S3C64XX_GPO_BASE + 0x04)
+#define S3C64XX_GPOPUD (S3C64XX_GPO_BASE + 0x08)
+#define S3C64XX_GPOCONSLP (S3C64XX_GPO_BASE + 0x0c)
+#define S3C64XX_GPOPUDSLP (S3C64XX_GPO_BASE + 0x10)
+
+#define S3C64XX_GPO_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPO_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPO_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPO0_MEM0_nCS2 (0x02 << 0)
+#define S3C64XX_GPO0_EINT_G7_0 (0x03 << 0)
+
+#define S3C64XX_GPO1_MEM0_nCS3 (0x02 << 2)
+#define S3C64XX_GPO1_EINT_G7_1 (0x03 << 2)
+
+#define S3C64XX_GPO2_MEM0_nCS4 (0x02 << 4)
+#define S3C64XX_GPO2_EINT_G7_2 (0x03 << 4)
+
+#define S3C64XX_GPO3_MEM0_nCS5 (0x02 << 6)
+#define S3C64XX_GPO3_EINT_G7_3 (0x03 << 6)
+
+#define S3C64XX_GPO4_EINT_G7_4 (0x03 << 8)
+
+#define S3C64XX_GPO5_EINT_G7_5 (0x03 << 10)
+
+#define S3C64XX_GPO6_MEM0_ADDR6 (0x02 << 12)
+#define S3C64XX_GPO6_EINT_G7_6 (0x03 << 12)
+
+#define S3C64XX_GPO7_MEM0_ADDR7 (0x02 << 14)
+#define S3C64XX_GPO7_EINT_G7_7 (0x03 << 14)
+
+#define S3C64XX_GPO8_MEM0_ADDR8 (0x02 << 16)
+#define S3C64XX_GPO8_EINT_G7_8 (0x03 << 16)
+
+#define S3C64XX_GPO9_MEM0_ADDR9 (0x02 << 18)
+#define S3C64XX_GPO9_EINT_G7_9 (0x03 << 18)
+
+#define S3C64XX_GPO10_MEM0_ADDR10 (0x02 << 20)
+#define S3C64XX_GPO10_EINT_G7_10 (0x03 << 20)
+
+#define S3C64XX_GPO11_MEM0_ADDR11 (0x02 << 22)
+#define S3C64XX_GPO11_EINT_G7_11 (0x03 << 22)
+
+#define S3C64XX_GPO12_MEM0_ADDR12 (0x02 << 24)
+#define S3C64XX_GPO12_EINT_G7_12 (0x03 << 24)
+
+#define S3C64XX_GPO13_MEM0_ADDR13 (0x02 << 26)
+#define S3C64XX_GPO13_EINT_G7_13 (0x03 << 26)
+
+#define S3C64XX_GPO14_MEM0_ADDR14 (0x02 << 28)
+#define S3C64XX_GPO14_EINT_G7_14 (0x03 << 28)
+
+#define S3C64XX_GPO15_MEM0_ADDR15 (0x02 << 30)
+#define S3C64XX_GPO15_EINT_G7_15 (0x03 << 30)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-p.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-p.h
new file mode 100644
index 000000000000..92f00517926b
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-p.h
@@ -0,0 +1,69 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-p.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank P register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPPCON (S3C64XX_GPP_BASE + 0x00)
+#define S3C64XX_GPPDAT (S3C64XX_GPP_BASE + 0x04)
+#define S3C64XX_GPPPUD (S3C64XX_GPP_BASE + 0x08)
+#define S3C64XX_GPPCONSLP (S3C64XX_GPP_BASE + 0x0c)
+#define S3C64XX_GPPPUDSLP (S3C64XX_GPP_BASE + 0x10)
+
+#define S3C64XX_GPP_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPP_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPP_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPP0_MEM0_ADDRV (0x02 << 0)
+#define S3C64XX_GPP0_EINT_G8_0 (0x03 << 0)
+
+#define S3C64XX_GPP1_MEM0_SMCLK (0x02 << 2)
+#define S3C64XX_GPP1_EINT_G8_1 (0x03 << 2)
+
+#define S3C64XX_GPP2_MEM0_nWAIT (0x02 << 4)
+#define S3C64XX_GPP2_EINT_G8_2 (0x03 << 4)
+
+#define S3C64XX_GPP3_MEM0_RDY0_ALE (0x02 << 6)
+#define S3C64XX_GPP3_EINT_G8_3 (0x03 << 6)
+
+#define S3C64XX_GPP4_MEM0_RDY1_CLE (0x02 << 8)
+#define S3C64XX_GPP4_EINT_G8_4 (0x03 << 8)
+
+#define S3C64XX_GPP5_MEM0_INTsm0_FWE (0x02 << 10)
+#define S3C64XX_GPP5_EINT_G8_5 (0x03 << 10)
+
+#define S3C64XX_GPP6_MEM0_(null) (0x02 << 12)
+#define S3C64XX_GPP6_EINT_G8_6 (0x03 << 12)
+
+#define S3C64XX_GPP7_MEM0_INTsm1_FRE (0x02 << 14)
+#define S3C64XX_GPP7_EINT_G8_7 (0x03 << 14)
+
+#define S3C64XX_GPP8_MEM0_RPn_RnB (0x02 << 16)
+#define S3C64XX_GPP8_EINT_G8_8 (0x03 << 16)
+
+#define S3C64XX_GPP9_MEM0_ATA_RESET (0x02 << 18)
+#define S3C64XX_GPP9_EINT_G8_9 (0x03 << 18)
+
+#define S3C64XX_GPP10_MEM0_ATA_INPACK (0x02 << 20)
+#define S3C64XX_GPP10_EINT_G8_10 (0x03 << 20)
+
+#define S3C64XX_GPP11_MEM0_ATA_REG (0x02 << 22)
+#define S3C64XX_GPP11_EINT_G8_11 (0x03 << 22)
+
+#define S3C64XX_GPP12_MEM0_ATA_WE (0x02 << 24)
+#define S3C64XX_GPP12_EINT_G8_12 (0x03 << 24)
+
+#define S3C64XX_GPP13_MEM0_ATA_OE (0x02 << 26)
+#define S3C64XX_GPP13_EINT_G8_13 (0x03 << 26)
+
+#define S3C64XX_GPP14_MEM0_ATA_CD (0x02 << 28)
+#define S3C64XX_GPP14_EINT_G8_14 (0x03 << 28)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/gpio-bank-q.h b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-q.h
new file mode 100644
index 000000000000..565e60aaee47
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/gpio-bank-q.h
@@ -0,0 +1,46 @@
+/* linux/arch/arm/plat-s3c64xx/include/plat/gpio-bank-q.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * GPIO Bank Q register and configuration definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C64XX_GPQCON (S3C64XX_GPQ_BASE + 0x00)
+#define S3C64XX_GPQDAT (S3C64XX_GPQ_BASE + 0x04)
+#define S3C64XX_GPQPUD (S3C64XX_GPQ_BASE + 0x08)
+#define S3C64XX_GPQCONSLP (S3C64XX_GPQ_BASE + 0x0c)
+#define S3C64XX_GPQPUDSLP (S3C64XX_GPQ_BASE + 0x10)
+
+#define S3C64XX_GPQ_CONMASK(__gpio) (0x3 << ((__gpio) * 2))
+#define S3C64XX_GPQ_INPUT(__gpio) (0x0 << ((__gpio) * 2))
+#define S3C64XX_GPQ_OUTPUT(__gpio) (0x1 << ((__gpio) * 2))
+
+#define S3C64XX_GPQ0_MEM0_ADDR18_RAS (0x02 << 0)
+#define S3C64XX_GPQ0_EINT_G9_0 (0x03 << 0)
+
+#define S3C64XX_GPQ1_MEM0_ADDR19_CAS (0x02 << 2)
+#define S3C64XX_GPQ1_EINT_G9_1 (0x03 << 2)
+
+#define S3C64XX_GPQ2_EINT_G9_2 (0x03 << 4)
+
+#define S3C64XX_GPQ3_EINT_G9_3 (0x03 << 6)
+
+#define S3C64XX_GPQ4_EINT_G9_4 (0x03 << 8)
+
+#define S3C64XX_GPQ5_EINT_G9_5 (0x03 << 10)
+
+#define S3C64XX_GPQ6_EINT_G9_6 (0x03 << 12)
+
+#define S3C64XX_GPQ7_MEM0_ADDR17_WENDMC (0x02 << 14)
+#define S3C64XX_GPQ7_EINT_G9_7 (0x03 << 14)
+
+#define S3C64XX_GPQ8_MEM0_ADDR16_APDMC (0x02 << 16)
+#define S3C64XX_GPQ8_EINT_G9_8 (0x03 << 16)
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/irqs.h b/arch/arm/plat-s3c64xx/include/plat/irqs.h
new file mode 100644
index 000000000000..02e8dd4c97d5
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/irqs.h
@@ -0,0 +1,201 @@
+/* linux/arch/arm/plat-s3c64xx/include/mach/irqs.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - Common IRQ support
+ */
+
+#ifndef __ASM_PLAT_S3C64XX_IRQS_H
+#define __ASM_PLAT_S3C64XX_IRQS_H __FILE__
+
+/* we keep the first set of CPU IRQs out of the range of
+ * the ISA space, so that the PC104 has them to itself
+ * and we don't end up having to do horrible things to the
+ * standard ISA drivers....
+ *
+ * note, since we're using the VICs, our start must be a
+ * mulitple of 32 to allow the common code to work
+ */
+
+#define S3C_IRQ_OFFSET (32)
+
+#define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET)
+
+#define S3C_VIC0_BASE S3C_IRQ(0)
+#define S3C_VIC1_BASE S3C_IRQ(32)
+
+/* UART interrupts, each UART has 4 intterupts per channel so
+ * use the space between the ISA and S3C main interrupts. Note, these
+ * are not in the same order as the S3C24XX series! */
+
+#define IRQ_S3CUART_BASE0 (16)
+#define IRQ_S3CUART_BASE1 (20)
+#define IRQ_S3CUART_BASE2 (24)
+#define IRQ_S3CUART_BASE3 (28)
+
+#define UART_IRQ_RXD (0)
+#define UART_IRQ_ERR (1)
+#define UART_IRQ_TXD (2)
+#define UART_IRQ_MODEM (3)
+
+#define IRQ_S3CUART_RX0 (IRQ_S3CUART_BASE0 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX0 (IRQ_S3CUART_BASE0 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR0 (IRQ_S3CUART_BASE0 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX1 (IRQ_S3CUART_BASE1 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX1 (IRQ_S3CUART_BASE1 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR1 (IRQ_S3CUART_BASE1 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX2 (IRQ_S3CUART_BASE2 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX2 (IRQ_S3CUART_BASE2 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR2 (IRQ_S3CUART_BASE2 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX3 (IRQ_S3CUART_BASE3 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX3 (IRQ_S3CUART_BASE3 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR3 (IRQ_S3CUART_BASE3 + UART_IRQ_ERR)
+
+/* VIC based IRQs */
+
+#define S3C64XX_IRQ_VIC0(x) (S3C_VIC0_BASE + (x))
+#define S3C64XX_IRQ_VIC1(x) (S3C_VIC1_BASE + (x))
+
+/* VIC0 */
+
+#define IRQ_EINT0_3 S3C64XX_IRQ_VIC0(0)
+#define IRQ_EINT4_11 S3C64XX_IRQ_VIC0(1)
+#define IRQ_RTC_TIC S3C64XX_IRQ_VIC0(2)
+#define IRQ_CAMIF_C S3C64XX_IRQ_VIC0(3)
+#define IRQ_CAMIF_P S3C64XX_IRQ_VIC0(4)
+#define IRQ_CAMIF_MC S3C64XX_IRQ_VIC0(5)
+#define IRQ_S3C6410_IIC1 S3C64XX_IRQ_VIC0(5)
+#define IRQ_S3C6410_IIS S3C64XX_IRQ_VIC0(6)
+#define IRQ_S3C6400_CAMIF_MP S3C64XX_IRQ_VIC0(6)
+#define IRQ_CAMIF_WE_C S3C64XX_IRQ_VIC0(7)
+#define IRQ_S3C6410_G3D S3C64XX_IRQ_VIC0(8)
+#define IRQ_S3C6400_CAMIF_WE_P S3C64XX_IRQ_VIC0(8)
+#define IRQ_POST0 S3C64XX_IRQ_VIC0(9)
+#define IRQ_ROTATOR S3C64XX_IRQ_VIC0(10)
+#define IRQ_2D S3C64XX_IRQ_VIC0(11)
+#define IRQ_TVENC S3C64XX_IRQ_VIC0(12)
+#define IRQ_SCALER S3C64XX_IRQ_VIC0(13)
+#define IRQ_BATF S3C64XX_IRQ_VIC0(14)
+#define IRQ_JPEG S3C64XX_IRQ_VIC0(15)
+#define IRQ_MFC S3C64XX_IRQ_VIC0(16)
+#define IRQ_SDMA0 S3C64XX_IRQ_VIC0(17)
+#define IRQ_SDMA1 S3C64XX_IRQ_VIC0(18)
+#define IRQ_ARM_DMAERR S3C64XX_IRQ_VIC0(19)
+#define IRQ_ARM_DMA S3C64XX_IRQ_VIC0(20)
+#define IRQ_ARM_DMAS S3C64XX_IRQ_VIC0(21)
+#define IRQ_KEYPAD S3C64XX_IRQ_VIC0(22)
+#define IRQ_TIMER0_VIC S3C64XX_IRQ_VIC0(23)
+#define IRQ_TIMER1_VIC S3C64XX_IRQ_VIC0(24)
+#define IRQ_TIMER2_VIC S3C64XX_IRQ_VIC0(25)
+#define IRQ_WDT S3C64XX_IRQ_VIC0(26)
+#define IRQ_TIMER3_VIC S3C64XX_IRQ_VIC0(27)
+#define IRQ_TIMER4_VIC S3C64XX_IRQ_VIC0(28)
+#define IRQ_LCD_FIFO S3C64XX_IRQ_VIC0(29)
+#define IRQ_LCD_VSYNC S3C64XX_IRQ_VIC0(30)
+#define IRQ_LCD_SYSTEM S3C64XX_IRQ_VIC0(31)
+
+/* VIC1 */
+
+#define IRQ_EINT12_19 S3C64XX_IRQ_VIC1(0)
+#define IRQ_EINT20_27 S3C64XX_IRQ_VIC1(1)
+#define IRQ_PCM0 S3C64XX_IRQ_VIC1(2)
+#define IRQ_PCM1 S3C64XX_IRQ_VIC1(3)
+#define IRQ_AC97 S3C64XX_IRQ_VIC1(4)
+#define IRQ_UART0 S3C64XX_IRQ_VIC1(5)
+#define IRQ_UART1 S3C64XX_IRQ_VIC1(6)
+#define IRQ_UART2 S3C64XX_IRQ_VIC1(7)
+#define IRQ_UART3 S3C64XX_IRQ_VIC1(8)
+#define IRQ_DMA0 S3C64XX_IRQ_VIC1(9)
+#define IRQ_DMA1 S3C64XX_IRQ_VIC1(10)
+#define IRQ_ONENAND0 S3C64XX_IRQ_VIC1(11)
+#define IRQ_ONENAND1 S3C64XX_IRQ_VIC1(12)
+#define IRQ_NFC S3C64XX_IRQ_VIC1(13)
+#define IRQ_CFCON S3C64XX_IRQ_VIC1(14)
+#define IRQ_UHOST S3C64XX_IRQ_VIC1(15)
+#define IRQ_SPI0 S3C64XX_IRQ_VIC1(16)
+#define IRQ_SPI1 S3C64XX_IRQ_VIC1(17)
+#define IRQ_IIC S3C64XX_IRQ_VIC1(18)
+#define IRQ_HSItx S3C64XX_IRQ_VIC1(19)
+#define IRQ_HSIrx S3C64XX_IRQ_VIC1(20)
+#define IRQ_RESERVED S3C64XX_IRQ_VIC1(21)
+#define IRQ_MSM S3C64XX_IRQ_VIC1(22)
+#define IRQ_HOSTIF S3C64XX_IRQ_VIC1(23)
+#define IRQ_HSMMC0 S3C64XX_IRQ_VIC1(24)
+#define IRQ_HSMMC1 S3C64XX_IRQ_VIC1(25)
+#define IRQ_HSMMC2 IRQ_SPI1 /* shared with SPI1 */
+#define IRQ_OTG S3C64XX_IRQ_VIC1(26)
+#define IRQ_IRDA S3C64XX_IRQ_VIC1(27)
+#define IRQ_RTC_ALARM S3C64XX_IRQ_VIC1(28)
+#define IRQ_SEC S3C64XX_IRQ_VIC1(29)
+#define IRQ_PENDN S3C64XX_IRQ_VIC1(30)
+#define IRQ_TC IRQ_PENDN
+#define IRQ_ADC S3C64XX_IRQ_VIC1(31)
+
+#define S3C64XX_TIMER_IRQ(x) S3C_IRQ(64 + (x))
+
+#define IRQ_TIMER0 S3C64XX_TIMER_IRQ(0)
+#define IRQ_TIMER1 S3C64XX_TIMER_IRQ(1)
+#define IRQ_TIMER2 S3C64XX_TIMER_IRQ(2)
+#define IRQ_TIMER3 S3C64XX_TIMER_IRQ(3)
+#define IRQ_TIMER4 S3C64XX_TIMER_IRQ(4)
+
+/* compatibility for device defines */
+
+#define IRQ_IIC1 IRQ_S3C6410_IIC1
+
+/* Since the IRQ_EINT(x) are a linear mapping on current s3c64xx series
+ * we just defined them as an IRQ_EINT(x) macro from S3C_IRQ_EINT_BASE
+ * which we place after the pair of VICs. */
+
+#define S3C_IRQ_EINT_BASE S3C_IRQ(64+5)
+
+#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE)
+#define IRQ_EINT(x) S3C_EINT(x)
+
+/* Next the external interrupt groups. These are similar to the IRQ_EINT(x)
+ * that they are sourced from the GPIO pins but with a different scheme for
+ * priority and source indication.
+ *
+ * The IRQ_EINT(x) can be thought of as 'group 0' of the available GPIO
+ * interrupts, but for historical reasons they are kept apart from these
+ * next interrupts.
+ *
+ * Use IRQ_EINT_GROUP(group, offset) to get the number for use in the
+ * machine specific support files.
+ */
+
+#define IRQ_EINT_GROUP1_NR (15)
+#define IRQ_EINT_GROUP2_NR (8)
+#define IRQ_EINT_GROUP3_NR (5)
+#define IRQ_EINT_GROUP4_NR (14)
+#define IRQ_EINT_GROUP5_NR (7)
+#define IRQ_EINT_GROUP6_NR (10)
+#define IRQ_EINT_GROUP7_NR (16)
+#define IRQ_EINT_GROUP8_NR (15)
+#define IRQ_EINT_GROUP9_NR (9)
+
+#define IRQ_EINT_GROUP_BASE S3C_EINT(28)
+#define IRQ_EINT_GROUP1_BASE (IRQ_EINT_GROUP_BASE + 0x00)
+#define IRQ_EINT_GROUP2_BASE (IRQ_EINT_GROUP1_BASE + IRQ_EINT_GROUP1_NR)
+#define IRQ_EINT_GROUP3_BASE (IRQ_EINT_GROUP2_BASE + IRQ_EINT_GROUP2_NR)
+#define IRQ_EINT_GROUP4_BASE (IRQ_EINT_GROUP3_BASE + IRQ_EINT_GROUP3_NR)
+#define IRQ_EINT_GROUP5_BASE (IRQ_EINT_GROUP4_BASE + IRQ_EINT_GROUP4_NR)
+#define IRQ_EINT_GROUP6_BASE (IRQ_EINT_GROUP5_BASE + IRQ_EINT_GROUP5_NR)
+#define IRQ_EINT_GROUP7_BASE (IRQ_EINT_GROUP6_BASE + IRQ_EINT_GROUP6_NR)
+#define IRQ_EINT_GROUP8_BASE (IRQ_EINT_GROUP7_BASE + IRQ_EINT_GROUP7_NR)
+#define IRQ_EINT_GROUP9_BASE (IRQ_EINT_GROUP8_BASE + IRQ_EINT_GROUP8_NR)
+
+#define IRQ_EINT_GROUP(group, no) (IRQ_EINT_GROUP##group##__BASE + (x))
+
+/* Set the default NR_IRQS */
+
+#define NR_IRQS (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1)
+
+#endif /* __ASM_PLAT_S3C64XX_IRQS_H */
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/pll.h b/arch/arm/plat-s3c64xx/include/plat/pll.h
new file mode 100644
index 000000000000..90bbd72fdc4e
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/pll.h
@@ -0,0 +1,74 @@
+/* arch/arm/plat-s3c64xx/include/plat/pll.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX PLL 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.
+*/
+
+#define S3C6400_PLL_MDIV_MASK ((1 << (25-16+1)) - 1)
+#define S3C6400_PLL_PDIV_MASK ((1 << (13-8+1)) - 1)
+#define S3C6400_PLL_SDIV_MASK ((1 << (2-0+1)) - 1)
+#define S3C6400_PLL_MDIV_SHIFT (16)
+#define S3C6400_PLL_PDIV_SHIFT (8)
+#define S3C6400_PLL_SDIV_SHIFT (0)
+
+#include <asm/div64.h>
+
+static inline unsigned long s3c6400_get_pll(unsigned long baseclk,
+ u32 pllcon)
+{
+ u32 mdiv, pdiv, sdiv;
+ u64 fvco = baseclk;
+
+ mdiv = (pllcon >> S3C6400_PLL_MDIV_SHIFT) & S3C6400_PLL_MDIV_MASK;
+ pdiv = (pllcon >> S3C6400_PLL_PDIV_SHIFT) & S3C6400_PLL_PDIV_MASK;
+ sdiv = (pllcon >> S3C6400_PLL_SDIV_SHIFT) & S3C6400_PLL_SDIV_MASK;
+
+ fvco *= mdiv;
+ do_div(fvco, (pdiv << sdiv));
+
+ return (unsigned long)fvco;
+}
+
+#define S3C6400_EPLL_MDIV_MASK ((1 << (23-16)) - 1)
+#define S3C6400_EPLL_PDIV_MASK ((1 << (13-8)) - 1)
+#define S3C6400_EPLL_SDIV_MASK ((1 << (2-0)) - 1)
+#define S3C6400_EPLL_MDIV_SHIFT (16)
+#define S3C6400_EPLL_PDIV_SHIFT (8)
+#define S3C6400_EPLL_SDIV_SHIFT (0)
+#define S3C6400_EPLL_KDIV_MASK (0xffff)
+
+static inline unsigned long s3c6400_get_epll(unsigned long baseclk)
+{
+ unsigned long result;
+ u32 epll0 = __raw_readl(S3C_EPLL_CON0);
+ u32 epll1 = __raw_readl(S3C_EPLL_CON1);
+ u32 mdiv, pdiv, sdiv, kdiv;
+ u64 tmp;
+
+ mdiv = (epll0 >> S3C6400_EPLL_MDIV_SHIFT) & S3C6400_EPLL_MDIV_MASK;
+ pdiv = (epll0 >> S3C6400_EPLL_PDIV_SHIFT) & S3C6400_EPLL_PDIV_MASK;
+ sdiv = (epll0 >> S3C6400_EPLL_SDIV_SHIFT) & S3C6400_EPLL_SDIV_MASK;
+ kdiv = epll1 & S3C6400_EPLL_KDIV_MASK;
+
+ /* We need to multiple baseclk by mdiv (the integer part) and kdiv
+ * which is in 2^16ths, so shift mdiv up (does not overflow) and
+ * add kdiv before multiplying. The use of tmp is to avoid any
+ * overflows before shifting bac down into result when multipling
+ * by the mdiv and kdiv pair.
+ */
+
+ tmp = baseclk;
+ tmp *= (mdiv << 16) + kdiv;
+ do_div(tmp, (pdiv << sdiv));
+ result = tmp >> 16;
+
+ return result;
+}
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h
new file mode 100644
index 000000000000..b1082c163247
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h
@@ -0,0 +1,224 @@
+/* arch/arm/plat-s3c64xx/include/plat/regs-clock.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX clock register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_REGS_CLOCK_H
+#define __PLAT_REGS_CLOCK_H __FILE__
+
+#define S3C_CLKREG(x) (S3C_VA_SYS + (x))
+
+#define S3C_APLL_LOCK S3C_CLKREG(0x00)
+#define S3C_MPLL_LOCK S3C_CLKREG(0x04)
+#define S3C_EPLL_LOCK S3C_CLKREG(0x08)
+#define S3C_APLL_CON S3C_CLKREG(0x0C)
+#define S3C_MPLL_CON S3C_CLKREG(0x10)
+#define S3C_EPLL_CON0 S3C_CLKREG(0x14)
+#define S3C_EPLL_CON1 S3C_CLKREG(0x18)
+#define S3C_CLK_SRC S3C_CLKREG(0x1C)
+#define S3C_CLK_DIV0 S3C_CLKREG(0x20)
+#define S3C_CLK_DIV1 S3C_CLKREG(0x24)
+#define S3C_CLK_DIV2 S3C_CLKREG(0x28)
+#define S3C_CLK_OUT S3C_CLKREG(0x2C)
+#define S3C_HCLK_GATE S3C_CLKREG(0x30)
+#define S3C_PCLK_GATE S3C_CLKREG(0x34)
+#define S3C_SCLK_GATE S3C_CLKREG(0x38)
+
+/* CLKDIV0 */
+#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28)
+#define S3C6400_CLKDIV0_MFC_SHIFT (28)
+#define S3C6400_CLKDIV0_JPEG_MASK (0xf << 24)
+#define S3C6400_CLKDIV0_JPEG_SHIFT (24)
+#define S3C6400_CLKDIV0_CAM_MASK (0xf << 20)
+#define S3C6400_CLKDIV0_CAM_SHIFT (20)
+#define S3C6400_CLKDIV0_SECURITY_MASK (0x3 << 18)
+#define S3C6400_CLKDIV0_SECURITY_SHIFT (18)
+#define S3C6400_CLKDIV0_PCLK_MASK (0xf << 12)
+#define S3C6400_CLKDIV0_PCLK_SHIFT (12)
+#define S3C6400_CLKDIV0_HCLK2_MASK (0x7 << 9)
+#define S3C6400_CLKDIV0_HCLK2_SHIFT (9)
+#define S3C6400_CLKDIV0_HCLK_MASK (0x1 << 8)
+#define S3C6400_CLKDIV0_HCLK_SHIFT (8)
+#define S3C6400_CLKDIV0_MPLL_MASK (0x1 << 4)
+#define S3C6400_CLKDIV0_MPLL_SHIFT (4)
+#define S3C6400_CLKDIV0_ARM_MASK (0x3 << 0)
+#define S3C6410_CLKDIV0_ARM_MASK (0x7 << 0)
+#define S3C6400_CLKDIV0_ARM_SHIFT (0)
+
+/* CLKDIV1 */
+#define S3C6410_CLKDIV1_FIMC_MASK (0xf << 24)
+#define S3C6410_CLKDIV1_FIMC_SHIFT (24)
+#define S3C6400_CLKDIV1_UHOST_MASK (0xf << 20)
+#define S3C6400_CLKDIV1_UHOST_SHIFT (20)
+#define S3C6400_CLKDIV1_SCALER_MASK (0xf << 16)
+#define S3C6400_CLKDIV1_SCALER_SHIFT (16)
+#define S3C6400_CLKDIV1_LCD_MASK (0xf << 12)
+#define S3C6400_CLKDIV1_LCD_SHIFT (12)
+#define S3C6400_CLKDIV1_MMC2_MASK (0xf << 8)
+#define S3C6400_CLKDIV1_MMC2_SHIFT (8)
+#define S3C6400_CLKDIV1_MMC1_MASK (0xf << 4)
+#define S3C6400_CLKDIV1_MMC1_SHIFT (4)
+#define S3C6400_CLKDIV1_MMC0_MASK (0xf << 0)
+#define S3C6400_CLKDIV1_MMC0_SHIFT (0)
+
+/* CLKDIV2 */
+#define S3C6410_CLKDIV2_AUDIO2_MASK (0xf << 24)
+#define S3C6410_CLKDIV2_AUDIO2_SHIFT (24)
+#define S3C6400_CLKDIV2_IRDA_MASK (0xf << 20)
+#define S3C6400_CLKDIV2_IRDA_SHIFT (20)
+#define S3C6400_CLKDIV2_UART_MASK (0xf << 16)
+#define S3C6400_CLKDIV2_UART_SHIFT (16)
+#define S3C6400_CLKDIV2_AUDIO1_MASK (0xf << 12)
+#define S3C6400_CLKDIV2_AUDIO1_SHIFT (12)
+#define S3C6400_CLKDIV2_AUDIO0_MASK (0xf << 8)
+#define S3C6400_CLKDIV2_AUDIO0_SHIFT (8)
+#define S3C6400_CLKDIV2_SPI1_MASK (0xf << 4)
+#define S3C6400_CLKDIV2_SPI1_SHIFT (4)
+#define S3C6400_CLKDIV2_SPI0_MASK (0xf << 0)
+#define S3C6400_CLKDIV2_SPI0_SHIFT (0)
+
+/* HCLK GATE Registers */
+#define S3C_CLKCON_HCLK_BUS (1<<30)
+#define S3C_CLKCON_HCLK_SECUR (1<<29)
+#define S3C_CLKCON_HCLK_SDMA1 (1<<28)
+#define S3C_CLKCON_HCLK_SDMA2 (1<<27)
+#define S3C_CLKCON_HCLK_UHOST (1<<26)
+#define S3C_CLKCON_HCLK_IROM (1<<25)
+#define S3C_CLKCON_HCLK_DDR1 (1<<24)
+#define S3C_CLKCON_HCLK_DDR0 (1<<23)
+#define S3C_CLKCON_HCLK_MEM1 (1<<22)
+#define S3C_CLKCON_HCLK_MEM0 (1<<21)
+#define S3C_CLKCON_HCLK_USB (1<<20)
+#define S3C_CLKCON_HCLK_HSMMC2 (1<<19)
+#define S3C_CLKCON_HCLK_HSMMC1 (1<<18)
+#define S3C_CLKCON_HCLK_HSMMC0 (1<<17)
+#define S3C_CLKCON_HCLK_MDP (1<<16)
+#define S3C_CLKCON_HCLK_DHOST (1<<15)
+#define S3C_CLKCON_HCLK_IHOST (1<<14)
+#define S3C_CLKCON_HCLK_DMA1 (1<<13)
+#define S3C_CLKCON_HCLK_DMA0 (1<<12)
+#define S3C_CLKCON_HCLK_JPEG (1<<11)
+#define S3C_CLKCON_HCLK_CAMIF (1<<10)
+#define S3C_CLKCON_HCLK_SCALER (1<<9)
+#define S3C_CLKCON_HCLK_2D (1<<8)
+#define S3C_CLKCON_HCLK_TV (1<<7)
+#define S3C_CLKCON_HCLK_POST0 (1<<5)
+#define S3C_CLKCON_HCLK_ROT (1<<4)
+#define S3C_CLKCON_HCLK_LCD (1<<3)
+#define S3C_CLKCON_HCLK_TZIC (1<<2)
+#define S3C_CLKCON_HCLK_INTC (1<<1)
+#define S3C_CLKCON_HCLK_MFC (1<<0)
+
+/* PCLK GATE Registers */
+#define S3C6410_CLKCON_PCLK_I2C1 (1<<27)
+#define S3C6410_CLKCON_PCLK_IIS2 (1<<26)
+#define S3C_CLKCON_PCLK_SKEY (1<<24)
+#define S3C_CLKCON_PCLK_CHIPID (1<<23)
+#define S3C_CLKCON_PCLK_SPI1 (1<<22)
+#define S3C_CLKCON_PCLK_SPI0 (1<<21)
+#define S3C_CLKCON_PCLK_HSIRX (1<<20)
+#define S3C_CLKCON_PCLK_HSITX (1<<19)
+#define S3C_CLKCON_PCLK_GPIO (1<<18)
+#define S3C_CLKCON_PCLK_IIC (1<<17)
+#define S3C_CLKCON_PCLK_IIS1 (1<<16)
+#define S3C_CLKCON_PCLK_IIS0 (1<<15)
+#define S3C_CLKCON_PCLK_AC97 (1<<14)
+#define S3C_CLKCON_PCLK_TZPC (1<<13)
+#define S3C_CLKCON_PCLK_TSADC (1<<12)
+#define S3C_CLKCON_PCLK_KEYPAD (1<<11)
+#define S3C_CLKCON_PCLK_IRDA (1<<10)
+#define S3C_CLKCON_PCLK_PCM1 (1<<9)
+#define S3C_CLKCON_PCLK_PCM0 (1<<8)
+#define S3C_CLKCON_PCLK_PWM (1<<7)
+#define S3C_CLKCON_PCLK_RTC (1<<6)
+#define S3C_CLKCON_PCLK_WDT (1<<5)
+#define S3C_CLKCON_PCLK_UART3 (1<<4)
+#define S3C_CLKCON_PCLK_UART2 (1<<3)
+#define S3C_CLKCON_PCLK_UART1 (1<<2)
+#define S3C_CLKCON_PCLK_UART0 (1<<1)
+#define S3C_CLKCON_PCLK_MFC (1<<0)
+
+/* SCLK GATE Registers */
+#define S3C_CLKCON_SCLK_UHOST (1<<30)
+#define S3C_CLKCON_SCLK_MMC2_48 (1<<29)
+#define S3C_CLKCON_SCLK_MMC1_48 (1<<28)
+#define S3C_CLKCON_SCLK_MMC0_48 (1<<27)
+#define S3C_CLKCON_SCLK_MMC2 (1<<26)
+#define S3C_CLKCON_SCLK_MMC1 (1<<25)
+#define S3C_CLKCON_SCLK_MMC0 (1<<24)
+#define S3C_CLKCON_SCLK_SPI1_48 (1<<23)
+#define S3C_CLKCON_SCLK_SPI0_48 (1<<22)
+#define S3C_CLKCON_SCLK_SPI1 (1<<21)
+#define S3C_CLKCON_SCLK_SPI0 (1<<20)
+#define S3C_CLKCON_SCLK_DAC27 (1<<19)
+#define S3C_CLKCON_SCLK_TV27 (1<<18)
+#define S3C_CLKCON_SCLK_SCALER27 (1<<17)
+#define S3C_CLKCON_SCLK_SCALER (1<<16)
+#define S3C_CLKCON_SCLK_LCD27 (1<<15)
+#define S3C_CLKCON_SCLK_LCD (1<<14)
+#define S3C6400_CLKCON_SCLK_POST1_27 (1<<13)
+#define S3C6410_CLKCON_FIMC (1<<13)
+#define S3C_CLKCON_SCLK_POST0_27 (1<<12)
+#define S3C6400_CLKCON_SCLK_POST1 (1<<11)
+#define S3C6410_CLKCON_SCLK_AUDIO2 (1<<11)
+#define S3C_CLKCON_SCLK_POST0 (1<<10)
+#define S3C_CLKCON_SCLK_AUDIO1 (1<<9)
+#define S3C_CLKCON_SCLK_AUDIO0 (1<<8)
+#define S3C_CLKCON_SCLK_SECUR (1<<7)
+#define S3C_CLKCON_SCLK_IRDA (1<<6)
+#define S3C_CLKCON_SCLK_UART (1<<5)
+#define S3C_CLKCON_SCLK_ONENAND (1<<4)
+#define S3C_CLKCON_SCLK_MFC (1<<3)
+#define S3C_CLKCON_SCLK_CAM (1<<2)
+#define S3C_CLKCON_SCLK_JPEG (1<<1)
+
+/* CLKSRC */
+
+#define S3C6400_CLKSRC_APLL_MOUT (1 << 0)
+#define S3C6400_CLKSRC_MPLL_MOUT (1 << 1)
+#define S3C6400_CLKSRC_EPLL_MOUT (1 << 2)
+#define S3C6400_CLKSRC_APLL_MOUT_SHIFT (0)
+#define S3C6400_CLKSRC_MPLL_MOUT_SHIFT (1)
+#define S3C6400_CLKSRC_EPLL_MOUT_SHIFT (2)
+#define S3C6400_CLKSRC_MFC (1 << 4)
+
+#define S3C6410_CLKSRC_TV27_MASK (0x1 << 31)
+#define S3C6410_CLKSRC_TV27_SHIFT (31)
+#define S3C6410_CLKSRC_DAC27_MASK (0x1 << 30)
+#define S3C6410_CLKSRC_DAC27_SHIFT (30)
+#define S3C6400_CLKSRC_SCALER_MASK (0x3 << 28)
+#define S3C6400_CLKSRC_SCALER_SHIFT (28)
+#define S3C6400_CLKSRC_LCD_MASK (0x3 << 26)
+#define S3C6400_CLKSRC_LCD_SHIFT (26)
+#define S3C6400_CLKSRC_IRDA_MASK (0x3 << 24)
+#define S3C6400_CLKSRC_IRDA_SHIFT (24)
+#define S3C6400_CLKSRC_MMC2_MASK (0x3 << 22)
+#define S3C6400_CLKSRC_MMC2_SHIFT (22)
+#define S3C6400_CLKSRC_MMC1_MASK (0x3 << 20)
+#define S3C6400_CLKSRC_MMC1_SHIFT (20)
+#define S3C6400_CLKSRC_MMC0_MASK (0x3 << 18)
+#define S3C6400_CLKSRC_MMC0_SHIFT (18)
+#define S3C6400_CLKSRC_SPI1_MASK (0x3 << 16)
+#define S3C6400_CLKSRC_SPI1_SHIFT (16)
+#define S3C6400_CLKSRC_SPI0_MASK (0x3 << 14)
+#define S3C6400_CLKSRC_SPI0_SHIFT (14)
+#define S3C6400_CLKSRC_UART_MASK (0x1 << 13)
+#define S3C6400_CLKSRC_UART_SHIFT (13)
+#define S3C6400_CLKSRC_AUDIO1_MASK (0x7 << 10)
+#define S3C6400_CLKSRC_AUDIO1_SHIFT (10)
+#define S3C6400_CLKSRC_AUDIO0_MASK (0x7 << 7)
+#define S3C6400_CLKSRC_AUDIO0_SHIFT (7)
+#define S3C6400_CLKSRC_UHOST_MASK (0x3 << 5)
+#define S3C6400_CLKSRC_UHOST_SHIFT (5)
+
+
+#endif /* _PLAT_REGS_CLOCK_H */
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h b/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h
new file mode 100644
index 000000000000..75b873d82808
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/regs-gpio.h
@@ -0,0 +1,35 @@
+/* linux/arch/arm/plat-s3c64xx/include/mach/regs-gpio.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - GPIO register definitions
+ */
+
+#ifndef __ASM_PLAT_S3C64XX_REGS_GPIO_H
+#define __ASM_PLAT_S3C64XX_REGS_GPIO_H __FILE__
+
+/* Base addresses for each of the banks */
+
+#define S3C64XX_GPA_BASE (S3C64XX_VA_GPIO + 0x0000)
+#define S3C64XX_GPB_BASE (S3C64XX_VA_GPIO + 0x0020)
+#define S3C64XX_GPC_BASE (S3C64XX_VA_GPIO + 0x0040)
+#define S3C64XX_GPD_BASE (S3C64XX_VA_GPIO + 0x0060)
+#define S3C64XX_GPE_BASE (S3C64XX_VA_GPIO + 0x0080)
+#define S3C64XX_GPF_BASE (S3C64XX_VA_GPIO + 0x00A0)
+#define S3C64XX_GPG_BASE (S3C64XX_VA_GPIO + 0x00C0)
+#define S3C64XX_GPH_BASE (S3C64XX_VA_GPIO + 0x00E0)
+#define S3C64XX_GPI_BASE (S3C64XX_VA_GPIO + 0x0100)
+#define S3C64XX_GPJ_BASE (S3C64XX_VA_GPIO + 0x0120)
+#define S3C64XX_GPK_BASE (S3C64XX_VA_GPIO + 0x0800)
+#define S3C64XX_GPL_BASE (S3C64XX_VA_GPIO + 0x0810)
+#define S3C64XX_GPM_BASE (S3C64XX_VA_GPIO + 0x0820)
+#define S3C64XX_GPN_BASE (S3C64XX_VA_GPIO + 0x0830)
+#define S3C64XX_GPO_BASE (S3C64XX_VA_GPIO + 0x0140)
+#define S3C64XX_GPP_BASE (S3C64XX_VA_GPIO + 0x0160)
+#define S3C64XX_GPQ_BASE (S3C64XX_VA_GPIO + 0x0180)
+
+#endif /* __ASM_PLAT_S3C64XX_REGS_GPIO_H */
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-sys.h b/arch/arm/plat-s3c64xx/include/plat/regs-sys.h
new file mode 100644
index 000000000000..d8ed82917096
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/regs-sys.h
@@ -0,0 +1,24 @@
+/* arch/arm/plat-s3c64xx/include/plat/regs-sys.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX system register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_REGS_SYS_H
+#define __PLAT_REGS_SYS_H __FILE__
+
+#define S3C_SYSREG(x) (S3C_VA_SYS + (x))
+
+#define S3C64XX_OTHERS S3C_SYSREG(0x900)
+
+#define S3C64XX_OTHERS_USBMASK (1 << 16)
+
+#endif /* _PLAT_REGS_SYS_H */
diff --git a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
new file mode 100644
index 000000000000..571eaa2e54f1
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
@@ -0,0 +1,35 @@
+/* arch/arm/plat-s3c64xx/include/plat/s3c6400.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Header file for s3c6400 cpu support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* Common init code for S3C6400 related SoCs */
+
+extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
+extern void s3c6400_register_clocks(void);
+extern void s3c6400_setup_clocks(void);
+
+#ifdef CONFIG_CPU_S3C6400
+
+extern int s3c6400_init(void);
+extern void s3c6400_map_io(void);
+extern void s3c6400_init_clocks(int xtal);
+
+#define s3c6400_init_uarts s3c6400_common_init_uarts
+
+#else
+#define s3c6400_init_clocks NULL
+#define s3c6400_init_uarts NULL
+#define s3c6400_map_io NULL
+#define s3c6400_init NULL
+#endif
+
diff --git a/arch/arm/plat-s3c64xx/include/plat/s3c6410.h b/arch/arm/plat-s3c64xx/include/plat/s3c6410.h
new file mode 100644
index 000000000000..50dcdd6f6800
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/include/plat/s3c6410.h
@@ -0,0 +1,29 @@
+/* arch/arm/plat-s3c64xx/include/plat/s3c6410.h
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Header file for s3c6410 cpu support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifdef CONFIG_CPU_S3C6410
+
+extern int s3c6410_init(void);
+extern void s3c6410_init_irq(void);
+extern void s3c6410_map_io(void);
+extern void s3c6410_init_clocks(int xtal);
+
+#define s3c6410_init_uarts s3c6400_common_init_uarts
+
+#else
+#define s3c6410_init_clocks NULL
+#define s3c6410_init_uarts NULL
+#define s3c6410_map_io NULL
+#define s3c6410_init NULL
+#endif
diff --git a/arch/arm/plat-s3c64xx/irq-eint.c b/arch/arm/plat-s3c64xx/irq-eint.c
new file mode 100644
index 000000000000..1f7cc0067f5c
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/irq-eint.c
@@ -0,0 +1,202 @@
+/* arch/arm/plat-s3c64xx/irq-eint.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - Interrupt handling for IRQ_EINT(x)
+ *
+ * This program is free software; you can 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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/hardware/vic.h>
+
+#include <plat/regs-irqtype.h>
+
+#include <mach/map.h>
+#include <plat/cpu.h>
+
+/* GPIO is 0x7F008xxx, */
+#define S3C64XX_GPIOREG(x) (S3C64XX_VA_GPIO + (x))
+
+#define S3C64XX_EINT0CON0 S3C64XX_GPIOREG(0x900)
+#define S3C64XX_EINT0CON1 S3C64XX_GPIOREG(0x904)
+#define S3C64XX_EINT0FLTCON0 S3C64XX_GPIOREG(0x910)
+#define S3C64XX_EINT0FLTCON1 S3C64XX_GPIOREG(0x914)
+#define S3C64XX_EINT0FLTCON2 S3C64XX_GPIOREG(0x918)
+#define S3C64XX_EINT0FLTCON3 S3C64XX_GPIOREG(0x91C)
+
+#define S3C64XX_EINT0MASK S3C64XX_GPIOREG(0x920)
+#define S3C64XX_EINT0PEND S3C64XX_GPIOREG(0x924)
+
+
+#define eint_offset(irq) ((irq) - IRQ_EINT(0))
+#define eint_irq_to_bit(irq) (1 << eint_offset(irq))
+
+static inline void s3c_irq_eint_mask(unsigned int irq)
+{
+ u32 mask;
+
+ mask = __raw_readl(S3C64XX_EINT0MASK);
+ mask |= eint_irq_to_bit(irq);
+ __raw_writel(mask, S3C64XX_EINT0MASK);
+}
+
+static void s3c_irq_eint_unmask(unsigned int irq)
+{
+ u32 mask;
+
+ mask = __raw_readl(S3C64XX_EINT0MASK);
+ mask |= eint_irq_to_bit(irq);
+ __raw_writel(mask, S3C64XX_EINT0MASK);
+}
+
+static inline void s3c_irq_eint_ack(unsigned int irq)
+{
+ __raw_writel(eint_irq_to_bit(irq), S3C64XX_EINT0PEND);
+}
+
+static void s3c_irq_eint_maskack(unsigned int irq)
+{
+ /* compiler should in-line these */
+ s3c_irq_eint_mask(irq);
+ s3c_irq_eint_ack(irq);
+}
+
+static int s3c_irq_eint_set_type(unsigned int irq, unsigned int type)
+{
+ int offs = eint_offset(irq);
+ int shift;
+ u32 ctrl, mask;
+ u32 newvalue = 0;
+ void __iomem *reg;
+
+ if (offs > 27)
+ return -EINVAL;
+
+ if (offs <= 15)
+ reg = S3C64XX_EINT0CON0;
+ else
+ reg = S3C64XX_EINT0CON1;
+
+ switch (type) {
+ case IRQ_TYPE_NONE:
+ printk(KERN_WARNING "No edge setting!\n");
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ newvalue = S3C2410_EXTINT_RISEEDGE;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ newvalue = S3C2410_EXTINT_FALLEDGE;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ newvalue = S3C2410_EXTINT_BOTHEDGE;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ newvalue = S3C2410_EXTINT_LOWLEV;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ newvalue = S3C2410_EXTINT_HILEV;
+ break;
+
+ default:
+ printk(KERN_ERR "No such irq type %d", type);
+ return -1;
+ }
+
+ shift = (offs / 2) * 4;
+ mask = 0x7 << shift;
+
+ ctrl = __raw_readl(reg);
+ ctrl &= ~mask;
+ ctrl |= newvalue << shift;
+ __raw_writel(ctrl, reg);
+
+ return 0;
+}
+
+static struct irq_chip s3c_irq_eint = {
+ .name = "s3c-eint",
+ .mask = s3c_irq_eint_mask,
+ .unmask = s3c_irq_eint_unmask,
+ .mask_ack = s3c_irq_eint_maskack,
+ .ack = s3c_irq_eint_ack,
+ .set_type = s3c_irq_eint_set_type,
+};
+
+/* s3c_irq_demux_eint
+ *
+ * This function demuxes the IRQ from the group0 external interrupts,
+ * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into
+ * the specific handlers s3c_irq_demux_eintX_Y.
+ */
+static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end)
+{
+ u32 status = __raw_readl(S3C64XX_EINT0PEND);
+ u32 mask = __raw_readl(S3C64XX_EINT0MASK);
+ unsigned int irq;
+
+ status &= ~mask;
+ status >>= start;
+ status &= (1 << (end - start + 1)) - 1;
+
+ for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) {
+ if (status & 1)
+ generic_handle_irq(irq);
+
+ status >>= 1;
+ }
+}
+
+static void s3c_irq_demux_eint0_3(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_eint(0, 3);
+}
+
+static void s3c_irq_demux_eint4_11(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_eint(4, 11);
+}
+
+static void s3c_irq_demux_eint12_19(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_eint(12, 19);
+}
+
+static void s3c_irq_demux_eint20_27(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_eint(20, 27);
+}
+
+int __init s3c64xx_init_irq_eint(void)
+{
+ int irq;
+
+ for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) {
+ set_irq_chip(irq, &s3c_irq_eint);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ set_irq_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3);
+ set_irq_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11);
+ set_irq_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19);
+ set_irq_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27);
+
+ return 0;
+}
+
+arch_initcall(s3c64xx_init_irq_eint);
diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c
new file mode 100644
index 000000000000..a94f1d5e819d
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/irq.c
@@ -0,0 +1,257 @@
+/* arch/arm/plat-s3c64xx/irq.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C64XX - Interrupt handling
+ *
+ * This program is free software; you can 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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/hardware/vic.h>
+
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <plat/cpu.h>
+
+/* Timer interrupt handling */
+
+static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq)
+{
+ generic_handle_irq(sub_irq);
+}
+
+static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER0);
+}
+
+static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER1);
+}
+
+static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER2);
+}
+
+static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER3);
+}
+
+static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER4);
+}
+
+/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */
+
+static void s3c_irq_timer_mask(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f; /* mask out pending interrupts */
+ reg &= ~(1 << (irq - IRQ_TIMER0));
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static void s3c_irq_timer_unmask(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f; /* mask out pending interrupts */
+ reg |= 1 << (irq - IRQ_TIMER0);
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static void s3c_irq_timer_ack(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f;
+ reg |= (1 << 5) << (irq - IRQ_TIMER0);
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static struct irq_chip s3c_irq_timer = {
+ .name = "s3c-timer",
+ .mask = s3c_irq_timer_mask,
+ .unmask = s3c_irq_timer_unmask,
+ .ack = s3c_irq_timer_ack,
+};
+
+struct uart_irq {
+ void __iomem *regs;
+ unsigned int base_irq;
+ unsigned int parent_irq;
+};
+
+/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3]
+ * are consecutive when looking up the interrupt in the demux routines.
+ */
+static struct uart_irq uart_irqs[] = {
+ [0] = {
+ .regs = S3C_VA_UART0,
+ .base_irq = IRQ_S3CUART_BASE0,
+ .parent_irq = IRQ_UART0,
+ },
+ [1] = {
+ .regs = S3C_VA_UART1,
+ .base_irq = IRQ_S3CUART_BASE1,
+ .parent_irq = IRQ_UART1,
+ },
+ [2] = {
+ .regs = S3C_VA_UART2,
+ .base_irq = IRQ_S3CUART_BASE2,
+ .parent_irq = IRQ_UART2,
+ },
+ [3] = {
+ .regs = S3C_VA_UART3,
+ .base_irq = IRQ_S3CUART_BASE3,
+ .parent_irq = IRQ_UART3,
+ },
+};
+
+static inline void __iomem *s3c_irq_uart_base(unsigned int irq)
+{
+ struct uart_irq *uirq = get_irq_chip_data(irq);
+ return uirq->regs;
+}
+
+static inline unsigned int s3c_irq_uart_bit(unsigned int irq)
+{
+ return irq & 3;
+}
+
+/* UART interrupt registers, not worth adding to seperate include header */
+#define S3C64XX_UINTP 0x30
+#define S3C64XX_UINTSP 0x34
+#define S3C64XX_UINTM 0x38
+
+static void s3c_irq_uart_mask(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg |= (1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+}
+
+static void s3c_irq_uart_maskack(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg |= (1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+ __raw_writel(1 << bit, regs + S3C64XX_UINTP);
+}
+
+static void s3c_irq_uart_unmask(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg &= ~(1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+}
+
+static void s3c_irq_uart_ack(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+
+ __raw_writel(1 << bit, regs + S3C64XX_UINTP);
+}
+
+static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc)
+{
+ struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0];
+ u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP);
+ int base = uirq->base_irq;
+
+ if (pend & (1 << 0))
+ generic_handle_irq(base);
+ if (pend & (1 << 1))
+ generic_handle_irq(base + 1);
+ if (pend & (1 << 2))
+ generic_handle_irq(base + 2);
+ if (pend & (1 << 3))
+ generic_handle_irq(base + 3);
+}
+
+static struct irq_chip s3c_irq_uart = {
+ .name = "s3c-uart",
+ .mask = s3c_irq_uart_mask,
+ .unmask = s3c_irq_uart_unmask,
+ .mask_ack = s3c_irq_uart_maskack,
+ .ack = s3c_irq_uart_ack,
+};
+
+static void __init s3c64xx_uart_irq(struct uart_irq *uirq)
+{
+ void *reg_base = uirq->regs;
+ unsigned int irq;
+ int offs;
+
+ /* mask all interrupts at the start. */
+ __raw_writel(0xf, reg_base + S3C64XX_UINTM);
+
+ for (offs = 0; offs < 3; offs++) {
+ irq = uirq->base_irq + offs;
+
+ set_irq_chip(irq, &s3c_irq_uart);
+ set_irq_chip_data(irq, uirq);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);
+}
+
+void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
+{
+ int uart, irq;
+
+ printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);
+
+ /* initialise the pair of VICs */
+ vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid);
+ vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid);
+
+ /* add the timer sub-irqs */
+
+ set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0);
+ set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1);
+ set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2);
+ set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3);
+ set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4);
+
+ for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
+ set_irq_chip(irq, &s3c_irq_timer);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
+ s3c64xx_uart_irq(&uart_irqs[uart]);
+}
+
+
diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c
new file mode 100644
index 000000000000..8d9a0cada668
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c
@@ -0,0 +1,655 @@
+/* linux/arch/arm/plat-s3c64xx/s3c6400-clock.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 based common clock support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/sysdev.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/cpu-freq.h>
+
+#include <plat/regs-clock.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+#include <plat/pll.h>
+
+/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
+ * ext_xtal_mux for want of an actual name from the manual.
+*/
+
+struct clk clk_ext_xtal_mux = {
+ .name = "ext_xtal",
+ .id = -1,
+};
+
+#define clk_fin_apll clk_ext_xtal_mux
+#define clk_fin_mpll clk_ext_xtal_mux
+#define clk_fin_epll clk_ext_xtal_mux
+
+#define clk_fout_mpll clk_mpll
+
+struct clk_sources {
+ unsigned int nr_sources;
+ struct clk **sources;
+};
+
+struct clksrc_clk {
+ struct clk clk;
+ unsigned int mask;
+ unsigned int shift;
+
+ struct clk_sources *sources;
+
+ unsigned int divider_shift;
+ void __iomem *reg_divider;
+};
+
+struct clk clk_fout_apll = {
+ .name = "fout_apll",
+ .id = -1,
+};
+
+static struct clk *clk_src_apll_list[] = {
+ [0] = &clk_fin_apll,
+ [1] = &clk_fout_apll,
+};
+
+static struct clk_sources clk_src_apll = {
+ .sources = clk_src_apll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_apll_list),
+};
+
+struct clksrc_clk clk_mout_apll = {
+ .clk = {
+ .name = "mout_apll",
+ .id = -1,
+ },
+ .shift = S3C6400_CLKSRC_APLL_MOUT_SHIFT,
+ .mask = S3C6400_CLKSRC_APLL_MOUT,
+ .sources = &clk_src_apll,
+};
+
+struct clk clk_fout_epll = {
+ .name = "fout_epll",
+ .id = -1,
+};
+
+static struct clk *clk_src_epll_list[] = {
+ [0] = &clk_fin_epll,
+ [1] = &clk_fout_epll,
+};
+
+static struct clk_sources clk_src_epll = {
+ .sources = clk_src_epll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_epll_list),
+};
+
+struct clksrc_clk clk_mout_epll = {
+ .clk = {
+ .name = "mout_epll",
+ .id = -1,
+ },
+ .shift = S3C6400_CLKSRC_EPLL_MOUT_SHIFT,
+ .mask = S3C6400_CLKSRC_EPLL_MOUT,
+ .sources = &clk_src_epll,
+};
+
+static struct clk *clk_src_mpll_list[] = {
+ [0] = &clk_fin_mpll,
+ [1] = &clk_fout_mpll,
+};
+
+static struct clk_sources clk_src_mpll = {
+ .sources = clk_src_mpll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
+};
+
+struct clksrc_clk clk_mout_mpll = {
+ .clk = {
+ .name = "mout_mpll",
+ .id = -1,
+ },
+ .shift = S3C6400_CLKSRC_MPLL_MOUT_SHIFT,
+ .mask = S3C6400_CLKSRC_MPLL_MOUT,
+ .sources = &clk_src_mpll,
+};
+
+static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
+{
+ unsigned long rate = clk_get_rate(clk->parent);
+
+ printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate);
+
+ if (__raw_readl(S3C_CLK_DIV0) & S3C6400_CLKDIV0_MPLL_MASK)
+ rate /= 2;
+
+ return rate;
+}
+
+struct clk clk_dout_mpll = {
+ .name = "dout_mpll",
+ .id = -1,
+ .parent = &clk_mout_mpll.clk,
+ .get_rate = s3c64xx_clk_doutmpll_get_rate,
+};
+
+static struct clk *clkset_spi_mmc_list[] = {
+ &clk_mout_epll.clk,
+ &clk_dout_mpll,
+ &clk_fin_epll,
+ &clk_27m,
+};
+
+static struct clk_sources clkset_spi_mmc = {
+ .sources = clkset_spi_mmc_list,
+ .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list),
+};
+
+static struct clk *clkset_irda_list[] = {
+ &clk_mout_epll.clk,
+ &clk_dout_mpll,
+ NULL,
+ &clk_27m,
+};
+
+static struct clk_sources clkset_irda = {
+ .sources = clkset_irda_list,
+ .nr_sources = ARRAY_SIZE(clkset_irda_list),
+};
+
+static struct clk *clkset_uart_list[] = {
+ &clk_mout_epll.clk,
+ &clk_dout_mpll,
+ NULL,
+ NULL
+};
+
+static struct clk_sources clkset_uart = {
+ .sources = clkset_uart_list,
+ .nr_sources = ARRAY_SIZE(clkset_uart_list),
+};
+
+static struct clk *clkset_uhost_list[] = {
+ &clk_mout_epll.clk,
+ &clk_dout_mpll,
+ &clk_fin_epll,
+ &clk_48m,
+};
+
+static struct clk_sources clkset_uhost = {
+ .sources = clkset_uhost_list,
+ .nr_sources = ARRAY_SIZE(clkset_uhost_list),
+};
+
+
+/* The peripheral clocks are all controlled via clocksource followed
+ * by an optional divider and gate stage. We currently roll this into
+ * one clock which hides the intermediate clock from the mux.
+ *
+ * Note, the JPEG clock can only be an even divider...
+ *
+ * The scaler and LCD clocks depend on the S3C64XX version, and also
+ * have a common parent divisor so are not included here.
+ */
+
+static inline struct clksrc_clk *to_clksrc(struct clk *clk)
+{
+ return container_of(clk, struct clksrc_clk, clk);
+}
+
+static unsigned long s3c64xx_getrate_clksrc(struct clk *clk)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ unsigned long rate = clk_get_rate(clk->parent);
+ u32 clkdiv = __raw_readl(sclk->reg_divider);
+
+ clkdiv >>= sclk->divider_shift;
+ clkdiv &= 0xf;
+ clkdiv++;
+
+ rate /= clkdiv;
+ return rate;
+}
+
+static int s3c64xx_setrate_clksrc(struct clk *clk, unsigned long rate)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ void __iomem *reg = sclk->reg_divider;
+ unsigned int div;
+ u32 val;
+
+ rate = clk_round_rate(clk, rate);
+ div = clk_get_rate(clk->parent) / rate;
+
+ val = __raw_readl(reg);
+ val &= ~sclk->mask;
+ val |= (rate - 1) << sclk->shift;
+ __raw_writel(val, reg);
+
+ return 0;
+}
+
+static int s3c64xx_setparent_clksrc(struct clk *clk, struct clk *parent)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ struct clk_sources *srcs = sclk->sources;
+ u32 clksrc = __raw_readl(S3C_CLK_SRC);
+ int src_nr = -1;
+ int ptr;
+
+ for (ptr = 0; ptr < srcs->nr_sources; ptr++)
+ if (srcs->sources[ptr] == parent) {
+ src_nr = ptr;
+ break;
+ }
+
+ if (src_nr >= 0) {
+ clksrc &= ~sclk->mask;
+ clksrc |= src_nr << sclk->shift;
+
+ __raw_writel(clksrc, S3C_CLK_SRC);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static unsigned long s3c64xx_roundrate_clksrc(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long parent_rate = clk_get_rate(clk->parent);
+ int div;
+
+ if (rate > parent_rate)
+ rate = parent_rate;
+ else {
+ div = rate / parent_rate;
+
+ if (div == 0)
+ div = 1;
+ if (div > 16)
+ div = 16;
+
+ rate = parent_rate / div;
+ }
+
+ return rate;
+}
+
+static struct clksrc_clk clk_mmc0 = {
+ .clk = {
+ .name = "mmc_bus",
+ .id = 0,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC0,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_MMC0_SHIFT,
+ .mask = S3C6400_CLKSRC_MMC0_MASK,
+ .sources = &clkset_spi_mmc,
+ .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT,
+ .reg_divider = S3C_CLK_DIV1,
+};
+
+static struct clksrc_clk clk_mmc1 = {
+ .clk = {
+ .name = "mmc_bus",
+ .id = 1,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC1,
+ .enable = s3c64xx_sclk_ctrl,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_MMC1_SHIFT,
+ .mask = S3C6400_CLKSRC_MMC1_MASK,
+ .sources = &clkset_spi_mmc,
+ .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT,
+ .reg_divider = S3C_CLK_DIV1,
+};
+
+static struct clksrc_clk clk_mmc2 = {
+ .clk = {
+ .name = "mmc_bus",
+ .id = 2,
+ .ctrlbit = S3C_CLKCON_SCLK_MMC2,
+ .enable = s3c64xx_sclk_ctrl,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_MMC2_SHIFT,
+ .mask = S3C6400_CLKSRC_MMC2_MASK,
+ .sources = &clkset_spi_mmc,
+ .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT,
+ .reg_divider = S3C_CLK_DIV1,
+};
+
+static struct clksrc_clk clk_usbhost = {
+ .clk = {
+ .name = "usb-host-bus",
+ .id = -1,
+ .ctrlbit = S3C_CLKCON_SCLK_UHOST,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_UHOST_SHIFT,
+ .mask = S3C6400_CLKSRC_UHOST_MASK,
+ .sources = &clkset_uhost,
+ .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT,
+ .reg_divider = S3C_CLK_DIV1,
+};
+
+static struct clksrc_clk clk_uart_uclk1 = {
+ .clk = {
+ .name = "uclk1",
+ .id = -1,
+ .ctrlbit = S3C_CLKCON_SCLK_UART,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_UART_SHIFT,
+ .mask = S3C6400_CLKSRC_UART_MASK,
+ .sources = &clkset_uart,
+ .divider_shift = S3C6400_CLKDIV2_UART_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+/* Where does UCLK0 come from? */
+
+static struct clksrc_clk clk_spi0 = {
+ .clk = {
+ .name = "spi-bus",
+ .id = 0,
+ .ctrlbit = S3C_CLKCON_SCLK_SPI0,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_SPI0_SHIFT,
+ .mask = S3C6400_CLKSRC_SPI0_MASK,
+ .sources = &clkset_spi_mmc,
+ .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+static struct clksrc_clk clk_spi1 = {
+ .clk = {
+ .name = "spi-bus",
+ .id = 1,
+ .ctrlbit = S3C_CLKCON_SCLK_SPI1,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_SPI1_SHIFT,
+ .mask = S3C6400_CLKSRC_SPI1_MASK,
+ .sources = &clkset_spi_mmc,
+ .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+static struct clk clk_iis_cd0 = {
+ .name = "iis_cdclk0",
+ .id = -1,
+};
+
+static struct clk clk_iis_cd1 = {
+ .name = "iis_cdclk1",
+ .id = -1,
+};
+
+static struct clk clk_pcm_cd = {
+ .name = "pcm_cdclk",
+ .id = -1,
+};
+
+static struct clk *clkset_audio0_list[] = {
+ [0] = &clk_mout_epll.clk,
+ [1] = &clk_dout_mpll,
+ [2] = &clk_fin_epll,
+ [3] = &clk_iis_cd0,
+ [4] = &clk_pcm_cd,
+};
+
+static struct clk_sources clkset_audio0 = {
+ .sources = clkset_audio0_list,
+ .nr_sources = ARRAY_SIZE(clkset_audio0_list),
+};
+
+static struct clksrc_clk clk_audio0 = {
+ .clk = {
+ .name = "audio-bus",
+ .id = 0,
+ .ctrlbit = S3C_CLKCON_SCLK_AUDIO0,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_AUDIO0_SHIFT,
+ .mask = S3C6400_CLKSRC_AUDIO0_MASK,
+ .sources = &clkset_audio0,
+ .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+static struct clk *clkset_audio1_list[] = {
+ [0] = &clk_mout_epll.clk,
+ [1] = &clk_dout_mpll,
+ [2] = &clk_fin_epll,
+ [3] = &clk_iis_cd1,
+ [4] = &clk_pcm_cd,
+};
+
+static struct clk_sources clkset_audio1 = {
+ .sources = clkset_audio1_list,
+ .nr_sources = ARRAY_SIZE(clkset_audio1_list),
+};
+
+static struct clksrc_clk clk_audio1 = {
+ .clk = {
+ .name = "audio-bus",
+ .id = 1,
+ .ctrlbit = S3C_CLKCON_SCLK_AUDIO1,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_AUDIO1_SHIFT,
+ .mask = S3C6400_CLKSRC_AUDIO1_MASK,
+ .sources = &clkset_audio1,
+ .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+static struct clksrc_clk clk_irda = {
+ .clk = {
+ .name = "irda-bus",
+ .id = 0,
+ .ctrlbit = S3C_CLKCON_SCLK_IRDA,
+ .enable = s3c64xx_sclk_ctrl,
+ .set_parent = s3c64xx_setparent_clksrc,
+ .get_rate = s3c64xx_getrate_clksrc,
+ .set_rate = s3c64xx_setrate_clksrc,
+ .round_rate = s3c64xx_roundrate_clksrc,
+ },
+ .shift = S3C6400_CLKSRC_IRDA_SHIFT,
+ .mask = S3C6400_CLKSRC_IRDA_MASK,
+ .sources = &clkset_irda,
+ .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT,
+ .reg_divider = S3C_CLK_DIV2,
+};
+
+/* Clock initialisation code */
+
+static struct clksrc_clk *init_parents[] = {
+ &clk_mout_apll,
+ &clk_mout_epll,
+ &clk_mout_mpll,
+ &clk_mmc0,
+ &clk_mmc1,
+ &clk_mmc2,
+ &clk_usbhost,
+ &clk_uart_uclk1,
+ &clk_spi0,
+ &clk_spi1,
+ &clk_audio0,
+ &clk_audio1,
+ &clk_irda,
+};
+
+static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk)
+{
+ struct clk_sources *srcs = clk->sources;
+ u32 clksrc = __raw_readl(S3C_CLK_SRC);
+
+ clksrc &= clk->mask;
+ clksrc >>= clk->shift;
+
+ if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) {
+ printk(KERN_ERR "%s: bad source %d\n",
+ clk->clk.name, clksrc);
+ return;
+ }
+
+ clk->clk.parent = srcs->sources[clksrc];
+
+ printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n",
+ clk->clk.name, clk->clk.parent->name, clksrc,
+ clk_get_rate(&clk->clk));
+}
+
+#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
+
+void __init_or_cpufreq s3c6400_setup_clocks(void)
+{
+ struct clk *xtal_clk;
+ unsigned long xtal;
+ unsigned long fclk;
+ unsigned long hclk;
+ unsigned long hclk2;
+ unsigned long pclk;
+ unsigned long epll;
+ unsigned long apll;
+ unsigned long mpll;
+ unsigned int ptr;
+ u32 clkdiv0;
+
+ printk(KERN_DEBUG "%s: registering clocks\n", __func__);
+
+ clkdiv0 = __raw_readl(S3C_CLK_DIV0);
+ printk(KERN_DEBUG "%s: clkdiv0 = %08x\n", __func__, clkdiv0);
+
+ xtal_clk = clk_get(NULL, "xtal");
+ BUG_ON(IS_ERR(xtal_clk));
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
+
+ epll = s3c6400_get_epll(xtal);
+ mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON));
+ apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON));
+
+ fclk = mpll;
+
+ printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n",
+ apll, mpll, epll);
+
+ hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
+ hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK);
+ pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK);
+
+ printk(KERN_INFO "S3C64XX: HCLK2=%ld, HCLK=%ld, PCLK=%ld\n",
+ hclk2, hclk, pclk);
+
+ clk_fout_mpll.rate = mpll;
+ clk_fout_epll.rate = epll;
+ clk_fout_apll.rate = apll;
+
+ clk_h.rate = hclk;
+ clk_p.rate = pclk;
+ clk_f.rate = fclk;
+
+ for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
+ s3c6400_set_clksrc(init_parents[ptr]);
+}
+
+static struct clk *clks[] __initdata = {
+ &clk_ext_xtal_mux,
+ &clk_iis_cd0,
+ &clk_iis_cd1,
+ &clk_pcm_cd,
+ &clk_mout_epll.clk,
+ &clk_fout_epll,
+ &clk_mout_mpll.clk,
+ &clk_dout_mpll,
+ &clk_mmc0.clk,
+ &clk_mmc1.clk,
+ &clk_mmc2.clk,
+ &clk_usbhost.clk,
+ &clk_uart_uclk1.clk,
+ &clk_spi0.clk,
+ &clk_spi1.clk,
+ &clk_audio0.clk,
+ &clk_audio1.clk,
+ &clk_irda.clk,
+};
+
+void __init s3c6400_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
+ clkp = clks[ptr];
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ }
+
+ clk_mpll.parent = &clk_mout_mpll.clk;
+ clk_epll.parent = &clk_mout_epll.clk;
+}
diff --git a/arch/arm/plat-s3c64xx/s3c6400-init.c b/arch/arm/plat-s3c64xx/s3c6400-init.c
new file mode 100644
index 000000000000..6c28f39df097
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/s3c6400-init.c
@@ -0,0 +1,29 @@
+/* linux/arch/arm/plat-s3c64xx/s3c6400-init.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C6400 - CPU initialisation (common with other S3C64XX chips)
+ *
+ * This program is free software; you can 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 <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/s3c6400.h>
+#include <plat/s3c6410.h>
+
+/* uart registration process */
+
+void __init s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+ s3c24xx_init_uartdevs("s3c6400-uart", s3c64xx_uart_resources, cfg, no);
+}
diff --git a/arch/arm/plat-s3c64xx/setup-fb-24bpp.c b/arch/arm/plat-s3c64xx/setup-fb-24bpp.c
new file mode 100644
index 000000000000..8e28e448dd20
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/setup-fb-24bpp.c
@@ -0,0 +1,37 @@
+/* linux/arch/arm/plat-s3c64xx/setup-fb-24bpp.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Base S3C64XX setup information for 24bpp LCD framebuffer
+ *
+ * This program is free software; you can 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/fb.h>
+
+#include <mach/regs-fb.h>
+#include <mach/gpio.h>
+#include <plat/fb.h>
+#include <plat/gpio-cfg.h>
+
+extern void s3c64xx_fb_gpio_setup_24bpp(void)
+{
+ unsigned int gpio;
+
+ for (gpio = S3C64XX_GPI(0); gpio <= S3C64XX_GPI(15); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ for (gpio = S3C64XX_GPJ(0); gpio <= S3C64XX_GPJ(11); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+}
diff --git a/arch/arm/plat-s3c64xx/setup-i2c0.c b/arch/arm/plat-s3c64xx/setup-i2c0.c
new file mode 100644
index 000000000000..364480763728
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/setup-i2c0.c
@@ -0,0 +1,31 @@
+/* linux/arch/arm/plat-s3c64xx/setup-i2c0.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Base S3C64XX I2C bus 0 gpio configuration
+ *
+ * This program is free software; you can 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>
+
+struct platform_device; /* don't need the contents */
+
+#include <mach/gpio.h>
+#include <plat/iic.h>
+#include <plat/gpio-bank-b.h>
+#include <plat/gpio-cfg.h>
+
+void s3c_i2c0_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S3C64XX_GPB(5), S3C64XX_GPB5_I2C_SCL0);
+ s3c_gpio_cfgpin(S3C64XX_GPB(6), S3C64XX_GPB6_I2C_SDA0);
+ s3c_gpio_setpull(S3C64XX_GPB(5), S3C_GPIO_PULL_UP);
+ s3c_gpio_setpull(S3C64XX_GPB(6), S3C_GPIO_PULL_UP);
+}
diff --git a/arch/arm/plat-s3c64xx/setup-i2c1.c b/arch/arm/plat-s3c64xx/setup-i2c1.c
new file mode 100644
index 000000000000..bbe229bd90ca
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/setup-i2c1.c
@@ -0,0 +1,31 @@
+/* linux/arch/arm/plat-s3c64xx/setup-i2c1.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Base S3C64XX I2C bus 1 gpio configuration
+ *
+ * This program is free software; you can 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>
+
+struct platform_device; /* don't need the contents */
+
+#include <mach/gpio.h>
+#include <plat/iic.h>
+#include <plat/gpio-bank-b.h>
+#include <plat/gpio-cfg.h>
+
+void s3c_i2c1_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S3C64XX_GPB(2), S3C64XX_GPB2_I2C_SCL1);
+ s3c_gpio_cfgpin(S3C64XX_GPB(3), S3C64XX_GPB3_I2C_SDA1);
+ s3c_gpio_setpull(S3C64XX_GPB(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_setpull(S3C64XX_GPB(3), S3C_GPIO_PULL_UP);
+}
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 43aa2020f85c..fd23c0e9e698 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -12,7 +12,7 @@
#
# http://www.arm.linux.org.uk/developer/machines/?action=new
#
-# Last update: Thu Sep 25 10:10:50 2008
+# Last update: Sun Nov 30 16:39:36 2008
#
# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
#
@@ -1380,7 +1380,7 @@ holon MACH_HOLON HOLON 1377
olip8 MACH_OLIP8 OLIP8 1378
ghi270hg MACH_GHI270HG GHI270HG 1379
davinci_dm6467_evm MACH_DAVINCI_DM6467_EVM DAVINCI_DM6467_EVM 1380
-davinci_dm355_evm MACH_DAVINCI_DM350_EVM DAVINCI_DM350_EVM 1381
+davinci_dm355_evm MACH_DAVINCI_DM355_EVM DAVINCI_DM355_EVM 1381
blackriver MACH_BLACKRIVER BLACKRIVER 1383
sandgate_wp MACH_SANDGATEWP SANDGATEWP 1384
cdotbwsg MACH_CDOTBWSG CDOTBWSG 1385
@@ -1771,7 +1771,7 @@ axs_ultrax MACH_AXS_ULTRAX AXS_ULTRAX 1779
at572d940deb MACH_AT572D940DEB AT572D940DEB 1780
davinci_da8xx_evm MACH_DAVINCI_DA8XX_EVM DAVINCI_DA8XX_EVM 1781
ep9302 MACH_EP9302 EP9302 1782
-at572d940hfeb MACH_AT572D940HFEB AT572D940HFEB 1783
+at572d940hfek MACH_AT572D940HFEB AT572D940HFEB 1783
cybook3 MACH_CYBOOK3 CYBOOK3 1784
wdg002 MACH_WDG002 WDG002 1785
sg560adsl MACH_SG560ADSL SG560ADSL 1786
@@ -1899,3 +1899,98 @@ rut100 MACH_RUT100 RUT100 1908
asusp535 MACH_ASUSP535 ASUSP535 1909
htcraphael MACH_HTCRAPHAEL HTCRAPHAEL 1910
sygdg1 MACH_SYGDG1 SYGDG1 1911
+sygdg2 MACH_SYGDG2 SYGDG2 1912
+seoul MACH_SEOUL SEOUL 1913
+salerno MACH_SALERNO SALERNO 1914
+ucn_s3c64xx MACH_UCN_S3C64XX UCN_S3C64XX 1915
+msm7201a MACH_MSM7201A MSM7201A 1916
+lpr1 MACH_LPR1 LPR1 1917
+armadillo500fx MACH_ARMADILLO500FX ARMADILLO500FX 1918
+g3evm MACH_G3EVM G3EVM 1919
+z3_dm355 MACH_Z3_DM355 Z3_DM355 1920
+w90p910evb MACH_W90P910EVB W90P910EVB 1921
+w90p920evb MACH_W90P920EVB W90P920EVB 1922
+w90p950evb MACH_W90P950EVB W90P950EVB 1923
+w90n960evb MACH_W90N960EVB W90N960EVB 1924
+camhd MACH_CAMHD CAMHD 1925
+mvc100 MACH_MVC100 MVC100 1926
+electrum_200 MACH_ELECTRUM_200 ELECTRUM_200 1927
+htcjade MACH_HTCJADE HTCJADE 1928
+memphis MACH_MEMPHIS MEMPHIS 1929
+imx27sbc MACH_IMX27SBC IMX27SBC 1930
+lextar MACH_LEXTAR LEXTAR 1931
+mv88f6281gtw_ge MACH_MV88F6281GTW_GE MV88F6281GTW_GE 1932
+ncp MACH_NCP NCP 1933
+z32an_series MACH_Z32AN Z32AN 1934
+tmq_capd MACH_TMQ_CAPD TMQ_CAPD 1935
+omap3_wl MACH_OMAP3_WL OMAP3_WL 1936
+chumby MACH_CHUMBY CHUMBY 1937
+atsarm9 MACH_ATSARM9 ATSARM9 1938
+davinci_dm365_evm MACH_DAVINCI_DM365_EVM DAVINCI_DM365_EVM 1939
+bahamas MACH_BAHAMAS BAHAMAS 1940
+das MACH_DAS DAS 1941
+minidas MACH_MINIDAS MINIDAS 1942
+vk1000 MACH_VK1000 VK1000 1943
+centro MACH_CENTRO CENTRO 1944
+ctera_2bay MACH_CTERA_2BAY CTERA_2BAY 1945
+edgeconnect MACH_EDGECONNECT EDGECONNECT 1946
+nd27000 MACH_ND27000 ND27000 1947
+cobra MACH_GEMALTO_COBRA GEMALTO_COBRA 1948
+ingelabs_comet MACH_INGELABS_COMET INGELABS_COMET 1949
+pollux_wiz MACH_POLLUX_WIZ POLLUX_WIZ 1950
+blackstone MACH_BLACKSTONE BLACKSTONE 1951
+topaz MACH_TOPAZ TOPAZ 1952
+aixle MACH_AIXLE AIXLE 1953
+mw998 MACH_MW998 MW998 1954
+nokia_rx51 MACH_NOKIA_RX51 NOKIA_RX51 1955
+vsc5605ev MACH_VSC5605EV VSC5605EV 1956
+nt98700dk MACH_NT98700DK NT98700DK 1957
+icontact MACH_ICONTACT ICONTACT 1958
+swarco_frcpu MACH_SWARCO_FRCPU SWARCO_FRCPU 1959
+swarco_scpu MACH_SWARCO_SCPU SWARCO_SCPU 1960
+bbox_p16 MACH_BBOX_P16 BBOX_P16 1961
+bstd MACH_BSTD BSTD 1962
+sbc2440ii MACH_SBC2440II SBC2440II 1963
+pcm034 MACH_PCM034 PCM034 1964
+neso MACH_NESO NESO 1965
+wlnx_9g20 MACH_WLNX_9G20 WLNX_9G20 1966
+omap_zoom2 MACH_OMAP_ZOOM2 OMAP_ZOOM2 1967
+totemnova MACH_TOTEMNOVA TOTEMNOVA 1968
+c5000 MACH_C5000 C5000 1969
+unipo_at91sam9263 MACH_UNIPO_AT91SAM9263 UNIPO_AT91SAM9263 1970
+ethernut5 MACH_ETHERNUT5 ETHERNUT5 1971
+arm11 MACH_ARM11 ARM11 1972
+cpuat9260 MACH_CPUAT9260 CPUAT9260 1973
+cpupxa255 MACH_CPUPXA255 CPUPXA255 1974
+cpuimx27 MACH_CPUIMX27 CPUIMX27 1975
+cheflux MACH_CHEFLUX CHEFLUX 1976
+eb_cpux9k2 MACH_EB_CPUX9K2 EB_CPUX9K2 1977
+opcotec MACH_OPCOTEC OPCOTEC 1978
+yt MACH_YT YT 1979
+motoq MACH_MOTOQ MOTOQ 1980
+bsb1 MACH_BSB1 BSB1 1981
+acs5k MACH_ACS5K ACS5K 1982
+milan MACH_MILAN MILAN 1983
+quartzv2 MACH_QUARTZV2 QUARTZV2 1984
+rsvp MACH_RSVP RSVP 1985
+rmp200 MACH_RMP200 RMP200 1986
+snapper_9260 MACH_SNAPPER_9260 SNAPPER_9260 1987
+dsm320 MACH_DSM320 DSM320 1988
+adsgcm MACH_ADSGCM ADSGCM 1989
+ase2_400 MACH_ASE2_400 ASE2_400 1990
+pizza MACH_PIZZA PIZZA 1991
+spot_ngpl MACH_SPOT_NGPL SPOT_NGPL 1992
+armata MACH_ARMATA ARMATA 1993
+exeda MACH_EXEDA EXEDA 1994
+mx31sf005 MACH_MX31SF005 MX31SF005 1995
+f5d8231_4_v2 MACH_F5D8231_4_V2 F5D8231_4_V2 1996
+q2440 MACH_Q2440 Q2440 1997
+qq2440 MACH_QQ2440 QQ2440 1998
+mini2440 MACH_MINI2440 MINI2440 1999
+colibri300 MACH_COLIBRI300 COLIBRI300 2000
+jades MACH_JADES JADES 2001
+spark MACH_SPARK SPARK 2002
+benzina MACH_BENZINA BENZINA 2003
+blaze MACH_BLAZE BLAZE 2004
+linkstation_ls_hgl MACH_LINKSTATION_LS_HGL LINKSTATION_LS_HGL 2005
+htcvenus MACH_HTCVENUS HTCVENUS 2006
diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h
index c85860bad585..8de86e4feada 100644
--- a/arch/arm/vfp/vfp.h
+++ b/arch/arm/vfp/vfp.h
@@ -377,6 +377,6 @@ struct op {
u32 flags;
};
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) || defined(CONFIG_PM)
extern void vfp_save_state(void *location, u32 fpexc);
#endif
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index a62dcf7098ba..c92a08bd6a86 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -101,9 +101,12 @@ ENTRY(vfp_support_entry)
VFPFSTMIA r4, r5 @ save the working registers
VFPFMRX r5, FPSCR @ current status
tst r1, #FPEXC_EX @ is there additional state to save?
- VFPFMRX r6, FPINST, NE @ FPINST (only if FPEXC.EX is set)
- tstne r1, #FPEXC_FP2V @ is there an FPINST2 to read?
- VFPFMRX r8, FPINST2, NE @ FPINST2 if needed (and present)
+ beq 1f
+ VFPFMRX r6, FPINST @ FPINST (only if FPEXC.EX is set)
+ tst r1, #FPEXC_FP2V @ is there an FPINST2 to read?
+ beq 1f
+ VFPFMRX r8, FPINST2 @ FPINST2 if needed (and present)
+1:
stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
@ and point r4 at the word at the
@ start of the register dump
@@ -117,9 +120,12 @@ no_old_VFP_process:
@ FPEXC is in a safe state
ldmia r10, {r1, r5, r6, r8} @ load FPEXC, FPSCR, FPINST, FPINST2
tst r1, #FPEXC_EX @ is there additional state to restore?
- VFPFMXR FPINST, r6, NE @ restore FPINST (only if FPEXC.EX is set)
- tstne r1, #FPEXC_FP2V @ is there an FPINST2 to write?
- VFPFMXR FPINST2, r8, NE @ FPINST2 if needed (and present)
+ beq 1f
+ VFPFMXR FPINST, r6 @ restore FPINST (only if FPEXC.EX is set)
+ tst r1, #FPEXC_FP2V @ is there an FPINST2 to write?
+ beq 1f
+ VFPFMXR FPINST2, r8 @ FPINST2 if needed (and present)
+1:
VFPFMXR FPSCR, r5 @ restore status
check_for_exception:
@@ -166,7 +172,7 @@ process_exception:
@ retry the faulted instruction
ENDPROC(vfp_support_entry)
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) || defined(CONFIG_PM)
ENTRY(vfp_save_state)
@ Save the current VFP state
@ r0 - save location
@@ -175,9 +181,12 @@ ENTRY(vfp_save_state)
VFPFSTMIA r0, r2 @ save the working registers
VFPFMRX r2, FPSCR @ current status
tst r1, #FPEXC_EX @ is there additional state to save?
- VFPFMRX r3, FPINST, NE @ FPINST (only if FPEXC.EX is set)
- tstne r1, #FPEXC_FP2V @ is there an FPINST2 to read?
- VFPFMRX r12, FPINST2, NE @ FPINST2 if needed (and present)
+ beq 1f
+ VFPFMRX r3, FPINST @ FPINST (only if FPEXC.EX is set)
+ tst r1, #FPEXC_FP2V @ is there an FPINST2 to read?
+ beq 1f
+ VFPFMRX r12, FPINST2 @ FPINST2 if needed (and present)
+1:
stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2
mov pc, lr
ENDPROC(vfp_save_state)
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index c0d2c9bb952b..9f476a1be2ca 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -322,6 +322,61 @@ static void vfp_enable(void *unused)
set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11));
}
+#ifdef CONFIG_PM
+#include <linux/sysdev.h>
+
+static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state)
+{
+ struct thread_info *ti = current_thread_info();
+ u32 fpexc = fmrx(FPEXC);
+
+ /* if vfp is on, then save state for resumption */
+ if (fpexc & FPEXC_EN) {
+ printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
+ vfp_save_state(&ti->vfpstate, fpexc);
+
+ /* disable, just in case */
+ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+ }
+
+ /* clear any information we had about last context state */
+ memset(last_VFP_context, 0, sizeof(last_VFP_context));
+
+ return 0;
+}
+
+static int vfp_pm_resume(struct sys_device *dev)
+{
+ /* ensure we have access to the vfp */
+ vfp_enable(NULL);
+
+ /* and disable it to ensure the next usage restores the state */
+ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+
+ return 0;
+}
+
+static struct sysdev_class vfp_pm_sysclass = {
+ .name = "vfp",
+ .suspend = vfp_pm_suspend,
+ .resume = vfp_pm_resume,
+};
+
+static struct sys_device vfp_pm_sysdev = {
+ .cls = &vfp_pm_sysclass,
+};
+
+static void vfp_pm_init(void)
+{
+ sysdev_class_register(&vfp_pm_sysclass);
+ sysdev_register(&vfp_pm_sysdev);
+}
+
+
+#else
+static inline void vfp_pm_init(void) { }
+#endif /* CONFIG_PM */
+
#include <linux/smp.h>
/*
@@ -365,12 +420,22 @@ static int __init vfp_init(void)
vfp_vector = vfp_support_entry;
thread_register_notifier(&vfp_notifier_block);
+ vfp_pm_init();
/*
* We detected VFP, and the support code is
* in place; report VFP support to userspace.
*/
elf_hwcap |= HWCAP_VFP;
+#ifdef CONFIG_NEON
+ /*
+ * Check for the presence of the Advanced SIMD
+ * load/store instructions, integer and single
+ * precision floating point operations.
+ */
+ if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100)
+ elf_hwcap |= HWCAP_NEON;
+#endif
}
return 0;
}
diff --git a/arch/avr32/boards/favr-32/flash.c b/arch/avr32/boards/favr-32/flash.c
index 5f139b7cb5f7..604bbd5e41d9 100644
--- a/arch/avr32/boards/favr-32/flash.c
+++ b/arch/avr32/boards/favr-32/flash.c
@@ -13,7 +13,7 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
-#include <asm/arch/smc.h>
+#include <mach/smc.h>
static struct smc_timing flash_timing __initdata = {
.ncs_read_setup = 0,
diff --git a/arch/avr32/boards/favr-32/setup.c b/arch/avr32/boards/favr-32/setup.c
index 7538f3d2b9e0..1ee4faf0742d 100644
--- a/arch/avr32/boards/favr-32/setup.c
+++ b/arch/avr32/boards/favr-32/setup.c
@@ -25,10 +25,10 @@
#include <asm/setup.h>
-#include <asm/arch/at32ap700x.h>
-#include <asm/arch/init.h>
-#include <asm/arch/board.h>
-#include <asm/arch/portmux.h>
+#include <mach/at32ap700x.h>
+#include <mach/init.h>
+#include <mach/board.h>
+#include <mach/portmux.h>
/* Oscillator frequencies. These are board-specific */
unsigned long at32_board_osc_rates[3] = {
diff --git a/arch/avr32/boot/images/Makefile b/arch/avr32/boot/images/Makefile
index 219720a47bf9..1848bf0d7f62 100644
--- a/arch/avr32/boot/images/Makefile
+++ b/arch/avr32/boot/images/Makefile
@@ -10,7 +10,7 @@ MKIMAGE := $(srctree)/scripts/mkuboot.sh
extra-y := vmlinux.bin vmlinux.gz
-OBJCOPYFLAGS_vmlinux.bin := -O binary
+OBJCOPYFLAGS_vmlinux.bin := -O binary -R .note.gnu.build-id
$(obj)/vmlinux.bin: vmlinux FORCE
$(call if_changed,objcopy)
diff --git a/arch/avr32/configs/atstk1006_defconfig b/arch/avr32/configs/atstk1006_defconfig
index 8b6e54c9946a..6c45a3b77aa3 100644
--- a/arch/avr32/configs/atstk1006_defconfig
+++ b/arch/avr32/configs/atstk1006_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27-rc1
-# Tue Aug 5 15:40:26 2008
+# Linux kernel version: 2.6.28-rc8
+# Thu Dec 18 11:22:23 2008
#
CONFIG_AVR32=y
CONFIG_GENERIC_GPIO=y
@@ -67,6 +67,7 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_SLUB_DEBUG=y
# CONFIG_SLAB is not set
@@ -77,15 +78,8 @@ CONFIG_PROFILING=y
CONFIG_OPROFILE=m
CONFIG_HAVE_OPROFILE=y
CONFIG_KPROBES=y
-# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
-# CONFIG_HAVE_IOREMAP_PROT is not set
CONFIG_HAVE_KPROBES=y
-# CONFIG_HAVE_KRETPROBES is not set
-# CONFIG_HAVE_ARCH_TRACEHOOK is not set
-# CONFIG_HAVE_DMA_ATTRS is not set
-# CONFIG_USE_GENERIC_SMP_HELPERS is not set
CONFIG_HAVE_CLK=y
-CONFIG_PROC_PAGE_MONITOR=y
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
@@ -118,6 +112,7 @@ CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"
CONFIG_CLASSIC_RCU=y
+CONFIG_FREEZER=y
#
# System Type and features
@@ -134,6 +129,8 @@ CONFIG_CPU_AT32AP700X=y
CONFIG_CPU_AT32AP7000=y
CONFIG_BOARD_ATSTK1000=y
# CONFIG_BOARD_ATNGW100 is not set
+# CONFIG_BOARD_FAVR_32 is not set
+# CONFIG_BOARD_MIMC200 is not set
# CONFIG_BOARD_ATSTK1002 is not set
# CONFIG_BOARD_ATSTK1003 is not set
# CONFIG_BOARD_ATSTK1004 is not set
@@ -171,14 +168,14 @@ CONFIG_FLATMEM_MANUAL=y
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
-# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_NR_QUICK=2
CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
# CONFIG_OWNERSHIP_TRACE is not set
CONFIG_NMI_DEBUGGING=y
# CONFIG_HZ_100 is not set
@@ -186,7 +183,7 @@ CONFIG_HZ_250=y
# CONFIG_HZ_300 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250
-# CONFIG_SCHED_HRTICK is not set
+CONFIG_SCHED_HRTICK=y
CONFIG_CMDLINE=""
#
@@ -228,6 +225,8 @@ CONFIG_CPU_FREQ_AT32AP=y
# Executable file formats
#
CONFIG_BINFMT_ELF=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+# CONFIG_HAVE_AOUT is not set
# CONFIG_BINFMT_MISC is not set
CONFIG_NET=y
@@ -299,6 +298,7 @@ CONFIG_IPV6_TUNNEL=m
# CONFIG_ATM is not set
CONFIG_STP=m
CONFIG_BRIDGE=m
+# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
CONFIG_LLC=m
@@ -321,14 +321,8 @@ CONFIG_LLC=m
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
-
-#
-# Wireless
-#
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
-# CONFIG_IEEE80211 is not set
+# CONFIG_PHONET is not set
+# CONFIG_WIRELESS is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -359,6 +353,7 @@ CONFIG_MTD_CMDLINE_PARTS=y
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
+CONFIG_HAVE_MTD_OTP=y
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
@@ -407,6 +402,8 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=2
# Self-contained MTD device drivers
#
CONFIG_MTD_DATAFLASH=m
+# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set
+CONFIG_MTD_DATAFLASH_OTP=y
CONFIG_MTD_M25P80=m
CONFIG_M25PXX_USE_FAST_READ=y
# CONFIG_MTD_SLRAM is not set
@@ -464,9 +461,10 @@ CONFIG_ATMEL_TCLIB=y
CONFIG_ATMEL_TCB_CLKSRC=y
CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
CONFIG_ATMEL_SSC=m
# CONFIG_ENCLOSURE_SERVICES is not set
-# CONFIG_HAVE_IDE is not set
+# CONFIG_C2PORT is not set
#
# SCSI device support
@@ -548,6 +546,9 @@ CONFIG_MACB=y
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
@@ -653,6 +654,7 @@ CONFIG_UNIX98_PTYS=y
CONFIG_I2C=m
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_ALGOBIT=m
#
@@ -717,6 +719,10 @@ CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
#
+# Memory mapped GPIO expanders:
+#
+
+#
# I2C GPIO expanders:
#
# CONFIG_GPIO_MAX732X is not set
@@ -745,11 +751,11 @@ CONFIG_WATCHDOG=y
#
# CONFIG_SOFT_WATCHDOG is not set
CONFIG_AT32AP700X_WDT=y
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
@@ -758,6 +764,10 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_MFD_CORE is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_REGULATOR is not set
#
# Multimedia devices
@@ -783,6 +793,7 @@ CONFIG_SSB_POSSIBLE=y
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
@@ -804,10 +815,13 @@ CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_ATMEL=y
# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_LTV350QV=y
# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
# CONFIG_LCD_VGG2432A4 is not set
# CONFIG_LCD_PLATFORM is not set
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
@@ -818,6 +832,7 @@ CONFIG_LCD_LTV350QV=y
# CONFIG_DISPLAY_SUPPORT is not set
# CONFIG_LOGO is not set
CONFIG_SOUND=m
+CONFIG_SOUND_OSS_CORE=y
CONFIG_SND=m
CONFIG_SND_TIMER=m
CONFIG_SND_PCM=m
@@ -848,28 +863,32 @@ CONFIG_USB_SUPPORT=y
# CONFIG_USB_ARCH_HAS_EHCI is not set
# CONFIG_USB_OTG_WHITELIST is not set
# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
#
CONFIG_USB_GADGET=y
# CONFIG_USB_GADGET_DEBUG is not set
# CONFIG_USB_GADGET_DEBUG_FILES is not set
# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
CONFIG_USB_GADGET_SELECTED=y
-# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_AT91 is not set
CONFIG_USB_GADGET_ATMEL_USBA=y
CONFIG_USB_ATMEL_USBA=y
# CONFIG_USB_GADGET_FSL_USB2 is not set
-# CONFIG_USB_GADGET_NET2280 is not set
-# CONFIG_USB_GADGET_PXA25X is not set
-# CONFIG_USB_GADGET_M66592 is not set
-# CONFIG_USB_GADGET_PXA27X is not set
-# CONFIG_USB_GADGET_GOKU is not set
# CONFIG_USB_GADGET_LH7A40X is not set
# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
# CONFIG_USB_GADGET_S3C2410 is not set
-# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
# CONFIG_USB_GADGET_DUMMY_HCD is not set
CONFIG_USB_GADGET_DUALSPEED=y
CONFIG_USB_ZERO=m
@@ -887,7 +906,7 @@ CONFIG_MMC=y
# CONFIG_MMC_UNSAFE_RESUME is not set
#
-# MMC/SD Card Drivers
+# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_BOUNCE=y
@@ -895,10 +914,11 @@ CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_MMC_TEST is not set
#
-# MMC/SD Host Controller Drivers
+# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_SDHCI is not set
CONFIG_MMC_ATMELMCI=y
+# CONFIG_MMC_ATMELMCI_DMA is not set
CONFIG_MMC_SPI=m
# CONFIG_MEMSTICK is not set
CONFIG_NEW_LEDS=y
@@ -918,6 +938,7 @@ CONFIG_LEDS_GPIO=m
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_TIMER=m
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
# CONFIG_ACCESSIBILITY is not set
CONFIG_RTC_LIB=y
@@ -950,25 +971,31 @@ CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
#
# SPI RTC drivers
#
# CONFIG_RTC_DRV_M41T94 is not set
# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
# CONFIG_RTC_DRV_MAX6902 is not set
# CONFIG_RTC_DRV_R9701 is not set
# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
#
# Platform RTC drivers
#
+# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_V3020 is not set
#
@@ -989,6 +1016,8 @@ CONFIG_DMA_ENGINE=y
# CONFIG_NET_DMA is not set
CONFIG_DMATEST=m
# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
#
# File systems
@@ -998,12 +1027,17 @@ CONFIG_EXT2_FS=m
# CONFIG_EXT2_FS_XIP is not set
CONFIG_EXT3_FS=m
# CONFIG_EXT3_FS_XATTR is not set
-# CONFIG_EXT4DEV_FS is not set
+CONFIG_EXT4_FS=m
+CONFIG_EXT4DEV_COMPAT=y
+# CONFIG_EXT4_FS_XATTR is not set
CONFIG_JBD=m
# CONFIG_JBD_DEBUG is not set
+CONFIG_JBD2=m
+# CONFIG_JBD2_DEBUG is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_DNOTIFY is not set
@@ -1036,6 +1070,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
@@ -1054,7 +1089,8 @@ CONFIG_TMPFS=y
# CONFIG_EFS_FS is not set
CONFIG_JFFS2_FS=y
CONFIG_JFFS2_FS_DEBUG=0
-# CONFIG_JFFS2_FS_WRITEBUFFER is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
# CONFIG_JFFS2_SUMMARY is not set
# CONFIG_JFFS2_FS_XATTR is not set
# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
@@ -1088,6 +1124,7 @@ CONFIG_LOCKD=y
CONFIG_LOCKD_V4=y
CONFIG_NFS_COMMON=y
CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_REGISTER_V4 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
@@ -1185,10 +1222,21 @@ CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_FRAME_POINTER=y
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_KPROBES_SANITY_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
+
+#
+# Tracers
+#
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_CONTEXT_SWITCH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
# CONFIG_SAMPLES is not set
#
@@ -1196,17 +1244,26 @@ CONFIG_FRAME_POINTER=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_CRYPTO=y
#
# Crypto core or helper
#
+CONFIG_CRYPTO_FIPS=y
CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_AEAD2=y
CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_BLKCIPHER2=y
CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=m
+CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_MANAGER2=y
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
@@ -1257,7 +1314,7 @@ CONFIG_CRYPTO_SHA1=m
#
# Ciphers
#
-# CONFIG_CRYPTO_AES is not set
+CONFIG_CRYPTO_AES=m
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
@@ -1278,14 +1335,17 @@ CONFIG_CRYPTO_DES=m
#
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+CONFIG_CRYPTO_ANSI_CPRNG=m
# CONFIG_CRYPTO_HW is not set
#
# Library routines
#
CONFIG_BITREVERSE=y
-# CONFIG_GENERIC_FIND_FIRST_BIT is not set
-# CONFIG_GENERIC_FIND_NEXT_BIT is not set
CONFIG_CRC_CCITT=m
CONFIG_CRC16=y
CONFIG_CRC_T10DIF=m
diff --git a/arch/avr32/include/asm/bitops.h b/arch/avr32/include/asm/bitops.h
index 1a50b69b1a19..f7dd5f71edf7 100644
--- a/arch/avr32/include/asm/bitops.h
+++ b/arch/avr32/include/asm/bitops.h
@@ -263,6 +263,11 @@ static inline int fls(unsigned long word)
return 32 - result;
}
+static inline int __fls(unsigned long word)
+{
+ return fls(word) - 1;
+}
+
unsigned long find_first_zero_bit(const unsigned long *addr,
unsigned long size);
unsigned long find_next_zero_bit(const unsigned long *addr,
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 0c6e02f80a31..066252eebf61 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -967,28 +967,28 @@ static inline void configure_usart0_pins(void)
{
u32 pin_mask = (1 << 8) | (1 << 9); /* RXD & TXD */
- select_peripheral(PIOA, pin_mask, PERIPH_B, 0);
+ select_peripheral(PIOA, pin_mask, PERIPH_B, AT32_GPIOF_PULLUP);
}
static inline void configure_usart1_pins(void)
{
u32 pin_mask = (1 << 17) | (1 << 18); /* RXD & TXD */
- select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
+ select_peripheral(PIOA, pin_mask, PERIPH_A, AT32_GPIOF_PULLUP);
}
static inline void configure_usart2_pins(void)
{
u32 pin_mask = (1 << 26) | (1 << 27); /* RXD & TXD */
- select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
+ select_peripheral(PIOB, pin_mask, PERIPH_B, AT32_GPIOF_PULLUP);
}
static inline void configure_usart3_pins(void)
{
u32 pin_mask = (1 << 18) | (1 << 17); /* RXD & TXD */
- select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
+ select_peripheral(PIOB, pin_mask, PERIPH_B, AT32_GPIOF_PULLUP);
}
static struct platform_device *__initdata at32_usarts[4];
diff --git a/arch/blackfin/boot/Makefile b/arch/blackfin/boot/Makefile
index 522f3c124060..e028d13481a9 100644
--- a/arch/blackfin/boot/Makefile
+++ b/arch/blackfin/boot/Makefile
@@ -25,7 +25,7 @@ $(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
$(obj)/vmImage: $(obj)/vmlinux.gz
$(call if_changed,uimage)
- @echo 'Kernel: $@ is ready'
+ @$(kecho) 'Kernel: $@ is ready'
install:
sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
diff --git a/arch/blackfin/include/asm/bitops.h b/arch/blackfin/include/asm/bitops.h
index b39a175c79c1..c428e4106f89 100644
--- a/arch/blackfin/include/asm/bitops.h
+++ b/arch/blackfin/include/asm/bitops.h
@@ -213,6 +213,7 @@ static __inline__ int __test_bit(int nr, const void *addr)
#endif /* __KERNEL__ */
#include <asm-generic/bitops/fls.h>
+#include <asm-generic/bitops/__fls.h>
#include <asm-generic/bitops/fls64.h>
#endif /* _BLACKFIN_BITOPS_H */
diff --git a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c
index 3d47839a0c48..e4d8fde68103 100644
--- a/arch/ia64/hp/sim/simeth.c
+++ b/arch/ia64/hp/sim/simeth.c
@@ -167,6 +167,15 @@ netdev_read(int fd, unsigned char *buf, unsigned int len)
return ia64_ssc(fd, __pa(buf), len, 0, SSC_NETDEV_RECV);
}
+static const struct net_device_ops simeth_netdev_ops = {
+ .ndo_open = simeth_open,
+ .ndo_stop = simeth_close,
+ .ndo_start_xmit = simeth_tx,
+ .ndo_get_stats = simeth_get_stats,
+ .ndo_set_multicast_list = set_multicast_list, /* not yet used */
+
+};
+
/*
* Function shared with module code, so cannot be in init section
*
@@ -206,14 +215,10 @@ simeth_probe1(void)
memcpy(dev->dev_addr, mac_addr, sizeof(mac_addr));
- local = dev->priv;
+ local = netdev_priv(dev);
local->simfd = fd; /* keep track of underlying file descriptor */
- dev->open = simeth_open;
- dev->stop = simeth_close;
- dev->hard_start_xmit = simeth_tx;
- dev->get_stats = simeth_get_stats;
- dev->set_multicast_list = set_multicast_list; /* no yet used */
+ dev->netdev_ops = &simeth_netdev_ops;
err = register_netdev(dev);
if (err) {
@@ -325,7 +330,7 @@ simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr)
* we get DOWN then UP.
*/
- local = dev->priv;
+ local = netdev_priv(dev);
/* now do it for real */
r = event == NETDEV_UP ?
netdev_attach(local->simfd, dev->irq, ntohl(ifa->ifa_local)):
@@ -380,7 +385,7 @@ frame_print(unsigned char *from, unsigned char *frame, int len)
static int
simeth_tx(struct sk_buff *skb, struct net_device *dev)
{
- struct simeth_local *local = dev->priv;
+ struct simeth_local *local = netdev_priv(dev);
#if 0
/* ensure we have at least ETH_ZLEN bytes (min frame size) */
@@ -443,7 +448,7 @@ simeth_rx(struct net_device *dev)
int len;
int rcv_count = SIMETH_RECV_MAX;
- local = dev->priv;
+ local = netdev_priv(dev);
/*
* the loop concept has been borrowed from other drivers
* looks to me like it's a throttling thing to avoid pushing to many
@@ -507,7 +512,7 @@ simeth_interrupt(int irq, void *dev_id)
static struct net_device_stats *
simeth_get_stats(struct net_device *dev)
{
- struct simeth_local *local = dev->priv;
+ struct simeth_local *local = netdev_priv(dev);
return &local->stats;
}
diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c
index 5e92ae00bdbb..16ef61a91d95 100644
--- a/arch/ia64/ia32/sys_ia32.c
+++ b/arch/ia64/ia32/sys_ia32.c
@@ -1767,25 +1767,24 @@ groups16_from_user(struct group_info *group_info, short __user *grouplist)
asmlinkage long
sys32_getgroups16 (int gidsetsize, short __user *grouplist)
{
+ const struct cred *cred = current_cred();
int i;
if (gidsetsize < 0)
return -EINVAL;
- get_group_info(current->group_info);
- i = current->group_info->ngroups;
+ i = cred->group_info->ngroups;
if (gidsetsize) {
if (i > gidsetsize) {
i = -EINVAL;
goto out;
}
- if (groups16_to_user(grouplist, current->group_info)) {
+ if (groups16_to_user(grouplist, cred->group_info)) {
i = -EFAULT;
goto out;
}
}
out:
- put_group_info(current->group_info);
return i;
}
diff --git a/arch/ia64/include/asm/topology.h b/arch/ia64/include/asm/topology.h
index a3cc9f65f954..76a33a91ca69 100644
--- a/arch/ia64/include/asm/topology.h
+++ b/arch/ia64/include/asm/topology.h
@@ -34,6 +34,7 @@
* Returns a bitmask of CPUs on Node 'node'.
*/
#define node_to_cpumask(node) (node_to_cpu_mask[node])
+#define cpumask_of_node(node) (&node_to_cpu_mask[node])
/*
* Returns the number of the node containing Node 'nid'.
@@ -45,7 +46,7 @@
/*
* Returns the number of the first CPU on Node 'node'.
*/
-#define node_to_first_cpu(node) (first_cpu(node_to_cpumask(node)))
+#define node_to_first_cpu(node) (cpumask_first(cpumask_of_node(node)))
/*
* Determines the node for a given pci bus
@@ -109,6 +110,8 @@ void build_cpu_to_node_map(void);
#define topology_core_id(cpu) (cpu_data(cpu)->core_id)
#define topology_core_siblings(cpu) (cpu_core_map[cpu])
#define topology_thread_siblings(cpu) (per_cpu(cpu_sibling_map, cpu))
+#define topology_core_cpumask(cpu) (&cpu_core_map[cpu])
+#define topology_thread_cpumask(cpu) (&per_cpu(cpu_sibling_map, cpu))
#define smt_capable() (smp_num_siblings > 1)
#endif
@@ -119,6 +122,10 @@ extern void arch_fix_phys_package_id(int num, u32 slot);
node_to_cpumask(pcibus_to_node(bus)) \
)
+#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
+ cpu_all_mask : \
+ cpumask_from_node(pcibus_to_node(bus)))
+
#include <asm-generic/topology.h>
#endif /* _ASM_IA64_TOPOLOGY_H */
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index bd7acc71e8a9..0553648b7595 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -202,7 +202,6 @@ char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size)
Boot-time Table Parsing
-------------------------------------------------------------------------- */
-static int total_cpus __initdata;
static int available_cpus __initdata;
struct acpi_table_madt *acpi_madt __initdata;
static u8 has_8259;
@@ -1001,7 +1000,7 @@ acpi_map_iosapic(acpi_handle handle, u32 depth, void *context, void **ret)
node = pxm_to_node(pxm);
if (node >= MAX_NUMNODES || !node_online(node) ||
- cpus_empty(node_to_cpumask(node)))
+ cpumask_empty(cpumask_of_node(node)))
return AE_OK;
/* We know a gsi to node mapping! */
diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
index c8adecd5b416..5cfd3d91001a 100644
--- a/arch/ia64/kernel/iosapic.c
+++ b/arch/ia64/kernel/iosapic.c
@@ -695,32 +695,31 @@ get_target_cpu (unsigned int gsi, int irq)
#ifdef CONFIG_NUMA
{
int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0;
- cpumask_t cpu_mask;
+ const struct cpumask *cpu_mask;
iosapic_index = find_iosapic(gsi);
if (iosapic_index < 0 ||
iosapic_lists[iosapic_index].node == MAX_NUMNODES)
goto skip_numa_setup;
- cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node);
- cpus_and(cpu_mask, cpu_mask, domain);
- for_each_cpu_mask(numa_cpu, cpu_mask) {
- if (!cpu_online(numa_cpu))
- cpu_clear(numa_cpu, cpu_mask);
+ cpu_mask = cpumask_of_node(iosapic_lists[iosapic_index].node);
+ num_cpus = 0;
+ for_each_cpu_and(numa_cpu, cpu_mask, &domain) {
+ if (cpu_online(numa_cpu))
+ num_cpus++;
}
- num_cpus = cpus_weight(cpu_mask);
-
if (!num_cpus)
goto skip_numa_setup;
/* Use irq assignment to distribute across cpus in node */
cpu_index = irq % num_cpus;
- for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++)
- numa_cpu = next_cpu(numa_cpu, cpu_mask);
+ for_each_cpu_and(numa_cpu, cpu_mask, &domain)
+ if (cpu_online(numa_cpu) && i++ >= cpu_index)
+ break;
- if (numa_cpu != NR_CPUS)
+ if (numa_cpu < nr_cpu_ids)
return cpu_physical_id(numa_cpu);
}
skip_numa_setup:
@@ -731,7 +730,7 @@ skip_numa_setup:
* case of NUMA.)
*/
do {
- if (++cpu >= NR_CPUS)
+ if (++cpu >= nr_cpu_ids)
cpu = 0;
} while (!cpu_online(cpu) || !cpu_isset(cpu, domain));
diff --git a/arch/ia64/kernel/mca_drv.c b/arch/ia64/kernel/mca_drv.c
index fab1d21a4f2c..f94aaa86933f 100644
--- a/arch/ia64/kernel/mca_drv.c
+++ b/arch/ia64/kernel/mca_drv.c
@@ -158,7 +158,7 @@ mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr)
ia64_mlogbuf_dump();
printk(KERN_ERR "OS_MCA: process [cpu %d, pid: %d, uid: %d, "
"iip: %p, psr: 0x%lx,paddr: 0x%lx](%s) encounters MCA.\n",
- raw_smp_processor_id(), current->pid, current->uid,
+ raw_smp_processor_id(), current->pid, current_uid(),
iip, ipsr, paddr, current->comm);
spin_lock(&mca_bh_lock);
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index 6543a5547c84..0e499757309b 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -2220,8 +2220,8 @@ pfm_alloc_file(pfm_context_t *ctx)
DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode));
inode->i_mode = S_IFCHR|S_IRUGO;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
sprintf(name, "[%lu]", inode->i_ino);
this.name = name;
@@ -2399,22 +2399,33 @@ error_kmem:
static int
pfm_bad_permissions(struct task_struct *task)
{
+ const struct cred *tcred;
+ uid_t uid = current_uid();
+ gid_t gid = current_gid();
+ int ret;
+
+ rcu_read_lock();
+ tcred = __task_cred(task);
+
/* inspired by ptrace_attach() */
DPRINT(("cur: uid=%d gid=%d task: euid=%d suid=%d uid=%d egid=%d sgid=%d\n",
- current->uid,
- current->gid,
- task->euid,
- task->suid,
- task->uid,
- task->egid,
- task->sgid));
-
- return ((current->uid != task->euid)
- || (current->uid != task->suid)
- || (current->uid != task->uid)
- || (current->gid != task->egid)
- || (current->gid != task->sgid)
- || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE);
+ uid,
+ gid,
+ tcred->euid,
+ tcred->suid,
+ tcred->uid,
+ tcred->egid,
+ tcred->sgid));
+
+ ret = ((uid != tcred->euid)
+ || (uid != tcred->suid)
+ || (uid != tcred->uid)
+ || (gid != tcred->egid)
+ || (gid != tcred->sgid)
+ || (gid != tcred->gid)) && !capable(CAP_SYS_PTRACE);
+
+ rcu_read_unlock();
+ return ret;
}
static int
diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
index e12500a9c443..e1821ca4c7df 100644
--- a/arch/ia64/kernel/signal.c
+++ b/arch/ia64/kernel/signal.c
@@ -229,7 +229,7 @@ ia64_rt_sigreturn (struct sigscratch *scr)
si.si_errno = 0;
si.si_code = SI_KERNEL;
si.si_pid = task_pid_vnr(current);
- si.si_uid = current->uid;
+ si.si_uid = current_uid();
si.si_addr = sc;
force_sig_info(SIGSEGV, &si, current);
return retval;
@@ -326,7 +326,7 @@ force_sigsegv_info (int sig, void __user *addr)
si.si_errno = 0;
si.si_code = SI_KERNEL;
si.si_pid = task_pid_vnr(current);
- si.si_uid = current->uid;
+ si.si_uid = current_uid();
si.si_addr = addr;
force_sig_info(SIGSEGV, &si, current);
return 0;
diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
index 636588e7e068..be339477f906 100644
--- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c
+++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c
@@ -385,7 +385,6 @@ static int sn_topology_show(struct seq_file *s, void *d)
int j;
const char *slabname;
int ordinal;
- cpumask_t cpumask;
char slice;
struct cpuinfo_ia64 *c;
struct sn_hwperf_port_info *ptdata;
@@ -473,23 +472,21 @@ static int sn_topology_show(struct seq_file *s, void *d)
* CPUs on this node, if any
*/
if (!SN_HWPERF_IS_IONODE(obj)) {
- cpumask = node_to_cpumask(ordinal);
- for_each_online_cpu(i) {
- if (cpu_isset(i, cpumask)) {
- slice = 'a' + cpuid_to_slice(i);
- c = cpu_data(i);
- seq_printf(s, "cpu %d %s%c local"
- " freq %luMHz, arch ia64",
- i, obj->location, slice,
- c->proc_freq / 1000000);
- for_each_online_cpu(j) {
- seq_printf(s, j ? ":%d" : ", dist %d",
- node_distance(
+ for_each_cpu_and(i, cpu_online_mask,
+ cpumask_of_node(ordinal)) {
+ slice = 'a' + cpuid_to_slice(i);
+ c = cpu_data(i);
+ seq_printf(s, "cpu %d %s%c local"
+ " freq %luMHz, arch ia64",
+ i, obj->location, slice,
+ c->proc_freq / 1000000);
+ for_each_online_cpu(j) {
+ seq_printf(s, j ? ":%d" : ", dist %d",
+ node_distance(
cpu_to_node(i),
cpu_to_node(j)));
- }
- seq_putc(s, '\n');
}
+ seq_putc(s, '\n');
}
}
}
diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c
index 0f06b3722e96..2547d6c4a827 100644
--- a/arch/m32r/kernel/smpboot.c
+++ b/arch/m32r/kernel/smpboot.c
@@ -592,7 +592,7 @@ int setup_profiling_timer(unsigned int multiplier)
* accounting. At that time they also adjust their APIC timers
* accordingly.
*/
- for (i = 0; i < NR_CPUS; ++i)
+ for_each_possible_cpu(i)
per_cpu(prof_multiplier, i) = multiplier;
return 0;
diff --git a/arch/m68k/fpsp040/setox.S b/arch/m68k/fpsp040/setox.S
index 145af5447581..f1acf7e36d6b 100644
--- a/arch/m68k/fpsp040/setox.S
+++ b/arch/m68k/fpsp040/setox.S
@@ -36,9 +36,9 @@
| depending on their values, the program may run faster or slower --
| but no worse than 10% slower even in the extreme cases.
|
-| The program setoxm1 takes approximately ???/??? cycles for input
+| The program setoxm1 takes approximately ??? / ??? cycles for input
| argument X, 0.25 <= |X| < 70log2. For |X| < 0.25, it takes
-| approximately ???/??? cycles. For the less common arguments,
+| approximately ??? / ??? cycles. For the less common arguments,
| depending on their values, the program may run faster or slower --
| but no worse than 10% slower even in the extreme cases.
|
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
index c7b25b0aacff..245d16d078ad 100644
--- a/arch/m68k/mac/baboon.c
+++ b/arch/m68k/mac/baboon.c
@@ -18,11 +18,14 @@
#include <asm/macints.h>
#include <asm/mac_baboon.h>
-/* #define DEBUG_BABOON */
/* #define DEBUG_IRQS */
+extern void mac_enable_irq(unsigned int);
+extern void mac_disable_irq(unsigned int);
+
int baboon_present;
static volatile struct baboon *baboon;
+static unsigned char baboon_disabled;
#if 0
extern int macide_ack_intr(struct ata_channel *);
@@ -88,34 +91,51 @@ static irqreturn_t baboon_irq(int irq, void *dev_id)
void __init baboon_register_interrupts(void)
{
- request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
- "baboon", (void *) baboon);
+ baboon_disabled = 0;
+ request_irq(IRQ_NUBUS_C, baboon_irq, 0, "baboon", (void *)baboon);
}
-void baboon_irq_enable(int irq) {
+/*
+ * The means for masking individual baboon interrupts remains a mystery, so
+ * enable the umbrella interrupt only when no baboon interrupt is disabled.
+ */
+
+void baboon_irq_enable(int irq)
+{
+ int irq_idx = IRQ_IDX(irq);
+
#ifdef DEBUG_IRQUSE
printk("baboon_irq_enable(%d)\n", irq);
#endif
- /* FIXME: figure out how to mask and unmask baboon interrupt sources */
- enable_irq(IRQ_NUBUS_C);
+
+ baboon_disabled &= ~(1 << irq_idx);
+ if (!baboon_disabled)
+ mac_enable_irq(IRQ_NUBUS_C);
}
-void baboon_irq_disable(int irq) {
+void baboon_irq_disable(int irq)
+{
+ int irq_idx = IRQ_IDX(irq);
+
#ifdef DEBUG_IRQUSE
printk("baboon_irq_disable(%d)\n", irq);
#endif
- disable_irq(IRQ_NUBUS_C);
+
+ baboon_disabled |= 1 << irq_idx;
+ if (baboon_disabled)
+ mac_disable_irq(IRQ_NUBUS_C);
}
-void baboon_irq_clear(int irq) {
- int irq_idx = IRQ_IDX(irq);
+void baboon_irq_clear(int irq)
+{
+ int irq_idx = IRQ_IDX(irq);
baboon->mb_ifr &= ~(1 << irq_idx);
}
int baboon_irq_pending(int irq)
{
- int irq_idx = IRQ_IDX(irq);
+ int irq_idx = IRQ_IDX(irq);
return baboon->mb_ifr & (1 << irq_idx);
}
diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c
index c45e18449f32..8819b97be324 100644
--- a/arch/m68k/mac/config.c
+++ b/arch/m68k/mac/config.c
@@ -162,10 +162,7 @@ void __init config_mac(void)
mach_init_IRQ = mac_init_IRQ;
mach_get_model = mac_get_model;
mach_gettimeoffset = mac_gettimeoffset;
-#warning move to adb/via init
-#if 0
mach_hwclk = mac_hwclk;
-#endif
mach_set_clock_mmss = mac_set_clock_mmss;
mach_reset = mac_reset;
mach_halt = mac_poweroff;
diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c
index 2165740786a5..65dd77a742a3 100644
--- a/arch/m68k/mac/debug.c
+++ b/arch/m68k/mac/debug.c
@@ -24,7 +24,6 @@
#define BOOTINFO_COMPAT_1_0
#include <asm/setup.h>
#include <asm/bootinfo.h>
-#include <asm/machw.h>
#include <asm/macints.h>
extern unsigned long mac_videobase;
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c
index ecddac4a02b9..82e560c076ce 100644
--- a/arch/m68k/mac/macints.c
+++ b/arch/m68k/mac/macints.c
@@ -127,7 +127,6 @@
#include <asm/irq.h>
#include <asm/traps.h>
#include <asm/bootinfo.h>
-#include <asm/machw.h>
#include <asm/macintosh.h>
#include <asm/mac_via.h>
#include <asm/mac_psc.h>
@@ -215,8 +214,8 @@ irqreturn_t mac_debug_handler(int, void *);
/* #define DEBUG_MACINTS */
-static void mac_enable_irq(unsigned int irq);
-static void mac_disable_irq(unsigned int irq);
+void mac_enable_irq(unsigned int irq);
+void mac_disable_irq(unsigned int irq);
static struct irq_controller mac_irq_controller = {
.name = "mac",
@@ -275,7 +274,7 @@ void __init mac_init_IRQ(void)
* These routines are just dispatchers to the VIA/OSS/PSC routines.
*/
-static void mac_enable_irq(unsigned int irq)
+void mac_enable_irq(unsigned int irq)
{
int irq_src = IRQ_SRC(irq);
@@ -308,7 +307,7 @@ static void mac_enable_irq(unsigned int irq)
}
}
-static void mac_disable_irq(unsigned int irq)
+void mac_disable_irq(unsigned int irq)
{
int irq_src = IRQ_SRC(irq);
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c
index 56d1f5676ade..a44c7086ab39 100644
--- a/arch/m68k/mac/misc.c
+++ b/arch/m68k/mac/misc.c
@@ -93,7 +93,7 @@ static void cuda_write_pram(int offset, __u8 data)
#define cuda_write_pram NULL
#endif
-#ifdef CONFIG_ADB_PMU68K
+#if 0 /* def CONFIG_ADB_PMU68K */
static long pmu_read_time(void)
{
struct adb_request req;
@@ -148,7 +148,7 @@ static void pmu_write_pram(int offset, __u8 data)
#define pmu_write_pram NULL
#endif
-#ifdef CONFIG_ADB_MACIISI
+#if 0 /* def CONFIG_ADB_MACIISI */
extern int maciisi_request(struct adb_request *req,
void (*done)(struct adb_request *), int nbytes, ...);
@@ -717,13 +717,18 @@ int mac_hwclk(int op, struct rtc_time *t)
unmktime(now, 0,
&t->tm_year, &t->tm_mon, &t->tm_mday,
&t->tm_hour, &t->tm_min, &t->tm_sec);
+#if 0
printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
- t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+ t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+#endif
} else { /* write */
+#if 0
printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
- t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+ t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+#endif
-#if 0 /* it trashes my rtc */
now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
@@ -742,7 +747,6 @@ int mac_hwclk(int op, struct rtc_time *t)
case MAC_ADB_IISI:
maciisi_write_time(now);
}
-#endif
}
return 0;
}
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c
index 43d83e054b8e..8426501119ca 100644
--- a/arch/m68k/mac/oss.c
+++ b/arch/m68k/mac/oss.c
@@ -21,7 +21,6 @@
#include <linux/init.h>
#include <asm/bootinfo.h>
-#include <asm/machw.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_via.h>
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c
index 1bdb03c73c0f..f01d418e64fe 100644
--- a/arch/m68k/mac/via.c
+++ b/arch/m68k/mac/via.c
@@ -32,15 +32,10 @@
#include <asm/bootinfo.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#include <asm/mac_psc.h>
volatile __u8 *via1, *via2;
-#if 0
-/* See note in mac_via.h about how this is possibly not useful */
-volatile long *via_memory_bogon=(long *)&via_memory_bogon;
-#endif
int rbv_present;
int via_alt_mapping;
EXPORT_SYMBOL(via_alt_mapping);
@@ -66,7 +61,7 @@ static int gIER,gIFR,gBufA,gBufB;
#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF)
#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8)
-/* To disable a NuBus slot on Quadras we make the slot IRQ lines outputs, set
+/* To disable a NuBus slot on Quadras we make that slot IRQ line an output set
* high. On RBV we just use the slot interrupt enable register. On Macs with
* genuine VIA chips we must use nubus_disabled to keep track of disabled slot
* interrupts. When any slot IRQ is disabled we mask the (edge triggered) CA1
@@ -180,7 +175,7 @@ void __init via_init(void)
via1[vT1CH] = 0;
via1[vT2CL] = 0;
via1[vT2CH] = 0;
- via1[vACR] &= 0x3F;
+ via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
via1[vACR] &= ~0x03; /* disable port A & B latches */
/*
@@ -203,40 +198,41 @@ void __init via_init(void)
/* Everything below this point is VIA2/RBV only... */
- if (oss_present) return;
+ if (oss_present)
+ return;
-#if 1
/* Some machines support an alternate IRQ mapping that spreads */
/* Ethernet and Sound out to their own autolevel IRQs and moves */
/* VIA1 to level 6. A/UX uses this mapping and we do too. Note */
/* that the IIfx emulates this alternate mapping using the OSS. */
- switch(macintosh_config->ident) {
- case MAC_MODEL_P475:
- case MAC_MODEL_P475F:
- case MAC_MODEL_P575:
- case MAC_MODEL_Q605:
- case MAC_MODEL_Q605_ACC:
- case MAC_MODEL_C610:
- case MAC_MODEL_Q610:
- case MAC_MODEL_Q630:
- case MAC_MODEL_C650:
- case MAC_MODEL_Q650:
- case MAC_MODEL_Q700:
- case MAC_MODEL_Q800:
- case MAC_MODEL_Q900:
- case MAC_MODEL_Q950:
+ via_alt_mapping = 0;
+ if (macintosh_config->via_type == MAC_VIA_QUADRA)
+ switch (macintosh_config->ident) {
+ case MAC_MODEL_C660:
+ case MAC_MODEL_Q840:
+ /* not applicable */
+ break;
+ case MAC_MODEL_P588:
+ case MAC_MODEL_TV:
+ case MAC_MODEL_PB140:
+ case MAC_MODEL_PB145:
+ case MAC_MODEL_PB160:
+ case MAC_MODEL_PB165:
+ case MAC_MODEL_PB165C:
+ case MAC_MODEL_PB170:
+ case MAC_MODEL_PB180:
+ case MAC_MODEL_PB180C:
+ case MAC_MODEL_PB190:
+ case MAC_MODEL_PB520:
+ /* not yet tested */
+ break;
+ default:
via_alt_mapping = 1;
via1[vDirB] |= 0x40;
via1[vBufB] &= ~0x40;
break;
- default:
- via_alt_mapping = 0;
- break;
- }
-#else
- via_alt_mapping = 0;
-#endif
+ }
/*
* Now initialize VIA2. For RBV we just kill all interrupts;
@@ -252,14 +248,17 @@ void __init via_init(void)
via2[vT1CH] = 0;
via2[vT2CL] = 0;
via2[vT2CH] = 0;
- via2[vACR] &= 0x3F;
+ via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
via2[vACR] &= ~0x03; /* disable port A & B latches */
}
/*
- * Set vPCR for SCSI interrupts (but not on RBV)
+ * Set vPCR for control line interrupts (but not on RBV)
*/
if (!rbv_present) {
+ /* For all VIA types, CA1 (SLOTS IRQ) and CB1 (ASC IRQ)
+ * are made negative edge triggered here.
+ */
if (macintosh_config->scsi_type == MAC_SCSI_OLD) {
/* CB2 (IRQ) indep. input, positive edge */
/* CA2 (DRQ) indep. input, positive edge */
@@ -466,21 +465,6 @@ irqreturn_t via1_irq(int irq, void *dev_id)
++irq_num;
irq_bit <<= 1;
} while (events >= irq_bit);
-
-#if 0 /* freakin' pmu is doing weird stuff */
- if (!oss_present) {
- /* This (still) seems to be necessary to get IDE
- working. However, if you enable VBL interrupts,
- you're screwed... */
- /* FIXME: should we check the SLOTIRQ bit before
- pulling this stunt? */
- /* No, it won't be set. that's why we're doing this. */
- via_irq_disable(IRQ_MAC_NUBUS);
- via_irq_clear(IRQ_MAC_NUBUS);
- m68k_handle_int(IRQ_MAC_NUBUS);
- via_irq_enable(IRQ_MAC_NUBUS);
- }
-#endif
return IRQ_HANDLED;
}
diff --git a/arch/m68knommu/include/asm/bitops.h b/arch/m68knommu/include/asm/bitops.h
index 6f3685eab44c..9d3cbe5fad1e 100644
--- a/arch/m68knommu/include/asm/bitops.h
+++ b/arch/m68knommu/include/asm/bitops.h
@@ -331,6 +331,7 @@ found_middle:
#endif /* __KERNEL__ */
#include <asm-generic/bitops/fls.h>
+#include <asm-generic/bitops/__fls.h>
#include <asm-generic/bitops/fls64.h>
#endif /* _M68KNOMMU_BITOPS_H */
diff --git a/arch/mips/include/asm/byteorder.h b/arch/mips/include/asm/byteorder.h
index 2988d29a0867..33790b9e0cc0 100644
--- a/arch/mips/include/asm/byteorder.h
+++ b/arch/mips/include/asm/byteorder.h
@@ -50,9 +50,8 @@ static inline __attribute_const__ __u32 __arch_swab32(__u32 x)
static inline __attribute_const__ __u64 __arch_swab64(__u64 x)
{
__asm__(
- " dsbh %0, %1 \n"
- " dshd %0, %0 \n"
- " drotr %0, %0, 32 \n"
+ " dsbh %0, %1\n"
+ " dshd %0, %0"
: "=r" (x)
: "r" (x));
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index a8eac1697b3d..d58f128aa747 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -232,7 +232,7 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
*/
#ifdef __MIPSEB__
#define ELF_DATA ELFDATA2MSB
-#elif __MIPSEL__
+#elif defined(__MIPSEL__)
#define ELF_DATA ELFDATA2LSB
#endif
#define ELF_ARCH EM_MIPS
diff --git a/arch/mips/include/asm/mach-ip27/topology.h b/arch/mips/include/asm/mach-ip27/topology.h
index 1fb959f98982..55d481569a1f 100644
--- a/arch/mips/include/asm/mach-ip27/topology.h
+++ b/arch/mips/include/asm/mach-ip27/topology.h
@@ -25,11 +25,13 @@ extern struct cpuinfo_ip27 sn_cpu_info[NR_CPUS];
#define cpu_to_node(cpu) (sn_cpu_info[(cpu)].p_nodeid)
#define parent_node(node) (node)
#define node_to_cpumask(node) (hub_data(node)->h_cpus)
-#define node_to_first_cpu(node) (first_cpu(node_to_cpumask(node)))
+#define cpumask_of_node(node) (&hub_data(node)->h_cpus)
+#define node_to_first_cpu(node) (cpumask_first(cpumask_of_node(node)))
struct pci_bus;
extern int pcibus_to_node(struct pci_bus *);
#define pcibus_to_cpumask(bus) (cpu_online_map)
+#define cpumask_of_pcibus(bus) (cpu_online_mask)
extern unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES];
diff --git a/arch/mips/kernel/kspd.c b/arch/mips/kernel/kspd.c
index b0591ae0ce56..fd6e51224034 100644
--- a/arch/mips/kernel/kspd.c
+++ b/arch/mips/kernel/kspd.c
@@ -174,8 +174,8 @@ static unsigned int translate_open_flags(int flags)
static void sp_setfsuidgid( uid_t uid, gid_t gid)
{
- current->fsuid = uid;
- current->fsgid = gid;
+ current->cred->fsuid = uid;
+ current->cred->fsgid = gid;
key_fsuid_changed(current);
key_fsgid_changed(current);
diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c
index dc9eb72ed9de..5e77a3a21f98 100644
--- a/arch/mips/kernel/mips-mt-fpaff.c
+++ b/arch/mips/kernel/mips-mt-fpaff.c
@@ -51,6 +51,7 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
int retval;
struct task_struct *p;
struct thread_info *ti;
+ uid_t euid;
if (len < sizeof(new_mask))
return -EINVAL;
@@ -76,9 +77,9 @@ asmlinkage long mipsmt_sys_sched_setaffinity(pid_t pid, unsigned int len,
*/
get_task_struct(p);
+ euid = current_euid();
retval = -EPERM;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
- !capable(CAP_SYS_NICE)) {
+ if (euid != p->euid && euid != p->uid && !capable(CAP_SYS_NICE)) {
read_unlock(&tasklist_lock);
goto out_unlock;
}
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c
index a1b3da6bad5c..010b27e01f7b 100644
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -1085,8 +1085,8 @@ static int vpe_open(struct inode *inode, struct file *filp)
v->load_addr = NULL;
v->len = 0;
- v->uid = filp->f_uid;
- v->gid = filp->f_gid;
+ v->uid = filp->f_cred->fsuid;
+ v->gid = filp->f_cred->fsgid;
#ifdef CONFIG_MIPS_APSP_KSPD
/* get kspd to tell us when a syscall_exit happens */
diff --git a/arch/parisc/include/asm/smp.h b/arch/parisc/include/asm/smp.h
index 409e698f4361..6ef4b7867b1b 100644
--- a/arch/parisc/include/asm/smp.h
+++ b/arch/parisc/include/asm/smp.h
@@ -16,8 +16,6 @@
#include <linux/cpumask.h>
typedef unsigned long address_t;
-extern cpumask_t cpu_online_map;
-
/*
* Private routines/data
diff --git a/arch/parisc/include/asm/tlbflush.h b/arch/parisc/include/asm/tlbflush.h
index b72ec66db699..1f6fd4fc05b9 100644
--- a/arch/parisc/include/asm/tlbflush.h
+++ b/arch/parisc/include/asm/tlbflush.h
@@ -44,9 +44,12 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
{
BUG_ON(mm == &init_mm); /* Should never happen */
-#ifdef CONFIG_SMP
+#if 1 || defined(CONFIG_SMP)
flush_tlb_all();
#else
+ /* FIXME: currently broken, causing space id and protection ids
+ * to go out of sync, resulting in faults on userspace accesses.
+ */
if (mm) {
if (mm->context != 0)
free_sid(mm->context);
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c
index 06213d1d6d95..f82544225e8e 100644
--- a/arch/parisc/kernel/signal.c
+++ b/arch/parisc/kernel/signal.c
@@ -182,7 +182,7 @@ give_sigsegv:
si.si_errno = 0;
si.si_code = SI_KERNEL;
si.si_pid = task_pid_vnr(current);
- si.si_uid = current->uid;
+ si.si_uid = current_uid();
si.si_addr = &frame->uc;
force_sig_info(SIGSEGV, &si, current);
return;
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index adb23ea1c1ef..79f25cef32df 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -285,6 +285,10 @@ config IOMMU_VMERGE
config IOMMU_HELPER
def_bool PPC64
+config PPC_NEED_DMA_SYNC_OPS
+ def_bool y
+ depends on NOT_COHERENT_CACHE
+
config HOTPLUG_CPU
bool "Support for enabling/disabling CPUs"
depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC)
@@ -322,7 +326,7 @@ config KEXEC
config CRASH_DUMP
bool "Build a kdump crash kernel"
- depends on PPC_MULTIPLATFORM && PPC64 && RELOCATABLE
+ depends on (PPC64 && RELOCATABLE) || 6xx
help
Build a kernel suitable for use as a kdump capture kernel.
The same kernel binary can be used as production kernel and dump
@@ -401,23 +405,53 @@ config PPC_HAS_HASH_64K
depends on PPC64
default n
-config PPC_64K_PAGES
- bool "64k page size"
- depends on PPC64
- select PPC_HAS_HASH_64K
+choice
+ prompt "Page size"
+ default PPC_4K_PAGES
help
- This option changes the kernel logical page size to 64k. On machines
- without processor support for 64k pages, the kernel will simulate
- them by loading each individual 4k page on demand transparently,
- while on hardware with such support, it will be used to map
- normal application pages.
+ Select the kernel logical page size. Increasing the page size
+ will reduce software overhead at each page boundary, allow
+ hardware prefetch mechanisms to be more effective, and allow
+ larger dma transfers increasing IO efficiency and reducing
+ overhead. However the utilization of memory will increase.
+ For example, each cached file will using a multiple of the
+ page size to hold its contents and the difference between the
+ end of file and the end of page is wasted.
+
+ Some dedicated systems, such as software raid serving with
+ accelerated calculations, have shown significant increases.
+
+ If you configure a 64 bit kernel for 64k pages but the
+ processor does not support them, then the kernel will simulate
+ them with 4k pages, loading them on demand, but with the
+ reduced software overhead and larger internal fragmentation.
+ For the 32 bit kernel, a large page option will not be offered
+ unless it is supported by the configured processor.
+
+ If unsure, choose 4K_PAGES.
+
+config PPC_4K_PAGES
+ bool "4k page size"
+
+config PPC_16K_PAGES
+ bool "16k page size" if 44x
+
+config PPC_64K_PAGES
+ bool "64k page size" if 44x || PPC_STD_MMU_64
+ select PPC_HAS_HASH_64K if PPC_STD_MMU_64
+
+endchoice
config FORCE_MAX_ZONEORDER
int "Maximum zone order"
- range 9 64 if PPC_64K_PAGES
- default "9" if PPC_64K_PAGES
- range 13 64 if PPC64 && !PPC_64K_PAGES
- default "13" if PPC64 && !PPC_64K_PAGES
+ range 9 64 if PPC_STD_MMU_64 && PPC_64K_PAGES
+ default "9" if PPC_STD_MMU_64 && PPC_64K_PAGES
+ range 13 64 if PPC_STD_MMU_64 && !PPC_64K_PAGES
+ default "13" if PPC_STD_MMU_64 && !PPC_64K_PAGES
+ range 9 64 if PPC_STD_MMU_32 && PPC_16K_PAGES
+ default "9" if PPC_STD_MMU_32 && PPC_16K_PAGES
+ range 7 64 if PPC_STD_MMU_32 && PPC_64K_PAGES
+ default "7" if PPC_STD_MMU_32 && PPC_64K_PAGES
range 11 64
default "11"
help
@@ -437,7 +471,7 @@ config FORCE_MAX_ZONEORDER
config PPC_SUBPAGE_PROT
bool "Support setting protections for 4k subpages"
- depends on PPC_64K_PAGES
+ depends on PPC_STD_MMU_64 && PPC_64K_PAGES
help
This option adds support for a system call to allow user programs
to set access permissions (read/write, readonly, or no access)
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 15eb27861fc7..08f7cc0a1953 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -2,6 +2,15 @@ menu "Kernel hacking"
source "lib/Kconfig.debug"
+config PRINT_STACK_DEPTH
+ int "Stack depth to print" if DEBUG_KERNEL
+ default 64
+ help
+ This option allows you to set the stack depth that the kernel
+ prints in stack traces. This can be useful if your display is
+ too small and stack traces cause important information to
+ scroll off the screen.
+
config DEBUG_STACKOVERFLOW
bool "Check for stack overflows"
depends on DEBUG_KERNEL
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 1f0667069940..72d17f50e54f 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -107,7 +107,6 @@ KBUILD_CFLAGS += $(call cc-option,-mno-altivec)
# (We use all available options to help semi-broken compilers)
KBUILD_CFLAGS += $(call cc-option,-mno-spe)
KBUILD_CFLAGS += $(call cc-option,-mspe=no)
-KBUILD_CFLAGS += $(call cc-option,-mabi=no-spe)
# Enable unit-at-a-time mode when possible. It shrinks the
# kernel considerably.
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 3d3daa674299..f32829937aad 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -194,6 +194,7 @@ image-$(CONFIG_PPC_MAPLE) += zImage.pseries
image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries
image-$(CONFIG_PPC_PS3) += dtbImage.ps3
image-$(CONFIG_PPC_CELLEB) += zImage.pseries
+image-$(CONFIG_PPC_CELL_QPACE) += zImage.pseries
image-$(CONFIG_PPC_CHRP) += zImage.chrp
image-$(CONFIG_PPC_EFIKA) += zImage.chrp
image-$(CONFIG_PPC_PMAC) += zImage.pmac
diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c
index 5d12336dc360..a7e21a35c03a 100644
--- a/arch/powerpc/boot/devtree.c
+++ b/arch/powerpc/boot/devtree.c
@@ -213,7 +213,7 @@ static int find_range(u32 *reg, u32 *ranges, int nregaddr,
u32 range_addr[MAX_ADDR_CELLS];
u32 range_size[MAX_ADDR_CELLS];
- copy_val(range_addr, ranges + i, naddr);
+ copy_val(range_addr, ranges + i, nregaddr);
copy_val(range_size, ranges + i + nregaddr + naddr, nsize);
if (compare_reg(reg, range_addr, range_size))
diff --git a/arch/powerpc/boot/dts/asp834x-redboot.dts b/arch/powerpc/boot/dts/asp834x-redboot.dts
index 6235fca445de..524af7ef9f26 100644
--- a/arch/powerpc/boot/dts/asp834x-redboot.dts
+++ b/arch/powerpc/boot/dts/asp834x-redboot.dts
@@ -199,8 +199,26 @@
reg = <0x2>;
device_type = "ethernet-phy";
};
+
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -210,6 +228,7 @@
local-mac-address = [ 00 08 e5 11 32 33 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
linux,network-index = <0>;
};
@@ -223,6 +242,7 @@
local-mac-address = [ 00 08 e5 11 32 34 ];
interrupts = <35 0x8 36 0x8 37 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
linux,network-index = <1>;
};
diff --git a/arch/powerpc/boot/dts/bamboo.dts b/arch/powerpc/boot/dts/bamboo.dts
index 6ce0cc2c0208..aa68911f6560 100644
--- a/arch/powerpc/boot/dts/bamboo.dts
+++ b/arch/powerpc/boot/dts/bamboo.dts
@@ -269,7 +269,8 @@
* later cannot be changed. Chip supports a second
* IO range but we don't use it for now
*/
- ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x20000000
+ ranges = <0x02000000 0x00000000 0xa0000000 0x00000000 0xa0000000 0x00000000 0x40000000
+ 0x02000000 0x00000000 0x00000000 0x00000000 0xe0000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x00000000 0xe8000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
diff --git a/arch/powerpc/boot/dts/canyonlands.dts b/arch/powerpc/boot/dts/canyonlands.dts
index 79fe412c11c9..8b5ba8261a36 100644
--- a/arch/powerpc/boot/dts/canyonlands.dts
+++ b/arch/powerpc/boot/dts/canyonlands.dts
@@ -40,6 +40,7 @@
d-cache-size = <32768>;
dcr-controller;
dcr-access-method = "native";
+ next-level-cache = <&L2C0>;
};
};
@@ -104,6 +105,16 @@
dcr-reg = <0x00c 0x002>;
};
+ L2C0: l2c {
+ compatible = "ibm,l2-cache-460ex", "ibm,l2-cache";
+ dcr-reg = <0x020 0x008 /* Internal SRAM DCR's */
+ 0x030 0x008>; /* L2 cache DCR's */
+ cache-line-size = <32>; /* 32 bytes */
+ cache-size = <262144>; /* L2, 256K */
+ interrupt-parent = <&UIC1>;
+ interrupts = <11 1>;
+ };
+
plb {
compatible = "ibm,plb-460ex", "ibm,plb4";
#address-cells = <2>;
@@ -343,6 +354,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000d 0x80000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000c 0x0ee00000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000c 0x08000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
@@ -373,6 +385,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
@@ -414,6 +427,7 @@
* later cannot be changed
*/
ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x80000000 0x00000000 0x80000000
+ 0x02000000 0x00000000 0x00000000 0x0000000f 0x00100000 0x00000000 0x00100000
0x01000000 0x00000000 0x00000000 0x0000000f 0x80010000 0x00000000 0x00010000>;
/* Inbound 2GB range starting at 0 */
diff --git a/arch/powerpc/boot/dts/gef_sbc610.dts b/arch/powerpc/boot/dts/gef_sbc610.dts
index e48cfa740c8a..9708b3423bbd 100644
--- a/arch/powerpc/boot/dts/gef_sbc610.dts
+++ b/arch/powerpc/boot/dts/gef_sbc610.dts
@@ -98,6 +98,12 @@
interrupt-parent = <&mpic>;
};
+ gef_gpio: gpio@7,14000 {
+ #gpio-cells = <2>;
+ compatible = "gef,sbc610-gpio";
+ reg = <0x7 0x14000 0x24>;
+ gpio-controller;
+ };
};
soc@fef00000 {
@@ -119,6 +125,11 @@
interrupt-parent = <&mpic>;
dfsrr;
+ rtc@51 {
+ compatible = "epson,rx8581";
+ reg = <0x00000051>;
+ };
+
eti@6b {
compatible = "dallas,ds1682";
reg = <0x6b>;
diff --git a/arch/powerpc/boot/dts/ksi8560.dts b/arch/powerpc/boot/dts/ksi8560.dts
index 49737589ffc8..3bfff47418db 100644
--- a/arch/powerpc/boot/dts/ksi8560.dts
+++ b/arch/powerpc/boot/dts/ksi8560.dts
@@ -141,8 +141,26 @@
reg = <0x2>;
device_type = "ethernet-phy";
};
+
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+
enet0: ethernet@24000 {
device_type = "network";
model = "TSEC";
@@ -152,6 +170,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&PHY1>;
};
@@ -164,6 +183,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x23 0x2 0x24 0x2 0x28 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&PHY2>;
};
diff --git a/arch/powerpc/boot/dts/kuroboxHD.dts b/arch/powerpc/boot/dts/kuroboxHD.dts
index 2e5a1a1812b6..8d725d10882f 100644
--- a/arch/powerpc/boot/dts/kuroboxHD.dts
+++ b/arch/powerpc/boot/dts/kuroboxHD.dts
@@ -76,7 +76,6 @@ XXXX add flash parts, rtc, ??
interrupt-parent = <&mpic>;
rtc@32 {
- device_type = "rtc";
compatible = "ricoh,rs5c372a";
reg = <0x32>;
};
diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts
index e4916e69ad31..b13a11eb81b0 100644
--- a/arch/powerpc/boot/dts/kuroboxHG.dts
+++ b/arch/powerpc/boot/dts/kuroboxHG.dts
@@ -76,7 +76,6 @@ XXXX add flash parts, rtc, ??
interrupt-parent = <&mpic>;
rtc@32 {
- device_type = "rtc";
compatible = "ricoh,rs5c372a";
reg = <0x32>;
};
diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts
index 2cf9a8768f44..3f7a5dce8de0 100644
--- a/arch/powerpc/boot/dts/lite5200.dts
+++ b/arch/powerpc/boot/dts/lite5200.dts
@@ -130,7 +130,6 @@
rtc@800 { // Real time clock
compatible = "fsl,mpc5200-rtc";
- device_type = "rtc";
reg = <0x800 0x100>;
interrupts = <1 5 0 1 6 0>;
interrupt-parent = <&mpc5200_pic>;
diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts
index 7bd5b9c399b8..63e3bb48e843 100644
--- a/arch/powerpc/boot/dts/lite5200b.dts
+++ b/arch/powerpc/boot/dts/lite5200b.dts
@@ -130,7 +130,6 @@
rtc@800 { // Real time clock
compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc";
- device_type = "rtc";
reg = <0x800 0x100>;
interrupts = <1 5 0 1 6 0>;
interrupt-parent = <&mpc5200_pic>;
diff --git a/arch/powerpc/boot/dts/motionpro.dts b/arch/powerpc/boot/dts/motionpro.dts
index 9e3c921be164..52ba6f98b273 100644
--- a/arch/powerpc/boot/dts/motionpro.dts
+++ b/arch/powerpc/boot/dts/motionpro.dts
@@ -248,7 +248,6 @@
fsl5200-clocking;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
};
diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts
index 503031766825..d4df8b6857a4 100644
--- a/arch/powerpc/boot/dts/mpc8313erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8313erdb.dts
@@ -190,6 +190,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 0x8 36 0x8 35 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = < &tbi0 >;
phy-handle = < &phy1 >;
fsl,magic-packet;
@@ -210,6 +211,10 @@
reg = <0x4>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
};
@@ -222,9 +227,24 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <34 0x8 33 0x8 32 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = < &tbi1 >;
phy-handle = < &phy4 >;
sleep = <&pmc 0x10000000>;
fsl,magic-packet;
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+
};
serial0: serial@4500 {
diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts
index 6b850670de1d..072c9b0f8c8e 100644
--- a/arch/powerpc/boot/dts/mpc8315erdb.dts
+++ b/arch/powerpc/boot/dts/mpc8315erdb.dts
@@ -117,7 +117,6 @@
interrupt-parent = <&ipic>;
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
};
@@ -206,8 +205,25 @@
reg = <0x1>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -217,6 +233,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = < &phy0 >;
};
@@ -229,6 +246,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 0x8 36 0x8 37 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = < &phy1 >;
};
diff --git a/arch/powerpc/boot/dts/mpc8349emitx.dts b/arch/powerpc/boot/dts/mpc8349emitx.dts
index 4bdbaf4993a1..b5eda94a8e2a 100644
--- a/arch/powerpc/boot/dts/mpc8349emitx.dts
+++ b/arch/powerpc/boot/dts/mpc8349emitx.dts
@@ -85,7 +85,6 @@
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
interrupts = <18 0x8>;
@@ -184,6 +183,22 @@
reg = <0x1c>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -195,6 +210,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy1c>;
linux,network-index = <0>;
};
@@ -211,6 +227,7 @@
/* Vitesse 7385 isn't on the MDIO bus */
fixed-link = <1 1 1000 0 0>;
linux,network-index = <1>;
+ tbi-handle = <&tbi1>;
};
serial0: serial@4500 {
diff --git a/arch/powerpc/boot/dts/mpc8349emitxgp.dts b/arch/powerpc/boot/dts/mpc8349emitxgp.dts
index fa40647ee62e..c87a6015e165 100644
--- a/arch/powerpc/boot/dts/mpc8349emitxgp.dts
+++ b/arch/powerpc/boot/dts/mpc8349emitxgp.dts
@@ -83,7 +83,6 @@
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
interrupts = <18 0x8>;
@@ -163,6 +162,10 @@
reg = <0x1c>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -174,6 +177,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy1c>;
linux,network-index = <0>;
};
diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts
index c986c541e9bb..d9adba01c09c 100644
--- a/arch/powerpc/boot/dts/mpc834x_mds.dts
+++ b/arch/powerpc/boot/dts/mpc834x_mds.dts
@@ -185,8 +185,25 @@
reg = <0x1>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -196,6 +213,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
linux,network-index = <0>;
};
@@ -209,6 +227,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 0x8 36 0x8 37 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
linux,network-index = <1>;
};
diff --git a/arch/powerpc/boot/dts/mpc8377_mds.dts b/arch/powerpc/boot/dts/mpc8377_mds.dts
index 0484561bd2c0..1d14d7052e6d 100644
--- a/arch/powerpc/boot/dts/mpc8377_mds.dts
+++ b/arch/powerpc/boot/dts/mpc8377_mds.dts
@@ -193,8 +193,25 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -205,6 +222,7 @@
interrupts = <32 0x8 33 0x8 34 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -218,6 +236,7 @@
interrupts = <35 0x8 36 0x8 37 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy3>;
};
diff --git a/arch/powerpc/boot/dts/mpc8377_rdb.dts b/arch/powerpc/boot/dts/mpc8377_rdb.dts
index 435ef3dd022d..9413af3b9925 100644
--- a/arch/powerpc/boot/dts/mpc8377_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc8377_rdb.dts
@@ -117,7 +117,6 @@
interrupt-parent = <&ipic>;
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
};
@@ -211,8 +210,25 @@
reg = <0x2>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -223,6 +239,7 @@
interrupts = <32 0x8 33 0x8 34 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -237,6 +254,7 @@
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
fixed-link = <1 1 1000 0 0>;
+ tbi-handle = <&tbi1>;
};
serial0: serial@4500 {
diff --git a/arch/powerpc/boot/dts/mpc8378_mds.dts b/arch/powerpc/boot/dts/mpc8378_mds.dts
index 67a08d2e2ff2..b85fc02682d2 100644
--- a/arch/powerpc/boot/dts/mpc8378_mds.dts
+++ b/arch/powerpc/boot/dts/mpc8378_mds.dts
@@ -232,8 +232,25 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -244,6 +261,7 @@
interrupts = <32 0x8 33 0x8 34 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -257,6 +275,7 @@
interrupts = <35 0x8 36 0x8 37 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy3>;
};
diff --git a/arch/powerpc/boot/dts/mpc8378_rdb.dts b/arch/powerpc/boot/dts/mpc8378_rdb.dts
index b11e68f56a06..23c10ce22c2c 100644
--- a/arch/powerpc/boot/dts/mpc8378_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc8378_rdb.dts
@@ -117,7 +117,6 @@
interrupt-parent = <&ipic>;
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
};
@@ -211,8 +210,25 @@
reg = <0x2>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
diff --git a/arch/powerpc/boot/dts/mpc8379_mds.dts b/arch/powerpc/boot/dts/mpc8379_mds.dts
index 323370a2b5ff..acf06c438dbf 100644
--- a/arch/powerpc/boot/dts/mpc8379_mds.dts
+++ b/arch/powerpc/boot/dts/mpc8379_mds.dts
@@ -232,6 +232,22 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -244,6 +260,7 @@
interrupts = <32 0x8 33 0x8 34 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -257,6 +274,7 @@
interrupts = <35 0x8 36 0x8 37 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy3>;
};
diff --git a/arch/powerpc/boot/dts/mpc8379_rdb.dts b/arch/powerpc/boot/dts/mpc8379_rdb.dts
index 337af6ea26d3..72cdc3c4c7e3 100644
--- a/arch/powerpc/boot/dts/mpc8379_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc8379_rdb.dts
@@ -117,7 +117,6 @@
interrupt-parent = <&ipic>;
dfsrr;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1339";
reg = <0x68>;
};
@@ -211,6 +210,22 @@
reg = <0x2>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -223,6 +238,7 @@
interrupts = <32 0x8 33 0x8 34 0x8>;
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -237,6 +253,7 @@
phy-connection-type = "mii";
interrupt-parent = <&ipic>;
fixed-link = <1 1 1000 0 0>;
+ tbi-handle = <&tbi1>;
};
serial0: serial@4500 {
diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
index 35db1e5440c7..3c905df1812c 100644
--- a/arch/powerpc/boot/dts/mpc8536ds.dts
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -155,6 +155,22 @@
reg = <1>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
usb@22000 {
@@ -186,6 +202,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy1>;
phy-connection-type = "rgmii-id";
};
@@ -199,6 +216,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy0>;
phy-connection-type = "rgmii-id";
};
diff --git a/arch/powerpc/boot/dts/mpc8540ads.dts b/arch/powerpc/boot/dts/mpc8540ads.dts
index 9568bfaff8f7..79570ffe41b9 100644
--- a/arch/powerpc/boot/dts/mpc8540ads.dts
+++ b/arch/powerpc/boot/dts/mpc8540ads.dts
@@ -150,6 +150,34 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -161,6 +189,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -173,6 +202,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
@@ -185,6 +215,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <41 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy3>;
};
diff --git a/arch/powerpc/boot/dts/mpc8541cds.dts b/arch/powerpc/boot/dts/mpc8541cds.dts
index 6480f4fd96e0..221036a8ce23 100644
--- a/arch/powerpc/boot/dts/mpc8541cds.dts
+++ b/arch/powerpc/boot/dts/mpc8541cds.dts
@@ -144,6 +144,22 @@
reg = <0x1>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -155,6 +171,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -167,6 +184,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/mpc8544ds.dts b/arch/powerpc/boot/dts/mpc8544ds.dts
index f1fb20737e3e..b9da42105066 100644
--- a/arch/powerpc/boot/dts/mpc8544ds.dts
+++ b/arch/powerpc/boot/dts/mpc8544ds.dts
@@ -116,8 +116,26 @@
reg = <0x1>;
device_type = "ethernet-phy";
};
+
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+
dma@21300 {
#address-cells = <1>;
#size-cells = <1>;
@@ -169,6 +187,7 @@
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
phy-handle = <&phy0>;
+ tbi-handle = <&tbi0>;
phy-connection-type = "rgmii-id";
};
@@ -182,6 +201,7 @@
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
phy-handle = <&phy1>;
+ tbi-handle = <&tbi1>;
phy-connection-type = "rgmii-id";
};
diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts
index 431b496270dc..df774a7088ff 100644
--- a/arch/powerpc/boot/dts/mpc8548cds.dts
+++ b/arch/powerpc/boot/dts/mpc8548cds.dts
@@ -172,6 +172,46 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -183,6 +223,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -195,6 +236,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
@@ -208,6 +250,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy2>;
};
@@ -220,6 +263,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy3>;
};
*/
diff --git a/arch/powerpc/boot/dts/mpc8555cds.dts b/arch/powerpc/boot/dts/mpc8555cds.dts
index d833a5c4f476..053b01e1c93b 100644
--- a/arch/powerpc/boot/dts/mpc8555cds.dts
+++ b/arch/powerpc/boot/dts/mpc8555cds.dts
@@ -144,6 +144,22 @@
reg = <0x1>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -155,6 +171,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -167,6 +184,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/mpc8560ads.dts b/arch/powerpc/boot/dts/mpc8560ads.dts
index 4d1f2f284094..11b1bcbe14ce 100644
--- a/arch/powerpc/boot/dts/mpc8560ads.dts
+++ b/arch/powerpc/boot/dts/mpc8560ads.dts
@@ -145,6 +145,22 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -156,6 +172,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -168,6 +185,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/mpc8568mds.dts b/arch/powerpc/boot/dts/mpc8568mds.dts
index c80158f7741d..1955bd9e113d 100644
--- a/arch/powerpc/boot/dts/mpc8568mds.dts
+++ b/arch/powerpc/boot/dts/mpc8568mds.dts
@@ -179,6 +179,22 @@
reg = <0x3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -190,6 +206,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -202,6 +219,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy3>;
};
diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts
index 5c69b2fafd32..21459e161d02 100644
--- a/arch/powerpc/boot/dts/mpc8572ds.dts
+++ b/arch/powerpc/boot/dts/mpc8572ds.dts
@@ -63,6 +63,119 @@
device_type = "memory";
};
+ localbus@ffe05000 {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8572-elbc", "fsl,elbc", "simple-bus";
+ reg = <0 0xffe05000 0 0x1000>;
+ interrupts = <19 2>;
+ interrupt-parent = <&mpic>;
+
+ ranges = <0x0 0x0 0x0 0xe8000000 0x08000000
+ 0x1 0x0 0x0 0xe0000000 0x08000000
+ 0x2 0x0 0x0 0xffa00000 0x00040000
+ 0x3 0x0 0x0 0xffdf0000 0x00008000
+ 0x4 0x0 0x0 0xffa40000 0x00040000
+ 0x5 0x0 0x0 0xffa80000 0x00040000
+ 0x6 0x0 0x0 0xffac0000 0x00040000>;
+
+ nor@0,0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "cfi-flash";
+ reg = <0x0 0x0 0x8000000>;
+ bank-width = <2>;
+ device-width = <1>;
+
+ ramdisk@0 {
+ reg = <0x0 0x03000000>;
+ readl-only;
+ };
+
+ diagnostic@3000000 {
+ reg = <0x03000000 0x00e00000>;
+ read-only;
+ };
+
+ dink@3e00000 {
+ reg = <0x03e00000 0x00200000>;
+ read-only;
+ };
+
+ kernel@4000000 {
+ reg = <0x04000000 0x00400000>;
+ read-only;
+ };
+
+ jffs2@4400000 {
+ reg = <0x04400000 0x03b00000>;
+ };
+
+ dtb@7f00000 {
+ reg = <0x07f00000 0x00080000>;
+ read-only;
+ };
+
+ u-boot@7f80000 {
+ reg = <0x07f80000 0x00080000>;
+ read-only;
+ };
+ };
+
+ nand@2,0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8572-fcm-nand",
+ "fsl,elbc-fcm-nand";
+ reg = <0x2 0x0 0x40000>;
+
+ u-boot@0 {
+ reg = <0x0 0x02000000>;
+ read-only;
+ };
+
+ jffs2@2000000 {
+ reg = <0x02000000 0x10000000>;
+ };
+
+ ramdisk@12000000 {
+ reg = <0x12000000 0x08000000>;
+ read-only;
+ };
+
+ kernel@1a000000 {
+ reg = <0x1a000000 0x04000000>;
+ };
+
+ dtb@1e000000 {
+ reg = <0x1e000000 0x01000000>;
+ read-only;
+ };
+
+ empty@1f000000 {
+ reg = <0x1f000000 0x21000000>;
+ };
+ };
+
+ nand@4,0 {
+ compatible = "fsl,mpc8572-fcm-nand",
+ "fsl,elbc-fcm-nand";
+ reg = <0x4 0x0 0x40000>;
+ };
+
+ nand@5,0 {
+ compatible = "fsl,mpc8572-fcm-nand",
+ "fsl,elbc-fcm-nand";
+ reg = <0x5 0x0 0x40000>;
+ };
+
+ nand@6,0 {
+ compatible = "fsl,mpc8572-fcm-nand",
+ "fsl,elbc-fcm-nand";
+ reg = <0x6 0x0 0x40000>;
+ };
+ };
+
soc8572@ffe00000 {
#address-cells = <1>;
#size-cells = <1>;
@@ -225,6 +338,47 @@
interrupts = <10 1>;
reg = <0x3>;
};
+
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -236,6 +390,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
phy-connection-type = "rgmii-id";
};
@@ -249,6 +404,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
phy-connection-type = "rgmii-id";
};
@@ -262,6 +418,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy2>;
phy-connection-type = "rgmii-id";
};
@@ -275,6 +432,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy3>;
phy-connection-type = "rgmii-id";
};
diff --git a/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts b/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts
new file mode 100644
index 000000000000..c114c4ee9931
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc8572ds_camp_core0.dts
@@ -0,0 +1,483 @@
+/*
+ * MPC8572 DS Core0 Device Tree Source in CAMP mode.
+ *
+ * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache
+ * can be shared, all the other devices must be assigned to one core only.
+ * This dts file allows core0 to have memory, l2, i2c, dma1, global-util, eth0,
+ * eth1, crypto, pci0, pci1.
+ *
+ * Copyright 2007, 2008 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.
+ */
+
+/dts-v1/;
+/ {
+ model = "fsl,MPC8572DS";
+ compatible = "fsl,MPC8572DS", "fsl,MPC8572DS-CAMP";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ aliases {
+ ethernet0 = &enet0;
+ ethernet1 = &enet1;
+ serial0 = &serial0;
+ pci0 = &pci0;
+ pci1 = &pci1;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8572@0 {
+ device_type = "cpu";
+ reg = <0x0>;
+ d-cache-line-size = <32>; // 32 bytes
+ i-cache-line-size = <32>; // 32 bytes
+ d-cache-size = <0x8000>; // L1, 32K
+ i-cache-size = <0x8000>; // L1, 32K
+ timebase-frequency = <0>;
+ bus-frequency = <0>;
+ clock-frequency = <0>;
+ next-level-cache = <&L2>;
+ };
+
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x0>; // Filled by U-Boot
+ };
+
+ soc8572@ffe00000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ compatible = "simple-bus";
+ ranges = <0x0 0xffe00000 0x100000>;
+ reg = <0xffe00000 0x1000>; // CCSRBAR & soc regs, remove once parse code for immrbase fixed
+ bus-frequency = <0>; // Filled out by uboot.
+
+ memory-controller@2000 {
+ compatible = "fsl,mpc8572-memory-controller";
+ reg = <0x2000 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <18 2>;
+ };
+
+ memory-controller@6000 {
+ compatible = "fsl,mpc8572-memory-controller";
+ reg = <0x6000 0x1000>;
+ interrupt-parent = <&mpic>;
+ interrupts = <18 2>;
+ };
+
+ L2: l2-cache-controller@20000 {
+ compatible = "fsl,mpc8572-l2-cache-controller";
+ reg = <0x20000 0x1000>;
+ cache-line-size = <32>; // 32 bytes
+ cache-size = <0x80000>; // L2, 512K
+ interrupt-parent = <&mpic>;
+ interrupts = <16 2>;
+ };
+
+ i2c@3000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ compatible = "fsl-i2c";
+ reg = <0x3000 0x100>;
+ interrupts = <43 2>;
+ interrupt-parent = <&mpic>;
+ dfsrr;
+ };
+
+ i2c@3100 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <1>;
+ compatible = "fsl-i2c";
+ reg = <0x3100 0x100>;
+ interrupts = <43 2>;
+ interrupt-parent = <&mpic>;
+ dfsrr;
+ };
+
+ dma@21300 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8572-dma", "fsl,eloplus-dma";
+ reg = <0x21300 0x4>;
+ ranges = <0x0 0x21100 0x200>;
+ cell-index = <0>;
+ dma-channel@0 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x0 0x80>;
+ cell-index = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <20 2>;
+ };
+ dma-channel@80 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x80 0x80>;
+ cell-index = <1>;
+ interrupt-parent = <&mpic>;
+ interrupts = <21 2>;
+ };
+ dma-channel@100 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x100 0x80>;
+ cell-index = <2>;
+ interrupt-parent = <&mpic>;
+ interrupts = <22 2>;
+ };
+ dma-channel@180 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x180 0x80>;
+ cell-index = <3>;
+ interrupt-parent = <&mpic>;
+ interrupts = <23 2>;
+ };
+ };
+
+ mdio@24520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-mdio";
+ reg = <0x24520 0x20>;
+
+ phy0: ethernet-phy@0 {
+ interrupt-parent = <&mpic>;
+ interrupts = <10 1>;
+ reg = <0x0>;
+ };
+ phy1: ethernet-phy@1 {
+ interrupt-parent = <&mpic>;
+ interrupts = <10 1>;
+ reg = <0x1>;
+ };
+ };
+
+ enet0: ethernet@24000 {
+ cell-index = <0>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "gianfar";
+ reg = <0x24000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <29 2 30 2 34 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy0>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ enet1: ethernet@25000 {
+ cell-index = <1>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "gianfar";
+ reg = <0x25000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <35 2 36 2 40 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy1>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ serial0: serial@4500 {
+ cell-index = <0>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4500 0x100>;
+ clock-frequency = <0>;
+ };
+
+ global-utilities@e0000 { //global utilities block
+ compatible = "fsl,mpc8572-guts";
+ reg = <0xe0000 0x1000>;
+ fsl,has-rstcr;
+ };
+
+ crypto@30000 {
+ compatible = "fsl,sec3.0", "fsl,sec2.4", "fsl,sec2.2",
+ "fsl,sec2.1", "fsl,sec2.0";
+ reg = <0x30000 0x10000>;
+ interrupts = <45 2 58 2>;
+ interrupt-parent = <&mpic>;
+ fsl,num-channels = <4>;
+ fsl,channel-fifo-len = <24>;
+ fsl,exec-units-mask = <0x9fe>;
+ fsl,descriptor-types-mask = <0x3ab0ebf>;
+ };
+
+ mpic: pic@40000 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x40000 0x40000>;
+ compatible = "chrp,open-pic";
+ device_type = "open-pic";
+ protected-sources = <
+ 31 32 33 37 38 39 /* enet2 enet3 */
+ 76 77 78 79 27 42 /* dma2 pci2 serial*/
+ 0xe0 0xe1 0xe2 0xe3 /* msi */
+ 0xe4 0xe5 0xe6 0xe7
+ >;
+ };
+ };
+
+ pci0: pcie@ffe08000 {
+ cell-index = <0>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe08000 0x1000>;
+ bus-range = <0 255>;
+ ranges = <0x2000000 0x0 0x80000000 0x80000000 0x0 0x20000000
+ 0x1000000 0x0 0x0 0xffc00000 0x0 0x10000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <24 2>;
+ interrupt-map-mask = <0xff00 0x0 0x0 0x7>;
+ interrupt-map = <
+ /* IDSEL 0x11 func 0 - PCI slot 1 */
+ 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 1 - PCI slot 1 */
+ 0x8900 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8900 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8900 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8900 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 2 - PCI slot 1 */
+ 0x8a00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8a00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8a00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8a00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 3 - PCI slot 1 */
+ 0x8b00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8b00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8b00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8b00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 4 - PCI slot 1 */
+ 0x8c00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8c00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8c00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8c00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 5 - PCI slot 1 */
+ 0x8d00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8d00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8d00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8d00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 6 - PCI slot 1 */
+ 0x8e00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8e00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8e00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8e00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x11 func 7 - PCI slot 1 */
+ 0x8f00 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8f00 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8f00 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8f00 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x12 func 0 - PCI slot 2 */
+ 0x9000 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9000 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9000 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9000 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 1 - PCI slot 2 */
+ 0x9100 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9100 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9100 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9100 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 2 - PCI slot 2 */
+ 0x9200 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9200 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9200 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9200 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 3 - PCI slot 2 */
+ 0x9300 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9300 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9300 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9300 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 4 - PCI slot 2 */
+ 0x9400 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9400 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9400 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9400 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 5 - PCI slot 2 */
+ 0x9500 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9500 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9500 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9500 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 6 - PCI slot 2 */
+ 0x9600 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9600 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9600 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9600 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ /* IDSEL 0x12 func 7 - PCI slot 2 */
+ 0x9700 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9700 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9700 0x0 0x0 0x3 &mpic 0x1 0x1
+ 0x9700 0x0 0x0 0x4 &mpic 0x2 0x1
+
+ // IDSEL 0x1c USB
+ 0xe000 0x0 0x0 0x1 &i8259 0xc 0x2
+ 0xe100 0x0 0x0 0x2 &i8259 0x9 0x2
+ 0xe200 0x0 0x0 0x3 &i8259 0xa 0x2
+ 0xe300 0x0 0x0 0x4 &i8259 0xb 0x2
+
+ // IDSEL 0x1d Audio
+ 0xe800 0x0 0x0 0x1 &i8259 0x6 0x2
+
+ // IDSEL 0x1e Legacy
+ 0xf000 0x0 0x0 0x1 &i8259 0x7 0x2
+ 0xf100 0x0 0x0 0x1 &i8259 0x7 0x2
+
+ // IDSEL 0x1f IDE/SATA
+ 0xf800 0x0 0x0 0x1 &i8259 0xe 0x2
+ 0xf900 0x0 0x0 0x1 &i8259 0x5 0x2
+
+ >;
+
+ pcie@0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x2000000 0x0 0x80000000
+ 0x2000000 0x0 0x80000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x100000>;
+ uli1575@0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ ranges = <0x2000000 0x0 0x80000000
+ 0x2000000 0x0 0x80000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x100000>;
+ isa@1e {
+ device_type = "isa";
+ #interrupt-cells = <2>;
+ #size-cells = <1>;
+ #address-cells = <2>;
+ reg = <0xf000 0x0 0x0 0x0 0x0>;
+ ranges = <0x1 0x0 0x1000000 0x0 0x0
+ 0x1000>;
+ interrupt-parent = <&i8259>;
+
+ i8259: interrupt-controller@20 {
+ reg = <0x1 0x20 0x2
+ 0x1 0xa0 0x2
+ 0x1 0x4d0 0x2>;
+ interrupt-controller;
+ device_type = "interrupt-controller";
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ compatible = "chrp,iic";
+ interrupts = <9 2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ i8042@60 {
+ #size-cells = <0>;
+ #address-cells = <1>;
+ reg = <0x1 0x60 0x1 0x1 0x64 0x1>;
+ interrupts = <1 3 12 3>;
+ interrupt-parent =
+ <&i8259>;
+
+ keyboard@0 {
+ reg = <0x0>;
+ compatible = "pnpPNP,303";
+ };
+
+ mouse@1 {
+ reg = <0x1>;
+ compatible = "pnpPNP,f03";
+ };
+ };
+
+ rtc@70 {
+ compatible = "pnpPNP,b00";
+ reg = <0x1 0x70 0x2>;
+ };
+
+ gpio@400 {
+ reg = <0x1 0x400 0x80>;
+ };
+ };
+ };
+ };
+
+ };
+
+ pci1: pcie@ffe09000 {
+ cell-index = <1>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe09000 0x1000>;
+ bus-range = <0 255>;
+ ranges = <0x2000000 0x0 0xa0000000 0xa0000000 0x0 0x20000000
+ 0x1000000 0x0 0x0 0xffc10000 0x0 0x10000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <26 2>;
+ interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0x0 0x0 0x1 &mpic 0x4 0x1
+ 0000 0x0 0x0 0x2 &mpic 0x5 0x1
+ 0000 0x0 0x0 0x3 &mpic 0x6 0x1
+ 0000 0x0 0x0 0x4 &mpic 0x7 0x1
+ >;
+ pcie@0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x2000000 0x0 0xa0000000
+ 0x2000000 0x0 0xa0000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x100000>;
+ };
+ };
+};
diff --git a/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts b/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts
new file mode 100644
index 000000000000..04ecda18d206
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc8572ds_camp_core1.dts
@@ -0,0 +1,234 @@
+/*
+ * MPC8572 DS Core1 Device Tree Source in CAMP mode.
+ *
+ * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache
+ * can be shared, all the other devices must be assigned to one core only.
+ * This dts allows core1 to have l2, dma2, eth2, eth3, pci2, msi.
+ *
+ * Please note to add "-b 1" for core1's dts compiling.
+ *
+ * Copyright 2007, 2008 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.
+ */
+
+/dts-v1/;
+/ {
+ model = "fsl,MPC8572DS";
+ compatible = "fsl,MPC8572DS", "fsl,MPC8572DS-CAMP";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ aliases {
+ ethernet2 = &enet2;
+ ethernet3 = &enet3;
+ serial0 = &serial0;
+ pci2 = &pci2;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8572@1 {
+ device_type = "cpu";
+ reg = <0x1>;
+ d-cache-line-size = <32>; // 32 bytes
+ i-cache-line-size = <32>; // 32 bytes
+ d-cache-size = <0x8000>; // L1, 32K
+ i-cache-size = <0x8000>; // L1, 32K
+ timebase-frequency = <0>;
+ bus-frequency = <0>;
+ clock-frequency = <0>;
+ next-level-cache = <&L2>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x0>; // Filled by U-Boot
+ };
+
+ soc8572@ffe00000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ compatible = "simple-bus";
+ ranges = <0x0 0xffe00000 0x100000>;
+ reg = <0xffe00000 0x1000>; // CCSRBAR & soc regs, remove once parse code for immrbase fixed
+ bus-frequency = <0>; // Filled out by uboot.
+
+ L2: l2-cache-controller@20000 {
+ compatible = "fsl,mpc8572-l2-cache-controller";
+ reg = <0x20000 0x1000>;
+ cache-line-size = <32>; // 32 bytes
+ cache-size = <0x80000>; // L2, 512K
+ interrupt-parent = <&mpic>;
+ };
+
+ dma@c300 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "fsl,mpc8572-dma", "fsl,eloplus-dma";
+ reg = <0xc300 0x4>;
+ ranges = <0x0 0xc100 0x200>;
+ cell-index = <0>;
+ dma-channel@0 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x0 0x80>;
+ cell-index = <0>;
+ interrupt-parent = <&mpic>;
+ interrupts = <76 2>;
+ };
+ dma-channel@80 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x80 0x80>;
+ cell-index = <1>;
+ interrupt-parent = <&mpic>;
+ interrupts = <77 2>;
+ };
+ dma-channel@100 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x100 0x80>;
+ cell-index = <2>;
+ interrupt-parent = <&mpic>;
+ interrupts = <78 2>;
+ };
+ dma-channel@180 {
+ compatible = "fsl,mpc8572-dma-channel",
+ "fsl,eloplus-dma-channel";
+ reg = <0x180 0x80>;
+ cell-index = <3>;
+ interrupt-parent = <&mpic>;
+ interrupts = <79 2>;
+ };
+ };
+
+ mdio@24520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-mdio";
+ reg = <0x24520 0x20>;
+
+ phy2: ethernet-phy@2 {
+ interrupt-parent = <&mpic>;
+ reg = <0x2>;
+ };
+ phy3: ethernet-phy@3 {
+ interrupt-parent = <&mpic>;
+ reg = <0x3>;
+ };
+ };
+
+ enet2: ethernet@26000 {
+ cell-index = <2>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "gianfar";
+ reg = <0x26000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <31 2 32 2 33 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy2>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ enet3: ethernet@27000 {
+ cell-index = <3>;
+ device_type = "network";
+ model = "eTSEC";
+ compatible = "gianfar";
+ reg = <0x27000 0x1000>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ interrupts = <37 2 38 2 39 2>;
+ interrupt-parent = <&mpic>;
+ phy-handle = <&phy3>;
+ phy-connection-type = "rgmii-id";
+ };
+
+ msi@41600 {
+ compatible = "fsl,mpc8572-msi", "fsl,mpic-msi";
+ reg = <0x41600 0x80>;
+ msi-available-ranges = <0 0x100>;
+ interrupts = <
+ 0xe0 0
+ 0xe1 0
+ 0xe2 0
+ 0xe3 0
+ 0xe4 0
+ 0xe5 0
+ 0xe6 0
+ 0xe7 0>;
+ interrupt-parent = <&mpic>;
+ };
+
+ serial0: serial@4600 {
+ cell-index = <1>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4600 0x100>;
+ clock-frequency = <0>;
+ };
+
+ mpic: pic@40000 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x40000 0x40000>;
+ compatible = "chrp,open-pic";
+ device_type = "open-pic";
+ protected-sources = <
+ 18 16 10 42 45 58 /* MEM L2 mdio serial crypto */
+ 29 30 34 35 36 40 /* enet0 enet1 */
+ 24 26 20 21 22 23 /* pcie0 pcie1 dma1 */
+ 43 /* i2c */
+ 0x1 0x2 0x3 0x4 /* pci slot */
+ 0x9 0xa 0xb 0xc /* usb */
+ 0x6 0x7 0xe 0x5 /* Audio elgacy SATA */
+ >;
+ };
+ };
+
+ pci2: pcie@ffe0a000 {
+ cell-index = <2>;
+ compatible = "fsl,mpc8548-pcie";
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xffe0a000 0x1000>;
+ bus-range = <0 255>;
+ ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000
+ 0x1000000 0x0 0x0 0xffc20000 0x0 0x10000>;
+ clock-frequency = <33333333>;
+ interrupt-parent = <&mpic>;
+ interrupts = <27 2>;
+ interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+ interrupt-map = <
+ /* IDSEL 0x0 */
+ 0000 0x0 0x0 0x1 &mpic 0x0 0x1
+ 0000 0x0 0x0 0x2 &mpic 0x1 0x1
+ 0000 0x0 0x0 0x3 &mpic 0x2 0x1
+ 0000 0x0 0x0 0x4 &mpic 0x3 0x1
+ >;
+ pcie@0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ device_type = "pci";
+ ranges = <0x2000000 0x0 0xc0000000
+ 0x2000000 0x0 0xc0000000
+ 0x0 0x20000000
+
+ 0x1000000 0x0 0x0
+ 0x1000000 0x0 0x0
+ 0x0 0x100000>;
+ };
+ };
+};
diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts
index d665e767822a..35d5e248ccd7 100644
--- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts
+++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts
@@ -205,8 +205,49 @@
reg = <3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
+
enet0: ethernet@24000 {
cell-index = <0>;
device_type = "network";
@@ -216,6 +257,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
phy-connection-type = "rgmii-id";
};
@@ -229,6 +271,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
phy-connection-type = "rgmii-id";
};
@@ -242,6 +285,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy2>;
phy-connection-type = "rgmii-id";
};
@@ -255,6 +299,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy3>;
phy-connection-type = "rgmii-id";
};
diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts
index 7c1bb952360c..be2c11ca0594 100644
--- a/arch/powerpc/boot/dts/pcm030.dts
+++ b/arch/powerpc/boot/dts/pcm030.dts
@@ -143,7 +143,6 @@
rtc@800 { // Real time clock
compatible = "fsl,mpc5200b-rtc","fsl,mpc5200-rtc";
- device_type = "rtc";
reg = <0x800 0x100>;
interrupts = <0x1 0x5 0x0 0x1 0x6 0x0>;
interrupt-parent = <&mpc5200_pic>;
@@ -301,7 +300,6 @@
interrupt-parent = <&mpc5200_pic>;
fsl5200-clocking;
rtc@51 {
- device_type = "rtc";
compatible = "nxp,pcf8563";
reg = <0x51>;
};
diff --git a/arch/powerpc/boot/dts/sbc8349.dts b/arch/powerpc/boot/dts/sbc8349.dts
index 0f941f310e44..8d365a57ebc1 100644
--- a/arch/powerpc/boot/dts/sbc8349.dts
+++ b/arch/powerpc/boot/dts/sbc8349.dts
@@ -177,6 +177,22 @@
reg = <0x1a>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -188,6 +204,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <32 0x8 33 0x8 34 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
linux,network-index = <0>;
};
@@ -201,6 +218,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 0x8 36 0x8 37 0x8>;
interrupt-parent = <&ipic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
linux,network-index = <1>;
};
diff --git a/arch/powerpc/boot/dts/sbc8548.dts b/arch/powerpc/boot/dts/sbc8548.dts
index 333552b4e90d..2baf4a51f224 100644
--- a/arch/powerpc/boot/dts/sbc8548.dts
+++ b/arch/powerpc/boot/dts/sbc8548.dts
@@ -252,6 +252,22 @@
reg = <0x1a>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -263,6 +279,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -275,6 +292,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x23 0x2 0x24 0x2 0x28 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/sbc8560.dts b/arch/powerpc/boot/dts/sbc8560.dts
index db3632ef9888..01542f7062ab 100644
--- a/arch/powerpc/boot/dts/sbc8560.dts
+++ b/arch/powerpc/boot/dts/sbc8560.dts
@@ -168,6 +168,22 @@
reg = <0x1c>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -179,6 +195,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
};
@@ -191,6 +208,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <0x23 0x2 0x24 0x2 0x28 0x2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/sbc8641d.dts b/arch/powerpc/boot/dts/sbc8641d.dts
index 9652456158fb..36db981548e4 100644
--- a/arch/powerpc/boot/dts/sbc8641d.dts
+++ b/arch/powerpc/boot/dts/sbc8641d.dts
@@ -222,6 +222,46 @@
reg = <2>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -233,6 +273,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy0>;
phy-connection-type = "rgmii-id";
};
@@ -246,6 +287,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
phy-connection-type = "rgmii-id";
};
@@ -259,6 +301,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy2>;
phy-connection-type = "rgmii-id";
};
@@ -272,6 +315,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy3>;
phy-connection-type = "rgmii-id";
};
diff --git a/arch/powerpc/boot/dts/stx_gp3_8560.dts b/arch/powerpc/boot/dts/stx_gp3_8560.dts
index fcd1db6ca0a8..fff33fe6efc6 100644
--- a/arch/powerpc/boot/dts/stx_gp3_8560.dts
+++ b/arch/powerpc/boot/dts/stx_gp3_8560.dts
@@ -142,6 +142,22 @@
reg = <4>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -153,6 +169,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -165,6 +182,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy4>;
};
diff --git a/arch/powerpc/boot/dts/tqm5200.dts b/arch/powerpc/boot/dts/tqm5200.dts
index 3008bf8830c1..906302e26a62 100644
--- a/arch/powerpc/boot/dts/tqm5200.dts
+++ b/arch/powerpc/boot/dts/tqm5200.dts
@@ -181,7 +181,6 @@
fsl5200-clocking;
rtc@68 {
- device_type = "rtc";
compatible = "dallas,ds1307";
reg = <0x68>;
};
diff --git a/arch/powerpc/boot/dts/tqm8540.dts b/arch/powerpc/boot/dts/tqm8540.dts
index e1d260b9085e..a693f01c21aa 100644
--- a/arch/powerpc/boot/dts/tqm8540.dts
+++ b/arch/powerpc/boot/dts/tqm8540.dts
@@ -155,6 +155,34 @@
reg = <3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
diff --git a/arch/powerpc/boot/dts/tqm8541.dts b/arch/powerpc/boot/dts/tqm8541.dts
index d76441ec5dc7..9e3f5f0dde20 100644
--- a/arch/powerpc/boot/dts/tqm8541.dts
+++ b/arch/powerpc/boot/dts/tqm8541.dts
@@ -154,6 +154,22 @@
reg = <3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -165,6 +181,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -177,6 +194,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/tqm8548-bigflash.dts b/arch/powerpc/boot/dts/tqm8548-bigflash.dts
index 4199e89b4e50..15086eb65c50 100644
--- a/arch/powerpc/boot/dts/tqm8548-bigflash.dts
+++ b/arch/powerpc/boot/dts/tqm8548-bigflash.dts
@@ -179,6 +179,46 @@
reg = <5>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -190,6 +230,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -202,6 +243,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
@@ -214,6 +256,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy3>;
};
@@ -226,6 +269,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy4>;
};
diff --git a/arch/powerpc/boot/dts/tqm8548.dts b/arch/powerpc/boot/dts/tqm8548.dts
index 58ee4185454b..b7b65f5e79b6 100644
--- a/arch/powerpc/boot/dts/tqm8548.dts
+++ b/arch/powerpc/boot/dts/tqm8548.dts
@@ -179,6 +179,46 @@
reg = <5>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@26520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x26520 0x20>;
+
+ tbi2: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@27520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x27520 0x20>;
+
+ tbi3: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -190,6 +230,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -202,6 +243,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
@@ -214,6 +256,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <31 2 32 2 33 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi2>;
phy-handle = <&phy3>;
};
@@ -226,6 +269,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <37 2 38 2 39 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi3>;
phy-handle = <&phy4>;
};
diff --git a/arch/powerpc/boot/dts/tqm8555.dts b/arch/powerpc/boot/dts/tqm8555.dts
index 6f7ea59c4846..cf92b4e7945e 100644
--- a/arch/powerpc/boot/dts/tqm8555.dts
+++ b/arch/powerpc/boot/dts/tqm8555.dts
@@ -154,6 +154,22 @@
reg = <3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -165,6 +181,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -177,6 +194,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/dts/tqm8560.dts b/arch/powerpc/boot/dts/tqm8560.dts
index 3fe35208907b..9e1ab2d2f669 100644
--- a/arch/powerpc/boot/dts/tqm8560.dts
+++ b/arch/powerpc/boot/dts/tqm8560.dts
@@ -156,6 +156,22 @@
reg = <3>;
device_type = "ethernet-phy";
};
+ tbi0: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
+ };
+
+ mdio@25520 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,gianfar-tbi";
+ reg = <0x25520 0x20>;
+
+ tbi1: tbi-phy@11 {
+ reg = <0x11>;
+ device_type = "tbi-phy";
+ };
};
enet0: ethernet@24000 {
@@ -167,6 +183,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <29 2 30 2 34 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi0>;
phy-handle = <&phy2>;
};
@@ -179,6 +196,7 @@
local-mac-address = [ 00 00 00 00 00 00 ];
interrupts = <35 2 36 2 40 2>;
interrupt-parent = <&mpic>;
+ tbi-handle = <&tbi1>;
phy-handle = <&phy1>;
};
diff --git a/arch/powerpc/boot/libfdt-wrapper.c b/arch/powerpc/boot/libfdt-wrapper.c
index 9276327bc2bb..bb8b9b3505ee 100644
--- a/arch/powerpc/boot/libfdt-wrapper.c
+++ b/arch/powerpc/boot/libfdt-wrapper.c
@@ -185,7 +185,7 @@ void fdt_init(void *blob)
/* Make sure the dt blob is the right version and so forth */
fdt = blob;
- bufsize = fdt_totalsize(fdt) + 4;
+ bufsize = fdt_totalsize(fdt) + EXPAND_GRANULARITY;
buf = malloc(bufsize);
if(!buf)
fatal("malloc failed. can't relocate the device tree\n\r");
diff --git a/arch/powerpc/configs/86xx/gef_sbc610_defconfig b/arch/powerpc/configs/86xx/gef_sbc610_defconfig
index 07ccaf89f379..cd1ffa449327 100644
--- a/arch/powerpc/configs/86xx/gef_sbc610_defconfig
+++ b/arch/powerpc/configs/86xx/gef_sbc610_defconfig
@@ -1397,8 +1397,11 @@ CONFIG_USB_STORAGE=y
# CONFIG_ACCESSIBILITY is not set
# CONFIG_INFINIBAND is not set
# CONFIG_EDAC is not set
-CONFIG_RTC_LIB=m
-CONFIG_RTC_CLASS=m
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
#
# RTC interfaces
@@ -1424,6 +1427,7 @@ CONFIG_RTC_INTF_DEV=y
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
+CONFIG_RTC_DRV_RX8581=y
#
# SPI RTC drivers
diff --git a/arch/powerpc/configs/ppc44x_defconfig b/arch/powerpc/configs/ppc44x_defconfig
index cfc94cfcf4cb..034a1fbdc887 100644
--- a/arch/powerpc/configs/ppc44x_defconfig
+++ b/arch/powerpc/configs/ppc44x_defconfig
@@ -267,7 +267,7 @@ CONFIG_PCI_SYSCALL=y
# CONFIG_PCIEPORTBUS is not set
CONFIG_ARCH_SUPPORTS_MSI=y
# CONFIG_PCI_MSI is not set
-CONFIG_PCI_LEGACY=y
+# CONFIG_PCI_LEGACY is not set
# CONFIG_PCI_DEBUG is not set
# CONFIG_PCCARD is not set
# CONFIG_HOTPLUG_PCI is not set
@@ -354,7 +354,7 @@ CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_IP_SCTP is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
-# CONFIG_BRIDGE is not set
+CONFIG_BRIDGE=m
# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
@@ -579,7 +579,7 @@ CONFIG_NETDEVICES=y
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
-# CONFIG_TUN is not set
+CONFIG_TUN=m
# CONFIG_VETH is not set
# CONFIG_ARCNET is not set
# CONFIG_PHYLIB is not set
@@ -1001,11 +1001,11 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y
# CONFIG_USB_TMC is not set
#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
#
#
-# may also be needed; see USB_STORAGE Help for more information
+# see USB_STORAGE Help for more information
#
CONFIG_USB_STORAGE=m
# CONFIG_USB_STORAGE_DEBUG is not set
@@ -1418,6 +1418,6 @@ CONFIG_CRYPTO_LZO=m
# CONFIG_PPC_CLOCK is not set
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=y
-CONFIG_KVM_BOOKE_HOST=y
+CONFIG_KVM_440=y
# CONFIG_VIRTIO_PCI is not set
# CONFIG_VIRTIO_BALLOON is not set
diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h
index f3fc733758f5..499be5bdd6fa 100644
--- a/arch/powerpc/include/asm/atomic.h
+++ b/arch/powerpc/include/asm/atomic.h
@@ -111,7 +111,7 @@ static __inline__ void atomic_inc(atomic_t *v)
bne- 1b"
: "=&r" (t), "+m" (v->counter)
: "r" (&v->counter)
- : "cc");
+ : "cc", "xer");
}
static __inline__ int atomic_inc_return(atomic_t *v)
@@ -128,7 +128,7 @@ static __inline__ int atomic_inc_return(atomic_t *v)
ISYNC_ON_SMP
: "=&r" (t)
: "r" (&v->counter)
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
@@ -155,7 +155,7 @@ static __inline__ void atomic_dec(atomic_t *v)
bne- 1b"
: "=&r" (t), "+m" (v->counter)
: "r" (&v->counter)
- : "cc");
+ : "cc", "xer");
}
static __inline__ int atomic_dec_return(atomic_t *v)
@@ -172,7 +172,7 @@ static __inline__ int atomic_dec_return(atomic_t *v)
ISYNC_ON_SMP
: "=&r" (t)
: "r" (&v->counter)
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
@@ -346,7 +346,7 @@ static __inline__ void atomic64_inc(atomic64_t *v)
bne- 1b"
: "=&r" (t), "+m" (v->counter)
: "r" (&v->counter)
- : "cc");
+ : "cc", "xer");
}
static __inline__ long atomic64_inc_return(atomic64_t *v)
@@ -362,7 +362,7 @@ static __inline__ long atomic64_inc_return(atomic64_t *v)
ISYNC_ON_SMP
: "=&r" (t)
: "r" (&v->counter)
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
@@ -388,7 +388,7 @@ static __inline__ void atomic64_dec(atomic64_t *v)
bne- 1b"
: "=&r" (t), "+m" (v->counter)
: "r" (&v->counter)
- : "cc");
+ : "cc", "xer");
}
static __inline__ long atomic64_dec_return(atomic64_t *v)
@@ -404,7 +404,7 @@ static __inline__ long atomic64_dec_return(atomic64_t *v)
ISYNC_ON_SMP
: "=&r" (t)
: "r" (&v->counter)
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
@@ -431,7 +431,7 @@ static __inline__ long atomic64_dec_if_positive(atomic64_t *v)
"\n\
2:" : "=&r" (t)
: "r" (&v->counter)
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index e55d1f66b86f..64e1fdca233e 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -3,6 +3,7 @@
#ifdef __KERNEL__
#include <asm/asm-compat.h>
+
/*
* Define an illegal instr to trap on the bug.
* We don't use 0 because that marks the end of a function
@@ -14,6 +15,7 @@
#ifdef CONFIG_BUG
#ifdef __ASSEMBLY__
+#include <asm/asm-offsets.h>
#ifdef CONFIG_DEBUG_BUGVERBOSE
.macro EMIT_BUG_ENTRY addr,file,line,flags
.section __bug_table,"a"
@@ -26,7 +28,7 @@
.previous
.endm
#else
- .macro EMIT_BUG_ENTRY addr,file,line,flags
+.macro EMIT_BUG_ENTRY addr,file,line,flags
.section __bug_table,"a"
5001: PPC_LONG \addr
.short \flags
@@ -113,6 +115,13 @@
#define HAVE_ARCH_BUG_ON
#define HAVE_ARCH_WARN_ON
#endif /* __ASSEMBLY __ */
+#else
+#ifdef __ASSEMBLY__
+.macro EMIT_BUG_ENTRY addr,file,line,flags
+.endm
+#else /* !__ASSEMBLY__ */
+#define _EMIT_BUG_ENTRY
+#endif
#endif /* CONFIG_BUG */
#include <asm-generic/bug.h>
diff --git a/arch/powerpc/include/asm/byteorder.h b/arch/powerpc/include/asm/byteorder.h
index b37752214a16..d5de325472e9 100644
--- a/arch/powerpc/include/asm/byteorder.h
+++ b/arch/powerpc/include/asm/byteorder.h
@@ -11,6 +11,8 @@
#include <asm/types.h>
#include <linux/compiler.h>
+#define __BIG_ENDIAN
+
#ifdef __GNUC__
#ifdef __KERNEL__
@@ -21,12 +23,19 @@ static __inline__ __u16 ld_le16(const volatile __u16 *addr)
__asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr));
return val;
}
+#define __arch_swab16p ld_le16
static __inline__ void st_le16(volatile __u16 *addr, const __u16 val)
{
__asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
}
+static inline void __arch_swab16s(__u16 *addr)
+{
+ st_le16(addr, *addr);
+}
+#define __arch_swab16s __arch_swab16s
+
static __inline__ __u32 ld_le32(const volatile __u32 *addr)
{
__u32 val;
@@ -34,13 +43,20 @@ static __inline__ __u32 ld_le32(const volatile __u32 *addr)
__asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (addr), "m" (*addr));
return val;
}
+#define __arch_swab32p ld_le32
static __inline__ void st_le32(volatile __u32 *addr, const __u32 val)
{
__asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (addr));
}
-static __inline__ __attribute_const__ __u16 ___arch__swab16(__u16 value)
+static inline void __arch_swab32s(__u32 *addr)
+{
+ st_le32(addr, *addr);
+}
+#define __arch_swab32s __arch_swab32s
+
+static inline __attribute_const__ __u16 __arch_swab16(__u16 value)
{
__u16 result;
@@ -49,8 +65,9 @@ static __inline__ __attribute_const__ __u16 ___arch__swab16(__u16 value)
: "r" (value), "0" (value >> 8));
return result;
}
+#define __arch_swab16 __arch_swab16
-static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 value)
+static inline __attribute_const__ __u32 __arch_swab32(__u32 value)
{
__u32 result;
@@ -61,29 +78,16 @@ static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 value)
: "r" (value), "0" (value >> 24));
return result;
}
-
-#define __arch__swab16(x) ___arch__swab16(x)
-#define __arch__swab32(x) ___arch__swab32(x)
-
-/* The same, but returns converted value from the location pointer by addr. */
-#define __arch__swab16p(addr) ld_le16(addr)
-#define __arch__swab32p(addr) ld_le32(addr)
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-#define __arch__swab16s(addr) st_le16(addr,*addr)
-#define __arch__swab32s(addr) st_le32(addr,*addr)
+#define __arch_swab32 __arch_swab32
#endif /* __KERNEL__ */
-#ifndef __STRICT_ANSI__
-#define __BYTEORDER_HAS_U64__
#ifndef __powerpc64__
#define __SWAB_64_THRU_32__
#endif /* __powerpc64__ */
-#endif /* __STRICT_ANSI__ */
#endif /* __GNUC__ */
-#include <linux/byteorder/big_endian.h>
+#include <linux/byteorder.h>
#endif /* _ASM_POWERPC_BYTEORDER_H */
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index 1e94b07a020e..4911104791c3 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -82,6 +82,7 @@ struct cpu_spec {
char *cpu_name;
unsigned long cpu_features; /* Kernel features */
unsigned int cpu_user_features; /* Userland features */
+ unsigned int mmu_features; /* MMU features */
/* cache line sizes */
unsigned int icache_bsize;
@@ -144,17 +145,14 @@ extern const char *powerpc_base_platform;
#define CPU_FTR_USE_TB ASM_CONST(0x0000000000000040)
#define CPU_FTR_L2CSR ASM_CONST(0x0000000000000080)
#define CPU_FTR_601 ASM_CONST(0x0000000000000100)
-#define CPU_FTR_HPTE_TABLE ASM_CONST(0x0000000000000200)
#define CPU_FTR_CAN_NAP ASM_CONST(0x0000000000000400)
#define CPU_FTR_L3CR ASM_CONST(0x0000000000000800)
#define CPU_FTR_L3_DISABLE_NAP ASM_CONST(0x0000000000001000)
#define CPU_FTR_NAP_DISABLE_L2_PR ASM_CONST(0x0000000000002000)
#define CPU_FTR_DUAL_PLL_750FX ASM_CONST(0x0000000000004000)
#define CPU_FTR_NO_DPM ASM_CONST(0x0000000000008000)
-#define CPU_FTR_HAS_HIGH_BATS ASM_CONST(0x0000000000010000)
#define CPU_FTR_NEED_COHERENT ASM_CONST(0x0000000000020000)
#define CPU_FTR_NO_BTIC ASM_CONST(0x0000000000040000)
-#define CPU_FTR_BIG_PHYS ASM_CONST(0x0000000000080000)
#define CPU_FTR_NODSISRALIGN ASM_CONST(0x0000000000100000)
#define CPU_FTR_PPC_LE ASM_CONST(0x0000000000200000)
#define CPU_FTR_REAL_LE ASM_CONST(0x0000000000400000)
@@ -163,6 +161,8 @@ extern const char *powerpc_base_platform;
#define CPU_FTR_SPE ASM_CONST(0x0000000002000000)
#define CPU_FTR_NEED_PAIRED_STWCX ASM_CONST(0x0000000004000000)
#define CPU_FTR_LWSYNC ASM_CONST(0x0000000008000000)
+#define CPU_FTR_NOEXECUTE ASM_CONST(0x0000000010000000)
+#define CPU_FTR_INDEXED_DCR ASM_CONST(0x0000000020000000)
/*
* Add the 64-bit processor unique features in the top half of the word;
@@ -177,7 +177,6 @@ extern const char *powerpc_base_platform;
#define CPU_FTR_SLB LONG_ASM_CONST(0x0000000100000000)
#define CPU_FTR_16M_PAGE LONG_ASM_CONST(0x0000000200000000)
#define CPU_FTR_TLBIEL LONG_ASM_CONST(0x0000000400000000)
-#define CPU_FTR_NOEXECUTE LONG_ASM_CONST(0x0000000800000000)
#define CPU_FTR_IABR LONG_ASM_CONST(0x0000002000000000)
#define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000004000000000)
#define CPU_FTR_CTRL LONG_ASM_CONST(0x0000008000000000)
@@ -194,6 +193,7 @@ extern const char *powerpc_base_platform;
#define CPU_FTR_VSX LONG_ASM_CONST(0x0010000000000000)
#define CPU_FTR_SAO LONG_ASM_CONST(0x0020000000000000)
#define CPU_FTR_CP_USE_DCBTZ LONG_ASM_CONST(0x0040000000000000)
+#define CPU_FTR_UNALIGNED_LD_STD LONG_ASM_CONST(0x0080000000000000)
#ifndef __ASSEMBLY__
@@ -264,164 +264,159 @@ extern const char *powerpc_base_platform;
!defined(CONFIG_POWER3) && !defined(CONFIG_POWER4) && \
!defined(CONFIG_BOOKE))
-#define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE | \
+#define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | \
CPU_FTR_COHERENT_ICACHE | CPU_FTR_UNIFIED_ID_CACHE)
#define CPU_FTRS_603 (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE)
#define CPU_FTRS_604 (CPU_FTR_COMMON | \
- CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_PPC_LE)
+ CPU_FTR_USE_TB | CPU_FTR_PPC_LE)
#define CPU_FTRS_740_NOTAU (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE)
+ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE)
#define CPU_FTRS_740 (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
- CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
+ CPU_FTR_TAU | CPU_FTR_MAYBE_CAN_NAP | \
CPU_FTR_PPC_LE)
#define CPU_FTRS_750 (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
- CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \
+ CPU_FTR_TAU | CPU_FTR_MAYBE_CAN_NAP | \
CPU_FTR_PPC_LE)
-#define CPU_FTRS_750CL (CPU_FTRS_750 | CPU_FTR_HAS_HIGH_BATS)
+#define CPU_FTRS_750CL (CPU_FTRS_750)
#define CPU_FTRS_750FX1 (CPU_FTRS_750 | CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM)
#define CPU_FTRS_750FX2 (CPU_FTRS_750 | CPU_FTR_NO_DPM)
-#define CPU_FTRS_750FX (CPU_FTRS_750 | CPU_FTR_DUAL_PLL_750FX | \
- CPU_FTR_HAS_HIGH_BATS)
+#define CPU_FTRS_750FX (CPU_FTRS_750 | CPU_FTR_DUAL_PLL_750FX)
#define CPU_FTRS_750GX (CPU_FTRS_750FX)
#define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
- CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \
+ CPU_FTR_ALTIVEC_COMP | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE)
#define CPU_FTRS_7400 (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \
- CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \
+ CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE)
#define CPU_FTRS_7450_20 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | \
CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7450_21 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | \
CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \
CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7450_23 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | CPU_FTR_NEED_PAIRED_STWCX | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | \
CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE)
#define CPU_FTRS_7455_1 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | CPU_FTR_NEED_PAIRED_STWCX | \
CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS | \
- CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE)
+ CPU_FTR_SPEC7450 | CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE)
#define CPU_FTRS_7455_20 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | CPU_FTR_NEED_PAIRED_STWCX | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | \
CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \
- CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE)
+ CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE)
#define CPU_FTRS_7455 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
- CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | \
CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7447_10 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
- CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | \
CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC | CPU_FTR_PPC_LE | \
CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7447 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
- CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_L3CR | CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | \
CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7447A (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
- CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | \
CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_7448 (CPU_FTR_COMMON | \
CPU_FTR_USE_TB | \
CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \
- CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_SPEC7450 | CPU_FTR_NAP_DISABLE_L2_PR | \
CPU_FTR_PPC_LE | CPU_FTR_NEED_PAIRED_STWCX)
#define CPU_FTRS_82XX (CPU_FTR_COMMON | \
CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB)
#define CPU_FTRS_G2_LE (CPU_FTR_COMMON | CPU_FTR_MAYBE_CAN_DOZE | \
- CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS)
+ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP)
#define CPU_FTRS_E300 (CPU_FTR_MAYBE_CAN_DOZE | \
- CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | \
CPU_FTR_COMMON)
#define CPU_FTRS_E300C2 (CPU_FTR_MAYBE_CAN_DOZE | \
- CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_HAS_HIGH_BATS | \
+ CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | \
CPU_FTR_COMMON | CPU_FTR_FPU_UNAVAILABLE)
-#define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | \
- CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE)
+#define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | CPU_FTR_USE_TB)
#define CPU_FTRS_8XX (CPU_FTR_USE_TB)
-#define CPU_FTRS_40X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN)
-#define CPU_FTRS_44X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN)
+#define CPU_FTRS_40X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
+#define CPU_FTRS_44X (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
+#define CPU_FTRS_440x6 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE | \
+ CPU_FTR_INDEXED_DCR)
#define CPU_FTRS_E200 (CPU_FTR_USE_TB | CPU_FTR_SPE_COMP | \
CPU_FTR_NODSISRALIGN | CPU_FTR_COHERENT_ICACHE | \
- CPU_FTR_UNIFIED_ID_CACHE)
+ CPU_FTR_UNIFIED_ID_CACHE | CPU_FTR_NOEXECUTE)
#define CPU_FTRS_E500 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
- CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN)
+ CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \
+ CPU_FTR_NOEXECUTE)
#define CPU_FTRS_E500_2 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
- CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_BIG_PHYS | \
- CPU_FTR_NODSISRALIGN)
+ CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | \
+ CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
#define CPU_FTRS_E500MC (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \
- CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_BIG_PHYS | CPU_FTR_NODSISRALIGN | \
- CPU_FTR_L2CSR | CPU_FTR_LWSYNC)
+ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \
+ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE)
#define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN)
/* 64-bit CPUs */
#define CPU_FTRS_POWER3 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | CPU_FTR_PPC_LE)
+ CPU_FTR_IABR | CPU_FTR_PPC_LE)
#define CPU_FTRS_RS64 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | \
+ CPU_FTR_IABR | \
CPU_FTR_MMCRA | CPU_FTR_CTRL)
#define CPU_FTRS_POWER4 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_MMCRA | CPU_FTR_CP_USE_DCBTZ)
#define CPU_FTRS_PPC970 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_ALTIVEC_COMP | CPU_FTR_CAN_NAP | CPU_FTR_MMCRA | \
CPU_FTR_CP_USE_DCBTZ)
#define CPU_FTRS_POWER5 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_MMCRA | CPU_FTR_SMT | \
CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \
CPU_FTR_PURR)
#define CPU_FTRS_POWER6 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_MMCRA | CPU_FTR_SMT | \
CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \
CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \
- CPU_FTR_DSCR)
+ CPU_FTR_DSCR | CPU_FTR_UNALIGNED_LD_STD)
#define CPU_FTRS_POWER7 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_MMCRA | CPU_FTR_SMT | \
CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \
CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \
CPU_FTR_DSCR | CPU_FTR_SAO)
#define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
+ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \
CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \
CPU_FTR_PAUSE_ZERO | CPU_FTR_CI_LARGE_PAGE | \
- CPU_FTR_CELL_TB_BUG | CPU_FTR_CP_USE_DCBTZ)
+ CPU_FTR_CELL_TB_BUG | CPU_FTR_CP_USE_DCBTZ | \
+ CPU_FTR_UNALIGNED_LD_STD)
#define CPU_FTRS_PA6T (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \
+ CPU_FTR_PPCAS_ARCH_V2 | \
CPU_FTR_ALTIVEC_COMP | CPU_FTR_CI_LARGE_PAGE | \
CPU_FTR_PURR | CPU_FTR_REAL_LE | CPU_FTR_NO_SLBIE_B)
-#define CPU_FTRS_COMPATIBLE (CPU_FTR_USE_TB | \
- CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2)
+#define CPU_FTRS_COMPATIBLE (CPU_FTR_USE_TB | CPU_FTR_PPCAS_ARCH_V2)
#ifdef __powerpc64__
#define CPU_FTRS_POSSIBLE \
@@ -452,7 +447,7 @@ enum {
CPU_FTRS_40X |
#endif
#ifdef CONFIG_44x
- CPU_FTRS_44X |
+ CPU_FTRS_44X | CPU_FTRS_440x6 |
#endif
#ifdef CONFIG_E200
CPU_FTRS_E200 |
@@ -492,7 +487,7 @@ enum {
CPU_FTRS_40X &
#endif
#ifdef CONFIG_44x
- CPU_FTRS_44X &
+ CPU_FTRS_44X & CPU_FTRS_440x6 &
#endif
#ifdef CONFIG_E200
CPU_FTRS_E200 &
diff --git a/arch/powerpc/include/asm/dcr-native.h b/arch/powerpc/include/asm/dcr-native.h
index 72d2b72c7390..7d2e6235726d 100644
--- a/arch/powerpc/include/asm/dcr-native.h
+++ b/arch/powerpc/include/asm/dcr-native.h
@@ -23,6 +23,7 @@
#ifndef __ASSEMBLY__
#include <linux/spinlock.h>
+#include <asm/cputable.h>
typedef struct {
unsigned int base;
@@ -39,23 +40,45 @@ static inline bool dcr_map_ok_native(dcr_host_native_t host)
#define dcr_read_native(host, dcr_n) mfdcr(dcr_n + host.base)
#define dcr_write_native(host, dcr_n, value) mtdcr(dcr_n + host.base, value)
-/* Device Control Registers */
-void __mtdcr(int reg, unsigned int val);
-unsigned int __mfdcr(int reg);
+/* Table based DCR accessors */
+extern void __mtdcr(unsigned int reg, unsigned int val);
+extern unsigned int __mfdcr(unsigned int reg);
+
+/* mfdcrx/mtdcrx instruction based accessors. We hand code
+ * the opcodes in order not to depend on newer binutils
+ */
+static inline unsigned int mfdcrx(unsigned int reg)
+{
+ unsigned int ret;
+ asm volatile(".long 0x7c000206 | (%0 << 21) | (%1 << 16)"
+ : "=r" (ret) : "r" (reg));
+ return ret;
+}
+
+static inline void mtdcrx(unsigned int reg, unsigned int val)
+{
+ asm volatile(".long 0x7c000306 | (%0 << 21) | (%1 << 16)"
+ : : "r" (val), "r" (reg));
+}
+
#define mfdcr(rn) \
({unsigned int rval; \
- if (__builtin_constant_p(rn)) \
+ if (__builtin_constant_p(rn) && rn < 1024) \
asm volatile("mfdcr %0," __stringify(rn) \
: "=r" (rval)); \
+ else if (likely(cpu_has_feature(CPU_FTR_INDEXED_DCR))) \
+ rval = mfdcrx(rn); \
else \
rval = __mfdcr(rn); \
rval;})
#define mtdcr(rn, v) \
do { \
- if (__builtin_constant_p(rn)) \
+ if (__builtin_constant_p(rn) && rn < 1024) \
asm volatile("mtdcr " __stringify(rn) ",%0" \
: : "r" (v)); \
+ else if (likely(cpu_has_feature(CPU_FTR_INDEXED_DCR))) \
+ mtdcrx(rn, v); \
else \
__mtdcr(rn, v); \
} while (0)
@@ -69,8 +92,13 @@ static inline unsigned __mfdcri(int base_addr, int base_data, int reg)
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
- __mtdcr(base_addr, reg);
- val = __mfdcr(base_data);
+ if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
+ mtdcrx(base_addr, reg);
+ val = mfdcrx(base_data);
+ } else {
+ __mtdcr(base_addr, reg);
+ val = __mfdcr(base_data);
+ }
spin_unlock_irqrestore(&dcr_ind_lock, flags);
return val;
}
@@ -81,8 +109,13 @@ static inline void __mtdcri(int base_addr, int base_data, int reg,
unsigned long flags;
spin_lock_irqsave(&dcr_ind_lock, flags);
- __mtdcr(base_addr, reg);
- __mtdcr(base_data, val);
+ if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
+ mtdcrx(base_addr, reg);
+ mtdcrx(base_data, val);
+ } else {
+ __mtdcr(base_addr, reg);
+ __mtdcr(base_data, val);
+ }
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
@@ -93,9 +126,15 @@ static inline void __dcri_clrset(int base_addr, int base_data, int reg,
unsigned int val;
spin_lock_irqsave(&dcr_ind_lock, flags);
- __mtdcr(base_addr, reg);
- val = (__mfdcr(base_data) & ~clr) | set;
- __mtdcr(base_data, val);
+ if (cpu_has_feature(CPU_FTR_INDEXED_DCR)) {
+ mtdcrx(base_addr, reg);
+ val = (mfdcrx(base_data) & ~clr) | set;
+ mtdcrx(base_data, val);
+ } else {
+ __mtdcr(base_addr, reg);
+ val = (__mfdcr(base_data) & ~clr) | set;
+ __mtdcr(base_data, val);
+ }
spin_unlock_irqrestore(&dcr_ind_lock, flags);
}
diff --git a/arch/powerpc/include/asm/dcr.h b/arch/powerpc/include/asm/dcr.h
index d13fb68bb5c0..9d6851cfb841 100644
--- a/arch/powerpc/include/asm/dcr.h
+++ b/arch/powerpc/include/asm/dcr.h
@@ -68,9 +68,9 @@ typedef dcr_host_mmio_t dcr_host_t;
* additional helpers to read the DCR * base from the device-tree
*/
struct device_node;
-extern unsigned int dcr_resource_start(struct device_node *np,
+extern unsigned int dcr_resource_start(const struct device_node *np,
unsigned int index);
-extern unsigned int dcr_resource_len(struct device_node *np,
+extern unsigned int dcr_resource_len(const struct device_node *np,
unsigned int index);
#endif /* CONFIG_PPC_DCR */
#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index dfd504caccc1..7d2277cef09a 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -18,4 +18,16 @@ struct dev_archdata {
void *dma_data;
};
+static inline void dev_archdata_set_node(struct dev_archdata *ad,
+ struct device_node *np)
+{
+ ad->of_node = np;
+}
+
+static inline struct device_node *
+dev_archdata_get_node(const struct dev_archdata *ad)
+{
+ return ad->of_node;
+}
+
#endif /* _ASM_POWERPC_DEVICE_H */
diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h
index fddb229bd74f..86cef7ddc8d5 100644
--- a/arch/powerpc/include/asm/dma-mapping.h
+++ b/arch/powerpc/include/asm/dma-mapping.h
@@ -60,12 +60,6 @@ struct dma_mapping_ops {
dma_addr_t *dma_handle, gfp_t flag);
void (*free_coherent)(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle);
- dma_addr_t (*map_single)(struct device *dev, void *ptr,
- size_t size, enum dma_data_direction direction,
- struct dma_attrs *attrs);
- void (*unmap_single)(struct device *dev, dma_addr_t dma_addr,
- size_t size, enum dma_data_direction direction,
- struct dma_attrs *attrs);
int (*map_sg)(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction,
struct dma_attrs *attrs);
@@ -82,6 +76,22 @@ struct dma_mapping_ops {
dma_addr_t dma_address, size_t size,
enum dma_data_direction direction,
struct dma_attrs *attrs);
+#ifdef CONFIG_PPC_NEED_DMA_SYNC_OPS
+ void (*sync_single_range_for_cpu)(struct device *hwdev,
+ dma_addr_t dma_handle, unsigned long offset,
+ size_t size,
+ enum dma_data_direction direction);
+ void (*sync_single_range_for_device)(struct device *hwdev,
+ dma_addr_t dma_handle, unsigned long offset,
+ size_t size,
+ enum dma_data_direction direction);
+ void (*sync_sg_for_cpu)(struct device *hwdev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction direction);
+ void (*sync_sg_for_device)(struct device *hwdev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction direction);
+#endif
};
/*
@@ -149,10 +159,9 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask)
}
/*
- * TODO: map_/unmap_single will ideally go away, to be completely
- * replaced by map/unmap_page. Until then, we allow dma_ops to have
- * one or the other, or both by checking to see if the specific
- * function requested exists; and if not, falling back on the other set.
+ * map_/unmap_single actually call through to map/unmap_page now that all the
+ * dma_mapping_ops have been converted over. We just have to get the page and
+ * offset to pass through to map_page
*/
static inline dma_addr_t dma_map_single_attrs(struct device *dev,
void *cpu_addr,
@@ -164,10 +173,6 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev,
BUG_ON(!dma_ops);
- if (dma_ops->map_single)
- return dma_ops->map_single(dev, cpu_addr, size, direction,
- attrs);
-
return dma_ops->map_page(dev, virt_to_page(cpu_addr),
(unsigned long)cpu_addr % PAGE_SIZE, size,
direction, attrs);
@@ -183,11 +188,6 @@ static inline void dma_unmap_single_attrs(struct device *dev,
BUG_ON(!dma_ops);
- if (dma_ops->unmap_single) {
- dma_ops->unmap_single(dev, dma_addr, size, direction, attrs);
- return;
- }
-
dma_ops->unmap_page(dev, dma_addr, size, direction, attrs);
}
@@ -201,12 +201,7 @@ static inline dma_addr_t dma_map_page_attrs(struct device *dev,
BUG_ON(!dma_ops);
- if (dma_ops->map_page)
- return dma_ops->map_page(dev, page, offset, size, direction,
- attrs);
-
- return dma_ops->map_single(dev, page_address(page) + offset, size,
- direction, attrs);
+ return dma_ops->map_page(dev, page, offset, size, direction, attrs);
}
static inline void dma_unmap_page_attrs(struct device *dev,
@@ -219,12 +214,7 @@ static inline void dma_unmap_page_attrs(struct device *dev,
BUG_ON(!dma_ops);
- if (dma_ops->unmap_page) {
- dma_ops->unmap_page(dev, dma_address, size, direction, attrs);
- return;
- }
-
- dma_ops->unmap_single(dev, dma_address, size, direction, attrs);
+ dma_ops->unmap_page(dev, dma_address, size, direction, attrs);
}
static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
@@ -308,47 +298,107 @@ static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
dma_unmap_sg_attrs(dev, sg, nhwentries, direction, NULL);
}
+#ifdef CONFIG_PPC_NEED_DMA_SYNC_OPS
static inline void dma_sync_single_for_cpu(struct device *dev,
dma_addr_t dma_handle, size_t size,
enum dma_data_direction direction)
{
- BUG_ON(direction == DMA_NONE);
- __dma_sync(bus_to_virt(dma_handle), size, direction);
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->sync_single_range_for_cpu(dev, dma_handle, 0,
+ size, direction);
}
static inline void dma_sync_single_for_device(struct device *dev,
dma_addr_t dma_handle, size_t size,
enum dma_data_direction direction)
{
- BUG_ON(direction == DMA_NONE);
- __dma_sync(bus_to_virt(dma_handle), size, direction);
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->sync_single_range_for_device(dev, dma_handle,
+ 0, size, direction);
}
static inline void dma_sync_sg_for_cpu(struct device *dev,
struct scatterlist *sgl, int nents,
enum dma_data_direction direction)
{
- struct scatterlist *sg;
- int i;
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
- BUG_ON(direction == DMA_NONE);
+ BUG_ON(!dma_ops);
+ dma_ops->sync_sg_for_cpu(dev, sgl, nents, direction);
+}
+
+static inline void dma_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sgl, int nents,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->sync_sg_for_device(dev, sgl, nents, direction);
+}
+
+static inline void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
- for_each_sg(sgl, sg, nents, i)
- __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction);
+ BUG_ON(!dma_ops);
+ dma_ops->sync_single_range_for_cpu(dev, dma_handle,
+ offset, size, direction);
+}
+
+static inline void dma_sync_single_range_for_device(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ struct dma_mapping_ops *dma_ops = get_dma_ops(dev);
+
+ BUG_ON(!dma_ops);
+ dma_ops->sync_single_range_for_device(dev, dma_handle, offset,
+ size, direction);
+}
+#else /* CONFIG_PPC_NEED_DMA_SYNC_OPS */
+static inline void dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction direction)
+{
+}
+
+static inline void dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction direction)
+{
+}
+
+static inline void dma_sync_sg_for_cpu(struct device *dev,
+ struct scatterlist *sgl, int nents,
+ enum dma_data_direction direction)
+{
}
static inline void dma_sync_sg_for_device(struct device *dev,
struct scatterlist *sgl, int nents,
enum dma_data_direction direction)
{
- struct scatterlist *sg;
- int i;
+}
- BUG_ON(direction == DMA_NONE);
+static inline void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+}
- for_each_sg(sgl, sg, nents, i)
- __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction);
+static inline void dma_sync_single_range_for_device(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
}
+#endif
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
@@ -382,22 +432,6 @@ static inline int dma_get_cache_alignment(void)
#endif
}
-static inline void dma_sync_single_range_for_cpu(struct device *dev,
- dma_addr_t dma_handle, unsigned long offset, size_t size,
- enum dma_data_direction direction)
-{
- /* just sync everything for now */
- dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction);
-}
-
-static inline void dma_sync_single_range_for_device(struct device *dev,
- dma_addr_t dma_handle, unsigned long offset, size_t size,
- enum dma_data_direction direction)
-{
- /* just sync everything for now */
- dma_sync_single_for_device(dev, dma_handle, offset + size, direction);
-}
-
static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
{
diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h
index b886bec67016..66ea9b8b95c5 100644
--- a/arch/powerpc/include/asm/eeh.h
+++ b/arch/powerpc/include/asm/eeh.h
@@ -17,8 +17,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#ifndef _PPC64_EEH_H
-#define _PPC64_EEH_H
+#ifndef _POWERPC_EEH_H
+#define _POWERPC_EEH_H
#ifdef __KERNEL__
#include <linux/init.h>
@@ -110,6 +110,7 @@ static inline void eeh_remove_bus_device(struct pci_dev *dev) { }
#define EEH_IO_ERROR_VALUE(size) (-1UL)
#endif /* CONFIG_EEH */
+#ifdef CONFIG_PPC64
/*
* MMIO read/write operations with EEH support.
*/
@@ -207,5 +208,6 @@ static inline void eeh_readsl(const volatile void __iomem *addr, void * buf,
eeh_check_failure(addr, *(u32*)buf);
}
+#endif /* CONFIG_PPC64 */
#endif /* __KERNEL__ */
-#endif /* _PPC64_EEH_H */
+#endif /* _POWERPC_EEH_H */
diff --git a/arch/powerpc/include/asm/elf.h b/arch/powerpc/include/asm/elf.h
index d812929390e4..cd46f023ec6d 100644
--- a/arch/powerpc/include/asm/elf.h
+++ b/arch/powerpc/include/asm/elf.h
@@ -267,7 +267,7 @@ extern int ucache_bsize;
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
- int executable_stack);
+ int uses_interp);
#define VDSO_AUX_ENT(a,b) NEW_AUX_ENT(a,b);
#endif /* __KERNEL__ */
diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h
index a1029967620b..e4094a5cb05b 100644
--- a/arch/powerpc/include/asm/feature-fixups.h
+++ b/arch/powerpc/include/asm/feature-fixups.h
@@ -81,6 +81,36 @@ label##5: \
#define ALT_FTR_SECTION_END_IFCLR(msk) \
ALT_FTR_SECTION_END_NESTED_IFCLR(msk, 97)
+/* MMU feature dependent sections */
+#define BEGIN_MMU_FTR_SECTION_NESTED(label) START_FTR_SECTION(label)
+#define BEGIN_MMU_FTR_SECTION START_FTR_SECTION(97)
+
+#define END_MMU_FTR_SECTION_NESTED(msk, val, label) \
+ FTR_SECTION_ELSE_NESTED(label) \
+ MAKE_FTR_SECTION_ENTRY(msk, val, label, __mmu_ftr_fixup)
+
+#define END_MMU_FTR_SECTION(msk, val) \
+ END_MMU_FTR_SECTION_NESTED(msk, val, 97)
+
+#define END_MMU_FTR_SECTION_IFSET(msk) END_MMU_FTR_SECTION((msk), (msk))
+#define END_MMU_FTR_SECTION_IFCLR(msk) END_MMU_FTR_SECTION((msk), 0)
+
+/* MMU feature sections with alternatives, use BEGIN_FTR_SECTION to start */
+#define MMU_FTR_SECTION_ELSE_NESTED(label) FTR_SECTION_ELSE_NESTED(label)
+#define MMU_FTR_SECTION_ELSE MMU_FTR_SECTION_ELSE_NESTED(97)
+#define ALT_MMU_FTR_SECTION_END_NESTED(msk, val, label) \
+ MAKE_FTR_SECTION_ENTRY(msk, val, label, __mmu_ftr_fixup)
+#define ALT_MMU_FTR_SECTION_END_NESTED_IFSET(msk, label) \
+ ALT_MMU_FTR_SECTION_END_NESTED(msk, msk, label)
+#define ALT_MMU_FTR_SECTION_END_NESTED_IFCLR(msk, label) \
+ ALT_MMU_FTR_SECTION_END_NESTED(msk, 0, label)
+#define ALT_MMU_FTR_SECTION_END(msk, val) \
+ ALT_MMU_FTR_SECTION_END_NESTED(msk, val, 97)
+#define ALT_MMU_FTR_SECTION_END_IFSET(msk) \
+ ALT_MMU_FTR_SECTION_END_NESTED_IFSET(msk, 97)
+#define ALT_MMU_FTR_SECTION_END_IFCLR(msk) \
+ ALT_MMU_FTR_SECTION_END_NESTED_IFCLR(msk, 97)
+
/* Firmware feature dependent sections */
#define BEGIN_FW_FTR_SECTION_NESTED(label) START_FTR_SECTION(label)
#define BEGIN_FW_FTR_SECTION START_FTR_SECTION(97)
diff --git a/arch/powerpc/include/asm/highmem.h b/arch/powerpc/include/asm/highmem.h
index 91c589520c0a..04e4a620952e 100644
--- a/arch/powerpc/include/asm/highmem.h
+++ b/arch/powerpc/include/asm/highmem.h
@@ -38,9 +38,24 @@ extern pte_t *pkmap_page_table;
* easily, subsequent pte tables have to be allocated in one physical
* chunk of RAM.
*/
-#define LAST_PKMAP (1 << PTE_SHIFT)
-#define LAST_PKMAP_MASK (LAST_PKMAP-1)
+/*
+ * We use one full pte table with 4K pages. And with 16K/64K pages pte
+ * table covers enough memory (32MB and 512MB resp.) that both FIXMAP
+ * and PKMAP can be placed in single pte table. We use 1024 pages for
+ * PKMAP in case of 16K/64K pages.
+ */
+#ifdef CONFIG_PPC_4K_PAGES
+#define PKMAP_ORDER PTE_SHIFT
+#else
+#define PKMAP_ORDER 10
+#endif
+#define LAST_PKMAP (1 << PKMAP_ORDER)
+#ifndef CONFIG_PPC_4K_PAGES
+#define PKMAP_BASE (FIXADDR_START - PAGE_SIZE*(LAST_PKMAP + 1))
+#else
#define PKMAP_BASE ((FIXADDR_START - PAGE_SIZE*(LAST_PKMAP + 1)) & PMD_MASK)
+#endif
+#define LAST_PKMAP_MASK (LAST_PKMAP-1)
#define PKMAP_NR(virt) ((virt-PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
@@ -85,7 +100,7 @@ static inline void *kmap_atomic_prot(struct page *page, enum km_type type, pgpro
BUG_ON(!pte_none(*(kmap_pte-idx)));
#endif
__set_pte_at(&init_mm, vaddr, kmap_pte-idx, mk_pte(page, prot));
- flush_tlb_page(NULL, vaddr);
+ local_flush_tlb_page(NULL, vaddr);
return (void*) vaddr;
}
@@ -113,7 +128,7 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
* this pte without first remap it
*/
pte_clear(&init_mm, vaddr, kmap_pte-idx);
- flush_tlb_page(NULL, vaddr);
+ local_flush_tlb_page(NULL, vaddr);
#endif
pagefault_enable();
}
diff --git a/arch/powerpc/include/asm/io.h b/arch/powerpc/include/asm/io.h
index 08266d2728b3..494cd8b0a278 100644
--- a/arch/powerpc/include/asm/io.h
+++ b/arch/powerpc/include/asm/io.h
@@ -713,13 +713,6 @@ static inline void * phys_to_virt(unsigned long address)
*/
#define page_to_phys(page) ((phys_addr_t)page_to_pfn(page) << PAGE_SHIFT)
-/* We do NOT want virtual merging, it would put too much pressure on
- * our iommu allocator. Instead, we want drivers to be smart enough
- * to coalesce sglists that happen to have been mapped in a contiguous
- * way by the iommu
- */
-#define BIO_VMERGE_BOUNDARY 0
-
/*
* 32 bits still uses virt_to_bus() for it's implementation of DMA
* mappings se we have to keep it defined here. We also have some old
diff --git a/arch/powerpc/include/asm/kdump.h b/arch/powerpc/include/asm/kdump.h
index b07ebb9784d3..5ebfe5d3c61f 100644
--- a/arch/powerpc/include/asm/kdump.h
+++ b/arch/powerpc/include/asm/kdump.h
@@ -1,6 +1,8 @@
#ifndef _PPC64_KDUMP_H
#define _PPC64_KDUMP_H
+#include <asm/page.h>
+
/* Kdump kernel runs at 32 MB, change at your peril. */
#define KDUMP_KERNELBASE 0x2000000
@@ -11,8 +13,19 @@
#ifdef CONFIG_CRASH_DUMP
+/*
+ * On PPC64 translation is disabled during trampoline setup, so we use
+ * physical addresses. Though on PPC32 translation is already enabled,
+ * so we can't do the same. Luckily create_trampoline() creates relative
+ * branches, so we can just add the PAGE_OFFSET and don't worry about it.
+ */
+#ifdef __powerpc64__
#define KDUMP_TRAMPOLINE_START 0x0100
#define KDUMP_TRAMPOLINE_END 0x3000
+#else
+#define KDUMP_TRAMPOLINE_START (0x0100 + PAGE_OFFSET)
+#define KDUMP_TRAMPOLINE_END (0x3000 + PAGE_OFFSET)
+#endif /* __powerpc64__ */
#define KDUMP_MIN_TCE_ENTRIES 2048
diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h
index 3736d9b33289..6dbffc981702 100644
--- a/arch/powerpc/include/asm/kexec.h
+++ b/arch/powerpc/include/asm/kexec.h
@@ -33,12 +33,12 @@
#ifndef __ASSEMBLY__
#include <linux/cpumask.h>
+#include <asm/reg.h>
typedef void (*crash_shutdown_t)(void);
#ifdef CONFIG_KEXEC
-#ifdef __powerpc64__
/*
* This function is responsible for capturing register states if coming
* via panic or invoking dump using sysrq-trigger.
@@ -48,6 +48,7 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
{
if (oldregs)
memcpy(newregs, oldregs, sizeof(*newregs));
+#ifdef __powerpc64__
else {
/* FIXME Merge this with xmon_save_regs ?? */
unsigned long tmp1, tmp2;
@@ -100,15 +101,11 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
: "b" (newregs)
: "memory");
}
-}
#else
-/*
- * Provide a dummy definition to avoid build failures. Will remain
- * empty till crash dump support is enabled.
- */
-static inline void crash_setup_regs(struct pt_regs *newregs,
- struct pt_regs *oldregs) { }
-#endif /* !__powerpc64 __ */
+ else
+ ppc_save_regs(newregs);
+#endif /* __powerpc64__ */
+}
extern void kexec_smp_wait(void); /* get and clear naca physid, wait for
master to copy new code to 0 */
diff --git a/arch/powerpc/include/asm/local.h b/arch/powerpc/include/asm/local.h
index 612d83276653..84b457a3c1bc 100644
--- a/arch/powerpc/include/asm/local.h
+++ b/arch/powerpc/include/asm/local.h
@@ -67,7 +67,7 @@ static __inline__ long local_inc_return(local_t *l)
bne- 1b"
: "=&r" (t)
: "r" (&(l->a.counter))
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
@@ -94,7 +94,7 @@ static __inline__ long local_dec_return(local_t *l)
bne- 1b"
: "=&r" (t)
: "r" (&(l->a.counter))
- : "cc", "memory");
+ : "cc", "xer", "memory");
return t;
}
diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h
index 2fe268b10333..25aaa97facd8 100644
--- a/arch/powerpc/include/asm/lppaca.h
+++ b/arch/powerpc/include/asm/lppaca.h
@@ -133,7 +133,8 @@ struct lppaca {
//=============================================================================
// CACHE_LINE_4-5 0x0180 - 0x027F Contains PMC interrupt data
//=============================================================================
- u8 pmc_save_area[256]; // PMC interrupt Area x00-xFF
+ u32 page_ins; // CMO Hint - # page ins by OS x00-x04
+ u8 pmc_save_area[252]; // PMC interrupt Area x04-xFF
} __attribute__((__aligned__(0x400)));
extern struct lppaca lppaca[];
diff --git a/arch/powerpc/include/asm/mmu-40x.h b/arch/powerpc/include/asm/mmu-40x.h
index 3d108676584c..776f415a36aa 100644
--- a/arch/powerpc/include/asm/mmu-40x.h
+++ b/arch/powerpc/include/asm/mmu-40x.h
@@ -54,8 +54,9 @@
#ifndef __ASSEMBLY__
typedef struct {
- unsigned long id;
- unsigned long vdso_base;
+ unsigned int id;
+ unsigned int active;
+ unsigned long vdso_base;
} mm_context_t;
#endif /* !__ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/mmu-44x.h b/arch/powerpc/include/asm/mmu-44x.h
index a825524c981a..8a97cfb08b7e 100644
--- a/arch/powerpc/include/asm/mmu-44x.h
+++ b/arch/powerpc/include/asm/mmu-44x.h
@@ -4,6 +4,8 @@
* PPC440 support
*/
+#include <asm/page.h>
+
#define PPC44x_MMUCR_TID 0x000000ff
#define PPC44x_MMUCR_STS 0x00010000
@@ -56,8 +58,9 @@
extern unsigned int tlb_44x_hwater;
typedef struct {
- unsigned long id;
- unsigned long vdso_base;
+ unsigned int id;
+ unsigned int active;
+ unsigned long vdso_base;
} mm_context_t;
#endif /* !__ASSEMBLY__ */
@@ -73,4 +76,19 @@ typedef struct {
/* Size of the TLBs used for pinning in lowmem */
#define PPC_PIN_SIZE (1 << 28) /* 256M */
+#if (PAGE_SHIFT == 12)
+#define PPC44x_TLBE_SIZE PPC44x_TLB_4K
+#elif (PAGE_SHIFT == 14)
+#define PPC44x_TLBE_SIZE PPC44x_TLB_16K
+#elif (PAGE_SHIFT == 16)
+#define PPC44x_TLBE_SIZE PPC44x_TLB_64K
+#else
+#error "Unsupported PAGE_SIZE"
+#endif
+
+#define PPC44x_PGD_OFF_SHIFT (32 - PGDIR_SHIFT + PGD_T_LOG2)
+#define PPC44x_PGD_OFF_MASK_BIT (PGDIR_SHIFT - PGD_T_LOG2)
+#define PPC44x_PTE_ADD_SHIFT (32 - PGDIR_SHIFT + PTE_SHIFT + PTE_T_LOG2)
+#define PPC44x_PTE_ADD_MASK_BIT (32 - PTE_T_LOG2 - PTE_SHIFT)
+
#endif /* _ASM_POWERPC_MMU_44X_H_ */
diff --git a/arch/powerpc/include/asm/mmu-8xx.h b/arch/powerpc/include/asm/mmu-8xx.h
index 9db877eb88db..07865a357848 100644
--- a/arch/powerpc/include/asm/mmu-8xx.h
+++ b/arch/powerpc/include/asm/mmu-8xx.h
@@ -137,7 +137,8 @@
#ifndef __ASSEMBLY__
typedef struct {
- unsigned long id;
+ unsigned int id;
+ unsigned int active;
unsigned long vdso_base;
} mm_context_t;
#endif /* !__ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/mmu-fsl-booke.h b/arch/powerpc/include/asm/mmu-fsl-booke.h
index 925d93cf64d8..3f941c0f7e8e 100644
--- a/arch/powerpc/include/asm/mmu-fsl-booke.h
+++ b/arch/powerpc/include/asm/mmu-fsl-booke.h
@@ -40,6 +40,8 @@
#define MAS2_M 0x00000004
#define MAS2_G 0x00000002
#define MAS2_E 0x00000001
+#define MAS2_EPN_MASK(size) (~0 << (2*(size) + 10))
+#define MAS2_VAL(addr, size, flags) ((addr) & MAS2_EPN_MASK(size) | (flags))
#define MAS3_RPN 0xFFFFF000
#define MAS3_U0 0x00000200
@@ -74,8 +76,9 @@
#ifndef __ASSEMBLY__
typedef struct {
- unsigned long id;
- unsigned long vdso_base;
+ unsigned int id;
+ unsigned int active;
+ unsigned long vdso_base;
} mm_context_t;
#endif /* !__ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h
index 4c0e1b4f975c..6e7639911318 100644
--- a/arch/powerpc/include/asm/mmu.h
+++ b/arch/powerpc/include/asm/mmu.h
@@ -2,6 +2,63 @@
#define _ASM_POWERPC_MMU_H_
#ifdef __KERNEL__
+#include <asm/asm-compat.h>
+#include <asm/feature-fixups.h>
+
+/*
+ * MMU features bit definitions
+ */
+
+/*
+ * First half is MMU families
+ */
+#define MMU_FTR_HPTE_TABLE ASM_CONST(0x00000001)
+#define MMU_FTR_TYPE_8xx ASM_CONST(0x00000002)
+#define MMU_FTR_TYPE_40x ASM_CONST(0x00000004)
+#define MMU_FTR_TYPE_44x ASM_CONST(0x00000008)
+#define MMU_FTR_TYPE_FSL_E ASM_CONST(0x00000010)
+
+/*
+ * This is individual features
+ */
+
+/* Enable use of high BAT registers */
+#define MMU_FTR_USE_HIGH_BATS ASM_CONST(0x00010000)
+
+/* Enable >32-bit physical addresses on 32-bit processor, only used
+ * by CONFIG_6xx currently as BookE supports that from day 1
+ */
+#define MMU_FTR_BIG_PHYS ASM_CONST(0x00020000)
+
+/* Enable use of broadcast TLB invalidations. We don't always set it
+ * on processors that support it due to other constraints with the
+ * use of such invalidations
+ */
+#define MMU_FTR_USE_TLBIVAX_BCAST ASM_CONST(0x00040000)
+
+/* Enable use of tlbilx invalidate-by-PID variant.
+ */
+#define MMU_FTR_USE_TLBILX_PID ASM_CONST(0x00080000)
+
+/* This indicates that the processor cannot handle multiple outstanding
+ * broadcast tlbivax or tlbsync. This makes the code use a spinlock
+ * around such invalidate forms.
+ */
+#define MMU_FTR_LOCK_BCAST_INVAL ASM_CONST(0x00100000)
+
+#ifndef __ASSEMBLY__
+#include <asm/cputable.h>
+
+static inline int mmu_has_feature(unsigned long feature)
+{
+ return (cur_cpu_spec->mmu_features & feature);
+}
+
+extern unsigned int __start___mmu_ftr_fixup, __stop___mmu_ftr_fixup;
+
+#endif /* !__ASSEMBLY__ */
+
+
#ifdef CONFIG_PPC64
/* 64-bit classic hash table MMU */
# include <asm/mmu-hash64.h>
diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
index 6b993ef452ff..ab4f19263c42 100644
--- a/arch/powerpc/include/asm/mmu_context.h
+++ b/arch/powerpc/include/asm/mmu_context.h
@@ -2,237 +2,26 @@
#define __ASM_POWERPC_MMU_CONTEXT_H
#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
#include <asm/mmu.h>
#include <asm/cputable.h>
#include <asm-generic/mm_hooks.h>
-
-#ifndef CONFIG_PPC64
-#include <asm/atomic.h>
-#include <linux/bitops.h>
-
-/*
- * On 32-bit PowerPC 6xx/7xx/7xxx CPUs, we use a set of 16 VSIDs
- * (virtual segment identifiers) for each context. Although the
- * hardware supports 24-bit VSIDs, and thus >1 million contexts,
- * we only use 32,768 of them. That is ample, since there can be
- * at most around 30,000 tasks in the system anyway, and it means
- * that we can use a bitmap to indicate which contexts are in use.
- * Using a bitmap means that we entirely avoid all of the problems
- * that we used to have when the context number overflowed,
- * particularly on SMP systems.
- * -- paulus.
- */
-
-/*
- * This function defines the mapping from contexts to VSIDs (virtual
- * segment IDs). We use a skew on both the context and the high 4 bits
- * of the 32-bit virtual address (the "effective segment ID") in order
- * to spread out the entries in the MMU hash table. Note, if this
- * function is changed then arch/ppc/mm/hashtable.S will have to be
- * changed to correspond.
- */
-#define CTX_TO_VSID(ctx, va) (((ctx) * (897 * 16) + ((va) >> 28) * 0x111) \
- & 0xffffff)
-
-/*
- The MPC8xx has only 16 contexts. We rotate through them on each
- task switch. A better way would be to keep track of tasks that
- own contexts, and implement an LRU usage. That way very active
- tasks don't always have to pay the TLB reload overhead. The
- kernel pages are mapped shared, so the kernel can run on behalf
- of any task that makes a kernel entry. Shared does not mean they
- are not protected, just that the ASID comparison is not performed.
- -- Dan
-
- The IBM4xx has 256 contexts, so we can just rotate through these
- as a way of "switching" contexts. If the TID of the TLB is zero,
- the PID/TID comparison is disabled, so we can use a TID of zero
- to represent all kernel pages as shared among all contexts.
- -- Dan
- */
-
-static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
-{
-}
-
-#ifdef CONFIG_8xx
-#define NO_CONTEXT 16
-#define LAST_CONTEXT 15
-#define FIRST_CONTEXT 0
-
-#elif defined(CONFIG_4xx)
-#define NO_CONTEXT 256
-#define LAST_CONTEXT 255
-#define FIRST_CONTEXT 1
-
-#elif defined(CONFIG_E200) || defined(CONFIG_E500)
-#define NO_CONTEXT 256
-#define LAST_CONTEXT 255
-#define FIRST_CONTEXT 1
-
-#else
-
-/* PPC 6xx, 7xx CPUs */
-#define NO_CONTEXT ((unsigned long) -1)
-#define LAST_CONTEXT 32767
-#define FIRST_CONTEXT 1
-#endif
-
-/*
- * Set the current MMU context.
- * On 32-bit PowerPCs (other than the 8xx embedded chips), this is done by
- * loading up the segment registers for the user part of the address space.
- *
- * Since the PGD is immediately available, it is much faster to simply
- * pass this along as a second parameter, which is required for 8xx and
- * can be used for debugging on all processors (if you happen to have
- * an Abatron).
- */
-extern void set_context(unsigned long contextid, pgd_t *pgd);
-
-/*
- * Bitmap of contexts in use.
- * The size of this bitmap is LAST_CONTEXT + 1 bits.
- */
-extern unsigned long context_map[];
-
-/*
- * This caches the next context number that we expect to be free.
- * Its use is an optimization only, we can't rely on this context
- * number to be free, but it usually will be.
- */
-extern unsigned long next_mmu_context;
-
-/*
- * If we don't have sufficient contexts to give one to every task
- * that could be in the system, we need to be able to steal contexts.
- * These variables support that.
- */
-#if LAST_CONTEXT < 30000
-#define FEW_CONTEXTS 1
-extern atomic_t nr_free_contexts;
-extern struct mm_struct *context_mm[LAST_CONTEXT+1];
-extern void steal_context(void);
-#endif
-
-/*
- * Get a new mmu context for the address space described by `mm'.
- */
-static inline void get_mmu_context(struct mm_struct *mm)
-{
- unsigned long ctx;
-
- if (mm->context.id != NO_CONTEXT)
- return;
-#ifdef FEW_CONTEXTS
- while (atomic_dec_if_positive(&nr_free_contexts) < 0)
- steal_context();
-#endif
- ctx = next_mmu_context;
- while (test_and_set_bit(ctx, context_map)) {
- ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx);
- if (ctx > LAST_CONTEXT)
- ctx = 0;
- }
- next_mmu_context = (ctx + 1) & LAST_CONTEXT;
- mm->context.id = ctx;
-#ifdef FEW_CONTEXTS
- context_mm[ctx] = mm;
-#endif
-}
-
-/*
- * Set up the context for a new address space.
- */
-static inline int init_new_context(struct task_struct *t, struct mm_struct *mm)
-{
- mm->context.id = NO_CONTEXT;
- return 0;
-}
-
-/*
- * We're finished using the context for an address space.
- */
-static inline void destroy_context(struct mm_struct *mm)
-{
- preempt_disable();
- if (mm->context.id != NO_CONTEXT) {
- clear_bit(mm->context.id, context_map);
- mm->context.id = NO_CONTEXT;
-#ifdef FEW_CONTEXTS
- atomic_inc(&nr_free_contexts);
-#endif
- }
- preempt_enable();
-}
-
-static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
- struct task_struct *tsk)
-{
-#ifdef CONFIG_ALTIVEC
- if (cpu_has_feature(CPU_FTR_ALTIVEC))
- asm volatile ("dssall;\n"
-#ifndef CONFIG_POWER4
- "sync;\n" /* G4 needs a sync here, G5 apparently not */
-#endif
- : : );
-#endif /* CONFIG_ALTIVEC */
-
- tsk->thread.pgdir = next->pgd;
-
- /* No need to flush userspace segments if the mm doesnt change */
- if (prev == next)
- return;
-
- /* Setup new userspace context */
- get_mmu_context(next);
- set_context(next->context.id, next->pgd);
-}
-
-#define deactivate_mm(tsk,mm) do { } while (0)
+#include <asm/cputhreads.h>
/*
- * After we have set current->mm to a new value, this activates
- * the context for the new mm so we see the new mappings.
+ * Most if the context management is out of line
*/
-#define activate_mm(active_mm, mm) switch_mm(active_mm, mm, current)
-
extern void mmu_context_init(void);
-
-
-#else
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-
-/*
- * Copyright (C) 2001 PPC 64 Team, IBM Corp
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-static inline void enter_lazy_tlb(struct mm_struct *mm,
- struct task_struct *tsk)
-{
-}
-
-/*
- * The proto-VSID space has 2^35 - 1 segments available for user mappings.
- * Each segment contains 2^28 bytes. Each context maps 2^44 bytes,
- * so we can support 2^19-1 contexts (19 == 35 + 28 - 44).
- */
-#define NO_CONTEXT 0
-#define MAX_CONTEXT ((1UL << 19) - 1)
-
extern int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
extern void destroy_context(struct mm_struct *mm);
+extern void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next);
extern void switch_stab(struct task_struct *tsk, struct mm_struct *mm);
extern void switch_slb(struct task_struct *tsk, struct mm_struct *mm);
+extern void set_context(unsigned long id, pgd_t *pgd);
/*
* switch_mm is the entry point called from the architecture independent
@@ -241,22 +30,39 @@ extern void switch_slb(struct task_struct *tsk, struct mm_struct *mm);
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
- if (!cpu_isset(smp_processor_id(), next->cpu_vm_mask))
- cpu_set(smp_processor_id(), next->cpu_vm_mask);
+ /* Mark this context has been used on the new CPU */
+ cpu_set(smp_processor_id(), next->cpu_vm_mask);
+
+ /* 32-bit keeps track of the current PGDIR in the thread struct */
+#ifdef CONFIG_PPC32
+ tsk->thread.pgdir = next->pgd;
+#endif /* CONFIG_PPC32 */
- /* No need to flush userspace segments if the mm doesnt change */
+ /* Nothing else to do if we aren't actually switching */
if (prev == next)
return;
+ /* We must stop all altivec streams before changing the HW
+ * context
+ */
#ifdef CONFIG_ALTIVEC
if (cpu_has_feature(CPU_FTR_ALTIVEC))
asm volatile ("dssall");
#endif /* CONFIG_ALTIVEC */
+ /* The actual HW switching method differs between the various
+ * sub architectures.
+ */
+#ifdef CONFIG_PPC_STD_MMU_64
if (cpu_has_feature(CPU_FTR_SLB))
switch_slb(tsk, next);
else
switch_stab(tsk, next);
+#else
+ /* Out of line for now */
+ switch_mmu_context(prev, next);
+#endif
+
}
#define deactivate_mm(tsk,mm) do { } while (0)
@@ -274,6 +80,11 @@ static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
local_irq_restore(flags);
}
-#endif /* CONFIG_PPC64 */
+/* We don't currently use enter_lazy_tlb() for anything */
+static inline void enter_lazy_tlb(struct mm_struct *mm,
+ struct task_struct *tsk)
+{
+}
+
#endif /* __KERNEL__ */
#endif /* __ASM_POWERPC_MMU_CONTEXT_H */
diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h
index 81ef10b6b672..81a23932a160 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -239,6 +239,25 @@ struct mpc52xx_cdm {
u16 mclken_div_psc6; /* CDM + 0x36 reg13 byte2,3 */
};
+/* Interrupt controller Register set */
+struct mpc52xx_intr {
+ u32 per_mask; /* INTR + 0x00 */
+ u32 per_pri1; /* INTR + 0x04 */
+ u32 per_pri2; /* INTR + 0x08 */
+ u32 per_pri3; /* INTR + 0x0c */
+ u32 ctrl; /* INTR + 0x10 */
+ u32 main_mask; /* INTR + 0x14 */
+ u32 main_pri1; /* INTR + 0x18 */
+ u32 main_pri2; /* INTR + 0x1c */
+ u32 reserved1; /* INTR + 0x20 */
+ u32 enc_status; /* INTR + 0x24 */
+ u32 crit_status; /* INTR + 0x28 */
+ u32 main_status; /* INTR + 0x2c */
+ u32 per_status; /* INTR + 0x30 */
+ u32 reserved2; /* INTR + 0x34 */
+ u32 per_error; /* INTR + 0x38 */
+};
+
#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/mpc52xx_psc.h b/arch/powerpc/include/asm/mpc52xx_psc.h
index 8917ed630565..a218da6bec7c 100644
--- a/arch/powerpc/include/asm/mpc52xx_psc.h
+++ b/arch/powerpc/include/asm/mpc52xx_psc.h
@@ -68,12 +68,20 @@
#define MPC52xx_PSC_IMR_ORERR 0x1000
#define MPC52xx_PSC_IMR_IPC 0x8000
-/* PSC input port change bit */
+/* PSC input port change bits */
#define MPC52xx_PSC_CTS 0x01
#define MPC52xx_PSC_DCD 0x02
#define MPC52xx_PSC_D_CTS 0x10
#define MPC52xx_PSC_D_DCD 0x20
+/* PSC acr bits */
+#define MPC52xx_PSC_IEC_CTS 0x01
+#define MPC52xx_PSC_IEC_DCD 0x02
+
+/* PSC output port bits */
+#define MPC52xx_PSC_OP_RTS 0x01
+#define MPC52xx_PSC_OP_RES 0x02
+
/* PSC mode fields */
#define MPC52xx_PSC_MODE_5_BITS 0x00
#define MPC52xx_PSC_MODE_6_BITS 0x01
@@ -91,6 +99,7 @@
#define MPC52xx_PSC_MODE_ONE_STOP_5_BITS 0x00
#define MPC52xx_PSC_MODE_ONE_STOP 0x07
#define MPC52xx_PSC_MODE_TWO_STOP 0x0f
+#define MPC52xx_PSC_MODE_TXCTS 0x10
#define MPC52xx_PSC_RFNUM_MASK 0x01ff
diff --git a/arch/powerpc/include/asm/mutex.h b/arch/powerpc/include/asm/mutex.h
index 458c1f7fbc18..dabc01c727b8 100644
--- a/arch/powerpc/include/asm/mutex.h
+++ b/arch/powerpc/include/asm/mutex.h
@@ -1,9 +1,134 @@
/*
- * Pull in the generic implementation for the mutex fastpath.
+ * Optimised mutex implementation of include/asm-generic/mutex-dec.h algorithm
+ */
+#ifndef _ASM_POWERPC_MUTEX_H
+#define _ASM_POWERPC_MUTEX_H
+
+static inline int __mutex_cmpxchg_lock(atomic_t *v, int old, int new)
+{
+ int t;
+
+ __asm__ __volatile__ (
+"1: lwarx %0,0,%1 # mutex trylock\n\
+ cmpw 0,%0,%2\n\
+ bne- 2f\n"
+ PPC405_ERR77(0,%1)
+" stwcx. %3,0,%1\n\
+ bne- 1b"
+ ISYNC_ON_SMP
+ "\n\
+2:"
+ : "=&r" (t)
+ : "r" (&v->counter), "r" (old), "r" (new)
+ : "cc", "memory");
+
+ return t;
+}
+
+static inline int __mutex_dec_return_lock(atomic_t *v)
+{
+ int t;
+
+ __asm__ __volatile__(
+"1: lwarx %0,0,%1 # mutex lock\n\
+ addic %0,%0,-1\n"
+ PPC405_ERR77(0,%1)
+" stwcx. %0,0,%1\n\
+ bne- 1b"
+ ISYNC_ON_SMP
+ : "=&r" (t)
+ : "r" (&v->counter)
+ : "cc", "memory");
+
+ return t;
+}
+
+static inline int __mutex_inc_return_unlock(atomic_t *v)
+{
+ int t;
+
+ __asm__ __volatile__(
+ LWSYNC_ON_SMP
+"1: lwarx %0,0,%1 # mutex unlock\n\
+ addic %0,%0,1\n"
+ PPC405_ERR77(0,%1)
+" stwcx. %0,0,%1 \n\
+ bne- 1b"
+ : "=&r" (t)
+ : "r" (&v->counter)
+ : "cc", "memory");
+
+ return t;
+}
+
+/**
+ * __mutex_fastpath_lock - try to take the lock by moving the count
+ * from 1 to a 0 value
+ * @count: pointer of type atomic_t
+ * @fail_fn: function to call if the original value was not 1
+ *
+ * Change the count from 1 to a value lower than 1, and call <fail_fn> if
+ * it wasn't 1 originally. This function MUST leave the value lower than
+ * 1 even when the "1" assertion wasn't true.
+ */
+static inline void
+__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+ if (unlikely(__mutex_dec_return_lock(count) < 0))
+ fail_fn(count);
+}
+
+/**
+ * __mutex_fastpath_lock_retval - try to take the lock by moving the count
+ * from 1 to a 0 value
+ * @count: pointer of type atomic_t
+ * @fail_fn: function to call if the original value was not 1
+ *
+ * Change the count from 1 to a value lower than 1, and call <fail_fn> if
+ * it wasn't 1 originally. This function returns 0 if the fastpath succeeds,
+ * or anything the slow path function returns.
+ */
+static inline int
+__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+ if (unlikely(__mutex_dec_return_lock(count) < 0))
+ return fail_fn(count);
+ return 0;
+}
+
+/**
+ * __mutex_fastpath_unlock - try to promote the count from 0 to 1
+ * @count: pointer of type atomic_t
+ * @fail_fn: function to call if the original value was not 0
+ *
+ * Try to promote the count from 0 to 1. If it wasn't 0, call <fail_fn>.
+ * In the failure case, this function is allowed to either set the value to
+ * 1, or to set it to a value lower than 1.
+ */
+static inline void
+__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+ if (unlikely(__mutex_inc_return_unlock(count) <= 0))
+ fail_fn(count);
+}
+
+#define __mutex_slowpath_needs_to_unlock() 1
+
+/**
+ * __mutex_fastpath_trylock - try to acquire the mutex, without waiting
+ *
+ * @count: pointer of type atomic_t
+ * @fail_fn: fallback function
*
- * TODO: implement optimized primitives instead, or leave the generic
- * implementation in place, or pick the atomic_xchg() based generic
- * implementation. (see asm-generic/mutex-xchg.h for details)
+ * Change the count from 1 to 0, and return 1 (success), or if the count
+ * was not 1, then return 0 (failure).
*/
+static inline int
+__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+ if (likely(__mutex_cmpxchg_lock(count, 1, 0) == 1))
+ return 1;
+ return 0;
+}
-#include <asm-generic/mutex-dec.h>
+#endif
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index c0b8d4a29a91..197d569f5bd3 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -19,12 +19,15 @@
#include <asm/kdump.h>
/*
- * On PPC32 page size is 4K. For PPC64 we support either 4K or 64K software
+ * On regular PPC32 page size is 4K (but we support 4K/16K/64K pages
+ * on PPC44x). For PPC64 we support either 4K or 64K software
* page size. When using 64K pages however, whether we are really supporting
* 64K pages in HW or not is irrelevant to those definitions.
*/
-#ifdef CONFIG_PPC_64K_PAGES
+#if defined(CONFIG_PPC_64K_PAGES)
#define PAGE_SHIFT 16
+#elif defined(CONFIG_PPC_16K_PAGES)
+#define PAGE_SHIFT 14
#else
#define PAGE_SHIFT 12
#endif
@@ -151,7 +154,7 @@ typedef struct { pte_basic_t pte; } pte_t;
/* 64k pages additionally define a bigger "real PTE" type that gathers
* the "second half" part of the PTE for pseudo 64k pages
*/
-#ifdef CONFIG_PPC_64K_PAGES
+#if defined(CONFIG_PPC_64K_PAGES) && defined(CONFIG_PPC_STD_MMU_64)
typedef struct { pte_t pte; unsigned long hidx; } real_pte_t;
#else
typedef struct { pte_t pte; } real_pte_t;
@@ -191,10 +194,10 @@ typedef pte_basic_t pte_t;
#define pte_val(x) (x)
#define __pte(x) (x)
-#ifdef CONFIG_PPC_64K_PAGES
+#if defined(CONFIG_PPC_64K_PAGES) && defined(CONFIG_PPC_STD_MMU_64)
typedef struct { pte_t pte; unsigned long hidx; } real_pte_t;
#else
-typedef unsigned long real_pte_t;
+typedef pte_t real_pte_t;
#endif
diff --git a/arch/powerpc/include/asm/page_32.h b/arch/powerpc/include/asm/page_32.h
index d77072a32cc6..1458d9500381 100644
--- a/arch/powerpc/include/asm/page_32.h
+++ b/arch/powerpc/include/asm/page_32.h
@@ -19,6 +19,8 @@
#define PTE_FLAGS_OFFSET 0
#endif
+#define PTE_SHIFT (PAGE_SHIFT - PTE_T_LOG2) /* full page */
+
#ifndef __ASSEMBLY__
/*
* The basic type of a PTE - 64 bits for those CPUs with > 32 bit
@@ -26,10 +28,8 @@
*/
#ifdef CONFIG_PTE_64BIT
typedef unsigned long long pte_basic_t;
-#define PTE_SHIFT (PAGE_SHIFT - 3) /* 512 ptes per page */
#else
typedef unsigned long pte_basic_t;
-#define PTE_SHIFT (PAGE_SHIFT - 2) /* 1024 ptes per page */
#endif
struct page;
@@ -39,6 +39,9 @@ extern void copy_page(void *to, void *from);
#include <asm-generic/page.h>
+#define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1)
+#define PTE_T_LOG2 (__builtin_ffs(sizeof(pte_t)) - 1)
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_POWERPC_PAGE_32_H */
diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h
index 9047af7baa69..84007afabdb5 100644
--- a/arch/powerpc/include/asm/pci-bridge.h
+++ b/arch/powerpc/include/asm/pci-bridge.h
@@ -13,7 +13,6 @@
struct device_node;
-extern unsigned int ppc_pci_flags;
enum {
/* Force re-assigning all resources (ignore firmware
* setup completely)
@@ -36,6 +35,31 @@ enum {
/* ... except for domain 0 */
PPC_PCI_COMPAT_DOMAIN_0 = 0x00000020,
};
+#ifdef CONFIG_PCI
+extern unsigned int ppc_pci_flags;
+
+static inline void ppc_pci_set_flags(int flags)
+{
+ ppc_pci_flags = flags;
+}
+
+static inline void ppc_pci_add_flags(int flags)
+{
+ ppc_pci_flags |= flags;
+}
+
+static inline int ppc_pci_has_flag(int flag)
+{
+ return (ppc_pci_flags & flag);
+}
+#else
+static inline void ppc_pci_set_flags(int flags) { }
+static inline void ppc_pci_add_flags(int flags) { }
+static inline int ppc_pci_has_flag(int flag)
+{
+ return 0;
+}
+#endif
/*
@@ -241,9 +265,6 @@ extern void pcibios_remove_pci_devices(struct pci_bus *bus);
/** Discover new pci devices under this bus, and add them */
extern void pcibios_add_pci_devices(struct pci_bus *bus);
-extern void pcibios_fixup_new_pci_devices(struct pci_bus *bus);
-
-extern int pcibios_remove_root_bus(struct pci_controller *phb);
static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
{
@@ -290,6 +311,7 @@ extern void pci_process_bridge_OF_ranges(struct pci_controller *hose,
/* Allocate & free a PCI host bridge structure */
extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev);
extern void pcibios_free_controller(struct pci_controller *phb);
+extern void pcibios_setup_phb_resources(struct pci_controller *hose);
#ifdef CONFIG_PCI
extern unsigned long pci_address_to_pio(phys_addr_t address);
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h
index 57a2a494886b..3548159a1beb 100644
--- a/arch/powerpc/include/asm/pci.h
+++ b/arch/powerpc/include/asm/pci.h
@@ -38,8 +38,8 @@ struct pci_dev;
* Set this to 1 if you want the kernel to re-assign all PCI
* bus numbers (don't do that on ppc64 yet !)
*/
-#define pcibios_assign_all_busses() (ppc_pci_flags & \
- PPC_PCI_REASSIGN_ALL_BUS)
+#define pcibios_assign_all_busses() \
+ (ppc_pci_has_flag(PPC_PCI_REASSIGN_ALL_BUS))
#define pcibios_scan_all_fns(a, b) 0
static inline void pcibios_set_master(struct pci_dev *dev)
@@ -204,15 +204,14 @@ static inline struct resource *pcibios_select_root(struct pci_dev *pdev,
return root;
}
-extern void pcibios_setup_new_device(struct pci_dev *dev);
-
extern void pcibios_claim_one_bus(struct pci_bus *b);
-extern void pcibios_allocate_bus_resources(struct pci_bus *bus);
+extern void pcibios_finish_adding_to_bus(struct pci_bus *bus);
extern void pcibios_resource_survey(void);
extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
+extern int remove_phb_dynamic(struct pci_controller *phb);
extern struct pci_dev *of_create_pci_dev(struct device_node *node,
struct pci_bus *bus, int devfn);
@@ -221,6 +220,7 @@ extern void of_scan_pci_bridge(struct device_node *node,
struct pci_dev *dev);
extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
+extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus);
extern int pci_read_irq_line(struct pci_dev *dev);
@@ -235,9 +235,8 @@ extern void pci_resource_to_user(const struct pci_dev *dev, int bar,
const struct resource *rsrc,
resource_size_t *start, resource_size_t *end);
-extern void pcibios_do_bus_setup(struct pci_bus *bus);
-extern void pcibios_fixup_of_probed_bus(struct pci_bus *bus);
-
+extern void pcibios_setup_bus_devices(struct pci_bus *bus);
+extern void pcibios_setup_bus_self(struct pci_bus *bus);
#endif /* __KERNEL__ */
#endif /* __ASM_POWERPC_PCI_H */
diff --git a/arch/powerpc/include/asm/pgalloc-32.h b/arch/powerpc/include/asm/pgalloc-32.h
index 58c07147b3ea..0815eb40acae 100644
--- a/arch/powerpc/include/asm/pgalloc-32.h
+++ b/arch/powerpc/include/asm/pgalloc-32.h
@@ -3,6 +3,8 @@
#include <linux/threads.h>
+#define PTE_NONCACHE_NUM 0 /* dummy for now to share code w/ppc64 */
+
extern void __bad_pte(pmd_t *pmd);
extern pgd_t *pgd_alloc(struct mm_struct *mm);
@@ -33,10 +35,13 @@ extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr);
extern pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long addr);
-extern void pte_free_kernel(struct mm_struct *mm, pte_t *pte);
-extern void pte_free(struct mm_struct *mm, pgtable_t pte);
-#define __pte_free_tlb(tlb, pte) pte_free((tlb)->mm, (pte))
+static inline void pgtable_free(pgtable_free_t pgf)
+{
+ void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK);
+
+ free_page((unsigned long)p);
+}
#define check_pgt_cache() do { } while (0)
diff --git a/arch/powerpc/include/asm/pgalloc-64.h b/arch/powerpc/include/asm/pgalloc-64.h
index 812a1d8f35cb..afda2bdd860f 100644
--- a/arch/powerpc/include/asm/pgalloc-64.h
+++ b/arch/powerpc/include/asm/pgalloc-64.h
@@ -7,7 +7,6 @@
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/cpumask.h>
#include <linux/percpu.h>
@@ -108,31 +107,6 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
return page;
}
-static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
-{
- free_page((unsigned long)pte);
-}
-
-static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
-{
- pgtable_page_dtor(ptepage);
- __free_page(ptepage);
-}
-
-#define PGF_CACHENUM_MASK 0x7
-
-typedef struct pgtable_free {
- unsigned long val;
-} pgtable_free_t;
-
-static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum,
- unsigned long mask)
-{
- BUG_ON(cachenum > PGF_CACHENUM_MASK);
-
- return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum};
-}
-
static inline void pgtable_free(pgtable_free_t pgf)
{
void *p = (void *)(pgf.val & ~PGF_CACHENUM_MASK);
@@ -144,14 +118,6 @@ static inline void pgtable_free(pgtable_free_t pgf)
kmem_cache_free(pgtable_cache[cachenum], p);
}
-extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf);
-
-#define __pte_free_tlb(tlb,ptepage) \
-do { \
- pgtable_page_dtor(ptepage); \
- pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \
- PTE_NONCACHE_NUM, PTE_TABLE_SIZE-1)); \
-} while (0)
#define __pmd_free_tlb(tlb, pmd) \
pgtable_free_tlb(tlb, pgtable_free_cache(pmd, \
PMD_CACHE_NUM, PMD_TABLE_SIZE-1))
diff --git a/arch/powerpc/include/asm/pgalloc.h b/arch/powerpc/include/asm/pgalloc.h
index b4505ed0f0f2..5d8480265a77 100644
--- a/arch/powerpc/include/asm/pgalloc.h
+++ b/arch/powerpc/include/asm/pgalloc.h
@@ -2,11 +2,52 @@
#define _ASM_POWERPC_PGALLOC_H
#ifdef __KERNEL__
+#include <linux/mm.h>
+
+static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
+{
+ free_page((unsigned long)pte);
+}
+
+static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
+{
+ pgtable_page_dtor(ptepage);
+ __free_page(ptepage);
+}
+
+typedef struct pgtable_free {
+ unsigned long val;
+} pgtable_free_t;
+
+#define PGF_CACHENUM_MASK 0x7
+
+static inline pgtable_free_t pgtable_free_cache(void *p, int cachenum,
+ unsigned long mask)
+{
+ BUG_ON(cachenum > PGF_CACHENUM_MASK);
+
+ return (pgtable_free_t){.val = ((unsigned long) p & ~mask) | cachenum};
+}
+
#ifdef CONFIG_PPC64
#include <asm/pgalloc-64.h>
#else
#include <asm/pgalloc-32.h>
#endif
+extern void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf);
+
+#ifdef CONFIG_SMP
+#define __pte_free_tlb(tlb,ptepage) \
+do { \
+ pgtable_page_dtor(ptepage); \
+ pgtable_free_tlb(tlb, pgtable_free_cache(page_address(ptepage), \
+ PTE_NONCACHE_NUM, PTE_TABLE_SIZE-1)); \
+} while (0)
+#else
+#define __pte_free_tlb(tlb, pte) pte_free((tlb)->mm, (pte))
+#endif
+
+
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_PGALLOC_H */
diff --git a/arch/powerpc/include/asm/pgtable-ppc32.h b/arch/powerpc/include/asm/pgtable-ppc32.h
index 6ab7c67cb5ab..f69a4d977729 100644
--- a/arch/powerpc/include/asm/pgtable-ppc32.h
+++ b/arch/powerpc/include/asm/pgtable-ppc32.h
@@ -228,9 +228,10 @@ extern int icache_44x_need_flush;
* - FILE *must* be in the bottom three bits because swap cache
* entries use the top 29 bits for TLB2.
*
- * - CACHE COHERENT bit (M) has no effect on PPC440 core, because it
- * doesn't support SMP. So we can use this as software bit, like
- * DIRTY.
+ * - CACHE COHERENT bit (M) has no effect on original PPC440 cores,
+ * because it doesn't support SMP. However, some later 460 variants
+ * have -some- form of SMP support and so I keep the bit there for
+ * future use
*
* With the PPC 44x Linux implementation, the 0-11th LSBs of the PTE are used
* for memory protection related functions (see PTE structure in
@@ -436,20 +437,23 @@ extern int icache_44x_need_flush;
_PAGE_USER | _PAGE_ACCESSED | \
_PAGE_RW | _PAGE_HWWRITE | _PAGE_DIRTY | \
_PAGE_EXEC | _PAGE_HWEXEC)
+
/*
- * Note: the _PAGE_COHERENT bit automatically gets set in the hardware
- * PTE if CONFIG_SMP is defined (hash_page does this); there is no need
- * to have it in the Linux PTE, and in fact the bit could be reused for
- * another purpose. -- paulus.
+ * We define 2 sets of base prot bits, one for basic pages (ie,
+ * cacheable kernel and user pages) and one for non cacheable
+ * pages. We always set _PAGE_COHERENT when SMP is enabled or
+ * the processor might need it for DMA coherency.
*/
-
-#ifdef CONFIG_44x
-#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_GUARDED)
+#if defined(CONFIG_SMP) || defined(CONFIG_PPC_STD_MMU)
+#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_COHERENT)
#else
#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED)
#endif
+#define _PAGE_BASE_NC (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NO_CACHE)
+
#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE)
#define _PAGE_KERNEL (_PAGE_BASE | _PAGE_SHARED | _PAGE_WRENABLE)
+#define _PAGE_KERNEL_NC (_PAGE_BASE_NC | _PAGE_SHARED | _PAGE_WRENABLE)
#ifdef CONFIG_PPC_STD_MMU
/* On standard PPC MMU, no user access implies kernel read/write access,
@@ -459,7 +463,7 @@ extern int icache_44x_need_flush;
#define _PAGE_KERNEL_RO (_PAGE_BASE | _PAGE_SHARED)
#endif
-#define _PAGE_IO (_PAGE_KERNEL | _PAGE_NO_CACHE | _PAGE_GUARDED)
+#define _PAGE_IO (_PAGE_KERNEL_NC | _PAGE_GUARDED)
#define _PAGE_RAM (_PAGE_KERNEL | _PAGE_HWEXEC)
#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) || defined(CONFIG_BDI_SWITCH) ||\
@@ -552,9 +556,6 @@ static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;
static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; }
static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; }
-static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; }
-static inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; }
-
static inline pte_t pte_wrprotect(pte_t pte) {
pte_val(pte) &= ~(_PAGE_RW | _PAGE_HWWRITE); return pte; }
static inline pte_t pte_mkclean(pte_t pte) {
@@ -693,10 +694,11 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
#endif
}
+
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte)
{
-#if defined(CONFIG_PTE_64BIT) && defined(CONFIG_SMP)
+#if defined(CONFIG_PTE_64BIT) && defined(CONFIG_SMP) && defined(CONFIG_DEBUG_VM)
WARN_ON(pte_present(*ptep));
#endif
__set_pte_at(mm, addr, ptep, pte);
@@ -760,16 +762,6 @@ static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty)
__changed; \
})
-/*
- * Macro to mark a page protection value as "uncacheable".
- */
-#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED))
-
-struct file;
-extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
- unsigned long size, pgprot_t vma_prot);
-#define __HAVE_PHYS_MEM_ACCESS_PROT
-
#define __HAVE_ARCH_PTE_SAME
#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HASHPTE) == 0)
diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index 4c0a8c62859d..b0f18be81d9f 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -100,7 +100,7 @@
#define _PAGE_WRENABLE (_PAGE_RW | _PAGE_DIRTY)
-/* __pgprot defined in arch/powerpc/incliude/asm/page.h */
+/* __pgprot defined in arch/powerpc/include/asm/page.h */
#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
#define PAGE_SHARED __pgprot(_PAGE_BASE | _PAGE_RW | _PAGE_USER)
@@ -245,9 +245,6 @@ static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED;}
static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE;}
static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; }
-static inline void pte_uncache(pte_t pte) { pte_val(pte) |= _PAGE_NO_CACHE; }
-static inline void pte_cache(pte_t pte) { pte_val(pte) &= ~_PAGE_NO_CACHE; }
-
static inline pte_t pte_wrprotect(pte_t pte) {
pte_val(pte) &= ~(_PAGE_RW); return pte; }
static inline pte_t pte_mkclean(pte_t pte) {
@@ -405,16 +402,6 @@ static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry, int dirty)
__changed; \
})
-/*
- * Macro to mark a page protection value as "uncacheable".
- */
-#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) | _PAGE_NO_CACHE | _PAGE_GUARDED))
-
-struct file;
-extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
- unsigned long size, pgprot_t vma_prot);
-#define __HAVE_PHYS_MEM_ACCESS_PROT
-
#define __HAVE_ARCH_PTE_SAME
#define pte_same(A,B) (((pte_val(A) ^ pte_val(B)) & ~_PAGE_HPTEFLAGS) == 0)
diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h
index dbb8ca172e44..07f55e601696 100644
--- a/arch/powerpc/include/asm/pgtable.h
+++ b/arch/powerpc/include/asm/pgtable.h
@@ -16,6 +16,32 @@ struct mm_struct;
#endif
#ifndef __ASSEMBLY__
+
+/*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+
+#define _PAGE_CACHE_CTL (_PAGE_COHERENT | _PAGE_GUARDED | _PAGE_NO_CACHE | \
+ _PAGE_WRITETHRU)
+
+#define pgprot_noncached(prot) (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+ _PAGE_NO_CACHE | _PAGE_GUARDED))
+
+#define pgprot_noncached_wc(prot) (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+ _PAGE_NO_CACHE))
+
+#define pgprot_cached(prot) (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+ _PAGE_COHERENT))
+
+#define pgprot_cached_wthru(prot) (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+ _PAGE_COHERENT | _PAGE_WRITETHRU))
+
+
+struct file;
+extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+ unsigned long size, pgprot_t vma_prot);
+#define __HAVE_PHYS_MEM_ACCESS_PROT
+
/*
* ZERO_PAGE is a global shared page that is always zero: used
* for zero-mapped memory areas etc..
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index c4a029ccb4d3..1a0d628eb114 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -425,14 +425,14 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
#define fromreal(rd) tovirt(rd,rd)
#define tophys(rd,rs) \
-0: addis rd,rs,-KERNELBASE@h; \
+0: addis rd,rs,-PAGE_OFFSET@h; \
.section ".vtop_fixup","aw"; \
.align 1; \
.long 0b; \
.previous
#define tovirt(rd,rs) \
-0: addis rd,rs,KERNELBASE@h; \
+0: addis rd,rs,PAGE_OFFSET@h; \
.section ".ptov_fixup","aw"; \
.align 1; \
.long 0b; \
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 101ed87f7d84..d3466490104a 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -69,8 +69,6 @@ extern int _prep_type;
#ifdef __KERNEL__
-extern int have_of;
-
struct task_struct;
void start_thread(struct pt_regs *regs, unsigned long fdptr, unsigned long sp);
void release_thread(struct task_struct *);
@@ -207,6 +205,11 @@ struct thread_struct {
#define INIT_SP_LIMIT \
(_ALIGN_UP(sizeof(init_thread_info), 16) + (unsigned long) &init_stack)
+#ifdef CONFIG_SPE
+#define SPEFSCR_INIT .spefscr = SPEFSCR_FINVE | SPEFSCR_FDBZE | SPEFSCR_FUNFE | SPEFSCR_FOVFE,
+#else
+#define SPEFSCR_INIT
+#endif
#ifdef CONFIG_PPC32
#define INIT_THREAD { \
@@ -215,6 +218,7 @@ struct thread_struct {
.fs = KERNEL_DS, \
.pgdir = swapper_pg_dir, \
.fpexc_mode = MSR_FE0 | MSR_FE1, \
+ SPEFSCR_INIT \
}
#else
#define INIT_THREAD { \
diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h
index eb3bd2e1c7f6..6ff04185d2aa 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -253,6 +253,9 @@ extern void kdump_move_device_tree(void);
/* CPU OF node matching */
struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
+/* cache lookup */
+struct device_node *of_find_next_cache_node(struct device_node *np);
+
/* Get the MAC address */
extern const void *of_get_mac_address(struct device_node *np);
diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h
index f9e34c493cbb..cff30c0ef1ff 100644
--- a/arch/powerpc/include/asm/ps3.h
+++ b/arch/powerpc/include/asm/ps3.h
@@ -305,30 +305,34 @@ static inline const char* ps3_result(int result)
/* system bus routines */
enum ps3_match_id {
- PS3_MATCH_ID_EHCI = 1,
- PS3_MATCH_ID_OHCI = 2,
- PS3_MATCH_ID_GELIC = 3,
- PS3_MATCH_ID_AV_SETTINGS = 4,
- PS3_MATCH_ID_SYSTEM_MANAGER = 5,
- PS3_MATCH_ID_STOR_DISK = 6,
- PS3_MATCH_ID_STOR_ROM = 7,
- PS3_MATCH_ID_STOR_FLASH = 8,
- PS3_MATCH_ID_SOUND = 9,
- PS3_MATCH_ID_GRAPHICS = 10,
- PS3_MATCH_ID_LPM = 11,
+ PS3_MATCH_ID_EHCI = 1,
+ PS3_MATCH_ID_OHCI = 2,
+ PS3_MATCH_ID_GELIC = 3,
+ PS3_MATCH_ID_AV_SETTINGS = 4,
+ PS3_MATCH_ID_SYSTEM_MANAGER = 5,
+ PS3_MATCH_ID_STOR_DISK = 6,
+ PS3_MATCH_ID_STOR_ROM = 7,
+ PS3_MATCH_ID_STOR_FLASH = 8,
+ PS3_MATCH_ID_SOUND = 9,
+ PS3_MATCH_ID_GPU = 10,
+ PS3_MATCH_ID_LPM = 11,
};
-#define PS3_MODULE_ALIAS_EHCI "ps3:1"
-#define PS3_MODULE_ALIAS_OHCI "ps3:2"
-#define PS3_MODULE_ALIAS_GELIC "ps3:3"
-#define PS3_MODULE_ALIAS_AV_SETTINGS "ps3:4"
-#define PS3_MODULE_ALIAS_SYSTEM_MANAGER "ps3:5"
-#define PS3_MODULE_ALIAS_STOR_DISK "ps3:6"
-#define PS3_MODULE_ALIAS_STOR_ROM "ps3:7"
-#define PS3_MODULE_ALIAS_STOR_FLASH "ps3:8"
-#define PS3_MODULE_ALIAS_SOUND "ps3:9"
-#define PS3_MODULE_ALIAS_GRAPHICS "ps3:10"
-#define PS3_MODULE_ALIAS_LPM "ps3:11"
+enum ps3_match_sub_id {
+ PS3_MATCH_SUB_ID_GPU_FB = 1,
+};
+
+#define PS3_MODULE_ALIAS_EHCI "ps3:1:0"
+#define PS3_MODULE_ALIAS_OHCI "ps3:2:0"
+#define PS3_MODULE_ALIAS_GELIC "ps3:3:0"
+#define PS3_MODULE_ALIAS_AV_SETTINGS "ps3:4:0"
+#define PS3_MODULE_ALIAS_SYSTEM_MANAGER "ps3:5:0"
+#define PS3_MODULE_ALIAS_STOR_DISK "ps3:6:0"
+#define PS3_MODULE_ALIAS_STOR_ROM "ps3:7:0"
+#define PS3_MODULE_ALIAS_STOR_FLASH "ps3:8:0"
+#define PS3_MODULE_ALIAS_SOUND "ps3:9:0"
+#define PS3_MODULE_ALIAS_GPU_FB "ps3:10:1"
+#define PS3_MODULE_ALIAS_LPM "ps3:11:0"
enum ps3_system_bus_device_type {
PS3_DEVICE_TYPE_IOC0 = 1,
@@ -337,11 +341,6 @@ enum ps3_system_bus_device_type {
PS3_DEVICE_TYPE_LPM,
};
-enum ps3_match_sub_id {
- /* for PS3_MATCH_ID_GRAPHICS */
- PS3_MATCH_SUB_ID_FB = 1,
-};
-
/**
* struct ps3_system_bus_device - a device on the system bus
*/
@@ -516,4 +515,7 @@ void ps3_sync_irq(int node);
u32 ps3_get_hw_thread_id(int cpu);
u64 ps3_get_spe_id(void *arg);
+/* mutex synchronizing GPU accesses and video mode changes */
+extern struct mutex ps3_gpu_mutex;
+
#endif
diff --git a/arch/powerpc/include/asm/ps3av.h b/arch/powerpc/include/asm/ps3av.h
index 5aa22cffdbd6..cd24ac16660a 100644
--- a/arch/powerpc/include/asm/ps3av.h
+++ b/arch/powerpc/include/asm/ps3av.h
@@ -740,8 +740,4 @@ extern int ps3av_audio_mute(int);
extern int ps3av_audio_mute_analog(int);
extern int ps3av_dev_open(void);
extern int ps3av_dev_close(void);
-extern void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
- void *flip_data);
-extern void ps3av_flip_ctl(int on);
-
#endif /* _ASM_POWERPC_PS3AV_H_ */
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index c6d1ab650778..f484a343efba 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -783,6 +783,10 @@ extern void scom970_write(unsigned int address, unsigned long value);
#define __get_SP() ({unsigned long sp; \
asm volatile("mr %0,1": "=r" (sp)); sp;})
+struct pt_regs;
+
+extern void ppc_save_regs(struct pt_regs *regs);
+
#endif /* __ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _ASM_POWERPC_REG_H */
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 8eaa7b28d9d0..e0175beb4462 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -168,6 +168,7 @@ extern void rtas_os_term(char *str);
extern int rtas_get_sensor(int sensor, int index, int *state);
extern int rtas_get_power_level(int powerdomain, int *level);
extern int rtas_set_power_level(int powerdomain, int level, int *setlevel);
+extern bool rtas_indicator_present(int token, int *maxindex);
extern int rtas_set_indicator(int indicator, int index, int new_value);
extern int rtas_set_indicator_fast(int indicator, int index, int new_value);
extern void rtas_progress(char *s, unsigned short hex);
diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h
index ced34f1dc8f8..3d9f831c3c55 100644
--- a/arch/powerpc/include/asm/sfp-machine.h
+++ b/arch/powerpc/include/asm/sfp-machine.h
@@ -82,7 +82,7 @@
#define _FP_MUL_MEAT_S(R,X,Y) _FP_MUL_MEAT_1_wide(_FP_WFRACBITS_S,R,X,Y,umul_ppmm)
#define _FP_MUL_MEAT_D(R,X,Y) _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_D,R,X,Y,umul_ppmm)
-#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv(S,R,X,Y)
+#define _FP_DIV_MEAT_S(R,X,Y) _FP_DIV_MEAT_1_udiv_norm(S,R,X,Y)
#define _FP_DIV_MEAT_D(R,X,Y) _FP_DIV_MEAT_2_udiv(D,R,X,Y)
/* These macros define what NaN looks like. They're supposed to expand to
@@ -97,6 +97,20 @@
#define _FP_KEEPNANFRACP 1
+#ifdef FP_EX_BOOKE_E500_SPE
+#define FP_EX_INEXACT (1 << 21)
+#define FP_EX_INVALID (1 << 20)
+#define FP_EX_DIVZERO (1 << 19)
+#define FP_EX_UNDERFLOW (1 << 18)
+#define FP_EX_OVERFLOW (1 << 17)
+#define FP_INHIBIT_RESULTS 0
+
+#define __FPU_FPSCR (current->thread.spefscr)
+#define __FPU_ENABLED_EXC \
+({ \
+ (__FPU_FPSCR >> 2) & 0x1f; \
+})
+#else
/* Exception flags. We use the bit positions of the appropriate bits
in the FPSCR, which also correspond to the FE_* bits. This makes
everything easier ;-). */
@@ -111,22 +125,6 @@
#define FP_EX_DIVZERO (1 << (31 - 5))
#define FP_EX_INEXACT (1 << (31 - 6))
-/* This macro appears to be called when both X and Y are NaNs, and
- * has to choose one and copy it to R. i386 goes for the larger of the
- * two, sparc64 just picks Y. I don't understand this at all so I'll
- * go with sparc64 because it's shorter :-> -- PMM
- */
-#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \
- do { \
- R##_s = Y##_s; \
- _FP_FRAC_COPY_##wc(R,Y); \
- R##_c = FP_CLS_NAN; \
- } while (0)
-
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-
#define __FPU_FPSCR (current->thread.fpscr.val)
/* We only actually write to the destination register
@@ -137,6 +135,32 @@
(__FPU_FPSCR >> 3) & 0x1f; \
})
+#endif
+
+/*
+ * If one NaN is signaling and the other is not,
+ * we choose that one, otherwise we choose X.
+ */
+#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP) \
+ do { \
+ if ((_FP_FRAC_HIGH_RAW_##fs(Y) & _FP_QNANBIT_##fs) \
+ && !(_FP_FRAC_HIGH_RAW_##fs(X) & _FP_QNANBIT_##fs)) \
+ { \
+ R##_s = X##_s; \
+ _FP_FRAC_COPY_##wc(R,X); \
+ } \
+ else \
+ { \
+ R##_s = Y##_s; \
+ _FP_FRAC_COPY_##wc(R,Y); \
+ } \
+ R##_c = FP_CLS_NAN; \
+ } while (0)
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
#define __FPU_TRAP_P(bits) \
((__FPU_ENABLED_EXC & (bits)) != 0)
diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index 1866cec4f967..c25f73d1d842 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -81,6 +81,13 @@ extern int cpu_to_core_id(int cpu);
#define PPC_MSG_CALL_FUNC_SINGLE 2
#define PPC_MSG_DEBUGGER_BREAK 3
+/*
+ * irq controllers that have dedicated ipis per message and don't
+ * need additional code in the action handler may use this
+ */
+extern int smp_request_message_ipi(int virq, int message);
+extern const char *smp_ipi_name[];
+
void smp_init_iSeries(void);
void smp_init_pSeries(void);
void smp_init_cell(void);
diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h
index f56a843f4705..36864364e601 100644
--- a/arch/powerpc/include/asm/spinlock.h
+++ b/arch/powerpc/include/asm/spinlock.h
@@ -277,7 +277,7 @@ static inline void __raw_read_unlock(raw_rwlock_t *rw)
bne- 1b"
: "=&r"(tmp)
: "r"(&rw->lock)
- : "cr0", "memory");
+ : "cr0", "xer", "memory");
}
static inline void __raw_write_unlock(raw_rwlock_t *rw)
diff --git a/arch/powerpc/include/asm/synch.h b/arch/powerpc/include/asm/synch.h
index 45963e80f557..28f6ddbff4cf 100644
--- a/arch/powerpc/include/asm/synch.h
+++ b/arch/powerpc/include/asm/synch.h
@@ -5,6 +5,10 @@
#include <linux/stringify.h>
#include <asm/feature-fixups.h>
+#if defined(__powerpc64__) || defined(CONFIG_PPC_E500MC)
+#define __SUBARCH_HAS_LWSYNC
+#endif
+
#ifndef __ASSEMBLY__
extern unsigned int __start___lwsync_fixup, __stop___lwsync_fixup;
extern void do_lwsync_fixups(unsigned long value, void *fixup_start,
diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h
index d6648c143322..2a4be19a92c4 100644
--- a/arch/powerpc/include/asm/system.h
+++ b/arch/powerpc/include/asm/system.h
@@ -23,15 +23,17 @@
* read_barrier_depends() prevents data-dependent loads being reordered
* across this point (nop on PPC).
*
- * We have to use the sync instructions for mb(), since lwsync doesn't
- * order loads with respect to previous stores. Lwsync is fine for
- * rmb(), though. Note that rmb() actually uses a sync on 32-bit
- * architectures.
+ * *mb() variants without smp_ prefix must order all types of memory
+ * operations with one another. sync is the only instruction sufficient
+ * to do this.
*
- * For wmb(), we use sync since wmb is used in drivers to order
- * stores to system memory with respect to writes to the device.
- * However, smp_wmb() can be a lighter-weight lwsync or eieio barrier
- * on SMP since it is only used to order updates to system memory.
+ * For the smp_ barriers, ordering is for cacheable memory operations
+ * only. We have to use the sync instruction for smp_mb(), since lwsync
+ * doesn't order loads with respect to previous stores. Lwsync can be
+ * used for smp_rmb() and smp_wmb().
+ *
+ * However, on CPUs that don't support lwsync, lwsync actually maps to a
+ * heavy-weight sync, so smp_wmb() can be a lighter-weight eieio.
*/
#define mb() __asm__ __volatile__ ("sync" : : : "memory")
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
@@ -45,14 +47,14 @@
#ifdef CONFIG_SMP
#ifdef __SUBARCH_HAS_LWSYNC
-# define SMPWMB lwsync
+# define SMPWMB LWSYNC
#else
# define SMPWMB eieio
#endif
#define smp_mb() mb()
-#define smp_rmb() rmb()
-#define smp_wmb() __asm__ __volatile__ (__stringify(SMPWMB) : : :"memory")
+#define smp_rmb() __asm__ __volatile__ (stringify_in_c(LWSYNC) : : :"memory")
+#define smp_wmb() __asm__ __volatile__ (stringify_in_c(SMPWMB) : : :"memory")
#define smp_read_barrier_depends() read_barrier_depends()
#else
#define smp_mb() barrier()
diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h
index febd581ec9b0..27ccb764fdab 100644
--- a/arch/powerpc/include/asm/time.h
+++ b/arch/powerpc/include/asm/time.h
@@ -48,26 +48,6 @@ extern unsigned long ppc_proc_freq;
extern unsigned long ppc_tb_freq;
#define DEFAULT_TB_FREQ 125000000UL
-/*
- * By putting all of this stuff into a single struct we
- * reduce the number of cache lines touched by do_gettimeofday.
- * Both by collecting all of the data in one cache line and
- * by touching only one TOC entry on ppc64.
- */
-struct gettimeofday_vars {
- u64 tb_to_xs;
- u64 stamp_xsec;
- u64 tb_orig_stamp;
-};
-
-struct gettimeofday_struct {
- unsigned long tb_ticks_per_sec;
- struct gettimeofday_vars vars[2];
- struct gettimeofday_vars * volatile varp;
- unsigned var_idx;
- unsigned tb_to_us;
-};
-
struct div_result {
u64 result_high;
u64 result_low;
diff --git a/arch/powerpc/include/asm/tlbflush.h b/arch/powerpc/include/asm/tlbflush.h
index a2c6bfd85fb7..abbe3419d1dd 100644
--- a/arch/powerpc/include/asm/tlbflush.h
+++ b/arch/powerpc/include/asm/tlbflush.h
@@ -6,6 +6,9 @@
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
+ * - local_flush_tlb_mm(mm) flushes the specified mm context on
+ * the local processor
+ * - local_flush_tlb_page(vma, vmaddr) flushes one page on the local processor
* - flush_tlb_page_nohash(vma, vmaddr) flushes one page if SW loaded TLB
* - flush_tlb_range(vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
@@ -17,7 +20,7 @@
*/
#ifdef __KERNEL__
-#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE)
+#ifdef CONFIG_PPC_MMU_NOHASH
/*
* TLB flushing for software loaded TLB chips
*
@@ -28,63 +31,49 @@
#include <linux/mm.h>
-extern void _tlbie(unsigned long address, unsigned int pid);
-extern void _tlbil_all(void);
-extern void _tlbil_pid(unsigned int pid);
-extern void _tlbil_va(unsigned long address, unsigned int pid);
+#define MMU_NO_CONTEXT ((unsigned int)-1)
-#if defined(CONFIG_40x) || defined(CONFIG_8xx)
-#define _tlbia() asm volatile ("tlbia; sync" : : : "memory")
-#else /* CONFIG_44x || CONFIG_FSL_BOOKE */
-extern void _tlbia(void);
-#endif
-
-static inline void flush_tlb_mm(struct mm_struct *mm)
-{
- _tlbil_pid(mm->context.id);
-}
-
-static inline void flush_tlb_page(struct vm_area_struct *vma,
- unsigned long vmaddr)
-{
- _tlbil_va(vmaddr, vma ? vma->vm_mm->context.id : 0);
-}
+extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end);
+extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
-static inline void flush_tlb_page_nohash(struct vm_area_struct *vma,
- unsigned long vmaddr)
-{
- flush_tlb_page(vma, vmaddr);
-}
+extern void local_flush_tlb_mm(struct mm_struct *mm);
+extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
-static inline void flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
-{
- _tlbil_pid(vma->vm_mm->context.id);
-}
+#ifdef CONFIG_SMP
+extern void flush_tlb_mm(struct mm_struct *mm);
+extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
+#else
+#define flush_tlb_mm(mm) local_flush_tlb_mm(mm)
+#define flush_tlb_page(vma,addr) local_flush_tlb_page(vma,addr)
+#endif
+#define flush_tlb_page_nohash(vma,addr) flush_tlb_page(vma,addr)
-static inline void flush_tlb_kernel_range(unsigned long start,
- unsigned long end)
-{
- _tlbil_pid(0);
-}
+#elif defined(CONFIG_PPC_STD_MMU_32)
-#elif defined(CONFIG_PPC32)
/*
- * TLB flushing for "classic" hash-MMMU 32-bit CPUs, 6xx, 7xx, 7xxx
+ * TLB flushing for "classic" hash-MMU 32-bit CPUs, 6xx, 7xx, 7xxx
*/
-extern void _tlbie(unsigned long address);
-extern void _tlbia(void);
-
extern void flush_tlb_mm(struct mm_struct *mm);
extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr);
extern void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr);
extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
+static inline void local_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long vmaddr)
+{
+ flush_tlb_page(vma, vmaddr);
+}
+static inline void local_flush_tlb_mm(struct mm_struct *mm)
+{
+ flush_tlb_mm(mm);
+}
+
+#elif defined(CONFIG_PPC_STD_MMU_64)
-#else
/*
- * TLB flushing for 64-bit has-MMU CPUs
+ * TLB flushing for 64-bit hash-MMU CPUs
*/
#include <linux/percpu.h>
@@ -134,10 +123,19 @@ extern void flush_hash_page(unsigned long va, real_pte_t pte, int psize,
extern void flush_hash_range(unsigned long number, int local);
+static inline void local_flush_tlb_mm(struct mm_struct *mm)
+{
+}
+
static inline void flush_tlb_mm(struct mm_struct *mm)
{
}
+static inline void local_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long vmaddr)
+{
+}
+
static inline void flush_tlb_page(struct vm_area_struct *vma,
unsigned long vmaddr)
{
@@ -162,7 +160,8 @@ static inline void flush_tlb_kernel_range(unsigned long start,
extern void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
unsigned long end);
-
+#else
+#error Unsupported MMU type
#endif
#endif /*__KERNEL__ */
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 373fca394a54..375258559ae6 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -22,11 +22,11 @@ static inline cpumask_t node_to_cpumask(int node)
return numa_cpumask_lookup_table[node];
}
+#define cpumask_of_node(node) (&numa_cpumask_lookup_table[node])
+
static inline int node_to_first_cpu(int node)
{
- cpumask_t tmp;
- tmp = node_to_cpumask(node);
- return first_cpu(tmp);
+ return cpumask_first(cpumask_of_node(node));
}
int of_node_to_nid(struct device_node *device);
@@ -46,6 +46,10 @@ static inline int pcibus_to_node(struct pci_bus *bus)
node_to_cpumask(pcibus_to_node(bus)) \
)
+#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
+ cpu_all_mask : \
+ cpumask_of_node(pcibus_to_node(bus)))
+
/* sched_domains SD_NODE_INIT for PPC64 machines */
#define SD_NODE_INIT (struct sched_domain) { \
.parent = NULL, \
@@ -108,6 +112,8 @@ static inline void sysfs_remove_device_from_node(struct sys_device *dev,
#define topology_thread_siblings(cpu) (per_cpu(cpu_sibling_map, cpu))
#define topology_core_siblings(cpu) (per_cpu(cpu_core_map, cpu))
+#define topology_thread_cpumask(cpu) (&per_cpu(cpu_sibling_map, cpu))
+#define topology_core_cpumask(cpu) (&per_cpu(cpu_core_map, cpu))
#define topology_core_id(cpu) (cpu_to_core_id(cpu))
#endif
#endif
diff --git a/arch/powerpc/include/asm/vdso_datapage.h b/arch/powerpc/include/asm/vdso_datapage.h
index f01393224b52..13c2c283e178 100644
--- a/arch/powerpc/include/asm/vdso_datapage.h
+++ b/arch/powerpc/include/asm/vdso_datapage.h
@@ -39,6 +39,7 @@
#ifndef __ASSEMBLY__
#include <linux/unistd.h>
+#include <linux/time.h>
#define SYSCALL_MAP_SIZE ((__NR_syscalls + 31) / 32)
@@ -83,6 +84,7 @@ struct vdso_data {
__u32 icache_log_block_size; /* L1 i-cache log block size */
__s32 wtom_clock_sec; /* Wall to monotonic clock */
__s32 wtom_clock_nsec;
+ struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */
__u32 syscall_map_64[SYSCALL_MAP_SIZE]; /* map of syscalls */
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
};
@@ -102,6 +104,7 @@ struct vdso_data {
__u32 tz_dsttime; /* Type of dst correction 0x5C */
__s32 wtom_clock_sec; /* Wall to monotonic clock */
__s32 wtom_clock_nsec;
+ struct timespec stamp_xtime; /* xtime as at tb_orig_stamp */
__u32 syscall_map_32[SYSCALL_MAP_SIZE]; /* map of syscalls */
__u32 dcache_block_size; /* L1 d-cache block size */
__u32 icache_block_size; /* L1 i-cache block size */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index d17edb4a2f9d..1308a86e9070 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -103,6 +103,10 @@ endif
obj-$(CONFIG_PPC64) += $(obj64-y)
+ifneq ($(CONFIG_XMON)$(CONFIG_KEXEC),)
+obj-y += ppc_save_regs.o
+endif
+
extra-$(CONFIG_PPC_FPU) += fpu.o
extra-$(CONFIG_PPC64) += entry_64.o
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 75c5dd0138fd..661d07d2146b 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -60,6 +60,7 @@ int main(void)
{
DEFINE(THREAD, offsetof(struct task_struct, thread));
DEFINE(MM, offsetof(struct task_struct, mm));
+ DEFINE(MMCONTEXTID, offsetof(struct mm_struct, context.id));
#ifdef CONFIG_PPC64
DEFINE(AUDITCONTEXT, offsetof(struct task_struct, audit_context));
#else
@@ -306,6 +307,7 @@ int main(void)
DEFINE(CFG_SYSCALL_MAP32, offsetof(struct vdso_data, syscall_map_32));
DEFINE(WTOM_CLOCK_SEC, offsetof(struct vdso_data, wtom_clock_sec));
DEFINE(WTOM_CLOCK_NSEC, offsetof(struct vdso_data, wtom_clock_nsec));
+ DEFINE(STAMP_XTIME, offsetof(struct vdso_data, stamp_xtime));
DEFINE(CFG_ICACHE_BLOCKSZ, offsetof(struct vdso_data, icache_block_size));
DEFINE(CFG_DCACHE_BLOCKSZ, offsetof(struct vdso_data, dcache_block_size));
DEFINE(CFG_ICACHE_LOGBLOCKSZ, offsetof(struct vdso_data, icache_log_block_size));
@@ -378,6 +380,10 @@ int main(void)
DEFINE(VCPU_FAULT_DEAR, offsetof(struct kvm_vcpu, arch.fault_dear));
DEFINE(VCPU_FAULT_ESR, offsetof(struct kvm_vcpu, arch.fault_esr));
#endif
+#ifdef CONFIG_44x
+ DEFINE(PGD_T_LOG2, PGD_T_LOG2);
+ DEFINE(PTE_T_LOG2, PTE_T_LOG2);
+#endif
return 0;
}
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 7e8719504f39..923f87aff20a 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -19,6 +19,7 @@
#include <asm/oprofile_impl.h>
#include <asm/cputable.h>
#include <asm/prom.h> /* for PTRRELOC on ARCH=ppc */
+#include <asm/mmu.h>
struct cpu_spec* cur_cpu_spec = NULL;
EXPORT_SYMBOL(cur_cpu_spec);
@@ -94,6 +95,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER3 (630)",
.cpu_features = CPU_FTRS_POWER3,
.cpu_user_features = COMMON_USER_PPC64|PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -109,6 +111,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER3 (630+)",
.cpu_features = CPU_FTRS_POWER3,
.cpu_user_features = COMMON_USER_PPC64|PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -124,6 +127,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "RS64-II (northstar)",
.cpu_features = CPU_FTRS_RS64,
.cpu_user_features = COMMON_USER_PPC64,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -139,6 +143,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "RS64-III (pulsar)",
.cpu_features = CPU_FTRS_RS64,
.cpu_user_features = COMMON_USER_PPC64,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -154,6 +159,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "RS64-III (icestar)",
.cpu_features = CPU_FTRS_RS64,
.cpu_user_features = COMMON_USER_PPC64,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -169,6 +175,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "RS64-IV (sstar)",
.cpu_features = CPU_FTRS_RS64,
.cpu_user_features = COMMON_USER_PPC64,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -184,6 +191,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER4 (gp)",
.cpu_features = CPU_FTRS_POWER4,
.cpu_user_features = COMMON_USER_POWER4,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -199,6 +207,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER4+ (gq)",
.cpu_features = CPU_FTRS_POWER4,
.cpu_user_features = COMMON_USER_POWER4,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -215,6 +224,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC970,
.cpu_user_features = COMMON_USER_POWER4 |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -233,6 +243,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC970,
.cpu_user_features = COMMON_USER_POWER4 |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -251,6 +262,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC970,
.cpu_user_features = COMMON_USER_POWER4 |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -269,6 +281,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC970,
.cpu_user_features = COMMON_USER_POWER4 |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -287,6 +300,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC970,
.cpu_user_features = COMMON_USER_POWER4 |
PPC_FEATURE_HAS_ALTIVEC_COMP,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 8,
@@ -303,6 +317,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER5 (gr)",
.cpu_features = CPU_FTRS_POWER5,
.cpu_user_features = COMMON_USER_POWER5,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -323,6 +338,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER5+ (gs)",
.cpu_features = CPU_FTRS_POWER5,
.cpu_user_features = COMMON_USER_POWER5_PLUS,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -339,6 +355,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER5+ (gs)",
.cpu_features = CPU_FTRS_POWER5,
.cpu_user_features = COMMON_USER_POWER5_PLUS,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -356,6 +373,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER5+",
.cpu_features = CPU_FTRS_POWER5,
.cpu_user_features = COMMON_USER_POWER5_PLUS,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.machine_check = machine_check_generic,
@@ -369,6 +387,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_POWER6,
.cpu_user_features = COMMON_USER_POWER6 |
PPC_FEATURE_POWER6_EXT,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -388,6 +407,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER6 (architected)",
.cpu_features = CPU_FTRS_POWER6,
.cpu_user_features = COMMON_USER_POWER6,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.machine_check = machine_check_generic,
@@ -400,6 +420,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER7 (architected)",
.cpu_features = CPU_FTRS_POWER7,
.cpu_user_features = COMMON_USER_POWER7,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.machine_check = machine_check_generic,
@@ -412,6 +433,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER7 (raw)",
.cpu_features = CPU_FTRS_POWER7,
.cpu_user_features = COMMON_USER_POWER7,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -434,6 +456,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_PPC64 |
PPC_FEATURE_CELL | PPC_FEATURE_HAS_ALTIVEC_COMP |
PPC_FEATURE_SMT,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 4,
@@ -449,6 +472,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "PA6T",
.cpu_features = CPU_FTRS_PA6T,
.cpu_user_features = COMMON_USER_PA6T,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 64,
.dcache_bsize = 64,
.num_pmcs = 6,
@@ -466,6 +490,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "POWER4 (compatible)",
.cpu_features = CPU_FTRS_COMPATIBLE,
.cpu_user_features = COMMON_USER_PPC64,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 128,
.dcache_bsize = 128,
.num_pmcs = 6,
@@ -483,6 +508,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_PPC601,
.cpu_user_features = COMMON_USER | PPC_FEATURE_601_INSTR |
PPC_FEATURE_UNIFIED_CACHE | PPC_FEATURE_NO_TB,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_generic,
@@ -494,6 +520,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "603",
.cpu_features = CPU_FTRS_603,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = 0,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -506,6 +533,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "603e",
.cpu_features = CPU_FTRS_603,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = 0,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -518,6 +546,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "603ev",
.cpu_features = CPU_FTRS_603,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = 0,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -530,6 +559,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "604",
.cpu_features = CPU_FTRS_604,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 2,
@@ -543,6 +573,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "604e",
.cpu_features = CPU_FTRS_604,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -556,6 +587,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "604r",
.cpu_features = CPU_FTRS_604,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -569,6 +601,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "604ev",
.cpu_features = CPU_FTRS_604,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -582,6 +615,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "740/750",
.cpu_features = CPU_FTRS_740_NOTAU,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -595,6 +629,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750CX",
.cpu_features = CPU_FTRS_750,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -608,6 +643,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750CX",
.cpu_features = CPU_FTRS_750,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -622,6 +658,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750CXe",
.cpu_features = CPU_FTRS_750,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -636,6 +673,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750CXe",
.cpu_features = CPU_FTRS_750,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -650,6 +688,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750CL",
.cpu_features = CPU_FTRS_750CL,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -664,6 +703,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "745/755",
.cpu_features = CPU_FTRS_750,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -678,6 +718,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750FX",
.cpu_features = CPU_FTRS_750FX1,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -692,6 +733,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750FX",
.cpu_features = CPU_FTRS_750FX2,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -706,6 +748,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750FX",
.cpu_features = CPU_FTRS_750FX,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -720,6 +763,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "750GX",
.cpu_features = CPU_FTRS_750GX,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -734,6 +778,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "740/750",
.cpu_features = CPU_FTRS_740,
.cpu_user_features = COMMON_USER | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -749,6 +794,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7400_NOTAU,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -764,6 +810,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7400,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -779,6 +826,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7400,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -794,6 +842,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7450_20,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -811,6 +860,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7450_21,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -828,6 +878,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7450_23,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -845,6 +896,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7455_1,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -862,6 +914,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7455_20,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -879,6 +932,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7455,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -896,6 +950,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7447_10,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -913,6 +968,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7447_10,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -929,6 +985,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "7447/7457",
.cpu_features = CPU_FTRS_7447,
.cpu_user_features = COMMON_USER | PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -946,6 +1003,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7447A,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -963,6 +1021,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_7448,
.cpu_user_features = COMMON_USER |
PPC_FEATURE_HAS_ALTIVEC_COMP | PPC_FEATURE_PPC_LE,
+ .mmu_features = MMU_FTR_HPTE_TABLE | MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 6,
@@ -979,6 +1038,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "82xx",
.cpu_features = CPU_FTRS_82XX,
.cpu_user_features = COMMON_USER,
+ .mmu_features = 0,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -991,6 +1051,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "G2_LE",
.cpu_features = CPU_FTRS_G2_LE,
.cpu_user_features = COMMON_USER,
+ .mmu_features = MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -1003,6 +1064,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e300c1",
.cpu_features = CPU_FTRS_E300,
.cpu_user_features = COMMON_USER,
+ .mmu_features = MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -1015,6 +1077,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e300c2",
.cpu_features = CPU_FTRS_E300C2,
.cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .mmu_features = MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -1027,6 +1090,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e300c3",
.cpu_features = CPU_FTRS_E300,
.cpu_user_features = COMMON_USER,
+ .mmu_features = MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -1041,6 +1105,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e300c4",
.cpu_features = CPU_FTRS_E300,
.cpu_user_features = COMMON_USER,
+ .mmu_features = MMU_FTR_USE_HIGH_BATS,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_603,
@@ -1056,6 +1121,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "(generic PPC)",
.cpu_features = CPU_FTRS_CLASSIC32,
.cpu_user_features = COMMON_USER,
+ .mmu_features = MMU_FTR_HPTE_TABLE,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_generic,
@@ -1071,6 +1137,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
* if the 8xx code is there.... */
.cpu_features = CPU_FTRS_8XX,
.cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .mmu_features = MMU_FTR_TYPE_8xx,
.icache_bsize = 16,
.dcache_bsize = 16,
.platform = "ppc823",
@@ -1083,6 +1150,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "403GC",
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 16,
.dcache_bsize = 16,
.machine_check = machine_check_4xx,
@@ -1095,6 +1163,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_NO_TB,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 16,
.dcache_bsize = 16,
.machine_check = machine_check_4xx,
@@ -1106,6 +1175,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "403G ??",
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 16,
.dcache_bsize = 16,
.machine_check = machine_check_4xx,
@@ -1118,6 +1188,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1130,6 +1201,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1142,6 +1214,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1154,6 +1227,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1166,6 +1240,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1178,6 +1253,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1190,6 +1266,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1202,6 +1279,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1213,6 +1291,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "405LP",
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1225,6 +1304,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1237,6 +1317,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1249,6 +1330,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1261,6 +1343,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1273,6 +1356,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1286,6 +1370,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1298,6 +1383,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_features = CPU_FTRS_40X,
.cpu_user_features = PPC_FEATURE_32 |
PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
+ .mmu_features = MMU_FTR_TYPE_40x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1312,6 +1398,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GR Rev. A",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1323,6 +1410,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440EP Rev. A",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440ep,
@@ -1335,6 +1423,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GR Rev. B",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1346,6 +1435,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440EP Rev. C",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440ep,
@@ -1358,6 +1448,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440EP Rev. B",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440ep,
@@ -1370,6 +1461,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GRX",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440grx,
@@ -1382,6 +1474,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440EPX",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440epx,
@@ -1394,6 +1487,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GP Rev. B",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1405,6 +1499,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GP Rev. C",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1416,6 +1511,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GX Rev. A",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440gx,
@@ -1428,6 +1524,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GX Rev. B",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440gx,
@@ -1440,6 +1537,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GX Rev. C",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440gx,
@@ -1452,6 +1550,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440GX Rev. F",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440gx,
@@ -1464,6 +1563,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440SP Rev. A",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1475,6 +1575,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440SPe Rev. A",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440spe,
@@ -1487,6 +1588,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440SPe Rev. B",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440spe,
@@ -1499,6 +1601,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "440 in Virtex-5 FXT",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_440x5,
@@ -1509,8 +1612,9 @@ static struct cpu_spec __initdata cpu_specs[] = {
.pvr_mask = 0xffff0002,
.pvr_value = 0x13020002,
.cpu_name = "460EX",
- .cpu_features = CPU_FTRS_44X,
+ .cpu_features = CPU_FTRS_440x6,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_460ex,
@@ -1521,8 +1625,9 @@ static struct cpu_spec __initdata cpu_specs[] = {
.pvr_mask = 0xffff0002,
.pvr_value = 0x13020000,
.cpu_name = "460GT",
- .cpu_features = CPU_FTRS_44X,
+ .cpu_features = CPU_FTRS_440x6,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.cpu_setup = __setup_cpu_460gt,
@@ -1535,6 +1640,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "(generic 44x PPC)",
.cpu_features = CPU_FTRS_44X,
.cpu_user_features = COMMON_USER_BOOKE,
+ .mmu_features = MMU_FTR_TYPE_44x,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_4xx,
@@ -1551,6 +1657,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_BOOKE |
PPC_FEATURE_HAS_EFP_SINGLE |
PPC_FEATURE_UNIFIED_CACHE,
+ .mmu_features = MMU_FTR_TYPE_FSL_E,
.dcache_bsize = 32,
.machine_check = machine_check_e200,
.platform = "ppc5554",
@@ -1565,6 +1672,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP |
PPC_FEATURE_UNIFIED_CACHE,
+ .mmu_features = MMU_FTR_TYPE_FSL_E,
.dcache_bsize = 32,
.machine_check = machine_check_e200,
.platform = "ppc5554",
@@ -1577,6 +1685,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_BOOKE |
PPC_FEATURE_HAS_EFP_SINGLE |
PPC_FEATURE_UNIFIED_CACHE,
+ .mmu_features = MMU_FTR_TYPE_FSL_E,
.dcache_bsize = 32,
.machine_check = machine_check_e200,
.platform = "ppc5554",
@@ -1591,6 +1700,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_BOOKE |
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP,
+ .mmu_features = MMU_FTR_TYPE_FSL_E,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -1608,6 +1718,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP |
PPC_FEATURE_HAS_EFP_DOUBLE_COMP,
+ .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS,
.icache_bsize = 32,
.dcache_bsize = 32,
.num_pmcs = 4,
@@ -1622,6 +1733,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_name = "e500mc",
.cpu_features = CPU_FTRS_E500MC,
.cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
+ .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS,
.icache_bsize = 64,
.dcache_bsize = 64,
.num_pmcs = 4,
@@ -1638,6 +1750,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
.cpu_user_features = COMMON_USER_BOOKE |
PPC_FEATURE_HAS_SPE_COMP |
PPC_FEATURE_HAS_EFP_SINGLE_COMP,
+ .mmu_features = MMU_FTR_TYPE_FSL_E,
.icache_bsize = 32,
.dcache_bsize = 32,
.machine_check = machine_check_e500,
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index 3a6eaa876ee1..1c5c8a6fc129 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -120,6 +120,26 @@ static inline void dma_direct_unmap_page(struct device *dev,
{
}
+#ifdef CONFIG_NOT_COHERENT_CACHE
+static inline void dma_direct_sync_sg(struct device *dev,
+ struct scatterlist *sgl, int nents,
+ enum dma_data_direction direction)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sgl, sg, nents, i)
+ __dma_sync_page(sg_page(sg), sg->offset, sg->length, direction);
+}
+
+static inline void dma_direct_sync_single_range(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ __dma_sync(bus_to_virt(dma_handle+offset), size, direction);
+}
+#endif
+
struct dma_mapping_ops dma_direct_ops = {
.alloc_coherent = dma_direct_alloc_coherent,
.free_coherent = dma_direct_free_coherent,
@@ -128,5 +148,11 @@ struct dma_mapping_ops dma_direct_ops = {
.dma_supported = dma_direct_dma_supported,
.map_page = dma_direct_map_page,
.unmap_page = dma_direct_unmap_page,
+#ifdef CONFIG_NOT_COHERENT_CACHE
+ .sync_single_range_for_cpu = dma_direct_sync_single_range,
+ .sync_single_range_for_device = dma_direct_sync_single_range,
+ .sync_sg_for_cpu = dma_direct_sync_sg,
+ .sync_sg_for_device = dma_direct_sync_sg,
+#endif
};
EXPORT_SYMBOL(dma_direct_ops);
diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S
index 0c326823c6d4..a1c4cfd25ded 100644
--- a/arch/powerpc/kernel/head_32.S
+++ b/arch/powerpc/kernel/head_32.S
@@ -31,6 +31,7 @@
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/ptrace.h>
+#include <asm/bug.h>
/* 601 only have IBAT; cr0.eq is set on 601 when using this macro */
#define LOAD_BAT(n, reg, RA, RB) \
@@ -182,7 +183,8 @@ __after_mmu_off:
bl reloc_offset
mr r26,r3
addis r4,r3,KERNELBASE@h /* current address of _start */
- cmpwi 0,r4,0 /* are we already running at 0? */
+ lis r5,PHYSICAL_START@h
+ cmplw 0,r4,r5 /* already running at PHYSICAL_START? */
bne relocate_kernel
/*
* we now have the 1st 16M of ram mapped with the bats.
@@ -810,13 +812,13 @@ giveup_altivec:
/*
* This code is jumped to from the startup code to copy
- * the kernel image to physical address 0.
+ * the kernel image to physical address PHYSICAL_START.
*/
relocate_kernel:
addis r9,r26,klimit@ha /* fetch klimit */
lwz r25,klimit@l(r9)
addis r25,r25,-KERNELBASE@h
- li r3,0 /* Destination base address */
+ lis r3,PHYSICAL_START@h /* Destination base address */
li r6,0 /* Destination offset */
li r5,0x4000 /* # bytes of memory to copy */
bl copy_and_flush /* copy the first 0x4000 bytes */
@@ -989,12 +991,12 @@ load_up_mmu:
LOAD_BAT(1,r3,r4,r5)
LOAD_BAT(2,r3,r4,r5)
LOAD_BAT(3,r3,r4,r5)
-BEGIN_FTR_SECTION
+BEGIN_MMU_FTR_SECTION
LOAD_BAT(4,r3,r4,r5)
LOAD_BAT(5,r3,r4,r5)
LOAD_BAT(6,r3,r4,r5)
LOAD_BAT(7,r3,r4,r5)
-END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
blr
/*
@@ -1070,9 +1072,14 @@ start_here:
RFI
/*
+ * void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next);
+ *
* Set up the segment registers for a new context.
*/
-_ENTRY(set_context)
+_ENTRY(switch_mmu_context)
+ lwz r3,MMCONTEXTID(r4)
+ cmpwi cr0,r3,0
+ blt- 4f
mulli r3,r3,897 /* multiply context by skew factor */
rlwinm r3,r3,4,8,27 /* VSID = (context & 0xfffff) << 4 */
addis r3,r3,0x6000 /* Set Ks, Ku bits */
@@ -1083,6 +1090,7 @@ _ENTRY(set_context)
/* Context switch the PTE pointer for the Abatron BDI2000.
* The PGDIR is passed as second argument.
*/
+ lwz r4,MM_PGD(r4)
lis r5, KERNELBASE@h
lwz r5, 0xf0(r5)
stw r4, 0x4(r5)
@@ -1098,6 +1106,9 @@ _ENTRY(set_context)
sync
isync
blr
+4: trap
+ EMIT_BUG_ENTRY 4b,__FILE__,__LINE__,0
+ blr
/*
* An undocumented "feature" of 604e requires that the v bit
@@ -1131,7 +1142,7 @@ clear_bats:
mtspr SPRN_IBAT2L,r10
mtspr SPRN_IBAT3U,r10
mtspr SPRN_IBAT3L,r10
-BEGIN_FTR_SECTION
+BEGIN_MMU_FTR_SECTION
/* Here's a tweak: at this point, CPU setup have
* not been called yet, so HIGH_BAT_EN may not be
* set in HID0 for the 745x processors. However, it
@@ -1154,7 +1165,7 @@ BEGIN_FTR_SECTION
mtspr SPRN_IBAT6L,r10
mtspr SPRN_IBAT7U,r10
mtspr SPRN_IBAT7L,r10
-END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
blr
flush_tlbs:
@@ -1178,11 +1189,11 @@ mmu_off:
/*
* Use the first pair of BAT registers to map the 1st 16MB
- * of RAM to KERNELBASE. From this point on we can't safely
+ * of RAM to PAGE_OFFSET. From this point on we can't safely
* call OF any more.
*/
initial_bats:
- lis r11,KERNELBASE@h
+ lis r11,PAGE_OFFSET@h
mfspr r9,SPRN_PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
cmpwi 0,r9,1
diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S
index f3a1ea9d7fe4..b56fecc93a16 100644
--- a/arch/powerpc/kernel/head_44x.S
+++ b/arch/powerpc/kernel/head_44x.S
@@ -69,6 +69,17 @@ _ENTRY(_start);
li r24,0 /* CPU number */
/*
+ * In case the firmware didn't do it, we apply some workarounds
+ * that are good for all 440 core variants here
+ */
+ mfspr r3,SPRN_CCR0
+ rlwinm r3,r3,0,0,27 /* disable icache prefetch */
+ isync
+ mtspr SPRN_CCR0,r3
+ isync
+ sync
+
+/*
* Set up the initial MMU state
*
* We are still executing code at the virtual address
@@ -391,12 +402,14 @@ interrupt_base:
rlwimi r13,r12,10,30,30
/* Load the PTE */
- rlwinm r12, r10, 13, 19, 29 /* Compute pgdir/pmd offset */
+ /* Compute pgdir/pmd offset */
+ rlwinm r12, r10, PPC44x_PGD_OFF_SHIFT, PPC44x_PGD_OFF_MASK_BIT, 29
lwzx r11, r12, r11 /* Get pgd/pmd entry */
rlwinm. r12, r11, 0, 0, 20 /* Extract pt base address */
beq 2f /* Bail if no table */
- rlwimi r12, r10, 23, 20, 28 /* Compute pte address */
+ /* Compute pte address */
+ rlwimi r12, r10, PPC44x_PTE_ADD_SHIFT, PPC44x_PTE_ADD_MASK_BIT, 28
lwz r11, 0(r12) /* Get high word of pte entry */
lwz r12, 4(r12) /* Get low word of pte entry */
@@ -485,12 +498,14 @@ tlb_44x_patch_hwater_D:
/* Make up the required permissions */
li r13,_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_HWEXEC
- rlwinm r12, r10, 13, 19, 29 /* Compute pgdir/pmd offset */
+ /* Compute pgdir/pmd offset */
+ rlwinm r12, r10, PPC44x_PGD_OFF_SHIFT, PPC44x_PGD_OFF_MASK_BIT, 29
lwzx r11, r12, r11 /* Get pgd/pmd entry */
rlwinm. r12, r11, 0, 0, 20 /* Extract pt base address */
beq 2f /* Bail if no table */
- rlwimi r12, r10, 23, 20, 28 /* Compute pte address */
+ /* Compute pte address */
+ rlwimi r12, r10, PPC44x_PTE_ADD_SHIFT, PPC44x_PTE_ADD_MASK_BIT, 28
lwz r11, 0(r12) /* Get high word of pte entry */
lwz r12, 4(r12) /* Get low word of pte entry */
@@ -554,15 +569,16 @@ tlb_44x_patch_hwater_I:
*/
finish_tlb_load:
/* Combine RPN & ERPN an write WS 0 */
- rlwimi r11,r12,0,0,19
+ rlwimi r11,r12,0,0,31-PAGE_SHIFT
tlbwe r11,r13,PPC44x_TLB_XLAT
/*
* Create WS1. This is the faulting address (EPN),
* page size, and valid flag.
*/
- li r11,PPC44x_TLB_VALID | PPC44x_TLB_4K
- rlwimi r10,r11,0,20,31 /* Insert valid and page size*/
+ li r11,PPC44x_TLB_VALID | PPC44x_TLBE_SIZE
+ /* Insert valid and page size */
+ rlwimi r10,r11,0,PPC44x_PTE_ADD_MASK_BIT,31
tlbwe r10,r13,PPC44x_TLB_PAGEID /* Write PAGEID */
/* And WS 2 */
@@ -634,12 +650,12 @@ _GLOBAL(set_context)
* goes at the beginning of the data segment, which is page-aligned.
*/
.data
- .align 12
+ .align PAGE_SHIFT
.globl sdata
sdata:
.globl empty_zero_page
empty_zero_page:
- .space 4096
+ .space PAGE_SIZE
/*
* To support >32-bit physical addresses, we use an 8KB pgdir.
diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S
index 590304c24dad..11b549acc034 100644
--- a/arch/powerpc/kernel/head_fsl_booke.S
+++ b/arch/powerpc/kernel/head_fsl_booke.S
@@ -92,6 +92,7 @@ _ENTRY(_start);
* if needed
*/
+_ENTRY(__early_start)
/* 1. Find the index of the entry we're executing in */
bl invstr /* Find our address */
invstr: mflr r6 /* Make it accessible */
@@ -235,36 +236,40 @@ skpinv: addi r6,r6,1 /* Increment */
tlbivax 0,r9
TLBSYNC
+/* The mapping only needs to be cache-coherent on SMP */
+#ifdef CONFIG_SMP
+#define M_IF_SMP MAS2_M
+#else
+#define M_IF_SMP 0
+#endif
+
/* 6. Setup KERNELBASE mapping in TLB1[0] */
lis r6,0x1000 /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */
mtspr SPRN_MAS0,r6
lis r6,(MAS1_VALID|MAS1_IPROT)@h
ori r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_64M))@l
mtspr SPRN_MAS1,r6
- li r7,0
- lis r6,PAGE_OFFSET@h
- ori r6,r6,PAGE_OFFSET@l
- rlwimi r6,r7,0,20,31
+ lis r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@h
+ ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOKE_PAGESZ_64M, M_IF_SMP)@l
mtspr SPRN_MAS2,r6
mtspr SPRN_MAS3,r8
tlbwe
/* 7. Jump to KERNELBASE mapping */
- lis r6,KERNELBASE@h
- ori r6,r6,KERNELBASE@l
- rlwimi r6,r7,0,20,31
+ lis r6,(KERNELBASE & ~0xfff)@h
+ ori r6,r6,(KERNELBASE & ~0xfff)@l
lis r7,MSR_KERNEL@h
ori r7,r7,MSR_KERNEL@l
bl 1f /* Find our address */
1: mflr r9
rlwimi r6,r9,0,20,31
- addi r6,r6,24
+ addi r6,r6,(2f - 1b)
mtspr SPRN_SRR0,r6
mtspr SPRN_SRR1,r7
rfi /* start execution out of TLB1[0] entry */
/* 8. Clear out the temp mapping */
- lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */
+2: lis r7,0x1000 /* Set MAS0(TLBSEL) = 1 */
rlwimi r7,r5,16,4,15 /* Setup MAS0 = TLBSEL | ESEL(r5) */
mtspr SPRN_MAS0,r7
tlbre
@@ -344,6 +349,15 @@ skpinv: addi r6,r6,1 /* Increment */
mtspr SPRN_DBSR,r2
#endif
+#ifdef CONFIG_SMP
+ /* Check to see if we're the second processor, and jump
+ * to the secondary_start code if so
+ */
+ mfspr r24,SPRN_PIR
+ cmpwi r24,0
+ bne __secondary_start
+#endif
+
/*
* This is where the main kernel code starts.
*/
@@ -685,12 +699,13 @@ interrupt_base:
/* SPE Floating Point Data */
#ifdef CONFIG_SPE
EXCEPTION(0x2030, SPEFloatingPointData, SPEFloatingPointException, EXC_XFER_EE);
-#else
- EXCEPTION(0x2040, SPEFloatingPointData, unknown_exception, EXC_XFER_EE)
-#endif /* CONFIG_SPE */
/* SPE Floating Point Round */
+ EXCEPTION(0x2050, SPEFloatingPointRound, SPEFloatingPointRoundException, EXC_XFER_EE)
+#else
+ EXCEPTION(0x2040, SPEFloatingPointData, unknown_exception, EXC_XFER_EE)
EXCEPTION(0x2050, SPEFloatingPointRound, unknown_exception, EXC_XFER_EE)
+#endif /* CONFIG_SPE */
/* Performance Monitor */
EXCEPTION(0x2060, PerformanceMonitor, performance_monitor_exception, EXC_XFER_STD)
@@ -735,6 +750,9 @@ finish_tlb_load:
#else
rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */
#endif
+#ifdef CONFIG_SMP
+ ori r12, r12, MAS2_M
+#endif
mtspr SPRN_MAS2, r12
li r10, (_PAGE_HWEXEC | _PAGE_PRESENT)
@@ -746,15 +764,15 @@ finish_tlb_load:
iseleq r12, r12, r10
#ifdef CONFIG_PTE_64BIT
-2: rlwimi r12, r13, 24, 0, 7 /* grab RPN[32:39] */
+ rlwimi r12, r13, 24, 0, 7 /* grab RPN[32:39] */
rlwimi r12, r11, 24, 8, 19 /* grab RPN[40:51] */
mtspr SPRN_MAS3, r12
-BEGIN_FTR_SECTION
+BEGIN_MMU_FTR_SECTION
srwi r10, r13, 8 /* grab RPN[8:31] */
mtspr SPRN_MAS7, r10
-END_FTR_SECTION_IFSET(CPU_FTR_BIG_PHYS)
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS)
#else
-2: rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */
+ rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */
mtspr SPRN_MAS3, r11
#endif
#ifdef CONFIG_E200
@@ -1037,6 +1055,63 @@ _GLOBAL(flush_dcache_L1)
blr
+#ifdef CONFIG_SMP
+/* When we get here, r24 needs to hold the CPU # */
+ .globl __secondary_start
+__secondary_start:
+ lis r3,__secondary_hold_acknowledge@h
+ ori r3,r3,__secondary_hold_acknowledge@l
+ stw r24,0(r3)
+
+ li r3,0
+ mr r4,r24 /* Why? */
+ bl call_setup_cpu
+
+ lis r3,tlbcam_index@ha
+ lwz r3,tlbcam_index@l(r3)
+ mtctr r3
+ li r26,0 /* r26 safe? */
+
+ /* Load each CAM entry */
+1: mr r3,r26
+ bl loadcam_entry
+ addi r26,r26,1
+ bdnz 1b
+
+ /* get current_thread_info and current */
+ lis r1,secondary_ti@ha
+ lwz r1,secondary_ti@l(r1)
+ lwz r2,TI_TASK(r1)
+
+ /* stack */
+ addi r1,r1,THREAD_SIZE-STACK_FRAME_OVERHEAD
+ li r0,0
+ stw r0,0(r1)
+
+ /* ptr to current thread */
+ addi r4,r2,THREAD /* address of our thread_struct */
+ mtspr SPRN_SPRG3,r4
+
+ /* Setup the defaults for TLB entries */
+ li r4,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l
+ mtspr SPRN_MAS4,r4
+
+ /* Jump to start_secondary */
+ lis r4,MSR_KERNEL@h
+ ori r4,r4,MSR_KERNEL@l
+ lis r3,start_secondary@h
+ ori r3,r3,start_secondary@l
+ mtspr SPRN_SRR0,r3
+ mtspr SPRN_SRR1,r4
+ sync
+ rfi
+ sync
+
+ .globl __secondary_hold_acknowledge
+__secondary_hold_acknowledge:
+ .long -1
+#endif
+
/*
* We put a few things here that have to be page-aligned. This stuff
* goes at the beginning of the data segment, which is page-aligned.
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index 64299d28f364..6e3f62493659 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -47,7 +47,7 @@
#include <asm/abs_addr.h>
static struct device ibmebus_bus_device = { /* fake "parent" device */
- .bus_id = "ibmebus",
+ .init_name = "ibmebus",
};
struct bus_type ibmebus_bus_type;
@@ -231,6 +231,7 @@ void ibmebus_free_irq(u32 ist, void *dev_id)
unsigned int irq = irq_find_mapping(NULL, ist);
free_irq(irq, dev_id);
+ irq_dispose_mapping(irq);
}
EXPORT_SYMBOL(ibmebus_free_irq);
diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c
index ac2a21f45c75..b3abebb7ee64 100644
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -13,13 +13,17 @@
#include <linux/reboot.h>
#include <linux/threads.h>
#include <linux/lmb.h>
+#include <linux/of.h>
#include <asm/machdep.h>
#include <asm/prom.h>
+#include <asm/sections.h>
void machine_crash_shutdown(struct pt_regs *regs)
{
if (ppc_md.machine_crash_shutdown)
ppc_md.machine_crash_shutdown(regs);
+ else
+ default_machine_crash_shutdown(regs);
}
/*
@@ -31,11 +35,8 @@ int machine_kexec_prepare(struct kimage *image)
{
if (ppc_md.machine_kexec_prepare)
return ppc_md.machine_kexec_prepare(image);
- /*
- * Fail if platform doesn't provide its own machine_kexec_prepare
- * implementation.
- */
- return -ENOSYS;
+ else
+ return default_machine_kexec_prepare(image);
}
void machine_kexec_cleanup(struct kimage *image)
@@ -52,13 +53,11 @@ void machine_kexec(struct kimage *image)
{
if (ppc_md.machine_kexec)
ppc_md.machine_kexec(image);
- else {
- /*
- * Fall back to normal restart if platform doesn't provide
- * its own kexec function, and user insist to kexec...
- */
- machine_restart(NULL);
- }
+ else
+ default_machine_kexec(image);
+
+ /* Fall back to normal restart if we're still alive. */
+ machine_restart(NULL);
for(;;);
}
@@ -118,3 +117,71 @@ int overlaps_crashkernel(unsigned long start, unsigned long size)
{
return (start + size) > crashk_res.start && start <= crashk_res.end;
}
+
+/* Values we need to export to the second kernel via the device tree. */
+static unsigned long kernel_end;
+static unsigned long crashk_size;
+
+static struct property kernel_end_prop = {
+ .name = "linux,kernel-end",
+ .length = sizeof(unsigned long),
+ .value = &kernel_end,
+};
+
+static struct property crashk_base_prop = {
+ .name = "linux,crashkernel-base",
+ .length = sizeof(unsigned long),
+ .value = &crashk_res.start,
+};
+
+static struct property crashk_size_prop = {
+ .name = "linux,crashkernel-size",
+ .length = sizeof(unsigned long),
+ .value = &crashk_size,
+};
+
+static void __init export_crashk_values(struct device_node *node)
+{
+ struct property *prop;
+
+ /* There might be existing crash kernel properties, but we can't
+ * be sure what's in them, so remove them. */
+ prop = of_find_property(node, "linux,crashkernel-base", NULL);
+ if (prop)
+ prom_remove_property(node, prop);
+
+ prop = of_find_property(node, "linux,crashkernel-size", NULL);
+ if (prop)
+ prom_remove_property(node, prop);
+
+ if (crashk_res.start != 0) {
+ prom_add_property(node, &crashk_base_prop);
+ crashk_size = crashk_res.end - crashk_res.start + 1;
+ prom_add_property(node, &crashk_size_prop);
+ }
+}
+
+static int __init kexec_setup(void)
+{
+ struct device_node *node;
+ struct property *prop;
+
+ node = of_find_node_by_path("/chosen");
+ if (!node)
+ return -ENOENT;
+
+ /* remove any stale properties so ours can be found */
+ prop = of_find_property(node, kernel_end_prop.name, NULL);
+ if (prop)
+ prom_remove_property(node, prop);
+
+ /* information needed by userspace when using default_machine_kexec */
+ kernel_end = __pa(_end);
+ prom_add_property(node, &kernel_end_prop);
+
+ export_crashk_values(node);
+
+ of_node_put(node);
+ return 0;
+}
+late_initcall(kexec_setup);
diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c
index 3c4ca046e854..49e705fcee6d 100644
--- a/arch/powerpc/kernel/machine_kexec_64.c
+++ b/arch/powerpc/kernel/machine_kexec_64.c
@@ -289,7 +289,7 @@ void default_machine_kexec(struct kimage *image)
}
/* Values we need to export to the second kernel via the device tree. */
-static unsigned long htab_base, kernel_end;
+static unsigned long htab_base;
static struct property htab_base_prop = {
.name = "linux,htab-base",
@@ -303,25 +303,20 @@ static struct property htab_size_prop = {
.value = &htab_size_bytes,
};
-static struct property kernel_end_prop = {
- .name = "linux,kernel-end",
- .length = sizeof(unsigned long),
- .value = &kernel_end,
-};
-
-static void __init export_htab_values(void)
+static int __init export_htab_values(void)
{
struct device_node *node;
struct property *prop;
+ /* On machines with no htab htab_address is NULL */
+ if (!htab_address)
+ return -ENODEV;
+
node = of_find_node_by_path("/chosen");
if (!node)
- return;
+ return -ENODEV;
/* remove any stale propertys so ours can be found */
- prop = of_find_property(node, kernel_end_prop.name, NULL);
- if (prop)
- prom_remove_property(node, prop);
prop = of_find_property(node, htab_base_prop.name, NULL);
if (prop)
prom_remove_property(node, prop);
@@ -329,68 +324,11 @@ static void __init export_htab_values(void)
if (prop)
prom_remove_property(node, prop);
- /* information needed by userspace when using default_machine_kexec */
- kernel_end = __pa(_end);
- prom_add_property(node, &kernel_end_prop);
-
- /* On machines with no htab htab_address is NULL */
- if (NULL == htab_address)
- goto out;
-
htab_base = __pa(htab_address);
prom_add_property(node, &htab_base_prop);
prom_add_property(node, &htab_size_prop);
- out:
- of_node_put(node);
-}
-
-static struct property crashk_base_prop = {
- .name = "linux,crashkernel-base",
- .length = sizeof(unsigned long),
- .value = &crashk_res.start,
-};
-
-static unsigned long crashk_size;
-
-static struct property crashk_size_prop = {
- .name = "linux,crashkernel-size",
- .length = sizeof(unsigned long),
- .value = &crashk_size,
-};
-
-static void __init export_crashk_values(void)
-{
- struct device_node *node;
- struct property *prop;
-
- node = of_find_node_by_path("/chosen");
- if (!node)
- return;
-
- /* There might be existing crash kernel properties, but we can't
- * be sure what's in them, so remove them. */
- prop = of_find_property(node, "linux,crashkernel-base", NULL);
- if (prop)
- prom_remove_property(node, prop);
-
- prop = of_find_property(node, "linux,crashkernel-size", NULL);
- if (prop)
- prom_remove_property(node, prop);
-
- if (crashk_res.start != 0) {
- prom_add_property(node, &crashk_base_prop);
- crashk_size = crashk_res.end - crashk_res.start + 1;
- prom_add_property(node, &crashk_size_prop);
- }
-
of_node_put(node);
-}
-
-static int __init kexec_setup(void)
-{
- export_htab_values();
- export_crashk_values();
return 0;
}
-__initcall(kexec_setup);
+late_initcall(export_htab_values);
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index 5c33bc14bd9f..15f28e0de78d 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -29,6 +29,7 @@
#include <asm/asm-offsets.h>
#include <asm/processor.h>
#include <asm/kexec.h>
+#include <asm/bug.h>
.text
@@ -271,231 +272,6 @@ _GLOBAL(real_writeb)
#endif /* CONFIG_40x */
-/*
- * Flush MMU TLB
- */
-#ifndef CONFIG_FSL_BOOKE
-_GLOBAL(_tlbil_all)
-_GLOBAL(_tlbil_pid)
-#endif
-_GLOBAL(_tlbia)
-#if defined(CONFIG_40x)
- sync /* Flush to memory before changing mapping */
- tlbia
- isync /* Flush shadow TLB */
-#elif defined(CONFIG_44x)
- li r3,0
- sync
-
- /* Load high watermark */
- lis r4,tlb_44x_hwater@ha
- lwz r5,tlb_44x_hwater@l(r4)
-
-1: tlbwe r3,r3,PPC44x_TLB_PAGEID
- addi r3,r3,1
- cmpw 0,r3,r5
- ble 1b
-
- isync
-#elif defined(CONFIG_FSL_BOOKE)
- /* Invalidate all entries in TLB0 */
- li r3, 0x04
- tlbivax 0,3
- /* Invalidate all entries in TLB1 */
- li r3, 0x0c
- tlbivax 0,3
- msync
-#ifdef CONFIG_SMP
- tlbsync
-#endif /* CONFIG_SMP */
-#else /* !(CONFIG_40x || CONFIG_44x || CONFIG_FSL_BOOKE) */
-#if defined(CONFIG_SMP)
- rlwinm r8,r1,0,0,(31-THREAD_SHIFT)
- lwz r8,TI_CPU(r8)
- oris r8,r8,10
- mfmsr r10
- SYNC
- rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */
- rlwinm r0,r0,0,28,26 /* clear DR */
- mtmsr r0
- SYNC_601
- isync
- lis r9,mmu_hash_lock@h
- ori r9,r9,mmu_hash_lock@l
- tophys(r9,r9)
-10: lwarx r7,0,r9
- cmpwi 0,r7,0
- bne- 10b
- stwcx. r8,0,r9
- bne- 10b
- sync
- tlbia
- sync
- TLBSYNC
- li r0,0
- stw r0,0(r9) /* clear mmu_hash_lock */
- mtmsr r10
- SYNC_601
- isync
-#else /* CONFIG_SMP */
- sync
- tlbia
- sync
-#endif /* CONFIG_SMP */
-#endif /* ! defined(CONFIG_40x) */
- blr
-
-/*
- * Flush MMU TLB for a particular address
- */
-#ifndef CONFIG_FSL_BOOKE
-_GLOBAL(_tlbil_va)
-#endif
-_GLOBAL(_tlbie)
-#if defined(CONFIG_40x)
- /* We run the search with interrupts disabled because we have to change
- * the PID and I don't want to preempt when that happens.
- */
- mfmsr r5
- mfspr r6,SPRN_PID
- wrteei 0
- mtspr SPRN_PID,r4
- tlbsx. r3, 0, r3
- mtspr SPRN_PID,r6
- wrtee r5
- bne 10f
- sync
- /* There are only 64 TLB entries, so r3 < 64, which means bit 25 is clear.
- * Since 25 is the V bit in the TLB_TAG, loading this value will invalidate
- * the TLB entry. */
- tlbwe r3, r3, TLB_TAG
- isync
-10:
-
-#elif defined(CONFIG_44x)
- mfspr r5,SPRN_MMUCR
- rlwimi r5,r4,0,24,31 /* Set TID */
-
- /* We have to run the search with interrupts disabled, even critical
- * and debug interrupts (in fact the only critical exceptions we have
- * are debug and machine check). Otherwise an interrupt which causes
- * a TLB miss can clobber the MMUCR between the mtspr and the tlbsx. */
- mfmsr r4
- lis r6,(MSR_EE|MSR_CE|MSR_ME|MSR_DE)@ha
- addi r6,r6,(MSR_EE|MSR_CE|MSR_ME|MSR_DE)@l
- andc r6,r4,r6
- mtmsr r6
- mtspr SPRN_MMUCR,r5
- tlbsx. r3, 0, r3
- mtmsr r4
- bne 10f
- sync
- /* There are only 64 TLB entries, so r3 < 64,
- * which means bit 22, is clear. Since 22 is
- * the V bit in the TLB_PAGEID, loading this
- * value will invalidate the TLB entry.
- */
- tlbwe r3, r3, PPC44x_TLB_PAGEID
- isync
-10:
-#elif defined(CONFIG_FSL_BOOKE)
- rlwinm r4, r3, 0, 0, 19
- ori r5, r4, 0x08 /* TLBSEL = 1 */
- tlbivax 0, r4
- tlbivax 0, r5
- msync
-#if defined(CONFIG_SMP)
- tlbsync
-#endif /* CONFIG_SMP */
-#else /* !(CONFIG_40x || CONFIG_44x || CONFIG_FSL_BOOKE) */
-#if defined(CONFIG_SMP)
- rlwinm r8,r1,0,0,(31-THREAD_SHIFT)
- lwz r8,TI_CPU(r8)
- oris r8,r8,11
- mfmsr r10
- SYNC
- rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */
- rlwinm r0,r0,0,28,26 /* clear DR */
- mtmsr r0
- SYNC_601
- isync
- lis r9,mmu_hash_lock@h
- ori r9,r9,mmu_hash_lock@l
- tophys(r9,r9)
-10: lwarx r7,0,r9
- cmpwi 0,r7,0
- bne- 10b
- stwcx. r8,0,r9
- bne- 10b
- eieio
- tlbie r3
- sync
- TLBSYNC
- li r0,0
- stw r0,0(r9) /* clear mmu_hash_lock */
- mtmsr r10
- SYNC_601
- isync
-#else /* CONFIG_SMP */
- tlbie r3
- sync
-#endif /* CONFIG_SMP */
-#endif /* ! CONFIG_40x */
- blr
-
-#if defined(CONFIG_FSL_BOOKE)
-/*
- * Flush MMU TLB, but only on the local processor (no broadcast)
- */
-_GLOBAL(_tlbil_all)
-#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
- MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
- li r3,(MMUCSR0_TLBFI)@l
- mtspr SPRN_MMUCSR0, r3
-1:
- mfspr r3,SPRN_MMUCSR0
- andi. r3,r3,MMUCSR0_TLBFI@l
- bne 1b
- blr
-
-/*
- * Flush MMU TLB for a particular process id, but only on the local processor
- * (no broadcast)
- */
-_GLOBAL(_tlbil_pid)
-/* we currently do an invalidate all since we don't have per pid invalidate */
- li r3,(MMUCSR0_TLBFI)@l
- mtspr SPRN_MMUCSR0, r3
-1:
- mfspr r3,SPRN_MMUCSR0
- andi. r3,r3,MMUCSR0_TLBFI@l
- bne 1b
- msync
- isync
- blr
-
-/*
- * Flush MMU TLB for a particular address, but only on the local processor
- * (no broadcast)
- */
-_GLOBAL(_tlbil_va)
- mfmsr r10
- wrteei 0
- slwi r4,r4,16
- mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
- tlbsx 0,r3
- mfspr r4,SPRN_MAS1 /* check valid */
- andis. r3,r4,MAS1_VALID@h
- beq 1f
- rlwinm r4,r4,0,1,31
- mtspr SPRN_MAS1,r4
- tlbwe
- msync
- isync
-1: wrtee r10
- blr
-#endif /* CONFIG_FSL_BOOKE */
-
/*
* Flush instruction cache.
@@ -650,8 +426,8 @@ _GLOBAL(__flush_dcache_icache)
BEGIN_FTR_SECTION
blr
END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
- rlwinm r3,r3,0,0,19 /* Get page base address */
- li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */
+ rlwinm r3,r3,0,0,31-PAGE_SHIFT /* Get page base address */
+ li r4,PAGE_SIZE/L1_CACHE_BYTES /* Number of lines in a page */
mtctr r4
mr r6,r3
0: dcbst 0,r3 /* Write line to ram */
@@ -691,8 +467,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
rlwinm r0,r10,0,28,26 /* clear DR */
mtmsr r0
isync
- rlwinm r3,r3,0,0,19 /* Get page base address */
- li r4,4096/L1_CACHE_BYTES /* Number of lines in a page */
+ rlwinm r3,r3,0,0,31-PAGE_SHIFT /* Get page base address */
+ li r4,PAGE_SIZE/L1_CACHE_BYTES /* Number of lines in a page */
mtctr r4
mr r6,r3
0: dcbst 0,r3 /* Write line to ram */
@@ -716,7 +492,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
* void clear_pages(void *page, int order) ;
*/
_GLOBAL(clear_pages)
- li r0,4096/L1_CACHE_BYTES
+ li r0,PAGE_SIZE/L1_CACHE_BYTES
slw r0,r0,r4
mtctr r0
#ifdef CONFIG_8xx
@@ -774,7 +550,7 @@ _GLOBAL(copy_page)
dcbt r5,r4
li r11,L1_CACHE_BYTES+4
#endif /* MAX_COPY_PREFETCH */
- li r0,4096/L1_CACHE_BYTES - MAX_COPY_PREFETCH
+ li r0,PAGE_SIZE/L1_CACHE_BYTES - MAX_COPY_PREFETCH
crclr 4*cr0+eq
2:
mtctr r0
diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c
index 7ff292475269..43e7e3a7f130 100644
--- a/arch/powerpc/kernel/module.c
+++ b/arch/powerpc/kernel/module.c
@@ -78,6 +78,12 @@ int module_finalize(const Elf_Ehdr *hdr,
(void *)sect->sh_addr,
(void *)sect->sh_addr + sect->sh_size);
+ sect = find_section(hdr, sechdrs, "__mmu_ftr_fixup");
+ if (sect != NULL)
+ do_feature_fixups(cur_cpu_spec->mmu_features,
+ (void *)sect->sh_addr,
+ (void *)sect->sh_addr + sect->sh_size);
+
#ifdef CONFIG_PPC64
sect = find_section(hdr, sechdrs, "__fw_ftr_fixup");
if (sect != NULL)
diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c
index f3c9cae01dd5..fa983a59c4ce 100644
--- a/arch/powerpc/kernel/of_device.c
+++ b/arch/powerpc/kernel/of_device.c
@@ -14,7 +14,6 @@ static void of_device_make_bus_id(struct of_device *dev)
{
static atomic_t bus_no_reg_magic;
struct device_node *node = dev->node;
- char *name = dev->dev.bus_id;
const u32 *reg;
u64 addr;
int magic;
@@ -27,14 +26,12 @@ static void of_device_make_bus_id(struct of_device *dev)
reg = of_get_property(node, "dcr-reg", NULL);
if (reg) {
#ifdef CONFIG_PPC_DCR_NATIVE
- snprintf(name, BUS_ID_SIZE, "d%x.%s",
- *reg, node->name);
+ dev_set_name(&dev->dev, "d%x.%s", *reg, node->name);
#else /* CONFIG_PPC_DCR_NATIVE */
addr = of_translate_dcr_address(node, *reg, NULL);
if (addr != OF_BAD_ADDR) {
- snprintf(name, BUS_ID_SIZE,
- "D%llx.%s", (unsigned long long)addr,
- node->name);
+ dev_set_name(&dev->dev, "D%llx.%s",
+ (unsigned long long)addr, node->name);
return;
}
#endif /* !CONFIG_PPC_DCR_NATIVE */
@@ -48,9 +45,8 @@ static void of_device_make_bus_id(struct of_device *dev)
if (reg) {
addr = of_translate_address(node, reg);
if (addr != OF_BAD_ADDR) {
- snprintf(name, BUS_ID_SIZE,
- "%llx.%s", (unsigned long long)addr,
- node->name);
+ dev_set_name(&dev->dev, "%llx.%s",
+ (unsigned long long)addr, node->name);
return;
}
}
@@ -60,7 +56,7 @@ static void of_device_make_bus_id(struct of_device *dev)
* counter (and pray...)
*/
magic = atomic_add_return(1, &bus_no_reg_magic);
- snprintf(name, BUS_ID_SIZE, "%s.%d", node->name, magic - 1);
+ dev_set_name(&dev->dev, "%s.%d", node->name, magic - 1);
}
struct of_device *of_device_alloc(struct device_node *np,
@@ -80,7 +76,7 @@ struct of_device *of_device_alloc(struct device_node *np,
dev->dev.archdata.of_node = np;
if (bus_id)
- strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
+ dev_set_name(&dev->dev, bus_id);
else
of_device_make_bus_id(dev);
diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c
index 48a347133f41..c744b327bcab 100644
--- a/arch/powerpc/kernel/paca.c
+++ b/arch/powerpc/kernel/paca.c
@@ -37,6 +37,7 @@ struct lppaca lppaca[] = {
.end_of_quantum = 0xfffffffffffffffful,
.slb_count = 64,
.vmxregs_in_use = 0,
+ .page_ins = 0,
},
};
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index f36936d9fda3..2538030954d8 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -37,13 +37,7 @@
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
#include <asm/firmware.h>
-
-#ifdef DEBUG
-#include <asm/udbg.h>
-#define DBG(fmt...) printk(fmt)
-#else
-#define DBG(fmt...)
-#endif
+#include <asm/eeh.h>
static DEFINE_SPINLOCK(hose_spinlock);
@@ -53,8 +47,9 @@ static int global_phb_number; /* Global phb counter */
/* ISA Memory physical address */
resource_size_t isa_mem_base;
-/* Default PCI flags is 0 */
-unsigned int ppc_pci_flags;
+/* Default PCI flags is 0 on ppc32, modified at boot on ppc64 */
+unsigned int ppc_pci_flags = 0;
+
static struct dma_mapping_ops *pci_dma_ops;
@@ -165,8 +160,6 @@ EXPORT_SYMBOL(pci_domain_nr);
*/
struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
{
- if (!have_of)
- return NULL;
while(node) {
struct pci_controller *hose, *tmp;
list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
@@ -208,26 +201,6 @@ char __devinit *pcibios_setup(char *str)
return str;
}
-void __devinit pcibios_setup_new_device(struct pci_dev *dev)
-{
- struct dev_archdata *sd = &dev->dev.archdata;
-
- sd->of_node = pci_device_to_OF_node(dev);
-
- DBG("PCI: device %s OF node: %s\n", pci_name(dev),
- sd->of_node ? sd->of_node->full_name : "<none>");
-
- sd->dma_ops = pci_dma_ops;
-#ifdef CONFIG_PPC32
- sd->dma_data = (void *)PCI_DRAM_OFFSET;
-#endif
- set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
-
- if (ppc_md.pci_dma_dev_setup)
- ppc_md.pci_dma_dev_setup(dev);
-}
-EXPORT_SYMBOL(pcibios_setup_new_device);
-
/*
* Reads the interrupt pin to determine if interrupt is use by card.
* If the interrupt is used, then gets the interrupt line from the
@@ -252,7 +225,7 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
return -1;
#endif
- DBG("Try to map irq for %s...\n", pci_name(pci_dev));
+ pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev));
#ifdef DEBUG
memset(&oirq, 0xff, sizeof(oirq));
@@ -276,26 +249,26 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
line == 0xff || line == 0) {
return -1;
}
- DBG(" -> no map ! Using line %d (pin %d) from PCI config\n",
- line, pin);
+ pr_debug(" No map ! Using line %d (pin %d) from PCI config\n",
+ line, pin);
virq = irq_create_mapping(NULL, line);
if (virq != NO_IRQ)
set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
} else {
- DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n",
- oirq.size, oirq.specifier[0], oirq.specifier[1],
+ pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n",
+ oirq.size, oirq.specifier[0], oirq.specifier[1],
oirq.controller->full_name);
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
oirq.size);
}
if(virq == NO_IRQ) {
- DBG(" -> failed to map !\n");
+ pr_debug(" Failed to map !\n");
return -1;
}
- DBG(" -> mapped to linux irq %d\n", virq);
+ pr_debug(" Mapped to linux irq %d\n", virq);
pci_dev->irq = virq;
@@ -397,13 +370,10 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp,
}
/* XXX would be nice to have a way to ask for write-through */
- prot |= _PAGE_NO_CACHE;
if (write_combine)
- prot &= ~_PAGE_GUARDED;
+ return pgprot_noncached_wc(prot);
else
- prot |= _PAGE_GUARDED;
-
- return __pgprot(prot);
+ return pgprot_noncached(prot);
}
/*
@@ -414,19 +384,17 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp,
pgprot_t pci_phys_mem_access_prot(struct file *file,
unsigned long pfn,
unsigned long size,
- pgprot_t protection)
+ pgprot_t prot)
{
struct pci_dev *pdev = NULL;
struct resource *found = NULL;
- unsigned long prot = pgprot_val(protection);
resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT;
int i;
if (page_is_ram(pfn))
- return __pgprot(prot);
-
- prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+ return prot;
+ prot = pgprot_noncached(prot);
for_each_pci_dev(pdev) {
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
struct resource *rp = &pdev->resource[i];
@@ -447,14 +415,14 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
}
if (found) {
if (found->flags & IORESOURCE_PREFETCH)
- prot &= ~_PAGE_GUARDED;
+ prot = pgprot_noncached_wc(prot);
pci_dev_put(pdev);
}
- DBG("non-PCI map for %llx, prot: %lx\n",
- (unsigned long long)offset, prot);
+ pr_debug("PCI: Non-PCI map for %llx, prot: %lx\n",
+ (unsigned long long)offset, pgprot_val(prot));
- return __pgprot(prot);
+ return prot;
}
@@ -610,8 +578,7 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus,
pr_debug(" -> mapping phys %llx\n", (unsigned long long)offset);
vma->vm_pgoff = offset >> PAGE_SHIFT;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
@@ -853,15 +820,12 @@ void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
int pci_proc_domain(struct pci_bus *bus)
{
struct pci_controller *hose = pci_bus_to_host(bus);
-#ifdef CONFIG_PPC64
- return hose->buid != 0;
-#else
+
if (!(ppc_pci_flags & PPC_PCI_ENABLE_PROC_DOMAINS))
return 0;
if (ppc_pci_flags & PPC_PCI_COMPAT_DOMAIN_0)
return hose->global_number != 0;
return 1;
-#endif
}
void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
@@ -1083,27 +1047,50 @@ static void __devinit pcibios_fixup_bridge(struct pci_bus *bus)
}
}
-static void __devinit __pcibios_fixup_bus(struct pci_bus *bus)
+void __devinit pcibios_setup_bus_self(struct pci_bus *bus)
{
- struct pci_dev *dev = bus->self;
-
- pr_debug("PCI: Fixup bus %d (%s)\n", bus->number, dev ? pci_name(dev) : "PHB");
-
- /* Fixup PCI<->PCI bridges. Host bridges are handled separately, for
- * now differently between 32 and 64 bits.
- */
- if (dev != NULL)
+ /* Fix up the bus resources for P2P bridges */
+ if (bus->self != NULL)
pcibios_fixup_bridge(bus);
- /* Additional setup that is different between 32 and 64 bits for now */
- pcibios_do_bus_setup(bus);
-
- /* Platform specific bus fixups */
+ /* Platform specific bus fixups. This is currently only used
+ * by fsl_pci and I'm hoping to get rid of it at some point
+ */
if (ppc_md.pcibios_fixup_bus)
ppc_md.pcibios_fixup_bus(bus);
- /* Read default IRQs and fixup if necessary */
+ /* Setup bus DMA mappings */
+ if (ppc_md.pci_dma_bus_setup)
+ ppc_md.pci_dma_bus_setup(bus);
+}
+
+void __devinit pcibios_setup_bus_devices(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+
+ pr_debug("PCI: Fixup bus devices %d (%s)\n",
+ bus->number, bus->self ? pci_name(bus->self) : "PHB");
+
list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct dev_archdata *sd = &dev->dev.archdata;
+
+ /* Setup OF node pointer in archdata */
+ sd->of_node = pci_device_to_OF_node(dev);
+
+ /* Fixup NUMA node as it may not be setup yet by the generic
+ * code and is needed by the DMA init
+ */
+ set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
+
+ /* Hook up default DMA ops */
+ sd->dma_ops = pci_dma_ops;
+ sd->dma_data = (void *)PCI_DRAM_OFFSET;
+
+ /* Additional platform DMA/iommu setup */
+ if (ppc_md.pci_dma_dev_setup)
+ ppc_md.pci_dma_dev_setup(dev);
+
+ /* Read default IRQs and fixup if necessary */
pci_read_irq_line(dev);
if (ppc_md.pci_irq_fixup)
ppc_md.pci_irq_fixup(dev);
@@ -1113,22 +1100,19 @@ static void __devinit __pcibios_fixup_bus(struct pci_bus *bus)
void __devinit pcibios_fixup_bus(struct pci_bus *bus)
{
/* When called from the generic PCI probe, read PCI<->PCI bridge
- * bases before proceeding
+ * bases. This is -not- called when generating the PCI tree from
+ * the OF device-tree.
*/
if (bus->self != NULL)
pci_read_bridge_bases(bus);
- __pcibios_fixup_bus(bus);
-}
-EXPORT_SYMBOL(pcibios_fixup_bus);
-/* When building a bus from the OF tree rather than probing, we need a
- * slightly different version of the fixup which doesn't read the
- * bridge bases using config space accesses
- */
-void __devinit pcibios_fixup_of_probed_bus(struct pci_bus *bus)
-{
- __pcibios_fixup_bus(bus);
+ /* Now fixup the bus bus */
+ pcibios_setup_bus_self(bus);
+
+ /* Now fixup devices on that bus */
+ pcibios_setup_bus_devices(bus);
}
+EXPORT_SYMBOL(pcibios_fixup_bus);
static int skip_isa_ioresource_align(struct pci_dev *dev)
{
@@ -1198,10 +1182,10 @@ static int __init reparent_resources(struct resource *parent,
*pp = NULL;
for (p = res->child; p != NULL; p = p->sibling) {
p->parent = res;
- DBG(KERN_INFO "PCI: reparented %s [%llx..%llx] under %s\n",
- p->name,
- (unsigned long long)p->start,
- (unsigned long long)p->end, res->name);
+ pr_debug("PCI: Reparented %s [%llx..%llx] under %s\n",
+ p->name,
+ (unsigned long long)p->start,
+ (unsigned long long)p->end, res->name);
}
return 0;
}
@@ -1245,9 +1229,12 @@ void pcibios_allocate_bus_resources(struct pci_bus *bus)
int i;
struct resource *res, *pr;
+ pr_debug("PCI: Allocating bus resources for %04x:%02x...\n",
+ pci_domain_nr(bus), bus->number);
+
for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
if ((res = bus->resource[i]) == NULL || !res->flags
- || res->start > res->end)
+ || res->start > res->end || res->parent)
continue;
if (bus->parent == NULL)
pr = (res->flags & IORESOURCE_IO) ?
@@ -1271,14 +1258,14 @@ void pcibios_allocate_bus_resources(struct pci_bus *bus)
}
}
- DBG("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx "
- "[0x%x], parent %p (%s)\n",
- bus->self ? pci_name(bus->self) : "PHB",
- bus->number, i,
- (unsigned long long)res->start,
- (unsigned long long)res->end,
- (unsigned int)res->flags,
- pr, (pr && pr->name) ? pr->name : "nil");
+ pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx "
+ "[0x%x], parent %p (%s)\n",
+ bus->self ? pci_name(bus->self) : "PHB",
+ bus->number, i,
+ (unsigned long long)res->start,
+ (unsigned long long)res->end,
+ (unsigned int)res->flags,
+ pr, (pr && pr->name) ? pr->name : "nil");
if (pr && !(pr->flags & IORESOURCE_UNSET)) {
if (request_resource(pr, res) == 0)
@@ -1305,11 +1292,11 @@ static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
{
struct resource *pr, *r = &dev->resource[idx];
- DBG("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
- pci_name(dev), idx,
- (unsigned long long)r->start,
- (unsigned long long)r->end,
- (unsigned int)r->flags);
+ pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
+ pci_name(dev), idx,
+ (unsigned long long)r->start,
+ (unsigned long long)r->end,
+ (unsigned int)r->flags);
pr = pci_find_parent_resource(dev, r);
if (!pr || (pr->flags & IORESOURCE_UNSET) ||
@@ -1317,10 +1304,11 @@ static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
printk(KERN_WARNING "PCI: Cannot allocate resource region %d"
" of device %s, will remap\n", idx, pci_name(dev));
if (pr)
- DBG("PCI: parent is %p: %016llx-%016llx [%x]\n", pr,
- (unsigned long long)pr->start,
- (unsigned long long)pr->end,
- (unsigned int)pr->flags);
+ pr_debug("PCI: parent is %p: %016llx-%016llx [%x]\n",
+ pr,
+ (unsigned long long)pr->start,
+ (unsigned long long)pr->end,
+ (unsigned int)pr->flags);
/* We'll assign a new address later */
r->flags |= IORESOURCE_UNSET;
r->end -= r->start;
@@ -1358,7 +1346,8 @@ static void __init pcibios_allocate_resources(int pass)
* but keep it unregistered.
*/
u32 reg;
- DBG("PCI: Switching off ROM of %s\n", pci_name(dev));
+ pr_debug("PCI: Switching off ROM of %s\n",
+ pci_name(dev));
r->flags &= ~IORESOURCE_ROM_ENABLE;
pci_read_config_dword(dev, dev->rom_base_reg, &reg);
pci_write_config_dword(dev, dev->rom_base_reg,
@@ -1383,7 +1372,7 @@ void __init pcibios_resource_survey(void)
}
if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) {
- DBG("PCI: Assigning unassigned resouces...\n");
+ pr_debug("PCI: Assigning unassigned resouces...\n");
pci_assign_unassigned_resources();
}
@@ -1393,9 +1382,11 @@ void __init pcibios_resource_survey(void)
}
#ifdef CONFIG_HOTPLUG
-/* This is used by the pSeries hotplug driver to allocate resource
+
+/* This is used by the PCI hotplug driver to allocate resource
* of newly plugged busses. We can try to consolidate with the
- * rest of the code later, for now, keep it as-is
+ * rest of the code later, for now, keep it as-is as our main
+ * resource allocation function doesn't deal with sub-trees yet.
*/
void __devinit pcibios_claim_one_bus(struct pci_bus *bus)
{
@@ -1410,6 +1401,14 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus)
if (r->parent || !r->start || !r->flags)
continue;
+
+ pr_debug("PCI: Claiming %s: "
+ "Resource %d: %016llx..%016llx [%x]\n",
+ pci_name(dev), i,
+ (unsigned long long)r->start,
+ (unsigned long long)r->end,
+ (unsigned int)r->flags);
+
pci_claim_resource(dev, i);
}
}
@@ -1418,6 +1417,31 @@ void __devinit pcibios_claim_one_bus(struct pci_bus *bus)
pcibios_claim_one_bus(child_bus);
}
EXPORT_SYMBOL_GPL(pcibios_claim_one_bus);
+
+
+/* pcibios_finish_adding_to_bus
+ *
+ * This is to be called by the hotplug code after devices have been
+ * added to a bus, this include calling it for a PHB that is just
+ * being added
+ */
+void pcibios_finish_adding_to_bus(struct pci_bus *bus)
+{
+ pr_debug("PCI: Finishing adding to hotplug bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
+
+ /* Allocate bus and devices resources */
+ pcibios_allocate_bus_resources(bus);
+ pcibios_claim_one_bus(bus);
+
+ /* Add new devices to global lists. Register in proc, sysfs. */
+ pci_bus_add_devices(bus);
+
+ /* Fixup EEH */
+ eeh_add_device_tree_late(bus);
+}
+EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus);
+
#endif /* CONFIG_HOTPLUG */
int pcibios_enable_device(struct pci_dev *dev, int mask)
@@ -1428,3 +1452,61 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
return pci_enable_resources(dev, mask);
}
+
+void __devinit pcibios_setup_phb_resources(struct pci_controller *hose)
+{
+ struct pci_bus *bus = hose->bus;
+ struct resource *res;
+ int i;
+
+ /* Hookup PHB IO resource */
+ bus->resource[0] = res = &hose->io_resource;
+
+ if (!res->flags) {
+ printk(KERN_WARNING "PCI: I/O resource not set for host"
+ " bridge %s (domain %d)\n",
+ hose->dn->full_name, hose->global_number);
+#ifdef CONFIG_PPC32
+ /* Workaround for lack of IO resource only on 32-bit */
+ res->start = (unsigned long)hose->io_base_virt - isa_io_base;
+ res->end = res->start + IO_SPACE_LIMIT;
+ res->flags = IORESOURCE_IO;
+#endif /* CONFIG_PPC32 */
+ }
+
+ pr_debug("PCI: PHB IO resource = %016llx-%016llx [%lx]\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end,
+ (unsigned long)res->flags);
+
+ /* Hookup PHB Memory resources */
+ for (i = 0; i < 3; ++i) {
+ res = &hose->mem_resources[i];
+ if (!res->flags) {
+ if (i > 0)
+ continue;
+ printk(KERN_ERR "PCI: Memory resource 0 not set for "
+ "host bridge %s (domain %d)\n",
+ hose->dn->full_name, hose->global_number);
+#ifdef CONFIG_PPC32
+ /* Workaround for lack of MEM resource only on 32-bit */
+ res->start = hose->pci_mem_offset;
+ res->end = (resource_size_t)-1LL;
+ res->flags = IORESOURCE_MEM;
+#endif /* CONFIG_PPC32 */
+ }
+ bus->resource[i+1] = res;
+
+ pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n", i,
+ (unsigned long long)res->start,
+ (unsigned long long)res->end,
+ (unsigned long)res->flags);
+ }
+
+ pr_debug("PCI: PHB MEM offset = %016llx\n",
+ (unsigned long long)hose->pci_mem_offset);
+ pr_debug("PCI: PHB IO offset = %08lx\n",
+ (unsigned long)hose->io_base_virt - _IO_BASE);
+
+}
+
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index 131b1dfa68c6..132cd80afa21 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -26,12 +26,6 @@
#undef DEBUG
-#ifdef DEBUG
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
unsigned long isa_io_base = 0;
unsigned long pci_dram_offset = 0;
int pcibios_assign_bus_offset = 1;
@@ -272,17 +266,14 @@ pci_busdev_to_OF_node(struct pci_bus *bus, int devfn)
{
struct device_node *parent, *np;
- if (!have_of)
- return NULL;
-
- DBG("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
+ pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
parent = scan_OF_for_pci_bus(bus);
if (parent == NULL)
return NULL;
- DBG(" parent is %s\n", parent ? parent->full_name : "<NULL>");
+ pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>");
np = scan_OF_for_pci_dev(parent, devfn);
of_node_put(parent);
- DBG(" result is %s\n", np ? np->full_name : "<NULL>");
+ pr_debug(" result is %s\n", np ? np->full_name : "<NULL>");
/* XXX most callers don't release the returned node
* mostly because ppc64 doesn't increase the refcount,
@@ -315,8 +306,6 @@ pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
struct pci_controller* hose;
struct pci_dev* dev = NULL;
- if (!have_of)
- return -ENODEV;
/* Make sure it's really a PCI device */
hose = pci_find_hose_for_OF_device(node);
if (!hose || !hose->dn)
@@ -379,10 +368,41 @@ void pcibios_make_OF_bus_map(void)
}
#endif /* CONFIG_PPC_OF */
+static void __devinit pcibios_scan_phb(struct pci_controller *hose)
+{
+ struct pci_bus *bus;
+ struct device_node *node = hose->dn;
+ unsigned long io_offset;
+ struct resource *res = &hose->io_resource;
+
+ pr_debug("PCI: Scanning PHB %s\n",
+ node ? node->full_name : "<NO NAME>");
+
+ /* Create an empty bus for the toplevel */
+ bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, hose);
+ if (bus == NULL) {
+ printk(KERN_ERR "Failed to create bus for PCI domain %04x\n",
+ hose->global_number);
+ return;
+ }
+ bus->secondary = hose->first_busno;
+ hose->bus = bus;
+
+ /* Fixup IO space offset */
+ io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
+ res->start = (res->start + io_offset) & 0xffffffffu;
+ res->end = (res->end + io_offset) & 0xffffffffu;
+
+ /* Wire up PHB bus resources */
+ pcibios_setup_phb_resources(hose);
+
+ /* Scan children */
+ hose->last_busno = bus->subordinate = pci_scan_child_bus(bus);
+}
+
static int __init pcibios_init(void)
{
struct pci_controller *hose, *tmp;
- struct pci_bus *bus;
int next_busno = 0;
printk(KERN_INFO "PCI: Probing PCI hardware\n");
@@ -395,12 +415,8 @@ static int __init pcibios_init(void)
if (pci_assign_all_buses)
hose->first_busno = next_busno;
hose->last_busno = 0xff;
- bus = pci_scan_bus_parented(hose->parent, hose->first_busno,
- hose->ops, hose);
- if (bus) {
- pci_bus_add_devices(bus);
- hose->last_busno = bus->subordinate;
- }
+ pcibios_scan_phb(hose);
+ pci_bus_add_devices(hose->bus);
if (pci_assign_all_buses || next_busno <= hose->last_busno)
next_busno = hose->last_busno + pcibios_assign_bus_offset;
}
@@ -410,7 +426,7 @@ static int __init pcibios_init(void)
* numbers vs. kernel bus numbers since we may have to
* remap them.
*/
- if (pci_assign_all_buses && have_of)
+ if (pci_assign_all_buses)
pcibios_make_OF_bus_map();
/* Call common code to handle resource allocation */
@@ -425,54 +441,6 @@ static int __init pcibios_init(void)
subsys_initcall(pcibios_init);
-void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
-{
- struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
- unsigned long io_offset;
- struct resource *res;
- int i;
- struct pci_dev *dev;
-
- /* Hookup PHB resources */
- io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
- if (bus->parent == NULL) {
- /* This is a host bridge - fill in its resources */
- hose->bus = bus;
-
- bus->resource[0] = res = &hose->io_resource;
- if (!res->flags) {
- if (io_offset)
- printk(KERN_ERR "I/O resource not set for host"
- " bridge %d\n", hose->global_number);
- res->start = 0;
- res->end = IO_SPACE_LIMIT;
- res->flags = IORESOURCE_IO;
- }
- res->start = (res->start + io_offset) & 0xffffffffu;
- res->end = (res->end + io_offset) & 0xffffffffu;
-
- for (i = 0; i < 3; ++i) {
- res = &hose->mem_resources[i];
- if (!res->flags) {
- if (i > 0)
- continue;
- printk(KERN_ERR "Memory resource not set for "
- "host bridge %d\n", hose->global_number);
- res->start = hose->pci_mem_offset;
- res->end = ~0U;
- res->flags = IORESOURCE_MEM;
- }
- bus->resource[i+1] = res;
- }
- }
-
- if (ppc_md.pci_dma_bus_setup)
- ppc_md.pci_dma_bus_setup(bus);
-
- list_for_each_entry(dev, &bus->devices, bus_list)
- pcibios_setup_new_device(dev);
-}
-
/* the next one is stolen from the alpha port... */
void __init
pcibios_update_irq(struct pci_dev *dev, int irq)
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index 3502b9101e6b..39fadc6e1492 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -32,13 +32,6 @@
#include <asm/machdep.h>
#include <asm/ppc-pci.h>
-#ifdef DEBUG
-#include <asm/udbg.h>
-#define DBG(fmt...) printk(fmt)
-#else
-#define DBG(fmt...)
-#endif
-
unsigned long pci_probe_only = 1;
/* pci_io_base -- the base address from which io bars are offsets.
@@ -102,7 +95,7 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev)
addrs = of_get_property(node, "assigned-addresses", &proplen);
if (!addrs)
return;
- DBG(" parse addresses (%d bytes) @ %p\n", proplen, addrs);
+ pr_debug(" parse addresses (%d bytes) @ %p\n", proplen, addrs);
for (; proplen >= 20; proplen -= 20, addrs += 5) {
flags = pci_parse_of_flags(addrs[0]);
if (!flags)
@@ -112,8 +105,9 @@ static void pci_parse_of_addrs(struct device_node *node, struct pci_dev *dev)
if (!size)
continue;
i = addrs[0] & 0xff;
- DBG(" base: %llx, size: %llx, i: %x\n",
- (unsigned long long)base, (unsigned long long)size, i);
+ pr_debug(" base: %llx, size: %llx, i: %x\n",
+ (unsigned long long)base,
+ (unsigned long long)size, i);
if (PCI_BASE_ADDRESS_0 <= i && i <= PCI_BASE_ADDRESS_5) {
res = &dev->resource[(i - PCI_BASE_ADDRESS_0) >> 2];
@@ -144,7 +138,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
if (type == NULL)
type = "";
- DBG(" create device, devfn: %x, type: %s\n", devfn, type);
+ pr_debug(" create device, devfn: %x, type: %s\n", devfn, type);
dev->bus = bus;
dev->sysdata = node;
@@ -165,8 +159,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
dev->class = get_int_prop(node, "class-code", 0);
dev->revision = get_int_prop(node, "revision-id", 0);
- DBG(" class: 0x%x\n", dev->class);
- DBG(" revision: 0x%x\n", dev->revision);
+ pr_debug(" class: 0x%x\n", dev->class);
+ pr_debug(" revision: 0x%x\n", dev->revision);
dev->current_state = 4; /* unknown power state */
dev->error_state = pci_channel_io_normal;
@@ -187,7 +181,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
pci_parse_of_addrs(node, dev);
- DBG(" adding to system ...\n");
+ pr_debug(" adding to system ...\n");
pci_device_add(dev, bus);
@@ -195,19 +189,20 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
}
EXPORT_SYMBOL(of_create_pci_dev);
-void __devinit of_scan_bus(struct device_node *node,
- struct pci_bus *bus)
+static void __devinit __of_scan_bus(struct device_node *node,
+ struct pci_bus *bus, int rescan_existing)
{
struct device_node *child;
const u32 *reg;
int reglen, devfn;
struct pci_dev *dev;
- DBG("of_scan_bus(%s) bus no %d... \n", node->full_name, bus->number);
+ pr_debug("of_scan_bus(%s) bus no %d... \n",
+ node->full_name, bus->number);
/* Scan direct children */
for_each_child_of_node(node, child) {
- DBG(" * %s\n", child->full_name);
+ pr_debug(" * %s\n", child->full_name);
reg = of_get_property(child, "reg", &reglen);
if (reg == NULL || reglen < 20)
continue;
@@ -217,11 +212,15 @@ void __devinit of_scan_bus(struct device_node *node,
dev = of_create_pci_dev(child, bus, devfn);
if (!dev)
continue;
- DBG(" dev header type: %x\n", dev->hdr_type);
+ pr_debug(" dev header type: %x\n", dev->hdr_type);
}
- /* Ally all fixups */
- pcibios_fixup_of_probed_bus(bus);
+ /* Apply all fixups necessary. We don't fixup the bus "self"
+ * for an existing bridge that is being rescanned
+ */
+ if (!rescan_existing)
+ pcibios_setup_bus_self(bus);
+ pcibios_setup_bus_devices(bus);
/* Now scan child busses */
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -233,7 +232,20 @@ void __devinit of_scan_bus(struct device_node *node,
}
}
}
-EXPORT_SYMBOL(of_scan_bus);
+
+void __devinit of_scan_bus(struct device_node *node,
+ struct pci_bus *bus)
+{
+ __of_scan_bus(node, bus, 0);
+}
+EXPORT_SYMBOL_GPL(of_scan_bus);
+
+void __devinit of_rescan_bus(struct device_node *node,
+ struct pci_bus *bus)
+{
+ __of_scan_bus(node, bus, 1);
+}
+EXPORT_SYMBOL_GPL(of_rescan_bus);
void __devinit of_scan_pci_bridge(struct device_node *node,
struct pci_dev *dev)
@@ -245,7 +257,7 @@ void __devinit of_scan_pci_bridge(struct device_node *node,
unsigned int flags;
u64 size;
- DBG("of_scan_pci_bridge(%s)\n", node->full_name);
+ pr_debug("of_scan_pci_bridge(%s)\n", node->full_name);
/* parse bus-range property */
busrange = of_get_property(node, "bus-range", &len);
@@ -309,12 +321,12 @@ void __devinit of_scan_pci_bridge(struct device_node *node,
}
sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
bus->number);
- DBG(" bus name: %s\n", bus->name);
+ pr_debug(" bus name: %s\n", bus->name);
mode = PCI_PROBE_NORMAL;
if (ppc_md.pci_probe_mode)
mode = ppc_md.pci_probe_mode(bus);
- DBG(" probe mode: %d\n", mode);
+ pr_debug(" probe mode: %d\n", mode);
if (mode == PCI_PROBE_DEVTREE)
of_scan_bus(node, bus);
@@ -327,9 +339,10 @@ void __devinit scan_phb(struct pci_controller *hose)
{
struct pci_bus *bus;
struct device_node *node = hose->dn;
- int i, mode;
+ int mode;
- DBG("PCI: Scanning PHB %s\n", node ? node->full_name : "<NO NAME>");
+ pr_debug("PCI: Scanning PHB %s\n",
+ node ? node->full_name : "<NO NAME>");
/* Create an empty bus for the toplevel */
bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node);
@@ -345,26 +358,13 @@ void __devinit scan_phb(struct pci_controller *hose)
pcibios_map_io_space(bus);
/* Wire up PHB bus resources */
- DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n",
- hose->io_resource.start, hose->io_resource.end,
- hose->io_resource.flags);
- bus->resource[0] = &hose->io_resource;
- for (i = 0; i < 3; ++i) {
- DBG("PCI: PHB MEM resource %d = %016lx-%016lx [%lx]\n", i,
- hose->mem_resources[i].start,
- hose->mem_resources[i].end,
- hose->mem_resources[i].flags);
- bus->resource[i+1] = &hose->mem_resources[i];
- }
- DBG("PCI: PHB MEM offset = %016lx\n", hose->pci_mem_offset);
- DBG("PCI: PHB IO offset = %08lx\n",
- (unsigned long)hose->io_base_virt - _IO_BASE);
+ pcibios_setup_phb_resources(hose);
/* Get probe mode and perform scan */
mode = PCI_PROBE_NORMAL;
if (node && ppc_md.pci_probe_mode)
mode = ppc_md.pci_probe_mode(bus);
- DBG(" probe mode: %d\n", mode);
+ pr_debug(" probe mode: %d\n", mode);
if (mode == PCI_PROBE_DEVTREE) {
bus->subordinate = hose->last_busno;
of_scan_bus(node, bus);
@@ -380,7 +380,7 @@ static int __init pcibios_init(void)
printk(KERN_INFO "PCI: Probing PCI hardware\n");
- /* For now, override phys_mem_access_prot. If we need it,
+ /* For now, override phys_mem_access_prot. If we need it,g
* later, we may move that initialization to each ppc_md
*/
ppc_md.phys_mem_access_prot = pci_phys_mem_access_prot;
@@ -388,6 +388,11 @@ static int __init pcibios_init(void)
if (pci_probe_only)
ppc_pci_flags |= PPC_PCI_PROBE_ONLY;
+ /* On ppc64, we always enable PCI domains and we keep domain 0
+ * backward compatible in /proc for video cards
+ */
+ ppc_pci_flags |= PPC_PCI_ENABLE_PROC_DOMAINS | PPC_PCI_COMPAT_DOMAIN_0;
+
/* Scan all of the recorded PCI controllers. */
list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
scan_phb(hose);
@@ -422,8 +427,8 @@ int pcibios_unmap_io_space(struct pci_bus *bus)
if (bus->self) {
struct resource *res = bus->resource[0];
- DBG("IO unmapping for PCI-PCI bridge %s\n",
- pci_name(bus->self));
+ pr_debug("IO unmapping for PCI-PCI bridge %s\n",
+ pci_name(bus->self));
__flush_hash_table_range(&init_mm, res->start + _IO_BASE,
res->end + _IO_BASE + 1);
@@ -437,8 +442,8 @@ int pcibios_unmap_io_space(struct pci_bus *bus)
if (hose->io_base_alloc == 0)
return 0;
- DBG("IO unmapping for PHB %s\n", hose->dn->full_name);
- DBG(" alloc=0x%p\n", hose->io_base_alloc);
+ pr_debug("IO unmapping for PHB %s\n", hose->dn->full_name);
+ pr_debug(" alloc=0x%p\n", hose->io_base_alloc);
/* This is a PHB, we fully unmap the IO area */
vunmap(hose->io_base_alloc);
@@ -463,11 +468,11 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus)
* thus HPTEs will be faulted in when needed
*/
if (bus->self) {
- DBG("IO mapping for PCI-PCI bridge %s\n",
- pci_name(bus->self));
- DBG(" virt=0x%016lx...0x%016lx\n",
- bus->resource[0]->start + _IO_BASE,
- bus->resource[0]->end + _IO_BASE);
+ pr_debug("IO mapping for PCI-PCI bridge %s\n",
+ pci_name(bus->self));
+ pr_debug(" virt=0x%016lx...0x%016lx\n",
+ bus->resource[0]->start + _IO_BASE,
+ bus->resource[0]->end + _IO_BASE);
return 0;
}
@@ -496,11 +501,11 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus)
hose->io_base_virt = (void __iomem *)(area->addr +
hose->io_base_phys - phys_page);
- DBG("IO mapping for PHB %s\n", hose->dn->full_name);
- DBG(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n",
- hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc);
- DBG(" size=0x%016lx (alloc=0x%016lx)\n",
- hose->pci_io_size, size_page);
+ pr_debug("IO mapping for PHB %s\n", hose->dn->full_name);
+ pr_debug(" phys=0x%016lx, virt=0x%p (alloc=0x%p)\n",
+ hose->io_base_phys, hose->io_base_virt, hose->io_base_alloc);
+ pr_debug(" size=0x%016lx (alloc=0x%016lx)\n",
+ hose->pci_io_size, size_page);
/* Establish the mapping */
if (__ioremap_at(phys_page, area->addr, size_page,
@@ -512,24 +517,13 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus)
hose->io_resource.start += io_virt_offset;
hose->io_resource.end += io_virt_offset;
- DBG(" hose->io_resource=0x%016lx...0x%016lx\n",
- hose->io_resource.start, hose->io_resource.end);
+ pr_debug(" hose->io_resource=0x%016lx...0x%016lx\n",
+ hose->io_resource.start, hose->io_resource.end);
return 0;
}
EXPORT_SYMBOL_GPL(pcibios_map_io_space);
-void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
-{
- struct pci_dev *dev;
-
- if (ppc_md.pci_dma_bus_setup)
- ppc_md.pci_dma_bus_setup(bus);
-
- list_for_each_entry(dev, &bus->devices, bus_list)
- pcibios_setup_new_device(dev);
-}
-
unsigned long pci_address_to_pio(phys_addr_t address)
{
struct pci_controller *hose, *tmp;
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
index 260089dccfb0..dcec1325d340 100644
--- a/arch/powerpc/kernel/ppc_ksyms.c
+++ b/arch/powerpc/kernel/ppc_ksyms.c
@@ -116,12 +116,6 @@ EXPORT_SYMBOL(giveup_spe);
#ifndef CONFIG_PPC64
EXPORT_SYMBOL(flush_instruction_cache);
-EXPORT_SYMBOL(flush_tlb_kernel_range);
-EXPORT_SYMBOL(flush_tlb_page);
-EXPORT_SYMBOL(_tlbie);
-#if defined(CONFIG_4xx) || defined(CONFIG_8xx) || defined(CONFIG_FSL_BOOKE)
-EXPORT_SYMBOL(_tlbil_va);
-#endif
#endif
EXPORT_SYMBOL(__flush_icache_range);
EXPORT_SYMBOL(flush_dcache_range);
@@ -174,8 +168,7 @@ EXPORT_SYMBOL(cacheable_memcpy);
#endif
#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(next_mmu_context);
-EXPORT_SYMBOL(set_context);
+EXPORT_SYMBOL(switch_mmu_context);
#endif
#ifdef CONFIG_PPC_STD_MMU_32
diff --git a/arch/powerpc/kernel/ppc_save_regs.S b/arch/powerpc/kernel/ppc_save_regs.S
new file mode 100644
index 000000000000..5113bd2285e1
--- /dev/null
+++ b/arch/powerpc/kernel/ppc_save_regs.S
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute 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: assert(sizeof(buf) > 23 * sizeof(long))
+ */
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * Grab the register values as they are now.
+ * This won't do a particularily good job because we really
+ * want our caller's caller's registers, and our caller has
+ * already executed its prologue.
+ * ToDo: We could reach back into the caller's save area to do
+ * a better job of representing the caller's state (note that
+ * that will be different for 32-bit and 64-bit, because of the
+ * different ABIs, though).
+ */
+_GLOBAL(ppc_save_regs)
+ PPC_STL r0,0*SZL(r3)
+ PPC_STL r2,2*SZL(r3)
+ PPC_STL r3,3*SZL(r3)
+ PPC_STL r4,4*SZL(r3)
+ PPC_STL r5,5*SZL(r3)
+ PPC_STL r6,6*SZL(r3)
+ PPC_STL r7,7*SZL(r3)
+ PPC_STL r8,8*SZL(r3)
+ PPC_STL r9,9*SZL(r3)
+ PPC_STL r10,10*SZL(r3)
+ PPC_STL r11,11*SZL(r3)
+ PPC_STL r12,12*SZL(r3)
+ PPC_STL r13,13*SZL(r3)
+ PPC_STL r14,14*SZL(r3)
+ PPC_STL r15,15*SZL(r3)
+ PPC_STL r16,16*SZL(r3)
+ PPC_STL r17,17*SZL(r3)
+ PPC_STL r18,18*SZL(r3)
+ PPC_STL r19,19*SZL(r3)
+ PPC_STL r20,20*SZL(r3)
+ PPC_STL r21,21*SZL(r3)
+ PPC_STL r22,22*SZL(r3)
+ PPC_STL r23,23*SZL(r3)
+ PPC_STL r24,24*SZL(r3)
+ PPC_STL r25,25*SZL(r3)
+ PPC_STL r26,26*SZL(r3)
+ PPC_STL r27,27*SZL(r3)
+ PPC_STL r28,28*SZL(r3)
+ PPC_STL r29,29*SZL(r3)
+ PPC_STL r30,30*SZL(r3)
+ PPC_STL r31,31*SZL(r3)
+ /* go up one stack frame for SP */
+ PPC_LL r4,0(r1)
+ PPC_STL r4,1*SZL(r3)
+ /* get caller's LR */
+ PPC_LL r0,LRSAVE(r4)
+ PPC_STL r0,_NIP-STACK_FRAME_OVERHEAD(r3)
+ PPC_STL r0,_LINK-STACK_FRAME_OVERHEAD(r3)
+ mfmsr r0
+ PPC_STL r0,_MSR-STACK_FRAME_OVERHEAD(r3)
+ mfctr r0
+ PPC_STL r0,_CTR-STACK_FRAME_OVERHEAD(r3)
+ mfxer r0
+ PPC_STL r0,_XER-STACK_FRAME_OVERHEAD(r3)
+ mfcr r0
+ PPC_STL r0,_CCR-STACK_FRAME_OVERHEAD(r3)
+ li r0,0
+ PPC_STL r0,_TRAP-STACK_FRAME_OVERHEAD(r3)
+ blr
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 957bded0020d..51b201ddf9a1 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -467,6 +467,8 @@ static struct regbit {
{MSR_VEC, "VEC"},
{MSR_VSX, "VSX"},
{MSR_ME, "ME"},
+ {MSR_CE, "CE"},
+ {MSR_DE, "DE"},
{MSR_IR, "IR"},
{MSR_DR, "DR"},
{0, NULL}
@@ -998,7 +1000,7 @@ unsigned long get_wchan(struct task_struct *p)
return 0;
}
-static int kstack_depth_to_print = 64;
+static int kstack_depth_to_print = CONFIG_PRINT_STACK_DEPTH;
void show_stack(struct task_struct *tsk, unsigned long *stack)
{
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 3a2dc7e6586a..6f73c739f1e2 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -1160,6 +1160,8 @@ static inline void __init phyp_dump_reserve_mem(void) {}
void __init early_init_devtree(void *params)
{
+ unsigned long limit;
+
DBG(" -> early_init_devtree(%p)\n", params);
/* Setup flat device-tree pointer */
@@ -1200,7 +1202,19 @@ void __init early_init_devtree(void *params)
early_reserve_mem();
phyp_dump_reserve_mem();
- lmb_enforce_memory_limit(memory_limit);
+ limit = memory_limit;
+ if (! limit) {
+ unsigned long memsize;
+
+ /* Ensure that total memory size is page-aligned, because
+ * otherwise mark_bootmem() gets upset. */
+ lmb_analyze();
+ memsize = lmb_phys_mem_size();
+ if ((memsize & PAGE_MASK) != memsize)
+ limit = memsize & PAGE_MASK;
+ }
+ lmb_enforce_memory_limit(limit);
+
lmb_analyze();
DBG("Phys. mem: %lx\n", lmb_phys_mem_size());
@@ -1271,6 +1285,37 @@ struct device_node *of_find_node_by_phandle(phandle handle)
EXPORT_SYMBOL(of_find_node_by_phandle);
/**
+ * of_find_next_cache_node - Find a node's subsidiary cache
+ * @np: node of type "cpu" or "cache"
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done. Caller should hold a reference
+ * to np.
+ */
+struct device_node *of_find_next_cache_node(struct device_node *np)
+{
+ struct device_node *child;
+ const phandle *handle;
+
+ handle = of_get_property(np, "l2-cache", NULL);
+ if (!handle)
+ handle = of_get_property(np, "next-level-cache", NULL);
+
+ if (handle)
+ return of_find_node_by_phandle(*handle);
+
+ /* OF on pmac has nodes instead of properties named "l2-cache"
+ * beneath CPU nodes.
+ */
+ if (!strcmp(np->type, "cpu"))
+ for_each_child_of_node(np, child)
+ if (!strcmp(child->type, "cache"))
+ return child;
+
+ return NULL;
+}
+
+/**
* of_find_all_nodes - Get next node in global list
* @prev: Previous node or NULL to start iteration
* of_node_put() will be called on it
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c
index a11d68976dc8..8c1335566089 100644
--- a/arch/powerpc/kernel/prom_parse.c
+++ b/arch/powerpc/kernel/prom_parse.c
@@ -734,10 +734,7 @@ void of_irq_map_init(unsigned int flags)
if (flags & OF_IMAP_NO_PHANDLE) {
struct device_node *np;
- for(np = NULL; (np = of_find_all_nodes(np)) != NULL;) {
- if (of_get_property(np, "interrupt-controller", NULL)
- == NULL)
- continue;
+ for_each_node_with_property(np, "interrupt-controller") {
/* Skip /chosen/interrupt-controller */
if (strcmp(np->name, "chosen") == 0)
continue;
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 1f8505c23548..fdfe14c4bdef 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -566,6 +566,32 @@ int rtas_get_sensor(int sensor, int index, int *state)
}
EXPORT_SYMBOL(rtas_get_sensor);
+bool rtas_indicator_present(int token, int *maxindex)
+{
+ int proplen, count, i;
+ const struct indicator_elem {
+ u32 token;
+ u32 maxindex;
+ } *indicators;
+
+ indicators = of_get_property(rtas.dev, "rtas-indicators", &proplen);
+ if (!indicators)
+ return false;
+
+ count = proplen / sizeof(struct indicator_elem);
+
+ for (i = 0; i < count; i++) {
+ if (indicators[i].token != token)
+ continue;
+ if (maxindex)
+ *maxindex = indicators[i].maxindex;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(rtas_indicator_present);
+
int rtas_set_indicator(int indicator, int index, int new_value)
{
int token = rtas_token("set-indicator");
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index 589a2797eac2..8869001ab5d7 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -301,51 +301,3 @@ void __init find_and_init_phbs(void)
#endif /* CONFIG_PPC32 */
}
}
-
-/* RPA-specific bits for removing PHBs */
-int pcibios_remove_root_bus(struct pci_controller *phb)
-{
- struct pci_bus *b = phb->bus;
- struct resource *res;
- int rc, i;
-
- res = b->resource[0];
- if (!res->flags) {
- printk(KERN_ERR "%s: no IO resource for PHB %s\n", __func__,
- b->name);
- return 1;
- }
-
- rc = pcibios_unmap_io_space(b);
- if (rc) {
- printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
- __func__, b->name);
- return 1;
- }
-
- if (release_resource(res)) {
- printk(KERN_ERR "%s: failed to release IO on bus %s\n",
- __func__, b->name);
- return 1;
- }
-
- for (i = 1; i < 3; ++i) {
- res = b->resource[i];
- if (!res->flags && i == 0) {
- printk(KERN_ERR "%s: no MEM resource for PHB %s\n",
- __func__, b->name);
- return 1;
- }
- if (res->flags && release_resource(res)) {
- printk(KERN_ERR
- "%s: failed to release IO %d on bus %s\n",
- __func__, i, b->name);
- return 1;
- }
- }
-
- pcibios_free_controller(phb);
-
- return 0;
-}
-EXPORT_SYMBOL(pcibios_remove_root_bus);
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index c1a27626a940..9e1ca745d8f0 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -38,6 +38,7 @@
#include <asm/time.h>
#include <asm/serial.h>
#include <asm/udbg.h>
+#include <asm/mmu_context.h>
#include "setup.h"
@@ -49,12 +50,12 @@ int boot_cpuid;
EXPORT_SYMBOL_GPL(boot_cpuid);
int boot_cpuid_phys;
+int smp_hw_index[NR_CPUS];
+
unsigned long ISA_DMA_THRESHOLD;
unsigned int DMA_MODE_READ;
unsigned int DMA_MODE_WRITE;
-int have_of = 1;
-
#ifdef CONFIG_VGA_CONSOLE
unsigned long vgacon_remap_base;
EXPORT_SYMBOL(vgacon_remap_base);
@@ -97,6 +98,10 @@ notrace unsigned long __init early_init(unsigned long dt_ptr)
PTRRELOC(&__start___ftr_fixup),
PTRRELOC(&__stop___ftr_fixup));
+ do_feature_fixups(spec->mmu_features,
+ PTRRELOC(&__start___mmu_ftr_fixup),
+ PTRRELOC(&__stop___mmu_ftr_fixup));
+
do_lwsync_fixups(spec->cpu_features,
PTRRELOC(&__start___lwsync_fixup),
PTRRELOC(&__stop___lwsync_fixup));
@@ -121,6 +126,8 @@ notrace void __init machine_init(unsigned long dt_ptr)
probe_machine();
+ setup_kdump_trampoline();
+
#ifdef CONFIG_6xx
if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||
cpu_has_feature(CPU_FTR_CAN_NAP))
@@ -326,4 +333,8 @@ void __init setup_arch(char **cmdline_p)
if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab);
paging_init();
+
+ /* Initialize the MMU context management stuff */
+ mmu_context_init();
+
}
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 169d74cef157..d8bd2161e738 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -70,7 +70,6 @@
#define DBG(fmt...)
#endif
-int have_of = 1;
int boot_cpuid = 0;
u64 ppc64_pft_size;
@@ -362,6 +361,8 @@ void __init setup_system(void)
*/
do_feature_fixups(cur_cpu_spec->cpu_features,
&__start___ftr_fixup, &__stop___ftr_fixup);
+ do_feature_fixups(cur_cpu_spec->mmu_features,
+ &__start___mmu_ftr_fixup, &__stop___mmu_ftr_fixup);
do_feature_fixups(powerpc_firmware_features,
&__start___fw_ftr_fixup, &__stop___fw_ftr_fixup);
do_lwsync_fixups(cur_cpu_spec->cpu_features,
@@ -606,8 +607,6 @@ void __init setup_per_cpu_areas(void)
for_each_possible_cpu(i) {
ptr = alloc_bootmem_pages_node(NODE_DATA(cpu_to_node(i)), size);
- if (!ptr)
- panic("Cannot allocate cpu data for CPU %d\n", i);
paca[i].data_offset = ptr - __per_cpu_start;
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
diff --git a/arch/powerpc/kernel/smp-tbsync.c b/arch/powerpc/kernel/smp-tbsync.c
index bc892e69b4f7..a5e54526403d 100644
--- a/arch/powerpc/kernel/smp-tbsync.c
+++ b/arch/powerpc/kernel/smp-tbsync.c
@@ -113,7 +113,7 @@ void __devinit smp_generic_give_timebase(void)
{
int i, score, score2, old, min=0, max=5000, offset=1000;
- printk("Synchronizing timebase\n");
+ pr_debug("Software timebase sync\n");
/* if this fails then this kernel won't work anyway... */
tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
@@ -123,13 +123,13 @@ void __devinit smp_generic_give_timebase(void)
while (!tbsync->ack)
barrier();
- printk("Got ack\n");
+ pr_debug("Got ack\n");
/* binary search */
for (old = -1; old != offset ; offset = (min+max) / 2) {
score = start_contest(kSetAndTest, offset, NUM_ITER);
- printk("score %d, offset %d\n", score, offset );
+ pr_debug("score %d, offset %d\n", score, offset );
if( score > 0 )
max = offset;
@@ -140,8 +140,8 @@ void __devinit smp_generic_give_timebase(void)
score = start_contest(kSetAndTest, min, NUM_ITER);
score2 = start_contest(kSetAndTest, max, NUM_ITER);
- printk("Min %d (score %d), Max %d (score %d)\n",
- min, score, max, score2);
+ pr_debug("Min %d (score %d), Max %d (score %d)\n",
+ min, score, max, score2);
score = abs(score);
score2 = abs(score2);
offset = (score < score2) ? min : max;
@@ -155,7 +155,7 @@ void __devinit smp_generic_give_timebase(void)
if (score2 <= score || score2 < 20)
break;
}
- printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
+ pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
/* exiting */
tbsync->cmd = kExit;
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index d1165566f064..65484b2200b3 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -57,7 +57,6 @@
#define DBG(fmt...)
#endif
-int smp_hw_index[NR_CPUS];
struct thread_info *secondary_ti;
DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE;
@@ -119,6 +118,65 @@ void smp_message_recv(int msg)
}
}
+static irqreturn_t call_function_action(int irq, void *data)
+{
+ generic_smp_call_function_interrupt();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t reschedule_action(int irq, void *data)
+{
+ /* we just need the return path side effect of checking need_resched */
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t call_function_single_action(int irq, void *data)
+{
+ generic_smp_call_function_single_interrupt();
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t debug_ipi_action(int irq, void *data)
+{
+ smp_message_recv(PPC_MSG_DEBUGGER_BREAK);
+ return IRQ_HANDLED;
+}
+
+static irq_handler_t smp_ipi_action[] = {
+ [PPC_MSG_CALL_FUNCTION] = call_function_action,
+ [PPC_MSG_RESCHEDULE] = reschedule_action,
+ [PPC_MSG_CALL_FUNC_SINGLE] = call_function_single_action,
+ [PPC_MSG_DEBUGGER_BREAK] = debug_ipi_action,
+};
+
+const char *smp_ipi_name[] = {
+ [PPC_MSG_CALL_FUNCTION] = "ipi call function",
+ [PPC_MSG_RESCHEDULE] = "ipi reschedule",
+ [PPC_MSG_CALL_FUNC_SINGLE] = "ipi call function single",
+ [PPC_MSG_DEBUGGER_BREAK] = "ipi debugger",
+};
+
+/* optional function to request ipi, for controllers with >= 4 ipis */
+int smp_request_message_ipi(int virq, int msg)
+{
+ int err;
+
+ if (msg < 0 || msg > PPC_MSG_DEBUGGER_BREAK) {
+ return -EINVAL;
+ }
+#if !defined(CONFIG_DEBUGGER) && !defined(CONFIG_KEXEC)
+ if (msg == PPC_MSG_DEBUGGER_BREAK) {
+ return 1;
+ }
+#endif
+ err = request_irq(virq, smp_ipi_action[msg], IRQF_DISABLED|IRQF_PERCPU,
+ smp_ipi_name[msg], 0);
+ WARN(err < 0, "unable to request_irq %d for %s (rc %d)\n",
+ virq, smp_ipi_name[msg], err);
+
+ return err;
+}
+
void smp_send_reschedule(int cpu)
{
if (likely(smp_ops))
@@ -404,8 +462,7 @@ out:
static struct device_node *cpu_to_l2cache(int cpu)
{
struct device_node *np;
- const phandle *php;
- phandle ph;
+ struct device_node *cache;
if (!cpu_present(cpu))
return NULL;
@@ -414,13 +471,11 @@ static struct device_node *cpu_to_l2cache(int cpu)
if (np == NULL)
return NULL;
- php = of_get_property(np, "l2-cache", NULL);
- if (php == NULL)
- return NULL;
- ph = *php;
+ cache = of_find_next_cache_node(np);
+
of_node_put(np);
- return of_find_node_by_phandle(ph);
+ return cache;
}
/* Activate a secondary processor. */
diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c
index 77b7b34b5955..560c96119501 100644
--- a/arch/powerpc/kernel/swsusp.c
+++ b/arch/powerpc/kernel/swsusp.c
@@ -34,6 +34,6 @@ void save_processor_state(void)
void restore_processor_state(void)
{
#ifdef CONFIG_PPC32
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
#endif
}
diff --git a/arch/powerpc/kernel/swsusp_32.S b/arch/powerpc/kernel/swsusp_32.S
index 77fc76607ab2..b47d8ceffb52 100644
--- a/arch/powerpc/kernel/swsusp_32.S
+++ b/arch/powerpc/kernel/swsusp_32.S
@@ -5,7 +5,7 @@
#include <asm/thread_info.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
-
+#include <asm/mmu.h>
/*
* Structure for storing CPU registers on the save area.
@@ -279,7 +279,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
mtibatl 3,r4
#endif
-BEGIN_FTR_SECTION
+BEGIN_MMU_FTR_SECTION
li r4,0
mtspr SPRN_DBAT4U,r4
mtspr SPRN_DBAT4L,r4
@@ -297,7 +297,7 @@ BEGIN_FTR_SECTION
mtspr SPRN_IBAT6L,r4
mtspr SPRN_IBAT7U,r4
mtspr SPRN_IBAT7L,r4
-END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
/* Flush all TLBs */
lis r4,0x1000
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 20885a38237a..0c64f10087b9 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -566,7 +566,6 @@ static bool cache_is_unified(struct device_node *np)
static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level)
{
- const phandle *next_cache_phandle;
struct device_node *next_cache;
struct cache_desc *new, **end;
@@ -591,11 +590,7 @@ static struct cache_desc * __cpuinit create_cache_index_info(struct device_node
while (*end)
end = &(*end)->next;
- next_cache_phandle = of_get_property(np, "l2-cache", NULL);
- if (!next_cache_phandle)
- goto out;
-
- next_cache = of_find_node_by_phandle(*next_cache_phandle);
+ next_cache = of_find_next_cache_node(np);
if (!next_cache)
goto out;
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 6f39d35d6f55..99f1ddd68582 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -164,8 +164,6 @@ static u64 tb_to_ns_scale __read_mostly;
static unsigned tb_to_ns_shift __read_mostly;
static unsigned long boot_tb __read_mostly;
-static struct gettimeofday_struct do_gtod;
-
extern struct timezone sys_tz;
static long timezone_offset;
@@ -415,31 +413,9 @@ void udelay(unsigned long usecs)
}
EXPORT_SYMBOL(udelay);
-
-/*
- * There are two copies of tb_to_xs and stamp_xsec so that no
- * lock is needed to access and use these values in
- * do_gettimeofday. We alternate the copies and as long as a
- * reasonable time elapses between changes, there will never
- * be inconsistent values. ntpd has a minimum of one minute
- * between updates.
- */
static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec,
u64 new_tb_to_xs)
{
- unsigned temp_idx;
- struct gettimeofday_vars *temp_varp;
-
- temp_idx = (do_gtod.var_idx == 0);
- temp_varp = &do_gtod.vars[temp_idx];
-
- temp_varp->tb_to_xs = new_tb_to_xs;
- temp_varp->tb_orig_stamp = new_tb_stamp;
- temp_varp->stamp_xsec = new_stamp_xsec;
- smp_mb();
- do_gtod.varp = temp_varp;
- do_gtod.var_idx = temp_idx;
-
/*
* tb_update_count is used to allow the userspace gettimeofday code
* to assure itself that it sees a consistent view of the tb_to_xs and
@@ -456,6 +432,7 @@ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec,
vdso_data->tb_to_xs = new_tb_to_xs;
vdso_data->wtom_clock_sec = wall_to_monotonic.tv_sec;
vdso_data->wtom_clock_nsec = wall_to_monotonic.tv_nsec;
+ vdso_data->stamp_xtime = xtime;
smp_wmb();
++(vdso_data->tb_update_count);
}
@@ -514,9 +491,7 @@ static int __init iSeries_tb_recal(void)
tb_ticks_per_sec = new_tb_ticks_per_sec;
calc_cputime_factors();
div128_by_32( XSEC_PER_SEC, 0, tb_ticks_per_sec, &divres );
- do_gtod.tb_ticks_per_sec = tb_ticks_per_sec;
tb_to_xs = divres.result_low;
- do_gtod.varp->tb_to_xs = tb_to_xs;
vdso_data->tb_ticks_per_sec = tb_ticks_per_sec;
vdso_data->tb_to_xs = tb_to_xs;
}
@@ -988,15 +963,6 @@ void __init time_init(void)
sys_tz.tz_dsttime = 0;
}
- do_gtod.varp = &do_gtod.vars[0];
- do_gtod.var_idx = 0;
- do_gtod.varp->tb_orig_stamp = tb_last_jiffy;
- __get_cpu_var(last_jiffy) = tb_last_jiffy;
- do_gtod.varp->stamp_xsec = (u64) xtime.tv_sec * XSEC_PER_SEC;
- do_gtod.tb_ticks_per_sec = tb_ticks_per_sec;
- do_gtod.varp->tb_to_xs = tb_to_xs;
- do_gtod.tb_to_us = tb_to_us;
-
vdso_data->tb_orig_stamp = tb_last_jiffy;
vdso_data->tb_update_count = 0;
vdso_data->tb_ticks_per_sec = tb_ticks_per_sec;
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index f5def6cf5cd6..5457e9575685 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -1160,37 +1160,85 @@ void CacheLockingException(struct pt_regs *regs, unsigned long address,
#ifdef CONFIG_SPE
void SPEFloatingPointException(struct pt_regs *regs)
{
+ extern int do_spe_mathemu(struct pt_regs *regs);
unsigned long spefscr;
int fpexc_mode;
int code = 0;
+ int err;
+
+ preempt_disable();
+ if (regs->msr & MSR_SPE)
+ giveup_spe(current);
+ preempt_enable();
spefscr = current->thread.spefscr;
fpexc_mode = current->thread.fpexc_mode;
- /* Hardware does not neccessarily set sticky
- * underflow/overflow/invalid flags */
if ((spefscr & SPEFSCR_FOVF) && (fpexc_mode & PR_FP_EXC_OVF)) {
code = FPE_FLTOVF;
- spefscr |= SPEFSCR_FOVFS;
}
else if ((spefscr & SPEFSCR_FUNF) && (fpexc_mode & PR_FP_EXC_UND)) {
code = FPE_FLTUND;
- spefscr |= SPEFSCR_FUNFS;
}
else if ((spefscr & SPEFSCR_FDBZ) && (fpexc_mode & PR_FP_EXC_DIV))
code = FPE_FLTDIV;
else if ((spefscr & SPEFSCR_FINV) && (fpexc_mode & PR_FP_EXC_INV)) {
code = FPE_FLTINV;
- spefscr |= SPEFSCR_FINVS;
}
else if ((spefscr & (SPEFSCR_FG | SPEFSCR_FX)) && (fpexc_mode & PR_FP_EXC_RES))
code = FPE_FLTRES;
- current->thread.spefscr = spefscr;
+ err = do_spe_mathemu(regs);
+ if (err == 0) {
+ regs->nip += 4; /* skip emulated instruction */
+ emulate_single_step(regs);
+ return;
+ }
+
+ if (err == -EFAULT) {
+ /* got an error reading the instruction */
+ _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip);
+ } else if (err == -EINVAL) {
+ /* didn't recognize the instruction */
+ printk(KERN_ERR "unrecognized spe instruction "
+ "in %s at %lx\n", current->comm, regs->nip);
+ } else {
+ _exception(SIGFPE, regs, code, regs->nip);
+ }
- _exception(SIGFPE, regs, code, regs->nip);
return;
}
+
+void SPEFloatingPointRoundException(struct pt_regs *regs)
+{
+ extern int speround_handler(struct pt_regs *regs);
+ int err;
+
+ preempt_disable();
+ if (regs->msr & MSR_SPE)
+ giveup_spe(current);
+ preempt_enable();
+
+ regs->nip -= 4;
+ err = speround_handler(regs);
+ if (err == 0) {
+ regs->nip += 4; /* skip emulated instruction */
+ emulate_single_step(regs);
+ return;
+ }
+
+ if (err == -EFAULT) {
+ /* got an error reading the instruction */
+ _exception(SIGSEGV, regs, SEGV_ACCERR, regs->nip);
+ } else if (err == -EINVAL) {
+ /* didn't recognize the instruction */
+ printk(KERN_ERR "unrecognized spe instruction "
+ "in %s at %lx\n", current->comm, regs->nip);
+ } else {
+ _exception(SIGFPE, regs, 0, regs->nip);
+ return;
+ }
+}
#endif
/*
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c
index 65639a43e644..ad06d5c75b15 100644
--- a/arch/powerpc/kernel/vdso.c
+++ b/arch/powerpc/kernel/vdso.c
@@ -184,8 +184,7 @@ static void dump_vdso_pages(struct vm_area_struct * vma)
* This is called from binfmt_elf, we create the special vma for the
* vDSO and insert it into the mm struct tree
*/
-int arch_setup_additional_pages(struct linux_binprm *bprm,
- int executable_stack)
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
struct page **vdso_pagelist;
@@ -567,6 +566,11 @@ static __init int vdso_fixup_features(struct lib32_elfinfo *v32,
do_feature_fixups(cur_cpu_spec->cpu_features,
start64, start64 + size64);
+ start64 = find_section64(v64->hdr, "__mmu_ftr_fixup", &size64);
+ if (start64)
+ do_feature_fixups(cur_cpu_spec->mmu_features,
+ start64, start64 + size64);
+
start64 = find_section64(v64->hdr, "__fw_ftr_fixup", &size64);
if (start64)
do_feature_fixups(powerpc_firmware_features,
@@ -583,6 +587,11 @@ static __init int vdso_fixup_features(struct lib32_elfinfo *v32,
do_feature_fixups(cur_cpu_spec->cpu_features,
start32, start32 + size32);
+ start32 = find_section32(v32->hdr, "__mmu_ftr_fixup", &size32);
+ if (start32)
+ do_feature_fixups(cur_cpu_spec->mmu_features,
+ start32, start32 + size32);
+
#ifdef CONFIG_PPC64
start32 = find_section32(v32->hdr, "__fw_ftr_fixup", &size32);
if (start32)
diff --git a/arch/powerpc/kernel/vdso32/gettimeofday.S b/arch/powerpc/kernel/vdso32/gettimeofday.S
index 72ca26df457e..ee038d4bf252 100644
--- a/arch/powerpc/kernel/vdso32/gettimeofday.S
+++ b/arch/powerpc/kernel/vdso32/gettimeofday.S
@@ -16,6 +16,13 @@
#include <asm/asm-offsets.h>
#include <asm/unistd.h>
+/* Offset for the low 32-bit part of a field of long type */
+#ifdef CONFIG_PPC64
+#define LOPART 4
+#else
+#define LOPART 0
+#endif
+
.text
/*
* Exact prototype of gettimeofday
@@ -90,101 +97,53 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
mflr r12 /* r12 saves lr */
.cfi_register lr,r12
- mr r10,r3 /* r10 saves id */
mr r11,r4 /* r11 saves tp */
bl __get_datapage@local /* get data page */
mr r9,r3 /* datapage ptr in r9 */
- beq cr1,50f /* if monotonic -> jump there */
-
- /*
- * CLOCK_REALTIME
- */
-
- bl __do_get_xsec@local /* get xsec from tb & kernel */
- bne- 98f /* out of line -> do syscall */
-
- /* seconds are xsec >> 20 */
- rlwinm r5,r4,12,20,31
- rlwimi r5,r3,12,0,19
- stw r5,TSPC32_TV_SEC(r11)
- /* get remaining xsec and convert to nsec. we scale
- * up remaining xsec by 12 bits and get the top 32 bits
- * of the multiplication, then we multiply by 1000
- */
- rlwinm r5,r4,12,0,19
- lis r6,1000000@h
- ori r6,r6,1000000@l
- mulhwu r5,r5,r6
- mulli r5,r5,1000
- stw r5,TSPC32_TV_NSEC(r11)
- mtlr r12
- crclr cr0*4+so
- li r3,0
- blr
+50: bl __do_get_tspec@local /* get sec/nsec from tb & kernel */
+ bne cr1,80f /* not monotonic -> all done */
/*
* CLOCK_MONOTONIC
*/
-50: bl __do_get_xsec@local /* get xsec from tb & kernel */
- bne- 98f /* out of line -> do syscall */
-
- /* seconds are xsec >> 20 */
- rlwinm r6,r4,12,20,31
- rlwimi r6,r3,12,0,19
-
- /* get remaining xsec and convert to nsec. we scale
- * up remaining xsec by 12 bits and get the top 32 bits
- * of the multiplication, then we multiply by 1000
- */
- rlwinm r7,r4,12,0,19
- lis r5,1000000@h
- ori r5,r5,1000000@l
- mulhwu r7,r7,r5
- mulli r7,r7,1000
-
/* now we must fixup using wall to monotonic. We need to snapshot
* that value and do the counter trick again. Fortunately, we still
* have the counter value in r8 that was returned by __do_get_xsec.
- * At this point, r6,r7 contain our sec/nsec values, r3,r4 and r5
- * can be used
+ * At this point, r3,r4 contain our sec/nsec values, r5 and r6
+ * can be used, r7 contains NSEC_PER_SEC.
*/
- lwz r3,WTOM_CLOCK_SEC(r9)
- lwz r4,WTOM_CLOCK_NSEC(r9)
+ lwz r5,WTOM_CLOCK_SEC(r9)
+ lwz r6,WTOM_CLOCK_NSEC(r9)
- /* We now have our result in r3,r4. We create a fake dependency
- * on that result and re-check the counter
+ /* We now have our offset in r5,r6. We create a fake dependency
+ * on that value and re-check the counter
*/
- or r5,r4,r3
- xor r0,r5,r5
+ or r0,r6,r5
+ xor r0,r0,r0
add r9,r9,r0
-#ifdef CONFIG_PPC64
- lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9)
-#else
- lwz r0,(CFG_TB_UPDATE_COUNT)(r9)
-#endif
+ lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
cmpl cr0,r8,r0 /* check if updated */
bne- 50b
- /* Calculate and store result. Note that this mimmics the C code,
+ /* Calculate and store result. Note that this mimics the C code,
* which may cause funny results if nsec goes negative... is that
* possible at all ?
*/
- add r3,r3,r6
- add r4,r4,r7
- lis r5,NSEC_PER_SEC@h
- ori r5,r5,NSEC_PER_SEC@l
- cmpl cr0,r4,r5
- cmpli cr1,r4,0
+ add r3,r3,r5
+ add r4,r4,r6
+ cmpw cr0,r4,r7
+ cmpwi cr1,r4,0
blt 1f
- subf r4,r5,r4
+ subf r4,r7,r4
addi r3,r3,1
-1: bge cr1,1f
+1: bge cr1,80f
addi r3,r3,-1
- add r4,r4,r5
-1: stw r3,TSPC32_TV_SEC(r11)
+ add r4,r4,r7
+
+80: stw r3,TSPC32_TV_SEC(r11)
stw r4,TSPC32_TV_NSEC(r11)
mtlr r12
@@ -195,10 +154,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
/*
* syscall fallback
*/
-98:
- mtlr r12
- mr r3,r10
- mr r4,r11
99:
li r0,__NR_clock_gettime
sc
@@ -254,11 +209,7 @@ __do_get_xsec:
/* Check for update count & load values. We use the low
* order 32 bits of the update count
*/
-#ifdef CONFIG_PPC64
-1: lwz r8,(CFG_TB_UPDATE_COUNT+4)(r9)
-#else
-1: lwz r8,(CFG_TB_UPDATE_COUNT)(r9)
-#endif
+1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
andi. r0,r8,1 /* pending update ? loop */
bne- 1b
xor r0,r8,r8 /* create dependency */
@@ -305,11 +256,7 @@ __do_get_xsec:
or r6,r4,r3
xor r0,r6,r6
add r9,r9,r0
-#ifdef CONFIG_PPC64
- lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9)
-#else
- lwz r0,(CFG_TB_UPDATE_COUNT)(r9)
-#endif
+ lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
cmpl cr0,r8,r0 /* check if updated */
bne- 1b
@@ -322,3 +269,98 @@ __do_get_xsec:
*/
3: blr
.cfi_endproc
+
+/*
+ * This is the core of clock_gettime(), it returns the current
+ * time in seconds and nanoseconds in r3 and r4.
+ * It expects the datapage ptr in r9 and doesn't clobber it.
+ * It clobbers r0, r5, r6, r10 and returns NSEC_PER_SEC in r7.
+ * On return, r8 contains the counter value that can be reused.
+ * This clobbers cr0 but not any other cr field.
+ */
+__do_get_tspec:
+ .cfi_startproc
+ /* Check for update count & load values. We use the low
+ * order 32 bits of the update count
+ */
+1: lwz r8,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
+ andi. r0,r8,1 /* pending update ? loop */
+ bne- 1b
+ xor r0,r8,r8 /* create dependency */
+ add r9,r9,r0
+
+ /* Load orig stamp (offset to TB) */
+ lwz r5,CFG_TB_ORIG_STAMP(r9)
+ lwz r6,(CFG_TB_ORIG_STAMP+4)(r9)
+
+ /* Get a stable TB value */
+2: mftbu r3
+ mftbl r4
+ mftbu r0
+ cmpl cr0,r3,r0
+ bne- 2b
+
+ /* Subtract tb orig stamp and shift left 12 bits.
+ */
+ subfc r7,r6,r4
+ subfe r0,r5,r3
+ slwi r0,r0,12
+ rlwimi. r0,r7,12,20,31
+ slwi r7,r7,12
+
+ /* Load scale factor & do multiplication */
+ lwz r5,CFG_TB_TO_XS(r9) /* load values */
+ lwz r6,(CFG_TB_TO_XS+4)(r9)
+ mulhwu r3,r7,r6
+ mullw r10,r7,r5
+ mulhwu r4,r7,r5
+ addc r10,r3,r10
+ li r3,0
+
+ beq+ 4f /* skip high part computation if 0 */
+ mulhwu r3,r0,r5
+ mullw r7,r0,r5
+ mulhwu r5,r0,r6
+ mullw r6,r0,r6
+ adde r4,r4,r7
+ addze r3,r3
+ addc r4,r4,r5
+ addze r3,r3
+ addc r10,r10,r6
+
+4: addze r4,r4 /* add in carry */
+ lis r7,NSEC_PER_SEC@h
+ ori r7,r7,NSEC_PER_SEC@l
+ mulhwu r4,r4,r7 /* convert to nanoseconds */
+
+ /* At this point, we have seconds & nanoseconds since the xtime
+ * stamp in r3+CA and r4. Load & add the xtime stamp.
+ */
+#ifdef CONFIG_PPC64
+ lwz r5,STAMP_XTIME+TSPC64_TV_SEC+LOPART(r9)
+ lwz r6,STAMP_XTIME+TSPC64_TV_NSEC+LOPART(r9)
+#else
+ lwz r5,STAMP_XTIME+TSPC32_TV_SEC(r9)
+ lwz r6,STAMP_XTIME+TSPC32_TV_NSEC(r9)
+#endif
+ add r4,r4,r6
+ adde r3,r3,r5
+
+ /* We now have our result in r3,r4. We create a fake dependency
+ * on that result and re-check the counter
+ */
+ or r6,r4,r3
+ xor r0,r6,r6
+ add r9,r9,r0
+ lwz r0,(CFG_TB_UPDATE_COUNT+LOPART)(r9)
+ cmpl cr0,r8,r0 /* check if updated */
+ bne- 1b
+
+ /* check for nanosecond overflow and adjust if necessary */
+ cmpw r4,r7
+ bltlr /* all done if no overflow */
+ subf r4,r7,r4 /* adjust if overflow */
+ addi r3,r3,1
+
+ blr
+ .cfi_endproc
diff --git a/arch/powerpc/kernel/vdso32/vdso32.lds.S b/arch/powerpc/kernel/vdso32/vdso32.lds.S
index be3b6a41dc09..904ef1360dd7 100644
--- a/arch/powerpc/kernel/vdso32/vdso32.lds.S
+++ b/arch/powerpc/kernel/vdso32/vdso32.lds.S
@@ -34,6 +34,9 @@ SECTIONS
__ftr_fixup : { *(__ftr_fixup) }
. = ALIGN(8);
+ __mmu_ftr_fixup : { *(__mmu_ftr_fixup) }
+
+ . = ALIGN(8);
__lwsync_fixup : { *(__lwsync_fixup) }
#ifdef CONFIG_PPC64
diff --git a/arch/powerpc/kernel/vdso64/gettimeofday.S b/arch/powerpc/kernel/vdso64/gettimeofday.S
index c6401f9e37f1..262cd5857a56 100644
--- a/arch/powerpc/kernel/vdso64/gettimeofday.S
+++ b/arch/powerpc/kernel/vdso64/gettimeofday.S
@@ -75,90 +75,49 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
mflr r12 /* r12 saves lr */
.cfi_register lr,r12
- mr r10,r3 /* r10 saves id */
mr r11,r4 /* r11 saves tp */
bl V_LOCAL_FUNC(__get_datapage) /* get data page */
- beq cr1,50f /* if monotonic -> jump there */
-
- /*
- * CLOCK_REALTIME
- */
-
- bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */
-
- lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */
- ori r7,r7,16960
- rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */
- rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */
- std r5,TSPC64_TV_SEC(r11) /* store sec in tv */
- subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */
- mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) /
- * XSEC_PER_SEC
- */
- rldicl r0,r0,44,20
- mulli r0,r0,1000 /* nsec = usec * 1000 */
- std r0,TSPC64_TV_NSEC(r11) /* store nsec in tp */
-
- mtlr r12
- crclr cr0*4+so
- li r3,0
- blr
+50: bl V_LOCAL_FUNC(__do_get_tspec) /* get time from tb & kernel */
+ bne cr1,80f /* if not monotonic, all done */
/*
* CLOCK_MONOTONIC
*/
-50: bl V_LOCAL_FUNC(__do_get_xsec) /* get xsec from tb & kernel */
-
- lis r7,15 /* r7 = 1000000 = USEC_PER_SEC */
- ori r7,r7,16960
- rldicl r5,r4,44,20 /* r5 = sec = xsec / XSEC_PER_SEC */
- rldicr r6,r5,20,43 /* r6 = sec * XSEC_PER_SEC */
- subf r0,r6,r4 /* r0 = xsec = (xsec - r6) */
- mulld r0,r0,r7 /* usec = (xsec * USEC_PER_SEC) /
- * XSEC_PER_SEC
- */
- rldicl r6,r0,44,20
- mulli r6,r6,1000 /* nsec = usec * 1000 */
-
/* now we must fixup using wall to monotonic. We need to snapshot
* that value and do the counter trick again. Fortunately, we still
- * have the counter value in r8 that was returned by __do_get_xsec.
- * At this point, r5,r6 contain our sec/nsec values.
- * can be used
+ * have the counter value in r8 that was returned by __do_get_tspec.
+ * At this point, r4,r5 contain our sec/nsec values.
*/
- lwa r4,WTOM_CLOCK_SEC(r3)
- lwa r7,WTOM_CLOCK_NSEC(r3)
+ lwa r6,WTOM_CLOCK_SEC(r3)
+ lwa r9,WTOM_CLOCK_NSEC(r3)
- /* We now have our result in r4,r7. We create a fake dependency
+ /* We now have our result in r6,r9. We create a fake dependency
* on that result and re-check the counter
*/
- or r9,r4,r7
- xor r0,r9,r9
+ or r0,r6,r9
+ xor r0,r0,r0
add r3,r3,r0
ld r0,CFG_TB_UPDATE_COUNT(r3)
cmpld cr0,r0,r8 /* check if updated */
bne- 50b
- /* Calculate and store result. Note that this mimmics the C code,
- * which may cause funny results if nsec goes negative... is that
- * possible at all ?
+ /* Add wall->monotonic offset and check for overflow or underflow.
*/
- add r4,r4,r5
- add r7,r7,r6
- lis r9,NSEC_PER_SEC@h
- ori r9,r9,NSEC_PER_SEC@l
- cmpl cr0,r7,r9
- cmpli cr1,r7,0
+ add r4,r4,r6
+ add r5,r5,r9
+ cmpd cr0,r5,r7
+ cmpdi cr1,r5,0
blt 1f
- subf r7,r9,r7
+ subf r5,r7,r5
addi r4,r4,1
-1: bge cr1,1f
+1: bge cr1,80f
addi r4,r4,-1
- add r7,r7,r9
-1: std r4,TSPC64_TV_SEC(r11)
- std r7,TSPC64_TV_NSEC(r11)
+ add r5,r5,r7
+
+80: std r4,TSPC64_TV_SEC(r11)
+ std r5,TSPC64_TV_NSEC(r11)
mtlr r12
crclr cr0*4+so
@@ -168,10 +127,6 @@ V_FUNCTION_BEGIN(__kernel_clock_gettime)
/*
* syscall fallback
*/
-98:
- mtlr r12
- mr r3,r10
- mr r4,r11
99:
li r0,__NR_clock_gettime
sc
@@ -253,3 +208,59 @@ V_FUNCTION_BEGIN(__do_get_xsec)
blr
.cfi_endproc
V_FUNCTION_END(__do_get_xsec)
+
+/*
+ * This is the core of clock_gettime(), it returns the current
+ * time in seconds and nanoseconds in r4 and r5.
+ * It expects the datapage ptr in r3 and doesn't clobber it.
+ * It clobbers r0 and r6 and returns NSEC_PER_SEC in r7.
+ * On return, r8 contains the counter value that can be reused.
+ * This clobbers cr0 but not any other cr field.
+ */
+V_FUNCTION_BEGIN(__do_get_tspec)
+ .cfi_startproc
+ /* check for update count & load values */
+1: ld r8,CFG_TB_UPDATE_COUNT(r3)
+ andi. r0,r8,1 /* pending update ? loop */
+ bne- 1b
+ xor r0,r8,r8 /* create dependency */
+ add r3,r3,r0
+
+ /* Get TB & offset it. We use the MFTB macro which will generate
+ * workaround code for Cell.
+ */
+ MFTB(r7)
+ ld r9,CFG_TB_ORIG_STAMP(r3)
+ subf r7,r9,r7
+
+ /* Scale result */
+ ld r5,CFG_TB_TO_XS(r3)
+ sldi r7,r7,12 /* compute time since stamp_xtime */
+ mulhdu r6,r7,r5 /* in units of 2^-32 seconds */
+
+ /* Add stamp since epoch */
+ ld r4,STAMP_XTIME+TSPC64_TV_SEC(r3)
+ ld r5,STAMP_XTIME+TSPC64_TV_NSEC(r3)
+ or r0,r4,r5
+ or r0,r0,r6
+ xor r0,r0,r0
+ add r3,r3,r0
+ ld r0,CFG_TB_UPDATE_COUNT(r3)
+ cmpld r0,r8 /* check if updated */
+ bne- 1b /* reload if so */
+
+ /* convert to seconds & nanoseconds and add to stamp */
+ lis r7,NSEC_PER_SEC@h
+ ori r7,r7,NSEC_PER_SEC@l
+ mulhwu r0,r6,r7 /* compute nanoseconds and */
+ srdi r6,r6,32 /* seconds since stamp_xtime */
+ clrldi r0,r0,32
+ add r5,r5,r0 /* add nanoseconds together */
+ cmpd r5,r7 /* overflow? */
+ add r4,r4,r6
+ bltlr /* all done if no overflow */
+ subf r5,r7,r5 /* if overflow, adjust */
+ addi r4,r4,1
+ blr
+ .cfi_endproc
+V_FUNCTION_END(__do_get_tspec)
diff --git a/arch/powerpc/kernel/vdso64/vdso64.lds.S b/arch/powerpc/kernel/vdso64/vdso64.lds.S
index d0b2526dd38d..0e615404e247 100644
--- a/arch/powerpc/kernel/vdso64/vdso64.lds.S
+++ b/arch/powerpc/kernel/vdso64/vdso64.lds.S
@@ -35,6 +35,9 @@ SECTIONS
__ftr_fixup : { *(__ftr_fixup) }
. = ALIGN(8);
+ __mmu_ftr_fixup : { *(__mmu_ftr_fixup) }
+
+ . = ALIGN(8);
__lwsync_fixup : { *(__lwsync_fixup) }
. = ALIGN(8);
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index a11e6bc59b30..94aa7b011b27 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -41,9 +41,9 @@
static struct bus_type vio_bus_type;
static struct vio_dev vio_bus_device = { /* fake "parent" device */
- .name = vio_bus_device.dev.bus_id,
+ .name = "vio",
.type = "",
- .dev.bus_id = "vio",
+ .dev.init_name = "vio",
.dev.bus = &vio_bus_type,
};
@@ -1216,7 +1216,7 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node)
viodev->irq = irq_of_parse_and_map(of_node, 0);
- snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address);
+ dev_set_name(&viodev->dev, "%x", *unit_address);
viodev->name = of_node->name;
viodev->type = of_node->type;
viodev->unit_address = *unit_address;
@@ -1243,7 +1243,7 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node)
/* register with generic device framework */
if (device_register(&viodev->dev)) {
printk(KERN_ERR "%s: failed to register device %s\n",
- __func__, viodev->dev.bus_id);
+ __func__, dev_name(&viodev->dev));
/* XXX free TCE table */
kfree(viodev);
return NULL;
@@ -1400,13 +1400,13 @@ static struct vio_dev *vio_find_name(const char *name)
struct vio_dev *vio_find_node(struct device_node *vnode)
{
const uint32_t *unit_address;
- char kobj_name[BUS_ID_SIZE];
+ char kobj_name[20];
/* construct the kobject name from the device node */
unit_address = of_get_property(vnode, "reg", NULL);
if (!unit_address)
return NULL;
- snprintf(kobj_name, BUS_ID_SIZE, "%x", *unit_address);
+ snprintf(kobj_name, sizeof(kobj_name), "%x", *unit_address);
return vio_find_name(kobj_name);
}
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 2412c056baa4..47bf15cd2c9e 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -152,6 +152,12 @@ SECTIONS
__stop___ftr_fixup = .;
}
. = ALIGN(8);
+ __mmu_ftr_fixup : AT(ADDR(__mmu_ftr_fixup) - LOAD_OFFSET) {
+ __start___mmu_ftr_fixup = .;
+ *(__mmu_ftr_fixup)
+ __stop___mmu_ftr_fixup = .;
+ }
+ . = ALIGN(8);
__lwsync_fixup : AT(ADDR(__lwsync_fixup) - LOAD_OFFSET) {
__start___lwsync_fixup = .;
*(__lwsync_fixup)
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index fda9baada132..8bef0efcdfe1 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -28,6 +28,7 @@
#include <asm/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/tlbflush.h>
+#include "../mm/mmu_decl.h"
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
@@ -330,7 +331,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
/* XXX It would be nice to differentiate between heavyweight exit and
* sched_out here, since we could avoid the TLB flush for heavyweight
* exits. */
- _tlbia();
+ _tlbil_all();
}
int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S
index 25ec5378afa4..70693a5c12a1 100644
--- a/arch/powerpc/lib/copyuser_64.S
+++ b/arch/powerpc/lib/copyuser_64.S
@@ -26,11 +26,24 @@ _GLOBAL(__copy_tofrom_user)
andi. r6,r6,7
PPC_MTOCRF 0x01,r5
blt cr1,.Lshort_copy
+/* Below we want to nop out the bne if we're on a CPU that has the
+ * CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit
+ * cleared.
+ * At the time of writing the only CPU that has this combination of bits
+ * set is Power6.
+ */
+BEGIN_FTR_SECTION
+ nop
+FTR_SECTION_ELSE
bne .Ldst_unaligned
+ALT_FTR_SECTION_END(CPU_FTR_UNALIGNED_LD_STD | CPU_FTR_CP_USE_DCBTZ, \
+ CPU_FTR_UNALIGNED_LD_STD)
.Ldst_aligned:
- andi. r0,r4,7
addi r3,r3,-16
+BEGIN_FTR_SECTION
+ andi. r0,r4,7
bne .Lsrc_unaligned
+END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
srdi r7,r5,4
20: ld r9,0(r4)
addi r4,r4,-8
@@ -138,7 +151,7 @@ _GLOBAL(__copy_tofrom_user)
PPC_MTOCRF 0x01,r6 /* put #bytes to 8B bdry into cr7 */
subf r5,r6,r5
li r7,0
- cmpldi r1,r5,16
+ cmpldi cr1,r5,16
bf cr7*4+3,1f
35: lbz r0,0(r4)
81: stb r0,0(r3)
diff --git a/arch/powerpc/lib/dma-noncoherent.c b/arch/powerpc/lib/dma-noncoherent.c
index 31734c0969cd..b7dc4c19f582 100644
--- a/arch/powerpc/lib/dma-noncoherent.c
+++ b/arch/powerpc/lib/dma-noncoherent.c
@@ -77,26 +77,26 @@ static DEFINE_SPINLOCK(consistent_lock);
* the amount of RAM found at boot time.) I would imagine that get_vm_area()
* would have to initialise this each time prior to calling vm_region_alloc().
*/
-struct vm_region {
+struct ppc_vm_region {
struct list_head vm_list;
unsigned long vm_start;
unsigned long vm_end;
};
-static struct vm_region consistent_head = {
+static struct ppc_vm_region consistent_head = {
.vm_list = LIST_HEAD_INIT(consistent_head.vm_list),
.vm_start = CONSISTENT_BASE,
.vm_end = CONSISTENT_END,
};
-static struct vm_region *
-vm_region_alloc(struct vm_region *head, size_t size, gfp_t gfp)
+static struct ppc_vm_region *
+ppc_vm_region_alloc(struct ppc_vm_region *head, size_t size, gfp_t gfp)
{
unsigned long addr = head->vm_start, end = head->vm_end - size;
unsigned long flags;
- struct vm_region *c, *new;
+ struct ppc_vm_region *c, *new;
- new = kmalloc(sizeof(struct vm_region), gfp);
+ new = kmalloc(sizeof(struct ppc_vm_region), gfp);
if (!new)
goto out;
@@ -130,9 +130,9 @@ vm_region_alloc(struct vm_region *head, size_t size, gfp_t gfp)
return NULL;
}
-static struct vm_region *vm_region_find(struct vm_region *head, unsigned long addr)
+static struct ppc_vm_region *ppc_vm_region_find(struct ppc_vm_region *head, unsigned long addr)
{
- struct vm_region *c;
+ struct ppc_vm_region *c;
list_for_each_entry(c, &head->vm_list, vm_list) {
if (c->vm_start == addr)
@@ -151,7 +151,7 @@ void *
__dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
{
struct page *page;
- struct vm_region *c;
+ struct ppc_vm_region *c;
unsigned long order;
u64 mask = 0x00ffffff, limit; /* ISA default */
@@ -191,7 +191,7 @@ __dma_alloc_coherent(size_t size, dma_addr_t *handle, gfp_t gfp)
/*
* Allocate a virtual address in the consistent mapping region.
*/
- c = vm_region_alloc(&consistent_head, size,
+ c = ppc_vm_region_alloc(&consistent_head, size,
gfp & ~(__GFP_DMA | __GFP_HIGHMEM));
if (c) {
unsigned long vaddr = c->vm_start;
@@ -239,7 +239,7 @@ EXPORT_SYMBOL(__dma_alloc_coherent);
*/
void __dma_free_coherent(size_t size, void *vaddr)
{
- struct vm_region *c;
+ struct ppc_vm_region *c;
unsigned long flags, addr;
pte_t *ptep;
@@ -247,7 +247,7 @@ void __dma_free_coherent(size_t size, void *vaddr)
spin_lock_irqsave(&consistent_lock, flags);
- c = vm_region_find(&consistent_head, (unsigned long)vaddr);
+ c = ppc_vm_region_find(&consistent_head, (unsigned long)vaddr);
if (!c)
goto no_area;
@@ -320,7 +320,6 @@ static int __init dma_alloc_init(void)
ret = -ENOMEM;
break;
}
- WARN_ON(!pmd_none(*pmd));
pte = pte_alloc_kernel(pmd, CONSISTENT_BASE);
if (!pte) {
diff --git a/arch/powerpc/lib/memcpy_64.S b/arch/powerpc/lib/memcpy_64.S
index 3f131129d1c1..fe2d34e5332d 100644
--- a/arch/powerpc/lib/memcpy_64.S
+++ b/arch/powerpc/lib/memcpy_64.S
@@ -18,11 +18,23 @@ _GLOBAL(memcpy)
andi. r6,r6,7
dcbt 0,r4
blt cr1,.Lshort_copy
+/* Below we want to nop out the bne if we're on a CPU that has the
+ CPU_FTR_UNALIGNED_LD_STD bit set and the CPU_FTR_CP_USE_DCBTZ bit
+ cleared.
+ At the time of writing the only CPU that has this combination of bits
+ set is Power6. */
+BEGIN_FTR_SECTION
+ nop
+FTR_SECTION_ELSE
bne .Ldst_unaligned
+ALT_FTR_SECTION_END(CPU_FTR_UNALIGNED_LD_STD | CPU_FTR_CP_USE_DCBTZ, \
+ CPU_FTR_UNALIGNED_LD_STD)
.Ldst_aligned:
- andi. r0,r4,7
addi r3,r3,-16
+BEGIN_FTR_SECTION
+ andi. r0,r4,7
bne .Lsrc_unaligned
+END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
srdi r7,r5,4
ld r9,0(r4)
addi r4,r4,-8
@@ -131,7 +143,7 @@ _GLOBAL(memcpy)
PPC_MTOCRF 0x01,r6 # put #bytes to 8B bdry into cr7
subf r5,r6,r5
li r7,0
- cmpldi r1,r5,16
+ cmpldi cr1,r5,16
bf cr7*4+3,1f
lbz r0,0(r4)
stb r0,0(r3)
diff --git a/arch/powerpc/math-emu/Makefile b/arch/powerpc/math-emu/Makefile
index 03aa98dd9f0a..f9e506a735ae 100644
--- a/arch/powerpc/math-emu/Makefile
+++ b/arch/powerpc/math-emu/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_MATH_EMULATION) += fabs.o fadd.o fadds.o fcmpo.o fcmpu.o \
mcrfs.o mffs.o mtfsb0.o mtfsb1.o \
mtfsf.o mtfsfi.o stfiwx.o stfs.o
+obj-$(CONFIG_SPE) += math_efp.o
+
CFLAGS_fabs.o = -fno-builtin-fabs
CFLAGS_math.o = -fno-builtin-fabs
diff --git a/arch/powerpc/math-emu/fadd.c b/arch/powerpc/math-emu/fadd.c
index 04d3b4aa32ce..0158a16e2b82 100644
--- a/arch/powerpc/math-emu/fadd.c
+++ b/arch/powerpc/math-emu/fadd.c
@@ -13,7 +13,6 @@ fadd(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
diff --git a/arch/powerpc/math-emu/fcmpo.c b/arch/powerpc/math-emu/fcmpo.c
index b5dc4498cd71..5bce011c2aec 100644
--- a/arch/powerpc/math-emu/fcmpo.c
+++ b/arch/powerpc/math-emu/fcmpo.c
@@ -14,7 +14,6 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB)
FP_DECL_EX;
int code[4] = { (1 << 3), (1 << 1), (1 << 2), (1 << 0) };
long cmp;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p (%08x) %d %p %p\n", __func__, ccr, *ccr, crfD, frA, frB);
@@ -29,7 +28,7 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB)
#endif
if (A_c == FP_CLS_NAN || B_c == FP_CLS_NAN)
- ret |= EFLAG_VXVC;
+ FP_SET_EXCEPTION(EFLAG_VXVC);
FP_CMP_D(cmp, A, B, 2);
cmp = code[(cmp + 1) & 3];
@@ -44,5 +43,5 @@ fcmpo(u32 *ccr, int crfD, void *frA, void *frB)
printk("CR: %08x\n", *ccr);
#endif
- return ret;
+ return FP_CUR_EXCEPTIONS;
}
diff --git a/arch/powerpc/math-emu/fdiv.c b/arch/powerpc/math-emu/fdiv.c
index 2db15097d98e..a29239c05e3e 100644
--- a/arch/powerpc/math-emu/fdiv.c
+++ b/arch/powerpc/math-emu/fdiv.c
@@ -13,7 +13,6 @@ fdiv(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -28,22 +27,22 @@ fdiv(void *frD, void *frA, void *frB)
#endif
if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) {
- ret |= EFLAG_VXZDZ;
+ FP_SET_EXCEPTION(EFLAG_VXZDZ);
#ifdef DEBUG
printk("%s: FPSCR_VXZDZ raised\n", __func__);
#endif
}
if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) {
- ret |= EFLAG_VXIDI;
+ FP_SET_EXCEPTION(EFLAG_VXIDI);
#ifdef DEBUG
printk("%s: FPSCR_VXIDI raised\n", __func__);
#endif
}
if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) {
- ret |= EFLAG_DIVZERO;
+ FP_SET_EXCEPTION(EFLAG_DIVZERO);
if (__FPU_TRAP_P(EFLAG_DIVZERO))
- return ret;
+ return FP_CUR_EXCEPTIONS;
}
FP_DIV_D(R, A, B);
diff --git a/arch/powerpc/math-emu/fdivs.c b/arch/powerpc/math-emu/fdivs.c
index 797f6a9a20b5..526bc261275f 100644
--- a/arch/powerpc/math-emu/fdivs.c
+++ b/arch/powerpc/math-emu/fdivs.c
@@ -14,7 +14,6 @@ fdivs(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -29,22 +28,22 @@ fdivs(void *frD, void *frA, void *frB)
#endif
if (A_c == FP_CLS_ZERO && B_c == FP_CLS_ZERO) {
- ret |= EFLAG_VXZDZ;
+ FP_SET_EXCEPTION(EFLAG_VXZDZ);
#ifdef DEBUG
printk("%s: FPSCR_VXZDZ raised\n", __func__);
#endif
}
if (A_c == FP_CLS_INF && B_c == FP_CLS_INF) {
- ret |= EFLAG_VXIDI;
+ FP_SET_EXCEPTION(EFLAG_VXIDI);
#ifdef DEBUG
printk("%s: FPSCR_VXIDI raised\n", __func__);
#endif
}
if (B_c == FP_CLS_ZERO && A_c != FP_CLS_ZERO) {
- ret |= EFLAG_DIVZERO;
+ FP_SET_EXCEPTION(EFLAG_DIVZERO);
if (__FPU_TRAP_P(EFLAG_DIVZERO))
- return ret;
+ return FP_CUR_EXCEPTIONS;
}
FP_DIV_D(R, A, B);
diff --git a/arch/powerpc/math-emu/fmadd.c b/arch/powerpc/math-emu/fmadd.c
index 925313aa6f82..8c3f20aa5a95 100644
--- a/arch/powerpc/math-emu/fmadd.c
+++ b/arch/powerpc/math-emu/fmadd.c
@@ -15,7 +15,6 @@ fmadd(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -33,12 +32,12 @@ fmadd(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fmadds.c b/arch/powerpc/math-emu/fmadds.c
index aea80ef79399..794fb31e59d1 100644
--- a/arch/powerpc/math-emu/fmadds.c
+++ b/arch/powerpc/math-emu/fmadds.c
@@ -16,7 +16,6 @@ fmadds(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -34,12 +33,12 @@ fmadds(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fmsub.c b/arch/powerpc/math-emu/fmsub.c
index a644d525fca6..626f6fed84ac 100644
--- a/arch/powerpc/math-emu/fmsub.c
+++ b/arch/powerpc/math-emu/fmsub.c
@@ -15,7 +15,6 @@ fmsub(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -33,7 +32,7 @@ fmsub(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
@@ -41,7 +40,7 @@ fmsub(void *frD, void *frA, void *frB, void *frC)
B_s ^= 1;
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fmsubs.c b/arch/powerpc/math-emu/fmsubs.c
index 2fdeeb9bb569..3425bc899760 100644
--- a/arch/powerpc/math-emu/fmsubs.c
+++ b/arch/powerpc/math-emu/fmsubs.c
@@ -16,7 +16,6 @@ fmsubs(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -34,7 +33,7 @@ fmsubs(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
@@ -42,7 +41,7 @@ fmsubs(void *frD, void *frA, void *frB, void *frC)
B_s ^= 1;
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fmul.c b/arch/powerpc/math-emu/fmul.c
index 391fd17d3440..2c1929779892 100644
--- a/arch/powerpc/math-emu/fmul.c
+++ b/arch/powerpc/math-emu/fmul.c
@@ -13,7 +13,6 @@ fmul(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -31,7 +30,7 @@ fmul(void *frD, void *frA, void *frB)
if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && B_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(R, A, B);
diff --git a/arch/powerpc/math-emu/fmuls.c b/arch/powerpc/math-emu/fmuls.c
index 2d3ec5f7da20..f5ad5c9c77d0 100644
--- a/arch/powerpc/math-emu/fmuls.c
+++ b/arch/powerpc/math-emu/fmuls.c
@@ -14,7 +14,6 @@ fmuls(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -32,7 +31,7 @@ fmuls(void *frD, void *frA, void *frB)
if ((A_c == FP_CLS_INF && B_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && B_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(R, A, B);
diff --git a/arch/powerpc/math-emu/fnmadd.c b/arch/powerpc/math-emu/fnmadd.c
index 2497b86494e5..e817bc5453ef 100644
--- a/arch/powerpc/math-emu/fnmadd.c
+++ b/arch/powerpc/math-emu/fnmadd.c
@@ -15,7 +15,6 @@ fnmadd(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -33,12 +32,12 @@ fnmadd(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fnmadds.c b/arch/powerpc/math-emu/fnmadds.c
index ee9d71e0b376..4db4b7d9ba8d 100644
--- a/arch/powerpc/math-emu/fnmadds.c
+++ b/arch/powerpc/math-emu/fnmadds.c
@@ -16,7 +16,6 @@ fnmadds(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -34,12 +33,12 @@ fnmadds(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fnmsub.c b/arch/powerpc/math-emu/fnmsub.c
index 3885a77acc93..f65979fa770e 100644
--- a/arch/powerpc/math-emu/fnmsub.c
+++ b/arch/powerpc/math-emu/fnmsub.c
@@ -15,7 +15,6 @@ fnmsub(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -33,7 +32,7 @@ fnmsub(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
@@ -41,7 +40,7 @@ fnmsub(void *frD, void *frA, void *frB, void *frC)
B_s ^= 1;
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fnmsubs.c b/arch/powerpc/math-emu/fnmsubs.c
index f835dfeb0fd1..9021dacc03b8 100644
--- a/arch/powerpc/math-emu/fnmsubs.c
+++ b/arch/powerpc/math-emu/fnmsubs.c
@@ -16,7 +16,6 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC)
FP_DECL_D(C);
FP_DECL_D(T);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frA, frB, frC);
@@ -34,7 +33,7 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC)
if ((A_c == FP_CLS_INF && C_c == FP_CLS_ZERO) ||
(A_c == FP_CLS_ZERO && C_c == FP_CLS_INF))
- ret |= EFLAG_VXIMZ;
+ FP_SET_EXCEPTION(EFLAG_VXIMZ);
FP_MUL_D(T, A, C);
@@ -42,7 +41,7 @@ fnmsubs(void *frD, void *frA, void *frB, void *frC)
B_s ^= 1;
if (T_s != B_s && T_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, T, B);
diff --git a/arch/powerpc/math-emu/fsqrt.c b/arch/powerpc/math-emu/fsqrt.c
index 3e90072693a0..a55fc7d49983 100644
--- a/arch/powerpc/math-emu/fsqrt.c
+++ b/arch/powerpc/math-emu/fsqrt.c
@@ -12,7 +12,6 @@ fsqrt(void *frD, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frB);
@@ -25,9 +24,9 @@ fsqrt(void *frD, void *frB)
#endif
if (B_s && B_c != FP_CLS_ZERO)
- ret |= EFLAG_VXSQRT;
+ FP_SET_EXCEPTION(EFLAG_VXSQRT);
if (B_c == FP_CLS_NAN)
- ret |= EFLAG_VXSNAN;
+ FP_SET_EXCEPTION(EFLAG_VXSNAN);
FP_SQRT_D(R, B);
diff --git a/arch/powerpc/math-emu/fsqrts.c b/arch/powerpc/math-emu/fsqrts.c
index 2843be986e2e..31dccbfc39ff 100644
--- a/arch/powerpc/math-emu/fsqrts.c
+++ b/arch/powerpc/math-emu/fsqrts.c
@@ -13,7 +13,6 @@ fsqrts(void *frD, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p %p\n", __func__, frD, frB);
@@ -26,9 +25,9 @@ fsqrts(void *frD, void *frB)
#endif
if (B_s && B_c != FP_CLS_ZERO)
- ret |= EFLAG_VXSQRT;
+ FP_SET_EXCEPTION(EFLAG_VXSQRT);
if (B_c == FP_CLS_NAN)
- ret |= EFLAG_VXSNAN;
+ FP_SET_EXCEPTION(EFLAG_VXSNAN);
FP_SQRT_D(R, B);
diff --git a/arch/powerpc/math-emu/fsub.c b/arch/powerpc/math-emu/fsub.c
index 78b09446a0e1..02c5dff458ba 100644
--- a/arch/powerpc/math-emu/fsub.c
+++ b/arch/powerpc/math-emu/fsub.c
@@ -13,7 +13,6 @@ fsub(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -31,7 +30,7 @@ fsub(void *frD, void *frA, void *frB)
B_s ^= 1;
if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, A, B);
diff --git a/arch/powerpc/math-emu/fsubs.c b/arch/powerpc/math-emu/fsubs.c
index d3bf90863cf2..5d9b18c35e07 100644
--- a/arch/powerpc/math-emu/fsubs.c
+++ b/arch/powerpc/math-emu/fsubs.c
@@ -14,7 +14,6 @@ fsubs(void *frD, void *frA, void *frB)
FP_DECL_D(B);
FP_DECL_D(R);
FP_DECL_EX;
- int ret = 0;
#ifdef DEBUG
printk("%s: %p %p %p\n", __func__, frD, frA, frB);
@@ -32,7 +31,7 @@ fsubs(void *frD, void *frA, void *frB)
B_s ^= 1;
if (A_s != B_s && A_c == FP_CLS_INF && B_c == FP_CLS_INF)
- ret |= EFLAG_VXISI;
+ FP_SET_EXCEPTION(EFLAG_VXISI);
FP_ADD_D(R, A, B);
diff --git a/arch/powerpc/math-emu/math_efp.c b/arch/powerpc/math-emu/math_efp.c
new file mode 100644
index 000000000000..41f4ef30e480
--- /dev/null
+++ b/arch/powerpc/math-emu/math_efp.c
@@ -0,0 +1,720 @@
+/*
+ * arch/powerpc/math-emu/math_efp.c
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Ebony Zhu, <ebony.zhu@freescale.com>
+ * Yu Liu, <yu.liu@freescale.com>
+ *
+ * Derived from arch/alpha/math-emu/math.c
+ * arch/powerpc/math-emu/math.c
+ *
+ * Description:
+ * This file is the exception handler to make E500 SPE instructions
+ * fully comply with IEEE-754 floating point standard.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+
+#include <asm/uaccess.h>
+#include <asm/reg.h>
+
+#define FP_EX_BOOKE_E500_SPE
+#include <asm/sfp-machine.h>
+
+#include <math-emu/soft-fp.h>
+#include <math-emu/single.h>
+#include <math-emu/double.h>
+
+#define EFAPU 0x4
+
+#define VCT 0x4
+#define SPFP 0x6
+#define DPFP 0x7
+
+#define EFSADD 0x2c0
+#define EFSSUB 0x2c1
+#define EFSABS 0x2c4
+#define EFSNABS 0x2c5
+#define EFSNEG 0x2c6
+#define EFSMUL 0x2c8
+#define EFSDIV 0x2c9
+#define EFSCMPGT 0x2cc
+#define EFSCMPLT 0x2cd
+#define EFSCMPEQ 0x2ce
+#define EFSCFD 0x2cf
+#define EFSCFSI 0x2d1
+#define EFSCTUI 0x2d4
+#define EFSCTSI 0x2d5
+#define EFSCTUF 0x2d6
+#define EFSCTSF 0x2d7
+#define EFSCTUIZ 0x2d8
+#define EFSCTSIZ 0x2da
+
+#define EVFSADD 0x280
+#define EVFSSUB 0x281
+#define EVFSABS 0x284
+#define EVFSNABS 0x285
+#define EVFSNEG 0x286
+#define EVFSMUL 0x288
+#define EVFSDIV 0x289
+#define EVFSCMPGT 0x28c
+#define EVFSCMPLT 0x28d
+#define EVFSCMPEQ 0x28e
+#define EVFSCTUI 0x294
+#define EVFSCTSI 0x295
+#define EVFSCTUF 0x296
+#define EVFSCTSF 0x297
+#define EVFSCTUIZ 0x298
+#define EVFSCTSIZ 0x29a
+
+#define EFDADD 0x2e0
+#define EFDSUB 0x2e1
+#define EFDABS 0x2e4
+#define EFDNABS 0x2e5
+#define EFDNEG 0x2e6
+#define EFDMUL 0x2e8
+#define EFDDIV 0x2e9
+#define EFDCTUIDZ 0x2ea
+#define EFDCTSIDZ 0x2eb
+#define EFDCMPGT 0x2ec
+#define EFDCMPLT 0x2ed
+#define EFDCMPEQ 0x2ee
+#define EFDCFS 0x2ef
+#define EFDCTUI 0x2f4
+#define EFDCTSI 0x2f5
+#define EFDCTUF 0x2f6
+#define EFDCTSF 0x2f7
+#define EFDCTUIZ 0x2f8
+#define EFDCTSIZ 0x2fa
+
+#define AB 2
+#define XA 3
+#define XB 4
+#define XCR 5
+#define NOTYPE 0
+
+#define SIGN_BIT_S (1UL << 31)
+#define SIGN_BIT_D (1ULL << 63)
+#define FP_EX_MASK (FP_EX_INEXACT | FP_EX_INVALID | FP_EX_DIVZERO | \
+ FP_EX_UNDERFLOW | FP_EX_OVERFLOW)
+
+union dw_union {
+ u64 dp[1];
+ u32 wp[2];
+};
+
+static unsigned long insn_type(unsigned long speinsn)
+{
+ unsigned long ret = NOTYPE;
+
+ switch (speinsn & 0x7ff) {
+ case EFSABS: ret = XA; break;
+ case EFSADD: ret = AB; break;
+ case EFSCFD: ret = XB; break;
+ case EFSCMPEQ: ret = XCR; break;
+ case EFSCMPGT: ret = XCR; break;
+ case EFSCMPLT: ret = XCR; break;
+ case EFSCTSF: ret = XB; break;
+ case EFSCTSI: ret = XB; break;
+ case EFSCTSIZ: ret = XB; break;
+ case EFSCTUF: ret = XB; break;
+ case EFSCTUI: ret = XB; break;
+ case EFSCTUIZ: ret = XB; break;
+ case EFSDIV: ret = AB; break;
+ case EFSMUL: ret = AB; break;
+ case EFSNABS: ret = XA; break;
+ case EFSNEG: ret = XA; break;
+ case EFSSUB: ret = AB; break;
+ case EFSCFSI: ret = XB; break;
+
+ case EVFSABS: ret = XA; break;
+ case EVFSADD: ret = AB; break;
+ case EVFSCMPEQ: ret = XCR; break;
+ case EVFSCMPGT: ret = XCR; break;
+ case EVFSCMPLT: ret = XCR; break;
+ case EVFSCTSF: ret = XB; break;
+ case EVFSCTSI: ret = XB; break;
+ case EVFSCTSIZ: ret = XB; break;
+ case EVFSCTUF: ret = XB; break;
+ case EVFSCTUI: ret = XB; break;
+ case EVFSCTUIZ: ret = XB; break;
+ case EVFSDIV: ret = AB; break;
+ case EVFSMUL: ret = AB; break;
+ case EVFSNABS: ret = XA; break;
+ case EVFSNEG: ret = XA; break;
+ case EVFSSUB: ret = AB; break;
+
+ case EFDABS: ret = XA; break;
+ case EFDADD: ret = AB; break;
+ case EFDCFS: ret = XB; break;
+ case EFDCMPEQ: ret = XCR; break;
+ case EFDCMPGT: ret = XCR; break;
+ case EFDCMPLT: ret = XCR; break;
+ case EFDCTSF: ret = XB; break;
+ case EFDCTSI: ret = XB; break;
+ case EFDCTSIDZ: ret = XB; break;
+ case EFDCTSIZ: ret = XB; break;
+ case EFDCTUF: ret = XB; break;
+ case EFDCTUI: ret = XB; break;
+ case EFDCTUIDZ: ret = XB; break;
+ case EFDCTUIZ: ret = XB; break;
+ case EFDDIV: ret = AB; break;
+ case EFDMUL: ret = AB; break;
+ case EFDNABS: ret = XA; break;
+ case EFDNEG: ret = XA; break;
+ case EFDSUB: ret = AB; break;
+
+ default:
+ printk(KERN_ERR "\nOoops! SPE instruction no type found.");
+ printk(KERN_ERR "\ninst code: %08lx\n", speinsn);
+ }
+
+ return ret;
+}
+
+int do_spe_mathemu(struct pt_regs *regs)
+{
+ FP_DECL_EX;
+ int IR, cmp;
+
+ unsigned long type, func, fc, fa, fb, src, speinsn;
+ union dw_union vc, va, vb;
+
+ if (get_user(speinsn, (unsigned int __user *) regs->nip))
+ return -EFAULT;
+ if ((speinsn >> 26) != EFAPU)
+ return -EINVAL; /* not an spe instruction */
+
+ type = insn_type(speinsn);
+ if (type == NOTYPE)
+ return -ENOSYS;
+
+ func = speinsn & 0x7ff;
+ fc = (speinsn >> 21) & 0x1f;
+ fa = (speinsn >> 16) & 0x1f;
+ fb = (speinsn >> 11) & 0x1f;
+ src = (speinsn >> 5) & 0x7;
+
+ vc.wp[0] = current->thread.evr[fc];
+ vc.wp[1] = regs->gpr[fc];
+ va.wp[0] = current->thread.evr[fa];
+ va.wp[1] = regs->gpr[fa];
+ vb.wp[0] = current->thread.evr[fb];
+ vb.wp[1] = regs->gpr[fb];
+
+ __FPU_FPSCR = mfspr(SPRN_SPEFSCR);
+
+#ifdef DEBUG
+ printk("speinsn:%08lx spefscr:%08lx\n", speinsn, __FPU_FPSCR);
+ printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]);
+ printk("va: %08x %08x\n", va.wp[0], va.wp[1]);
+ printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]);
+#endif
+
+ switch (src) {
+ case SPFP: {
+ FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
+
+ switch (type) {
+ case AB:
+ case XCR:
+ FP_UNPACK_SP(SA, va.wp + 1);
+ case XB:
+ FP_UNPACK_SP(SB, vb.wp + 1);
+ break;
+ case XA:
+ FP_UNPACK_SP(SA, va.wp + 1);
+ break;
+ }
+
+#ifdef DEBUG
+ printk("SA: %ld %08lx %ld (%ld)\n", SA_s, SA_f, SA_e, SA_c);
+ printk("SB: %ld %08lx %ld (%ld)\n", SB_s, SB_f, SB_e, SB_c);
+#endif
+
+ switch (func) {
+ case EFSABS:
+ vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
+ goto update_regs;
+
+ case EFSNABS:
+ vc.wp[1] = va.wp[1] | SIGN_BIT_S;
+ goto update_regs;
+
+ case EFSNEG:
+ vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
+ goto update_regs;
+
+ case EFSADD:
+ FP_ADD_S(SR, SA, SB);
+ goto pack_s;
+
+ case EFSSUB:
+ FP_SUB_S(SR, SA, SB);
+ goto pack_s;
+
+ case EFSMUL:
+ FP_MUL_S(SR, SA, SB);
+ goto pack_s;
+
+ case EFSDIV:
+ FP_DIV_S(SR, SA, SB);
+ goto pack_s;
+
+ case EFSCMPEQ:
+ cmp = 0;
+ goto cmp_s;
+
+ case EFSCMPGT:
+ cmp = 1;
+ goto cmp_s;
+
+ case EFSCMPLT:
+ cmp = -1;
+ goto cmp_s;
+
+ case EFSCTSF:
+ case EFSCTUF:
+ if (!((vb.wp[1] >> 23) == 0xff && ((vb.wp[1] & 0x7fffff) > 0))) {
+ /* NaN */
+ if (((vb.wp[1] >> 23) & 0xff) == 0) {
+ /* denorm */
+ vc.wp[1] = 0x0;
+ } else if ((vb.wp[1] >> 31) == 0) {
+ /* positive normal */
+ vc.wp[1] = (func == EFSCTSF) ?
+ 0x7fffffff : 0xffffffff;
+ } else { /* negative normal */
+ vc.wp[1] = (func == EFSCTSF) ?
+ 0x80000000 : 0x0;
+ }
+ } else { /* rB is NaN */
+ vc.wp[1] = 0x0;
+ }
+ goto update_regs;
+
+ case EFSCFD: {
+ FP_DECL_D(DB);
+ FP_CLEAR_EXCEPTIONS;
+ FP_UNPACK_DP(DB, vb.dp);
+#ifdef DEBUG
+ printk("DB: %ld %08lx %08lx %ld (%ld)\n",
+ DB_s, DB_f1, DB_f0, DB_e, DB_c);
+#endif
+ FP_CONV(S, D, 1, 2, SR, DB);
+ goto pack_s;
+ }
+
+ case EFSCTSI:
+ case EFSCTSIZ:
+ case EFSCTUI:
+ case EFSCTUIZ:
+ if (func & 0x4) {
+ _FP_ROUND(1, SB);
+ } else {
+ _FP_ROUND_ZERO(1, SB);
+ }
+ FP_TO_INT_S(vc.wp[1], SB, 32, ((func & 0x3) != 0));
+ goto update_regs;
+
+ default:
+ goto illegal;
+ }
+ break;
+
+pack_s:
+#ifdef DEBUG
+ printk("SR: %ld %08lx %ld (%ld)\n", SR_s, SR_f, SR_e, SR_c);
+#endif
+ FP_PACK_SP(vc.wp + 1, SR);
+ goto update_regs;
+
+cmp_s:
+ FP_CMP_S(IR, SA, SB, 3);
+ if (IR == 3 && (FP_ISSIGNAN_S(SA) || FP_ISSIGNAN_S(SB)))
+ FP_SET_EXCEPTION(FP_EX_INVALID);
+ if (IR == cmp) {
+ IR = 0x4;
+ } else {
+ IR = 0;
+ }
+ goto update_ccr;
+ }
+
+ case DPFP: {
+ FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
+
+ switch (type) {
+ case AB:
+ case XCR:
+ FP_UNPACK_DP(DA, va.dp);
+ case XB:
+ FP_UNPACK_DP(DB, vb.dp);
+ break;
+ case XA:
+ FP_UNPACK_DP(DA, va.dp);
+ break;
+ }
+
+#ifdef DEBUG
+ printk("DA: %ld %08lx %08lx %ld (%ld)\n",
+ DA_s, DA_f1, DA_f0, DA_e, DA_c);
+ printk("DB: %ld %08lx %08lx %ld (%ld)\n",
+ DB_s, DB_f1, DB_f0, DB_e, DB_c);
+#endif
+
+ switch (func) {
+ case EFDABS:
+ vc.dp[0] = va.dp[0] & ~SIGN_BIT_D;
+ goto update_regs;
+
+ case EFDNABS:
+ vc.dp[0] = va.dp[0] | SIGN_BIT_D;
+ goto update_regs;
+
+ case EFDNEG:
+ vc.dp[0] = va.dp[0] ^ SIGN_BIT_D;
+ goto update_regs;
+
+ case EFDADD:
+ FP_ADD_D(DR, DA, DB);
+ goto pack_d;
+
+ case EFDSUB:
+ FP_SUB_D(DR, DA, DB);
+ goto pack_d;
+
+ case EFDMUL:
+ FP_MUL_D(DR, DA, DB);
+ goto pack_d;
+
+ case EFDDIV:
+ FP_DIV_D(DR, DA, DB);
+ goto pack_d;
+
+ case EFDCMPEQ:
+ cmp = 0;
+ goto cmp_d;
+
+ case EFDCMPGT:
+ cmp = 1;
+ goto cmp_d;
+
+ case EFDCMPLT:
+ cmp = -1;
+ goto cmp_d;
+
+ case EFDCTSF:
+ case EFDCTUF:
+ if (!((vb.wp[0] >> 20) == 0x7ff &&
+ ((vb.wp[0] & 0xfffff) > 0 || (vb.wp[1] > 0)))) {
+ /* not a NaN */
+ if (((vb.wp[0] >> 20) & 0x7ff) == 0) {
+ /* denorm */
+ vc.wp[1] = 0x0;
+ } else if ((vb.wp[0] >> 31) == 0) {
+ /* positive normal */
+ vc.wp[1] = (func == EFDCTSF) ?
+ 0x7fffffff : 0xffffffff;
+ } else { /* negative normal */
+ vc.wp[1] = (func == EFDCTSF) ?
+ 0x80000000 : 0x0;
+ }
+ } else { /* NaN */
+ vc.wp[1] = 0x0;
+ }
+ goto update_regs;
+
+ case EFDCFS: {
+ FP_DECL_S(SB);
+ FP_CLEAR_EXCEPTIONS;
+ FP_UNPACK_SP(SB, vb.wp + 1);
+#ifdef DEBUG
+ printk("SB: %ld %08lx %ld (%ld)\n",
+ SB_s, SB_f, SB_e, SB_c);
+#endif
+ FP_CONV(D, S, 2, 1, DR, SB);
+ goto pack_d;
+ }
+
+ case EFDCTUIDZ:
+ case EFDCTSIDZ:
+ _FP_ROUND_ZERO(2, DB);
+ FP_TO_INT_D(vc.dp[0], DB, 64, ((func & 0x1) == 0));
+ goto update_regs;
+
+ case EFDCTUI:
+ case EFDCTSI:
+ case EFDCTUIZ:
+ case EFDCTSIZ:
+ if (func & 0x4) {
+ _FP_ROUND(2, DB);
+ } else {
+ _FP_ROUND_ZERO(2, DB);
+ }
+ FP_TO_INT_D(vc.wp[1], DB, 32, ((func & 0x3) != 0));
+ goto update_regs;
+
+ default:
+ goto illegal;
+ }
+ break;
+
+pack_d:
+#ifdef DEBUG
+ printk("DR: %ld %08lx %08lx %ld (%ld)\n",
+ DR_s, DR_f1, DR_f0, DR_e, DR_c);
+#endif
+ FP_PACK_DP(vc.dp, DR);
+ goto update_regs;
+
+cmp_d:
+ FP_CMP_D(IR, DA, DB, 3);
+ if (IR == 3 && (FP_ISSIGNAN_D(DA) || FP_ISSIGNAN_D(DB)))
+ FP_SET_EXCEPTION(FP_EX_INVALID);
+ if (IR == cmp) {
+ IR = 0x4;
+ } else {
+ IR = 0;
+ }
+ goto update_ccr;
+
+ }
+
+ case VCT: {
+ FP_DECL_S(SA0); FP_DECL_S(SB0); FP_DECL_S(SR0);
+ FP_DECL_S(SA1); FP_DECL_S(SB1); FP_DECL_S(SR1);
+ int IR0, IR1;
+
+ switch (type) {
+ case AB:
+ case XCR:
+ FP_UNPACK_SP(SA0, va.wp);
+ FP_UNPACK_SP(SA1, va.wp + 1);
+ case XB:
+ FP_UNPACK_SP(SB0, vb.wp);
+ FP_UNPACK_SP(SB1, vb.wp + 1);
+ break;
+ case XA:
+ FP_UNPACK_SP(SA0, va.wp);
+ FP_UNPACK_SP(SA1, va.wp + 1);
+ break;
+ }
+
+#ifdef DEBUG
+ printk("SA0: %ld %08lx %ld (%ld)\n", SA0_s, SA0_f, SA0_e, SA0_c);
+ printk("SA1: %ld %08lx %ld (%ld)\n", SA1_s, SA1_f, SA1_e, SA1_c);
+ printk("SB0: %ld %08lx %ld (%ld)\n", SB0_s, SB0_f, SB0_e, SB0_c);
+ printk("SB1: %ld %08lx %ld (%ld)\n", SB1_s, SB1_f, SB1_e, SB1_c);
+#endif
+
+ switch (func) {
+ case EVFSABS:
+ vc.wp[0] = va.wp[0] & ~SIGN_BIT_S;
+ vc.wp[1] = va.wp[1] & ~SIGN_BIT_S;
+ goto update_regs;
+
+ case EVFSNABS:
+ vc.wp[0] = va.wp[0] | SIGN_BIT_S;
+ vc.wp[1] = va.wp[1] | SIGN_BIT_S;
+ goto update_regs;
+
+ case EVFSNEG:
+ vc.wp[0] = va.wp[0] ^ SIGN_BIT_S;
+ vc.wp[1] = va.wp[1] ^ SIGN_BIT_S;
+ goto update_regs;
+
+ case EVFSADD:
+ FP_ADD_S(SR0, SA0, SB0);
+ FP_ADD_S(SR1, SA1, SB1);
+ goto pack_vs;
+
+ case EVFSSUB:
+ FP_SUB_S(SR0, SA0, SB0);
+ FP_SUB_S(SR1, SA1, SB1);
+ goto pack_vs;
+
+ case EVFSMUL:
+ FP_MUL_S(SR0, SA0, SB0);
+ FP_MUL_S(SR1, SA1, SB1);
+ goto pack_vs;
+
+ case EVFSDIV:
+ FP_DIV_S(SR0, SA0, SB0);
+ FP_DIV_S(SR1, SA1, SB1);
+ goto pack_vs;
+
+ case EVFSCMPEQ:
+ cmp = 0;
+ goto cmp_vs;
+
+ case EVFSCMPGT:
+ cmp = 1;
+ goto cmp_vs;
+
+ case EVFSCMPLT:
+ cmp = -1;
+ goto cmp_vs;
+
+ case EVFSCTSF:
+ __asm__ __volatile__ ("mtspr 512, %4\n"
+ "efsctsf %0, %2\n"
+ "efsctsf %1, %3\n"
+ : "=r" (vc.wp[0]), "=r" (vc.wp[1])
+ : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
+ goto update_regs;
+
+ case EVFSCTUF:
+ __asm__ __volatile__ ("mtspr 512, %4\n"
+ "efsctuf %0, %2\n"
+ "efsctuf %1, %3\n"
+ : "=r" (vc.wp[0]), "=r" (vc.wp[1])
+ : "r" (vb.wp[0]), "r" (vb.wp[1]), "r" (0));
+ goto update_regs;
+
+ case EVFSCTUI:
+ case EVFSCTSI:
+ case EVFSCTUIZ:
+ case EVFSCTSIZ:
+ if (func & 0x4) {
+ _FP_ROUND(1, SB0);
+ _FP_ROUND(1, SB1);
+ } else {
+ _FP_ROUND_ZERO(1, SB0);
+ _FP_ROUND_ZERO(1, SB1);
+ }
+ FP_TO_INT_S(vc.wp[0], SB0, 32, ((func & 0x3) != 0));
+ FP_TO_INT_S(vc.wp[1], SB1, 32, ((func & 0x3) != 0));
+ goto update_regs;
+
+ default:
+ goto illegal;
+ }
+ break;
+
+pack_vs:
+#ifdef DEBUG
+ printk("SR0: %ld %08lx %ld (%ld)\n", SR0_s, SR0_f, SR0_e, SR0_c);
+ printk("SR1: %ld %08lx %ld (%ld)\n", SR1_s, SR1_f, SR1_e, SR1_c);
+#endif
+ FP_PACK_SP(vc.wp, SR0);
+ FP_PACK_SP(vc.wp + 1, SR1);
+ goto update_regs;
+
+cmp_vs:
+ {
+ int ch, cl;
+
+ FP_CMP_S(IR0, SA0, SB0, 3);
+ FP_CMP_S(IR1, SA1, SB1, 3);
+ if (IR0 == 3 && (FP_ISSIGNAN_S(SA0) || FP_ISSIGNAN_S(SB0)))
+ FP_SET_EXCEPTION(FP_EX_INVALID);
+ if (IR1 == 3 && (FP_ISSIGNAN_S(SA1) || FP_ISSIGNAN_S(SB1)))
+ FP_SET_EXCEPTION(FP_EX_INVALID);
+ ch = (IR0 == cmp) ? 1 : 0;
+ cl = (IR1 == cmp) ? 1 : 0;
+ IR = (ch << 3) | (cl << 2) | ((ch | cl) << 1) |
+ ((ch & cl) << 0);
+ goto update_ccr;
+ }
+ }
+ default:
+ return -EINVAL;
+ }
+
+update_ccr:
+ regs->ccr &= ~(15 << ((7 - ((speinsn >> 23) & 0x7)) << 2));
+ regs->ccr |= (IR << ((7 - ((speinsn >> 23) & 0x7)) << 2));
+
+update_regs:
+ __FPU_FPSCR &= ~FP_EX_MASK;
+ __FPU_FPSCR |= (FP_CUR_EXCEPTIONS & FP_EX_MASK);
+ mtspr(SPRN_SPEFSCR, __FPU_FPSCR);
+
+ current->thread.evr[fc] = vc.wp[0];
+ regs->gpr[fc] = vc.wp[1];
+
+#ifdef DEBUG
+ printk("ccr = %08lx\n", regs->ccr);
+ printk("cur exceptions = %08x spefscr = %08lx\n",
+ FP_CUR_EXCEPTIONS, __FPU_FPSCR);
+ printk("vc: %08x %08x\n", vc.wp[0], vc.wp[1]);
+ printk("va: %08x %08x\n", va.wp[0], va.wp[1]);
+ printk("vb: %08x %08x\n", vb.wp[0], vb.wp[1]);
+#endif
+
+ return 0;
+
+illegal:
+ printk(KERN_ERR "\nOoops! IEEE-754 compliance handler encountered un-supported instruction.\ninst code: %08lx\n", speinsn);
+ return -ENOSYS;
+}
+
+int speround_handler(struct pt_regs *regs)
+{
+ union dw_union fgpr;
+ int s_lo, s_hi;
+ unsigned long speinsn, type, fc;
+
+ if (get_user(speinsn, (unsigned int __user *) regs->nip))
+ return -EFAULT;
+ if ((speinsn >> 26) != 4)
+ return -EINVAL; /* not an spe instruction */
+
+ type = insn_type(speinsn & 0x7ff);
+ if (type == XCR) return -ENOSYS;
+
+ fc = (speinsn >> 21) & 0x1f;
+ s_lo = regs->gpr[fc] & SIGN_BIT_S;
+ s_hi = current->thread.evr[fc] & SIGN_BIT_S;
+ fgpr.wp[0] = current->thread.evr[fc];
+ fgpr.wp[1] = regs->gpr[fc];
+
+ __FPU_FPSCR = mfspr(SPRN_SPEFSCR);
+
+ switch ((speinsn >> 5) & 0x7) {
+ /* Since SPE instructions on E500 core can handle round to nearest
+ * and round toward zero with IEEE-754 complied, we just need
+ * to handle round toward +Inf and round toward -Inf by software.
+ */
+ case SPFP:
+ if ((FP_ROUNDMODE) == FP_RND_PINF) {
+ if (!s_lo) fgpr.wp[1]++; /* Z > 0, choose Z1 */
+ } else { /* round to -Inf */
+ if (s_lo) fgpr.wp[1]++; /* Z < 0, choose Z2 */
+ }
+ break;
+
+ case DPFP:
+ if (FP_ROUNDMODE == FP_RND_PINF) {
+ if (!s_hi) fgpr.dp[0]++; /* Z > 0, choose Z1 */
+ } else { /* round to -Inf */
+ if (s_hi) fgpr.dp[0]++; /* Z < 0, choose Z2 */
+ }
+ break;
+
+ case VCT:
+ if (FP_ROUNDMODE == FP_RND_PINF) {
+ if (!s_lo) fgpr.wp[1]++; /* Z_low > 0, choose Z1 */
+ if (!s_hi) fgpr.wp[0]++; /* Z_high word > 0, choose Z1 */
+ } else { /* round to -Inf */
+ if (s_lo) fgpr.wp[1]++; /* Z_low < 0, choose Z2 */
+ if (s_hi) fgpr.wp[0]++; /* Z_high < 0, choose Z2 */
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ current->thread.evr[fc] = fgpr.wp[0];
+ regs->gpr[fc] = fgpr.wp[1];
+
+ return 0;
+}
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index e7392b45a5ef..953cc4a1cde5 100644
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -6,17 +6,19 @@ ifeq ($(CONFIG_PPC64),y)
EXTRA_CFLAGS += -mno-minimal-toc
endif
-obj-y := fault.o mem.o \
+obj-y := fault.o mem.o pgtable.o \
init_$(CONFIG_WORD_SIZE).o \
- pgtable_$(CONFIG_WORD_SIZE).o \
- mmu_context_$(CONFIG_WORD_SIZE).o
+ pgtable_$(CONFIG_WORD_SIZE).o
+obj-$(CONFIG_PPC_MMU_NOHASH) += mmu_context_nohash.o tlb_nohash.o \
+ tlb_nohash_low.o
hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o
obj-$(CONFIG_PPC64) += hash_utils_64.o \
slb_low.o slb.o stab.o \
gup.o mmap.o $(hash-y)
obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o
obj-$(CONFIG_PPC_STD_MMU) += hash_low_$(CONFIG_WORD_SIZE).o \
- tlb_$(CONFIG_WORD_SIZE).o
+ tlb_hash$(CONFIG_WORD_SIZE).o \
+ mmu_context_hash$(CONFIG_WORD_SIZE).o
obj-$(CONFIG_40x) += 40x_mmu.o
obj-$(CONFIG_44x) += 44x_mmu.o
obj-$(CONFIG_FSL_BOOKE) += fsl_booke_mmu.o
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c
index 565b7a237c84..91c7b8636b8a 100644
--- a/arch/powerpc/mm/fault.c
+++ b/arch/powerpc/mm/fault.c
@@ -30,6 +30,7 @@
#include <linux/kprobes.h>
#include <linux/kdebug.h>
+#include <asm/firmware.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
@@ -283,7 +284,7 @@ good_area:
}
pte_update(ptep, 0, _PAGE_HWEXEC |
_PAGE_ACCESSED);
- _tlbie(address, mm->context.id);
+ local_flush_tlb_page(vma, address);
pte_unmap_unlock(ptep, ptl);
up_read(&mm->mmap_sem);
return 0;
@@ -318,9 +319,16 @@ good_area:
goto do_sigbus;
BUG();
}
- if (ret & VM_FAULT_MAJOR)
+ if (ret & VM_FAULT_MAJOR) {
current->maj_flt++;
- else
+#ifdef CONFIG_PPC_SMLPAR
+ if (firmware_has_feature(FW_FEATURE_CMO)) {
+ preempt_disable();
+ get_lppaca()->page_ins += (1 << PAGE_FACTOR);
+ preempt_enable();
+ }
+#endif
+ } else
current->min_flt++;
up_read(&mm->mmap_sem);
return 0;
@@ -339,7 +347,7 @@ bad_area_nosemaphore:
&& printk_ratelimit())
printk(KERN_CRIT "kernel tried to execute NX-protected"
" page (%lx) - exploit attempt? (uid: %d)\n",
- address, current->uid);
+ address, current_uid());
return SIGSEGV;
diff --git a/arch/powerpc/mm/hash_low_32.S b/arch/powerpc/mm/hash_low_32.S
index 7bffb70b9fe2..67850ec9feb3 100644
--- a/arch/powerpc/mm/hash_low_32.S
+++ b/arch/powerpc/mm/hash_low_32.S
@@ -36,36 +36,6 @@ mmu_hash_lock:
#endif /* CONFIG_SMP */
/*
- * Sync CPUs with hash_page taking & releasing the hash
- * table lock
- */
-#ifdef CONFIG_SMP
- .text
-_GLOBAL(hash_page_sync)
- mfmsr r10
- rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */
- mtmsr r0
- lis r8,mmu_hash_lock@h
- ori r8,r8,mmu_hash_lock@l
- lis r0,0x0fff
- b 10f
-11: lwz r6,0(r8)
- cmpwi 0,r6,0
- bne 11b
-10: lwarx r6,0,r8
- cmpwi 0,r6,0
- bne- 11b
- stwcx. r0,0,r8
- bne- 10b
- isync
- eieio
- li r0,0
- stw r0,0(r8)
- mtmsr r10
- blr
-#endif /* CONFIG_SMP */
-
-/*
* Load a PTE into the hash table, if possible.
* The address is in r4, and r3 contains an access flag:
* _PAGE_RW (0x400) if a write.
@@ -353,8 +323,8 @@ _GLOBAL(create_hpte)
ori r8,r8,0xe14 /* clear out reserved bits and M */
andc r8,r5,r8 /* PP = user? (rw&dirty? 2: 3): 0 */
BEGIN_FTR_SECTION
- ori r8,r8,_PAGE_COHERENT /* set M (coherence required) */
-END_FTR_SECTION_IFSET(CPU_FTR_NEED_COHERENT)
+ rlwinm r8,r8,0,~_PAGE_COHERENT /* clear M (coherence not required) */
+END_FTR_SECTION_IFCLR(CPU_FTR_NEED_COHERENT)
#ifdef CONFIG_PTE_64BIT
/* Put the XPN bits into the PTE */
rlwimi r8,r10,8,20,22
@@ -663,3 +633,80 @@ _GLOBAL(flush_hash_patch_B)
SYNC_601
isync
blr
+
+/*
+ * Flush an entry from the TLB
+ */
+_GLOBAL(_tlbie)
+#ifdef CONFIG_SMP
+ rlwinm r8,r1,0,0,(31-THREAD_SHIFT)
+ lwz r8,TI_CPU(r8)
+ oris r8,r8,11
+ mfmsr r10
+ SYNC
+ rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */
+ rlwinm r0,r0,0,28,26 /* clear DR */
+ mtmsr r0
+ SYNC_601
+ isync
+ lis r9,mmu_hash_lock@h
+ ori r9,r9,mmu_hash_lock@l
+ tophys(r9,r9)
+10: lwarx r7,0,r9
+ cmpwi 0,r7,0
+ bne- 10b
+ stwcx. r8,0,r9
+ bne- 10b
+ eieio
+ tlbie r3
+ sync
+ TLBSYNC
+ li r0,0
+ stw r0,0(r9) /* clear mmu_hash_lock */
+ mtmsr r10
+ SYNC_601
+ isync
+#else /* CONFIG_SMP */
+ tlbie r3
+ sync
+#endif /* CONFIG_SMP */
+ blr
+
+/*
+ * Flush the entire TLB. 603/603e only
+ */
+_GLOBAL(_tlbia)
+#if defined(CONFIG_SMP)
+ rlwinm r8,r1,0,0,(31-THREAD_SHIFT)
+ lwz r8,TI_CPU(r8)
+ oris r8,r8,10
+ mfmsr r10
+ SYNC
+ rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */
+ rlwinm r0,r0,0,28,26 /* clear DR */
+ mtmsr r0
+ SYNC_601
+ isync
+ lis r9,mmu_hash_lock@h
+ ori r9,r9,mmu_hash_lock@l
+ tophys(r9,r9)
+10: lwarx r7,0,r9
+ cmpwi 0,r7,0
+ bne- 10b
+ stwcx. r8,0,r9
+ bne- 10b
+ sync
+ tlbia
+ sync
+ TLBSYNC
+ li r0,0
+ stw r0,0(r9) /* clear mmu_hash_lock */
+ mtmsr r10
+ SYNC_601
+ isync
+#else /* CONFIG_SMP */
+ sync
+ tlbia
+ sync
+#endif /* CONFIG_SMP */
+ blr
diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c
index f0c3b88d50fa..201c7a5486cb 100644
--- a/arch/powerpc/mm/hugetlbpage.c
+++ b/arch/powerpc/mm/hugetlbpage.c
@@ -53,8 +53,7 @@ unsigned int mmu_huge_psizes[MMU_PAGE_COUNT] = { }; /* initialize all to 0 */
/* Subtract one from array size because we don't need a cache for 4K since
* is not a huge page size */
-#define huge_pgtable_cache(psize) (pgtable_cache[HUGEPTE_CACHE_NUM \
- + psize-1])
+#define HUGE_PGTABLE_INDEX(psize) (HUGEPTE_CACHE_NUM + psize - 1)
#define HUGEPTE_CACHE_NAME(psize) (huge_pgtable_cache_name[psize])
static const char *huge_pgtable_cache_name[MMU_PAGE_COUNT] = {
@@ -113,7 +112,7 @@ static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr,
static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp,
unsigned long address, unsigned int psize)
{
- pte_t *new = kmem_cache_zalloc(huge_pgtable_cache(psize),
+ pte_t *new = kmem_cache_zalloc(pgtable_cache[HUGE_PGTABLE_INDEX(psize)],
GFP_KERNEL|__GFP_REPEAT);
if (! new)
@@ -121,7 +120,7 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp,
spin_lock(&mm->page_table_lock);
if (!hugepd_none(*hpdp))
- kmem_cache_free(huge_pgtable_cache(psize), new);
+ kmem_cache_free(pgtable_cache[HUGE_PGTABLE_INDEX(psize)], new);
else
hpdp->pd = (unsigned long)new | HUGEPD_OK;
spin_unlock(&mm->page_table_lock);
@@ -763,13 +762,14 @@ static int __init hugetlbpage_init(void)
for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
if (mmu_huge_psizes[psize]) {
- huge_pgtable_cache(psize) = kmem_cache_create(
- HUGEPTE_CACHE_NAME(psize),
- HUGEPTE_TABLE_SIZE(psize),
- HUGEPTE_TABLE_SIZE(psize),
- 0,
- NULL);
- if (!huge_pgtable_cache(psize))
+ pgtable_cache[HUGE_PGTABLE_INDEX(psize)] =
+ kmem_cache_create(
+ HUGEPTE_CACHE_NAME(psize),
+ HUGEPTE_TABLE_SIZE(psize),
+ HUGEPTE_TABLE_SIZE(psize),
+ 0,
+ NULL);
+ if (!pgtable_cache[HUGE_PGTABLE_INDEX(psize)])
panic("hugetlbpage_init(): could not create %s"\
"\n", HUGEPTE_CACHE_NAME(psize));
}
diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
index 388ceda632f3..666a5e8a5be1 100644
--- a/arch/powerpc/mm/init_32.c
+++ b/arch/powerpc/mm/init_32.c
@@ -35,7 +35,6 @@
#include <asm/pgalloc.h>
#include <asm/prom.h>
#include <asm/io.h>
-#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
#include <asm/smp.h>
@@ -49,7 +48,7 @@
#if defined(CONFIG_KERNEL_START_BOOL) || defined(CONFIG_LOWMEM_SIZE_BOOL)
/* The ammount of lowmem must be within 0xF0000000 - KERNELBASE. */
-#if (CONFIG_LOWMEM_SIZE > (0xF0000000 - KERNELBASE))
+#if (CONFIG_LOWMEM_SIZE > (0xF0000000 - PAGE_OFFSET))
#error "You must adjust CONFIG_LOWMEM_SIZE or CONFIG_START_KERNEL"
#endif
#endif
@@ -180,9 +179,6 @@ void __init MMU_init(void)
if (ppc_md.progress)
ppc_md.progress("MMU:setio", 0x302);
- /* Initialize the context management stuff */
- mmu_context_init();
-
if (ppc_md.progress)
ppc_md.progress("MMU:exit", 0x211);
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index b9e1a1da6e52..53b06ebb3f2f 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -102,8 +102,8 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
return ppc_md.phys_mem_access_prot(file, pfn, size, vma_prot);
if (!page_is_ram(pfn))
- vma_prot = __pgprot(pgprot_val(vma_prot)
- | _PAGE_GUARDED | _PAGE_NO_CACHE);
+ vma_prot = pgprot_noncached(vma_prot);
+
return vma_prot;
}
EXPORT_SYMBOL(phys_mem_access_prot);
@@ -488,7 +488,7 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address,
* we invalidate the TLB here, thus avoiding dcbst
* misbehaviour.
*/
- _tlbie(address, 0 /* 8xx doesn't care about PID */);
+ _tlbil_va(address, 0 /* 8xx doesn't care about PID */);
#endif
/* The _PAGE_USER test should really be _PAGE_EXEC, but
* older glibc versions execute some code from no-exec
diff --git a/arch/powerpc/mm/mmu_context_32.c b/arch/powerpc/mm/mmu_context_32.c
deleted file mode 100644
index cc32ba41d900..000000000000
--- a/arch/powerpc/mm/mmu_context_32.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This file contains the routines for handling the MMU on those
- * PowerPC implementations where the MMU substantially follows the
- * architecture specification. This includes the 6xx, 7xx, 7xxx,
- * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
- * -- paulus
- *
- * Derived from arch/ppc/mm/init.c:
- * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
- * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
- * and Cort Dougan (PReP) (cort@cs.nmt.edu)
- * Copyright (C) 1996 Paul Mackerras
- *
- * Derived from "arch/i386/mm/init.c"
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- *
- * This program is free software; you can redistribute 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/mm.h>
-#include <linux/init.h>
-
-#include <asm/mmu_context.h>
-#include <asm/tlbflush.h>
-
-unsigned long next_mmu_context;
-unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1];
-#ifdef FEW_CONTEXTS
-atomic_t nr_free_contexts;
-struct mm_struct *context_mm[LAST_CONTEXT+1];
-void steal_context(void);
-#endif /* FEW_CONTEXTS */
-
-/*
- * Initialize the context management stuff.
- */
-void __init
-mmu_context_init(void)
-{
- /*
- * Some processors have too few contexts to reserve one for
- * init_mm, and require using context 0 for a normal task.
- * Other processors reserve the use of context zero for the kernel.
- * This code assumes FIRST_CONTEXT < 32.
- */
- context_map[0] = (1 << FIRST_CONTEXT) - 1;
- next_mmu_context = FIRST_CONTEXT;
-#ifdef FEW_CONTEXTS
- atomic_set(&nr_free_contexts, LAST_CONTEXT - FIRST_CONTEXT + 1);
-#endif /* FEW_CONTEXTS */
-}
-
-#ifdef FEW_CONTEXTS
-/*
- * Steal a context from a task that has one at the moment.
- * This is only used on 8xx and 4xx and we presently assume that
- * they don't do SMP. If they do then this will have to check
- * whether the MM we steal is in use.
- * We also assume that this is only used on systems that don't
- * use an MMU hash table - this is true for 8xx and 4xx.
- * This isn't an LRU system, it just frees up each context in
- * turn (sort-of pseudo-random replacement :). This would be the
- * place to implement an LRU scheme if anyone was motivated to do it.
- * -- paulus
- */
-void
-steal_context(void)
-{
- struct mm_struct *mm;
-
- /* free up context `next_mmu_context' */
- /* if we shouldn't free context 0, don't... */
- if (next_mmu_context < FIRST_CONTEXT)
- next_mmu_context = FIRST_CONTEXT;
- mm = context_mm[next_mmu_context];
- flush_tlb_mm(mm);
- destroy_context(mm);
-}
-#endif /* FEW_CONTEXTS */
diff --git a/arch/powerpc/mm/mmu_context_64.c b/arch/powerpc/mm/mmu_context_64.c
deleted file mode 100644
index 1db38ba1f544..000000000000
--- a/arch/powerpc/mm/mmu_context_64.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * MMU context allocation for 64-bit kernels.
- *
- * Copyright (C) 2004 Anton Blanchard, IBM Corp. <anton@samba.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/spinlock.h>
-#include <linux/idr.h>
-
-#include <asm/mmu_context.h>
-
-static DEFINE_SPINLOCK(mmu_context_lock);
-static DEFINE_IDR(mmu_context_idr);
-
-int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
-{
- int index;
- int err;
-
-again:
- if (!idr_pre_get(&mmu_context_idr, GFP_KERNEL))
- return -ENOMEM;
-
- spin_lock(&mmu_context_lock);
- err = idr_get_new_above(&mmu_context_idr, NULL, 1, &index);
- spin_unlock(&mmu_context_lock);
-
- if (err == -EAGAIN)
- goto again;
- else if (err)
- return err;
-
- if (index > MAX_CONTEXT) {
- spin_lock(&mmu_context_lock);
- idr_remove(&mmu_context_idr, index);
- spin_unlock(&mmu_context_lock);
- return -ENOMEM;
- }
-
- /* The old code would re-promote on fork, we don't do that
- * when using slices as it could cause problem promoting slices
- * that have been forced down to 4K
- */
- if (slice_mm_new_context(mm))
- slice_set_user_psize(mm, mmu_virtual_psize);
- mm->context.id = index;
-
- return 0;
-}
-
-void destroy_context(struct mm_struct *mm)
-{
- spin_lock(&mmu_context_lock);
- idr_remove(&mmu_context_idr, mm->context.id);
- spin_unlock(&mmu_context_lock);
-
- mm->context.id = NO_CONTEXT;
-}
diff --git a/arch/powerpc/mm/mmu_context_hash32.c b/arch/powerpc/mm/mmu_context_hash32.c
new file mode 100644
index 000000000000..0dfba2bf7f31
--- /dev/null
+++ b/arch/powerpc/mm/mmu_context_hash32.c
@@ -0,0 +1,103 @@
+/*
+ * This file contains the routines for handling the MMU on those
+ * PowerPC implementations where the MMU substantially follows the
+ * architecture specification. This includes the 6xx, 7xx, 7xxx,
+ * 8260, and POWER3 implementations but excludes the 8xx and 4xx.
+ * -- paulus
+ *
+ * Derived from arch/ppc/mm/init.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * This program is free software; you can redistribute 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/mm.h>
+#include <linux/init.h>
+
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+
+/*
+ * On 32-bit PowerPC 6xx/7xx/7xxx CPUs, we use a set of 16 VSIDs
+ * (virtual segment identifiers) for each context. Although the
+ * hardware supports 24-bit VSIDs, and thus >1 million contexts,
+ * we only use 32,768 of them. That is ample, since there can be
+ * at most around 30,000 tasks in the system anyway, and it means
+ * that we can use a bitmap to indicate which contexts are in use.
+ * Using a bitmap means that we entirely avoid all of the problems
+ * that we used to have when the context number overflowed,
+ * particularly on SMP systems.
+ * -- paulus.
+ */
+#define NO_CONTEXT ((unsigned long) -1)
+#define LAST_CONTEXT 32767
+#define FIRST_CONTEXT 1
+
+/*
+ * This function defines the mapping from contexts to VSIDs (virtual
+ * segment IDs). We use a skew on both the context and the high 4 bits
+ * of the 32-bit virtual address (the "effective segment ID") in order
+ * to spread out the entries in the MMU hash table. Note, if this
+ * function is changed then arch/ppc/mm/hashtable.S will have to be
+ * changed to correspond.
+ *
+ *
+ * CTX_TO_VSID(ctx, va) (((ctx) * (897 * 16) + ((va) >> 28) * 0x111) \
+ * & 0xffffff)
+ */
+
+static unsigned long next_mmu_context;
+static unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1];
+
+
+/*
+ * Set up the context for a new address space.
+ */
+int init_new_context(struct task_struct *t, struct mm_struct *mm)
+{
+ unsigned long ctx = next_mmu_context;
+
+ while (test_and_set_bit(ctx, context_map)) {
+ ctx = find_next_zero_bit(context_map, LAST_CONTEXT+1, ctx);
+ if (ctx > LAST_CONTEXT)
+ ctx = 0;
+ }
+ next_mmu_context = (ctx + 1) & LAST_CONTEXT;
+ mm->context.id = ctx;
+
+ return 0;
+}
+
+/*
+ * We're finished using the context for an address space.
+ */
+void destroy_context(struct mm_struct *mm)
+{
+ preempt_disable();
+ if (mm->context.id != NO_CONTEXT) {
+ clear_bit(mm->context.id, context_map);
+ mm->context.id = NO_CONTEXT;
+ }
+ preempt_enable();
+}
+
+/*
+ * Initialize the context management stuff.
+ */
+void __init mmu_context_init(void)
+{
+ /* Reserve context 0 for kernel use */
+ context_map[0] = (1 << FIRST_CONTEXT) - 1;
+ next_mmu_context = FIRST_CONTEXT;
+}
diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c
new file mode 100644
index 000000000000..dbeb86ac90cd
--- /dev/null
+++ b/arch/powerpc/mm/mmu_context_hash64.c
@@ -0,0 +1,78 @@
+/*
+ * MMU context allocation for 64-bit kernels.
+ *
+ * Copyright (C) 2004 Anton Blanchard, IBM Corp. <anton@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+
+#include <asm/mmu_context.h>
+
+static DEFINE_SPINLOCK(mmu_context_lock);
+static DEFINE_IDR(mmu_context_idr);
+
+/*
+ * The proto-VSID space has 2^35 - 1 segments available for user mappings.
+ * Each segment contains 2^28 bytes. Each context maps 2^44 bytes,
+ * so we can support 2^19-1 contexts (19 == 35 + 28 - 44).
+ */
+#define NO_CONTEXT 0
+#define MAX_CONTEXT ((1UL << 19) - 1)
+
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+ int index;
+ int err;
+
+again:
+ if (!idr_pre_get(&mmu_context_idr, GFP_KERNEL))
+ return -ENOMEM;
+
+ spin_lock(&mmu_context_lock);
+ err = idr_get_new_above(&mmu_context_idr, NULL, 1, &index);
+ spin_unlock(&mmu_context_lock);
+
+ if (err == -EAGAIN)
+ goto again;
+ else if (err)
+ return err;
+
+ if (index > MAX_CONTEXT) {
+ spin_lock(&mmu_context_lock);
+ idr_remove(&mmu_context_idr, index);
+ spin_unlock(&mmu_context_lock);
+ return -ENOMEM;
+ }
+
+ /* The old code would re-promote on fork, we don't do that
+ * when using slices as it could cause problem promoting slices
+ * that have been forced down to 4K
+ */
+ if (slice_mm_new_context(mm))
+ slice_set_user_psize(mm, mmu_virtual_psize);
+ mm->context.id = index;
+
+ return 0;
+}
+
+void destroy_context(struct mm_struct *mm)
+{
+ spin_lock(&mmu_context_lock);
+ idr_remove(&mmu_context_idr, mm->context.id);
+ spin_unlock(&mmu_context_lock);
+
+ mm->context.id = NO_CONTEXT;
+}
diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c
new file mode 100644
index 000000000000..52a0cfc38b64
--- /dev/null
+++ b/arch/powerpc/mm/mmu_context_nohash.c
@@ -0,0 +1,397 @@
+/*
+ * This file contains the routines for handling the MMU on those
+ * PowerPC implementations where the MMU is not using the hash
+ * table, such as 8xx, 4xx, BookE's etc...
+ *
+ * Copyright 2008 Ben Herrenschmidt <benh@kernel.crashing.org>
+ * IBM Corp.
+ *
+ * Derived from previous arch/powerpc/mm/mmu_context.c
+ * and arch/powerpc/include/asm/mmu_context.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * TODO:
+ *
+ * - The global context lock will not scale very well
+ * - The maps should be dynamically allocated to allow for processors
+ * that support more PID bits at runtime
+ * - Implement flush_tlb_mm() by making the context stale and picking
+ * a new one
+ * - More aggressively clear stale map bits and maybe find some way to
+ * also clear mm->cpu_vm_mask bits when processes are migrated
+ */
+
+#undef DEBUG
+#define DEBUG_STEAL_ONLY
+#undef DEBUG_MAP_CONSISTENCY
+/*#define DEBUG_CLAMP_LAST_CONTEXT 15 */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/bootmem.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+
+static unsigned int first_context, last_context;
+static unsigned int next_context, nr_free_contexts;
+static unsigned long *context_map;
+static unsigned long *stale_map[NR_CPUS];
+static struct mm_struct **context_mm;
+static spinlock_t context_lock = SPIN_LOCK_UNLOCKED;
+
+#define CTX_MAP_SIZE \
+ (sizeof(unsigned long) * (last_context / BITS_PER_LONG + 1))
+
+
+/* Steal a context from a task that has one at the moment.
+ *
+ * This is used when we are running out of available PID numbers
+ * on the processors.
+ *
+ * This isn't an LRU system, it just frees up each context in
+ * turn (sort-of pseudo-random replacement :). This would be the
+ * place to implement an LRU scheme if anyone was motivated to do it.
+ * -- paulus
+ *
+ * For context stealing, we use a slightly different approach for
+ * SMP and UP. Basically, the UP one is simpler and doesn't use
+ * the stale map as we can just flush the local CPU
+ * -- benh
+ */
+#ifdef CONFIG_SMP
+static unsigned int steal_context_smp(unsigned int id)
+{
+ struct mm_struct *mm;
+ unsigned int cpu, max;
+
+ again:
+ max = last_context - first_context;
+
+ /* Attempt to free next_context first and then loop until we manage */
+ while (max--) {
+ /* Pick up the victim mm */
+ mm = context_mm[id];
+
+ /* We have a candidate victim, check if it's active, on SMP
+ * we cannot steal active contexts
+ */
+ if (mm->context.active) {
+ id++;
+ if (id > last_context)
+ id = first_context;
+ continue;
+ }
+ pr_debug("[%d] steal context %d from mm @%p\n",
+ smp_processor_id(), id, mm);
+
+ /* Mark this mm has having no context anymore */
+ mm->context.id = MMU_NO_CONTEXT;
+
+ /* Mark it stale on all CPUs that used this mm */
+ for_each_cpu_mask_nr(cpu, mm->cpu_vm_mask)
+ __set_bit(id, stale_map[cpu]);
+ return id;
+ }
+
+ /* This will happen if you have more CPUs than available contexts,
+ * all we can do here is wait a bit and try again
+ */
+ spin_unlock(&context_lock);
+ cpu_relax();
+ spin_lock(&context_lock);
+ goto again;
+}
+#endif /* CONFIG_SMP */
+
+/* Note that this will also be called on SMP if all other CPUs are
+ * offlined, which means that it may be called for cpu != 0. For
+ * this to work, we somewhat assume that CPUs that are onlined
+ * come up with a fully clean TLB (or are cleaned when offlined)
+ */
+static unsigned int steal_context_up(unsigned int id)
+{
+ struct mm_struct *mm;
+ int cpu = smp_processor_id();
+
+ /* Pick up the victim mm */
+ mm = context_mm[id];
+
+ pr_debug("[%d] steal context %d from mm @%p\n", cpu, id, mm);
+
+ /* Mark this mm has having no context anymore */
+ mm->context.id = MMU_NO_CONTEXT;
+
+ /* Flush the TLB for that context */
+ local_flush_tlb_mm(mm);
+
+ /* XXX This clear should ultimately be part of local_flush_tlb_mm */
+ __clear_bit(id, stale_map[cpu]);
+
+ return id;
+}
+
+#ifdef DEBUG_MAP_CONSISTENCY
+static void context_check_map(void)
+{
+ unsigned int id, nrf, nact;
+
+ nrf = nact = 0;
+ for (id = first_context; id <= last_context; id++) {
+ int used = test_bit(id, context_map);
+ if (!used)
+ nrf++;
+ if (used != (context_mm[id] != NULL))
+ pr_err("MMU: Context %d is %s and MM is %p !\n",
+ id, used ? "used" : "free", context_mm[id]);
+ if (context_mm[id] != NULL)
+ nact += context_mm[id]->context.active;
+ }
+ if (nrf != nr_free_contexts) {
+ pr_err("MMU: Free context count out of sync ! (%d vs %d)\n",
+ nr_free_contexts, nrf);
+ nr_free_contexts = nrf;
+ }
+ if (nact > num_online_cpus())
+ pr_err("MMU: More active contexts than CPUs ! (%d vs %d)\n",
+ nact, num_online_cpus());
+ if (first_context > 0 && !test_bit(0, context_map))
+ pr_err("MMU: Context 0 has been freed !!!\n");
+}
+#else
+static void context_check_map(void) { }
+#endif
+
+void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
+{
+ unsigned int id, cpu = smp_processor_id();
+ unsigned long *map;
+
+ /* No lockless fast path .. yet */
+ spin_lock(&context_lock);
+
+#ifndef DEBUG_STEAL_ONLY
+ pr_debug("[%d] activating context for mm @%p, active=%d, id=%d\n",
+ cpu, next, next->context.active, next->context.id);
+#endif
+
+#ifdef CONFIG_SMP
+ /* Mark us active and the previous one not anymore */
+ next->context.active++;
+ if (prev) {
+#ifndef DEBUG_STEAL_ONLY
+ pr_debug(" old context %p active was: %d\n",
+ prev, prev->context.active);
+#endif
+ WARN_ON(prev->context.active < 1);
+ prev->context.active--;
+ }
+#endif /* CONFIG_SMP */
+
+ /* If we already have a valid assigned context, skip all that */
+ id = next->context.id;
+ if (likely(id != MMU_NO_CONTEXT))
+ goto ctxt_ok;
+
+ /* We really don't have a context, let's try to acquire one */
+ id = next_context;
+ if (id > last_context)
+ id = first_context;
+ map = context_map;
+
+ /* No more free contexts, let's try to steal one */
+ if (nr_free_contexts == 0) {
+#ifdef CONFIG_SMP
+ if (num_online_cpus() > 1) {
+ id = steal_context_smp(id);
+ goto stolen;
+ }
+#endif /* CONFIG_SMP */
+ id = steal_context_up(id);
+ goto stolen;
+ }
+ nr_free_contexts--;
+
+ /* We know there's at least one free context, try to find it */
+ while (__test_and_set_bit(id, map)) {
+ id = find_next_zero_bit(map, last_context+1, id);
+ if (id > last_context)
+ id = first_context;
+ }
+ stolen:
+ next_context = id + 1;
+ context_mm[id] = next;
+ next->context.id = id;
+
+#ifndef DEBUG_STEAL_ONLY
+ pr_debug("[%d] picked up new id %d, nrf is now %d\n",
+ cpu, id, nr_free_contexts);
+#endif
+
+ context_check_map();
+ ctxt_ok:
+
+ /* If that context got marked stale on this CPU, then flush the
+ * local TLB for it and unmark it before we use it
+ */
+ if (test_bit(id, stale_map[cpu])) {
+ pr_debug("[%d] flushing stale context %d for mm @%p !\n",
+ cpu, id, next);
+ local_flush_tlb_mm(next);
+
+ /* XXX This clear should ultimately be part of local_flush_tlb_mm */
+ __clear_bit(id, stale_map[cpu]);
+ }
+
+ /* Flick the MMU and release lock */
+ set_context(id, next->pgd);
+ spin_unlock(&context_lock);
+}
+
+/*
+ * Set up the context for a new address space.
+ */
+int init_new_context(struct task_struct *t, struct mm_struct *mm)
+{
+ mm->context.id = MMU_NO_CONTEXT;
+ mm->context.active = 0;
+
+ return 0;
+}
+
+/*
+ * We're finished using the context for an address space.
+ */
+void destroy_context(struct mm_struct *mm)
+{
+ unsigned int id;
+
+ if (mm->context.id == MMU_NO_CONTEXT)
+ return;
+
+ WARN_ON(mm->context.active != 0);
+
+ spin_lock(&context_lock);
+ id = mm->context.id;
+ if (id != MMU_NO_CONTEXT) {
+ __clear_bit(id, context_map);
+ mm->context.id = MMU_NO_CONTEXT;
+#ifdef DEBUG_MAP_CONSISTENCY
+ mm->context.active = 0;
+ context_mm[id] = NULL;
+#endif
+ nr_free_contexts++;
+ }
+ spin_unlock(&context_lock);
+}
+
+#ifdef CONFIG_SMP
+
+static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned int)(long)hcpu;
+
+ /* We don't touch CPU 0 map, it's allocated at aboot and kept
+ * around forever
+ */
+ if (cpu == 0)
+ return NOTIFY_OK;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ pr_debug("MMU: Allocating stale context map for CPU %d\n", cpu);
+ stale_map[cpu] = kzalloc(CTX_MAP_SIZE, GFP_KERNEL);
+ break;
+#ifdef CONFIG_HOTPLUG_CPU
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ pr_debug("MMU: Freeing stale context map for CPU %d\n", cpu);
+ kfree(stale_map[cpu]);
+ stale_map[cpu] = NULL;
+ break;
+#endif
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata mmu_context_cpu_nb = {
+ .notifier_call = mmu_context_cpu_notify,
+};
+
+#endif /* CONFIG_SMP */
+
+/*
+ * Initialize the context management stuff.
+ */
+void __init mmu_context_init(void)
+{
+ /* Mark init_mm as being active on all possible CPUs since
+ * we'll get called with prev == init_mm the first time
+ * we schedule on a given CPU
+ */
+ init_mm.context.active = NR_CPUS;
+
+ /*
+ * The MPC8xx has only 16 contexts. We rotate through them on each
+ * task switch. A better way would be to keep track of tasks that
+ * own contexts, and implement an LRU usage. That way very active
+ * tasks don't always have to pay the TLB reload overhead. The
+ * kernel pages are mapped shared, so the kernel can run on behalf
+ * of any task that makes a kernel entry. Shared does not mean they
+ * are not protected, just that the ASID comparison is not performed.
+ * -- Dan
+ *
+ * The IBM4xx has 256 contexts, so we can just rotate through these
+ * as a way of "switching" contexts. If the TID of the TLB is zero,
+ * the PID/TID comparison is disabled, so we can use a TID of zero
+ * to represent all kernel pages as shared among all contexts.
+ * -- Dan
+ */
+ if (mmu_has_feature(MMU_FTR_TYPE_8xx)) {
+ first_context = 0;
+ last_context = 15;
+ } else {
+ first_context = 1;
+ last_context = 255;
+ }
+
+#ifdef DEBUG_CLAMP_LAST_CONTEXT
+ last_context = DEBUG_CLAMP_LAST_CONTEXT;
+#endif
+ /*
+ * Allocate the maps used by context management
+ */
+ context_map = alloc_bootmem(CTX_MAP_SIZE);
+ context_mm = alloc_bootmem(sizeof(void *) * (last_context + 1));
+ stale_map[0] = alloc_bootmem(CTX_MAP_SIZE);
+
+#ifdef CONFIG_SMP
+ register_cpu_notifier(&mmu_context_cpu_nb);
+#endif
+
+ printk(KERN_INFO
+ "MMU: Allocated %d bytes of context maps for %d contexts\n",
+ 2 * CTX_MAP_SIZE + (sizeof(void *) * (last_context + 1)),
+ last_context - first_context + 1);
+
+ /*
+ * Some processors have too few contexts to reserve one for
+ * init_mm, and require using context 0 for a normal task.
+ * Other processors reserve the use of context zero for the kernel.
+ * This code assumes first_context < 32.
+ */
+ context_map[0] = (1 << first_context) - 1;
+ next_context = first_context;
+ nr_free_contexts = last_context - first_context + 1;
+}
+
diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
index fab3cfad4099..4314b39b6faf 100644
--- a/arch/powerpc/mm/mmu_decl.h
+++ b/arch/powerpc/mm/mmu_decl.h
@@ -22,10 +22,58 @@
#include <asm/tlbflush.h>
#include <asm/mmu.h>
+#ifdef CONFIG_PPC_MMU_NOHASH
+
+/*
+ * On 40x and 8xx, we directly inline tlbia and tlbivax
+ */
+#if defined(CONFIG_40x) || defined(CONFIG_8xx)
+static inline void _tlbil_all(void)
+{
+ asm volatile ("sync; tlbia; isync" : : : "memory")
+}
+static inline void _tlbil_pid(unsigned int pid)
+{
+ asm volatile ("sync; tlbia; isync" : : : "memory")
+}
+#else /* CONFIG_40x || CONFIG_8xx */
+extern void _tlbil_all(void);
+extern void _tlbil_pid(unsigned int pid);
+#endif /* !(CONFIG_40x || CONFIG_8xx) */
+
+/*
+ * On 8xx, we directly inline tlbie, on others, it's extern
+ */
+#ifdef CONFIG_8xx
+static inline void _tlbil_va(unsigned long address, unsigned int pid)
+{
+ asm volatile ("tlbie %0; sync" : : "r" (address) : "memory")
+}
+#else /* CONFIG_8xx */
+extern void _tlbil_va(unsigned long address, unsigned int pid);
+#endif /* CONIFG_8xx */
+
+/*
+ * As of today, we don't support tlbivax broadcast on any
+ * implementation. When that becomes the case, this will be
+ * an extern.
+ */
+static inline void _tlbivax_bcast(unsigned long address, unsigned int pid)
+{
+ BUG();
+}
+
+#else /* CONFIG_PPC_MMU_NOHASH */
+
extern void hash_preload(struct mm_struct *mm, unsigned long ea,
unsigned long access, unsigned long trap);
+extern void _tlbie(unsigned long address);
+extern void _tlbia(void);
+
+#endif /* CONFIG_PPC_MMU_NOHASH */
+
#ifdef CONFIG_PPC32
extern void mapin_ram(void);
extern int map_page(unsigned long va, phys_addr_t pa, int flags);
@@ -58,17 +106,14 @@ extern phys_addr_t lowmem_end_addr;
* architectures. -- Dan
*/
#if defined(CONFIG_8xx)
-#define flush_HPTE(X, va, pg) _tlbie(va, 0 /* 8xx doesn't care about PID */)
#define MMU_init_hw() do { } while(0)
#define mmu_mapin_ram() (0UL)
#elif defined(CONFIG_4xx)
-#define flush_HPTE(pid, va, pg) _tlbie(va, pid)
extern void MMU_init_hw(void);
extern unsigned long mmu_mapin_ram(void);
#elif defined(CONFIG_FSL_BOOKE)
-#define flush_HPTE(pid, va, pg) _tlbie(va, pid)
extern void MMU_init_hw(void);
extern unsigned long mmu_mapin_ram(void);
extern void adjust_total_lowmem(void);
@@ -77,18 +122,4 @@ extern void adjust_total_lowmem(void);
/* anything 32-bit except 4xx or 8xx */
extern void MMU_init_hw(void);
extern unsigned long mmu_mapin_ram(void);
-
-/* Be careful....this needs to be updated if we ever encounter 603 SMPs,
- * which includes all new 82xx processors. We need tlbie/tlbsync here
- * in that case (I think). -- Dan.
- */
-static inline void flush_HPTE(unsigned context, unsigned long va,
- unsigned long pdval)
-{
- if ((Hash != 0) &&
- cpu_has_feature(CPU_FTR_HPTE_TABLE))
- flush_hash_pages(0, va, pdval, 1);
- else
- _tlbie(va);
-}
#endif
diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c
new file mode 100644
index 000000000000..6d94116fdea1
--- /dev/null
+++ b/arch/powerpc/mm/pgtable.c
@@ -0,0 +1,117 @@
+/*
+ * This file contains common routines for dealing with free of page tables
+ *
+ * Derived from arch/powerpc/mm/tlb_64.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * Dave Engebretsen <engebret@us.ibm.com>
+ * Rework for PPC64 port.
+ *
+ * This program is free software; you can redistribute 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/mm.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+
+static DEFINE_PER_CPU(struct pte_freelist_batch *, pte_freelist_cur);
+static unsigned long pte_freelist_forced_free;
+
+struct pte_freelist_batch
+{
+ struct rcu_head rcu;
+ unsigned int index;
+ pgtable_free_t tables[0];
+};
+
+#define PTE_FREELIST_SIZE \
+ ((PAGE_SIZE - sizeof(struct pte_freelist_batch)) \
+ / sizeof(pgtable_free_t))
+
+static void pte_free_smp_sync(void *arg)
+{
+ /* Do nothing, just ensure we sync with all CPUs */
+}
+
+/* This is only called when we are critically out of memory
+ * (and fail to get a page in pte_free_tlb).
+ */
+static void pgtable_free_now(pgtable_free_t pgf)
+{
+ pte_freelist_forced_free++;
+
+ smp_call_function(pte_free_smp_sync, NULL, 1);
+
+ pgtable_free(pgf);
+}
+
+static void pte_free_rcu_callback(struct rcu_head *head)
+{
+ struct pte_freelist_batch *batch =
+ container_of(head, struct pte_freelist_batch, rcu);
+ unsigned int i;
+
+ for (i = 0; i < batch->index; i++)
+ pgtable_free(batch->tables[i]);
+
+ free_page((unsigned long)batch);
+}
+
+static void pte_free_submit(struct pte_freelist_batch *batch)
+{
+ INIT_RCU_HEAD(&batch->rcu);
+ call_rcu(&batch->rcu, pte_free_rcu_callback);
+}
+
+void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf)
+{
+ /* This is safe since tlb_gather_mmu has disabled preemption */
+ cpumask_t local_cpumask = cpumask_of_cpu(smp_processor_id());
+ struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur);
+
+ if (atomic_read(&tlb->mm->mm_users) < 2 ||
+ cpus_equal(tlb->mm->cpu_vm_mask, local_cpumask)) {
+ pgtable_free(pgf);
+ return;
+ }
+
+ if (*batchp == NULL) {
+ *batchp = (struct pte_freelist_batch *)__get_free_page(GFP_ATOMIC);
+ if (*batchp == NULL) {
+ pgtable_free_now(pgf);
+ return;
+ }
+ (*batchp)->index = 0;
+ }
+ (*batchp)->tables[(*batchp)->index++] = pgf;
+ if ((*batchp)->index == PTE_FREELIST_SIZE) {
+ pte_free_submit(*batchp);
+ *batchp = NULL;
+ }
+}
+
+void pte_free_finish(void)
+{
+ /* This is safe since tlb_gather_mmu has disabled preemption */
+ struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur);
+
+ if (*batchp == NULL)
+ return;
+ pte_free_submit(*batchp);
+ *batchp = NULL;
+}
diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c
index c31d6d26f0b5..38ff35f2142a 100644
--- a/arch/powerpc/mm/pgtable_32.c
+++ b/arch/powerpc/mm/pgtable_32.c
@@ -48,10 +48,6 @@ EXPORT_SYMBOL(ioremap_bot); /* aka VMALLOC_END */
extern char etext[], _stext[];
-#ifdef CONFIG_SMP
-extern void hash_page_sync(void);
-#endif
-
#ifdef HAVE_BATS
extern phys_addr_t v_mapped_by_bats(unsigned long va);
extern unsigned long p_mapped_by_bats(phys_addr_t pa);
@@ -72,24 +68,29 @@ extern unsigned long p_mapped_by_tlbcam(unsigned long pa);
#define p_mapped_by_tlbcam(x) (0UL)
#endif /* HAVE_TLBCAM */
-#ifdef CONFIG_PTE_64BIT
-/* Some processors use an 8kB pgdir because they have 8-byte Linux PTEs. */
-#define PGDIR_ORDER 1
-#else
-#define PGDIR_ORDER 0
-#endif
+#define PGDIR_ORDER (32 + PGD_T_LOG2 - PGDIR_SHIFT)
pgd_t *pgd_alloc(struct mm_struct *mm)
{
pgd_t *ret;
- ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, PGDIR_ORDER);
+ /* pgdir take page or two with 4K pages and a page fraction otherwise */
+#ifndef CONFIG_PPC_4K_PAGES
+ ret = (pgd_t *)kzalloc(1 << PGDIR_ORDER, GFP_KERNEL);
+#else
+ ret = (pgd_t *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
+ PGDIR_ORDER - PAGE_SHIFT);
+#endif
return ret;
}
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
- free_pages((unsigned long)pgd, PGDIR_ORDER);
+#ifndef CONFIG_PPC_4K_PAGES
+ kfree((void *)pgd);
+#else
+ free_pages((unsigned long)pgd, PGDIR_ORDER - PAGE_SHIFT);
+#endif
}
__init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
@@ -125,23 +126,6 @@ pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address)
return ptepage;
}
-void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
-{
-#ifdef CONFIG_SMP
- hash_page_sync();
-#endif
- free_page((unsigned long)pte);
-}
-
-void pte_free(struct mm_struct *mm, pgtable_t ptepage)
-{
-#ifdef CONFIG_SMP
- hash_page_sync();
-#endif
- pgtable_page_dtor(ptepage);
- __free_page(ptepage);
-}
-
void __iomem *
ioremap(phys_addr_t addr, unsigned long size)
{
@@ -194,6 +178,7 @@ __ioremap(phys_addr_t addr, unsigned long size, unsigned long flags)
if (p < 16*1024*1024)
p += _ISA_MEM_BASE;
+#ifndef CONFIG_CRASH_DUMP
/*
* Don't allow anybody to remap normal RAM that we're using.
* mem_init() sets high_memory so only do the check after that.
@@ -203,6 +188,7 @@ __ioremap(phys_addr_t addr, unsigned long size, unsigned long flags)
(unsigned long long)p, __builtin_return_address(0));
return NULL;
}
+#endif
if (size == 0)
return NULL;
@@ -288,7 +274,7 @@ int map_page(unsigned long va, phys_addr_t pa, int flags)
}
/*
- * Map in a big chunk of physical memory starting at KERNELBASE.
+ * Map in a big chunk of physical memory starting at PAGE_OFFSET.
*/
void __init mapin_ram(void)
{
@@ -297,7 +283,7 @@ void __init mapin_ram(void)
int ktext;
s = mmu_mapin_ram();
- v = KERNELBASE + s;
+ v = PAGE_OFFSET + s;
p = memstart_addr + s;
for (; s < total_lowmem; s += PAGE_SIZE) {
ktext = ((char *) v >= _stext && (char *) v < etext);
@@ -363,7 +349,11 @@ static int __change_page_attr(struct page *page, pgprot_t prot)
return -EINVAL;
set_pte_at(&init_mm, address, kpte, mk_pte(page, prot));
wmb();
- flush_HPTE(0, address, pmd_val(*kpmd));
+#ifdef CONFIG_PPC_STD_MMU
+ flush_hash_pages(0, address, pmd_val(*kpmd), 1);
+#else
+ flush_tlb_page(NULL, address);
+#endif
pte_unmap(kpte);
return 0;
@@ -400,7 +390,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
#endif /* CONFIG_DEBUG_PAGEALLOC */
static int fixmaps;
-unsigned long FIXADDR_TOP = 0xfffff000;
+unsigned long FIXADDR_TOP = (-PAGE_SIZE);
EXPORT_SYMBOL(FIXADDR_TOP);
void __set_fixmap (enum fixed_addresses idx, phys_addr_t phys, pgprot_t flags)
diff --git a/arch/powerpc/mm/ppc_mmu_32.c b/arch/powerpc/mm/ppc_mmu_32.c
index 6aa120813775..45d925360b89 100644
--- a/arch/powerpc/mm/ppc_mmu_32.c
+++ b/arch/powerpc/mm/ppc_mmu_32.c
@@ -95,16 +95,16 @@ unsigned long __init mmu_mapin_ram(void)
break;
}
- setbat(2, KERNELBASE, 0, bl, _PAGE_RAM);
- done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
+ setbat(2, PAGE_OFFSET, 0, bl, _PAGE_RAM);
+ done = (unsigned long)bat_addrs[2].limit - PAGE_OFFSET + 1;
if ((done < tot) && !bat_addrs[3].limit) {
/* use BAT3 to cover a bit more */
tot -= done;
for (bl = 128<<10; bl < max_size; bl <<= 1)
if (bl * 2 > tot)
break;
- setbat(3, KERNELBASE+done, done, bl, _PAGE_RAM);
- done = (unsigned long)bat_addrs[3].limit - KERNELBASE + 1;
+ setbat(3, PAGE_OFFSET+done, done, bl, _PAGE_RAM);
+ done = (unsigned long)bat_addrs[3].limit - PAGE_OFFSET + 1;
}
return done;
@@ -192,7 +192,7 @@ void __init MMU_init_hw(void)
extern unsigned int hash_page[];
extern unsigned int flush_hash_patch_A[], flush_hash_patch_B[];
- if (!cpu_has_feature(CPU_FTR_HPTE_TABLE)) {
+ if (!mmu_has_feature(MMU_FTR_HPTE_TABLE)) {
/*
* Put a blr (procedure return) instruction at the
* start of hash_page, since we can still get DSI
diff --git a/arch/powerpc/mm/tlb_32.c b/arch/powerpc/mm/tlb_32.c
deleted file mode 100644
index f9a47fee3927..000000000000
--- a/arch/powerpc/mm/tlb_32.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * This file contains the routines for TLB flushing.
- * On machines where the MMU uses a hash table to store virtual to
- * physical translations, these routines flush entries from the
- * hash table also.
- * -- paulus
- *
- * Derived from arch/ppc/mm/init.c:
- * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
- * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
- * and Cort Dougan (PReP) (cort@cs.nmt.edu)
- * Copyright (C) 1996 Paul Mackerras
- *
- * Derived from "arch/i386/mm/init.c"
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- *
- * This program is free software; you can redistribute 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/mm.h>
-#include <linux/init.h>
-#include <linux/highmem.h>
-#include <linux/pagemap.h>
-
-#include <asm/tlbflush.h>
-#include <asm/tlb.h>
-
-#include "mmu_decl.h"
-
-/*
- * Called when unmapping pages to flush entries from the TLB/hash table.
- */
-void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr)
-{
- unsigned long ptephys;
-
- if (Hash != 0) {
- ptephys = __pa(ptep) & PAGE_MASK;
- flush_hash_pages(mm->context.id, addr, ptephys, 1);
- }
-}
-EXPORT_SYMBOL(flush_hash_entry);
-
-/*
- * Called by ptep_set_access_flags, must flush on CPUs for which the
- * DSI handler can't just "fixup" the TLB on a write fault
- */
-void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr)
-{
- if (Hash != 0)
- return;
- _tlbie(addr);
-}
-
-/*
- * Called at the end of a mmu_gather operation to make sure the
- * TLB flush is completely done.
- */
-void tlb_flush(struct mmu_gather *tlb)
-{
- if (Hash == 0) {
- /*
- * 603 needs to flush the whole TLB here since
- * it doesn't use a hash table.
- */
- _tlbia();
- }
-}
-
-/*
- * TLB flushing:
- *
- * - flush_tlb_mm(mm) flushes the specified mm context TLB's
- * - flush_tlb_page(vma, vmaddr) flushes one page
- * - flush_tlb_range(vma, start, end) flushes a range of pages
- * - flush_tlb_kernel_range(start, end) flushes kernel pages
- *
- * since the hardware hash table functions as an extension of the
- * tlb as far as the linux tables are concerned, flush it too.
- * -- Cort
- */
-
-/*
- * 750 SMP is a Bad Idea because the 750 doesn't broadcast all
- * the cache operations on the bus. Hence we need to use an IPI
- * to get the other CPU(s) to invalidate their TLBs.
- */
-#ifdef CONFIG_SMP_750
-#define FINISH_FLUSH smp_send_tlb_invalidate(0)
-#else
-#define FINISH_FLUSH do { } while (0)
-#endif
-
-static void flush_range(struct mm_struct *mm, unsigned long start,
- unsigned long end)
-{
- pmd_t *pmd;
- unsigned long pmd_end;
- int count;
- unsigned int ctx = mm->context.id;
-
- if (Hash == 0) {
- _tlbia();
- return;
- }
- start &= PAGE_MASK;
- if (start >= end)
- return;
- end = (end - 1) | ~PAGE_MASK;
- pmd = pmd_offset(pud_offset(pgd_offset(mm, start), start), start);
- for (;;) {
- pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1;
- if (pmd_end > end)
- pmd_end = end;
- if (!pmd_none(*pmd)) {
- count = ((pmd_end - start) >> PAGE_SHIFT) + 1;
- flush_hash_pages(ctx, start, pmd_val(*pmd), count);
- }
- if (pmd_end == end)
- break;
- start = pmd_end + 1;
- ++pmd;
- }
-}
-
-/*
- * Flush kernel TLB entries in the given range
- */
-void flush_tlb_kernel_range(unsigned long start, unsigned long end)
-{
- flush_range(&init_mm, start, end);
- FINISH_FLUSH;
-}
-
-/*
- * Flush all the (user) entries for the address space described by mm.
- */
-void flush_tlb_mm(struct mm_struct *mm)
-{
- struct vm_area_struct *mp;
-
- if (Hash == 0) {
- _tlbia();
- return;
- }
-
- /*
- * It is safe to go down the mm's list of vmas when called
- * from dup_mmap, holding mmap_sem. It would also be safe from
- * unmap_region or exit_mmap, but not from vmtruncate on SMP -
- * but it seems dup_mmap is the only SMP case which gets here.
- */
- for (mp = mm->mmap; mp != NULL; mp = mp->vm_next)
- flush_range(mp->vm_mm, mp->vm_start, mp->vm_end);
- FINISH_FLUSH;
-}
-
-void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
-{
- struct mm_struct *mm;
- pmd_t *pmd;
-
- if (Hash == 0) {
- _tlbie(vmaddr);
- return;
- }
- mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
- pmd = pmd_offset(pud_offset(pgd_offset(mm, vmaddr), vmaddr), vmaddr);
- if (!pmd_none(*pmd))
- flush_hash_pages(mm->context.id, vmaddr, pmd_val(*pmd), 1);
- FINISH_FLUSH;
-}
-
-/*
- * For each address in the range, find the pte for the address
- * and check _PAGE_HASHPTE bit; if it is set, find and destroy
- * the corresponding HPTE.
- */
-void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end)
-{
- flush_range(vma->vm_mm, start, end);
- FINISH_FLUSH;
-}
diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c
deleted file mode 100644
index be7dd422c0fa..000000000000
--- a/arch/powerpc/mm/tlb_64.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * This file contains the routines for flushing entries from the
- * TLB and MMU hash table.
- *
- * Derived from arch/ppc64/mm/init.c:
- * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
- * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
- * and Cort Dougan (PReP) (cort@cs.nmt.edu)
- * Copyright (C) 1996 Paul Mackerras
- *
- * Derived from "arch/i386/mm/init.c"
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- *
- * Dave Engebretsen <engebret@us.ibm.com>
- * Rework for PPC64 port.
- *
- * This program is free software; you can redistribute 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/mm.h>
-#include <linux/init.h>
-#include <linux/percpu.h>
-#include <linux/hardirq.h>
-#include <asm/pgalloc.h>
-#include <asm/tlbflush.h>
-#include <asm/tlb.h>
-#include <asm/bug.h>
-
-DEFINE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch);
-
-/* This is declared as we are using the more or less generic
- * arch/powerpc/include/asm/tlb.h file -- tgall
- */
-DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
-static DEFINE_PER_CPU(struct pte_freelist_batch *, pte_freelist_cur);
-static unsigned long pte_freelist_forced_free;
-
-struct pte_freelist_batch
-{
- struct rcu_head rcu;
- unsigned int index;
- pgtable_free_t tables[0];
-};
-
-#define PTE_FREELIST_SIZE \
- ((PAGE_SIZE - sizeof(struct pte_freelist_batch)) \
- / sizeof(pgtable_free_t))
-
-static void pte_free_smp_sync(void *arg)
-{
- /* Do nothing, just ensure we sync with all CPUs */
-}
-
-/* This is only called when we are critically out of memory
- * (and fail to get a page in pte_free_tlb).
- */
-static void pgtable_free_now(pgtable_free_t pgf)
-{
- pte_freelist_forced_free++;
-
- smp_call_function(pte_free_smp_sync, NULL, 1);
-
- pgtable_free(pgf);
-}
-
-static void pte_free_rcu_callback(struct rcu_head *head)
-{
- struct pte_freelist_batch *batch =
- container_of(head, struct pte_freelist_batch, rcu);
- unsigned int i;
-
- for (i = 0; i < batch->index; i++)
- pgtable_free(batch->tables[i]);
-
- free_page((unsigned long)batch);
-}
-
-static void pte_free_submit(struct pte_freelist_batch *batch)
-{
- INIT_RCU_HEAD(&batch->rcu);
- call_rcu(&batch->rcu, pte_free_rcu_callback);
-}
-
-void pgtable_free_tlb(struct mmu_gather *tlb, pgtable_free_t pgf)
-{
- /* This is safe since tlb_gather_mmu has disabled preemption */
- cpumask_t local_cpumask = cpumask_of_cpu(smp_processor_id());
- struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur);
-
- if (atomic_read(&tlb->mm->mm_users) < 2 ||
- cpus_equal(tlb->mm->cpu_vm_mask, local_cpumask)) {
- pgtable_free(pgf);
- return;
- }
-
- if (*batchp == NULL) {
- *batchp = (struct pte_freelist_batch *)__get_free_page(GFP_ATOMIC);
- if (*batchp == NULL) {
- pgtable_free_now(pgf);
- return;
- }
- (*batchp)->index = 0;
- }
- (*batchp)->tables[(*batchp)->index++] = pgf;
- if ((*batchp)->index == PTE_FREELIST_SIZE) {
- pte_free_submit(*batchp);
- *batchp = NULL;
- }
-}
-
-/*
- * A linux PTE was changed and the corresponding hash table entry
- * neesd to be flushed. This function will either perform the flush
- * immediately or will batch it up if the current CPU has an active
- * batch on it.
- *
- * Must be called from within some kind of spinlock/non-preempt region...
- */
-void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, unsigned long pte, int huge)
-{
- struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
- unsigned long vsid, vaddr;
- unsigned int psize;
- int ssize;
- real_pte_t rpte;
- int i;
-
- i = batch->index;
-
- /* We mask the address for the base page size. Huge pages will
- * have applied their own masking already
- */
- addr &= PAGE_MASK;
-
- /* Get page size (maybe move back to caller).
- *
- * NOTE: when using special 64K mappings in 4K environment like
- * for SPEs, we obtain the page size from the slice, which thus
- * must still exist (and thus the VMA not reused) at the time
- * of this call
- */
- if (huge) {
-#ifdef CONFIG_HUGETLB_PAGE
- psize = get_slice_psize(mm, addr);;
-#else
- BUG();
- psize = pte_pagesize_index(mm, addr, pte); /* shutup gcc */
-#endif
- } else
- psize = pte_pagesize_index(mm, addr, pte);
-
- /* Build full vaddr */
- if (!is_kernel_addr(addr)) {
- ssize = user_segment_size(addr);
- vsid = get_vsid(mm->context.id, addr, ssize);
- WARN_ON(vsid == 0);
- } else {
- vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
- ssize = mmu_kernel_ssize;
- }
- vaddr = hpt_va(addr, vsid, ssize);
- rpte = __real_pte(__pte(pte), ptep);
-
- /*
- * Check if we have an active batch on this CPU. If not, just
- * flush now and return. For now, we don global invalidates
- * in that case, might be worth testing the mm cpu mask though
- * and decide to use local invalidates instead...
- */
- if (!batch->active) {
- flush_hash_page(vaddr, rpte, psize, ssize, 0);
- return;
- }
-
- /*
- * This can happen when we are in the middle of a TLB batch and
- * we encounter memory pressure (eg copy_page_range when it tries
- * to allocate a new pte). If we have to reclaim memory and end
- * up scanning and resetting referenced bits then our batch context
- * will change mid stream.
- *
- * We also need to ensure only one page size is present in a given
- * batch
- */
- if (i != 0 && (mm != batch->mm || batch->psize != psize ||
- batch->ssize != ssize)) {
- __flush_tlb_pending(batch);
- i = 0;
- }
- if (i == 0) {
- batch->mm = mm;
- batch->psize = psize;
- batch->ssize = ssize;
- }
- batch->pte[i] = rpte;
- batch->vaddr[i] = vaddr;
- batch->index = ++i;
- if (i >= PPC64_TLB_BATCH_NR)
- __flush_tlb_pending(batch);
-}
-
-/*
- * This function is called when terminating an mmu batch or when a batch
- * is full. It will perform the flush of all the entries currently stored
- * in a batch.
- *
- * Must be called from within some kind of spinlock/non-preempt region...
- */
-void __flush_tlb_pending(struct ppc64_tlb_batch *batch)
-{
- cpumask_t tmp;
- int i, local = 0;
-
- i = batch->index;
- tmp = cpumask_of_cpu(smp_processor_id());
- if (cpus_equal(batch->mm->cpu_vm_mask, tmp))
- local = 1;
- if (i == 1)
- flush_hash_page(batch->vaddr[0], batch->pte[0],
- batch->psize, batch->ssize, local);
- else
- flush_hash_range(i, local);
- batch->index = 0;
-}
-
-void pte_free_finish(void)
-{
- /* This is safe since tlb_gather_mmu has disabled preemption */
- struct pte_freelist_batch **batchp = &__get_cpu_var(pte_freelist_cur);
-
- if (*batchp == NULL)
- return;
- pte_free_submit(*batchp);
- *batchp = NULL;
-}
-
-/**
- * __flush_hash_table_range - Flush all HPTEs for a given address range
- * from the hash table (and the TLB). But keeps
- * the linux PTEs intact.
- *
- * @mm : mm_struct of the target address space (generally init_mm)
- * @start : starting address
- * @end : ending address (not included in the flush)
- *
- * This function is mostly to be used by some IO hotplug code in order
- * to remove all hash entries from a given address range used to map IO
- * space on a removed PCI-PCI bidge without tearing down the full mapping
- * since 64K pages may overlap with other bridges when using 64K pages
- * with 4K HW pages on IO space.
- *
- * Because of that usage pattern, it's only available with CONFIG_HOTPLUG
- * and is implemented for small size rather than speed.
- */
-#ifdef CONFIG_HOTPLUG
-
-void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
- unsigned long end)
-{
- unsigned long flags;
-
- start = _ALIGN_DOWN(start, PAGE_SIZE);
- end = _ALIGN_UP(end, PAGE_SIZE);
-
- BUG_ON(!mm->pgd);
-
- /* Note: Normally, we should only ever use a batch within a
- * PTE locked section. This violates the rule, but will work
- * since we don't actually modify the PTEs, we just flush the
- * hash while leaving the PTEs intact (including their reference
- * to being hashed). This is not the most performance oriented
- * way to do things but is fine for our needs here.
- */
- local_irq_save(flags);
- arch_enter_lazy_mmu_mode();
- for (; start < end; start += PAGE_SIZE) {
- pte_t *ptep = find_linux_pte(mm->pgd, start);
- unsigned long pte;
-
- if (ptep == NULL)
- continue;
- pte = pte_val(*ptep);
- if (!(pte & _PAGE_HASHPTE))
- continue;
- hpte_need_flush(mm, start, ptep, pte, 0);
- }
- arch_leave_lazy_mmu_mode();
- local_irq_restore(flags);
-}
-
-#endif /* CONFIG_HOTPLUG */
diff --git a/arch/powerpc/mm/tlb_hash32.c b/arch/powerpc/mm/tlb_hash32.c
new file mode 100644
index 000000000000..65190587a365
--- /dev/null
+++ b/arch/powerpc/mm/tlb_hash32.c
@@ -0,0 +1,194 @@
+/*
+ * This file contains the routines for TLB flushing.
+ * On machines where the MMU uses a hash table to store virtual to
+ * physical translations, these routines flush entries from the
+ * hash table also.
+ * -- paulus
+ *
+ * Derived from arch/ppc/mm/init.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * This program is free software; you can redistribute 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/mm.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+
+#include "mmu_decl.h"
+
+/*
+ * Called when unmapping pages to flush entries from the TLB/hash table.
+ */
+void flush_hash_entry(struct mm_struct *mm, pte_t *ptep, unsigned long addr)
+{
+ unsigned long ptephys;
+
+ if (Hash != 0) {
+ ptephys = __pa(ptep) & PAGE_MASK;
+ flush_hash_pages(mm->context.id, addr, ptephys, 1);
+ }
+}
+EXPORT_SYMBOL(flush_hash_entry);
+
+/*
+ * Called by ptep_set_access_flags, must flush on CPUs for which the
+ * DSI handler can't just "fixup" the TLB on a write fault
+ */
+void flush_tlb_page_nohash(struct vm_area_struct *vma, unsigned long addr)
+{
+ if (Hash != 0)
+ return;
+ _tlbie(addr);
+}
+
+/*
+ * Called at the end of a mmu_gather operation to make sure the
+ * TLB flush is completely done.
+ */
+void tlb_flush(struct mmu_gather *tlb)
+{
+ if (Hash == 0) {
+ /*
+ * 603 needs to flush the whole TLB here since
+ * it doesn't use a hash table.
+ */
+ _tlbia();
+ }
+}
+
+/*
+ * TLB flushing:
+ *
+ * - flush_tlb_mm(mm) flushes the specified mm context TLB's
+ * - flush_tlb_page(vma, vmaddr) flushes one page
+ * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_kernel_range(start, end) flushes kernel pages
+ *
+ * since the hardware hash table functions as an extension of the
+ * tlb as far as the linux tables are concerned, flush it too.
+ * -- Cort
+ */
+
+/*
+ * 750 SMP is a Bad Idea because the 750 doesn't broadcast all
+ * the cache operations on the bus. Hence we need to use an IPI
+ * to get the other CPU(s) to invalidate their TLBs.
+ */
+#ifdef CONFIG_SMP_750
+#define FINISH_FLUSH smp_send_tlb_invalidate(0)
+#else
+#define FINISH_FLUSH do { } while (0)
+#endif
+
+static void flush_range(struct mm_struct *mm, unsigned long start,
+ unsigned long end)
+{
+ pmd_t *pmd;
+ unsigned long pmd_end;
+ int count;
+ unsigned int ctx = mm->context.id;
+
+ if (Hash == 0) {
+ _tlbia();
+ return;
+ }
+ start &= PAGE_MASK;
+ if (start >= end)
+ return;
+ end = (end - 1) | ~PAGE_MASK;
+ pmd = pmd_offset(pud_offset(pgd_offset(mm, start), start), start);
+ for (;;) {
+ pmd_end = ((start + PGDIR_SIZE) & PGDIR_MASK) - 1;
+ if (pmd_end > end)
+ pmd_end = end;
+ if (!pmd_none(*pmd)) {
+ count = ((pmd_end - start) >> PAGE_SHIFT) + 1;
+ flush_hash_pages(ctx, start, pmd_val(*pmd), count);
+ }
+ if (pmd_end == end)
+ break;
+ start = pmd_end + 1;
+ ++pmd;
+ }
+}
+
+/*
+ * Flush kernel TLB entries in the given range
+ */
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ flush_range(&init_mm, start, end);
+ FINISH_FLUSH;
+}
+EXPORT_SYMBOL(flush_tlb_kernel_range);
+
+/*
+ * Flush all the (user) entries for the address space described by mm.
+ */
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ struct vm_area_struct *mp;
+
+ if (Hash == 0) {
+ _tlbia();
+ return;
+ }
+
+ /*
+ * It is safe to go down the mm's list of vmas when called
+ * from dup_mmap, holding mmap_sem. It would also be safe from
+ * unmap_region or exit_mmap, but not from vmtruncate on SMP -
+ * but it seems dup_mmap is the only SMP case which gets here.
+ */
+ for (mp = mm->mmap; mp != NULL; mp = mp->vm_next)
+ flush_range(mp->vm_mm, mp->vm_start, mp->vm_end);
+ FINISH_FLUSH;
+}
+EXPORT_SYMBOL(flush_tlb_mm);
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
+{
+ struct mm_struct *mm;
+ pmd_t *pmd;
+
+ if (Hash == 0) {
+ _tlbie(vmaddr);
+ return;
+ }
+ mm = (vmaddr < TASK_SIZE)? vma->vm_mm: &init_mm;
+ pmd = pmd_offset(pud_offset(pgd_offset(mm, vmaddr), vmaddr), vmaddr);
+ if (!pmd_none(*pmd))
+ flush_hash_pages(mm->context.id, vmaddr, pmd_val(*pmd), 1);
+ FINISH_FLUSH;
+}
+EXPORT_SYMBOL(flush_tlb_page);
+
+/*
+ * For each address in the range, find the pte for the address
+ * and check _PAGE_HASHPTE bit; if it is set, find and destroy
+ * the corresponding HPTE.
+ */
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ flush_range(vma->vm_mm, start, end);
+ FINISH_FLUSH;
+}
+EXPORT_SYMBOL(flush_tlb_range);
diff --git a/arch/powerpc/mm/tlb_hash64.c b/arch/powerpc/mm/tlb_hash64.c
new file mode 100644
index 000000000000..c931bc7d1079
--- /dev/null
+++ b/arch/powerpc/mm/tlb_hash64.c
@@ -0,0 +1,211 @@
+/*
+ * This file contains the routines for flushing entries from the
+ * TLB and MMU hash table.
+ *
+ * Derived from arch/ppc64/mm/init.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * Dave Engebretsen <engebret@us.ibm.com>
+ * Rework for PPC64 port.
+ *
+ * This program is free software; you can redistribute 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/mm.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+#include <asm/bug.h>
+
+DEFINE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch);
+
+/* This is declared as we are using the more or less generic
+ * arch/powerpc/include/asm/tlb.h file -- tgall
+ */
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+/*
+ * A linux PTE was changed and the corresponding hash table entry
+ * neesd to be flushed. This function will either perform the flush
+ * immediately or will batch it up if the current CPU has an active
+ * batch on it.
+ *
+ * Must be called from within some kind of spinlock/non-preempt region...
+ */
+void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, unsigned long pte, int huge)
+{
+ struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
+ unsigned long vsid, vaddr;
+ unsigned int psize;
+ int ssize;
+ real_pte_t rpte;
+ int i;
+
+ i = batch->index;
+
+ /* We mask the address for the base page size. Huge pages will
+ * have applied their own masking already
+ */
+ addr &= PAGE_MASK;
+
+ /* Get page size (maybe move back to caller).
+ *
+ * NOTE: when using special 64K mappings in 4K environment like
+ * for SPEs, we obtain the page size from the slice, which thus
+ * must still exist (and thus the VMA not reused) at the time
+ * of this call
+ */
+ if (huge) {
+#ifdef CONFIG_HUGETLB_PAGE
+ psize = get_slice_psize(mm, addr);;
+#else
+ BUG();
+ psize = pte_pagesize_index(mm, addr, pte); /* shutup gcc */
+#endif
+ } else
+ psize = pte_pagesize_index(mm, addr, pte);
+
+ /* Build full vaddr */
+ if (!is_kernel_addr(addr)) {
+ ssize = user_segment_size(addr);
+ vsid = get_vsid(mm->context.id, addr, ssize);
+ WARN_ON(vsid == 0);
+ } else {
+ vsid = get_kernel_vsid(addr, mmu_kernel_ssize);
+ ssize = mmu_kernel_ssize;
+ }
+ vaddr = hpt_va(addr, vsid, ssize);
+ rpte = __real_pte(__pte(pte), ptep);
+
+ /*
+ * Check if we have an active batch on this CPU. If not, just
+ * flush now and return. For now, we don global invalidates
+ * in that case, might be worth testing the mm cpu mask though
+ * and decide to use local invalidates instead...
+ */
+ if (!batch->active) {
+ flush_hash_page(vaddr, rpte, psize, ssize, 0);
+ return;
+ }
+
+ /*
+ * This can happen when we are in the middle of a TLB batch and
+ * we encounter memory pressure (eg copy_page_range when it tries
+ * to allocate a new pte). If we have to reclaim memory and end
+ * up scanning and resetting referenced bits then our batch context
+ * will change mid stream.
+ *
+ * We also need to ensure only one page size is present in a given
+ * batch
+ */
+ if (i != 0 && (mm != batch->mm || batch->psize != psize ||
+ batch->ssize != ssize)) {
+ __flush_tlb_pending(batch);
+ i = 0;
+ }
+ if (i == 0) {
+ batch->mm = mm;
+ batch->psize = psize;
+ batch->ssize = ssize;
+ }
+ batch->pte[i] = rpte;
+ batch->vaddr[i] = vaddr;
+ batch->index = ++i;
+ if (i >= PPC64_TLB_BATCH_NR)
+ __flush_tlb_pending(batch);
+}
+
+/*
+ * This function is called when terminating an mmu batch or when a batch
+ * is full. It will perform the flush of all the entries currently stored
+ * in a batch.
+ *
+ * Must be called from within some kind of spinlock/non-preempt region...
+ */
+void __flush_tlb_pending(struct ppc64_tlb_batch *batch)
+{
+ cpumask_t tmp;
+ int i, local = 0;
+
+ i = batch->index;
+ tmp = cpumask_of_cpu(smp_processor_id());
+ if (cpus_equal(batch->mm->cpu_vm_mask, tmp))
+ local = 1;
+ if (i == 1)
+ flush_hash_page(batch->vaddr[0], batch->pte[0],
+ batch->psize, batch->ssize, local);
+ else
+ flush_hash_range(i, local);
+ batch->index = 0;
+}
+
+/**
+ * __flush_hash_table_range - Flush all HPTEs for a given address range
+ * from the hash table (and the TLB). But keeps
+ * the linux PTEs intact.
+ *
+ * @mm : mm_struct of the target address space (generally init_mm)
+ * @start : starting address
+ * @end : ending address (not included in the flush)
+ *
+ * This function is mostly to be used by some IO hotplug code in order
+ * to remove all hash entries from a given address range used to map IO
+ * space on a removed PCI-PCI bidge without tearing down the full mapping
+ * since 64K pages may overlap with other bridges when using 64K pages
+ * with 4K HW pages on IO space.
+ *
+ * Because of that usage pattern, it's only available with CONFIG_HOTPLUG
+ * and is implemented for small size rather than speed.
+ */
+#ifdef CONFIG_HOTPLUG
+
+void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
+ unsigned long end)
+{
+ unsigned long flags;
+
+ start = _ALIGN_DOWN(start, PAGE_SIZE);
+ end = _ALIGN_UP(end, PAGE_SIZE);
+
+ BUG_ON(!mm->pgd);
+
+ /* Note: Normally, we should only ever use a batch within a
+ * PTE locked section. This violates the rule, but will work
+ * since we don't actually modify the PTEs, we just flush the
+ * hash while leaving the PTEs intact (including their reference
+ * to being hashed). This is not the most performance oriented
+ * way to do things but is fine for our needs here.
+ */
+ local_irq_save(flags);
+ arch_enter_lazy_mmu_mode();
+ for (; start < end; start += PAGE_SIZE) {
+ pte_t *ptep = find_linux_pte(mm->pgd, start);
+ unsigned long pte;
+
+ if (ptep == NULL)
+ continue;
+ pte = pte_val(*ptep);
+ if (!(pte & _PAGE_HASHPTE))
+ continue;
+ hpte_need_flush(mm, start, ptep, pte, 0);
+ }
+ arch_leave_lazy_mmu_mode();
+ local_irq_restore(flags);
+}
+
+#endif /* CONFIG_HOTPLUG */
diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c
new file mode 100644
index 000000000000..803a64c02b06
--- /dev/null
+++ b/arch/powerpc/mm/tlb_nohash.c
@@ -0,0 +1,209 @@
+/*
+ * This file contains the routines for TLB flushing.
+ * On machines where the MMU does not use a hash table to store virtual to
+ * physical translations (ie, SW loaded TLBs or Book3E compilant processors,
+ * this does -not- include 603 however which shares the implementation with
+ * hash based processors)
+ *
+ * -- BenH
+ *
+ * Copyright 2008 Ben Herrenschmidt <benh@kernel.crashing.org>
+ * IBM Corp.
+ *
+ * Derived from arch/ppc/mm/init.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * This program is free software; you can redistribute 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/mm.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/preempt.h>
+#include <linux/spinlock.h>
+
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+
+#include "mmu_decl.h"
+
+/*
+ * Base TLB flushing operations:
+ *
+ * - flush_tlb_mm(mm) flushes the specified mm context TLB's
+ * - flush_tlb_page(vma, vmaddr) flushes one page
+ * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_kernel_range(start, end) flushes kernel pages
+ *
+ * - local_* variants of page and mm only apply to the current
+ * processor
+ */
+
+/*
+ * These are the base non-SMP variants of page and mm flushing
+ */
+void local_flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned int pid;
+
+ preempt_disable();
+ pid = mm->context.id;
+ if (pid != MMU_NO_CONTEXT)
+ _tlbil_pid(pid);
+ preempt_enable();
+}
+EXPORT_SYMBOL(local_flush_tlb_mm);
+
+void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
+{
+ unsigned int pid;
+
+ preempt_disable();
+ pid = vma ? vma->vm_mm->context.id : 0;
+ if (pid != MMU_NO_CONTEXT)
+ _tlbil_va(vmaddr, pid);
+ preempt_enable();
+}
+EXPORT_SYMBOL(local_flush_tlb_page);
+
+
+/*
+ * And here are the SMP non-local implementations
+ */
+#ifdef CONFIG_SMP
+
+static DEFINE_SPINLOCK(tlbivax_lock);
+
+struct tlb_flush_param {
+ unsigned long addr;
+ unsigned int pid;
+};
+
+static void do_flush_tlb_mm_ipi(void *param)
+{
+ struct tlb_flush_param *p = param;
+
+ _tlbil_pid(p ? p->pid : 0);
+}
+
+static void do_flush_tlb_page_ipi(void *param)
+{
+ struct tlb_flush_param *p = param;
+
+ _tlbil_va(p->addr, p->pid);
+}
+
+
+/* Note on invalidations and PID:
+ *
+ * We snapshot the PID with preempt disabled. At this point, it can still
+ * change either because:
+ * - our context is being stolen (PID -> NO_CONTEXT) on another CPU
+ * - we are invaliating some target that isn't currently running here
+ * and is concurrently acquiring a new PID on another CPU
+ * - some other CPU is re-acquiring a lost PID for this mm
+ * etc...
+ *
+ * However, this shouldn't be a problem as we only guarantee
+ * invalidation of TLB entries present prior to this call, so we
+ * don't care about the PID changing, and invalidating a stale PID
+ * is generally harmless.
+ */
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ cpumask_t cpu_mask;
+ unsigned int pid;
+
+ preempt_disable();
+ pid = mm->context.id;
+ if (unlikely(pid == MMU_NO_CONTEXT))
+ goto no_context;
+ cpu_mask = mm->cpu_vm_mask;
+ cpu_clear(smp_processor_id(), cpu_mask);
+ if (!cpus_empty(cpu_mask)) {
+ struct tlb_flush_param p = { .pid = pid };
+ smp_call_function_mask(cpu_mask, do_flush_tlb_mm_ipi, &p, 1);
+ }
+ _tlbil_pid(pid);
+ no_context:
+ preempt_enable();
+}
+EXPORT_SYMBOL(flush_tlb_mm);
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
+{
+ cpumask_t cpu_mask;
+ unsigned int pid;
+
+ preempt_disable();
+ pid = vma ? vma->vm_mm->context.id : 0;
+ if (unlikely(pid == MMU_NO_CONTEXT))
+ goto bail;
+ cpu_mask = vma->vm_mm->cpu_vm_mask;
+ cpu_clear(smp_processor_id(), cpu_mask);
+ if (!cpus_empty(cpu_mask)) {
+ /* If broadcast tlbivax is supported, use it */
+ if (mmu_has_feature(MMU_FTR_USE_TLBIVAX_BCAST)) {
+ int lock = mmu_has_feature(MMU_FTR_LOCK_BCAST_INVAL);
+ if (lock)
+ spin_lock(&tlbivax_lock);
+ _tlbivax_bcast(vmaddr, pid);
+ if (lock)
+ spin_unlock(&tlbivax_lock);
+ goto bail;
+ } else {
+ struct tlb_flush_param p = { .pid = pid, .addr = vmaddr };
+ smp_call_function_mask(cpu_mask,
+ do_flush_tlb_page_ipi, &p, 1);
+ }
+ }
+ _tlbil_va(vmaddr, pid);
+ bail:
+ preempt_enable();
+}
+EXPORT_SYMBOL(flush_tlb_page);
+
+#endif /* CONFIG_SMP */
+
+/*
+ * Flush kernel TLB entries in the given range
+ */
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+#ifdef CONFIG_SMP
+ preempt_disable();
+ smp_call_function(do_flush_tlb_mm_ipi, NULL, 1);
+ _tlbil_pid(0);
+ preempt_enable();
+#endif
+ _tlbil_pid(0);
+}
+EXPORT_SYMBOL(flush_tlb_kernel_range);
+
+/*
+ * Currently, for range flushing, we just do a full mm flush. This should
+ * be optimized based on a threshold on the size of the range, since
+ * some implementation can stack multiple tlbivax before a tlbsync but
+ * for now, we keep it that way
+ */
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+
+{
+ flush_tlb_mm(vma->vm_mm);
+}
+EXPORT_SYMBOL(flush_tlb_range);
diff --git a/arch/powerpc/mm/tlb_nohash_low.S b/arch/powerpc/mm/tlb_nohash_low.S
new file mode 100644
index 000000000000..f900a39e6ec4
--- /dev/null
+++ b/arch/powerpc/mm/tlb_nohash_low.S
@@ -0,0 +1,166 @@
+/*
+ * This file contains low-level functions for performing various
+ * types of TLB invalidations on various processors with no hash
+ * table.
+ *
+ * This file implements the following functions for all no-hash
+ * processors. Some aren't implemented for some variants. Some
+ * are inline in tlbflush.h
+ *
+ * - tlbil_va
+ * - tlbil_pid
+ * - tlbil_all
+ * - tlbivax_bcast (not yet)
+ *
+ * Code mostly moved over from misc_32.S
+ *
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Partially rewritten by Cort Dougan (cort@cs.nmt.edu)
+ * Paul Mackerras, Kumar Gala and Benjamin Herrenschmidt.
+ *
+ * This program is free software; you can redistribute 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 <asm/reg.h>
+#include <asm/page.h>
+#include <asm/cputable.h>
+#include <asm/mmu.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/processor.h>
+
+#if defined(CONFIG_40x)
+
+/*
+ * 40x implementation needs only tlbil_va
+ */
+_GLOBAL(_tlbil_va)
+ /* We run the search with interrupts disabled because we have to change
+ * the PID and I don't want to preempt when that happens.
+ */
+ mfmsr r5
+ mfspr r6,SPRN_PID
+ wrteei 0
+ mtspr SPRN_PID,r4
+ tlbsx. r3, 0, r3
+ mtspr SPRN_PID,r6
+ wrtee r5
+ bne 1f
+ sync
+ /* There are only 64 TLB entries, so r3 < 64, which means bit 25 is
+ * clear. Since 25 is the V bit in the TLB_TAG, loading this value
+ * will invalidate the TLB entry. */
+ tlbwe r3, r3, TLB_TAG
+ isync
+1: blr
+
+#elif defined(CONFIG_8xx)
+
+/*
+ * Nothing to do for 8xx, everything is inline
+ */
+
+#elif defined(CONFIG_44x)
+
+/*
+ * 440 implementation uses tlbsx/we for tlbil_va and a full sweep
+ * of the TLB for everything else.
+ */
+_GLOBAL(_tlbil_va)
+ mfspr r5,SPRN_MMUCR
+ rlwimi r5,r4,0,24,31 /* Set TID */
+
+ /* We have to run the search with interrupts disabled, otherwise
+ * an interrupt which causes a TLB miss can clobber the MMUCR
+ * between the mtspr and the tlbsx.
+ *
+ * Critical and Machine Check interrupts take care of saving
+ * and restoring MMUCR, so only normal interrupts have to be
+ * taken care of.
+ */
+ mfmsr r4
+ wrteei 0
+ mtspr SPRN_MMUCR,r5
+ tlbsx. r3, 0, r3
+ wrtee r4
+ bne 1f
+ sync
+ /* There are only 64 TLB entries, so r3 < 64,
+ * which means bit 22, is clear. Since 22 is
+ * the V bit in the TLB_PAGEID, loading this
+ * value will invalidate the TLB entry.
+ */
+ tlbwe r3, r3, PPC44x_TLB_PAGEID
+ isync
+1: blr
+
+_GLOBAL(_tlbil_all)
+_GLOBAL(_tlbil_pid)
+ li r3,0
+ sync
+
+ /* Load high watermark */
+ lis r4,tlb_44x_hwater@ha
+ lwz r5,tlb_44x_hwater@l(r4)
+
+1: tlbwe r3,r3,PPC44x_TLB_PAGEID
+ addi r3,r3,1
+ cmpw 0,r3,r5
+ ble 1b
+
+ isync
+ blr
+
+#elif defined(CONFIG_FSL_BOOKE)
+/*
+ * FSL BookE implementations. Currently _pid and _all are the
+ * same. This will change when tlbilx is actually supported and
+ * performs invalidate-by-PID. This change will be driven by
+ * mmu_features conditional
+ */
+
+/*
+ * Flush MMU TLB on the local processor
+ */
+_GLOBAL(_tlbil_pid)
+_GLOBAL(_tlbil_all)
+#define MMUCSR0_TLBFI (MMUCSR0_TLB0FI | MMUCSR0_TLB1FI | \
+ MMUCSR0_TLB2FI | MMUCSR0_TLB3FI)
+ li r3,(MMUCSR0_TLBFI)@l
+ mtspr SPRN_MMUCSR0, r3
+1:
+ mfspr r3,SPRN_MMUCSR0
+ andi. r3,r3,MMUCSR0_TLBFI@l
+ bne 1b
+ msync
+ isync
+ blr
+
+/*
+ * Flush MMU TLB for a particular address, but only on the local processor
+ * (no broadcast)
+ */
+_GLOBAL(_tlbil_va)
+ mfmsr r10
+ wrteei 0
+ slwi r4,r4,16
+ mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
+ tlbsx 0,r3
+ mfspr r4,SPRN_MAS1 /* check valid */
+ andis. r3,r4,MAS1_VALID@h
+ beq 1f
+ rlwinm r4,r4,0,1,31
+ mtspr SPRN_MAS1,r4
+ tlbwe
+ msync
+ isync
+1: wrtee r10
+ blr
+#elif
+#error Unsupported processor type !
+#endif
diff --git a/arch/powerpc/platforms/40x/ep405.c b/arch/powerpc/platforms/40x/ep405.c
index ae2e7f67c18e..4058fd1e7fc7 100644
--- a/arch/powerpc/platforms/40x/ep405.c
+++ b/arch/powerpc/platforms/40x/ep405.c
@@ -100,7 +100,7 @@ static void __init ep405_setup_arch(void)
/* Find & init the BCSR CPLD */
ep405_init_bcsr();
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
}
static int __init ep405_probe(void)
diff --git a/arch/powerpc/platforms/40x/kilauea.c b/arch/powerpc/platforms/40x/kilauea.c
index 1dd24ffc0dc1..fd7d934dac8b 100644
--- a/arch/powerpc/platforms/40x/kilauea.c
+++ b/arch/powerpc/platforms/40x/kilauea.c
@@ -44,7 +44,7 @@ static int __init kilauea_probe(void)
if (!of_flat_dt_is_compatible(root, "amcc,kilauea"))
return 0;
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
return 1;
}
diff --git a/arch/powerpc/platforms/40x/ppc40x_simple.c b/arch/powerpc/platforms/40x/ppc40x_simple.c
index 4498a86b46c3..f40ac9b8f99f 100644
--- a/arch/powerpc/platforms/40x/ppc40x_simple.c
+++ b/arch/powerpc/platforms/40x/ppc40x_simple.c
@@ -61,7 +61,7 @@ static int __init ppc40x_probe(void)
for (i = 0; i < ARRAY_SIZE(board); i++) {
if (of_flat_dt_is_compatible(root, board[i])) {
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
return 1;
}
}
diff --git a/arch/powerpc/platforms/44x/ebony.c b/arch/powerpc/platforms/44x/ebony.c
index a0e8fe4662f6..88b9117fa691 100644
--- a/arch/powerpc/platforms/44x/ebony.c
+++ b/arch/powerpc/platforms/44x/ebony.c
@@ -54,7 +54,7 @@ static int __init ebony_probe(void)
if (!of_flat_dt_is_compatible(root, "ibm,ebony"))
return 0;
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
return 1;
}
diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c
index 29671262801f..76fdc51dac8b 100644
--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
+++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
@@ -69,7 +69,7 @@ static int __init ppc44x_probe(void)
for (i = 0; i < ARRAY_SIZE(board); i++) {
if (of_flat_dt_is_compatible(root, board[i])) {
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
return 1;
}
}
diff --git a/arch/powerpc/platforms/44x/sam440ep.c b/arch/powerpc/platforms/44x/sam440ep.c
index 47f10e647735..a78e8eb6da41 100644
--- a/arch/powerpc/platforms/44x/sam440ep.c
+++ b/arch/powerpc/platforms/44x/sam440ep.c
@@ -51,7 +51,7 @@ static int __init sam440ep_probe(void)
if (!of_flat_dt_is_compatible(root, "acube,sam440ep"))
return 0;
- ppc_pci_flags = PPC_PCI_REASSIGN_ALL_RSRC;
+ ppc_pci_set_flags(PPC_PCI_REASSIGN_ALL_RSRC);
return 1;
}
diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c
index fe92e65103ed..b5c753db125e 100644
--- a/arch/powerpc/platforms/52xx/lite5200_pm.c
+++ b/arch/powerpc/platforms/52xx/lite5200_pm.c
@@ -3,7 +3,6 @@
#include <asm/io.h>
#include <asm/time.h>
#include <asm/mpc52xx.h>
-#include "mpc52xx_pic.h"
/* defined in lite5200_sleep.S and only used here */
extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar);
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
index b49a18527661..c3f2c21024e3 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pci.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c
@@ -375,7 +375,7 @@ mpc52xx_add_bridge(struct device_node *node)
pr_debug("Adding MPC52xx PCI host bridge %s\n", node->full_name);
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
if (of_address_to_resource(node, 0, &rsrc) != 0) {
printk(KERN_ERR "Can't get %s resources\n", node->full_name);
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
index 8479394e9ab4..72865e8e4b51 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
@@ -2,20 +2,100 @@
*
* Programmable Interrupt Controller functions for the Freescale MPC52xx.
*
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
* Copyright (C) 2006 bplan GmbH
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 Montavista Software, Inc
*
* Based on the code from the 2.4 kernel by
* Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
*
- * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2003 Montavista Software, Inc
- *
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
*/
+/*
+ * This is the device driver for the MPC5200 interrupt controller.
+ *
+ * hardware overview
+ * -----------------
+ * The MPC5200 interrupt controller groups the all interrupt sources into
+ * three groups called 'critical', 'main', and 'peripheral'. The critical
+ * group has 3 irqs, External IRQ0, slice timer 0 irq, and wake from deep
+ * sleep. Main group include the other 3 external IRQs, slice timer 1, RTC,
+ * gpios, and the general purpose timers. Peripheral group contains the
+ * remaining irq sources from all of the on-chip peripherals (PSCs, Ethernet,
+ * USB, DMA, etc).
+ *
+ * virqs
+ * -----
+ * The Linux IRQ subsystem requires that each irq source be assigned a
+ * system wide unique IRQ number starting at 1 (0 means no irq). Since
+ * systems can have multiple interrupt controllers, the virtual IRQ (virq)
+ * infrastructure lets each interrupt controller to define a local set
+ * of IRQ numbers and the virq infrastructure maps those numbers into
+ * a unique range of the global IRQ# space.
+ *
+ * To define a range of virq numbers for this controller, this driver first
+ * assigns a number to each of the irq groups (called the level 1 or L1
+ * value). Within each group individual irq sources are also assigned a
+ * number, as defined by the MPC5200 user guide, and refers to it as the
+ * level 2 or L2 value. The virq number is determined by shifting up the
+ * L1 value by MPC52xx_IRQ_L1_OFFSET and ORing it with the L2 value.
+ *
+ * For example, the TMR0 interrupt is irq 9 in the main group. The
+ * virq for TMR0 is calculated by ((1 << MPC52xx_IRQ_L1_OFFSET) | 9).
+ *
+ * The observant reader will also notice that this driver defines a 4th
+ * interrupt group called 'bestcomm'. The bestcomm group isn't physically
+ * part of the MPC5200 interrupt controller, but it is used here to assign
+ * a separate virq number for each bestcomm task (since any of the 16
+ * bestcomm tasks can cause the bestcomm interrupt to be raised). When a
+ * bestcomm interrupt occurs (peripheral group, irq 0) this driver determines
+ * which task needs servicing and returns the irq number for that task. This
+ * allows drivers which use bestcomm to define their own interrupt handlers.
+ *
+ * irq_chip structures
+ * -------------------
+ * For actually manipulating IRQs (masking, enabling, clearing, etc) this
+ * driver defines four separate 'irq_chip' structures, one for the main
+ * group, one for the peripherals group, one for the bestcomm group and one
+ * for external interrupts. The irq_chip structures provide the hooks needed
+ * to manipulate each IRQ source, and since each group is has a separate set
+ * of registers for controlling the irq, it makes sense to divide up the
+ * hooks along those lines.
+ *
+ * You'll notice that there is not an irq_chip for the critical group and
+ * you'll also notice that there is an irq_chip defined for external
+ * interrupts even though there is no external interrupt group. The reason
+ * for this is that the four external interrupts are all managed with the same
+ * register even though one of the external IRQs is in the critical group and
+ * the other three are in the main group. For this reason it makes sense for
+ * the 4 external irqs to be managed using a separate set of hooks. The
+ * reason there is no crit irq_chip is that of the 3 irqs in the critical
+ * group, only external interrupt is actually support at this time by this
+ * driver and since external interrupt is the only one used, it can just
+ * be directed to make use of the external irq irq_chip.
+ *
+ * device tree bindings
+ * --------------------
+ * The device tree bindings for this controller reflect the two level
+ * organization of irqs in the device. #interrupt-cells = <3> where the
+ * first cell is the group number [0..3], the second cell is the irq
+ * number in the group, and the third cell is the sense type (level/edge).
+ * For reference, the following is a list of the interrupt property values
+ * associated with external interrupt sources on the MPC5200 (just because
+ * it is non-obvious to determine what the interrupts property should be
+ * when reading the mpc5200 manual and it is a frequently asked question).
+ *
+ * External interrupts:
+ * <0 0 n> external irq0, n is sense (n=0: level high,
+ * <1 1 n> external irq1, n is sense n=1: edge rising,
+ * <1 2 n> external irq2, n is sense n=2: edge falling,
+ * <1 3 n> external irq3, n is sense n=3: level low)
+ */
#undef DEBUG
#include <linux/interrupt.h>
@@ -24,11 +104,19 @@
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/mpc52xx.h>
-#include "mpc52xx_pic.h"
-/*
- *
-*/
+/* HW IRQ mapping */
+#define MPC52xx_IRQ_L1_CRIT (0)
+#define MPC52xx_IRQ_L1_MAIN (1)
+#define MPC52xx_IRQ_L1_PERP (2)
+#define MPC52xx_IRQ_L1_SDMA (3)
+
+#define MPC52xx_IRQ_L1_OFFSET (6)
+#define MPC52xx_IRQ_L1_MASK (0x00c0)
+#define MPC52xx_IRQ_L2_MASK (0x003f)
+
+#define MPC52xx_IRQ_HIGHTESTHWIRQ (0xd0)
+
/* MPC5200 device tree match tables */
static struct of_device_id mpc52xx_pic_ids[] __initdata = {
@@ -53,10 +141,7 @@ static unsigned char mpc52xx_map_senses[4] = {
IRQ_TYPE_LEVEL_LOW,
};
-/*
- *
-*/
-
+/* Utility functions */
static inline void io_be_setbit(u32 __iomem *addr, int bitno)
{
out_be32(addr, in_be32(addr) | (1 << bitno));
@@ -69,15 +154,14 @@ static inline void io_be_clrbit(u32 __iomem *addr, int bitno)
/*
* IRQ[0-3] interrupt irq_chip
-*/
-
+ */
static void mpc52xx_extirq_mask(unsigned int virq)
{
int irq;
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -90,7 +174,7 @@ static void mpc52xx_extirq_unmask(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -103,7 +187,7 @@ static void mpc52xx_extirq_ack(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -117,7 +201,7 @@ static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type);
@@ -156,15 +240,14 @@ static struct irq_chip mpc52xx_extirq_irqchip = {
/*
* Main interrupt irq_chip
-*/
-
+ */
static void mpc52xx_main_mask(unsigned int virq)
{
int irq;
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -177,7 +260,7 @@ static void mpc52xx_main_unmask(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -193,15 +276,14 @@ static struct irq_chip mpc52xx_main_irqchip = {
/*
* Peripherals interrupt irq_chip
-*/
-
+ */
static void mpc52xx_periph_mask(unsigned int virq)
{
int irq;
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -214,7 +296,7 @@ static void mpc52xx_periph_unmask(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -230,15 +312,14 @@ static struct irq_chip mpc52xx_periph_irqchip = {
/*
* SDMA interrupt irq_chip
-*/
-
+ */
static void mpc52xx_sdma_mask(unsigned int virq)
{
int irq;
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -251,7 +332,7 @@ static void mpc52xx_sdma_unmask(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -264,7 +345,7 @@ static void mpc52xx_sdma_ack(unsigned int virq)
int l2irq;
irq = irq_map[virq].hwirq;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
pr_debug("%s: irq=%x. l2=%d\n", __func__, irq, l2irq);
@@ -278,13 +359,12 @@ static struct irq_chip mpc52xx_sdma_irqchip = {
.ack = mpc52xx_sdma_ack,
};
-/*
- * irq_host
-*/
-
+/**
+ * mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property
+ */
static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
- u32 * intspec, unsigned int intsize,
- irq_hw_number_t * out_hwirq,
+ u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
unsigned int *out_flags)
{
int intrvect_l1;
@@ -299,10 +379,9 @@ static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
intrvect_l2 = (int)intspec[1];
intrvect_type = (int)intspec[2];
- intrvect_linux =
- (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) & MPC52xx_IRQ_L1_MASK;
- intrvect_linux |=
- (intrvect_l2 << MPC52xx_IRQ_L2_OFFSET) & MPC52xx_IRQ_L2_MASK;
+ intrvect_linux = (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) &
+ MPC52xx_IRQ_L1_MASK;
+ intrvect_linux |= intrvect_l2 & MPC52xx_IRQ_L2_MASK;
pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1,
intrvect_l2);
@@ -313,11 +392,11 @@ static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct,
return 0;
}
-/*
- * this function retrieves the correct IRQ type out
- * of the MPC regs
- * Only externals IRQs needs this
-*/
+/**
+ * mpc52xx_irqx_gettype - determine the IRQ sense type (level/edge)
+ *
+ * Only external IRQs need this.
+ */
static int mpc52xx_irqx_gettype(int irq)
{
int type;
@@ -329,6 +408,9 @@ static int mpc52xx_irqx_gettype(int irq)
return mpc52xx_map_senses[type];
}
+/**
+ * mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure
+ */
static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t irq)
{
@@ -339,7 +421,7 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
int type;
l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET;
- l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET;
+ l2irq = irq & MPC52xx_IRQ_L2_MASK;
/*
* Most of ours IRQs will be level low
@@ -379,8 +461,7 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
break;
default:
- pr_debug("%s: Error, unknown L1 IRQ (0x%x)\n", __func__, l1irq);
- printk(KERN_ERR "Unknow IRQ!\n");
+ pr_err("%s: invalid virq requested (0x%x)\n", __func__, virq);
return -EINVAL;
}
@@ -406,10 +487,15 @@ static struct irq_host_ops mpc52xx_irqhost_ops = {
.map = mpc52xx_irqhost_map,
};
-/*
- * init (public)
-*/
-
+/**
+ * mpc52xx_init_irq - Initialize and register with the virq subsystem
+ *
+ * Hook for setting up IRQs on an mpc5200 system. A pointer to this function
+ * is to be put into the machine definition structure.
+ *
+ * This function searches the device tree for an MPC5200 interrupt controller,
+ * initializes it, and registers it with the virq subsystem.
+ */
void __init mpc52xx_init_irq(void)
{
u32 intr_ctrl;
@@ -454,7 +540,6 @@ void __init mpc52xx_init_irq(void)
* As last step, add an irq host to translate the real
* hw irq information provided by the ofw to linux virq
*/
-
mpc52xx_irqhost = irq_alloc_host(picnode, IRQ_HOST_MAP_LINEAR,
MPC52xx_IRQ_HIGHTESTHWIRQ,
&mpc52xx_irqhost_ops, -1);
@@ -462,12 +547,38 @@ void __init mpc52xx_init_irq(void)
if (!mpc52xx_irqhost)
panic(__FILE__ ": Cannot allocate the IRQ host\n");
- printk(KERN_INFO "MPC52xx PIC is up and running!\n");
+ irq_set_default_host(mpc52xx_irqhost);
+
+ pr_info("MPC52xx PIC is up and running!\n");
}
-/*
- * get_irq (public)
-*/
+/**
+ * mpc52xx_get_irq - Get pending interrupt number hook function
+ *
+ * Called by the interupt handler to determine what IRQ handler needs to be
+ * executed.
+ *
+ * Status of pending interrupts is determined by reading the encoded status
+ * register. The encoded status register has three fields; one for each of the
+ * types of interrupts defined by the controller - 'critical', 'main' and
+ * 'peripheral'. This function reads the status register and returns the IRQ
+ * number associated with the highest priority pending interrupt. 'Critical'
+ * interrupts have the highest priority, followed by 'main' interrupts, and
+ * then 'peripheral'.
+ *
+ * The mpc5200 interrupt controller can be configured to boost the priority
+ * of individual 'peripheral' interrupts. If this is the case then a special
+ * value will appear in either the crit or main fields indicating a high
+ * or medium priority peripheral irq has occurred.
+ *
+ * This function checks each of the 3 irq request fields and returns the
+ * first pending interrupt that it finds.
+ *
+ * This function also identifies a 4th type of interrupt; 'bestcomm'. Each
+ * bestcomm DMA task can raise the bestcomm peripheral interrupt. When this
+ * occurs at task-specific IRQ# is decoded so that each task can have its
+ * own IRQ handler.
+ */
unsigned int mpc52xx_get_irq(void)
{
u32 status;
@@ -478,25 +589,21 @@ unsigned int mpc52xx_get_irq(void)
irq = (status >> 8) & 0x3;
if (irq == 2) /* high priority peripheral */
goto peripheral;
- irq |= (MPC52xx_IRQ_L1_CRIT << MPC52xx_IRQ_L1_OFFSET) &
- MPC52xx_IRQ_L1_MASK;
+ irq |= (MPC52xx_IRQ_L1_CRIT << MPC52xx_IRQ_L1_OFFSET);
} else if (status & 0x00200000) { /* main */
irq = (status >> 16) & 0x1f;
if (irq == 4) /* low priority peripheral */
goto peripheral;
- irq |= (MPC52xx_IRQ_L1_MAIN << MPC52xx_IRQ_L1_OFFSET) &
- MPC52xx_IRQ_L1_MASK;
+ irq |= (MPC52xx_IRQ_L1_MAIN << MPC52xx_IRQ_L1_OFFSET);
} else if (status & 0x20000000) { /* peripheral */
peripheral:
irq = (status >> 24) & 0x1f;
if (irq == 0) { /* bestcomm */
status = in_be32(&sdma->IntPend);
irq = ffs(status) - 1;
- irq |= (MPC52xx_IRQ_L1_SDMA << MPC52xx_IRQ_L1_OFFSET) &
- MPC52xx_IRQ_L1_MASK;
+ irq |= (MPC52xx_IRQ_L1_SDMA << MPC52xx_IRQ_L1_OFFSET);
} else {
- irq |= (MPC52xx_IRQ_L1_PERP << MPC52xx_IRQ_L1_OFFSET) &
- MPC52xx_IRQ_L1_MASK;
+ irq |= (MPC52xx_IRQ_L1_PERP << MPC52xx_IRQ_L1_OFFSET);
}
}
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.h b/arch/powerpc/platforms/52xx/mpc52xx_pic.h
deleted file mode 100644
index 1a26bcdb3049..000000000000
--- a/arch/powerpc/platforms/52xx/mpc52xx_pic.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Header file for Freescale MPC52xx Interrupt controller
- *
- * Copyright (C) 2004-2005 Sylvain Munaut <tnt@246tNt.com>
- * Copyright (C) 2003 MontaVista, Software, Inc.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#ifndef __POWERPC_SYSDEV_MPC52xx_PIC_H__
-#define __POWERPC_SYSDEV_MPC52xx_PIC_H__
-
-#include <asm/types.h>
-
-
-/* HW IRQ mapping */
-#define MPC52xx_IRQ_L1_CRIT (0)
-#define MPC52xx_IRQ_L1_MAIN (1)
-#define MPC52xx_IRQ_L1_PERP (2)
-#define MPC52xx_IRQ_L1_SDMA (3)
-
-#define MPC52xx_IRQ_L1_OFFSET (6)
-#define MPC52xx_IRQ_L1_MASK (0x00c0)
-
-#define MPC52xx_IRQ_L2_OFFSET (0)
-#define MPC52xx_IRQ_L2_MASK (0x003f)
-
-#define MPC52xx_IRQ_HIGHTESTHWIRQ (0xd0)
-
-
-/* Interrupt controller Register set */
-struct mpc52xx_intr {
- u32 per_mask; /* INTR + 0x00 */
- u32 per_pri1; /* INTR + 0x04 */
- u32 per_pri2; /* INTR + 0x08 */
- u32 per_pri3; /* INTR + 0x0c */
- u32 ctrl; /* INTR + 0x10 */
- u32 main_mask; /* INTR + 0x14 */
- u32 main_pri1; /* INTR + 0x18 */
- u32 main_pri2; /* INTR + 0x1c */
- u32 reserved1; /* INTR + 0x20 */
- u32 enc_status; /* INTR + 0x24 */
- u32 crit_status; /* INTR + 0x28 */
- u32 main_status; /* INTR + 0x2c */
- u32 per_status; /* INTR + 0x30 */
- u32 reserved2; /* INTR + 0x34 */
- u32 per_error; /* INTR + 0x38 */
-};
-
-#endif /* __POWERPC_SYSDEV_MPC52xx_PIC_H__ */
-
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
index c72d3304387f..a55b0b6813ed 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pm.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c
@@ -5,9 +5,6 @@
#include <asm/cacheflush.h>
#include <asm/mpc52xx.h>
-#include "mpc52xx_pic.h"
-
-
/* these are defined in mpc52xx_sleep.S, and only used here */
extern void mpc52xx_deep_sleep(void __iomem *sram, void __iomem *sdram_regs,
struct mpc52xx_cdm __iomem *, struct mpc52xx_intr __iomem*);
diff --git a/arch/powerpc/platforms/82xx/pq2.c b/arch/powerpc/platforms/82xx/pq2.c
index 1b75902fad64..9761a59f175f 100644
--- a/arch/powerpc/platforms/82xx/pq2.c
+++ b/arch/powerpc/platforms/82xx/pq2.c
@@ -53,7 +53,7 @@ static void __init pq2_pci_add_bridge(struct device_node *np)
if (of_address_to_resource(np, 0, &r) || r.end - r.start < 0x10b)
goto err;
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
hose = pcibios_alloc_controller(np);
if (!hose)
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index cb3054e1001d..f0798c09980f 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -1,6 +1,8 @@
#
# Makefile for the PowerPC 85xx linux kernel.
#
+obj-$(CONFIG_SMP) += smp.o
+
obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o
obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o
obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c
index 613bf8c2e30d..a8301c8ad537 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c
@@ -63,6 +63,7 @@ void __init mpc85xx_ds_pic_init(void)
struct device_node *cascade_node = NULL;
int cascade_irq;
#endif
+ unsigned long root = of_get_flat_dt_root();
np = of_find_node_by_type(NULL, "open-pic");
if (np == NULL) {
@@ -76,11 +77,19 @@ void __init mpc85xx_ds_pic_init(void)
return;
}
- mpic = mpic_alloc(np, r.start,
+ if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) {
+ mpic = mpic_alloc(np, r.start,
+ MPIC_PRIMARY |
+ MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS,
+ 0, 256, " OpenPIC ");
+ } else {
+ mpic = mpic_alloc(np, r.start,
MPIC_PRIMARY | MPIC_WANTS_RESET |
MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS |
MPIC_SINGLE_DEST_CPU,
0, 256, " OpenPIC ");
+ }
+
BUG_ON(mpic == NULL);
of_node_put(np);
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
index 2494c5155919..658a36fab3ab 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
@@ -231,7 +231,7 @@ static void __init mpc85xx_mds_setup_arch(void)
static int __init board_fixups(void)
{
- char phy_id[BUS_ID_SIZE];
+ char phy_id[20];
char *compstrs[2] = {"fsl,gianfar-mdio", "fsl,ucc-mdio"};
struct device_node *mdio;
struct resource res;
@@ -241,13 +241,15 @@ static int __init board_fixups(void)
mdio = of_find_compatible_node(NULL, NULL, compstrs[i]);
of_address_to_resource(mdio, 0, &res);
- snprintf(phy_id, BUS_ID_SIZE, "%x:%02x", res.start, 1);
+ snprintf(phy_id, sizeof(phy_id), "%llx:%02x",
+ (unsigned long long)res.start, 1);
phy_register_fixup_for_id(phy_id, mpc8568_fixup_125_clock);
phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups);
/* Register a workaround for errata */
- snprintf(phy_id, BUS_ID_SIZE, "%x:%02x", res.start, 7);
+ snprintf(phy_id, sizeof(phy_id), "%llx:%02x",
+ (unsigned long long)res.start, 7);
phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups);
of_node_put(mdio);
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
new file mode 100644
index 000000000000..d652c713f496
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -0,0 +1,104 @@
+/*
+ * Author: Andy Fleming <afleming@freescale.com>
+ * Kumar Gala <galak@kernel.crashing.org>
+ *
+ * Copyright 2006-2008 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#include <asm/machdep.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/mpic.h>
+#include <asm/cacheflush.h>
+
+#include <sysdev/fsl_soc.h>
+
+extern volatile unsigned long __secondary_hold_acknowledge;
+extern void __early_start(void);
+
+#define BOOT_ENTRY_ADDR_UPPER 0
+#define BOOT_ENTRY_ADDR_LOWER 1
+#define BOOT_ENTRY_R3_UPPER 2
+#define BOOT_ENTRY_R3_LOWER 3
+#define BOOT_ENTRY_RESV 4
+#define BOOT_ENTRY_PIR 5
+#define BOOT_ENTRY_R6_UPPER 6
+#define BOOT_ENTRY_R6_LOWER 7
+#define NUM_BOOT_ENTRY 8
+#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32))
+
+static void __init
+smp_85xx_kick_cpu(int nr)
+{
+ unsigned long flags;
+ const u64 *cpu_rel_addr;
+ __iomem u32 *bptr_vaddr;
+ struct device_node *np;
+ int n = 0;
+
+ WARN_ON (nr < 0 || nr >= NR_CPUS);
+
+ pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);
+
+ local_irq_save(flags);
+
+ np = of_get_cpu_node(nr, NULL);
+ cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL);
+
+ if (cpu_rel_addr == NULL) {
+ printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr);
+ return;
+ }
+
+ /* Map the spin table */
+ bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY);
+
+ out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
+ out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
+
+ /* Wait a bit for the CPU to ack. */
+ while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
+ mdelay(1);
+
+ iounmap(bptr_vaddr);
+
+ local_irq_restore(flags);
+
+ pr_debug("waited %d msecs for CPU #%d.\n", n, nr);
+}
+
+static void __init
+smp_85xx_setup_cpu(int cpu_nr)
+{
+ mpic_setup_this_cpu();
+
+ /* Clear any pending timer interrupts */
+ mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
+
+ /* Enable decrementer interrupt */
+ mtspr(SPRN_TCR, TCR_DIE);
+}
+
+struct smp_ops_t smp_85xx_ops = {
+ .message_pass = smp_mpic_message_pass,
+ .probe = smp_mpic_probe,
+ .kick_cpu = smp_85xx_kick_cpu,
+ .setup_cpu = smp_85xx_setup_cpu,
+};
+
+void __init
+mpc85xx_smp_init(void)
+{
+ smp_ops = &smp_85xx_ops;
+}
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig
index 77dd797a2580..8e5693935975 100644
--- a/arch/powerpc/platforms/86xx/Kconfig
+++ b/arch/powerpc/platforms/86xx/Kconfig
@@ -34,6 +34,8 @@ config MPC8610_HPCD
config GEF_SBC610
bool "GE Fanuc SBC610"
select DEFAULT_UIMAGE
+ select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
select HAS_RAPIDIO
help
This option enables support for GE Fanuc's SBC610.
diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile
index 4a56ff619afd..31e540c2ebbc 100644
--- a/arch/powerpc/platforms/86xx/Makefile
+++ b/arch/powerpc/platforms/86xx/Makefile
@@ -7,4 +7,5 @@ obj-$(CONFIG_SMP) += mpc86xx_smp.o
obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o
obj-$(CONFIG_SBC8641D) += sbc8641d.o
obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o
-obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o
+gef-gpio-$(CONFIG_GPIOLIB) += gef_gpio.o
+obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o $(gef-gpio-y)
diff --git a/arch/powerpc/platforms/86xx/gef_gpio.c b/arch/powerpc/platforms/86xx/gef_gpio.c
new file mode 100644
index 000000000000..85b2800f4cb7
--- /dev/null
+++ b/arch/powerpc/platforms/86xx/gef_gpio.c
@@ -0,0 +1,143 @@
+/*
+ * Driver for GE Fanuc's FPGA based GPIO pins
+ *
+ * Author: Martyn Welch <martyn.welch@gefanuc.com>
+ *
+ * 2008 (c) GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+/* TODO
+ *
+ * Configuration of output modes (totem-pole/open-drain)
+ * Interrupt configuration - interrupts are always generated the FPGA relies on
+ * the I/O interrupt controllers mask to stop them propergating
+ */
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+
+#define GEF_GPIO_DIRECT 0x00
+#define GEF_GPIO_IN 0x04
+#define GEF_GPIO_OUT 0x08
+#define GEF_GPIO_TRIG 0x0C
+#define GEF_GPIO_POLAR_A 0x10
+#define GEF_GPIO_POLAR_B 0x14
+#define GEF_GPIO_INT_STAT 0x18
+#define GEF_GPIO_OVERRUN 0x1C
+#define GEF_GPIO_MODE 0x20
+
+#define NUM_GPIO 19
+
+static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value)
+{
+ unsigned int data;
+
+ data = ioread32be(reg);
+ /* value: 0=low; 1=high */
+ if (value & 0x1)
+ data = data | (0x1 << offset);
+ else
+ data = data & ~(0x1 << offset);
+
+ iowrite32be(data, reg);
+}
+
+
+static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned int data;
+ struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+ data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT);
+ data = data | (0x1 << offset);
+ iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT);
+
+ return 0;
+}
+
+static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ unsigned int data;
+ struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+ /* Set direction before switching to input */
+ _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value);
+
+ data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT);
+ data = data & ~(0x1 << offset);
+ iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT);
+
+ return 0;
+}
+
+static int gef_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ unsigned int data;
+ int state = 0;
+ struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+ data = ioread32be(mmchip->regs + GEF_GPIO_IN);
+ state = (int)((data >> offset) & 0x1);
+
+ return state;
+}
+
+static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip);
+
+ _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value);
+}
+
+static int __init gef_gpio_init(void)
+{
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "gef,sbc610-gpio") {
+ int retval;
+ struct of_mm_gpio_chip *gef_gpio_chip;
+
+ pr_debug("%s: Initialising GEF GPIO\n", np->full_name);
+
+ /* Allocate chip structure */
+ gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL);
+ if (!gef_gpio_chip) {
+ pr_err("%s: Unable to allocate structure\n",
+ np->full_name);
+ continue;
+ }
+
+ /* Setup pointers to chip functions */
+ gef_gpio_chip->of_gc.gpio_cells = 2;
+ gef_gpio_chip->of_gc.gc.ngpio = NUM_GPIO;
+ gef_gpio_chip->of_gc.gc.direction_input = gef_gpio_dir_in;
+ gef_gpio_chip->of_gc.gc.direction_output = gef_gpio_dir_out;
+ gef_gpio_chip->of_gc.gc.get = gef_gpio_get;
+ gef_gpio_chip->of_gc.gc.set = gef_gpio_set;
+
+ /* This function adds a memory mapped GPIO chip */
+ retval = of_mm_gpiochip_add(np, gef_gpio_chip);
+ if (retval) {
+ kfree(gef_gpio_chip);
+ pr_err("%s: Unable to add GPIO\n", np->full_name);
+ }
+ }
+
+ return 0;
+};
+arch_initcall(gef_gpio_init);
+
+MODULE_DESCRIPTION("GE Fanuc I/O FPGA GPIO driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com");
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 548efa55c8fe..3d0c776f888d 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -195,16 +195,24 @@ config SPE
config PPC_STD_MMU
bool
- depends on 6xx || POWER3 || POWER4 || PPC64
+ depends on 6xx || PPC64
default y
config PPC_STD_MMU_32
def_bool y
depends on PPC_STD_MMU && PPC32
+config PPC_STD_MMU_64
+ def_bool y
+ depends on PPC_STD_MMU && PPC64
+
+config PPC_MMU_NOHASH
+ def_bool y
+ depends on !PPC_STD_MMU
+
config PPC_MM_SLICES
bool
- default y if HUGETLB_PAGE || PPC_64K_PAGES
+ default y if HUGETLB_PAGE || (PPC_STD_MMU_64 && PPC_64K_PAGES)
default n
config VIRT_CPU_ACCOUNTING
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index c14d7d8d96c8..5cc3279559a4 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -2,13 +2,18 @@ config PPC_CELL
bool
default n
-config PPC_CELL_NATIVE
+config PPC_CELL_COMMON
bool
select PPC_CELL
select PPC_DCR_MMIO
- select PPC_OF_PLATFORM_PCI
select PPC_INDIRECT_IO
select PPC_NATIVE
+ select PPC_RTAS
+
+config PPC_CELL_NATIVE
+ bool
+ select PPC_CELL_COMMON
+ select PPC_OF_PLATFORM_PCI
select MPIC
select IBM_NEW_EMAC_EMAC4
select IBM_NEW_EMAC_RGMII
@@ -20,7 +25,6 @@ config PPC_IBM_CELL_BLADE
bool "IBM Cell Blade"
depends on PPC_MULTIPLATFORM && PPC64
select PPC_CELL_NATIVE
- select PPC_RTAS
select MMIO_NVRAM
select PPC_UDBG_16550
select UDBG_RTAS_CONSOLE
@@ -28,16 +32,17 @@ config PPC_IBM_CELL_BLADE
config PPC_CELLEB
bool "Toshiba's Cell Reference Set 'Celleb' Architecture"
depends on PPC_MULTIPLATFORM && PPC64
- select PPC_CELL
select PPC_CELL_NATIVE
- select PPC_RTAS
- select PPC_INDIRECT_IO
- select PPC_OF_PLATFORM_PCI
select HAS_TXX9_SERIAL
select PPC_UDBG_BEAT
select USB_OHCI_BIG_ENDIAN_MMIO
select USB_EHCI_BIG_ENDIAN_MMIO
+config PPC_CELL_QPACE
+ bool "IBM Cell - QPACE"
+ depends on PPC_MULTIPLATFORM && PPC64
+ select PPC_CELL_COMMON
+
menu "Cell Broadband Engine options"
depends on PPC_CELL
@@ -102,7 +107,7 @@ config PPC_IBM_CELL_POWERBUTTON
config CBE_THERM
tristate "CBE thermal support"
default m
- depends on CBE_RAS
+ depends on CBE_RAS && SPU_BASE
config CBE_CPUFREQ
tristate "CBE frequency scaling"
@@ -136,5 +141,5 @@ endmenu
config OPROFILE_CELL
def_bool y
- depends on PPC_CELL_NATIVE && (OPROFILE = m || OPROFILE = y)
+ depends on PPC_CELL_NATIVE && (OPROFILE = m || OPROFILE = y) && SPU_BASE
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index 7fd830872c43..43eccb270301 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -1,7 +1,7 @@
-obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \
- cbe_regs.o spider-pic.o \
- pervasive.o pmu.o io-workarounds.o \
- spider-pci.o
+obj-$(CONFIG_PPC_CELL_COMMON) += cbe_regs.o interrupt.o pervasive.o
+
+obj-$(CONFIG_PPC_CELL_NATIVE) += iommu.o setup.o spider-pic.o \
+ pmu.o io-workarounds.o spider-pci.o
obj-$(CONFIG_CBE_RAS) += ras.o
obj-$(CONFIG_CBE_THERM) += cbe_thermal.o
@@ -14,13 +14,12 @@ obj-$(CONFIG_PPC_IBM_CELL_POWERBUTTON) += cbe_powerbutton.o
ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o
+obj-$(CONFIG_PPC_CELL_QPACE) += smp.o
endif
# needed only when building loadable spufs.ko
-spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o
-
-spu-manage-$(CONFIG_PPC_CELLEB) += spu_manage.o
-spu-manage-$(CONFIG_PPC_CELL_NATIVE) += spu_manage.o
+spu-priv1-$(CONFIG_PPC_CELL_COMMON) += spu_priv1_mmio.o
+spu-manage-$(CONFIG_PPC_CELL_COMMON) += spu_manage.o
obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
spu_notify.o \
@@ -31,6 +30,8 @@ obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
obj-$(CONFIG_PCI_MSI) += axon_msi.o
+# qpace setup
+obj-$(CONFIG_PPC_CELL_QPACE) += qpace_setup.o
# celleb stuff
ifeq ($(CONFIG_PPC_CELLEB),y)
diff --git a/arch/powerpc/platforms/cell/celleb_setup.c b/arch/powerpc/platforms/cell/celleb_setup.c
index b11cb30decb2..07c234f6b2b6 100644
--- a/arch/powerpc/platforms/cell/celleb_setup.c
+++ b/arch/powerpc/platforms/cell/celleb_setup.c
@@ -45,7 +45,6 @@
#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/io.h>
-#include <asm/kexec.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/cputable.h>
@@ -226,9 +225,6 @@ define_machine(celleb_beat) {
.pci_setup_phb = celleb_setup_phb,
#ifdef CONFIG_KEXEC
.kexec_cpu_down = beat_kexec_cpu_down,
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
#endif
};
@@ -248,9 +244,4 @@ define_machine(celleb_native) {
.pci_probe_mode = celleb_pci_probe_mode,
.pci_setup_phb = celleb_setup_phb,
.init_IRQ = celleb_init_IRQ_native,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
};
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 3168272ab0d7..86db4dd170a0 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -1053,10 +1053,7 @@ static int __init cell_iommu_fixed_mapping_init(void)
}
/* We must have dma-ranges properties for fixed mapping to work */
- for (np = NULL; (np = of_find_all_nodes(np));) {
- if (of_find_property(np, "dma-ranges", NULL))
- break;
- }
+ np = of_find_node_with_property(NULL, "dma-ranges");
of_node_put(np);
if (!np) {
diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c
new file mode 100644
index 000000000000..be84e6a16b30
--- /dev/null
+++ b/arch/powerpc/platforms/cell/qpace_setup.c
@@ -0,0 +1,152 @@
+/*
+ * linux/arch/powerpc/platforms/cell/qpace_setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified by PPC64 Team, IBM Corp
+ * Modified by Cell Team, IBM Deutschland Entwicklung GmbH
+ * Modified by Benjamin Krill <ben@codiert.org>, IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/console.h>
+#include <linux/of_platform.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/kexec.h>
+#include <asm/pgtable.h>
+#include <asm/prom.h>
+#include <asm/rtas.h>
+#include <asm/dma.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/cputable.h>
+#include <asm/irq.h>
+#include <asm/spu.h>
+#include <asm/spu_priv1.h>
+#include <asm/udbg.h>
+#include <asm/cell-regs.h>
+
+#include "interrupt.h"
+#include "pervasive.h"
+#include "ras.h"
+#include "io-workarounds.h"
+
+static void qpace_show_cpuinfo(struct seq_file *m)
+{
+ struct device_node *root;
+ const char *model = "";
+
+ root = of_find_node_by_path("/");
+ if (root)
+ model = of_get_property(root, "model", NULL);
+ seq_printf(m, "machine\t\t: CHRP %s\n", model);
+ of_node_put(root);
+}
+
+static void qpace_progress(char *s, unsigned short hex)
+{
+ printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+static int __init qpace_publish_devices(void)
+{
+ int node;
+
+ /* Publish OF platform devices for southbridge IOs */
+ of_platform_bus_probe(NULL, NULL, NULL);
+
+ /* There is no device for the MIC memory controller, thus we create
+ * a platform device for it to attach the EDAC driver to.
+ */
+ for_each_online_node(node) {
+ if (cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(node)) == NULL)
+ continue;
+ platform_device_register_simple("cbe-mic", node, NULL, 0);
+ }
+
+ return 0;
+}
+machine_subsys_initcall(qpace, qpace_publish_devices);
+
+extern int qpace_notify(struct device *dev)
+{
+ /* set dma_ops for of_platform bus */
+ if (dev->bus && dev->bus->name
+ && !strcmp(dev->bus->name, "of_platform"))
+ set_dma_ops(dev, &dma_direct_ops);
+
+ return 0;
+}
+
+static void __init qpace_setup_arch(void)
+{
+#ifdef CONFIG_SPU_BASE
+ spu_priv1_ops = &spu_priv1_mmio_ops;
+ spu_management_ops = &spu_management_of_ops;
+#endif
+
+ cbe_regs_init();
+
+#ifdef CONFIG_CBE_RAS
+ cbe_ras_init();
+#endif
+
+#ifdef CONFIG_SMP
+ smp_init_cell();
+#endif
+
+ /* init to some ~sane value until calibrate_delay() runs */
+ loops_per_jiffy = 50000000;
+
+ cbe_pervasive_init();
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+
+ /* set notifier function */
+ platform_notify = &qpace_notify;
+}
+
+static int __init qpace_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (!of_flat_dt_is_compatible(root, "IBM,QPACE"))
+ return 0;
+
+ hpte_init_native();
+
+ return 1;
+}
+
+define_machine(qpace) {
+ .name = "QPACE",
+ .probe = qpace_probe,
+ .setup_arch = qpace_setup_arch,
+ .show_cpuinfo = qpace_show_cpuinfo,
+ .restart = rtas_restart,
+ .power_off = rtas_power_off,
+ .halt = rtas_halt,
+ .get_boot_time = rtas_get_boot_time,
+ .calibrate_decr = generic_calibrate_decr,
+ .progress = qpace_progress,
+ .init_IRQ = iic_init_IRQ,
+#ifdef CONFIG_KEXEC
+ .machine_kexec = default_machine_kexec,
+ .machine_kexec_prepare = default_machine_kexec_prepare,
+ .machine_crash_shutdown = default_machine_crash_shutdown,
+#endif
+};
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index ab721b50fbba..59305369f6b2 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -35,7 +35,6 @@
#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/io.h>
-#include <asm/kexec.h>
#include <asm/pgtable.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -289,9 +288,4 @@ define_machine(cell) {
.progress = cell_progress,
.init_IRQ = cell_init_irq,
.pci_setup_phb = cell_setup_phb,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
};
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
index 906a0a2a9fe1..1410443731eb 100644
--- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c
+++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c
@@ -80,10 +80,10 @@ static void cpu_affinity_set(struct spu *spu, int cpu)
u64 route;
if (nr_cpus_node(spu->node)) {
- cpumask_t spumask = node_to_cpumask(spu->node);
- cpumask_t cpumask = node_to_cpumask(cpu_to_node(cpu));
+ const struct cpumask *spumask = cpumask_of_node(spu->node),
+ *cpumask = cpumask_of_node(cpu_to_node(cpu));
- if (!cpus_intersects(spumask, cpumask))
+ if (!cpumask_intersects(spumask, cpumask))
return;
}
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 1b26071a86ca..7106b63d401b 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -273,12 +273,10 @@ spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return VM_FAULT_NOPAGE;
if (ctx->state == SPU_STATE_SAVED) {
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- & ~_PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
} else {
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
}
vm_insert_pfn(vma, address, pfn);
@@ -338,8 +336,7 @@ static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE);
+ vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
vma->vm_ops = &spufs_mem_mmap_vmops;
return 0;
@@ -452,8 +449,7 @@ static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_cntl_mmap_vmops;
return 0;
@@ -1155,8 +1151,7 @@ static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_signal1_mmap_vmops;
return 0;
@@ -1292,8 +1287,7 @@ static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_signal2_mmap_vmops;
return 0;
@@ -1414,8 +1408,7 @@ static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_mss_mmap_vmops;
return 0;
@@ -1476,8 +1469,7 @@ static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_psmap_mmap_vmops;
return 0;
@@ -1536,8 +1528,7 @@ static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_PFNMAP;
- vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
- | _PAGE_NO_CACHE | _PAGE_GUARDED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_ops = &spufs_mfc_mmap_vmops;
return 0;
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index cb85d237e492..6296bfd9cb0b 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -95,8 +95,8 @@ spufs_new_inode(struct super_block *sb, int mode)
goto out;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
out:
@@ -323,7 +323,7 @@ static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
goto out;
}
- filp = dentry_open(dentry, mnt, O_RDONLY);
+ filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
ret = PTR_ERR(filp);
@@ -562,7 +562,7 @@ static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
goto out;
}
- filp = dentry_open(dentry, mnt, O_RDONLY);
+ filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
ret = PTR_ERR(filp);
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 2ad914c47493..6a0ad196aeb3 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -166,9 +166,9 @@ void spu_update_sched_info(struct spu_context *ctx)
static int __node_allowed(struct spu_context *ctx, int node)
{
if (nr_cpus_node(node)) {
- cpumask_t mask = node_to_cpumask(node);
+ const struct cpumask *mask = cpumask_of_node(node);
- if (cpus_intersects(mask, ctx->cpus_allowed))
+ if (cpumask_intersects(mask, &ctx->cpus_allowed))
return 1;
}
diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c
index d3cde6b9d2df..f6b0c519d5a2 100644
--- a/arch/powerpc/platforms/chrp/pci.c
+++ b/arch/powerpc/platforms/chrp/pci.c
@@ -141,6 +141,7 @@ hydra_init(void)
of_node_put(np);
return 0;
}
+ of_node_put(np);
Hydra = ioremap(r.start, r.end-r.start);
printk("Hydra Mac I/O at %llx\n", (unsigned long long)r.start);
printk("Hydra Feature_Control was %x",
@@ -198,7 +199,7 @@ static void __init setup_peg2(struct pci_controller *hose, struct device_node *d
printk ("RTAS supporting Pegasos OF not found, please upgrade"
" your firmware\n");
}
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
/* keep the reference to the root node */
}
diff --git a/arch/powerpc/platforms/embedded6xx/c2k.c b/arch/powerpc/platforms/embedded6xx/c2k.c
index 32ba0fa0ad03..8cab5731850f 100644
--- a/arch/powerpc/platforms/embedded6xx/c2k.c
+++ b/arch/powerpc/platforms/embedded6xx/c2k.c
@@ -20,7 +20,6 @@
#include <linux/seq_file.h>
#include <linux/time.h>
#include <linux/of.h>
-#include <linux/kexec.h>
#include <asm/machdep.h>
#include <asm/prom.h>
@@ -147,9 +146,4 @@ define_machine(c2k) {
.get_irq = mv64x60_get_irq,
.restart = c2k_restart,
.calibrate_decr = generic_calibrate_decr,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
};
diff --git a/arch/powerpc/platforms/embedded6xx/prpmc2800.c b/arch/powerpc/platforms/embedded6xx/prpmc2800.c
index 4c485e984236..670035f49a69 100644
--- a/arch/powerpc/platforms/embedded6xx/prpmc2800.c
+++ b/arch/powerpc/platforms/embedded6xx/prpmc2800.c
@@ -19,7 +19,6 @@
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/time.h>
-#include <asm/kexec.h>
#include <mm/mmu_decl.h>
@@ -155,9 +154,4 @@ define_machine(prpmc2800){
.get_irq = mv64x60_get_irq,
.restart = prpmc2800_restart,
.calibrate_decr = generic_calibrate_decr,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
};
diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig
index 45ffd8e542f4..ed3753d8c109 100644
--- a/arch/powerpc/platforms/iseries/Kconfig
+++ b/arch/powerpc/platforms/iseries/Kconfig
@@ -9,6 +9,7 @@ menu "iSeries device drivers"
config VIODASD
tristate "iSeries Virtual I/O disk support"
+ depends on BLOCK
help
If you are running on an iSeries system and you want to use
virtual disks created and managed by OS/400, say Y.
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
index d4c61c3c9669..bfd60e4accee 100644
--- a/arch/powerpc/platforms/maple/setup.c
+++ b/arch/powerpc/platforms/maple/setup.c
@@ -50,7 +50,6 @@
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#include <asm/kexec.h>
#include <asm/pci-bridge.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
@@ -335,9 +334,4 @@ define_machine(maple) {
.calibrate_decr = generic_calibrate_decr,
.progress = maple_progress,
.power_save = power4_idle,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
};
diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c
index 792d3ce8112e..65c585b8b00d 100644
--- a/arch/powerpc/platforms/powermac/cpufreq_32.c
+++ b/arch/powerpc/platforms/powermac/cpufreq_32.c
@@ -310,7 +310,7 @@ static int pmu_set_cpu_speed(int low_speed)
_set_L3CR(save_l3cr);
/* Restore userland MMU context */
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
#ifdef DEBUG_FREQ
printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));
diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c
index bcf50d7056e9..54b7b76ed4f0 100644
--- a/arch/powerpc/platforms/powermac/pci.c
+++ b/arch/powerpc/platforms/powermac/pci.c
@@ -729,7 +729,7 @@ static void __init setup_bandit(struct pci_controller *hose,
static int __init setup_uninorth(struct pci_controller *hose,
struct resource *addr)
{
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
has_uninorth = 1;
hose->ops = &macrisc_pci_ops;
hose->cfg_addr = ioremap(addr->start + 0x800000, 0x1000);
@@ -996,7 +996,7 @@ void __init pmac_pci_init(void)
struct device_node *np, *root;
struct device_node *ht = NULL;
- ppc_pci_flags = PPC_PCI_CAN_SKIP_ISA_ALIGN;
+ ppc_pci_set_flags(PPC_PCI_CAN_SKIP_ISA_ALIGN);
root = of_find_node_by_path("/");
if (root == NULL) {
@@ -1055,7 +1055,7 @@ void __init pmac_pci_init(void)
* some offset between bus number and domains for now when we
* assign all busses should help for now
*/
- if (ppc_pci_flags & PPC_PCI_REASSIGN_ALL_BUS)
+ if (ppc_pci_has_flag(PPC_PCI_REASSIGN_ALL_BUS))
pcibios_assign_bus_offset = 0x10;
#endif
}
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 82c14d203d8b..9b78f5300c24 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -60,7 +60,6 @@
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#include <asm/kexec.h>
#include <asm/pci-bridge.h>
#include <asm/ohare.h>
#include <asm/mediabay.h>
@@ -310,9 +309,7 @@ static void __init pmac_setup_arch(void)
}
/* See if newworld or oldworld */
- for (ic = NULL; (ic = of_find_all_nodes(ic)) != NULL; )
- if (of_get_property(ic, "interrupt-controller", NULL))
- break;
+ ic = of_find_node_with_property(NULL, "interrupt-controller");
if (ic) {
pmac_newworld = 1;
of_node_put(ic);
@@ -740,11 +737,6 @@ define_machine(powermac) {
.pci_probe_mode = pmac_pci_probe_mode,
.power_save = power4_idle,
.enable_pmcs = power4_enable_pmcs,
-#ifdef CONFIG_KEXEC
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
-#endif
#endif /* CONFIG_PPC64 */
#ifdef CONFIG_PPC32
.pcibios_enable_device_hook = pmac_pci_enable_device_hook,
diff --git a/arch/powerpc/platforms/powermac/sleep.S b/arch/powerpc/platforms/powermac/sleep.S
index adee28da353f..1c2802fabd57 100644
--- a/arch/powerpc/platforms/powermac/sleep.S
+++ b/arch/powerpc/platforms/powermac/sleep.S
@@ -17,6 +17,7 @@
#include <asm/cache.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
+#include <asm/mmu.h>
#define MAGIC 0x4c617273 /* 'Lars' */
@@ -323,7 +324,7 @@ grackle_wake_up:
lwz r4,SL_IBAT3+4(r1)
mtibatl 3,r4
-BEGIN_FTR_SECTION
+BEGIN_MMU_FTR_SECTION
li r4,0
mtspr SPRN_DBAT4U,r4
mtspr SPRN_DBAT4L,r4
@@ -341,7 +342,7 @@ BEGIN_FTR_SECTION
mtspr SPRN_IBAT6L,r4
mtspr SPRN_IBAT7U,r4
mtspr SPRN_IBAT7L,r4
-END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_HIGH_BATS)
/* Flush all TLBs */
lis r4,0x1000
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c
index 40f72c2a4699..6b0711c15eca 100644
--- a/arch/powerpc/platforms/powermac/smp.c
+++ b/arch/powerpc/platforms/powermac/smp.c
@@ -739,7 +739,7 @@ static void __init smp_core99_setup(int ncpus)
/* XXX should get this from reg properties */
for (i = 1; i < ncpus; ++i)
- smp_hw_index[i] = i;
+ set_hard_smp_processor_id(i, i);
}
#endif
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index ffdd8e963fbd..dbc124e05646 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -314,11 +314,17 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id,
result = ps3_system_bus_device_register(&p->dev);
- if (result)
+ if (result) {
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
__func__, __LINE__);
-
+ goto fail_device_register;
+ }
pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return 0;
+
+fail_device_register:
+ kfree(p);
+ pr_debug(" <- %s:%d fail\n", __func__, __LINE__);
return result;
}
@@ -463,11 +469,17 @@ static int __init ps3_register_sound_devices(void)
result = ps3_system_bus_device_register(&p->dev);
- if (result)
+ if (result) {
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
__func__, __LINE__);
-
+ goto fail_device_register;
+ }
pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return 0;
+
+fail_device_register:
+ kfree(p);
+ pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
return result;
}
@@ -485,17 +497,24 @@ static int __init ps3_register_graphics_devices(void)
if (!p)
return -ENOMEM;
- p->dev.match_id = PS3_MATCH_ID_GRAPHICS;
- p->dev.match_sub_id = PS3_MATCH_SUB_ID_FB;
+ p->dev.match_id = PS3_MATCH_ID_GPU;
+ p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_FB;
p->dev.dev_type = PS3_DEVICE_TYPE_IOC0;
result = ps3_system_bus_device_register(&p->dev);
- if (result)
+ if (result) {
pr_debug("%s:%d ps3_system_bus_device_register failed\n",
__func__, __LINE__);
+ goto fail_device_register;
+ }
pr_debug(" <- %s:%d\n", __func__, __LINE__);
+ return 0;
+
+fail_device_register:
+ kfree(p);
+ pr_debug(" <- %s:%d failed\n", __func__, __LINE__);
return result;
}
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c
index 3a58ffabccd9..a4d49dd9e8a9 100644
--- a/arch/powerpc/platforms/ps3/mm.c
+++ b/arch/powerpc/platforms/ps3/mm.c
@@ -649,7 +649,7 @@ static int dma_sb_region_create(struct ps3_dma_region *r)
{
int result;
- pr_info(" -> %s:%d:\n", __func__, __LINE__);
+ DBG(" -> %s:%d:\n", __func__, __LINE__);
BUG_ON(!r);
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c
index 77bc330263c4..35f3e85cf60e 100644
--- a/arch/powerpc/platforms/ps3/setup.c
+++ b/arch/powerpc/platforms/ps3/setup.c
@@ -23,7 +23,6 @@
#include <linux/fs.h>
#include <linux/root_dev.h>
#include <linux/console.h>
-#include <linux/kexec.h>
#include <linux/bootmem.h>
#include <asm/machdep.h>
@@ -42,6 +41,10 @@
#define DBG pr_debug
#endif
+/* mutex synchronizing GPU accesses and video mode changes */
+DEFINE_MUTEX(ps3_gpu_mutex);
+EXPORT_SYMBOL_GPL(ps3_gpu_mutex);
+
#if !defined(CONFIG_SMP)
static void smp_send_stop(void) {}
#endif
@@ -277,8 +280,5 @@ define_machine(ps3) {
.halt = ps3_halt,
#if defined(CONFIG_KEXEC)
.kexec_cpu_down = ps3_kexec_cpu_down,
- .machine_kexec = default_machine_kexec,
- .machine_kexec_prepare = default_machine_kexec_prepare,
- .machine_crash_shutdown = default_machine_crash_shutdown,
#endif
};
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 661e9f77ebf6..ee0d22911621 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -31,7 +31,7 @@
#include "platform.h"
static struct device ps3_system_bus = {
- .bus_id = "ps3_system",
+ .init_name = "ps3_system",
};
/* FIXME: need device usage counters! */
@@ -175,7 +175,7 @@ int ps3_open_hv_device(struct ps3_system_bus_device *dev)
return ps3_open_hv_device_sb(dev);
case PS3_MATCH_ID_SOUND:
- case PS3_MATCH_ID_GRAPHICS:
+ case PS3_MATCH_ID_GPU:
return ps3_open_hv_device_gpu(dev);
case PS3_MATCH_ID_AV_SETTINGS:
@@ -213,7 +213,7 @@ int ps3_close_hv_device(struct ps3_system_bus_device *dev)
return ps3_close_hv_device_sb(dev);
case PS3_MATCH_ID_SOUND:
- case PS3_MATCH_ID_GRAPHICS:
+ case PS3_MATCH_ID_GPU:
return ps3_close_hv_device_gpu(dev);
case PS3_MATCH_ID_AV_SETTINGS:
@@ -356,12 +356,12 @@ static int ps3_system_bus_match(struct device *_dev,
if (result)
pr_info("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): match\n",
__func__, __LINE__,
- dev->match_id, dev->match_sub_id, dev->core.bus_id,
+ dev->match_id, dev->match_sub_id, dev_name(&dev->core),
drv->match_id, drv->match_sub_id, drv->core.name);
else
pr_debug("%s:%d: dev=%u.%u(%s), drv=%u.%u(%s): miss\n",
__func__, __LINE__,
- dev->match_id, dev->match_sub_id, dev->core.bus_id,
+ dev->match_id, dev->match_sub_id, dev_name(&dev->core),
drv->match_id, drv->match_sub_id, drv->core.name);
return result;
@@ -383,9 +383,9 @@ static int ps3_system_bus_probe(struct device *_dev)
result = drv->probe(dev);
else
pr_debug("%s:%d: %s no probe method\n", __func__, __LINE__,
- dev->core.bus_id);
+ dev_name(&dev->core));
- pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id);
+ pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));
return result;
}
@@ -407,7 +407,7 @@ static int ps3_system_bus_remove(struct device *_dev)
dev_dbg(&dev->core, "%s:%d %s: no remove method\n",
__func__, __LINE__, drv->core.name);
- pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev->core.bus_id);
+ pr_debug(" <- %s:%d: %s\n", __func__, __LINE__, dev_name(&dev->core));
return result;
}
@@ -432,7 +432,7 @@ static void ps3_system_bus_shutdown(struct device *_dev)
BUG_ON(!drv);
dev_dbg(&dev->core, "%s:%d: %s -> %s\n", __func__, __LINE__,
- dev->core.bus_id, drv->core.name);
+ dev_name(&dev->core), drv->core.name);
if (drv->shutdown)
drv->shutdown(dev);
@@ -453,7 +453,8 @@ static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *en
{
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
- if (add_uevent_var(env, "MODALIAS=ps3:%d", dev->match_id))
+ if (add_uevent_var(env, "MODALIAS=ps3:%d:%d", dev->match_id,
+ dev->match_sub_id))
return -ENOMEM;
return 0;
}
@@ -462,7 +463,8 @@ static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
char *buf)
{
struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
- int len = snprintf(buf, PAGE_SIZE, "ps3:%d\n", dev->match_id);
+ int len = snprintf(buf, PAGE_SIZE, "ps3:%d:%d\n", dev->match_id,
+ dev->match_sub_id);
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}
@@ -742,22 +744,18 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
switch (dev->dev_type) {
case PS3_DEVICE_TYPE_IOC0:
dev->core.archdata.dma_ops = &ps3_ioc0_dma_ops;
- snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
- "ioc0_%02x", ++dev_ioc0_count);
+ dev_set_name(&dev->core, "ioc0_%02x", ++dev_ioc0_count);
break;
case PS3_DEVICE_TYPE_SB:
dev->core.archdata.dma_ops = &ps3_sb_dma_ops;
- snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
- "sb_%02x", ++dev_sb_count);
+ dev_set_name(&dev->core, "sb_%02x", ++dev_sb_count);
break;
case PS3_DEVICE_TYPE_VUART:
- snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
- "vuart_%02x", ++dev_vuart_count);
+ dev_set_name(&dev->core, "vuart_%02x", ++dev_vuart_count);
break;
case PS3_DEVICE_TYPE_LPM:
- snprintf(dev->core.bus_id, sizeof(dev->core.bus_id),
- "lpm_%02x", ++dev_lpm_count);
+ dev_set_name(&dev->core, "lpm_%02x", ++dev_lpm_count);
break;
default:
BUG();
@@ -766,7 +764,7 @@ int ps3_system_bus_device_register(struct ps3_system_bus_device *dev)
dev->core.archdata.of_node = NULL;
set_dev_node(&dev->core, 0);
- pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id);
+ pr_debug("%s:%d add %s\n", __func__, __LINE__, dev_name(&dev->core));
result = device_register(&dev->core);
return result;
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 97619fd51e39..ddc2a307cd50 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -54,7 +54,7 @@ config PPC_SMLPAR
config CMM
tristate "Collaborative memory management"
- depends on PPC_SMLPAR
+ depends on PPC_SMLPAR && !CRASH_DUMP
default y
help
Select this option, if you want to enable the kernel interface
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index 5cd4d2761620..6567439fe78d 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -28,6 +28,7 @@
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/oom.h>
+#include <linux/reboot.h>
#include <linux/sched.h>
#include <linux/stringify.h>
#include <linux/swap.h>
@@ -384,6 +385,26 @@ static void cmm_unregister_sysfs(struct sys_device *sysdev)
}
/**
+ * cmm_reboot_notifier - Make sure pages are not still marked as "loaned"
+ *
+ **/
+static int cmm_reboot_notifier(struct notifier_block *nb,
+ unsigned long action, void *unused)
+{
+ if (action == SYS_RESTART) {
+ if (cmm_thread_ptr)
+ kthread_stop(cmm_thread_ptr);
+ cmm_thread_ptr = NULL;
+ cmm_free_pages(loaned_pages);
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block cmm_reboot_nb = {
+ .notifier_call = cmm_reboot_notifier,
+};
+
+/**
* cmm_init - Module initialization
*
* Return value:
@@ -399,9 +420,12 @@ static int cmm_init(void)
if ((rc = register_oom_notifier(&cmm_oom_nb)) < 0)
return rc;
- if ((rc = cmm_sysfs_register(&cmm_sysdev)))
+ if ((rc = register_reboot_notifier(&cmm_reboot_nb)))
goto out_oom_notifier;
+ if ((rc = cmm_sysfs_register(&cmm_sysdev)))
+ goto out_reboot_notifier;
+
if (cmm_disabled)
return rc;
@@ -415,6 +439,8 @@ static int cmm_init(void)
out_unregister_sysfs:
cmm_unregister_sysfs(&cmm_sysdev);
+out_reboot_notifier:
+ unregister_reboot_notifier(&cmm_reboot_nb);
out_oom_notifier:
unregister_oom_notifier(&cmm_oom_nb);
return rc;
@@ -431,6 +457,7 @@ static void cmm_exit(void)
if (cmm_thread_ptr)
kthread_stop(cmm_thread_ptr);
unregister_oom_notifier(&cmm_oom_nb);
+ unregister_reboot_notifier(&cmm_reboot_nb);
cmm_free_pages(loaned_pages);
cmm_unregister_sysfs(&cmm_sysdev);
}
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c
index 54816d75b578..989d6462c154 100644
--- a/arch/powerpc/platforms/pseries/eeh.c
+++ b/arch/powerpc/platforms/pseries/eeh.c
@@ -21,6 +21,8 @@
* Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
*/
+#undef DEBUG
+
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/list.h>
@@ -488,10 +490,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) ||
pdn->eeh_mode & EEH_MODE_NOCHECK) {
ignored_check++;
-#ifdef DEBUG
- printk ("EEH:ignored check (%x) for %s %s\n",
- pdn->eeh_mode, pci_name (dev), dn->full_name);
-#endif
+ pr_debug("EEH: Ignored check (%x) for %s %s\n",
+ pdn->eeh_mode, pci_name (dev), dn->full_name);
return 0;
}
@@ -1014,10 +1014,9 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
eeh_subsystem_enabled = 1;
pdn->eeh_mode |= EEH_MODE_SUPPORTED;
-#ifdef DEBUG
- printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n",
- dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr);
-#endif
+ pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n",
+ dn->full_name, pdn->eeh_config_addr,
+ pdn->eeh_pe_config_addr);
} else {
/* This device doesn't support EEH, but it may have an
@@ -1161,13 +1160,17 @@ static void eeh_add_device_late(struct pci_dev *dev)
if (!dev || !eeh_subsystem_enabled)
return;
-#ifdef DEBUG
- printk(KERN_DEBUG "EEH: adding device %s\n", pci_name(dev));
-#endif
+ pr_debug("EEH: Adding device %s\n", pci_name(dev));
- pci_dev_get (dev);
dn = pci_device_to_OF_node(dev);
pdn = PCI_DN(dn);
+ if (pdn->pcidev == dev) {
+ pr_debug("EEH: Already referenced !\n");
+ return;
+ }
+ WARN_ON(pdn->pcidev);
+
+ pci_dev_get (dev);
pdn->pcidev = dev;
pci_addr_cache_insert_device(dev);
@@ -1206,17 +1209,18 @@ static void eeh_remove_device(struct pci_dev *dev)
return;
/* Unregister the device with the EEH/PCI address search system */
-#ifdef DEBUG
- printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev));
-#endif
- pci_addr_cache_remove_device(dev);
- eeh_sysfs_remove_device(dev);
+ pr_debug("EEH: Removing device %s\n", pci_name(dev));
dn = pci_device_to_OF_node(dev);
- if (PCI_DN(dn)->pcidev) {
- PCI_DN(dn)->pcidev = NULL;
- pci_dev_put (dev);
+ if (PCI_DN(dn)->pcidev == NULL) {
+ pr_debug("EEH: Not referenced !\n");
+ return;
}
+ PCI_DN(dn)->pcidev = NULL;
+ pci_dev_put (dev);
+
+ pci_addr_cache_remove_device(dev);
+ eeh_sysfs_remove_device(dev);
}
void eeh_remove_bus_device(struct pci_dev *dev)
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index 1f032483c026..a20ead87153d 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -116,7 +116,7 @@ static void pseries_cpu_die(unsigned int cpu)
cpu_status = query_cpu_stopped(pcpu);
if (cpu_status == 0 || cpu_status == -1)
break;
- msleep(200);
+ cpu_relax();
}
if (cpu_status != 0) {
printk("Querying DEAD? cpu %i (%i) shows %i\n",
diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c
index 7190493e9bdc..5e1ed3d60ee5 100644
--- a/arch/powerpc/platforms/pseries/pci_dlpar.c
+++ b/arch/powerpc/platforms/pseries/pci_dlpar.c
@@ -25,6 +25,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#undef DEBUG
+
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <asm/ppc-pci.h>
@@ -69,74 +71,25 @@ EXPORT_SYMBOL_GPL(pcibios_find_pci_bus);
* Remove all of the PCI devices under this bus both from the
* linux pci device tree, and from the powerpc EEH address cache.
*/
-void
-pcibios_remove_pci_devices(struct pci_bus *bus)
+void pcibios_remove_pci_devices(struct pci_bus *bus)
{
- struct pci_dev *dev, *tmp;
+ struct pci_dev *dev, *tmp;
+ struct pci_bus *child_bus;
+
+ /* First go down child busses */
+ list_for_each_entry(child_bus, &bus->children, node)
+ pcibios_remove_pci_devices(child_bus);
+ pr_debug("PCI: Removing devices on bus %04x:%02x\n",
+ pci_domain_nr(bus), bus->number);
list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
+ pr_debug(" * Removing %s...\n", pci_name(dev));
eeh_remove_bus_device(dev);
- pci_remove_bus_device(dev);
- }
+ pci_remove_bus_device(dev);
+ }
}
EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices);
-/* Must be called before pci_bus_add_devices */
-void
-pcibios_fixup_new_pci_devices(struct pci_bus *bus)
-{
- struct pci_dev *dev;
-
- list_for_each_entry(dev, &bus->devices, bus_list) {
- /* Skip already-added devices */
- if (!dev->is_added) {
- int i;
-
- /* Fill device archdata and setup iommu table */
- pcibios_setup_new_device(dev);
-
- pci_read_irq_line(dev);
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- struct resource *r = &dev->resource[i];
-
- if (r->parent || !r->start || !r->flags)
- continue;
- pci_claim_resource(dev, i);
- }
- }
- }
-}
-EXPORT_SYMBOL_GPL(pcibios_fixup_new_pci_devices);
-
-static int
-pcibios_pci_config_bridge(struct pci_dev *dev)
-{
- u8 sec_busno;
- struct pci_bus *child_bus;
-
- /* Get busno of downstream bus */
- pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
-
- /* Add to children of PCI bridge dev->bus */
- child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
- if (!child_bus) {
- printk (KERN_ERR "%s: could not add second bus\n", __func__);
- return -EIO;
- }
- sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
-
- pci_scan_child_bus(child_bus);
-
- /* Fixup new pci devices */
- pcibios_fixup_new_pci_devices(child_bus);
-
- /* Make the discovered devices available */
- pci_bus_add_devices(child_bus);
-
- eeh_add_device_tree_late(child_bus);
- return 0;
-}
-
/**
* pcibios_add_pci_devices - adds new pci devices to bus
*
@@ -147,10 +100,9 @@ pcibios_pci_config_bridge(struct pci_dev *dev)
* is how this routine differs from other, similar pcibios
* routines.)
*/
-void
-pcibios_add_pci_devices(struct pci_bus * bus)
+void pcibios_add_pci_devices(struct pci_bus * bus)
{
- int slotno, num, mode;
+ int slotno, num, mode, pass, max;
struct pci_dev *dev;
struct device_node *dn = pci_bus_to_OF_node(bus);
@@ -162,26 +114,23 @@ pcibios_add_pci_devices(struct pci_bus * bus)
if (mode == PCI_PROBE_DEVTREE) {
/* use ofdt-based probe */
- of_scan_bus(dn, bus);
- if (!list_empty(&bus->devices)) {
- pcibios_fixup_new_pci_devices(bus);
- pci_bus_add_devices(bus);
- eeh_add_device_tree_late(bus);
- }
+ of_rescan_bus(dn, bus);
} else if (mode == PCI_PROBE_NORMAL) {
/* use legacy probe */
slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
- if (num) {
- pcibios_fixup_new_pci_devices(bus);
- pci_bus_add_devices(bus);
- eeh_add_device_tree_late(bus);
+ if (!num)
+ return;
+ pcibios_setup_bus_devices(bus);
+ max = bus->secondary;
+ for (pass=0; pass < 2; pass++)
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+ dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+ max = pci_scan_bridge(bus, dev, max, pass);
}
-
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
- pcibios_pci_config_bridge(dev);
}
+ pcibios_finish_adding_to_bus(bus);
}
EXPORT_SYMBOL_GPL(pcibios_add_pci_devices);
@@ -190,6 +139,8 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
struct pci_controller *phb;
int primary;
+ pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name);
+
primary = list_empty(&hose_list);
phb = pcibios_alloc_controller(dn);
if (!phb)
@@ -203,11 +154,59 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
eeh_add_device_tree_early(dn);
scan_phb(phb);
- pcibios_allocate_bus_resources(phb->bus);
- pcibios_fixup_new_pci_devices(phb->bus);
- pci_bus_add_devices(phb->bus);
- eeh_add_device_tree_late(phb->bus);
+ pcibios_finish_adding_to_bus(phb->bus);
return phb;
}
EXPORT_SYMBOL_GPL(init_phb_dynamic);
+
+/* RPA-specific bits for removing PHBs */
+int remove_phb_dynamic(struct pci_controller *phb)
+{
+ struct pci_bus *b = phb->bus;
+ struct resource *res;
+ int rc, i;
+
+ pr_debug("PCI: Removing PHB %04x:%02x... \n",
+ pci_domain_nr(b), b->number);
+
+ /* We cannot to remove a root bus that has children */
+ if (!(list_empty(&b->children) && list_empty(&b->devices)))
+ return -EBUSY;
+
+ /* We -know- there aren't any child devices anymore at this stage
+ * and thus, we can safely unmap the IO space as it's not in use
+ */
+ res = &phb->io_resource;
+ if (res->flags & IORESOURCE_IO) {
+ rc = pcibios_unmap_io_space(b);
+ if (rc) {
+ printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
+ __func__, b->name);
+ return 1;
+ }
+ }
+
+ /* Unregister the bridge device from sysfs and remove the PCI bus */
+ device_unregister(b->bridge);
+ phb->bus = NULL;
+ pci_remove_bus(b);
+
+ /* Now release the IO resource */
+ if (res->flags & IORESOURCE_IO)
+ release_resource(res);
+
+ /* Release memory resources */
+ for (i = 0; i < 3; ++i) {
+ res = &phb->mem_resources[i];
+ if (!(res->flags & IORESOURCE_MEM))
+ continue;
+ release_resource(res);
+ }
+
+ /* Free pci_controller data structure */
+ pcibios_free_controller(phb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(remove_phb_dynamic);
diff --git a/arch/powerpc/platforms/pseries/phyp_dump.c b/arch/powerpc/platforms/pseries/phyp_dump.c
index edbc012c2ebc..6cf35cd8d0b5 100644
--- a/arch/powerpc/platforms/pseries/phyp_dump.c
+++ b/arch/powerpc/platforms/pseries/phyp_dump.c
@@ -130,6 +130,9 @@ static unsigned long init_dump_header(struct phyp_dump_header *ph)
static void print_dump_header(const struct phyp_dump_header *ph)
{
#ifdef DEBUG
+ if (ph == NULL)
+ return;
+
printk(KERN_INFO "dump header:\n");
/* setup some ph->sections required */
printk(KERN_INFO "version = %d\n", ph->version);
@@ -411,6 +414,8 @@ static int __init phyp_dump_setup(void)
of_node_put(rtas);
}
+ ibm_configure_kernel_dump = rtas_token("ibm,configure-kernel-dump");
+
print_dump_header(dump_header);
dump_area_length = init_dump_header(&phdr);
/* align down */
diff --git a/arch/powerpc/platforms/pseries/rtasd.c b/arch/powerpc/platforms/pseries/rtasd.c
index f4e55be2eea9..afad9f5ac0ac 100644
--- a/arch/powerpc/platforms/pseries/rtasd.c
+++ b/arch/powerpc/platforms/pseries/rtasd.c
@@ -208,6 +208,7 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
break;
case ERR_TYPE_KERNEL_PANIC:
default:
+ WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
spin_unlock_irqrestore(&rtasd_log_lock, s);
return;
}
@@ -227,6 +228,7 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
/* Check to see if we need to or have stopped logging */
if (fatal || !logging_enabled) {
logging_enabled = 0;
+ WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
spin_unlock_irqrestore(&rtasd_log_lock, s);
return;
}
@@ -249,11 +251,13 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal)
else
rtas_log_start += 1;
+ WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
spin_unlock_irqrestore(&rtasd_log_lock, s);
wake_up_interruptible(&rtas_log_wait);
break;
case ERR_TYPE_KERNEL_PANIC:
default:
+ WARN_ON_ONCE(!irqs_disabled()); /* @@@ DEBUG @@@ */
spin_unlock_irqrestore(&rtasd_log_lock, s);
return;
}
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c
index 424b335a71c8..84e058f1e1cc 100644
--- a/arch/powerpc/platforms/pseries/xics.c
+++ b/arch/powerpc/platforms/pseries/xics.c
@@ -579,7 +579,7 @@ static void xics_update_irq_servers(void)
int i, j;
struct device_node *np;
u32 ilen;
- const u32 *ireg, *isize;
+ const u32 *ireg;
u32 hcpuid;
/* Find the server numbers for the boot cpu. */
@@ -607,11 +607,6 @@ static void xics_update_irq_servers(void)
}
}
- /* get the bit size of server numbers */
- isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
- if (isize)
- interrupt_server_size = *isize;
-
of_node_put(np);
}
@@ -682,6 +677,7 @@ void __init xics_init_IRQ(void)
struct device_node *np;
u32 indx = 0;
int found = 0;
+ const u32 *isize;
ppc64_boot_msg(0x20, "XICS Init");
@@ -701,6 +697,26 @@ void __init xics_init_IRQ(void)
if (found == 0)
return;
+ /* get the bit size of server numbers */
+ found = 0;
+
+ for_each_compatible_node(np, NULL, "ibm,ppc-xics") {
+ isize = of_get_property(np, "ibm,interrupt-server#-size", NULL);
+
+ if (!isize)
+ continue;
+
+ if (!found) {
+ interrupt_server_size = *isize;
+ found = 1;
+ } else if (*isize != interrupt_server_size) {
+ printk(KERN_WARNING "XICS: "
+ "mismatched ibm,interrupt-server#-size\n");
+ interrupt_server_size = max(*isize,
+ interrupt_server_size);
+ }
+ }
+
xics_update_irq_servers();
xics_init_host();
@@ -728,9 +744,18 @@ static void xics_set_cpu_priority(unsigned char cppr)
/* Have the calling processor join or leave the specified global queue */
static void xics_set_cpu_giq(unsigned int gserver, unsigned int join)
{
- int status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE,
- (1UL << interrupt_server_size) - 1 - gserver, join);
- WARN_ON(status < 0);
+ int index;
+ int status;
+
+ if (!rtas_indicator_present(GLOBAL_INTERRUPT_QUEUE, NULL))
+ return;
+
+ index = (1UL << interrupt_server_size) - 1 - gserver;
+
+ status = rtas_set_indicator_fast(GLOBAL_INTERRUPT_QUEUE, index, join);
+
+ WARN(status < 0, "set-indicator(%d, %d, %u) returned %d\n",
+ GLOBAL_INTERRUPT_QUEUE, index, join, status);
}
void xics_setup_cpu(void)
diff --git a/arch/powerpc/sysdev/bestcomm/ata.c b/arch/powerpc/sysdev/bestcomm/ata.c
index 1f5258fb38c3..901c9f91e5dd 100644
--- a/arch/powerpc/sysdev/bestcomm/ata.c
+++ b/arch/powerpc/sysdev/bestcomm/ata.c
@@ -61,6 +61,9 @@ bcom_ata_init(int queue_len, int maxbufsize)
struct bcom_ata_var *var;
struct bcom_ata_inc *inc;
+ /* Prefetch breaks ATA DMA. Turn it off for ATA DMA */
+ bcom_disable_prefetch();
+
tsk = bcom_task_alloc(queue_len, sizeof(struct bcom_ata_bd), 0);
if (!tsk)
return NULL;
diff --git a/arch/powerpc/sysdev/bestcomm/ata.h b/arch/powerpc/sysdev/bestcomm/ata.h
index 10982769c465..0b2371811334 100644
--- a/arch/powerpc/sysdev/bestcomm/ata.h
+++ b/arch/powerpc/sysdev/bestcomm/ata.h
@@ -16,22 +16,15 @@
struct bcom_ata_bd {
u32 status;
- u32 dst_pa;
u32 src_pa;
+ u32 dst_pa;
};
-extern struct bcom_task *
-bcom_ata_init(int queue_len, int maxbufsize);
-
-extern void
-bcom_ata_rx_prepare(struct bcom_task *tsk);
-
-extern void
-bcom_ata_tx_prepare(struct bcom_task *tsk);
-
-extern void
-bcom_ata_reset_bd(struct bcom_task *tsk);
-
+extern struct bcom_task * bcom_ata_init(int queue_len, int maxbufsize);
+extern void bcom_ata_rx_prepare(struct bcom_task *tsk);
+extern void bcom_ata_tx_prepare(struct bcom_task *tsk);
+extern void bcom_ata_reset_bd(struct bcom_task *tsk);
+extern void bcom_ata_release(struct bcom_task *tsk);
#endif /* __BESTCOMM_ATA_H__ */
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/arch/powerpc/sysdev/bestcomm/bestcomm.c
index 446c9ea85b30..378ebd9aac18 100644
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.c
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.c
@@ -279,7 +279,6 @@ bcom_engine_init(void)
int task;
phys_addr_t tdt_pa, ctx_pa, var_pa, fdt_pa;
unsigned int tdt_size, ctx_size, var_size, fdt_size;
- u16 regval;
/* Allocate & clear SRAM zones for FDT, TDTs, contexts and vars/incs */
tdt_size = BCOM_MAX_TASKS * sizeof(struct bcom_tdt);
@@ -331,10 +330,8 @@ bcom_engine_init(void)
out_8(&bcom_eng->regs->ipr[BCOM_INITIATOR_ALWAYS], BCOM_IPR_ALWAYS);
/* Disable COMM Bus Prefetch on the original 5200; it's broken */
- if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) {
- regval = in_be16(&bcom_eng->regs->PtdCntrl);
- out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
- }
+ if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR)
+ bcom_disable_prefetch();
/* Init lock */
spin_lock_init(&bcom_eng->lock);
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/arch/powerpc/sysdev/bestcomm/bestcomm.h
index c960a8b49655..23a95f80dfdb 100644
--- a/arch/powerpc/sysdev/bestcomm/bestcomm.h
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm.h
@@ -16,8 +16,19 @@
#ifndef __BESTCOMM_H__
#define __BESTCOMM_H__
-struct bcom_bd; /* defined later on ... */
-
+/**
+ * struct bcom_bd - Structure describing a generic BestComm buffer descriptor
+ * @status: The current status of this buffer. Exact meaning depends on the
+ * task type
+ * @data: An array of u32 extra data. Size of array is task dependant.
+ *
+ * Note: Don't dereference a bcom_bd pointer as an array. The size of the
+ * bcom_bd is variable. Use bcom_get_bd() instead.
+ */
+struct bcom_bd {
+ u32 status;
+ u32 data[0]; /* variable payload size */
+};
/* ======================================================================== */
/* Generic task management */
@@ -84,17 +95,6 @@ bcom_get_task_irq(struct bcom_task *tsk) {
/* BD based tasks helpers */
/* ======================================================================== */
-/**
- * struct bcom_bd - Structure describing a generic BestComm buffer descriptor
- * @status: The current status of this buffer. Exact meaning depends on the
- * task type
- * @data: An array of u32 whose meaning depends on the task type.
- */
-struct bcom_bd {
- u32 status;
- u32 data[1]; /* variable, but at least 1 */
-};
-
#define BCOM_BD_READY 0x40000000ul
/** _bcom_next_index - Get next input index.
@@ -140,15 +140,31 @@ bcom_queue_full(struct bcom_task *tsk)
}
/**
+ * bcom_get_bd - Get a BD from the queue
+ * @tsk: The BestComm task structure
+ * index: Index of the BD to fetch
+ */
+static inline struct bcom_bd
+*bcom_get_bd(struct bcom_task *tsk, unsigned int index)
+{
+ /* A cast to (void*) so the address can be incremented by the
+ * real size instead of by sizeof(struct bcom_bd) */
+ return ((void *)tsk->bd) + (index * tsk->bd_size);
+}
+
+/**
* bcom_buffer_done - Checks if a BestComm
* @tsk: The BestComm task structure
*/
static inline int
bcom_buffer_done(struct bcom_task *tsk)
{
+ struct bcom_bd *bd;
if (bcom_queue_empty(tsk))
return 0;
- return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY);
+
+ bd = bcom_get_bd(tsk, tsk->outdex);
+ return !(bd->status & BCOM_BD_READY);
}
/**
@@ -160,16 +176,21 @@ bcom_buffer_done(struct bcom_task *tsk)
static inline struct bcom_bd *
bcom_prepare_next_buffer(struct bcom_task *tsk)
{
- tsk->bd[tsk->index].status = 0; /* cleanup last status */
- return &tsk->bd[tsk->index];
+ struct bcom_bd *bd;
+
+ bd = bcom_get_bd(tsk, tsk->index);
+ bd->status = 0; /* cleanup last status */
+ return bd;
}
static inline void
bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie)
{
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index);
+
tsk->cookie[tsk->index] = cookie;
mb(); /* ensure the bd is really up-to-date */
- tsk->bd[tsk->index].status |= BCOM_BD_READY;
+ bd->status |= BCOM_BD_READY;
tsk->index = _bcom_next_index(tsk);
if (tsk->flags & BCOM_FLAGS_ENABLE_TASK)
bcom_enable(tsk);
@@ -179,10 +200,12 @@ static inline void *
bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd)
{
void *cookie = tsk->cookie[tsk->outdex];
+ struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex);
+
if (p_status)
- *p_status = tsk->bd[tsk->outdex].status;
+ *p_status = bd->status;
if (p_bd)
- *p_bd = &tsk->bd[tsk->outdex];
+ *p_bd = bd;
tsk->outdex = _bcom_next_outdex(tsk);
return cookie;
}
diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
index 866a2915ef2f..eb0d1c883c31 100644
--- a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
+++ b/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h
@@ -198,8 +198,8 @@ struct bcom_task_header {
#define BCOM_IPR_SCTMR_1 2
#define BCOM_IPR_FEC_RX 6
#define BCOM_IPR_FEC_TX 5
-#define BCOM_IPR_ATA_RX 4
-#define BCOM_IPR_ATA_TX 3
+#define BCOM_IPR_ATA_RX 7
+#define BCOM_IPR_ATA_TX 7
#define BCOM_IPR_SCPCI_RX 2
#define BCOM_IPR_SCPCI_TX 2
#define BCOM_IPR_PSC3_RX 2
@@ -241,6 +241,22 @@ extern void bcom_set_initiator(int task, int initiator);
#define TASK_ENABLE 0x8000
+/**
+ * bcom_disable_prefetch - Hook to disable bus prefetching
+ *
+ * ATA DMA and the original MPC5200 need this due to silicon bugs. At the
+ * moment disabling prefetch is a one-way street. There is no mechanism
+ * in place to turn prefetch back on after it has been disabled. There is
+ * no reason it couldn't be done, it would just be more complex to implement.
+ */
+static inline void bcom_disable_prefetch(void)
+{
+ u16 regval;
+
+ regval = in_be16(&bcom_eng->regs->PtdCntrl);
+ out_be16(&bcom_eng->regs->PtdCntrl, regval | 1);
+};
+
static inline void
bcom_enable_task(int task)
{
diff --git a/arch/powerpc/sysdev/dcr-low.S b/arch/powerpc/sysdev/dcr-low.S
index 2078f39e2f17..d3098ef1404a 100644
--- a/arch/powerpc/sysdev/dcr-low.S
+++ b/arch/powerpc/sysdev/dcr-low.S
@@ -11,14 +11,20 @@
#include <asm/ppc_asm.h>
#include <asm/processor.h>
+#include <asm/bug.h>
#define DCR_ACCESS_PROLOG(table) \
+ cmpli cr0,r3,1024; \
rlwinm r3,r3,4,18,27; \
lis r5,table@h; \
ori r5,r5,table@l; \
add r3,r3,r5; \
+ bge- 1f; \
mtctr r3; \
- bctr
+ bctr; \
+1: trap; \
+ EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,0; \
+ blr
_GLOBAL(__mfdcr)
DCR_ACCESS_PROLOG(__mfdcr_table)
diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c
index a8ba9983dd5a..bb44aa9fd470 100644
--- a/arch/powerpc/sysdev/dcr.c
+++ b/arch/powerpc/sysdev/dcr.c
@@ -124,7 +124,8 @@ EXPORT_SYMBOL_GPL(dcr_write_generic);
#endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */
-unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
+unsigned int dcr_resource_start(const struct device_node *np,
+ unsigned int index)
{
unsigned int ds;
const u32 *dr = of_get_property(np, "dcr-reg", &ds);
@@ -136,7 +137,7 @@ unsigned int dcr_resource_start(struct device_node *np, unsigned int index)
}
EXPORT_SYMBOL_GPL(dcr_resource_start);
-unsigned int dcr_resource_len(struct device_node *np, unsigned int index)
+unsigned int dcr_resource_len(const struct device_node *np, unsigned int index)
{
unsigned int ds;
const u32 *dr = of_get_property(np, "dcr-reg", &ds);
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 5b264eb4b1f7..d5f9ae0f1b75 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -187,7 +187,7 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
printk(KERN_WARNING "Can't get bus-range for %s, assume"
" bus 0\n", dev->full_name);
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
hose = pcibios_alloc_controller(dev);
if (!hose)
return -ENOMEM;
@@ -300,7 +300,7 @@ int __init mpc83xx_add_bridge(struct device_node *dev)
" bus 0\n", dev->full_name);
}
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
hose = pcibios_alloc_controller(dev);
if (!hose)
return -ENOMEM;
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 26ecb96f9731..115cb16351fd 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -207,236 +207,51 @@ static int __init of_add_fixed_phys(void)
arch_initcall(of_add_fixed_phys);
#endif /* CONFIG_FIXED_PHY */
-static int gfar_mdio_of_init_one(struct device_node *np)
-{
- int k;
- struct device_node *child = NULL;
- struct gianfar_mdio_data mdio_data;
- struct platform_device *mdio_dev;
- struct resource res;
- int ret;
-
- memset(&res, 0, sizeof(res));
- memset(&mdio_data, 0, sizeof(mdio_data));
-
- ret = of_address_to_resource(np, 0, &res);
- if (ret)
- return ret;
-
- /* The gianfar device will try to use the same ID created below to find
- * this bus, to coordinate register access (since they share). */
- mdio_dev = platform_device_register_simple("fsl-gianfar_mdio",
- res.start&0xfffff, &res, 1);
- if (IS_ERR(mdio_dev))
- return PTR_ERR(mdio_dev);
-
- for (k = 0; k < 32; k++)
- mdio_data.irq[k] = PHY_POLL;
-
- while ((child = of_get_next_child(np, child)) != NULL) {
- int irq = irq_of_parse_and_map(child, 0);
- if (irq != NO_IRQ) {
- const u32 *id = of_get_property(child, "reg", NULL);
- mdio_data.irq[*id] = irq;
- }
- }
-
- ret = platform_device_add_data(mdio_dev, &mdio_data,
- sizeof(struct gianfar_mdio_data));
- if (ret)
- platform_device_unregister(mdio_dev);
-
- return ret;
-}
-
-static int __init gfar_mdio_of_init(void)
-{
- struct device_node *np = NULL;
-
- for_each_compatible_node(np, NULL, "fsl,gianfar-mdio")
- gfar_mdio_of_init_one(np);
-
- /* try the deprecated version */
- for_each_compatible_node(np, "mdio", "gianfar");
- gfar_mdio_of_init_one(np);
-
- return 0;
-}
-
-arch_initcall(gfar_mdio_of_init);
-
-static const char *gfar_tx_intr = "tx";
-static const char *gfar_rx_intr = "rx";
-static const char *gfar_err_intr = "error";
-
-static int __init gfar_of_init(void)
+#ifdef CONFIG_PPC_83xx
+static int __init mpc83xx_wdt_init(void)
{
+ struct resource r;
struct device_node *np;
- unsigned int i;
- struct platform_device *gfar_dev;
- struct resource res;
+ struct platform_device *dev;
+ u32 freq = fsl_get_sys_freq();
int ret;
- for (np = NULL, i = 0;
- (np = of_find_compatible_node(np, "network", "gianfar")) != NULL;
- i++) {
- struct resource r[4];
- struct device_node *phy, *mdio;
- struct gianfar_platform_data gfar_data;
- const unsigned int *id;
- const char *model;
- const char *ctype;
- const void *mac_addr;
- const phandle *ph;
- int n_res = 2;
-
- if (!of_device_is_available(np))
- continue;
-
- memset(r, 0, sizeof(r));
- memset(&gfar_data, 0, sizeof(gfar_data));
-
- ret = of_address_to_resource(np, 0, &r[0]);
- if (ret)
- goto err;
-
- of_irq_to_resource(np, 0, &r[1]);
-
- model = of_get_property(np, "model", NULL);
-
- /* If we aren't the FEC we have multiple interrupts */
- if (model && strcasecmp(model, "FEC")) {
- r[1].name = gfar_tx_intr;
-
- r[2].name = gfar_rx_intr;
- of_irq_to_resource(np, 1, &r[2]);
+ np = of_find_compatible_node(NULL, "watchdog", "mpc83xx_wdt");
- r[3].name = gfar_err_intr;
- of_irq_to_resource(np, 2, &r[3]);
-
- n_res += 2;
- }
-
- gfar_dev =
- platform_device_register_simple("fsl-gianfar", i, &r[0],
- n_res);
-
- if (IS_ERR(gfar_dev)) {
- ret = PTR_ERR(gfar_dev);
- goto err;
- }
-
- mac_addr = of_get_mac_address(np);
- if (mac_addr)
- memcpy(gfar_data.mac_addr, mac_addr, 6);
-
- if (model && !strcasecmp(model, "TSEC"))
- gfar_data.device_flags =
- FSL_GIANFAR_DEV_HAS_GIGABIT |
- FSL_GIANFAR_DEV_HAS_COALESCE |
- FSL_GIANFAR_DEV_HAS_RMON |
- FSL_GIANFAR_DEV_HAS_MULTI_INTR;
- if (model && !strcasecmp(model, "eTSEC"))
- gfar_data.device_flags =
- FSL_GIANFAR_DEV_HAS_GIGABIT |
- FSL_GIANFAR_DEV_HAS_COALESCE |
- FSL_GIANFAR_DEV_HAS_RMON |
- FSL_GIANFAR_DEV_HAS_MULTI_INTR |
- FSL_GIANFAR_DEV_HAS_CSUM |
- FSL_GIANFAR_DEV_HAS_VLAN |
- FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
-
- ctype = of_get_property(np, "phy-connection-type", NULL);
-
- /* We only care about rgmii-id. The rest are autodetected */
- if (ctype && !strcmp(ctype, "rgmii-id"))
- gfar_data.interface = PHY_INTERFACE_MODE_RGMII_ID;
- else
- gfar_data.interface = PHY_INTERFACE_MODE_MII;
-
- if (of_get_property(np, "fsl,magic-packet", NULL))
- gfar_data.device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
-
- ph = of_get_property(np, "phy-handle", NULL);
- if (ph == NULL) {
- u32 *fixed_link;
-
- fixed_link = (u32 *)of_get_property(np, "fixed-link",
- NULL);
- if (!fixed_link) {
- ret = -ENODEV;
- goto unreg;
- }
-
- snprintf(gfar_data.bus_id, MII_BUS_ID_SIZE, "0");
- gfar_data.phy_id = fixed_link[0];
- } else {
- phy = of_find_node_by_phandle(*ph);
-
- if (phy == NULL) {
- ret = -ENODEV;
- goto unreg;
- }
-
- mdio = of_get_parent(phy);
-
- id = of_get_property(phy, "reg", NULL);
- ret = of_address_to_resource(mdio, 0, &res);
- if (ret) {
- of_node_put(phy);
- of_node_put(mdio);
- goto unreg;
- }
-
- gfar_data.phy_id = *id;
- snprintf(gfar_data.bus_id, MII_BUS_ID_SIZE, "%llx",
- (unsigned long long)res.start&0xfffff);
+ if (!np) {
+ ret = -ENODEV;
+ goto nodev;
+ }
- of_node_put(phy);
- of_node_put(mdio);
- }
+ memset(&r, 0, sizeof(r));
- /* Get MDIO bus controlled by this eTSEC, if any. Normally only
- * eTSEC 1 will control an MDIO bus, not necessarily the same
- * bus that its PHY is on ('mdio' above), so we can't just use
- * that. What we do is look for a gianfar mdio device that has
- * overlapping registers with this device. That's really the
- * whole point, to find the device sharing our registers to
- * coordinate access with it.
- */
- for_each_compatible_node(mdio, NULL, "fsl,gianfar-mdio") {
- if (of_address_to_resource(mdio, 0, &res))
- continue;
-
- if (res.start >= r[0].start && res.end <= r[0].end) {
- /* Get the ID the mdio bus platform device was
- * registered with. gfar_data.bus_id is
- * different because it's for finding a PHY,
- * while this is for finding a MII bus.
- */
- gfar_data.mdio_bus = res.start&0xfffff;
- of_node_put(mdio);
- break;
- }
- }
+ ret = of_address_to_resource(np, 0, &r);
+ if (ret)
+ goto err;
- ret =
- platform_device_add_data(gfar_dev, &gfar_data,
- sizeof(struct
- gianfar_platform_data));
- if (ret)
- goto unreg;
+ dev = platform_device_register_simple("mpc83xx_wdt", 0, &r, 1);
+ if (IS_ERR(dev)) {
+ ret = PTR_ERR(dev);
+ goto err;
}
+ ret = platform_device_add_data(dev, &freq, sizeof(freq));
+ if (ret)
+ goto unreg;
+
+ of_node_put(np);
return 0;
unreg:
- platform_device_unregister(gfar_dev);
+ platform_device_unregister(dev);
err:
+ of_node_put(np);
+nodev:
return ret;
}
-arch_initcall(gfar_of_init);
+arch_initcall(mpc83xx_wdt_init);
+#endif
static enum fsl_usb2_phy_modes determine_usb_phy(const char *phy_type)
{
diff --git a/arch/powerpc/sysdev/grackle.c b/arch/powerpc/sysdev/grackle.c
index d502927644c6..5da37c2f22ee 100644
--- a/arch/powerpc/sysdev/grackle.c
+++ b/arch/powerpc/sysdev/grackle.c
@@ -57,7 +57,7 @@ void __init setup_grackle(struct pci_controller *hose)
{
setup_indirect_pci(hose, 0xfec00000, 0xfee00000, 0);
if (machine_is_compatible("PowerMac1,1"))
- ppc_pci_flags |= PPC_PCI_REASSIGN_ALL_BUS;
+ ppc_pci_add_flags(PPC_PCI_REASSIGN_ALL_BUS);
if (machine_is_compatible("AAPL,PowerBook1998"))
grackle_set_loop_snoop(hose, 1);
#if 0 /* Disabled for now, HW problems ??? */
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 5d7f9f0c93c3..3e0d89dcdba2 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -661,17 +661,6 @@ static inline void mpic_eoi(struct mpic *mpic)
(void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));
}
-#ifdef CONFIG_SMP
-static irqreturn_t mpic_ipi_action(int irq, void *data)
-{
- long ipi = (long)data;
-
- smp_message_recv(ipi);
-
- return IRQ_HANDLED;
-}
-#endif /* CONFIG_SMP */
-
/*
* Linux descriptor level callbacks
*/
@@ -1548,13 +1537,7 @@ unsigned int mpic_get_mcirq(void)
void mpic_request_ipis(void)
{
struct mpic *mpic = mpic_primary;
- long i, err;
- static char *ipi_names[] = {
- "IPI0 (call function)",
- "IPI1 (reschedule)",
- "IPI2 (call function single)",
- "IPI3 (debugger break)",
- };
+ int i;
BUG_ON(mpic == NULL);
printk(KERN_INFO "mpic: requesting IPIs ... \n");
@@ -1563,17 +1546,10 @@ void mpic_request_ipis(void)
unsigned int vipi = irq_create_mapping(mpic->irqhost,
mpic->ipi_vecs[0] + i);
if (vipi == NO_IRQ) {
- printk(KERN_ERR "Failed to map IPI %ld\n", i);
- break;
- }
- err = request_irq(vipi, mpic_ipi_action,
- IRQF_DISABLED|IRQF_PERCPU,
- ipi_names[i], (void *)i);
- if (err) {
- printk(KERN_ERR "Request of irq %d for IPI %ld failed\n",
- vipi, i);
- break;
+ printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]);
+ continue;
}
+ smp_request_message_ipi(vipi, i);
}
}
diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c
index d3e4d61030b5..77fae5f64f2e 100644
--- a/arch/powerpc/sysdev/ppc4xx_pci.c
+++ b/arch/powerpc/sysdev/ppc4xx_pci.c
@@ -194,11 +194,41 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
* 4xx PCI 2.x part
*/
+static int __init ppc4xx_setup_one_pci_PMM(struct pci_controller *hose,
+ void __iomem *reg,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 ma, pcila, pciha;
+
+ if ((plb_addr + size) > 0xffffffffull || !is_power_of_2(size) ||
+ size < 0x1000 || (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+ ma = (0xffffffffu << ilog2(size)) | 1;
+ if (flags & IORESOURCE_PREFETCH)
+ ma |= 2;
+
+ pciha = RES_TO_U32_HIGH(pci_addr);
+ pcila = RES_TO_U32_LOW(pci_addr);
+
+ writel(plb_addr, reg + PCIL0_PMM0LA + (0x10 * index));
+ writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * index));
+ writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * index));
+ writel(ma, reg + PCIL0_PMM0MA + (0x10 * index));
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
void __iomem *reg)
{
- u32 la, ma, pcila, pciha;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
@@ -213,28 +243,29 @@ static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
break;
}
- /* Calculate register values */
- la = res->start;
- pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
-
- ma = res->end + 1 - res->start;
- if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- hose->dn->full_name);
- continue;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pci_PMM(hose, reg,
+ res->start,
+ res->start - hose->pci_mem_offset,
+ res->end + 1 - res->start,
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == hose->pci_mem_offset)
+ found_isa_hole = 1;
}
- ma = (0xffffffffu << ilog2(ma)) | 0x1;
- if (res->flags & IORESOURCE_PREFETCH)
- ma |= 0x2;
-
- /* Program register values */
- writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
- writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
- writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
- writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
- j++;
}
+
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 2 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pci_PMM(hose, reg, hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
}
static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
@@ -352,11 +383,52 @@ static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
* 4xx PCI-X part
*/
+static int __init ppc4xx_setup_one_pcix_POM(struct pci_controller *hose,
+ void __iomem *reg,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 lah, lal, pciah, pcial, sa;
+
+ if (!is_power_of_2(size) || size < 0x1000 ||
+ (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+
+ /* Calculate register values */
+ lah = RES_TO_U32_HIGH(plb_addr);
+ lal = RES_TO_U32_LOW(plb_addr);
+ pciah = RES_TO_U32_HIGH(pci_addr);
+ pcial = RES_TO_U32_LOW(pci_addr);
+ sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+ /* Program register values */
+ if (index == 0) {
+ writel(lah, reg + PCIX0_POM0LAH);
+ writel(lal, reg + PCIX0_POM0LAL);
+ writel(pciah, reg + PCIX0_POM0PCIAH);
+ writel(pcial, reg + PCIX0_POM0PCIAL);
+ writel(sa, reg + PCIX0_POM0SA);
+ } else {
+ writel(lah, reg + PCIX0_POM1LAH);
+ writel(lal, reg + PCIX0_POM1LAL);
+ writel(pciah, reg + PCIX0_POM1PCIAH);
+ writel(pcial, reg + PCIX0_POM1PCIAL);
+ writel(sa, reg + PCIX0_POM1SA);
+ }
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
void __iomem *reg)
{
- u32 lah, lal, pciah, pcial, sa;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
@@ -371,36 +443,29 @@ static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
break;
}
- /* Calculate register values */
- lah = RES_TO_U32_HIGH(res->start);
- lal = RES_TO_U32_LOW(res->start);
- pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
- sa = res->end + 1 - res->start;
- if (!is_power_of_2(sa) || sa < 0x100000 ||
- sa > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- hose->dn->full_name);
- continue;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pcix_POM(hose, reg,
+ res->start,
+ res->start - hose->pci_mem_offset,
+ res->end + 1 - res->start,
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == hose->pci_mem_offset)
+ found_isa_hole = 1;
}
- sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
- /* Program register values */
- if (j == 0) {
- writel(lah, reg + PCIX0_POM0LAH);
- writel(lal, reg + PCIX0_POM0LAL);
- writel(pciah, reg + PCIX0_POM0PCIAH);
- writel(pcial, reg + PCIX0_POM0PCIAL);
- writel(sa, reg + PCIX0_POM0SA);
- } else {
- writel(lah, reg + PCIX0_POM1LAH);
- writel(lal, reg + PCIX0_POM1LAL);
- writel(pciah, reg + PCIX0_POM1PCIAH);
- writel(pcial, reg + PCIX0_POM1PCIAL);
- writel(sa, reg + PCIX0_POM1SA);
- }
- j++;
}
+
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pcix_POM(hose, reg, hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
}
static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
@@ -1317,12 +1382,72 @@ static struct pci_ops ppc4xx_pciex_pci_ops =
.write = ppc4xx_pciex_write_config,
};
+static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port,
+ struct pci_controller *hose,
+ void __iomem *mbase,
+ u64 plb_addr,
+ u64 pci_addr,
+ u64 size,
+ unsigned int flags,
+ int index)
+{
+ u32 lah, lal, pciah, pcial, sa;
+
+ if (!is_power_of_2(size) ||
+ (index < 2 && size < 0x100000) ||
+ (index == 2 && size < 0x100) ||
+ (plb_addr & (size - 1)) != 0) {
+ printk(KERN_WARNING "%s: Resource out of range\n",
+ hose->dn->full_name);
+ return -1;
+ }
+
+ /* Calculate register values */
+ lah = RES_TO_U32_HIGH(plb_addr);
+ lal = RES_TO_U32_LOW(plb_addr);
+ pciah = RES_TO_U32_HIGH(pci_addr);
+ pcial = RES_TO_U32_LOW(pci_addr);
+ sa = (0xffffffffu << ilog2(size)) | 0x1;
+
+ /* Program register values */
+ switch (index) {
+ case 0:
+ out_le32(mbase + PECFG_POM0LAH, pciah);
+ out_le32(mbase + PECFG_POM0LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
+ /* Note that 3 here means enabled | single region */
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
+ break;
+ case 1:
+ out_le32(mbase + PECFG_POM1LAH, pciah);
+ out_le32(mbase + PECFG_POM1LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
+ /* Note that 3 here means enabled | single region */
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
+ break;
+ case 2:
+ out_le32(mbase + PECFG_POM2LAH, pciah);
+ out_le32(mbase + PECFG_POM2LAL, pcial);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
+ /* Note that 3 here means enabled | IO space !!! */
+ dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3);
+ break;
+ }
+
+ return 0;
+}
+
static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
struct pci_controller *hose,
void __iomem *mbase)
{
- u32 lah, lal, pciah, pcial, sa;
- int i, j;
+ int i, j, found_isa_hole = 0;
/* Setup outbound memory windows */
for (i = j = 0; i < 3; i++) {
@@ -1337,53 +1462,38 @@ static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
break;
}
- /* Calculate register values */
- lah = RES_TO_U32_HIGH(res->start);
- lal = RES_TO_U32_LOW(res->start);
- pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
- pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
- sa = res->end + 1 - res->start;
- if (!is_power_of_2(sa) || sa < 0x100000 ||
- sa > 0xffffffffu) {
- printk(KERN_WARNING "%s: Resource out of range\n",
- port->node->full_name);
- continue;
- }
- sa = (0xffffffffu << ilog2(sa)) | 0x1;
-
- /* Program register values */
- switch (j) {
- case 0:
- out_le32(mbase + PECFG_POM0LAH, pciah);
- out_le32(mbase + PECFG_POM0LAL, pcial);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
- break;
- case 1:
- out_le32(mbase + PECFG_POM1LAH, pciah);
- out_le32(mbase + PECFG_POM1LAL, pcial);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
- break;
+ /* Configure the resource */
+ if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ res->start,
+ res->start - hose->pci_mem_offset,
+ res->end + 1 - res->start,
+ res->flags,
+ j) == 0) {
+ j++;
+
+ /* If the resource PCI address is 0 then we have our
+ * ISA memory hole
+ */
+ if (res->start == hose->pci_mem_offset)
+ found_isa_hole = 1;
}
- j++;
}
- /* Configure IO, always 64K starting at 0 */
- if (hose->io_resource.flags & IORESOURCE_IO) {
- lah = RES_TO_U32_HIGH(hose->io_base_phys);
- lal = RES_TO_U32_LOW(hose->io_base_phys);
- out_le32(mbase + PECFG_POM2LAH, 0);
- out_le32(mbase + PECFG_POM2LAL, 0);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
- dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
- }
+ /* Handle ISA memory hole if not already covered */
+ if (j <= 1 && !found_isa_hole && hose->isa_mem_size)
+ if (ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ hose->isa_mem_phys, 0,
+ hose->isa_mem_size, 0, j) == 0)
+ printk(KERN_INFO "%s: Legacy ISA memory support enabled\n",
+ hose->dn->full_name);
+
+ /* Configure IO, always 64K starting at 0. We hard wire it to 64K !
+ * Note also that it -has- to be region index 2 on this HW
+ */
+ if (hose->io_resource.flags & IORESOURCE_IO)
+ ppc4xx_setup_one_pciex_POM(port, hose, mbase,
+ hose->io_base_phys, 0,
+ 0x10000, IORESOURCE_IO, 2);
}
static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index b3b73ae57d6d..01bce3784b0a 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
+#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/bootmem.h>
@@ -38,6 +39,8 @@ static void qe_snums_init(void);
static int qe_sdma_init(void);
static DEFINE_SPINLOCK(qe_lock);
+DEFINE_SPINLOCK(cmxgcr_lock);
+EXPORT_SYMBOL(cmxgcr_lock);
/* QE snum state */
enum qe_snum_state {
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 1d78071aad7d..ebb442ea1917 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -18,6 +18,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/stddef.h>
+#include <linux/spinlock.h>
#include <linux/module.h>
#include <asm/irq.h>
@@ -26,9 +27,6 @@
#include <asm/qe.h>
#include <asm/ucc.h>
-DEFINE_SPINLOCK(cmxgcr_lock);
-EXPORT_SYMBOL(cmxgcr_lock);
-
int ucc_set_qe_mux_mii_mng(unsigned int ucc_num)
{
unsigned long flags;
diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile
index 51d97588e762..9cb03b71b9d6 100644
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -4,7 +4,7 @@ ifdef CONFIG_PPC64
EXTRA_CFLAGS += -mno-minimal-toc
endif
-obj-y += xmon.o setjmp.o start.o nonstdio.o
+obj-y += xmon.o start.o nonstdio.o
ifdef CONFIG_XMON_DISASSEMBLY
obj-y += ppc-dis.o ppc-opc.o
diff --git a/arch/powerpc/xmon/setjmp.S b/arch/powerpc/xmon/setjmp.S
deleted file mode 100644
index 04c0b305ad4a..000000000000
--- a/arch/powerpc/xmon/setjmp.S
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 1996 Paul Mackerras.
- *
- * This program is free software; you can redistribute 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: assert(sizeof(buf) > 23 * sizeof(long))
- */
-#include <asm/processor.h>
-#include <asm/ppc_asm.h>
-#include <asm/asm-offsets.h>
-
-/*
- * Grab the register values as they are now.
- * This won't do a particularily good job because we really
- * want our caller's caller's registers, and our caller has
- * already executed its prologue.
- * ToDo: We could reach back into the caller's save area to do
- * a better job of representing the caller's state (note that
- * that will be different for 32-bit and 64-bit, because of the
- * different ABIs, though).
- */
-_GLOBAL(xmon_save_regs)
- PPC_STL r0,0*SZL(r3)
- PPC_STL r2,2*SZL(r3)
- PPC_STL r3,3*SZL(r3)
- PPC_STL r4,4*SZL(r3)
- PPC_STL r5,5*SZL(r3)
- PPC_STL r6,6*SZL(r3)
- PPC_STL r7,7*SZL(r3)
- PPC_STL r8,8*SZL(r3)
- PPC_STL r9,9*SZL(r3)
- PPC_STL r10,10*SZL(r3)
- PPC_STL r11,11*SZL(r3)
- PPC_STL r12,12*SZL(r3)
- PPC_STL r13,13*SZL(r3)
- PPC_STL r14,14*SZL(r3)
- PPC_STL r15,15*SZL(r3)
- PPC_STL r16,16*SZL(r3)
- PPC_STL r17,17*SZL(r3)
- PPC_STL r18,18*SZL(r3)
- PPC_STL r19,19*SZL(r3)
- PPC_STL r20,20*SZL(r3)
- PPC_STL r21,21*SZL(r3)
- PPC_STL r22,22*SZL(r3)
- PPC_STL r23,23*SZL(r3)
- PPC_STL r24,24*SZL(r3)
- PPC_STL r25,25*SZL(r3)
- PPC_STL r26,26*SZL(r3)
- PPC_STL r27,27*SZL(r3)
- PPC_STL r28,28*SZL(r3)
- PPC_STL r29,29*SZL(r3)
- PPC_STL r30,30*SZL(r3)
- PPC_STL r31,31*SZL(r3)
- /* go up one stack frame for SP */
- PPC_LL r4,0(r1)
- PPC_STL r4,1*SZL(r3)
- /* get caller's LR */
- PPC_LL r0,LRSAVE(r4)
- PPC_STL r0,_NIP-STACK_FRAME_OVERHEAD(r3)
- PPC_STL r0,_LINK-STACK_FRAME_OVERHEAD(r3)
- mfmsr r0
- PPC_STL r0,_MSR-STACK_FRAME_OVERHEAD(r3)
- mfctr r0
- PPC_STL r0,_CTR-STACK_FRAME_OVERHEAD(r3)
- mfxer r0
- PPC_STL r0,_XER-STACK_FRAME_OVERHEAD(r3)
- mfcr r0
- PPC_STL r0,_CCR-STACK_FRAME_OVERHEAD(r3)
- li r0,0
- PPC_STL r0,_TRAP-STACK_FRAME_OVERHEAD(r3)
- blr
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index 076368c8b8a9..8dfad7d9a004 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -41,6 +41,7 @@
#include <asm/spu_priv1.h>
#include <asm/firmware.h>
#include <asm/setjmp.h>
+#include <asm/reg.h>
#ifdef CONFIG_PPC64
#include <asm/hvcall.h>
@@ -159,8 +160,6 @@ static int xmon_no_auto_backtrace;
extern void xmon_enter(void);
extern void xmon_leave(void);
-extern void xmon_save_regs(struct pt_regs *);
-
#ifdef CONFIG_PPC64
#define REG "%.16lx"
#define REGS_PER_LINE 4
@@ -532,7 +531,7 @@ int xmon(struct pt_regs *excp)
struct pt_regs regs;
if (excp == NULL) {
- xmon_save_regs(&regs);
+ ppc_save_regs(&regs);
excp = &regs;
}
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index b4aa5869c7f9..19577aeffd7b 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -43,6 +43,9 @@ config GENERIC_HWEIGHT
config GENERIC_TIME
def_bool y
+config GENERIC_TIME_VSYSCALL
+ def_bool y
+
config GENERIC_CLOCKEVENTS
def_bool y
@@ -66,10 +69,15 @@ config PGSTE
bool
default y if KVM
+config VIRT_CPU_ACCOUNTING
+ def_bool y
+
mainmenu "Linux Kernel Configuration"
config S390
def_bool y
+ select USE_GENERIC_SMP_HELPERS if SMP
+ select HAVE_FUNCTION_TRACER
select HAVE_OPROFILE
select HAVE_KPROBES
select HAVE_KRETPROBES
@@ -226,6 +234,14 @@ config MARCH_Z9_109
Class (z9 BC). The kernel will be slightly faster but will not
work on older machines such as the z990, z890, z900, and z800.
+config MARCH_Z10
+ bool "IBM System z10"
+ help
+ Select this to enable optimizations for IBM System z10. The
+ kernel will be slightly faster but will not work on older
+ machines such as the z990, z890, z900, z800, z9-109, z9-ec
+ and z9-bc.
+
endchoice
config PACK_STACK
@@ -344,16 +360,6 @@ config QDIO
If unsure, say Y.
-config QDIO_DEBUG
- bool "Extended debugging information"
- depends on QDIO
- help
- Say Y here to get extended debugging output in
- /sys/kernel/debug/s390dbf/qdio...
- Warning: this option reduces the performance of the QDIO module.
-
- If unsure, say N.
-
config CHSC_SCH
tristate "Support for CHSC subchannels"
help
@@ -467,22 +473,9 @@ config PAGE_STATES
hypervisor. The ESSA instruction is used to do the states
changes between a page that has content and the unused state.
-config VIRT_TIMER
- bool "Virtual CPU timer support"
- help
- This provides a kernel interface for virtual CPU timers.
- Default is disabled.
-
-config VIRT_CPU_ACCOUNTING
- bool "Base user process accounting on virtual cpu timer"
- depends on VIRT_TIMER
- help
- Select this option to use CPU timer deltas to do user
- process accounting.
-
config APPLDATA_BASE
bool "Linux - VM Monitor Stream, base infrastructure"
- depends on PROC_FS && VIRT_TIMER=y
+ depends on PROC_FS
help
This provides a kernel interface for creating and updating z/VM APPLDATA
monitor records. The monitor records are updated at certain time
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index 792a4e7743ce..578c61f15a4b 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -34,6 +34,7 @@ cflags-$(CONFIG_MARCH_G5) += $(call cc-option,-march=g5)
cflags-$(CONFIG_MARCH_Z900) += $(call cc-option,-march=z900)
cflags-$(CONFIG_MARCH_Z990) += $(call cc-option,-march=z990)
cflags-$(CONFIG_MARCH_Z9_109) += $(call cc-option,-march=z9-109)
+cflags-$(CONFIG_MARCH_Z10) += $(call cc-option,-march=z10)
#KBUILD_IMAGE is necessary for make rpm
KBUILD_IMAGE :=arch/s390/boot/image
diff --git a/arch/s390/appldata/appldata.h b/arch/s390/appldata/appldata.h
index 17a2636fec0a..f0b23fc759ba 100644
--- a/arch/s390/appldata/appldata.h
+++ b/arch/s390/appldata/appldata.h
@@ -26,10 +26,6 @@
#define CTL_APPLDATA_NET_SUM 2125
#define CTL_APPLDATA_PROC 2126
-#define P_INFO(x...) printk(KERN_INFO MY_PRINT_NAME " info: " x)
-#define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x)
-#define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x)
-
struct appldata_ops {
struct list_head list;
struct ctl_table_header *sysctl_header;
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index a06a47cdd5e0..27b70d8a359c 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -10,6 +10,9 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
+#define KMSG_COMPONENT "appldata"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -32,7 +35,6 @@
#include "appldata.h"
-#define MY_PRINT_NAME "appldata" /* for debug messages, etc. */
#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for
sampling interval in
milliseconds */
@@ -390,8 +392,8 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
(unsigned long) ops->data, ops->size,
ops->mod_lvl);
if (rc != 0) {
- P_ERROR("START DIAG 0xDC for %s failed, "
- "return code: %d\n", ops->name, rc);
+ pr_err("Starting the data collection for %s "
+ "failed with rc=%d\n", ops->name, rc);
module_put(ops->owner);
} else
ops->active = 1;
@@ -401,8 +403,8 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
(unsigned long) ops->data, ops->size,
ops->mod_lvl);
if (rc != 0)
- P_ERROR("STOP DIAG 0xDC for %s failed, "
- "return code: %d\n", ops->name, rc);
+ pr_err("Stopping the data collection for %s "
+ "failed with rc=%d\n", ops->name, rc);
module_put(ops->owner);
}
spin_unlock(&appldata_ops_lock);
diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c
index 3b746556e1a3..fa741f84c5b9 100644
--- a/arch/s390/appldata/appldata_net_sum.c
+++ b/arch/s390/appldata/appldata_net_sum.c
@@ -67,7 +67,6 @@ static void appldata_get_net_sum_data(void *data)
int i;
struct appldata_net_sum_data *net_data;
struct net_device *dev;
- struct net_device_stats *stats;
unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors,
tx_errors, rx_dropped, tx_dropped, collisions;
@@ -86,7 +85,8 @@ static void appldata_get_net_sum_data(void *data)
collisions = 0;
read_lock(&dev_base_lock);
for_each_netdev(&init_net, dev) {
- stats = dev->get_stats(dev);
+ const struct net_device_stats *stats = dev_get_stats(dev);
+
rx_packets += stats->rx_packets;
tx_packets += stats->tx_packets;
rx_bytes += stats->rx_bytes;
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c
index eb44f9f8ab91..55c80ffd42b9 100644
--- a/arch/s390/appldata/appldata_os.c
+++ b/arch/s390/appldata/appldata_os.c
@@ -9,6 +9,9 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
+#define KMSG_COMPONENT "appldata"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -22,7 +25,6 @@
#include "appldata.h"
-#define MY_PRINT_NAME "appldata_os" /* for debug messages, etc. */
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
@@ -143,21 +145,16 @@ static void appldata_get_os_data(void *data)
(unsigned long) ops.data, new_size,
ops.mod_lvl);
if (rc != 0)
- P_ERROR("os: START NEW DIAG 0xDC failed, "
- "return code: %d, new size = %i\n", rc,
- new_size);
+ pr_err("Starting a new OS data collection "
+ "failed with rc=%d\n", rc);
rc = appldata_diag(APPLDATA_RECORD_OS_ID,
APPLDATA_STOP_REC,
(unsigned long) ops.data, ops.size,
ops.mod_lvl);
if (rc != 0)
- P_ERROR("os: STOP OLD DIAG 0xDC failed, "
- "return code: %d, old size = %i\n", rc,
- ops.size);
- else
- P_INFO("os: old record size = %i stopped\n",
- ops.size);
+ pr_err("Stopping a faulty OS data "
+ "collection failed with rc=%d\n", rc);
}
ops.size = new_size;
}
@@ -178,8 +175,8 @@ static int __init appldata_os_init(void)
max_size = sizeof(struct appldata_os_data) +
(NR_CPUS * sizeof(struct appldata_os_per_cpu));
if (max_size > APPLDATA_MAX_REC_SIZE) {
- P_ERROR("Max. size of OS record = %i, bigger than maximum "
- "record size (%i)\n", max_size, APPLDATA_MAX_REC_SIZE);
+ pr_err("Maximum OS record size %i exceeds the maximum "
+ "record size %i\n", max_size, APPLDATA_MAX_REC_SIZE);
rc = -ENOMEM;
goto out;
}
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index e33f32b54c08..c42cd898f68b 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -17,6 +17,9 @@
*
*/
+#define KMSG_COMPONENT "aes_s390"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <linux/err.h>
@@ -169,7 +172,8 @@ static int fallback_init_cip(struct crypto_tfm *tfm)
CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(sctx->fallback.cip)) {
- printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ pr_err("Allocating AES fallback algorithm %s failed\n",
+ name);
return PTR_ERR(sctx->fallback.blk);
}
@@ -349,7 +353,8 @@ static int fallback_init_blk(struct crypto_tfm *tfm)
CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(sctx->fallback.blk)) {
- printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ pr_err("Allocating AES fallback algorithm %s failed\n",
+ name);
return PTR_ERR(sctx->fallback.blk);
}
@@ -515,9 +520,8 @@ static int __init aes_s390_init(void)
/* z9 109 and z9 BC/EC only support 128 bit key length */
if (keylen_flag == AES_KEYLEN_128)
- printk(KERN_INFO
- "aes_s390: hardware acceleration only available for "
- "128 bit keys\n");
+ pr_info("AES hardware acceleration is only available for"
+ " 128-bit keys\n");
ret = crypto_register_alg(&aes_alg);
if (ret)
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c
index b9a1ce1f28e4..b1e892a43816 100644
--- a/arch/s390/hypfs/hypfs_diag.c
+++ b/arch/s390/hypfs/hypfs_diag.c
@@ -3,10 +3,13 @@
* Hypervisor filesystem for Linux on s390. Diag 204 and 224
* implementation.
*
- * Copyright (C) IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2008
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
*/
+#define KMSG_COMPONENT "hypfs"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -527,13 +530,14 @@ __init int hypfs_diag_init(void)
int rc;
if (diag204_probe()) {
- printk(KERN_ERR "hypfs: diag 204 not working.");
+ pr_err("The hardware system does not support hypfs\n");
return -ENODATA;
}
rc = diag224_get_name_table();
if (rc) {
diag204_free_buffer();
- printk(KERN_ERR "hypfs: could not get name table.\n");
+ pr_err("The hardware system does not provide all "
+ "functions required by hypfs\n");
}
return rc;
}
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 36313801cd5c..9d4f8e6c0800 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -2,10 +2,13 @@
* arch/s390/hypfs/inode.c
* Hypervisor filesystem for Linux on s390.
*
- * Copyright (C) IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2008
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
*/
+#define KMSG_COMPONENT "hypfs"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -200,7 +203,7 @@ static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
else
rc = hypfs_diag_create_files(sb, sb->s_root);
if (rc) {
- printk(KERN_ERR "hypfs: Update failed\n");
+ pr_err("Updating the hypfs tree failed\n");
hypfs_delete_tree(sb->s_root);
goto out;
}
@@ -252,8 +255,7 @@ static int hypfs_parse_options(char *options, struct super_block *sb)
break;
case opt_err:
default:
- printk(KERN_ERR "hypfs: Unrecognized mount option "
- "\"%s\" or missing value\n", str);
+ pr_err("%s is not a valid mount option\n", str);
return -EINVAL;
}
}
@@ -280,8 +282,8 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
if (!sbi)
return -ENOMEM;
mutex_init(&sbi->lock);
- sbi->uid = current->uid;
- sbi->gid = current->gid;
+ sbi->uid = current_uid();
+ sbi->gid = current_gid();
sb->s_fs_info = sbi;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -317,7 +319,7 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
}
hypfs_update_update(sb);
sb->s_root = root_dentry;
- printk(KERN_INFO "hypfs: Hypervisor filesystem mounted\n");
+ pr_info("Hypervisor filesystem mounted\n");
return 0;
err_tree:
@@ -513,7 +515,7 @@ fail_sysfs:
if (!MACHINE_IS_VM)
hypfs_diag_exit();
fail_diag:
- printk(KERN_ERR "hypfs: Initialization failed with rc = %i.\n", rc);
+ pr_err("Initialization of hypfs failed with rc=%i\n", rc);
return rc;
}
diff --git a/arch/s390/include/asm/auxvec.h b/arch/s390/include/asm/auxvec.h
index 0d340720fd99..a1f153e89133 100644
--- a/arch/s390/include/asm/auxvec.h
+++ b/arch/s390/include/asm/auxvec.h
@@ -1,4 +1,6 @@
#ifndef __ASMS390_AUXVEC_H
#define __ASMS390_AUXVEC_H
+#define AT_SYSINFO_EHDR 33
+
#endif
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index 384e3621e341..7efd0abe8887 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -47,7 +47,10 @@
#endif /* CONFIG_DEBUG_BUGVERBOSE */
-#define BUG() __EMIT_BUG(0)
+#define BUG() do { \
+ __EMIT_BUG(0); \
+ for (;;); \
+} while (0)
#define WARN_ON(x) ({ \
int __ret_warn_on = !!(x); \
diff --git a/arch/s390/include/asm/byteorder.h b/arch/s390/include/asm/byteorder.h
index 1fe2492baa8d..8bcf277c8468 100644
--- a/arch/s390/include/asm/byteorder.h
+++ b/arch/s390/include/asm/byteorder.h
@@ -11,32 +11,39 @@
#include <asm/types.h>
-#ifdef __GNUC__
+#define __BIG_ENDIAN
+
+#ifndef __s390x__
+# define __SWAB_64_THRU_32__
+#endif
#ifdef __s390x__
-static inline __u64 ___arch__swab64p(const __u64 *x)
+static inline __u64 __arch_swab64p(const __u64 *x)
{
__u64 result;
asm volatile("lrvg %0,%1" : "=d" (result) : "m" (*x));
return result;
}
+#define __arch_swab64p __arch_swab64p
-static inline __u64 ___arch__swab64(__u64 x)
+static inline __u64 __arch_swab64(__u64 x)
{
__u64 result;
asm volatile("lrvgr %0,%1" : "=d" (result) : "d" (x));
return result;
}
+#define __arch_swab64 __arch_swab64
-static inline void ___arch__swab64s(__u64 *x)
+static inline void __arch_swab64s(__u64 *x)
{
- *x = ___arch__swab64p(x);
+ *x = __arch_swab64p(x);
}
+#define __arch_swab64s __arch_swab64s
#endif /* __s390x__ */
-static inline __u32 ___arch__swab32p(const __u32 *x)
+static inline __u32 __arch_swab32p(const __u32 *x)
{
__u32 result;
@@ -53,25 +60,20 @@ static inline __u32 ___arch__swab32p(const __u32 *x)
#endif /* __s390x__ */
return result;
}
+#define __arch_swab32p __arch_swab32p
-static inline __u32 ___arch__swab32(__u32 x)
+#ifdef __s390x__
+static inline __u32 __arch_swab32(__u32 x)
{
-#ifndef __s390x__
- return ___arch__swab32p(&x);
-#else /* __s390x__ */
__u32 result;
asm volatile("lrvr %0,%1" : "=d" (result) : "d" (x));
return result;
-#endif /* __s390x__ */
-}
-
-static __inline__ void ___arch__swab32s(__u32 *x)
-{
- *x = ___arch__swab32p(x);
}
+#define __arch_swab32 __arch_swab32
+#endif /* __s390x__ */
-static __inline__ __u16 ___arch__swab16p(const __u16 *x)
+static inline __u16 __arch_swab16p(const __u16 *x)
{
__u16 result;
@@ -86,40 +88,8 @@ static __inline__ __u16 ___arch__swab16p(const __u16 *x)
#endif /* __s390x__ */
return result;
}
+#define __arch_swab16p __arch_swab16p
-static __inline__ __u16 ___arch__swab16(__u16 x)
-{
- return ___arch__swab16p(&x);
-}
-
-static __inline__ void ___arch__swab16s(__u16 *x)
-{
- *x = ___arch__swab16p(x);
-}
-
-#ifdef __s390x__
-#define __arch__swab64(x) ___arch__swab64(x)
-#define __arch__swab64p(x) ___arch__swab64p(x)
-#define __arch__swab64s(x) ___arch__swab64s(x)
-#endif /* __s390x__ */
-#define __arch__swab32(x) ___arch__swab32(x)
-#define __arch__swab16(x) ___arch__swab16(x)
-#define __arch__swab32p(x) ___arch__swab32p(x)
-#define __arch__swab16p(x) ___arch__swab16p(x)
-#define __arch__swab32s(x) ___arch__swab32s(x)
-#define __arch__swab16s(x) ___arch__swab16s(x)
-
-#ifndef __s390x__
-#if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
-# define __BYTEORDER_HAS_U64__
-# define __SWAB_64_THRU_32__
-#endif
-#else /* __s390x__ */
-#define __BYTEORDER_HAS_U64__
-#endif /* __s390x__ */
-
-#endif /* __GNUC__ */
-
-#include <linux/byteorder/big_endian.h>
+#include <linux/byteorder.h>
#endif /* _S390_BYTEORDER_H */
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index 261785ab5b22..d480f39d65e6 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -120,6 +120,10 @@ typedef s390_compat_regs compat_elf_gregset_t;
#include <asm/system.h> /* for save_access_regs */
#include <asm/mmu_context.h>
+#include <asm/vdso.h>
+
+extern unsigned int vdso_enabled;
+
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
@@ -191,4 +195,16 @@ do { \
current->mm->context.noexec == 0; \
})
+#define ARCH_DLINFO \
+do { \
+ if (vdso_enabled) \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, \
+ (unsigned long)current->mm->context.vdso_base); \
+} while (0)
+
+struct linux_binprm;
+
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+int arch_setup_additional_pages(struct linux_binprm *, int);
+
#endif
diff --git a/arch/s390/include/asm/fcx.h b/arch/s390/include/asm/fcx.h
index 8be1f3a58042..ef6170995076 100644
--- a/arch/s390/include/asm/fcx.h
+++ b/arch/s390/include/asm/fcx.h
@@ -248,8 +248,8 @@ struct dcw {
#define TCCB_MAX_SIZE (sizeof(struct tccb_tcah) + \
TCCB_MAX_DCW * sizeof(struct dcw) + \
sizeof(struct tccb_tcat))
-#define TCCB_SAC_DEFAULT 0xf901
-#define TCCB_SAC_INTRG 0xf902
+#define TCCB_SAC_DEFAULT 0x1ffe
+#define TCCB_SAC_INTRG 0x1fff
/**
* struct tccb_tcah - Transport-Command-Area Header (TCAH)
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
new file mode 100644
index 000000000000..5a5bc75e19d4
--- /dev/null
+++ b/arch/s390/include/asm/ftrace.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_S390_FTRACE_H
+#define _ASM_S390_FTRACE_H
+
+#ifndef __ASSEMBLY__
+extern void _mcount(void);
+#endif
+
+#endif /* _ASM_S390_FTRACE_H */
diff --git a/arch/s390/include/asm/isc.h b/arch/s390/include/asm/isc.h
index 34bb8916db4f..1420a1115948 100644
--- a/arch/s390/include/asm/isc.h
+++ b/arch/s390/include/asm/isc.h
@@ -17,6 +17,7 @@
#define CHSC_SCH_ISC 7 /* CHSC subchannels */
/* Adapter interrupts. */
#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
+#define AP_ISC 6 /* adjunct processor (crypto) devices */
/* Functions for registration of I/O interruption subclasses */
void isc_register(unsigned int isc);
diff --git a/arch/s390/include/asm/kvm_virtio.h b/arch/s390/include/asm/kvm_virtio.h
index c13568b9351c..0503936f101f 100644
--- a/arch/s390/include/asm/kvm_virtio.h
+++ b/arch/s390/include/asm/kvm_virtio.h
@@ -50,6 +50,10 @@ struct kvm_vqconfig {
#define KVM_S390_VIRTIO_RESET 1
#define KVM_S390_VIRTIO_SET_STATUS 2
+/* The alignment to use between consumer and producer parts of vring.
+ * This is pagesize for historical reasons. */
+#define KVM_S390_VIRTIO_RING_ALIGN 4096
+
#ifdef __KERNEL__
/* early virtio console setup */
#ifdef CONFIG_S390_GUEST
diff --git a/arch/s390/include/asm/mmu.h b/arch/s390/include/asm/mmu.h
index d2b4ff831477..3b59216e6284 100644
--- a/arch/s390/include/asm/mmu.h
+++ b/arch/s390/include/asm/mmu.h
@@ -6,6 +6,7 @@ typedef struct {
struct list_head pgtable_list;
unsigned long asce_bits;
unsigned long asce_limit;
+ unsigned long vdso_base;
int noexec;
int has_pgste; /* The mmu context has extended page tables */
int alloc_pgste; /* cloned contexts will have extended page tables */
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h
index 991ba939408c..32e8f6aa4384 100644
--- a/arch/s390/include/asm/page.h
+++ b/arch/s390/include/asm/page.h
@@ -152,4 +152,6 @@ void arch_alloc_page(struct page *page, int order);
#include <asm-generic/memory_model.h>
#include <asm-generic/page.h>
+#define __HAVE_ARCH_GATE_AREA 1
+
#endif /* _S390_PAGE_H */
diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h
index f5b2bf3d7c1d..b2658b9220fe 100644
--- a/arch/s390/include/asm/pgalloc.h
+++ b/arch/s390/include/asm/pgalloc.h
@@ -28,6 +28,8 @@ void disable_noexec(struct mm_struct *, struct task_struct *);
static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
{
+ typedef struct { char _[n]; } addrtype;
+
*s = val;
n = (n / 256) - 1;
asm volatile(
@@ -39,7 +41,8 @@ static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
"0: mvc 256(256,%0),0(%0)\n"
" la %0,256(%0)\n"
" brct %1,0b\n"
- : "+a" (s), "+d" (n));
+ : "+a" (s), "+d" (n), "=m" (*(addrtype *) s)
+ : "m" (*(addrtype *) s));
}
static inline void crst_table_init(unsigned long *crst, unsigned long entry)
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 4af80af2a88f..066b99502e09 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -13,6 +13,7 @@
#ifndef __ASM_S390_PROCESSOR_H
#define __ASM_S390_PROCESSOR_H
+#include <linux/linkage.h>
#include <asm/ptrace.h>
#ifdef __KERNEL__
@@ -258,7 +259,7 @@ static inline void enabled_wait(void)
* Function to drop a processor into disabled wait state
*/
-static inline void disabled_wait(unsigned long code)
+static inline void ATTRIB_NORET disabled_wait(unsigned long code)
{
unsigned long ctl_buf;
psw_t dw_psw;
@@ -322,6 +323,7 @@ static inline void disabled_wait(unsigned long code)
: "=m" (ctl_buf)
: "a" (&dw_psw), "a" (&ctl_buf), "m" (dw_psw) : "cc", "0");
#endif /* __s390x__ */
+ while (1);
}
/*
diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h
index 4734c3f05354..27fc1746de15 100644
--- a/arch/s390/include/asm/qdio.h
+++ b/arch/s390/include/asm/qdio.h
@@ -373,16 +373,16 @@ struct qdio_initialize {
#define QDIO_FLAG_SYNC_OUTPUT 0x02
#define QDIO_FLAG_PCI_OUT 0x10
-extern int qdio_initialize(struct qdio_initialize *init_data);
-extern int qdio_allocate(struct qdio_initialize *init_data);
-extern int qdio_establish(struct qdio_initialize *init_data);
+extern int qdio_initialize(struct qdio_initialize *);
+extern int qdio_allocate(struct qdio_initialize *);
+extern int qdio_establish(struct qdio_initialize *);
extern int qdio_activate(struct ccw_device *);
-extern int do_QDIO(struct ccw_device*, unsigned int flags,
- int q_nr, int qidx, int count);
-extern int qdio_cleanup(struct ccw_device*, int how);
-extern int qdio_shutdown(struct ccw_device*, int how);
+extern int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
+ int q_nr, int bufnr, int count);
+extern int qdio_cleanup(struct ccw_device*, int);
+extern int qdio_shutdown(struct ccw_device*, int);
extern int qdio_free(struct ccw_device *);
-extern struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev);
+extern int qdio_get_ssqd_desc(struct ccw_device *dev, struct qdio_ssqd_desc*);
#endif /* __QDIO_H__ */
diff --git a/arch/s390/include/asm/sigp.h b/arch/s390/include/asm/sigp.h
index e16d56f8dfe1..ec403d4304f8 100644
--- a/arch/s390/include/asm/sigp.h
+++ b/arch/s390/include/asm/sigp.h
@@ -61,6 +61,7 @@ typedef enum
{
ec_schedule=0,
ec_call_function,
+ ec_call_function_single,
ec_bit_last
} ec_bit_sig;
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index ae89cf2478fc..024b91e06239 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -91,8 +91,9 @@ extern int __cpu_up (unsigned int cpu);
extern struct mutex smp_cpu_state_mutex;
extern int smp_cpu_polarization[];
-extern int smp_call_function_mask(cpumask_t mask, void (*func)(void *),
- void *info, int wait);
+extern void arch_send_call_function_single_ipi(int cpu);
+extern void arch_send_call_function_ipi(cpumask_t mask);
+
#endif
#ifndef CONFIG_SMP
diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h
index 79d01343f8b0..ad93212d9e16 100644
--- a/arch/s390/include/asm/sysinfo.h
+++ b/arch/s390/include/asm/sysinfo.h
@@ -118,4 +118,15 @@ static inline int stsi(void *sysinfo, int fc, int sel1, int sel2)
return r0;
}
+/*
+ * Service level reporting interface.
+ */
+struct service_level {
+ struct list_head list;
+ void (*seq_print)(struct seq_file *, struct service_level *);
+};
+
+int register_service_level(struct service_level *);
+int unregister_service_level(struct service_level *);
+
#endif /* __ASM_S390_SYSINFO_H */
diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h
index 819e7d99ca0c..024ef42ed6d7 100644
--- a/arch/s390/include/asm/system.h
+++ b/arch/s390/include/asm/system.h
@@ -12,6 +12,7 @@
#define __ASM_SYSTEM_H
#include <linux/kernel.h>
+#include <linux/errno.h>
#include <asm/types.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
@@ -98,13 +99,9 @@ static inline void restore_access_regs(unsigned int *acrs)
prev = __switch_to(prev,next); \
} while (0)
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
extern void account_vtime(struct task_struct *);
extern void account_tick_vtime(struct task_struct *);
extern void account_system_vtime(struct task_struct *);
-#else
-#define account_vtime(x) do { /* empty */ } while (0)
-#endif
#ifdef CONFIG_PFAULT
extern void pfault_irq_init(void);
@@ -413,8 +410,6 @@ __set_psw_mask(unsigned long mask)
#define local_mcck_enable() __set_psw_mask(psw_kernel_bits)
#define local_mcck_disable() __set_psw_mask(psw_kernel_bits & ~PSW_MASK_MCHECK)
-int stfle(unsigned long long *list, int doublewords);
-
#ifdef CONFIG_SMP
extern void smp_ctl_set_bit(int cr, int bit);
@@ -438,6 +433,23 @@ static inline unsigned int stfl(void)
return S390_lowcore.stfl_fac_list;
}
+static inline int __stfle(unsigned long long *list, int doublewords)
+{
+ typedef struct { unsigned long long _[doublewords]; } addrtype;
+ register unsigned long __nr asm("0") = doublewords - 1;
+
+ asm volatile(".insn s,0xb2b00000,%0" /* stfle */
+ : "=m" (*(addrtype *) list), "+d" (__nr) : : "cc");
+ return __nr + 1;
+}
+
+static inline int stfle(unsigned long long *list, int doublewords)
+{
+ if (!(stfl() & (1UL << 24)))
+ return -EOPNOTSUPP;
+ return __stfle(list, doublewords);
+}
+
static inline unsigned short stap(void)
{
unsigned short cpu_address;
diff --git a/arch/s390/include/asm/timer.h b/arch/s390/include/asm/timer.h
index d98d79e35cd6..61705d60f995 100644
--- a/arch/s390/include/asm/timer.h
+++ b/arch/s390/include/asm/timer.h
@@ -48,18 +48,9 @@ extern int del_virt_timer(struct vtimer_list *timer);
extern void init_cpu_vtimer(void);
extern void vtime_init(void);
-#ifdef CONFIG_VIRT_TIMER
-
extern void vtime_start_cpu_timer(void);
extern void vtime_stop_cpu_timer(void);
-#else
-
-static inline void vtime_start_cpu_timer(void) { }
-static inline void vtime_stop_cpu_timer(void) { }
-
-#endif /* CONFIG_VIRT_TIMER */
-
#endif /* __KERNEL__ */
#endif /* _ASM_S390_TIMER_H */
diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h
index d96c91643458..c93eb50e1d09 100644
--- a/arch/s390/include/asm/topology.h
+++ b/arch/s390/include/asm/topology.h
@@ -6,10 +6,12 @@
#define mc_capable() (1)
cpumask_t cpu_coregroup_map(unsigned int cpu);
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu);
extern cpumask_t cpu_core_map[NR_CPUS];
#define topology_core_siblings(cpu) (cpu_core_map[cpu])
+#define topology_core_cpumask(cpu) (&cpu_core_map[cpu])
int topology_set_cpu_management(int fc);
void topology_schedule_update(void);
diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h
new file mode 100644
index 000000000000..a44f4fe16a35
--- /dev/null
+++ b/arch/s390/include/asm/vdso.h
@@ -0,0 +1,39 @@
+#ifndef __S390_VDSO_H__
+#define __S390_VDSO_H__
+
+#ifdef __KERNEL__
+
+/* Default link addresses for the vDSOs */
+#define VDSO32_LBASE 0
+#define VDSO64_LBASE 0
+
+#define VDSO_VERSION_STRING LINUX_2.6.26
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Note about this structure:
+ *
+ * NEVER USE THIS IN USERSPACE CODE DIRECTLY. The layout of this
+ * structure is supposed to be known only to the function in the vdso
+ * itself and may change without notice.
+ */
+
+struct vdso_data {
+ __u64 tb_update_count; /* Timebase atomicity ctr 0x00 */
+ __u64 xtime_tod_stamp; /* TOD clock for xtime 0x08 */
+ __u64 xtime_clock_sec; /* Kernel time 0x10 */
+ __u64 xtime_clock_nsec; /* 0x18 */
+ __u64 wtom_clock_sec; /* Wall to monotonic clock 0x20 */
+ __u64 wtom_clock_nsec; /* 0x28 */
+ __u32 tz_minuteswest; /* Minutes west of Greenwich 0x30 */
+ __u32 tz_dsttime; /* Type of dst correction 0x34 */
+};
+
+extern struct vdso_data *vdso_data;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* __S390_VDSO_H__ */
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 50f657e77344..3edc6c6f258b 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -2,6 +2,11 @@
# Makefile for the linux kernel.
#
+ifdef CONFIG_FUNCTION_TRACER
+# Do not trace early boot code
+CFLAGS_REMOVE_early.o = -pg
+endif
+
#
# Passing null pointers is ok for smp code, since we access the lowcore here.
#
@@ -12,9 +17,10 @@ CFLAGS_smp.o := -Wno-nonnull
#
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
-obj-y := bitmap.o traps.o time.o process.o base.o early.o \
- setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
- s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o
+obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o \
+ processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
+ s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o \
+ vdso.o vtime.o
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
@@ -30,12 +36,16 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
compat_wrapper.o compat_exec_domain.o \
$(compat-obj-y)
-obj-$(CONFIG_VIRT_TIMER) += vtime.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_FUNCTION_TRACER) += mcount.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)
+# vdso
+obj-$(CONFIG_64BIT) += vdso64/
+obj-$(CONFIG_32BIT) += vdso32/
+obj-$(CONFIG_COMPAT) += vdso32/
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index 3d144e6020c6..e641f60bac99 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -6,6 +6,7 @@
#include <linux/sched.h>
#include <linux/kbuild.h>
+#include <asm/vdso.h>
int main(void)
{
@@ -38,5 +39,19 @@ int main(void)
DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
DEFINE(__SF_GPRS, offsetof(struct stack_frame, gprs));
DEFINE(__SF_EMPTY, offsetof(struct stack_frame, empty1));
+ BLANK();
+ /* timeval/timezone offsets for use by vdso */
+ DEFINE(__VDSO_UPD_COUNT, offsetof(struct vdso_data, tb_update_count));
+ DEFINE(__VDSO_XTIME_STAMP, offsetof(struct vdso_data, xtime_tod_stamp));
+ DEFINE(__VDSO_XTIME_SEC, offsetof(struct vdso_data, xtime_clock_sec));
+ DEFINE(__VDSO_XTIME_NSEC, offsetof(struct vdso_data, xtime_clock_nsec));
+ DEFINE(__VDSO_WTOM_SEC, offsetof(struct vdso_data, wtom_clock_sec));
+ DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec));
+ DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest));
+ /* constants used by the vdso */
+ DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
+ DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
+
return 0;
}
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 4646382af34f..6cc87d8c8682 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -148,9 +148,9 @@ asmlinkage long sys32_getresuid16(u16 __user *ruid, u16 __user *euid, u16 __user
{
int retval;
- if (!(retval = put_user(high2lowuid(current->uid), ruid)) &&
- !(retval = put_user(high2lowuid(current->euid), euid)))
- retval = put_user(high2lowuid(current->suid), suid);
+ if (!(retval = put_user(high2lowuid(current->cred->uid), ruid)) &&
+ !(retval = put_user(high2lowuid(current->cred->euid), euid)))
+ retval = put_user(high2lowuid(current->cred->suid), suid);
return retval;
}
@@ -165,9 +165,9 @@ asmlinkage long sys32_getresgid16(u16 __user *rgid, u16 __user *egid, u16 __user
{
int retval;
- if (!(retval = put_user(high2lowgid(current->gid), rgid)) &&
- !(retval = put_user(high2lowgid(current->egid), egid)))
- retval = put_user(high2lowgid(current->sgid), sgid);
+ if (!(retval = put_user(high2lowgid(current->cred->gid), rgid)) &&
+ !(retval = put_user(high2lowgid(current->cred->egid), egid)))
+ retval = put_user(high2lowgid(current->cred->sgid), sgid);
return retval;
}
@@ -217,20 +217,20 @@ asmlinkage long sys32_getgroups16(int gidsetsize, u16 __user *grouplist)
if (gidsetsize < 0)
return -EINVAL;
- get_group_info(current->group_info);
- i = current->group_info->ngroups;
+ get_group_info(current->cred->group_info);
+ i = current->cred->group_info->ngroups;
if (gidsetsize) {
if (i > gidsetsize) {
i = -EINVAL;
goto out;
}
- if (groups16_to_user(grouplist, current->group_info)) {
+ if (groups16_to_user(grouplist, current->cred->group_info)) {
i = -EFAULT;
goto out;
}
}
out:
- put_group_info(current->group_info);
+ put_group_info(current->cred->group_info);
return i;
}
@@ -261,22 +261,22 @@ asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist)
asmlinkage long sys32_getuid16(void)
{
- return high2lowuid(current->uid);
+ return high2lowuid(current->cred->uid);
}
asmlinkage long sys32_geteuid16(void)
{
- return high2lowuid(current->euid);
+ return high2lowuid(current->cred->euid);
}
asmlinkage long sys32_getgid16(void)
{
- return high2lowgid(current->gid);
+ return high2lowgid(current->cred->gid);
}
asmlinkage long sys32_getegid16(void)
{
- return high2lowgid(current->egid);
+ return high2lowgid(current->cred->egid);
}
/*
diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c
index d8c1131e0815..3e8b8816f309 100644
--- a/arch/s390/kernel/cpcmd.c
+++ b/arch/s390/kernel/cpcmd.c
@@ -7,6 +7,9 @@
* Christian Borntraeger (cborntra@de.ibm.com),
*/
+#define KMSG_COMPONENT "cpcmd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -104,8 +107,8 @@ int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
(((unsigned long)response + rlen) >> 31)) {
lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA);
if (!lowbuf) {
- printk(KERN_WARNING
- "cpcmd: could not allocate response buffer\n");
+ pr_warning("The cpcmd kernel function failed to "
+ "allocate a response buffer\n");
return -ENOMEM;
}
spin_lock_irqsave(&cpcmd_lock, flags);
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index d80fcd4a7fe1..ba03fc0a3a56 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -10,6 +10,9 @@
* Bugreports to: <Linux390@de.ibm.com>
*/
+#define KMSG_COMPONENT "s390dbf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -388,7 +391,7 @@ debug_info_copy(debug_info_t* in, int mode)
debug_info_free(rc);
} while (1);
- if(!rc || (mode == NO_AREAS))
+ if (mode == NO_AREAS)
goto out;
for(i = 0; i < in->nr_areas; i++){
@@ -693,8 +696,8 @@ debug_info_t *debug_register_mode(const char *name, int pages_per_area,
/* Since debugfs currently does not support uid/gid other than root, */
/* we do not allow gid/uid != 0 until we get support for that. */
if ((uid != 0) || (gid != 0))
- printk(KERN_WARNING "debug: Warning - Currently only uid/gid "
- "= 0 are supported. Using root as owner now!");
+ pr_warning("Root becomes the owner of all s390dbf files "
+ "in sysfs\n");
if (!initialized)
BUG();
mutex_lock(&debug_mutex);
@@ -709,7 +712,7 @@ debug_info_t *debug_register_mode(const char *name, int pages_per_area,
debug_register_view(rc, &debug_pages_view);
out:
if (!rc){
- printk(KERN_ERR "debug: debug_register failed for %s\n",name);
+ pr_err("Registering debug feature %s failed\n", name);
}
mutex_unlock(&debug_mutex);
return rc;
@@ -763,8 +766,8 @@ debug_set_size(debug_info_t* id, int nr_areas, int pages_per_area)
if(pages_per_area > 0){
new_areas = debug_areas_alloc(pages_per_area, nr_areas);
if(!new_areas) {
- printk(KERN_WARNING "debug: could not allocate memory "\
- "for pagenumber: %i\n",pages_per_area);
+ pr_info("Allocating memory for %i pages failed\n",
+ pages_per_area);
rc = -ENOMEM;
goto out;
}
@@ -780,8 +783,7 @@ debug_set_size(debug_info_t* id, int nr_areas, int pages_per_area)
memset(id->active_entries,0,sizeof(int)*id->nr_areas);
memset(id->active_pages, 0, sizeof(int)*id->nr_areas);
spin_unlock_irqrestore(&id->lock,flags);
- printk(KERN_INFO "debug: %s: set new size (%i pages)\n"\
- ,id->name, pages_per_area);
+ pr_info("%s: set new size (%i pages)\n" ,id->name, pages_per_area);
out:
return rc;
}
@@ -800,10 +802,9 @@ debug_set_level(debug_info_t* id, int new_level)
spin_lock_irqsave(&id->lock,flags);
if(new_level == DEBUG_OFF_LEVEL){
id->level = DEBUG_OFF_LEVEL;
- printk(KERN_INFO "debug: %s: switched off\n",id->name);
+ pr_info("%s: switched off\n",id->name);
} else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) {
- printk(KERN_INFO
- "debug: %s: level %i is out of range (%i - %i)\n",
+ pr_info("%s: level %i is out of range (%i - %i)\n",
id->name, new_level, 0, DEBUG_MAX_LEVEL);
} else {
id->level = new_level;
@@ -1108,8 +1109,8 @@ debug_register_view(debug_info_t * id, struct debug_view *view)
pde = debugfs_create_file(view->name, mode, id->debugfs_root_entry,
id , &debug_file_ops);
if (!pde){
- printk(KERN_WARNING "debug: debugfs_create_file() failed!"\
- " Cannot register view %s/%s\n", id->name,view->name);
+ pr_err("Registering view %s/%s failed due to out of "
+ "memory\n", id->name,view->name);
rc = -1;
goto out;
}
@@ -1119,10 +1120,8 @@ debug_register_view(debug_info_t * id, struct debug_view *view)
break;
}
if (i == DEBUG_MAX_VIEWS) {
- printk(KERN_WARNING "debug: cannot register view %s/%s\n",
- id->name,view->name);
- printk(KERN_WARNING
- "debug: maximum number of views reached (%i)!\n", i);
+ pr_err("Registering view %s/%s would exceed the maximum "
+ "number of views %i\n", id->name, view->name, i);
debugfs_remove(pde);
rc = -1;
} else {
@@ -1303,7 +1302,8 @@ debug_input_level_fn(debug_info_t * id, struct debug_view *view,
new_level = debug_get_uint(str);
}
if(new_level < 0) {
- printk(KERN_INFO "debug: level `%s` is not valid\n", str);
+ pr_warning("%s is not a valid level for a debug "
+ "feature\n", str);
rc = -EINVAL;
} else {
debug_set_level(id, new_level);
@@ -1380,7 +1380,8 @@ debug_input_flush_fn(debug_info_t * id, struct debug_view *view,
goto out;
}
- printk(KERN_INFO "debug: area `%c` is not valid\n", input_buf[0]);
+ pr_info("Flushing debug data failed because %c is not a valid "
+ "area\n", input_buf[0]);
out:
*offset += user_len;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 198ea18a534d..55de521aef77 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -109,13 +109,6 @@ STACK_SIZE = 1 << STACK_SHIFT
* R15 - kernel stack pointer
*/
- .macro STORE_TIMER lc_offset
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
- stpt \lc_offset
-#endif
- .endm
-
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.macro UPDATE_VTIME lc_from,lc_to,lc_sum
lm %r10,%r11,\lc_from
sl %r10,\lc_to
@@ -128,7 +121,6 @@ STACK_SIZE = 1 << STACK_SHIFT
al %r10,BASED(.Lc_1)
1: stm %r10,%r11,\lc_sum
.endm
-#endif
.macro SAVE_ALL_BASE savearea
stm %r12,%r15,\savearea
@@ -198,7 +190,7 @@ STACK_SIZE = 1 << STACK_SHIFT
ni \psworg+1,0xfd # clear wait state bit
.endif
lm %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user
- STORE_TIMER __LC_EXIT_TIMER
+ stpt __LC_EXIT_TIMER
lpsw \psworg # back to caller
.endm
@@ -247,20 +239,18 @@ __critical_start:
.globl system_call
system_call:
- STORE_TIMER __LC_SYNC_ENTER_TIMER
+ stpt __LC_SYNC_ENTER_TIMER
sysc_saveall:
SAVE_ALL_BASE __LC_SAVE_AREA
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA
lh %r7,0x8a # get svc number from lowcore
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
sysc_vtime:
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
sysc_stime:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
sysc_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
sysc_do_svc:
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
ltr %r7,%r7 # test for svc 0
@@ -436,7 +426,7 @@ ret_from_fork:
basr %r14,%r1
TRACE_IRQS_ON
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
- b BASED(sysc_return)
+ b BASED(sysc_tracenogo)
#
# kernel_execve function needs to deal with pt_regs that is not
@@ -490,20 +480,18 @@ pgm_check_handler:
* we just ignore the PER event (FIXME: is there anything we have to do
* for LPSW?).
*/
- STORE_TIMER __LC_SYNC_ENTER_TIMER
+ stpt __LC_SYNC_ENTER_TIMER
SAVE_ALL_BASE __LC_SAVE_AREA
tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception
bnz BASED(pgm_per) # got per exception -> special case
SAVE_ALL_SYNC __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_PGM_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
bz BASED(pgm_no_vtime)
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
pgm_no_vtime:
-#endif
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
l %r3,__LC_PGM_ILC # load program interruption code
@@ -536,14 +524,12 @@ pgm_per:
pgm_per_std:
SAVE_ALL_SYNC __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_PGM_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
bz BASED(pgm_no_vtime2)
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
pgm_no_vtime2:
-#endif
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
l %r1,__TI_task(%r9)
@@ -565,11 +551,9 @@ pgm_no_vtime2:
pgm_svcper:
SAVE_ALL_SYNC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
lh %r7,0x8a # get svc number from lowcore
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
@@ -599,19 +583,17 @@ kernel_per:
.globl io_int_handler
io_int_handler:
- STORE_TIMER __LC_ASYNC_ENTER_TIMER
+ stpt __LC_ASYNC_ENTER_TIMER
stck __LC_INT_CLOCK
SAVE_ALL_BASE __LC_SAVE_AREA+16
SAVE_ALL_ASYNC __LC_IO_OLD_PSW,__LC_SAVE_AREA+16
CREATE_STACK_FRAME __LC_IO_OLD_PSW,__LC_SAVE_AREA+16
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
bz BASED(io_no_vtime)
UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
io_no_vtime:
-#endif
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
l %r1,BASED(.Ldo_IRQ) # load address of do_IRQ
@@ -741,19 +723,17 @@ io_notify_resume:
.globl ext_int_handler
ext_int_handler:
- STORE_TIMER __LC_ASYNC_ENTER_TIMER
+ stpt __LC_ASYNC_ENTER_TIMER
stck __LC_INT_CLOCK
SAVE_ALL_BASE __LC_SAVE_AREA+16
SAVE_ALL_ASYNC __LC_EXT_OLD_PSW,__LC_SAVE_AREA+16
CREATE_STACK_FRAME __LC_EXT_OLD_PSW,__LC_SAVE_AREA+16
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
bz BASED(ext_no_vtime)
UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
ext_no_vtime:
-#endif
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
la %r2,SP_PTREGS(%r15) # address of register-save area
@@ -776,7 +756,6 @@ mcck_int_handler:
la %r12,__LC_MCK_OLD_PSW
tm __LC_MCCK_CODE,0x80 # system damage?
bo BASED(mcck_int_main) # yes -> rest of mcck code invalid
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_SAVE_AREA+52(8),__LC_ASYNC_ENTER_TIMER
mvc __LC_ASYNC_ENTER_TIMER(8),__LC_CPU_TIMER_SAVE_AREA
tm __LC_MCCK_CODE+5,0x02 # stored cpu timer value valid?
@@ -793,9 +772,7 @@ mcck_int_handler:
la %r14,__LC_LAST_UPDATE_TIMER
0: spt 0(%r14)
mvc __LC_ASYNC_ENTER_TIMER(8),0(%r14)
-1:
-#endif
- tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
+1: tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
bno BASED(mcck_int_main) # no -> skip cleanup critical
tm __LC_MCK_OLD_PSW+1,0x01 # test problem state bit
bnz BASED(mcck_int_main) # from user -> load async stack
@@ -812,7 +789,6 @@ mcck_int_main:
be BASED(0f)
l %r15,__LC_PANIC_STACK # load panic stack
0: CREATE_STACK_FRAME __LC_MCK_OLD_PSW,__LC_SAVE_AREA+32
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm __LC_MCCK_CODE+2,0x08 # mwp of old psw valid?
bno BASED(mcck_no_vtime) # no -> skip cleanup critical
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
@@ -821,7 +797,6 @@ mcck_int_main:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
mcck_no_vtime:
-#endif
l %r9,__LC_THREAD_INFO # load pointer to thread_info struct
la %r2,SP_PTREGS(%r15) # load pt_regs
l %r1,BASED(.Ls390_mcck)
@@ -843,16 +818,13 @@ mcck_no_vtime:
mcck_return:
mvc __LC_RETURN_MCCK_PSW(8),SP_PSW(%r15) # move return PSW
ni __LC_RETURN_MCCK_PSW+1,0xfd # clear wait state bit
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_ASYNC_ENTER_TIMER(8),__LC_SAVE_AREA+52
tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ?
bno BASED(0f)
lm %r0,%r15,SP_R0(%r15) # load gprs 0-15
stpt __LC_EXIT_TIMER
lpsw __LC_RETURN_MCCK_PSW # back to caller
-0:
-#endif
- lm %r0,%r15,SP_R0(%r15) # load gprs 0-15
+0: lm %r0,%r15,SP_R0(%r15) # load gprs 0-15
lpsw __LC_RETURN_MCCK_PSW # back to caller
RESTORE_ALL __LC_RETURN_MCCK_PSW,0
@@ -976,13 +948,11 @@ cleanup_system_call:
b BASED(1f)
0: la %r12,__LC_SAVE_AREA+32
1:
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+4)
bh BASED(0f)
mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER
0: clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+8)
bhe BASED(cleanup_vtime)
-#endif
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn)
bh BASED(0f)
mvc __LC_SAVE_AREA(16),0(%r12)
@@ -993,7 +963,6 @@ cleanup_system_call:
l %r12,__LC_SAVE_AREA+48 # argh
st %r15,12(%r12)
lh %r7,0x8a
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
cleanup_vtime:
clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12)
bhe BASED(cleanup_stime)
@@ -1004,18 +973,15 @@ cleanup_stime:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
cleanup_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
mvc __LC_RETURN_PSW+4(4),BASED(cleanup_table_system_call+4)
la %r12,__LC_RETURN_PSW
br %r14
cleanup_system_call_insn:
.long sysc_saveall + 0x80000000
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.long system_call + 0x80000000
.long sysc_vtime + 0x80000000
.long sysc_stime + 0x80000000
.long sysc_update + 0x80000000
-#endif
cleanup_sysc_return:
mvc __LC_RETURN_PSW(4),0(%r12)
@@ -1026,11 +992,9 @@ cleanup_sysc_return:
cleanup_sysc_leave:
clc 4(4,%r12),BASED(cleanup_sysc_leave_insn)
be BASED(2f)
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
clc 4(4,%r12),BASED(cleanup_sysc_leave_insn+4)
be BASED(2f)
-#endif
mvc __LC_RETURN_PSW(8),SP_PSW(%r15)
c %r12,BASED(.Lmck_old_psw)
bne BASED(0f)
@@ -1043,9 +1007,7 @@ cleanup_sysc_leave:
br %r14
cleanup_sysc_leave_insn:
.long sysc_done - 4 + 0x80000000
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.long sysc_done - 8 + 0x80000000
-#endif
cleanup_io_return:
mvc __LC_RETURN_PSW(4),0(%r12)
@@ -1056,11 +1018,9 @@ cleanup_io_return:
cleanup_io_leave:
clc 4(4,%r12),BASED(cleanup_io_leave_insn)
be BASED(2f)
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
clc 4(4,%r12),BASED(cleanup_io_leave_insn+4)
be BASED(2f)
-#endif
mvc __LC_RETURN_PSW(8),SP_PSW(%r15)
c %r12,BASED(.Lmck_old_psw)
bne BASED(0f)
@@ -1073,9 +1033,7 @@ cleanup_io_leave:
br %r14
cleanup_io_leave_insn:
.long io_done - 4 + 0x80000000
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.long io_done - 8 + 0x80000000
-#endif
/*
* Integer constants
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 89c121ae6339..16bb4fd1a403 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -96,20 +96,12 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
#define LOCKDEP_SYS_EXIT
#endif
- .macro STORE_TIMER lc_offset
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
- stpt \lc_offset
-#endif
- .endm
-
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.macro UPDATE_VTIME lc_from,lc_to,lc_sum
lg %r10,\lc_from
slg %r10,\lc_to
alg %r10,\lc_sum
stg %r10,\lc_sum
.endm
-#endif
/*
* Register usage in interrupt handlers:
@@ -186,7 +178,7 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
ni \psworg+1,0xfd # clear wait state bit
.endif
lmg %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user
- STORE_TIMER __LC_EXIT_TIMER
+ stpt __LC_EXIT_TIMER
lpswe \psworg # back to caller
.endm
@@ -233,20 +225,18 @@ __critical_start:
.globl system_call
system_call:
- STORE_TIMER __LC_SYNC_ENTER_TIMER
+ stpt __LC_SYNC_ENTER_TIMER
sysc_saveall:
SAVE_ALL_BASE __LC_SAVE_AREA
SAVE_ALL_SVC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA
llgh %r7,__LC_SVC_INT_CODE # get svc number from lowcore
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
sysc_vtime:
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
sysc_stime:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
sysc_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
sysc_do_svc:
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
ltgr %r7,%r7 # test for svc 0
@@ -417,7 +407,7 @@ ret_from_fork:
0: brasl %r14,schedule_tail
TRACE_IRQS_ON
stosm 24(%r15),0x03 # reenable interrupts
- j sysc_return
+ j sysc_tracenogo
#
# kernel_execve function needs to deal with pt_regs that is not
@@ -469,20 +459,18 @@ pgm_check_handler:
* we just ignore the PER event (FIXME: is there anything we have to do
* for LPSW?).
*/
- STORE_TIMER __LC_SYNC_ENTER_TIMER
+ stpt __LC_SYNC_ENTER_TIMER
SAVE_ALL_BASE __LC_SAVE_AREA
tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception
jnz pgm_per # got per exception -> special case
SAVE_ALL_SYNC __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_PGM_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
jz pgm_no_vtime
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
pgm_no_vtime:
-#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
mvc SP_ARGS(8,%r15),__LC_LAST_BREAK
TRACE_IRQS_OFF
@@ -516,14 +504,12 @@ pgm_per:
pgm_per_std:
SAVE_ALL_SYNC __LC_PGM_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_PGM_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
jz pgm_no_vtime2
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
pgm_no_vtime2:
-#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
lg %r1,__TI_task(%r9)
@@ -545,11 +531,9 @@ pgm_no_vtime2:
pgm_svcper:
SAVE_ALL_SYNC __LC_SVC_OLD_PSW,__LC_SAVE_AREA
CREATE_STACK_FRAME __LC_SVC_OLD_PSW,__LC_SAVE_AREA
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
llgh %r7,__LC_SVC_INT_CODE # get svc number from lowcore
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
lg %r1,__TI_task(%r9)
@@ -575,19 +559,17 @@ kernel_per:
*/
.globl io_int_handler
io_int_handler:
- STORE_TIMER __LC_ASYNC_ENTER_TIMER
+ stpt __LC_ASYNC_ENTER_TIMER
stck __LC_INT_CLOCK
SAVE_ALL_BASE __LC_SAVE_AREA+32
SAVE_ALL_ASYNC __LC_IO_OLD_PSW,__LC_SAVE_AREA+32
CREATE_STACK_FRAME __LC_IO_OLD_PSW,__LC_SAVE_AREA+32
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
jz io_no_vtime
UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
io_no_vtime:
-#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
la %r2,SP_PTREGS(%r15) # address of register-save area
@@ -739,19 +721,17 @@ io_notify_resume:
*/
.globl ext_int_handler
ext_int_handler:
- STORE_TIMER __LC_ASYNC_ENTER_TIMER
+ stpt __LC_ASYNC_ENTER_TIMER
stck __LC_INT_CLOCK
SAVE_ALL_BASE __LC_SAVE_AREA+32
SAVE_ALL_ASYNC __LC_EXT_OLD_PSW,__LC_SAVE_AREA+32
CREATE_STACK_FRAME __LC_EXT_OLD_PSW,__LC_SAVE_AREA+32
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
jz ext_no_vtime
UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
ext_no_vtime:
-#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
TRACE_IRQS_OFF
la %r2,SP_PTREGS(%r15) # address of register-save area
@@ -773,7 +753,6 @@ mcck_int_handler:
la %r12,__LC_MCK_OLD_PSW
tm __LC_MCCK_CODE,0x80 # system damage?
jo mcck_int_main # yes -> rest of mcck code invalid
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
la %r14,4095
mvc __LC_SAVE_AREA+104(8),__LC_ASYNC_ENTER_TIMER
mvc __LC_ASYNC_ENTER_TIMER(8),__LC_CPU_TIMER_SAVE_AREA-4095(%r14)
@@ -791,9 +770,7 @@ mcck_int_handler:
la %r14,__LC_LAST_UPDATE_TIMER
0: spt 0(%r14)
mvc __LC_ASYNC_ENTER_TIMER(8),0(%r14)
-1:
-#endif
- tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
+1: tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
jno mcck_int_main # no -> skip cleanup critical
tm __LC_MCK_OLD_PSW+1,0x01 # test problem state bit
jnz mcck_int_main # from user -> load kernel stack
@@ -809,7 +786,6 @@ mcck_int_main:
jz 0f
lg %r15,__LC_PANIC_STACK # load panic stack
0: CREATE_STACK_FRAME __LC_MCK_OLD_PSW,__LC_SAVE_AREA+64
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
tm __LC_MCCK_CODE+2,0x08 # mwp of old psw valid?
jno mcck_no_vtime # no -> no timer update
tm SP_PSW+1(%r15),0x01 # interrupting from user ?
@@ -818,7 +794,6 @@ mcck_int_main:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER
mcck_no_vtime:
-#endif
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
la %r2,SP_PTREGS(%r15) # load pt_regs
brasl %r14,s390_do_machine_check
@@ -839,14 +814,11 @@ mcck_return:
mvc __LC_RETURN_MCCK_PSW(16),SP_PSW(%r15) # move return PSW
ni __LC_RETURN_MCCK_PSW+1,0xfd # clear wait state bit
lmg %r0,%r15,SP_R0(%r15) # load gprs 0-15
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_ASYNC_ENTER_TIMER(8),__LC_SAVE_AREA+104
tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ?
jno 0f
stpt __LC_EXIT_TIMER
-0:
-#endif
- lpswe __LC_RETURN_MCCK_PSW # back to caller
+0: lpswe __LC_RETURN_MCCK_PSW # back to caller
/*
* Restart interruption handler, kick starter for additional CPUs
@@ -964,13 +936,11 @@ cleanup_system_call:
j 1f
0: la %r12,__LC_SAVE_AREA+64
1:
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+8)
jh 0f
mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER
0: clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+16)
jhe cleanup_vtime
-#endif
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn)
jh 0f
mvc __LC_SAVE_AREA(32),0(%r12)
@@ -981,7 +951,6 @@ cleanup_system_call:
lg %r12,__LC_SAVE_AREA+96 # argh
stg %r15,24(%r12)
llgh %r7,__LC_SVC_INT_CODE
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
cleanup_vtime:
clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24)
jhe cleanup_stime
@@ -992,18 +961,15 @@ cleanup_stime:
UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER
cleanup_update:
mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
-#endif
mvc __LC_RETURN_PSW+8(8),BASED(cleanup_table_system_call+8)
la %r12,__LC_RETURN_PSW
br %r14
cleanup_system_call_insn:
.quad sysc_saveall
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.quad system_call
.quad sysc_vtime
.quad sysc_stime
.quad sysc_update
-#endif
cleanup_sysc_return:
mvc __LC_RETURN_PSW(8),0(%r12)
@@ -1014,11 +980,9 @@ cleanup_sysc_return:
cleanup_sysc_leave:
clc 8(8,%r12),BASED(cleanup_sysc_leave_insn)
je 2f
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
clc 8(8,%r12),BASED(cleanup_sysc_leave_insn+8)
je 2f
-#endif
mvc __LC_RETURN_PSW(16),SP_PSW(%r15)
cghi %r12,__LC_MCK_OLD_PSW
jne 0f
@@ -1031,9 +995,7 @@ cleanup_sysc_leave:
br %r14
cleanup_sysc_leave_insn:
.quad sysc_done - 4
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.quad sysc_done - 8
-#endif
cleanup_io_return:
mvc __LC_RETURN_PSW(8),0(%r12)
@@ -1044,11 +1006,9 @@ cleanup_io_return:
cleanup_io_leave:
clc 8(8,%r12),BASED(cleanup_io_leave_insn)
je 2f
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER
clc 8(8,%r12),BASED(cleanup_io_leave_insn+8)
je 2f
-#endif
mvc __LC_RETURN_PSW(16),SP_PSW(%r15)
cghi %r12,__LC_MCK_OLD_PSW
jne 0f
@@ -1061,9 +1021,7 @@ cleanup_io_leave:
br %r14
cleanup_io_leave_insn:
.quad io_done - 4
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.quad io_done - 8
-#endif
/*
* Integer constants
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
index 83477c7dc743..ec7e35f6055b 100644
--- a/arch/s390/kernel/head.S
+++ b/arch/s390/kernel/head.S
@@ -461,6 +461,55 @@ start:
.byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7
.byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+#
+# startup-code at 0x10000, running in absolute addressing mode
+# this is called either by the ipl loader or directly by PSW restart
+# or linload or SALIPL
+#
+ .org 0x10000
+startup:basr %r13,0 # get base
+.LPG0:
+
+#ifndef CONFIG_MARCH_G5
+ # check processor version against MARCH_{G5,Z900,Z990,Z9_109,Z10}
+ stidp __LC_CPUID # store cpuid
+ lhi %r0,(3f-2f) / 2
+ la %r1,2f-.LPG0(%r13)
+0: clc __LC_CPUID+4(2),0(%r1)
+ jne 3f
+ lpsw 1f-.LPG0(13) # machine type not good enough, crash
+ .align 16
+1: .long 0x000a0000,0x00000000
+2:
+#if defined(CONFIG_MARCH_Z10)
+ .short 0x9672, 0x2064, 0x2066, 0x2084, 0x2086, 0x2094, 0x2096
+#elif defined(CONFIG_MARCH_Z9_109)
+ .short 0x9672, 0x2064, 0x2066, 0x2084, 0x2086
+#elif defined(CONFIG_MARCH_Z990)
+ .short 0x9672, 0x2064, 0x2066
+#elif defined(CONFIG_MARCH_Z900)
+ .short 0x9672
+#endif
+3: la %r1,2(%r1)
+ brct %r0,0b
+#endif
+
+ l %r13,0f-.LPG0(%r13)
+ b 0(%r13)
+0: .long startup_continue
+
+#
+# params at 10400 (setup.h)
+#
+ .org PARMAREA
+ .long 0,0 # IPL_DEVICE
+ .long 0,0 # INITRD_START
+ .long 0,0 # INITRD_SIZE
+
+ .org COMMAND_LINE
+ .byte "root=/dev/ram0 ro"
+ .byte 0
+
#ifdef CONFIG_64BIT
#include "head64.S"
#else
diff --git a/arch/s390/kernel/head31.S b/arch/s390/kernel/head31.S
index a816e2de32b9..db476d114caa 100644
--- a/arch/s390/kernel/head31.S
+++ b/arch/s390/kernel/head31.S
@@ -10,34 +10,13 @@
*
*/
-#
-# startup-code at 0x10000, running in absolute addressing mode
-# this is called either by the ipl loader or directly by PSW restart
-# or linload or SALIPL
-#
- .org 0x10000
-startup:basr %r13,0 # get base
-.LPG0: l %r13,0f-.LPG0(%r13)
- b 0(%r13)
-0: .long startup_continue
-
-#
-# params at 10400 (setup.h)
-#
- .org PARMAREA
- .long 0,0 # IPL_DEVICE
- .long 0,0 # INITRD_START
- .long 0,0 # INITRD_SIZE
-
- .org COMMAND_LINE
- .byte "root=/dev/ram0 ro"
- .byte 0
-
.org 0x11000
startup_continue:
basr %r13,0 # get base
-.LPG1: mvi __LC_AR_MODE_ID,0 # set ESA flag (mode 0)
+.LPG1:
+
+ mvi __LC_AR_MODE_ID,0 # set ESA flag (mode 0)
lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
# move IPL device to lowcore
@@ -50,7 +29,6 @@ startup_continue:
ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union+THREAD_SIZE
st %r15,__LC_KERNEL_STACK # set end of kernel stack
ahi %r15,-96
- xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
#
# Save ipl parameters, clear bss memory, initialize storage key for kernel pages,
# and create a kernel NSS if the SAVESYS= parm is defined
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 1d06961e87b3..3ccd36b24b8f 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -10,29 +10,6 @@
*
*/
-#
-# startup-code at 0x10000, running in absolute addressing mode
-# this is called either by the ipl loader or directly by PSW restart
-# or linload or SALIPL
-#
- .org 0x10000
-startup:basr %r13,0 # get base
-.LPG0: l %r13,0f-.LPG0(%r13)
- b 0(%r13)
-0: .long startup_continue
-
-#
-# params at 10400 (setup.h)
-#
- .org PARMAREA
- .quad 0 # IPL_DEVICE
- .quad 0 # INITRD_START
- .quad 0 # INITRD_SIZE
-
- .org COMMAND_LINE
- .byte "root=/dev/ram0 ro"
- .byte 0
-
.org 0x11000
startup_continue:
@@ -119,7 +96,6 @@ startup_continue:
aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE
stg %r15,__LC_KERNEL_STACK # set end of kernel stack
aghi %r15,-160
- xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain
#
# Save ipl parameters, clear bss memory, initialize storage key for kernel pages,
# and create a kernel NSS if the SAVESYS= parm is defined
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
new file mode 100644
index 000000000000..397d131a345f
--- /dev/null
+++ b/arch/s390/kernel/mcount.S
@@ -0,0 +1,56 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ */
+
+#ifndef CONFIG_64BIT
+.globl _mcount
+_mcount:
+ stm %r0,%r5,8(%r15)
+ st %r14,56(%r15)
+ lr %r1,%r15
+ ahi %r15,-96
+ l %r3,100(%r15)
+ la %r2,0(%r14)
+ st %r1,0(%r15)
+ la %r3,0(%r3)
+ bras %r14,0f
+ .long ftrace_trace_function
+0: l %r14,0(%r14)
+ l %r14,0(%r14)
+ basr %r14,%r14
+ ahi %r15,96
+ lm %r0,%r5,8(%r15)
+ l %r14,56(%r15)
+ br %r14
+
+.globl ftrace_stub
+ftrace_stub:
+ br %r14
+
+#else /* CONFIG_64BIT */
+
+.globl _mcount
+_mcount:
+ stmg %r0,%r5,16(%r15)
+ stg %r14,112(%r15)
+ lgr %r1,%r15
+ aghi %r15,-160
+ stg %r1,0(%r15)
+ lgr %r2,%r14
+ lg %r3,168(%r15)
+ larl %r14,ftrace_trace_function
+ lg %r14,0(%r14)
+ basr %r14,%r14
+ aghi %r15,160
+ lmg %r0,%r5,16(%r15)
+ lg %r14,112(%r15)
+ br %r14
+
+.globl ftrace_stub
+ftrace_stub:
+ br %r14
+
+#endif /* CONFIG_64BIT */
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
new file mode 100644
index 000000000000..82c1872cfe80
--- /dev/null
+++ b/arch/s390/kernel/processor.c
@@ -0,0 +1,98 @@
+/*
+ * arch/s390/kernel/processor.c
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+
+#include <asm/elf.h>
+#include <asm/lowcore.h>
+#include <asm/param.h>
+
+void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo)
+{
+ pr_info("Processor %d started, address %d, identification %06X\n",
+ cpuinfo->cpu_nr, cpuinfo->cpu_addr, cpuinfo->cpu_id.ident);
+}
+
+/*
+ * show_cpuinfo - Get information on one CPU for use by procfs.
+ */
+
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ static const char *hwcap_str[8] = {
+ "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
+ "edat"
+ };
+ struct cpuinfo_S390 *cpuinfo;
+ unsigned long n = (unsigned long) v - 1;
+ int i;
+
+ s390_adjust_jiffies();
+ preempt_disable();
+ if (!n) {
+ seq_printf(m, "vendor_id : IBM/S390\n"
+ "# processors : %i\n"
+ "bogomips per cpu: %lu.%02lu\n",
+ num_online_cpus(), loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ))%100);
+ seq_puts(m, "features\t: ");
+ for (i = 0; i < 8; i++)
+ if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
+ seq_printf(m, "%s ", hwcap_str[i]);
+ seq_puts(m, "\n");
+ }
+
+ if (cpu_online(n)) {
+#ifdef CONFIG_SMP
+ if (smp_processor_id() == n)
+ cpuinfo = &S390_lowcore.cpu_data;
+ else
+ cpuinfo = &lowcore_ptr[n]->cpu_data;
+#else
+ cpuinfo = &S390_lowcore.cpu_data;
+#endif
+ seq_printf(m, "processor %li: "
+ "version = %02X, "
+ "identification = %06X, "
+ "machine = %04X\n",
+ n, cpuinfo->cpu_id.version,
+ cpuinfo->cpu_id.ident,
+ cpuinfo->cpu_id.machine);
+ }
+ preempt_enable();
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+
+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 = show_cpuinfo,
+};
+
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 38ff2bce1203..75c496f4f16d 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -204,7 +204,6 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
static int
peek_user(struct task_struct *child, addr_t addr, addr_t data)
{
- struct user *dummy = NULL;
addr_t tmp, mask;
/*
@@ -213,8 +212,8 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
*/
mask = __ADDR_MASK;
#ifdef CONFIG_64BIT
- if (addr >= (addr_t) &dummy->regs.acrs &&
- addr < (addr_t) &dummy->regs.orig_gpr2)
+ if (addr >= (addr_t) &((struct user *) NULL)->regs.acrs &&
+ addr < (addr_t) &((struct user *) NULL)->regs.orig_gpr2)
mask = 3;
#endif
if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
@@ -312,7 +311,6 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
static int
poke_user(struct task_struct *child, addr_t addr, addr_t data)
{
- struct user *dummy = NULL;
addr_t mask;
/*
@@ -321,8 +319,8 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
*/
mask = __ADDR_MASK;
#ifdef CONFIG_64BIT
- if (addr >= (addr_t) &dummy->regs.acrs &&
- addr < (addr_t) &dummy->regs.orig_gpr2)
+ if (addr >= (addr_t) &((struct user *) NULL)->regs.acrs &&
+ addr < (addr_t) &((struct user *) NULL)->regs.orig_gpr2)
mask = 3;
#endif
if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c
index 48238a114ce9..46b90cb03707 100644
--- a/arch/s390/kernel/s390_ksyms.c
+++ b/arch/s390/kernel/s390_ksyms.c
@@ -14,6 +14,7 @@
#include <asm/delay.h>
#include <asm/pgalloc.h>
#include <asm/setup.h>
+#include <asm/ftrace.h>
#ifdef CONFIG_IP_MULTICAST
#include <net/arp.h>
#endif
@@ -43,3 +44,7 @@ EXPORT_SYMBOL(csum_fold);
EXPORT_SYMBOL(console_mode);
EXPORT_SYMBOL(console_devno);
EXPORT_SYMBOL(console_irq);
+
+#ifdef CONFIG_FUNCTION_TRACER
+EXPORT_SYMBOL(_mcount);
+#endif
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 400b040df7fa..b7a1efd5522c 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -14,6 +14,9 @@
* This file handles the architecture-dependent parts of initialization
*/
+#define KMSG_COMPONENT "setup"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/sched.h>
@@ -32,7 +35,6 @@
#include <linux/bootmem.h>
#include <linux/root_dev.h>
#include <linux/console.h>
-#include <linux/seq_file.h>
#include <linux/kernel_stat.h>
#include <linux/device.h>
#include <linux/notifier.h>
@@ -291,8 +293,8 @@ unsigned int switch_amode = 0;
#endif
EXPORT_SYMBOL_GPL(switch_amode);
-static void set_amode_and_uaccess(unsigned long user_amode,
- unsigned long user32_amode)
+static int set_amode_and_uaccess(unsigned long user_amode,
+ unsigned long user32_amode)
{
psw_user_bits = PSW_BASE_BITS | PSW_MASK_DAT | user_amode |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK |
@@ -309,11 +311,11 @@ static void set_amode_and_uaccess(unsigned long user_amode,
PSW_MASK_MCHECK | PSW_DEFAULT_KEY;
if (MACHINE_HAS_MVCOS) {
- printk("mvcos available.\n");
memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess));
+ return 1;
} else {
- printk("mvcos not available.\n");
memcpy(&uaccess, &uaccess_pt, sizeof(uaccess));
+ return 0;
}
}
@@ -328,9 +330,10 @@ static int __init early_parse_switch_amode(char *p)
early_param("switch_amode", early_parse_switch_amode);
#else /* CONFIG_S390_SWITCH_AMODE */
-static inline void set_amode_and_uaccess(unsigned long user_amode,
- unsigned long user32_amode)
+static inline int set_amode_and_uaccess(unsigned long user_amode,
+ unsigned long user32_amode)
{
+ return 0;
}
#endif /* CONFIG_S390_SWITCH_AMODE */
@@ -355,11 +358,20 @@ early_param("noexec", early_parse_noexec);
static void setup_addressing_mode(void)
{
if (s390_noexec) {
- printk("S390 execute protection active, ");
- set_amode_and_uaccess(PSW_ASC_SECONDARY, PSW32_ASC_SECONDARY);
+ if (set_amode_and_uaccess(PSW_ASC_SECONDARY,
+ PSW32_ASC_SECONDARY))
+ pr_info("Execute protection active, "
+ "mvcos available\n");
+ else
+ pr_info("Execute protection active, "
+ "mvcos not available\n");
} else if (switch_amode) {
- printk("S390 address spaces switched, ");
- set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY);
+ if (set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY))
+ pr_info("Address spaces switched, "
+ "mvcos available\n");
+ else
+ pr_info("Address spaces switched, "
+ "mvcos not available\n");
}
#ifdef CONFIG_TRACE_IRQFLAGS
sysc_restore_trace_psw.mask = psw_kernel_bits & ~PSW_MASK_MCHECK;
@@ -572,15 +584,15 @@ setup_memory(void)
start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE;
if (start + INITRD_SIZE > memory_end) {
- printk("initrd extends beyond end of memory "
- "(0x%08lx > 0x%08lx)\n"
+ pr_err("initrd extends beyond end of "
+ "memory (0x%08lx > 0x%08lx) "
"disabling initrd\n",
start + INITRD_SIZE, memory_end);
INITRD_START = INITRD_SIZE = 0;
} else {
- printk("Moving initrd (0x%08lx -> 0x%08lx, "
- "size: %ld)\n",
- INITRD_START, start, INITRD_SIZE);
+ pr_info("Moving initrd (0x%08lx -> "
+ "0x%08lx, size: %ld)\n",
+ INITRD_START, start, INITRD_SIZE);
memmove((void *) start, (void *) INITRD_START,
INITRD_SIZE);
INITRD_START = start;
@@ -642,8 +654,9 @@ setup_memory(void)
initrd_start = INITRD_START;
initrd_end = initrd_start + INITRD_SIZE;
} else {
- printk("initrd extends beyond end of memory "
- "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ pr_err("initrd extends beyond end of "
+ "memory (0x%08lx > 0x%08lx) "
+ "disabling initrd\n",
initrd_start + INITRD_SIZE, memory_end);
initrd_start = initrd_end = 0;
}
@@ -651,23 +664,6 @@ setup_memory(void)
#endif
}
-static int __init __stfle(unsigned long long *list, int doublewords)
-{
- typedef struct { unsigned long long _[doublewords]; } addrtype;
- register unsigned long __nr asm("0") = doublewords - 1;
-
- asm volatile(".insn s,0xb2b00000,%0" /* stfle */
- : "=m" (*(addrtype *) list), "+d" (__nr) : : "cc");
- return __nr + 1;
-}
-
-int __init stfle(unsigned long long *list, int doublewords)
-{
- if (!(stfl() & (1UL << 24)))
- return -EOPNOTSUPP;
- return __stfle(list, doublewords);
-}
-
/*
* Setup hardware capabilities.
*/
@@ -739,8 +735,13 @@ static void __init setup_hwcaps(void)
strcpy(elf_platform, "z990");
break;
case 0x2094:
+ case 0x2096:
strcpy(elf_platform, "z9-109");
break;
+ case 0x2097:
+ case 0x2098:
+ strcpy(elf_platform, "z10");
+ break;
}
}
@@ -752,25 +753,34 @@ static void __init setup_hwcaps(void)
void __init
setup_arch(char **cmdline_p)
{
+ /* set up preferred console */
+ add_preferred_console("ttyS", 0, NULL);
+
/*
* print what head.S has found out about the machine
*/
#ifndef CONFIG_64BIT
- printk((MACHINE_IS_VM) ?
- "We are running under VM (31 bit mode)\n" :
- "We are running native (31 bit mode)\n");
- printk((MACHINE_HAS_IEEE) ?
- "This machine has an IEEE fpu\n" :
- "This machine has no IEEE fpu\n");
+ if (MACHINE_IS_VM)
+ pr_info("Linux is running as a z/VM "
+ "guest operating system in 31-bit mode\n");
+ else
+ pr_info("Linux is running natively in 31-bit mode\n");
+ if (MACHINE_HAS_IEEE)
+ pr_info("The hardware system has IEEE compatible "
+ "floating point units\n");
+ else
+ pr_info("The hardware system has no IEEE compatible "
+ "floating point units\n");
#else /* CONFIG_64BIT */
if (MACHINE_IS_VM)
- printk("We are running under VM (64 bit mode)\n");
+ pr_info("Linux is running as a z/VM "
+ "guest operating system in 64-bit mode\n");
else if (MACHINE_IS_KVM) {
- printk("We are running under KVM (64 bit mode)\n");
+ pr_info("Linux is running under KVM in 64-bit mode\n");
add_preferred_console("hvc", 0, NULL);
s390_virtio_console_init();
} else
- printk("We are running native (64 bit mode)\n");
+ pr_info("Linux is running natively in 64-bit mode\n");
#endif /* CONFIG_64BIT */
/* Have one command line that is parsed and saved in /proc/cmdline */
@@ -818,90 +828,3 @@ setup_arch(char **cmdline_p)
/* Setup zfcpdump support */
setup_zfcpdump(console_devno);
}
-
-void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo)
-{
- printk(KERN_INFO "cpu %d "
-#ifdef CONFIG_SMP
- "phys_idx=%d "
-#endif
- "vers=%02X ident=%06X machine=%04X unused=%04X\n",
- cpuinfo->cpu_nr,
-#ifdef CONFIG_SMP
- cpuinfo->cpu_addr,
-#endif
- cpuinfo->cpu_id.version,
- cpuinfo->cpu_id.ident,
- cpuinfo->cpu_id.machine,
- cpuinfo->cpu_id.unused);
-}
-
-/*
- * show_cpuinfo - Get information on one CPU for use by procfs.
- */
-
-static int show_cpuinfo(struct seq_file *m, void *v)
-{
- static const char *hwcap_str[8] = {
- "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
- "edat"
- };
- struct cpuinfo_S390 *cpuinfo;
- unsigned long n = (unsigned long) v - 1;
- int i;
-
- s390_adjust_jiffies();
- preempt_disable();
- if (!n) {
- seq_printf(m, "vendor_id : IBM/S390\n"
- "# processors : %i\n"
- "bogomips per cpu: %lu.%02lu\n",
- num_online_cpus(), loops_per_jiffy/(500000/HZ),
- (loops_per_jiffy/(5000/HZ))%100);
- seq_puts(m, "features\t: ");
- for (i = 0; i < 8; i++)
- if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
- seq_printf(m, "%s ", hwcap_str[i]);
- seq_puts(m, "\n");
- }
-
- if (cpu_online(n)) {
-#ifdef CONFIG_SMP
- if (smp_processor_id() == n)
- cpuinfo = &S390_lowcore.cpu_data;
- else
- cpuinfo = &lowcore_ptr[n]->cpu_data;
-#else
- cpuinfo = &S390_lowcore.cpu_data;
-#endif
- seq_printf(m, "processor %li: "
- "version = %02X, "
- "identification = %06X, "
- "machine = %04X\n",
- n, cpuinfo->cpu_id.version,
- cpuinfo->cpu_id.ident,
- cpuinfo->cpu_id.machine);
- }
- preempt_enable();
- return 0;
-}
-
-static void *c_start(struct seq_file *m, loff_t *pos)
-{
- return *pos < NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
-}
-static void *c_next(struct seq_file *m, void *v, loff_t *pos)
-{
- ++*pos;
- return c_start(m, pos);
-}
-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 = show_cpuinfo,
-};
-
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index f03914b8ed2f..3ed5c7a83c6c 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -20,6 +20,9 @@
* cpu_number_map in other architectures.
*/
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
@@ -71,159 +74,6 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices);
static void smp_ext_bitcall(int, ec_bit_sig);
-/*
- * Structure and data for __smp_call_function_map(). This is designed to
- * minimise static memory requirements. It also looks cleaner.
- */
-static DEFINE_SPINLOCK(call_lock);
-
-struct call_data_struct {
- void (*func) (void *info);
- void *info;
- cpumask_t started;
- cpumask_t finished;
- int wait;
-};
-
-static struct call_data_struct *call_data;
-
-/*
- * 'Call function' interrupt callback
- */
-static void do_call_function(void)
-{
- void (*func) (void *info) = call_data->func;
- void *info = call_data->info;
- int wait = call_data->wait;
-
- cpu_set(smp_processor_id(), call_data->started);
- (*func)(info);
- if (wait)
- cpu_set(smp_processor_id(), call_data->finished);;
-}
-
-static void __smp_call_function_map(void (*func) (void *info), void *info,
- int wait, cpumask_t map)
-{
- struct call_data_struct data;
- int cpu, local = 0;
-
- /*
- * Can deadlock when interrupts are disabled or if in wrong context.
- */
- WARN_ON(irqs_disabled() || in_irq());
-
- /*
- * Check for local function call. We have to have the same call order
- * as in on_each_cpu() because of machine_restart_smp().
- */
- if (cpu_isset(smp_processor_id(), map)) {
- local = 1;
- cpu_clear(smp_processor_id(), map);
- }
-
- cpus_and(map, map, cpu_online_map);
- if (cpus_empty(map))
- goto out;
-
- data.func = func;
- data.info = info;
- data.started = CPU_MASK_NONE;
- data.wait = wait;
- if (wait)
- data.finished = CPU_MASK_NONE;
-
- call_data = &data;
-
- for_each_cpu_mask(cpu, map)
- smp_ext_bitcall(cpu, ec_call_function);
-
- /* Wait for response */
- while (!cpus_equal(map, data.started))
- cpu_relax();
- if (wait)
- while (!cpus_equal(map, data.finished))
- cpu_relax();
-out:
- if (local) {
- local_irq_disable();
- func(info);
- local_irq_enable();
- }
-}
-
-/*
- * smp_call_function:
- * @func: the function to run; this must be fast and non-blocking
- * @info: an arbitrary pointer to pass to the function
- * @wait: if true, wait (atomically) until function has completed on other CPUs
- *
- * Run a function on all other CPUs.
- *
- * You must not call this function with disabled interrupts, from a
- * hardware interrupt handler or from a bottom half.
- */
-int smp_call_function(void (*func) (void *info), void *info, int wait)
-{
- cpumask_t map;
-
- spin_lock(&call_lock);
- map = cpu_online_map;
- cpu_clear(smp_processor_id(), map);
- __smp_call_function_map(func, info, wait, map);
- spin_unlock(&call_lock);
- return 0;
-}
-EXPORT_SYMBOL(smp_call_function);
-
-/*
- * smp_call_function_single:
- * @cpu: the CPU where func should run
- * @func: the function to run; this must be fast and non-blocking
- * @info: an arbitrary pointer to pass to the function
- * @wait: if true, wait (atomically) until function has completed on other CPUs
- *
- * Run a function on one processor.
- *
- * You must not call this function with disabled interrupts, from a
- * hardware interrupt handler or from a bottom half.
- */
-int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
- int wait)
-{
- spin_lock(&call_lock);
- __smp_call_function_map(func, info, wait, cpumask_of_cpu(cpu));
- spin_unlock(&call_lock);
- return 0;
-}
-EXPORT_SYMBOL(smp_call_function_single);
-
-/**
- * smp_call_function_mask(): Run a function on a set of other CPUs.
- * @mask: The set of cpus to run on. Must not include the current cpu.
- * @func: The function to run. This must be fast and non-blocking.
- * @info: An arbitrary pointer to pass to the function.
- * @wait: If true, wait (atomically) until function has completed on other CPUs.
- *
- * Returns 0 on success, else a negative status code.
- *
- * If @wait is true, then returns once @func has returned; otherwise
- * it returns just before the target cpu calls @func.
- *
- * You must not call this function with disabled interrupts or from a
- * hardware interrupt handler or from a bottom half handler.
- */
-int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
- int wait)
-{
- spin_lock(&call_lock);
- cpu_clear(smp_processor_id(), mask);
- __smp_call_function_map(func, info, wait, mask);
- spin_unlock(&call_lock);
- return 0;
-}
-EXPORT_SYMBOL(smp_call_function_mask);
-
void smp_send_stop(void)
{
int cpu, rc;
@@ -265,7 +115,10 @@ static void do_ext_call_interrupt(__u16 code)
bits = xchg(&S390_lowcore.ext_call_fast, 0);
if (test_bit(ec_call_function, &bits))
- do_call_function();
+ generic_smp_call_function_interrupt();
+
+ if (test_bit(ec_call_function_single, &bits))
+ generic_smp_call_function_single_interrupt();
}
/*
@@ -282,6 +135,19 @@ static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
udelay(10);
}
+void arch_send_call_function_ipi(cpumask_t mask)
+{
+ int cpu;
+
+ for_each_cpu_mask(cpu, mask)
+ smp_ext_bitcall(cpu, ec_call_function);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ smp_ext_bitcall(cpu, ec_call_function_single);
+}
+
#ifndef CONFIG_64BIT
/*
* this function sends a 'purge tlb' signal to another CPU.
@@ -382,8 +248,8 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu)
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return;
if (cpu >= NR_CPUS) {
- printk(KERN_WARNING "Registers for cpu %i not saved since dump "
- "kernel was compiled with NR_CPUS=%i\n", cpu, NR_CPUS);
+ pr_warning("CPU %i exceeds the maximum %i and is excluded from "
+ "the dump\n", cpu, NR_CPUS - 1);
return;
}
zfcpdump_save_areas[cpu] = kmalloc(sizeof(union save_area), GFP_KERNEL);
@@ -556,7 +422,7 @@ static void __init smp_detect_cpus(void)
}
out:
kfree(info);
- printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus);
+ pr_info("%d configured CPUs, %d standby CPUs\n", c_cpus, s_cpus);
get_online_cpus();
__smp_rescan_cpus();
put_online_cpus();
@@ -572,19 +438,17 @@ int __cpuinit start_secondary(void *cpuvoid)
preempt_disable();
/* Enable TOD clock interrupts on the secondary cpu. */
init_cpu_timer();
-#ifdef CONFIG_VIRT_TIMER
/* Enable cpu timer interrupts on the secondary cpu. */
init_cpu_vtimer();
-#endif
/* Enable pfault pseudo page faults on this cpu. */
pfault_init();
/* call cpu notifiers */
notify_cpu_starting(smp_processor_id());
/* Mark this cpu as online */
- spin_lock(&call_lock);
+ ipi_call_lock();
cpu_set(smp_processor_id(), cpu_online_map);
- spin_unlock(&call_lock);
+ ipi_call_unlock();
/* Switch on interrupts */
local_irq_enable();
/* Print info about this processor */
@@ -633,18 +497,15 @@ static int __cpuinit smp_alloc_lowcore(int cpu)
save_area = get_zeroed_page(GFP_KERNEL);
if (!save_area)
- goto out_save_area;
+ goto out;
lowcore->extended_save_area_addr = (u32) save_area;
}
#endif
lowcore_ptr[cpu] = lowcore;
return 0;
-#ifndef CONFIG_64BIT
-out_save_area:
- free_page(panic_stack);
-#endif
out:
+ free_page(panic_stack);
free_pages(async_stack, ASYNC_ORDER);
free_pages((unsigned long) lowcore, lc_order);
return -ENOMEM;
@@ -684,12 +545,8 @@ int __cpuinit __cpu_up(unsigned int cpu)
ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
cpu, sigp_set_prefix);
- if (ccode) {
- printk("sigp_set_prefix failed for cpu %d "
- "with condition code %d\n",
- (int) cpu, (int) ccode);
+ if (ccode)
return -EIO;
- }
idle = current_set[cpu];
cpu_lowcore = lowcore_ptr[cpu];
@@ -772,7 +629,7 @@ void __cpu_die(unsigned int cpu)
while (!smp_cpu_not_running(cpu))
cpu_relax();
smp_free_lowcore(cpu);
- printk(KERN_INFO "Processor %d spun down\n", cpu);
+ pr_info("Processor %d stopped\n", cpu);
}
void cpu_die(void)
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index f5bd141c8443..d649600df5b9 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -12,6 +12,9 @@
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*/
+#define KMSG_COMPONENT "time"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/sched.h>
@@ -20,6 +23,8 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <linux/stop_machine.h>
#include <linux/time.h>
#include <linux/sysdev.h>
#include <linux/delay.h>
@@ -36,6 +41,7 @@
#include <asm/delay.h>
#include <asm/s390_ext.h>
#include <asm/div64.h>
+#include <asm/vdso.h>
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <asm/timer.h>
@@ -223,6 +229,36 @@ static struct clocksource clocksource_tod = {
};
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
+{
+ if (clock != &clocksource_tod)
+ return;
+
+ /* Make userspace gettimeofday spin until we're done. */
+ ++vdso_data->tb_update_count;
+ smp_wmb();
+ vdso_data->xtime_tod_stamp = clock->cycle_last;
+ vdso_data->xtime_clock_sec = xtime.tv_sec;
+ vdso_data->xtime_clock_nsec = xtime.tv_nsec;
+ vdso_data->wtom_clock_sec = wall_to_monotonic.tv_sec;
+ vdso_data->wtom_clock_nsec = wall_to_monotonic.tv_nsec;
+ smp_wmb();
+ ++vdso_data->tb_update_count;
+}
+
+extern struct timezone sys_tz;
+
+void update_vsyscall_tz(void)
+{
+ /* Make userspace gettimeofday spin until we're done. */
+ ++vdso_data->tb_update_count;
+ smp_wmb();
+ vdso_data->tz_minuteswest = sys_tz.tz_minuteswest;
+ vdso_data->tz_dsttime = sys_tz.tz_dsttime;
+ smp_wmb();
+ ++vdso_data->tb_update_count;
+}
+
/*
* Initialize the TOD clock and the CPU timer of
* the boot cpu.
@@ -253,10 +289,8 @@ void __init time_init(void)
/* Enable TOD clock interrupts on the boot cpu. */
init_cpu_timer();
-
-#ifdef CONFIG_VIRT_TIMER
+ /* Enable cpu timer interrupts on the boot cpu. */
vtime_init();
-#endif
}
/*
@@ -288,8 +322,8 @@ static unsigned long long adjust_time(unsigned long long old,
}
sched_clock_base_cc += delta;
if (adjust.offset != 0) {
- printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
- adjust.offset);
+ pr_notice("The ETR interface has adjusted the clock "
+ "by %li microseconds\n", adjust.offset);
adjust.modes = ADJ_OFFSET_SINGLESHOT;
do_adjtimex(&adjust);
}
@@ -360,6 +394,15 @@ static void enable_sync_clock(void)
atomic_set_mask(0x80000000, sw_ptr);
}
+/* Single threaded workqueue used for etr and stp sync events */
+static struct workqueue_struct *time_sync_wq;
+
+static void __init time_init_wq(void)
+{
+ if (!time_sync_wq)
+ time_sync_wq = create_singlethread_workqueue("timesync");
+}
+
/*
* External Time Reference (ETR) code.
*/
@@ -425,6 +468,7 @@ static struct timer_list etr_timer;
static void etr_timeout(unsigned long dummy);
static void etr_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(etr_work_mutex);
static DECLARE_WORK(etr_work, etr_work_fn);
/*
@@ -440,8 +484,8 @@ static void etr_reset(void)
etr_tolec = get_clock();
set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags);
} else if (etr_port0_online || etr_port1_online) {
- printk(KERN_WARNING "Running on non ETR capable "
- "machine, only local mode available.\n");
+ pr_warning("The real or virtual hardware system does "
+ "not provide an ETR interface\n");
etr_port0_online = etr_port1_online = 0;
}
}
@@ -452,17 +496,18 @@ static int __init etr_init(void)
if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
return 0;
+ time_init_wq();
/* Check if this machine has the steai instruction. */
if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)
etr_steai_available = 1;
setup_timer(&etr_timer, etr_timeout, 0UL);
if (etr_port0_online) {
set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
if (etr_port1_online) {
set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
return 0;
}
@@ -489,7 +534,7 @@ void etr_switch_to_local(void)
if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
disable_sync_clock(NULL);
set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
/*
@@ -505,7 +550,7 @@ void etr_sync_check(void)
if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
disable_sync_clock(NULL);
set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
/*
@@ -529,13 +574,13 @@ static void etr_timing_alert(struct etr_irq_parm *intparm)
* Both ports are not up-to-date now.
*/
set_bit(ETR_EVENT_PORT_ALERT, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
static void etr_timeout(unsigned long dummy)
{
set_bit(ETR_EVENT_UPDATE, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
/*
@@ -642,14 +687,16 @@ static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p)
}
struct clock_sync_data {
+ atomic_t cpus;
int in_sync;
unsigned long long fixup_cc;
+ int etr_port;
+ struct etr_aib *etr_aib;
};
-static void clock_sync_cpu_start(void *dummy)
+static void clock_sync_cpu(struct clock_sync_data *sync)
{
- struct clock_sync_data *sync = dummy;
-
+ atomic_dec(&sync->cpus);
enable_sync_clock();
/*
* This looks like a busy wait loop but it isn't. etr_sync_cpus
@@ -675,39 +722,35 @@ static void clock_sync_cpu_start(void *dummy)
fixup_clock_comparator(sync->fixup_cc);
}
-static void clock_sync_cpu_end(void *dummy)
-{
-}
-
/*
* Sync the TOD clock using the port refered to by aibp. This port
* has to be enabled and the other port has to be disabled. The
* last eacr update has to be more than 1.6 seconds in the past.
*/
-static int etr_sync_clock(struct etr_aib *aib, int port)
+static int etr_sync_clock(void *data)
{
- struct etr_aib *sync_port;
- struct clock_sync_data etr_sync;
+ static int first;
unsigned long long clock, old_clock, delay, delta;
- int follows;
+ struct clock_sync_data *etr_sync;
+ struct etr_aib *sync_port, *aib;
+ int port;
int rc;
- /* Check if the current aib is adjacent to the sync port aib. */
- sync_port = (port == 0) ? &etr_port0 : &etr_port1;
- follows = etr_aib_follows(sync_port, aib, port);
- memcpy(sync_port, aib, sizeof(*aib));
- if (!follows)
- return -EAGAIN;
+ etr_sync = data;
- /*
- * Catch all other cpus and make them wait until we have
- * successfully synced the clock. smp_call_function will
- * return after all other cpus are in etr_sync_cpu_start.
- */
- memset(&etr_sync, 0, sizeof(etr_sync));
- preempt_disable();
- smp_call_function(clock_sync_cpu_start, &etr_sync, 0);
- local_irq_disable();
+ if (xchg(&first, 1) == 1) {
+ /* Slave */
+ clock_sync_cpu(etr_sync);
+ return 0;
+ }
+
+ /* Wait until all other cpus entered the sync function. */
+ while (atomic_read(&etr_sync->cpus) != 0)
+ cpu_relax();
+
+ port = etr_sync->etr_port;
+ aib = etr_sync->etr_aib;
+ sync_port = (port == 0) ? &etr_port0 : &etr_port1;
enable_sync_clock();
/* Set clock to next OTE. */
@@ -724,16 +767,16 @@ static int etr_sync_clock(struct etr_aib *aib, int port)
delay = (unsigned long long)
(aib->edf2.etv - sync_port->edf2.etv) << 32;
delta = adjust_time(old_clock, clock, delay);
- etr_sync.fixup_cc = delta;
+ etr_sync->fixup_cc = delta;
fixup_clock_comparator(delta);
/* Verify that the clock is properly set. */
if (!etr_aib_follows(sync_port, aib, port)) {
/* Didn't work. */
disable_sync_clock(NULL);
- etr_sync.in_sync = -EAGAIN;
+ etr_sync->in_sync = -EAGAIN;
rc = -EAGAIN;
} else {
- etr_sync.in_sync = 1;
+ etr_sync->in_sync = 1;
rc = 0;
}
} else {
@@ -741,12 +784,33 @@ static int etr_sync_clock(struct etr_aib *aib, int port)
__ctl_clear_bit(0, 29);
__ctl_clear_bit(14, 21);
disable_sync_clock(NULL);
- etr_sync.in_sync = -EAGAIN;
+ etr_sync->in_sync = -EAGAIN;
rc = -EAGAIN;
}
- local_irq_enable();
- smp_call_function(clock_sync_cpu_end, NULL, 0);
- preempt_enable();
+ xchg(&first, 0);
+ return rc;
+}
+
+static int etr_sync_clock_stop(struct etr_aib *aib, int port)
+{
+ struct clock_sync_data etr_sync;
+ struct etr_aib *sync_port;
+ int follows;
+ int rc;
+
+ /* Check if the current aib is adjacent to the sync port aib. */
+ sync_port = (port == 0) ? &etr_port0 : &etr_port1;
+ follows = etr_aib_follows(sync_port, aib, port);
+ memcpy(sync_port, aib, sizeof(*aib));
+ if (!follows)
+ return -EAGAIN;
+ memset(&etr_sync, 0, sizeof(etr_sync));
+ etr_sync.etr_aib = aib;
+ etr_sync.etr_port = port;
+ get_online_cpus();
+ atomic_set(&etr_sync.cpus, num_online_cpus() - 1);
+ rc = stop_machine(etr_sync_clock, &etr_sync, &cpu_online_map);
+ put_online_cpus();
return rc;
}
@@ -903,7 +967,7 @@ static void etr_update_eacr(struct etr_eacr eacr)
}
/*
- * ETR tasklet. In this function you'll find the main logic. In
+ * ETR work. In this function you'll find the main logic. In
* particular this is the only function that calls etr_update_eacr(),
* it "controls" the etr control register.
*/
@@ -914,6 +978,9 @@ static void etr_work_fn(struct work_struct *work)
struct etr_aib aib;
int sync_port;
+ /* prevent multiple execution. */
+ mutex_lock(&etr_work_mutex);
+
/* Create working copy of etr_eacr. */
eacr = etr_eacr;
@@ -929,7 +996,7 @@ static void etr_work_fn(struct work_struct *work)
del_timer_sync(&etr_timer);
etr_update_eacr(eacr);
clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
- return;
+ goto out_unlock;
}
/* Store aib to get the current ETR status word. */
@@ -1016,7 +1083,7 @@ static void etr_work_fn(struct work_struct *work)
eacr.es || sync_port < 0) {
etr_update_eacr(eacr);
etr_set_tolec_timeout(now);
- return;
+ goto out_unlock;
}
/*
@@ -1036,7 +1103,7 @@ static void etr_work_fn(struct work_struct *work)
etr_update_eacr(eacr);
set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
if (now < etr_tolec + (1600000 << 12) ||
- etr_sync_clock(&aib, sync_port) != 0) {
+ etr_sync_clock_stop(&aib, sync_port) != 0) {
/* Sync failed. Try again in 1/2 second. */
eacr.es = 0;
etr_update_eacr(eacr);
@@ -1044,6 +1111,8 @@ static void etr_work_fn(struct work_struct *work)
etr_set_sync_timeout();
} else
etr_set_tolec_timeout(now);
+out_unlock:
+ mutex_unlock(&etr_work_mutex);
}
/*
@@ -1125,13 +1194,13 @@ static ssize_t etr_online_store(struct sys_device *dev,
return count; /* Nothing to do. */
etr_port0_online = value;
set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
} else {
if (etr_port1_online == value)
return count; /* Nothing to do. */
etr_port1_online = value;
set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
}
return count;
}
@@ -1332,6 +1401,7 @@ static struct stp_sstpi stp_info;
static void *stp_page;
static void stp_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(stp_work_mutex);
static DECLARE_WORK(stp_work, stp_work_fn);
static int __init early_parse_stp(char *p)
@@ -1356,7 +1426,8 @@ static void __init stp_reset(void)
if (rc == 0)
set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
else if (stp_online) {
- printk(KERN_WARNING "Running on non STP capable machine.\n");
+ pr_warning("The real or virtual hardware system does "
+ "not provide an STP interface\n");
free_bootmem((unsigned long) stp_page, PAGE_SIZE);
stp_page = NULL;
stp_online = 0;
@@ -1365,8 +1436,12 @@ static void __init stp_reset(void)
static int __init stp_init(void)
{
- if (test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online)
- schedule_work(&stp_work);
+ if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return 0;
+ time_init_wq();
+ if (!stp_online)
+ return 0;
+ queue_work(time_sync_wq, &stp_work);
return 0;
}
@@ -1383,7 +1458,7 @@ arch_initcall(stp_init);
static void stp_timing_alert(struct stp_irq_parm *intparm)
{
if (intparm->tsc || intparm->lac || intparm->tcpc)
- schedule_work(&stp_work);
+ queue_work(time_sync_wq, &stp_work);
}
/*
@@ -1397,7 +1472,7 @@ void stp_sync_check(void)
if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
return;
disable_sync_clock(NULL);
- schedule_work(&stp_work);
+ queue_work(time_sync_wq, &stp_work);
}
/*
@@ -1411,46 +1486,34 @@ void stp_island_check(void)
if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
return;
disable_sync_clock(NULL);
- schedule_work(&stp_work);
+ queue_work(time_sync_wq, &stp_work);
}
-/*
- * STP tasklet. Check for the STP state and take over the clock
- * synchronization if the STP clock source is usable.
- */
-static void stp_work_fn(struct work_struct *work)
+
+static int stp_sync_clock(void *data)
{
- struct clock_sync_data stp_sync;
+ static int first;
unsigned long long old_clock, delta;
+ struct clock_sync_data *stp_sync;
int rc;
- if (!stp_online) {
- chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
- return;
- }
+ stp_sync = data;
- rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
- if (rc)
- return;
+ if (xchg(&first, 1) == 1) {
+ /* Slave */
+ clock_sync_cpu(stp_sync);
+ return 0;
+ }
- rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
- if (rc || stp_info.c == 0)
- return;
+ /* Wait until all other cpus entered the sync function. */
+ while (atomic_read(&stp_sync->cpus) != 0)
+ cpu_relax();
- /*
- * Catch all other cpus and make them wait until we have
- * successfully synced the clock. smp_call_function will
- * return after all other cpus are in clock_sync_cpu_start.
- */
- memset(&stp_sync, 0, sizeof(stp_sync));
- preempt_disable();
- smp_call_function(clock_sync_cpu_start, &stp_sync, 0);
- local_irq_disable();
enable_sync_clock();
set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
rc = 0;
if (stp_info.todoff[0] || stp_info.todoff[1] ||
@@ -1469,16 +1532,49 @@ static void stp_work_fn(struct work_struct *work)
}
if (rc) {
disable_sync_clock(NULL);
- stp_sync.in_sync = -EAGAIN;
+ stp_sync->in_sync = -EAGAIN;
clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
if (etr_port0_online || etr_port1_online)
- schedule_work(&etr_work);
+ queue_work(time_sync_wq, &etr_work);
} else
- stp_sync.in_sync = 1;
+ stp_sync->in_sync = 1;
+ xchg(&first, 0);
+ return 0;
+}
+
+/*
+ * STP work. Check for the STP state and take over the clock
+ * synchronization if the STP clock source is usable.
+ */
+static void stp_work_fn(struct work_struct *work)
+{
+ struct clock_sync_data stp_sync;
+ int rc;
+
+ /* prevent multiple execution. */
+ mutex_lock(&stp_work_mutex);
+
+ if (!stp_online) {
+ chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+ goto out_unlock;
+ }
+
+ rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
+ if (rc)
+ goto out_unlock;
+
+ rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+ if (rc || stp_info.c == 0)
+ goto out_unlock;
+
+ memset(&stp_sync, 0, sizeof(stp_sync));
+ get_online_cpus();
+ atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
+ stop_machine(stp_sync_clock, &stp_sync, &cpu_online_map);
+ put_online_cpus();
- local_irq_enable();
- smp_call_function(clock_sync_cpu_end, NULL, 0);
- preempt_enable();
+out_unlock:
+ mutex_unlock(&stp_work_mutex);
}
/*
@@ -1587,7 +1683,7 @@ static ssize_t stp_online_store(struct sysdev_class *class,
if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
return -EOPNOTSUPP;
stp_online = value;
- schedule_work(&stp_work);
+ queue_work(time_sync_wq, &stp_work);
return count;
}
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index bf96f1b5c6ec..cc362c9ea8f1 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -3,6 +3,9 @@
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
*/
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
@@ -12,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/cpu.h>
#include <linux/smp.h>
+#include <linux/cpuset.h>
#include <asm/delay.h>
#include <asm/s390_ext.h>
#include <asm/sysinfo.h>
@@ -57,11 +61,11 @@ struct core_info {
cpumask_t mask;
};
+static int topology_enabled;
static void topology_work_fn(struct work_struct *work);
static struct tl_info *tl_info;
static struct core_info core_info;
static int machine_has_topology;
-static int machine_has_topology_irq;
static struct timer_list topology_timer;
static void set_topology_timer(void);
static DECLARE_WORK(topology_work, topology_work_fn);
@@ -77,8 +81,8 @@ cpumask_t cpu_coregroup_map(unsigned int cpu)
cpumask_t mask;
cpus_clear(mask);
- if (!machine_has_topology)
- return cpu_present_map;
+ if (!topology_enabled || !machine_has_topology)
+ return cpu_possible_map;
spin_lock_irqsave(&topology_lock, flags);
while (core) {
if (cpu_isset(cpu, core->mask)) {
@@ -93,6 +97,11 @@ cpumask_t cpu_coregroup_map(unsigned int cpu)
return mask;
}
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu)
+{
+ return &cpu_core_map[cpu];
+}
+
static void add_cpus_to_core(struct tl_cpu *tl_cpu, struct core_info *core)
{
unsigned int cpu;
@@ -168,7 +177,7 @@ static void topology_update_polarization_simple(void)
int cpu;
mutex_lock(&smp_cpu_state_mutex);
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
smp_cpu_polarization[cpu] = POLARIZATION_HRZ;
mutex_unlock(&smp_cpu_state_mutex);
}
@@ -199,7 +208,7 @@ int topology_set_cpu_management(int fc)
rc = ptf(PTF_HORIZONTAL);
if (rc)
return -EBUSY;
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
smp_cpu_polarization[cpu] = POLARIZATION_UNKNWN;
return rc;
}
@@ -208,7 +217,7 @@ static void update_cpu_core_map(void)
{
int cpu;
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
cpu_core_map[cpu] = cpu_coregroup_map(cpu);
}
@@ -235,7 +244,7 @@ int arch_update_cpu_topology(void)
static void topology_work_fn(struct work_struct *work)
{
- arch_reinit_sched_domains();
+ rebuild_sched_domains();
}
void topology_schedule_update(void)
@@ -258,10 +267,14 @@ static void set_topology_timer(void)
add_timer(&topology_timer);
}
-static void topology_interrupt(__u16 code)
+static int __init early_parse_topology(char *p)
{
- schedule_work(&topology_work);
+ if (strncmp(p, "on", 2))
+ return 0;
+ topology_enabled = 1;
+ return 0;
}
+early_param("topology", early_parse_topology);
static int __init init_topology_update(void)
{
@@ -273,14 +286,7 @@ static int __init init_topology_update(void)
goto out;
}
init_timer_deferrable(&topology_timer);
- if (machine_has_topology_irq) {
- rc = register_external_interrupt(0x2005, topology_interrupt);
- if (rc)
- goto out;
- ctl_set_bit(0, 8);
- }
- else
- set_topology_timer();
+ set_topology_timer();
out:
update_cpu_core_map();
return rc;
@@ -301,9 +307,6 @@ void __init s390_init_cpu_topology(void)
return;
machine_has_topology = 1;
- if (facility_bits & (1ULL << 51))
- machine_has_topology_irq = 1;
-
tl_info = alloc_bootmem_pages(PAGE_SIZE);
info = tl_info;
stsi(info, 15, 1, 2);
@@ -312,7 +315,7 @@ void __init s390_init_cpu_topology(void)
for (i = 0; i < info->mnest - 2; i++)
nr_cores *= info->mag[NR_MAG - 3 - i];
- printk(KERN_INFO "CPU topology:");
+ pr_info("The CPU configuration topology of the machine is:");
for (i = 0; i < NR_MAG; i++)
printk(" %d", info->mag[i]);
printk(" / %d\n", info->mnest);
@@ -327,5 +330,4 @@ void __init s390_init_cpu_topology(void)
return;
error:
machine_has_topology = 0;
- machine_has_topology_irq = 0;
}
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
new file mode 100644
index 000000000000..10a6ccef4412
--- /dev/null
+++ b/arch/s390/kernel/vdso.c
@@ -0,0 +1,234 @@
+/*
+ * vdso setup for s390
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/security.h>
+#include <linux/bootmem.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/sections.h>
+#include <asm/vdso.h>
+
+/* Max supported size for symbol names */
+#define MAX_SYMNAME 64
+
+#if defined(CONFIG_32BIT) || defined(CONFIG_COMPAT)
+extern char vdso32_start, vdso32_end;
+static void *vdso32_kbase = &vdso32_start;
+static unsigned int vdso32_pages;
+static struct page **vdso32_pagelist;
+#endif
+
+#ifdef CONFIG_64BIT
+extern char vdso64_start, vdso64_end;
+static void *vdso64_kbase = &vdso64_start;
+static unsigned int vdso64_pages;
+static struct page **vdso64_pagelist;
+#endif /* CONFIG_64BIT */
+
+/*
+ * Should the kernel map a VDSO page into processes and pass its
+ * address down to glibc upon exec()?
+ */
+unsigned int __read_mostly vdso_enabled = 1;
+
+static int __init vdso_setup(char *s)
+{
+ vdso_enabled = simple_strtoul(s, NULL, 0);
+ return 1;
+}
+__setup("vdso=", vdso_setup);
+
+/*
+ * The vdso data page
+ */
+static union {
+ struct vdso_data data;
+ u8 page[PAGE_SIZE];
+} vdso_data_store __attribute__((__section__(".data.page_aligned")));
+struct vdso_data *vdso_data = &vdso_data_store.data;
+
+/*
+ * This is called from binfmt_elf, we create the special vma for the
+ * vDSO and insert it into the mm struct tree
+ */
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+{
+ struct mm_struct *mm = current->mm;
+ struct page **vdso_pagelist;
+ unsigned long vdso_pages;
+ unsigned long vdso_base;
+ int rc;
+
+ if (!vdso_enabled)
+ return 0;
+ /*
+ * Only map the vdso for dynamically linked elf binaries.
+ */
+ if (!uses_interp)
+ return 0;
+
+ vdso_base = mm->mmap_base;
+#ifdef CONFIG_64BIT
+ vdso_pagelist = vdso64_pagelist;
+ vdso_pages = vdso64_pages;
+#ifdef CONFIG_COMPAT
+ if (test_thread_flag(TIF_31BIT)) {
+ vdso_pagelist = vdso32_pagelist;
+ vdso_pages = vdso32_pages;
+ }
+#endif
+#else
+ vdso_pagelist = vdso32_pagelist;
+ vdso_pages = vdso32_pages;
+#endif
+
+ /*
+ * vDSO has a problem and was disabled, just don't "enable" it for
+ * the process
+ */
+ if (vdso_pages == 0)
+ return 0;
+
+ current->mm->context.vdso_base = 0;
+
+ /*
+ * pick a base address for the vDSO in process space. We try to put
+ * it at vdso_base which is the "natural" base for it, but we might
+ * fail and end up putting it elsewhere.
+ */
+ down_write(&mm->mmap_sem);
+ vdso_base = get_unmapped_area(NULL, vdso_base,
+ vdso_pages << PAGE_SHIFT, 0, 0);
+ if (IS_ERR_VALUE(vdso_base)) {
+ rc = vdso_base;
+ goto out_up;
+ }
+
+ /*
+ * our vma flags don't have VM_WRITE so by default, the process
+ * isn't allowed to write those pages.
+ * gdb can break that with ptrace interface, and thus trigger COW
+ * on those pages but it's then your responsibility to never do that
+ * on the "data" page of the vDSO or you'll stop getting kernel
+ * updates and your nice userland gettimeofday will be totally dead.
+ * It's fine to use that for setting breakpoints in the vDSO code
+ * pages though
+ *
+ * Make sure the vDSO gets into every core dump.
+ * Dumping its contents makes post-mortem fully interpretable later
+ * without matching up the same kernel and hardware config to see
+ * what PC values meant.
+ */
+ rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT,
+ VM_READ|VM_EXEC|
+ VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC|
+ VM_ALWAYSDUMP,
+ vdso_pagelist);
+ if (rc)
+ goto out_up;
+
+ /* Put vDSO base into mm struct */
+ current->mm->context.vdso_base = vdso_base;
+
+ up_write(&mm->mmap_sem);
+ return 0;
+
+out_up:
+ up_write(&mm->mmap_sem);
+ return rc;
+}
+
+const char *arch_vma_name(struct vm_area_struct *vma)
+{
+ if (vma->vm_mm && vma->vm_start == vma->vm_mm->context.vdso_base)
+ return "[vdso]";
+ return NULL;
+}
+
+static int __init vdso_init(void)
+{
+ int i;
+
+#if defined(CONFIG_32BIT) || defined(CONFIG_COMPAT)
+ /* Calculate the size of the 32 bit vDSO */
+ vdso32_pages = ((&vdso32_end - &vdso32_start
+ + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
+
+ /* Make sure pages are in the correct state */
+ vdso32_pagelist = kzalloc(sizeof(struct page *) * (vdso32_pages + 1),
+ GFP_KERNEL);
+ BUG_ON(vdso32_pagelist == NULL);
+ for (i = 0; i < vdso32_pages - 1; i++) {
+ struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE);
+ ClearPageReserved(pg);
+ get_page(pg);
+ vdso32_pagelist[i] = pg;
+ }
+ vdso32_pagelist[vdso32_pages - 1] = virt_to_page(vdso_data);
+ vdso32_pagelist[vdso32_pages] = NULL;
+#endif
+
+#ifdef CONFIG_64BIT
+ /* Calculate the size of the 64 bit vDSO */
+ vdso64_pages = ((&vdso64_end - &vdso64_start
+ + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
+
+ /* Make sure pages are in the correct state */
+ vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 1),
+ GFP_KERNEL);
+ BUG_ON(vdso64_pagelist == NULL);
+ for (i = 0; i < vdso64_pages - 1; i++) {
+ struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE);
+ ClearPageReserved(pg);
+ get_page(pg);
+ vdso64_pagelist[i] = pg;
+ }
+ vdso64_pagelist[vdso64_pages - 1] = virt_to_page(vdso_data);
+ vdso64_pagelist[vdso64_pages] = NULL;
+#endif /* CONFIG_64BIT */
+
+ get_page(virt_to_page(vdso_data));
+
+ smp_wmb();
+
+ return 0;
+}
+arch_initcall(vdso_init);
+
+int in_gate_area_no_task(unsigned long addr)
+{
+ return 0;
+}
+
+int in_gate_area(struct task_struct *task, unsigned long addr)
+{
+ return 0;
+}
+
+struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
+{
+ return NULL;
+}
diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile
new file mode 100644
index 000000000000..ca78ad60ba24
--- /dev/null
+++ b/arch/s390/kernel/vdso32/Makefile
@@ -0,0 +1,55 @@
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso32 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+
+# Build rules
+
+targets := $(obj-vdso32) vdso32.so vdso32.so.dbg
+obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
+
+KBUILD_AFLAGS_31 := $(filter-out -m64,$(KBUILD_AFLAGS))
+KBUILD_AFLAGS_31 += -m31 -s
+
+KBUILD_CFLAGS_31 := $(filter-out -m64,$(KBUILD_CFLAGS))
+KBUILD_CFLAGS_31 += -m31 -fPIC -shared -fno-common -fno-builtin
+KBUILD_CFLAGS_31 += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
+ $(call ld-option, -Wl$(comma)--hash-style=sysv)
+
+$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_31)
+$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_31)
+
+obj-y += vdso32_wrapper.o
+extra-y += vdso32.lds
+CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso32.so.dbg: $(src)/vdso32.lds $(obj-vdso32)
+ $(call if_changed,vdso32ld)
+
+# strip rule for the .so file
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+ $(call if_changed,objcopy)
+
+# assembly rules for the .S files
+$(obj-vdso32): %.o: %.S
+ $(call if_changed_dep,vdso32as)
+
+# actual build commands
+quiet_cmd_vdso32ld = VDSO32L $@
+ cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdso32as = VDSO32A $@
+ cmd_vdso32as = $(CC) $(a_flags) -c -o $@ $<
+
+# install commands for the unstripped file
+quiet_cmd_vdso_install = INSTALL $@
+ cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+
+vdso32.so: $(obj)/vdso32.so.dbg
+ @mkdir -p $(MODLIB)/vdso
+ $(call cmd,vdso_install)
+
+vdso_install: vdso32.so
diff --git a/arch/s390/kernel/vdso32/clock_getres.S b/arch/s390/kernel/vdso32/clock_getres.S
new file mode 100644
index 000000000000..9532c4e6a9d2
--- /dev/null
+++ b/arch/s390/kernel/vdso32/clock_getres.S
@@ -0,0 +1,39 @@
+/*
+ * Userland implementation of clock_getres() for 32 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_getres
+ .type __kernel_clock_getres,@function
+__kernel_clock_getres:
+ .cfi_startproc
+ chi %r2,CLOCK_REALTIME
+ je 0f
+ chi %r2,CLOCK_MONOTONIC
+ jne 3f
+0: ltr %r3,%r3
+ jz 2f /* res == NULL */
+ basr %r1,0
+1: l %r0,4f-1b(%r1)
+ xc 0(4,%r3),0(%r3) /* set tp->tv_sec to zero */
+ st %r0,4(%r3) /* store tp->tv_usec */
+2: lhi %r2,0
+ br %r14
+3: lhi %r1,__NR_clock_getres /* fallback to svc */
+ svc 0
+ br %r14
+4: .long CLOCK_REALTIME_RES
+ .cfi_endproc
+ .size __kernel_clock_getres,.-__kernel_clock_getres
diff --git a/arch/s390/kernel/vdso32/clock_gettime.S b/arch/s390/kernel/vdso32/clock_gettime.S
new file mode 100644
index 000000000000..4a98909a8310
--- /dev/null
+++ b/arch/s390/kernel/vdso32/clock_gettime.S
@@ -0,0 +1,128 @@
+/*
+ * Userland implementation of clock_gettime() for 32 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_gettime
+ .type __kernel_clock_gettime,@function
+__kernel_clock_gettime:
+ .cfi_startproc
+ basr %r5,0
+0: al %r5,21f-0b(%r5) /* get &_vdso_data */
+ chi %r2,CLOCK_REALTIME
+ je 10f
+ chi %r2,CLOCK_MONOTONIC
+ jne 19f
+
+ /* CLOCK_MONOTONIC */
+ ltr %r3,%r3
+ jz 9f /* tp == NULL */
+1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
+ tml %r4,0x0001 /* pending update ? loop */
+ jnz 1b
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%r15)
+ s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ sl %r1,__VDSO_XTIME_STAMP+4(%r5)
+ brc 3,2f
+ ahi %r0,-1
+2: mhi %r0,1000 /* cyc2ns(clock,cycle_delta) */
+ lr %r2,%r0
+ lhi %r0,1000
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 3f
+ ahi %r0,1000
+3: alr %r0,%r2
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,4f
+ ahi %r0,1
+4: l %r2,__VDSO_XTIME_SEC+4(%r5)
+ al %r0,__VDSO_WTOM_NSEC(%r5) /* + wall_to_monotonic */
+ al %r1,__VDSO_WTOM_NSEC+4(%r5)
+ brc 12,5f
+ ahi %r0,1
+5: al %r2,__VDSO_WTOM_SEC+4(%r5)
+ cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
+ jne 1b
+ basr %r5,0
+6: ltr %r0,%r0
+ jnz 7f
+ cl %r1,20f-6b(%r5)
+ jl 8f
+7: ahi %r2,1
+ sl %r1,20f-6b(%r5)
+ brc 3,6b
+ ahi %r0,-1
+ j 6b
+8: st %r2,0(%r3) /* store tp->tv_sec */
+ st %r1,4(%r3) /* store tp->tv_nsec */
+9: lhi %r2,0
+ br %r14
+
+ /* CLOCK_REALTIME */
+10: ltr %r3,%r3 /* tp == NULL */
+ jz 18f
+11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
+ tml %r4,0x0001 /* pending update ? loop */
+ jnz 11b
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%r15)
+ s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ sl %r1,__VDSO_XTIME_STAMP+4(%r5)
+ brc 3,12f
+ ahi %r0,-1
+12: mhi %r0,1000 /* cyc2ns(clock,cycle_delta) */
+ lr %r2,%r0
+ lhi %r0,1000
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 13f
+ ahi %r0,1000
+13: alr %r0,%r2
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,14f
+ ahi %r0,1
+14: l %r2,__VDSO_XTIME_SEC+4(%r5)
+ cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
+ jne 11b
+ basr %r5,0
+15: ltr %r0,%r0
+ jnz 16f
+ cl %r1,20f-15b(%r5)
+ jl 17f
+16: ahi %r2,1
+ sl %r1,20f-15b(%r5)
+ brc 3,15b
+ ahi %r0,-1
+ j 15b
+17: st %r2,0(%r3) /* store tp->tv_sec */
+ st %r1,4(%r3) /* store tp->tv_nsec */
+18: lhi %r2,0
+ br %r14
+
+ /* Fallback to system call */
+19: lhi %r1,__NR_clock_gettime
+ svc 0
+ br %r14
+
+20: .long 1000000000
+21: .long _vdso_data - 0b
+ .cfi_endproc
+ .size __kernel_clock_gettime,.-__kernel_clock_gettime
diff --git a/arch/s390/kernel/vdso32/gettimeofday.S b/arch/s390/kernel/vdso32/gettimeofday.S
new file mode 100644
index 000000000000..c32f29c3d70c
--- /dev/null
+++ b/arch/s390/kernel/vdso32/gettimeofday.S
@@ -0,0 +1,82 @@
+/*
+ * Userland implementation of gettimeofday() for 32 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_gettimeofday
+ .type __kernel_gettimeofday,@function
+__kernel_gettimeofday:
+ .cfi_startproc
+ basr %r5,0
+0: al %r5,13f-0b(%r5) /* get &_vdso_data */
+1: ltr %r3,%r3 /* check if tz is NULL */
+ je 2f
+ mvc 0(8,%r3),__VDSO_TIMEZONE(%r5)
+2: ltr %r2,%r2 /* check if tv is NULL */
+ je 10f
+ l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
+ tml %r4,0x0001 /* pending update ? loop */
+ jnz 1b
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%r15)
+ s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ sl %r1,__VDSO_XTIME_STAMP+4(%r5)
+ brc 3,3f
+ ahi %r0,-1
+3: mhi %r0,1000 /* cyc2ns(clock,cycle_delta) */
+ st %r0,24(%r15)
+ lhi %r0,1000
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 4f
+ ahi %r0,1000
+4: al %r0,24(%r15)
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,5f
+ ahi %r0,1
+5: mvc 24(4,%r15),__VDSO_XTIME_SEC+4(%r5)
+ cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
+ jne 1b
+ l %r4,24(%r15) /* get tv_sec from stack */
+ basr %r5,0
+6: ltr %r0,%r0
+ jnz 7f
+ cl %r1,11f-6b(%r5)
+ jl 8f
+7: ahi %r4,1
+ sl %r1,11f-6b(%r5)
+ brc 3,6b
+ ahi %r0,-1
+ j 6b
+8: st %r4,0(%r2) /* store tv->tv_sec */
+ ltr %r1,%r1
+ m %r0,12f-6b(%r5)
+ jnm 9f
+ al %r0,12f-6b(%r5)
+9: srl %r0,6
+ st %r0,4(%r2) /* store tv->tv_usec */
+10: slr %r2,%r2
+ br %r14
+11: .long 1000000000
+12: .long 274877907
+13: .long _vdso_data - 0b
+ .cfi_endproc
+ .size __kernel_gettimeofday,.-__kernel_gettimeofday
diff --git a/arch/s390/kernel/vdso32/note.S b/arch/s390/kernel/vdso32/note.S
new file mode 100644
index 000000000000..79a071e4357e
--- /dev/null
+++ b/arch/s390/kernel/vdso32/note.S
@@ -0,0 +1,12 @@
+/*
+ * 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/s390/kernel/vdso32/vdso32.lds.S b/arch/s390/kernel/vdso32/vdso32.lds.S
new file mode 100644
index 000000000000..a8c379fa1247
--- /dev/null
+++ b/arch/s390/kernel/vdso32/vdso32.lds.S
@@ -0,0 +1,138 @@
+/*
+ * This is the infamous ld script for the 32 bits vdso
+ * library
+ */
+#include <asm/vdso.h>
+
+OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
+OUTPUT_ARCH(s390:31-bit)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = VDSO32_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 .stub .text.* .gnu.linkonce.t.*)
+ } :text
+ PROVIDE(__etext = .);
+ PROVIDE(_etext = .);
+ PROVIDE(etext = .);
+
+ /*
+ * Other stuff is appended to the text segment:
+ */
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+
+ .dynamic : { *(.dynamic) } :text :dynamic
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
+ .eh_frame : { KEEP (*(.eh_frame)) } :text
+ .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
+
+ .rela.dyn ALIGN(8) : { *(.rela.dyn) }
+ .got ALIGN(8) : { *(.got .toc) }
+
+ _end = .;
+ PROVIDE(end = .);
+
+ /*
+ * Stabs debugging sections are here too.
+ */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+
+ /*
+ * DWARF debug sections.
+ * Symbols in the DWARF debugging sections are relative to the
+ * beginning of the section so we begin them at 0.
+ */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+
+ . = ALIGN(4096);
+ PROVIDE(_vdso_data = .);
+
+ /DISCARD/ : {
+ *(.note.GNU-stack)
+ *(.branch_lt)
+ *(.data .data.* .gnu.linkonce.d.* .sdata*)
+ *(.bss .sbss .dynbss .dynsbss)
+ }
+}
+
+/*
+ * Very old versions of ld do not recognize this name token; use the constant.
+ */
+#define PT_GNU_EH_FRAME 0x6474e550
+
+/*
+ * 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 FILEHDR PHDRS FLAGS(5); /* 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
+{
+ VDSO_VERSION_STRING {
+ global:
+ /*
+ * Has to be there for the kernel to find
+ */
+ __kernel_gettimeofday;
+ __kernel_clock_gettime;
+ __kernel_clock_getres;
+
+ local: *;
+ };
+}
diff --git a/arch/s390/kernel/vdso32/vdso32_wrapper.S b/arch/s390/kernel/vdso32/vdso32_wrapper.S
new file mode 100644
index 000000000000..61639a89e70b
--- /dev/null
+++ b/arch/s390/kernel/vdso32/vdso32_wrapper.S
@@ -0,0 +1,13 @@
+#include <linux/init.h>
+#include <asm/page.h>
+
+ .section ".data.page_aligned"
+
+ .globl vdso32_start, vdso32_end
+ .balign PAGE_SIZE
+vdso32_start:
+ .incbin "arch/s390/kernel/vdso32/vdso32.so"
+ .balign PAGE_SIZE
+vdso32_end:
+
+ .previous
diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile
new file mode 100644
index 000000000000..6fc8e829258c
--- /dev/null
+++ b/arch/s390/kernel/vdso64/Makefile
@@ -0,0 +1,55 @@
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso64 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+
+# Build rules
+
+targets := $(obj-vdso64) vdso64.so vdso64.so.dbg
+obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))
+
+KBUILD_AFLAGS_64 := $(filter-out -m64,$(KBUILD_AFLAGS))
+KBUILD_AFLAGS_64 += -m64 -s
+
+KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS))
+KBUILD_CFLAGS_64 += -m64 -fPIC -shared -fno-common -fno-builtin
+KBUILD_CFLAGS_64 += -nostdlib -Wl,-soname=linux-vdso64.so.1 \
+ $(call ld-option, -Wl$(comma)--hash-style=sysv)
+
+$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64)
+$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64)
+
+obj-y += vdso64_wrapper.o
+extra-y += vdso64.lds
+CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso64.so.dbg: $(src)/vdso64.lds $(obj-vdso64)
+ $(call if_changed,vdso64ld)
+
+# strip rule for the .so file
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+ $(call if_changed,objcopy)
+
+# assembly rules for the .S files
+$(obj-vdso64): %.o: %.S
+ $(call if_changed_dep,vdso64as)
+
+# actual build commands
+quiet_cmd_vdso64ld = VDSO64L $@
+ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdso64as = VDSO64A $@
+ cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
+
+# install commands for the unstripped file
+quiet_cmd_vdso_install = INSTALL $@
+ cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+
+vdso64.so: $(obj)/vdso64.so.dbg
+ @mkdir -p $(MODLIB)/vdso
+ $(call cmd,vdso_install)
+
+vdso_install: vdso64.so
diff --git a/arch/s390/kernel/vdso64/clock_getres.S b/arch/s390/kernel/vdso64/clock_getres.S
new file mode 100644
index 000000000000..488e31a3c0e7
--- /dev/null
+++ b/arch/s390/kernel/vdso64/clock_getres.S
@@ -0,0 +1,39 @@
+/*
+ * Userland implementation of clock_getres() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_getres
+ .type __kernel_clock_getres,@function
+__kernel_clock_getres:
+ .cfi_startproc
+ cghi %r2,CLOCK_REALTIME
+ je 0f
+ cghi %r2,CLOCK_MONOTONIC
+ jne 2f
+0: ltgr %r3,%r3
+ jz 1f /* res == NULL */
+ larl %r1,3f
+ lg %r0,0(%r1)
+ xc 0(8,%r3),0(%r3) /* set tp->tv_sec to zero */
+ stg %r0,8(%r3) /* store tp->tv_usec */
+1: lghi %r2,0
+ br %r14
+2: lghi %r1,__NR_clock_getres /* fallback to svc */
+ svc 0
+ br %r14
+3: .quad CLOCK_REALTIME_RES
+ .cfi_endproc
+ .size __kernel_clock_getres,.-__kernel_clock_getres
diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S
new file mode 100644
index 000000000000..738a410b7eb2
--- /dev/null
+++ b/arch/s390/kernel/vdso64/clock_gettime.S
@@ -0,0 +1,89 @@
+/*
+ * Userland implementation of clock_gettime() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_gettime
+ .type __kernel_clock_gettime,@function
+__kernel_clock_gettime:
+ .cfi_startproc
+ larl %r5,_vdso_data
+ cghi %r2,CLOCK_REALTIME
+ je 4f
+ cghi %r2,CLOCK_MONOTONIC
+ jne 9f
+
+ /* CLOCK_MONOTONIC */
+ ltgr %r3,%r3
+ jz 3f /* tp == NULL */
+0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 0b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ mghi %r1,1000
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ lg %r0,__VDSO_XTIME_SEC(%r5)
+ alg %r1,__VDSO_WTOM_NSEC(%r5) /* + wall_to_monotonic */
+ alg %r0,__VDSO_WTOM_SEC(%r5)
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 0b
+ larl %r5,10f
+1: clg %r1,0(%r5)
+ jl 2f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 1b
+2: stg %r0,0(%r3) /* store tp->tv_sec */
+ stg %r1,8(%r3) /* store tp->tv_nsec */
+3: lghi %r2,0
+ br %r14
+
+ /* CLOCK_REALTIME */
+4: ltr %r3,%r3 /* tp == NULL */
+ jz 8f
+5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 5b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ mghi %r1,1000
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ lg %r0,__VDSO_XTIME_SEC(%r5)
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 5b
+ larl %r5,10f
+6: clg %r1,0(%r5)
+ jl 7f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 6b
+7: stg %r0,0(%r3) /* store tp->tv_sec */
+ stg %r1,8(%r3) /* store tp->tv_nsec */
+8: lghi %r2,0
+ br %r14
+
+ /* Fallback to system call */
+9: lghi %r1,__NR_clock_gettime
+ svc 0
+ br %r14
+
+10: .quad 1000000000
+ .cfi_endproc
+ .size __kernel_clock_gettime,.-__kernel_clock_gettime
diff --git a/arch/s390/kernel/vdso64/gettimeofday.S b/arch/s390/kernel/vdso64/gettimeofday.S
new file mode 100644
index 000000000000..f873e75634e1
--- /dev/null
+++ b/arch/s390/kernel/vdso64/gettimeofday.S
@@ -0,0 +1,56 @@
+/*
+ * Userland implementation of gettimeofday() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.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 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_gettimeofday
+ .type __kernel_gettimeofday,@function
+__kernel_gettimeofday:
+ .cfi_startproc
+ larl %r5,_vdso_data
+0: ltgr %r3,%r3 /* check if tz is NULL */
+ je 1f
+ mvc 0(8,%r3),__VDSO_TIMEZONE(%r5)
+1: ltgr %r2,%r2 /* check if tv is NULL */
+ je 4f
+ lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 0b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ mghi %r1,1000
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime.tv_nsec */
+ lg %r0,__VDSO_XTIME_SEC(%r5) /* xtime.tv_sec */
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 0b
+ larl %r5,5f
+2: clg %r1,0(%r5)
+ jl 3f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 2b
+3: stg %r0,0(%r2) /* store tv->tv_sec */
+ slgr %r0,%r0 /* tv_nsec -> tv_usec */
+ ml %r0,8(%r5)
+ srlg %r0,%r0,6
+ stg %r0,8(%r2) /* store tv->tv_usec */
+4: lghi %r2,0
+ br %r14
+5: .quad 1000000000
+ .long 274877907
+ .cfi_endproc
+ .size __kernel_gettimeofday,.-__kernel_gettimeofday
diff --git a/arch/s390/kernel/vdso64/note.S b/arch/s390/kernel/vdso64/note.S
new file mode 100644
index 000000000000..79a071e4357e
--- /dev/null
+++ b/arch/s390/kernel/vdso64/note.S
@@ -0,0 +1,12 @@
+/*
+ * 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/s390/kernel/vdso64/vdso64.lds.S b/arch/s390/kernel/vdso64/vdso64.lds.S
new file mode 100644
index 000000000000..9f5979d102a9
--- /dev/null
+++ b/arch/s390/kernel/vdso64/vdso64.lds.S
@@ -0,0 +1,138 @@
+/*
+ * This is the infamous ld script for the 64 bits vdso
+ * library
+ */
+#include <asm/vdso.h>
+
+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
+OUTPUT_ARCH(s390:64-bit)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = VDSO64_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 .stub .text.* .gnu.linkonce.t.*)
+ } :text
+ PROVIDE(__etext = .);
+ PROVIDE(_etext = .);
+ PROVIDE(etext = .);
+
+ /*
+ * Other stuff is appended to the text segment:
+ */
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+
+ .dynamic : { *(.dynamic) } :text :dynamic
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
+ .eh_frame : { KEEP (*(.eh_frame)) } :text
+ .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
+
+ .rela.dyn ALIGN(8) : { *(.rela.dyn) }
+ .got ALIGN(8) : { *(.got .toc) }
+
+ _end = .;
+ PROVIDE(end = .);
+
+ /*
+ * Stabs debugging sections are here too.
+ */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+
+ /*
+ * DWARF debug sections.
+ * Symbols in the DWARF debugging sections are relative to the
+ * beginning of the section so we begin them at 0.
+ */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+
+ . = ALIGN(4096);
+ PROVIDE(_vdso_data = .);
+
+ /DISCARD/ : {
+ *(.note.GNU-stack)
+ *(.branch_lt)
+ *(.data .data.* .gnu.linkonce.d.* .sdata*)
+ *(.bss .sbss .dynbss .dynsbss)
+ }
+}
+
+/*
+ * Very old versions of ld do not recognize this name token; use the constant.
+ */
+#define PT_GNU_EH_FRAME 0x6474e550
+
+/*
+ * 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 FILEHDR PHDRS FLAGS(5); /* 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
+{
+ VDSO_VERSION_STRING {
+ global:
+ /*
+ * Has to be there for the kernel to find
+ */
+ __kernel_gettimeofday;
+ __kernel_clock_gettime;
+ __kernel_clock_getres;
+
+ local: *;
+ };
+}
diff --git a/arch/s390/kernel/vdso64/vdso64_wrapper.S b/arch/s390/kernel/vdso64/vdso64_wrapper.S
new file mode 100644
index 000000000000..d8e2ac14d564
--- /dev/null
+++ b/arch/s390/kernel/vdso64/vdso64_wrapper.S
@@ -0,0 +1,13 @@
+#include <linux/init.h>
+#include <asm/page.h>
+
+ .section ".data.page_aligned"
+
+ .globl vdso64_start, vdso64_end
+ .balign PAGE_SIZE
+vdso64_start:
+ .incbin "arch/s390/kernel/vdso64/vdso64.so"
+ .balign PAGE_SIZE
+vdso64_end:
+
+ .previous
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index 0fa5dc5d68e1..75a6e62ea973 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -27,7 +27,6 @@
static ext_int_info_t ext_int_info_timer;
static DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer);
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
/*
* Update process times based on virtual cpu times stored by entry.S
* to the lowcore fields user_timer, system_timer & steal_clock.
@@ -125,16 +124,6 @@ static inline void set_vtimer(__u64 expires)
/* store expire time for this CPU timer */
__get_cpu_var(virt_cpu_timer).to_expire = expires;
}
-#else
-static inline void set_vtimer(__u64 expires)
-{
- S390_lowcore.last_update_timer = expires;
- asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer));
-
- /* store expire time for this CPU timer */
- __get_cpu_var(virt_cpu_timer).to_expire = expires;
-}
-#endif
void vtime_start_cpu_timer(void)
{
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c
index 580fc64cc735..5c8457129603 100644
--- a/arch/s390/mm/extmem.c
+++ b/arch/s390/mm/extmem.c
@@ -7,6 +7,9 @@
* (C) IBM Corporation 2002-2004
*/
+#define KMSG_COMPONENT "extmem"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/spinlock.h>
@@ -24,19 +27,6 @@
#include <asm/cpcmd.h>
#include <asm/setup.h>
-#define DCSS_DEBUG /* Debug messages on/off */
-
-#define DCSS_NAME "extmem"
-#ifdef DCSS_DEBUG
-#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSS_NAME " debug:" x)
-#else
-#define PRINT_DEBUG(x...) do {} while (0)
-#endif
-#define PRINT_INFO(x...) printk(KERN_INFO DCSS_NAME " info:" x)
-#define PRINT_WARN(x...) printk(KERN_WARNING DCSS_NAME " warning:" x)
-#define PRINT_ERR(x...) printk(KERN_ERR DCSS_NAME " error:" x)
-
-
#define DCSS_LOADSHR 0x00
#define DCSS_LOADNSR 0x04
#define DCSS_PURGESEG 0x08
@@ -286,7 +276,7 @@ query_segment_type (struct dcss_segment *seg)
goto out_free;
}
if (diag_cc > 1) {
- PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
+ pr_warning("Querying a DCSS type failed with rc=%ld\n", vmrc);
rc = dcss_diag_translate_rc (vmrc);
goto out_free;
}
@@ -368,7 +358,6 @@ query_segment_type (struct dcss_segment *seg)
* -EIO : could not perform query diagnose
* -ENOENT : no such segment
* -ENOTSUPP: multi-part segment cannot be used with linux
- * -ENOSPC : segment cannot be used (overlaps with storage)
* -ENOMEM : out of memory
* 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h
*/
@@ -480,9 +469,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
goto out_resource;
}
if (diag_cc > 1) {
- PRINT_WARN ("segment_load: could not load segment %s - "
- "diag returned error (%ld)\n",
- name, end_addr);
+ pr_warning("Loading DCSS %s failed with rc=%ld\n", name,
+ end_addr);
rc = dcss_diag_translate_rc(end_addr);
dcss_diag(&purgeseg_scode, seg->dcss_name,
&dummy, &dummy);
@@ -496,15 +484,13 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
*addr = seg->start_addr;
*end = seg->end;
if (do_nonshared)
- PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
- "type %s in non-shared mode\n", name,
- (void*)seg->start_addr, (void*)seg->end,
- segtype_string[seg->vm_segtype]);
+ pr_info("DCSS %s of range %p to %p and type %s loaded as "
+ "exclusive-writable\n", name, (void*) seg->start_addr,
+ (void*) seg->end, segtype_string[seg->vm_segtype]);
else {
- PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
- "type %s in shared mode\n", name,
- (void*)seg->start_addr, (void*)seg->end,
- segtype_string[seg->vm_segtype]);
+ pr_info("DCSS %s of range %p to %p and type %s loaded in "
+ "shared access mode\n", name, (void*) seg->start_addr,
+ (void*) seg->end, segtype_string[seg->vm_segtype]);
}
goto out;
out_resource:
@@ -593,14 +579,14 @@ segment_modify_shared (char *name, int do_nonshared)
goto out_unlock;
}
if (do_nonshared == seg->do_nonshared) {
- PRINT_INFO ("segment_modify_shared: not reloading segment %s"
- " - already in requested mode\n",name);
+ pr_info("DCSS %s is already in the requested access "
+ "mode\n", name);
rc = 0;
goto out_unlock;
}
if (atomic_read (&seg->ref_count) != 1) {
- PRINT_WARN ("segment_modify_shared: not reloading segment %s - "
- "segment is in use by other driver(s)\n",name);
+ pr_warning("DCSS %s is in use and cannot be reloaded\n",
+ name);
rc = -EAGAIN;
goto out_unlock;
}
@@ -613,8 +599,8 @@ segment_modify_shared (char *name, int do_nonshared)
seg->res->flags |= IORESOURCE_READONLY;
if (request_resource(&iomem_resource, seg->res)) {
- PRINT_WARN("segment_modify_shared: could not reload segment %s"
- " - overlapping resources\n", name);
+ pr_warning("DCSS %s overlaps with used memory resources "
+ "and cannot be reloaded\n", name);
rc = -EBUSY;
kfree(seg->res);
goto out_del_mem;
@@ -632,9 +618,8 @@ segment_modify_shared (char *name, int do_nonshared)
goto out_del_res;
}
if (diag_cc > 1) {
- PRINT_WARN ("segment_modify_shared: could not reload segment %s"
- " - diag returned error (%ld)\n",
- name, end_addr);
+ pr_warning("Reloading DCSS %s failed with rc=%ld\n", name,
+ end_addr);
rc = dcss_diag_translate_rc(end_addr);
goto out_del_res;
}
@@ -673,8 +658,7 @@ segment_unload(char *name)
mutex_lock(&dcss_lock);
seg = segment_by_name (name);
if (seg == NULL) {
- PRINT_ERR ("could not find segment %s in segment_unload, "
- "please report to linux390@de.ibm.com\n",name);
+ pr_err("Unloading unknown DCSS %s failed\n", name);
goto out_unlock;
}
if (atomic_dec_return(&seg->ref_count) != 0)
@@ -709,8 +693,7 @@ segment_save(char *name)
seg = segment_by_name (name);
if (seg == NULL) {
- PRINT_ERR("could not find segment %s in segment_save, please "
- "report to linux390@de.ibm.com\n", name);
+ pr_err("Saving unknown DCSS %s failed\n", name);
goto out;
}
@@ -727,14 +710,14 @@ segment_save(char *name)
response = 0;
cpcmd(cmd1, NULL, 0, &response);
if (response) {
- PRINT_ERR("segment_save: DEFSEG failed with response code %i\n",
- response);
+ pr_err("Saving a DCSS failed with DEFSEG response code "
+ "%i\n", response);
goto out;
}
cpcmd(cmd2, NULL, 0, &response);
if (response) {
- PRINT_ERR("segment_save: SAVESEG failed with response code %i\n",
- response);
+ pr_err("Saving a DCSS failed with SAVESEG response code "
+ "%i\n", response);
goto out;
}
out:
@@ -749,44 +732,41 @@ void segment_warning(int rc, char *seg_name)
{
switch (rc) {
case -ENOENT:
- PRINT_WARN("cannot load/query segment %s, "
- "does not exist\n", seg_name);
+ pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
break;
case -ENOSYS:
- PRINT_WARN("cannot load/query segment %s, "
- "not running on VM\n", seg_name);
+ pr_err("DCSS %s cannot be loaded or queried without "
+ "z/VM\n", seg_name);
break;
case -EIO:
- PRINT_WARN("cannot load/query segment %s, "
- "hardware error\n", seg_name);
+ pr_err("Loading or querying DCSS %s resulted in a "
+ "hardware error\n", seg_name);
break;
case -ENOTSUPP:
- PRINT_WARN("cannot load/query segment %s, "
- "is a multi-part segment\n", seg_name);
+ pr_err("DCSS %s has multiple page ranges and cannot be "
+ "loaded or queried\n", seg_name);
break;
case -ENOSPC:
- PRINT_WARN("cannot load/query segment %s, "
- "overlaps with storage\n", seg_name);
+ pr_err("DCSS %s overlaps with used storage and cannot "
+ "be loaded\n", seg_name);
break;
case -EBUSY:
- PRINT_WARN("cannot load/query segment %s, "
- "overlaps with already loaded dcss\n", seg_name);
+ pr_err("%s needs used memory resources and cannot be "
+ "loaded or queried\n", seg_name);
break;
case -EPERM:
- PRINT_WARN("cannot load/query segment %s, "
- "already loaded in incompatible mode\n", seg_name);
+ pr_err("DCSS %s is already loaded in a different access "
+ "mode\n", seg_name);
break;
case -ENOMEM:
- PRINT_WARN("cannot load/query segment %s, "
- "out of memory\n", seg_name);
+ pr_err("There is not enough memory to load or query "
+ "DCSS %s\n", seg_name);
break;
case -ERANGE:
- PRINT_WARN("cannot load/query segment %s, "
- "exceeds kernel mapping range\n", seg_name);
+ pr_err("DCSS %s exceeds the kernel mapping range (%lu) "
+ "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS);
break;
default:
- PRINT_WARN("cannot load/query segment %s, "
- "return value %i\n", seg_name, rc);
break;
}
}
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index ef3635b52fc0..0767827540b1 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -263,7 +263,7 @@ int s390_enable_sie(void)
/* lets check if we are allowed to replace the mm */
task_lock(tsk);
if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
- tsk->mm != tsk->active_mm || tsk->mm->ioctx_list) {
+ tsk->mm != tsk->active_mm || !hlist_empty(&tsk->mm->ioctx_list)) {
task_unlock(tsk);
return -EINVAL;
}
@@ -279,7 +279,7 @@ int s390_enable_sie(void)
/* Now lets check again if something happened */
task_lock(tsk);
if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
- tsk->mm != tsk->active_mm || tsk->mm->ioctx_list) {
+ tsk->mm != tsk->active_mm || !hlist_empty(&tsk->mm->ioctx_list)) {
mmput(mm);
task_unlock(tsk);
return -EINVAL;
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 5c9cbfc14c4d..f32a5197128d 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -13,6 +13,7 @@ config SUPERH
select HAVE_OPROFILE
select HAVE_GENERIC_DMA_COHERENT
select HAVE_IOREMAP_PROT if MMU
+ select HAVE_ARCH_TRACEHOOK
help
The SuperH is a RISC processor targeted for use in embedded systems
and consumer electronics; it was also used in the Sega Dreamcast
@@ -23,8 +24,10 @@ config SUPERH32
def_bool !SUPERH64
select HAVE_KPROBES
select HAVE_KRETPROBES
- select HAVE_ARCH_TRACEHOOK
select HAVE_FUNCTION_TRACER
+ select HAVE_FTRACE_MCOUNT_RECORD
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_ARCH_KGDB
config SUPERH64
def_bool y if CPU_SH5
@@ -55,8 +58,6 @@ config GENERIC_HARDIRQS
config GENERIC_HARDIRQS_NO__DO_IRQ
def_bool y
- depends on SUPERH32 && (!SH_DREAMCAST && !SH_SH4202_MICRODEV && \
- !SH_7751_SYSTEMH && !HD64461)
config GENERIC_IRQ_PROBE
def_bool y
@@ -85,10 +86,17 @@ config GENERIC_LOCKBREAK
config SYS_SUPPORTS_PM
bool
+ depends on !SMP
+
+config ARCH_SUSPEND_POSSIBLE
+ def_bool n
+
+config ARCH_HIBERNATION_POSSIBLE
+ def_bool n
config SYS_SUPPORTS_APM_EMULATION
bool
- select SYS_SUPPORTS_PM
+ select ARCH_SUSPEND_POSSIBLE
config SYS_SUPPORTS_SMP
bool
@@ -183,6 +191,11 @@ config CPU_SUBTYPE_SH7619
# SH-2A Processor Support
+config CPU_SUBTYPE_SH7201
+ bool "Support SH7201 processor"
+ select CPU_SH2A
+ select CPU_HAS_FPU
+
config CPU_SUBTYPE_SH7203
bool "Support SH7203 processor"
select CPU_SH2A
@@ -456,8 +469,12 @@ config SH_CPU_FREQ
depends on CPU_FREQ
select CPU_FREQ_TABLE
help
- This adds the cpufreq driver for SuperH. At present, only
- the SH-4 is supported.
+ This adds the cpufreq driver for SuperH. Any CPU that supports
+ clock rate rounding through the clock framework can use this
+ driver. While it will make the kernel slightly larger, this is
+ harmless for CPUs that don't support rate rounding. The driver
+ will also generate a notice in the boot log before disabling
+ itself if the CPU in question is not capable of rate rounding.
For details, take a look at <file:Documentation/cpu-freq>.
@@ -469,9 +486,6 @@ source "arch/sh/drivers/Kconfig"
endmenu
-config ISA_DMA_API
- bool
-
menu "Kernel features"
source kernel/Kconfig.hz
@@ -688,49 +702,6 @@ config MAPLE
Dreamcast with a serial line terminal or a remote network
connection.
-config CF_ENABLER
- bool "Compact Flash Enabler support"
- depends on SOLUTION_ENGINE || SH_SH03
- ---help---
- Compact Flash is a small, removable mass storage device introduced
- in 1994 originally as a PCMCIA device. If you say `Y' here, you
- compile in support for Compact Flash devices directly connected to
- a SuperH processor. A Compact Flash FAQ is available at
- <http://www.compactflash.org/faqs/faq.htm>.
-
- If your board has "Directly Connected" CompactFlash at area 5 or 6,
- you may want to enable this option. Then, you can use CF as
- primary IDE drive (only tested for SanDisk).
-
- If in doubt, select 'N'.
-
-choice
- prompt "Compact Flash Connection Area"
- depends on CF_ENABLER
- default CF_AREA6
-
-config CF_AREA5
- bool "Area5"
- help
- If your board has "Directly Connected" CompactFlash, You should
- select the area where your CF is connected to.
-
- - "Area5" if CompactFlash is connected to Area 5 (0x14000000)
- - "Area6" if it is connected to Area 6 (0x18000000)
-
- "Area6" will work for most boards.
-
-config CF_AREA6
- bool "Area6"
-
-endchoice
-
-config CF_BASE_ADDR
- hex
- depends on CF_ENABLER
- default "0xb8000000" if CF_AREA6
- default "0xb4000000" if CF_AREA5
-
source "arch/sh/drivers/pci/Kconfig"
source "drivers/pci/Kconfig"
@@ -748,13 +719,11 @@ source "fs/Kconfig.binfmt"
endmenu
menu "Power management options (EXPERIMENTAL)"
-depends on EXPERIMENTAL && SYS_SUPPORTS_PM
+depends on EXPERIMENTAL
-config ARCH_SUSPEND_POSSIBLE
- def_bool y
- depends on !SMP
+source "kernel/power/Kconfig"
-source kernel/power/Kconfig
+source "drivers/cpuidle/Kconfig"
endmenu
diff --git a/arch/sh/Kconfig.debug b/arch/sh/Kconfig.debug
index e6d2c8b11abd..0d62681f72a0 100644
--- a/arch/sh/Kconfig.debug
+++ b/arch/sh/Kconfig.debug
@@ -98,18 +98,29 @@ config IRQSTACKS
for handling hard and soft interrupts. This can help avoid
overflowing the process kernel stacks.
-config SH_KGDB
- bool "Include KGDB kernel debugger"
- select FRAME_POINTER
- select DEBUG_INFO
- depends on CPU_SH3 || CPU_SH4
+config DUMP_CODE
+ bool "Show disassembly of nearby code in register dumps"
+ depends on DEBUG_KERNEL && SUPERH32
+ default y if DEBUG_BUGVERBOSE
+ default n
+ help
+ This prints out a code trace of the instructions leading up to
+ the faulting instruction as a debugging aid. As this does grow
+ the kernel in size a bit, most users will want to say N here.
+
+ Those looking for more verbose debugging output should say Y.
+
+config SH_NO_BSS_INIT
+ bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
+ depends on DEBUG_KERNEL
+ default n
help
- Include in-kernel hooks for kgdb, the Linux kernel source level
- debugger. See <http://kgdb.sourceforge.net/> for more information.
- Unless you are intending to debug the kernel, say N here.
+ If running in painfully slow environments, such as an RTL
+ simulation or from remote memory via SHdebug, where the memory
+ can already be gauranteed to ber zeroed on boot, say Y.
-menu "KGDB configuration options"
- depends on SH_KGDB
+ For all other cases, say N. If this option seems perplexing, or
+ you aren't sure, say N.
config MORE_COMPILE_OPTIONS
bool "Add any additional compile options"
@@ -122,85 +133,16 @@ config COMPILE_OPTIONS
string "Additional compile arguments"
depends on MORE_COMPILE_OPTIONS
-config KGDB_NMI
- def_bool n
- prompt "Enter KGDB on NMI"
-
-config SH_KGDB_CONSOLE
- def_bool n
- prompt "Console messages through GDB"
- depends on !SERIAL_SH_SCI_CONSOLE && SERIAL_SH_SCI=y
- select SERIAL_CORE_CONSOLE
-
-config KGDB_SYSRQ
- def_bool y
- prompt "Allow SysRq 'G' to enter KGDB"
- depends on MAGIC_SYSRQ
-
-comment "Serial port setup"
-
-config KGDB_DEFPORT
- int "Port number (ttySCn)"
- default "1"
-
-config KGDB_DEFBAUD
- int "Baud rate"
- default "115200"
-
-choice
- prompt "Parity"
- depends on SH_KGDB
- default KGDB_DEFPARITY_N
-
-config KGDB_DEFPARITY_N
- bool "None"
-
-config KGDB_DEFPARITY_E
- bool "Even"
-
-config KGDB_DEFPARITY_O
- bool "Odd"
-
-endchoice
-
-choice
- prompt "Data bits"
- depends on SH_KGDB
- default KGDB_DEFBITS_8
-
-config KGDB_DEFBITS_8
- bool "8"
-
-config KGDB_DEFBITS_7
- bool "7"
-
-endchoice
-
-endmenu
-
-if SUPERH64
-
-config SH64_PROC_ASIDS
- bool "Debug: report ASIDs through /proc/asids"
- depends on PROC_FS && MMU
-
config SH64_SR_WATCH
bool "Debug: set SR.WATCH to enable hardware watchpoints and trace"
+ depends on SUPERH64
config POOR_MANS_STRACE
bool "Debug: enable rudimentary strace facility"
+ depends on SUPERH64
help
This option allows system calls to be traced to the console. It also
aids in detecting kernel stack underflow. It is useful for debugging
early-userland problems (e.g. init incurring fatal exceptions.)
-config SH_ALPHANUMERIC
- bool "Enable debug outputs to on-board alphanumeric display"
- depends on SH_CAYMAN
-
-config SH_NO_BSS_INIT
- bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
-
-endif
-
endmenu
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index c43eb0d7fa3b..4067b0d9287b 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -32,6 +32,7 @@ cflags-$(CONFIG_CPU_SH4) := $(call cc-option,-m4,) \
$(call cc-option,-mno-implicit-fp,-m4-nofpu)
cflags-$(CONFIG_CPU_SH4A) += $(call cc-option,-m4a,) \
$(call cc-option,-m4a-nofpu,)
+cflags-$(CONFIG_CPU_SH4AL_DSP) += $(call cc-option,-m4al,)
cflags-$(CONFIG_CPU_SH5) := $(call cc-option,-m5-32media-nofpu,)
ifeq ($(cflags-y),)
@@ -39,22 +40,16 @@ ifeq ($(cflags-y),)
# In the case where we are stuck with a compiler that has been uselessly
# restricted to a particular ISA, a favourite default of newer GCCs when
# extensive multilib targets are not provided, ensure we get the best fit
-# regarding FP generation. This is necessary to avoid references to FP
-# variants in libgcc where integer variants exist, which otherwise result
-# in link errors. This is intentionally stupid (albeit many orders of
-# magnitude less than GCC's default behaviour), as anything with a large
-# number of multilib targets better have been built correctly for
-# the target in mind.
+# regarding FP generation. This is intentionally stupid (albeit many
+# orders of magnitude less than GCC's default behaviour), as anything
+# with a large number of multilib targets better have been built
+# correctly for the target in mind.
#
cflags-y += $(shell $(CC) $(KBUILD_CFLAGS) -print-multi-lib | \
grep nofpu | sed q | sed -e 's/^/-/;s/;.*$$//')
-endif
-
-cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mb
-cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -ml
-
-cflags-y += $(call cc-option,-mno-fdpic)
-
+# At this point, anything goes.
+isaflags-y := $(call as-option,-Wa$(comma)-isa=any,)
+else
#
# -Wa,-isa= tuning implies -Wa,-dsp for the versions of binutils that
# support it, while -Wa,-dsp by itself limits the range of usable opcodes
@@ -67,7 +62,12 @@ isaflags-y := $(call as-option,-Wa$(comma)-isa=$(isa-y),)
isaflags-$(CONFIG_SH_DSP) := \
$(call as-option,-Wa$(comma)-isa=$(isa-y),-Wa$(comma)-dsp)
+endif
+cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mb
+cflags-$(CONFIG_CPU_LITTLE_ENDIAN) += -ml
+
+cflags-y += $(call cc-option,-mno-fdpic)
cflags-y += $(isaflags-y) -ffreestanding
cflags-$(CONFIG_MORE_COMPILE_OPTIONS) += \
@@ -79,6 +79,9 @@ OBJCOPYFLAGS := -O binary -R .note -R .note.gnu.build-id -R .comment \
# Give the various platforms the opportunity to set default image types
defaultimage-$(CONFIG_SUPERH32) := zImage
defaultimage-$(CONFIG_SH_SH7785LCR) := uImage
+defaultimage-$(CONFIG_SH_RSK) := uImage
+defaultimage-$(CONFIG_SH_7206_SOLUTION_ENGINE) := vmlinux
+defaultimage-$(CONFIG_SH_7619_SOLUTION_ENGINE) := vmlinux
# Set some sensible Kbuild defaults
KBUILD_DEFCONFIG := shx3_defconfig
@@ -132,6 +135,7 @@ machdir-$(CONFIG_SH_LANDISK) += mach-landisk
machdir-$(CONFIG_SH_TITAN) += mach-titan
machdir-$(CONFIG_SH_LBOX_RE2) += mach-lboxre2
machdir-$(CONFIG_SH_CAYMAN) += mach-cayman
+machdir-$(CONFIG_SH_RSK) += mach-rsk
ifneq ($(machdir-y),)
core-y += $(addprefix arch/sh/boards/, \
@@ -173,11 +177,8 @@ KBUILD_CFLAGS += -pipe $(cflags-y)
KBUILD_CPPFLAGS += $(cflags-y)
KBUILD_AFLAGS += $(cflags-y)
-LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
-
libs-$(CONFIG_SUPERH32) := arch/sh/lib/ $(libs-y)
libs-$(CONFIG_SUPERH64) := arch/sh/lib64/ $(libs-y)
-libs-y += $(LIBGCC)
PHONY += maketools FORCE
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 50467f9d0d0b..861914747e4e 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -126,10 +126,12 @@ config SH_RTS7751R2D
Select RTS7751R2D if configuring for a Renesas Technology
Sales SH-Graphics board.
-config SH_RSK7203
- bool "RSK7203"
- select GENERIC_GPIO
- depends on CPU_SUBTYPE_SH7203
+config SH_RSK
+ bool "Renesas Starter Kit"
+ depends on CPU_SUBTYPE_SH7201 || CPU_SUBTYPE_SH7203
+ help
+ Select this option if configuring for any of the RSK+ MCU
+ evaluation platforms.
config SH_SDK7780
bool "SDK7780R3"
@@ -253,6 +255,7 @@ source "arch/sh/boards/mach-r2d/Kconfig"
source "arch/sh/boards/mach-highlander/Kconfig"
source "arch/sh/boards/mach-sdk7780/Kconfig"
source "arch/sh/boards/mach-migor/Kconfig"
+source "arch/sh/boards/mach-rsk/Kconfig"
if SH_MAGIC_PANEL_R2
diff --git a/arch/sh/boards/Makefile b/arch/sh/boards/Makefile
index d9efa3923721..269ae2be49ef 100644
--- a/arch/sh/boards/Makefile
+++ b/arch/sh/boards/Makefile
@@ -3,7 +3,6 @@
#
obj-$(CONFIG_SH_AP325RXA) += board-ap325rxa.o
obj-$(CONFIG_SH_MAGIC_PANEL_R2) += board-magicpanelr2.o
-obj-$(CONFIG_SH_RSK7203) += board-rsk7203.o
obj-$(CONFIG_SH_SH7785LCR) += board-sh7785lcr.o
obj-$(CONFIG_SH_SHMIN) += board-shmin.o
obj-$(CONFIG_SH_EDOSK7760) += board-edosk7760.o
diff --git a/arch/sh/boards/board-ap325rxa.c b/arch/sh/boards/board-ap325rxa.c
index 8881a643ac32..1c67cba6e34f 100644
--- a/arch/sh/boards/board-ap325rxa.c
+++ b/arch/sh/boards/board-ap325rxa.c
@@ -197,6 +197,10 @@ static struct resource lcdc_resources[] = {
.end = 0xfe941fff,
.flags = IORESOURCE_MEM,
},
+ [1] = {
+ .start = 28,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct platform_device lcdc_device = {
@@ -303,6 +307,7 @@ static struct resource ceu_resources[] = {
static struct platform_device ceu_device = {
.name = "sh_mobile_ceu",
+ .id = 0, /* "ceu0" clock */
.num_resources = ARRAY_SIZE(ceu_resources),
.resource = ceu_resources,
.dev = {
@@ -344,7 +349,6 @@ static int __init ap325rxa_devices_setup(void)
gpio_export(GPIO_PTF7, 0);
/* LCDC */
- clk_always_enable("mstp200");
gpio_request(GPIO_FN_LCDD15, NULL);
gpio_request(GPIO_FN_LCDD14, NULL);
gpio_request(GPIO_FN_LCDD13, NULL);
@@ -375,7 +379,6 @@ static int __init ap325rxa_devices_setup(void)
gpio_direction_output(GPIO_PTS3, 1);
/* CEU */
- clk_always_enable("mstp203");
gpio_request(GPIO_FN_VIO_CLK2, NULL);
gpio_request(GPIO_FN_VIO_VD2, NULL);
gpio_request(GPIO_FN_VIO_HD2, NULL);
diff --git a/arch/sh/boards/board-rsk7203.c b/arch/sh/boards/board-rsk7203.c
deleted file mode 100644
index 58266f06134a..000000000000
--- a/arch/sh/boards/board-rsk7203.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Renesas Technology Europe RSK+ 7203 Support.
- *
- * Copyright (C) 2008 Paul Mundt
- *
- * 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/init.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/mtd/map.h>
-#include <linux/smc911x.h>
-#include <linux/gpio.h>
-#include <linux/leds.h>
-#include <asm/machvec.h>
-#include <asm/io.h>
-#include <cpu/sh7203.h>
-
-static struct smc911x_platdata smc911x_info = {
- .flags = SMC911X_USE_16BIT,
- .irq_flags = IRQF_TRIGGER_LOW,
-};
-
-static struct resource smc911x_resources[] = {
- [0] = {
- .start = 0x24000000,
- .end = 0x24000000 + 0x100,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 64,
- .end = 64,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device smc911x_device = {
- .name = "smc911x",
- .id = -1,
- .num_resources = ARRAY_SIZE(smc911x_resources),
- .resource = smc911x_resources,
- .dev = {
- .platform_data = &smc911x_info,
- },
-};
-
-static const char *probes[] = { "cmdlinepart", NULL };
-
-static struct mtd_partition *parsed_partitions;
-
-static struct mtd_partition rsk7203_partitions[] = {
- {
- .name = "Bootloader",
- .offset = 0x00000000,
- .size = 0x00040000,
- .mask_flags = MTD_WRITEABLE,
- }, {
- .name = "Kernel",
- .offset = MTDPART_OFS_NXTBLK,
- .size = 0x001c0000,
- }, {
- .name = "Flash_FS",
- .offset = MTDPART_OFS_NXTBLK,
- .size = MTDPART_SIZ_FULL,
- }
-};
-
-static struct physmap_flash_data flash_data = {
- .width = 2,
-};
-
-static struct resource flash_resource = {
- .start = 0x20000000,
- .end = 0x20400000,
- .flags = IORESOURCE_MEM,
-};
-
-static struct platform_device flash_device = {
- .name = "physmap-flash",
- .id = -1,
- .resource = &flash_resource,
- .num_resources = 1,
- .dev = {
- .platform_data = &flash_data,
- },
-};
-
-static struct mtd_info *flash_mtd;
-
-static struct map_info rsk7203_flash_map = {
- .name = "RSK+ Flash",
- .size = 0x400000,
- .bankwidth = 2,
-};
-
-static void __init set_mtd_partitions(void)
-{
- int nr_parts = 0;
-
- simple_map_init(&rsk7203_flash_map);
- flash_mtd = do_map_probe("cfi_probe", &rsk7203_flash_map);
- nr_parts = parse_mtd_partitions(flash_mtd, probes,
- &parsed_partitions, 0);
- /* If there is no partition table, used the hard coded table */
- if (nr_parts <= 0) {
- flash_data.parts = rsk7203_partitions;
- flash_data.nr_parts = ARRAY_SIZE(rsk7203_partitions);
- } else {
- flash_data.nr_parts = nr_parts;
- flash_data.parts = parsed_partitions;
- }
-}
-
-static struct gpio_led rsk7203_gpio_leds[] = {
- {
- .name = "green",
- .gpio = GPIO_PE10,
- .active_low = 1,
- }, {
- .name = "orange",
- .default_trigger = "nand-disk",
- .gpio = GPIO_PE12,
- .active_low = 1,
- }, {
- .name = "red:timer",
- .default_trigger = "timer",
- .gpio = GPIO_PC14,
- .active_low = 1,
- }, {
- .name = "red:heartbeat",
- .default_trigger = "heartbeat",
- .gpio = GPIO_PE11,
- .active_low = 1,
- },
-};
-
-static struct gpio_led_platform_data rsk7203_gpio_leds_info = {
- .leds = rsk7203_gpio_leds,
- .num_leds = ARRAY_SIZE(rsk7203_gpio_leds),
-};
-
-static struct platform_device led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &rsk7203_gpio_leds_info,
- },
-};
-
-static struct platform_device *rsk7203_devices[] __initdata = {
- &smc911x_device,
- &flash_device,
- &led_device,
-};
-
-static int __init rsk7203_devices_setup(void)
-{
- /* Select pins for SCIF0 */
- gpio_request(GPIO_FN_TXD0, NULL);
- gpio_request(GPIO_FN_RXD0, NULL);
-
- set_mtd_partitions();
- return platform_add_devices(rsk7203_devices,
- ARRAY_SIZE(rsk7203_devices));
-}
-device_initcall(rsk7203_devices_setup);
-
-/*
- * The Machine Vector
- */
-static struct sh_machine_vector mv_rsk7203 __initmv = {
- .mv_name = "RSK+7203",
-};
diff --git a/arch/sh/boards/board-shmin.c b/arch/sh/boards/board-shmin.c
index 5cc0867de5ab..b1dcbbc89188 100644
--- a/arch/sh/boards/board-shmin.c
+++ b/arch/sh/boards/board-shmin.c
@@ -22,21 +22,13 @@ static void __init init_shmin_irq(void)
plat_irq_setup_pins(IRQ_MODE_IRQ);
}
-static void __iomem *shmin_ioport_map(unsigned long port, unsigned int size)
+static void __init shmin_setup(char **cmdline_p)
{
- static int dummy;
-
- if ((port & ~0x1f) == SHMIN_NE_BASE)
- return (void __iomem *)(SHMIN_IO_BASE + port);
-
- dummy = 0;
-
- return &dummy;
-
+ __set_io_port_base(SHMIN_IO_BASE);
}
static struct sh_machine_vector mv_shmin __initmv = {
.mv_name = "SHMIN",
+ .mv_setup = shmin_setup,
.mv_init_irq = init_shmin_irq,
- .mv_ioport_map = shmin_ioport_map,
};
diff --git a/arch/sh/boards/mach-cayman/Makefile b/arch/sh/boards/mach-cayman/Makefile
index 489a8f867368..cafe1ac3b29c 100644
--- a/arch/sh/boards/mach-cayman/Makefile
+++ b/arch/sh/boards/mach-cayman/Makefile
@@ -2,4 +2,3 @@
# Makefile for the Hitachi Cayman specific parts of the kernel
#
obj-y := setup.o irq.o
-obj-$(CONFIG_HEARTBEAT) += led.o
diff --git a/arch/sh/boards/mach-cayman/irq.c b/arch/sh/boards/mach-cayman/irq.c
index ceb37ae92c70..da62ad516994 100644
--- a/arch/sh/boards/mach-cayman/irq.c
+++ b/arch/sh/boards/mach-cayman/irq.c
@@ -94,31 +94,11 @@ static void ack_cayman_irq(unsigned int irq)
disable_cayman_irq(irq);
}
-static void end_cayman_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_cayman_irq(irq);
-}
-
-static unsigned int startup_cayman_irq(unsigned int irq)
-{
- enable_cayman_irq(irq);
- return 0; /* never anything pending */
-}
-
-static void shutdown_cayman_irq(unsigned int irq)
-{
- disable_cayman_irq(irq);
-}
-
-struct hw_interrupt_type cayman_irq_type = {
- .typename = "Cayman-IRQ",
- .startup = startup_cayman_irq,
- .shutdown = shutdown_cayman_irq,
- .enable = enable_cayman_irq,
- .disable = disable_cayman_irq,
- .ack = ack_cayman_irq,
- .end = end_cayman_irq,
+struct irq_chip cayman_irq_type = {
+ .name = "Cayman-IRQ",
+ .unmask = enable_cayman_irq,
+ .mask = disable_cayman_irq,
+ .mask_ack = ack_cayman_irq,
};
int cayman_irq_demux(int evt)
@@ -187,8 +167,9 @@ void init_cayman_irq(void)
return;
}
- for (i=0; i<NR_EXT_IRQS; i++) {
- irq_desc[START_EXT_IRQS + i].chip = &cayman_irq_type;
+ for (i = 0; i < NR_EXT_IRQS; i++) {
+ set_irq_chip_and_handler(START_EXT_IRQS + i, &cayman_irq_type,
+ handle_level_irq);
}
/* Setup the SMSC interrupt */
diff --git a/arch/sh/boards/mach-cayman/led.c b/arch/sh/boards/mach-cayman/led.c
deleted file mode 100644
index a808eac4ecd6..000000000000
--- a/arch/sh/boards/mach-cayman/led.c
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * arch/sh/boards/cayman/led.c
- *
- * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Flash the LEDs
- */
-#include <asm/io.h>
-
-/*
-** It is supposed these functions to be used for a low level
-** debugging (via Cayman LEDs), hence to be available as soon
-** as possible.
-** Unfortunately Cayman LEDs relies on Cayman EPLD to be mapped
-** (this happen when IRQ are initialized... quite late).
-** These triky dependencies should be removed. Temporary, it
-** may be enough to NOP until EPLD is mapped.
-*/
-
-extern unsigned long epld_virt;
-
-#define LED_ADDR (epld_virt + 0x008)
-#define HDSP2534_ADDR (epld_virt + 0x100)
-
-void mach_led(int position, int value)
-{
- if (!epld_virt)
- return;
-
- if (value)
- ctrl_outl(0, LED_ADDR);
- else
- ctrl_outl(1, LED_ADDR);
-
-}
-
-void mach_alphanum(int position, unsigned char value)
-{
- if (!epld_virt)
- return;
-
- ctrl_outb(value, HDSP2534_ADDR + 0xe0 + (position << 2));
-}
-
-void mach_alphanum_brightness(int setting)
-{
- ctrl_outb(setting & 7, HDSP2534_ADDR + 0xc0);
-}
diff --git a/arch/sh/boards/mach-dreamcast/irq.c b/arch/sh/boards/mach-dreamcast/irq.c
index 67bdc33dd411..f55fc8e795e9 100644
--- a/arch/sh/boards/mach-dreamcast/irq.c
+++ b/arch/sh/boards/mach-dreamcast/irq.c
@@ -10,106 +10,90 @@
*/
#include <linux/irq.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/irq.h>
#include <mach/sysasic.h>
-/* Dreamcast System ASIC Hardware Events -
-
- The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
- hardware events from system peripherals and triggering an SH7750 IRQ.
- Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
- set in the Event Mask Registers (EMRs). When a hardware event is
- triggered, it's corresponding bit in the Event Status Registers (ESRs)
- is set, and that bit should be rewritten to the ESR to acknowledge that
- event.
-
- There are three 32-bit ESRs located at 0xa05f8900 - 0xa05f6908. Event
- types can be found in include/asm-sh/dreamcast/sysasic.h. There are three
- groups of EMRs that parallel the ESRs. Each EMR group corresponds to an
- IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13, 0xa05f6920 - 0xa05f6928
- triggers IRQ 11, and 0xa05f6930 - 0xa05f6938 triggers IRQ 9.
-
- In the kernel, these events are mapped to virtual IRQs so that drivers can
- respond to them as they would a normal interrupt. In order to keep this
- mapping simple, the events are mapped as:
-
- 6900/6910 - Events 0-31, IRQ 13
- 6904/6924 - Events 32-63, IRQ 11
- 6908/6938 - Events 64-95, IRQ 9
-
-*/
+/*
+ * Dreamcast System ASIC Hardware Events -
+ *
+ * The Dreamcast's System ASIC (a.k.a. Holly) is responsible for receiving
+ * hardware events from system peripherals and triggering an SH7750 IRQ.
+ * Hardware events can trigger IRQs 13, 11, or 9 depending on which bits are
+ * set in the Event Mask Registers (EMRs). When a hardware event is
+ * triggered, its corresponding bit in the Event Status Registers (ESRs)
+ * is set, and that bit should be rewritten to the ESR to acknowledge that
+ * event.
+ *
+ * There are three 32-bit ESRs located at 0xa05f6900 - 0xa05f6908. Event
+ * types can be found in arch/sh/include/mach-dreamcast/mach/sysasic.h.
+ * There are three groups of EMRs that parallel the ESRs. Each EMR group
+ * corresponds to an IRQ, so 0xa05f6910 - 0xa05f6918 triggers IRQ 13,
+ * 0xa05f6920 - 0xa05f6928 triggers IRQ 11, and 0xa05f6930 - 0xa05f6938
+ * triggers IRQ 9.
+ *
+ * In the kernel, these events are mapped to virtual IRQs so that drivers can
+ * respond to them as they would a normal interrupt. In order to keep this
+ * mapping simple, the events are mapped as:
+ *
+ * 6900/6910 - Events 0-31, IRQ 13
+ * 6904/6924 - Events 32-63, IRQ 11
+ * 6908/6938 - Events 64-95, IRQ 9
+ *
+ */
#define ESR_BASE 0x005f6900 /* Base event status register */
#define EMR_BASE 0x005f6910 /* Base event mask register */
-/* Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
- 1 = 0x6920, 2 = 0x6930; also determine the event offset */
+/*
+ * Helps us determine the EMR group that this event belongs to: 0 = 0x6910,
+ * 1 = 0x6920, 2 = 0x6930; also determine the event offset.
+ */
#define LEVEL(event) (((event) - HW_EVENT_IRQ_BASE) / 32)
/* Return the hardware event's bit positon within the EMR/ESR */
#define EVENT_BIT(event) (((event) - HW_EVENT_IRQ_BASE) & 31)
-/* For each of these *_irq routines, the IRQ passed in is the virtual IRQ
- (logically mapped to the corresponding bit for the hardware event). */
+/*
+ * For each of these *_irq routines, the IRQ passed in is the virtual IRQ
+ * (logically mapped to the corresponding bit for the hardware event).
+ */
/* Disable the hardware event by masking its bit in its EMR */
static inline void disable_systemasic_irq(unsigned int irq)
{
- __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
- __u32 mask;
+ __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
+ __u32 mask;
- mask = inl(emr);
- mask &= ~(1 << EVENT_BIT(irq));
- outl(mask, emr);
+ mask = inl(emr);
+ mask &= ~(1 << EVENT_BIT(irq));
+ outl(mask, emr);
}
/* Enable the hardware event by setting its bit in its EMR */
static inline void enable_systemasic_irq(unsigned int irq)
{
- __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
- __u32 mask;
+ __u32 emr = EMR_BASE + (LEVEL(irq) << 4) + (LEVEL(irq) << 2);
+ __u32 mask;
- mask = inl(emr);
- mask |= (1 << EVENT_BIT(irq));
- outl(mask, emr);
+ mask = inl(emr);
+ mask |= (1 << EVENT_BIT(irq));
+ outl(mask, emr);
}
/* Acknowledge a hardware event by writing its bit back to its ESR */
-static void ack_systemasic_irq(unsigned int irq)
-{
- __u32 esr = ESR_BASE + (LEVEL(irq) << 2);
- disable_systemasic_irq(irq);
- outl((1 << EVENT_BIT(irq)), esr);
-}
-
-/* After a IRQ has been ack'd and responded to, it needs to be renabled */
-static void end_systemasic_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_systemasic_irq(irq);
-}
-
-static unsigned int startup_systemasic_irq(unsigned int irq)
-{
- enable_systemasic_irq(irq);
-
- return 0;
-}
-
-static void shutdown_systemasic_irq(unsigned int irq)
+static void mask_ack_systemasic_irq(unsigned int irq)
{
- disable_systemasic_irq(irq);
+ __u32 esr = ESR_BASE + (LEVEL(irq) << 2);
+ disable_systemasic_irq(irq);
+ outl((1 << EVENT_BIT(irq)), esr);
}
-struct hw_interrupt_type systemasic_int = {
- .typename = "System ASIC",
- .startup = startup_systemasic_irq,
- .shutdown = shutdown_systemasic_irq,
- .enable = enable_systemasic_irq,
- .disable = disable_systemasic_irq,
- .ack = ack_systemasic_irq,
- .end = end_systemasic_irq,
+struct irq_chip systemasic_int = {
+ .name = "System ASIC",
+ .mask = disable_systemasic_irq,
+ .mask_ack = mask_ack_systemasic_irq,
+ .unmask = enable_systemasic_irq,
};
/*
@@ -117,37 +101,37 @@ struct hw_interrupt_type systemasic_int = {
*/
int systemasic_irq_demux(int irq)
{
- __u32 emr, esr, status, level;
- __u32 j, bit;
-
- switch (irq) {
- case 13:
- level = 0;
- break;
- case 11:
- level = 1;
- break;
- case 9:
- level = 2;
- break;
- default:
- return irq;
- }
- emr = EMR_BASE + (level << 4) + (level << 2);
- esr = ESR_BASE + (level << 2);
-
- /* Mask the ESR to filter any spurious, unwanted interrupts */
- status = inl(esr);
- status &= inl(emr);
-
- /* Now scan and find the first set bit as the event to map */
- for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
- if (status & bit) {
- irq = HW_EVENT_IRQ_BASE + j + (level << 5);
- return irq;
- }
- }
-
- /* Not reached */
- return irq;
+ __u32 emr, esr, status, level;
+ __u32 j, bit;
+
+ switch (irq) {
+ case 13:
+ level = 0;
+ break;
+ case 11:
+ level = 1;
+ break;
+ case 9:
+ level = 2;
+ break;
+ default:
+ return irq;
+ }
+ emr = EMR_BASE + (level << 4) + (level << 2);
+ esr = ESR_BASE + (level << 2);
+
+ /* Mask the ESR to filter any spurious, unwanted interrupts */
+ status = inl(esr);
+ status &= inl(emr);
+
+ /* Now scan and find the first set bit as the event to map */
+ for (bit = 1, j = 0; j < 32; bit <<= 1, j++) {
+ if (status & bit) {
+ irq = HW_EVENT_IRQ_BASE + j + (level << 5);
+ return irq;
+ }
+ }
+
+ /* Not reached */
+ return irq;
}
diff --git a/arch/sh/boards/mach-dreamcast/setup.c b/arch/sh/boards/mach-dreamcast/setup.c
index 7d944fc75e93..d1bee4884cd6 100644
--- a/arch/sh/boards/mach-dreamcast/setup.c
+++ b/arch/sh/boards/mach-dreamcast/setup.c
@@ -28,7 +28,7 @@
#include <asm/machvec.h>
#include <mach/sysasic.h>
-extern struct hw_interrupt_type systemasic_int;
+extern struct irq_chip systemasic_int;
extern void aica_time_init(void);
extern int gapspci_init(void);
extern int systemasic_irq_demux(int);
@@ -47,7 +47,8 @@ static void __init dreamcast_setup(char **cmdline_p)
/* Assign all virtual IRQs to the System ASIC int. handler */
for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++)
- irq_desc[i].chip = &systemasic_int;
+ set_irq_chip_and_handler(i, &systemasic_int,
+ handle_level_irq);
board_time_init = aica_time_init;
diff --git a/arch/sh/boards/mach-edosk7705/Makefile b/arch/sh/boards/mach-edosk7705/Makefile
index 14bdd531f116..cd54acb51499 100644
--- a/arch/sh/boards/mach-edosk7705/Makefile
+++ b/arch/sh/boards/mach-edosk7705/Makefile
@@ -3,4 +3,3 @@
#
obj-y := setup.o io.o
-
diff --git a/arch/sh/boards/mach-edosk7705/io.c b/arch/sh/boards/mach-edosk7705/io.c
index 7d153e50a01b..5b9c57c43241 100644
--- a/arch/sh/boards/mach-edosk7705/io.c
+++ b/arch/sh/boards/mach-edosk7705/io.c
@@ -10,28 +10,24 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <mach/edosk7705.h>
#include <asm/addrspace.h>
#define SMC_IOADDR 0xA2000000
-#define maybebadio(name,port) \
- printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
- #name, (port), (__u32) __builtin_return_address(0))
-
/* Map the Ethernet addresses as if it is at 0x300 - 0x320 */
-unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
+static unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
{
- if (port >= 0x300 && port < 0x320) {
- /* SMC91C96 registers are 4 byte aligned rather than the
- * usual 2 byte!
- */
- return SMC_IOADDR + ( (port - 0x300) * 2);
- }
+ /*
+ * SMC91C96 registers are 4 byte aligned rather than the
+ * usual 2 byte!
+ */
+ if (port >= 0x300 && port < 0x320)
+ return SMC_IOADDR + ((port - 0x300) * 2);
- maybebadio(sh_edosk7705_isa_port2addr, port);
- return port;
+ maybebadio(port);
+ return port;
}
/* Trying to read / write bytes on odd-byte boundaries to the Ethernet
@@ -42,53 +38,34 @@ unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
*/
unsigned char sh_edosk7705_inb(unsigned long port)
{
- if (port >= 0x300 && port < 0x320 && port & 0x01) {
- return (volatile unsigned char)(generic_inw(port -1) >> 8);
- }
- return *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port);
-}
+ if (port >= 0x300 && port < 0x320 && port & 0x01)
+ return __raw_readw(port - 1) >> 8;
-unsigned int sh_edosk7705_inl(unsigned long port)
-{
- return *(volatile unsigned long *)port;
+ return __raw_readb(sh_edosk7705_isa_port2addr(port));
}
void sh_edosk7705_outb(unsigned char value, unsigned long port)
{
if (port >= 0x300 && port < 0x320 && port & 0x01) {
- generic_outw(((unsigned short)value << 8), port -1);
+ __raw_writew(((unsigned short)value << 8), port - 1);
return;
}
- *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port) = value;
-}
-void sh_edosk7705_outl(unsigned int value, unsigned long port)
-{
- *(volatile unsigned long *)port = value;
+ __raw_writeb(value, sh_edosk7705_isa_port2addr(port));
}
void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count)
{
unsigned char *p = addr;
- while (count--) *p++ = sh_edosk7705_inb(port);
-}
-void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count)
-{
- unsigned long *p = (unsigned long*)addr;
while (count--)
- *p++ = *(volatile unsigned long *)port;
+ *p++ = sh_edosk7705_inb(port);
}
void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count)
{
- unsigned char *p = (unsigned char*)addr;
- while (count--) sh_edosk7705_outb(*p++, port);
-}
+ unsigned char *p = (unsigned char *)addr;
-void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned long *p = (unsigned long*)addr;
- while (count--) sh_edosk7705_outl(*p++, port);
+ while (count--)
+ sh_edosk7705_outb(*p++, port);
}
-
diff --git a/arch/sh/boards/mach-edosk7705/setup.c b/arch/sh/boards/mach-edosk7705/setup.c
index ab3f47bffdf3..d59225e26fb9 100644
--- a/arch/sh/boards/mach-edosk7705/setup.c
+++ b/arch/sh/boards/mach-edosk7705/setup.c
@@ -9,6 +9,7 @@
* board by S. Dunn, 2003.
*/
#include <linux/init.h>
+#include <linux/irq.h>
#include <asm/machvec.h>
#include <mach/edosk7705.h>
@@ -26,18 +27,10 @@ static struct sh_machine_vector mv_edosk7705 __initmv = {
.mv_nr_irqs = 80,
.mv_inb = sh_edosk7705_inb,
- .mv_inl = sh_edosk7705_inl,
.mv_outb = sh_edosk7705_outb,
- .mv_outl = sh_edosk7705_outl,
-
- .mv_inl_p = sh_edosk7705_inl,
- .mv_outl_p = sh_edosk7705_outl,
.mv_insb = sh_edosk7705_insb,
- .mv_insl = sh_edosk7705_insl,
.mv_outsb = sh_edosk7705_outsb,
- .mv_outsl = sh_edosk7705_outsl,
- .mv_isa_port2addr = sh_edosk7705_isa_port2addr,
.mv_init_irq = sh_edosk7705_init_irq,
};
diff --git a/arch/sh/boards/mach-hp6xx/pm.c b/arch/sh/boards/mach-hp6xx/pm.c
index 64af1f2eef05..d936c1af7620 100644
--- a/arch/sh/boards/mach-hp6xx/pm.c
+++ b/arch/sh/boards/mach-hp6xx/pm.c
@@ -10,15 +10,91 @@
#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
#include <asm/io.h>
#include <asm/hd64461.h>
#include <mach/hp6xx.h>
#include <cpu/dac.h>
-#include <asm/pm.h>
+#include <asm/freq.h>
+#include <asm/watchdog.h>
+
+#define INTR_OFFSET 0x600
#define STBCR 0xffffff82
#define STBCR2 0xffffff88
+#define STBCR_STBY 0x80
+#define STBCR_MSTP2 0x04
+
+#define MCR 0xffffff68
+#define RTCNT 0xffffff70
+
+#define MCR_RMODE 2
+#define MCR_RFSH 4
+
+extern u8 wakeup_start;
+extern u8 wakeup_end;
+
+static void pm_enter(void)
+{
+ u8 stbcr, csr;
+ u16 frqcr, mcr;
+ u32 vbr_new, vbr_old;
+
+ set_bl_bit();
+
+ /* set wdt */
+ csr = sh_wdt_read_csr();
+ csr &= ~WTCSR_TME;
+ csr |= WTCSR_CKS_4096;
+ sh_wdt_write_csr(csr);
+ csr = sh_wdt_read_csr();
+ sh_wdt_write_cnt(0);
+
+ /* disable PLL1 */
+ frqcr = ctrl_inw(FRQCR);
+ frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY);
+ ctrl_outw(frqcr, FRQCR);
+
+ /* enable standby */
+ stbcr = ctrl_inb(STBCR);
+ ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR);
+
+ /* set self-refresh */
+ mcr = ctrl_inw(MCR);
+ ctrl_outw(mcr & ~MCR_RFSH, MCR);
+
+ /* set interrupt handler */
+ asm volatile("stc vbr, %0" : "=r" (vbr_old));
+ vbr_new = get_zeroed_page(GFP_ATOMIC);
+ udelay(50);
+ memcpy((void*)(vbr_new + INTR_OFFSET),
+ &wakeup_start, &wakeup_end - &wakeup_start);
+ asm volatile("ldc %0, vbr" : : "r" (vbr_new));
+
+ ctrl_outw(0, RTCNT);
+ ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR);
+
+ cpu_sleep();
+
+ asm volatile("ldc %0, vbr" : : "r" (vbr_old));
+
+ free_page(vbr_new);
+
+ /* enable PLL1 */
+ frqcr = ctrl_inw(FRQCR);
+ frqcr |= FRQCR_PSTBY;
+ ctrl_outw(frqcr, FRQCR);
+ udelay(50);
+ frqcr |= FRQCR_PLLEN;
+ ctrl_outw(frqcr, FRQCR);
+
+ ctrl_outb(stbcr, STBCR);
+
+ clear_bl_bit();
+}
+
static int hp6x0_pm_enter(suspend_state_t state)
{
u8 stbcr, stbcr2;
diff --git a/arch/sh/boards/mach-microdev/Makefile b/arch/sh/boards/mach-microdev/Makefile
index 1387dd6c85eb..4e3588e8806b 100644
--- a/arch/sh/boards/mach-microdev/Makefile
+++ b/arch/sh/boards/mach-microdev/Makefile
@@ -2,7 +2,4 @@
# Makefile for the SuperH MicroDev specific parts of the kernel
#
-obj-y := setup.o irq.o io.o
-
-obj-$(CONFIG_HEARTBEAT) += led.o
-
+obj-y := setup.o irq.o io.o fdc37c93xapm.o
diff --git a/arch/sh/boards/mach-microdev/fdc37c93xapm.c b/arch/sh/boards/mach-microdev/fdc37c93xapm.c
new file mode 100644
index 000000000000..458a7cf5fb46
--- /dev/null
+++ b/arch/sh/boards/mach-microdev/fdc37c93xapm.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * Setup for the SMSC FDC37C93xAPM
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ * Copyright (C) 2003, 2004 SuperH, Inc.
+ * Copyright (C) 2004, 2005 Paul Mundt
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ */
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/microdev.h>
+
+#define SMSC_CONFIG_PORT_ADDR (0x3F0)
+#define SMSC_INDEX_PORT_ADDR SMSC_CONFIG_PORT_ADDR
+#define SMSC_DATA_PORT_ADDR (SMSC_INDEX_PORT_ADDR + 1)
+
+#define SMSC_ENTER_CONFIG_KEY 0x55
+#define SMSC_EXIT_CONFIG_KEY 0xaa
+
+#define SMCS_LOGICAL_DEV_INDEX 0x07 /* Logical Device Number */
+#define SMSC_DEVICE_ID_INDEX 0x20 /* Device ID */
+#define SMSC_DEVICE_REV_INDEX 0x21 /* Device Revision */
+#define SMSC_ACTIVATE_INDEX 0x30 /* Activate */
+#define SMSC_PRIMARY_BASE_INDEX 0x60 /* Primary Base Address */
+#define SMSC_SECONDARY_BASE_INDEX 0x62 /* Secondary Base Address */
+#define SMSC_PRIMARY_INT_INDEX 0x70 /* Primary Interrupt Select */
+#define SMSC_SECONDARY_INT_INDEX 0x72 /* Secondary Interrupt Select */
+#define SMSC_HDCS0_INDEX 0xf0 /* HDCS0 Address Decoder */
+#define SMSC_HDCS1_INDEX 0xf1 /* HDCS1 Address Decoder */
+
+#define SMSC_IDE1_DEVICE 1 /* IDE #1 logical device */
+#define SMSC_IDE2_DEVICE 2 /* IDE #2 logical device */
+#define SMSC_PARALLEL_DEVICE 3 /* Parallel Port logical device */
+#define SMSC_SERIAL1_DEVICE 4 /* Serial #1 logical device */
+#define SMSC_SERIAL2_DEVICE 5 /* Serial #2 logical device */
+#define SMSC_KEYBOARD_DEVICE 7 /* Keyboard logical device */
+#define SMSC_CONFIG_REGISTERS 8 /* Configuration Registers (Aux I/O) */
+
+#define SMSC_READ_INDEXED(index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ inb(SMSC_DATA_PORT_ADDR); })
+#define SMSC_WRITE_INDEXED(val, index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ outb((val), SMSC_DATA_PORT_ADDR); })
+
+#define IDE1_PRIMARY_BASE 0x01f0 /* Task File Registe base for IDE #1 */
+#define IDE1_SECONDARY_BASE 0x03f6 /* Miscellaneous AT registers for IDE #1 */
+#define IDE2_PRIMARY_BASE 0x0170 /* Task File Registe base for IDE #2 */
+#define IDE2_SECONDARY_BASE 0x0376 /* Miscellaneous AT registers for IDE #2 */
+
+#define SERIAL1_PRIMARY_BASE 0x03f8
+#define SERIAL2_PRIMARY_BASE 0x02f8
+
+#define MSB(x) ( (x) >> 8 )
+#define LSB(x) ( (x) & 0xff )
+
+ /* General-Purpose base address on CPU-board FPGA */
+#define MICRODEV_FPGA_GP_BASE 0xa6100000ul
+
+static int __init smsc_superio_setup(void)
+{
+
+ unsigned char devid, devrev;
+
+ /* Initially the chip is in run state */
+ /* Put it into configuration state */
+ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ /* Read device ID info */
+ devid = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
+ devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
+
+ if ((devid == 0x30) && (devrev == 0x01))
+ printk("SMSC FDC37C93xAPM SuperIO device detected\n");
+ else
+ return -ENODEV;
+
+ /* Select the keyboard device */
+ SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ /* enable it */
+ SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+ /* enable the interrupts */
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_KEYBOARD, SMSC_PRIMARY_INT_INDEX);
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_MOUSE, SMSC_SECONDARY_INT_INDEX);
+
+ /* Select the Serial #1 device */
+ SMSC_WRITE_INDEXED(SMSC_SERIAL1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ /* enable it */
+ SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+ /* program with port addresses */
+ SMSC_WRITE_INDEXED(MSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+ SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+ /* enable the interrupts */
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL1, SMSC_PRIMARY_INT_INDEX);
+
+ /* Select the Serial #2 device */
+ SMSC_WRITE_INDEXED(SMSC_SERIAL2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ /* enable it */
+ SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+ /* program with port addresses */
+ SMSC_WRITE_INDEXED(MSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+ SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+ /* enable the interrupts */
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL2, SMSC_PRIMARY_INT_INDEX);
+
+ /* Select the IDE#1 device */
+ SMSC_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ /* enable it */
+ SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+ /* program with port addresses */
+ SMSC_WRITE_INDEXED(MSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+ SMSC_WRITE_INDEXED(MSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+ SMSC_WRITE_INDEXED(0x0c, SMSC_HDCS0_INDEX);
+ SMSC_WRITE_INDEXED(0x00, SMSC_HDCS1_INDEX);
+ /* select the interrupt */
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE1, SMSC_PRIMARY_INT_INDEX);
+
+ /* Select the IDE#2 device */
+ SMSC_WRITE_INDEXED(SMSC_IDE2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+ /* enable it */
+ SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+ /* program with port addresses */
+ SMSC_WRITE_INDEXED(MSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+ SMSC_WRITE_INDEXED(MSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+ SMSC_WRITE_INDEXED(LSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+ /* select the interrupt */
+ SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE2, SMSC_PRIMARY_INT_INDEX);
+
+ /* Select the configuration registers */
+ SMSC_WRITE_INDEXED(SMSC_CONFIG_REGISTERS, SMCS_LOGICAL_DEV_INDEX);
+ /* enable the appropriate GPIO pins for IDE functionality:
+ * bit[0] In/Out 1==input; 0==output
+ * bit[1] Polarity 1==invert; 0==no invert
+ * bit[2] Int Enb #1 1==Enable Combined IRQ #1; 0==disable
+ * bit[3:4] Function Select 00==original; 01==Alternate Function #1
+ */
+ SMSC_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
+ SMSC_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
+ SMSC_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
+ SMSC_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
+ SMSC_WRITE_INDEXED(0x08, 0xe8); /* GP20 = nIDE2_OE */
+
+ /* Exit the configuration state */
+ outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ return 0;
+}
+device_initcall(smsc_superio_setup);
diff --git a/arch/sh/boards/mach-microdev/irq.c b/arch/sh/boards/mach-microdev/irq.c
index 702753cbd28f..b551963579c1 100644
--- a/arch/sh/boards/mach-microdev/irq.c
+++ b/arch/sh/boards/mach-microdev/irq.c
@@ -67,27 +67,13 @@ static const struct {
static void enable_microdev_irq(unsigned int irq);
static void disable_microdev_irq(unsigned int irq);
-
- /* shutdown is same as "disable" */
-#define shutdown_microdev_irq disable_microdev_irq
-
static void mask_and_ack_microdev(unsigned int);
-static void end_microdev_irq(unsigned int irq);
-
-static unsigned int startup_microdev_irq(unsigned int irq)
-{
- enable_microdev_irq(irq);
- return 0; /* never anything pending */
-}
-static struct hw_interrupt_type microdev_irq_type = {
- .typename = "MicroDev-IRQ",
- .startup = startup_microdev_irq,
- .shutdown = shutdown_microdev_irq,
- .enable = enable_microdev_irq,
- .disable = disable_microdev_irq,
+static struct irq_chip microdev_irq_type = {
+ .name = "MicroDev-IRQ",
+ .unmask = enable_microdev_irq,
+ .mask = disable_microdev_irq,
.ack = mask_and_ack_microdev,
- .end = end_microdev_irq
};
static void disable_microdev_irq(unsigned int irq)
@@ -130,11 +116,11 @@ static void enable_microdev_irq(unsigned int irq)
ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTENB_REG);
}
- /* This functions sets the desired irq handler to be a MicroDev type */
+/* This function sets the desired irq handler to be a MicroDev type */
static void __init make_microdev_irq(unsigned int irq)
{
disable_irq_nosync(irq);
- irq_desc[irq].chip = &microdev_irq_type;
+ set_irq_chip_and_handler(irq, &microdev_irq_type, handle_level_irq);
disable_microdev_irq(irq);
}
@@ -143,17 +129,11 @@ static void mask_and_ack_microdev(unsigned int irq)
disable_microdev_irq(irq);
}
-static void end_microdev_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_microdev_irq(irq);
-}
-
extern void __init init_microdev_irq(void)
{
int i;
- /* disable interrupts on the FPGA INTC register */
+ /* disable interrupts on the FPGA INTC register */
ctrl_outl(~0ul, MICRODEV_FPGA_INTDSB_REG);
for (i = 0; i < NUM_EXTERNAL_IRQS; i++)
@@ -179,5 +159,3 @@ extern void microdev_print_fpga_intc_status(void)
printk("FPGA_INTPRI[3..0] = %08x:%08x:%08x:%08x\n", *intprid, *intpric, *intprib, *intpria);
printk("-------------------------------------------------------------------------------\n");
}
-
-
diff --git a/arch/sh/boards/mach-microdev/led.c b/arch/sh/boards/mach-microdev/led.c
deleted file mode 100644
index 36e54b47a752..000000000000
--- a/arch/sh/boards/mach-microdev/led.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * linux/arch/sh/boards/superh/microdev/led.c
- *
- * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
- * Copyright (C) 2003 Richard Curnow (Richard.Curnow@superh.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- */
-
-#include <asm/io.h>
-
-#define LED_REGISTER 0xa6104d20
-
-static void mach_led_d9(int value)
-{
- unsigned long reg;
- reg = ctrl_inl(LED_REGISTER);
- reg &= ~1;
- reg |= (value & 1);
- ctrl_outl(reg, LED_REGISTER);
- return;
-}
-
-static void mach_led_d10(int value)
-{
- unsigned long reg;
- reg = ctrl_inl(LED_REGISTER);
- reg &= ~2;
- reg |= ((value & 1) << 1);
- ctrl_outl(reg, LED_REGISTER);
- return;
-}
-
-
-#ifdef CONFIG_HEARTBEAT
-#include <linux/sched.h>
-
-static unsigned char banner_table[] = {
- 0x11, 0x01, 0x11, 0x01, 0x11, 0x03,
- 0x11, 0x01, 0x11, 0x01, 0x13, 0x03,
- 0x11, 0x01, 0x13, 0x01, 0x13, 0x01, 0x11, 0x03,
- 0x11, 0x03,
- 0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
- 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x07,
- 0x13, 0x01, 0x13, 0x03,
- 0x11, 0x01, 0x11, 0x03,
- 0x13, 0x01, 0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
- 0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
- 0x13, 0x01, 0x13, 0x01, 0x13, 0x03,
- 0x13, 0x01, 0x11, 0x01, 0x11, 0x03,
- 0x11, 0x03,
- 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x13, 0x07,
- 0xff
-};
-
-static void banner(void)
-{
- static int pos = 0;
- static int count = 0;
-
- if (count) {
- count--;
- } else {
- int val = banner_table[pos];
- if (val == 0xff) {
- pos = 0;
- val = banner_table[pos];
- }
- pos++;
- mach_led_d10((val >> 4) & 1);
- count = 10 * (val & 0xf);
- }
-}
-
-/* From heartbeat_harp in the stboards directory */
-/* acts like an actual heart beat -- ie thump-thump-pause... */
-void microdev_heartbeat(void)
-{
- static unsigned cnt = 0, period = 0, dist = 0;
-
- if (cnt == 0 || cnt == dist)
- mach_led_d9(1);
- else if (cnt == 7 || cnt == dist+7)
- mach_led_d9(0);
-
- if (++cnt > period) {
- cnt = 0;
- /* The hyperbolic function below modifies the heartbeat period
- * length in dependency of the current (5min) load. It goes
- * through the points f(0)=126, f(1)=86, f(5)=51,
- * f(inf)->30. */
- period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
- dist = period / 4;
- }
-
- banner();
-}
-
-#endif
diff --git a/arch/sh/boards/mach-microdev/setup.c b/arch/sh/boards/mach-microdev/setup.c
index a9202fe3cb59..d1df2a4fb9b8 100644
--- a/arch/sh/boards/mach-microdev/setup.c
+++ b/arch/sh/boards/mach-microdev/setup.c
@@ -17,70 +17,12 @@
#include <mach/microdev.h>
#include <asm/io.h>
#include <asm/machvec.h>
-
-extern void microdev_heartbeat(void);
-
-
-/****************************************************************************/
-
-
- /*
- * Setup for the SMSC FDC37C93xAPM
- */
-#define SMSC_CONFIG_PORT_ADDR (0x3F0)
-#define SMSC_INDEX_PORT_ADDR SMSC_CONFIG_PORT_ADDR
-#define SMSC_DATA_PORT_ADDR (SMSC_INDEX_PORT_ADDR + 1)
-
-#define SMSC_ENTER_CONFIG_KEY 0x55
-#define SMSC_EXIT_CONFIG_KEY 0xaa
-
-#define SMCS_LOGICAL_DEV_INDEX 0x07 /* Logical Device Number */
-#define SMSC_DEVICE_ID_INDEX 0x20 /* Device ID */
-#define SMSC_DEVICE_REV_INDEX 0x21 /* Device Revision */
-#define SMSC_ACTIVATE_INDEX 0x30 /* Activate */
-#define SMSC_PRIMARY_BASE_INDEX 0x60 /* Primary Base Address */
-#define SMSC_SECONDARY_BASE_INDEX 0x62 /* Secondary Base Address */
-#define SMSC_PRIMARY_INT_INDEX 0x70 /* Primary Interrupt Select */
-#define SMSC_SECONDARY_INT_INDEX 0x72 /* Secondary Interrupt Select */
-#define SMSC_HDCS0_INDEX 0xf0 /* HDCS0 Address Decoder */
-#define SMSC_HDCS1_INDEX 0xf1 /* HDCS1 Address Decoder */
-
-#define SMSC_IDE1_DEVICE 1 /* IDE #1 logical device */
-#define SMSC_IDE2_DEVICE 2 /* IDE #2 logical device */
-#define SMSC_PARALLEL_DEVICE 3 /* Parallel Port logical device */
-#define SMSC_SERIAL1_DEVICE 4 /* Serial #1 logical device */
-#define SMSC_SERIAL2_DEVICE 5 /* Serial #2 logical device */
-#define SMSC_KEYBOARD_DEVICE 7 /* Keyboard logical device */
-#define SMSC_CONFIG_REGISTERS 8 /* Configuration Registers (Aux I/O) */
-
-#define SMSC_READ_INDEXED(index) ({ \
- outb((index), SMSC_INDEX_PORT_ADDR); \
- inb(SMSC_DATA_PORT_ADDR); })
-#define SMSC_WRITE_INDEXED(val, index) ({ \
- outb((index), SMSC_INDEX_PORT_ADDR); \
- outb((val), SMSC_DATA_PORT_ADDR); })
-
-#define IDE1_PRIMARY_BASE 0x01f0 /* Task File Registe base for IDE #1 */
-#define IDE1_SECONDARY_BASE 0x03f6 /* Miscellaneous AT registers for IDE #1 */
-#define IDE2_PRIMARY_BASE 0x0170 /* Task File Registe base for IDE #2 */
-#define IDE2_SECONDARY_BASE 0x0376 /* Miscellaneous AT registers for IDE #2 */
-
-#define SERIAL1_PRIMARY_BASE 0x03f8
-#define SERIAL2_PRIMARY_BASE 0x02f8
-
-#define MSB(x) ( (x) >> 8 )
-#define LSB(x) ( (x) & 0xff )
-
- /* General-Purpose base address on CPU-board FPGA */
-#define MICRODEV_FPGA_GP_BASE 0xa6100000ul
-
- /* assume a Keyboard Controller is present */
-int microdev_kbd_controller_present = 1;
+#include <asm/sizes.h>
static struct resource smc91x_resources[] = {
[0] = {
.start = 0x300,
- .end = 0x300 + 0x0001000 - 1,
+ .end = 0x300 + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
@@ -97,7 +39,6 @@ static struct platform_device smc91x_device = {
.resource = smc91x_resources,
};
-#ifdef CONFIG_FB_S1D13XXX
static struct s1d13xxxfb_regval s1d13806_initregs[] = {
{ S1DREG_MISC, 0x00 },
{ S1DREG_COM_DISP_MODE, 0x00 },
@@ -216,12 +157,12 @@ static struct s1d13xxxfb_pdata s1d13806_platform_data = {
static struct resource s1d13806_resources[] = {
[0] = {
.start = 0x07200000,
- .end = 0x07200000 + 0x00200000 - 1,
+ .end = 0x07200000 + SZ_2M - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0x07000000,
- .end = 0x07000000 + 0x00200000 - 1,
+ .end = 0x07000000 + SZ_2M - 1,
.flags = IORESOURCE_MEM,
},
};
@@ -236,145 +177,24 @@ static struct platform_device s1d13806_device = {
.platform_data = &s1d13806_platform_data,
},
};
-#endif
static struct platform_device *microdev_devices[] __initdata = {
&smc91x_device,
-#ifdef CONFIG_FB_S1D13XXX
&s1d13806_device,
-#endif
};
static int __init microdev_devices_setup(void)
{
return platform_add_devices(microdev_devices, ARRAY_SIZE(microdev_devices));
}
-
-/*
- * Setup for the SMSC FDC37C93xAPM
- */
-static int __init smsc_superio_setup(void)
-{
-
- unsigned char devid, devrev;
-
- /* Initially the chip is in run state */
- /* Put it into configuration state */
- outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
-
- /* Read device ID info */
- devid = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
- devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
- if ( (devid==0x30) && (devrev==0x01) )
- {
- printk("SMSC FDC37C93xAPM SuperIO device detected\n");
- }
- else
- { /* not the device identity we expected */
- printk("Not detected a SMSC FDC37C93xAPM SuperIO device (devid=0x%02x, rev=0x%02x)\n",
- devid, devrev);
- /* inform the keyboard driver that we have no keyboard controller */
- microdev_kbd_controller_present = 0;
- /* little point in doing anything else in this functon */
- return 0;
- }
-
- /* Select the keyboard device */
- SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
- /* enable it */
- SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
- /* enable the interrupts */
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_KEYBOARD, SMSC_PRIMARY_INT_INDEX);
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_MOUSE, SMSC_SECONDARY_INT_INDEX);
-
- /* Select the Serial #1 device */
- SMSC_WRITE_INDEXED(SMSC_SERIAL1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
- /* enable it */
- SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
- /* program with port addresses */
- SMSC_WRITE_INDEXED(MSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
- SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
- /* enable the interrupts */
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL1, SMSC_PRIMARY_INT_INDEX);
-
- /* Select the Serial #2 device */
- SMSC_WRITE_INDEXED(SMSC_SERIAL2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
- /* enable it */
- SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
- /* program with port addresses */
- SMSC_WRITE_INDEXED(MSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
- SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
- /* enable the interrupts */
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL2, SMSC_PRIMARY_INT_INDEX);
-
- /* Select the IDE#1 device */
- SMSC_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
- /* enable it */
- SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
- /* program with port addresses */
- SMSC_WRITE_INDEXED(MSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
- SMSC_WRITE_INDEXED(MSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
- SMSC_WRITE_INDEXED(0x0c, SMSC_HDCS0_INDEX);
- SMSC_WRITE_INDEXED(0x00, SMSC_HDCS1_INDEX);
- /* select the interrupt */
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE1, SMSC_PRIMARY_INT_INDEX);
-
- /* Select the IDE#2 device */
- SMSC_WRITE_INDEXED(SMSC_IDE2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
- /* enable it */
- SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
- /* program with port addresses */
- SMSC_WRITE_INDEXED(MSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
- SMSC_WRITE_INDEXED(MSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
- SMSC_WRITE_INDEXED(LSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
- /* select the interrupt */
- SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE2, SMSC_PRIMARY_INT_INDEX);
-
- /* Select the configuration registers */
- SMSC_WRITE_INDEXED(SMSC_CONFIG_REGISTERS, SMCS_LOGICAL_DEV_INDEX);
- /* enable the appropriate GPIO pins for IDE functionality:
- * bit[0] In/Out 1==input; 0==output
- * bit[1] Polarity 1==invert; 0==no invert
- * bit[2] Int Enb #1 1==Enable Combined IRQ #1; 0==disable
- * bit[3:4] Function Select 00==original; 01==Alternate Function #1
- */
- SMSC_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
- SMSC_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
- SMSC_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
- SMSC_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
- SMSC_WRITE_INDEXED(0x08, 0xe8); /* GP20 = nIDE2_OE */
-
- /* Exit the configuration state */
- outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
-
- return 0;
-}
-
-static void __init microdev_setup(char **cmdline_p)
-{
- int * const fpgaRevisionRegister = (int*)(MICRODEV_FPGA_GP_BASE + 0x8ul);
- const int fpgaRevision = *fpgaRevisionRegister;
- int * const CacheControlRegister = (int*)CCR;
-
- device_initcall(microdev_devices_setup);
- device_initcall(smsc_superio_setup);
-
- printk("SuperH %s board (FPGA rev: 0x%0x, CCR: 0x%0x)\n",
- get_system_type(), fpgaRevision, *CacheControlRegister);
-}
+device_initcall(microdev_devices_setup);
/*
* The Machine Vector
*/
static struct sh_machine_vector mv_sh4202_microdev __initmv = {
.mv_name = "SH4-202 MicroDev",
- .mv_setup = microdev_setup,
- .mv_nr_irqs = 72, /* QQQ need to check this - use the MACRO */
+ .mv_nr_irqs = 72,
.mv_inb = microdev_inb,
.mv_inw = microdev_inw,
@@ -398,8 +218,4 @@ static struct sh_machine_vector mv_sh4202_microdev __initmv = {
.mv_outsl = microdev_outsl,
.mv_init_irq = init_microdev_irq,
-
-#ifdef CONFIG_HEARTBEAT
- .mv_heartbeat = microdev_heartbeat,
-#endif
};
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 975281980299..cc1408119c24 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -89,6 +89,7 @@ static struct resource sh_keysc_resources[] = {
static struct platform_device sh_keysc_device = {
.name = "sh_keysc",
+ .id = 0, /* "keysc0" clock */
.num_resources = ARRAY_SIZE(sh_keysc_resources),
.resource = sh_keysc_resources,
.dev = {
@@ -261,6 +262,8 @@ static struct sh_mobile_lcdc_info sh_mobile_lcdc_info = {
.sys_bus_cfg = {
.ldmt2r = 0x06000a09,
.ldmt3r = 0x180e3418,
+ /* set 1s delay to encourage fsync() */
+ .deferred_io_msec = 1000,
},
}
#endif
@@ -273,6 +276,10 @@ static struct resource migor_lcdc_resources[] = {
.end = 0xfe941fff,
.flags = IORESOURCE_MEM,
},
+ [1] = {
+ .start = 28,
+ .flags = IORESOURCE_IRQ,
+ },
};
static struct platform_device migor_lcdc_device = {
@@ -300,6 +307,7 @@ static void camera_power_on(void)
gpio_set_value(GPIO_PTT3, 0);
mdelay(10);
gpio_set_value(GPIO_PTT3, 1);
+ mdelay(10); /* wait to let chip come out of reset */
}
static void camera_power_off(void)
@@ -432,6 +440,7 @@ static struct resource migor_ceu_resources[] = {
static struct platform_device migor_ceu_device = {
.name = "sh_mobile_ceu",
+ .id = 0, /* "ceu0" clock */
.num_resources = ARRAY_SIZE(migor_ceu_resources),
.resource = migor_ceu_resources,
.dev = {
@@ -479,7 +488,6 @@ static int __init migor_devices_setup(void)
ctrl_outl(0x00110080, BSC_CS4WCR);
/* KEYSC */
- clk_always_enable("mstp214"); /* KEYSC */
gpio_request(GPIO_FN_KEYOUT0, NULL);
gpio_request(GPIO_FN_KEYOUT1, NULL);
gpio_request(GPIO_FN_KEYOUT2, NULL);
@@ -501,7 +509,6 @@ static int __init migor_devices_setup(void)
gpio_request(GPIO_FN_IRQ6, NULL);
/* LCD Panel */
- clk_always_enable("mstp200"); /* LCDC */
#ifdef CONFIG_SH_MIGOR_QVGA /* LCDC - QVGA - Enable SYS Interface signals */
gpio_request(GPIO_FN_LCDD17, NULL);
gpio_request(GPIO_FN_LCDD16, NULL);
@@ -554,7 +561,6 @@ static int __init migor_devices_setup(void)
#endif
/* CEU */
- clk_always_enable("mstp203"); /* CEU */
gpio_request(GPIO_FN_VIO_CLK2, NULL);
gpio_request(GPIO_FN_VIO_VD2, NULL);
gpio_request(GPIO_FN_VIO_HD2, NULL);
@@ -589,12 +595,3 @@ static int __init migor_devices_setup(void)
return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices));
}
__initcall(migor_devices_setup);
-
-static void __init migor_setup(char **cmdline_p)
-{
-}
-
-static struct sh_machine_vector mv_migor __initmv = {
- .mv_name = "Migo-R",
- .mv_setup = migor_setup,
-};
diff --git a/arch/sh/boards/mach-rsk/Kconfig b/arch/sh/boards/mach-rsk/Kconfig
new file mode 100644
index 000000000000..bff095dffc02
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/Kconfig
@@ -0,0 +1,18 @@
+if SH_RSK
+
+choice
+ prompt "RSK+ options"
+ default SH_RSK7203
+
+config SH_RSK7201
+ bool "RSK7201"
+ depends on CPU_SUBTYPE_SH7201
+
+config SH_RSK7203
+ bool "RSK7203"
+ select GENERIC_GPIO
+ depends on CPU_SUBTYPE_SH7203
+
+endchoice
+
+endif
diff --git a/arch/sh/boards/mach-rsk/Makefile b/arch/sh/boards/mach-rsk/Makefile
new file mode 100644
index 000000000000..498da75ce38b
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/Makefile
@@ -0,0 +1,2 @@
+obj-y := setup.o
+obj-$(CONFIG_SH_RSK7203) += devices-rsk7203.o
diff --git a/arch/sh/boards/mach-rsk/devices-rsk7203.c b/arch/sh/boards/mach-rsk/devices-rsk7203.c
new file mode 100644
index 000000000000..73f743b9be8d
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/devices-rsk7203.c
@@ -0,0 +1,103 @@
+/*
+ * Renesas Technology Europe RSK+ 7203 Support.
+ *
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * 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/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/map.h>
+#include <linux/smc911x.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+#include <cpu/sh7203.h>
+
+static struct smc911x_platdata smc911x_info = {
+ .flags = SMC911X_USE_16BIT,
+ .irq_flags = IRQF_TRIGGER_LOW,
+};
+
+static struct resource smc911x_resources[] = {
+ [0] = {
+ .start = 0x24000000,
+ .end = 0x24000000 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 64,
+ .end = 64,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc911x_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc911x_resources),
+ .resource = smc911x_resources,
+ .dev = {
+ .platform_data = &smc911x_info,
+ },
+};
+
+static struct gpio_led rsk7203_gpio_leds[] = {
+ {
+ .name = "green",
+ .gpio = GPIO_PE10,
+ .active_low = 1,
+ }, {
+ .name = "orange",
+ .default_trigger = "nand-disk",
+ .gpio = GPIO_PE12,
+ .active_low = 1,
+ }, {
+ .name = "red:timer",
+ .default_trigger = "timer",
+ .gpio = GPIO_PC14,
+ .active_low = 1,
+ }, {
+ .name = "red:heartbeat",
+ .default_trigger = "heartbeat",
+ .gpio = GPIO_PE11,
+ .active_low = 1,
+ },
+};
+
+static struct gpio_led_platform_data rsk7203_gpio_leds_info = {
+ .leds = rsk7203_gpio_leds,
+ .num_leds = ARRAY_SIZE(rsk7203_gpio_leds),
+};
+
+static struct platform_device led_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &rsk7203_gpio_leds_info,
+ },
+};
+
+static struct platform_device *rsk7203_devices[] __initdata = {
+ &smc911x_device,
+ &led_device,
+};
+
+static int __init rsk7203_devices_setup(void)
+{
+ /* Select pins for SCIF0 */
+ gpio_request(GPIO_FN_TXD0, NULL);
+ gpio_request(GPIO_FN_RXD0, NULL);
+
+ return platform_add_devices(rsk7203_devices,
+ ARRAY_SIZE(rsk7203_devices));
+}
+device_initcall(rsk7203_devices_setup);
diff --git a/arch/sh/boards/mach-rsk/setup.c b/arch/sh/boards/mach-rsk/setup.c
new file mode 100644
index 000000000000..af64d030a5c7
--- /dev/null
+++ b/arch/sh/boards/mach-rsk/setup.c
@@ -0,0 +1,106 @@
+/*
+ * Renesas Technology Europe RSK+ Support.
+ *
+ * Copyright (C) 2008 Paul Mundt
+ * Copyright (C) 2008 Peter Griffin <pgriffin@mpc-data.co.uk>
+ *
+ * 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/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/map.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+
+static const char *probes[] = { "cmdlinepart", NULL };
+
+static struct mtd_partition *parsed_partitions;
+
+static struct mtd_partition rsk_partitions[] = {
+ {
+ .name = "Bootloader",
+ .offset = 0x00000000,
+ .size = 0x00040000,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "Kernel",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = 0x001c0000,
+ }, {
+ .name = "Flash_FS",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct physmap_flash_data flash_data = {
+ .width = 2,
+};
+
+static struct resource flash_resource = {
+ .start = 0x20000000,
+ .end = 0x20400000,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = &flash_resource,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &flash_data,
+ },
+};
+
+static struct mtd_info *flash_mtd;
+
+static struct map_info rsk_flash_map = {
+ .name = "RSK+ Flash",
+ .size = 0x400000,
+ .bankwidth = 2,
+};
+
+static void __init set_mtd_partitions(void)
+{
+ int nr_parts = 0;
+
+ simple_map_init(&rsk_flash_map);
+ flash_mtd = do_map_probe("cfi_probe", &rsk_flash_map);
+ nr_parts = parse_mtd_partitions(flash_mtd, probes,
+ &parsed_partitions, 0);
+ /* If there is no partition table, used the hard coded table */
+ if (nr_parts <= 0) {
+ flash_data.parts = rsk_partitions;
+ flash_data.nr_parts = ARRAY_SIZE(rsk_partitions);
+ } else {
+ flash_data.nr_parts = nr_parts;
+ flash_data.parts = parsed_partitions;
+ }
+}
+
+static struct platform_device *rsk_devices[] __initdata = {
+ &flash_device,
+};
+
+static int __init rsk_devices_setup(void)
+{
+ set_mtd_partitions();
+ return platform_add_devices(rsk_devices,
+ ARRAY_SIZE(rsk_devices));
+}
+device_initcall(rsk_devices_setup);
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_rsk __initmv = {
+ .mv_name = "RSK+",
+};
diff --git a/arch/sh/boards/mach-se/7343/Makefile b/arch/sh/boards/mach-se/7343/Makefile
index 3024796c6203..4c3666a93790 100644
--- a/arch/sh/boards/mach-se/7343/Makefile
+++ b/arch/sh/boards/mach-se/7343/Makefile
@@ -2,4 +2,4 @@
# Makefile for the 7343 SolutionEngine specific parts of the kernel
#
-obj-y := setup.o io.o irq.o
+obj-y := setup.o irq.o
diff --git a/arch/sh/boards/mach-se/7343/io.c b/arch/sh/boards/mach-se/7343/io.c
deleted file mode 100644
index 8741abc1da7b..000000000000
--- a/arch/sh/boards/mach-se/7343/io.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * arch/sh/boards/se/7343/io.c
- *
- * I/O routine for SH-Mobile3AS 7343 SolutionEngine.
- *
- */
-#include <linux/kernel.h>
-#include <asm/io.h>
-#include <mach-se/mach/se7343.h>
-
-#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a)
-
-struct iop {
- unsigned long start, end;
- unsigned long base;
- struct iop *(*check) (struct iop * p, unsigned long port);
- unsigned char (*inb) (struct iop * p, unsigned long port);
- unsigned short (*inw) (struct iop * p, unsigned long port);
- void (*outb) (struct iop * p, unsigned char value, unsigned long port);
- void (*outw) (struct iop * p, unsigned short value, unsigned long port);
-};
-
-struct iop *
-simple_check(struct iop *p, unsigned long port)
-{
- static int count;
-
- if (count < 100)
- count++;
-
- port &= 0xFFFF;
-
- if ((p->start <= port) && (port <= p->end))
- return p;
- else
- badio(check, port);
-}
-
-struct iop *
-ide_check(struct iop *p, unsigned long port)
-{
- if (((0x1f0 <= port) && (port <= 0x1f7)) || (port == 0x3f7))
- return p;
- return NULL;
-}
-
-unsigned char
-simple_inb(struct iop *p, unsigned long port)
-{
- return *(unsigned char *) (p->base + port);
-}
-
-unsigned short
-simple_inw(struct iop *p, unsigned long port)
-{
- return *(unsigned short *) (p->base + port);
-}
-
-void
-simple_outb(struct iop *p, unsigned char value, unsigned long port)
-{
- *(unsigned char *) (p->base + port) = value;
-}
-
-void
-simple_outw(struct iop *p, unsigned short value, unsigned long port)
-{
- *(unsigned short *) (p->base + port) = value;
-}
-
-unsigned char
-pcc_inb(struct iop *p, unsigned long port)
-{
- unsigned long addr = p->base + port + 0x40000;
- unsigned long v;
-
- if (port & 1)
- addr += 0x00400000;
- v = *(volatile unsigned char *) addr;
- return v;
-}
-
-void
-pcc_outb(struct iop *p, unsigned char value, unsigned long port)
-{
- unsigned long addr = p->base + port + 0x40000;
-
- if (port & 1)
- addr += 0x00400000;
- *(volatile unsigned char *) addr = value;
-}
-
-unsigned char
-bad_inb(struct iop *p, unsigned long port)
-{
- badio(inb, port);
-}
-
-void
-bad_outb(struct iop *p, unsigned char value, unsigned long port)
-{
- badio(inw, port);
-}
-
-#ifdef CONFIG_SMC91X
-/* MSTLANEX01 LAN at 0xb400:0000 */
-static struct iop laniop = {
- .start = 0x00,
- .end = 0x0F,
- .base = 0x04000000,
- .check = simple_check,
- .inb = simple_inb,
- .inw = simple_inw,
- .outb = simple_outb,
- .outw = simple_outw,
-};
-#endif
-
-#ifdef CONFIG_NE2000
-/* NE2000 pc card NIC */
-static struct iop neiop = {
- .start = 0x280,
- .end = 0x29f,
- .base = 0xb0600000 + 0x80, /* soft 0x280 -> hard 0x300 */
- .check = simple_check,
- .inb = pcc_inb,
- .inw = simple_inw,
- .outb = pcc_outb,
- .outw = simple_outw,
-};
-#endif
-
-#ifdef CONFIG_IDE
-/* CF in CF slot */
-static struct iop cfiop = {
- .base = 0xb0600000,
- .check = ide_check,
- .inb = pcc_inb,
- .inw = simple_inw,
- .outb = pcc_outb,
- .outw = simple_outw,
-};
-#endif
-
-static __inline__ struct iop *
-port2iop(unsigned long port)
-{
- if (0) ;
-#if defined(CONFIG_SMC91X)
- else if (laniop.check(&laniop, port))
- return &laniop;
-#endif
-#if defined(CONFIG_NE2000)
- else if (neiop.check(&neiop, port))
- return &neiop;
-#endif
-#if defined(CONFIG_IDE)
- else if (cfiop.check(&cfiop, port))
- return &cfiop;
-#endif
- else
- return NULL;
-}
-
-static inline void
-delay(void)
-{
- ctrl_inw(0xac000000);
- ctrl_inw(0xac000000);
-}
-
-unsigned char
-sh7343se_inb(unsigned long port)
-{
- struct iop *p = port2iop(port);
- return (p->inb) (p, port);
-}
-
-unsigned char
-sh7343se_inb_p(unsigned long port)
-{
- unsigned char v = sh7343se_inb(port);
- delay();
- return v;
-}
-
-unsigned short
-sh7343se_inw(unsigned long port)
-{
- struct iop *p = port2iop(port);
- return (p->inw) (p, port);
-}
-
-unsigned int
-sh7343se_inl(unsigned long port)
-{
- badio(inl, port);
-}
-
-void
-sh7343se_outb(unsigned char value, unsigned long port)
-{
- struct iop *p = port2iop(port);
- (p->outb) (p, value, port);
-}
-
-void
-sh7343se_outb_p(unsigned char value, unsigned long port)
-{
- sh7343se_outb(value, port);
- delay();
-}
-
-void
-sh7343se_outw(unsigned short value, unsigned long port)
-{
- struct iop *p = port2iop(port);
- (p->outw) (p, value, port);
-}
-
-void
-sh7343se_outl(unsigned int value, unsigned long port)
-{
- badio(outl, port);
-}
-
-void
-sh7343se_insb(unsigned long port, void *addr, unsigned long count)
-{
- unsigned char *a = addr;
- struct iop *p = port2iop(port);
- while (count--)
- *a++ = (p->inb) (p, port);
-}
-
-void
-sh7343se_insw(unsigned long port, void *addr, unsigned long count)
-{
- unsigned short *a = addr;
- struct iop *p = port2iop(port);
- while (count--)
- *a++ = (p->inw) (p, port);
-}
-
-void
-sh7343se_insl(unsigned long port, void *addr, unsigned long count)
-{
- badio(insl, port);
-}
-
-void
-sh7343se_outsb(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned char *a = (unsigned char *) addr;
- struct iop *p = port2iop(port);
- while (count--)
- (p->outb) (p, *a++, port);
-}
-
-void
-sh7343se_outsw(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned short *a = (unsigned short *) addr;
- struct iop *p = port2iop(port);
- while (count--)
- (p->outw) (p, *a++, port);
-}
-
-void
-sh7343se_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- badio(outsw, port);
-}
diff --git a/arch/sh/boards/mach-se/7343/setup.c b/arch/sh/boards/mach-se/7343/setup.c
index 486f40bf9274..4de56f35f419 100644
--- a/arch/sh/boards/mach-se/7343/setup.c
+++ b/arch/sh/boards/mach-se/7343/setup.c
@@ -1,36 +1,16 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/usb/isp116x.h>
+#include <linux/delay.h>
#include <asm/machvec.h>
#include <mach-se/mach/se7343.h>
#include <asm/heartbeat.h>
#include <asm/irq.h>
#include <asm/io.h>
-static struct resource smc91x_resources[] = {
- [0] = {
- .start = 0x10000000,
- .end = 0x1000000F,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- /*
- * shared with other devices via externel
- * interrupt controller in FPGA...
- */
- .start = SMC_IRQ,
- .end = SMC_IRQ,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device smc91x_device = {
- .name = "smc91x",
- .id = 0,
- .num_resources = ARRAY_SIZE(smc91x_resources),
- .resource = smc91x_resources,
-};
-
static struct resource heartbeat_resources[] = {
[0] = {
.start = PA_LED,
@@ -94,10 +74,83 @@ static struct platform_device nor_flash_device = {
.resource = nor_flash_resources,
};
+#define ST16C2550C_FLAGS (UPF_BOOT_AUTOCONF | UPF_IOREMAP)
+
+static struct plat_serial8250_port serial_platform_data[] = {
+ [0] = {
+ .iotype = UPIO_MEM,
+ .mapbase = 0x16000000,
+ .regshift = 1,
+ .flags = ST16C2550C_FLAGS,
+ .irq = UARTA_IRQ,
+ .uartclk = 7372800,
+ },
+ [1] = {
+ .iotype = UPIO_MEM,
+ .mapbase = 0x17000000,
+ .regshift = 1,
+ .flags = ST16C2550C_FLAGS,
+ .irq = UARTB_IRQ,
+ .uartclk = 7372800,
+ },
+ { },
+};
+
+static struct platform_device uart_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = serial_platform_data,
+ },
+};
+
+static void isp116x_delay(struct device *dev, int delay)
+{
+ ndelay(delay);
+}
+
+static struct resource usb_resources[] = {
+ [0] = {
+ .start = 0x11800000,
+ .end = 0x11800001,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 0x11800002,
+ .end = 0x11800003,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = USB_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct isp116x_platform_data usb_platform_data = {
+ .sel15Kres = 1,
+ .oc_enable = 1,
+ .int_act_high = 0,
+ .int_edge_triggered = 0,
+ .remote_wakeup_enable = 0,
+ .delay = isp116x_delay,
+};
+
+static struct platform_device usb_device = {
+ .name = "isp116x-hcd",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(usb_resources),
+ .resource = usb_resources,
+ .dev = {
+ .platform_data = &usb_platform_data,
+ },
+
+};
+
static struct platform_device *sh7343se_platform_devices[] __initdata = {
- &smc91x_device,
&heartbeat_device,
&nor_flash_device,
+ &uart_device,
+ &usb_device,
};
static int __init sh7343se_devices_setup(void)
@@ -126,27 +179,6 @@ static void __init sh7343se_setup(char **cmdline_p)
static struct sh_machine_vector mv_7343se __initmv = {
.mv_name = "SolutionEngine 7343",
.mv_setup = sh7343se_setup,
- .mv_nr_irqs = 108,
- .mv_inb = sh7343se_inb,
- .mv_inw = sh7343se_inw,
- .mv_inl = sh7343se_inl,
- .mv_outb = sh7343se_outb,
- .mv_outw = sh7343se_outw,
- .mv_outl = sh7343se_outl,
-
- .mv_inb_p = sh7343se_inb_p,
- .mv_inw_p = sh7343se_inw,
- .mv_inl_p = sh7343se_inl,
- .mv_outb_p = sh7343se_outb_p,
- .mv_outw_p = sh7343se_outw,
- .mv_outl_p = sh7343se_outl,
-
- .mv_insb = sh7343se_insb,
- .mv_insw = sh7343se_insw,
- .mv_insl = sh7343se_insl,
- .mv_outsb = sh7343se_outsb,
- .mv_outsw = sh7343se_outsw,
- .mv_outsl = sh7343se_outsl,
-
+ .mv_nr_irqs = SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_NR,
.mv_init_irq = init_7343se_IRQ,
};
diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c
index 9123d9687bf7..527eb6b12610 100644
--- a/arch/sh/boards/mach-se/770x/setup.c
+++ b/arch/sh/boards/mach-se/770x/setup.c
@@ -8,8 +8,9 @@
*/
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <asm/machvec.h>
#include <mach-se/mach/se.h>
+#include <mach-se/mach/mrshpc.h>
+#include <asm/machvec.h>
#include <asm/io.h>
#include <asm/smc37c93x.h>
#include <asm/heartbeat.h>
@@ -175,6 +176,7 @@ static struct platform_device *se_devices[] __initdata = {
static int __init se_devices_setup(void)
{
+ mrshpc_setup_windows();
return platform_add_devices(se_devices, ARRAY_SIZE(se_devices));
}
device_initcall(se_devices_setup);
diff --git a/arch/sh/boards/mach-se/7721/setup.c b/arch/sh/boards/mach-se/7721/setup.c
index d3fc80ff4d83..55af4c36b43a 100644
--- a/arch/sh/boards/mach-se/7721/setup.c
+++ b/arch/sh/boards/mach-se/7721/setup.c
@@ -12,8 +12,9 @@
*/
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <asm/machvec.h>
#include <mach-se/mach/se7721.h>
+#include <mach-se/mach/mrshpc.h>
+#include <asm/machvec.h>
#include <asm/io.h>
#include <asm/heartbeat.h>
@@ -74,8 +75,8 @@ static struct platform_device *se7721_devices[] __initdata = {
static int __init se7721_devices_setup(void)
{
- return platform_add_devices(se7721_devices,
- ARRAY_SIZE(se7721_devices));
+ mrshpc_setup_windows();
+ return platform_add_devices(se7721_devices, ARRAY_SIZE(se7721_devices));
}
device_initcall(se7721_devices_setup);
diff --git a/arch/sh/boards/mach-se/7722/setup.c b/arch/sh/boards/mach-se/7722/setup.c
index fe6f96517e12..af84904ed86f 100644
--- a/arch/sh/boards/mach-se/7722/setup.c
+++ b/arch/sh/boards/mach-se/7722/setup.c
@@ -15,9 +15,10 @@
#include <linux/ata_platform.h>
#include <linux/input.h>
#include <linux/smc91x.h>
+#include <mach-se/mach/se7722.h>
+#include <mach-se/mach/mrshpc.h>
#include <asm/machvec.h>
#include <asm/clock.h>
-#include <mach-se/mach/se7722.h>
#include <asm/io.h>
#include <asm/heartbeat.h>
#include <asm/sh_keysc.h>
@@ -130,6 +131,7 @@ static struct resource sh_keysc_resources[] = {
static struct platform_device sh_keysc_device = {
.name = "sh_keysc",
+ .id = 0, /* "keysc0" clock */
.num_resources = ARRAY_SIZE(sh_keysc_resources),
.resource = sh_keysc_resources,
.dev = {
@@ -146,10 +148,8 @@ static struct platform_device *se7722_devices[] __initdata = {
static int __init se7722_devices_setup(void)
{
- clk_always_enable("mstp214"); /* KEYSC */
-
- return platform_add_devices(se7722_devices,
- ARRAY_SIZE(se7722_devices));
+ mrshpc_setup_windows();
+ return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices));
}
device_initcall(se7722_devices_setup);
diff --git a/arch/sh/boards/mach-sh03/setup.c b/arch/sh/boards/mach-sh03/setup.c
index 5771219be3fd..74cfb4b8b03d 100644
--- a/arch/sh/boards/mach-sh03/setup.c
+++ b/arch/sh/boards/mach-sh03/setup.c
@@ -9,6 +9,7 @@
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/ata_platform.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include <mach-sh03/mach/io.h>
@@ -20,19 +21,6 @@ static void __init init_sh03_IRQ(void)
plat_irq_setup_pins(IRQ_MODE_IRQ);
}
-extern void *cf_io_base;
-
-static void __iomem *sh03_ioport_map(unsigned long port, unsigned int size)
-{
- if (PXSEG(port))
- return (void __iomem *)port;
- /* CompactFlash (IDE) */
- if (((port >= 0x1f0) && (port <= 0x1f7)) || (port == 0x3f6))
- return (void __iomem *)((unsigned long)cf_io_base + port);
-
- return (void __iomem *)(port + PCI_IO_BASE);
-}
-
/* arch/sh/boards/sh03/rtc.c */
void sh03_time_init(void);
@@ -41,6 +29,30 @@ static void __init sh03_setup(char **cmdline_p)
board_time_init = sh03_time_init;
}
+static struct resource cf_ide_resources[] = {
+ [0] = {
+ .start = 0x1f0,
+ .end = 0x1f0 + 8,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ .start = 0x1f0 + 0x206,
+ .end = 0x1f0 +8 + 0x206 + 8,
+ .flags = IORESOURCE_IO,
+ },
+ [2] = {
+ .start = IRL2_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device cf_ide_device = {
+ .name = "pata_platform",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cf_ide_resources),
+ .resource = cf_ide_resources,
+};
+
static struct resource heartbeat_resources[] = {
[0] = {
.start = 0xa0800000,
@@ -58,10 +70,30 @@ static struct platform_device heartbeat_device = {
static struct platform_device *sh03_devices[] __initdata = {
&heartbeat_device,
+ &cf_ide_device,
};
static int __init sh03_devices_setup(void)
{
+ pgprot_t prot;
+ unsigned long paddrbase;
+ void *cf_ide_base;
+
+ /* open I/O area window */
+ paddrbase = virt_to_phys((void *)PA_AREA5_IO);
+ prot = PAGE_KERNEL_PCC(1, _PAGE_PCC_IO16);
+ cf_ide_base = p3_ioremap(paddrbase, PAGE_SIZE, prot.pgprot);
+ if (!cf_ide_base) {
+ printk("allocate_cf_area : can't open CF I/O window!\n");
+ return -ENOMEM;
+ }
+
+ /* IDE cmd address : 0x1f0-0x1f7 and 0x3f6 */
+ cf_ide_resources[0].start += (unsigned long)cf_ide_base;
+ cf_ide_resources[0].end += (unsigned long)cf_ide_base;
+ cf_ide_resources[1].start += (unsigned long)cf_ide_base;
+ cf_ide_resources[1].end += (unsigned long)cf_ide_base;
+
return platform_add_devices(sh03_devices, ARRAY_SIZE(sh03_devices));
}
__initcall(sh03_devices_setup);
@@ -70,6 +102,5 @@ static struct sh_machine_vector mv_sh03 __initmv = {
.mv_name = "Interface (CTP/PCI-SH03)",
.mv_setup = sh03_setup,
.mv_nr_irqs = 48,
- .mv_ioport_map = sh03_ioport_map,
.mv_init_irq = init_sh03_IRQ,
};
diff --git a/arch/sh/boards/mach-systemh/irq.c b/arch/sh/boards/mach-systemh/irq.c
index 538406872a89..986a0e71d220 100644
--- a/arch/sh/boards/mach-systemh/irq.c
+++ b/arch/sh/boards/mach-systemh/irq.c
@@ -12,8 +12,8 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <mach/systemh7751.h>
#include <asm/smc37c93x.h>
@@ -24,35 +24,17 @@ static unsigned long *systemh_irq_mask_register = (unsigned long *)0xB3F10004;
static unsigned long *systemh_irq_request_register = (unsigned long *)0xB3F10000;
/* forward declaration */
-static unsigned int startup_systemh_irq(unsigned int irq);
-static void shutdown_systemh_irq(unsigned int irq);
static void enable_systemh_irq(unsigned int irq);
static void disable_systemh_irq(unsigned int irq);
static void mask_and_ack_systemh(unsigned int);
-static void end_systemh_irq(unsigned int irq);
-/* hw_interrupt_type */
-static struct hw_interrupt_type systemh_irq_type = {
- .typename = " SystemH Register",
- .startup = startup_systemh_irq,
- .shutdown = shutdown_systemh_irq,
- .enable = enable_systemh_irq,
- .disable = disable_systemh_irq,
+static struct irq_chip systemh_irq_type = {
+ .name = " SystemH Register",
+ .unmask = enable_systemh_irq,
+ .mask = disable_systemh_irq,
.ack = mask_and_ack_systemh,
- .end = end_systemh_irq
};
-static unsigned int startup_systemh_irq(unsigned int irq)
-{
- enable_systemh_irq(irq);
- return 0; /* never anything pending */
-}
-
-static void shutdown_systemh_irq(unsigned int irq)
-{
- disable_systemh_irq(irq);
-}
-
static void disable_systemh_irq(unsigned int irq)
{
if (systemh_irq_mask_register) {
@@ -86,16 +68,9 @@ static void mask_and_ack_systemh(unsigned int irq)
disable_systemh_irq(irq);
}
-static void end_systemh_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_systemh_irq(irq);
-}
-
void make_systemh_irq(unsigned int irq)
{
disable_irq_nosync(irq);
- irq_desc[irq].chip = &systemh_irq_type;
+ set_irq_chip_and_handler(irq, &systemh_irq_type, handle_level_irq);
disable_systemh_irq(irq);
}
-
diff --git a/arch/sh/cchips/hd6446x/hd64461.c b/arch/sh/cchips/hd6446x/hd64461.c
index f1a4a0763c59..27ceeb948bb1 100644
--- a/arch/sh/cchips/hd6446x/hd64461.c
+++ b/arch/sh/cchips/hd6446x/hd64461.c
@@ -10,99 +10,49 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/irq.h>
#include <asm/hd64461.h>
/* This belongs in cpu specific */
#define INTC_ICR1 0xA4140010UL
-static void disable_hd64461_irq(unsigned int irq)
+static void hd64461_mask_irq(unsigned int irq)
{
unsigned short nimr;
unsigned short mask = 1 << (irq - HD64461_IRQBASE);
- nimr = inw(HD64461_NIMR);
+ nimr = __raw_readw(HD64461_NIMR);
nimr |= mask;
- outw(nimr, HD64461_NIMR);
+ __raw_writew(nimr, HD64461_NIMR);
}
-static void enable_hd64461_irq(unsigned int irq)
+static void hd64461_unmask_irq(unsigned int irq)
{
unsigned short nimr;
unsigned short mask = 1 << (irq - HD64461_IRQBASE);
- nimr = inw(HD64461_NIMR);
+ nimr = __raw_readw(HD64461_NIMR);
nimr &= ~mask;
- outw(nimr, HD64461_NIMR);
+ __raw_writew(nimr, HD64461_NIMR);
}
-static void mask_and_ack_hd64461(unsigned int irq)
+static void hd64461_mask_and_ack_irq(unsigned int irq)
{
- disable_hd64461_irq(irq);
+ hd64461_mask_irq(irq);
#ifdef CONFIG_HD64461_ENABLER
if (irq == HD64461_IRQBASE + 13)
- outb(0x00, HD64461_PCC1CSCR);
+ __raw_writeb(0x00, HD64461_PCC1CSCR);
#endif
}
-static void end_hd64461_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- enable_hd64461_irq(irq);
-}
-
-static unsigned int startup_hd64461_irq(unsigned int irq)
-{
- enable_hd64461_irq(irq);
- return 0;
-}
-
-static void shutdown_hd64461_irq(unsigned int irq)
-{
- disable_hd64461_irq(irq);
-}
-
-static struct hw_interrupt_type hd64461_irq_type = {
- .typename = "HD64461-IRQ",
- .startup = startup_hd64461_irq,
- .shutdown = shutdown_hd64461_irq,
- .enable = enable_hd64461_irq,
- .disable = disable_hd64461_irq,
- .ack = mask_and_ack_hd64461,
- .end = end_hd64461_irq,
+static struct irq_chip hd64461_irq_chip = {
+ .name = "HD64461-IRQ",
+ .mask = hd64461_mask_irq,
+ .mask_ack = hd64461_mask_and_ack_irq,
+ .unmask = hd64461_unmask_irq,
};
-static irqreturn_t hd64461_interrupt(int irq, void *dev_id)
-{
- printk(KERN_INFO
- "HD64461: spurious interrupt, nirr: 0x%x nimr: 0x%x\n",
- inw(HD64461_NIRR), inw(HD64461_NIMR));
-
- return IRQ_NONE;
-}
-
-static struct {
- int (*func) (int, void *);
- void *dev;
-} hd64461_demux[HD64461_IRQ_NUM];
-
-void hd64461_register_irq_demux(int irq,
- int (*demux) (int irq, void *dev), void *dev)
-{
- hd64461_demux[irq - HD64461_IRQBASE].func = demux;
- hd64461_demux[irq - HD64461_IRQBASE].dev = dev;
-}
-
-EXPORT_SYMBOL(hd64461_register_irq_demux);
-
-void hd64461_unregister_irq_demux(int irq)
-{
- hd64461_demux[irq - HD64461_IRQBASE].func = 0;
-}
-
-EXPORT_SYMBOL(hd64461_unregister_irq_demux);
-
int hd64461_irq_demux(int irq)
{
if (irq == CONFIG_HD64461_IRQ) {
@@ -115,25 +65,11 @@ int hd64461_irq_demux(int irq)
for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
if (nirr & bit)
break;
- if (i == 16)
- irq = CONFIG_HD64461_IRQ;
- else {
- irq = HD64461_IRQBASE + i;
- if (hd64461_demux[i].func != 0) {
- irq = hd64461_demux[i].func(irq, hd64461_demux[i].dev);
- }
- }
+ irq = HD64461_IRQBASE + i;
}
return irq;
}
-static struct irqaction irq0 = {
- .handler = hd64461_interrupt,
- .flags = IRQF_DISABLED,
- .mask = CPU_MASK_NONE,
- .name = "HD64461",
-};
-
int __init setup_hd64461(void)
{
int i;
@@ -146,22 +82,21 @@ int __init setup_hd64461(void)
CONFIG_HD64461_IOBASE, CONFIG_HD64461_IRQ, HD64461_IRQBASE,
HD64461_IRQBASE + 15);
-#if defined(CONFIG_CPU_SUBTYPE_SH7709) /* Should be at processor specific part.. */
- outw(0x2240, INTC_ICR1);
+/* Should be at processor specific part.. */
+#if defined(CONFIG_CPU_SUBTYPE_SH7709)
+ __raw_writew(0x2240, INTC_ICR1);
#endif
- outw(0xffff, HD64461_NIMR);
+ __raw_writew(0xffff, HD64461_NIMR);
/* IRQ 80 -> 95 belongs to HD64461 */
- for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) {
- irq_desc[i].chip = &hd64461_irq_type;
- }
-
- setup_irq(CONFIG_HD64461_IRQ, &irq0);
+ for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++)
+ set_irq_chip_and_handler(i, &hd64461_irq_chip,
+ handle_level_irq);
#ifdef CONFIG_HD64461_ENABLER
printk(KERN_INFO "HD64461: enabling PCMCIA devices\n");
- outb(0x4c, HD64461_PCC1CSCIER);
- outb(0x00, HD64461_PCC1CSCR);
+ __raw_writeb(0x4c, HD64461_PCC1CSCIER);
+ __raw_writeb(0x00, HD64461_PCC1CSCR);
#endif
return 0;
diff --git a/arch/sh/configs/edosk7705_defconfig b/arch/sh/configs/edosk7705_defconfig
new file mode 100644
index 000000000000..8f4329fbbd39
--- /dev/null
+++ b/arch/sh/configs/edosk7705_defconfig
@@ -0,0 +1,438 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc6
+# Wed Dec 17 13:53:02 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+# CONFIG_EXPERIMENTAL is not set
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_EMBEDDED=y
+# CONFIG_UID16 is not set
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_KALLSYMS is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PRINTK is not set
+# CONFIG_BUG is not set
+# CONFIG_ELF_CORE is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+CONFIG_SHMEM=y
+# CONFIG_AIO is not set
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=1
+# CONFIG_MODULES is not set
+# CONFIG_BLOCK is not set
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH3=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+CONFIG_CPU_SUBTYPE_SH7705=y
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_29BIT=y
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_MIGRATION=y
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+CONFIG_UNEVICTABLE_LRU=y
+
+#
+# Cache configuration
+#
+CONFIG_SH7705_CACHE_32KB=y
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_ADC=y
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+
+#
+# Board support
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+CONFIG_SH_EDOSK7705=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=31250000
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+# CONFIG_GUSA_RB is not set
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_SH_SCI is not set
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
+
+#
+# File systems
+#
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Pseudo filesystems
+#
+# CONFIG_PROC_FS is not set
+# CONFIG_SYSFS is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+# CONFIG_CRC32 is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/rsk7201_defconfig b/arch/sh/configs/rsk7201_defconfig
new file mode 100644
index 000000000000..014c18cbf46a
--- /dev/null
+++ b/arch/sh/configs/rsk7201_defconfig
@@ -0,0 +1,703 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28-rc6
+# Mon Dec 8 14:48:02 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_GENERIC_IRQ_PROBE=y
+# CONFIG_GENERIC_GPIO is not set
+# CONFIG_GENERIC_TIME is not set
+# CONFIG_GENERIC_CLOCKEVENTS is not set
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_IKCONFIG=y
+# CONFIG_IKCONFIG_PROC is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+# CONFIG_AIO is not set
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_SLAB is not set
+# CONFIG_SLUB is not set
+CONFIG_SLOB=y
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_RT_MUTEXES=y
+CONFIG_TINY_SHMEM=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_FREEZER is not set
+
+#
+# System type
+#
+CONFIG_CPU_SH2=y
+CONFIG_CPU_SH2A=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+CONFIG_CPU_SUBTYPE_SH7201=y
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_PAGE_OFFSET=0x00000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x01000000
+CONFIG_29BIT=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_ENTRY_OFFSET=0x00001000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_SH_FPU=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+CONFIG_SH_RSK=y
+CONFIG_SH_RSK7201=y
+# CONFIG_SH_RSK7203 is not set
+
+#
+# Timer and clock configuration
+#
+# CONFIG_SH_CMT is not set
+CONFIG_SH_MTU2=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=40000000
+CONFIG_SH_CLK_MD=0
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+CONFIG_HZ_1000=y
+CONFIG_HZ=1000
+# CONFIG_SCHED_HRTICK is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_SECCOMP is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200 earlyprintk=serial ignore_loglevel"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF_FDPIC=y
+CONFIG_BINFMT_FLAT=y
+CONFIG_BINFMT_ZFLAT=y
+CONFIG_BINFMT_SHARED_FLAT=y
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x0
+CONFIG_MTD_PHYSMAP_LEN=0x0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=4
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=8
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_REGULATOR is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+CONFIG_DAB=y
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SH=y
+# CONFIG_DMADEVICES is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_FILE_LOCKING is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/rsk7203_defconfig b/arch/sh/configs/rsk7203_defconfig
index 85b0ac4fc667..dcdef31cf19b 100644
--- a/arch/sh/configs/rsk7203_defconfig
+++ b/arch/sh/configs/rsk7203_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Tue Oct 21 12:58:47 2008
+# Linux kernel version: 2.6.28-rc6
+# Mon Dec 8 14:35:03 2008
#
CONFIG_SUPERH=y
CONFIG_SUPERH32=y
@@ -16,6 +16,8 @@ CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_GENERIC_GPIO=y
# CONFIG_GENERIC_TIME is not set
# CONFIG_GENERIC_CLOCKEVENTS is not set
+# CONFIG_ARCH_SUSPEND_POSSIBLE is not set
+# CONFIG_ARCH_HIBERNATION_POSSIBLE is not set
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -75,7 +77,6 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
# CONFIG_SLAB is not set
# CONFIG_SLUB is not set
CONFIG_SLOB=y
@@ -126,6 +127,7 @@ CONFIG_CLASSIC_RCU=y
CONFIG_CPU_SH2=y
CONFIG_CPU_SH2A=y
# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
CONFIG_CPU_SUBTYPE_SH7203=y
# CONFIG_CPU_SUBTYPE_SH7206 is not set
# CONFIG_CPU_SUBTYPE_SH7263 is not set
@@ -211,6 +213,8 @@ CONFIG_CPU_HAS_FPU=y
#
# Board support
#
+CONFIG_SH_RSK=y
+# CONFIG_SH_RSK7201 is not set
CONFIG_SH_RSK7203=y
#
@@ -296,6 +300,14 @@ CONFIG_BINFMT_ZFLAT=y
CONFIG_BINFMT_SHARED_FLAT=y
# CONFIG_HAVE_AOUT is not set
# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options (EXPERIMENTAL)
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
CONFIG_NET=y
#
@@ -477,6 +489,7 @@ CONFIG_BLK_DEV=y
CONFIG_MISC_DEVICES=y
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -603,11 +616,11 @@ CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_HWMON is not set
CONFIG_THERMAL=y
# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
@@ -617,7 +630,11 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_MFD_SM501 is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
-# CONFIG_MFD_WM8400 is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
#
# Multimedia devices
@@ -702,19 +719,22 @@ CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_DEVICEFS=y
CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_OTG is not set
# CONFIG_USB_OTG_WHITELIST is not set
# CONFIG_USB_OTG_BLACKLIST_HUB is not set
CONFIG_USB_MON=y
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
#
# USB Host Controller Drivers
#
# CONFIG_USB_C67X00_HCD is not set
# CONFIG_USB_ISP116X_HCD is not set
-# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_SL811_HCD is not set
CONFIG_USB_R8A66597_HCD=y
+# CONFIG_USB_HWA_HCD is not set
#
# USB Device Class drivers
@@ -725,11 +745,11 @@ CONFIG_USB_R8A66597_HCD=y
# CONFIG_USB_TMC is not set
#
-# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
#
#
-# may also be needed; see USB_STORAGE Help for more information
+# see USB_STORAGE Help for more information
#
# CONFIG_USB_LIBUSUAL is not set
@@ -770,7 +790,22 @@ CONFIG_USB_R8A66597_HCD=y
# CONFIG_USB_GADGET is not set
# CONFIG_MMC is not set
# CONFIG_MEMSTICK is not set
-# CONFIG_NEW_LEDS is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_GPIO=y
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
# CONFIG_ACCESSIBILITY is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
@@ -812,6 +847,7 @@ CONFIG_RTC_DRV_SH=y
# CONFIG_DMADEVICES is not set
# CONFIG_UIO is not set
# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
#
# File systems
@@ -950,9 +986,14 @@ CONFIG_FRAME_POINTER=y
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
-# CONFIG_FTRACE is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
+# CONFIG_FUNCTION_TRACER is not set
# CONFIG_SCHED_TRACER is not set
# CONFIG_CONTEXT_SWITCH_TRACER is not set
# CONFIG_BOOT_TRACER is not set
diff --git a/arch/sh/configs/rts7751r2dplus_qemu_defconfig b/arch/sh/configs/rts7751r2dplus_qemu_defconfig
deleted file mode 100644
index ae8f63000fbf..000000000000
--- a/arch/sh/configs/rts7751r2dplus_qemu_defconfig
+++ /dev/null
@@ -1,949 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Wed Oct 22 18:51:20 2008
-#
-CONFIG_SUPERH=y
-CONFIG_SUPERH32=y
-CONFIG_ARCH_DEFCONFIG="arch/sh/configs/shx3_defconfig"
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-CONFIG_GENERIC_BUG=y
-CONFIG_GENERIC_FIND_NEXT_BIT=y
-CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
-CONFIG_GENERIC_IRQ_PROBE=y
-# CONFIG_GENERIC_GPIO is not set
-CONFIG_GENERIC_TIME=y
-CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_SYS_SUPPORTS_PCI=y
-CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_LOCKDEP_SUPPORT=y
-CONFIG_HAVE_LATENCYTOP_SUPPORT=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_ARCH_NO_VIRT_TO_BUS=y
-CONFIG_IO_TRAPPED=y
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
-
-#
-# General setup
-#
-CONFIG_EXPERIMENTAL=y
-CONFIG_BROKEN_ON_SMP=y
-CONFIG_INIT_ENV_ARG_LIMIT=32
-CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
-CONFIG_SWAP=y
-CONFIG_SYSVIPC=y
-CONFIG_SYSVIPC_SYSCTL=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_CGROUPS is not set
-CONFIG_GROUP_SCHED=y
-CONFIG_FAIR_GROUP_SCHED=y
-# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_USER_SCHED=y
-# CONFIG_CGROUP_SCHED is not set
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-# CONFIG_RELAY is not set
-# CONFIG_NAMESPACES is not set
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE=""
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-CONFIG_SYSCTL=y
-CONFIG_EMBEDDED=y
-CONFIG_UID16=y
-# CONFIG_SYSCTL_SYSCALL is not set
-CONFIG_KALLSYMS=y
-# CONFIG_KALLSYMS_ALL is not set
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
-# CONFIG_HOTPLUG is not set
-CONFIG_PRINTK=y
-CONFIG_BUG=y
-CONFIG_ELF_CORE=y
-CONFIG_COMPAT_BRK=y
-CONFIG_BASE_FULL=y
-CONFIG_FUTEX=y
-CONFIG_ANON_INODES=y
-CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
-CONFIG_SHMEM=y
-CONFIG_AIO=y
-CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
-# CONFIG_SLOB is not set
-CONFIG_PROFILING=y
-# CONFIG_MARKERS is not set
-CONFIG_OPROFILE=y
-CONFIG_HAVE_OPROFILE=y
-# CONFIG_KPROBES is not set
-CONFIG_HAVE_IOREMAP_PROT=y
-CONFIG_HAVE_KPROBES=y
-CONFIG_HAVE_KRETPROBES=y
-CONFIG_HAVE_ARCH_TRACEHOOK=y
-CONFIG_HAVE_CLK=y
-CONFIG_HAVE_GENERIC_DMA_COHERENT=y
-CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
-# CONFIG_TINY_SHMEM is not set
-CONFIG_BASE_SMALL=0
-CONFIG_MODULES=y
-# CONFIG_MODULE_FORCE_LOAD is not set
-# CONFIG_MODULE_UNLOAD is not set
-# CONFIG_MODVERSIONS is not set
-# CONFIG_MODULE_SRCVERSION_ALL is not set
-CONFIG_KMOD=y
-CONFIG_BLOCK=y
-# CONFIG_LBD is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_LSF is not set
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_BLK_DEV_INTEGRITY is not set
-
-#
-# IO Schedulers
-#
-CONFIG_IOSCHED_NOOP=y
-CONFIG_IOSCHED_AS=y
-CONFIG_IOSCHED_DEADLINE=y
-CONFIG_IOSCHED_CFQ=y
-CONFIG_DEFAULT_AS=y
-# CONFIG_DEFAULT_DEADLINE is not set
-# CONFIG_DEFAULT_CFQ is not set
-# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
-CONFIG_CLASSIC_RCU=y
-# CONFIG_FREEZER is not set
-
-#
-# System type
-#
-CONFIG_CPU_SH4=y
-# CONFIG_CPU_SUBTYPE_SH7619 is not set
-# CONFIG_CPU_SUBTYPE_SH7203 is not set
-# CONFIG_CPU_SUBTYPE_SH7206 is not set
-# CONFIG_CPU_SUBTYPE_SH7263 is not set
-# CONFIG_CPU_SUBTYPE_MXG is not set
-# CONFIG_CPU_SUBTYPE_SH7705 is not set
-# CONFIG_CPU_SUBTYPE_SH7706 is not set
-# CONFIG_CPU_SUBTYPE_SH7707 is not set
-# CONFIG_CPU_SUBTYPE_SH7708 is not set
-# CONFIG_CPU_SUBTYPE_SH7709 is not set
-# CONFIG_CPU_SUBTYPE_SH7710 is not set
-# CONFIG_CPU_SUBTYPE_SH7712 is not set
-# CONFIG_CPU_SUBTYPE_SH7720 is not set
-# CONFIG_CPU_SUBTYPE_SH7721 is not set
-# CONFIG_CPU_SUBTYPE_SH7750 is not set
-# CONFIG_CPU_SUBTYPE_SH7091 is not set
-# CONFIG_CPU_SUBTYPE_SH7750R is not set
-# CONFIG_CPU_SUBTYPE_SH7750S is not set
-# CONFIG_CPU_SUBTYPE_SH7751 is not set
-CONFIG_CPU_SUBTYPE_SH7751R=y
-# CONFIG_CPU_SUBTYPE_SH7760 is not set
-# CONFIG_CPU_SUBTYPE_SH4_202 is not set
-# CONFIG_CPU_SUBTYPE_SH7723 is not set
-# CONFIG_CPU_SUBTYPE_SH7763 is not set
-# CONFIG_CPU_SUBTYPE_SH7770 is not set
-# CONFIG_CPU_SUBTYPE_SH7780 is not set
-# CONFIG_CPU_SUBTYPE_SH7785 is not set
-# CONFIG_CPU_SUBTYPE_SHX3 is not set
-# CONFIG_CPU_SUBTYPE_SH7343 is not set
-# CONFIG_CPU_SUBTYPE_SH7722 is not set
-# CONFIG_CPU_SUBTYPE_SH7366 is not set
-# CONFIG_CPU_SUBTYPE_SH5_101 is not set
-# CONFIG_CPU_SUBTYPE_SH5_103 is not set
-
-#
-# Memory management options
-#
-CONFIG_QUICKLIST=y
-CONFIG_MMU=y
-CONFIG_PAGE_OFFSET=0x80000000
-CONFIG_MEMORY_START=0x0c000000
-CONFIG_MEMORY_SIZE=0x04000000
-CONFIG_29BIT=y
-CONFIG_VSYSCALL=y
-CONFIG_ARCH_FLATMEM_ENABLE=y
-CONFIG_ARCH_SPARSEMEM_ENABLE=y
-CONFIG_ARCH_SPARSEMEM_DEFAULT=y
-CONFIG_MAX_ACTIVE_REGIONS=1
-CONFIG_ARCH_POPULATES_NODE_MAP=y
-CONFIG_ARCH_SELECT_MEMORY_MODEL=y
-CONFIG_PAGE_SIZE_4KB=y
-# CONFIG_PAGE_SIZE_8KB is not set
-# CONFIG_PAGE_SIZE_16KB is not set
-# CONFIG_PAGE_SIZE_64KB is not set
-CONFIG_ENTRY_OFFSET=0x00001000
-CONFIG_SELECT_MEMORY_MODEL=y
-CONFIG_FLATMEM_MANUAL=y
-# CONFIG_DISCONTIGMEM_MANUAL is not set
-# CONFIG_SPARSEMEM_MANUAL is not set
-CONFIG_FLATMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-CONFIG_SPARSEMEM_STATIC=y
-CONFIG_PAGEFLAGS_EXTENDED=y
-CONFIG_SPLIT_PTLOCK_CPUS=4
-# CONFIG_RESOURCES_64BIT is not set
-# CONFIG_PHYS_ADDR_T_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=0
-CONFIG_NR_QUICK=2
-CONFIG_UNEVICTABLE_LRU=y
-
-#
-# Cache configuration
-#
-# CONFIG_SH_DIRECT_MAPPED is not set
-CONFIG_CACHE_WRITEBACK=y
-# CONFIG_CACHE_WRITETHROUGH is not set
-# CONFIG_CACHE_OFF is not set
-
-#
-# Processor features
-#
-CONFIG_CPU_LITTLE_ENDIAN=y
-# CONFIG_CPU_BIG_ENDIAN is not set
-CONFIG_SH_FPU=y
-# CONFIG_SH_STORE_QUEUES is not set
-CONFIG_CPU_HAS_INTEVT=y
-CONFIG_CPU_HAS_SR_RB=y
-CONFIG_CPU_HAS_PTEA=y
-CONFIG_CPU_HAS_FPU=y
-
-#
-# Board support
-#
-# CONFIG_SH_7751_SYSTEMH is not set
-# CONFIG_SH_SECUREEDGE5410 is not set
-CONFIG_SH_RTS7751R2D=y
-# CONFIG_SH_LANDISK is not set
-# CONFIG_SH_TITAN is not set
-# CONFIG_SH_LBOX_RE2 is not set
-
-#
-# RTS7751R2D Board Revision
-#
-CONFIG_RTS7751R2D_PLUS=y
-# CONFIG_RTS7751R2D_1 is not set
-
-#
-# Timer and clock configuration
-#
-CONFIG_SH_TMU=y
-CONFIG_SH_TIMER_IRQ=16
-CONFIG_SH_PCLK_FREQ=60000000
-# CONFIG_NO_HZ is not set
-# CONFIG_HIGH_RES_TIMERS is not set
-CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
-
-#
-# CPU Frequency scaling
-#
-# CONFIG_CPU_FREQ is not set
-
-#
-# DMA support
-#
-# CONFIG_SH_DMA is not set
-
-#
-# Companion Chips
-#
-
-#
-# Additional SuperH Device Drivers
-#
-CONFIG_HEARTBEAT=y
-# CONFIG_PUSH_SWITCH is not set
-
-#
-# Kernel features
-#
-# CONFIG_HZ_100 is not set
-CONFIG_HZ_250=y
-# CONFIG_HZ_300 is not set
-# CONFIG_HZ_1000 is not set
-CONFIG_HZ=250
-# CONFIG_SCHED_HRTICK is not set
-# CONFIG_KEXEC is not set
-# CONFIG_CRASH_DUMP is not set
-CONFIG_SECCOMP=y
-CONFIG_PREEMPT_NONE=y
-# CONFIG_PREEMPT_VOLUNTARY is not set
-# CONFIG_PREEMPT is not set
-CONFIG_GUSA=y
-# CONFIG_GUSA_RB is not set
-
-#
-# Boot options
-#
-CONFIG_ZERO_PAGE_OFFSET=0x00010000
-CONFIG_BOOT_LINK_OFFSET=0x00800000
-# CONFIG_UBC_WAKEUP is not set
-CONFIG_CMDLINE_BOOL=y
-CONFIG_CMDLINE="console=tty0 console=ttySC0,115200 root=/dev/sda1 earlyprintk=serial"
-
-#
-# Bus options
-#
-# CONFIG_PCI is not set
-# CONFIG_ARCH_SUPPORTS_MSI is not set
-
-#
-# Executable file formats
-#
-CONFIG_BINFMT_ELF=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_HAVE_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
-# CONFIG_NET is not set
-
-#
-# Device Drivers
-#
-
-#
-# Generic Driver Options
-#
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_DEBUG_DRIVER is not set
-# CONFIG_DEBUG_DEVRES is not set
-# CONFIG_SYS_HYPERVISOR is not set
-# CONFIG_MTD is not set
-# CONFIG_PARPORT is not set
-CONFIG_BLK_DEV=y
-# CONFIG_BLK_DEV_COW_COMMON is not set
-# CONFIG_BLK_DEV_LOOP is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=4096
-# CONFIG_BLK_DEV_XIP is not set
-# CONFIG_CDROM_PKTCDVD is not set
-# CONFIG_BLK_DEV_HD is not set
-CONFIG_MISC_DEVICES=y
-# CONFIG_EEPROM_93CX6 is not set
-# CONFIG_ENCLOSURE_SERVICES is not set
-CONFIG_HAVE_IDE=y
-# CONFIG_IDE is not set
-
-#
-# SCSI device support
-#
-# CONFIG_RAID_ATTRS is not set
-CONFIG_SCSI=y
-CONFIG_SCSI_DMA=y
-# CONFIG_SCSI_TGT is not set
-# CONFIG_SCSI_NETLINK is not set
-CONFIG_SCSI_PROC_FS=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-# CONFIG_CHR_DEV_OSST is not set
-# CONFIG_BLK_DEV_SR is not set
-# CONFIG_CHR_DEV_SG is not set
-# CONFIG_CHR_DEV_SCH is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
-# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
-# CONFIG_SCSI_LOGGING is not set
-# CONFIG_SCSI_SCAN_ASYNC is not set
-CONFIG_SCSI_WAIT_SCAN=m
-
-#
-# SCSI Transports
-#
-# CONFIG_SCSI_SPI_ATTRS is not set
-# CONFIG_SCSI_FC_ATTRS is not set
-# CONFIG_SCSI_SAS_LIBSAS is not set
-# CONFIG_SCSI_SRP_ATTRS is not set
-CONFIG_SCSI_LOWLEVEL=y
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_DH is not set
-CONFIG_ATA=y
-# CONFIG_ATA_NONSTANDARD is not set
-CONFIG_SATA_PMP=y
-CONFIG_ATA_SFF=y
-# CONFIG_SATA_MV is not set
-# CONFIG_PATA_PLATFORM is not set
-# CONFIG_MD is not set
-# CONFIG_PHONE is not set
-
-#
-# Input device support
-#
-CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-# CONFIG_INPUT_POLLDEV is not set
-
-#
-# Userland interfaces
-#
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_EVDEV is not set
-# CONFIG_INPUT_EVBUG is not set
-
-#
-# Input Device Drivers
-#
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TABLET is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
-# CONFIG_INPUT_MISC is not set
-
-#
-# Hardware I/O ports
-#
-# CONFIG_SERIO is not set
-# CONFIG_GAMEPORT is not set
-
-#
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_CONSOLE_TRANSLATIONS=y
-CONFIG_VT_CONSOLE=y
-CONFIG_HW_CONSOLE=y
-CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_DEVKMEM=y
-# CONFIG_SERIAL_NONSTANDARD is not set
-
-#
-# Serial drivers
-#
-CONFIG_SERIAL_8250=y
-# CONFIG_SERIAL_8250_CONSOLE is not set
-CONFIG_SERIAL_8250_NR_UARTS=4
-CONFIG_SERIAL_8250_RUNTIME_UARTS=4
-# CONFIG_SERIAL_8250_EXTENDED is not set
-
-#
-# Non-8250 serial port support
-#
-CONFIG_SERIAL_SH_SCI=y
-CONFIG_SERIAL_SH_SCI_NR_UARTS=1
-CONFIG_SERIAL_SH_SCI_CONSOLE=y
-CONFIG_SERIAL_CORE=y
-CONFIG_SERIAL_CORE_CONSOLE=y
-CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
-# CONFIG_IPMI_HANDLER is not set
-CONFIG_HW_RANDOM=y
-# CONFIG_R3964 is not set
-# CONFIG_RAW_DRIVER is not set
-# CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
-CONFIG_SPI=y
-# CONFIG_SPI_DEBUG is not set
-CONFIG_SPI_MASTER=y
-
-#
-# SPI Master Controller Drivers
-#
-CONFIG_SPI_BITBANG=y
-# CONFIG_SPI_SH_SCI is not set
-
-#
-# SPI Protocol Masters
-#
-# CONFIG_SPI_AT25 is not set
-# CONFIG_SPI_SPIDEV is not set
-# CONFIG_SPI_TLE62X0 is not set
-# CONFIG_W1 is not set
-# CONFIG_POWER_SUPPLY is not set
-CONFIG_HWMON=y
-# CONFIG_HWMON_VID is not set
-# CONFIG_SENSORS_ADCXX is not set
-# CONFIG_SENSORS_F71805F is not set
-# CONFIG_SENSORS_F71882FG is not set
-# CONFIG_SENSORS_IT87 is not set
-# CONFIG_SENSORS_LM70 is not set
-# CONFIG_SENSORS_MAX1111 is not set
-# CONFIG_SENSORS_PC87360 is not set
-# CONFIG_SENSORS_PC87427 is not set
-# CONFIG_SENSORS_SMSC47M1 is not set
-# CONFIG_SENSORS_SMSC47B397 is not set
-# CONFIG_SENSORS_VT1211 is not set
-# CONFIG_SENSORS_W83627HF is not set
-# CONFIG_SENSORS_W83627EHF is not set
-# CONFIG_HWMON_DEBUG_CHIP is not set
-# CONFIG_THERMAL is not set
-# CONFIG_THERMAL_HWMON is not set
-# CONFIG_WATCHDOG is not set
-
-#
-# Sonics Silicon Backplane
-#
-CONFIG_SSB_POSSIBLE=y
-# CONFIG_SSB is not set
-
-#
-# Multifunction device drivers
-#
-# CONFIG_MFD_CORE is not set
-CONFIG_MFD_SM501=y
-# CONFIG_HTC_PASIC3 is not set
-# CONFIG_MFD_TMIO is not set
-# CONFIG_MFD_WM8400 is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-CONFIG_DAB=y
-
-#
-# Graphics support
-#
-# CONFIG_VGASTATE is not set
-CONFIG_VIDEO_OUTPUT_CONTROL=m
-CONFIG_FB=y
-# CONFIG_FIRMWARE_EDID is not set
-# CONFIG_FB_DDC is not set
-# CONFIG_FB_BOOT_VESA_SUPPORT is not set
-CONFIG_FB_CFB_FILLRECT=y
-CONFIG_FB_CFB_COPYAREA=y
-CONFIG_FB_CFB_IMAGEBLIT=y
-# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
-# CONFIG_FB_SYS_FILLRECT is not set
-# CONFIG_FB_SYS_COPYAREA is not set
-# CONFIG_FB_SYS_IMAGEBLIT is not set
-# CONFIG_FB_FOREIGN_ENDIAN is not set
-# CONFIG_FB_SYS_FOPS is not set
-# CONFIG_FB_SVGALIB is not set
-# CONFIG_FB_MACMODES is not set
-# CONFIG_FB_BACKLIGHT is not set
-# CONFIG_FB_MODE_HELPERS is not set
-# CONFIG_FB_TILEBLITTING is not set
-
-#
-# Frame buffer hardware drivers
-#
-# CONFIG_FB_S1D13XXX is not set
-CONFIG_FB_SH_MOBILE_LCDC=m
-CONFIG_FB_SM501=y
-# CONFIG_FB_VIRTUAL is not set
-# CONFIG_FB_METRONOME is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
-
-#
-# Display device support
-#
-# CONFIG_DISPLAY_SUPPORT is not set
-
-#
-# Console display driver support
-#
-CONFIG_DUMMY_CONSOLE=y
-CONFIG_FRAMEBUFFER_CONSOLE=y
-# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
-# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
-# CONFIG_FONTS is not set
-CONFIG_FONT_8x8=y
-CONFIG_FONT_8x16=y
-CONFIG_LOGO=y
-# CONFIG_LOGO_LINUX_MONO is not set
-# CONFIG_LOGO_LINUX_VGA16 is not set
-# CONFIG_LOGO_LINUX_CLUT224 is not set
-# CONFIG_LOGO_SUPERH_MONO is not set
-# CONFIG_LOGO_SUPERH_VGA16 is not set
-CONFIG_LOGO_SUPERH_CLUT224=y
-CONFIG_SOUND=y
-CONFIG_SOUND_OSS_CORE=y
-CONFIG_SND=m
-# CONFIG_SND_SEQUENCER is not set
-# CONFIG_SND_MIXER_OSS is not set
-# CONFIG_SND_PCM_OSS is not set
-# CONFIG_SND_DYNAMIC_MINORS is not set
-CONFIG_SND_SUPPORT_OLD_API=y
-CONFIG_SND_VERBOSE_PROCFS=y
-# CONFIG_SND_VERBOSE_PRINTK is not set
-# CONFIG_SND_DEBUG is not set
-CONFIG_SND_DRIVERS=y
-# CONFIG_SND_DUMMY is not set
-# CONFIG_SND_MTPAV is not set
-# CONFIG_SND_SERIAL_U16550 is not set
-# CONFIG_SND_MPU401 is not set
-CONFIG_SND_SPI=y
-CONFIG_SND_SUPERH=y
-# CONFIG_SND_SOC is not set
-CONFIG_SOUND_PRIME=m
-CONFIG_HID_SUPPORT=y
-CONFIG_HID=y
-# CONFIG_HID_DEBUG is not set
-# CONFIG_HIDRAW is not set
-# CONFIG_HID_PID is not set
-
-#
-# Special HID drivers
-#
-CONFIG_HID_COMPAT=y
-# CONFIG_USB_SUPPORT is not set
-# CONFIG_MMC is not set
-# CONFIG_MEMSTICK is not set
-# CONFIG_NEW_LEDS is not set
-# CONFIG_ACCESSIBILITY is not set
-CONFIG_RTC_LIB=y
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_HCTOSYS=y
-CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
-# CONFIG_RTC_DEBUG is not set
-
-#
-# RTC interfaces
-#
-CONFIG_RTC_INTF_SYSFS=y
-CONFIG_RTC_INTF_PROC=y
-CONFIG_RTC_INTF_DEV=y
-# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
-# CONFIG_RTC_DRV_TEST is not set
-
-#
-# SPI RTC drivers
-#
-# CONFIG_RTC_DRV_M41T94 is not set
-# CONFIG_RTC_DRV_DS1305 is not set
-# CONFIG_RTC_DRV_MAX6902 is not set
-CONFIG_RTC_DRV_R9701=y
-# CONFIG_RTC_DRV_RS5C348 is not set
-# CONFIG_RTC_DRV_DS3234 is not set
-
-#
-# Platform RTC drivers
-#
-# CONFIG_RTC_DRV_DS1286 is not set
-# CONFIG_RTC_DRV_DS1511 is not set
-# CONFIG_RTC_DRV_DS1553 is not set
-# CONFIG_RTC_DRV_DS1742 is not set
-# CONFIG_RTC_DRV_STK17TA8 is not set
-# CONFIG_RTC_DRV_M48T86 is not set
-# CONFIG_RTC_DRV_M48T35 is not set
-# CONFIG_RTC_DRV_M48T59 is not set
-# CONFIG_RTC_DRV_BQ4802 is not set
-# CONFIG_RTC_DRV_V3020 is not set
-
-#
-# on-CPU RTC drivers
-#
-# CONFIG_RTC_DRV_SH is not set
-# CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
-# CONFIG_STAGING is not set
-
-#
-# File systems
-#
-CONFIG_EXT2_FS=y
-# CONFIG_EXT2_FS_XATTR is not set
-# CONFIG_EXT2_FS_XIP is not set
-# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4_FS is not set
-# CONFIG_REISERFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
-# CONFIG_XFS_FS is not set
-CONFIG_DNOTIFY=y
-CONFIG_INOTIFY=y
-CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_AUTOFS4_FS is not set
-# CONFIG_FUSE_FS is not set
-
-#
-# CD-ROM/DVD Filesystems
-#
-# CONFIG_ISO9660_FS is not set
-# CONFIG_UDF_FS is not set
-
-#
-# DOS/FAT/NT Filesystems
-#
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_FAT_DEFAULT_CODEPAGE=437
-CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-# CONFIG_NTFS_FS is not set
-
-#
-# Pseudo filesystems
-#
-CONFIG_PROC_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_PROC_SYSCTL=y
-CONFIG_PROC_PAGE_MONITOR=y
-CONFIG_SYSFS=y
-CONFIG_TMPFS=y
-# CONFIG_TMPFS_POSIX_ACL is not set
-# CONFIG_HUGETLBFS is not set
-# CONFIG_HUGETLB_PAGE is not set
-# CONFIG_CONFIGFS_FS is not set
-
-#
-# Miscellaneous filesystems
-#
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_HFSPLUS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_CRAMFS is not set
-# CONFIG_VXFS_FS is not set
-CONFIG_MINIX_FS=y
-# CONFIG_OMFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-
-#
-# Partition Types
-#
-# CONFIG_PARTITION_ADVANCED is not set
-CONFIG_MSDOS_PARTITION=y
-CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
-# CONFIG_NLS_CODEPAGE_437 is not set
-# CONFIG_NLS_CODEPAGE_737 is not set
-# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
-# CONFIG_NLS_CODEPAGE_855 is not set
-# CONFIG_NLS_CODEPAGE_857 is not set
-# CONFIG_NLS_CODEPAGE_860 is not set
-# CONFIG_NLS_CODEPAGE_861 is not set
-# CONFIG_NLS_CODEPAGE_862 is not set
-# CONFIG_NLS_CODEPAGE_863 is not set
-# CONFIG_NLS_CODEPAGE_864 is not set
-# CONFIG_NLS_CODEPAGE_865 is not set
-# CONFIG_NLS_CODEPAGE_866 is not set
-# CONFIG_NLS_CODEPAGE_869 is not set
-# CONFIG_NLS_CODEPAGE_936 is not set
-# CONFIG_NLS_CODEPAGE_950 is not set
-CONFIG_NLS_CODEPAGE_932=y
-# CONFIG_NLS_CODEPAGE_949 is not set
-# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_8 is not set
-# CONFIG_NLS_CODEPAGE_1250 is not set
-# CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ASCII is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_ISO8859_13 is not set
-# CONFIG_NLS_ISO8859_14 is not set
-# CONFIG_NLS_ISO8859_15 is not set
-# CONFIG_NLS_KOI8_R is not set
-# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
-
-#
-# Kernel hacking
-#
-CONFIG_TRACE_IRQFLAGS_SUPPORT=y
-# CONFIG_PRINTK_TIME is not set
-CONFIG_ENABLE_WARN_DEPRECATED=y
-CONFIG_ENABLE_MUST_CHECK=y
-CONFIG_FRAME_WARN=1024
-# CONFIG_MAGIC_SYSRQ is not set
-# CONFIG_UNUSED_SYMBOLS is not set
-CONFIG_DEBUG_FS=y
-# CONFIG_HEADERS_CHECK is not set
-CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_SHIRQ is not set
-CONFIG_DETECT_SOFTLOCKUP=y
-# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
-CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
-CONFIG_SCHED_DEBUG=y
-# CONFIG_SCHEDSTATS is not set
-# CONFIG_TIMER_STATS is not set
-# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_DEBUG_SLAB is not set
-# CONFIG_DEBUG_RT_MUTEXES is not set
-# CONFIG_RT_MUTEX_TESTER is not set
-# CONFIG_DEBUG_SPINLOCK is not set
-# CONFIG_DEBUG_MUTEXES is not set
-# CONFIG_DEBUG_LOCK_ALLOC is not set
-# CONFIG_PROVE_LOCKING is not set
-# CONFIG_LOCK_STAT is not set
-# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
-# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
-# CONFIG_DEBUG_KOBJECT is not set
-# CONFIG_DEBUG_BUGVERBOSE is not set
-CONFIG_DEBUG_INFO=y
-# CONFIG_DEBUG_VM is not set
-# CONFIG_DEBUG_WRITECOUNT is not set
-# CONFIG_DEBUG_MEMORY_INIT is not set
-# CONFIG_DEBUG_LIST is not set
-# CONFIG_DEBUG_SG is not set
-# CONFIG_FRAME_POINTER is not set
-# CONFIG_RCU_TORTURE_TEST is not set
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-# CONFIG_BACKTRACE_SELF_TEST is not set
-# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
-# CONFIG_FAULT_INJECTION is not set
-# CONFIG_LATENCYTOP is not set
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
-# CONFIG_FTRACE is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
-# CONFIG_SAMPLES is not set
-# CONFIG_SH_STANDARD_BIOS is not set
-CONFIG_EARLY_SCIF_CONSOLE=y
-CONFIG_EARLY_SCIF_CONSOLE_PORT=0xffe80000
-CONFIG_EARLY_PRINTK=y
-# CONFIG_DEBUG_BOOTMEM is not set
-# CONFIG_DEBUG_STACKOVERFLOW is not set
-# CONFIG_DEBUG_STACK_USAGE is not set
-# CONFIG_4KSTACKS is not set
-# CONFIG_IRQSTACKS is not set
-# CONFIG_SH_KGDB is not set
-
-#
-# Security options
-#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
-# CONFIG_SECURITYFS is not set
-# CONFIG_SECURITY_FILE_CAPABILITIES is not set
-CONFIG_CRYPTO=y
-
-#
-# Crypto core or helper
-#
-# CONFIG_CRYPTO_FIPS is not set
-# CONFIG_CRYPTO_MANAGER is not set
-# CONFIG_CRYPTO_GF128MUL is not set
-# CONFIG_CRYPTO_NULL is not set
-# CONFIG_CRYPTO_CRYPTD is not set
-# CONFIG_CRYPTO_AUTHENC is not set
-# CONFIG_CRYPTO_TEST is not set
-
-#
-# Authenticated Encryption with Associated Data
-#
-# CONFIG_CRYPTO_CCM is not set
-# CONFIG_CRYPTO_GCM is not set
-# CONFIG_CRYPTO_SEQIV is not set
-
-#
-# Block modes
-#
-# CONFIG_CRYPTO_CBC is not set
-# CONFIG_CRYPTO_CTR is not set
-# CONFIG_CRYPTO_CTS is not set
-# CONFIG_CRYPTO_ECB is not set
-# CONFIG_CRYPTO_LRW is not set
-# CONFIG_CRYPTO_PCBC is not set
-# CONFIG_CRYPTO_XTS is not set
-
-#
-# Hash modes
-#
-# CONFIG_CRYPTO_HMAC is not set
-# CONFIG_CRYPTO_XCBC is not set
-
-#
-# Digest
-#
-# CONFIG_CRYPTO_CRC32C is not set
-# CONFIG_CRYPTO_MD4 is not set
-# CONFIG_CRYPTO_MD5 is not set
-# CONFIG_CRYPTO_MICHAEL_MIC is not set
-# CONFIG_CRYPTO_RMD128 is not set
-# CONFIG_CRYPTO_RMD160 is not set
-# CONFIG_CRYPTO_RMD256 is not set
-# CONFIG_CRYPTO_RMD320 is not set
-# CONFIG_CRYPTO_SHA1 is not set
-# CONFIG_CRYPTO_SHA256 is not set
-# CONFIG_CRYPTO_SHA512 is not set
-# CONFIG_CRYPTO_TGR192 is not set
-# CONFIG_CRYPTO_WP512 is not set
-
-#
-# Ciphers
-#
-# CONFIG_CRYPTO_AES is not set
-# CONFIG_CRYPTO_ANUBIS is not set
-# CONFIG_CRYPTO_ARC4 is not set
-# CONFIG_CRYPTO_BLOWFISH is not set
-# CONFIG_CRYPTO_CAMELLIA is not set
-# CONFIG_CRYPTO_CAST5 is not set
-# CONFIG_CRYPTO_CAST6 is not set
-# CONFIG_CRYPTO_DES is not set
-# CONFIG_CRYPTO_FCRYPT is not set
-# CONFIG_CRYPTO_KHAZAD is not set
-# CONFIG_CRYPTO_SALSA20 is not set
-# CONFIG_CRYPTO_SEED is not set
-# CONFIG_CRYPTO_SERPENT is not set
-# CONFIG_CRYPTO_TEA is not set
-# CONFIG_CRYPTO_TWOFISH is not set
-
-#
-# Compression
-#
-# CONFIG_CRYPTO_DEFLATE is not set
-# CONFIG_CRYPTO_LZO is not set
-
-#
-# Random Number Generation
-#
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_CRYPTO_HW=y
-
-#
-# Library routines
-#
-CONFIG_BITREVERSE=y
-# CONFIG_CRC_CCITT is not set
-# CONFIG_CRC16 is not set
-CONFIG_CRC_T10DIF=y
-# CONFIG_CRC_ITU_T is not set
-CONFIG_CRC32=y
-# CONFIG_CRC7 is not set
-# CONFIG_LIBCRC32C is not set
-CONFIG_PLIST=y
-CONFIG_HAS_IOMEM=y
-CONFIG_HAS_IOPORT=y
-CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/se7343_defconfig b/arch/sh/configs/se7343_defconfig
index 075f42ed5b09..be246f381507 100644
--- a/arch/sh/configs/se7343_defconfig
+++ b/arch/sh/configs/se7343_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27
-# Wed Oct 22 19:00:21 2008
+# Linux kernel version: 2.6.28-rc6
+# Thu Dec 4 16:40:25 2008
#
CONFIG_SUPERH=y
CONFIG_SUPERH32=y
@@ -74,7 +74,6 @@ CONFIG_EVENTFD=y
# CONFIG_SHMEM is not set
CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_PCI_QUIRKS=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_SLOB is not set
@@ -127,6 +126,7 @@ CONFIG_CPU_SH4=y
CONFIG_CPU_SH4A=y
CONFIG_CPU_SH4AL_DSP=y
# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7201 is not set
# CONFIG_CPU_SUBTYPE_SH7203 is not set
# CONFIG_CPU_SUBTYPE_SH7206 is not set
# CONFIG_CPU_SUBTYPE_SH7263 is not set
@@ -227,7 +227,7 @@ CONFIG_SH_7343_SOLUTION_ENGINE=y
#
CONFIG_SH_TMU=y
CONFIG_SH_TIMER_IRQ=16
-CONFIG_SH_PCLK_FREQ=27000000
+CONFIG_SH_PCLK_FREQ=33333333
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
@@ -274,7 +274,8 @@ CONFIG_GUSA=y
#
CONFIG_ZERO_PAGE_OFFSET=0x00001000
CONFIG_BOOT_LINK_OFFSET=0x00800000
-# CONFIG_CMDLINE_BOOL is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200"
#
# Bus options
@@ -463,6 +464,7 @@ CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
@@ -519,23 +521,10 @@ CONFIG_NETDEVICES=y
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
-# CONFIG_PHYLIB is not set
-CONFIG_NET_ETHERNET=y
+# CONFIG_NET_ETHERNET is not set
CONFIG_MII=y
-# CONFIG_AX88796 is not set
-# CONFIG_STNIC is not set
-CONFIG_SMC91X=y
-# CONFIG_SMC911X is not set
-# CONFIG_IBM_NEW_EMAC_ZMII is not set
-# CONFIG_IBM_NEW_EMAC_RGMII is not set
-# CONFIG_IBM_NEW_EMAC_TAH is not set
-# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
-# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
-# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
-# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
-# CONFIG_B44 is not set
-CONFIG_NETDEV_1000=y
-CONFIG_NETDEV_10000=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
#
# Wireless LAN
@@ -543,6 +532,26 @@ CONFIG_NETDEV_10000=y
# CONFIG_WLAN_PRE80211 is not set
# CONFIG_WLAN_80211 is not set
# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_AX8817X is not set
+CONFIG_USB_NET_CDCETHER=y
+CONFIG_USB_NET_DM9601=y
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
# CONFIG_WAN is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
@@ -597,13 +606,17 @@ CONFIG_DEVKMEM=y
#
# Serial drivers
#
-# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+# CONFIG_SERIAL_8250_EXTENDED is not set
#
# Non-8250 serial port support
#
CONFIG_SERIAL_SH_SCI=y
-CONFIG_SERIAL_SH_SCI_NR_UARTS=2
+CONFIG_SERIAL_SH_SCI_NR_UARTS=4
CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
@@ -615,7 +628,51 @@ CONFIG_HW_RANDOM=y
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
-# CONFIG_I2C is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_SH_MOBILE=y
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
# CONFIG_SPI is not set
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
@@ -623,11 +680,11 @@ CONFIG_HW_RANDOM=y
# CONFIG_THERMAL is not set
# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
@@ -637,7 +694,10 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_MFD_SM501 is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_REGULATOR is not set
#
# Multimedia devices
@@ -657,6 +717,16 @@ CONFIG_VIDEO_MEDIA=y
# Multimedia drivers
#
# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
CONFIG_VIDEO_V4L2=y
CONFIG_VIDEO_V4L1=y
CONFIG_VIDEO_CAPTURE_DRIVERS=y
@@ -665,8 +735,57 @@ CONFIG_VIDEO_CAPTURE_DRIVERS=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
# CONFIG_VIDEO_VIVI is not set
# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
CONFIG_RADIO_ADAPTERS=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+# CONFIG_USB_MR800 is not set
# CONFIG_DAB is not set
#
@@ -700,6 +819,7 @@ CONFIG_FB_CFB_IMAGEBLIT=m
CONFIG_FB_SH_MOBILE_LCDC=m
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
#
@@ -737,27 +857,147 @@ CONFIG_SND_DRIVERS=y
# CONFIG_SND_SERIAL_U16550 is not set
# CONFIG_SND_MPU401 is not set
CONFIG_SND_SUPERH=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
# CONFIG_SND_SOC is not set
# CONFIG_SOUND_PRIME is not set
CONFIG_HID_SUPPORT=y
CONFIG_HID=y
# CONFIG_HID_DEBUG is not set
# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
#
# Special HID drivers
#
CONFIG_HID_COMPAT=y
-# CONFIG_USB_SUPPORT is not set
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_BRIGHT=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DELL=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_ISP116X_HCD=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;
+#
+
+#
+# see USB_STORAGE Help for more information
+#
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+# CONFIG_USB_GADGET is not set
# CONFIG_MMC is not set
# CONFIG_MEMSTICK is not set
# CONFIG_NEW_LEDS is not set
# CONFIG_ACCESSIBILITY is not set
# CONFIG_RTC_CLASS is not set
# CONFIG_DMADEVICES is not set
-# CONFIG_UIO is not set
+CONFIG_UIO=y
+# CONFIG_UIO_PDRV is not set
+# CONFIG_UIO_PDRV_GENIRQ is not set
+# CONFIG_UIO_SMX is not set
+# CONFIG_UIO_SERCOS3 is not set
# CONFIG_STAGING is not set
+CONFIG_STAGING_EXCLUDE_BUILD=y
#
# File systems
@@ -889,8 +1129,13 @@ CONFIG_FRAME_WARN=1024
# CONFIG_DEBUG_MEMORY_INIT is not set
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_LATENCYTOP is not set
-CONFIG_NOP_TRACER=y
-CONFIG_HAVE_FTRACE=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+
+#
+# Tracers
+#
# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
# CONFIG_SAMPLES is not set
# CONFIG_SH_STANDARD_BIOS is not set
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
index 1ac812d24488..ab956adacb47 100644
--- a/arch/sh/drivers/dma/Makefile
+++ b/arch/sh/drivers/dma/Makefile
@@ -3,7 +3,6 @@
#
obj-$(CONFIG_SH_DMA_API) += dma-api.o dma-sysfs.o
-obj-$(CONFIG_ISA_DMA_API) += dma-isa.o
obj-$(CONFIG_SH_DMA) += dma-sh.o
obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o
obj-$(CONFIG_SH_DMABRG) += dmabrg.o
diff --git a/arch/sh/drivers/dma/dma-isa.c b/arch/sh/drivers/dma/dma-isa.c
deleted file mode 100644
index 5fb044b791c3..000000000000
--- a/arch/sh/drivers/dma/dma-isa.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * arch/sh/drivers/dma/dma-isa.c
- *
- * Generic ISA DMA wrapper for SH DMA API
- *
- * Copyright (C) 2003, 2004 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <asm/dma.h>
-
-/*
- * This implements a small wrapper set to make code using the old ISA DMA API
- * work with the SH DMA API. Since most of the work in the new API happens
- * at ops->xfer() time, we simply use the various set_dma_xxx() routines to
- * fill in per-channel info, and then hand hand this off to ops->xfer() at
- * enable_dma() time.
- *
- * For channels that are doing on-demand data transfer via cascading, the
- * channel itself will still need to be configured through the new API. As
- * such, this code is meant for only the simplest of tasks (and shouldn't be
- * used in any new drivers at all).
- *
- * NOTE: ops->xfer() is the preferred way of doing things. However, there
- * are some users of the ISA DMA API that exist in common code that we
- * don't necessarily want to go out of our way to break, so we still
- * allow for some compatibility at that level. Any new code is strongly
- * advised to run far away from the ISA DMA API and use the SH DMA API
- * directly.
- */
-unsigned long claim_dma_lock(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dma_spin_lock, flags);
-
- return flags;
-}
-EXPORT_SYMBOL(claim_dma_lock);
-
-void release_dma_lock(unsigned long flags)
-{
- spin_unlock_irqrestore(&dma_spin_lock, flags);
-}
-EXPORT_SYMBOL(release_dma_lock);
-
-void disable_dma(unsigned int chan)
-{
- /* Nothing */
-}
-EXPORT_SYMBOL(disable_dma);
-
-void enable_dma(unsigned int chan)
-{
- struct dma_info *info = get_dma_info(chan);
- struct dma_channel *channel = &info->channels[chan];
-
- info->ops->xfer(channel);
-}
-EXPORT_SYMBOL(enable_dma);
-
-void clear_dma_ff(unsigned int chan)
-{
- /* Nothing */
-}
-EXPORT_SYMBOL(clear_dma_ff);
-
-void set_dma_mode(unsigned int chan, char mode)
-{
- struct dma_info *info = get_dma_info(chan);
- struct dma_channel *channel = &info->channels[chan];
-
- channel->mode = mode;
-}
-EXPORT_SYMBOL(set_dma_mode);
-
-void set_dma_addr(unsigned int chan, unsigned int addr)
-{
- struct dma_info *info = get_dma_info(chan);
- struct dma_channel *channel = &info->channels[chan];
-
- /*
- * Single address mode is the only thing supported through
- * this interface.
- */
- if ((channel->mode & DMA_MODE_MASK) == DMA_MODE_READ) {
- channel->sar = addr;
- } else {
- channel->dar = addr;
- }
-}
-EXPORT_SYMBOL(set_dma_addr);
-
-void set_dma_count(unsigned int chan, unsigned int count)
-{
- struct dma_info *info = get_dma_info(chan);
- struct dma_channel *channel = &info->channels[chan];
-
- channel->count = count;
-}
-EXPORT_SYMBOL(set_dma_count);
-
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index b2ffe649c7c0..50887a592dd0 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -205,7 +205,8 @@ static int sh_dmac_get_dma_residue(struct dma_channel *chan)
#if defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780)
+ defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709)
#define dmaor_read_reg() ctrl_inw(DMAOR)
#define dmaor_write_reg(data) ctrl_outw(data, DMAOR)
#else
diff --git a/arch/sh/drivers/dma/dma-sh.h b/arch/sh/drivers/dma/dma-sh.h
index b05af34fc15d..05fecd5428e4 100644
--- a/arch/sh/drivers/dma/dma-sh.h
+++ b/arch/sh/drivers/dma/dma-sh.h
@@ -29,6 +29,7 @@
#define RS_IN 0x00000200
#define RS_OUT 0x00000300
#define TS_BLK 0x00000040
+#define TM_BUR 0x00000020
#define CHCR_DE 0x00000001
#define CHCR_TE 0x00000002
#define CHCR_IE 0x00000004
diff --git a/arch/sh/drivers/pci/ops-sh03.c b/arch/sh/drivers/pci/ops-sh03.c
index ebb58e605d9d..e1703ff5a4d2 100644
--- a/arch/sh/drivers/pci/ops-sh03.c
+++ b/arch/sh/drivers/pci/ops-sh03.c
@@ -18,7 +18,8 @@
*/
int __init pcibios_init_platform(void)
{
- return 1;
+ __set_io_port_base(SH7751_PCI_IO_BASE);
+ return 1;
}
static struct resource sh7751_io_resource = {
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c
index b2a2bfa3c1bd..078dc44d6b08 100644
--- a/arch/sh/drivers/pci/pci-sh7780.c
+++ b/arch/sh/drivers/pci/pci-sh7780.c
@@ -123,16 +123,14 @@ int __init sh7780_pcic_init(struct sh4_pci_address_map *map)
* Window0 = map->window0.size @ non-cached area base = SDRAM
* Window1 = map->window1.size @ cached area base = SDRAM
*/
- word = ((map->window0.size - 1) & 0x1ff00001) | 0x01;
- pci_write_reg(0x07f00001, SH4_PCILSR0);
- word = ((map->window1.size - 1) & 0x1ff00001) | 0x01;
+ word = (CONFIG_MEMORY_SIZE - 0x00100000) | 0x00000001;
+ pci_write_reg(word, SH4_PCILSR0);
pci_write_reg(0x00000001, SH4_PCILSR1);
/* Set the values on window 0 PCI config registers */
- word = P2SEGADDR(map->window0.base);
- pci_write_reg(0xa8000000, SH4_PCILAR0);
- pci_write_reg(0x08000000, SH7780_PCIMBAR0);
+ word = (CONFIG_MEMORY_SIZE > 0x08000000) ? 0x10000000 : 0x08000000;
+ pci_write_reg(word | 0xa0000000, SH4_PCILAR0);
+ pci_write_reg(word, SH7780_PCIMBAR0);
/* Set the values on window 1 PCI config registers */
- word = P2SEGADDR(map->window1.base);
pci_write_reg(0x00000000, SH4_PCILAR1);
pci_write_reg(0x00000000, SH7780_PCIMBAR1);
diff --git a/arch/sh/include/asm/addrspace.h b/arch/sh/include/asm/addrspace.h
index 2702d81bfc0d..36736c7e93db 100644
--- a/arch/sh/include/asm/addrspace.h
+++ b/arch/sh/include/asm/addrspace.h
@@ -49,5 +49,16 @@
/* Check if an address can be reached in 29 bits */
#define IS_29BIT(a) (((unsigned long)(a)) < 0x20000000)
+#ifdef CONFIG_SH_STORE_QUEUES
+/*
+ * This is a special case for the SH-4 store queues, as pages for this
+ * space still need to be faulted in before it's possible to flush the
+ * store queue cache for writeout to the remapped region.
+ */
+#define P3_ADDR_MAX (P4SEG_STORE_QUE + 0x04000000)
+#else
+#define P3_ADDR_MAX P4SEG
+#endif
+
#endif /* __KERNEL__ */
#endif /* __ASM_SH_ADDRSPACE_H */
diff --git a/arch/sh/include/asm/bitops-grb.h b/arch/sh/include/asm/bitops-grb.h
index a5907b94395b..e73af33acbf4 100644
--- a/arch/sh/include/asm/bitops-grb.h
+++ b/arch/sh/include/asm/bitops-grb.h
@@ -166,4 +166,7 @@ static inline int test_and_change_bit(int nr, volatile void * addr)
return retval;
}
+
+#include <asm-generic/bitops/non-atomic.h>
+
#endif /* __ASM_SH_BITOPS_GRB_H */
diff --git a/arch/sh/include/asm/bitops-irq.h b/arch/sh/include/asm/bitops-irq.h
deleted file mode 100644
index 653a12750584..000000000000
--- a/arch/sh/include/asm/bitops-irq.h
+++ /dev/null
@@ -1,91 +0,0 @@
-#ifndef __ASM_SH_BITOPS_IRQ_H
-#define __ASM_SH_BITOPS_IRQ_H
-
-static inline void set_bit(int nr, volatile void *addr)
-{
- int mask;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- *a |= mask;
- local_irq_restore(flags);
-}
-
-static inline void clear_bit(int nr, volatile void *addr)
-{
- int mask;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- *a &= ~mask;
- local_irq_restore(flags);
-}
-
-static inline void change_bit(int nr, volatile void *addr)
-{
- int mask;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- *a ^= mask;
- local_irq_restore(flags);
-}
-
-static inline int test_and_set_bit(int nr, volatile void *addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- retval = (mask & *a) != 0;
- *a |= mask;
- local_irq_restore(flags);
-
- return retval;
-}
-
-static inline int test_and_clear_bit(int nr, volatile void *addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- retval = (mask & *a) != 0;
- *a &= ~mask;
- local_irq_restore(flags);
-
- return retval;
-}
-
-static inline int test_and_change_bit(int nr, volatile void *addr)
-{
- int mask, retval;
- volatile unsigned int *a = addr;
- unsigned long flags;
-
- a += nr >> 5;
- mask = 1 << (nr & 0x1f);
- local_irq_save(flags);
- retval = (mask & *a) != 0;
- *a ^= mask;
- local_irq_restore(flags);
-
- return retval;
-}
-
-#endif /* __ASM_SH_BITOPS_IRQ_H */
diff --git a/arch/sh/include/asm/bitops-llsc.h b/arch/sh/include/asm/bitops-llsc.h
index 43b8e1a8239e..1d2fc0b010ad 100644
--- a/arch/sh/include/asm/bitops-llsc.h
+++ b/arch/sh/include/asm/bitops-llsc.h
@@ -141,4 +141,6 @@ static inline int test_and_change_bit(int nr, volatile void * addr)
return retval != 0;
}
+#include <asm-generic/bitops/non-atomic.h>
+
#endif /* __ASM_SH_BITOPS_LLSC_H */
diff --git a/arch/sh/include/asm/bitops-op32.h b/arch/sh/include/asm/bitops-op32.h
new file mode 100644
index 000000000000..f0ae7e9218e0
--- /dev/null
+++ b/arch/sh/include/asm/bitops-op32.h
@@ -0,0 +1,142 @@
+#ifndef __ASM_SH_BITOPS_OP32_H
+#define __ASM_SH_BITOPS_OP32_H
+
+/*
+ * The bit modifying instructions on SH-2A are only capable of working
+ * with a 3-bit immediate, which signifies the shift position for the bit
+ * being worked on.
+ */
+#if defined(__BIG_ENDIAN)
+#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7)
+#define BYTE_NUMBER(nr) ((nr ^ BITOP_LE_SWIZZLE) / BITS_PER_BYTE)
+#define BYTE_OFFSET(nr) ((nr ^ BITOP_LE_SWIZZLE) % BITS_PER_BYTE)
+#else
+#define BYTE_NUMBER(nr) ((nr) / BITS_PER_BYTE)
+#define BYTE_OFFSET(nr) ((nr) % BITS_PER_BYTE)
+#endif
+
+#define IS_IMMEDIATE(nr) (__builtin_constant_p(nr))
+
+static inline void __set_bit(int nr, volatile unsigned long *addr)
+{
+ if (IS_IMMEDIATE(nr)) {
+ __asm__ __volatile__ (
+ "bset.b %1, @(%O2,%0) ! __set_bit\n\t"
+ : "+r" (addr)
+ : "i" (BYTE_OFFSET(nr)), "i" (BYTE_NUMBER(nr))
+ : "t", "memory"
+ );
+ } else {
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p |= mask;
+ }
+}
+
+static inline void __clear_bit(int nr, volatile unsigned long *addr)
+{
+ if (IS_IMMEDIATE(nr)) {
+ __asm__ __volatile__ (
+ "bclr.b %1, @(%O2,%0) ! __clear_bit\n\t"
+ : "+r" (addr)
+ : "i" (BYTE_OFFSET(nr)),
+ "i" (BYTE_NUMBER(nr))
+ : "t", "memory"
+ );
+ } else {
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p &= ~mask;
+ }
+}
+
+/**
+ * __change_bit - Toggle a bit in memory
+ * @nr: the bit to change
+ * @addr: the address to start counting from
+ *
+ * Unlike change_bit(), this function is non-atomic and may be reordered.
+ * If it's called on the same region of memory simultaneously, the effect
+ * may be that only one operation succeeds.
+ */
+static inline void __change_bit(int nr, volatile unsigned long *addr)
+{
+ if (IS_IMMEDIATE(nr)) {
+ __asm__ __volatile__ (
+ "bxor.b %1, @(%O2,%0) ! __change_bit\n\t"
+ : "+r" (addr)
+ : "i" (BYTE_OFFSET(nr)),
+ "i" (BYTE_NUMBER(nr))
+ : "t", "memory"
+ );
+ } else {
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p ^= mask;
+ }
+}
+
+/**
+ * __test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail. You must protect multiple accesses with a lock.
+ */
+static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+ unsigned long old = *p;
+
+ *p = old | mask;
+ return (old & mask) != 0;
+}
+
+/**
+ * __test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail. You must protect multiple accesses with a lock.
+ */
+static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+ unsigned long old = *p;
+
+ *p = old & ~mask;
+ return (old & mask) != 0;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static inline int __test_and_change_bit(int nr,
+ volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+ unsigned long old = *p;
+
+ *p = old ^ mask;
+ return (old & mask) != 0;
+}
+
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static inline int test_bit(int nr, const volatile unsigned long *addr)
+{
+ return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
+}
+
+#endif /* __ASM_SH_BITOPS_OP32_H */
diff --git a/arch/sh/include/asm/bitops.h b/arch/sh/include/asm/bitops.h
index 367930d8e5ae..ebe595b7ab1f 100644
--- a/arch/sh/include/asm/bitops.h
+++ b/arch/sh/include/asm/bitops.h
@@ -13,21 +13,22 @@
#ifdef CONFIG_GUSA_RB
#include <asm/bitops-grb.h>
+#elif defined(CONFIG_CPU_SH2A)
+#include <asm-generic/bitops/atomic.h>
+#include <asm/bitops-op32.h>
#elif defined(CONFIG_CPU_SH4A)
#include <asm/bitops-llsc.h>
#else
-#include <asm/bitops-irq.h>
+#include <asm-generic/bitops/atomic.h>
+#include <asm-generic/bitops/non-atomic.h>
#endif
-
/*
* clear_bit() doesn't provide any barrier for the compiler.
*/
#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
-#include <asm-generic/bitops/non-atomic.h>
-
#ifdef CONFIG_SUPERH32
static inline unsigned long ffz(unsigned long word)
{
diff --git a/arch/sh/include/asm/bugs.h b/arch/sh/include/asm/bugs.h
index 121b2ecddfc3..4924ff6f5439 100644
--- a/arch/sh/include/asm/bugs.h
+++ b/arch/sh/include/asm/bugs.h
@@ -25,7 +25,7 @@ static void __init check_bugs(void)
case CPU_SH7619:
*p++ = '2';
break;
- case CPU_SH7203 ... CPU_MXG:
+ case CPU_SH7201 ... CPU_MXG:
*p++ = '2';
*p++ = 'a';
break;
diff --git a/arch/sh/include/asm/elf.h b/arch/sh/include/asm/elf.h
index 9eb9036a1bdc..ccb1d93bb043 100644
--- a/arch/sh/include/asm/elf.h
+++ b/arch/sh/include/asm/elf.h
@@ -108,13 +108,11 @@ typedef struct user_fpu_struct elf_fpregset_t;
#define elf_check_fdpic(x) ((x)->e_flags & EF_SH_FDPIC)
#define elf_check_const_displacement(x) ((x)->e_flags & EF_SH_PIC)
-#ifdef CONFIG_SUPERH32
/*
* Enable dump using regset.
* This covers all of general/DSP/FPU regs.
*/
#define CORE_DUMP_USE_REGSET
-#endif
#define USE_ELF_CORE_DUMP
#define ELF_FDPIC_CORE_EFLAGS EF_SH_FDPIC
@@ -204,7 +202,7 @@ do { \
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
- int executable_stack);
+ int uses_interp);
extern unsigned int vdso_enabled;
extern void __kernel_vsyscall;
diff --git a/arch/sh/include/asm/ftrace.h b/arch/sh/include/asm/ftrace.h
index 3aed362c9463..8fea7d8c8258 100644
--- a/arch/sh/include/asm/ftrace.h
+++ b/arch/sh/include/asm/ftrace.h
@@ -1,8 +1,34 @@
#ifndef __ASM_SH_FTRACE_H
#define __ASM_SH_FTRACE_H
+#ifdef CONFIG_FUNCTION_TRACER
+
+#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
+
#ifndef __ASSEMBLY__
extern void mcount(void);
-#endif
+
+#define MCOUNT_ADDR ((long)(mcount))
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+#define CALLER_ADDR ((long)(ftrace_caller))
+#define STUB_ADDR ((long)(ftrace_stub))
+
+#define MCOUNT_INSN_OFFSET ((STUB_ADDR - CALLER_ADDR) >> 1)
+
+struct dyn_arch_ftrace {
+ /* No extra data needed on sh */
+};
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+static inline unsigned long ftrace_call_adjust(unsigned long addr)
+{
+ /* 'addr' is the memory table address. */
+ return addr;
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_FUNCTION_TRACER */
#endif /* __ASM_SH_FTRACE_H */
diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index 65eaae34e753..61f6dae40534 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -260,6 +260,10 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags)
return (void __iomem *)P2SEGADDR(offset);
}
+
+ /* P4 above the store queues are always mapped. */
+ if (unlikely(offset >= P3_ADDR_MAX))
+ return (void __iomem *)P4SEGADDR(offset);
#endif
return __ioremap(offset, size, flags);
diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h
index 24e42078f36f..72704ed725e5 100644
--- a/arch/sh/include/asm/kgdb.h
+++ b/arch/sh/include/asm/kgdb.h
@@ -1,21 +1,7 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Based on original code by Glenn Engel, Jim Kingdon,
- * David Grothe <dave@gcom.com>, Tigran Aivazian, <tigran@sco.com> and
- * Amit S. Kale <akale@veritas.com>
- *
- * Super-H port based on sh-stub.c (Ben Lee and Steve Chamberlain) by
- * Henry Bell <henry.bell@st.com>
- *
- * Header file for low-level support for remote debug using GDB.
- *
- */
-
-#ifndef __KGDB_H
-#define __KGDB_H
+#ifndef __ASM_SH_KGDB_H
+#define __ASM_SH_KGDB_H
+#include <asm/cacheflush.h>
#include <asm/ptrace.h>
/* Same as pt_regs but has vbr in place of syscall_nr */
@@ -30,40 +16,26 @@ struct kgdb_regs {
unsigned long vbr;
};
-/* State info */
-extern char kgdb_in_gdb_mode;
-extern int kgdb_nofault; /* Ignore bus errors (in gdb mem access) */
-extern char in_nmi; /* Debounce flag to prevent NMI reentry*/
+enum regnames {
+ GDB_R0, GDB_R1, GDB_R2, GDB_R3, GDB_R4, GDB_R5, GDB_R6, GDB_R7,
+ GDB_R8, GDB_R9, GDB_R10, GDB_R11, GDB_R12, GDB_R13, GDB_R14, GDB_R15,
-/* SCI */
-extern int kgdb_portnum;
-extern int kgdb_baud;
-extern char kgdb_parity;
-extern char kgdb_bits;
+ GDB_PC, GDB_PR, GDB_SR, GDB_GBR, GDB_MACH, GDB_MACL, GDB_VBR,
+};
-/* Init and interface stuff */
-extern int kgdb_init(void);
-extern int (*kgdb_getchar)(void);
-extern void (*kgdb_putchar)(int);
+#define NUMREGBYTES ((GDB_VBR + 1) * 4)
-/* Trap functions */
-typedef void (kgdb_debug_hook_t)(struct pt_regs *regs);
-typedef void (kgdb_bus_error_hook_t)(void);
-extern kgdb_debug_hook_t *kgdb_debug_hook;
-extern kgdb_bus_error_hook_t *kgdb_bus_err_hook;
+static inline void arch_kgdb_breakpoint(void)
+{
+ __asm__ __volatile__ ("trapa #0x3c\n");
+}
-/* Console */
-struct console;
-void kgdb_console_write(struct console *co, const char *s, unsigned count);
-extern int kgdb_console_setup(struct console *, char *);
+/* State info */
+extern char in_nmi; /* Debounce flag to prevent NMI reentry*/
-/* Prototypes for jmp fns */
-#define _JBLEN 9
-typedef int jmp_buf[_JBLEN];
-extern void longjmp(jmp_buf __jmpb, int __retval);
-extern int setjmp(jmp_buf __jmpb);
+#define BUFMAX 2048
-/* Forced breakpoint */
-#define breakpoint() __asm__ __volatile__("trapa #0x3c")
+#define CACHE_FLUSH_IS_SAFE 1
+#define BREAK_INSTR_SIZE 2
-#endif
+#endif /* __ASM_SH_KGDB_H */
diff --git a/arch/sh/include/asm/machvec.h b/arch/sh/include/asm/machvec.h
index f1bae02ef7b6..64b1c16a0f03 100644
--- a/arch/sh/include/asm/machvec.h
+++ b/arch/sh/include/asm/machvec.h
@@ -14,8 +14,6 @@
#include <linux/time.h>
#include <asm/machtypes.h>
-struct device;
-
struct sh_machine_vector {
void (*mv_setup)(char **cmdline_p);
const char *mv_name;
@@ -45,9 +43,6 @@ struct sh_machine_vector {
int (*mv_irq_demux)(int irq);
void (*mv_init_irq)(void);
- void (*mv_init_pci)(void);
-
- void (*mv_heartbeat)(void);
void __iomem *(*mv_ioport_map)(unsigned long port, unsigned int size);
void (*mv_ioport_unmap)(void __iomem *);
diff --git a/arch/sh/include/asm/mmu_context.h b/arch/sh/include/asm/mmu_context.h
index 04c0c9733ad6..5d9157bd474d 100644
--- a/arch/sh/include/asm/mmu_context.h
+++ b/arch/sh/include/asm/mmu_context.h
@@ -22,7 +22,7 @@
#define MMU_CONTEXT_ASID_MASK 0x000000ff
#define MMU_CONTEXT_VERSION_MASK 0xffffff00
#define MMU_CONTEXT_FIRST_VERSION 0x00000100
-#define NO_CONTEXT 0
+#define NO_CONTEXT 0UL
/* ASID is 8-bit value, so it can't be 0x100 */
#define MMU_NO_ASID 0x100
@@ -130,7 +130,7 @@ static inline void switch_mm(struct mm_struct *prev,
#define destroy_context(mm) do { } while (0)
#define set_asid(asid) do { } while (0)
#define get_asid() (0)
-#define cpu_asid(cpu, mm) ({ (void)cpu; 0; })
+#define cpu_asid(cpu, mm) ({ (void)cpu; NO_CONTEXT; })
#define switch_and_save_asid(asid) (0)
#define set_TTB(pgd) do { } while (0)
#define get_TTB() (0)
diff --git a/arch/sh/include/asm/mutex-llsc.h b/arch/sh/include/asm/mutex-llsc.h
new file mode 100644
index 000000000000..ee839ee58ac8
--- /dev/null
+++ b/arch/sh/include/asm/mutex-llsc.h
@@ -0,0 +1,112 @@
+/*
+ * arch/sh/include/asm/mutex-llsc.h
+ *
+ * SH-4A optimized mutex locking primitives
+ *
+ * Please look into asm-generic/mutex-xchg.h for a formal definition.
+ */
+#ifndef __ASM_SH_MUTEX_LLSC_H
+#define __ASM_SH_MUTEX_LLSC_H
+
+/*
+ * Attempting to lock a mutex on SH4A is done like in ARMv6+ architecure.
+ * with a bastardized atomic decrement (it is not a reliable atomic decrement
+ * but it satisfies the defined semantics for our purpose, while being
+ * smaller and faster than a real atomic decrement or atomic swap.
+ * The idea is to attempt decrementing the lock value only once. If once
+ * decremented it isn't zero, or if its store-back fails due to a dispute
+ * on the exclusive store, we simply bail out immediately through the slow
+ * path where the lock will be reattempted until it succeeds.
+ */
+static inline void
+__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+ int __ex_flag, __res;
+
+ __asm__ __volatile__ (
+ "movli.l @%2, %0 \n"
+ "add #-1, %0 \n"
+ "movco.l %0, @%2 \n"
+ "movt %1 \n"
+ : "=&z" (__res), "=&r" (__ex_flag)
+ : "r" (&(count)->counter)
+ : "t");
+
+ __res |= !__ex_flag;
+ if (unlikely(__res != 0))
+ fail_fn(count);
+}
+
+static inline int
+__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+ int __ex_flag, __res;
+
+ __asm__ __volatile__ (
+ "movli.l @%2, %0 \n"
+ "add #-1, %0 \n"
+ "movco.l %0, @%2 \n"
+ "movt %1 \n"
+ : "=&z" (__res), "=&r" (__ex_flag)
+ : "r" (&(count)->counter)
+ : "t");
+
+ __res |= !__ex_flag;
+ if (unlikely(__res != 0))
+ __res = fail_fn(count);
+
+ return __res;
+}
+
+static inline void
+__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
+{
+ int __ex_flag, __res;
+
+ __asm__ __volatile__ (
+ "movli.l @%2, %0 \n\t"
+ "add #1, %0 \n\t"
+ "movco.l %0, @%2 \n\t"
+ "movt %1 \n\t"
+ : "=&z" (__res), "=&r" (__ex_flag)
+ : "r" (&(count)->counter)
+ : "t");
+
+ __res |= !__ex_flag;
+ if (unlikely(__res <= 0))
+ fail_fn(count);
+}
+
+/*
+ * If the unlock was done on a contended lock, or if the unlock simply fails
+ * then the mutex remains locked.
+ */
+#define __mutex_slowpath_needs_to_unlock() 1
+
+/*
+ * For __mutex_fastpath_trylock we do an atomic decrement and check the
+ * result and put it in the __res variable.
+ */
+static inline int
+__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
+{
+ int __res, __orig;
+
+ __asm__ __volatile__ (
+ "1: movli.l @%2, %0 \n\t"
+ "dt %0 \n\t"
+ "movco.l %0,@%2 \n\t"
+ "bf 1b \n\t"
+ "cmp/eq #0,%0 \n\t"
+ "bt 2f \n\t"
+ "mov #0, %1 \n\t"
+ "bf 3f \n\t"
+ "2: mov #1, %1 \n\t"
+ "3: "
+ : "=&z" (__orig), "=&r" (__res)
+ : "r" (&count->counter)
+ : "t");
+
+ return __res;
+}
+#endif /* __ASM_SH_MUTEX_LLSC_H */
diff --git a/arch/sh/include/asm/mutex.h b/arch/sh/include/asm/mutex.h
index 458c1f7fbc18..d8e37716a4a0 100644
--- a/arch/sh/include/asm/mutex.h
+++ b/arch/sh/include/asm/mutex.h
@@ -5,5 +5,8 @@
* implementation in place, or pick the atomic_xchg() based generic
* implementation. (see asm-generic/mutex-xchg.h for details)
*/
-
+#if defined(CONFIG_CPU_SH4A)
+#include <asm/mutex-llsc.h>
+#else
#include <asm-generic/mutex-dec.h>
+#endif
diff --git a/arch/sh/include/asm/pm.h b/arch/sh/include/asm/pm.h
deleted file mode 100644
index 56fdbd6b1c94..000000000000
--- a/arch/sh/include/asm/pm.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com>
- *
- */
-#ifndef __ASM_SH_PM_H
-#define __ASM_SH_PM_H
-
-extern u8 wakeup_start;
-extern u8 wakeup_end;
-
-void pm_enter(void);
-
-#endif
diff --git a/arch/sh/include/asm/processor.h b/arch/sh/include/asm/processor.h
index 693364a20ad7..1ef4b24d7619 100644
--- a/arch/sh/include/asm/processor.h
+++ b/arch/sh/include/asm/processor.h
@@ -18,7 +18,7 @@ enum cpu_type {
CPU_SH7619,
/* SH-2A types */
- CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_MXG,
+ CPU_SH7201, CPU_SH7203, CPU_SH7206, CPU_SH7263, CPU_MXG,
/* SH-3 types */
CPU_SH7705, CPU_SH7706, CPU_SH7707,
@@ -82,6 +82,9 @@ extern struct sh_cpuinfo cpu_data[];
#define current_cpu_data cpu_data[smp_processor_id()]
#define raw_current_cpu_data cpu_data[raw_smp_processor_id()]
+#define cpu_sleep() __asm__ __volatile__ ("sleep" : : : "memory")
+#define cpu_relax() barrier()
+
/* Forward decl */
struct seq_operations;
diff --git a/arch/sh/include/asm/processor_32.h b/arch/sh/include/asm/processor_32.h
index a46a0207e977..d79063c5eb9c 100644
--- a/arch/sh/include/asm/processor_32.h
+++ b/arch/sh/include/asm/processor_32.h
@@ -175,6 +175,15 @@ static __inline__ void enable_fpu(void)
void show_trace(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs);
+
+#ifdef CONFIG_DUMP_CODE
+void show_code(struct pt_regs *regs);
+#else
+static inline void show_code(struct pt_regs *regs)
+{
+}
+#endif
+
extern unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc)
@@ -182,9 +191,6 @@ extern unsigned long get_wchan(struct task_struct *p);
#define user_stack_pointer(regs) ((regs)->regs[15])
-#define cpu_sleep() __asm__ __volatile__ ("sleep" : : : "memory")
-#define cpu_relax() barrier()
-
#if defined(CONFIG_CPU_SH2A) || defined(CONFIG_CPU_SH3) || \
defined(CONFIG_CPU_SH4)
#define PREFETCH_STRIDE L1_CACHE_BYTES
diff --git a/arch/sh/include/asm/processor_64.h b/arch/sh/include/asm/processor_64.h
index b0b4824dfc4c..803177fcf086 100644
--- a/arch/sh/include/asm/processor_64.h
+++ b/arch/sh/include/asm/processor_64.h
@@ -226,9 +226,7 @@ extern unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) ((tsk)->thread.pc)
#define KSTK_ESP(tsk) ((tsk)->thread.sp)
-#define user_stack_pointer(regs) ((regs)->sp)
-
-#define cpu_relax() barrier()
+#define user_stack_pointer(regs) ((regs)->regs[15])
#endif /* __ASSEMBLY__ */
#endif /* __ASM_SH_PROCESSOR_64_H */
diff --git a/arch/sh/include/asm/ptrace.h b/arch/sh/include/asm/ptrace.h
index 3ad18e91bca6..12912ab80c15 100644
--- a/arch/sh/include/asm/ptrace.h
+++ b/arch/sh/include/asm/ptrace.h
@@ -86,6 +86,7 @@ struct pt_dspregs {
unsigned long re;
unsigned long mod;
};
+#endif
#define PTRACE_GETREGS 12 /* General registers */
#define PTRACE_SETREGS 13
@@ -100,7 +101,6 @@ struct pt_dspregs {
#define PTRACE_GETDSPREGS 55 /* DSP registers */
#define PTRACE_SETDSPREGS 56
-#endif
#ifdef __KERNEL__
#include <asm/addrspace.h>
diff --git a/arch/sh/include/asm/sh_bios.h b/arch/sh/include/asm/sh_bios.h
index 0ca261956e3d..d9c96d7cf6c7 100644
--- a/arch/sh/include/asm/sh_bios.h
+++ b/arch/sh/include/asm/sh_bios.h
@@ -10,7 +10,6 @@
extern void sh_bios_console_write(const char *buf, unsigned int len);
extern void sh_bios_char_out(char ch);
-extern int sh_bios_in_gdb_mode(void);
extern void sh_bios_gdb_detach(void);
extern void sh_bios_get_node_addr(unsigned char *node_addr);
diff --git a/arch/sh/include/asm/string_64.h b/arch/sh/include/asm/string_64.h
index aa1fef229c78..742007172624 100644
--- a/arch/sh/include/asm/string_64.h
+++ b/arch/sh/include/asm/string_64.h
@@ -1,17 +1,20 @@
#ifndef __ASM_SH_STRING_64_H
#define __ASM_SH_STRING_64_H
-/*
- * include/asm-sh/string_64.h
- *
- * Copyright (C) 2000, 2001 Paolo Alberelli
- *
- * 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.
- */
+#ifdef __KERNEL__
+
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *__s, int __c, size_t __count);
#define __HAVE_ARCH_MEMCPY
extern void *memcpy(void *dest, const void *src, size_t count);
+#define __HAVE_ARCH_STRLEN
+extern size_t strlen(const char *);
+
+#define __HAVE_ARCH_STRCPY
+extern char *strcpy(char *__dest, const char *__src);
+
+#endif /* __KERNEL__ */
+
#endif /* __ASM_SH_STRING_64_H */
diff --git a/arch/sh/include/asm/syscall_32.h b/arch/sh/include/asm/syscall_32.h
index 54773f26cd44..05a868a71ef5 100644
--- a/arch/sh/include/asm/syscall_32.h
+++ b/arch/sh/include/asm/syscall_32.h
@@ -5,7 +5,7 @@
#include <linux/sched.h>
#include <asm/ptrace.h>
-/* The system call number is given by the user in %g1 */
+/* The system call number is given by the user in R3 */
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/sh/include/asm/syscall_64.h b/arch/sh/include/asm/syscall_64.h
index bcaaa8ca4d70..e1143b9784d6 100644
--- a/arch/sh/include/asm/syscall_64.h
+++ b/arch/sh/include/asm/syscall_64.h
@@ -1,6 +1,80 @@
#ifndef __ASM_SH_SYSCALL_64_H
#define __ASM_SH_SYSCALL_64_H
-#include <asm-generic/syscall.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+
+/* The system call number is given by the user in R9 */
+static inline long syscall_get_nr(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return (regs->syscall_nr >= 0) ? regs->regs[9] : -1L;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ /*
+ * XXX: This needs some thought. On SH we don't
+ * save away the original R9 value anywhere.
+ */
+}
+
+static inline bool syscall_has_error(struct pt_regs *regs)
+{
+ return (regs->sr & 0x1) ? true : false;
+}
+static inline void syscall_set_error(struct pt_regs *regs)
+{
+ regs->sr |= 0x1;
+}
+static inline void syscall_clear_error(struct pt_regs *regs)
+{
+ regs->sr &= ~0x1;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return syscall_has_error(regs) ? regs->regs[9] : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->regs[9];
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs,
+ int error, long val)
+{
+ if (error) {
+ syscall_set_error(regs);
+ regs->regs[9] = -error;
+ } else {
+ syscall_clear_error(regs);
+ regs->regs[9] = val;
+ }
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+ BUG_ON(i + n > 6);
+ memcpy(args, &regs->regs[2 + 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)
+{
+ BUG_ON(i + n > 6);
+ memcpy(&regs->regs[2 + i], args, n * sizeof(args[0]));
+}
#endif /* __ASM_SH_SYSCALL_64_H */
diff --git a/arch/sh/include/asm/system.h b/arch/sh/include/asm/system.h
index 6160fe445161..c9ec6af8e745 100644
--- a/arch/sh/include/asm/system.h
+++ b/arch/sh/include/asm/system.h
@@ -175,6 +175,8 @@ asmlinkage void name##_trap_handler(unsigned int vec, struct pt_regs *regs)
BUILD_TRAP_HANDLER(address_error);
BUILD_TRAP_HANDLER(debug);
BUILD_TRAP_HANDLER(bug);
+BUILD_TRAP_HANDLER(breakpoint);
+BUILD_TRAP_HANDLER(singlestep);
BUILD_TRAP_HANDLER(fpu_error);
BUILD_TRAP_HANDLER(fpu_state_restore);
diff --git a/arch/sh/include/asm/topology.h b/arch/sh/include/asm/topology.h
index 279d9cc4a007..066f0fba590e 100644
--- a/arch/sh/include/asm/topology.h
+++ b/arch/sh/include/asm/topology.h
@@ -32,6 +32,7 @@
#define parent_node(node) ((void)(node),0)
#define node_to_cpumask(node) ((void)node, cpu_online_map)
+#define cpumask_of_node(node) ((void)node, cpu_online_mask)
#define node_to_first_cpu(node) ((void)(node),0)
#define pcibus_to_node(bus) ((void)(bus), -1)
diff --git a/arch/sh/include/asm/unaligned-sh4a.h b/arch/sh/include/asm/unaligned-sh4a.h
new file mode 100644
index 000000000000..d8f89770275b
--- /dev/null
+++ b/arch/sh/include/asm/unaligned-sh4a.h
@@ -0,0 +1,258 @@
+#ifndef __ASM_SH_UNALIGNED_SH4A_H
+#define __ASM_SH_UNALIGNED_SH4A_H
+
+/*
+ * SH-4A has support for unaligned 32-bit loads, and 32-bit loads only.
+ * Support for 16 and 64-bit accesses are done through shifting and
+ * masking relative to the endianness. Unaligned stores are not supported
+ * by the instruction encoding, so these continue to use the packed
+ * struct.
+ *
+ * The same note as with the movli.l/movco.l pair applies here, as long
+ * as the load is gauranteed to be inlined, nothing else will hook in to
+ * r0 and we get the return value for free.
+ *
+ * NOTE: Due to the fact we require r0 encoding, care should be taken to
+ * avoid mixing these heavily with other r0 consumers, such as the atomic
+ * ops. Failure to adhere to this can result in the compiler running out
+ * of spill registers and blowing up when building at low optimization
+ * levels. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34777.
+ */
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+static __always_inline u32 __get_unaligned_cpu32(const u8 *p)
+{
+ unsigned long unaligned;
+
+ __asm__ __volatile__ (
+ "movua.l @%1, %0\n\t"
+ : "=z" (unaligned)
+ : "r" (p)
+ );
+
+ return unaligned;
+}
+
+struct __una_u16 { u16 x __attribute__((packed)); };
+struct __una_u32 { u32 x __attribute__((packed)); };
+struct __una_u64 { u64 x __attribute__((packed)); };
+
+static inline u16 __get_unaligned_cpu16(const u8 *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return __get_unaligned_cpu32(p) & 0xffff;
+#else
+ return __get_unaligned_cpu32(p) >> 16;
+#endif
+}
+
+/*
+ * Even though movua.l supports auto-increment on the read side, it can
+ * only store to r0 due to instruction encoding constraints, so just let
+ * the compiler sort it out on its own.
+ */
+static inline u64 __get_unaligned_cpu64(const u8 *p)
+{
+#ifdef __LITTLE_ENDIAN
+ return (u64)__get_unaligned_cpu32(p + 4) << 32 |
+ __get_unaligned_cpu32(p);
+#else
+ return (u64)__get_unaligned_cpu32(p) << 32 |
+ __get_unaligned_cpu32(p + 4);
+#endif
+}
+
+static inline u16 get_unaligned_le16(const void *p)
+{
+ return le16_to_cpu(__get_unaligned_cpu16(p));
+}
+
+static inline u32 get_unaligned_le32(const void *p)
+{
+ return le32_to_cpu(__get_unaligned_cpu32(p));
+}
+
+static inline u64 get_unaligned_le64(const void *p)
+{
+ return le64_to_cpu(__get_unaligned_cpu64(p));
+}
+
+static inline u16 get_unaligned_be16(const void *p)
+{
+ return be16_to_cpu(__get_unaligned_cpu16(p));
+}
+
+static inline u32 get_unaligned_be32(const void *p)
+{
+ return be32_to_cpu(__get_unaligned_cpu32(p));
+}
+
+static inline u64 get_unaligned_be64(const void *p)
+{
+ return be64_to_cpu(__get_unaligned_cpu64(p));
+}
+
+static inline void __put_le16_noalign(u8 *p, u16 val)
+{
+ *p++ = val;
+ *p++ = val >> 8;
+}
+
+static inline void __put_le32_noalign(u8 *p, u32 val)
+{
+ __put_le16_noalign(p, val);
+ __put_le16_noalign(p + 2, val >> 16);
+}
+
+static inline void __put_le64_noalign(u8 *p, u64 val)
+{
+ __put_le32_noalign(p, val);
+ __put_le32_noalign(p + 4, val >> 32);
+}
+
+static inline void __put_be16_noalign(u8 *p, u16 val)
+{
+ *p++ = val >> 8;
+ *p++ = val;
+}
+
+static inline void __put_be32_noalign(u8 *p, u32 val)
+{
+ __put_be16_noalign(p, val >> 16);
+ __put_be16_noalign(p + 2, val);
+}
+
+static inline void __put_be64_noalign(u8 *p, u64 val)
+{
+ __put_be32_noalign(p, val >> 32);
+ __put_be32_noalign(p + 4, val);
+}
+
+static inline void put_unaligned_le16(u16 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+ ((struct __una_u16 *)p)->x = val;
+#else
+ __put_le16_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_le32(u32 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+ ((struct __una_u32 *)p)->x = val;
+#else
+ __put_le32_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_le64(u64 val, void *p)
+{
+#ifdef __LITTLE_ENDIAN
+ ((struct __una_u64 *)p)->x = val;
+#else
+ __put_le64_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be16(u16 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+ ((struct __una_u16 *)p)->x = val;
+#else
+ __put_be16_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be32(u32 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+ ((struct __una_u32 *)p)->x = val;
+#else
+ __put_be32_noalign(p, val);
+#endif
+}
+
+static inline void put_unaligned_be64(u64 val, void *p)
+{
+#ifdef __BIG_ENDIAN
+ ((struct __una_u64 *)p)->x = val;
+#else
+ __put_be64_noalign(p, val);
+#endif
+}
+
+/*
+ * Cause a link-time error if we try an unaligned access other than
+ * 1,2,4 or 8 bytes long
+ */
+extern void __bad_unaligned_access_size(void);
+
+#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \
+ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \
+ __bad_unaligned_access_size())))); \
+ }))
+
+#define __get_unaligned_be(ptr) ((__force typeof(*(ptr)))({ \
+ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_be16((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_be32((ptr)), \
+ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_be64((ptr)), \
+ __bad_unaligned_access_size())))); \
+ }))
+
+#define __put_unaligned_le(val, ptr) ({ \
+ void *__gu_p = (ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ *(u8 *)__gu_p = (__force u8)(val); \
+ break; \
+ case 2: \
+ put_unaligned_le16((__force u16)(val), __gu_p); \
+ break; \
+ case 4: \
+ put_unaligned_le32((__force u32)(val), __gu_p); \
+ break; \
+ case 8: \
+ put_unaligned_le64((__force u64)(val), __gu_p); \
+ break; \
+ default: \
+ __bad_unaligned_access_size(); \
+ break; \
+ } \
+ (void)0; })
+
+#define __put_unaligned_be(val, ptr) ({ \
+ void *__gu_p = (ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ *(u8 *)__gu_p = (__force u8)(val); \
+ break; \
+ case 2: \
+ put_unaligned_be16((__force u16)(val), __gu_p); \
+ break; \
+ case 4: \
+ put_unaligned_be32((__force u32)(val), __gu_p); \
+ break; \
+ case 8: \
+ put_unaligned_be64((__force u64)(val), __gu_p); \
+ break; \
+ default: \
+ __bad_unaligned_access_size(); \
+ break; \
+ } \
+ (void)0; })
+
+#ifdef __LITTLE_ENDIAN
+# define get_unaligned __get_unaligned_le
+# define put_unaligned __put_unaligned_le
+#else
+# define get_unaligned __get_unaligned_be
+# define put_unaligned __put_unaligned_be
+#endif
+
+#endif /* __ASM_SH_UNALIGNED_SH4A_H */
diff --git a/arch/sh/include/asm/unaligned.h b/arch/sh/include/asm/unaligned.h
index c1641a01d50f..8c0ad5e4487a 100644
--- a/arch/sh/include/asm/unaligned.h
+++ b/arch/sh/include/asm/unaligned.h
@@ -1,7 +1,11 @@
#ifndef _ASM_SH_UNALIGNED_H
#define _ASM_SH_UNALIGNED_H
-/* SH can't handle unaligned accesses. */
+#ifdef CONFIG_CPU_SH4A
+/* SH-4A can handle unaligned loads in a relatively neutered fashion. */
+#include <asm/unaligned-sh4a.h>
+#else
+/* Otherwise, SH can't handle unaligned accesses. */
#ifdef __LITTLE_ENDIAN__
# include <linux/unaligned/le_struct.h>
# include <linux/unaligned/be_byteshift.h>
@@ -15,5 +19,6 @@
# define get_unaligned __get_unaligned_be
# define put_unaligned __put_unaligned_be
#endif
+#endif
#endif /* _ASM_SH_UNALIGNED_H */
diff --git a/arch/sh/include/cpu-sh3/cpu/gpio.h b/arch/sh/include/cpu-sh3/cpu/gpio.h
index 4e53eb314b8f..9a22b882f3dc 100644
--- a/arch/sh/include/cpu-sh3/cpu/gpio.h
+++ b/arch/sh/include/cpu-sh3/cpu/gpio.h
@@ -62,6 +62,20 @@
#define PORT_PSELC 0xA4050128UL
#define PORT_PSELD 0xA405012AUL
+#elif defined(CONFIG_CPU_SUBTYPE_SH7709)
+
+/* Control registers */
+#define PORT_PACR 0xa4000100UL
+#define PORT_PBCR 0xa4000102UL
+#define PORT_PCCR 0xa4000104UL
+#define PORT_PFCR 0xa400010aUL
+
+/* Data registers */
+#define PORT_PADR 0xa4000120UL
+#define PORT_PBDR 0xa4000122UL
+#define PORT_PCDR 0xa4000124UL
+#define PORT_PFDR 0xa400012aUL
+
#endif
#endif
diff --git a/arch/sh/include/mach-common/mach/edosk7705.h b/arch/sh/include/mach-common/mach/edosk7705.h
index 5bdc9d9be3de..efc43b323466 100644
--- a/arch/sh/include/mach-common/mach/edosk7705.h
+++ b/arch/sh/include/mach-common/mach/edosk7705.h
@@ -1,30 +1,7 @@
-/*
- * include/asm-sh/edosk7705.h
- *
- * Modified version of io_se.h for the EDOSK7705 specific functions.
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * IO functions for an Hitachi EDOSK7705 development board
- */
-
-#ifndef __ASM_SH_EDOSK7705_IO_H
-#define __ASM_SH_EDOSK7705_IO_H
+#ifndef __ASM_SH_EDOSK7705_H
+#define __ASM_SH_EDOSK7705_H
+#define __IO_PREFIX sh_edosk7705
#include <asm/io_generic.h>
-extern unsigned char sh_edosk7705_inb(unsigned long port);
-extern unsigned int sh_edosk7705_inl(unsigned long port);
-
-extern void sh_edosk7705_outb(unsigned char value, unsigned long port);
-extern void sh_edosk7705_outl(unsigned int value, unsigned long port);
-
-extern void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count);
-extern void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count);
-extern void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count);
-extern void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count);
-
-extern unsigned long sh_edosk7705_isa_port2addr(unsigned long offset);
-
-#endif /* __ASM_SH_EDOSK7705_IO_H */
+#endif /* __ASM_SH_EDOSK7705_H */
diff --git a/arch/sh/include/mach-se/mach/mrshpc.h b/arch/sh/include/mach-se/mach/mrshpc.h
new file mode 100644
index 000000000000..56287ee8563a
--- /dev/null
+++ b/arch/sh/include/mach-se/mach/mrshpc.h
@@ -0,0 +1,52 @@
+#ifndef __MACH_SE_MRSHPC_H
+#define __MACH_SE_MRSHPC_H
+
+#include <linux/io.h>
+
+static inline void __init mrshpc_setup_windows(void)
+{
+ if ((__raw_readw(MRSHPC_CSR) & 0x000c) != 0)
+ return; /* Not detected */
+
+ if ((__raw_readw(MRSHPC_CSR) & 0x0080) == 0) {
+ __raw_writew(0x0674, MRSHPC_CPWCR); /* Card Vcc is 3.3v? */
+ } else {
+ __raw_writew(0x0678, MRSHPC_CPWCR); /* Card Vcc is 5V */
+ }
+
+ /*
+ * PC-Card window open
+ * flag == COMMON/ATTRIBUTE/IO
+ */
+ /* common window open */
+ __raw_writew(0x8a84, MRSHPC_MW0CR1);
+ if((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+ /* common mode & bus width 16bit SWAP = 1*/
+ __raw_writew(0x0b00, MRSHPC_MW0CR2);
+ else
+ /* common mode & bus width 16bit SWAP = 0*/
+ __raw_writew(0x0300, MRSHPC_MW0CR2);
+
+ /* attribute window open */
+ __raw_writew(0x8a85, MRSHPC_MW1CR1);
+ if ((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+ /* attribute mode & bus width 16bit SWAP = 1*/
+ __raw_writew(0x0a00, MRSHPC_MW1CR2);
+ else
+ /* attribute mode & bus width 16bit SWAP = 0*/
+ __raw_writew(0x0200, MRSHPC_MW1CR2);
+
+ /* I/O window open */
+ __raw_writew(0x8a86, MRSHPC_IOWCR1);
+ __raw_writew(0x0008, MRSHPC_CDCR); /* I/O card mode */
+ if ((__raw_readw(MRSHPC_CSR) & 0x4000) != 0)
+ __raw_writew(0x0a00, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1*/
+ else
+ __raw_writew(0x0200, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0*/
+
+ __raw_writew(0x2000, MRSHPC_ICR);
+ __raw_writeb(0x00, PA_MRSHPC_MW2 + 0x206);
+ __raw_writeb(0x42, PA_MRSHPC_MW2 + 0x200);
+}
+
+#endif /* __MACH_SE_MRSHPC_H */
diff --git a/arch/sh/include/mach-se/mach/se.h b/arch/sh/include/mach-se/mach/se.h
index eb23000e1bbe..14be91c5a2f0 100644
--- a/arch/sh/include/mach-se/mach/se.h
+++ b/arch/sh/include/mach-se/mach/se.h
@@ -68,6 +68,24 @@
#define BCR_ILCRF (PA_BCR + 10)
#define BCR_ILCRG (PA_BCR + 12)
+#if defined(CONFIG_CPU_SUBTYPE_SH7709)
+#define INTC_IRR0 0xa4000004UL
+#define INTC_IRR1 0xa4000006UL
+#define INTC_IRR2 0xa4000008UL
+
+#define INTC_ICR0 0xfffffee0UL
+#define INTC_ICR1 0xa4000010UL
+#define INTC_ICR2 0xa4000012UL
+#define INTC_INTER 0xa4000014UL
+
+#define INTC_IPRC 0xa4000016UL
+#define INTC_IPRD 0xa4000018UL
+#define INTC_IPRE 0xa400001aUL
+
+#define IRQ0_IRQ 32
+#define IRQ1_IRQ 33
+#endif
+
#if defined(CONFIG_CPU_SUBTYPE_SH7705)
#define IRQ_STNIC 12
#define IRQ_CFCARD 14
diff --git a/arch/sh/include/mach-se/mach/se7343.h b/arch/sh/include/mach-se/mach/se7343.h
index 98458460e632..749914b400fb 100644
--- a/arch/sh/include/mach-se/mach/se7343.h
+++ b/arch/sh/include/mach-se/mach/se7343.h
@@ -118,9 +118,6 @@
#define FPGA_IN 0xb1400000
#define FPGA_OUT 0xb1400002
-#define __IO_PREFIX sh7343se
-#include <asm/io_generic.h>
-
#define IRQ0_IRQ 32
#define IRQ1_IRQ 33
#define IRQ4_IRQ 36
@@ -132,8 +129,10 @@
#define SE7343_FPGA_IRQ_MRSHPC3 3
#define SE7343_FPGA_IRQ_SMC 6 /* EXT_IRQ2 */
#define SE7343_FPGA_IRQ_USB 8
+#define SE7343_FPGA_IRQ_UARTA 10
+#define SE7343_FPGA_IRQ_UARTB 11
-#define SE7343_FPGA_IRQ_NR 11
+#define SE7343_FPGA_IRQ_NR 12
#define SE7343_FPGA_IRQ_BASE 120
#define MRSHPC_IRQ3 (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_MRSHPC3)
@@ -142,6 +141,8 @@
#define MRSHPC_IRQ0 (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_MRSHPC0)
#define SMC_IRQ (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_SMC)
#define USB_IRQ (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_USB)
+#define UARTA_IRQ (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_UARTA)
+#define UARTB_IRQ (SE7343_FPGA_IRQ_BASE + SE7343_FPGA_IRQ_UARTB)
/* arch/sh/boards/se/7343/irq.c */
void init_7343se_IRQ(void);
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32
index 48edfb145fb4..2e1b86e16ab5 100644
--- a/arch/sh/kernel/Makefile_32
+++ b/arch/sh/kernel/Makefile_32
@@ -4,25 +4,31 @@
extra-y := head_32.o init_task.o vmlinux.lds
-obj-y := debugtraps.o io.o io_generic.o irq.o machvec.o process_32.o \
- ptrace_32.o setup.o signal_32.o sys_sh.o sys_sh32.o \
- syscalls_32.o time_32.o topology.o traps.o traps_32.o
+ifdef CONFIG_FUNCTION_TRACER
+# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+obj-y := debugtraps.o idle.o io.o io_generic.o irq.o \
+ machvec.o process_32.o ptrace_32.o setup.o signal_32.o \
+ sys_sh.o sys_sh32.o syscalls_32.o time_32.o topology.o \
+ traps.o traps_32.o
obj-y += cpu/ timers/
obj-$(CONFIG_VSYSCALL) += vsyscall/
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_CF_ENABLER) += cf-enabler.o
obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
-obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
+obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_MODULES) += sh_ksyms_32.o module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
-obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_IO_TRAPPED) += io_trapped.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_DUMP_CODE) += disassemble.o
EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/kernel/Makefile_64 b/arch/sh/kernel/Makefile_64
index c97660b2b48d..fe425d7f6871 100644
--- a/arch/sh/kernel/Makefile_64
+++ b/arch/sh/kernel/Makefile_64
@@ -1,21 +1,18 @@
extra-y := head_64.o init_task.o vmlinux.lds
-obj-y := debugtraps.o io.o io_generic.o irq.o machvec.o process_64.o \
+obj-y := debugtraps.o idle.o io.o io_generic.o irq.o machvec.o process_64.o \
ptrace_64.o setup.o signal_64.o sys_sh.o sys_sh64.o \
syscalls_64.o time_64.o topology.o traps.o traps_64.o
obj-y += cpu/ timers/
obj-$(CONFIG_VSYSCALL) += vsyscall/
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_CF_ENABLER) += cf-enabler.o
obj-$(CONFIG_SH_STANDARD_BIOS) += sh_bios.o
-obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o
obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
obj-$(CONFIG_MODULES) += sh_ksyms_64.o module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
-obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_IO_TRAPPED) += io_trapped.o
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
diff --git a/arch/sh/kernel/cf-enabler.c b/arch/sh/kernel/cf-enabler.c
deleted file mode 100644
index bea40339919b..000000000000
--- a/arch/sh/kernel/cf-enabler.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/* $Id: cf-enabler.c,v 1.4 2004/02/22 22:44:36 kkojima Exp $
- *
- * linux/drivers/block/cf-enabler.c
- *
- * Copyright (C) 1999 Niibe Yutaka
- * Copyright (C) 2000 Toshiharu Nozawa
- * Copyright (C) 2001 A&D Co., Ltd.
- *
- * Enable the CF configuration.
- */
-
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-/*
- * You can connect Compact Flash directly to the bus of SuperH.
- * This is the enabler for that.
- *
- * SIM: How generic is this really? It looks pretty board, or at
- * least SH sub-type, specific to me.
- * I know it doesn't work on the Overdrive!
- */
-
-/*
- * 0xB8000000 : Attribute
- * 0xB8001000 : Common Memory
- * 0xBA000000 : I/O
- */
-#if defined(CONFIG_CPU_SH4)
-/* SH4 can't access PCMCIA interface through P2 area.
- * we must remap it with appropriate attribute bit of the page set.
- * this part is based on Greg Banks' hd64465_ss.c implementation - Masahiro Abe */
-
-#if defined(CONFIG_CF_AREA6)
-#define slot_no 0
-#else
-#define slot_no 1
-#endif
-
-/* use this pointer to access to directly connected compact flash io area*/
-void *cf_io_base;
-
-static int __init allocate_cf_area(void)
-{
- pgprot_t prot;
- unsigned long paddrbase, psize;
-
- /* open I/O area window */
- paddrbase = virt_to_phys((void*)CONFIG_CF_BASE_ADDR);
- psize = PAGE_SIZE;
- prot = PAGE_KERNEL_PCC(slot_no, _PAGE_PCC_IO16);
- cf_io_base = p3_ioremap(paddrbase, psize, prot.pgprot);
- if (!cf_io_base) {
- printk("allocate_cf_area : can't open CF I/O window!\n");
- return -ENOMEM;
- }
-/* printk("p3_ioremap(paddr=0x%08lx, psize=0x%08lx, prot=0x%08lx)=0x%08lx\n",
- paddrbase, psize, prot.pgprot, cf_io_base);*/
-
- /* XXX : do we need attribute and common-memory area also? */
-
- return 0;
-}
-#endif
-
-static int __init cf_init_default(void)
-{
-/* You must have enabled the card, and set the level interrupt
- * before reaching this point. Possibly in boot ROM or boot loader.
- */
-#if defined(CONFIG_CPU_SH4)
- allocate_cf_area();
-#endif
-
- return 0;
-}
-
-#if defined(CONFIG_SH_SOLUTION_ENGINE)
-#include <mach-se/mach/se.h>
-#elif defined(CONFIG_SH_7722_SOLUTION_ENGINE)
-#include <mach-se/mach/se7722.h>
-#elif defined(CONFIG_SH_7721_SOLUTION_ENGINE)
-#include <mach-se/mach/se7721.h>
-#endif
-
-/*
- * SolutionEngine Seriese
- *
- * about MS770xSE
- * 0xB8400000 : Common Memory
- * 0xB8500000 : Attribute
- * 0xB8600000 : I/O
- *
- * about MS7722SE
- * 0xB0400000 : Common Memory
- * 0xB0500000 : Attribute
- * 0xB0600000 : I/O
- */
-
-#if defined(CONFIG_SH_SOLUTION_ENGINE) || \
- defined(CONFIG_SH_7722_SOLUTION_ENGINE) || \
- defined(CONFIG_SH_7721_SOLUTION_ENGINE)
-static int __init cf_init_se(void)
-{
- if ((ctrl_inw(MRSHPC_CSR) & 0x000c) != 0)
- return 0; /* Not detected */
-
- if ((ctrl_inw(MRSHPC_CSR) & 0x0080) == 0) {
- ctrl_outw(0x0674, MRSHPC_CPWCR); /* Card Vcc is 3.3v? */
- } else {
- ctrl_outw(0x0678, MRSHPC_CPWCR); /* Card Vcc is 5V */
- }
-
- /*
- * PC-Card window open
- * flag == COMMON/ATTRIBUTE/IO
- */
- /* common window open */
- ctrl_outw(0x8a84, MRSHPC_MW0CR1);
- if((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
- /* common mode & bus width 16bit SWAP = 1*/
- ctrl_outw(0x0b00, MRSHPC_MW0CR2);
- else
- /* common mode & bus width 16bit SWAP = 0*/
- ctrl_outw(0x0300, MRSHPC_MW0CR2);
-
- /* attribute window open */
- ctrl_outw(0x8a85, MRSHPC_MW1CR1);
- if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
- /* attribute mode & bus width 16bit SWAP = 1*/
- ctrl_outw(0x0a00, MRSHPC_MW1CR2);
- else
- /* attribute mode & bus width 16bit SWAP = 0*/
- ctrl_outw(0x0200, MRSHPC_MW1CR2);
-
- /* I/O window open */
- ctrl_outw(0x8a86, MRSHPC_IOWCR1);
- ctrl_outw(0x0008, MRSHPC_CDCR); /* I/O card mode */
- if ((ctrl_inw(MRSHPC_CSR) & 0x4000) != 0)
- ctrl_outw(0x0a00, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 1*/
- else
- ctrl_outw(0x0200, MRSHPC_IOWCR2); /* bus width 16bit SWAP = 0*/
-
- ctrl_outw(0x2000, MRSHPC_ICR);
- ctrl_outb(0x00, PA_MRSHPC_MW2 + 0x206);
- ctrl_outb(0x42, PA_MRSHPC_MW2 + 0x200);
- return 0;
-}
-#else
-static int __init cf_init_se(void)
-{
- return -1;
-}
-#endif
-
-static int __init cf_init(void)
-{
- if (mach_is_se() || mach_is_7722se() || mach_is_7721se())
- return cf_init_se();
-
- return cf_init_default();
-}
-
-__initcall (cf_init);
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c
index b7e46d5bba43..7b17137536d6 100644
--- a/arch/sh/kernel/cpu/clock.c
+++ b/arch/sh/kernel/cpu/clock.c
@@ -117,6 +117,11 @@ int clk_enable(struct clk *clk)
unsigned long flags;
int ret;
+ if (!clk)
+ return -EINVAL;
+
+ clk_enable(clk->parent);
+
spin_lock_irqsave(&clock_lock, flags);
ret = __clk_enable(clk);
spin_unlock_irqrestore(&clock_lock, flags);
@@ -147,9 +152,14 @@ void clk_disable(struct clk *clk)
{
unsigned long flags;
+ if (!clk)
+ return;
+
spin_lock_irqsave(&clock_lock, flags);
__clk_disable(clk);
spin_unlock_irqrestore(&clock_lock, flags);
+
+ clk_disable(clk->parent);
}
EXPORT_SYMBOL_GPL(clk_disable);
diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c
index 75fb03d35670..d29e69c156f0 100644
--- a/arch/sh/kernel/cpu/init.c
+++ b/arch/sh/kernel/cpu/init.c
@@ -261,9 +261,11 @@ asmlinkage void __init sh_cpu_init(void)
cache_init();
if (raw_smp_processor_id() == 0) {
+#ifdef CONFIG_MMU
shm_align_mask = max_t(unsigned long,
current_cpu_data.dcache.way_size - 1,
PAGE_SIZE - 1);
+#endif
/* Boot CPU sets the cache shape */
detect_cache_shape();
diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile
index 428450cc0809..45f85c77ef75 100644
--- a/arch/sh/kernel/cpu/sh2a/Makefile
+++ b/arch/sh/kernel/cpu/sh2a/Makefile
@@ -8,9 +8,10 @@ common-y += ex.o entry.o
obj-$(CONFIG_SH_FPU) += fpu.o
-obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7201) += setup-sh7201.o clock-sh7201.o
obj-$(CONFIG_CPU_SUBTYPE_SH7203) += setup-sh7203.o clock-sh7203.o
obj-$(CONFIG_CPU_SUBTYPE_SH7263) += setup-sh7203.o clock-sh7203.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o
obj-$(CONFIG_CPU_SUBTYPE_MXG) += setup-mxg.o clock-sh7206.o
# Pinmux setup
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7201.c b/arch/sh/kernel/cpu/sh2a/clock-sh7201.c
new file mode 100644
index 000000000000..020a96fe961a
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7201.c
@@ -0,0 +1,85 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/clock-sh7201.c
+ *
+ * SH7201 support for the clock framework
+ *
+ * Copyright (C) 2008 Peter Griffin <pgriffin@mpc-data.co.uk>
+ *
+ * Based on clock-sh4.c
+ * Copyright (C) 2005 Paul Mundt
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+const static int pll1rate[]={1,2,3,4,6,8};
+const static int pfc_divisors[]={1,2,3,4,6,8,12};
+#define ifc_divisors pfc_divisors
+
+#if (CONFIG_SH_CLK_MD == 0)
+#define PLL2 (4)
+#elif (CONFIG_SH_CLK_MD == 2)
+#define PLL2 (2)
+#elif (CONFIG_SH_CLK_MD == 3)
+#define PLL2 (1)
+#else
+#error "Illegal Clock Mode!"
+#endif
+
+static void master_clk_init(struct clk *clk)
+{
+ clk->rate = 10000000 * PLL2 * pll1rate[(ctrl_inw(FREQCR) >> 8) & 0x0007];
+}
+
+static struct clk_ops sh7201_master_clk_ops = {
+ .init = master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FREQCR) & 0x0007);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7201_module_clk_ops = {
+ .recalc = module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+ int idx = (ctrl_inw(FREQCR) & 0x0007);
+ clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7201_bus_clk_ops = {
+ .recalc = bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+ int idx = ((ctrl_inw(FREQCR) >> 4) & 0x0007);
+ clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7201_cpu_clk_ops = {
+ .recalc = cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7201_clk_ops[] = {
+ &sh7201_master_clk_ops,
+ &sh7201_module_clk_ops,
+ &sh7201_bus_clk_ops,
+ &sh7201_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+ if (idx < ARRAY_SIZE(sh7201_clk_ops))
+ *ops = sh7201_clk_ops[idx];
+}
diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c
index 6e79132f6f30..e098e2f6aa08 100644
--- a/arch/sh/kernel/cpu/sh2a/probe.c
+++ b/arch/sh/kernel/cpu/sh2a/probe.c
@@ -18,16 +18,17 @@ int __init detect_cpu_and_cache_system(void)
/* All SH-2A CPUs have support for 16 and 32-bit opcodes.. */
boot_cpu_data.flags |= CPU_HAS_OP32;
-#if defined(CONFIG_CPU_SUBTYPE_SH7203)
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+ boot_cpu_data.type = CPU_SH7201;
+ boot_cpu_data.flags |= CPU_HAS_FPU;
+#elif defined(CONFIG_CPU_SUBTYPE_SH7203)
boot_cpu_data.type = CPU_SH7203;
- /* SH7203 has an FPU.. */
boot_cpu_data.flags |= CPU_HAS_FPU;
#elif defined(CONFIG_CPU_SUBTYPE_SH7263)
boot_cpu_data.type = CPU_SH7263;
boot_cpu_data.flags |= CPU_HAS_FPU;
#elif defined(CONFIG_CPU_SUBTYPE_SH7206)
boot_cpu_data.type = CPU_SH7206;
- /* While SH7206 has a DSP.. */
boot_cpu_data.flags |= CPU_HAS_DSP;
#elif defined(CONFIG_CPU_SUBTYPE_MXG)
boot_cpu_data.type = CPU_MXG;
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
new file mode 100644
index 000000000000..0631e421c022
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c
@@ -0,0 +1,331 @@
+/*
+ * SH7201 setup
+ *
+ * Copyright (C) 2008 Peter Griffin pgriffin@mpc-data.co.uk
+ *
+ * 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/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/serial_sci.h>
+
+enum {
+ UNUSED = 0,
+
+ /* interrupt sources */
+ IRQ0, IRQ1, IRQ2, IRQ3, IRQ4, IRQ5, IRQ6, IRQ7,
+ PINT0, PINT1, PINT2, PINT3, PINT4, PINT5, PINT6, PINT7,
+ ADC_ADI,
+ MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D,
+ MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F,
+ MTU2_TGI1A, MTU2_TGI1B, MTU2_TCI1V, MTU2_TCI1U,
+ MTU2_TGI2A, MTU2_TGI2B, MTU2_TCI2V, MTU2_TCI2U,
+ MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D, MTU2_TCI3V,
+ MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D, MTU2_TCI4V,
+ MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W,
+ RTC_ARM, RTC_PRD, RTC_CUP,
+ WDT,
+ IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI, IIC30_TEI,
+ IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI, IIC31_TEI,
+ IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI, IIC32_TEI,
+
+ DMAC0_DMINT0, DMAC1_DMINT1,
+ DMAC2_DMINT2, DMAC3_DMINT3,
+
+ SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI,
+ SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI,
+ SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI,
+ SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI,
+ SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI,
+ SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI,
+ SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI,
+ SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI,
+
+ DMAC0_DMINTA, DMAC4_DMINT4, DMAC5_DMINT5, DMAC6_DMINT6,
+ DMAC7_DMINT7,
+
+ RCAN0_ERS, RCAN0_OVR,
+ RCAN0_SLE,
+ RCAN0_RM0, RCAN0_RM1,
+
+ RCAN1_ERS, RCAN1_OVR,
+ RCAN1_SLE,
+ RCAN1_RM0, RCAN1_RM1,
+
+ SSI0_SSII, SSI1_SSII,
+
+ TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0,
+ TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1,
+
+ /* interrupt groups */
+
+ IRQ, PINT, ADC,
+ MTU20_ABCD, MTU20_VEF, MTU21_AB, MTU21_VU, MTU22_AB, MTU22_VU,
+ MTU23_ABCD, MTU24_ABCD, MTU25_UVW,
+ RTC, IIC30, IIC31, IIC32,
+ SCIF0, SCIF1, SCIF2, SCIF3, SCIF4, SCIF5, SCIF6, SCIF7,
+ RCAN0, RCAN1, TMR0, TMR1
+
+};
+
+static struct intc_vect vectors[] __initdata = {
+ INTC_IRQ(IRQ0, 64), INTC_IRQ(IRQ1, 65),
+ INTC_IRQ(IRQ2, 66), INTC_IRQ(IRQ3, 67),
+ INTC_IRQ(IRQ4, 68), INTC_IRQ(IRQ5, 69),
+ INTC_IRQ(IRQ6, 70), INTC_IRQ(IRQ7, 71),
+ INTC_IRQ(PINT0, 80), INTC_IRQ(PINT1, 81),
+ INTC_IRQ(PINT2, 82), INTC_IRQ(PINT3, 83),
+ INTC_IRQ(PINT4, 84), INTC_IRQ(PINT5, 85),
+ INTC_IRQ(PINT6, 86), INTC_IRQ(PINT7, 87),
+
+ INTC_IRQ(ADC_ADI, 92),
+
+ INTC_IRQ(MTU2_TGI0A, 108), INTC_IRQ(MTU2_TGI0B, 109),
+ INTC_IRQ(MTU2_TGI0C, 110), INTC_IRQ(MTU2_TGI0D, 111),
+ INTC_IRQ(MTU2_TCI0V, 112),
+ INTC_IRQ(MTU2_TGI0E, 113), INTC_IRQ(MTU2_TGI0F, 114),
+
+ INTC_IRQ(MTU2_TGI1A, 116), INTC_IRQ(MTU2_TGI1B, 117),
+ INTC_IRQ(MTU2_TCI1V, 120), INTC_IRQ(MTU2_TCI1U, 121),
+
+ INTC_IRQ(MTU2_TGI2A, 124), INTC_IRQ(MTU2_TGI2B, 125),
+ INTC_IRQ(MTU2_TCI2V, 128), INTC_IRQ(MTU2_TCI2U, 129),
+
+ INTC_IRQ(MTU2_TGI3A, 132), INTC_IRQ(MTU2_TGI3B, 133),
+ INTC_IRQ(MTU2_TGI3C, 134), INTC_IRQ(MTU2_TGI3D, 135),
+ INTC_IRQ(MTU2_TCI3V, 136),
+
+ INTC_IRQ(MTU2_TGI4A, 140), INTC_IRQ(MTU2_TGI4B, 141),
+ INTC_IRQ(MTU2_TGI4C, 142), INTC_IRQ(MTU2_TGI4D, 143),
+ INTC_IRQ(MTU2_TCI4V, 144),
+
+ INTC_IRQ(MTU2_TGI5U, 148), INTC_IRQ(MTU2_TGI5V, 149),
+ INTC_IRQ(MTU2_TGI5W, 150),
+
+ INTC_IRQ(RTC_ARM, 152), INTC_IRQ(RTC_PRD, 153),
+ INTC_IRQ(RTC_CUP, 154), INTC_IRQ(WDT, 156),
+
+ INTC_IRQ(IIC30_STPI, 157), INTC_IRQ(IIC30_NAKI, 158),
+ INTC_IRQ(IIC30_RXI, 159), INTC_IRQ(IIC30_TXI, 160),
+ INTC_IRQ(IIC30_TEI, 161),
+
+ INTC_IRQ(IIC31_STPI, 164), INTC_IRQ(IIC31_NAKI, 165),
+ INTC_IRQ(IIC31_RXI, 166), INTC_IRQ(IIC31_TXI, 167),
+ INTC_IRQ(IIC31_TEI, 168),
+
+ INTC_IRQ(IIC32_STPI, 170), INTC_IRQ(IIC32_NAKI, 171),
+ INTC_IRQ(IIC32_RXI, 172), INTC_IRQ(IIC32_TXI, 173),
+ INTC_IRQ(IIC32_TEI, 174),
+
+ INTC_IRQ(DMAC0_DMINT0, 176), INTC_IRQ(DMAC1_DMINT1, 177),
+ INTC_IRQ(DMAC2_DMINT2, 178), INTC_IRQ(DMAC3_DMINT3, 179),
+
+ INTC_IRQ(SCIF0_BRI, 180), INTC_IRQ(SCIF0_ERI, 181),
+ INTC_IRQ(SCIF0_RXI, 182), INTC_IRQ(SCIF0_TXI, 183),
+ INTC_IRQ(SCIF1_BRI, 184), INTC_IRQ(SCIF1_ERI, 185),
+ INTC_IRQ(SCIF1_RXI, 186), INTC_IRQ(SCIF1_TXI, 187),
+ INTC_IRQ(SCIF2_BRI, 188), INTC_IRQ(SCIF2_ERI, 189),
+ INTC_IRQ(SCIF2_RXI, 190), INTC_IRQ(SCIF2_TXI, 191),
+ INTC_IRQ(SCIF3_BRI, 192), INTC_IRQ(SCIF3_ERI, 193),
+ INTC_IRQ(SCIF3_RXI, 194), INTC_IRQ(SCIF3_TXI, 195),
+ INTC_IRQ(SCIF4_BRI, 196), INTC_IRQ(SCIF4_ERI, 197),
+ INTC_IRQ(SCIF4_RXI, 198), INTC_IRQ(SCIF4_TXI, 199),
+ INTC_IRQ(SCIF5_BRI, 200), INTC_IRQ(SCIF5_ERI, 201),
+ INTC_IRQ(SCIF5_RXI, 202), INTC_IRQ(SCIF5_TXI, 203),
+ INTC_IRQ(SCIF6_BRI, 204), INTC_IRQ(SCIF6_ERI, 205),
+ INTC_IRQ(SCIF6_RXI, 206), INTC_IRQ(SCIF6_TXI, 207),
+ INTC_IRQ(SCIF7_BRI, 208), INTC_IRQ(SCIF7_ERI, 209),
+ INTC_IRQ(SCIF7_RXI, 210), INTC_IRQ(SCIF7_TXI, 211),
+
+ INTC_IRQ(DMAC0_DMINTA, 212), INTC_IRQ(DMAC4_DMINT4, 216),
+ INTC_IRQ(DMAC5_DMINT5, 217), INTC_IRQ(DMAC6_DMINT6, 218),
+ INTC_IRQ(DMAC7_DMINT7, 219),
+
+ INTC_IRQ(RCAN0_ERS, 228), INTC_IRQ(RCAN0_OVR, 229),
+ INTC_IRQ(RCAN0_SLE, 230),
+ INTC_IRQ(RCAN0_RM0, 231), INTC_IRQ(RCAN0_RM1, 232),
+
+ INTC_IRQ(RCAN1_ERS, 234), INTC_IRQ(RCAN1_OVR, 235),
+ INTC_IRQ(RCAN1_SLE, 236),
+ INTC_IRQ(RCAN1_RM0, 237), INTC_IRQ(RCAN1_RM1, 238),
+
+ INTC_IRQ(SSI0_SSII, 244), INTC_IRQ(SSI1_SSII, 245),
+
+ INTC_IRQ(TMR0_CMIA0, 246), INTC_IRQ(TMR0_CMIB0, 247),
+ INTC_IRQ(TMR0_OVI0, 248),
+
+ INTC_IRQ(TMR1_CMIA1, 252), INTC_IRQ(TMR1_CMIB1, 253),
+ INTC_IRQ(TMR1_OVI1, 254),
+
+};
+
+static struct intc_group groups[] __initdata = {
+ INTC_GROUP(PINT, PINT0, PINT1, PINT2, PINT3,
+ PINT4, PINT5, PINT6, PINT7),
+ INTC_GROUP(MTU20_ABCD, MTU2_TGI0A, MTU2_TGI0B, MTU2_TGI0C, MTU2_TGI0D),
+ INTC_GROUP(MTU20_VEF, MTU2_TCI0V, MTU2_TGI0E, MTU2_TGI0F),
+
+ INTC_GROUP(MTU21_AB, MTU2_TGI1A, MTU2_TGI1B),
+ INTC_GROUP(MTU21_VU, MTU2_TCI1V, MTU2_TCI1U),
+ INTC_GROUP(MTU22_AB, MTU2_TGI2A, MTU2_TGI2B),
+ INTC_GROUP(MTU22_VU, MTU2_TCI2V, MTU2_TCI2U),
+ INTC_GROUP(MTU23_ABCD, MTU2_TGI3A, MTU2_TGI3B, MTU2_TGI3C, MTU2_TGI3D),
+ INTC_GROUP(MTU24_ABCD, MTU2_TGI4A, MTU2_TGI4B, MTU2_TGI4C, MTU2_TGI4D),
+ INTC_GROUP(MTU25_UVW, MTU2_TGI5U, MTU2_TGI5V, MTU2_TGI5W),
+ INTC_GROUP(RTC, RTC_ARM, RTC_PRD, RTC_CUP ),
+
+ INTC_GROUP(IIC30, IIC30_STPI, IIC30_NAKI, IIC30_RXI, IIC30_TXI,
+ IIC30_TEI),
+ INTC_GROUP(IIC31, IIC31_STPI, IIC31_NAKI, IIC31_RXI, IIC31_TXI,
+ IIC31_TEI),
+ INTC_GROUP(IIC32, IIC32_STPI, IIC32_NAKI, IIC32_RXI, IIC32_TXI,
+ IIC32_TEI),
+
+ INTC_GROUP(SCIF0, SCIF0_BRI, SCIF0_ERI, SCIF0_RXI, SCIF0_TXI),
+ INTC_GROUP(SCIF1, SCIF1_BRI, SCIF1_ERI, SCIF1_RXI, SCIF1_TXI),
+ INTC_GROUP(SCIF2, SCIF2_BRI, SCIF2_ERI, SCIF2_RXI, SCIF2_TXI),
+ INTC_GROUP(SCIF3, SCIF3_BRI, SCIF3_ERI, SCIF3_RXI, SCIF3_TXI),
+ INTC_GROUP(SCIF4, SCIF4_BRI, SCIF4_ERI, SCIF4_RXI, SCIF4_TXI),
+ INTC_GROUP(SCIF5, SCIF5_BRI, SCIF5_ERI, SCIF5_RXI, SCIF5_TXI),
+ INTC_GROUP(SCIF6, SCIF6_BRI, SCIF6_ERI, SCIF6_RXI, SCIF6_TXI),
+ INTC_GROUP(SCIF7, SCIF7_BRI, SCIF7_ERI, SCIF7_RXI, SCIF7_TXI),
+
+ INTC_GROUP(RCAN0, RCAN0_ERS, RCAN0_OVR, RCAN0_RM0, RCAN0_RM1,
+ RCAN0_SLE),
+ INTC_GROUP(RCAN1, RCAN1_ERS, RCAN1_OVR, RCAN1_RM0, RCAN1_RM1,
+ RCAN1_SLE),
+
+ INTC_GROUP(TMR0, TMR0_CMIA0, TMR0_CMIB0, TMR0_OVI0),
+ INTC_GROUP(TMR1, TMR1_CMIA1, TMR1_CMIB1, TMR1_OVI1),
+};
+
+static struct intc_prio_reg prio_registers[] __initdata = {
+ { 0xfffe9418, 0, 16, 4, /* IPR01 */ { IRQ0, IRQ1, IRQ2, IRQ3 } },
+ { 0xfffe941a, 0, 16, 4, /* IPR02 */ { IRQ4, IRQ5, IRQ6, IRQ7 } },
+ { 0xfffe9420, 0, 16, 4, /* IPR05 */ { PINT, 0, ADC_ADI, 0 } },
+ { 0xfffe9800, 0, 16, 4, /* IPR06 */ { 0, MTU20_ABCD, MTU20_VEF, MTU21_AB } },
+ { 0xfffe9802, 0, 16, 4, /* IPR07 */ { MTU21_VU, MTU22_AB, MTU22_VU, MTU23_ABCD } },
+ { 0xfffe9804, 0, 16, 4, /* IPR08 */ { MTU2_TCI3V, MTU24_ABCD, MTU2_TCI4V, MTU25_UVW } },
+
+ { 0xfffe9806, 0, 16, 4, /* IPR09 */ { RTC, WDT, IIC30, 0 } },
+ { 0xfffe9808, 0, 16, 4, /* IPR10 */ { IIC31, IIC32, DMAC0_DMINT0, DMAC1_DMINT1 } },
+ { 0xfffe980a, 0, 16, 4, /* IPR11 */ { DMAC2_DMINT2, DMAC3_DMINT3, SCIF0 , SCIF1 } },
+ { 0xfffe980c, 0, 16, 4, /* IPR12 */ { SCIF2, SCIF3, SCIF4, SCIF5 } },
+ { 0xfffe980e, 0, 16, 4, /* IPR13 */ { SCIF6, SCIF7, DMAC0_DMINTA, DMAC4_DMINT4 } },
+ { 0xfffe9810, 0, 16, 4, /* IPR14 */ { DMAC5_DMINT5, DMAC6_DMINT6, DMAC7_DMINT7, 0 } },
+ { 0xfffe9812, 0, 16, 4, /* IPR15 */ { 0, RCAN0, RCAN1, 0 } },
+ { 0xfffe9814, 0, 16, 4, /* IPR16 */ { SSI0_SSII, SSI1_SSII, TMR0, TMR1 } },
+};
+
+static struct intc_mask_reg mask_registers[] __initdata = {
+ { 0xfffe9408, 0, 16, /* PINTER */
+ { 0, 0, 0, 0, 0, 0, 0, 0,
+ PINT7, PINT6, PINT5, PINT4, PINT3, PINT2, PINT1, PINT0 } },
+};
+
+static DECLARE_INTC_DESC(intc_desc, "sh7201", vectors, groups,
+ mask_registers, prio_registers, NULL);
+
+static struct plat_sci_port sci_platform_data[] = {
+ {
+ .mapbase = 0xfffe8000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 181, 182, 183, 180}
+ }, {
+ .mapbase = 0xfffe8800,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 185, 186, 187, 184}
+ }, {
+ .mapbase = 0xfffe9000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 189, 186, 187, 188}
+ }, {
+ .mapbase = 0xfffe9800,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 193, 194, 195, 192}
+ }, {
+ .mapbase = 0xfffea000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 196, 198, 199, 196}
+ }, {
+ .mapbase = 0xfffea800,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 201, 202, 203, 200}
+ }, {
+ .mapbase = 0xfffeb000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 205, 206, 207, 204}
+ }, {
+ .mapbase = 0xfffeb800,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 209, 210, 211, 208}
+ }, {
+ .flags = 0,
+ }
+};
+
+static struct platform_device sci_device = {
+ .name = "sh-sci",
+ .id = -1,
+ .dev = {
+ .platform_data = sci_platform_data,
+ },
+};
+
+static struct resource rtc_resources[] = {
+ [0] = {
+ .start = 0xffff0800,
+ .end = 0xffff2000 + 0x58 - 1,
+ .flags = IORESOURCE_IO,
+ },
+ [1] = {
+ /* Period IRQ */
+ .start = 153,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ /* Carry IRQ */
+ .start = 154,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ /* Alarm IRQ */
+ .start = 152,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device rtc_device = {
+ .name = "sh-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+
+static struct platform_device *sh7201_devices[] __initdata = {
+ &sci_device,
+ &rtc_device,
+};
+
+static int __init sh7201_devices_setup(void)
+{
+ return platform_add_devices(sh7201_devices,
+ ARRAY_SIZE(sh7201_devices));
+}
+__initcall(sh7201_devices_setup);
+
+void __init plat_irq_setup(void)
+{
+ register_intc_controller(&intc_desc);
+}
diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S
index 3fe482dd05c1..b4106d0c68ec 100644
--- a/arch/sh/kernel/cpu/sh3/entry.S
+++ b/arch/sh/kernel/cpu/sh3/entry.S
@@ -52,7 +52,7 @@
* syscall #
*
*/
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
NMI_VEC = 0x1c0 ! Must catch early for debounce
#endif
@@ -307,7 +307,7 @@ skip_restore:
6: or k0, k2 ! Set the IMASK-bits
ldc k2, ssr
!
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
! Clear in_nmi
mov.l 6f, k0
mov #0, k1
@@ -320,7 +320,7 @@ skip_restore:
.align 2
5: .long 0x00001000 ! DSP
-#ifdef CONFIG_KGDB_NMI
+#ifdef CONFIG_KGDB
6: .long in_nmi
#endif
7: .long 0x30000000
@@ -376,9 +376,9 @@ tlb_miss:
!
.balign 512,0,512
interrupt:
- mov.l 2f, k2
mov.l 3f, k3
-#if defined(CONFIG_KGDB_NMI)
+#if defined(CONFIG_KGDB)
+ mov.l 2f, k2
! Debounce (filter nested NMI)
mov.l @k2, k0
mov.l 5f, k1
@@ -390,16 +390,16 @@ interrupt:
rte
nop
.align 2
+2: .long INTEVT
5: .long NMI_VEC
6: .long in_nmi
0:
-#endif /* defined(CONFIG_KGDB_NMI) */
+#endif /* defined(CONFIG_KGDB) */
bra handle_exception
mov #-1, k2 ! interrupt exception marker
.align 2
1: .long EXPEVT
-2: .long INTEVT
3: .long ret_from_irq
4: .long ret_from_exception
diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S
index dac429726899..e5a0de39a2db 100644
--- a/arch/sh/kernel/cpu/sh3/ex.S
+++ b/arch/sh/kernel/cpu/sh3/ex.S
@@ -26,7 +26,7 @@
#define fpu_error_trap_handler exception_error
#endif
-#if !defined(CONFIG_KGDB_NMI)
+#if !defined(CONFIG_KGDB)
#define kgdb_handle_exception exception_error
#endif
diff --git a/arch/sh/kernel/cpu/sh4/softfloat.c b/arch/sh/kernel/cpu/sh4/softfloat.c
index 2b747f3b02bd..42edf2e54e85 100644
--- a/arch/sh/kernel/cpu/sh4/softfloat.c
+++ b/arch/sh/kernel/cpu/sh4/softfloat.c
@@ -37,6 +37,7 @@
*/
#include <linux/kernel.h>
#include <cpu/fpu.h>
+#include <asm/div64.h>
#define LIT64( a ) a##LL
@@ -67,16 +68,16 @@ typedef unsigned long long float64;
extern void float_raise(unsigned int flags); /* in fpu.c */
extern int float_rounding_mode(void); /* in fpu.c */
-inline bits64 extractFloat64Frac(float64 a);
-inline flag extractFloat64Sign(float64 a);
-inline int16 extractFloat64Exp(float64 a);
-inline int16 extractFloat32Exp(float32 a);
-inline flag extractFloat32Sign(float32 a);
-inline bits32 extractFloat32Frac(float32 a);
-inline float64 packFloat64(flag zSign, int16 zExp, bits64 zSig);
-inline void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr);
-inline float32 packFloat32(flag zSign, int16 zExp, bits32 zSig);
-inline void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr);
+bits64 extractFloat64Frac(float64 a);
+flag extractFloat64Sign(float64 a);
+int16 extractFloat64Exp(float64 a);
+int16 extractFloat32Exp(float32 a);
+flag extractFloat32Sign(float32 a);
+bits32 extractFloat32Frac(float32 a);
+float64 packFloat64(flag zSign, int16 zExp, bits64 zSig);
+void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr);
+float32 packFloat32(flag zSign, int16 zExp, bits32 zSig);
+void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr);
float64 float64_sub(float64 a, float64 b);
float32 float32_sub(float32 a, float32 b);
float32 float32_add(float32 a, float32 b);
@@ -86,11 +87,11 @@ float32 float32_div(float32 a, float32 b);
float32 float32_mul(float32 a, float32 b);
float64 float64_mul(float64 a, float64 b);
float32 float64_to_float32(float64 a);
-inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
bits64 * z1Ptr);
-inline void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
bits64 * z1Ptr);
-inline void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr);
+void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr);
static int8 countLeadingZeros32(bits32 a);
static int8 countLeadingZeros64(bits64 a);
@@ -110,42 +111,42 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b);
static void normalizeFloat32Subnormal(bits32 aSig, int16 * zExpPtr,
bits32 * zSigPtr);
-inline bits64 extractFloat64Frac(float64 a)
+bits64 extractFloat64Frac(float64 a)
{
return a & LIT64(0x000FFFFFFFFFFFFF);
}
-inline flag extractFloat64Sign(float64 a)
+flag extractFloat64Sign(float64 a)
{
return a >> 63;
}
-inline int16 extractFloat64Exp(float64 a)
+int16 extractFloat64Exp(float64 a)
{
return (a >> 52) & 0x7FF;
}
-inline int16 extractFloat32Exp(float32 a)
+int16 extractFloat32Exp(float32 a)
{
return (a >> 23) & 0xFF;
}
-inline flag extractFloat32Sign(float32 a)
+flag extractFloat32Sign(float32 a)
{
return a >> 31;
}
-inline bits32 extractFloat32Frac(float32 a)
+bits32 extractFloat32Frac(float32 a)
{
return a & 0x007FFFFF;
}
-inline float64 packFloat64(flag zSign, int16 zExp, bits64 zSig)
+float64 packFloat64(flag zSign, int16 zExp, bits64 zSig)
{
return (((bits64) zSign) << 63) + (((bits64) zExp) << 52) + zSig;
}
-inline void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr)
+void shift64RightJamming(bits64 a, int16 count, bits64 * zPtr)
{
bits64 z;
@@ -338,12 +339,12 @@ static float64 addFloat64Sigs(float64 a, float64 b, flag zSign)
}
-inline float32 packFloat32(flag zSign, int16 zExp, bits32 zSig)
+float32 packFloat32(flag zSign, int16 zExp, bits32 zSig)
{
return (((bits32) zSign) << 31) + (((bits32) zExp) << 23) + zSig;
}
-inline void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr)
+void shift32RightJamming(bits32 a, int16 count, bits32 * zPtr)
{
bits32 z;
if (count == 0) {
@@ -634,7 +635,7 @@ normalizeFloat64Subnormal(bits64 aSig, int16 * zExpPtr, bits64 * zSigPtr)
*zExpPtr = 1 - shiftCount;
}
-inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
+void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
bits64 * z1Ptr)
{
bits64 z1;
@@ -644,7 +645,7 @@ inline void add128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
*z0Ptr = a0 + b0 + (z1 < a1);
}
-inline void
+void
sub128(bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 * z0Ptr,
bits64 * z1Ptr)
{
@@ -656,11 +657,14 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b)
{
bits64 b0, b1;
bits64 rem0, rem1, term0, term1;
- bits64 z;
+ bits64 z, tmp;
if (b <= a0)
return LIT64(0xFFFFFFFFFFFFFFFF);
b0 = b >> 32;
- z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : (a0 / b0) << 32;
+ tmp = a0;
+ do_div(tmp, b0);
+
+ z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : tmp << 32;
mul64To128(b, z, &term0, &term1);
sub128(a0, a1, term0, term1, &rem0, &rem1);
while (((sbits64) rem0) < 0) {
@@ -669,11 +673,13 @@ static bits64 estimateDiv128To64(bits64 a0, bits64 a1, bits64 b)
add128(rem0, rem1, b0, b1, &rem0, &rem1);
}
rem0 = (rem0 << 32) | (rem1 >> 32);
- z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : rem0 / b0;
+ tmp = rem0;
+ do_div(tmp, b0);
+ z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : tmp;
return z;
}
-inline void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr)
+void mul64To128(bits64 a, bits64 b, bits64 * z0Ptr, bits64 * z1Ptr)
{
bits32 aHigh, aLow, bHigh, bLow;
bits64 z0, zMiddleA, zMiddleB, z1;
@@ -769,7 +775,8 @@ float32 float32_div(float32 a, float32 b)
{
flag aSign, bSign, zSign;
int16 aExp, bExp, zExp;
- bits32 aSig, bSig, zSig;
+ bits32 aSig, bSig;
+ uint64_t zSig;
aSig = extractFloat32Frac(a);
aExp = extractFloat32Exp(a);
@@ -804,11 +811,13 @@ float32 float32_div(float32 a, float32 b)
aSig >>= 1;
++zExp;
}
- zSig = (((bits64) aSig) << 32) / bSig;
+ zSig = (((bits64) aSig) << 32);
+ do_div(zSig, bSig);
+
if ((zSig & 0x3F) == 0) {
zSig |= (((bits64) bSig) * zSig != ((bits64) aSig) << 32);
}
- return roundAndPackFloat32(zSign, zExp, zSig);
+ return roundAndPackFloat32(zSign, zExp, (bits32)zSig);
}
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
index db913855c2fd..0e174af21874 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c
@@ -229,7 +229,7 @@ struct frqcr_context sh7722_get_clk_context(const char *name)
}
/**
- * sh7722_find_divisors - find divisor for setting rate
+ * sh7722_find_div_index - find divisor for setting rate
*
* All sh7722 clocks use the same set of multipliers/divisors. This function
* chooses correct divisor to set the rate of clock with parent clock that
@@ -238,7 +238,7 @@ struct frqcr_context sh7722_get_clk_context(const char *name)
* @parent_rate: rate of parent clock
* @rate: requested rate to be set
*/
-static int sh7722_find_divisors(unsigned long parent_rate, unsigned rate)
+static int sh7722_find_div_index(unsigned long parent_rate, unsigned rate)
{
unsigned div2 = parent_rate * 2 / rate;
int index;
@@ -247,12 +247,12 @@ static int sh7722_find_divisors(unsigned long parent_rate, unsigned rate)
return -EINVAL;
for (index = 1; index < ARRAY_SIZE(divisors2); index++) {
- if (div2 > divisors2[index] && div2 <= divisors2[index])
+ if (div2 > divisors2[index - 1] && div2 <= divisors2[index])
break;
}
if (index >= ARRAY_SIZE(divisors2))
index = ARRAY_SIZE(divisors2) - 1;
- return divisors2[index];
+ return index;
}
static void sh7722_frqcr_recalc(struct clk *clk)
@@ -279,12 +279,12 @@ static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate,
return -EINVAL;
/* look for multiplier/divisor pair */
- div = sh7722_find_divisors(parent_rate, rate);
+ div = sh7722_find_div_index(parent_rate, rate);
if (div<0)
return div;
/* calculate new value of clock rate */
- clk->rate = parent_rate * 2 / div;
+ clk->rate = parent_rate * 2 / divisors2[div];
frqcr = ctrl_inl(FRQCR);
/* FIXME: adjust as algo_id specifies */
@@ -353,7 +353,7 @@ static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate,
int part_div;
if (likely(!err)) {
- part_div = sh7722_find_divisors(parent_rate,
+ part_div = sh7722_find_div_index(parent_rate,
rate);
if (part_div > 0) {
part_ctx = sh7722_get_clk_context(
@@ -394,12 +394,12 @@ static long sh7722_frqcr_round_rate(struct clk *clk, unsigned long rate)
int div;
/* look for multiplier/divisor pair */
- div = sh7722_find_divisors(parent_rate, rate);
+ div = sh7722_find_div_index(parent_rate, rate);
if (div < 0)
return clk->rate;
/* calculate new value of clock rate */
- return parent_rate * 2 / div;
+ return parent_rate * 2 / divisors2[div];
}
static struct clk_ops sh7722_frqcr_clk_ops = {
@@ -421,7 +421,7 @@ static int sh7722_siu_set_rate(struct clk *clk, unsigned long rate, int algo_id)
int div;
r = ctrl_inl(clk->arch_flags);
- div = sh7722_find_divisors(clk->parent->rate, rate);
+ div = sh7722_find_div_index(clk->parent->rate, rate);
if (div < 0)
return div;
r = (r & ~0xF) | div;
@@ -516,16 +516,19 @@ static struct clk_ops sh7722_video_clk_ops = {
static struct clk sh7722_umem_clock = {
.name = "umem_clk",
.ops = &sh7722_frqcr_clk_ops,
+ .flags = CLK_RATE_PROPAGATES,
};
static struct clk sh7722_sh_clock = {
.name = "sh_clk",
.ops = &sh7722_frqcr_clk_ops,
+ .flags = CLK_RATE_PROPAGATES,
};
static struct clk sh7722_peripheral_clock = {
.name = "peripheral_clk",
.ops = &sh7722_frqcr_clk_ops,
+ .flags = CLK_RATE_PROPAGATES,
};
static struct clk sh7722_sdram_clock = {
@@ -533,6 +536,11 @@ static struct clk sh7722_sdram_clock = {
.ops = &sh7722_frqcr_clk_ops,
};
+static struct clk sh7722_r_clock = {
+ .name = "r_clk",
+ .rate = 32768,
+ .flags = CLK_RATE_PROPAGATES,
+};
#ifndef CONFIG_CPU_SUBTYPE_SH7343
@@ -567,12 +575,30 @@ static struct clk sh7722_video_clock = {
.ops = &sh7722_video_clk_ops,
};
-static int sh7722_mstpcr_start_stop(struct clk *clk, unsigned long reg,
- int enable)
+#define MSTPCR_ARCH_FLAGS(reg, bit) (((reg) << 8) | (bit))
+#define MSTPCR_ARCH_FLAGS_REG(value) ((value) >> 8)
+#define MSTPCR_ARCH_FLAGS_BIT(value) ((value) & 0xff)
+
+static int sh7722_mstpcr_start_stop(struct clk *clk, int enable)
{
- unsigned long bit = clk->arch_flags;
+ unsigned long bit = MSTPCR_ARCH_FLAGS_BIT(clk->arch_flags);
+ unsigned long reg;
unsigned long r;
+ switch(MSTPCR_ARCH_FLAGS_REG(clk->arch_flags)) {
+ case 0:
+ reg = MSTPCR0;
+ break;
+ case 1:
+ reg = MSTPCR1;
+ break;
+ case 2:
+ reg = MSTPCR2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
r = ctrl_inl(reg);
if (enable)
@@ -584,96 +610,175 @@ static int sh7722_mstpcr_start_stop(struct clk *clk, unsigned long reg,
return 0;
}
-static void sh7722_mstpcr0_enable(struct clk *clk)
-{
- sh7722_mstpcr_start_stop(clk, MSTPCR0, 1);
-}
-
-static void sh7722_mstpcr0_disable(struct clk *clk)
-{
- sh7722_mstpcr_start_stop(clk, MSTPCR0, 0);
-}
-
-static void sh7722_mstpcr1_enable(struct clk *clk)
-{
- sh7722_mstpcr_start_stop(clk, MSTPCR1, 1);
-}
-
-static void sh7722_mstpcr1_disable(struct clk *clk)
+static void sh7722_mstpcr_enable(struct clk *clk)
{
- sh7722_mstpcr_start_stop(clk, MSTPCR1, 0);
+ sh7722_mstpcr_start_stop(clk, 1);
}
-static void sh7722_mstpcr2_enable(struct clk *clk)
+static void sh7722_mstpcr_disable(struct clk *clk)
{
- sh7722_mstpcr_start_stop(clk, MSTPCR2, 1);
+ sh7722_mstpcr_start_stop(clk, 0);
}
-static void sh7722_mstpcr2_disable(struct clk *clk)
+static void sh7722_mstpcr_recalc(struct clk *clk)
{
- sh7722_mstpcr_start_stop(clk, MSTPCR2, 0);
+ if (clk->parent)
+ clk->rate = clk->parent->rate;
}
-static struct clk_ops sh7722_mstpcr0_clk_ops = {
- .enable = sh7722_mstpcr0_enable,
- .disable = sh7722_mstpcr0_disable,
-};
-
-static struct clk_ops sh7722_mstpcr1_clk_ops = {
- .enable = sh7722_mstpcr1_enable,
- .disable = sh7722_mstpcr1_disable,
+static struct clk_ops sh7722_mstpcr_clk_ops = {
+ .enable = sh7722_mstpcr_enable,
+ .disable = sh7722_mstpcr_disable,
+ .recalc = sh7722_mstpcr_recalc,
};
-static struct clk_ops sh7722_mstpcr2_clk_ops = {
- .enable = sh7722_mstpcr2_enable,
- .disable = sh7722_mstpcr2_disable,
-};
-
-#define DECLARE_MSTPCRN(regnr, bitnr, bitstr) \
-{ \
- .name = "mstp" __stringify(regnr) bitstr, \
- .arch_flags = bitnr, \
- .ops = &sh7722_mstpcr ## regnr ## _clk_ops, \
+#define MSTPCR(_name, _parent, regnr, bitnr) \
+{ \
+ .name = _name, \
+ .arch_flags = MSTPCR_ARCH_FLAGS(regnr, bitnr), \
+ .ops = (void *)_parent, \
}
-#define DECLARE_MSTPCR(regnr) \
- DECLARE_MSTPCRN(regnr, 31, "31"), \
- DECLARE_MSTPCRN(regnr, 30, "30"), \
- DECLARE_MSTPCRN(regnr, 29, "29"), \
- DECLARE_MSTPCRN(regnr, 28, "28"), \
- DECLARE_MSTPCRN(regnr, 27, "27"), \
- DECLARE_MSTPCRN(regnr, 26, "26"), \
- DECLARE_MSTPCRN(regnr, 25, "25"), \
- DECLARE_MSTPCRN(regnr, 24, "24"), \
- DECLARE_MSTPCRN(regnr, 23, "23"), \
- DECLARE_MSTPCRN(regnr, 22, "22"), \
- DECLARE_MSTPCRN(regnr, 21, "21"), \
- DECLARE_MSTPCRN(regnr, 20, "20"), \
- DECLARE_MSTPCRN(regnr, 19, "19"), \
- DECLARE_MSTPCRN(regnr, 18, "18"), \
- DECLARE_MSTPCRN(regnr, 17, "17"), \
- DECLARE_MSTPCRN(regnr, 16, "16"), \
- DECLARE_MSTPCRN(regnr, 15, "15"), \
- DECLARE_MSTPCRN(regnr, 14, "14"), \
- DECLARE_MSTPCRN(regnr, 13, "13"), \
- DECLARE_MSTPCRN(regnr, 12, "12"), \
- DECLARE_MSTPCRN(regnr, 11, "11"), \
- DECLARE_MSTPCRN(regnr, 10, "10"), \
- DECLARE_MSTPCRN(regnr, 9, "09"), \
- DECLARE_MSTPCRN(regnr, 8, "08"), \
- DECLARE_MSTPCRN(regnr, 7, "07"), \
- DECLARE_MSTPCRN(regnr, 6, "06"), \
- DECLARE_MSTPCRN(regnr, 5, "05"), \
- DECLARE_MSTPCRN(regnr, 4, "04"), \
- DECLARE_MSTPCRN(regnr, 3, "03"), \
- DECLARE_MSTPCRN(regnr, 2, "02"), \
- DECLARE_MSTPCRN(regnr, 1, "01"), \
- DECLARE_MSTPCRN(regnr, 0, "00")
-
-static struct clk sh7722_mstpcr[] = {
- DECLARE_MSTPCR(0),
- DECLARE_MSTPCR(1),
- DECLARE_MSTPCR(2),
+static struct clk sh7722_mstpcr_clocks[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7722)
+ MSTPCR("uram0", "umem_clk", 0, 28),
+ MSTPCR("xymem0", "bus_clk", 0, 26),
+ MSTPCR("tmu0", "peripheral_clk", 0, 15),
+ MSTPCR("cmt0", "r_clk", 0, 14),
+ MSTPCR("rwdt0", "r_clk", 0, 13),
+ MSTPCR("flctl0", "peripheral_clk", 0, 10),
+ MSTPCR("scif0", "peripheral_clk", 0, 7),
+ MSTPCR("scif1", "peripheral_clk", 0, 6),
+ MSTPCR("scif2", "peripheral_clk", 0, 5),
+ MSTPCR("i2c0", "peripheral_clk", 1, 9),
+ MSTPCR("rtc0", "r_clk", 1, 8),
+ MSTPCR("sdhi0", "peripheral_clk", 2, 18),
+ MSTPCR("keysc0", "r_clk", 2, 14),
+ MSTPCR("usbf0", "peripheral_clk", 2, 11),
+ MSTPCR("2dg0", "bus_clk", 2, 9),
+ MSTPCR("siu0", "bus_clk", 2, 8),
+ MSTPCR("vou0", "bus_clk", 2, 5),
+ MSTPCR("jpu0", "bus_clk", 2, 6),
+ MSTPCR("beu0", "bus_clk", 2, 4),
+ MSTPCR("ceu0", "bus_clk", 2, 3),
+ MSTPCR("veu0", "bus_clk", 2, 2),
+ MSTPCR("vpu0", "bus_clk", 2, 1),
+ MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7723)
+ /* See page 60 of Datasheet V1.0: Overview -> Block Diagram */
+ MSTPCR("tlb0", "cpu_clk", 0, 31),
+ MSTPCR("ic0", "cpu_clk", 0, 30),
+ MSTPCR("oc0", "cpu_clk", 0, 29),
+ MSTPCR("l2c0", "sh_clk", 0, 28),
+ MSTPCR("ilmem0", "cpu_clk", 0, 27),
+ MSTPCR("fpu0", "cpu_clk", 0, 24),
+ MSTPCR("intc0", "cpu_clk", 0, 22),
+ MSTPCR("dmac0", "bus_clk", 0, 21),
+ MSTPCR("sh0", "sh_clk", 0, 20),
+ MSTPCR("hudi0", "peripheral_clk", 0, 19),
+ MSTPCR("ubc0", "cpu_clk", 0, 17),
+ MSTPCR("tmu0", "peripheral_clk", 0, 15),
+ MSTPCR("cmt0", "r_clk", 0, 14),
+ MSTPCR("rwdt0", "r_clk", 0, 13),
+ MSTPCR("dmac1", "bus_clk", 0, 12),
+ MSTPCR("tmu1", "peripheral_clk", 0, 11),
+ MSTPCR("flctl0", "peripheral_clk", 0, 10),
+ MSTPCR("scif0", "peripheral_clk", 0, 9),
+ MSTPCR("scif1", "peripheral_clk", 0, 8),
+ MSTPCR("scif2", "peripheral_clk", 0, 7),
+ MSTPCR("scif3", "bus_clk", 0, 6),
+ MSTPCR("scif4", "bus_clk", 0, 5),
+ MSTPCR("scif5", "bus_clk", 0, 4),
+ MSTPCR("msiof0", "bus_clk", 0, 2),
+ MSTPCR("msiof1", "bus_clk", 0, 1),
+ MSTPCR("meram0", "sh_clk", 0, 0),
+ MSTPCR("i2c0", "peripheral_clk", 1, 9),
+ MSTPCR("rtc0", "r_clk", 1, 8),
+ MSTPCR("atapi0", "sh_clk", 2, 28),
+ MSTPCR("adc0", "peripheral_clk", 2, 28),
+ MSTPCR("tpu0", "bus_clk", 2, 25),
+ MSTPCR("irda0", "peripheral_clk", 2, 24),
+ MSTPCR("tsif0", "bus_clk", 2, 22),
+ MSTPCR("icb0", "bus_clk", 2, 21),
+ MSTPCR("sdhi0", "bus_clk", 2, 18),
+ MSTPCR("sdhi1", "bus_clk", 2, 17),
+ MSTPCR("keysc0", "r_clk", 2, 14),
+ MSTPCR("usb0", "bus_clk", 2, 11),
+ MSTPCR("2dg0", "bus_clk", 2, 10),
+ MSTPCR("siu0", "bus_clk", 2, 8),
+ MSTPCR("veu1", "bus_clk", 2, 6),
+ MSTPCR("vou0", "bus_clk", 2, 5),
+ MSTPCR("beu0", "bus_clk", 2, 4),
+ MSTPCR("ceu0", "bus_clk", 2, 3),
+ MSTPCR("veu0", "bus_clk", 2, 2),
+ MSTPCR("vpu0", "bus_clk", 2, 1),
+ MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7343)
+ MSTPCR("uram0", "umem_clk", 0, 28),
+ MSTPCR("xymem0", "bus_clk", 0, 26),
+ MSTPCR("tmu0", "peripheral_clk", 0, 15),
+ MSTPCR("cmt0", "r_clk", 0, 14),
+ MSTPCR("rwdt0", "r_clk", 0, 13),
+ MSTPCR("scif0", "peripheral_clk", 0, 7),
+ MSTPCR("scif1", "peripheral_clk", 0, 6),
+ MSTPCR("scif2", "peripheral_clk", 0, 5),
+ MSTPCR("scif3", "peripheral_clk", 0, 4),
+ MSTPCR("i2c0", "peripheral_clk", 1, 9),
+ MSTPCR("i2c1", "peripheral_clk", 1, 8),
+ MSTPCR("sdhi0", "peripheral_clk", 2, 18),
+ MSTPCR("keysc0", "r_clk", 2, 14),
+ MSTPCR("usbf0", "peripheral_clk", 2, 11),
+ MSTPCR("siu0", "bus_clk", 2, 8),
+ MSTPCR("jpu0", "bus_clk", 2, 6),
+ MSTPCR("vou0", "bus_clk", 2, 5),
+ MSTPCR("beu0", "bus_clk", 2, 4),
+ MSTPCR("ceu0", "bus_clk", 2, 3),
+ MSTPCR("veu0", "bus_clk", 2, 2),
+ MSTPCR("vpu0", "bus_clk", 2, 1),
+ MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
+#if defined(CONFIG_CPU_SUBTYPE_SH7366)
+ /* See page 52 of Datasheet V0.40: Overview -> Block Diagram */
+ MSTPCR("tlb0", "cpu_clk", 0, 31),
+ MSTPCR("ic0", "cpu_clk", 0, 30),
+ MSTPCR("oc0", "cpu_clk", 0, 29),
+ MSTPCR("rsmem0", "sh_clk", 0, 28),
+ MSTPCR("xymem0", "cpu_clk", 0, 26),
+ MSTPCR("intc30", "peripheral_clk", 0, 23),
+ MSTPCR("intc0", "peripheral_clk", 0, 22),
+ MSTPCR("dmac0", "bus_clk", 0, 21),
+ MSTPCR("sh0", "sh_clk", 0, 20),
+ MSTPCR("hudi0", "peripheral_clk", 0, 19),
+ MSTPCR("ubc0", "cpu_clk", 0, 17),
+ MSTPCR("tmu0", "peripheral_clk", 0, 15),
+ MSTPCR("cmt0", "r_clk", 0, 14),
+ MSTPCR("rwdt0", "r_clk", 0, 13),
+ MSTPCR("flctl0", "peripheral_clk", 0, 10),
+ MSTPCR("scif0", "peripheral_clk", 0, 7),
+ MSTPCR("scif1", "bus_clk", 0, 6),
+ MSTPCR("scif2", "bus_clk", 0, 5),
+ MSTPCR("msiof0", "peripheral_clk", 0, 2),
+ MSTPCR("sbr0", "peripheral_clk", 0, 1),
+ MSTPCR("i2c0", "peripheral_clk", 1, 9),
+ MSTPCR("icb0", "bus_clk", 2, 27),
+ MSTPCR("meram0", "sh_clk", 2, 26),
+ MSTPCR("dacc0", "peripheral_clk", 2, 24),
+ MSTPCR("dacy0", "peripheral_clk", 2, 23),
+ MSTPCR("tsif0", "bus_clk", 2, 22),
+ MSTPCR("sdhi0", "bus_clk", 2, 18),
+ MSTPCR("mmcif0", "bus_clk", 2, 17),
+ MSTPCR("usb0", "bus_clk", 2, 11),
+ MSTPCR("siu0", "bus_clk", 2, 8),
+ MSTPCR("veu1", "bus_clk", 2, 7),
+ MSTPCR("vou0", "bus_clk", 2, 5),
+ MSTPCR("beu0", "bus_clk", 2, 4),
+ MSTPCR("ceu0", "bus_clk", 2, 3),
+ MSTPCR("veu0", "bus_clk", 2, 2),
+ MSTPCR("vpu0", "bus_clk", 2, 1),
+ MSTPCR("lcdc0", "bus_clk", 2, 0),
+#endif
};
static struct clk *sh7722_clocks[] = {
@@ -710,21 +815,30 @@ arch_init_clk_ops(struct clk_ops **ops, int type)
int __init arch_clk_init(void)
{
- struct clk *master;
+ struct clk *clk;
int i;
- master = clk_get(NULL, "master_clk");
+ clk = clk_get(NULL, "master_clk");
for (i = 0; i < ARRAY_SIZE(sh7722_clocks); i++) {
pr_debug( "Registering clock '%s'\n", sh7722_clocks[i]->name);
- sh7722_clocks[i]->parent = master;
+ sh7722_clocks[i]->parent = clk;
clk_register(sh7722_clocks[i]);
}
- clk_put(master);
-
- for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr); i++) {
- pr_debug( "Registering mstpcr '%s'\n", sh7722_mstpcr[i].name);
- clk_register(&sh7722_mstpcr[i]);
+ clk_put(clk);
+
+ clk_register(&sh7722_r_clock);
+
+ for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr_clocks); i++) {
+ pr_debug( "Registering mstpcr clock '%s'\n",
+ sh7722_mstpcr_clocks[i].name);
+ clk = clk_get(NULL, (void *) sh7722_mstpcr_clocks[i].ops);
+ sh7722_mstpcr_clocks[i].parent = clk;
+ sh7722_mstpcr_clocks[i].ops = &sh7722_mstpcr_clk_ops;
+ clk_register(&sh7722_mstpcr_clocks[i]);
+ clk_put(clk);
}
+ clk_recalc_rate(&sh7722_r_clock); /* make sure rate gets propagated */
+
return 0;
}
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
index 78881b4214da..0623e377f488 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c
@@ -30,6 +30,7 @@ static struct resource iic0_resources[] = {
static struct platform_device iic0_device = {
.name = "i2c-sh_mobile",
+ .id = 0, /* "i2c0" clock */
.num_resources = ARRAY_SIZE(iic0_resources),
.resource = iic0_resources,
};
@@ -50,6 +51,7 @@ static struct resource iic1_resources[] = {
static struct platform_device iic1_device = {
.name = "i2c-sh_mobile",
+ .id = 1, /* "i2c1" clock */
.num_resources = ARRAY_SIZE(iic1_resources),
.resource = iic1_resources,
};
@@ -115,7 +117,22 @@ static struct plat_sci_port sci_platform_data[] = {
.mapbase = 0xffe00000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 80, 81, 83, 82 },
+ .irqs = { 80, 80, 80, 80 },
+ }, {
+ .mapbase = 0xffe10000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 81, 81, 81, 81 },
+ }, {
+ .mapbase = 0xffe20000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 82, 82, 82, 82 },
+ }, {
+ .mapbase = 0xffe30000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 83, 83, 83, 83 },
}, {
.flags = 0,
}
@@ -139,18 +156,10 @@ static struct platform_device *sh7343_devices[] __initdata = {
static int __init sh7343_devices_setup(void)
{
- clk_always_enable("mstp031"); /* TLB */
- clk_always_enable("mstp030"); /* IC */
- clk_always_enable("mstp029"); /* OC */
- clk_always_enable("mstp028"); /* URAM */
- clk_always_enable("mstp026"); /* XYMEM */
- clk_always_enable("mstp023"); /* INTC3 */
- clk_always_enable("mstp022"); /* INTC */
- clk_always_enable("mstp020"); /* SuperHyway */
- clk_always_enable("mstp109"); /* I2C0 */
- clk_always_enable("mstp108"); /* I2C1 */
- clk_always_enable("mstp202"); /* VEU */
- clk_always_enable("mstp201"); /* VPU */
+ clk_always_enable("uram0"); /* URAM */
+ clk_always_enable("xymem0"); /* XYMEM */
+ clk_always_enable("veu0"); /* VEU */
+ clk_always_enable("vpu0"); /* VPU */
platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20);
platform_resource_setup_memory(&veu_device, "veu", 2 << 20);
@@ -171,7 +180,7 @@ enum {
MMC_ERR, MMC_TRAN, MMC_FSTAT, MMC_FRDY,
DMAC4, DMAC5, DMAC_DADERR,
KEYSC,
- SCIF, SCIF1, SCIF2, SCIF3, SCIF4,
+ SCIF, SCIF1, SCIF2, SCIF3,
SIOF0, SIOF1, SIO,
FLCTL_FLSTEI, FLCTL_FLENDI, FLCTL_FLTREQ0I, FLCTL_FLTREQ1I,
I2C0_ALI, I2C0_TACKI, I2C0_WAITI, I2C0_DTEI,
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
index e17db39b97aa..839ae97a7fd2 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c
@@ -32,6 +32,7 @@ static struct resource iic_resources[] = {
static struct platform_device iic_device = {
.name = "i2c-sh_mobile",
+ .id = 0, /* "i2c0" clock */
.num_resources = ARRAY_SIZE(iic_resources),
.resource = iic_resources,
};
@@ -176,19 +177,11 @@ static struct platform_device *sh7366_devices[] __initdata = {
static int __init sh7366_devices_setup(void)
{
- clk_always_enable("mstp031"); /* TLB */
- clk_always_enable("mstp030"); /* IC */
- clk_always_enable("mstp029"); /* OC */
- clk_always_enable("mstp028"); /* RSMEM */
- clk_always_enable("mstp026"); /* XYMEM */
- clk_always_enable("mstp023"); /* INTC3 */
- clk_always_enable("mstp022"); /* INTC */
- clk_always_enable("mstp020"); /* SuperHyway */
- clk_always_enable("mstp109"); /* I2C */
- clk_always_enable("mstp211"); /* USB */
- clk_always_enable("mstp207"); /* VEU-2 */
- clk_always_enable("mstp202"); /* VEU-1 */
- clk_always_enable("mstp201"); /* VPU */
+ clk_always_enable("rsmem0"); /* RSMEM */
+ clk_always_enable("xymem0"); /* XYMEM */
+ clk_always_enable("veu1"); /* VEU-2 */
+ clk_always_enable("veu0"); /* VEU-1 */
+ clk_always_enable("vpu0"); /* VPU */
platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20);
platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20);
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index ef77ee1d9f53..50cf6838ec41 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -62,7 +62,7 @@ static struct resource usbf_resources[] = {
static struct platform_device usbf_device = {
.name = "m66592_udc",
- .id = -1,
+ .id = 0, /* "usbf0" clock */
.dev = {
.dma_mask = NULL,
.coherent_dma_mask = 0xffffffff,
@@ -87,6 +87,7 @@ static struct resource iic_resources[] = {
static struct platform_device iic_device = {
.name = "i2c-sh_mobile",
+ .id = 0, /* "i2c0" clock */
.num_resources = ARRAY_SIZE(iic_resources),
.resource = iic_resources,
};
@@ -147,6 +148,34 @@ static struct platform_device veu_device = {
.num_resources = ARRAY_SIZE(veu_resources),
};
+static struct uio_info jpu_platform_data = {
+ .name = "JPU",
+ .version = "0",
+ .irq = 27,
+};
+
+static struct resource jpu_resources[] = {
+ [0] = {
+ .name = "JPU",
+ .start = 0xfea00000,
+ .end = 0xfea102d0,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ /* place holder for contiguous memory */
+ },
+};
+
+static struct platform_device jpu_device = {
+ .name = "uio_pdrv_genirq",
+ .id = 2,
+ .dev = {
+ .platform_data = &jpu_platform_data,
+ },
+ .resource = jpu_resources,
+ .num_resources = ARRAY_SIZE(jpu_resources),
+};
+
static struct plat_sci_port sci_platform_data[] = {
{
.mapbase = 0xffe00000,
@@ -186,24 +215,21 @@ static struct platform_device *sh7722_devices[] __initdata = {
&sci_device,
&vpu_device,
&veu_device,
+ &jpu_device,
};
static int __init sh7722_devices_setup(void)
{
- clk_always_enable("mstp031"); /* TLB */
- clk_always_enable("mstp030"); /* IC */
- clk_always_enable("mstp029"); /* OC */
- clk_always_enable("mstp028"); /* URAM */
- clk_always_enable("mstp026"); /* XYMEM */
- clk_always_enable("mstp022"); /* INTC */
- clk_always_enable("mstp020"); /* SuperHyway */
- clk_always_enable("mstp109"); /* I2C */
- clk_always_enable("mstp211"); /* USB */
- clk_always_enable("mstp202"); /* VEU */
- clk_always_enable("mstp201"); /* VPU */
+ clk_always_enable("uram0"); /* URAM */
+ clk_always_enable("xymem0"); /* XYMEM */
+ clk_always_enable("rtc0"); /* RTC */
+ clk_always_enable("veu0"); /* VEU */
+ clk_always_enable("vpu0"); /* VPU */
+ clk_always_enable("jpu0"); /* JPU */
platform_resource_setup_memory(&vpu_device, "vpu", 1 << 20);
platform_resource_setup_memory(&veu_device, "veu", 2 << 20);
+ platform_resource_setup_memory(&jpu_device, "jpu", 2 << 20);
return platform_add_devices(sh7722_devices,
ARRAY_SIZE(sh7722_devices));
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
index 6d9e6972cfc9..849770d780ae 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c
@@ -215,6 +215,7 @@ static struct resource iic_resources[] = {
static struct platform_device iic_device = {
.name = "i2c-sh_mobile",
+ .id = 0, /* "i2c0" clock */
.num_resources = ARRAY_SIZE(iic_resources),
.resource = iic_resources,
};
@@ -231,19 +232,11 @@ static struct platform_device *sh7723_devices[] __initdata = {
static int __init sh7723_devices_setup(void)
{
- clk_always_enable("mstp031"); /* TLB */
- clk_always_enable("mstp030"); /* IC */
- clk_always_enable("mstp029"); /* OC */
- clk_always_enable("mstp024"); /* FPU */
- clk_always_enable("mstp022"); /* INTC */
- clk_always_enable("mstp020"); /* SuperHyway */
- clk_always_enable("mstp000"); /* MERAM */
- clk_always_enable("mstp109"); /* I2C */
- clk_always_enable("mstp108"); /* RTC */
- clk_always_enable("mstp211"); /* USB */
- clk_always_enable("mstp206"); /* VEU2H1 */
- clk_always_enable("mstp202"); /* VEU2H0 */
- clk_always_enable("mstp201"); /* VPU */
+ clk_always_enable("meram0"); /* MERAM */
+ clk_always_enable("rtc0"); /* RTC */
+ clk_always_enable("veu1"); /* VEU2H1 */
+ clk_always_enable("veu0"); /* VEU2H0 */
+ clk_always_enable("vpu0"); /* VPU */
platform_resource_setup_memory(&vpu_device, "vpu", 2 << 20);
platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20);
diff --git a/arch/sh/kernel/debugtraps.S b/arch/sh/kernel/debugtraps.S
index 13b66746410a..591741383ee6 100644
--- a/arch/sh/kernel/debugtraps.S
+++ b/arch/sh/kernel/debugtraps.S
@@ -3,7 +3,7 @@
*
* Debug trap jump tables for SuperH
*
- * Copyright (C) 2006 Paul Mundt
+ * Copyright (C) 2006 - 2008 Paul Mundt
*
* 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
@@ -12,12 +12,13 @@
#include <linux/sys.h>
#include <linux/linkage.h>
-#if !defined(CONFIG_SH_KGDB)
-#define kgdb_handle_exception debug_trap_handler
+#if !defined(CONFIG_KGDB)
+#define breakpoint_trap_handler debug_trap_handler
+#define singlestep_trap_handler debug_trap_handler
#endif
#if !defined(CONFIG_SH_STANDARD_BIOS)
-#define sh_bios_handler debug_trap_handler
+#define sh_bios_handler debug_trap_handler
#endif
.data
@@ -35,7 +36,7 @@ ENTRY(debug_trap_table)
.long debug_trap_handler /* 0x39 */
.long debug_trap_handler /* 0x3a */
.long debug_trap_handler /* 0x3b */
- .long kgdb_handle_exception /* 0x3c */
- .long debug_trap_handler /* 0x3d */
+ .long breakpoint_trap_handler /* 0x3c */
+ .long singlestep_trap_handler /* 0x3d */
.long bug_trap_handler /* 0x3e */
.long sh_bios_handler /* 0x3f */
diff --git a/arch/sh/kernel/disassemble.c b/arch/sh/kernel/disassemble.c
new file mode 100644
index 000000000000..64d5d8dded7c
--- /dev/null
+++ b/arch/sh/kernel/disassemble.c
@@ -0,0 +1,573 @@
+/*
+ * Disassemble SuperH instructions.
+ *
+ * Copyright (C) 1999 kaz Kojima
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+/*
+ * Format of an instruction in memory.
+ */
+typedef enum {
+ HEX_0, HEX_1, HEX_2, HEX_3, HEX_4, HEX_5, HEX_6, HEX_7,
+ HEX_8, HEX_9, HEX_A, HEX_B, HEX_C, HEX_D, HEX_E, HEX_F,
+ REG_N, REG_M, REG_NM, REG_B,
+ BRANCH_12, BRANCH_8,
+ DISP_8, DISP_4,
+ IMM_4, IMM_4BY2, IMM_4BY4, PCRELIMM_8BY2, PCRELIMM_8BY4,
+ IMM_8, IMM_8BY2, IMM_8BY4,
+} sh_nibble_type;
+
+typedef enum {
+ A_END, A_BDISP12, A_BDISP8,
+ A_DEC_M, A_DEC_N,
+ A_DISP_GBR, A_DISP_PC, A_DISP_REG_M, A_DISP_REG_N,
+ A_GBR,
+ A_IMM,
+ A_INC_M, A_INC_N,
+ A_IND_M, A_IND_N, A_IND_R0_REG_M, A_IND_R0_REG_N,
+ A_MACH, A_MACL,
+ A_PR, A_R0, A_R0_GBR, A_REG_M, A_REG_N, A_REG_B,
+ A_SR, A_VBR, A_SSR, A_SPC, A_SGR, A_DBR,
+ F_REG_N, F_REG_M, D_REG_N, D_REG_M,
+ X_REG_N, /* Only used for argument parsing */
+ X_REG_M, /* Only used for argument parsing */
+ DX_REG_N, DX_REG_M, V_REG_N, V_REG_M,
+ FD_REG_N,
+ XMTRX_M4,
+ F_FR0,
+ FPUL_N, FPUL_M, FPSCR_N, FPSCR_M,
+} sh_arg_type;
+
+static struct sh_opcode_info {
+ char *name;
+ sh_arg_type arg[7];
+ sh_nibble_type nibbles[4];
+} sh_table[] = {
+ {"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM_8}},
+ {"add",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_C}},
+ {"addc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_E}},
+ {"addv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_F}},
+ {"and",{A_IMM,A_R0},{HEX_C,HEX_9,IMM_8}},
+ {"and",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_9}},
+ {"and.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_D,IMM_8}},
+ {"bra",{A_BDISP12},{HEX_A,BRANCH_12}},
+ {"bsr",{A_BDISP12},{HEX_B,BRANCH_12}},
+ {"bt",{A_BDISP8},{HEX_8,HEX_9,BRANCH_8}},
+ {"bf",{A_BDISP8},{HEX_8,HEX_B,BRANCH_8}},
+ {"bt.s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}},
+ {"bt/s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}},
+ {"bf.s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}},
+ {"bf/s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}},
+ {"clrmac",{0},{HEX_0,HEX_0,HEX_2,HEX_8}},
+ {"clrs",{0},{HEX_0,HEX_0,HEX_4,HEX_8}},
+ {"clrt",{0},{HEX_0,HEX_0,HEX_0,HEX_8}},
+ {"cmp/eq",{A_IMM,A_R0},{HEX_8,HEX_8,IMM_8}},
+ {"cmp/eq",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_0}},
+ {"cmp/ge",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_3}},
+ {"cmp/gt",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_7}},
+ {"cmp/hi",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_6}},
+ {"cmp/hs",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_2}},
+ {"cmp/pl",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_5}},
+ {"cmp/pz",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_1}},
+ {"cmp/str",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_C}},
+ {"div0s",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_7}},
+ {"div0u",{0},{HEX_0,HEX_0,HEX_1,HEX_9}},
+ {"div1",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_4}},
+ {"exts.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_E}},
+ {"exts.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_F}},
+ {"extu.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_C}},
+ {"extu.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_D}},
+ {"jmp",{A_IND_N},{HEX_4,REG_N,HEX_2,HEX_B}},
+ {"jsr",{A_IND_N},{HEX_4,REG_N,HEX_0,HEX_B}},
+ {"ldc",{A_REG_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_E}},
+ {"ldc",{A_REG_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_E}},
+ {"ldc",{A_REG_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_E}},
+ {"ldc",{A_REG_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_E}},
+ {"ldc",{A_REG_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_E}},
+ {"ldc",{A_REG_N,A_DBR},{HEX_4,REG_N,HEX_7,HEX_E}},
+ {"ldc",{A_REG_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_E}},
+ {"ldc.l",{A_INC_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_7}},
+ {"ldc.l",{A_INC_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_7}},
+ {"ldc.l",{A_INC_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_7}},
+ {"ldc.l",{A_INC_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_7}},
+ {"ldc.l",{A_INC_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_7}},
+ {"ldc.l",{A_INC_N,A_DBR},{HEX_4,REG_N,HEX_7,HEX_7}},
+ {"ldc.l",{A_INC_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_7}},
+ {"lds",{A_REG_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_A}},
+ {"lds",{A_REG_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_A}},
+ {"lds",{A_REG_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_A}},
+ {"lds",{A_REG_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_A}},
+ {"lds",{A_REG_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_A}},
+ {"lds.l",{A_INC_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_6}},
+ {"lds.l",{A_INC_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_6}},
+ {"lds.l",{A_INC_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_6}},
+ {"lds.l",{A_INC_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_6}},
+ {"lds.l",{A_INC_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_6}},
+ {"ldtlb",{0},{HEX_0,HEX_0,HEX_3,HEX_8}},
+ {"mac.w",{A_INC_M,A_INC_N},{HEX_4,REG_N,REG_M,HEX_F}},
+ {"mov",{A_IMM,A_REG_N},{HEX_E,REG_N,IMM_8}},
+ {"mov",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_3}},
+ {"mov.b",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_4}},
+ {"mov.b",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_4}},
+ {"mov.b",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_0}},
+ {"mov.b",{A_DISP_REG_M,A_R0},{HEX_8,HEX_4,REG_M,IMM_4}},
+ {"mov.b",{A_DISP_GBR,A_R0},{HEX_C,HEX_4,IMM_8}},
+ {"mov.b",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_C}},
+ {"mov.b",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_4}},
+ {"mov.b",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_0}},
+ {"mov.b",{A_R0,A_DISP_REG_M},{HEX_8,HEX_0,REG_M,IMM_4}},
+ {"mov.b",{A_R0,A_DISP_GBR},{HEX_C,HEX_0,IMM_8}},
+ {"mov.l",{ A_REG_M,A_DISP_REG_N},{HEX_1,REG_N,REG_M,IMM_4BY4}},
+ {"mov.l",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_6}},
+ {"mov.l",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_6}},
+ {"mov.l",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_2}},
+ {"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_5,REG_N,REG_M,IMM_4BY4}},
+ {"mov.l",{A_DISP_GBR,A_R0},{HEX_C,HEX_6,IMM_8BY4}},
+ {"mov.l",{A_DISP_PC,A_REG_N},{HEX_D,REG_N,PCRELIMM_8BY4}},
+ {"mov.l",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_E}},
+ {"mov.l",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_6}},
+ {"mov.l",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_2}},
+ {"mov.l",{A_R0,A_DISP_GBR},{HEX_C,HEX_2,IMM_8BY4}},
+ {"mov.w",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_5}},
+ {"mov.w",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_5}},
+ {"mov.w",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_1}},
+ {"mov.w",{A_DISP_REG_M,A_R0},{HEX_8,HEX_5,REG_M,IMM_4BY2}},
+ {"mov.w",{A_DISP_GBR,A_R0},{HEX_C,HEX_5,IMM_8BY2}},
+ {"mov.w",{A_DISP_PC,A_REG_N},{HEX_9,REG_N,PCRELIMM_8BY2}},
+ {"mov.w",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_D}},
+ {"mov.w",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_5}},
+ {"mov.w",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_1}},
+ {"mov.w",{A_R0,A_DISP_REG_M},{HEX_8,HEX_1,REG_M,IMM_4BY2}},
+ {"mov.w",{A_R0,A_DISP_GBR},{HEX_C,HEX_1,IMM_8BY2}},
+ {"mova",{A_DISP_PC,A_R0},{HEX_C,HEX_7,PCRELIMM_8BY4}},
+ {"movca.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_C,HEX_3}},
+ {"movt",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_9}},
+ {"muls",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}},
+ {"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}},
+ {"mulu",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}},
+ {"neg",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_B}},
+ {"negc",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_A}},
+ {"nop",{0},{HEX_0,HEX_0,HEX_0,HEX_9}},
+ {"not",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_7}},
+ {"ocbi",{A_IND_N},{HEX_0,REG_N,HEX_9,HEX_3}},
+ {"ocbp",{A_IND_N},{HEX_0,REG_N,HEX_A,HEX_3}},
+ {"ocbwb",{A_IND_N},{HEX_0,REG_N,HEX_B,HEX_3}},
+ {"or",{A_IMM,A_R0},{HEX_C,HEX_B,IMM_8}},
+ {"or",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_B}},
+ {"or.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_F,IMM_8}},
+ {"pref",{A_IND_N},{HEX_0,REG_N,HEX_8,HEX_3}},
+ {"rotcl",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_4}},
+ {"rotcr",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_5}},
+ {"rotl",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_4}},
+ {"rotr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_5}},
+ {"rte",{0},{HEX_0,HEX_0,HEX_2,HEX_B}},
+ {"rts",{0},{HEX_0,HEX_0,HEX_0,HEX_B}},
+ {"sets",{0},{HEX_0,HEX_0,HEX_5,HEX_8}},
+ {"sett",{0},{HEX_0,HEX_0,HEX_1,HEX_8}},
+ {"shad",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_C}},
+ {"shld",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_D}},
+ {"shal",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_0}},
+ {"shar",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_1}},
+ {"shll",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_0}},
+ {"shll16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_8}},
+ {"shll2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_8}},
+ {"shll8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_8}},
+ {"shlr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_1}},
+ {"shlr16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_9}},
+ {"shlr2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_9}},
+ {"shlr8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_9}},
+ {"sleep",{0},{HEX_0,HEX_0,HEX_1,HEX_B}},
+ {"stc",{A_SR,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_2}},
+ {"stc",{A_GBR,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_2}},
+ {"stc",{A_VBR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_2}},
+ {"stc",{A_SSR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_2}},
+ {"stc",{A_SPC,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_2}},
+ {"stc",{A_SGR,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_2}},
+ {"stc",{A_DBR,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_2}},
+ {"stc",{A_REG_B,A_REG_N},{HEX_0,REG_N,REG_B,HEX_2}},
+ {"stc.l",{A_SR,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_3}},
+ {"stc.l",{A_GBR,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_3}},
+ {"stc.l",{A_VBR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_3}},
+ {"stc.l",{A_SSR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_3}},
+ {"stc.l",{A_SPC,A_DEC_N},{HEX_4,REG_N,HEX_4,HEX_3}},
+ {"stc.l",{A_SGR,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_3}},
+ {"stc.l",{A_DBR,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_3}},
+ {"stc.l",{A_REG_B,A_DEC_N},{HEX_4,REG_N,REG_B,HEX_3}},
+ {"sts",{A_MACH,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_A}},
+ {"sts",{A_MACL,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_A}},
+ {"sts",{A_PR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_A}},
+ {"sts",{FPUL_M,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_A}},
+ {"sts",{FPSCR_M,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}},
+ {"sts.l",{A_MACH,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_2}},
+ {"sts.l",{A_MACL,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_2}},
+ {"sts.l",{A_PR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_2}},
+ {"sts.l",{FPUL_M,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_2}},
+ {"sts.l",{FPSCR_M,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}},
+ {"sub",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_8}},
+ {"subc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_A}},
+ {"subv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_B}},
+ {"swap.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_8}},
+ {"swap.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_9}},
+ {"tas.b",{A_IND_N},{HEX_4,REG_N,HEX_1,HEX_B}},
+ {"trapa",{A_IMM},{HEX_C,HEX_3,IMM_8}},
+ {"tst",{A_IMM,A_R0},{HEX_C,HEX_8,IMM_8}},
+ {"tst",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_8}},
+ {"tst.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_C,IMM_8}},
+ {"xor",{A_IMM,A_R0},{HEX_C,HEX_A,IMM_8}},
+ {"xor",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_A}},
+ {"xor.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_E,IMM_8}},
+ {"xtrct",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_D}},
+ {"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}},
+ {"dt",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_0}},
+ {"dmuls.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_D}},
+ {"dmulu.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_5}},
+ {"mac.l",{A_INC_M,A_INC_N},{HEX_0,REG_N,REG_M,HEX_F}},
+ {"braf",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_3}},
+ {"bsrf",{A_REG_N},{HEX_0,REG_N,HEX_0,HEX_3}},
+ {"fabs",{FD_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}},
+ {"fadd",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_0}},
+ {"fadd",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_0}},
+ {"fcmp/eq",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_4}},
+ {"fcmp/eq",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_4}},
+ {"fcmp/gt",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_5}},
+ {"fcmp/gt",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_5}},
+ {"fcnvds",{D_REG_N,FPUL_M},{HEX_F,REG_N,HEX_B,HEX_D}},
+ {"fcnvsd",{FPUL_M,D_REG_N},{HEX_F,REG_N,HEX_A,HEX_D}},
+ {"fdiv",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_3}},
+ {"fdiv",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_3}},
+ {"fipr",{V_REG_M,V_REG_N},{HEX_F,REG_NM,HEX_E,HEX_D}},
+ {"fldi0",{F_REG_N},{HEX_F,REG_N,HEX_8,HEX_D}},
+ {"fldi1",{F_REG_N},{HEX_F,REG_N,HEX_9,HEX_D}},
+ {"flds",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_1,HEX_D}},
+ {"float",{FPUL_M,FD_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}},
+ {"fmac",{F_FR0,F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_E}},
+ {"fmov",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_C}},
+ {"fmov",{DX_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_C}},
+ {"fmov",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+ {"fmov",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+ {"fmov",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+ {"fmov",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+ {"fmov",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+ {"fmov",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+ {"fmov",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+ {"fmov",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+ {"fmov",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+ {"fmov",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+ {"fmov",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+ {"fmov",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+ {"fmov.d",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+ {"fmov.d",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+ {"fmov.d",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+ {"fmov.d",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+ {"fmov.d",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+ {"fmov.d",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+ {"fmov.s",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}},
+ {"fmov.s",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}},
+ {"fmov.s",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}},
+ {"fmov.s",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}},
+ {"fmov.s",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}},
+ {"fmov.s",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}},
+ {"fmul",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_2}},
+ {"fmul",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_2}},
+ {"fneg",{FD_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}},
+ {"frchg",{0},{HEX_F,HEX_B,HEX_F,HEX_D}},
+ {"fschg",{0},{HEX_F,HEX_3,HEX_F,HEX_D}},
+ {"fsqrt",{FD_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}},
+ {"fsts",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_0,HEX_D}},
+ {"fsub",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_1}},
+ {"fsub",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_1}},
+ {"ftrc",{FD_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}},
+ {"ftrv",{XMTRX_M4,V_REG_N},{HEX_F,REG_NM,HEX_F,HEX_D}},
+ { 0 },
+};
+
+static void print_sh_insn(u32 memaddr, u16 insn)
+{
+ int relmask = ~0;
+ int nibs[4] = { (insn >> 12) & 0xf, (insn >> 8) & 0xf, (insn >> 4) & 0xf, insn & 0xf};
+ int lastsp;
+ struct sh_opcode_info *op = sh_table;
+
+ for (; op->name; op++) {
+ int n;
+ int imm = 0;
+ int rn = 0;
+ int rm = 0;
+ int rb = 0;
+ int disp_pc;
+ int disp_pc_addr = 0;
+
+ for (n = 0; n < 4; n++) {
+ int i = op->nibbles[n];
+
+ if (i < 16) {
+ if (nibs[n] == i)
+ continue;
+ goto fail;
+ }
+ switch (i) {
+ case BRANCH_8:
+ imm = (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x80)
+ imm |= ~0xff;
+ imm = ((char)imm) * 2 + 4 ;
+ goto ok;
+ case BRANCH_12:
+ imm = ((nibs[1]) << 8) | (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x800)
+ imm |= ~0xfff;
+ imm = imm * 2 + 4;
+ goto ok;
+ case IMM_4:
+ imm = nibs[3];
+ goto ok;
+ case IMM_4BY2:
+ imm = nibs[3] <<1;
+ goto ok;
+ case IMM_4BY4:
+ imm = nibs[3] <<2;
+ goto ok;
+ case IMM_8:
+ imm = (nibs[2] << 4) | nibs[3];
+ goto ok;
+ case PCRELIMM_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) <<1;
+ relmask = ~1;
+ goto ok;
+ case PCRELIMM_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) <<2;
+ relmask = ~3;
+ goto ok;
+ case IMM_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) <<1;
+ goto ok;
+ case IMM_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) <<2;
+ goto ok;
+ case DISP_8:
+ imm = (nibs[2] << 4) | (nibs[3]);
+ goto ok;
+ case DISP_4:
+ imm = nibs[3];
+ goto ok;
+ case REG_N:
+ rn = nibs[n];
+ break;
+ case REG_M:
+ rm = nibs[n];
+ break;
+ case REG_NM:
+ rn = (nibs[n] & 0xc) >> 2;
+ rm = (nibs[n] & 0x3);
+ break;
+ case REG_B:
+ rb = nibs[n] & 0x07;
+ break;
+ default:
+ return;
+ }
+ }
+
+ ok:
+ printk("%-8s ", op->name);
+ lastsp = (op->arg[0] == A_END);
+ disp_pc = 0;
+ for (n = 0; n < 6 && op->arg[n] != A_END; n++) {
+ if (n && op->arg[1] != A_END)
+ printk(", ");
+ switch (op->arg[n]) {
+ case A_IMM:
+ printk("#%d", (char)(imm));
+ break;
+ case A_R0:
+ printk("r0");
+ break;
+ case A_REG_N:
+ printk("r%d", rn);
+ break;
+ case A_INC_N:
+ printk("@r%d+", rn);
+ break;
+ case A_DEC_N:
+ printk("@-r%d", rn);
+ break;
+ case A_IND_N:
+ printk("@r%d", rn);
+ break;
+ case A_DISP_REG_N:
+ printk("@(%d,r%d)", imm, rn);
+ break;
+ case A_REG_M:
+ printk("r%d", rm);
+ break;
+ case A_INC_M:
+ printk("@r%d+", rm);
+ break;
+ case A_DEC_M:
+ printk("@-r%d", rm);
+ break;
+ case A_IND_M:
+ printk("@r%d", rm);
+ break;
+ case A_DISP_REG_M:
+ printk("@(%d,r%d)", imm, rm);
+ break;
+ case A_REG_B:
+ printk("r%d_bank", rb);
+ break;
+ case A_DISP_PC:
+ disp_pc = 1;
+ disp_pc_addr = imm + 4 + (memaddr & relmask);
+ printk("%08x <%pS>", disp_pc_addr,
+ (void *)disp_pc_addr);
+ break;
+ case A_IND_R0_REG_N:
+ printk("@(r0,r%d)", rn);
+ break;
+ case A_IND_R0_REG_M:
+ printk("@(r0,r%d)", rm);
+ break;
+ case A_DISP_GBR:
+ printk("@(%d,gbr)",imm);
+ break;
+ case A_R0_GBR:
+ printk("@(r0,gbr)");
+ break;
+ case A_BDISP12:
+ case A_BDISP8:
+ printk("%08x", imm + memaddr);
+ break;
+ case A_SR:
+ printk("sr");
+ break;
+ case A_GBR:
+ printk("gbr");
+ break;
+ case A_VBR:
+ printk("vbr");
+ break;
+ case A_SSR:
+ printk("ssr");
+ break;
+ case A_SPC:
+ printk("spc");
+ break;
+ case A_MACH:
+ printk("mach");
+ break;
+ case A_MACL:
+ printk("macl");
+ break;
+ case A_PR:
+ printk("pr");
+ break;
+ case A_SGR:
+ printk("sgr");
+ break;
+ case A_DBR:
+ printk("dbr");
+ break;
+ case FD_REG_N:
+ if (0)
+ goto d_reg_n;
+ case F_REG_N:
+ printk("fr%d", rn);
+ break;
+ case F_REG_M:
+ printk("fr%d", rm);
+ break;
+ case DX_REG_N:
+ if (rn & 1) {
+ printk("xd%d", rn & ~1);
+ break;
+ }
+ d_reg_n:
+ case D_REG_N:
+ printk("dr%d", rn);
+ break;
+ case DX_REG_M:
+ if (rm & 1) {
+ printk("xd%d", rm & ~1);
+ break;
+ }
+ case D_REG_M:
+ printk("dr%d", rm);
+ break;
+ case FPSCR_M:
+ case FPSCR_N:
+ printk("fpscr");
+ break;
+ case FPUL_M:
+ case FPUL_N:
+ printk("fpul");
+ break;
+ case F_FR0:
+ printk("fr0");
+ break;
+ case V_REG_N:
+ printk("fv%d", rn*4);
+ break;
+ case V_REG_M:
+ printk("fv%d", rm*4);
+ break;
+ case XMTRX_M4:
+ printk("xmtrx");
+ break;
+ default:
+ return;
+ }
+ }
+
+ if (disp_pc && strcmp(op->name, "mova") != 0) {
+ u32 val;
+
+ if (relmask == ~1)
+ __get_user(val, (u16 *)disp_pc_addr);
+ else
+ __get_user(val, (u32 *)disp_pc_addr);
+
+ printk(" ! %08x <%pS>", val, (void *)val);
+ }
+
+ return;
+ fail:
+ ;
+
+ }
+
+ printk(".word 0x%x%x%x%x", nibs[0], nibs[1], nibs[2], nibs[3]);
+}
+
+void show_code(struct pt_regs *regs)
+{
+ unsigned short *pc = (unsigned short *)regs->pc;
+ long i;
+
+ if (regs->pc & 0x1)
+ return;
+
+ printk("Code:\n");
+
+ for (i = -3 ; i < 6 ; i++) {
+ unsigned short insn;
+
+ if (__get_user(insn, pc + i)) {
+ printk(" (Bad address in pc)\n");
+ break;
+ }
+
+ printk("%s%08lx: ", (i ? " ": "->"), (unsigned long)(pc + i));
+ print_sh_insn((unsigned long)(pc + i), insn);
+ printk("\n");
+ }
+
+ printk("\n");
+}
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index 5b7efc4016fa..d62359cfbbe2 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -308,15 +308,19 @@ ENTRY(system_call)
mov.l 1f, r9
mov.l @r9, r8 ! Read from TRA (Trap Address) Register
#endif
+
+ mov #OFF_TRA, r10
+ add r15, r10
+ mov.l r8, @r10 ! set TRA value to tra
+
/*
* Check the trap type
*/
mov #((0x20 << 2) - 1), r9
cmp/hi r9, r8
bt/s debug_trap ! it's a debug trap..
- mov #OFF_TRA, r9
- add r15, r9
- mov.l r8, @r9 ! set TRA value to tra
+ nop
+
#ifdef CONFIG_TRACE_IRQFLAGS
mov.l 5f, r10
jsr @r10
@@ -371,47 +375,3 @@ syscall_exit:
#endif
7: .long do_syscall_trace_enter
8: .long do_syscall_trace_leave
-
-#ifdef CONFIG_FUNCTION_TRACER
- .align 2
- .globl _mcount
- .type _mcount,@function
- .globl mcount
- .type mcount,@function
-_mcount:
-mcount:
- mov.l r4, @-r15
- mov.l r5, @-r15
- mov.l r6, @-r15
- mov.l r7, @-r15
- sts.l pr, @-r15
-
- mov.l @(20,r15),r4
- sts pr, r5
-
- mov.l 1f, r6
- mov.l ftrace_stub, r7
- cmp/eq r6, r7
- bt skip_trace
-
- mov.l @r6, r6
- jsr @r6
- nop
-
-skip_trace:
-
- lds.l @r15+, pr
- mov.l @r15+, r7
- mov.l @r15+, r6
- mov.l @r15+, r5
- rts
- mov.l @r15+, r4
-
- .align 2
-1: .long ftrace_trace_function
-
- .globl ftrace_stub
-ftrace_stub:
- rts
- nop
-#endif /* CONFIG_FUNCTION_TRACER */
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
new file mode 100644
index 000000000000..4c3247477aa3
--- /dev/null
+++ b/arch/sh/kernel/ftrace.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2008 Matt Fleming <mjf@gentoo.org>
+ * Copyright (C) 2008 Paul Mundt <lethal@linux-sh.org>
+ *
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * Thanks goes to Ingo Molnar, for suggesting the idea.
+ * Mathieu Desnoyers, for suggesting postponing the modifications.
+ * Arjan van de Ven, for keeping me straight, and explaining to me
+ * the dangers of modifying code on the run.
+ */
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <asm/ftrace.h>
+#include <asm/cacheflush.h>
+
+static unsigned char ftrace_nop[] = {
+ 0x09, 0x00, /* nop */
+ 0x09, 0x00, /* nop */
+};
+
+static unsigned char ftrace_replaced_code[MCOUNT_INSN_SIZE];
+
+unsigned char *ftrace_nop_replace(void)
+{
+ return ftrace_nop;
+}
+
+static int is_sh_nop(unsigned char *ip)
+{
+ return strncmp(ip, ftrace_nop, sizeof(ftrace_nop));
+}
+
+unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+ /* Place the address in the memory table. */
+ if (addr == CALLER_ADDR)
+ __raw_writel(addr + MCOUNT_INSN_OFFSET, ftrace_replaced_code);
+ else
+ __raw_writel(addr, ftrace_replaced_code);
+
+ /*
+ * No locking needed, this must be called via kstop_machine
+ * which in essence is like running on a uniprocessor machine.
+ */
+ return ftrace_replaced_code;
+}
+
+int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned char replaced[MCOUNT_INSN_SIZE];
+
+ /*
+ * Note: Due to modules and __init, code can
+ * disappear and change, we need to protect against faulting
+ * as well as code changing. We do this by using the
+ * probe_kernel_* functions.
+ *
+ * No real locking needed, this code is run through
+ * kstop_machine, or before SMP starts.
+ */
+
+ /*
+ * If we're trying to nop out a call to a function, we instead
+ * place a call to the address after the memory table.
+ */
+ if (is_sh_nop(new_code) == 0)
+ __raw_writel(ip + MCOUNT_INSN_SIZE, (unsigned long)new_code);
+
+ /* read the text we want to modify */
+ if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
+ return -EFAULT;
+
+ /* Make sure it is what we expect it to be */
+ if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
+ return -EINVAL;
+
+ /* replace the text with the new text */
+ if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
+ return -EPERM;
+
+ flush_icache_range(ip, ip + MCOUNT_INSN_SIZE);
+
+ return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned char old[MCOUNT_INSN_SIZE], *new;
+
+ memcpy(old, (unsigned char *)(ip + MCOUNT_INSN_OFFSET), MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+
+ return ftrace_modify_code(ip + MCOUNT_INSN_OFFSET, old, new);
+}
+
+int ftrace_make_nop(struct module *mod,
+ struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned char *new, *old;
+ unsigned long ip = rec->ip;
+
+ old = ftrace_call_replace(ip, addr);
+ new = ftrace_nop_replace();
+
+ return ftrace_modify_code(rec->ip, old, new);
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ unsigned char *new, *old;
+ unsigned long ip = rec->ip;
+
+ old = ftrace_nop_replace();
+ new = ftrace_call_replace(ip, addr);
+
+ return ftrace_modify_code(rec->ip, old, new);
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ /* The return code is retured via data */
+ __raw_writel(0, (unsigned long)data);
+
+ return 0;
+}
diff --git a/arch/sh/kernel/head_32.S b/arch/sh/kernel/head_32.S
index ae0a382a82eb..788605ff7088 100644
--- a/arch/sh/kernel/head_32.S
+++ b/arch/sh/kernel/head_32.S
@@ -80,8 +80,14 @@ ENTRY(_stext)
mov.l 7f, r0
ldc r0, r7_bank ! ... and initial thread_info
#endif
-
- ! Clear BSS area
+
+#ifndef CONFIG_SH_NO_BSS_INIT
+ /*
+ * Don't clear BSS if running on slow platforms such as an RTL simulation,
+ * remote memory via SHdebug link, etc. For these the memory can be guaranteed
+ * to be all zero on boot anyway.
+ */
+ ! Clear BSS area
#ifdef CONFIG_SMP
mov.l 3f, r0
cmp/eq #0, r0 ! skip clear if set to zero
@@ -97,6 +103,8 @@ ENTRY(_stext)
mov.l r0,@-r2
10:
+#endif
+
! Additional CPU initialization
mov.l 6f, r0
jsr @r0
diff --git a/arch/sh/kernel/idle.c b/arch/sh/kernel/idle.c
new file mode 100644
index 000000000000..fe59ccfc1152
--- /dev/null
+++ b/arch/sh/kernel/idle.c
@@ -0,0 +1,81 @@
+/*
+ * The idle loop for all SuperH platforms.
+ *
+ * Copyright (C) 2002 - 2008 Paul Mundt
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/pm.h>
+#include <linux/tick.h>
+#include <linux/preempt.h>
+#include <linux/thread_info.h>
+#include <linux/irqflags.h>
+#include <asm/pgalloc.h>
+#include <asm/system.h>
+#include <asm/atomic.h>
+
+static int hlt_counter;
+void (*pm_idle)(void);
+void (*pm_power_off)(void);
+EXPORT_SYMBOL(pm_power_off);
+
+static int __init nohlt_setup(char *__unused)
+{
+ hlt_counter = 1;
+ return 1;
+}
+__setup("nohlt", nohlt_setup);
+
+static int __init hlt_setup(char *__unused)
+{
+ hlt_counter = 0;
+ return 1;
+}
+__setup("hlt", hlt_setup);
+
+static void default_idle(void)
+{
+ if (!hlt_counter) {
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb__after_clear_bit();
+ set_bl_bit();
+ stop_critical_timings();
+
+ while (!need_resched())
+ cpu_sleep();
+
+ start_critical_timings();
+ clear_bl_bit();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+ } else
+ while (!need_resched())
+ cpu_relax();
+}
+
+void cpu_idle(void)
+{
+ set_thread_flag(TIF_POLLING_NRFLAG);
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+ void (*idle)(void) = pm_idle;
+
+ if (!idle)
+ idle = default_idle;
+
+ tick_nohz_stop_sched_tick(1);
+ while (!need_resched())
+ idle();
+ tick_nohz_restart_sched_tick();
+
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ check_pgt_cache();
+ }
+}
diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c
new file mode 100644
index 000000000000..7c747e7d71b8
--- /dev/null
+++ b/arch/sh/kernel/kgdb.c
@@ -0,0 +1,285 @@
+/*
+ * SuperH KGDB support
+ *
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel.
+ *
+ * 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/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+
+char in_nmi = 0; /* Set during NMI to prevent re-entry */
+
+/* Macros for single step instruction identification */
+#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900)
+#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00)
+#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
+ (((op) & 0x7f ) << 1))
+#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00)
+#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00)
+#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000)
+#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023)
+#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8)
+#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000)
+#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
+ (((op) & 0x7ff) << 1))
+#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003)
+#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b)
+#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b)
+#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf)
+#define OPCODE_RTS(op) ((op) == 0xb)
+#define OPCODE_RTE(op) ((op) == 0x2b)
+
+#define SR_T_BIT_MASK 0x1
+#define STEP_OPCODE 0xc33d
+
+/* Calculate the new address for after a step */
+static short *get_step_address(struct pt_regs *linux_regs)
+{
+ opcode_t op = __raw_readw(linux_regs->pc);
+ long addr;
+
+ /* BT */
+ if (OPCODE_BT(op)) {
+ if (linux_regs->sr & SR_T_BIT_MASK)
+ addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = linux_regs->pc + 2;
+ }
+
+ /* BTS */
+ else if (OPCODE_BTS(op)) {
+ if (linux_regs->sr & SR_T_BIT_MASK)
+ addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = linux_regs->pc + 4; /* Not in delay slot */
+ }
+
+ /* BF */
+ else if (OPCODE_BF(op)) {
+ if (!(linux_regs->sr & SR_T_BIT_MASK))
+ addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = linux_regs->pc + 2;
+ }
+
+ /* BFS */
+ else if (OPCODE_BFS(op)) {
+ if (!(linux_regs->sr & SR_T_BIT_MASK))
+ addr = linux_regs->pc + 4 + OPCODE_BTF_DISP(op);
+ else
+ addr = linux_regs->pc + 4; /* Not in delay slot */
+ }
+
+ /* BRA */
+ else if (OPCODE_BRA(op))
+ addr = linux_regs->pc + 4 + OPCODE_BRA_DISP(op);
+
+ /* BRAF */
+ else if (OPCODE_BRAF(op))
+ addr = linux_regs->pc + 4
+ + linux_regs->regs[OPCODE_BRAF_REG(op)];
+
+ /* BSR */
+ else if (OPCODE_BSR(op))
+ addr = linux_regs->pc + 4 + OPCODE_BSR_DISP(op);
+
+ /* BSRF */
+ else if (OPCODE_BSRF(op))
+ addr = linux_regs->pc + 4
+ + linux_regs->regs[OPCODE_BSRF_REG(op)];
+
+ /* JMP */
+ else if (OPCODE_JMP(op))
+ addr = linux_regs->regs[OPCODE_JMP_REG(op)];
+
+ /* JSR */
+ else if (OPCODE_JSR(op))
+ addr = linux_regs->regs[OPCODE_JSR_REG(op)];
+
+ /* RTS */
+ else if (OPCODE_RTS(op))
+ addr = linux_regs->pr;
+
+ /* RTE */
+ else if (OPCODE_RTE(op))
+ addr = linux_regs->regs[15];
+
+ /* Other */
+ else
+ addr = linux_regs->pc + instruction_size(op);
+
+ flush_icache_range(addr, addr + instruction_size(op));
+ return (short *)addr;
+}
+
+/*
+ * Replace the instruction immediately after the current instruction
+ * (i.e. next in the expected flow of control) with a trap instruction,
+ * so that returning will cause only a single instruction to be executed.
+ * Note that this model is slightly broken for instructions with delay
+ * slots (e.g. B[TF]S, BSR, BRA etc), where both the branch and the
+ * instruction in the delay slot will be executed.
+ */
+
+static unsigned long stepped_address;
+static opcode_t stepped_opcode;
+
+static void do_single_step(struct pt_regs *linux_regs)
+{
+ /* Determine where the target instruction will send us to */
+ unsigned short *addr = get_step_address(linux_regs);
+
+ stepped_address = (int)addr;
+
+ /* Replace it */
+ stepped_opcode = __raw_readw((long)addr);
+ *addr = STEP_OPCODE;
+
+ /* Flush and return */
+ flush_icache_range((long)addr, (long)addr +
+ instruction_size(stepped_opcode));
+}
+
+/* Undo a single step */
+static void undo_single_step(struct pt_regs *linux_regs)
+{
+ /* If we have stepped, put back the old instruction */
+ /* Use stepped_address in case we stopped elsewhere */
+ if (stepped_opcode != 0) {
+ __raw_writew(stepped_opcode, stepped_address);
+ flush_icache_range(stepped_address, stepped_address + 2);
+ }
+
+ stepped_opcode = 0;
+}
+
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ gdb_regs[GDB_R0 + i] = regs->regs[i];
+
+ gdb_regs[GDB_PC] = regs->pc;
+ gdb_regs[GDB_PR] = regs->pr;
+ gdb_regs[GDB_SR] = regs->sr;
+ gdb_regs[GDB_GBR] = regs->gbr;
+ gdb_regs[GDB_MACH] = regs->mach;
+ gdb_regs[GDB_MACL] = regs->macl;
+
+ __asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR]));
+}
+
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i];
+
+ regs->pc = gdb_regs[GDB_PC];
+ regs->pr = gdb_regs[GDB_PR];
+ regs->sr = gdb_regs[GDB_SR];
+ regs->gbr = gdb_regs[GDB_GBR];
+ regs->mach = gdb_regs[GDB_MACH];
+ regs->macl = gdb_regs[GDB_MACL];
+
+ __asm__ __volatile__ ("ldc %0, vbr" : : "r" (gdb_regs[GDB_VBR]));
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+ gdb_regs[GDB_R15] = p->thread.sp;
+ gdb_regs[GDB_PC] = p->thread.pc;
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ char *remcomInBuffer, char *remcomOutBuffer,
+ struct pt_regs *linux_regs)
+{
+ unsigned long addr;
+ char *ptr;
+
+ /* Undo any stepping we may have done */
+ undo_single_step(linux_regs);
+
+ switch (remcomInBuffer[0]) {
+ case 'c':
+ case 's':
+ /* try to read optional parameter, pc unchanged if no parm */
+ ptr = &remcomInBuffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ linux_regs->pc = addr;
+ case 'D':
+ case 'k':
+ atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+ if (remcomInBuffer[0] == 's') {
+ do_single_step(linux_regs);
+ kgdb_single_step = 1;
+
+ atomic_set(&kgdb_cpu_doing_single_step,
+ raw_smp_processor_id());
+ }
+
+ return 0;
+ }
+
+ /* this means that we do not want to exit from the handler: */
+ return -1;
+}
+
+/*
+ * The primary entry points for the kgdb debug trap table entries.
+ */
+BUILD_TRAP_HANDLER(singlestep)
+{
+ unsigned long flags;
+ TRAP_HANDLER_DECL;
+
+ local_irq_save(flags);
+ regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
+ kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs);
+ local_irq_restore(flags);
+}
+
+
+BUILD_TRAP_HANDLER(breakpoint)
+{
+ unsigned long flags;
+ TRAP_HANDLER_DECL;
+
+ local_irq_save(flags);
+ kgdb_handle_exception(vec >> 2, SIGTRAP, 0, regs);
+ local_irq_restore(flags);
+}
+
+int kgdb_arch_init(void)
+{
+ return 0;
+}
+
+void kgdb_arch_exit(void)
+{
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ /* Breakpoint instruction: trapa #0x3c */
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ .gdb_bpt_instr = { 0x3c, 0xc3 },
+#else
+ .gdb_bpt_instr = { 0xc3, 0x3c },
+#endif
+};
diff --git a/arch/sh/kernel/kgdb_jmp.S b/arch/sh/kernel/kgdb_jmp.S
deleted file mode 100644
index 339bb1d7ff0b..000000000000
--- a/arch/sh/kernel/kgdb_jmp.S
+++ /dev/null
@@ -1,33 +0,0 @@
-#include <linux/linkage.h>
-
-ENTRY(setjmp)
- add #(9*4), r4
- sts.l pr, @-r4
- mov.l r15, @-r4
- mov.l r14, @-r4
- mov.l r13, @-r4
- mov.l r12, @-r4
- mov.l r11, @-r4
- mov.l r10, @-r4
- mov.l r9, @-r4
- mov.l r8, @-r4
- rts
- mov #0, r0
-
-ENTRY(longjmp)
- mov.l @r4+, r8
- mov.l @r4+, r9
- mov.l @r4+, r10
- mov.l @r4+, r11
- mov.l @r4+, r12
- mov.l @r4+, r13
- mov.l @r4+, r14
- mov.l @r4+, r15
- lds.l @r4+, pr
- mov r5, r0
- tst r0, r0
- bf 1f
- mov #1, r0 ! in case val==0
-1: rts
- nop
-
diff --git a/arch/sh/kernel/kgdb_stub.c b/arch/sh/kernel/kgdb_stub.c
deleted file mode 100644
index bf8ac4c71640..000000000000
--- a/arch/sh/kernel/kgdb_stub.c
+++ /dev/null
@@ -1,1052 +0,0 @@
-/*
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Contains extracts from code by Glenn Engel, Jim Kingdon,
- * David Grothe <dave@gcom.com>, Tigran Aivazian <tigran@sco.com>,
- * Amit S. Kale <akale@veritas.com>, William Gatliff <bgat@open-widgets.com>,
- * Ben Lee, Steve Chamberlain and Benoit Miller <fulg@iname.com>.
- *
- * This version by Henry Bell <henry.bell@st.com>
- * Minor modifications by Jeremy Siegel <jsiegel@mvista.com>
- *
- * Contains low-level support for remote debug using GDB.
- *
- * To enable debugger support, two things need to happen. A call to
- * set_debug_traps() is necessary in order to allow any breakpoints
- * or error conditions to be properly intercepted and reported to gdb.
- * A breakpoint also needs to be generated to begin communication. This
- * is most easily accomplished by a call to breakpoint() which does
- * a trapa if the initialisation phase has been successfully completed.
- *
- * In this case, set_debug_traps() is not used to "take over" exceptions;
- * other kernel code is modified instead to enter the kgdb functions here
- * when appropriate (see entry.S for breakpoint traps and NMI interrupts,
- * see traps.c for kernel error exceptions).
- *
- * The following gdb commands are supported:
- *
- * Command Function Return value
- *
- * g return the value of the CPU registers hex data or ENN
- * G set the value of the CPU registers OK or ENN
- *
- * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
- * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
- * XAA..AA,LLLL: Same, but data is binary (not hex) OK or ENN
- *
- * c Resume at current address SNN ( signal NN)
- * cAA..AA Continue at address AA..AA SNN
- * CNN; Resume at current address with signal SNN
- * CNN;AA..AA Resume at address AA..AA with signal SNN
- *
- * s Step one instruction SNN
- * sAA..AA Step one instruction from AA..AA SNN
- * SNN; Step one instruction with signal SNN
- * SNNAA..AA Step one instruction from AA..AA w/NN SNN
- *
- * k kill (Detach GDB)
- *
- * d Toggle debug flag
- * D Detach GDB
- *
- * Hct Set thread t for operations, OK or ENN
- * c = 'c' (step, cont), c = 'g' (other
- * operations)
- *
- * qC Query current thread ID QCpid
- * qfThreadInfo Get list of current threads (first) m<id>
- * qsThreadInfo " " " " " (subsequent)
- * qOffsets Get section offsets Text=x;Data=y;Bss=z
- *
- * TXX Find if thread XX is alive OK or ENN
- * ? What was the last sigval ? SNN (signal NN)
- * O Output to GDB console
- *
- * Remote communication protocol.
- *
- * A debug packet whose contents are <data> is encapsulated for
- * transmission in the form:
- *
- * $ <data> # CSUM1 CSUM2
- *
- * <data> must be ASCII alphanumeric and cannot include characters
- * '$' or '#'. If <data> starts with two characters followed by
- * ':', then the existing stubs interpret this as a sequence number.
- *
- * CSUM1 and CSUM2 are ascii hex representation of an 8-bit
- * checksum of <data>, the most significant nibble is sent first.
- * the hex digits 0-9,a-f are used.
- *
- * Receiver responds with:
- *
- * + - if CSUM is correct and ready for next packet
- * - - if CSUM is incorrect
- *
- * Responses can be run-length encoded to save space. A '*' means that
- * the next character is an ASCII encoding giving a repeat count which
- * stands for that many repetitions of the character preceding the '*'.
- * The encoding is n+29, yielding a printable character where n >=3
- * (which is where RLE starts to win). Don't use an n > 126.
- *
- * So "0* " means the same as "0000".
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
-#include <linux/delay.h>
-#include <linux/linkage.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/module.h>
-#include <asm/system.h>
-#include <asm/cacheflush.h>
-#include <asm/current.h>
-#include <asm/signal.h>
-#include <asm/pgtable.h>
-#include <asm/ptrace.h>
-#include <asm/kgdb.h>
-#include <asm/io.h>
-
-/* Function pointers for linkage */
-kgdb_debug_hook_t *kgdb_debug_hook;
-kgdb_bus_error_hook_t *kgdb_bus_err_hook;
-
-int (*kgdb_getchar)(void);
-EXPORT_SYMBOL_GPL(kgdb_getchar);
-void (*kgdb_putchar)(int);
-EXPORT_SYMBOL_GPL(kgdb_putchar);
-
-static void put_debug_char(int c)
-{
- if (!kgdb_putchar)
- return;
- (*kgdb_putchar)(c);
-}
-static int get_debug_char(void)
-{
- if (!kgdb_getchar)
- return -1;
- return (*kgdb_getchar)();
-}
-
-/* Num chars in in/out bound buffers, register packets need NUMREGBYTES * 2 */
-#define BUFMAX 1024
-#define NUMREGBYTES (MAXREG*4)
-#define OUTBUFMAX (NUMREGBYTES*2+512)
-
-enum {
- R0 = 0, R1, R2, R3, R4, R5, R6, R7,
- R8, R9, R10, R11, R12, R13, R14, R15,
- PC, PR, GBR, VBR, MACH, MACL, SR,
- /* */
- MAXREG
-};
-
-static unsigned int registers[MAXREG];
-struct kgdb_regs trap_registers;
-
-char kgdb_in_gdb_mode;
-char in_nmi; /* Set during NMI to prevent reentry */
-int kgdb_nofault; /* Boolean to ignore bus errs (i.e. in GDB) */
-
-/* Default values for SCI (can override via kernel args in setup.c) */
-#ifndef CONFIG_KGDB_DEFPORT
-#define CONFIG_KGDB_DEFPORT 1
-#endif
-
-#ifndef CONFIG_KGDB_DEFBAUD
-#define CONFIG_KGDB_DEFBAUD 115200
-#endif
-
-#if defined(CONFIG_KGDB_DEFPARITY_E)
-#define CONFIG_KGDB_DEFPARITY 'E'
-#elif defined(CONFIG_KGDB_DEFPARITY_O)
-#define CONFIG_KGDB_DEFPARITY 'O'
-#else /* CONFIG_KGDB_DEFPARITY_N */
-#define CONFIG_KGDB_DEFPARITY 'N'
-#endif
-
-#ifdef CONFIG_KGDB_DEFBITS_7
-#define CONFIG_KGDB_DEFBITS '7'
-#else /* CONFIG_KGDB_DEFBITS_8 */
-#define CONFIG_KGDB_DEFBITS '8'
-#endif
-
-/* SCI/UART settings, used in kgdb_console_setup() */
-int kgdb_portnum = CONFIG_KGDB_DEFPORT;
-EXPORT_SYMBOL_GPL(kgdb_portnum);
-int kgdb_baud = CONFIG_KGDB_DEFBAUD;
-EXPORT_SYMBOL_GPL(kgdb_baud);
-char kgdb_parity = CONFIG_KGDB_DEFPARITY;
-EXPORT_SYMBOL_GPL(kgdb_parity);
-char kgdb_bits = CONFIG_KGDB_DEFBITS;
-EXPORT_SYMBOL_GPL(kgdb_bits);
-
-/* Jump buffer for setjmp/longjmp */
-static jmp_buf rem_com_env;
-
-/* TRA differs sh3/4 */
-#if defined(CONFIG_CPU_SH3)
-#define TRA 0xffffffd0
-#elif defined(CONFIG_CPU_SH4)
-#define TRA 0xff000020
-#endif
-
-/* Macros for single step instruction identification */
-#define OPCODE_BT(op) (((op) & 0xff00) == 0x8900)
-#define OPCODE_BF(op) (((op) & 0xff00) == 0x8b00)
-#define OPCODE_BTF_DISP(op) (((op) & 0x80) ? (((op) | 0xffffff80) << 1) : \
- (((op) & 0x7f ) << 1))
-#define OPCODE_BFS(op) (((op) & 0xff00) == 0x8f00)
-#define OPCODE_BTS(op) (((op) & 0xff00) == 0x8d00)
-#define OPCODE_BRA(op) (((op) & 0xf000) == 0xa000)
-#define OPCODE_BRA_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
- (((op) & 0x7ff) << 1))
-#define OPCODE_BRAF(op) (((op) & 0xf0ff) == 0x0023)
-#define OPCODE_BRAF_REG(op) (((op) & 0x0f00) >> 8)
-#define OPCODE_BSR(op) (((op) & 0xf000) == 0xb000)
-#define OPCODE_BSR_DISP(op) (((op) & 0x800) ? (((op) | 0xfffff800) << 1) : \
- (((op) & 0x7ff) << 1))
-#define OPCODE_BSRF(op) (((op) & 0xf0ff) == 0x0003)
-#define OPCODE_BSRF_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_JMP(op) (((op) & 0xf0ff) == 0x402b)
-#define OPCODE_JMP_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_JSR(op) (((op) & 0xf0ff) == 0x400b)
-#define OPCODE_JSR_REG(op) (((op) >> 8) & 0xf)
-#define OPCODE_RTS(op) ((op) == 0xb)
-#define OPCODE_RTE(op) ((op) == 0x2b)
-
-#define SR_T_BIT_MASK 0x1
-#define STEP_OPCODE 0xc320
-#define BIOS_CALL_TRAP 0x3f
-
-/* Exception codes as per SH-4 core manual */
-#define ADDRESS_ERROR_LOAD_VEC 7
-#define ADDRESS_ERROR_STORE_VEC 8
-#define TRAP_VEC 11
-#define INVALID_INSN_VEC 12
-#define INVALID_SLOT_VEC 13
-#define NMI_VEC 14
-#define USER_BREAK_VEC 15
-#define SERIAL_BREAK_VEC 58
-
-/* Misc static */
-static int stepped_address;
-static short stepped_opcode;
-static char in_buffer[BUFMAX];
-static char out_buffer[OUTBUFMAX];
-
-static void kgdb_to_gdb(const char *s);
-
-/* Convert ch to hex */
-static int hex(const char ch)
-{
- if ((ch >= 'a') && (ch <= 'f'))
- return (ch - 'a' + 10);
- if ((ch >= '0') && (ch <= '9'))
- return (ch - '0');
- if ((ch >= 'A') && (ch <= 'F'))
- return (ch - 'A' + 10);
- return (-1);
-}
-
-/* Convert the memory pointed to by mem into hex, placing result in buf.
- Returns a pointer to the last char put in buf (null) */
-static char *mem_to_hex(const char *mem, char *buf, const int count)
-{
- int i;
- int ch;
- unsigned short s_val;
- unsigned long l_val;
-
- /* Check for 16 or 32 */
- if (count == 2 && ((long) mem & 1) == 0) {
- s_val = *(unsigned short *) mem;
- mem = (char *) &s_val;
- } else if (count == 4 && ((long) mem & 3) == 0) {
- l_val = *(unsigned long *) mem;
- mem = (char *) &l_val;
- }
- for (i = 0; i < count; i++) {
- ch = *mem++;
- buf = pack_hex_byte(buf, ch);
- }
- *buf = 0;
- return (buf);
-}
-
-/* Convert the hex array pointed to by buf into binary, to be placed in mem.
- Return a pointer to the character after the last byte written */
-static char *hex_to_mem(const char *buf, char *mem, const int count)
-{
- int i;
- unsigned char ch;
-
- for (i = 0; i < count; i++) {
- ch = hex(*buf++) << 4;
- ch = ch + hex(*buf++);
- *mem++ = ch;
- }
- return (mem);
-}
-
-/* While finding valid hex chars, convert to an integer, then return it */
-static int hex_to_int(char **ptr, int *int_value)
-{
- int num_chars = 0;
- int hex_value;
-
- *int_value = 0;
-
- while (**ptr) {
- hex_value = hex(**ptr);
- if (hex_value >= 0) {
- *int_value = (*int_value << 4) | hex_value;
- num_chars++;
- } else
- break;
- (*ptr)++;
- }
- return num_chars;
-}
-
-/* Copy the binary array pointed to by buf into mem. Fix $, #,
- and 0x7d escaped with 0x7d. Return a pointer to the character
- after the last byte written. */
-static char *ebin_to_mem(const char *buf, char *mem, int count)
-{
- for (; count > 0; count--, buf++) {
- if (*buf == 0x7d)
- *mem++ = *(++buf) ^ 0x20;
- else
- *mem++ = *buf;
- }
- return mem;
-}
-
-/* Scan for the start char '$', read the packet and check the checksum */
-static void get_packet(char *buffer, int buflen)
-{
- unsigned char checksum;
- unsigned char xmitcsum;
- int i;
- int count;
- char ch;
-
- do {
- /* Ignore everything until the start character */
- while ((ch = get_debug_char()) != '$');
-
- checksum = 0;
- xmitcsum = -1;
- count = 0;
-
- /* Now, read until a # or end of buffer is found */
- while (count < (buflen - 1)) {
- ch = get_debug_char();
-
- if (ch == '#')
- break;
-
- checksum = checksum + ch;
- buffer[count] = ch;
- count = count + 1;
- }
-
- buffer[count] = 0;
-
- /* Continue to read checksum following # */
- if (ch == '#') {
- xmitcsum = hex(get_debug_char()) << 4;
- xmitcsum += hex(get_debug_char());
-
- /* Checksum */
- if (checksum != xmitcsum)
- put_debug_char('-'); /* Failed checksum */
- else {
- /* Ack successful transfer */
- put_debug_char('+');
-
- /* If a sequence char is present, reply
- the sequence ID */
- if (buffer[2] == ':') {
- put_debug_char(buffer[0]);
- put_debug_char(buffer[1]);
-
- /* Remove sequence chars from buffer */
- count = strlen(buffer);
- for (i = 3; i <= count; i++)
- buffer[i - 3] = buffer[i];
- }
- }
- }
- }
- while (checksum != xmitcsum); /* Keep trying while we fail */
-}
-
-/* Send the packet in the buffer with run-length encoding */
-static void put_packet(char *buffer)
-{
- int checksum;
- char *src;
- int runlen;
- int encode;
-
- do {
- src = buffer;
- put_debug_char('$');
- checksum = 0;
-
- /* Continue while we still have chars left */
- while (*src) {
- /* Check for runs up to 99 chars long */
- for (runlen = 1; runlen < 99; runlen++) {
- if (src[0] != src[runlen])
- break;
- }
-
- if (runlen > 3) {
- /* Got a useful amount, send encoding */
- encode = runlen + ' ' - 4;
- put_debug_char(*src); checksum += *src;
- put_debug_char('*'); checksum += '*';
- put_debug_char(encode); checksum += encode;
- src += runlen;
- } else {
- /* Otherwise just send the current char */
- put_debug_char(*src); checksum += *src;
- src += 1;
- }
- }
-
- /* '#' Separator, put high and low components of checksum */
- put_debug_char('#');
- put_debug_char(hex_asc_hi(checksum));
- put_debug_char(hex_asc_lo(checksum));
- }
- while ((get_debug_char()) != '+'); /* While no ack */
-}
-
-/* A bus error has occurred - perform a longjmp to return execution and
- allow handling of the error */
-static void kgdb_handle_bus_error(void)
-{
- longjmp(rem_com_env, 1);
-}
-
-/* Translate SH-3/4 exception numbers to unix-like signal values */
-static int compute_signal(const int excep_code)
-{
- int sigval;
-
- switch (excep_code) {
-
- case INVALID_INSN_VEC:
- case INVALID_SLOT_VEC:
- sigval = SIGILL;
- break;
- case ADDRESS_ERROR_LOAD_VEC:
- case ADDRESS_ERROR_STORE_VEC:
- sigval = SIGSEGV;
- break;
-
- case SERIAL_BREAK_VEC:
- case NMI_VEC:
- sigval = SIGINT;
- break;
-
- case USER_BREAK_VEC:
- case TRAP_VEC:
- sigval = SIGTRAP;
- break;
-
- default:
- sigval = SIGBUS; /* "software generated" */
- break;
- }
-
- return (sigval);
-}
-
-/* Make a local copy of the registers passed into the handler (bletch) */
-static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs,
- int *gdb_regs)
-{
- gdb_regs[R0] = regs->regs[R0];
- gdb_regs[R1] = regs->regs[R1];
- gdb_regs[R2] = regs->regs[R2];
- gdb_regs[R3] = regs->regs[R3];
- gdb_regs[R4] = regs->regs[R4];
- gdb_regs[R5] = regs->regs[R5];
- gdb_regs[R6] = regs->regs[R6];
- gdb_regs[R7] = regs->regs[R7];
- gdb_regs[R8] = regs->regs[R8];
- gdb_regs[R9] = regs->regs[R9];
- gdb_regs[R10] = regs->regs[R10];
- gdb_regs[R11] = regs->regs[R11];
- gdb_regs[R12] = regs->regs[R12];
- gdb_regs[R13] = regs->regs[R13];
- gdb_regs[R14] = regs->regs[R14];
- gdb_regs[R15] = regs->regs[R15];
- gdb_regs[PC] = regs->pc;
- gdb_regs[PR] = regs->pr;
- gdb_regs[GBR] = regs->gbr;
- gdb_regs[MACH] = regs->mach;
- gdb_regs[MACL] = regs->macl;
- gdb_regs[SR] = regs->sr;
- gdb_regs[VBR] = regs->vbr;
-}
-
-/* Copy local gdb registers back to kgdb regs, for later copy to kernel */
-static void gdb_regs_to_kgdb_regs(const int *gdb_regs,
- struct kgdb_regs *regs)
-{
- regs->regs[R0] = gdb_regs[R0];
- regs->regs[R1] = gdb_regs[R1];
- regs->regs[R2] = gdb_regs[R2];
- regs->regs[R3] = gdb_regs[R3];
- regs->regs[R4] = gdb_regs[R4];
- regs->regs[R5] = gdb_regs[R5];
- regs->regs[R6] = gdb_regs[R6];
- regs->regs[R7] = gdb_regs[R7];
- regs->regs[R8] = gdb_regs[R8];
- regs->regs[R9] = gdb_regs[R9];
- regs->regs[R10] = gdb_regs[R10];
- regs->regs[R11] = gdb_regs[R11];
- regs->regs[R12] = gdb_regs[R12];
- regs->regs[R13] = gdb_regs[R13];
- regs->regs[R14] = gdb_regs[R14];
- regs->regs[R15] = gdb_regs[R15];
- regs->pc = gdb_regs[PC];
- regs->pr = gdb_regs[PR];
- regs->gbr = gdb_regs[GBR];
- regs->mach = gdb_regs[MACH];
- regs->macl = gdb_regs[MACL];
- regs->sr = gdb_regs[SR];
- regs->vbr = gdb_regs[VBR];
-}
-
-/* Calculate the new address for after a step */
-static short *get_step_address(void)
-{
- short op = *(short *) trap_registers.pc;
- long addr;
-
- /* BT */
- if (OPCODE_BT(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BTS */
- else if (OPCODE_BTS(op)) {
- if (trap_registers.sr & SR_T_BIT_MASK)
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BF */
- else if (OPCODE_BF(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 2;
- }
-
- /* BFS */
- else if (OPCODE_BFS(op)) {
- if (!(trap_registers.sr & SR_T_BIT_MASK))
- addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op);
- else
- addr = trap_registers.pc + 4; /* Not in delay slot */
- }
-
- /* BRA */
- else if (OPCODE_BRA(op))
- addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op);
-
- /* BRAF */
- else if (OPCODE_BRAF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BRAF_REG(op)];
-
- /* BSR */
- else if (OPCODE_BSR(op))
- addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op);
-
- /* BSRF */
- else if (OPCODE_BSRF(op))
- addr = trap_registers.pc + 4
- + trap_registers.regs[OPCODE_BSRF_REG(op)];
-
- /* JMP */
- else if (OPCODE_JMP(op))
- addr = trap_registers.regs[OPCODE_JMP_REG(op)];
-
- /* JSR */
- else if (OPCODE_JSR(op))
- addr = trap_registers.regs[OPCODE_JSR_REG(op)];
-
- /* RTS */
- else if (OPCODE_RTS(op))
- addr = trap_registers.pr;
-
- /* RTE */
- else if (OPCODE_RTE(op))
- addr = trap_registers.regs[15];
-
- /* Other */
- else
- addr = trap_registers.pc + 2;
-
- flush_icache_range(addr, addr + 2);
- return (short *) addr;
-}
-
-/* Set up a single-step. Replace the instruction immediately after the
- current instruction (i.e. next in the expected flow of control) with a
- trap instruction, so that returning will cause only a single instruction
- to be executed. Note that this model is slightly broken for instructions
- with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch
- and the instruction in the delay slot will be executed. */
-static void do_single_step(void)
-{
- unsigned short *addr = 0;
-
- /* Determine where the target instruction will send us to */
- addr = get_step_address();
- stepped_address = (int)addr;
-
- /* Replace it */
- stepped_opcode = *(short *)addr;
- *addr = STEP_OPCODE;
-
- /* Flush and return */
- flush_icache_range((long) addr, (long) addr + 2);
-}
-
-/* Undo a single step */
-static void undo_single_step(void)
-{
- /* If we have stepped, put back the old instruction */
- /* Use stepped_address in case we stopped elsewhere */
- if (stepped_opcode != 0) {
- *(short*)stepped_address = stepped_opcode;
- flush_icache_range(stepped_address, stepped_address + 2);
- }
- stepped_opcode = 0;
-}
-
-/* Send a signal message */
-static void send_signal_msg(const int signum)
-{
- out_buffer[0] = 'S';
- out_buffer[1] = hex_asc_hi(signum);
- out_buffer[2] = hex_asc_lo(signum);
- out_buffer[3] = 0;
- put_packet(out_buffer);
-}
-
-/* Reply that all was well */
-static void send_ok_msg(void)
-{
- strcpy(out_buffer, "OK");
- put_packet(out_buffer);
-}
-
-/* Reply that an error occurred */
-static void send_err_msg(void)
-{
- strcpy(out_buffer, "E01");
- put_packet(out_buffer);
-}
-
-/* Empty message indicates unrecognised command */
-static void send_empty_msg(void)
-{
- put_packet("");
-}
-
-/* Read memory due to 'm' message */
-static void read_mem_msg(void)
-{
- char *ptr;
- int addr;
- int length;
-
- /* Jmp, disable bus error handler */
- if (setjmp(rem_com_env) == 0) {
-
- kgdb_nofault = 1;
-
- /* Walk through, have m<addr>,<length> */
- ptr = &in_buffer[1];
- if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
- if (hex_to_int(&ptr, &length)) {
- ptr = 0;
- if (length * 2 > OUTBUFMAX)
- length = OUTBUFMAX / 2;
- mem_to_hex((char *) addr, out_buffer, length);
- }
- if (ptr)
- send_err_msg();
- else
- put_packet(out_buffer);
- } else
- send_err_msg();
-
- /* Restore bus error handler */
- kgdb_nofault = 0;
-}
-
-/* Write memory due to 'M' or 'X' message */
-static void write_mem_msg(int binary)
-{
- char *ptr;
- int addr;
- int length;
-
- if (setjmp(rem_com_env) == 0) {
-
- kgdb_nofault = 1;
-
- /* Walk through, have M<addr>,<length>:<data> */
- ptr = &in_buffer[1];
- if (hex_to_int(&ptr, &addr) && (*ptr++ == ','))
- if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) {
- if (binary)
- ebin_to_mem(ptr, (char*)addr, length);
- else
- hex_to_mem(ptr, (char*)addr, length);
- flush_icache_range(addr, addr + length);
- ptr = 0;
- send_ok_msg();
- }
- if (ptr)
- send_err_msg();
- } else
- send_err_msg();
-
- /* Restore bus error handler */
- kgdb_nofault = 0;
-}
-
-/* Continue message */
-static void continue_msg(void)
-{
- /* Try to read optional parameter, PC unchanged if none */
- char *ptr = &in_buffer[1];
- int addr;
-
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Continue message with signal */
-static void continue_with_sig_msg(void)
-{
- int signal;
- char *ptr = &in_buffer[1];
- int addr;
-
- /* Report limitation */
- kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n");
-
- /* Signal */
- hex_to_int(&ptr, &signal);
- if (*ptr == ';')
- ptr++;
-
- /* Optional address */
- if (hex_to_int(&ptr, &addr))
- trap_registers.pc = addr;
-}
-
-/* Step message */
-static void step_msg(void)
-{
- continue_msg();
- do_single_step();
-}
-
-/* Step message with signal */
-static void step_with_sig_msg(void)
-{
- continue_with_sig_msg();
- do_single_step();
-}
-
-/* Send register contents */
-static void send_regs_msg(void)
-{
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- mem_to_hex((char *) registers, out_buffer, NUMREGBYTES);
- put_packet(out_buffer);
-}
-
-/* Set register contents - currently can't set other thread's registers */
-static void set_regs_msg(void)
-{
- kgdb_regs_to_gdb_regs(&trap_registers, registers);
- hex_to_mem(&in_buffer[1], (char *) registers, NUMREGBYTES);
- gdb_regs_to_kgdb_regs(registers, &trap_registers);
- send_ok_msg();
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-/*
- * Bring up the ports..
- */
-static int __init kgdb_serial_setup(void)
-{
- struct console dummy;
- return kgdb_console_setup(&dummy, 0);
-}
-#else
-#define kgdb_serial_setup() 0
-#endif
-
-/* The command loop, read and act on requests */
-static void kgdb_command_loop(const int excep_code, const int trapa_value)
-{
- int sigval;
-
- /* Enter GDB mode (e.g. after detach) */
- if (!kgdb_in_gdb_mode) {
- /* Do serial setup, notify user, issue preemptive ack */
- printk(KERN_NOTICE "KGDB: Waiting for GDB\n");
- kgdb_in_gdb_mode = 1;
- put_debug_char('+');
- }
-
- /* Reply to host that an exception has occurred */
- sigval = compute_signal(excep_code);
- send_signal_msg(sigval);
-
- /* TRAP_VEC exception indicates a software trap inserted in place of
- code by GDB so back up PC by one instruction, as this instruction
- will later be replaced by its original one. Do NOT do this for
- trap 0xff, since that indicates a compiled-in breakpoint which
- will not be replaced (and we would retake the trap forever) */
- if ((excep_code == TRAP_VEC) && (trapa_value != (0x3c << 2)))
- trap_registers.pc -= 2;
-
- /* Undo any stepping we may have done */
- undo_single_step();
-
- while (1) {
- out_buffer[0] = 0;
- get_packet(in_buffer, BUFMAX);
-
- /* Examine first char of buffer to see what we need to do */
- switch (in_buffer[0]) {
- case '?': /* Send which signal we've received */
- send_signal_msg(sigval);
- break;
-
- case 'g': /* Return the values of the CPU registers */
- send_regs_msg();
- break;
-
- case 'G': /* Set the value of the CPU registers */
- set_regs_msg();
- break;
-
- case 'm': /* Read LLLL bytes address AA..AA */
- read_mem_msg();
- break;
-
- case 'M': /* Write LLLL bytes address AA..AA, ret OK */
- write_mem_msg(0); /* 0 = data in hex */
- break;
-
- case 'X': /* Write LLLL bytes esc bin address AA..AA */
- if (kgdb_bits == '8')
- write_mem_msg(1); /* 1 = data in binary */
- else
- send_empty_msg();
- break;
-
- case 'C': /* Continue, signum included, we ignore it */
- continue_with_sig_msg();
- return;
-
- case 'c': /* Continue at address AA..AA (optional) */
- continue_msg();
- return;
-
- case 'S': /* Step, signum included, we ignore it */
- step_with_sig_msg();
- return;
-
- case 's': /* Step one instruction from AA..AA */
- step_msg();
- return;
-
- case 'k': /* 'Kill the program' with a kernel ? */
- break;
-
- case 'D': /* Detach from program, send reply OK */
- kgdb_in_gdb_mode = 0;
- send_ok_msg();
- get_debug_char();
- return;
-
- default:
- send_empty_msg();
- break;
- }
- }
-}
-
-/* There has been an exception, most likely a breakpoint. */
-static void handle_exception(struct pt_regs *regs)
-{
- int excep_code, vbr_val;
- int count;
- int trapa_value = ctrl_inl(TRA);
-
- /* Copy kernel regs (from stack) */
- for (count = 0; count < 16; count++)
- trap_registers.regs[count] = regs->regs[count];
- trap_registers.pc = regs->pc;
- trap_registers.pr = regs->pr;
- trap_registers.sr = regs->sr;
- trap_registers.gbr = regs->gbr;
- trap_registers.mach = regs->mach;
- trap_registers.macl = regs->macl;
-
- asm("stc vbr, %0":"=r"(vbr_val));
- trap_registers.vbr = vbr_val;
-
- /* Get excode for command loop call, user access */
- asm("stc r2_bank, %0":"=r"(excep_code));
-
- /* Act on the exception */
- kgdb_command_loop(excep_code, trapa_value);
-
- /* Copy back the (maybe modified) registers */
- for (count = 0; count < 16; count++)
- regs->regs[count] = trap_registers.regs[count];
- regs->pc = trap_registers.pc;
- regs->pr = trap_registers.pr;
- regs->sr = trap_registers.sr;
- regs->gbr = trap_registers.gbr;
- regs->mach = trap_registers.mach;
- regs->macl = trap_registers.macl;
-
- vbr_val = trap_registers.vbr;
- asm("ldc %0, vbr": :"r"(vbr_val));
-}
-
-asmlinkage void kgdb_handle_exception(unsigned long r4, unsigned long r5,
- unsigned long r6, unsigned long r7,
- struct pt_regs __regs)
-{
- struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
- handle_exception(regs);
-}
-
-/* Initialise the KGDB data structures and serial configuration */
-int __init kgdb_init(void)
-{
- in_nmi = 0;
- kgdb_nofault = 0;
- stepped_opcode = 0;
- kgdb_in_gdb_mode = 0;
-
- if (kgdb_serial_setup() != 0) {
- printk(KERN_NOTICE "KGDB: serial setup error\n");
- return -1;
- }
-
- /* Init ptr to exception handler */
- kgdb_debug_hook = handle_exception;
- kgdb_bus_err_hook = kgdb_handle_bus_error;
-
- /* Enter kgdb now if requested, or just report init done */
- printk(KERN_NOTICE "KGDB: stub is initialized.\n");
-
- return 0;
-}
-
-/* Make function available for "user messages"; console will use it too. */
-
-char gdbmsgbuf[BUFMAX];
-#define MAXOUT ((BUFMAX-2)/2)
-
-static void kgdb_msg_write(const char *s, unsigned count)
-{
- int i;
- int wcount;
- char *bufptr;
-
- /* 'O'utput */
- gdbmsgbuf[0] = 'O';
-
- /* Fill and send buffers... */
- while (count > 0) {
- bufptr = gdbmsgbuf + 1;
-
- /* Calculate how many this time */
- wcount = (count > MAXOUT) ? MAXOUT : count;
-
- /* Pack in hex chars */
- for (i = 0; i < wcount; i++)
- bufptr = pack_hex_byte(bufptr, s[i]);
- *bufptr = '\0';
-
- /* Move up */
- s += wcount;
- count -= wcount;
-
- /* Write packet */
- put_packet(gdbmsgbuf);
- }
-}
-
-static void kgdb_to_gdb(const char *s)
-{
- kgdb_msg_write(s, strlen(s));
-}
-
-#ifdef CONFIG_SH_KGDB_CONSOLE
-void kgdb_console_write(struct console *co, const char *s, unsigned count)
-{
- /* Bail if we're not talking to GDB */
- if (!kgdb_in_gdb_mode)
- return;
-
- kgdb_msg_write(s, count);
-}
-#endif
-
-#ifdef CONFIG_KGDB_SYSRQ
-static void sysrq_handle_gdb(int key, struct tty_struct *tty)
-{
- printk("Entering GDB stub\n");
- breakpoint();
-}
-
-static struct sysrq_key_op sysrq_gdb_op = {
- .handler = sysrq_handle_gdb,
- .help_msg = "Gdb",
- .action_msg = "GDB",
-};
-
-static int gdb_register_sysrq(void)
-{
- printk("Registering GDB sysrq handler\n");
- register_sysrq_key('g', &sysrq_gdb_op);
- return 0;
-}
-module_init(gdb_register_sysrq);
-#endif
diff --git a/arch/sh/kernel/pm.c b/arch/sh/kernel/pm.c
deleted file mode 100644
index 10ab62c9aede..000000000000
--- a/arch/sh/kernel/pm.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Generic Power Management Routine
- *
- * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License.
- */
-#include <linux/suspend.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <asm/freq.h>
-#include <asm/io.h>
-#include <asm/watchdog.h>
-#include <asm/pm.h>
-
-#define INTR_OFFSET 0x600
-
-#define STBCR 0xffffff82
-#define STBCR2 0xffffff88
-
-#define STBCR_STBY 0x80
-#define STBCR_MSTP2 0x04
-
-#define MCR 0xffffff68
-#define RTCNT 0xffffff70
-
-#define MCR_RMODE 2
-#define MCR_RFSH 4
-
-void pm_enter(void)
-{
- u8 stbcr, csr;
- u16 frqcr, mcr;
- u32 vbr_new, vbr_old;
-
- set_bl_bit();
-
- /* set wdt */
- csr = sh_wdt_read_csr();
- csr &= ~WTCSR_TME;
- csr |= WTCSR_CKS_4096;
- sh_wdt_write_csr(csr);
- csr = sh_wdt_read_csr();
- sh_wdt_write_cnt(0);
-
- /* disable PLL1 */
- frqcr = ctrl_inw(FRQCR);
- frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY);
- ctrl_outw(frqcr, FRQCR);
-
- /* enable standby */
- stbcr = ctrl_inb(STBCR);
- ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR);
-
- /* set self-refresh */
- mcr = ctrl_inw(MCR);
- ctrl_outw(mcr & ~MCR_RFSH, MCR);
-
- /* set interrupt handler */
- asm volatile("stc vbr, %0" : "=r" (vbr_old));
- vbr_new = get_zeroed_page(GFP_ATOMIC);
- udelay(50);
- memcpy((void*)(vbr_new + INTR_OFFSET),
- &wakeup_start, &wakeup_end - &wakeup_start);
- asm volatile("ldc %0, vbr" : : "r" (vbr_new));
-
- ctrl_outw(0, RTCNT);
- ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR);
-
- cpu_sleep();
-
- asm volatile("ldc %0, vbr" : : "r" (vbr_old));
-
- free_page(vbr_new);
-
- /* enable PLL1 */
- frqcr = ctrl_inw(FRQCR);
- frqcr |= FRQCR_PSTBY;
- ctrl_outw(frqcr, FRQCR);
- udelay(50);
- frqcr |= FRQCR_PLLEN;
- ctrl_outw(frqcr, FRQCR);
-
- ctrl_outb(stbcr, STBCR);
-
- clear_bl_bit();
-}
diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c
index b965f0282c7d..ddafbbbab2ab 100644
--- a/arch/sh/kernel/process_32.c
+++ b/arch/sh/kernel/process_32.c
@@ -32,65 +32,8 @@
#include <asm/fpu.h>
#include <asm/syscalls.h>
-static int hlt_counter;
int ubc_usercnt = 0;
-void (*pm_idle)(void);
-void (*pm_power_off)(void);
-EXPORT_SYMBOL(pm_power_off);
-
-static int __init nohlt_setup(char *__unused)
-{
- hlt_counter = 1;
- return 1;
-}
-__setup("nohlt", nohlt_setup);
-
-static int __init hlt_setup(char *__unused)
-{
- hlt_counter = 0;
- return 1;
-}
-__setup("hlt", hlt_setup);
-
-static void default_idle(void)
-{
- if (!hlt_counter) {
- clear_thread_flag(TIF_POLLING_NRFLAG);
- smp_mb__after_clear_bit();
- set_bl_bit();
- while (!need_resched())
- cpu_sleep();
- clear_bl_bit();
- set_thread_flag(TIF_POLLING_NRFLAG);
- } else
- while (!need_resched())
- cpu_relax();
-}
-
-void cpu_idle(void)
-{
- set_thread_flag(TIF_POLLING_NRFLAG);
-
- /* endless idle loop with no priority at all */
- while (1) {
- void (*idle)(void) = pm_idle;
-
- if (!idle)
- idle = default_idle;
-
- tick_nohz_stop_sched_tick(1);
- while (!need_resched())
- idle();
- tick_nohz_restart_sched_tick();
-
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- check_pgt_cache();
- }
-}
-
void machine_restart(char * __unused)
{
/* SR.BL=1 and invoke address error to let CPU reset (manual reset) */
@@ -115,8 +58,8 @@ void machine_power_off(void)
void show_regs(struct pt_regs * regs)
{
printk("\n");
- printk("Pid : %d, Comm: %20s\n", task_pid_nr(current), current->comm);
- printk("CPU : %d %s (%s %.*s)\n",
+ printk("Pid : %d, Comm: \t\t%s\n", task_pid_nr(current), current->comm);
+ printk("CPU : %d \t\t%s (%s %.*s)\n\n",
smp_processor_id(), print_tainted(), init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
@@ -148,26 +91,16 @@ void show_regs(struct pt_regs * regs)
regs->mach, regs->macl, regs->gbr, regs->pr);
show_trace(NULL, (unsigned long *)regs->regs[15], regs);
+ show_code(regs);
}
/*
* Create a kernel thread
*/
-
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- */
-extern void kernel_thread_helper(void);
-__asm__(".align 5\n"
- "kernel_thread_helper:\n\t"
- "jsr @r5\n\t"
- " nop\n\t"
- "mov.l 1f, r1\n\t"
- "jsr @r1\n\t"
- " mov r0, r4\n\t"
- ".align 2\n\t"
- "1:.long do_exit");
+ATTRIB_NORET void kernel_thread_helper(void *arg, int (*fn)(void *))
+{
+ do_exit(fn(arg));
+}
/* Don't use this in BL=1(cli). Or else, CPU resets! */
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c
index b7aa09235b51..a7e5f2e74bac 100644
--- a/arch/sh/kernel/process_64.c
+++ b/arch/sh/kernel/process_64.c
@@ -23,7 +23,6 @@
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/proc_fs.h>
#include <linux/io.h>
#include <asm/syscalls.h>
#include <asm/uaccess.h>
@@ -33,56 +32,6 @@
struct task_struct *last_task_used_math = NULL;
-static int hlt_counter = 1;
-
-#define HARD_IDLE_TIMEOUT (HZ / 3)
-
-static int __init nohlt_setup(char *__unused)
-{
- hlt_counter = 1;
- return 1;
-}
-
-static int __init hlt_setup(char *__unused)
-{
- hlt_counter = 0;
- return 1;
-}
-
-__setup("nohlt", nohlt_setup);
-__setup("hlt", hlt_setup);
-
-static inline void hlt(void)
-{
- __asm__ __volatile__ ("sleep" : : : "memory");
-}
-
-/*
- * The idle loop on a uniprocessor SH..
- */
-void cpu_idle(void)
-{
- /* endless idle loop with no priority at all */
- while (1) {
- if (hlt_counter) {
- while (!need_resched())
- cpu_relax();
- } else {
- local_irq_disable();
- while (!need_resched()) {
- local_irq_enable();
- hlt();
- local_irq_disable();
- }
- local_irq_enable();
- }
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- }
-
-}
-
void machine_restart(char * __unused)
{
extern void phys_stext(void);
@@ -97,13 +46,6 @@ void machine_halt(void)
void machine_power_off(void)
{
-#if 0
- /* Disable watchdog timer */
- ctrl_outl(0xa5000000, WTCSR);
- /* Configure deep standby on sleep */
- ctrl_outl(0x03, STBCR);
-#endif
-
__asm__ __volatile__ (
"sleep\n\t"
"synci\n\t"
@@ -113,9 +55,6 @@ void machine_power_off(void)
panic("Unexpected wakeup!\n");
}
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
void show_regs(struct pt_regs * regs)
{
unsigned long long ah, al, bh, bl, ch, cl;
@@ -365,18 +304,6 @@ void show_regs(struct pt_regs * regs)
}
}
-struct task_struct * alloc_task_struct(void)
-{
- /* Get task descriptor pages */
- return (struct task_struct *)
- __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE));
-}
-
-void free_task_struct(struct task_struct *p)
-{
- free_pages((unsigned long) p, get_order(THREAD_SIZE));
-}
-
/*
* Create a kernel thread
*/
@@ -662,41 +589,3 @@ unsigned long get_wchan(struct task_struct *p)
#endif
return pc;
}
-
-/* Provide a /proc/asids file that lists out the
- ASIDs currently associated with the processes. (If the DM.PC register is
- examined through the debug link, this shows ASID + PC. To make use of this,
- the PID->ASID relationship needs to be known. This is primarily for
- debugging.)
- */
-
-#if defined(CONFIG_SH64_PROC_ASIDS)
-static int
-asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data)
-{
- int len=0;
- struct task_struct *p;
- read_lock(&tasklist_lock);
- for_each_process(p) {
- int pid = p->pid;
-
- if (!pid)
- continue;
- if (p->mm)
- len += sprintf(buf+len, "%5d : %02lx\n", pid,
- asid_cache(smp_processor_id()));
- else
- len += sprintf(buf+len, "%5d : (none)\n", pid);
- }
- read_unlock(&tasklist_lock);
- *eof = 1;
- return len;
-}
-
-static int __init register_proc_asids(void)
-{
- create_proc_read_entry("asids", 0, NULL, asids_proc_info, NULL);
- return 0;
-}
-__initcall(register_proc_asids);
-#endif
diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c
index e15b099c1f06..695097438f02 100644
--- a/arch/sh/kernel/ptrace_64.c
+++ b/arch/sh/kernel/ptrace_64.c
@@ -2,7 +2,7 @@
* arch/sh/kernel/ptrace_64.c
*
* Copyright (C) 2000, 2001 Paolo Alberelli
- * Copyright (C) 2003 - 2007 Paul Mundt
+ * Copyright (C) 2003 - 2008 Paul Mundt
*
* Started from SH3/4 version:
* SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
@@ -29,6 +29,8 @@
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <linux/tracehook.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -137,6 +139,165 @@ void user_disable_single_step(struct task_struct *child)
regs->sr &= ~SR_SSTEP;
}
+static int genregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct pt_regs *regs = task_pt_regs(target);
+ int ret;
+
+ /* PC, SR, SYSCALL */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &regs->pc,
+ 0, 3 * sizeof(unsigned long long));
+
+ /* R1 -> R63 */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ regs->regs,
+ offsetof(struct pt_regs, regs[0]),
+ 63 * sizeof(unsigned long long));
+ /* TR0 -> TR7 */
+ if (!ret)
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ regs->tregs,
+ offsetof(struct pt_regs, tregs[0]),
+ 8 * sizeof(unsigned long long));
+
+ if (!ret)
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ sizeof(struct pt_regs), -1);
+
+ return ret;
+}
+
+static int genregs_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 *regs = task_pt_regs(target);
+ int ret;
+
+ /* PC, SR, SYSCALL */
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &regs->pc,
+ 0, 3 * sizeof(unsigned long long));
+
+ /* R1 -> R63 */
+ if (!ret && count > 0)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ regs->regs,
+ offsetof(struct pt_regs, regs[0]),
+ 63 * sizeof(unsigned long long));
+
+ /* TR0 -> TR7 */
+ if (!ret && count > 0)
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ regs->tregs,
+ offsetof(struct pt_regs, tregs[0]),
+ 8 * sizeof(unsigned long long));
+
+ if (!ret)
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ sizeof(struct pt_regs), -1);
+
+ return ret;
+}
+
+#ifdef CONFIG_SH_FPU
+int fpregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret;
+
+ ret = init_fpu(target);
+ if (ret)
+ return ret;
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpu.hard, 0, -1);
+}
+
+static int fpregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+
+ ret = init_fpu(target);
+ if (ret)
+ return ret;
+
+ set_stopped_child_used_math(target);
+
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpu.hard, 0, -1);
+}
+
+static int fpregs_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ return tsk_used_math(target) ? regset->n : 0;
+}
+#endif
+
+/*
+ * These are our native regset flavours.
+ */
+enum sh_regset {
+ REGSET_GENERAL,
+#ifdef CONFIG_SH_FPU
+ REGSET_FPU,
+#endif
+};
+
+static const struct user_regset sh_regsets[] = {
+ /*
+ * Format is:
+ * PC, SR, SYSCALL,
+ * R1 --> R63,
+ * TR0 --> TR7,
+ */
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = ELF_NGREG,
+ .size = sizeof(long long),
+ .align = sizeof(long long),
+ .get = genregs_get,
+ .set = genregs_set,
+ },
+
+#ifdef CONFIG_SH_FPU
+ [REGSET_FPU] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_fpu_struct) /
+ sizeof(long long),
+ .size = sizeof(long long),
+ .align = sizeof(long long),
+ .get = fpregs_get,
+ .set = fpregs_set,
+ .active = fpregs_active,
+ },
+#endif
+};
+
+static const struct user_regset_view user_sh64_native_view = {
+ .name = "sh64",
+ .e_machine = EM_SH,
+ .regsets = sh_regsets,
+ .n = ARRAY_SIZE(sh_regsets),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &user_sh64_native_view;
+}
+
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int ret;
@@ -195,10 +356,33 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
}
break;
+ case PTRACE_GETREGS:
+ return copy_regset_to_user(child, &user_sh64_native_view,
+ REGSET_GENERAL,
+ 0, sizeof(struct pt_regs),
+ (void __user *)data);
+ case PTRACE_SETREGS:
+ return copy_regset_from_user(child, &user_sh64_native_view,
+ REGSET_GENERAL,
+ 0, sizeof(struct pt_regs),
+ (const void __user *)data);
+#ifdef CONFIG_SH_FPU
+ case PTRACE_GETFPREGS:
+ return copy_regset_to_user(child, &user_sh64_native_view,
+ REGSET_FPU,
+ 0, sizeof(struct user_fpu_struct),
+ (void __user *)data);
+ case PTRACE_SETFPREGS:
+ return copy_regset_from_user(child, &user_sh64_native_view,
+ REGSET_FPU,
+ 0, sizeof(struct user_fpu_struct),
+ (const void __user *)data);
+#endif
default:
ret = ptrace_request(child, request, addr, data);
break;
}
+
return ret;
}
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index e7152cc6930e..534247508572 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -417,6 +417,7 @@ void __init setup_arch(char **cmdline_p)
}
static const char *cpu_name[] = {
+ [CPU_SH7201] = "SH7201",
[CPU_SH7203] = "SH7203", [CPU_SH7263] = "SH7263",
[CPU_SH7206] = "SH7206", [CPU_SH7619] = "SH7619",
[CPU_SH7705] = "SH7705", [CPU_SH7706] = "SH7706",
diff --git a/arch/sh/kernel/sh_bios.c b/arch/sh/kernel/sh_bios.c
index d1bcac4fa269..c852f7805728 100644
--- a/arch/sh/kernel/sh_bios.c
+++ b/arch/sh/kernel/sh_bios.c
@@ -8,69 +8,50 @@
#include <linux/module.h>
#include <asm/sh_bios.h>
-#define BIOS_CALL_CONSOLE_WRITE 0
-#define BIOS_CALL_READ_BLOCK 1
+#define BIOS_CALL_CONSOLE_WRITE 0
#define BIOS_CALL_ETH_NODE_ADDR 10
#define BIOS_CALL_SHUTDOWN 11
-#define BIOS_CALL_CHAR_OUT 0x1f /* TODO: hack */
-#define BIOS_CALL_GDB_GET_MODE_PTR 0xfe
-#define BIOS_CALL_GDB_DETACH 0xff
+#define BIOS_CALL_CHAR_OUT 0x1f /* TODO: hack */
+#define BIOS_CALL_GDB_DETACH 0xff
-static __inline__ long sh_bios_call(long func, long arg0, long arg1, long arg2, long arg3)
+static inline long sh_bios_call(long func, long arg0, long arg1, long arg2,
+ long arg3)
{
- register long r0 __asm__("r0") = func;
- register long r4 __asm__("r4") = arg0;
- register long r5 __asm__("r5") = arg1;
- register long r6 __asm__("r6") = arg2;
- register long r7 __asm__("r7") = arg3;
- __asm__ __volatile__("trapa #0x3f"
- : "=z" (r0)
- : "0" (r0), "r" (r4), "r" (r5), "r" (r6), "r" (r7)
- : "memory");
- return r0;
+ register long r0 __asm__("r0") = func;
+ register long r4 __asm__("r4") = arg0;
+ register long r5 __asm__("r5") = arg1;
+ register long r6 __asm__("r6") = arg2;
+ register long r7 __asm__("r7") = arg3;
+
+ __asm__ __volatile__("trapa #0x3f":"=z"(r0)
+ :"0"(r0), "r"(r4), "r"(r5), "r"(r6), "r"(r7)
+ :"memory");
+ return r0;
}
-
void sh_bios_console_write(const char *buf, unsigned int len)
{
- sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0);
+ sh_bios_call(BIOS_CALL_CONSOLE_WRITE, (long)buf, (long)len, 0, 0);
}
-
void sh_bios_char_out(char ch)
{
- sh_bios_call(BIOS_CALL_CHAR_OUT, ch, 0, 0, 0);
-}
-
-
-int sh_bios_in_gdb_mode(void)
-{
- static char queried = 0;
- static char *gdb_mode_p = 0;
-
- if (!queried)
- {
- /* Query the gdb stub for address of its gdb mode variable */
- long r = sh_bios_call(BIOS_CALL_GDB_GET_MODE_PTR, 0, 0, 0, 0);
- if (r != ~0) /* BIOS returns -1 for unknown function */
- gdb_mode_p = (char *)r;
- queried = 1;
- }
- return (gdb_mode_p != 0 ? *gdb_mode_p : 0);
+ sh_bios_call(BIOS_CALL_CHAR_OUT, ch, 0, 0, 0);
}
void sh_bios_gdb_detach(void)
{
- sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0);
+ sh_bios_call(BIOS_CALL_GDB_DETACH, 0, 0, 0, 0);
}
-EXPORT_SYMBOL(sh_bios_gdb_detach);
+EXPORT_SYMBOL_GPL(sh_bios_gdb_detach);
-void sh_bios_get_node_addr (unsigned char *node_addr)
+void sh_bios_get_node_addr(unsigned char *node_addr)
{
- sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0);
+ sh_bios_call(BIOS_CALL_ETH_NODE_ADDR, 0, (long)node_addr, 0, 0);
}
+EXPORT_SYMBOL_GPL(sh_bios_get_node_addr);
void sh_bios_shutdown(unsigned int how)
{
- sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0);
+ sh_bios_call(BIOS_CALL_SHUTDOWN, how, 0, 0, 0);
}
diff --git a/arch/sh/kernel/sh_ksyms_32.c b/arch/sh/kernel/sh_ksyms_32.c
index 92ae5e6c099e..528de2955c81 100644
--- a/arch/sh/kernel/sh_ksyms_32.c
+++ b/arch/sh/kernel/sh_ksyms_32.c
@@ -52,16 +52,12 @@ EXPORT_SYMBOL(__const_udelay);
#define DECLARE_EXPORT(name) \
extern void name(void);EXPORT_SYMBOL(name)
-#define MAYBE_DECLARE_EXPORT(name) \
- extern void name(void) __weak;EXPORT_SYMBOL(name)
-/* These symbols are generated by the compiler itself */
DECLARE_EXPORT(__udivsi3);
DECLARE_EXPORT(__sdivsi3);
+DECLARE_EXPORT(__lshrsi3);
DECLARE_EXPORT(__ashrsi3);
DECLARE_EXPORT(__ashlsi3);
-DECLARE_EXPORT(__ashrdi3);
-DECLARE_EXPORT(__ashldi3);
DECLARE_EXPORT(__ashiftrt_r4_6);
DECLARE_EXPORT(__ashiftrt_r4_7);
DECLARE_EXPORT(__ashiftrt_r4_8);
@@ -79,8 +75,7 @@ DECLARE_EXPORT(__ashiftrt_r4_23);
DECLARE_EXPORT(__ashiftrt_r4_24);
DECLARE_EXPORT(__ashiftrt_r4_27);
DECLARE_EXPORT(__ashiftrt_r4_30);
-DECLARE_EXPORT(__lshrsi3);
-DECLARE_EXPORT(__lshrdi3);
+DECLARE_EXPORT(__movstr);
DECLARE_EXPORT(__movstrSI8);
DECLARE_EXPORT(__movstrSI12);
DECLARE_EXPORT(__movstrSI16);
@@ -95,31 +90,17 @@ DECLARE_EXPORT(__movstrSI48);
DECLARE_EXPORT(__movstrSI52);
DECLARE_EXPORT(__movstrSI56);
DECLARE_EXPORT(__movstrSI60);
-#if __GNUC__ == 4
-DECLARE_EXPORT(__movmem);
-#else
-DECLARE_EXPORT(__movstr);
-#endif
-
-#if __GNUC__ == 4
+DECLARE_EXPORT(__movstr_i4_even);
+DECLARE_EXPORT(__movstr_i4_odd);
+DECLARE_EXPORT(__movstrSI12_i4);
DECLARE_EXPORT(__movmem_i4_even);
DECLARE_EXPORT(__movmem_i4_odd);
DECLARE_EXPORT(__movmemSI12_i4);
-
-#if (__GNUC_MINOR__ >= 2 || defined(__GNUC_STM_RELEASE__))
-/*
- * GCC >= 4.2 emits these for division, as do GCC 4.1.x versions of the ST
- * compiler which include backported patches.
- */
DECLARE_EXPORT(__udiv_qrnnd_16);
-MAYBE_DECLARE_EXPORT(__sdivsi3_i4i);
-MAYBE_DECLARE_EXPORT(__udivsi3_i4i);
-#endif
-#else /* GCC 3.x */
-DECLARE_EXPORT(__movstr_i4_even);
-DECLARE_EXPORT(__movstr_i4_odd);
-DECLARE_EXPORT(__movstrSI12_i4);
-#endif /* __GNUC__ == 4 */
+DECLARE_EXPORT(__sdivsi3_i4);
+DECLARE_EXPORT(__udivsi3_i4);
+DECLARE_EXPORT(__sdivsi3_i4i);
+DECLARE_EXPORT(__udivsi3_i4i);
#if !defined(CONFIG_CACHE_OFF) && (defined(CONFIG_CPU_SH4) || \
defined(CONFIG_SH7705_CACHE_32KB))
diff --git a/arch/sh/kernel/sh_ksyms_64.c b/arch/sh/kernel/sh_ksyms_64.c
index 9324d32adacc..0d74d6b8774e 100644
--- a/arch/sh/kernel/sh_ksyms_64.c
+++ b/arch/sh/kernel/sh_ksyms_64.c
@@ -65,15 +65,16 @@ EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(__copy_user);
EXPORT_SYMBOL(empty_zero_page);
EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(__udelay);
EXPORT_SYMBOL(__ndelay);
EXPORT_SYMBOL(__const_udelay);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcpy);
/* Ugh. These come in from libgcc.a at link time. */
#define DECLARE_EXPORT(name) extern void name(void);EXPORT_SYMBOL(name)
DECLARE_EXPORT(__sdivsi3);
-DECLARE_EXPORT(__sdivsi3_2);
-DECLARE_EXPORT(__muldi3);
DECLARE_EXPORT(__udivsi3);
DECLARE_EXPORT(__div_table);
diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c
index 69d09c0b3498..77c21bde376a 100644
--- a/arch/sh/kernel/signal_32.c
+++ b/arch/sh/kernel/signal_32.c
@@ -533,7 +533,6 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info,
{
int ret;
-
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(sig, ka, info, oldset, regs);
diff --git a/arch/sh/kernel/signal_64.c b/arch/sh/kernel/signal_64.c
index ce3e851dffcb..b22fdfaaa191 100644
--- a/arch/sh/kernel/signal_64.c
+++ b/arch/sh/kernel/signal_64.c
@@ -2,7 +2,7 @@
* arch/sh/kernel/signal_64.c
*
* Copyright (C) 2000, 2001 Paolo Alberelli
- * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2003 - 2008 Paul Mundt
* Copyright (C) 2004 Richard Curnow
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -43,10 +43,38 @@
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-static void
+static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs * regs);
+static inline void
+handle_syscall_restart(struct pt_regs *regs, struct sigaction *sa)
+{
+ /* If we're not from a syscall, bail out */
+ if (regs->syscall_nr < 0)
+ return;
+
+ /* check for system call restart.. */
+ switch (regs->regs[REG_RET]) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ no_system_call_restart:
+ regs->regs[REG_RET] = -EINTR;
+ regs->sr |= 1;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(sa->sa_flags & SA_RESTART))
+ goto no_system_call_restart;
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* Decode syscall # */
+ regs->regs[REG_RET] = regs->syscall_nr;
+ regs->pc -= 4;
+ break;
+ }
+}
+
/*
* 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
@@ -80,21 +108,23 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset)
oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, 0);
-
if (signr > 0) {
- /* Whee! Actually deliver the signal. */
- handle_signal(signr, &info, &ka, oldset, regs);
+ if (regs->sr & 1)
+ handle_syscall_restart(regs, &ka.sa);
- /*
- * If a signal was successfully delivered, the saved sigmask
- * is in its frame, and we can clear the TIF_RESTORE_SIGMASK
- * flag.
- */
- if (test_thread_flag(TIF_RESTORE_SIGMASK))
- clear_thread_flag(TIF_RESTORE_SIGMASK);
-
- tracehook_signal_handler(signr, &info, &ka, regs, 0);
- return 1;
+ /* Whee! Actually deliver the signal. */
+ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
+ /*
+ * If a signal was successfully delivered, the
+ * saved sigmask is in its frame, and we can
+ * clear the TIF_RESTORE_SIGMASK flag.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+ tracehook_signal_handler(signr, &info, &ka, regs, 0);
+ return 1;
+ }
}
no_signal:
@@ -129,7 +159,6 @@ no_signal:
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
-
asmlinkage int
sys_sigsuspend(old_sigset_t mask,
unsigned long r3, unsigned long r4, unsigned long r5,
@@ -235,20 +264,16 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
return do_sigaltstack(uss, uoss, REF_REG_SP);
}
-
/*
* Do a signal return; undo the signal stack.
*/
-
-struct sigframe
-{
+struct sigframe {
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS-1];
long long retcode[2];
};
-struct rt_sigframe
-{
+struct rt_sigframe {
struct siginfo __user *pinfo;
void *puc;
struct siginfo info;
@@ -450,7 +475,6 @@ badframe:
/*
* Set up a signal frame.
*/
-
static int
setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
unsigned long mask)
@@ -504,8 +528,8 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
void sa_default_restorer(void); /* See comments below */
void sa_default_rt_restorer(void); /* See comments below */
-static void setup_frame(int sig, struct k_sigaction *ka,
- sigset_t *set, struct pt_regs *regs)
+static int setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
{
struct sigframe __user *frame;
int err = 0;
@@ -596,23 +620,21 @@ static void setup_frame(int sig, struct k_sigaction *ka,
set_fs(USER_DS);
-#if DEBUG_SIG
/* Broken %016Lx */
- printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
- signal,
- current->comm, current->pid, frame,
- regs->pc >> 32, regs->pc & 0xffffffff,
- DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
-#endif
+ pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+ signal, current->comm, current->pid, frame,
+ regs->pc >> 32, regs->pc & 0xffffffff,
+ DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
- return;
+ return 0;
give_sigsegv:
force_sigsegv(sig, current);
+ return -EFAULT;
}
-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
int err = 0;
@@ -702,62 +724,46 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
set_fs(USER_DS);
-#if DEBUG_SIG
- /* Broken %016Lx */
- printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
- signal,
- current->comm, current->pid, frame,
- regs->pc >> 32, regs->pc & 0xffffffff,
- DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
-#endif
+ pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+ signal, current->comm, current->pid, frame,
+ regs->pc >> 32, regs->pc & 0xffffffff,
+ DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
- return;
+ return 0;
give_sigsegv:
force_sigsegv(sig, current);
+ return -EFAULT;
}
/*
* OK, we're invoking a handler
*/
-
-static void
+static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs * regs)
{
- /* Are we from a system call? */
- if (regs->syscall_nr >= 0) {
- /* If so, check system call restarting.. */
- switch (regs->regs[REG_RET]) {
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- no_system_call_restart:
- regs->regs[REG_RET] = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART))
- goto no_system_call_restart;
- /* fallthrough */
- case -ERESTARTNOINTR:
- /* Decode syscall # */
- regs->regs[REG_RET] = regs->syscall_nr;
- regs->pc -= 4;
- }
- }
+ int ret;
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
- setup_rt_frame(sig, ka, info, oldset, regs);
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
- setup_frame(sig, ka, oldset, regs);
+ ret = setup_frame(sig, ka, oldset, regs);
+
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (ret == 0) {
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
- spin_lock_irq(&current->sighand->siglock);
- sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(&current->blocked,sig);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ return ret;
}
asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c
index 38f098c9c72d..58dfc02c7af1 100644
--- a/arch/sh/kernel/sys_sh.c
+++ b/arch/sh/kernel/sys_sh.c
@@ -22,102 +22,10 @@
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ipc.h>
-#include <asm/cacheflush.h>
#include <asm/syscalls.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
-unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */
-EXPORT_SYMBOL(shm_align_mask);
-
-#ifdef CONFIG_MMU
-/*
- * To avoid cache aliases, we map the shared page with same color.
- */
-#define COLOUR_ALIGN(addr, pgoff) \
- ((((addr) + shm_align_mask) & ~shm_align_mask) + \
- (((pgoff) << PAGE_SHIFT) & shm_align_mask))
-
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- unsigned long start_addr;
- int do_colour_align;
-
- if (flags & MAP_FIXED) {
- /* We do not accept a shared mapping if it would violate
- * cache aliasing constraints.
- */
- if ((flags & MAP_SHARED) && (addr & shm_align_mask))
- return -EINVAL;
- return addr;
- }
-
- if (unlikely(len > TASK_SIZE))
- return -ENOMEM;
-
- do_colour_align = 0;
- if (filp || (flags & MAP_SHARED))
- do_colour_align = 1;
-
- if (addr) {
- if (do_colour_align)
- addr = COLOUR_ALIGN(addr, pgoff);
- else
- addr = PAGE_ALIGN(addr);
-
- vma = find_vma(mm, addr);
- if (TASK_SIZE - len >= addr &&
- (!vma || addr + len <= vma->vm_start))
- return addr;
- }
-
- if (len > mm->cached_hole_size) {
- start_addr = addr = mm->free_area_cache;
- } else {
- mm->cached_hole_size = 0;
- start_addr = addr = TASK_UNMAPPED_BASE;
- }
-
-full_search:
- if (do_colour_align)
- addr = COLOUR_ALIGN(addr, pgoff);
- else
- addr = PAGE_ALIGN(mm->free_area_cache);
-
- for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
- /* At this point: (!vma || addr < vma->vm_end). */
- if (unlikely(TASK_SIZE - len < addr)) {
- /*
- * Start a new search - just in case we missed
- * some holes.
- */
- if (start_addr != TASK_UNMAPPED_BASE) {
- start_addr = addr = TASK_UNMAPPED_BASE;
- mm->cached_hole_size = 0;
- goto full_search;
- }
- return -ENOMEM;
- }
- if (likely(!vma || addr + len <= vma->vm_start)) {
- /*
- * Remember the place where we stopped the search:
- */
- mm->free_area_cache = addr + len;
- return addr;
- }
- if (addr + mm->cached_hole_size < vma->vm_start)
- mm->cached_hole_size = vma->vm_start - addr;
-
- addr = vma->vm_end;
- if (do_colour_align)
- addr = COLOUR_ALIGN(addr, pgoff);
- }
-}
-#endif /* CONFIG_MMU */
-
static inline long
do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
unsigned long flags, int fd, unsigned long pgoff)
diff --git a/arch/sh/kernel/time_32.c b/arch/sh/kernel/time_32.c
index 23ca711c27d2..8457f83242c5 100644
--- a/arch/sh/kernel/time_32.c
+++ b/arch/sh/kernel/time_32.c
@@ -125,11 +125,6 @@ void handle_timer_tick(void)
if (current->pid)
profile_tick(CPU_PROFILING);
-#ifdef CONFIG_HEARTBEAT
- if (sh_mv.mv_heartbeat != NULL)
- sh_mv.mv_heartbeat();
-#endif
-
/*
* Here we are in the timer irq handler. We just have irqs locally
* disabled but we don't know if the timer_bh is running on the other
@@ -277,11 +272,4 @@ void __init time_init(void)
((sh_hpt_frequency + 500) / 1000) / 1000,
((sh_hpt_frequency + 500) / 1000) % 1000);
-#if defined(CONFIG_SH_KGDB)
- /*
- * Set up kgdb as requested. We do it here because the serial
- * init uses the timer vars we just set up for figuring baud.
- */
- kgdb_init();
-#endif
}
diff --git a/arch/sh/kernel/time_64.c b/arch/sh/kernel/time_64.c
index bbb2af1004d9..59d2a03e8b3c 100644
--- a/arch/sh/kernel/time_64.c
+++ b/arch/sh/kernel/time_64.c
@@ -240,11 +240,6 @@ static inline void do_timer_interrupt(void)
do_timer(1);
-#ifdef CONFIG_HEARTBEAT
- if (sh_mv.mv_heartbeat != NULL)
- sh_mv.mv_heartbeat();
-#endif
-
/*
* If we have an externally synchronized Linux clock, then update
* RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
diff --git a/arch/sh/kernel/timers/timer-mtu2.c b/arch/sh/kernel/timers/timer-mtu2.c
index fe453c01f9c9..c3d237e1d566 100644
--- a/arch/sh/kernel/timers/timer-mtu2.c
+++ b/arch/sh/kernel/timers/timer-mtu2.c
@@ -34,7 +34,12 @@
#define MTU2_TIER_1 0xfffe4384
#define MTU2_TSR_1 0xfffe4385
#define MTU2_TCNT_1 0xfffe4386 /* 16-bit counter */
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+#define MTU2_TGRA_1 0xfffe4388
+#else
#define MTU2_TGRA_1 0xfffe438a
+#endif
#define STBCR3 0xfffe0408
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c
index 1e5c74efbacc..88807a2aacc3 100644
--- a/arch/sh/kernel/traps_32.c
+++ b/arch/sh/kernel/traps_32.c
@@ -28,17 +28,6 @@
#include <asm/fpu.h>
#include <asm/kprobes.h>
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
-#define CHK_REMOTE_DEBUG(regs) \
-{ \
- if (kgdb_debug_hook && !user_mode(regs))\
- (*kgdb_debug_hook)(regs); \
-}
-#else
-#define CHK_REMOTE_DEBUG(regs)
-#endif
-
#ifdef CONFIG_CPU_SH2
# define TRAP_RESERVED_INST 4
# define TRAP_ILLEGAL_SLOT_INST 6
@@ -94,7 +83,6 @@ void die(const char * str, struct pt_regs * regs, long err)
printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
- CHK_REMOTE_DEBUG(regs);
print_modules();
show_regs(regs);
@@ -683,13 +671,12 @@ asmlinkage void do_reserved_inst(unsigned long r4, unsigned long r5,
error_code = lookup_exception_vector();
local_irq_enable();
- CHK_REMOTE_DEBUG(regs);
force_sig(SIGILL, tsk);
die_if_no_fixup("reserved instruction", regs, error_code);
}
#ifdef CONFIG_SH_FPU_EMU
-static int emulate_branch(unsigned short inst, struct pt_regs* regs)
+static int emulate_branch(unsigned short inst, struct pt_regs *regs)
{
/*
* bfs: 8fxx: PC+=d*2+4;
@@ -702,27 +689,32 @@ static int emulate_branch(unsigned short inst, struct pt_regs* regs)
* jsr: 4x0b: PC=Rn after PR=PC+4;
* rts: 000b: PC=PR;
*/
- if ((inst & 0xfd00) == 0x8d00) {
+ if (((inst & 0xf000) == 0xb000) || /* bsr */
+ ((inst & 0xf0ff) == 0x0003) || /* bsrf */
+ ((inst & 0xf0ff) == 0x400b)) /* jsr */
+ regs->pr = regs->pc + 4;
+
+ if ((inst & 0xfd00) == 0x8d00) { /* bfs, bts */
regs->pc += SH_PC_8BIT_OFFSET(inst);
return 0;
}
- if ((inst & 0xe000) == 0xa000) {
+ if ((inst & 0xe000) == 0xa000) { /* bra, bsr */
regs->pc += SH_PC_12BIT_OFFSET(inst);
return 0;
}
- if ((inst & 0xf0df) == 0x0003) {
+ if ((inst & 0xf0df) == 0x0003) { /* braf, bsrf */
regs->pc += regs->regs[(inst & 0x0f00) >> 8] + 4;
return 0;
}
- if ((inst & 0xf0df) == 0x400b) {
+ if ((inst & 0xf0df) == 0x400b) { /* jmp, jsr */
regs->pc = regs->regs[(inst & 0x0f00) >> 8];
return 0;
}
- if ((inst & 0xffff) == 0x000b) {
+ if ((inst & 0xffff) == 0x000b) { /* rts */
regs->pc = regs->pr;
return 0;
}
@@ -756,7 +748,6 @@ asmlinkage void do_illegal_slot_inst(unsigned long r4, unsigned long r5,
inst = lookup_exception_vector();
local_irq_enable();
- CHK_REMOTE_DEBUG(regs);
force_sig(SIGILL, tsk);
die_if_no_fixup("illegal slot instruction", regs, inst);
}
@@ -868,10 +859,7 @@ void show_trace(struct task_struct *tsk, unsigned long *sp,
if (regs && user_mode(regs))
return;
- printk("\nCall trace: ");
-#ifdef CONFIG_KALLSYMS
- printk("\n");
-#endif
+ printk("\nCall trace:\n");
while (!kstack_end(sp)) {
addr = *sp++;
diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c
index 95f4de0800ec..3f7e415be86a 100644
--- a/arch/sh/kernel/vsyscall/vsyscall.c
+++ b/arch/sh/kernel/vsyscall/vsyscall.c
@@ -59,8 +59,7 @@ int __init vsyscall_init(void)
}
/* Setup a VMA at program startup for the vsyscall page */
-int arch_setup_additional_pages(struct linux_binprm *bprm,
- int executable_stack)
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
unsigned long addr;
diff --git a/arch/sh/lib/Makefile b/arch/sh/lib/Makefile
index 8596cc78e18d..aaea580b65bb 100644
--- a/arch/sh/lib/Makefile
+++ b/arch/sh/lib/Makefile
@@ -5,12 +5,26 @@
lib-y = delay.o memset.o memmove.o memchr.o \
checksum.o strlen.o div64.o div64-generic.o
+# Extracted from libgcc
+lib-y += movmem.o ashldi3.o ashrdi3.o lshrdi3.o \
+ ashlsi3.o ashrsi3.o ashiftrt.o lshrsi3.o \
+ udiv_qrnnd.o
+
+udivsi3-y := udivsi3_i4i-Os.o
+
+ifneq ($(CONFIG_CC_OPTIMIZE_FOR_SIZE),y)
+udivsi3-$(CONFIG_CPU_SH3) := udivsi3_i4i.o
+udivsi3-$(CONFIG_CPU_SH4) := udivsi3_i4i.o
+endif
+udivsi3-y += udivsi3.o
+
obj-y += io.o
memcpy-y := memcpy.o
memcpy-$(CONFIG_CPU_SH4) := memcpy-sh4.o
lib-$(CONFIG_MMU) += copy_page.o clear_page.o
-lib-y += $(memcpy-y)
+lib-$(CONFIG_FUNCTION_TRACER) += mcount.o
+lib-y += $(memcpy-y) $(udivsi3-y)
EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/lib/ashiftrt.S b/arch/sh/lib/ashiftrt.S
new file mode 100644
index 000000000000..45ce86558f46
--- /dev/null
+++ b/arch/sh/lib/ashiftrt.S
@@ -0,0 +1,149 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+ .global __ashiftrt_r4_0
+ .global __ashiftrt_r4_1
+ .global __ashiftrt_r4_2
+ .global __ashiftrt_r4_3
+ .global __ashiftrt_r4_4
+ .global __ashiftrt_r4_5
+ .global __ashiftrt_r4_6
+ .global __ashiftrt_r4_7
+ .global __ashiftrt_r4_8
+ .global __ashiftrt_r4_9
+ .global __ashiftrt_r4_10
+ .global __ashiftrt_r4_11
+ .global __ashiftrt_r4_12
+ .global __ashiftrt_r4_13
+ .global __ashiftrt_r4_14
+ .global __ashiftrt_r4_15
+ .global __ashiftrt_r4_16
+ .global __ashiftrt_r4_17
+ .global __ashiftrt_r4_18
+ .global __ashiftrt_r4_19
+ .global __ashiftrt_r4_20
+ .global __ashiftrt_r4_21
+ .global __ashiftrt_r4_22
+ .global __ashiftrt_r4_23
+ .global __ashiftrt_r4_24
+ .global __ashiftrt_r4_25
+ .global __ashiftrt_r4_26
+ .global __ashiftrt_r4_27
+ .global __ashiftrt_r4_28
+ .global __ashiftrt_r4_29
+ .global __ashiftrt_r4_30
+ .global __ashiftrt_r4_31
+ .global __ashiftrt_r4_32
+
+ .align 1
+__ashiftrt_r4_32:
+__ashiftrt_r4_31:
+ rotcl r4
+ rts
+ subc r4,r4
+__ashiftrt_r4_30:
+ shar r4
+__ashiftrt_r4_29:
+ shar r4
+__ashiftrt_r4_28:
+ shar r4
+__ashiftrt_r4_27:
+ shar r4
+__ashiftrt_r4_26:
+ shar r4
+__ashiftrt_r4_25:
+ shar r4
+__ashiftrt_r4_24:
+ shlr16 r4
+ shlr8 r4
+ rts
+ exts.b r4,r4
+__ashiftrt_r4_23:
+ shar r4
+__ashiftrt_r4_22:
+ shar r4
+__ashiftrt_r4_21:
+ shar r4
+__ashiftrt_r4_20:
+ shar r4
+__ashiftrt_r4_19:
+ shar r4
+__ashiftrt_r4_18:
+ shar r4
+__ashiftrt_r4_17:
+ shar r4
+__ashiftrt_r4_16:
+ shlr16 r4
+ rts
+ exts.w r4,r4
+__ashiftrt_r4_15:
+ shar r4
+__ashiftrt_r4_14:
+ shar r4
+__ashiftrt_r4_13:
+ shar r4
+__ashiftrt_r4_12:
+ shar r4
+__ashiftrt_r4_11:
+ shar r4
+__ashiftrt_r4_10:
+ shar r4
+__ashiftrt_r4_9:
+ shar r4
+__ashiftrt_r4_8:
+ shar r4
+__ashiftrt_r4_7:
+ shar r4
+__ashiftrt_r4_6:
+ shar r4
+__ashiftrt_r4_5:
+ shar r4
+__ashiftrt_r4_4:
+ shar r4
+__ashiftrt_r4_3:
+ shar r4
+__ashiftrt_r4_2:
+ shar r4
+__ashiftrt_r4_1:
+ rts
+ shar r4
+__ashiftrt_r4_0:
+ rts
+ nop
diff --git a/arch/sh/lib/ashldi3.c b/arch/sh/lib/ashldi3.c
new file mode 100644
index 000000000000..beb80f316095
--- /dev/null
+++ b/arch/sh/lib/ashldi3.c
@@ -0,0 +1,29 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __ashldi3(long long u, word_type b)
+{
+ DWunion uu, w;
+ word_type bm;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+ bm = 32 - b;
+
+ if (bm <= 0) {
+ w.s.low = 0;
+ w.s.high = (unsigned int) uu.s.low << -bm;
+ } else {
+ const unsigned int carries = (unsigned int) uu.s.low >> bm;
+
+ w.s.low = (unsigned int) uu.s.low << b;
+ w.s.high = ((unsigned int) uu.s.high << b) | carries;
+ }
+
+ return w.ll;
+}
+
+EXPORT_SYMBOL(__ashldi3);
diff --git a/arch/sh/lib/ashlsi3.S b/arch/sh/lib/ashlsi3.S
new file mode 100644
index 000000000000..bd47e9b403a5
--- /dev/null
+++ b/arch/sh/lib/ashlsi3.S
@@ -0,0 +1,193 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+!
+! __ashlsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+ .global __ashlsi3
+
+ .align 2
+__ashlsi3:
+ mov #31,r0
+ and r0,r5
+ mova ashlsi3_table,r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+ashlsi3_table:
+ .byte ashlsi3_0-ashlsi3_table
+ .byte ashlsi3_1-ashlsi3_table
+ .byte ashlsi3_2-ashlsi3_table
+ .byte ashlsi3_3-ashlsi3_table
+ .byte ashlsi3_4-ashlsi3_table
+ .byte ashlsi3_5-ashlsi3_table
+ .byte ashlsi3_6-ashlsi3_table
+ .byte ashlsi3_7-ashlsi3_table
+ .byte ashlsi3_8-ashlsi3_table
+ .byte ashlsi3_9-ashlsi3_table
+ .byte ashlsi3_10-ashlsi3_table
+ .byte ashlsi3_11-ashlsi3_table
+ .byte ashlsi3_12-ashlsi3_table
+ .byte ashlsi3_13-ashlsi3_table
+ .byte ashlsi3_14-ashlsi3_table
+ .byte ashlsi3_15-ashlsi3_table
+ .byte ashlsi3_16-ashlsi3_table
+ .byte ashlsi3_17-ashlsi3_table
+ .byte ashlsi3_18-ashlsi3_table
+ .byte ashlsi3_19-ashlsi3_table
+ .byte ashlsi3_20-ashlsi3_table
+ .byte ashlsi3_21-ashlsi3_table
+ .byte ashlsi3_22-ashlsi3_table
+ .byte ashlsi3_23-ashlsi3_table
+ .byte ashlsi3_24-ashlsi3_table
+ .byte ashlsi3_25-ashlsi3_table
+ .byte ashlsi3_26-ashlsi3_table
+ .byte ashlsi3_27-ashlsi3_table
+ .byte ashlsi3_28-ashlsi3_table
+ .byte ashlsi3_29-ashlsi3_table
+ .byte ashlsi3_30-ashlsi3_table
+ .byte ashlsi3_31-ashlsi3_table
+
+ashlsi3_6:
+ shll2 r0
+ashlsi3_4:
+ shll2 r0
+ashlsi3_2:
+ rts
+ shll2 r0
+
+ashlsi3_7:
+ shll2 r0
+ashlsi3_5:
+ shll2 r0
+ashlsi3_3:
+ shll2 r0
+ashlsi3_1:
+ rts
+ shll r0
+
+ashlsi3_14:
+ shll2 r0
+ashlsi3_12:
+ shll2 r0
+ashlsi3_10:
+ shll2 r0
+ashlsi3_8:
+ rts
+ shll8 r0
+
+ashlsi3_15:
+ shll2 r0
+ashlsi3_13:
+ shll2 r0
+ashlsi3_11:
+ shll2 r0
+ashlsi3_9:
+ shll8 r0
+ rts
+ shll r0
+
+ashlsi3_22:
+ shll2 r0
+ashlsi3_20:
+ shll2 r0
+ashlsi3_18:
+ shll2 r0
+ashlsi3_16:
+ rts
+ shll16 r0
+
+ashlsi3_23:
+ shll2 r0
+ashlsi3_21:
+ shll2 r0
+ashlsi3_19:
+ shll2 r0
+ashlsi3_17:
+ shll16 r0
+ rts
+ shll r0
+
+ashlsi3_30:
+ shll2 r0
+ashlsi3_28:
+ shll2 r0
+ashlsi3_26:
+ shll2 r0
+ashlsi3_24:
+ shll16 r0
+ rts
+ shll8 r0
+
+ashlsi3_31:
+ shll2 r0
+ashlsi3_29:
+ shll2 r0
+ashlsi3_27:
+ shll2 r0
+ashlsi3_25:
+ shll16 r0
+ shll8 r0
+ rts
+ shll r0
+
+ashlsi3_0:
+ rts
+ nop
diff --git a/arch/sh/lib/ashrdi3.c b/arch/sh/lib/ashrdi3.c
new file mode 100644
index 000000000000..c884a912b660
--- /dev/null
+++ b/arch/sh/lib/ashrdi3.c
@@ -0,0 +1,31 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __ashrdi3(long long u, word_type b)
+{
+ DWunion uu, w;
+ word_type bm;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+ bm = 32 - b;
+
+ if (bm <= 0) {
+ /* w.s.high = 1..1 or 0..0 */
+ w.s.high =
+ uu.s.high >> 31;
+ w.s.low = uu.s.high >> -bm;
+ } else {
+ const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+ w.s.high = uu.s.high >> b;
+ w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+
+EXPORT_SYMBOL(__ashrdi3);
diff --git a/arch/sh/lib/ashrsi3.S b/arch/sh/lib/ashrsi3.S
new file mode 100644
index 000000000000..6f3cf46b77c2
--- /dev/null
+++ b/arch/sh/lib/ashrsi3.S
@@ -0,0 +1,185 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+!
+! __ashrsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+
+ .global __ashrsi3
+
+ .align 2
+__ashrsi3:
+ mov #31,r0
+ and r0,r5
+ mova ashrsi3_table,r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+ashrsi3_table:
+ .byte ashrsi3_0-ashrsi3_table
+ .byte ashrsi3_1-ashrsi3_table
+ .byte ashrsi3_2-ashrsi3_table
+ .byte ashrsi3_3-ashrsi3_table
+ .byte ashrsi3_4-ashrsi3_table
+ .byte ashrsi3_5-ashrsi3_table
+ .byte ashrsi3_6-ashrsi3_table
+ .byte ashrsi3_7-ashrsi3_table
+ .byte ashrsi3_8-ashrsi3_table
+ .byte ashrsi3_9-ashrsi3_table
+ .byte ashrsi3_10-ashrsi3_table
+ .byte ashrsi3_11-ashrsi3_table
+ .byte ashrsi3_12-ashrsi3_table
+ .byte ashrsi3_13-ashrsi3_table
+ .byte ashrsi3_14-ashrsi3_table
+ .byte ashrsi3_15-ashrsi3_table
+ .byte ashrsi3_16-ashrsi3_table
+ .byte ashrsi3_17-ashrsi3_table
+ .byte ashrsi3_18-ashrsi3_table
+ .byte ashrsi3_19-ashrsi3_table
+ .byte ashrsi3_20-ashrsi3_table
+ .byte ashrsi3_21-ashrsi3_table
+ .byte ashrsi3_22-ashrsi3_table
+ .byte ashrsi3_23-ashrsi3_table
+ .byte ashrsi3_24-ashrsi3_table
+ .byte ashrsi3_25-ashrsi3_table
+ .byte ashrsi3_26-ashrsi3_table
+ .byte ashrsi3_27-ashrsi3_table
+ .byte ashrsi3_28-ashrsi3_table
+ .byte ashrsi3_29-ashrsi3_table
+ .byte ashrsi3_30-ashrsi3_table
+ .byte ashrsi3_31-ashrsi3_table
+
+ashrsi3_31:
+ rotcl r0
+ rts
+ subc r0,r0
+
+ashrsi3_30:
+ shar r0
+ashrsi3_29:
+ shar r0
+ashrsi3_28:
+ shar r0
+ashrsi3_27:
+ shar r0
+ashrsi3_26:
+ shar r0
+ashrsi3_25:
+ shar r0
+ashrsi3_24:
+ shlr16 r0
+ shlr8 r0
+ rts
+ exts.b r0,r0
+
+ashrsi3_23:
+ shar r0
+ashrsi3_22:
+ shar r0
+ashrsi3_21:
+ shar r0
+ashrsi3_20:
+ shar r0
+ashrsi3_19:
+ shar r0
+ashrsi3_18:
+ shar r0
+ashrsi3_17:
+ shar r0
+ashrsi3_16:
+ shlr16 r0
+ rts
+ exts.w r0,r0
+
+ashrsi3_15:
+ shar r0
+ashrsi3_14:
+ shar r0
+ashrsi3_13:
+ shar r0
+ashrsi3_12:
+ shar r0
+ashrsi3_11:
+ shar r0
+ashrsi3_10:
+ shar r0
+ashrsi3_9:
+ shar r0
+ashrsi3_8:
+ shar r0
+ashrsi3_7:
+ shar r0
+ashrsi3_6:
+ shar r0
+ashrsi3_5:
+ shar r0
+ashrsi3_4:
+ shar r0
+ashrsi3_3:
+ shar r0
+ashrsi3_2:
+ shar r0
+ashrsi3_1:
+ rts
+ shar r0
+
+ashrsi3_0:
+ rts
+ nop
diff --git a/arch/sh/lib/libgcc.h b/arch/sh/lib/libgcc.h
new file mode 100644
index 000000000000..3f19d1c5d942
--- /dev/null
+++ b/arch/sh/lib/libgcc.h
@@ -0,0 +1,26 @@
+#ifndef __ASM_LIBGCC_H
+#define __ASM_LIBGCC_H
+
+#include <asm/byteorder.h>
+
+typedef int word_type __attribute__ ((mode (__word__)));
+
+#ifdef __BIG_ENDIAN
+struct DWstruct {
+ int high, low;
+};
+#elif defined(__LITTLE_ENDIAN)
+struct DWstruct {
+ int low, high;
+};
+#else
+#error I feel sick.
+#endif
+
+typedef union
+{
+ struct DWstruct s;
+ long long ll;
+} DWunion;
+
+#endif /* __ASM_LIBGCC_H */
diff --git a/arch/sh/lib/lshrdi3.c b/arch/sh/lib/lshrdi3.c
new file mode 100644
index 000000000000..dcf8d6810b7c
--- /dev/null
+++ b/arch/sh/lib/lshrdi3.c
@@ -0,0 +1,29 @@
+#include <linux/module.h>
+
+#include "libgcc.h"
+
+long long __lshrdi3(long long u, word_type b)
+{
+ DWunion uu, w;
+ word_type bm;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+ bm = 32 - b;
+
+ if (bm <= 0) {
+ w.s.high = 0;
+ w.s.low = (unsigned int) uu.s.high >> -bm;
+ } else {
+ const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+ w.s.high = (unsigned int) uu.s.high >> b;
+ w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+
+EXPORT_SYMBOL(__lshrdi3);
diff --git a/arch/sh/lib/lshrsi3.S b/arch/sh/lib/lshrsi3.S
new file mode 100644
index 000000000000..1e7aaa557130
--- /dev/null
+++ b/arch/sh/lib/lshrsi3.S
@@ -0,0 +1,193 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+!
+! __lshrsi3
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+ .global __lshrsi3
+
+ .align 2
+__lshrsi3:
+ mov #31,r0
+ and r0,r5
+ mova lshrsi3_table,r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+lshrsi3_table:
+ .byte lshrsi3_0-lshrsi3_table
+ .byte lshrsi3_1-lshrsi3_table
+ .byte lshrsi3_2-lshrsi3_table
+ .byte lshrsi3_3-lshrsi3_table
+ .byte lshrsi3_4-lshrsi3_table
+ .byte lshrsi3_5-lshrsi3_table
+ .byte lshrsi3_6-lshrsi3_table
+ .byte lshrsi3_7-lshrsi3_table
+ .byte lshrsi3_8-lshrsi3_table
+ .byte lshrsi3_9-lshrsi3_table
+ .byte lshrsi3_10-lshrsi3_table
+ .byte lshrsi3_11-lshrsi3_table
+ .byte lshrsi3_12-lshrsi3_table
+ .byte lshrsi3_13-lshrsi3_table
+ .byte lshrsi3_14-lshrsi3_table
+ .byte lshrsi3_15-lshrsi3_table
+ .byte lshrsi3_16-lshrsi3_table
+ .byte lshrsi3_17-lshrsi3_table
+ .byte lshrsi3_18-lshrsi3_table
+ .byte lshrsi3_19-lshrsi3_table
+ .byte lshrsi3_20-lshrsi3_table
+ .byte lshrsi3_21-lshrsi3_table
+ .byte lshrsi3_22-lshrsi3_table
+ .byte lshrsi3_23-lshrsi3_table
+ .byte lshrsi3_24-lshrsi3_table
+ .byte lshrsi3_25-lshrsi3_table
+ .byte lshrsi3_26-lshrsi3_table
+ .byte lshrsi3_27-lshrsi3_table
+ .byte lshrsi3_28-lshrsi3_table
+ .byte lshrsi3_29-lshrsi3_table
+ .byte lshrsi3_30-lshrsi3_table
+ .byte lshrsi3_31-lshrsi3_table
+
+lshrsi3_6:
+ shlr2 r0
+lshrsi3_4:
+ shlr2 r0
+lshrsi3_2:
+ rts
+ shlr2 r0
+
+lshrsi3_7:
+ shlr2 r0
+lshrsi3_5:
+ shlr2 r0
+lshrsi3_3:
+ shlr2 r0
+lshrsi3_1:
+ rts
+ shlr r0
+
+lshrsi3_14:
+ shlr2 r0
+lshrsi3_12:
+ shlr2 r0
+lshrsi3_10:
+ shlr2 r0
+lshrsi3_8:
+ rts
+ shlr8 r0
+
+lshrsi3_15:
+ shlr2 r0
+lshrsi3_13:
+ shlr2 r0
+lshrsi3_11:
+ shlr2 r0
+lshrsi3_9:
+ shlr8 r0
+ rts
+ shlr r0
+
+lshrsi3_22:
+ shlr2 r0
+lshrsi3_20:
+ shlr2 r0
+lshrsi3_18:
+ shlr2 r0
+lshrsi3_16:
+ rts
+ shlr16 r0
+
+lshrsi3_23:
+ shlr2 r0
+lshrsi3_21:
+ shlr2 r0
+lshrsi3_19:
+ shlr2 r0
+lshrsi3_17:
+ shlr16 r0
+ rts
+ shlr r0
+
+lshrsi3_30:
+ shlr2 r0
+lshrsi3_28:
+ shlr2 r0
+lshrsi3_26:
+ shlr2 r0
+lshrsi3_24:
+ shlr16 r0
+ rts
+ shlr8 r0
+
+lshrsi3_31:
+ shlr2 r0
+lshrsi3_29:
+ shlr2 r0
+lshrsi3_27:
+ shlr2 r0
+lshrsi3_25:
+ shlr16 r0
+ shlr8 r0
+ rts
+ shlr r0
+
+lshrsi3_0:
+ rts
+ nop
diff --git a/arch/sh/lib/mcount.S b/arch/sh/lib/mcount.S
new file mode 100644
index 000000000000..110fbfe1831f
--- /dev/null
+++ b/arch/sh/lib/mcount.S
@@ -0,0 +1,90 @@
+/*
+ * arch/sh/lib/mcount.S
+ *
+ * Copyright (C) 2008 Paul Mundt
+ * Copyright (C) 2008 Matt Fleming
+ *
+ * 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 <asm/ftrace.h>
+
+#define MCOUNT_ENTER() \
+ mov.l r4, @-r15; \
+ mov.l r5, @-r15; \
+ mov.l r6, @-r15; \
+ mov.l r7, @-r15; \
+ sts.l pr, @-r15; \
+ \
+ mov.l @(20,r15),r4; \
+ sts pr, r5
+
+#define MCOUNT_LEAVE() \
+ lds.l @r15+, pr; \
+ mov.l @r15+, r7; \
+ mov.l @r15+, r6; \
+ mov.l @r15+, r5; \
+ rts; \
+ mov.l @r15+, r4
+
+ .align 2
+ .globl _mcount
+ .type _mcount,@function
+ .globl mcount
+ .type mcount,@function
+_mcount:
+mcount:
+ MCOUNT_ENTER()
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+ .globl mcount_call
+mcount_call:
+ mov.l .Lftrace_stub, r6
+#else
+ mov.l .Lftrace_trace_function, r6
+ mov.l ftrace_stub, r7
+ cmp/eq r6, r7
+ bt skip_trace
+ mov.l @r6, r6
+#endif
+
+ jsr @r6
+ nop
+
+skip_trace:
+ MCOUNT_LEAVE()
+
+ .align 2
+.Lftrace_trace_function:
+ .long ftrace_trace_function
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+ .globl ftrace_caller
+ftrace_caller:
+ MCOUNT_ENTER()
+
+ .globl ftrace_call
+ftrace_call:
+ mov.l .Lftrace_stub, r6
+ jsr @r6
+ nop
+
+ MCOUNT_LEAVE()
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+/*
+ * NOTE: From here on the locations of the .Lftrace_stub label and
+ * ftrace_stub itself are fixed. Adding additional data here will skew
+ * the displacement for the memory table and break the block replacement.
+ * Place new labels either after the ftrace_stub body, or before
+ * ftrace_caller. You have been warned.
+ */
+ .align 2
+.Lftrace_stub:
+ .long ftrace_stub
+
+ .globl ftrace_stub
+ftrace_stub:
+ rts
+ nop
diff --git a/arch/sh/lib/movmem.S b/arch/sh/lib/movmem.S
new file mode 100644
index 000000000000..62075f6bc67c
--- /dev/null
+++ b/arch/sh/lib/movmem.S
@@ -0,0 +1,238 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+ .text
+ .balign 4
+ .global __movmem
+ .global __movstr
+ .set __movstr, __movmem
+ /* This would be a lot simpler if r6 contained the byte count
+ minus 64, and we wouldn't be called here for a byte count of 64. */
+__movmem:
+ sts.l pr,@-r15
+ shll2 r6
+ bsr __movmemSI52+2
+ mov.l @(48,r5),r0
+ .balign 4
+movmem_loop: /* Reached with rts */
+ mov.l @(60,r5),r0
+ add #-64,r6
+ mov.l r0,@(60,r4)
+ tst r6,r6
+ mov.l @(56,r5),r0
+ bt movmem_done
+ mov.l r0,@(56,r4)
+ cmp/pl r6
+ mov.l @(52,r5),r0
+ add #64,r5
+ mov.l r0,@(52,r4)
+ add #64,r4
+ bt __movmemSI52
+! done all the large groups, do the remainder
+! jump to movmem+
+ mova __movmemSI4+4,r0
+ add r6,r0
+ jmp @r0
+movmem_done: ! share slot insn, works out aligned.
+ lds.l @r15+,pr
+ mov.l r0,@(56,r4)
+ mov.l @(52,r5),r0
+ rts
+ mov.l r0,@(52,r4)
+ .balign 4
+
+ .global __movmemSI64
+ .global __movstrSI64
+ .set __movstrSI64, __movmemSI64
+__movmemSI64:
+ mov.l @(60,r5),r0
+ mov.l r0,@(60,r4)
+ .global __movmemSI60
+ .global __movstrSI60
+ .set __movstrSI60, __movmemSI60
+__movmemSI60:
+ mov.l @(56,r5),r0
+ mov.l r0,@(56,r4)
+ .global __movmemSI56
+ .global __movstrSI56
+ .set __movstrSI56, __movmemSI56
+__movmemSI56:
+ mov.l @(52,r5),r0
+ mov.l r0,@(52,r4)
+ .global __movmemSI52
+ .global __movstrSI52
+ .set __movstrSI52, __movmemSI52
+__movmemSI52:
+ mov.l @(48,r5),r0
+ mov.l r0,@(48,r4)
+ .global __movmemSI48
+ .global __movstrSI48
+ .set __movstrSI48, __movmemSI48
+__movmemSI48:
+ mov.l @(44,r5),r0
+ mov.l r0,@(44,r4)
+ .global __movmemSI44
+ .global __movstrSI44
+ .set __movstrSI44, __movmemSI44
+__movmemSI44:
+ mov.l @(40,r5),r0
+ mov.l r0,@(40,r4)
+ .global __movmemSI40
+ .global __movstrSI40
+ .set __movstrSI40, __movmemSI40
+__movmemSI40:
+ mov.l @(36,r5),r0
+ mov.l r0,@(36,r4)
+ .global __movmemSI36
+ .global __movstrSI36
+ .set __movstrSI36, __movmemSI36
+__movmemSI36:
+ mov.l @(32,r5),r0
+ mov.l r0,@(32,r4)
+ .global __movmemSI32
+ .global __movstrSI32
+ .set __movstrSI32, __movmemSI32
+__movmemSI32:
+ mov.l @(28,r5),r0
+ mov.l r0,@(28,r4)
+ .global __movmemSI28
+ .global __movstrSI28
+ .set __movstrSI28, __movmemSI28
+__movmemSI28:
+ mov.l @(24,r5),r0
+ mov.l r0,@(24,r4)
+ .global __movmemSI24
+ .global __movstrSI24
+ .set __movstrSI24, __movmemSI24
+__movmemSI24:
+ mov.l @(20,r5),r0
+ mov.l r0,@(20,r4)
+ .global __movmemSI20
+ .global __movstrSI20
+ .set __movstrSI20, __movmemSI20
+__movmemSI20:
+ mov.l @(16,r5),r0
+ mov.l r0,@(16,r4)
+ .global __movmemSI16
+ .global __movstrSI16
+ .set __movstrSI16, __movmemSI16
+__movmemSI16:
+ mov.l @(12,r5),r0
+ mov.l r0,@(12,r4)
+ .global __movmemSI12
+ .global __movstrSI12
+ .set __movstrSI12, __movmemSI12
+__movmemSI12:
+ mov.l @(8,r5),r0
+ mov.l r0,@(8,r4)
+ .global __movmemSI8
+ .global __movstrSI8
+ .set __movstrSI8, __movmemSI8
+__movmemSI8:
+ mov.l @(4,r5),r0
+ mov.l r0,@(4,r4)
+ .global __movmemSI4
+ .global __movstrSI4
+ .set __movstrSI4, __movmemSI4
+__movmemSI4:
+ mov.l @(0,r5),r0
+ rts
+ mov.l r0,@(0,r4)
+
+ .global __movmem_i4_even
+ .global __movstr_i4_even
+ .set __movstr_i4_even, __movmem_i4_even
+
+ .global __movmem_i4_odd
+ .global __movstr_i4_odd
+ .set __movstr_i4_odd, __movmem_i4_odd
+
+ .global __movmemSI12_i4
+ .global __movstrSI12_i4
+ .set __movstrSI12_i4, __movmemSI12_i4
+
+ .p2align 5
+L_movmem_2mod4_end:
+ mov.l r0,@(16,r4)
+ rts
+ mov.l r1,@(20,r4)
+
+ .p2align 2
+
+__movmem_i4_even:
+ mov.l @r5+,r0
+ bra L_movmem_start_even
+ mov.l @r5+,r1
+
+__movmem_i4_odd:
+ mov.l @r5+,r1
+ add #-4,r4
+ mov.l @r5+,r2
+ mov.l @r5+,r3
+ mov.l r1,@(4,r4)
+ mov.l r2,@(8,r4)
+
+L_movmem_loop:
+ mov.l r3,@(12,r4)
+ dt r6
+ mov.l @r5+,r0
+ bt/s L_movmem_2mod4_end
+ mov.l @r5+,r1
+ add #16,r4
+L_movmem_start_even:
+ mov.l @r5+,r2
+ mov.l @r5+,r3
+ mov.l r0,@r4
+ dt r6
+ mov.l r1,@(4,r4)
+ bf/s L_movmem_loop
+ mov.l r2,@(8,r4)
+ rts
+ mov.l r3,@(12,r4)
+
+ .p2align 4
+__movmemSI12_i4:
+ mov.l @r5,r0
+ mov.l @(4,r5),r1
+ mov.l @(8,r5),r2
+ mov.l r0,@r4
+ mov.l r1,@(4,r4)
+ rts
+ mov.l r2,@(8,r4)
diff --git a/arch/sh/lib/udiv_qrnnd.S b/arch/sh/lib/udiv_qrnnd.S
new file mode 100644
index 000000000000..32b9a36de943
--- /dev/null
+++ b/arch/sh/lib/udiv_qrnnd.S
@@ -0,0 +1,81 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+ /* r0: rn r1: qn */ /* r0: n1 r4: n0 r5: d r6: d1 */ /* r2: __m */
+ /* n1 < d, but n1 might be larger than d1. */
+ .global __udiv_qrnnd_16
+ .balign 8
+__udiv_qrnnd_16:
+ div0u
+ cmp/hi r6,r0
+ bt .Lots
+ .rept 16
+ div1 r6,r0
+ .endr
+ extu.w r0,r1
+ bt 0f
+ add r6,r0
+0: rotcl r1
+ mulu.w r1,r5
+ xtrct r4,r0
+ swap.w r0,r0
+ sts macl,r2
+ cmp/hs r2,r0
+ sub r2,r0
+ bt 0f
+ addc r5,r0
+ add #-1,r1
+ bt 0f
+1: add #-1,r1
+ rts
+ add r5,r0
+ .balign 8
+.Lots:
+ sub r5,r0
+ swap.w r4,r1
+ xtrct r0,r1
+ clrt
+ mov r1,r0
+ addc r5,r0
+ mov #-1,r1
+ bf/s 1b
+ shlr16 r1
+0: rts
+ nop
diff --git a/arch/sh/lib/udivsi3.S b/arch/sh/lib/udivsi3.S
new file mode 100644
index 000000000000..72157ab5c314
--- /dev/null
+++ b/arch/sh/lib/udivsi3.S
@@ -0,0 +1,87 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+ .balign 4
+ .global __udivsi3
+ .type __udivsi3, @function
+div8:
+ div1 r5,r4
+div7:
+ div1 r5,r4; div1 r5,r4; div1 r5,r4
+ div1 r5,r4; div1 r5,r4; div1 r5,r4; rts; div1 r5,r4
+
+divx4:
+ div1 r5,r4; rotcl r0
+ div1 r5,r4; rotcl r0
+ div1 r5,r4; rotcl r0
+ rts; div1 r5,r4
+
+__udivsi3:
+ sts.l pr,@-r15
+ extu.w r5,r0
+ cmp/eq r5,r0
+ bf/s large_divisor
+ div0u
+ swap.w r4,r0
+ shlr16 r4
+ bsr div8
+ shll16 r5
+ bsr div7
+ div1 r5,r4
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr div8
+ swap.w r4,r4
+ bsr div7
+ div1 r5,r4
+ lds.l @r15+,pr
+ xtrct r4,r0
+ swap.w r0,r0
+ rotcl r0
+ rts
+ shlr16 r5
+
+large_divisor:
+ mov #0,r0
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr divx4
+ rotcl r0
+ bsr divx4
+ rotcl r0
+ bsr divx4
+ rotcl r0
+ bsr divx4
+ rotcl r0
+ lds.l @r15+,pr
+ rts
+ rotcl r0
diff --git a/arch/sh/lib/udivsi3_i4i-Os.S b/arch/sh/lib/udivsi3_i4i-Os.S
new file mode 100644
index 000000000000..4835553e1ea9
--- /dev/null
+++ b/arch/sh/lib/udivsi3_i4i-Os.S
@@ -0,0 +1,149 @@
+/* Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+/* Moderately Space-optimized libgcc routines for the Renesas SH /
+ STMicroelectronics ST40 CPUs.
+ Contributed by J"orn Rennecke joern.rennecke@st.com. */
+
+/* Size: 186 bytes jointly for udivsi3_i4i and sdivsi3_i4i
+ sh4-200 run times:
+ udiv small divisor: 55 cycles
+ udiv large divisor: 52 cycles
+ sdiv small divisor, positive result: 59 cycles
+ sdiv large divisor, positive result: 56 cycles
+ sdiv small divisor, negative result: 65 cycles (*)
+ sdiv large divisor, negative result: 62 cycles (*)
+ (*): r2 is restored in the rts delay slot and has a lingering latency
+ of two more cycles. */
+ .balign 4
+ .global __udivsi3_i4i
+ .global __udivsi3_i4
+ .set __udivsi3_i4, __udivsi3_i4i
+ .type __udivsi3_i4i, @function
+ .type __sdivsi3_i4i, @function
+__udivsi3_i4i:
+ sts pr,r1
+ mov.l r4,@-r15
+ extu.w r5,r0
+ cmp/eq r5,r0
+ swap.w r4,r0
+ shlr16 r4
+ bf/s large_divisor
+ div0u
+ mov.l r5,@-r15
+ shll16 r5
+sdiv_small_divisor:
+ div1 r5,r4
+ bsr div6
+ div1 r5,r4
+ div1 r5,r4
+ bsr div6
+ div1 r5,r4
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr div7
+ swap.w r4,r4
+ div1 r5,r4
+ bsr div7
+ div1 r5,r4
+ xtrct r4,r0
+ mov.l @r15+,r5
+ swap.w r0,r0
+ mov.l @r15+,r4
+ jmp @r1
+ rotcl r0
+div7:
+ div1 r5,r4
+div6:
+ div1 r5,r4; div1 r5,r4; div1 r5,r4
+ div1 r5,r4; div1 r5,r4; rts; div1 r5,r4
+
+divx3:
+ rotcl r0
+ div1 r5,r4
+ rotcl r0
+ div1 r5,r4
+ rotcl r0
+ rts
+ div1 r5,r4
+
+large_divisor:
+ mov.l r5,@-r15
+sdiv_large_divisor:
+ xor r4,r0
+ .rept 4
+ rotcl r0
+ bsr divx3
+ div1 r5,r4
+ .endr
+ mov.l @r15+,r5
+ mov.l @r15+,r4
+ jmp @r1
+ rotcl r0
+
+ .global __sdivsi3_i4i
+ .global __sdivsi3_i4
+ .global __sdivsi3
+ .set __sdivsi3_i4, __sdivsi3_i4i
+ .set __sdivsi3, __sdivsi3_i4i
+__sdivsi3_i4i:
+ mov.l r4,@-r15
+ cmp/pz r5
+ mov.l r5,@-r15
+ bt/s pos_divisor
+ cmp/pz r4
+ neg r5,r5
+ extu.w r5,r0
+ bt/s neg_result
+ cmp/eq r5,r0
+ neg r4,r4
+pos_result:
+ swap.w r4,r0
+ bra sdiv_check_divisor
+ sts pr,r1
+pos_divisor:
+ extu.w r5,r0
+ bt/s pos_result
+ cmp/eq r5,r0
+ neg r4,r4
+neg_result:
+ mova negate_result,r0
+ ;
+ mov r0,r1
+ swap.w r4,r0
+ lds r2,macl
+ sts pr,r2
+sdiv_check_divisor:
+ shlr16 r4
+ bf/s sdiv_large_divisor
+ div0u
+ bra sdiv_small_divisor
+ shll16 r5
+ .balign 4
+negate_result:
+ neg r0,r0
+ jmp @r2
+ sts macl,r2
diff --git a/arch/sh/lib/udivsi3_i4i.S b/arch/sh/lib/udivsi3_i4i.S
new file mode 100644
index 000000000000..f1a79d9c5015
--- /dev/null
+++ b/arch/sh/lib/udivsi3_i4i.S
@@ -0,0 +1,666 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file. (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING. If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+/* This code used shld, thus is not suitable for SH1 / SH2. */
+
+/* Signed / unsigned division without use of FPU, optimized for SH4.
+ Uses a lookup table for divisors in the range -128 .. +128, and
+ div1 with case distinction for larger divisors in three more ranges.
+ The code is lumped together with the table to allow the use of mova. */
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#define L_LSB 0
+#define L_LSWMSB 1
+#define L_MSWLSB 2
+#else
+#define L_LSB 3
+#define L_LSWMSB 2
+#define L_MSWLSB 1
+#endif
+
+ .balign 4
+ .global __udivsi3_i4i
+ .global __udivsi3_i4
+ .set __udivsi3_i4, __udivsi3_i4i
+ .type __udivsi3_i4i, @function
+__udivsi3_i4i:
+ mov.w c128_w, r1
+ div0u
+ mov r4,r0
+ shlr8 r0
+ cmp/hi r1,r5
+ extu.w r5,r1
+ bf udiv_le128
+ cmp/eq r5,r1
+ bf udiv_ge64k
+ shlr r0
+ mov r5,r1
+ shll16 r5
+ mov.l r4,@-r15
+ div1 r5,r0
+ mov.l r1,@-r15
+ div1 r5,r0
+ div1 r5,r0
+ bra udiv_25
+ div1 r5,r0
+
+div_le128:
+ mova div_table_ix,r0
+ bra div_le128_2
+ mov.b @(r0,r5),r1
+udiv_le128:
+ mov.l r4,@-r15
+ mova div_table_ix,r0
+ mov.b @(r0,r5),r1
+ mov.l r5,@-r15
+div_le128_2:
+ mova div_table_inv,r0
+ mov.l @(r0,r1),r1
+ mov r5,r0
+ tst #0xfe,r0
+ mova div_table_clz,r0
+ dmulu.l r1,r4
+ mov.b @(r0,r5),r1
+ bt/s div_by_1
+ mov r4,r0
+ mov.l @r15+,r5
+ sts mach,r0
+ /* clrt */
+ addc r4,r0
+ mov.l @r15+,r4
+ rotcr r0
+ rts
+ shld r1,r0
+
+div_by_1_neg:
+ neg r4,r0
+div_by_1:
+ mov.l @r15+,r5
+ rts
+ mov.l @r15+,r4
+
+div_ge64k:
+ bt/s div_r8
+ div0u
+ shll8 r5
+ bra div_ge64k_2
+ div1 r5,r0
+udiv_ge64k:
+ cmp/hi r0,r5
+ mov r5,r1
+ bt udiv_r8
+ shll8 r5
+ mov.l r4,@-r15
+ div1 r5,r0
+ mov.l r1,@-r15
+div_ge64k_2:
+ div1 r5,r0
+ mov.l zero_l,r1
+ .rept 4
+ div1 r5,r0
+ .endr
+ mov.l r1,@-r15
+ div1 r5,r0
+ mov.w m256_w,r1
+ div1 r5,r0
+ mov.b r0,@(L_LSWMSB,r15)
+ xor r4,r0
+ and r1,r0
+ bra div_ge64k_end
+ xor r4,r0
+
+div_r8:
+ shll16 r4
+ bra div_r8_2
+ shll8 r4
+udiv_r8:
+ mov.l r4,@-r15
+ shll16 r4
+ clrt
+ shll8 r4
+ mov.l r5,@-r15
+div_r8_2:
+ rotcl r4
+ mov r0,r1
+ div1 r5,r1
+ mov r4,r0
+ rotcl r0
+ mov r5,r4
+ div1 r5,r1
+ .rept 5
+ rotcl r0; div1 r5,r1
+ .endr
+ rotcl r0
+ mov.l @r15+,r5
+ div1 r4,r1
+ mov.l @r15+,r4
+ rts
+ rotcl r0
+
+ .global __sdivsi3_i4i
+ .global __sdivsi3_i4
+ .global __sdivsi3
+ .set __sdivsi3_i4, __sdivsi3_i4i
+ .set __sdivsi3, __sdivsi3_i4i
+ .type __sdivsi3_i4i, @function
+ /* This is link-compatible with a __sdivsi3 call,
+ but we effectively clobber only r1. */
+__sdivsi3_i4i:
+ mov.l r4,@-r15
+ cmp/pz r5
+ mov.w c128_w, r1
+ bt/s pos_divisor
+ cmp/pz r4
+ mov.l r5,@-r15
+ neg r5,r5
+ bt/s neg_result
+ cmp/hi r1,r5
+ neg r4,r4
+pos_result:
+ extu.w r5,r0
+ bf div_le128
+ cmp/eq r5,r0
+ mov r4,r0
+ shlr8 r0
+ bf/s div_ge64k
+ cmp/hi r0,r5
+ div0u
+ shll16 r5
+ div1 r5,r0
+ div1 r5,r0
+ div1 r5,r0
+udiv_25:
+ mov.l zero_l,r1
+ div1 r5,r0
+ div1 r5,r0
+ mov.l r1,@-r15
+ .rept 3
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_MSWLSB,r15)
+ xtrct r4,r0
+ swap.w r0,r0
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_LSWMSB,r15)
+div_ge64k_end:
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+ extu.b r0,r0
+ mov.l @r15+,r5
+ or r4,r0
+ mov.l @r15+,r4
+ rts
+ rotcl r0
+
+div_le128_neg:
+ tst #0xfe,r0
+ mova div_table_ix,r0
+ mov.b @(r0,r5),r1
+ mova div_table_inv,r0
+ bt/s div_by_1_neg
+ mov.l @(r0,r1),r1
+ mova div_table_clz,r0
+ dmulu.l r1,r4
+ mov.b @(r0,r5),r1
+ mov.l @r15+,r5
+ sts mach,r0
+ /* clrt */
+ addc r4,r0
+ mov.l @r15+,r4
+ rotcr r0
+ shld r1,r0
+ rts
+ neg r0,r0
+
+pos_divisor:
+ mov.l r5,@-r15
+ bt/s pos_result
+ cmp/hi r1,r5
+ neg r4,r4
+neg_result:
+ extu.w r5,r0
+ bf div_le128_neg
+ cmp/eq r5,r0
+ mov r4,r0
+ shlr8 r0
+ bf/s div_ge64k_neg
+ cmp/hi r0,r5
+ div0u
+ mov.l zero_l,r1
+ shll16 r5
+ div1 r5,r0
+ mov.l r1,@-r15
+ .rept 7
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_MSWLSB,r15)
+ xtrct r4,r0
+ swap.w r0,r0
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_LSWMSB,r15)
+div_ge64k_neg_end:
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+ extu.b r0,r1
+ mov.l @r15+,r5
+ or r4,r1
+div_r8_neg_end:
+ mov.l @r15+,r4
+ rotcl r1
+ rts
+ neg r1,r0
+
+div_ge64k_neg:
+ bt/s div_r8_neg
+ div0u
+ shll8 r5
+ mov.l zero_l,r1
+ .rept 6
+ div1 r5,r0
+ .endr
+ mov.l r1,@-r15
+ div1 r5,r0
+ mov.w m256_w,r1
+ div1 r5,r0
+ mov.b r0,@(L_LSWMSB,r15)
+ xor r4,r0
+ and r1,r0
+ bra div_ge64k_neg_end
+ xor r4,r0
+
+c128_w:
+ .word 128
+
+div_r8_neg:
+ clrt
+ shll16 r4
+ mov r4,r1
+ shll8 r1
+ mov r5,r4
+ .rept 7
+ rotcl r1; div1 r5,r0
+ .endr
+ mov.l @r15+,r5
+ rotcl r1
+ bra div_r8_neg_end
+ div1 r4,r0
+
+m256_w:
+ .word 0xff00
+/* This table has been generated by divtab-sh4.c. */
+ .balign 4
+div_table_clz:
+ .byte 0
+ .byte 1
+ .byte 0
+ .byte -1
+ .byte -1
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+/* Lookup table translating positive divisor to index into table of
+ normalized inverse. N.B. the '0' entry is also the last entry of the
+ previous table, and causes an unaligned access for division by zero. */
+div_table_ix:
+ .byte -6
+ .byte -128
+ .byte -128
+ .byte 0
+ .byte -128
+ .byte -64
+ .byte 0
+ .byte 64
+ .byte -128
+ .byte -96
+ .byte -64
+ .byte -32
+ .byte 0
+ .byte 32
+ .byte 64
+ .byte 96
+ .byte -128
+ .byte -112
+ .byte -96
+ .byte -80
+ .byte -64
+ .byte -48
+ .byte -32
+ .byte -16
+ .byte 0
+ .byte 16
+ .byte 32
+ .byte 48
+ .byte 64
+ .byte 80
+ .byte 96
+ .byte 112
+ .byte -128
+ .byte -120
+ .byte -112
+ .byte -104
+ .byte -96
+ .byte -88
+ .byte -80
+ .byte -72
+ .byte -64
+ .byte -56
+ .byte -48
+ .byte -40
+ .byte -32
+ .byte -24
+ .byte -16
+ .byte -8
+ .byte 0
+ .byte 8
+ .byte 16
+ .byte 24
+ .byte 32
+ .byte 40
+ .byte 48
+ .byte 56
+ .byte 64
+ .byte 72
+ .byte 80
+ .byte 88
+ .byte 96
+ .byte 104
+ .byte 112
+ .byte 120
+ .byte -128
+ .byte -124
+ .byte -120
+ .byte -116
+ .byte -112
+ .byte -108
+ .byte -104
+ .byte -100
+ .byte -96
+ .byte -92
+ .byte -88
+ .byte -84
+ .byte -80
+ .byte -76
+ .byte -72
+ .byte -68
+ .byte -64
+ .byte -60
+ .byte -56
+ .byte -52
+ .byte -48
+ .byte -44
+ .byte -40
+ .byte -36
+ .byte -32
+ .byte -28
+ .byte -24
+ .byte -20
+ .byte -16
+ .byte -12
+ .byte -8
+ .byte -4
+ .byte 0
+ .byte 4
+ .byte 8
+ .byte 12
+ .byte 16
+ .byte 20
+ .byte 24
+ .byte 28
+ .byte 32
+ .byte 36
+ .byte 40
+ .byte 44
+ .byte 48
+ .byte 52
+ .byte 56
+ .byte 60
+ .byte 64
+ .byte 68
+ .byte 72
+ .byte 76
+ .byte 80
+ .byte 84
+ .byte 88
+ .byte 92
+ .byte 96
+ .byte 100
+ .byte 104
+ .byte 108
+ .byte 112
+ .byte 116
+ .byte 120
+ .byte 124
+ .byte -128
+/* 1/64 .. 1/127, normalized. There is an implicit leading 1 in bit 32. */
+ .balign 4
+zero_l:
+ .long 0x0
+ .long 0xF81F81F9
+ .long 0xF07C1F08
+ .long 0xE9131AC0
+ .long 0xE1E1E1E2
+ .long 0xDAE6076C
+ .long 0xD41D41D5
+ .long 0xCD856891
+ .long 0xC71C71C8
+ .long 0xC0E07039
+ .long 0xBACF914D
+ .long 0xB4E81B4F
+ .long 0xAF286BCB
+ .long 0xA98EF607
+ .long 0xA41A41A5
+ .long 0x9EC8E952
+ .long 0x9999999A
+ .long 0x948B0FCE
+ .long 0x8F9C18FA
+ .long 0x8ACB90F7
+ .long 0x86186187
+ .long 0x81818182
+ .long 0x7D05F418
+ .long 0x78A4C818
+ .long 0x745D1746
+ .long 0x702E05C1
+ .long 0x6C16C16D
+ .long 0x68168169
+ .long 0x642C8591
+ .long 0x60581606
+ .long 0x5C9882BA
+ .long 0x58ED2309
+div_table_inv:
+ .long 0x55555556
+ .long 0x51D07EAF
+ .long 0x4E5E0A73
+ .long 0x4AFD6A06
+ .long 0x47AE147B
+ .long 0x446F8657
+ .long 0x41414142
+ .long 0x3E22CBCF
+ .long 0x3B13B13C
+ .long 0x38138139
+ .long 0x3521CFB3
+ .long 0x323E34A3
+ .long 0x2F684BDB
+ .long 0x2C9FB4D9
+ .long 0x29E4129F
+ .long 0x27350B89
+ .long 0x24924925
+ .long 0x21FB7813
+ .long 0x1F7047DD
+ .long 0x1CF06ADB
+ .long 0x1A7B9612
+ .long 0x18118119
+ .long 0x15B1E5F8
+ .long 0x135C8114
+ .long 0x11111112
+ .long 0xECF56BF
+ .long 0xC9714FC
+ .long 0xA6810A7
+ .long 0x8421085
+ .long 0x624DD30
+ .long 0x4104105
+ .long 0x2040811
+ /* maximum error: 0.987342 scaled: 0.921875*/
diff --git a/arch/sh/lib64/Makefile b/arch/sh/lib64/Makefile
index 9950966923a0..4bacb9e83478 100644
--- a/arch/sh/lib64/Makefile
+++ b/arch/sh/lib64/Makefile
@@ -2,7 +2,7 @@
# Makefile for the SH-5 specific library files..
#
# Copyright (C) 2000, 2001 Paolo Alberelli
-# Copyright (C) 2003 Paul Mundt
+# Copyright (C) 2003 - 2008 Paul Mundt
#
# 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
@@ -10,6 +10,8 @@
#
# Panic should really be compiled as PIC
-lib-y := udelay.o c-checksum.o dbg.o panic.o memcpy.o copy_user_memcpy.o \
- copy_page.o clear_page.o
+lib-y := udelay.o c-checksum.o dbg.o panic.o memcpy.o memset.o \
+ copy_user_memcpy.o copy_page.o clear_page.o strcpy.o strlen.o
+# Extracted from libgcc
+lib-y += udivsi3.o udivdi3.o sdivsi3.o
diff --git a/arch/sh/lib64/c-checksum.c b/arch/sh/lib64/c-checksum.c
index 5c284e0cff9c..73c0877e3a29 100644
--- a/arch/sh/lib64/c-checksum.c
+++ b/arch/sh/lib64/c-checksum.c
@@ -35,7 +35,7 @@ static inline unsigned short foldto16(unsigned long x)
static inline unsigned short myfoldto16(unsigned long long x)
{
- /* Fold down to 32-bits so we don't loose in the typedef-less
+ /* Fold down to 32-bits so we don't lose in the typedef-less
network stack. */
/* 64 to 33 */
x = (x & 0xffffffff) + (x >> 32);
@@ -199,7 +199,7 @@ __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
result = (__force u64) saddr + (__force u64) daddr +
(__force u64) sum + ((len + proto) << 8);
- /* Fold down to 32-bits so we don't loose in the typedef-less
+ /* Fold down to 32-bits so we don't lose in the typedef-less
network stack. */
/* 64 to 33 */
result = (result & 0xffffffff) + (result >> 32);
diff --git a/arch/sh/lib64/memcpy.S b/arch/sh/lib64/memcpy.S
new file mode 100644
index 000000000000..dd300c372ce1
--- /dev/null
+++ b/arch/sh/lib64/memcpy.S
@@ -0,0 +1,201 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+!
+! Fast SH memcpy
+!
+! by Toshiyasu Morita (tm@netcom.com)
+! hacked by J"orn Rernnecke (joern.rennecke@superh.com) ("o for o-umlaut)
+! SH5 code Copyright 2002 SuperH Ltd.
+!
+! Entry: ARG0: destination pointer
+! ARG1: source pointer
+! ARG2: byte count
+!
+! Exit: RESULT: destination pointer
+! any other registers in the range r0-r7: trashed
+!
+! Notes: Usually one wants to do small reads and write a longword, but
+! unfortunately it is difficult in some cases to concatanate bytes
+! into a longword on the SH, so this does a longword read and small
+! writes.
+!
+! This implementation makes two assumptions about how it is called:
+!
+! 1.: If the byte count is nonzero, the address of the last byte to be
+! copied is unsigned greater than the address of the first byte to
+! be copied. This could be easily swapped for a signed comparison,
+! but the algorithm used needs some comparison.
+!
+! 2.: When there are two or three bytes in the last word of an 11-or-more
+! bytes memory chunk to b copied, the rest of the word can be read
+! without side effects.
+! This could be easily changed by increasing the minumum size of
+! a fast memcpy and the amount subtracted from r7 before L_2l_loop be 2,
+! however, this would cost a few extra cyles on average.
+! For SHmedia, the assumption is that any quadword can be read in its
+! enirety if at least one byte is included in the copy.
+!
+
+ .section .text..SHmedia32,"ax"
+ .globl memcpy
+ .type memcpy, @function
+ .align 5
+
+memcpy:
+
+#define LDUAQ(P,O,D0,D1) ldlo.q P,O,D0; ldhi.q P,O+7,D1
+#define STUAQ(P,O,D0,D1) stlo.q P,O,D0; sthi.q P,O+7,D1
+#define LDUAL(P,O,D0,D1) ldlo.l P,O,D0; ldhi.l P,O+3,D1
+#define STUAL(P,O,D0,D1) stlo.l P,O,D0; sthi.l P,O+3,D1
+
+ ld.b r3,0,r63
+ pta/l Large,tr0
+ movi 25,r0
+ bgeu/u r4,r0,tr0
+ nsb r4,r0
+ shlli r0,5,r0
+ movi (L1-L0+63*32 + 1) & 0xffff,r1
+ sub r1, r0, r0
+L0: ptrel r0,tr0
+ add r2,r4,r5
+ ptabs r18,tr1
+ add r3,r4,r6
+ blink tr0,r63
+
+/* Rearranged to make cut2 safe */
+ .balign 8
+L4_7: /* 4..7 byte memcpy cntd. */
+ stlo.l r2, 0, r0
+ or r6, r7, r6
+ sthi.l r5, -1, r6
+ stlo.l r5, -4, r6
+ blink tr1,r63
+
+ .balign 8
+L1: /* 0 byte memcpy */
+ nop
+ blink tr1,r63
+ nop
+ nop
+ nop
+ nop
+
+L2_3: /* 2 or 3 byte memcpy cntd. */
+ st.b r5,-1,r6
+ blink tr1,r63
+
+ /* 1 byte memcpy */
+ ld.b r3,0,r0
+ st.b r2,0,r0
+ blink tr1,r63
+
+L8_15: /* 8..15 byte memcpy cntd. */
+ stlo.q r2, 0, r0
+ or r6, r7, r6
+ sthi.q r5, -1, r6
+ stlo.q r5, -8, r6
+ blink tr1,r63
+
+ /* 2 or 3 byte memcpy */
+ ld.b r3,0,r0
+ ld.b r2,0,r63
+ ld.b r3,1,r1
+ st.b r2,0,r0
+ pta/l L2_3,tr0
+ ld.b r6,-1,r6
+ st.b r2,1,r1
+ blink tr0, r63
+
+ /* 4 .. 7 byte memcpy */
+ LDUAL (r3, 0, r0, r1)
+ pta L4_7, tr0
+ ldlo.l r6, -4, r7
+ or r0, r1, r0
+ sthi.l r2, 3, r0
+ ldhi.l r6, -1, r6
+ blink tr0, r63
+
+ /* 8 .. 15 byte memcpy */
+ LDUAQ (r3, 0, r0, r1)
+ pta L8_15, tr0
+ ldlo.q r6, -8, r7
+ or r0, r1, r0
+ sthi.q r2, 7, r0
+ ldhi.q r6, -1, r6
+ blink tr0, r63
+
+ /* 16 .. 24 byte memcpy */
+ LDUAQ (r3, 0, r0, r1)
+ LDUAQ (r3, 8, r8, r9)
+ or r0, r1, r0
+ sthi.q r2, 7, r0
+ or r8, r9, r8
+ sthi.q r2, 15, r8
+ ldlo.q r6, -8, r7
+ ldhi.q r6, -1, r6
+ stlo.q r2, 8, r8
+ stlo.q r2, 0, r0
+ or r6, r7, r6
+ sthi.q r5, -1, r6
+ stlo.q r5, -8, r6
+ blink tr1,r63
+
+Large:
+ ld.b r2, 0, r63
+ pta/l Loop_ua, tr1
+ ori r3, -8, r7
+ sub r2, r7, r22
+ sub r3, r2, r6
+ add r2, r4, r5
+ ldlo.q r3, 0, r0
+ addi r5, -16, r5
+ movi 64+8, r27 // could subtract r7 from that.
+ stlo.q r2, 0, r0
+ sthi.q r2, 7, r0
+ ldx.q r22, r6, r0
+ bgtu/l r27, r4, tr1
+
+ addi r5, -48, r27
+ pta/l Loop_line, tr0
+ addi r6, 64, r36
+ addi r6, -24, r19
+ addi r6, -16, r20
+ addi r6, -8, r21
+
+Loop_line:
+ ldx.q r22, r36, r63
+ alloco r22, 32
+ addi r22, 32, r22
+ ldx.q r22, r19, r23
+ sthi.q r22, -25, r0
+ ldx.q r22, r20, r24
+ ldx.q r22, r21, r25
+ stlo.q r22, -32, r0
+ ldx.q r22, r6, r0
+ sthi.q r22, -17, r23
+ sthi.q r22, -9, r24
+ sthi.q r22, -1, r25
+ stlo.q r22, -24, r23
+ stlo.q r22, -16, r24
+ stlo.q r22, -8, r25
+ bgeu r27, r22, tr0
+
+Loop_ua:
+ addi r22, 8, r22
+ sthi.q r22, -1, r0
+ stlo.q r22, -8, r0
+ ldx.q r22, r6, r0
+ bgtu/l r5, r22, tr1
+
+ add r3, r4, r7
+ ldlo.q r7, -8, r1
+ sthi.q r22, 7, r0
+ ldhi.q r7, -1, r7
+ ptabs r18,tr1
+ stlo.q r22, 0, r0
+ or r1, r7, r1
+ sthi.q r5, 15, r1
+ stlo.q r5, 8, r1
+ blink tr1, r63
+
+ .size memcpy,.-memcpy
diff --git a/arch/sh/lib64/memcpy.c b/arch/sh/lib64/memcpy.c
deleted file mode 100644
index fba436a92bfa..000000000000
--- a/arch/sh/lib64/memcpy.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2002 Mark Debbage (Mark.Debbage@superh.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- */
-
-#include <linux/types.h>
-#include <asm/string.h>
-
-// This is a simplistic optimization of memcpy to increase the
-// granularity of access beyond one byte using aligned
-// loads and stores. This is not an optimal implementation
-// for SH-5 (especially with regard to prefetching and the cache),
-// and a better version should be provided later ...
-
-void *memcpy(void *dest, const void *src, size_t count)
-{
- char *d = (char *) dest, *s = (char *) src;
-
- if (count >= 32) {
- int i = 8 - (((unsigned long) d) & 0x7);
-
- if (i != 8)
- while (i-- && count--) {
- *d++ = *s++;
- }
-
- if (((((unsigned long) d) & 0x7) == 0) &&
- ((((unsigned long) s) & 0x7) == 0)) {
- while (count >= 32) {
- unsigned long long t1, t2, t3, t4;
- t1 = *(unsigned long long *) (s);
- t2 = *(unsigned long long *) (s + 8);
- t3 = *(unsigned long long *) (s + 16);
- t4 = *(unsigned long long *) (s + 24);
- *(unsigned long long *) (d) = t1;
- *(unsigned long long *) (d + 8) = t2;
- *(unsigned long long *) (d + 16) = t3;
- *(unsigned long long *) (d + 24) = t4;
- d += 32;
- s += 32;
- count -= 32;
- }
- while (count >= 8) {
- *(unsigned long long *) d =
- *(unsigned long long *) s;
- d += 8;
- s += 8;
- count -= 8;
- }
- }
-
- if (((((unsigned long) d) & 0x3) == 0) &&
- ((((unsigned long) s) & 0x3) == 0)) {
- while (count >= 4) {
- *(unsigned long *) d = *(unsigned long *) s;
- d += 4;
- s += 4;
- count -= 4;
- }
- }
-
- if (((((unsigned long) d) & 0x1) == 0) &&
- ((((unsigned long) s) & 0x1) == 0)) {
- while (count >= 2) {
- *(unsigned short *) d = *(unsigned short *) s;
- d += 2;
- s += 2;
- count -= 2;
- }
- }
- }
-
- while (count--) {
- *d++ = *s++;
- }
-
- return d;
-}
diff --git a/arch/sh/lib64/memset.S b/arch/sh/lib64/memset.S
new file mode 100644
index 000000000000..2d37b0488552
--- /dev/null
+++ b/arch/sh/lib64/memset.S
@@ -0,0 +1,91 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+!
+! Fast SH memset
+!
+! by Toshiyasu Morita (tm@netcom.com)
+!
+! SH5 code by J"orn Rennecke (joern.rennecke@superh.com)
+! Copyright 2002 SuperH Ltd.
+!
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SHHI shlld
+#define SHLO shlrd
+#else
+#define SHHI shlrd
+#define SHLO shlld
+#endif
+
+ .section .text..SHmedia32,"ax"
+ .globl memset
+ .type memset, @function
+
+ .align 5
+
+memset:
+ pta/l multiquad, tr0
+ andi r2, 7, r22
+ ptabs r18, tr2
+ mshflo.b r3,r3,r3
+ add r4, r22, r23
+ mperm.w r3, r63, r3 // Fill pattern now in every byte of r3
+
+ movi 8, r9
+ bgtu/u r23, r9, tr0 // multiquad
+
+ beqi/u r4, 0, tr2 // Return with size 0 - ensures no mem accesses
+ ldlo.q r2, 0, r7
+ shlli r4, 2, r4
+ movi -1, r8
+ SHHI r8, r4, r8
+ SHHI r8, r4, r8
+ mcmv r7, r8, r3
+ stlo.q r2, 0, r3
+ blink tr2, r63
+
+multiquad:
+ pta/l lastquad, tr0
+ stlo.q r2, 0, r3
+ shlri r23, 3, r24
+ add r2, r4, r5
+ beqi/u r24, 1, tr0 // lastquad
+ pta/l loop, tr1
+ sub r2, r22, r25
+ andi r5, -8, r20 // calculate end address and
+ addi r20, -7*8, r8 // loop end address; This might overflow, so we need
+ // to use a different test before we start the loop
+ bge/u r24, r9, tr1 // loop
+ st.q r25, 8, r3
+ st.q r20, -8, r3
+ shlri r24, 1, r24
+ beqi/u r24, 1, tr0 // lastquad
+ st.q r25, 16, r3
+ st.q r20, -16, r3
+ beqi/u r24, 2, tr0 // lastquad
+ st.q r25, 24, r3
+ st.q r20, -24, r3
+lastquad:
+ sthi.q r5, -1, r3
+ blink tr2,r63
+
+loop:
+!!! alloco r25, 32 // QQQ comment out for short-term fix to SHUK #3895.
+ // QQQ commenting out is locically correct, but sub-optimal
+ // QQQ Sean McGoogan - 4th April 2003.
+ st.q r25, 8, r3
+ st.q r25, 16, r3
+ st.q r25, 24, r3
+ st.q r25, 32, r3
+ addi r25, 32, r25
+ bgeu/l r8, r25, tr1 // loop
+
+ st.q r20, -40, r3
+ st.q r20, -32, r3
+ st.q r20, -24, r3
+ st.q r20, -16, r3
+ st.q r20, -8, r3
+ sthi.q r5, -1, r3
+ blink tr2,r63
+
+ .size memset,.-memset
diff --git a/arch/sh/lib64/sdivsi3.S b/arch/sh/lib64/sdivsi3.S
new file mode 100644
index 000000000000..6a800c6a4904
--- /dev/null
+++ b/arch/sh/lib64/sdivsi3.S
@@ -0,0 +1,131 @@
+ .global __sdivsi3
+ .section .text..SHmedia32,"ax"
+ .align 2
+
+ /* inputs: r4,r5 */
+ /* clobbered: r1,r18,r19,r20,r21,r25,tr0 */
+ /* result in r0 */
+__sdivsi3:
+ ptb __div_table,tr0
+
+ nsb r5, r1
+ shlld r5, r1, r25 /* normalize; [-2 ..1, 1..2) in s2.62 */
+ shari r25, 58, r21 /* extract 5(6) bit index (s2.4 with hole -1..1) */
+ /* bubble */
+ gettr tr0,r20
+ ldx.ub r20, r21, r19 /* u0.8 */
+ shari r25, 32, r25 /* normalize to s2.30 */
+ shlli r21, 1, r21
+ muls.l r25, r19, r19 /* s2.38 */
+ ldx.w r20, r21, r21 /* s2.14 */
+ ptabs r18, tr0
+ shari r19, 24, r19 /* truncate to s2.14 */
+ sub r21, r19, r19 /* some 11 bit inverse in s1.14 */
+ muls.l r19, r19, r21 /* u0.28 */
+ sub r63, r1, r1
+ addi r1, 92, r1
+ muls.l r25, r21, r18 /* s2.58 */
+ shlli r19, 45, r19 /* multiply by two and convert to s2.58 */
+ /* bubble */
+ sub r19, r18, r18
+ shari r18, 28, r18 /* some 22 bit inverse in s1.30 */
+ muls.l r18, r25, r0 /* s2.60 */
+ muls.l r18, r4, r25 /* s32.30 */
+ /* bubble */
+ shari r0, 16, r19 /* s-16.44 */
+ muls.l r19, r18, r19 /* s-16.74 */
+ shari r25, 63, r0
+ shari r4, 14, r18 /* s19.-14 */
+ shari r19, 30, r19 /* s-16.44 */
+ muls.l r19, r18, r19 /* s15.30 */
+ xor r21, r0, r21 /* You could also use the constant 1 << 27. */
+ add r21, r25, r21
+ sub r21, r19, r21
+ shard r21, r1, r21
+ sub r21, r0, r0
+ blink tr0, r63
+
+/* This table has been generated by divtab.c .
+Defects for bias -330:
+ Max defect: 6.081536e-07 at -1.000000e+00
+ Min defect: 2.849516e-08 at 1.030651e+00
+ Max 2nd step defect: 9.606539e-12 at -1.000000e+00
+ Min 2nd step defect: 0.000000e+00 at 0.000000e+00
+ Defect at 1: 1.238659e-07
+ Defect at -2: 1.061708e-07 */
+
+ .balign 2
+ .type __div_table,@object
+ .size __div_table,128
+/* negative division constants */
+ .word -16638
+ .word -17135
+ .word -17737
+ .word -18433
+ .word -19103
+ .word -19751
+ .word -20583
+ .word -21383
+ .word -22343
+ .word -23353
+ .word -24407
+ .word -25582
+ .word -26863
+ .word -28382
+ .word -29965
+ .word -31800
+/* negative division factors */
+ .byte 66
+ .byte 70
+ .byte 75
+ .byte 81
+ .byte 87
+ .byte 93
+ .byte 101
+ .byte 109
+ .byte 119
+ .byte 130
+ .byte 142
+ .byte 156
+ .byte 172
+ .byte 192
+ .byte 214
+ .byte 241
+ .skip 16
+ .global __div_table
+__div_table:
+ .skip 16
+/* positive division factors */
+ .byte 241
+ .byte 214
+ .byte 192
+ .byte 172
+ .byte 156
+ .byte 142
+ .byte 130
+ .byte 119
+ .byte 109
+ .byte 101
+ .byte 93
+ .byte 87
+ .byte 81
+ .byte 75
+ .byte 70
+ .byte 66
+/* positive division constants */
+ .word 31801
+ .word 29966
+ .word 28383
+ .word 26864
+ .word 25583
+ .word 24408
+ .word 23354
+ .word 22344
+ .word 21384
+ .word 20584
+ .word 19752
+ .word 19104
+ .word 18434
+ .word 17738
+ .word 17136
+ .word 16639
diff --git a/arch/sh/lib64/strcpy.S b/arch/sh/lib64/strcpy.S
new file mode 100644
index 000000000000..ea7c9c533eea
--- /dev/null
+++ b/arch/sh/lib64/strcpy.S
@@ -0,0 +1,97 @@
+/* Cloned and hacked for uClibc by Paul Mundt, December 2003 */
+/* Modified by SuperH, Inc. September 2003 */
+! Entry: arg0: destination
+! arg1: source
+! Exit: result: destination
+!
+! SH5 code Copyright 2002 SuperH Ltd.
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SHHI shlld
+#define SHLO shlrd
+#else
+#define SHHI shlrd
+#define SHLO shlld
+#endif
+
+ .section .text..SHmedia32,"ax"
+ .globl strcpy
+ .type strcpy, @function
+ .align 5
+
+strcpy:
+
+ pta/l shortstring,tr1
+ ldlo.q r3,0,r4
+ ptabs r18,tr4
+ shlli r3,3,r7
+ addi r2, 8, r0
+ mcmpeq.b r4,r63,r6
+ SHHI r6,r7,r6
+ bnei/u r6,0,tr1 // shortstring
+ pta/l no_lddst, tr2
+ ori r3,-8,r23
+ sub r2, r23, r0
+ sub r3, r2, r21
+ addi r21, 8, r20
+ ldx.q r0, r21, r5
+ pta/l loop, tr0
+ ori r2,-8,r22
+ mcmpeq.b r5, r63, r6
+ bgt/u r22, r23, tr2 // no_lddst
+
+ // r22 < r23 : Need to do a load from the destination.
+ // r22 == r23 : Doesn't actually need to load from destination,
+ // but still can be handled here.
+ ldlo.q r2, 0, r9
+ movi -1, r8
+ SHLO r8, r7, r8
+ mcmv r4, r8, r9
+ stlo.q r2, 0, r9
+ beqi/l r6, 0, tr0 // loop
+
+ add r5, r63, r4
+ addi r0, 8, r0
+ blink tr1, r63 // shortstring
+no_lddst:
+ // r22 > r23: note that for r22 == r23 the sthi.q would clobber
+ // bytes before the destination region.
+ stlo.q r2, 0, r4
+ SHHI r4, r7, r4
+ sthi.q r0, -1, r4
+ beqi/l r6, 0, tr0 // loop
+
+ add r5, r63, r4
+ addi r0, 8, r0
+shortstring:
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+ pta/l shortstring2,tr1
+ byterev r4,r4
+#endif
+shortstring2:
+ st.b r0,-8,r4
+ andi r4,0xff,r5
+ shlri r4,8,r4
+ addi r0,1,r0
+ bnei/l r5,0,tr1
+ blink tr4,r63 // return
+
+ .balign 8
+loop:
+ stlo.q r0, 0, r5
+ ldx.q r0, r20, r4
+ addi r0, 16, r0
+ sthi.q r0, -9, r5
+ mcmpeq.b r4, r63, r6
+ bnei/u r6, 0, tr1 // shortstring
+ ldx.q r0, r21, r5
+ stlo.q r0, -8, r4
+ sthi.q r0, -1, r4
+ mcmpeq.b r5, r63, r6
+ beqi/l r6, 0, tr0 // loop
+
+ add r5, r63, r4
+ addi r0, 8, r0
+ blink tr1, r63 // shortstring
+
+ .size strcpy,.-strcpy
diff --git a/arch/sh/lib64/strlen.S b/arch/sh/lib64/strlen.S
new file mode 100644
index 000000000000..cbc0d912e5f3
--- /dev/null
+++ b/arch/sh/lib64/strlen.S
@@ -0,0 +1,33 @@
+/*
+ * Simplistic strlen() implementation for SHmedia.
+ *
+ * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
+ */
+
+ .section .text..SHmedia32,"ax"
+ .globl strlen
+ .type strlen,@function
+
+ .balign 16
+strlen:
+ ptabs r18, tr4
+
+ /*
+ * Note: We could easily deal with the NULL case here with a simple
+ * sanity check, though it seems that the behavior we want is to fault
+ * in the event that r2 == NULL, so we don't bother.
+ */
+/* beqi r2, 0, tr4 */ ! Sanity check
+
+ movi -1, r0
+ pta/l loop, tr0
+loop:
+ ld.b r2, 0, r1
+ addi r2, 1, r2
+ addi r0, 1, r0
+ bnei/l r1, 0, tr0
+
+ or r0, r63, r2
+ blink tr4, r63
+
+ .size strlen,.-strlen
diff --git a/arch/sh/lib64/udivdi3.S b/arch/sh/lib64/udivdi3.S
new file mode 100644
index 000000000000..6895c0225b85
--- /dev/null
+++ b/arch/sh/lib64/udivdi3.S
@@ -0,0 +1,120 @@
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global __udivdi3
+__udivdi3:
+ shlri r3,1,r4
+ nsb r4,r22
+ shlld r3,r22,r6
+ shlri r6,49,r5
+ movi 0xffffffffffffbaf1,r21 /* .l shift count 17. */
+ sub r21,r5,r1
+ mmulfx.w r1,r1,r4
+ mshflo.w r1,r63,r1
+ sub r63,r22,r20 // r63 == 64 % 64
+ mmulfx.w r5,r4,r4
+ pta large_divisor,tr0
+ addi r20,32,r9
+ msub.w r1,r4,r1
+ madd.w r1,r1,r1
+ mmulfx.w r1,r1,r4
+ shlri r6,32,r7
+ bgt/u r9,r63,tr0 // large_divisor
+ mmulfx.w r5,r4,r4
+ shlri r2,32+14,r19
+ addi r22,-31,r0
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r19,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ mulu.l r5,r3,r8
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ shlld r8,r0,r8
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r2,r8,r2
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r2. */
+
+ shlri r2,22,r21
+ mulu.l r21,r1,r21
+ shlld r5,r0,r8
+ addi r20,30-22,r0
+ shlrd r21,r0,r21
+ mulu.l r21,r3,r5
+ add r8,r21,r8
+ mcmpgt.l r21,r63,r21 // See Note 1
+ addi r20,30,r0
+ mshfhi.l r63,r21,r21
+ sub r2,r5,r2
+ andc r2,r21,r2
+
+ /* small divisor: need a third divide step */
+ mulu.l r2,r1,r7
+ ptabs r18,tr0
+ addi r2,1,r2
+ shlrd r7,r0,r7
+ mulu.l r7,r3,r5
+ add r8,r7,r8
+ sub r2,r3,r2
+ cmpgt r2,r5,r5
+ add r8,r5,r2
+ /* could test r3 here to check for divide by zero. */
+ blink tr0,r63
+
+large_divisor:
+ mmulfx.w r5,r4,r4
+ shlrd r2,r9,r25
+ shlri r25,32,r8
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r8,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ shlri r5,14-1,r8
+ mulu.l r8,r7,r5
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r25,r5,r25
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r25. */
+
+ shlri r25,22,r21
+ mulu.l r21,r1,r21
+ pta no_lo_adj,tr0
+ addi r22,32,r0
+ shlri r21,40,r21
+ mulu.l r21,r7,r5
+ add r8,r21,r8
+ shlld r2,r0,r2
+ sub r25,r5,r25
+ bgtu/u r7,r25,tr0 // no_lo_adj
+ addi r8,1,r8
+ sub r25,r7,r25
+no_lo_adj:
+ mextr4 r2,r25,r2
+
+ /* large_divisor: only needs a few adjustments. */
+ mulu.l r8,r6,r5
+ ptabs r18,tr0
+ /* bubble */
+ cmpgtu r5,r2,r5
+ sub r8,r5,r2
+ blink tr0,r63
+
+/* Note 1: To shift the result of the second divide stage so that the result
+ always fits into 32 bits, yet we still reduce the rest sufficiently
+ would require a lot of instructions to do the shifts just right. Using
+ the full 64 bit shift result to multiply with the divisor would require
+ four extra instructions for the upper 32 bits (shift / mulu / shift / sub).
+ Fortunately, if the upper 32 bits of the shift result are nonzero, we
+ know that the rest after taking this partial result into account will
+ fit into 32 bits. So we just clear the upper 32 bits of the rest if the
+ upper 32 bits of the partial result are nonzero. */
diff --git a/arch/sh/lib64/udivsi3.S b/arch/sh/lib64/udivsi3.S
new file mode 100644
index 000000000000..e68120e4b847
--- /dev/null
+++ b/arch/sh/lib64/udivsi3.S
@@ -0,0 +1,59 @@
+ .global __udivsi3
+ .section .text..SHmedia32,"ax"
+ .align 2
+
+/*
+ inputs: r4,r5
+ clobbered: r18,r19,r20,r21,r22,r25,tr0
+ result in r0.
+ */
+__udivsi3:
+ addz.l r5,r63,r22
+ nsb r22,r0
+ shlld r22,r0,r25
+ shlri r25,48,r25
+ movi 0xffffffffffffbb0c,r20 /* shift count eqiv 76 */
+ sub r20,r25,r21
+ mmulfx.w r21,r21,r19
+ mshflo.w r21,r63,r21
+ ptabs r18,tr0
+ mmulfx.w r25,r19,r19
+ sub r20,r0,r0
+ /* bubble */
+ msub.w r21,r19,r19
+
+ /*
+ * It would be nice for scheduling to do this add to r21 before
+ * the msub.w, but we need a different value for r19 to keep
+ * errors under control.
+ */
+ addi r19,-2,r21
+ mulu.l r4,r21,r18
+ mmulfx.w r19,r19,r19
+ shlli r21,15,r21
+ shlrd r18,r0,r18
+ mulu.l r18,r22,r20
+ mmacnfx.wl r25,r19,r21
+ /* bubble */
+ sub r4,r20,r25
+
+ mulu.l r25,r21,r19
+ addi r0,14,r0
+ /* bubble */
+ shlrd r19,r0,r19
+ mulu.l r19,r22,r20
+ add r18,r19,r18
+ /* bubble */
+ sub.l r25,r20,r25
+
+ mulu.l r25,r21,r19
+ addz.l r25,r63,r25
+ sub r25,r22,r25
+ shlrd r19,r0,r19
+ mulu.l r19,r22,r20
+ addi r25,1,r25
+ add r18,r19,r18
+
+ cmpgt r25,r20,r25
+ add.l r18,r25,r0
+ blink tr0,r63
diff --git a/arch/sh/mm/Makefile_32 b/arch/sh/mm/Makefile_32
index f066e76da204..cb2f3f299591 100644
--- a/arch/sh/mm/Makefile_32
+++ b/arch/sh/mm/Makefile_32
@@ -18,6 +18,7 @@ mmu-y := tlb-nommu.o pg-nommu.o
mmu-$(CONFIG_MMU) := fault_32.o tlbflush_32.o ioremap_32.o
obj-y += $(mmu-y)
+obj-$(CONFIG_DEBUG_FS) += asids-debugfs.o
ifdef CONFIG_DEBUG_FS
obj-$(CONFIG_CPU_SH4) += cache-debugfs.o
diff --git a/arch/sh/mm/Makefile_64 b/arch/sh/mm/Makefile_64
index 9481d0f54efd..2863ffb7006d 100644
--- a/arch/sh/mm/Makefile_64
+++ b/arch/sh/mm/Makefile_64
@@ -13,6 +13,7 @@ obj-y += cache-sh5.o
endif
obj-y += $(mmu-y)
+obj-$(CONFIG_DEBUG_FS) += asids-debugfs.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_NUMA) += numa.o
diff --git a/arch/sh/mm/asids-debugfs.c b/arch/sh/mm/asids-debugfs.c
new file mode 100644
index 000000000000..8e912a15e94f
--- /dev/null
+++ b/arch/sh/mm/asids-debugfs.c
@@ -0,0 +1,79 @@
+/*
+ * debugfs ops for process ASIDs
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 - 2008 Paul Mundt
+ * Copyright (C) 2003, 2004 Richard Curnow
+ *
+ * Provides a debugfs file that lists out the ASIDs currently associated
+ * with the processes.
+ *
+ * In the SH-5 case, if the DM.PC register is examined through the debug
+ * link, this shows ASID + PC. To make use of this, the PID->ASID
+ * relationship needs to be known. This is primarily for debugging.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+
+static int asids_seq_show(struct seq_file *file, void *iter)
+{
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+
+ for_each_process(p) {
+ int pid = p->pid;
+
+ if (unlikely(!pid))
+ continue;
+
+ if (p->mm)
+ seq_printf(file, "%5d : %02lx\n", pid,
+ cpu_asid(smp_processor_id(), p->mm));
+ else
+ seq_printf(file, "%5d : (none)\n", pid);
+ }
+
+ read_unlock(&tasklist_lock);
+
+ return 0;
+}
+
+static int asids_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, asids_seq_show, inode->i_private);
+}
+
+static const struct file_operations asids_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = asids_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init asids_debugfs_init(void)
+{
+ struct dentry *asids_dentry;
+
+ asids_dentry = debugfs_create_file("asids", S_IRUSR, sh_debugfs_root,
+ NULL, &asids_debugfs_fops);
+ if (!asids_dentry)
+ return -ENOMEM;
+ if (IS_ERR(asids_dentry))
+ return PTR_ERR(asids_dentry);
+
+ return 0;
+}
+module_init(asids_debugfs_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
index 9f8ea3ada4db..edcd5fbf9651 100644
--- a/arch/sh/mm/consistent.c
+++ b/arch/sh/mm/consistent.c
@@ -42,6 +42,8 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
return NULL;
}
+ split_page(pfn_to_page(virt_to_phys(ret) >> PAGE_SHIFT), order);
+
*dma_handle = virt_to_phys(ret);
return ret_nocache;
}
@@ -51,10 +53,13 @@ void dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
int order = get_order(size);
+ unsigned long pfn = dma_handle >> PAGE_SHIFT;
+ int k;
if (!dma_release_from_coherent(dev, order, vaddr)) {
WARN_ON(irqs_disabled()); /* for portability */
- free_pages((unsigned long)phys_to_virt(dma_handle), order);
+ for (k = 0; k < (1 << order); k++)
+ __free_pages(pfn_to_page(pfn + k), 0);
iounmap(vaddr);
}
}
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c
index 898d477e47c1..31a33ebdef6f 100644
--- a/arch/sh/mm/fault_32.c
+++ b/arch/sh/mm/fault_32.c
@@ -20,7 +20,6 @@
#include <asm/system.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
-#include <asm/kgdb.h>
/*
* This routine handles page faults. It determines the address,
@@ -265,17 +264,6 @@ static inline int notify_page_fault(struct pt_regs *regs, int trap)
return ret;
}
-#ifdef CONFIG_SH_STORE_QUEUES
-/*
- * This is a special case for the SH-4 store queues, as pages for this
- * space still need to be faulted in before it's possible to flush the
- * store queue cache for writeout to the remapped region.
- */
-#define P3_ADDR_MAX (P4SEG_STORE_QUE + 0x04000000)
-#else
-#define P3_ADDR_MAX P4SEG
-#endif
-
/*
* Called with interrupts disabled.
*/
@@ -293,11 +281,6 @@ asmlinkage int __kprobes __do_page_fault(struct pt_regs *regs,
if (notify_page_fault(regs, lookup_exception_vector()))
goto out;
-#ifdef CONFIG_SH_KGDB
- if (kgdb_nofault && kgdb_bus_err_hook)
- kgdb_bus_err_hook();
-#endif
-
ret = 1;
/*
diff --git a/arch/sh/mm/ioremap_32.c b/arch/sh/mm/ioremap_32.c
index 882a32ebc6b7..32946fba123e 100644
--- a/arch/sh/mm/ioremap_32.c
+++ b/arch/sh/mm/ioremap_32.c
@@ -116,9 +116,10 @@ EXPORT_SYMBOL(__ioremap);
void __iounmap(void __iomem *addr)
{
unsigned long vaddr = (unsigned long __force)addr;
+ unsigned long seg = PXSEG(vaddr);
struct vm_struct *p;
- if (PXSEG(vaddr) < P3SEG || is_pci_memaddr(vaddr))
+ if (seg < P3SEG || seg >= P3_ADDR_MAX || is_pci_memaddr(vaddr))
return;
#ifdef CONFIG_32BIT
diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c
index 8837d511710a..931f4d003fa0 100644
--- a/arch/sh/mm/mmap.c
+++ b/arch/sh/mm/mmap.c
@@ -9,7 +9,101 @@
*/
#include <linux/io.h>
#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/module.h>
#include <asm/page.h>
+#include <asm/processor.h>
+
+#ifdef CONFIG_MMU
+unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */
+EXPORT_SYMBOL(shm_align_mask);
+
+/*
+ * To avoid cache aliases, we map the shared page with same color.
+ */
+#define COLOUR_ALIGN(addr, pgoff) \
+ ((((addr) + shm_align_mask) & ~shm_align_mask) + \
+ (((pgoff) << PAGE_SHIFT) & shm_align_mask))
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long start_addr;
+ int do_colour_align;
+
+ if (flags & MAP_FIXED) {
+ /* We do not accept a shared mapping if it would violate
+ * cache aliasing constraints.
+ */
+ if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+ return -EINVAL;
+ return addr;
+ }
+
+ if (unlikely(len > TASK_SIZE))
+ return -ENOMEM;
+
+ do_colour_align = 0;
+ if (filp || (flags & MAP_SHARED))
+ do_colour_align = 1;
+
+ if (addr) {
+ if (do_colour_align)
+ addr = COLOUR_ALIGN(addr, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+
+ vma = find_vma(mm, addr);
+ if (TASK_SIZE - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ return addr;
+ }
+
+ if (len > mm->cached_hole_size) {
+ start_addr = addr = mm->free_area_cache;
+ } else {
+ mm->cached_hole_size = 0;
+ start_addr = addr = TASK_UNMAPPED_BASE;
+ }
+
+full_search:
+ if (do_colour_align)
+ addr = COLOUR_ALIGN(addr, pgoff);
+ else
+ addr = PAGE_ALIGN(mm->free_area_cache);
+
+ for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+ /* At this point: (!vma || addr < vma->vm_end). */
+ if (unlikely(TASK_SIZE - len < addr)) {
+ /*
+ * Start a new search - just in case we missed
+ * some holes.
+ */
+ if (start_addr != TASK_UNMAPPED_BASE) {
+ start_addr = addr = TASK_UNMAPPED_BASE;
+ mm->cached_hole_size = 0;
+ goto full_search;
+ }
+ return -ENOMEM;
+ }
+ if (likely(!vma || addr + len <= vma->vm_start)) {
+ /*
+ * Remember the place where we stopped the search:
+ */
+ mm->free_area_cache = addr + len;
+ return addr;
+ }
+ if (addr + mm->cached_hole_size < vma->vm_start)
+ mm->cached_hole_size = vma->vm_start - addr;
+
+ addr = vma->vm_end;
+ if (do_colour_align)
+ addr = COLOUR_ALIGN(addr, pgoff);
+ }
+}
+#endif /* CONFIG_MMU */
/*
* You really shouldn't be using read() or write() on /dev/mem. This
diff --git a/arch/sh/oprofile/Makefile b/arch/sh/oprofile/Makefile
index 2efc2e79fd29..8e6eec91c14c 100644
--- a/arch/sh/oprofile/Makefile
+++ b/arch/sh/oprofile/Makefile
@@ -6,13 +6,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofilefs.o oprofile_stats.o \
timer_int.o )
-profdrvr-y := op_model_null.o
+oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
-# SH7750-style performance counters exist across 7750/7750S and 7091.
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750S) := op_model_sh7750.o
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7750) := op_model_sh7750.o
-profdrvr-$(CONFIG_CPU_SUBTYPE_SH7091) := op_model_sh7750.o
-
-oprofile-y := $(DRIVER_OBJS) $(profdrvr-y)
-
-EXTRA_CFLAGS += -Werror
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7750S) += op_model_sh7750.o
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7750) += op_model_sh7750.o
+oprofile-$(CONFIG_CPU_SUBTYPE_SH7091) += op_model_sh7750.o
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c
new file mode 100644
index 000000000000..9499a2914f89
--- /dev/null
+++ b/arch/sh/oprofile/backtrace.c
@@ -0,0 +1,114 @@
+/*
+ * SH specific backtracing code for oprofile
+ *
+ * Copyright 2007 STMicroelectronics Ltd.
+ *
+ * Author: Dave Peverley <dpeverley@mpc-data.co.uk>
+ *
+ * Based on ARM oprofile backtrace code by Richard Purdie and in turn, i386
+ * oprofile backtrace code by John Levon, David Smith
+ *
+ * This program is free software; you can 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/oprofile.h>
+#include <linux/sched.h>
+#include <linux/kallsyms.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+
+/* Limit to stop backtracing too far. */
+static int backtrace_limit = 20;
+
+static unsigned long *
+user_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
+{
+ unsigned long buf_stack;
+
+ /* Also check accessibility of address */
+ if (!access_ok(VERIFY_READ, stackaddr, sizeof(unsigned long)))
+ return NULL;
+
+ if (__copy_from_user_inatomic(&buf_stack, stackaddr, sizeof(unsigned long)))
+ return NULL;
+
+ /* Quick paranoia check */
+ if (buf_stack & 3)
+ return NULL;
+
+ oprofile_add_trace(buf_stack);
+
+ stackaddr++;
+
+ return stackaddr;
+}
+
+/*
+ * | | /\ Higher addresses
+ * | |
+ * --------------- stack base (address of current_thread_info)
+ * | thread info |
+ * . .
+ * | stack |
+ * --------------- saved regs->regs[15] value if valid
+ * . .
+ * --------------- struct pt_regs stored on stack (struct pt_regs *)
+ * | |
+ * . .
+ * | |
+ * --------------- ???
+ * | |
+ * | | \/ Lower addresses
+ *
+ * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
+ */
+static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs)
+{
+ unsigned long stack = (unsigned long)regs;
+ unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
+
+ return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base);
+}
+
+static unsigned long *
+kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
+{
+ unsigned long addr;
+
+ /*
+ * If not a valid kernel address, keep going till we find one
+ * or the SP stops being a valid address.
+ */
+ do {
+ addr = *stackaddr++;
+ oprofile_add_trace(addr);
+ } while (valid_kernel_stack(stackaddr, regs));
+
+ return stackaddr;
+}
+
+void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+ unsigned long *stackaddr;
+
+ /*
+ * Paranoia - clip max depth as we could get lost in the weeds.
+ */
+ if (depth > backtrace_limit)
+ depth = backtrace_limit;
+
+ stackaddr = (unsigned long *)regs->regs[15];
+ if (!user_mode(regs)) {
+ while (depth-- && valid_kernel_stack(stackaddr, regs))
+ stackaddr = kernel_backtrace(stackaddr, regs);
+
+ return;
+ }
+
+ while (depth-- && (stackaddr != NULL))
+ stackaddr = user_backtrace(stackaddr, regs);
+}
diff --git a/arch/sh/oprofile/common.c b/arch/sh/oprofile/common.c
new file mode 100644
index 000000000000..1d97d64cb95f
--- /dev/null
+++ b/arch/sh/oprofile/common.c
@@ -0,0 +1,150 @@
+/*
+ * arch/sh/oprofile/init.c
+ *
+ * Copyright (C) 2003 - 2008 Paul Mundt
+ *
+ * Based on arch/mips/oprofile/common.c:
+ *
+ * Copyright (C) 2004, 2005 Ralf Baechle
+ * Copyright (C) 2005 MIPS Technologies, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <asm/processor.h>
+#include "op_impl.h"
+
+extern struct op_sh_model op_model_sh7750_ops __weak;
+extern struct op_sh_model op_model_sh4a_ops __weak;
+
+static struct op_sh_model *model;
+
+static struct op_counter_config ctr[20];
+
+extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth);
+
+static int op_sh_setup(void)
+{
+ /* Pre-compute the values to stuff in the hardware registers. */
+ model->reg_setup(ctr);
+
+ /* Configure the registers on all cpus. */
+ on_each_cpu(model->cpu_setup, NULL, 1);
+
+ return 0;
+}
+
+static int op_sh_create_files(struct super_block *sb, struct dentry *root)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < model->num_counters; i++) {
+ struct dentry *dir;
+ char buf[4];
+
+ snprintf(buf, sizeof(buf), "%d", i);
+ dir = oprofilefs_mkdir(sb, root, buf);
+
+ ret |= oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+ ret |= oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+ ret |= oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+ ret |= oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+ if (model->create_files)
+ ret |= model->create_files(sb, dir);
+ else
+ ret |= oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+
+ /* Dummy entries */
+ ret |= oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ }
+
+ return ret;
+}
+
+static int op_sh_start(void)
+{
+ /* Enable performance monitoring for all counters. */
+ on_each_cpu(model->cpu_start, NULL, 1);
+
+ return 0;
+}
+
+static void op_sh_stop(void)
+{
+ /* Disable performance monitoring for all counters. */
+ on_each_cpu(model->cpu_stop, NULL, 1);
+}
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+ struct op_sh_model *lmodel = NULL;
+ int ret;
+
+ /*
+ * Always assign the backtrace op. If the counter initialization
+ * fails, we fall back to the timer which will still make use of
+ * this.
+ */
+ ops->backtrace = sh_backtrace;
+
+ switch (current_cpu_data.type) {
+ /* SH-4 types */
+ case CPU_SH7750:
+ case CPU_SH7750S:
+ lmodel = &op_model_sh7750_ops;
+ break;
+
+ /* SH-4A types */
+ case CPU_SH7763:
+ case CPU_SH7770:
+ case CPU_SH7780:
+ case CPU_SH7781:
+ case CPU_SH7785:
+ case CPU_SH7723:
+ case CPU_SHX3:
+ lmodel = &op_model_sh4a_ops;
+ break;
+
+ /* SH4AL-DSP types */
+ case CPU_SH7343:
+ case CPU_SH7722:
+ case CPU_SH7366:
+ lmodel = &op_model_sh4a_ops;
+ break;
+ }
+
+ if (!lmodel)
+ return -ENODEV;
+ if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER))
+ return -ENODEV;
+
+ ret = lmodel->init();
+ if (unlikely(ret != 0))
+ return ret;
+
+ model = lmodel;
+
+ ops->setup = op_sh_setup;
+ ops->create_files = op_sh_create_files;
+ ops->start = op_sh_start;
+ ops->stop = op_sh_stop;
+ ops->cpu_type = lmodel->cpu_type;
+
+ printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+ lmodel->cpu_type);
+
+ return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+ if (model && model->exit)
+ model->exit();
+}
diff --git a/arch/sh/oprofile/op_impl.h b/arch/sh/oprofile/op_impl.h
new file mode 100644
index 000000000000..4d509975eba6
--- /dev/null
+++ b/arch/sh/oprofile/op_impl.h
@@ -0,0 +1,33 @@
+#ifndef __OP_IMPL_H
+#define __OP_IMPL_H
+
+/* Per-counter configuration as set via oprofilefs. */
+struct op_counter_config {
+ unsigned long enabled;
+ unsigned long event;
+
+ unsigned long long count;
+
+ /* Dummy values for userspace tool compliance */
+ unsigned long kernel;
+ unsigned long user;
+ unsigned long unit_mask;
+};
+
+/* Per-architecture configury and hooks. */
+struct op_sh_model {
+ void (*reg_setup)(struct op_counter_config *);
+ int (*create_files)(struct super_block *sb, struct dentry *dir);
+ void (*cpu_setup)(void *dummy);
+ int (*init)(void);
+ void (*exit)(void);
+ void (*cpu_start)(void *args);
+ void (*cpu_stop)(void *args);
+ char *cpu_type;
+ unsigned char num_counters;
+};
+
+/* arch/sh/oprofile/common.c */
+extern void sh_backtrace(struct pt_regs * const regs, unsigned int depth);
+
+#endif /* __OP_IMPL_H */
diff --git a/arch/sh/oprofile/op_model_null.c b/arch/sh/oprofile/op_model_null.c
deleted file mode 100644
index a845b088edb4..000000000000
--- a/arch/sh/oprofile/op_model_null.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * arch/sh/oprofile/op_model_null.c
- *
- * Copyright (C) 2003 Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-
-int __init oprofile_arch_init(struct oprofile_operations *ops)
-{
- return -ENODEV;
-}
-
-void oprofile_arch_exit(void)
-{
-}
-
diff --git a/arch/sh/oprofile/op_model_sh7750.c b/arch/sh/oprofile/op_model_sh7750.c
index 008b3b03750a..c892c7c30c2f 100644
--- a/arch/sh/oprofile/op_model_sh7750.c
+++ b/arch/sh/oprofile/op_model_sh7750.c
@@ -3,7 +3,7 @@
*
* OProfile support for SH7750/SH7750S Performance Counters
*
- * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2003 - 2008 Paul Mundt
*
* 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
@@ -15,19 +15,16 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/fs.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
+#include "op_impl.h"
#define PM_CR_BASE 0xff000084 /* 16-bit */
#define PM_CTR_BASE 0xff100004 /* 32-bit */
-#define PMCR1 (PM_CR_BASE + 0x00)
-#define PMCR2 (PM_CR_BASE + 0x04)
-#define PMCTR1H (PM_CTR_BASE + 0x00)
-#define PMCTR1L (PM_CTR_BASE + 0x04)
-#define PMCTR2H (PM_CTR_BASE + 0x08)
-#define PMCTR2L (PM_CTR_BASE + 0x0c)
+#define PMCR(n) (PM_CR_BASE + ((n) * 0x04))
+#define PMCTRH(n) (PM_CTR_BASE + 0x00 + ((n) * 0x08))
+#define PMCTRL(n) (PM_CTR_BASE + 0x04 + ((n) * 0x08))
#define PMCR_PMM_MASK 0x0000003f
@@ -36,25 +33,15 @@
#define PMCR_PMST 0x00004000
#define PMCR_PMEN 0x00008000
-#define PMCR_ENABLE (PMCR_PMST | PMCR_PMEN)
+struct op_sh_model op_model_sh7750_ops;
-/*
- * SH7750/SH7750S have 2 perf counters
- */
#define NR_CNTRS 2
-struct op_counter_config {
- unsigned long enabled;
- unsigned long event;
- unsigned long count;
-
- /* Dummy values for userspace tool compliance */
- unsigned long kernel;
- unsigned long user;
- unsigned long unit_mask;
-};
-
-static struct op_counter_config ctr[NR_CNTRS];
+static struct sh7750_ppc_register_config {
+ unsigned int ctrl;
+ unsigned long cnt_hi;
+ unsigned long cnt_lo;
+} regcache[NR_CNTRS];
/*
* There are a number of events supported by each counter (33 in total).
@@ -116,12 +103,8 @@ static int sh7750_timer_notify(struct pt_regs *regs)
static u64 sh7750_read_counter(int counter)
{
- u32 hi, lo;
-
- hi = (counter == 0) ? ctrl_inl(PMCTR1H) : ctrl_inl(PMCTR2H);
- lo = (counter == 0) ? ctrl_inl(PMCTR1L) : ctrl_inl(PMCTR2L);
-
- return (u64)((u64)(hi & 0xffff) << 32) | lo;
+ return (u64)((u64)(__raw_readl(PMCTRH(counter)) & 0xffff) << 32) |
+ __raw_readl(PMCTRL(counter));
}
/*
@@ -170,11 +153,7 @@ static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
*/
WARN_ON(val != 0);
- if (counter == 0) {
- ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
- } else {
- ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
- }
+ __raw_writew(__raw_readw(PMCR(counter)) | PMCR_PMCLR, PMCR(counter));
return count;
}
@@ -184,88 +163,93 @@ static const struct file_operations count_fops = {
.write = sh7750_write_count,
};
-static int sh7750_perf_counter_create_files(struct super_block *sb, struct dentry *root)
+static int sh7750_ppc_create_files(struct super_block *sb, struct dentry *dir)
{
- int i;
+ return oprofilefs_create_file(sb, dir, "count", &count_fops);
+}
- for (i = 0; i < NR_CNTRS; i++) {
- struct dentry *dir;
- char buf[4];
+static void sh7750_ppc_reg_setup(struct op_counter_config *ctr)
+{
+ unsigned int counters = op_model_sh7750_ops.num_counters;
+ int i;
- snprintf(buf, sizeof(buf), "%d", i);
- dir = oprofilefs_mkdir(sb, root, buf);
+ for (i = 0; i < counters; i++) {
+ regcache[i].ctrl = 0;
+ regcache[i].cnt_hi = 0;
+ regcache[i].cnt_lo = 0;
- oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
- oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
- oprofilefs_create_file(sb, dir, "count", &count_fops);
+ if (!ctr[i].enabled)
+ continue;
- /* Dummy entries */
- oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
- oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
- oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+ regcache[i].ctrl |= ctr[i].event | PMCR_PMEN | PMCR_PMST;
+ regcache[i].cnt_hi = (unsigned long)((ctr->count >> 32) & 0xffff);
+ regcache[i].cnt_lo = (unsigned long)(ctr->count & 0xffffffff);
}
-
- return 0;
}
-static int sh7750_perf_counter_start(void)
+static void sh7750_ppc_cpu_setup(void *args)
{
- u16 pmcr;
-
- /* Enable counter 1 */
- if (ctr[0].enabled) {
- pmcr = ctrl_inw(PMCR1);
- WARN_ON(pmcr & PMCR_PMEN);
-
- pmcr &= ~PMCR_PMM_MASK;
- pmcr |= ctr[0].event;
- ctrl_outw(pmcr | PMCR_ENABLE, PMCR1);
- }
-
- /* Enable counter 2 */
- if (ctr[1].enabled) {
- pmcr = ctrl_inw(PMCR2);
- WARN_ON(pmcr & PMCR_PMEN);
+ unsigned int counters = op_model_sh7750_ops.num_counters;
+ int i;
- pmcr &= ~PMCR_PMM_MASK;
- pmcr |= ctr[1].event;
- ctrl_outw(pmcr | PMCR_ENABLE, PMCR2);
+ for (i = 0; i < counters; i++) {
+ __raw_writew(0, PMCR(i));
+ __raw_writel(regcache[i].cnt_hi, PMCTRH(i));
+ __raw_writel(regcache[i].cnt_lo, PMCTRL(i));
}
-
- return register_timer_hook(sh7750_timer_notify);
}
-static void sh7750_perf_counter_stop(void)
+static void sh7750_ppc_cpu_start(void *args)
{
- ctrl_outw(ctrl_inw(PMCR1) & ~PMCR_PMEN, PMCR1);
- ctrl_outw(ctrl_inw(PMCR2) & ~PMCR_PMEN, PMCR2);
+ unsigned int counters = op_model_sh7750_ops.num_counters;
+ int i;
- unregister_timer_hook(sh7750_timer_notify);
+ for (i = 0; i < counters; i++)
+ __raw_writew(regcache[i].ctrl, PMCR(i));
}
-static struct oprofile_operations sh7750_perf_counter_ops = {
- .create_files = sh7750_perf_counter_create_files,
- .start = sh7750_perf_counter_start,
- .stop = sh7750_perf_counter_stop,
-};
-
-int __init oprofile_arch_init(struct oprofile_operations *ops)
+static void sh7750_ppc_cpu_stop(void *args)
{
- if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER))
- return -ENODEV;
+ unsigned int counters = op_model_sh7750_ops.num_counters;
+ int i;
- ops = &sh7750_perf_counter_ops;
- ops->cpu_type = "sh/sh7750";
+ /* Disable the counters */
+ for (i = 0; i < counters; i++)
+ __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
+}
- printk(KERN_INFO "oprofile: using SH-4 performance monitoring.\n");
+static inline void sh7750_ppc_reset(void)
+{
+ unsigned int counters = op_model_sh7750_ops.num_counters;
+ int i;
/* Clear the counters */
- ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
- ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
+ for (i = 0; i < counters; i++)
+ __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMCLR, PMCR(i));
+}
- return 0;
+static int sh7750_ppc_init(void)
+{
+ sh7750_ppc_reset();
+
+ return register_timer_hook(sh7750_timer_notify);
}
-void oprofile_arch_exit(void)
+static void sh7750_ppc_exit(void)
{
+ unregister_timer_hook(sh7750_timer_notify);
+
+ sh7750_ppc_reset();
}
+
+struct op_sh_model op_model_sh7750_ops = {
+ .cpu_type = "sh/sh7750",
+ .num_counters = NR_CNTRS,
+ .reg_setup = sh7750_ppc_reg_setup,
+ .cpu_setup = sh7750_ppc_cpu_setup,
+ .cpu_start = sh7750_ppc_cpu_start,
+ .cpu_stop = sh7750_ppc_cpu_stop,
+ .init = sh7750_ppc_init,
+ .exit = sh7750_ppc_exit,
+ .create_files = sh7750_ppc_create_files,
+};
diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types
index d0c2928d1066..284b7e867496 100644
--- a/arch/sh/tools/mach-types
+++ b/arch/sh/tools/mach-types
@@ -8,6 +8,7 @@
SE SH_SOLUTION_ENGINE
HIGHLANDER SH_HIGHLANDER
RTS7751R2D SH_RTS7751R2D
+RSK SH_RSK
#
# List of companion chips / MFDs.
@@ -46,6 +47,7 @@ R2D_1 RTS7751R2D_1
CAYMAN SH_CAYMAN
SDK7780 SH_SDK7780
MIGOR SH_MIGOR
+RSK7201 SH_RSK7201
RSK7203 SH_RSK7203
AP325RXA SH_AP325RXA
SH7763RDP SH_SH7763RDP
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index e594559c8dba..0a94d9c9cde1 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -4,21 +4,116 @@
mainmenu "Linux/SPARC Kernel Configuration"
+config SPARC
+ bool
+ default y
+ select HAVE_IDE
+ select HAVE_OPROFILE
+ select HAVE_ARCH_KGDB if !SMP || SPARC64
+ select HAVE_ARCH_TRACEHOOK
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select RTC_CLASS
+ select RTC_DRV_M48T59
+
+# Identify this as a Sparc32 build
+config SPARC32
+ bool
+ default y if ARCH = "sparc"
+ help
+ SPARC is a family of RISC microprocessors designed and marketed by
+ Sun Microsystems, incorporated. They are very widely found in Sun
+ workstations and clones. This port covers the original 32-bit SPARC;
+ it is old and stable and usually considered one of the "big three"
+ along with the Intel and Alpha ports. The UltraLinux project
+ maintains both the SPARC32 and SPARC64 ports; its web page is
+ available at <http://www.ultralinux.org/>.
+
+config SPARC64
+ bool
+ default y if ARCH = "sparc64"
+ select ARCH_SUPPORTS_MSI
+ select HAVE_FUNCTION_TRACER
+ select HAVE_KRETPROBES
+ select HAVE_KPROBES
+ select HAVE_LMB
+ select USE_GENERIC_SMP_HELPERS if SMP
+ select RTC_DRV_CMOS
+ select RTC_DRV_BQ4802
+ select RTC_DRV_SUN4V
+ select RTC_DRV_STARFIRE
+
+config ARCH_DEFCONFIG
+ string
+ default "arch/sparc/configs/sparc32_defconfig" if SPARC32
+ default "arch/sparc/configs/sparc64_defconfig" if SPARC64
+
+# CONFIG_BITS can be used at source level to get 32/64 bits
+config BITS
+ int
+ default 32 if SPARC32
+ default 64 if SPARC64
+
+config 64BIT
+ def_bool y if SPARC64
+
+config GENERIC_TIME
+ bool
+ default y if SPARC64
+
+config GENERIC_CMOS_UPDATE
+ bool
+ default y if SPARC64
+
+config GENERIC_CLOCKEVENTS
+ bool
+ default y if SPARC64
+
+config IOMMU_HELPER
+ bool
+ default y if SPARC64
+
+config QUICKLIST
+ bool
+ default y if SPARC64
+
+config STACKTRACE_SUPPORT
+ bool
+ default y if SPARC64
+
+config LOCKDEP_SUPPORT
+ bool
+ default y if SPARC64
+
+config HAVE_LATENCYTOP_SUPPORT
+ bool
+ default y if SPARC64
+
+config AUDIT_ARCH
+ bool
+ default y
+
+config HAVE_SETUP_PER_CPU_AREA
+ def_bool y if SPARC64
+
+config GENERIC_HARDIRQS_NO__DO_IRQ
+ bool
+ def_bool y if SPARC64
+
config MMU
bool
default y
config HIGHMEM
bool
- default y
+ default y if SPARC32
config ZONE_DMA
bool
- default y
+ default y if SPARC32
config GENERIC_ISA_DMA
bool
- default y
+ default y if SPARC32
config GENERIC_GPIO
bool
@@ -31,15 +126,11 @@ config ARCH_NO_VIRT_TO_BUS
config OF
def_bool y
-config HZ
- int
- default 100
-
source "init/Kconfig"
source "kernel/Kconfig.freezer"
-menu "General machine setup"
+menu "Processor type and features"
config SMP
bool "Symmetric multi-processing support (does not work on sun4/sun4c)"
@@ -64,82 +155,269 @@ config SMP
If you don't know what to do here, say N.
config NR_CPUS
- int "Maximum number of CPUs (2-32)"
- range 2 32
+ int "Maximum number of CPUs"
depends on SMP
- default "32"
+ range 2 32 if SPARC32
+ range 2 1024 if SPARC64
+ default 32 if SPARC32
+ default 64 if SPARC64
-config SPARC
+source kernel/Kconfig.hz
+
+config RWSEM_GENERIC_SPINLOCK
+ bool
+ default y if SPARC32
+
+config RWSEM_XCHGADD_ALGORITHM
+ bool
+ default y if SPARC64
+
+config GENERIC_FIND_NEXT_BIT
bool
default y
- select HAVE_IDE
- select HAVE_OPROFILE
- select HAVE_ARCH_KGDB if !SMP
- select HAVE_ARCH_TRACEHOOK
- select ARCH_WANT_OPTIONAL_GPIOLIB
- select RTC_CLASS
- select RTC_DRV_M48T59
-# Identify this as a Sparc32 build
-config SPARC32
+config GENERIC_HWEIGHT
+ bool
+ default y if !ULTRA_HAS_POPULATION_COUNT
+
+config GENERIC_CALIBRATE_DELAY
bool
default y
- help
- SPARC is a family of RISC microprocessors designed and marketed by
- Sun Microsystems, incorporated. They are very widely found in Sun
- workstations and clones. This port covers the original 32-bit SPARC;
- it is old and stable and usually considered one of the "big three"
- along with the Intel and Alpha ports. The UltraLinux project
- maintains both the SPARC32 and SPARC64 ports; its web page is
- available at <http://www.ultralinux.org/>.
-# Global things across all Sun machines.
-config ISA
+config ARCH_MAY_HAVE_PC_FDC
bool
- help
- ISA is found on Espresso only and is not supported currently.
- Say N
+ default y
-config EISA
+config ARCH_HAS_ILOG2_U32
bool
+ default n
+
+config ARCH_HAS_ILOG2_U64
+ bool
+ default n
+
+config EMULATED_CMPXCHG
+ bool
+ default y if SPARC32
help
- EISA is not supported.
- Say N
+ Sparc32 does not have a CAS instruction like sparc64. cmpxchg()
+ is emulated, and therefore it is not completely atomic.
-config MCA
+# Makefile helpers
+config SPARC32_SMP
+ bool
+ default y
+ depends on SPARC32 && SMP
+
+config SPARC64_SMP
bool
+ default y
+ depends on SPARC64 && SMP
+
+choice
+ prompt "Kernel page size" if SPARC64
+ default SPARC64_PAGE_SIZE_8KB
+
+config SPARC64_PAGE_SIZE_8KB
+ bool "8KB"
help
- MCA is not supported.
- Say N
+ This lets you select the page size of the kernel.
-config PCMCIA
- tristate
- ---help---
- Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
- computer. These are credit-card size devices such as network cards,
- modems or hard drives often used with laptops computers. There are
- actually two varieties of these cards: the older 16 bit PCMCIA cards
- and the newer 32 bit CardBus cards. If you want to use CardBus
- cards, you need to say Y here and also to "CardBus support" below.
+ 8KB and 64KB work quite well, since SPARC ELF sections
+ provide for up to 64KB alignment.
- To use your PC-cards, you will need supporting software from David
- Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
- for location). Please also read the PCMCIA-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
+ If you don't know what to do, choose 8KB.
- To compile this driver as modules, choose M here: the
- modules will be called pcmcia_core and ds.
+config SPARC64_PAGE_SIZE_64KB
+ bool "64KB"
-config SBUS
+endchoice
+
+config SECCOMP
+ bool "Enable seccomp to safely compute untrusted bytecode"
+ depends on SPARC64 && PROC_FS
+ default y
+ 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. Only embedded should say N here.
+
+config HOTPLUG_CPU
+ bool "Support for hot-pluggable CPUs"
+ depends on SPARC64 && SMP
+ select HOTPLUG
+ help
+ Say Y here to experiment with turning CPUs off and on. CPUs
+ can be controlled through /sys/devices/system/cpu/cpu#.
+ Say N if you want to disable CPU hotplug.
+
+config GENERIC_HARDIRQS
bool
+ default y if SPARC64
+
+source "kernel/time/Kconfig"
+
+if SPARC64
+source "drivers/cpufreq/Kconfig"
+
+config US3_FREQ
+ tristate "UltraSPARC-III CPU Frequency driver"
+ depends on CPU_FREQ
+ select CPU_FREQ_TABLE
+ help
+ This adds the CPUFreq driver for UltraSPARC-III processors.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+config US2E_FREQ
+ tristate "UltraSPARC-IIe CPU Frequency driver"
+ depends on CPU_FREQ
+ select CPU_FREQ_TABLE
+ help
+ This adds the CPUFreq driver for UltraSPARC-IIe processors.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+endif
+
+config US3_MC
+ tristate "UltraSPARC-III Memory Controller driver"
+ depends on SPARC64
default y
+ help
+ This adds a driver for the UltraSPARC-III memory controller.
+ Loading this driver allows exact mnemonic strings to be
+ printed in the event of a memory error, so that the faulty DIMM
+ on the motherboard can be matched to the error.
-config SBUSCHAR
+ If in doubt, say Y, as this information can be very useful.
+
+# Global things across all Sun machines.
+config GENERIC_LOCKBREAK
bool
default y
+ depends on SPARC64 && SMP && PREEMPT
+
+choice
+ prompt "SPARC64 Huge TLB Page Size"
+ depends on SPARC64 && HUGETLB_PAGE
+ default HUGETLB_PAGE_SIZE_4MB
+
+config HUGETLB_PAGE_SIZE_4MB
+ bool "4MB"
+
+config HUGETLB_PAGE_SIZE_512K
+ bool "512K"
+
+config HUGETLB_PAGE_SIZE_64K
+ depends on !SPARC64_PAGE_SIZE_64KB
+ bool "64K"
+
+endchoice
+
+config NUMA
+ bool "NUMA support"
+ depends on SPARC64 && SMP
+
+config NODES_SHIFT
+ int
+ default "4"
+ depends on NEED_MULTIPLE_NODES
+
+# Some NUMA nodes have memory ranges that span
+# other nodes. Even though a pfn is valid and
+# between a node's start and end pfns, it may not
+# reside on that node. See memmap_init_zone()
+# for details.
+config NODES_SPAN_OTHER_NODES
+ def_bool y
+ depends on NEED_MULTIPLE_NODES
+
+config ARCH_POPULATES_NODE_MAP
+ def_bool y if SPARC64
+
+config ARCH_SELECT_MEMORY_MODEL
+ def_bool y if SPARC64
+
+config ARCH_SPARSEMEM_ENABLE
+ def_bool y if SPARC64
+ select SPARSEMEM_VMEMMAP_ENABLE
+
+config ARCH_SPARSEMEM_DEFAULT
+ def_bool y if SPARC64
+
+source "mm/Kconfig"
+
+config SCHED_SMT
+ bool "SMT (Hyperthreading) scheduler support"
+ depends on SPARC64 && SMP
+ default y
+ help
+ SMT scheduler support improves the CPU scheduler's decision making
+ when dealing with SPARC cpus at a cost of slightly increased overhead
+ in some places. If unsure say N here.
+
+config SCHED_MC
+ bool "Multi-core scheduler support"
+ depends on SPARC64 && SMP
+ default y
+ help
+ Multi-core scheduler support improves the CPU scheduler's decision
+ making when dealing with multi-core CPU chips at a cost of slightly
+ increased overhead in some places. If unsure say N here.
+
+if SPARC64
+source "kernel/Kconfig.preempt"
+endif
+
+config CMDLINE_BOOL
+ bool "Default bootloader kernel arguments"
+ depends on SPARC64
+
+config CMDLINE
+ string "Initial kernel command string"
+ depends on CMDLINE_BOOL
+ default "console=ttyS0,9600 root=/dev/sda1"
+ help
+ Say Y here if you want to be able to pass default arguments to
+ the kernel. This will be overridden by the bootloader, if you
+ use one (such as SILO). This is most useful if you want to boot
+ a kernel from TFTP, and want default options to be available
+ with having them passed on the command line.
+
+ NOTE: This option WILL override the PROM bootargs setting!
+
+config SUN_PM
+ bool
+ default y if SPARC32
+ help
+ Enable power management and CPU standby features on supported
+ SPARC platforms.
+
+config SPARC_LED
+ tristate "Sun4m LED driver"
+ depends on SPARC32
+ help
+ This driver toggles the front-panel LED on sun4m systems
+ in a user-specifiable manner. Its state can be probed
+ by reading /proc/led and its blinking mode can be changed
+ via writes to /proc/led
config SERIAL_CONSOLE
bool
+ depends on SPARC32
default y
---help---
If you say Y here, it will be possible to use a serial port as the
@@ -161,71 +439,66 @@ config SERIAL_CONSOLE
If unsure, say N.
-config SUN_AUXIO
- bool
- default y
-
-config SUN_IO
- bool
- default y
-
-config RWSEM_GENERIC_SPINLOCK
- bool
- default y
+endmenu
-config RWSEM_XCHGADD_ALGORITHM
+menu "Bus options (PCI etc.)"
+config ISA
bool
+ help
+ ISA is found on Espresso only and is not supported currently.
-config GENERIC_FIND_NEXT_BIT
+config ISAPNP
bool
- default y
+ help
+ ISAPNP is not supported
-config GENERIC_HWEIGHT
+config EISA
bool
- default y
+ help
+ EISA is not supported.
-config GENERIC_CALIBRATE_DELAY
+config MCA
bool
- default y
+ help
+ MCA is not supported.
-config ARCH_MAY_HAVE_PC_FDC
+config SBUS
bool
default y
-config ARCH_HAS_ILOG2_U32
- bool
- default n
-
-config ARCH_HAS_ILOG2_U64
- bool
- default n
-
-config EMULATED_CMPXCHG
+config SBUSCHAR
bool
default y
- help
- Sparc32 does not have a CAS instruction like sparc64. cmpxchg()
- is emulated, and therefore it is not completely atomic.
-config SUN_PM
- bool
- default y
+config SUN_LDOMS
+ bool "Sun Logical Domains support"
+ depends on SPARC64
help
- Enable power management and CPU standby features on supported
- SPARC platforms.
+ Say Y here is you want to support virtual devices via
+ Logical Domains.
config PCI
bool "Support for PCI and PS/2 keyboard/mouse"
help
+ Find out whether your system includes a PCI bus. PCI is the name of
+ a bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. If you say Y here, the kernel will include drivers and
+ infrastructure code to support PCI bus devices.
+
CONFIG_PCI is needed for all JavaStation's (including MrCoffee),
CP-1200, JavaEngine-1, Corona, Red October, and Serengeti SGSC.
All of these platforms are extremely obscure, so say N if unsure.
+config PCI_DOMAINS
+ def_bool PCI if SPARC64
+
config PCI_SYSCALL
def_bool PCI
source "drivers/pci/Kconfig"
+source "drivers/pcmcia/Kconfig"
+
config SUN_OPENPROMFS
tristate "Openprom tree appears in /proc/openprom"
help
@@ -239,17 +512,33 @@ config SUN_OPENPROMFS
Only choose N if you know in advance that you will not need to modify
OpenPROM settings on the running system.
-config SPARC_LED
- tristate "Sun4m LED driver"
- help
- This driver toggles the front-panel LED on sun4m systems
- in a user-specifiable manner. Its state can be probed
- by reading /proc/led and its blinking mode can be changed
- via writes to /proc/led
+# Makefile helpers
+config SPARC32_PCI
+ bool
+ default y
+ depends on SPARC32 && PCI
+
+config SPARC64_PCI
+ bool
+ default y
+ depends on SPARC64 && PCI
+
+endmenu
+
+menu "Executable file formats"
source "fs/Kconfig.binfmt"
-source "mm/Kconfig"
+config COMPAT
+ bool
+ depends on SPARC64
+ default y
+ select COMPAT_BINFMT_ELF
+
+config SYSVIPC_COMPAT
+ bool
+ depends on COMPAT && SYSVIPC
+ default y
endmenu
@@ -259,40 +548,6 @@ source "drivers/Kconfig"
source "drivers/sbus/char/Kconfig"
-# This one must be before the filesystem configs. -DaveM
-
-menu "Unix98 PTY support"
-
-config UNIX98_PTYS
- bool "Unix98 PTY support"
- ---help---
- A pseudo terminal (PTY) is a software device consisting of two
- halves: a master and a slave. The slave device behaves identical to
- a physical terminal; the master device is used by a process to
- read data from and write data to the slave, thereby emulating a
- terminal. Typical programs for the master side are telnet servers
- and xterms.
-
- Linux has traditionally used the BSD-like names /dev/ptyxx for
- masters and /dev/ttyxx for slaves of pseudo terminals. This scheme
- has a number of problems. The GNU C library glibc 2.1 and later,
- however, supports the Unix98 naming standard: in order to acquire a
- pseudo terminal, a process opens /dev/ptmx; the number of the pseudo
- terminal is then made available to the process and the pseudo
- terminal slave can be accessed as /dev/pts/<number>. What was
- traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
-
- The entries in /dev/pts/ are created on the fly by a virtual
- file system; therefore, if you say Y here you should say Y to
- "/dev/pts file system for Unix98 PTYs" as well.
-
- If you want to say Y here, you need to have the C library glibc 2.1
- or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*").
- Read the instructions in <file:Documentation/Changes> pertaining to
- pseudo terminals. It's safe to say N.
-
-endmenu
-
source "fs/Kconfig"
source "arch/sparc/Kconfig.debug"
diff --git a/arch/sparc/Kconfig.debug b/arch/sparc/Kconfig.debug
index 87dd496f15eb..b8a15e271bfa 100644
--- a/arch/sparc/Kconfig.debug
+++ b/arch/sparc/Kconfig.debug
@@ -15,4 +15,30 @@ config DEBUG_STACK_USAGE
This option will slow down process creation somewhat.
+config DEBUG_DCFLUSH
+ bool "D-cache flush debugging"
+ depends on SPARC64 && DEBUG_KERNEL
+
+config STACK_DEBUG
+ bool "Stack Overflow Detection Support"
+
+config DEBUG_PAGEALLOC
+ bool "Debug page memory allocations"
+ depends on SPARC64 && DEBUG_KERNEL && !HIBERNATION
+ help
+ Unmap pages from the kernel linear mapping after free_pages().
+ This results in a large slowdown, but helps to find certain types
+ of memory corruptions.
+
+config MCOUNT
+ bool
+ depends on SPARC64
+ depends on STACK_DEBUG || FUNCTION_TRACER
+ default y
+
+config FRAME_POINTER
+ bool
+ depends on MCOUNT
+ default y
+
endmenu
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 9592889a6fd0..2003ded054c2 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -2,18 +2,31 @@
# sparc/Makefile
#
# Makefile for the architecture dependent flags and dependencies on the
-# Sparc.
+# Sparc and sparc64.
#
-# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1994,1996,1998 David S. Miller (davem@caip.rutgers.edu)
+# Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
+
+# We are not yet configured - so test on arch
+ifeq ($(ARCH),sparc)
+ KBUILD_DEFCONFIG := sparc32_defconfig
+else
+ KBUILD_DEFCONFIG := sparc64_defconfig
+endif
+
+ifeq ($(CONFIG_SPARC32),y)
+#####
+# sparc32
#
#
# Uncomment the first KBUILD_CFLAGS if you are doing kgdb source level
# debugging of the kernel to get the proper debugging information.
-AS := $(AS) -32
-LDFLAGS := -m elf32_sparc
-CHECKFLAGS += -D__sparc__
+AS := $(AS) -32
+LDFLAGS := -m elf32_sparc
+CHECKFLAGS += -D__sparc__
+export BITS := 32
#KBUILD_CFLAGS += -g -pipe -fcall-used-g5 -fcall-used-g7
KBUILD_CFLAGS += -m32 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7
@@ -25,38 +38,60 @@ CPPFLAGS_vmlinux.lds += -m32
# Actual linking is done with "make image".
LDFLAGS_vmlinux = -r
-head-y := arch/sparc/kernel/head.o arch/sparc/kernel/init_task.o
-HEAD_Y := $(head-y)
+# Default target
+all: zImage
+
+
+else
+#####
+# sparc64
+#
-core-y += arch/sparc/kernel/ arch/sparc/mm/ arch/sparc/math-emu/
-libs-y += arch/sparc/prom/ arch/sparc/lib/
+CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64
+
+# Undefine sparc when processing vmlinux.lds - it is used
+# And teach CPP we are doing 64 bit builds (for this case)
+CPPFLAGS_vmlinux.lds += -m64 -Usparc
+LDFLAGS := -m elf64_sparc
+export BITS := 64
+
+KBUILD_CFLAGS += -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \
+ -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare \
+ -Wa,--undeclared-regs
+KBUILD_CFLAGS += $(call cc-option,-mtune=ultrasparc3)
+KBUILD_AFLAGS += -m64 -mcpu=ultrasparc -Wa,--undeclared-regs
+
+ifeq ($(CONFIG_MCOUNT),y)
+ KBUILD_CFLAGS += -pg
+endif
+
+endif
+
+head-y := arch/sparc/kernel/head_$(BITS).o
+head-y += arch/sparc/kernel/init_task.o
+
+core-y += arch/sparc/kernel/
+core-y += arch/sparc/mm/ arch/sparc/math-emu/
+
+libs-y += arch/sparc/prom/
+libs-y += arch/sparc/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc/oprofile/
# Export what is needed by arch/sparc/boot/Makefile
-# Renaming is done to avoid confusing pattern matching rules in 2.5.45 (multy-)
-INIT_Y := $(patsubst %/, %/built-in.o, $(init-y))
-CORE_Y := $(core-y)
-CORE_Y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
-CORE_Y := $(patsubst %/, %/built-in.o, $(CORE_Y))
-DRIVERS_Y := $(patsubst %/, %/built-in.o, $(drivers-y))
-NET_Y := $(patsubst %/, %/built-in.o, $(net-y))
-LIBS_Y1 := $(patsubst %/, %/lib.a, $(libs-y))
-LIBS_Y2 := $(patsubst %/, %/built-in.o, $(libs-y))
-LIBS_Y := $(LIBS_Y1) $(LIBS_Y2)
+export VMLINUX_INIT VMLINUX_MAIN
+VMLINUX_INIT := $(head-y) $(init-y)
+VMLINUX_MAIN := $(core-y) kernel/ mm/ fs/ ipc/ security/ crypto/ block/
+VMLINUX_MAIN += $(patsubst %/, %/lib.a, $(libs-y)) $(libs-y)
+VMLINUX_MAIN += $(drivers-y) $(net-y)
ifdef CONFIG_KALLSYMS
-kallsyms.o := .tmp_kallsyms2.o
+export kallsyms.o := .tmp_kallsyms2.o
endif
-export INIT_Y CORE_Y DRIVERS_Y NET_Y LIBS_Y HEAD_Y kallsyms.o
-
-# Default target
-all: zImage
-
boot := arch/sparc/boot
-image zImage tftpboot.img: vmlinux
+image zImage tftpboot.img vmlinux.aout: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
archclean:
@@ -65,11 +100,17 @@ archclean:
# This is the image used for packaging
KBUILD_IMAGE := $(boot)/zImage
-CLEAN_FILES += arch/$(ARCH)/boot/System.map
-
# Don't use tabs in echo arguments.
+ifeq ($(ARCH),sparc)
define archhelp
echo '* image - kernel image ($(boot)/image)'
echo '* zImage - stripped kernel image ($(boot)/zImage)'
echo ' tftpboot.img - image prepared for tftp'
endef
+else
+define archhelp
+ echo '* vmlinux - Standard sparc64 kernel'
+ echo ' vmlinux.aout - a.out kernel for sparc64'
+ echo ' tftpboot.img - image prepared for tftp'
+endef
+endif
diff --git a/arch/sparc/boot/.gitignore b/arch/sparc/boot/.gitignore
new file mode 100644
index 000000000000..fc6f3986c76c
--- /dev/null
+++ b/arch/sparc/boot/.gitignore
@@ -0,0 +1,8 @@
+btfix.S
+btfixupprep
+image
+zImage
+tftpboot.img
+vmlinux.aout
+piggyback
+
diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile
index 3e77a9f52248..96041a8d39e8 100644
--- a/arch/sparc/boot/Makefile
+++ b/arch/sparc/boot/Makefile
@@ -6,13 +6,16 @@
ROOT_IMG := /usr/src/root.img
ELFTOAOUT := elftoaout
-hostprogs-y := piggyback btfixupprep
-targets := tftpboot.img btfix.o btfix.S image
+hostprogs-y := piggyback_32 piggyback_64 btfixupprep
+targets := tftpboot.img btfix.o btfix.S image zImage vmlinux.aout
+clean-files := System.map
quiet_cmd_elftoaout = ELFTOAOUT $@
cmd_elftoaout = $(ELFTOAOUT) $(obj)/image -o $@
+
+ifeq ($(CONFIG_SPARC32),y)
quiet_cmd_piggy = PIGGY $@
- cmd_piggy = $(obj)/piggyback $@ $(obj)/System.map $(ROOT_IMG)
+ cmd_piggy = $(obj)/piggyback_32 $@ $(obj)/System.map $(ROOT_IMG)
quiet_cmd_btfix = BTFIX $@
cmd_btfix = $(OBJDUMP) -x vmlinux | $(obj)/btfixupprep > $@
quiet_cmd_sysmap = SYSMAP $(obj)/System.map
@@ -37,8 +40,8 @@ define rule_image
echo 'cmd_$@ := $(cmd_image)' > $(@D)/.$(@F).cmd
endef
-BTOBJS := $(HEAD_Y) $(INIT_Y)
-BTLIBS := $(CORE_Y) $(LIBS_Y) $(DRIVERS_Y) $(NET_Y)
+BTOBJS := $(patsubst %/, %/built-in.o, $(VMLINUX_INIT))
+BTLIBS := $(patsubst %/, %/built-in.o, $(VMLINUX_MAIN))
LDFLAGS_image := -T arch/sparc/kernel/vmlinux.lds $(BTOBJS) \
--start-group $(BTLIBS) --end-group \
$(kallsyms.o) $(obj)/btfix.o
@@ -61,3 +64,28 @@ $(obj)/tftpboot.img: $(obj)/piggyback $(obj)/System.map $(obj)/image FORCE
$(obj)/btfix.S: $(obj)/btfixupprep vmlinux FORCE
$(call if_changed,btfix)
+
+endif
+
+ifeq ($(CONFIG_SPARC64),y)
+quiet_cmd_piggy = PIGGY $@
+ cmd_piggy = $(obj)/piggyback_64 $@ System.map $(ROOT_IMG)
+quiet_cmd_strip = STRIP $@
+ cmd_strip = $(STRIP) -R .comment -R .note -K sun4u_init -K _end -K _start vmlinux -o $@
+
+
+# Actual linking
+$(obj)/image: vmlinux FORCE
+ $(call if_changed,strip)
+ @echo ' kernel: $@ is ready'
+
+$(obj)/tftpboot.img: vmlinux $(obj)/piggyback_64 System.map $(ROOT_IMG) FORCE
+ $(call if_changed,elftoaout)
+ $(call if_changed,piggy)
+ @echo ' kernel: $@ is ready'
+
+$(obj)/vmlinux.aout: vmlinux FORCE
+ $(call if_changed,elftoaout)
+ @echo ' kernel: $@ is ready'
+endif
+
diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback_32.c
index c9f500c1a8b2..c9f500c1a8b2 100644
--- a/arch/sparc/boot/piggyback.c
+++ b/arch/sparc/boot/piggyback_32.c
diff --git a/arch/sparc64/boot/piggyback.c b/arch/sparc/boot/piggyback_64.c
index de364bfed0bb..de364bfed0bb 100644
--- a/arch/sparc64/boot/piggyback.c
+++ b/arch/sparc/boot/piggyback_64.c
diff --git a/arch/sparc/defconfig b/arch/sparc/configs/sparc32_defconfig
index 2e3a149ea0e7..2e3a149ea0e7 100644
--- a/arch/sparc/defconfig
+++ b/arch/sparc/configs/sparc32_defconfig
diff --git a/arch/sparc64/defconfig b/arch/sparc/configs/sparc64_defconfig
index 05d19a3e590f..05d19a3e590f 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc/configs/sparc64_defconfig
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index 2d2769d766ec..89c260aab45c 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -15,8 +15,6 @@ header-y += signal_32.h
header-y += signal_64.h
header-y += stat_32.h
header-y += stat_64.h
-header-y += unistd_32.h
-header-y += unistd_64.h
header-y += apc.h
header-y += asi.h
diff --git a/arch/sparc/include/asm/asm.h b/arch/sparc/include/asm/asm.h
new file mode 100644
index 000000000000..e8e1d94b4cc9
--- /dev/null
+++ b/arch/sparc/include/asm/asm.h
@@ -0,0 +1,40 @@
+#ifndef _SPARC_ASM_H
+#define _SPARC_ASM_H
+
+/* Macros to assist the sharing of assembler code between 32-bit and
+ * 64-bit sparc.
+ */
+
+#ifdef CONFIG_SPARC64
+#define BRANCH32(TYPE, PREDICT, DEST) \
+ TYPE,PREDICT %icc, DEST
+#define BRANCH32_ANNUL(TYPE, PREDICT, DEST) \
+ TYPE,a,PREDICT %icc, DEST
+#define BRANCH_REG_ZERO(PREDICT, REG, DEST) \
+ brz,PREDICT REG, DEST
+#define BRANCH_REG_ZERO_ANNUL(PREDICT, REG, DEST) \
+ brz,a,PREDICT REG, DEST
+#define BRANCH_REG_NOT_ZERO(PREDICT, REG, DEST) \
+ brnz,PREDICT REG, DEST
+#define BRANCH_REG_NOT_ZERO_ANNUL(PREDICT, REG, DEST) \
+ brnz,a,PREDICT REG, DEST
+#else
+#define BRANCH32(TYPE, PREDICT, DEST) \
+ TYPE DEST
+#define BRANCH32_ANNUL(TYPE, PREDICT, DEST) \
+ TYPE,a DEST
+#define BRANCH_REG_ZERO(PREDICT, REG, DEST) \
+ cmp REG, 0; \
+ be DEST
+#define BRANCH_REG_ZERO_ANNUL(PREDICT, REG, DEST) \
+ cmp REG, 0; \
+ be,a DEST
+#define BRANCH_REG_NOT_ZERO(PREDICT, REG, DEST) \
+ cmp REG, 0; \
+ bne DEST
+#define BRANCH_REG_NOT_ZERO_ANNUL(PREDICT, REG, DEST) \
+ cmp REG, 0; \
+ bne,a DEST
+#endif
+
+#endif /* _SPARC_ASM_H */
diff --git a/arch/sparc/include/asm/atomic_64.h b/arch/sparc/include/asm/atomic_64.h
index 2c71ec4a3b18..5982c5ae7f07 100644
--- a/arch/sparc/include/asm/atomic_64.h
+++ b/arch/sparc/include/asm/atomic_64.h
@@ -112,17 +112,10 @@ static inline int atomic64_add_unless(atomic64_t *v, long a, long u)
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
/* Atomic operations are already serializing */
-#ifdef CONFIG_SMP
-#define smp_mb__before_atomic_dec() membar_storeload_loadload();
-#define smp_mb__after_atomic_dec() membar_storeload_storestore();
-#define smp_mb__before_atomic_inc() membar_storeload_loadload();
-#define smp_mb__after_atomic_inc() membar_storeload_storestore();
-#else
#define smp_mb__before_atomic_dec() barrier()
#define smp_mb__after_atomic_dec() barrier()
#define smp_mb__before_atomic_inc() barrier()
#define smp_mb__after_atomic_inc() barrier()
-#endif
#include <asm-generic/atomic.h>
#endif /* !(__ARCH_SPARC64_ATOMIC__) */
diff --git a/arch/sparc/include/asm/bitops_64.h b/arch/sparc/include/asm/bitops_64.h
index bb87b8080220..e72ac9cdfb98 100644
--- a/arch/sparc/include/asm/bitops_64.h
+++ b/arch/sparc/include/asm/bitops_64.h
@@ -23,13 +23,8 @@ extern void change_bit(unsigned long nr, volatile unsigned long *addr);
#include <asm-generic/bitops/non-atomic.h>
-#ifdef CONFIG_SMP
-#define smp_mb__before_clear_bit() membar_storeload_loadload()
-#define smp_mb__after_clear_bit() membar_storeload_storestore()
-#else
#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
-#endif
#include <asm-generic/bitops/ffz.h>
#include <asm-generic/bitops/__ffs.h>
diff --git a/arch/sparc/include/asm/device.h b/arch/sparc/include/asm/device.h
index 19790eb99cc6..3702e087df2c 100644
--- a/arch/sparc/include/asm/device.h
+++ b/arch/sparc/include/asm/device.h
@@ -20,4 +20,16 @@ struct dev_archdata {
int numa_node;
};
+static inline void dev_archdata_set_node(struct dev_archdata *ad,
+ struct device_node *np)
+{
+ ad->prom_node = np;
+}
+
+static inline struct device_node *
+dev_archdata_get_node(const struct dev_archdata *ad)
+{
+ return ad->prom_node;
+}
+
#endif /* _ASM_SPARC_DEVICE_H */
diff --git a/arch/sparc/include/asm/hypervisor.h b/arch/sparc/include/asm/hypervisor.h
index 109ae24ba242..bafe5a631b6d 100644
--- a/arch/sparc/include/asm/hypervisor.h
+++ b/arch/sparc/include/asm/hypervisor.h
@@ -2713,6 +2713,30 @@ extern unsigned long sun4v_ldc_revoke(unsigned long channel,
*/
#define HV_FAST_SET_PERFREG 0x101
+#define HV_N2_PERF_SPARC_CTL 0x0
+#define HV_N2_PERF_DRAM_CTL0 0x1
+#define HV_N2_PERF_DRAM_CNT0 0x2
+#define HV_N2_PERF_DRAM_CTL1 0x3
+#define HV_N2_PERF_DRAM_CNT1 0x4
+#define HV_N2_PERF_DRAM_CTL2 0x5
+#define HV_N2_PERF_DRAM_CNT2 0x6
+#define HV_N2_PERF_DRAM_CTL3 0x7
+#define HV_N2_PERF_DRAM_CNT3 0x8
+
+#define HV_FAST_N2_GET_PERFREG 0x104
+#define HV_FAST_N2_SET_PERFREG 0x105
+
+#ifndef __ASSEMBLY__
+extern unsigned long sun4v_niagara_getperf(unsigned long reg,
+ unsigned long *val);
+extern unsigned long sun4v_niagara_setperf(unsigned long reg,
+ unsigned long val);
+extern unsigned long sun4v_niagara2_getperf(unsigned long reg,
+ unsigned long *val);
+extern unsigned long sun4v_niagara2_setperf(unsigned long reg,
+ unsigned long val);
+#endif
+
/* MMU statistics services.
*
* The hypervisor maintains MMU statistics and privileged code provides
diff --git a/arch/sparc/include/asm/irq_32.h b/arch/sparc/include/asm/irq_32.h
index fe205cc444b8..ea43057d4763 100644
--- a/arch/sparc/include/asm/irq_32.h
+++ b/arch/sparc/include/asm/irq_32.h
@@ -12,4 +12,5 @@
#define irq_canonicalize(irq) (irq)
+extern void __init init_IRQ(void);
#endif
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h
index 71673eca3660..d47d4a1955a9 100644
--- a/arch/sparc/include/asm/irq_64.h
+++ b/arch/sparc/include/asm/irq_64.h
@@ -66,6 +66,9 @@ extern void virt_irq_free(unsigned int virt_irq);
extern void __init init_IRQ(void);
extern void fixup_irqs(void);
+extern int register_perfctr_intr(void (*handler)(struct pt_regs *));
+extern void release_perfctr_intr(void (*handler)(struct pt_regs *));
+
static inline void set_softint(unsigned long bits)
{
__asm__ __volatile__("wr %0, 0x0, %%set_softint"
diff --git a/arch/sparc/include/asm/irqflags_64.h b/arch/sparc/include/asm/irqflags_64.h
index bb42e59162aa..8b49bf920df3 100644
--- a/arch/sparc/include/asm/irqflags_64.h
+++ b/arch/sparc/include/asm/irqflags_64.h
@@ -10,6 +10,8 @@
#ifndef _ASM_IRQFLAGS_H
#define _ASM_IRQFLAGS_H
+#include <asm/pil.h>
+
#ifndef __ASSEMBLY__
static inline unsigned long __raw_local_save_flags(void)
@@ -40,9 +42,9 @@ static inline void raw_local_irq_restore(unsigned long flags)
static inline void raw_local_irq_disable(void)
{
__asm__ __volatile__(
- "wrpr 15, %%pil"
+ "wrpr %0, %%pil"
: /* no outputs */
- : /* no inputs */
+ : "i" (PIL_NORMAL_MAX)
: "memory"
);
}
diff --git a/arch/sparc/include/asm/module.h b/arch/sparc/include/asm/module.h
index e82cf9a3e60e..ff8e02d80334 100644
--- a/arch/sparc/include/asm/module.h
+++ b/arch/sparc/include/asm/module.h
@@ -1,8 +1,24 @@
-#ifndef ___ASM_SPARC_MODULE_H
-#define ___ASM_SPARC_MODULE_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/module_64.h>
-#else
-#include <asm/module_32.h>
-#endif
-#endif
+#ifndef __SPARC_MODULE_H
+#define __SPARC_MODULE_H
+struct mod_arch_specific { };
+
+/*
+ * Use some preprocessor magic to define the correct symbol
+ * for sparc32 and sparc64.
+ * Elf_Addr becomes Elf32_Addr for sparc32 and Elf64_Addr for sparc64
+ */
+#define ___ELF(a, b, c) a##b##c
+#define __ELF(a, b, c) ___ELF(a, b, c)
+#define _Elf(t) __ELF(Elf, CONFIG_BITS, t)
+#define _ELF(t) __ELF(ELF, CONFIG_BITS, t)
+
+#define Elf_Shdr _Elf(_Shdr)
+#define Elf_Sym _Elf(_Sym)
+#define Elf_Ehdr _Elf(_Ehdr)
+#define Elf_Rela _Elf(_Rela)
+#define Elf_Addr _Elf(_Addr)
+
+#define ELF_R_SYM _ELF(_R_SYM)
+#define ELF_R_TYPE _ELF(_R_TYPE)
+
+#endif /* __SPARC_MODULE_H */
diff --git a/arch/sparc/include/asm/module_32.h b/arch/sparc/include/asm/module_32.h
deleted file mode 100644
index cbd9e67b0c0b..000000000000
--- a/arch/sparc/include/asm/module_32.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _ASM_SPARC_MODULE_H
-#define _ASM_SPARC_MODULE_H
-struct mod_arch_specific { };
-#define Elf_Shdr Elf32_Shdr
-#define Elf_Sym Elf32_Sym
-#define Elf_Ehdr Elf32_Ehdr
-#endif /* _ASM_SPARC_MODULE_H */
diff --git a/arch/sparc/include/asm/module_64.h b/arch/sparc/include/asm/module_64.h
deleted file mode 100644
index 3d77ba465783..000000000000
--- a/arch/sparc/include/asm/module_64.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _ASM_SPARC64_MODULE_H
-#define _ASM_SPARC64_MODULE_H
-struct mod_arch_specific { };
-#define Elf_Shdr Elf64_Shdr
-#define Elf_Sym Elf64_Sym
-#define Elf_Ehdr Elf64_Ehdr
-#endif /* _ASM_SPARC64_MODULE_H */
diff --git a/arch/sparc/include/asm/openprom_32.h b/arch/sparc/include/asm/openprom_32.h
index 8b1649f29ed9..875da3552d80 100644
--- a/arch/sparc/include/asm/openprom_32.h
+++ b/arch/sparc/include/asm/openprom_32.h
@@ -170,9 +170,9 @@ struct linux_romvec {
struct linux_nodeops {
int (*no_nextnode)(int node);
int (*no_child)(int node);
- int (*no_proplen)(int node, char *name);
- int (*no_getprop)(int node, char *name, char *val);
- int (*no_setprop)(int node, char *name, char *val, int len);
+ int (*no_proplen)(int node, const char *name);
+ int (*no_getprop)(int node, const char *name, char *val);
+ int (*no_setprop)(int node, const char *name, char *val, int len);
char * (*no_nextprop)(int node, char *name);
};
diff --git a/arch/sparc/include/asm/oplib_32.h b/arch/sparc/include/asm/oplib_32.h
index 699da05235c8..73d45521db04 100644
--- a/arch/sparc/include/asm/oplib_32.h
+++ b/arch/sparc/include/asm/oplib_32.h
@@ -136,7 +136,7 @@ extern char prom_getchar(void);
extern void prom_putchar(char character);
/* Prom's internal routines, don't use in kernel/boot code. */
-extern void prom_printf(char *fmt, ...);
+extern void prom_printf(const char *fmt, ...);
extern void prom_write(const char *buf, unsigned int len);
/* Multiprocessor operations... */
@@ -199,12 +199,12 @@ extern int prom_getsibling(int node);
/* Get the length, at the passed node, of the given property type.
* Returns -1 on error (ie. no such property at this node).
*/
-extern int prom_getproplen(int thisnode, char *property);
+extern int prom_getproplen(int thisnode, const char *property);
/* Fetch the requested property using the given buffer. Returns
* the number of bytes the prom put into your buffer or -1 on error.
*/
-extern int __must_check prom_getproperty(int thisnode, char *property,
+extern int __must_check prom_getproperty(int thisnode, const char *property,
char *prop_buffer, int propbuf_size);
/* Acquire an integer property. */
@@ -246,7 +246,7 @@ extern int prom_node_has_property(int node, char *property);
/* Set the indicated property at the given node with the passed value.
* Returns the number of bytes of your value that the prom took.
*/
-extern int prom_setprop(int node, char *prop_name, char *prop_value,
+extern int prom_setprop(int node, const char *prop_name, char *prop_value,
int value_size);
extern int prom_pathtoinode(char *path);
diff --git a/arch/sparc/include/asm/pil.h b/arch/sparc/include/asm/pil.h
index 71819bb943fc..d573820c0ff4 100644
--- a/arch/sparc/include/asm/pil.h
+++ b/arch/sparc/include/asm/pil.h
@@ -10,7 +10,12 @@
*
* In fact any XCALL which has to etrap/rtrap has a problem because
* it is difficult to prevent rtrap from running BH's, and that would
- * need to be done if the XCALL arrived while %pil==15.
+ * need to be done if the XCALL arrived while %pil==PIL_NORMAL_MAX.
+ *
+ * Finally, in order to handle profiling events even when a
+ * local_irq_disable() is in progress, we only disable up to level 14
+ * interrupts. Profile counter overflow interrupts arrive at level
+ * 15.
*/
#define PIL_SMP_CALL_FUNC 1
#define PIL_SMP_RECEIVE_SIGNAL 2
@@ -18,5 +23,7 @@
#define PIL_SMP_CTX_NEW_VERSION 4
#define PIL_DEVICE_IRQ 5
#define PIL_SMP_CALL_FUNC_SNGL 6
+#define PIL_NORMAL_MAX 14
+#define PIL_NMI 15
#endif /* !(_SPARC64_PIL_H) */
diff --git a/arch/sparc/include/asm/ptrace_32.h b/arch/sparc/include/asm/ptrace_32.h
index d409c4f21a5c..4cef450167dd 100644
--- a/arch/sparc/include/asm/ptrace_32.h
+++ b/arch/sparc/include/asm/ptrace_32.h
@@ -62,6 +62,8 @@ struct sparc_stackf {
#ifdef __KERNEL__
+#include <asm/system.h>
+
static inline bool pt_regs_is_syscall(struct pt_regs *regs)
{
return (regs->psr & PSR_SYSCALL);
@@ -72,6 +74,14 @@ static inline bool pt_regs_clear_syscall(struct pt_regs *regs)
return (regs->psr &= ~PSR_SYSCALL);
}
+#define arch_ptrace_stop_needed(exit_code, info) \
+({ flush_user_windows(); \
+ current_thread_info()->w_saved != 0; \
+})
+
+#define arch_ptrace_stop(exit_code, info) \
+ synchronize_user_stack()
+
#define user_mode(regs) (!((regs)->psr & PSR_PS))
#define instruction_pointer(regs) ((regs)->pc)
#define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP])
diff --git a/arch/sparc/include/asm/ptrace_64.h b/arch/sparc/include/asm/ptrace_64.h
index 84e969f06afe..cd6fbfc20435 100644
--- a/arch/sparc/include/asm/ptrace_64.h
+++ b/arch/sparc/include/asm/ptrace_64.h
@@ -114,6 +114,7 @@ struct sparc_trapf {
#ifdef __KERNEL__
#include <linux/threads.h>
+#include <asm/system.h>
static inline int pt_regs_trap_type(struct pt_regs *regs)
{
@@ -130,6 +131,14 @@ static inline bool pt_regs_clear_syscall(struct pt_regs *regs)
return (regs->tstate &= ~TSTATE_SYSCALL);
}
+#define arch_ptrace_stop_needed(exit_code, info) \
+({ flush_user_windows(); \
+ get_thread_wsaved() != 0; \
+})
+
+#define arch_ptrace_stop(exit_code, info) \
+ synchronize_user_stack()
+
struct global_reg_snapshot {
unsigned long tstate;
unsigned long tpc;
diff --git a/arch/sparc/include/asm/scatterlist.h b/arch/sparc/include/asm/scatterlist.h
index ec21a4517641..e580f5581c88 100644
--- a/arch/sparc/include/asm/scatterlist.h
+++ b/arch/sparc/include/asm/scatterlist.h
@@ -1,8 +1,27 @@
-#ifndef ___ASM_SPARC_SCATTERLIST_H
-#define ___ASM_SPARC_SCATTERLIST_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/scatterlist_64.h>
-#else
-#include <asm/scatterlist_32.h>
-#endif
+#ifndef _SPARC_SCATTERLIST_H
+#define _SPARC_SCATTERLIST_H
+
+#include <asm/page.h>
+#include <asm/types.h>
+
+struct scatterlist {
+#ifdef CONFIG_DEBUG_SG
+ unsigned long sg_magic;
#endif
+ unsigned long page_link;
+ unsigned int offset;
+
+ unsigned int length;
+
+ dma_addr_t dma_address;
+ __u32 dma_length;
+};
+
+#define sg_dma_address(sg) ((sg)->dma_address)
+#define sg_dma_len(sg) ((sg)->dma_length)
+
+#define ISA_DMA_THRESHOLD (~0UL)
+
+#define ARCH_HAS_SG_CHAIN
+
+#endif /* !(_SPARC_SCATTERLIST_H) */
diff --git a/arch/sparc/include/asm/scatterlist_32.h b/arch/sparc/include/asm/scatterlist_32.h
deleted file mode 100644
index c82609ca1d0f..000000000000
--- a/arch/sparc/include/asm/scatterlist_32.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef _SPARC_SCATTERLIST_H
-#define _SPARC_SCATTERLIST_H
-
-#include <linux/types.h>
-
-struct scatterlist {
-#ifdef CONFIG_DEBUG_SG
- unsigned long sg_magic;
-#endif
- unsigned long page_link;
- unsigned int offset;
-
- unsigned int length;
-
- __u32 dvma_address; /* A place to hang host-specific addresses at. */
- __u32 dvma_length;
-};
-
-#define sg_dma_address(sg) ((sg)->dvma_address)
-#define sg_dma_len(sg) ((sg)->dvma_length)
-
-#define ISA_DMA_THRESHOLD (~0UL)
-
-#define ARCH_HAS_SG_CHAIN
-
-#endif /* !(_SPARC_SCATTERLIST_H) */
diff --git a/arch/sparc/include/asm/scatterlist_64.h b/arch/sparc/include/asm/scatterlist_64.h
deleted file mode 100644
index 81bd058f9382..000000000000
--- a/arch/sparc/include/asm/scatterlist_64.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef _SPARC64_SCATTERLIST_H
-#define _SPARC64_SCATTERLIST_H
-
-#include <asm/page.h>
-#include <asm/types.h>
-
-struct scatterlist {
-#ifdef CONFIG_DEBUG_SG
- unsigned long sg_magic;
-#endif
- unsigned long page_link;
- unsigned int offset;
-
- unsigned int length;
-
- dma_addr_t dma_address;
- __u32 dma_length;
-};
-
-#define sg_dma_address(sg) ((sg)->dma_address)
-#define sg_dma_len(sg) ((sg)->dma_length)
-
-#define ISA_DMA_THRESHOLD (~0UL)
-
-#define ARCH_HAS_SG_CHAIN
-
-#endif /* !(_SPARC64_SCATTERLIST_H) */
diff --git a/arch/sparc/include/asm/sections.h b/arch/sparc/include/asm/sections.h
index c7c69b00967f..0b0553bbd8a0 100644
--- a/arch/sparc/include/asm/sections.h
+++ b/arch/sparc/include/asm/sections.h
@@ -1,8 +1,10 @@
-#ifndef ___ASM_SPARC_SECTIONS_H
-#define ___ASM_SPARC_SECTIONS_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/sections_64.h>
-#else
-#include <asm/sections_32.h>
-#endif
+#ifndef __SPARC_SECTIONS_H
+#define __SPARC_SECTIONS_H
+
+/* nothing to see, move along */
+#include <asm-generic/sections.h>
+
+/* sparc entry point */
+extern char _start[];
+
#endif
diff --git a/arch/sparc/include/asm/sections_32.h b/arch/sparc/include/asm/sections_32.h
deleted file mode 100644
index 6832841df051..000000000000
--- a/arch/sparc/include/asm/sections_32.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC_SECTIONS_H
-#define _SPARC_SECTIONS_H
-
-#include <asm-generic/sections.h>
-
-#endif
diff --git a/arch/sparc/include/asm/sections_64.h b/arch/sparc/include/asm/sections_64.h
deleted file mode 100644
index 3f4b9fdc28d0..000000000000
--- a/arch/sparc/include/asm/sections_64.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _SPARC64_SECTIONS_H
-#define _SPARC64_SECTIONS_H
-
-/* nothing to see, move along */
-#include <asm-generic/sections.h>
-
-extern char _start[];
-
-#endif
diff --git a/arch/sparc/include/asm/spinlock_64.h b/arch/sparc/include/asm/spinlock_64.h
index 120cfe4577c7..c4d274d330e9 100644
--- a/arch/sparc/include/asm/spinlock_64.h
+++ b/arch/sparc/include/asm/spinlock_64.h
@@ -13,17 +13,12 @@
* and rebuild your kernel.
*/
-/* All of these locking primitives are expected to work properly
- * even in an RMO memory model, which currently is what the kernel
- * runs in.
- *
- * There is another issue. Because we play games to save cycles
- * in the non-contention case, we need to be extra careful about
- * branch targets into the "spinning" code. They live in their
- * own section, but the newer V9 branches have a shorter range
- * than the traditional 32-bit sparc branch variants. The rule
- * is that the branches that go into and out of the spinner sections
- * must be pre-V9 branches.
+/* Because we play games to save cycles in the non-contention case, we
+ * need to be extra careful about branch targets into the "spinning"
+ * code. They live in their own section, but the newer V9 branches
+ * have a shorter range than the traditional 32-bit sparc branch
+ * variants. The rule is that the branches that go into and out of
+ * the spinner sections must be pre-V9 branches.
*/
#define __raw_spin_is_locked(lp) ((lp)->lock != 0)
@@ -38,12 +33,10 @@ static inline void __raw_spin_lock(raw_spinlock_t *lock)
__asm__ __volatile__(
"1: ldstub [%1], %0\n"
-" membar #StoreLoad | #StoreStore\n"
" brnz,pn %0, 2f\n"
" nop\n"
" .subsection 2\n"
"2: ldub [%1], %0\n"
-" membar #LoadLoad\n"
" brnz,pt %0, 2b\n"
" nop\n"
" ba,a,pt %%xcc, 1b\n"
@@ -59,7 +52,6 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock)
__asm__ __volatile__(
" ldstub [%1], %0\n"
-" membar #StoreLoad | #StoreStore"
: "=r" (result)
: "r" (lock)
: "memory");
@@ -70,7 +62,6 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock)
static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
__asm__ __volatile__(
-" membar #StoreStore | #LoadStore\n"
" stb %%g0, [%0]"
: /* No outputs */
: "r" (lock)
@@ -83,14 +74,12 @@ static inline void __raw_spin_lock_flags(raw_spinlock_t *lock, unsigned long fla
__asm__ __volatile__(
"1: ldstub [%2], %0\n"
-" membar #StoreLoad | #StoreStore\n"
" brnz,pn %0, 2f\n"
" nop\n"
" .subsection 2\n"
"2: rdpr %%pil, %1\n"
" wrpr %3, %%pil\n"
"3: ldub [%2], %0\n"
-" membar #LoadLoad\n"
" brnz,pt %0, 3b\n"
" nop\n"
" ba,pt %%xcc, 1b\n"
@@ -113,12 +102,10 @@ static void inline __read_lock(raw_rwlock_t *lock)
"4: add %0, 1, %1\n"
" cas [%2], %0, %1\n"
" cmp %0, %1\n"
-" membar #StoreLoad | #StoreStore\n"
" bne,pn %%icc, 1b\n"
" nop\n"
" .subsection 2\n"
"2: ldsw [%2], %0\n"
-" membar #LoadLoad\n"
" brlz,pt %0, 2b\n"
" nop\n"
" ba,a,pt %%xcc, 4b\n"
@@ -139,7 +126,6 @@ static int inline __read_trylock(raw_rwlock_t *lock)
" add %0, 1, %1\n"
" cas [%2], %0, %1\n"
" cmp %0, %1\n"
-" membar #StoreLoad | #StoreStore\n"
" bne,pn %%icc, 1b\n"
" mov 1, %0\n"
"2:"
@@ -155,7 +141,6 @@ static void inline __read_unlock(raw_rwlock_t *lock)
unsigned long tmp1, tmp2;
__asm__ __volatile__(
-" membar #StoreLoad | #LoadLoad\n"
"1: lduw [%2], %0\n"
" sub %0, 1, %1\n"
" cas [%2], %0, %1\n"
@@ -179,12 +164,10 @@ static void inline __write_lock(raw_rwlock_t *lock)
"4: or %0, %3, %1\n"
" cas [%2], %0, %1\n"
" cmp %0, %1\n"
-" membar #StoreLoad | #StoreStore\n"
" bne,pn %%icc, 1b\n"
" nop\n"
" .subsection 2\n"
"2: lduw [%2], %0\n"
-" membar #LoadLoad\n"
" brnz,pt %0, 2b\n"
" nop\n"
" ba,a,pt %%xcc, 4b\n"
@@ -197,7 +180,6 @@ static void inline __write_lock(raw_rwlock_t *lock)
static void inline __write_unlock(raw_rwlock_t *lock)
{
__asm__ __volatile__(
-" membar #LoadStore | #StoreStore\n"
" stw %%g0, [%0]"
: /* no outputs */
: "r" (lock)
@@ -217,7 +199,6 @@ static int inline __write_trylock(raw_rwlock_t *lock)
" or %0, %4, %1\n"
" cas [%3], %0, %1\n"
" cmp %0, %1\n"
-" membar #StoreLoad | #StoreStore\n"
" bne,pn %%icc, 1b\n"
" nop\n"
" mov 1, %2\n"
diff --git a/arch/sparc/include/asm/spitfire.h b/arch/sparc/include/asm/spitfire.h
index 985ea7e31992..f0d0c40c44da 100644
--- a/arch/sparc/include/asm/spitfire.h
+++ b/arch/sparc/include/asm/spitfire.h
@@ -6,6 +6,8 @@
#ifndef _SPARC64_SPITFIRE_H
#define _SPARC64_SPITFIRE_H
+#ifdef CONFIG_SPARC64
+
#include <asm/asi.h>
/* The following register addresses are accessible via ASI_DMMU
@@ -338,5 +340,5 @@ static inline void cheetah_put_itlb_data(int entry, unsigned long data)
}
#endif /* !(__ASSEMBLY__) */
-
+#endif /* CONFIG_SPARC64 */
#endif /* !(_SPARC64_SPITFIRE_H) */
diff --git a/arch/sparc/include/asm/system_32.h b/arch/sparc/include/asm/system_32.h
index 8623fc48fe24..79c1ae2b42a3 100644
--- a/arch/sparc/include/asm/system_32.h
+++ b/arch/sparc/include/asm/system_32.h
@@ -15,6 +15,11 @@
#include <linux/irqflags.h>
+static inline unsigned int probe_irq_mask(unsigned long val)
+{
+ return 0;
+}
+
/*
* Sparc (general) CPU types
*/
diff --git a/arch/sparc/include/asm/system_64.h b/arch/sparc/include/asm/system_64.h
index 8759f2a1b837..6c077816ab28 100644
--- a/arch/sparc/include/asm/system_64.h
+++ b/arch/sparc/include/asm/system_64.h
@@ -59,20 +59,9 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \
: : : "memory"); \
} while (0)
-#define mb() \
- membar_safe("#LoadLoad | #LoadStore | #StoreStore | #StoreLoad")
-#define rmb() \
- membar_safe("#LoadLoad")
-#define wmb() \
- membar_safe("#StoreStore")
-#define membar_storeload() \
- membar_safe("#StoreLoad")
-#define membar_storeload_storestore() \
- membar_safe("#StoreLoad | #StoreStore")
-#define membar_storeload_loadload() \
- membar_safe("#StoreLoad | #LoadLoad")
-#define membar_storestore_loadstore() \
- membar_safe("#StoreStore | #LoadStore")
+#define mb() membar_safe("#StoreLoad")
+#define rmb() __asm__ __volatile__("":::"memory")
+#define wmb() __asm__ __volatile__("":::"memory")
#endif
@@ -80,20 +69,20 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \
#define read_barrier_depends() do { } while(0)
#define set_mb(__var, __value) \
- do { __var = __value; membar_storeload_storestore(); } while(0)
+ do { __var = __value; membar_safe("#StoreLoad"); } while(0)
#ifdef CONFIG_SMP
#define smp_mb() mb()
#define smp_rmb() rmb()
#define smp_wmb() wmb()
-#define smp_read_barrier_depends() read_barrier_depends()
#else
#define smp_mb() __asm__ __volatile__("":::"memory")
#define smp_rmb() __asm__ __volatile__("":::"memory")
#define smp_wmb() __asm__ __volatile__("":::"memory")
-#define smp_read_barrier_depends() do { } while(0)
#endif
+#define smp_read_barrier_depends() do { } while(0)
+
#define flushi(addr) __asm__ __volatile__ ("flush %0" : : "r" (addr) : "memory")
#define flushw_all() __asm__ __volatile__("flushw")
@@ -107,11 +96,12 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \
* arch/sparc64/kernel/smp.c:smp_percpu_timer_interrupt()
* for more information.
*/
-#define reset_pic() \
- __asm__ __volatile__("ba,pt %xcc, 99f\n\t" \
+#define write_pic(__p) \
+ __asm__ __volatile__("ba,pt %%xcc, 99f\n\t" \
".align 64\n" \
- "99:wr %g0, 0x0, %pic\n\t" \
- "rd %pic, %g0")
+ "99:wr %0, 0x0, %%pic\n\t" \
+ "rd %%pic, %%g0" : : "r" (__p))
+#define reset_pic() write_pic(0)
#ifndef __ASSEMBLY__
@@ -170,6 +160,7 @@ do { if (test_thread_flag(TIF_PERFCTR)) { \
"stb %%o5, [%%g6 + %5]\n\t" \
"rdpr %%cwp, %%o5\n\t" \
"stb %%o5, [%%g6 + %8]\n\t" \
+ "wrpr %%g0, 15, %%pil\n\t" \
"mov %4, %%g6\n\t" \
"ldub [%4 + %8], %%g1\n\t" \
"wrpr %%g1, %%cwp\n\t" \
@@ -180,6 +171,7 @@ do { if (test_thread_flag(TIF_PERFCTR)) { \
"ldx [%%sp + 2047 + 0x70], %%i6\n\t" \
"ldx [%%sp + 2047 + 0x78], %%i7\n\t" \
"ldx [%%g6 + %9], %%g4\n\t" \
+ "wrpr %%g0, 14, %%pil\n\t" \
"brz,pt %%o7, switch_to_pc\n\t" \
" mov %%g7, %0\n\t" \
"sethi %%hi(ret_from_syscall), %%g1\n\t" \
@@ -209,14 +201,12 @@ static inline unsigned long xchg32(__volatile__ unsigned int *m, unsigned int va
unsigned long tmp1, tmp2;
__asm__ __volatile__(
-" membar #StoreLoad | #LoadLoad\n"
" mov %0, %1\n"
"1: lduw [%4], %2\n"
" cas [%4], %2, %0\n"
" cmp %2, %0\n"
" bne,a,pn %%icc, 1b\n"
" mov %1, %0\n"
-" membar #StoreLoad | #StoreStore\n"
: "=&r" (val), "=&r" (tmp1), "=&r" (tmp2)
: "0" (val), "r" (m)
: "cc", "memory");
@@ -228,14 +218,12 @@ static inline unsigned long xchg64(__volatile__ unsigned long *m, unsigned long
unsigned long tmp1, tmp2;
__asm__ __volatile__(
-" membar #StoreLoad | #LoadLoad\n"
" mov %0, %1\n"
"1: ldx [%4], %2\n"
" casx [%4], %2, %0\n"
" cmp %2, %0\n"
" bne,a,pn %%xcc, 1b\n"
" mov %1, %0\n"
-" membar #StoreLoad | #StoreStore\n"
: "=&r" (val), "=&r" (tmp1), "=&r" (tmp2)
: "0" (val), "r" (m)
: "cc", "memory");
@@ -272,9 +260,7 @@ extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noret
static inline unsigned long
__cmpxchg_u32(volatile int *m, int old, int new)
{
- __asm__ __volatile__("membar #StoreLoad | #LoadLoad\n"
- "cas [%2], %3, %0\n\t"
- "membar #StoreLoad | #StoreStore"
+ __asm__ __volatile__("cas [%2], %3, %0"
: "=&r" (new)
: "0" (new), "r" (m), "r" (old)
: "memory");
@@ -285,9 +271,7 @@ __cmpxchg_u32(volatile int *m, int old, int new)
static inline unsigned long
__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
{
- __asm__ __volatile__("membar #StoreLoad | #LoadLoad\n"
- "casx [%2], %3, %0\n\t"
- "membar #StoreLoad | #StoreStore"
+ __asm__ __volatile__("casx [%2], %3, %0"
: "=&r" (new)
: "0" (new), "r" (m), "r" (old)
: "memory");
diff --git a/arch/sparc/include/asm/topology_64.h b/arch/sparc/include/asm/topology_64.h
index 001c04027c82..b8a65b64e1df 100644
--- a/arch/sparc/include/asm/topology_64.h
+++ b/arch/sparc/include/asm/topology_64.h
@@ -16,8 +16,12 @@ static inline cpumask_t node_to_cpumask(int node)
{
return numa_cpumask_lookup_table[node];
}
+#define cpumask_of_node(node) (&numa_cpumask_lookup_table[node])
-/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
+/*
+ * Returns a pointer to the cpumask of CPUs on Node 'node'.
+ * Deprecated: use "const struct cpumask *mask = cpumask_of_node(node)"
+ */
#define node_to_cpumask_ptr(v, node) \
cpumask_t *v = &(numa_cpumask_lookup_table[node])
@@ -26,9 +30,7 @@ static inline cpumask_t node_to_cpumask(int node)
static inline int node_to_first_cpu(int node)
{
- cpumask_t tmp;
- tmp = node_to_cpumask(node);
- return first_cpu(tmp);
+ return cpumask_first(cpumask_of_node(node));
}
struct pci_bus;
@@ -77,10 +79,13 @@ static inline int pcibus_to_node(struct pci_bus *pbus)
#define topology_core_id(cpu) (cpu_data(cpu).core_id)
#define topology_core_siblings(cpu) (cpu_core_map[cpu])
#define topology_thread_siblings(cpu) (per_cpu(cpu_sibling_map, cpu))
+#define topology_core_cpumask(cpu) (&cpu_core_map[cpu])
+#define topology_thread_cpumask(cpu) (&per_cpu(cpu_sibling_map, cpu))
#define mc_capable() (sparc64_multi_core)
#define smt_capable() (sparc64_multi_core)
#endif /* CONFIG_SMP */
#define cpu_coregroup_map(cpu) (cpu_core_map[cpu])
+#define cpu_coregroup_mask(cpu) (&cpu_core_map[cpu])
#endif /* _ASM_SPARC64_TOPOLOGY_H */
diff --git a/arch/sparc/include/asm/tsb.h b/arch/sparc/include/asm/tsb.h
index 76e4299dd9bc..83c571d8c8a7 100644
--- a/arch/sparc/include/asm/tsb.h
+++ b/arch/sparc/include/asm/tsb.h
@@ -50,8 +50,6 @@
#define TSB_TAG_INVALID_BIT 46
#define TSB_TAG_INVALID_HIGH (1 << (TSB_TAG_INVALID_BIT - 32))
-#define TSB_MEMBAR membar #StoreStore
-
/* Some cpus support physical address quad loads. We want to use
* those if possible so we don't need to hard-lock the TSB mapping
* into the TLB. We encode some instruction patching in order to
@@ -128,13 +126,11 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
cmp REG1, REG2; \
bne,pn %icc, 99b; \
nop; \
- TSB_MEMBAR
#define TSB_WRITE(TSB, TTE, TAG) \
add TSB, 0x8, TSB; \
TSB_STORE(TSB, TTE); \
sub TSB, 0x8, TSB; \
- TSB_MEMBAR; \
TSB_STORE(TSB, TAG);
#define KTSB_LOAD_QUAD(TSB, REG) \
@@ -153,13 +149,11 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
cmp REG1, REG2; \
bne,pn %icc, 99b; \
nop; \
- TSB_MEMBAR
#define KTSB_WRITE(TSB, TTE, TAG) \
add TSB, 0x8, TSB; \
stxa TTE, [TSB] ASI_N; \
sub TSB, 0x8, TSB; \
- TSB_MEMBAR; \
stxa TAG, [TSB] ASI_N;
/* Do a kernel page table walk. Leaves physical PTE pointer in
diff --git a/arch/sparc/include/asm/ttable.h b/arch/sparc/include/asm/ttable.h
index 5708ba2719fb..48f2807d3265 100644
--- a/arch/sparc/include/asm/ttable.h
+++ b/arch/sparc/include/asm/ttable.h
@@ -2,6 +2,7 @@
#define _SPARC64_TTABLE_H
#include <asm/utrap.h>
+#include <asm/pil.h>
#ifdef __ASSEMBLY__
#include <asm/thread_info.h>
@@ -123,7 +124,7 @@
#define TRAP_IRQ(routine, level) \
rdpr %pil, %g2; \
- wrpr %g0, 15, %pil; \
+ wrpr %g0, PIL_NORMAL_MAX, %pil; \
sethi %hi(1f-4), %g7; \
ba,pt %xcc, etrap_irq; \
or %g7, %lo(1f-4), %g7; \
@@ -143,7 +144,7 @@
#define TRAP_IRQ(routine, level) \
rdpr %pil, %g2; \
- wrpr %g0, 15, %pil; \
+ wrpr %g0, PIL_NORMAL_MAX, %pil; \
ba,pt %xcc, etrap_irq; \
rd %pc, %g7; \
mov level, %o0; \
@@ -153,6 +154,16 @@
#endif
+#define TRAP_NMI_IRQ(routine, level) \
+ rdpr %pil, %g2; \
+ wrpr %g0, PIL_NMI, %pil; \
+ ba,pt %xcc, etrap_irq; \
+ rd %pc, %g7; \
+ mov level, %o0; \
+ call routine; \
+ add %sp, PTREGS_OFF, %o1; \
+ ba,a,pt %xcc, rtrap_nmi;
+
#define TRAP_IVEC TRAP_NOSAVE(do_ivec)
#define BTRAP(lvl) TRAP_ARG(bad_trap, lvl)
diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
index 4207fb362da0..031f038b19f7 100644
--- a/arch/sparc/include/asm/unistd.h
+++ b/arch/sparc/include/asm/unistd.h
@@ -1,8 +1,444 @@
-#ifndef ___ASM_SPARC_UNISTD_H
-#define ___ASM_SPARC_UNISTD_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/unistd_64.h>
+#ifndef _SPARC_UNISTD_H
+#define _SPARC_UNISTD_H
+
+/*
+ * System calls under the Sparc.
+ *
+ * Don't be scared by the ugly clobbers, it is the only way I can
+ * think of right now to force the arguments into fixed registers
+ * before the trap into the system call with gcc 'asm' statements.
+ *
+ * Copyright (C) 1995, 2007 David S. Miller (davem@davemloft.net)
+ *
+ * SunOS compatibility based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+#ifndef __32bit_syscall_numbers__
+#ifndef __arch64__
+#define __32bit_syscall_numbers__
+#endif
+#endif
+
+#define __NR_restart_syscall 0 /* Linux Specific */
+#define __NR_exit 1 /* Common */
+#define __NR_fork 2 /* Common */
+#define __NR_read 3 /* Common */
+#define __NR_write 4 /* Common */
+#define __NR_open 5 /* Common */
+#define __NR_close 6 /* Common */
+#define __NR_wait4 7 /* Common */
+#define __NR_creat 8 /* Common */
+#define __NR_link 9 /* Common */
+#define __NR_unlink 10 /* Common */
+#define __NR_execv 11 /* SunOS Specific */
+#define __NR_chdir 12 /* Common */
+#define __NR_chown 13 /* Common */
+#define __NR_mknod 14 /* Common */
+#define __NR_chmod 15 /* Common */
+#define __NR_lchown 16 /* Common */
+#define __NR_brk 17 /* Common */
+#define __NR_perfctr 18 /* Performance counter operations */
+#define __NR_lseek 19 /* Common */
+#define __NR_getpid 20 /* Common */
+#define __NR_capget 21 /* Linux Specific */
+#define __NR_capset 22 /* Linux Specific */
+#define __NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define __NR_getuid 24 /* Common */
+#define __NR_vmsplice 25 /* ENOSYS under SunOS */
+#define __NR_ptrace 26 /* Common */
+#define __NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define __NR_sigaltstack 28 /* Common */
+#define __NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define __NR_utime 30 /* Implemented via utimes() under SunOS */
+#ifdef __32bit_syscall_numbers__
+#define __NR_lchown32 31 /* Linux sparc32 specific */
+#define __NR_fchown32 32 /* Linux sparc32 specific */
+#endif
+#define __NR_access 33 /* Common */
+#define __NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+#ifdef __32bit_syscall_numbers__
+#define __NR_chown32 35 /* Linux sparc32 specific */
+#endif
+#define __NR_sync 36 /* Common */
+#define __NR_kill 37 /* Common */
+#define __NR_stat 38 /* Common */
+#define __NR_sendfile 39 /* Linux Specific */
+#define __NR_lstat 40 /* Common */
+#define __NR_dup 41 /* Common */
+#define __NR_pipe 42 /* Common */
+#define __NR_times 43 /* Implemented via getrusage() in SunOS */
+#ifdef __32bit_syscall_numbers__
+#define __NR_getuid32 44 /* Linux sparc32 specific */
+#endif
+#define __NR_umount2 45 /* Linux Specific */
+#define __NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define __NR_getgid 47 /* Common */
+#define __NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define __NR_geteuid 49 /* SunOS calls getuid() */
+#define __NR_getegid 50 /* SunOS calls getgid() */
+#define __NR_acct 51 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_getgid32 53 /* Linux sparc32 specific */
+#else
+#define __NR_memory_ordering 52 /* Linux Specific */
+#endif
+#define __NR_ioctl 54 /* Common */
+#define __NR_reboot 55 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_mmap2 56 /* Linux sparc32 Specific */
+#endif
+#define __NR_symlink 57 /* Common */
+#define __NR_readlink 58 /* Common */
+#define __NR_execve 59 /* Common */
+#define __NR_umask 60 /* Common */
+#define __NR_chroot 61 /* Common */
+#define __NR_fstat 62 /* Common */
+#define __NR_fstat64 63 /* Linux Specific */
+#define __NR_getpagesize 64 /* Common */
+#define __NR_msync 65 /* Common in newer 1.3.x revs... */
+#define __NR_vfork 66 /* Common */
+#define __NR_pread64 67 /* Linux Specific */
+#define __NR_pwrite64 68 /* Linux Specific */
+#ifdef __32bit_syscall_numbers__
+#define __NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */
+#define __NR_getegid32 70 /* Linux sparc32, sstk under SunOS */
+#endif
+#define __NR_mmap 71 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */
+#endif
+#define __NR_munmap 73 /* Common */
+#define __NR_mprotect 74 /* Common */
+#define __NR_madvise 75 /* Common */
+#define __NR_vhangup 76 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_truncate64 77 /* Linux sparc32 Specific */
+#endif
+#define __NR_mincore 78 /* Common */
+#define __NR_getgroups 79 /* Common */
+#define __NR_setgroups 80 /* Common */
+#define __NR_getpgrp 81 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */
+#endif
+#define __NR_setitimer 83 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_ftruncate64 84 /* Linux sparc32 Specific */
+#endif
+#define __NR_swapon 85 /* Common */
+#define __NR_getitimer 86 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */
+#endif
+#define __NR_sethostname 88 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */
+#endif
+#define __NR_dup2 90 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */
+#endif
+#define __NR_fcntl 92 /* Common */
+#define __NR_select 93 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */
+#endif
+#define __NR_fsync 95 /* Common */
+#define __NR_setpriority 96 /* Common */
+#define __NR_socket 97 /* Common */
+#define __NR_connect 98 /* Common */
+#define __NR_accept 99 /* Common */
+#define __NR_getpriority 100 /* Common */
+#define __NR_rt_sigreturn 101 /* Linux Specific */
+#define __NR_rt_sigaction 102 /* Linux Specific */
+#define __NR_rt_sigprocmask 103 /* Linux Specific */
+#define __NR_rt_sigpending 104 /* Linux Specific */
+#define __NR_rt_sigtimedwait 105 /* Linux Specific */
+#define __NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define __NR_rt_sigsuspend 107 /* Linux Specific */
+#ifdef __32bit_syscall_numbers__
+#define __NR_setresuid32 108 /* Linux Specific, sigvec under SunOS */
+#define __NR_getresuid32 109 /* Linux Specific, sigblock under SunOS */
+#define __NR_setresgid32 110 /* Linux Specific, sigsetmask under SunOS */
+#define __NR_getresgid32 111 /* Linux Specific, sigpause under SunOS */
+#define __NR_setregid32 112 /* Linux sparc32, sigstack under SunOS */
+#else
+#define __NR_setresuid 108 /* Linux Specific, sigvec under SunOS */
+#define __NR_getresuid 109 /* Linux Specific, sigblock under SunOS */
+#define __NR_setresgid 110 /* Linux Specific, sigsetmask under SunOS */
+#define __NR_getresgid 111 /* Linux Specific, sigpause under SunOS */
+#endif
+#define __NR_recvmsg 113 /* Common */
+#define __NR_sendmsg 114 /* Common */
+#ifdef __32bit_syscall_numbers__
+#define __NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */
+#endif
+#define __NR_gettimeofday 116 /* Common */
+#define __NR_getrusage 117 /* Common */
+#define __NR_getsockopt 118 /* Common */
+#define __NR_getcwd 119 /* Linux Specific */
+#define __NR_readv 120 /* Common */
+#define __NR_writev 121 /* Common */
+#define __NR_settimeofday 122 /* Common */
+#define __NR_fchown 123 /* Common */
+#define __NR_fchmod 124 /* Common */
+#define __NR_recvfrom 125 /* Common */
+#define __NR_setreuid 126 /* Common */
+#define __NR_setregid 127 /* Common */
+#define __NR_rename 128 /* Common */
+#define __NR_truncate 129 /* Common */
+#define __NR_ftruncate 130 /* Common */
+#define __NR_flock 131 /* Common */
+#define __NR_lstat64 132 /* Linux Specific */
+#define __NR_sendto 133 /* Common */
+#define __NR_shutdown 134 /* Common */
+#define __NR_socketpair 135 /* Common */
+#define __NR_mkdir 136 /* Common */
+#define __NR_rmdir 137 /* Common */
+#define __NR_utimes 138 /* SunOS Specific */
+#define __NR_stat64 139 /* Linux Specific */
+#define __NR_sendfile64 140 /* adjtime under SunOS */
+#define __NR_getpeername 141 /* Common */
+#define __NR_futex 142 /* gethostid under SunOS */
+#define __NR_gettid 143 /* ENOSYS under SunOS */
+#define __NR_getrlimit 144 /* Common */
+#define __NR_setrlimit 145 /* Common */
+#define __NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
+#define __NR_prctl 147 /* ENOSYS under SunOS */
+#define __NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define __NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define __NR_getsockname 150 /* Common */
+#define __NR_inotify_init 151 /* Linux specific */
+#define __NR_inotify_add_watch 152 /* Linux specific */
+#define __NR_poll 153 /* Common */
+#define __NR_getdents64 154 /* Linux specific */
+#ifdef __32bit_syscall_numbers__
+#define __NR_fcntl64 155 /* Linux sparc32 Specific */
+#endif
+#define __NR_inotify_rm_watch 156 /* Linux specific */
+#define __NR_statfs 157 /* Common */
+#define __NR_fstatfs 158 /* Common */
+#define __NR_umount 159 /* Common */
+#define __NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */
+#define __NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */
+#define __NR_getdomainname 162 /* SunOS Specific */
+#define __NR_setdomainname 163 /* Common */
+#ifndef __32bit_syscall_numbers__
+#define __NR_utrap_install 164 /* SYSV ABI/v9 required */
+#endif
+#define __NR_quotactl 165 /* Common */
+#define __NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */
+#define __NR_mount 167 /* Common */
+#define __NR_ustat 168 /* Common */
+#define __NR_setxattr 169 /* SunOS: semsys */
+#define __NR_lsetxattr 170 /* SunOS: msgsys */
+#define __NR_fsetxattr 171 /* SunOS: shmsys */
+#define __NR_getxattr 172 /* SunOS: auditsys */
+#define __NR_lgetxattr 173 /* SunOS: rfssys */
+#define __NR_getdents 174 /* Common */
+#define __NR_setsid 175 /* Common */
+#define __NR_fchdir 176 /* Common */
+#define __NR_fgetxattr 177 /* SunOS: fchroot */
+#define __NR_listxattr 178 /* SunOS: vpixsys */
+#define __NR_llistxattr 179 /* SunOS: aioread */
+#define __NR_flistxattr 180 /* SunOS: aiowrite */
+#define __NR_removexattr 181 /* SunOS: aiowait */
+#define __NR_lremovexattr 182 /* SunOS: aiocancel */
+#define __NR_sigpending 183 /* Common */
+#define __NR_query_module 184 /* Linux Specific */
+#define __NR_setpgid 185 /* Common */
+#define __NR_fremovexattr 186 /* SunOS: pathconf */
+#define __NR_tkill 187 /* SunOS: fpathconf */
+#define __NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
+#define __NR_uname 189 /* Linux Specific */
+#define __NR_init_module 190 /* Linux Specific */
+#define __NR_personality 191 /* Linux Specific */
+#define __NR_remap_file_pages 192 /* Linux Specific */
+#define __NR_epoll_create 193 /* Linux Specific */
+#define __NR_epoll_ctl 194 /* Linux Specific */
+#define __NR_epoll_wait 195 /* Linux Specific */
+#define __NR_ioprio_set 196 /* Linux Specific */
+#define __NR_getppid 197 /* Linux Specific */
+#define __NR_sigaction 198 /* Linux Specific */
+#define __NR_sgetmask 199 /* Linux Specific */
+#define __NR_ssetmask 200 /* Linux Specific */
+#define __NR_sigsuspend 201 /* Linux Specific */
+#define __NR_oldlstat 202 /* Linux Specific */
+#define __NR_uselib 203 /* Linux Specific */
+#define __NR_readdir 204 /* Linux Specific */
+#define __NR_readahead 205 /* Linux Specific */
+#define __NR_socketcall 206 /* Linux Specific */
+#define __NR_syslog 207 /* Linux Specific */
+#define __NR_lookup_dcookie 208 /* Linux Specific */
+#define __NR_fadvise64 209 /* Linux Specific */
+#define __NR_fadvise64_64 210 /* Linux Specific */
+#define __NR_tgkill 211 /* Linux Specific */
+#define __NR_waitpid 212 /* Linux Specific */
+#define __NR_swapoff 213 /* Linux Specific */
+#define __NR_sysinfo 214 /* Linux Specific */
+#define __NR_ipc 215 /* Linux Specific */
+#define __NR_sigreturn 216 /* Linux Specific */
+#define __NR_clone 217 /* Linux Specific */
+#define __NR_ioprio_get 218 /* Linux Specific */
+#define __NR_adjtimex 219 /* Linux Specific */
+#define __NR_sigprocmask 220 /* Linux Specific */
+#define __NR_create_module 221 /* Linux Specific */
+#define __NR_delete_module 222 /* Linux Specific */
+#define __NR_get_kernel_syms 223 /* Linux Specific */
+#define __NR_getpgid 224 /* Linux Specific */
+#define __NR_bdflush 225 /* Linux Specific */
+#define __NR_sysfs 226 /* Linux Specific */
+#define __NR_afs_syscall 227 /* Linux Specific */
+#define __NR_setfsuid 228 /* Linux Specific */
+#define __NR_setfsgid 229 /* Linux Specific */
+#define __NR__newselect 230 /* Linux Specific */
+#ifdef __32bit_syscall_numbers__
+#define __NR_time 231 /* Linux Specific */
#else
-#include <asm/unistd_32.h>
+#ifdef __KERNEL__
+#define __NR_time 231 /* Linux sparc32 */
+#endif
+#endif
+#define __NR_splice 232 /* Linux Specific */
+#define __NR_stime 233 /* Linux Specific */
+#define __NR_statfs64 234 /* Linux Specific */
+#define __NR_fstatfs64 235 /* Linux Specific */
+#define __NR__llseek 236 /* Linux Specific */
+#define __NR_mlock 237
+#define __NR_munlock 238
+#define __NR_mlockall 239
+#define __NR_munlockall 240
+#define __NR_sched_setparam 241
+#define __NR_sched_getparam 242
+#define __NR_sched_setscheduler 243
+#define __NR_sched_getscheduler 244
+#define __NR_sched_yield 245
+#define __NR_sched_get_priority_max 246
+#define __NR_sched_get_priority_min 247
+#define __NR_sched_rr_get_interval 248
+#define __NR_nanosleep 249
+#define __NR_mremap 250
+#define __NR__sysctl 251
+#define __NR_getsid 252
+#define __NR_fdatasync 253
+#define __NR_nfsservctl 254
+#define __NR_sync_file_range 255
+#define __NR_clock_settime 256
+#define __NR_clock_gettime 257
+#define __NR_clock_getres 258
+#define __NR_clock_nanosleep 259
+#define __NR_sched_getaffinity 260
+#define __NR_sched_setaffinity 261
+#define __NR_timer_settime 262
+#define __NR_timer_gettime 263
+#define __NR_timer_getoverrun 264
+#define __NR_timer_delete 265
+#define __NR_timer_create 266
+/* #define __NR_vserver 267 Reserved for VSERVER */
+#define __NR_io_setup 268
+#define __NR_io_destroy 269
+#define __NR_io_submit 270
+#define __NR_io_cancel 271
+#define __NR_io_getevents 272
+#define __NR_mq_open 273
+#define __NR_mq_unlink 274
+#define __NR_mq_timedsend 275
+#define __NR_mq_timedreceive 276
+#define __NR_mq_notify 277
+#define __NR_mq_getsetattr 278
+#define __NR_waitid 279
+#define __NR_tee 280
+#define __NR_add_key 281
+#define __NR_request_key 282
+#define __NR_keyctl 283
+#define __NR_openat 284
+#define __NR_mkdirat 285
+#define __NR_mknodat 286
+#define __NR_fchownat 287
+#define __NR_futimesat 288
+#define __NR_fstatat64 289
+#define __NR_unlinkat 290
+#define __NR_renameat 291
+#define __NR_linkat 292
+#define __NR_symlinkat 293
+#define __NR_readlinkat 294
+#define __NR_fchmodat 295
+#define __NR_faccessat 296
+#define __NR_pselect6 297
+#define __NR_ppoll 298
+#define __NR_unshare 299
+#define __NR_set_robust_list 300
+#define __NR_get_robust_list 301
+#define __NR_migrate_pages 302
+#define __NR_mbind 303
+#define __NR_get_mempolicy 304
+#define __NR_set_mempolicy 305
+#define __NR_kexec_load 306
+#define __NR_move_pages 307
+#define __NR_getcpu 308
+#define __NR_epoll_pwait 309
+#define __NR_utimensat 310
+#define __NR_signalfd 311
+#define __NR_timerfd_create 312
+#define __NR_eventfd 313
+#define __NR_fallocate 314
+#define __NR_timerfd_settime 315
+#define __NR_timerfd_gettime 316
+#define __NR_signalfd4 317
+#define __NR_eventfd2 318
+#define __NR_epoll_create1 319
+#define __NR_dup3 320
+#define __NR_pipe2 321
+#define __NR_inotify_init1 322
+#define __NR_accept4 323
+
+#define NR_SYSCALLS 324
+
+#ifdef __32bit_syscall_numbers__
+/* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
+ * it never had the plain ones and there is no value to adding those
+ * old versions into the syscall table.
+ */
+#define __IGNORE_setresuid
+#define __IGNORE_getresuid
+#define __IGNORE_setresgid
+#define __IGNORE_getresgid
#endif
+
+#ifdef __KERNEL__
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
+#ifndef __32bit_syscall_numbers__
+#define __ARCH_WANT_COMPAT_SYS_TIME
+#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
+
+#endif /* __KERNEL__ */
+#endif /* _SPARC_UNISTD_H */
diff --git a/arch/sparc/include/asm/unistd_32.h b/arch/sparc/include/asm/unistd_32.h
deleted file mode 100644
index 0d13d2a4c76f..000000000000
--- a/arch/sparc/include/asm/unistd_32.h
+++ /dev/null
@@ -1,385 +0,0 @@
-#ifndef _SPARC_UNISTD_H
-#define _SPARC_UNISTD_H
-
-/*
- * System calls under the Sparc.
- *
- * Don't be scared by the ugly clobbers, it is the only way I can
- * think of right now to force the arguments into fixed registers
- * before the trap into the system call with gcc 'asm' statements.
- *
- * Copyright (C) 1995, 2007 David S. Miller (davem@davemloft.net)
- *
- * SunOS compatibility based upon preliminary work which is:
- *
- * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
- */
-
-#define __NR_restart_syscall 0 /* Linux Specific */
-#define __NR_exit 1 /* Common */
-#define __NR_fork 2 /* Common */
-#define __NR_read 3 /* Common */
-#define __NR_write 4 /* Common */
-#define __NR_open 5 /* Common */
-#define __NR_close 6 /* Common */
-#define __NR_wait4 7 /* Common */
-#define __NR_creat 8 /* Common */
-#define __NR_link 9 /* Common */
-#define __NR_unlink 10 /* Common */
-#define __NR_execv 11 /* SunOS Specific */
-#define __NR_chdir 12 /* Common */
-#define __NR_chown 13 /* Common */
-#define __NR_mknod 14 /* Common */
-#define __NR_chmod 15 /* Common */
-#define __NR_lchown 16 /* Common */
-#define __NR_brk 17 /* Common */
-#define __NR_perfctr 18 /* Performance counter operations */
-#define __NR_lseek 19 /* Common */
-#define __NR_getpid 20 /* Common */
-#define __NR_capget 21 /* Linux Specific */
-#define __NR_capset 22 /* Linux Specific */
-#define __NR_setuid 23 /* Implemented via setreuid in SunOS */
-#define __NR_getuid 24 /* Common */
-#define __NR_vmsplice 25 /* ENOSYS under SunOS */
-#define __NR_ptrace 26 /* Common */
-#define __NR_alarm 27 /* Implemented via setitimer in SunOS */
-#define __NR_sigaltstack 28 /* Common */
-#define __NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
-#define __NR_utime 30 /* Implemented via utimes() under SunOS */
-#define __NR_lchown32 31 /* Linux sparc32 specific */
-#define __NR_fchown32 32 /* Linux sparc32 specific */
-#define __NR_access 33 /* Common */
-#define __NR_nice 34 /* Implemented via get/setpriority() in SunOS */
-#define __NR_chown32 35 /* Linux sparc32 specific */
-#define __NR_sync 36 /* Common */
-#define __NR_kill 37 /* Common */
-#define __NR_stat 38 /* Common */
-#define __NR_sendfile 39 /* Linux Specific */
-#define __NR_lstat 40 /* Common */
-#define __NR_dup 41 /* Common */
-#define __NR_pipe 42 /* Common */
-#define __NR_times 43 /* Implemented via getrusage() in SunOS */
-#define __NR_getuid32 44 /* Linux sparc32 specific */
-#define __NR_umount2 45 /* Linux Specific */
-#define __NR_setgid 46 /* Implemented via setregid() in SunOS */
-#define __NR_getgid 47 /* Common */
-#define __NR_signal 48 /* Implemented via sigvec() in SunOS */
-#define __NR_geteuid 49 /* SunOS calls getuid() */
-#define __NR_getegid 50 /* SunOS calls getgid() */
-#define __NR_acct 51 /* Common */
-/* #define __NR_memory_ordering 52 Linux sparc64 specific */
-#define __NR_getgid32 53 /* Linux sparc32 specific */
-#define __NR_ioctl 54 /* Common */
-#define __NR_reboot 55 /* Common */
-#define __NR_mmap2 56 /* Linux sparc32 Specific */
-#define __NR_symlink 57 /* Common */
-#define __NR_readlink 58 /* Common */
-#define __NR_execve 59 /* Common */
-#define __NR_umask 60 /* Common */
-#define __NR_chroot 61 /* Common */
-#define __NR_fstat 62 /* Common */
-#define __NR_fstat64 63 /* Linux Specific */
-#define __NR_getpagesize 64 /* Common */
-#define __NR_msync 65 /* Common in newer 1.3.x revs... */
-#define __NR_vfork 66 /* Common */
-#define __NR_pread64 67 /* Linux Specific */
-#define __NR_pwrite64 68 /* Linux Specific */
-#define __NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */
-#define __NR_getegid32 70 /* Linux sparc32, sstk under SunOS */
-#define __NR_mmap 71 /* Common */
-#define __NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */
-#define __NR_munmap 73 /* Common */
-#define __NR_mprotect 74 /* Common */
-#define __NR_madvise 75 /* Common */
-#define __NR_vhangup 76 /* Common */
-#define __NR_truncate64 77 /* Linux sparc32 Specific */
-#define __NR_mincore 78 /* Common */
-#define __NR_getgroups 79 /* Common */
-#define __NR_setgroups 80 /* Common */
-#define __NR_getpgrp 81 /* Common */
-#define __NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */
-#define __NR_setitimer 83 /* Common */
-#define __NR_ftruncate64 84 /* Linux sparc32 Specific */
-#define __NR_swapon 85 /* Common */
-#define __NR_getitimer 86 /* Common */
-#define __NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */
-#define __NR_sethostname 88 /* Common */
-#define __NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */
-#define __NR_dup2 90 /* Common */
-#define __NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */
-#define __NR_fcntl 92 /* Common */
-#define __NR_select 93 /* Common */
-#define __NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */
-#define __NR_fsync 95 /* Common */
-#define __NR_setpriority 96 /* Common */
-#define __NR_socket 97 /* Common */
-#define __NR_connect 98 /* Common */
-#define __NR_accept 99 /* Common */
-#define __NR_getpriority 100 /* Common */
-#define __NR_rt_sigreturn 101 /* Linux Specific */
-#define __NR_rt_sigaction 102 /* Linux Specific */
-#define __NR_rt_sigprocmask 103 /* Linux Specific */
-#define __NR_rt_sigpending 104 /* Linux Specific */
-#define __NR_rt_sigtimedwait 105 /* Linux Specific */
-#define __NR_rt_sigqueueinfo 106 /* Linux Specific */
-#define __NR_rt_sigsuspend 107 /* Linux Specific */
-#define __NR_setresuid32 108 /* Linux Specific, sigvec under SunOS */
-#define __NR_getresuid32 109 /* Linux Specific, sigblock under SunOS */
-#define __NR_setresgid32 110 /* Linux Specific, sigsetmask under SunOS */
-#define __NR_getresgid32 111 /* Linux Specific, sigpause under SunOS */
-#define __NR_setregid32 112 /* Linux sparc32, sigstack under SunOS */
-#define __NR_recvmsg 113 /* Common */
-#define __NR_sendmsg 114 /* Common */
-#define __NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */
-#define __NR_gettimeofday 116 /* Common */
-#define __NR_getrusage 117 /* Common */
-#define __NR_getsockopt 118 /* Common */
-#define __NR_getcwd 119 /* Linux Specific */
-#define __NR_readv 120 /* Common */
-#define __NR_writev 121 /* Common */
-#define __NR_settimeofday 122 /* Common */
-#define __NR_fchown 123 /* Common */
-#define __NR_fchmod 124 /* Common */
-#define __NR_recvfrom 125 /* Common */
-#define __NR_setreuid 126 /* Common */
-#define __NR_setregid 127 /* Common */
-#define __NR_rename 128 /* Common */
-#define __NR_truncate 129 /* Common */
-#define __NR_ftruncate 130 /* Common */
-#define __NR_flock 131 /* Common */
-#define __NR_lstat64 132 /* Linux Specific */
-#define __NR_sendto 133 /* Common */
-#define __NR_shutdown 134 /* Common */
-#define __NR_socketpair 135 /* Common */
-#define __NR_mkdir 136 /* Common */
-#define __NR_rmdir 137 /* Common */
-#define __NR_utimes 138 /* SunOS Specific */
-#define __NR_stat64 139 /* Linux Specific */
-#define __NR_sendfile64 140 /* adjtime under SunOS */
-#define __NR_getpeername 141 /* Common */
-#define __NR_futex 142 /* gethostid under SunOS */
-#define __NR_gettid 143 /* ENOSYS under SunOS */
-#define __NR_getrlimit 144 /* Common */
-#define __NR_setrlimit 145 /* Common */
-#define __NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
-#define __NR_prctl 147 /* ENOSYS under SunOS */
-#define __NR_pciconfig_read 148 /* ENOSYS under SunOS */
-#define __NR_pciconfig_write 149 /* ENOSYS under SunOS */
-#define __NR_getsockname 150 /* Common */
-#define __NR_inotify_init 151 /* Linux specific */
-#define __NR_inotify_add_watch 152 /* Linux specific */
-#define __NR_poll 153 /* Common */
-#define __NR_getdents64 154 /* Linux specific */
-#define __NR_fcntl64 155 /* Linux sparc32 Specific */
-#define __NR_inotify_rm_watch 156 /* Linux specific */
-#define __NR_statfs 157 /* Common */
-#define __NR_fstatfs 158 /* Common */
-#define __NR_umount 159 /* Common */
-#define __NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */
-#define __NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */
-#define __NR_getdomainname 162 /* SunOS Specific */
-#define __NR_setdomainname 163 /* Common */
-/* #define __NR_utrap_install 164 Linux sparc64 specific */
-#define __NR_quotactl 165 /* Common */
-#define __NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */
-#define __NR_mount 167 /* Common */
-#define __NR_ustat 168 /* Common */
-#define __NR_setxattr 169 /* SunOS: semsys */
-#define __NR_lsetxattr 170 /* SunOS: msgsys */
-#define __NR_fsetxattr 171 /* SunOS: shmsys */
-#define __NR_getxattr 172 /* SunOS: auditsys */
-#define __NR_lgetxattr 173 /* SunOS: rfssys */
-#define __NR_getdents 174 /* Common */
-#define __NR_setsid 175 /* Common */
-#define __NR_fchdir 176 /* Common */
-#define __NR_fgetxattr 177 /* SunOS: fchroot */
-#define __NR_listxattr 178 /* SunOS: vpixsys */
-#define __NR_llistxattr 179 /* SunOS: aioread */
-#define __NR_flistxattr 180 /* SunOS: aiowrite */
-#define __NR_removexattr 181 /* SunOS: aiowait */
-#define __NR_lremovexattr 182 /* SunOS: aiocancel */
-#define __NR_sigpending 183 /* Common */
-#define __NR_query_module 184 /* Linux Specific */
-#define __NR_setpgid 185 /* Common */
-#define __NR_fremovexattr 186 /* SunOS: pathconf */
-#define __NR_tkill 187 /* SunOS: fpathconf */
-#define __NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
-#define __NR_uname 189 /* Linux Specific */
-#define __NR_init_module 190 /* Linux Specific */
-#define __NR_personality 191 /* Linux Specific */
-#define __NR_remap_file_pages 192 /* Linux Specific */
-#define __NR_epoll_create 193 /* Linux Specific */
-#define __NR_epoll_ctl 194 /* Linux Specific */
-#define __NR_epoll_wait 195 /* Linux Specific */
-#define __NR_ioprio_set 196 /* Linux Specific */
-#define __NR_getppid 197 /* Linux Specific */
-#define __NR_sigaction 198 /* Linux Specific */
-#define __NR_sgetmask 199 /* Linux Specific */
-#define __NR_ssetmask 200 /* Linux Specific */
-#define __NR_sigsuspend 201 /* Linux Specific */
-#define __NR_oldlstat 202 /* Linux Specific */
-#define __NR_uselib 203 /* Linux Specific */
-#define __NR_readdir 204 /* Linux Specific */
-#define __NR_readahead 205 /* Linux Specific */
-#define __NR_socketcall 206 /* Linux Specific */
-#define __NR_syslog 207 /* Linux Specific */
-#define __NR_lookup_dcookie 208 /* Linux Specific */
-#define __NR_fadvise64 209 /* Linux Specific */
-#define __NR_fadvise64_64 210 /* Linux Specific */
-#define __NR_tgkill 211 /* Linux Specific */
-#define __NR_waitpid 212 /* Linux Specific */
-#define __NR_swapoff 213 /* Linux Specific */
-#define __NR_sysinfo 214 /* Linux Specific */
-#define __NR_ipc 215 /* Linux Specific */
-#define __NR_sigreturn 216 /* Linux Specific */
-#define __NR_clone 217 /* Linux Specific */
-#define __NR_ioprio_get 218 /* Linux Specific */
-#define __NR_adjtimex 219 /* Linux Specific */
-#define __NR_sigprocmask 220 /* Linux Specific */
-#define __NR_create_module 221 /* Linux Specific */
-#define __NR_delete_module 222 /* Linux Specific */
-#define __NR_get_kernel_syms 223 /* Linux Specific */
-#define __NR_getpgid 224 /* Linux Specific */
-#define __NR_bdflush 225 /* Linux Specific */
-#define __NR_sysfs 226 /* Linux Specific */
-#define __NR_afs_syscall 227 /* Linux Specific */
-#define __NR_setfsuid 228 /* Linux Specific */
-#define __NR_setfsgid 229 /* Linux Specific */
-#define __NR__newselect 230 /* Linux Specific */
-#define __NR_time 231 /* Linux Specific */
-#define __NR_splice 232 /* Linux Specific */
-#define __NR_stime 233 /* Linux Specific */
-#define __NR_statfs64 234 /* Linux Specific */
-#define __NR_fstatfs64 235 /* Linux Specific */
-#define __NR__llseek 236 /* Linux Specific */
-#define __NR_mlock 237
-#define __NR_munlock 238
-#define __NR_mlockall 239
-#define __NR_munlockall 240
-#define __NR_sched_setparam 241
-#define __NR_sched_getparam 242
-#define __NR_sched_setscheduler 243
-#define __NR_sched_getscheduler 244
-#define __NR_sched_yield 245
-#define __NR_sched_get_priority_max 246
-#define __NR_sched_get_priority_min 247
-#define __NR_sched_rr_get_interval 248
-#define __NR_nanosleep 249
-#define __NR_mremap 250
-#define __NR__sysctl 251
-#define __NR_getsid 252
-#define __NR_fdatasync 253
-#define __NR_nfsservctl 254
-#define __NR_sync_file_range 255
-#define __NR_clock_settime 256
-#define __NR_clock_gettime 257
-#define __NR_clock_getres 258
-#define __NR_clock_nanosleep 259
-#define __NR_sched_getaffinity 260
-#define __NR_sched_setaffinity 261
-#define __NR_timer_settime 262
-#define __NR_timer_gettime 263
-#define __NR_timer_getoverrun 264
-#define __NR_timer_delete 265
-#define __NR_timer_create 266
-/* #define __NR_vserver 267 Reserved for VSERVER */
-#define __NR_io_setup 268
-#define __NR_io_destroy 269
-#define __NR_io_submit 270
-#define __NR_io_cancel 271
-#define __NR_io_getevents 272
-#define __NR_mq_open 273
-#define __NR_mq_unlink 274
-#define __NR_mq_timedsend 275
-#define __NR_mq_timedreceive 276
-#define __NR_mq_notify 277
-#define __NR_mq_getsetattr 278
-#define __NR_waitid 279
-#define __NR_tee 280
-#define __NR_add_key 281
-#define __NR_request_key 282
-#define __NR_keyctl 283
-#define __NR_openat 284
-#define __NR_mkdirat 285
-#define __NR_mknodat 286
-#define __NR_fchownat 287
-#define __NR_futimesat 288
-#define __NR_fstatat64 289
-#define __NR_unlinkat 290
-#define __NR_renameat 291
-#define __NR_linkat 292
-#define __NR_symlinkat 293
-#define __NR_readlinkat 294
-#define __NR_fchmodat 295
-#define __NR_faccessat 296
-#define __NR_pselect6 297
-#define __NR_ppoll 298
-#define __NR_unshare 299
-#define __NR_set_robust_list 300
-#define __NR_get_robust_list 301
-#define __NR_migrate_pages 302
-#define __NR_mbind 303
-#define __NR_get_mempolicy 304
-#define __NR_set_mempolicy 305
-#define __NR_kexec_load 306
-#define __NR_move_pages 307
-#define __NR_getcpu 308
-#define __NR_epoll_pwait 309
-#define __NR_utimensat 310
-#define __NR_signalfd 311
-#define __NR_timerfd_create 312
-#define __NR_eventfd 313
-#define __NR_fallocate 314
-#define __NR_timerfd_settime 315
-#define __NR_timerfd_gettime 316
-#define __NR_signalfd4 317
-#define __NR_eventfd2 318
-#define __NR_epoll_create1 319
-#define __NR_dup3 320
-#define __NR_pipe2 321
-#define __NR_inotify_init1 322
-#define __NR_accept4 323
-
-#define NR_SYSCALLS 324
-
-/* Sparc 32-bit only has the "setresuid32", "getresuid32" variants,
- * it never had the plain ones and there is no value to adding those
- * old versions into the syscall table.
- */
-#define __IGNORE_setresuid
-#define __IGNORE_getresuid
-#define __IGNORE_setresgid
-#define __IGNORE_getresgid
-
-#ifdef __KERNEL__
-#define __ARCH_WANT_IPC_PARSE_VERSION
-#define __ARCH_WANT_OLD_READDIR
-#define __ARCH_WANT_STAT64
-#define __ARCH_WANT_SYS_ALARM
-#define __ARCH_WANT_SYS_GETHOSTNAME
-#define __ARCH_WANT_SYS_PAUSE
-#define __ARCH_WANT_SYS_SGETMASK
-#define __ARCH_WANT_SYS_SIGNAL
-#define __ARCH_WANT_SYS_TIME
-#define __ARCH_WANT_SYS_UTIME
-#define __ARCH_WANT_SYS_WAITPID
-#define __ARCH_WANT_SYS_SOCKETCALL
-#define __ARCH_WANT_SYS_FADVISE64
-#define __ARCH_WANT_SYS_GETPGRP
-#define __ARCH_WANT_SYS_LLSEEK
-#define __ARCH_WANT_SYS_NICE
-#define __ARCH_WANT_SYS_OLDUMOUNT
-#define __ARCH_WANT_SYS_SIGPENDING
-#define __ARCH_WANT_SYS_SIGPROCMASK
-#define __ARCH_WANT_SYS_RT_SIGSUSPEND
-
-/*
- * "Conditional" syscalls
- *
- * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
- * but it doesn't work on all toolchains, so we just do it by hand
- */
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
-
-#endif /* __KERNEL__ */
-#endif /* _SPARC_UNISTD_H */
diff --git a/arch/sparc/include/asm/unistd_64.h b/arch/sparc/include/asm/unistd_64.h
deleted file mode 100644
index fa5d3c0343c7..000000000000
--- a/arch/sparc/include/asm/unistd_64.h
+++ /dev/null
@@ -1,380 +0,0 @@
-#ifndef _SPARC64_UNISTD_H
-#define _SPARC64_UNISTD_H
-
-/*
- * System calls under the Sparc.
- *
- * Don't be scared by the ugly clobbers, it is the only way I can
- * think of right now to force the arguments into fixed registers
- * before the trap into the system call with gcc 'asm' statements.
- *
- * Copyright (C) 1995, 2007 David S. Miller (davem@davemloft.net)
- *
- * SunOS compatibility based upon preliminary work which is:
- *
- * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
- */
-
-#define __NR_restart_syscall 0 /* Linux Specific */
-#define __NR_exit 1 /* Common */
-#define __NR_fork 2 /* Common */
-#define __NR_read 3 /* Common */
-#define __NR_write 4 /* Common */
-#define __NR_open 5 /* Common */
-#define __NR_close 6 /* Common */
-#define __NR_wait4 7 /* Common */
-#define __NR_creat 8 /* Common */
-#define __NR_link 9 /* Common */
-#define __NR_unlink 10 /* Common */
-#define __NR_execv 11 /* SunOS Specific */
-#define __NR_chdir 12 /* Common */
-#define __NR_chown 13 /* Common */
-#define __NR_mknod 14 /* Common */
-#define __NR_chmod 15 /* Common */
-#define __NR_lchown 16 /* Common */
-#define __NR_brk 17 /* Common */
-#define __NR_perfctr 18 /* Performance counter operations */
-#define __NR_lseek 19 /* Common */
-#define __NR_getpid 20 /* Common */
-#define __NR_capget 21 /* Linux Specific */
-#define __NR_capset 22 /* Linux Specific */
-#define __NR_setuid 23 /* Implemented via setreuid in SunOS */
-#define __NR_getuid 24 /* Common */
-#define __NR_vmsplice 25 /* ENOSYS under SunOS */
-#define __NR_ptrace 26 /* Common */
-#define __NR_alarm 27 /* Implemented via setitimer in SunOS */
-#define __NR_sigaltstack 28 /* Common */
-#define __NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
-#define __NR_utime 30 /* Implemented via utimes() under SunOS */
-/* #define __NR_lchown32 31 Linux sparc32 specific */
-/* #define __NR_fchown32 32 Linux sparc32 specific */
-#define __NR_access 33 /* Common */
-#define __NR_nice 34 /* Implemented via get/setpriority() in SunOS */
-/* #define __NR_chown32 35 Linux sparc32 specific */
-#define __NR_sync 36 /* Common */
-#define __NR_kill 37 /* Common */
-#define __NR_stat 38 /* Common */
-#define __NR_sendfile 39 /* Linux Specific */
-#define __NR_lstat 40 /* Common */
-#define __NR_dup 41 /* Common */
-#define __NR_pipe 42 /* Common */
-#define __NR_times 43 /* Implemented via getrusage() in SunOS */
-/* #define __NR_getuid32 44 Linux sparc32 specific */
-#define __NR_umount2 45 /* Linux Specific */
-#define __NR_setgid 46 /* Implemented via setregid() in SunOS */
-#define __NR_getgid 47 /* Common */
-#define __NR_signal 48 /* Implemented via sigvec() in SunOS */
-#define __NR_geteuid 49 /* SunOS calls getuid() */
-#define __NR_getegid 50 /* SunOS calls getgid() */
-#define __NR_acct 51 /* Common */
-#define __NR_memory_ordering 52 /* Linux Specific */
-/* #define __NR_getgid32 53 Linux sparc32 specific */
-#define __NR_ioctl 54 /* Common */
-#define __NR_reboot 55 /* Common */
-/* #define __NR_mmap2 56 Linux sparc32 Specific */
-#define __NR_symlink 57 /* Common */
-#define __NR_readlink 58 /* Common */
-#define __NR_execve 59 /* Common */
-#define __NR_umask 60 /* Common */
-#define __NR_chroot 61 /* Common */
-#define __NR_fstat 62 /* Common */
-#define __NR_fstat64 63 /* Linux Specific */
-#define __NR_getpagesize 64 /* Common */
-#define __NR_msync 65 /* Common in newer 1.3.x revs... */
-#define __NR_vfork 66 /* Common */
-#define __NR_pread64 67 /* Linux Specific */
-#define __NR_pwrite64 68 /* Linux Specific */
-/* #define __NR_geteuid32 69 Linux sparc32, sbrk under SunOS */
-/* #define __NR_getegid32 70 Linux sparc32, sstk under SunOS */
-#define __NR_mmap 71 /* Common */
-/* #define __NR_setreuid32 72 Linux sparc32, vadvise under SunOS */
-#define __NR_munmap 73 /* Common */
-#define __NR_mprotect 74 /* Common */
-#define __NR_madvise 75 /* Common */
-#define __NR_vhangup 76 /* Common */
-/* #define __NR_truncate64 77 Linux sparc32 Specific */
-#define __NR_mincore 78 /* Common */
-#define __NR_getgroups 79 /* Common */
-#define __NR_setgroups 80 /* Common */
-#define __NR_getpgrp 81 /* Common */
-/* #define __NR_setgroups32 82 Linux sparc32, setpgrp under SunOS */
-#define __NR_setitimer 83 /* Common */
-/* #define __NR_ftruncate64 84 Linux sparc32 Specific */
-#define __NR_swapon 85 /* Common */
-#define __NR_getitimer 86 /* Common */
-/* #define __NR_setuid32 87 Linux sparc32, gethostname under SunOS */
-#define __NR_sethostname 88 /* Common */
-/* #define __NR_setgid32 89 Linux sparc32, getdtablesize under SunOS */
-#define __NR_dup2 90 /* Common */
-/* #define __NR_setfsuid32 91 Linux sparc32, getdopt under SunOS */
-#define __NR_fcntl 92 /* Common */
-#define __NR_select 93 /* Common */
-/* #define __NR_setfsgid32 94 Linux sparc32, setdopt under SunOS */
-#define __NR_fsync 95 /* Common */
-#define __NR_setpriority 96 /* Common */
-#define __NR_socket 97 /* Common */
-#define __NR_connect 98 /* Common */
-#define __NR_accept 99 /* Common */
-#define __NR_getpriority 100 /* Common */
-#define __NR_rt_sigreturn 101 /* Linux Specific */
-#define __NR_rt_sigaction 102 /* Linux Specific */
-#define __NR_rt_sigprocmask 103 /* Linux Specific */
-#define __NR_rt_sigpending 104 /* Linux Specific */
-#define __NR_rt_sigtimedwait 105 /* Linux Specific */
-#define __NR_rt_sigqueueinfo 106 /* Linux Specific */
-#define __NR_rt_sigsuspend 107 /* Linux Specific */
-#define __NR_setresuid 108 /* Linux Specific, sigvec under SunOS */
-#define __NR_getresuid 109 /* Linux Specific, sigblock under SunOS */
-#define __NR_setresgid 110 /* Linux Specific, sigsetmask under SunOS */
-#define __NR_getresgid 111 /* Linux Specific, sigpause under SunOS */
-/* #define __NR_setregid32 75 Linux sparc32, sigstack under SunOS */
-#define __NR_recvmsg 113 /* Common */
-#define __NR_sendmsg 114 /* Common */
-/* #define __NR_getgroups32 115 Linux sparc32, vtrace under SunOS */
-#define __NR_gettimeofday 116 /* Common */
-#define __NR_getrusage 117 /* Common */
-#define __NR_getsockopt 118 /* Common */
-#define __NR_getcwd 119 /* Linux Specific */
-#define __NR_readv 120 /* Common */
-#define __NR_writev 121 /* Common */
-#define __NR_settimeofday 122 /* Common */
-#define __NR_fchown 123 /* Common */
-#define __NR_fchmod 124 /* Common */
-#define __NR_recvfrom 125 /* Common */
-#define __NR_setreuid 126 /* Common */
-#define __NR_setregid 127 /* Common */
-#define __NR_rename 128 /* Common */
-#define __NR_truncate 129 /* Common */
-#define __NR_ftruncate 130 /* Common */
-#define __NR_flock 131 /* Common */
-#define __NR_lstat64 132 /* Linux Specific */
-#define __NR_sendto 133 /* Common */
-#define __NR_shutdown 134 /* Common */
-#define __NR_socketpair 135 /* Common */
-#define __NR_mkdir 136 /* Common */
-#define __NR_rmdir 137 /* Common */
-#define __NR_utimes 138 /* SunOS Specific */
-#define __NR_stat64 139 /* Linux Specific */
-#define __NR_sendfile64 140 /* adjtime under SunOS */
-#define __NR_getpeername 141 /* Common */
-#define __NR_futex 142 /* gethostid under SunOS */
-#define __NR_gettid 143 /* ENOSYS under SunOS */
-#define __NR_getrlimit 144 /* Common */
-#define __NR_setrlimit 145 /* Common */
-#define __NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
-#define __NR_prctl 147 /* ENOSYS under SunOS */
-#define __NR_pciconfig_read 148 /* ENOSYS under SunOS */
-#define __NR_pciconfig_write 149 /* ENOSYS under SunOS */
-#define __NR_getsockname 150 /* Common */
-#define __NR_inotify_init 151 /* Linux specific */
-#define __NR_inotify_add_watch 152 /* Linux specific */
-#define __NR_poll 153 /* Common */
-#define __NR_getdents64 154 /* Linux specific */
-/* #define __NR_fcntl64 155 Linux sparc32 Specific */
-#define __NR_inotify_rm_watch 156 /* Linux specific */
-#define __NR_statfs 157 /* Common */
-#define __NR_fstatfs 158 /* Common */
-#define __NR_umount 159 /* Common */
-#define __NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */
-#define __NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */
-#define __NR_getdomainname 162 /* SunOS Specific */
-#define __NR_setdomainname 163 /* Common */
-#define __NR_utrap_install 164 /* SYSV ABI/v9 required */
-#define __NR_quotactl 165 /* Common */
-#define __NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */
-#define __NR_mount 167 /* Common */
-#define __NR_ustat 168 /* Common */
-#define __NR_setxattr 169 /* SunOS: semsys */
-#define __NR_lsetxattr 170 /* SunOS: msgsys */
-#define __NR_fsetxattr 171 /* SunOS: shmsys */
-#define __NR_getxattr 172 /* SunOS: auditsys */
-#define __NR_lgetxattr 173 /* SunOS: rfssys */
-#define __NR_getdents 174 /* Common */
-#define __NR_setsid 175 /* Common */
-#define __NR_fchdir 176 /* Common */
-#define __NR_fgetxattr 177 /* SunOS: fchroot */
-#define __NR_listxattr 178 /* SunOS: vpixsys */
-#define __NR_llistxattr 179 /* SunOS: aioread */
-#define __NR_flistxattr 180 /* SunOS: aiowrite */
-#define __NR_removexattr 181 /* SunOS: aiowait */
-#define __NR_lremovexattr 182 /* SunOS: aiocancel */
-#define __NR_sigpending 183 /* Common */
-#define __NR_query_module 184 /* Linux Specific */
-#define __NR_setpgid 185 /* Common */
-#define __NR_fremovexattr 186 /* SunOS: pathconf */
-#define __NR_tkill 187 /* SunOS: fpathconf */
-#define __NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
-#define __NR_uname 189 /* Linux Specific */
-#define __NR_init_module 190 /* Linux Specific */
-#define __NR_personality 191 /* Linux Specific */
-#define __NR_remap_file_pages 192 /* Linux Specific */
-#define __NR_epoll_create 193 /* Linux Specific */
-#define __NR_epoll_ctl 194 /* Linux Specific */
-#define __NR_epoll_wait 195 /* Linux Specific */
-#define __NR_ioprio_set 196 /* Linux Specific */
-#define __NR_getppid 197 /* Linux Specific */
-#define __NR_sigaction 198 /* Linux Specific */
-#define __NR_sgetmask 199 /* Linux Specific */
-#define __NR_ssetmask 200 /* Linux Specific */
-#define __NR_sigsuspend 201 /* Linux Specific */
-#define __NR_oldlstat 202 /* Linux Specific */
-#define __NR_uselib 203 /* Linux Specific */
-#define __NR_readdir 204 /* Linux Specific */
-#define __NR_readahead 205 /* Linux Specific */
-#define __NR_socketcall 206 /* Linux Specific */
-#define __NR_syslog 207 /* Linux Specific */
-#define __NR_lookup_dcookie 208 /* Linux Specific */
-#define __NR_fadvise64 209 /* Linux Specific */
-#define __NR_fadvise64_64 210 /* Linux Specific */
-#define __NR_tgkill 211 /* Linux Specific */
-#define __NR_waitpid 212 /* Linux Specific */
-#define __NR_swapoff 213 /* Linux Specific */
-#define __NR_sysinfo 214 /* Linux Specific */
-#define __NR_ipc 215 /* Linux Specific */
-#define __NR_sigreturn 216 /* Linux Specific */
-#define __NR_clone 217 /* Linux Specific */
-#define __NR_ioprio_get 218 /* Linux Specific */
-#define __NR_adjtimex 219 /* Linux Specific */
-#define __NR_sigprocmask 220 /* Linux Specific */
-#define __NR_create_module 221 /* Linux Specific */
-#define __NR_delete_module 222 /* Linux Specific */
-#define __NR_get_kernel_syms 223 /* Linux Specific */
-#define __NR_getpgid 224 /* Linux Specific */
-#define __NR_bdflush 225 /* Linux Specific */
-#define __NR_sysfs 226 /* Linux Specific */
-#define __NR_afs_syscall 227 /* Linux Specific */
-#define __NR_setfsuid 228 /* Linux Specific */
-#define __NR_setfsgid 229 /* Linux Specific */
-#define __NR__newselect 230 /* Linux Specific */
-#ifdef __KERNEL__
-#define __NR_time 231 /* Linux sparc32 */
-#endif
-#define __NR_splice 232 /* Linux Specific */
-#define __NR_stime 233 /* Linux Specific */
-#define __NR_statfs64 234 /* Linux Specific */
-#define __NR_fstatfs64 235 /* Linux Specific */
-#define __NR__llseek 236 /* Linux Specific */
-#define __NR_mlock 237
-#define __NR_munlock 238
-#define __NR_mlockall 239
-#define __NR_munlockall 240
-#define __NR_sched_setparam 241
-#define __NR_sched_getparam 242
-#define __NR_sched_setscheduler 243
-#define __NR_sched_getscheduler 244
-#define __NR_sched_yield 245
-#define __NR_sched_get_priority_max 246
-#define __NR_sched_get_priority_min 247
-#define __NR_sched_rr_get_interval 248
-#define __NR_nanosleep 249
-#define __NR_mremap 250
-#define __NR__sysctl 251
-#define __NR_getsid 252
-#define __NR_fdatasync 253
-#define __NR_nfsservctl 254
-#define __NR_sync_file_range 255
-#define __NR_clock_settime 256
-#define __NR_clock_gettime 257
-#define __NR_clock_getres 258
-#define __NR_clock_nanosleep 259
-#define __NR_sched_getaffinity 260
-#define __NR_sched_setaffinity 261
-#define __NR_timer_settime 262
-#define __NR_timer_gettime 263
-#define __NR_timer_getoverrun 264
-#define __NR_timer_delete 265
-#define __NR_timer_create 266
-/* #define __NR_vserver 267 Reserved for VSERVER */
-#define __NR_io_setup 268
-#define __NR_io_destroy 269
-#define __NR_io_submit 270
-#define __NR_io_cancel 271
-#define __NR_io_getevents 272
-#define __NR_mq_open 273
-#define __NR_mq_unlink 274
-#define __NR_mq_timedsend 275
-#define __NR_mq_timedreceive 276
-#define __NR_mq_notify 277
-#define __NR_mq_getsetattr 278
-#define __NR_waitid 279
-#define __NR_tee 280
-#define __NR_add_key 281
-#define __NR_request_key 282
-#define __NR_keyctl 283
-#define __NR_openat 284
-#define __NR_mkdirat 285
-#define __NR_mknodat 286
-#define __NR_fchownat 287
-#define __NR_futimesat 288
-#define __NR_fstatat64 289
-#define __NR_unlinkat 290
-#define __NR_renameat 291
-#define __NR_linkat 292
-#define __NR_symlinkat 293
-#define __NR_readlinkat 294
-#define __NR_fchmodat 295
-#define __NR_faccessat 296
-#define __NR_pselect6 297
-#define __NR_ppoll 298
-#define __NR_unshare 299
-#define __NR_set_robust_list 300
-#define __NR_get_robust_list 301
-#define __NR_migrate_pages 302
-#define __NR_mbind 303
-#define __NR_get_mempolicy 304
-#define __NR_set_mempolicy 305
-#define __NR_kexec_load 306
-#define __NR_move_pages 307
-#define __NR_getcpu 308
-#define __NR_epoll_pwait 309
-#define __NR_utimensat 310
-#define __NR_signalfd 311
-#define __NR_timerfd_create 312
-#define __NR_eventfd 313
-#define __NR_fallocate 314
-#define __NR_timerfd_settime 315
-#define __NR_timerfd_gettime 316
-#define __NR_signalfd4 317
-#define __NR_eventfd2 318
-#define __NR_epoll_create1 319
-#define __NR_dup3 320
-#define __NR_pipe2 321
-#define __NR_inotify_init1 322
-#define __NR_accept4 323
-
-#define NR_SYSCALLS 324
-
-#ifdef __KERNEL__
-#define __ARCH_WANT_IPC_PARSE_VERSION
-#define __ARCH_WANT_OLD_READDIR
-#define __ARCH_WANT_STAT64
-#define __ARCH_WANT_SYS_ALARM
-#define __ARCH_WANT_SYS_GETHOSTNAME
-#define __ARCH_WANT_SYS_PAUSE
-#define __ARCH_WANT_SYS_SGETMASK
-#define __ARCH_WANT_SYS_SIGNAL
-#define __ARCH_WANT_SYS_TIME
-#define __ARCH_WANT_COMPAT_SYS_TIME
-#define __ARCH_WANT_SYS_UTIME
-#define __ARCH_WANT_SYS_WAITPID
-#define __ARCH_WANT_SYS_SOCKETCALL
-#define __ARCH_WANT_SYS_FADVISE64
-#define __ARCH_WANT_SYS_GETPGRP
-#define __ARCH_WANT_SYS_LLSEEK
-#define __ARCH_WANT_SYS_NICE
-#define __ARCH_WANT_SYS_OLDUMOUNT
-#define __ARCH_WANT_SYS_SIGPENDING
-#define __ARCH_WANT_SYS_SIGPROCMASK
-#define __ARCH_WANT_SYS_RT_SIGSUSPEND
-#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
-
-/*
- * "Conditional" syscalls
- *
- * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
- * but it doesn't work on all toolchains, so we just do it by hand
- */
-#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
-
-#endif /* __KERNEL__ */
-#endif /* _SPARC64_UNISTD_H */
diff --git a/arch/sparc/kernel/.gitignore b/arch/sparc/kernel/.gitignore
new file mode 100644
index 000000000000..c5f676c3c224
--- /dev/null
+++ b/arch/sparc/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 2d6582095099..53adcaa0348b 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -2,25 +2,98 @@
# Makefile for the linux kernel.
#
-extra-y := head.o init_task.o vmlinux.lds
-
-EXTRA_AFLAGS := -ansi
-
-IRQ_OBJS := irq.o sun4m_irq.o sun4c_irq.o sun4d_irq.o
-obj-y := entry.o wof.o wuf.o etrap.o rtrap.o traps.o $(IRQ_OBJS) \
- process.o signal.o ioport.o setup.o idprom.o \
- sys_sparc.o systbls.o \
- time.o windows.o cpu.o devices.o \
- tadpole.o tick14.o ptrace.o \
- unaligned.o una_asm.o muldiv.o \
- prom.o of_device.o devres.o dma.o
-
-devres-y = ../../../kernel/irq/devres.o
-
-obj-$(CONFIG_PCI) += pcic.o
-obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
-obj-$(CONFIG_SUN_AUXIO) += auxio.o
-obj-$(CONFIG_SUN_PM) += apc.o pmc.o
-obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o
-obj-$(CONFIG_SPARC_LED) += led.o
-obj-$(CONFIG_KGDB) += kgdb.o
+asflags-y := -ansi
+ccflags-y := -Werror
+
+extra-y := head_$(BITS).o
+extra-y += init_task.o
+extra-y += vmlinux.lds
+
+obj-$(CONFIG_SPARC32) += entry.o wof.o wuf.o
+obj-$(CONFIG_SPARC32) += etrap_32.o
+obj-$(CONFIG_SPARC32) += rtrap_32.o
+obj-y += traps_$(BITS).o
+
+# IRQ
+obj-y += irq_$(BITS).o
+obj-$(CONFIG_SPARC32) += sun4m_irq.o sun4c_irq.o sun4d_irq.o
+
+obj-y += process_$(BITS).o
+obj-y += signal_$(BITS).o
+obj-$(CONFIG_SPARC32) += ioport.o
+obj-y += setup_$(BITS).o
+obj-y += idprom.o
+obj-y += sys_sparc_$(BITS).o
+obj-$(CONFIG_SPARC32) += systbls_32.o
+obj-y += time_$(BITS).o
+obj-$(CONFIG_SPARC32) += windows.o
+obj-y += cpu.o
+obj-$(CONFIG_SPARC32) += devices.o
+obj-$(CONFIG_SPARC32) += tadpole.o
+obj-$(CONFIG_SPARC32) += tick14.o
+obj-y += ptrace_$(BITS).o
+obj-y += unaligned_$(BITS).o
+obj-y += una_asm_$(BITS).o
+obj-$(CONFIG_SPARC32) += muldiv.o
+obj-y += prom_common.o
+obj-y += prom_$(BITS).o
+obj-y += of_device_$(BITS).o
+obj-$(CONFIG_SPARC64) += prom_irqtrans.o
+
+obj-$(CONFIG_SPARC64) += reboot.o
+obj-$(CONFIG_SPARC64) += sysfs.o
+obj-$(CONFIG_SPARC64) += iommu.o
+obj-$(CONFIG_SPARC64) += central.o
+obj-$(CONFIG_SPARC64) += starfire.o
+obj-$(CONFIG_SPARC64) += power.o
+obj-$(CONFIG_SPARC64) += sbus.o
+obj-$(CONFIG_SPARC64) += ebus.o
+obj-$(CONFIG_SPARC64) += visemul.o
+obj-$(CONFIG_SPARC64) += hvapi.o
+obj-$(CONFIG_SPARC64) += sstate.o
+obj-$(CONFIG_SPARC64) += mdesc.o
+
+# sparc32 do not use GENERIC_HARDIRQS but uses the generic devres implementation
+obj-$(CONFIG_SPARC32) += devres.o
+devres-y := ../../../kernel/irq/devres.o
+
+obj-$(CONFIG_SPARC32) += dma.o
+
+obj-$(CONFIG_SPARC32_PCI) += pcic.o
+
+obj-$(CONFIG_SMP) += trampoline_$(BITS).o smp_$(BITS).o
+obj-$(CONFIG_SPARC32_SMP) += sun4m_smp.o sun4d_smp.o
+obj-$(CONFIG_SPARC64_SMP) += hvtramp.o
+
+obj-y += auxio_$(BITS).o
+obj-$(CONFIG_SUN_PM) += apc.o pmc.o
+
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULES) += sparc_ksyms_$(BITS).o
+obj-$(CONFIG_SPARC_LED) += led.o
+obj-$(CONFIG_KGDB) += kgdb_$(BITS).o
+
+
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+CFLAGS_REMOVE_ftrace.o := -pg
+
+obj-$(CONFIG_STACKTRACE) += stacktrace.o
+# sparc64 PCI
+obj-$(CONFIG_SPARC64_PCI) += pci.o pci_common.o psycho_common.o
+obj-$(CONFIG_SPARC64_PCI) += pci_psycho.o pci_sabre.o pci_schizo.o
+obj-$(CONFIG_SPARC64_PCI) += pci_sun4v.o pci_sun4v_asm.o pci_fire.o
+obj-$(CONFIG_PCI_MSI) += pci_msi.o
+
+obj-$(CONFIG_COMPAT) += sys32.o sys_sparc32.o signal32.o
+
+# sparc64 cpufreq
+obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
+obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
+obj-$(CONFIG_US3_MC) += chmc.o
+
+obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o
+
+obj-$(CONFIG_AUDIT) += audit.o
+audit--$(CONFIG_AUDIT) := compat_audit.o
+obj-$(CONFIG_COMPAT) += $(audit--y)
diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c
index b5bb99ed892c..68f7e1118e9b 100644
--- a/arch/sparc/kernel/asm-offsets.c
+++ b/arch/sparc/kernel/asm-offsets.c
@@ -14,15 +14,28 @@
// #include <linux/mm.h>
#include <linux/kbuild.h>
-int foo(void)
+#ifdef CONFIG_SPARC32
+int sparc32_foo(void)
{
- DEFINE(AOFF_task_thread, offsetof(struct task_struct, thread));
- BLANK();
DEFINE(AOFF_thread_fork_kpsr,
offsetof(struct thread_struct, fork_kpsr));
+ return 0;
+}
+#else
+int sparc64_foo(void)
+{
+ return 0;
+}
+#endif
+
+int foo(void)
+{
+ BLANK();
+ DEFINE(AOFF_task_thread, offsetof(struct task_struct, thread));
BLANK();
DEFINE(AOFF_mm_context, offsetof(struct mm_struct, context));
/* DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); */
return 0;
}
+
diff --git a/arch/sparc64/kernel/audit.c b/arch/sparc/kernel/audit.c
index 8fff0ac63d56..8fff0ac63d56 100644
--- a/arch/sparc64/kernel/audit.c
+++ b/arch/sparc/kernel/audit.c
diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio_32.c
index 09c857215a52..09c857215a52 100644
--- a/arch/sparc/kernel/auxio.c
+++ b/arch/sparc/kernel/auxio_32.c
diff --git a/arch/sparc/kernel/auxio_64.c b/arch/sparc/kernel/auxio_64.c
new file mode 100644
index 000000000000..8b67347d4221
--- /dev/null
+++ b/arch/sparc/kernel/auxio_64.c
@@ -0,0 +1,149 @@
+/* auxio.c: Probing for the Sparc AUXIO register at boot time.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Refactoring for unified NCR/PCIO support 2002 Eric Brower (ebrower@usa.net)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/auxio.h>
+
+void __iomem *auxio_register = NULL;
+EXPORT_SYMBOL(auxio_register);
+
+enum auxio_type {
+ AUXIO_TYPE_NODEV,
+ AUXIO_TYPE_SBUS,
+ AUXIO_TYPE_EBUS
+};
+
+static enum auxio_type auxio_devtype = AUXIO_TYPE_NODEV;
+static DEFINE_SPINLOCK(auxio_lock);
+
+static void __auxio_rmw(u8 bits_on, u8 bits_off, int ebus)
+{
+ if (auxio_register) {
+ unsigned long flags;
+ u8 regval, newval;
+
+ spin_lock_irqsave(&auxio_lock, flags);
+
+ regval = (ebus ?
+ (u8) readl(auxio_register) :
+ sbus_readb(auxio_register));
+ newval = regval | bits_on;
+ newval &= ~bits_off;
+ if (!ebus)
+ newval &= ~AUXIO_AUX1_MASK;
+ if (ebus)
+ writel((u32) newval, auxio_register);
+ else
+ sbus_writeb(newval, auxio_register);
+
+ spin_unlock_irqrestore(&auxio_lock, flags);
+ }
+}
+
+static void __auxio_set_bit(u8 bit, int on, int ebus)
+{
+ u8 bits_on = (ebus ? AUXIO_PCIO_LED : AUXIO_AUX1_LED);
+ u8 bits_off = 0;
+
+ if (!on) {
+ u8 tmp = bits_off;
+ bits_off = bits_on;
+ bits_on = tmp;
+ }
+ __auxio_rmw(bits_on, bits_off, ebus);
+}
+
+void auxio_set_led(int on)
+{
+ int ebus = auxio_devtype == AUXIO_TYPE_EBUS;
+ u8 bit;
+
+ bit = (ebus ? AUXIO_PCIO_LED : AUXIO_AUX1_LED);
+ __auxio_set_bit(bit, on, ebus);
+}
+
+static void __auxio_sbus_set_lte(int on)
+{
+ __auxio_set_bit(AUXIO_AUX1_LTE, on, 0);
+}
+
+void auxio_set_lte(int on)
+{
+ switch(auxio_devtype) {
+ case AUXIO_TYPE_SBUS:
+ __auxio_sbus_set_lte(on);
+ break;
+ case AUXIO_TYPE_EBUS:
+ /* FALL-THROUGH */
+ default:
+ break;
+ }
+}
+
+static struct of_device_id __initdata auxio_match[] = {
+ {
+ .name = "auxio",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, auxio_match);
+
+static int __devinit auxio_probe(struct of_device *dev, const struct of_device_id *match)
+{
+ struct device_node *dp = dev->node;
+ unsigned long size;
+
+ if (!strcmp(dp->parent->name, "ebus")) {
+ auxio_devtype = AUXIO_TYPE_EBUS;
+ size = sizeof(u32);
+ } else if (!strcmp(dp->parent->name, "sbus")) {
+ auxio_devtype = AUXIO_TYPE_SBUS;
+ size = 1;
+ } else {
+ printk("auxio: Unknown parent bus type [%s]\n",
+ dp->parent->name);
+ return -ENODEV;
+ }
+ auxio_register = of_ioremap(&dev->resource[0], 0, size, "auxio");
+ if (!auxio_register)
+ return -ENODEV;
+
+ printk(KERN_INFO "AUXIO: Found device at %s\n",
+ dp->full_name);
+
+ if (auxio_devtype == AUXIO_TYPE_EBUS)
+ auxio_set_led(AUXIO_LED_ON);
+
+ return 0;
+}
+
+static struct of_platform_driver auxio_driver = {
+ .match_table = auxio_match,
+ .probe = auxio_probe,
+ .driver = {
+ .name = "auxio",
+ },
+};
+
+static int __init auxio_init(void)
+{
+ return of_register_driver(&auxio_driver, &of_platform_bus_type);
+}
+
+/* Must be after subsys_initcall() so that busses are probed. Must
+ * be before device_initcall() because things like the floppy driver
+ * need to use the AUXIO register.
+ */
+fs_initcall(auxio_init);
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc/kernel/central.c
index 05f1c916db06..05f1c916db06 100644
--- a/arch/sparc64/kernel/central.c
+++ b/arch/sparc/kernel/central.c
diff --git a/arch/sparc/kernel/cherrs.S b/arch/sparc/kernel/cherrs.S
new file mode 100644
index 000000000000..4ee1ad420862
--- /dev/null
+++ b/arch/sparc/kernel/cherrs.S
@@ -0,0 +1,579 @@
+ /* These get patched into the trap table at boot time
+ * once we know we have a cheetah processor.
+ */
+ .globl cheetah_fecc_trap_vector
+ .type cheetah_fecc_trap_vector,#function
+cheetah_fecc_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_DC | DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_fast_ecc), %g2
+ jmpl %g2 + %lo(cheetah_fast_ecc), %g0
+ mov 0, %g1
+ .size cheetah_fecc_trap_vector,.-cheetah_fecc_trap_vector
+
+ .globl cheetah_fecc_trap_vector_tl1
+ .type cheetah_fecc_trap_vector_tl1,#function
+cheetah_fecc_trap_vector_tl1:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_DC | DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_fast_ecc), %g2
+ jmpl %g2 + %lo(cheetah_fast_ecc), %g0
+ mov 1, %g1
+ .size cheetah_fecc_trap_vector_tl1,.-cheetah_fecc_trap_vector_tl1
+
+ .globl cheetah_cee_trap_vector
+ .type cheetah_cee_trap_vector,#function
+cheetah_cee_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_cee), %g2
+ jmpl %g2 + %lo(cheetah_cee), %g0
+ mov 0, %g1
+ .size cheetah_cee_trap_vector,.-cheetah_cee_trap_vector
+
+ .globl cheetah_cee_trap_vector_tl1
+ .type cheetah_cee_trap_vector_tl1,#function
+cheetah_cee_trap_vector_tl1:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ andn %g1, DCU_IC, %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ sethi %hi(cheetah_cee), %g2
+ jmpl %g2 + %lo(cheetah_cee), %g0
+ mov 1, %g1
+ .size cheetah_cee_trap_vector_tl1,.-cheetah_cee_trap_vector_tl1
+
+ .globl cheetah_deferred_trap_vector
+ .type cheetah_deferred_trap_vector,#function
+cheetah_deferred_trap_vector:
+ membar #Sync
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
+ andn %g1, DCU_DC | DCU_IC, %g1;
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
+ membar #Sync;
+ sethi %hi(cheetah_deferred_trap), %g2
+ jmpl %g2 + %lo(cheetah_deferred_trap), %g0
+ mov 0, %g1
+ .size cheetah_deferred_trap_vector,.-cheetah_deferred_trap_vector
+
+ .globl cheetah_deferred_trap_vector_tl1
+ .type cheetah_deferred_trap_vector_tl1,#function
+cheetah_deferred_trap_vector_tl1:
+ membar #Sync;
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
+ andn %g1, DCU_DC | DCU_IC, %g1;
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
+ membar #Sync;
+ sethi %hi(cheetah_deferred_trap), %g2
+ jmpl %g2 + %lo(cheetah_deferred_trap), %g0
+ mov 1, %g1
+ .size cheetah_deferred_trap_vector_tl1,.-cheetah_deferred_trap_vector_tl1
+
+ /* Cheetah+ specific traps. These are for the new I/D cache parity
+ * error traps. The first argument to cheetah_plus_parity_handler
+ * is encoded as follows:
+ *
+ * Bit0: 0=dcache,1=icache
+ * Bit1: 0=recoverable,1=unrecoverable
+ */
+ .globl cheetah_plus_dcpe_trap_vector
+ .type cheetah_plus_dcpe_trap_vector,#function
+cheetah_plus_dcpe_trap_vector:
+ membar #Sync
+ sethi %hi(do_cheetah_plus_data_parity), %g7
+ jmpl %g7 + %lo(do_cheetah_plus_data_parity), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ .size cheetah_plus_dcpe_trap_vector,.-cheetah_plus_dcpe_trap_vector
+
+ .type do_cheetah_plus_data_parity,#function
+do_cheetah_plus_data_parity:
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov 0x0, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,a,pt %xcc, rtrap_irq
+ .size do_cheetah_plus_data_parity,.-do_cheetah_plus_data_parity
+
+ .globl cheetah_plus_dcpe_trap_vector_tl1
+ .type cheetah_plus_dcpe_trap_vector_tl1,#function
+cheetah_plus_dcpe_trap_vector_tl1:
+ membar #Sync
+ wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
+ sethi %hi(do_dcpe_tl1), %g3
+ jmpl %g3 + %lo(do_dcpe_tl1), %g0
+ nop
+ nop
+ nop
+ nop
+ .size cheetah_plus_dcpe_trap_vector_tl1,.-cheetah_plus_dcpe_trap_vector_tl1
+
+ .globl cheetah_plus_icpe_trap_vector
+ .type cheetah_plus_icpe_trap_vector,#function
+cheetah_plus_icpe_trap_vector:
+ membar #Sync
+ sethi %hi(do_cheetah_plus_insn_parity), %g7
+ jmpl %g7 + %lo(do_cheetah_plus_insn_parity), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ .size cheetah_plus_icpe_trap_vector,.-cheetah_plus_icpe_trap_vector
+
+ .type do_cheetah_plus_insn_parity,#function
+do_cheetah_plus_insn_parity:
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov 0x1, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,a,pt %xcc, rtrap_irq
+ .size do_cheetah_plus_insn_parity,.-do_cheetah_plus_insn_parity
+
+ .globl cheetah_plus_icpe_trap_vector_tl1
+ .type cheetah_plus_icpe_trap_vector_tl1,#function
+cheetah_plus_icpe_trap_vector_tl1:
+ membar #Sync
+ wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
+ sethi %hi(do_icpe_tl1), %g3
+ jmpl %g3 + %lo(do_icpe_tl1), %g0
+ nop
+ nop
+ nop
+ nop
+ .size cheetah_plus_icpe_trap_vector_tl1,.-cheetah_plus_icpe_trap_vector_tl1
+
+ /* If we take one of these traps when tl >= 1, then we
+ * jump to interrupt globals. If some trap level above us
+ * was also using interrupt globals, we cannot recover.
+ * We may use all interrupt global registers except %g6.
+ */
+ .globl do_dcpe_tl1
+ .type do_dcpe_tl1,#function
+do_dcpe_tl1:
+ rdpr %tl, %g1 ! Save original trap level
+ mov 1, %g2 ! Setup TSTATE checking loop
+ sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
+1: wrpr %g2, %tl ! Set trap level to check
+ rdpr %tstate, %g4 ! Read TSTATE for this level
+ andcc %g4, %g3, %g0 ! Interrupt globals in use?
+ bne,a,pn %xcc, do_dcpe_tl1_fatal ! Yep, irrecoverable
+ wrpr %g1, %tl ! Restore original trap level
+ add %g2, 1, %g2 ! Next trap level
+ cmp %g2, %g1 ! Hit them all yet?
+ ble,pt %icc, 1b ! Not yet
+ nop
+ wrpr %g1, %tl ! Restore original trap level
+do_dcpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
+ sethi %hi(dcache_parity_tl1_occurred), %g2
+ lduw [%g2 + %lo(dcache_parity_tl1_occurred)], %g1
+ add %g1, 1, %g1
+ stw %g1, [%g2 + %lo(dcache_parity_tl1_occurred)]
+ /* Reset D-cache parity */
+ sethi %hi(1 << 16), %g1 ! D-cache size
+ mov (1 << 5), %g2 ! D-cache line size
+ sub %g1, %g2, %g1 ! Move down 1 cacheline
+1: srl %g1, 14, %g3 ! Compute UTAG
+ membar #Sync
+ stxa %g3, [%g1] ASI_DCACHE_UTAG
+ membar #Sync
+ sub %g2, 8, %g3 ! 64-bit data word within line
+2: membar #Sync
+ stxa %g0, [%g1 + %g3] ASI_DCACHE_DATA
+ membar #Sync
+ subcc %g3, 8, %g3 ! Next 64-bit data word
+ bge,pt %icc, 2b
+ nop
+ subcc %g1, %g2, %g1 ! Next cacheline
+ bge,pt %icc, 1b
+ nop
+ ba,pt %xcc, dcpe_icpe_tl1_common
+ nop
+
+do_dcpe_tl1_fatal:
+ sethi %hi(1f), %g7
+ ba,pt %xcc, etraptl1
+1: or %g7, %lo(1b), %g7
+ mov 0x2, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ nop
+ .size do_dcpe_tl1,.-do_dcpe_tl1
+
+ .globl do_icpe_tl1
+ .type do_icpe_tl1,#function
+do_icpe_tl1:
+ rdpr %tl, %g1 ! Save original trap level
+ mov 1, %g2 ! Setup TSTATE checking loop
+ sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
+1: wrpr %g2, %tl ! Set trap level to check
+ rdpr %tstate, %g4 ! Read TSTATE for this level
+ andcc %g4, %g3, %g0 ! Interrupt globals in use?
+ bne,a,pn %xcc, do_icpe_tl1_fatal ! Yep, irrecoverable
+ wrpr %g1, %tl ! Restore original trap level
+ add %g2, 1, %g2 ! Next trap level
+ cmp %g2, %g1 ! Hit them all yet?
+ ble,pt %icc, 1b ! Not yet
+ nop
+ wrpr %g1, %tl ! Restore original trap level
+do_icpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
+ sethi %hi(icache_parity_tl1_occurred), %g2
+ lduw [%g2 + %lo(icache_parity_tl1_occurred)], %g1
+ add %g1, 1, %g1
+ stw %g1, [%g2 + %lo(icache_parity_tl1_occurred)]
+ /* Flush I-cache */
+ sethi %hi(1 << 15), %g1 ! I-cache size
+ mov (1 << 5), %g2 ! I-cache line size
+ sub %g1, %g2, %g1
+1: or %g1, (2 << 3), %g3
+ stxa %g0, [%g3] ASI_IC_TAG
+ membar #Sync
+ subcc %g1, %g2, %g1
+ bge,pt %icc, 1b
+ nop
+ ba,pt %xcc, dcpe_icpe_tl1_common
+ nop
+
+do_icpe_tl1_fatal:
+ sethi %hi(1f), %g7
+ ba,pt %xcc, etraptl1
+1: or %g7, %lo(1b), %g7
+ mov 0x3, %o0
+ call cheetah_plus_parity_error
+ add %sp, PTREGS_OFF, %o1
+ ba,pt %xcc, rtrap
+ nop
+ .size do_icpe_tl1,.-do_icpe_tl1
+
+ .type dcpe_icpe_tl1_common,#function
+dcpe_icpe_tl1_common:
+ /* Flush D-cache, re-enable D/I caches in DCU and finally
+ * retry the trapping instruction.
+ */
+ sethi %hi(1 << 16), %g1 ! D-cache size
+ mov (1 << 5), %g2 ! D-cache line size
+ sub %g1, %g2, %g1
+1: stxa %g0, [%g1] ASI_DCACHE_TAG
+ membar #Sync
+ subcc %g1, %g2, %g1
+ bge,pt %icc, 1b
+ nop
+ ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
+ or %g1, (DCU_DC | DCU_IC), %g1
+ stxa %g1, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ retry
+ .size dcpe_icpe_tl1_common,.-dcpe_icpe_tl1_common
+
+ /* Capture I/D/E-cache state into per-cpu error scoreboard.
+ *
+ * %g1: (TL>=0) ? 1 : 0
+ * %g2: scratch
+ * %g3: scratch
+ * %g4: AFSR
+ * %g5: AFAR
+ * %g6: unused, will have current thread ptr after etrap
+ * %g7: scratch
+ */
+ .type __cheetah_log_error,#function
+__cheetah_log_error:
+ /* Put "TL1" software bit into AFSR. */
+ and %g1, 0x1, %g1
+ sllx %g1, 63, %g2
+ or %g4, %g2, %g4
+
+ /* Get log entry pointer for this cpu at this trap level. */
+ BRANCH_IF_JALAPENO(g2,g3,50f)
+ ldxa [%g0] ASI_SAFARI_CONFIG, %g2
+ srlx %g2, 17, %g2
+ ba,pt %xcc, 60f
+ and %g2, 0x3ff, %g2
+
+50: ldxa [%g0] ASI_JBUS_CONFIG, %g2
+ srlx %g2, 17, %g2
+ and %g2, 0x1f, %g2
+
+60: sllx %g2, 9, %g2
+ sethi %hi(cheetah_error_log), %g3
+ ldx [%g3 + %lo(cheetah_error_log)], %g3
+ brz,pn %g3, 80f
+ nop
+
+ add %g3, %g2, %g3
+ sllx %g1, 8, %g1
+ add %g3, %g1, %g1
+
+ /* %g1 holds pointer to the top of the logging scoreboard */
+ ldx [%g1 + 0x0], %g7
+ cmp %g7, -1
+ bne,pn %xcc, 80f
+ nop
+
+ stx %g4, [%g1 + 0x0]
+ stx %g5, [%g1 + 0x8]
+ add %g1, 0x10, %g1
+
+ /* %g1 now points to D-cache logging area */
+ set 0x3ff8, %g2 /* DC_addr mask */
+ and %g5, %g2, %g2 /* DC_addr bits of AFAR */
+ srlx %g5, 12, %g3
+ or %g3, 1, %g3 /* PHYS tag + valid */
+
+10: ldxa [%g2] ASI_DCACHE_TAG, %g7
+ cmp %g3, %g7 /* TAG match? */
+ bne,pt %xcc, 13f
+ nop
+
+ /* Yep, what we want, capture state. */
+ stx %g2, [%g1 + 0x20]
+ stx %g7, [%g1 + 0x28]
+
+ /* A membar Sync is required before and after utag access. */
+ membar #Sync
+ ldxa [%g2] ASI_DCACHE_UTAG, %g7
+ membar #Sync
+ stx %g7, [%g1 + 0x30]
+ ldxa [%g2] ASI_DCACHE_SNOOP_TAG, %g7
+ stx %g7, [%g1 + 0x38]
+ clr %g3
+
+12: ldxa [%g2 + %g3] ASI_DCACHE_DATA, %g7
+ stx %g7, [%g1]
+ add %g3, (1 << 5), %g3
+ cmp %g3, (4 << 5)
+ bl,pt %xcc, 12b
+ add %g1, 0x8, %g1
+
+ ba,pt %xcc, 20f
+ add %g1, 0x20, %g1
+
+13: sethi %hi(1 << 14), %g7
+ add %g2, %g7, %g2
+ srlx %g2, 14, %g7
+ cmp %g7, 4
+ bl,pt %xcc, 10b
+ nop
+
+ add %g1, 0x40, %g1
+
+ /* %g1 now points to I-cache logging area */
+20: set 0x1fe0, %g2 /* IC_addr mask */
+ and %g5, %g2, %g2 /* IC_addr bits of AFAR */
+ sllx %g2, 1, %g2 /* IC_addr[13:6]==VA[12:5] */
+ srlx %g5, (13 - 8), %g3 /* Make PTAG */
+ andn %g3, 0xff, %g3 /* Mask off undefined bits */
+
+21: ldxa [%g2] ASI_IC_TAG, %g7
+ andn %g7, 0xff, %g7
+ cmp %g3, %g7
+ bne,pt %xcc, 23f
+ nop
+
+ /* Yep, what we want, capture state. */
+ stx %g2, [%g1 + 0x40]
+ stx %g7, [%g1 + 0x48]
+ add %g2, (1 << 3), %g2
+ ldxa [%g2] ASI_IC_TAG, %g7
+ add %g2, (1 << 3), %g2
+ stx %g7, [%g1 + 0x50]
+ ldxa [%g2] ASI_IC_TAG, %g7
+ add %g2, (1 << 3), %g2
+ stx %g7, [%g1 + 0x60]
+ ldxa [%g2] ASI_IC_TAG, %g7
+ stx %g7, [%g1 + 0x68]
+ sub %g2, (3 << 3), %g2
+ ldxa [%g2] ASI_IC_STAG, %g7
+ stx %g7, [%g1 + 0x58]
+ clr %g3
+ srlx %g2, 2, %g2
+
+22: ldxa [%g2 + %g3] ASI_IC_INSTR, %g7
+ stx %g7, [%g1]
+ add %g3, (1 << 3), %g3
+ cmp %g3, (8 << 3)
+ bl,pt %xcc, 22b
+ add %g1, 0x8, %g1
+
+ ba,pt %xcc, 30f
+ add %g1, 0x30, %g1
+
+23: sethi %hi(1 << 14), %g7
+ add %g2, %g7, %g2
+ srlx %g2, 14, %g7
+ cmp %g7, 4
+ bl,pt %xcc, 21b
+ nop
+
+ add %g1, 0x70, %g1
+
+ /* %g1 now points to E-cache logging area */
+30: andn %g5, (32 - 1), %g2
+ stx %g2, [%g1 + 0x20]
+ ldxa [%g2] ASI_EC_TAG_DATA, %g7
+ stx %g7, [%g1 + 0x28]
+ ldxa [%g2] ASI_EC_R, %g0
+ clr %g3
+
+31: ldxa [%g3] ASI_EC_DATA, %g7
+ stx %g7, [%g1 + %g3]
+ add %g3, 0x8, %g3
+ cmp %g3, 0x20
+
+ bl,pt %xcc, 31b
+ nop
+80:
+ rdpr %tt, %g2
+ cmp %g2, 0x70
+ be c_fast_ecc
+ cmp %g2, 0x63
+ be c_cee
+ nop
+ ba,pt %xcc, c_deferred
+ .size __cheetah_log_error,.-__cheetah_log_error
+
+ /* Cheetah FECC trap handling, we get here from tl{0,1}_fecc
+ * in the trap table. That code has done a memory barrier
+ * and has disabled both the I-cache and D-cache in the DCU
+ * control register. The I-cache is disabled so that we may
+ * capture the corrupted cache line, and the D-cache is disabled
+ * because corrupt data may have been placed there and we don't
+ * want to reference it.
+ *
+ * %g1 is one if this trap occurred at %tl >= 1.
+ *
+ * Next, we turn off error reporting so that we don't recurse.
+ */
+ .globl cheetah_fast_ecc
+ .type cheetah_fast_ecc,#function
+cheetah_fast_ecc:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ ba,pt %xcc, __cheetah_log_error
+ nop
+ .size cheetah_fast_ecc,.-cheetah_fast_ecc
+
+ .type c_fast_ecc,#function
+c_fast_ecc:
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_fecc_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+ .size c_fast_ecc,.-c_fast_ecc
+
+ /* Our caller has disabled I-cache and performed membar Sync. */
+ .globl cheetah_cee
+ .type cheetah_cee,#function
+cheetah_cee:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ ba,pt %xcc, __cheetah_log_error
+ nop
+ .size cheetah_cee,.-cheetah_cee
+
+ .type c_cee,#function
+c_cee:
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_cee_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+ .size c_cee,.-c_cee
+
+ /* Our caller has disabled I-cache+D-cache and performed membar Sync. */
+ .globl cheetah_deferred_trap
+ .type cheetah_deferred_trap,#function
+cheetah_deferred_trap:
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
+ andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
+ stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Fetch and clear AFSR/AFAR */
+ ldxa [%g0] ASI_AFSR, %g4
+ ldxa [%g0] ASI_AFAR, %g5
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ ba,pt %xcc, __cheetah_log_error
+ nop
+ .size cheetah_deferred_trap,.-cheetah_deferred_trap
+
+ .type c_deferred,#function
+c_deferred:
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov %l4, %o1
+ mov %l5, %o2
+ call cheetah_deferred_handler
+ add %sp, PTREGS_OFF, %o0
+ ba,a,pt %xcc, rtrap_irq
+ .size c_deferred,.-c_deferred
diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc/kernel/chmc.c
index 3b9f4d6e14a9..3b9f4d6e14a9 100644
--- a/arch/sparc64/kernel/chmc.c
+++ b/arch/sparc/kernel/chmc.c
diff --git a/arch/sparc/kernel/compat_audit.c b/arch/sparc/kernel/compat_audit.c
new file mode 100644
index 000000000000..d865575b25bf
--- /dev/null
+++ b/arch/sparc/kernel/compat_audit.c
@@ -0,0 +1,43 @@
+#define __32bit_syscall_numbers__
+#include <asm/unistd.h>
+
+unsigned sparc32_dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+unsigned sparc32_chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+unsigned sparc32_write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+unsigned sparc32_read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+unsigned sparc32_signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int sparc32_classify_syscall(unsigned syscall)
+{
+ switch(syscall) {
+ case __NR_open:
+ return 2;
+ case __NR_openat:
+ return 3;
+ case __NR_socketcall:
+ return 4;
+ case __NR_execve:
+ return 5;
+ default:
+ return 1;
+ }
+}
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index 1fc17f59c6bf..6c2da2420f76 100644
--- a/arch/sparc/kernel/cpu.c
+++ b/arch/sparc/kernel/cpu.c
@@ -8,6 +8,8 @@
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/threads.h>
+
+#include <asm/spitfire.h>
#include <asm/oplib.h>
#include <asm/page.h>
#include <asm/head.h>
@@ -15,153 +17,322 @@
#include <asm/mbus.h>
#include <asm/cpudata.h>
+#include "kernel.h"
+
DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 };
-struct cpu_iu_info {
- int psr_impl;
- int psr_vers;
- char* cpu_name; /* should be enough I hope... */
+struct cpu_info {
+ int psr_vers;
+ const char *name;
+};
+
+struct fpu_info {
+ int fp_vers;
+ const char *name;
};
-struct cpu_fp_info {
- int psr_impl;
- int fp_vers;
- char* fp_name;
+#define NOCPU 8
+#define NOFPU 8
+
+struct manufacturer_info {
+ int psr_impl;
+ struct cpu_info cpu_info[NOCPU];
+ struct fpu_info fpu_info[NOFPU];
};
+#define CPU(ver, _name) \
+{ .psr_vers = ver, .name = _name }
+
+#define FPU(ver, _name) \
+{ .fp_vers = ver, .name = _name }
+
+static const struct manufacturer_info __initconst manufacturer_info[] = {
+{
+ 0,
+ /* Sun4/100, 4/200, SLC */
+ .cpu_info = {
+ CPU(0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"),
+ /* borned STP1012PGA */
+ CPU(4, "Fujitsu MB86904"),
+ CPU(5, "Fujitsu TurboSparc MB86907"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0, "Fujitsu MB86910 or Weitek WTL1164/5"),
+ FPU(1, "Fujitsu MB86911 or Weitek WTL1164/5 or LSI L64831"),
+ FPU(2, "LSI Logic L64802 or Texas Instruments ACT8847"),
+ /* SparcStation SLC, SparcStation1 */
+ FPU(3, "Weitek WTL3170/2"),
+ /* SPARCstation-5 */
+ FPU(4, "Lsi Logic/Meiko L64804 or compatible"),
+ FPU(-1, NULL)
+ }
+},{
+ 1,
+ .cpu_info = {
+ /* SparcStation2, SparcServer 490 & 690 */
+ CPU(0, "LSI Logic Corporation - L64811"),
+ /* SparcStation2 */
+ CPU(1, "Cypress/ROSS CY7C601"),
+ /* Embedded controller */
+ CPU(3, "Cypress/ROSS CY7C611"),
+ /* Ross Technologies HyperSparc */
+ CPU(0xf, "ROSS HyperSparc RT620"),
+ CPU(0xe, "ROSS HyperSparc RT625 or RT626"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0, "ROSS HyperSparc combined IU/FPU"),
+ FPU(1, "Lsi Logic L64814"),
+ FPU(2, "Texas Instruments TMS390-C602A"),
+ FPU(3, "Cypress CY7C602 FPU"),
+ FPU(-1, NULL)
+ }
+},{
+ 2,
+ .cpu_info = {
+ /* ECL Implementation, CRAY S-MP Supercomputer... AIEEE! */
+ /* Someone please write the code to support this beast! ;) */
+ CPU(0, "Bipolar Integrated Technology - B5010"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(-1, NULL)
+ }
+},{
+ 3,
+ .cpu_info = {
+ CPU(0, "LSI Logic Corporation - unknown-type"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(-1, NULL)
+ }
+},{
+ 4,
+ .cpu_info = {
+ CPU(0, "Texas Instruments, Inc. - SuperSparc-(II)"),
+ /* SparcClassic -- borned STP1010TAB-50*/
+ CPU(1, "Texas Instruments, Inc. - MicroSparc"),
+ CPU(2, "Texas Instruments, Inc. - MicroSparc II"),
+ CPU(3, "Texas Instruments, Inc. - SuperSparc 51"),
+ CPU(4, "Texas Instruments, Inc. - SuperSparc 61"),
+ CPU(5, "Texas Instruments, Inc. - unknown"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ /* SuperSparc 50 module */
+ FPU(0, "SuperSparc on-chip FPU"),
+ /* SparcClassic */
+ FPU(4, "TI MicroSparc on chip FPU"),
+ FPU(-1, NULL)
+ }
+},{
+ 5,
+ .cpu_info = {
+ CPU(0, "Matsushita - MN10501"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0, "Matsushita MN10501"),
+ FPU(-1, NULL)
+ }
+},{
+ 6,
+ .cpu_info = {
+ CPU(0, "Philips Corporation - unknown"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(-1, NULL)
+ }
+},{
+ 7,
+ .cpu_info = {
+ CPU(0, "Harvest VLSI Design Center, Inc. - unknown"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(-1, NULL)
+ }
+},{
+ 8,
+ .cpu_info = {
+ CPU(0, "Systems and Processes Engineering Corporation (SPEC)"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(-1, NULL)
+ }
+},{
+ 9,
+ .cpu_info = {
+ /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */
+ CPU(0, "Fujitsu or Weitek Power-UP"),
+ CPU(1, "Fujitsu or Weitek Power-UP"),
+ CPU(2, "Fujitsu or Weitek Power-UP"),
+ CPU(3, "Fujitsu or Weitek Power-UP"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(3, "Fujitsu or Weitek on-chip FPU"),
+ FPU(-1, NULL)
+ }
+},{
+ 0x17,
+ .cpu_info = {
+ CPU(0x10, "TI UltraSparc I (SpitFire)"),
+ CPU(0x11, "TI UltraSparc II (BlackBird)"),
+ CPU(0x12, "TI UltraSparc IIi (Sabre)"),
+ CPU(0x13, "TI UltraSparc IIe (Hummingbird)"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0x10, "UltraSparc I integrated FPU"),
+ FPU(0x11, "UltraSparc II integrated FPU"),
+ FPU(0x12, "UltraSparc IIi integrated FPU"),
+ FPU(0x13, "UltraSparc IIe integrated FPU"),
+ FPU(-1, NULL)
+ }
+},{
+ 0x22,
+ .cpu_info = {
+ CPU(0x10, "TI UltraSparc I (SpitFire)"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0x10, "UltraSparc I integrated FPU"),
+ FPU(-1, NULL)
+ }
+},{
+ 0x3e,
+ .cpu_info = {
+ CPU(0x14, "TI UltraSparc III (Cheetah)"),
+ CPU(0x15, "TI UltraSparc III+ (Cheetah+)"),
+ CPU(0x16, "TI UltraSparc IIIi (Jalapeno)"),
+ CPU(0x18, "TI UltraSparc IV (Jaguar)"),
+ CPU(0x19, "TI UltraSparc IV+ (Panther)"),
+ CPU(0x22, "TI UltraSparc IIIi+ (Serrano)"),
+ CPU(-1, NULL)
+ },
+ .fpu_info = {
+ FPU(0x14, "UltraSparc III integrated FPU"),
+ FPU(0x15, "UltraSparc III+ integrated FPU"),
+ FPU(0x16, "UltraSparc IIIi integrated FPU"),
+ FPU(0x18, "UltraSparc IV integrated FPU"),
+ FPU(0x19, "UltraSparc IV+ integrated FPU"),
+ FPU(0x22, "UltraSparc IIIi+ integrated FPU"),
+ FPU(-1, NULL)
+ }
+}};
+
/* In order to get the fpu type correct, you need to take the IDPROM's
* machine type value into consideration too. I will fix this.
*/
-static struct cpu_fp_info linux_sparc_fpu[] = {
- { 0, 0, "Fujitsu MB86910 or Weitek WTL1164/5"},
- { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5 or LSI L64831"},
- { 0, 2, "LSI Logic L64802 or Texas Instruments ACT8847"},
- /* SparcStation SLC, SparcStation1 */
- { 0, 3, "Weitek WTL3170/2"},
- /* SPARCstation-5 */
- { 0, 4, "Lsi Logic/Meiko L64804 or compatible"},
- { 0, 5, "reserved"},
- { 0, 6, "reserved"},
- { 0, 7, "No FPU"},
- { 1, 0, "ROSS HyperSparc combined IU/FPU"},
- { 1, 1, "Lsi Logic L64814"},
- { 1, 2, "Texas Instruments TMS390-C602A"},
- { 1, 3, "Cypress CY7C602 FPU"},
- { 1, 4, "reserved"},
- { 1, 5, "reserved"},
- { 1, 6, "reserved"},
- { 1, 7, "No FPU"},
- { 2, 0, "BIT B5010 or B5110/20 or B5210"},
- { 2, 1, "reserved"},
- { 2, 2, "reserved"},
- { 2, 3, "reserved"},
- { 2, 4, "reserved"},
- { 2, 5, "reserved"},
- { 2, 6, "reserved"},
- { 2, 7, "No FPU"},
- /* SuperSparc 50 module */
- { 4, 0, "SuperSparc on-chip FPU"},
- /* SparcClassic */
- { 4, 4, "TI MicroSparc on chip FPU"},
- { 5, 0, "Matsushita MN10501"},
- { 5, 1, "reserved"},
- { 5, 2, "reserved"},
- { 5, 3, "reserved"},
- { 5, 4, "reserved"},
- { 5, 5, "reserved"},
- { 5, 6, "reserved"},
- { 5, 7, "No FPU"},
- { 9, 3, "Fujitsu or Weitek on-chip FPU"},
-};
-#define NSPARCFPU ARRAY_SIZE(linux_sparc_fpu)
-
-static struct cpu_iu_info linux_sparc_chips[] = {
- /* Sun4/100, 4/200, SLC */
- { 0, 0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"},
- /* borned STP1012PGA */
- { 0, 4, "Fujitsu MB86904"},
- { 0, 5, "Fujitsu TurboSparc MB86907"},
- /* SparcStation2, SparcServer 490 & 690 */
- { 1, 0, "LSI Logic Corporation - L64811"},
- /* SparcStation2 */
- { 1, 1, "Cypress/ROSS CY7C601"},
- /* Embedded controller */
- { 1, 3, "Cypress/ROSS CY7C611"},
- /* Ross Technologies HyperSparc */
- { 1, 0xf, "ROSS HyperSparc RT620"},
- { 1, 0xe, "ROSS HyperSparc RT625 or RT626"},
- /* ECL Implementation, CRAY S-MP Supercomputer... AIEEE! */
- /* Someone please write the code to support this beast! ;) */
- { 2, 0, "Bipolar Integrated Technology - B5010"},
- { 3, 0, "LSI Logic Corporation - unknown-type"},
- { 4, 0, "Texas Instruments, Inc. - SuperSparc-(II)"},
- /* SparcClassic -- borned STP1010TAB-50*/
- { 4, 1, "Texas Instruments, Inc. - MicroSparc"},
- { 4, 2, "Texas Instruments, Inc. - MicroSparc II"},
- { 4, 3, "Texas Instruments, Inc. - SuperSparc 51"},
- { 4, 4, "Texas Instruments, Inc. - SuperSparc 61"},
- { 4, 5, "Texas Instruments, Inc. - unknown"},
- { 5, 0, "Matsushita - MN10501"},
- { 6, 0, "Philips Corporation - unknown"},
- { 7, 0, "Harvest VLSI Design Center, Inc. - unknown"},
- /* Gallium arsenide 200MHz, BOOOOGOOOOMIPS!!! */
- { 8, 0, "Systems and Processes Engineering Corporation (SPEC)"},
- { 9, 0, "Fujitsu or Weitek Power-UP"},
- { 9, 1, "Fujitsu or Weitek Power-UP"},
- { 9, 2, "Fujitsu or Weitek Power-UP"},
- { 9, 3, "Fujitsu or Weitek Power-UP"},
- { 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"},
- { 0xb, 0, "UNKNOWN CPU-VENDOR/TYPE"},
- { 0xc, 0, "UNKNOWN CPU-VENDOR/TYPE"},
- { 0xd, 0, "UNKNOWN CPU-VENDOR/TYPE"},
- { 0xe, 0, "UNKNOWN CPU-VENDOR/TYPE"},
- { 0xf, 0, "UNKNOWN CPU-VENDOR/TYPE"},
-};
+const char *sparc_cpu_type;
+const char *sparc_fpu_type;
-#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips)
+unsigned int fsr_storage;
-char *sparc_cpu_type;
-char *sparc_fpu_type;
+static void set_cpu_and_fpu(int psr_impl, int psr_vers, int fpu_vers)
+{
+ sparc_cpu_type = NULL;
+ sparc_fpu_type = NULL;
+ if (psr_impl < ARRAY_SIZE(manufacturer_info))
+ {
+ const struct cpu_info *cpu;
+ const struct fpu_info *fpu;
-unsigned int fsr_storage;
+ cpu = &manufacturer_info[psr_impl].cpu_info[0];
+ while (cpu->psr_vers != -1)
+ {
+ if (cpu->psr_vers == psr_vers) {
+ sparc_cpu_type = cpu->name;
+ sparc_fpu_type = "No FPU";
+ break;
+ }
+ cpu++;
+ }
+ fpu = &manufacturer_info[psr_impl].fpu_info[0];
+ while (fpu->fp_vers != -1)
+ {
+ if (fpu->fp_vers == fpu_vers) {
+ sparc_fpu_type = fpu->name;
+ break;
+ }
+ fpu++;
+ }
+ }
+ if (sparc_cpu_type == NULL)
+ {
+ printk(KERN_ERR "CPU: Unknown chip, impl[0x%x] vers[0x%x]\n",
+ psr_impl, psr_vers);
+ sparc_cpu_type = "Unknown CPU";
+ }
+ if (sparc_fpu_type == NULL)
+ {
+ printk(KERN_ERR "FPU: Unknown chip, impl[0x%x] vers[0x%x]\n",
+ psr_impl, fpu_vers);
+ sparc_fpu_type = "Unknown FPU";
+ }
+}
+#ifdef CONFIG_SPARC32
void __cpuinit cpu_probe(void)
{
int psr_impl, psr_vers, fpu_vers;
- int i, psr;
+ int psr;
- psr_impl = ((get_psr()>>28)&0xf);
- psr_vers = ((get_psr()>>24)&0xf);
+ psr_impl = ((get_psr() >> 28) & 0xf);
+ psr_vers = ((get_psr() >> 24) & 0xf);
psr = get_psr();
put_psr(psr | PSR_EF);
- fpu_vers = ((get_fsr()>>17)&0x7);
+ fpu_vers = ((get_fsr() >> 17) & 0x7);
put_psr(psr);
- for(i = 0; i<NSPARCCHIPS; i++) {
- if(linux_sparc_chips[i].psr_impl == psr_impl)
- if(linux_sparc_chips[i].psr_vers == psr_vers) {
- sparc_cpu_type = linux_sparc_chips[i].cpu_name;
- break;
- }
- }
+ set_cpu_and_fpu(psr_impl, psr_vers, fpu_vers);
+}
+#else
+static void __init sun4v_cpu_probe(void)
+{
+ switch (sun4v_chip_type) {
+ case SUN4V_CHIP_NIAGARA1:
+ sparc_cpu_type = "UltraSparc T1 (Niagara)";
+ sparc_fpu_type = "UltraSparc T1 integrated FPU";
+ break;
- if(i==NSPARCCHIPS)
- printk("DEBUG: psr.impl = 0x%x psr.vers = 0x%x\n", psr_impl,
- psr_vers);
+ case SUN4V_CHIP_NIAGARA2:
+ sparc_cpu_type = "UltraSparc T2 (Niagara2)";
+ sparc_fpu_type = "UltraSparc T2 integrated FPU";
+ break;
- for(i = 0; i<NSPARCFPU; i++) {
- if(linux_sparc_fpu[i].psr_impl == psr_impl)
- if(linux_sparc_fpu[i].fp_vers == fpu_vers) {
- sparc_fpu_type = linux_sparc_fpu[i].fp_name;
- break;
- }
+ default:
+ printk(KERN_WARNING "CPU: Unknown sun4v cpu type [%s]\n",
+ prom_cpu_compatible);
+ sparc_cpu_type = "Unknown SUN4V CPU";
+ sparc_fpu_type = "Unknown SUN4V FPU";
+ break;
}
+}
+
+static int __init cpu_type_probe(void)
+{
+ if (tlb_type == hypervisor) {
+ sun4v_cpu_probe();
+ } else {
+ unsigned long ver;
+ int manuf, impl;
- if(i == NSPARCFPU) {
- printk("DEBUG: psr.impl = 0x%x fsr.vers = 0x%x\n", psr_impl,
- fpu_vers);
- sparc_fpu_type = linux_sparc_fpu[31].fp_name;
+ __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver));
+
+ manuf = ((ver >> 48) & 0xffff);
+ impl = ((ver >> 32) & 0xffff);
+ set_cpu_and_fpu(manuf, impl, impl);
}
+ return 0;
}
+
+arch_initcall(cpu_type_probe);
+#endif
diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c
index ad656b044b8c..b171ae8de90d 100644
--- a/arch/sparc/kernel/devices.c
+++ b/arch/sparc/kernel/devices.c
@@ -133,14 +133,12 @@ void __init device_scan(void)
#endif /* !CONFIG_SMP */
cpu_probe();
-#ifdef CONFIG_SUN_AUXIO
{
extern void auxio_probe(void);
extern void auxio_power_probe(void);
auxio_probe();
auxio_power_probe();
}
-#endif
clock_stop_probe();
if (ARCH_SUN4C)
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc/kernel/ds.c
index f52e0534d91d..f52e0534d91d 100644
--- a/arch/sparc64/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc/kernel/dtlb_miss.S
index 09a6a15a7105..09a6a15a7105 100644
--- a/arch/sparc64/kernel/dtlb_miss.S
+++ b/arch/sparc/kernel/dtlb_miss.S
diff --git a/arch/sparc64/kernel/dtlb_prot.S b/arch/sparc/kernel/dtlb_prot.S
index b2c2c5be281c..b2c2c5be281c 100644
--- a/arch/sparc64/kernel/dtlb_prot.S
+++ b/arch/sparc/kernel/dtlb_prot.S
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc/kernel/ebus.c
index 77dbf6d45faf..77dbf6d45faf 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc/kernel/ebus.c
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
new file mode 100644
index 000000000000..4f53a2395ac6
--- /dev/null
+++ b/arch/sparc/kernel/entry.h
@@ -0,0 +1,229 @@
+#ifndef _ENTRY_H
+#define _ENTRY_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+/* irq */
+extern void handler_irq(int irq, struct pt_regs *regs);
+
+#ifdef CONFIG_SPARC32
+/* traps */
+extern void do_hw_interrupt(struct pt_regs *regs, unsigned long type);
+extern void do_illegal_instruction(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+
+extern void do_priv_instruction(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc,
+ unsigned long psr);
+extern void do_fpd_trap(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void do_fpe_trap(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void handle_tag_overflow(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void handle_watchpoint(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void handle_reg_access(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void handle_cp_disabled(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+extern void handle_cp_exception(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+
+
+
+/* entry.S */
+extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
+ void *fpqueue, unsigned long *fpqdepth);
+extern void fpload(unsigned long *fpregs, unsigned long *fsr);
+
+#else /* CONFIG_SPARC32 */
+extern void __init per_cpu_patch(void);
+extern void __init sun4v_patch(void);
+extern void __init boot_cpu_id_too_large(int cpu);
+extern unsigned int dcache_parity_tl1_occurred;
+extern unsigned int icache_parity_tl1_occurred;
+
+extern asmlinkage void update_perfctrs(void);
+extern asmlinkage void sparc_breakpoint(struct pt_regs *regs);
+extern void timer_interrupt(int irq, struct pt_regs *regs);
+
+extern void do_notify_resume(struct pt_regs *regs,
+ unsigned long orig_i0,
+ unsigned long thread_info_flags);
+
+extern asmlinkage int syscall_trace_enter(struct pt_regs *regs);
+extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
+
+extern void bad_trap_tl1(struct pt_regs *regs, long lvl);
+
+extern void do_fpe_common(struct pt_regs *regs);
+extern void do_fpieee(struct pt_regs *regs);
+extern void do_fpother(struct pt_regs *regs);
+extern void do_tof(struct pt_regs *regs);
+extern void do_div0(struct pt_regs *regs);
+extern void do_illegal_instruction(struct pt_regs *regs);
+extern void mem_address_unaligned(struct pt_regs *regs,
+ unsigned long sfar,
+ unsigned long sfsr);
+extern void sun4v_do_mna(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long type_ctx);
+extern void do_privop(struct pt_regs *regs);
+extern void do_privact(struct pt_regs *regs);
+extern void do_cee(struct pt_regs *regs);
+extern void do_cee_tl1(struct pt_regs *regs);
+extern void do_dae_tl1(struct pt_regs *regs);
+extern void do_iae_tl1(struct pt_regs *regs);
+extern void do_div0_tl1(struct pt_regs *regs);
+extern void do_fpdis_tl1(struct pt_regs *regs);
+extern void do_fpieee_tl1(struct pt_regs *regs);
+extern void do_fpother_tl1(struct pt_regs *regs);
+extern void do_ill_tl1(struct pt_regs *regs);
+extern void do_irq_tl1(struct pt_regs *regs);
+extern void do_lddfmna_tl1(struct pt_regs *regs);
+extern void do_stdfmna_tl1(struct pt_regs *regs);
+extern void do_paw(struct pt_regs *regs);
+extern void do_paw_tl1(struct pt_regs *regs);
+extern void do_vaw(struct pt_regs *regs);
+extern void do_vaw_tl1(struct pt_regs *regs);
+extern void do_tof_tl1(struct pt_regs *regs);
+extern void do_getpsr(struct pt_regs *regs);
+
+extern void spitfire_insn_access_exception(struct pt_regs *regs,
+ unsigned long sfsr,
+ unsigned long sfar);
+extern void spitfire_insn_access_exception_tl1(struct pt_regs *regs,
+ unsigned long sfsr,
+ unsigned long sfar);
+extern void spitfire_data_access_exception(struct pt_regs *regs,
+ unsigned long sfsr,
+ unsigned long sfar);
+extern void spitfire_data_access_exception_tl1(struct pt_regs *regs,
+ unsigned long sfsr,
+ unsigned long sfar);
+extern void spitfire_access_error(struct pt_regs *regs,
+ unsigned long status_encoded,
+ unsigned long afar);
+
+extern void cheetah_fecc_handler(struct pt_regs *regs,
+ unsigned long afsr,
+ unsigned long afar);
+extern void cheetah_cee_handler(struct pt_regs *regs,
+ unsigned long afsr,
+ unsigned long afar);
+extern void cheetah_deferred_handler(struct pt_regs *regs,
+ unsigned long afsr,
+ unsigned long afar);
+extern void cheetah_plus_parity_error(int type, struct pt_regs *regs);
+
+extern void sun4v_insn_access_exception(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long type_ctx);
+extern void sun4v_insn_access_exception_tl1(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long type_ctx);
+extern void sun4v_data_access_exception(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long type_ctx);
+extern void sun4v_data_access_exception_tl1(struct pt_regs *regs,
+ unsigned long addr,
+ unsigned long type_ctx);
+extern void sun4v_resum_error(struct pt_regs *regs,
+ unsigned long offset);
+extern void sun4v_resum_overflow(struct pt_regs *regs);
+extern void sun4v_nonresum_error(struct pt_regs *regs,
+ unsigned long offset);
+extern void sun4v_nonresum_overflow(struct pt_regs *regs);
+
+extern unsigned long sun4v_err_itlb_vaddr;
+extern unsigned long sun4v_err_itlb_ctx;
+extern unsigned long sun4v_err_itlb_pte;
+extern unsigned long sun4v_err_itlb_error;
+
+extern void sun4v_itlb_error_report(struct pt_regs *regs, int tl);
+
+extern unsigned long sun4v_err_dtlb_vaddr;
+extern unsigned long sun4v_err_dtlb_ctx;
+extern unsigned long sun4v_err_dtlb_pte;
+extern unsigned long sun4v_err_dtlb_error;
+
+extern void sun4v_dtlb_error_report(struct pt_regs *regs, int tl);
+extern void hypervisor_tlbop_error(unsigned long err,
+ unsigned long op);
+extern void hypervisor_tlbop_error_xcall(unsigned long err,
+ unsigned long op);
+
+/* WARNING: The error trap handlers in assembly know the precise
+ * layout of the following structure.
+ *
+ * C-level handlers in traps.c use this information to log the
+ * error and then determine how to recover (if possible).
+ */
+struct cheetah_err_info {
+/*0x00*/u64 afsr;
+/*0x08*/u64 afar;
+
+ /* D-cache state */
+/*0x10*/u64 dcache_data[4]; /* The actual data */
+/*0x30*/u64 dcache_index; /* D-cache index */
+/*0x38*/u64 dcache_tag; /* D-cache tag/valid */
+/*0x40*/u64 dcache_utag; /* D-cache microtag */
+/*0x48*/u64 dcache_stag; /* D-cache snooptag */
+
+ /* I-cache state */
+/*0x50*/u64 icache_data[8]; /* The actual insns + predecode */
+/*0x90*/u64 icache_index; /* I-cache index */
+/*0x98*/u64 icache_tag; /* I-cache phys tag */
+/*0xa0*/u64 icache_utag; /* I-cache microtag */
+/*0xa8*/u64 icache_stag; /* I-cache snooptag */
+/*0xb0*/u64 icache_upper; /* I-cache upper-tag */
+/*0xb8*/u64 icache_lower; /* I-cache lower-tag */
+
+ /* E-cache state */
+/*0xc0*/u64 ecache_data[4]; /* 32 bytes from staging registers */
+/*0xe0*/u64 ecache_index; /* E-cache index */
+/*0xe8*/u64 ecache_tag; /* E-cache tag/state */
+
+/*0xf0*/u64 __pad[32 - 30];
+};
+#define CHAFSR_INVALID ((u64)-1L)
+
+/* This is allocated at boot time based upon the largest hardware
+ * cpu ID in the system. We allocate two entries per cpu, one for
+ * TL==0 logging and one for TL >= 1 logging.
+ */
+extern struct cheetah_err_info *cheetah_error_log;
+
+/* UPA nodes send interrupt packet to UltraSparc with first data reg
+ * value low 5 (7 on Starfire) bits holding the IRQ identifier being
+ * delivered. We must translate this into a non-vector IRQ so we can
+ * set the softint on this cpu.
+ *
+ * To make processing these packets efficient and race free we use
+ * an array of irq buckets below. The interrupt vector handler in
+ * entry.S feeds incoming packets into per-cpu pil-indexed lists.
+ *
+ * If you make changes to ino_bucket, please update hand coded assembler
+ * of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S
+ */
+struct ino_bucket {
+/*0x00*/unsigned long __irq_chain_pa;
+
+ /* Virtual interrupt number assigned to this INO. */
+/*0x08*/unsigned int __virt_irq;
+/*0x0c*/unsigned int __pad;
+};
+
+extern struct ino_bucket *ivector_table;
+extern unsigned long ivector_table_pa;
+
+extern void init_irqwork_curcpu(void);
+extern void __cpuinit sun4v_register_mondo_queues(int this_cpu);
+
+#endif /* CONFIG_SPARC32 */
+#endif /* _ENTRY_H */
diff --git a/arch/sparc/kernel/etrap.S b/arch/sparc/kernel/etrap_32.S
index e806fcdc46db..e806fcdc46db 100644
--- a/arch/sparc/kernel/etrap.S
+++ b/arch/sparc/kernel/etrap_32.S
diff --git a/arch/sparc/kernel/etrap_64.S b/arch/sparc/kernel/etrap_64.S
new file mode 100644
index 000000000000..786b185e6e3f
--- /dev/null
+++ b/arch/sparc/kernel/etrap_64.S
@@ -0,0 +1,236 @@
+/*
+ * etrap.S: Preparing for entry into the kernel on Sparc V9.
+ *
+ * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+
+#include <asm/asi.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spitfire.h>
+#include <asm/head.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+
+#define TASK_REGOFF (THREAD_SIZE-TRACEREG_SZ-STACKFRAME_SZ)
+#define ETRAP_PSTATE1 (PSTATE_TSO | PSTATE_PRIV)
+#define ETRAP_PSTATE2 \
+ (PSTATE_TSO | PSTATE_PEF | PSTATE_PRIV | PSTATE_IE)
+
+/*
+ * On entry, %g7 is return address - 0x4.
+ * %g4 and %g5 will be preserved %l4 and %l5 respectively.
+ */
+
+ .text
+ .align 64
+ .globl etrap_syscall, etrap, etrap_irq, etraptl1
+etrap: rdpr %pil, %g2
+etrap_irq: clr %g3
+etrap_syscall: TRAP_LOAD_THREAD_REG(%g6, %g1)
+ rdpr %tstate, %g1
+ or %g1, %g3, %g1
+ sllx %g2, 20, %g3
+ andcc %g1, TSTATE_PRIV, %g0
+ or %g1, %g3, %g1
+ bne,pn %xcc, 1f
+ sub %sp, STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS, %g2
+ wrpr %g0, 7, %cleanwin
+
+ sethi %hi(TASK_REGOFF), %g2
+ sethi %hi(TSTATE_PEF), %g3
+ or %g2, %lo(TASK_REGOFF), %g2
+ and %g1, %g3, %g3
+ brnz,pn %g3, 1f
+ add %g6, %g2, %g2
+ wr %g0, 0, %fprs
+1: rdpr %tpc, %g3
+
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE]
+ rdpr %tnpc, %g1
+ stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC]
+ rd %y, %g3
+ stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC]
+ rdpr %tt, %g1
+ st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y]
+ sethi %hi(PT_REGS_MAGIC), %g3
+ or %g3, %g1, %g1
+ st %g1, [%g2 + STACKFRAME_SZ + PT_V9_MAGIC]
+
+ rdpr %cansave, %g1
+ brnz,pt %g1, etrap_save
+ nop
+
+ rdpr %cwp, %g1
+ add %g1, 2, %g1
+ wrpr %g1, %cwp
+ be,pt %xcc, etrap_user_spill
+ mov ASI_AIUP, %g3
+
+ rdpr %otherwin, %g3
+ brz %g3, etrap_kernel_spill
+ mov ASI_AIUS, %g3
+
+etrap_user_spill:
+
+ wr %g3, 0x0, %asi
+ ldx [%g6 + TI_FLAGS], %g3
+ and %g3, _TIF_32BIT, %g3
+ brnz,pt %g3, etrap_user_spill_32bit
+ nop
+ ba,a,pt %xcc, etrap_user_spill_64bit
+
+etrap_save: save %g2, -STACK_BIAS, %sp
+ mov %g6, %l6
+
+ bne,pn %xcc, 3f
+ mov PRIMARY_CONTEXT, %l4
+ rdpr %canrestore, %g3
+ rdpr %wstate, %g2
+ wrpr %g0, 0, %canrestore
+ sll %g2, 3, %g2
+ mov 1, %l5
+ stb %l5, [%l6 + TI_FPDEPTH]
+
+ wrpr %g3, 0, %otherwin
+ wrpr %g2, 0, %wstate
+ sethi %hi(sparc64_kern_pri_context), %g2
+ ldx [%g2 + %lo(sparc64_kern_pri_context)], %g3
+
+661: stxa %g3, [%l4] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g3, [%l4] ASI_MMU
+ .previous
+
+ sethi %hi(KERNBASE), %l4
+ flush %l4
+ mov ASI_AIUS, %l7
+2: mov %g4, %l4
+ mov %g5, %l5
+ add %g7, 4, %l2
+
+ /* Go to trap time globals so we can save them. */
+661: wrpr %g0, ETRAP_PSTATE1, %pstate
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ SET_GL(0)
+ .previous
+
+ stx %g1, [%sp + PTREGS_OFF + PT_V9_G1]
+ stx %g2, [%sp + PTREGS_OFF + PT_V9_G2]
+ sllx %l7, 24, %l7
+ stx %g3, [%sp + PTREGS_OFF + PT_V9_G3]
+ rdpr %cwp, %l0
+ stx %g4, [%sp + PTREGS_OFF + PT_V9_G4]
+ stx %g5, [%sp + PTREGS_OFF + PT_V9_G5]
+ stx %g6, [%sp + PTREGS_OFF + PT_V9_G6]
+ stx %g7, [%sp + PTREGS_OFF + PT_V9_G7]
+ or %l7, %l0, %l7
+ sethi %hi(TSTATE_TSO | TSTATE_PEF), %l0
+ or %l7, %l0, %l7
+ wrpr %l2, %tnpc
+ wrpr %l7, (TSTATE_PRIV | TSTATE_IE), %tstate
+ stx %i0, [%sp + PTREGS_OFF + PT_V9_I0]
+ stx %i1, [%sp + PTREGS_OFF + PT_V9_I1]
+ stx %i2, [%sp + PTREGS_OFF + PT_V9_I2]
+ stx %i3, [%sp + PTREGS_OFF + PT_V9_I3]
+ stx %i4, [%sp + PTREGS_OFF + PT_V9_I4]
+ stx %i5, [%sp + PTREGS_OFF + PT_V9_I5]
+ stx %i6, [%sp + PTREGS_OFF + PT_V9_I6]
+ mov %l6, %g6
+ stx %i7, [%sp + PTREGS_OFF + PT_V9_I7]
+ LOAD_PER_CPU_BASE(%g5, %g6, %g4, %g3, %l1)
+ ldx [%g6 + TI_TASK], %g4
+ done
+
+3: mov ASI_P, %l7
+ ldub [%l6 + TI_FPDEPTH], %l5
+ add %l6, TI_FPSAVED + 1, %l4
+ srl %l5, 1, %l3
+ add %l5, 2, %l5
+ stb %l5, [%l6 + TI_FPDEPTH]
+ ba,pt %xcc, 2b
+ stb %g0, [%l4 + %l3]
+ nop
+
+etraptl1: /* Save tstate/tpc/tnpc of TL 1-->4 and the tl register itself.
+ * We place this right after pt_regs on the trap stack.
+ * The layout is:
+ * 0x00 TL1's TSTATE
+ * 0x08 TL1's TPC
+ * 0x10 TL1's TNPC
+ * 0x18 TL1's TT
+ * ...
+ * 0x58 TL4's TT
+ * 0x60 TL
+ */
+ TRAP_LOAD_THREAD_REG(%g6, %g1)
+ sub %sp, ((4 * 8) * 4) + 8, %g2
+ rdpr %tl, %g1
+
+ wrpr %g0, 1, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x00]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x08]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x10]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x18]
+
+ wrpr %g0, 2, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x20]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x28]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x30]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x38]
+
+ sethi %hi(is_sun4v), %g3
+ lduw [%g3 + %lo(is_sun4v)], %g3
+ brnz,pn %g3, finish_tl1_capture
+ nop
+
+ wrpr %g0, 3, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x40]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x48]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x50]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x58]
+
+ wrpr %g0, 4, %tl
+ rdpr %tstate, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x60]
+ rdpr %tpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x68]
+ rdpr %tnpc, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x70]
+ rdpr %tt, %g3
+ stx %g3, [%g2 + STACK_BIAS + 0x78]
+
+ stx %g1, [%g2 + STACK_BIAS + 0x80]
+
+finish_tl1_capture:
+ wrpr %g0, 1, %tl
+661: nop
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ SET_GL(1)
+ .previous
+
+ rdpr %tstate, %g1
+ sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2
+ ba,pt %xcc, 1b
+ andcc %g1, TSTATE_PRIV, %g0
+
+#undef TASK_REGOFF
+#undef ETRAP_PSTATE1
diff --git a/arch/sparc64/kernel/fpu_traps.S b/arch/sparc/kernel/fpu_traps.S
index a6864826a4bd..a6864826a4bd 100644
--- a/arch/sparc64/kernel/fpu_traps.S
+++ b/arch/sparc/kernel/fpu_traps.S
diff --git a/arch/sparc64/kernel/ftrace.c b/arch/sparc/kernel/ftrace.c
index d0218e73f982..d0218e73f982 100644
--- a/arch/sparc64/kernel/ftrace.c
+++ b/arch/sparc/kernel/ftrace.c
diff --git a/arch/sparc64/kernel/getsetcc.S b/arch/sparc/kernel/getsetcc.S
index a14d272d2061..a14d272d2061 100644
--- a/arch/sparc64/kernel/getsetcc.S
+++ b/arch/sparc/kernel/getsetcc.S
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S
deleted file mode 100644
index 51b40426f9c6..000000000000
--- a/arch/sparc/kernel/head.S
+++ /dev/null
@@ -1,1295 +0,0 @@
-/*
- * head.S: The initial boot code for the Sparc port of Linux.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995,1999 Pete Zaitcev (zaitcev@yahoo.com)
- * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
- *
- * CompactPCI platform by Eric Brower, 1999.
- */
-
-#include <linux/version.h>
-#include <linux/init.h>
-
-#include <asm/head.h>
-#include <asm/asi.h>
-#include <asm/contregs.h>
-#include <asm/ptrace.h>
-#include <asm/psr.h>
-#include <asm/page.h>
-#include <asm/kdebug.h>
-#include <asm/winmacro.h>
-#include <asm/thread_info.h> /* TI_UWINMASK */
-#include <asm/errno.h>
-#include <asm/pgtsrmmu.h> /* SRMMU_PGDIR_SHIFT */
-
- .data
-/*
- * The following are used with the prom_vector node-ops to figure out
- * the cpu-type
- */
-
- .align 4
-cputyp:
- .word 1
-
- .align 4
- .globl cputypval
-cputypval:
- .asciz "sun4c"
- .ascii " "
-
-cputypvalend:
-cputypvallen = cputypvar - cputypval
-
- .align 4
-/*
- * Sun people can't spell worth damn. "compatability" indeed.
- * At least we *know* we can't spell, and use a spell-checker.
- */
-
-/* Uh, actually Linus it is I who cannot spell. Too much murky
- * Sparc assembly will do this to ya.
- */
-cputypvar:
- .asciz "compatability"
-
-/* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */
- .align 4
-cputypvar_sun4m:
- .asciz "compatible"
-
- .align 4
-
-sun4_notsup:
- .asciz "Sparc-Linux sun4 support does no longer exist.\n\n"
- .align 4
-
-sun4e_notsup:
- .asciz "Sparc-Linux sun4e support does not exist\n\n"
- .align 4
-
- /* The Sparc trap table, bootloader gives us control at _start. */
- .section .text.head,"ax"
- .globl start, _stext, _start, __stext
- .globl trapbase
-_start: /* danger danger */
-__stext:
-_stext:
-start:
-trapbase:
-#ifdef CONFIG_SMP
-trapbase_cpu0:
-#endif
-/* We get control passed to us here at t_zero. */
-t_zero: b gokernel; nop; nop; nop;
-t_tflt: SPARC_TFAULT /* Inst. Access Exception */
-t_bins: TRAP_ENTRY(0x2, bad_instruction) /* Illegal Instruction */
-t_pins: TRAP_ENTRY(0x3, priv_instruction) /* Privileged Instruction */
-t_fpd: TRAP_ENTRY(0x4, fpd_trap_handler) /* Floating Point Disabled */
-t_wovf: WINDOW_SPILL /* Window Overflow */
-t_wunf: WINDOW_FILL /* Window Underflow */
-t_mna: TRAP_ENTRY(0x7, mna_handler) /* Memory Address Not Aligned */
-t_fpe: TRAP_ENTRY(0x8, fpe_trap_handler) /* Floating Point Exception */
-t_dflt: SPARC_DFAULT /* Data Miss Exception */
-t_tio: TRAP_ENTRY(0xa, do_tag_overflow) /* Tagged Instruction Ovrflw */
-t_wpt: TRAP_ENTRY(0xb, do_watchpoint) /* Watchpoint Detected */
-t_badc: BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
-t_irq1: TRAP_ENTRY_INTERRUPT(1) /* IRQ Software/SBUS Level 1 */
-t_irq2: TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */
-t_irq3: TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */
-t_irq4: TRAP_ENTRY_INTERRUPT(4) /* IRQ Software Level 4 */
-t_irq5: TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */
-t_irq6: TRAP_ENTRY_INTERRUPT(6) /* IRQ Software Level 6 */
-t_irq7: TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */
-t_irq8: TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */
-t_irq9: TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */
-t_irq10:TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 (one we use) */
-t_irq11:TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */
-t_irq12:TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */
-t_irq13:TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */
-t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */
- .globl t_nmi
-#ifndef CONFIG_SMP
-t_nmi: NMI_TRAP /* Level 15 (NMI) */
-#else
-t_nmi: TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
-#endif
-t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */
-t_iacce:BAD_TRAP(0x21) /* Instr Access Error */
-t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23)
-t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */
-t_uflsh:SKIP_TRAP(0x25, unimp_flush) /* Unimplemented FLUSH inst. */
-t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
-t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */
-t_dacce:SPARC_DFAULT /* Data Access Error */
-t_hwdz: TRAP_ENTRY(0x2a, do_hw_divzero) /* Division by zero, you lose... */
-t_dserr:BAD_TRAP(0x2b) /* Data Store Error */
-t_daccm:BAD_TRAP(0x2c) /* Data Access MMU-Miss */
-t_bad2d:BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
-t_bad32:BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
-t_bad37:BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
-t_iaccm:BAD_TRAP(0x3c) /* Instr Access MMU-Miss */
-t_bad3d:BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40) BAD_TRAP(0x41)
-t_bad42:BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45) BAD_TRAP(0x46)
-t_bad47:BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a) BAD_TRAP(0x4b)
-t_bad4c:BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f) BAD_TRAP(0x50)
-t_bad51:BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
-t_bad56:BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
-t_bad5b:BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
-t_bad60:BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
-t_bad65:BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
-t_bad6a:BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
-t_bad6f:BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
-t_bad74:BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
-t_bad79:BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
-t_bad7e:BAD_TRAP(0x7e) BAD_TRAP(0x7f)
-t_bad80:BAD_TRAP(0x80) /* SunOS System Call */
-t_sbkpt:BREAKPOINT_TRAP /* Software Breakpoint/KGDB */
-t_divz: TRAP_ENTRY(0x82, do_hw_divzero) /* Divide by zero trap */
-t_flwin:TRAP_ENTRY(0x83, do_flush_windows) /* Flush Windows Trap */
-t_clwin:BAD_TRAP(0x84) /* Clean Windows Trap */
-t_rchk: BAD_TRAP(0x85) /* Range Check */
-t_funal:BAD_TRAP(0x86) /* Fix Unaligned Access Trap */
-t_iovf: BAD_TRAP(0x87) /* Integer Overflow Trap */
-t_bad88:BAD_TRAP(0x88) /* Slowaris System Call */
-t_bad89:BAD_TRAP(0x89) /* Net-B.S. System Call */
-t_bad8a:BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c) BAD_TRAP(0x8d) BAD_TRAP(0x8e)
-t_bad8f:BAD_TRAP(0x8f)
-t_linux:LINUX_SYSCALL_TRAP /* Linux System Call */
-t_bad91:BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95)
-t_bad96:BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a)
-t_bad9b:BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) BAD_TRAP(0x9f)
-t_getcc:GETCC_TRAP /* Get Condition Codes */
-t_setcc:SETCC_TRAP /* Set Condition Codes */
-t_getpsr:GETPSR_TRAP /* Get PSR Register */
-t_bada3:BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
-t_bada7:BAD_TRAP(0xa7)
-t_bada8:BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
-t_badac:BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
-t_badb1:BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
-t_badb6:BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
-t_badbb:BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
-t_badc0:BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
-t_badc5:BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
-t_badca:BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
-t_badcf:BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
-t_badd4:BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
-t_badd9:BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
-t_badde:BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
-t_bade3:BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
-t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
-t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
-t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
-t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
-t_badfc:BAD_TRAP(0xfc)
-t_kgdb: KGDB_TRAP(0xfd)
-dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */
-dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */
-
- .globl end_traptable
-end_traptable:
-
-#ifdef CONFIG_SMP
- /* Trap tables for the other cpus. */
- .globl trapbase_cpu1, trapbase_cpu2, trapbase_cpu3
-trapbase_cpu1:
- BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
- TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
- WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
- TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
- TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
- BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
- TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
- TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
- TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
- TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
- TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
- TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
- TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
- TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
- TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
- BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
- SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
- BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
- BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
- BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
- BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
- BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
- BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
- BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
- BAD_TRAP(0x50)
- BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
- BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
- BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
- BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
- BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
- BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
- BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
- BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
- BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
- BAD_TRAP(0x7e) BAD_TRAP(0x7f)
- BAD_TRAP(0x80)
- BREAKPOINT_TRAP
- TRAP_ENTRY(0x82, do_hw_divzero)
- TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
- BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
- BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
- BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
- LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
- BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
- BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
- BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
- BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
- BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
- BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
- BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
- BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
- BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
- BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
- BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
- BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
- BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
- BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
- BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
- BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
- BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
- BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
- BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
- BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
- BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
- BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
-
-trapbase_cpu2:
- BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
- TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
- WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
- TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
- TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
- BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
- TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
- TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
- TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
- TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
- TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
- TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
- TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
- TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
- TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
- BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
- SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
- BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
- BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
- BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
- BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
- BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
- BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
- BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
- BAD_TRAP(0x50)
- BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
- BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
- BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
- BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
- BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
- BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
- BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
- BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
- BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
- BAD_TRAP(0x7e) BAD_TRAP(0x7f)
- BAD_TRAP(0x80)
- BREAKPOINT_TRAP
- TRAP_ENTRY(0x82, do_hw_divzero)
- TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
- BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
- BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
- BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
- LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
- BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
- BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
- BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
- BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
- BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
- BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
- BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
- BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
- BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
- BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
- BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
- BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
- BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
- BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
- BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
- BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
- BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
- BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
- BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
- BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
- BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
- BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
-
-trapbase_cpu3:
- BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
- TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
- WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
- TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
- TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
- BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
- TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
- TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
- TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
- TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
- TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
- TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
- TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
- TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
- TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
- BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
- BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
- SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
- BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
- BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
- BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
- BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
- BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
- BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
- BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
- BAD_TRAP(0x50)
- BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
- BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
- BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
- BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
- BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
- BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
- BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
- BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
- BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
- BAD_TRAP(0x7e) BAD_TRAP(0x7f)
- BAD_TRAP(0x80)
- BREAKPOINT_TRAP
- TRAP_ENTRY(0x82, do_hw_divzero)
- TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
- BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
- BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
- BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
- LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
- BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
- BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
- BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
- BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
- BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
- BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
- BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
- BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
- BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
- BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
- BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
- BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
- BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
- BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
- BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
- BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
- BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
- BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
- BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
- BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
- BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
- BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
-
-#endif
- .align PAGE_SIZE
-
-/* This was the only reasonable way I could think of to properly align
- * these page-table data structures.
- */
- .globl pg0, pg1, pg2, pg3
- .globl empty_bad_page
- .globl empty_bad_page_table
- .globl empty_zero_page
- .globl swapper_pg_dir
-swapper_pg_dir: .skip PAGE_SIZE
-pg0: .skip PAGE_SIZE
-pg1: .skip PAGE_SIZE
-pg2: .skip PAGE_SIZE
-pg3: .skip PAGE_SIZE
-empty_bad_page: .skip PAGE_SIZE
-empty_bad_page_table: .skip PAGE_SIZE
-empty_zero_page: .skip PAGE_SIZE
-
- .global root_flags
- .global ram_flags
- .global root_dev
- .global sparc_ramdisk_image
- .global sparc_ramdisk_size
-
-/* This stuff has to be in sync with SILO and other potential boot loaders
- * Fields should be kept upward compatible and whenever any change is made,
- * HdrS version should be incremented.
- */
- .ascii "HdrS"
- .word LINUX_VERSION_CODE
- .half 0x0203 /* HdrS version */
-root_flags:
- .half 1
-root_dev:
- .half 0
-ram_flags:
- .half 0
-sparc_ramdisk_image:
- .word 0
-sparc_ramdisk_size:
- .word 0
- .word reboot_command
- .word 0, 0, 0
- .word _end
-
-/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
- * %g7 and at prom_vector_p. And also quickly check whether we are on
- * a v0, v2, or v3 prom.
- */
-gokernel:
- /* Ok, it's nice to know, as early as possible, if we
- * are already mapped where we expect to be in virtual
- * memory. The Solaris /boot elf format bootloader
- * will peek into our elf header and load us where
- * we want to be, otherwise we have to re-map.
- *
- * Some boot loaders don't place the jmp'rs address
- * in %o7, so we do a pc-relative call to a local
- * label, then see what %o7 has.
- */
-
- mov %o7, %g4 ! Save %o7
-
- /* Jump to it, and pray... */
-current_pc:
- call 1f
- nop
-
-1:
- mov %o7, %g3
-
- tst %o0
- be no_sun4u_here
- mov %g4, %o7 /* Previous %o7. */
-
- mov %o0, %l0 ! stash away romvec
- mov %o0, %g7 ! put it here too
- mov %o1, %l1 ! stash away debug_vec too
-
- /* Ok, let's check out our run time program counter. */
- set current_pc, %g5
- cmp %g3, %g5
- be already_mapped
- nop
-
- /* %l6 will hold the offset we have to subtract
- * from absolute symbols in order to access areas
- * in our own image. If already mapped this is
- * just plain zero, else it is KERNBASE.
- */
- set KERNBASE, %l6
- b copy_prom_lvl14
- nop
-
-already_mapped:
- mov 0, %l6
-
- /* Copy over the Prom's level 14 clock handler. */
-copy_prom_lvl14:
-#if 1
- /* DJHR
- * preserve our linked/calculated instructions
- */
- set lvl14_save, %g1
- set t_irq14, %g3
- sub %g1, %l6, %g1 ! translate to physical
- sub %g3, %l6, %g3 ! translate to physical
- ldd [%g3], %g4
- std %g4, [%g1]
- ldd [%g3+8], %g4
- std %g4, [%g1+8]
-#endif
- rd %tbr, %g1
- andn %g1, 0xfff, %g1 ! proms trap table base
- or %g0, (0x1e<<4), %g2 ! offset to lvl14 intr
- or %g1, %g2, %g2
- set t_irq14, %g3
- sub %g3, %l6, %g3
- ldd [%g2], %g4
- std %g4, [%g3]
- ldd [%g2 + 0x8], %g4
- std %g4, [%g3 + 0x8] ! Copy proms handler
-
-/* Must determine whether we are on a sun4c MMU, SRMMU, or SUN4/400 MUTANT
- * MMU so we can remap ourselves properly. DON'T TOUCH %l0 thru %l5 in these
- * remapping routines, we need their values afterwards!
- */
- /* Now check whether we are already mapped, if we
- * are we can skip all this garbage coming up.
- */
-copy_prom_done:
- cmp %l6, 0
- be go_to_highmem ! this will be a nop then
- nop
-
- set LOAD_ADDR, %g6
- cmp %g7, %g6
- bne remap_not_a_sun4 ! This is not a Sun4
- nop
-
- or %g0, 0x1, %g1
- lduba [%g1] ASI_CONTROL, %g1 ! Only safe to try on Sun4.
- subcc %g1, 0x24, %g0 ! Is this a mutant Sun4/400???
- be sun4_mutant_remap ! Ugh, it is...
- nop
-
- b sun4_normal_remap ! regular sun4, 2 level mmu
- nop
-
-remap_not_a_sun4:
- lda [%g0] ASI_M_MMUREGS, %g1 ! same as ASI_PTE on sun4c
- and %g1, 0x1, %g1 ! Test SRMMU Enable bit ;-)
- cmp %g1, 0x0
- be sun4c_remap ! A sun4c MMU or normal Sun4
- nop
-srmmu_remap:
- /* First, check for a viking (TI) module. */
- set 0x40000000, %g2
- rd %psr, %g3
- and %g2, %g3, %g3
- subcc %g3, 0x0, %g0
- bz srmmu_nviking
- nop
-
- /* Figure out what kind of viking we are on.
- * We need to know if we have to play with the
- * AC bit and disable traps or not.
- */
-
- /* I've only seen MicroSparc's on SparcClassics with this
- * bit set.
- */
- set 0x800, %g2
- lda [%g0] ASI_M_MMUREGS, %g3 ! peek in the control reg
- and %g2, %g3, %g3
- subcc %g3, 0x0, %g0
- bnz srmmu_nviking ! is in mbus mode
- nop
-
- rd %psr, %g3 ! DO NOT TOUCH %g3
- andn %g3, PSR_ET, %g2
- wr %g2, 0x0, %psr
- WRITE_PAUSE
-
- /* Get context table pointer, then convert to
- * a physical address, which is 36 bits.
- */
- set AC_M_CTPR, %g4
- lda [%g4] ASI_M_MMUREGS, %g4
- sll %g4, 0x4, %g4 ! We use this below
- ! DO NOT TOUCH %g4
-
- /* Set the AC bit in the Viking's MMU control reg. */
- lda [%g0] ASI_M_MMUREGS, %g5 ! DO NOT TOUCH %g5
- set 0x8000, %g6 ! AC bit mask
- or %g5, %g6, %g6 ! Or it in...
- sta %g6, [%g0] ASI_M_MMUREGS ! Close your eyes...
-
- /* Grrr, why does it seem like every other load/store
- * on the sun4m is in some ASI space...
- * Fine with me, let's get the pointer to the level 1
- * page table directory and fetch its entry.
- */
- lda [%g4] ASI_M_BYPASS, %o1 ! This is a level 1 ptr
- srl %o1, 0x4, %o1 ! Clear low 4 bits
- sll %o1, 0x8, %o1 ! Make physical
-
- /* Ok, pull in the PTD. */
- lda [%o1] ASI_M_BYPASS, %o2 ! This is the 0x0 16MB pgd
-
- /* Calculate to KERNBASE entry. */
- add %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3
-
- /* Poke the entry into the calculated address. */
- sta %o2, [%o3] ASI_M_BYPASS
-
- /* I don't get it Sun, if you engineered all these
- * boot loaders and the PROM (thank you for the debugging
- * features btw) why did you not have them load kernel
- * images up in high address space, since this is necessary
- * for ABI compliance anyways? Does this low-mapping provide
- * enhanced interoperability?
- *
- * "The PROM is the computer."
- */
-
- /* Ok, restore the MMU control register we saved in %g5 */
- sta %g5, [%g0] ASI_M_MMUREGS ! POW... ouch
-
- /* Turn traps back on. We saved it in %g3 earlier. */
- wr %g3, 0x0, %psr ! tick tock, tick tock
-
- /* Now we burn precious CPU cycles due to bad engineering. */
- WRITE_PAUSE
-
- /* Wow, all that just to move a 32-bit value from one
- * place to another... Jump to high memory.
- */
- b go_to_highmem
- nop
-
- /* This works on viking's in Mbus mode and all
- * other MBUS modules. It is virtually the same as
- * the above madness sans turning traps off and flipping
- * the AC bit.
- */
-srmmu_nviking:
- set AC_M_CTPR, %g1
- lda [%g1] ASI_M_MMUREGS, %g1 ! get ctx table ptr
- sll %g1, 0x4, %g1 ! make physical addr
- lda [%g1] ASI_M_BYPASS, %g1 ! ptr to level 1 pg_table
- srl %g1, 0x4, %g1
- sll %g1, 0x8, %g1 ! make phys addr for l1 tbl
-
- lda [%g1] ASI_M_BYPASS, %g2 ! get level1 entry for 0x0
- add %g1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %g3
- sta %g2, [%g3] ASI_M_BYPASS ! place at KERNBASE entry
- b go_to_highmem
- nop ! wheee....
-
- /* This remaps the kernel on Sun4/4xx machines
- * that have the Sun Mutant Three Level MMU.
- * It's like a platypus, Sun didn't have the
- * SRMMU in conception so they kludged the three
- * level logic in the regular Sun4 MMU probably.
- *
- * Basically, you take each entry in the top level
- * directory that maps the low 3MB starting at
- * address zero and put the mapping in the KERNBASE
- * slots. These top level pgd's are called regmaps.
- */
-sun4_mutant_remap:
- or %g0, %g0, %g3 ! source base
- sethi %hi(KERNBASE), %g4 ! destination base
- or %g4, %lo(KERNBASE), %g4
- sethi %hi(0x300000), %g5
- or %g5, %lo(0x300000), %g5 ! upper bound 3MB
- or %g0, 0x1, %l6
- sll %l6, 24, %l6 ! Regmap mapping size
- add %g3, 0x2, %g3 ! Base magic
- add %g4, 0x2, %g4 ! Base magic
-
- /* Main remapping loop on Sun4-Mutant-MMU.
- * "I am not an animal..." -Famous Mutant Person
- */
-sun4_mutant_loop:
- lduha [%g3] ASI_REGMAP, %g2 ! Get lower entry
- stha %g2, [%g4] ASI_REGMAP ! Store in high entry
- add %g4, %l6, %g4 ! Move up high memory ptr
- subcc %g3, %g5, %g0 ! Reached our limit?
- blu sun4_mutant_loop ! Nope, loop again
- add %g3, %l6, %g3 ! delay, Move up low ptr
- b go_to_highmem ! Jump to high memory.
- nop
-
- /* The following is for non-4/4xx sun4 MMU's. */
-sun4_normal_remap:
- mov 0, %g3 ! source base
- set KERNBASE, %g4 ! destination base
- set 0x300000, %g5 ! upper bound 3MB
- mov 1, %l6
- sll %l6, 18, %l6 ! sun4 mmu segmap size
-sun4_normal_loop:
- lduha [%g3] ASI_SEGMAP, %g6 ! load phys_seg
- stha %g6, [%g4] ASI_SEGMAP ! stort new virt mapping
- add %g3, %l6, %g3 ! increment source pointer
- subcc %g3, %g5, %g0 ! reached limit?
- blu sun4_normal_loop ! nope, loop again
- add %g4, %l6, %g4 ! delay, increment dest ptr
- b go_to_highmem
- nop
-
- /* The following works for Sun4c MMU's */
-sun4c_remap:
- mov 0, %g3 ! source base
- set KERNBASE, %g4 ! destination base
- set 0x300000, %g5 ! upper bound 3MB
- mov 1, %l6
- sll %l6, 18, %l6 ! sun4c mmu segmap size
-sun4c_remap_loop:
- lda [%g3] ASI_SEGMAP, %g6 ! load phys_seg
- sta %g6, [%g4] ASI_SEGMAP ! store new virt mapping
- add %g3, %l6, %g3 ! Increment source ptr
- subcc %g3, %g5, %g0 ! Reached limit?
- bl sun4c_remap_loop ! Nope, loop again
- add %g4, %l6, %g4 ! delay, Increment dest ptr
-
-/* Now do a non-relative jump so that PC is in high-memory */
-go_to_highmem:
- set execute_in_high_mem, %g1
- jmpl %g1, %g0
- nop
-
-/* The code above should be at beginning and we have to take care about
- * short jumps, as branching to .text.init section from .text is usually
- * impossible */
- __INIT
-/* Acquire boot time privileged register values, this will help debugging.
- * I figure out and store nwindows and nwindowsm1 later on.
- */
-execute_in_high_mem:
- mov %l0, %o0 ! put back romvec
- mov %l1, %o1 ! and debug_vec
-
- sethi %hi(prom_vector_p), %g1
- st %o0, [%g1 + %lo(prom_vector_p)]
-
- sethi %hi(linux_dbvec), %g1
- st %o1, [%g1 + %lo(linux_dbvec)]
-
- ld [%o0 + 0x4], %o3
- and %o3, 0x3, %o5 ! get the version
-
- cmp %o3, 0x2 ! a v2 prom?
- be found_version
- nop
-
- /* paul@sfe.com.au */
- cmp %o3, 0x3 ! a v3 prom?
- be found_version
- nop
-
-/* Old sun4's pass our load address into %o0 instead of the prom
- * pointer. On sun4's you have to hard code the romvec pointer into
- * your code. Sun probably still does that because they don't even
- * trust their own "OpenBoot" specifications.
- */
- set LOAD_ADDR, %g6
- cmp %o0, %g6 ! an old sun4?
- be sun4_init
- nop
-
-found_version:
-/* Get the machine type via the mysterious romvec node operations. */
-
- add %g7, 0x1c, %l1
- ld [%l1], %l0
- ld [%l0], %l0
- call %l0
- or %g0, %g0, %o0 ! next_node(0) = first_node
- or %o0, %g0, %g6
-
- sethi %hi(cputypvar), %o1 ! First node has cpu-arch
- or %o1, %lo(cputypvar), %o1
- sethi %hi(cputypval), %o2 ! information, the string
- or %o2, %lo(cputypval), %o2
- ld [%l1], %l0 ! 'compatibility' tells
- ld [%l0 + 0xc], %l0 ! that we want 'sun4x' where
- call %l0 ! x is one of '', 'c', 'm',
- nop ! 'd' or 'e'. %o2 holds pointer
- ! to a buf where above string
- ! will get stored by the prom.
-
- subcc %o0, %g0, %g0
- bpos got_prop ! Got the property
- nop
-
- or %g6, %g0, %o0
- sethi %hi(cputypvar_sun4m), %o1
- or %o1, %lo(cputypvar_sun4m), %o1
- sethi %hi(cputypval), %o2
- or %o2, %lo(cputypval), %o2
- ld [%l1], %l0
- ld [%l0 + 0xc], %l0
- call %l0
- nop
-
-got_prop:
- set cputypval, %o2
- ldub [%o2 + 0x4], %l1
-
- cmp %l1, ' '
- be 1f
- cmp %l1, 'c'
- be 1f
- cmp %l1, 'm'
- be 1f
- cmp %l1, 's'
- be 1f
- cmp %l1, 'd'
- be 1f
- cmp %l1, 'e'
- be no_sun4e_here ! Could be a sun4e.
- nop
- b no_sun4u_here ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
- nop
-
-1: set cputypval, %l1
- ldub [%l1 + 0x4], %l1
- cmp %l1, 'm' ! Test for sun4d, sun4e ?
- be sun4m_init
- cmp %l1, 's' ! Treat sun4s as sun4m
- be sun4m_init
- cmp %l1, 'd' ! Let us see how the beast will die
- be sun4d_init
- nop
-
- /* Jump into mmu context zero. */
- set AC_CONTEXT, %g1
- stba %g0, [%g1] ASI_CONTROL
-
- b sun4c_continue_boot
- nop
-
-/* CPUID in bootbus can be found at PA 0xff0140000 */
-#define SUN4D_BOOTBUS_CPUID 0xf0140000
-
-sun4d_init:
- /* Need to patch call to handler_irq */
- set patch_handler_irq, %g4
- set sun4d_handler_irq, %g5
- sethi %hi(0x40000000), %g3 ! call
- sub %g5, %g4, %g5
- srl %g5, 2, %g5
- or %g5, %g3, %g5
- st %g5, [%g4]
-
-#ifdef CONFIG_SMP
- /* Get our CPU id out of bootbus */
- set SUN4D_BOOTBUS_CPUID, %g3
- lduba [%g3] ASI_M_CTL, %g3
- and %g3, 0xf8, %g3
- srl %g3, 3, %g4
- sta %g4, [%g0] ASI_M_VIKING_TMP1
- sethi %hi(boot_cpu_id), %g5
- stb %g4, [%g5 + %lo(boot_cpu_id)]
- sll %g4, 2, %g4
- sethi %hi(boot_cpu_id4), %g5
- stb %g4, [%g5 + %lo(boot_cpu_id4)]
-#endif
-
- /* Fall through to sun4m_init */
-
-sun4m_init:
- /* XXX Fucking Cypress... */
- lda [%g0] ASI_M_MMUREGS, %g5
- srl %g5, 28, %g4
-
- cmp %g4, 1
- bne 1f
- srl %g5, 24, %g4
-
- and %g4, 0xf, %g4
- cmp %g4, 7 /* This would be a HyperSparc. */
-
- bne 2f
- nop
-
-1:
-
-#define PATCH_IT(dst, src) \
- set (dst), %g5; \
- set (src), %g4; \
- ld [%g4], %g3; \
- st %g3, [%g5]; \
- ld [%g4+0x4], %g3; \
- st %g3, [%g5+0x4];
-
- /* Signed multiply. */
- PATCH_IT(.mul, .mul_patch)
- PATCH_IT(.mul+0x08, .mul_patch+0x08)
-
- /* Signed remainder. */
- PATCH_IT(.rem, .rem_patch)
- PATCH_IT(.rem+0x08, .rem_patch+0x08)
- PATCH_IT(.rem+0x10, .rem_patch+0x10)
- PATCH_IT(.rem+0x18, .rem_patch+0x18)
- PATCH_IT(.rem+0x20, .rem_patch+0x20)
- PATCH_IT(.rem+0x28, .rem_patch+0x28)
-
- /* Signed division. */
- PATCH_IT(.div, .div_patch)
- PATCH_IT(.div+0x08, .div_patch+0x08)
- PATCH_IT(.div+0x10, .div_patch+0x10)
- PATCH_IT(.div+0x18, .div_patch+0x18)
- PATCH_IT(.div+0x20, .div_patch+0x20)
-
- /* Unsigned multiply. */
- PATCH_IT(.umul, .umul_patch)
- PATCH_IT(.umul+0x08, .umul_patch+0x08)
-
- /* Unsigned remainder. */
- PATCH_IT(.urem, .urem_patch)
- PATCH_IT(.urem+0x08, .urem_patch+0x08)
- PATCH_IT(.urem+0x10, .urem_patch+0x10)
- PATCH_IT(.urem+0x18, .urem_patch+0x18)
-
- /* Unsigned division. */
- PATCH_IT(.udiv, .udiv_patch)
- PATCH_IT(.udiv+0x08, .udiv_patch+0x08)
- PATCH_IT(.udiv+0x10, .udiv_patch+0x10)
-
-#undef PATCH_IT
-
-/* Ok, the PROM could have done funny things and apple cider could still
- * be sitting in the fault status/address registers. Read them all to
- * clear them so we don't get magic faults later on.
- */
-/* This sucks, apparently this makes Vikings call prom panic, will fix later */
-2:
- rd %psr, %o1
- srl %o1, 28, %o1 ! Get a type of the CPU
-
- subcc %o1, 4, %g0 ! TI: Viking or MicroSPARC
- be sun4c_continue_boot
- nop
-
- set AC_M_SFSR, %o0
- lda [%o0] ASI_M_MMUREGS, %g0
- set AC_M_SFAR, %o0
- lda [%o0] ASI_M_MMUREGS, %g0
-
- /* Fujitsu MicroSPARC-II has no asynchronous flavors of FARs */
- subcc %o1, 0, %g0
- be sun4c_continue_boot
- nop
-
- set AC_M_AFSR, %o0
- lda [%o0] ASI_M_MMUREGS, %g0
- set AC_M_AFAR, %o0
- lda [%o0] ASI_M_MMUREGS, %g0
- nop
-
-
-sun4c_continue_boot:
-
-
-/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
- * show-time!
- */
-
- sethi %hi(cputyp), %o0
- st %g4, [%o0 + %lo(cputyp)]
-
- /* Turn on Supervisor, EnableFloating, and all the PIL bits.
- * Also puts us in register window zero with traps off.
- */
- set (PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
- wr %g2, 0x0, %psr
- WRITE_PAUSE
-
- /* I want a kernel stack NOW! */
- set init_thread_union, %g1
- set (THREAD_SIZE - STACKFRAME_SZ), %g2
- add %g1, %g2, %sp
- mov 0, %fp /* And for good luck */
-
- /* Zero out our BSS section. */
- set __bss_start , %o0 ! First address of BSS
- set end , %o1 ! Last address of BSS
- add %o0, 0x1, %o0
-1:
- stb %g0, [%o0]
- subcc %o0, %o1, %g0
- bl 1b
- add %o0, 0x1, %o0
-
- /* Initialize the uwinmask value for init task just in case.
- * But first make current_set[boot_cpu_id] point to something useful.
- */
- set init_thread_union, %g6
- set current_set, %g2
-#ifdef CONFIG_SMP
- sethi %hi(boot_cpu_id4), %g3
- ldub [%g3 + %lo(boot_cpu_id4)], %g3
- st %g6, [%g2]
- add %g2, %g3, %g2
-#endif
- st %g6, [%g2]
-
- st %g0, [%g6 + TI_UWINMASK]
-
-/* Compute NWINDOWS and stash it away. Now uses %wim trick explained
- * in the V8 manual. Ok, this method seems to work, Sparc is cool...
- * No, it doesn't work, have to play the save/readCWP/restore trick.
- */
-
- wr %g0, 0x0, %wim ! so we do not get a trap
- WRITE_PAUSE
-
- save
-
- rd %psr, %g3
-
- restore
-
- and %g3, 0x1f, %g3
- add %g3, 0x1, %g3
-
- mov 2, %g1
- wr %g1, 0x0, %wim ! make window 1 invalid
- WRITE_PAUSE
-
- cmp %g3, 0x7
- bne 2f
- nop
-
- /* Adjust our window handling routines to
- * do things correctly on 7 window Sparcs.
- */
-
-#define PATCH_INSN(src, dest) \
- set src, %g5; \
- set dest, %g2; \
- ld [%g5], %g4; \
- st %g4, [%g2];
-
- /* Patch for window spills... */
- PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
- PATCH_INSN(spnwin_patch2_7win, spnwin_patch2)
- PATCH_INSN(spnwin_patch3_7win, spnwin_patch3)
-
- /* Patch for window fills... */
- PATCH_INSN(fnwin_patch1_7win, fnwin_patch1)
- PATCH_INSN(fnwin_patch2_7win, fnwin_patch2)
-
- /* Patch for trap entry setup... */
- PATCH_INSN(tsetup_7win_patch1, tsetup_patch1)
- PATCH_INSN(tsetup_7win_patch2, tsetup_patch2)
- PATCH_INSN(tsetup_7win_patch3, tsetup_patch3)
- PATCH_INSN(tsetup_7win_patch4, tsetup_patch4)
- PATCH_INSN(tsetup_7win_patch5, tsetup_patch5)
- PATCH_INSN(tsetup_7win_patch6, tsetup_patch6)
-
- /* Patch for returning from traps... */
- PATCH_INSN(rtrap_7win_patch1, rtrap_patch1)
- PATCH_INSN(rtrap_7win_patch2, rtrap_patch2)
- PATCH_INSN(rtrap_7win_patch3, rtrap_patch3)
- PATCH_INSN(rtrap_7win_patch4, rtrap_patch4)
- PATCH_INSN(rtrap_7win_patch5, rtrap_patch5)
-
- /* Patch for killing user windows from the register file. */
- PATCH_INSN(kuw_patch1_7win, kuw_patch1)
-
- /* Now patch the kernel window flush sequences.
- * This saves 2 traps on every switch and fork.
- */
- set 0x01000000, %g4
- set flush_patch_one, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
- set flush_patch_two, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
- set flush_patch_three, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
- set flush_patch_four, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
- set flush_patch_exception, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
- set flush_patch_switch, %g5
- st %g4, [%g5 + 0x18]
- st %g4, [%g5 + 0x1c]
-
-2:
- sethi %hi(nwindows), %g4
- st %g3, [%g4 + %lo(nwindows)] ! store final value
- sub %g3, 0x1, %g3
- sethi %hi(nwindowsm1), %g4
- st %g3, [%g4 + %lo(nwindowsm1)]
-
- /* Here we go, start using Linux's trap table... */
- set trapbase, %g3
- wr %g3, 0x0, %tbr
- WRITE_PAUSE
-
- /* Finally, turn on traps so that we can call c-code. */
- rd %psr, %g3
- wr %g3, 0x0, %psr
- WRITE_PAUSE
-
- wr %g3, PSR_ET, %psr
- WRITE_PAUSE
-
- /* First we call prom_init() to set up PROMLIB, then
- * off to start_kernel().
- */
-
- sethi %hi(prom_vector_p), %g5
- ld [%g5 + %lo(prom_vector_p)], %o0
- call prom_init
- nop
-
- call start_kernel
- nop
-
- /* We should not get here. */
- call halt_me
- nop
-
-sun4_init:
- sethi %hi(SUN4_PROM_VECTOR+0x84), %o1
- ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
- set sun4_notsup, %o0
- call %o1 /* printf */
- nop
- sethi %hi(SUN4_PROM_VECTOR+0xc4), %o1
- ld [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1
- call %o1 /* exittomon */
- nop
-1: ba 1b ! Cannot exit into KMON
- nop
-
-no_sun4e_here:
- ld [%g7 + 0x68], %o1
- set sun4e_notsup, %o0
- call %o1
- nop
- b halt_me
- nop
-
- __INITDATA
-
-sun4u_1:
- .asciz "finddevice"
- .align 4
-sun4u_2:
- .asciz "/chosen"
- .align 4
-sun4u_3:
- .asciz "getprop"
- .align 4
-sun4u_4:
- .asciz "stdout"
- .align 4
-sun4u_5:
- .asciz "write"
- .align 4
-sun4u_6:
- .asciz "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
-sun4u_6e:
- .align 4
-sun4u_7:
- .asciz "exit"
- .align 8
-sun4u_a1:
- .word 0, sun4u_1, 0, 1, 0, 1, 0, sun4u_2, 0
-sun4u_r1:
- .word 0
-sun4u_a2:
- .word 0, sun4u_3, 0, 4, 0, 1, 0
-sun4u_i2:
- .word 0, 0, sun4u_4, 0, sun4u_1, 0, 8, 0
-sun4u_r2:
- .word 0
-sun4u_a3:
- .word 0, sun4u_5, 0, 3, 0, 1, 0
-sun4u_i3:
- .word 0, 0, sun4u_6, 0, sun4u_6e - sun4u_6 - 1, 0
-sun4u_r3:
- .word 0
-sun4u_a4:
- .word 0, sun4u_7, 0, 0, 0, 0
-sun4u_r4:
-
- __INIT
-no_sun4u_here:
- set sun4u_a1, %o0
- set current_pc, %l2
- cmp %l2, %g3
- be 1f
- mov %o4, %l0
- sub %g3, %l2, %l6
- add %o0, %l6, %o0
- mov %o0, %l4
- mov sun4u_r4 - sun4u_a1, %l3
- ld [%l4], %l5
-2:
- add %l4, 4, %l4
- cmp %l5, %l2
- add %l5, %l6, %l5
- bgeu,a 3f
- st %l5, [%l4 - 4]
-3:
- subcc %l3, 4, %l3
- bne 2b
- ld [%l4], %l5
-1:
- call %l0
- mov %o0, %l1
-
- ld [%l1 + (sun4u_r1 - sun4u_a1)], %o1
- add %l1, (sun4u_a2 - sun4u_a1), %o0
- call %l0
- st %o1, [%o0 + (sun4u_i2 - sun4u_a2)]
-
- ld [%l1 + (sun4u_1 - sun4u_a1)], %o1
- add %l1, (sun4u_a3 - sun4u_a1), %o0
- call %l0
- st %o1, [%o0 + (sun4u_i3 - sun4u_a3)]
-
- call %l0
- add %l1, (sun4u_a4 - sun4u_a1), %o0
-
- /* Not reached */
-halt_me:
- ld [%g7 + 0x74], %o0
- call %o0 ! Get us out of here...
- nop ! Apparently Solaris is better.
-
-/* Ok, now we continue in the .data/.text sections */
-
- .data
- .align 4
-
-/*
- * Fill up the prom vector, note in particular the kind first element,
- * no joke. I don't need all of them in here as the entire prom vector
- * gets initialized in c-code so all routines can use it.
- */
-
-prom_vector_p:
- .word 0
-
-/* We calculate the following at boot time, window fills/spills and trap entry
- * code uses these to keep track of the register windows.
- */
-
- .align 4
- .globl nwindows
- .globl nwindowsm1
-nwindows:
- .word 8
-nwindowsm1:
- .word 7
-
-/* Boot time debugger vector value. We need this later on. */
-
- .align 4
- .globl linux_dbvec
-linux_dbvec:
- .word 0
- .word 0
-
- .align 8
-
- .globl lvl14_save
-lvl14_save:
- .word 0
- .word 0
- .word 0
- .word 0
- .word t_irq14
-
- .section ".fixup",#alloc,#execinstr
- .globl __ret_efault
-__ret_efault:
- ret
- restore %g0, -EFAULT, %o0
diff --git a/arch/sparc/kernel/head_32.S b/arch/sparc/kernel/head_32.S
new file mode 100644
index 000000000000..f0b4b516304f
--- /dev/null
+++ b/arch/sparc/kernel/head_32.S
@@ -0,0 +1,1295 @@
+/*
+ * head.S: The initial boot code for the Sparc port of Linux.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,1999 Pete Zaitcev (zaitcev@yahoo.com)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
+ *
+ * CompactPCI platform by Eric Brower, 1999.
+ */
+
+#include <linux/version.h>
+#include <linux/init.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/page.h>
+#include <asm/kdebug.h>
+#include <asm/winmacro.h>
+#include <asm/thread_info.h> /* TI_UWINMASK */
+#include <asm/errno.h>
+#include <asm/pgtsrmmu.h> /* SRMMU_PGDIR_SHIFT */
+
+ .data
+/*
+ * The following are used with the prom_vector node-ops to figure out
+ * the cpu-type
+ */
+
+ .align 4
+cputyp:
+ .word 1
+
+ .align 4
+ .globl cputypval
+cputypval:
+ .asciz "sun4c"
+ .ascii " "
+
+cputypvalend:
+cputypvallen = cputypvar - cputypval
+
+ .align 4
+/*
+ * Sun people can't spell worth damn. "compatability" indeed.
+ * At least we *know* we can't spell, and use a spell-checker.
+ */
+
+/* Uh, actually Linus it is I who cannot spell. Too much murky
+ * Sparc assembly will do this to ya.
+ */
+cputypvar:
+ .asciz "compatability"
+
+/* Tested on SS-5, SS-10. Probably someone at Sun applied a spell-checker. */
+ .align 4
+cputypvar_sun4m:
+ .asciz "compatible"
+
+ .align 4
+
+sun4_notsup:
+ .asciz "Sparc-Linux sun4 support does no longer exist.\n\n"
+ .align 4
+
+sun4e_notsup:
+ .asciz "Sparc-Linux sun4e support does not exist\n\n"
+ .align 4
+
+ /* The Sparc trap table, bootloader gives us control at _start. */
+ .section .text.head,"ax"
+ .globl start, _stext, _start, __stext
+ .globl trapbase
+_start: /* danger danger */
+__stext:
+_stext:
+start:
+trapbase:
+#ifdef CONFIG_SMP
+trapbase_cpu0:
+#endif
+/* We get control passed to us here at t_zero. */
+t_zero: b gokernel; nop; nop; nop;
+t_tflt: SPARC_TFAULT /* Inst. Access Exception */
+t_bins: TRAP_ENTRY(0x2, bad_instruction) /* Illegal Instruction */
+t_pins: TRAP_ENTRY(0x3, priv_instruction) /* Privileged Instruction */
+t_fpd: TRAP_ENTRY(0x4, fpd_trap_handler) /* Floating Point Disabled */
+t_wovf: WINDOW_SPILL /* Window Overflow */
+t_wunf: WINDOW_FILL /* Window Underflow */
+t_mna: TRAP_ENTRY(0x7, mna_handler) /* Memory Address Not Aligned */
+t_fpe: TRAP_ENTRY(0x8, fpe_trap_handler) /* Floating Point Exception */
+t_dflt: SPARC_DFAULT /* Data Miss Exception */
+t_tio: TRAP_ENTRY(0xa, do_tag_overflow) /* Tagged Instruction Ovrflw */
+t_wpt: TRAP_ENTRY(0xb, do_watchpoint) /* Watchpoint Detected */
+t_badc: BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+t_irq1: TRAP_ENTRY_INTERRUPT(1) /* IRQ Software/SBUS Level 1 */
+t_irq2: TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */
+t_irq3: TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */
+t_irq4: TRAP_ENTRY_INTERRUPT(4) /* IRQ Software Level 4 */
+t_irq5: TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */
+t_irq6: TRAP_ENTRY_INTERRUPT(6) /* IRQ Software Level 6 */
+t_irq7: TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */
+t_irq8: TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */
+t_irq9: TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */
+t_irq10:TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 (one we use) */
+t_irq11:TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */
+t_irq12:TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */
+t_irq13:TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */
+t_irq14:TRAP_ENTRY_INTERRUPT(14) /* IRQ Timer #2 */
+ .globl t_nmi
+#ifndef CONFIG_SMP
+t_nmi: NMI_TRAP /* Level 15 (NMI) */
+#else
+t_nmi: TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+#endif
+t_racc: TRAP_ENTRY(0x20, do_reg_access) /* General Register Access Error */
+t_iacce:BAD_TRAP(0x21) /* Instr Access Error */
+t_bad22:BAD_TRAP(0x22) BAD_TRAP(0x23)
+t_cpdis:TRAP_ENTRY(0x24, do_cp_disabled) /* Co-Processor Disabled */
+t_uflsh:SKIP_TRAP(0x25, unimp_flush) /* Unimplemented FLUSH inst. */
+t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
+t_cpexc:TRAP_ENTRY(0x28, do_cp_exception) /* Co-Processor Exception */
+t_dacce:SPARC_DFAULT /* Data Access Error */
+t_hwdz: TRAP_ENTRY(0x2a, do_hw_divzero) /* Division by zero, you lose... */
+t_dserr:BAD_TRAP(0x2b) /* Data Store Error */
+t_daccm:BAD_TRAP(0x2c) /* Data Access MMU-Miss */
+t_bad2d:BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+t_bad32:BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+t_bad37:BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+t_iaccm:BAD_TRAP(0x3c) /* Instr Access MMU-Miss */
+t_bad3d:BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40) BAD_TRAP(0x41)
+t_bad42:BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45) BAD_TRAP(0x46)
+t_bad47:BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a) BAD_TRAP(0x4b)
+t_bad4c:BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f) BAD_TRAP(0x50)
+t_bad51:BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+t_bad56:BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+t_bad5b:BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+t_bad60:BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+t_bad65:BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+t_bad6a:BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+t_bad6f:BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+t_bad74:BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+t_bad79:BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+t_bad7e:BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+t_bad80:BAD_TRAP(0x80) /* SunOS System Call */
+t_sbkpt:BREAKPOINT_TRAP /* Software Breakpoint/KGDB */
+t_divz: TRAP_ENTRY(0x82, do_hw_divzero) /* Divide by zero trap */
+t_flwin:TRAP_ENTRY(0x83, do_flush_windows) /* Flush Windows Trap */
+t_clwin:BAD_TRAP(0x84) /* Clean Windows Trap */
+t_rchk: BAD_TRAP(0x85) /* Range Check */
+t_funal:BAD_TRAP(0x86) /* Fix Unaligned Access Trap */
+t_iovf: BAD_TRAP(0x87) /* Integer Overflow Trap */
+t_bad88:BAD_TRAP(0x88) /* Slowaris System Call */
+t_bad89:BAD_TRAP(0x89) /* Net-B.S. System Call */
+t_bad8a:BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c) BAD_TRAP(0x8d) BAD_TRAP(0x8e)
+t_bad8f:BAD_TRAP(0x8f)
+t_linux:LINUX_SYSCALL_TRAP /* Linux System Call */
+t_bad91:BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94) BAD_TRAP(0x95)
+t_bad96:BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99) BAD_TRAP(0x9a)
+t_bad9b:BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e) BAD_TRAP(0x9f)
+t_getcc:GETCC_TRAP /* Get Condition Codes */
+t_setcc:SETCC_TRAP /* Set Condition Codes */
+t_getpsr:GETPSR_TRAP /* Get PSR Register */
+t_bada3:BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+t_bada7:BAD_TRAP(0xa7)
+t_bada8:BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+t_badac:BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+t_badb1:BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+t_badb6:BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+t_badbb:BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+t_badc0:BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+t_badc5:BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+t_badca:BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+t_badcf:BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+t_badd4:BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+t_badd9:BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+t_badde:BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+t_bade3:BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+t_badfc:BAD_TRAP(0xfc)
+t_kgdb: KGDB_TRAP(0xfd)
+dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */
+dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */
+
+ .globl end_traptable
+end_traptable:
+
+#ifdef CONFIG_SMP
+ /* Trap tables for the other cpus. */
+ .globl trapbase_cpu1, trapbase_cpu2, trapbase_cpu3
+trapbase_cpu1:
+ BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+ TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+ WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+ TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+ TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+ BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+ TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+ TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+ TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+ TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
+ TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+ TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+ TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+ TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+ TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+ BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+ SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+ BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+ BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+ BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+ BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+ BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+ BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+ BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+ BAD_TRAP(0x50)
+ BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+ BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+ BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+ BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+ BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+ BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+ BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+ BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+ BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+ BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+ BAD_TRAP(0x80)
+ BREAKPOINT_TRAP
+ TRAP_ENTRY(0x82, do_hw_divzero)
+ TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+ BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
+ BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+ BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+ BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+ BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+ BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+ BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+ BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+ BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+ BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+ BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+ BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+ BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+ BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+ BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+ BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+ BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+ BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+ BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+ BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+ BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+ BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+ BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+ BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+ BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+trapbase_cpu2:
+ BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+ TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+ WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+ TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+ TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+ BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+ TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+ TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+ TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+ TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
+ TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+ TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+ TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+ TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+ TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+ BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+ SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+ BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+ BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+ BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+ BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+ BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+ BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+ BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+ BAD_TRAP(0x50)
+ BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+ BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+ BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+ BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+ BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+ BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+ BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+ BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+ BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+ BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+ BAD_TRAP(0x80)
+ BREAKPOINT_TRAP
+ TRAP_ENTRY(0x82, do_hw_divzero)
+ TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+ BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
+ BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+ BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+ BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+ BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+ BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+ BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+ BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+ BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+ BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+ BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+ BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+ BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+ BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+ BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+ BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+ BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+ BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+ BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+ BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+ BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+ BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+ BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+ BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+ BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+trapbase_cpu3:
+ BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction)
+ TRAP_ENTRY(0x3, priv_instruction) TRAP_ENTRY(0x4, fpd_trap_handler)
+ WINDOW_SPILL WINDOW_FILL TRAP_ENTRY(0x7, mna_handler)
+ TRAP_ENTRY(0x8, fpe_trap_handler) SRMMU_DFAULT
+ TRAP_ENTRY(0xa, do_tag_overflow) TRAP_ENTRY(0xb, do_watchpoint)
+ BAD_TRAP(0xc) BAD_TRAP(0xd) BAD_TRAP(0xe) BAD_TRAP(0xf) BAD_TRAP(0x10)
+ TRAP_ENTRY_INTERRUPT(1) TRAP_ENTRY_INTERRUPT(2)
+ TRAP_ENTRY_INTERRUPT(3) TRAP_ENTRY_INTERRUPT(4)
+ TRAP_ENTRY_INTERRUPT(5) TRAP_ENTRY_INTERRUPT(6)
+ TRAP_ENTRY_INTERRUPT(7) TRAP_ENTRY_INTERRUPT(8)
+ TRAP_ENTRY_INTERRUPT(9) TRAP_ENTRY_INTERRUPT(10)
+ TRAP_ENTRY_INTERRUPT(11) TRAP_ENTRY_INTERRUPT(12)
+ TRAP_ENTRY_INTERRUPT(13) TRAP_ENTRY_INTERRUPT(14)
+ TRAP_ENTRY(0x1f, linux_trap_ipi15_sun4m)
+ TRAP_ENTRY(0x20, do_reg_access) BAD_TRAP(0x21) BAD_TRAP(0x22)
+ BAD_TRAP(0x23) TRAP_ENTRY(0x24, do_cp_disabled) SKIP_TRAP(0x25, unimp_flush)
+ BAD_TRAP(0x26) BAD_TRAP(0x27) TRAP_ENTRY(0x28, do_cp_exception)
+ SRMMU_DFAULT TRAP_ENTRY(0x2a, do_hw_divzero) BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+ BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
+ BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
+ BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)
+ BAD_TRAP(0x3c) BAD_TRAP(0x3d) BAD_TRAP(0x3e) BAD_TRAP(0x3f) BAD_TRAP(0x40)
+ BAD_TRAP(0x41) BAD_TRAP(0x42) BAD_TRAP(0x43) BAD_TRAP(0x44) BAD_TRAP(0x45)
+ BAD_TRAP(0x46) BAD_TRAP(0x47) BAD_TRAP(0x48) BAD_TRAP(0x49) BAD_TRAP(0x4a)
+ BAD_TRAP(0x4b) BAD_TRAP(0x4c) BAD_TRAP(0x4d) BAD_TRAP(0x4e) BAD_TRAP(0x4f)
+ BAD_TRAP(0x50)
+ BAD_TRAP(0x51) BAD_TRAP(0x52) BAD_TRAP(0x53) BAD_TRAP(0x54) BAD_TRAP(0x55)
+ BAD_TRAP(0x56) BAD_TRAP(0x57) BAD_TRAP(0x58) BAD_TRAP(0x59) BAD_TRAP(0x5a)
+ BAD_TRAP(0x5b) BAD_TRAP(0x5c) BAD_TRAP(0x5d) BAD_TRAP(0x5e) BAD_TRAP(0x5f)
+ BAD_TRAP(0x60) BAD_TRAP(0x61) BAD_TRAP(0x62) BAD_TRAP(0x63) BAD_TRAP(0x64)
+ BAD_TRAP(0x65) BAD_TRAP(0x66) BAD_TRAP(0x67) BAD_TRAP(0x68) BAD_TRAP(0x69)
+ BAD_TRAP(0x6a) BAD_TRAP(0x6b) BAD_TRAP(0x6c) BAD_TRAP(0x6d) BAD_TRAP(0x6e)
+ BAD_TRAP(0x6f) BAD_TRAP(0x70) BAD_TRAP(0x71) BAD_TRAP(0x72) BAD_TRAP(0x73)
+ BAD_TRAP(0x74) BAD_TRAP(0x75) BAD_TRAP(0x76) BAD_TRAP(0x77) BAD_TRAP(0x78)
+ BAD_TRAP(0x79) BAD_TRAP(0x7a) BAD_TRAP(0x7b) BAD_TRAP(0x7c) BAD_TRAP(0x7d)
+ BAD_TRAP(0x7e) BAD_TRAP(0x7f)
+ BAD_TRAP(0x80)
+ BREAKPOINT_TRAP
+ TRAP_ENTRY(0x82, do_hw_divzero)
+ TRAP_ENTRY(0x83, do_flush_windows) BAD_TRAP(0x84) BAD_TRAP(0x85)
+ BAD_TRAP(0x86) BAD_TRAP(0x87) BAD_TRAP(0x88)
+ BAD_TRAP(0x89) BAD_TRAP(0x8a) BAD_TRAP(0x8b) BAD_TRAP(0x8c)
+ BAD_TRAP(0x8d) BAD_TRAP(0x8e) BAD_TRAP(0x8f)
+ LINUX_SYSCALL_TRAP BAD_TRAP(0x91) BAD_TRAP(0x92) BAD_TRAP(0x93) BAD_TRAP(0x94)
+ BAD_TRAP(0x95) BAD_TRAP(0x96) BAD_TRAP(0x97) BAD_TRAP(0x98) BAD_TRAP(0x99)
+ BAD_TRAP(0x9a) BAD_TRAP(0x9b) BAD_TRAP(0x9c) BAD_TRAP(0x9d) BAD_TRAP(0x9e)
+ BAD_TRAP(0x9f) GETCC_TRAP SETCC_TRAP GETPSR_TRAP
+ BAD_TRAP(0xa3) BAD_TRAP(0xa4) BAD_TRAP(0xa5) BAD_TRAP(0xa6)
+ BAD_TRAP(0xa7) BAD_TRAP(0xa8) BAD_TRAP(0xa9) BAD_TRAP(0xaa) BAD_TRAP(0xab)
+ BAD_TRAP(0xac) BAD_TRAP(0xad) BAD_TRAP(0xae) BAD_TRAP(0xaf) BAD_TRAP(0xb0)
+ BAD_TRAP(0xb1) BAD_TRAP(0xb2) BAD_TRAP(0xb3) BAD_TRAP(0xb4) BAD_TRAP(0xb5)
+ BAD_TRAP(0xb6) BAD_TRAP(0xb7) BAD_TRAP(0xb8) BAD_TRAP(0xb9) BAD_TRAP(0xba)
+ BAD_TRAP(0xbb) BAD_TRAP(0xbc) BAD_TRAP(0xbd) BAD_TRAP(0xbe) BAD_TRAP(0xbf)
+ BAD_TRAP(0xc0) BAD_TRAP(0xc1) BAD_TRAP(0xc2) BAD_TRAP(0xc3) BAD_TRAP(0xc4)
+ BAD_TRAP(0xc5) BAD_TRAP(0xc6) BAD_TRAP(0xc7) BAD_TRAP(0xc8) BAD_TRAP(0xc9)
+ BAD_TRAP(0xca) BAD_TRAP(0xcb) BAD_TRAP(0xcc) BAD_TRAP(0xcd) BAD_TRAP(0xce)
+ BAD_TRAP(0xcf) BAD_TRAP(0xd0) BAD_TRAP(0xd1) BAD_TRAP(0xd2) BAD_TRAP(0xd3)
+ BAD_TRAP(0xd4) BAD_TRAP(0xd5) BAD_TRAP(0xd6) BAD_TRAP(0xd7) BAD_TRAP(0xd8)
+ BAD_TRAP(0xd9) BAD_TRAP(0xda) BAD_TRAP(0xdb) BAD_TRAP(0xdc) BAD_TRAP(0xdd)
+ BAD_TRAP(0xde) BAD_TRAP(0xdf) BAD_TRAP(0xe0) BAD_TRAP(0xe1) BAD_TRAP(0xe2)
+ BAD_TRAP(0xe3) BAD_TRAP(0xe4) BAD_TRAP(0xe5) BAD_TRAP(0xe6) BAD_TRAP(0xe7)
+ BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xec)
+ BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1)
+ BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6)
+ BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb)
+ BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff)
+
+#endif
+ .align PAGE_SIZE
+
+/* This was the only reasonable way I could think of to properly align
+ * these page-table data structures.
+ */
+ .globl pg0, pg1, pg2, pg3
+ .globl empty_bad_page
+ .globl empty_bad_page_table
+ .globl empty_zero_page
+ .globl swapper_pg_dir
+swapper_pg_dir: .skip PAGE_SIZE
+pg0: .skip PAGE_SIZE
+pg1: .skip PAGE_SIZE
+pg2: .skip PAGE_SIZE
+pg3: .skip PAGE_SIZE
+empty_bad_page: .skip PAGE_SIZE
+empty_bad_page_table: .skip PAGE_SIZE
+empty_zero_page: .skip PAGE_SIZE
+
+ .global root_flags
+ .global ram_flags
+ .global root_dev
+ .global sparc_ramdisk_image
+ .global sparc_ramdisk_size
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+ .ascii "HdrS"
+ .word LINUX_VERSION_CODE
+ .half 0x0203 /* HdrS version */
+root_flags:
+ .half 1
+root_dev:
+ .half 0
+ram_flags:
+ .half 0
+sparc_ramdisk_image:
+ .word 0
+sparc_ramdisk_size:
+ .word 0
+ .word reboot_command
+ .word 0, 0, 0
+ .word _end
+
+/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
+ * %g7 and at prom_vector_p. And also quickly check whether we are on
+ * a v0, v2, or v3 prom.
+ */
+gokernel:
+ /* Ok, it's nice to know, as early as possible, if we
+ * are already mapped where we expect to be in virtual
+ * memory. The Solaris /boot elf format bootloader
+ * will peek into our elf header and load us where
+ * we want to be, otherwise we have to re-map.
+ *
+ * Some boot loaders don't place the jmp'rs address
+ * in %o7, so we do a pc-relative call to a local
+ * label, then see what %o7 has.
+ */
+
+ mov %o7, %g4 ! Save %o7
+
+ /* Jump to it, and pray... */
+current_pc:
+ call 1f
+ nop
+
+1:
+ mov %o7, %g3
+
+ tst %o0
+ be no_sun4u_here
+ mov %g4, %o7 /* Previous %o7. */
+
+ mov %o0, %l0 ! stash away romvec
+ mov %o0, %g7 ! put it here too
+ mov %o1, %l1 ! stash away debug_vec too
+
+ /* Ok, let's check out our run time program counter. */
+ set current_pc, %g5
+ cmp %g3, %g5
+ be already_mapped
+ nop
+
+ /* %l6 will hold the offset we have to subtract
+ * from absolute symbols in order to access areas
+ * in our own image. If already mapped this is
+ * just plain zero, else it is KERNBASE.
+ */
+ set KERNBASE, %l6
+ b copy_prom_lvl14
+ nop
+
+already_mapped:
+ mov 0, %l6
+
+ /* Copy over the Prom's level 14 clock handler. */
+copy_prom_lvl14:
+#if 1
+ /* DJHR
+ * preserve our linked/calculated instructions
+ */
+ set lvl14_save, %g1
+ set t_irq14, %g3
+ sub %g1, %l6, %g1 ! translate to physical
+ sub %g3, %l6, %g3 ! translate to physical
+ ldd [%g3], %g4
+ std %g4, [%g1]
+ ldd [%g3+8], %g4
+ std %g4, [%g1+8]
+#endif
+ rd %tbr, %g1
+ andn %g1, 0xfff, %g1 ! proms trap table base
+ or %g0, (0x1e<<4), %g2 ! offset to lvl14 intr
+ or %g1, %g2, %g2
+ set t_irq14, %g3
+ sub %g3, %l6, %g3
+ ldd [%g2], %g4
+ std %g4, [%g3]
+ ldd [%g2 + 0x8], %g4
+ std %g4, [%g3 + 0x8] ! Copy proms handler
+
+/* Must determine whether we are on a sun4c MMU, SRMMU, or SUN4/400 MUTANT
+ * MMU so we can remap ourselves properly. DON'T TOUCH %l0 thru %l5 in these
+ * remapping routines, we need their values afterwards!
+ */
+ /* Now check whether we are already mapped, if we
+ * are we can skip all this garbage coming up.
+ */
+copy_prom_done:
+ cmp %l6, 0
+ be go_to_highmem ! this will be a nop then
+ nop
+
+ set LOAD_ADDR, %g6
+ cmp %g7, %g6
+ bne remap_not_a_sun4 ! This is not a Sun4
+ nop
+
+ or %g0, 0x1, %g1
+ lduba [%g1] ASI_CONTROL, %g1 ! Only safe to try on Sun4.
+ subcc %g1, 0x24, %g0 ! Is this a mutant Sun4/400???
+ be sun4_mutant_remap ! Ugh, it is...
+ nop
+
+ b sun4_normal_remap ! regular sun4, 2 level mmu
+ nop
+
+remap_not_a_sun4:
+ lda [%g0] ASI_M_MMUREGS, %g1 ! same as ASI_PTE on sun4c
+ and %g1, 0x1, %g1 ! Test SRMMU Enable bit ;-)
+ cmp %g1, 0x0
+ be sun4c_remap ! A sun4c MMU or normal Sun4
+ nop
+srmmu_remap:
+ /* First, check for a viking (TI) module. */
+ set 0x40000000, %g2
+ rd %psr, %g3
+ and %g2, %g3, %g3
+ subcc %g3, 0x0, %g0
+ bz srmmu_nviking
+ nop
+
+ /* Figure out what kind of viking we are on.
+ * We need to know if we have to play with the
+ * AC bit and disable traps or not.
+ */
+
+ /* I've only seen MicroSparc's on SparcClassics with this
+ * bit set.
+ */
+ set 0x800, %g2
+ lda [%g0] ASI_M_MMUREGS, %g3 ! peek in the control reg
+ and %g2, %g3, %g3
+ subcc %g3, 0x0, %g0
+ bnz srmmu_nviking ! is in mbus mode
+ nop
+
+ rd %psr, %g3 ! DO NOT TOUCH %g3
+ andn %g3, PSR_ET, %g2
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+
+ /* Get context table pointer, then convert to
+ * a physical address, which is 36 bits.
+ */
+ set AC_M_CTPR, %g4
+ lda [%g4] ASI_M_MMUREGS, %g4
+ sll %g4, 0x4, %g4 ! We use this below
+ ! DO NOT TOUCH %g4
+
+ /* Set the AC bit in the Viking's MMU control reg. */
+ lda [%g0] ASI_M_MMUREGS, %g5 ! DO NOT TOUCH %g5
+ set 0x8000, %g6 ! AC bit mask
+ or %g5, %g6, %g6 ! Or it in...
+ sta %g6, [%g0] ASI_M_MMUREGS ! Close your eyes...
+
+ /* Grrr, why does it seem like every other load/store
+ * on the sun4m is in some ASI space...
+ * Fine with me, let's get the pointer to the level 1
+ * page table directory and fetch its entry.
+ */
+ lda [%g4] ASI_M_BYPASS, %o1 ! This is a level 1 ptr
+ srl %o1, 0x4, %o1 ! Clear low 4 bits
+ sll %o1, 0x8, %o1 ! Make physical
+
+ /* Ok, pull in the PTD. */
+ lda [%o1] ASI_M_BYPASS, %o2 ! This is the 0x0 16MB pgd
+
+ /* Calculate to KERNBASE entry. */
+ add %o1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %o3
+
+ /* Poke the entry into the calculated address. */
+ sta %o2, [%o3] ASI_M_BYPASS
+
+ /* I don't get it Sun, if you engineered all these
+ * boot loaders and the PROM (thank you for the debugging
+ * features btw) why did you not have them load kernel
+ * images up in high address space, since this is necessary
+ * for ABI compliance anyways? Does this low-mapping provide
+ * enhanced interoperability?
+ *
+ * "The PROM is the computer."
+ */
+
+ /* Ok, restore the MMU control register we saved in %g5 */
+ sta %g5, [%g0] ASI_M_MMUREGS ! POW... ouch
+
+ /* Turn traps back on. We saved it in %g3 earlier. */
+ wr %g3, 0x0, %psr ! tick tock, tick tock
+
+ /* Now we burn precious CPU cycles due to bad engineering. */
+ WRITE_PAUSE
+
+ /* Wow, all that just to move a 32-bit value from one
+ * place to another... Jump to high memory.
+ */
+ b go_to_highmem
+ nop
+
+ /* This works on viking's in Mbus mode and all
+ * other MBUS modules. It is virtually the same as
+ * the above madness sans turning traps off and flipping
+ * the AC bit.
+ */
+srmmu_nviking:
+ set AC_M_CTPR, %g1
+ lda [%g1] ASI_M_MMUREGS, %g1 ! get ctx table ptr
+ sll %g1, 0x4, %g1 ! make physical addr
+ lda [%g1] ASI_M_BYPASS, %g1 ! ptr to level 1 pg_table
+ srl %g1, 0x4, %g1
+ sll %g1, 0x8, %g1 ! make phys addr for l1 tbl
+
+ lda [%g1] ASI_M_BYPASS, %g2 ! get level1 entry for 0x0
+ add %g1, KERNBASE >> (SRMMU_PGDIR_SHIFT - 2), %g3
+ sta %g2, [%g3] ASI_M_BYPASS ! place at KERNBASE entry
+ b go_to_highmem
+ nop ! wheee....
+
+ /* This remaps the kernel on Sun4/4xx machines
+ * that have the Sun Mutant Three Level MMU.
+ * It's like a platypus, Sun didn't have the
+ * SRMMU in conception so they kludged the three
+ * level logic in the regular Sun4 MMU probably.
+ *
+ * Basically, you take each entry in the top level
+ * directory that maps the low 3MB starting at
+ * address zero and put the mapping in the KERNBASE
+ * slots. These top level pgd's are called regmaps.
+ */
+sun4_mutant_remap:
+ or %g0, %g0, %g3 ! source base
+ sethi %hi(KERNBASE), %g4 ! destination base
+ or %g4, %lo(KERNBASE), %g4
+ sethi %hi(0x300000), %g5
+ or %g5, %lo(0x300000), %g5 ! upper bound 3MB
+ or %g0, 0x1, %l6
+ sll %l6, 24, %l6 ! Regmap mapping size
+ add %g3, 0x2, %g3 ! Base magic
+ add %g4, 0x2, %g4 ! Base magic
+
+ /* Main remapping loop on Sun4-Mutant-MMU.
+ * "I am not an animal..." -Famous Mutant Person
+ */
+sun4_mutant_loop:
+ lduha [%g3] ASI_REGMAP, %g2 ! Get lower entry
+ stha %g2, [%g4] ASI_REGMAP ! Store in high entry
+ add %g4, %l6, %g4 ! Move up high memory ptr
+ subcc %g3, %g5, %g0 ! Reached our limit?
+ blu sun4_mutant_loop ! Nope, loop again
+ add %g3, %l6, %g3 ! delay, Move up low ptr
+ b go_to_highmem ! Jump to high memory.
+ nop
+
+ /* The following is for non-4/4xx sun4 MMU's. */
+sun4_normal_remap:
+ mov 0, %g3 ! source base
+ set KERNBASE, %g4 ! destination base
+ set 0x300000, %g5 ! upper bound 3MB
+ mov 1, %l6
+ sll %l6, 18, %l6 ! sun4 mmu segmap size
+sun4_normal_loop:
+ lduha [%g3] ASI_SEGMAP, %g6 ! load phys_seg
+ stha %g6, [%g4] ASI_SEGMAP ! stort new virt mapping
+ add %g3, %l6, %g3 ! increment source pointer
+ subcc %g3, %g5, %g0 ! reached limit?
+ blu sun4_normal_loop ! nope, loop again
+ add %g4, %l6, %g4 ! delay, increment dest ptr
+ b go_to_highmem
+ nop
+
+ /* The following works for Sun4c MMU's */
+sun4c_remap:
+ mov 0, %g3 ! source base
+ set KERNBASE, %g4 ! destination base
+ set 0x300000, %g5 ! upper bound 3MB
+ mov 1, %l6
+ sll %l6, 18, %l6 ! sun4c mmu segmap size
+sun4c_remap_loop:
+ lda [%g3] ASI_SEGMAP, %g6 ! load phys_seg
+ sta %g6, [%g4] ASI_SEGMAP ! store new virt mapping
+ add %g3, %l6, %g3 ! Increment source ptr
+ subcc %g3, %g5, %g0 ! Reached limit?
+ bl sun4c_remap_loop ! Nope, loop again
+ add %g4, %l6, %g4 ! delay, Increment dest ptr
+
+/* Now do a non-relative jump so that PC is in high-memory */
+go_to_highmem:
+ set execute_in_high_mem, %g1
+ jmpl %g1, %g0
+ nop
+
+/* The code above should be at beginning and we have to take care about
+ * short jumps, as branching to .text.init section from .text is usually
+ * impossible */
+ __INIT
+/* Acquire boot time privileged register values, this will help debugging.
+ * I figure out and store nwindows and nwindowsm1 later on.
+ */
+execute_in_high_mem:
+ mov %l0, %o0 ! put back romvec
+ mov %l1, %o1 ! and debug_vec
+
+ sethi %hi(prom_vector_p), %g1
+ st %o0, [%g1 + %lo(prom_vector_p)]
+
+ sethi %hi(linux_dbvec), %g1
+ st %o1, [%g1 + %lo(linux_dbvec)]
+
+ ld [%o0 + 0x4], %o3
+ and %o3, 0x3, %o5 ! get the version
+
+ cmp %o3, 0x2 ! a v2 prom?
+ be found_version
+ nop
+
+ /* paul@sfe.com.au */
+ cmp %o3, 0x3 ! a v3 prom?
+ be found_version
+ nop
+
+/* Old sun4's pass our load address into %o0 instead of the prom
+ * pointer. On sun4's you have to hard code the romvec pointer into
+ * your code. Sun probably still does that because they don't even
+ * trust their own "OpenBoot" specifications.
+ */
+ set LOAD_ADDR, %g6
+ cmp %o0, %g6 ! an old sun4?
+ be sun4_init
+ nop
+
+found_version:
+/* Get the machine type via the mysterious romvec node operations. */
+
+ add %g7, 0x1c, %l1
+ ld [%l1], %l0
+ ld [%l0], %l0
+ call %l0
+ or %g0, %g0, %o0 ! next_node(0) = first_node
+ or %o0, %g0, %g6
+
+ sethi %hi(cputypvar), %o1 ! First node has cpu-arch
+ or %o1, %lo(cputypvar), %o1
+ sethi %hi(cputypval), %o2 ! information, the string
+ or %o2, %lo(cputypval), %o2
+ ld [%l1], %l0 ! 'compatibility' tells
+ ld [%l0 + 0xc], %l0 ! that we want 'sun4x' where
+ call %l0 ! x is one of '', 'c', 'm',
+ nop ! 'd' or 'e'. %o2 holds pointer
+ ! to a buf where above string
+ ! will get stored by the prom.
+
+ subcc %o0, %g0, %g0
+ bpos got_prop ! Got the property
+ nop
+
+ or %g6, %g0, %o0
+ sethi %hi(cputypvar_sun4m), %o1
+ or %o1, %lo(cputypvar_sun4m), %o1
+ sethi %hi(cputypval), %o2
+ or %o2, %lo(cputypval), %o2
+ ld [%l1], %l0
+ ld [%l0 + 0xc], %l0
+ call %l0
+ nop
+
+got_prop:
+ set cputypval, %o2
+ ldub [%o2 + 0x4], %l1
+
+ cmp %l1, ' '
+ be 1f
+ cmp %l1, 'c'
+ be 1f
+ cmp %l1, 'm'
+ be 1f
+ cmp %l1, 's'
+ be 1f
+ cmp %l1, 'd'
+ be 1f
+ cmp %l1, 'e'
+ be no_sun4e_here ! Could be a sun4e.
+ nop
+ b no_sun4u_here ! AIEEE, a V9 sun4u... Get our BIG BROTHER kernel :))
+ nop
+
+1: set cputypval, %l1
+ ldub [%l1 + 0x4], %l1
+ cmp %l1, 'm' ! Test for sun4d, sun4e ?
+ be sun4m_init
+ cmp %l1, 's' ! Treat sun4s as sun4m
+ be sun4m_init
+ cmp %l1, 'd' ! Let us see how the beast will die
+ be sun4d_init
+ nop
+
+ /* Jump into mmu context zero. */
+ set AC_CONTEXT, %g1
+ stba %g0, [%g1] ASI_CONTROL
+
+ b sun4c_continue_boot
+ nop
+
+/* CPUID in bootbus can be found at PA 0xff0140000 */
+#define SUN4D_BOOTBUS_CPUID 0xf0140000
+
+sun4d_init:
+ /* Need to patch call to handler_irq */
+ set patch_handler_irq, %g4
+ set sun4d_handler_irq, %g5
+ sethi %hi(0x40000000), %g3 ! call
+ sub %g5, %g4, %g5
+ srl %g5, 2, %g5
+ or %g5, %g3, %g5
+ st %g5, [%g4]
+
+#ifdef CONFIG_SMP
+ /* Get our CPU id out of bootbus */
+ set SUN4D_BOOTBUS_CPUID, %g3
+ lduba [%g3] ASI_M_CTL, %g3
+ and %g3, 0xf8, %g3
+ srl %g3, 3, %g4
+ sta %g4, [%g0] ASI_M_VIKING_TMP1
+ sethi %hi(boot_cpu_id), %g5
+ stb %g4, [%g5 + %lo(boot_cpu_id)]
+ sll %g4, 2, %g4
+ sethi %hi(boot_cpu_id4), %g5
+ stb %g4, [%g5 + %lo(boot_cpu_id4)]
+#endif
+
+ /* Fall through to sun4m_init */
+
+sun4m_init:
+ /* XXX Fucking Cypress... */
+ lda [%g0] ASI_M_MMUREGS, %g5
+ srl %g5, 28, %g4
+
+ cmp %g4, 1
+ bne 1f
+ srl %g5, 24, %g4
+
+ and %g4, 0xf, %g4
+ cmp %g4, 7 /* This would be a HyperSparc. */
+
+ bne 2f
+ nop
+
+1:
+
+#define PATCH_IT(dst, src) \
+ set (dst), %g5; \
+ set (src), %g4; \
+ ld [%g4], %g3; \
+ st %g3, [%g5]; \
+ ld [%g4+0x4], %g3; \
+ st %g3, [%g5+0x4];
+
+ /* Signed multiply. */
+ PATCH_IT(.mul, .mul_patch)
+ PATCH_IT(.mul+0x08, .mul_patch+0x08)
+
+ /* Signed remainder. */
+ PATCH_IT(.rem, .rem_patch)
+ PATCH_IT(.rem+0x08, .rem_patch+0x08)
+ PATCH_IT(.rem+0x10, .rem_patch+0x10)
+ PATCH_IT(.rem+0x18, .rem_patch+0x18)
+ PATCH_IT(.rem+0x20, .rem_patch+0x20)
+ PATCH_IT(.rem+0x28, .rem_patch+0x28)
+
+ /* Signed division. */
+ PATCH_IT(.div, .div_patch)
+ PATCH_IT(.div+0x08, .div_patch+0x08)
+ PATCH_IT(.div+0x10, .div_patch+0x10)
+ PATCH_IT(.div+0x18, .div_patch+0x18)
+ PATCH_IT(.div+0x20, .div_patch+0x20)
+
+ /* Unsigned multiply. */
+ PATCH_IT(.umul, .umul_patch)
+ PATCH_IT(.umul+0x08, .umul_patch+0x08)
+
+ /* Unsigned remainder. */
+ PATCH_IT(.urem, .urem_patch)
+ PATCH_IT(.urem+0x08, .urem_patch+0x08)
+ PATCH_IT(.urem+0x10, .urem_patch+0x10)
+ PATCH_IT(.urem+0x18, .urem_patch+0x18)
+
+ /* Unsigned division. */
+ PATCH_IT(.udiv, .udiv_patch)
+ PATCH_IT(.udiv+0x08, .udiv_patch+0x08)
+ PATCH_IT(.udiv+0x10, .udiv_patch+0x10)
+
+#undef PATCH_IT
+
+/* Ok, the PROM could have done funny things and apple cider could still
+ * be sitting in the fault status/address registers. Read them all to
+ * clear them so we don't get magic faults later on.
+ */
+/* This sucks, apparently this makes Vikings call prom panic, will fix later */
+2:
+ rd %psr, %o1
+ srl %o1, 28, %o1 ! Get a type of the CPU
+
+ subcc %o1, 4, %g0 ! TI: Viking or MicroSPARC
+ be sun4c_continue_boot
+ nop
+
+ set AC_M_SFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ set AC_M_SFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+
+ /* Fujitsu MicroSPARC-II has no asynchronous flavors of FARs */
+ subcc %o1, 0, %g0
+ be sun4c_continue_boot
+ nop
+
+ set AC_M_AFSR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ set AC_M_AFAR, %o0
+ lda [%o0] ASI_M_MMUREGS, %g0
+ nop
+
+
+sun4c_continue_boot:
+
+
+/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
+ * show-time!
+ */
+
+ sethi %hi(cputyp), %o0
+ st %g4, [%o0 + %lo(cputyp)]
+
+ /* Turn on Supervisor, EnableFloating, and all the PIL bits.
+ * Also puts us in register window zero with traps off.
+ */
+ set (PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+
+ /* I want a kernel stack NOW! */
+ set init_thread_union, %g1
+ set (THREAD_SIZE - STACKFRAME_SZ), %g2
+ add %g1, %g2, %sp
+ mov 0, %fp /* And for good luck */
+
+ /* Zero out our BSS section. */
+ set __bss_start , %o0 ! First address of BSS
+ set _end , %o1 ! Last address of BSS
+ add %o0, 0x1, %o0
+1:
+ stb %g0, [%o0]
+ subcc %o0, %o1, %g0
+ bl 1b
+ add %o0, 0x1, %o0
+
+ /* Initialize the uwinmask value for init task just in case.
+ * But first make current_set[boot_cpu_id] point to something useful.
+ */
+ set init_thread_union, %g6
+ set current_set, %g2
+#ifdef CONFIG_SMP
+ sethi %hi(boot_cpu_id4), %g3
+ ldub [%g3 + %lo(boot_cpu_id4)], %g3
+ st %g6, [%g2]
+ add %g2, %g3, %g2
+#endif
+ st %g6, [%g2]
+
+ st %g0, [%g6 + TI_UWINMASK]
+
+/* Compute NWINDOWS and stash it away. Now uses %wim trick explained
+ * in the V8 manual. Ok, this method seems to work, Sparc is cool...
+ * No, it doesn't work, have to play the save/readCWP/restore trick.
+ */
+
+ wr %g0, 0x0, %wim ! so we do not get a trap
+ WRITE_PAUSE
+
+ save
+
+ rd %psr, %g3
+
+ restore
+
+ and %g3, 0x1f, %g3
+ add %g3, 0x1, %g3
+
+ mov 2, %g1
+ wr %g1, 0x0, %wim ! make window 1 invalid
+ WRITE_PAUSE
+
+ cmp %g3, 0x7
+ bne 2f
+ nop
+
+ /* Adjust our window handling routines to
+ * do things correctly on 7 window Sparcs.
+ */
+
+#define PATCH_INSN(src, dest) \
+ set src, %g5; \
+ set dest, %g2; \
+ ld [%g5], %g4; \
+ st %g4, [%g2];
+
+ /* Patch for window spills... */
+ PATCH_INSN(spnwin_patch1_7win, spnwin_patch1)
+ PATCH_INSN(spnwin_patch2_7win, spnwin_patch2)
+ PATCH_INSN(spnwin_patch3_7win, spnwin_patch3)
+
+ /* Patch for window fills... */
+ PATCH_INSN(fnwin_patch1_7win, fnwin_patch1)
+ PATCH_INSN(fnwin_patch2_7win, fnwin_patch2)
+
+ /* Patch for trap entry setup... */
+ PATCH_INSN(tsetup_7win_patch1, tsetup_patch1)
+ PATCH_INSN(tsetup_7win_patch2, tsetup_patch2)
+ PATCH_INSN(tsetup_7win_patch3, tsetup_patch3)
+ PATCH_INSN(tsetup_7win_patch4, tsetup_patch4)
+ PATCH_INSN(tsetup_7win_patch5, tsetup_patch5)
+ PATCH_INSN(tsetup_7win_patch6, tsetup_patch6)
+
+ /* Patch for returning from traps... */
+ PATCH_INSN(rtrap_7win_patch1, rtrap_patch1)
+ PATCH_INSN(rtrap_7win_patch2, rtrap_patch2)
+ PATCH_INSN(rtrap_7win_patch3, rtrap_patch3)
+ PATCH_INSN(rtrap_7win_patch4, rtrap_patch4)
+ PATCH_INSN(rtrap_7win_patch5, rtrap_patch5)
+
+ /* Patch for killing user windows from the register file. */
+ PATCH_INSN(kuw_patch1_7win, kuw_patch1)
+
+ /* Now patch the kernel window flush sequences.
+ * This saves 2 traps on every switch and fork.
+ */
+ set 0x01000000, %g4
+ set flush_patch_one, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+ set flush_patch_two, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+ set flush_patch_three, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+ set flush_patch_four, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+ set flush_patch_exception, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+ set flush_patch_switch, %g5
+ st %g4, [%g5 + 0x18]
+ st %g4, [%g5 + 0x1c]
+
+2:
+ sethi %hi(nwindows), %g4
+ st %g3, [%g4 + %lo(nwindows)] ! store final value
+ sub %g3, 0x1, %g3
+ sethi %hi(nwindowsm1), %g4
+ st %g3, [%g4 + %lo(nwindowsm1)]
+
+ /* Here we go, start using Linux's trap table... */
+ set trapbase, %g3
+ wr %g3, 0x0, %tbr
+ WRITE_PAUSE
+
+ /* Finally, turn on traps so that we can call c-code. */
+ rd %psr, %g3
+ wr %g3, 0x0, %psr
+ WRITE_PAUSE
+
+ wr %g3, PSR_ET, %psr
+ WRITE_PAUSE
+
+ /* First we call prom_init() to set up PROMLIB, then
+ * off to start_kernel().
+ */
+
+ sethi %hi(prom_vector_p), %g5
+ ld [%g5 + %lo(prom_vector_p)], %o0
+ call prom_init
+ nop
+
+ call start_kernel
+ nop
+
+ /* We should not get here. */
+ call halt_me
+ nop
+
+sun4_init:
+ sethi %hi(SUN4_PROM_VECTOR+0x84), %o1
+ ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
+ set sun4_notsup, %o0
+ call %o1 /* printf */
+ nop
+ sethi %hi(SUN4_PROM_VECTOR+0xc4), %o1
+ ld [%o1 + %lo(SUN4_PROM_VECTOR+0xc4)], %o1
+ call %o1 /* exittomon */
+ nop
+1: ba 1b ! Cannot exit into KMON
+ nop
+
+no_sun4e_here:
+ ld [%g7 + 0x68], %o1
+ set sun4e_notsup, %o0
+ call %o1
+ nop
+ b halt_me
+ nop
+
+ __INITDATA
+
+sun4u_1:
+ .asciz "finddevice"
+ .align 4
+sun4u_2:
+ .asciz "/chosen"
+ .align 4
+sun4u_3:
+ .asciz "getprop"
+ .align 4
+sun4u_4:
+ .asciz "stdout"
+ .align 4
+sun4u_5:
+ .asciz "write"
+ .align 4
+sun4u_6:
+ .asciz "\n\rOn sun4u you have to use UltraLinux (64bit) kernel\n\rand not a 32bit sun4[cdem] version\n\r\n\r"
+sun4u_6e:
+ .align 4
+sun4u_7:
+ .asciz "exit"
+ .align 8
+sun4u_a1:
+ .word 0, sun4u_1, 0, 1, 0, 1, 0, sun4u_2, 0
+sun4u_r1:
+ .word 0
+sun4u_a2:
+ .word 0, sun4u_3, 0, 4, 0, 1, 0
+sun4u_i2:
+ .word 0, 0, sun4u_4, 0, sun4u_1, 0, 8, 0
+sun4u_r2:
+ .word 0
+sun4u_a3:
+ .word 0, sun4u_5, 0, 3, 0, 1, 0
+sun4u_i3:
+ .word 0, 0, sun4u_6, 0, sun4u_6e - sun4u_6 - 1, 0
+sun4u_r3:
+ .word 0
+sun4u_a4:
+ .word 0, sun4u_7, 0, 0, 0, 0
+sun4u_r4:
+
+ __INIT
+no_sun4u_here:
+ set sun4u_a1, %o0
+ set current_pc, %l2
+ cmp %l2, %g3
+ be 1f
+ mov %o4, %l0
+ sub %g3, %l2, %l6
+ add %o0, %l6, %o0
+ mov %o0, %l4
+ mov sun4u_r4 - sun4u_a1, %l3
+ ld [%l4], %l5
+2:
+ add %l4, 4, %l4
+ cmp %l5, %l2
+ add %l5, %l6, %l5
+ bgeu,a 3f
+ st %l5, [%l4 - 4]
+3:
+ subcc %l3, 4, %l3
+ bne 2b
+ ld [%l4], %l5
+1:
+ call %l0
+ mov %o0, %l1
+
+ ld [%l1 + (sun4u_r1 - sun4u_a1)], %o1
+ add %l1, (sun4u_a2 - sun4u_a1), %o0
+ call %l0
+ st %o1, [%o0 + (sun4u_i2 - sun4u_a2)]
+
+ ld [%l1 + (sun4u_1 - sun4u_a1)], %o1
+ add %l1, (sun4u_a3 - sun4u_a1), %o0
+ call %l0
+ st %o1, [%o0 + (sun4u_i3 - sun4u_a3)]
+
+ call %l0
+ add %l1, (sun4u_a4 - sun4u_a1), %o0
+
+ /* Not reached */
+halt_me:
+ ld [%g7 + 0x74], %o0
+ call %o0 ! Get us out of here...
+ nop ! Apparently Solaris is better.
+
+/* Ok, now we continue in the .data/.text sections */
+
+ .data
+ .align 4
+
+/*
+ * Fill up the prom vector, note in particular the kind first element,
+ * no joke. I don't need all of them in here as the entire prom vector
+ * gets initialized in c-code so all routines can use it.
+ */
+
+prom_vector_p:
+ .word 0
+
+/* We calculate the following at boot time, window fills/spills and trap entry
+ * code uses these to keep track of the register windows.
+ */
+
+ .align 4
+ .globl nwindows
+ .globl nwindowsm1
+nwindows:
+ .word 8
+nwindowsm1:
+ .word 7
+
+/* Boot time debugger vector value. We need this later on. */
+
+ .align 4
+ .globl linux_dbvec
+linux_dbvec:
+ .word 0
+ .word 0
+
+ .align 8
+
+ .globl lvl14_save
+lvl14_save:
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word t_irq14
+
+ .section ".fixup",#alloc,#execinstr
+ .globl __ret_efault
+__ret_efault:
+ ret
+ restore %g0, -EFAULT, %o0
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
new file mode 100644
index 000000000000..8ffee714f932
--- /dev/null
+++ b/arch/sparc/kernel/head_64.S
@@ -0,0 +1,900 @@
+/* head.S: Initial boot code for the Sparc64 port of Linux.
+ *
+ * Copyright (C) 1996, 1997, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au)
+ * Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ */
+
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/threads.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/asi.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/spitfire.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/processor.h>
+#include <asm/lsu.h>
+#include <asm/dcr.h>
+#include <asm/dcu.h>
+#include <asm/head.h>
+#include <asm/ttable.h>
+#include <asm/mmu.h>
+#include <asm/cpudata.h>
+#include <asm/pil.h>
+#include <asm/estate.h>
+#include <asm/sfafsr.h>
+#include <asm/unistd.h>
+
+/* This section from from _start to sparc64_boot_end should fit into
+ * 0x0000000000404000 to 0x0000000000408000.
+ */
+ .text
+ .globl start, _start, stext, _stext
+_start:
+start:
+_stext:
+stext:
+! 0x0000000000404000
+ b sparc64_boot
+ flushw /* Flush register file. */
+
+/* This stuff has to be in sync with SILO and other potential boot loaders
+ * Fields should be kept upward compatible and whenever any change is made,
+ * HdrS version should be incremented.
+ */
+ .global root_flags, ram_flags, root_dev
+ .global sparc_ramdisk_image, sparc_ramdisk_size
+ .global sparc_ramdisk_image64
+
+ .ascii "HdrS"
+ .word LINUX_VERSION_CODE
+
+ /* History:
+ *
+ * 0x0300 : Supports being located at other than 0x4000
+ * 0x0202 : Supports kernel params string
+ * 0x0201 : Supports reboot_command
+ */
+ .half 0x0301 /* HdrS version */
+
+root_flags:
+ .half 1
+root_dev:
+ .half 0
+ram_flags:
+ .half 0
+sparc_ramdisk_image:
+ .word 0
+sparc_ramdisk_size:
+ .word 0
+ .xword reboot_command
+ .xword bootstr_info
+sparc_ramdisk_image64:
+ .xword 0
+ .word _end
+
+ /* PROM cif handler code address is in %o4. */
+sparc64_boot:
+ mov %o4, %l7
+
+ /* We need to remap the kernel. Use position independant
+ * code to remap us to KERNBASE.
+ *
+ * SILO can invoke us with 32-bit address masking enabled,
+ * so make sure that's clear.
+ */
+ rdpr %pstate, %g1
+ andn %g1, PSTATE_AM, %g1
+ wrpr %g1, 0x0, %pstate
+ ba,a,pt %xcc, 1f
+
+ .globl prom_finddev_name, prom_chosen_path, prom_root_node
+ .globl prom_getprop_name, prom_mmu_name, prom_peer_name
+ .globl prom_callmethod_name, prom_translate_name, prom_root_compatible
+ .globl prom_map_name, prom_unmap_name, prom_mmu_ihandle_cache
+ .globl prom_boot_mapped_pc, prom_boot_mapping_mode
+ .globl prom_boot_mapping_phys_high, prom_boot_mapping_phys_low
+ .globl prom_compatible_name, prom_cpu_path, prom_cpu_compatible
+ .globl is_sun4v, sun4v_chip_type, prom_set_trap_table_name
+prom_peer_name:
+ .asciz "peer"
+prom_compatible_name:
+ .asciz "compatible"
+prom_finddev_name:
+ .asciz "finddevice"
+prom_chosen_path:
+ .asciz "/chosen"
+prom_cpu_path:
+ .asciz "/cpu"
+prom_getprop_name:
+ .asciz "getprop"
+prom_mmu_name:
+ .asciz "mmu"
+prom_callmethod_name:
+ .asciz "call-method"
+prom_translate_name:
+ .asciz "translate"
+prom_map_name:
+ .asciz "map"
+prom_unmap_name:
+ .asciz "unmap"
+prom_set_trap_table_name:
+ .asciz "SUNW,set-trap-table"
+prom_sun4v_name:
+ .asciz "sun4v"
+prom_niagara_prefix:
+ .asciz "SUNW,UltraSPARC-T"
+ .align 4
+prom_root_compatible:
+ .skip 64
+prom_cpu_compatible:
+ .skip 64
+prom_root_node:
+ .word 0
+prom_mmu_ihandle_cache:
+ .word 0
+prom_boot_mapped_pc:
+ .word 0
+prom_boot_mapping_mode:
+ .word 0
+ .align 8
+prom_boot_mapping_phys_high:
+ .xword 0
+prom_boot_mapping_phys_low:
+ .xword 0
+is_sun4v:
+ .word 0
+sun4v_chip_type:
+ .word SUN4V_CHIP_INVALID
+1:
+ rd %pc, %l0
+
+ mov (1b - prom_peer_name), %l1
+ sub %l0, %l1, %l1
+ mov 0, %l2
+
+ /* prom_root_node = prom_peer(0) */
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "peer"
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, 0
+ stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ ldx [%sp + 2047 + 128 + 0x20], %l4 ! prom root node
+ mov (1b - prom_root_node), %l1
+ sub %l0, %l1, %l1
+ stw %l4, [%l1]
+
+ mov (1b - prom_getprop_name), %l1
+ mov (1b - prom_compatible_name), %l2
+ mov (1b - prom_root_compatible), %l5
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ sub %l0, %l5, %l5
+
+ /* prom_getproperty(prom_root_node, "compatible",
+ * &prom_root_compatible, 64)
+ */
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
+ mov 4, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, prom_root_node
+ stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "compatible"
+ stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_root_compatible
+ mov 64, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, size
+ stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ mov (1b - prom_finddev_name), %l1
+ mov (1b - prom_chosen_path), %l2
+ mov (1b - prom_boot_mapped_pc), %l3
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ sub %l0, %l3, %l3
+ stw %l0, [%l3]
+ sub %sp, (192 + 128), %sp
+
+ /* chosen_node = prom_finddevice("/chosen") */
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "finddevice"
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, "/chosen"
+ stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ ldx [%sp + 2047 + 128 + 0x20], %l4 ! chosen device node
+
+ mov (1b - prom_getprop_name), %l1
+ mov (1b - prom_mmu_name), %l2
+ mov (1b - prom_mmu_ihandle_cache), %l5
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ sub %l0, %l5, %l5
+
+ /* prom_mmu_ihandle_cache = prom_getint(chosen_node, "mmu") */
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
+ mov 4, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, chosen_node
+ stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "mmu"
+ stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_mmu_ihandle_cache
+ mov 4, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, sizeof(arg3)
+ stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ mov (1b - prom_callmethod_name), %l1
+ mov (1b - prom_translate_name), %l2
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ lduw [%l5], %l5 ! prom_mmu_ihandle_cache
+
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "call-method"
+ mov 3, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 3
+ mov 5, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 5
+ stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1: "translate"
+ stx %l5, [%sp + 2047 + 128 + 0x20] ! arg2: prom_mmu_ihandle_cache
+ /* PAGE align */
+ srlx %l0, 13, %l3
+ sllx %l3, 13, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x28] ! arg3: vaddr, our PC
+ stx %g0, [%sp + 2047 + 128 + 0x30] ! res1
+ stx %g0, [%sp + 2047 + 128 + 0x38] ! res2
+ stx %g0, [%sp + 2047 + 128 + 0x40] ! res3
+ stx %g0, [%sp + 2047 + 128 + 0x48] ! res4
+ stx %g0, [%sp + 2047 + 128 + 0x50] ! res5
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ ldx [%sp + 2047 + 128 + 0x40], %l1 ! translation mode
+ mov (1b - prom_boot_mapping_mode), %l4
+ sub %l0, %l4, %l4
+ stw %l1, [%l4]
+ mov (1b - prom_boot_mapping_phys_high), %l4
+ sub %l0, %l4, %l4
+ ldx [%sp + 2047 + 128 + 0x48], %l2 ! physaddr high
+ stx %l2, [%l4 + 0x0]
+ ldx [%sp + 2047 + 128 + 0x50], %l3 ! physaddr low
+ /* 4MB align */
+ srlx %l3, 22, %l3
+ sllx %l3, 22, %l3
+ stx %l3, [%l4 + 0x8]
+
+ /* Leave service as-is, "call-method" */
+ mov 7, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 7
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ mov (1b - prom_map_name), %l3
+ sub %l0, %l3, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x18] ! arg1: "map"
+ /* Leave arg2 as-is, prom_mmu_ihandle_cache */
+ mov -1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x28] ! arg3: mode (-1 default)
+ /* 4MB align the kernel image size. */
+ set (_end - KERNBASE), %l3
+ set ((4 * 1024 * 1024) - 1), %l4
+ add %l3, %l4, %l3
+ andn %l3, %l4, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4: roundup(ksize, 4MB)
+ sethi %hi(KERNBASE), %l3
+ stx %l3, [%sp + 2047 + 128 + 0x38] ! arg5: vaddr (KERNBASE)
+ stx %g0, [%sp + 2047 + 128 + 0x40] ! arg6: empty
+ mov (1b - prom_boot_mapping_phys_low), %l3
+ sub %l0, %l3, %l3
+ ldx [%l3], %l3
+ stx %l3, [%sp + 2047 + 128 + 0x48] ! arg7: phys addr
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ add %sp, (192 + 128), %sp
+
+ sethi %hi(prom_root_compatible), %g1
+ or %g1, %lo(prom_root_compatible), %g1
+ sethi %hi(prom_sun4v_name), %g7
+ or %g7, %lo(prom_sun4v_name), %g7
+ mov 5, %g3
+90: ldub [%g7], %g2
+ ldub [%g1], %g4
+ cmp %g2, %g4
+ bne,pn %icc, 80f
+ add %g7, 1, %g7
+ subcc %g3, 1, %g3
+ bne,pt %xcc, 90b
+ add %g1, 1, %g1
+
+ sethi %hi(is_sun4v), %g1
+ or %g1, %lo(is_sun4v), %g1
+ mov 1, %g7
+ stw %g7, [%g1]
+
+ /* cpu_node = prom_finddevice("/cpu") */
+ mov (1b - prom_finddev_name), %l1
+ mov (1b - prom_cpu_path), %l2
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ sub %sp, (192 + 128), %sp
+
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "finddevice"
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, "/cpu"
+ stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ ldx [%sp + 2047 + 128 + 0x20], %l4 ! cpu device node
+
+ mov (1b - prom_getprop_name), %l1
+ mov (1b - prom_compatible_name), %l2
+ mov (1b - prom_cpu_compatible), %l5
+ sub %l0, %l1, %l1
+ sub %l0, %l2, %l2
+ sub %l0, %l5, %l5
+
+ /* prom_getproperty(cpu_node, "compatible",
+ * &prom_cpu_compatible, 64)
+ */
+ stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
+ mov 4, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
+ mov 1, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
+ stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, cpu_node
+ stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "compatible"
+ stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_cpu_compatible
+ mov 64, %l3
+ stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, size
+ stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
+ call %l7
+ add %sp, (2047 + 128), %o0 ! argument array
+
+ add %sp, (192 + 128), %sp
+
+ sethi %hi(prom_cpu_compatible), %g1
+ or %g1, %lo(prom_cpu_compatible), %g1
+ sethi %hi(prom_niagara_prefix), %g7
+ or %g7, %lo(prom_niagara_prefix), %g7
+ mov 17, %g3
+90: ldub [%g7], %g2
+ ldub [%g1], %g4
+ cmp %g2, %g4
+ bne,pn %icc, 4f
+ add %g7, 1, %g7
+ subcc %g3, 1, %g3
+ bne,pt %xcc, 90b
+ add %g1, 1, %g1
+
+ sethi %hi(prom_cpu_compatible), %g1
+ or %g1, %lo(prom_cpu_compatible), %g1
+ ldub [%g1 + 17], %g2
+ cmp %g2, '1'
+ be,pt %xcc, 5f
+ mov SUN4V_CHIP_NIAGARA1, %g4
+ cmp %g2, '2'
+ be,pt %xcc, 5f
+ mov SUN4V_CHIP_NIAGARA2, %g4
+4:
+ mov SUN4V_CHIP_UNKNOWN, %g4
+5: sethi %hi(sun4v_chip_type), %g2
+ or %g2, %lo(sun4v_chip_type), %g2
+ stw %g4, [%g2]
+
+80:
+ BRANCH_IF_SUN4V(g1, jump_to_sun4u_init)
+ BRANCH_IF_CHEETAH_BASE(g1,g7,cheetah_boot)
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,cheetah_plus_boot)
+ ba,pt %xcc, spitfire_boot
+ nop
+
+cheetah_plus_boot:
+ /* Preserve OBP chosen DCU and DCR register settings. */
+ ba,pt %xcc, cheetah_generic_boot
+ nop
+
+cheetah_boot:
+ mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
+ wr %g1, %asr18
+
+ sethi %uhi(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
+ or %g7, %ulo(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
+ sllx %g7, 32, %g7
+ or %g7, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g7
+ stxa %g7, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+
+cheetah_generic_boot:
+ mov TSB_EXTENSION_P, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_S, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_N, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+
+ ba,a,pt %xcc, jump_to_sun4u_init
+
+spitfire_boot:
+ /* Typically PROM has already enabled both MMU's and both on-chip
+ * caches, but we do it here anyway just to be paranoid.
+ */
+ mov (LSU_CONTROL_IC|LSU_CONTROL_DC|LSU_CONTROL_IM|LSU_CONTROL_DM), %g1
+ stxa %g1, [%g0] ASI_LSU_CONTROL
+ membar #Sync
+
+jump_to_sun4u_init:
+ /*
+ * Make sure we are in privileged mode, have address masking,
+ * using the ordinary globals and have enabled floating
+ * point.
+ *
+ * Again, typically PROM has left %pil at 13 or similar, and
+ * (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate.
+ */
+ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
+ wr %g0, 0, %fprs
+
+ set sun4u_init, %g2
+ jmpl %g2 + %g0, %g0
+ nop
+
+ .section .text.init.refok
+sun4u_init:
+ BRANCH_IF_SUN4V(g1, sun4v_init)
+
+ /* Set ctx 0 */
+ mov PRIMARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ membar #Sync
+
+ mov SECONDARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_DMMU
+ membar #Sync
+
+ ba,pt %xcc, sun4u_continue
+ nop
+
+sun4v_init:
+ /* Set ctx 0 */
+ mov PRIMARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+
+ mov SECONDARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+ ba,pt %xcc, niagara_tlb_fixup
+ nop
+
+sun4u_continue:
+ BRANCH_IF_ANY_CHEETAH(g1, g7, cheetah_tlb_fixup)
+
+ ba,pt %xcc, spitfire_tlb_fixup
+ nop
+
+niagara_tlb_fixup:
+ mov 3, %g2 /* Set TLB type to hypervisor. */
+ sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+
+ /* Patch copy/clear ops. */
+ sethi %hi(sun4v_chip_type), %g1
+ lduw [%g1 + %lo(sun4v_chip_type)], %g1
+ cmp %g1, SUN4V_CHIP_NIAGARA1
+ be,pt %xcc, niagara_patch
+ cmp %g1, SUN4V_CHIP_NIAGARA2
+ be,pt %xcc, niagara2_patch
+ nop
+
+ call generic_patch_copyops
+ nop
+ call generic_patch_bzero
+ nop
+ call generic_patch_pageops
+ nop
+
+ ba,a,pt %xcc, 80f
+niagara2_patch:
+ call niagara2_patch_copyops
+ nop
+ call niagara_patch_bzero
+ nop
+ call niagara2_patch_pageops
+ nop
+
+ ba,a,pt %xcc, 80f
+
+niagara_patch:
+ call niagara_patch_copyops
+ nop
+ call niagara_patch_bzero
+ nop
+ call niagara_patch_pageops
+ nop
+
+80:
+ /* Patch TLB/cache ops. */
+ call hypervisor_patch_cachetlbops
+ nop
+
+ ba,pt %xcc, tlb_fixup_done
+ nop
+
+cheetah_tlb_fixup:
+ mov 2, %g2 /* Set TLB type to cheetah+. */
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,1f)
+
+ mov 1, %g2 /* Set TLB type to cheetah. */
+
+1: sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+
+ /* Patch copy/page operations to cheetah optimized versions. */
+ call cheetah_patch_copyops
+ nop
+ call cheetah_patch_copy_page
+ nop
+ call cheetah_patch_cachetlbops
+ nop
+
+ ba,pt %xcc, tlb_fixup_done
+ nop
+
+spitfire_tlb_fixup:
+ /* Set TLB type to spitfire. */
+ mov 0, %g2
+ sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+
+tlb_fixup_done:
+ sethi %hi(init_thread_union), %g6
+ or %g6, %lo(init_thread_union), %g6
+ ldx [%g6 + TI_TASK], %g4
+ mov %sp, %l6
+
+ wr %g0, ASI_P, %asi
+ mov 1, %g1
+ sllx %g1, THREAD_SHIFT, %g1
+ sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1
+ add %g6, %g1, %sp
+ mov 0, %fp
+
+ /* Set per-cpu pointer initially to zero, this makes
+ * the boot-cpu use the in-kernel-image per-cpu areas
+ * before setup_per_cpu_area() is invoked.
+ */
+ clr %g5
+
+ wrpr %g0, 0, %wstate
+ wrpr %g0, 0x0, %tl
+
+ /* Clear the bss */
+ sethi %hi(__bss_start), %o0
+ or %o0, %lo(__bss_start), %o0
+ sethi %hi(_end), %o1
+ or %o1, %lo(_end), %o1
+ call __bzero
+ sub %o1, %o0, %o1
+
+#ifdef CONFIG_LOCKDEP
+ /* We have this call this super early, as even prom_init can grab
+ * spinlocks and thus call into the lockdep code.
+ */
+ call lockdep_init
+ nop
+#endif
+
+ mov %l6, %o1 ! OpenPROM stack
+ call prom_init
+ mov %l7, %o0 ! OpenPROM cif handler
+
+ /* Initialize current_thread_info()->cpu as early as possible.
+ * In order to do that accurately we have to patch up the get_cpuid()
+ * assembler sequences. And that, in turn, requires that we know
+ * if we are on a Starfire box or not. While we're here, patch up
+ * the sun4v sequences as well.
+ */
+ call check_if_starfire
+ nop
+ call per_cpu_patch
+ nop
+ call sun4v_patch
+ nop
+
+#ifdef CONFIG_SMP
+ call hard_smp_processor_id
+ nop
+ cmp %o0, NR_CPUS
+ blu,pt %xcc, 1f
+ nop
+ call boot_cpu_id_too_large
+ nop
+ /* Not reached... */
+
+1:
+ /* If we boot on a non-zero cpu, all of the per-cpu
+ * variable references we make before setting up the
+ * per-cpu areas will use a bogus offset. Put a
+ * compensating factor into __per_cpu_base to handle
+ * this cleanly.
+ *
+ * What the per-cpu code calculates is:
+ *
+ * __per_cpu_base + (cpu << __per_cpu_shift)
+ *
+ * These two variables are zero initially, so to
+ * make it all cancel out to zero we need to put
+ * "0 - (cpu << 0)" into __per_cpu_base so that the
+ * above formula evaluates to zero.
+ *
+ * We cannot even perform a printk() until this stuff
+ * is setup as that calls cpu_clock() which uses
+ * per-cpu variables.
+ */
+ sub %g0, %o0, %o1
+ sethi %hi(__per_cpu_base), %o2
+ stx %o1, [%o2 + %lo(__per_cpu_base)]
+#else
+ mov 0, %o0
+#endif
+ sth %o0, [%g6 + TI_CPU]
+
+ call prom_init_report
+ nop
+
+ /* Off we go.... */
+ call start_kernel
+ nop
+ /* Not reached... */
+
+ .previous
+
+ /* This is meant to allow the sharing of this code between
+ * boot processor invocation (via setup_tba() below) and
+ * secondary processor startup (via trampoline.S). The
+ * former does use this code, the latter does not yet due
+ * to some complexities. That should be fixed up at some
+ * point.
+ *
+ * There used to be enormous complexity wrt. transferring
+ * over from the firwmare's trap table to the Linux kernel's.
+ * For example, there was a chicken & egg problem wrt. building
+ * the OBP page tables, yet needing to be on the Linux kernel
+ * trap table (to translate PAGE_OFFSET addresses) in order to
+ * do that.
+ *
+ * We now handle OBP tlb misses differently, via linear lookups
+ * into the prom_trans[] array. So that specific problem no
+ * longer exists. Yet, unfortunately there are still some issues
+ * preventing trampoline.S from using this code... ho hum.
+ */
+ .globl setup_trap_table
+setup_trap_table:
+ save %sp, -192, %sp
+
+ /* Force interrupts to be disabled. */
+ rdpr %pstate, %l0
+ andn %l0, PSTATE_IE, %o1
+ wrpr %o1, 0x0, %pstate
+ rdpr %pil, %l1
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+
+ /* Make the firmware call to jump over to the Linux trap table. */
+ sethi %hi(is_sun4v), %o0
+ lduw [%o0 + %lo(is_sun4v)], %o0
+ brz,pt %o0, 1f
+ nop
+
+ TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
+ add %g2, TRAP_PER_CPU_FAULT_INFO, %g2
+ stxa %g2, [%g0] ASI_SCRATCHPAD
+
+ /* Compute physical address:
+ *
+ * paddr = kern_base + (mmfsa_vaddr - KERNBASE)
+ */
+ sethi %hi(KERNBASE), %g3
+ sub %g2, %g3, %g2
+ sethi %hi(kern_base), %g3
+ ldx [%g3 + %lo(kern_base)], %g3
+ add %g2, %g3, %o1
+ sethi %hi(sparc64_ttable_tl0), %o0
+
+ set prom_set_trap_table_name, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 2, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 0, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ stx %o0, [%sp + 2047 + 128 + 0x18]
+ stx %o1, [%sp + 2047 + 128 + 0x20]
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+ ba,pt %xcc, 2f
+ nop
+
+1: sethi %hi(sparc64_ttable_tl0), %o0
+ set prom_set_trap_table_name, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 0, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ stx %o0, [%sp + 2047 + 128 + 0x18]
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+ /* Start using proper page size encodings in ctx register. */
+2: sethi %hi(sparc64_kern_pri_context), %g3
+ ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
+
+ mov PRIMARY_CONTEXT, %g1
+
+661: stxa %g2, [%g1] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g2, [%g1] ASI_MMU
+ .previous
+
+ membar #Sync
+
+ BRANCH_IF_SUN4V(o2, 1f)
+
+ /* Kill PROM timer */
+ sethi %hi(0x80000000), %o2
+ sllx %o2, 32, %o2
+ wr %o2, 0, %tick_cmpr
+
+ BRANCH_IF_ANY_CHEETAH(o2, o3, 1f)
+
+ ba,pt %xcc, 2f
+ nop
+
+ /* Disable STICK_INT interrupts. */
+1:
+ sethi %hi(0x80000000), %o2
+ sllx %o2, 32, %o2
+ wr %o2, %asr25
+
+2:
+ wrpr %g0, %g0, %wstate
+
+ call init_irqwork_curcpu
+ nop
+
+ /* Now we can restore interrupt state. */
+ wrpr %l0, 0, %pstate
+ wrpr %l1, 0x0, %pil
+
+ ret
+ restore
+
+ .globl setup_tba
+setup_tba:
+ save %sp, -192, %sp
+
+ /* The boot processor is the only cpu which invokes this
+ * routine, the other cpus set things up via trampoline.S.
+ * So save the OBP trap table address here.
+ */
+ rdpr %tba, %g7
+ sethi %hi(prom_tba), %o1
+ or %o1, %lo(prom_tba), %o1
+ stx %g7, [%o1]
+
+ call setup_trap_table
+ nop
+
+ ret
+ restore
+sparc64_boot_end:
+
+#include "etrap_64.S"
+#include "rtrap_64.S"
+#include "winfixup.S"
+#include "fpu_traps.S"
+#include "ivec.S"
+#include "getsetcc.S"
+#include "utrap.S"
+#include "spiterrs.S"
+#include "cherrs.S"
+#include "misctrap.S"
+#include "syscalls.S"
+#include "helpers.S"
+#include "hvcalls.S"
+#include "sun4v_tlb_miss.S"
+#include "sun4v_ivec.S"
+#include "ktlb.S"
+#include "tsb.S"
+
+/*
+ * The following skip makes sure the trap table in ttable.S is aligned
+ * on a 32K boundary as required by the v9 specs for TBA register.
+ *
+ * We align to a 32K boundary, then we have the 32K kernel TSB,
+ * the 64K kernel 4MB TSB, and then the 32K aligned trap table.
+ */
+1:
+ .skip 0x4000 + _start - 1b
+
+! 0x0000000000408000
+
+ .globl swapper_tsb
+swapper_tsb:
+ .skip (32 * 1024)
+
+ .globl swapper_4m_tsb
+swapper_4m_tsb:
+ .skip (64 * 1024)
+
+! 0x0000000000420000
+
+ /* Some care needs to be exercised if you try to move the
+ * location of the trap table relative to other things. For
+ * one thing there are br* instructions in some of the
+ * trap table entires which branch back to code in ktlb.S
+ * Those instructions can only handle a signed 16-bit
+ * displacement.
+ *
+ * There is a binutils bug (bugzilla #4558) which causes
+ * the relocation overflow checks for such instructions to
+ * not be done correctly. So bintuils will not notice the
+ * error and will instead write junk into the relocation and
+ * you'll have an unbootable kernel.
+ */
+#include "ttable.S"
+
+! 0x0000000000428000
+
+#include "systbls_64.S"
+
+ .data
+ .align 8
+ .globl prom_tba, tlb_type
+prom_tba: .xword 0
+tlb_type: .word 0 /* Must NOT end up in BSS */
+ .section ".fixup",#alloc,#execinstr
+
+ .globl __ret_efault, __retl_efault
+__ret_efault:
+ ret
+ restore %g0, -EFAULT, %o0
+__retl_efault:
+ retl
+ mov -EFAULT, %o0
diff --git a/arch/sparc64/kernel/helpers.S b/arch/sparc/kernel/helpers.S
index 314dd0c9fc5b..314dd0c9fc5b 100644
--- a/arch/sparc64/kernel/helpers.S
+++ b/arch/sparc/kernel/helpers.S
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc/kernel/hvapi.c
index 1d272c3b5740..1d272c3b5740 100644
--- a/arch/sparc64/kernel/hvapi.c
+++ b/arch/sparc/kernel/hvapi.c
diff --git a/arch/sparc/kernel/hvcalls.S b/arch/sparc/kernel/hvcalls.S
new file mode 100644
index 000000000000..8a5f35ffb15e
--- /dev/null
+++ b/arch/sparc/kernel/hvcalls.S
@@ -0,0 +1,800 @@
+ /* %o0: devhandle
+ * %o1: devino
+ *
+ * returns %o0: sysino
+ */
+ENTRY(sun4v_devino_to_sysino)
+ mov HV_FAST_INTR_DEVINO2SYSINO, %o5
+ ta HV_FAST_TRAP
+ retl
+ mov %o1, %o0
+ENDPROC(sun4v_devino_to_sysino)
+
+ /* %o0: sysino
+ *
+ * returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED})
+ */
+ENTRY(sun4v_intr_getenabled)
+ mov HV_FAST_INTR_GETENABLED, %o5
+ ta HV_FAST_TRAP
+ retl
+ mov %o1, %o0
+ENDPROC(sun4v_intr_getenabled)
+
+ /* %o0: sysino
+ * %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED})
+ */
+ENTRY(sun4v_intr_setenabled)
+ mov HV_FAST_INTR_SETENABLED, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_intr_setenabled)
+
+ /* %o0: sysino
+ *
+ * returns %o0: intr_state (HV_INTR_STATE_*)
+ */
+ENTRY(sun4v_intr_getstate)
+ mov HV_FAST_INTR_GETSTATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ mov %o1, %o0
+ENDPROC(sun4v_intr_getstate)
+
+ /* %o0: sysino
+ * %o1: intr_state (HV_INTR_STATE_*)
+ */
+ENTRY(sun4v_intr_setstate)
+ mov HV_FAST_INTR_SETSTATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_intr_setstate)
+
+ /* %o0: sysino
+ *
+ * returns %o0: cpuid
+ */
+ENTRY(sun4v_intr_gettarget)
+ mov HV_FAST_INTR_GETTARGET, %o5
+ ta HV_FAST_TRAP
+ retl
+ mov %o1, %o0
+ENDPROC(sun4v_intr_gettarget)
+
+ /* %o0: sysino
+ * %o1: cpuid
+ */
+ENTRY(sun4v_intr_settarget)
+ mov HV_FAST_INTR_SETTARGET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_intr_settarget)
+
+ /* %o0: cpuid
+ * %o1: pc
+ * %o2: rtba
+ * %o3: arg0
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_cpu_start)
+ mov HV_FAST_CPU_START, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_cpu_start)
+
+ /* %o0: cpuid
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_cpu_stop)
+ mov HV_FAST_CPU_STOP, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_cpu_stop)
+
+ /* returns %o0: status */
+ENTRY(sun4v_cpu_yield)
+ mov HV_FAST_CPU_YIELD, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_cpu_yield)
+
+ /* %o0: type
+ * %o1: queue paddr
+ * %o2: num queue entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_cpu_qconf)
+ mov HV_FAST_CPU_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_cpu_qconf)
+
+ /* %o0: num cpus in cpu list
+ * %o1: cpu list paddr
+ * %o2: mondo block paddr
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_cpu_mondo_send)
+ mov HV_FAST_CPU_MONDO_SEND, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_cpu_mondo_send)
+
+ /* %o0: CPU ID
+ *
+ * returns %o0: -status if status non-zero, else
+ * %o0: cpu state as HV_CPU_STATE_*
+ */
+ENTRY(sun4v_cpu_state)
+ mov HV_FAST_CPU_STATE, %o5
+ ta HV_FAST_TRAP
+ brnz,pn %o0, 1f
+ sub %g0, %o0, %o0
+ mov %o1, %o0
+1: retl
+ nop
+ENDPROC(sun4v_cpu_state)
+
+ /* %o0: virtual address
+ * %o1: must be zero
+ * %o2: TTE
+ * %o3: HV_MMU_* flags
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_mmu_map_perm_addr)
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_mmu_map_perm_addr)
+
+ /* %o0: number of TSB descriptions
+ * %o1: TSB descriptions real address
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_mmu_tsb_ctx0)
+ mov HV_FAST_MMU_TSB_CTX0, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_mmu_tsb_ctx0)
+
+ /* %o0: API group number
+ * %o1: pointer to unsigned long major number storage
+ * %o2: pointer to unsigned long minor number storage
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_get_version)
+ mov HV_CORE_GET_VER, %o5
+ mov %o1, %o3
+ mov %o2, %o4
+ ta HV_CORE_TRAP
+ stx %o1, [%o3]
+ retl
+ stx %o2, [%o4]
+ENDPROC(sun4v_get_version)
+
+ /* %o0: API group number
+ * %o1: desired major number
+ * %o2: desired minor number
+ * %o3: pointer to unsigned long actual minor number storage
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_set_version)
+ mov HV_CORE_SET_VER, %o5
+ mov %o3, %o4
+ ta HV_CORE_TRAP
+ retl
+ stx %o1, [%o4]
+ENDPROC(sun4v_set_version)
+
+ /* %o0: pointer to unsigned long time
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_tod_get)
+ mov %o0, %o4
+ mov HV_FAST_TOD_GET, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_tod_get)
+
+ /* %o0: time
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_tod_set)
+ mov HV_FAST_TOD_SET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_tod_set)
+
+ /* %o0: pointer to unsigned long status
+ *
+ * returns %o0: signed character
+ */
+ENTRY(sun4v_con_getchar)
+ mov %o0, %o4
+ mov HV_FAST_CONS_GETCHAR, %o5
+ clr %o0
+ clr %o1
+ ta HV_FAST_TRAP
+ stx %o0, [%o4]
+ retl
+ sra %o1, 0, %o0
+ENDPROC(sun4v_con_getchar)
+
+ /* %o0: signed long character
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_con_putchar)
+ mov HV_FAST_CONS_PUTCHAR, %o5
+ ta HV_FAST_TRAP
+ retl
+ sra %o0, 0, %o0
+ENDPROC(sun4v_con_putchar)
+
+ /* %o0: buffer real address
+ * %o1: buffer size
+ * %o2: pointer to unsigned long bytes_read
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_con_read)
+ mov %o2, %o4
+ mov HV_FAST_CONS_READ, %o5
+ ta HV_FAST_TRAP
+ brnz %o0, 1f
+ cmp %o1, -1 /* break */
+ be,a,pn %icc, 1f
+ mov %o1, %o0
+ cmp %o1, -2 /* hup */
+ be,a,pn %icc, 1f
+ mov %o1, %o0
+ stx %o1, [%o4]
+1: retl
+ nop
+ENDPROC(sun4v_con_read)
+
+ /* %o0: buffer real address
+ * %o1: buffer size
+ * %o2: pointer to unsigned long bytes_written
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_con_write)
+ mov %o2, %o4
+ mov HV_FAST_CONS_WRITE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_con_write)
+
+ /* %o0: soft state
+ * %o1: address of description string
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_mach_set_soft_state)
+ mov HV_FAST_MACH_SET_SOFT_STATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_mach_set_soft_state)
+
+ /* %o0: exit code
+ *
+ * Does not return.
+ */
+ENTRY(sun4v_mach_exit)
+ mov HV_FAST_MACH_EXIT, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_mach_exit)
+
+ /* %o0: buffer real address
+ * %o1: buffer length
+ * %o2: pointer to unsigned long real_buf_len
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_mach_desc)
+ mov %o2, %o4
+ mov HV_FAST_MACH_DESC, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_mach_desc)
+
+ /* %o0: new timeout in milliseconds
+ * %o1: pointer to unsigned long orig_timeout
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_mach_set_watchdog)
+ mov %o1, %o4
+ mov HV_FAST_MACH_SET_WATCHDOG, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_mach_set_watchdog)
+
+ /* No inputs and does not return. */
+ENTRY(sun4v_mach_sir)
+ mov %o1, %o4
+ mov HV_FAST_MACH_SIR, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_mach_sir)
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_tx_qconf)
+ mov HV_FAST_LDC_TX_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_tx_qconf)
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_tx_qinfo)
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_TX_QINFO, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ENDPROC(sun4v_ldc_tx_qinfo)
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long head_off
+ * %o2: pointer to unsigned long tail_off
+ * %o2: pointer to unsigned long chan_state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_tx_get_state)
+ mov %o1, %g1
+ mov %o2, %g2
+ mov %o3, %g3
+ mov HV_FAST_LDC_TX_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ stx %o3, [%g3]
+ retl
+ nop
+ENDPROC(sun4v_ldc_tx_get_state)
+
+ /* %o0: channel
+ * %o1: tail_off
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_tx_set_qtail)
+ mov HV_FAST_LDC_TX_SET_QTAIL, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_tx_set_qtail)
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_rx_qconf)
+ mov HV_FAST_LDC_RX_QCONF, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_rx_qconf)
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_rx_qinfo)
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_RX_QINFO, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ENDPROC(sun4v_ldc_rx_qinfo)
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long head_off
+ * %o2: pointer to unsigned long tail_off
+ * %o2: pointer to unsigned long chan_state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_rx_get_state)
+ mov %o1, %g1
+ mov %o2, %g2
+ mov %o3, %g3
+ mov HV_FAST_LDC_RX_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ stx %o3, [%g3]
+ retl
+ nop
+ENDPROC(sun4v_ldc_rx_get_state)
+
+ /* %o0: channel
+ * %o1: head_off
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_rx_set_qhead)
+ mov HV_FAST_LDC_RX_SET_QHEAD, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_rx_set_qhead)
+
+ /* %o0: channel
+ * %o1: ra
+ * %o2: num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_set_map_table)
+ mov HV_FAST_LDC_SET_MAP_TABLE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_set_map_table)
+
+ /* %o0: channel
+ * %o1: pointer to unsigned long ra
+ * %o2: pointer to unsigned long num_entries
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_get_map_table)
+ mov %o1, %g1
+ mov %o2, %g2
+ mov HV_FAST_LDC_GET_MAP_TABLE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ENDPROC(sun4v_ldc_get_map_table)
+
+ /* %o0: channel
+ * %o1: dir_code
+ * %o2: tgt_raddr
+ * %o3: lcl_raddr
+ * %o4: len
+ * %o5: pointer to unsigned long actual_len
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_copy)
+ mov %o5, %g1
+ mov HV_FAST_LDC_COPY, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ENDPROC(sun4v_ldc_copy)
+
+ /* %o0: channel
+ * %o1: cookie
+ * %o2: pointer to unsigned long ra
+ * %o3: pointer to unsigned long perm
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_mapin)
+ mov %o2, %g1
+ mov %o3, %g2
+ mov HV_FAST_LDC_MAPIN, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ stx %o2, [%g2]
+ retl
+ nop
+ENDPROC(sun4v_ldc_mapin)
+
+ /* %o0: ra
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_unmap)
+ mov HV_FAST_LDC_UNMAP, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_unmap)
+
+ /* %o0: channel
+ * %o1: cookie
+ * %o2: mte_cookie
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ldc_revoke)
+ mov HV_FAST_LDC_REVOKE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ldc_revoke)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long cookie
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_get_cookie)
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_COOKIE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ENDPROC(sun4v_vintr_get_cookie)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: cookie
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_set_cookie)
+ mov HV_FAST_VINTR_SET_COOKIE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_vintr_set_cookie)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long valid_state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_get_valid)
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_VALID, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ENDPROC(sun4v_vintr_get_valid)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: valid_state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_set_valid)
+ mov HV_FAST_VINTR_SET_VALID, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_vintr_set_valid)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_get_state)
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_STATE, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ENDPROC(sun4v_vintr_get_state)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: state
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_set_state)
+ mov HV_FAST_VINTR_SET_STATE, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_vintr_set_state)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: pointer to unsigned long cpuid
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_get_target)
+ mov %o2, %g1
+ mov HV_FAST_VINTR_GET_TARGET, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%g1]
+ retl
+ nop
+ENDPROC(sun4v_vintr_get_target)
+
+ /* %o0: device handle
+ * %o1: device INO
+ * %o2: cpuid
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_vintr_set_target)
+ mov HV_FAST_VINTR_SET_TARGET, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_vintr_set_target)
+
+ /* %o0: NCS sub-function
+ * %o1: sub-function arg real-address
+ * %o2: sub-function arg size
+ *
+ * returns %o0: status
+ */
+ENTRY(sun4v_ncs_request)
+ mov HV_FAST_NCS_REQUEST, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_ncs_request)
+
+ENTRY(sun4v_svc_send)
+ save %sp, -192, %sp
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ mov HV_FAST_SVC_SEND, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%i3]
+ ret
+ restore
+ENDPROC(sun4v_svc_send)
+
+ENTRY(sun4v_svc_recv)
+ save %sp, -192, %sp
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ mov HV_FAST_SVC_RECV, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%i3]
+ ret
+ restore
+ENDPROC(sun4v_svc_recv)
+
+ENTRY(sun4v_svc_getstatus)
+ mov HV_FAST_SVC_GETSTATUS, %o5
+ mov %o1, %o4
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_svc_getstatus)
+
+ENTRY(sun4v_svc_setstatus)
+ mov HV_FAST_SVC_SETSTATUS, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_svc_setstatus)
+
+ENTRY(sun4v_svc_clrstatus)
+ mov HV_FAST_SVC_CLRSTATUS, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_svc_clrstatus)
+
+ENTRY(sun4v_mmustat_conf)
+ mov %o1, %o4
+ mov HV_FAST_MMUSTAT_CONF, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_mmustat_conf)
+
+ENTRY(sun4v_mmustat_info)
+ mov %o0, %o4
+ mov HV_FAST_MMUSTAT_INFO, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_mmustat_info)
+
+ENTRY(sun4v_mmu_demap_all)
+ clr %o0
+ clr %o1
+ mov HV_MMU_ALL, %o2
+ mov HV_FAST_MMU_DEMAP_ALL, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_mmu_demap_all)
+
+ENTRY(sun4v_niagara_getperf)
+ mov %o0, %o4
+ mov HV_FAST_GET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_niagara_getperf)
+
+ENTRY(sun4v_niagara_setperf)
+ mov HV_FAST_SET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_niagara_setperf)
+
+ENTRY(sun4v_niagara2_getperf)
+ mov %o0, %o4
+ mov HV_FAST_N2_GET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_niagara2_getperf)
+
+ENTRY(sun4v_niagara2_setperf)
+ mov HV_FAST_N2_SET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_niagara2_setperf)
diff --git a/arch/sparc/kernel/hvtramp.S b/arch/sparc/kernel/hvtramp.S
new file mode 100644
index 000000000000..9365432904d6
--- /dev/null
+++ b/arch/sparc/kernel/hvtramp.S
@@ -0,0 +1,140 @@
+/* hvtramp.S: Hypervisor start-cpu trampoline code.
+ *
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/init.h>
+
+#include <asm/thread_info.h>
+#include <asm/hypervisor.h>
+#include <asm/scratchpad.h>
+#include <asm/spitfire.h>
+#include <asm/hvtramp.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/pil.h>
+
+ __CPUINIT
+ .align 8
+ .globl hv_cpu_startup, hv_cpu_startup_end
+
+ /* This code executes directly out of the hypervisor
+ * with physical addressing (va==pa). %o0 contains
+ * our client argument which for Linux points to
+ * a descriptor data structure which defines the
+ * MMU entries we need to load up.
+ *
+ * After we set things up we enable the MMU and call
+ * into the kernel.
+ *
+ * First setup basic privileged cpu state.
+ */
+hv_cpu_startup:
+ SET_GL(0)
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ wrpr %g0, 0, %canrestore
+ wrpr %g0, 0, %otherwin
+ wrpr %g0, 6, %cansave
+ wrpr %g0, 6, %cleanwin
+ wrpr %g0, 0, %cwp
+ wrpr %g0, 0, %wstate
+ wrpr %g0, 0, %tl
+
+ sethi %hi(sparc64_ttable_tl0), %g1
+ wrpr %g1, %tba
+
+ mov %o0, %l0
+
+ lduw [%l0 + HVTRAMP_DESCR_CPU], %g1
+ mov SCRATCHPAD_CPUID, %g2
+ stxa %g1, [%g2] ASI_SCRATCHPAD
+
+ ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_VA], %g2
+ stxa %g2, [%g0] ASI_SCRATCHPAD
+
+ mov 0, %l1
+ lduw [%l0 + HVTRAMP_DESCR_NUM_MAPPINGS], %l2
+ add %l0, HVTRAMP_DESCR_MAPS, %l3
+
+1: ldx [%l3 + HVTRAMP_MAPPING_VADDR], %o0
+ clr %o1
+ ldx [%l3 + HVTRAMP_MAPPING_TTE], %o2
+ mov HV_MMU_IMMU | HV_MMU_DMMU, %o3
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ ta HV_FAST_TRAP
+
+ brnz,pn %o0, 80f
+ nop
+
+ add %l1, 1, %l1
+ cmp %l1, %l2
+ blt,a,pt %xcc, 1b
+ add %l3, HVTRAMP_MAPPING_SIZE, %l3
+
+ ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_PA], %o0
+ mov HV_FAST_MMU_FAULT_AREA_CONF, %o5
+ ta HV_FAST_TRAP
+
+ brnz,pn %o0, 80f
+ nop
+
+ wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
+
+ ldx [%l0 + HVTRAMP_DESCR_THREAD_REG], %l6
+
+ mov 1, %o0
+ set 1f, %o1
+ mov HV_FAST_MMU_ENABLE, %o5
+ ta HV_FAST_TRAP
+
+ ba,pt %xcc, 80f
+ nop
+
+1:
+ wr %g0, 0, %fprs
+ wr %g0, ASI_P, %asi
+
+ mov PRIMARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+
+ mov SECONDARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+
+ mov %l6, %g6
+ ldx [%g6 + TI_TASK], %g4
+
+ mov 1, %g5
+ sllx %g5, THREAD_SHIFT, %g5
+ sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
+ add %g6, %g5, %sp
+ mov 0, %fp
+
+ call init_irqwork_curcpu
+ nop
+ call hard_smp_processor_id
+ nop
+
+ call sun4v_register_mondo_queues
+ nop
+
+ call init_cur_cpu_trap
+ mov %g6, %o0
+
+ wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate
+
+ call smp_callin
+ nop
+ call cpu_idle
+ mov 0, %o0
+ call cpu_panic
+ nop
+
+80: ba,pt %xcc, 80b
+ nop
+
+ .align 8
+hv_cpu_startup_end:
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
index 223a6582e1e2..c16135e0c151 100644
--- a/arch/sparc/kernel/idprom.c
+++ b/arch/sparc/kernel/idprom.c
@@ -11,35 +11,37 @@
#include <asm/oplib.h>
#include <asm/idprom.h>
-#include <asm/machines.h> /* Fun with Sun released architectures. */
struct idprom *idprom;
static struct idprom idprom_buffer;
+#ifdef CONFIG_SPARC32
+#include <asm/machines.h> /* Fun with Sun released architectures. */
+
/* Here is the master table of Sun machines which use some implementation
* of the Sparc CPU and have a meaningful IDPROM machtype value that we
* know about. See asm-sparc/machines.h for empirical constants.
*/
static struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = {
/* First, Sun4's */
-{ "Sun 4/100 Series", (SM_SUN4 | SM_4_110) },
-{ "Sun 4/200 Series", (SM_SUN4 | SM_4_260) },
-{ "Sun 4/300 Series", (SM_SUN4 | SM_4_330) },
-{ "Sun 4/400 Series", (SM_SUN4 | SM_4_470) },
+{ .name = "Sun 4/100 Series", .id_machtype = (SM_SUN4 | SM_4_110) },
+{ .name = "Sun 4/200 Series", .id_machtype = (SM_SUN4 | SM_4_260) },
+{ .name = "Sun 4/300 Series", .id_machtype = (SM_SUN4 | SM_4_330) },
+{ .name = "Sun 4/400 Series", .id_machtype = (SM_SUN4 | SM_4_470) },
/* Now, Sun4c's */
-{ "Sun4c SparcStation 1", (SM_SUN4C | SM_4C_SS1) },
-{ "Sun4c SparcStation IPC", (SM_SUN4C | SM_4C_IPC) },
-{ "Sun4c SparcStation 1+", (SM_SUN4C | SM_4C_SS1PLUS) },
-{ "Sun4c SparcStation SLC", (SM_SUN4C | SM_4C_SLC) },
-{ "Sun4c SparcStation 2", (SM_SUN4C | SM_4C_SS2) },
-{ "Sun4c SparcStation ELC", (SM_SUN4C | SM_4C_ELC) },
-{ "Sun4c SparcStation IPX", (SM_SUN4C | SM_4C_IPX) },
+{ .name = "Sun4c SparcStation 1", .id_machtype = (SM_SUN4C | SM_4C_SS1) },
+{ .name = "Sun4c SparcStation IPC", .id_machtype = (SM_SUN4C | SM_4C_IPC) },
+{ .name = "Sun4c SparcStation 1+", .id_machtype = (SM_SUN4C | SM_4C_SS1PLUS) },
+{ .name = "Sun4c SparcStation SLC", .id_machtype = (SM_SUN4C | SM_4C_SLC) },
+{ .name = "Sun4c SparcStation 2", .id_machtype = (SM_SUN4C | SM_4C_SS2) },
+{ .name = "Sun4c SparcStation ELC", .id_machtype = (SM_SUN4C | SM_4C_ELC) },
+{ .name = "Sun4c SparcStation IPX", .id_machtype = (SM_SUN4C | SM_4C_IPX) },
/* Finally, early Sun4m's */
-{ "Sun4m SparcSystem600", (SM_SUN4M | SM_4M_SS60) },
-{ "Sun4m SparcStation10/20", (SM_SUN4M | SM_4M_SS50) },
-{ "Sun4m SparcStation5", (SM_SUN4M | SM_4M_SS40) },
+{ .name = "Sun4m SparcSystem600", .id_machtype = (SM_SUN4M | SM_4M_SS60) },
+{ .name = "Sun4m SparcStation10/20", .id_machtype = (SM_SUN4M | SM_4M_SS50) },
+{ .name = "Sun4m SparcStation5", .id_machtype = (SM_SUN4M | SM_4M_SS40) },
/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
-{ "Sun4M OBP based system", (SM_SUN4M_OBP | 0x0) } };
+{ .name = "Sun4M OBP based system", .id_machtype = (SM_SUN4M_OBP | 0x0) } };
static void __init display_system_type(unsigned char machtype)
{
@@ -47,21 +49,25 @@ static void __init display_system_type(unsigned char machtype)
register int i;
for (i = 0; i < NUM_SUN_MACHINES; i++) {
- if(Sun_Machines[i].id_machtype == machtype) {
+ if (Sun_Machines[i].id_machtype == machtype) {
if (machtype != (SM_SUN4M_OBP | 0x00) ||
prom_getproperty(prom_root_node, "banner-name",
sysname, sizeof(sysname)) <= 0)
- printk("TYPE: %s\n", Sun_Machines[i].name);
+ printk(KERN_WARNING "TYPE: %s\n",
+ Sun_Machines[i].name);
else
- printk("TYPE: %s\n", sysname);
+ printk(KERN_WARNING "TYPE: %s\n", sysname);
return;
}
}
- prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype);
- prom_halt();
+ prom_printf("IDPROM: Warning, bogus id_machtype value, 0x%x\n", machtype);
}
-
+#else
+static void __init display_system_type(unsigned char machtype)
+{
+}
+#endif
/* Calculate the IDPROM checksum (xor of the data bytes). */
static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
{
@@ -80,21 +86,14 @@ void __init idprom_init(void)
idprom = &idprom_buffer;
- if (idprom->id_format != 0x01) {
- prom_printf("IDPROM: Unknown format type!\n");
- prom_halt();
- }
+ if (idprom->id_format != 0x01)
+ prom_printf("IDPROM: Warning, unknown format type!\n");
- if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
- prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n",
+ if (idprom->id_cksum != calc_idprom_cksum(idprom))
+ prom_printf("IDPROM: Warning, checksum failure (nvram=%x, calc=%x)!\n",
idprom->id_cksum, calc_idprom_cksum(idprom));
- prom_halt();
- }
display_system_type(idprom->id_machtype);
- printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
- idprom->id_ethaddr[0], idprom->id_ethaddr[1],
- idprom->id_ethaddr[2], idprom->id_ethaddr[3],
- idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
+ printk(KERN_WARNING "Ethernet address: %pM\n", idprom->id_ethaddr);
}
diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c
index 8e64ebc445ef..62126e4cec54 100644
--- a/arch/sparc/kernel/init_task.c
+++ b/arch/sparc/kernel/init_task.c
@@ -23,6 +23,5 @@ EXPORT_SYMBOL(init_task);
* in etrap.S which assumes it.
*/
union thread_union init_thread_union
- __attribute__((section (".text\"\n\t#")))
- __attribute__((aligned (THREAD_SIZE)))
+ __attribute__((section (".data.init_task")))
= { INIT_THREAD_INFO(init_task) };
diff --git a/arch/sparc64/kernel/iommu.c b/arch/sparc/kernel/iommu.c
index 1cc1995531e2..1cc1995531e2 100644
--- a/arch/sparc64/kernel/iommu.c
+++ b/arch/sparc/kernel/iommu.c
diff --git a/arch/sparc64/kernel/iommu_common.h b/arch/sparc/kernel/iommu_common.h
index 591f5879039c..591f5879039c 100644
--- a/arch/sparc64/kernel/iommu_common.h
+++ b/arch/sparc/kernel/iommu_common.h
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index 4f025b36934b..7ce14f05eb48 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -552,8 +552,8 @@ int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sgl, int nents,
/* IIep is write-through, not flushing. */
for_each_sg(sgl, sg, nents, n) {
BUG_ON(page_address(sg_page(sg)) == NULL);
- sg->dvma_address = virt_to_phys(sg_virt(sg));
- sg->dvma_length = sg->length;
+ sg->dma_address = virt_to_phys(sg_virt(sg));
+ sg->dma_length = sg->length;
}
return nents;
}
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
deleted file mode 100644
index 93e1d1c65290..000000000000
--- a/arch/sparc/kernel/irq.c
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
- * Sparc the IRQs are basically 'cast in stone'
- * and you are supposed to probe the prom's device
- * node trees to find out who's got which IRQ.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
- * Copyright (C) 1995,2002 Pete A. Zaitcev (zaitcev@yahoo.com)
- * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
- * Copyright (C) 1998-2000 Anton Blanchard (anton@samba.org)
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/linkage.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/init.h>
-#include <linux/smp.h>
-#include <linux/delay.h>
-#include <linux/threads.h>
-#include <linux/spinlock.h>
-#include <linux/seq_file.h>
-
-#include <asm/ptrace.h>
-#include <asm/processor.h>
-#include <asm/system.h>
-#include <asm/psr.h>
-#include <asm/smp.h>
-#include <asm/vaddrs.h>
-#include <asm/timer.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/traps.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
-#include <asm/pcic.h>
-#include <asm/cacheflush.h>
-#include <asm/irq_regs.h>
-
-#include "irq.h"
-
-#ifdef CONFIG_SMP
-#define SMP_NOP2 "nop; nop;\n\t"
-#define SMP_NOP3 "nop; nop; nop;\n\t"
-#else
-#define SMP_NOP2
-#define SMP_NOP3
-#endif /* SMP */
-unsigned long __raw_local_irq_save(void)
-{
- unsigned long retval;
- unsigned long tmp;
-
- __asm__ __volatile__(
- "rd %%psr, %0\n\t"
- SMP_NOP3 /* Sun4m + Cypress + SMP bug */
- "or %0, %2, %1\n\t"
- "wr %1, 0, %%psr\n\t"
- "nop; nop; nop\n"
- : "=&r" (retval), "=r" (tmp)
- : "i" (PSR_PIL)
- : "memory");
-
- return retval;
-}
-
-void raw_local_irq_enable(void)
-{
- unsigned long tmp;
-
- __asm__ __volatile__(
- "rd %%psr, %0\n\t"
- SMP_NOP3 /* Sun4m + Cypress + SMP bug */
- "andn %0, %1, %0\n\t"
- "wr %0, 0, %%psr\n\t"
- "nop; nop; nop\n"
- : "=&r" (tmp)
- : "i" (PSR_PIL)
- : "memory");
-}
-
-void raw_local_irq_restore(unsigned long old_psr)
-{
- unsigned long tmp;
-
- __asm__ __volatile__(
- "rd %%psr, %0\n\t"
- "and %2, %1, %2\n\t"
- SMP_NOP2 /* Sun4m + Cypress + SMP bug */
- "andn %0, %1, %0\n\t"
- "wr %0, %2, %%psr\n\t"
- "nop; nop; nop\n"
- : "=&r" (tmp)
- : "i" (PSR_PIL), "r" (old_psr)
- : "memory");
-}
-
-EXPORT_SYMBOL(__raw_local_irq_save);
-EXPORT_SYMBOL(raw_local_irq_enable);
-EXPORT_SYMBOL(raw_local_irq_restore);
-
-/*
- * Dave Redman (djhr@tadpole.co.uk)
- *
- * IRQ numbers.. These are no longer restricted to 15..
- *
- * this is done to enable SBUS cards and onboard IO to be masked
- * correctly. using the interrupt level isn't good enough.
- *
- * For example:
- * A device interrupting at sbus level6 and the Floppy both come in
- * at IRQ11, but enabling and disabling them requires writing to
- * different bits in the SLAVIO/SEC.
- *
- * As a result of these changes sun4m machines could now support
- * directed CPU interrupts using the existing enable/disable irq code
- * with tweaks.
- *
- */
-
-static void irq_panic(void)
-{
- extern char *cputypval;
- prom_printf("machine: %s doesn't have irq handlers defined!\n",cputypval);
- prom_halt();
-}
-
-void (*sparc_init_timers)(irq_handler_t ) =
- (void (*)(irq_handler_t )) irq_panic;
-
-/*
- * Dave Redman (djhr@tadpole.co.uk)
- *
- * There used to be extern calls and hard coded values here.. very sucky!
- * instead, because some of the devices attach very early, I do something
- * equally sucky but at least we'll never try to free statically allocated
- * space or call kmalloc before kmalloc_init :(.
- *
- * In fact it's the timer10 that attaches first.. then timer14
- * then kmalloc_init is called.. then the tty interrupts attach.
- * hmmm....
- *
- */
-#define MAX_STATIC_ALLOC 4
-struct irqaction static_irqaction[MAX_STATIC_ALLOC];
-int static_irq_count;
-
-static struct {
- struct irqaction *action;
- int flags;
-} sparc_irq[NR_IRQS];
-#define SPARC_IRQ_INPROGRESS 1
-
-/* Used to protect the IRQ action lists */
-DEFINE_SPINLOCK(irq_action_lock);
-
-int show_interrupts(struct seq_file *p, void *v)
-{
- int i = *(loff_t *) v;
- struct irqaction * action;
- unsigned long flags;
-#ifdef CONFIG_SMP
- int j;
-#endif
-
- if (sparc_cpu_model == sun4d) {
- extern int show_sun4d_interrupts(struct seq_file *, void *);
-
- return show_sun4d_interrupts(p, v);
- }
- spin_lock_irqsave(&irq_action_lock, flags);
- if (i < NR_IRQS) {
- action = sparc_irq[i].action;
- if (!action)
- goto out_unlock;
- seq_printf(p, "%3d: ", i);
-#ifndef CONFIG_SMP
- seq_printf(p, "%10u ", kstat_irqs(i));
-#else
- for_each_online_cpu(j) {
- seq_printf(p, "%10u ",
- kstat_cpu(j).irqs[i]);
- }
-#endif
- seq_printf(p, " %c %s",
- (action->flags & IRQF_DISABLED) ? '+' : ' ',
- action->name);
- for (action=action->next; action; action = action->next) {
- seq_printf(p, ",%s %s",
- (action->flags & IRQF_DISABLED) ? " +" : "",
- action->name);
- }
- seq_putc(p, '\n');
- }
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
- return 0;
-}
-
-void free_irq(unsigned int irq, void *dev_id)
-{
- struct irqaction * action;
- struct irqaction **actionp;
- unsigned long flags;
- unsigned int cpu_irq;
-
- if (sparc_cpu_model == sun4d) {
- extern void sun4d_free_irq(unsigned int, void *);
-
- sun4d_free_irq(irq, dev_id);
- return;
- }
- cpu_irq = irq & (NR_IRQS - 1);
- if (cpu_irq > 14) { /* 14 irq levels on the sparc */
- printk("Trying to free bogus IRQ %d\n", irq);
- return;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- actionp = &sparc_irq[cpu_irq].action;
- action = *actionp;
-
- if (!action->handler) {
- printk("Trying to free free IRQ%d\n",irq);
- goto out_unlock;
- }
- if (dev_id) {
- for (; action; action = action->next) {
- if (action->dev_id == dev_id)
- break;
- actionp = &action->next;
- }
- if (!action) {
- printk("Trying to free free shared IRQ%d\n",irq);
- goto out_unlock;
- }
- } else if (action->flags & IRQF_SHARED) {
- printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
- goto out_unlock;
- }
- if (action->flags & SA_STATIC_ALLOC)
- {
- /* This interrupt is marked as specially allocated
- * so it is a bad idea to free it.
- */
- printk("Attempt to free statically allocated IRQ%d (%s)\n",
- irq, action->name);
- goto out_unlock;
- }
-
- *actionp = action->next;
-
- spin_unlock_irqrestore(&irq_action_lock, flags);
-
- synchronize_irq(irq);
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- kfree(action);
-
- if (!sparc_irq[cpu_irq].action)
- __disable_irq(irq);
-
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-}
-
-EXPORT_SYMBOL(free_irq);
-
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-#ifdef CONFIG_SMP
-void synchronize_irq(unsigned int irq)
-{
- unsigned int cpu_irq;
-
- cpu_irq = irq & (NR_IRQS - 1);
- while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
- cpu_relax();
-}
-#endif /* SMP */
-
-void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs)
-{
- int i;
- struct irqaction * action;
- unsigned int cpu_irq;
-
- cpu_irq = irq & (NR_IRQS - 1);
- action = sparc_irq[cpu_irq].action;
-
- printk("IO device interrupt, irq = %d\n", irq);
- printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
- regs->npc, regs->u_regs[14]);
- if (action) {
- printk("Expecting: ");
- for (i = 0; i < 16; i++)
- if (action->handler)
- printk("[%s:%d:0x%x] ", action->name,
- (int) i, (unsigned int) action->handler);
- }
- printk("AIEEE\n");
- panic("bogus interrupt received");
-}
-
-void handler_irq(int irq, struct pt_regs * regs)
-{
- struct pt_regs *old_regs;
- struct irqaction * action;
- int cpu = smp_processor_id();
-#ifdef CONFIG_SMP
- extern void smp4m_irq_rotate(int cpu);
-#endif
-
- old_regs = set_irq_regs(regs);
- irq_enter();
- disable_pil_irq(irq);
-#ifdef CONFIG_SMP
- /* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */
- if((sparc_cpu_model==sun4m) && (irq < 10))
- smp4m_irq_rotate(cpu);
-#endif
- action = sparc_irq[irq].action;
- sparc_irq[irq].flags |= SPARC_IRQ_INPROGRESS;
- kstat_cpu(cpu).irqs[irq]++;
- do {
- if (!action || !action->handler)
- unexpected_irq(irq, NULL, regs);
- action->handler(irq, action->dev_id);
- action = action->next;
- } while (action);
- sparc_irq[irq].flags &= ~SPARC_IRQ_INPROGRESS;
- enable_pil_irq(irq);
- irq_exit();
- set_irq_regs(old_regs);
-}
-
-#if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
-
-/* Fast IRQs on the Sparc can only have one routine attached to them,
- * thus no sharing possible.
- */
-static int request_fast_irq(unsigned int irq,
- void (*handler)(void),
- unsigned long irqflags, const char *devname)
-{
- struct irqaction *action;
- unsigned long flags;
- unsigned int cpu_irq;
- int ret;
-#ifdef CONFIG_SMP
- struct tt_entry *trap_table;
- extern struct tt_entry trapbase_cpu1, trapbase_cpu2, trapbase_cpu3;
-#endif
-
- cpu_irq = irq & (NR_IRQS - 1);
- if(cpu_irq > 14) {
- ret = -EINVAL;
- goto out;
- }
- if(!handler) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- action = sparc_irq[cpu_irq].action;
- if(action) {
- if(action->flags & IRQF_SHARED)
- panic("Trying to register fast irq when already shared.\n");
- if(irqflags & IRQF_SHARED)
- panic("Trying to register fast irq as shared.\n");
-
- /* Anyway, someone already owns it so cannot be made fast. */
- printk("request_fast_irq: Trying to register yet already owned.\n");
- ret = -EBUSY;
- goto out_unlock;
- }
-
- /* If this is flagged as statically allocated then we use our
- * private struct which is never freed.
- */
- if (irqflags & SA_STATIC_ALLOC) {
- if (static_irq_count < MAX_STATIC_ALLOC)
- action = &static_irqaction[static_irq_count++];
- else
- printk("Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
- irq, devname);
- }
-
- if (action == NULL)
- action = kmalloc(sizeof(struct irqaction),
- GFP_ATOMIC);
-
- if (!action) {
- ret = -ENOMEM;
- goto out_unlock;
- }
-
- /* Dork with trap table if we get this far. */
-#define INSTANTIATE(table) \
- table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
- table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
- SPARC_BRANCH((unsigned long) handler, \
- (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
- table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
- table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
-
- INSTANTIATE(sparc_ttable)
-#ifdef CONFIG_SMP
- trap_table = &trapbase_cpu1; INSTANTIATE(trap_table)
- trap_table = &trapbase_cpu2; INSTANTIATE(trap_table)
- trap_table = &trapbase_cpu3; INSTANTIATE(trap_table)
-#endif
-#undef INSTANTIATE
- /*
- * XXX Correct thing whould be to flush only I- and D-cache lines
- * which contain the handler in question. But as of time of the
- * writing we have no CPU-neutral interface to fine-grained flushes.
- */
- flush_cache_all();
-
- action->flags = irqflags;
- cpus_clear(action->mask);
- action->name = devname;
- action->dev_id = NULL;
- action->next = NULL;
-
- sparc_irq[cpu_irq].action = action;
-
- __enable_irq(irq);
-
- ret = 0;
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
- return ret;
-}
-
-/* These variables are used to access state from the assembler
- * interrupt handler, floppy_hardint, so we cannot put these in
- * the floppy driver image because that would not work in the
- * modular case.
- */
-volatile unsigned char *fdc_status;
-EXPORT_SYMBOL(fdc_status);
-
-char *pdma_vaddr;
-EXPORT_SYMBOL(pdma_vaddr);
-
-unsigned long pdma_size;
-EXPORT_SYMBOL(pdma_size);
-
-volatile int doing_pdma;
-EXPORT_SYMBOL(doing_pdma);
-
-char *pdma_base;
-EXPORT_SYMBOL(pdma_base);
-
-unsigned long pdma_areasize;
-EXPORT_SYMBOL(pdma_areasize);
-
-extern void floppy_hardint(void);
-
-static irq_handler_t floppy_irq_handler;
-
-void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct pt_regs *old_regs;
- int cpu = smp_processor_id();
-
- old_regs = set_irq_regs(regs);
- disable_pil_irq(irq);
- irq_enter();
- kstat_cpu(cpu).irqs[irq]++;
- floppy_irq_handler(irq, dev_id);
- irq_exit();
- enable_pil_irq(irq);
- set_irq_regs(old_regs);
- // XXX Eek, it's totally changed with preempt_count() and such
- // if (softirq_pending(cpu))
- // do_softirq();
-}
-
-int sparc_floppy_request_irq(int irq, unsigned long flags,
- irq_handler_t irq_handler)
-{
- floppy_irq_handler = irq_handler;
- return request_fast_irq(irq, floppy_hardint, flags, "floppy");
-}
-EXPORT_SYMBOL(sparc_floppy_request_irq);
-
-#endif
-
-int request_irq(unsigned int irq,
- irq_handler_t handler,
- unsigned long irqflags, const char * devname, void *dev_id)
-{
- struct irqaction * action, **actionp;
- unsigned long flags;
- unsigned int cpu_irq;
- int ret;
-
- if (sparc_cpu_model == sun4d) {
- extern int sun4d_request_irq(unsigned int,
- irq_handler_t ,
- unsigned long, const char *, void *);
- return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
- }
- cpu_irq = irq & (NR_IRQS - 1);
- if(cpu_irq > 14) {
- ret = -EINVAL;
- goto out;
- }
- if (!handler) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- actionp = &sparc_irq[cpu_irq].action;
- action = *actionp;
- if (action) {
- if (!(action->flags & IRQF_SHARED) || !(irqflags & IRQF_SHARED)) {
- ret = -EBUSY;
- goto out_unlock;
- }
- if ((action->flags & IRQF_DISABLED) != (irqflags & IRQF_DISABLED)) {
- printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
- ret = -EBUSY;
- goto out_unlock;
- }
- for ( ; action; action = *actionp)
- actionp = &action->next;
- }
-
- /* If this is flagged as statically allocated then we use our
- * private struct which is never freed.
- */
- if (irqflags & SA_STATIC_ALLOC) {
- if (static_irq_count < MAX_STATIC_ALLOC)
- action = &static_irqaction[static_irq_count++];
- else
- printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", irq, devname);
- }
-
- if (action == NULL)
- action = kmalloc(sizeof(struct irqaction),
- GFP_ATOMIC);
-
- if (!action) {
- ret = -ENOMEM;
- goto out_unlock;
- }
-
- action->handler = handler;
- action->flags = irqflags;
- cpus_clear(action->mask);
- action->name = devname;
- action->next = NULL;
- action->dev_id = dev_id;
-
- *actionp = action;
-
- __enable_irq(irq);
-
- ret = 0;
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
- return ret;
-}
-
-EXPORT_SYMBOL(request_irq);
-
-void disable_irq_nosync(unsigned int irq)
-{
- return __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq_nosync);
-
-void disable_irq(unsigned int irq)
-{
- return __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq);
-
-void enable_irq(unsigned int irq)
-{
- return __enable_irq(irq);
-}
-
-EXPORT_SYMBOL(enable_irq);
-
-/* We really don't need these at all on the Sparc. We only have
- * stubs here because they are exported to modules.
- */
-unsigned long probe_irq_on(void)
-{
- return 0;
-}
-
-EXPORT_SYMBOL(probe_irq_on);
-
-int probe_irq_off(unsigned long mask)
-{
- return 0;
-}
-
-EXPORT_SYMBOL(probe_irq_off);
-
-/* djhr
- * This could probably be made indirect too and assigned in the CPU
- * bits of the code. That would be much nicer I think and would also
- * fit in with the idea of being able to tune your kernel for your machine
- * by removing unrequired machine and device support.
- *
- */
-
-void __init init_IRQ(void)
-{
- extern void sun4c_init_IRQ( void );
- extern void sun4m_init_IRQ( void );
- extern void sun4d_init_IRQ( void );
-
- switch(sparc_cpu_model) {
- case sun4c:
- case sun4:
- sun4c_init_IRQ();
- break;
-
- case sun4m:
-#ifdef CONFIG_PCI
- pcic_probe();
- if (pcic_present()) {
- sun4m_pci_init_IRQ();
- break;
- }
-#endif
- sun4m_init_IRQ();
- break;
-
- case sun4d:
- sun4d_init_IRQ();
- break;
-
- default:
- prom_printf("Cannot initialize IRQs on this Sun machine...");
- break;
- }
- btfixup();
-}
-
-void init_irq_proc(void)
-{
- /* For now, nothing... */
-}
diff --git a/arch/sparc/kernel/irq_32.c b/arch/sparc/kernel/irq_32.c
new file mode 100644
index 000000000000..f3488c45d57a
--- /dev/null
+++ b/arch/sparc/kernel/irq_32.c
@@ -0,0 +1,675 @@
+/*
+ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
+ * Sparc the IRQs are basically 'cast in stone'
+ * and you are supposed to probe the prom's device
+ * node trees to find out who's got which IRQ.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1995,2002 Pete A. Zaitcev (zaitcev@yahoo.com)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1998-2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/seq_file.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/smp.h>
+#include <asm/vaddrs.h>
+#include <asm/timer.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/pcic.h>
+#include <asm/cacheflush.h>
+#include <asm/irq_regs.h>
+
+#include "kernel.h"
+#include "irq.h"
+
+#ifdef CONFIG_SMP
+#define SMP_NOP2 "nop; nop;\n\t"
+#define SMP_NOP3 "nop; nop; nop;\n\t"
+#else
+#define SMP_NOP2
+#define SMP_NOP3
+#endif /* SMP */
+unsigned long __raw_local_irq_save(void)
+{
+ unsigned long retval;
+ unsigned long tmp;
+
+ __asm__ __volatile__(
+ "rd %%psr, %0\n\t"
+ SMP_NOP3 /* Sun4m + Cypress + SMP bug */
+ "or %0, %2, %1\n\t"
+ "wr %1, 0, %%psr\n\t"
+ "nop; nop; nop\n"
+ : "=&r" (retval), "=r" (tmp)
+ : "i" (PSR_PIL)
+ : "memory");
+
+ return retval;
+}
+
+void raw_local_irq_enable(void)
+{
+ unsigned long tmp;
+
+ __asm__ __volatile__(
+ "rd %%psr, %0\n\t"
+ SMP_NOP3 /* Sun4m + Cypress + SMP bug */
+ "andn %0, %1, %0\n\t"
+ "wr %0, 0, %%psr\n\t"
+ "nop; nop; nop\n"
+ : "=&r" (tmp)
+ : "i" (PSR_PIL)
+ : "memory");
+}
+
+void raw_local_irq_restore(unsigned long old_psr)
+{
+ unsigned long tmp;
+
+ __asm__ __volatile__(
+ "rd %%psr, %0\n\t"
+ "and %2, %1, %2\n\t"
+ SMP_NOP2 /* Sun4m + Cypress + SMP bug */
+ "andn %0, %1, %0\n\t"
+ "wr %0, %2, %%psr\n\t"
+ "nop; nop; nop\n"
+ : "=&r" (tmp)
+ : "i" (PSR_PIL), "r" (old_psr)
+ : "memory");
+}
+
+EXPORT_SYMBOL(__raw_local_irq_save);
+EXPORT_SYMBOL(raw_local_irq_enable);
+EXPORT_SYMBOL(raw_local_irq_restore);
+
+/*
+ * Dave Redman (djhr@tadpole.co.uk)
+ *
+ * IRQ numbers.. These are no longer restricted to 15..
+ *
+ * this is done to enable SBUS cards and onboard IO to be masked
+ * correctly. using the interrupt level isn't good enough.
+ *
+ * For example:
+ * A device interrupting at sbus level6 and the Floppy both come in
+ * at IRQ11, but enabling and disabling them requires writing to
+ * different bits in the SLAVIO/SEC.
+ *
+ * As a result of these changes sun4m machines could now support
+ * directed CPU interrupts using the existing enable/disable irq code
+ * with tweaks.
+ *
+ */
+
+static void irq_panic(void)
+{
+ extern char *cputypval;
+ prom_printf("machine: %s doesn't have irq handlers defined!\n",cputypval);
+ prom_halt();
+}
+
+void (*sparc_init_timers)(irq_handler_t ) =
+ (void (*)(irq_handler_t )) irq_panic;
+
+/*
+ * Dave Redman (djhr@tadpole.co.uk)
+ *
+ * There used to be extern calls and hard coded values here.. very sucky!
+ * instead, because some of the devices attach very early, I do something
+ * equally sucky but at least we'll never try to free statically allocated
+ * space or call kmalloc before kmalloc_init :(.
+ *
+ * In fact it's the timer10 that attaches first.. then timer14
+ * then kmalloc_init is called.. then the tty interrupts attach.
+ * hmmm....
+ *
+ */
+#define MAX_STATIC_ALLOC 4
+struct irqaction static_irqaction[MAX_STATIC_ALLOC];
+int static_irq_count;
+
+static struct {
+ struct irqaction *action;
+ int flags;
+} sparc_irq[NR_IRQS];
+#define SPARC_IRQ_INPROGRESS 1
+
+/* Used to protect the IRQ action lists */
+DEFINE_SPINLOCK(irq_action_lock);
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v;
+ struct irqaction * action;
+ unsigned long flags;
+#ifdef CONFIG_SMP
+ int j;
+#endif
+
+ if (sparc_cpu_model == sun4d) {
+ extern int show_sun4d_interrupts(struct seq_file *, void *);
+
+ return show_sun4d_interrupts(p, v);
+ }
+ spin_lock_irqsave(&irq_action_lock, flags);
+ if (i < NR_IRQS) {
+ action = sparc_irq[i].action;
+ if (!action)
+ goto out_unlock;
+ seq_printf(p, "%3d: ", i);
+#ifndef CONFIG_SMP
+ seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+ for_each_online_cpu(j) {
+ seq_printf(p, "%10u ",
+ kstat_cpu(j).irqs[i]);
+ }
+#endif
+ seq_printf(p, " %c %s",
+ (action->flags & IRQF_DISABLED) ? '+' : ' ',
+ action->name);
+ for (action=action->next; action; action = action->next) {
+ seq_printf(p, ",%s %s",
+ (action->flags & IRQF_DISABLED) ? " +" : "",
+ action->name);
+ }
+ seq_putc(p, '\n');
+ }
+out_unlock:
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+ return 0;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction * action;
+ struct irqaction **actionp;
+ unsigned long flags;
+ unsigned int cpu_irq;
+
+ if (sparc_cpu_model == sun4d) {
+ extern void sun4d_free_irq(unsigned int, void *);
+
+ sun4d_free_irq(irq, dev_id);
+ return;
+ }
+ cpu_irq = irq & (NR_IRQS - 1);
+ if (cpu_irq > 14) { /* 14 irq levels on the sparc */
+ printk("Trying to free bogus IRQ %d\n", irq);
+ return;
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ actionp = &sparc_irq[cpu_irq].action;
+ action = *actionp;
+
+ if (!action->handler) {
+ printk("Trying to free free IRQ%d\n",irq);
+ goto out_unlock;
+ }
+ if (dev_id) {
+ for (; action; action = action->next) {
+ if (action->dev_id == dev_id)
+ break;
+ actionp = &action->next;
+ }
+ if (!action) {
+ printk("Trying to free free shared IRQ%d\n",irq);
+ goto out_unlock;
+ }
+ } else if (action->flags & IRQF_SHARED) {
+ printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+ goto out_unlock;
+ }
+ if (action->flags & SA_STATIC_ALLOC)
+ {
+ /* This interrupt is marked as specially allocated
+ * so it is a bad idea to free it.
+ */
+ printk("Attempt to free statically allocated IRQ%d (%s)\n",
+ irq, action->name);
+ goto out_unlock;
+ }
+
+ *actionp = action->next;
+
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+
+ synchronize_irq(irq);
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ kfree(action);
+
+ if (!sparc_irq[cpu_irq].action)
+ __disable_irq(irq);
+
+out_unlock:
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+}
+
+EXPORT_SYMBOL(free_irq);
+
+/*
+ * This is called when we want to synchronize with
+ * interrupts. We may for example tell a device to
+ * stop sending interrupts: but to make sure there
+ * are no interrupts that are executing on another
+ * CPU we need to call this function.
+ */
+#ifdef CONFIG_SMP
+void synchronize_irq(unsigned int irq)
+{
+ unsigned int cpu_irq;
+
+ cpu_irq = irq & (NR_IRQS - 1);
+ while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
+ cpu_relax();
+}
+#endif /* SMP */
+
+void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+ int i;
+ struct irqaction * action;
+ unsigned int cpu_irq;
+
+ cpu_irq = irq & (NR_IRQS - 1);
+ action = sparc_irq[cpu_irq].action;
+
+ printk("IO device interrupt, irq = %d\n", irq);
+ printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
+ regs->npc, regs->u_regs[14]);
+ if (action) {
+ printk("Expecting: ");
+ for (i = 0; i < 16; i++)
+ if (action->handler)
+ printk("[%s:%d:0x%x] ", action->name,
+ (int) i, (unsigned int) action->handler);
+ }
+ printk("AIEEE\n");
+ panic("bogus interrupt received");
+}
+
+void handler_irq(int irq, struct pt_regs * regs)
+{
+ struct pt_regs *old_regs;
+ struct irqaction * action;
+ int cpu = smp_processor_id();
+#ifdef CONFIG_SMP
+ extern void smp4m_irq_rotate(int cpu);
+#endif
+
+ old_regs = set_irq_regs(regs);
+ irq_enter();
+ disable_pil_irq(irq);
+#ifdef CONFIG_SMP
+ /* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */
+ if((sparc_cpu_model==sun4m) && (irq < 10))
+ smp4m_irq_rotate(cpu);
+#endif
+ action = sparc_irq[irq].action;
+ sparc_irq[irq].flags |= SPARC_IRQ_INPROGRESS;
+ kstat_cpu(cpu).irqs[irq]++;
+ do {
+ if (!action || !action->handler)
+ unexpected_irq(irq, NULL, regs);
+ action->handler(irq, action->dev_id);
+ action = action->next;
+ } while (action);
+ sparc_irq[irq].flags &= ~SPARC_IRQ_INPROGRESS;
+ enable_pil_irq(irq);
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+#if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
+
+/* Fast IRQs on the Sparc can only have one routine attached to them,
+ * thus no sharing possible.
+ */
+static int request_fast_irq(unsigned int irq,
+ void (*handler)(void),
+ unsigned long irqflags, const char *devname)
+{
+ struct irqaction *action;
+ unsigned long flags;
+ unsigned int cpu_irq;
+ int ret;
+#ifdef CONFIG_SMP
+ struct tt_entry *trap_table;
+ extern struct tt_entry trapbase_cpu1, trapbase_cpu2, trapbase_cpu3;
+#endif
+
+ cpu_irq = irq & (NR_IRQS - 1);
+ if(cpu_irq > 14) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if(!handler) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ action = sparc_irq[cpu_irq].action;
+ if(action) {
+ if(action->flags & IRQF_SHARED)
+ panic("Trying to register fast irq when already shared.\n");
+ if(irqflags & IRQF_SHARED)
+ panic("Trying to register fast irq as shared.\n");
+
+ /* Anyway, someone already owns it so cannot be made fast. */
+ printk("request_fast_irq: Trying to register yet already owned.\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* If this is flagged as statically allocated then we use our
+ * private struct which is never freed.
+ */
+ if (irqflags & SA_STATIC_ALLOC) {
+ if (static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
+ irq, devname);
+ }
+
+ if (action == NULL)
+ action = kmalloc(sizeof(struct irqaction),
+ GFP_ATOMIC);
+
+ if (!action) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ /* Dork with trap table if we get this far. */
+#define INSTANTIATE(table) \
+ table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
+ table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
+ SPARC_BRANCH((unsigned long) handler, \
+ (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
+ table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
+ table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
+
+ INSTANTIATE(sparc_ttable)
+#ifdef CONFIG_SMP
+ trap_table = &trapbase_cpu1; INSTANTIATE(trap_table)
+ trap_table = &trapbase_cpu2; INSTANTIATE(trap_table)
+ trap_table = &trapbase_cpu3; INSTANTIATE(trap_table)
+#endif
+#undef INSTANTIATE
+ /*
+ * XXX Correct thing whould be to flush only I- and D-cache lines
+ * which contain the handler in question. But as of time of the
+ * writing we have no CPU-neutral interface to fine-grained flushes.
+ */
+ flush_cache_all();
+
+ action->flags = irqflags;
+ cpus_clear(action->mask);
+ action->name = devname;
+ action->dev_id = NULL;
+ action->next = NULL;
+
+ sparc_irq[cpu_irq].action = action;
+
+ __enable_irq(irq);
+
+ ret = 0;
+out_unlock:
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+out:
+ return ret;
+}
+
+/* These variables are used to access state from the assembler
+ * interrupt handler, floppy_hardint, so we cannot put these in
+ * the floppy driver image because that would not work in the
+ * modular case.
+ */
+volatile unsigned char *fdc_status;
+EXPORT_SYMBOL(fdc_status);
+
+char *pdma_vaddr;
+EXPORT_SYMBOL(pdma_vaddr);
+
+unsigned long pdma_size;
+EXPORT_SYMBOL(pdma_size);
+
+volatile int doing_pdma;
+EXPORT_SYMBOL(doing_pdma);
+
+char *pdma_base;
+EXPORT_SYMBOL(pdma_base);
+
+unsigned long pdma_areasize;
+EXPORT_SYMBOL(pdma_areasize);
+
+extern void floppy_hardint(void);
+
+static irq_handler_t floppy_irq_handler;
+
+void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs;
+ int cpu = smp_processor_id();
+
+ old_regs = set_irq_regs(regs);
+ disable_pil_irq(irq);
+ irq_enter();
+ kstat_cpu(cpu).irqs[irq]++;
+ floppy_irq_handler(irq, dev_id);
+ irq_exit();
+ enable_pil_irq(irq);
+ set_irq_regs(old_regs);
+ // XXX Eek, it's totally changed with preempt_count() and such
+ // if (softirq_pending(cpu))
+ // do_softirq();
+}
+
+int sparc_floppy_request_irq(int irq, unsigned long flags,
+ irq_handler_t irq_handler)
+{
+ floppy_irq_handler = irq_handler;
+ return request_fast_irq(irq, floppy_hardint, flags, "floppy");
+}
+EXPORT_SYMBOL(sparc_floppy_request_irq);
+
+#endif
+
+int request_irq(unsigned int irq,
+ irq_handler_t handler,
+ unsigned long irqflags, const char * devname, void *dev_id)
+{
+ struct irqaction * action, **actionp;
+ unsigned long flags;
+ unsigned int cpu_irq;
+ int ret;
+
+ if (sparc_cpu_model == sun4d) {
+ extern int sun4d_request_irq(unsigned int,
+ irq_handler_t ,
+ unsigned long, const char *, void *);
+ return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
+ }
+ cpu_irq = irq & (NR_IRQS - 1);
+ if(cpu_irq > 14) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!handler) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_lock_irqsave(&irq_action_lock, flags);
+
+ actionp = &sparc_irq[cpu_irq].action;
+ action = *actionp;
+ if (action) {
+ if (!(action->flags & IRQF_SHARED) || !(irqflags & IRQF_SHARED)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if ((action->flags & IRQF_DISABLED) != (irqflags & IRQF_DISABLED)) {
+ printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ for ( ; action; action = *actionp)
+ actionp = &action->next;
+ }
+
+ /* If this is flagged as statically allocated then we use our
+ * private struct which is never freed.
+ */
+ if (irqflags & SA_STATIC_ALLOC) {
+ if (static_irq_count < MAX_STATIC_ALLOC)
+ action = &static_irqaction[static_irq_count++];
+ else
+ printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", irq, devname);
+ }
+
+ if (action == NULL)
+ action = kmalloc(sizeof(struct irqaction),
+ GFP_ATOMIC);
+
+ if (!action) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ action->handler = handler;
+ action->flags = irqflags;
+ cpus_clear(action->mask);
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ *actionp = action;
+
+ __enable_irq(irq);
+
+ ret = 0;
+out_unlock:
+ spin_unlock_irqrestore(&irq_action_lock, flags);
+out:
+ return ret;
+}
+
+EXPORT_SYMBOL(request_irq);
+
+void disable_irq_nosync(unsigned int irq)
+{
+ __disable_irq(irq);
+}
+EXPORT_SYMBOL(disable_irq_nosync);
+
+void disable_irq(unsigned int irq)
+{
+ __disable_irq(irq);
+}
+EXPORT_SYMBOL(disable_irq);
+
+void enable_irq(unsigned int irq)
+{
+ __enable_irq(irq);
+}
+
+EXPORT_SYMBOL(enable_irq);
+
+/* We really don't need these at all on the Sparc. We only have
+ * stubs here because they are exported to modules.
+ */
+unsigned long probe_irq_on(void)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_on);
+
+int probe_irq_off(unsigned long mask)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(probe_irq_off);
+
+/* djhr
+ * This could probably be made indirect too and assigned in the CPU
+ * bits of the code. That would be much nicer I think and would also
+ * fit in with the idea of being able to tune your kernel for your machine
+ * by removing unrequired machine and device support.
+ *
+ */
+
+void __init init_IRQ(void)
+{
+ extern void sun4c_init_IRQ( void );
+ extern void sun4m_init_IRQ( void );
+ extern void sun4d_init_IRQ( void );
+
+ switch(sparc_cpu_model) {
+ case sun4c:
+ case sun4:
+ sun4c_init_IRQ();
+ break;
+
+ case sun4m:
+#ifdef CONFIG_PCI
+ pcic_probe();
+ if (pcic_present()) {
+ sun4m_pci_init_IRQ();
+ break;
+ }
+#endif
+ sun4m_init_IRQ();
+ break;
+
+ case sun4d:
+ sun4d_init_IRQ();
+ break;
+
+ default:
+ prom_printf("Cannot initialize IRQs on this Sun machine...");
+ break;
+ }
+ btfixup();
+}
+
+void init_irq_proc(void)
+{
+ /* For now, nothing... */
+}
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
new file mode 100644
index 000000000000..cab8e0286871
--- /dev/null
+++ b/arch/sparc/kernel/irq_64.c
@@ -0,0 +1,1104 @@
+/* irq.c: UltraSparc IRQ handling/init/registry.
+ *
+ * Copyright (C) 1997, 2007, 2008 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/iommu.h>
+#include <asm/upa.h>
+#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/timer.h>
+#include <asm/smp.h>
+#include <asm/starfire.h>
+#include <asm/uaccess.h>
+#include <asm/cache.h>
+#include <asm/cpudata.h>
+#include <asm/auxio.h>
+#include <asm/head.h>
+#include <asm/hypervisor.h>
+#include <asm/cacheflush.h>
+
+#include "entry.h"
+
+#define NUM_IVECS (IMAP_INR + 1)
+
+struct ino_bucket *ivector_table;
+unsigned long ivector_table_pa;
+
+/* On several sun4u processors, it is illegal to mix bypass and
+ * non-bypass accesses. Therefore we access all INO buckets
+ * using bypass accesses only.
+ */
+static unsigned long bucket_get_chain_pa(unsigned long bucket_pa)
+{
+ unsigned long ret;
+
+ __asm__ __volatile__("ldxa [%1] %2, %0"
+ : "=&r" (ret)
+ : "r" (bucket_pa +
+ offsetof(struct ino_bucket,
+ __irq_chain_pa)),
+ "i" (ASI_PHYS_USE_EC));
+
+ return ret;
+}
+
+static void bucket_clear_chain_pa(unsigned long bucket_pa)
+{
+ __asm__ __volatile__("stxa %%g0, [%0] %1"
+ : /* no outputs */
+ : "r" (bucket_pa +
+ offsetof(struct ino_bucket,
+ __irq_chain_pa)),
+ "i" (ASI_PHYS_USE_EC));
+}
+
+static unsigned int bucket_get_virt_irq(unsigned long bucket_pa)
+{
+ unsigned int ret;
+
+ __asm__ __volatile__("lduwa [%1] %2, %0"
+ : "=&r" (ret)
+ : "r" (bucket_pa +
+ offsetof(struct ino_bucket,
+ __virt_irq)),
+ "i" (ASI_PHYS_USE_EC));
+
+ return ret;
+}
+
+static void bucket_set_virt_irq(unsigned long bucket_pa,
+ unsigned int virt_irq)
+{
+ __asm__ __volatile__("stwa %0, [%1] %2"
+ : /* no outputs */
+ : "r" (virt_irq),
+ "r" (bucket_pa +
+ offsetof(struct ino_bucket,
+ __virt_irq)),
+ "i" (ASI_PHYS_USE_EC));
+}
+
+#define irq_work_pa(__cpu) &(trap_block[(__cpu)].irq_worklist_pa)
+
+static struct {
+ unsigned int dev_handle;
+ unsigned int dev_ino;
+ unsigned int in_use;
+} virt_irq_table[NR_IRQS];
+static DEFINE_SPINLOCK(virt_irq_alloc_lock);
+
+unsigned char virt_irq_alloc(unsigned int dev_handle,
+ unsigned int dev_ino)
+{
+ unsigned long flags;
+ unsigned char ent;
+
+ BUILD_BUG_ON(NR_IRQS >= 256);
+
+ spin_lock_irqsave(&virt_irq_alloc_lock, flags);
+
+ for (ent = 1; ent < NR_IRQS; ent++) {
+ if (!virt_irq_table[ent].in_use)
+ break;
+ }
+ if (ent >= NR_IRQS) {
+ printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
+ ent = 0;
+ } else {
+ virt_irq_table[ent].dev_handle = dev_handle;
+ virt_irq_table[ent].dev_ino = dev_ino;
+ virt_irq_table[ent].in_use = 1;
+ }
+
+ spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);
+
+ return ent;
+}
+
+#ifdef CONFIG_PCI_MSI
+void virt_irq_free(unsigned int virt_irq)
+{
+ unsigned long flags;
+
+ if (virt_irq >= NR_IRQS)
+ return;
+
+ spin_lock_irqsave(&virt_irq_alloc_lock, flags);
+
+ virt_irq_table[virt_irq].in_use = 0;
+
+ spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);
+}
+#endif
+
+/*
+ * /proc/interrupts printing:
+ */
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, j;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_printf(p, " ");
+ for_each_online_cpu(j)
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto skip;
+ seq_printf(p, "%3d: ",i);
+#ifndef CONFIG_SMP
+ seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
+#endif
+ seq_printf(p, " %9s", irq_desc[i].chip->typename);
+ seq_printf(p, " %s", action->name);
+
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
+ seq_putc(p, '\n');
+skip:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ }
+ return 0;
+}
+
+static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
+{
+ unsigned int tid;
+
+ if (this_is_starfire) {
+ tid = starfire_translate(imap, cpuid);
+ tid <<= IMAP_TID_SHIFT;
+ tid &= IMAP_TID_UPA;
+ } else {
+ if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ unsigned long ver;
+
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID) {
+ tid = cpuid << IMAP_TID_SHIFT;
+ tid &= IMAP_TID_JBUS;
+ } else {
+ unsigned int a = cpuid & 0x1f;
+ unsigned int n = (cpuid >> 5) & 0x1f;
+
+ tid = ((a << IMAP_AID_SHIFT) |
+ (n << IMAP_NID_SHIFT));
+ tid &= (IMAP_AID_SAFARI |
+ IMAP_NID_SAFARI);;
+ }
+ } else {
+ tid = cpuid << IMAP_TID_SHIFT;
+ tid &= IMAP_TID_UPA;
+ }
+ }
+
+ return tid;
+}
+
+struct irq_handler_data {
+ unsigned long iclr;
+ unsigned long imap;
+
+ void (*pre_handler)(unsigned int, void *, void *);
+ void *arg1;
+ void *arg2;
+};
+
+#ifdef CONFIG_SMP
+static int irq_choose_cpu(unsigned int virt_irq)
+{
+ cpumask_t mask = irq_desc[virt_irq].affinity;
+ int cpuid;
+
+ if (cpus_equal(mask, CPU_MASK_ALL)) {
+ static int irq_rover;
+ static DEFINE_SPINLOCK(irq_rover_lock);
+ unsigned long flags;
+
+ /* Round-robin distribution... */
+ do_round_robin:
+ spin_lock_irqsave(&irq_rover_lock, flags);
+
+ while (!cpu_online(irq_rover)) {
+ if (++irq_rover >= NR_CPUS)
+ irq_rover = 0;
+ }
+ cpuid = irq_rover;
+ do {
+ if (++irq_rover >= NR_CPUS)
+ irq_rover = 0;
+ } while (!cpu_online(irq_rover));
+
+ spin_unlock_irqrestore(&irq_rover_lock, flags);
+ } else {
+ cpumask_t tmp;
+
+ cpus_and(tmp, cpu_online_map, mask);
+
+ if (cpus_empty(tmp))
+ goto do_round_robin;
+
+ cpuid = first_cpu(tmp);
+ }
+
+ return cpuid;
+}
+#else
+static int irq_choose_cpu(unsigned int virt_irq)
+{
+ return real_hard_smp_processor_id();
+}
+#endif
+
+static void sun4u_irq_enable(unsigned int virt_irq)
+{
+ struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+
+ if (likely(data)) {
+ unsigned long cpuid, imap, val;
+ unsigned int tid;
+
+ cpuid = irq_choose_cpu(virt_irq);
+ imap = data->imap;
+
+ tid = sun4u_compute_tid(imap, cpuid);
+
+ val = upa_readq(imap);
+ val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
+ IMAP_AID_SAFARI | IMAP_NID_SAFARI);
+ val |= tid | IMAP_VALID;
+ upa_writeq(val, imap);
+ upa_writeq(ICLR_IDLE, data->iclr);
+ }
+}
+
+static void sun4u_set_affinity(unsigned int virt_irq,
+ const struct cpumask *mask)
+{
+ sun4u_irq_enable(virt_irq);
+}
+
+static void sun4u_irq_disable(unsigned int virt_irq)
+{
+ struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+
+ if (likely(data)) {
+ unsigned long imap = data->imap;
+ unsigned long tmp = upa_readq(imap);
+
+ tmp &= ~IMAP_VALID;
+ upa_writeq(tmp, imap);
+ }
+}
+
+static void sun4u_irq_eoi(unsigned int virt_irq)
+{
+ struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+ struct irq_desc *desc = irq_desc + virt_irq;
+
+ if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ return;
+
+ if (likely(data))
+ upa_writeq(ICLR_IDLE, data->iclr);
+}
+
+static void sun4v_irq_enable(unsigned int virt_irq)
+{
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+ unsigned long cpuid = irq_choose_cpu(virt_irq);
+ int err;
+
+ err = sun4v_intr_settarget(ino, cpuid);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_settarget(%x,%lu): "
+ "err(%d)\n", ino, cpuid, err);
+ err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_setstate(%x): "
+ "err(%d)\n", ino, err);
+ err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_setenabled(%x): err(%d)\n",
+ ino, err);
+}
+
+static void sun4v_set_affinity(unsigned int virt_irq,
+ const struct cpumask *mask)
+{
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+ unsigned long cpuid = irq_choose_cpu(virt_irq);
+ int err;
+
+ err = sun4v_intr_settarget(ino, cpuid);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_settarget(%x,%lu): "
+ "err(%d)\n", ino, cpuid, err);
+}
+
+static void sun4v_irq_disable(unsigned int virt_irq)
+{
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+ int err;
+
+ err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_setenabled(%x): "
+ "err(%d)\n", ino, err);
+}
+
+static void sun4v_irq_eoi(unsigned int virt_irq)
+{
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+ struct irq_desc *desc = irq_desc + virt_irq;
+ int err;
+
+ if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ return;
+
+ err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_intr_setstate(%x): "
+ "err(%d)\n", ino, err);
+}
+
+static void sun4v_virq_enable(unsigned int virt_irq)
+{
+ unsigned long cpuid, dev_handle, dev_ino;
+ int err;
+
+ cpuid = irq_choose_cpu(virt_irq);
+
+ dev_handle = virt_irq_table[virt_irq].dev_handle;
+ dev_ino = virt_irq_table[virt_irq].dev_ino;
+
+ err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
+ "err(%d)\n",
+ dev_handle, dev_ino, cpuid, err);
+ err = sun4v_vintr_set_state(dev_handle, dev_ino,
+ HV_INTR_STATE_IDLE);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
+ "HV_INTR_STATE_IDLE): err(%d)\n",
+ dev_handle, dev_ino, err);
+ err = sun4v_vintr_set_valid(dev_handle, dev_ino,
+ HV_INTR_ENABLED);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
+ "HV_INTR_ENABLED): err(%d)\n",
+ dev_handle, dev_ino, err);
+}
+
+static void sun4v_virt_set_affinity(unsigned int virt_irq,
+ const struct cpumask *mask)
+{
+ unsigned long cpuid, dev_handle, dev_ino;
+ int err;
+
+ cpuid = irq_choose_cpu(virt_irq);
+
+ dev_handle = virt_irq_table[virt_irq].dev_handle;
+ dev_ino = virt_irq_table[virt_irq].dev_ino;
+
+ err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
+ "err(%d)\n",
+ dev_handle, dev_ino, cpuid, err);
+}
+
+static void sun4v_virq_disable(unsigned int virt_irq)
+{
+ unsigned long dev_handle, dev_ino;
+ int err;
+
+ dev_handle = virt_irq_table[virt_irq].dev_handle;
+ dev_ino = virt_irq_table[virt_irq].dev_ino;
+
+ err = sun4v_vintr_set_valid(dev_handle, dev_ino,
+ HV_INTR_DISABLED);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
+ "HV_INTR_DISABLED): err(%d)\n",
+ dev_handle, dev_ino, err);
+}
+
+static void sun4v_virq_eoi(unsigned int virt_irq)
+{
+ struct irq_desc *desc = irq_desc + virt_irq;
+ unsigned long dev_handle, dev_ino;
+ int err;
+
+ if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ return;
+
+ dev_handle = virt_irq_table[virt_irq].dev_handle;
+ dev_ino = virt_irq_table[virt_irq].dev_ino;
+
+ err = sun4v_vintr_set_state(dev_handle, dev_ino,
+ HV_INTR_STATE_IDLE);
+ if (err != HV_EOK)
+ printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
+ "HV_INTR_STATE_IDLE): err(%d)\n",
+ dev_handle, dev_ino, err);
+}
+
+static struct irq_chip sun4u_irq = {
+ .typename = "sun4u",
+ .enable = sun4u_irq_enable,
+ .disable = sun4u_irq_disable,
+ .eoi = sun4u_irq_eoi,
+ .set_affinity = sun4u_set_affinity,
+};
+
+static struct irq_chip sun4v_irq = {
+ .typename = "sun4v",
+ .enable = sun4v_irq_enable,
+ .disable = sun4v_irq_disable,
+ .eoi = sun4v_irq_eoi,
+ .set_affinity = sun4v_set_affinity,
+};
+
+static struct irq_chip sun4v_virq = {
+ .typename = "vsun4v",
+ .enable = sun4v_virq_enable,
+ .disable = sun4v_virq_disable,
+ .eoi = sun4v_virq_eoi,
+ .set_affinity = sun4v_virt_set_affinity,
+};
+
+static void pre_flow_handler(unsigned int virt_irq,
+ struct irq_desc *desc)
+{
+ struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+
+ data->pre_handler(ino, data->arg1, data->arg2);
+
+ handle_fasteoi_irq(virt_irq, desc);
+}
+
+void irq_install_pre_handler(int virt_irq,
+ void (*func)(unsigned int, void *, void *),
+ void *arg1, void *arg2)
+{
+ struct irq_handler_data *data = get_irq_chip_data(virt_irq);
+ struct irq_desc *desc = irq_desc + virt_irq;
+
+ data->pre_handler = func;
+ data->arg1 = arg1;
+ data->arg2 = arg2;
+
+ desc->handle_irq = pre_flow_handler;
+}
+
+unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
+{
+ struct ino_bucket *bucket;
+ struct irq_handler_data *data;
+ unsigned int virt_irq;
+ int ino;
+
+ BUG_ON(tlb_type == hypervisor);
+
+ ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
+ bucket = &ivector_table[ino];
+ virt_irq = bucket_get_virt_irq(__pa(bucket));
+ if (!virt_irq) {
+ virt_irq = virt_irq_alloc(0, ino);
+ bucket_set_virt_irq(__pa(bucket), virt_irq);
+ set_irq_chip_and_handler_name(virt_irq,
+ &sun4u_irq,
+ handle_fasteoi_irq,
+ "IVEC");
+ }
+
+ data = get_irq_chip_data(virt_irq);
+ if (unlikely(data))
+ goto out;
+
+ data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
+ if (unlikely(!data)) {
+ prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
+ prom_halt();
+ }
+ set_irq_chip_data(virt_irq, data);
+
+ data->imap = imap;
+ data->iclr = iclr;
+
+out:
+ return virt_irq;
+}
+
+static unsigned int sun4v_build_common(unsigned long sysino,
+ struct irq_chip *chip)
+{
+ struct ino_bucket *bucket;
+ struct irq_handler_data *data;
+ unsigned int virt_irq;
+
+ BUG_ON(tlb_type != hypervisor);
+
+ bucket = &ivector_table[sysino];
+ virt_irq = bucket_get_virt_irq(__pa(bucket));
+ if (!virt_irq) {
+ virt_irq = virt_irq_alloc(0, sysino);
+ bucket_set_virt_irq(__pa(bucket), virt_irq);
+ set_irq_chip_and_handler_name(virt_irq, chip,
+ handle_fasteoi_irq,
+ "IVEC");
+ }
+
+ data = get_irq_chip_data(virt_irq);
+ if (unlikely(data))
+ goto out;
+
+ data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
+ if (unlikely(!data)) {
+ prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
+ prom_halt();
+ }
+ set_irq_chip_data(virt_irq, data);
+
+ /* Catch accidental accesses to these things. IMAP/ICLR handling
+ * is done by hypervisor calls on sun4v platforms, not by direct
+ * register accesses.
+ */
+ data->imap = ~0UL;
+ data->iclr = ~0UL;
+
+out:
+ return virt_irq;
+}
+
+unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
+{
+ unsigned long sysino = sun4v_devino_to_sysino(devhandle, devino);
+
+ return sun4v_build_common(sysino, &sun4v_irq);
+}
+
+unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
+{
+ struct irq_handler_data *data;
+ unsigned long hv_err, cookie;
+ struct ino_bucket *bucket;
+ struct irq_desc *desc;
+ unsigned int virt_irq;
+
+ bucket = kzalloc(sizeof(struct ino_bucket), GFP_ATOMIC);
+ if (unlikely(!bucket))
+ return 0;
+ __flush_dcache_range((unsigned long) bucket,
+ ((unsigned long) bucket +
+ sizeof(struct ino_bucket)));
+
+ virt_irq = virt_irq_alloc(devhandle, devino);
+ bucket_set_virt_irq(__pa(bucket), virt_irq);
+
+ set_irq_chip_and_handler_name(virt_irq, &sun4v_virq,
+ handle_fasteoi_irq,
+ "IVEC");
+
+ data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
+ if (unlikely(!data))
+ return 0;
+
+ /* In order to make the LDC channel startup sequence easier,
+ * especially wrt. locking, we do not let request_irq() enable
+ * the interrupt.
+ */
+ desc = irq_desc + virt_irq;
+ desc->status |= IRQ_NOAUTOEN;
+
+ set_irq_chip_data(virt_irq, data);
+
+ /* Catch accidental accesses to these things. IMAP/ICLR handling
+ * is done by hypervisor calls on sun4v platforms, not by direct
+ * register accesses.
+ */
+ data->imap = ~0UL;
+ data->iclr = ~0UL;
+
+ cookie = ~__pa(bucket);
+ hv_err = sun4v_vintr_set_cookie(devhandle, devino, cookie);
+ if (hv_err) {
+ prom_printf("IRQ: Fatal, cannot set cookie for [%x:%x] "
+ "err=%lu\n", devhandle, devino, hv_err);
+ prom_halt();
+ }
+
+ return virt_irq;
+}
+
+void ack_bad_irq(unsigned int virt_irq)
+{
+ unsigned int ino = virt_irq_table[virt_irq].dev_ino;
+
+ if (!ino)
+ ino = 0xdeadbeef;
+
+ printk(KERN_CRIT "Unexpected IRQ from ino[%x] virt_irq[%u]\n",
+ ino, virt_irq);
+}
+
+void *hardirq_stack[NR_CPUS];
+void *softirq_stack[NR_CPUS];
+
+static __attribute__((always_inline)) void *set_hardirq_stack(void)
+{
+ void *orig_sp, *sp = hardirq_stack[smp_processor_id()];
+
+ __asm__ __volatile__("mov %%sp, %0" : "=r" (orig_sp));
+ if (orig_sp < sp ||
+ orig_sp > (sp + THREAD_SIZE)) {
+ sp += THREAD_SIZE - 192 - STACK_BIAS;
+ __asm__ __volatile__("mov %0, %%sp" : : "r" (sp));
+ }
+
+ return orig_sp;
+}
+static __attribute__((always_inline)) void restore_hardirq_stack(void *orig_sp)
+{
+ __asm__ __volatile__("mov %0, %%sp" : : "r" (orig_sp));
+}
+
+void handler_irq(int irq, struct pt_regs *regs)
+{
+ unsigned long pstate, bucket_pa;
+ struct pt_regs *old_regs;
+ void *orig_sp;
+
+ clear_softint(1 << irq);
+
+ old_regs = set_irq_regs(regs);
+ irq_enter();
+
+ /* Grab an atomic snapshot of the pending IVECs. */
+ __asm__ __volatile__("rdpr %%pstate, %0\n\t"
+ "wrpr %0, %3, %%pstate\n\t"
+ "ldx [%2], %1\n\t"
+ "stx %%g0, [%2]\n\t"
+ "wrpr %0, 0x0, %%pstate\n\t"
+ : "=&r" (pstate), "=&r" (bucket_pa)
+ : "r" (irq_work_pa(smp_processor_id())),
+ "i" (PSTATE_IE)
+ : "memory");
+
+ orig_sp = set_hardirq_stack();
+
+ while (bucket_pa) {
+ struct irq_desc *desc;
+ unsigned long next_pa;
+ unsigned int virt_irq;
+
+ next_pa = bucket_get_chain_pa(bucket_pa);
+ virt_irq = bucket_get_virt_irq(bucket_pa);
+ bucket_clear_chain_pa(bucket_pa);
+
+ desc = irq_desc + virt_irq;
+
+ desc->handle_irq(virt_irq, desc);
+
+ bucket_pa = next_pa;
+ }
+
+ restore_hardirq_stack(orig_sp);
+
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+void do_softirq(void)
+{
+ unsigned long flags;
+
+ if (in_interrupt())
+ return;
+
+ local_irq_save(flags);
+
+ if (local_softirq_pending()) {
+ void *orig_sp, *sp = softirq_stack[smp_processor_id()];
+
+ sp += THREAD_SIZE - 192 - STACK_BIAS;
+
+ __asm__ __volatile__("mov %%sp, %0\n\t"
+ "mov %1, %%sp"
+ : "=&r" (orig_sp)
+ : "r" (sp));
+ __do_softirq();
+ __asm__ __volatile__("mov %0, %%sp"
+ : : "r" (orig_sp));
+ }
+
+ local_irq_restore(flags);
+}
+
+static void unhandled_perf_irq(struct pt_regs *regs)
+{
+ unsigned long pcr, pic;
+
+ read_pcr(pcr);
+ read_pic(pic);
+
+ write_pcr(0);
+
+ printk(KERN_EMERG "CPU %d: Got unexpected perf counter IRQ.\n",
+ smp_processor_id());
+ printk(KERN_EMERG "CPU %d: PCR[%016lx] PIC[%016lx]\n",
+ smp_processor_id(), pcr, pic);
+}
+
+/* Almost a direct copy of the powerpc PMC code. */
+static DEFINE_SPINLOCK(perf_irq_lock);
+static void *perf_irq_owner_caller; /* mostly for debugging */
+static void (*perf_irq)(struct pt_regs *regs) = unhandled_perf_irq;
+
+/* Invoked from level 15 PIL handler in trap table. */
+void perfctr_irq(int irq, struct pt_regs *regs)
+{
+ clear_softint(1 << irq);
+ perf_irq(regs);
+}
+
+int register_perfctr_intr(void (*handler)(struct pt_regs *))
+{
+ int ret;
+
+ if (!handler)
+ return -EINVAL;
+
+ spin_lock(&perf_irq_lock);
+ if (perf_irq != unhandled_perf_irq) {
+ printk(KERN_WARNING "register_perfctr_intr: "
+ "perf IRQ busy (reserved by caller %p)\n",
+ perf_irq_owner_caller);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ perf_irq_owner_caller = __builtin_return_address(0);
+ perf_irq = handler;
+
+ ret = 0;
+out:
+ spin_unlock(&perf_irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_perfctr_intr);
+
+void release_perfctr_intr(void (*handler)(struct pt_regs *))
+{
+ spin_lock(&perf_irq_lock);
+ perf_irq_owner_caller = NULL;
+ perf_irq = unhandled_perf_irq;
+ spin_unlock(&perf_irq_lock);
+}
+EXPORT_SYMBOL_GPL(release_perfctr_intr);
+
+#ifdef CONFIG_HOTPLUG_CPU
+void fixup_irqs(void)
+{
+ unsigned int irq;
+
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_desc[irq].lock, flags);
+ if (irq_desc[irq].action &&
+ !(irq_desc[irq].status & IRQ_PER_CPU)) {
+ if (irq_desc[irq].chip->set_affinity)
+ irq_desc[irq].chip->set_affinity(irq,
+ &irq_desc[irq].affinity);
+ }
+ spin_unlock_irqrestore(&irq_desc[irq].lock, flags);
+ }
+
+ tick_ops->disable_irq();
+}
+#endif
+
+struct sun5_timer {
+ u64 count0;
+ u64 limit0;
+ u64 count1;
+ u64 limit1;
+};
+
+static struct sun5_timer *prom_timers;
+static u64 prom_limit0, prom_limit1;
+
+static void map_prom_timers(void)
+{
+ struct device_node *dp;
+ const unsigned int *addr;
+
+ /* PROM timer node hangs out in the top level of device siblings... */
+ dp = of_find_node_by_path("/");
+ dp = dp->child;
+ while (dp) {
+ if (!strcmp(dp->name, "counter-timer"))
+ break;
+ dp = dp->sibling;
+ }
+
+ /* Assume if node is not present, PROM uses different tick mechanism
+ * which we should not care about.
+ */
+ if (!dp) {
+ prom_timers = (struct sun5_timer *) 0;
+ return;
+ }
+
+ /* If PROM is really using this, it must be mapped by him. */
+ addr = of_get_property(dp, "address", NULL);
+ if (!addr) {
+ prom_printf("PROM does not have timer mapped, trying to continue.\n");
+ prom_timers = (struct sun5_timer *) 0;
+ return;
+ }
+ prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]);
+}
+
+static void kill_prom_timer(void)
+{
+ if (!prom_timers)
+ return;
+
+ /* Save them away for later. */
+ prom_limit0 = prom_timers->limit0;
+ prom_limit1 = prom_timers->limit1;
+
+ /* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14.
+ * We turn both off here just to be paranoid.
+ */
+ prom_timers->limit0 = 0;
+ prom_timers->limit1 = 0;
+
+ /* Wheee, eat the interrupt packet too... */
+ __asm__ __volatile__(
+" mov 0x40, %%g2\n"
+" ldxa [%%g0] %0, %%g1\n"
+" ldxa [%%g2] %1, %%g1\n"
+" stxa %%g0, [%%g0] %0\n"
+" membar #Sync\n"
+ : /* no outputs */
+ : "i" (ASI_INTR_RECEIVE), "i" (ASI_INTR_R)
+ : "g1", "g2");
+}
+
+void notrace init_irqwork_curcpu(void)
+{
+ int cpu = hard_smp_processor_id();
+
+ trap_block[cpu].irq_worklist_pa = 0UL;
+}
+
+/* Please be very careful with register_one_mondo() and
+ * sun4v_register_mondo_queues().
+ *
+ * On SMP this gets invoked from the CPU trampoline before
+ * the cpu has fully taken over the trap table from OBP,
+ * and it's kernel stack + %g6 thread register state is
+ * not fully cooked yet.
+ *
+ * Therefore you cannot make any OBP calls, not even prom_printf,
+ * from these two routines.
+ */
+static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
+{
+ unsigned long num_entries = (qmask + 1) / 64;
+ unsigned long status;
+
+ status = sun4v_cpu_qconf(type, paddr, num_entries);
+ if (status != HV_EOK) {
+ prom_printf("SUN4V: sun4v_cpu_qconf(%lu:%lx:%lu) failed, "
+ "err %lu\n", type, paddr, num_entries, status);
+ prom_halt();
+ }
+}
+
+void __cpuinit notrace sun4v_register_mondo_queues(int this_cpu)
+{
+ struct trap_per_cpu *tb = &trap_block[this_cpu];
+
+ register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO,
+ tb->cpu_mondo_qmask);
+ register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO,
+ tb->dev_mondo_qmask);
+ register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR,
+ tb->resum_qmask);
+ register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR,
+ tb->nonresum_qmask);
+}
+
+static void __init alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask)
+{
+ unsigned long size = PAGE_ALIGN(qmask + 1);
+ void *p = __alloc_bootmem(size, size, 0);
+ if (!p) {
+ prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
+ prom_halt();
+ }
+
+ *pa_ptr = __pa(p);
+}
+
+static void __init alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask)
+{
+ unsigned long size = PAGE_ALIGN(qmask + 1);
+ void *p = __alloc_bootmem(size, size, 0);
+
+ if (!p) {
+ prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
+ prom_halt();
+ }
+
+ *pa_ptr = __pa(p);
+}
+
+static void __init init_cpu_send_mondo_info(struct trap_per_cpu *tb)
+{
+#ifdef CONFIG_SMP
+ void *page;
+
+ BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64));
+
+ page = alloc_bootmem_pages(PAGE_SIZE);
+ if (!page) {
+ prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n");
+ prom_halt();
+ }
+
+ tb->cpu_mondo_block_pa = __pa(page);
+ tb->cpu_list_pa = __pa(page + 64);
+#endif
+}
+
+/* Allocate mondo and error queues for all possible cpus. */
+static void __init sun4v_init_mondo_queues(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct trap_per_cpu *tb = &trap_block[cpu];
+
+ alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask);
+ alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask);
+ alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask);
+ alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask);
+ alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask);
+ alloc_one_kbuf(&tb->nonresum_kernel_buf_pa,
+ tb->nonresum_qmask);
+ }
+}
+
+static void __init init_send_mondo_info(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct trap_per_cpu *tb = &trap_block[cpu];
+
+ init_cpu_send_mondo_info(tb);
+ }
+}
+
+static struct irqaction timer_irq_action = {
+ .name = "timer",
+};
+
+/* Only invoked on boot processor. */
+void __init init_IRQ(void)
+{
+ unsigned long size;
+
+ map_prom_timers();
+ kill_prom_timer();
+
+ size = sizeof(struct ino_bucket) * NUM_IVECS;
+ ivector_table = alloc_bootmem(size);
+ if (!ivector_table) {
+ prom_printf("Fatal error, cannot allocate ivector_table\n");
+ prom_halt();
+ }
+ __flush_dcache_range((unsigned long) ivector_table,
+ ((unsigned long) ivector_table) + size);
+
+ ivector_table_pa = __pa(ivector_table);
+
+ if (tlb_type == hypervisor)
+ sun4v_init_mondo_queues();
+
+ init_send_mondo_info();
+
+ if (tlb_type == hypervisor) {
+ /* Load up the boot cpu's entries. */
+ sun4v_register_mondo_queues(hard_smp_processor_id());
+ }
+
+ /* We need to clear any IRQ's pending in the soft interrupt
+ * registers, a spurious one could be left around from the
+ * PROM timer which we just disabled.
+ */
+ clear_softint(get_softint());
+
+ /* Now that ivector table is initialized, it is safe
+ * to receive IRQ vector traps. We will normally take
+ * one or two right now, in case some device PROM used
+ * to boot us wants to speak to us. We just ignore them.
+ */
+ __asm__ __volatile__("rdpr %%pstate, %%g1\n\t"
+ "or %%g1, %0, %%g1\n\t"
+ "wrpr %%g1, 0x0, %%pstate"
+ : /* No outputs */
+ : "i" (PSTATE_IE)
+ : "g1");
+
+ irq_desc[0].action = &timer_irq_action;
+}
diff --git a/arch/sparc64/kernel/itlb_miss.S b/arch/sparc/kernel/itlb_miss.S
index 5a8377b54955..5a8377b54955 100644
--- a/arch/sparc64/kernel/itlb_miss.S
+++ b/arch/sparc/kernel/itlb_miss.S
diff --git a/arch/sparc64/kernel/ivec.S b/arch/sparc/kernel/ivec.S
index d29f92ebca5e..d29f92ebca5e 100644
--- a/arch/sparc64/kernel/ivec.S
+++ b/arch/sparc/kernel/ivec.S
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
new file mode 100644
index 000000000000..81a972e8d8ea
--- /dev/null
+++ b/arch/sparc/kernel/kernel.h
@@ -0,0 +1,31 @@
+#ifndef __SPARC_KERNEL_H
+#define __SPARC_KERNEL_H
+
+#include <linux/interrupt.h>
+
+/* cpu.c */
+extern const char *sparc_cpu_type;
+extern const char *sparc_fpu_type;
+
+extern unsigned int fsr_storage;
+
+#ifdef CONFIG_SPARC32
+/* cpu.c */
+extern void cpu_probe(void);
+
+/* traps_32.c */
+extern void handle_hw_divzero(struct pt_regs *regs, unsigned long pc,
+ unsigned long npc, unsigned long psr);
+/* muldiv.c */
+extern int do_user_muldiv (struct pt_regs *, unsigned long);
+
+/* irq_32.c */
+extern struct irqaction static_irqaction[];
+extern int static_irq_count;
+extern spinlock_t irq_action_lock;
+
+extern void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs);
+
+#else /* CONFIG_SPARC32 */
+#endif /* CONFIG_SPARC32 */
+#endif /* !(__SPARC_KERNEL_H) */
diff --git a/arch/sparc/kernel/kgdb.c b/arch/sparc/kernel/kgdb_32.c
index 757805ce02ee..757805ce02ee 100644
--- a/arch/sparc/kernel/kgdb.c
+++ b/arch/sparc/kernel/kgdb_32.c
diff --git a/arch/sparc64/kernel/kgdb.c b/arch/sparc/kernel/kgdb_64.c
index fefbe6dc51be..fefbe6dc51be 100644
--- a/arch/sparc64/kernel/kgdb.c
+++ b/arch/sparc/kernel/kgdb_64.c
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc/kernel/kprobes.c
index 201a6e547e4a..201a6e547e4a 100644
--- a/arch/sparc64/kernel/kprobes.c
+++ b/arch/sparc/kernel/kprobes.c
diff --git a/arch/sparc64/kernel/kstack.h b/arch/sparc/kernel/kstack.h
index 4248d969272f..4248d969272f 100644
--- a/arch/sparc64/kernel/kstack.h
+++ b/arch/sparc/kernel/kstack.h
diff --git a/arch/sparc64/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index cef8defcd7a9..cef8defcd7a9 100644
--- a/arch/sparc64/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
diff --git a/arch/sparc64/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index d68982330f66..d68982330f66 100644
--- a/arch/sparc64/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
new file mode 100644
index 000000000000..3c539a6d7c18
--- /dev/null
+++ b/arch/sparc/kernel/mdesc.c
@@ -0,0 +1,917 @@
+/* mdesc.c: Sun4V machine description handling.
+ *
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/lmb.h>
+#include <linux/log2.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+
+#include <asm/cpudata.h>
+#include <asm/hypervisor.h>
+#include <asm/mdesc.h>
+#include <asm/prom.h>
+#include <asm/oplib.h>
+#include <asm/smp.h>
+
+/* Unlike the OBP device tree, the machine description is a full-on
+ * DAG. An arbitrary number of ARCs are possible from one
+ * node to other nodes and thus we can't use the OBP device_node
+ * data structure to represent these nodes inside of the kernel.
+ *
+ * Actually, it isn't even a DAG, because there are back pointers
+ * which create cycles in the graph.
+ *
+ * mdesc_hdr and mdesc_elem describe the layout of the data structure
+ * we get from the Hypervisor.
+ */
+struct mdesc_hdr {
+ u32 version; /* Transport version */
+ u32 node_sz; /* node block size */
+ u32 name_sz; /* name block size */
+ u32 data_sz; /* data block size */
+} __attribute__((aligned(16)));
+
+struct mdesc_elem {
+ u8 tag;
+#define MD_LIST_END 0x00
+#define MD_NODE 0x4e
+#define MD_NODE_END 0x45
+#define MD_NOOP 0x20
+#define MD_PROP_ARC 0x61
+#define MD_PROP_VAL 0x76
+#define MD_PROP_STR 0x73
+#define MD_PROP_DATA 0x64
+ u8 name_len;
+ u16 resv;
+ u32 name_offset;
+ union {
+ struct {
+ u32 data_len;
+ u32 data_offset;
+ } data;
+ u64 val;
+ } d;
+};
+
+struct mdesc_mem_ops {
+ struct mdesc_handle *(*alloc)(unsigned int mdesc_size);
+ void (*free)(struct mdesc_handle *handle);
+};
+
+struct mdesc_handle {
+ struct list_head list;
+ struct mdesc_mem_ops *mops;
+ void *self_base;
+ atomic_t refcnt;
+ unsigned int handle_size;
+ struct mdesc_hdr mdesc;
+};
+
+static void mdesc_handle_init(struct mdesc_handle *hp,
+ unsigned int handle_size,
+ void *base)
+{
+ BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1));
+
+ memset(hp, 0, handle_size);
+ INIT_LIST_HEAD(&hp->list);
+ hp->self_base = base;
+ atomic_set(&hp->refcnt, 1);
+ hp->handle_size = handle_size;
+}
+
+static struct mdesc_handle * __init mdesc_lmb_alloc(unsigned int mdesc_size)
+{
+ unsigned int handle_size, alloc_size;
+ struct mdesc_handle *hp;
+ unsigned long paddr;
+
+ handle_size = (sizeof(struct mdesc_handle) -
+ sizeof(struct mdesc_hdr) +
+ mdesc_size);
+ alloc_size = PAGE_ALIGN(handle_size);
+
+ paddr = lmb_alloc(alloc_size, PAGE_SIZE);
+
+ hp = NULL;
+ if (paddr) {
+ hp = __va(paddr);
+ mdesc_handle_init(hp, handle_size, hp);
+ }
+ return hp;
+}
+
+static void mdesc_lmb_free(struct mdesc_handle *hp)
+{
+ unsigned int alloc_size, handle_size = hp->handle_size;
+ unsigned long start, end;
+
+ BUG_ON(atomic_read(&hp->refcnt) != 0);
+ BUG_ON(!list_empty(&hp->list));
+
+ alloc_size = PAGE_ALIGN(handle_size);
+
+ start = (unsigned long) hp;
+ end = start + alloc_size;
+
+ while (start < end) {
+ struct page *p;
+
+ p = virt_to_page(start);
+ ClearPageReserved(p);
+ __free_page(p);
+ start += PAGE_SIZE;
+ }
+}
+
+static struct mdesc_mem_ops lmb_mdesc_ops = {
+ .alloc = mdesc_lmb_alloc,
+ .free = mdesc_lmb_free,
+};
+
+static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
+{
+ unsigned int handle_size;
+ void *base;
+
+ handle_size = (sizeof(struct mdesc_handle) -
+ sizeof(struct mdesc_hdr) +
+ mdesc_size);
+
+ base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);
+ if (base) {
+ struct mdesc_handle *hp;
+ unsigned long addr;
+
+ addr = (unsigned long)base;
+ addr = (addr + 15UL) & ~15UL;
+ hp = (struct mdesc_handle *) addr;
+
+ mdesc_handle_init(hp, handle_size, base);
+ return hp;
+ }
+
+ return NULL;
+}
+
+static void mdesc_kfree(struct mdesc_handle *hp)
+{
+ BUG_ON(atomic_read(&hp->refcnt) != 0);
+ BUG_ON(!list_empty(&hp->list));
+
+ kfree(hp->self_base);
+}
+
+static struct mdesc_mem_ops kmalloc_mdesc_memops = {
+ .alloc = mdesc_kmalloc,
+ .free = mdesc_kfree,
+};
+
+static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size,
+ struct mdesc_mem_ops *mops)
+{
+ struct mdesc_handle *hp = mops->alloc(mdesc_size);
+
+ if (hp)
+ hp->mops = mops;
+
+ return hp;
+}
+
+static void mdesc_free(struct mdesc_handle *hp)
+{
+ hp->mops->free(hp);
+}
+
+static struct mdesc_handle *cur_mdesc;
+static LIST_HEAD(mdesc_zombie_list);
+static DEFINE_SPINLOCK(mdesc_lock);
+
+struct mdesc_handle *mdesc_grab(void)
+{
+ struct mdesc_handle *hp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdesc_lock, flags);
+ hp = cur_mdesc;
+ if (hp)
+ atomic_inc(&hp->refcnt);
+ spin_unlock_irqrestore(&mdesc_lock, flags);
+
+ return hp;
+}
+EXPORT_SYMBOL(mdesc_grab);
+
+void mdesc_release(struct mdesc_handle *hp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdesc_lock, flags);
+ if (atomic_dec_and_test(&hp->refcnt)) {
+ list_del_init(&hp->list);
+ hp->mops->free(hp);
+ }
+ spin_unlock_irqrestore(&mdesc_lock, flags);
+}
+EXPORT_SYMBOL(mdesc_release);
+
+static DEFINE_MUTEX(mdesc_mutex);
+static struct mdesc_notifier_client *client_list;
+
+void mdesc_register_notifier(struct mdesc_notifier_client *client)
+{
+ u64 node;
+
+ mutex_lock(&mdesc_mutex);
+ client->next = client_list;
+ client_list = client;
+
+ mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
+ client->add(cur_mdesc, node);
+
+ mutex_unlock(&mdesc_mutex);
+}
+
+static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node)
+{
+ const u64 *id;
+ u64 a;
+
+ id = NULL;
+ mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
+ u64 target;
+
+ target = mdesc_arc_target(hp, a);
+ id = mdesc_get_property(hp, target,
+ "cfg-handle", NULL);
+ if (id)
+ break;
+ }
+
+ return id;
+}
+
+/* Run 'func' on nodes which are in A but not in B. */
+static void invoke_on_missing(const char *name,
+ struct mdesc_handle *a,
+ struct mdesc_handle *b,
+ void (*func)(struct mdesc_handle *, u64))
+{
+ u64 node;
+
+ mdesc_for_each_node_by_name(a, node, name) {
+ int found = 0, is_vdc_port = 0;
+ const char *name_prop;
+ const u64 *id;
+ u64 fnode;
+
+ name_prop = mdesc_get_property(a, node, "name", NULL);
+ if (name_prop && !strcmp(name_prop, "vdc-port")) {
+ is_vdc_port = 1;
+ id = parent_cfg_handle(a, node);
+ } else
+ id = mdesc_get_property(a, node, "id", NULL);
+
+ if (!id) {
+ printk(KERN_ERR "MD: Cannot find ID for %s node.\n",
+ (name_prop ? name_prop : name));
+ continue;
+ }
+
+ mdesc_for_each_node_by_name(b, fnode, name) {
+ const u64 *fid;
+
+ if (is_vdc_port) {
+ name_prop = mdesc_get_property(b, fnode,
+ "name", NULL);
+ if (!name_prop ||
+ strcmp(name_prop, "vdc-port"))
+ continue;
+ fid = parent_cfg_handle(b, fnode);
+ if (!fid) {
+ printk(KERN_ERR "MD: Cannot find ID "
+ "for vdc-port node.\n");
+ continue;
+ }
+ } else
+ fid = mdesc_get_property(b, fnode,
+ "id", NULL);
+
+ if (*id == *fid) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ func(a, node);
+ }
+}
+
+static void notify_one(struct mdesc_notifier_client *p,
+ struct mdesc_handle *old_hp,
+ struct mdesc_handle *new_hp)
+{
+ invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);
+ invoke_on_missing(p->node_name, new_hp, old_hp, p->add);
+}
+
+static void mdesc_notify_clients(struct mdesc_handle *old_hp,
+ struct mdesc_handle *new_hp)
+{
+ struct mdesc_notifier_client *p = client_list;
+
+ while (p) {
+ notify_one(p, old_hp, new_hp);
+ p = p->next;
+ }
+}
+
+void mdesc_update(void)
+{
+ unsigned long len, real_len, status;
+ struct mdesc_handle *hp, *orig_hp;
+ unsigned long flags;
+
+ mutex_lock(&mdesc_mutex);
+
+ (void) sun4v_mach_desc(0UL, 0UL, &len);
+
+ hp = mdesc_alloc(len, &kmalloc_mdesc_memops);
+ if (!hp) {
+ printk(KERN_ERR "MD: mdesc alloc fails\n");
+ goto out;
+ }
+
+ status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
+ if (status != HV_EOK || real_len > len) {
+ printk(KERN_ERR "MD: mdesc reread fails with %lu\n",
+ status);
+ atomic_dec(&hp->refcnt);
+ mdesc_free(hp);
+ goto out;
+ }
+
+ spin_lock_irqsave(&mdesc_lock, flags);
+ orig_hp = cur_mdesc;
+ cur_mdesc = hp;
+ spin_unlock_irqrestore(&mdesc_lock, flags);
+
+ mdesc_notify_clients(orig_hp, hp);
+
+ spin_lock_irqsave(&mdesc_lock, flags);
+ if (atomic_dec_and_test(&orig_hp->refcnt))
+ mdesc_free(orig_hp);
+ else
+ list_add(&orig_hp->list, &mdesc_zombie_list);
+ spin_unlock_irqrestore(&mdesc_lock, flags);
+
+out:
+ mutex_unlock(&mdesc_mutex);
+}
+
+static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
+{
+ return (struct mdesc_elem *) (mdesc + 1);
+}
+
+static void *name_block(struct mdesc_hdr *mdesc)
+{
+ return ((void *) node_block(mdesc)) + mdesc->node_sz;
+}
+
+static void *data_block(struct mdesc_hdr *mdesc)
+{
+ return ((void *) name_block(mdesc)) + mdesc->name_sz;
+}
+
+u64 mdesc_node_by_name(struct mdesc_handle *hp,
+ u64 from_node, const char *name)
+{
+ struct mdesc_elem *ep = node_block(&hp->mdesc);
+ const char *names = name_block(&hp->mdesc);
+ u64 last_node = hp->mdesc.node_sz / 16;
+ u64 ret;
+
+ if (from_node == MDESC_NODE_NULL) {
+ ret = from_node = 0;
+ } else if (from_node >= last_node) {
+ return MDESC_NODE_NULL;
+ } else {
+ ret = ep[from_node].d.val;
+ }
+
+ while (ret < last_node) {
+ if (ep[ret].tag != MD_NODE)
+ return MDESC_NODE_NULL;
+ if (!strcmp(names + ep[ret].name_offset, name))
+ break;
+ ret = ep[ret].d.val;
+ }
+ if (ret >= last_node)
+ ret = MDESC_NODE_NULL;
+ return ret;
+}
+EXPORT_SYMBOL(mdesc_node_by_name);
+
+const void *mdesc_get_property(struct mdesc_handle *hp, u64 node,
+ const char *name, int *lenp)
+{
+ const char *names = name_block(&hp->mdesc);
+ u64 last_node = hp->mdesc.node_sz / 16;
+ void *data = data_block(&hp->mdesc);
+ struct mdesc_elem *ep;
+
+ if (node == MDESC_NODE_NULL || node >= last_node)
+ return NULL;
+
+ ep = node_block(&hp->mdesc) + node;
+ ep++;
+ for (; ep->tag != MD_NODE_END; ep++) {
+ void *val = NULL;
+ int len = 0;
+
+ switch (ep->tag) {
+ case MD_PROP_VAL:
+ val = &ep->d.val;
+ len = 8;
+ break;
+
+ case MD_PROP_STR:
+ case MD_PROP_DATA:
+ val = data + ep->d.data.data_offset;
+ len = ep->d.data.data_len;
+ break;
+
+ default:
+ break;
+ }
+ if (!val)
+ continue;
+
+ if (!strcmp(names + ep->name_offset, name)) {
+ if (lenp)
+ *lenp = len;
+ return val;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(mdesc_get_property);
+
+u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type)
+{
+ struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
+ const char *names = name_block(&hp->mdesc);
+ u64 last_node = hp->mdesc.node_sz / 16;
+
+ if (from == MDESC_NODE_NULL || from >= last_node)
+ return MDESC_NODE_NULL;
+
+ ep = base + from;
+
+ ep++;
+ for (; ep->tag != MD_NODE_END; ep++) {
+ if (ep->tag != MD_PROP_ARC)
+ continue;
+
+ if (strcmp(names + ep->name_offset, arc_type))
+ continue;
+
+ return ep - base;
+ }
+
+ return MDESC_NODE_NULL;
+}
+EXPORT_SYMBOL(mdesc_next_arc);
+
+u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc)
+{
+ struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
+
+ ep = base + arc;
+
+ return ep->d.val;
+}
+EXPORT_SYMBOL(mdesc_arc_target);
+
+const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)
+{
+ struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
+ const char *names = name_block(&hp->mdesc);
+ u64 last_node = hp->mdesc.node_sz / 16;
+
+ if (node == MDESC_NODE_NULL || node >= last_node)
+ return NULL;
+
+ ep = base + node;
+ if (ep->tag != MD_NODE)
+ return NULL;
+
+ return names + ep->name_offset;
+}
+EXPORT_SYMBOL(mdesc_node_name);
+
+static void __init report_platform_properties(void)
+{
+ struct mdesc_handle *hp = mdesc_grab();
+ u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform");
+ const char *s;
+ const u64 *v;
+
+ if (pn == MDESC_NODE_NULL) {
+ prom_printf("No platform node in machine-description.\n");
+ prom_halt();
+ }
+
+ s = mdesc_get_property(hp, pn, "banner-name", NULL);
+ printk("PLATFORM: banner-name [%s]\n", s);
+ s = mdesc_get_property(hp, pn, "name", NULL);
+ printk("PLATFORM: name [%s]\n", s);
+
+ v = mdesc_get_property(hp, pn, "hostid", NULL);
+ if (v)
+ printk("PLATFORM: hostid [%08lx]\n", *v);
+ v = mdesc_get_property(hp, pn, "serial#", NULL);
+ if (v)
+ printk("PLATFORM: serial# [%08lx]\n", *v);
+ v = mdesc_get_property(hp, pn, "stick-frequency", NULL);
+ printk("PLATFORM: stick-frequency [%08lx]\n", *v);
+ v = mdesc_get_property(hp, pn, "mac-address", NULL);
+ if (v)
+ printk("PLATFORM: mac-address [%lx]\n", *v);
+ v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL);
+ if (v)
+ printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v);
+ v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL);
+ if (v)
+ printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v);
+ v = mdesc_get_property(hp, pn, "max-cpus", NULL);
+ if (v)
+ printk("PLATFORM: max-cpus [%lu]\n", *v);
+
+#ifdef CONFIG_SMP
+ {
+ int max_cpu, i;
+
+ if (v) {
+ max_cpu = *v;
+ if (max_cpu > NR_CPUS)
+ max_cpu = NR_CPUS;
+ } else {
+ max_cpu = NR_CPUS;
+ }
+ for (i = 0; i < max_cpu; i++)
+ cpu_set(i, cpu_possible_map);
+ }
+#endif
+
+ mdesc_release(hp);
+}
+
+static void __devinit fill_in_one_cache(cpuinfo_sparc *c,
+ struct mdesc_handle *hp,
+ u64 mp)
+{
+ const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
+ const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
+ const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
+ const char *type;
+ int type_len;
+
+ type = mdesc_get_property(hp, mp, "type", &type_len);
+
+ switch (*level) {
+ case 1:
+ if (of_find_in_proplist(type, "instn", type_len)) {
+ c->icache_size = *size;
+ c->icache_line_size = *line_size;
+ } else if (of_find_in_proplist(type, "data", type_len)) {
+ c->dcache_size = *size;
+ c->dcache_line_size = *line_size;
+ }
+ break;
+
+ case 2:
+ c->ecache_size = *size;
+ c->ecache_line_size = *line_size;
+ break;
+
+ default:
+ break;
+ }
+
+ if (*level == 1) {
+ u64 a;
+
+ mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(hp, a);
+ const char *name = mdesc_node_name(hp, target);
+
+ if (!strcmp(name, "cache"))
+ fill_in_one_cache(c, hp, target);
+ }
+ }
+}
+
+static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp,
+ int core_id)
+{
+ u64 a;
+
+ mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
+ u64 t = mdesc_arc_target(hp, a);
+ const char *name;
+ const u64 *id;
+
+ name = mdesc_node_name(hp, t);
+ if (!strcmp(name, "cpu")) {
+ id = mdesc_get_property(hp, t, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_data(*id).core_id = core_id;
+ } else {
+ u64 j;
+
+ mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) {
+ u64 n = mdesc_arc_target(hp, j);
+ const char *n_name;
+
+ n_name = mdesc_node_name(hp, n);
+ if (strcmp(n_name, "cpu"))
+ continue;
+
+ id = mdesc_get_property(hp, n, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_data(*id).core_id = core_id;
+ }
+ }
+ }
+}
+
+static void __devinit set_core_ids(struct mdesc_handle *hp)
+{
+ int idx;
+ u64 mp;
+
+ idx = 1;
+ mdesc_for_each_node_by_name(hp, mp, "cache") {
+ const u64 *level;
+ const char *type;
+ int len;
+
+ level = mdesc_get_property(hp, mp, "level", NULL);
+ if (*level != 1)
+ continue;
+
+ type = mdesc_get_property(hp, mp, "type", &len);
+ if (!of_find_in_proplist(type, "instn", len))
+ continue;
+
+ mark_core_ids(hp, mp, idx);
+
+ idx++;
+ }
+}
+
+static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp,
+ int proc_id)
+{
+ u64 a;
+
+ mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
+ u64 t = mdesc_arc_target(hp, a);
+ const char *name;
+ const u64 *id;
+
+ name = mdesc_node_name(hp, t);
+ if (strcmp(name, "cpu"))
+ continue;
+
+ id = mdesc_get_property(hp, t, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_data(*id).proc_id = proc_id;
+ }
+}
+
+static void __devinit __set_proc_ids(struct mdesc_handle *hp,
+ const char *exec_unit_name)
+{
+ int idx;
+ u64 mp;
+
+ idx = 0;
+ mdesc_for_each_node_by_name(hp, mp, exec_unit_name) {
+ const char *type;
+ int len;
+
+ type = mdesc_get_property(hp, mp, "type", &len);
+ if (!of_find_in_proplist(type, "int", len) &&
+ !of_find_in_proplist(type, "integer", len))
+ continue;
+
+ mark_proc_ids(hp, mp, idx);
+
+ idx++;
+ }
+}
+
+static void __devinit set_proc_ids(struct mdesc_handle *hp)
+{
+ __set_proc_ids(hp, "exec_unit");
+ __set_proc_ids(hp, "exec-unit");
+}
+
+static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
+ unsigned char def)
+{
+ u64 val;
+
+ if (!p)
+ goto use_default;
+ val = *p;
+
+ if (!val || val >= 64)
+ goto use_default;
+
+ *mask = ((1U << val) * 64U) - 1U;
+ return;
+
+use_default:
+ *mask = ((1U << def) * 64U) - 1U;
+}
+
+static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
+ struct trap_per_cpu *tb)
+{
+ const u64 *val;
+
+ val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
+ get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);
+
+ val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
+ get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);
+
+ val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
+ get_one_mondo_bits(val, &tb->resum_qmask, 6);
+
+ val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
+ get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
+}
+
+void __cpuinit mdesc_fill_in_cpu_data(cpumask_t mask)
+{
+ struct mdesc_handle *hp = mdesc_grab();
+ u64 mp;
+
+ ncpus_probed = 0;
+ mdesc_for_each_node_by_name(hp, mp, "cpu") {
+ const u64 *id = mdesc_get_property(hp, mp, "id", NULL);
+ const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL);
+ struct trap_per_cpu *tb;
+ cpuinfo_sparc *c;
+ int cpuid;
+ u64 a;
+
+ ncpus_probed++;
+
+ cpuid = *id;
+
+#ifdef CONFIG_SMP
+ if (cpuid >= NR_CPUS) {
+ printk(KERN_WARNING "Ignoring CPU %d which is "
+ ">= NR_CPUS (%d)\n",
+ cpuid, NR_CPUS);
+ continue;
+ }
+ if (!cpu_isset(cpuid, mask))
+ continue;
+#else
+ /* On uniprocessor we only want the values for the
+ * real physical cpu the kernel booted onto, however
+ * cpu_data() only has one entry at index 0.
+ */
+ if (cpuid != real_hard_smp_processor_id())
+ continue;
+ cpuid = 0;
+#endif
+
+ c = &cpu_data(cpuid);
+ c->clock_tick = *cfreq;
+
+ tb = &trap_block[cpuid];
+ get_mondo_data(hp, mp, tb);
+
+ mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
+ u64 j, t = mdesc_arc_target(hp, a);
+ const char *t_name;
+
+ t_name = mdesc_node_name(hp, t);
+ if (!strcmp(t_name, "cache")) {
+ fill_in_one_cache(c, hp, t);
+ continue;
+ }
+
+ mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) {
+ u64 n = mdesc_arc_target(hp, j);
+ const char *n_name;
+
+ n_name = mdesc_node_name(hp, n);
+ if (!strcmp(n_name, "cache"))
+ fill_in_one_cache(c, hp, n);
+ }
+ }
+
+#ifdef CONFIG_SMP
+ cpu_set(cpuid, cpu_present_map);
+#endif
+
+ c->core_id = 0;
+ c->proc_id = -1;
+ }
+
+#ifdef CONFIG_SMP
+ sparc64_multi_core = 1;
+#endif
+
+ set_core_ids(hp);
+ set_proc_ids(hp);
+
+ smp_fill_in_sib_core_maps();
+
+ mdesc_release(hp);
+}
+
+static ssize_t mdesc_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offp)
+{
+ struct mdesc_handle *hp = mdesc_grab();
+ int err;
+
+ if (!hp)
+ return -ENODEV;
+
+ err = hp->handle_size;
+ if (len < hp->handle_size)
+ err = -EMSGSIZE;
+ else if (copy_to_user(buf, &hp->mdesc, hp->handle_size))
+ err = -EFAULT;
+ mdesc_release(hp);
+
+ return err;
+}
+
+static const struct file_operations mdesc_fops = {
+ .read = mdesc_read,
+ .owner = THIS_MODULE,
+};
+
+static struct miscdevice mdesc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mdesc",
+ .fops = &mdesc_fops,
+};
+
+static int __init mdesc_misc_init(void)
+{
+ return misc_register(&mdesc_misc);
+}
+
+__initcall(mdesc_misc_init);
+
+void __init sun4v_mdesc_init(void)
+{
+ struct mdesc_handle *hp;
+ unsigned long len, real_len, status;
+ cpumask_t mask;
+
+ (void) sun4v_mach_desc(0UL, 0UL, &len);
+
+ printk("MDESC: Size is %lu bytes.\n", len);
+
+ hp = mdesc_alloc(len, &lmb_mdesc_ops);
+ if (hp == NULL) {
+ prom_printf("MDESC: alloc of %lu bytes failed.\n", len);
+ prom_halt();
+ }
+
+ status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
+ if (status != HV_EOK || real_len > len) {
+ prom_printf("sun4v_mach_desc fails, err(%lu), "
+ "len(%lu), real_len(%lu)\n",
+ status, len, real_len);
+ mdesc_free(hp);
+ prom_halt();
+ }
+
+ cur_mdesc = hp;
+
+ report_platform_properties();
+
+ cpus_setall(mask);
+ mdesc_fill_in_cpu_data(mask);
+}
diff --git a/arch/sparc64/kernel/misctrap.S b/arch/sparc/kernel/misctrap.S
index 753b4f031bfb..753b4f031bfb 100644
--- a/arch/sparc64/kernel/misctrap.S
+++ b/arch/sparc/kernel/misctrap.S
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index 598682f31ebf..90273765e81f 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -1,4 +1,4 @@
-/* Kernel module help for sparc32.
+/* Kernel module help for sparc64.
*
* Copyright (C) 2001 Rusty Russell.
* Copyright (C) 2002 David S. Miller.
@@ -11,6 +11,48 @@
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <asm/processor.h>
+#include <asm/spitfire.h>
+
+#ifdef CONFIG_SPARC64
+static void *module_map(unsigned long size)
+{
+ struct vm_struct *area;
+
+ size = PAGE_ALIGN(size);
+ if (!size || size > MODULES_LEN)
+ return NULL;
+
+ area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
+ if (!area)
+ return NULL;
+
+ return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
+}
+
+static char *dot2underscore(char *name)
+{
+ return name;
+}
+#else
+static void *module_map(unsigned long size)
+{
+ return vmalloc(size);
+}
+
+/* Replace references to .func with _Func */
+static char *dot2underscore(char *name)
+{
+ if (name[0] == '.') {
+ name[0] = '_';
+ name[1] = toupper(name[1]);
+ }
+ return name;
+}
+#endif /* CONFIG_SPARC64 */
void *module_alloc(unsigned long size)
{
@@ -20,7 +62,7 @@ void *module_alloc(unsigned long size)
if (size == 0)
return NULL;
- ret = vmalloc(size);
+ ret = module_map(size);
if (!ret)
ret = ERR_PTR(-ENOMEM);
else
@@ -37,16 +79,14 @@ void module_free(struct module *mod, void *module_region)
table entries. */
}
-/* Make generic code ignore STT_REGISTER dummy undefined symbols,
- * and replace references to .func with _Func
- */
+/* Make generic code ignore STT_REGISTER dummy undefined symbols. */
int module_frob_arch_sections(Elf_Ehdr *hdr,
Elf_Shdr *sechdrs,
char *secstrings,
struct module *mod)
{
unsigned int symidx;
- Elf32_Sym *sym;
+ Elf_Sym *sym;
char *strtab;
int i;
@@ -56,26 +96,23 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
return -ENOEXEC;
}
}
- sym = (Elf32_Sym *)sechdrs[symidx].sh_addr;
+ sym = (Elf_Sym *)sechdrs[symidx].sh_addr;
strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr;
for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) {
if (sym[i].st_shndx == SHN_UNDEF) {
- if (ELF32_ST_TYPE(sym[i].st_info) == STT_REGISTER)
+ if (ELF_ST_TYPE(sym[i].st_info) == STT_REGISTER) {
sym[i].st_shndx = SHN_ABS;
- else {
+ } else {
char *name = strtab + sym[i].st_name;
- if (name[0] == '.') {
- name[0] = '_';
- name[1] = toupper(name[1]);
- }
+ dot2underscore(name);
}
}
}
return 0;
}
-int apply_relocate(Elf32_Shdr *sechdrs,
+int apply_relocate(Elf_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
@@ -86,32 +123,68 @@ int apply_relocate(Elf32_Shdr *sechdrs,
return -ENOEXEC;
}
-int apply_relocate_add(Elf32_Shdr *sechdrs,
+int apply_relocate_add(Elf_Shdr *sechdrs,
const char *strtab,
unsigned int symindex,
unsigned int relsec,
struct module *me)
{
unsigned int i;
- Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
- Elf32_Sym *sym;
+ Elf_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+ Elf_Sym *sym;
u8 *location;
u32 *loc32;
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
- Elf32_Addr v;
+ Elf_Addr v;
/* This is where to make the change */
location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rel[i].r_offset;
loc32 = (u32 *) location;
+
+#ifdef CONFIG_SPARC64
+ BUG_ON(((u64)location >> (u64)32) != (u64)0);
+#endif /* CONFIG_SPARC64 */
+
/* This is the symbol it is referring to. Note that all
undefined symbols have been resolved. */
- sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
- + ELF32_R_SYM(rel[i].r_info);
+ sym = (Elf_Sym *)sechdrs[symindex].sh_addr
+ + ELF_R_SYM(rel[i].r_info);
v = sym->st_value + rel[i].r_addend;
- switch (ELF32_R_TYPE(rel[i].r_info)) {
+ switch (ELF_R_TYPE(rel[i].r_info) & 0xff) {
+#ifdef CONFIG_SPARC64
+ case R_SPARC_64:
+ location[0] = v >> 56;
+ location[1] = v >> 48;
+ location[2] = v >> 40;
+ location[3] = v >> 32;
+ location[4] = v >> 24;
+ location[5] = v >> 16;
+ location[6] = v >> 8;
+ 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) |
+ ((v >> 2) & 0x7ffff);
+ break;
+
+ case R_SPARC_OLO10:
+ *loc32 = (*loc32 & ~0x1fff) |
+ (((v & 0x3ff) +
+ (ELF_R_TYPE(rel[i].r_info) >> 8))
+ & 0x1fff);
+ break;
+#endif /* CONFIG_SPARC64 */
+
case R_SPARC_32:
case R_SPARC_UA32:
location[0] = v >> 24;
@@ -121,13 +194,13 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
break;
case R_SPARC_WDISP30:
- v -= (Elf32_Addr) location;
+ v -= (Elf_Addr) location;
*loc32 = (*loc32 & ~0x3fffffff) |
((v >> 2) & 0x3fffffff);
break;
case R_SPARC_WDISP22:
- v -= (Elf32_Addr) location;
+ v -= (Elf_Addr) location;
*loc32 = (*loc32 & ~0x3fffff) |
((v >> 2) & 0x3fffff);
break;
@@ -144,19 +217,38 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
default:
printk(KERN_ERR "module %s: Unknown relocation: %x\n",
me->name,
- (int) (ELF32_R_TYPE(rel[i].r_info) & 0xff));
+ (int) (ELF_R_TYPE(rel[i].r_info) & 0xff));
return -ENOEXEC;
};
}
return 0;
}
+#ifdef CONFIG_SPARC64
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
{
+ /* Cheetah's I-cache is fully coherent. */
+ if (tlb_type == spitfire) {
+ unsigned long va;
+
+ flushw_all();
+ for (va = 0; va < (PAGE_SIZE << 1); va += 32)
+ spitfire_put_icache_tag(va, 0x0);
+ __asm__ __volatile__("flush %g6");
+ }
+
return 0;
}
+#else
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ return 0;
+}
+#endif /* CONFIG_SPARC64 */
void module_arch_cleanup(struct module *mod)
{
diff --git a/arch/sparc/kernel/muldiv.c b/arch/sparc/kernel/muldiv.c
index e352239e72c8..ba960c02bb55 100644
--- a/arch/sparc/kernel/muldiv.c
+++ b/arch/sparc/kernel/muldiv.c
@@ -17,6 +17,8 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#include "kernel.h"
+
/* #define DEBUG_MULDIV */
static inline int has_imm13(int insn)
@@ -88,9 +90,6 @@ store_reg(unsigned int result, unsigned int reg, struct pt_regs *regs)
return (put_user(result, &win->locals[reg - 16]));
}
}
-
-extern void handle_hw_divzero (struct pt_regs *regs, unsigned long pc,
- unsigned long npc, unsigned long psr);
/* Should return 0 if mul/div emulation succeeded and SIGILL should
* not be issued.
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device_32.c
index 0a83bd737654..0a83bd737654 100644
--- a/arch/sparc/kernel/of_device.c
+++ b/arch/sparc/kernel/of_device_32.c
diff --git a/arch/sparc/kernel/of_device_64.c b/arch/sparc/kernel/of_device_64.c
new file mode 100644
index 000000000000..4873f28905b0
--- /dev/null
+++ b/arch/sparc/kernel/of_device_64.c
@@ -0,0 +1,898 @@
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name)
+{
+ unsigned long ret = res->start + offset;
+ struct resource *r;
+
+ if (res->flags & IORESOURCE_MEM)
+ r = request_mem_region(ret, size, name);
+ else
+ r = request_region(ret, size, name);
+ if (!r)
+ ret = 0;
+
+ return (void __iomem *) ret;
+}
+EXPORT_SYMBOL(of_ioremap);
+
+void of_iounmap(struct resource *res, void __iomem *base, unsigned long size)
+{
+ if (res->flags & IORESOURCE_MEM)
+ release_mem_region((unsigned long) base, size);
+ else
+ release_region((unsigned long) base, size);
+}
+EXPORT_SYMBOL(of_iounmap);
+
+static int node_match(struct device *dev, void *data)
+{
+ struct of_device *op = to_of_device(dev);
+ struct device_node *dp = data;
+
+ return (op->node == dp);
+}
+
+struct of_device *of_find_device_by_node(struct device_node *dp)
+{
+ struct device *dev = bus_find_device(&of_platform_bus_type, NULL,
+ dp, node_match);
+
+ if (dev)
+ return to_of_device(dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_node);
+
+unsigned int irq_of_parse_and_map(struct device_node *node, int index)
+{
+ struct of_device *op = of_find_device_by_node(node);
+
+ if (!op || index >= op->num_irqs)
+ return 0;
+
+ return op->irqs[index];
+}
+EXPORT_SYMBOL(irq_of_parse_and_map);
+
+/* Take the archdata values for IOMMU, STC, and HOSTDATA found in
+ * BUS and propagate to all child of_device objects.
+ */
+void of_propagate_archdata(struct of_device *bus)
+{
+ struct dev_archdata *bus_sd = &bus->dev.archdata;
+ struct device_node *bus_dp = bus->node;
+ struct device_node *dp;
+
+ for (dp = bus_dp->child; dp; dp = dp->sibling) {
+ struct of_device *op = of_find_device_by_node(dp);
+
+ op->dev.archdata.iommu = bus_sd->iommu;
+ op->dev.archdata.stc = bus_sd->stc;
+ op->dev.archdata.host_controller = bus_sd->host_controller;
+ op->dev.archdata.numa_node = bus_sd->numa_node;
+
+ if (dp->child)
+ of_propagate_archdata(op);
+ }
+}
+
+struct bus_type of_platform_bus_type;
+EXPORT_SYMBOL(of_platform_bus_type);
+
+static inline u64 of_read_addr(const u32 *cell, int size)
+{
+ u64 r = 0;
+ while (size--)
+ r = (r << 32) | *(cell++);
+ return r;
+}
+
+static void __init get_cells(struct device_node *dp,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = of_n_addr_cells(dp);
+ if (sizec)
+ *sizec = of_n_size_cells(dp);
+}
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+
+struct of_bus {
+ const char *name;
+ const char *addr_prop_name;
+ int (*match)(struct device_node *parent);
+ void (*count_cells)(struct device_node *child,
+ int *addrc, int *sizec);
+ int (*map)(u32 *addr, const u32 *range,
+ int na, int ns, int pna);
+ unsigned long (*get_flags)(const u32 *addr, unsigned long);
+};
+
+/*
+ * Default translator (generic bus)
+ */
+
+static void of_bus_default_count_cells(struct device_node *dev,
+ int *addrc, int *sizec)
+{
+ get_cells(dev, addrc, sizec);
+}
+
+/* Make sure the least significant 64-bits are in-range. Even
+ * for 3 or 4 cell values it is a good enough approximation.
+ */
+static int of_out_of_range(const u32 *addr, const u32 *base,
+ const u32 *size, int na, int ns)
+{
+ u64 a = of_read_addr(addr, na);
+ u64 b = of_read_addr(base, na);
+
+ if (a < b)
+ return 1;
+
+ b += of_read_addr(size, ns);
+ if (a >= b)
+ return 1;
+
+ return 0;
+}
+
+static int of_bus_default_map(u32 *addr, const u32 *range,
+ int na, int ns, int pna)
+{
+ u32 result[OF_MAX_ADDR_CELLS];
+ int i;
+
+ if (ns > 2) {
+ printk("of_device: Cannot handle size cells (%d) > 2.", ns);
+ return -EINVAL;
+ }
+
+ if (of_out_of_range(addr, range, range + na + pna, na, ns))
+ return -EINVAL;
+
+ /* Start with the parent range base. */
+ memcpy(result, range + na, pna * 4);
+
+ /* Add in the child address offset. */
+ for (i = 0; i < na; i++)
+ result[pna - 1 - i] +=
+ (addr[na - 1 - i] -
+ range[na - 1 - i]);
+
+ memcpy(addr, result, pna * 4);
+
+ return 0;
+}
+
+static unsigned long of_bus_default_get_flags(const u32 *addr, unsigned long flags)
+{
+ if (flags)
+ return flags;
+ return IORESOURCE_MEM;
+}
+
+/*
+ * PCI bus specific translator
+ */
+
+static int of_bus_pci_match(struct device_node *np)
+{
+ if (!strcmp(np->name, "pci")) {
+ const char *model = of_get_property(np, "model", NULL);
+
+ if (model && !strcmp(model, "SUNW,simba"))
+ return 0;
+
+ /* Do not do PCI specific frobbing if the
+ * PCI bridge lacks a ranges property. We
+ * want to pass it through up to the next
+ * parent as-is, not with the PCI translate
+ * method which chops off the top address cell.
+ */
+ if (!of_find_property(np, "ranges", NULL))
+ return 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int of_bus_simba_match(struct device_node *np)
+{
+ const char *model = of_get_property(np, "model", NULL);
+
+ if (model && !strcmp(model, "SUNW,simba"))
+ return 1;
+
+ /* Treat PCI busses lacking ranges property just like
+ * simba.
+ */
+ if (!strcmp(np->name, "pci")) {
+ if (!of_find_property(np, "ranges", NULL))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int of_bus_simba_map(u32 *addr, const u32 *range,
+ int na, int ns, int pna)
+{
+ return 0;
+}
+
+static void of_bus_pci_count_cells(struct device_node *np,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 3;
+ if (sizec)
+ *sizec = 2;
+}
+
+static int of_bus_pci_map(u32 *addr, const u32 *range,
+ int na, int ns, int pna)
+{
+ u32 result[OF_MAX_ADDR_CELLS];
+ int i;
+
+ /* Check address type match */
+ if ((addr[0] ^ range[0]) & 0x03000000)
+ return -EINVAL;
+
+ if (of_out_of_range(addr + 1, range + 1, range + na + pna,
+ na - 1, ns))
+ return -EINVAL;
+
+ /* Start with the parent range base. */
+ memcpy(result, range + na, pna * 4);
+
+ /* Add in the child address offset, skipping high cell. */
+ for (i = 0; i < na - 1; i++)
+ result[pna - 1 - i] +=
+ (addr[na - 1 - i] -
+ range[na - 1 - i]);
+
+ memcpy(addr, result, pna * 4);
+
+ return 0;
+}
+
+static unsigned long of_bus_pci_get_flags(const u32 *addr, unsigned long flags)
+{
+ u32 w = addr[0];
+
+ /* For PCI, we override whatever child busses may have used. */
+ flags = 0;
+ switch((w >> 24) & 0x03) {
+ case 0x01:
+ flags |= IORESOURCE_IO;
+ break;
+
+ case 0x02: /* 32 bits */
+ case 0x03: /* 64 bits */
+ flags |= IORESOURCE_MEM;
+ break;
+ }
+ if (w & 0x40000000)
+ flags |= IORESOURCE_PREFETCH;
+ return flags;
+}
+
+/*
+ * SBUS bus specific translator
+ */
+
+static int of_bus_sbus_match(struct device_node *np)
+{
+ return !strcmp(np->name, "sbus") ||
+ !strcmp(np->name, "sbi");
+}
+
+static void of_bus_sbus_count_cells(struct device_node *child,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 2;
+ if (sizec)
+ *sizec = 1;
+}
+
+/*
+ * FHC/Central bus specific translator.
+ *
+ * This is just needed to hard-code the address and size cell
+ * counts. 'fhc' and 'central' nodes lack the #address-cells and
+ * #size-cells properties, and if you walk to the root on such
+ * Enterprise boxes all you'll get is a #size-cells of 2 which is
+ * not what we want to use.
+ */
+static int of_bus_fhc_match(struct device_node *np)
+{
+ return !strcmp(np->name, "fhc") ||
+ !strcmp(np->name, "central");
+}
+
+#define of_bus_fhc_count_cells of_bus_sbus_count_cells
+
+/*
+ * Array of bus specific translators
+ */
+
+static struct of_bus of_busses[] = {
+ /* PCI */
+ {
+ .name = "pci",
+ .addr_prop_name = "assigned-addresses",
+ .match = of_bus_pci_match,
+ .count_cells = of_bus_pci_count_cells,
+ .map = of_bus_pci_map,
+ .get_flags = of_bus_pci_get_flags,
+ },
+ /* SIMBA */
+ {
+ .name = "simba",
+ .addr_prop_name = "assigned-addresses",
+ .match = of_bus_simba_match,
+ .count_cells = of_bus_pci_count_cells,
+ .map = of_bus_simba_map,
+ .get_flags = of_bus_pci_get_flags,
+ },
+ /* SBUS */
+ {
+ .name = "sbus",
+ .addr_prop_name = "reg",
+ .match = of_bus_sbus_match,
+ .count_cells = of_bus_sbus_count_cells,
+ .map = of_bus_default_map,
+ .get_flags = of_bus_default_get_flags,
+ },
+ /* FHC */
+ {
+ .name = "fhc",
+ .addr_prop_name = "reg",
+ .match = of_bus_fhc_match,
+ .count_cells = of_bus_fhc_count_cells,
+ .map = of_bus_default_map,
+ .get_flags = of_bus_default_get_flags,
+ },
+ /* Default */
+ {
+ .name = "default",
+ .addr_prop_name = "reg",
+ .match = NULL,
+ .count_cells = of_bus_default_count_cells,
+ .map = of_bus_default_map,
+ .get_flags = of_bus_default_get_flags,
+ },
+};
+
+static struct of_bus *of_match_bus(struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
+ if (!of_busses[i].match || of_busses[i].match(np))
+ return &of_busses[i];
+ BUG();
+ return NULL;
+}
+
+static int __init build_one_resource(struct device_node *parent,
+ struct of_bus *bus,
+ struct of_bus *pbus,
+ u32 *addr,
+ int na, int ns, int pna)
+{
+ const u32 *ranges;
+ int rone, rlen;
+
+ ranges = of_get_property(parent, "ranges", &rlen);
+ if (ranges == NULL || rlen == 0) {
+ u32 result[OF_MAX_ADDR_CELLS];
+ int i;
+
+ memset(result, 0, pna * 4);
+ for (i = 0; i < na; i++)
+ result[pna - 1 - i] =
+ addr[na - 1 - i];
+
+ memcpy(addr, result, pna * 4);
+ return 0;
+ }
+
+ /* Now walk through the ranges */
+ rlen /= 4;
+ rone = na + pna + ns;
+ for (; rlen >= rone; rlen -= rone, ranges += rone) {
+ if (!bus->map(addr, ranges, na, ns, pna))
+ return 0;
+ }
+
+ /* When we miss an I/O space match on PCI, just pass it up
+ * to the next PCI bridge and/or controller.
+ */
+ if (!strcmp(bus->name, "pci") &&
+ (addr[0] & 0x03000000) == 0x01000000)
+ return 0;
+
+ return 1;
+}
+
+static int __init use_1to1_mapping(struct device_node *pp)
+{
+ /* If we have a ranges property in the parent, use it. */
+ if (of_find_property(pp, "ranges", NULL) != NULL)
+ return 0;
+
+ /* If the parent is the dma node of an ISA bus, pass
+ * the translation up to the root.
+ *
+ * Some SBUS devices use intermediate nodes to express
+ * hierarchy within the device itself. These aren't
+ * real bus nodes, and don't have a 'ranges' property.
+ * But, we should still pass the translation work up
+ * to the SBUS itself.
+ */
+ if (!strcmp(pp->name, "dma") ||
+ !strcmp(pp->name, "espdma") ||
+ !strcmp(pp->name, "ledma") ||
+ !strcmp(pp->name, "lebuffer"))
+ return 0;
+
+ /* Similarly for all PCI bridges, if we get this far
+ * it lacks a ranges property, and this will include
+ * cases like Simba.
+ */
+ if (!strcmp(pp->name, "pci"))
+ return 0;
+
+ return 1;
+}
+
+static int of_resource_verbose;
+
+static void __init build_device_resources(struct of_device *op,
+ struct device *parent)
+{
+ struct of_device *p_op;
+ struct of_bus *bus;
+ int na, ns;
+ int index, num_reg;
+ const void *preg;
+
+ if (!parent)
+ return;
+
+ p_op = to_of_device(parent);
+ bus = of_match_bus(p_op->node);
+ bus->count_cells(op->node, &na, &ns);
+
+ preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);
+ if (!preg || num_reg == 0)
+ return;
+
+ /* Convert to num-cells. */
+ num_reg /= 4;
+
+ /* Convert to num-entries. */
+ num_reg /= na + ns;
+
+ /* Prevent overrunning the op->resources[] array. */
+ if (num_reg > PROMREG_MAX) {
+ printk(KERN_WARNING "%s: Too many regs (%d), "
+ "limiting to %d.\n",
+ op->node->full_name, num_reg, PROMREG_MAX);
+ num_reg = PROMREG_MAX;
+ }
+
+ for (index = 0; index < num_reg; index++) {
+ struct resource *r = &op->resource[index];
+ u32 addr[OF_MAX_ADDR_CELLS];
+ const u32 *reg = (preg + (index * ((na + ns) * 4)));
+ struct device_node *dp = op->node;
+ struct device_node *pp = p_op->node;
+ struct of_bus *pbus, *dbus;
+ u64 size, result = OF_BAD_ADDR;
+ unsigned long flags;
+ int dna, dns;
+ int pna, pns;
+
+ size = of_read_addr(reg + na, ns);
+ memcpy(addr, reg, na * 4);
+
+ flags = bus->get_flags(addr, 0);
+
+ if (use_1to1_mapping(pp)) {
+ result = of_read_addr(addr, na);
+ goto build_res;
+ }
+
+ dna = na;
+ dns = ns;
+ dbus = bus;
+
+ while (1) {
+ dp = pp;
+ pp = dp->parent;
+ if (!pp) {
+ result = of_read_addr(addr, dna);
+ break;
+ }
+
+ pbus = of_match_bus(pp);
+ pbus->count_cells(dp, &pna, &pns);
+
+ if (build_one_resource(dp, dbus, pbus, addr,
+ dna, dns, pna))
+ break;
+
+ flags = pbus->get_flags(addr, flags);
+
+ dna = pna;
+ dns = pns;
+ dbus = pbus;
+ }
+
+ build_res:
+ memset(r, 0, sizeof(*r));
+
+ if (of_resource_verbose)
+ printk("%s reg[%d] -> %lx\n",
+ op->node->full_name, index,
+ result);
+
+ if (result != OF_BAD_ADDR) {
+ if (tlb_type == hypervisor)
+ result &= 0x0fffffffffffffffUL;
+
+ r->start = result;
+ r->end = result + size - 1;
+ r->flags = flags;
+ }
+ r->name = op->node->name;
+ }
+}
+
+static struct device_node * __init
+apply_interrupt_map(struct device_node *dp, struct device_node *pp,
+ const u32 *imap, int imlen, const u32 *imask,
+ unsigned int *irq_p)
+{
+ struct device_node *cp;
+ unsigned int irq = *irq_p;
+ struct of_bus *bus;
+ phandle handle;
+ const u32 *reg;
+ int na, num_reg, i;
+
+ bus = of_match_bus(pp);
+ bus->count_cells(dp, &na, NULL);
+
+ reg = of_get_property(dp, "reg", &num_reg);
+ if (!reg || !num_reg)
+ return NULL;
+
+ imlen /= ((na + 3) * 4);
+ handle = 0;
+ for (i = 0; i < imlen; i++) {
+ int j;
+
+ for (j = 0; j < na; j++) {
+ if ((reg[j] & imask[j]) != imap[j])
+ goto next;
+ }
+ if (imap[na] == irq) {
+ handle = imap[na + 1];
+ irq = imap[na + 2];
+ break;
+ }
+
+ next:
+ imap += (na + 3);
+ }
+ if (i == imlen) {
+ /* Psycho and Sabre PCI controllers can have 'interrupt-map'
+ * properties that do not include the on-board device
+ * interrupts. Instead, the device's 'interrupts' property
+ * is already a fully specified INO value.
+ *
+ * Handle this by deciding that, if we didn't get a
+ * match in the parent's 'interrupt-map', and the
+ * parent is an IRQ translater, then use the parent as
+ * our IRQ controller.
+ */
+ if (pp->irq_trans)
+ return pp;
+
+ return NULL;
+ }
+
+ *irq_p = irq;
+ cp = of_find_node_by_phandle(handle);
+
+ return cp;
+}
+
+static unsigned int __init pci_irq_swizzle(struct device_node *dp,
+ struct device_node *pp,
+ unsigned int irq)
+{
+ const struct linux_prom_pci_registers *regs;
+ unsigned int bus, devfn, slot, ret;
+
+ if (irq < 1 || irq > 4)
+ return irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (!regs)
+ return irq;
+
+ bus = (regs->phys_hi >> 16) & 0xff;
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ slot = (devfn >> 3) & 0x1f;
+
+ if (pp->irq_trans) {
+ /* Derived from Table 8-3, U2P User's Manual. This branch
+ * is handling a PCI controller that lacks a proper set of
+ * interrupt-map and interrupt-map-mask properties. The
+ * Ultra-E450 is one example.
+ *
+ * The bit layout is BSSLL, where:
+ * B: 0 on bus A, 1 on bus B
+ * D: 2-bit slot number, derived from PCI device number as
+ * (dev - 1) for bus A, or (dev - 2) for bus B
+ * L: 2-bit line number
+ */
+ if (bus & 0x80) {
+ /* PBM-A */
+ bus = 0x00;
+ slot = (slot - 1) << 2;
+ } else {
+ /* PBM-B */
+ bus = 0x10;
+ slot = (slot - 2) << 2;
+ }
+ irq -= 1;
+
+ ret = (bus | slot | irq);
+ } else {
+ /* Going through a PCI-PCI bridge that lacks a set of
+ * interrupt-map and interrupt-map-mask properties.
+ */
+ ret = ((irq - 1 + (slot & 3)) & 3) + 1;
+ }
+
+ return ret;
+}
+
+static int of_irq_verbose;
+
+static unsigned int __init build_one_device_irq(struct of_device *op,
+ struct device *parent,
+ unsigned int irq)
+{
+ struct device_node *dp = op->node;
+ struct device_node *pp, *ip;
+ unsigned int orig_irq = irq;
+ int nid;
+
+ if (irq == 0xffffffff)
+ return irq;
+
+ if (dp->irq_trans) {
+ irq = dp->irq_trans->irq_build(dp, irq,
+ dp->irq_trans->data);
+
+ if (of_irq_verbose)
+ printk("%s: direct translate %x --> %x\n",
+ dp->full_name, orig_irq, irq);
+
+ goto out;
+ }
+
+ /* Something more complicated. Walk up to the root, applying
+ * interrupt-map or bus specific translations, until we hit
+ * an IRQ translator.
+ *
+ * If we hit a bus type or situation we cannot handle, we
+ * stop and assume that the original IRQ number was in a
+ * format which has special meaning to it's immediate parent.
+ */
+ pp = dp->parent;
+ ip = NULL;
+ while (pp) {
+ const void *imap, *imsk;
+ int imlen;
+
+ imap = of_get_property(pp, "interrupt-map", &imlen);
+ imsk = of_get_property(pp, "interrupt-map-mask", NULL);
+ if (imap && imsk) {
+ struct device_node *iret;
+ int this_orig_irq = irq;
+
+ iret = apply_interrupt_map(dp, pp,
+ imap, imlen, imsk,
+ &irq);
+
+ if (of_irq_verbose)
+ printk("%s: Apply [%s:%x] imap --> [%s:%x]\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq,
+ (iret ? iret->full_name : "NULL"), irq);
+
+ if (!iret)
+ break;
+
+ if (iret->irq_trans) {
+ ip = iret;
+ break;
+ }
+ } else {
+ if (!strcmp(pp->name, "pci")) {
+ unsigned int this_orig_irq = irq;
+
+ irq = pci_irq_swizzle(dp, pp, irq);
+ if (of_irq_verbose)
+ printk("%s: PCI swizzle [%s] "
+ "%x --> %x\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq,
+ irq);
+
+ }
+
+ if (pp->irq_trans) {
+ ip = pp;
+ break;
+ }
+ }
+ dp = pp;
+ pp = pp->parent;
+ }
+ if (!ip)
+ return orig_irq;
+
+ irq = ip->irq_trans->irq_build(op->node, irq,
+ ip->irq_trans->data);
+ if (of_irq_verbose)
+ printk("%s: Apply IRQ trans [%s] %x --> %x\n",
+ op->node->full_name, ip->full_name, orig_irq, irq);
+
+out:
+ nid = of_node_to_nid(dp);
+ if (nid != -1) {
+ cpumask_t numa_mask = *cpumask_of_node(nid);
+
+ irq_set_affinity(irq, &numa_mask);
+ }
+
+ return irq;
+}
+
+static struct of_device * __init scan_one_device(struct device_node *dp,
+ struct device *parent)
+{
+ struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
+ const unsigned int *irq;
+ struct dev_archdata *sd;
+ int len, i;
+
+ if (!op)
+ return NULL;
+
+ sd = &op->dev.archdata;
+ sd->prom_node = dp;
+ sd->op = op;
+
+ op->node = dp;
+
+ op->clock_freq = of_getintprop_default(dp, "clock-frequency",
+ (25*1000*1000));
+ op->portid = of_getintprop_default(dp, "upa-portid", -1);
+ if (op->portid == -1)
+ op->portid = of_getintprop_default(dp, "portid", -1);
+
+ irq = of_get_property(dp, "interrupts", &len);
+ if (irq) {
+ op->num_irqs = len / 4;
+
+ /* Prevent overrunning the op->irqs[] array. */
+ if (op->num_irqs > PROMINTR_MAX) {
+ printk(KERN_WARNING "%s: Too many irqs (%d), "
+ "limiting to %d.\n",
+ dp->full_name, op->num_irqs, PROMINTR_MAX);
+ op->num_irqs = PROMINTR_MAX;
+ }
+ memcpy(op->irqs, irq, op->num_irqs * 4);
+ } else {
+ op->num_irqs = 0;
+ }
+
+ build_device_resources(op, parent);
+ for (i = 0; i < op->num_irqs; i++)
+ op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);
+
+ op->dev.parent = parent;
+ op->dev.bus = &of_platform_bus_type;
+ if (!parent)
+ dev_set_name(&op->dev, "root");
+ else
+ dev_set_name(&op->dev, "%08x", dp->node);
+
+ if (of_device_register(op)) {
+ printk("%s: Could not register of device.\n",
+ dp->full_name);
+ kfree(op);
+ op = NULL;
+ }
+
+ return op;
+}
+
+static void __init scan_tree(struct device_node *dp, struct device *parent)
+{
+ while (dp) {
+ struct of_device *op = scan_one_device(dp, parent);
+
+ if (op)
+ scan_tree(dp->child, &op->dev);
+
+ dp = dp->sibling;
+ }
+}
+
+static void __init scan_of_devices(void)
+{
+ struct device_node *root = of_find_node_by_path("/");
+ struct of_device *parent;
+
+ parent = scan_one_device(root, NULL);
+ if (!parent)
+ return;
+
+ scan_tree(root->child, &parent->dev);
+}
+
+static int __init of_bus_driver_init(void)
+{
+ int err;
+
+ err = of_bus_type_init(&of_platform_bus_type, "of");
+ if (!err)
+ scan_of_devices();
+
+ return err;
+}
+
+postcore_initcall(of_bus_driver_init);
+
+static int __init of_debug(char *str)
+{
+ int val = 0;
+
+ get_option(&str, &val);
+ if (val & 1)
+ of_resource_verbose = 1;
+ if (val & 2)
+ of_irq_verbose = 1;
+ return 1;
+}
+
+__setup("of_debug=", of_debug);
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc/kernel/pci.c
index bdb7c0a6d83d..bdb7c0a6d83d 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c
index 23b88082d0b2..23b88082d0b2 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc/kernel/pci_common.c
diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc/kernel/pci_fire.c
index 9462b68f4894..9462b68f4894 100644
--- a/arch/sparc64/kernel/pci_fire.c
+++ b/arch/sparc/kernel/pci_fire.c
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h
index 03186824327e..03186824327e 100644
--- a/arch/sparc64/kernel/pci_impl.h
+++ b/arch/sparc/kernel/pci_impl.h
diff --git a/arch/sparc/kernel/pci_msi.c b/arch/sparc/kernel/pci_msi.c
new file mode 100644
index 000000000000..4ef282e81912
--- /dev/null
+++ b/arch/sparc/kernel/pci_msi.c
@@ -0,0 +1,447 @@
+/* pci_msi.c: Sparc64 MSI support common layer.
+ *
+ * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include "pci_impl.h"
+
+static irqreturn_t sparc64_msiq_interrupt(int irq, void *cookie)
+{
+ struct sparc64_msiq_cookie *msiq_cookie = cookie;
+ struct pci_pbm_info *pbm = msiq_cookie->pbm;
+ unsigned long msiqid = msiq_cookie->msiqid;
+ const struct sparc64_msiq_ops *ops;
+ unsigned long orig_head, head;
+ int err;
+
+ ops = pbm->msi_ops;
+
+ err = ops->get_head(pbm, msiqid, &head);
+ if (unlikely(err < 0))
+ goto err_get_head;
+
+ orig_head = head;
+ for (;;) {
+ unsigned long msi;
+
+ err = ops->dequeue_msi(pbm, msiqid, &head, &msi);
+ if (likely(err > 0)) {
+ struct irq_desc *desc;
+ unsigned int virt_irq;
+
+ virt_irq = pbm->msi_irq_table[msi - pbm->msi_first];
+ desc = irq_desc + virt_irq;
+
+ desc->handle_irq(virt_irq, desc);
+ }
+
+ if (unlikely(err < 0))
+ goto err_dequeue;
+
+ if (err == 0)
+ break;
+ }
+ if (likely(head != orig_head)) {
+ err = ops->set_head(pbm, msiqid, head);
+ if (unlikely(err < 0))
+ goto err_set_head;
+ }
+ return IRQ_HANDLED;
+
+err_get_head:
+ printk(KERN_EMERG "MSI: Get head on msiqid[%lu] gives error %d\n",
+ msiqid, err);
+ goto err_out;
+
+err_dequeue:
+ printk(KERN_EMERG "MSI: Dequeue head[%lu] from msiqid[%lu] "
+ "gives error %d\n",
+ head, msiqid, err);
+ goto err_out;
+
+err_set_head:
+ printk(KERN_EMERG "MSI: Set head[%lu] on msiqid[%lu] "
+ "gives error %d\n",
+ head, msiqid, err);
+ goto err_out;
+
+err_out:
+ return IRQ_NONE;
+}
+
+static u32 pick_msiq(struct pci_pbm_info *pbm)
+{
+ static DEFINE_SPINLOCK(rotor_lock);
+ unsigned long flags;
+ u32 ret, rotor;
+
+ spin_lock_irqsave(&rotor_lock, flags);
+
+ rotor = pbm->msiq_rotor;
+ ret = pbm->msiq_first + rotor;
+
+ if (++rotor >= pbm->msiq_num)
+ rotor = 0;
+ pbm->msiq_rotor = rotor;
+
+ spin_unlock_irqrestore(&rotor_lock, flags);
+
+ return ret;
+}
+
+
+static int alloc_msi(struct pci_pbm_info *pbm)
+{
+ int i;
+
+ for (i = 0; i < pbm->msi_num; i++) {
+ if (!test_and_set_bit(i, pbm->msi_bitmap))
+ return i + pbm->msi_first;
+ }
+
+ return -ENOENT;
+}
+
+static void free_msi(struct pci_pbm_info *pbm, int msi_num)
+{
+ msi_num -= pbm->msi_first;
+ clear_bit(msi_num, pbm->msi_bitmap);
+}
+
+static struct irq_chip msi_irq = {
+ .typename = "PCI-MSI",
+ .mask = mask_msi_irq,
+ .unmask = unmask_msi_irq,
+ .enable = unmask_msi_irq,
+ .disable = mask_msi_irq,
+ /* XXX affinity XXX */
+};
+
+static int sparc64_setup_msi_irq(unsigned int *virt_irq_p,
+ struct pci_dev *pdev,
+ struct msi_desc *entry)
+{
+ struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
+ const struct sparc64_msiq_ops *ops = pbm->msi_ops;
+ struct msi_msg msg;
+ int msi, err;
+ u32 msiqid;
+
+ *virt_irq_p = virt_irq_alloc(0, 0);
+ err = -ENOMEM;
+ if (!*virt_irq_p)
+ goto out_err;
+
+ set_irq_chip_and_handler_name(*virt_irq_p, &msi_irq,
+ handle_simple_irq, "MSI");
+
+ err = alloc_msi(pbm);
+ if (unlikely(err < 0))
+ goto out_virt_irq_free;
+
+ msi = err;
+
+ msiqid = pick_msiq(pbm);
+
+ err = ops->msi_setup(pbm, msiqid, msi,
+ (entry->msi_attrib.is_64 ? 1 : 0));
+ if (err)
+ goto out_msi_free;
+
+ pbm->msi_irq_table[msi - pbm->msi_first] = *virt_irq_p;
+
+ if (entry->msi_attrib.is_64) {
+ msg.address_hi = pbm->msi64_start >> 32;
+ msg.address_lo = pbm->msi64_start & 0xffffffff;
+ } else {
+ msg.address_hi = 0;
+ msg.address_lo = pbm->msi32_start;
+ }
+ msg.data = msi;
+
+ set_irq_msi(*virt_irq_p, entry);
+ write_msi_msg(*virt_irq_p, &msg);
+
+ return 0;
+
+out_msi_free:
+ free_msi(pbm, msi);
+
+out_virt_irq_free:
+ set_irq_chip(*virt_irq_p, NULL);
+ virt_irq_free(*virt_irq_p);
+ *virt_irq_p = 0;
+
+out_err:
+ return err;
+}
+
+static void sparc64_teardown_msi_irq(unsigned int virt_irq,
+ struct pci_dev *pdev)
+{
+ struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
+ const struct sparc64_msiq_ops *ops = pbm->msi_ops;
+ unsigned int msi_num;
+ int i, err;
+
+ for (i = 0; i < pbm->msi_num; i++) {
+ if (pbm->msi_irq_table[i] == virt_irq)
+ break;
+ }
+ if (i >= pbm->msi_num) {
+ printk(KERN_ERR "%s: teardown: No MSI for irq %u\n",
+ pbm->name, virt_irq);
+ return;
+ }
+
+ msi_num = pbm->msi_first + i;
+ pbm->msi_irq_table[i] = ~0U;
+
+ err = ops->msi_teardown(pbm, msi_num);
+ if (err) {
+ printk(KERN_ERR "%s: teardown: ops->teardown() on MSI %u, "
+ "irq %u, gives error %d\n",
+ pbm->name, msi_num, virt_irq, err);
+ return;
+ }
+
+ free_msi(pbm, msi_num);
+
+ set_irq_chip(virt_irq, NULL);
+ virt_irq_free(virt_irq);
+}
+
+static int msi_bitmap_alloc(struct pci_pbm_info *pbm)
+{
+ unsigned long size, bits_per_ulong;
+
+ bits_per_ulong = sizeof(unsigned long) * 8;
+ size = (pbm->msi_num + (bits_per_ulong - 1)) & ~(bits_per_ulong - 1);
+ size /= 8;
+ BUG_ON(size % sizeof(unsigned long));
+
+ pbm->msi_bitmap = kzalloc(size, GFP_KERNEL);
+ if (!pbm->msi_bitmap)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void msi_bitmap_free(struct pci_pbm_info *pbm)
+{
+ kfree(pbm->msi_bitmap);
+ pbm->msi_bitmap = NULL;
+}
+
+static int msi_table_alloc(struct pci_pbm_info *pbm)
+{
+ int size, i;
+
+ size = pbm->msiq_num * sizeof(struct sparc64_msiq_cookie);
+ pbm->msiq_irq_cookies = kzalloc(size, GFP_KERNEL);
+ if (!pbm->msiq_irq_cookies)
+ return -ENOMEM;
+
+ for (i = 0; i < pbm->msiq_num; i++) {
+ struct sparc64_msiq_cookie *p;
+
+ p = &pbm->msiq_irq_cookies[i];
+ p->pbm = pbm;
+ p->msiqid = pbm->msiq_first + i;
+ }
+
+ size = pbm->msi_num * sizeof(unsigned int);
+ pbm->msi_irq_table = kzalloc(size, GFP_KERNEL);
+ if (!pbm->msi_irq_table) {
+ kfree(pbm->msiq_irq_cookies);
+ pbm->msiq_irq_cookies = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void msi_table_free(struct pci_pbm_info *pbm)
+{
+ kfree(pbm->msiq_irq_cookies);
+ pbm->msiq_irq_cookies = NULL;
+
+ kfree(pbm->msi_irq_table);
+ pbm->msi_irq_table = NULL;
+}
+
+static int bringup_one_msi_queue(struct pci_pbm_info *pbm,
+ const struct sparc64_msiq_ops *ops,
+ unsigned long msiqid,
+ unsigned long devino)
+{
+ int irq = ops->msiq_build_irq(pbm, msiqid, devino);
+ int err, nid;
+
+ if (irq < 0)
+ return irq;
+
+ nid = pbm->numa_node;
+ if (nid != -1) {
+ cpumask_t numa_mask = *cpumask_of_node(nid);
+
+ irq_set_affinity(irq, &numa_mask);
+ }
+ err = request_irq(irq, sparc64_msiq_interrupt, 0,
+ "MSIQ",
+ &pbm->msiq_irq_cookies[msiqid - pbm->msiq_first]);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int sparc64_bringup_msi_queues(struct pci_pbm_info *pbm,
+ const struct sparc64_msiq_ops *ops)
+{
+ int i;
+
+ for (i = 0; i < pbm->msiq_num; i++) {
+ unsigned long msiqid = i + pbm->msiq_first;
+ unsigned long devino = i + pbm->msiq_first_devino;
+ int err;
+
+ err = bringup_one_msi_queue(pbm, ops, msiqid, devino);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+void sparc64_pbm_msi_init(struct pci_pbm_info *pbm,
+ const struct sparc64_msiq_ops *ops)
+{
+ const u32 *val;
+ int len;
+
+ val = of_get_property(pbm->op->node, "#msi-eqs", &len);
+ if (!val || len != 4)
+ goto no_msi;
+ pbm->msiq_num = *val;
+ if (pbm->msiq_num) {
+ const struct msiq_prop {
+ u32 first_msiq;
+ u32 num_msiq;
+ u32 first_devino;
+ } *mqp;
+ const struct msi_range_prop {
+ u32 first_msi;
+ u32 num_msi;
+ } *mrng;
+ const struct addr_range_prop {
+ u32 msi32_high;
+ u32 msi32_low;
+ u32 msi32_len;
+ u32 msi64_high;
+ u32 msi64_low;
+ u32 msi64_len;
+ } *arng;
+
+ val = of_get_property(pbm->op->node, "msi-eq-size", &len);
+ if (!val || len != 4)
+ goto no_msi;
+
+ pbm->msiq_ent_count = *val;
+
+ mqp = of_get_property(pbm->op->node,
+ "msi-eq-to-devino", &len);
+ if (!mqp)
+ mqp = of_get_property(pbm->op->node,
+ "msi-eq-devino", &len);
+ if (!mqp || len != sizeof(struct msiq_prop))
+ goto no_msi;
+
+ pbm->msiq_first = mqp->first_msiq;
+ pbm->msiq_first_devino = mqp->first_devino;
+
+ val = of_get_property(pbm->op->node, "#msi", &len);
+ if (!val || len != 4)
+ goto no_msi;
+ pbm->msi_num = *val;
+
+ mrng = of_get_property(pbm->op->node, "msi-ranges", &len);
+ if (!mrng || len != sizeof(struct msi_range_prop))
+ goto no_msi;
+ pbm->msi_first = mrng->first_msi;
+
+ val = of_get_property(pbm->op->node, "msi-data-mask", &len);
+ if (!val || len != 4)
+ goto no_msi;
+ pbm->msi_data_mask = *val;
+
+ val = of_get_property(pbm->op->node, "msix-data-width", &len);
+ if (!val || len != 4)
+ goto no_msi;
+ pbm->msix_data_width = *val;
+
+ arng = of_get_property(pbm->op->node, "msi-address-ranges",
+ &len);
+ if (!arng || len != sizeof(struct addr_range_prop))
+ goto no_msi;
+ pbm->msi32_start = ((u64)arng->msi32_high << 32) |
+ (u64) arng->msi32_low;
+ pbm->msi64_start = ((u64)arng->msi64_high << 32) |
+ (u64) arng->msi64_low;
+ pbm->msi32_len = arng->msi32_len;
+ pbm->msi64_len = arng->msi64_len;
+
+ if (msi_bitmap_alloc(pbm))
+ goto no_msi;
+
+ if (msi_table_alloc(pbm)) {
+ msi_bitmap_free(pbm);
+ goto no_msi;
+ }
+
+ if (ops->msiq_alloc(pbm)) {
+ msi_table_free(pbm);
+ msi_bitmap_free(pbm);
+ goto no_msi;
+ }
+
+ if (sparc64_bringup_msi_queues(pbm, ops)) {
+ ops->msiq_free(pbm);
+ msi_table_free(pbm);
+ msi_bitmap_free(pbm);
+ goto no_msi;
+ }
+
+ printk(KERN_INFO "%s: MSI Queue first[%u] num[%u] count[%u] "
+ "devino[0x%x]\n",
+ pbm->name,
+ pbm->msiq_first, pbm->msiq_num,
+ pbm->msiq_ent_count,
+ pbm->msiq_first_devino);
+ printk(KERN_INFO "%s: MSI first[%u] num[%u] mask[0x%x] "
+ "width[%u]\n",
+ pbm->name,
+ pbm->msi_first, pbm->msi_num, pbm->msi_data_mask,
+ pbm->msix_data_width);
+ printk(KERN_INFO "%s: MSI addr32[0x%lx:0x%x] "
+ "addr64[0x%lx:0x%x]\n",
+ pbm->name,
+ pbm->msi32_start, pbm->msi32_len,
+ pbm->msi64_start, pbm->msi64_len);
+ printk(KERN_INFO "%s: MSI queues at RA [%016lx]\n",
+ pbm->name,
+ __pa(pbm->msi_queues));
+
+ pbm->msi_ops = ops;
+ pbm->setup_msi_irq = sparc64_setup_msi_irq;
+ pbm->teardown_msi_irq = sparc64_teardown_msi_irq;
+ }
+ return;
+
+no_msi:
+ pbm->msiq_num = 0;
+ printk(KERN_INFO "%s: No MSI support.\n", pbm->name);
+}
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc/kernel/pci_psycho.c
index dfb3ec892987..dfb3ec892987 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc/kernel/pci_psycho.c
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc/kernel/pci_sabre.c
index 713257b6963c..713257b6963c 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc/kernel/pci_sabre.c
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc/kernel/pci_schizo.c
index 45d9dba1ba11..45d9dba1ba11 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc/kernel/pci_schizo.c
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c
index 34a1fded3941..34a1fded3941 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc/kernel/pci_sun4v.c
diff --git a/arch/sparc64/kernel/pci_sun4v.h b/arch/sparc/kernel/pci_sun4v.h
index 8e9fc3a5b4f5..8e9fc3a5b4f5 100644
--- a/arch/sparc64/kernel/pci_sun4v.h
+++ b/arch/sparc/kernel/pci_sun4v.h
diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc/kernel/pci_sun4v_asm.S
index e606d46c6815..e606d46c6815 100644
--- a/arch/sparc64/kernel/pci_sun4v_asm.S
+++ b/arch/sparc/kernel/pci_sun4v_asm.S
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index 462584e55fba..75ed98be3edf 100644
--- a/arch/sparc/kernel/pcic.c
+++ b/arch/sparc/kernel/pcic.c
@@ -436,7 +436,7 @@ int pcic_present(void)
return pcic0_up;
}
-static int __init pdev_to_pnode(struct linux_pbm_info *pbm,
+static int __devinit pdev_to_pnode(struct linux_pbm_info *pbm,
struct pci_dev *pdev)
{
struct linux_prom_pci_registers regs[PROMREG_MAX];
diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c
index 2afcfab4f11c..5e4563d86f19 100644
--- a/arch/sparc/kernel/pmc.c
+++ b/arch/sparc/kernel/pmc.c
@@ -24,32 +24,32 @@
*/
#define PMC_OBPNAME "SUNW,pmc"
-#define PMC_DEVNAME "pmc"
+#define PMC_DEVNAME "pmc"
#define PMC_IDLE_REG 0x00
-#define PMC_IDLE_ON 0x01
+#define PMC_IDLE_ON 0x01
static u8 __iomem *regs;
#define pmc_readb(offs) (sbus_readb(regs+offs))
-#define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs))
+#define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs))
-/*
+/*
* CPU idle callback function
* See .../arch/sparc/kernel/process.c
*/
-void pmc_swift_idle(void)
+static void pmc_swift_idle(void)
{
#ifdef PMC_DEBUG_LED
- set_auxio(0x00, AUXIO_LED);
+ set_auxio(0x00, AUXIO_LED);
#endif
pmc_writeb(pmc_readb(PMC_IDLE_REG) | PMC_IDLE_ON, PMC_IDLE_REG);
#ifdef PMC_DEBUG_LED
- set_auxio(AUXIO_LED, 0x00);
+ set_auxio(AUXIO_LED, 0x00);
#endif
-}
+}
static int __devinit pmc_probe(struct of_device *op,
const struct of_device_id *match)
@@ -63,7 +63,7 @@ static int __devinit pmc_probe(struct of_device *op,
#ifndef PMC_NO_IDLE
/* Assign power management IDLE handler */
- pm_idle = pmc_swift_idle;
+ pm_idle = pmc_swift_idle;
#endif
printk(KERN_INFO "%s: power management initialized\n", PMC_DEVNAME);
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc/kernel/power.c
index 076cad7f9757..076cad7f9757 100644
--- a/arch/sparc64/kernel/power.c
+++ b/arch/sparc/kernel/power.c
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
deleted file mode 100644
index e8c43ffe317e..000000000000
--- a/arch/sparc/kernel/process.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/* linux/arch/sparc/kernel/process.c
- *
- * Copyright (C) 1995, 2008 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- */
-
-/*
- * This file handles the architecture-dependent parts of process handling..
- */
-
-#include <stdarg.h>
-
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/stddef.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/user.h>
-#include <linux/smp.h>
-#include <linux/reboot.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/init.h>
-
-#include <asm/auxio.h>
-#include <asm/oplib.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
-#include <asm/delay.h>
-#include <asm/processor.h>
-#include <asm/psr.h>
-#include <asm/elf.h>
-#include <asm/prom.h>
-#include <asm/unistd.h>
-
-/*
- * Power management idle function
- * Set in pm platform drivers (apc.c and pmc.c)
- */
-void (*pm_idle)(void);
-
-/*
- * Power-off handler instantiation for pm.h compliance
- * This is done via auxio, but could be used as a fallback
- * handler when auxio is not present-- unused for now...
- */
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
-/*
- * sysctl - toggle power-off restriction for serial console
- * systems in machine_power_off()
- */
-int scons_pwroff = 1;
-
-extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
-
-struct task_struct *last_task_used_math = NULL;
-struct thread_info *current_set[NR_CPUS];
-
-#ifndef CONFIG_SMP
-
-#define SUN4C_FAULT_HIGH 100
-
-/*
- * the idle loop on a Sparc... ;)
- */
-void cpu_idle(void)
-{
- /* endless idle loop with no priority at all */
- for (;;) {
- if (ARCH_SUN4C) {
- static int count = HZ;
- static unsigned long last_jiffies;
- static unsigned long last_faults;
- static unsigned long fps;
- unsigned long now;
- unsigned long faults;
-
- extern unsigned long sun4c_kernel_faults;
- extern void sun4c_grow_kernel_ring(void);
-
- local_irq_disable();
- now = jiffies;
- count -= (now - last_jiffies);
- last_jiffies = now;
- if (count < 0) {
- count += HZ;
- faults = sun4c_kernel_faults;
- fps = (fps + (faults - last_faults)) >> 1;
- last_faults = faults;
-#if 0
- printk("kernel faults / second = %ld\n", fps);
-#endif
- if (fps >= SUN4C_FAULT_HIGH) {
- sun4c_grow_kernel_ring();
- }
- }
- local_irq_enable();
- }
-
- if (pm_idle) {
- while (!need_resched())
- (*pm_idle)();
- } else {
- while (!need_resched())
- cpu_relax();
- }
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- check_pgt_cache();
- }
-}
-
-#else
-
-/* This is being executed in task 0 'user space'. */
-void cpu_idle(void)
-{
- set_thread_flag(TIF_POLLING_NRFLAG);
- /* endless idle loop with no priority at all */
- while(1) {
- while (!need_resched())
- cpu_relax();
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- check_pgt_cache();
- }
-}
-
-#endif
-
-/* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
-void machine_halt(void)
-{
- local_irq_enable();
- mdelay(8);
- local_irq_disable();
- prom_halt();
- panic("Halt failed!");
-}
-
-void machine_restart(char * cmd)
-{
- char *p;
-
- local_irq_enable();
- mdelay(8);
- local_irq_disable();
-
- p = strchr (reboot_command, '\n');
- if (p) *p = 0;
- if (cmd)
- prom_reboot(cmd);
- if (*reboot_command)
- prom_reboot(reboot_command);
- prom_feval ("reset");
- panic("Reboot failed!");
-}
-
-void machine_power_off(void)
-{
-#ifdef CONFIG_SUN_AUXIO
- if (auxio_power_register &&
- (strcmp(of_console_device->type, "serial") || scons_pwroff))
- *auxio_power_register |= AUXIO_POWER_OFF;
-#endif
- machine_halt();
-}
-
-#if 0
-
-static DEFINE_SPINLOCK(sparc_backtrace_lock);
-
-void __show_backtrace(unsigned long fp)
-{
- struct reg_window *rw;
- unsigned long flags;
- int cpu = smp_processor_id();
-
- spin_lock_irqsave(&sparc_backtrace_lock, flags);
-
- rw = (struct reg_window *)fp;
- while(rw && (((unsigned long) rw) >= PAGE_OFFSET) &&
- !(((unsigned long) rw) & 0x7)) {
- printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] "
- "FP[%08lx] CALLER[%08lx]: ", cpu,
- rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
- rw->ins[4], rw->ins[5],
- rw->ins[6],
- rw->ins[7]);
- printk("%pS\n", (void *) rw->ins[7]);
- rw = (struct reg_window *) rw->ins[6];
- }
- spin_unlock_irqrestore(&sparc_backtrace_lock, flags);
-}
-
-#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
-#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
-#define __GET_FP(fp) __asm__ __volatile__("mov %%i6, %0" : "=r" (fp))
-
-void show_backtrace(void)
-{
- unsigned long fp;
-
- __SAVE; __SAVE; __SAVE; __SAVE;
- __SAVE; __SAVE; __SAVE; __SAVE;
- __RESTORE; __RESTORE; __RESTORE; __RESTORE;
- __RESTORE; __RESTORE; __RESTORE; __RESTORE;
-
- __GET_FP(fp);
-
- __show_backtrace(fp);
-}
-
-#ifdef CONFIG_SMP
-void smp_show_backtrace_all_cpus(void)
-{
- xc0((smpfunc_t) show_backtrace);
- show_backtrace();
-}
-#endif
-
-void show_stackframe(struct sparc_stackf *sf)
-{
- unsigned long size;
- unsigned long *stk;
- int i;
-
- printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
- "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
- sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
- sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
- printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
- "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
- sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
- sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
- printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx "
- "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
- (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
- sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
- sf->xxargs[0]);
- size = ((unsigned long)sf->fp) - ((unsigned long)sf);
- size -= STACKFRAME_SZ;
- stk = (unsigned long *)((unsigned long)sf + STACKFRAME_SZ);
- i = 0;
- do {
- printk("s%d: %08lx\n", i++, *stk++);
- } while ((size -= sizeof(unsigned long)));
-}
-#endif
-
-void show_regs(struct pt_regs *r)
-{
- struct reg_window *rw = (struct reg_window *) r->u_regs[14];
-
- printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx %s\n",
- r->psr, r->pc, r->npc, r->y, print_tainted());
- printk("PC: <%pS>\n", (void *) r->pc);
- printk("%%G: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- r->u_regs[0], r->u_regs[1], r->u_regs[2], r->u_regs[3],
- r->u_regs[4], r->u_regs[5], r->u_regs[6], r->u_regs[7]);
- printk("%%O: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- r->u_regs[8], r->u_regs[9], r->u_regs[10], r->u_regs[11],
- r->u_regs[12], r->u_regs[13], r->u_regs[14], r->u_regs[15]);
- printk("RPC: <%pS>\n", (void *) r->u_regs[15]);
-
- printk("%%L: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
- rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
- printk("%%I: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
- rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
- rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
-}
-
-/*
- * The show_stack is an external API which we do not use ourselves.
- * The oops is printed in die_if_kernel.
- */
-void show_stack(struct task_struct *tsk, unsigned long *_ksp)
-{
- unsigned long pc, fp;
- unsigned long task_base;
- struct reg_window *rw;
- int count = 0;
-
- if (tsk != NULL)
- task_base = (unsigned long) task_stack_page(tsk);
- else
- task_base = (unsigned long) current_thread_info();
-
- fp = (unsigned long) _ksp;
- do {
- /* Bogus frame pointer? */
- if (fp < (task_base + sizeof(struct thread_info)) ||
- fp >= (task_base + (PAGE_SIZE << 1)))
- break;
- rw = (struct reg_window *) fp;
- pc = rw->ins[7];
- printk("[%08lx : ", pc);
- printk("%pS ] ", (void *) pc);
- fp = rw->ins[6];
- } while (++count < 16);
- printk("\n");
-}
-
-void dump_stack(void)
-{
- unsigned long *ksp;
-
- __asm__ __volatile__("mov %%fp, %0"
- : "=r" (ksp));
- show_stack(current, ksp);
-}
-
-EXPORT_SYMBOL(dump_stack);
-
-/*
- * Note: sparc64 has a pretty intricated thread_saved_pc, check it out.
- */
-unsigned long thread_saved_pc(struct task_struct *tsk)
-{
- return task_thread_info(tsk)->kpc;
-}
-
-/*
- * Free current thread data structures etc..
- */
-void exit_thread(void)
-{
-#ifndef CONFIG_SMP
- if(last_task_used_math == current) {
-#else
- if (test_thread_flag(TIF_USEDFPU)) {
-#endif
- /* Keep process from leaving FPU in a bogon state. */
- put_psr(get_psr() | PSR_EF);
- fpsave(&current->thread.float_regs[0], &current->thread.fsr,
- &current->thread.fpqueue[0], &current->thread.fpqdepth);
-#ifndef CONFIG_SMP
- last_task_used_math = NULL;
-#else
- clear_thread_flag(TIF_USEDFPU);
-#endif
- }
-}
-
-void flush_thread(void)
-{
- current_thread_info()->w_saved = 0;
-
-#ifndef CONFIG_SMP
- if(last_task_used_math == current) {
-#else
- if (test_thread_flag(TIF_USEDFPU)) {
-#endif
- /* Clean the fpu. */
- put_psr(get_psr() | PSR_EF);
- fpsave(&current->thread.float_regs[0], &current->thread.fsr,
- &current->thread.fpqueue[0], &current->thread.fpqdepth);
-#ifndef CONFIG_SMP
- last_task_used_math = NULL;
-#else
- clear_thread_flag(TIF_USEDFPU);
-#endif
- }
-
- /* Now, this task is no longer a kernel thread. */
- current->thread.current_ds = USER_DS;
- if (current->thread.flags & SPARC_FLAG_KTHREAD) {
- current->thread.flags &= ~SPARC_FLAG_KTHREAD;
-
- /* We must fixup kregs as well. */
- /* XXX This was not fixed for ti for a while, worked. Unused? */
- current->thread.kregs = (struct pt_regs *)
- (task_stack_page(current) + (THREAD_SIZE - TRACEREG_SZ));
- }
-}
-
-static inline struct sparc_stackf __user *
-clone_stackframe(struct sparc_stackf __user *dst,
- struct sparc_stackf __user *src)
-{
- unsigned long size, fp;
- struct sparc_stackf *tmp;
- struct sparc_stackf __user *sp;
-
- if (get_user(tmp, &src->fp))
- return NULL;
-
- fp = (unsigned long) tmp;
- size = (fp - ((unsigned long) src));
- fp = (unsigned long) dst;
- sp = (struct sparc_stackf __user *)(fp - size);
-
- /* do_fork() grabs the parent semaphore, we must release it
- * temporarily so we can build the child clone stack frame
- * without deadlocking.
- */
- if (__copy_user(sp, src, size))
- sp = NULL;
- else if (put_user(fp, &sp->fp))
- sp = NULL;
-
- return sp;
-}
-
-asmlinkage int sparc_do_fork(unsigned long clone_flags,
- unsigned long stack_start,
- struct pt_regs *regs,
- unsigned long stack_size)
-{
- unsigned long parent_tid_ptr, child_tid_ptr;
- unsigned long orig_i1 = regs->u_regs[UREG_I1];
- long ret;
-
- parent_tid_ptr = regs->u_regs[UREG_I2];
- child_tid_ptr = regs->u_regs[UREG_I4];
-
- ret = do_fork(clone_flags, stack_start,
- regs, stack_size,
- (int __user *) parent_tid_ptr,
- (int __user *) child_tid_ptr);
-
- /* If we get an error and potentially restart the system
- * call, we're screwed because copy_thread() clobbered
- * the parent's %o1. So detect that case and restore it
- * here.
- */
- if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
- regs->u_regs[UREG_I1] = orig_i1;
-
- return ret;
-}
-
-/* Copy a Sparc thread. The fork() return value conventions
- * under SunOS are nothing short of bletcherous:
- * Parent --> %o0 == childs pid, %o1 == 0
- * Child --> %o0 == parents pid, %o1 == 1
- *
- * NOTE: We have a separate fork kpsr/kwim because
- * the parent could change these values between
- * sys_fork invocation and when we reach here
- * if the parent should sleep while trying to
- * allocate the task_struct and kernel stack in
- * do_fork().
- * XXX See comment above sys_vfork in sparc64. todo.
- */
-extern void ret_from_fork(void);
-
-int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
- unsigned long unused,
- struct task_struct *p, struct pt_regs *regs)
-{
- struct thread_info *ti = task_thread_info(p);
- struct pt_regs *childregs;
- char *new_stack;
-
-#ifndef CONFIG_SMP
- if(last_task_used_math == current) {
-#else
- if (test_thread_flag(TIF_USEDFPU)) {
-#endif
- put_psr(get_psr() | PSR_EF);
- fpsave(&p->thread.float_regs[0], &p->thread.fsr,
- &p->thread.fpqueue[0], &p->thread.fpqdepth);
-#ifdef CONFIG_SMP
- clear_thread_flag(TIF_USEDFPU);
-#endif
- }
-
- /*
- * p->thread_info new_stack childregs
- * ! ! ! {if(PSR_PS) }
- * V V (stk.fr.) V (pt_regs) { (stk.fr.) }
- * +----- - - - - - ------+===========+============={+==========}+
- */
- new_stack = task_stack_page(p) + THREAD_SIZE;
- if (regs->psr & PSR_PS)
- new_stack -= STACKFRAME_SZ;
- new_stack -= STACKFRAME_SZ + TRACEREG_SZ;
- memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
- childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ);
-
- /*
- * A new process must start with interrupts closed in 2.5,
- * because this is how Mingo's scheduler works (see schedule_tail
- * and finish_arch_switch). If we do not do it, a timer interrupt hits
- * before we unlock, attempts to re-take the rq->lock, and then we die.
- * Thus, kpsr|=PSR_PIL.
- */
- ti->ksp = (unsigned long) new_stack;
- ti->kpc = (((unsigned long) ret_from_fork) - 0x8);
- ti->kpsr = current->thread.fork_kpsr | PSR_PIL;
- ti->kwim = current->thread.fork_kwim;
-
- if(regs->psr & PSR_PS) {
- extern struct pt_regs fake_swapper_regs;
-
- p->thread.kregs = &fake_swapper_regs;
- new_stack += STACKFRAME_SZ + TRACEREG_SZ;
- childregs->u_regs[UREG_FP] = (unsigned long) new_stack;
- p->thread.flags |= SPARC_FLAG_KTHREAD;
- p->thread.current_ds = KERNEL_DS;
- memcpy(new_stack, (void *)regs->u_regs[UREG_FP], STACKFRAME_SZ);
- childregs->u_regs[UREG_G6] = (unsigned long) ti;
- } else {
- p->thread.kregs = childregs;
- childregs->u_regs[UREG_FP] = sp;
- p->thread.flags &= ~SPARC_FLAG_KTHREAD;
- p->thread.current_ds = USER_DS;
-
- if (sp != regs->u_regs[UREG_FP]) {
- struct sparc_stackf __user *childstack;
- struct sparc_stackf __user *parentstack;
-
- /*
- * This is a clone() call with supplied user stack.
- * Set some valid stack frames to give to the child.
- */
- childstack = (struct sparc_stackf __user *)
- (sp & ~0x7UL);
- parentstack = (struct sparc_stackf __user *)
- regs->u_regs[UREG_FP];
-
-#if 0
- printk("clone: parent stack:\n");
- show_stackframe(parentstack);
-#endif
-
- childstack = clone_stackframe(childstack, parentstack);
- if (!childstack)
- return -EFAULT;
-
-#if 0
- printk("clone: child stack:\n");
- show_stackframe(childstack);
-#endif
-
- childregs->u_regs[UREG_FP] = (unsigned long)childstack;
- }
- }
-
-#ifdef CONFIG_SMP
- /* FPU must be disabled on SMP. */
- childregs->psr &= ~PSR_EF;
-#endif
-
- /* Set the return value for the child. */
- childregs->u_regs[UREG_I0] = current->pid;
- childregs->u_regs[UREG_I1] = 1;
-
- /* Set the return value for the parent. */
- regs->u_regs[UREG_I1] = 0;
-
- if (clone_flags & CLONE_SETTLS)
- childregs->u_regs[UREG_G7] = regs->u_regs[UREG_I3];
-
- return 0;
-}
-
-/*
- * fill in the fpu structure for a core dump.
- */
-int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
-{
- if (used_math()) {
- memset(fpregs, 0, sizeof(*fpregs));
- fpregs->pr_q_entrysize = 8;
- return 1;
- }
-#ifdef CONFIG_SMP
- if (test_thread_flag(TIF_USEDFPU)) {
- put_psr(get_psr() | PSR_EF);
- fpsave(&current->thread.float_regs[0], &current->thread.fsr,
- &current->thread.fpqueue[0], &current->thread.fpqdepth);
- if (regs != NULL) {
- regs->psr &= ~(PSR_EF);
- clear_thread_flag(TIF_USEDFPU);
- }
- }
-#else
- if (current == last_task_used_math) {
- put_psr(get_psr() | PSR_EF);
- fpsave(&current->thread.float_regs[0], &current->thread.fsr,
- &current->thread.fpqueue[0], &current->thread.fpqdepth);
- if (regs != NULL) {
- regs->psr &= ~(PSR_EF);
- last_task_used_math = NULL;
- }
- }
-#endif
- memcpy(&fpregs->pr_fr.pr_regs[0],
- &current->thread.float_regs[0],
- (sizeof(unsigned long) * 32));
- fpregs->pr_fsr = current->thread.fsr;
- fpregs->pr_qcnt = current->thread.fpqdepth;
- fpregs->pr_q_entrysize = 8;
- fpregs->pr_en = 1;
- if(fpregs->pr_qcnt != 0) {
- memcpy(&fpregs->pr_q[0],
- &current->thread.fpqueue[0],
- sizeof(struct fpq) * fpregs->pr_qcnt);
- }
- /* Zero out the rest. */
- memset(&fpregs->pr_q[fpregs->pr_qcnt], 0,
- sizeof(struct fpq) * (32 - fpregs->pr_qcnt));
- return 1;
-}
-
-/*
- * sparc_execve() executes a new program after the asm stub has set
- * things up for us. This should basically do what I want it to.
- */
-asmlinkage int sparc_execve(struct pt_regs *regs)
-{
- int error, base = 0;
- char *filename;
-
- /* Check for indirect call. */
- if(regs->u_regs[UREG_G1] == 0)
- base = 1;
-
- filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
- error = PTR_ERR(filename);
- if(IS_ERR(filename))
- goto out;
- error = do_execve(filename,
- (char __user * __user *)regs->u_regs[base + UREG_I1],
- (char __user * __user *)regs->u_regs[base + UREG_I2],
- regs);
- putname(filename);
-out:
- return error;
-}
-
-/*
- * This is the mechanism for creating a new kernel thread.
- *
- * NOTE! Only a kernel-only process(ie the swapper or direct descendants
- * who haven't done an "execve()") should use this: it will work within
- * a system call from a "real" process, but the process memory space will
- * not be freed until both the parent and the child have exited.
- */
-pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
-{
- long retval;
-
- __asm__ __volatile__("mov %4, %%g2\n\t" /* Set aside fn ptr... */
- "mov %5, %%g3\n\t" /* and arg. */
- "mov %1, %%g1\n\t"
- "mov %2, %%o0\n\t" /* Clone flags. */
- "mov 0, %%o1\n\t" /* usp arg == 0 */
- "t 0x10\n\t" /* Linux/Sparc clone(). */
- "cmp %%o1, 0\n\t"
- "be 1f\n\t" /* The parent, just return. */
- " nop\n\t" /* Delay slot. */
- "jmpl %%g2, %%o7\n\t" /* Call the function. */
- " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
- "mov %3, %%g1\n\t"
- "t 0x10\n\t" /* Linux/Sparc exit(). */
- /* Notreached by child. */
- "1: mov %%o0, %0\n\t" :
- "=r" (retval) :
- "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
- "i" (__NR_exit), "r" (fn), "r" (arg) :
- "g1", "g2", "g3", "o0", "o1", "memory", "cc");
- return retval;
-}
-
-unsigned long get_wchan(struct task_struct *task)
-{
- unsigned long pc, fp, bias = 0;
- unsigned long task_base = (unsigned long) task;
- unsigned long ret = 0;
- struct reg_window *rw;
- int count = 0;
-
- if (!task || task == current ||
- task->state == TASK_RUNNING)
- goto out;
-
- fp = task_thread_info(task)->ksp + bias;
- do {
- /* Bogus frame pointer? */
- if (fp < (task_base + sizeof(struct thread_info)) ||
- fp >= (task_base + (2 * PAGE_SIZE)))
- break;
- rw = (struct reg_window *) fp;
- pc = rw->ins[7];
- if (!in_sched_functions(pc)) {
- ret = pc;
- goto out;
- }
- fp = rw->ins[6] + bias;
- } while (++count < 16);
-
-out:
- return ret;
-}
-
diff --git a/arch/sparc/kernel/process_32.c b/arch/sparc/kernel/process_32.c
new file mode 100644
index 000000000000..69d9315f4a93
--- /dev/null
+++ b/arch/sparc/kernel/process_32.c
@@ -0,0 +1,707 @@
+/* linux/arch/sparc/kernel/process.c
+ *
+ * Copyright (C) 1995, 2008 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+#include <stdarg.h>
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/smp.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+
+#include <asm/auxio.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/delay.h>
+#include <asm/processor.h>
+#include <asm/psr.h>
+#include <asm/elf.h>
+#include <asm/prom.h>
+#include <asm/unistd.h>
+
+/*
+ * Power management idle function
+ * Set in pm platform drivers (apc.c and pmc.c)
+ */
+void (*pm_idle)(void);
+
+/*
+ * Power-off handler instantiation for pm.h compliance
+ * This is done via auxio, but could be used as a fallback
+ * handler when auxio is not present-- unused for now...
+ */
+void (*pm_power_off)(void) = machine_power_off;
+EXPORT_SYMBOL(pm_power_off);
+
+/*
+ * sysctl - toggle power-off restriction for serial console
+ * systems in machine_power_off()
+ */
+int scons_pwroff = 1;
+
+extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *);
+
+struct task_struct *last_task_used_math = NULL;
+struct thread_info *current_set[NR_CPUS];
+
+#ifndef CONFIG_SMP
+
+#define SUN4C_FAULT_HIGH 100
+
+/*
+ * the idle loop on a Sparc... ;)
+ */
+void cpu_idle(void)
+{
+ /* endless idle loop with no priority at all */
+ for (;;) {
+ if (ARCH_SUN4C) {
+ static int count = HZ;
+ static unsigned long last_jiffies;
+ static unsigned long last_faults;
+ static unsigned long fps;
+ unsigned long now;
+ unsigned long faults;
+
+ extern unsigned long sun4c_kernel_faults;
+ extern void sun4c_grow_kernel_ring(void);
+
+ local_irq_disable();
+ now = jiffies;
+ count -= (now - last_jiffies);
+ last_jiffies = now;
+ if (count < 0) {
+ count += HZ;
+ faults = sun4c_kernel_faults;
+ fps = (fps + (faults - last_faults)) >> 1;
+ last_faults = faults;
+#if 0
+ printk("kernel faults / second = %ld\n", fps);
+#endif
+ if (fps >= SUN4C_FAULT_HIGH) {
+ sun4c_grow_kernel_ring();
+ }
+ }
+ local_irq_enable();
+ }
+
+ if (pm_idle) {
+ while (!need_resched())
+ (*pm_idle)();
+ } else {
+ while (!need_resched())
+ cpu_relax();
+ }
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ check_pgt_cache();
+ }
+}
+
+#else
+
+/* This is being executed in task 0 'user space'. */
+void cpu_idle(void)
+{
+ set_thread_flag(TIF_POLLING_NRFLAG);
+ /* endless idle loop with no priority at all */
+ while(1) {
+ while (!need_resched())
+ cpu_relax();
+ preempt_enable_no_resched();
+ schedule();
+ preempt_disable();
+ check_pgt_cache();
+ }
+}
+
+#endif
+
+/* XXX cli/sti -> local_irq_xxx here, check this works once SMP is fixed. */
+void machine_halt(void)
+{
+ local_irq_enable();
+ mdelay(8);
+ local_irq_disable();
+ prom_halt();
+ panic("Halt failed!");
+}
+
+void machine_restart(char * cmd)
+{
+ char *p;
+
+ local_irq_enable();
+ mdelay(8);
+ local_irq_disable();
+
+ p = strchr (reboot_command, '\n');
+ if (p) *p = 0;
+ if (cmd)
+ prom_reboot(cmd);
+ if (*reboot_command)
+ prom_reboot(reboot_command);
+ prom_feval ("reset");
+ panic("Reboot failed!");
+}
+
+void machine_power_off(void)
+{
+ if (auxio_power_register &&
+ (strcmp(of_console_device->type, "serial") || scons_pwroff))
+ *auxio_power_register |= AUXIO_POWER_OFF;
+ machine_halt();
+}
+
+#if 0
+
+static DEFINE_SPINLOCK(sparc_backtrace_lock);
+
+void __show_backtrace(unsigned long fp)
+{
+ struct reg_window *rw;
+ unsigned long flags;
+ int cpu = smp_processor_id();
+
+ spin_lock_irqsave(&sparc_backtrace_lock, flags);
+
+ rw = (struct reg_window *)fp;
+ while(rw && (((unsigned long) rw) >= PAGE_OFFSET) &&
+ !(((unsigned long) rw) & 0x7)) {
+ printk("CPU[%d]: ARGS[%08lx,%08lx,%08lx,%08lx,%08lx,%08lx] "
+ "FP[%08lx] CALLER[%08lx]: ", cpu,
+ rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
+ rw->ins[4], rw->ins[5],
+ rw->ins[6],
+ rw->ins[7]);
+ printk("%pS\n", (void *) rw->ins[7]);
+ rw = (struct reg_window *) rw->ins[6];
+ }
+ spin_unlock_irqrestore(&sparc_backtrace_lock, flags);
+}
+
+#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
+#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
+#define __GET_FP(fp) __asm__ __volatile__("mov %%i6, %0" : "=r" (fp))
+
+void show_backtrace(void)
+{
+ unsigned long fp;
+
+ __SAVE; __SAVE; __SAVE; __SAVE;
+ __SAVE; __SAVE; __SAVE; __SAVE;
+ __RESTORE; __RESTORE; __RESTORE; __RESTORE;
+ __RESTORE; __RESTORE; __RESTORE; __RESTORE;
+
+ __GET_FP(fp);
+
+ __show_backtrace(fp);
+}
+
+#ifdef CONFIG_SMP
+void smp_show_backtrace_all_cpus(void)
+{
+ xc0((smpfunc_t) show_backtrace);
+ show_backtrace();
+}
+#endif
+
+void show_stackframe(struct sparc_stackf *sf)
+{
+ unsigned long size;
+ unsigned long *stk;
+ int i;
+
+ printk("l0: %08lx l1: %08lx l2: %08lx l3: %08lx "
+ "l4: %08lx l5: %08lx l6: %08lx l7: %08lx\n",
+ sf->locals[0], sf->locals[1], sf->locals[2], sf->locals[3],
+ sf->locals[4], sf->locals[5], sf->locals[6], sf->locals[7]);
+ printk("i0: %08lx i1: %08lx i2: %08lx i3: %08lx "
+ "i4: %08lx i5: %08lx fp: %08lx i7: %08lx\n",
+ sf->ins[0], sf->ins[1], sf->ins[2], sf->ins[3],
+ sf->ins[4], sf->ins[5], (unsigned long)sf->fp, sf->callers_pc);
+ printk("sp: %08lx x0: %08lx x1: %08lx x2: %08lx "
+ "x3: %08lx x4: %08lx x5: %08lx xx: %08lx\n",
+ (unsigned long)sf->structptr, sf->xargs[0], sf->xargs[1],
+ sf->xargs[2], sf->xargs[3], sf->xargs[4], sf->xargs[5],
+ sf->xxargs[0]);
+ size = ((unsigned long)sf->fp) - ((unsigned long)sf);
+ size -= STACKFRAME_SZ;
+ stk = (unsigned long *)((unsigned long)sf + STACKFRAME_SZ);
+ i = 0;
+ do {
+ printk("s%d: %08lx\n", i++, *stk++);
+ } while ((size -= sizeof(unsigned long)));
+}
+#endif
+
+void show_regs(struct pt_regs *r)
+{
+ struct reg_window *rw = (struct reg_window *) r->u_regs[14];
+
+ printk("PSR: %08lx PC: %08lx NPC: %08lx Y: %08lx %s\n",
+ r->psr, r->pc, r->npc, r->y, print_tainted());
+ printk("PC: <%pS>\n", (void *) r->pc);
+ printk("%%G: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ r->u_regs[0], r->u_regs[1], r->u_regs[2], r->u_regs[3],
+ r->u_regs[4], r->u_regs[5], r->u_regs[6], r->u_regs[7]);
+ printk("%%O: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ r->u_regs[8], r->u_regs[9], r->u_regs[10], r->u_regs[11],
+ r->u_regs[12], r->u_regs[13], r->u_regs[14], r->u_regs[15]);
+ printk("RPC: <%pS>\n", (void *) r->u_regs[15]);
+
+ printk("%%L: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
+ rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
+ printk("%%I: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+ rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
+ rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
+}
+
+/*
+ * The show_stack is an external API which we do not use ourselves.
+ * The oops is printed in die_if_kernel.
+ */
+void show_stack(struct task_struct *tsk, unsigned long *_ksp)
+{
+ unsigned long pc, fp;
+ unsigned long task_base;
+ struct reg_window *rw;
+ int count = 0;
+
+ if (tsk != NULL)
+ task_base = (unsigned long) task_stack_page(tsk);
+ else
+ task_base = (unsigned long) current_thread_info();
+
+ fp = (unsigned long) _ksp;
+ do {
+ /* Bogus frame pointer? */
+ if (fp < (task_base + sizeof(struct thread_info)) ||
+ fp >= (task_base + (PAGE_SIZE << 1)))
+ break;
+ rw = (struct reg_window *) fp;
+ pc = rw->ins[7];
+ printk("[%08lx : ", pc);
+ printk("%pS ] ", (void *) pc);
+ fp = rw->ins[6];
+ } while (++count < 16);
+ printk("\n");
+}
+
+void dump_stack(void)
+{
+ unsigned long *ksp;
+
+ __asm__ __volatile__("mov %%fp, %0"
+ : "=r" (ksp));
+ show_stack(current, ksp);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+/*
+ * Note: sparc64 has a pretty intricated thread_saved_pc, check it out.
+ */
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+ return task_thread_info(tsk)->kpc;
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+#ifndef CONFIG_SMP
+ if(last_task_used_math == current) {
+#else
+ if (test_thread_flag(TIF_USEDFPU)) {
+#endif
+ /* Keep process from leaving FPU in a bogon state. */
+ put_psr(get_psr() | PSR_EF);
+ fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+ &current->thread.fpqueue[0], &current->thread.fpqdepth);
+#ifndef CONFIG_SMP
+ last_task_used_math = NULL;
+#else
+ clear_thread_flag(TIF_USEDFPU);
+#endif
+ }
+}
+
+void flush_thread(void)
+{
+ current_thread_info()->w_saved = 0;
+
+#ifndef CONFIG_SMP
+ if(last_task_used_math == current) {
+#else
+ if (test_thread_flag(TIF_USEDFPU)) {
+#endif
+ /* Clean the fpu. */
+ put_psr(get_psr() | PSR_EF);
+ fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+ &current->thread.fpqueue[0], &current->thread.fpqdepth);
+#ifndef CONFIG_SMP
+ last_task_used_math = NULL;
+#else
+ clear_thread_flag(TIF_USEDFPU);
+#endif
+ }
+
+ /* Now, this task is no longer a kernel thread. */
+ current->thread.current_ds = USER_DS;
+ if (current->thread.flags & SPARC_FLAG_KTHREAD) {
+ current->thread.flags &= ~SPARC_FLAG_KTHREAD;
+
+ /* We must fixup kregs as well. */
+ /* XXX This was not fixed for ti for a while, worked. Unused? */
+ current->thread.kregs = (struct pt_regs *)
+ (task_stack_page(current) + (THREAD_SIZE - TRACEREG_SZ));
+ }
+}
+
+static inline struct sparc_stackf __user *
+clone_stackframe(struct sparc_stackf __user *dst,
+ struct sparc_stackf __user *src)
+{
+ unsigned long size, fp;
+ struct sparc_stackf *tmp;
+ struct sparc_stackf __user *sp;
+
+ if (get_user(tmp, &src->fp))
+ return NULL;
+
+ fp = (unsigned long) tmp;
+ size = (fp - ((unsigned long) src));
+ fp = (unsigned long) dst;
+ sp = (struct sparc_stackf __user *)(fp - size);
+
+ /* do_fork() grabs the parent semaphore, we must release it
+ * temporarily so we can build the child clone stack frame
+ * without deadlocking.
+ */
+ if (__copy_user(sp, src, size))
+ sp = NULL;
+ else if (put_user(fp, &sp->fp))
+ sp = NULL;
+
+ return sp;
+}
+
+asmlinkage int sparc_do_fork(unsigned long clone_flags,
+ unsigned long stack_start,
+ struct pt_regs *regs,
+ unsigned long stack_size)
+{
+ unsigned long parent_tid_ptr, child_tid_ptr;
+ unsigned long orig_i1 = regs->u_regs[UREG_I1];
+ long ret;
+
+ parent_tid_ptr = regs->u_regs[UREG_I2];
+ child_tid_ptr = regs->u_regs[UREG_I4];
+
+ ret = do_fork(clone_flags, stack_start,
+ regs, stack_size,
+ (int __user *) parent_tid_ptr,
+ (int __user *) child_tid_ptr);
+
+ /* If we get an error and potentially restart the system
+ * call, we're screwed because copy_thread() clobbered
+ * the parent's %o1. So detect that case and restore it
+ * here.
+ */
+ if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK)
+ regs->u_regs[UREG_I1] = orig_i1;
+
+ return ret;
+}
+
+/* Copy a Sparc thread. The fork() return value conventions
+ * under SunOS are nothing short of bletcherous:
+ * Parent --> %o0 == childs pid, %o1 == 0
+ * Child --> %o0 == parents pid, %o1 == 1
+ *
+ * NOTE: We have a separate fork kpsr/kwim because
+ * the parent could change these values between
+ * sys_fork invocation and when we reach here
+ * if the parent should sleep while trying to
+ * allocate the task_struct and kernel stack in
+ * do_fork().
+ * XXX See comment above sys_vfork in sparc64. todo.
+ */
+extern void ret_from_fork(void);
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct thread_info *ti = task_thread_info(p);
+ struct pt_regs *childregs;
+ char *new_stack;
+
+#ifndef CONFIG_SMP
+ if(last_task_used_math == current) {
+#else
+ if (test_thread_flag(TIF_USEDFPU)) {
+#endif
+ put_psr(get_psr() | PSR_EF);
+ fpsave(&p->thread.float_regs[0], &p->thread.fsr,
+ &p->thread.fpqueue[0], &p->thread.fpqdepth);
+#ifdef CONFIG_SMP
+ clear_thread_flag(TIF_USEDFPU);
+#endif
+ }
+
+ /*
+ * p->thread_info new_stack childregs
+ * ! ! ! {if(PSR_PS) }
+ * V V (stk.fr.) V (pt_regs) { (stk.fr.) }
+ * +----- - - - - - ------+===========+============={+==========}+
+ */
+ new_stack = task_stack_page(p) + THREAD_SIZE;
+ if (regs->psr & PSR_PS)
+ new_stack -= STACKFRAME_SZ;
+ new_stack -= STACKFRAME_SZ + TRACEREG_SZ;
+ memcpy(new_stack, (char *)regs - STACKFRAME_SZ, STACKFRAME_SZ + TRACEREG_SZ);
+ childregs = (struct pt_regs *) (new_stack + STACKFRAME_SZ);
+
+ /*
+ * A new process must start with interrupts closed in 2.5,
+ * because this is how Mingo's scheduler works (see schedule_tail
+ * and finish_arch_switch). If we do not do it, a timer interrupt hits
+ * before we unlock, attempts to re-take the rq->lock, and then we die.
+ * Thus, kpsr|=PSR_PIL.
+ */
+ ti->ksp = (unsigned long) new_stack;
+ ti->kpc = (((unsigned long) ret_from_fork) - 0x8);
+ ti->kpsr = current->thread.fork_kpsr | PSR_PIL;
+ ti->kwim = current->thread.fork_kwim;
+
+ if(regs->psr & PSR_PS) {
+ extern struct pt_regs fake_swapper_regs;
+
+ p->thread.kregs = &fake_swapper_regs;
+ new_stack += STACKFRAME_SZ + TRACEREG_SZ;
+ childregs->u_regs[UREG_FP] = (unsigned long) new_stack;
+ p->thread.flags |= SPARC_FLAG_KTHREAD;
+ p->thread.current_ds = KERNEL_DS;
+ memcpy(new_stack, (void *)regs->u_regs[UREG_FP], STACKFRAME_SZ);
+ childregs->u_regs[UREG_G6] = (unsigned long) ti;
+ } else {
+ p->thread.kregs = childregs;
+ childregs->u_regs[UREG_FP] = sp;
+ p->thread.flags &= ~SPARC_FLAG_KTHREAD;
+ p->thread.current_ds = USER_DS;
+
+ if (sp != regs->u_regs[UREG_FP]) {
+ struct sparc_stackf __user *childstack;
+ struct sparc_stackf __user *parentstack;
+
+ /*
+ * This is a clone() call with supplied user stack.
+ * Set some valid stack frames to give to the child.
+ */
+ childstack = (struct sparc_stackf __user *)
+ (sp & ~0x7UL);
+ parentstack = (struct sparc_stackf __user *)
+ regs->u_regs[UREG_FP];
+
+#if 0
+ printk("clone: parent stack:\n");
+ show_stackframe(parentstack);
+#endif
+
+ childstack = clone_stackframe(childstack, parentstack);
+ if (!childstack)
+ return -EFAULT;
+
+#if 0
+ printk("clone: child stack:\n");
+ show_stackframe(childstack);
+#endif
+
+ childregs->u_regs[UREG_FP] = (unsigned long)childstack;
+ }
+ }
+
+#ifdef CONFIG_SMP
+ /* FPU must be disabled on SMP. */
+ childregs->psr &= ~PSR_EF;
+#endif
+
+ /* Set the return value for the child. */
+ childregs->u_regs[UREG_I0] = current->pid;
+ childregs->u_regs[UREG_I1] = 1;
+
+ /* Set the return value for the parent. */
+ regs->u_regs[UREG_I1] = 0;
+
+ if (clone_flags & CLONE_SETTLS)
+ childregs->u_regs[UREG_G7] = regs->u_regs[UREG_I3];
+
+ return 0;
+}
+
+/*
+ * fill in the fpu structure for a core dump.
+ */
+int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs)
+{
+ if (used_math()) {
+ memset(fpregs, 0, sizeof(*fpregs));
+ fpregs->pr_q_entrysize = 8;
+ return 1;
+ }
+#ifdef CONFIG_SMP
+ if (test_thread_flag(TIF_USEDFPU)) {
+ put_psr(get_psr() | PSR_EF);
+ fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+ &current->thread.fpqueue[0], &current->thread.fpqdepth);
+ if (regs != NULL) {
+ regs->psr &= ~(PSR_EF);
+ clear_thread_flag(TIF_USEDFPU);
+ }
+ }
+#else
+ if (current == last_task_used_math) {
+ put_psr(get_psr() | PSR_EF);
+ fpsave(&current->thread.float_regs[0], &current->thread.fsr,
+ &current->thread.fpqueue[0], &current->thread.fpqdepth);
+ if (regs != NULL) {
+ regs->psr &= ~(PSR_EF);
+ last_task_used_math = NULL;
+ }
+ }
+#endif
+ memcpy(&fpregs->pr_fr.pr_regs[0],
+ &current->thread.float_regs[0],
+ (sizeof(unsigned long) * 32));
+ fpregs->pr_fsr = current->thread.fsr;
+ fpregs->pr_qcnt = current->thread.fpqdepth;
+ fpregs->pr_q_entrysize = 8;
+ fpregs->pr_en = 1;
+ if(fpregs->pr_qcnt != 0) {
+ memcpy(&fpregs->pr_q[0],
+ &current->thread.fpqueue[0],
+ sizeof(struct fpq) * fpregs->pr_qcnt);
+ }
+ /* Zero out the rest. */
+ memset(&fpregs->pr_q[fpregs->pr_qcnt], 0,
+ sizeof(struct fpq) * (32 - fpregs->pr_qcnt));
+ return 1;
+}
+
+/*
+ * sparc_execve() executes a new program after the asm stub has set
+ * things up for us. This should basically do what I want it to.
+ */
+asmlinkage int sparc_execve(struct pt_regs *regs)
+{
+ int error, base = 0;
+ char *filename;
+
+ /* Check for indirect call. */
+ if(regs->u_regs[UREG_G1] == 0)
+ base = 1;
+
+ filename = getname((char __user *)regs->u_regs[base + UREG_I0]);
+ error = PTR_ERR(filename);
+ if(IS_ERR(filename))
+ goto out;
+ error = do_execve(filename,
+ (char __user * __user *)regs->u_regs[base + UREG_I1],
+ (char __user * __user *)regs->u_regs[base + UREG_I2],
+ regs);
+ putname(filename);
+out:
+ return error;
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be freed until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ long retval;
+
+ __asm__ __volatile__("mov %4, %%g2\n\t" /* Set aside fn ptr... */
+ "mov %5, %%g3\n\t" /* and arg. */
+ "mov %1, %%g1\n\t"
+ "mov %2, %%o0\n\t" /* Clone flags. */
+ "mov 0, %%o1\n\t" /* usp arg == 0 */
+ "t 0x10\n\t" /* Linux/Sparc clone(). */
+ "cmp %%o1, 0\n\t"
+ "be 1f\n\t" /* The parent, just return. */
+ " nop\n\t" /* Delay slot. */
+ "jmpl %%g2, %%o7\n\t" /* Call the function. */
+ " mov %%g3, %%o0\n\t" /* Get back the arg in delay. */
+ "mov %3, %%g1\n\t"
+ "t 0x10\n\t" /* Linux/Sparc exit(). */
+ /* Notreached by child. */
+ "1: mov %%o0, %0\n\t" :
+ "=r" (retval) :
+ "i" (__NR_clone), "r" (flags | CLONE_VM | CLONE_UNTRACED),
+ "i" (__NR_exit), "r" (fn), "r" (arg) :
+ "g1", "g2", "g3", "o0", "o1", "memory", "cc");
+ return retval;
+}
+
+unsigned long get_wchan(struct task_struct *task)
+{
+ unsigned long pc, fp, bias = 0;
+ unsigned long task_base = (unsigned long) task;
+ unsigned long ret = 0;
+ struct reg_window *rw;
+ int count = 0;
+
+ if (!task || task == current ||
+ task->state == TASK_RUNNING)
+ goto out;
+
+ fp = task_thread_info(task)->ksp + bias;
+ do {
+ /* Bogus frame pointer? */
+ if (fp < (task_base + sizeof(struct thread_info)) ||
+ fp >= (task_base + (2 * PAGE_SIZE)))
+ break;
+ rw = (struct reg_window *) fp;
+ pc = rw->ins[7];
+ if (!in_sched_functions(pc)) {
+ ret = pc;
+ goto out;
+ }
+ fp = rw->ins[6] + bias;
+ } while (++count < 16);
+
+out:
+ return ret;
+}
+
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc/kernel/process_64.c
index d5e2acef9877..d5e2acef9877 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc/kernel/process_64.c
diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c
deleted file mode 100644
index eee5efcfe50e..000000000000
--- a/arch/sparc/kernel/prom.c
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Procedures for creating, accessing and interpreting the device tree.
- *
- * Paul Mackerras August 1996.
- * Copyright (C) 1996-2005 Paul Mackerras.
- *
- * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
- * {engebret|bergner}@us.ibm.com
- *
- * Adapted for sparc32 by David S. Miller davem@davemloft.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.
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/bootmem.h>
-#include <linux/module.h>
-
-#include <asm/prom.h>
-#include <asm/oplib.h>
-
-extern struct device_node *allnodes; /* temporary while merging */
-
-extern rwlock_t devtree_lock; /* temporary while merging */
-
-struct device_node *of_find_node_by_phandle(phandle handle)
-{
- struct device_node *np;
-
- for (np = allnodes; np != 0; np = np->allnext)
- if (np->node == handle)
- break;
-
- return np;
-}
-EXPORT_SYMBOL(of_find_node_by_phandle);
-
-int of_getintprop_default(struct device_node *np, const char *name, int def)
-{
- struct property *prop;
- int len;
-
- prop = of_find_property(np, name, &len);
- if (!prop || len != 4)
- return def;
-
- return *(int *) prop->value;
-}
-EXPORT_SYMBOL(of_getintprop_default);
-
-DEFINE_MUTEX(of_set_property_mutex);
-EXPORT_SYMBOL(of_set_property_mutex);
-
-int of_set_property(struct device_node *dp, const char *name, void *val, int len)
-{
- struct property **prevp;
- void *new_val;
- int err;
-
- new_val = kmalloc(len, GFP_KERNEL);
- if (!new_val)
- return -ENOMEM;
-
- memcpy(new_val, val, len);
-
- err = -ENODEV;
-
- write_lock(&devtree_lock);
- prevp = &dp->properties;
- while (*prevp) {
- struct property *prop = *prevp;
-
- if (!strcasecmp(prop->name, name)) {
- void *old_val = prop->value;
- int ret;
-
- mutex_lock(&of_set_property_mutex);
- ret = prom_setprop(dp->node, (char *) name, val, len);
- mutex_unlock(&of_set_property_mutex);
-
- err = -EINVAL;
- if (ret >= 0) {
- prop->value = new_val;
- prop->length = len;
-
- if (OF_IS_DYNAMIC(prop))
- kfree(old_val);
-
- OF_MARK_DYNAMIC(prop);
-
- err = 0;
- }
- break;
- }
- prevp = &(*prevp)->next;
- }
- write_unlock(&devtree_lock);
-
- /* XXX Upate procfs if necessary... */
-
- return err;
-}
-EXPORT_SYMBOL(of_set_property);
-
-int of_find_in_proplist(const char *list, const char *match, int len)
-{
- while (len > 0) {
- int l;
-
- if (!strcmp(list, match))
- return 1;
- l = strlen(list) + 1;
- list += l;
- len -= l;
- }
- return 0;
-}
-EXPORT_SYMBOL(of_find_in_proplist);
-
-static unsigned int prom_early_allocated;
-
-static void * __init prom_early_alloc(unsigned long size)
-{
- void *ret;
-
- ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
- if (ret != NULL)
- memset(ret, 0, size);
-
- prom_early_allocated += size;
-
- return ret;
-}
-
-static int is_root_node(const struct device_node *dp)
-{
- if (!dp)
- return 0;
-
- return (dp->parent == NULL);
-}
-
-/* The following routines deal with the black magic of fully naming a
- * node.
- *
- * Certain well known named nodes are just the simple name string.
- *
- * Actual devices have an address specifier appended to the base name
- * string, like this "foo@addr". The "addr" can be in any number of
- * formats, and the platform plus the type of the node determine the
- * format and how it is constructed.
- *
- * For children of the ROOT node, the naming convention is fixed and
- * determined by whether this is a sun4u or sun4v system.
- *
- * For children of other nodes, it is bus type specific. So
- * we walk up the tree until we discover a "device_type" property
- * we recognize and we go from there.
- */
-static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_registers *regs;
- struct property *rprop;
-
- rprop = of_find_property(dp, "reg", NULL);
- if (!rprop)
- return;
-
- regs = rprop->value;
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- regs->which_io, regs->phys_addr);
-}
-
-/* "name@slot,offset" */
-static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- regs->which_io,
- regs->phys_addr);
-}
-
-/* "name@devnum[,func]" */
-static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_pci_registers *regs;
- struct property *prop;
- unsigned int devfn;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
- devfn = (regs->phys_hi >> 8) & 0xff;
- if (devfn & 0x07) {
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- devfn >> 3,
- devfn & 0x07);
- } else {
- sprintf(tmp_buf, "%s@%x",
- dp->name,
- devfn >> 3);
- }
-}
-
-/* "name@addrhi,addrlo" */
-static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- regs->which_io, regs->phys_addr);
-}
-
-static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct device_node *parent = dp->parent;
-
- if (parent != NULL) {
- if (!strcmp(parent->type, "pci") ||
- !strcmp(parent->type, "pciex"))
- return pci_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "sbus"))
- return sbus_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "ebus"))
- return ebus_path_component(dp, tmp_buf);
-
- /* "isa" is handled with platform naming */
- }
-
- /* Use platform naming convention. */
- return sparc32_path_component(dp, tmp_buf);
-}
-
-static char * __init build_path_component(struct device_node *dp)
-{
- char tmp_buf[64], *n;
-
- tmp_buf[0] = '\0';
- __build_path_component(dp, tmp_buf);
- if (tmp_buf[0] == '\0')
- strcpy(tmp_buf, dp->name);
-
- n = prom_early_alloc(strlen(tmp_buf) + 1);
- strcpy(n, tmp_buf);
-
- return n;
-}
-
-static char * __init build_full_name(struct device_node *dp)
-{
- int len, ourlen, plen;
- char *n;
-
- plen = strlen(dp->parent->full_name);
- ourlen = strlen(dp->path_component_name);
- len = ourlen + plen + 2;
-
- n = prom_early_alloc(len);
- strcpy(n, dp->parent->full_name);
- if (!is_root_node(dp->parent)) {
- strcpy(n + plen, "/");
- plen++;
- }
- strcpy(n + plen, dp->path_component_name);
-
- return n;
-}
-
-static unsigned int unique_id;
-
-static struct property * __init build_one_prop(phandle node, char *prev, char *special_name, void *special_val, int special_len)
-{
- static struct property *tmp = NULL;
- struct property *p;
- int len;
- const char *name;
-
- if (tmp) {
- p = tmp;
- memset(p, 0, sizeof(*p) + 32);
- tmp = NULL;
- } else {
- p = prom_early_alloc(sizeof(struct property) + 32);
- p->unique_id = unique_id++;
- }
-
- p->name = (char *) (p + 1);
- if (special_name) {
- strcpy(p->name, special_name);
- p->length = special_len;
- p->value = prom_early_alloc(special_len);
- memcpy(p->value, special_val, special_len);
- } else {
- if (prev == NULL) {
- name = prom_firstprop(node, NULL);
- } else {
- name = prom_nextprop(node, prev, NULL);
- }
- if (strlen(name) == 0) {
- tmp = p;
- return NULL;
- }
- strcpy(p->name, name);
- p->length = prom_getproplen(node, p->name);
- if (p->length <= 0) {
- p->length = 0;
- } else {
- p->value = prom_early_alloc(p->length + 1);
- len = prom_getproperty(node, p->name, p->value,
- p->length);
- if (len <= 0)
- p->length = 0;
- ((unsigned char *)p->value)[p->length] = '\0';
- }
- }
- return p;
-}
-
-static struct property * __init build_prop_list(phandle node)
-{
- struct property *head, *tail;
-
- head = tail = build_one_prop(node, NULL,
- ".node", &node, sizeof(node));
-
- tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
- tail = tail->next;
- while(tail) {
- tail->next = build_one_prop(node, tail->name,
- NULL, NULL, 0);
- tail = tail->next;
- }
-
- return head;
-}
-
-static char * __init get_one_property(phandle node, char *name)
-{
- char *buf = "<NULL>";
- int len;
-
- len = prom_getproplen(node, name);
- if (len > 0) {
- buf = prom_early_alloc(len);
- len = prom_getproperty(node, name, buf, len);
- }
-
- return buf;
-}
-
-static struct device_node * __init create_node(phandle node)
-{
- struct device_node *dp;
-
- if (!node)
- return NULL;
-
- dp = prom_early_alloc(sizeof(*dp));
- dp->unique_id = unique_id++;
-
- kref_init(&dp->kref);
-
- dp->name = get_one_property(node, "name");
- dp->type = get_one_property(node, "device_type");
- dp->node = node;
-
- /* Build interrupts later... */
-
- dp->properties = build_prop_list(node);
-
- return dp;
-}
-
-static struct device_node * __init build_tree(struct device_node *parent, phandle node, struct device_node ***nextp)
-{
- struct device_node *dp;
-
- dp = create_node(node);
- if (dp) {
- *(*nextp) = dp;
- *nextp = &dp->allnext;
-
- dp->parent = parent;
- dp->path_component_name = build_path_component(dp);
- dp->full_name = build_full_name(dp);
-
- dp->child = build_tree(dp, prom_getchild(node), nextp);
-
- dp->sibling = build_tree(parent, prom_getsibling(node), nextp);
- }
-
- return dp;
-}
-
-struct device_node *of_console_device;
-EXPORT_SYMBOL(of_console_device);
-
-char *of_console_path;
-EXPORT_SYMBOL(of_console_path);
-
-char *of_console_options;
-EXPORT_SYMBOL(of_console_options);
-
-extern void restore_current(void);
-
-static void __init of_console_init(void)
-{
- char *msg = "OF stdout device is: %s\n";
- struct device_node *dp;
- unsigned long flags;
- const char *type;
- phandle node;
- int skip, tmp, fd;
-
- of_console_path = prom_early_alloc(256);
-
- switch (prom_vers) {
- case PROM_V0:
- skip = 0;
- switch (*romvec->pv_stdout) {
- case PROMDEV_SCREEN:
- type = "display";
- break;
-
- case PROMDEV_TTYB:
- skip = 1;
- /* FALLTHRU */
-
- case PROMDEV_TTYA:
- type = "serial";
- break;
-
- default:
- prom_printf("Invalid PROM_V0 stdout value %u\n",
- *romvec->pv_stdout);
- prom_halt();
- }
-
- tmp = skip;
- for_each_node_by_type(dp, type) {
- if (!tmp--)
- break;
- }
- if (!dp) {
- prom_printf("Cannot find PROM_V0 console node.\n");
- prom_halt();
- }
- of_console_device = dp;
-
- strcpy(of_console_path, dp->full_name);
- if (!strcmp(type, "serial")) {
- strcat(of_console_path,
- (skip ? ":b" : ":a"));
- }
- break;
-
- default:
- case PROM_V2:
- case PROM_V3:
- fd = *romvec->pv_v2bootargs.fd_stdout;
-
- spin_lock_irqsave(&prom_lock, flags);
- node = (*romvec->pv_v2devops.v2_inst2pkg)(fd);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-
- if (!node) {
- prom_printf("Cannot resolve stdout node from "
- "instance %08x.\n", fd);
- prom_halt();
- }
- dp = of_find_node_by_phandle(node);
- type = of_get_property(dp, "device_type", NULL);
-
- if (!type) {
- prom_printf("Console stdout lacks "
- "device_type property.\n");
- prom_halt();
- }
-
- if (strcmp(type, "display") && strcmp(type, "serial")) {
- prom_printf("Console device_type is neither display "
- "nor serial.\n");
- prom_halt();
- }
-
- of_console_device = dp;
-
- if (prom_vers == PROM_V2) {
- strcpy(of_console_path, dp->full_name);
- switch (*romvec->pv_stdout) {
- case PROMDEV_TTYA:
- strcat(of_console_path, ":a");
- break;
- case PROMDEV_TTYB:
- strcat(of_console_path, ":b");
- break;
- }
- } else {
- const char *path;
-
- dp = of_find_node_by_path("/");
- path = of_get_property(dp, "stdout-path", NULL);
- if (!path) {
- prom_printf("No stdout-path in root node.\n");
- prom_halt();
- }
- strcpy(of_console_path, path);
- }
- break;
- }
-
- of_console_options = strrchr(of_console_path, ':');
- if (of_console_options) {
- of_console_options++;
- if (*of_console_options == '\0')
- of_console_options = NULL;
- }
-
- prom_printf(msg, of_console_path);
- printk(msg, of_console_path);
-}
-
-void __init prom_build_devicetree(void)
-{
- struct device_node **nextp;
-
- allnodes = create_node(prom_root_node);
- allnodes->path_component_name = "";
- allnodes->full_name = "/";
-
- nextp = &allnodes->allnext;
- allnodes->child = build_tree(allnodes,
- prom_getchild(allnodes->node),
- &nextp);
- of_console_init();
-
- printk("PROM: Built device tree with %u bytes of memory.\n",
- prom_early_allocated);
-}
diff --git a/arch/sparc/kernel/prom.h b/arch/sparc/kernel/prom.h
new file mode 100644
index 000000000000..bb0f0fda6cab
--- /dev/null
+++ b/arch/sparc/kernel/prom.h
@@ -0,0 +1,29 @@
+#ifndef __PROM_H
+#define __PROM_H
+
+#include <linux/spinlock.h>
+#include <asm/prom.h>
+
+extern struct device_node *allnodes; /* temporary while merging */
+extern rwlock_t devtree_lock; /* temporary while merging */
+
+extern void * prom_early_alloc(unsigned long size);
+extern void irq_trans_init(struct device_node *dp);
+
+extern unsigned int prom_unique_id;
+
+static inline int is_root_node(const struct device_node *dp)
+{
+ if (!dp)
+ return 0;
+
+ return (dp->parent == NULL);
+}
+
+extern char *build_path_component(struct device_node *dp);
+extern void of_console_init(void);
+extern void of_fill_in_cpu_data(void);
+
+extern unsigned int prom_early_allocated;
+
+#endif /* __PROM_H */
diff --git a/arch/sparc/kernel/prom_32.c b/arch/sparc/kernel/prom_32.c
new file mode 100644
index 000000000000..fe43e80772db
--- /dev/null
+++ b/arch/sparc/kernel/prom_32.c
@@ -0,0 +1,295 @@
+/*
+ * Procedures for creating, accessing and interpreting the device tree.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc32 by David S. Miller davem@davemloft.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/module.h>
+
+#include <asm/prom.h>
+#include <asm/oplib.h>
+
+#include "prom.h"
+
+void * __init prom_early_alloc(unsigned long size)
+{
+ void *ret;
+
+ ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
+ if (ret != NULL)
+ memset(ret, 0, size);
+
+ prom_early_allocated += size;
+
+ return ret;
+}
+
+/* The following routines deal with the black magic of fully naming a
+ * node.
+ *
+ * Certain well known named nodes are just the simple name string.
+ *
+ * Actual devices have an address specifier appended to the base name
+ * string, like this "foo@addr". The "addr" can be in any number of
+ * formats, and the platform plus the type of the node determine the
+ * format and how it is constructed.
+ *
+ * For children of the ROOT node, the naming convention is fixed and
+ * determined by whether this is a sun4u or sun4v system.
+ *
+ * For children of other nodes, it is bus type specific. So
+ * we walk up the tree until we discover a "device_type" property
+ * we recognize and we go from there.
+ */
+static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_registers *regs;
+ struct property *rprop;
+
+ rprop = of_find_property(dp, "reg", NULL);
+ if (!rprop)
+ return;
+
+ regs = rprop->value;
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ regs->which_io, regs->phys_addr);
+}
+
+/* "name@slot,offset" */
+static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ regs->which_io,
+ regs->phys_addr);
+}
+
+/* "name@devnum[,func]" */
+static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_pci_registers *regs;
+ struct property *prop;
+ unsigned int devfn;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ if (devfn & 0x07) {
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ devfn >> 3,
+ devfn & 0x07);
+ } else {
+ sprintf(tmp_buf, "%s@%x",
+ dp->name,
+ devfn >> 3);
+ }
+}
+
+/* "name@addrhi,addrlo" */
+static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ regs->which_io, regs->phys_addr);
+}
+
+static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct device_node *parent = dp->parent;
+
+ if (parent != NULL) {
+ if (!strcmp(parent->type, "pci") ||
+ !strcmp(parent->type, "pciex"))
+ return pci_path_component(dp, tmp_buf);
+ if (!strcmp(parent->type, "sbus"))
+ return sbus_path_component(dp, tmp_buf);
+ if (!strcmp(parent->type, "ebus"))
+ return ebus_path_component(dp, tmp_buf);
+
+ /* "isa" is handled with platform naming */
+ }
+
+ /* Use platform naming convention. */
+ return sparc32_path_component(dp, tmp_buf);
+}
+
+char * __init build_path_component(struct device_node *dp)
+{
+ char tmp_buf[64], *n;
+
+ tmp_buf[0] = '\0';
+ __build_path_component(dp, tmp_buf);
+ if (tmp_buf[0] == '\0')
+ strcpy(tmp_buf, dp->name);
+
+ n = prom_early_alloc(strlen(tmp_buf) + 1);
+ strcpy(n, tmp_buf);
+
+ return n;
+}
+
+extern void restore_current(void);
+
+void __init of_console_init(void)
+{
+ char *msg = "OF stdout device is: %s\n";
+ struct device_node *dp;
+ unsigned long flags;
+ const char *type;
+ phandle node;
+ int skip, tmp, fd;
+
+ of_console_path = prom_early_alloc(256);
+
+ switch (prom_vers) {
+ case PROM_V0:
+ skip = 0;
+ switch (*romvec->pv_stdout) {
+ case PROMDEV_SCREEN:
+ type = "display";
+ break;
+
+ case PROMDEV_TTYB:
+ skip = 1;
+ /* FALLTHRU */
+
+ case PROMDEV_TTYA:
+ type = "serial";
+ break;
+
+ default:
+ prom_printf("Invalid PROM_V0 stdout value %u\n",
+ *romvec->pv_stdout);
+ prom_halt();
+ }
+
+ tmp = skip;
+ for_each_node_by_type(dp, type) {
+ if (!tmp--)
+ break;
+ }
+ if (!dp) {
+ prom_printf("Cannot find PROM_V0 console node.\n");
+ prom_halt();
+ }
+ of_console_device = dp;
+
+ strcpy(of_console_path, dp->full_name);
+ if (!strcmp(type, "serial")) {
+ strcat(of_console_path,
+ (skip ? ":b" : ":a"));
+ }
+ break;
+
+ default:
+ case PROM_V2:
+ case PROM_V3:
+ fd = *romvec->pv_v2bootargs.fd_stdout;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ node = (*romvec->pv_v2devops.v2_inst2pkg)(fd);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+
+ if (!node) {
+ prom_printf("Cannot resolve stdout node from "
+ "instance %08x.\n", fd);
+ prom_halt();
+ }
+ dp = of_find_node_by_phandle(node);
+ type = of_get_property(dp, "device_type", NULL);
+
+ if (!type) {
+ prom_printf("Console stdout lacks "
+ "device_type property.\n");
+ prom_halt();
+ }
+
+ if (strcmp(type, "display") && strcmp(type, "serial")) {
+ prom_printf("Console device_type is neither display "
+ "nor serial.\n");
+ prom_halt();
+ }
+
+ of_console_device = dp;
+
+ if (prom_vers == PROM_V2) {
+ strcpy(of_console_path, dp->full_name);
+ switch (*romvec->pv_stdout) {
+ case PROMDEV_TTYA:
+ strcat(of_console_path, ":a");
+ break;
+ case PROMDEV_TTYB:
+ strcat(of_console_path, ":b");
+ break;
+ }
+ } else {
+ const char *path;
+
+ dp = of_find_node_by_path("/");
+ path = of_get_property(dp, "stdout-path", NULL);
+ if (!path) {
+ prom_printf("No stdout-path in root node.\n");
+ prom_halt();
+ }
+ strcpy(of_console_path, path);
+ }
+ break;
+ }
+
+ of_console_options = strrchr(of_console_path, ':');
+ if (of_console_options) {
+ of_console_options++;
+ if (*of_console_options == '\0')
+ of_console_options = NULL;
+ }
+
+ prom_printf(msg, of_console_path);
+ printk(msg, of_console_path);
+}
+
+void __init of_fill_in_cpu_data(void)
+{
+}
+
+void __init irq_trans_init(struct device_node *dp)
+{
+}
diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c
new file mode 100644
index 000000000000..edecca7b8116
--- /dev/null
+++ b/arch/sparc/kernel/prom_64.c
@@ -0,0 +1,571 @@
+/*
+ * Procedures for creating, accessing and interpreting the device tree.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc64 by David S. Miller davem@davemloft.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/lmb.h>
+#include <linux/of_device.h>
+
+#include <asm/prom.h>
+#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/asi.h>
+#include <asm/upa.h>
+#include <asm/smp.h>
+
+#include "prom.h"
+
+void * __init prom_early_alloc(unsigned long size)
+{
+ unsigned long paddr = lmb_alloc(size, SMP_CACHE_BYTES);
+ void *ret;
+
+ if (!paddr) {
+ prom_printf("prom_early_alloc(%lu) failed\n");
+ prom_halt();
+ }
+
+ ret = __va(paddr);
+ memset(ret, 0, size);
+ prom_early_allocated += size;
+
+ return ret;
+}
+
+/* The following routines deal with the black magic of fully naming a
+ * node.
+ *
+ * Certain well known named nodes are just the simple name string.
+ *
+ * Actual devices have an address specifier appended to the base name
+ * string, like this "foo@addr". The "addr" can be in any number of
+ * formats, and the platform plus the type of the node determine the
+ * format and how it is constructed.
+ *
+ * For children of the ROOT node, the naming convention is fixed and
+ * determined by whether this is a sun4u or sun4v system.
+ *
+ * For children of other nodes, it is bus type specific. So
+ * we walk up the tree until we discover a "device_type" property
+ * we recognize and we go from there.
+ *
+ * As an example, the boot device on my workstation has a full path:
+ *
+ * /pci@1e,600000/ide@d/disk@0,0:c
+ */
+static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom64_registers *regs;
+ struct property *rprop;
+ u32 high_bits, low_bits, type;
+
+ rprop = of_find_property(dp, "reg", NULL);
+ if (!rprop)
+ return;
+
+ regs = rprop->value;
+ if (!is_root_node(dp->parent)) {
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ (unsigned int) (regs->phys_addr >> 32UL),
+ (unsigned int) (regs->phys_addr & 0xffffffffUL));
+ return;
+ }
+
+ type = regs->phys_addr >> 60UL;
+ high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL;
+ low_bits = (regs->phys_addr & 0xffffffffUL);
+
+ if (type == 0 || type == 8) {
+ const char *prefix = (type == 0) ? "m" : "i";
+
+ if (low_bits)
+ sprintf(tmp_buf, "%s@%s%x,%x",
+ dp->name, prefix,
+ high_bits, low_bits);
+ else
+ sprintf(tmp_buf, "%s@%s%x",
+ dp->name,
+ prefix,
+ high_bits);
+ } else if (type == 12) {
+ sprintf(tmp_buf, "%s@%x",
+ dp->name, high_bits);
+ }
+}
+
+static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom64_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ if (!is_root_node(dp->parent)) {
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ (unsigned int) (regs->phys_addr >> 32UL),
+ (unsigned int) (regs->phys_addr & 0xffffffffUL));
+ return;
+ }
+
+ prop = of_find_property(dp, "upa-portid", NULL);
+ if (!prop)
+ prop = of_find_property(dp, "portid", NULL);
+ if (prop) {
+ unsigned long mask = 0xffffffffUL;
+
+ if (tlb_type >= cheetah)
+ mask = 0x7fffff;
+
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ *(u32 *)prop->value,
+ (unsigned int) (regs->phys_addr & mask));
+ }
+}
+
+/* "name@slot,offset" */
+static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ regs->which_io,
+ regs->phys_addr);
+}
+
+/* "name@devnum[,func]" */
+static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom_pci_registers *regs;
+ struct property *prop;
+ unsigned int devfn;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ if (devfn & 0x07) {
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ devfn >> 3,
+ devfn & 0x07);
+ } else {
+ sprintf(tmp_buf, "%s@%x",
+ dp->name,
+ devfn >> 3);
+ }
+}
+
+/* "name@UPA_PORTID,offset" */
+static void __init upa_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom64_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ prop = of_find_property(dp, "upa-portid", NULL);
+ if (!prop)
+ return;
+
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ *(u32 *) prop->value,
+ (unsigned int) (regs->phys_addr & 0xffffffffUL));
+}
+
+/* "name@reg" */
+static void __init vdev_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct property *prop;
+ u32 *regs;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ sprintf(tmp_buf, "%s@%x", dp->name, *regs);
+}
+
+/* "name@addrhi,addrlo" */
+static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct linux_prom64_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name,
+ (unsigned int) (regs->phys_addr >> 32UL),
+ (unsigned int) (regs->phys_addr & 0xffffffffUL));
+}
+
+/* "name@bus,addr" */
+static void __init i2c_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct property *prop;
+ u32 *regs;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ /* This actually isn't right... should look at the #address-cells
+ * property of the i2c bus node etc. etc.
+ */
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name, regs[0], regs[1]);
+}
+
+/* "name@reg0[,reg1]" */
+static void __init usb_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct property *prop;
+ u32 *regs;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ if (prop->length == sizeof(u32) || regs[1] == 1) {
+ sprintf(tmp_buf, "%s@%x",
+ dp->name, regs[0]);
+ } else {
+ sprintf(tmp_buf, "%s@%x,%x",
+ dp->name, regs[0], regs[1]);
+ }
+}
+
+/* "name@reg0reg1[,reg2reg3]" */
+static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct property *prop;
+ u32 *regs;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ if (regs[2] || regs[3]) {
+ sprintf(tmp_buf, "%s@%08x%08x,%04x%08x",
+ dp->name, regs[0], regs[1], regs[2], regs[3]);
+ } else {
+ sprintf(tmp_buf, "%s@%08x%08x",
+ dp->name, regs[0], regs[1]);
+ }
+}
+
+static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct device_node *parent = dp->parent;
+
+ if (parent != NULL) {
+ if (!strcmp(parent->type, "pci") ||
+ !strcmp(parent->type, "pciex")) {
+ pci_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "sbus")) {
+ sbus_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "upa")) {
+ upa_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "ebus")) {
+ ebus_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->name, "usb") ||
+ !strcmp(parent->name, "hub")) {
+ usb_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "i2c")) {
+ i2c_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "firewire")) {
+ ieee1394_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "virtual-devices")) {
+ vdev_path_component(dp, tmp_buf);
+ return;
+ }
+ /* "isa" is handled with platform naming */
+ }
+
+ /* Use platform naming convention. */
+ if (tlb_type == hypervisor) {
+ sun4v_path_component(dp, tmp_buf);
+ return;
+ } else {
+ sun4u_path_component(dp, tmp_buf);
+ }
+}
+
+char * __init build_path_component(struct device_node *dp)
+{
+ char tmp_buf[64], *n;
+
+ tmp_buf[0] = '\0';
+ __build_path_component(dp, tmp_buf);
+ if (tmp_buf[0] == '\0')
+ strcpy(tmp_buf, dp->name);
+
+ n = prom_early_alloc(strlen(tmp_buf) + 1);
+ strcpy(n, tmp_buf);
+
+ return n;
+}
+
+static const char *get_mid_prop(void)
+{
+ return (tlb_type == spitfire ? "upa-portid" : "portid");
+}
+
+struct device_node *of_find_node_by_cpuid(int cpuid)
+{
+ struct device_node *dp;
+ const char *mid_prop = get_mid_prop();
+
+ for_each_node_by_type(dp, "cpu") {
+ int id = of_getintprop_default(dp, mid_prop, -1);
+ const char *this_mid_prop = mid_prop;
+
+ if (id < 0) {
+ this_mid_prop = "cpuid";
+ id = of_getintprop_default(dp, this_mid_prop, -1);
+ }
+
+ if (id < 0) {
+ prom_printf("OF: Serious problem, cpu lacks "
+ "%s property", this_mid_prop);
+ prom_halt();
+ }
+ if (cpuid == id)
+ return dp;
+ }
+ return NULL;
+}
+
+void __init of_fill_in_cpu_data(void)
+{
+ struct device_node *dp;
+ const char *mid_prop;
+
+ if (tlb_type == hypervisor)
+ return;
+
+ mid_prop = get_mid_prop();
+ ncpus_probed = 0;
+ for_each_node_by_type(dp, "cpu") {
+ int cpuid = of_getintprop_default(dp, mid_prop, -1);
+ const char *this_mid_prop = mid_prop;
+ struct device_node *portid_parent;
+ int portid = -1;
+
+ portid_parent = NULL;
+ if (cpuid < 0) {
+ this_mid_prop = "cpuid";
+ cpuid = of_getintprop_default(dp, this_mid_prop, -1);
+ if (cpuid >= 0) {
+ int limit = 2;
+
+ portid_parent = dp;
+ while (limit--) {
+ portid_parent = portid_parent->parent;
+ if (!portid_parent)
+ break;
+ portid = of_getintprop_default(portid_parent,
+ "portid", -1);
+ if (portid >= 0)
+ break;
+ }
+ }
+ }
+
+ if (cpuid < 0) {
+ prom_printf("OF: Serious problem, cpu lacks "
+ "%s property", this_mid_prop);
+ prom_halt();
+ }
+
+ ncpus_probed++;
+
+#ifdef CONFIG_SMP
+ if (cpuid >= NR_CPUS) {
+ printk(KERN_WARNING "Ignoring CPU %d which is "
+ ">= NR_CPUS (%d)\n",
+ cpuid, NR_CPUS);
+ continue;
+ }
+#else
+ /* On uniprocessor we only want the values for the
+ * real physical cpu the kernel booted onto, however
+ * cpu_data() only has one entry at index 0.
+ */
+ if (cpuid != real_hard_smp_processor_id())
+ continue;
+ cpuid = 0;
+#endif
+
+ cpu_data(cpuid).clock_tick =
+ of_getintprop_default(dp, "clock-frequency", 0);
+
+ if (portid_parent) {
+ cpu_data(cpuid).dcache_size =
+ of_getintprop_default(dp, "l1-dcache-size",
+ 16 * 1024);
+ cpu_data(cpuid).dcache_line_size =
+ of_getintprop_default(dp, "l1-dcache-line-size",
+ 32);
+ cpu_data(cpuid).icache_size =
+ of_getintprop_default(dp, "l1-icache-size",
+ 8 * 1024);
+ cpu_data(cpuid).icache_line_size =
+ of_getintprop_default(dp, "l1-icache-line-size",
+ 32);
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(dp, "l2-cache-size", 0);
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(dp, "l2-cache-line-size", 0);
+ if (!cpu_data(cpuid).ecache_size ||
+ !cpu_data(cpuid).ecache_line_size) {
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(portid_parent,
+ "l2-cache-size",
+ (4 * 1024 * 1024));
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(portid_parent,
+ "l2-cache-line-size", 64);
+ }
+
+ cpu_data(cpuid).core_id = portid + 1;
+ cpu_data(cpuid).proc_id = portid;
+#ifdef CONFIG_SMP
+ sparc64_multi_core = 1;
+#endif
+ } else {
+ cpu_data(cpuid).dcache_size =
+ of_getintprop_default(dp, "dcache-size", 16 * 1024);
+ cpu_data(cpuid).dcache_line_size =
+ of_getintprop_default(dp, "dcache-line-size", 32);
+
+ cpu_data(cpuid).icache_size =
+ of_getintprop_default(dp, "icache-size", 16 * 1024);
+ cpu_data(cpuid).icache_line_size =
+ of_getintprop_default(dp, "icache-line-size", 32);
+
+ cpu_data(cpuid).ecache_size =
+ of_getintprop_default(dp, "ecache-size",
+ (4 * 1024 * 1024));
+ cpu_data(cpuid).ecache_line_size =
+ of_getintprop_default(dp, "ecache-line-size", 64);
+
+ cpu_data(cpuid).core_id = 0;
+ cpu_data(cpuid).proc_id = -1;
+ }
+
+#ifdef CONFIG_SMP
+ cpu_set(cpuid, cpu_present_map);
+ cpu_set(cpuid, cpu_possible_map);
+#endif
+ }
+
+ smp_fill_in_sib_core_maps();
+}
+
+void __init of_console_init(void)
+{
+ char *msg = "OF stdout device is: %s\n";
+ struct device_node *dp;
+ const char *type;
+ phandle node;
+
+ of_console_path = prom_early_alloc(256);
+ if (prom_ihandle2path(prom_stdout, of_console_path, 256) < 0) {
+ prom_printf("Cannot obtain path of stdout.\n");
+ prom_halt();
+ }
+ of_console_options = strrchr(of_console_path, ':');
+ if (of_console_options) {
+ of_console_options++;
+ if (*of_console_options == '\0')
+ of_console_options = NULL;
+ }
+
+ node = prom_inst2pkg(prom_stdout);
+ if (!node) {
+ prom_printf("Cannot resolve stdout node from "
+ "instance %08x.\n", prom_stdout);
+ prom_halt();
+ }
+
+ dp = of_find_node_by_phandle(node);
+ type = of_get_property(dp, "device_type", NULL);
+ if (!type) {
+ prom_printf("Console stdout lacks device_type property.\n");
+ prom_halt();
+ }
+
+ if (strcmp(type, "display") && strcmp(type, "serial")) {
+ prom_printf("Console device_type is neither display "
+ "nor serial.\n");
+ prom_halt();
+ }
+
+ of_console_device = dp;
+
+ printk(msg, of_console_path);
+}
diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
new file mode 100644
index 000000000000..4e9af593db49
--- /dev/null
+++ b/arch/sparc/kernel/prom_common.c
@@ -0,0 +1,326 @@
+/* prom_common.c: OF device tree support common code.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc by David S. Miller davem@davemloft.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/prom.h>
+#include <asm/oplib.h>
+
+#include "prom.h"
+
+struct device_node *of_console_device;
+EXPORT_SYMBOL(of_console_device);
+
+char *of_console_path;
+EXPORT_SYMBOL(of_console_path);
+
+char *of_console_options;
+EXPORT_SYMBOL(of_console_options);
+
+struct device_node *of_find_node_by_phandle(phandle handle)
+{
+ struct device_node *np;
+
+ for (np = allnodes; np; np = np->allnext)
+ if (np->node == handle)
+ break;
+
+ return np;
+}
+EXPORT_SYMBOL(of_find_node_by_phandle);
+
+int of_getintprop_default(struct device_node *np, const char *name, int def)
+{
+ struct property *prop;
+ int len;
+
+ prop = of_find_property(np, name, &len);
+ if (!prop || len != 4)
+ return def;
+
+ return *(int *) prop->value;
+}
+EXPORT_SYMBOL(of_getintprop_default);
+
+DEFINE_MUTEX(of_set_property_mutex);
+EXPORT_SYMBOL(of_set_property_mutex);
+
+int of_set_property(struct device_node *dp, const char *name, void *val, int len)
+{
+ struct property **prevp;
+ void *new_val;
+ int err;
+
+ new_val = kmalloc(len, GFP_KERNEL);
+ if (!new_val)
+ return -ENOMEM;
+
+ memcpy(new_val, val, len);
+
+ err = -ENODEV;
+
+ write_lock(&devtree_lock);
+ prevp = &dp->properties;
+ while (*prevp) {
+ struct property *prop = *prevp;
+
+ if (!strcasecmp(prop->name, name)) {
+ void *old_val = prop->value;
+ int ret;
+
+ mutex_lock(&of_set_property_mutex);
+ ret = prom_setprop(dp->node, name, val, len);
+ mutex_unlock(&of_set_property_mutex);
+
+ err = -EINVAL;
+ if (ret >= 0) {
+ prop->value = new_val;
+ prop->length = len;
+
+ if (OF_IS_DYNAMIC(prop))
+ kfree(old_val);
+
+ OF_MARK_DYNAMIC(prop);
+
+ err = 0;
+ }
+ break;
+ }
+ prevp = &(*prevp)->next;
+ }
+ write_unlock(&devtree_lock);
+
+ /* XXX Upate procfs if necessary... */
+
+ return err;
+}
+EXPORT_SYMBOL(of_set_property);
+
+int of_find_in_proplist(const char *list, const char *match, int len)
+{
+ while (len > 0) {
+ int l;
+
+ if (!strcmp(list, match))
+ return 1;
+ l = strlen(list) + 1;
+ list += l;
+ len -= l;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(of_find_in_proplist);
+
+unsigned int prom_unique_id;
+
+static struct property * __init build_one_prop(phandle node, char *prev,
+ char *special_name,
+ void *special_val,
+ int special_len)
+{
+ static struct property *tmp = NULL;
+ struct property *p;
+ const char *name;
+
+ if (tmp) {
+ p = tmp;
+ memset(p, 0, sizeof(*p) + 32);
+ tmp = NULL;
+ } else {
+ p = prom_early_alloc(sizeof(struct property) + 32);
+ p->unique_id = prom_unique_id++;
+ }
+
+ p->name = (char *) (p + 1);
+ if (special_name) {
+ strcpy(p->name, special_name);
+ p->length = special_len;
+ p->value = prom_early_alloc(special_len);
+ memcpy(p->value, special_val, special_len);
+ } else {
+#ifdef CONFIG_SPARC32
+ if (prev == NULL) {
+ name = prom_firstprop(node, NULL);
+ } else {
+ name = prom_nextprop(node, prev, NULL);
+ }
+#else
+ if (prev == NULL) {
+ prom_firstprop(node, p->name);
+ } else {
+ prom_nextprop(node, prev, p->name);
+ }
+ name = p->name;
+#endif
+ if (strlen(name) == 0) {
+ tmp = p;
+ return NULL;
+ }
+#ifdef CONFIG_SPARC32
+ strcpy(p->name, name);
+#endif
+ p->length = prom_getproplen(node, p->name);
+ if (p->length <= 0) {
+ p->length = 0;
+ } else {
+ int len;
+
+ p->value = prom_early_alloc(p->length + 1);
+ len = prom_getproperty(node, p->name, p->value,
+ p->length);
+ if (len <= 0)
+ p->length = 0;
+ ((unsigned char *)p->value)[p->length] = '\0';
+ }
+ }
+ return p;
+}
+
+static struct property * __init build_prop_list(phandle node)
+{
+ struct property *head, *tail;
+
+ head = tail = build_one_prop(node, NULL,
+ ".node", &node, sizeof(node));
+
+ tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
+ tail = tail->next;
+ while(tail) {
+ tail->next = build_one_prop(node, tail->name,
+ NULL, NULL, 0);
+ tail = tail->next;
+ }
+
+ return head;
+}
+
+static char * __init get_one_property(phandle node, const char *name)
+{
+ char *buf = "<NULL>";
+ int len;
+
+ len = prom_getproplen(node, name);
+ if (len > 0) {
+ buf = prom_early_alloc(len);
+ len = prom_getproperty(node, name, buf, len);
+ }
+
+ return buf;
+}
+
+static struct device_node * __init prom_create_node(phandle node,
+ struct device_node *parent)
+{
+ struct device_node *dp;
+
+ if (!node)
+ return NULL;
+
+ dp = prom_early_alloc(sizeof(*dp));
+ dp->unique_id = prom_unique_id++;
+ dp->parent = parent;
+
+ kref_init(&dp->kref);
+
+ dp->name = get_one_property(node, "name");
+ dp->type = get_one_property(node, "device_type");
+ dp->node = node;
+
+ dp->properties = build_prop_list(node);
+
+ irq_trans_init(dp);
+
+ return dp;
+}
+
+static char * __init build_full_name(struct device_node *dp)
+{
+ int len, ourlen, plen;
+ char *n;
+
+ plen = strlen(dp->parent->full_name);
+ ourlen = strlen(dp->path_component_name);
+ len = ourlen + plen + 2;
+
+ n = prom_early_alloc(len);
+ strcpy(n, dp->parent->full_name);
+ if (!is_root_node(dp->parent)) {
+ strcpy(n + plen, "/");
+ plen++;
+ }
+ strcpy(n + plen, dp->path_component_name);
+
+ return n;
+}
+
+static struct device_node * __init prom_build_tree(struct device_node *parent,
+ phandle node,
+ struct device_node ***nextp)
+{
+ struct device_node *ret = NULL, *prev_sibling = NULL;
+ struct device_node *dp;
+
+ while (1) {
+ dp = prom_create_node(node, parent);
+ if (!dp)
+ break;
+
+ if (prev_sibling)
+ prev_sibling->sibling = dp;
+
+ if (!ret)
+ ret = dp;
+ prev_sibling = dp;
+
+ *(*nextp) = dp;
+ *nextp = &dp->allnext;
+
+ dp->path_component_name = build_path_component(dp);
+ dp->full_name = build_full_name(dp);
+
+ dp->child = prom_build_tree(dp, prom_getchild(node), nextp);
+
+ node = prom_getsibling(node);
+ }
+
+ return ret;
+}
+
+unsigned int prom_early_allocated __initdata;
+
+void __init prom_build_devicetree(void)
+{
+ struct device_node **nextp;
+
+ allnodes = prom_create_node(prom_root_node, NULL);
+ allnodes->path_component_name = "";
+ allnodes->full_name = "/";
+
+ nextp = &allnodes->allnext;
+ allnodes->child = prom_build_tree(allnodes,
+ prom_getchild(allnodes->node),
+ &nextp);
+ of_console_init();
+
+ printk("PROM: Built device tree with %u bytes of memory.\n",
+ prom_early_allocated);
+
+ of_fill_in_cpu_data();
+}
diff --git a/arch/sparc/kernel/prom_irqtrans.c b/arch/sparc/kernel/prom_irqtrans.c
new file mode 100644
index 000000000000..96958c4dce8e
--- /dev/null
+++ b/arch/sparc/kernel/prom_irqtrans.c
@@ -0,0 +1,842 @@
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/irq.h>
+#include <asm/upa.h>
+
+#include "prom.h"
+
+#ifdef CONFIG_PCI
+/* PSYCHO interrupt mapping support. */
+#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
+#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
+}
+
+#define PSYCHO_OBIO_IMAP_BASE 0x1000UL
+
+#define PSYCHO_ONBOARD_IRQ_BASE 0x20
+#define psycho_onboard_imap_offset(__ino) \
+ (PSYCHO_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3))
+
+#define PSYCHO_ICLR_A_SLOT0 0x1400UL
+#define PSYCHO_ICLR_SCSI 0x1800UL
+
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static unsigned int psycho_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long controller_regs = (unsigned long) _data;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+
+ ino &= 0x3f;
+ if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = psycho_pcislot_imap_offset(ino);
+ } else {
+ /* Onboard device */
+ imap_off = psycho_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+
+ iclr_off = psycho_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ return build_irq(inofixup, iclr, imap);
+}
+
+static void __init psycho_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = psycho_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) regs[2].phys_addr;
+}
+
+#define sabre_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+
+struct sabre_irq_data {
+ unsigned long controller_regs;
+ unsigned int pci_first_busno;
+};
+#define SABRE_CONFIGSPACE 0x001000000UL
+#define SABRE_WRSYNC 0x1c20UL
+
+#define SABRE_CONFIG_BASE(CONFIG_SPACE) \
+ (CONFIG_SPACE | (1UL << 24))
+#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+/* When a device lives behind a bridge deeper in the PCI bus topology
+ * than APB, a special sequence must run to make sure all pending DMA
+ * transfers at the time of IRQ delivery are visible in the coherency
+ * domain by the cpu. This sequence is to perform a read on the far
+ * side of the non-APB bridge, then perform a read of Sabre's DMA
+ * write-sync register.
+ */
+static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned int phys_hi = (unsigned int) (unsigned long) _arg1;
+ struct sabre_irq_data *irq_data = _arg2;
+ unsigned long controller_regs = irq_data->controller_regs;
+ unsigned long sync_reg = controller_regs + SABRE_WRSYNC;
+ unsigned long config_space = controller_regs + SABRE_CONFIGSPACE;
+ unsigned int bus, devfn;
+ u16 _unused;
+
+ config_space = SABRE_CONFIG_BASE(config_space);
+
+ bus = (phys_hi >> 16) & 0xff;
+ devfn = (phys_hi >> 8) & 0xff;
+
+ config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00);
+
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduha [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (_unused)
+ : "r" ((u16 *) config_space),
+ "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+
+ sabre_read(sync_reg);
+}
+
+#define SABRE_IMAP_A_SLOT0 0x0c00UL
+#define SABRE_IMAP_B_SLOT0 0x0c20UL
+#define SABRE_ICLR_A_SLOT0 0x1400UL
+#define SABRE_ICLR_B_SLOT0 0x1480UL
+#define SABRE_ICLR_SCSI 0x1800UL
+#define SABRE_ICLR_ETH 0x1808UL
+#define SABRE_ICLR_BPP 0x1810UL
+#define SABRE_ICLR_AU_REC 0x1818UL
+#define SABRE_ICLR_AU_PLAY 0x1820UL
+#define SABRE_ICLR_PFAIL 0x1828UL
+#define SABRE_ICLR_KMS 0x1830UL
+#define SABRE_ICLR_FLPY 0x1838UL
+#define SABRE_ICLR_SHW 0x1840UL
+#define SABRE_ICLR_KBD 0x1848UL
+#define SABRE_ICLR_MS 0x1850UL
+#define SABRE_ICLR_SER 0x1858UL
+#define SABRE_ICLR_UE 0x1870UL
+#define SABRE_ICLR_CE 0x1878UL
+#define SABRE_ICLR_PCIERR 0x1880UL
+
+static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return SABRE_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return SABRE_IMAP_B_SLOT0 + (slot * 8);
+}
+
+#define SABRE_OBIO_IMAP_BASE 0x1000UL
+#define SABRE_ONBOARD_IRQ_BASE 0x20
+#define sabre_onboard_imap_offset(__ino) \
+ (SABRE_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3))
+
+#define sabre_iclr_offset(ino) \
+ ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static int sabre_device_needs_wsync(struct device_node *dp)
+{
+ struct device_node *parent = dp->parent;
+ const char *parent_model, *parent_compat;
+
+ /* This traversal up towards the root is meant to
+ * handle two cases:
+ *
+ * 1) non-PCI bus sitting under PCI, such as 'ebus'
+ * 2) the PCI controller interrupts themselves, which
+ * will use the sabre_irq_build but do not need
+ * the DMA synchronization handling
+ */
+ while (parent) {
+ if (!strcmp(parent->type, "pci"))
+ break;
+ parent = parent->parent;
+ }
+
+ if (!parent)
+ return 0;
+
+ parent_model = of_get_property(parent,
+ "model", NULL);
+ if (parent_model &&
+ (!strcmp(parent_model, "SUNW,sabre") ||
+ !strcmp(parent_model, "SUNW,simba")))
+ return 0;
+
+ parent_compat = of_get_property(parent,
+ "compatible", NULL);
+ if (parent_compat &&
+ (!strcmp(parent_compat, "pci108e,a000") ||
+ !strcmp(parent_compat, "pci108e,a001")))
+ return 0;
+
+ return 1;
+}
+
+static unsigned int sabre_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct sabre_irq_data *irq_data = _data;
+ unsigned long controller_regs = irq_data->controller_regs;
+ const struct linux_prom_pci_registers *regs;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+ int virt_irq;
+
+ ino &= 0x3f;
+ if (ino < SABRE_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = sabre_pcislot_imap_offset(ino);
+ } else {
+ /* onboard device */
+ imap_off = sabre_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+
+ iclr_off = sabre_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ virt_irq = build_irq(inofixup, iclr, imap);
+
+ /* If the parent device is a PCI<->PCI bridge other than
+ * APB, we have to install a pre-handler to ensure that
+ * all pending DMA is drained before the interrupt handler
+ * is run.
+ */
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs && sabre_device_needs_wsync(dp)) {
+ irq_install_pre_handler(virt_irq,
+ sabre_wsync_handler,
+ (void *) (long) regs->phys_hi,
+ (void *) irq_data);
+ }
+
+ return virt_irq;
+}
+
+static void __init sabre_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+ struct sabre_irq_data *irq_data;
+ const u32 *busrange;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sabre_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct sabre_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ irq_data->controller_regs = regs[0].phys_addr;
+
+ busrange = of_get_property(dp, "bus-range", NULL);
+ irq_data->pci_first_busno = busrange[0];
+
+ dp->irq_trans->data = irq_data;
+}
+
+/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
+ * imap/iclr registers are per-PBM.
+ */
+#define SCHIZO_IMAP_BASE 0x1000UL
+#define SCHIZO_ICLR_BASE 0x1400UL
+
+static unsigned long schizo_imap_offset(unsigned long ino)
+{
+ return SCHIZO_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_iclr_offset(unsigned long ino)
+{
+ return SCHIZO_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
+ unsigned int ino)
+{
+
+ return pbm_regs + schizo_iclr_offset(ino);
+}
+
+static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + schizo_imap_offset(ino);
+}
+
+#define schizo_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define schizo_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned long sync_reg = (unsigned long) _arg2;
+ u64 mask = 1UL << (ino & IMAP_INO);
+ u64 val;
+ int limit;
+
+ schizo_write(sync_reg, mask);
+
+ limit = 100000;
+ val = 0;
+ while (--limit) {
+ val = schizo_read(sync_reg);
+ if (!(val & mask))
+ break;
+ }
+ if (limit <= 0) {
+ printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
+ val, mask);
+ }
+
+ if (_arg1) {
+ static unsigned char cacheline[64]
+ __attribute__ ((aligned (64)));
+
+ __asm__ __volatile__("rd %%fprs, %0\n\t"
+ "or %0, %4, %1\n\t"
+ "wr %1, 0x0, %%fprs\n\t"
+ "stda %%f0, [%5] %6\n\t"
+ "wr %0, 0x0, %%fprs\n\t"
+ "membar #Sync"
+ : "=&r" (mask), "=&r" (val)
+ : "0" (mask), "1" (val),
+ "i" (FPRS_FEF), "r" (&cacheline[0]),
+ "i" (ASI_BLK_COMMIT_P));
+ }
+}
+
+struct schizo_irq_data {
+ unsigned long pbm_regs;
+ unsigned long sync_reg;
+ u32 portid;
+ int chip_version;
+};
+
+static unsigned int schizo_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct schizo_irq_data *irq_data = _data;
+ unsigned long pbm_regs = irq_data->pbm_regs;
+ unsigned long imap, iclr;
+ int ign_fixup;
+ int virt_irq;
+ int is_tomatillo;
+
+ ino &= 0x3f;
+
+ /* Now build the IRQ bucket. */
+ imap = schizo_ino_to_imap(pbm_regs, ino);
+ iclr = schizo_ino_to_iclr(pbm_regs, ino);
+
+ /* On Schizo, no inofixup occurs. This is because each
+ * INO has it's own IMAP register. On Psycho and Sabre
+ * there is only one IMAP register for each PCI slot even
+ * though four different INOs can be generated by each
+ * PCI slot.
+ *
+ * But, for JBUS variants (essentially, Tomatillo), we have
+ * to fixup the lowest bit of the interrupt group number.
+ */
+ ign_fixup = 0;
+
+ is_tomatillo = (irq_data->sync_reg != 0UL);
+
+ if (is_tomatillo) {
+ if (irq_data->portid & 1)
+ ign_fixup = (1 << 6);
+ }
+
+ virt_irq = build_irq(ign_fixup, iclr, imap);
+
+ if (is_tomatillo) {
+ irq_install_pre_handler(virt_irq,
+ tomatillo_wsync_handler,
+ ((irq_data->chip_version <= 4) ?
+ (void *) 1 : (void *) 0),
+ (void *) irq_data->sync_reg);
+ }
+
+ return virt_irq;
+}
+
+static void __init __schizo_irq_trans_init(struct device_node *dp,
+ int is_tomatillo)
+{
+ const struct linux_prom64_registers *regs;
+ struct schizo_irq_data *irq_data;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = schizo_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct schizo_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = irq_data;
+
+ irq_data->pbm_regs = regs[0].phys_addr;
+ if (is_tomatillo)
+ irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL;
+ else
+ irq_data->sync_reg = 0UL;
+ irq_data->portid = of_getintprop_default(dp, "portid", 0);
+ irq_data->chip_version = of_getintprop_default(dp, "version#", 0);
+}
+
+static void __init schizo_irq_trans_init(struct device_node *dp)
+{
+ __schizo_irq_trans_init(dp, 0);
+}
+
+static void __init tomatillo_irq_trans_init(struct device_node *dp)
+{
+ __schizo_irq_trans_init(dp, 1);
+}
+
+static unsigned int pci_sun4v_irq_build(struct device_node *dp,
+ unsigned int devino,
+ void *_data)
+{
+ u32 devhandle = (u32) (unsigned long) _data;
+
+ return sun4v_build_irq(devhandle, devino);
+}
+
+static void __init pci_sun4v_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = pci_sun4v_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long)
+ ((regs->phys_addr >> 32UL) & 0x0fffffff);
+}
+
+struct fire_irq_data {
+ unsigned long pbm_regs;
+ u32 portid;
+};
+
+#define FIRE_IMAP_BASE 0x001000
+#define FIRE_ICLR_BASE 0x001400
+
+static unsigned long fire_imap_offset(unsigned long ino)
+{
+ return FIRE_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_iclr_offset(unsigned long ino)
+{
+ return FIRE_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long fire_ino_to_iclr(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + fire_iclr_offset(ino);
+}
+
+static unsigned long fire_ino_to_imap(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + fire_imap_offset(ino);
+}
+
+static unsigned int fire_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct fire_irq_data *irq_data = _data;
+ unsigned long pbm_regs = irq_data->pbm_regs;
+ unsigned long imap, iclr;
+ unsigned long int_ctrlr;
+
+ ino &= 0x3f;
+
+ /* Now build the IRQ bucket. */
+ imap = fire_ino_to_imap(pbm_regs, ino);
+ iclr = fire_ino_to_iclr(pbm_regs, ino);
+
+ /* Set the interrupt controller number. */
+ int_ctrlr = 1 << 6;
+ upa_writeq(int_ctrlr, imap);
+
+ /* The interrupt map registers do not have an INO field
+ * like other chips do. They return zero in the INO
+ * field, and the interrupt controller number is controlled
+ * in bits 6 to 9. So in order for build_irq() to get
+ * the INO right we pass it in as part of the fixup
+ * which will get added to the map register zero value
+ * read by build_irq().
+ */
+ ino |= (irq_data->portid << 6);
+ ino -= int_ctrlr;
+ return build_irq(ino, iclr, imap);
+}
+
+static void __init fire_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+ struct fire_irq_data *irq_data;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = fire_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = irq_data;
+
+ irq_data->pbm_regs = regs[0].phys_addr;
+ irq_data->portid = of_getintprop_default(dp, "portid", 0);
+}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_SBUS
+/* INO number to IMAP register offset for SYSIO external IRQ's.
+ * This should conform to both Sunfire/Wildfire server and Fusion
+ * desktop designs.
+ */
+#define SYSIO_IMAP_SLOT0 0x2c00UL
+#define SYSIO_IMAP_SLOT1 0x2c08UL
+#define SYSIO_IMAP_SLOT2 0x2c10UL
+#define SYSIO_IMAP_SLOT3 0x2c18UL
+#define SYSIO_IMAP_SCSI 0x3000UL
+#define SYSIO_IMAP_ETH 0x3008UL
+#define SYSIO_IMAP_BPP 0x3010UL
+#define SYSIO_IMAP_AUDIO 0x3018UL
+#define SYSIO_IMAP_PFAIL 0x3020UL
+#define SYSIO_IMAP_KMS 0x3028UL
+#define SYSIO_IMAP_FLPY 0x3030UL
+#define SYSIO_IMAP_SHW 0x3038UL
+#define SYSIO_IMAP_KBD 0x3040UL
+#define SYSIO_IMAP_MS 0x3048UL
+#define SYSIO_IMAP_SER 0x3050UL
+#define SYSIO_IMAP_TIM0 0x3060UL
+#define SYSIO_IMAP_TIM1 0x3068UL
+#define SYSIO_IMAP_UE 0x3070UL
+#define SYSIO_IMAP_CE 0x3078UL
+#define SYSIO_IMAP_SBERR 0x3080UL
+#define SYSIO_IMAP_PMGMT 0x3088UL
+#define SYSIO_IMAP_GFX 0x3090UL
+#define SYSIO_IMAP_EUPA 0x3098UL
+
+#define bogon ((unsigned long) -1)
+static unsigned long sysio_irq_offsets[] = {
+ /* SBUS Slot 0 --> 3, level 1 --> 7 */
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+
+ /* Onboard devices (not relevant/used on SunFire). */
+ SYSIO_IMAP_SCSI,
+ SYSIO_IMAP_ETH,
+ SYSIO_IMAP_BPP,
+ bogon,
+ SYSIO_IMAP_AUDIO,
+ SYSIO_IMAP_PFAIL,
+ bogon,
+ bogon,
+ SYSIO_IMAP_KMS,
+ SYSIO_IMAP_FLPY,
+ SYSIO_IMAP_SHW,
+ SYSIO_IMAP_KBD,
+ SYSIO_IMAP_MS,
+ SYSIO_IMAP_SER,
+ bogon,
+ bogon,
+ SYSIO_IMAP_TIM0,
+ SYSIO_IMAP_TIM1,
+ bogon,
+ bogon,
+ SYSIO_IMAP_UE,
+ SYSIO_IMAP_CE,
+ SYSIO_IMAP_SBERR,
+ SYSIO_IMAP_PMGMT,
+ SYSIO_IMAP_GFX,
+ SYSIO_IMAP_EUPA,
+};
+
+#undef bogon
+
+#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets)
+
+/* Convert Interrupt Mapping register pointer to associated
+ * Interrupt Clear register pointer, SYSIO specific version.
+ */
+#define SYSIO_ICLR_UNUSED0 0x3400UL
+#define SYSIO_ICLR_SLOT0 0x3408UL
+#define SYSIO_ICLR_SLOT1 0x3448UL
+#define SYSIO_ICLR_SLOT2 0x3488UL
+#define SYSIO_ICLR_SLOT3 0x34c8UL
+static unsigned long sysio_imap_to_iclr(unsigned long imap)
+{
+ unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0;
+ return imap + diff;
+}
+
+static unsigned int sbus_of_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long reg_base = (unsigned long) _data;
+ const struct linux_prom_registers *regs;
+ unsigned long imap, iclr;
+ int sbus_slot = 0;
+ int sbus_level = 0;
+
+ ino &= 0x3f;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs)
+ sbus_slot = regs->which_io;
+
+ if (ino < 0x20)
+ ino += (sbus_slot * 8);
+
+ imap = sysio_irq_offsets[ino];
+ if (imap == ((unsigned long)-1)) {
+ prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n",
+ ino);
+ prom_halt();
+ }
+ imap += reg_base;
+
+ /* SYSIO inconsistency. For external SLOTS, we have to select
+ * the right ICLR register based upon the lower SBUS irq level
+ * bits.
+ */
+ if (ino >= 0x20) {
+ iclr = sysio_imap_to_iclr(imap);
+ } else {
+ sbus_level = ino & 0x7;
+
+ switch(sbus_slot) {
+ case 0:
+ iclr = reg_base + SYSIO_ICLR_SLOT0;
+ break;
+ case 1:
+ iclr = reg_base + SYSIO_ICLR_SLOT1;
+ break;
+ case 2:
+ iclr = reg_base + SYSIO_ICLR_SLOT2;
+ break;
+ default:
+ case 3:
+ iclr = reg_base + SYSIO_ICLR_SLOT3;
+ break;
+ };
+
+ iclr += ((unsigned long)sbus_level - 1UL) * 8UL;
+ }
+ return build_irq(sbus_level, iclr, imap);
+}
+
+static void __init sbus_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sbus_of_build_irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr;
+}
+#endif /* CONFIG_SBUS */
+
+
+static unsigned int central_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct device_node *central_dp = _data;
+ struct of_device *central_op = of_find_device_by_node(central_dp);
+ struct resource *res;
+ unsigned long imap, iclr;
+ u32 tmp;
+
+ if (!strcmp(dp->name, "eeprom")) {
+ res = &central_op->resource[5];
+ } else if (!strcmp(dp->name, "zs")) {
+ res = &central_op->resource[4];
+ } else if (!strcmp(dp->name, "clock-board")) {
+ res = &central_op->resource[3];
+ } else {
+ return ino;
+ }
+
+ imap = res->start + 0x00UL;
+ iclr = res->start + 0x10UL;
+
+ /* Set the INO state to idle, and disable. */
+ upa_writel(0, iclr);
+ upa_readl(iclr);
+
+ tmp = upa_readl(imap);
+ tmp &= ~0x80000000;
+ upa_writel(tmp, imap);
+
+ return build_irq(0, iclr, imap);
+}
+
+static void __init central_irq_trans_init(struct device_node *dp)
+{
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = central_build_irq;
+
+ dp->irq_trans->data = dp;
+}
+
+struct irq_trans {
+ const char *name;
+ void (*init)(struct device_node *);
+};
+
+#ifdef CONFIG_PCI
+static struct irq_trans __initdata pci_irq_trans_table[] = {
+ { "SUNW,sabre", sabre_irq_trans_init },
+ { "pci108e,a000", sabre_irq_trans_init },
+ { "pci108e,a001", sabre_irq_trans_init },
+ { "SUNW,psycho", psycho_irq_trans_init },
+ { "pci108e,8000", psycho_irq_trans_init },
+ { "SUNW,schizo", schizo_irq_trans_init },
+ { "pci108e,8001", schizo_irq_trans_init },
+ { "SUNW,schizo+", schizo_irq_trans_init },
+ { "pci108e,8002", schizo_irq_trans_init },
+ { "SUNW,tomatillo", tomatillo_irq_trans_init },
+ { "pci108e,a801", tomatillo_irq_trans_init },
+ { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
+ { "pciex108e,80f0", fire_irq_trans_init },
+};
+#endif
+
+static unsigned int sun4v_vdev_irq_build(struct device_node *dp,
+ unsigned int devino,
+ void *_data)
+{
+ u32 devhandle = (u32) (unsigned long) _data;
+
+ return sun4v_build_irq(devhandle, devino);
+}
+
+static void __init sun4v_vdev_irq_trans_init(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sun4v_vdev_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long)
+ ((regs->phys_addr >> 32UL) & 0x0fffffff);
+}
+
+void __init irq_trans_init(struct device_node *dp)
+{
+#ifdef CONFIG_PCI
+ const char *model;
+ int i;
+#endif
+
+#ifdef CONFIG_PCI
+ model = of_get_property(dp, "model", NULL);
+ if (!model)
+ model = of_get_property(dp, "compatible", NULL);
+ if (model) {
+ for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) {
+ struct irq_trans *t = &pci_irq_trans_table[i];
+
+ if (!strcmp(model, t->name)) {
+ t->init(dp);
+ return;
+ }
+ }
+ }
+#endif
+#ifdef CONFIG_SBUS
+ if (!strcmp(dp->name, "sbus") ||
+ !strcmp(dp->name, "sbi")) {
+ sbus_irq_trans_init(dp);
+ return;
+ }
+#endif
+ if (!strcmp(dp->name, "fhc") &&
+ !strcmp(dp->parent->name, "central")) {
+ central_irq_trans_init(dp);
+ return;
+ }
+ if (!strcmp(dp->name, "virtual-devices") ||
+ !strcmp(dp->name, "niu")) {
+ sun4v_vdev_irq_trans_init(dp);
+ return;
+ }
+}
diff --git a/arch/sparc64/kernel/psycho_common.c b/arch/sparc/kernel/psycho_common.c
index 790996428c14..790996428c14 100644
--- a/arch/sparc64/kernel/psycho_common.c
+++ b/arch/sparc/kernel/psycho_common.c
diff --git a/arch/sparc64/kernel/psycho_common.h b/arch/sparc/kernel/psycho_common.h
index 092c278ef28d..092c278ef28d 100644
--- a/arch/sparc64/kernel/psycho_common.h
+++ b/arch/sparc/kernel/psycho_common.h
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace_32.c
index 8ce6285a06d5..8ce6285a06d5 100644
--- a/arch/sparc/kernel/ptrace.c
+++ b/arch/sparc/kernel/ptrace_32.c
diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc/kernel/ptrace_64.c
index a941c610e7ce..a941c610e7ce 100644
--- a/arch/sparc64/kernel/ptrace.c
+++ b/arch/sparc/kernel/ptrace_64.c
diff --git a/arch/sparc64/kernel/reboot.c b/arch/sparc/kernel/reboot.c
index ef89d3d69748..ef89d3d69748 100644
--- a/arch/sparc64/kernel/reboot.c
+++ b/arch/sparc/kernel/reboot.c
diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap_32.S
index 4da2e1f66290..4da2e1f66290 100644
--- a/arch/sparc/kernel/rtrap.S
+++ b/arch/sparc/kernel/rtrap_32.S
diff --git a/arch/sparc/kernel/rtrap_64.S b/arch/sparc/kernel/rtrap_64.S
new file mode 100644
index 000000000000..fd3cee4d117c
--- /dev/null
+++ b/arch/sparc/kernel/rtrap_64.S
@@ -0,0 +1,450 @@
+/*
+ * rtrap.S: Preparing for return from trap on Sparc V9.
+ *
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+
+#include <asm/asi.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/spitfire.h>
+#include <asm/head.h>
+#include <asm/visasm.h>
+#include <asm/processor.h>
+
+#define RTRAP_PSTATE (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_IE)
+#define RTRAP_PSTATE_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV)
+#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
+
+ .text
+ .align 32
+__handle_softirq:
+ call do_softirq
+ nop
+ ba,a,pt %xcc, __handle_softirq_continue
+ nop
+__handle_preemption:
+ call schedule
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ ba,pt %xcc, __handle_preemption_continue
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+
+__handle_user_windows:
+ call fault_in_user_windows
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ /* Redo sched+sig checks */
+ ldx [%g6 + TI_FLAGS], %l0
+ andcc %l0, _TIF_NEED_RESCHED, %g0
+
+ be,pt %xcc, 1f
+ nop
+ call schedule
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ ldx [%g6 + TI_FLAGS], %l0
+
+1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
+ be,pt %xcc, __handle_user_windows_continue
+ nop
+ mov %l5, %o1
+ add %sp, PTREGS_OFF, %o0
+ mov %l0, %o2
+
+ call do_notify_resume
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ /* Signal delivery can modify pt_regs tstate, so we must
+ * reload it.
+ */
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+ sethi %hi(0xf << 20), %l4
+ and %l1, %l4, %l4
+ ba,pt %xcc, __handle_user_windows_continue
+
+ andn %l1, %l4, %l1
+__handle_perfctrs:
+ call update_perfctrs
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ ldub [%g6 + TI_WSAVED], %o2
+ brz,pt %o2, 1f
+ nop
+ /* Redo userwin+sched+sig checks */
+ call fault_in_user_windows
+
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ ldx [%g6 + TI_FLAGS], %l0
+ andcc %l0, _TIF_NEED_RESCHED, %g0
+ be,pt %xcc, 1f
+
+ nop
+ call schedule
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ ldx [%g6 + TI_FLAGS], %l0
+1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
+
+ be,pt %xcc, __handle_perfctrs_continue
+ sethi %hi(TSTATE_PEF), %o0
+ mov %l5, %o1
+ add %sp, PTREGS_OFF, %o0
+ mov %l0, %o2
+ call do_notify_resume
+
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ /* Signal delivery can modify pt_regs tstate, so we must
+ * reload it.
+ */
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+ sethi %hi(0xf << 20), %l4
+ and %l1, %l4, %l4
+ andn %l1, %l4, %l1
+ ba,pt %xcc, __handle_perfctrs_continue
+
+ sethi %hi(TSTATE_PEF), %o0
+__handle_userfpu:
+ rd %fprs, %l5
+ andcc %l5, FPRS_FEF, %g0
+ sethi %hi(TSTATE_PEF), %o0
+ be,a,pn %icc, __handle_userfpu_continue
+ andn %l1, %o0, %l1
+ ba,a,pt %xcc, __handle_userfpu_continue
+
+__handle_signal:
+ mov %l5, %o1
+ add %sp, PTREGS_OFF, %o0
+ mov %l0, %o2
+ call do_notify_resume
+ wrpr %g0, RTRAP_PSTATE, %pstate
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+
+ /* Signal delivery can modify pt_regs tstate, so we must
+ * reload it.
+ */
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+ sethi %hi(0xf << 20), %l4
+ and %l1, %l4, %l4
+ ba,pt %xcc, __handle_signal_continue
+ andn %l1, %l4, %l1
+
+ /* When returning from a NMI (%pil==15) interrupt we want to
+ * avoid running softirqs, doing IRQ tracing, preempting, etc.
+ */
+ .globl rtrap_nmi
+rtrap_nmi: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+ sethi %hi(0xf << 20), %l4
+ and %l1, %l4, %l4
+ andn %l1, %l4, %l1
+ srl %l4, 20, %l4
+ ba,pt %xcc, rtrap_no_irq_enable
+ wrpr %l4, %pil
+
+ .align 64
+ .globl rtrap_irq, rtrap, irqsz_patchme, rtrap_xcall
+rtrap_irq:
+rtrap:
+#ifndef CONFIG_SMP
+ sethi %hi(per_cpu____cpu_data), %l0
+ lduw [%l0 + %lo(per_cpu____cpu_data)], %l1
+#else
+ sethi %hi(per_cpu____cpu_data), %l0
+ or %l0, %lo(per_cpu____cpu_data), %l0
+ lduw [%l0 + %g5], %l1
+#endif
+ cmp %l1, 0
+
+ /* mm/ultra.S:xcall_report_regs KNOWS about this load. */
+ bne,pn %icc, __handle_softirq
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+__handle_softirq_continue:
+rtrap_xcall:
+ sethi %hi(0xf << 20), %l4
+ and %l1, %l4, %l4
+ andn %l1, %l4, %l1
+ srl %l4, 20, %l4
+#ifdef CONFIG_TRACE_IRQFLAGS
+ brnz,pn %l4, rtrap_no_irq_enable
+ nop
+ call trace_hardirqs_on
+ nop
+ wrpr %l4, %pil
+#endif
+rtrap_no_irq_enable:
+ andcc %l1, TSTATE_PRIV, %l3
+ bne,pn %icc, to_kernel
+ nop
+
+ /* We must hold IRQs off and atomically test schedule+signal
+ * state, then hold them off all the way back to userspace.
+ * If we are returning to kernel, none of this matters. Note
+ * that we are disabling interrupts via PSTATE_IE, not using
+ * %pil.
+ *
+ * If we do not do this, there is a window where we would do
+ * the tests, later the signal/resched event arrives but we do
+ * not process it since we are still in kernel mode. It would
+ * take until the next local IRQ before the signal/resched
+ * event would be handled.
+ *
+ * This also means that if we have to deal with performance
+ * counters or user windows, we have to redo all of these
+ * sched+signal checks with IRQs disabled.
+ */
+to_user: wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ wrpr 0, %pil
+__handle_preemption_continue:
+ ldx [%g6 + TI_FLAGS], %l0
+ sethi %hi(_TIF_USER_WORK_MASK), %o0
+ or %o0, %lo(_TIF_USER_WORK_MASK), %o0
+ andcc %l0, %o0, %g0
+ sethi %hi(TSTATE_PEF), %o0
+ be,pt %xcc, user_nowork
+ andcc %l1, %o0, %g0
+ andcc %l0, _TIF_NEED_RESCHED, %g0
+ bne,pn %xcc, __handle_preemption
+ andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
+ bne,pn %xcc, __handle_signal
+__handle_signal_continue:
+ ldub [%g6 + TI_WSAVED], %o2
+ brnz,pn %o2, __handle_user_windows
+ nop
+__handle_user_windows_continue:
+ ldx [%g6 + TI_FLAGS], %l5
+ andcc %l5, _TIF_PERFCTR, %g0
+ sethi %hi(TSTATE_PEF), %o0
+ bne,pn %xcc, __handle_perfctrs
+__handle_perfctrs_continue:
+ andcc %l1, %o0, %g0
+
+ /* This fpdepth clear is necessary for non-syscall rtraps only */
+user_nowork:
+ bne,pn %xcc, __handle_userfpu
+ stb %g0, [%g6 + TI_FPDEPTH]
+__handle_userfpu_continue:
+
+rt_continue: ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1
+ ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2
+
+ ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3
+ ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4
+ ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5
+ brz,pt %l3, 1f
+ mov %g6, %l2
+
+ /* Must do this before thread reg is clobbered below. */
+ LOAD_PER_CPU_BASE(%g5, %g6, %i0, %i1, %i2)
+1:
+ ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6
+ ldx [%sp + PTREGS_OFF + PT_V9_G7], %g7
+
+ /* Normal globals are restored, go to trap globals. */
+661: wrpr %g0, RTRAP_PSTATE_AG_IRQOFF, %pstate
+ nop
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
+ SET_GL(1)
+ .previous
+
+ mov %l2, %g6
+
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0
+ ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1
+
+ ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2
+ ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3
+ ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4
+ ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5
+ ldx [%sp + PTREGS_OFF + PT_V9_I6], %i6
+ ldx [%sp + PTREGS_OFF + PT_V9_I7], %i7
+ ldx [%sp + PTREGS_OFF + PT_V9_TPC], %l2
+ ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %o2
+
+ ld [%sp + PTREGS_OFF + PT_V9_Y], %o3
+ wr %o3, %g0, %y
+ wrpr %l4, 0x0, %pil
+ wrpr %g0, 0x1, %tl
+ andn %l1, TSTATE_SYSCALL, %l1
+ wrpr %l1, %g0, %tstate
+ wrpr %l2, %g0, %tpc
+ wrpr %o2, %g0, %tnpc
+
+ brnz,pn %l3, kern_rtt
+ mov PRIMARY_CONTEXT, %l7
+
+661: ldxa [%l7 + %l7] ASI_DMMU, %l0
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ ldxa [%l7 + %l7] ASI_MMU, %l0
+ .previous
+
+ sethi %hi(sparc64_kern_pri_nuc_bits), %l1
+ ldx [%l1 + %lo(sparc64_kern_pri_nuc_bits)], %l1
+ or %l0, %l1, %l0
+
+661: stxa %l0, [%l7] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %l0, [%l7] ASI_MMU
+ .previous
+
+ sethi %hi(KERNBASE), %l7
+ flush %l7
+ rdpr %wstate, %l1
+ rdpr %otherwin, %l2
+ srl %l1, 3, %l1
+
+ wrpr %l2, %g0, %canrestore
+ wrpr %l1, %g0, %wstate
+ brnz,pt %l2, user_rtt_restore
+ wrpr %g0, %g0, %otherwin
+
+ ldx [%g6 + TI_FLAGS], %g3
+ wr %g0, ASI_AIUP, %asi
+ rdpr %cwp, %g1
+ andcc %g3, _TIF_32BIT, %g0
+ sub %g1, 1, %g1
+ bne,pt %xcc, user_rtt_fill_32bit
+ wrpr %g1, %cwp
+ ba,a,pt %xcc, user_rtt_fill_64bit
+
+user_rtt_fill_fixup:
+ rdpr %cwp, %g1
+ add %g1, 1, %g1
+ wrpr %g1, 0x0, %cwp
+
+ rdpr %wstate, %g2
+ sll %g2, 3, %g2
+ wrpr %g2, 0x0, %wstate
+
+ /* We know %canrestore and %otherwin are both zero. */
+
+ sethi %hi(sparc64_kern_pri_context), %g2
+ ldx [%g2 + %lo(sparc64_kern_pri_context)], %g2
+ mov PRIMARY_CONTEXT, %g1
+
+661: stxa %g2, [%g1] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g2, [%g1] ASI_MMU
+ .previous
+
+ sethi %hi(KERNBASE), %g1
+ flush %g1
+
+ or %g4, FAULT_CODE_WINFIXUP, %g4
+ stb %g4, [%g6 + TI_FAULT_CODE]
+ stx %g5, [%g6 + TI_FAULT_ADDR]
+
+ mov %g6, %l1
+ wrpr %g0, 0x0, %tl
+
+661: nop
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ SET_GL(0)
+ .previous
+
+ wrpr %g0, RTRAP_PSTATE, %pstate
+
+ mov %l1, %g6
+ ldx [%g6 + TI_TASK], %g4
+ LOAD_PER_CPU_BASE(%g5, %g6, %g1, %g2, %g3)
+ call do_sparc64_fault
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+
+user_rtt_pre_restore:
+ add %g1, 1, %g1
+ wrpr %g1, 0x0, %cwp
+
+user_rtt_restore:
+ restore
+ rdpr %canrestore, %g1
+ wrpr %g1, 0x0, %cleanwin
+ retry
+ nop
+
+kern_rtt: rdpr %canrestore, %g1
+ brz,pn %g1, kern_rtt_fill
+ nop
+kern_rtt_restore:
+ stw %g0, [%sp + PTREGS_OFF + PT_V9_MAGIC]
+ restore
+ retry
+
+to_kernel:
+#ifdef CONFIG_PREEMPT
+ ldsw [%g6 + TI_PRE_COUNT], %l5
+ brnz %l5, kern_fpucheck
+ ldx [%g6 + TI_FLAGS], %l5
+ andcc %l5, _TIF_NEED_RESCHED, %g0
+ be,pt %xcc, kern_fpucheck
+ nop
+ cmp %l4, 0
+ bne,pn %xcc, kern_fpucheck
+ sethi %hi(PREEMPT_ACTIVE), %l6
+ stw %l6, [%g6 + TI_PRE_COUNT]
+ call schedule
+ nop
+ ba,pt %xcc, rtrap
+ stw %g0, [%g6 + TI_PRE_COUNT]
+#endif
+kern_fpucheck: ldub [%g6 + TI_FPDEPTH], %l5
+ brz,pt %l5, rt_continue
+ srl %l5, 1, %o0
+ add %g6, TI_FPSAVED, %l6
+ ldub [%l6 + %o0], %l2
+ sub %l5, 2, %l5
+
+ add %g6, TI_GSR, %o1
+ andcc %l2, (FPRS_FEF|FPRS_DU), %g0
+ be,pt %icc, 2f
+ and %l2, FPRS_DL, %l6
+ andcc %l2, FPRS_FEF, %g0
+ be,pn %icc, 5f
+ sll %o0, 3, %o5
+ rd %fprs, %g1
+
+ wr %g1, FPRS_FEF, %fprs
+ ldx [%o1 + %o5], %g1
+ add %g6, TI_XFSR, %o1
+ sll %o0, 8, %o2
+ add %g6, TI_FPREGS, %o3
+ brz,pn %l6, 1f
+ add %g6, TI_FPREGS+0x40, %o4
+
+ membar #Sync
+ ldda [%o3 + %o2] ASI_BLK_P, %f0
+ ldda [%o4 + %o2] ASI_BLK_P, %f16
+ membar #Sync
+1: andcc %l2, FPRS_DU, %g0
+ be,pn %icc, 1f
+ wr %g1, 0, %gsr
+ add %o2, 0x80, %o2
+ membar #Sync
+ ldda [%o3 + %o2] ASI_BLK_P, %f32
+ ldda [%o4 + %o2] ASI_BLK_P, %f48
+1: membar #Sync
+ ldx [%o1 + %o5], %fsr
+2: stb %l5, [%g6 + TI_FPDEPTH]
+ ba,pt %xcc, rt_continue
+ nop
+5: wr %g0, FPRS_FEF, %fprs
+ sll %o0, 8, %o2
+
+ add %g6, TI_FPREGS+0x80, %o3
+ add %g6, TI_FPREGS+0xc0, %o4
+ membar #Sync
+ ldda [%o3 + %o2] ASI_BLK_P, %f32
+ ldda [%o4 + %o2] ASI_BLK_P, %f48
+ membar #Sync
+ wr %g0, FPRS_DU, %fprs
+ ba,pt %xcc, rt_continue
+ stb %l5, [%g6 + TI_FPDEPTH]
diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc/kernel/sbus.c
index 2ead310066d1..2ead310066d1 100644
--- a/arch/sparc64/kernel/sbus.c
+++ b/arch/sparc/kernel/sbus.c
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
deleted file mode 100644
index 24fe3078bd4b..000000000000
--- a/arch/sparc/kernel/setup.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * linux/arch/sparc/kernel/setup.c
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/initrd.h>
-#include <asm/smp.h>
-#include <linux/user.h>
-#include <linux/screen_info.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/syscalls.h>
-#include <linux/kdev_t.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/console.h>
-#include <linux/spinlock.h>
-#include <linux/root_dev.h>
-#include <linux/cpu.h>
-#include <linux/kdebug.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/processor.h>
-#include <asm/oplib.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/traps.h>
-#include <asm/vaddrs.h>
-#include <asm/mbus.h>
-#include <asm/idprom.h>
-#include <asm/machines.h>
-#include <asm/cpudata.h>
-#include <asm/setup.h>
-
-struct screen_info screen_info = {
- 0, 0, /* orig-x, orig-y */
- 0, /* unused */
- 0, /* orig-video-page */
- 0, /* orig-video-mode */
- 128, /* orig-video-cols */
- 0,0,0, /* ega_ax, ega_bx, ega_cx */
- 54, /* orig-video-lines */
- 0, /* orig-video-isVGA */
- 16 /* orig-video-points */
-};
-
-/* Typing sync at the prom prompt calls the function pointed to by
- * romvec->pv_synchook which I set to the following function.
- * This should sync all filesystems and return, for now it just
- * prints out pretty messages and returns.
- */
-
-extern unsigned long trapbase;
-
-/* Pretty sick eh? */
-static void prom_sync_me(void)
-{
- unsigned long prom_tbr, flags;
-
- /* XXX Badly broken. FIX! - Anton */
- local_irq_save(flags);
- __asm__ __volatile__("rd %%tbr, %0\n\t" : "=r" (prom_tbr));
- __asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
- "nop\n\t"
- "nop\n\t"
- "nop\n\t" : : "r" (&trapbase));
-
- prom_printf("PROM SYNC COMMAND...\n");
- show_free_areas();
- if(current->pid != 0) {
- local_irq_enable();
- sys_sync();
- local_irq_disable();
- }
- prom_printf("Returning to prom\n");
-
- __asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
- "nop\n\t"
- "nop\n\t"
- "nop\n\t" : : "r" (prom_tbr));
- local_irq_restore(flags);
-
- return;
-}
-
-static unsigned int boot_flags __initdata = 0;
-#define BOOTME_DEBUG 0x1
-
-/* Exported for mm/init.c:paging_init. */
-unsigned long cmdline_memory_size __initdata = 0;
-
-static void
-prom_console_write(struct console *con, const char *s, unsigned n)
-{
- prom_write(s, n);
-}
-
-static struct console prom_debug_console = {
- .name = "debug",
- .write = prom_console_write,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
-/*
- * Process kernel command line switches that are specific to the
- * SPARC or that require special low-level processing.
- */
-static void __init process_switch(char c)
-{
- switch (c) {
- case 'd':
- boot_flags |= BOOTME_DEBUG;
- break;
- case 's':
- break;
- case 'h':
- prom_printf("boot_flags_init: Halt!\n");
- prom_halt();
- break;
- case 'p':
- /* Use PROM debug console. */
- register_console(&prom_debug_console);
- break;
- default:
- printk("Unknown boot switch (-%c)\n", c);
- break;
- }
-}
-
-static void __init boot_flags_init(char *commands)
-{
- while (*commands) {
- /* Move to the start of the next "argument". */
- while (*commands && *commands == ' ')
- commands++;
-
- /* Process any command switches, otherwise skip it. */
- if (*commands == '\0')
- break;
- if (*commands == '-') {
- commands++;
- while (*commands && *commands != ' ')
- process_switch(*commands++);
- continue;
- }
- if (!strncmp(commands, "mem=", 4)) {
- /*
- * "mem=XXX[kKmM] overrides the PROM-reported
- * memory size.
- */
- cmdline_memory_size = simple_strtoul(commands + 4,
- &commands, 0);
- if (*commands == 'K' || *commands == 'k') {
- cmdline_memory_size <<= 10;
- commands++;
- } else if (*commands=='M' || *commands=='m') {
- cmdline_memory_size <<= 20;
- commands++;
- }
- }
- while (*commands && *commands != ' ')
- commands++;
- }
-}
-
-/* This routine will in the future do all the nasty prom stuff
- * to probe for the mmu type and its parameters, etc. This will
- * also be where SMP things happen.
- */
-
-extern void sun4c_probe_vac(void);
-extern char cputypval;
-extern unsigned long start, end;
-
-extern unsigned short root_flags;
-extern unsigned short root_dev;
-extern unsigned short ram_flags;
-#define RAMDISK_IMAGE_START_MASK 0x07FF
-#define RAMDISK_PROMPT_FLAG 0x8000
-#define RAMDISK_LOAD_FLAG 0x4000
-
-extern int root_mountflags;
-
-char reboot_command[COMMAND_LINE_SIZE];
-enum sparc_cpu sparc_cpu_model;
-
-struct tt_entry *sparc_ttable;
-
-struct pt_regs fake_swapper_regs;
-
-void __init setup_arch(char **cmdline_p)
-{
- int i;
- unsigned long highest_paddr;
-
- sparc_ttable = (struct tt_entry *) &start;
-
- /* Initialize PROM console and command line. */
- *cmdline_p = prom_getbootargs();
- strcpy(boot_command_line, *cmdline_p);
- parse_early_param();
-
- /* Set sparc_cpu_model */
- sparc_cpu_model = sun_unknown;
- if (!strcmp(&cputypval,"sun4 "))
- sparc_cpu_model = sun4;
- if (!strcmp(&cputypval,"sun4c"))
- sparc_cpu_model = sun4c;
- if (!strcmp(&cputypval,"sun4m"))
- sparc_cpu_model = sun4m;
- if (!strcmp(&cputypval,"sun4s"))
- sparc_cpu_model = sun4m; /* CP-1200 with PROM 2.30 -E */
- if (!strcmp(&cputypval,"sun4d"))
- sparc_cpu_model = sun4d;
- if (!strcmp(&cputypval,"sun4e"))
- sparc_cpu_model = sun4e;
- if (!strcmp(&cputypval,"sun4u"))
- sparc_cpu_model = sun4u;
-
- printk("ARCH: ");
- switch(sparc_cpu_model) {
- case sun4:
- printk("SUN4\n");
- break;
- case sun4c:
- printk("SUN4C\n");
- break;
- case sun4m:
- printk("SUN4M\n");
- break;
- case sun4d:
- printk("SUN4D\n");
- break;
- case sun4e:
- printk("SUN4E\n");
- break;
- case sun4u:
- printk("SUN4U\n");
- break;
- default:
- printk("UNKNOWN!\n");
- break;
- };
-
-#ifdef CONFIG_DUMMY_CONSOLE
- conswitchp = &dummy_con;
-#elif defined(CONFIG_PROM_CONSOLE)
- conswitchp = &prom_con;
-#endif
- boot_flags_init(*cmdline_p);
-
- idprom_init();
- if (ARCH_SUN4C)
- sun4c_probe_vac();
- load_mmu();
-
- phys_base = 0xffffffffUL;
- highest_paddr = 0UL;
- for (i = 0; sp_banks[i].num_bytes != 0; i++) {
- unsigned long top;
-
- if (sp_banks[i].base_addr < phys_base)
- phys_base = sp_banks[i].base_addr;
- top = sp_banks[i].base_addr +
- sp_banks[i].num_bytes;
- if (highest_paddr < top)
- highest_paddr = top;
- }
- pfn_base = phys_base >> PAGE_SHIFT;
-
- if (!root_flags)
- root_mountflags &= ~MS_RDONLY;
- ROOT_DEV = old_decode_dev(root_dev);
-#ifdef CONFIG_BLK_DEV_RAM
- rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
- rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
- rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
-#endif
-
- prom_setsync(prom_sync_me);
-
- if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) &&
- ((*(short *)linux_dbvec) != -1)) {
- printk("Booted under KADB. Syncing trap table.\n");
- (*(linux_dbvec->teach_debugger))();
- }
-
- init_mm.context = (unsigned long) NO_CONTEXT;
- init_task.thread.kregs = &fake_swapper_regs;
-
- paging_init();
-
- smp_setup_cpu_possible_map();
-}
-
-extern char *sparc_cpu_type;
-extern char *sparc_fpu_type;
-
-static int ncpus_probed;
-
-static int show_cpuinfo(struct seq_file *m, void *__unused)
-{
- seq_printf(m,
- "cpu\t\t: %s\n"
- "fpu\t\t: %s\n"
- "promlib\t\t: Version %d Revision %d\n"
- "prom\t\t: %d.%d\n"
- "type\t\t: %s\n"
- "ncpus probed\t: %d\n"
- "ncpus active\t: %d\n"
-#ifndef CONFIG_SMP
- "CPU0Bogo\t: %lu.%02lu\n"
- "CPU0ClkTck\t: %ld\n"
-#endif
- ,
- sparc_cpu_type ? sparc_cpu_type : "undetermined",
- sparc_fpu_type ? sparc_fpu_type : "undetermined",
- romvec->pv_romvers,
- prom_rev,
- romvec->pv_printrev >> 16,
- romvec->pv_printrev & 0xffff,
- &cputypval,
- ncpus_probed,
- num_online_cpus()
-#ifndef CONFIG_SMP
- , cpu_data(0).udelay_val/(500000/HZ),
- (cpu_data(0).udelay_val/(5000/HZ)) % 100,
- cpu_data(0).clock_tick
-#endif
- );
-
-#ifdef CONFIG_SMP
- smp_bogo(m);
-#endif
- mmu_info(m);
-#ifdef CONFIG_SMP
- smp_info(m);
-#endif
- return 0;
-}
-
-static void *c_start(struct seq_file *m, loff_t *pos)
-{
- /* The pointer we are returning is arbitrary,
- * it just has to be non-NULL and not IS_ERR
- * in the success case.
- */
- return *pos == 0 ? &c_start : NULL;
-}
-
-static void *c_next(struct seq_file *m, void *v, loff_t *pos)
-{
- ++*pos;
- return c_start(m, pos);
-}
-
-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 = show_cpuinfo,
-};
-
-extern int stop_a_enabled;
-
-void sun_do_break(void)
-{
- if (!stop_a_enabled)
- return;
-
- printk("\n");
- flush_user_windows();
-
- prom_cmdline();
-}
-
-int stop_a_enabled = 1;
-
-static int __init topology_init(void)
-{
- int i, ncpus, err;
-
- /* Count the number of physically present processors in
- * the machine, even on uniprocessor, so that /proc/cpuinfo
- * output is consistent with 2.4.x
- */
- ncpus = 0;
- while (!cpu_find_by_instance(ncpus, NULL, NULL))
- ncpus++;
- ncpus_probed = ncpus;
-
- err = 0;
- for_each_online_cpu(i) {
- struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL);
- if (!p)
- err = -ENOMEM;
- else
- register_cpu(p, i);
- }
-
- return err;
-}
-
-subsys_initcall(topology_init);
diff --git a/arch/sparc/kernel/setup_32.c b/arch/sparc/kernel/setup_32.c
new file mode 100644
index 000000000000..c96c65d1b58b
--- /dev/null
+++ b/arch/sparc/kernel/setup_32.c
@@ -0,0 +1,422 @@
+/*
+ * linux/arch/sparc/kernel/setup.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/initrd.h>
+#include <asm/smp.h>
+#include <linux/user.h>
+#include <linux/screen_info.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/syscalls.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/root_dev.h>
+#include <linux/cpu.h>
+#include <linux/kdebug.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/traps.h>
+#include <asm/vaddrs.h>
+#include <asm/mbus.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>
+#include <asm/cpudata.h>
+#include <asm/setup.h>
+
+#include "kernel.h"
+
+struct screen_info screen_info = {
+ 0, 0, /* orig-x, orig-y */
+ 0, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 128, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 54, /* orig-video-lines */
+ 0, /* orig-video-isVGA */
+ 16 /* orig-video-points */
+};
+
+/* Typing sync at the prom prompt calls the function pointed to by
+ * romvec->pv_synchook which I set to the following function.
+ * This should sync all filesystems and return, for now it just
+ * prints out pretty messages and returns.
+ */
+
+extern unsigned long trapbase;
+
+/* Pretty sick eh? */
+static void prom_sync_me(void)
+{
+ unsigned long prom_tbr, flags;
+
+ /* XXX Badly broken. FIX! - Anton */
+ local_irq_save(flags);
+ __asm__ __volatile__("rd %%tbr, %0\n\t" : "=r" (prom_tbr));
+ __asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t" : : "r" (&trapbase));
+
+ prom_printf("PROM SYNC COMMAND...\n");
+ show_free_areas();
+ if(current->pid != 0) {
+ local_irq_enable();
+ sys_sync();
+ local_irq_disable();
+ }
+ prom_printf("Returning to prom\n");
+
+ __asm__ __volatile__("wr %0, 0x0, %%tbr\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t" : : "r" (prom_tbr));
+ local_irq_restore(flags);
+
+ return;
+}
+
+static unsigned int boot_flags __initdata = 0;
+#define BOOTME_DEBUG 0x1
+
+/* Exported for mm/init.c:paging_init. */
+unsigned long cmdline_memory_size __initdata = 0;
+
+static void
+prom_console_write(struct console *con, const char *s, unsigned n)
+{
+ prom_write(s, n);
+}
+
+static struct console prom_debug_console = {
+ .name = "debug",
+ .write = prom_console_write,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+/*
+ * Process kernel command line switches that are specific to the
+ * SPARC or that require special low-level processing.
+ */
+static void __init process_switch(char c)
+{
+ switch (c) {
+ case 'd':
+ boot_flags |= BOOTME_DEBUG;
+ break;
+ case 's':
+ break;
+ case 'h':
+ prom_printf("boot_flags_init: Halt!\n");
+ prom_halt();
+ break;
+ case 'p':
+ /* Use PROM debug console. */
+ register_console(&prom_debug_console);
+ break;
+ default:
+ printk("Unknown boot switch (-%c)\n", c);
+ break;
+ }
+}
+
+static void __init boot_flags_init(char *commands)
+{
+ while (*commands) {
+ /* Move to the start of the next "argument". */
+ while (*commands && *commands == ' ')
+ commands++;
+
+ /* Process any command switches, otherwise skip it. */
+ if (*commands == '\0')
+ break;
+ if (*commands == '-') {
+ commands++;
+ while (*commands && *commands != ' ')
+ process_switch(*commands++);
+ continue;
+ }
+ if (!strncmp(commands, "mem=", 4)) {
+ /*
+ * "mem=XXX[kKmM] overrides the PROM-reported
+ * memory size.
+ */
+ cmdline_memory_size = simple_strtoul(commands + 4,
+ &commands, 0);
+ if (*commands == 'K' || *commands == 'k') {
+ cmdline_memory_size <<= 10;
+ commands++;
+ } else if (*commands=='M' || *commands=='m') {
+ cmdline_memory_size <<= 20;
+ commands++;
+ }
+ }
+ while (*commands && *commands != ' ')
+ commands++;
+ }
+}
+
+/* This routine will in the future do all the nasty prom stuff
+ * to probe for the mmu type and its parameters, etc. This will
+ * also be where SMP things happen.
+ */
+
+extern void sun4c_probe_vac(void);
+extern char cputypval;
+extern unsigned long start, end;
+
+extern unsigned short root_flags;
+extern unsigned short root_dev;
+extern unsigned short ram_flags;
+#define RAMDISK_IMAGE_START_MASK 0x07FF
+#define RAMDISK_PROMPT_FLAG 0x8000
+#define RAMDISK_LOAD_FLAG 0x4000
+
+extern int root_mountflags;
+
+char reboot_command[COMMAND_LINE_SIZE];
+enum sparc_cpu sparc_cpu_model;
+
+struct tt_entry *sparc_ttable;
+
+struct pt_regs fake_swapper_regs;
+
+void __init setup_arch(char **cmdline_p)
+{
+ int i;
+ unsigned long highest_paddr;
+
+ sparc_ttable = (struct tt_entry *) &start;
+
+ /* Initialize PROM console and command line. */
+ *cmdline_p = prom_getbootargs();
+ strcpy(boot_command_line, *cmdline_p);
+ parse_early_param();
+
+ /* Set sparc_cpu_model */
+ sparc_cpu_model = sun_unknown;
+ if (!strcmp(&cputypval,"sun4 "))
+ sparc_cpu_model = sun4;
+ if (!strcmp(&cputypval,"sun4c"))
+ sparc_cpu_model = sun4c;
+ if (!strcmp(&cputypval,"sun4m"))
+ sparc_cpu_model = sun4m;
+ if (!strcmp(&cputypval,"sun4s"))
+ sparc_cpu_model = sun4m; /* CP-1200 with PROM 2.30 -E */
+ if (!strcmp(&cputypval,"sun4d"))
+ sparc_cpu_model = sun4d;
+ if (!strcmp(&cputypval,"sun4e"))
+ sparc_cpu_model = sun4e;
+ if (!strcmp(&cputypval,"sun4u"))
+ sparc_cpu_model = sun4u;
+
+ printk("ARCH: ");
+ switch(sparc_cpu_model) {
+ case sun4:
+ printk("SUN4\n");
+ break;
+ case sun4c:
+ printk("SUN4C\n");
+ break;
+ case sun4m:
+ printk("SUN4M\n");
+ break;
+ case sun4d:
+ printk("SUN4D\n");
+ break;
+ case sun4e:
+ printk("SUN4E\n");
+ break;
+ case sun4u:
+ printk("SUN4U\n");
+ break;
+ default:
+ printk("UNKNOWN!\n");
+ break;
+ };
+
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#elif defined(CONFIG_PROM_CONSOLE)
+ conswitchp = &prom_con;
+#endif
+ boot_flags_init(*cmdline_p);
+
+ idprom_init();
+ if (ARCH_SUN4C)
+ sun4c_probe_vac();
+ load_mmu();
+
+ phys_base = 0xffffffffUL;
+ highest_paddr = 0UL;
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ unsigned long top;
+
+ if (sp_banks[i].base_addr < phys_base)
+ phys_base = sp_banks[i].base_addr;
+ top = sp_banks[i].base_addr +
+ sp_banks[i].num_bytes;
+ if (highest_paddr < top)
+ highest_paddr = top;
+ }
+ pfn_base = phys_base >> PAGE_SHIFT;
+
+ if (!root_flags)
+ root_mountflags &= ~MS_RDONLY;
+ ROOT_DEV = old_decode_dev(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
+#endif
+
+ prom_setsync(prom_sync_me);
+
+ if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) &&
+ ((*(short *)linux_dbvec) != -1)) {
+ printk("Booted under KADB. Syncing trap table.\n");
+ (*(linux_dbvec->teach_debugger))();
+ }
+
+ init_mm.context = (unsigned long) NO_CONTEXT;
+ init_task.thread.kregs = &fake_swapper_regs;
+
+ paging_init();
+
+ smp_setup_cpu_possible_map();
+}
+
+static int ncpus_probed;
+
+static int show_cpuinfo(struct seq_file *m, void *__unused)
+{
+ seq_printf(m,
+ "cpu\t\t: %s\n"
+ "fpu\t\t: %s\n"
+ "promlib\t\t: Version %d Revision %d\n"
+ "prom\t\t: %d.%d\n"
+ "type\t\t: %s\n"
+ "ncpus probed\t: %d\n"
+ "ncpus active\t: %d\n"
+#ifndef CONFIG_SMP
+ "CPU0Bogo\t: %lu.%02lu\n"
+ "CPU0ClkTck\t: %ld\n"
+#endif
+ ,
+ sparc_cpu_type,
+ sparc_fpu_type ,
+ romvec->pv_romvers,
+ prom_rev,
+ romvec->pv_printrev >> 16,
+ romvec->pv_printrev & 0xffff,
+ &cputypval,
+ ncpus_probed,
+ num_online_cpus()
+#ifndef CONFIG_SMP
+ , cpu_data(0).udelay_val/(500000/HZ),
+ (cpu_data(0).udelay_val/(5000/HZ)) % 100,
+ cpu_data(0).clock_tick
+#endif
+ );
+
+#ifdef CONFIG_SMP
+ smp_bogo(m);
+#endif
+ mmu_info(m);
+#ifdef CONFIG_SMP
+ smp_info(m);
+#endif
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ /* The pointer we are returning is arbitrary,
+ * it just has to be non-NULL and not IS_ERR
+ * in the success case.
+ */
+ return *pos == 0 ? &c_start : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+
+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 = show_cpuinfo,
+};
+
+extern int stop_a_enabled;
+
+void sun_do_break(void)
+{
+ if (!stop_a_enabled)
+ return;
+
+ printk("\n");
+ flush_user_windows();
+
+ prom_cmdline();
+}
+
+int stop_a_enabled = 1;
+
+static int __init topology_init(void)
+{
+ int i, ncpus, err;
+
+ /* Count the number of physically present processors in
+ * the machine, even on uniprocessor, so that /proc/cpuinfo
+ * output is consistent with 2.4.x
+ */
+ ncpus = 0;
+ while (!cpu_find_by_instance(ncpus, NULL, NULL))
+ ncpus++;
+ ncpus_probed = ncpus;
+
+ err = 0;
+ for_each_online_cpu(i) {
+ struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ err = -ENOMEM;
+ else
+ register_cpu(p, i);
+ }
+
+ return err;
+}
+
+subsys_initcall(topology_init);
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
new file mode 100644
index 000000000000..555db7452ebe
--- /dev/null
+++ b/arch/sparc/kernel/setup_64.c
@@ -0,0 +1,429 @@
+/*
+ * linux/arch/sparc64/kernel/setup.c
+ *
+ * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <asm/smp.h>
+#include <linux/user.h>
+#include <linux/screen_info.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/syscalls.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/inet.h>
+#include <linux/console.h>
+#include <linux/root_dev.h>
+#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <linux/initrd.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/idprom.h>
+#include <asm/head.h>
+#include <asm/starfire.h>
+#include <asm/mmu_context.h>
+#include <asm/timer.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/mmu.h>
+#include <asm/ns87303.h>
+
+#ifdef CONFIG_IP_PNP
+#include <net/ipconfig.h>
+#endif
+
+#include "entry.h"
+#include "kernel.h"
+
+/* Used to synchronize accesses to NatSemi SUPER I/O chip configure
+ * operations in asm/ns87303.h
+ */
+DEFINE_SPINLOCK(ns87303_lock);
+
+struct screen_info screen_info = {
+ 0, 0, /* orig-x, orig-y */
+ 0, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 128, /* orig-video-cols */
+ 0, 0, 0, /* unused, ega_bx, unused */
+ 54, /* orig-video-lines */
+ 0, /* orig-video-isVGA */
+ 16 /* orig-video-points */
+};
+
+static void
+prom_console_write(struct console *con, const char *s, unsigned n)
+{
+ prom_write(s, n);
+}
+
+/* Exported for mm/init.c:paging_init. */
+unsigned long cmdline_memory_size = 0;
+
+static struct console prom_early_console = {
+ .name = "earlyprom",
+ .write = prom_console_write,
+ .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
+ .index = -1,
+};
+
+/*
+ * Process kernel command line switches that are specific to the
+ * SPARC or that require special low-level processing.
+ */
+static void __init process_switch(char c)
+{
+ switch (c) {
+ case 'd':
+ case 's':
+ break;
+ case 'h':
+ prom_printf("boot_flags_init: Halt!\n");
+ prom_halt();
+ break;
+ case 'p':
+ /* Just ignore, this behavior is now the default. */
+ break;
+ case 'P':
+ /* Force UltraSPARC-III P-Cache on. */
+ if (tlb_type != cheetah) {
+ printk("BOOT: Ignoring P-Cache force option.\n");
+ break;
+ }
+ cheetah_pcache_forced_on = 1;
+ add_taint(TAINT_MACHINE_CHECK);
+ cheetah_enable_pcache();
+ break;
+
+ default:
+ printk("Unknown boot switch (-%c)\n", c);
+ break;
+ }
+}
+
+static void __init boot_flags_init(char *commands)
+{
+ while (*commands) {
+ /* Move to the start of the next "argument". */
+ while (*commands && *commands == ' ')
+ commands++;
+
+ /* Process any command switches, otherwise skip it. */
+ if (*commands == '\0')
+ break;
+ if (*commands == '-') {
+ commands++;
+ while (*commands && *commands != ' ')
+ process_switch(*commands++);
+ continue;
+ }
+ if (!strncmp(commands, "mem=", 4)) {
+ /*
+ * "mem=XXX[kKmM]" overrides the PROM-reported
+ * memory size.
+ */
+ cmdline_memory_size = simple_strtoul(commands + 4,
+ &commands, 0);
+ if (*commands == 'K' || *commands == 'k') {
+ cmdline_memory_size <<= 10;
+ commands++;
+ } else if (*commands=='M' || *commands=='m') {
+ cmdline_memory_size <<= 20;
+ commands++;
+ }
+ }
+ while (*commands && *commands != ' ')
+ commands++;
+ }
+}
+
+extern unsigned short root_flags;
+extern unsigned short root_dev;
+extern unsigned short ram_flags;
+#define RAMDISK_IMAGE_START_MASK 0x07FF
+#define RAMDISK_PROMPT_FLAG 0x8000
+#define RAMDISK_LOAD_FLAG 0x4000
+
+extern int root_mountflags;
+
+char reboot_command[COMMAND_LINE_SIZE];
+
+static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 };
+
+void __init per_cpu_patch(void)
+{
+ struct cpuid_patch_entry *p;
+ unsigned long ver;
+ int is_jbus;
+
+ if (tlb_type == spitfire && !this_is_starfire)
+ return;
+
+ is_jbus = 0;
+ if (tlb_type != hypervisor) {
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ is_jbus = ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID);
+ }
+
+ p = &__cpuid_patch;
+ while (p < &__cpuid_patch_end) {
+ unsigned long addr = p->addr;
+ unsigned int *insns;
+
+ switch (tlb_type) {
+ case spitfire:
+ insns = &p->starfire[0];
+ break;
+ case cheetah:
+ case cheetah_plus:
+ if (is_jbus)
+ insns = &p->cheetah_jbus[0];
+ else
+ insns = &p->cheetah_safari[0];
+ break;
+ case hypervisor:
+ insns = &p->sun4v[0];
+ break;
+ default:
+ prom_printf("Unknown cpu type, halting.\n");
+ prom_halt();
+ };
+
+ *(unsigned int *) (addr + 0) = insns[0];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 0));
+
+ *(unsigned int *) (addr + 4) = insns[1];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 4));
+
+ *(unsigned int *) (addr + 8) = insns[2];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 8));
+
+ *(unsigned int *) (addr + 12) = insns[3];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 12));
+
+ p++;
+ }
+}
+
+void __init sun4v_patch(void)
+{
+ extern void sun4v_hvapi_init(void);
+ struct sun4v_1insn_patch_entry *p1;
+ struct sun4v_2insn_patch_entry *p2;
+
+ if (tlb_type != hypervisor)
+ return;
+
+ p1 = &__sun4v_1insn_patch;
+ while (p1 < &__sun4v_1insn_patch_end) {
+ unsigned long addr = p1->addr;
+
+ *(unsigned int *) (addr + 0) = p1->insn;
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 0));
+
+ p1++;
+ }
+
+ p2 = &__sun4v_2insn_patch;
+ while (p2 < &__sun4v_2insn_patch_end) {
+ unsigned long addr = p2->addr;
+
+ *(unsigned int *) (addr + 0) = p2->insns[0];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 0));
+
+ *(unsigned int *) (addr + 4) = p2->insns[1];
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (addr + 4));
+
+ p2++;
+ }
+
+ sun4v_hvapi_init();
+}
+
+#ifdef CONFIG_SMP
+void __init boot_cpu_id_too_large(int cpu)
+{
+ prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n",
+ cpu, NR_CPUS);
+ prom_halt();
+}
+#endif
+
+void __init setup_arch(char **cmdline_p)
+{
+ /* Initialize PROM console and command line. */
+ *cmdline_p = prom_getbootargs();
+ strcpy(boot_command_line, *cmdline_p);
+ parse_early_param();
+
+ boot_flags_init(*cmdline_p);
+ register_console(&prom_early_console);
+
+ if (tlb_type == hypervisor)
+ printk("ARCH: SUN4V\n");
+ else
+ printk("ARCH: SUN4U\n");
+
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#elif defined(CONFIG_PROM_CONSOLE)
+ conswitchp = &prom_con;
+#endif
+
+ idprom_init();
+
+ if (!root_flags)
+ root_mountflags &= ~MS_RDONLY;
+ ROOT_DEV = old_decode_dev(root_dev);
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
+#endif
+
+ task_thread_info(&init_task)->kregs = &fake_swapper_regs;
+
+#ifdef CONFIG_IP_PNP
+ if (!ic_set_manually) {
+ int chosen = prom_finddevice ("/chosen");
+ u32 cl, sv, gw;
+
+ cl = prom_getintdefault (chosen, "client-ip", 0);
+ sv = prom_getintdefault (chosen, "server-ip", 0);
+ gw = prom_getintdefault (chosen, "gateway-ip", 0);
+ if (cl && sv) {
+ ic_myaddr = cl;
+ ic_servaddr = sv;
+ if (gw)
+ ic_gateway = gw;
+#if defined(CONFIG_IP_PNP_BOOTP) || defined(CONFIG_IP_PNP_RARP)
+ ic_proto_enabled = 0;
+#endif
+ }
+ }
+#endif
+
+ /* Get boot processor trap_block[] setup. */
+ init_cur_cpu_trap(current_thread_info());
+
+ paging_init();
+}
+
+/* BUFFER is PAGE_SIZE bytes long. */
+
+extern void smp_info(struct seq_file *);
+extern void smp_bogo(struct seq_file *);
+extern void mmu_info(struct seq_file *);
+
+unsigned int dcache_parity_tl1_occurred;
+unsigned int icache_parity_tl1_occurred;
+
+int ncpus_probed;
+
+static int show_cpuinfo(struct seq_file *m, void *__unused)
+{
+ seq_printf(m,
+ "cpu\t\t: %s\n"
+ "fpu\t\t: %s\n"
+ "prom\t\t: %s\n"
+ "type\t\t: %s\n"
+ "ncpus probed\t: %d\n"
+ "ncpus active\t: %d\n"
+ "D$ parity tl1\t: %u\n"
+ "I$ parity tl1\t: %u\n"
+#ifndef CONFIG_SMP
+ "Cpu0ClkTck\t: %016lx\n"
+#endif
+ ,
+ sparc_cpu_type,
+ sparc_fpu_type,
+ prom_version,
+ ((tlb_type == hypervisor) ?
+ "sun4v" :
+ "sun4u"),
+ ncpus_probed,
+ num_online_cpus(),
+ dcache_parity_tl1_occurred,
+ icache_parity_tl1_occurred
+#ifndef CONFIG_SMP
+ , cpu_data(0).clock_tick
+#endif
+ );
+#ifdef CONFIG_SMP
+ smp_bogo(m);
+#endif
+ mmu_info(m);
+#ifdef CONFIG_SMP
+ smp_info(m);
+#endif
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ /* The pointer we are returning is arbitrary,
+ * it just has to be non-NULL and not IS_ERR
+ * in the success case.
+ */
+ return *pos == 0 ? &c_start : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+
+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 = show_cpuinfo,
+};
+
+extern int stop_a_enabled;
+
+void sun_do_break(void)
+{
+ if (!stop_a_enabled)
+ return;
+
+ prom_printf("\n");
+ flush_user_windows();
+
+ prom_cmdline();
+}
+
+int stop_a_enabled = 1;
diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index ba5b09ad6666..ba5b09ad6666 100644
--- a/arch/sparc64/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal_32.c
index c94f91c8b6e0..c94f91c8b6e0 100644
--- a/arch/sparc/kernel/signal.c
+++ b/arch/sparc/kernel/signal_32.c
diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc/kernel/signal_64.c
index ec82d76dc6f2..ec82d76dc6f2 100644
--- a/arch/sparc64/kernel/signal.c
+++ b/arch/sparc/kernel/signal_64.c
diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp_32.c
index 1e5ac4e282e1..1e5ac4e282e1 100644
--- a/arch/sparc/kernel/smp.c
+++ b/arch/sparc/kernel/smp_32.c
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
new file mode 100644
index 000000000000..46329799f346
--- /dev/null
+++ b/arch/sparc/kernel/smp_64.c
@@ -0,0 +1,1408 @@
+/* smp.c: Sparc64 SMP support.
+ *
+ * Copyright (C) 1997, 2007, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/threads.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/cache.h>
+#include <linux/jiffies.h>
+#include <linux/profile.h>
+#include <linux/lmb.h>
+#include <linux/cpu.h>
+
+#include <asm/head.h>
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
+#include <asm/cpudata.h>
+#include <asm/hvtramp.h>
+#include <asm/io.h>
+#include <asm/timer.h>
+
+#include <asm/irq.h>
+#include <asm/irq_regs.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+#include <asm/starfire.h>
+#include <asm/tlb.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/mdesc.h>
+#include <asm/ldc.h>
+#include <asm/hypervisor.h>
+
+int sparc64_multi_core __read_mostly;
+
+DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE;
+cpumask_t cpu_core_map[NR_CPUS] __read_mostly =
+ { [0 ... NR_CPUS-1] = CPU_MASK_NONE };
+
+EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);
+EXPORT_SYMBOL(cpu_core_map);
+
+static cpumask_t smp_commenced_mask;
+
+void smp_info(struct seq_file *m)
+{
+ int i;
+
+ seq_printf(m, "State:\n");
+ for_each_online_cpu(i)
+ seq_printf(m, "CPU%d:\t\tonline\n", i);
+}
+
+void smp_bogo(struct seq_file *m)
+{
+ int i;
+
+ for_each_online_cpu(i)
+ seq_printf(m,
+ "Cpu%dClkTck\t: %016lx\n",
+ i, cpu_data(i).clock_tick);
+}
+
+extern void setup_sparc64_timer(void);
+
+static volatile unsigned long callin_flag = 0;
+
+void __cpuinit smp_callin(void)
+{
+ int cpuid = hard_smp_processor_id();
+
+ __local_per_cpu_offset = __per_cpu_offset(cpuid);
+
+ if (tlb_type == hypervisor)
+ sun4v_ktsb_register();
+
+ __flush_tlb_all();
+
+ setup_sparc64_timer();
+
+ if (cheetah_pcache_forced_on)
+ cheetah_enable_pcache();
+
+ local_irq_enable();
+
+ callin_flag = 1;
+ __asm__ __volatile__("membar #Sync\n\t"
+ "flush %%g6" : : : "memory");
+
+ /* Clear this or we will die instantly when we
+ * schedule back to this idler...
+ */
+ current_thread_info()->new_child = 0;
+
+ /* Attach to the address space of init_task. */
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+
+ /* inform the notifiers about the new cpu */
+ notify_cpu_starting(cpuid);
+
+ while (!cpu_isset(cpuid, smp_commenced_mask))
+ rmb();
+
+ ipi_call_lock();
+ cpu_set(cpuid, cpu_online_map);
+ ipi_call_unlock();
+
+ /* idle thread is expected to have preempt disabled */
+ preempt_disable();
+}
+
+void cpu_panic(void)
+{
+ printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id());
+ panic("SMP bolixed\n");
+}
+
+/* This tick register synchronization scheme is taken entirely from
+ * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit.
+ *
+ * The only change I've made is to rework it so that the master
+ * initiates the synchonization instead of the slave. -DaveM
+ */
+
+#define MASTER 0
+#define SLAVE (SMP_CACHE_BYTES/sizeof(unsigned long))
+
+#define NUM_ROUNDS 64 /* magic value */
+#define NUM_ITERS 5 /* likewise */
+
+static DEFINE_SPINLOCK(itc_sync_lock);
+static unsigned long go[SLAVE + 1];
+
+#define DEBUG_TICK_SYNC 0
+
+static inline long get_delta (long *rt, long *master)
+{
+ unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
+ unsigned long tcenter, t0, t1, tm;
+ unsigned long i;
+
+ for (i = 0; i < NUM_ITERS; i++) {
+ t0 = tick_ops->get_tick();
+ go[MASTER] = 1;
+ membar_safe("#StoreLoad");
+ while (!(tm = go[SLAVE]))
+ rmb();
+ go[SLAVE] = 0;
+ wmb();
+ t1 = tick_ops->get_tick();
+
+ if (t1 - t0 < best_t1 - best_t0)
+ best_t0 = t0, best_t1 = t1, best_tm = tm;
+ }
+
+ *rt = best_t1 - best_t0;
+ *master = best_tm - best_t0;
+
+ /* average best_t0 and best_t1 without overflow: */
+ tcenter = (best_t0/2 + best_t1/2);
+ if (best_t0 % 2 + best_t1 % 2 == 2)
+ tcenter++;
+ return tcenter - best_tm;
+}
+
+void smp_synchronize_tick_client(void)
+{
+ long i, delta, adj, adjust_latency = 0, done = 0;
+ unsigned long flags, rt, master_time_stamp, bound;
+#if DEBUG_TICK_SYNC
+ struct {
+ long rt; /* roundtrip time */
+ long master; /* master's timestamp */
+ long diff; /* difference between midpoint and master's timestamp */
+ long lat; /* estimate of itc adjustment latency */
+ } t[NUM_ROUNDS];
+#endif
+
+ go[MASTER] = 1;
+
+ while (go[MASTER])
+ rmb();
+
+ local_irq_save(flags);
+ {
+ for (i = 0; i < NUM_ROUNDS; i++) {
+ delta = get_delta(&rt, &master_time_stamp);
+ if (delta == 0) {
+ done = 1; /* let's lock on to this... */
+ bound = rt;
+ }
+
+ if (!done) {
+ if (i > 0) {
+ adjust_latency += -delta;
+ adj = -delta + adjust_latency/4;
+ } else
+ adj = -delta;
+
+ tick_ops->add_tick(adj);
+ }
+#if DEBUG_TICK_SYNC
+ t[i].rt = rt;
+ t[i].master = master_time_stamp;
+ t[i].diff = delta;
+ t[i].lat = adjust_latency/4;
+#endif
+ }
+ }
+ local_irq_restore(flags);
+
+#if DEBUG_TICK_SYNC
+ for (i = 0; i < NUM_ROUNDS; i++)
+ printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
+ t[i].rt, t[i].master, t[i].diff, t[i].lat);
+#endif
+
+ printk(KERN_INFO "CPU %d: synchronized TICK with master CPU "
+ "(last diff %ld cycles, maxerr %lu cycles)\n",
+ smp_processor_id(), delta, rt);
+}
+
+static void smp_start_sync_tick_client(int cpu);
+
+static void smp_synchronize_one_tick(int cpu)
+{
+ unsigned long flags, i;
+
+ go[MASTER] = 0;
+
+ smp_start_sync_tick_client(cpu);
+
+ /* wait for client to be ready */
+ while (!go[MASTER])
+ rmb();
+
+ /* now let the client proceed into his loop */
+ go[MASTER] = 0;
+ membar_safe("#StoreLoad");
+
+ spin_lock_irqsave(&itc_sync_lock, flags);
+ {
+ for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) {
+ while (!go[MASTER])
+ rmb();
+ go[MASTER] = 0;
+ wmb();
+ go[SLAVE] = tick_ops->get_tick();
+ membar_safe("#StoreLoad");
+ }
+ }
+ spin_unlock_irqrestore(&itc_sync_lock, flags);
+}
+
+#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
+/* XXX Put this in some common place. XXX */
+static unsigned long kimage_addr_to_ra(void *p)
+{
+ unsigned long val = (unsigned long) p;
+
+ return kern_base + (val - KERNBASE);
+}
+
+static void __cpuinit ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg)
+{
+ extern unsigned long sparc64_ttable_tl0;
+ extern unsigned long kern_locked_tte_data;
+ struct hvtramp_descr *hdesc;
+ unsigned long trampoline_ra;
+ struct trap_per_cpu *tb;
+ u64 tte_vaddr, tte_data;
+ unsigned long hv_err;
+ int i;
+
+ hdesc = kzalloc(sizeof(*hdesc) +
+ (sizeof(struct hvtramp_mapping) *
+ num_kernel_image_mappings - 1),
+ GFP_KERNEL);
+ if (!hdesc) {
+ printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate "
+ "hvtramp_descr.\n");
+ return;
+ }
+
+ hdesc->cpu = cpu;
+ hdesc->num_mappings = num_kernel_image_mappings;
+
+ tb = &trap_block[cpu];
+ tb->hdesc = hdesc;
+
+ hdesc->fault_info_va = (unsigned long) &tb->fault_info;
+ hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info);
+
+ hdesc->thread_reg = thread_reg;
+
+ tte_vaddr = (unsigned long) KERNBASE;
+ tte_data = kern_locked_tte_data;
+
+ for (i = 0; i < hdesc->num_mappings; i++) {
+ hdesc->maps[i].vaddr = tte_vaddr;
+ hdesc->maps[i].tte = tte_data;
+ tte_vaddr += 0x400000;
+ tte_data += 0x400000;
+ }
+
+ trampoline_ra = kimage_addr_to_ra(hv_cpu_startup);
+
+ hv_err = sun4v_cpu_start(cpu, trampoline_ra,
+ kimage_addr_to_ra(&sparc64_ttable_tl0),
+ __pa(hdesc));
+ if (hv_err)
+ printk(KERN_ERR "ldom_startcpu_cpuid: sun4v_cpu_start() "
+ "gives error %lu\n", hv_err);
+}
+#endif
+
+extern unsigned long sparc64_cpu_startup;
+
+/* The OBP cpu startup callback truncates the 3rd arg cookie to
+ * 32-bits (I think) so to be safe we have it read the pointer
+ * contained here so we work on >4GB machines. -DaveM
+ */
+static struct thread_info *cpu_new_thread = NULL;
+
+static int __cpuinit smp_boot_one_cpu(unsigned int cpu)
+{
+ struct trap_per_cpu *tb = &trap_block[cpu];
+ unsigned long entry =
+ (unsigned long)(&sparc64_cpu_startup);
+ unsigned long cookie =
+ (unsigned long)(&cpu_new_thread);
+ struct task_struct *p;
+ int timeout, ret;
+
+ p = fork_idle(cpu);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ callin_flag = 0;
+ cpu_new_thread = task_thread_info(p);
+
+ if (tlb_type == hypervisor) {
+#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
+ if (ldom_domaining_enabled)
+ ldom_startcpu_cpuid(cpu,
+ (unsigned long) cpu_new_thread);
+ else
+#endif
+ prom_startcpu_cpuid(cpu, entry, cookie);
+ } else {
+ struct device_node *dp = of_find_node_by_cpuid(cpu);
+
+ prom_startcpu(dp->node, entry, cookie);
+ }
+
+ for (timeout = 0; timeout < 50000; timeout++) {
+ if (callin_flag)
+ break;
+ udelay(100);
+ }
+
+ if (callin_flag) {
+ ret = 0;
+ } else {
+ printk("Processor %d is stuck.\n", cpu);
+ ret = -ENODEV;
+ }
+ cpu_new_thread = NULL;
+
+ if (tb->hdesc) {
+ kfree(tb->hdesc);
+ tb->hdesc = NULL;
+ }
+
+ return ret;
+}
+
+static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
+{
+ u64 result, target;
+ int stuck, tmp;
+
+ if (this_is_starfire) {
+ /* map to real upaid */
+ cpu = (((cpu & 0x3c) << 1) |
+ ((cpu & 0x40) >> 4) |
+ (cpu & 0x3));
+ }
+
+ target = (cpu << 14) | 0x70;
+again:
+ /* Ok, this is the real Spitfire Errata #54.
+ * One must read back from a UDB internal register
+ * after writes to the UDB interrupt dispatch, but
+ * before the membar Sync for that write.
+ * So we use the high UDB control register (ASI 0x7f,
+ * ADDR 0x20) for the dummy read. -DaveM
+ */
+ tmp = 0x40;
+ __asm__ __volatile__(
+ "wrpr %1, %2, %%pstate\n\t"
+ "stxa %4, [%0] %3\n\t"
+ "stxa %5, [%0+%8] %3\n\t"
+ "add %0, %8, %0\n\t"
+ "stxa %6, [%0+%8] %3\n\t"
+ "membar #Sync\n\t"
+ "stxa %%g0, [%7] %3\n\t"
+ "membar #Sync\n\t"
+ "mov 0x20, %%g1\n\t"
+ "ldxa [%%g1] 0x7f, %%g0\n\t"
+ "membar #Sync"
+ : "=r" (tmp)
+ : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_INTR_W),
+ "r" (data0), "r" (data1), "r" (data2), "r" (target),
+ "r" (0x10), "0" (tmp)
+ : "g1");
+
+ /* NOTE: PSTATE_IE is still clear. */
+ stuck = 100000;
+ do {
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (result)
+ : "i" (ASI_INTR_DISPATCH_STAT));
+ if (result == 0) {
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
+ : : "r" (pstate));
+ return;
+ }
+ stuck -= 1;
+ if (stuck == 0)
+ break;
+ } while (result & 0x1);
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
+ : : "r" (pstate));
+ if (stuck == 0) {
+ printk("CPU[%d]: mondo stuckage result[%016lx]\n",
+ smp_processor_id(), result);
+ } else {
+ udelay(2);
+ goto again;
+ }
+}
+
+static void spitfire_xcall_deliver(struct trap_per_cpu *tb, int cnt)
+{
+ u64 *mondo, data0, data1, data2;
+ u16 *cpu_list;
+ u64 pstate;
+ int i;
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+ cpu_list = __va(tb->cpu_list_pa);
+ mondo = __va(tb->cpu_mondo_block_pa);
+ data0 = mondo[0];
+ data1 = mondo[1];
+ data2 = mondo[2];
+ for (i = 0; i < cnt; i++)
+ spitfire_xcall_helper(data0, data1, data2, pstate, cpu_list[i]);
+}
+
+/* Cheetah now allows to send the whole 64-bytes of data in the interrupt
+ * packet, but we have no use for that. However we do take advantage of
+ * the new pipelining feature (ie. dispatch to multiple cpus simultaneously).
+ */
+static void cheetah_xcall_deliver(struct trap_per_cpu *tb, int cnt)
+{
+ int nack_busy_id, is_jbus, need_more;
+ u64 *mondo, pstate, ver, busy_mask;
+ u16 *cpu_list;
+
+ cpu_list = __va(tb->cpu_list_pa);
+ mondo = __va(tb->cpu_mondo_block_pa);
+
+ /* Unfortunately, someone at Sun had the brilliant idea to make the
+ * busy/nack fields hard-coded by ITID number for this Ultra-III
+ * derivative processor.
+ */
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ is_jbus = ((ver >> 32) == __JALAPENO_ID ||
+ (ver >> 32) == __SERRANO_ID);
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+
+retry:
+ need_more = 0;
+ __asm__ __volatile__("wrpr %0, %1, %%pstate\n\t"
+ : : "r" (pstate), "i" (PSTATE_IE));
+
+ /* Setup the dispatch data registers. */
+ __asm__ __volatile__("stxa %0, [%3] %6\n\t"
+ "stxa %1, [%4] %6\n\t"
+ "stxa %2, [%5] %6\n\t"
+ "membar #Sync\n\t"
+ : /* no outputs */
+ : "r" (mondo[0]), "r" (mondo[1]), "r" (mondo[2]),
+ "r" (0x40), "r" (0x50), "r" (0x60),
+ "i" (ASI_INTR_W));
+
+ nack_busy_id = 0;
+ busy_mask = 0;
+ {
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ u64 target, nr;
+
+ nr = cpu_list[i];
+ if (nr == 0xffff)
+ continue;
+
+ target = (nr << 14) | 0x70;
+ if (is_jbus) {
+ busy_mask |= (0x1UL << (nr * 2));
+ } else {
+ target |= (nack_busy_id << 24);
+ busy_mask |= (0x1UL <<
+ (nack_busy_id * 2));
+ }
+ __asm__ __volatile__(
+ "stxa %%g0, [%0] %1\n\t"
+ "membar #Sync\n\t"
+ : /* no outputs */
+ : "r" (target), "i" (ASI_INTR_W));
+ nack_busy_id++;
+ if (nack_busy_id == 32) {
+ need_more = 1;
+ break;
+ }
+ }
+ }
+
+ /* Now, poll for completion. */
+ {
+ u64 dispatch_stat, nack_mask;
+ long stuck;
+
+ stuck = 100000 * nack_busy_id;
+ nack_mask = busy_mask << 1;
+ do {
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (dispatch_stat)
+ : "i" (ASI_INTR_DISPATCH_STAT));
+ if (!(dispatch_stat & (busy_mask | nack_mask))) {
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
+ : : "r" (pstate));
+ if (unlikely(need_more)) {
+ int i, this_cnt = 0;
+ for (i = 0; i < cnt; i++) {
+ if (cpu_list[i] == 0xffff)
+ continue;
+ cpu_list[i] = 0xffff;
+ this_cnt++;
+ if (this_cnt == 32)
+ break;
+ }
+ goto retry;
+ }
+ return;
+ }
+ if (!--stuck)
+ break;
+ } while (dispatch_stat & busy_mask);
+
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
+ : : "r" (pstate));
+
+ if (dispatch_stat & busy_mask) {
+ /* Busy bits will not clear, continue instead
+ * of freezing up on this cpu.
+ */
+ printk("CPU[%d]: mondo stuckage result[%016lx]\n",
+ smp_processor_id(), dispatch_stat);
+ } else {
+ int i, this_busy_nack = 0;
+
+ /* Delay some random time with interrupts enabled
+ * to prevent deadlock.
+ */
+ udelay(2 * nack_busy_id);
+
+ /* Clear out the mask bits for cpus which did not
+ * NACK us.
+ */
+ for (i = 0; i < cnt; i++) {
+ u64 check_mask, nr;
+
+ nr = cpu_list[i];
+ if (nr == 0xffff)
+ continue;
+
+ if (is_jbus)
+ check_mask = (0x2UL << (2*nr));
+ else
+ check_mask = (0x2UL <<
+ this_busy_nack);
+ if ((dispatch_stat & check_mask) == 0)
+ cpu_list[i] = 0xffff;
+ this_busy_nack += 2;
+ if (this_busy_nack == 64)
+ break;
+ }
+
+ goto retry;
+ }
+ }
+}
+
+/* Multi-cpu list version. */
+static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
+{
+ int retries, this_cpu, prev_sent, i, saw_cpu_error;
+ unsigned long status;
+ u16 *cpu_list;
+
+ this_cpu = smp_processor_id();
+
+ cpu_list = __va(tb->cpu_list_pa);
+
+ saw_cpu_error = 0;
+ retries = 0;
+ prev_sent = 0;
+ do {
+ int forward_progress, n_sent;
+
+ status = sun4v_cpu_mondo_send(cnt,
+ tb->cpu_list_pa,
+ tb->cpu_mondo_block_pa);
+
+ /* HV_EOK means all cpus received the xcall, we're done. */
+ if (likely(status == HV_EOK))
+ break;
+
+ /* First, see if we made any forward progress.
+ *
+ * The hypervisor indicates successful sends by setting
+ * cpu list entries to the value 0xffff.
+ */
+ n_sent = 0;
+ for (i = 0; i < cnt; i++) {
+ if (likely(cpu_list[i] == 0xffff))
+ n_sent++;
+ }
+
+ forward_progress = 0;
+ if (n_sent > prev_sent)
+ forward_progress = 1;
+
+ prev_sent = n_sent;
+
+ /* If we get a HV_ECPUERROR, then one or more of the cpus
+ * in the list are in error state. Use the cpu_state()
+ * hypervisor call to find out which cpus are in error state.
+ */
+ if (unlikely(status == HV_ECPUERROR)) {
+ for (i = 0; i < cnt; i++) {
+ long err;
+ u16 cpu;
+
+ cpu = cpu_list[i];
+ if (cpu == 0xffff)
+ continue;
+
+ err = sun4v_cpu_state(cpu);
+ if (err == HV_CPU_STATE_ERROR) {
+ saw_cpu_error = (cpu + 1);
+ cpu_list[i] = 0xffff;
+ }
+ }
+ } else if (unlikely(status != HV_EWOULDBLOCK))
+ goto fatal_mondo_error;
+
+ /* Don't bother rewriting the CPU list, just leave the
+ * 0xffff and non-0xffff entries in there and the
+ * hypervisor will do the right thing.
+ *
+ * Only advance timeout state if we didn't make any
+ * forward progress.
+ */
+ if (unlikely(!forward_progress)) {
+ if (unlikely(++retries > 10000))
+ goto fatal_mondo_timeout;
+
+ /* Delay a little bit to let other cpus catch up
+ * on their cpu mondo queue work.
+ */
+ udelay(2 * cnt);
+ }
+ } while (1);
+
+ if (unlikely(saw_cpu_error))
+ goto fatal_mondo_cpu_error;
+
+ return;
+
+fatal_mondo_cpu_error:
+ printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus "
+ "(including %d) were in error state\n",
+ this_cpu, saw_cpu_error - 1);
+ return;
+
+fatal_mondo_timeout:
+ printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward "
+ " progress after %d retries.\n",
+ this_cpu, retries);
+ goto dump_cpu_list_and_out;
+
+fatal_mondo_error:
+ printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n",
+ this_cpu, status);
+ printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) "
+ "mondo_block_pa(%lx)\n",
+ this_cpu, cnt, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
+
+dump_cpu_list_and_out:
+ printk(KERN_CRIT "CPU[%d]: CPU list [ ", this_cpu);
+ for (i = 0; i < cnt; i++)
+ printk("%u ", cpu_list[i]);
+ printk("]\n");
+}
+
+static void (*xcall_deliver_impl)(struct trap_per_cpu *, int);
+
+static void xcall_deliver(u64 data0, u64 data1, u64 data2, const cpumask_t *mask)
+{
+ struct trap_per_cpu *tb;
+ int this_cpu, i, cnt;
+ unsigned long flags;
+ u16 *cpu_list;
+ u64 *mondo;
+
+ /* We have to do this whole thing with interrupts fully disabled.
+ * Otherwise if we send an xcall from interrupt context it will
+ * corrupt both our mondo block and cpu list state.
+ *
+ * One consequence of this is that we cannot use timeout mechanisms
+ * that depend upon interrupts being delivered locally. So, for
+ * example, we cannot sample jiffies and expect it to advance.
+ *
+ * Fortunately, udelay() uses %stick/%tick so we can use that.
+ */
+ local_irq_save(flags);
+
+ this_cpu = smp_processor_id();
+ tb = &trap_block[this_cpu];
+
+ mondo = __va(tb->cpu_mondo_block_pa);
+ mondo[0] = data0;
+ mondo[1] = data1;
+ mondo[2] = data2;
+ wmb();
+
+ cpu_list = __va(tb->cpu_list_pa);
+
+ /* Setup the initial cpu list. */
+ cnt = 0;
+ for_each_cpu(i, mask) {
+ if (i == this_cpu || !cpu_online(i))
+ continue;
+ cpu_list[cnt++] = i;
+ }
+
+ if (cnt)
+ xcall_deliver_impl(tb, cnt);
+
+ local_irq_restore(flags);
+}
+
+/* Send cross call to all processors mentioned in MASK_P
+ * except self. Really, there are only two cases currently,
+ * "&cpu_online_map" and "&mm->cpu_vm_mask".
+ */
+static void smp_cross_call_masked(unsigned long *func, u32 ctx, u64 data1, u64 data2, const cpumask_t *mask)
+{
+ u64 data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff));
+
+ xcall_deliver(data0, data1, data2, mask);
+}
+
+/* Send cross call to all processors except self. */
+static void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2)
+{
+ smp_cross_call_masked(func, ctx, data1, data2, &cpu_online_map);
+}
+
+extern unsigned long xcall_sync_tick;
+
+static void smp_start_sync_tick_client(int cpu)
+{
+ xcall_deliver((u64) &xcall_sync_tick, 0, 0,
+ &cpumask_of_cpu(cpu));
+}
+
+extern unsigned long xcall_call_function;
+
+void arch_send_call_function_ipi(cpumask_t mask)
+{
+ xcall_deliver((u64) &xcall_call_function, 0, 0, &mask);
+}
+
+extern unsigned long xcall_call_function_single;
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ xcall_deliver((u64) &xcall_call_function_single, 0, 0,
+ &cpumask_of_cpu(cpu));
+}
+
+void smp_call_function_client(int irq, struct pt_regs *regs)
+{
+ clear_softint(1 << irq);
+ generic_smp_call_function_interrupt();
+}
+
+void smp_call_function_single_client(int irq, struct pt_regs *regs)
+{
+ clear_softint(1 << irq);
+ generic_smp_call_function_single_interrupt();
+}
+
+static void tsb_sync(void *info)
+{
+ struct trap_per_cpu *tp = &trap_block[raw_smp_processor_id()];
+ struct mm_struct *mm = info;
+
+ /* It is not valid to test "currrent->active_mm == mm" here.
+ *
+ * The value of "current" is not changed atomically with
+ * switch_mm(). But that's OK, we just need to check the
+ * current cpu's trap block PGD physical address.
+ */
+ if (tp->pgd_paddr == __pa(mm->pgd))
+ tsb_context_switch(mm);
+}
+
+void smp_tsb_sync(struct mm_struct *mm)
+{
+ smp_call_function_mask(mm->cpu_vm_mask, tsb_sync, mm, 1);
+}
+
+extern unsigned long xcall_flush_tlb_mm;
+extern unsigned long xcall_flush_tlb_pending;
+extern unsigned long xcall_flush_tlb_kernel_range;
+extern unsigned long xcall_fetch_glob_regs;
+extern unsigned long xcall_receive_signal;
+extern unsigned long xcall_new_mmu_context_version;
+#ifdef CONFIG_KGDB
+extern unsigned long xcall_kgdb_capture;
+#endif
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+extern unsigned long xcall_flush_dcache_page_cheetah;
+#endif
+extern unsigned long xcall_flush_dcache_page_spitfire;
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+extern atomic_t dcpage_flushes;
+extern atomic_t dcpage_flushes_xcall;
+#endif
+
+static inline void __local_flush_dcache_page(struct page *page)
+{
+#ifdef DCACHE_ALIASING_POSSIBLE
+ __flush_dcache_page(page_address(page),
+ ((tlb_type == spitfire) &&
+ page_mapping(page) != NULL));
+#else
+ if (page_mapping(page) != NULL &&
+ tlb_type == spitfire)
+ __flush_icache_page(__pa(page_address(page)));
+#endif
+}
+
+void smp_flush_dcache_page_impl(struct page *page, int cpu)
+{
+ int this_cpu;
+
+ if (tlb_type == hypervisor)
+ return;
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+ atomic_inc(&dcpage_flushes);
+#endif
+
+ this_cpu = get_cpu();
+
+ if (cpu == this_cpu) {
+ __local_flush_dcache_page(page);
+ } else if (cpu_online(cpu)) {
+ void *pg_addr = page_address(page);
+ u64 data0 = 0;
+
+ if (tlb_type == spitfire) {
+ data0 = ((u64)&xcall_flush_dcache_page_spitfire);
+ if (page_mapping(page) != NULL)
+ data0 |= ((u64)1 << 32);
+ } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+#ifdef DCACHE_ALIASING_POSSIBLE
+ data0 = ((u64)&xcall_flush_dcache_page_cheetah);
+#endif
+ }
+ if (data0) {
+ xcall_deliver(data0, __pa(pg_addr),
+ (u64) pg_addr, &cpumask_of_cpu(cpu));
+#ifdef CONFIG_DEBUG_DCFLUSH
+ atomic_inc(&dcpage_flushes_xcall);
+#endif
+ }
+ }
+
+ put_cpu();
+}
+
+void flush_dcache_page_all(struct mm_struct *mm, struct page *page)
+{
+ void *pg_addr;
+ int this_cpu;
+ u64 data0;
+
+ if (tlb_type == hypervisor)
+ return;
+
+ this_cpu = get_cpu();
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+ atomic_inc(&dcpage_flushes);
+#endif
+ data0 = 0;
+ pg_addr = page_address(page);
+ if (tlb_type == spitfire) {
+ data0 = ((u64)&xcall_flush_dcache_page_spitfire);
+ if (page_mapping(page) != NULL)
+ data0 |= ((u64)1 << 32);
+ } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+#ifdef DCACHE_ALIASING_POSSIBLE
+ data0 = ((u64)&xcall_flush_dcache_page_cheetah);
+#endif
+ }
+ if (data0) {
+ xcall_deliver(data0, __pa(pg_addr),
+ (u64) pg_addr, &cpu_online_map);
+#ifdef CONFIG_DEBUG_DCFLUSH
+ atomic_inc(&dcpage_flushes_xcall);
+#endif
+ }
+ __local_flush_dcache_page(page);
+
+ put_cpu();
+}
+
+void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs)
+{
+ struct mm_struct *mm;
+ unsigned long flags;
+
+ clear_softint(1 << irq);
+
+ /* See if we need to allocate a new TLB context because
+ * the version of the one we are using is now out of date.
+ */
+ mm = current->active_mm;
+ if (unlikely(!mm || (mm == &init_mm)))
+ return;
+
+ spin_lock_irqsave(&mm->context.lock, flags);
+
+ if (unlikely(!CTX_VALID(mm->context)))
+ get_new_mmu_context(mm);
+
+ spin_unlock_irqrestore(&mm->context.lock, flags);
+
+ load_secondary_context(mm);
+ __flush_tlb_mm(CTX_HWBITS(mm->context),
+ SECONDARY_CONTEXT);
+}
+
+void smp_new_mmu_context_version(void)
+{
+ smp_cross_call(&xcall_new_mmu_context_version, 0, 0, 0);
+}
+
+#ifdef CONFIG_KGDB
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ smp_cross_call(&xcall_kgdb_capture, 0, 0, 0);
+}
+#endif
+
+void smp_fetch_global_regs(void)
+{
+ smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
+}
+
+/* We know that the window frames of the user have been flushed
+ * to the stack before we get here because all callers of us
+ * are flush_tlb_*() routines, and these run after flush_cache_*()
+ * which performs the flushw.
+ *
+ * The SMP TLB coherency scheme we use works as follows:
+ *
+ * 1) mm->cpu_vm_mask is a bit mask of which cpus an address
+ * space has (potentially) executed on, this is the heuristic
+ * we use to avoid doing cross calls.
+ *
+ * Also, for flushing from kswapd and also for clones, we
+ * use cpu_vm_mask as the list of cpus to make run the TLB.
+ *
+ * 2) TLB context numbers are shared globally across all processors
+ * in the system, this allows us to play several games to avoid
+ * cross calls.
+ *
+ * One invariant is that when a cpu switches to a process, and
+ * that processes tsk->active_mm->cpu_vm_mask does not have the
+ * current cpu's bit set, that tlb context is flushed locally.
+ *
+ * If the address space is non-shared (ie. mm->count == 1) we avoid
+ * cross calls when we want to flush the currently running process's
+ * tlb state. This is done by clearing all cpu bits except the current
+ * processor's in current->active_mm->cpu_vm_mask and performing the
+ * flush locally only. This will force any subsequent cpus which run
+ * this task to flush the context from the local tlb if the process
+ * migrates to another cpu (again).
+ *
+ * 3) For shared address spaces (threads) and swapping we bite the
+ * bullet for most cases and perform the cross call (but only to
+ * the cpus listed in cpu_vm_mask).
+ *
+ * The performance gain from "optimizing" away the cross call for threads is
+ * questionable (in theory the big win for threads is the massive sharing of
+ * address space state across processors).
+ */
+
+/* This currently is only used by the hugetlb arch pre-fault
+ * hook on UltraSPARC-III+ and later when changing the pagesize
+ * bits of the context register for an address space.
+ */
+void smp_flush_tlb_mm(struct mm_struct *mm)
+{
+ u32 ctx = CTX_HWBITS(mm->context);
+ int cpu = get_cpu();
+
+ if (atomic_read(&mm->mm_users) == 1) {
+ mm->cpu_vm_mask = cpumask_of_cpu(cpu);
+ goto local_flush_and_out;
+ }
+
+ smp_cross_call_masked(&xcall_flush_tlb_mm,
+ ctx, 0, 0,
+ &mm->cpu_vm_mask);
+
+local_flush_and_out:
+ __flush_tlb_mm(ctx, SECONDARY_CONTEXT);
+
+ put_cpu();
+}
+
+void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long *vaddrs)
+{
+ u32 ctx = CTX_HWBITS(mm->context);
+ int cpu = get_cpu();
+
+ if (mm == current->active_mm && atomic_read(&mm->mm_users) == 1)
+ mm->cpu_vm_mask = cpumask_of_cpu(cpu);
+ else
+ smp_cross_call_masked(&xcall_flush_tlb_pending,
+ ctx, nr, (unsigned long) vaddrs,
+ &mm->cpu_vm_mask);
+
+ __flush_tlb_pending(ctx, nr, vaddrs);
+
+ put_cpu();
+}
+
+void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ start &= PAGE_MASK;
+ end = PAGE_ALIGN(end);
+ if (start != end) {
+ smp_cross_call(&xcall_flush_tlb_kernel_range,
+ 0, start, end);
+
+ __flush_tlb_kernel_range(start, end);
+ }
+}
+
+/* CPU capture. */
+/* #define CAPTURE_DEBUG */
+extern unsigned long xcall_capture;
+
+static atomic_t smp_capture_depth = ATOMIC_INIT(0);
+static atomic_t smp_capture_registry = ATOMIC_INIT(0);
+static unsigned long penguins_are_doing_time;
+
+void smp_capture(void)
+{
+ int result = atomic_add_ret(1, &smp_capture_depth);
+
+ if (result == 1) {
+ int ncpus = num_online_cpus();
+
+#ifdef CAPTURE_DEBUG
+ printk("CPU[%d]: Sending penguins to jail...",
+ smp_processor_id());
+#endif
+ penguins_are_doing_time = 1;
+ atomic_inc(&smp_capture_registry);
+ smp_cross_call(&xcall_capture, 0, 0, 0);
+ while (atomic_read(&smp_capture_registry) != ncpus)
+ rmb();
+#ifdef CAPTURE_DEBUG
+ printk("done\n");
+#endif
+ }
+}
+
+void smp_release(void)
+{
+ if (atomic_dec_and_test(&smp_capture_depth)) {
+#ifdef CAPTURE_DEBUG
+ printk("CPU[%d]: Giving pardon to "
+ "imprisoned penguins\n",
+ smp_processor_id());
+#endif
+ penguins_are_doing_time = 0;
+ membar_safe("#StoreLoad");
+ atomic_dec(&smp_capture_registry);
+ }
+}
+
+/* Imprisoned penguins run with %pil == PIL_NORMAL_MAX, but PSTATE_IE
+ * set, so they can service tlb flush xcalls...
+ */
+extern void prom_world(int);
+
+void smp_penguin_jailcell(int irq, struct pt_regs *regs)
+{
+ clear_softint(1 << irq);
+
+ preempt_disable();
+
+ __asm__ __volatile__("flushw");
+ prom_world(1);
+ atomic_inc(&smp_capture_registry);
+ membar_safe("#StoreLoad");
+ while (penguins_are_doing_time)
+ rmb();
+ atomic_dec(&smp_capture_registry);
+ prom_world(0);
+
+ preempt_enable();
+}
+
+/* /proc/profile writes can call this, don't __init it please. */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return -EINVAL;
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+}
+
+void __devinit smp_prepare_boot_cpu(void)
+{
+}
+
+void __init smp_setup_processor_id(void)
+{
+ if (tlb_type == spitfire)
+ xcall_deliver_impl = spitfire_xcall_deliver;
+ else if (tlb_type == cheetah || tlb_type == cheetah_plus)
+ xcall_deliver_impl = cheetah_xcall_deliver;
+ else
+ xcall_deliver_impl = hypervisor_xcall_deliver;
+}
+
+void __devinit smp_fill_in_sib_core_maps(void)
+{
+ unsigned int i;
+
+ for_each_present_cpu(i) {
+ unsigned int j;
+
+ cpus_clear(cpu_core_map[i]);
+ if (cpu_data(i).core_id == 0) {
+ cpu_set(i, cpu_core_map[i]);
+ continue;
+ }
+
+ for_each_present_cpu(j) {
+ if (cpu_data(i).core_id ==
+ cpu_data(j).core_id)
+ cpu_set(j, cpu_core_map[i]);
+ }
+ }
+
+ for_each_present_cpu(i) {
+ unsigned int j;
+
+ cpus_clear(per_cpu(cpu_sibling_map, i));
+ if (cpu_data(i).proc_id == -1) {
+ cpu_set(i, per_cpu(cpu_sibling_map, i));
+ continue;
+ }
+
+ for_each_present_cpu(j) {
+ if (cpu_data(i).proc_id ==
+ cpu_data(j).proc_id)
+ cpu_set(j, per_cpu(cpu_sibling_map, i));
+ }
+ }
+}
+
+int __cpuinit __cpu_up(unsigned int cpu)
+{
+ int ret = smp_boot_one_cpu(cpu);
+
+ if (!ret) {
+ cpu_set(cpu, smp_commenced_mask);
+ while (!cpu_isset(cpu, cpu_online_map))
+ mb();
+ if (!cpu_isset(cpu, cpu_online_map)) {
+ ret = -ENODEV;
+ } else {
+ /* On SUN4V, writes to %tick and %stick are
+ * not allowed.
+ */
+ if (tlb_type != hypervisor)
+ smp_synchronize_one_tick(cpu);
+ }
+ }
+ return ret;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+void cpu_play_dead(void)
+{
+ int cpu = smp_processor_id();
+ unsigned long pstate;
+
+ idle_task_exit();
+
+ if (tlb_type == hypervisor) {
+ struct trap_per_cpu *tb = &trap_block[cpu];
+
+ sun4v_cpu_qconf(HV_CPU_QUEUE_CPU_MONDO,
+ tb->cpu_mondo_pa, 0);
+ sun4v_cpu_qconf(HV_CPU_QUEUE_DEVICE_MONDO,
+ tb->dev_mondo_pa, 0);
+ sun4v_cpu_qconf(HV_CPU_QUEUE_RES_ERROR,
+ tb->resum_mondo_pa, 0);
+ sun4v_cpu_qconf(HV_CPU_QUEUE_NONRES_ERROR,
+ tb->nonresum_mondo_pa, 0);
+ }
+
+ cpu_clear(cpu, smp_commenced_mask);
+ membar_safe("#Sync");
+
+ local_irq_disable();
+
+ __asm__ __volatile__(
+ "rdpr %%pstate, %0\n\t"
+ "wrpr %0, %1, %%pstate"
+ : "=r" (pstate)
+ : "i" (PSTATE_IE));
+
+ while (1)
+ barrier();
+}
+
+int __cpu_disable(void)
+{
+ int cpu = smp_processor_id();
+ cpuinfo_sparc *c;
+ int i;
+
+ for_each_cpu_mask(i, cpu_core_map[cpu])
+ cpu_clear(cpu, cpu_core_map[i]);
+ cpus_clear(cpu_core_map[cpu]);
+
+ for_each_cpu_mask(i, per_cpu(cpu_sibling_map, cpu))
+ cpu_clear(cpu, per_cpu(cpu_sibling_map, i));
+ cpus_clear(per_cpu(cpu_sibling_map, cpu));
+
+ c = &cpu_data(cpu);
+
+ c->core_id = 0;
+ c->proc_id = -1;
+
+ smp_wmb();
+
+ /* Make sure no interrupts point to this cpu. */
+ fixup_irqs();
+
+ local_irq_enable();
+ mdelay(1);
+ local_irq_disable();
+
+ ipi_call_lock();
+ cpu_clear(cpu, cpu_online_map);
+ ipi_call_unlock();
+
+ return 0;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ smp_rmb();
+ if (!cpu_isset(cpu, smp_commenced_mask))
+ break;
+ msleep(100);
+ }
+ if (cpu_isset(cpu, smp_commenced_mask)) {
+ printk(KERN_ERR "CPU %u didn't die...\n", cpu);
+ } else {
+#if defined(CONFIG_SUN_LDOMS)
+ unsigned long hv_err;
+ int limit = 100;
+
+ do {
+ hv_err = sun4v_cpu_stop(cpu);
+ if (hv_err == HV_EOK) {
+ cpu_clear(cpu, cpu_present_map);
+ break;
+ }
+ } while (--limit > 0);
+ if (limit <= 0) {
+ printk(KERN_ERR "sun4v_cpu_stop() fails err=%lu\n",
+ hv_err);
+ }
+#endif
+ }
+}
+#endif
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+void smp_send_reschedule(int cpu)
+{
+ xcall_deliver((u64) &xcall_receive_signal, 0, 0,
+ &cpumask_of_cpu(cpu));
+}
+
+void smp_receive_signal_client(int irq, struct pt_regs *regs)
+{
+ clear_softint(1 << irq);
+}
+
+/* This is a nop because we capture all other cpus
+ * anyways when making the PROM active.
+ */
+void smp_send_stop(void)
+{
+}
+
+unsigned long __per_cpu_base __read_mostly;
+unsigned long __per_cpu_shift __read_mostly;
+
+EXPORT_SYMBOL(__per_cpu_base);
+EXPORT_SYMBOL(__per_cpu_shift);
+
+void __init real_setup_per_cpu_areas(void)
+{
+ unsigned long paddr, goal, size, i;
+ char *ptr;
+
+ /* Copy section for each CPU (we discard the original) */
+ goal = PERCPU_ENOUGH_ROOM;
+
+ __per_cpu_shift = PAGE_SHIFT;
+ for (size = PAGE_SIZE; size < goal; size <<= 1UL)
+ __per_cpu_shift++;
+
+ paddr = lmb_alloc(size * NR_CPUS, PAGE_SIZE);
+ if (!paddr) {
+ prom_printf("Cannot allocate per-cpu memory.\n");
+ prom_halt();
+ }
+
+ ptr = __va(paddr);
+ __per_cpu_base = ptr - __per_cpu_start;
+
+ for (i = 0; i < NR_CPUS; i++, ptr += size)
+ memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
+
+ /* Setup %g5 for the boot cpu. */
+ __local_per_cpu_offset = __per_cpu_offset(smp_processor_id());
+}
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
deleted file mode 100644
index 32d11a5fe3a8..000000000000
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- */
-
-/* Tell string.h we don't want memcpy etc. as cpp defines */
-#define EXPORT_SYMTAB_STROPS
-#define PROMLIB_INTERNAL
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/smp.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/in6.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/syscalls.h>
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
-#include <linux/pm.h>
-#ifdef CONFIG_HIGHMEM
-#include <linux/highmem.h>
-#endif
-
-#include <asm/oplib.h>
-#include <asm/delay.h>
-#include <asm/system.h>
-#include <asm/auxio.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/idprom.h>
-#include <asm/head.h>
-#include <asm/smp.h>
-#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-#include <asm/checksum.h>
-#ifdef CONFIG_SBUS
-#include <asm/dma.h>
-#endif
-#include <asm/io-unit.h>
-#include <asm/bug.h>
-
-extern spinlock_t rtc_lock;
-
-struct poll {
- int fd;
- short events;
- short revents;
-};
-
-extern void (*__copy_1page)(void *, const void *);
-extern void __memmove(void *, const void *, __kernel_size_t);
-extern void (*bzero_1page)(void *);
-extern void *__bzero(void *, size_t);
-extern void *__memscan_zero(void *, size_t);
-extern void *__memscan_generic(void *, int, size_t);
-extern int __memcmp(const void *, const void *, __kernel_size_t);
-extern int __strncmp(const char *, const char *, __kernel_size_t);
-
-extern int __ashrdi3(int, int);
-extern int __ashldi3(int, int);
-extern int __lshrdi3(int, int);
-extern int __muldi3(int, int);
-extern int __divdi3(int, int);
-
-/* Private functions with odd calling conventions. */
-extern void ___atomic24_add(void);
-extern void ___atomic24_sub(void);
-extern void ___rw_read_enter(void);
-extern void ___rw_read_try(void);
-extern void ___rw_read_exit(void);
-extern void ___rw_write_enter(void);
-
-/* Alias functions whose names begin with "." and export the aliases.
- * The module references will be fixed up by module_frob_arch_sections.
- */
-extern int _Div(int, int);
-extern int _Mul(int, int);
-extern int _Rem(int, int);
-extern unsigned _Udiv(unsigned, unsigned);
-extern unsigned _Umul(unsigned, unsigned);
-extern unsigned _Urem(unsigned, unsigned);
-
-/* used by various drivers */
-EXPORT_SYMBOL(sparc_cpu_model);
-EXPORT_SYMBOL(kernel_thread);
-#ifdef CONFIG_SMP
-// XXX find what uses (or used) these. AV: see asm/spinlock.h
-EXPORT_SYMBOL(___rw_read_enter);
-EXPORT_SYMBOL(___rw_read_try);
-EXPORT_SYMBOL(___rw_read_exit);
-EXPORT_SYMBOL(___rw_write_enter);
-#endif
-
-EXPORT_SYMBOL(sparc_valid_addr_bitmap);
-EXPORT_SYMBOL(phys_base);
-EXPORT_SYMBOL(pfn_base);
-
-/* Atomic operations. */
-EXPORT_SYMBOL(___atomic24_add);
-EXPORT_SYMBOL(___atomic24_sub);
-
-/* Per-CPU information table */
-EXPORT_PER_CPU_SYMBOL(__cpu_data);
-
-#ifdef CONFIG_SMP
-/* IRQ implementation. */
-EXPORT_SYMBOL(synchronize_irq);
-#endif
-
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__ndelay);
-EXPORT_SYMBOL(rtc_lock);
-#ifdef CONFIG_SUN_AUXIO
-EXPORT_SYMBOL(set_auxio);
-EXPORT_SYMBOL(get_auxio);
-#endif
-EXPORT_SYMBOL(io_remap_pfn_range);
-
-#ifndef CONFIG_SMP
-EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32));
-#else
-EXPORT_SYMBOL(BTFIXUP_CALL(__hard_smp_processor_id));
-#endif
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_unlockarea));
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_lockarea));
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_sgl));
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_one));
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_sgl));
-EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_one));
-
-EXPORT_SYMBOL(BTFIXUP_CALL(pgprot_noncached));
-
-#ifdef CONFIG_SBUS
-EXPORT_SYMBOL(sbus_set_sbus64);
-#endif
-#ifdef CONFIG_PCI
-EXPORT_SYMBOL(insb);
-EXPORT_SYMBOL(outsb);
-EXPORT_SYMBOL(insw);
-EXPORT_SYMBOL(outsw);
-EXPORT_SYMBOL(insl);
-EXPORT_SYMBOL(outsl);
-EXPORT_SYMBOL(pci_alloc_consistent);
-EXPORT_SYMBOL(pci_free_consistent);
-EXPORT_SYMBOL(pci_map_single);
-EXPORT_SYMBOL(pci_unmap_single);
-EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
-EXPORT_SYMBOL(pci_dma_sync_single_for_device);
-EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu);
-EXPORT_SYMBOL(pci_dma_sync_sg_for_device);
-EXPORT_SYMBOL(pci_map_sg);
-EXPORT_SYMBOL(pci_unmap_sg);
-EXPORT_SYMBOL(pci_map_page);
-EXPORT_SYMBOL(pci_unmap_page);
-/* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */
-EXPORT_SYMBOL(ioremap);
-EXPORT_SYMBOL(iounmap);
-#endif
-
-/* in arch/sparc/mm/highmem.c */
-#ifdef CONFIG_HIGHMEM
-EXPORT_SYMBOL(kmap_atomic);
-EXPORT_SYMBOL(kunmap_atomic);
-#endif
-
-/* prom symbols */
-EXPORT_SYMBOL(idprom);
-EXPORT_SYMBOL(prom_root_node);
-EXPORT_SYMBOL(prom_getchild);
-EXPORT_SYMBOL(prom_getsibling);
-EXPORT_SYMBOL(prom_searchsiblings);
-EXPORT_SYMBOL(prom_firstprop);
-EXPORT_SYMBOL(prom_nextprop);
-EXPORT_SYMBOL(prom_getproplen);
-EXPORT_SYMBOL(prom_getproperty);
-EXPORT_SYMBOL(prom_node_has_property);
-EXPORT_SYMBOL(prom_setprop);
-EXPORT_SYMBOL(saved_command_line);
-EXPORT_SYMBOL(prom_apply_obio_ranges);
-EXPORT_SYMBOL(prom_feval);
-EXPORT_SYMBOL(prom_getbool);
-EXPORT_SYMBOL(prom_getstring);
-EXPORT_SYMBOL(prom_getint);
-EXPORT_SYMBOL(prom_getintdefault);
-EXPORT_SYMBOL(prom_finddevice);
-EXPORT_SYMBOL(romvec);
-EXPORT_SYMBOL(__prom_getchild);
-EXPORT_SYMBOL(__prom_getsibling);
-
-/* sparc library symbols */
-EXPORT_SYMBOL(memscan);
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncmp);
-EXPORT_SYMBOL(page_kernel);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(__copy_1page);
-EXPORT_SYMBOL(__memcpy);
-EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(bzero_1page);
-EXPORT_SYMBOL(__bzero);
-EXPORT_SYMBOL(__memscan_zero);
-EXPORT_SYMBOL(__memscan_generic);
-EXPORT_SYMBOL(__memcmp);
-EXPORT_SYMBOL(__strncmp);
-EXPORT_SYMBOL(__memmove);
-
-/* Moving data to/from userspace. */
-EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__strnlen_user);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
-EXPORT_SYMBOL(csum_partial);
-
-/* Cache flushing. */
-EXPORT_SYMBOL(sparc_flush_page_to_ram);
-
-/* For when serial stuff is built as modules. */
-EXPORT_SYMBOL(sun_do_break);
-
-EXPORT_SYMBOL(__ret_efault);
-
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__muldi3);
-EXPORT_SYMBOL(__divdi3);
-
-EXPORT_SYMBOL(_Rem);
-EXPORT_SYMBOL(_Urem);
-EXPORT_SYMBOL(_Mul);
-EXPORT_SYMBOL(_Umul);
-EXPORT_SYMBOL(_Div);
-EXPORT_SYMBOL(_Udiv);
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-EXPORT_SYMBOL(do_BUG);
-#endif
-
-/* Sun Power Management Idle Handler */
-EXPORT_SYMBOL(pm_idle);
-
-EXPORT_SYMBOL(empty_zero_page);
diff --git a/arch/sparc/kernel/sparc_ksyms_32.c b/arch/sparc/kernel/sparc_ksyms_32.c
new file mode 100644
index 000000000000..e1e97639231b
--- /dev/null
+++ b/arch/sparc/kernel/sparc_ksyms_32.c
@@ -0,0 +1,253 @@
+/*
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+/* Tell string.h we don't want memcpy etc. as cpp defines */
+#define EXPORT_SYMTAB_STROPS
+#define PROMLIB_INTERNAL
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/in6.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/syscalls.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+#include <linux/pm.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/highmem.h>
+#endif
+
+#include <asm/oplib.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/auxio.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/idprom.h>
+#include <asm/head.h>
+#include <asm/smp.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#ifdef CONFIG_SBUS
+#include <asm/dma.h>
+#endif
+#include <asm/io-unit.h>
+#include <asm/bug.h>
+
+extern spinlock_t rtc_lock;
+
+struct poll {
+ int fd;
+ short events;
+ short revents;
+};
+
+extern void (*__copy_1page)(void *, const void *);
+extern void __memmove(void *, const void *, __kernel_size_t);
+extern void (*bzero_1page)(void *);
+extern void *__bzero(void *, size_t);
+extern void *__memscan_zero(void *, size_t);
+extern void *__memscan_generic(void *, int, size_t);
+extern int __strncmp(const char *, const char *, __kernel_size_t);
+
+extern int __ashrdi3(int, int);
+extern int __ashldi3(int, int);
+extern int __lshrdi3(int, int);
+extern int __muldi3(int, int);
+extern int __divdi3(int, int);
+
+/* Private functions with odd calling conventions. */
+extern void ___atomic24_add(void);
+extern void ___atomic24_sub(void);
+extern void ___rw_read_enter(void);
+extern void ___rw_read_try(void);
+extern void ___rw_read_exit(void);
+extern void ___rw_write_enter(void);
+
+/* Alias functions whose names begin with "." and export the aliases.
+ * The module references will be fixed up by module_frob_arch_sections.
+ */
+extern int _Div(int, int);
+extern int _Mul(int, int);
+extern int _Rem(int, int);
+extern unsigned _Udiv(unsigned, unsigned);
+extern unsigned _Umul(unsigned, unsigned);
+extern unsigned _Urem(unsigned, unsigned);
+
+/* used by various drivers */
+EXPORT_SYMBOL(sparc_cpu_model);
+EXPORT_SYMBOL(kernel_thread);
+#ifdef CONFIG_SMP
+// XXX find what uses (or used) these. AV: see asm/spinlock.h
+EXPORT_SYMBOL(___rw_read_enter);
+EXPORT_SYMBOL(___rw_read_try);
+EXPORT_SYMBOL(___rw_read_exit);
+EXPORT_SYMBOL(___rw_write_enter);
+#endif
+
+EXPORT_SYMBOL(sparc_valid_addr_bitmap);
+EXPORT_SYMBOL(phys_base);
+EXPORT_SYMBOL(pfn_base);
+
+/* Atomic operations. */
+EXPORT_SYMBOL(___atomic24_add);
+EXPORT_SYMBOL(___atomic24_sub);
+
+/* Per-CPU information table */
+EXPORT_PER_CPU_SYMBOL(__cpu_data);
+
+#ifdef CONFIG_SMP
+/* IRQ implementation. */
+EXPORT_SYMBOL(synchronize_irq);
+#endif
+
+EXPORT_SYMBOL(__udelay);
+EXPORT_SYMBOL(__ndelay);
+EXPORT_SYMBOL(rtc_lock);
+EXPORT_SYMBOL(set_auxio);
+EXPORT_SYMBOL(get_auxio);
+EXPORT_SYMBOL(io_remap_pfn_range);
+
+#ifndef CONFIG_SMP
+EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32));
+#else
+EXPORT_SYMBOL(BTFIXUP_CALL(__hard_smp_processor_id));
+#endif
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_unlockarea));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_lockarea));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_sgl));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_get_scsi_one));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_sgl));
+EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_one));
+
+EXPORT_SYMBOL(BTFIXUP_CALL(pgprot_noncached));
+
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(sbus_set_sbus64);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+EXPORT_SYMBOL(pci_map_single);
+EXPORT_SYMBOL(pci_unmap_single);
+EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
+EXPORT_SYMBOL(pci_dma_sync_single_for_device);
+EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu);
+EXPORT_SYMBOL(pci_dma_sync_sg_for_device);
+EXPORT_SYMBOL(pci_map_sg);
+EXPORT_SYMBOL(pci_unmap_sg);
+EXPORT_SYMBOL(pci_map_page);
+EXPORT_SYMBOL(pci_unmap_page);
+/* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */
+EXPORT_SYMBOL(ioremap);
+EXPORT_SYMBOL(iounmap);
+#endif
+
+/* in arch/sparc/mm/highmem.c */
+#ifdef CONFIG_HIGHMEM
+EXPORT_SYMBOL(kmap_atomic);
+EXPORT_SYMBOL(kunmap_atomic);
+#endif
+
+/* prom symbols */
+EXPORT_SYMBOL(idprom);
+EXPORT_SYMBOL(prom_root_node);
+EXPORT_SYMBOL(prom_getchild);
+EXPORT_SYMBOL(prom_getsibling);
+EXPORT_SYMBOL(prom_searchsiblings);
+EXPORT_SYMBOL(prom_firstprop);
+EXPORT_SYMBOL(prom_nextprop);
+EXPORT_SYMBOL(prom_getproplen);
+EXPORT_SYMBOL(prom_getproperty);
+EXPORT_SYMBOL(prom_node_has_property);
+EXPORT_SYMBOL(prom_setprop);
+EXPORT_SYMBOL(saved_command_line);
+EXPORT_SYMBOL(prom_apply_obio_ranges);
+EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getbool);
+EXPORT_SYMBOL(prom_getstring);
+EXPORT_SYMBOL(prom_getint);
+EXPORT_SYMBOL(prom_getintdefault);
+EXPORT_SYMBOL(prom_finddevice);
+EXPORT_SYMBOL(romvec);
+EXPORT_SYMBOL(__prom_getchild);
+EXPORT_SYMBOL(__prom_getsibling);
+
+/* sparc library symbols */
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(page_kernel);
+
+/* Special internal versions of library functions. */
+EXPORT_SYMBOL(__copy_1page);
+EXPORT_SYMBOL(__memcpy);
+EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(bzero_1page);
+EXPORT_SYMBOL(__bzero);
+EXPORT_SYMBOL(__memscan_zero);
+EXPORT_SYMBOL(__memscan_generic);
+EXPORT_SYMBOL(__strncmp);
+EXPORT_SYMBOL(__memmove);
+
+/* Moving data to/from userspace. */
+EXPORT_SYMBOL(__copy_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(__strnlen_user);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
+EXPORT_SYMBOL(csum_partial);
+
+/* Cache flushing. */
+EXPORT_SYMBOL(sparc_flush_page_to_ram);
+
+/* For when serial stuff is built as modules. */
+EXPORT_SYMBOL(sun_do_break);
+
+EXPORT_SYMBOL(__ret_efault);
+
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(__ashrdi3);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__lshrdi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__divdi3);
+
+EXPORT_SYMBOL(_Rem);
+EXPORT_SYMBOL(_Urem);
+EXPORT_SYMBOL(_Mul);
+EXPORT_SYMBOL(_Umul);
+EXPORT_SYMBOL(_Div);
+EXPORT_SYMBOL(_Udiv);
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+EXPORT_SYMBOL(do_BUG);
+#endif
+
+/* Sun Power Management Idle Handler */
+EXPORT_SYMBOL(pm_idle);
+
+EXPORT_SYMBOL(empty_zero_page);
diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c
new file mode 100644
index 000000000000..0133211ab634
--- /dev/null
+++ b/arch/sparc/kernel/sparc_ksyms_64.c
@@ -0,0 +1,284 @@
+/* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
+ *
+ * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+/* Tell string.h we don't want memcpy etc. as cpp defines */
+#define EXPORT_SYMTAB_STROPS
+#define PROMLIB_INTERNAL
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/in6.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/fs_struct.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/syscalls.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/rwsem.h>
+#include <net/compat.h>
+
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/auxio.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/idprom.h>
+#include <asm/elf.h>
+#include <asm/head.h>
+#include <asm/smp.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/fpumacro.h>
+#include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
+#ifdef CONFIG_SBUS
+#include <asm/dma.h>
+#endif
+#include <asm/ns87303.h>
+#include <asm/timer.h>
+#include <asm/cpudata.h>
+#include <asm/ftrace.h>
+#include <asm/hypervisor.h>
+
+struct poll {
+ int fd;
+ short events;
+ short revents;
+};
+
+extern void die_if_kernel(char *str, struct pt_regs *regs);
+extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern void *__bzero(void *, size_t);
+extern void *__memscan_zero(void *, size_t);
+extern void *__memscan_generic(void *, int, size_t);
+extern __kernel_size_t strlen(const char *);
+extern void sys_sigsuspend(void);
+extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
+extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
+extern long sparc32_open(const char __user * filename, int flags, int mode);
+extern int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
+ unsigned long pfn, unsigned long size, pgprot_t prot);
+
+extern int __ashrdi3(int, int);
+
+extern int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs);
+
+extern void xor_vis_2(unsigned long, unsigned long *, unsigned long *);
+extern void xor_vis_3(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *);
+extern void xor_vis_4(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *, unsigned long *);
+extern void xor_vis_5(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *, unsigned long *, unsigned long *);
+
+extern void xor_niagara_2(unsigned long, unsigned long *, unsigned long *);
+extern void xor_niagara_3(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *);
+extern void xor_niagara_4(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *, unsigned long *);
+extern void xor_niagara_5(unsigned long, unsigned long *, unsigned long *,
+ unsigned long *, unsigned long *, unsigned long *);
+
+/* Per-CPU information table */
+EXPORT_PER_CPU_SYMBOL(__cpu_data);
+
+/* used by various drivers */
+#ifdef CONFIG_SMP
+/* Out of line rw-locking implementation. */
+EXPORT_SYMBOL(__read_lock);
+EXPORT_SYMBOL(__read_unlock);
+EXPORT_SYMBOL(__write_lock);
+EXPORT_SYMBOL(__write_unlock);
+EXPORT_SYMBOL(__write_trylock);
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_MCOUNT
+EXPORT_SYMBOL(_mcount);
+#endif
+
+EXPORT_SYMBOL(sparc64_get_clock_tick);
+
+/* RW semaphores */
+EXPORT_SYMBOL(__down_read);
+EXPORT_SYMBOL(__down_read_trylock);
+EXPORT_SYMBOL(__down_write);
+EXPORT_SYMBOL(__down_write_trylock);
+EXPORT_SYMBOL(__up_read);
+EXPORT_SYMBOL(__up_write);
+EXPORT_SYMBOL(__downgrade_write);
+
+/* Atomic counter implementation. */
+EXPORT_SYMBOL(atomic_add);
+EXPORT_SYMBOL(atomic_add_ret);
+EXPORT_SYMBOL(atomic_sub);
+EXPORT_SYMBOL(atomic_sub_ret);
+EXPORT_SYMBOL(atomic64_add);
+EXPORT_SYMBOL(atomic64_add_ret);
+EXPORT_SYMBOL(atomic64_sub);
+EXPORT_SYMBOL(atomic64_sub_ret);
+
+/* Atomic bit operations. */
+EXPORT_SYMBOL(test_and_set_bit);
+EXPORT_SYMBOL(test_and_clear_bit);
+EXPORT_SYMBOL(test_and_change_bit);
+EXPORT_SYMBOL(set_bit);
+EXPORT_SYMBOL(clear_bit);
+EXPORT_SYMBOL(change_bit);
+
+EXPORT_SYMBOL(__flushw_user);
+
+EXPORT_SYMBOL(tlb_type);
+EXPORT_SYMBOL(sun4v_chip_type);
+EXPORT_SYMBOL(get_fb_unmapped_area);
+EXPORT_SYMBOL(flush_icache_range);
+
+EXPORT_SYMBOL(flush_dcache_page);
+#ifdef DCACHE_ALIASING_POSSIBLE
+EXPORT_SYMBOL(__flush_dcache_range);
+#endif
+
+EXPORT_SYMBOL(sun4v_niagara_getperf);
+EXPORT_SYMBOL(sun4v_niagara_setperf);
+EXPORT_SYMBOL(sun4v_niagara2_getperf);
+EXPORT_SYMBOL(sun4v_niagara2_setperf);
+
+EXPORT_SYMBOL(auxio_set_led);
+EXPORT_SYMBOL(auxio_set_lte);
+#ifdef CONFIG_SBUS
+EXPORT_SYMBOL(sbus_set_sbus64);
+#endif
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+EXPORT_SYMBOL(pci_map_single);
+EXPORT_SYMBOL(pci_unmap_single);
+EXPORT_SYMBOL(pci_map_sg);
+EXPORT_SYMBOL(pci_unmap_sg);
+EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
+EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu);
+EXPORT_SYMBOL(pci_dma_supported);
+#endif
+
+/* I/O device mmaping on Sparc64. */
+EXPORT_SYMBOL(io_remap_pfn_range);
+
+EXPORT_SYMBOL(dump_fpu);
+
+/* math-emu wants this */
+EXPORT_SYMBOL(die_if_kernel);
+
+/* Kernel thread creation. */
+EXPORT_SYMBOL(kernel_thread);
+
+/* prom symbols */
+EXPORT_SYMBOL(idprom);
+EXPORT_SYMBOL(prom_root_node);
+EXPORT_SYMBOL(prom_getchild);
+EXPORT_SYMBOL(prom_getsibling);
+EXPORT_SYMBOL(prom_searchsiblings);
+EXPORT_SYMBOL(prom_firstprop);
+EXPORT_SYMBOL(prom_nextprop);
+EXPORT_SYMBOL(prom_getproplen);
+EXPORT_SYMBOL(prom_getproperty);
+EXPORT_SYMBOL(prom_node_has_property);
+EXPORT_SYMBOL(prom_setprop);
+EXPORT_SYMBOL(saved_command_line);
+EXPORT_SYMBOL(prom_finddevice);
+EXPORT_SYMBOL(prom_feval);
+EXPORT_SYMBOL(prom_getbool);
+EXPORT_SYMBOL(prom_getstring);
+EXPORT_SYMBOL(prom_getint);
+EXPORT_SYMBOL(prom_getintdefault);
+EXPORT_SYMBOL(__prom_getchild);
+EXPORT_SYMBOL(__prom_getsibling);
+
+/* sparc library symbols */
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(__strlen_user);
+EXPORT_SYMBOL(__strnlen_user);
+
+/* Special internal versions of library functions. */
+EXPORT_SYMBOL(_clear_page);
+EXPORT_SYMBOL(clear_user_page);
+EXPORT_SYMBOL(copy_user_page);
+EXPORT_SYMBOL(__bzero);
+EXPORT_SYMBOL(__memscan_zero);
+EXPORT_SYMBOL(__memscan_generic);
+EXPORT_SYMBOL(__memset);
+
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
+EXPORT_SYMBOL(__csum_partial_copy_from_user);
+EXPORT_SYMBOL(__csum_partial_copy_to_user);
+EXPORT_SYMBOL(ip_fast_csum);
+
+/* Moving data to/from/in userspace. */
+EXPORT_SYMBOL(___copy_to_user);
+EXPORT_SYMBOL(___copy_from_user);
+EXPORT_SYMBOL(___copy_in_user);
+EXPORT_SYMBOL(copy_to_user_fixup);
+EXPORT_SYMBOL(copy_from_user_fixup);
+EXPORT_SYMBOL(copy_in_user_fixup);
+EXPORT_SYMBOL(__strncpy_from_user);
+EXPORT_SYMBOL(__clear_user);
+
+/* Various address conversion macros use this. */
+EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
+
+/* No version information on this, heavily used in inline asm,
+ * and will always be 'void __ret_efault(void)'.
+ */
+EXPORT_SYMBOL(__ret_efault);
+
+/* No version information on these, as gcc produces such symbols. */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strncmp);
+
+void VISenter(void);
+/* RAID code needs this */
+EXPORT_SYMBOL(VISenter);
+
+/* for input/keybdev */
+EXPORT_SYMBOL(sun_do_break);
+EXPORT_SYMBOL(stop_a_enabled);
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+EXPORT_SYMBOL(do_BUG);
+#endif
+
+/* for ns8703 */
+EXPORT_SYMBOL(ns87303_lock);
+
+EXPORT_SYMBOL(tick_ops);
+
+EXPORT_SYMBOL(xor_vis_2);
+EXPORT_SYMBOL(xor_vis_3);
+EXPORT_SYMBOL(xor_vis_4);
+EXPORT_SYMBOL(xor_vis_5);
+
+EXPORT_SYMBOL(xor_niagara_2);
+EXPORT_SYMBOL(xor_niagara_3);
+EXPORT_SYMBOL(xor_niagara_4);
+EXPORT_SYMBOL(xor_niagara_5);
+
+EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
diff --git a/arch/sparc/kernel/spiterrs.S b/arch/sparc/kernel/spiterrs.S
new file mode 100644
index 000000000000..c357e40ffd01
--- /dev/null
+++ b/arch/sparc/kernel/spiterrs.S
@@ -0,0 +1,245 @@
+ /* We need to carefully read the error status, ACK the errors,
+ * prevent recursive traps, and pass the information on to C
+ * code for logging.
+ *
+ * We pass the AFAR in as-is, and we encode the status
+ * information as described in asm-sparc64/sfafsr.h
+ */
+ .type __spitfire_access_error,#function
+__spitfire_access_error:
+ /* Disable ESTATE error reporting so that we do not take
+ * recursive traps and RED state the processor.
+ */
+ stxa %g0, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ mov UDBE_UE, %g1
+ ldxa [%g0] ASI_AFSR, %g4 ! Get AFSR
+
+ /* __spitfire_cee_trap branches here with AFSR in %g4 and
+ * UDBE_CE in %g1. It only clears ESTATE_ERR_CE in the ESTATE
+ * Error Enable register.
+ */
+__spitfire_cee_trap_continue:
+ ldxa [%g0] ASI_AFAR, %g5 ! Get AFAR
+
+ rdpr %tt, %g3
+ and %g3, 0x1ff, %g3 ! Paranoia
+ sllx %g3, SFSTAT_TRAP_TYPE_SHIFT, %g3
+ or %g4, %g3, %g4
+ rdpr %tl, %g3
+ cmp %g3, 1
+ mov 1, %g3
+ bleu %xcc, 1f
+ sllx %g3, SFSTAT_TL_GT_ONE_SHIFT, %g3
+
+ or %g4, %g3, %g4
+
+ /* Read in the UDB error register state, clearing the sticky
+ * error bits as-needed. We only clear them if the UE bit is
+ * set. Likewise, __spitfire_cee_trap below will only do so
+ * if the CE bit is set.
+ *
+ * NOTE: UltraSparc-I/II have high and low UDB error
+ * registers, corresponding to the two UDB units
+ * present on those chips. UltraSparc-IIi only
+ * has a single UDB, called "SDB" in the manual.
+ * For IIi the upper UDB register always reads
+ * as zero so for our purposes things will just
+ * work with the checks below.
+ */
+1: ldxa [%g0] ASI_UDBH_ERROR_R, %g3
+ and %g3, 0x3ff, %g7 ! Paranoia
+ sllx %g7, SFSTAT_UDBH_SHIFT, %g7
+ or %g4, %g7, %g4
+ andcc %g3, %g1, %g3 ! UDBE_UE or UDBE_CE
+ be,pn %xcc, 1f
+ nop
+ stxa %g3, [%g0] ASI_UDB_ERROR_W
+ membar #Sync
+
+1: mov 0x18, %g3
+ ldxa [%g3] ASI_UDBL_ERROR_R, %g3
+ and %g3, 0x3ff, %g7 ! Paranoia
+ sllx %g7, SFSTAT_UDBL_SHIFT, %g7
+ or %g4, %g7, %g4
+ andcc %g3, %g1, %g3 ! UDBE_UE or UDBE_CE
+ be,pn %xcc, 1f
+ nop
+ mov 0x18, %g7
+ stxa %g3, [%g7] ASI_UDB_ERROR_W
+ membar #Sync
+
+1: /* Ok, now that we've latched the error state, clear the
+ * sticky bits in the AFSR.
+ */
+ stxa %g4, [%g0] ASI_AFSR
+ membar #Sync
+
+ rdpr %tl, %g2
+ cmp %g2, 1
+ rdpr %pil, %g2
+ bleu,pt %xcc, 1f
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+
+ ba,pt %xcc, etraptl1
+ rd %pc, %g7
+
+ ba,pt %xcc, 2f
+ nop
+
+1: ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+
+2:
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ mov %l4, %o1
+ mov %l5, %o2
+ call spitfire_access_error
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+ .size __spitfire_access_error,.-__spitfire_access_error
+
+ /* This is the trap handler entry point for ECC correctable
+ * errors. They are corrected, but we listen for the trap so
+ * that the event can be logged.
+ *
+ * Disrupting errors are either:
+ * 1) single-bit ECC errors during UDB reads to system
+ * memory
+ * 2) data parity errors during write-back events
+ *
+ * As far as I can make out from the manual, the CEE trap is
+ * only for correctable errors during memory read accesses by
+ * the front-end of the processor.
+ *
+ * The code below is only for trap level 1 CEE events, as it
+ * is the only situation where we can safely record and log.
+ * For trap level >1 we just clear the CE bit in the AFSR and
+ * return.
+ *
+ * This is just like __spiftire_access_error above, but it
+ * specifically handles correctable errors. If an
+ * uncorrectable error is indicated in the AFSR we will branch
+ * directly above to __spitfire_access_error to handle it
+ * instead. Uncorrectable therefore takes priority over
+ * correctable, and the error logging C code will notice this
+ * case by inspecting the trap type.
+ */
+ .type __spitfire_cee_trap,#function
+__spitfire_cee_trap:
+ ldxa [%g0] ASI_AFSR, %g4 ! Get AFSR
+ mov 1, %g3
+ sllx %g3, SFAFSR_UE_SHIFT, %g3
+ andcc %g4, %g3, %g0 ! Check for UE
+ bne,pn %xcc, __spitfire_access_error
+ nop
+
+ /* Ok, in this case we only have a correctable error.
+ * Indicate we only wish to capture that state in register
+ * %g1, and we only disable CE error reporting unlike UE
+ * handling which disables all errors.
+ */
+ ldxa [%g0] ASI_ESTATE_ERROR_EN, %g3
+ andn %g3, ESTATE_ERR_CE, %g3
+ stxa %g3, [%g0] ASI_ESTATE_ERROR_EN
+ membar #Sync
+
+ /* Preserve AFSR in %g4, indicate UDB state to capture in %g1 */
+ ba,pt %xcc, __spitfire_cee_trap_continue
+ mov UDBE_CE, %g1
+ .size __spitfire_cee_trap,.-__spitfire_cee_trap
+
+ .type __spitfire_data_access_exception_tl1,#function
+__spitfire_data_access_exception_tl1:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
+ membar #Sync
+ rdpr %tt, %g3
+ cmp %g3, 0x80 ! first win spill/fill trap
+ blu,pn %xcc, 1f
+ cmp %g3, 0xff ! last win spill/fill trap
+ bgu,pn %xcc, 1f
+ nop
+ ba,pt %xcc, winfix_dax
+ rdpr %tpc, %g3
+1: sethi %hi(109f), %g7
+ ba,pt %xcc, etraptl1
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call spitfire_data_access_exception_tl1
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+ .size __spitfire_data_access_exception_tl1,.-__spitfire_data_access_exception_tl1
+
+ .type __spitfire_data_access_exception,#function
+__spitfire_data_access_exception:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ mov DMMU_SFAR, %g5
+ ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
+ ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
+ stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call spitfire_data_access_exception
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+ .size __spitfire_data_access_exception,.-__spitfire_data_access_exception
+
+ .type __spitfire_insn_access_exception_tl1,#function
+__spitfire_insn_access_exception_tl1:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ ldxa [%g3] ASI_IMMU, %g4 ! Get SFSR
+ rdpr %tpc, %g5 ! IMMU has no SFAR, use TPC
+ stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etraptl1
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call spitfire_insn_access_exception_tl1
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+ .size __spitfire_insn_access_exception_tl1,.-__spitfire_insn_access_exception_tl1
+
+ .type __spitfire_insn_access_exception,#function
+__spitfire_insn_access_exception:
+ rdpr %pstate, %g4
+ wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
+ mov TLB_SFSR, %g3
+ ldxa [%g3] ASI_IMMU, %g4 ! Get SFSR
+ rdpr %tpc, %g5 ! IMMU has no SFAR, use TPC
+ stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
+ membar #Sync
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap
+109: or %g7, %lo(109b), %g7
+ mov %l4, %o1
+ mov %l5, %o2
+ call spitfire_insn_access_exception
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+ .size __spitfire_insn_access_exception,.-__spitfire_insn_access_exception
diff --git a/arch/sparc64/kernel/sstate.c b/arch/sparc/kernel/sstate.c
index 8cdbe5946b43..8cdbe5946b43 100644
--- a/arch/sparc64/kernel/sstate.c
+++ b/arch/sparc/kernel/sstate.c
diff --git a/arch/sparc/kernel/stacktrace.c b/arch/sparc/kernel/stacktrace.c
new file mode 100644
index 000000000000..acb12f673757
--- /dev/null
+++ b/arch/sparc/kernel/stacktrace.c
@@ -0,0 +1,64 @@
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/thread_info.h>
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <asm/stacktrace.h>
+
+#include "kstack.h"
+
+static void __save_stack_trace(struct thread_info *tp,
+ struct stack_trace *trace,
+ bool skip_sched)
+{
+ unsigned long ksp, fp;
+
+ if (tp == current_thread_info()) {
+ stack_trace_flush();
+ __asm__ __volatile__("mov %%fp, %0" : "=r" (ksp));
+ } else {
+ ksp = tp->ksp;
+ }
+
+ fp = ksp + STACK_BIAS;
+ do {
+ struct sparc_stackf *sf;
+ struct pt_regs *regs;
+ unsigned long pc;
+
+ if (!kstack_valid(tp, fp))
+ break;
+
+ sf = (struct sparc_stackf *) fp;
+ regs = (struct pt_regs *) (sf + 1);
+
+ if (kstack_is_trap_frame(tp, regs)) {
+ if (!(regs->tstate & TSTATE_PRIV))
+ break;
+ pc = regs->tpc;
+ fp = regs->u_regs[UREG_I6] + STACK_BIAS;
+ } else {
+ pc = sf->callers_pc;
+ fp = (unsigned long)sf->fp + STACK_BIAS;
+ }
+
+ if (trace->skip > 0)
+ trace->skip--;
+ else if (!skip_sched || !in_sched_functions(pc))
+ trace->entries[trace->nr_entries++] = pc;
+ } while (trace->nr_entries < trace->max_entries);
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+ __save_stack_trace(current_thread_info(), trace, false);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ struct thread_info *tp = task_thread_info(tsk);
+
+ __save_stack_trace(tp, trace, true);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc/kernel/starfire.c
index 060d0f3a6151..060d0f3a6151 100644
--- a/arch/sparc64/kernel/starfire.c
+++ b/arch/sparc/kernel/starfire.c
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c
index 5dc8a5769489..bc3adbf79c6a 100644
--- a/arch/sparc/kernel/sun4c_irq.c
+++ b/arch/sparc/kernel/sun4c_irq.c
@@ -160,6 +160,7 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn)
sun4c_timers = (void __iomem *) (unsigned long) addr[0];
irq = of_get_property(dp, "intr", NULL);
+ of_node_put(dp);
if (!irq) {
prom_printf("sun4c_init_timers: No intr property\n");
prom_halt();
@@ -200,6 +201,7 @@ void __init sun4c_init_IRQ(void)
}
addr = of_get_property(dp, "address", NULL);
+ of_node_put(dp);
if (!addr) {
prom_printf("sun4c_init_IRQ: No address property\n");
prom_halt();
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
index d3cb76ce418b..3369fef5b4b3 100644
--- a/arch/sparc/kernel/sun4d_irq.c
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -40,6 +40,7 @@
#include <asm/cacheflush.h>
#include <asm/irq_regs.h>
+#include "kernel.h"
#include "irq.h"
/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
@@ -58,7 +59,6 @@ static struct sun4d_timer_regs __iomem *sun4d_timers;
#define TIMER_IRQ 10
#define MAX_STATIC_ALLOC 4
-extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
extern int static_irq_count;
static unsigned char sbus_tid[32];
@@ -508,6 +508,7 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn)
* bootbus.
*/
reg = of_get_property(dp, "reg", NULL);
+ of_node_put(dp);
if (!reg) {
prom_printf("sun4d_init_timers: No reg property\n");
prom_halt();
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index f10317179ee6..301892e2d718 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -374,6 +374,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
}
addr = of_get_property(dp, "address", &len);
+ of_node_put(dp);
if (!addr) {
printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n");
return;
@@ -437,6 +438,7 @@ void __init sun4m_init_IRQ(void)
}
addr = of_get_property(dp, "address", &len);
+ of_node_put(dp);
if (!addr) {
printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
return;
diff --git a/arch/sparc/kernel/sun4v_ivec.S b/arch/sparc/kernel/sun4v_ivec.S
new file mode 100644
index 000000000000..559bc5e9c199
--- /dev/null
+++ b/arch/sparc/kernel/sun4v_ivec.S
@@ -0,0 +1,341 @@
+/* sun4v_ivec.S: Sun4v interrupt vector handling.
+ *
+ * Copyright (C) 2006 <davem@davemloft.net>
+ */
+
+#include <asm/cpudata.h>
+#include <asm/intr_queue.h>
+#include <asm/pil.h>
+
+ .text
+ .align 32
+
+sun4v_cpu_mondo:
+ /* Head offset in %g2, tail offset in %g4.
+ * If they are the same, no work.
+ */
+ mov INTRQ_CPU_MONDO_HEAD, %g2
+ ldxa [%g2] ASI_QUEUE, %g2
+ mov INTRQ_CPU_MONDO_TAIL, %g4
+ ldxa [%g4] ASI_QUEUE, %g4
+ cmp %g2, %g4
+ be,pn %xcc, sun4v_cpu_mondo_queue_empty
+ nop
+
+ /* Get &trap_block[smp_processor_id()] into %g4. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g4
+ sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
+
+ /* Get CPU mondo queue base phys address into %g7. */
+ ldx [%g4 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
+
+ /* Now get the cross-call arguments and handler PC, same
+ * layout as sun4u:
+ *
+ * 1st 64-bit word: low half is 32-bit PC, put into %g3 and jmpl to it
+ * high half is context arg to MMU flushes, into %g5
+ * 2nd 64-bit word: 64-bit arg, load into %g1
+ * 3rd 64-bit word: 64-bit arg, load into %g7
+ */
+ ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g3
+ add %g2, 0x8, %g2
+ srlx %g3, 32, %g5
+ ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
+ add %g2, 0x8, %g2
+ srl %g3, 0, %g3
+ ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g7
+ add %g2, 0x40 - 0x8 - 0x8, %g2
+
+ /* Update queue head pointer. */
+ lduw [%g4 + TRAP_PER_CPU_CPU_MONDO_QMASK], %g4
+ and %g2, %g4, %g2
+
+ mov INTRQ_CPU_MONDO_HEAD, %g4
+ stxa %g2, [%g4] ASI_QUEUE
+ membar #Sync
+
+ jmpl %g3, %g0
+ nop
+
+sun4v_cpu_mondo_queue_empty:
+ retry
+
+sun4v_dev_mondo:
+ /* Head offset in %g2, tail offset in %g4. */
+ mov INTRQ_DEVICE_MONDO_HEAD, %g2
+ ldxa [%g2] ASI_QUEUE, %g2
+ mov INTRQ_DEVICE_MONDO_TAIL, %g4
+ ldxa [%g4] ASI_QUEUE, %g4
+ cmp %g2, %g4
+ be,pn %xcc, sun4v_dev_mondo_queue_empty
+ nop
+
+ /* Get &trap_block[smp_processor_id()] into %g4. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g4
+ sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
+
+ /* Get DEV mondo queue base phys address into %g5. */
+ ldx [%g4 + TRAP_PER_CPU_DEV_MONDO_PA], %g5
+
+ /* Load IVEC into %g3. */
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ add %g2, 0x40, %g2
+
+ /* XXX There can be a full 64-byte block of data here.
+ * XXX This is how we can get at MSI vector data.
+ * XXX Current we do not capture this, but when we do we'll
+ * XXX need to add a 64-byte storage area in the struct ino_bucket
+ * XXX or the struct irq_desc.
+ */
+
+ /* Update queue head pointer, this frees up some registers. */
+ lduw [%g4 + TRAP_PER_CPU_DEV_MONDO_QMASK], %g4
+ and %g2, %g4, %g2
+
+ mov INTRQ_DEVICE_MONDO_HEAD, %g4
+ stxa %g2, [%g4] ASI_QUEUE
+ membar #Sync
+
+ TRAP_LOAD_IRQ_WORK_PA(%g1, %g4)
+
+ /* For VIRQs, cookie is encoded as ~bucket_phys_addr */
+ brlz,pt %g3, 1f
+ xnor %g3, %g0, %g4
+
+ /* Get __pa(&ivector_table[IVEC]) into %g4. */
+ sethi %hi(ivector_table_pa), %g4
+ ldx [%g4 + %lo(ivector_table_pa)], %g4
+ sllx %g3, 4, %g3
+ add %g4, %g3, %g4
+
+1: ldx [%g1], %g2
+ stxa %g2, [%g4] ASI_PHYS_USE_EC
+ stx %g4, [%g1]
+
+ /* Signal the interrupt by setting (1 << pil) in %softint. */
+ wr %g0, 1 << PIL_DEVICE_IRQ, %set_softint
+
+sun4v_dev_mondo_queue_empty:
+ retry
+
+sun4v_res_mondo:
+ /* Head offset in %g2, tail offset in %g4. */
+ mov INTRQ_RESUM_MONDO_HEAD, %g2
+ ldxa [%g2] ASI_QUEUE, %g2
+ mov INTRQ_RESUM_MONDO_TAIL, %g4
+ ldxa [%g4] ASI_QUEUE, %g4
+ cmp %g2, %g4
+ be,pn %xcc, sun4v_res_mondo_queue_empty
+ nop
+
+ /* Get &trap_block[smp_processor_id()] into %g3. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g3
+ sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
+
+ /* Get RES mondo queue base phys address into %g5. */
+ ldx [%g3 + TRAP_PER_CPU_RESUM_MONDO_PA], %g5
+
+ /* Get RES kernel buffer base phys address into %g7. */
+ ldx [%g3 + TRAP_PER_CPU_RESUM_KBUF_PA], %g7
+
+ /* If the first word is non-zero, queue is full. */
+ ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
+ brnz,pn %g1, sun4v_res_mondo_queue_full
+ nop
+
+ lduw [%g3 + TRAP_PER_CPU_RESUM_QMASK], %g4
+
+ /* Remember this entry's offset in %g1. */
+ mov %g2, %g1
+
+ /* Copy 64-byte queue entry into kernel buffer. */
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+
+ /* Update queue head pointer. */
+ and %g2, %g4, %g2
+
+ mov INTRQ_RESUM_MONDO_HEAD, %g4
+ stxa %g2, [%g4] ASI_QUEUE
+ membar #Sync
+
+ /* Disable interrupts and save register state so we can call
+ * C code. The etrap handling will leave %g4 in %l4 for us
+ * when it's done.
+ */
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ mov %g1, %g4
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ /* Log the event. */
+ add %sp, PTREGS_OFF, %o0
+ call sun4v_resum_error
+ mov %l4, %o1
+
+ /* Return from trap. */
+ ba,pt %xcc, rtrap_irq
+ nop
+
+sun4v_res_mondo_queue_empty:
+ retry
+
+sun4v_res_mondo_queue_full:
+ /* The queue is full, consolidate our damage by setting
+ * the head equal to the tail. We'll just trap again otherwise.
+ * Call C code to log the event.
+ */
+ mov INTRQ_RESUM_MONDO_HEAD, %g2
+ stxa %g4, [%g2] ASI_QUEUE
+ membar #Sync
+
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ call sun4v_resum_overflow
+ add %sp, PTREGS_OFF, %o0
+
+ ba,pt %xcc, rtrap_irq
+ nop
+
+sun4v_nonres_mondo:
+ /* Head offset in %g2, tail offset in %g4. */
+ mov INTRQ_NONRESUM_MONDO_HEAD, %g2
+ ldxa [%g2] ASI_QUEUE, %g2
+ mov INTRQ_NONRESUM_MONDO_TAIL, %g4
+ ldxa [%g4] ASI_QUEUE, %g4
+ cmp %g2, %g4
+ be,pn %xcc, sun4v_nonres_mondo_queue_empty
+ nop
+
+ /* Get &trap_block[smp_processor_id()] into %g3. */
+ ldxa [%g0] ASI_SCRATCHPAD, %g3
+ sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
+
+ /* Get RES mondo queue base phys address into %g5. */
+ ldx [%g3 + TRAP_PER_CPU_NONRESUM_MONDO_PA], %g5
+
+ /* Get RES kernel buffer base phys address into %g7. */
+ ldx [%g3 + TRAP_PER_CPU_NONRESUM_KBUF_PA], %g7
+
+ /* If the first word is non-zero, queue is full. */
+ ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
+ brnz,pn %g1, sun4v_nonres_mondo_queue_full
+ nop
+
+ lduw [%g3 + TRAP_PER_CPU_NONRESUM_QMASK], %g4
+
+ /* Remember this entry's offset in %g1. */
+ mov %g2, %g1
+
+ /* Copy 64-byte queue entry into kernel buffer. */
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+ ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
+ stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
+ add %g2, 0x08, %g2
+
+ /* Update queue head pointer. */
+ and %g2, %g4, %g2
+
+ mov INTRQ_NONRESUM_MONDO_HEAD, %g4
+ stxa %g2, [%g4] ASI_QUEUE
+ membar #Sync
+
+ /* Disable interrupts and save register state so we can call
+ * C code. The etrap handling will leave %g4 in %l4 for us
+ * when it's done.
+ */
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ mov %g1, %g4
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ /* Log the event. */
+ add %sp, PTREGS_OFF, %o0
+ call sun4v_nonresum_error
+ mov %l4, %o1
+
+ /* Return from trap. */
+ ba,pt %xcc, rtrap_irq
+ nop
+
+sun4v_nonres_mondo_queue_empty:
+ retry
+
+sun4v_nonres_mondo_queue_full:
+ /* The queue is full, consolidate our damage by setting
+ * the head equal to the tail. We'll just trap again otherwise.
+ * Call C code to log the event.
+ */
+ mov INTRQ_NONRESUM_MONDO_HEAD, %g2
+ stxa %g4, [%g2] ASI_QUEUE
+ membar #Sync
+
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ ba,pt %xcc, etrap_irq
+ rd %pc, %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ call sun4v_nonresum_overflow
+ add %sp, PTREGS_OFF, %o0
+
+ ba,pt %xcc, rtrap_irq
+ nop
diff --git a/arch/sparc64/kernel/sun4v_tlb_miss.S b/arch/sparc/kernel/sun4v_tlb_miss.S
index e1fbf8c75787..e1fbf8c75787 100644
--- a/arch/sparc64/kernel/sun4v_tlb_miss.S
+++ b/arch/sparc/kernel/sun4v_tlb_miss.S
diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc/kernel/sys32.S
index f061c4dda9ef..f061c4dda9ef 100644
--- a/arch/sparc64/kernel/sys32.S
+++ b/arch/sparc/kernel/sys32.S
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index e800503879e4..e800503879e4 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc_32.c
index 03035c852a43..03035c852a43 100644
--- a/arch/sparc/kernel/sys_sparc.c
+++ b/arch/sparc/kernel/sys_sparc_32.c
diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc_64.c
index 39749e32dc7e..39749e32dc7e 100644
--- a/arch/sparc64/kernel/sys_sparc.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
diff --git a/arch/sparc64/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 7a6786a71363..7a6786a71363 100644
--- a/arch/sparc64/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
diff --git a/arch/sparc/kernel/sysfs.c b/arch/sparc/kernel/sysfs.c
new file mode 100644
index 000000000000..d28f496f4669
--- /dev/null
+++ b/arch/sparc/kernel/sysfs.c
@@ -0,0 +1,314 @@
+/* sysfs.c: Toplogy sysfs support code for sparc64.
+ *
+ * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/sysdev.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+
+#include <asm/cpudata.h>
+#include <asm/hypervisor.h>
+#include <asm/spitfire.h>
+
+static DEFINE_PER_CPU(struct hv_mmu_statistics, mmu_stats) __attribute__((aligned(64)));
+
+#define SHOW_MMUSTAT_ULONG(NAME) \
+static ssize_t show_##NAME(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ struct hv_mmu_statistics *p = &per_cpu(mmu_stats, dev->id); \
+ return sprintf(buf, "%lu\n", p->NAME); \
+} \
+static SYSDEV_ATTR(NAME, 0444, show_##NAME, NULL)
+
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_8k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_8k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_64k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_64k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_4mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_4mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_256mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_256mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_8k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_8k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_64k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_64k_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_4mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_4mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_256mb_tte);
+SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_256mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_8k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_8k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_64k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_64k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_4mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_4mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_256mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_256mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_8k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_8k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_64k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_64k_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_4mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_4mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_256mb_tte);
+SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_256mb_tte);
+
+static struct attribute *mmu_stat_attrs[] = {
+ &attr_immu_tsb_hits_ctx0_8k_tte.attr,
+ &attr_immu_tsb_ticks_ctx0_8k_tte.attr,
+ &attr_immu_tsb_hits_ctx0_64k_tte.attr,
+ &attr_immu_tsb_ticks_ctx0_64k_tte.attr,
+ &attr_immu_tsb_hits_ctx0_4mb_tte.attr,
+ &attr_immu_tsb_ticks_ctx0_4mb_tte.attr,
+ &attr_immu_tsb_hits_ctx0_256mb_tte.attr,
+ &attr_immu_tsb_ticks_ctx0_256mb_tte.attr,
+ &attr_immu_tsb_hits_ctxnon0_8k_tte.attr,
+ &attr_immu_tsb_ticks_ctxnon0_8k_tte.attr,
+ &attr_immu_tsb_hits_ctxnon0_64k_tte.attr,
+ &attr_immu_tsb_ticks_ctxnon0_64k_tte.attr,
+ &attr_immu_tsb_hits_ctxnon0_4mb_tte.attr,
+ &attr_immu_tsb_ticks_ctxnon0_4mb_tte.attr,
+ &attr_immu_tsb_hits_ctxnon0_256mb_tte.attr,
+ &attr_immu_tsb_ticks_ctxnon0_256mb_tte.attr,
+ &attr_dmmu_tsb_hits_ctx0_8k_tte.attr,
+ &attr_dmmu_tsb_ticks_ctx0_8k_tte.attr,
+ &attr_dmmu_tsb_hits_ctx0_64k_tte.attr,
+ &attr_dmmu_tsb_ticks_ctx0_64k_tte.attr,
+ &attr_dmmu_tsb_hits_ctx0_4mb_tte.attr,
+ &attr_dmmu_tsb_ticks_ctx0_4mb_tte.attr,
+ &attr_dmmu_tsb_hits_ctx0_256mb_tte.attr,
+ &attr_dmmu_tsb_ticks_ctx0_256mb_tte.attr,
+ &attr_dmmu_tsb_hits_ctxnon0_8k_tte.attr,
+ &attr_dmmu_tsb_ticks_ctxnon0_8k_tte.attr,
+ &attr_dmmu_tsb_hits_ctxnon0_64k_tte.attr,
+ &attr_dmmu_tsb_ticks_ctxnon0_64k_tte.attr,
+ &attr_dmmu_tsb_hits_ctxnon0_4mb_tte.attr,
+ &attr_dmmu_tsb_ticks_ctxnon0_4mb_tte.attr,
+ &attr_dmmu_tsb_hits_ctxnon0_256mb_tte.attr,
+ &attr_dmmu_tsb_ticks_ctxnon0_256mb_tte.attr,
+ NULL,
+};
+
+static struct attribute_group mmu_stat_group = {
+ .attrs = mmu_stat_attrs,
+ .name = "mmu_stats",
+};
+
+/* XXX convert to rusty's on_one_cpu */
+static unsigned long run_on_cpu(unsigned long cpu,
+ unsigned long (*func)(unsigned long),
+ unsigned long arg)
+{
+ cpumask_t old_affinity = current->cpus_allowed;
+ unsigned long ret;
+
+ /* should return -EINVAL to userspace */
+ if (set_cpus_allowed(current, cpumask_of_cpu(cpu)))
+ return 0;
+
+ ret = func(arg);
+
+ set_cpus_allowed(current, old_affinity);
+
+ return ret;
+}
+
+static unsigned long read_mmustat_enable(unsigned long junk)
+{
+ unsigned long ra = 0;
+
+ sun4v_mmustat_info(&ra);
+
+ return ra != 0;
+}
+
+static unsigned long write_mmustat_enable(unsigned long val)
+{
+ unsigned long ra, orig_ra;
+
+ if (val)
+ ra = __pa(&per_cpu(mmu_stats, smp_processor_id()));
+ else
+ ra = 0UL;
+
+ return sun4v_mmustat_conf(ra, &orig_ra);
+}
+
+static ssize_t show_mmustat_enable(struct sys_device *s,
+ struct sysdev_attribute *attr, char *buf)
+{
+ unsigned long val = run_on_cpu(s->id, read_mmustat_enable, 0);
+ return sprintf(buf, "%lx\n", val);
+}
+
+static ssize_t store_mmustat_enable(struct sys_device *s,
+ struct sysdev_attribute *attr, const char *buf,
+ size_t count)
+{
+ unsigned long val, err;
+ int ret = sscanf(buf, "%ld", &val);
+
+ if (ret != 1)
+ return -EINVAL;
+
+ err = run_on_cpu(s->id, write_mmustat_enable, val);
+ if (err)
+ return -EIO;
+
+ return count;
+}
+
+static SYSDEV_ATTR(mmustat_enable, 0644, show_mmustat_enable, store_mmustat_enable);
+
+static int mmu_stats_supported;
+
+static int register_mmu_stats(struct sys_device *s)
+{
+ if (!mmu_stats_supported)
+ return 0;
+ sysdev_create_file(s, &attr_mmustat_enable);
+ return sysfs_create_group(&s->kobj, &mmu_stat_group);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void unregister_mmu_stats(struct sys_device *s)
+{
+ if (!mmu_stats_supported)
+ return;
+ sysfs_remove_group(&s->kobj, &mmu_stat_group);
+ sysdev_remove_file(s, &attr_mmustat_enable);
+}
+#endif
+
+#define SHOW_CPUDATA_ULONG_NAME(NAME, MEMBER) \
+static ssize_t show_##NAME(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ cpuinfo_sparc *c = &cpu_data(dev->id); \
+ return sprintf(buf, "%lu\n", c->MEMBER); \
+}
+
+#define SHOW_CPUDATA_UINT_NAME(NAME, MEMBER) \
+static ssize_t show_##NAME(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ cpuinfo_sparc *c = &cpu_data(dev->id); \
+ return sprintf(buf, "%u\n", c->MEMBER); \
+}
+
+SHOW_CPUDATA_ULONG_NAME(clock_tick, clock_tick);
+SHOW_CPUDATA_UINT_NAME(l1_dcache_size, dcache_size);
+SHOW_CPUDATA_UINT_NAME(l1_dcache_line_size, dcache_line_size);
+SHOW_CPUDATA_UINT_NAME(l1_icache_size, icache_size);
+SHOW_CPUDATA_UINT_NAME(l1_icache_line_size, icache_line_size);
+SHOW_CPUDATA_UINT_NAME(l2_cache_size, ecache_size);
+SHOW_CPUDATA_UINT_NAME(l2_cache_line_size, ecache_line_size);
+
+static struct sysdev_attribute cpu_core_attrs[] = {
+ _SYSDEV_ATTR(clock_tick, 0444, show_clock_tick, NULL),
+ _SYSDEV_ATTR(l1_dcache_size, 0444, show_l1_dcache_size, NULL),
+ _SYSDEV_ATTR(l1_dcache_line_size, 0444, show_l1_dcache_line_size, NULL),
+ _SYSDEV_ATTR(l1_icache_size, 0444, show_l1_icache_size, NULL),
+ _SYSDEV_ATTR(l1_icache_line_size, 0444, show_l1_icache_line_size, NULL),
+ _SYSDEV_ATTR(l2_cache_size, 0444, show_l2_cache_size, NULL),
+ _SYSDEV_ATTR(l2_cache_line_size, 0444, show_l2_cache_line_size, NULL),
+};
+
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+
+static void register_cpu_online(unsigned int cpu)
+{
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
+ struct sys_device *s = &c->sysdev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
+ sysdev_create_file(s, &cpu_core_attrs[i]);
+
+ register_mmu_stats(s);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void unregister_cpu_online(unsigned int cpu)
+{
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
+ struct sys_device *s = &c->sysdev;
+ int i;
+
+ unregister_mmu_stats(s);
+ for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
+ sysdev_remove_file(s, &cpu_core_attrs[i]);
+}
+#endif
+
+static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned int)(long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ register_cpu_online(cpu);
+ break;
+#ifdef CONFIG_HOTPLUG_CPU
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ unregister_cpu_online(cpu);
+ break;
+#endif
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata sysfs_cpu_nb = {
+ .notifier_call = sysfs_cpu_notify,
+};
+
+static void __init check_mmu_stats(void)
+{
+ unsigned long dummy1, err;
+
+ if (tlb_type != hypervisor)
+ return;
+
+ err = sun4v_mmustat_info(&dummy1);
+ if (!err)
+ mmu_stats_supported = 1;
+}
+
+static void register_nodes(void)
+{
+#ifdef CONFIG_NUMA
+ int i;
+
+ for (i = 0; i < MAX_NUMNODES; i++)
+ register_one_node(i);
+#endif
+}
+
+static int __init topology_init(void)
+{
+ int cpu;
+
+ register_nodes();
+
+ check_mmu_stats();
+
+ register_cpu_notifier(&sysfs_cpu_nb);
+
+ for_each_possible_cpu(cpu) {
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
+
+ register_cpu(c, cpu);
+ if (cpu_online(cpu))
+ register_cpu_online(cpu);
+ }
+
+ return 0;
+}
+
+subsys_initcall(topology_init);
diff --git a/arch/sparc64/kernel/systbls.h b/arch/sparc/kernel/systbls.h
index bc9f5dac4069..bc9f5dac4069 100644
--- a/arch/sparc64/kernel/systbls.h
+++ b/arch/sparc/kernel/systbls.h
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls_32.S
index 7d0807586442..7d0807586442 100644
--- a/arch/sparc/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls_32.S
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc/kernel/systbls_64.S
index 9fc78cf354bd..9fc78cf354bd 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls_64.S
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time_32.c
index 00f7383c7657..00f7383c7657 100644
--- a/arch/sparc/kernel/time.c
+++ b/arch/sparc/kernel/time_32.c
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc/kernel/time_64.c
index 9df8f095a8b1..9df8f095a8b1 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc/kernel/time_64.c
diff --git a/arch/sparc/kernel/trampoline.S b/arch/sparc/kernel/trampoline_32.S
index 5e235c52d667..5e235c52d667 100644
--- a/arch/sparc/kernel/trampoline.S
+++ b/arch/sparc/kernel/trampoline_32.S
diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
new file mode 100644
index 000000000000..da1b781b5e65
--- /dev/null
+++ b/arch/sparc/kernel/trampoline_64.S
@@ -0,0 +1,417 @@
+/*
+ * trampoline.S: Jump start slave processors on sparc64.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/init.h>
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/lsu.h>
+#include <asm/dcr.h>
+#include <asm/dcu.h>
+#include <asm/pstate.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/spitfire.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+#include <asm/mmu.h>
+#include <asm/hypervisor.h>
+#include <asm/cpudata.h>
+
+ .data
+ .align 8
+call_method:
+ .asciz "call-method"
+ .align 8
+itlb_load:
+ .asciz "SUNW,itlb-load"
+ .align 8
+dtlb_load:
+ .asciz "SUNW,dtlb-load"
+
+ /* XXX __cpuinit this thing XXX */
+#define TRAMP_STACK_SIZE 1024
+ .align 16
+tramp_stack:
+ .skip TRAMP_STACK_SIZE
+
+ __CPUINIT
+ .align 8
+ .globl sparc64_cpu_startup, sparc64_cpu_startup_end
+sparc64_cpu_startup:
+ BRANCH_IF_SUN4V(g1, niagara_startup)
+ BRANCH_IF_CHEETAH_BASE(g1, g5, cheetah_startup)
+ BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1, g5, cheetah_plus_startup)
+
+ ba,pt %xcc, spitfire_startup
+ nop
+
+cheetah_plus_startup:
+ /* Preserve OBP chosen DCU and DCR register settings. */
+ ba,pt %xcc, cheetah_generic_startup
+ nop
+
+cheetah_startup:
+ mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
+ wr %g1, %asr18
+
+ sethi %uhi(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g5
+ or %g5, %ulo(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g5
+ sllx %g5, 32, %g5
+ or %g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5
+ stxa %g5, [%g0] ASI_DCU_CONTROL_REG
+ membar #Sync
+ /* fallthru */
+
+cheetah_generic_startup:
+ mov TSB_EXTENSION_P, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_S, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ membar #Sync
+
+ mov TSB_EXTENSION_N, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ stxa %g0, [%g3] ASI_IMMU
+ membar #Sync
+ /* fallthru */
+
+niagara_startup:
+ /* Disable STICK_INT interrupts. */
+ sethi %hi(0x80000000), %g5
+ sllx %g5, 32, %g5
+ wr %g5, %asr25
+
+ ba,pt %xcc, startup_continue
+ nop
+
+spitfire_startup:
+ mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1
+ stxa %g1, [%g0] ASI_LSU_CONTROL
+ membar #Sync
+
+startup_continue:
+ mov %o0, %l0
+ BRANCH_IF_SUN4V(g1, niagara_lock_tlb)
+
+ sethi %hi(0x80000000), %g2
+ sllx %g2, 32, %g2
+ wr %g2, 0, %tick_cmpr
+
+ /* Call OBP by hand to lock KERNBASE into i/d tlbs.
+ * We lock 'num_kernel_image_mappings' consequetive entries.
+ */
+ sethi %hi(prom_entry_lock), %g2
+1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
+ brnz,pn %g1, 1b
+ nop
+
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x10], %l2
+ add %l2, -(192 + 128), %sp
+ flushw
+
+ /* Setup the loop variables:
+ * %l3: VADDR base
+ * %l4: TTE base
+ * %l5: Loop iterator, iterates from 0 to 'num_kernel_image_mappings'
+ * %l6: Number of TTE entries to map
+ * %l7: Highest TTE entry number, we count down
+ */
+ sethi %hi(KERNBASE), %l3
+ sethi %hi(kern_locked_tte_data), %l4
+ ldx [%l4 + %lo(kern_locked_tte_data)], %l4
+ clr %l5
+ sethi %hi(num_kernel_image_mappings), %l6
+ lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
+ add %l6, 1, %l6
+
+ mov 15, %l7
+ BRANCH_IF_ANY_CHEETAH(g1,g5,2f)
+
+ mov 63, %l7
+2:
+
+3:
+ /* Lock into I-MMU */
+ sethi %hi(call_method), %g2
+ or %g2, %lo(call_method), %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 5, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ sethi %hi(itlb_load), %g2
+ or %g2, %lo(itlb_load), %g2
+ stx %g2, [%sp + 2047 + 128 + 0x18]
+ sethi %hi(prom_mmu_ihandle_cache), %g2
+ lduw [%g2 + %lo(prom_mmu_ihandle_cache)], %g2
+ stx %g2, [%sp + 2047 + 128 + 0x20]
+
+ /* Each TTE maps 4MB, convert index to offset. */
+ sllx %l5, 22, %g1
+
+ add %l3, %g1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x28] ! VADDR
+ add %l4, %g1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x30] ! TTE
+
+ /* TTE index is highest minus loop index. */
+ sub %l7, %l5, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x38]
+
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+ /* Lock into D-MMU */
+ sethi %hi(call_method), %g2
+ or %g2, %lo(call_method), %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 5, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ sethi %hi(dtlb_load), %g2
+ or %g2, %lo(dtlb_load), %g2
+ stx %g2, [%sp + 2047 + 128 + 0x18]
+ sethi %hi(prom_mmu_ihandle_cache), %g2
+ lduw [%g2 + %lo(prom_mmu_ihandle_cache)], %g2
+ stx %g2, [%sp + 2047 + 128 + 0x20]
+
+ /* Each TTE maps 4MB, convert index to offset. */
+ sllx %l5, 22, %g1
+
+ add %l3, %g1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x28] ! VADDR
+ add %l4, %g1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x30] ! TTE
+
+ /* TTE index is highest minus loop index. */
+ sub %l7, %l5, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x38]
+
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+ add %l5, 1, %l5
+ cmp %l5, %l6
+ bne,pt %xcc, 3b
+ nop
+
+ sethi %hi(prom_entry_lock), %g2
+ stb %g0, [%g2 + %lo(prom_entry_lock)]
+
+ ba,pt %xcc, after_lock_tlb
+ nop
+
+niagara_lock_tlb:
+ sethi %hi(KERNBASE), %l3
+ sethi %hi(kern_locked_tte_data), %l4
+ ldx [%l4 + %lo(kern_locked_tte_data)], %l4
+ clr %l5
+ sethi %hi(num_kernel_image_mappings), %l6
+ lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
+ add %l6, 1, %l6
+
+1:
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ sllx %l5, 22, %g2
+ add %l3, %g2, %o0
+ clr %o1
+ add %l4, %g2, %o2
+ mov HV_MMU_IMMU, %o3
+ ta HV_FAST_TRAP
+
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ sllx %l5, 22, %g2
+ add %l3, %g2, %o0
+ clr %o1
+ add %l4, %g2, %o2
+ mov HV_MMU_DMMU, %o3
+ ta HV_FAST_TRAP
+
+ add %l5, 1, %l5
+ cmp %l5, %l6
+ bne,pt %xcc, 1b
+ nop
+
+after_lock_tlb:
+ wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
+ wr %g0, 0, %fprs
+
+ wr %g0, ASI_P, %asi
+
+ mov PRIMARY_CONTEXT, %g7
+
+661: stxa %g0, [%g7] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g0, [%g7] ASI_MMU
+ .previous
+
+ membar #Sync
+ mov SECONDARY_CONTEXT, %g7
+
+661: stxa %g0, [%g7] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g0, [%g7] ASI_MMU
+ .previous
+
+ membar #Sync
+
+ /* Everything we do here, until we properly take over the
+ * trap table, must be done with extreme care. We cannot
+ * make any references to %g6 (current thread pointer),
+ * %g4 (current task pointer), or %g5 (base of current cpu's
+ * per-cpu area) until we properly take over the trap table
+ * from the firmware and hypervisor.
+ *
+ * Get onto temporary stack which is in the locked kernel image.
+ */
+ sethi %hi(tramp_stack), %g1
+ or %g1, %lo(tramp_stack), %g1
+ add %g1, TRAMP_STACK_SIZE, %g1
+ sub %g1, STACKFRAME_SZ + STACK_BIAS + 256, %sp
+ mov 0, %fp
+
+ /* Put garbage in these registers to trap any access to them. */
+ set 0xdeadbeef, %g4
+ set 0xdeadbeef, %g5
+ set 0xdeadbeef, %g6
+
+ call init_irqwork_curcpu
+ nop
+
+ sethi %hi(tlb_type), %g3
+ lduw [%g3 + %lo(tlb_type)], %g2
+ cmp %g2, 3
+ bne,pt %icc, 1f
+ nop
+
+ call hard_smp_processor_id
+ nop
+
+ call sun4v_register_mondo_queues
+ nop
+
+1: call init_cur_cpu_trap
+ ldx [%l0], %o0
+
+ /* Start using proper page size encodings in ctx register. */
+ sethi %hi(sparc64_kern_pri_context), %g3
+ ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
+ mov PRIMARY_CONTEXT, %g1
+
+661: stxa %g2, [%g1] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g2, [%g1] ASI_MMU
+ .previous
+
+ membar #Sync
+
+ wrpr %g0, 0, %wstate
+
+ sethi %hi(prom_entry_lock), %g2
+1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
+ brnz,pn %g1, 1b
+ nop
+
+ /* As a hack, put &init_thread_union into %g6.
+ * prom_world() loads from here to restore the %asi
+ * register.
+ */
+ sethi %hi(init_thread_union), %g6
+ or %g6, %lo(init_thread_union), %g6
+
+ sethi %hi(is_sun4v), %o0
+ lduw [%o0 + %lo(is_sun4v)], %o0
+ brz,pt %o0, 2f
+ nop
+
+ TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
+ add %g2, TRAP_PER_CPU_FAULT_INFO, %g2
+ stxa %g2, [%g0] ASI_SCRATCHPAD
+
+ /* Compute physical address:
+ *
+ * paddr = kern_base + (mmfsa_vaddr - KERNBASE)
+ */
+ sethi %hi(KERNBASE), %g3
+ sub %g2, %g3, %g2
+ sethi %hi(kern_base), %g3
+ ldx [%g3 + %lo(kern_base)], %g3
+ add %g2, %g3, %o1
+ sethi %hi(sparc64_ttable_tl0), %o0
+
+ set prom_set_trap_table_name, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 2, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 0, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ stx %o0, [%sp + 2047 + 128 + 0x18]
+ stx %o1, [%sp + 2047 + 128 + 0x20]
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+ ba,pt %xcc, 3f
+ nop
+
+2: sethi %hi(sparc64_ttable_tl0), %o0
+ set prom_set_trap_table_name, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x00]
+ mov 1, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x08]
+ mov 0, %g2
+ stx %g2, [%sp + 2047 + 128 + 0x10]
+ stx %o0, [%sp + 2047 + 128 + 0x18]
+ sethi %hi(p1275buf), %g2
+ or %g2, %lo(p1275buf), %g2
+ ldx [%g2 + 0x08], %o1
+ call %o1
+ add %sp, (2047 + 128), %o0
+
+3: sethi %hi(prom_entry_lock), %g2
+ stb %g0, [%g2 + %lo(prom_entry_lock)]
+
+ ldx [%l0], %g6
+ ldx [%g6 + TI_TASK], %g4
+
+ mov 1, %g5
+ sllx %g5, THREAD_SHIFT, %g5
+ sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
+ add %g6, %g5, %sp
+ mov 0, %fp
+
+ rdpr %pstate, %o1
+ or %o1, PSTATE_IE, %o1
+ wrpr %o1, 0, %pstate
+
+ call smp_callin
+ nop
+ call cpu_idle
+ mov 0, %o0
+ call cpu_panic
+ nop
+1: b,a,pt %xcc, 1b
+
+ .align 8
+sparc64_cpu_startup_end:
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
deleted file mode 100644
index 2b7d50659036..000000000000
--- a/arch/sparc/kernel/traps.c
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * arch/sparc/kernel/traps.c
- *
- * Copyright 1995, 2008 David S. Miller (davem@davemloft.net)
- * Copyright 2000 Jakub Jelinek (jakub@redhat.com)
- */
-
-/*
- * I hate traps on the sparc, grrr...
- */
-
-#include <linux/sched.h> /* for jiffies */
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/smp.h>
-#include <linux/smp_lock.h>
-#include <linux/kdebug.h>
-
-#include <asm/delay.h>
-#include <asm/system.h>
-#include <asm/ptrace.h>
-#include <asm/oplib.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/unistd.h>
-#include <asm/traps.h>
-
-/* #define TRAP_DEBUG */
-
-struct trap_trace_entry {
- unsigned long pc;
- unsigned long type;
-};
-
-void syscall_trace_entry(struct pt_regs *regs)
-{
- printk("%s[%d]: ", current->comm, task_pid_nr(current));
- printk("scall<%d> (could be %d)\n", (int) regs->u_regs[UREG_G1],
- (int) regs->u_regs[UREG_I0]);
-}
-
-void syscall_trace_exit(struct pt_regs *regs)
-{
-}
-
-void sun4d_nmi(struct pt_regs *regs)
-{
- printk("Aieee: sun4d NMI received!\n");
- printk("you lose buddy boy...\n");
- show_regs(regs);
- prom_halt();
-}
-
-static void instruction_dump(unsigned long *pc)
-{
- int i;
-
- if((((unsigned long) pc) & 3))
- return;
-
- for(i = -3; i < 6; i++)
- printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
- printk("\n");
-}
-
-#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
-#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
-
-void die_if_kernel(char *str, struct pt_regs *regs)
-{
- static int die_counter;
- int count = 0;
-
- /* Amuse the user. */
- printk(
-" \\|/ ____ \\|/\n"
-" \"@'/ ,. \\`@\"\n"
-" /_| \\__/ |_\\\n"
-" \\__U_/\n");
-
- printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
- show_regs(regs);
- add_taint(TAINT_DIE);
-
- __SAVE; __SAVE; __SAVE; __SAVE;
- __SAVE; __SAVE; __SAVE; __SAVE;
- __RESTORE; __RESTORE; __RESTORE; __RESTORE;
- __RESTORE; __RESTORE; __RESTORE; __RESTORE;
-
- {
- struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
-
- /* Stop the back trace when we hit userland or we
- * find some badly aligned kernel stack. Set an upper
- * bound in case our stack is trashed and we loop.
- */
- while(rw &&
- count++ < 30 &&
- (((unsigned long) rw) >= PAGE_OFFSET) &&
- !(((unsigned long) rw) & 0x7)) {
- printk("Caller[%08lx]: %pS\n", rw->ins[7],
- (void *) rw->ins[7]);
- rw = (struct reg_window *)rw->ins[6];
- }
- }
- printk("Instruction DUMP:");
- instruction_dump ((unsigned long *) regs->pc);
- if(regs->psr & PSR_PS)
- do_exit(SIGKILL);
- do_exit(SIGSEGV);
-}
-
-void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
-{
- siginfo_t info;
-
- if(type < 0x80) {
- /* Sun OS's puke from bad traps, Linux survives! */
- printk("Unimplemented Sparc TRAP, type = %02lx\n", type);
- die_if_kernel("Whee... Hello Mr. Penguin", regs);
- }
-
- if(regs->psr & PSR_PS)
- die_if_kernel("Kernel bad trap", regs);
-
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_ILLTRP;
- info.si_addr = (void __user *)regs->pc;
- info.si_trapno = type - 0x80;
- force_sig_info(SIGILL, &info, current);
-}
-
-void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- extern int do_user_muldiv (struct pt_regs *, unsigned long);
- siginfo_t info;
-
- if(psr & PSR_PS)
- die_if_kernel("Kernel illegal instruction", regs);
-#ifdef TRAP_DEBUG
- printk("Ill instr. at pc=%08lx instruction is %08lx\n",
- regs->pc, *(unsigned long *)regs->pc);
-#endif
- if (!do_user_muldiv (regs, pc))
- return;
-
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_ILLOPC;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGILL, &info, current);
-}
-
-void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
- if(psr & PSR_PS)
- die_if_kernel("Penguin instruction from Penguin mode??!?!", regs);
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_PRVOPC;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGILL, &info, current);
-}
-
-/* XXX User may want to be allowed to do this. XXX */
-
-void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
- if(regs->psr & PSR_PS) {
- printk("KERNEL MNA at pc %08lx npc %08lx called by %08lx\n", pc, npc,
- regs->u_regs[UREG_RETPC]);
- die_if_kernel("BOGUS", regs);
- /* die_if_kernel("Kernel MNA access", regs); */
- }
-#if 0
- show_regs (regs);
- instruction_dump ((unsigned long *) regs->pc);
- printk ("do_MNA!\n");
-#endif
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_ADRALN;
- info.si_addr = /* FIXME: Should dig out mna address */ (void *)0;
- info.si_trapno = 0;
- send_sig_info(SIGBUS, &info, current);
-}
-
-extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
- void *fpqueue, unsigned long *fpqdepth);
-extern void fpload(unsigned long *fpregs, unsigned long *fsr);
-
-static unsigned long init_fsr = 0x0UL;
-static unsigned long init_fregs[32] __attribute__ ((aligned (8))) =
- { ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
- ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
- ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
- ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL };
-
-void do_fpd_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- /* Sanity check... */
- if(psr & PSR_PS)
- die_if_kernel("Kernel gets FloatingPenguinUnit disabled trap", regs);
-
- put_psr(get_psr() | PSR_EF); /* Allow FPU ops. */
- regs->psr |= PSR_EF;
-#ifndef CONFIG_SMP
- if(last_task_used_math == current)
- return;
- if(last_task_used_math) {
- /* Other processes fpu state, save away */
- struct task_struct *fptask = last_task_used_math;
- fpsave(&fptask->thread.float_regs[0], &fptask->thread.fsr,
- &fptask->thread.fpqueue[0], &fptask->thread.fpqdepth);
- }
- last_task_used_math = current;
- if(used_math()) {
- fpload(&current->thread.float_regs[0], &current->thread.fsr);
- } else {
- /* Set initial sane state. */
- fpload(&init_fregs[0], &init_fsr);
- set_used_math();
- }
-#else
- if(!used_math()) {
- fpload(&init_fregs[0], &init_fsr);
- set_used_math();
- } else {
- fpload(&current->thread.float_regs[0], &current->thread.fsr);
- }
- set_thread_flag(TIF_USEDFPU);
-#endif
-}
-
-static unsigned long fake_regs[32] __attribute__ ((aligned (8)));
-static unsigned long fake_fsr;
-static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
-static unsigned long fake_depth;
-
-extern int do_mathemu(struct pt_regs *, struct task_struct *);
-
-void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- static int calls;
- siginfo_t info;
- unsigned long fsr;
- int ret = 0;
-#ifndef CONFIG_SMP
- struct task_struct *fpt = last_task_used_math;
-#else
- struct task_struct *fpt = current;
-#endif
- put_psr(get_psr() | PSR_EF);
- /* If nobody owns the fpu right now, just clear the
- * error into our fake static buffer and hope it don't
- * happen again. Thank you crashme...
- */
-#ifndef CONFIG_SMP
- if(!fpt) {
-#else
- if (!test_tsk_thread_flag(fpt, TIF_USEDFPU)) {
-#endif
- fpsave(&fake_regs[0], &fake_fsr, &fake_queue[0], &fake_depth);
- regs->psr &= ~PSR_EF;
- return;
- }
- fpsave(&fpt->thread.float_regs[0], &fpt->thread.fsr,
- &fpt->thread.fpqueue[0], &fpt->thread.fpqdepth);
-#ifdef DEBUG_FPU
- printk("Hmm, FP exception, fsr was %016lx\n", fpt->thread.fsr);
-#endif
-
- switch ((fpt->thread.fsr & 0x1c000)) {
- /* switch on the contents of the ftt [floating point trap type] field */
-#ifdef DEBUG_FPU
- case (1 << 14):
- printk("IEEE_754_exception\n");
- break;
-#endif
- case (2 << 14): /* unfinished_FPop (underflow & co) */
- case (3 << 14): /* unimplemented_FPop (quad stuff, maybe sqrt) */
- ret = do_mathemu(regs, fpt);
- break;
-#ifdef DEBUG_FPU
- case (4 << 14):
- printk("sequence_error (OS bug...)\n");
- break;
- case (5 << 14):
- printk("hardware_error (uhoh!)\n");
- break;
- case (6 << 14):
- printk("invalid_fp_register (user error)\n");
- break;
-#endif /* DEBUG_FPU */
- }
- /* If we successfully emulated the FPop, we pretend the trap never happened :-> */
- if (ret) {
- fpload(&current->thread.float_regs[0], &current->thread.fsr);
- return;
- }
- /* nope, better SIGFPE the offending process... */
-
-#ifdef CONFIG_SMP
- clear_tsk_thread_flag(fpt, TIF_USEDFPU);
-#endif
- if(psr & PSR_PS) {
- /* The first fsr store/load we tried trapped,
- * the second one will not (we hope).
- */
- printk("WARNING: FPU exception from kernel mode. at pc=%08lx\n",
- regs->pc);
- regs->pc = regs->npc;
- regs->npc += 4;
- calls++;
- if(calls > 2)
- die_if_kernel("Too many Penguin-FPU traps from kernel mode",
- regs);
- return;
- }
-
- fsr = fpt->thread.fsr;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- info.si_code = __SI_FAULT;
- if ((fsr & 0x1c000) == (1 << 14)) {
- if (fsr & 0x10)
- info.si_code = FPE_FLTINV;
- else if (fsr & 0x08)
- info.si_code = FPE_FLTOVF;
- else if (fsr & 0x04)
- info.si_code = FPE_FLTUND;
- else if (fsr & 0x02)
- info.si_code = FPE_FLTDIV;
- else if (fsr & 0x01)
- info.si_code = FPE_FLTRES;
- }
- send_sig_info(SIGFPE, &info, fpt);
-#ifndef CONFIG_SMP
- last_task_used_math = NULL;
-#endif
- regs->psr &= ~PSR_EF;
- if(calls > 0)
- calls=0;
-}
-
-void handle_tag_overflow(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
- if(psr & PSR_PS)
- die_if_kernel("Penguin overflow trap from kernel mode", regs);
- info.si_signo = SIGEMT;
- info.si_errno = 0;
- info.si_code = EMT_TAGOVF;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGEMT, &info, current);
-}
-
-void handle_watchpoint(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
-#ifdef TRAP_DEBUG
- printk("Watchpoint detected at PC %08lx NPC %08lx PSR %08lx\n",
- pc, npc, psr);
-#endif
- if(psr & PSR_PS)
- panic("Tell me what a watchpoint trap is, and I'll then deal "
- "with such a beast...");
-}
-
-void handle_reg_access(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
-#ifdef TRAP_DEBUG
- printk("Register Access Exception at PC %08lx NPC %08lx PSR %08lx\n",
- pc, npc, psr);
-#endif
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_OBJERR;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- force_sig_info(SIGBUS, &info, current);
-}
-
-void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_COPROC;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGILL, &info, current);
-}
-
-void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
-#ifdef TRAP_DEBUG
- printk("Co-Processor Exception at PC %08lx NPC %08lx PSR %08lx\n",
- pc, npc, psr);
-#endif
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_COPROC;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGILL, &info, current);
-}
-
-void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
- unsigned long psr)
-{
- siginfo_t info;
-
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = FPE_INTDIV;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- send_sig_info(SIGFPE, &info, current);
-}
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-void do_BUG(const char *file, int line)
-{
- // bust_spinlocks(1); XXX Not in our original BUG()
- printk("kernel BUG at %s:%d!\n", file, line);
-}
-#endif
-
-/* Since we have our mappings set up, on multiprocessors we can spin them
- * up here so that timer interrupts work during initialization.
- */
-
-extern void sparc_cpu_startup(void);
-
-void trap_init(void)
-{
- extern void thread_info_offsets_are_bolixed_pete(void);
-
- /* Force linker to barf if mismatched */
- if (TI_UWINMASK != offsetof(struct thread_info, uwinmask) ||
- TI_TASK != offsetof(struct thread_info, task) ||
- TI_EXECDOMAIN != offsetof(struct thread_info, exec_domain) ||
- TI_FLAGS != offsetof(struct thread_info, flags) ||
- TI_CPU != offsetof(struct thread_info, cpu) ||
- TI_PREEMPT != offsetof(struct thread_info, preempt_count) ||
- TI_SOFTIRQ != offsetof(struct thread_info, softirq_count) ||
- TI_HARDIRQ != offsetof(struct thread_info, hardirq_count) ||
- TI_KSP != offsetof(struct thread_info, ksp) ||
- TI_KPC != offsetof(struct thread_info, kpc) ||
- TI_KPSR != offsetof(struct thread_info, kpsr) ||
- TI_KWIM != offsetof(struct thread_info, kwim) ||
- TI_REG_WINDOW != offsetof(struct thread_info, reg_window) ||
- TI_RWIN_SPTRS != offsetof(struct thread_info, rwbuf_stkptrs) ||
- TI_W_SAVED != offsetof(struct thread_info, w_saved))
- thread_info_offsets_are_bolixed_pete();
-
- /* Attach to the address space of init_task. */
- atomic_inc(&init_mm.mm_count);
- current->active_mm = &init_mm;
-
- /* NOTE: Other cpus have this done as they are started
- * up on SMP.
- */
-}
diff --git a/arch/sparc/kernel/traps_32.c b/arch/sparc/kernel/traps_32.c
new file mode 100644
index 000000000000..716f3946c494
--- /dev/null
+++ b/arch/sparc/kernel/traps_32.c
@@ -0,0 +1,462 @@
+/*
+ * arch/sparc/kernel/traps.c
+ *
+ * Copyright 1995, 2008 David S. Miller (davem@davemloft.net)
+ * Copyright 2000 Jakub Jelinek (jakub@redhat.com)
+ */
+
+/*
+ * I hate traps on the sparc, grrr...
+ */
+
+#include <linux/sched.h> /* for jiffies */
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kdebug.h>
+
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/unistd.h>
+#include <asm/traps.h>
+
+#include "entry.h"
+#include "kernel.h"
+
+/* #define TRAP_DEBUG */
+
+static void instruction_dump(unsigned long *pc)
+{
+ int i;
+
+ if((((unsigned long) pc) & 3))
+ return;
+
+ for(i = -3; i < 6; i++)
+ printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
+ printk("\n");
+}
+
+#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
+#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
+
+void die_if_kernel(char *str, struct pt_regs *regs)
+{
+ static int die_counter;
+ int count = 0;
+
+ /* Amuse the user. */
+ printk(
+" \\|/ ____ \\|/\n"
+" \"@'/ ,. \\`@\"\n"
+" /_| \\__/ |_\\\n"
+" \\__U_/\n");
+
+ printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
+ show_regs(regs);
+ add_taint(TAINT_DIE);
+
+ __SAVE; __SAVE; __SAVE; __SAVE;
+ __SAVE; __SAVE; __SAVE; __SAVE;
+ __RESTORE; __RESTORE; __RESTORE; __RESTORE;
+ __RESTORE; __RESTORE; __RESTORE; __RESTORE;
+
+ {
+ struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
+
+ /* Stop the back trace when we hit userland or we
+ * find some badly aligned kernel stack. Set an upper
+ * bound in case our stack is trashed and we loop.
+ */
+ while(rw &&
+ count++ < 30 &&
+ (((unsigned long) rw) >= PAGE_OFFSET) &&
+ !(((unsigned long) rw) & 0x7)) {
+ printk("Caller[%08lx]: %pS\n", rw->ins[7],
+ (void *) rw->ins[7]);
+ rw = (struct reg_window *)rw->ins[6];
+ }
+ }
+ printk("Instruction DUMP:");
+ instruction_dump ((unsigned long *) regs->pc);
+ if(regs->psr & PSR_PS)
+ do_exit(SIGKILL);
+ do_exit(SIGSEGV);
+}
+
+void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
+{
+ siginfo_t info;
+
+ if(type < 0x80) {
+ /* Sun OS's puke from bad traps, Linux survives! */
+ printk("Unimplemented Sparc TRAP, type = %02lx\n", type);
+ die_if_kernel("Whee... Hello Mr. Penguin", regs);
+ }
+
+ if(regs->psr & PSR_PS)
+ die_if_kernel("Kernel bad trap", regs);
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLTRP;
+ info.si_addr = (void __user *)regs->pc;
+ info.si_trapno = type - 0x80;
+ force_sig_info(SIGILL, &info, current);
+}
+
+void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ if(psr & PSR_PS)
+ die_if_kernel("Kernel illegal instruction", regs);
+#ifdef TRAP_DEBUG
+ printk("Ill instr. at pc=%08lx instruction is %08lx\n",
+ regs->pc, *(unsigned long *)regs->pc);
+#endif
+ if (!do_user_muldiv (regs, pc))
+ return;
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPC;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGILL, &info, current);
+}
+
+void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ if(psr & PSR_PS)
+ die_if_kernel("Penguin instruction from Penguin mode??!?!", regs);
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_PRVOPC;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGILL, &info, current);
+}
+
+/* XXX User may want to be allowed to do this. XXX */
+
+void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ if(regs->psr & PSR_PS) {
+ printk("KERNEL MNA at pc %08lx npc %08lx called by %08lx\n", pc, npc,
+ regs->u_regs[UREG_RETPC]);
+ die_if_kernel("BOGUS", regs);
+ /* die_if_kernel("Kernel MNA access", regs); */
+ }
+#if 0
+ show_regs (regs);
+ instruction_dump ((unsigned long *) regs->pc);
+ printk ("do_MNA!\n");
+#endif
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = /* FIXME: Should dig out mna address */ (void *)0;
+ info.si_trapno = 0;
+ send_sig_info(SIGBUS, &info, current);
+}
+
+static unsigned long init_fsr = 0x0UL;
+static unsigned long init_fregs[32] __attribute__ ((aligned (8))) =
+ { ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+ ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+ ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
+ ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL };
+
+void do_fpd_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ /* Sanity check... */
+ if(psr & PSR_PS)
+ die_if_kernel("Kernel gets FloatingPenguinUnit disabled trap", regs);
+
+ put_psr(get_psr() | PSR_EF); /* Allow FPU ops. */
+ regs->psr |= PSR_EF;
+#ifndef CONFIG_SMP
+ if(last_task_used_math == current)
+ return;
+ if(last_task_used_math) {
+ /* Other processes fpu state, save away */
+ struct task_struct *fptask = last_task_used_math;
+ fpsave(&fptask->thread.float_regs[0], &fptask->thread.fsr,
+ &fptask->thread.fpqueue[0], &fptask->thread.fpqdepth);
+ }
+ last_task_used_math = current;
+ if(used_math()) {
+ fpload(&current->thread.float_regs[0], &current->thread.fsr);
+ } else {
+ /* Set initial sane state. */
+ fpload(&init_fregs[0], &init_fsr);
+ set_used_math();
+ }
+#else
+ if(!used_math()) {
+ fpload(&init_fregs[0], &init_fsr);
+ set_used_math();
+ } else {
+ fpload(&current->thread.float_regs[0], &current->thread.fsr);
+ }
+ set_thread_flag(TIF_USEDFPU);
+#endif
+}
+
+static unsigned long fake_regs[32] __attribute__ ((aligned (8)));
+static unsigned long fake_fsr;
+static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
+static unsigned long fake_depth;
+
+extern int do_mathemu(struct pt_regs *, struct task_struct *);
+
+void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ static int calls;
+ siginfo_t info;
+ unsigned long fsr;
+ int ret = 0;
+#ifndef CONFIG_SMP
+ struct task_struct *fpt = last_task_used_math;
+#else
+ struct task_struct *fpt = current;
+#endif
+ put_psr(get_psr() | PSR_EF);
+ /* If nobody owns the fpu right now, just clear the
+ * error into our fake static buffer and hope it don't
+ * happen again. Thank you crashme...
+ */
+#ifndef CONFIG_SMP
+ if(!fpt) {
+#else
+ if (!test_tsk_thread_flag(fpt, TIF_USEDFPU)) {
+#endif
+ fpsave(&fake_regs[0], &fake_fsr, &fake_queue[0], &fake_depth);
+ regs->psr &= ~PSR_EF;
+ return;
+ }
+ fpsave(&fpt->thread.float_regs[0], &fpt->thread.fsr,
+ &fpt->thread.fpqueue[0], &fpt->thread.fpqdepth);
+#ifdef DEBUG_FPU
+ printk("Hmm, FP exception, fsr was %016lx\n", fpt->thread.fsr);
+#endif
+
+ switch ((fpt->thread.fsr & 0x1c000)) {
+ /* switch on the contents of the ftt [floating point trap type] field */
+#ifdef DEBUG_FPU
+ case (1 << 14):
+ printk("IEEE_754_exception\n");
+ break;
+#endif
+ case (2 << 14): /* unfinished_FPop (underflow & co) */
+ case (3 << 14): /* unimplemented_FPop (quad stuff, maybe sqrt) */
+ ret = do_mathemu(regs, fpt);
+ break;
+#ifdef DEBUG_FPU
+ case (4 << 14):
+ printk("sequence_error (OS bug...)\n");
+ break;
+ case (5 << 14):
+ printk("hardware_error (uhoh!)\n");
+ break;
+ case (6 << 14):
+ printk("invalid_fp_register (user error)\n");
+ break;
+#endif /* DEBUG_FPU */
+ }
+ /* If we successfully emulated the FPop, we pretend the trap never happened :-> */
+ if (ret) {
+ fpload(&current->thread.float_regs[0], &current->thread.fsr);
+ return;
+ }
+ /* nope, better SIGFPE the offending process... */
+
+#ifdef CONFIG_SMP
+ clear_tsk_thread_flag(fpt, TIF_USEDFPU);
+#endif
+ if(psr & PSR_PS) {
+ /* The first fsr store/load we tried trapped,
+ * the second one will not (we hope).
+ */
+ printk("WARNING: FPU exception from kernel mode. at pc=%08lx\n",
+ regs->pc);
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ calls++;
+ if(calls > 2)
+ die_if_kernel("Too many Penguin-FPU traps from kernel mode",
+ regs);
+ return;
+ }
+
+ fsr = fpt->thread.fsr;
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ info.si_code = __SI_FAULT;
+ if ((fsr & 0x1c000) == (1 << 14)) {
+ if (fsr & 0x10)
+ info.si_code = FPE_FLTINV;
+ else if (fsr & 0x08)
+ info.si_code = FPE_FLTOVF;
+ else if (fsr & 0x04)
+ info.si_code = FPE_FLTUND;
+ else if (fsr & 0x02)
+ info.si_code = FPE_FLTDIV;
+ else if (fsr & 0x01)
+ info.si_code = FPE_FLTRES;
+ }
+ send_sig_info(SIGFPE, &info, fpt);
+#ifndef CONFIG_SMP
+ last_task_used_math = NULL;
+#endif
+ regs->psr &= ~PSR_EF;
+ if(calls > 0)
+ calls=0;
+}
+
+void handle_tag_overflow(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ if(psr & PSR_PS)
+ die_if_kernel("Penguin overflow trap from kernel mode", regs);
+ info.si_signo = SIGEMT;
+ info.si_errno = 0;
+ info.si_code = EMT_TAGOVF;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGEMT, &info, current);
+}
+
+void handle_watchpoint(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+#ifdef TRAP_DEBUG
+ printk("Watchpoint detected at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+#endif
+ if(psr & PSR_PS)
+ panic("Tell me what a watchpoint trap is, and I'll then deal "
+ "with such a beast...");
+}
+
+void handle_reg_access(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+#ifdef TRAP_DEBUG
+ printk("Register Access Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+#endif
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_OBJERR;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
+}
+
+void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGILL, &info, current);
+}
+
+void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+#ifdef TRAP_DEBUG
+ printk("Co-Processor Exception at PC %08lx NPC %08lx PSR %08lx\n",
+ pc, npc, psr);
+#endif
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGILL, &info, current);
+}
+
+void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
+ unsigned long psr)
+{
+ siginfo_t info;
+
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = FPE_INTDIV;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ send_sig_info(SIGFPE, &info, current);
+}
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+void do_BUG(const char *file, int line)
+{
+ // bust_spinlocks(1); XXX Not in our original BUG()
+ printk("kernel BUG at %s:%d!\n", file, line);
+}
+#endif
+
+/* Since we have our mappings set up, on multiprocessors we can spin them
+ * up here so that timer interrupts work during initialization.
+ */
+
+void trap_init(void)
+{
+ extern void thread_info_offsets_are_bolixed_pete(void);
+
+ /* Force linker to barf if mismatched */
+ if (TI_UWINMASK != offsetof(struct thread_info, uwinmask) ||
+ TI_TASK != offsetof(struct thread_info, task) ||
+ TI_EXECDOMAIN != offsetof(struct thread_info, exec_domain) ||
+ TI_FLAGS != offsetof(struct thread_info, flags) ||
+ TI_CPU != offsetof(struct thread_info, cpu) ||
+ TI_PREEMPT != offsetof(struct thread_info, preempt_count) ||
+ TI_SOFTIRQ != offsetof(struct thread_info, softirq_count) ||
+ TI_HARDIRQ != offsetof(struct thread_info, hardirq_count) ||
+ TI_KSP != offsetof(struct thread_info, ksp) ||
+ TI_KPC != offsetof(struct thread_info, kpc) ||
+ TI_KPSR != offsetof(struct thread_info, kpsr) ||
+ TI_KWIM != offsetof(struct thread_info, kwim) ||
+ TI_REG_WINDOW != offsetof(struct thread_info, reg_window) ||
+ TI_RWIN_SPTRS != offsetof(struct thread_info, rwbuf_stkptrs) ||
+ TI_W_SAVED != offsetof(struct thread_info, w_saved))
+ thread_info_offsets_are_bolixed_pete();
+
+ /* Attach to the address space of init_task. */
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+
+ /* NOTE: Other cpus have this done as they are started
+ * up on SMP.
+ */
+}
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
new file mode 100644
index 000000000000..4638af2f55a0
--- /dev/null
+++ b/arch/sparc/kernel/traps_64.c
@@ -0,0 +1,2600 @@
+/* arch/sparc64/kernel/traps.c
+ *
+ * Copyright (C) 1995,1997,2008 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1997,1999,2000 Jakub Jelinek (jakub@redhat.com)
+ */
+
+/*
+ * I like traps on v9, :))))
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/kdebug.h>
+
+#include <asm/smp.h>
+#include <asm/delay.h>
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/oplib.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/unistd.h>
+#include <asm/uaccess.h>
+#include <asm/fpumacro.h>
+#include <asm/lsu.h>
+#include <asm/dcu.h>
+#include <asm/estate.h>
+#include <asm/chafsr.h>
+#include <asm/sfafsr.h>
+#include <asm/psrcompat.h>
+#include <asm/processor.h>
+#include <asm/timer.h>
+#include <asm/head.h>
+#include <asm/prom.h>
+#include <asm/memctrl.h>
+
+#include "entry.h"
+#include "kstack.h"
+
+/* When an irrecoverable trap occurs at tl > 0, the trap entry
+ * code logs the trap state registers at every level in the trap
+ * stack. It is found at (pt_regs + sizeof(pt_regs)) and the layout
+ * is as follows:
+ */
+struct tl1_traplog {
+ struct {
+ unsigned long tstate;
+ unsigned long tpc;
+ unsigned long tnpc;
+ unsigned long tt;
+ } trapstack[4];
+ unsigned long tl;
+};
+
+static void dump_tl1_traplog(struct tl1_traplog *p)
+{
+ int i, limit;
+
+ printk(KERN_EMERG "TRAPLOG: Error at trap level 0x%lx, "
+ "dumping track stack.\n", p->tl);
+
+ limit = (tlb_type == hypervisor) ? 2 : 4;
+ for (i = 0; i < limit; i++) {
+ printk(KERN_EMERG
+ "TRAPLOG: Trap level %d TSTATE[%016lx] TPC[%016lx] "
+ "TNPC[%016lx] TT[%lx]\n",
+ i + 1,
+ p->trapstack[i].tstate, p->trapstack[i].tpc,
+ p->trapstack[i].tnpc, p->trapstack[i].tt);
+ printk("TRAPLOG: TPC<%pS>\n", (void *) p->trapstack[i].tpc);
+ }
+}
+
+void bad_trap(struct pt_regs *regs, long lvl)
+{
+ char buffer[32];
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "bad trap", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (lvl < 0x100) {
+ sprintf(buffer, "Bad hw trap %lx at tl0\n", lvl);
+ die_if_kernel(buffer, regs);
+ }
+
+ lvl -= 0x100;
+ if (regs->tstate & TSTATE_PRIV) {
+ sprintf(buffer, "Kernel bad sw trap %lx", lvl);
+ die_if_kernel(buffer, regs);
+ }
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLTRP;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = lvl;
+ force_sig_info(SIGILL, &info, current);
+}
+
+void bad_trap_tl1(struct pt_regs *regs, long lvl)
+{
+ char buffer[32];
+
+ if (notify_die(DIE_TRAP_TL1, "bad trap tl1", regs,
+ 0, lvl, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+
+ sprintf (buffer, "Bad trap %lx at tl>0", lvl);
+ die_if_kernel (buffer, regs);
+}
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+void do_BUG(const char *file, int line)
+{
+ bust_spinlocks(1);
+ printk("kernel BUG at %s:%d!\n", file, line);
+}
+#endif
+
+static DEFINE_SPINLOCK(dimm_handler_lock);
+static dimm_printer_t dimm_handler;
+
+static int sprintf_dimm(int synd_code, unsigned long paddr, char *buf, int buflen)
+{
+ unsigned long flags;
+ int ret = -ENODEV;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (dimm_handler) {
+ ret = dimm_handler(synd_code, paddr, buf, buflen);
+ } else if (tlb_type == spitfire) {
+ if (prom_getunumber(synd_code, paddr, buf, buflen) == -1)
+ ret = -EINVAL;
+ else
+ ret = 0;
+ } else
+ ret = -ENODEV;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+
+ return ret;
+}
+
+int register_dimm_printer(dimm_printer_t func)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (!dimm_handler)
+ dimm_handler = func;
+ else
+ ret = -EEXIST;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_dimm_printer);
+
+void unregister_dimm_printer(dimm_printer_t func)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (dimm_handler == func)
+ dimm_handler = NULL;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+}
+EXPORT_SYMBOL_GPL(unregister_dimm_printer);
+
+void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ printk("spitfire_insn_access_exception: SFSR[%016lx] "
+ "SFAR[%016lx], going.\n", sfsr, sfar);
+ die_if_kernel("Iax", regs);
+ }
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = 0;
+ force_sig_info(SIGSEGV, &info, current);
+}
+
+void spitfire_insn_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
+{
+ if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ spitfire_insn_access_exception(regs, sfsr, sfar);
+}
+
+void sun4v_insn_access_exception(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
+{
+ unsigned short type = (type_ctx >> 16);
+ unsigned short ctx = (type_ctx & 0xffff);
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "instruction access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ printk("sun4v_insn_access_exception: ADDR[%016lx] "
+ "CTX[%04x] TYPE[%04x], going.\n",
+ addr, ctx, type);
+ die_if_kernel("Iax", regs);
+ }
+
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = (void __user *) addr;
+ info.si_trapno = 0;
+ force_sig_info(SIGSEGV, &info, current);
+}
+
+void sun4v_insn_access_exception_tl1(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
+{
+ if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ sun4v_insn_access_exception(regs, addr, type_ctx);
+}
+
+void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ /* Test if this comes from uaccess places. */
+ const struct exception_table_entry *entry;
+
+ entry = search_exception_tables(regs->tpc);
+ if (entry) {
+ /* Ouch, somebody is trying VM hole tricks on us... */
+#ifdef DEBUG_EXCEPTIONS
+ printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc);
+ printk("EX_TABLE: insn<%016lx> fixup<%016lx>\n",
+ regs->tpc, entry->fixup);
+#endif
+ regs->tpc = entry->fixup;
+ regs->tnpc = regs->tpc + 4;
+ return;
+ }
+ /* Shit... */
+ printk("spitfire_data_access_exception: SFSR[%016lx] "
+ "SFAR[%016lx], going.\n", sfsr, sfar);
+ die_if_kernel("Dax", regs);
+ }
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = (void __user *)sfar;
+ info.si_trapno = 0;
+ force_sig_info(SIGSEGV, &info, current);
+}
+
+void spitfire_data_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
+{
+ if (notify_die(DIE_TRAP_TL1, "data access exception tl1", regs,
+ 0, 0x30, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ spitfire_data_access_exception(regs, sfsr, sfar);
+}
+
+void sun4v_data_access_exception(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
+{
+ unsigned short type = (type_ctx >> 16);
+ unsigned short ctx = (type_ctx & 0xffff);
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "data access exception", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ printk("sun4v_data_access_exception: ADDR[%016lx] "
+ "CTX[%04x] TYPE[%04x], going.\n",
+ addr, ctx, type);
+ die_if_kernel("Dax", regs);
+ }
+
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = (void __user *) addr;
+ info.si_trapno = 0;
+ force_sig_info(SIGSEGV, &info, current);
+}
+
+void sun4v_data_access_exception_tl1(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
+{
+ if (notify_die(DIE_TRAP_TL1, "data access exception tl1", regs,
+ 0, 0x8, SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ sun4v_data_access_exception(regs, addr, type_ctx);
+}
+
+#ifdef CONFIG_PCI
+#include "pci_impl.h"
+#endif
+
+/* When access exceptions happen, we must do this. */
+static void spitfire_clean_and_reenable_l1_caches(void)
+{
+ unsigned long va;
+
+ if (tlb_type != spitfire)
+ BUG();
+
+ /* Clean 'em. */
+ for (va = 0; va < (PAGE_SIZE << 1); va += 32) {
+ spitfire_put_icache_tag(va, 0x0);
+ spitfire_put_dcache_tag(va, 0x0);
+ }
+
+ /* Re-enable in LSU. */
+ __asm__ __volatile__("flush %%g6\n\t"
+ "membar #Sync\n\t"
+ "stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
+ LSU_CONTROL_IM | LSU_CONTROL_DM),
+ "i" (ASI_LSU_CONTROL)
+ : "memory");
+}
+
+static void spitfire_enable_estate_errors(void)
+{
+ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (ESTATE_ERR_ALL),
+ "i" (ASI_ESTATE_ERROR_EN));
+}
+
+static char ecc_syndrome_table[] = {
+ 0x4c, 0x40, 0x41, 0x48, 0x42, 0x48, 0x48, 0x49,
+ 0x43, 0x48, 0x48, 0x49, 0x48, 0x49, 0x49, 0x4a,
+ 0x44, 0x48, 0x48, 0x20, 0x48, 0x39, 0x4b, 0x48,
+ 0x48, 0x25, 0x31, 0x48, 0x28, 0x48, 0x48, 0x2c,
+ 0x45, 0x48, 0x48, 0x21, 0x48, 0x3d, 0x04, 0x48,
+ 0x48, 0x4b, 0x35, 0x48, 0x2d, 0x48, 0x48, 0x29,
+ 0x48, 0x00, 0x01, 0x48, 0x0a, 0x48, 0x48, 0x4b,
+ 0x0f, 0x48, 0x48, 0x4b, 0x48, 0x49, 0x49, 0x48,
+ 0x46, 0x48, 0x48, 0x2a, 0x48, 0x3b, 0x27, 0x48,
+ 0x48, 0x4b, 0x33, 0x48, 0x22, 0x48, 0x48, 0x2e,
+ 0x48, 0x19, 0x1d, 0x48, 0x1b, 0x4a, 0x48, 0x4b,
+ 0x1f, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48,
+ 0x48, 0x4b, 0x24, 0x48, 0x07, 0x48, 0x48, 0x36,
+ 0x4b, 0x48, 0x48, 0x3e, 0x48, 0x30, 0x38, 0x48,
+ 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x16, 0x48,
+ 0x48, 0x12, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b,
+ 0x47, 0x48, 0x48, 0x2f, 0x48, 0x3f, 0x4b, 0x48,
+ 0x48, 0x06, 0x37, 0x48, 0x23, 0x48, 0x48, 0x2b,
+ 0x48, 0x05, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x32,
+ 0x26, 0x48, 0x48, 0x3a, 0x48, 0x34, 0x3c, 0x48,
+ 0x48, 0x11, 0x15, 0x48, 0x13, 0x4a, 0x48, 0x4b,
+ 0x17, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48,
+ 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x1e, 0x48,
+ 0x48, 0x1a, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b,
+ 0x48, 0x08, 0x0d, 0x48, 0x02, 0x48, 0x48, 0x49,
+ 0x03, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x4b, 0x48,
+ 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x10, 0x48,
+ 0x48, 0x14, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b,
+ 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x18, 0x48,
+ 0x48, 0x1c, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b,
+ 0x4a, 0x0c, 0x09, 0x48, 0x0e, 0x48, 0x48, 0x4b,
+ 0x0b, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x4b, 0x4a
+};
+
+static char *syndrome_unknown = "<Unknown>";
+
+static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long bit)
+{
+ unsigned short scode;
+ char memmod_str[64], *p;
+
+ if (udbl & bit) {
+ scode = ecc_syndrome_table[udbl & 0xff];
+ if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
+ p = syndrome_unknown;
+ else
+ p = memmod_str;
+ printk(KERN_WARNING "CPU[%d]: UDBL Syndrome[%x] "
+ "Memory Module \"%s\"\n",
+ smp_processor_id(), scode, p);
+ }
+
+ if (udbh & bit) {
+ scode = ecc_syndrome_table[udbh & 0xff];
+ if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
+ p = syndrome_unknown;
+ else
+ p = memmod_str;
+ printk(KERN_WARNING "CPU[%d]: UDBH Syndrome[%x] "
+ "Memory Module \"%s\"\n",
+ smp_processor_id(), scode, p);
+ }
+
+}
+
+static void spitfire_cee_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, int tl1, struct pt_regs *regs)
+{
+
+ printk(KERN_WARNING "CPU[%d]: Correctable ECC Error "
+ "AFSR[%lx] AFAR[%016lx] UDBL[%lx] UDBH[%lx] TL>1[%d]\n",
+ smp_processor_id(), afsr, afar, udbl, udbh, tl1);
+
+ spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_CE);
+
+ /* We always log it, even if someone is listening for this
+ * trap.
+ */
+ notify_die(DIE_TRAP, "Correctable ECC Error", regs,
+ 0, TRAP_TYPE_CEE, SIGTRAP);
+
+ /* The Correctable ECC Error trap does not disable I/D caches. So
+ * we only have to restore the ESTATE Error Enable register.
+ */
+ spitfire_enable_estate_errors();
+}
+
+static void spitfire_ue_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long tt, int tl1, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ printk(KERN_WARNING "CPU[%d]: Uncorrectable Error AFSR[%lx] "
+ "AFAR[%lx] UDBL[%lx] UDBH[%ld] TT[%lx] TL>1[%d]\n",
+ smp_processor_id(), afsr, afar, udbl, udbh, tt, tl1);
+
+ /* XXX add more human friendly logging of the error status
+ * XXX as is implemented for cheetah
+ */
+
+ spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_UE);
+
+ /* We always log it, even if someone is listening for this
+ * trap.
+ */
+ notify_die(DIE_TRAP, "Uncorrectable Error", regs,
+ 0, tt, SIGTRAP);
+
+ if (regs->tstate & TSTATE_PRIV) {
+ if (tl1)
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("UE", regs);
+ }
+
+ /* XXX need more intelligent processing here, such as is implemented
+ * XXX for cheetah errors, in fact if the E-cache still holds the
+ * XXX line with bad parity this will loop
+ */
+
+ spitfire_clean_and_reenable_l1_caches();
+ spitfire_enable_estate_errors();
+
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_OBJERR;
+ info.si_addr = (void *)0;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
+}
+
+void spitfire_access_error(struct pt_regs *regs, unsigned long status_encoded, unsigned long afar)
+{
+ unsigned long afsr, tt, udbh, udbl;
+ int tl1;
+
+ afsr = (status_encoded & SFSTAT_AFSR_MASK) >> SFSTAT_AFSR_SHIFT;
+ tt = (status_encoded & SFSTAT_TRAP_TYPE) >> SFSTAT_TRAP_TYPE_SHIFT;
+ tl1 = (status_encoded & SFSTAT_TL_GT_ONE) ? 1 : 0;
+ udbl = (status_encoded & SFSTAT_UDBL_MASK) >> SFSTAT_UDBL_SHIFT;
+ udbh = (status_encoded & SFSTAT_UDBH_MASK) >> SFSTAT_UDBH_SHIFT;
+
+#ifdef CONFIG_PCI
+ if (tt == TRAP_TYPE_DAE &&
+ pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
+ spitfire_clean_and_reenable_l1_caches();
+ spitfire_enable_estate_errors();
+
+ pci_poke_faulted = 1;
+ regs->tnpc = regs->tpc + 4;
+ return;
+ }
+#endif
+
+ if (afsr & SFAFSR_UE)
+ spitfire_ue_log(afsr, afar, udbh, udbl, tt, tl1, regs);
+
+ if (tt == TRAP_TYPE_CEE) {
+ /* Handle the case where we took a CEE trap, but ACK'd
+ * only the UE state in the UDB error registers.
+ */
+ if (afsr & SFAFSR_UE) {
+ if (udbh & UDBE_CE) {
+ __asm__ __volatile__(
+ "stxa %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (udbh & UDBE_CE),
+ "r" (0x0), "i" (ASI_UDB_ERROR_W));
+ }
+ if (udbl & UDBE_CE) {
+ __asm__ __volatile__(
+ "stxa %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (udbl & UDBE_CE),
+ "r" (0x18), "i" (ASI_UDB_ERROR_W));
+ }
+ }
+
+ spitfire_cee_log(afsr, afar, udbh, udbl, tl1, regs);
+ }
+}
+
+int cheetah_pcache_forced_on;
+
+void cheetah_enable_pcache(void)
+{
+ unsigned long dcr;
+
+ printk("CHEETAH: Enabling P-Cache on cpu %d.\n",
+ smp_processor_id());
+
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (dcr)
+ : "i" (ASI_DCU_CONTROL_REG));
+ dcr |= (DCU_PE | DCU_HPE | DCU_SPE | DCU_SL);
+ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (dcr), "i" (ASI_DCU_CONTROL_REG));
+}
+
+/* Cheetah error trap handling. */
+static unsigned long ecache_flush_physbase;
+static unsigned long ecache_flush_linesize;
+static unsigned long ecache_flush_size;
+
+/* This table is ordered in priority of errors and matches the
+ * AFAR overwrite policy as well.
+ */
+
+struct afsr_error_table {
+ unsigned long mask;
+ const char *name;
+};
+
+static const char CHAFSR_PERR_msg[] =
+ "System interface protocol error";
+static const char CHAFSR_IERR_msg[] =
+ "Internal processor error";
+static const char CHAFSR_ISAP_msg[] =
+ "System request parity error on incoming addresss";
+static const char CHAFSR_UCU_msg[] =
+ "Uncorrectable E-cache ECC error for ifetch/data";
+static const char CHAFSR_UCC_msg[] =
+ "SW Correctable E-cache ECC error for ifetch/data";
+static const char CHAFSR_UE_msg[] =
+ "Uncorrectable system bus data ECC error for read";
+static const char CHAFSR_EDU_msg[] =
+ "Uncorrectable E-cache ECC error for stmerge/blkld";
+static const char CHAFSR_EMU_msg[] =
+ "Uncorrectable system bus MTAG error";
+static const char CHAFSR_WDU_msg[] =
+ "Uncorrectable E-cache ECC error for writeback";
+static const char CHAFSR_CPU_msg[] =
+ "Uncorrectable ECC error for copyout";
+static const char CHAFSR_CE_msg[] =
+ "HW corrected system bus data ECC error for read";
+static const char CHAFSR_EDC_msg[] =
+ "HW corrected E-cache ECC error for stmerge/blkld";
+static const char CHAFSR_EMC_msg[] =
+ "HW corrected system bus MTAG ECC error";
+static const char CHAFSR_WDC_msg[] =
+ "HW corrected E-cache ECC error for writeback";
+static const char CHAFSR_CPC_msg[] =
+ "HW corrected ECC error for copyout";
+static const char CHAFSR_TO_msg[] =
+ "Unmapped error from system bus";
+static const char CHAFSR_BERR_msg[] =
+ "Bus error response from system bus";
+static const char CHAFSR_IVC_msg[] =
+ "HW corrected system bus data ECC error for ivec read";
+static const char CHAFSR_IVU_msg[] =
+ "Uncorrectable system bus data ECC error for ivec read";
+static struct afsr_error_table __cheetah_error_table[] = {
+ { CHAFSR_PERR, CHAFSR_PERR_msg },
+ { CHAFSR_IERR, CHAFSR_IERR_msg },
+ { CHAFSR_ISAP, CHAFSR_ISAP_msg },
+ { CHAFSR_UCU, CHAFSR_UCU_msg },
+ { CHAFSR_UCC, CHAFSR_UCC_msg },
+ { CHAFSR_UE, CHAFSR_UE_msg },
+ { CHAFSR_EDU, CHAFSR_EDU_msg },
+ { CHAFSR_EMU, CHAFSR_EMU_msg },
+ { CHAFSR_WDU, CHAFSR_WDU_msg },
+ { CHAFSR_CPU, CHAFSR_CPU_msg },
+ { CHAFSR_CE, CHAFSR_CE_msg },
+ { CHAFSR_EDC, CHAFSR_EDC_msg },
+ { CHAFSR_EMC, CHAFSR_EMC_msg },
+ { CHAFSR_WDC, CHAFSR_WDC_msg },
+ { CHAFSR_CPC, CHAFSR_CPC_msg },
+ { CHAFSR_TO, CHAFSR_TO_msg },
+ { CHAFSR_BERR, CHAFSR_BERR_msg },
+ /* These two do not update the AFAR. */
+ { CHAFSR_IVC, CHAFSR_IVC_msg },
+ { CHAFSR_IVU, CHAFSR_IVU_msg },
+ { 0, NULL },
+};
+static const char CHPAFSR_DTO_msg[] =
+ "System bus unmapped error for prefetch/storequeue-read";
+static const char CHPAFSR_DBERR_msg[] =
+ "System bus error for prefetch/storequeue-read";
+static const char CHPAFSR_THCE_msg[] =
+ "Hardware corrected E-cache Tag ECC error";
+static const char CHPAFSR_TSCE_msg[] =
+ "SW handled correctable E-cache Tag ECC error";
+static const char CHPAFSR_TUE_msg[] =
+ "Uncorrectable E-cache Tag ECC error";
+static const char CHPAFSR_DUE_msg[] =
+ "System bus uncorrectable data ECC error due to prefetch/store-fill";
+static struct afsr_error_table __cheetah_plus_error_table[] = {
+ { CHAFSR_PERR, CHAFSR_PERR_msg },
+ { CHAFSR_IERR, CHAFSR_IERR_msg },
+ { CHAFSR_ISAP, CHAFSR_ISAP_msg },
+ { CHAFSR_UCU, CHAFSR_UCU_msg },
+ { CHAFSR_UCC, CHAFSR_UCC_msg },
+ { CHAFSR_UE, CHAFSR_UE_msg },
+ { CHAFSR_EDU, CHAFSR_EDU_msg },
+ { CHAFSR_EMU, CHAFSR_EMU_msg },
+ { CHAFSR_WDU, CHAFSR_WDU_msg },
+ { CHAFSR_CPU, CHAFSR_CPU_msg },
+ { CHAFSR_CE, CHAFSR_CE_msg },
+ { CHAFSR_EDC, CHAFSR_EDC_msg },
+ { CHAFSR_EMC, CHAFSR_EMC_msg },
+ { CHAFSR_WDC, CHAFSR_WDC_msg },
+ { CHAFSR_CPC, CHAFSR_CPC_msg },
+ { CHAFSR_TO, CHAFSR_TO_msg },
+ { CHAFSR_BERR, CHAFSR_BERR_msg },
+ { CHPAFSR_DTO, CHPAFSR_DTO_msg },
+ { CHPAFSR_DBERR, CHPAFSR_DBERR_msg },
+ { CHPAFSR_THCE, CHPAFSR_THCE_msg },
+ { CHPAFSR_TSCE, CHPAFSR_TSCE_msg },
+ { CHPAFSR_TUE, CHPAFSR_TUE_msg },
+ { CHPAFSR_DUE, CHPAFSR_DUE_msg },
+ /* These two do not update the AFAR. */
+ { CHAFSR_IVC, CHAFSR_IVC_msg },
+ { CHAFSR_IVU, CHAFSR_IVU_msg },
+ { 0, NULL },
+};
+static const char JPAFSR_JETO_msg[] =
+ "System interface protocol error, hw timeout caused";
+static const char JPAFSR_SCE_msg[] =
+ "Parity error on system snoop results";
+static const char JPAFSR_JEIC_msg[] =
+ "System interface protocol error, illegal command detected";
+static const char JPAFSR_JEIT_msg[] =
+ "System interface protocol error, illegal ADTYPE detected";
+static const char JPAFSR_OM_msg[] =
+ "Out of range memory error has occurred";
+static const char JPAFSR_ETP_msg[] =
+ "Parity error on L2 cache tag SRAM";
+static const char JPAFSR_UMS_msg[] =
+ "Error due to unsupported store";
+static const char JPAFSR_RUE_msg[] =
+ "Uncorrectable ECC error from remote cache/memory";
+static const char JPAFSR_RCE_msg[] =
+ "Correctable ECC error from remote cache/memory";
+static const char JPAFSR_BP_msg[] =
+ "JBUS parity error on returned read data";
+static const char JPAFSR_WBP_msg[] =
+ "JBUS parity error on data for writeback or block store";
+static const char JPAFSR_FRC_msg[] =
+ "Foreign read to DRAM incurring correctable ECC error";
+static const char JPAFSR_FRU_msg[] =
+ "Foreign read to DRAM incurring uncorrectable ECC error";
+static struct afsr_error_table __jalapeno_error_table[] = {
+ { JPAFSR_JETO, JPAFSR_JETO_msg },
+ { JPAFSR_SCE, JPAFSR_SCE_msg },
+ { JPAFSR_JEIC, JPAFSR_JEIC_msg },
+ { JPAFSR_JEIT, JPAFSR_JEIT_msg },
+ { CHAFSR_PERR, CHAFSR_PERR_msg },
+ { CHAFSR_IERR, CHAFSR_IERR_msg },
+ { CHAFSR_ISAP, CHAFSR_ISAP_msg },
+ { CHAFSR_UCU, CHAFSR_UCU_msg },
+ { CHAFSR_UCC, CHAFSR_UCC_msg },
+ { CHAFSR_UE, CHAFSR_UE_msg },
+ { CHAFSR_EDU, CHAFSR_EDU_msg },
+ { JPAFSR_OM, JPAFSR_OM_msg },
+ { CHAFSR_WDU, CHAFSR_WDU_msg },
+ { CHAFSR_CPU, CHAFSR_CPU_msg },
+ { CHAFSR_CE, CHAFSR_CE_msg },
+ { CHAFSR_EDC, CHAFSR_EDC_msg },
+ { JPAFSR_ETP, JPAFSR_ETP_msg },
+ { CHAFSR_WDC, CHAFSR_WDC_msg },
+ { CHAFSR_CPC, CHAFSR_CPC_msg },
+ { CHAFSR_TO, CHAFSR_TO_msg },
+ { CHAFSR_BERR, CHAFSR_BERR_msg },
+ { JPAFSR_UMS, JPAFSR_UMS_msg },
+ { JPAFSR_RUE, JPAFSR_RUE_msg },
+ { JPAFSR_RCE, JPAFSR_RCE_msg },
+ { JPAFSR_BP, JPAFSR_BP_msg },
+ { JPAFSR_WBP, JPAFSR_WBP_msg },
+ { JPAFSR_FRC, JPAFSR_FRC_msg },
+ { JPAFSR_FRU, JPAFSR_FRU_msg },
+ /* These two do not update the AFAR. */
+ { CHAFSR_IVU, CHAFSR_IVU_msg },
+ { 0, NULL },
+};
+static struct afsr_error_table *cheetah_error_table;
+static unsigned long cheetah_afsr_errors;
+
+struct cheetah_err_info *cheetah_error_log;
+
+static inline struct cheetah_err_info *cheetah_get_error_log(unsigned long afsr)
+{
+ struct cheetah_err_info *p;
+ int cpu = smp_processor_id();
+
+ if (!cheetah_error_log)
+ return NULL;
+
+ p = cheetah_error_log + (cpu * 2);
+ if ((afsr & CHAFSR_TL1) != 0UL)
+ p++;
+
+ return p;
+}
+
+extern unsigned int tl0_icpe[], tl1_icpe[];
+extern unsigned int tl0_dcpe[], tl1_dcpe[];
+extern unsigned int tl0_fecc[], tl1_fecc[];
+extern unsigned int tl0_cee[], tl1_cee[];
+extern unsigned int tl0_iae[], tl1_iae[];
+extern unsigned int tl0_dae[], tl1_dae[];
+extern unsigned int cheetah_plus_icpe_trap_vector[], cheetah_plus_icpe_trap_vector_tl1[];
+extern unsigned int cheetah_plus_dcpe_trap_vector[], cheetah_plus_dcpe_trap_vector_tl1[];
+extern unsigned int cheetah_fecc_trap_vector[], cheetah_fecc_trap_vector_tl1[];
+extern unsigned int cheetah_cee_trap_vector[], cheetah_cee_trap_vector_tl1[];
+extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector_tl1[];
+
+void __init cheetah_ecache_flush_init(void)
+{
+ unsigned long largest_size, smallest_linesize, order, ver;
+ int i, sz;
+
+ /* Scan all cpu device tree nodes, note two values:
+ * 1) largest E-cache size
+ * 2) smallest E-cache line size
+ */
+ largest_size = 0UL;
+ smallest_linesize = ~0UL;
+
+ for (i = 0; i < NR_CPUS; i++) {
+ unsigned long val;
+
+ val = cpu_data(i).ecache_size;
+ if (!val)
+ continue;
+
+ if (val > largest_size)
+ largest_size = val;
+
+ val = cpu_data(i).ecache_line_size;
+ if (val < smallest_linesize)
+ smallest_linesize = val;
+
+ }
+
+ if (largest_size == 0UL || smallest_linesize == ~0UL) {
+ prom_printf("cheetah_ecache_flush_init: Cannot probe cpu E-cache "
+ "parameters.\n");
+ prom_halt();
+ }
+
+ ecache_flush_size = (2 * largest_size);
+ ecache_flush_linesize = smallest_linesize;
+
+ ecache_flush_physbase = find_ecache_flush_span(ecache_flush_size);
+
+ if (ecache_flush_physbase == ~0UL) {
+ prom_printf("cheetah_ecache_flush_init: Cannot find %d byte "
+ "contiguous physical memory.\n",
+ ecache_flush_size);
+ prom_halt();
+ }
+
+ /* Now allocate error trap reporting scoreboard. */
+ sz = NR_CPUS * (2 * sizeof(struct cheetah_err_info));
+ for (order = 0; order < MAX_ORDER; order++) {
+ if ((PAGE_SIZE << order) >= sz)
+ break;
+ }
+ cheetah_error_log = (struct cheetah_err_info *)
+ __get_free_pages(GFP_KERNEL, order);
+ if (!cheetah_error_log) {
+ prom_printf("cheetah_ecache_flush_init: Failed to allocate "
+ "error logging scoreboard (%d bytes).\n", sz);
+ prom_halt();
+ }
+ memset(cheetah_error_log, 0, PAGE_SIZE << order);
+
+ /* Mark all AFSRs as invalid so that the trap handler will
+ * log new new information there.
+ */
+ for (i = 0; i < 2 * NR_CPUS; i++)
+ cheetah_error_log[i].afsr = CHAFSR_INVALID;
+
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32) == __JALAPENO_ID ||
+ (ver >> 32) == __SERRANO_ID) {
+ cheetah_error_table = &__jalapeno_error_table[0];
+ cheetah_afsr_errors = JPAFSR_ERRORS;
+ } else if ((ver >> 32) == 0x003e0015) {
+ cheetah_error_table = &__cheetah_plus_error_table[0];
+ cheetah_afsr_errors = CHPAFSR_ERRORS;
+ } else {
+ cheetah_error_table = &__cheetah_error_table[0];
+ cheetah_afsr_errors = CHAFSR_ERRORS;
+ }
+
+ /* Now patch trap tables. */
+ memcpy(tl0_fecc, cheetah_fecc_trap_vector, (8 * 4));
+ memcpy(tl1_fecc, cheetah_fecc_trap_vector_tl1, (8 * 4));
+ memcpy(tl0_cee, cheetah_cee_trap_vector, (8 * 4));
+ memcpy(tl1_cee, cheetah_cee_trap_vector_tl1, (8 * 4));
+ memcpy(tl0_iae, cheetah_deferred_trap_vector, (8 * 4));
+ memcpy(tl1_iae, cheetah_deferred_trap_vector_tl1, (8 * 4));
+ memcpy(tl0_dae, cheetah_deferred_trap_vector, (8 * 4));
+ memcpy(tl1_dae, cheetah_deferred_trap_vector_tl1, (8 * 4));
+ if (tlb_type == cheetah_plus) {
+ memcpy(tl0_dcpe, cheetah_plus_dcpe_trap_vector, (8 * 4));
+ memcpy(tl1_dcpe, cheetah_plus_dcpe_trap_vector_tl1, (8 * 4));
+ memcpy(tl0_icpe, cheetah_plus_icpe_trap_vector, (8 * 4));
+ memcpy(tl1_icpe, cheetah_plus_icpe_trap_vector_tl1, (8 * 4));
+ }
+ flushi(PAGE_OFFSET);
+}
+
+static void cheetah_flush_ecache(void)
+{
+ unsigned long flush_base = ecache_flush_physbase;
+ unsigned long flush_linesize = ecache_flush_linesize;
+ unsigned long flush_size = ecache_flush_size;
+
+ __asm__ __volatile__("1: subcc %0, %4, %0\n\t"
+ " bne,pt %%xcc, 1b\n\t"
+ " ldxa [%2 + %0] %3, %%g0\n\t"
+ : "=&r" (flush_size)
+ : "0" (flush_size), "r" (flush_base),
+ "i" (ASI_PHYS_USE_EC), "r" (flush_linesize));
+}
+
+static void cheetah_flush_ecache_line(unsigned long physaddr)
+{
+ unsigned long alias;
+
+ physaddr &= ~(8UL - 1UL);
+ physaddr = (ecache_flush_physbase +
+ (physaddr & ((ecache_flush_size>>1UL) - 1UL)));
+ alias = physaddr + (ecache_flush_size >> 1UL);
+ __asm__ __volatile__("ldxa [%0] %2, %%g0\n\t"
+ "ldxa [%1] %2, %%g0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (physaddr), "r" (alias),
+ "i" (ASI_PHYS_USE_EC));
+}
+
+/* Unfortunately, the diagnostic access to the I-cache tags we need to
+ * use to clear the thing interferes with I-cache coherency transactions.
+ *
+ * So we must only flush the I-cache when it is disabled.
+ */
+static void __cheetah_flush_icache(void)
+{
+ unsigned int icache_size, icache_line_size;
+ unsigned long addr;
+
+ icache_size = local_cpu_data().icache_size;
+ icache_line_size = local_cpu_data().icache_line_size;
+
+ /* Clear the valid bits in all the tags. */
+ for (addr = 0; addr < icache_size; addr += icache_line_size) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (addr | (2 << 3)),
+ "i" (ASI_IC_TAG));
+ }
+}
+
+static void cheetah_flush_icache(void)
+{
+ unsigned long dcu_save;
+
+ /* Save current DCU, disable I-cache. */
+ __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+ "or %0, %2, %%g1\n\t"
+ "stxa %%g1, [%%g0] %1\n\t"
+ "membar #Sync"
+ : "=r" (dcu_save)
+ : "i" (ASI_DCU_CONTROL_REG), "i" (DCU_IC)
+ : "g1");
+
+ __cheetah_flush_icache();
+
+ /* Restore DCU register */
+ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (dcu_save), "i" (ASI_DCU_CONTROL_REG));
+}
+
+static void cheetah_flush_dcache(void)
+{
+ unsigned int dcache_size, dcache_line_size;
+ unsigned long addr;
+
+ dcache_size = local_cpu_data().dcache_size;
+ dcache_line_size = local_cpu_data().dcache_line_size;
+
+ for (addr = 0; addr < dcache_size; addr += dcache_line_size) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (addr), "i" (ASI_DCACHE_TAG));
+ }
+}
+
+/* In order to make the even parity correct we must do two things.
+ * First, we clear DC_data_parity and set DC_utag to an appropriate value.
+ * Next, we clear out all 32-bytes of data for that line. Data of
+ * all-zero + tag parity value of zero == correct parity.
+ */
+static void cheetah_plus_zap_dcache_parity(void)
+{
+ unsigned int dcache_size, dcache_line_size;
+ unsigned long addr;
+
+ dcache_size = local_cpu_data().dcache_size;
+ dcache_line_size = local_cpu_data().dcache_line_size;
+
+ for (addr = 0; addr < dcache_size; addr += dcache_line_size) {
+ unsigned long tag = (addr >> 14);
+ unsigned long line;
+
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stxa %0, [%1] %2\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (tag), "r" (addr),
+ "i" (ASI_DCACHE_UTAG));
+ for (line = addr; line < addr + dcache_line_size; line += 8)
+ __asm__ __volatile__("membar #Sync\n\t"
+ "stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (line),
+ "i" (ASI_DCACHE_DATA));
+ }
+}
+
+/* Conversion tables used to frob Cheetah AFSR syndrome values into
+ * something palatable to the memory controller driver get_unumber
+ * routine.
+ */
+#define MT0 137
+#define MT1 138
+#define MT2 139
+#define NONE 254
+#define MTC0 140
+#define MTC1 141
+#define MTC2 142
+#define MTC3 143
+#define C0 128
+#define C1 129
+#define C2 130
+#define C3 131
+#define C4 132
+#define C5 133
+#define C6 134
+#define C7 135
+#define C8 136
+#define M2 144
+#define M3 145
+#define M4 146
+#define M 147
+static unsigned char cheetah_ecc_syntab[] = {
+/*00*/NONE, C0, C1, M2, C2, M2, M3, 47, C3, M2, M2, 53, M2, 41, 29, M,
+/*01*/C4, M, M, 50, M2, 38, 25, M2, M2, 33, 24, M2, 11, M, M2, 16,
+/*02*/C5, M, M, 46, M2, 37, 19, M2, M, 31, 32, M, 7, M2, M2, 10,
+/*03*/M2, 40, 13, M2, 59, M, M2, 66, M, M2, M2, 0, M2, 67, 71, M,
+/*04*/C6, M, M, 43, M, 36, 18, M, M2, 49, 15, M, 63, M2, M2, 6,
+/*05*/M2, 44, 28, M2, M, M2, M2, 52, 68, M2, M2, 62, M2, M3, M3, M4,
+/*06*/M2, 26, 106, M2, 64, M, M2, 2, 120, M, M2, M3, M, M3, M3, M4,
+/*07*/116, M2, M2, M3, M2, M3, M, M4, M2, 58, 54, M2, M, M4, M4, M3,
+/*08*/C7, M2, M, 42, M, 35, 17, M2, M, 45, 14, M2, 21, M2, M2, 5,
+/*09*/M, 27, M, M, 99, M, M, 3, 114, M2, M2, 20, M2, M3, M3, M,
+/*0a*/M2, 23, 113, M2, 112, M2, M, 51, 95, M, M2, M3, M2, M3, M3, M2,
+/*0b*/103, M, M2, M3, M2, M3, M3, M4, M2, 48, M, M, 73, M2, M, M3,
+/*0c*/M2, 22, 110, M2, 109, M2, M, 9, 108, M2, M, M3, M2, M3, M3, M,
+/*0d*/102, M2, M, M, M2, M3, M3, M, M2, M3, M3, M2, M, M4, M, M3,
+/*0e*/98, M, M2, M3, M2, M, M3, M4, M2, M3, M3, M4, M3, M, M, M,
+/*0f*/M2, M3, M3, M, M3, M, M, M, 56, M4, M, M3, M4, M, M, M,
+/*10*/C8, M, M2, 39, M, 34, 105, M2, M, 30, 104, M, 101, M, M, 4,
+/*11*/M, M, 100, M, 83, M, M2, 12, 87, M, M, 57, M2, M, M3, M,
+/*12*/M2, 97, 82, M2, 78, M2, M2, 1, 96, M, M, M, M, M, M3, M2,
+/*13*/94, M, M2, M3, M2, M, M3, M, M2, M, 79, M, 69, M, M4, M,
+/*14*/M2, 93, 92, M, 91, M, M2, 8, 90, M2, M2, M, M, M, M, M4,
+/*15*/89, M, M, M3, M2, M3, M3, M, M, M, M3, M2, M3, M2, M, M3,
+/*16*/86, M, M2, M3, M2, M, M3, M, M2, M, M3, M, M3, M, M, M3,
+/*17*/M, M, M3, M2, M3, M2, M4, M, 60, M, M2, M3, M4, M, M, M2,
+/*18*/M2, 88, 85, M2, 84, M, M2, 55, 81, M2, M2, M3, M2, M3, M3, M4,
+/*19*/77, M, M, M, M2, M3, M, M, M2, M3, M3, M4, M3, M2, M, M,
+/*1a*/74, M, M2, M3, M, M, M3, M, M, M, M3, M, M3, M, M4, M3,
+/*1b*/M2, 70, 107, M4, 65, M2, M2, M, 127, M, M, M, M2, M3, M3, M,
+/*1c*/80, M2, M2, 72, M, 119, 118, M, M2, 126, 76, M, 125, M, M4, M3,
+/*1d*/M2, 115, 124, M, 75, M, M, M3, 61, M, M4, M, M4, M, M, M,
+/*1e*/M, 123, 122, M4, 121, M4, M, M3, 117, M2, M2, M3, M4, M3, M, M,
+/*1f*/111, M, M, M, M4, M3, M3, M, M, M, M3, M, M3, M2, M, M
+};
+static unsigned char cheetah_mtag_syntab[] = {
+ NONE, MTC0,
+ MTC1, NONE,
+ MTC2, NONE,
+ NONE, MT0,
+ MTC3, NONE,
+ NONE, MT1,
+ NONE, MT2,
+ NONE, NONE
+};
+
+/* Return the highest priority error conditon mentioned. */
+static inline unsigned long cheetah_get_hipri(unsigned long afsr)
+{
+ unsigned long tmp = 0;
+ int i;
+
+ for (i = 0; cheetah_error_table[i].mask; i++) {
+ if ((tmp = (afsr & cheetah_error_table[i].mask)) != 0UL)
+ return tmp;
+ }
+ return tmp;
+}
+
+static const char *cheetah_get_string(unsigned long bit)
+{
+ int i;
+
+ for (i = 0; cheetah_error_table[i].mask; i++) {
+ if ((bit & cheetah_error_table[i].mask) != 0UL)
+ return cheetah_error_table[i].name;
+ }
+ return "???";
+}
+
+static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info,
+ unsigned long afsr, unsigned long afar, int recoverable)
+{
+ unsigned long hipri;
+ char unum[256];
+
+ printk("%s" "ERROR(%d): Cheetah error trap taken afsr[%016lx] afar[%016lx] TL1(%d)\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ afsr, afar,
+ (afsr & CHAFSR_TL1) ? 1 : 0);
+ printk("%s" "ERROR(%d): TPC[%lx] TNPC[%lx] O7[%lx] TSTATE[%lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ regs->tpc, regs->tnpc, regs->u_regs[UREG_I7], regs->tstate);
+ printk("%s" "ERROR(%d): ",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id());
+ printk("TPC<%pS>\n", (void *) regs->tpc);
+ printk("%s" "ERROR(%d): M_SYND(%lx), E_SYND(%lx)%s%s\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT,
+ (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT,
+ (afsr & CHAFSR_ME) ? ", Multiple Errors" : "",
+ (afsr & CHAFSR_PRIV) ? ", Privileged" : "");
+ hipri = cheetah_get_hipri(afsr);
+ printk("%s" "ERROR(%d): Highest priority error (%016lx) \"%s\"\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ hipri, cheetah_get_string(hipri));
+
+ /* Try to get unumber if relevant. */
+#define ESYND_ERRORS (CHAFSR_IVC | CHAFSR_IVU | \
+ CHAFSR_CPC | CHAFSR_CPU | \
+ CHAFSR_UE | CHAFSR_CE | \
+ CHAFSR_EDC | CHAFSR_EDU | \
+ CHAFSR_UCC | CHAFSR_UCU | \
+ CHAFSR_WDU | CHAFSR_WDC)
+#define MSYND_ERRORS (CHAFSR_EMC | CHAFSR_EMU)
+ if (afsr & ESYND_ERRORS) {
+ int syndrome;
+ int ret;
+
+ syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT;
+ syndrome = cheetah_ecc_syntab[syndrome];
+ ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
+ if (ret != -1)
+ printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT),
+ smp_processor_id(), unum);
+ } else if (afsr & MSYND_ERRORS) {
+ int syndrome;
+ int ret;
+
+ syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT;
+ syndrome = cheetah_mtag_syntab[syndrome];
+ ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
+ if (ret != -1)
+ printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT),
+ smp_processor_id(), unum);
+ }
+
+ /* Now dump the cache snapshots. */
+ printk("%s" "ERROR(%d): D-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ (int) info->dcache_index,
+ info->dcache_tag,
+ info->dcache_utag,
+ info->dcache_stag);
+ printk("%s" "ERROR(%d): D-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ info->dcache_data[0],
+ info->dcache_data[1],
+ info->dcache_data[2],
+ info->dcache_data[3]);
+ printk("%s" "ERROR(%d): I-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx] "
+ "u[%016lx] l[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ (int) info->icache_index,
+ info->icache_tag,
+ info->icache_utag,
+ info->icache_stag,
+ info->icache_upper,
+ info->icache_lower);
+ printk("%s" "ERROR(%d): I-cache INSN0[%016lx] INSN1[%016lx] INSN2[%016lx] INSN3[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ info->icache_data[0],
+ info->icache_data[1],
+ info->icache_data[2],
+ info->icache_data[3]);
+ printk("%s" "ERROR(%d): I-cache INSN4[%016lx] INSN5[%016lx] INSN6[%016lx] INSN7[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ info->icache_data[4],
+ info->icache_data[5],
+ info->icache_data[6],
+ info->icache_data[7]);
+ printk("%s" "ERROR(%d): E-cache idx[%x] tag[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ (int) info->ecache_index, info->ecache_tag);
+ printk("%s" "ERROR(%d): E-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
+ info->ecache_data[0],
+ info->ecache_data[1],
+ info->ecache_data[2],
+ info->ecache_data[3]);
+
+ afsr = (afsr & ~hipri) & cheetah_afsr_errors;
+ while (afsr != 0UL) {
+ unsigned long bit = cheetah_get_hipri(afsr);
+
+ printk("%s" "ERROR: Multiple-error (%016lx) \"%s\"\n",
+ (recoverable ? KERN_WARNING : KERN_CRIT),
+ bit, cheetah_get_string(bit));
+
+ afsr &= ~bit;
+ }
+
+ if (!recoverable)
+ printk(KERN_CRIT "ERROR: This condition is not recoverable.\n");
+}
+
+static int cheetah_recheck_errors(struct cheetah_err_info *logp)
+{
+ unsigned long afsr, afar;
+ int ret = 0;
+
+ __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+ : "=r" (afsr)
+ : "i" (ASI_AFSR));
+ if ((afsr & cheetah_afsr_errors) != 0) {
+ if (logp != NULL) {
+ __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
+ : "=r" (afar)
+ : "i" (ASI_AFAR));
+ logp->afsr = afsr;
+ logp->afar = afar;
+ }
+ ret = 1;
+ }
+ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+ "membar #Sync\n\t"
+ : : "r" (afsr), "i" (ASI_AFSR));
+
+ return ret;
+}
+
+void cheetah_fecc_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+ struct cheetah_err_info local_snapshot, *p;
+ int recoverable;
+
+ /* Flush E-cache */
+ cheetah_flush_ecache();
+
+ p = cheetah_get_error_log(afsr);
+ if (!p) {
+ prom_printf("ERROR: Early Fast-ECC error afsr[%016lx] afar[%016lx]\n",
+ afsr, afar);
+ prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+ smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+ prom_halt();
+ }
+
+ /* Grab snapshot of logged error. */
+ memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+ /* If the current trap snapshot does not match what the
+ * trap handler passed along into our args, big trouble.
+ * In such a case, mark the local copy as invalid.
+ *
+ * Else, it matches and we mark the afsr in the non-local
+ * copy as invalid so we may log new error traps there.
+ */
+ if (p->afsr != afsr || p->afar != afar)
+ local_snapshot.afsr = CHAFSR_INVALID;
+ else
+ p->afsr = CHAFSR_INVALID;
+
+ cheetah_flush_icache();
+ cheetah_flush_dcache();
+
+ /* Re-enable I-cache/D-cache */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_DC | DCU_IC)
+ : "g1");
+
+ /* Re-enable error reporting */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_ESTATE_ERROR_EN),
+ "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+ : "g1");
+
+ /* Decide if we can continue after handling this trap and
+ * logging the error.
+ */
+ recoverable = 1;
+ if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+ recoverable = 0;
+
+ /* Re-check AFSR/AFAR. What we are looking for here is whether a new
+ * error was logged while we had error reporting traps disabled.
+ */
+ if (cheetah_recheck_errors(&local_snapshot)) {
+ unsigned long new_afsr = local_snapshot.afsr;
+
+ /* If we got a new asynchronous error, die... */
+ if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
+ CHAFSR_WDU | CHAFSR_CPU |
+ CHAFSR_IVU | CHAFSR_UE |
+ CHAFSR_BERR | CHAFSR_TO))
+ recoverable = 0;
+ }
+
+ /* Log errors. */
+ cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+ if (!recoverable)
+ panic("Irrecoverable Fast-ECC error trap.\n");
+
+ /* Flush E-cache to kick the error trap handlers out. */
+ cheetah_flush_ecache();
+}
+
+/* Try to fix a correctable error by pushing the line out from
+ * the E-cache. Recheck error reporting registers to see if the
+ * problem is intermittent.
+ */
+static int cheetah_fix_ce(unsigned long physaddr)
+{
+ unsigned long orig_estate;
+ unsigned long alias1, alias2;
+ int ret;
+
+ /* Make sure correctable error traps are disabled. */
+ __asm__ __volatile__("ldxa [%%g0] %2, %0\n\t"
+ "andn %0, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %2\n\t"
+ "membar #Sync"
+ : "=&r" (orig_estate)
+ : "i" (ESTATE_ERROR_CEEN),
+ "i" (ASI_ESTATE_ERROR_EN)
+ : "g1");
+
+ /* We calculate alias addresses that will force the
+ * cache line in question out of the E-cache. Then
+ * we bring it back in with an atomic instruction so
+ * that we get it in some modified/exclusive state,
+ * then we displace it again to try and get proper ECC
+ * pushed back into the system.
+ */
+ physaddr &= ~(8UL - 1UL);
+ alias1 = (ecache_flush_physbase +
+ (physaddr & ((ecache_flush_size >> 1) - 1)));
+ alias2 = alias1 + (ecache_flush_size >> 1);
+ __asm__ __volatile__("ldxa [%0] %3, %%g0\n\t"
+ "ldxa [%1] %3, %%g0\n\t"
+ "casxa [%2] %3, %%g0, %%g0\n\t"
+ "ldxa [%0] %3, %%g0\n\t"
+ "ldxa [%1] %3, %%g0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (alias1), "r" (alias2),
+ "r" (physaddr), "i" (ASI_PHYS_USE_EC));
+
+ /* Did that trigger another error? */
+ if (cheetah_recheck_errors(NULL)) {
+ /* Try one more time. */
+ __asm__ __volatile__("ldxa [%0] %1, %%g0\n\t"
+ "membar #Sync"
+ : : "r" (physaddr), "i" (ASI_PHYS_USE_EC));
+ if (cheetah_recheck_errors(NULL))
+ ret = 2;
+ else
+ ret = 1;
+ } else {
+ /* No new error, intermittent problem. */
+ ret = 0;
+ }
+
+ /* Restore error enables. */
+ __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
+ "membar #Sync"
+ : : "r" (orig_estate), "i" (ASI_ESTATE_ERROR_EN));
+
+ return ret;
+}
+
+/* Return non-zero if PADDR is a valid physical memory address. */
+static int cheetah_check_main_memory(unsigned long paddr)
+{
+ unsigned long vaddr = PAGE_OFFSET + paddr;
+
+ if (vaddr > (unsigned long) high_memory)
+ return 0;
+
+ return kern_addr_valid(vaddr);
+}
+
+void cheetah_cee_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+ struct cheetah_err_info local_snapshot, *p;
+ int recoverable, is_memory;
+
+ p = cheetah_get_error_log(afsr);
+ if (!p) {
+ prom_printf("ERROR: Early CEE error afsr[%016lx] afar[%016lx]\n",
+ afsr, afar);
+ prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+ smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+ prom_halt();
+ }
+
+ /* Grab snapshot of logged error. */
+ memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+ /* If the current trap snapshot does not match what the
+ * trap handler passed along into our args, big trouble.
+ * In such a case, mark the local copy as invalid.
+ *
+ * Else, it matches and we mark the afsr in the non-local
+ * copy as invalid so we may log new error traps there.
+ */
+ if (p->afsr != afsr || p->afar != afar)
+ local_snapshot.afsr = CHAFSR_INVALID;
+ else
+ p->afsr = CHAFSR_INVALID;
+
+ is_memory = cheetah_check_main_memory(afar);
+
+ if (is_memory && (afsr & CHAFSR_CE) != 0UL) {
+ /* XXX Might want to log the results of this operation
+ * XXX somewhere... -DaveM
+ */
+ cheetah_fix_ce(afar);
+ }
+
+ {
+ int flush_all, flush_line;
+
+ flush_all = flush_line = 0;
+ if ((afsr & CHAFSR_EDC) != 0UL) {
+ if ((afsr & cheetah_afsr_errors) == CHAFSR_EDC)
+ flush_line = 1;
+ else
+ flush_all = 1;
+ } else if ((afsr & CHAFSR_CPC) != 0UL) {
+ if ((afsr & cheetah_afsr_errors) == CHAFSR_CPC)
+ flush_line = 1;
+ else
+ flush_all = 1;
+ }
+
+ /* Trap handler only disabled I-cache, flush it. */
+ cheetah_flush_icache();
+
+ /* Re-enable I-cache */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_IC)
+ : "g1");
+
+ if (flush_all)
+ cheetah_flush_ecache();
+ else if (flush_line)
+ cheetah_flush_ecache_line(afar);
+ }
+
+ /* Re-enable error reporting */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_ESTATE_ERROR_EN),
+ "i" (ESTATE_ERROR_CEEN)
+ : "g1");
+
+ /* Decide if we can continue after handling this trap and
+ * logging the error.
+ */
+ recoverable = 1;
+ if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+ recoverable = 0;
+
+ /* Re-check AFSR/AFAR */
+ (void) cheetah_recheck_errors(&local_snapshot);
+
+ /* Log errors. */
+ cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+ if (!recoverable)
+ panic("Irrecoverable Correctable-ECC error trap.\n");
+}
+
+void cheetah_deferred_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
+{
+ struct cheetah_err_info local_snapshot, *p;
+ int recoverable, is_memory;
+
+#ifdef CONFIG_PCI
+ /* Check for the special PCI poke sequence. */
+ if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
+ cheetah_flush_icache();
+ cheetah_flush_dcache();
+
+ /* Re-enable I-cache/D-cache */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_DC | DCU_IC)
+ : "g1");
+
+ /* Re-enable error reporting */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_ESTATE_ERROR_EN),
+ "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+ : "g1");
+
+ (void) cheetah_recheck_errors(NULL);
+
+ pci_poke_faulted = 1;
+ regs->tpc += 4;
+ regs->tnpc = regs->tpc + 4;
+ return;
+ }
+#endif
+
+ p = cheetah_get_error_log(afsr);
+ if (!p) {
+ prom_printf("ERROR: Early deferred error afsr[%016lx] afar[%016lx]\n",
+ afsr, afar);
+ prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
+ smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
+ prom_halt();
+ }
+
+ /* Grab snapshot of logged error. */
+ memcpy(&local_snapshot, p, sizeof(local_snapshot));
+
+ /* If the current trap snapshot does not match what the
+ * trap handler passed along into our args, big trouble.
+ * In such a case, mark the local copy as invalid.
+ *
+ * Else, it matches and we mark the afsr in the non-local
+ * copy as invalid so we may log new error traps there.
+ */
+ if (p->afsr != afsr || p->afar != afar)
+ local_snapshot.afsr = CHAFSR_INVALID;
+ else
+ p->afsr = CHAFSR_INVALID;
+
+ is_memory = cheetah_check_main_memory(afar);
+
+ {
+ int flush_all, flush_line;
+
+ flush_all = flush_line = 0;
+ if ((afsr & CHAFSR_EDU) != 0UL) {
+ if ((afsr & cheetah_afsr_errors) == CHAFSR_EDU)
+ flush_line = 1;
+ else
+ flush_all = 1;
+ } else if ((afsr & CHAFSR_BERR) != 0UL) {
+ if ((afsr & cheetah_afsr_errors) == CHAFSR_BERR)
+ flush_line = 1;
+ else
+ flush_all = 1;
+ }
+
+ cheetah_flush_icache();
+ cheetah_flush_dcache();
+
+ /* Re-enable I/D caches */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_IC | DCU_DC)
+ : "g1");
+
+ if (flush_all)
+ cheetah_flush_ecache();
+ else if (flush_line)
+ cheetah_flush_ecache_line(afar);
+ }
+
+ /* Re-enable error reporting */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_ESTATE_ERROR_EN),
+ "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
+ : "g1");
+
+ /* Decide if we can continue after handling this trap and
+ * logging the error.
+ */
+ recoverable = 1;
+ if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
+ recoverable = 0;
+
+ /* Re-check AFSR/AFAR. What we are looking for here is whether a new
+ * error was logged while we had error reporting traps disabled.
+ */
+ if (cheetah_recheck_errors(&local_snapshot)) {
+ unsigned long new_afsr = local_snapshot.afsr;
+
+ /* If we got a new asynchronous error, die... */
+ if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
+ CHAFSR_WDU | CHAFSR_CPU |
+ CHAFSR_IVU | CHAFSR_UE |
+ CHAFSR_BERR | CHAFSR_TO))
+ recoverable = 0;
+ }
+
+ /* Log errors. */
+ cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
+
+ /* "Recoverable" here means we try to yank the page from ever
+ * being newly used again. This depends upon a few things:
+ * 1) Must be main memory, and AFAR must be valid.
+ * 2) If we trapped from user, OK.
+ * 3) Else, if we trapped from kernel we must find exception
+ * table entry (ie. we have to have been accessing user
+ * space).
+ *
+ * If AFAR is not in main memory, or we trapped from kernel
+ * and cannot find an exception table entry, it is unacceptable
+ * to try and continue.
+ */
+ if (recoverable && is_memory) {
+ if ((regs->tstate & TSTATE_PRIV) == 0UL) {
+ /* OK, usermode access. */
+ recoverable = 1;
+ } else {
+ const struct exception_table_entry *entry;
+
+ entry = search_exception_tables(regs->tpc);
+ if (entry) {
+ /* OK, kernel access to userspace. */
+ recoverable = 1;
+
+ } else {
+ /* BAD, privileged state is corrupted. */
+ recoverable = 0;
+ }
+
+ if (recoverable) {
+ if (pfn_valid(afar >> PAGE_SHIFT))
+ get_page(pfn_to_page(afar >> PAGE_SHIFT));
+ else
+ recoverable = 0;
+
+ /* Only perform fixup if we still have a
+ * recoverable condition.
+ */
+ if (recoverable) {
+ regs->tpc = entry->fixup;
+ regs->tnpc = regs->tpc + 4;
+ }
+ }
+ }
+ } else {
+ recoverable = 0;
+ }
+
+ if (!recoverable)
+ panic("Irrecoverable deferred error trap.\n");
+}
+
+/* Handle a D/I cache parity error trap. TYPE is encoded as:
+ *
+ * Bit0: 0=dcache,1=icache
+ * Bit1: 0=recoverable,1=unrecoverable
+ *
+ * The hardware has disabled both the I-cache and D-cache in
+ * the %dcr register.
+ */
+void cheetah_plus_parity_error(int type, struct pt_regs *regs)
+{
+ if (type & 0x1)
+ __cheetah_flush_icache();
+ else
+ cheetah_plus_zap_dcache_parity();
+ cheetah_flush_dcache();
+
+ /* Re-enable I-cache/D-cache */
+ __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
+ "or %%g1, %1, %%g1\n\t"
+ "stxa %%g1, [%%g0] %0\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "i" (ASI_DCU_CONTROL_REG),
+ "i" (DCU_DC | DCU_IC)
+ : "g1");
+
+ if (type & 0x2) {
+ printk(KERN_EMERG "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
+ smp_processor_id(),
+ (type & 0x1) ? 'I' : 'D',
+ regs->tpc);
+ printk(KERN_EMERG "TPC<%pS>\n", (void *) regs->tpc);
+ panic("Irrecoverable Cheetah+ parity error.");
+ }
+
+ printk(KERN_WARNING "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
+ smp_processor_id(),
+ (type & 0x1) ? 'I' : 'D',
+ regs->tpc);
+ printk(KERN_WARNING "TPC<%pS>\n", (void *) regs->tpc);
+}
+
+struct sun4v_error_entry {
+ u64 err_handle;
+ u64 err_stick;
+
+ u32 err_type;
+#define SUN4V_ERR_TYPE_UNDEFINED 0
+#define SUN4V_ERR_TYPE_UNCORRECTED_RES 1
+#define SUN4V_ERR_TYPE_PRECISE_NONRES 2
+#define SUN4V_ERR_TYPE_DEFERRED_NONRES 3
+#define SUN4V_ERR_TYPE_WARNING_RES 4
+
+ u32 err_attrs;
+#define SUN4V_ERR_ATTRS_PROCESSOR 0x00000001
+#define SUN4V_ERR_ATTRS_MEMORY 0x00000002
+#define SUN4V_ERR_ATTRS_PIO 0x00000004
+#define SUN4V_ERR_ATTRS_INT_REGISTERS 0x00000008
+#define SUN4V_ERR_ATTRS_FPU_REGISTERS 0x00000010
+#define SUN4V_ERR_ATTRS_USER_MODE 0x01000000
+#define SUN4V_ERR_ATTRS_PRIV_MODE 0x02000000
+#define SUN4V_ERR_ATTRS_RES_QUEUE_FULL 0x80000000
+
+ u64 err_raddr;
+ u32 err_size;
+ u16 err_cpu;
+ u16 err_pad;
+};
+
+static atomic_t sun4v_resum_oflow_cnt = ATOMIC_INIT(0);
+static atomic_t sun4v_nonresum_oflow_cnt = ATOMIC_INIT(0);
+
+static const char *sun4v_err_type_to_str(u32 type)
+{
+ switch (type) {
+ case SUN4V_ERR_TYPE_UNDEFINED:
+ return "undefined";
+ case SUN4V_ERR_TYPE_UNCORRECTED_RES:
+ return "uncorrected resumable";
+ case SUN4V_ERR_TYPE_PRECISE_NONRES:
+ return "precise nonresumable";
+ case SUN4V_ERR_TYPE_DEFERRED_NONRES:
+ return "deferred nonresumable";
+ case SUN4V_ERR_TYPE_WARNING_RES:
+ return "warning resumable";
+ default:
+ return "unknown";
+ };
+}
+
+static void sun4v_log_error(struct pt_regs *regs, struct sun4v_error_entry *ent, int cpu, const char *pfx, atomic_t *ocnt)
+{
+ int cnt;
+
+ printk("%s: Reporting on cpu %d\n", pfx, cpu);
+ printk("%s: err_handle[%lx] err_stick[%lx] err_type[%08x:%s]\n",
+ pfx,
+ ent->err_handle, ent->err_stick,
+ ent->err_type,
+ sun4v_err_type_to_str(ent->err_type));
+ printk("%s: err_attrs[%08x:%s %s %s %s %s %s %s %s]\n",
+ pfx,
+ ent->err_attrs,
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_PROCESSOR) ?
+ "processor" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_MEMORY) ?
+ "memory" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_PIO) ?
+ "pio" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_INT_REGISTERS) ?
+ "integer-regs" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_FPU_REGISTERS) ?
+ "fpu-regs" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_USER_MODE) ?
+ "user" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_PRIV_MODE) ?
+ "privileged" : ""),
+ ((ent->err_attrs & SUN4V_ERR_ATTRS_RES_QUEUE_FULL) ?
+ "queue-full" : ""));
+ printk("%s: err_raddr[%016lx] err_size[%u] err_cpu[%u]\n",
+ pfx,
+ ent->err_raddr, ent->err_size, ent->err_cpu);
+
+ show_regs(regs);
+
+ if ((cnt = atomic_read(ocnt)) != 0) {
+ atomic_set(ocnt, 0);
+ wmb();
+ printk("%s: Queue overflowed %d times.\n",
+ pfx, cnt);
+ }
+}
+
+/* We run with %pil set to PIL_NORMAL_MAX and PSTATE_IE enabled in %pstate.
+ * Log the event and clear the first word of the entry.
+ */
+void sun4v_resum_error(struct pt_regs *regs, unsigned long offset)
+{
+ struct sun4v_error_entry *ent, local_copy;
+ struct trap_per_cpu *tb;
+ unsigned long paddr;
+ int cpu;
+
+ cpu = get_cpu();
+
+ tb = &trap_block[cpu];
+ paddr = tb->resum_kernel_buf_pa + offset;
+ ent = __va(paddr);
+
+ memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry));
+
+ /* We have a local copy now, so release the entry. */
+ ent->err_handle = 0;
+ wmb();
+
+ put_cpu();
+
+ if (ent->err_type == SUN4V_ERR_TYPE_WARNING_RES) {
+ /* If err_type is 0x4, it's a powerdown request. Do
+ * not do the usual resumable error log because that
+ * makes it look like some abnormal error.
+ */
+ printk(KERN_INFO "Power down request...\n");
+ kill_cad_pid(SIGINT, 1);
+ return;
+ }
+
+ sun4v_log_error(regs, &local_copy, cpu,
+ KERN_ERR "RESUMABLE ERROR",
+ &sun4v_resum_oflow_cnt);
+}
+
+/* If we try to printk() we'll probably make matters worse, by trying
+ * to retake locks this cpu already holds or causing more errors. So
+ * just bump a counter, and we'll report these counter bumps above.
+ */
+void sun4v_resum_overflow(struct pt_regs *regs)
+{
+ atomic_inc(&sun4v_resum_oflow_cnt);
+}
+
+/* We run with %pil set to PIL_NORMAL_MAX and PSTATE_IE enabled in %pstate.
+ * Log the event, clear the first word of the entry, and die.
+ */
+void sun4v_nonresum_error(struct pt_regs *regs, unsigned long offset)
+{
+ struct sun4v_error_entry *ent, local_copy;
+ struct trap_per_cpu *tb;
+ unsigned long paddr;
+ int cpu;
+
+ cpu = get_cpu();
+
+ tb = &trap_block[cpu];
+ paddr = tb->nonresum_kernel_buf_pa + offset;
+ ent = __va(paddr);
+
+ memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry));
+
+ /* We have a local copy now, so release the entry. */
+ ent->err_handle = 0;
+ wmb();
+
+ put_cpu();
+
+#ifdef CONFIG_PCI
+ /* Check for the special PCI poke sequence. */
+ if (pci_poke_in_progress && pci_poke_cpu == cpu) {
+ pci_poke_faulted = 1;
+ regs->tpc += 4;
+ regs->tnpc = regs->tpc + 4;
+ return;
+ }
+#endif
+
+ sun4v_log_error(regs, &local_copy, cpu,
+ KERN_EMERG "NON-RESUMABLE ERROR",
+ &sun4v_nonresum_oflow_cnt);
+
+ panic("Non-resumable error.");
+}
+
+/* If we try to printk() we'll probably make matters worse, by trying
+ * to retake locks this cpu already holds or causing more errors. So
+ * just bump a counter, and we'll report these counter bumps above.
+ */
+void sun4v_nonresum_overflow(struct pt_regs *regs)
+{
+ /* XXX Actually even this can make not that much sense. Perhaps
+ * XXX we should just pull the plug and panic directly from here?
+ */
+ atomic_inc(&sun4v_nonresum_oflow_cnt);
+}
+
+unsigned long sun4v_err_itlb_vaddr;
+unsigned long sun4v_err_itlb_ctx;
+unsigned long sun4v_err_itlb_pte;
+unsigned long sun4v_err_itlb_error;
+
+void sun4v_itlb_error_report(struct pt_regs *regs, int tl)
+{
+ if (tl > 1)
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+
+ printk(KERN_EMERG "SUN4V-ITLB: Error at TPC[%lx], tl %d\n",
+ regs->tpc, tl);
+ printk(KERN_EMERG "SUN4V-ITLB: TPC<%pS>\n", (void *) regs->tpc);
+ printk(KERN_EMERG "SUN4V-ITLB: O7[%lx]\n", regs->u_regs[UREG_I7]);
+ printk(KERN_EMERG "SUN4V-ITLB: O7<%pS>\n",
+ (void *) regs->u_regs[UREG_I7]);
+ printk(KERN_EMERG "SUN4V-ITLB: vaddr[%lx] ctx[%lx] "
+ "pte[%lx] error[%lx]\n",
+ sun4v_err_itlb_vaddr, sun4v_err_itlb_ctx,
+ sun4v_err_itlb_pte, sun4v_err_itlb_error);
+
+ prom_halt();
+}
+
+unsigned long sun4v_err_dtlb_vaddr;
+unsigned long sun4v_err_dtlb_ctx;
+unsigned long sun4v_err_dtlb_pte;
+unsigned long sun4v_err_dtlb_error;
+
+void sun4v_dtlb_error_report(struct pt_regs *regs, int tl)
+{
+ if (tl > 1)
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+
+ printk(KERN_EMERG "SUN4V-DTLB: Error at TPC[%lx], tl %d\n",
+ regs->tpc, tl);
+ printk(KERN_EMERG "SUN4V-DTLB: TPC<%pS>\n", (void *) regs->tpc);
+ printk(KERN_EMERG "SUN4V-DTLB: O7[%lx]\n", regs->u_regs[UREG_I7]);
+ printk(KERN_EMERG "SUN4V-DTLB: O7<%pS>\n",
+ (void *) regs->u_regs[UREG_I7]);
+ printk(KERN_EMERG "SUN4V-DTLB: vaddr[%lx] ctx[%lx] "
+ "pte[%lx] error[%lx]\n",
+ sun4v_err_dtlb_vaddr, sun4v_err_dtlb_ctx,
+ sun4v_err_dtlb_pte, sun4v_err_dtlb_error);
+
+ prom_halt();
+}
+
+void hypervisor_tlbop_error(unsigned long err, unsigned long op)
+{
+ printk(KERN_CRIT "SUN4V: TLB hv call error %lu for op %lu\n",
+ err, op);
+}
+
+void hypervisor_tlbop_error_xcall(unsigned long err, unsigned long op)
+{
+ printk(KERN_CRIT "SUN4V: XCALL TLB hv call error %lu for op %lu\n",
+ err, op);
+}
+
+void do_fpe_common(struct pt_regs *regs)
+{
+ if (regs->tstate & TSTATE_PRIV) {
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+ } else {
+ unsigned long fsr = current_thread_info()->xfsr[0];
+ siginfo_t info;
+
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = 0;
+ info.si_code = __SI_FAULT;
+ if ((fsr & 0x1c000) == (1 << 14)) {
+ if (fsr & 0x10)
+ info.si_code = FPE_FLTINV;
+ else if (fsr & 0x08)
+ info.si_code = FPE_FLTOVF;
+ else if (fsr & 0x04)
+ info.si_code = FPE_FLTUND;
+ else if (fsr & 0x02)
+ info.si_code = FPE_FLTDIV;
+ else if (fsr & 0x01)
+ info.si_code = FPE_FLTRES;
+ }
+ force_sig_info(SIGFPE, &info, current);
+ }
+}
+
+void do_fpieee(struct pt_regs *regs)
+{
+ if (notify_die(DIE_TRAP, "fpu exception ieee", regs,
+ 0, 0x24, SIGFPE) == NOTIFY_STOP)
+ return;
+
+ do_fpe_common(regs);
+}
+
+extern int do_mathemu(struct pt_regs *, struct fpustate *);
+
+void do_fpother(struct pt_regs *regs)
+{
+ struct fpustate *f = FPUSTATE;
+ int ret = 0;
+
+ if (notify_die(DIE_TRAP, "fpu exception other", regs,
+ 0, 0x25, SIGFPE) == NOTIFY_STOP)
+ return;
+
+ switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
+ case (2 << 14): /* unfinished_FPop */
+ case (3 << 14): /* unimplemented_FPop */
+ ret = do_mathemu(regs, f);
+ break;
+ }
+ if (ret)
+ return;
+ do_fpe_common(regs);
+}
+
+void do_tof(struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs,
+ 0, 0x26, SIGEMT) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV)
+ die_if_kernel("Penguin overflow trap from kernel mode", regs);
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGEMT;
+ info.si_errno = 0;
+ info.si_code = EMT_TAGOVF;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = 0;
+ force_sig_info(SIGEMT, &info, current);
+}
+
+void do_div0(struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "integer division by zero", regs,
+ 0, 0x28, SIGFPE) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV)
+ die_if_kernel("TL0: Kernel divide by zero.", regs);
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = FPE_INTDIV;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = 0;
+ force_sig_info(SIGFPE, &info, current);
+}
+
+static void instruction_dump(unsigned int *pc)
+{
+ int i;
+
+ if ((((unsigned long) pc) & 3))
+ return;
+
+ printk("Instruction DUMP:");
+ for (i = -3; i < 6; i++)
+ printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
+ printk("\n");
+}
+
+static void user_instruction_dump(unsigned int __user *pc)
+{
+ int i;
+ unsigned int buf[9];
+
+ if ((((unsigned long) pc) & 3))
+ return;
+
+ if (copy_from_user(buf, pc - 3, sizeof(buf)))
+ return;
+
+ printk("Instruction DUMP:");
+ for (i = 0; i < 9; i++)
+ printk("%c%08x%c",i==3?' ':'<',buf[i],i==3?' ':'>');
+ printk("\n");
+}
+
+void show_stack(struct task_struct *tsk, unsigned long *_ksp)
+{
+ unsigned long fp, thread_base, ksp;
+ struct thread_info *tp;
+ int count = 0;
+
+ ksp = (unsigned long) _ksp;
+ if (!tsk)
+ tsk = current;
+ tp = task_thread_info(tsk);
+ if (ksp == 0UL) {
+ if (tsk == current)
+ asm("mov %%fp, %0" : "=r" (ksp));
+ else
+ ksp = tp->ksp;
+ }
+ if (tp == current_thread_info())
+ flushw_all();
+
+ fp = ksp + STACK_BIAS;
+ thread_base = (unsigned long) tp;
+
+ printk("Call Trace:\n");
+ do {
+ struct sparc_stackf *sf;
+ struct pt_regs *regs;
+ unsigned long pc;
+
+ if (!kstack_valid(tp, fp))
+ break;
+ sf = (struct sparc_stackf *) fp;
+ regs = (struct pt_regs *) (sf + 1);
+
+ if (kstack_is_trap_frame(tp, regs)) {
+ if (!(regs->tstate & TSTATE_PRIV))
+ break;
+ pc = regs->tpc;
+ fp = regs->u_regs[UREG_I6] + STACK_BIAS;
+ } else {
+ pc = sf->callers_pc;
+ fp = (unsigned long)sf->fp + STACK_BIAS;
+ }
+
+ printk(" [%016lx] %pS\n", pc, (void *) pc);
+ } while (++count < 16);
+}
+
+void dump_stack(void)
+{
+ show_stack(current, NULL);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+static inline int is_kernel_stack(struct task_struct *task,
+ struct reg_window *rw)
+{
+ unsigned long rw_addr = (unsigned long) rw;
+ unsigned long thread_base, thread_end;
+
+ if (rw_addr < PAGE_OFFSET) {
+ if (task != &init_task)
+ return 0;
+ }
+
+ thread_base = (unsigned long) task_stack_page(task);
+ thread_end = thread_base + sizeof(union thread_union);
+ if (rw_addr >= thread_base &&
+ rw_addr < thread_end &&
+ !(rw_addr & 0x7UL))
+ return 1;
+
+ return 0;
+}
+
+static inline struct reg_window *kernel_stack_up(struct reg_window *rw)
+{
+ unsigned long fp = rw->ins[6];
+
+ if (!fp)
+ return NULL;
+
+ return (struct reg_window *) (fp + STACK_BIAS);
+}
+
+void die_if_kernel(char *str, struct pt_regs *regs)
+{
+ static int die_counter;
+ int count = 0;
+
+ /* Amuse the user. */
+ printk(
+" \\|/ ____ \\|/\n"
+" \"@'/ .. \\`@\"\n"
+" /_| \\__/ |_\\\n"
+" \\__U_/\n");
+
+ printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
+ notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV);
+ __asm__ __volatile__("flushw");
+ show_regs(regs);
+ add_taint(TAINT_DIE);
+ if (regs->tstate & TSTATE_PRIV) {
+ struct reg_window *rw = (struct reg_window *)
+ (regs->u_regs[UREG_FP] + STACK_BIAS);
+
+ /* Stop the back trace when we hit userland or we
+ * find some badly aligned kernel stack.
+ */
+ while (rw &&
+ count++ < 30&&
+ is_kernel_stack(current, rw)) {
+ printk("Caller[%016lx]: %pS\n", rw->ins[7],
+ (void *) rw->ins[7]);
+
+ rw = kernel_stack_up(rw);
+ }
+ instruction_dump ((unsigned int *) regs->tpc);
+ } else {
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ user_instruction_dump ((unsigned int __user *) regs->tpc);
+ }
+ if (regs->tstate & TSTATE_PRIV)
+ do_exit(SIGKILL);
+ do_exit(SIGSEGV);
+}
+
+#define VIS_OPCODE_MASK ((0x3 << 30) | (0x3f << 19))
+#define VIS_OPCODE_VAL ((0x2 << 30) | (0x36 << 19))
+
+extern int handle_popc(u32 insn, struct pt_regs *regs);
+extern int handle_ldf_stq(u32 insn, struct pt_regs *regs);
+
+void do_illegal_instruction(struct pt_regs *regs)
+{
+ unsigned long pc = regs->tpc;
+ unsigned long tstate = regs->tstate;
+ u32 insn;
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "illegal instruction", regs,
+ 0, 0x10, SIGILL) == NOTIFY_STOP)
+ return;
+
+ if (tstate & TSTATE_PRIV)
+ die_if_kernel("Kernel illegal instruction", regs);
+ if (test_thread_flag(TIF_32BIT))
+ pc = (u32)pc;
+ if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
+ if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ {
+ if (handle_popc(insn, regs))
+ return;
+ } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ {
+ if (handle_ldf_stq(insn, regs))
+ return;
+ } else if (tlb_type == hypervisor) {
+ if ((insn & VIS_OPCODE_MASK) == VIS_OPCODE_VAL) {
+ if (!vis_emul(regs, insn))
+ return;
+ } else {
+ struct fpustate *f = FPUSTATE;
+
+ /* XXX maybe verify XFSR bits like
+ * XXX do_fpother() does?
+ */
+ if (do_mathemu(regs, f))
+ return;
+ }
+ }
+ }
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPC;
+ info.si_addr = (void __user *)pc;
+ info.si_trapno = 0;
+ force_sig_info(SIGILL, &info, current);
+}
+
+extern void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn);
+
+void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "memory address unaligned", regs,
+ 0, 0x34, SIGSEGV) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
+ return;
+ }
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void __user *)sfar;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
+}
+
+void sun4v_do_mna(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "memory address unaligned", regs,
+ 0, 0x34, SIGSEGV) == NOTIFY_STOP)
+ return;
+
+ if (regs->tstate & TSTATE_PRIV) {
+ kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
+ return;
+ }
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void __user *) addr;
+ info.si_trapno = 0;
+ force_sig_info(SIGBUS, &info, current);
+}
+
+void do_privop(struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, "privileged operation", regs,
+ 0, 0x11, SIGILL) == NOTIFY_STOP)
+ return;
+
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_PRVOPC;
+ info.si_addr = (void __user *)regs->tpc;
+ info.si_trapno = 0;
+ force_sig_info(SIGILL, &info, current);
+}
+
+void do_privact(struct pt_regs *regs)
+{
+ do_privop(regs);
+}
+
+/* Trap level 1 stuff or other traps we should never see... */
+void do_cee(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Cache Error Exception", regs);
+}
+
+void do_cee_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Cache Error Exception", regs);
+}
+
+void do_dae_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Data Access Exception", regs);
+}
+
+void do_iae_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Instruction Access Exception", regs);
+}
+
+void do_div0_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: DIV0 Exception", regs);
+}
+
+void do_fpdis_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: FPU Disabled", regs);
+}
+
+void do_fpieee_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: FPU IEEE Exception", regs);
+}
+
+void do_fpother_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: FPU Other Exception", regs);
+}
+
+void do_ill_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Illegal Instruction Exception", regs);
+}
+
+void do_irq_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: IRQ Exception", regs);
+}
+
+void do_lddfmna_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: LDDF Exception", regs);
+}
+
+void do_stdfmna_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: STDF Exception", regs);
+}
+
+void do_paw(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Phys Watchpoint Exception", regs);
+}
+
+void do_paw_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Phys Watchpoint Exception", regs);
+}
+
+void do_vaw(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Virt Watchpoint Exception", regs);
+}
+
+void do_vaw_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Virt Watchpoint Exception", regs);
+}
+
+void do_tof_tl1(struct pt_regs *regs)
+{
+ dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
+ die_if_kernel("TL1: Tag Overflow Exception", regs);
+}
+
+void do_getpsr(struct pt_regs *regs)
+{
+ regs->u_regs[UREG_I0] = tstate_to_psr(regs->tstate);
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+ if (test_thread_flag(TIF_32BIT)) {
+ regs->tpc &= 0xffffffff;
+ regs->tnpc &= 0xffffffff;
+ }
+}
+
+struct trap_per_cpu trap_block[NR_CPUS];
+
+/* This can get invoked before sched_init() so play it super safe
+ * and use hard_smp_processor_id().
+ */
+void notrace init_cur_cpu_trap(struct thread_info *t)
+{
+ int cpu = hard_smp_processor_id();
+ struct trap_per_cpu *p = &trap_block[cpu];
+
+ p->thread = t;
+ p->pgd_paddr = 0;
+}
+
+extern void thread_info_offsets_are_bolixed_dave(void);
+extern void trap_per_cpu_offsets_are_bolixed_dave(void);
+extern void tsb_config_offsets_are_bolixed_dave(void);
+
+/* Only invoked on boot processor. */
+void __init trap_init(void)
+{
+ /* Compile time sanity check. */
+ if (TI_TASK != offsetof(struct thread_info, task) ||
+ TI_FLAGS != offsetof(struct thread_info, flags) ||
+ TI_CPU != offsetof(struct thread_info, cpu) ||
+ TI_FPSAVED != offsetof(struct thread_info, fpsaved) ||
+ TI_KSP != offsetof(struct thread_info, ksp) ||
+ TI_FAULT_ADDR != offsetof(struct thread_info, fault_address) ||
+ TI_KREGS != offsetof(struct thread_info, kregs) ||
+ TI_UTRAPS != offsetof(struct thread_info, utraps) ||
+ TI_EXEC_DOMAIN != offsetof(struct thread_info, exec_domain) ||
+ TI_REG_WINDOW != offsetof(struct thread_info, reg_window) ||
+ TI_RWIN_SPTRS != offsetof(struct thread_info, rwbuf_stkptrs) ||
+ TI_GSR != offsetof(struct thread_info, gsr) ||
+ TI_XFSR != offsetof(struct thread_info, xfsr) ||
+ TI_USER_CNTD0 != offsetof(struct thread_info, user_cntd0) ||
+ TI_USER_CNTD1 != offsetof(struct thread_info, user_cntd1) ||
+ TI_KERN_CNTD0 != offsetof(struct thread_info, kernel_cntd0) ||
+ TI_KERN_CNTD1 != offsetof(struct thread_info, kernel_cntd1) ||
+ TI_PCR != offsetof(struct thread_info, pcr_reg) ||
+ TI_PRE_COUNT != offsetof(struct thread_info, preempt_count) ||
+ TI_NEW_CHILD != offsetof(struct thread_info, new_child) ||
+ TI_SYS_NOERROR != offsetof(struct thread_info, syscall_noerror) ||
+ TI_RESTART_BLOCK != offsetof(struct thread_info, restart_block) ||
+ TI_KUNA_REGS != offsetof(struct thread_info, kern_una_regs) ||
+ TI_KUNA_INSN != offsetof(struct thread_info, kern_una_insn) ||
+ TI_FPREGS != offsetof(struct thread_info, fpregs) ||
+ (TI_FPREGS & (64 - 1)))
+ thread_info_offsets_are_bolixed_dave();
+
+ if (TRAP_PER_CPU_THREAD != offsetof(struct trap_per_cpu, thread) ||
+ (TRAP_PER_CPU_PGD_PADDR !=
+ offsetof(struct trap_per_cpu, pgd_paddr)) ||
+ (TRAP_PER_CPU_CPU_MONDO_PA !=
+ offsetof(struct trap_per_cpu, cpu_mondo_pa)) ||
+ (TRAP_PER_CPU_DEV_MONDO_PA !=
+ offsetof(struct trap_per_cpu, dev_mondo_pa)) ||
+ (TRAP_PER_CPU_RESUM_MONDO_PA !=
+ offsetof(struct trap_per_cpu, resum_mondo_pa)) ||
+ (TRAP_PER_CPU_RESUM_KBUF_PA !=
+ offsetof(struct trap_per_cpu, resum_kernel_buf_pa)) ||
+ (TRAP_PER_CPU_NONRESUM_MONDO_PA !=
+ offsetof(struct trap_per_cpu, nonresum_mondo_pa)) ||
+ (TRAP_PER_CPU_NONRESUM_KBUF_PA !=
+ offsetof(struct trap_per_cpu, nonresum_kernel_buf_pa)) ||
+ (TRAP_PER_CPU_FAULT_INFO !=
+ offsetof(struct trap_per_cpu, fault_info)) ||
+ (TRAP_PER_CPU_CPU_MONDO_BLOCK_PA !=
+ offsetof(struct trap_per_cpu, cpu_mondo_block_pa)) ||
+ (TRAP_PER_CPU_CPU_LIST_PA !=
+ offsetof(struct trap_per_cpu, cpu_list_pa)) ||
+ (TRAP_PER_CPU_TSB_HUGE !=
+ offsetof(struct trap_per_cpu, tsb_huge)) ||
+ (TRAP_PER_CPU_TSB_HUGE_TEMP !=
+ offsetof(struct trap_per_cpu, tsb_huge_temp)) ||
+ (TRAP_PER_CPU_IRQ_WORKLIST_PA !=
+ offsetof(struct trap_per_cpu, irq_worklist_pa)) ||
+ (TRAP_PER_CPU_CPU_MONDO_QMASK !=
+ offsetof(struct trap_per_cpu, cpu_mondo_qmask)) ||
+ (TRAP_PER_CPU_DEV_MONDO_QMASK !=
+ offsetof(struct trap_per_cpu, dev_mondo_qmask)) ||
+ (TRAP_PER_CPU_RESUM_QMASK !=
+ offsetof(struct trap_per_cpu, resum_qmask)) ||
+ (TRAP_PER_CPU_NONRESUM_QMASK !=
+ offsetof(struct trap_per_cpu, nonresum_qmask)))
+ trap_per_cpu_offsets_are_bolixed_dave();
+
+ if ((TSB_CONFIG_TSB !=
+ offsetof(struct tsb_config, tsb)) ||
+ (TSB_CONFIG_RSS_LIMIT !=
+ offsetof(struct tsb_config, tsb_rss_limit)) ||
+ (TSB_CONFIG_NENTRIES !=
+ offsetof(struct tsb_config, tsb_nentries)) ||
+ (TSB_CONFIG_REG_VAL !=
+ offsetof(struct tsb_config, tsb_reg_val)) ||
+ (TSB_CONFIG_MAP_VADDR !=
+ offsetof(struct tsb_config, tsb_map_vaddr)) ||
+ (TSB_CONFIG_MAP_PTE !=
+ offsetof(struct tsb_config, tsb_map_pte)))
+ tsb_config_offsets_are_bolixed_dave();
+
+ /* Attach to the address space of init_task. On SMP we
+ * do this in smp.c:smp_callin for other cpus.
+ */
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+}
diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S
new file mode 100644
index 000000000000..8c91d9b29a2f
--- /dev/null
+++ b/arch/sparc/kernel/tsb.S
@@ -0,0 +1,552 @@
+/* tsb.S: Sparc64 TSB table handling.
+ *
+ * Copyright (C) 2006 David S. Miller <davem@davemloft.net>
+ */
+
+
+#include <asm/tsb.h>
+#include <asm/hypervisor.h>
+#include <asm/page.h>
+#include <asm/cpudata.h>
+#include <asm/mmu.h>
+
+ .text
+ .align 32
+
+ /* Invoked from TLB miss handler, we are in the
+ * MMU global registers and they are setup like
+ * this:
+ *
+ * %g1: TSB entry pointer
+ * %g2: available temporary
+ * %g3: FAULT_CODE_{D,I}TLB
+ * %g4: available temporary
+ * %g5: available temporary
+ * %g6: TAG TARGET
+ * %g7: available temporary, will be loaded by us with
+ * the physical address base of the linux page
+ * tables for the current address space
+ */
+tsb_miss_dtlb:
+ mov TLB_TAG_ACCESS, %g4
+ ba,pt %xcc, tsb_miss_page_table_walk
+ ldxa [%g4] ASI_DMMU, %g4
+
+tsb_miss_itlb:
+ mov TLB_TAG_ACCESS, %g4
+ ba,pt %xcc, tsb_miss_page_table_walk
+ ldxa [%g4] ASI_IMMU, %g4
+
+ /* At this point we have:
+ * %g1 -- PAGE_SIZE TSB entry address
+ * %g3 -- FAULT_CODE_{D,I}TLB
+ * %g4 -- missing virtual address
+ * %g6 -- TAG TARGET (vaddr >> 22)
+ */
+tsb_miss_page_table_walk:
+ TRAP_LOAD_TRAP_BLOCK(%g7, %g5)
+
+ /* Before committing to a full page table walk,
+ * check the huge page TSB.
+ */
+#ifdef CONFIG_HUGETLB_PAGE
+
+661: ldx [%g7 + TRAP_PER_CPU_TSB_HUGE], %g5
+ nop
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ mov SCRATCHPAD_UTSBREG2, %g5
+ ldxa [%g5] ASI_SCRATCHPAD, %g5
+ .previous
+
+ cmp %g5, -1
+ be,pt %xcc, 80f
+ nop
+
+ /* We need an aligned pair of registers containing 2 values
+ * which can be easily rematerialized. %g6 and %g7 foot the
+ * bill just nicely. We'll save %g6 away into %g2 for the
+ * huge page TSB TAG comparison.
+ *
+ * Perform a huge page TSB lookup.
+ */
+ mov %g6, %g2
+ and %g5, 0x7, %g6
+ mov 512, %g7
+ andn %g5, 0x7, %g5
+ sllx %g7, %g6, %g7
+ srlx %g4, HPAGE_SHIFT, %g6
+ sub %g7, 1, %g7
+ and %g6, %g7, %g6
+ sllx %g6, 4, %g6
+ add %g5, %g6, %g5
+
+ TSB_LOAD_QUAD(%g5, %g6)
+ cmp %g6, %g2
+ be,a,pt %xcc, tsb_tlb_reload
+ mov %g7, %g5
+
+ /* No match, remember the huge page TSB entry address,
+ * and restore %g6 and %g7.
+ */
+ TRAP_LOAD_TRAP_BLOCK(%g7, %g6)
+ srlx %g4, 22, %g6
+80: stx %g5, [%g7 + TRAP_PER_CPU_TSB_HUGE_TEMP]
+
+#endif
+
+ ldx [%g7 + TRAP_PER_CPU_PGD_PADDR], %g7
+
+ /* At this point we have:
+ * %g1 -- TSB entry address
+ * %g3 -- FAULT_CODE_{D,I}TLB
+ * %g4 -- missing virtual address
+ * %g6 -- TAG TARGET (vaddr >> 22)
+ * %g7 -- page table physical address
+ *
+ * We know that both the base PAGE_SIZE TSB and the HPAGE_SIZE
+ * TSB both lack a matching entry.
+ */
+tsb_miss_page_table_walk_sun4v_fastpath:
+ USER_PGTABLE_WALK_TL1(%g4, %g7, %g5, %g2, tsb_do_fault)
+
+ /* Load and check PTE. */
+ ldxa [%g5] ASI_PHYS_USE_EC, %g5
+ brgez,pn %g5, tsb_do_fault
+ nop
+
+#ifdef CONFIG_HUGETLB_PAGE
+661: sethi %uhi(_PAGE_SZALL_4U), %g7
+ sllx %g7, 32, %g7
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ mov _PAGE_SZALL_4V, %g7
+ nop
+ .previous
+
+ and %g5, %g7, %g2
+
+661: sethi %uhi(_PAGE_SZHUGE_4U), %g7
+ sllx %g7, 32, %g7
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ mov _PAGE_SZHUGE_4V, %g7
+ nop
+ .previous
+
+ cmp %g2, %g7
+ bne,pt %xcc, 60f
+ nop
+
+ /* It is a huge page, use huge page TSB entry address we
+ * calculated above.
+ */
+ TRAP_LOAD_TRAP_BLOCK(%g7, %g2)
+ ldx [%g7 + TRAP_PER_CPU_TSB_HUGE_TEMP], %g2
+ cmp %g2, -1
+ movne %xcc, %g2, %g1
+60:
+#endif
+
+ /* At this point we have:
+ * %g1 -- TSB entry address
+ * %g3 -- FAULT_CODE_{D,I}TLB
+ * %g5 -- valid PTE
+ * %g6 -- TAG TARGET (vaddr >> 22)
+ */
+tsb_reload:
+ TSB_LOCK_TAG(%g1, %g2, %g7)
+ TSB_WRITE(%g1, %g5, %g6)
+
+ /* Finally, load TLB and return from trap. */
+tsb_tlb_reload:
+ cmp %g3, FAULT_CODE_DTLB
+ bne,pn %xcc, tsb_itlb_load
+ nop
+
+tsb_dtlb_load:
+
+661: stxa %g5, [%g0] ASI_DTLB_DATA_IN
+ retry
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ nop
+ nop
+ .previous
+
+ /* For sun4v the ASI_DTLB_DATA_IN store and the retry
+ * instruction get nop'd out and we get here to branch
+ * to the sun4v tlb load code. The registers are setup
+ * as follows:
+ *
+ * %g4: vaddr
+ * %g5: PTE
+ * %g6: TAG
+ *
+ * The sun4v TLB load wants the PTE in %g3 so we fix that
+ * up here.
+ */
+ ba,pt %xcc, sun4v_dtlb_load
+ mov %g5, %g3
+
+tsb_itlb_load:
+ /* Executable bit must be set. */
+661: andcc %g5, _PAGE_EXEC_4U, %g0
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ andcc %g5, _PAGE_EXEC_4V, %g0
+ .previous
+
+ be,pn %xcc, tsb_do_fault
+ nop
+
+661: stxa %g5, [%g0] ASI_ITLB_DATA_IN
+ retry
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ nop
+ nop
+ .previous
+
+ /* For sun4v the ASI_ITLB_DATA_IN store and the retry
+ * instruction get nop'd out and we get here to branch
+ * to the sun4v tlb load code. The registers are setup
+ * as follows:
+ *
+ * %g4: vaddr
+ * %g5: PTE
+ * %g6: TAG
+ *
+ * The sun4v TLB load wants the PTE in %g3 so we fix that
+ * up here.
+ */
+ ba,pt %xcc, sun4v_itlb_load
+ mov %g5, %g3
+
+ /* No valid entry in the page tables, do full fault
+ * processing.
+ */
+
+ .globl tsb_do_fault
+tsb_do_fault:
+ cmp %g3, FAULT_CODE_DTLB
+
+661: rdpr %pstate, %g5
+ wrpr %g5, PSTATE_AG | PSTATE_MG, %pstate
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ SET_GL(1)
+ ldxa [%g0] ASI_SCRATCHPAD, %g4
+ .previous
+
+ bne,pn %xcc, tsb_do_itlb_fault
+ nop
+
+tsb_do_dtlb_fault:
+ rdpr %tl, %g3
+ cmp %g3, 1
+
+661: mov TLB_TAG_ACCESS, %g4
+ ldxa [%g4] ASI_DMMU, %g5
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ ldx [%g4 + HV_FAULT_D_ADDR_OFFSET], %g5
+ nop
+ .previous
+
+ be,pt %xcc, sparc64_realfault_common
+ mov FAULT_CODE_DTLB, %g4
+ ba,pt %xcc, winfix_trampoline
+ nop
+
+tsb_do_itlb_fault:
+ rdpr %tpc, %g5
+ ba,pt %xcc, sparc64_realfault_common
+ mov FAULT_CODE_ITLB, %g4
+
+ .globl sparc64_realfault_common
+sparc64_realfault_common:
+ /* fault code in %g4, fault address in %g5, etrap will
+ * preserve these two values in %l4 and %l5 respectively
+ */
+ ba,pt %xcc, etrap ! Save trap state
+1: rd %pc, %g7 ! ...
+ stb %l4, [%g6 + TI_FAULT_CODE] ! Save fault code
+ stx %l5, [%g6 + TI_FAULT_ADDR] ! Save fault address
+ call do_sparc64_fault ! Call fault handler
+ add %sp, PTREGS_OFF, %o0 ! Compute pt_regs arg
+ ba,pt %xcc, rtrap ! Restore cpu state
+ nop ! Delay slot (fill me)
+
+winfix_trampoline:
+ rdpr %tpc, %g3 ! Prepare winfixup TNPC
+ or %g3, 0x7c, %g3 ! Compute branch offset
+ wrpr %g3, %tnpc ! Write it into TNPC
+ done ! Trap return
+
+ /* Insert an entry into the TSB.
+ *
+ * %o0: TSB entry pointer (virt or phys address)
+ * %o1: tag
+ * %o2: pte
+ */
+ .align 32
+ .globl __tsb_insert
+__tsb_insert:
+ rdpr %pstate, %o5
+ wrpr %o5, PSTATE_IE, %pstate
+ TSB_LOCK_TAG(%o0, %g2, %g3)
+ TSB_WRITE(%o0, %o2, %o1)
+ wrpr %o5, %pstate
+ retl
+ nop
+ .size __tsb_insert, .-__tsb_insert
+
+ /* Flush the given TSB entry if it has the matching
+ * tag.
+ *
+ * %o0: TSB entry pointer (virt or phys address)
+ * %o1: tag
+ */
+ .align 32
+ .globl tsb_flush
+ .type tsb_flush,#function
+tsb_flush:
+ sethi %hi(TSB_TAG_LOCK_HIGH), %g2
+1: TSB_LOAD_TAG(%o0, %g1)
+ srlx %g1, 32, %o3
+ andcc %o3, %g2, %g0
+ bne,pn %icc, 1b
+ nop
+ cmp %g1, %o1
+ mov 1, %o3
+ bne,pt %xcc, 2f
+ sllx %o3, TSB_TAG_INVALID_BIT, %o3
+ TSB_CAS_TAG(%o0, %g1, %o3)
+ cmp %g1, %o3
+ bne,pn %xcc, 1b
+ nop
+2: retl
+ nop
+ .size tsb_flush, .-tsb_flush
+
+ /* Reload MMU related context switch state at
+ * schedule() time.
+ *
+ * %o0: page table physical address
+ * %o1: TSB base config pointer
+ * %o2: TSB huge config pointer, or NULL if none
+ * %o3: Hypervisor TSB descriptor physical address
+ *
+ * We have to run this whole thing with interrupts
+ * disabled so that the current cpu doesn't change
+ * due to preemption.
+ */
+ .align 32
+ .globl __tsb_context_switch
+ .type __tsb_context_switch,#function
+__tsb_context_switch:
+ rdpr %pstate, %g1
+ wrpr %g1, PSTATE_IE, %pstate
+
+ TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
+
+ stx %o0, [%g2 + TRAP_PER_CPU_PGD_PADDR]
+
+ ldx [%o1 + TSB_CONFIG_REG_VAL], %o0
+ brz,pt %o2, 1f
+ mov -1, %g3
+
+ ldx [%o2 + TSB_CONFIG_REG_VAL], %g3
+
+1: stx %g3, [%g2 + TRAP_PER_CPU_TSB_HUGE]
+
+ sethi %hi(tlb_type), %g2
+ lduw [%g2 + %lo(tlb_type)], %g2
+ cmp %g2, 3
+ bne,pt %icc, 50f
+ nop
+
+ /* Hypervisor TSB switch. */
+ mov SCRATCHPAD_UTSBREG1, %o5
+ stxa %o0, [%o5] ASI_SCRATCHPAD
+ mov SCRATCHPAD_UTSBREG2, %o5
+ stxa %g3, [%o5] ASI_SCRATCHPAD
+
+ mov 2, %o0
+ cmp %g3, -1
+ move %xcc, 1, %o0
+
+ mov HV_FAST_MMU_TSB_CTXNON0, %o5
+ mov %o3, %o1
+ ta HV_FAST_TRAP
+
+ /* Finish up. */
+ ba,pt %xcc, 9f
+ nop
+
+ /* SUN4U TSB switch. */
+50: mov TSB_REG, %o5
+ stxa %o0, [%o5] ASI_DMMU
+ membar #Sync
+ stxa %o0, [%o5] ASI_IMMU
+ membar #Sync
+
+2: ldx [%o1 + TSB_CONFIG_MAP_VADDR], %o4
+ brz %o4, 9f
+ ldx [%o1 + TSB_CONFIG_MAP_PTE], %o5
+
+ sethi %hi(sparc64_highest_unlocked_tlb_ent), %g2
+ mov TLB_TAG_ACCESS, %g3
+ lduw [%g2 + %lo(sparc64_highest_unlocked_tlb_ent)], %g2
+ stxa %o4, [%g3] ASI_DMMU
+ membar #Sync
+ sllx %g2, 3, %g2
+ stxa %o5, [%g2] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+ brz,pt %o2, 9f
+ nop
+
+ ldx [%o2 + TSB_CONFIG_MAP_VADDR], %o4
+ ldx [%o2 + TSB_CONFIG_MAP_PTE], %o5
+ mov TLB_TAG_ACCESS, %g3
+ stxa %o4, [%g3] ASI_DMMU
+ membar #Sync
+ sub %g2, (1 << 3), %g2
+ stxa %o5, [%g2] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+
+9:
+ wrpr %g1, %pstate
+
+ retl
+ nop
+ .size __tsb_context_switch, .-__tsb_context_switch
+
+#define TSB_PASS_BITS ((1 << TSB_TAG_LOCK_BIT) | \
+ (1 << TSB_TAG_INVALID_BIT))
+
+ .align 32
+ .globl copy_tsb
+ .type copy_tsb,#function
+copy_tsb: /* %o0=old_tsb_base, %o1=old_tsb_size
+ * %o2=new_tsb_base, %o3=new_tsb_size
+ */
+ sethi %uhi(TSB_PASS_BITS), %g7
+ srlx %o3, 4, %o3
+ add %o0, %o1, %g1 /* end of old tsb */
+ sllx %g7, 32, %g7
+ sub %o3, 1, %o3 /* %o3 == new tsb hash mask */
+
+661: prefetcha [%o0] ASI_N, #one_read
+ .section .tsb_phys_patch, "ax"
+ .word 661b
+ prefetcha [%o0] ASI_PHYS_USE_EC, #one_read
+ .previous
+
+90: andcc %o0, (64 - 1), %g0
+ bne 1f
+ add %o0, 64, %o5
+
+661: prefetcha [%o5] ASI_N, #one_read
+ .section .tsb_phys_patch, "ax"
+ .word 661b
+ prefetcha [%o5] ASI_PHYS_USE_EC, #one_read
+ .previous
+
+1: TSB_LOAD_QUAD(%o0, %g2) /* %g2/%g3 == TSB entry */
+ andcc %g2, %g7, %g0 /* LOCK or INVALID set? */
+ bne,pn %xcc, 80f /* Skip it */
+ sllx %g2, 22, %o4 /* TAG --> VADDR */
+
+ /* This can definitely be computed faster... */
+ srlx %o0, 4, %o5 /* Build index */
+ and %o5, 511, %o5 /* Mask index */
+ sllx %o5, PAGE_SHIFT, %o5 /* Put into vaddr position */
+ or %o4, %o5, %o4 /* Full VADDR. */
+ srlx %o4, PAGE_SHIFT, %o4 /* Shift down to create index */
+ and %o4, %o3, %o4 /* Mask with new_tsb_nents-1 */
+ sllx %o4, 4, %o4 /* Shift back up into tsb ent offset */
+ TSB_STORE(%o2 + %o4, %g2) /* Store TAG */
+ add %o4, 0x8, %o4 /* Advance to TTE */
+ TSB_STORE(%o2 + %o4, %g3) /* Store TTE */
+
+80: add %o0, 16, %o0
+ cmp %o0, %g1
+ bne,pt %xcc, 90b
+ nop
+
+ retl
+ nop
+ .size copy_tsb, .-copy_tsb
+
+ /* Set the invalid bit in all TSB entries. */
+ .align 32
+ .globl tsb_init
+ .type tsb_init,#function
+tsb_init: /* %o0 = TSB vaddr, %o1 = size in bytes */
+ prefetch [%o0 + 0x000], #n_writes
+ mov 1, %g1
+ prefetch [%o0 + 0x040], #n_writes
+ sllx %g1, TSB_TAG_INVALID_BIT, %g1
+ prefetch [%o0 + 0x080], #n_writes
+1: prefetch [%o0 + 0x0c0], #n_writes
+ stx %g1, [%o0 + 0x00]
+ stx %g1, [%o0 + 0x10]
+ stx %g1, [%o0 + 0x20]
+ stx %g1, [%o0 + 0x30]
+ prefetch [%o0 + 0x100], #n_writes
+ stx %g1, [%o0 + 0x40]
+ stx %g1, [%o0 + 0x50]
+ stx %g1, [%o0 + 0x60]
+ stx %g1, [%o0 + 0x70]
+ prefetch [%o0 + 0x140], #n_writes
+ stx %g1, [%o0 + 0x80]
+ stx %g1, [%o0 + 0x90]
+ stx %g1, [%o0 + 0xa0]
+ stx %g1, [%o0 + 0xb0]
+ prefetch [%o0 + 0x180], #n_writes
+ stx %g1, [%o0 + 0xc0]
+ stx %g1, [%o0 + 0xd0]
+ stx %g1, [%o0 + 0xe0]
+ stx %g1, [%o0 + 0xf0]
+ subcc %o1, 0x100, %o1
+ bne,pt %xcc, 1b
+ add %o0, 0x100, %o0
+ retl
+ nop
+ nop
+ nop
+ .size tsb_init, .-tsb_init
+
+ .globl NGtsb_init
+ .type NGtsb_init,#function
+NGtsb_init:
+ rd %asi, %g2
+ mov 1, %g1
+ wr %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
+ sllx %g1, TSB_TAG_INVALID_BIT, %g1
+1: stxa %g1, [%o0 + 0x00] %asi
+ stxa %g1, [%o0 + 0x10] %asi
+ stxa %g1, [%o0 + 0x20] %asi
+ stxa %g1, [%o0 + 0x30] %asi
+ stxa %g1, [%o0 + 0x40] %asi
+ stxa %g1, [%o0 + 0x50] %asi
+ stxa %g1, [%o0 + 0x60] %asi
+ stxa %g1, [%o0 + 0x70] %asi
+ stxa %g1, [%o0 + 0x80] %asi
+ stxa %g1, [%o0 + 0x90] %asi
+ stxa %g1, [%o0 + 0xa0] %asi
+ stxa %g1, [%o0 + 0xb0] %asi
+ stxa %g1, [%o0 + 0xc0] %asi
+ stxa %g1, [%o0 + 0xd0] %asi
+ stxa %g1, [%o0 + 0xe0] %asi
+ stxa %g1, [%o0 + 0xf0] %asi
+ subcc %o1, 0x100, %o1
+ bne,pt %xcc, 1b
+ add %o0, 0x100, %o0
+ membar #Sync
+ retl
+ wr %g2, 0x0, %asi
+ .size NGtsb_init, .-NGtsb_init
diff --git a/arch/sparc/kernel/ttable.S b/arch/sparc/kernel/ttable.S
new file mode 100644
index 000000000000..ea925503b42e
--- /dev/null
+++ b/arch/sparc/kernel/ttable.S
@@ -0,0 +1,266 @@
+/* ttable.S: Sparc V9 Trap Table(s) with SpitFire/Cheetah/SUN4V extensions.
+ *
+ * Copyright (C) 1996, 2001, 2006 David S. Miller (davem@davemloft.net)
+ */
+
+
+ .globl sparc64_ttable_tl0, sparc64_ttable_tl1
+ .globl tl0_icpe, tl1_icpe
+ .globl tl0_dcpe, tl1_dcpe
+ .globl tl0_fecc, tl1_fecc
+ .globl tl0_cee, tl1_cee
+ .globl tl0_iae, tl1_iae
+ .globl tl0_dae, tl1_dae
+
+sparc64_ttable_tl0:
+tl0_resv000: BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
+tl0_resv004: BTRAP(0x4) BTRAP(0x5) BTRAP(0x6) BTRAP(0x7)
+tl0_iax: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_insn_access_exception)
+tl0_itsb_4v: SUN4V_ITSB_MISS
+tl0_iae: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_access_error)
+tl0_resv00b: BTRAP(0xb) BTRAP(0xc) BTRAP(0xd) BTRAP(0xe) BTRAP(0xf)
+tl0_ill: membar #Sync
+ TRAP_7INSNS(do_illegal_instruction)
+tl0_privop: TRAP(do_privop)
+tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17)
+tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d)
+tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f)
+tl0_fpdis: TRAP_NOSAVE(do_fpdis)
+tl0_fpieee: TRAP_SAVEFPU(do_fpieee)
+tl0_fpother: TRAP_NOSAVE(do_fpother_check_fitos)
+tl0_tof: TRAP(do_tof)
+tl0_cwin: CLEAN_WINDOW
+tl0_div0: TRAP(do_div0)
+tl0_resv029: BTRAP(0x29) BTRAP(0x2a) BTRAP(0x2b) BTRAP(0x2c) BTRAP(0x2d) BTRAP(0x2e)
+tl0_resv02f: BTRAP(0x2f)
+tl0_dax: TRAP_NOSAVE(__spitfire_data_access_exception)
+tl0_dtsb_4v: SUN4V_DTSB_MISS
+tl0_dae: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_access_error)
+tl0_resv033: BTRAP(0x33)
+tl0_mna: TRAP_NOSAVE(do_mna)
+tl0_lddfmna: TRAP_NOSAVE(do_lddfmna)
+tl0_stdfmna: TRAP_NOSAVE(do_stdfmna)
+tl0_privact: TRAP_NOSAVE(__do_privact)
+tl0_resv038: BTRAP(0x38) BTRAP(0x39) BTRAP(0x3a) BTRAP(0x3b) BTRAP(0x3c) BTRAP(0x3d)
+tl0_resv03e: BTRAP(0x3e) BTRAP(0x3f) BTRAP(0x40)
+#ifdef CONFIG_SMP
+tl0_irq1: TRAP_IRQ(smp_call_function_client, 1)
+tl0_irq2: TRAP_IRQ(smp_receive_signal_client, 2)
+tl0_irq3: TRAP_IRQ(smp_penguin_jailcell, 3)
+tl0_irq4: TRAP_IRQ(smp_new_mmu_context_version_client, 4)
+#else
+tl0_irq1: BTRAP(0x41)
+tl0_irq2: BTRAP(0x42)
+tl0_irq3: BTRAP(0x43)
+tl0_irq4: BTRAP(0x44)
+#endif
+tl0_irq5: TRAP_IRQ(handler_irq, 5)
+#ifdef CONFIG_SMP
+tl0_irq6: TRAP_IRQ(smp_call_function_single_client, 6)
+#else
+tl0_irq6: BTRAP(0x46)
+#endif
+tl0_irq7: BTRAP(0x47) BTRAP(0x48) BTRAP(0x49)
+tl0_irq10: BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d)
+tl0_irq14: TRAP_IRQ(timer_interrupt, 14)
+tl0_irq15: TRAP_NMI_IRQ(perfctr_irq, 15)
+tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
+tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
+tl0_resv05c: BTRAP(0x5c) BTRAP(0x5d) BTRAP(0x5e) BTRAP(0x5f)
+tl0_ivec: TRAP_IVEC
+tl0_paw: TRAP(do_paw)
+tl0_vaw: TRAP(do_vaw)
+tl0_cee: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_cee_trap)
+tl0_iamiss:
+#include "itlb_miss.S"
+tl0_damiss:
+#include "dtlb_miss.S"
+tl0_daprot:
+#include "dtlb_prot.S"
+tl0_fecc: BTRAP(0x70) /* Fast-ECC on Cheetah */
+tl0_dcpe: BTRAP(0x71) /* D-cache Parity Error on Cheetah+ */
+tl0_icpe: BTRAP(0x72) /* I-cache Parity Error on Cheetah+ */
+tl0_resv073: BTRAP(0x73) BTRAP(0x74) BTRAP(0x75)
+tl0_resv076: BTRAP(0x76) BTRAP(0x77) BTRAP(0x78) BTRAP(0x79) BTRAP(0x7a) BTRAP(0x7b)
+tl0_cpu_mondo: TRAP_NOSAVE(sun4v_cpu_mondo)
+tl0_dev_mondo: TRAP_NOSAVE(sun4v_dev_mondo)
+tl0_res_mondo: TRAP_NOSAVE(sun4v_res_mondo)
+tl0_nres_mondo: TRAP_NOSAVE(sun4v_nonres_mondo)
+tl0_s0n: SPILL_0_NORMAL
+tl0_s1n: SPILL_1_NORMAL
+tl0_s2n: SPILL_2_NORMAL
+tl0_s3n: SPILL_0_NORMAL_ETRAP
+tl0_s4n: SPILL_1_GENERIC_ETRAP
+tl0_s5n: SPILL_1_GENERIC_ETRAP_FIXUP
+tl0_s6n: SPILL_2_GENERIC_ETRAP
+tl0_s7n: SPILL_2_GENERIC_ETRAP_FIXUP
+tl0_s0o: SPILL_0_OTHER
+tl0_s1o: SPILL_1_OTHER
+tl0_s2o: SPILL_2_OTHER
+tl0_s3o: SPILL_3_OTHER
+tl0_s4o: SPILL_4_OTHER
+tl0_s5o: SPILL_5_OTHER
+tl0_s6o: SPILL_6_OTHER
+tl0_s7o: SPILL_7_OTHER
+tl0_f0n: FILL_0_NORMAL
+tl0_f1n: FILL_1_NORMAL
+tl0_f2n: FILL_2_NORMAL
+tl0_f3n: FILL_3_NORMAL
+tl0_f4n: FILL_4_NORMAL
+tl0_f5n: FILL_0_NORMAL_RTRAP
+tl0_f6n: FILL_1_GENERIC_RTRAP
+tl0_f7n: FILL_2_GENERIC_RTRAP
+tl0_f0o: FILL_0_OTHER
+tl0_f1o: FILL_1_OTHER
+tl0_f2o: FILL_2_OTHER
+tl0_f3o: FILL_3_OTHER
+tl0_f4o: FILL_4_OTHER
+tl0_f5o: FILL_5_OTHER
+tl0_f6o: FILL_6_OTHER
+tl0_f7o: FILL_7_OTHER
+tl0_resv100: BTRAP(0x100)
+tl0_bkpt: BREAKPOINT_TRAP
+tl0_divz: TRAP(do_div0)
+tl0_flushw: FLUSH_WINDOW_TRAP
+tl0_resv104: BTRAP(0x104) BTRAP(0x105) BTRAP(0x106) BTRAP(0x107) BTRAP(0x108)
+tl0_resv109: BTRAP(0x109) BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d)
+tl0_resv10e: BTRAP(0x10e) BTRAP(0x10f)
+tl0_linux32: LINUX_32BIT_SYSCALL_TRAP
+tl0_oldlinux64: LINUX_64BIT_SYSCALL_TRAP
+tl0_resv112: TRAP_UTRAP(UT_TRAP_INSTRUCTION_18,0x112) TRAP_UTRAP(UT_TRAP_INSTRUCTION_19,0x113)
+tl0_resv114: TRAP_UTRAP(UT_TRAP_INSTRUCTION_20,0x114) TRAP_UTRAP(UT_TRAP_INSTRUCTION_21,0x115)
+tl0_resv116: TRAP_UTRAP(UT_TRAP_INSTRUCTION_22,0x116) TRAP_UTRAP(UT_TRAP_INSTRUCTION_23,0x117)
+tl0_resv118: TRAP_UTRAP(UT_TRAP_INSTRUCTION_24,0x118) TRAP_UTRAP(UT_TRAP_INSTRUCTION_25,0x119)
+tl0_resv11a: TRAP_UTRAP(UT_TRAP_INSTRUCTION_26,0x11a) TRAP_UTRAP(UT_TRAP_INSTRUCTION_27,0x11b)
+tl0_resv11c: TRAP_UTRAP(UT_TRAP_INSTRUCTION_28,0x11c) TRAP_UTRAP(UT_TRAP_INSTRUCTION_29,0x11d)
+tl0_resv11e: TRAP_UTRAP(UT_TRAP_INSTRUCTION_30,0x11e) TRAP_UTRAP(UT_TRAP_INSTRUCTION_31,0x11f)
+tl0_getcc: GETCC_TRAP
+tl0_setcc: SETCC_TRAP
+tl0_getpsr: TRAP(do_getpsr)
+tl0_resv123: BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126) BTRAP(0x127)
+tl0_resv128: BTRAP(0x128) BTRAP(0x129) BTRAP(0x12a) BTRAP(0x12b) BTRAP(0x12c)
+tl0_resv12d: BTRAP(0x12d) BTRAP(0x12e) BTRAP(0x12f) BTRAP(0x130) BTRAP(0x131)
+tl0_resv132: BTRAP(0x132) BTRAP(0x133) BTRAP(0x134) BTRAP(0x135) BTRAP(0x136)
+tl0_resv137: BTRAP(0x137) BTRAP(0x138) BTRAP(0x139) BTRAP(0x13a) BTRAP(0x13b)
+tl0_resv13c: BTRAP(0x13c) BTRAP(0x13d) BTRAP(0x13e) BTRAP(0x13f) BTRAP(0x140)
+tl0_resv141: BTRAP(0x141) BTRAP(0x142) BTRAP(0x143) BTRAP(0x144) BTRAP(0x145)
+tl0_resv146: BTRAP(0x146) BTRAP(0x147) BTRAP(0x148) BTRAP(0x149) BTRAP(0x14a)
+tl0_resv14b: BTRAP(0x14b) BTRAP(0x14c) BTRAP(0x14d) BTRAP(0x14e) BTRAP(0x14f)
+tl0_resv150: BTRAP(0x150) BTRAP(0x151) BTRAP(0x152) BTRAP(0x153) BTRAP(0x154)
+tl0_resv155: BTRAP(0x155) BTRAP(0x156) BTRAP(0x157) BTRAP(0x158) BTRAP(0x159)
+tl0_resv15a: BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e)
+tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163)
+tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168)
+tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c)
+tl0_linux64: LINUX_64BIT_SYSCALL_TRAP
+tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context)
+tl0_resv170: KPROBES_TRAP(0x170) KPROBES_TRAP(0x171) KGDB_TRAP(0x172)
+tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177)
+tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c)
+tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f)
+#define BTRAPS(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) BTRAP(x+4) BTRAP(x+5) BTRAP(x+6) BTRAP(x+7)
+tl0_resv180: BTRAPS(0x180) BTRAPS(0x188)
+tl0_resv190: BTRAPS(0x190) BTRAPS(0x198)
+tl0_resv1a0: BTRAPS(0x1a0) BTRAPS(0x1a8)
+tl0_resv1b0: BTRAPS(0x1b0) BTRAPS(0x1b8)
+tl0_resv1c0: BTRAPS(0x1c0) BTRAPS(0x1c8)
+tl0_resv1d0: BTRAPS(0x1d0) BTRAPS(0x1d8)
+tl0_resv1e0: BTRAPS(0x1e0) BTRAPS(0x1e8)
+tl0_resv1f0: BTRAPS(0x1f0) BTRAPS(0x1f8)
+
+sparc64_ttable_tl1:
+tl1_resv000: BOOT_KERNEL BTRAPTL1(0x1) BTRAPTL1(0x2) BTRAPTL1(0x3)
+tl1_resv004: BTRAPTL1(0x4) BTRAPTL1(0x5) BTRAPTL1(0x6) BTRAPTL1(0x7)
+tl1_iax: TRAP_NOSAVE(__spitfire_insn_access_exception_tl1)
+tl1_itsb_4v: SUN4V_ITSB_MISS
+tl1_iae: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_access_error)
+tl1_resv00b: BTRAPTL1(0xb) BTRAPTL1(0xc) BTRAPTL1(0xd) BTRAPTL1(0xe) BTRAPTL1(0xf)
+tl1_ill: TRAPTL1(do_ill_tl1)
+tl1_privop: BTRAPTL1(0x11)
+tl1_resv012: BTRAPTL1(0x12) BTRAPTL1(0x13) BTRAPTL1(0x14) BTRAPTL1(0x15)
+tl1_resv016: BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19)
+tl1_resv01a: BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d)
+tl1_resv01e: BTRAPTL1(0x1e) BTRAPTL1(0x1f)
+tl1_fpdis: TRAP_NOSAVE(do_fpdis)
+tl1_fpieee: TRAPTL1(do_fpieee_tl1)
+tl1_fpother: TRAPTL1(do_fpother_tl1)
+tl1_tof: TRAPTL1(do_tof_tl1)
+tl1_cwin: CLEAN_WINDOW
+tl1_div0: TRAPTL1(do_div0_tl1)
+tl1_resv029: BTRAPTL1(0x29) BTRAPTL1(0x2a) BTRAPTL1(0x2b) BTRAPTL1(0x2c)
+tl1_resv02d: BTRAPTL1(0x2d) BTRAPTL1(0x2e) BTRAPTL1(0x2f)
+tl1_dax: TRAP_NOSAVE(__spitfire_data_access_exception_tl1)
+tl1_dtsb_4v: SUN4V_DTSB_MISS
+tl1_dae: membar #Sync
+ TRAP_NOSAVE_7INSNS(__spitfire_access_error)
+tl1_resv033: BTRAPTL1(0x33)
+tl1_mna: TRAP_NOSAVE(do_mna)
+tl1_lddfmna: TRAPTL1(do_lddfmna_tl1)
+tl1_stdfmna: TRAPTL1(do_stdfmna_tl1)
+tl1_privact: BTRAPTL1(0x37)
+tl1_resv038: BTRAPTL1(0x38) BTRAPTL1(0x39) BTRAPTL1(0x3a) BTRAPTL1(0x3b)
+tl1_resv03c: BTRAPTL1(0x3c) BTRAPTL1(0x3d) BTRAPTL1(0x3e) BTRAPTL1(0x3f)
+tl1_resv040: BTRAPTL1(0x40)
+tl1_irq1: TRAP_IRQ(do_irq_tl1, 1) TRAP_IRQ(do_irq_tl1, 2) TRAP_IRQ(do_irq_tl1, 3)
+tl1_irq4: TRAP_IRQ(do_irq_tl1, 4) TRAP_IRQ(do_irq_tl1, 5) TRAP_IRQ(do_irq_tl1, 6)
+tl1_irq7: TRAP_IRQ(do_irq_tl1, 7) TRAP_IRQ(do_irq_tl1, 8) TRAP_IRQ(do_irq_tl1, 9)
+tl1_irq10: TRAP_IRQ(do_irq_tl1, 10) TRAP_IRQ(do_irq_tl1, 11)
+tl1_irq12: TRAP_IRQ(do_irq_tl1, 12) TRAP_IRQ(do_irq_tl1, 13)
+tl1_irq14: TRAP_IRQ(do_irq_tl1, 14) TRAP_IRQ(do_irq_tl1, 15)
+tl1_resv050: BTRAPTL1(0x50) BTRAPTL1(0x51) BTRAPTL1(0x52) BTRAPTL1(0x53)
+tl1_resv054: BTRAPTL1(0x54) BTRAPTL1(0x55) BTRAPTL1(0x56) BTRAPTL1(0x57)
+tl1_resv058: BTRAPTL1(0x58) BTRAPTL1(0x59) BTRAPTL1(0x5a) BTRAPTL1(0x5b)
+tl1_resv05c: BTRAPTL1(0x5c) BTRAPTL1(0x5d) BTRAPTL1(0x5e) BTRAPTL1(0x5f)
+tl1_ivec: TRAP_IVEC
+tl1_paw: TRAPTL1(do_paw_tl1)
+tl1_vaw: TRAPTL1(do_vaw_tl1)
+tl1_cee: BTRAPTL1(0x63)
+tl1_iamiss: BTRAPTL1(0x64) BTRAPTL1(0x65) BTRAPTL1(0x66) BTRAPTL1(0x67)
+tl1_damiss:
+#include "dtlb_miss.S"
+tl1_daprot:
+#include "dtlb_prot.S"
+tl1_fecc: BTRAPTL1(0x70) /* Fast-ECC on Cheetah */
+tl1_dcpe: BTRAPTL1(0x71) /* D-cache Parity Error on Cheetah+ */
+tl1_icpe: BTRAPTL1(0x72) /* I-cache Parity Error on Cheetah+ */
+tl1_resv073: BTRAPTL1(0x73)
+tl1_resv074: BTRAPTL1(0x74) BTRAPTL1(0x75) BTRAPTL1(0x76) BTRAPTL1(0x77)
+tl1_resv078: BTRAPTL1(0x78) BTRAPTL1(0x79) BTRAPTL1(0x7a) BTRAPTL1(0x7b)
+tl1_resv07c: BTRAPTL1(0x7c) BTRAPTL1(0x7d) BTRAPTL1(0x7e) BTRAPTL1(0x7f)
+tl1_s0n: SPILL_0_NORMAL
+tl1_s1n: SPILL_1_NORMAL
+tl1_s2n: SPILL_2_NORMAL
+tl1_s3n: SPILL_3_NORMAL
+tl1_s4n: SPILL_4_NORMAL
+tl1_s5n: SPILL_5_NORMAL
+tl1_s6n: SPILL_6_NORMAL
+tl1_s7n: SPILL_7_NORMAL
+tl1_s0o: SPILL_0_OTHER
+tl1_s1o: SPILL_1_OTHER
+tl1_s2o: SPILL_2_OTHER
+tl1_s3o: SPILL_3_OTHER
+tl1_s4o: SPILL_4_OTHER
+tl1_s5o: SPILL_5_OTHER
+tl1_s6o: SPILL_6_OTHER
+tl1_s7o: SPILL_7_OTHER
+tl1_f0n: FILL_0_NORMAL
+tl1_f1n: FILL_1_NORMAL
+tl1_f2n: FILL_2_NORMAL
+tl1_f3n: FILL_3_NORMAL
+tl1_f4n: FILL_4_NORMAL
+tl1_f5n: FILL_5_NORMAL
+tl1_f6n: FILL_6_NORMAL
+tl1_f7n: FILL_7_NORMAL
+tl1_f0o: FILL_0_OTHER
+tl1_f1o: FILL_1_OTHER
+tl1_f2o: FILL_2_OTHER
+tl1_f3o: FILL_3_OTHER
+tl1_f4o: FILL_4_OTHER
+tl1_f5o: FILL_5_OTHER
+tl1_f6o: FILL_6_OTHER
+tl1_f7o: FILL_7_OTHER
diff --git a/arch/sparc/kernel/una_asm.S b/arch/sparc/kernel/una_asm_32.S
index 8cc03458eb7e..8cc03458eb7e 100644
--- a/arch/sparc/kernel/una_asm.S
+++ b/arch/sparc/kernel/una_asm_32.S
diff --git a/arch/sparc64/kernel/una_asm.S b/arch/sparc/kernel/una_asm_64.S
index be183fe41443..be183fe41443 100644
--- a/arch/sparc64/kernel/una_asm.S
+++ b/arch/sparc/kernel/una_asm_64.S
diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned_32.c
index c2a28c5ad650..c2a28c5ad650 100644
--- a/arch/sparc/kernel/unaligned.c
+++ b/arch/sparc/kernel/unaligned_32.c
diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc/kernel/unaligned_64.c
index 203ddfad9f27..203ddfad9f27 100644
--- a/arch/sparc64/kernel/unaligned.c
+++ b/arch/sparc/kernel/unaligned_64.c
diff --git a/arch/sparc64/kernel/us2e_cpufreq.c b/arch/sparc/kernel/us2e_cpufreq.c
index 791c15138f3a..791c15138f3a 100644
--- a/arch/sparc64/kernel/us2e_cpufreq.c
+++ b/arch/sparc/kernel/us2e_cpufreq.c
diff --git a/arch/sparc64/kernel/us3_cpufreq.c b/arch/sparc/kernel/us3_cpufreq.c
index 365b6464e2ce..365b6464e2ce 100644
--- a/arch/sparc64/kernel/us3_cpufreq.c
+++ b/arch/sparc/kernel/us3_cpufreq.c
diff --git a/arch/sparc64/kernel/utrap.S b/arch/sparc/kernel/utrap.S
index b7f0f3f3a909..b7f0f3f3a909 100644
--- a/arch/sparc64/kernel/utrap.S
+++ b/arch/sparc/kernel/utrap.S
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc/kernel/vio.c
index 92b1f8ec01de..92b1f8ec01de 100644
--- a/arch/sparc64/kernel/vio.c
+++ b/arch/sparc/kernel/vio.c
diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc/kernel/viohs.c
index 708fa1705fbe..708fa1705fbe 100644
--- a/arch/sparc64/kernel/viohs.c
+++ b/arch/sparc/kernel/viohs.c
diff --git a/arch/sparc64/kernel/visemul.c b/arch/sparc/kernel/visemul.c
index b956fd71c131..b956fd71c131 100644
--- a/arch/sparc64/kernel/visemul.c
+++ b/arch/sparc/kernel/visemul.c
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
index 5b7e69a8c32f..76267085b13b 100644
--- a/arch/sparc/kernel/vmlinux.lds.S
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -1,27 +1,56 @@
-/* ld script to make SparcLinux kernel */
+/* ld script for sparc32/sparc64 kernel */
#include <asm-generic/vmlinux.lds.h>
+
#include <asm/page.h>
+#include <asm/thread_info.h>
+
+#ifdef CONFIG_SPARC32
+#define INITIAL_ADDRESS 0x10000 + SIZEOF_HEADERS
+#define TEXTSTART 0xf0004000
+
+#define SMP_CACHE_BYTES_SHIFT 5
+
+#else
+#define SMP_CACHE_BYTES_SHIFT 6
+#define INITIAL_ADDRESS 0x4000
+#define TEXTSTART 0x0000000000404000
+
+#endif
+
+#define SMP_CACHE_BYTES (1 << SMP_CACHE_BYTES_SHIFT)
+#ifdef CONFIG_SPARC32
OUTPUT_FORMAT("elf32-sparc", "elf32-sparc", "elf32-sparc")
OUTPUT_ARCH(sparc)
ENTRY(_start)
jiffies = jiffies_64 + 4;
+#else
+/* sparc64 */
+OUTPUT_FORMAT("elf64-sparc", "elf64-sparc", "elf64-sparc")
+OUTPUT_ARCH(sparc:v9a)
+ENTRY(_start)
+jiffies = jiffies_64;
+#endif
+
SECTIONS
{
- . = 0x10000 + SIZEOF_HEADERS;
- .text 0xf0004000 :
+ /* swapper_low_pmd_dir is sparc64 only */
+ swapper_low_pmd_dir = 0x0000000000402000;
+ . = INITIAL_ADDRESS;
+ .text TEXTSTART :
{
_text = .;
*(.text.head)
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
+ KPROBES_TEXT
*(.gnu.warning)
} = 0
_etext = .;
- PROVIDE (etext = .);
- RODATA
+
+ RO_DATA(PAGE_SIZE)
.data : {
DATA_DATA
CONSTRUCTORS
@@ -29,25 +58,38 @@ SECTIONS
.data1 : {
*(.data1)
}
+ . = ALIGN(SMP_CACHE_BYTES);
+ .data.cacheline_aligned : {
+ *(.data.cacheline_aligned)
+ }
+ . = ALIGN(SMP_CACHE_BYTES);
+ .data.read_mostly : {
+ *(.data.read_mostly)
+ }
+ /* End of data section */
_edata = .;
- PROVIDE (edata = .);
+ /* init_task */
+ . = ALIGN(THREAD_SIZE);
+ .data.init_task : {
+ *(.data.init_task)
+ }
.fixup : {
__start___fixup = .;
*(.fixup)
__stop___fixup = .;
}
+ . = ALIGN(16);
__ex_table : {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
}
-
NOTES
. = ALIGN(PAGE_SIZE);
- __init_begin = .;
.init.text : {
+ __init_begin = .;
_sinittext = .;
INIT_TEXT
_einittext = .;
@@ -65,7 +107,7 @@ SECTIONS
.initcall.init : {
__initcall_start = .;
INITCALLS
- __initcall_end = .;
+ __initcall_end = .;
}
.con_initcall.init : {
__con_initcall_start = .;
@@ -74,38 +116,61 @@ SECTIONS
}
SECURITY_INIT
+ . = ALIGN(4);
+ .tsb_ldquad_phys_patch : {
+ __tsb_ldquad_phys_patch = .;
+ *(.tsb_ldquad_phys_patch)
+ __tsb_ldquad_phys_patch_end = .;
+ }
+
+ .tsb_phys_patch : {
+ __tsb_phys_patch = .;
+ *(.tsb_phys_patch)
+ __tsb_phys_patch_end = .;
+ }
+
+ .cpuid_patch : {
+ __cpuid_patch = .;
+ *(.cpuid_patch)
+ __cpuid_patch_end = .;
+ }
+
+ .sun4v_1insn_patch : {
+ __sun4v_1insn_patch = .;
+ *(.sun4v_1insn_patch)
+ __sun4v_1insn_patch_end = .;
+ }
+ .sun4v_2insn_patch : {
+ __sun4v_2insn_patch = .;
+ *(.sun4v_2insn_patch)
+ __sun4v_2insn_patch_end = .;
+ }
+
#ifdef CONFIG_BLK_DEV_INITRD
. = ALIGN(PAGE_SIZE);
.init.ramfs : {
- __initramfs_start = .;
+ __initramfs_start = .;
*(.init.ramfs)
- __initramfs_end = .;
+ __initramfs_end = .;
}
#endif
PERCPU(PAGE_SIZE)
+
. = ALIGN(PAGE_SIZE);
__init_end = .;
- . = ALIGN(32);
- .data.cacheline_aligned : {
- *(.data.cacheline_aligned)
- }
- . = ALIGN(32);
- .data.read_mostly : {
- *(.data.read_mostly)
- }
-
__bss_start = .;
.sbss : {
*(.sbss)
- *(.scommon) }
+ *(.scommon)
+ }
.bss : {
*(.dynbss)
*(.bss)
*(COMMON)
}
_end = . ;
- PROVIDE (end = .);
+
/DISCARD/ : {
EXIT_TEXT
EXIT_DATA
diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc/kernel/winfixup.S
index a6b0863c27df..a6b0863c27df 100644
--- a/arch/sparc64/kernel/winfixup.S
+++ b/arch/sparc/kernel/winfixup.S
diff --git a/arch/sparc64/lib/GENbzero.S b/arch/sparc/lib/GENbzero.S
index 6a4f956a2f7a..6a4f956a2f7a 100644
--- a/arch/sparc64/lib/GENbzero.S
+++ b/arch/sparc/lib/GENbzero.S
diff --git a/arch/sparc64/lib/GENcopy_from_user.S b/arch/sparc/lib/GENcopy_from_user.S
index 2b9df99e87f9..2b9df99e87f9 100644
--- a/arch/sparc64/lib/GENcopy_from_user.S
+++ b/arch/sparc/lib/GENcopy_from_user.S
diff --git a/arch/sparc64/lib/GENcopy_to_user.S b/arch/sparc/lib/GENcopy_to_user.S
index bb3f7084daf9..bb3f7084daf9 100644
--- a/arch/sparc64/lib/GENcopy_to_user.S
+++ b/arch/sparc/lib/GENcopy_to_user.S
diff --git a/arch/sparc64/lib/GENmemcpy.S b/arch/sparc/lib/GENmemcpy.S
index 89358ee94851..89358ee94851 100644
--- a/arch/sparc64/lib/GENmemcpy.S
+++ b/arch/sparc/lib/GENmemcpy.S
diff --git a/arch/sparc64/lib/GENpage.S b/arch/sparc/lib/GENpage.S
index 2ef9d05f21bc..2ef9d05f21bc 100644
--- a/arch/sparc64/lib/GENpage.S
+++ b/arch/sparc/lib/GENpage.S
diff --git a/arch/sparc64/lib/GENpatch.S b/arch/sparc/lib/GENpatch.S
index fab9e89f16bd..fab9e89f16bd 100644
--- a/arch/sparc64/lib/GENpatch.S
+++ b/arch/sparc/lib/GENpatch.S
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index 6e303e10c3b9..375016e19144 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -1,13 +1,44 @@
# Makefile for Sparc library files..
#
-EXTRA_AFLAGS := -ansi -DST_DIV0=0x02
+asflags-y := -ansi -DST_DIV0=0x02
+ccflags-y := -Werror
-lib-y := mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
- strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
- strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
- copy_user.o locks.o atomic.o \
- lshrdi3.o ashldi3.o rwsem.o muldi3.o bitext.o \
- cmpdi2.o
+lib-$(CONFIG_SPARC32) += mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o
+lib-$(CONFIG_SPARC32) += memcpy.o memset.o
+lib-y += strlen.o
+lib-y += checksum_$(BITS).o
+lib-$(CONFIG_SPARC32) += blockops.o
+lib-y += memscan_$(BITS).o memcmp.o strncmp_$(BITS).o
+lib-y += strncpy_from_user_$(BITS).o strlen_user_$(BITS).o
+lib-$(CONFIG_SPARC32) += divdi3.o udivdi3.o
+lib-$(CONFIG_SPARC32) += copy_user.o locks.o
+lib-y += atomic_$(BITS).o
+lib-$(CONFIG_SPARC32) += lshrdi3.o ashldi3.o
+lib-y += rwsem_$(BITS).o
+lib-$(CONFIG_SPARC32) += muldi3.o bitext.o cmpdi2.o
-obj-y += iomap.o atomic32.o
+lib-$(CONFIG_SPARC64) += PeeCeeI.o copy_page.o clear_page.o bzero.o
+lib-$(CONFIG_SPARC64) += csum_copy.o csum_copy_from_user.o csum_copy_to_user.o
+lib-$(CONFIG_SPARC64) += VISsave.o
+lib-$(CONFIG_SPARC64) += bitops.o
+
+lib-$(CONFIG_SPARC64) += U1memcpy.o U1copy_from_user.o U1copy_to_user.o
+
+lib-$(CONFIG_SPARC64) += U3memcpy.o U3copy_from_user.o U3copy_to_user.o
+lib-$(CONFIG_SPARC64) += U3patch.o
+
+lib-$(CONFIG_SPARC64) += NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o
+lib-$(CONFIG_SPARC64) += NGpatch.o NGpage.o NGbzero.o
+
+lib-$(CONFIG_SPARC64) += NG2memcpy.o NG2copy_from_user.o NG2copy_to_user.o
+lib-$(CONFIG_SPARC64) += NG2patch.o NG2page.o
+
+lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o
+lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o
+
+lib-$(CONFIG_SPARC64) += copy_in_user.o user_fixup.o memmove.o
+lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o
+
+obj-y += iomap.o
+obj-$(CONFIG_SPARC32) += atomic32.o
diff --git a/arch/sparc64/lib/NG2copy_from_user.S b/arch/sparc/lib/NG2copy_from_user.S
index c77ef5f22102..c77ef5f22102 100644
--- a/arch/sparc64/lib/NG2copy_from_user.S
+++ b/arch/sparc/lib/NG2copy_from_user.S
diff --git a/arch/sparc64/lib/NG2copy_to_user.S b/arch/sparc/lib/NG2copy_to_user.S
index 4bd4093acbbd..4bd4093acbbd 100644
--- a/arch/sparc64/lib/NG2copy_to_user.S
+++ b/arch/sparc/lib/NG2copy_to_user.S
diff --git a/arch/sparc64/lib/NG2memcpy.S b/arch/sparc/lib/NG2memcpy.S
index 0aed75653b50..0aed75653b50 100644
--- a/arch/sparc64/lib/NG2memcpy.S
+++ b/arch/sparc/lib/NG2memcpy.S
diff --git a/arch/sparc64/lib/NG2page.S b/arch/sparc/lib/NG2page.S
index 73b6b7c72cbf..73b6b7c72cbf 100644
--- a/arch/sparc64/lib/NG2page.S
+++ b/arch/sparc/lib/NG2page.S
diff --git a/arch/sparc64/lib/NG2patch.S b/arch/sparc/lib/NG2patch.S
index 28c36f06a6d1..28c36f06a6d1 100644
--- a/arch/sparc64/lib/NG2patch.S
+++ b/arch/sparc/lib/NG2patch.S
diff --git a/arch/sparc64/lib/NGbzero.S b/arch/sparc/lib/NGbzero.S
index 814d5f7a45e1..814d5f7a45e1 100644
--- a/arch/sparc64/lib/NGbzero.S
+++ b/arch/sparc/lib/NGbzero.S
diff --git a/arch/sparc64/lib/NGcopy_from_user.S b/arch/sparc/lib/NGcopy_from_user.S
index e7f433f71b42..e7f433f71b42 100644
--- a/arch/sparc64/lib/NGcopy_from_user.S
+++ b/arch/sparc/lib/NGcopy_from_user.S
diff --git a/arch/sparc64/lib/NGcopy_to_user.S b/arch/sparc/lib/NGcopy_to_user.S
index 6ea01c5532a0..6ea01c5532a0 100644
--- a/arch/sparc64/lib/NGcopy_to_user.S
+++ b/arch/sparc/lib/NGcopy_to_user.S
diff --git a/arch/sparc64/lib/NGmemcpy.S b/arch/sparc/lib/NGmemcpy.S
index 96a14caf6966..96a14caf6966 100644
--- a/arch/sparc64/lib/NGmemcpy.S
+++ b/arch/sparc/lib/NGmemcpy.S
diff --git a/arch/sparc64/lib/NGpage.S b/arch/sparc/lib/NGpage.S
index 428920de05ba..428920de05ba 100644
--- a/arch/sparc64/lib/NGpage.S
+++ b/arch/sparc/lib/NGpage.S
diff --git a/arch/sparc64/lib/NGpatch.S b/arch/sparc/lib/NGpatch.S
index 3b0674fc3366..3b0674fc3366 100644
--- a/arch/sparc64/lib/NGpatch.S
+++ b/arch/sparc/lib/NGpatch.S
diff --git a/arch/sparc64/lib/PeeCeeI.c b/arch/sparc/lib/PeeCeeI.c
index 46053e6ddd7b..46053e6ddd7b 100644
--- a/arch/sparc64/lib/PeeCeeI.c
+++ b/arch/sparc/lib/PeeCeeI.c
diff --git a/arch/sparc64/lib/U1copy_from_user.S b/arch/sparc/lib/U1copy_from_user.S
index 3192b0bf4fab..3192b0bf4fab 100644
--- a/arch/sparc64/lib/U1copy_from_user.S
+++ b/arch/sparc/lib/U1copy_from_user.S
diff --git a/arch/sparc64/lib/U1copy_to_user.S b/arch/sparc/lib/U1copy_to_user.S
index d1210ffb0b82..d1210ffb0b82 100644
--- a/arch/sparc64/lib/U1copy_to_user.S
+++ b/arch/sparc/lib/U1copy_to_user.S
diff --git a/arch/sparc64/lib/U1memcpy.S b/arch/sparc/lib/U1memcpy.S
index bafd2fc07acb..bafd2fc07acb 100644
--- a/arch/sparc64/lib/U1memcpy.S
+++ b/arch/sparc/lib/U1memcpy.S
diff --git a/arch/sparc64/lib/U3copy_from_user.S b/arch/sparc/lib/U3copy_from_user.S
index f5bfc8d9d216..f5bfc8d9d216 100644
--- a/arch/sparc64/lib/U3copy_from_user.S
+++ b/arch/sparc/lib/U3copy_from_user.S
diff --git a/arch/sparc64/lib/U3copy_to_user.S b/arch/sparc/lib/U3copy_to_user.S
index 2334f111bb0c..2334f111bb0c 100644
--- a/arch/sparc64/lib/U3copy_to_user.S
+++ b/arch/sparc/lib/U3copy_to_user.S
diff --git a/arch/sparc64/lib/U3memcpy.S b/arch/sparc/lib/U3memcpy.S
index 7cae9cc6a204..7cae9cc6a204 100644
--- a/arch/sparc64/lib/U3memcpy.S
+++ b/arch/sparc/lib/U3memcpy.S
diff --git a/arch/sparc64/lib/U3patch.S b/arch/sparc/lib/U3patch.S
index ecc302619a6e..ecc302619a6e 100644
--- a/arch/sparc64/lib/U3patch.S
+++ b/arch/sparc/lib/U3patch.S
diff --git a/arch/sparc64/lib/VISsave.S b/arch/sparc/lib/VISsave.S
index b320ae9e2e2e..b320ae9e2e2e 100644
--- a/arch/sparc64/lib/VISsave.S
+++ b/arch/sparc/lib/VISsave.S
diff --git a/arch/sparc/lib/atomic.S b/arch/sparc/lib/atomic_32.S
index 178cbb8ae1b9..178cbb8ae1b9 100644
--- a/arch/sparc/lib/atomic.S
+++ b/arch/sparc/lib/atomic_32.S
diff --git a/arch/sparc/lib/atomic_64.S b/arch/sparc/lib/atomic_64.S
new file mode 100644
index 000000000000..0268210ca168
--- /dev/null
+++ b/arch/sparc/lib/atomic_64.S
@@ -0,0 +1,138 @@
+/* atomic.S: These things are too big to do inline.
+ *
+ * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <asm/asi.h>
+#include <asm/backoff.h>
+
+ .text
+
+ /* Two versions of the atomic routines, one that
+ * does not return a value and does not perform
+ * memory barriers, and a second which returns
+ * a value and does the barriers.
+ */
+ .globl atomic_add
+ .type atomic_add,#function
+atomic_add: /* %o0 = increment, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: lduw [%o1], %g1
+ add %g1, %o0, %g7
+ cas [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic_add, .-atomic_add
+
+ .globl atomic_sub
+ .type atomic_sub,#function
+atomic_sub: /* %o0 = decrement, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: lduw [%o1], %g1
+ sub %g1, %o0, %g7
+ cas [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic_sub, .-atomic_sub
+
+ .globl atomic_add_ret
+ .type atomic_add_ret,#function
+atomic_add_ret: /* %o0 = increment, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: lduw [%o1], %g1
+ add %g1, %o0, %g7
+ cas [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 2f
+ add %g7, %o0, %g7
+ sra %g7, 0, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic_add_ret, .-atomic_add_ret
+
+ .globl atomic_sub_ret
+ .type atomic_sub_ret,#function
+atomic_sub_ret: /* %o0 = decrement, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: lduw [%o1], %g1
+ sub %g1, %o0, %g7
+ cas [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 2f
+ sub %g7, %o0, %g7
+ sra %g7, 0, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic_sub_ret, .-atomic_sub_ret
+
+ .globl atomic64_add
+ .type atomic64_add,#function
+atomic64_add: /* %o0 = increment, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: ldx [%o1], %g1
+ add %g1, %o0, %g7
+ casx [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %xcc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic64_add, .-atomic64_add
+
+ .globl atomic64_sub
+ .type atomic64_sub,#function
+atomic64_sub: /* %o0 = decrement, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: ldx [%o1], %g1
+ sub %g1, %o0, %g7
+ casx [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %xcc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic64_sub, .-atomic64_sub
+
+ .globl atomic64_add_ret
+ .type atomic64_add_ret,#function
+atomic64_add_ret: /* %o0 = increment, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: ldx [%o1], %g1
+ add %g1, %o0, %g7
+ casx [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %xcc, 2f
+ add %g7, %o0, %g7
+ mov %g7, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic64_add_ret, .-atomic64_add_ret
+
+ .globl atomic64_sub_ret
+ .type atomic64_sub_ret,#function
+atomic64_sub_ret: /* %o0 = decrement, %o1 = atomic_ptr */
+ BACKOFF_SETUP(%o2)
+1: ldx [%o1], %g1
+ sub %g1, %o0, %g7
+ casx [%o1], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %xcc, 2f
+ sub %g7, %o0, %g7
+ mov %g7, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o2, %o3, 1b)
+ .size atomic64_sub_ret, .-atomic64_sub_ret
diff --git a/arch/sparc/lib/bitops.S b/arch/sparc/lib/bitops.S
new file mode 100644
index 000000000000..2b7228cb8c22
--- /dev/null
+++ b/arch/sparc/lib/bitops.S
@@ -0,0 +1,141 @@
+/* bitops.S: Sparc64 atomic bit operations.
+ *
+ * Copyright (C) 2000, 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <asm/asi.h>
+#include <asm/backoff.h>
+
+ .text
+
+ .globl test_and_set_bit
+ .type test_and_set_bit,#function
+test_and_set_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ or %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ and %g7, %o2, %g2
+ clr %o0
+ movrne %g2, 1, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size test_and_set_bit, .-test_and_set_bit
+
+ .globl test_and_clear_bit
+ .type test_and_clear_bit,#function
+test_and_clear_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ andn %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ and %g7, %o2, %g2
+ clr %o0
+ movrne %g2, 1, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size test_and_clear_bit, .-test_and_clear_bit
+
+ .globl test_and_change_bit
+ .type test_and_change_bit,#function
+test_and_change_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ xor %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ and %g7, %o2, %g2
+ clr %o0
+ movrne %g2, 1, %o0
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size test_and_change_bit, .-test_and_change_bit
+
+ .globl set_bit
+ .type set_bit,#function
+set_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ or %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size set_bit, .-set_bit
+
+ .globl clear_bit
+ .type clear_bit,#function
+clear_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ andn %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size clear_bit, .-clear_bit
+
+ .globl change_bit
+ .type change_bit,#function
+change_bit: /* %o0=nr, %o1=addr */
+ BACKOFF_SETUP(%o3)
+ srlx %o0, 6, %g1
+ mov 1, %o2
+ sllx %g1, 3, %g3
+ and %o0, 63, %g2
+ sllx %o2, %g2, %o2
+ add %o1, %g3, %o1
+1: ldx [%o1], %g7
+ xor %g7, %o2, %g1
+ casx [%o1], %g7, %g1
+ cmp %g7, %g1
+ bne,pn %xcc, 2f
+ nop
+ retl
+ nop
+2: BACKOFF_SPIN(%o3, %o4, 1b)
+ .size change_bit, .-change_bit
diff --git a/arch/sparc64/lib/bzero.S b/arch/sparc/lib/bzero.S
index c7bbae8c590f..c7bbae8c590f 100644
--- a/arch/sparc64/lib/bzero.S
+++ b/arch/sparc/lib/bzero.S
diff --git a/arch/sparc/lib/checksum.S b/arch/sparc/lib/checksum_32.S
index 77f228533d47..77f228533d47 100644
--- a/arch/sparc/lib/checksum.S
+++ b/arch/sparc/lib/checksum_32.S
diff --git a/arch/sparc64/lib/checksum.S b/arch/sparc/lib/checksum_64.S
index 1d230f693dc4..1d230f693dc4 100644
--- a/arch/sparc64/lib/checksum.S
+++ b/arch/sparc/lib/checksum_64.S
diff --git a/arch/sparc64/lib/clear_page.S b/arch/sparc/lib/clear_page.S
index 77e531f6c2a7..77e531f6c2a7 100644
--- a/arch/sparc64/lib/clear_page.S
+++ b/arch/sparc/lib/clear_page.S
diff --git a/arch/sparc64/lib/copy_in_user.S b/arch/sparc/lib/copy_in_user.S
index 650af3f21f78..650af3f21f78 100644
--- a/arch/sparc64/lib/copy_in_user.S
+++ b/arch/sparc/lib/copy_in_user.S
diff --git a/arch/sparc64/lib/copy_page.S b/arch/sparc/lib/copy_page.S
index b243d3b606ba..b243d3b606ba 100644
--- a/arch/sparc64/lib/copy_page.S
+++ b/arch/sparc/lib/copy_page.S
diff --git a/arch/sparc64/lib/csum_copy.S b/arch/sparc/lib/csum_copy.S
index e566c770a0f6..e566c770a0f6 100644
--- a/arch/sparc64/lib/csum_copy.S
+++ b/arch/sparc/lib/csum_copy.S
diff --git a/arch/sparc64/lib/csum_copy_from_user.S b/arch/sparc/lib/csum_copy_from_user.S
index a22eddbe5dba..a22eddbe5dba 100644
--- a/arch/sparc64/lib/csum_copy_from_user.S
+++ b/arch/sparc/lib/csum_copy_from_user.S
diff --git a/arch/sparc64/lib/csum_copy_to_user.S b/arch/sparc/lib/csum_copy_to_user.S
index d5b12f441f02..d5b12f441f02 100644
--- a/arch/sparc64/lib/csum_copy_to_user.S
+++ b/arch/sparc/lib/csum_copy_to_user.S
diff --git a/arch/sparc64/lib/ipcsum.S b/arch/sparc/lib/ipcsum.S
index 58ca5b9a8778..58ca5b9a8778 100644
--- a/arch/sparc64/lib/ipcsum.S
+++ b/arch/sparc/lib/ipcsum.S
diff --git a/arch/sparc64/lib/mcount.S b/arch/sparc/lib/mcount.S
index 7ce9c65f3592..7ce9c65f3592 100644
--- a/arch/sparc64/lib/mcount.S
+++ b/arch/sparc/lib/mcount.S
diff --git a/arch/sparc/lib/memcmp.S b/arch/sparc/lib/memcmp.S
index cb4bdb0cc2af..efa106c41ed0 100644
--- a/arch/sparc/lib/memcmp.S
+++ b/arch/sparc/lib/memcmp.S
@@ -1,312 +1,27 @@
- .text
- .align 4
- .global __memcmp, memcmp
-__memcmp:
-memcmp:
-#if 1
- cmp %o2, 0
- ble L3
- mov 0, %g3
-L5:
- ldub [%o0], %g2
- ldub [%o1], %g3
- sub %g2, %g3, %g2
- mov %g2, %g3
- sll %g2, 24, %g2
-
- cmp %g2, 0
- bne L3
- add %o0, 1, %o0
+/* Sparc optimized memcmp code.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2000, 2008 David S. Miller (davem@davemloft.net)
+ */
- add %o2, -1, %o2
+#include <linux/linkage.h>
+#include <asm/asm.h>
+ .text
+ENTRY(memcmp)
cmp %o2, 0
- bg L5
- add %o1, 1, %o1
-L3:
- sll %g3, 24, %o0
- sra %o0, 24, %o0
-
- retl
+1: BRANCH32(be, pn, 2f)
nop
-#else
- save %sp, -104, %sp
- mov %i2, %o4
- mov %i0, %o0
-
- cmp %o4, 15
- ble L72
- mov %i1, %i2
-
- andcc %i2, 3, %g0
- be L161
- andcc %o0, 3, %g2
-L75:
- ldub [%o0], %g3
- ldub [%i2], %g2
- add %o0,1, %o0
-
- subcc %g3, %g2, %i0
- bne L156
- add %i2, 1, %i2
-
- andcc %i2, 3, %g0
- bne L75
- add %o4, -1, %o4
-
- andcc %o0, 3, %g2
-L161:
- bne,a L78
- mov %i2, %i1
-
- mov %o0, %i5
- mov %i2, %i3
- srl %o4, 2, %i4
-
- cmp %i4, 0
- bge L93
- mov %i4, %g2
-
- add %i4, 3, %g2
-L93:
- sra %g2, 2, %g2
- sll %g2, 2, %g2
- sub %i4, %g2, %g2
-
- cmp %g2, 1
- be,a L88
- add %o0, 4, %i5
-
- bg L94
- cmp %g2, 2
-
- cmp %g2, 0
- be,a L86
- ld [%o0], %g3
-
- b L162
- ld [%i5], %g3
-L94:
- be L81
- cmp %g2, 3
-
- be,a L83
- add %o0, -4, %i5
-
- b L162
- ld [%i5], %g3
-L81:
- add %o0, -8, %i5
- ld [%o0], %g3
- add %i2, -8, %i3
- ld [%i2], %g2
-
- b L82
- add %i4, 2, %i4
-L83:
- ld [%o0], %g4
- add %i2, -4, %i3
- ld [%i2], %g1
-
- b L84
- add %i4, 1, %i4
-L86:
- b L87
- ld [%i2], %g2
-L88:
- add %i2, 4, %i3
- ld [%o0], %g4
- add %i4, -1, %i4
- ld [%i2], %g1
-L95:
- ld [%i5], %g3
-L162:
- cmp %g4, %g1
- be L87
- ld [%i3], %g2
-
- cmp %g4, %g1
-L163:
- bleu L114
- mov -1, %i0
-
- b L114
- mov 1, %i0
-L87:
- ld [%i5 + 4], %g4
- cmp %g3, %g2
- bne L163
- ld [%i3 + 4], %g1
-L84:
- ld [%i5 + 8], %g3
-
- cmp %g4, %g1
- bne L163
- ld [%i3 + 8], %g2
-L82:
- ld [%i5 + 12], %g4
- cmp %g3, %g2
- bne L163
- ld [%i3 + 12], %g1
-
- add %i5, 16, %i5
-
- addcc %i4, -4, %i4
- bne L95
- add %i3, 16, %i3
-
- cmp %g4, %g1
- bne L163
- nop
-
- b L114
- mov 0, %i0
-L78:
- srl %o4, 2, %i0
- and %o0, -4, %i3
- orcc %i0, %g0, %g3
- sll %g2, 3, %o7
- mov 32, %g2
-
- bge L129
- sub %g2, %o7, %o1
-
- add %i0, 3, %g3
-L129:
- sra %g3, 2, %g2
- sll %g2, 2, %g2
- sub %i0, %g2, %g2
-
- cmp %g2, 1
- be,a L124
- ld [%i3], %o3
-
- bg L130
- cmp %g2, 2
-
- cmp %g2, 0
- be,a L122
- ld [%i3], %o2
-
- b L164
- sll %o3, %o7, %g3
-L130:
- be L117
- cmp %g2, 3
-
- be,a L119
- ld [%i3], %g1
-
- b L164
- sll %o3, %o7, %g3
-L117:
- ld [%i3], %g4
- add %i2, -8, %i1
- ld [%i3 + 4], %o3
- add %i0, 2, %i0
- ld [%i2], %i4
-
- b L118
- add %i3, -4, %i3
-L119:
- ld [%i3 + 4], %g4
- add %i2, -4, %i1
- ld [%i2], %i5
-
- b L120
- add %i0, 1, %i0
-L122:
- ld [%i3 + 4], %g1
- ld [%i2], %i4
-
- b L123
- add %i3, 4, %i3
-L124:
- add %i2, 4, %i1
- ld [%i3 + 4], %o2
- add %i0, -1, %i0
- ld [%i2], %i5
- add %i3, 8, %i3
-L131:
- sll %o3, %o7, %g3
-L164:
- srl %o2, %o1, %g2
- ld [%i3], %g1
- or %g3, %g2, %g3
-
- cmp %g3, %i5
- bne L163
- ld [%i1], %i4
-L123:
- sll %o2, %o7, %g3
- srl %g1, %o1, %g2
- ld [%i3 + 4], %g4
- or %g3, %g2, %g3
-
- cmp %g3, %i4
- bne L163
- ld [%i1 + 4], %i5
-L120:
- sll %g1, %o7, %g3
- srl %g4, %o1, %g2
- ld [%i3 + 8], %o3
- or %g3, %g2, %g3
-
- cmp %g3, %i5
- bne L163
- ld [%i1 + 8], %i4
-L118:
- sll %g4, %o7, %g3
- srl %o3, %o1, %g2
- ld [%i3 + 12], %o2
- or %g3, %g2, %g3
-
- cmp %g3, %i4
- bne L163
- ld [%i1 + 12], %i5
-
- add %i3, 16, %i3
- addcc %i0, -4, %i0
- bne L131
- add %i1, 16, %i1
-
- sll %o3, %o7, %g3
- srl %o2, %o1, %g2
- or %g3, %g2, %g3
-
- cmp %g3, %i5
- be,a L114
- mov 0, %i0
-
- b,a L163
-L114:
- cmp %i0, 0
- bne L156
- and %o4, -4, %g2
-
- add %o0, %g2, %o0
- add %i2, %g2, %i2
- and %o4, 3, %o4
-L72:
- cmp %o4, 0
- be L156
- mov 0, %i0
-
- ldub [%o0], %g3
-L165:
- ldub [%i2], %g2
+ ldub [%o0], %g7
+ ldub [%o1], %g3
+ sub %o2, 1, %o2
add %o0, 1, %o0
-
- subcc %g3, %g2, %i0
- bne L156
- add %i2, 1, %i2
-
- addcc %o4, -1, %o4
- bne,a L165
- ldub [%o0], %g3
-
- mov 0, %i0
-L156:
- ret
- restore
-#endif
+ add %o1, 1, %o1
+ subcc %g7, %g3, %g3
+ BRANCH32(be, pt, 1b)
+ cmp %o2, 0
+ retl
+ mov %g3, %o0
+2: retl
+ mov 0, %o0
+ENDPROC(memcmp)
diff --git a/arch/sparc64/lib/memmove.S b/arch/sparc/lib/memmove.S
index 97395802c23c..97395802c23c 100644
--- a/arch/sparc64/lib/memmove.S
+++ b/arch/sparc/lib/memmove.S
diff --git a/arch/sparc/lib/memscan.S b/arch/sparc/lib/memscan_32.S
index 4ff1657dfc24..4ff1657dfc24 100644
--- a/arch/sparc/lib/memscan.S
+++ b/arch/sparc/lib/memscan_32.S
diff --git a/arch/sparc64/lib/memscan.S b/arch/sparc/lib/memscan_64.S
index 5686dfa5dc15..5686dfa5dc15 100644
--- a/arch/sparc64/lib/memscan.S
+++ b/arch/sparc/lib/memscan_64.S
diff --git a/arch/sparc/lib/rwsem.S b/arch/sparc/lib/rwsem_32.S
index 9675268e7fde..9675268e7fde 100644
--- a/arch/sparc/lib/rwsem.S
+++ b/arch/sparc/lib/rwsem_32.S
diff --git a/arch/sparc/lib/rwsem_64.S b/arch/sparc/lib/rwsem_64.S
new file mode 100644
index 000000000000..91a7d29a79d5
--- /dev/null
+++ b/arch/sparc/lib/rwsem_64.S
@@ -0,0 +1,163 @@
+/* rwsem.S: RW semaphore assembler.
+ *
+ * Written by David S. Miller (davem@redhat.com), 2001.
+ * Derived from asm-i386/rwsem.h
+ */
+
+#include <asm/rwsem-const.h>
+
+ .section .sched.text, "ax"
+
+ .globl __down_read
+__down_read:
+1: lduw [%o0], %g1
+ add %g1, 1, %g7
+ cas [%o0], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 1b
+ add %g7, 1, %g7
+ cmp %g7, 0
+ bl,pn %icc, 3f
+ nop
+2:
+ retl
+ nop
+3:
+ save %sp, -192, %sp
+ call rwsem_down_read_failed
+ mov %i0, %o0
+ ret
+ restore
+ .size __down_read, .-__down_read
+
+ .globl __down_read_trylock
+__down_read_trylock:
+1: lduw [%o0], %g1
+ add %g1, 1, %g7
+ cmp %g7, 0
+ bl,pn %icc, 2f
+ mov 0, %o1
+ cas [%o0], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 1b
+ mov 1, %o1
+2: retl
+ mov %o1, %o0
+ .size __down_read_trylock, .-__down_read_trylock
+
+ .globl __down_write
+__down_write:
+ sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
+ or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
+1:
+ lduw [%o0], %g3
+ add %g3, %g1, %g7
+ cas [%o0], %g3, %g7
+ cmp %g3, %g7
+ bne,pn %icc, 1b
+ cmp %g7, 0
+ bne,pn %icc, 3f
+ nop
+2: retl
+ nop
+3:
+ save %sp, -192, %sp
+ call rwsem_down_write_failed
+ mov %i0, %o0
+ ret
+ restore
+ .size __down_write, .-__down_write
+
+ .globl __down_write_trylock
+__down_write_trylock:
+ sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
+ or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
+1:
+ lduw [%o0], %g3
+ cmp %g3, 0
+ bne,pn %icc, 2f
+ mov 0, %o1
+ add %g3, %g1, %g7
+ cas [%o0], %g3, %g7
+ cmp %g3, %g7
+ bne,pn %icc, 1b
+ mov 1, %o1
+2: retl
+ mov %o1, %o0
+ .size __down_write_trylock, .-__down_write_trylock
+
+ .globl __up_read
+__up_read:
+1:
+ lduw [%o0], %g1
+ sub %g1, 1, %g7
+ cas [%o0], %g1, %g7
+ cmp %g1, %g7
+ bne,pn %icc, 1b
+ cmp %g7, 0
+ bl,pn %icc, 3f
+ nop
+2: retl
+ nop
+3: sethi %hi(RWSEM_ACTIVE_MASK), %g1
+ sub %g7, 1, %g7
+ or %g1, %lo(RWSEM_ACTIVE_MASK), %g1
+ andcc %g7, %g1, %g0
+ bne,pn %icc, 2b
+ nop
+ save %sp, -192, %sp
+ call rwsem_wake
+ mov %i0, %o0
+ ret
+ restore
+ .size __up_read, .-__up_read
+
+ .globl __up_write
+__up_write:
+ sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
+ or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
+1:
+ lduw [%o0], %g3
+ sub %g3, %g1, %g7
+ cas [%o0], %g3, %g7
+ cmp %g3, %g7
+ bne,pn %icc, 1b
+ sub %g7, %g1, %g7
+ cmp %g7, 0
+ bl,pn %icc, 3f
+ nop
+2:
+ retl
+ nop
+3:
+ save %sp, -192, %sp
+ call rwsem_wake
+ mov %i0, %o0
+ ret
+ restore
+ .size __up_write, .-__up_write
+
+ .globl __downgrade_write
+__downgrade_write:
+ sethi %hi(RWSEM_WAITING_BIAS), %g1
+ or %g1, %lo(RWSEM_WAITING_BIAS), %g1
+1:
+ lduw [%o0], %g3
+ sub %g3, %g1, %g7
+ cas [%o0], %g3, %g7
+ cmp %g3, %g7
+ bne,pn %icc, 1b
+ sub %g7, %g1, %g7
+ cmp %g7, 0
+ bl,pn %icc, 3f
+ nop
+2:
+ retl
+ nop
+3:
+ save %sp, -192, %sp
+ call rwsem_downgrade_wake
+ mov %i0, %o0
+ ret
+ restore
+ .size __downgrade_write, .-__downgrade_write
diff --git a/arch/sparc/lib/strlen.S b/arch/sparc/lib/strlen.S
index ed9a763368cd..536f83507fbf 100644
--- a/arch/sparc/lib/strlen.S
+++ b/arch/sparc/lib/strlen.S
@@ -1,51 +1,40 @@
/* strlen.S: Sparc optimized strlen code
* Hand optimized from GNU libc's strlen
* Copyright (C) 1991,1996 Free Software Foundation
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996,2008 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
#define LO_MAGIC 0x01010101
#define HI_MAGIC 0x80808080
-0:
+ .text
+ENTRY(strlen)
+ mov %o0, %o1
+ andcc %o0, 3, %g0
+ BRANCH32(be, pt, 9f)
+ sethi %hi(HI_MAGIC), %o4
ldub [%o0], %o5
- cmp %o5, 0
- be 1f
+ BRANCH_REG_ZERO(pn, %o5, 11f)
add %o0, 1, %o0
andcc %o0, 3, %g0
- be 4f
+ BRANCH32(be, pn, 4f)
or %o4, %lo(HI_MAGIC), %o3
ldub [%o0], %o5
- cmp %o5, 0
- be 2f
+ BRANCH_REG_ZERO(pn, %o5, 12f)
add %o0, 1, %o0
andcc %o0, 3, %g0
- be 5f
+ BRANCH32(be, pt, 5f)
sethi %hi(LO_MAGIC), %o4
ldub [%o0], %o5
- cmp %o5, 0
- be 3f
+ BRANCH_REG_ZERO(pn, %o5, 13f)
add %o0, 1, %o0
- b 8f
+ BRANCH32(ba, pt, 8f)
or %o4, %lo(LO_MAGIC), %o2
-1:
- retl
- mov 0, %o0
-2:
- retl
- mov 1, %o0
-3:
- retl
- mov 2, %o0
-
- .align 4
- .global strlen
-strlen:
- mov %o0, %o1
- andcc %o0, 3, %g0
- bne 0b
- sethi %hi(HI_MAGIC), %o4
+9:
or %o4, %lo(HI_MAGIC), %o3
4:
sethi %hi(LO_MAGIC), %o4
@@ -56,26 +45,36 @@ strlen:
2:
sub %o5, %o2, %o4
andcc %o4, %o3, %g0
- be 8b
+ BRANCH32(be, pt, 8b)
add %o0, 4, %o0
/* Check every byte. */
- srl %o5, 24, %g5
- andcc %g5, 0xff, %g0
- be 1f
+ srl %o5, 24, %g7
+ andcc %g7, 0xff, %g0
+ BRANCH32(be, pn, 1f)
add %o0, -4, %o4
- srl %o5, 16, %g5
- andcc %g5, 0xff, %g0
- be 1f
+ srl %o5, 16, %g7
+ andcc %g7, 0xff, %g0
+ BRANCH32(be, pn, 1f)
add %o4, 1, %o4
- srl %o5, 8, %g5
- andcc %g5, 0xff, %g0
- be 1f
+ srl %o5, 8, %g7
+ andcc %g7, 0xff, %g0
+ BRANCH32(be, pn, 1f)
add %o4, 1, %o4
andcc %o5, 0xff, %g0
- bne,a 2b
+ BRANCH32_ANNUL(bne, pt, 2b)
ld [%o0], %o5
add %o4, 1, %o4
1:
retl
sub %o4, %o1, %o0
+11:
+ retl
+ mov 0, %o0
+12:
+ retl
+ mov 1, %o0
+13:
+ retl
+ mov 2, %o0
+ENDPROC(strlen)
diff --git a/arch/sparc/lib/strlen_user.S b/arch/sparc/lib/strlen_user_32.S
index 8c8a371df3c9..8c8a371df3c9 100644
--- a/arch/sparc/lib/strlen_user.S
+++ b/arch/sparc/lib/strlen_user_32.S
diff --git a/arch/sparc64/lib/strlen_user.S b/arch/sparc/lib/strlen_user_64.S
index 114ed111e251..114ed111e251 100644
--- a/arch/sparc64/lib/strlen_user.S
+++ b/arch/sparc/lib/strlen_user_64.S
diff --git a/arch/sparc/lib/strncmp.S b/arch/sparc/lib/strncmp_32.S
index 494ec664537a..494ec664537a 100644
--- a/arch/sparc/lib/strncmp.S
+++ b/arch/sparc/lib/strncmp_32.S
diff --git a/arch/sparc64/lib/strncmp.S b/arch/sparc/lib/strncmp_64.S
index 980e83751556..980e83751556 100644
--- a/arch/sparc64/lib/strncmp.S
+++ b/arch/sparc/lib/strncmp_64.S
diff --git a/arch/sparc/lib/strncpy_from_user.S b/arch/sparc/lib/strncpy_from_user_32.S
index d77198976a66..d77198976a66 100644
--- a/arch/sparc/lib/strncpy_from_user.S
+++ b/arch/sparc/lib/strncpy_from_user_32.S
diff --git a/arch/sparc64/lib/strncpy_from_user.S b/arch/sparc/lib/strncpy_from_user_64.S
index 511c8f136f95..511c8f136f95 100644
--- a/arch/sparc64/lib/strncpy_from_user.S
+++ b/arch/sparc/lib/strncpy_from_user_64.S
diff --git a/arch/sparc64/lib/user_fixup.c b/arch/sparc/lib/user_fixup.c
index 05a361b0a1a4..05a361b0a1a4 100644
--- a/arch/sparc64/lib/user_fixup.c
+++ b/arch/sparc/lib/user_fixup.c
diff --git a/arch/sparc64/lib/xor.S b/arch/sparc/lib/xor.S
index f44f58f40234..f44f58f40234 100644
--- a/arch/sparc64/lib/xor.S
+++ b/arch/sparc/lib/xor.S
diff --git a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile
index 8136987977f4..b9085ecbb27b 100644
--- a/arch/sparc/math-emu/Makefile
+++ b/arch/sparc/math-emu/Makefile
@@ -2,7 +2,7 @@
# Makefile for the FPU instruction emulation.
#
-obj-y := math.o
+# supress all warnings - as math.c produces a lot!
+ccflags-y := -w
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS = -I. -Iinclude/math-emu -w
+obj-y := math_$(BITS).o
diff --git a/arch/sparc/math-emu/ashldi3.S b/arch/sparc/math-emu/ashldi3.S
deleted file mode 100644
index 7230ff5c7aa1..000000000000
--- a/arch/sparc/math-emu/ashldi3.S
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * ashldi3.S: Math-emu code creates all kinds of references to
- * this little routine on the sparc with gcc.
- *
- * Copyright (C) 1998 Jakub Jelinek(jj@ultra.linux.cz)
- */
-
-#include <asm/cprefix.h>
-
- .globl C_LABEL(__ashldi3)
-C_LABEL(__ashldi3):
- tst %o2
- be 3f
- mov 32, %g2
-
- sub %g2, %o2, %g2
-
- tst %g2
- bg 1f
- srl %o1, %g2, %g3
-
- clr %o5
- neg %g2
- ba 2f
- sll %o1, %g2, %o4
-
-1:
- sll %o1, %o2, %o5
- srl %o0, %o2, %g2
- or %g2, %g3, %o4
-2:
- mov %o4, %o0
- mov %o5, %o1
-3:
- jmpl %o7 + 8, %g0
- nop
diff --git a/arch/sparc/math-emu/math.c b/arch/sparc/math-emu/math.c
deleted file mode 100644
index 8613b3eb877c..000000000000
--- a/arch/sparc/math-emu/math.c
+++ /dev/null
@@ -1,511 +0,0 @@
-/*
- * arch/sparc/math-emu/math.c
- *
- * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
- * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
- *
- * This is a good place to start if you're trying to understand the
- * emulation code, because it's pretty simple. What we do is
- * essentially analyse the instruction to work out what the operation
- * is and which registers are involved. We then execute the appropriate
- * FXXXX function. [The floating point queue introduces a minor wrinkle;
- * see below...]
- * The fxxxxx.c files each emulate a single insn. They look relatively
- * simple because the complexity is hidden away in an unholy tangle
- * of preprocessor macros.
- *
- * The first layer of macros is single.h, double.h, quad.h. Generally
- * these files define macros for working with floating point numbers
- * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles,
- * for instance. These macros are usually defined as calls to more
- * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
- * of machine words required to store the given IEEE format is passed
- * as a parameter. [double.h and co check the number of bits in a word
- * and define FP_ADD_D & co appropriately].
- * The generic macros are defined in op-common.h. This is where all
- * the grotty stuff like handling NaNs is coded. To handle the possible
- * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
- * where wc is the 'number of machine words' parameter (here 2).
- * These are defined in the third layer of macros: op-1.h, op-2.h
- * and op-4.h. These handle operations on floating point numbers composed
- * of 1,2 and 4 machine words respectively. [For example, on sparc64
- * doubles are one machine word so macros in double.h eventually use
- * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
- * soft-fp.h is on the same level as op-common.h, and defines some
- * macros which are independent of both word size and FP format.
- * Finally, sfp-machine.h is the machine dependent part of the
- * code: it defines the word size and what type a word is. It also
- * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
- * provide several possible flavours of multiply algorithm, most
- * of which require that you supply some form of asm or C primitive to
- * do the actual multiply. (such asm primitives should be defined
- * in sfp-machine.h too). udivmodti4.c is the same sort of thing.
- *
- * There may be some errors here because I'm working from a
- * SPARC architecture manual V9, and what I really want is V8...
- * Also, the insns which can generate exceptions seem to be a
- * greater subset of the FPops than for V9 (for example, FCMPED
- * has to be emulated on V8). So I think I'm going to have
- * to emulate them all just to be on the safe side...
- *
- * Emulation routines originate from soft-fp package, which is
- * part of glibc and has appropriate copyrights in it (allegedly).
- *
- * NB: on sparc int == long == 4 bytes, long long == 8 bytes.
- * Most bits of the kernel seem to go for long rather than int,
- * so we follow that practice...
- */
-
-/* TODO:
- * fpsave() saves the FP queue but fpload() doesn't reload it.
- * Therefore when we context switch or change FPU ownership
- * we have to check to see if the queue had anything in it and
- * emulate it if it did. This is going to be a pain.
- */
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <asm/uaccess.h>
-
-#include "sfp-util.h"
-#include <math-emu/soft-fp.h>
-#include <math-emu/single.h>
-#include <math-emu/double.h>
-#include <math-emu/quad.h>
-
-#define FLOATFUNC(x) extern int x(void *,void *,void *)
-
-/* The Vn labels indicate what version of the SPARC architecture gas thinks
- * each insn is. This is from the binutils source :->
- */
-/* quadword instructions */
-#define FSQRTQ 0x02b /* v8 */
-#define FADDQ 0x043 /* v8 */
-#define FSUBQ 0x047 /* v8 */
-#define FMULQ 0x04b /* v8 */
-#define FDIVQ 0x04f /* v8 */
-#define FDMULQ 0x06e /* v8 */
-#define FQTOS 0x0c7 /* v8 */
-#define FQTOD 0x0cb /* v8 */
-#define FITOQ 0x0cc /* v8 */
-#define FSTOQ 0x0cd /* v8 */
-#define FDTOQ 0x0ce /* v8 */
-#define FQTOI 0x0d3 /* v8 */
-#define FCMPQ 0x053 /* v8 */
-#define FCMPEQ 0x057 /* v8 */
-/* single/double instructions (subnormal): should all work */
-#define FSQRTS 0x029 /* v7 */
-#define FSQRTD 0x02a /* v7 */
-#define FADDS 0x041 /* v6 */
-#define FADDD 0x042 /* v6 */
-#define FSUBS 0x045 /* v6 */
-#define FSUBD 0x046 /* v6 */
-#define FMULS 0x049 /* v6 */
-#define FMULD 0x04a /* v6 */
-#define FDIVS 0x04d /* v6 */
-#define FDIVD 0x04e /* v6 */
-#define FSMULD 0x069 /* v6 */
-#define FDTOS 0x0c6 /* v6 */
-#define FSTOD 0x0c9 /* v6 */
-#define FSTOI 0x0d1 /* v6 */
-#define FDTOI 0x0d2 /* v6 */
-#define FABSS 0x009 /* v6 */
-#define FCMPS 0x051 /* v6 */
-#define FCMPES 0x055 /* v6 */
-#define FCMPD 0x052 /* v6 */
-#define FCMPED 0x056 /* v6 */
-#define FMOVS 0x001 /* v6 */
-#define FNEGS 0x005 /* v6 */
-#define FITOS 0x0c4 /* v6 */
-#define FITOD 0x0c8 /* v6 */
-
-#define FSR_TEM_SHIFT 23UL
-#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
-#define FSR_AEXC_SHIFT 5UL
-#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
-#define FSR_CEXC_SHIFT 0UL
-#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
-
-static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);
-
-/* Unlike the Sparc64 version (which has a struct fpustate), we
- * pass the taskstruct corresponding to the task which currently owns the
- * FPU. This is partly because we don't have the fpustate struct and
- * partly because the task owning the FPU isn't always current (as is
- * the case for the Sparc64 port). This is probably SMP-related...
- * This function returns 1 if all queued insns were emulated successfully.
- * The test for unimplemented FPop in kernel mode has been moved into
- * kernel/traps.c for simplicity.
- */
-int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
-{
- /* regs->pc isn't necessarily the PC at which the offending insn is sitting.
- * The FPU maintains a queue of FPops which cause traps.
- * When it hits an instruction that requires that the trapped op succeeded
- * (usually because it reads a reg. that the trapped op wrote) then it
- * causes this exception. We need to emulate all the insns on the queue
- * and then allow the op to proceed.
- * This code should also handle the case where the trap was precise,
- * in which case the queue length is zero and regs->pc points at the
- * single FPop to be emulated. (this case is untested, though :->)
- * You'll need this case if you want to be able to emulate all FPops
- * because the FPU either doesn't exist or has been software-disabled.
- * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it
- * might sound because the Ultra does funky things with a superscalar
- * architecture.]
- */
-
- /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
-
- int i;
- int retcode = 0; /* assume all succeed */
- unsigned long insn;
-
-#ifdef DEBUG_MATHEMU
- printk("In do_mathemu()... pc is %08lx\n", regs->pc);
- printk("fpqdepth is %ld\n", fpt->thread.fpqdepth);
- for (i = 0; i < fpt->thread.fpqdepth; i++)
- printk("%d: %08lx at %08lx\n", i, fpt->thread.fpqueue[i].insn,
- (unsigned long)fpt->thread.fpqueue[i].insn_addr);
-#endif
-
- if (fpt->thread.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */
-#ifdef DEBUG_MATHEMU
- printk("precise trap at %08lx\n", regs->pc);
-#endif
- if (!get_user(insn, (u32 __user *) regs->pc)) {
- retcode = do_one_mathemu(insn, &fpt->thread.fsr, fpt->thread.float_regs);
- if (retcode) {
- /* in this case we need to fix up PC & nPC */
- regs->pc = regs->npc;
- regs->npc += 4;
- }
- }
- return retcode;
- }
-
- /* Normal case: need to empty the queue... */
- for (i = 0; i < fpt->thread.fpqdepth; i++) {
- retcode = do_one_mathemu(fpt->thread.fpqueue[i].insn, &(fpt->thread.fsr), fpt->thread.float_regs);
- if (!retcode) /* insn failed, no point doing any more */
- break;
- }
- /* Now empty the queue and clear the queue_not_empty flag */
- if (retcode)
- fpt->thread.fsr &= ~(0x3000 | FSR_CEXC_MASK);
- else
- fpt->thread.fsr &= ~0x3000;
- fpt->thread.fpqdepth = 0;
-
- return retcode;
-}
-
-/* All routines returning an exception to raise should detect
- * such exceptions _before_ rounding to be consistent with
- * the behavior of the hardware in the implemented cases
- * (and thus with the recommendations in the V9 architecture
- * manual).
- *
- * We return 0 if a SIGFPE should be sent, 1 otherwise.
- */
-static inline int record_exception(unsigned long *pfsr, int eflag)
-{
- unsigned long fsr = *pfsr;
- int would_trap;
-
- /* Determine if this exception would have generated a trap. */
- would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
-
- /* If trapping, we only want to signal one bit. */
- if (would_trap != 0) {
- eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
- if ((eflag & (eflag - 1)) != 0) {
- if (eflag & FP_EX_INVALID)
- eflag = FP_EX_INVALID;
- else if (eflag & FP_EX_OVERFLOW)
- eflag = FP_EX_OVERFLOW;
- else if (eflag & FP_EX_UNDERFLOW)
- eflag = FP_EX_UNDERFLOW;
- else if (eflag & FP_EX_DIVZERO)
- eflag = FP_EX_DIVZERO;
- else if (eflag & FP_EX_INEXACT)
- eflag = FP_EX_INEXACT;
- }
- }
-
- /* Set CEXC, here is the rule:
- *
- * In general all FPU ops will set one and only one
- * bit in the CEXC field, this is always the case
- * when the IEEE exception trap is enabled in TEM.
- */
- fsr &= ~(FSR_CEXC_MASK);
- fsr |= ((long)eflag << FSR_CEXC_SHIFT);
-
- /* Set the AEXC field, rule is:
- *
- * If a trap would not be generated, the
- * CEXC just generated is OR'd into the
- * existing value of AEXC.
- */
- if (would_trap == 0)
- fsr |= ((long)eflag << FSR_AEXC_SHIFT);
-
- /* If trapping, indicate fault trap type IEEE. */
- if (would_trap != 0)
- fsr |= (1UL << 14);
-
- *pfsr = fsr;
-
- return (would_trap ? 0 : 1);
-}
-
-typedef union {
- u32 s;
- u64 d;
- u64 q[2];
-} *argp;
-
-static int do_one_mathemu(u32 insn, unsigned long *pfsr, unsigned long *fregs)
-{
- /* Emulate the given insn, updating fsr and fregs appropriately. */
- int type = 0;
- /* r is rd, b is rs2 and a is rs1. The *u arg tells
- whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
- non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
-#define TYPE(dummy, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6)
- int freg;
- argp rs1 = NULL, rs2 = NULL, rd = NULL;
- FP_DECL_EX;
- FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
- FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
- FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
- int IR;
- long fsr;
-
-#ifdef DEBUG_MATHEMU
- printk("In do_mathemu(), emulating %08lx\n", insn);
-#endif
-
- if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
- switch ((insn >> 5) & 0x1ff) {
- case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
- case FADDQ:
- case FSUBQ:
- case FMULQ:
- case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
- case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
- case FQTOS: TYPE(3,1,1,3,1,0,0); break;
- case FQTOD: TYPE(3,2,1,3,1,0,0); break;
- case FITOQ: TYPE(3,3,1,1,0,0,0); break;
- case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
- case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
- case FQTOI: TYPE(3,1,0,3,1,0,0); break;
- case FSQRTS: TYPE(2,1,1,1,1,0,0); break;
- case FSQRTD: TYPE(2,2,1,2,1,0,0); break;
- case FADDD:
- case FSUBD:
- case FMULD:
- case FDIVD: TYPE(2,2,1,2,1,2,1); break;
- case FADDS:
- case FSUBS:
- case FMULS:
- case FDIVS: TYPE(2,1,1,1,1,1,1); break;
- case FSMULD: TYPE(2,2,1,1,1,1,1); break;
- case FDTOS: TYPE(2,1,1,2,1,0,0); break;
- case FSTOD: TYPE(2,2,1,1,1,0,0); break;
- case FSTOI: TYPE(2,1,0,1,1,0,0); break;
- case FDTOI: TYPE(2,1,0,2,1,0,0); break;
- case FITOS: TYPE(2,1,1,1,0,0,0); break;
- case FITOD: TYPE(2,2,1,1,0,0,0); break;
- case FMOVS:
- case FABSS:
- case FNEGS: TYPE(2,1,0,1,0,0,0); break;
- }
- } else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
- switch ((insn >> 5) & 0x1ff) {
- case FCMPS: TYPE(3,0,0,1,1,1,1); break;
- case FCMPES: TYPE(3,0,0,1,1,1,1); break;
- case FCMPD: TYPE(3,0,0,2,1,2,1); break;
- case FCMPED: TYPE(3,0,0,2,1,2,1); break;
- case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
- case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
- }
- }
-
- if (!type) { /* oops, didn't recognise that FPop */
-#ifdef DEBUG_MATHEMU
- printk("attempt to emulate unrecognised FPop!\n");
-#endif
- return 0;
- }
-
- /* Decode the registers to be used */
- freg = (*pfsr >> 14) & 0xf;
-
- *pfsr &= ~0x1c000; /* clear the traptype bits */
-
- freg = ((insn >> 14) & 0x1f);
- switch (type & 0x3) { /* is rs1 single, double or quad? */
- case 3:
- if (freg & 3) { /* quadwords must have bits 4&5 of the */
- /* encoded reg. number set to zero. */
- *pfsr |= (6 << 14);
- return 0; /* simulate invalid_fp_register exception */
- }
- /* fall through */
- case 2:
- if (freg & 1) { /* doublewords must have bit 5 zeroed */
- *pfsr |= (6 << 14);
- return 0;
- }
- }
- rs1 = (argp)&fregs[freg];
- switch (type & 0x7) {
- case 7: FP_UNPACK_QP (QA, rs1); break;
- case 6: FP_UNPACK_DP (DA, rs1); break;
- case 5: FP_UNPACK_SP (SA, rs1); break;
- }
- freg = (insn & 0x1f);
- switch ((type >> 3) & 0x3) { /* same again for rs2 */
- case 3:
- if (freg & 3) { /* quadwords must have bits 4&5 of the */
- /* encoded reg. number set to zero. */
- *pfsr |= (6 << 14);
- return 0; /* simulate invalid_fp_register exception */
- }
- /* fall through */
- case 2:
- if (freg & 1) { /* doublewords must have bit 5 zeroed */
- *pfsr |= (6 << 14);
- return 0;
- }
- }
- rs2 = (argp)&fregs[freg];
- switch ((type >> 3) & 0x7) {
- case 7: FP_UNPACK_QP (QB, rs2); break;
- case 6: FP_UNPACK_DP (DB, rs2); break;
- case 5: FP_UNPACK_SP (SB, rs2); break;
- }
- freg = ((insn >> 25) & 0x1f);
- switch ((type >> 6) & 0x3) { /* and finally rd. This one's a bit different */
- case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */
- if (freg) { /* V8 has only one set of condition codes, so */
- /* anything but 0 in the rd field is an error */
- *pfsr |= (6 << 14); /* (should probably flag as invalid opcode */
- return 0; /* but SIGFPE will do :-> ) */
- }
- break;
- case 3:
- if (freg & 3) { /* quadwords must have bits 4&5 of the */
- /* encoded reg. number set to zero. */
- *pfsr |= (6 << 14);
- return 0; /* simulate invalid_fp_register exception */
- }
- /* fall through */
- case 2:
- if (freg & 1) { /* doublewords must have bit 5 zeroed */
- *pfsr |= (6 << 14);
- return 0;
- }
- /* fall through */
- case 1:
- rd = (void *)&fregs[freg];
- break;
- }
-#ifdef DEBUG_MATHEMU
- printk("executing insn...\n");
-#endif
- /* do the Right Thing */
- switch ((insn >> 5) & 0x1ff) {
- /* + */
- case FADDS: FP_ADD_S (SR, SA, SB); break;
- case FADDD: FP_ADD_D (DR, DA, DB); break;
- case FADDQ: FP_ADD_Q (QR, QA, QB); break;
- /* - */
- case FSUBS: FP_SUB_S (SR, SA, SB); break;
- case FSUBD: FP_SUB_D (DR, DA, DB); break;
- case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
- /* * */
- case FMULS: FP_MUL_S (SR, SA, SB); break;
- case FSMULD: FP_CONV (D, S, 2, 1, DA, SA);
- FP_CONV (D, S, 2, 1, DB, SB);
- case FMULD: FP_MUL_D (DR, DA, DB); break;
- case FDMULQ: FP_CONV (Q, D, 4, 2, QA, DA);
- FP_CONV (Q, D, 4, 2, QB, DB);
- case FMULQ: FP_MUL_Q (QR, QA, QB); break;
- /* / */
- case FDIVS: FP_DIV_S (SR, SA, SB); break;
- case FDIVD: FP_DIV_D (DR, DA, DB); break;
- case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
- /* sqrt */
- case FSQRTS: FP_SQRT_S (SR, SB); break;
- case FSQRTD: FP_SQRT_D (DR, DB); break;
- case FSQRTQ: FP_SQRT_Q (QR, QB); break;
- /* mov */
- case FMOVS: rd->s = rs2->s; break;
- case FABSS: rd->s = rs2->s & 0x7fffffff; break;
- case FNEGS: rd->s = rs2->s ^ 0x80000000; break;
- /* float to int */
- case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
- case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
- case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
- /* int to float */
- case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
- case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
- case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
- /* float to float */
- case FSTOD: FP_CONV (D, S, 2, 1, DR, SB); break;
- case FSTOQ: FP_CONV (Q, S, 4, 1, QR, SB); break;
- case FDTOQ: FP_CONV (Q, D, 4, 2, QR, DB); break;
- case FDTOS: FP_CONV (S, D, 1, 2, SR, DB); break;
- case FQTOS: FP_CONV (S, Q, 1, 4, SR, QB); break;
- case FQTOD: FP_CONV (D, Q, 2, 4, DR, QB); break;
- /* comparison */
- case FCMPS:
- case FCMPES:
- FP_CMP_S(IR, SB, SA, 3);
- if (IR == 3 &&
- (((insn >> 5) & 0x1ff) == FCMPES ||
- FP_ISSIGNAN_S(SA) ||
- FP_ISSIGNAN_S(SB)))
- FP_SET_EXCEPTION (FP_EX_INVALID);
- break;
- case FCMPD:
- case FCMPED:
- FP_CMP_D(IR, DB, DA, 3);
- if (IR == 3 &&
- (((insn >> 5) & 0x1ff) == FCMPED ||
- FP_ISSIGNAN_D(DA) ||
- FP_ISSIGNAN_D(DB)))
- FP_SET_EXCEPTION (FP_EX_INVALID);
- break;
- case FCMPQ:
- case FCMPEQ:
- FP_CMP_Q(IR, QB, QA, 3);
- if (IR == 3 &&
- (((insn >> 5) & 0x1ff) == FCMPEQ ||
- FP_ISSIGNAN_Q(QA) ||
- FP_ISSIGNAN_Q(QB)))
- FP_SET_EXCEPTION (FP_EX_INVALID);
- }
- if (!FP_INHIBIT_RESULTS) {
- switch ((type >> 6) & 0x7) {
- case 0: fsr = *pfsr;
- if (IR == -1) IR = 2;
- /* fcc is always fcc0 */
- fsr &= ~0xc00; fsr |= (IR << 10); break;
- *pfsr = fsr;
- break;
- case 1: rd->s = IR; break;
- case 5: FP_PACK_SP (rd, SR); break;
- case 6: FP_PACK_DP (rd, DR); break;
- case 7: FP_PACK_QP (rd, QR); break;
- }
- }
- if (_fex == 0)
- return 1; /* success! */
- return record_exception(pfsr, _fex);
-}
diff --git a/arch/sparc/math-emu/math_32.c b/arch/sparc/math-emu/math_32.c
new file mode 100644
index 000000000000..e13f65da17df
--- /dev/null
+++ b/arch/sparc/math-emu/math_32.c
@@ -0,0 +1,511 @@
+/*
+ * arch/sparc/math-emu/math.c
+ *
+ * Copyright (C) 1998 Peter Maydell (pmaydell@chiark.greenend.org.uk)
+ * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ * This is a good place to start if you're trying to understand the
+ * emulation code, because it's pretty simple. What we do is
+ * essentially analyse the instruction to work out what the operation
+ * is and which registers are involved. We then execute the appropriate
+ * FXXXX function. [The floating point queue introduces a minor wrinkle;
+ * see below...]
+ * The fxxxxx.c files each emulate a single insn. They look relatively
+ * simple because the complexity is hidden away in an unholy tangle
+ * of preprocessor macros.
+ *
+ * The first layer of macros is single.h, double.h, quad.h. Generally
+ * these files define macros for working with floating point numbers
+ * of the three IEEE formats. FP_ADD_D(R,A,B) is for adding doubles,
+ * for instance. These macros are usually defined as calls to more
+ * generic macros (in this case _FP_ADD(D,2,R,X,Y) where the number
+ * of machine words required to store the given IEEE format is passed
+ * as a parameter. [double.h and co check the number of bits in a word
+ * and define FP_ADD_D & co appropriately].
+ * The generic macros are defined in op-common.h. This is where all
+ * the grotty stuff like handling NaNs is coded. To handle the possible
+ * word sizes macros in op-common.h use macros like _FP_FRAC_SLL_##wc()
+ * where wc is the 'number of machine words' parameter (here 2).
+ * These are defined in the third layer of macros: op-1.h, op-2.h
+ * and op-4.h. These handle operations on floating point numbers composed
+ * of 1,2 and 4 machine words respectively. [For example, on sparc64
+ * doubles are one machine word so macros in double.h eventually use
+ * constructs in op-1.h, but on sparc32 they use op-2.h definitions.]
+ * soft-fp.h is on the same level as op-common.h, and defines some
+ * macros which are independent of both word size and FP format.
+ * Finally, sfp-machine.h is the machine dependent part of the
+ * code: it defines the word size and what type a word is. It also
+ * defines how _FP_MUL_MEAT_t() maps to _FP_MUL_MEAT_n_* : op-n.h
+ * provide several possible flavours of multiply algorithm, most
+ * of which require that you supply some form of asm or C primitive to
+ * do the actual multiply. (such asm primitives should be defined
+ * in sfp-machine.h too). udivmodti4.c is the same sort of thing.
+ *
+ * There may be some errors here because I'm working from a
+ * SPARC architecture manual V9, and what I really want is V8...
+ * Also, the insns which can generate exceptions seem to be a
+ * greater subset of the FPops than for V9 (for example, FCMPED
+ * has to be emulated on V8). So I think I'm going to have
+ * to emulate them all just to be on the safe side...
+ *
+ * Emulation routines originate from soft-fp package, which is
+ * part of glibc and has appropriate copyrights in it (allegedly).
+ *
+ * NB: on sparc int == long == 4 bytes, long long == 8 bytes.
+ * Most bits of the kernel seem to go for long rather than int,
+ * so we follow that practice...
+ */
+
+/* TODO:
+ * fpsave() saves the FP queue but fpload() doesn't reload it.
+ * Therefore when we context switch or change FPU ownership
+ * we have to check to see if the queue had anything in it and
+ * emulate it if it did. This is going to be a pain.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+#include "sfp-util_32.h"
+#include <math-emu/soft-fp.h>
+#include <math-emu/single.h>
+#include <math-emu/double.h>
+#include <math-emu/quad.h>
+
+#define FLOATFUNC(x) extern int x(void *,void *,void *)
+
+/* The Vn labels indicate what version of the SPARC architecture gas thinks
+ * each insn is. This is from the binutils source :->
+ */
+/* quadword instructions */
+#define FSQRTQ 0x02b /* v8 */
+#define FADDQ 0x043 /* v8 */
+#define FSUBQ 0x047 /* v8 */
+#define FMULQ 0x04b /* v8 */
+#define FDIVQ 0x04f /* v8 */
+#define FDMULQ 0x06e /* v8 */
+#define FQTOS 0x0c7 /* v8 */
+#define FQTOD 0x0cb /* v8 */
+#define FITOQ 0x0cc /* v8 */
+#define FSTOQ 0x0cd /* v8 */
+#define FDTOQ 0x0ce /* v8 */
+#define FQTOI 0x0d3 /* v8 */
+#define FCMPQ 0x053 /* v8 */
+#define FCMPEQ 0x057 /* v8 */
+/* single/double instructions (subnormal): should all work */
+#define FSQRTS 0x029 /* v7 */
+#define FSQRTD 0x02a /* v7 */
+#define FADDS 0x041 /* v6 */
+#define FADDD 0x042 /* v6 */
+#define FSUBS 0x045 /* v6 */
+#define FSUBD 0x046 /* v6 */
+#define FMULS 0x049 /* v6 */
+#define FMULD 0x04a /* v6 */
+#define FDIVS 0x04d /* v6 */
+#define FDIVD 0x04e /* v6 */
+#define FSMULD 0x069 /* v6 */
+#define FDTOS 0x0c6 /* v6 */
+#define FSTOD 0x0c9 /* v6 */
+#define FSTOI 0x0d1 /* v6 */
+#define FDTOI 0x0d2 /* v6 */
+#define FABSS 0x009 /* v6 */
+#define FCMPS 0x051 /* v6 */
+#define FCMPES 0x055 /* v6 */
+#define FCMPD 0x052 /* v6 */
+#define FCMPED 0x056 /* v6 */
+#define FMOVS 0x001 /* v6 */
+#define FNEGS 0x005 /* v6 */
+#define FITOS 0x0c4 /* v6 */
+#define FITOD 0x0c8 /* v6 */
+
+#define FSR_TEM_SHIFT 23UL
+#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
+#define FSR_AEXC_SHIFT 5UL
+#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
+#define FSR_CEXC_SHIFT 0UL
+#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
+
+static int do_one_mathemu(u32 insn, unsigned long *fsr, unsigned long *fregs);
+
+/* Unlike the Sparc64 version (which has a struct fpustate), we
+ * pass the taskstruct corresponding to the task which currently owns the
+ * FPU. This is partly because we don't have the fpustate struct and
+ * partly because the task owning the FPU isn't always current (as is
+ * the case for the Sparc64 port). This is probably SMP-related...
+ * This function returns 1 if all queued insns were emulated successfully.
+ * The test for unimplemented FPop in kernel mode has been moved into
+ * kernel/traps.c for simplicity.
+ */
+int do_mathemu(struct pt_regs *regs, struct task_struct *fpt)
+{
+ /* regs->pc isn't necessarily the PC at which the offending insn is sitting.
+ * The FPU maintains a queue of FPops which cause traps.
+ * When it hits an instruction that requires that the trapped op succeeded
+ * (usually because it reads a reg. that the trapped op wrote) then it
+ * causes this exception. We need to emulate all the insns on the queue
+ * and then allow the op to proceed.
+ * This code should also handle the case where the trap was precise,
+ * in which case the queue length is zero and regs->pc points at the
+ * single FPop to be emulated. (this case is untested, though :->)
+ * You'll need this case if you want to be able to emulate all FPops
+ * because the FPU either doesn't exist or has been software-disabled.
+ * [The UltraSPARC makes FP a precise trap; this isn't as stupid as it
+ * might sound because the Ultra does funky things with a superscalar
+ * architecture.]
+ */
+
+ /* You wouldn't believe how often I typed 'ftp' when I meant 'fpt' :-> */
+
+ int i;
+ int retcode = 0; /* assume all succeed */
+ unsigned long insn;
+
+#ifdef DEBUG_MATHEMU
+ printk("In do_mathemu()... pc is %08lx\n", regs->pc);
+ printk("fpqdepth is %ld\n", fpt->thread.fpqdepth);
+ for (i = 0; i < fpt->thread.fpqdepth; i++)
+ printk("%d: %08lx at %08lx\n", i, fpt->thread.fpqueue[i].insn,
+ (unsigned long)fpt->thread.fpqueue[i].insn_addr);
+#endif
+
+ if (fpt->thread.fpqdepth == 0) { /* no queue, guilty insn is at regs->pc */
+#ifdef DEBUG_MATHEMU
+ printk("precise trap at %08lx\n", regs->pc);
+#endif
+ if (!get_user(insn, (u32 __user *) regs->pc)) {
+ retcode = do_one_mathemu(insn, &fpt->thread.fsr, fpt->thread.float_regs);
+ if (retcode) {
+ /* in this case we need to fix up PC & nPC */
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ }
+ }
+ return retcode;
+ }
+
+ /* Normal case: need to empty the queue... */
+ for (i = 0; i < fpt->thread.fpqdepth; i++) {
+ retcode = do_one_mathemu(fpt->thread.fpqueue[i].insn, &(fpt->thread.fsr), fpt->thread.float_regs);
+ if (!retcode) /* insn failed, no point doing any more */
+ break;
+ }
+ /* Now empty the queue and clear the queue_not_empty flag */
+ if (retcode)
+ fpt->thread.fsr &= ~(0x3000 | FSR_CEXC_MASK);
+ else
+ fpt->thread.fsr &= ~0x3000;
+ fpt->thread.fpqdepth = 0;
+
+ return retcode;
+}
+
+/* All routines returning an exception to raise should detect
+ * such exceptions _before_ rounding to be consistent with
+ * the behavior of the hardware in the implemented cases
+ * (and thus with the recommendations in the V9 architecture
+ * manual).
+ *
+ * We return 0 if a SIGFPE should be sent, 1 otherwise.
+ */
+static inline int record_exception(unsigned long *pfsr, int eflag)
+{
+ unsigned long fsr = *pfsr;
+ int would_trap;
+
+ /* Determine if this exception would have generated a trap. */
+ would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
+
+ /* If trapping, we only want to signal one bit. */
+ if (would_trap != 0) {
+ eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
+ if ((eflag & (eflag - 1)) != 0) {
+ if (eflag & FP_EX_INVALID)
+ eflag = FP_EX_INVALID;
+ else if (eflag & FP_EX_OVERFLOW)
+ eflag = FP_EX_OVERFLOW;
+ else if (eflag & FP_EX_UNDERFLOW)
+ eflag = FP_EX_UNDERFLOW;
+ else if (eflag & FP_EX_DIVZERO)
+ eflag = FP_EX_DIVZERO;
+ else if (eflag & FP_EX_INEXACT)
+ eflag = FP_EX_INEXACT;
+ }
+ }
+
+ /* Set CEXC, here is the rule:
+ *
+ * In general all FPU ops will set one and only one
+ * bit in the CEXC field, this is always the case
+ * when the IEEE exception trap is enabled in TEM.
+ */
+ fsr &= ~(FSR_CEXC_MASK);
+ fsr |= ((long)eflag << FSR_CEXC_SHIFT);
+
+ /* Set the AEXC field, rule is:
+ *
+ * If a trap would not be generated, the
+ * CEXC just generated is OR'd into the
+ * existing value of AEXC.
+ */
+ if (would_trap == 0)
+ fsr |= ((long)eflag << FSR_AEXC_SHIFT);
+
+ /* If trapping, indicate fault trap type IEEE. */
+ if (would_trap != 0)
+ fsr |= (1UL << 14);
+
+ *pfsr = fsr;
+
+ return (would_trap ? 0 : 1);
+}
+
+typedef union {
+ u32 s;
+ u64 d;
+ u64 q[2];
+} *argp;
+
+static int do_one_mathemu(u32 insn, unsigned long *pfsr, unsigned long *fregs)
+{
+ /* Emulate the given insn, updating fsr and fregs appropriately. */
+ int type = 0;
+ /* r is rd, b is rs2 and a is rs1. The *u arg tells
+ whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
+ non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
+#define TYPE(dummy, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6)
+ int freg;
+ argp rs1 = NULL, rs2 = NULL, rd = NULL;
+ FP_DECL_EX;
+ FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
+ FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
+ FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
+ int IR;
+ long fsr;
+
+#ifdef DEBUG_MATHEMU
+ printk("In do_mathemu(), emulating %08lx\n", insn);
+#endif
+
+ if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
+ switch ((insn >> 5) & 0x1ff) {
+ case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
+ case FADDQ:
+ case FSUBQ:
+ case FMULQ:
+ case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
+ case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
+ case FQTOS: TYPE(3,1,1,3,1,0,0); break;
+ case FQTOD: TYPE(3,2,1,3,1,0,0); break;
+ case FITOQ: TYPE(3,3,1,1,0,0,0); break;
+ case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
+ case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
+ case FQTOI: TYPE(3,1,0,3,1,0,0); break;
+ case FSQRTS: TYPE(2,1,1,1,1,0,0); break;
+ case FSQRTD: TYPE(2,2,1,2,1,0,0); break;
+ case FADDD:
+ case FSUBD:
+ case FMULD:
+ case FDIVD: TYPE(2,2,1,2,1,2,1); break;
+ case FADDS:
+ case FSUBS:
+ case FMULS:
+ case FDIVS: TYPE(2,1,1,1,1,1,1); break;
+ case FSMULD: TYPE(2,2,1,1,1,1,1); break;
+ case FDTOS: TYPE(2,1,1,2,1,0,0); break;
+ case FSTOD: TYPE(2,2,1,1,1,0,0); break;
+ case FSTOI: TYPE(2,1,0,1,1,0,0); break;
+ case FDTOI: TYPE(2,1,0,2,1,0,0); break;
+ case FITOS: TYPE(2,1,1,1,0,0,0); break;
+ case FITOD: TYPE(2,2,1,1,0,0,0); break;
+ case FMOVS:
+ case FABSS:
+ case FNEGS: TYPE(2,1,0,1,0,0,0); break;
+ }
+ } else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
+ switch ((insn >> 5) & 0x1ff) {
+ case FCMPS: TYPE(3,0,0,1,1,1,1); break;
+ case FCMPES: TYPE(3,0,0,1,1,1,1); break;
+ case FCMPD: TYPE(3,0,0,2,1,2,1); break;
+ case FCMPED: TYPE(3,0,0,2,1,2,1); break;
+ case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
+ case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
+ }
+ }
+
+ if (!type) { /* oops, didn't recognise that FPop */
+#ifdef DEBUG_MATHEMU
+ printk("attempt to emulate unrecognised FPop!\n");
+#endif
+ return 0;
+ }
+
+ /* Decode the registers to be used */
+ freg = (*pfsr >> 14) & 0xf;
+
+ *pfsr &= ~0x1c000; /* clear the traptype bits */
+
+ freg = ((insn >> 14) & 0x1f);
+ switch (type & 0x3) { /* is rs1 single, double or quad? */
+ case 3:
+ if (freg & 3) { /* quadwords must have bits 4&5 of the */
+ /* encoded reg. number set to zero. */
+ *pfsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) { /* doublewords must have bit 5 zeroed */
+ *pfsr |= (6 << 14);
+ return 0;
+ }
+ }
+ rs1 = (argp)&fregs[freg];
+ switch (type & 0x7) {
+ case 7: FP_UNPACK_QP (QA, rs1); break;
+ case 6: FP_UNPACK_DP (DA, rs1); break;
+ case 5: FP_UNPACK_SP (SA, rs1); break;
+ }
+ freg = (insn & 0x1f);
+ switch ((type >> 3) & 0x3) { /* same again for rs2 */
+ case 3:
+ if (freg & 3) { /* quadwords must have bits 4&5 of the */
+ /* encoded reg. number set to zero. */
+ *pfsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) { /* doublewords must have bit 5 zeroed */
+ *pfsr |= (6 << 14);
+ return 0;
+ }
+ }
+ rs2 = (argp)&fregs[freg];
+ switch ((type >> 3) & 0x7) {
+ case 7: FP_UNPACK_QP (QB, rs2); break;
+ case 6: FP_UNPACK_DP (DB, rs2); break;
+ case 5: FP_UNPACK_SP (SB, rs2); break;
+ }
+ freg = ((insn >> 25) & 0x1f);
+ switch ((type >> 6) & 0x3) { /* and finally rd. This one's a bit different */
+ case 0: /* dest is fcc. (this must be FCMPQ or FCMPEQ) */
+ if (freg) { /* V8 has only one set of condition codes, so */
+ /* anything but 0 in the rd field is an error */
+ *pfsr |= (6 << 14); /* (should probably flag as invalid opcode */
+ return 0; /* but SIGFPE will do :-> ) */
+ }
+ break;
+ case 3:
+ if (freg & 3) { /* quadwords must have bits 4&5 of the */
+ /* encoded reg. number set to zero. */
+ *pfsr |= (6 << 14);
+ return 0; /* simulate invalid_fp_register exception */
+ }
+ /* fall through */
+ case 2:
+ if (freg & 1) { /* doublewords must have bit 5 zeroed */
+ *pfsr |= (6 << 14);
+ return 0;
+ }
+ /* fall through */
+ case 1:
+ rd = (void *)&fregs[freg];
+ break;
+ }
+#ifdef DEBUG_MATHEMU
+ printk("executing insn...\n");
+#endif
+ /* do the Right Thing */
+ switch ((insn >> 5) & 0x1ff) {
+ /* + */
+ case FADDS: FP_ADD_S (SR, SA, SB); break;
+ case FADDD: FP_ADD_D (DR, DA, DB); break;
+ case FADDQ: FP_ADD_Q (QR, QA, QB); break;
+ /* - */
+ case FSUBS: FP_SUB_S (SR, SA, SB); break;
+ case FSUBD: FP_SUB_D (DR, DA, DB); break;
+ case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
+ /* * */
+ case FMULS: FP_MUL_S (SR, SA, SB); break;
+ case FSMULD: FP_CONV (D, S, 2, 1, DA, SA);
+ FP_CONV (D, S, 2, 1, DB, SB);
+ case FMULD: FP_MUL_D (DR, DA, DB); break;
+ case FDMULQ: FP_CONV (Q, D, 4, 2, QA, DA);
+ FP_CONV (Q, D, 4, 2, QB, DB);
+ case FMULQ: FP_MUL_Q (QR, QA, QB); break;
+ /* / */
+ case FDIVS: FP_DIV_S (SR, SA, SB); break;
+ case FDIVD: FP_DIV_D (DR, DA, DB); break;
+ case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
+ /* sqrt */
+ case FSQRTS: FP_SQRT_S (SR, SB); break;
+ case FSQRTD: FP_SQRT_D (DR, DB); break;
+ case FSQRTQ: FP_SQRT_Q (QR, QB); break;
+ /* mov */
+ case FMOVS: rd->s = rs2->s; break;
+ case FABSS: rd->s = rs2->s & 0x7fffffff; break;
+ case FNEGS: rd->s = rs2->s ^ 0x80000000; break;
+ /* float to int */
+ case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
+ case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
+ case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
+ /* int to float */
+ case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
+ case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
+ case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
+ /* float to float */
+ case FSTOD: FP_CONV (D, S, 2, 1, DR, SB); break;
+ case FSTOQ: FP_CONV (Q, S, 4, 1, QR, SB); break;
+ case FDTOQ: FP_CONV (Q, D, 4, 2, QR, DB); break;
+ case FDTOS: FP_CONV (S, D, 1, 2, SR, DB); break;
+ case FQTOS: FP_CONV (S, Q, 1, 4, SR, QB); break;
+ case FQTOD: FP_CONV (D, Q, 2, 4, DR, QB); break;
+ /* comparison */
+ case FCMPS:
+ case FCMPES:
+ FP_CMP_S(IR, SB, SA, 3);
+ if (IR == 3 &&
+ (((insn >> 5) & 0x1ff) == FCMPES ||
+ FP_ISSIGNAN_S(SA) ||
+ FP_ISSIGNAN_S(SB)))
+ FP_SET_EXCEPTION (FP_EX_INVALID);
+ break;
+ case FCMPD:
+ case FCMPED:
+ FP_CMP_D(IR, DB, DA, 3);
+ if (IR == 3 &&
+ (((insn >> 5) & 0x1ff) == FCMPED ||
+ FP_ISSIGNAN_D(DA) ||
+ FP_ISSIGNAN_D(DB)))
+ FP_SET_EXCEPTION (FP_EX_INVALID);
+ break;
+ case FCMPQ:
+ case FCMPEQ:
+ FP_CMP_Q(IR, QB, QA, 3);
+ if (IR == 3 &&
+ (((insn >> 5) & 0x1ff) == FCMPEQ ||
+ FP_ISSIGNAN_Q(QA) ||
+ FP_ISSIGNAN_Q(QB)))
+ FP_SET_EXCEPTION (FP_EX_INVALID);
+ }
+ if (!FP_INHIBIT_RESULTS) {
+ switch ((type >> 6) & 0x7) {
+ case 0: fsr = *pfsr;
+ if (IR == -1) IR = 2;
+ /* fcc is always fcc0 */
+ fsr &= ~0xc00; fsr |= (IR << 10); break;
+ *pfsr = fsr;
+ break;
+ case 1: rd->s = IR; break;
+ case 5: FP_PACK_SP (rd, SR); break;
+ case 6: FP_PACK_DP (rd, DR); break;
+ case 7: FP_PACK_QP (rd, QR); break;
+ }
+ }
+ if (_fex == 0)
+ return 1; /* success! */
+ return record_exception(pfsr, _fex);
+}
diff --git a/arch/sparc/math-emu/math_64.c b/arch/sparc/math-emu/math_64.c
new file mode 100644
index 000000000000..6863c9bde25c
--- /dev/null
+++ b/arch/sparc/math-emu/math_64.c
@@ -0,0 +1,513 @@
+/*
+ * arch/sparc64/math-emu/math.c
+ *
+ * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1999 David S. Miller (davem@redhat.com)
+ *
+ * Emulation routines originate from soft-fp package, which is part
+ * of glibc and has appropriate copyrights in it.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+
+#include <asm/fpumacro.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+
+#include "sfp-util_64.h"
+#include <math-emu/soft-fp.h>
+#include <math-emu/single.h>
+#include <math-emu/double.h>
+#include <math-emu/quad.h>
+
+/* QUAD - ftt == 3 */
+#define FMOVQ 0x003
+#define FNEGQ 0x007
+#define FABSQ 0x00b
+#define FSQRTQ 0x02b
+#define FADDQ 0x043
+#define FSUBQ 0x047
+#define FMULQ 0x04b
+#define FDIVQ 0x04f
+#define FDMULQ 0x06e
+#define FQTOX 0x083
+#define FXTOQ 0x08c
+#define FQTOS 0x0c7
+#define FQTOD 0x0cb
+#define FITOQ 0x0cc
+#define FSTOQ 0x0cd
+#define FDTOQ 0x0ce
+#define FQTOI 0x0d3
+/* SUBNORMAL - ftt == 2 */
+#define FSQRTS 0x029
+#define FSQRTD 0x02a
+#define FADDS 0x041
+#define FADDD 0x042
+#define FSUBS 0x045
+#define FSUBD 0x046
+#define FMULS 0x049
+#define FMULD 0x04a
+#define FDIVS 0x04d
+#define FDIVD 0x04e
+#define FSMULD 0x069
+#define FSTOX 0x081
+#define FDTOX 0x082
+#define FDTOS 0x0c6
+#define FSTOD 0x0c9
+#define FSTOI 0x0d1
+#define FDTOI 0x0d2
+#define FXTOS 0x084 /* Only Ultra-III generates this. */
+#define FXTOD 0x088 /* Only Ultra-III generates this. */
+#if 0 /* Optimized inline in sparc64/kernel/entry.S */
+#define FITOS 0x0c4 /* Only Ultra-III generates this. */
+#endif
+#define FITOD 0x0c8 /* Only Ultra-III generates this. */
+/* FPOP2 */
+#define FCMPQ 0x053
+#define FCMPEQ 0x057
+#define FMOVQ0 0x003
+#define FMOVQ1 0x043
+#define FMOVQ2 0x083
+#define FMOVQ3 0x0c3
+#define FMOVQI 0x103
+#define FMOVQX 0x183
+#define FMOVQZ 0x027
+#define FMOVQLE 0x047
+#define FMOVQLZ 0x067
+#define FMOVQNZ 0x0a7
+#define FMOVQGZ 0x0c7
+#define FMOVQGE 0x0e7
+
+#define FSR_TEM_SHIFT 23UL
+#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
+#define FSR_AEXC_SHIFT 5UL
+#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
+#define FSR_CEXC_SHIFT 0UL
+#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
+
+/* All routines returning an exception to raise should detect
+ * such exceptions _before_ rounding to be consistent with
+ * the behavior of the hardware in the implemented cases
+ * (and thus with the recommendations in the V9 architecture
+ * manual).
+ *
+ * We return 0 if a SIGFPE should be sent, 1 otherwise.
+ */
+static inline int record_exception(struct pt_regs *regs, int eflag)
+{
+ u64 fsr = current_thread_info()->xfsr[0];
+ int would_trap;
+
+ /* Determine if this exception would have generated a trap. */
+ would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
+
+ /* If trapping, we only want to signal one bit. */
+ if(would_trap != 0) {
+ eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
+ if((eflag & (eflag - 1)) != 0) {
+ if(eflag & FP_EX_INVALID)
+ eflag = FP_EX_INVALID;
+ else if(eflag & FP_EX_OVERFLOW)
+ eflag = FP_EX_OVERFLOW;
+ else if(eflag & FP_EX_UNDERFLOW)
+ eflag = FP_EX_UNDERFLOW;
+ else if(eflag & FP_EX_DIVZERO)
+ eflag = FP_EX_DIVZERO;
+ else if(eflag & FP_EX_INEXACT)
+ eflag = FP_EX_INEXACT;
+ }
+ }
+
+ /* Set CEXC, here is the rule:
+ *
+ * In general all FPU ops will set one and only one
+ * bit in the CEXC field, this is always the case
+ * when the IEEE exception trap is enabled in TEM.
+ */
+ fsr &= ~(FSR_CEXC_MASK);
+ fsr |= ((long)eflag << FSR_CEXC_SHIFT);
+
+ /* Set the AEXC field, rule is:
+ *
+ * If a trap would not be generated, the
+ * CEXC just generated is OR'd into the
+ * existing value of AEXC.
+ */
+ if(would_trap == 0)
+ fsr |= ((long)eflag << FSR_AEXC_SHIFT);
+
+ /* If trapping, indicate fault trap type IEEE. */
+ if(would_trap != 0)
+ fsr |= (1UL << 14);
+
+ current_thread_info()->xfsr[0] = fsr;
+
+ /* If we will not trap, advance the program counter over
+ * the instruction being handled.
+ */
+ if(would_trap == 0) {
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+ }
+
+ return (would_trap ? 0 : 1);
+}
+
+typedef union {
+ u32 s;
+ u64 d;
+ u64 q[2];
+} *argp;
+
+int do_mathemu(struct pt_regs *regs, struct fpustate *f)
+{
+ unsigned long pc = regs->tpc;
+ unsigned long tstate = regs->tstate;
+ u32 insn = 0;
+ int type = 0;
+ /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
+ whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
+ non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
+#define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
+ int freg;
+ static u64 zero[2] = { 0L, 0L };
+ int flags;
+ FP_DECL_EX;
+ FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
+ FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
+ FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
+ int IR;
+ long XR, xfsr;
+
+ if (tstate & TSTATE_PRIV)
+ die_if_kernel("unfinished/unimplemented FPop from kernel", regs);
+ if (test_thread_flag(TIF_32BIT))
+ pc = (u32)pc;
+ if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
+ if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
+ switch ((insn >> 5) & 0x1ff) {
+ /* QUAD - ftt == 3 */
+ case FMOVQ:
+ case FNEGQ:
+ case FABSQ: TYPE(3,3,0,3,0,0,0); break;
+ case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
+ case FADDQ:
+ case FSUBQ:
+ case FMULQ:
+ case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
+ case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
+ case FQTOX: TYPE(3,2,0,3,1,0,0); break;
+ case FXTOQ: TYPE(3,3,1,2,0,0,0); break;
+ case FQTOS: TYPE(3,1,1,3,1,0,0); break;
+ case FQTOD: TYPE(3,2,1,3,1,0,0); break;
+ case FITOQ: TYPE(3,3,1,1,0,0,0); break;
+ case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
+ case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
+ case FQTOI: TYPE(3,1,0,3,1,0,0); break;
+
+ /* We can get either unimplemented or unfinished
+ * for these cases. Pre-Niagara systems generate
+ * unfinished fpop for SUBNORMAL cases, and Niagara
+ * always gives unimplemented fpop for fsqrt{s,d}.
+ */
+ case FSQRTS: {
+ unsigned long x = current_thread_info()->xfsr[0];
+
+ x = (x >> 14) & 0xf;
+ TYPE(x,1,1,1,1,0,0);
+ break;
+ }
+
+ case FSQRTD: {
+ unsigned long x = current_thread_info()->xfsr[0];
+
+ x = (x >> 14) & 0xf;
+ TYPE(x,2,1,2,1,0,0);
+ break;
+ }
+
+ /* SUBNORMAL - ftt == 2 */
+ case FADDD:
+ case FSUBD:
+ case FMULD:
+ case FDIVD: TYPE(2,2,1,2,1,2,1); break;
+ case FADDS:
+ case FSUBS:
+ case FMULS:
+ case FDIVS: TYPE(2,1,1,1,1,1,1); break;
+ case FSMULD: TYPE(2,2,1,1,1,1,1); break;
+ case FSTOX: TYPE(2,2,0,1,1,0,0); break;
+ case FDTOX: TYPE(2,2,0,2,1,0,0); break;
+ case FDTOS: TYPE(2,1,1,2,1,0,0); break;
+ case FSTOD: TYPE(2,2,1,1,1,0,0); break;
+ case FSTOI: TYPE(2,1,0,1,1,0,0); break;
+ case FDTOI: TYPE(2,1,0,2,1,0,0); break;
+
+ /* Only Ultra-III generates these */
+ case FXTOS: TYPE(2,1,1,2,0,0,0); break;
+ case FXTOD: TYPE(2,2,1,2,0,0,0); break;
+#if 0 /* Optimized inline in sparc64/kernel/entry.S */
+ case FITOS: TYPE(2,1,1,1,0,0,0); break;
+#endif
+ case FITOD: TYPE(2,2,1,1,0,0,0); break;
+ }
+ }
+ else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
+ IR = 2;
+ switch ((insn >> 5) & 0x1ff) {
+ case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
+ case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
+ /* Now the conditional fmovq support */
+ case FMOVQ0:
+ case FMOVQ1:
+ case FMOVQ2:
+ case FMOVQ3:
+ /* fmovq %fccX, %fY, %fZ */
+ if (!((insn >> 11) & 3))
+ XR = current_thread_info()->xfsr[0] >> 10;
+ else
+ XR = current_thread_info()->xfsr[0] >> (30 + ((insn >> 10) & 0x6));
+ XR &= 3;
+ IR = 0;
+ switch ((insn >> 14) & 0x7) {
+ /* case 0: IR = 0; break; */ /* Never */
+ case 1: if (XR) IR = 1; break; /* Not Equal */
+ case 2: if (XR == 1 || XR == 2) IR = 1; break; /* Less or Greater */
+ case 3: if (XR & 1) IR = 1; break; /* Unordered or Less */
+ case 4: if (XR == 1) IR = 1; break; /* Less */
+ case 5: if (XR & 2) IR = 1; break; /* Unordered or Greater */
+ case 6: if (XR == 2) IR = 1; break; /* Greater */
+ case 7: if (XR == 3) IR = 1; break; /* Unordered */
+ }
+ if ((insn >> 14) & 8)
+ IR ^= 1;
+ break;
+ case FMOVQI:
+ case FMOVQX:
+ /* fmovq %[ix]cc, %fY, %fZ */
+ XR = regs->tstate >> 32;
+ if ((insn >> 5) & 0x80)
+ XR >>= 4;
+ XR &= 0xf;
+ IR = 0;
+ freg = ((XR >> 2) ^ XR) & 2;
+ switch ((insn >> 14) & 0x7) {
+ /* case 0: IR = 0; break; */ /* Never */
+ case 1: if (XR & 4) IR = 1; break; /* Equal */
+ case 2: if ((XR & 4) || freg) IR = 1; break; /* Less or Equal */
+ case 3: if (freg) IR = 1; break; /* Less */
+ case 4: if (XR & 5) IR = 1; break; /* Less or Equal Unsigned */
+ case 5: if (XR & 1) IR = 1; break; /* Carry Set */
+ case 6: if (XR & 8) IR = 1; break; /* Negative */
+ case 7: if (XR & 2) IR = 1; break; /* Overflow Set */
+ }
+ if ((insn >> 14) & 8)
+ IR ^= 1;
+ break;
+ case FMOVQZ:
+ case FMOVQLE:
+ case FMOVQLZ:
+ case FMOVQNZ:
+ case FMOVQGZ:
+ case FMOVQGE:
+ freg = (insn >> 14) & 0x1f;
+ if (!freg)
+ XR = 0;
+ else if (freg < 16)
+ XR = regs->u_regs[freg];
+ else if (test_thread_flag(TIF_32BIT)) {
+ struct reg_window32 __user *win32;
+ flushw_user ();
+ win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ get_user(XR, &win32->locals[freg - 16]);
+ } else {
+ struct reg_window __user *win;
+ flushw_user ();
+ win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ get_user(XR, &win->locals[freg - 16]);
+ }
+ IR = 0;
+ switch ((insn >> 10) & 3) {
+ case 1: if (!XR) IR = 1; break; /* Register Zero */
+ case 2: if (XR <= 0) IR = 1; break; /* Register Less Than or Equal to Zero */
+ case 3: if (XR < 0) IR = 1; break; /* Register Less Than Zero */
+ }
+ if ((insn >> 10) & 4)
+ IR ^= 1;
+ break;
+ }
+ if (IR == 0) {
+ /* The fmov test was false. Do a nop instead */
+ current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+ return 1;
+ } else if (IR == 1) {
+ /* Change the instruction into plain fmovq */
+ insn = (insn & 0x3e00001f) | 0x81a00060;
+ TYPE(3,3,0,3,0,0,0);
+ }
+ }
+ }
+ if (type) {
+ argp rs1 = NULL, rs2 = NULL, rd = NULL;
+
+ freg = (current_thread_info()->xfsr[0] >> 14) & 0xf;
+ if (freg != (type >> 9))
+ goto err;
+ current_thread_info()->xfsr[0] &= ~0x1c000;
+ freg = ((insn >> 14) & 0x1f);
+ switch (type & 0x3) {
+ case 3: if (freg & 2) {
+ current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
+ goto err;
+ }
+ case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+ case 1: rs1 = (argp)&f->regs[freg];
+ flags = (freg < 32) ? FPRS_DL : FPRS_DU;
+ if (!(current_thread_info()->fpsaved[0] & flags))
+ rs1 = (argp)&zero;
+ break;
+ }
+ switch (type & 0x7) {
+ case 7: FP_UNPACK_QP (QA, rs1); break;
+ case 6: FP_UNPACK_DP (DA, rs1); break;
+ case 5: FP_UNPACK_SP (SA, rs1); break;
+ }
+ freg = (insn & 0x1f);
+ switch ((type >> 3) & 0x3) {
+ case 3: if (freg & 2) {
+ current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
+ goto err;
+ }
+ case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+ case 1: rs2 = (argp)&f->regs[freg];
+ flags = (freg < 32) ? FPRS_DL : FPRS_DU;
+ if (!(current_thread_info()->fpsaved[0] & flags))
+ rs2 = (argp)&zero;
+ break;
+ }
+ switch ((type >> 3) & 0x7) {
+ case 7: FP_UNPACK_QP (QB, rs2); break;
+ case 6: FP_UNPACK_DP (DB, rs2); break;
+ case 5: FP_UNPACK_SP (SB, rs2); break;
+ }
+ freg = ((insn >> 25) & 0x1f);
+ switch ((type >> 6) & 0x3) {
+ case 3: if (freg & 2) {
+ current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
+ goto err;
+ }
+ case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
+ case 1: rd = (argp)&f->regs[freg];
+ flags = (freg < 32) ? FPRS_DL : FPRS_DU;
+ if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) {
+ current_thread_info()->fpsaved[0] = FPRS_FEF;
+ current_thread_info()->gsr[0] = 0;
+ }
+ if (!(current_thread_info()->fpsaved[0] & flags)) {
+ if (freg < 32)
+ memset(f->regs, 0, 32*sizeof(u32));
+ else
+ memset(f->regs+32, 0, 32*sizeof(u32));
+ }
+ current_thread_info()->fpsaved[0] |= flags;
+ break;
+ }
+ switch ((insn >> 5) & 0x1ff) {
+ /* + */
+ case FADDS: FP_ADD_S (SR, SA, SB); break;
+ case FADDD: FP_ADD_D (DR, DA, DB); break;
+ case FADDQ: FP_ADD_Q (QR, QA, QB); break;
+ /* - */
+ case FSUBS: FP_SUB_S (SR, SA, SB); break;
+ case FSUBD: FP_SUB_D (DR, DA, DB); break;
+ case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
+ /* * */
+ case FMULS: FP_MUL_S (SR, SA, SB); break;
+ case FSMULD: FP_CONV (D, S, 1, 1, DA, SA);
+ FP_CONV (D, S, 1, 1, DB, SB);
+ case FMULD: FP_MUL_D (DR, DA, DB); break;
+ case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA);
+ FP_CONV (Q, D, 2, 1, QB, DB);
+ case FMULQ: FP_MUL_Q (QR, QA, QB); break;
+ /* / */
+ case FDIVS: FP_DIV_S (SR, SA, SB); break;
+ case FDIVD: FP_DIV_D (DR, DA, DB); break;
+ case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
+ /* sqrt */
+ case FSQRTS: FP_SQRT_S (SR, SB); break;
+ case FSQRTD: FP_SQRT_D (DR, DB); break;
+ case FSQRTQ: FP_SQRT_Q (QR, QB); break;
+ /* mov */
+ case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break;
+ case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break;
+ case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break;
+ /* float to int */
+ case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
+ case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
+ case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
+ case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break;
+ case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break;
+ case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break;
+ /* int to float */
+ case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
+ case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break;
+ /* Only Ultra-III generates these */
+ case FXTOS: XR = rs2->d; FP_FROM_INT_S (SR, XR, 64, long); break;
+ case FXTOD: XR = rs2->d; FP_FROM_INT_D (DR, XR, 64, long); break;
+#if 0 /* Optimized inline in sparc64/kernel/entry.S */
+ case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
+#endif
+ case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
+ /* float to float */
+ case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break;
+ case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break;
+ case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break;
+ case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break;
+ case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break;
+ case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break;
+ /* comparison */
+ case FCMPQ:
+ case FCMPEQ:
+ FP_CMP_Q(XR, QB, QA, 3);
+ if (XR == 3 &&
+ (((insn >> 5) & 0x1ff) == FCMPEQ ||
+ FP_ISSIGNAN_Q(QA) ||
+ FP_ISSIGNAN_Q(QB)))
+ FP_SET_EXCEPTION (FP_EX_INVALID);
+ }
+ if (!FP_INHIBIT_RESULTS) {
+ switch ((type >> 6) & 0x7) {
+ case 0: xfsr = current_thread_info()->xfsr[0];
+ if (XR == -1) XR = 2;
+ switch (freg & 3) {
+ /* fcc0, 1, 2, 3 */
+ case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break;
+ case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break;
+ case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break;
+ case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break;
+ }
+ current_thread_info()->xfsr[0] = xfsr;
+ break;
+ case 1: rd->s = IR; break;
+ case 2: rd->d = XR; break;
+ case 5: FP_PACK_SP (rd, SR); break;
+ case 6: FP_PACK_DP (rd, DR); break;
+ case 7: FP_PACK_QP (rd, QR); break;
+ }
+ }
+
+ if(_fex != 0)
+ return record_exception(regs, _fex);
+
+ /* Success and no exceptions detected. */
+ current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+ return 1;
+ }
+err: return 0;
+}
diff --git a/arch/sparc/math-emu/sfp-util.h b/arch/sparc/math-emu/sfp-util_32.h
index d1b2aff3c259..d1b2aff3c259 100644
--- a/arch/sparc/math-emu/sfp-util.h
+++ b/arch/sparc/math-emu/sfp-util_32.h
diff --git a/arch/sparc64/math-emu/sfp-util.h b/arch/sparc/math-emu/sfp-util_64.h
index 425d3cf01af4..425d3cf01af4 100644
--- a/arch/sparc64/math-emu/sfp-util.h
+++ b/arch/sparc/math-emu/sfp-util_64.h
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
index ea88955d97ff..681abe0a4594 100644
--- a/arch/sparc/mm/Makefile
+++ b/arch/sparc/mm/Makefile
@@ -1,17 +1,25 @@
# Makefile for the linux Sparc-specific parts of the memory manager.
#
-EXTRA_AFLAGS := -ansi
+asflags-y := -ansi
+ccflags-y := -Werror
-obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o \
- srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
+obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o
+obj-y += fault_$(BITS).o
+obj-y += init_$(BITS).o
+obj-$(CONFIG_SPARC32) += loadmmu.o
+obj-y += generic_$(BITS).o
+obj-$(CONFIG_SPARC32) += extable.o btfixup.o srmmu.o iommu.o io-unit.o
+obj-$(CONFIG_SPARC32) += hypersparc.o viking.o tsunami.o swift.o
-ifdef CONFIG_HIGHMEM
-obj-y += highmem.o
-endif
+# Only used by sparc64
+obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
+
+# Only used by sparc32
+obj-$(CONFIG_HIGHMEM) += highmem.o
ifdef CONFIG_SMP
-obj-y += nosun4c.o
+obj-$(CONFIG_SPARC32) += nosun4c.o
else
-obj-y += sun4c.o
+obj-$(CONFIG_SPARC32) += sun4c.o
endif
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault_32.c
index a507e1174662..a507e1174662 100644
--- a/arch/sparc/mm/fault.c
+++ b/arch/sparc/mm/fault_32.c
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc/mm/fault_64.c
index a9e474bf6385..a9e474bf6385 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc/mm/fault_64.c
diff --git a/arch/sparc/mm/generic.c b/arch/sparc/mm/generic_32.c
index a289261da9fd..a289261da9fd 100644
--- a/arch/sparc/mm/generic.c
+++ b/arch/sparc/mm/generic_32.c
diff --git a/arch/sparc64/mm/generic.c b/arch/sparc/mm/generic_64.c
index f362c2037013..f362c2037013 100644
--- a/arch/sparc64/mm/generic.c
+++ b/arch/sparc/mm/generic_64.c
diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c
index f27d10369e0c..f27d10369e0c 100644
--- a/arch/sparc64/mm/hugetlbpage.c
+++ b/arch/sparc/mm/hugetlbpage.c
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
deleted file mode 100644
index 677c1e187a23..000000000000
--- a/arch/sparc/mm/init.c
+++ /dev/null
@@ -1,524 +0,0 @@
-/*
- * linux/arch/sparc/mm/init.c
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
- */
-
-#include <linux/module.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/ptrace.h>
-#include <linux/mman.h>
-#include <linux/mm.h>
-#include <linux/swap.h>
-#include <linux/initrd.h>
-#include <linux/init.h>
-#include <linux/highmem.h>
-#include <linux/bootmem.h>
-#include <linux/pagemap.h>
-#include <linux/poison.h>
-
-#include <asm/system.h>
-#include <asm/vac-ops.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/vaddrs.h>
-#include <asm/pgalloc.h> /* bug in asm-generic/tlb.h: check_pgt_cache */
-#include <asm/tlb.h>
-#include <asm/prom.h>
-
-DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
-
-unsigned long *sparc_valid_addr_bitmap;
-
-unsigned long phys_base;
-unsigned long pfn_base;
-
-unsigned long page_kernel;
-
-struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS+1];
-unsigned long sparc_unmapped_base;
-
-struct pgtable_cache_struct pgt_quicklists;
-
-/* References to section boundaries */
-extern char __init_begin, __init_end, _start, _end, etext , edata;
-
-/* Initial ramdisk setup */
-extern unsigned int sparc_ramdisk_image;
-extern unsigned int sparc_ramdisk_size;
-
-unsigned long highstart_pfn, highend_pfn;
-
-pte_t *kmap_pte;
-pgprot_t kmap_prot;
-
-#define kmap_get_fixmap_pte(vaddr) \
- pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr))
-
-void __init kmap_init(void)
-{
- /* cache the first kmap pte */
- kmap_pte = kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN));
- kmap_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV | SRMMU_CACHE);
-}
-
-void show_mem(void)
-{
- printk("Mem-info:\n");
- show_free_areas();
- printk("Free swap: %6ldkB\n",
- nr_swap_pages << (PAGE_SHIFT-10));
- printk("%ld pages of RAM\n", totalram_pages);
- printk("%ld free pages\n", nr_free_pages());
-#if 0 /* undefined pgtable_cache_size, pgd_cache_size */
- printk("%ld pages in page table cache\n",pgtable_cache_size);
-#ifndef CONFIG_SMP
- if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d)
- printk("%ld entries in page dir cache\n",pgd_cache_size);
-#endif
-#endif
-}
-
-void __init sparc_context_init(int numctx)
-{
- int ctx;
-
- ctx_list_pool = __alloc_bootmem(numctx * sizeof(struct ctx_list), SMP_CACHE_BYTES, 0UL);
-
- for(ctx = 0; ctx < numctx; ctx++) {
- struct ctx_list *clist;
-
- clist = (ctx_list_pool + ctx);
- clist->ctx_number = ctx;
- clist->ctx_mm = NULL;
- }
- ctx_free.next = ctx_free.prev = &ctx_free;
- ctx_used.next = ctx_used.prev = &ctx_used;
- for(ctx = 0; ctx < numctx; ctx++)
- add_to_free_ctxlist(ctx_list_pool + ctx);
-}
-
-extern unsigned long cmdline_memory_size;
-unsigned long last_valid_pfn;
-
-unsigned long calc_highpages(void)
-{
- int i;
- int nr = 0;
-
- for (i = 0; sp_banks[i].num_bytes != 0; i++) {
- unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
- unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
-
- if (end_pfn <= max_low_pfn)
- continue;
-
- if (start_pfn < max_low_pfn)
- start_pfn = max_low_pfn;
-
- nr += end_pfn - start_pfn;
- }
-
- return nr;
-}
-
-static unsigned long calc_max_low_pfn(void)
-{
- int i;
- unsigned long tmp = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
- unsigned long curr_pfn, last_pfn;
-
- last_pfn = (sp_banks[0].base_addr + sp_banks[0].num_bytes) >> PAGE_SHIFT;
- for (i = 1; sp_banks[i].num_bytes != 0; i++) {
- curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
-
- if (curr_pfn >= tmp) {
- if (last_pfn < tmp)
- tmp = last_pfn;
- break;
- }
-
- last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
- }
-
- return tmp;
-}
-
-unsigned long __init bootmem_init(unsigned long *pages_avail)
-{
- unsigned long bootmap_size, start_pfn;
- unsigned long end_of_phys_memory = 0UL;
- unsigned long bootmap_pfn, bytes_avail, size;
- int i;
-
- bytes_avail = 0UL;
- for (i = 0; sp_banks[i].num_bytes != 0; i++) {
- end_of_phys_memory = sp_banks[i].base_addr +
- sp_banks[i].num_bytes;
- bytes_avail += sp_banks[i].num_bytes;
- if (cmdline_memory_size) {
- if (bytes_avail > cmdline_memory_size) {
- unsigned long slack = bytes_avail - cmdline_memory_size;
-
- bytes_avail -= slack;
- end_of_phys_memory -= slack;
-
- sp_banks[i].num_bytes -= slack;
- if (sp_banks[i].num_bytes == 0) {
- sp_banks[i].base_addr = 0xdeadbeef;
- } else {
- sp_banks[i+1].num_bytes = 0;
- sp_banks[i+1].base_addr = 0xdeadbeef;
- }
- break;
- }
- }
- }
-
- /* Start with page aligned address of last symbol in kernel
- * image.
- */
- start_pfn = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &_end));
-
- /* Now shift down to get the real physical page frame number. */
- start_pfn >>= PAGE_SHIFT;
-
- bootmap_pfn = start_pfn;
-
- max_pfn = end_of_phys_memory >> PAGE_SHIFT;
-
- max_low_pfn = max_pfn;
- highstart_pfn = highend_pfn = max_pfn;
-
- if (max_low_pfn > pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT)) {
- highstart_pfn = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
- max_low_pfn = calc_max_low_pfn();
- printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
- calc_highpages() >> (20 - PAGE_SHIFT));
- }
-
-#ifdef CONFIG_BLK_DEV_INITRD
- /* Now have to check initial ramdisk, so that bootmap does not overwrite it */
- if (sparc_ramdisk_image) {
- if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE)
- sparc_ramdisk_image -= KERNBASE;
- initrd_start = sparc_ramdisk_image + phys_base;
- initrd_end = initrd_start + sparc_ramdisk_size;
- if (initrd_end > end_of_phys_memory) {
- printk(KERN_CRIT "initrd extends beyond end of memory "
- "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
- initrd_end, end_of_phys_memory);
- initrd_start = 0;
- }
- if (initrd_start) {
- if (initrd_start >= (start_pfn << PAGE_SHIFT) &&
- initrd_start < (start_pfn << PAGE_SHIFT) + 2 * PAGE_SIZE)
- bootmap_pfn = PAGE_ALIGN (initrd_end) >> PAGE_SHIFT;
- }
- }
-#endif
- /* Initialize the boot-time allocator. */
- bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn, pfn_base,
- max_low_pfn);
-
- /* Now register the available physical memory with the
- * allocator.
- */
- *pages_avail = 0;
- for (i = 0; sp_banks[i].num_bytes != 0; i++) {
- unsigned long curr_pfn, last_pfn;
-
- curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
- if (curr_pfn >= max_low_pfn)
- break;
-
- last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
- if (last_pfn > max_low_pfn)
- last_pfn = max_low_pfn;
-
- /*
- * .. finally, did all the rounding and playing
- * around just make the area go away?
- */
- if (last_pfn <= curr_pfn)
- continue;
-
- size = (last_pfn - curr_pfn) << PAGE_SHIFT;
- *pages_avail += last_pfn - curr_pfn;
-
- free_bootmem(sp_banks[i].base_addr, size);
- }
-
-#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_start) {
- /* Reserve the initrd image area. */
- size = initrd_end - initrd_start;
- reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT);
- *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
-
- initrd_start = (initrd_start - phys_base) + PAGE_OFFSET;
- initrd_end = (initrd_end - phys_base) + PAGE_OFFSET;
- }
-#endif
- /* Reserve the kernel text/data/bss. */
- size = (start_pfn << PAGE_SHIFT) - phys_base;
- reserve_bootmem(phys_base, size, BOOTMEM_DEFAULT);
- *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
-
- /* Reserve the bootmem map. We do not account for it
- * in pages_avail because we will release that memory
- * in free_all_bootmem.
- */
- size = bootmap_size;
- reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT);
- *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
-
- return max_pfn;
-}
-
-/*
- * check_pgt_cache
- *
- * This is called at the end of unmapping of VMA (zap_page_range),
- * to rescan the page cache for architecture specific things,
- * presumably something like sun4/sun4c PMEGs. Most architectures
- * define check_pgt_cache empty.
- *
- * We simply copy the 2.4 implementation for now.
- */
-static int pgt_cache_water[2] = { 25, 50 };
-
-void check_pgt_cache(void)
-{
- do_check_pgt_cache(pgt_cache_water[0], pgt_cache_water[1]);
-}
-
-/*
- * paging_init() sets up the page tables: We call the MMU specific
- * init routine based upon the Sun model type on the Sparc.
- *
- */
-extern void sun4c_paging_init(void);
-extern void srmmu_paging_init(void);
-extern void device_scan(void);
-
-pgprot_t PAGE_SHARED __read_mostly;
-EXPORT_SYMBOL(PAGE_SHARED);
-
-void __init paging_init(void)
-{
- switch(sparc_cpu_model) {
- case sun4c:
- case sun4e:
- case sun4:
- sun4c_paging_init();
- sparc_unmapped_base = 0xe0000000;
- BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000);
- break;
- case sun4m:
- case sun4d:
- srmmu_paging_init();
- sparc_unmapped_base = 0x50000000;
- BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
- break;
- default:
- prom_printf("paging_init: Cannot init paging on this Sparc\n");
- prom_printf("paging_init: sparc_cpu_model = %d\n", sparc_cpu_model);
- prom_printf("paging_init: Halting...\n");
- prom_halt();
- };
-
- /* Initialize the protection map with non-constant, MMU dependent values. */
- protection_map[0] = PAGE_NONE;
- protection_map[1] = PAGE_READONLY;
- protection_map[2] = PAGE_COPY;
- protection_map[3] = PAGE_COPY;
- protection_map[4] = PAGE_READONLY;
- protection_map[5] = PAGE_READONLY;
- protection_map[6] = PAGE_COPY;
- protection_map[7] = PAGE_COPY;
- protection_map[8] = PAGE_NONE;
- protection_map[9] = PAGE_READONLY;
- protection_map[10] = PAGE_SHARED;
- protection_map[11] = PAGE_SHARED;
- protection_map[12] = PAGE_READONLY;
- protection_map[13] = PAGE_READONLY;
- protection_map[14] = PAGE_SHARED;
- protection_map[15] = PAGE_SHARED;
- btfixup();
- prom_build_devicetree();
- device_scan();
-}
-
-static void __init taint_real_pages(void)
-{
- int i;
-
- for (i = 0; sp_banks[i].num_bytes; i++) {
- unsigned long start, end;
-
- start = sp_banks[i].base_addr;
- end = start + sp_banks[i].num_bytes;
-
- while (start < end) {
- set_bit(start >> 20, sparc_valid_addr_bitmap);
- start += PAGE_SIZE;
- }
- }
-}
-
-static void map_high_region(unsigned long start_pfn, unsigned long end_pfn)
-{
- unsigned long tmp;
-
-#ifdef CONFIG_DEBUG_HIGHMEM
- printk("mapping high region %08lx - %08lx\n", start_pfn, end_pfn);
-#endif
-
- for (tmp = start_pfn; tmp < end_pfn; tmp++) {
- struct page *page = pfn_to_page(tmp);
-
- ClearPageReserved(page);
- init_page_count(page);
- __free_page(page);
- totalhigh_pages++;
- }
-}
-
-void __init mem_init(void)
-{
- int codepages = 0;
- int datapages = 0;
- int initpages = 0;
- int reservedpages = 0;
- int i;
-
- if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) {
- prom_printf("BUG: fixmap and pkmap areas overlap\n");
- prom_printf("pkbase: 0x%lx pkend: 0x%lx fixstart 0x%lx\n",
- PKMAP_BASE,
- (unsigned long)PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
- FIXADDR_START);
- prom_printf("Please mail sparclinux@vger.kernel.org.\n");
- prom_halt();
- }
-
-
- /* Saves us work later. */
- memset((void *)&empty_zero_page, 0, PAGE_SIZE);
-
- i = last_valid_pfn >> ((20 - PAGE_SHIFT) + 5);
- i += 1;
- sparc_valid_addr_bitmap = (unsigned long *)
- __alloc_bootmem(i << 2, SMP_CACHE_BYTES, 0UL);
-
- if (sparc_valid_addr_bitmap == NULL) {
- prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
- prom_halt();
- }
- memset(sparc_valid_addr_bitmap, 0, i << 2);
-
- taint_real_pages();
-
- max_mapnr = last_valid_pfn - pfn_base;
- high_memory = __va(max_low_pfn << PAGE_SHIFT);
-
- totalram_pages = free_all_bootmem();
-
- for (i = 0; sp_banks[i].num_bytes != 0; i++) {
- unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
- unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
-
- num_physpages += sp_banks[i].num_bytes >> PAGE_SHIFT;
-
- if (end_pfn <= highstart_pfn)
- continue;
-
- if (start_pfn < highstart_pfn)
- start_pfn = highstart_pfn;
-
- map_high_region(start_pfn, end_pfn);
- }
-
- totalram_pages += totalhigh_pages;
-
- codepages = (((unsigned long) &etext) - ((unsigned long)&_start));
- codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
- datapages = (((unsigned long) &edata) - ((unsigned long)&etext));
- datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
- initpages = (((unsigned long) &__init_end) - ((unsigned long) &__init_begin));
- initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
-
- /* Ignore memory holes for the purpose of counting reserved pages */
- for (i=0; i < max_low_pfn; i++)
- if (test_bit(i >> (20 - PAGE_SHIFT), sparc_valid_addr_bitmap)
- && PageReserved(pfn_to_page(i)))
- reservedpages++;
-
- printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
- (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
- num_physpages << (PAGE_SHIFT - 10),
- codepages << (PAGE_SHIFT-10),
- reservedpages << (PAGE_SHIFT - 10),
- datapages << (PAGE_SHIFT-10),
- initpages << (PAGE_SHIFT-10),
- totalhigh_pages << (PAGE_SHIFT-10));
-}
-
-void free_initmem (void)
-{
- unsigned long addr;
-
- addr = (unsigned long)(&__init_begin);
- for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
- struct page *p;
-
- memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
- p = virt_to_page(addr);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- num_physpages++;
- }
- printk(KERN_INFO "Freeing unused kernel memory: %dk freed\n",
- (&__init_end - &__init_begin) >> 10);
-}
-
-#ifdef CONFIG_BLK_DEV_INITRD
-void free_initrd_mem(unsigned long start, unsigned long end)
-{
- if (start < end)
- printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
- (end - start) >> 10);
- for (; start < end; start += PAGE_SIZE) {
- struct page *p;
-
- memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE);
- p = virt_to_page(start);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- totalram_pages++;
- num_physpages++;
- }
-}
-#endif
-
-void sparc_flush_page_to_ram(struct page *page)
-{
- unsigned long vaddr = (unsigned long)page_address(page);
-
- if (vaddr)
- __flush_page_to_ram(vaddr);
-}
diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c
new file mode 100644
index 000000000000..fec926021f49
--- /dev/null
+++ b/arch/sparc/mm/init_32.c
@@ -0,0 +1,524 @@
+/*
+ * linux/arch/sparc/mm/init.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/initrd.h>
+#include <linux/init.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/pagemap.h>
+#include <linux/poison.h>
+
+#include <asm/sections.h>
+#include <asm/system.h>
+#include <asm/vac-ops.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/vaddrs.h>
+#include <asm/pgalloc.h> /* bug in asm-generic/tlb.h: check_pgt_cache */
+#include <asm/tlb.h>
+#include <asm/prom.h>
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+unsigned long *sparc_valid_addr_bitmap;
+
+unsigned long phys_base;
+unsigned long pfn_base;
+
+unsigned long page_kernel;
+
+struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS+1];
+unsigned long sparc_unmapped_base;
+
+struct pgtable_cache_struct pgt_quicklists;
+
+/* Initial ramdisk setup */
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
+
+unsigned long highstart_pfn, highend_pfn;
+
+pte_t *kmap_pte;
+pgprot_t kmap_prot;
+
+#define kmap_get_fixmap_pte(vaddr) \
+ pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr))
+
+void __init kmap_init(void)
+{
+ /* cache the first kmap pte */
+ kmap_pte = kmap_get_fixmap_pte(__fix_to_virt(FIX_KMAP_BEGIN));
+ kmap_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV | SRMMU_CACHE);
+}
+
+void show_mem(void)
+{
+ printk("Mem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6ldkB\n",
+ nr_swap_pages << (PAGE_SHIFT-10));
+ printk("%ld pages of RAM\n", totalram_pages);
+ printk("%ld free pages\n", nr_free_pages());
+#if 0 /* undefined pgtable_cache_size, pgd_cache_size */
+ printk("%ld pages in page table cache\n",pgtable_cache_size);
+#ifndef CONFIG_SMP
+ if (sparc_cpu_model == sun4m || sparc_cpu_model == sun4d)
+ printk("%ld entries in page dir cache\n",pgd_cache_size);
+#endif
+#endif
+}
+
+void __init sparc_context_init(int numctx)
+{
+ int ctx;
+
+ ctx_list_pool = __alloc_bootmem(numctx * sizeof(struct ctx_list), SMP_CACHE_BYTES, 0UL);
+
+ for(ctx = 0; ctx < numctx; ctx++) {
+ struct ctx_list *clist;
+
+ clist = (ctx_list_pool + ctx);
+ clist->ctx_number = ctx;
+ clist->ctx_mm = NULL;
+ }
+ ctx_free.next = ctx_free.prev = &ctx_free;
+ ctx_used.next = ctx_used.prev = &ctx_used;
+ for(ctx = 0; ctx < numctx; ctx++)
+ add_to_free_ctxlist(ctx_list_pool + ctx);
+}
+
+extern unsigned long cmdline_memory_size;
+unsigned long last_valid_pfn;
+
+unsigned long calc_highpages(void)
+{
+ int i;
+ int nr = 0;
+
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+ unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+
+ if (end_pfn <= max_low_pfn)
+ continue;
+
+ if (start_pfn < max_low_pfn)
+ start_pfn = max_low_pfn;
+
+ nr += end_pfn - start_pfn;
+ }
+
+ return nr;
+}
+
+static unsigned long calc_max_low_pfn(void)
+{
+ int i;
+ unsigned long tmp = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
+ unsigned long curr_pfn, last_pfn;
+
+ last_pfn = (sp_banks[0].base_addr + sp_banks[0].num_bytes) >> PAGE_SHIFT;
+ for (i = 1; sp_banks[i].num_bytes != 0; i++) {
+ curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+
+ if (curr_pfn >= tmp) {
+ if (last_pfn < tmp)
+ tmp = last_pfn;
+ break;
+ }
+
+ last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+ }
+
+ return tmp;
+}
+
+unsigned long __init bootmem_init(unsigned long *pages_avail)
+{
+ unsigned long bootmap_size, start_pfn;
+ unsigned long end_of_phys_memory = 0UL;
+ unsigned long bootmap_pfn, bytes_avail, size;
+ int i;
+
+ bytes_avail = 0UL;
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ end_of_phys_memory = sp_banks[i].base_addr +
+ sp_banks[i].num_bytes;
+ bytes_avail += sp_banks[i].num_bytes;
+ if (cmdline_memory_size) {
+ if (bytes_avail > cmdline_memory_size) {
+ unsigned long slack = bytes_avail - cmdline_memory_size;
+
+ bytes_avail -= slack;
+ end_of_phys_memory -= slack;
+
+ sp_banks[i].num_bytes -= slack;
+ if (sp_banks[i].num_bytes == 0) {
+ sp_banks[i].base_addr = 0xdeadbeef;
+ } else {
+ sp_banks[i+1].num_bytes = 0;
+ sp_banks[i+1].base_addr = 0xdeadbeef;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Start with page aligned address of last symbol in kernel
+ * image.
+ */
+ start_pfn = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &_end));
+
+ /* Now shift down to get the real physical page frame number. */
+ start_pfn >>= PAGE_SHIFT;
+
+ bootmap_pfn = start_pfn;
+
+ max_pfn = end_of_phys_memory >> PAGE_SHIFT;
+
+ max_low_pfn = max_pfn;
+ highstart_pfn = highend_pfn = max_pfn;
+
+ if (max_low_pfn > pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT)) {
+ highstart_pfn = pfn_base + (SRMMU_MAXMEM >> PAGE_SHIFT);
+ max_low_pfn = calc_max_low_pfn();
+ printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
+ calc_highpages() >> (20 - PAGE_SHIFT));
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Now have to check initial ramdisk, so that bootmap does not overwrite it */
+ if (sparc_ramdisk_image) {
+ if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE)
+ sparc_ramdisk_image -= KERNBASE;
+ initrd_start = sparc_ramdisk_image + phys_base;
+ initrd_end = initrd_start + sparc_ramdisk_size;
+ if (initrd_end > end_of_phys_memory) {
+ printk(KERN_CRIT "initrd extends beyond end of memory "
+ "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
+ initrd_end, end_of_phys_memory);
+ initrd_start = 0;
+ }
+ if (initrd_start) {
+ if (initrd_start >= (start_pfn << PAGE_SHIFT) &&
+ initrd_start < (start_pfn << PAGE_SHIFT) + 2 * PAGE_SIZE)
+ bootmap_pfn = PAGE_ALIGN (initrd_end) >> PAGE_SHIFT;
+ }
+ }
+#endif
+ /* Initialize the boot-time allocator. */
+ bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn, pfn_base,
+ max_low_pfn);
+
+ /* Now register the available physical memory with the
+ * allocator.
+ */
+ *pages_avail = 0;
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ unsigned long curr_pfn, last_pfn;
+
+ curr_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+ if (curr_pfn >= max_low_pfn)
+ break;
+
+ last_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+ if (last_pfn > max_low_pfn)
+ last_pfn = max_low_pfn;
+
+ /*
+ * .. finally, did all the rounding and playing
+ * around just make the area go away?
+ */
+ if (last_pfn <= curr_pfn)
+ continue;
+
+ size = (last_pfn - curr_pfn) << PAGE_SHIFT;
+ *pages_avail += last_pfn - curr_pfn;
+
+ free_bootmem(sp_banks[i].base_addr, size);
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ /* Reserve the initrd image area. */
+ size = initrd_end - initrd_start;
+ reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT);
+ *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+ initrd_start = (initrd_start - phys_base) + PAGE_OFFSET;
+ initrd_end = (initrd_end - phys_base) + PAGE_OFFSET;
+ }
+#endif
+ /* Reserve the kernel text/data/bss. */
+ size = (start_pfn << PAGE_SHIFT) - phys_base;
+ reserve_bootmem(phys_base, size, BOOTMEM_DEFAULT);
+ *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+ /* Reserve the bootmem map. We do not account for it
+ * in pages_avail because we will release that memory
+ * in free_all_bootmem.
+ */
+ size = bootmap_size;
+ reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT);
+ *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+ return max_pfn;
+}
+
+/*
+ * check_pgt_cache
+ *
+ * This is called at the end of unmapping of VMA (zap_page_range),
+ * to rescan the page cache for architecture specific things,
+ * presumably something like sun4/sun4c PMEGs. Most architectures
+ * define check_pgt_cache empty.
+ *
+ * We simply copy the 2.4 implementation for now.
+ */
+static int pgt_cache_water[2] = { 25, 50 };
+
+void check_pgt_cache(void)
+{
+ do_check_pgt_cache(pgt_cache_water[0], pgt_cache_water[1]);
+}
+
+/*
+ * paging_init() sets up the page tables: We call the MMU specific
+ * init routine based upon the Sun model type on the Sparc.
+ *
+ */
+extern void sun4c_paging_init(void);
+extern void srmmu_paging_init(void);
+extern void device_scan(void);
+
+pgprot_t PAGE_SHARED __read_mostly;
+EXPORT_SYMBOL(PAGE_SHARED);
+
+void __init paging_init(void)
+{
+ switch(sparc_cpu_model) {
+ case sun4c:
+ case sun4e:
+ case sun4:
+ sun4c_paging_init();
+ sparc_unmapped_base = 0xe0000000;
+ BTFIXUPSET_SETHI(sparc_unmapped_base, 0xe0000000);
+ break;
+ case sun4m:
+ case sun4d:
+ srmmu_paging_init();
+ sparc_unmapped_base = 0x50000000;
+ BTFIXUPSET_SETHI(sparc_unmapped_base, 0x50000000);
+ break;
+ default:
+ prom_printf("paging_init: Cannot init paging on this Sparc\n");
+ prom_printf("paging_init: sparc_cpu_model = %d\n", sparc_cpu_model);
+ prom_printf("paging_init: Halting...\n");
+ prom_halt();
+ };
+
+ /* Initialize the protection map with non-constant, MMU dependent values. */
+ protection_map[0] = PAGE_NONE;
+ protection_map[1] = PAGE_READONLY;
+ protection_map[2] = PAGE_COPY;
+ protection_map[3] = PAGE_COPY;
+ protection_map[4] = PAGE_READONLY;
+ protection_map[5] = PAGE_READONLY;
+ protection_map[6] = PAGE_COPY;
+ protection_map[7] = PAGE_COPY;
+ protection_map[8] = PAGE_NONE;
+ protection_map[9] = PAGE_READONLY;
+ protection_map[10] = PAGE_SHARED;
+ protection_map[11] = PAGE_SHARED;
+ protection_map[12] = PAGE_READONLY;
+ protection_map[13] = PAGE_READONLY;
+ protection_map[14] = PAGE_SHARED;
+ protection_map[15] = PAGE_SHARED;
+ btfixup();
+ prom_build_devicetree();
+ device_scan();
+}
+
+static void __init taint_real_pages(void)
+{
+ int i;
+
+ for (i = 0; sp_banks[i].num_bytes; i++) {
+ unsigned long start, end;
+
+ start = sp_banks[i].base_addr;
+ end = start + sp_banks[i].num_bytes;
+
+ while (start < end) {
+ set_bit(start >> 20, sparc_valid_addr_bitmap);
+ start += PAGE_SIZE;
+ }
+ }
+}
+
+static void map_high_region(unsigned long start_pfn, unsigned long end_pfn)
+{
+ unsigned long tmp;
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+ printk("mapping high region %08lx - %08lx\n", start_pfn, end_pfn);
+#endif
+
+ for (tmp = start_pfn; tmp < end_pfn; tmp++) {
+ struct page *page = pfn_to_page(tmp);
+
+ ClearPageReserved(page);
+ init_page_count(page);
+ __free_page(page);
+ totalhigh_pages++;
+ }
+}
+
+void __init mem_init(void)
+{
+ int codepages = 0;
+ int datapages = 0;
+ int initpages = 0;
+ int reservedpages = 0;
+ int i;
+
+ if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) {
+ prom_printf("BUG: fixmap and pkmap areas overlap\n");
+ prom_printf("pkbase: 0x%lx pkend: 0x%lx fixstart 0x%lx\n",
+ PKMAP_BASE,
+ (unsigned long)PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
+ FIXADDR_START);
+ prom_printf("Please mail sparclinux@vger.kernel.org.\n");
+ prom_halt();
+ }
+
+
+ /* Saves us work later. */
+ memset((void *)&empty_zero_page, 0, PAGE_SIZE);
+
+ i = last_valid_pfn >> ((20 - PAGE_SHIFT) + 5);
+ i += 1;
+ sparc_valid_addr_bitmap = (unsigned long *)
+ __alloc_bootmem(i << 2, SMP_CACHE_BYTES, 0UL);
+
+ if (sparc_valid_addr_bitmap == NULL) {
+ prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
+ prom_halt();
+ }
+ memset(sparc_valid_addr_bitmap, 0, i << 2);
+
+ taint_real_pages();
+
+ max_mapnr = last_valid_pfn - pfn_base;
+ high_memory = __va(max_low_pfn << PAGE_SHIFT);
+
+ totalram_pages = free_all_bootmem();
+
+ for (i = 0; sp_banks[i].num_bytes != 0; i++) {
+ unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT;
+ unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT;
+
+ num_physpages += sp_banks[i].num_bytes >> PAGE_SHIFT;
+
+ if (end_pfn <= highstart_pfn)
+ continue;
+
+ if (start_pfn < highstart_pfn)
+ start_pfn = highstart_pfn;
+
+ map_high_region(start_pfn, end_pfn);
+ }
+
+ totalram_pages += totalhigh_pages;
+
+ codepages = (((unsigned long) &_etext) - ((unsigned long)&_start));
+ codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
+ datapages = (((unsigned long) &_edata) - ((unsigned long)&_etext));
+ datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
+ initpages = (((unsigned long) &__init_end) - ((unsigned long) &__init_begin));
+ initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
+
+ /* Ignore memory holes for the purpose of counting reserved pages */
+ for (i=0; i < max_low_pfn; i++)
+ if (test_bit(i >> (20 - PAGE_SHIFT), sparc_valid_addr_bitmap)
+ && PageReserved(pfn_to_page(i)))
+ reservedpages++;
+
+ printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
+ (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+ num_physpages << (PAGE_SHIFT - 10),
+ codepages << (PAGE_SHIFT-10),
+ reservedpages << (PAGE_SHIFT - 10),
+ datapages << (PAGE_SHIFT-10),
+ initpages << (PAGE_SHIFT-10),
+ totalhigh_pages << (PAGE_SHIFT-10));
+}
+
+void free_initmem (void)
+{
+ unsigned long addr;
+ unsigned long freed;
+
+ addr = (unsigned long)(&__init_begin);
+ freed = (unsigned long)(&__init_end) - addr;
+ for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
+ struct page *p;
+
+ memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
+ p = virt_to_page(addr);
+
+ ClearPageReserved(p);
+ init_page_count(p);
+ __free_page(p);
+ totalram_pages++;
+ num_physpages++;
+ }
+ printk(KERN_INFO "Freeing unused kernel memory: %ldk freed\n",
+ freed >> 10);
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (start < end)
+ printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
+ (end - start) >> 10);
+ for (; start < end; start += PAGE_SIZE) {
+ struct page *p;
+
+ memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE);
+ p = virt_to_page(start);
+
+ ClearPageReserved(p);
+ init_page_count(p);
+ __free_page(p);
+ totalram_pages++;
+ num_physpages++;
+ }
+}
+#endif
+
+void sparc_flush_page_to_ram(struct page *page)
+{
+ unsigned long vaddr = (unsigned long)page_address(page);
+
+ if (vaddr)
+ __flush_page_to_ram(vaddr);
+}
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
new file mode 100644
index 000000000000..6ea73da29312
--- /dev/null
+++ b/arch/sparc/mm/init_64.c
@@ -0,0 +1,2360 @@
+/*
+ * arch/sparc64/mm/init.c
+ *
+ * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/slab.h>
+#include <linux/initrd.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/poison.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/kprobes.h>
+#include <linux/cache.h>
+#include <linux/sort.h>
+#include <linux/percpu.h>
+#include <linux/lmb.h>
+#include <linux/mmzone.h>
+
+#include <asm/head.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/oplib.h>
+#include <asm/iommu.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+#include <asm/dma.h>
+#include <asm/starfire.h>
+#include <asm/tlb.h>
+#include <asm/spitfire.h>
+#include <asm/sections.h>
+#include <asm/tsb.h>
+#include <asm/hypervisor.h>
+#include <asm/prom.h>
+#include <asm/mdesc.h>
+#include <asm/cpudata.h>
+#include <asm/irq.h>
+
+#include "init_64.h"
+
+unsigned long kern_linear_pte_xor[2] __read_mostly;
+
+/* A bitmap, one bit for every 256MB of physical memory. If the bit
+ * is clear, we should use a 4MB page (via kern_linear_pte_xor[0]) else
+ * if set we should use a 256MB page (via kern_linear_pte_xor[1]).
+ */
+unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
+
+#ifndef CONFIG_DEBUG_PAGEALLOC
+/* A special kernel TSB for 4MB and 256MB linear mappings.
+ * Space is allocated for this right after the trap table
+ * in arch/sparc64/kernel/head.S
+ */
+extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
+#endif
+
+#define MAX_BANKS 32
+
+static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;
+static int pavail_ents __initdata;
+
+static int cmp_p64(const void *a, const void *b)
+{
+ const struct linux_prom64_registers *x = a, *y = b;
+
+ if (x->phys_addr > y->phys_addr)
+ return 1;
+ if (x->phys_addr < y->phys_addr)
+ return -1;
+ return 0;
+}
+
+static void __init read_obp_memory(const char *property,
+ struct linux_prom64_registers *regs,
+ int *num_ents)
+{
+ int node = prom_finddevice("/memory");
+ int prop_size = prom_getproplen(node, property);
+ int ents, ret, i;
+
+ ents = prop_size / sizeof(struct linux_prom64_registers);
+ if (ents > MAX_BANKS) {
+ prom_printf("The machine has more %s property entries than "
+ "this kernel can support (%d).\n",
+ property, MAX_BANKS);
+ prom_halt();
+ }
+
+ ret = prom_getproperty(node, property, (char *) regs, prop_size);
+ if (ret == -1) {
+ prom_printf("Couldn't get %s property from /memory.\n");
+ prom_halt();
+ }
+
+ /* Sanitize what we got from the firmware, by page aligning
+ * everything.
+ */
+ for (i = 0; i < ents; i++) {
+ unsigned long base, size;
+
+ base = regs[i].phys_addr;
+ size = regs[i].reg_size;
+
+ size &= PAGE_MASK;
+ if (base & ~PAGE_MASK) {
+ unsigned long new_base = PAGE_ALIGN(base);
+
+ size -= new_base - base;
+ if ((long) size < 0L)
+ size = 0UL;
+ base = new_base;
+ }
+ if (size == 0UL) {
+ /* If it is empty, simply get rid of it.
+ * This simplifies the logic of the other
+ * functions that process these arrays.
+ */
+ memmove(&regs[i], &regs[i + 1],
+ (ents - i - 1) * sizeof(regs[0]));
+ i--;
+ ents--;
+ continue;
+ }
+ regs[i].phys_addr = base;
+ regs[i].reg_size = size;
+ }
+
+ *num_ents = ents;
+
+ sort(regs, ents, sizeof(struct linux_prom64_registers),
+ cmp_p64, NULL);
+}
+
+unsigned long *sparc64_valid_addr_bitmap __read_mostly;
+
+/* Kernel physical address base and size in bytes. */
+unsigned long kern_base __read_mostly;
+unsigned long kern_size __read_mostly;
+
+/* Initial ramdisk setup */
+extern unsigned long sparc_ramdisk_image64;
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
+
+struct page *mem_map_zero __read_mostly;
+EXPORT_SYMBOL(mem_map_zero);
+
+unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly;
+
+unsigned long sparc64_kern_pri_context __read_mostly;
+unsigned long sparc64_kern_pri_nuc_bits __read_mostly;
+unsigned long sparc64_kern_sec_context __read_mostly;
+
+int num_kernel_image_mappings;
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+atomic_t dcpage_flushes = ATOMIC_INIT(0);
+#ifdef CONFIG_SMP
+atomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);
+#endif
+#endif
+
+inline void flush_dcache_page_impl(struct page *page)
+{
+ BUG_ON(tlb_type == hypervisor);
+#ifdef CONFIG_DEBUG_DCFLUSH
+ atomic_inc(&dcpage_flushes);
+#endif
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ __flush_dcache_page(page_address(page),
+ ((tlb_type == spitfire) &&
+ page_mapping(page) != NULL));
+#else
+ if (page_mapping(page) != NULL &&
+ tlb_type == spitfire)
+ __flush_icache_page(__pa(page_address(page)));
+#endif
+}
+
+#define PG_dcache_dirty PG_arch_1
+#define PG_dcache_cpu_shift 32UL
+#define PG_dcache_cpu_mask \
+ ((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)
+
+#define dcache_dirty_cpu(page) \
+ (((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)
+
+static inline void set_dcache_dirty(struct page *page, int this_cpu)
+{
+ unsigned long mask = this_cpu;
+ unsigned long non_cpu_bits;
+
+ non_cpu_bits = ~(PG_dcache_cpu_mask << PG_dcache_cpu_shift);
+ mask = (mask << PG_dcache_cpu_shift) | (1UL << PG_dcache_dirty);
+
+ __asm__ __volatile__("1:\n\t"
+ "ldx [%2], %%g7\n\t"
+ "and %%g7, %1, %%g1\n\t"
+ "or %%g1, %0, %%g1\n\t"
+ "casx [%2], %%g7, %%g1\n\t"
+ "cmp %%g7, %%g1\n\t"
+ "bne,pn %%xcc, 1b\n\t"
+ " nop"
+ : /* no outputs */
+ : "r" (mask), "r" (non_cpu_bits), "r" (&page->flags)
+ : "g1", "g7");
+}
+
+static inline void clear_dcache_dirty_cpu(struct page *page, unsigned long cpu)
+{
+ unsigned long mask = (1UL << PG_dcache_dirty);
+
+ __asm__ __volatile__("! test_and_clear_dcache_dirty\n"
+ "1:\n\t"
+ "ldx [%2], %%g7\n\t"
+ "srlx %%g7, %4, %%g1\n\t"
+ "and %%g1, %3, %%g1\n\t"
+ "cmp %%g1, %0\n\t"
+ "bne,pn %%icc, 2f\n\t"
+ " andn %%g7, %1, %%g1\n\t"
+ "casx [%2], %%g7, %%g1\n\t"
+ "cmp %%g7, %%g1\n\t"
+ "bne,pn %%xcc, 1b\n\t"
+ " nop\n"
+ "2:"
+ : /* no outputs */
+ : "r" (cpu), "r" (mask), "r" (&page->flags),
+ "i" (PG_dcache_cpu_mask),
+ "i" (PG_dcache_cpu_shift)
+ : "g1", "g7");
+}
+
+static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long pte)
+{
+ unsigned long tsb_addr = (unsigned long) ent;
+
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ tsb_addr = __pa(tsb_addr);
+
+ __tsb_insert(tsb_addr, tag, pte);
+}
+
+unsigned long _PAGE_ALL_SZ_BITS __read_mostly;
+unsigned long _PAGE_SZBITS __read_mostly;
+
+void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+ struct mm_struct *mm;
+ struct tsb *tsb;
+ unsigned long tag, flags;
+ unsigned long tsb_index, tsb_hash_shift;
+
+ if (tlb_type != hypervisor) {
+ unsigned long pfn = pte_pfn(pte);
+ unsigned long pg_flags;
+ struct page *page;
+
+ if (pfn_valid(pfn) &&
+ (page = pfn_to_page(pfn), page_mapping(page)) &&
+ ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {
+ int cpu = ((pg_flags >> PG_dcache_cpu_shift) &
+ PG_dcache_cpu_mask);
+ int this_cpu = get_cpu();
+
+ /* This is just to optimize away some function calls
+ * in the SMP case.
+ */
+ if (cpu == this_cpu)
+ flush_dcache_page_impl(page);
+ else
+ smp_flush_dcache_page_impl(page, cpu);
+
+ clear_dcache_dirty_cpu(page, cpu);
+
+ put_cpu();
+ }
+ }
+
+ mm = vma->vm_mm;
+
+ tsb_index = MM_TSB_BASE;
+ tsb_hash_shift = PAGE_SHIFT;
+
+ spin_lock_irqsave(&mm->context.lock, flags);
+
+#ifdef CONFIG_HUGETLB_PAGE
+ if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL) {
+ if ((tlb_type == hypervisor &&
+ (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||
+ (tlb_type != hypervisor &&
+ (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U)) {
+ tsb_index = MM_TSB_HUGE;
+ tsb_hash_shift = HPAGE_SHIFT;
+ }
+ }
+#endif
+
+ tsb = mm->context.tsb_block[tsb_index].tsb;
+ tsb += ((address >> tsb_hash_shift) &
+ (mm->context.tsb_block[tsb_index].tsb_nentries - 1UL));
+ tag = (address >> 22UL);
+ tsb_insert(tsb, tag, pte_val(pte));
+
+ spin_unlock_irqrestore(&mm->context.lock, flags);
+}
+
+void flush_dcache_page(struct page *page)
+{
+ struct address_space *mapping;
+ int this_cpu;
+
+ if (tlb_type == hypervisor)
+ return;
+
+ /* Do not bother with the expensive D-cache flush if it
+ * is merely the zero page. The 'bigcore' testcase in GDB
+ * causes this case to run millions of times.
+ */
+ if (page == ZERO_PAGE(0))
+ return;
+
+ this_cpu = get_cpu();
+
+ mapping = page_mapping(page);
+ if (mapping && !mapping_mapped(mapping)) {
+ int dirty = test_bit(PG_dcache_dirty, &page->flags);
+ if (dirty) {
+ int dirty_cpu = dcache_dirty_cpu(page);
+
+ if (dirty_cpu == this_cpu)
+ goto out;
+ smp_flush_dcache_page_impl(page, dirty_cpu);
+ }
+ set_dcache_dirty(page, this_cpu);
+ } else {
+ /* We could delay the flush for the !page_mapping
+ * case too. But that case is for exec env/arg
+ * pages and those are %99 certainly going to get
+ * faulted into the tlb (and thus flushed) anyways.
+ */
+ flush_dcache_page_impl(page);
+ }
+
+out:
+ put_cpu();
+}
+
+void __kprobes flush_icache_range(unsigned long start, unsigned long end)
+{
+ /* Cheetah and Hypervisor platform cpus have coherent I-cache. */
+ if (tlb_type == spitfire) {
+ unsigned long kaddr;
+
+ /* This code only runs on Spitfire cpus so this is
+ * why we can assume _PAGE_PADDR_4U.
+ */
+ for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {
+ unsigned long paddr, mask = _PAGE_PADDR_4U;
+
+ if (kaddr >= PAGE_OFFSET)
+ paddr = kaddr & mask;
+ else {
+ pgd_t *pgdp = pgd_offset_k(kaddr);
+ pud_t *pudp = pud_offset(pgdp, kaddr);
+ pmd_t *pmdp = pmd_offset(pudp, kaddr);
+ pte_t *ptep = pte_offset_kernel(pmdp, kaddr);
+
+ paddr = pte_val(*ptep) & mask;
+ }
+ __flush_icache_page(paddr);
+ }
+ }
+}
+
+void mmu_info(struct seq_file *m)
+{
+ if (tlb_type == cheetah)
+ seq_printf(m, "MMU Type\t: Cheetah\n");
+ else if (tlb_type == cheetah_plus)
+ seq_printf(m, "MMU Type\t: Cheetah+\n");
+ else if (tlb_type == spitfire)
+ seq_printf(m, "MMU Type\t: Spitfire\n");
+ else if (tlb_type == hypervisor)
+ seq_printf(m, "MMU Type\t: Hypervisor (sun4v)\n");
+ else
+ seq_printf(m, "MMU Type\t: ???\n");
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+ seq_printf(m, "DCPageFlushes\t: %d\n",
+ atomic_read(&dcpage_flushes));
+#ifdef CONFIG_SMP
+ seq_printf(m, "DCPageFlushesXC\t: %d\n",
+ atomic_read(&dcpage_flushes_xcall));
+#endif /* CONFIG_SMP */
+#endif /* CONFIG_DEBUG_DCFLUSH */
+}
+
+struct linux_prom_translation prom_trans[512] __read_mostly;
+unsigned int prom_trans_ents __read_mostly;
+
+unsigned long kern_locked_tte_data;
+
+/* The obp translations are saved based on 8k pagesize, since obp can
+ * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
+ * HI_OBP_ADDRESS range are handled in ktlb.S.
+ */
+static inline int in_obp_range(unsigned long vaddr)
+{
+ return (vaddr >= LOW_OBP_ADDRESS &&
+ vaddr < HI_OBP_ADDRESS);
+}
+
+static int cmp_ptrans(const void *a, const void *b)
+{
+ const struct linux_prom_translation *x = a, *y = b;
+
+ if (x->virt > y->virt)
+ return 1;
+ if (x->virt < y->virt)
+ return -1;
+ return 0;
+}
+
+/* Read OBP translations property into 'prom_trans[]'. */
+static void __init read_obp_translations(void)
+{
+ int n, node, ents, first, last, i;
+
+ node = prom_finddevice("/virtual-memory");
+ n = prom_getproplen(node, "translations");
+ if (unlikely(n == 0 || n == -1)) {
+ prom_printf("prom_mappings: Couldn't get size.\n");
+ prom_halt();
+ }
+ if (unlikely(n > sizeof(prom_trans))) {
+ prom_printf("prom_mappings: Size %Zd is too big.\n", n);
+ prom_halt();
+ }
+
+ if ((n = prom_getproperty(node, "translations",
+ (char *)&prom_trans[0],
+ sizeof(prom_trans))) == -1) {
+ prom_printf("prom_mappings: Couldn't get property.\n");
+ prom_halt();
+ }
+
+ n = n / sizeof(struct linux_prom_translation);
+
+ ents = n;
+
+ sort(prom_trans, ents, sizeof(struct linux_prom_translation),
+ cmp_ptrans, NULL);
+
+ /* Now kick out all the non-OBP entries. */
+ for (i = 0; i < ents; i++) {
+ if (in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ first = i;
+ for (; i < ents; i++) {
+ if (!in_obp_range(prom_trans[i].virt))
+ break;
+ }
+ last = i;
+
+ for (i = 0; i < (last - first); i++) {
+ struct linux_prom_translation *src = &prom_trans[i + first];
+ struct linux_prom_translation *dest = &prom_trans[i];
+
+ *dest = *src;
+ }
+ for (; i < ents; i++) {
+ struct linux_prom_translation *dest = &prom_trans[i];
+ dest->virt = dest->size = dest->data = 0x0UL;
+ }
+
+ prom_trans_ents = last - first;
+
+ if (tlb_type == spitfire) {
+ /* Clear diag TTE bits. */
+ for (i = 0; i < prom_trans_ents; i++)
+ prom_trans[i].data &= ~0x0003fe0000000000UL;
+ }
+}
+
+static void __init hypervisor_tlb_lock(unsigned long vaddr,
+ unsigned long pte,
+ unsigned long mmu)
+{
+ unsigned long ret = sun4v_mmu_map_perm_addr(vaddr, 0, pte, mmu);
+
+ if (ret != 0) {
+ prom_printf("hypervisor_tlb_lock[%lx:%lx:%lx:%lx]: "
+ "errors with %lx\n", vaddr, 0, pte, mmu, ret);
+ prom_halt();
+ }
+}
+
+static unsigned long kern_large_tte(unsigned long paddr);
+
+static void __init remap_kernel(void)
+{
+ unsigned long phys_page, tte_vaddr, tte_data;
+ int i, tlb_ent = sparc64_highest_locked_tlbent();
+
+ tte_vaddr = (unsigned long) KERNBASE;
+ phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
+ tte_data = kern_large_tte(phys_page);
+
+ kern_locked_tte_data = tte_data;
+
+ /* Now lock us into the TLBs via Hypervisor or OBP. */
+ if (tlb_type == hypervisor) {
+ for (i = 0; i < num_kernel_image_mappings; i++) {
+ hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_DMMU);
+ hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_IMMU);
+ tte_vaddr += 0x400000;
+ tte_data += 0x400000;
+ }
+ } else {
+ for (i = 0; i < num_kernel_image_mappings; i++) {
+ prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
+ prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
+ tte_vaddr += 0x400000;
+ tte_data += 0x400000;
+ }
+ sparc64_highest_unlocked_tlb_ent = tlb_ent - i;
+ }
+ if (tlb_type == cheetah_plus) {
+ sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 |
+ CTX_CHEETAH_PLUS_NUC);
+ sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC;
+ sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0;
+ }
+}
+
+
+static void __init inherit_prom_mappings(void)
+{
+ /* Now fixup OBP's idea about where we really are mapped. */
+ printk("Remapping the kernel... ");
+ remap_kernel();
+ printk("done.\n");
+}
+
+void prom_world(int enter)
+{
+ if (!enter)
+ set_fs((mm_segment_t) { get_thread_current_ds() });
+
+ __asm__ __volatile__("flushw");
+}
+
+void __flush_dcache_range(unsigned long start, unsigned long end)
+{
+ unsigned long va;
+
+ if (tlb_type == spitfire) {
+ int n = 0;
+
+ for (va = start; va < end; va += 32) {
+ spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
+ if (++n >= 512)
+ break;
+ }
+ } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ start = __pa(start);
+ end = __pa(end);
+ for (va = start; va < end; va += 32)
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (va),
+ "i" (ASI_DCACHE_INVALIDATE));
+ }
+}
+
+/* get_new_mmu_context() uses "cache + 1". */
+DEFINE_SPINLOCK(ctx_alloc_lock);
+unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
+#define MAX_CTX_NR (1UL << CTX_NR_BITS)
+#define CTX_BMAP_SLOTS BITS_TO_LONGS(MAX_CTX_NR)
+DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR);
+
+/* Caller does TLB context flushing on local CPU if necessary.
+ * The caller also ensures that CTX_VALID(mm->context) is false.
+ *
+ * We must be careful about boundary cases so that we never
+ * let the user have CTX 0 (nucleus) or we ever use a CTX
+ * version of zero (and thus NO_CONTEXT would not be caught
+ * by version mis-match tests in mmu_context.h).
+ *
+ * Always invoked with interrupts disabled.
+ */
+void get_new_mmu_context(struct mm_struct *mm)
+{
+ unsigned long ctx, new_ctx;
+ unsigned long orig_pgsz_bits;
+ unsigned long flags;
+ int new_version;
+
+ spin_lock_irqsave(&ctx_alloc_lock, flags);
+ orig_pgsz_bits = (mm->context.sparc64_ctx_val & CTX_PGSZ_MASK);
+ ctx = (tlb_context_cache + 1) & CTX_NR_MASK;
+ new_ctx = find_next_zero_bit(mmu_context_bmap, 1 << CTX_NR_BITS, ctx);
+ new_version = 0;
+ if (new_ctx >= (1 << CTX_NR_BITS)) {
+ new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1);
+ if (new_ctx >= ctx) {
+ int i;
+ new_ctx = (tlb_context_cache & CTX_VERSION_MASK) +
+ CTX_FIRST_VERSION;
+ if (new_ctx == 1)
+ new_ctx = CTX_FIRST_VERSION;
+
+ /* Don't call memset, for 16 entries that's just
+ * plain silly...
+ */
+ mmu_context_bmap[0] = 3;
+ mmu_context_bmap[1] = 0;
+ mmu_context_bmap[2] = 0;
+ mmu_context_bmap[3] = 0;
+ for (i = 4; i < CTX_BMAP_SLOTS; i += 4) {
+ mmu_context_bmap[i + 0] = 0;
+ mmu_context_bmap[i + 1] = 0;
+ mmu_context_bmap[i + 2] = 0;
+ mmu_context_bmap[i + 3] = 0;
+ }
+ new_version = 1;
+ goto out;
+ }
+ }
+ mmu_context_bmap[new_ctx>>6] |= (1UL << (new_ctx & 63));
+ new_ctx |= (tlb_context_cache & CTX_VERSION_MASK);
+out:
+ tlb_context_cache = new_ctx;
+ mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
+ spin_unlock_irqrestore(&ctx_alloc_lock, flags);
+
+ if (unlikely(new_version))
+ smp_new_mmu_context_version();
+}
+
+static int numa_enabled = 1;
+static int numa_debug;
+
+static int __init early_numa(char *p)
+{
+ if (!p)
+ return 0;
+
+ if (strstr(p, "off"))
+ numa_enabled = 0;
+
+ if (strstr(p, "debug"))
+ numa_debug = 1;
+
+ return 0;
+}
+early_param("numa", early_numa);
+
+#define numadbg(f, a...) \
+do { if (numa_debug) \
+ printk(KERN_INFO f, ## a); \
+} while (0)
+
+static void __init find_ramdisk(unsigned long phys_base)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (sparc_ramdisk_image || sparc_ramdisk_image64) {
+ unsigned long ramdisk_image;
+
+ /* Older versions of the bootloader only supported a
+ * 32-bit physical address for the ramdisk image
+ * location, stored at sparc_ramdisk_image. Newer
+ * SILO versions set sparc_ramdisk_image to zero and
+ * provide a full 64-bit physical address at
+ * sparc_ramdisk_image64.
+ */
+ ramdisk_image = sparc_ramdisk_image;
+ if (!ramdisk_image)
+ ramdisk_image = sparc_ramdisk_image64;
+
+ /* Another bootloader quirk. The bootloader normalizes
+ * the physical address to KERNBASE, so we have to
+ * factor that back out and add in the lowest valid
+ * physical page address to get the true physical address.
+ */
+ ramdisk_image -= KERNBASE;
+ ramdisk_image += phys_base;
+
+ numadbg("Found ramdisk at physical address 0x%lx, size %u\n",
+ ramdisk_image, sparc_ramdisk_size);
+
+ initrd_start = ramdisk_image;
+ initrd_end = ramdisk_image + sparc_ramdisk_size;
+
+ lmb_reserve(initrd_start, sparc_ramdisk_size);
+
+ initrd_start += PAGE_OFFSET;
+ initrd_end += PAGE_OFFSET;
+ }
+#endif
+}
+
+struct node_mem_mask {
+ unsigned long mask;
+ unsigned long val;
+ unsigned long bootmem_paddr;
+};
+static struct node_mem_mask node_masks[MAX_NUMNODES];
+static int num_node_masks;
+
+int numa_cpu_lookup_table[NR_CPUS];
+cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+
+struct mdesc_mblock {
+ u64 base;
+ u64 size;
+ u64 offset; /* RA-to-PA */
+};
+static struct mdesc_mblock *mblocks;
+static int num_mblocks;
+
+static unsigned long ra_to_pa(unsigned long addr)
+{
+ int i;
+
+ for (i = 0; i < num_mblocks; i++) {
+ struct mdesc_mblock *m = &mblocks[i];
+
+ if (addr >= m->base &&
+ addr < (m->base + m->size)) {
+ addr += m->offset;
+ break;
+ }
+ }
+ return addr;
+}
+
+static int find_node(unsigned long addr)
+{
+ int i;
+
+ addr = ra_to_pa(addr);
+ for (i = 0; i < num_node_masks; i++) {
+ struct node_mem_mask *p = &node_masks[i];
+
+ if ((addr & p->mask) == p->val)
+ return i;
+ }
+ return -1;
+}
+
+static unsigned long nid_range(unsigned long start, unsigned long end,
+ int *nid)
+{
+ *nid = find_node(start);
+ start += PAGE_SIZE;
+ while (start < end) {
+ int n = find_node(start);
+
+ if (n != *nid)
+ break;
+ start += PAGE_SIZE;
+ }
+
+ if (start > end)
+ start = end;
+
+ return start;
+}
+#else
+static unsigned long nid_range(unsigned long start, unsigned long end,
+ int *nid)
+{
+ *nid = 0;
+ return end;
+}
+#endif
+
+/* This must be invoked after performing all of the necessary
+ * add_active_range() calls for 'nid'. We need to be able to get
+ * correct data from get_pfn_range_for_nid().
+ */
+static void __init allocate_node_data(int nid)
+{
+ unsigned long paddr, num_pages, start_pfn, end_pfn;
+ struct pglist_data *p;
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ paddr = lmb_alloc_nid(sizeof(struct pglist_data),
+ SMP_CACHE_BYTES, nid, nid_range);
+ if (!paddr) {
+ prom_printf("Cannot allocate pglist_data for nid[%d]\n", nid);
+ prom_halt();
+ }
+ NODE_DATA(nid) = __va(paddr);
+ memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
+
+ NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
+#endif
+
+ p = NODE_DATA(nid);
+
+ get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+ p->node_start_pfn = start_pfn;
+ p->node_spanned_pages = end_pfn - start_pfn;
+
+ if (p->node_spanned_pages) {
+ num_pages = bootmem_bootmap_pages(p->node_spanned_pages);
+
+ paddr = lmb_alloc_nid(num_pages << PAGE_SHIFT, PAGE_SIZE, nid,
+ nid_range);
+ if (!paddr) {
+ prom_printf("Cannot allocate bootmap for nid[%d]\n",
+ nid);
+ prom_halt();
+ }
+ node_masks[nid].bootmem_paddr = paddr;
+ }
+}
+
+static void init_node_masks_nonnuma(void)
+{
+ int i;
+
+ numadbg("Initializing tables for non-numa.\n");
+
+ node_masks[0].mask = node_masks[0].val = 0;
+ num_node_masks = 1;
+
+ for (i = 0; i < NR_CPUS; i++)
+ numa_cpu_lookup_table[i] = 0;
+
+ numa_cpumask_lookup_table[0] = CPU_MASK_ALL;
+}
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+struct pglist_data *node_data[MAX_NUMNODES];
+
+EXPORT_SYMBOL(numa_cpu_lookup_table);
+EXPORT_SYMBOL(numa_cpumask_lookup_table);
+EXPORT_SYMBOL(node_data);
+
+struct mdesc_mlgroup {
+ u64 node;
+ u64 latency;
+ u64 match;
+ u64 mask;
+};
+static struct mdesc_mlgroup *mlgroups;
+static int num_mlgroups;
+
+static int scan_pio_for_cfg_handle(struct mdesc_handle *md, u64 pio,
+ u32 cfg_handle)
+{
+ u64 arc;
+
+ mdesc_for_each_arc(arc, md, pio, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ const u64 *val;
+
+ val = mdesc_get_property(md, target,
+ "cfg-handle", NULL);
+ if (val && *val == cfg_handle)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int scan_arcs_for_cfg_handle(struct mdesc_handle *md, u64 grp,
+ u32 cfg_handle)
+{
+ u64 arc, candidate, best_latency = ~(u64)0;
+
+ candidate = MDESC_NODE_NULL;
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ const char *name = mdesc_node_name(md, target);
+ const u64 *val;
+
+ if (strcmp(name, "pio-latency-group"))
+ continue;
+
+ val = mdesc_get_property(md, target, "latency", NULL);
+ if (!val)
+ continue;
+
+ if (*val < best_latency) {
+ candidate = target;
+ best_latency = *val;
+ }
+ }
+
+ if (candidate == MDESC_NODE_NULL)
+ return -ENODEV;
+
+ return scan_pio_for_cfg_handle(md, candidate, cfg_handle);
+}
+
+int of_node_to_nid(struct device_node *dp)
+{
+ const struct linux_prom64_registers *regs;
+ struct mdesc_handle *md;
+ u32 cfg_handle;
+ int count, nid;
+ u64 grp;
+
+ /* This is the right thing to do on currently supported
+ * SUN4U NUMA platforms as well, as the PCI controller does
+ * not sit behind any particular memory controller.
+ */
+ if (!mlgroups)
+ return -1;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (!regs)
+ return -1;
+
+ cfg_handle = (regs->phys_addr >> 32UL) & 0x0fffffff;
+
+ md = mdesc_grab();
+
+ count = 0;
+ nid = -1;
+ mdesc_for_each_node_by_name(md, grp, "group") {
+ if (!scan_arcs_for_cfg_handle(md, grp, cfg_handle)) {
+ nid = count;
+ break;
+ }
+ count++;
+ }
+
+ mdesc_release(md);
+
+ return nid;
+}
+
+static void add_node_ranges(void)
+{
+ int i;
+
+ for (i = 0; i < lmb.memory.cnt; i++) {
+ unsigned long size = lmb_size_bytes(&lmb.memory, i);
+ unsigned long start, end;
+
+ start = lmb.memory.region[i].base;
+ end = start + size;
+ while (start < end) {
+ unsigned long this_end;
+ int nid;
+
+ this_end = nid_range(start, end, &nid);
+
+ numadbg("Adding active range nid[%d] "
+ "start[%lx] end[%lx]\n",
+ nid, start, this_end);
+
+ add_active_range(nid,
+ start >> PAGE_SHIFT,
+ this_end >> PAGE_SHIFT);
+
+ start = this_end;
+ }
+ }
+}
+
+static int __init grab_mlgroups(struct mdesc_handle *md)
+{
+ unsigned long paddr;
+ int count = 0;
+ u64 node;
+
+ mdesc_for_each_node_by_name(md, node, "memory-latency-group")
+ count++;
+ if (!count)
+ return -ENOENT;
+
+ paddr = lmb_alloc(count * sizeof(struct mdesc_mlgroup),
+ SMP_CACHE_BYTES);
+ if (!paddr)
+ return -ENOMEM;
+
+ mlgroups = __va(paddr);
+ num_mlgroups = count;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "memory-latency-group") {
+ struct mdesc_mlgroup *m = &mlgroups[count++];
+ const u64 *val;
+
+ m->node = node;
+
+ val = mdesc_get_property(md, node, "latency", NULL);
+ m->latency = *val;
+ val = mdesc_get_property(md, node, "address-match", NULL);
+ m->match = *val;
+ val = mdesc_get_property(md, node, "address-mask", NULL);
+ m->mask = *val;
+
+ numadbg("MLGROUP[%d]: node[%lx] latency[%lx] "
+ "match[%lx] mask[%lx]\n",
+ count - 1, m->node, m->latency, m->match, m->mask);
+ }
+
+ return 0;
+}
+
+static int __init grab_mblocks(struct mdesc_handle *md)
+{
+ unsigned long paddr;
+ int count = 0;
+ u64 node;
+
+ mdesc_for_each_node_by_name(md, node, "mblock")
+ count++;
+ if (!count)
+ return -ENOENT;
+
+ paddr = lmb_alloc(count * sizeof(struct mdesc_mblock),
+ SMP_CACHE_BYTES);
+ if (!paddr)
+ return -ENOMEM;
+
+ mblocks = __va(paddr);
+ num_mblocks = count;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "mblock") {
+ struct mdesc_mblock *m = &mblocks[count++];
+ const u64 *val;
+
+ val = mdesc_get_property(md, node, "base", NULL);
+ m->base = *val;
+ val = mdesc_get_property(md, node, "size", NULL);
+ m->size = *val;
+ val = mdesc_get_property(md, node,
+ "address-congruence-offset", NULL);
+ m->offset = *val;
+
+ numadbg("MBLOCK[%d]: base[%lx] size[%lx] offset[%lx]\n",
+ count - 1, m->base, m->size, m->offset);
+ }
+
+ return 0;
+}
+
+static void __init numa_parse_mdesc_group_cpus(struct mdesc_handle *md,
+ u64 grp, cpumask_t *mask)
+{
+ u64 arc;
+
+ cpus_clear(*mask);
+
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_BACK) {
+ u64 target = mdesc_arc_target(md, arc);
+ const char *name = mdesc_node_name(md, target);
+ const u64 *id;
+
+ if (strcmp(name, "cpu"))
+ continue;
+ id = mdesc_get_property(md, target, "id", NULL);
+ if (*id < NR_CPUS)
+ cpu_set(*id, *mask);
+ }
+}
+
+static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
+{
+ int i;
+
+ for (i = 0; i < num_mlgroups; i++) {
+ struct mdesc_mlgroup *m = &mlgroups[i];
+ if (m->node == node)
+ return m;
+ }
+ return NULL;
+}
+
+static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
+ int index)
+{
+ struct mdesc_mlgroup *candidate = NULL;
+ u64 arc, best_latency = ~(u64)0;
+ struct node_mem_mask *n;
+
+ mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ struct mdesc_mlgroup *m = find_mlgroup(target);
+ if (!m)
+ continue;
+ if (m->latency < best_latency) {
+ candidate = m;
+ best_latency = m->latency;
+ }
+ }
+ if (!candidate)
+ return -ENOENT;
+
+ if (num_node_masks != index) {
+ printk(KERN_ERR "Inconsistent NUMA state, "
+ "index[%d] != num_node_masks[%d]\n",
+ index, num_node_masks);
+ return -EINVAL;
+ }
+
+ n = &node_masks[num_node_masks++];
+
+ n->mask = candidate->mask;
+ n->val = candidate->match;
+
+ numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n",
+ index, n->mask, n->val, candidate->latency);
+
+ return 0;
+}
+
+static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
+ int index)
+{
+ cpumask_t mask;
+ int cpu;
+
+ numa_parse_mdesc_group_cpus(md, grp, &mask);
+
+ for_each_cpu_mask(cpu, mask)
+ numa_cpu_lookup_table[cpu] = index;
+ numa_cpumask_lookup_table[index] = mask;
+
+ if (numa_debug) {
+ printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index);
+ for_each_cpu_mask(cpu, mask)
+ printk("%d ", cpu);
+ printk("]\n");
+ }
+
+ return numa_attach_mlgroup(md, grp, index);
+}
+
+static int __init numa_parse_mdesc(void)
+{
+ struct mdesc_handle *md = mdesc_grab();
+ int i, err, count;
+ u64 node;
+
+ node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
+ if (node == MDESC_NODE_NULL) {
+ mdesc_release(md);
+ return -ENOENT;
+ }
+
+ err = grab_mblocks(md);
+ if (err < 0)
+ goto out;
+
+ err = grab_mlgroups(md);
+ if (err < 0)
+ goto out;
+
+ count = 0;
+ mdesc_for_each_node_by_name(md, node, "group") {
+ err = numa_parse_mdesc_group(md, node, count);
+ if (err < 0)
+ break;
+ count++;
+ }
+
+ add_node_ranges();
+
+ for (i = 0; i < num_node_masks; i++) {
+ allocate_node_data(i);
+ node_set_online(i);
+ }
+
+ err = 0;
+out:
+ mdesc_release(md);
+ return err;
+}
+
+static int __init numa_parse_jbus(void)
+{
+ unsigned long cpu, index;
+
+ /* NUMA node id is encoded in bits 36 and higher, and there is
+ * a 1-to-1 mapping from CPU ID to NUMA node ID.
+ */
+ index = 0;
+ for_each_present_cpu(cpu) {
+ numa_cpu_lookup_table[cpu] = index;
+ numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu);
+ node_masks[index].mask = ~((1UL << 36UL) - 1UL);
+ node_masks[index].val = cpu << 36UL;
+
+ index++;
+ }
+ num_node_masks = index;
+
+ add_node_ranges();
+
+ for (index = 0; index < num_node_masks; index++) {
+ allocate_node_data(index);
+ node_set_online(index);
+ }
+
+ return 0;
+}
+
+static int __init numa_parse_sun4u(void)
+{
+ if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ unsigned long ver;
+
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID)
+ return numa_parse_jbus();
+ }
+ return -1;
+}
+
+static int __init bootmem_init_numa(void)
+{
+ int err = -1;
+
+ numadbg("bootmem_init_numa()\n");
+
+ if (numa_enabled) {
+ if (tlb_type == hypervisor)
+ err = numa_parse_mdesc();
+ else
+ err = numa_parse_sun4u();
+ }
+ return err;
+}
+
+#else
+
+static int bootmem_init_numa(void)
+{
+ return -1;
+}
+
+#endif
+
+static void __init bootmem_init_nonnuma(void)
+{
+ unsigned long top_of_ram = lmb_end_of_DRAM();
+ unsigned long total_ram = lmb_phys_mem_size();
+ unsigned int i;
+
+ numadbg("bootmem_init_nonnuma()\n");
+
+ printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
+ top_of_ram, total_ram);
+ printk(KERN_INFO "Memory hole size: %ldMB\n",
+ (top_of_ram - total_ram) >> 20);
+
+ init_node_masks_nonnuma();
+
+ for (i = 0; i < lmb.memory.cnt; i++) {
+ unsigned long size = lmb_size_bytes(&lmb.memory, i);
+ unsigned long start_pfn, end_pfn;
+
+ if (!size)
+ continue;
+
+ start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
+ end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
+ add_active_range(0, start_pfn, end_pfn);
+ }
+
+ allocate_node_data(0);
+
+ node_set_online(0);
+}
+
+static void __init reserve_range_in_node(int nid, unsigned long start,
+ unsigned long end)
+{
+ numadbg(" reserve_range_in_node(nid[%d],start[%lx],end[%lx]\n",
+ nid, start, end);
+ while (start < end) {
+ unsigned long this_end;
+ int n;
+
+ this_end = nid_range(start, end, &n);
+ if (n == nid) {
+ numadbg(" MATCH reserving range [%lx:%lx]\n",
+ start, this_end);
+ reserve_bootmem_node(NODE_DATA(nid), start,
+ (this_end - start), BOOTMEM_DEFAULT);
+ } else
+ numadbg(" NO MATCH, advancing start to %lx\n",
+ this_end);
+
+ start = this_end;
+ }
+}
+
+static void __init trim_reserved_in_node(int nid)
+{
+ int i;
+
+ numadbg(" trim_reserved_in_node(%d)\n", nid);
+
+ for (i = 0; i < lmb.reserved.cnt; i++) {
+ unsigned long start = lmb.reserved.region[i].base;
+ unsigned long size = lmb_size_bytes(&lmb.reserved, i);
+ unsigned long end = start + size;
+
+ reserve_range_in_node(nid, start, end);
+ }
+}
+
+static void __init bootmem_init_one_node(int nid)
+{
+ struct pglist_data *p;
+
+ numadbg("bootmem_init_one_node(%d)\n", nid);
+
+ p = NODE_DATA(nid);
+
+ if (p->node_spanned_pages) {
+ unsigned long paddr = node_masks[nid].bootmem_paddr;
+ unsigned long end_pfn;
+
+ end_pfn = p->node_start_pfn + p->node_spanned_pages;
+
+ numadbg(" init_bootmem_node(%d, %lx, %lx, %lx)\n",
+ nid, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn);
+
+ init_bootmem_node(p, paddr >> PAGE_SHIFT,
+ p->node_start_pfn, end_pfn);
+
+ numadbg(" free_bootmem_with_active_regions(%d, %lx)\n",
+ nid, end_pfn);
+ free_bootmem_with_active_regions(nid, end_pfn);
+
+ trim_reserved_in_node(nid);
+
+ numadbg(" sparse_memory_present_with_active_regions(%d)\n",
+ nid);
+ sparse_memory_present_with_active_regions(nid);
+ }
+}
+
+static unsigned long __init bootmem_init(unsigned long phys_base)
+{
+ unsigned long end_pfn;
+ int nid;
+
+ end_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
+ max_pfn = max_low_pfn = end_pfn;
+ min_low_pfn = (phys_base >> PAGE_SHIFT);
+
+ if (bootmem_init_numa() < 0)
+ bootmem_init_nonnuma();
+
+ /* XXX cpu notifier XXX */
+
+ for_each_online_node(nid)
+ bootmem_init_one_node(nid);
+
+ sparse_init();
+
+ return end_pfn;
+}
+
+static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
+static int pall_ents __initdata;
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+static unsigned long __ref kernel_map_range(unsigned long pstart,
+ unsigned long pend, pgprot_t prot)
+{
+ unsigned long vstart = PAGE_OFFSET + pstart;
+ unsigned long vend = PAGE_OFFSET + pend;
+ unsigned long alloc_bytes = 0UL;
+
+ if ((vstart & ~PAGE_MASK) || (vend & ~PAGE_MASK)) {
+ prom_printf("kernel_map: Unaligned physmem[%lx:%lx]\n",
+ vstart, vend);
+ prom_halt();
+ }
+
+ while (vstart < vend) {
+ unsigned long this_end, paddr = __pa(vstart);
+ pgd_t *pgd = pgd_offset_k(vstart);
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ pud = pud_offset(pgd, vstart);
+ if (pud_none(*pud)) {
+ pmd_t *new;
+
+ new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+ alloc_bytes += PAGE_SIZE;
+ pud_populate(&init_mm, pud, new);
+ }
+
+ pmd = pmd_offset(pud, vstart);
+ if (!pmd_present(*pmd)) {
+ pte_t *new;
+
+ new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+ alloc_bytes += PAGE_SIZE;
+ pmd_populate_kernel(&init_mm, pmd, new);
+ }
+
+ pte = pte_offset_kernel(pmd, vstart);
+ this_end = (vstart + PMD_SIZE) & PMD_MASK;
+ if (this_end > vend)
+ this_end = vend;
+
+ while (vstart < this_end) {
+ pte_val(*pte) = (paddr | pgprot_val(prot));
+
+ vstart += PAGE_SIZE;
+ paddr += PAGE_SIZE;
+ pte++;
+ }
+ }
+
+ return alloc_bytes;
+}
+
+extern unsigned int kvmap_linear_patch[1];
+#endif /* CONFIG_DEBUG_PAGEALLOC */
+
+static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
+{
+ const unsigned long shift_256MB = 28;
+ const unsigned long mask_256MB = ((1UL << shift_256MB) - 1UL);
+ const unsigned long size_256MB = (1UL << shift_256MB);
+
+ while (start < end) {
+ long remains;
+
+ remains = end - start;
+ if (remains < size_256MB)
+ break;
+
+ if (start & mask_256MB) {
+ start = (start + size_256MB) & ~mask_256MB;
+ continue;
+ }
+
+ while (remains >= size_256MB) {
+ unsigned long index = start >> shift_256MB;
+
+ __set_bit(index, kpte_linear_bitmap);
+
+ start += size_256MB;
+ remains -= size_256MB;
+ }
+ }
+}
+
+static void __init init_kpte_bitmap(void)
+{
+ unsigned long i;
+
+ for (i = 0; i < pall_ents; i++) {
+ unsigned long phys_start, phys_end;
+
+ phys_start = pall[i].phys_addr;
+ phys_end = phys_start + pall[i].reg_size;
+
+ mark_kpte_bitmap(phys_start, phys_end);
+ }
+}
+
+static void __init kernel_physical_mapping_init(void)
+{
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ unsigned long i, mem_alloced = 0UL;
+
+ for (i = 0; i < pall_ents; i++) {
+ unsigned long phys_start, phys_end;
+
+ phys_start = pall[i].phys_addr;
+ phys_end = phys_start + pall[i].reg_size;
+
+ mem_alloced += kernel_map_range(phys_start, phys_end,
+ PAGE_KERNEL);
+ }
+
+ printk("Allocated %ld bytes for kernel page tables.\n",
+ mem_alloced);
+
+ kvmap_linear_patch[0] = 0x01000000; /* nop */
+ flushi(&kvmap_linear_patch[0]);
+
+ __flush_tlb_all();
+#endif
+}
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+void kernel_map_pages(struct page *page, int numpages, int enable)
+{
+ unsigned long phys_start = page_to_pfn(page) << PAGE_SHIFT;
+ unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
+
+ kernel_map_range(phys_start, phys_end,
+ (enable ? PAGE_KERNEL : __pgprot(0)));
+
+ flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
+ PAGE_OFFSET + phys_end);
+
+ /* we should perform an IPI and flush all tlbs,
+ * but that can deadlock->flush only current cpu.
+ */
+ __flush_tlb_kernel_range(PAGE_OFFSET + phys_start,
+ PAGE_OFFSET + phys_end);
+}
+#endif
+
+unsigned long __init find_ecache_flush_span(unsigned long size)
+{
+ int i;
+
+ for (i = 0; i < pavail_ents; i++) {
+ if (pavail[i].reg_size >= size)
+ return pavail[i].phys_addr;
+ }
+
+ return ~0UL;
+}
+
+static void __init tsb_phys_patch(void)
+{
+ struct tsb_ldquad_phys_patch_entry *pquad;
+ struct tsb_phys_patch_entry *p;
+
+ pquad = &__tsb_ldquad_phys_patch;
+ while (pquad < &__tsb_ldquad_phys_patch_end) {
+ unsigned long addr = pquad->addr;
+
+ if (tlb_type == hypervisor)
+ *(unsigned int *) addr = pquad->sun4v_insn;
+ else
+ *(unsigned int *) addr = pquad->sun4u_insn;
+ wmb();
+ __asm__ __volatile__("flush %0"
+ : /* no outputs */
+ : "r" (addr));
+
+ pquad++;
+ }
+
+ p = &__tsb_phys_patch;
+ while (p < &__tsb_phys_patch_end) {
+ unsigned long addr = p->addr;
+
+ *(unsigned int *) addr = p->insn;
+ wmb();
+ __asm__ __volatile__("flush %0"
+ : /* no outputs */
+ : "r" (addr));
+
+ p++;
+ }
+}
+
+/* Don't mark as init, we give this to the Hypervisor. */
+#ifndef CONFIG_DEBUG_PAGEALLOC
+#define NUM_KTSB_DESCR 2
+#else
+#define NUM_KTSB_DESCR 1
+#endif
+static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+
+static void __init sun4v_ktsb_init(void)
+{
+ unsigned long ktsb_pa;
+
+ /* First KTSB for PAGE_SIZE mappings. */
+ ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
+
+ switch (PAGE_SIZE) {
+ case 8 * 1024:
+ default:
+ ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_8K;
+ ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_8K;
+ break;
+
+ case 64 * 1024:
+ ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_64K;
+ ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_64K;
+ break;
+
+ case 512 * 1024:
+ ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_512K;
+ ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_512K;
+ break;
+
+ case 4 * 1024 * 1024:
+ ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_4MB;
+ ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_4MB;
+ break;
+ };
+
+ ktsb_descr[0].assoc = 1;
+ ktsb_descr[0].num_ttes = KERNEL_TSB_NENTRIES;
+ ktsb_descr[0].ctx_idx = 0;
+ ktsb_descr[0].tsb_base = ktsb_pa;
+ ktsb_descr[0].resv = 0;
+
+#ifndef CONFIG_DEBUG_PAGEALLOC
+ /* Second KTSB for 4MB/256MB mappings. */
+ ktsb_pa = (kern_base +
+ ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
+
+ ktsb_descr[1].pgsz_idx = HV_PGSZ_IDX_4MB;
+ ktsb_descr[1].pgsz_mask = (HV_PGSZ_MASK_4MB |
+ HV_PGSZ_MASK_256MB);
+ ktsb_descr[1].assoc = 1;
+ ktsb_descr[1].num_ttes = KERNEL_TSB4M_NENTRIES;
+ ktsb_descr[1].ctx_idx = 0;
+ ktsb_descr[1].tsb_base = ktsb_pa;
+ ktsb_descr[1].resv = 0;
+#endif
+}
+
+void __cpuinit sun4v_ktsb_register(void)
+{
+ unsigned long pa, ret;
+
+ pa = kern_base + ((unsigned long)&ktsb_descr[0] - KERNBASE);
+
+ ret = sun4v_mmu_tsb_ctx0(NUM_KTSB_DESCR, pa);
+ if (ret != 0) {
+ prom_printf("hypervisor_mmu_tsb_ctx0[%lx]: "
+ "errors with %lx\n", pa, ret);
+ prom_halt();
+ }
+}
+
+/* paging_init() sets up the page tables */
+
+static unsigned long last_valid_pfn;
+pgd_t swapper_pg_dir[2048];
+
+static void sun4u_pgprot_init(void);
+static void sun4v_pgprot_init(void);
+
+/* Dummy function */
+void __init setup_per_cpu_areas(void)
+{
+}
+
+void __init paging_init(void)
+{
+ unsigned long end_pfn, shift, phys_base;
+ unsigned long real_end, i;
+
+ /* These build time checkes make sure that the dcache_dirty_cpu()
+ * page->flags usage will work.
+ *
+ * When a page gets marked as dcache-dirty, we store the
+ * cpu number starting at bit 32 in the page->flags. Also,
+ * functions like clear_dcache_dirty_cpu use the cpu mask
+ * in 13-bit signed-immediate instruction fields.
+ */
+
+ /*
+ * Page flags must not reach into upper 32 bits that are used
+ * for the cpu number
+ */
+ BUILD_BUG_ON(NR_PAGEFLAGS > 32);
+
+ /*
+ * The bit fields placed in the high range must not reach below
+ * the 32 bit boundary. Otherwise we cannot place the cpu field
+ * at the 32 bit boundary.
+ */
+ BUILD_BUG_ON(SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH +
+ ilog2(roundup_pow_of_two(NR_CPUS)) > 32);
+
+ BUILD_BUG_ON(NR_CPUS > 4096);
+
+ kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
+ kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
+
+ /* Invalidate both kernel TSBs. */
+ memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
+#ifndef CONFIG_DEBUG_PAGEALLOC
+ memset(swapper_4m_tsb, 0x40, sizeof(swapper_4m_tsb));
+#endif
+
+ if (tlb_type == hypervisor)
+ sun4v_pgprot_init();
+ else
+ sun4u_pgprot_init();
+
+ if (tlb_type == cheetah_plus ||
+ tlb_type == hypervisor)
+ tsb_phys_patch();
+
+ if (tlb_type == hypervisor) {
+ sun4v_patch_tlb_handlers();
+ sun4v_ktsb_init();
+ }
+
+ lmb_init();
+
+ /* Find available physical memory...
+ *
+ * Read it twice in order to work around a bug in openfirmware.
+ * The call to grab this table itself can cause openfirmware to
+ * allocate memory, which in turn can take away some space from
+ * the list of available memory. Reading it twice makes sure
+ * we really do get the final value.
+ */
+ read_obp_translations();
+ read_obp_memory("reg", &pall[0], &pall_ents);
+ read_obp_memory("available", &pavail[0], &pavail_ents);
+ read_obp_memory("available", &pavail[0], &pavail_ents);
+
+ phys_base = 0xffffffffffffffffUL;
+ for (i = 0; i < pavail_ents; i++) {
+ phys_base = min(phys_base, pavail[i].phys_addr);
+ lmb_add(pavail[i].phys_addr, pavail[i].reg_size);
+ }
+
+ lmb_reserve(kern_base, kern_size);
+
+ find_ramdisk(phys_base);
+
+ lmb_enforce_memory_limit(cmdline_memory_size);
+
+ lmb_analyze();
+ lmb_dump_all();
+
+ set_bit(0, mmu_context_bmap);
+
+ shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE);
+
+ real_end = (unsigned long)_end;
+ num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22);
+ printk("Kernel: Using %d locked TLB entries for main kernel image.\n",
+ num_kernel_image_mappings);
+
+ /* Set kernel pgd to upper alias so physical page computations
+ * work.
+ */
+ init_mm.pgd += ((shift) / (sizeof(pgd_t)));
+
+ memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
+
+ /* Now can init the kernel/bad page tables. */
+ pud_set(pud_offset(&swapper_pg_dir[0], 0),
+ swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
+
+ inherit_prom_mappings();
+
+ init_kpte_bitmap();
+
+ /* Ok, we can use our TLB miss and window trap handlers safely. */
+ setup_tba();
+
+ __flush_tlb_all();
+
+ if (tlb_type == hypervisor)
+ sun4v_ktsb_register();
+
+ /* We must setup the per-cpu areas before we pull in the
+ * PROM and the MDESC. The code there fills in cpu and
+ * other information into per-cpu data structures.
+ */
+ real_setup_per_cpu_areas();
+
+ prom_build_devicetree();
+
+ if (tlb_type == hypervisor)
+ sun4v_mdesc_init();
+
+ /* Once the OF device tree and MDESC have been setup, we know
+ * the list of possible cpus. Therefore we can allocate the
+ * IRQ stacks.
+ */
+ for_each_possible_cpu(i) {
+ /* XXX Use node local allocations... XXX */
+ softirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
+ hardirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
+ }
+
+ /* Setup bootmem... */
+ last_valid_pfn = end_pfn = bootmem_init(phys_base);
+
+#ifndef CONFIG_NEED_MULTIPLE_NODES
+ max_mapnr = last_valid_pfn;
+#endif
+ kernel_physical_mapping_init();
+
+ {
+ unsigned long max_zone_pfns[MAX_NR_ZONES];
+
+ memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+
+ max_zone_pfns[ZONE_NORMAL] = end_pfn;
+
+ free_area_init_nodes(max_zone_pfns);
+ }
+
+ printk("Booting Linux...\n");
+}
+
+int __init page_in_phys_avail(unsigned long paddr)
+{
+ int i;
+
+ paddr &= PAGE_MASK;
+
+ for (i = 0; i < pavail_ents; i++) {
+ unsigned long start, end;
+
+ start = pavail[i].phys_addr;
+ end = start + pavail[i].reg_size;
+
+ if (paddr >= start && paddr < end)
+ return 1;
+ }
+ if (paddr >= kern_base && paddr < (kern_base + kern_size))
+ return 1;
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (paddr >= __pa(initrd_start) &&
+ paddr < __pa(PAGE_ALIGN(initrd_end)))
+ return 1;
+#endif
+
+ return 0;
+}
+
+static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
+static int pavail_rescan_ents __initdata;
+
+/* Certain OBP calls, such as fetching "available" properties, can
+ * claim physical memory. So, along with initializing the valid
+ * address bitmap, what we do here is refetch the physical available
+ * memory list again, and make sure it provides at least as much
+ * memory as 'pavail' does.
+ */
+static void __init setup_valid_addr_bitmap_from_pavail(void)
+{
+ int i;
+
+ read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
+
+ for (i = 0; i < pavail_ents; i++) {
+ unsigned long old_start, old_end;
+
+ old_start = pavail[i].phys_addr;
+ old_end = old_start + pavail[i].reg_size;
+ while (old_start < old_end) {
+ int n;
+
+ for (n = 0; n < pavail_rescan_ents; n++) {
+ unsigned long new_start, new_end;
+
+ new_start = pavail_rescan[n].phys_addr;
+ new_end = new_start +
+ pavail_rescan[n].reg_size;
+
+ if (new_start <= old_start &&
+ new_end >= (old_start + PAGE_SIZE)) {
+ set_bit(old_start >> 22,
+ sparc64_valid_addr_bitmap);
+ goto do_next_page;
+ }
+ }
+
+ prom_printf("mem_init: Lost memory in pavail\n");
+ prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
+ pavail[i].phys_addr,
+ pavail[i].reg_size);
+ prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
+ pavail_rescan[i].phys_addr,
+ pavail_rescan[i].reg_size);
+ prom_printf("mem_init: Cannot continue, aborting.\n");
+ prom_halt();
+
+ do_next_page:
+ old_start += PAGE_SIZE;
+ }
+ }
+}
+
+void __init mem_init(void)
+{
+ unsigned long codepages, datapages, initpages;
+ unsigned long addr, last;
+ int i;
+
+ i = last_valid_pfn >> ((22 - PAGE_SHIFT) + 6);
+ i += 1;
+ sparc64_valid_addr_bitmap = (unsigned long *) alloc_bootmem(i << 3);
+ if (sparc64_valid_addr_bitmap == NULL) {
+ prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
+ prom_halt();
+ }
+ memset(sparc64_valid_addr_bitmap, 0, i << 3);
+
+ addr = PAGE_OFFSET + kern_base;
+ last = PAGE_ALIGN(kern_size) + addr;
+ while (addr < last) {
+ set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
+ addr += PAGE_SIZE;
+ }
+
+ setup_valid_addr_bitmap_from_pavail();
+
+ high_memory = __va(last_valid_pfn << PAGE_SHIFT);
+
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ for_each_online_node(i) {
+ if (NODE_DATA(i)->node_spanned_pages != 0) {
+ totalram_pages +=
+ free_all_bootmem_node(NODE_DATA(i));
+ }
+ }
+#else
+ totalram_pages = free_all_bootmem();
+#endif
+
+ /* We subtract one to account for the mem_map_zero page
+ * allocated below.
+ */
+ totalram_pages -= 1;
+ num_physpages = totalram_pages;
+
+ /*
+ * Set up the zero page, mark it reserved, so that page count
+ * is not manipulated when freeing the page from user ptes.
+ */
+ mem_map_zero = alloc_pages(GFP_KERNEL|__GFP_ZERO, 0);
+ if (mem_map_zero == NULL) {
+ prom_printf("paging_init: Cannot alloc zero page.\n");
+ prom_halt();
+ }
+ SetPageReserved(mem_map_zero);
+
+ codepages = (((unsigned long) _etext) - ((unsigned long) _start));
+ codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
+ datapages = (((unsigned long) _edata) - ((unsigned long) _etext));
+ datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
+ initpages = (((unsigned long) __init_end) - ((unsigned long) __init_begin));
+ initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
+
+ printk("Memory: %luk available (%ldk kernel code, %ldk data, %ldk init) [%016lx,%016lx]\n",
+ nr_free_pages() << (PAGE_SHIFT-10),
+ codepages << (PAGE_SHIFT-10),
+ datapages << (PAGE_SHIFT-10),
+ initpages << (PAGE_SHIFT-10),
+ PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT));
+
+ if (tlb_type == cheetah || tlb_type == cheetah_plus)
+ cheetah_ecache_flush_init();
+}
+
+void free_initmem(void)
+{
+ unsigned long addr, initend;
+ int do_free = 1;
+
+ /* If the physical memory maps were trimmed by kernel command
+ * line options, don't even try freeing this initmem stuff up.
+ * The kernel image could have been in the trimmed out region
+ * and if so the freeing below will free invalid page structs.
+ */
+ if (cmdline_memory_size)
+ do_free = 0;
+
+ /*
+ * The init section is aligned to 8k in vmlinux.lds. Page align for >8k pagesizes.
+ */
+ addr = PAGE_ALIGN((unsigned long)(__init_begin));
+ initend = (unsigned long)(__init_end) & PAGE_MASK;
+ for (; addr < initend; addr += PAGE_SIZE) {
+ unsigned long page;
+ struct page *p;
+
+ page = (addr +
+ ((unsigned long) __va(kern_base)) -
+ ((unsigned long) KERNBASE));
+ memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
+
+ if (do_free) {
+ p = virt_to_page(page);
+
+ ClearPageReserved(p);
+ init_page_count(p);
+ __free_page(p);
+ num_physpages++;
+ totalram_pages++;
+ }
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (start < end)
+ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+ for (; start < end; start += PAGE_SIZE) {
+ struct page *p = virt_to_page(start);
+
+ ClearPageReserved(p);
+ init_page_count(p);
+ __free_page(p);
+ num_physpages++;
+ totalram_pages++;
+ }
+}
+#endif
+
+#define _PAGE_CACHE_4U (_PAGE_CP_4U | _PAGE_CV_4U)
+#define _PAGE_CACHE_4V (_PAGE_CP_4V | _PAGE_CV_4V)
+#define __DIRTY_BITS_4U (_PAGE_MODIFIED_4U | _PAGE_WRITE_4U | _PAGE_W_4U)
+#define __DIRTY_BITS_4V (_PAGE_MODIFIED_4V | _PAGE_WRITE_4V | _PAGE_W_4V)
+#define __ACCESS_BITS_4U (_PAGE_ACCESSED_4U | _PAGE_READ_4U | _PAGE_R)
+#define __ACCESS_BITS_4V (_PAGE_ACCESSED_4V | _PAGE_READ_4V | _PAGE_R)
+
+pgprot_t PAGE_KERNEL __read_mostly;
+EXPORT_SYMBOL(PAGE_KERNEL);
+
+pgprot_t PAGE_KERNEL_LOCKED __read_mostly;
+pgprot_t PAGE_COPY __read_mostly;
+
+pgprot_t PAGE_SHARED __read_mostly;
+EXPORT_SYMBOL(PAGE_SHARED);
+
+unsigned long pg_iobits __read_mostly;
+
+unsigned long _PAGE_IE __read_mostly;
+EXPORT_SYMBOL(_PAGE_IE);
+
+unsigned long _PAGE_E __read_mostly;
+EXPORT_SYMBOL(_PAGE_E);
+
+unsigned long _PAGE_CACHE __read_mostly;
+EXPORT_SYMBOL(_PAGE_CACHE);
+
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+unsigned long vmemmap_table[VMEMMAP_SIZE];
+
+int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
+{
+ unsigned long vstart = (unsigned long) start;
+ unsigned long vend = (unsigned long) (start + nr);
+ unsigned long phys_start = (vstart - VMEMMAP_BASE);
+ unsigned long phys_end = (vend - VMEMMAP_BASE);
+ unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
+ unsigned long end = VMEMMAP_ALIGN(phys_end);
+ unsigned long pte_base;
+
+ pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
+ _PAGE_CP_4U | _PAGE_CV_4U |
+ _PAGE_P_4U | _PAGE_W_4U);
+ if (tlb_type == hypervisor)
+ pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4V |
+ _PAGE_CP_4V | _PAGE_CV_4V |
+ _PAGE_P_4V | _PAGE_W_4V);
+
+ for (; addr < end; addr += VMEMMAP_CHUNK) {
+ unsigned long *vmem_pp =
+ vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
+ void *block;
+
+ if (!(*vmem_pp & _PAGE_VALID)) {
+ block = vmemmap_alloc_block(1UL << 22, node);
+ if (!block)
+ return -ENOMEM;
+
+ *vmem_pp = pte_base | __pa(block);
+
+ printk(KERN_INFO "[%p-%p] page_structs=%lu "
+ "node=%d entry=%lu/%lu\n", start, block, nr,
+ node,
+ addr >> VMEMMAP_CHUNK_SHIFT,
+ VMEMMAP_SIZE >> VMEMMAP_CHUNK_SHIFT);
+ }
+ }
+ return 0;
+}
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
+
+static void prot_init_common(unsigned long page_none,
+ unsigned long page_shared,
+ unsigned long page_copy,
+ unsigned long page_readonly,
+ unsigned long page_exec_bit)
+{
+ PAGE_COPY = __pgprot(page_copy);
+ PAGE_SHARED = __pgprot(page_shared);
+
+ protection_map[0x0] = __pgprot(page_none);
+ protection_map[0x1] = __pgprot(page_readonly & ~page_exec_bit);
+ protection_map[0x2] = __pgprot(page_copy & ~page_exec_bit);
+ protection_map[0x3] = __pgprot(page_copy & ~page_exec_bit);
+ protection_map[0x4] = __pgprot(page_readonly);
+ protection_map[0x5] = __pgprot(page_readonly);
+ protection_map[0x6] = __pgprot(page_copy);
+ protection_map[0x7] = __pgprot(page_copy);
+ protection_map[0x8] = __pgprot(page_none);
+ protection_map[0x9] = __pgprot(page_readonly & ~page_exec_bit);
+ protection_map[0xa] = __pgprot(page_shared & ~page_exec_bit);
+ protection_map[0xb] = __pgprot(page_shared & ~page_exec_bit);
+ protection_map[0xc] = __pgprot(page_readonly);
+ protection_map[0xd] = __pgprot(page_readonly);
+ protection_map[0xe] = __pgprot(page_shared);
+ protection_map[0xf] = __pgprot(page_shared);
+}
+
+static void __init sun4u_pgprot_init(void)
+{
+ unsigned long page_none, page_shared, page_copy, page_readonly;
+ unsigned long page_exec_bit;
+
+ PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
+ _PAGE_CACHE_4U | _PAGE_P_4U |
+ __ACCESS_BITS_4U | __DIRTY_BITS_4U |
+ _PAGE_EXEC_4U);
+ PAGE_KERNEL_LOCKED = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
+ _PAGE_CACHE_4U | _PAGE_P_4U |
+ __ACCESS_BITS_4U | __DIRTY_BITS_4U |
+ _PAGE_EXEC_4U | _PAGE_L_4U);
+
+ _PAGE_IE = _PAGE_IE_4U;
+ _PAGE_E = _PAGE_E_4U;
+ _PAGE_CACHE = _PAGE_CACHE_4U;
+
+ pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4U | __DIRTY_BITS_4U |
+ __ACCESS_BITS_4U | _PAGE_E_4U);
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^
+ 0xfffff80000000000UL;
+#else
+ kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
+ 0xfffff80000000000UL;
+#endif
+ kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
+ _PAGE_P_4U | _PAGE_W_4U);
+
+ /* XXX Should use 256MB on Panther. XXX */
+ kern_linear_pte_xor[1] = kern_linear_pte_xor[0];
+
+ _PAGE_SZBITS = _PAGE_SZBITS_4U;
+ _PAGE_ALL_SZ_BITS = (_PAGE_SZ4MB_4U | _PAGE_SZ512K_4U |
+ _PAGE_SZ64K_4U | _PAGE_SZ8K_4U |
+ _PAGE_SZ32MB_4U | _PAGE_SZ256MB_4U);
+
+
+ page_none = _PAGE_PRESENT_4U | _PAGE_ACCESSED_4U | _PAGE_CACHE_4U;
+ page_shared = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+ __ACCESS_BITS_4U | _PAGE_WRITE_4U | _PAGE_EXEC_4U);
+ page_copy = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+ __ACCESS_BITS_4U | _PAGE_EXEC_4U);
+ page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
+ __ACCESS_BITS_4U | _PAGE_EXEC_4U);
+
+ page_exec_bit = _PAGE_EXEC_4U;
+
+ prot_init_common(page_none, page_shared, page_copy, page_readonly,
+ page_exec_bit);
+}
+
+static void __init sun4v_pgprot_init(void)
+{
+ unsigned long page_none, page_shared, page_copy, page_readonly;
+ unsigned long page_exec_bit;
+
+ PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4V | _PAGE_VALID |
+ _PAGE_CACHE_4V | _PAGE_P_4V |
+ __ACCESS_BITS_4V | __DIRTY_BITS_4V |
+ _PAGE_EXEC_4V);
+ PAGE_KERNEL_LOCKED = PAGE_KERNEL;
+
+ _PAGE_IE = _PAGE_IE_4V;
+ _PAGE_E = _PAGE_E_4V;
+ _PAGE_CACHE = _PAGE_CACHE_4V;
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
+ 0xfffff80000000000UL;
+#else
+ kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
+ 0xfffff80000000000UL;
+#endif
+ kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
+ _PAGE_P_4V | _PAGE_W_4V);
+
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
+ 0xfffff80000000000UL;
+#else
+ kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
+ 0xfffff80000000000UL;
+#endif
+ kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
+ _PAGE_P_4V | _PAGE_W_4V);
+
+ pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4V | __DIRTY_BITS_4V |
+ __ACCESS_BITS_4V | _PAGE_E_4V);
+
+ _PAGE_SZBITS = _PAGE_SZBITS_4V;
+ _PAGE_ALL_SZ_BITS = (_PAGE_SZ16GB_4V | _PAGE_SZ2GB_4V |
+ _PAGE_SZ256MB_4V | _PAGE_SZ32MB_4V |
+ _PAGE_SZ4MB_4V | _PAGE_SZ512K_4V |
+ _PAGE_SZ64K_4V | _PAGE_SZ8K_4V);
+
+ page_none = _PAGE_PRESENT_4V | _PAGE_ACCESSED_4V | _PAGE_CACHE_4V;
+ page_shared = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+ __ACCESS_BITS_4V | _PAGE_WRITE_4V | _PAGE_EXEC_4V);
+ page_copy = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+ __ACCESS_BITS_4V | _PAGE_EXEC_4V);
+ page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
+ __ACCESS_BITS_4V | _PAGE_EXEC_4V);
+
+ page_exec_bit = _PAGE_EXEC_4V;
+
+ prot_init_common(page_none, page_shared, page_copy, page_readonly,
+ page_exec_bit);
+}
+
+unsigned long pte_sz_bits(unsigned long sz)
+{
+ if (tlb_type == hypervisor) {
+ switch (sz) {
+ case 8 * 1024:
+ default:
+ return _PAGE_SZ8K_4V;
+ case 64 * 1024:
+ return _PAGE_SZ64K_4V;
+ case 512 * 1024:
+ return _PAGE_SZ512K_4V;
+ case 4 * 1024 * 1024:
+ return _PAGE_SZ4MB_4V;
+ };
+ } else {
+ switch (sz) {
+ case 8 * 1024:
+ default:
+ return _PAGE_SZ8K_4U;
+ case 64 * 1024:
+ return _PAGE_SZ64K_4U;
+ case 512 * 1024:
+ return _PAGE_SZ512K_4U;
+ case 4 * 1024 * 1024:
+ return _PAGE_SZ4MB_4U;
+ };
+ }
+}
+
+pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space, unsigned long page_size)
+{
+ pte_t pte;
+
+ pte_val(pte) = page | pgprot_val(pgprot_noncached(prot));
+ pte_val(pte) |= (((unsigned long)space) << 32);
+ pte_val(pte) |= pte_sz_bits(page_size);
+
+ return pte;
+}
+
+static unsigned long kern_large_tte(unsigned long paddr)
+{
+ unsigned long val;
+
+ val = (_PAGE_VALID | _PAGE_SZ4MB_4U |
+ _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U |
+ _PAGE_EXEC_4U | _PAGE_L_4U | _PAGE_W_4U);
+ if (tlb_type == hypervisor)
+ val = (_PAGE_VALID | _PAGE_SZ4MB_4V |
+ _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V |
+ _PAGE_EXEC_4V | _PAGE_W_4V);
+
+ return val | paddr;
+}
+
+/* If not locked, zap it. */
+void __flush_tlb_all(void)
+{
+ unsigned long pstate;
+ int i;
+
+ __asm__ __volatile__("flushw\n\t"
+ "rdpr %%pstate, %0\n\t"
+ "wrpr %0, %1, %%pstate"
+ : "=r" (pstate)
+ : "i" (PSTATE_IE));
+ if (tlb_type == hypervisor) {
+ sun4v_mmu_demap_all();
+ } else if (tlb_type == spitfire) {
+ for (i = 0; i < 64; i++) {
+ /* Spitfire Errata #32 workaround */
+ /* NOTE: Always runs on spitfire, so no
+ * cheetah+ page size encodings.
+ */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
+ if (!(spitfire_get_dtlb_data(i) & _PAGE_L_4U)) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
+ spitfire_put_dtlb_data(i, 0x0UL);
+ }
+
+ /* Spitfire Errata #32 workaround */
+ /* NOTE: Always runs on spitfire, so no
+ * cheetah+ page size encodings.
+ */
+ __asm__ __volatile__("stxa %0, [%1] %2\n\t"
+ "flush %%g6"
+ : /* No outputs */
+ : "r" (0),
+ "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
+
+ if (!(spitfire_get_itlb_data(i) & _PAGE_L_4U)) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
+ spitfire_put_itlb_data(i, 0x0UL);
+ }
+ }
+ } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ cheetah_flush_dtlb_all();
+ cheetah_flush_itlb_all();
+ }
+ __asm__ __volatile__("wrpr %0, 0, %%pstate"
+ : : "r" (pstate));
+}
diff --git a/arch/sparc64/mm/init.h b/arch/sparc/mm/init_64.h
index 16063870a489..16063870a489 100644
--- a/arch/sparc64/mm/init.h
+++ b/arch/sparc/mm/init_64.h
diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c
index daadf5f88050..005e758a4db7 100644
--- a/arch/sparc/mm/io-unit.c
+++ b/arch/sparc/mm/io-unit.c
@@ -156,8 +156,8 @@ static void iounit_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int
spin_lock_irqsave(&iounit->lock, flags);
while (sz != 0) {
--sz;
- sg->dvma_address = iounit_get_area(iounit, (unsigned long) sg_virt(sg), sg->length);
- sg->dvma_length = sg->length;
+ sg->dma_address = iounit_get_area(iounit, (unsigned long) sg_virt(sg), sg->length);
+ sg->dma_length = sg->length;
sg = sg_next(sg);
}
spin_unlock_irqrestore(&iounit->lock, flags);
@@ -186,8 +186,8 @@ static void iounit_release_scsi_sgl(struct device *dev, struct scatterlist *sg,
spin_lock_irqsave(&iounit->lock, flags);
while (sz != 0) {
--sz;
- len = ((sg->dvma_address & ~PAGE_MASK) + sg->length + (PAGE_SIZE-1)) >> PAGE_SHIFT;
- vaddr = (sg->dvma_address - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
+ len = ((sg->dma_address & ~PAGE_MASK) + sg->length + (PAGE_SIZE-1)) >> PAGE_SHIFT;
+ vaddr = (sg->dma_address - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
IOD(("iounit_release %08lx-%08lx\n", (long)vaddr, (long)len+vaddr));
for (len += vaddr; vaddr < len; vaddr++)
clear_bit(vaddr, iounit->bmap);
diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c
index e7a499e3aa3c..b2e6e73888b5 100644
--- a/arch/sparc/mm/iommu.c
+++ b/arch/sparc/mm/iommu.c
@@ -245,8 +245,8 @@ static void iommu_get_scsi_sgl_noflush(struct device *dev, struct scatterlist *s
while (sz != 0) {
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
- sg->dvma_length = (__u32) sg->length;
+ sg->dma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
+ sg->dma_length = sg->length;
sg = sg_next(sg);
}
}
@@ -259,8 +259,8 @@ static void iommu_get_scsi_sgl_gflush(struct device *dev, struct scatterlist *sg
while (sz != 0) {
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
- sg->dvma_length = (__u32) sg->length;
+ sg->dma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
+ sg->dma_length = sg->length;
sg = sg_next(sg);
}
}
@@ -290,8 +290,8 @@ static void iommu_get_scsi_sgl_pflush(struct device *dev, struct scatterlist *sg
}
}
- sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
- sg->dvma_length = (__u32) sg->length;
+ sg->dma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
+ sg->dma_length = sg->length;
sg = sg_next(sg);
}
}
@@ -330,8 +330,8 @@ static void iommu_release_scsi_sgl(struct device *dev, struct scatterlist *sg, i
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- iommu_release_one(dev, sg->dvma_address & PAGE_MASK, n);
- sg->dvma_address = 0x21212121;
+ iommu_release_one(dev, sg->dma_address & PAGE_MASK, n);
+ sg->dma_address = 0x21212121;
sg = sg_next(sg);
}
}
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index dd8aa36f366c..fe7ed08390bb 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -1312,10 +1312,8 @@ void __init srmmu_paging_init(void)
#endif
poke_srmmu();
-#ifdef CONFIG_SUN_IO
srmmu_allocate_ptable_skeleton(sparc_iomap.start, IOBASE_END);
srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
-#endif
srmmu_allocate_ptable_skeleton(
__fix_to_virt(__end_of_fixed_addresses - 1), FIXADDR_TOP);
@@ -1916,18 +1914,6 @@ static void __cpuinit poke_viking(void)
mreg |= VIKING_SBENABLE;
mreg &= ~(VIKING_ACENABLE);
srmmu_set_mmureg(mreg);
-
-#ifdef CONFIG_SMP
- /* Avoid unnecessary cross calls. */
- BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all);
- BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm);
- BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range);
- BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page);
- BTFIXUPCOPY_CALL(__flush_page_to_ram, local_flush_page_to_ram);
- BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns);
- BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma);
- btfixup();
-#endif
}
static void __init init_viking(void)
@@ -2272,6 +2258,17 @@ void __init ld_mmu_srmmu(void)
BTFIXUPSET_CALL(__flush_page_to_ram, smp_flush_page_to_ram, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(flush_sig_insns, smp_flush_sig_insns, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(flush_page_for_dma, smp_flush_page_for_dma, BTFIXUPCALL_NORM);
+
+ if (poke_srmmu == poke_viking) {
+ /* Avoid unnecessary cross calls. */
+ BTFIXUPCOPY_CALL(flush_cache_all, local_flush_cache_all);
+ BTFIXUPCOPY_CALL(flush_cache_mm, local_flush_cache_mm);
+ BTFIXUPCOPY_CALL(flush_cache_range, local_flush_cache_range);
+ BTFIXUPCOPY_CALL(flush_cache_page, local_flush_cache_page);
+ BTFIXUPCOPY_CALL(__flush_page_to_ram, local_flush_page_to_ram);
+ BTFIXUPCOPY_CALL(flush_sig_insns, local_flush_sig_insns);
+ BTFIXUPCOPY_CALL(flush_page_for_dma, local_flush_page_for_dma);
+ }
#endif
if (sparc_cpu_model == sun4d)
diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
index fe65aeeb3947..2ffacd67c424 100644
--- a/arch/sparc/mm/sun4c.c
+++ b/arch/sparc/mm/sun4c.c
@@ -18,6 +18,7 @@
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
+#include <asm/sections.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
@@ -240,9 +241,7 @@ void sun4c_complete_all_stores(void)
_unused = sun4c_get_context();
sun4c_set_context(_unused);
-#ifdef CONFIG_SUN_AUXIO
_unused = get_auxio();
-#endif
}
/* Bootup utility functions. */
@@ -1124,8 +1123,8 @@ static void sun4c_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int s
{
while (sz != 0) {
--sz;
- sg->dvma_address = (__u32)sun4c_lockarea(sg_virt(sg), sg->length);
- sg->dvma_length = sg->length;
+ sg->dma_address = (__u32)sun4c_lockarea(sg_virt(sg), sg->length);
+ sg->dma_length = sg->length;
sg = sg_next(sg);
}
}
@@ -1141,7 +1140,7 @@ static void sun4c_release_scsi_sgl(struct device *dev, struct scatterlist *sg, i
{
while (sz != 0) {
--sz;
- sun4c_unlockarea((char *)sg->dvma_address, sg->length);
+ sun4c_unlockarea((char *)sg->dma_address, sg->length);
sg = sg_next(sg);
}
}
@@ -1953,7 +1952,6 @@ void sun4c_update_mmu_cache(struct vm_area_struct *vma, unsigned long address, p
}
extern void sparc_context_init(int);
-extern unsigned long end;
extern unsigned long bootmem_init(unsigned long *pages_avail);
extern unsigned long last_valid_pfn;
@@ -1964,7 +1962,7 @@ void __init sun4c_paging_init(void)
extern struct resource sparc_iomap;
unsigned long end_pfn, pages_avail;
- kernel_end = (unsigned long) &end;
+ kernel_end = (unsigned long) &_end;
kernel_end = SUN4C_REAL_PGDIR_ALIGN(kernel_end);
pages_avail = 0;
diff --git a/arch/sparc64/mm/tlb.c b/arch/sparc/mm/tlb.c
index d8f21e24a82f..d8f21e24a82f 100644
--- a/arch/sparc64/mm/tlb.c
+++ b/arch/sparc/mm/tlb.c
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
new file mode 100644
index 000000000000..36a0813f9517
--- /dev/null
+++ b/arch/sparc/mm/tsb.c
@@ -0,0 +1,503 @@
+/* arch/sparc64/mm/tsb.c
+ *
+ * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/preempt.h>
+#include <asm/system.h>
+#include <asm/page.h>
+#include <asm/tlbflush.h>
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/tsb.h>
+#include <asm/oplib.h>
+
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
+
+static inline unsigned long tsb_hash(unsigned long vaddr, unsigned long hash_shift, unsigned long nentries)
+{
+ vaddr >>= hash_shift;
+ return vaddr & (nentries - 1);
+}
+
+static inline int tag_compare(unsigned long tag, unsigned long vaddr)
+{
+ return (tag == (vaddr >> 22));
+}
+
+/* TSB flushes need only occur on the processor initiating the address
+ * space modification, not on each cpu the address space has run on.
+ * Only the TLB flush needs that treatment.
+ */
+
+void flush_tsb_kernel_range(unsigned long start, unsigned long end)
+{
+ unsigned long v;
+
+ for (v = start; v < end; v += PAGE_SIZE) {
+ unsigned long hash = tsb_hash(v, PAGE_SHIFT,
+ KERNEL_TSB_NENTRIES);
+ struct tsb *ent = &swapper_tsb[hash];
+
+ if (tag_compare(ent->tag, v))
+ ent->tag = (1UL << TSB_TAG_INVALID_BIT);
+ }
+}
+
+static void __flush_tsb_one(struct mmu_gather *mp, unsigned long hash_shift, unsigned long tsb, unsigned long nentries)
+{
+ unsigned long i;
+
+ for (i = 0; i < mp->tlb_nr; i++) {
+ unsigned long v = mp->vaddrs[i];
+ unsigned long tag, ent, hash;
+
+ v &= ~0x1UL;
+
+ hash = tsb_hash(v, hash_shift, nentries);
+ ent = tsb + (hash * sizeof(struct tsb));
+ tag = (v >> 22UL);
+
+ tsb_flush(ent, tag);
+ }
+}
+
+void flush_tsb_user(struct mmu_gather *mp)
+{
+ struct mm_struct *mm = mp->mm;
+ unsigned long nentries, base, flags;
+
+ spin_lock_irqsave(&mm->context.lock, flags);
+
+ base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
+ nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ base = __pa(base);
+ __flush_tsb_one(mp, PAGE_SHIFT, base, nentries);
+
+#ifdef CONFIG_HUGETLB_PAGE
+ if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
+ base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
+ nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ base = __pa(base);
+ __flush_tsb_one(mp, HPAGE_SHIFT, base, nentries);
+ }
+#endif
+ spin_unlock_irqrestore(&mm->context.lock, flags);
+}
+
+#if defined(CONFIG_SPARC64_PAGE_SIZE_8KB)
+#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_8K
+#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_8K
+#elif defined(CONFIG_SPARC64_PAGE_SIZE_64KB)
+#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_64K
+#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_64K
+#else
+#error Broken base page size setting...
+#endif
+
+#ifdef CONFIG_HUGETLB_PAGE
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_64K
+#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_64K
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
+#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_512K
+#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_512K
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
+#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_4MB
+#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_4MB
+#else
+#error Broken huge page size setting...
+#endif
+#endif
+
+static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigned long tsb_bytes)
+{
+ unsigned long tsb_reg, base, tsb_paddr;
+ unsigned long page_sz, tte;
+
+ mm->context.tsb_block[tsb_idx].tsb_nentries =
+ tsb_bytes / sizeof(struct tsb);
+
+ base = TSBMAP_BASE;
+ tte = pgprot_val(PAGE_KERNEL_LOCKED);
+ tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb);
+ BUG_ON(tsb_paddr & (tsb_bytes - 1UL));
+
+ /* Use the smallest page size that can map the whole TSB
+ * in one TLB entry.
+ */
+ switch (tsb_bytes) {
+ case 8192 << 0:
+ tsb_reg = 0x0UL;
+#ifdef DCACHE_ALIASING_POSSIBLE
+ base += (tsb_paddr & 8192);
+#endif
+ page_sz = 8192;
+ break;
+
+ case 8192 << 1:
+ tsb_reg = 0x1UL;
+ page_sz = 64 * 1024;
+ break;
+
+ case 8192 << 2:
+ tsb_reg = 0x2UL;
+ page_sz = 64 * 1024;
+ break;
+
+ case 8192 << 3:
+ tsb_reg = 0x3UL;
+ page_sz = 64 * 1024;
+ break;
+
+ case 8192 << 4:
+ tsb_reg = 0x4UL;
+ page_sz = 512 * 1024;
+ break;
+
+ case 8192 << 5:
+ tsb_reg = 0x5UL;
+ page_sz = 512 * 1024;
+ break;
+
+ case 8192 << 6:
+ tsb_reg = 0x6UL;
+ page_sz = 512 * 1024;
+ break;
+
+ case 8192 << 7:
+ tsb_reg = 0x7UL;
+ page_sz = 4 * 1024 * 1024;
+ break;
+
+ default:
+ printk(KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n",
+ current->comm, current->pid, tsb_bytes);
+ do_exit(SIGSEGV);
+ };
+ tte |= pte_sz_bits(page_sz);
+
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+ /* Physical mapping, no locked TLB entry for TSB. */
+ tsb_reg |= tsb_paddr;
+
+ mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
+ mm->context.tsb_block[tsb_idx].tsb_map_vaddr = 0;
+ mm->context.tsb_block[tsb_idx].tsb_map_pte = 0;
+ } else {
+ tsb_reg |= base;
+ tsb_reg |= (tsb_paddr & (page_sz - 1UL));
+ tte |= (tsb_paddr & ~(page_sz - 1UL));
+
+ mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
+ mm->context.tsb_block[tsb_idx].tsb_map_vaddr = base;
+ mm->context.tsb_block[tsb_idx].tsb_map_pte = tte;
+ }
+
+ /* Setup the Hypervisor TSB descriptor. */
+ if (tlb_type == hypervisor) {
+ struct hv_tsb_descr *hp = &mm->context.tsb_descr[tsb_idx];
+
+ switch (tsb_idx) {
+ case MM_TSB_BASE:
+ hp->pgsz_idx = HV_PGSZ_IDX_BASE;
+ break;
+#ifdef CONFIG_HUGETLB_PAGE
+ case MM_TSB_HUGE:
+ hp->pgsz_idx = HV_PGSZ_IDX_HUGE;
+ break;
+#endif
+ default:
+ BUG();
+ };
+ hp->assoc = 1;
+ hp->num_ttes = tsb_bytes / 16;
+ hp->ctx_idx = 0;
+ switch (tsb_idx) {
+ case MM_TSB_BASE:
+ hp->pgsz_mask = HV_PGSZ_MASK_BASE;
+ break;
+#ifdef CONFIG_HUGETLB_PAGE
+ case MM_TSB_HUGE:
+ hp->pgsz_mask = HV_PGSZ_MASK_HUGE;
+ break;
+#endif
+ default:
+ BUG();
+ };
+ hp->tsb_base = tsb_paddr;
+ hp->resv = 0;
+ }
+}
+
+static struct kmem_cache *tsb_caches[8] __read_mostly;
+
+static const char *tsb_cache_names[8] = {
+ "tsb_8KB",
+ "tsb_16KB",
+ "tsb_32KB",
+ "tsb_64KB",
+ "tsb_128KB",
+ "tsb_256KB",
+ "tsb_512KB",
+ "tsb_1MB",
+};
+
+void __init pgtable_cache_init(void)
+{
+ unsigned long i;
+
+ for (i = 0; i < 8; i++) {
+ unsigned long size = 8192 << i;
+ const char *name = tsb_cache_names[i];
+
+ tsb_caches[i] = kmem_cache_create(name,
+ size, size,
+ 0, NULL);
+ if (!tsb_caches[i]) {
+ prom_printf("Could not create %s cache\n", name);
+ prom_halt();
+ }
+ }
+}
+
+int sysctl_tsb_ratio = -2;
+
+static unsigned long tsb_size_to_rss_limit(unsigned long new_size)
+{
+ unsigned long num_ents = (new_size / sizeof(struct tsb));
+
+ if (sysctl_tsb_ratio < 0)
+ return num_ents - (num_ents >> -sysctl_tsb_ratio);
+ else
+ return num_ents + (num_ents >> sysctl_tsb_ratio);
+}
+
+/* When the RSS of an address space exceeds tsb_rss_limit for a TSB,
+ * do_sparc64_fault() invokes this routine to try and grow it.
+ *
+ * When we reach the maximum TSB size supported, we stick ~0UL into
+ * tsb_rss_limit for that TSB so the grow checks in do_sparc64_fault()
+ * will not trigger any longer.
+ *
+ * The TSB can be anywhere from 8K to 1MB in size, in increasing powers
+ * of two. The TSB must be aligned to it's size, so f.e. a 512K TSB
+ * must be 512K aligned. It also must be physically contiguous, so we
+ * cannot use vmalloc().
+ *
+ * The idea here is to grow the TSB when the RSS of the process approaches
+ * the number of entries that the current TSB can hold at once. Currently,
+ * we trigger when the RSS hits 3/4 of the TSB capacity.
+ */
+void tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss)
+{
+ unsigned long max_tsb_size = 1 * 1024 * 1024;
+ unsigned long new_size, old_size, flags;
+ struct tsb *old_tsb, *new_tsb;
+ unsigned long new_cache_index, old_cache_index;
+ unsigned long new_rss_limit;
+ gfp_t gfp_flags;
+
+ if (max_tsb_size > (PAGE_SIZE << MAX_ORDER))
+ max_tsb_size = (PAGE_SIZE << MAX_ORDER);
+
+ new_cache_index = 0;
+ for (new_size = 8192; new_size < max_tsb_size; new_size <<= 1UL) {
+ new_rss_limit = tsb_size_to_rss_limit(new_size);
+ if (new_rss_limit > rss)
+ break;
+ new_cache_index++;
+ }
+
+ if (new_size == max_tsb_size)
+ new_rss_limit = ~0UL;
+
+retry_tsb_alloc:
+ gfp_flags = GFP_KERNEL;
+ if (new_size > (PAGE_SIZE * 2))
+ gfp_flags = __GFP_NOWARN | __GFP_NORETRY;
+
+ new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index],
+ gfp_flags, numa_node_id());
+ if (unlikely(!new_tsb)) {
+ /* Not being able to fork due to a high-order TSB
+ * allocation failure is very bad behavior. Just back
+ * down to a 0-order allocation and force no TSB
+ * growing for this address space.
+ */
+ if (mm->context.tsb_block[tsb_index].tsb == NULL &&
+ new_cache_index > 0) {
+ new_cache_index = 0;
+ new_size = 8192;
+ new_rss_limit = ~0UL;
+ goto retry_tsb_alloc;
+ }
+
+ /* If we failed on a TSB grow, we are under serious
+ * memory pressure so don't try to grow any more.
+ */
+ if (mm->context.tsb_block[tsb_index].tsb != NULL)
+ mm->context.tsb_block[tsb_index].tsb_rss_limit = ~0UL;
+ return;
+ }
+
+ /* Mark all tags as invalid. */
+ tsb_init(new_tsb, new_size);
+
+ /* Ok, we are about to commit the changes. If we are
+ * growing an existing TSB the locking is very tricky,
+ * so WATCH OUT!
+ *
+ * We have to hold mm->context.lock while committing to the
+ * new TSB, this synchronizes us with processors in
+ * flush_tsb_user() and switch_mm() for this address space.
+ *
+ * But even with that lock held, processors run asynchronously
+ * accessing the old TSB via TLB miss handling. This is OK
+ * because those actions are just propagating state from the
+ * Linux page tables into the TSB, page table mappings are not
+ * being changed. If a real fault occurs, the processor will
+ * synchronize with us when it hits flush_tsb_user(), this is
+ * also true for the case where vmscan is modifying the page
+ * tables. The only thing we need to be careful with is to
+ * skip any locked TSB entries during copy_tsb().
+ *
+ * When we finish committing to the new TSB, we have to drop
+ * the lock and ask all other cpus running this address space
+ * to run tsb_context_switch() to see the new TSB table.
+ */
+ spin_lock_irqsave(&mm->context.lock, flags);
+
+ old_tsb = mm->context.tsb_block[tsb_index].tsb;
+ old_cache_index =
+ (mm->context.tsb_block[tsb_index].tsb_reg_val & 0x7UL);
+ old_size = (mm->context.tsb_block[tsb_index].tsb_nentries *
+ sizeof(struct tsb));
+
+
+ /* Handle multiple threads trying to grow the TSB at the same time.
+ * One will get in here first, and bump the size and the RSS limit.
+ * The others will get in here next and hit this check.
+ */
+ if (unlikely(old_tsb &&
+ (rss < mm->context.tsb_block[tsb_index].tsb_rss_limit))) {
+ spin_unlock_irqrestore(&mm->context.lock, flags);
+
+ kmem_cache_free(tsb_caches[new_cache_index], new_tsb);
+ return;
+ }
+
+ mm->context.tsb_block[tsb_index].tsb_rss_limit = new_rss_limit;
+
+ if (old_tsb) {
+ extern void copy_tsb(unsigned long old_tsb_base,
+ unsigned long old_tsb_size,
+ unsigned long new_tsb_base,
+ unsigned long new_tsb_size);
+ unsigned long old_tsb_base = (unsigned long) old_tsb;
+ unsigned long new_tsb_base = (unsigned long) new_tsb;
+
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+ old_tsb_base = __pa(old_tsb_base);
+ new_tsb_base = __pa(new_tsb_base);
+ }
+ copy_tsb(old_tsb_base, old_size, new_tsb_base, new_size);
+ }
+
+ mm->context.tsb_block[tsb_index].tsb = new_tsb;
+ setup_tsb_params(mm, tsb_index, new_size);
+
+ spin_unlock_irqrestore(&mm->context.lock, flags);
+
+ /* If old_tsb is NULL, we're being invoked for the first time
+ * from init_new_context().
+ */
+ if (old_tsb) {
+ /* Reload it on the local cpu. */
+ tsb_context_switch(mm);
+
+ /* Now force other processors to do the same. */
+ preempt_disable();
+ smp_tsb_sync(mm);
+ preempt_enable();
+
+ /* Now it is safe to free the old tsb. */
+ kmem_cache_free(tsb_caches[old_cache_index], old_tsb);
+ }
+}
+
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+#ifdef CONFIG_HUGETLB_PAGE
+ unsigned long huge_pte_count;
+#endif
+ unsigned int i;
+
+ spin_lock_init(&mm->context.lock);
+
+ mm->context.sparc64_ctx_val = 0UL;
+
+#ifdef CONFIG_HUGETLB_PAGE
+ /* We reset it to zero because the fork() page copying
+ * will re-increment the counters as the parent PTEs are
+ * copied into the child address space.
+ */
+ huge_pte_count = mm->context.huge_pte_count;
+ mm->context.huge_pte_count = 0;
+#endif
+
+ /* copy_mm() copies over the parent's mm_struct before calling
+ * us, so we need to zero out the TSB pointer or else tsb_grow()
+ * will be confused and think there is an older TSB to free up.
+ */
+ for (i = 0; i < MM_NUM_TSBS; i++)
+ mm->context.tsb_block[i].tsb = NULL;
+
+ /* If this is fork, inherit the parent's TSB size. We would
+ * grow it to that size on the first page fault anyways.
+ */
+ tsb_grow(mm, MM_TSB_BASE, get_mm_rss(mm));
+
+#ifdef CONFIG_HUGETLB_PAGE
+ if (unlikely(huge_pte_count))
+ tsb_grow(mm, MM_TSB_HUGE, huge_pte_count);
+#endif
+
+ if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void tsb_destroy_one(struct tsb_config *tp)
+{
+ unsigned long cache_index;
+
+ if (!tp->tsb)
+ return;
+ cache_index = tp->tsb_reg_val & 0x7UL;
+ kmem_cache_free(tsb_caches[cache_index], tp->tsb);
+ tp->tsb = NULL;
+ tp->tsb_reg_val = 0UL;
+}
+
+void destroy_context(struct mm_struct *mm)
+{
+ unsigned long flags, i;
+
+ for (i = 0; i < MM_NUM_TSBS; i++)
+ tsb_destroy_one(&mm->context.tsb_block[i]);
+
+ spin_lock_irqsave(&ctx_alloc_lock, flags);
+
+ if (CTX_VALID(mm->context)) {
+ unsigned long nr = CTX_NRBITS(mm->context);
+ mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63));
+ }
+
+ spin_unlock_irqrestore(&ctx_alloc_lock, flags);
+}
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
new file mode 100644
index 000000000000..80c788ec7c32
--- /dev/null
+++ b/arch/sparc/mm/ultra.S
@@ -0,0 +1,767 @@
+/*
+ * ultra.S: Don't expand these all over the place...
+ *
+ * Copyright (C) 1997, 2000, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <asm/asi.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/spitfire.h>
+#include <asm/mmu_context.h>
+#include <asm/mmu.h>
+#include <asm/pil.h>
+#include <asm/head.h>
+#include <asm/thread_info.h>
+#include <asm/cacheflush.h>
+#include <asm/hypervisor.h>
+#include <asm/cpudata.h>
+
+ /* Basically, most of the Spitfire vs. Cheetah madness
+ * has to do with the fact that Cheetah does not support
+ * IMMU flushes out of the secondary context. Someone needs
+ * to throw a south lake birthday party for the folks
+ * in Microelectronics who refused to fix this shit.
+ */
+
+ /* This file is meant to be read efficiently by the CPU, not humans.
+ * Staraj sie tego nikomu nie pierdolnac...
+ */
+ .text
+ .align 32
+ .globl __flush_tlb_mm
+__flush_tlb_mm: /* 18 insns */
+ /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
+ ldxa [%o1] ASI_DMMU, %g2
+ cmp %g2, %o0
+ bne,pn %icc, __spitfire_flush_tlb_mm_slow
+ mov 0x50, %g3
+ stxa %g0, [%g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g3] ASI_IMMU_DEMAP
+ sethi %hi(KERNBASE), %g3
+ flush %g3
+ retl
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ .align 32
+ .globl __flush_tlb_pending
+__flush_tlb_pending: /* 26 insns */
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ rdpr %pstate, %g7
+ sllx %o1, 3, %o1
+ andn %g7, PSTATE_IE, %g2
+ wrpr %g2, %pstate
+ mov SECONDARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ stxa %o0, [%o4] ASI_DMMU
+1: sub %o1, (1 << 3), %o1
+ ldx [%o2 + %o1], %o3
+ andcc %o3, 1, %g0
+ andn %o3, 1, %o3
+ be,pn %icc, 2f
+ or %o3, 0x10, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+2: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ membar #Sync
+ brnz,pt %o1, 1b
+ nop
+ stxa %g2, [%o4] ASI_DMMU
+ sethi %hi(KERNBASE), %o4
+ flush %o4
+ retl
+ wrpr %g7, 0x0, %pstate
+ nop
+ nop
+ nop
+ nop
+
+ .align 32
+ .globl __flush_tlb_kernel_range
+__flush_tlb_kernel_range: /* 16 insns */
+ /* %o0=start, %o1=end */
+ cmp %o0, %o1
+ be,pn %xcc, 2f
+ sethi %hi(PAGE_SIZE), %o4
+ sub %o1, %o0, %o3
+ sub %o3, %o4, %o3
+ or %o0, 0x20, %o0 ! Nucleus
+1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
+ stxa %g0, [%o0 + %o3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %o3, 1b
+ sub %o3, %o4, %o3
+2: sethi %hi(KERNBASE), %o3
+ flush %o3
+ retl
+ nop
+ nop
+
+__spitfire_flush_tlb_mm_slow:
+ rdpr %pstate, %g1
+ wrpr %g1, PSTATE_IE, %pstate
+ stxa %o0, [%o1] ASI_DMMU
+ stxa %g0, [%g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g3] ASI_IMMU_DEMAP
+ flush %g6
+ stxa %g2, [%o1] ASI_DMMU
+ sethi %hi(KERNBASE), %o1
+ flush %o1
+ retl
+ wrpr %g1, 0, %pstate
+
+/*
+ * The following code flushes one page_size worth.
+ */
+ .section .kprobes.text, "ax"
+ .align 32
+ .globl __flush_icache_page
+__flush_icache_page: /* %o0 = phys_page */
+ srlx %o0, PAGE_SHIFT, %o0
+ sethi %uhi(PAGE_OFFSET), %g1
+ sllx %o0, PAGE_SHIFT, %o0
+ sethi %hi(PAGE_SIZE), %g2
+ sllx %g1, 32, %g1
+ add %o0, %g1, %o0
+1: subcc %g2, 32, %g2
+ bne,pt %icc, 1b
+ flush %o0 + %g2
+ retl
+ nop
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+
+#if (PAGE_SHIFT != 13)
+#error only page shift of 13 is supported by dcache flush
+#endif
+
+#define DTAG_MASK 0x3
+
+ /* This routine is Spitfire specific so the hardcoded
+ * D-cache size and line-size are OK.
+ */
+ .align 64
+ .globl __flush_dcache_page
+__flush_dcache_page: /* %o0=kaddr, %o1=flush_icache */
+ sethi %uhi(PAGE_OFFSET), %g1
+ sllx %g1, 32, %g1
+ sub %o0, %g1, %o0 ! physical address
+ srlx %o0, 11, %o0 ! make D-cache TAG
+ sethi %hi(1 << 14), %o2 ! D-cache size
+ sub %o2, (1 << 5), %o2 ! D-cache line size
+1: ldxa [%o2] ASI_DCACHE_TAG, %o3 ! load D-cache TAG
+ andcc %o3, DTAG_MASK, %g0 ! Valid?
+ be,pn %xcc, 2f ! Nope, branch
+ andn %o3, DTAG_MASK, %o3 ! Clear valid bits
+ cmp %o3, %o0 ! TAG match?
+ bne,pt %xcc, 2f ! Nope, branch
+ nop
+ stxa %g0, [%o2] ASI_DCACHE_TAG ! Invalidate TAG
+ membar #Sync
+2: brnz,pt %o2, 1b
+ sub %o2, (1 << 5), %o2 ! D-cache line size
+
+ /* The I-cache does not snoop local stores so we
+ * better flush that too when necessary.
+ */
+ brnz,pt %o1, __flush_icache_page
+ sllx %o0, 11, %o0
+ retl
+ nop
+
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+ .previous
+
+ /* Cheetah specific versions, patched at boot time. */
+__cheetah_flush_tlb_mm: /* 19 insns */
+ rdpr %pstate, %g7
+ andn %g7, PSTATE_IE, %g2
+ wrpr %g2, 0x0, %pstate
+ wrpr %g0, 1, %tl
+ mov PRIMARY_CONTEXT, %o2
+ mov 0x40, %g3
+ ldxa [%o2] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o1
+ sllx %o1, CTX_PGSZ1_NUC_SHIFT, %o1
+ or %o0, %o1, %o0 /* Preserve nucleus page size fields */
+ stxa %o0, [%o2] ASI_DMMU
+ stxa %g0, [%g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g3] ASI_IMMU_DEMAP
+ stxa %g2, [%o2] ASI_DMMU
+ sethi %hi(KERNBASE), %o2
+ flush %o2
+ wrpr %g0, 0, %tl
+ retl
+ wrpr %g7, 0x0, %pstate
+
+__cheetah_flush_tlb_pending: /* 27 insns */
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ rdpr %pstate, %g7
+ sllx %o1, 3, %o1
+ andn %g7, PSTATE_IE, %g2
+ wrpr %g2, 0x0, %pstate
+ wrpr %g0, 1, %tl
+ mov PRIMARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o3
+ sllx %o3, CTX_PGSZ1_NUC_SHIFT, %o3
+ or %o0, %o3, %o0 /* Preserve nucleus page size fields */
+ stxa %o0, [%o4] ASI_DMMU
+1: sub %o1, (1 << 3), %o1
+ ldx [%o2 + %o1], %o3
+ andcc %o3, 1, %g0
+ be,pn %icc, 2f
+ andn %o3, 1, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+2: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ membar #Sync
+ brnz,pt %o1, 1b
+ nop
+ stxa %g2, [%o4] ASI_DMMU
+ sethi %hi(KERNBASE), %o4
+ flush %o4
+ wrpr %g0, 0, %tl
+ retl
+ wrpr %g7, 0x0, %pstate
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+__cheetah_flush_dcache_page: /* 11 insns */
+ sethi %uhi(PAGE_OFFSET), %g1
+ sllx %g1, 32, %g1
+ sub %o0, %g1, %o0
+ sethi %hi(PAGE_SIZE), %o4
+1: subcc %o4, (1 << 5), %o4
+ stxa %g0, [%o0 + %o4] ASI_DCACHE_INVALIDATE
+ membar #Sync
+ bne,pt %icc, 1b
+ nop
+ retl /* I-cache flush never needed on Cheetah, see callers. */
+ nop
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+ /* Hypervisor specific versions, patched at boot time. */
+__hypervisor_tlb_tl0_error:
+ save %sp, -192, %sp
+ mov %i0, %o0
+ call hypervisor_tlbop_error
+ mov %i1, %o1
+ ret
+ restore
+
+__hypervisor_flush_tlb_mm: /* 10 insns */
+ mov %o0, %o2 /* ARG2: mmu context */
+ mov 0, %o0 /* ARG0: CPU lists unimplemented */
+ mov 0, %o1 /* ARG1: CPU lists unimplemented */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ brnz,pn %o0, __hypervisor_tlb_tl0_error
+ mov HV_FAST_MMU_DEMAP_CTX, %o1
+ retl
+ nop
+
+__hypervisor_flush_tlb_pending: /* 16 insns */
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ sllx %o1, 3, %g1
+ mov %o2, %g2
+ mov %o0, %g3
+1: sub %g1, (1 << 3), %g1
+ ldx [%g2 + %g1], %o0 /* ARG0: vaddr + IMMU-bit */
+ mov %g3, %o1 /* ARG1: mmu context */
+ mov HV_MMU_ALL, %o2 /* ARG2: flags */
+ srlx %o0, PAGE_SHIFT, %o0
+ sllx %o0, PAGE_SHIFT, %o0
+ ta HV_MMU_UNMAP_ADDR_TRAP
+ brnz,pn %o0, __hypervisor_tlb_tl0_error
+ mov HV_MMU_UNMAP_ADDR_TRAP, %o1
+ brnz,pt %g1, 1b
+ nop
+ retl
+ nop
+
+__hypervisor_flush_tlb_kernel_range: /* 16 insns */
+ /* %o0=start, %o1=end */
+ cmp %o0, %o1
+ be,pn %xcc, 2f
+ sethi %hi(PAGE_SIZE), %g3
+ mov %o0, %g1
+ sub %o1, %g1, %g2
+ sub %g2, %g3, %g2
+1: add %g1, %g2, %o0 /* ARG0: virtual address */
+ mov 0, %o1 /* ARG1: mmu context */
+ mov HV_MMU_ALL, %o2 /* ARG2: flags */
+ ta HV_MMU_UNMAP_ADDR_TRAP
+ brnz,pn %o0, __hypervisor_tlb_tl0_error
+ mov HV_MMU_UNMAP_ADDR_TRAP, %o1
+ brnz,pt %g2, 1b
+ sub %g2, %g3, %g2
+2: retl
+ nop
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ /* XXX Niagara and friends have an 8K cache, so no aliasing is
+ * XXX possible, but nothing explicit in the Hypervisor API
+ * XXX guarantees this.
+ */
+__hypervisor_flush_dcache_page: /* 2 insns */
+ retl
+ nop
+#endif
+
+tlb_patch_one:
+1: lduw [%o1], %g1
+ stw %g1, [%o0]
+ flush %o0
+ subcc %o2, 1, %o2
+ add %o1, 4, %o1
+ bne,pt %icc, 1b
+ add %o0, 4, %o0
+ retl
+ nop
+
+ .globl cheetah_patch_cachetlbops
+cheetah_patch_cachetlbops:
+ save %sp, -128, %sp
+
+ sethi %hi(__flush_tlb_mm), %o0
+ or %o0, %lo(__flush_tlb_mm), %o0
+ sethi %hi(__cheetah_flush_tlb_mm), %o1
+ or %o1, %lo(__cheetah_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 19, %o2
+
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__cheetah_flush_tlb_pending), %o1
+ or %o1, %lo(__cheetah_flush_tlb_pending), %o1
+ call tlb_patch_one
+ mov 27, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ sethi %hi(__flush_dcache_page), %o0
+ or %o0, %lo(__flush_dcache_page), %o0
+ sethi %hi(__cheetah_flush_dcache_page), %o1
+ or %o1, %lo(__cheetah_flush_dcache_page), %o1
+ call tlb_patch_one
+ mov 11, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+ ret
+ restore
+
+#ifdef CONFIG_SMP
+ /* These are all called by the slaves of a cross call, at
+ * trap level 1, with interrupts fully disabled.
+ *
+ * Register usage:
+ * %g5 mm->context (all tlb flushes)
+ * %g1 address arg 1 (tlb page and range flushes)
+ * %g7 address arg 2 (tlb range flush only)
+ *
+ * %g6 scratch 1
+ * %g2 scratch 2
+ * %g3 scratch 3
+ * %g4 scratch 4
+ */
+ .align 32
+ .globl xcall_flush_tlb_mm
+xcall_flush_tlb_mm: /* 21 insns */
+ mov PRIMARY_CONTEXT, %g2
+ ldxa [%g2] ASI_DMMU, %g3
+ srlx %g3, CTX_PGSZ1_NUC_SHIFT, %g4
+ sllx %g4, CTX_PGSZ1_NUC_SHIFT, %g4
+ or %g5, %g4, %g5 /* Preserve nucleus page size fields */
+ stxa %g5, [%g2] ASI_DMMU
+ mov 0x40, %g4
+ stxa %g0, [%g4] ASI_DMMU_DEMAP
+ stxa %g0, [%g4] ASI_IMMU_DEMAP
+ stxa %g3, [%g2] ASI_DMMU
+ retry
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ .globl xcall_flush_tlb_pending
+xcall_flush_tlb_pending: /* 21 insns */
+ /* %g5=context, %g1=nr, %g7=vaddrs[] */
+ sllx %g1, 3, %g1
+ mov PRIMARY_CONTEXT, %g4
+ ldxa [%g4] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %g4
+ sllx %g4, CTX_PGSZ1_NUC_SHIFT, %g4
+ or %g5, %g4, %g5
+ mov PRIMARY_CONTEXT, %g4
+ stxa %g5, [%g4] ASI_DMMU
+1: sub %g1, (1 << 3), %g1
+ ldx [%g7 + %g1], %g5
+ andcc %g5, 0x1, %g0
+ be,pn %icc, 2f
+
+ andn %g5, 0x1, %g5
+ stxa %g0, [%g5] ASI_IMMU_DEMAP
+2: stxa %g0, [%g5] ASI_DMMU_DEMAP
+ membar #Sync
+ brnz,pt %g1, 1b
+ nop
+ stxa %g2, [%g4] ASI_DMMU
+ retry
+ nop
+
+ .globl xcall_flush_tlb_kernel_range
+xcall_flush_tlb_kernel_range: /* 25 insns */
+ sethi %hi(PAGE_SIZE - 1), %g2
+ or %g2, %lo(PAGE_SIZE - 1), %g2
+ andn %g1, %g2, %g1
+ andn %g7, %g2, %g7
+ sub %g7, %g1, %g3
+ add %g2, 1, %g2
+ sub %g3, %g2, %g3
+ or %g1, 0x20, %g1 ! Nucleus
+1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %g3, 1b
+ sub %g3, %g2, %g3
+ retry
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ /* This runs in a very controlled environment, so we do
+ * not need to worry about BH races etc.
+ */
+ .globl xcall_sync_tick
+xcall_sync_tick:
+
+661: rdpr %pstate, %g2
+ wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ nop
+ nop
+ .previous
+
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ sethi %hi(109f), %g7
+ b,pt %xcc, etrap_irq
+109: or %g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ call smp_synchronize_tick_client
+ nop
+ b rtrap_xcall
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+
+ .globl xcall_fetch_glob_regs
+xcall_fetch_glob_regs:
+ sethi %hi(global_reg_snapshot), %g1
+ or %g1, %lo(global_reg_snapshot), %g1
+ __GET_CPUID(%g2)
+ sllx %g2, 6, %g3
+ add %g1, %g3, %g1
+ rdpr %tstate, %g7
+ stx %g7, [%g1 + GR_SNAP_TSTATE]
+ rdpr %tpc, %g7
+ stx %g7, [%g1 + GR_SNAP_TPC]
+ rdpr %tnpc, %g7
+ stx %g7, [%g1 + GR_SNAP_TNPC]
+ stx %o7, [%g1 + GR_SNAP_O7]
+ stx %i7, [%g1 + GR_SNAP_I7]
+ /* Don't try this at home kids... */
+ rdpr %cwp, %g2
+ sub %g2, 1, %g7
+ wrpr %g7, %cwp
+ mov %i7, %g7
+ wrpr %g2, %cwp
+ stx %g7, [%g1 + GR_SNAP_RPC]
+ sethi %hi(trap_block), %g7
+ or %g7, %lo(trap_block), %g7
+ sllx %g2, TRAP_BLOCK_SZ_SHIFT, %g2
+ add %g7, %g2, %g7
+ ldx [%g7 + TRAP_PER_CPU_THREAD], %g3
+ stx %g3, [%g1 + GR_SNAP_THREAD]
+ retry
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ .align 32
+ .globl xcall_flush_dcache_page_cheetah
+xcall_flush_dcache_page_cheetah: /* %g1 == physical page address */
+ sethi %hi(PAGE_SIZE), %g3
+1: subcc %g3, (1 << 5), %g3
+ stxa %g0, [%g1 + %g3] ASI_DCACHE_INVALIDATE
+ membar #Sync
+ bne,pt %icc, 1b
+ nop
+ retry
+ nop
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+ .globl xcall_flush_dcache_page_spitfire
+xcall_flush_dcache_page_spitfire: /* %g1 == physical page address
+ %g7 == kernel page virtual address
+ %g5 == (page->mapping != NULL) */
+#ifdef DCACHE_ALIASING_POSSIBLE
+ srlx %g1, (13 - 2), %g1 ! Form tag comparitor
+ sethi %hi(L1DCACHE_SIZE), %g3 ! D$ size == 16K
+ sub %g3, (1 << 5), %g3 ! D$ linesize == 32
+1: ldxa [%g3] ASI_DCACHE_TAG, %g2
+ andcc %g2, 0x3, %g0
+ be,pn %xcc, 2f
+ andn %g2, 0x3, %g2
+ cmp %g2, %g1
+
+ bne,pt %xcc, 2f
+ nop
+ stxa %g0, [%g3] ASI_DCACHE_TAG
+ membar #Sync
+2: cmp %g3, 0
+ bne,pt %xcc, 1b
+ sub %g3, (1 << 5), %g3
+
+ brz,pn %g5, 2f
+#endif /* DCACHE_ALIASING_POSSIBLE */
+ sethi %hi(PAGE_SIZE), %g3
+
+1: flush %g7
+ subcc %g3, (1 << 5), %g3
+ bne,pt %icc, 1b
+ add %g7, (1 << 5), %g7
+
+2: retry
+ nop
+ nop
+
+ /* %g5: error
+ * %g6: tlb op
+ */
+__hypervisor_tlb_xcall_error:
+ mov %g5, %g4
+ mov %g6, %g5
+ ba,pt %xcc, etrap
+ rd %pc, %g7
+ mov %l4, %o0
+ call hypervisor_tlbop_error_xcall
+ mov %l5, %o1
+ ba,a,pt %xcc, rtrap
+
+ .globl __hypervisor_xcall_flush_tlb_mm
+__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
+ /* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
+ mov %o0, %g2
+ mov %o1, %g3
+ mov %o2, %g4
+ mov %o3, %g1
+ mov %o5, %g7
+ clr %o0 /* ARG0: CPU lists unimplemented */
+ clr %o1 /* ARG1: CPU lists unimplemented */
+ mov %g5, %o2 /* ARG2: mmu context */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ mov HV_FAST_MMU_DEMAP_CTX, %g6
+ brnz,pn %o0, __hypervisor_tlb_xcall_error
+ mov %o0, %g5
+ mov %g2, %o0
+ mov %g3, %o1
+ mov %g4, %o2
+ mov %g1, %o3
+ mov %g7, %o5
+ membar #Sync
+ retry
+
+ .globl __hypervisor_xcall_flush_tlb_pending
+__hypervisor_xcall_flush_tlb_pending: /* 21 insns */
+ /* %g5=ctx, %g1=nr, %g7=vaddrs[], %g2,%g3,%g4,g6=scratch */
+ sllx %g1, 3, %g1
+ mov %o0, %g2
+ mov %o1, %g3
+ mov %o2, %g4
+1: sub %g1, (1 << 3), %g1
+ ldx [%g7 + %g1], %o0 /* ARG0: virtual address */
+ mov %g5, %o1 /* ARG1: mmu context */
+ mov HV_MMU_ALL, %o2 /* ARG2: flags */
+ srlx %o0, PAGE_SHIFT, %o0
+ sllx %o0, PAGE_SHIFT, %o0
+ ta HV_MMU_UNMAP_ADDR_TRAP
+ mov HV_MMU_UNMAP_ADDR_TRAP, %g6
+ brnz,a,pn %o0, __hypervisor_tlb_xcall_error
+ mov %o0, %g5
+ brnz,pt %g1, 1b
+ nop
+ mov %g2, %o0
+ mov %g3, %o1
+ mov %g4, %o2
+ membar #Sync
+ retry
+
+ .globl __hypervisor_xcall_flush_tlb_kernel_range
+__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
+ /* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
+ sethi %hi(PAGE_SIZE - 1), %g2
+ or %g2, %lo(PAGE_SIZE - 1), %g2
+ andn %g1, %g2, %g1
+ andn %g7, %g2, %g7
+ sub %g7, %g1, %g3
+ add %g2, 1, %g2
+ sub %g3, %g2, %g3
+ mov %o0, %g2
+ mov %o1, %g4
+ mov %o2, %g7
+1: add %g1, %g3, %o0 /* ARG0: virtual address */
+ mov 0, %o1 /* ARG1: mmu context */
+ mov HV_MMU_ALL, %o2 /* ARG2: flags */
+ ta HV_MMU_UNMAP_ADDR_TRAP
+ mov HV_MMU_UNMAP_ADDR_TRAP, %g6
+ brnz,pn %o0, __hypervisor_tlb_xcall_error
+ mov %o0, %g5
+ sethi %hi(PAGE_SIZE), %o2
+ brnz,pt %g3, 1b
+ sub %g3, %o2, %g3
+ mov %g2, %o0
+ mov %g4, %o1
+ mov %g7, %o2
+ membar #Sync
+ retry
+
+ /* These just get rescheduled to PIL vectors. */
+ .globl xcall_call_function
+xcall_call_function:
+ wr %g0, (1 << PIL_SMP_CALL_FUNC), %set_softint
+ retry
+
+ .globl xcall_call_function_single
+xcall_call_function_single:
+ wr %g0, (1 << PIL_SMP_CALL_FUNC_SNGL), %set_softint
+ retry
+
+ .globl xcall_receive_signal
+xcall_receive_signal:
+ wr %g0, (1 << PIL_SMP_RECEIVE_SIGNAL), %set_softint
+ retry
+
+ .globl xcall_capture
+xcall_capture:
+ wr %g0, (1 << PIL_SMP_CAPTURE), %set_softint
+ retry
+
+ .globl xcall_new_mmu_context_version
+xcall_new_mmu_context_version:
+ wr %g0, (1 << PIL_SMP_CTX_NEW_VERSION), %set_softint
+ retry
+
+#ifdef CONFIG_KGDB
+ .globl xcall_kgdb_capture
+xcall_kgdb_capture:
+661: rdpr %pstate, %g2
+ wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate
+ .section .sun4v_2insn_patch, "ax"
+ .word 661b
+ nop
+ nop
+ .previous
+
+ rdpr %pil, %g2
+ wrpr %g0, PIL_NORMAL_MAX, %pil
+ sethi %hi(109f), %g7
+ ba,pt %xcc, etrap_irq
+109: or %g7, %lo(109b), %g7
+#ifdef CONFIG_TRACE_IRQFLAGS
+ call trace_hardirqs_off
+ nop
+#endif
+ call smp_kgdb_capture_client
+ add %sp, PTREGS_OFF, %o0
+ /* Has to be a non-v9 branch due to the large distance. */
+ ba rtrap_xcall
+ ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+#endif
+
+#endif /* CONFIG_SMP */
+
+
+ .globl hypervisor_patch_cachetlbops
+hypervisor_patch_cachetlbops:
+ save %sp, -128, %sp
+
+ sethi %hi(__flush_tlb_mm), %o0
+ or %o0, %lo(__flush_tlb_mm), %o0
+ sethi %hi(__hypervisor_flush_tlb_mm), %o1
+ or %o1, %lo(__hypervisor_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 10, %o2
+
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__hypervisor_flush_tlb_pending), %o1
+ or %o1, %lo(__hypervisor_flush_tlb_pending), %o1
+ call tlb_patch_one
+ mov 16, %o2
+
+ sethi %hi(__flush_tlb_kernel_range), %o0
+ or %o0, %lo(__flush_tlb_kernel_range), %o0
+ sethi %hi(__hypervisor_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 16, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ sethi %hi(__flush_dcache_page), %o0
+ or %o0, %lo(__flush_dcache_page), %o0
+ sethi %hi(__hypervisor_flush_dcache_page), %o1
+ or %o1, %lo(__hypervisor_flush_dcache_page), %o1
+ call tlb_patch_one
+ mov 2, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+#ifdef CONFIG_SMP
+ sethi %hi(xcall_flush_tlb_mm), %o0
+ or %o0, %lo(xcall_flush_tlb_mm), %o0
+ sethi %hi(__hypervisor_xcall_flush_tlb_mm), %o1
+ or %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 21, %o2
+
+ sethi %hi(xcall_flush_tlb_pending), %o0
+ or %o0, %lo(xcall_flush_tlb_pending), %o0
+ sethi %hi(__hypervisor_xcall_flush_tlb_pending), %o1
+ or %o1, %lo(__hypervisor_xcall_flush_tlb_pending), %o1
+ call tlb_patch_one
+ mov 21, %o2
+
+ sethi %hi(xcall_flush_tlb_kernel_range), %o0
+ or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
+ sethi %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 25, %o2
+#endif /* CONFIG_SMP */
+
+ ret
+ restore
diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c
index 17bb6035069b..d6e170c074fc 100644
--- a/arch/sparc/oprofile/init.c
+++ b/arch/sparc/oprofile/init.c
@@ -12,12 +12,239 @@
#include <linux/errno.h>
#include <linux/init.h>
+#ifdef CONFIG_SPARC64
+#include <asm/hypervisor.h>
+#include <asm/spitfire.h>
+#include <asm/cpudata.h>
+#include <asm/irq.h>
+
+static int nmi_enabled;
+
+struct pcr_ops {
+ u64 (*read)(void);
+ void (*write)(u64);
+};
+static const struct pcr_ops *pcr_ops;
+
+static u64 direct_pcr_read(void)
+{
+ u64 val;
+
+ read_pcr(val);
+ return val;
+}
+
+static void direct_pcr_write(u64 val)
+{
+ write_pcr(val);
+}
+
+static const struct pcr_ops direct_pcr_ops = {
+ .read = direct_pcr_read,
+ .write = direct_pcr_write,
+};
+
+static void n2_pcr_write(u64 val)
+{
+ unsigned long ret;
+
+ ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val);
+ if (val != HV_EOK)
+ write_pcr(val);
+}
+
+static const struct pcr_ops n2_pcr_ops = {
+ .read = direct_pcr_read,
+ .write = n2_pcr_write,
+};
+
+/* In order to commonize as much of the implementation as
+ * possible, we use PICH as our counter. Mostly this is
+ * to accomodate Niagara-1 which can only count insn cycles
+ * in PICH.
+ */
+static u64 picl_value(void)
+{
+ u32 delta = local_cpu_data().clock_tick / HZ;
+
+ return ((u64)((0 - delta) & 0xffffffff)) << 32;
+}
+
+#define PCR_PIC_PRIV 0x00000001 /* PIC access is privileged */
+#define PCR_STRACE 0x00000002 /* Trace supervisor events */
+#define PCR_UTRACE 0x00000004 /* Trace user events */
+#define PCR_N2_HTRACE 0x00000008 /* Trace hypervisor events */
+#define PCR_N2_TOE_OV0 0x00000010 /* Trap if PIC 0 overflows */
+#define PCR_N2_TOE_OV1 0x00000020 /* Trap if PIC 1 overflows */
+#define PCR_N2_MASK0 0x00003fc0
+#define PCR_N2_MASK0_SHIFT 6
+#define PCR_N2_SL0 0x0003c000
+#define PCR_N2_SL0_SHIFT 14
+#define PCR_N2_OV0 0x00040000
+#define PCR_N2_MASK1 0x07f80000
+#define PCR_N2_MASK1_SHIFT 19
+#define PCR_N2_SL1 0x78000000
+#define PCR_N2_SL1_SHIFT 27
+#define PCR_N2_OV1 0x80000000
+
+#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
+#define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
+ PCR_N2_TOE_OV1 | \
+ (2 << PCR_N2_SL1_SHIFT) | \
+ (0xff << PCR_N2_MASK1_SHIFT))
+
+static u64 pcr_enable = PCR_SUN4U_ENABLE;
+
+static void nmi_handler(struct pt_regs *regs)
+{
+ pcr_ops->write(PCR_PIC_PRIV);
+
+ if (nmi_enabled) {
+ oprofile_add_sample(regs, 0);
+
+ write_pic(picl_value());
+ pcr_ops->write(pcr_enable);
+ }
+}
+
+/* We count "clock cycle" events in the lower 32-bit PIC.
+ * Then configure it such that it overflows every HZ, and thus
+ * generates a level 15 interrupt at that frequency.
+ */
+static void cpu_nmi_start(void *_unused)
+{
+ pcr_ops->write(PCR_PIC_PRIV);
+ write_pic(picl_value());
+
+ pcr_ops->write(pcr_enable);
+}
+
+static void cpu_nmi_stop(void *_unused)
+{
+ pcr_ops->write(PCR_PIC_PRIV);
+}
+
+static int nmi_start(void)
+{
+ int err = register_perfctr_intr(nmi_handler);
+
+ if (!err) {
+ nmi_enabled = 1;
+ wmb();
+ err = on_each_cpu(cpu_nmi_start, NULL, 1);
+ if (err) {
+ nmi_enabled = 0;
+ wmb();
+ on_each_cpu(cpu_nmi_stop, NULL, 1);
+ release_perfctr_intr(nmi_handler);
+ }
+ }
+
+ return err;
+}
+
+static void nmi_stop(void)
+{
+ nmi_enabled = 0;
+ wmb();
+
+ on_each_cpu(cpu_nmi_stop, NULL, 1);
+ release_perfctr_intr(nmi_handler);
+ synchronize_sched();
+}
+
+static unsigned long perf_hsvc_group;
+static unsigned long perf_hsvc_major;
+static unsigned long perf_hsvc_minor;
+
+static int __init register_perf_hsvc(void)
+{
+ if (tlb_type == hypervisor) {
+ switch (sun4v_chip_type) {
+ case SUN4V_CHIP_NIAGARA1:
+ perf_hsvc_group = HV_GRP_NIAG_PERF;
+ break;
+
+ case SUN4V_CHIP_NIAGARA2:
+ perf_hsvc_group = HV_GRP_N2_CPU;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+
+ perf_hsvc_major = 1;
+ perf_hsvc_minor = 0;
+ if (sun4v_hvapi_register(perf_hsvc_group,
+ perf_hsvc_major,
+ &perf_hsvc_minor)) {
+ printk("perfmon: Could not register N2 hvapi.\n");
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+static void unregister_perf_hsvc(void)
+{
+ if (tlb_type != hypervisor)
+ return;
+ sun4v_hvapi_unregister(perf_hsvc_group);
+}
+
+static int oprofile_nmi_init(struct oprofile_operations *ops)
+{
+ int err = register_perf_hsvc();
+
+ if (err)
+ return err;
+
+ switch (tlb_type) {
+ case hypervisor:
+ pcr_ops = &n2_pcr_ops;
+ pcr_enable = PCR_N2_ENABLE;
+ break;
+
+ case cheetah:
+ case cheetah_plus:
+ pcr_ops = &direct_pcr_ops;
+ break;
+
+ default:
+ return -ENODEV;
+ }
+
+ ops->create_files = NULL;
+ ops->setup = NULL;
+ ops->shutdown = NULL;
+ ops->start = nmi_start;
+ ops->stop = nmi_stop;
+ ops->cpu_type = "timer";
+
+ printk(KERN_INFO "oprofile: Using perfctr based NMI timer interrupt.\n");
+
+ return 0;
+}
+#endif
+
int __init oprofile_arch_init(struct oprofile_operations *ops)
{
- return -ENODEV;
+ int ret = -ENODEV;
+
+#ifdef CONFIG_SPARC64
+ ret = oprofile_nmi_init(ops);
+ if (!ret)
+ return ret;
+#endif
+
+ return ret;
}
void oprofile_arch_exit(void)
{
+#ifdef CONFIG_SPARC64
+ unregister_perf_hsvc();
+#endif
}
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
index 8f7e18546c97..1b8c073adb44 100644
--- a/arch/sparc/prom/Makefile
+++ b/arch/sparc/prom/Makefile
@@ -1,6 +1,21 @@
# Makefile for the Sun Boot PROM interface library under
# Linux.
#
+asflags := -ansi
+ccflags := -Werror
-lib-y := bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
- palloc.o ranges.o segment.o console.o printf.o tree.o
+lib-y := bootstr_$(BITS).o
+lib-$(CONFIG_SPARC32) += devmap.o
+lib-y += devops_$(BITS).o
+lib-y += init_$(BITS).o
+lib-$(CONFIG_SPARC32) += memory.o
+lib-y += misc_$(BITS).o
+lib-$(CONFIG_SPARC32) += mp.o
+lib-$(CONFIG_SPARC32) += palloc.o
+lib-$(CONFIG_SPARC32) += ranges.o
+lib-$(CONFIG_SPARC32) += segment.o
+lib-y += console_$(BITS).o
+lib-y += printf.o
+lib-y += tree_$(BITS).o
+lib-$(CONFIG_SPARC64) += p1275.o
+lib-$(CONFIG_SPARC64) += cif.o
diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr_32.c
index 916831da7e67..916831da7e67 100644
--- a/arch/sparc/prom/bootstr.c
+++ b/arch/sparc/prom/bootstr_32.c
diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc/prom/bootstr_64.c
index ab9ccc63b388..ab9ccc63b388 100644
--- a/arch/sparc64/prom/bootstr.c
+++ b/arch/sparc/prom/bootstr_64.c
diff --git a/arch/sparc64/prom/cif.S b/arch/sparc/prom/cif.S
index 5f27ad779c0c..5f27ad779c0c 100644
--- a/arch/sparc64/prom/cif.S
+++ b/arch/sparc/prom/cif.S
diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console_32.c
index b3075d73fc19..b3075d73fc19 100644
--- a/arch/sparc/prom/console.c
+++ b/arch/sparc/prom/console_32.c
diff --git a/arch/sparc64/prom/console.c b/arch/sparc/prom/console_64.c
index e1c3fc87484d..e1c3fc87484d 100644
--- a/arch/sparc64/prom/console.c
+++ b/arch/sparc/prom/console_64.c
diff --git a/arch/sparc/prom/devops.c b/arch/sparc/prom/devops_32.c
index 9f1a95c91ad1..9f1a95c91ad1 100644
--- a/arch/sparc/prom/devops.c
+++ b/arch/sparc/prom/devops_32.c
diff --git a/arch/sparc64/prom/devops.c b/arch/sparc/prom/devops_64.c
index 9dbd803e46e1..9dbd803e46e1 100644
--- a/arch/sparc64/prom/devops.c
+++ b/arch/sparc/prom/devops_64.c
diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init_32.c
index 873217c6d823..873217c6d823 100644
--- a/arch/sparc/prom/init.c
+++ b/arch/sparc/prom/init_32.c
diff --git a/arch/sparc64/prom/init.c b/arch/sparc/prom/init_64.c
index 7b00f89490a4..7b00f89490a4 100644
--- a/arch/sparc64/prom/init.c
+++ b/arch/sparc/prom/init_64.c
diff --git a/arch/sparc/prom/misc.c b/arch/sparc/prom/misc.c
deleted file mode 100644
index 49b5057b9601..000000000000
--- a/arch/sparc/prom/misc.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * misc.c: Miscellaneous prom functions that don't belong
- * anywhere else.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/auxio.h>
-#include <asm/system.h>
-
-extern void restore_current(void);
-
-DEFINE_SPINLOCK(prom_lock);
-
-/* Reset and reboot the machine with the command 'bcommand'. */
-void
-prom_reboot(char *bcommand)
-{
- unsigned long flags;
- spin_lock_irqsave(&prom_lock, flags);
- (*(romvec->pv_reboot))(bcommand);
- /* Never get here. */
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-}
-
-/* Forth evaluate the expression contained in 'fstring'. */
-void
-prom_feval(char *fstring)
-{
- unsigned long flags;
- if(!fstring || fstring[0] == 0)
- return;
- spin_lock_irqsave(&prom_lock, flags);
- if(prom_vers == PROM_V0)
- (*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
- else
- (*(romvec->pv_fortheval.v2_eval))(fstring);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-}
-
-/* Drop into the prom, with the chance to continue with the 'go'
- * prom command.
- */
-void
-prom_cmdline(void)
-{
- extern void install_obp_ticker(void);
- extern void install_linux_ticker(void);
- unsigned long flags;
-
- spin_lock_irqsave(&prom_lock, flags);
- install_obp_ticker();
- (*(romvec->pv_abort))();
- restore_current();
- install_linux_ticker();
- spin_unlock_irqrestore(&prom_lock, flags);
-#ifdef CONFIG_SUN_AUXIO
- set_auxio(AUXIO_LED, 0);
-#endif
-}
-
-/* Drop into the prom, but completely terminate the program.
- * No chance of continuing.
- */
-void
-prom_halt(void)
-{
- unsigned long flags;
-again:
- spin_lock_irqsave(&prom_lock, flags);
- (*(romvec->pv_halt))();
- /* Never get here. */
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
- goto again; /* PROM is out to get me -DaveM */
-}
-
-typedef void (*sfunc_t)(void);
-
-/* Set prom sync handler to call function 'funcp'. */
-void
-prom_setsync(sfunc_t funcp)
-{
- if(!funcp) return;
- *romvec->pv_synchook = funcp;
-}
-
-/* Get the idprom and stuff it into buffer 'idbuf'. Returns the
- * format type. 'num_bytes' is the number of bytes that your idbuf
- * has space for. Returns 0xff on error.
- */
-unsigned char
-prom_get_idprom(char *idbuf, int num_bytes)
-{
- int len;
-
- len = prom_getproplen(prom_root_node, "idprom");
- if((len>num_bytes) || (len==-1)) return 0xff;
- if(!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
- return idbuf[0];
-
- return 0xff;
-}
-
-/* Get the major prom version number. */
-int
-prom_version(void)
-{
- return romvec->pv_romvers;
-}
-
-/* Get the prom plugin-revision. */
-int
-prom_getrev(void)
-{
- return prom_rev;
-}
-
-/* Get the prom firmware print revision. */
-int
-prom_getprev(void)
-{
- return prom_prev;
-}
diff --git a/arch/sparc/prom/misc_32.c b/arch/sparc/prom/misc_32.c
new file mode 100644
index 000000000000..cf6c3f6d36c3
--- /dev/null
+++ b/arch/sparc/prom/misc_32.c
@@ -0,0 +1,129 @@
+/*
+ * misc.c: Miscellaneous prom functions that don't belong
+ * anywhere else.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/auxio.h>
+#include <asm/system.h>
+
+extern void restore_current(void);
+
+DEFINE_SPINLOCK(prom_lock);
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void
+prom_reboot(char *bcommand)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&prom_lock, flags);
+ (*(romvec->pv_reboot))(bcommand);
+ /* Never get here. */
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+}
+
+/* Forth evaluate the expression contained in 'fstring'. */
+void
+prom_feval(char *fstring)
+{
+ unsigned long flags;
+ if(!fstring || fstring[0] == 0)
+ return;
+ spin_lock_irqsave(&prom_lock, flags);
+ if(prom_vers == PROM_V0)
+ (*(romvec->pv_fortheval.v0_eval))(strlen(fstring), fstring);
+ else
+ (*(romvec->pv_fortheval.v2_eval))(fstring);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+}
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+void
+prom_cmdline(void)
+{
+ extern void install_obp_ticker(void);
+ extern void install_linux_ticker(void);
+ unsigned long flags;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ install_obp_ticker();
+ (*(romvec->pv_abort))();
+ restore_current();
+ install_linux_ticker();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ set_auxio(AUXIO_LED, 0);
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void
+prom_halt(void)
+{
+ unsigned long flags;
+again:
+ spin_lock_irqsave(&prom_lock, flags);
+ (*(romvec->pv_halt))();
+ /* Never get here. */
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ goto again; /* PROM is out to get me -DaveM */
+}
+
+typedef void (*sfunc_t)(void);
+
+/* Set prom sync handler to call function 'funcp'. */
+void
+prom_setsync(sfunc_t funcp)
+{
+ if(!funcp) return;
+ *romvec->pv_synchook = funcp;
+}
+
+/* Get the idprom and stuff it into buffer 'idbuf'. Returns the
+ * format type. 'num_bytes' is the number of bytes that your idbuf
+ * has space for. Returns 0xff on error.
+ */
+unsigned char
+prom_get_idprom(char *idbuf, int num_bytes)
+{
+ int len;
+
+ len = prom_getproplen(prom_root_node, "idprom");
+ if((len>num_bytes) || (len==-1)) return 0xff;
+ if(!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
+ return idbuf[0];
+
+ return 0xff;
+}
+
+/* Get the major prom version number. */
+int
+prom_version(void)
+{
+ return romvec->pv_romvers;
+}
+
+/* Get the prom plugin-revision. */
+int
+prom_getrev(void)
+{
+ return prom_rev;
+}
+
+/* Get the prom firmware print revision. */
+int
+prom_getprev(void)
+{
+ return prom_prev;
+}
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc/prom/misc_64.c
index 9b0c0760901e..9b0c0760901e 100644
--- a/arch/sparc64/prom/misc.c
+++ b/arch/sparc/prom/misc_64.c
diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc/prom/p1275.c
index 4b7c937bba61..4b7c937bba61 100644
--- a/arch/sparc64/prom/p1275.c
+++ b/arch/sparc/prom/p1275.c
diff --git a/arch/sparc/prom/printf.c b/arch/sparc/prom/printf.c
index a36ab9c5ee08..660943ee4c2a 100644
--- a/arch/sparc/prom/printf.c
+++ b/arch/sparc/prom/printf.c
@@ -2,6 +2,7 @@
* printf.c: Internal prom library printf facility.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com)
*
* We used to warn all over the code: DO NOT USE prom_printf(),
@@ -13,7 +14,6 @@
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
@@ -34,7 +34,7 @@ prom_write(const char *buf, unsigned int n)
}
void
-prom_printf(char *fmt, ...)
+prom_printf(const char *fmt, ...)
{
va_list args;
int i;
@@ -45,4 +45,3 @@ prom_printf(char *fmt, ...)
prom_write(ppbuf, i);
}
-EXPORT_SYMBOL(prom_printf);
diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c
deleted file mode 100644
index f228fe057b24..000000000000
--- a/arch/sparc/prom/tree.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * tree.c: Basic device tree traversal/scanning for the Linux
- * prom library.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#define PROMLIB_INTERNAL
-
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/ctype.h>
-
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-
-extern void restore_current(void);
-
-static char promlib_buf[128];
-
-/* Internal version of prom_getchild that does not alter return values. */
-int __prom_getchild(int node)
-{
- unsigned long flags;
- int cnode;
-
- spin_lock_irqsave(&prom_lock, flags);
- cnode = prom_nodeops->no_child(node);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-
- return cnode;
-}
-
-/* Return the child of node 'node' or zero if no this node has no
- * direct descendent.
- */
-int prom_getchild(int node)
-{
- int cnode;
-
- if (node == -1)
- return 0;
-
- cnode = __prom_getchild(node);
- if (cnode == 0 || cnode == -1)
- return 0;
-
- return cnode;
-}
-
-/* Internal version of prom_getsibling that does not alter return values. */
-int __prom_getsibling(int node)
-{
- unsigned long flags;
- int cnode;
-
- spin_lock_irqsave(&prom_lock, flags);
- cnode = prom_nodeops->no_nextnode(node);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-
- return cnode;
-}
-
-/* Return the next sibling of node 'node' or zero if no more siblings
- * at this level of depth in the tree.
- */
-int prom_getsibling(int node)
-{
- int sibnode;
-
- if (node == -1)
- return 0;
-
- sibnode = __prom_getsibling(node);
- if (sibnode == 0 || sibnode == -1)
- return 0;
-
- return sibnode;
-}
-
-/* Return the length in bytes of property 'prop' at node 'node'.
- * Return -1 on error.
- */
-int prom_getproplen(int node, char *prop)
-{
- int ret;
- unsigned long flags;
-
- if((!node) || (!prop))
- return -1;
-
- spin_lock_irqsave(&prom_lock, flags);
- ret = prom_nodeops->no_proplen(node, prop);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
- return ret;
-}
-
-/* Acquire a property 'prop' at node 'node' and place it in
- * 'buffer' which has a size of 'bufsize'. If the acquisition
- * was successful the length will be returned, else -1 is returned.
- */
-int prom_getproperty(int node, char *prop, char *buffer, int bufsize)
-{
- int plen, ret;
- unsigned long flags;
-
- plen = prom_getproplen(node, prop);
- if((plen > bufsize) || (plen == 0) || (plen == -1))
- return -1;
- /* Ok, things seem all right. */
- spin_lock_irqsave(&prom_lock, flags);
- ret = prom_nodeops->no_getprop(node, prop, buffer);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
- return ret;
-}
-
-/* Acquire an integer property and return its value. Returns -1
- * on failure.
- */
-int prom_getint(int node, char *prop)
-{
- static int intprop;
-
- if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1)
- return intprop;
-
- return -1;
-}
-
-/* Acquire an integer property, upon error return the passed default
- * integer.
- */
-int prom_getintdefault(int node, char *property, int deflt)
-{
- int retval;
-
- retval = prom_getint(node, property);
- if(retval == -1) return deflt;
-
- return retval;
-}
-
-/* Acquire a boolean property, 1=TRUE 0=FALSE. */
-int prom_getbool(int node, char *prop)
-{
- int retval;
-
- retval = prom_getproplen(node, prop);
- if(retval == -1) return 0;
- return 1;
-}
-
-/* Acquire a property whose value is a string, returns a null
- * string on error. The char pointer is the user supplied string
- * buffer.
- */
-void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
-{
- int len;
-
- len = prom_getproperty(node, prop, user_buf, ubuf_size);
- if(len != -1) return;
- user_buf[0] = 0;
- return;
-}
-
-
-/* Does the device at node 'node' have name 'name'?
- * YES = 1 NO = 0
- */
-int prom_nodematch(int node, char *name)
-{
- int error;
-
- static char namebuf[128];
- error = prom_getproperty(node, "name", namebuf, sizeof(namebuf));
- if (error == -1) return 0;
- if(strcmp(namebuf, name) == 0) return 1;
- return 0;
-}
-
-/* Search siblings at 'node_start' for a node with name
- * 'nodename'. Return node if successful, zero if not.
- */
-int prom_searchsiblings(int node_start, char *nodename)
-{
-
- int thisnode, error;
-
- for(thisnode = node_start; thisnode;
- thisnode=prom_getsibling(thisnode)) {
- error = prom_getproperty(thisnode, "name", promlib_buf,
- sizeof(promlib_buf));
- /* Should this ever happen? */
- if(error == -1) continue;
- if(strcmp(nodename, promlib_buf)==0) return thisnode;
- }
-
- return 0;
-}
-
-/* Interal version of nextprop that does not alter return values. */
-char * __prom_nextprop(int node, char * oprop)
-{
- unsigned long flags;
- char *prop;
-
- spin_lock_irqsave(&prom_lock, flags);
- prop = prom_nodeops->no_nextprop(node, oprop);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
-
- return prop;
-}
-
-/* Return the first property name for node 'node'. */
-/* buffer is unused argument, but as v9 uses it, we need to have the same interface */
-char * prom_firstprop(int node, char *bufer)
-{
- if (node == 0 || node == -1)
- return "";
-
- return __prom_nextprop(node, "");
-}
-
-/* Return the property type string after property type 'oprop'
- * at node 'node' . Returns empty string if no more
- * property types for this node.
- */
-char * prom_nextprop(int node, char *oprop, char *buffer)
-{
- if (node == 0 || node == -1)
- return "";
-
- return __prom_nextprop(node, oprop);
-}
-
-int prom_finddevice(char *name)
-{
- char nbuf[128];
- char *s = name, *d;
- int node = prom_root_node, node2;
- unsigned int which_io, phys_addr;
- struct linux_prom_registers reg[PROMREG_MAX];
-
- while (*s++) {
- if (!*s) return node; /* path '.../' is legal */
- node = prom_getchild(node);
-
- for (d = nbuf; *s != 0 && *s != '@' && *s != '/';)
- *d++ = *s++;
- *d = 0;
-
- node = prom_searchsiblings(node, nbuf);
- if (!node)
- return 0;
-
- if (*s == '@') {
- if (isxdigit(s[1]) && s[2] == ',') {
- which_io = simple_strtoul(s+1, NULL, 16);
- phys_addr = simple_strtoul(s+3, &d, 16);
- if (d != s + 3 && (!*d || *d == '/')
- && d <= s + 3 + 8) {
- node2 = node;
- while (node2 && node2 != -1) {
- if (prom_getproperty (node2, "reg", (char *)reg, sizeof (reg)) > 0) {
- if (which_io == reg[0].which_io && phys_addr == reg[0].phys_addr) {
- node = node2;
- break;
- }
- }
- node2 = prom_getsibling(node2);
- if (!node2 || node2 == -1)
- break;
- node2 = prom_searchsiblings(prom_getsibling(node2), nbuf);
- }
- }
- }
- while (*s != 0 && *s != '/') s++;
- }
- }
- return node;
-}
-
-int prom_node_has_property(int node, char *prop)
-{
- char *current_property = "";
-
- do {
- current_property = prom_nextprop(node, current_property, NULL);
- if(!strcmp(current_property, prop))
- return 1;
- } while (*current_property);
- return 0;
-}
-
-/* Set property 'pname' at node 'node' to value 'value' which has a length
- * of 'size' bytes. Return the number of bytes the prom accepted.
- */
-int prom_setprop(int node, char *pname, char *value, int size)
-{
- unsigned long flags;
- int ret;
-
- if(size == 0) return 0;
- if((pname == 0) || (value == 0)) return 0;
- spin_lock_irqsave(&prom_lock, flags);
- ret = prom_nodeops->no_setprop(node, pname, value, size);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
- return ret;
-}
-
-int prom_inst2pkg(int inst)
-{
- int node;
- unsigned long flags;
-
- spin_lock_irqsave(&prom_lock, flags);
- node = (*romvec->pv_v2devops.v2_inst2pkg)(inst);
- restore_current();
- spin_unlock_irqrestore(&prom_lock, flags);
- if (node == -1) return 0;
- return node;
-}
-
-/* Return 'node' assigned to a particular prom 'path'
- * FIXME: Should work for v0 as well
- */
-int prom_pathtoinode(char *path)
-{
- int node, inst;
-
- inst = prom_devopen (path);
- if (inst == -1) return 0;
- node = prom_inst2pkg (inst);
- prom_devclose (inst);
- if (node == -1) return 0;
- return node;
-}
diff --git a/arch/sparc/prom/tree_32.c b/arch/sparc/prom/tree_32.c
new file mode 100644
index 000000000000..6d8187357331
--- /dev/null
+++ b/arch/sparc/prom/tree_32.c
@@ -0,0 +1,346 @@
+/*
+ * tree.c: Basic device tree traversal/scanning for the Linux
+ * prom library.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#define PROMLIB_INTERNAL
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+extern void restore_current(void);
+
+static char promlib_buf[128];
+
+/* Internal version of prom_getchild that does not alter return values. */
+int __prom_getchild(int node)
+{
+ unsigned long flags;
+ int cnode;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ cnode = prom_nodeops->no_child(node);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+
+ return cnode;
+}
+
+/* Return the child of node 'node' or zero if no this node has no
+ * direct descendent.
+ */
+int prom_getchild(int node)
+{
+ int cnode;
+
+ if (node == -1)
+ return 0;
+
+ cnode = __prom_getchild(node);
+ if (cnode == 0 || cnode == -1)
+ return 0;
+
+ return cnode;
+}
+
+/* Internal version of prom_getsibling that does not alter return values. */
+int __prom_getsibling(int node)
+{
+ unsigned long flags;
+ int cnode;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ cnode = prom_nodeops->no_nextnode(node);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+
+ return cnode;
+}
+
+/* Return the next sibling of node 'node' or zero if no more siblings
+ * at this level of depth in the tree.
+ */
+int prom_getsibling(int node)
+{
+ int sibnode;
+
+ if (node == -1)
+ return 0;
+
+ sibnode = __prom_getsibling(node);
+ if (sibnode == 0 || sibnode == -1)
+ return 0;
+
+ return sibnode;
+}
+
+/* Return the length in bytes of property 'prop' at node 'node'.
+ * Return -1 on error.
+ */
+int prom_getproplen(int node, const char *prop)
+{
+ int ret;
+ unsigned long flags;
+
+ if((!node) || (!prop))
+ return -1;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ ret = prom_nodeops->no_proplen(node, prop);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ return ret;
+}
+
+/* Acquire a property 'prop' at node 'node' and place it in
+ * 'buffer' which has a size of 'bufsize'. If the acquisition
+ * was successful the length will be returned, else -1 is returned.
+ */
+int prom_getproperty(int node, const char *prop, char *buffer, int bufsize)
+{
+ int plen, ret;
+ unsigned long flags;
+
+ plen = prom_getproplen(node, prop);
+ if((plen > bufsize) || (plen == 0) || (plen == -1))
+ return -1;
+ /* Ok, things seem all right. */
+ spin_lock_irqsave(&prom_lock, flags);
+ ret = prom_nodeops->no_getprop(node, prop, buffer);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ return ret;
+}
+
+/* Acquire an integer property and return its value. Returns -1
+ * on failure.
+ */
+int prom_getint(int node, char *prop)
+{
+ static int intprop;
+
+ if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1)
+ return intprop;
+
+ return -1;
+}
+
+/* Acquire an integer property, upon error return the passed default
+ * integer.
+ */
+int prom_getintdefault(int node, char *property, int deflt)
+{
+ int retval;
+
+ retval = prom_getint(node, property);
+ if(retval == -1) return deflt;
+
+ return retval;
+}
+
+/* Acquire a boolean property, 1=TRUE 0=FALSE. */
+int prom_getbool(int node, char *prop)
+{
+ int retval;
+
+ retval = prom_getproplen(node, prop);
+ if(retval == -1) return 0;
+ return 1;
+}
+
+/* Acquire a property whose value is a string, returns a null
+ * string on error. The char pointer is the user supplied string
+ * buffer.
+ */
+void prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
+{
+ int len;
+
+ len = prom_getproperty(node, prop, user_buf, ubuf_size);
+ if(len != -1) return;
+ user_buf[0] = 0;
+ return;
+}
+
+
+/* Does the device at node 'node' have name 'name'?
+ * YES = 1 NO = 0
+ */
+int prom_nodematch(int node, char *name)
+{
+ int error;
+
+ static char namebuf[128];
+ error = prom_getproperty(node, "name", namebuf, sizeof(namebuf));
+ if (error == -1) return 0;
+ if(strcmp(namebuf, name) == 0) return 1;
+ return 0;
+}
+
+/* Search siblings at 'node_start' for a node with name
+ * 'nodename'. Return node if successful, zero if not.
+ */
+int prom_searchsiblings(int node_start, char *nodename)
+{
+
+ int thisnode, error;
+
+ for(thisnode = node_start; thisnode;
+ thisnode=prom_getsibling(thisnode)) {
+ error = prom_getproperty(thisnode, "name", promlib_buf,
+ sizeof(promlib_buf));
+ /* Should this ever happen? */
+ if(error == -1) continue;
+ if(strcmp(nodename, promlib_buf)==0) return thisnode;
+ }
+
+ return 0;
+}
+
+/* Interal version of nextprop that does not alter return values. */
+char * __prom_nextprop(int node, char * oprop)
+{
+ unsigned long flags;
+ char *prop;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ prop = prom_nodeops->no_nextprop(node, oprop);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+
+ return prop;
+}
+
+/* Return the first property name for node 'node'. */
+/* buffer is unused argument, but as v9 uses it, we need to have the same interface */
+char * prom_firstprop(int node, char *bufer)
+{
+ if (node == 0 || node == -1)
+ return "";
+
+ return __prom_nextprop(node, "");
+}
+
+/* Return the property type string after property type 'oprop'
+ * at node 'node' . Returns empty string if no more
+ * property types for this node.
+ */
+char * prom_nextprop(int node, char *oprop, char *buffer)
+{
+ if (node == 0 || node == -1)
+ return "";
+
+ return __prom_nextprop(node, oprop);
+}
+
+int prom_finddevice(char *name)
+{
+ char nbuf[128];
+ char *s = name, *d;
+ int node = prom_root_node, node2;
+ unsigned int which_io, phys_addr;
+ struct linux_prom_registers reg[PROMREG_MAX];
+
+ while (*s++) {
+ if (!*s) return node; /* path '.../' is legal */
+ node = prom_getchild(node);
+
+ for (d = nbuf; *s != 0 && *s != '@' && *s != '/';)
+ *d++ = *s++;
+ *d = 0;
+
+ node = prom_searchsiblings(node, nbuf);
+ if (!node)
+ return 0;
+
+ if (*s == '@') {
+ if (isxdigit(s[1]) && s[2] == ',') {
+ which_io = simple_strtoul(s+1, NULL, 16);
+ phys_addr = simple_strtoul(s+3, &d, 16);
+ if (d != s + 3 && (!*d || *d == '/')
+ && d <= s + 3 + 8) {
+ node2 = node;
+ while (node2 && node2 != -1) {
+ if (prom_getproperty (node2, "reg", (char *)reg, sizeof (reg)) > 0) {
+ if (which_io == reg[0].which_io && phys_addr == reg[0].phys_addr) {
+ node = node2;
+ break;
+ }
+ }
+ node2 = prom_getsibling(node2);
+ if (!node2 || node2 == -1)
+ break;
+ node2 = prom_searchsiblings(prom_getsibling(node2), nbuf);
+ }
+ }
+ }
+ while (*s != 0 && *s != '/') s++;
+ }
+ }
+ return node;
+}
+
+int prom_node_has_property(int node, char *prop)
+{
+ char *current_property = "";
+
+ do {
+ current_property = prom_nextprop(node, current_property, NULL);
+ if(!strcmp(current_property, prop))
+ return 1;
+ } while (*current_property);
+ return 0;
+}
+
+/* Set property 'pname' at node 'node' to value 'value' which has a length
+ * of 'size' bytes. Return the number of bytes the prom accepted.
+ */
+int prom_setprop(int node, const char *pname, char *value, int size)
+{
+ unsigned long flags;
+ int ret;
+
+ if(size == 0) return 0;
+ if((pname == 0) || (value == 0)) return 0;
+ spin_lock_irqsave(&prom_lock, flags);
+ ret = prom_nodeops->no_setprop(node, pname, value, size);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ return ret;
+}
+
+int prom_inst2pkg(int inst)
+{
+ int node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ node = (*romvec->pv_v2devops.v2_inst2pkg)(inst);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+ if (node == -1) return 0;
+ return node;
+}
+
+/* Return 'node' assigned to a particular prom 'path'
+ * FIXME: Should work for v0 as well
+ */
+int prom_pathtoinode(char *path)
+{
+ int node, inst;
+
+ inst = prom_devopen (path);
+ if (inst == -1) return 0;
+ node = prom_inst2pkg (inst);
+ prom_devclose (inst);
+ if (node == -1) return 0;
+ return node;
+}
diff --git a/arch/sparc64/prom/tree.c b/arch/sparc/prom/tree_64.c
index 281aea44790b..281aea44790b 100644
--- a/arch/sparc64/prom/tree.c
+++ b/arch/sparc/prom/tree_64.c
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
deleted file mode 100644
index 3b96e70b4670..000000000000
--- a/arch/sparc64/Kconfig
+++ /dev/null
@@ -1,433 +0,0 @@
-# sparc64 configuration
-mainmenu "Linux Kernel Configuration for 64-bit SPARC"
-
-config SPARC
- bool
- default y
- select HAVE_OPROFILE
- select HAVE_KPROBES
- select HAVE_KRETPROBES
-
-config SPARC64
- bool
- default y
- select HAVE_FUNCTION_TRACER
- select HAVE_IDE
- select HAVE_LMB
- select HAVE_ARCH_KGDB
- select USE_GENERIC_SMP_HELPERS if SMP
- select HAVE_ARCH_TRACEHOOK
- select ARCH_WANT_OPTIONAL_GPIOLIB
- select RTC_CLASS
- select RTC_DRV_M48T59
- select RTC_DRV_CMOS
- select RTC_DRV_BQ4802
- select RTC_DRV_SUN4V
- select RTC_DRV_STARFIRE
-
-config GENERIC_TIME
- bool
- default y
-
-config GENERIC_CMOS_UPDATE
- bool
- default y
-
-config GENERIC_CLOCKEVENTS
- bool
- default y
-
-config GENERIC_GPIO
- bool
- help
- Generic GPIO API support
-
-config 64BIT
- def_bool y
-
-config MMU
- bool
- default y
-
-config IOMMU_HELPER
- bool
- default y
-
-config QUICKLIST
- bool
- default y
-
-config STACKTRACE_SUPPORT
- bool
- default y
-
-config LOCKDEP_SUPPORT
- bool
- default y
-
-config ARCH_MAY_HAVE_PC_FDC
- bool
- default y
-
-config ARCH_HAS_ILOG2_U32
- bool
- default n
-
-config ARCH_HAS_ILOG2_U64
- bool
- default n
-
-config AUDIT_ARCH
- bool
- default y
-
-config HAVE_SETUP_PER_CPU_AREA
- def_bool y
-
-config ARCH_NO_VIRT_TO_BUS
- def_bool y
-
-config OF
- def_bool y
-
-config GENERIC_HARDIRQS_NO__DO_IRQ
- bool
- def_bool y
-
-source "init/Kconfig"
-source "kernel/Kconfig.freezer"
-
-menu "Processor type and features"
-
-choice
- prompt "Kernel page size"
- default SPARC64_PAGE_SIZE_8KB
-
-config SPARC64_PAGE_SIZE_8KB
- bool "8KB"
- help
- This lets you select the page size of the kernel.
-
- 8KB and 64KB work quite well, since SPARC ELF sections
- provide for up to 64KB alignment.
-
- If you don't know what to do, choose 8KB.
-
-config SPARC64_PAGE_SIZE_64KB
- bool "64KB"
-
-endchoice
-
-config SECCOMP
- bool "Enable seccomp to safely compute untrusted bytecode"
- depends on PROC_FS
- default y
- 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. Only embedded should say N here.
-
-source kernel/Kconfig.hz
-
-config HOTPLUG_CPU
- bool "Support for hot-pluggable CPUs"
- depends on SMP
- select HOTPLUG
- help
- Say Y here to experiment with turning CPUs off and on. CPUs
- can be controlled through /sys/devices/system/cpu/cpu#.
- Say N if you want to disable CPU hotplug.
-
-config GENERIC_HARDIRQS
- bool
- default y
-
-source "kernel/time/Kconfig"
-
-config SMP
- bool "Symmetric multi-processing support"
- help
- This enables support for systems with more than one CPU. If you have
- a system with only one CPU, say N. If you have a system with more than
- one CPU, say Y.
-
- 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 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-1024)"
- range 2 1024
- depends on SMP
- default "64"
-
-source "drivers/cpufreq/Kconfig"
-
-config US3_FREQ
- tristate "UltraSPARC-III CPU Frequency driver"
- depends on CPU_FREQ
- select CPU_FREQ_TABLE
- help
- This adds the CPUFreq driver for UltraSPARC-III processors.
-
- For details, take a look at <file:Documentation/cpu-freq>.
-
- If in doubt, say N.
-
-config US2E_FREQ
- tristate "UltraSPARC-IIe CPU Frequency driver"
- depends on CPU_FREQ
- select CPU_FREQ_TABLE
- help
- This adds the CPUFreq driver for UltraSPARC-IIe processors.
-
- For details, take a look at <file:Documentation/cpu-freq>.
-
- If in doubt, say N.
-
-config US3_MC
- tristate "UltraSPARC-III Memory Controller driver"
- default y
- help
- This adds a driver for the UltraSPARC-III memory controller.
- Loading this driver allows exact mnemonic strings to be
- printed in the event of a memory error, so that the faulty DIMM
- on the motherboard can be matched to the error.
-
- If in doubt, say Y, as this information can be very useful.
-
-# Global things across all Sun machines.
-config GENERIC_LOCKBREAK
- bool
- default y
- depends on SMP && PREEMPT
-
-config RWSEM_GENERIC_SPINLOCK
- bool
-
-config RWSEM_XCHGADD_ALGORITHM
- bool
- default y
-
-config GENERIC_FIND_NEXT_BIT
- bool
- default y
-
-config GENERIC_HWEIGHT
- bool
- default y if !ULTRA_HAS_POPULATION_COUNT
-
-config GENERIC_CALIBRATE_DELAY
- bool
- default y
-
-choice
- prompt "SPARC64 Huge TLB Page Size"
- depends on HUGETLB_PAGE
- default HUGETLB_PAGE_SIZE_4MB
-
-config HUGETLB_PAGE_SIZE_4MB
- bool "4MB"
-
-config HUGETLB_PAGE_SIZE_512K
- bool "512K"
-
-config HUGETLB_PAGE_SIZE_64K
- depends on !SPARC64_PAGE_SIZE_64KB
- bool "64K"
-
-endchoice
-
-endmenu
-
-config NUMA
- bool "NUMA support"
- depends on SMP
-
-config NODES_SHIFT
- int
- default "4"
- depends on NEED_MULTIPLE_NODES
-
-# Some NUMA nodes have memory ranges that span
-# other nodes. Even though a pfn is valid and
-# between a node's start and end pfns, it may not
-# reside on that node. See memmap_init_zone()
-# for details.
-config NODES_SPAN_OTHER_NODES
- def_bool y
- depends on NEED_MULTIPLE_NODES
-
-config ARCH_POPULATES_NODE_MAP
- def_bool y
-
-config ARCH_SELECT_MEMORY_MODEL
- def_bool y
-
-config ARCH_SPARSEMEM_ENABLE
- def_bool y
- select SPARSEMEM_VMEMMAP_ENABLE
-
-config ARCH_SPARSEMEM_DEFAULT
- def_bool y
-
-source "mm/Kconfig"
-
-config ISA
- bool
-
-config ISAPNP
- bool
-
-config EISA
- bool
-
-config MCA
- bool
-
-config PCMCIA
- tristate
- help
- Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
- computer. These are credit-card size devices such as network cards,
- modems or hard drives often used with laptops computers. There are
- actually two varieties of these cards: the older 16 bit PCMCIA cards
- and the newer 32 bit CardBus cards. If you want to use CardBus
- cards, you need to say Y here and also to "CardBus support" below.
-
- To use your PC-cards, you will need supporting software from David
- Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
- for location). Please also read the PCMCIA-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To compile this driver as modules, choose M here: the
- modules will be called pcmcia_core and ds.
-
-config SBUS
- bool
- default y
-
-config SBUSCHAR
- bool
- default y
-
-config SUN_AUXIO
- bool
- default y
-
-config SUN_IO
- bool
- default y
-
-config SUN_LDOMS
- bool "Sun Logical Domains support"
- help
- Say Y here is you want to support virtual devices via
- Logical Domains.
-
-config PCI
- bool "PCI support"
- select ARCH_SUPPORTS_MSI
- help
- Find out whether your system includes a PCI bus. PCI is the name of
- a bus system, i.e. the way the CPU talks to the other stuff inside
- your box. If you say Y here, the kernel will include drivers and
- infrastructure code to support PCI bus devices.
-
-config PCI_DOMAINS
- def_bool PCI
-
-config PCI_SYSCALL
- def_bool PCI
-
-source "drivers/pci/Kconfig"
-
-config SUN_OPENPROMFS
- tristate "Openprom tree appears in /proc/openprom"
- help
- If you say Y, the OpenPROM device tree will be available as a
- virtual file system, which you can mount to /proc/openprom by "mount
- -t openpromfs none /proc/openprom".
-
- To compile the /proc/openprom support as a module, choose M here: the
- module will be called openpromfs. If unsure, choose M.
-
-menu "Executable file formats"
-
-source "fs/Kconfig.binfmt"
-
-config COMPAT
- bool
- default y
- select COMPAT_BINFMT_ELF
-
-config SYSVIPC_COMPAT
- bool
- depends on COMPAT && SYSVIPC
- default y
-
-endmenu
-
-config SCHED_SMT
- bool "SMT (Hyperthreading) scheduler support"
- depends on SMP
- default y
- help
- SMT scheduler support improves the CPU scheduler's decision making
- when dealing with SPARC cpus at a cost of slightly increased overhead
- in some places. If unsure say N here.
-
-config SCHED_MC
- bool "Multi-core scheduler support"
- depends on SMP
- default y
- help
- Multi-core scheduler support improves the CPU scheduler's decision
- making when dealing with multi-core CPU chips at a cost of slightly
- increased overhead in some places. If unsure say N here.
-
-source "kernel/Kconfig.preempt"
-
-config CMDLINE_BOOL
- bool "Default bootloader kernel arguments"
-
-config CMDLINE
- string "Initial kernel command string"
- depends on CMDLINE_BOOL
- default "console=ttyS0,9600 root=/dev/sda1"
- help
- Say Y here if you want to be able to pass default arguments to
- the kernel. This will be overridden by the bootloader, if you
- use one (such as SILO). This is most useful if you want to boot
- a kernel from TFTP, and want default options to be available
- with having them passed on the command line.
-
- NOTE: This option WILL override the PROM bootargs setting!
-
-source "net/Kconfig"
-
-source "drivers/Kconfig"
-
-source "drivers/sbus/char/Kconfig"
-
-source "fs/Kconfig"
-
-source "arch/sparc64/Kconfig.debug"
-
-source "security/Kconfig"
-
-source "crypto/Kconfig"
-
-source "lib/Kconfig"
diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug
deleted file mode 100644
index c40515c06690..000000000000
--- a/arch/sparc64/Kconfig.debug
+++ /dev/null
@@ -1,44 +0,0 @@
-menu "Kernel hacking"
-
-config TRACE_IRQFLAGS_SUPPORT
- bool
- default y
-
-source "lib/Kconfig.debug"
-
-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 and sysrq-P debug output.
-
- This option will slow down process creation somewhat.
-
-config DEBUG_DCFLUSH
- bool "D-cache flush debugging"
- depends on DEBUG_KERNEL
-
-config STACK_DEBUG
- depends on DEBUG_KERNEL
- bool "Stack Overflow Detection Support"
-
-config DEBUG_PAGEALLOC
- bool "Debug page memory allocations"
- depends on DEBUG_KERNEL && !HIBERNATION
- help
- Unmap pages from the kernel linear mapping after free_pages().
- This results in a large slowdown, but helps to find certain types
- of memory corruptions.
-
-config MCOUNT
- bool
- depends on STACK_DEBUG || FUNCTION_TRACER
- default y
-
-config FRAME_POINTER
- bool
- depends on MCOUNT
- default y
-
-endmenu
diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile
deleted file mode 100644
index c7214abc0d84..000000000000
--- a/arch/sparc64/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-# sparc64/Makefile
-#
-# Makefile for the architecture dependent flags and dependencies on the
-# 64-bit Sparc.
-#
-# Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
-# Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
-#
-
-CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64
-
-# Undefine sparc when processing vmlinux.lds - it is used
-# And teach CPP we are doing 64 bit builds (for this case)
-CPPFLAGS_vmlinux.lds += -m64 -Usparc
-
-LDFLAGS := -m elf64_sparc
-
-KBUILD_CFLAGS += -m64 -pipe -mno-fpu -mcpu=ultrasparc -mcmodel=medlow \
- -ffixed-g4 -ffixed-g5 -fcall-used-g7 -Wno-sign-compare \
- -Wa,--undeclared-regs
-KBUILD_CFLAGS += $(call cc-option,-mtune=ultrasparc3)
-KBUILD_AFLAGS += -m64 -mcpu=ultrasparc -Wa,--undeclared-regs
-
-ifeq ($(CONFIG_MCOUNT),y)
- KBUILD_CFLAGS += -pg
-endif
-
-head-y := arch/sparc64/kernel/head.o arch/sparc64/kernel/init_task.o
-
-core-y += arch/sparc64/kernel/ arch/sparc64/mm/
-core-y += arch/sparc64/math-emu/
-libs-y += arch/sparc64/prom/ arch/sparc64/lib/
-drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
-
-boot := arch/sparc64/boot
-
-image tftpboot.img vmlinux.aout: vmlinux
- $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
-
-archclean:
- $(Q)$(MAKE) $(clean)=$(boot)
-
-define archhelp
- echo '* vmlinux - Standard sparc64 kernel'
- echo ' vmlinux.aout - a.out kernel for sparc64'
- echo ' tftpboot.img - Image prepared for tftp'
-endef
-
diff --git a/arch/sparc64/boot/.gitignore b/arch/sparc64/boot/.gitignore
deleted file mode 100644
index 36356f9d498e..000000000000
--- a/arch/sparc64/boot/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-image
-tftpboot.img
-vmlinux.aout
-piggyback
diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile
deleted file mode 100644
index 0458b5244f09..000000000000
--- a/arch/sparc64/boot/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-# Makefile for the Sparc64 boot stuff.
-#
-# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
-# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
-
-ROOT_IMG := /usr/src/root.img
-ELFTOAOUT := elftoaout
-
-hostprogs-y := piggyback
-targets := image tftpboot.img vmlinux.aout
-
-quiet_cmd_elftoaout = ELF2AOUT $@
- cmd_elftoaout = $(ELFTOAOUT) vmlinux -o $@
-quiet_cmd_piggy = PIGGY $@
- cmd_piggy = $(obj)/piggyback $@ System.map $(ROOT_IMG)
-quiet_cmd_strip = STRIP $@
- cmd_strip = $(STRIP) -R .comment -R .note -K sun4u_init -K _end -K _start vmlinux -o $@
-
-
-# Actual linking
-$(obj)/image: vmlinux FORCE
- $(call if_changed,strip)
- @echo ' kernel: $@ is ready'
-
-$(obj)/tftpboot.img: vmlinux $(obj)/piggyback System.map $(ROOT_IMG) FORCE
- $(call if_changed,elftoaout)
- $(call if_changed,piggy)
- @echo ' kernel: $@ is ready'
-
-$(obj)/vmlinux.aout: vmlinux FORCE
- $(call if_changed,elftoaout)
- @echo ' kernel: $@ is ready'
-
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
deleted file mode 100644
index b3e0b986bef8..000000000000
--- a/arch/sparc64/kernel/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Makefile for the linux kernel.
-#
-
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS := -Werror
-
-CFLAGS_REMOVE_ftrace.o = -pg
-
-extra-y := head.o init_task.o vmlinux.lds
-
-obj-y := process.o setup.o cpu.o idprom.o reboot.o \
- traps.o auxio.o una_asm.o sysfs.o iommu.o \
- irq.o ptrace.o time.o sys_sparc.o signal.o \
- unaligned.o central.o starfire.o \
- power.o sbus.o sparc64_ksyms.o ebus.o \
- visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o
-
-obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
-obj-$(CONFIG_STACKTRACE) += stacktrace.o
-obj-$(CONFIG_PCI) += pci.o pci_common.o psycho_common.o \
- pci_psycho.o pci_sabre.o pci_schizo.o \
- pci_sun4v.o pci_sun4v_asm.o pci_fire.o
-obj-$(CONFIG_PCI_MSI) += pci_msi.o
-obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o
-obj-$(CONFIG_COMPAT) += sys32.o sys_sparc32.o signal32.o
-obj-$(CONFIG_MODULES) += module.o
-obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
-obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
-obj-$(CONFIG_US3_MC) += chmc.o
-obj-$(CONFIG_KPROBES) += kprobes.o
-obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o
-obj-$(CONFIG_AUDIT) += audit.o
-obj-$(CONFIG_AUDIT)$(CONFIG_COMPAT) += compat_audit.o
-obj-y += $(obj-yy)
-obj-$(CONFIG_KGDB) += kgdb.o
diff --git a/arch/sparc64/kernel/asm-offsets.c b/arch/sparc64/kernel/asm-offsets.c
deleted file mode 100644
index 9e263112a6e2..000000000000
--- a/arch/sparc64/kernel/asm-offsets.c
+++ /dev/null
@@ -1 +0,0 @@
-/* Dummy asm-offsets.c file. Required by kbuild and ready to be used - hint! */
diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c
deleted file mode 100644
index 858beda86524..000000000000
--- a/arch/sparc64/kernel/auxio.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* auxio.c: Probing for the Sparc AUXIO register at boot time.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- *
- * Refactoring for unified NCR/PCIO support 2002 Eric Brower (ebrower@usa.net)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/of_device.h>
-
-#include <asm/prom.h>
-#include <asm/io.h>
-#include <asm/auxio.h>
-
-void __iomem *auxio_register = NULL;
-EXPORT_SYMBOL(auxio_register);
-
-enum auxio_type {
- AUXIO_TYPE_NODEV,
- AUXIO_TYPE_SBUS,
- AUXIO_TYPE_EBUS
-};
-
-static enum auxio_type auxio_devtype = AUXIO_TYPE_NODEV;
-static DEFINE_SPINLOCK(auxio_lock);
-
-static void __auxio_sbus_set(u8 bits_on, u8 bits_off)
-{
- if (auxio_register) {
- unsigned char regval;
- unsigned long flags;
- unsigned char newval;
-
- spin_lock_irqsave(&auxio_lock, flags);
-
- regval = sbus_readb(auxio_register);
- newval = regval | bits_on;
- newval &= ~bits_off;
- newval &= ~AUXIO_AUX1_MASK;
- sbus_writeb(newval, auxio_register);
-
- spin_unlock_irqrestore(&auxio_lock, flags);
- }
-}
-
-static void __auxio_ebus_set(u8 bits_on, u8 bits_off)
-{
- if (auxio_register) {
- unsigned char regval;
- unsigned long flags;
- unsigned char newval;
-
- spin_lock_irqsave(&auxio_lock, flags);
-
- regval = (u8)readl(auxio_register);
- newval = regval | bits_on;
- newval &= ~bits_off;
- writel((u32)newval, auxio_register);
-
- spin_unlock_irqrestore(&auxio_lock, flags);
- }
-}
-
-static inline void __auxio_ebus_set_led(int on)
-{
- (on) ? __auxio_ebus_set(AUXIO_PCIO_LED, 0) :
- __auxio_ebus_set(0, AUXIO_PCIO_LED) ;
-}
-
-static inline void __auxio_sbus_set_led(int on)
-{
- (on) ? __auxio_sbus_set(AUXIO_AUX1_LED, 0) :
- __auxio_sbus_set(0, AUXIO_AUX1_LED) ;
-}
-
-void auxio_set_led(int on)
-{
- switch(auxio_devtype) {
- case AUXIO_TYPE_SBUS:
- __auxio_sbus_set_led(on);
- break;
- case AUXIO_TYPE_EBUS:
- __auxio_ebus_set_led(on);
- break;
- default:
- break;
- }
-}
-
-static inline void __auxio_sbus_set_lte(int on)
-{
- (on) ? __auxio_sbus_set(AUXIO_AUX1_LTE, 0) :
- __auxio_sbus_set(0, AUXIO_AUX1_LTE) ;
-}
-
-void auxio_set_lte(int on)
-{
- switch(auxio_devtype) {
- case AUXIO_TYPE_SBUS:
- __auxio_sbus_set_lte(on);
- break;
- case AUXIO_TYPE_EBUS:
- /* FALL-THROUGH */
- default:
- break;
- }
-}
-
-static struct of_device_id __initdata auxio_match[] = {
- {
- .name = "auxio",
- },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, auxio_match);
-
-static int __devinit auxio_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct device_node *dp = dev->node;
- unsigned long size;
-
- if (!strcmp(dp->parent->name, "ebus")) {
- auxio_devtype = AUXIO_TYPE_EBUS;
- size = sizeof(u32);
- } else if (!strcmp(dp->parent->name, "sbus")) {
- auxio_devtype = AUXIO_TYPE_SBUS;
- size = 1;
- } else {
- printk("auxio: Unknown parent bus type [%s]\n",
- dp->parent->name);
- return -ENODEV;
- }
- auxio_register = of_ioremap(&dev->resource[0], 0, size, "auxio");
- if (!auxio_register)
- return -ENODEV;
-
- printk(KERN_INFO "AUXIO: Found device at %s\n",
- dp->full_name);
-
- if (auxio_devtype == AUXIO_TYPE_EBUS)
- auxio_set_led(AUXIO_LED_ON);
-
- return 0;
-}
-
-static struct of_platform_driver auxio_driver = {
- .match_table = auxio_match,
- .probe = auxio_probe,
- .driver = {
- .name = "auxio",
- },
-};
-
-static int __init auxio_init(void)
-{
- return of_register_driver(&auxio_driver, &of_platform_bus_type);
-}
-
-/* Must be after subsys_initcall() so that busses are probed. Must
- * be before device_initcall() because things like the floppy driver
- * need to use the AUXIO register.
- */
-fs_initcall(auxio_init);
diff --git a/arch/sparc64/kernel/cherrs.S b/arch/sparc64/kernel/cherrs.S
deleted file mode 100644
index 89afebd7eca0..000000000000
--- a/arch/sparc64/kernel/cherrs.S
+++ /dev/null
@@ -1,579 +0,0 @@
- /* These get patched into the trap table at boot time
- * once we know we have a cheetah processor.
- */
- .globl cheetah_fecc_trap_vector
- .type cheetah_fecc_trap_vector,#function
-cheetah_fecc_trap_vector:
- membar #Sync
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
- andn %g1, DCU_DC | DCU_IC, %g1
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- sethi %hi(cheetah_fast_ecc), %g2
- jmpl %g2 + %lo(cheetah_fast_ecc), %g0
- mov 0, %g1
- .size cheetah_fecc_trap_vector,.-cheetah_fecc_trap_vector
-
- .globl cheetah_fecc_trap_vector_tl1
- .type cheetah_fecc_trap_vector_tl1,#function
-cheetah_fecc_trap_vector_tl1:
- membar #Sync
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
- andn %g1, DCU_DC | DCU_IC, %g1
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- sethi %hi(cheetah_fast_ecc), %g2
- jmpl %g2 + %lo(cheetah_fast_ecc), %g0
- mov 1, %g1
- .size cheetah_fecc_trap_vector_tl1,.-cheetah_fecc_trap_vector_tl1
-
- .globl cheetah_cee_trap_vector
- .type cheetah_cee_trap_vector,#function
-cheetah_cee_trap_vector:
- membar #Sync
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
- andn %g1, DCU_IC, %g1
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- sethi %hi(cheetah_cee), %g2
- jmpl %g2 + %lo(cheetah_cee), %g0
- mov 0, %g1
- .size cheetah_cee_trap_vector,.-cheetah_cee_trap_vector
-
- .globl cheetah_cee_trap_vector_tl1
- .type cheetah_cee_trap_vector_tl1,#function
-cheetah_cee_trap_vector_tl1:
- membar #Sync
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
- andn %g1, DCU_IC, %g1
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- sethi %hi(cheetah_cee), %g2
- jmpl %g2 + %lo(cheetah_cee), %g0
- mov 1, %g1
- .size cheetah_cee_trap_vector_tl1,.-cheetah_cee_trap_vector_tl1
-
- .globl cheetah_deferred_trap_vector
- .type cheetah_deferred_trap_vector,#function
-cheetah_deferred_trap_vector:
- membar #Sync
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
- andn %g1, DCU_DC | DCU_IC, %g1;
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
- membar #Sync;
- sethi %hi(cheetah_deferred_trap), %g2
- jmpl %g2 + %lo(cheetah_deferred_trap), %g0
- mov 0, %g1
- .size cheetah_deferred_trap_vector,.-cheetah_deferred_trap_vector
-
- .globl cheetah_deferred_trap_vector_tl1
- .type cheetah_deferred_trap_vector_tl1,#function
-cheetah_deferred_trap_vector_tl1:
- membar #Sync;
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1;
- andn %g1, DCU_DC | DCU_IC, %g1;
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG;
- membar #Sync;
- sethi %hi(cheetah_deferred_trap), %g2
- jmpl %g2 + %lo(cheetah_deferred_trap), %g0
- mov 1, %g1
- .size cheetah_deferred_trap_vector_tl1,.-cheetah_deferred_trap_vector_tl1
-
- /* Cheetah+ specific traps. These are for the new I/D cache parity
- * error traps. The first argument to cheetah_plus_parity_handler
- * is encoded as follows:
- *
- * Bit0: 0=dcache,1=icache
- * Bit1: 0=recoverable,1=unrecoverable
- */
- .globl cheetah_plus_dcpe_trap_vector
- .type cheetah_plus_dcpe_trap_vector,#function
-cheetah_plus_dcpe_trap_vector:
- membar #Sync
- sethi %hi(do_cheetah_plus_data_parity), %g7
- jmpl %g7 + %lo(do_cheetah_plus_data_parity), %g0
- nop
- nop
- nop
- nop
- nop
- .size cheetah_plus_dcpe_trap_vector,.-cheetah_plus_dcpe_trap_vector
-
- .type do_cheetah_plus_data_parity,#function
-do_cheetah_plus_data_parity:
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov 0x0, %o0
- call cheetah_plus_parity_error
- add %sp, PTREGS_OFF, %o1
- ba,a,pt %xcc, rtrap_irq
- .size do_cheetah_plus_data_parity,.-do_cheetah_plus_data_parity
-
- .globl cheetah_plus_dcpe_trap_vector_tl1
- .type cheetah_plus_dcpe_trap_vector_tl1,#function
-cheetah_plus_dcpe_trap_vector_tl1:
- membar #Sync
- wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
- sethi %hi(do_dcpe_tl1), %g3
- jmpl %g3 + %lo(do_dcpe_tl1), %g0
- nop
- nop
- nop
- nop
- .size cheetah_plus_dcpe_trap_vector_tl1,.-cheetah_plus_dcpe_trap_vector_tl1
-
- .globl cheetah_plus_icpe_trap_vector
- .type cheetah_plus_icpe_trap_vector,#function
-cheetah_plus_icpe_trap_vector:
- membar #Sync
- sethi %hi(do_cheetah_plus_insn_parity), %g7
- jmpl %g7 + %lo(do_cheetah_plus_insn_parity), %g0
- nop
- nop
- nop
- nop
- nop
- .size cheetah_plus_icpe_trap_vector,.-cheetah_plus_icpe_trap_vector
-
- .type do_cheetah_plus_insn_parity,#function
-do_cheetah_plus_insn_parity:
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov 0x1, %o0
- call cheetah_plus_parity_error
- add %sp, PTREGS_OFF, %o1
- ba,a,pt %xcc, rtrap_irq
- .size do_cheetah_plus_insn_parity,.-do_cheetah_plus_insn_parity
-
- .globl cheetah_plus_icpe_trap_vector_tl1
- .type cheetah_plus_icpe_trap_vector_tl1,#function
-cheetah_plus_icpe_trap_vector_tl1:
- membar #Sync
- wrpr PSTATE_IG | PSTATE_PEF | PSTATE_PRIV, %pstate
- sethi %hi(do_icpe_tl1), %g3
- jmpl %g3 + %lo(do_icpe_tl1), %g0
- nop
- nop
- nop
- nop
- .size cheetah_plus_icpe_trap_vector_tl1,.-cheetah_plus_icpe_trap_vector_tl1
-
- /* If we take one of these traps when tl >= 1, then we
- * jump to interrupt globals. If some trap level above us
- * was also using interrupt globals, we cannot recover.
- * We may use all interrupt global registers except %g6.
- */
- .globl do_dcpe_tl1
- .type do_dcpe_tl1,#function
-do_dcpe_tl1:
- rdpr %tl, %g1 ! Save original trap level
- mov 1, %g2 ! Setup TSTATE checking loop
- sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
-1: wrpr %g2, %tl ! Set trap level to check
- rdpr %tstate, %g4 ! Read TSTATE for this level
- andcc %g4, %g3, %g0 ! Interrupt globals in use?
- bne,a,pn %xcc, do_dcpe_tl1_fatal ! Yep, irrecoverable
- wrpr %g1, %tl ! Restore original trap level
- add %g2, 1, %g2 ! Next trap level
- cmp %g2, %g1 ! Hit them all yet?
- ble,pt %icc, 1b ! Not yet
- nop
- wrpr %g1, %tl ! Restore original trap level
-do_dcpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
- sethi %hi(dcache_parity_tl1_occurred), %g2
- lduw [%g2 + %lo(dcache_parity_tl1_occurred)], %g1
- add %g1, 1, %g1
- stw %g1, [%g2 + %lo(dcache_parity_tl1_occurred)]
- /* Reset D-cache parity */
- sethi %hi(1 << 16), %g1 ! D-cache size
- mov (1 << 5), %g2 ! D-cache line size
- sub %g1, %g2, %g1 ! Move down 1 cacheline
-1: srl %g1, 14, %g3 ! Compute UTAG
- membar #Sync
- stxa %g3, [%g1] ASI_DCACHE_UTAG
- membar #Sync
- sub %g2, 8, %g3 ! 64-bit data word within line
-2: membar #Sync
- stxa %g0, [%g1 + %g3] ASI_DCACHE_DATA
- membar #Sync
- subcc %g3, 8, %g3 ! Next 64-bit data word
- bge,pt %icc, 2b
- nop
- subcc %g1, %g2, %g1 ! Next cacheline
- bge,pt %icc, 1b
- nop
- ba,pt %xcc, dcpe_icpe_tl1_common
- nop
-
-do_dcpe_tl1_fatal:
- sethi %hi(1f), %g7
- ba,pt %xcc, etraptl1
-1: or %g7, %lo(1b), %g7
- mov 0x2, %o0
- call cheetah_plus_parity_error
- add %sp, PTREGS_OFF, %o1
- ba,pt %xcc, rtrap
- nop
- .size do_dcpe_tl1,.-do_dcpe_tl1
-
- .globl do_icpe_tl1
- .type do_icpe_tl1,#function
-do_icpe_tl1:
- rdpr %tl, %g1 ! Save original trap level
- mov 1, %g2 ! Setup TSTATE checking loop
- sethi %hi(TSTATE_IG), %g3 ! TSTATE mask bit
-1: wrpr %g2, %tl ! Set trap level to check
- rdpr %tstate, %g4 ! Read TSTATE for this level
- andcc %g4, %g3, %g0 ! Interrupt globals in use?
- bne,a,pn %xcc, do_icpe_tl1_fatal ! Yep, irrecoverable
- wrpr %g1, %tl ! Restore original trap level
- add %g2, 1, %g2 ! Next trap level
- cmp %g2, %g1 ! Hit them all yet?
- ble,pt %icc, 1b ! Not yet
- nop
- wrpr %g1, %tl ! Restore original trap level
-do_icpe_tl1_nonfatal: /* Ok we may use interrupt globals safely. */
- sethi %hi(icache_parity_tl1_occurred), %g2
- lduw [%g2 + %lo(icache_parity_tl1_occurred)], %g1
- add %g1, 1, %g1
- stw %g1, [%g2 + %lo(icache_parity_tl1_occurred)]
- /* Flush I-cache */
- sethi %hi(1 << 15), %g1 ! I-cache size
- mov (1 << 5), %g2 ! I-cache line size
- sub %g1, %g2, %g1
-1: or %g1, (2 << 3), %g3
- stxa %g0, [%g3] ASI_IC_TAG
- membar #Sync
- subcc %g1, %g2, %g1
- bge,pt %icc, 1b
- nop
- ba,pt %xcc, dcpe_icpe_tl1_common
- nop
-
-do_icpe_tl1_fatal:
- sethi %hi(1f), %g7
- ba,pt %xcc, etraptl1
-1: or %g7, %lo(1b), %g7
- mov 0x3, %o0
- call cheetah_plus_parity_error
- add %sp, PTREGS_OFF, %o1
- ba,pt %xcc, rtrap
- nop
- .size do_icpe_tl1,.-do_icpe_tl1
-
- .type dcpe_icpe_tl1_common,#function
-dcpe_icpe_tl1_common:
- /* Flush D-cache, re-enable D/I caches in DCU and finally
- * retry the trapping instruction.
- */
- sethi %hi(1 << 16), %g1 ! D-cache size
- mov (1 << 5), %g2 ! D-cache line size
- sub %g1, %g2, %g1
-1: stxa %g0, [%g1] ASI_DCACHE_TAG
- membar #Sync
- subcc %g1, %g2, %g1
- bge,pt %icc, 1b
- nop
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g1
- or %g1, (DCU_DC | DCU_IC), %g1
- stxa %g1, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- retry
- .size dcpe_icpe_tl1_common,.-dcpe_icpe_tl1_common
-
- /* Capture I/D/E-cache state into per-cpu error scoreboard.
- *
- * %g1: (TL>=0) ? 1 : 0
- * %g2: scratch
- * %g3: scratch
- * %g4: AFSR
- * %g5: AFAR
- * %g6: unused, will have current thread ptr after etrap
- * %g7: scratch
- */
- .type __cheetah_log_error,#function
-__cheetah_log_error:
- /* Put "TL1" software bit into AFSR. */
- and %g1, 0x1, %g1
- sllx %g1, 63, %g2
- or %g4, %g2, %g4
-
- /* Get log entry pointer for this cpu at this trap level. */
- BRANCH_IF_JALAPENO(g2,g3,50f)
- ldxa [%g0] ASI_SAFARI_CONFIG, %g2
- srlx %g2, 17, %g2
- ba,pt %xcc, 60f
- and %g2, 0x3ff, %g2
-
-50: ldxa [%g0] ASI_JBUS_CONFIG, %g2
- srlx %g2, 17, %g2
- and %g2, 0x1f, %g2
-
-60: sllx %g2, 9, %g2
- sethi %hi(cheetah_error_log), %g3
- ldx [%g3 + %lo(cheetah_error_log)], %g3
- brz,pn %g3, 80f
- nop
-
- add %g3, %g2, %g3
- sllx %g1, 8, %g1
- add %g3, %g1, %g1
-
- /* %g1 holds pointer to the top of the logging scoreboard */
- ldx [%g1 + 0x0], %g7
- cmp %g7, -1
- bne,pn %xcc, 80f
- nop
-
- stx %g4, [%g1 + 0x0]
- stx %g5, [%g1 + 0x8]
- add %g1, 0x10, %g1
-
- /* %g1 now points to D-cache logging area */
- set 0x3ff8, %g2 /* DC_addr mask */
- and %g5, %g2, %g2 /* DC_addr bits of AFAR */
- srlx %g5, 12, %g3
- or %g3, 1, %g3 /* PHYS tag + valid */
-
-10: ldxa [%g2] ASI_DCACHE_TAG, %g7
- cmp %g3, %g7 /* TAG match? */
- bne,pt %xcc, 13f
- nop
-
- /* Yep, what we want, capture state. */
- stx %g2, [%g1 + 0x20]
- stx %g7, [%g1 + 0x28]
-
- /* A membar Sync is required before and after utag access. */
- membar #Sync
- ldxa [%g2] ASI_DCACHE_UTAG, %g7
- membar #Sync
- stx %g7, [%g1 + 0x30]
- ldxa [%g2] ASI_DCACHE_SNOOP_TAG, %g7
- stx %g7, [%g1 + 0x38]
- clr %g3
-
-12: ldxa [%g2 + %g3] ASI_DCACHE_DATA, %g7
- stx %g7, [%g1]
- add %g3, (1 << 5), %g3
- cmp %g3, (4 << 5)
- bl,pt %xcc, 12b
- add %g1, 0x8, %g1
-
- ba,pt %xcc, 20f
- add %g1, 0x20, %g1
-
-13: sethi %hi(1 << 14), %g7
- add %g2, %g7, %g2
- srlx %g2, 14, %g7
- cmp %g7, 4
- bl,pt %xcc, 10b
- nop
-
- add %g1, 0x40, %g1
-
- /* %g1 now points to I-cache logging area */
-20: set 0x1fe0, %g2 /* IC_addr mask */
- and %g5, %g2, %g2 /* IC_addr bits of AFAR */
- sllx %g2, 1, %g2 /* IC_addr[13:6]==VA[12:5] */
- srlx %g5, (13 - 8), %g3 /* Make PTAG */
- andn %g3, 0xff, %g3 /* Mask off undefined bits */
-
-21: ldxa [%g2] ASI_IC_TAG, %g7
- andn %g7, 0xff, %g7
- cmp %g3, %g7
- bne,pt %xcc, 23f
- nop
-
- /* Yep, what we want, capture state. */
- stx %g2, [%g1 + 0x40]
- stx %g7, [%g1 + 0x48]
- add %g2, (1 << 3), %g2
- ldxa [%g2] ASI_IC_TAG, %g7
- add %g2, (1 << 3), %g2
- stx %g7, [%g1 + 0x50]
- ldxa [%g2] ASI_IC_TAG, %g7
- add %g2, (1 << 3), %g2
- stx %g7, [%g1 + 0x60]
- ldxa [%g2] ASI_IC_TAG, %g7
- stx %g7, [%g1 + 0x68]
- sub %g2, (3 << 3), %g2
- ldxa [%g2] ASI_IC_STAG, %g7
- stx %g7, [%g1 + 0x58]
- clr %g3
- srlx %g2, 2, %g2
-
-22: ldxa [%g2 + %g3] ASI_IC_INSTR, %g7
- stx %g7, [%g1]
- add %g3, (1 << 3), %g3
- cmp %g3, (8 << 3)
- bl,pt %xcc, 22b
- add %g1, 0x8, %g1
-
- ba,pt %xcc, 30f
- add %g1, 0x30, %g1
-
-23: sethi %hi(1 << 14), %g7
- add %g2, %g7, %g2
- srlx %g2, 14, %g7
- cmp %g7, 4
- bl,pt %xcc, 21b
- nop
-
- add %g1, 0x70, %g1
-
- /* %g1 now points to E-cache logging area */
-30: andn %g5, (32 - 1), %g2
- stx %g2, [%g1 + 0x20]
- ldxa [%g2] ASI_EC_TAG_DATA, %g7
- stx %g7, [%g1 + 0x28]
- ldxa [%g2] ASI_EC_R, %g0
- clr %g3
-
-31: ldxa [%g3] ASI_EC_DATA, %g7
- stx %g7, [%g1 + %g3]
- add %g3, 0x8, %g3
- cmp %g3, 0x20
-
- bl,pt %xcc, 31b
- nop
-80:
- rdpr %tt, %g2
- cmp %g2, 0x70
- be c_fast_ecc
- cmp %g2, 0x63
- be c_cee
- nop
- ba,pt %xcc, c_deferred
- .size __cheetah_log_error,.-__cheetah_log_error
-
- /* Cheetah FECC trap handling, we get here from tl{0,1}_fecc
- * in the trap table. That code has done a memory barrier
- * and has disabled both the I-cache and D-cache in the DCU
- * control register. The I-cache is disabled so that we may
- * capture the corrupted cache line, and the D-cache is disabled
- * because corrupt data may have been placed there and we don't
- * want to reference it.
- *
- * %g1 is one if this trap occurred at %tl >= 1.
- *
- * Next, we turn off error reporting so that we don't recurse.
- */
- .globl cheetah_fast_ecc
- .type cheetah_fast_ecc,#function
-cheetah_fast_ecc:
- ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
- andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
- stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
- membar #Sync
-
- /* Fetch and clear AFSR/AFAR */
- ldxa [%g0] ASI_AFSR, %g4
- ldxa [%g0] ASI_AFAR, %g5
- stxa %g4, [%g0] ASI_AFSR
- membar #Sync
-
- ba,pt %xcc, __cheetah_log_error
- nop
- .size cheetah_fast_ecc,.-cheetah_fast_ecc
-
- .type c_fast_ecc,#function
-c_fast_ecc:
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov %l4, %o1
- mov %l5, %o2
- call cheetah_fecc_handler
- add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_irq
- .size c_fast_ecc,.-c_fast_ecc
-
- /* Our caller has disabled I-cache and performed membar Sync. */
- .globl cheetah_cee
- .type cheetah_cee,#function
-cheetah_cee:
- ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
- andn %g2, ESTATE_ERROR_CEEN, %g2
- stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
- membar #Sync
-
- /* Fetch and clear AFSR/AFAR */
- ldxa [%g0] ASI_AFSR, %g4
- ldxa [%g0] ASI_AFAR, %g5
- stxa %g4, [%g0] ASI_AFSR
- membar #Sync
-
- ba,pt %xcc, __cheetah_log_error
- nop
- .size cheetah_cee,.-cheetah_cee
-
- .type c_cee,#function
-c_cee:
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov %l4, %o1
- mov %l5, %o2
- call cheetah_cee_handler
- add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_irq
- .size c_cee,.-c_cee
-
- /* Our caller has disabled I-cache+D-cache and performed membar Sync. */
- .globl cheetah_deferred_trap
- .type cheetah_deferred_trap,#function
-cheetah_deferred_trap:
- ldxa [%g0] ASI_ESTATE_ERROR_EN, %g2
- andn %g2, ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN, %g2
- stxa %g2, [%g0] ASI_ESTATE_ERROR_EN
- membar #Sync
-
- /* Fetch and clear AFSR/AFAR */
- ldxa [%g0] ASI_AFSR, %g4
- ldxa [%g0] ASI_AFAR, %g5
- stxa %g4, [%g0] ASI_AFSR
- membar #Sync
-
- ba,pt %xcc, __cheetah_log_error
- nop
- .size cheetah_deferred_trap,.-cheetah_deferred_trap
-
- .type c_deferred,#function
-c_deferred:
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov %l4, %o1
- mov %l5, %o2
- call cheetah_deferred_handler
- add %sp, PTREGS_OFF, %o0
- ba,a,pt %xcc, rtrap_irq
- .size c_deferred,.-c_deferred
diff --git a/arch/sparc64/kernel/compat_audit.c b/arch/sparc64/kernel/compat_audit.c
deleted file mode 100644
index c831b0a4e660..000000000000
--- a/arch/sparc64/kernel/compat_audit.c
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <asm/unistd_32.h>
-
-unsigned sparc32_dir_class[] = {
-#include <asm-generic/audit_dir_write.h>
-~0U
-};
-
-unsigned sparc32_chattr_class[] = {
-#include <asm-generic/audit_change_attr.h>
-~0U
-};
-
-unsigned sparc32_write_class[] = {
-#include <asm-generic/audit_write.h>
-~0U
-};
-
-unsigned sparc32_read_class[] = {
-#include <asm-generic/audit_read.h>
-~0U
-};
-
-unsigned sparc32_signal_class[] = {
-#include <asm-generic/audit_signal.h>
-~0U
-};
-
-int sparc32_classify_syscall(unsigned syscall)
-{
- switch(syscall) {
- case __NR_open:
- return 2;
- case __NR_openat:
- return 3;
- case __NR_socketcall:
- return 4;
- case __NR_execve:
- return 5;
- default:
- return 1;
- }
-}
diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c
deleted file mode 100644
index 0c9ac83ed0a8..000000000000
--- a/arch/sparc64/kernel/cpu.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/* cpu.c: Dinky routines to look for the kind of Sparc cpu
- * we are on.
- *
- * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net)
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <asm/asi.h>
-#include <asm/system.h>
-#include <asm/fpumacro.h>
-#include <asm/cpudata.h>
-#include <asm/spitfire.h>
-#include <asm/oplib.h>
-
-#include "entry.h"
-
-DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 };
-
-struct cpu_chip_info {
- unsigned short manuf;
- unsigned short impl;
- const char *cpu_name;
- const char *fp_name;
-};
-
-static const struct cpu_chip_info cpu_chips[] = {
- {
- .manuf = 0x17,
- .impl = 0x10,
- .cpu_name = "TI UltraSparc I (SpitFire)",
- .fp_name = "UltraSparc I integrated FPU",
- },
- {
- .manuf = 0x22,
- .impl = 0x10,
- .cpu_name = "TI UltraSparc I (SpitFire)",
- .fp_name = "UltraSparc I integrated FPU",
- },
- {
- .manuf = 0x17,
- .impl = 0x11,
- .cpu_name = "TI UltraSparc II (BlackBird)",
- .fp_name = "UltraSparc II integrated FPU",
- },
- {
- .manuf = 0x17,
- .impl = 0x12,
- .cpu_name = "TI UltraSparc IIi (Sabre)",
- .fp_name = "UltraSparc IIi integrated FPU",
- },
- {
- .manuf = 0x17,
- .impl = 0x13,
- .cpu_name = "TI UltraSparc IIe (Hummingbird)",
- .fp_name = "UltraSparc IIe integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x14,
- .cpu_name = "TI UltraSparc III (Cheetah)",
- .fp_name = "UltraSparc III integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x15,
- .cpu_name = "TI UltraSparc III+ (Cheetah+)",
- .fp_name = "UltraSparc III+ integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x16,
- .cpu_name = "TI UltraSparc IIIi (Jalapeno)",
- .fp_name = "UltraSparc IIIi integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x18,
- .cpu_name = "TI UltraSparc IV (Jaguar)",
- .fp_name = "UltraSparc IV integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x19,
- .cpu_name = "TI UltraSparc IV+ (Panther)",
- .fp_name = "UltraSparc IV+ integrated FPU",
- },
- {
- .manuf = 0x3e,
- .impl = 0x22,
- .cpu_name = "TI UltraSparc IIIi+ (Serrano)",
- .fp_name = "UltraSparc IIIi+ integrated FPU",
- },
-};
-
-#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips)
-
-const char *sparc_cpu_type;
-const char *sparc_fpu_type;
-
-static void __init sun4v_cpu_probe(void)
-{
- switch (sun4v_chip_type) {
- case SUN4V_CHIP_NIAGARA1:
- sparc_cpu_type = "UltraSparc T1 (Niagara)";
- sparc_fpu_type = "UltraSparc T1 integrated FPU";
- break;
-
- case SUN4V_CHIP_NIAGARA2:
- sparc_cpu_type = "UltraSparc T2 (Niagara2)";
- sparc_fpu_type = "UltraSparc T2 integrated FPU";
- break;
-
- default:
- printk(KERN_WARNING "CPU: Unknown sun4v cpu type [%s]\n",
- prom_cpu_compatible);
- sparc_cpu_type = "Unknown SUN4V CPU";
- sparc_fpu_type = "Unknown SUN4V FPU";
- break;
- }
-}
-
-static const struct cpu_chip_info * __init find_cpu_chip(unsigned short manuf,
- unsigned short impl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(cpu_chips); i++) {
- const struct cpu_chip_info *p = &cpu_chips[i];
-
- if (p->manuf == manuf && p->impl == impl)
- return p;
- }
- return NULL;
-}
-
-static int __init cpu_type_probe(void)
-{
- if (tlb_type == hypervisor) {
- sun4v_cpu_probe();
- } else {
- unsigned long ver, manuf, impl;
- const struct cpu_chip_info *p;
-
- __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver));
-
- manuf = ((ver >> 48) & 0xffff);
- impl = ((ver >> 32) & 0xffff);
-
- p = find_cpu_chip(manuf, impl);
- if (p) {
- sparc_cpu_type = p->cpu_name;
- sparc_fpu_type = p->fp_name;
- } else {
- printk(KERN_ERR "CPU: Unknown chip, manuf[%lx] impl[%lx]\n",
- manuf, impl);
- sparc_cpu_type = "Unknown CPU";
- sparc_fpu_type = "Unknown FPU";
- }
- }
- return 0;
-}
-
-arch_initcall(cpu_type_probe);
diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h
deleted file mode 100644
index 34d7ab5e10d2..000000000000
--- a/arch/sparc64/kernel/entry.h
+++ /dev/null
@@ -1,195 +0,0 @@
-#ifndef _ENTRY_H
-#define _ENTRY_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-
-extern const char *sparc_cpu_type;
-extern const char *sparc_fpu_type;
-
-extern void __init per_cpu_patch(void);
-extern void __init sun4v_patch(void);
-extern void __init boot_cpu_id_too_large(int cpu);
-extern unsigned int dcache_parity_tl1_occurred;
-extern unsigned int icache_parity_tl1_occurred;
-
-extern asmlinkage void update_perfctrs(void);
-extern asmlinkage void sparc_breakpoint(struct pt_regs *regs);
-extern void timer_interrupt(int irq, struct pt_regs *regs);
-
-extern void do_notify_resume(struct pt_regs *regs,
- unsigned long orig_i0,
- unsigned long thread_info_flags);
-
-extern asmlinkage int syscall_trace_enter(struct pt_regs *regs);
-extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
-
-extern void bad_trap_tl1(struct pt_regs *regs, long lvl);
-
-extern void do_fpe_common(struct pt_regs *regs);
-extern void do_fpieee(struct pt_regs *regs);
-extern void do_fpother(struct pt_regs *regs);
-extern void do_tof(struct pt_regs *regs);
-extern void do_div0(struct pt_regs *regs);
-extern void do_illegal_instruction(struct pt_regs *regs);
-extern void mem_address_unaligned(struct pt_regs *regs,
- unsigned long sfar,
- unsigned long sfsr);
-extern void sun4v_do_mna(struct pt_regs *regs,
- unsigned long addr,
- unsigned long type_ctx);
-extern void do_privop(struct pt_regs *regs);
-extern void do_privact(struct pt_regs *regs);
-extern void do_cee(struct pt_regs *regs);
-extern void do_cee_tl1(struct pt_regs *regs);
-extern void do_dae_tl1(struct pt_regs *regs);
-extern void do_iae_tl1(struct pt_regs *regs);
-extern void do_div0_tl1(struct pt_regs *regs);
-extern void do_fpdis_tl1(struct pt_regs *regs);
-extern void do_fpieee_tl1(struct pt_regs *regs);
-extern void do_fpother_tl1(struct pt_regs *regs);
-extern void do_ill_tl1(struct pt_regs *regs);
-extern void do_irq_tl1(struct pt_regs *regs);
-extern void do_lddfmna_tl1(struct pt_regs *regs);
-extern void do_stdfmna_tl1(struct pt_regs *regs);
-extern void do_paw(struct pt_regs *regs);
-extern void do_paw_tl1(struct pt_regs *regs);
-extern void do_vaw(struct pt_regs *regs);
-extern void do_vaw_tl1(struct pt_regs *regs);
-extern void do_tof_tl1(struct pt_regs *regs);
-extern void do_getpsr(struct pt_regs *regs);
-
-extern void spitfire_insn_access_exception(struct pt_regs *regs,
- unsigned long sfsr,
- unsigned long sfar);
-extern void spitfire_insn_access_exception_tl1(struct pt_regs *regs,
- unsigned long sfsr,
- unsigned long sfar);
-extern void spitfire_data_access_exception(struct pt_regs *regs,
- unsigned long sfsr,
- unsigned long sfar);
-extern void spitfire_data_access_exception_tl1(struct pt_regs *regs,
- unsigned long sfsr,
- unsigned long sfar);
-extern void spitfire_access_error(struct pt_regs *regs,
- unsigned long status_encoded,
- unsigned long afar);
-
-extern void cheetah_fecc_handler(struct pt_regs *regs,
- unsigned long afsr,
- unsigned long afar);
-extern void cheetah_cee_handler(struct pt_regs *regs,
- unsigned long afsr,
- unsigned long afar);
-extern void cheetah_deferred_handler(struct pt_regs *regs,
- unsigned long afsr,
- unsigned long afar);
-extern void cheetah_plus_parity_error(int type, struct pt_regs *regs);
-
-extern void sun4v_insn_access_exception(struct pt_regs *regs,
- unsigned long addr,
- unsigned long type_ctx);
-extern void sun4v_insn_access_exception_tl1(struct pt_regs *regs,
- unsigned long addr,
- unsigned long type_ctx);
-extern void sun4v_data_access_exception(struct pt_regs *regs,
- unsigned long addr,
- unsigned long type_ctx);
-extern void sun4v_data_access_exception_tl1(struct pt_regs *regs,
- unsigned long addr,
- unsigned long type_ctx);
-extern void sun4v_resum_error(struct pt_regs *regs,
- unsigned long offset);
-extern void sun4v_resum_overflow(struct pt_regs *regs);
-extern void sun4v_nonresum_error(struct pt_regs *regs,
- unsigned long offset);
-extern void sun4v_nonresum_overflow(struct pt_regs *regs);
-
-extern unsigned long sun4v_err_itlb_vaddr;
-extern unsigned long sun4v_err_itlb_ctx;
-extern unsigned long sun4v_err_itlb_pte;
-extern unsigned long sun4v_err_itlb_error;
-
-extern void sun4v_itlb_error_report(struct pt_regs *regs, int tl);
-
-extern unsigned long sun4v_err_dtlb_vaddr;
-extern unsigned long sun4v_err_dtlb_ctx;
-extern unsigned long sun4v_err_dtlb_pte;
-extern unsigned long sun4v_err_dtlb_error;
-
-extern void sun4v_dtlb_error_report(struct pt_regs *regs, int tl);
-extern void hypervisor_tlbop_error(unsigned long err,
- unsigned long op);
-extern void hypervisor_tlbop_error_xcall(unsigned long err,
- unsigned long op);
-
-/* WARNING: The error trap handlers in assembly know the precise
- * layout of the following structure.
- *
- * C-level handlers in traps.c use this information to log the
- * error and then determine how to recover (if possible).
- */
-struct cheetah_err_info {
-/*0x00*/u64 afsr;
-/*0x08*/u64 afar;
-
- /* D-cache state */
-/*0x10*/u64 dcache_data[4]; /* The actual data */
-/*0x30*/u64 dcache_index; /* D-cache index */
-/*0x38*/u64 dcache_tag; /* D-cache tag/valid */
-/*0x40*/u64 dcache_utag; /* D-cache microtag */
-/*0x48*/u64 dcache_stag; /* D-cache snooptag */
-
- /* I-cache state */
-/*0x50*/u64 icache_data[8]; /* The actual insns + predecode */
-/*0x90*/u64 icache_index; /* I-cache index */
-/*0x98*/u64 icache_tag; /* I-cache phys tag */
-/*0xa0*/u64 icache_utag; /* I-cache microtag */
-/*0xa8*/u64 icache_stag; /* I-cache snooptag */
-/*0xb0*/u64 icache_upper; /* I-cache upper-tag */
-/*0xb8*/u64 icache_lower; /* I-cache lower-tag */
-
- /* E-cache state */
-/*0xc0*/u64 ecache_data[4]; /* 32 bytes from staging registers */
-/*0xe0*/u64 ecache_index; /* E-cache index */
-/*0xe8*/u64 ecache_tag; /* E-cache tag/state */
-
-/*0xf0*/u64 __pad[32 - 30];
-};
-#define CHAFSR_INVALID ((u64)-1L)
-
-/* This is allocated at boot time based upon the largest hardware
- * cpu ID in the system. We allocate two entries per cpu, one for
- * TL==0 logging and one for TL >= 1 logging.
- */
-extern struct cheetah_err_info *cheetah_error_log;
-
-/* UPA nodes send interrupt packet to UltraSparc with first data reg
- * value low 5 (7 on Starfire) bits holding the IRQ identifier being
- * delivered. We must translate this into a non-vector IRQ so we can
- * set the softint on this cpu.
- *
- * To make processing these packets efficient and race free we use
- * an array of irq buckets below. The interrupt vector handler in
- * entry.S feeds incoming packets into per-cpu pil-indexed lists.
- *
- * If you make changes to ino_bucket, please update hand coded assembler
- * of the vectored interrupt trap handler(s) in entry.S and sun4v_ivec.S
- */
-struct ino_bucket {
-/*0x00*/unsigned long __irq_chain_pa;
-
- /* Virtual interrupt number assigned to this INO. */
-/*0x08*/unsigned int __virt_irq;
-/*0x0c*/unsigned int __pad;
-};
-
-extern struct ino_bucket *ivector_table;
-extern unsigned long ivector_table_pa;
-
-extern void handler_irq(int irq, struct pt_regs *regs);
-extern void init_irqwork_curcpu(void);
-extern void __cpuinit sun4v_register_mondo_queues(int this_cpu);
-
-#endif /* _ENTRY_H */
diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S
deleted file mode 100644
index 29ce489bc188..000000000000
--- a/arch/sparc64/kernel/etrap.S
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * etrap.S: Preparing for entry into the kernel on Sparc V9.
- *
- * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-
-#include <asm/asi.h>
-#include <asm/pstate.h>
-#include <asm/ptrace.h>
-#include <asm/page.h>
-#include <asm/spitfire.h>
-#include <asm/head.h>
-#include <asm/processor.h>
-#include <asm/mmu.h>
-
-#define TASK_REGOFF (THREAD_SIZE-TRACEREG_SZ-STACKFRAME_SZ)
-#define ETRAP_PSTATE1 (PSTATE_RMO | PSTATE_PRIV)
-#define ETRAP_PSTATE2 \
- (PSTATE_RMO | PSTATE_PEF | PSTATE_PRIV | PSTATE_IE)
-
-/*
- * On entry, %g7 is return address - 0x4.
- * %g4 and %g5 will be preserved %l4 and %l5 respectively.
- */
-
- .text
- .align 64
- .globl etrap_syscall, etrap, etrap_irq, etraptl1
-etrap: rdpr %pil, %g2
-etrap_irq: clr %g3
-etrap_syscall: TRAP_LOAD_THREAD_REG(%g6, %g1)
- rdpr %tstate, %g1
- or %g1, %g3, %g1
- sllx %g2, 20, %g3
- andcc %g1, TSTATE_PRIV, %g0
- or %g1, %g3, %g1
- bne,pn %xcc, 1f
- sub %sp, STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS, %g2
- wrpr %g0, 7, %cleanwin
-
- sethi %hi(TASK_REGOFF), %g2
- sethi %hi(TSTATE_PEF), %g3
- or %g2, %lo(TASK_REGOFF), %g2
- and %g1, %g3, %g3
- brnz,pn %g3, 1f
- add %g6, %g2, %g2
- wr %g0, 0, %fprs
-1: rdpr %tpc, %g3
-
- stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE]
- rdpr %tnpc, %g1
- stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC]
- rd %y, %g3
- stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC]
- rdpr %tt, %g1
- st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y]
- sethi %hi(PT_REGS_MAGIC), %g3
- or %g3, %g1, %g1
- st %g1, [%g2 + STACKFRAME_SZ + PT_V9_MAGIC]
-
- rdpr %cansave, %g1
- brnz,pt %g1, etrap_save
- nop
-
- rdpr %cwp, %g1
- add %g1, 2, %g1
- wrpr %g1, %cwp
- be,pt %xcc, etrap_user_spill
- mov ASI_AIUP, %g3
-
- rdpr %otherwin, %g3
- brz %g3, etrap_kernel_spill
- mov ASI_AIUS, %g3
-
-etrap_user_spill:
-
- wr %g3, 0x0, %asi
- ldx [%g6 + TI_FLAGS], %g3
- and %g3, _TIF_32BIT, %g3
- brnz,pt %g3, etrap_user_spill_32bit
- nop
- ba,a,pt %xcc, etrap_user_spill_64bit
-
-etrap_save: save %g2, -STACK_BIAS, %sp
- mov %g6, %l6
-
- bne,pn %xcc, 3f
- mov PRIMARY_CONTEXT, %l4
- rdpr %canrestore, %g3
- rdpr %wstate, %g2
- wrpr %g0, 0, %canrestore
- sll %g2, 3, %g2
- mov 1, %l5
- stb %l5, [%l6 + TI_FPDEPTH]
-
- wrpr %g3, 0, %otherwin
- wrpr %g2, 0, %wstate
- sethi %hi(sparc64_kern_pri_context), %g2
- ldx [%g2 + %lo(sparc64_kern_pri_context)], %g3
-
-661: stxa %g3, [%l4] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g3, [%l4] ASI_MMU
- .previous
-
- sethi %hi(KERNBASE), %l4
- flush %l4
- mov ASI_AIUS, %l7
-2: mov %g4, %l4
- mov %g5, %l5
- add %g7, 4, %l2
-
- /* Go to trap time globals so we can save them. */
-661: wrpr %g0, ETRAP_PSTATE1, %pstate
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- SET_GL(0)
- .previous
-
- stx %g1, [%sp + PTREGS_OFF + PT_V9_G1]
- stx %g2, [%sp + PTREGS_OFF + PT_V9_G2]
- sllx %l7, 24, %l7
- stx %g3, [%sp + PTREGS_OFF + PT_V9_G3]
- rdpr %cwp, %l0
- stx %g4, [%sp + PTREGS_OFF + PT_V9_G4]
- stx %g5, [%sp + PTREGS_OFF + PT_V9_G5]
- stx %g6, [%sp + PTREGS_OFF + PT_V9_G6]
- stx %g7, [%sp + PTREGS_OFF + PT_V9_G7]
- or %l7, %l0, %l7
- sethi %hi(TSTATE_RMO | TSTATE_PEF), %l0
- or %l7, %l0, %l7
- wrpr %l2, %tnpc
- wrpr %l7, (TSTATE_PRIV | TSTATE_IE), %tstate
- stx %i0, [%sp + PTREGS_OFF + PT_V9_I0]
- stx %i1, [%sp + PTREGS_OFF + PT_V9_I1]
- stx %i2, [%sp + PTREGS_OFF + PT_V9_I2]
- stx %i3, [%sp + PTREGS_OFF + PT_V9_I3]
- stx %i4, [%sp + PTREGS_OFF + PT_V9_I4]
- stx %i5, [%sp + PTREGS_OFF + PT_V9_I5]
- stx %i6, [%sp + PTREGS_OFF + PT_V9_I6]
- mov %l6, %g6
- stx %i7, [%sp + PTREGS_OFF + PT_V9_I7]
- LOAD_PER_CPU_BASE(%g5, %g6, %g4, %g3, %l1)
- ldx [%g6 + TI_TASK], %g4
- done
-
-3: mov ASI_P, %l7
- ldub [%l6 + TI_FPDEPTH], %l5
- add %l6, TI_FPSAVED + 1, %l4
- srl %l5, 1, %l3
- add %l5, 2, %l5
- stb %l5, [%l6 + TI_FPDEPTH]
- ba,pt %xcc, 2b
- stb %g0, [%l4 + %l3]
- nop
-
-etraptl1: /* Save tstate/tpc/tnpc of TL 1-->4 and the tl register itself.
- * We place this right after pt_regs on the trap stack.
- * The layout is:
- * 0x00 TL1's TSTATE
- * 0x08 TL1's TPC
- * 0x10 TL1's TNPC
- * 0x18 TL1's TT
- * ...
- * 0x58 TL4's TT
- * 0x60 TL
- */
- TRAP_LOAD_THREAD_REG(%g6, %g1)
- sub %sp, ((4 * 8) * 4) + 8, %g2
- rdpr %tl, %g1
-
- wrpr %g0, 1, %tl
- rdpr %tstate, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x00]
- rdpr %tpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x08]
- rdpr %tnpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x10]
- rdpr %tt, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x18]
-
- wrpr %g0, 2, %tl
- rdpr %tstate, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x20]
- rdpr %tpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x28]
- rdpr %tnpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x30]
- rdpr %tt, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x38]
-
- sethi %hi(is_sun4v), %g3
- lduw [%g3 + %lo(is_sun4v)], %g3
- brnz,pn %g3, finish_tl1_capture
- nop
-
- wrpr %g0, 3, %tl
- rdpr %tstate, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x40]
- rdpr %tpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x48]
- rdpr %tnpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x50]
- rdpr %tt, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x58]
-
- wrpr %g0, 4, %tl
- rdpr %tstate, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x60]
- rdpr %tpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x68]
- rdpr %tnpc, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x70]
- rdpr %tt, %g3
- stx %g3, [%g2 + STACK_BIAS + 0x78]
-
- stx %g1, [%g2 + STACK_BIAS + 0x80]
-
-finish_tl1_capture:
- wrpr %g0, 1, %tl
-661: nop
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- SET_GL(1)
- .previous
-
- rdpr %tstate, %g1
- sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2
- ba,pt %xcc, 1b
- andcc %g1, TSTATE_PRIV, %g0
-
-#undef TASK_REGOFF
-#undef ETRAP_PSTATE1
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
deleted file mode 100644
index 353226fa0239..000000000000
--- a/arch/sparc64/kernel/head.S
+++ /dev/null
@@ -1,900 +0,0 @@
-/* head.S: Initial boot code for the Sparc64 port of Linux.
- *
- * Copyright (C) 1996, 1997, 2007 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 David Sitsky (David.Sitsky@anu.edu.au)
- * Copyright (C) 1997, 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
- */
-
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/threads.h>
-#include <linux/init.h>
-#include <linux/linkage.h>
-#include <asm/thread_info.h>
-#include <asm/asi.h>
-#include <asm/pstate.h>
-#include <asm/ptrace.h>
-#include <asm/spitfire.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/errno.h>
-#include <asm/signal.h>
-#include <asm/processor.h>
-#include <asm/lsu.h>
-#include <asm/dcr.h>
-#include <asm/dcu.h>
-#include <asm/head.h>
-#include <asm/ttable.h>
-#include <asm/mmu.h>
-#include <asm/cpudata.h>
-#include <asm/pil.h>
-#include <asm/estate.h>
-#include <asm/sfafsr.h>
-#include <asm/unistd.h>
-
-/* This section from from _start to sparc64_boot_end should fit into
- * 0x0000000000404000 to 0x0000000000408000.
- */
- .text
- .globl start, _start, stext, _stext
-_start:
-start:
-_stext:
-stext:
-! 0x0000000000404000
- b sparc64_boot
- flushw /* Flush register file. */
-
-/* This stuff has to be in sync with SILO and other potential boot loaders
- * Fields should be kept upward compatible and whenever any change is made,
- * HdrS version should be incremented.
- */
- .global root_flags, ram_flags, root_dev
- .global sparc_ramdisk_image, sparc_ramdisk_size
- .global sparc_ramdisk_image64
-
- .ascii "HdrS"
- .word LINUX_VERSION_CODE
-
- /* History:
- *
- * 0x0300 : Supports being located at other than 0x4000
- * 0x0202 : Supports kernel params string
- * 0x0201 : Supports reboot_command
- */
- .half 0x0301 /* HdrS version */
-
-root_flags:
- .half 1
-root_dev:
- .half 0
-ram_flags:
- .half 0
-sparc_ramdisk_image:
- .word 0
-sparc_ramdisk_size:
- .word 0
- .xword reboot_command
- .xword bootstr_info
-sparc_ramdisk_image64:
- .xword 0
- .word _end
-
- /* PROM cif handler code address is in %o4. */
-sparc64_boot:
- mov %o4, %l7
-
- /* We need to remap the kernel. Use position independant
- * code to remap us to KERNBASE.
- *
- * SILO can invoke us with 32-bit address masking enabled,
- * so make sure that's clear.
- */
- rdpr %pstate, %g1
- andn %g1, PSTATE_AM, %g1
- wrpr %g1, 0x0, %pstate
- ba,a,pt %xcc, 1f
-
- .globl prom_finddev_name, prom_chosen_path, prom_root_node
- .globl prom_getprop_name, prom_mmu_name, prom_peer_name
- .globl prom_callmethod_name, prom_translate_name, prom_root_compatible
- .globl prom_map_name, prom_unmap_name, prom_mmu_ihandle_cache
- .globl prom_boot_mapped_pc, prom_boot_mapping_mode
- .globl prom_boot_mapping_phys_high, prom_boot_mapping_phys_low
- .globl prom_compatible_name, prom_cpu_path, prom_cpu_compatible
- .globl is_sun4v, sun4v_chip_type, prom_set_trap_table_name
-prom_peer_name:
- .asciz "peer"
-prom_compatible_name:
- .asciz "compatible"
-prom_finddev_name:
- .asciz "finddevice"
-prom_chosen_path:
- .asciz "/chosen"
-prom_cpu_path:
- .asciz "/cpu"
-prom_getprop_name:
- .asciz "getprop"
-prom_mmu_name:
- .asciz "mmu"
-prom_callmethod_name:
- .asciz "call-method"
-prom_translate_name:
- .asciz "translate"
-prom_map_name:
- .asciz "map"
-prom_unmap_name:
- .asciz "unmap"
-prom_set_trap_table_name:
- .asciz "SUNW,set-trap-table"
-prom_sun4v_name:
- .asciz "sun4v"
-prom_niagara_prefix:
- .asciz "SUNW,UltraSPARC-T"
- .align 4
-prom_root_compatible:
- .skip 64
-prom_cpu_compatible:
- .skip 64
-prom_root_node:
- .word 0
-prom_mmu_ihandle_cache:
- .word 0
-prom_boot_mapped_pc:
- .word 0
-prom_boot_mapping_mode:
- .word 0
- .align 8
-prom_boot_mapping_phys_high:
- .xword 0
-prom_boot_mapping_phys_low:
- .xword 0
-is_sun4v:
- .word 0
-sun4v_chip_type:
- .word SUN4V_CHIP_INVALID
-1:
- rd %pc, %l0
-
- mov (1b - prom_peer_name), %l1
- sub %l0, %l1, %l1
- mov 0, %l2
-
- /* prom_root_node = prom_peer(0) */
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "peer"
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, 0
- stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- ldx [%sp + 2047 + 128 + 0x20], %l4 ! prom root node
- mov (1b - prom_root_node), %l1
- sub %l0, %l1, %l1
- stw %l4, [%l1]
-
- mov (1b - prom_getprop_name), %l1
- mov (1b - prom_compatible_name), %l2
- mov (1b - prom_root_compatible), %l5
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- sub %l0, %l5, %l5
-
- /* prom_getproperty(prom_root_node, "compatible",
- * &prom_root_compatible, 64)
- */
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
- mov 4, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, prom_root_node
- stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "compatible"
- stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_root_compatible
- mov 64, %l3
- stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, size
- stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- mov (1b - prom_finddev_name), %l1
- mov (1b - prom_chosen_path), %l2
- mov (1b - prom_boot_mapped_pc), %l3
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- sub %l0, %l3, %l3
- stw %l0, [%l3]
- sub %sp, (192 + 128), %sp
-
- /* chosen_node = prom_finddevice("/chosen") */
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "finddevice"
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, "/chosen"
- stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- ldx [%sp + 2047 + 128 + 0x20], %l4 ! chosen device node
-
- mov (1b - prom_getprop_name), %l1
- mov (1b - prom_mmu_name), %l2
- mov (1b - prom_mmu_ihandle_cache), %l5
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- sub %l0, %l5, %l5
-
- /* prom_mmu_ihandle_cache = prom_getint(chosen_node, "mmu") */
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
- mov 4, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, chosen_node
- stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "mmu"
- stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_mmu_ihandle_cache
- mov 4, %l3
- stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, sizeof(arg3)
- stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- mov (1b - prom_callmethod_name), %l1
- mov (1b - prom_translate_name), %l2
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- lduw [%l5], %l5 ! prom_mmu_ihandle_cache
-
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "call-method"
- mov 3, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 3
- mov 5, %l3
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 5
- stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1: "translate"
- stx %l5, [%sp + 2047 + 128 + 0x20] ! arg2: prom_mmu_ihandle_cache
- /* PAGE align */
- srlx %l0, 13, %l3
- sllx %l3, 13, %l3
- stx %l3, [%sp + 2047 + 128 + 0x28] ! arg3: vaddr, our PC
- stx %g0, [%sp + 2047 + 128 + 0x30] ! res1
- stx %g0, [%sp + 2047 + 128 + 0x38] ! res2
- stx %g0, [%sp + 2047 + 128 + 0x40] ! res3
- stx %g0, [%sp + 2047 + 128 + 0x48] ! res4
- stx %g0, [%sp + 2047 + 128 + 0x50] ! res5
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- ldx [%sp + 2047 + 128 + 0x40], %l1 ! translation mode
- mov (1b - prom_boot_mapping_mode), %l4
- sub %l0, %l4, %l4
- stw %l1, [%l4]
- mov (1b - prom_boot_mapping_phys_high), %l4
- sub %l0, %l4, %l4
- ldx [%sp + 2047 + 128 + 0x48], %l2 ! physaddr high
- stx %l2, [%l4 + 0x0]
- ldx [%sp + 2047 + 128 + 0x50], %l3 ! physaddr low
- /* 4MB align */
- srlx %l3, 22, %l3
- sllx %l3, 22, %l3
- stx %l3, [%l4 + 0x8]
-
- /* Leave service as-is, "call-method" */
- mov 7, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 7
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- mov (1b - prom_map_name), %l3
- sub %l0, %l3, %l3
- stx %l3, [%sp + 2047 + 128 + 0x18] ! arg1: "map"
- /* Leave arg2 as-is, prom_mmu_ihandle_cache */
- mov -1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x28] ! arg3: mode (-1 default)
- /* 4MB align the kernel image size. */
- set (_end - KERNBASE), %l3
- set ((4 * 1024 * 1024) - 1), %l4
- add %l3, %l4, %l3
- andn %l3, %l4, %l3
- stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4: roundup(ksize, 4MB)
- sethi %hi(KERNBASE), %l3
- stx %l3, [%sp + 2047 + 128 + 0x38] ! arg5: vaddr (KERNBASE)
- stx %g0, [%sp + 2047 + 128 + 0x40] ! arg6: empty
- mov (1b - prom_boot_mapping_phys_low), %l3
- sub %l0, %l3, %l3
- ldx [%l3], %l3
- stx %l3, [%sp + 2047 + 128 + 0x48] ! arg7: phys addr
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- add %sp, (192 + 128), %sp
-
- sethi %hi(prom_root_compatible), %g1
- or %g1, %lo(prom_root_compatible), %g1
- sethi %hi(prom_sun4v_name), %g7
- or %g7, %lo(prom_sun4v_name), %g7
- mov 5, %g3
-90: ldub [%g7], %g2
- ldub [%g1], %g4
- cmp %g2, %g4
- bne,pn %icc, 80f
- add %g7, 1, %g7
- subcc %g3, 1, %g3
- bne,pt %xcc, 90b
- add %g1, 1, %g1
-
- sethi %hi(is_sun4v), %g1
- or %g1, %lo(is_sun4v), %g1
- mov 1, %g7
- stw %g7, [%g1]
-
- /* cpu_node = prom_finddevice("/cpu") */
- mov (1b - prom_finddev_name), %l1
- mov (1b - prom_cpu_path), %l2
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- sub %sp, (192 + 128), %sp
-
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "finddevice"
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 1
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l2, [%sp + 2047 + 128 + 0x18] ! arg1, "/cpu"
- stx %g0, [%sp + 2047 + 128 + 0x20] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- ldx [%sp + 2047 + 128 + 0x20], %l4 ! cpu device node
-
- mov (1b - prom_getprop_name), %l1
- mov (1b - prom_compatible_name), %l2
- mov (1b - prom_cpu_compatible), %l5
- sub %l0, %l1, %l1
- sub %l0, %l2, %l2
- sub %l0, %l5, %l5
-
- /* prom_getproperty(cpu_node, "compatible",
- * &prom_cpu_compatible, 64)
- */
- stx %l1, [%sp + 2047 + 128 + 0x00] ! service, "getprop"
- mov 4, %l3
- stx %l3, [%sp + 2047 + 128 + 0x08] ! num_args, 4
- mov 1, %l3
- stx %l3, [%sp + 2047 + 128 + 0x10] ! num_rets, 1
- stx %l4, [%sp + 2047 + 128 + 0x18] ! arg1, cpu_node
- stx %l2, [%sp + 2047 + 128 + 0x20] ! arg2, "compatible"
- stx %l5, [%sp + 2047 + 128 + 0x28] ! arg3, &prom_cpu_compatible
- mov 64, %l3
- stx %l3, [%sp + 2047 + 128 + 0x30] ! arg4, size
- stx %g0, [%sp + 2047 + 128 + 0x38] ! ret1
- call %l7
- add %sp, (2047 + 128), %o0 ! argument array
-
- add %sp, (192 + 128), %sp
-
- sethi %hi(prom_cpu_compatible), %g1
- or %g1, %lo(prom_cpu_compatible), %g1
- sethi %hi(prom_niagara_prefix), %g7
- or %g7, %lo(prom_niagara_prefix), %g7
- mov 17, %g3
-90: ldub [%g7], %g2
- ldub [%g1], %g4
- cmp %g2, %g4
- bne,pn %icc, 4f
- add %g7, 1, %g7
- subcc %g3, 1, %g3
- bne,pt %xcc, 90b
- add %g1, 1, %g1
-
- sethi %hi(prom_cpu_compatible), %g1
- or %g1, %lo(prom_cpu_compatible), %g1
- ldub [%g1 + 17], %g2
- cmp %g2, '1'
- be,pt %xcc, 5f
- mov SUN4V_CHIP_NIAGARA1, %g4
- cmp %g2, '2'
- be,pt %xcc, 5f
- mov SUN4V_CHIP_NIAGARA2, %g4
-4:
- mov SUN4V_CHIP_UNKNOWN, %g4
-5: sethi %hi(sun4v_chip_type), %g2
- or %g2, %lo(sun4v_chip_type), %g2
- stw %g4, [%g2]
-
-80:
- BRANCH_IF_SUN4V(g1, jump_to_sun4u_init)
- BRANCH_IF_CHEETAH_BASE(g1,g7,cheetah_boot)
- BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,cheetah_plus_boot)
- ba,pt %xcc, spitfire_boot
- nop
-
-cheetah_plus_boot:
- /* Preserve OBP chosen DCU and DCR register settings. */
- ba,pt %xcc, cheetah_generic_boot
- nop
-
-cheetah_boot:
- mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
- wr %g1, %asr18
-
- sethi %uhi(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
- or %g7, %ulo(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g7
- sllx %g7, 32, %g7
- or %g7, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g7
- stxa %g7, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
-
-cheetah_generic_boot:
- mov TSB_EXTENSION_P, %g3
- stxa %g0, [%g3] ASI_DMMU
- stxa %g0, [%g3] ASI_IMMU
- membar #Sync
-
- mov TSB_EXTENSION_S, %g3
- stxa %g0, [%g3] ASI_DMMU
- membar #Sync
-
- mov TSB_EXTENSION_N, %g3
- stxa %g0, [%g3] ASI_DMMU
- stxa %g0, [%g3] ASI_IMMU
- membar #Sync
-
- ba,a,pt %xcc, jump_to_sun4u_init
-
-spitfire_boot:
- /* Typically PROM has already enabled both MMU's and both on-chip
- * caches, but we do it here anyway just to be paranoid.
- */
- mov (LSU_CONTROL_IC|LSU_CONTROL_DC|LSU_CONTROL_IM|LSU_CONTROL_DM), %g1
- stxa %g1, [%g0] ASI_LSU_CONTROL
- membar #Sync
-
-jump_to_sun4u_init:
- /*
- * Make sure we are in privileged mode, have address masking,
- * using the ordinary globals and have enabled floating
- * point.
- *
- * Again, typically PROM has left %pil at 13 or similar, and
- * (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate.
- */
- wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
- wr %g0, 0, %fprs
-
- set sun4u_init, %g2
- jmpl %g2 + %g0, %g0
- nop
-
- .section .text.init.refok
-sun4u_init:
- BRANCH_IF_SUN4V(g1, sun4v_init)
-
- /* Set ctx 0 */
- mov PRIMARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_DMMU
- membar #Sync
-
- mov SECONDARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_DMMU
- membar #Sync
-
- ba,pt %xcc, sun4u_continue
- nop
-
-sun4v_init:
- /* Set ctx 0 */
- mov PRIMARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_MMU
- membar #Sync
-
- mov SECONDARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_MMU
- membar #Sync
- ba,pt %xcc, niagara_tlb_fixup
- nop
-
-sun4u_continue:
- BRANCH_IF_ANY_CHEETAH(g1, g7, cheetah_tlb_fixup)
-
- ba,pt %xcc, spitfire_tlb_fixup
- nop
-
-niagara_tlb_fixup:
- mov 3, %g2 /* Set TLB type to hypervisor. */
- sethi %hi(tlb_type), %g1
- stw %g2, [%g1 + %lo(tlb_type)]
-
- /* Patch copy/clear ops. */
- sethi %hi(sun4v_chip_type), %g1
- lduw [%g1 + %lo(sun4v_chip_type)], %g1
- cmp %g1, SUN4V_CHIP_NIAGARA1
- be,pt %xcc, niagara_patch
- cmp %g1, SUN4V_CHIP_NIAGARA2
- be,pt %xcc, niagara2_patch
- nop
-
- call generic_patch_copyops
- nop
- call generic_patch_bzero
- nop
- call generic_patch_pageops
- nop
-
- ba,a,pt %xcc, 80f
-niagara2_patch:
- call niagara2_patch_copyops
- nop
- call niagara_patch_bzero
- nop
- call niagara2_patch_pageops
- nop
-
- ba,a,pt %xcc, 80f
-
-niagara_patch:
- call niagara_patch_copyops
- nop
- call niagara_patch_bzero
- nop
- call niagara_patch_pageops
- nop
-
-80:
- /* Patch TLB/cache ops. */
- call hypervisor_patch_cachetlbops
- nop
-
- ba,pt %xcc, tlb_fixup_done
- nop
-
-cheetah_tlb_fixup:
- mov 2, %g2 /* Set TLB type to cheetah+. */
- BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,1f)
-
- mov 1, %g2 /* Set TLB type to cheetah. */
-
-1: sethi %hi(tlb_type), %g1
- stw %g2, [%g1 + %lo(tlb_type)]
-
- /* Patch copy/page operations to cheetah optimized versions. */
- call cheetah_patch_copyops
- nop
- call cheetah_patch_copy_page
- nop
- call cheetah_patch_cachetlbops
- nop
-
- ba,pt %xcc, tlb_fixup_done
- nop
-
-spitfire_tlb_fixup:
- /* Set TLB type to spitfire. */
- mov 0, %g2
- sethi %hi(tlb_type), %g1
- stw %g2, [%g1 + %lo(tlb_type)]
-
-tlb_fixup_done:
- sethi %hi(init_thread_union), %g6
- or %g6, %lo(init_thread_union), %g6
- ldx [%g6 + TI_TASK], %g4
- mov %sp, %l6
-
- wr %g0, ASI_P, %asi
- mov 1, %g1
- sllx %g1, THREAD_SHIFT, %g1
- sub %g1, (STACKFRAME_SZ + STACK_BIAS), %g1
- add %g6, %g1, %sp
- mov 0, %fp
-
- /* Set per-cpu pointer initially to zero, this makes
- * the boot-cpu use the in-kernel-image per-cpu areas
- * before setup_per_cpu_area() is invoked.
- */
- clr %g5
-
- wrpr %g0, 0, %wstate
- wrpr %g0, 0x0, %tl
-
- /* Clear the bss */
- sethi %hi(__bss_start), %o0
- or %o0, %lo(__bss_start), %o0
- sethi %hi(_end), %o1
- or %o1, %lo(_end), %o1
- call __bzero
- sub %o1, %o0, %o1
-
-#ifdef CONFIG_LOCKDEP
- /* We have this call this super early, as even prom_init can grab
- * spinlocks and thus call into the lockdep code.
- */
- call lockdep_init
- nop
-#endif
-
- mov %l6, %o1 ! OpenPROM stack
- call prom_init
- mov %l7, %o0 ! OpenPROM cif handler
-
- /* Initialize current_thread_info()->cpu as early as possible.
- * In order to do that accurately we have to patch up the get_cpuid()
- * assembler sequences. And that, in turn, requires that we know
- * if we are on a Starfire box or not. While we're here, patch up
- * the sun4v sequences as well.
- */
- call check_if_starfire
- nop
- call per_cpu_patch
- nop
- call sun4v_patch
- nop
-
-#ifdef CONFIG_SMP
- call hard_smp_processor_id
- nop
- cmp %o0, NR_CPUS
- blu,pt %xcc, 1f
- nop
- call boot_cpu_id_too_large
- nop
- /* Not reached... */
-
-1:
- /* If we boot on a non-zero cpu, all of the per-cpu
- * variable references we make before setting up the
- * per-cpu areas will use a bogus offset. Put a
- * compensating factor into __per_cpu_base to handle
- * this cleanly.
- *
- * What the per-cpu code calculates is:
- *
- * __per_cpu_base + (cpu << __per_cpu_shift)
- *
- * These two variables are zero initially, so to
- * make it all cancel out to zero we need to put
- * "0 - (cpu << 0)" into __per_cpu_base so that the
- * above formula evaluates to zero.
- *
- * We cannot even perform a printk() until this stuff
- * is setup as that calls cpu_clock() which uses
- * per-cpu variables.
- */
- sub %g0, %o0, %o1
- sethi %hi(__per_cpu_base), %o2
- stx %o1, [%o2 + %lo(__per_cpu_base)]
-#else
- mov 0, %o0
-#endif
- sth %o0, [%g6 + TI_CPU]
-
- call prom_init_report
- nop
-
- /* Off we go.... */
- call start_kernel
- nop
- /* Not reached... */
-
- .previous
-
- /* This is meant to allow the sharing of this code between
- * boot processor invocation (via setup_tba() below) and
- * secondary processor startup (via trampoline.S). The
- * former does use this code, the latter does not yet due
- * to some complexities. That should be fixed up at some
- * point.
- *
- * There used to be enormous complexity wrt. transferring
- * over from the firwmare's trap table to the Linux kernel's.
- * For example, there was a chicken & egg problem wrt. building
- * the OBP page tables, yet needing to be on the Linux kernel
- * trap table (to translate PAGE_OFFSET addresses) in order to
- * do that.
- *
- * We now handle OBP tlb misses differently, via linear lookups
- * into the prom_trans[] array. So that specific problem no
- * longer exists. Yet, unfortunately there are still some issues
- * preventing trampoline.S from using this code... ho hum.
- */
- .globl setup_trap_table
-setup_trap_table:
- save %sp, -192, %sp
-
- /* Force interrupts to be disabled. */
- rdpr %pstate, %l0
- andn %l0, PSTATE_IE, %o1
- wrpr %o1, 0x0, %pstate
- rdpr %pil, %l1
- wrpr %g0, 15, %pil
-
- /* Make the firmware call to jump over to the Linux trap table. */
- sethi %hi(is_sun4v), %o0
- lduw [%o0 + %lo(is_sun4v)], %o0
- brz,pt %o0, 1f
- nop
-
- TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
- add %g2, TRAP_PER_CPU_FAULT_INFO, %g2
- stxa %g2, [%g0] ASI_SCRATCHPAD
-
- /* Compute physical address:
- *
- * paddr = kern_base + (mmfsa_vaddr - KERNBASE)
- */
- sethi %hi(KERNBASE), %g3
- sub %g2, %g3, %g2
- sethi %hi(kern_base), %g3
- ldx [%g3 + %lo(kern_base)], %g3
- add %g2, %g3, %o1
- sethi %hi(sparc64_ttable_tl0), %o0
-
- set prom_set_trap_table_name, %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 2, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 0, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- stx %o0, [%sp + 2047 + 128 + 0x18]
- stx %o1, [%sp + 2047 + 128 + 0x20]
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
- ba,pt %xcc, 2f
- nop
-
-1: sethi %hi(sparc64_ttable_tl0), %o0
- set prom_set_trap_table_name, %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 0, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- stx %o0, [%sp + 2047 + 128 + 0x18]
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
- /* Start using proper page size encodings in ctx register. */
-2: sethi %hi(sparc64_kern_pri_context), %g3
- ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
-
- mov PRIMARY_CONTEXT, %g1
-
-661: stxa %g2, [%g1] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g2, [%g1] ASI_MMU
- .previous
-
- membar #Sync
-
- BRANCH_IF_SUN4V(o2, 1f)
-
- /* Kill PROM timer */
- sethi %hi(0x80000000), %o2
- sllx %o2, 32, %o2
- wr %o2, 0, %tick_cmpr
-
- BRANCH_IF_ANY_CHEETAH(o2, o3, 1f)
-
- ba,pt %xcc, 2f
- nop
-
- /* Disable STICK_INT interrupts. */
-1:
- sethi %hi(0x80000000), %o2
- sllx %o2, 32, %o2
- wr %o2, %asr25
-
-2:
- wrpr %g0, %g0, %wstate
-
- call init_irqwork_curcpu
- nop
-
- /* Now we can restore interrupt state. */
- wrpr %l0, 0, %pstate
- wrpr %l1, 0x0, %pil
-
- ret
- restore
-
- .globl setup_tba
-setup_tba:
- save %sp, -192, %sp
-
- /* The boot processor is the only cpu which invokes this
- * routine, the other cpus set things up via trampoline.S.
- * So save the OBP trap table address here.
- */
- rdpr %tba, %g7
- sethi %hi(prom_tba), %o1
- or %o1, %lo(prom_tba), %o1
- stx %g7, [%o1]
-
- call setup_trap_table
- nop
-
- ret
- restore
-sparc64_boot_end:
-
-#include "etrap.S"
-#include "rtrap.S"
-#include "winfixup.S"
-#include "fpu_traps.S"
-#include "ivec.S"
-#include "getsetcc.S"
-#include "utrap.S"
-#include "spiterrs.S"
-#include "cherrs.S"
-#include "misctrap.S"
-#include "syscalls.S"
-#include "helpers.S"
-#include "hvcalls.S"
-#include "sun4v_tlb_miss.S"
-#include "sun4v_ivec.S"
-#include "ktlb.S"
-#include "tsb.S"
-
-/*
- * The following skip makes sure the trap table in ttable.S is aligned
- * on a 32K boundary as required by the v9 specs for TBA register.
- *
- * We align to a 32K boundary, then we have the 32K kernel TSB,
- * the 64K kernel 4MB TSB, and then the 32K aligned trap table.
- */
-1:
- .skip 0x4000 + _start - 1b
-
-! 0x0000000000408000
-
- .globl swapper_tsb
-swapper_tsb:
- .skip (32 * 1024)
-
- .globl swapper_4m_tsb
-swapper_4m_tsb:
- .skip (64 * 1024)
-
-! 0x0000000000420000
-
- /* Some care needs to be exercised if you try to move the
- * location of the trap table relative to other things. For
- * one thing there are br* instructions in some of the
- * trap table entires which branch back to code in ktlb.S
- * Those instructions can only handle a signed 16-bit
- * displacement.
- *
- * There is a binutils bug (bugzilla #4558) which causes
- * the relocation overflow checks for such instructions to
- * not be done correctly. So bintuils will not notice the
- * error and will instead write junk into the relocation and
- * you'll have an unbootable kernel.
- */
-#include "ttable.S"
-
-! 0x0000000000428000
-
-#include "systbls.S"
-
- .data
- .align 8
- .globl prom_tba, tlb_type
-prom_tba: .xword 0
-tlb_type: .word 0 /* Must NOT end up in BSS */
- .section ".fixup",#alloc,#execinstr
-
- .globl __ret_efault, __retl_efault
-__ret_efault:
- ret
- restore %g0, -EFAULT, %o0
-__retl_efault:
- retl
- mov -EFAULT, %o0
diff --git a/arch/sparc64/kernel/hvcalls.S b/arch/sparc64/kernel/hvcalls.S
deleted file mode 100644
index e066269d1594..000000000000
--- a/arch/sparc64/kernel/hvcalls.S
+++ /dev/null
@@ -1,768 +0,0 @@
- /* %o0: devhandle
- * %o1: devino
- *
- * returns %o0: sysino
- */
-ENTRY(sun4v_devino_to_sysino)
- mov HV_FAST_INTR_DEVINO2SYSINO, %o5
- ta HV_FAST_TRAP
- retl
- mov %o1, %o0
-ENDPROC(sun4v_devino_to_sysino)
-
- /* %o0: sysino
- *
- * returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED})
- */
-ENTRY(sun4v_intr_getenabled)
- mov HV_FAST_INTR_GETENABLED, %o5
- ta HV_FAST_TRAP
- retl
- mov %o1, %o0
-ENDPROC(sun4v_intr_getenabled)
-
- /* %o0: sysino
- * %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED})
- */
-ENTRY(sun4v_intr_setenabled)
- mov HV_FAST_INTR_SETENABLED, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_intr_setenabled)
-
- /* %o0: sysino
- *
- * returns %o0: intr_state (HV_INTR_STATE_*)
- */
-ENTRY(sun4v_intr_getstate)
- mov HV_FAST_INTR_GETSTATE, %o5
- ta HV_FAST_TRAP
- retl
- mov %o1, %o0
-ENDPROC(sun4v_intr_getstate)
-
- /* %o0: sysino
- * %o1: intr_state (HV_INTR_STATE_*)
- */
-ENTRY(sun4v_intr_setstate)
- mov HV_FAST_INTR_SETSTATE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_intr_setstate)
-
- /* %o0: sysino
- *
- * returns %o0: cpuid
- */
-ENTRY(sun4v_intr_gettarget)
- mov HV_FAST_INTR_GETTARGET, %o5
- ta HV_FAST_TRAP
- retl
- mov %o1, %o0
-ENDPROC(sun4v_intr_gettarget)
-
- /* %o0: sysino
- * %o1: cpuid
- */
-ENTRY(sun4v_intr_settarget)
- mov HV_FAST_INTR_SETTARGET, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_intr_settarget)
-
- /* %o0: cpuid
- * %o1: pc
- * %o2: rtba
- * %o3: arg0
- *
- * returns %o0: status
- */
-ENTRY(sun4v_cpu_start)
- mov HV_FAST_CPU_START, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_cpu_start)
-
- /* %o0: cpuid
- *
- * returns %o0: status
- */
-ENTRY(sun4v_cpu_stop)
- mov HV_FAST_CPU_STOP, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_cpu_stop)
-
- /* returns %o0: status */
-ENTRY(sun4v_cpu_yield)
- mov HV_FAST_CPU_YIELD, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_cpu_yield)
-
- /* %o0: type
- * %o1: queue paddr
- * %o2: num queue entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_cpu_qconf)
- mov HV_FAST_CPU_QCONF, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_cpu_qconf)
-
- /* %o0: num cpus in cpu list
- * %o1: cpu list paddr
- * %o2: mondo block paddr
- *
- * returns %o0: status
- */
-ENTRY(sun4v_cpu_mondo_send)
- mov HV_FAST_CPU_MONDO_SEND, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_cpu_mondo_send)
-
- /* %o0: CPU ID
- *
- * returns %o0: -status if status non-zero, else
- * %o0: cpu state as HV_CPU_STATE_*
- */
-ENTRY(sun4v_cpu_state)
- mov HV_FAST_CPU_STATE, %o5
- ta HV_FAST_TRAP
- brnz,pn %o0, 1f
- sub %g0, %o0, %o0
- mov %o1, %o0
-1: retl
- nop
-ENDPROC(sun4v_cpu_state)
-
- /* %o0: virtual address
- * %o1: must be zero
- * %o2: TTE
- * %o3: HV_MMU_* flags
- *
- * returns %o0: status
- */
-ENTRY(sun4v_mmu_map_perm_addr)
- mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_mmu_map_perm_addr)
-
- /* %o0: number of TSB descriptions
- * %o1: TSB descriptions real address
- *
- * returns %o0: status
- */
-ENTRY(sun4v_mmu_tsb_ctx0)
- mov HV_FAST_MMU_TSB_CTX0, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_mmu_tsb_ctx0)
-
- /* %o0: API group number
- * %o1: pointer to unsigned long major number storage
- * %o2: pointer to unsigned long minor number storage
- *
- * returns %o0: status
- */
-ENTRY(sun4v_get_version)
- mov HV_CORE_GET_VER, %o5
- mov %o1, %o3
- mov %o2, %o4
- ta HV_CORE_TRAP
- stx %o1, [%o3]
- retl
- stx %o2, [%o4]
-ENDPROC(sun4v_get_version)
-
- /* %o0: API group number
- * %o1: desired major number
- * %o2: desired minor number
- * %o3: pointer to unsigned long actual minor number storage
- *
- * returns %o0: status
- */
-ENTRY(sun4v_set_version)
- mov HV_CORE_SET_VER, %o5
- mov %o3, %o4
- ta HV_CORE_TRAP
- retl
- stx %o1, [%o4]
-ENDPROC(sun4v_set_version)
-
- /* %o0: pointer to unsigned long time
- *
- * returns %o0: status
- */
-ENTRY(sun4v_tod_get)
- mov %o0, %o4
- mov HV_FAST_TOD_GET, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_tod_get)
-
- /* %o0: time
- *
- * returns %o0: status
- */
-ENTRY(sun4v_tod_set)
- mov HV_FAST_TOD_SET, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_tod_set)
-
- /* %o0: pointer to unsigned long status
- *
- * returns %o0: signed character
- */
-ENTRY(sun4v_con_getchar)
- mov %o0, %o4
- mov HV_FAST_CONS_GETCHAR, %o5
- clr %o0
- clr %o1
- ta HV_FAST_TRAP
- stx %o0, [%o4]
- retl
- sra %o1, 0, %o0
-ENDPROC(sun4v_con_getchar)
-
- /* %o0: signed long character
- *
- * returns %o0: status
- */
-ENTRY(sun4v_con_putchar)
- mov HV_FAST_CONS_PUTCHAR, %o5
- ta HV_FAST_TRAP
- retl
- sra %o0, 0, %o0
-ENDPROC(sun4v_con_putchar)
-
- /* %o0: buffer real address
- * %o1: buffer size
- * %o2: pointer to unsigned long bytes_read
- *
- * returns %o0: status
- */
-ENTRY(sun4v_con_read)
- mov %o2, %o4
- mov HV_FAST_CONS_READ, %o5
- ta HV_FAST_TRAP
- brnz %o0, 1f
- cmp %o1, -1 /* break */
- be,a,pn %icc, 1f
- mov %o1, %o0
- cmp %o1, -2 /* hup */
- be,a,pn %icc, 1f
- mov %o1, %o0
- stx %o1, [%o4]
-1: retl
- nop
-ENDPROC(sun4v_con_read)
-
- /* %o0: buffer real address
- * %o1: buffer size
- * %o2: pointer to unsigned long bytes_written
- *
- * returns %o0: status
- */
-ENTRY(sun4v_con_write)
- mov %o2, %o4
- mov HV_FAST_CONS_WRITE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_con_write)
-
- /* %o0: soft state
- * %o1: address of description string
- *
- * returns %o0: status
- */
-ENTRY(sun4v_mach_set_soft_state)
- mov HV_FAST_MACH_SET_SOFT_STATE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_mach_set_soft_state)
-
- /* %o0: exit code
- *
- * Does not return.
- */
-ENTRY(sun4v_mach_exit)
- mov HV_FAST_MACH_EXIT, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_mach_exit)
-
- /* %o0: buffer real address
- * %o1: buffer length
- * %o2: pointer to unsigned long real_buf_len
- *
- * returns %o0: status
- */
-ENTRY(sun4v_mach_desc)
- mov %o2, %o4
- mov HV_FAST_MACH_DESC, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_mach_desc)
-
- /* %o0: new timeout in milliseconds
- * %o1: pointer to unsigned long orig_timeout
- *
- * returns %o0: status
- */
-ENTRY(sun4v_mach_set_watchdog)
- mov %o1, %o4
- mov HV_FAST_MACH_SET_WATCHDOG, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_mach_set_watchdog)
-
- /* No inputs and does not return. */
-ENTRY(sun4v_mach_sir)
- mov %o1, %o4
- mov HV_FAST_MACH_SIR, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_mach_sir)
-
- /* %o0: channel
- * %o1: ra
- * %o2: num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_tx_qconf)
- mov HV_FAST_LDC_TX_QCONF, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_tx_qconf)
-
- /* %o0: channel
- * %o1: pointer to unsigned long ra
- * %o2: pointer to unsigned long num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_tx_qinfo)
- mov %o1, %g1
- mov %o2, %g2
- mov HV_FAST_LDC_TX_QINFO, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- retl
- nop
-ENDPROC(sun4v_ldc_tx_qinfo)
-
- /* %o0: channel
- * %o1: pointer to unsigned long head_off
- * %o2: pointer to unsigned long tail_off
- * %o2: pointer to unsigned long chan_state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_tx_get_state)
- mov %o1, %g1
- mov %o2, %g2
- mov %o3, %g3
- mov HV_FAST_LDC_TX_GET_STATE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- stx %o3, [%g3]
- retl
- nop
-ENDPROC(sun4v_ldc_tx_get_state)
-
- /* %o0: channel
- * %o1: tail_off
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_tx_set_qtail)
- mov HV_FAST_LDC_TX_SET_QTAIL, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_tx_set_qtail)
-
- /* %o0: channel
- * %o1: ra
- * %o2: num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_rx_qconf)
- mov HV_FAST_LDC_RX_QCONF, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_rx_qconf)
-
- /* %o0: channel
- * %o1: pointer to unsigned long ra
- * %o2: pointer to unsigned long num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_rx_qinfo)
- mov %o1, %g1
- mov %o2, %g2
- mov HV_FAST_LDC_RX_QINFO, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- retl
- nop
-ENDPROC(sun4v_ldc_rx_qinfo)
-
- /* %o0: channel
- * %o1: pointer to unsigned long head_off
- * %o2: pointer to unsigned long tail_off
- * %o2: pointer to unsigned long chan_state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_rx_get_state)
- mov %o1, %g1
- mov %o2, %g2
- mov %o3, %g3
- mov HV_FAST_LDC_RX_GET_STATE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- stx %o3, [%g3]
- retl
- nop
-ENDPROC(sun4v_ldc_rx_get_state)
-
- /* %o0: channel
- * %o1: head_off
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_rx_set_qhead)
- mov HV_FAST_LDC_RX_SET_QHEAD, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_rx_set_qhead)
-
- /* %o0: channel
- * %o1: ra
- * %o2: num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_set_map_table)
- mov HV_FAST_LDC_SET_MAP_TABLE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_set_map_table)
-
- /* %o0: channel
- * %o1: pointer to unsigned long ra
- * %o2: pointer to unsigned long num_entries
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_get_map_table)
- mov %o1, %g1
- mov %o2, %g2
- mov HV_FAST_LDC_GET_MAP_TABLE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- retl
- nop
-ENDPROC(sun4v_ldc_get_map_table)
-
- /* %o0: channel
- * %o1: dir_code
- * %o2: tgt_raddr
- * %o3: lcl_raddr
- * %o4: len
- * %o5: pointer to unsigned long actual_len
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_copy)
- mov %o5, %g1
- mov HV_FAST_LDC_COPY, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- retl
- nop
-ENDPROC(sun4v_ldc_copy)
-
- /* %o0: channel
- * %o1: cookie
- * %o2: pointer to unsigned long ra
- * %o3: pointer to unsigned long perm
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_mapin)
- mov %o2, %g1
- mov %o3, %g2
- mov HV_FAST_LDC_MAPIN, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- stx %o2, [%g2]
- retl
- nop
-ENDPROC(sun4v_ldc_mapin)
-
- /* %o0: ra
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_unmap)
- mov HV_FAST_LDC_UNMAP, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_unmap)
-
- /* %o0: channel
- * %o1: cookie
- * %o2: mte_cookie
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ldc_revoke)
- mov HV_FAST_LDC_REVOKE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ldc_revoke)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: pointer to unsigned long cookie
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_get_cookie)
- mov %o2, %g1
- mov HV_FAST_VINTR_GET_COOKIE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- retl
- nop
-ENDPROC(sun4v_vintr_get_cookie)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: cookie
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_set_cookie)
- mov HV_FAST_VINTR_SET_COOKIE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_vintr_set_cookie)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: pointer to unsigned long valid_state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_get_valid)
- mov %o2, %g1
- mov HV_FAST_VINTR_GET_VALID, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- retl
- nop
-ENDPROC(sun4v_vintr_get_valid)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: valid_state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_set_valid)
- mov HV_FAST_VINTR_SET_VALID, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_vintr_set_valid)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: pointer to unsigned long state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_get_state)
- mov %o2, %g1
- mov HV_FAST_VINTR_GET_STATE, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- retl
- nop
-ENDPROC(sun4v_vintr_get_state)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: state
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_set_state)
- mov HV_FAST_VINTR_SET_STATE, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_vintr_set_state)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: pointer to unsigned long cpuid
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_get_target)
- mov %o2, %g1
- mov HV_FAST_VINTR_GET_TARGET, %o5
- ta HV_FAST_TRAP
- stx %o1, [%g1]
- retl
- nop
-ENDPROC(sun4v_vintr_get_target)
-
- /* %o0: device handle
- * %o1: device INO
- * %o2: cpuid
- *
- * returns %o0: status
- */
-ENTRY(sun4v_vintr_set_target)
- mov HV_FAST_VINTR_SET_TARGET, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_vintr_set_target)
-
- /* %o0: NCS sub-function
- * %o1: sub-function arg real-address
- * %o2: sub-function arg size
- *
- * returns %o0: status
- */
-ENTRY(sun4v_ncs_request)
- mov HV_FAST_NCS_REQUEST, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_ncs_request)
-
-ENTRY(sun4v_svc_send)
- save %sp, -192, %sp
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
- mov HV_FAST_SVC_SEND, %o5
- ta HV_FAST_TRAP
- stx %o1, [%i3]
- ret
- restore
-ENDPROC(sun4v_svc_send)
-
-ENTRY(sun4v_svc_recv)
- save %sp, -192, %sp
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
- mov HV_FAST_SVC_RECV, %o5
- ta HV_FAST_TRAP
- stx %o1, [%i3]
- ret
- restore
-ENDPROC(sun4v_svc_recv)
-
-ENTRY(sun4v_svc_getstatus)
- mov HV_FAST_SVC_GETSTATUS, %o5
- mov %o1, %o4
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_svc_getstatus)
-
-ENTRY(sun4v_svc_setstatus)
- mov HV_FAST_SVC_SETSTATUS, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_svc_setstatus)
-
-ENTRY(sun4v_svc_clrstatus)
- mov HV_FAST_SVC_CLRSTATUS, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_svc_clrstatus)
-
-ENTRY(sun4v_mmustat_conf)
- mov %o1, %o4
- mov HV_FAST_MMUSTAT_CONF, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_mmustat_conf)
-
-ENTRY(sun4v_mmustat_info)
- mov %o0, %o4
- mov HV_FAST_MMUSTAT_INFO, %o5
- ta HV_FAST_TRAP
- stx %o1, [%o4]
- retl
- nop
-ENDPROC(sun4v_mmustat_info)
-
-ENTRY(sun4v_mmu_demap_all)
- clr %o0
- clr %o1
- mov HV_MMU_ALL, %o2
- mov HV_FAST_MMU_DEMAP_ALL, %o5
- ta HV_FAST_TRAP
- retl
- nop
-ENDPROC(sun4v_mmu_demap_all)
diff --git a/arch/sparc64/kernel/hvtramp.S b/arch/sparc64/kernel/hvtramp.S
deleted file mode 100644
index 0236c43772fa..000000000000
--- a/arch/sparc64/kernel/hvtramp.S
+++ /dev/null
@@ -1,139 +0,0 @@
-/* hvtramp.S: Hypervisor start-cpu trampoline code.
- *
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
- */
-
-#include <linux/init.h>
-
-#include <asm/thread_info.h>
-#include <asm/hypervisor.h>
-#include <asm/scratchpad.h>
-#include <asm/spitfire.h>
-#include <asm/hvtramp.h>
-#include <asm/pstate.h>
-#include <asm/ptrace.h>
-#include <asm/head.h>
-#include <asm/asi.h>
-
- __CPUINIT
- .align 8
- .globl hv_cpu_startup, hv_cpu_startup_end
-
- /* This code executes directly out of the hypervisor
- * with physical addressing (va==pa). %o0 contains
- * our client argument which for Linux points to
- * a descriptor data structure which defines the
- * MMU entries we need to load up.
- *
- * After we set things up we enable the MMU and call
- * into the kernel.
- *
- * First setup basic privileged cpu state.
- */
-hv_cpu_startup:
- SET_GL(0)
- wrpr %g0, 15, %pil
- wrpr %g0, 0, %canrestore
- wrpr %g0, 0, %otherwin
- wrpr %g0, 6, %cansave
- wrpr %g0, 6, %cleanwin
- wrpr %g0, 0, %cwp
- wrpr %g0, 0, %wstate
- wrpr %g0, 0, %tl
-
- sethi %hi(sparc64_ttable_tl0), %g1
- wrpr %g1, %tba
-
- mov %o0, %l0
-
- lduw [%l0 + HVTRAMP_DESCR_CPU], %g1
- mov SCRATCHPAD_CPUID, %g2
- stxa %g1, [%g2] ASI_SCRATCHPAD
-
- ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_VA], %g2
- stxa %g2, [%g0] ASI_SCRATCHPAD
-
- mov 0, %l1
- lduw [%l0 + HVTRAMP_DESCR_NUM_MAPPINGS], %l2
- add %l0, HVTRAMP_DESCR_MAPS, %l3
-
-1: ldx [%l3 + HVTRAMP_MAPPING_VADDR], %o0
- clr %o1
- ldx [%l3 + HVTRAMP_MAPPING_TTE], %o2
- mov HV_MMU_IMMU | HV_MMU_DMMU, %o3
- mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
- ta HV_FAST_TRAP
-
- brnz,pn %o0, 80f
- nop
-
- add %l1, 1, %l1
- cmp %l1, %l2
- blt,a,pt %xcc, 1b
- add %l3, HVTRAMP_MAPPING_SIZE, %l3
-
- ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_PA], %o0
- mov HV_FAST_MMU_FAULT_AREA_CONF, %o5
- ta HV_FAST_TRAP
-
- brnz,pn %o0, 80f
- nop
-
- wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
-
- ldx [%l0 + HVTRAMP_DESCR_THREAD_REG], %l6
-
- mov 1, %o0
- set 1f, %o1
- mov HV_FAST_MMU_ENABLE, %o5
- ta HV_FAST_TRAP
-
- ba,pt %xcc, 80f
- nop
-
-1:
- wr %g0, 0, %fprs
- wr %g0, ASI_P, %asi
-
- mov PRIMARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_MMU
- membar #Sync
-
- mov SECONDARY_CONTEXT, %g7
- stxa %g0, [%g7] ASI_MMU
- membar #Sync
-
- mov %l6, %g6
- ldx [%g6 + TI_TASK], %g4
-
- mov 1, %g5
- sllx %g5, THREAD_SHIFT, %g5
- sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
- add %g6, %g5, %sp
- mov 0, %fp
-
- call init_irqwork_curcpu
- nop
- call hard_smp_processor_id
- nop
-
- call sun4v_register_mondo_queues
- nop
-
- call init_cur_cpu_trap
- mov %g6, %o0
-
- wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate
-
- call smp_callin
- nop
- call cpu_idle
- mov 0, %o0
- call cpu_panic
- nop
-
-80: ba,pt %xcc, 80b
- nop
-
- .align 8
-hv_cpu_startup_end:
diff --git a/arch/sparc64/kernel/idprom.c b/arch/sparc64/kernel/idprom.c
deleted file mode 100644
index 5b45a808c621..000000000000
--- a/arch/sparc64/kernel/idprom.c
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * idprom.c: Routines to load the idprom into kernel addresses and
- * interpret the data contained within.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-
-#include <asm/oplib.h>
-#include <asm/idprom.h>
-
-struct idprom *idprom;
-static struct idprom idprom_buffer;
-
-/* Calculate the IDPROM checksum (xor of the data bytes). */
-static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
-{
- unsigned char cksum, i, *ptr = (unsigned char *)idprom;
-
- for (i = cksum = 0; i <= 0x0E; i++)
- cksum ^= *ptr++;
-
- return cksum;
-}
-
-/* Create a local IDPROM copy and verify integrity. */
-void __init idprom_init(void)
-{
- prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
-
- idprom = &idprom_buffer;
-
- if (idprom->id_format != 0x01) {
- prom_printf("IDPROM: Warning, unknown format type!\n");
- }
-
- if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
- prom_printf("IDPROM: Warning, checksum failure (nvram=%x, calc=%x)!\n",
- idprom->id_cksum, calc_idprom_cksum(idprom));
- }
-
- printk("Ethernet address: %02x:%02x:%02x:%02x:%02x:%02x\n",
- idprom->id_ethaddr[0], idprom->id_ethaddr[1],
- idprom->id_ethaddr[2], idprom->id_ethaddr[3],
- idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
-}
diff --git a/arch/sparc64/kernel/init_task.c b/arch/sparc64/kernel/init_task.c
deleted file mode 100644
index d2b312381c19..000000000000
--- a/arch/sparc64/kernel/init_task.c
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/init_task.h>
-#include <linux/mqueue.h>
-
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/processor.h>
-
-static struct fs_struct init_fs = INIT_FS;
-static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
-static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
-struct mm_struct init_mm = INIT_MM(init_mm);
-
-EXPORT_SYMBOL(init_mm);
-
-/* .text section in head.S is aligned at 2 page boundary and this gets linked
- * right after that so that the init_thread_union is aligned properly as well.
- * We really don't need this special alignment like the Intel does, but
- * I do it anyways for completeness.
- */
-__asm__ (".text");
-union thread_union init_thread_union = { INIT_THREAD_INFO(init_task) };
-
-/*
- * Initial task structure.
- *
- * All other task structs will be allocated on slabs in fork.c
- */
-EXPORT_SYMBOL(init_task);
-
-__asm__(".data");
-struct task_struct init_task = INIT_TASK(init_task);
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
deleted file mode 100644
index 4aaf18e83c8c..000000000000
--- a/arch/sparc64/kernel/irq.c
+++ /dev/null
@@ -1,1041 +0,0 @@
-/* irq.c: UltraSparc IRQ handling/init/registry.
- *
- * Copyright (C) 1997, 2007, 2008 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/linkage.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/bootmem.h>
-#include <linux/irq.h>
-
-#include <asm/ptrace.h>
-#include <asm/processor.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <asm/iommu.h>
-#include <asm/upa.h>
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/timer.h>
-#include <asm/smp.h>
-#include <asm/starfire.h>
-#include <asm/uaccess.h>
-#include <asm/cache.h>
-#include <asm/cpudata.h>
-#include <asm/auxio.h>
-#include <asm/head.h>
-#include <asm/hypervisor.h>
-#include <asm/cacheflush.h>
-
-#include "entry.h"
-
-#define NUM_IVECS (IMAP_INR + 1)
-
-struct ino_bucket *ivector_table;
-unsigned long ivector_table_pa;
-
-/* On several sun4u processors, it is illegal to mix bypass and
- * non-bypass accesses. Therefore we access all INO buckets
- * using bypass accesses only.
- */
-static unsigned long bucket_get_chain_pa(unsigned long bucket_pa)
-{
- unsigned long ret;
-
- __asm__ __volatile__("ldxa [%1] %2, %0"
- : "=&r" (ret)
- : "r" (bucket_pa +
- offsetof(struct ino_bucket,
- __irq_chain_pa)),
- "i" (ASI_PHYS_USE_EC));
-
- return ret;
-}
-
-static void bucket_clear_chain_pa(unsigned long bucket_pa)
-{
- __asm__ __volatile__("stxa %%g0, [%0] %1"
- : /* no outputs */
- : "r" (bucket_pa +
- offsetof(struct ino_bucket,
- __irq_chain_pa)),
- "i" (ASI_PHYS_USE_EC));
-}
-
-static unsigned int bucket_get_virt_irq(unsigned long bucket_pa)
-{
- unsigned int ret;
-
- __asm__ __volatile__("lduwa [%1] %2, %0"
- : "=&r" (ret)
- : "r" (bucket_pa +
- offsetof(struct ino_bucket,
- __virt_irq)),
- "i" (ASI_PHYS_USE_EC));
-
- return ret;
-}
-
-static void bucket_set_virt_irq(unsigned long bucket_pa,
- unsigned int virt_irq)
-{
- __asm__ __volatile__("stwa %0, [%1] %2"
- : /* no outputs */
- : "r" (virt_irq),
- "r" (bucket_pa +
- offsetof(struct ino_bucket,
- __virt_irq)),
- "i" (ASI_PHYS_USE_EC));
-}
-
-#define irq_work_pa(__cpu) &(trap_block[(__cpu)].irq_worklist_pa)
-
-static struct {
- unsigned int dev_handle;
- unsigned int dev_ino;
- unsigned int in_use;
-} virt_irq_table[NR_IRQS];
-static DEFINE_SPINLOCK(virt_irq_alloc_lock);
-
-unsigned char virt_irq_alloc(unsigned int dev_handle,
- unsigned int dev_ino)
-{
- unsigned long flags;
- unsigned char ent;
-
- BUILD_BUG_ON(NR_IRQS >= 256);
-
- spin_lock_irqsave(&virt_irq_alloc_lock, flags);
-
- for (ent = 1; ent < NR_IRQS; ent++) {
- if (!virt_irq_table[ent].in_use)
- break;
- }
- if (ent >= NR_IRQS) {
- printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
- ent = 0;
- } else {
- virt_irq_table[ent].dev_handle = dev_handle;
- virt_irq_table[ent].dev_ino = dev_ino;
- virt_irq_table[ent].in_use = 1;
- }
-
- spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);
-
- return ent;
-}
-
-#ifdef CONFIG_PCI_MSI
-void virt_irq_free(unsigned int virt_irq)
-{
- unsigned long flags;
-
- if (virt_irq >= NR_IRQS)
- return;
-
- spin_lock_irqsave(&virt_irq_alloc_lock, flags);
-
- virt_irq_table[virt_irq].in_use = 0;
-
- spin_unlock_irqrestore(&virt_irq_alloc_lock, flags);
-}
-#endif
-
-/*
- * /proc/interrupts printing:
- */
-
-int show_interrupts(struct seq_file *p, void *v)
-{
- int i = *(loff_t *) v, j;
- struct irqaction * action;
- unsigned long flags;
-
- if (i == 0) {
- seq_printf(p, " ");
- for_each_online_cpu(j)
- seq_printf(p, "CPU%d ",j);
- seq_putc(p, '\n');
- }
-
- if (i < NR_IRQS) {
- spin_lock_irqsave(&irq_desc[i].lock, flags);
- action = irq_desc[i].action;
- if (!action)
- goto skip;
- seq_printf(p, "%3d: ",i);
-#ifndef CONFIG_SMP
- seq_printf(p, "%10u ", kstat_irqs(i));
-#else
- for_each_online_cpu(j)
- seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
-#endif
- seq_printf(p, " %9s", irq_desc[i].chip->typename);
- seq_printf(p, " %s", action->name);
-
- for (action=action->next; action; action = action->next)
- seq_printf(p, ", %s", action->name);
-
- seq_putc(p, '\n');
-skip:
- spin_unlock_irqrestore(&irq_desc[i].lock, flags);
- }
- return 0;
-}
-
-static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
-{
- unsigned int tid;
-
- if (this_is_starfire) {
- tid = starfire_translate(imap, cpuid);
- tid <<= IMAP_TID_SHIFT;
- tid &= IMAP_TID_UPA;
- } else {
- if (tlb_type == cheetah || tlb_type == cheetah_plus) {
- unsigned long ver;
-
- __asm__ ("rdpr %%ver, %0" : "=r" (ver));
- if ((ver >> 32UL) == __JALAPENO_ID ||
- (ver >> 32UL) == __SERRANO_ID) {
- tid = cpuid << IMAP_TID_SHIFT;
- tid &= IMAP_TID_JBUS;
- } else {
- unsigned int a = cpuid & 0x1f;
- unsigned int n = (cpuid >> 5) & 0x1f;
-
- tid = ((a << IMAP_AID_SHIFT) |
- (n << IMAP_NID_SHIFT));
- tid &= (IMAP_AID_SAFARI |
- IMAP_NID_SAFARI);;
- }
- } else {
- tid = cpuid << IMAP_TID_SHIFT;
- tid &= IMAP_TID_UPA;
- }
- }
-
- return tid;
-}
-
-struct irq_handler_data {
- unsigned long iclr;
- unsigned long imap;
-
- void (*pre_handler)(unsigned int, void *, void *);
- void *arg1;
- void *arg2;
-};
-
-#ifdef CONFIG_SMP
-static int irq_choose_cpu(unsigned int virt_irq)
-{
- cpumask_t mask = irq_desc[virt_irq].affinity;
- int cpuid;
-
- if (cpus_equal(mask, CPU_MASK_ALL)) {
- static int irq_rover;
- static DEFINE_SPINLOCK(irq_rover_lock);
- unsigned long flags;
-
- /* Round-robin distribution... */
- do_round_robin:
- spin_lock_irqsave(&irq_rover_lock, flags);
-
- while (!cpu_online(irq_rover)) {
- if (++irq_rover >= NR_CPUS)
- irq_rover = 0;
- }
- cpuid = irq_rover;
- do {
- if (++irq_rover >= NR_CPUS)
- irq_rover = 0;
- } while (!cpu_online(irq_rover));
-
- spin_unlock_irqrestore(&irq_rover_lock, flags);
- } else {
- cpumask_t tmp;
-
- cpus_and(tmp, cpu_online_map, mask);
-
- if (cpus_empty(tmp))
- goto do_round_robin;
-
- cpuid = first_cpu(tmp);
- }
-
- return cpuid;
-}
-#else
-static int irq_choose_cpu(unsigned int virt_irq)
-{
- return real_hard_smp_processor_id();
-}
-#endif
-
-static void sun4u_irq_enable(unsigned int virt_irq)
-{
- struct irq_handler_data *data = get_irq_chip_data(virt_irq);
-
- if (likely(data)) {
- unsigned long cpuid, imap, val;
- unsigned int tid;
-
- cpuid = irq_choose_cpu(virt_irq);
- imap = data->imap;
-
- tid = sun4u_compute_tid(imap, cpuid);
-
- val = upa_readq(imap);
- val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
- IMAP_AID_SAFARI | IMAP_NID_SAFARI);
- val |= tid | IMAP_VALID;
- upa_writeq(val, imap);
- upa_writeq(ICLR_IDLE, data->iclr);
- }
-}
-
-static void sun4u_set_affinity(unsigned int virt_irq,
- const struct cpumask *mask)
-{
- sun4u_irq_enable(virt_irq);
-}
-
-static void sun4u_irq_disable(unsigned int virt_irq)
-{
- struct irq_handler_data *data = get_irq_chip_data(virt_irq);
-
- if (likely(data)) {
- unsigned long imap = data->imap;
- unsigned long tmp = upa_readq(imap);
-
- tmp &= ~IMAP_VALID;
- upa_writeq(tmp, imap);
- }
-}
-
-static void sun4u_irq_eoi(unsigned int virt_irq)
-{
- struct irq_handler_data *data = get_irq_chip_data(virt_irq);
- struct irq_desc *desc = irq_desc + virt_irq;
-
- if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- return;
-
- if (likely(data))
- upa_writeq(ICLR_IDLE, data->iclr);
-}
-
-static void sun4v_irq_enable(unsigned int virt_irq)
-{
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
- unsigned long cpuid = irq_choose_cpu(virt_irq);
- int err;
-
- err = sun4v_intr_settarget(ino, cpuid);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_settarget(%x,%lu): "
- "err(%d)\n", ino, cpuid, err);
- err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_setstate(%x): "
- "err(%d)\n", ino, err);
- err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_setenabled(%x): err(%d)\n",
- ino, err);
-}
-
-static void sun4v_set_affinity(unsigned int virt_irq,
- const struct cpumask *mask)
-{
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
- unsigned long cpuid = irq_choose_cpu(virt_irq);
- int err;
-
- err = sun4v_intr_settarget(ino, cpuid);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_settarget(%x,%lu): "
- "err(%d)\n", ino, cpuid, err);
-}
-
-static void sun4v_irq_disable(unsigned int virt_irq)
-{
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
- int err;
-
- err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_setenabled(%x): "
- "err(%d)\n", ino, err);
-}
-
-static void sun4v_irq_eoi(unsigned int virt_irq)
-{
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
- struct irq_desc *desc = irq_desc + virt_irq;
- int err;
-
- if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- return;
-
- err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_intr_setstate(%x): "
- "err(%d)\n", ino, err);
-}
-
-static void sun4v_virq_enable(unsigned int virt_irq)
-{
- unsigned long cpuid, dev_handle, dev_ino;
- int err;
-
- cpuid = irq_choose_cpu(virt_irq);
-
- dev_handle = virt_irq_table[virt_irq].dev_handle;
- dev_ino = virt_irq_table[virt_irq].dev_ino;
-
- err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
- "err(%d)\n",
- dev_handle, dev_ino, cpuid, err);
- err = sun4v_vintr_set_state(dev_handle, dev_ino,
- HV_INTR_STATE_IDLE);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
- "HV_INTR_STATE_IDLE): err(%d)\n",
- dev_handle, dev_ino, err);
- err = sun4v_vintr_set_valid(dev_handle, dev_ino,
- HV_INTR_ENABLED);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
- "HV_INTR_ENABLED): err(%d)\n",
- dev_handle, dev_ino, err);
-}
-
-static void sun4v_virt_set_affinity(unsigned int virt_irq,
- const struct cpumask *mask)
-{
- unsigned long cpuid, dev_handle, dev_ino;
- int err;
-
- cpuid = irq_choose_cpu(virt_irq);
-
- dev_handle = virt_irq_table[virt_irq].dev_handle;
- dev_ino = virt_irq_table[virt_irq].dev_ino;
-
- err = sun4v_vintr_set_target(dev_handle, dev_ino, cpuid);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_target(%lx,%lx,%lu): "
- "err(%d)\n",
- dev_handle, dev_ino, cpuid, err);
-}
-
-static void sun4v_virq_disable(unsigned int virt_irq)
-{
- unsigned long dev_handle, dev_ino;
- int err;
-
- dev_handle = virt_irq_table[virt_irq].dev_handle;
- dev_ino = virt_irq_table[virt_irq].dev_ino;
-
- err = sun4v_vintr_set_valid(dev_handle, dev_ino,
- HV_INTR_DISABLED);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
- "HV_INTR_DISABLED): err(%d)\n",
- dev_handle, dev_ino, err);
-}
-
-static void sun4v_virq_eoi(unsigned int virt_irq)
-{
- struct irq_desc *desc = irq_desc + virt_irq;
- unsigned long dev_handle, dev_ino;
- int err;
-
- if (unlikely(desc->status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- return;
-
- dev_handle = virt_irq_table[virt_irq].dev_handle;
- dev_ino = virt_irq_table[virt_irq].dev_ino;
-
- err = sun4v_vintr_set_state(dev_handle, dev_ino,
- HV_INTR_STATE_IDLE);
- if (err != HV_EOK)
- printk(KERN_ERR "sun4v_vintr_set_state(%lx,%lx,"
- "HV_INTR_STATE_IDLE): err(%d)\n",
- dev_handle, dev_ino, err);
-}
-
-static struct irq_chip sun4u_irq = {
- .typename = "sun4u",
- .enable = sun4u_irq_enable,
- .disable = sun4u_irq_disable,
- .eoi = sun4u_irq_eoi,
- .set_affinity = sun4u_set_affinity,
-};
-
-static struct irq_chip sun4v_irq = {
- .typename = "sun4v",
- .enable = sun4v_irq_enable,
- .disable = sun4v_irq_disable,
- .eoi = sun4v_irq_eoi,
- .set_affinity = sun4v_set_affinity,
-};
-
-static struct irq_chip sun4v_virq = {
- .typename = "vsun4v",
- .enable = sun4v_virq_enable,
- .disable = sun4v_virq_disable,
- .eoi = sun4v_virq_eoi,
- .set_affinity = sun4v_virt_set_affinity,
-};
-
-static void pre_flow_handler(unsigned int virt_irq,
- struct irq_desc *desc)
-{
- struct irq_handler_data *data = get_irq_chip_data(virt_irq);
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
-
- data->pre_handler(ino, data->arg1, data->arg2);
-
- handle_fasteoi_irq(virt_irq, desc);
-}
-
-void irq_install_pre_handler(int virt_irq,
- void (*func)(unsigned int, void *, void *),
- void *arg1, void *arg2)
-{
- struct irq_handler_data *data = get_irq_chip_data(virt_irq);
- struct irq_desc *desc = irq_desc + virt_irq;
-
- data->pre_handler = func;
- data->arg1 = arg1;
- data->arg2 = arg2;
-
- desc->handle_irq = pre_flow_handler;
-}
-
-unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
-{
- struct ino_bucket *bucket;
- struct irq_handler_data *data;
- unsigned int virt_irq;
- int ino;
-
- BUG_ON(tlb_type == hypervisor);
-
- ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
- bucket = &ivector_table[ino];
- virt_irq = bucket_get_virt_irq(__pa(bucket));
- if (!virt_irq) {
- virt_irq = virt_irq_alloc(0, ino);
- bucket_set_virt_irq(__pa(bucket), virt_irq);
- set_irq_chip_and_handler_name(virt_irq,
- &sun4u_irq,
- handle_fasteoi_irq,
- "IVEC");
- }
-
- data = get_irq_chip_data(virt_irq);
- if (unlikely(data))
- goto out;
-
- data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
- if (unlikely(!data)) {
- prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
- prom_halt();
- }
- set_irq_chip_data(virt_irq, data);
-
- data->imap = imap;
- data->iclr = iclr;
-
-out:
- return virt_irq;
-}
-
-static unsigned int sun4v_build_common(unsigned long sysino,
- struct irq_chip *chip)
-{
- struct ino_bucket *bucket;
- struct irq_handler_data *data;
- unsigned int virt_irq;
-
- BUG_ON(tlb_type != hypervisor);
-
- bucket = &ivector_table[sysino];
- virt_irq = bucket_get_virt_irq(__pa(bucket));
- if (!virt_irq) {
- virt_irq = virt_irq_alloc(0, sysino);
- bucket_set_virt_irq(__pa(bucket), virt_irq);
- set_irq_chip_and_handler_name(virt_irq, chip,
- handle_fasteoi_irq,
- "IVEC");
- }
-
- data = get_irq_chip_data(virt_irq);
- if (unlikely(data))
- goto out;
-
- data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
- if (unlikely(!data)) {
- prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n");
- prom_halt();
- }
- set_irq_chip_data(virt_irq, data);
-
- /* Catch accidental accesses to these things. IMAP/ICLR handling
- * is done by hypervisor calls on sun4v platforms, not by direct
- * register accesses.
- */
- data->imap = ~0UL;
- data->iclr = ~0UL;
-
-out:
- return virt_irq;
-}
-
-unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino)
-{
- unsigned long sysino = sun4v_devino_to_sysino(devhandle, devino);
-
- return sun4v_build_common(sysino, &sun4v_irq);
-}
-
-unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino)
-{
- struct irq_handler_data *data;
- unsigned long hv_err, cookie;
- struct ino_bucket *bucket;
- struct irq_desc *desc;
- unsigned int virt_irq;
-
- bucket = kzalloc(sizeof(struct ino_bucket), GFP_ATOMIC);
- if (unlikely(!bucket))
- return 0;
- __flush_dcache_range((unsigned long) bucket,
- ((unsigned long) bucket +
- sizeof(struct ino_bucket)));
-
- virt_irq = virt_irq_alloc(devhandle, devino);
- bucket_set_virt_irq(__pa(bucket), virt_irq);
-
- set_irq_chip_and_handler_name(virt_irq, &sun4v_virq,
- handle_fasteoi_irq,
- "IVEC");
-
- data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC);
- if (unlikely(!data))
- return 0;
-
- /* In order to make the LDC channel startup sequence easier,
- * especially wrt. locking, we do not let request_irq() enable
- * the interrupt.
- */
- desc = irq_desc + virt_irq;
- desc->status |= IRQ_NOAUTOEN;
-
- set_irq_chip_data(virt_irq, data);
-
- /* Catch accidental accesses to these things. IMAP/ICLR handling
- * is done by hypervisor calls on sun4v platforms, not by direct
- * register accesses.
- */
- data->imap = ~0UL;
- data->iclr = ~0UL;
-
- cookie = ~__pa(bucket);
- hv_err = sun4v_vintr_set_cookie(devhandle, devino, cookie);
- if (hv_err) {
- prom_printf("IRQ: Fatal, cannot set cookie for [%x:%x] "
- "err=%lu\n", devhandle, devino, hv_err);
- prom_halt();
- }
-
- return virt_irq;
-}
-
-void ack_bad_irq(unsigned int virt_irq)
-{
- unsigned int ino = virt_irq_table[virt_irq].dev_ino;
-
- if (!ino)
- ino = 0xdeadbeef;
-
- printk(KERN_CRIT "Unexpected IRQ from ino[%x] virt_irq[%u]\n",
- ino, virt_irq);
-}
-
-void *hardirq_stack[NR_CPUS];
-void *softirq_stack[NR_CPUS];
-
-static __attribute__((always_inline)) void *set_hardirq_stack(void)
-{
- void *orig_sp, *sp = hardirq_stack[smp_processor_id()];
-
- __asm__ __volatile__("mov %%sp, %0" : "=r" (orig_sp));
- if (orig_sp < sp ||
- orig_sp > (sp + THREAD_SIZE)) {
- sp += THREAD_SIZE - 192 - STACK_BIAS;
- __asm__ __volatile__("mov %0, %%sp" : : "r" (sp));
- }
-
- return orig_sp;
-}
-static __attribute__((always_inline)) void restore_hardirq_stack(void *orig_sp)
-{
- __asm__ __volatile__("mov %0, %%sp" : : "r" (orig_sp));
-}
-
-void handler_irq(int irq, struct pt_regs *regs)
-{
- unsigned long pstate, bucket_pa;
- struct pt_regs *old_regs;
- void *orig_sp;
-
- clear_softint(1 << irq);
-
- old_regs = set_irq_regs(regs);
- irq_enter();
-
- /* Grab an atomic snapshot of the pending IVECs. */
- __asm__ __volatile__("rdpr %%pstate, %0\n\t"
- "wrpr %0, %3, %%pstate\n\t"
- "ldx [%2], %1\n\t"
- "stx %%g0, [%2]\n\t"
- "wrpr %0, 0x0, %%pstate\n\t"
- : "=&r" (pstate), "=&r" (bucket_pa)
- : "r" (irq_work_pa(smp_processor_id())),
- "i" (PSTATE_IE)
- : "memory");
-
- orig_sp = set_hardirq_stack();
-
- while (bucket_pa) {
- struct irq_desc *desc;
- unsigned long next_pa;
- unsigned int virt_irq;
-
- next_pa = bucket_get_chain_pa(bucket_pa);
- virt_irq = bucket_get_virt_irq(bucket_pa);
- bucket_clear_chain_pa(bucket_pa);
-
- desc = irq_desc + virt_irq;
-
- desc->handle_irq(virt_irq, desc);
-
- bucket_pa = next_pa;
- }
-
- restore_hardirq_stack(orig_sp);
-
- irq_exit();
- set_irq_regs(old_regs);
-}
-
-void do_softirq(void)
-{
- unsigned long flags;
-
- if (in_interrupt())
- return;
-
- local_irq_save(flags);
-
- if (local_softirq_pending()) {
- void *orig_sp, *sp = softirq_stack[smp_processor_id()];
-
- sp += THREAD_SIZE - 192 - STACK_BIAS;
-
- __asm__ __volatile__("mov %%sp, %0\n\t"
- "mov %1, %%sp"
- : "=&r" (orig_sp)
- : "r" (sp));
- __do_softirq();
- __asm__ __volatile__("mov %0, %%sp"
- : : "r" (orig_sp));
- }
-
- local_irq_restore(flags);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-void fixup_irqs(void)
-{
- unsigned int irq;
-
- for (irq = 0; irq < NR_IRQS; irq++) {
- unsigned long flags;
-
- spin_lock_irqsave(&irq_desc[irq].lock, flags);
- if (irq_desc[irq].action &&
- !(irq_desc[irq].status & IRQ_PER_CPU)) {
- if (irq_desc[irq].chip->set_affinity)
- irq_desc[irq].chip->set_affinity(irq,
- &irq_desc[irq].affinity);
- }
- spin_unlock_irqrestore(&irq_desc[irq].lock, flags);
- }
-
- tick_ops->disable_irq();
-}
-#endif
-
-struct sun5_timer {
- u64 count0;
- u64 limit0;
- u64 count1;
- u64 limit1;
-};
-
-static struct sun5_timer *prom_timers;
-static u64 prom_limit0, prom_limit1;
-
-static void map_prom_timers(void)
-{
- struct device_node *dp;
- const unsigned int *addr;
-
- /* PROM timer node hangs out in the top level of device siblings... */
- dp = of_find_node_by_path("/");
- dp = dp->child;
- while (dp) {
- if (!strcmp(dp->name, "counter-timer"))
- break;
- dp = dp->sibling;
- }
-
- /* Assume if node is not present, PROM uses different tick mechanism
- * which we should not care about.
- */
- if (!dp) {
- prom_timers = (struct sun5_timer *) 0;
- return;
- }
-
- /* If PROM is really using this, it must be mapped by him. */
- addr = of_get_property(dp, "address", NULL);
- if (!addr) {
- prom_printf("PROM does not have timer mapped, trying to continue.\n");
- prom_timers = (struct sun5_timer *) 0;
- return;
- }
- prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]);
-}
-
-static void kill_prom_timer(void)
-{
- if (!prom_timers)
- return;
-
- /* Save them away for later. */
- prom_limit0 = prom_timers->limit0;
- prom_limit1 = prom_timers->limit1;
-
- /* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14.
- * We turn both off here just to be paranoid.
- */
- prom_timers->limit0 = 0;
- prom_timers->limit1 = 0;
-
- /* Wheee, eat the interrupt packet too... */
- __asm__ __volatile__(
-" mov 0x40, %%g2\n"
-" ldxa [%%g0] %0, %%g1\n"
-" ldxa [%%g2] %1, %%g1\n"
-" stxa %%g0, [%%g0] %0\n"
-" membar #Sync\n"
- : /* no outputs */
- : "i" (ASI_INTR_RECEIVE), "i" (ASI_INTR_R)
- : "g1", "g2");
-}
-
-void notrace init_irqwork_curcpu(void)
-{
- int cpu = hard_smp_processor_id();
-
- trap_block[cpu].irq_worklist_pa = 0UL;
-}
-
-/* Please be very careful with register_one_mondo() and
- * sun4v_register_mondo_queues().
- *
- * On SMP this gets invoked from the CPU trampoline before
- * the cpu has fully taken over the trap table from OBP,
- * and it's kernel stack + %g6 thread register state is
- * not fully cooked yet.
- *
- * Therefore you cannot make any OBP calls, not even prom_printf,
- * from these two routines.
- */
-static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type, unsigned long qmask)
-{
- unsigned long num_entries = (qmask + 1) / 64;
- unsigned long status;
-
- status = sun4v_cpu_qconf(type, paddr, num_entries);
- if (status != HV_EOK) {
- prom_printf("SUN4V: sun4v_cpu_qconf(%lu:%lx:%lu) failed, "
- "err %lu\n", type, paddr, num_entries, status);
- prom_halt();
- }
-}
-
-void __cpuinit notrace sun4v_register_mondo_queues(int this_cpu)
-{
- struct trap_per_cpu *tb = &trap_block[this_cpu];
-
- register_one_mondo(tb->cpu_mondo_pa, HV_CPU_QUEUE_CPU_MONDO,
- tb->cpu_mondo_qmask);
- register_one_mondo(tb->dev_mondo_pa, HV_CPU_QUEUE_DEVICE_MONDO,
- tb->dev_mondo_qmask);
- register_one_mondo(tb->resum_mondo_pa, HV_CPU_QUEUE_RES_ERROR,
- tb->resum_qmask);
- register_one_mondo(tb->nonresum_mondo_pa, HV_CPU_QUEUE_NONRES_ERROR,
- tb->nonresum_qmask);
-}
-
-static void __init alloc_one_mondo(unsigned long *pa_ptr, unsigned long qmask)
-{
- unsigned long size = PAGE_ALIGN(qmask + 1);
- void *p = __alloc_bootmem(size, size, 0);
- if (!p) {
- prom_printf("SUN4V: Error, cannot allocate mondo queue.\n");
- prom_halt();
- }
-
- *pa_ptr = __pa(p);
-}
-
-static void __init alloc_one_kbuf(unsigned long *pa_ptr, unsigned long qmask)
-{
- unsigned long size = PAGE_ALIGN(qmask + 1);
- void *p = __alloc_bootmem(size, size, 0);
-
- if (!p) {
- prom_printf("SUN4V: Error, cannot allocate kbuf page.\n");
- prom_halt();
- }
-
- *pa_ptr = __pa(p);
-}
-
-static void __init init_cpu_send_mondo_info(struct trap_per_cpu *tb)
-{
-#ifdef CONFIG_SMP
- void *page;
-
- BUILD_BUG_ON((NR_CPUS * sizeof(u16)) > (PAGE_SIZE - 64));
-
- page = alloc_bootmem_pages(PAGE_SIZE);
- if (!page) {
- prom_printf("SUN4V: Error, cannot allocate cpu mondo page.\n");
- prom_halt();
- }
-
- tb->cpu_mondo_block_pa = __pa(page);
- tb->cpu_list_pa = __pa(page + 64);
-#endif
-}
-
-/* Allocate mondo and error queues for all possible cpus. */
-static void __init sun4v_init_mondo_queues(void)
-{
- int cpu;
-
- for_each_possible_cpu(cpu) {
- struct trap_per_cpu *tb = &trap_block[cpu];
-
- alloc_one_mondo(&tb->cpu_mondo_pa, tb->cpu_mondo_qmask);
- alloc_one_mondo(&tb->dev_mondo_pa, tb->dev_mondo_qmask);
- alloc_one_mondo(&tb->resum_mondo_pa, tb->resum_qmask);
- alloc_one_kbuf(&tb->resum_kernel_buf_pa, tb->resum_qmask);
- alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask);
- alloc_one_kbuf(&tb->nonresum_kernel_buf_pa,
- tb->nonresum_qmask);
- }
-}
-
-static void __init init_send_mondo_info(void)
-{
- int cpu;
-
- for_each_possible_cpu(cpu) {
- struct trap_per_cpu *tb = &trap_block[cpu];
-
- init_cpu_send_mondo_info(tb);
- }
-}
-
-static struct irqaction timer_irq_action = {
- .name = "timer",
-};
-
-/* Only invoked on boot processor. */
-void __init init_IRQ(void)
-{
- unsigned long size;
-
- map_prom_timers();
- kill_prom_timer();
-
- size = sizeof(struct ino_bucket) * NUM_IVECS;
- ivector_table = alloc_bootmem(size);
- if (!ivector_table) {
- prom_printf("Fatal error, cannot allocate ivector_table\n");
- prom_halt();
- }
- __flush_dcache_range((unsigned long) ivector_table,
- ((unsigned long) ivector_table) + size);
-
- ivector_table_pa = __pa(ivector_table);
-
- if (tlb_type == hypervisor)
- sun4v_init_mondo_queues();
-
- init_send_mondo_info();
-
- if (tlb_type == hypervisor) {
- /* Load up the boot cpu's entries. */
- sun4v_register_mondo_queues(hard_smp_processor_id());
- }
-
- /* We need to clear any IRQ's pending in the soft interrupt
- * registers, a spurious one could be left around from the
- * PROM timer which we just disabled.
- */
- clear_softint(get_softint());
-
- /* Now that ivector table is initialized, it is safe
- * to receive IRQ vector traps. We will normally take
- * one or two right now, in case some device PROM used
- * to boot us wants to speak to us. We just ignore them.
- */
- __asm__ __volatile__("rdpr %%pstate, %%g1\n\t"
- "or %%g1, %0, %%g1\n\t"
- "wrpr %%g1, 0x0, %%pstate"
- : /* No outputs */
- : "i" (PSTATE_IE)
- : "g1");
-
- irq_desc[0].action = &timer_irq_action;
-}
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c
deleted file mode 100644
index dde52bcf5c64..000000000000
--- a/arch/sparc64/kernel/mdesc.c
+++ /dev/null
@@ -1,916 +0,0 @@
-/* mdesc.c: Sun4V machine description handling.
- *
- * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/lmb.h>
-#include <linux/log2.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/miscdevice.h>
-
-#include <asm/hypervisor.h>
-#include <asm/mdesc.h>
-#include <asm/prom.h>
-#include <asm/oplib.h>
-#include <asm/smp.h>
-
-/* Unlike the OBP device tree, the machine description is a full-on
- * DAG. An arbitrary number of ARCs are possible from one
- * node to other nodes and thus we can't use the OBP device_node
- * data structure to represent these nodes inside of the kernel.
- *
- * Actually, it isn't even a DAG, because there are back pointers
- * which create cycles in the graph.
- *
- * mdesc_hdr and mdesc_elem describe the layout of the data structure
- * we get from the Hypervisor.
- */
-struct mdesc_hdr {
- u32 version; /* Transport version */
- u32 node_sz; /* node block size */
- u32 name_sz; /* name block size */
- u32 data_sz; /* data block size */
-} __attribute__((aligned(16)));
-
-struct mdesc_elem {
- u8 tag;
-#define MD_LIST_END 0x00
-#define MD_NODE 0x4e
-#define MD_NODE_END 0x45
-#define MD_NOOP 0x20
-#define MD_PROP_ARC 0x61
-#define MD_PROP_VAL 0x76
-#define MD_PROP_STR 0x73
-#define MD_PROP_DATA 0x64
- u8 name_len;
- u16 resv;
- u32 name_offset;
- union {
- struct {
- u32 data_len;
- u32 data_offset;
- } data;
- u64 val;
- } d;
-};
-
-struct mdesc_mem_ops {
- struct mdesc_handle *(*alloc)(unsigned int mdesc_size);
- void (*free)(struct mdesc_handle *handle);
-};
-
-struct mdesc_handle {
- struct list_head list;
- struct mdesc_mem_ops *mops;
- void *self_base;
- atomic_t refcnt;
- unsigned int handle_size;
- struct mdesc_hdr mdesc;
-};
-
-static void mdesc_handle_init(struct mdesc_handle *hp,
- unsigned int handle_size,
- void *base)
-{
- BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1));
-
- memset(hp, 0, handle_size);
- INIT_LIST_HEAD(&hp->list);
- hp->self_base = base;
- atomic_set(&hp->refcnt, 1);
- hp->handle_size = handle_size;
-}
-
-static struct mdesc_handle * __init mdesc_lmb_alloc(unsigned int mdesc_size)
-{
- unsigned int handle_size, alloc_size;
- struct mdesc_handle *hp;
- unsigned long paddr;
-
- handle_size = (sizeof(struct mdesc_handle) -
- sizeof(struct mdesc_hdr) +
- mdesc_size);
- alloc_size = PAGE_ALIGN(handle_size);
-
- paddr = lmb_alloc(alloc_size, PAGE_SIZE);
-
- hp = NULL;
- if (paddr) {
- hp = __va(paddr);
- mdesc_handle_init(hp, handle_size, hp);
- }
- return hp;
-}
-
-static void mdesc_lmb_free(struct mdesc_handle *hp)
-{
- unsigned int alloc_size, handle_size = hp->handle_size;
- unsigned long start, end;
-
- BUG_ON(atomic_read(&hp->refcnt) != 0);
- BUG_ON(!list_empty(&hp->list));
-
- alloc_size = PAGE_ALIGN(handle_size);
-
- start = (unsigned long) hp;
- end = start + alloc_size;
-
- while (start < end) {
- struct page *p;
-
- p = virt_to_page(start);
- ClearPageReserved(p);
- __free_page(p);
- start += PAGE_SIZE;
- }
-}
-
-static struct mdesc_mem_ops lmb_mdesc_ops = {
- .alloc = mdesc_lmb_alloc,
- .free = mdesc_lmb_free,
-};
-
-static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size)
-{
- unsigned int handle_size;
- void *base;
-
- handle_size = (sizeof(struct mdesc_handle) -
- sizeof(struct mdesc_hdr) +
- mdesc_size);
-
- base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);
- if (base) {
- struct mdesc_handle *hp;
- unsigned long addr;
-
- addr = (unsigned long)base;
- addr = (addr + 15UL) & ~15UL;
- hp = (struct mdesc_handle *) addr;
-
- mdesc_handle_init(hp, handle_size, base);
- return hp;
- }
-
- return NULL;
-}
-
-static void mdesc_kfree(struct mdesc_handle *hp)
-{
- BUG_ON(atomic_read(&hp->refcnt) != 0);
- BUG_ON(!list_empty(&hp->list));
-
- kfree(hp->self_base);
-}
-
-static struct mdesc_mem_ops kmalloc_mdesc_memops = {
- .alloc = mdesc_kmalloc,
- .free = mdesc_kfree,
-};
-
-static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size,
- struct mdesc_mem_ops *mops)
-{
- struct mdesc_handle *hp = mops->alloc(mdesc_size);
-
- if (hp)
- hp->mops = mops;
-
- return hp;
-}
-
-static void mdesc_free(struct mdesc_handle *hp)
-{
- hp->mops->free(hp);
-}
-
-static struct mdesc_handle *cur_mdesc;
-static LIST_HEAD(mdesc_zombie_list);
-static DEFINE_SPINLOCK(mdesc_lock);
-
-struct mdesc_handle *mdesc_grab(void)
-{
- struct mdesc_handle *hp;
- unsigned long flags;
-
- spin_lock_irqsave(&mdesc_lock, flags);
- hp = cur_mdesc;
- if (hp)
- atomic_inc(&hp->refcnt);
- spin_unlock_irqrestore(&mdesc_lock, flags);
-
- return hp;
-}
-EXPORT_SYMBOL(mdesc_grab);
-
-void mdesc_release(struct mdesc_handle *hp)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mdesc_lock, flags);
- if (atomic_dec_and_test(&hp->refcnt)) {
- list_del_init(&hp->list);
- hp->mops->free(hp);
- }
- spin_unlock_irqrestore(&mdesc_lock, flags);
-}
-EXPORT_SYMBOL(mdesc_release);
-
-static DEFINE_MUTEX(mdesc_mutex);
-static struct mdesc_notifier_client *client_list;
-
-void mdesc_register_notifier(struct mdesc_notifier_client *client)
-{
- u64 node;
-
- mutex_lock(&mdesc_mutex);
- client->next = client_list;
- client_list = client;
-
- mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)
- client->add(cur_mdesc, node);
-
- mutex_unlock(&mdesc_mutex);
-}
-
-static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node)
-{
- const u64 *id;
- u64 a;
-
- id = NULL;
- mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
- u64 target;
-
- target = mdesc_arc_target(hp, a);
- id = mdesc_get_property(hp, target,
- "cfg-handle", NULL);
- if (id)
- break;
- }
-
- return id;
-}
-
-/* Run 'func' on nodes which are in A but not in B. */
-static void invoke_on_missing(const char *name,
- struct mdesc_handle *a,
- struct mdesc_handle *b,
- void (*func)(struct mdesc_handle *, u64))
-{
- u64 node;
-
- mdesc_for_each_node_by_name(a, node, name) {
- int found = 0, is_vdc_port = 0;
- const char *name_prop;
- const u64 *id;
- u64 fnode;
-
- name_prop = mdesc_get_property(a, node, "name", NULL);
- if (name_prop && !strcmp(name_prop, "vdc-port")) {
- is_vdc_port = 1;
- id = parent_cfg_handle(a, node);
- } else
- id = mdesc_get_property(a, node, "id", NULL);
-
- if (!id) {
- printk(KERN_ERR "MD: Cannot find ID for %s node.\n",
- (name_prop ? name_prop : name));
- continue;
- }
-
- mdesc_for_each_node_by_name(b, fnode, name) {
- const u64 *fid;
-
- if (is_vdc_port) {
- name_prop = mdesc_get_property(b, fnode,
- "name", NULL);
- if (!name_prop ||
- strcmp(name_prop, "vdc-port"))
- continue;
- fid = parent_cfg_handle(b, fnode);
- if (!fid) {
- printk(KERN_ERR "MD: Cannot find ID "
- "for vdc-port node.\n");
- continue;
- }
- } else
- fid = mdesc_get_property(b, fnode,
- "id", NULL);
-
- if (*id == *fid) {
- found = 1;
- break;
- }
- }
- if (!found)
- func(a, node);
- }
-}
-
-static void notify_one(struct mdesc_notifier_client *p,
- struct mdesc_handle *old_hp,
- struct mdesc_handle *new_hp)
-{
- invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);
- invoke_on_missing(p->node_name, new_hp, old_hp, p->add);
-}
-
-static void mdesc_notify_clients(struct mdesc_handle *old_hp,
- struct mdesc_handle *new_hp)
-{
- struct mdesc_notifier_client *p = client_list;
-
- while (p) {
- notify_one(p, old_hp, new_hp);
- p = p->next;
- }
-}
-
-void mdesc_update(void)
-{
- unsigned long len, real_len, status;
- struct mdesc_handle *hp, *orig_hp;
- unsigned long flags;
-
- mutex_lock(&mdesc_mutex);
-
- (void) sun4v_mach_desc(0UL, 0UL, &len);
-
- hp = mdesc_alloc(len, &kmalloc_mdesc_memops);
- if (!hp) {
- printk(KERN_ERR "MD: mdesc alloc fails\n");
- goto out;
- }
-
- status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
- if (status != HV_EOK || real_len > len) {
- printk(KERN_ERR "MD: mdesc reread fails with %lu\n",
- status);
- atomic_dec(&hp->refcnt);
- mdesc_free(hp);
- goto out;
- }
-
- spin_lock_irqsave(&mdesc_lock, flags);
- orig_hp = cur_mdesc;
- cur_mdesc = hp;
- spin_unlock_irqrestore(&mdesc_lock, flags);
-
- mdesc_notify_clients(orig_hp, hp);
-
- spin_lock_irqsave(&mdesc_lock, flags);
- if (atomic_dec_and_test(&orig_hp->refcnt))
- mdesc_free(orig_hp);
- else
- list_add(&orig_hp->list, &mdesc_zombie_list);
- spin_unlock_irqrestore(&mdesc_lock, flags);
-
-out:
- mutex_unlock(&mdesc_mutex);
-}
-
-static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc)
-{
- return (struct mdesc_elem *) (mdesc + 1);
-}
-
-static void *name_block(struct mdesc_hdr *mdesc)
-{
- return ((void *) node_block(mdesc)) + mdesc->node_sz;
-}
-
-static void *data_block(struct mdesc_hdr *mdesc)
-{
- return ((void *) name_block(mdesc)) + mdesc->name_sz;
-}
-
-u64 mdesc_node_by_name(struct mdesc_handle *hp,
- u64 from_node, const char *name)
-{
- struct mdesc_elem *ep = node_block(&hp->mdesc);
- const char *names = name_block(&hp->mdesc);
- u64 last_node = hp->mdesc.node_sz / 16;
- u64 ret;
-
- if (from_node == MDESC_NODE_NULL) {
- ret = from_node = 0;
- } else if (from_node >= last_node) {
- return MDESC_NODE_NULL;
- } else {
- ret = ep[from_node].d.val;
- }
-
- while (ret < last_node) {
- if (ep[ret].tag != MD_NODE)
- return MDESC_NODE_NULL;
- if (!strcmp(names + ep[ret].name_offset, name))
- break;
- ret = ep[ret].d.val;
- }
- if (ret >= last_node)
- ret = MDESC_NODE_NULL;
- return ret;
-}
-EXPORT_SYMBOL(mdesc_node_by_name);
-
-const void *mdesc_get_property(struct mdesc_handle *hp, u64 node,
- const char *name, int *lenp)
-{
- const char *names = name_block(&hp->mdesc);
- u64 last_node = hp->mdesc.node_sz / 16;
- void *data = data_block(&hp->mdesc);
- struct mdesc_elem *ep;
-
- if (node == MDESC_NODE_NULL || node >= last_node)
- return NULL;
-
- ep = node_block(&hp->mdesc) + node;
- ep++;
- for (; ep->tag != MD_NODE_END; ep++) {
- void *val = NULL;
- int len = 0;
-
- switch (ep->tag) {
- case MD_PROP_VAL:
- val = &ep->d.val;
- len = 8;
- break;
-
- case MD_PROP_STR:
- case MD_PROP_DATA:
- val = data + ep->d.data.data_offset;
- len = ep->d.data.data_len;
- break;
-
- default:
- break;
- }
- if (!val)
- continue;
-
- if (!strcmp(names + ep->name_offset, name)) {
- if (lenp)
- *lenp = len;
- return val;
- }
- }
-
- return NULL;
-}
-EXPORT_SYMBOL(mdesc_get_property);
-
-u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type)
-{
- struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
- const char *names = name_block(&hp->mdesc);
- u64 last_node = hp->mdesc.node_sz / 16;
-
- if (from == MDESC_NODE_NULL || from >= last_node)
- return MDESC_NODE_NULL;
-
- ep = base + from;
-
- ep++;
- for (; ep->tag != MD_NODE_END; ep++) {
- if (ep->tag != MD_PROP_ARC)
- continue;
-
- if (strcmp(names + ep->name_offset, arc_type))
- continue;
-
- return ep - base;
- }
-
- return MDESC_NODE_NULL;
-}
-EXPORT_SYMBOL(mdesc_next_arc);
-
-u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc)
-{
- struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
-
- ep = base + arc;
-
- return ep->d.val;
-}
-EXPORT_SYMBOL(mdesc_arc_target);
-
-const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)
-{
- struct mdesc_elem *ep, *base = node_block(&hp->mdesc);
- const char *names = name_block(&hp->mdesc);
- u64 last_node = hp->mdesc.node_sz / 16;
-
- if (node == MDESC_NODE_NULL || node >= last_node)
- return NULL;
-
- ep = base + node;
- if (ep->tag != MD_NODE)
- return NULL;
-
- return names + ep->name_offset;
-}
-EXPORT_SYMBOL(mdesc_node_name);
-
-static void __init report_platform_properties(void)
-{
- struct mdesc_handle *hp = mdesc_grab();
- u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform");
- const char *s;
- const u64 *v;
-
- if (pn == MDESC_NODE_NULL) {
- prom_printf("No platform node in machine-description.\n");
- prom_halt();
- }
-
- s = mdesc_get_property(hp, pn, "banner-name", NULL);
- printk("PLATFORM: banner-name [%s]\n", s);
- s = mdesc_get_property(hp, pn, "name", NULL);
- printk("PLATFORM: name [%s]\n", s);
-
- v = mdesc_get_property(hp, pn, "hostid", NULL);
- if (v)
- printk("PLATFORM: hostid [%08lx]\n", *v);
- v = mdesc_get_property(hp, pn, "serial#", NULL);
- if (v)
- printk("PLATFORM: serial# [%08lx]\n", *v);
- v = mdesc_get_property(hp, pn, "stick-frequency", NULL);
- printk("PLATFORM: stick-frequency [%08lx]\n", *v);
- v = mdesc_get_property(hp, pn, "mac-address", NULL);
- if (v)
- printk("PLATFORM: mac-address [%lx]\n", *v);
- v = mdesc_get_property(hp, pn, "watchdog-resolution", NULL);
- if (v)
- printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v);
- v = mdesc_get_property(hp, pn, "watchdog-max-timeout", NULL);
- if (v)
- printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v);
- v = mdesc_get_property(hp, pn, "max-cpus", NULL);
- if (v)
- printk("PLATFORM: max-cpus [%lu]\n", *v);
-
-#ifdef CONFIG_SMP
- {
- int max_cpu, i;
-
- if (v) {
- max_cpu = *v;
- if (max_cpu > NR_CPUS)
- max_cpu = NR_CPUS;
- } else {
- max_cpu = NR_CPUS;
- }
- for (i = 0; i < max_cpu; i++)
- cpu_set(i, cpu_possible_map);
- }
-#endif
-
- mdesc_release(hp);
-}
-
-static void __devinit fill_in_one_cache(cpuinfo_sparc *c,
- struct mdesc_handle *hp,
- u64 mp)
-{
- const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
- const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
- const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
- const char *type;
- int type_len;
-
- type = mdesc_get_property(hp, mp, "type", &type_len);
-
- switch (*level) {
- case 1:
- if (of_find_in_proplist(type, "instn", type_len)) {
- c->icache_size = *size;
- c->icache_line_size = *line_size;
- } else if (of_find_in_proplist(type, "data", type_len)) {
- c->dcache_size = *size;
- c->dcache_line_size = *line_size;
- }
- break;
-
- case 2:
- c->ecache_size = *size;
- c->ecache_line_size = *line_size;
- break;
-
- default:
- break;
- }
-
- if (*level == 1) {
- u64 a;
-
- mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
- u64 target = mdesc_arc_target(hp, a);
- const char *name = mdesc_node_name(hp, target);
-
- if (!strcmp(name, "cache"))
- fill_in_one_cache(c, hp, target);
- }
- }
-}
-
-static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp,
- int core_id)
-{
- u64 a;
-
- mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
- u64 t = mdesc_arc_target(hp, a);
- const char *name;
- const u64 *id;
-
- name = mdesc_node_name(hp, t);
- if (!strcmp(name, "cpu")) {
- id = mdesc_get_property(hp, t, "id", NULL);
- if (*id < NR_CPUS)
- cpu_data(*id).core_id = core_id;
- } else {
- u64 j;
-
- mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_BACK) {
- u64 n = mdesc_arc_target(hp, j);
- const char *n_name;
-
- n_name = mdesc_node_name(hp, n);
- if (strcmp(n_name, "cpu"))
- continue;
-
- id = mdesc_get_property(hp, n, "id", NULL);
- if (*id < NR_CPUS)
- cpu_data(*id).core_id = core_id;
- }
- }
- }
-}
-
-static void __devinit set_core_ids(struct mdesc_handle *hp)
-{
- int idx;
- u64 mp;
-
- idx = 1;
- mdesc_for_each_node_by_name(hp, mp, "cache") {
- const u64 *level;
- const char *type;
- int len;
-
- level = mdesc_get_property(hp, mp, "level", NULL);
- if (*level != 1)
- continue;
-
- type = mdesc_get_property(hp, mp, "type", &len);
- if (!of_find_in_proplist(type, "instn", len))
- continue;
-
- mark_core_ids(hp, mp, idx);
-
- idx++;
- }
-}
-
-static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp,
- int proc_id)
-{
- u64 a;
-
- mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
- u64 t = mdesc_arc_target(hp, a);
- const char *name;
- const u64 *id;
-
- name = mdesc_node_name(hp, t);
- if (strcmp(name, "cpu"))
- continue;
-
- id = mdesc_get_property(hp, t, "id", NULL);
- if (*id < NR_CPUS)
- cpu_data(*id).proc_id = proc_id;
- }
-}
-
-static void __devinit __set_proc_ids(struct mdesc_handle *hp,
- const char *exec_unit_name)
-{
- int idx;
- u64 mp;
-
- idx = 0;
- mdesc_for_each_node_by_name(hp, mp, exec_unit_name) {
- const char *type;
- int len;
-
- type = mdesc_get_property(hp, mp, "type", &len);
- if (!of_find_in_proplist(type, "int", len) &&
- !of_find_in_proplist(type, "integer", len))
- continue;
-
- mark_proc_ids(hp, mp, idx);
-
- idx++;
- }
-}
-
-static void __devinit set_proc_ids(struct mdesc_handle *hp)
-{
- __set_proc_ids(hp, "exec_unit");
- __set_proc_ids(hp, "exec-unit");
-}
-
-static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
- unsigned char def)
-{
- u64 val;
-
- if (!p)
- goto use_default;
- val = *p;
-
- if (!val || val >= 64)
- goto use_default;
-
- *mask = ((1U << val) * 64U) - 1U;
- return;
-
-use_default:
- *mask = ((1U << def) * 64U) - 1U;
-}
-
-static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
- struct trap_per_cpu *tb)
-{
- const u64 *val;
-
- val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
- get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);
-
- val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
- get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);
-
- val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
- get_one_mondo_bits(val, &tb->resum_qmask, 6);
-
- val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
- get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
-}
-
-void __cpuinit mdesc_fill_in_cpu_data(cpumask_t mask)
-{
- struct mdesc_handle *hp = mdesc_grab();
- u64 mp;
-
- ncpus_probed = 0;
- mdesc_for_each_node_by_name(hp, mp, "cpu") {
- const u64 *id = mdesc_get_property(hp, mp, "id", NULL);
- const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency", NULL);
- struct trap_per_cpu *tb;
- cpuinfo_sparc *c;
- int cpuid;
- u64 a;
-
- ncpus_probed++;
-
- cpuid = *id;
-
-#ifdef CONFIG_SMP
- if (cpuid >= NR_CPUS) {
- printk(KERN_WARNING "Ignoring CPU %d which is "
- ">= NR_CPUS (%d)\n",
- cpuid, NR_CPUS);
- continue;
- }
- if (!cpu_isset(cpuid, mask))
- continue;
-#else
- /* On uniprocessor we only want the values for the
- * real physical cpu the kernel booted onto, however
- * cpu_data() only has one entry at index 0.
- */
- if (cpuid != real_hard_smp_processor_id())
- continue;
- cpuid = 0;
-#endif
-
- c = &cpu_data(cpuid);
- c->clock_tick = *cfreq;
-
- tb = &trap_block[cpuid];
- get_mondo_data(hp, mp, tb);
-
- mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
- u64 j, t = mdesc_arc_target(hp, a);
- const char *t_name;
-
- t_name = mdesc_node_name(hp, t);
- if (!strcmp(t_name, "cache")) {
- fill_in_one_cache(c, hp, t);
- continue;
- }
-
- mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) {
- u64 n = mdesc_arc_target(hp, j);
- const char *n_name;
-
- n_name = mdesc_node_name(hp, n);
- if (!strcmp(n_name, "cache"))
- fill_in_one_cache(c, hp, n);
- }
- }
-
-#ifdef CONFIG_SMP
- cpu_set(cpuid, cpu_present_map);
-#endif
-
- c->core_id = 0;
- c->proc_id = -1;
- }
-
-#ifdef CONFIG_SMP
- sparc64_multi_core = 1;
-#endif
-
- set_core_ids(hp);
- set_proc_ids(hp);
-
- smp_fill_in_sib_core_maps();
-
- mdesc_release(hp);
-}
-
-static ssize_t mdesc_read(struct file *file, char __user *buf,
- size_t len, loff_t *offp)
-{
- struct mdesc_handle *hp = mdesc_grab();
- int err;
-
- if (!hp)
- return -ENODEV;
-
- err = hp->handle_size;
- if (len < hp->handle_size)
- err = -EMSGSIZE;
- else if (copy_to_user(buf, &hp->mdesc, hp->handle_size))
- err = -EFAULT;
- mdesc_release(hp);
-
- return err;
-}
-
-static const struct file_operations mdesc_fops = {
- .read = mdesc_read,
- .owner = THIS_MODULE,
-};
-
-static struct miscdevice mdesc_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "mdesc",
- .fops = &mdesc_fops,
-};
-
-static int __init mdesc_misc_init(void)
-{
- return misc_register(&mdesc_misc);
-}
-
-__initcall(mdesc_misc_init);
-
-void __init sun4v_mdesc_init(void)
-{
- struct mdesc_handle *hp;
- unsigned long len, real_len, status;
- cpumask_t mask;
-
- (void) sun4v_mach_desc(0UL, 0UL, &len);
-
- printk("MDESC: Size is %lu bytes.\n", len);
-
- hp = mdesc_alloc(len, &lmb_mdesc_ops);
- if (hp == NULL) {
- prom_printf("MDESC: alloc of %lu bytes failed.\n", len);
- prom_halt();
- }
-
- status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);
- if (status != HV_EOK || real_len > len) {
- prom_printf("sun4v_mach_desc fails, err(%lu), "
- "len(%lu), real_len(%lu)\n",
- status, len, real_len);
- mdesc_free(hp);
- prom_halt();
- }
-
- cur_mdesc = hp;
-
- report_platform_properties();
-
- cpus_setall(mask);
- mdesc_fill_in_cpu_data(mask);
-}
diff --git a/arch/sparc64/kernel/module.c b/arch/sparc64/kernel/module.c
deleted file mode 100644
index 158484bf5999..000000000000
--- a/arch/sparc64/kernel/module.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/* Kernel module help for sparc64.
- *
- * Copyright (C) 2001 Rusty Russell.
- * Copyright (C) 2002 David S. Miller.
- */
-
-#include <linux/moduleloader.h>
-#include <linux/kernel.h>
-#include <linux/elf.h>
-#include <linux/vmalloc.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#include <asm/processor.h>
-#include <asm/spitfire.h>
-
-static void *module_map(unsigned long size)
-{
- struct vm_struct *area;
-
- size = PAGE_ALIGN(size);
- if (!size || size > MODULES_LEN)
- return NULL;
-
- area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
- if (!area)
- return NULL;
-
- return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
-}
-
-void *module_alloc(unsigned long size)
-{
- void *ret;
-
- /* We handle the zero case fine, unlike vmalloc */
- if (size == 0)
- return NULL;
-
- ret = module_map(size);
- if (!ret)
- ret = ERR_PTR(-ENOMEM);
- else
- memset(ret, 0, size);
-
- return ret;
-}
-
-/* Free memory returned from module_core_alloc/module_init_alloc */
-void module_free(struct module *mod, void *module_region)
-{
- vfree(module_region);
- /* FIXME: If module_region == mod->init_region, trim exception
- table entries. */
-}
-
-/* Make generic code ignore STT_REGISTER dummy undefined symbols. */
-int module_frob_arch_sections(Elf_Ehdr *hdr,
- Elf_Shdr *sechdrs,
- char *secstrings,
- struct module *mod)
-{
- unsigned int symidx;
- Elf64_Sym *sym;
- const char *strtab;
- int i;
-
- for (symidx = 0; sechdrs[symidx].sh_type != SHT_SYMTAB; symidx++) {
- if (symidx == hdr->e_shnum-1) {
- printk("%s: no symtab found.\n", mod->name);
- return -ENOEXEC;
- }
- }
- sym = (Elf64_Sym *)sechdrs[symidx].sh_addr;
- strtab = (char *)sechdrs[sechdrs[symidx].sh_link].sh_addr;
-
- for (i = 1; i < sechdrs[symidx].sh_size / sizeof(Elf_Sym); i++) {
- if (sym[i].st_shndx == SHN_UNDEF &&
- ELF64_ST_TYPE(sym[i].st_info) == STT_REGISTER)
- sym[i].st_shndx = SHN_ABS;
- }
- return 0;
-}
-
-int apply_relocate(Elf64_Shdr *sechdrs,
- const char *strtab,
- unsigned int symindex,
- unsigned int relsec,
- struct module *me)
-{
- printk(KERN_ERR "module %s: non-ADD RELOCATION unsupported\n",
- me->name);
- return -ENOEXEC;
-}
-
-int apply_relocate_add(Elf64_Shdr *sechdrs,
- const char *strtab,
- unsigned int symindex,
- unsigned int relsec,
- struct module *me)
-{
- unsigned int i;
- Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
- Elf64_Sym *sym;
- u8 *location;
- u32 *loc32;
-
- for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
- Elf64_Addr v;
-
- /* This is where to make the change */
- location = (u8 *)sechdrs[sechdrs[relsec].sh_info].sh_addr
- + rel[i].r_offset;
- loc32 = (u32 *) location;
-
- BUG_ON(((u64)location >> (u64)32) != (u64)0);
-
- /* This is the symbol it is referring to. Note that all
- undefined symbols have been resolved. */
- sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
- + ELF64_R_SYM(rel[i].r_info);
- v = sym->st_value + rel[i].r_addend;
-
- switch (ELF64_R_TYPE(rel[i].r_info) & 0xff) {
- case R_SPARC_64:
- location[0] = v >> 56;
- location[1] = v >> 48;
- location[2] = v >> 40;
- location[3] = v >> 32;
- location[4] = v >> 24;
- location[5] = v >> 16;
- location[6] = v >> 8;
- location[7] = v >> 0;
- break;
-
- case R_SPARC_32:
- location[0] = v >> 24;
- location[1] = v >> 16;
- location[2] = v >> 8;
- location[3] = v >> 0;
- break;
-
- case R_SPARC_DISP32:
- v -= (Elf64_Addr) location;
- *loc32 = v;
- break;
-
- case R_SPARC_WDISP30:
- v -= (Elf64_Addr) location;
- *loc32 = (*loc32 & ~0x3fffffff) |
- ((v >> 2) & 0x3fffffff);
- break;
-
- case R_SPARC_WDISP22:
- v -= (Elf64_Addr) location;
- *loc32 = (*loc32 & ~0x3fffff) |
- ((v >> 2) & 0x3fffff);
- break;
-
- case R_SPARC_WDISP19:
- v -= (Elf64_Addr) location;
- *loc32 = (*loc32 & ~0x7ffff) |
- ((v >> 2) & 0x7ffff);
- break;
-
- case R_SPARC_LO10:
- *loc32 = (*loc32 & ~0x3ff) | (v & 0x3ff);
- break;
-
- case R_SPARC_HI22:
- *loc32 = (*loc32 & ~0x3fffff) |
- ((v >> 10) & 0x3fffff);
- break;
-
- case R_SPARC_OLO10:
- *loc32 = (*loc32 & ~0x1fff) |
- (((v & 0x3ff) +
- (ELF64_R_TYPE(rel[i].r_info) >> 8))
- & 0x1fff);
- break;
-
- default:
- printk(KERN_ERR "module %s: Unknown relocation: %x\n",
- me->name,
- (int) (ELF64_R_TYPE(rel[i].r_info) & 0xff));
- return -ENOEXEC;
- };
- }
- return 0;
-}
-
-int module_finalize(const Elf_Ehdr *hdr,
- const Elf_Shdr *sechdrs,
- struct module *me)
-{
- /* Cheetah's I-cache is fully coherent. */
- if (tlb_type == spitfire) {
- unsigned long va;
-
- flushw_all();
- for (va = 0; va < (PAGE_SIZE << 1); va += 32)
- spitfire_put_icache_tag(va, 0x0);
- __asm__ __volatile__("flush %g6");
- }
-
- return 0;
-}
-
-void module_arch_cleanup(struct module *mod)
-{
-}
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
deleted file mode 100644
index df2efb7fc14c..000000000000
--- a/arch/sparc64/kernel/of_device.c
+++ /dev/null
@@ -1,898 +0,0 @@
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/irq.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-
-void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name)
-{
- unsigned long ret = res->start + offset;
- struct resource *r;
-
- if (res->flags & IORESOURCE_MEM)
- r = request_mem_region(ret, size, name);
- else
- r = request_region(ret, size, name);
- if (!r)
- ret = 0;
-
- return (void __iomem *) ret;
-}
-EXPORT_SYMBOL(of_ioremap);
-
-void of_iounmap(struct resource *res, void __iomem *base, unsigned long size)
-{
- if (res->flags & IORESOURCE_MEM)
- release_mem_region((unsigned long) base, size);
- else
- release_region((unsigned long) base, size);
-}
-EXPORT_SYMBOL(of_iounmap);
-
-static int node_match(struct device *dev, void *data)
-{
- struct of_device *op = to_of_device(dev);
- struct device_node *dp = data;
-
- return (op->node == dp);
-}
-
-struct of_device *of_find_device_by_node(struct device_node *dp)
-{
- struct device *dev = bus_find_device(&of_platform_bus_type, NULL,
- dp, node_match);
-
- if (dev)
- return to_of_device(dev);
-
- return NULL;
-}
-EXPORT_SYMBOL(of_find_device_by_node);
-
-unsigned int irq_of_parse_and_map(struct device_node *node, int index)
-{
- struct of_device *op = of_find_device_by_node(node);
-
- if (!op || index >= op->num_irqs)
- return 0;
-
- return op->irqs[index];
-}
-EXPORT_SYMBOL(irq_of_parse_and_map);
-
-/* Take the archdata values for IOMMU, STC, and HOSTDATA found in
- * BUS and propagate to all child of_device objects.
- */
-void of_propagate_archdata(struct of_device *bus)
-{
- struct dev_archdata *bus_sd = &bus->dev.archdata;
- struct device_node *bus_dp = bus->node;
- struct device_node *dp;
-
- for (dp = bus_dp->child; dp; dp = dp->sibling) {
- struct of_device *op = of_find_device_by_node(dp);
-
- op->dev.archdata.iommu = bus_sd->iommu;
- op->dev.archdata.stc = bus_sd->stc;
- op->dev.archdata.host_controller = bus_sd->host_controller;
- op->dev.archdata.numa_node = bus_sd->numa_node;
-
- if (dp->child)
- of_propagate_archdata(op);
- }
-}
-
-struct bus_type of_platform_bus_type;
-EXPORT_SYMBOL(of_platform_bus_type);
-
-static inline u64 of_read_addr(const u32 *cell, int size)
-{
- u64 r = 0;
- while (size--)
- r = (r << 32) | *(cell++);
- return r;
-}
-
-static void __init get_cells(struct device_node *dp,
- int *addrc, int *sizec)
-{
- if (addrc)
- *addrc = of_n_addr_cells(dp);
- if (sizec)
- *sizec = of_n_size_cells(dp);
-}
-
-/* Max address size we deal with */
-#define OF_MAX_ADDR_CELLS 4
-
-struct of_bus {
- const char *name;
- const char *addr_prop_name;
- int (*match)(struct device_node *parent);
- void (*count_cells)(struct device_node *child,
- int *addrc, int *sizec);
- int (*map)(u32 *addr, const u32 *range,
- int na, int ns, int pna);
- unsigned long (*get_flags)(const u32 *addr, unsigned long);
-};
-
-/*
- * Default translator (generic bus)
- */
-
-static void of_bus_default_count_cells(struct device_node *dev,
- int *addrc, int *sizec)
-{
- get_cells(dev, addrc, sizec);
-}
-
-/* Make sure the least significant 64-bits are in-range. Even
- * for 3 or 4 cell values it is a good enough approximation.
- */
-static int of_out_of_range(const u32 *addr, const u32 *base,
- const u32 *size, int na, int ns)
-{
- u64 a = of_read_addr(addr, na);
- u64 b = of_read_addr(base, na);
-
- if (a < b)
- return 1;
-
- b += of_read_addr(size, ns);
- if (a >= b)
- return 1;
-
- return 0;
-}
-
-static int of_bus_default_map(u32 *addr, const u32 *range,
- int na, int ns, int pna)
-{
- u32 result[OF_MAX_ADDR_CELLS];
- int i;
-
- if (ns > 2) {
- printk("of_device: Cannot handle size cells (%d) > 2.", ns);
- return -EINVAL;
- }
-
- if (of_out_of_range(addr, range, range + na + pna, na, ns))
- return -EINVAL;
-
- /* Start with the parent range base. */
- memcpy(result, range + na, pna * 4);
-
- /* Add in the child address offset. */
- for (i = 0; i < na; i++)
- result[pna - 1 - i] +=
- (addr[na - 1 - i] -
- range[na - 1 - i]);
-
- memcpy(addr, result, pna * 4);
-
- return 0;
-}
-
-static unsigned long of_bus_default_get_flags(const u32 *addr, unsigned long flags)
-{
- if (flags)
- return flags;
- return IORESOURCE_MEM;
-}
-
-/*
- * PCI bus specific translator
- */
-
-static int of_bus_pci_match(struct device_node *np)
-{
- if (!strcmp(np->name, "pci")) {
- const char *model = of_get_property(np, "model", NULL);
-
- if (model && !strcmp(model, "SUNW,simba"))
- return 0;
-
- /* Do not do PCI specific frobbing if the
- * PCI bridge lacks a ranges property. We
- * want to pass it through up to the next
- * parent as-is, not with the PCI translate
- * method which chops off the top address cell.
- */
- if (!of_find_property(np, "ranges", NULL))
- return 0;
-
- return 1;
- }
-
- return 0;
-}
-
-static int of_bus_simba_match(struct device_node *np)
-{
- const char *model = of_get_property(np, "model", NULL);
-
- if (model && !strcmp(model, "SUNW,simba"))
- return 1;
-
- /* Treat PCI busses lacking ranges property just like
- * simba.
- */
- if (!strcmp(np->name, "pci")) {
- if (!of_find_property(np, "ranges", NULL))
- return 1;
- }
-
- return 0;
-}
-
-static int of_bus_simba_map(u32 *addr, const u32 *range,
- int na, int ns, int pna)
-{
- return 0;
-}
-
-static void of_bus_pci_count_cells(struct device_node *np,
- int *addrc, int *sizec)
-{
- if (addrc)
- *addrc = 3;
- if (sizec)
- *sizec = 2;
-}
-
-static int of_bus_pci_map(u32 *addr, const u32 *range,
- int na, int ns, int pna)
-{
- u32 result[OF_MAX_ADDR_CELLS];
- int i;
-
- /* Check address type match */
- if ((addr[0] ^ range[0]) & 0x03000000)
- return -EINVAL;
-
- if (of_out_of_range(addr + 1, range + 1, range + na + pna,
- na - 1, ns))
- return -EINVAL;
-
- /* Start with the parent range base. */
- memcpy(result, range + na, pna * 4);
-
- /* Add in the child address offset, skipping high cell. */
- for (i = 0; i < na - 1; i++)
- result[pna - 1 - i] +=
- (addr[na - 1 - i] -
- range[na - 1 - i]);
-
- memcpy(addr, result, pna * 4);
-
- return 0;
-}
-
-static unsigned long of_bus_pci_get_flags(const u32 *addr, unsigned long flags)
-{
- u32 w = addr[0];
-
- /* For PCI, we override whatever child busses may have used. */
- flags = 0;
- switch((w >> 24) & 0x03) {
- case 0x01:
- flags |= IORESOURCE_IO;
- break;
-
- case 0x02: /* 32 bits */
- case 0x03: /* 64 bits */
- flags |= IORESOURCE_MEM;
- break;
- }
- if (w & 0x40000000)
- flags |= IORESOURCE_PREFETCH;
- return flags;
-}
-
-/*
- * SBUS bus specific translator
- */
-
-static int of_bus_sbus_match(struct device_node *np)
-{
- return !strcmp(np->name, "sbus") ||
- !strcmp(np->name, "sbi");
-}
-
-static void of_bus_sbus_count_cells(struct device_node *child,
- int *addrc, int *sizec)
-{
- if (addrc)
- *addrc = 2;
- if (sizec)
- *sizec = 1;
-}
-
-/*
- * FHC/Central bus specific translator.
- *
- * This is just needed to hard-code the address and size cell
- * counts. 'fhc' and 'central' nodes lack the #address-cells and
- * #size-cells properties, and if you walk to the root on such
- * Enterprise boxes all you'll get is a #size-cells of 2 which is
- * not what we want to use.
- */
-static int of_bus_fhc_match(struct device_node *np)
-{
- return !strcmp(np->name, "fhc") ||
- !strcmp(np->name, "central");
-}
-
-#define of_bus_fhc_count_cells of_bus_sbus_count_cells
-
-/*
- * Array of bus specific translators
- */
-
-static struct of_bus of_busses[] = {
- /* PCI */
- {
- .name = "pci",
- .addr_prop_name = "assigned-addresses",
- .match = of_bus_pci_match,
- .count_cells = of_bus_pci_count_cells,
- .map = of_bus_pci_map,
- .get_flags = of_bus_pci_get_flags,
- },
- /* SIMBA */
- {
- .name = "simba",
- .addr_prop_name = "assigned-addresses",
- .match = of_bus_simba_match,
- .count_cells = of_bus_pci_count_cells,
- .map = of_bus_simba_map,
- .get_flags = of_bus_pci_get_flags,
- },
- /* SBUS */
- {
- .name = "sbus",
- .addr_prop_name = "reg",
- .match = of_bus_sbus_match,
- .count_cells = of_bus_sbus_count_cells,
- .map = of_bus_default_map,
- .get_flags = of_bus_default_get_flags,
- },
- /* FHC */
- {
- .name = "fhc",
- .addr_prop_name = "reg",
- .match = of_bus_fhc_match,
- .count_cells = of_bus_fhc_count_cells,
- .map = of_bus_default_map,
- .get_flags = of_bus_default_get_flags,
- },
- /* Default */
- {
- .name = "default",
- .addr_prop_name = "reg",
- .match = NULL,
- .count_cells = of_bus_default_count_cells,
- .map = of_bus_default_map,
- .get_flags = of_bus_default_get_flags,
- },
-};
-
-static struct of_bus *of_match_bus(struct device_node *np)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
- if (!of_busses[i].match || of_busses[i].match(np))
- return &of_busses[i];
- BUG();
- return NULL;
-}
-
-static int __init build_one_resource(struct device_node *parent,
- struct of_bus *bus,
- struct of_bus *pbus,
- u32 *addr,
- int na, int ns, int pna)
-{
- const u32 *ranges;
- int rone, rlen;
-
- ranges = of_get_property(parent, "ranges", &rlen);
- if (ranges == NULL || rlen == 0) {
- u32 result[OF_MAX_ADDR_CELLS];
- int i;
-
- memset(result, 0, pna * 4);
- for (i = 0; i < na; i++)
- result[pna - 1 - i] =
- addr[na - 1 - i];
-
- memcpy(addr, result, pna * 4);
- return 0;
- }
-
- /* Now walk through the ranges */
- rlen /= 4;
- rone = na + pna + ns;
- for (; rlen >= rone; rlen -= rone, ranges += rone) {
- if (!bus->map(addr, ranges, na, ns, pna))
- return 0;
- }
-
- /* When we miss an I/O space match on PCI, just pass it up
- * to the next PCI bridge and/or controller.
- */
- if (!strcmp(bus->name, "pci") &&
- (addr[0] & 0x03000000) == 0x01000000)
- return 0;
-
- return 1;
-}
-
-static int __init use_1to1_mapping(struct device_node *pp)
-{
- /* If we have a ranges property in the parent, use it. */
- if (of_find_property(pp, "ranges", NULL) != NULL)
- return 0;
-
- /* If the parent is the dma node of an ISA bus, pass
- * the translation up to the root.
- *
- * Some SBUS devices use intermediate nodes to express
- * hierarchy within the device itself. These aren't
- * real bus nodes, and don't have a 'ranges' property.
- * But, we should still pass the translation work up
- * to the SBUS itself.
- */
- if (!strcmp(pp->name, "dma") ||
- !strcmp(pp->name, "espdma") ||
- !strcmp(pp->name, "ledma") ||
- !strcmp(pp->name, "lebuffer"))
- return 0;
-
- /* Similarly for all PCI bridges, if we get this far
- * it lacks a ranges property, and this will include
- * cases like Simba.
- */
- if (!strcmp(pp->name, "pci"))
- return 0;
-
- return 1;
-}
-
-static int of_resource_verbose;
-
-static void __init build_device_resources(struct of_device *op,
- struct device *parent)
-{
- struct of_device *p_op;
- struct of_bus *bus;
- int na, ns;
- int index, num_reg;
- const void *preg;
-
- if (!parent)
- return;
-
- p_op = to_of_device(parent);
- bus = of_match_bus(p_op->node);
- bus->count_cells(op->node, &na, &ns);
-
- preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);
- if (!preg || num_reg == 0)
- return;
-
- /* Convert to num-cells. */
- num_reg /= 4;
-
- /* Convert to num-entries. */
- num_reg /= na + ns;
-
- /* Prevent overrunning the op->resources[] array. */
- if (num_reg > PROMREG_MAX) {
- printk(KERN_WARNING "%s: Too many regs (%d), "
- "limiting to %d.\n",
- op->node->full_name, num_reg, PROMREG_MAX);
- num_reg = PROMREG_MAX;
- }
-
- for (index = 0; index < num_reg; index++) {
- struct resource *r = &op->resource[index];
- u32 addr[OF_MAX_ADDR_CELLS];
- const u32 *reg = (preg + (index * ((na + ns) * 4)));
- struct device_node *dp = op->node;
- struct device_node *pp = p_op->node;
- struct of_bus *pbus, *dbus;
- u64 size, result = OF_BAD_ADDR;
- unsigned long flags;
- int dna, dns;
- int pna, pns;
-
- size = of_read_addr(reg + na, ns);
- memcpy(addr, reg, na * 4);
-
- flags = bus->get_flags(addr, 0);
-
- if (use_1to1_mapping(pp)) {
- result = of_read_addr(addr, na);
- goto build_res;
- }
-
- dna = na;
- dns = ns;
- dbus = bus;
-
- while (1) {
- dp = pp;
- pp = dp->parent;
- if (!pp) {
- result = of_read_addr(addr, dna);
- break;
- }
-
- pbus = of_match_bus(pp);
- pbus->count_cells(dp, &pna, &pns);
-
- if (build_one_resource(dp, dbus, pbus, addr,
- dna, dns, pna))
- break;
-
- flags = pbus->get_flags(addr, flags);
-
- dna = pna;
- dns = pns;
- dbus = pbus;
- }
-
- build_res:
- memset(r, 0, sizeof(*r));
-
- if (of_resource_verbose)
- printk("%s reg[%d] -> %lx\n",
- op->node->full_name, index,
- result);
-
- if (result != OF_BAD_ADDR) {
- if (tlb_type == hypervisor)
- result &= 0x0fffffffffffffffUL;
-
- r->start = result;
- r->end = result + size - 1;
- r->flags = flags;
- }
- r->name = op->node->name;
- }
-}
-
-static struct device_node * __init
-apply_interrupt_map(struct device_node *dp, struct device_node *pp,
- const u32 *imap, int imlen, const u32 *imask,
- unsigned int *irq_p)
-{
- struct device_node *cp;
- unsigned int irq = *irq_p;
- struct of_bus *bus;
- phandle handle;
- const u32 *reg;
- int na, num_reg, i;
-
- bus = of_match_bus(pp);
- bus->count_cells(dp, &na, NULL);
-
- reg = of_get_property(dp, "reg", &num_reg);
- if (!reg || !num_reg)
- return NULL;
-
- imlen /= ((na + 3) * 4);
- handle = 0;
- for (i = 0; i < imlen; i++) {
- int j;
-
- for (j = 0; j < na; j++) {
- if ((reg[j] & imask[j]) != imap[j])
- goto next;
- }
- if (imap[na] == irq) {
- handle = imap[na + 1];
- irq = imap[na + 2];
- break;
- }
-
- next:
- imap += (na + 3);
- }
- if (i == imlen) {
- /* Psycho and Sabre PCI controllers can have 'interrupt-map'
- * properties that do not include the on-board device
- * interrupts. Instead, the device's 'interrupts' property
- * is already a fully specified INO value.
- *
- * Handle this by deciding that, if we didn't get a
- * match in the parent's 'interrupt-map', and the
- * parent is an IRQ translater, then use the parent as
- * our IRQ controller.
- */
- if (pp->irq_trans)
- return pp;
-
- return NULL;
- }
-
- *irq_p = irq;
- cp = of_find_node_by_phandle(handle);
-
- return cp;
-}
-
-static unsigned int __init pci_irq_swizzle(struct device_node *dp,
- struct device_node *pp,
- unsigned int irq)
-{
- const struct linux_prom_pci_registers *regs;
- unsigned int bus, devfn, slot, ret;
-
- if (irq < 1 || irq > 4)
- return irq;
-
- regs = of_get_property(dp, "reg", NULL);
- if (!regs)
- return irq;
-
- bus = (regs->phys_hi >> 16) & 0xff;
- devfn = (regs->phys_hi >> 8) & 0xff;
- slot = (devfn >> 3) & 0x1f;
-
- if (pp->irq_trans) {
- /* Derived from Table 8-3, U2P User's Manual. This branch
- * is handling a PCI controller that lacks a proper set of
- * interrupt-map and interrupt-map-mask properties. The
- * Ultra-E450 is one example.
- *
- * The bit layout is BSSLL, where:
- * B: 0 on bus A, 1 on bus B
- * D: 2-bit slot number, derived from PCI device number as
- * (dev - 1) for bus A, or (dev - 2) for bus B
- * L: 2-bit line number
- */
- if (bus & 0x80) {
- /* PBM-A */
- bus = 0x00;
- slot = (slot - 1) << 2;
- } else {
- /* PBM-B */
- bus = 0x10;
- slot = (slot - 2) << 2;
- }
- irq -= 1;
-
- ret = (bus | slot | irq);
- } else {
- /* Going through a PCI-PCI bridge that lacks a set of
- * interrupt-map and interrupt-map-mask properties.
- */
- ret = ((irq - 1 + (slot & 3)) & 3) + 1;
- }
-
- return ret;
-}
-
-static int of_irq_verbose;
-
-static unsigned int __init build_one_device_irq(struct of_device *op,
- struct device *parent,
- unsigned int irq)
-{
- struct device_node *dp = op->node;
- struct device_node *pp, *ip;
- unsigned int orig_irq = irq;
- int nid;
-
- if (irq == 0xffffffff)
- return irq;
-
- if (dp->irq_trans) {
- irq = dp->irq_trans->irq_build(dp, irq,
- dp->irq_trans->data);
-
- if (of_irq_verbose)
- printk("%s: direct translate %x --> %x\n",
- dp->full_name, orig_irq, irq);
-
- goto out;
- }
-
- /* Something more complicated. Walk up to the root, applying
- * interrupt-map or bus specific translations, until we hit
- * an IRQ translator.
- *
- * If we hit a bus type or situation we cannot handle, we
- * stop and assume that the original IRQ number was in a
- * format which has special meaning to it's immediate parent.
- */
- pp = dp->parent;
- ip = NULL;
- while (pp) {
- const void *imap, *imsk;
- int imlen;
-
- imap = of_get_property(pp, "interrupt-map", &imlen);
- imsk = of_get_property(pp, "interrupt-map-mask", NULL);
- if (imap && imsk) {
- struct device_node *iret;
- int this_orig_irq = irq;
-
- iret = apply_interrupt_map(dp, pp,
- imap, imlen, imsk,
- &irq);
-
- if (of_irq_verbose)
- printk("%s: Apply [%s:%x] imap --> [%s:%x]\n",
- op->node->full_name,
- pp->full_name, this_orig_irq,
- (iret ? iret->full_name : "NULL"), irq);
-
- if (!iret)
- break;
-
- if (iret->irq_trans) {
- ip = iret;
- break;
- }
- } else {
- if (!strcmp(pp->name, "pci")) {
- unsigned int this_orig_irq = irq;
-
- irq = pci_irq_swizzle(dp, pp, irq);
- if (of_irq_verbose)
- printk("%s: PCI swizzle [%s] "
- "%x --> %x\n",
- op->node->full_name,
- pp->full_name, this_orig_irq,
- irq);
-
- }
-
- if (pp->irq_trans) {
- ip = pp;
- break;
- }
- }
- dp = pp;
- pp = pp->parent;
- }
- if (!ip)
- return orig_irq;
-
- irq = ip->irq_trans->irq_build(op->node, irq,
- ip->irq_trans->data);
- if (of_irq_verbose)
- printk("%s: Apply IRQ trans [%s] %x --> %x\n",
- op->node->full_name, ip->full_name, orig_irq, irq);
-
-out:
- nid = of_node_to_nid(dp);
- if (nid != -1) {
- cpumask_t numa_mask = node_to_cpumask(nid);
-
- irq_set_affinity(irq, &numa_mask);
- }
-
- return irq;
-}
-
-static struct of_device * __init scan_one_device(struct device_node *dp,
- struct device *parent)
-{
- struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
- const unsigned int *irq;
- struct dev_archdata *sd;
- int len, i;
-
- if (!op)
- return NULL;
-
- sd = &op->dev.archdata;
- sd->prom_node = dp;
- sd->op = op;
-
- op->node = dp;
-
- op->clock_freq = of_getintprop_default(dp, "clock-frequency",
- (25*1000*1000));
- op->portid = of_getintprop_default(dp, "upa-portid", -1);
- if (op->portid == -1)
- op->portid = of_getintprop_default(dp, "portid", -1);
-
- irq = of_get_property(dp, "interrupts", &len);
- if (irq) {
- memcpy(op->irqs, irq, len);
- op->num_irqs = len / 4;
- } else {
- op->num_irqs = 0;
- }
-
- /* Prevent overrunning the op->irqs[] array. */
- if (op->num_irqs > PROMINTR_MAX) {
- printk(KERN_WARNING "%s: Too many irqs (%d), "
- "limiting to %d.\n",
- dp->full_name, op->num_irqs, PROMINTR_MAX);
- op->num_irqs = PROMINTR_MAX;
- }
-
- build_device_resources(op, parent);
- for (i = 0; i < op->num_irqs; i++)
- op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);
-
- op->dev.parent = parent;
- op->dev.bus = &of_platform_bus_type;
- if (!parent)
- dev_set_name(&op->dev, "root");
- else
- dev_set_name(&op->dev, "%08x", dp->node);
-
- if (of_device_register(op)) {
- printk("%s: Could not register of device.\n",
- dp->full_name);
- kfree(op);
- op = NULL;
- }
-
- return op;
-}
-
-static void __init scan_tree(struct device_node *dp, struct device *parent)
-{
- while (dp) {
- struct of_device *op = scan_one_device(dp, parent);
-
- if (op)
- scan_tree(dp->child, &op->dev);
-
- dp = dp->sibling;
- }
-}
-
-static void __init scan_of_devices(void)
-{
- struct device_node *root = of_find_node_by_path("/");
- struct of_device *parent;
-
- parent = scan_one_device(root, NULL);
- if (!parent)
- return;
-
- scan_tree(root->child, &parent->dev);
-}
-
-static int __init of_bus_driver_init(void)
-{
- int err;
-
- err = of_bus_type_init(&of_platform_bus_type, "of");
- if (!err)
- scan_of_devices();
-
- return err;
-}
-
-postcore_initcall(of_bus_driver_init);
-
-static int __init of_debug(char *str)
-{
- int val = 0;
-
- get_option(&str, &val);
- if (val & 1)
- of_resource_verbose = 1;
- if (val & 2)
- of_irq_verbose = 1;
- return 1;
-}
-
-__setup("of_debug=", of_debug);
diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c
deleted file mode 100644
index 0d0cd815e83e..000000000000
--- a/arch/sparc64/kernel/pci_msi.c
+++ /dev/null
@@ -1,447 +0,0 @@
-/* pci_msi.c: Sparc64 MSI support common layer.
- *
- * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
- */
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-
-#include "pci_impl.h"
-
-static irqreturn_t sparc64_msiq_interrupt(int irq, void *cookie)
-{
- struct sparc64_msiq_cookie *msiq_cookie = cookie;
- struct pci_pbm_info *pbm = msiq_cookie->pbm;
- unsigned long msiqid = msiq_cookie->msiqid;
- const struct sparc64_msiq_ops *ops;
- unsigned long orig_head, head;
- int err;
-
- ops = pbm->msi_ops;
-
- err = ops->get_head(pbm, msiqid, &head);
- if (unlikely(err < 0))
- goto err_get_head;
-
- orig_head = head;
- for (;;) {
- unsigned long msi;
-
- err = ops->dequeue_msi(pbm, msiqid, &head, &msi);
- if (likely(err > 0)) {
- struct irq_desc *desc;
- unsigned int virt_irq;
-
- virt_irq = pbm->msi_irq_table[msi - pbm->msi_first];
- desc = irq_desc + virt_irq;
-
- desc->handle_irq(virt_irq, desc);
- }
-
- if (unlikely(err < 0))
- goto err_dequeue;
-
- if (err == 0)
- break;
- }
- if (likely(head != orig_head)) {
- err = ops->set_head(pbm, msiqid, head);
- if (unlikely(err < 0))
- goto err_set_head;
- }
- return IRQ_HANDLED;
-
-err_get_head:
- printk(KERN_EMERG "MSI: Get head on msiqid[%lu] gives error %d\n",
- msiqid, err);
- goto err_out;
-
-err_dequeue:
- printk(KERN_EMERG "MSI: Dequeue head[%lu] from msiqid[%lu] "
- "gives error %d\n",
- head, msiqid, err);
- goto err_out;
-
-err_set_head:
- printk(KERN_EMERG "MSI: Set head[%lu] on msiqid[%lu] "
- "gives error %d\n",
- head, msiqid, err);
- goto err_out;
-
-err_out:
- return IRQ_NONE;
-}
-
-static u32 pick_msiq(struct pci_pbm_info *pbm)
-{
- static DEFINE_SPINLOCK(rotor_lock);
- unsigned long flags;
- u32 ret, rotor;
-
- spin_lock_irqsave(&rotor_lock, flags);
-
- rotor = pbm->msiq_rotor;
- ret = pbm->msiq_first + rotor;
-
- if (++rotor >= pbm->msiq_num)
- rotor = 0;
- pbm->msiq_rotor = rotor;
-
- spin_unlock_irqrestore(&rotor_lock, flags);
-
- return ret;
-}
-
-
-static int alloc_msi(struct pci_pbm_info *pbm)
-{
- int i;
-
- for (i = 0; i < pbm->msi_num; i++) {
- if (!test_and_set_bit(i, pbm->msi_bitmap))
- return i + pbm->msi_first;
- }
-
- return -ENOENT;
-}
-
-static void free_msi(struct pci_pbm_info *pbm, int msi_num)
-{
- msi_num -= pbm->msi_first;
- clear_bit(msi_num, pbm->msi_bitmap);
-}
-
-static struct irq_chip msi_irq = {
- .typename = "PCI-MSI",
- .mask = mask_msi_irq,
- .unmask = unmask_msi_irq,
- .enable = unmask_msi_irq,
- .disable = mask_msi_irq,
- /* XXX affinity XXX */
-};
-
-static int sparc64_setup_msi_irq(unsigned int *virt_irq_p,
- struct pci_dev *pdev,
- struct msi_desc *entry)
-{
- struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
- const struct sparc64_msiq_ops *ops = pbm->msi_ops;
- struct msi_msg msg;
- int msi, err;
- u32 msiqid;
-
- *virt_irq_p = virt_irq_alloc(0, 0);
- err = -ENOMEM;
- if (!*virt_irq_p)
- goto out_err;
-
- set_irq_chip_and_handler_name(*virt_irq_p, &msi_irq,
- handle_simple_irq, "MSI");
-
- err = alloc_msi(pbm);
- if (unlikely(err < 0))
- goto out_virt_irq_free;
-
- msi = err;
-
- msiqid = pick_msiq(pbm);
-
- err = ops->msi_setup(pbm, msiqid, msi,
- (entry->msi_attrib.is_64 ? 1 : 0));
- if (err)
- goto out_msi_free;
-
- pbm->msi_irq_table[msi - pbm->msi_first] = *virt_irq_p;
-
- if (entry->msi_attrib.is_64) {
- msg.address_hi = pbm->msi64_start >> 32;
- msg.address_lo = pbm->msi64_start & 0xffffffff;
- } else {
- msg.address_hi = 0;
- msg.address_lo = pbm->msi32_start;
- }
- msg.data = msi;
-
- set_irq_msi(*virt_irq_p, entry);
- write_msi_msg(*virt_irq_p, &msg);
-
- return 0;
-
-out_msi_free:
- free_msi(pbm, msi);
-
-out_virt_irq_free:
- set_irq_chip(*virt_irq_p, NULL);
- virt_irq_free(*virt_irq_p);
- *virt_irq_p = 0;
-
-out_err:
- return err;
-}
-
-static void sparc64_teardown_msi_irq(unsigned int virt_irq,
- struct pci_dev *pdev)
-{
- struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
- const struct sparc64_msiq_ops *ops = pbm->msi_ops;
- unsigned int msi_num;
- int i, err;
-
- for (i = 0; i < pbm->msi_num; i++) {
- if (pbm->msi_irq_table[i] == virt_irq)
- break;
- }
- if (i >= pbm->msi_num) {
- printk(KERN_ERR "%s: teardown: No MSI for irq %u\n",
- pbm->name, virt_irq);
- return;
- }
-
- msi_num = pbm->msi_first + i;
- pbm->msi_irq_table[i] = ~0U;
-
- err = ops->msi_teardown(pbm, msi_num);
- if (err) {
- printk(KERN_ERR "%s: teardown: ops->teardown() on MSI %u, "
- "irq %u, gives error %d\n",
- pbm->name, msi_num, virt_irq, err);
- return;
- }
-
- free_msi(pbm, msi_num);
-
- set_irq_chip(virt_irq, NULL);
- virt_irq_free(virt_irq);
-}
-
-static int msi_bitmap_alloc(struct pci_pbm_info *pbm)
-{
- unsigned long size, bits_per_ulong;
-
- bits_per_ulong = sizeof(unsigned long) * 8;
- size = (pbm->msi_num + (bits_per_ulong - 1)) & ~(bits_per_ulong - 1);
- size /= 8;
- BUG_ON(size % sizeof(unsigned long));
-
- pbm->msi_bitmap = kzalloc(size, GFP_KERNEL);
- if (!pbm->msi_bitmap)
- return -ENOMEM;
-
- return 0;
-}
-
-static void msi_bitmap_free(struct pci_pbm_info *pbm)
-{
- kfree(pbm->msi_bitmap);
- pbm->msi_bitmap = NULL;
-}
-
-static int msi_table_alloc(struct pci_pbm_info *pbm)
-{
- int size, i;
-
- size = pbm->msiq_num * sizeof(struct sparc64_msiq_cookie);
- pbm->msiq_irq_cookies = kzalloc(size, GFP_KERNEL);
- if (!pbm->msiq_irq_cookies)
- return -ENOMEM;
-
- for (i = 0; i < pbm->msiq_num; i++) {
- struct sparc64_msiq_cookie *p;
-
- p = &pbm->msiq_irq_cookies[i];
- p->pbm = pbm;
- p->msiqid = pbm->msiq_first + i;
- }
-
- size = pbm->msi_num * sizeof(unsigned int);
- pbm->msi_irq_table = kzalloc(size, GFP_KERNEL);
- if (!pbm->msi_irq_table) {
- kfree(pbm->msiq_irq_cookies);
- pbm->msiq_irq_cookies = NULL;
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void msi_table_free(struct pci_pbm_info *pbm)
-{
- kfree(pbm->msiq_irq_cookies);
- pbm->msiq_irq_cookies = NULL;
-
- kfree(pbm->msi_irq_table);
- pbm->msi_irq_table = NULL;
-}
-
-static int bringup_one_msi_queue(struct pci_pbm_info *pbm,
- const struct sparc64_msiq_ops *ops,
- unsigned long msiqid,
- unsigned long devino)
-{
- int irq = ops->msiq_build_irq(pbm, msiqid, devino);
- int err, nid;
-
- if (irq < 0)
- return irq;
-
- nid = pbm->numa_node;
- if (nid != -1) {
- cpumask_t numa_mask = node_to_cpumask(nid);
-
- irq_set_affinity(irq, &numa_mask);
- }
- err = request_irq(irq, sparc64_msiq_interrupt, 0,
- "MSIQ",
- &pbm->msiq_irq_cookies[msiqid - pbm->msiq_first]);
- if (err)
- return err;
-
- return 0;
-}
-
-static int sparc64_bringup_msi_queues(struct pci_pbm_info *pbm,
- const struct sparc64_msiq_ops *ops)
-{
- int i;
-
- for (i = 0; i < pbm->msiq_num; i++) {
- unsigned long msiqid = i + pbm->msiq_first;
- unsigned long devino = i + pbm->msiq_first_devino;
- int err;
-
- err = bringup_one_msi_queue(pbm, ops, msiqid, devino);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-void sparc64_pbm_msi_init(struct pci_pbm_info *pbm,
- const struct sparc64_msiq_ops *ops)
-{
- const u32 *val;
- int len;
-
- val = of_get_property(pbm->op->node, "#msi-eqs", &len);
- if (!val || len != 4)
- goto no_msi;
- pbm->msiq_num = *val;
- if (pbm->msiq_num) {
- const struct msiq_prop {
- u32 first_msiq;
- u32 num_msiq;
- u32 first_devino;
- } *mqp;
- const struct msi_range_prop {
- u32 first_msi;
- u32 num_msi;
- } *mrng;
- const struct addr_range_prop {
- u32 msi32_high;
- u32 msi32_low;
- u32 msi32_len;
- u32 msi64_high;
- u32 msi64_low;
- u32 msi64_len;
- } *arng;
-
- val = of_get_property(pbm->op->node, "msi-eq-size", &len);
- if (!val || len != 4)
- goto no_msi;
-
- pbm->msiq_ent_count = *val;
-
- mqp = of_get_property(pbm->op->node,
- "msi-eq-to-devino", &len);
- if (!mqp)
- mqp = of_get_property(pbm->op->node,
- "msi-eq-devino", &len);
- if (!mqp || len != sizeof(struct msiq_prop))
- goto no_msi;
-
- pbm->msiq_first = mqp->first_msiq;
- pbm->msiq_first_devino = mqp->first_devino;
-
- val = of_get_property(pbm->op->node, "#msi", &len);
- if (!val || len != 4)
- goto no_msi;
- pbm->msi_num = *val;
-
- mrng = of_get_property(pbm->op->node, "msi-ranges", &len);
- if (!mrng || len != sizeof(struct msi_range_prop))
- goto no_msi;
- pbm->msi_first = mrng->first_msi;
-
- val = of_get_property(pbm->op->node, "msi-data-mask", &len);
- if (!val || len != 4)
- goto no_msi;
- pbm->msi_data_mask = *val;
-
- val = of_get_property(pbm->op->node, "msix-data-width", &len);
- if (!val || len != 4)
- goto no_msi;
- pbm->msix_data_width = *val;
-
- arng = of_get_property(pbm->op->node, "msi-address-ranges",
- &len);
- if (!arng || len != sizeof(struct addr_range_prop))
- goto no_msi;
- pbm->msi32_start = ((u64)arng->msi32_high << 32) |
- (u64) arng->msi32_low;
- pbm->msi64_start = ((u64)arng->msi64_high << 32) |
- (u64) arng->msi64_low;
- pbm->msi32_len = arng->msi32_len;
- pbm->msi64_len = arng->msi64_len;
-
- if (msi_bitmap_alloc(pbm))
- goto no_msi;
-
- if (msi_table_alloc(pbm)) {
- msi_bitmap_free(pbm);
- goto no_msi;
- }
-
- if (ops->msiq_alloc(pbm)) {
- msi_table_free(pbm);
- msi_bitmap_free(pbm);
- goto no_msi;
- }
-
- if (sparc64_bringup_msi_queues(pbm, ops)) {
- ops->msiq_free(pbm);
- msi_table_free(pbm);
- msi_bitmap_free(pbm);
- goto no_msi;
- }
-
- printk(KERN_INFO "%s: MSI Queue first[%u] num[%u] count[%u] "
- "devino[0x%x]\n",
- pbm->name,
- pbm->msiq_first, pbm->msiq_num,
- pbm->msiq_ent_count,
- pbm->msiq_first_devino);
- printk(KERN_INFO "%s: MSI first[%u] num[%u] mask[0x%x] "
- "width[%u]\n",
- pbm->name,
- pbm->msi_first, pbm->msi_num, pbm->msi_data_mask,
- pbm->msix_data_width);
- printk(KERN_INFO "%s: MSI addr32[0x%lx:0x%x] "
- "addr64[0x%lx:0x%x]\n",
- pbm->name,
- pbm->msi32_start, pbm->msi32_len,
- pbm->msi64_start, pbm->msi64_len);
- printk(KERN_INFO "%s: MSI queues at RA [%016lx]\n",
- pbm->name,
- __pa(pbm->msi_queues));
-
- pbm->msi_ops = ops;
- pbm->setup_msi_irq = sparc64_setup_msi_irq;
- pbm->teardown_msi_irq = sparc64_teardown_msi_irq;
- }
- return;
-
-no_msi:
- pbm->msiq_num = 0;
- printk(KERN_INFO "%s: No MSI support.\n", pbm->name);
-}
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
deleted file mode 100644
index dbba82f9b142..000000000000
--- a/arch/sparc64/kernel/prom.c
+++ /dev/null
@@ -1,1684 +0,0 @@
-/*
- * Procedures for creating, accessing and interpreting the device tree.
- *
- * Paul Mackerras August 1996.
- * Copyright (C) 1996-2005 Paul Mackerras.
- *
- * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
- * {engebret|bergner}@us.ibm.com
- *
- * Adapted for sparc64 by David S. Miller davem@davemloft.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.
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/lmb.h>
-#include <linux/of_device.h>
-
-#include <asm/prom.h>
-#include <asm/oplib.h>
-#include <asm/irq.h>
-#include <asm/asi.h>
-#include <asm/upa.h>
-#include <asm/smp.h>
-
-extern struct device_node *allnodes; /* temporary while merging */
-
-extern rwlock_t devtree_lock; /* temporary while merging */
-
-struct device_node *of_find_node_by_phandle(phandle handle)
-{
- struct device_node *np;
-
- for (np = allnodes; np; np = np->allnext)
- if (np->node == handle)
- break;
-
- return np;
-}
-EXPORT_SYMBOL(of_find_node_by_phandle);
-
-int of_getintprop_default(struct device_node *np, const char *name, int def)
-{
- struct property *prop;
- int len;
-
- prop = of_find_property(np, name, &len);
- if (!prop || len != 4)
- return def;
-
- return *(int *) prop->value;
-}
-EXPORT_SYMBOL(of_getintprop_default);
-
-DEFINE_MUTEX(of_set_property_mutex);
-EXPORT_SYMBOL(of_set_property_mutex);
-
-int of_set_property(struct device_node *dp, const char *name, void *val, int len)
-{
- struct property **prevp;
- void *new_val;
- int err;
-
- new_val = kmalloc(len, GFP_KERNEL);
- if (!new_val)
- return -ENOMEM;
-
- memcpy(new_val, val, len);
-
- err = -ENODEV;
-
- write_lock(&devtree_lock);
- prevp = &dp->properties;
- while (*prevp) {
- struct property *prop = *prevp;
-
- if (!strcasecmp(prop->name, name)) {
- void *old_val = prop->value;
- int ret;
-
- mutex_lock(&of_set_property_mutex);
- ret = prom_setprop(dp->node, name, val, len);
- mutex_unlock(&of_set_property_mutex);
-
- err = -EINVAL;
- if (ret >= 0) {
- prop->value = new_val;
- prop->length = len;
-
- if (OF_IS_DYNAMIC(prop))
- kfree(old_val);
-
- OF_MARK_DYNAMIC(prop);
-
- err = 0;
- }
- break;
- }
- prevp = &(*prevp)->next;
- }
- write_unlock(&devtree_lock);
-
- /* XXX Upate procfs if necessary... */
-
- return err;
-}
-EXPORT_SYMBOL(of_set_property);
-
-int of_find_in_proplist(const char *list, const char *match, int len)
-{
- while (len > 0) {
- int l;
-
- if (!strcmp(list, match))
- return 1;
- l = strlen(list) + 1;
- list += l;
- len -= l;
- }
- return 0;
-}
-EXPORT_SYMBOL(of_find_in_proplist);
-
-static unsigned int prom_early_allocated __initdata;
-
-static void * __init prom_early_alloc(unsigned long size)
-{
- unsigned long paddr = lmb_alloc(size, SMP_CACHE_BYTES);
- void *ret;
-
- if (!paddr) {
- prom_printf("prom_early_alloc(%lu) failed\n");
- prom_halt();
- }
-
- ret = __va(paddr);
- memset(ret, 0, size);
- prom_early_allocated += size;
-
- return ret;
-}
-
-#ifdef CONFIG_PCI
-/* PSYCHO interrupt mapping support. */
-#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
-#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
- else
- return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
-}
-
-#define PSYCHO_OBIO_IMAP_BASE 0x1000UL
-
-#define PSYCHO_ONBOARD_IRQ_BASE 0x20
-#define psycho_onboard_imap_offset(__ino) \
- (PSYCHO_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3))
-
-#define PSYCHO_ICLR_A_SLOT0 0x1400UL
-#define PSYCHO_ICLR_SCSI 0x1800UL
-
-#define psycho_iclr_offset(ino) \
- ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-static unsigned int psycho_irq_build(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- unsigned long controller_regs = (unsigned long) _data;
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
-
- ino &= 0x3f;
- if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = psycho_pcislot_imap_offset(ino);
- } else {
- /* Onboard device */
- imap_off = psycho_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = controller_regs + imap_off;
-
- iclr_off = psycho_iclr_offset(ino);
- iclr = controller_regs + iclr_off;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- return build_irq(inofixup, iclr, imap);
-}
-
-static void __init psycho_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = psycho_irq_build;
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = (void *) regs[2].phys_addr;
-}
-
-#define sabre_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-
-struct sabre_irq_data {
- unsigned long controller_regs;
- unsigned int pci_first_busno;
-};
-#define SABRE_CONFIGSPACE 0x001000000UL
-#define SABRE_WRSYNC 0x1c20UL
-
-#define SABRE_CONFIG_BASE(CONFIG_SPACE) \
- (CONFIG_SPACE | (1UL << 24))
-#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \
- (((unsigned long)(BUS) << 16) | \
- ((unsigned long)(DEVFN) << 8) | \
- ((unsigned long)(REG)))
-
-/* When a device lives behind a bridge deeper in the PCI bus topology
- * than APB, a special sequence must run to make sure all pending DMA
- * transfers at the time of IRQ delivery are visible in the coherency
- * domain by the cpu. This sequence is to perform a read on the far
- * side of the non-APB bridge, then perform a read of Sabre's DMA
- * write-sync register.
- */
-static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- unsigned int phys_hi = (unsigned int) (unsigned long) _arg1;
- struct sabre_irq_data *irq_data = _arg2;
- unsigned long controller_regs = irq_data->controller_regs;
- unsigned long sync_reg = controller_regs + SABRE_WRSYNC;
- unsigned long config_space = controller_regs + SABRE_CONFIGSPACE;
- unsigned int bus, devfn;
- u16 _unused;
-
- config_space = SABRE_CONFIG_BASE(config_space);
-
- bus = (phys_hi >> 16) & 0xff;
- devfn = (phys_hi >> 8) & 0xff;
-
- config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00);
-
- __asm__ __volatile__("membar #Sync\n\t"
- "lduha [%1] %2, %0\n\t"
- "membar #Sync"
- : "=r" (_unused)
- : "r" ((u16 *) config_space),
- "i" (ASI_PHYS_BYPASS_EC_E_L)
- : "memory");
-
- sabre_read(sync_reg);
-}
-
-#define SABRE_IMAP_A_SLOT0 0x0c00UL
-#define SABRE_IMAP_B_SLOT0 0x0c20UL
-#define SABRE_ICLR_A_SLOT0 0x1400UL
-#define SABRE_ICLR_B_SLOT0 0x1480UL
-#define SABRE_ICLR_SCSI 0x1800UL
-#define SABRE_ICLR_ETH 0x1808UL
-#define SABRE_ICLR_BPP 0x1810UL
-#define SABRE_ICLR_AU_REC 0x1818UL
-#define SABRE_ICLR_AU_PLAY 0x1820UL
-#define SABRE_ICLR_PFAIL 0x1828UL
-#define SABRE_ICLR_KMS 0x1830UL
-#define SABRE_ICLR_FLPY 0x1838UL
-#define SABRE_ICLR_SHW 0x1840UL
-#define SABRE_ICLR_KBD 0x1848UL
-#define SABRE_ICLR_MS 0x1850UL
-#define SABRE_ICLR_SER 0x1858UL
-#define SABRE_ICLR_UE 0x1870UL
-#define SABRE_ICLR_CE 0x1878UL
-#define SABRE_ICLR_PCIERR 0x1880UL
-
-static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return SABRE_IMAP_A_SLOT0 + (slot * 8);
- else
- return SABRE_IMAP_B_SLOT0 + (slot * 8);
-}
-
-#define SABRE_OBIO_IMAP_BASE 0x1000UL
-#define SABRE_ONBOARD_IRQ_BASE 0x20
-#define sabre_onboard_imap_offset(__ino) \
- (SABRE_OBIO_IMAP_BASE + (((__ino) & 0x1f) << 3))
-
-#define sabre_iclr_offset(ino) \
- ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-static int sabre_device_needs_wsync(struct device_node *dp)
-{
- struct device_node *parent = dp->parent;
- const char *parent_model, *parent_compat;
-
- /* This traversal up towards the root is meant to
- * handle two cases:
- *
- * 1) non-PCI bus sitting under PCI, such as 'ebus'
- * 2) the PCI controller interrupts themselves, which
- * will use the sabre_irq_build but do not need
- * the DMA synchronization handling
- */
- while (parent) {
- if (!strcmp(parent->type, "pci"))
- break;
- parent = parent->parent;
- }
-
- if (!parent)
- return 0;
-
- parent_model = of_get_property(parent,
- "model", NULL);
- if (parent_model &&
- (!strcmp(parent_model, "SUNW,sabre") ||
- !strcmp(parent_model, "SUNW,simba")))
- return 0;
-
- parent_compat = of_get_property(parent,
- "compatible", NULL);
- if (parent_compat &&
- (!strcmp(parent_compat, "pci108e,a000") ||
- !strcmp(parent_compat, "pci108e,a001")))
- return 0;
-
- return 1;
-}
-
-static unsigned int sabre_irq_build(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- struct sabre_irq_data *irq_data = _data;
- unsigned long controller_regs = irq_data->controller_regs;
- const struct linux_prom_pci_registers *regs;
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
- int virt_irq;
-
- ino &= 0x3f;
- if (ino < SABRE_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = sabre_pcislot_imap_offset(ino);
- } else {
- /* onboard device */
- imap_off = sabre_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = controller_regs + imap_off;
-
- iclr_off = sabre_iclr_offset(ino);
- iclr = controller_regs + iclr_off;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- virt_irq = build_irq(inofixup, iclr, imap);
-
- /* If the parent device is a PCI<->PCI bridge other than
- * APB, we have to install a pre-handler to ensure that
- * all pending DMA is drained before the interrupt handler
- * is run.
- */
- regs = of_get_property(dp, "reg", NULL);
- if (regs && sabre_device_needs_wsync(dp)) {
- irq_install_pre_handler(virt_irq,
- sabre_wsync_handler,
- (void *) (long) regs->phys_hi,
- (void *) irq_data);
- }
-
- return virt_irq;
-}
-
-static void __init sabre_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
- struct sabre_irq_data *irq_data;
- const u32 *busrange;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = sabre_irq_build;
-
- irq_data = prom_early_alloc(sizeof(struct sabre_irq_data));
-
- regs = of_get_property(dp, "reg", NULL);
- irq_data->controller_regs = regs[0].phys_addr;
-
- busrange = of_get_property(dp, "bus-range", NULL);
- irq_data->pci_first_busno = busrange[0];
-
- dp->irq_trans->data = irq_data;
-}
-
-/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
- * imap/iclr registers are per-PBM.
- */
-#define SCHIZO_IMAP_BASE 0x1000UL
-#define SCHIZO_ICLR_BASE 0x1400UL
-
-static unsigned long schizo_imap_offset(unsigned long ino)
-{
- return SCHIZO_IMAP_BASE + (ino * 8UL);
-}
-
-static unsigned long schizo_iclr_offset(unsigned long ino)
-{
- return SCHIZO_ICLR_BASE + (ino * 8UL);
-}
-
-static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
- unsigned int ino)
-{
-
- return pbm_regs + schizo_iclr_offset(ino);
-}
-
-static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
- unsigned int ino)
-{
- return pbm_regs + schizo_imap_offset(ino);
-}
-
-#define schizo_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-#define schizo_write(__reg, __val) \
- __asm__ __volatile__("stxa %0, [%1] %2" \
- : /* no outputs */ \
- : "r" (__val), "r" (__reg), \
- "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory")
-
-static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- unsigned long sync_reg = (unsigned long) _arg2;
- u64 mask = 1UL << (ino & IMAP_INO);
- u64 val;
- int limit;
-
- schizo_write(sync_reg, mask);
-
- limit = 100000;
- val = 0;
- while (--limit) {
- val = schizo_read(sync_reg);
- if (!(val & mask))
- break;
- }
- if (limit <= 0) {
- printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
- val, mask);
- }
-
- if (_arg1) {
- static unsigned char cacheline[64]
- __attribute__ ((aligned (64)));
-
- __asm__ __volatile__("rd %%fprs, %0\n\t"
- "or %0, %4, %1\n\t"
- "wr %1, 0x0, %%fprs\n\t"
- "stda %%f0, [%5] %6\n\t"
- "wr %0, 0x0, %%fprs\n\t"
- "membar #Sync"
- : "=&r" (mask), "=&r" (val)
- : "0" (mask), "1" (val),
- "i" (FPRS_FEF), "r" (&cacheline[0]),
- "i" (ASI_BLK_COMMIT_P));
- }
-}
-
-struct schizo_irq_data {
- unsigned long pbm_regs;
- unsigned long sync_reg;
- u32 portid;
- int chip_version;
-};
-
-static unsigned int schizo_irq_build(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- struct schizo_irq_data *irq_data = _data;
- unsigned long pbm_regs = irq_data->pbm_regs;
- unsigned long imap, iclr;
- int ign_fixup;
- int virt_irq;
- int is_tomatillo;
-
- ino &= 0x3f;
-
- /* Now build the IRQ bucket. */
- imap = schizo_ino_to_imap(pbm_regs, ino);
- iclr = schizo_ino_to_iclr(pbm_regs, ino);
-
- /* On Schizo, no inofixup occurs. This is because each
- * INO has it's own IMAP register. On Psycho and Sabre
- * there is only one IMAP register for each PCI slot even
- * though four different INOs can be generated by each
- * PCI slot.
- *
- * But, for JBUS variants (essentially, Tomatillo), we have
- * to fixup the lowest bit of the interrupt group number.
- */
- ign_fixup = 0;
-
- is_tomatillo = (irq_data->sync_reg != 0UL);
-
- if (is_tomatillo) {
- if (irq_data->portid & 1)
- ign_fixup = (1 << 6);
- }
-
- virt_irq = build_irq(ign_fixup, iclr, imap);
-
- if (is_tomatillo) {
- irq_install_pre_handler(virt_irq,
- tomatillo_wsync_handler,
- ((irq_data->chip_version <= 4) ?
- (void *) 1 : (void *) 0),
- (void *) irq_data->sync_reg);
- }
-
- return virt_irq;
-}
-
-static void __init __schizo_irq_trans_init(struct device_node *dp,
- int is_tomatillo)
-{
- const struct linux_prom64_registers *regs;
- struct schizo_irq_data *irq_data;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = schizo_irq_build;
-
- irq_data = prom_early_alloc(sizeof(struct schizo_irq_data));
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = irq_data;
-
- irq_data->pbm_regs = regs[0].phys_addr;
- if (is_tomatillo)
- irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL;
- else
- irq_data->sync_reg = 0UL;
- irq_data->portid = of_getintprop_default(dp, "portid", 0);
- irq_data->chip_version = of_getintprop_default(dp, "version#", 0);
-}
-
-static void __init schizo_irq_trans_init(struct device_node *dp)
-{
- __schizo_irq_trans_init(dp, 0);
-}
-
-static void __init tomatillo_irq_trans_init(struct device_node *dp)
-{
- __schizo_irq_trans_init(dp, 1);
-}
-
-static unsigned int pci_sun4v_irq_build(struct device_node *dp,
- unsigned int devino,
- void *_data)
-{
- u32 devhandle = (u32) (unsigned long) _data;
-
- return sun4v_build_irq(devhandle, devino);
-}
-
-static void __init pci_sun4v_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = pci_sun4v_irq_build;
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = (void *) (unsigned long)
- ((regs->phys_addr >> 32UL) & 0x0fffffff);
-}
-
-struct fire_irq_data {
- unsigned long pbm_regs;
- u32 portid;
-};
-
-#define FIRE_IMAP_BASE 0x001000
-#define FIRE_ICLR_BASE 0x001400
-
-static unsigned long fire_imap_offset(unsigned long ino)
-{
- return FIRE_IMAP_BASE + (ino * 8UL);
-}
-
-static unsigned long fire_iclr_offset(unsigned long ino)
-{
- return FIRE_ICLR_BASE + (ino * 8UL);
-}
-
-static unsigned long fire_ino_to_iclr(unsigned long pbm_regs,
- unsigned int ino)
-{
- return pbm_regs + fire_iclr_offset(ino);
-}
-
-static unsigned long fire_ino_to_imap(unsigned long pbm_regs,
- unsigned int ino)
-{
- return pbm_regs + fire_imap_offset(ino);
-}
-
-static unsigned int fire_irq_build(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- struct fire_irq_data *irq_data = _data;
- unsigned long pbm_regs = irq_data->pbm_regs;
- unsigned long imap, iclr;
- unsigned long int_ctrlr;
-
- ino &= 0x3f;
-
- /* Now build the IRQ bucket. */
- imap = fire_ino_to_imap(pbm_regs, ino);
- iclr = fire_ino_to_iclr(pbm_regs, ino);
-
- /* Set the interrupt controller number. */
- int_ctrlr = 1 << 6;
- upa_writeq(int_ctrlr, imap);
-
- /* The interrupt map registers do not have an INO field
- * like other chips do. They return zero in the INO
- * field, and the interrupt controller number is controlled
- * in bits 6 to 9. So in order for build_irq() to get
- * the INO right we pass it in as part of the fixup
- * which will get added to the map register zero value
- * read by build_irq().
- */
- ino |= (irq_data->portid << 6);
- ino -= int_ctrlr;
- return build_irq(ino, iclr, imap);
-}
-
-static void __init fire_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
- struct fire_irq_data *irq_data;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = fire_irq_build;
-
- irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = irq_data;
-
- irq_data->pbm_regs = regs[0].phys_addr;
- irq_data->portid = of_getintprop_default(dp, "portid", 0);
-}
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_SBUS
-/* INO number to IMAP register offset for SYSIO external IRQ's.
- * This should conform to both Sunfire/Wildfire server and Fusion
- * desktop designs.
- */
-#define SYSIO_IMAP_SLOT0 0x2c00UL
-#define SYSIO_IMAP_SLOT1 0x2c08UL
-#define SYSIO_IMAP_SLOT2 0x2c10UL
-#define SYSIO_IMAP_SLOT3 0x2c18UL
-#define SYSIO_IMAP_SCSI 0x3000UL
-#define SYSIO_IMAP_ETH 0x3008UL
-#define SYSIO_IMAP_BPP 0x3010UL
-#define SYSIO_IMAP_AUDIO 0x3018UL
-#define SYSIO_IMAP_PFAIL 0x3020UL
-#define SYSIO_IMAP_KMS 0x3028UL
-#define SYSIO_IMAP_FLPY 0x3030UL
-#define SYSIO_IMAP_SHW 0x3038UL
-#define SYSIO_IMAP_KBD 0x3040UL
-#define SYSIO_IMAP_MS 0x3048UL
-#define SYSIO_IMAP_SER 0x3050UL
-#define SYSIO_IMAP_TIM0 0x3060UL
-#define SYSIO_IMAP_TIM1 0x3068UL
-#define SYSIO_IMAP_UE 0x3070UL
-#define SYSIO_IMAP_CE 0x3078UL
-#define SYSIO_IMAP_SBERR 0x3080UL
-#define SYSIO_IMAP_PMGMT 0x3088UL
-#define SYSIO_IMAP_GFX 0x3090UL
-#define SYSIO_IMAP_EUPA 0x3098UL
-
-#define bogon ((unsigned long) -1)
-static unsigned long sysio_irq_offsets[] = {
- /* SBUS Slot 0 --> 3, level 1 --> 7 */
- SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
- SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
- SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
- SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
- SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
- SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
- SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
- SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
-
- /* Onboard devices (not relevant/used on SunFire). */
- SYSIO_IMAP_SCSI,
- SYSIO_IMAP_ETH,
- SYSIO_IMAP_BPP,
- bogon,
- SYSIO_IMAP_AUDIO,
- SYSIO_IMAP_PFAIL,
- bogon,
- bogon,
- SYSIO_IMAP_KMS,
- SYSIO_IMAP_FLPY,
- SYSIO_IMAP_SHW,
- SYSIO_IMAP_KBD,
- SYSIO_IMAP_MS,
- SYSIO_IMAP_SER,
- bogon,
- bogon,
- SYSIO_IMAP_TIM0,
- SYSIO_IMAP_TIM1,
- bogon,
- bogon,
- SYSIO_IMAP_UE,
- SYSIO_IMAP_CE,
- SYSIO_IMAP_SBERR,
- SYSIO_IMAP_PMGMT,
- SYSIO_IMAP_GFX,
- SYSIO_IMAP_EUPA,
-};
-
-#undef bogon
-
-#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets)
-
-/* Convert Interrupt Mapping register pointer to associated
- * Interrupt Clear register pointer, SYSIO specific version.
- */
-#define SYSIO_ICLR_UNUSED0 0x3400UL
-#define SYSIO_ICLR_SLOT0 0x3408UL
-#define SYSIO_ICLR_SLOT1 0x3448UL
-#define SYSIO_ICLR_SLOT2 0x3488UL
-#define SYSIO_ICLR_SLOT3 0x34c8UL
-static unsigned long sysio_imap_to_iclr(unsigned long imap)
-{
- unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0;
- return imap + diff;
-}
-
-static unsigned int sbus_of_build_irq(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- unsigned long reg_base = (unsigned long) _data;
- const struct linux_prom_registers *regs;
- unsigned long imap, iclr;
- int sbus_slot = 0;
- int sbus_level = 0;
-
- ino &= 0x3f;
-
- regs = of_get_property(dp, "reg", NULL);
- if (regs)
- sbus_slot = regs->which_io;
-
- if (ino < 0x20)
- ino += (sbus_slot * 8);
-
- imap = sysio_irq_offsets[ino];
- if (imap == ((unsigned long)-1)) {
- prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n",
- ino);
- prom_halt();
- }
- imap += reg_base;
-
- /* SYSIO inconsistency. For external SLOTS, we have to select
- * the right ICLR register based upon the lower SBUS irq level
- * bits.
- */
- if (ino >= 0x20) {
- iclr = sysio_imap_to_iclr(imap);
- } else {
- sbus_level = ino & 0x7;
-
- switch(sbus_slot) {
- case 0:
- iclr = reg_base + SYSIO_ICLR_SLOT0;
- break;
- case 1:
- iclr = reg_base + SYSIO_ICLR_SLOT1;
- break;
- case 2:
- iclr = reg_base + SYSIO_ICLR_SLOT2;
- break;
- default:
- case 3:
- iclr = reg_base + SYSIO_ICLR_SLOT3;
- break;
- };
-
- iclr += ((unsigned long)sbus_level - 1UL) * 8UL;
- }
- return build_irq(sbus_level, iclr, imap);
-}
-
-static void __init sbus_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = sbus_of_build_irq;
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr;
-}
-#endif /* CONFIG_SBUS */
-
-
-static unsigned int central_build_irq(struct device_node *dp,
- unsigned int ino,
- void *_data)
-{
- struct device_node *central_dp = _data;
- struct of_device *central_op = of_find_device_by_node(central_dp);
- struct resource *res;
- unsigned long imap, iclr;
- u32 tmp;
-
- if (!strcmp(dp->name, "eeprom")) {
- res = &central_op->resource[5];
- } else if (!strcmp(dp->name, "zs")) {
- res = &central_op->resource[4];
- } else if (!strcmp(dp->name, "clock-board")) {
- res = &central_op->resource[3];
- } else {
- return ino;
- }
-
- imap = res->start + 0x00UL;
- iclr = res->start + 0x10UL;
-
- /* Set the INO state to idle, and disable. */
- upa_writel(0, iclr);
- upa_readl(iclr);
-
- tmp = upa_readl(imap);
- tmp &= ~0x80000000;
- upa_writel(tmp, imap);
-
- return build_irq(0, iclr, imap);
-}
-
-static void __init central_irq_trans_init(struct device_node *dp)
-{
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = central_build_irq;
-
- dp->irq_trans->data = dp;
-}
-
-struct irq_trans {
- const char *name;
- void (*init)(struct device_node *);
-};
-
-#ifdef CONFIG_PCI
-static struct irq_trans __initdata pci_irq_trans_table[] = {
- { "SUNW,sabre", sabre_irq_trans_init },
- { "pci108e,a000", sabre_irq_trans_init },
- { "pci108e,a001", sabre_irq_trans_init },
- { "SUNW,psycho", psycho_irq_trans_init },
- { "pci108e,8000", psycho_irq_trans_init },
- { "SUNW,schizo", schizo_irq_trans_init },
- { "pci108e,8001", schizo_irq_trans_init },
- { "SUNW,schizo+", schizo_irq_trans_init },
- { "pci108e,8002", schizo_irq_trans_init },
- { "SUNW,tomatillo", tomatillo_irq_trans_init },
- { "pci108e,a801", tomatillo_irq_trans_init },
- { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
- { "pciex108e,80f0", fire_irq_trans_init },
-};
-#endif
-
-static unsigned int sun4v_vdev_irq_build(struct device_node *dp,
- unsigned int devino,
- void *_data)
-{
- u32 devhandle = (u32) (unsigned long) _data;
-
- return sun4v_build_irq(devhandle, devino);
-}
-
-static void __init sun4v_vdev_irq_trans_init(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
-
- dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
- dp->irq_trans->irq_build = sun4v_vdev_irq_build;
-
- regs = of_get_property(dp, "reg", NULL);
- dp->irq_trans->data = (void *) (unsigned long)
- ((regs->phys_addr >> 32UL) & 0x0fffffff);
-}
-
-static void __init irq_trans_init(struct device_node *dp)
-{
-#ifdef CONFIG_PCI
- const char *model;
- int i;
-#endif
-
-#ifdef CONFIG_PCI
- model = of_get_property(dp, "model", NULL);
- if (!model)
- model = of_get_property(dp, "compatible", NULL);
- if (model) {
- for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) {
- struct irq_trans *t = &pci_irq_trans_table[i];
-
- if (!strcmp(model, t->name)) {
- t->init(dp);
- return;
- }
- }
- }
-#endif
-#ifdef CONFIG_SBUS
- if (!strcmp(dp->name, "sbus") ||
- !strcmp(dp->name, "sbi")) {
- sbus_irq_trans_init(dp);
- return;
- }
-#endif
- if (!strcmp(dp->name, "fhc") &&
- !strcmp(dp->parent->name, "central")) {
- central_irq_trans_init(dp);
- return;
- }
- if (!strcmp(dp->name, "virtual-devices") ||
- !strcmp(dp->name, "niu")) {
- sun4v_vdev_irq_trans_init(dp);
- return;
- }
-}
-
-static int is_root_node(const struct device_node *dp)
-{
- if (!dp)
- return 0;
-
- return (dp->parent == NULL);
-}
-
-/* The following routines deal with the black magic of fully naming a
- * node.
- *
- * Certain well known named nodes are just the simple name string.
- *
- * Actual devices have an address specifier appended to the base name
- * string, like this "foo@addr". The "addr" can be in any number of
- * formats, and the platform plus the type of the node determine the
- * format and how it is constructed.
- *
- * For children of the ROOT node, the naming convention is fixed and
- * determined by whether this is a sun4u or sun4v system.
- *
- * For children of other nodes, it is bus type specific. So
- * we walk up the tree until we discover a "device_type" property
- * we recognize and we go from there.
- *
- * As an example, the boot device on my workstation has a full path:
- *
- * /pci@1e,600000/ide@d/disk@0,0:c
- */
-static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom64_registers *regs;
- struct property *rprop;
- u32 high_bits, low_bits, type;
-
- rprop = of_find_property(dp, "reg", NULL);
- if (!rprop)
- return;
-
- regs = rprop->value;
- if (!is_root_node(dp->parent)) {
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- (unsigned int) (regs->phys_addr >> 32UL),
- (unsigned int) (regs->phys_addr & 0xffffffffUL));
- return;
- }
-
- type = regs->phys_addr >> 60UL;
- high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL;
- low_bits = (regs->phys_addr & 0xffffffffUL);
-
- if (type == 0 || type == 8) {
- const char *prefix = (type == 0) ? "m" : "i";
-
- if (low_bits)
- sprintf(tmp_buf, "%s@%s%x,%x",
- dp->name, prefix,
- high_bits, low_bits);
- else
- sprintf(tmp_buf, "%s@%s%x",
- dp->name,
- prefix,
- high_bits);
- } else if (type == 12) {
- sprintf(tmp_buf, "%s@%x",
- dp->name, high_bits);
- }
-}
-
-static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom64_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
- if (!is_root_node(dp->parent)) {
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- (unsigned int) (regs->phys_addr >> 32UL),
- (unsigned int) (regs->phys_addr & 0xffffffffUL));
- return;
- }
-
- prop = of_find_property(dp, "upa-portid", NULL);
- if (!prop)
- prop = of_find_property(dp, "portid", NULL);
- if (prop) {
- unsigned long mask = 0xffffffffUL;
-
- if (tlb_type >= cheetah)
- mask = 0x7fffff;
-
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- *(u32 *)prop->value,
- (unsigned int) (regs->phys_addr & mask));
- }
-}
-
-/* "name@slot,offset" */
-static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- regs->which_io,
- regs->phys_addr);
-}
-
-/* "name@devnum[,func]" */
-static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom_pci_registers *regs;
- struct property *prop;
- unsigned int devfn;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
- devfn = (regs->phys_hi >> 8) & 0xff;
- if (devfn & 0x07) {
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- devfn >> 3,
- devfn & 0x07);
- } else {
- sprintf(tmp_buf, "%s@%x",
- dp->name,
- devfn >> 3);
- }
-}
-
-/* "name@UPA_PORTID,offset" */
-static void __init upa_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom64_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- prop = of_find_property(dp, "upa-portid", NULL);
- if (!prop)
- return;
-
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- *(u32 *) prop->value,
- (unsigned int) (regs->phys_addr & 0xffffffffUL));
-}
-
-/* "name@reg" */
-static void __init vdev_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct property *prop;
- u32 *regs;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- sprintf(tmp_buf, "%s@%x", dp->name, *regs);
-}
-
-/* "name@addrhi,addrlo" */
-static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct linux_prom64_registers *regs;
- struct property *prop;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name,
- (unsigned int) (regs->phys_addr >> 32UL),
- (unsigned int) (regs->phys_addr & 0xffffffffUL));
-}
-
-/* "name@bus,addr" */
-static void __init i2c_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct property *prop;
- u32 *regs;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- /* This actually isn't right... should look at the #address-cells
- * property of the i2c bus node etc. etc.
- */
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name, regs[0], regs[1]);
-}
-
-/* "name@reg0[,reg1]" */
-static void __init usb_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct property *prop;
- u32 *regs;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- if (prop->length == sizeof(u32) || regs[1] == 1) {
- sprintf(tmp_buf, "%s@%x",
- dp->name, regs[0]);
- } else {
- sprintf(tmp_buf, "%s@%x,%x",
- dp->name, regs[0], regs[1]);
- }
-}
-
-/* "name@reg0reg1[,reg2reg3]" */
-static void __init ieee1394_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct property *prop;
- u32 *regs;
-
- prop = of_find_property(dp, "reg", NULL);
- if (!prop)
- return;
-
- regs = prop->value;
-
- if (regs[2] || regs[3]) {
- sprintf(tmp_buf, "%s@%08x%08x,%04x%08x",
- dp->name, regs[0], regs[1], regs[2], regs[3]);
- } else {
- sprintf(tmp_buf, "%s@%08x%08x",
- dp->name, regs[0], regs[1]);
- }
-}
-
-static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
-{
- struct device_node *parent = dp->parent;
-
- if (parent != NULL) {
- if (!strcmp(parent->type, "pci") ||
- !strcmp(parent->type, "pciex")) {
- pci_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "sbus")) {
- sbus_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "upa")) {
- upa_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "ebus")) {
- ebus_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->name, "usb") ||
- !strcmp(parent->name, "hub")) {
- usb_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "i2c")) {
- i2c_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "firewire")) {
- ieee1394_path_component(dp, tmp_buf);
- return;
- }
- if (!strcmp(parent->type, "virtual-devices")) {
- vdev_path_component(dp, tmp_buf);
- return;
- }
- /* "isa" is handled with platform naming */
- }
-
- /* Use platform naming convention. */
- if (tlb_type == hypervisor) {
- sun4v_path_component(dp, tmp_buf);
- return;
- } else {
- sun4u_path_component(dp, tmp_buf);
- }
-}
-
-static char * __init build_path_component(struct device_node *dp)
-{
- char tmp_buf[64], *n;
-
- tmp_buf[0] = '\0';
- __build_path_component(dp, tmp_buf);
- if (tmp_buf[0] == '\0')
- strcpy(tmp_buf, dp->name);
-
- n = prom_early_alloc(strlen(tmp_buf) + 1);
- strcpy(n, tmp_buf);
-
- return n;
-}
-
-static char * __init build_full_name(struct device_node *dp)
-{
- int len, ourlen, plen;
- char *n;
-
- plen = strlen(dp->parent->full_name);
- ourlen = strlen(dp->path_component_name);
- len = ourlen + plen + 2;
-
- n = prom_early_alloc(len);
- strcpy(n, dp->parent->full_name);
- if (!is_root_node(dp->parent)) {
- strcpy(n + plen, "/");
- plen++;
- }
- strcpy(n + plen, dp->path_component_name);
-
- return n;
-}
-
-static unsigned int unique_id;
-
-static struct property * __init build_one_prop(phandle node, char *prev, char *special_name, void *special_val, int special_len)
-{
- static struct property *tmp = NULL;
- struct property *p;
-
- if (tmp) {
- p = tmp;
- memset(p, 0, sizeof(*p) + 32);
- tmp = NULL;
- } else {
- p = prom_early_alloc(sizeof(struct property) + 32);
- p->unique_id = unique_id++;
- }
-
- p->name = (char *) (p + 1);
- if (special_name) {
- strcpy(p->name, special_name);
- p->length = special_len;
- p->value = prom_early_alloc(special_len);
- memcpy(p->value, special_val, special_len);
- } else {
- if (prev == NULL) {
- prom_firstprop(node, p->name);
- } else {
- prom_nextprop(node, prev, p->name);
- }
- if (strlen(p->name) == 0) {
- tmp = p;
- return NULL;
- }
- p->length = prom_getproplen(node, p->name);
- if (p->length <= 0) {
- p->length = 0;
- } else {
- p->value = prom_early_alloc(p->length + 1);
- prom_getproperty(node, p->name, p->value, p->length);
- ((unsigned char *)p->value)[p->length] = '\0';
- }
- }
- return p;
-}
-
-static struct property * __init build_prop_list(phandle node)
-{
- struct property *head, *tail;
-
- head = tail = build_one_prop(node, NULL,
- ".node", &node, sizeof(node));
-
- tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
- tail = tail->next;
- while(tail) {
- tail->next = build_one_prop(node, tail->name,
- NULL, NULL, 0);
- tail = tail->next;
- }
-
- return head;
-}
-
-static char * __init get_one_property(phandle node, const char *name)
-{
- char *buf = "<NULL>";
- int len;
-
- len = prom_getproplen(node, name);
- if (len > 0) {
- buf = prom_early_alloc(len);
- prom_getproperty(node, name, buf, len);
- }
-
- return buf;
-}
-
-static struct device_node * __init create_node(phandle node, struct device_node *parent)
-{
- struct device_node *dp;
-
- if (!node)
- return NULL;
-
- dp = prom_early_alloc(sizeof(*dp));
- dp->unique_id = unique_id++;
- dp->parent = parent;
-
- kref_init(&dp->kref);
-
- dp->name = get_one_property(node, "name");
- dp->type = get_one_property(node, "device_type");
- dp->node = node;
-
- dp->properties = build_prop_list(node);
-
- irq_trans_init(dp);
-
- return dp;
-}
-
-static struct device_node * __init build_tree(struct device_node *parent, phandle node, struct device_node ***nextp)
-{
- struct device_node *ret = NULL, *prev_sibling = NULL;
- struct device_node *dp;
-
- while (1) {
- dp = create_node(node, parent);
- if (!dp)
- break;
-
- if (prev_sibling)
- prev_sibling->sibling = dp;
-
- if (!ret)
- ret = dp;
- prev_sibling = dp;
-
- *(*nextp) = dp;
- *nextp = &dp->allnext;
-
- dp->path_component_name = build_path_component(dp);
- dp->full_name = build_full_name(dp);
-
- dp->child = build_tree(dp, prom_getchild(node), nextp);
-
- node = prom_getsibling(node);
- }
-
- return ret;
-}
-
-static const char *get_mid_prop(void)
-{
- return (tlb_type == spitfire ? "upa-portid" : "portid");
-}
-
-struct device_node *of_find_node_by_cpuid(int cpuid)
-{
- struct device_node *dp;
- const char *mid_prop = get_mid_prop();
-
- for_each_node_by_type(dp, "cpu") {
- int id = of_getintprop_default(dp, mid_prop, -1);
- const char *this_mid_prop = mid_prop;
-
- if (id < 0) {
- this_mid_prop = "cpuid";
- id = of_getintprop_default(dp, this_mid_prop, -1);
- }
-
- if (id < 0) {
- prom_printf("OF: Serious problem, cpu lacks "
- "%s property", this_mid_prop);
- prom_halt();
- }
- if (cpuid == id)
- return dp;
- }
- return NULL;
-}
-
-static void __init of_fill_in_cpu_data(void)
-{
- struct device_node *dp;
- const char *mid_prop = get_mid_prop();
-
- ncpus_probed = 0;
- for_each_node_by_type(dp, "cpu") {
- int cpuid = of_getintprop_default(dp, mid_prop, -1);
- const char *this_mid_prop = mid_prop;
- struct device_node *portid_parent;
- int portid = -1;
-
- portid_parent = NULL;
- if (cpuid < 0) {
- this_mid_prop = "cpuid";
- cpuid = of_getintprop_default(dp, this_mid_prop, -1);
- if (cpuid >= 0) {
- int limit = 2;
-
- portid_parent = dp;
- while (limit--) {
- portid_parent = portid_parent->parent;
- if (!portid_parent)
- break;
- portid = of_getintprop_default(portid_parent,
- "portid", -1);
- if (portid >= 0)
- break;
- }
- }
- }
-
- if (cpuid < 0) {
- prom_printf("OF: Serious problem, cpu lacks "
- "%s property", this_mid_prop);
- prom_halt();
- }
-
- ncpus_probed++;
-
-#ifdef CONFIG_SMP
- if (cpuid >= NR_CPUS) {
- printk(KERN_WARNING "Ignoring CPU %d which is "
- ">= NR_CPUS (%d)\n",
- cpuid, NR_CPUS);
- continue;
- }
-#else
- /* On uniprocessor we only want the values for the
- * real physical cpu the kernel booted onto, however
- * cpu_data() only has one entry at index 0.
- */
- if (cpuid != real_hard_smp_processor_id())
- continue;
- cpuid = 0;
-#endif
-
- cpu_data(cpuid).clock_tick =
- of_getintprop_default(dp, "clock-frequency", 0);
-
- if (portid_parent) {
- cpu_data(cpuid).dcache_size =
- of_getintprop_default(dp, "l1-dcache-size",
- 16 * 1024);
- cpu_data(cpuid).dcache_line_size =
- of_getintprop_default(dp, "l1-dcache-line-size",
- 32);
- cpu_data(cpuid).icache_size =
- of_getintprop_default(dp, "l1-icache-size",
- 8 * 1024);
- cpu_data(cpuid).icache_line_size =
- of_getintprop_default(dp, "l1-icache-line-size",
- 32);
- cpu_data(cpuid).ecache_size =
- of_getintprop_default(dp, "l2-cache-size", 0);
- cpu_data(cpuid).ecache_line_size =
- of_getintprop_default(dp, "l2-cache-line-size", 0);
- if (!cpu_data(cpuid).ecache_size ||
- !cpu_data(cpuid).ecache_line_size) {
- cpu_data(cpuid).ecache_size =
- of_getintprop_default(portid_parent,
- "l2-cache-size",
- (4 * 1024 * 1024));
- cpu_data(cpuid).ecache_line_size =
- of_getintprop_default(portid_parent,
- "l2-cache-line-size", 64);
- }
-
- cpu_data(cpuid).core_id = portid + 1;
- cpu_data(cpuid).proc_id = portid;
-#ifdef CONFIG_SMP
- sparc64_multi_core = 1;
-#endif
- } else {
- cpu_data(cpuid).dcache_size =
- of_getintprop_default(dp, "dcache-size", 16 * 1024);
- cpu_data(cpuid).dcache_line_size =
- of_getintprop_default(dp, "dcache-line-size", 32);
-
- cpu_data(cpuid).icache_size =
- of_getintprop_default(dp, "icache-size", 16 * 1024);
- cpu_data(cpuid).icache_line_size =
- of_getintprop_default(dp, "icache-line-size", 32);
-
- cpu_data(cpuid).ecache_size =
- of_getintprop_default(dp, "ecache-size",
- (4 * 1024 * 1024));
- cpu_data(cpuid).ecache_line_size =
- of_getintprop_default(dp, "ecache-line-size", 64);
-
- cpu_data(cpuid).core_id = 0;
- cpu_data(cpuid).proc_id = -1;
- }
-
-#ifdef CONFIG_SMP
- cpu_set(cpuid, cpu_present_map);
- cpu_set(cpuid, cpu_possible_map);
-#endif
- }
-
- smp_fill_in_sib_core_maps();
-}
-
-struct device_node *of_console_device;
-EXPORT_SYMBOL(of_console_device);
-
-char *of_console_path;
-EXPORT_SYMBOL(of_console_path);
-
-char *of_console_options;
-EXPORT_SYMBOL(of_console_options);
-
-static void __init of_console_init(void)
-{
- char *msg = "OF stdout device is: %s\n";
- struct device_node *dp;
- const char *type;
- phandle node;
-
- of_console_path = prom_early_alloc(256);
- if (prom_ihandle2path(prom_stdout, of_console_path, 256) < 0) {
- prom_printf("Cannot obtain path of stdout.\n");
- prom_halt();
- }
- of_console_options = strrchr(of_console_path, ':');
- if (of_console_options) {
- of_console_options++;
- if (*of_console_options == '\0')
- of_console_options = NULL;
- }
-
- node = prom_inst2pkg(prom_stdout);
- if (!node) {
- prom_printf("Cannot resolve stdout node from "
- "instance %08x.\n", prom_stdout);
- prom_halt();
- }
-
- dp = of_find_node_by_phandle(node);
- type = of_get_property(dp, "device_type", NULL);
- if (!type) {
- prom_printf("Console stdout lacks device_type property.\n");
- prom_halt();
- }
-
- if (strcmp(type, "display") && strcmp(type, "serial")) {
- prom_printf("Console device_type is neither display "
- "nor serial.\n");
- prom_halt();
- }
-
- of_console_device = dp;
-
- printk(msg, of_console_path);
-}
-
-void __init prom_build_devicetree(void)
-{
- struct device_node **nextp;
-
- allnodes = create_node(prom_root_node, NULL);
- allnodes->path_component_name = "";
- allnodes->full_name = "/";
-
- nextp = &allnodes->allnext;
- allnodes->child = build_tree(allnodes,
- prom_getchild(allnodes->node),
- &nextp);
- of_console_init();
-
- printk("PROM: Built device tree with %u bytes of memory.\n",
- prom_early_allocated);
-
- if (tlb_type != hypervisor)
- of_fill_in_cpu_data();
-}
diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S
deleted file mode 100644
index 97a993c1f7f3..000000000000
--- a/arch/sparc64/kernel/rtrap.S
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * rtrap.S: Preparing for return from trap on Sparc V9.
- *
- * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- */
-
-
-#include <asm/asi.h>
-#include <asm/pstate.h>
-#include <asm/ptrace.h>
-#include <asm/spitfire.h>
-#include <asm/head.h>
-#include <asm/visasm.h>
-#include <asm/processor.h>
-
-#define RTRAP_PSTATE (PSTATE_RMO|PSTATE_PEF|PSTATE_PRIV|PSTATE_IE)
-#define RTRAP_PSTATE_IRQOFF (PSTATE_RMO|PSTATE_PEF|PSTATE_PRIV)
-#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_RMO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
-
- .text
- .align 32
-__handle_softirq:
- call do_softirq
- nop
- ba,a,pt %xcc, __handle_softirq_continue
- nop
-__handle_preemption:
- call schedule
- wrpr %g0, RTRAP_PSTATE, %pstate
- ba,pt %xcc, __handle_preemption_continue
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
-
-__handle_user_windows:
- call fault_in_user_windows
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- /* Redo sched+sig checks */
- ldx [%g6 + TI_FLAGS], %l0
- andcc %l0, _TIF_NEED_RESCHED, %g0
-
- be,pt %xcc, 1f
- nop
- call schedule
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- ldx [%g6 + TI_FLAGS], %l0
-
-1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
- be,pt %xcc, __handle_user_windows_continue
- nop
- mov %l5, %o1
- add %sp, PTREGS_OFF, %o0
- mov %l0, %o2
-
- call do_notify_resume
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- /* Signal delivery can modify pt_regs tstate, so we must
- * reload it.
- */
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
- sethi %hi(0xf << 20), %l4
- and %l1, %l4, %l4
- ba,pt %xcc, __handle_user_windows_continue
-
- andn %l1, %l4, %l1
-__handle_perfctrs:
- call update_perfctrs
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- ldub [%g6 + TI_WSAVED], %o2
- brz,pt %o2, 1f
- nop
- /* Redo userwin+sched+sig checks */
- call fault_in_user_windows
-
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- ldx [%g6 + TI_FLAGS], %l0
- andcc %l0, _TIF_NEED_RESCHED, %g0
- be,pt %xcc, 1f
-
- nop
- call schedule
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- ldx [%g6 + TI_FLAGS], %l0
-1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
-
- be,pt %xcc, __handle_perfctrs_continue
- sethi %hi(TSTATE_PEF), %o0
- mov %l5, %o1
- add %sp, PTREGS_OFF, %o0
- mov %l0, %o2
- call do_notify_resume
-
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- /* Signal delivery can modify pt_regs tstate, so we must
- * reload it.
- */
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
- sethi %hi(0xf << 20), %l4
- and %l1, %l4, %l4
- andn %l1, %l4, %l1
- ba,pt %xcc, __handle_perfctrs_continue
-
- sethi %hi(TSTATE_PEF), %o0
-__handle_userfpu:
- rd %fprs, %l5
- andcc %l5, FPRS_FEF, %g0
- sethi %hi(TSTATE_PEF), %o0
- be,a,pn %icc, __handle_userfpu_continue
- andn %l1, %o0, %l1
- ba,a,pt %xcc, __handle_userfpu_continue
-
-__handle_signal:
- mov %l5, %o1
- add %sp, PTREGS_OFF, %o0
- mov %l0, %o2
- call do_notify_resume
- wrpr %g0, RTRAP_PSTATE, %pstate
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
-
- /* Signal delivery can modify pt_regs tstate, so we must
- * reload it.
- */
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
- sethi %hi(0xf << 20), %l4
- and %l1, %l4, %l4
- ba,pt %xcc, __handle_signal_continue
- andn %l1, %l4, %l1
-
- .align 64
- .globl rtrap_irq, rtrap, irqsz_patchme, rtrap_xcall
-rtrap_irq:
-rtrap:
-#ifndef CONFIG_SMP
- sethi %hi(per_cpu____cpu_data), %l0
- lduw [%l0 + %lo(per_cpu____cpu_data)], %l1
-#else
- sethi %hi(per_cpu____cpu_data), %l0
- or %l0, %lo(per_cpu____cpu_data), %l0
- lduw [%l0 + %g5], %l1
-#endif
- cmp %l1, 0
-
- /* mm/ultra.S:xcall_report_regs KNOWS about this load. */
- bne,pn %icc, __handle_softirq
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-__handle_softirq_continue:
-rtrap_xcall:
- sethi %hi(0xf << 20), %l4
- and %l1, %l4, %l4
- andn %l1, %l4, %l1
- srl %l4, 20, %l4
-#ifdef CONFIG_TRACE_IRQFLAGS
- brnz,pn %l4, rtrap_no_irq_enable
- nop
- call trace_hardirqs_on
- nop
- wrpr %l4, %pil
-rtrap_no_irq_enable:
-#endif
- andcc %l1, TSTATE_PRIV, %l3
- bne,pn %icc, to_kernel
- nop
-
- /* We must hold IRQs off and atomically test schedule+signal
- * state, then hold them off all the way back to userspace.
- * If we are returning to kernel, none of this matters. Note
- * that we are disabling interrupts via PSTATE_IE, not using
- * %pil.
- *
- * If we do not do this, there is a window where we would do
- * the tests, later the signal/resched event arrives but we do
- * not process it since we are still in kernel mode. It would
- * take until the next local IRQ before the signal/resched
- * event would be handled.
- *
- * This also means that if we have to deal with performance
- * counters or user windows, we have to redo all of these
- * sched+signal checks with IRQs disabled.
- */
-to_user: wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- wrpr 0, %pil
-__handle_preemption_continue:
- ldx [%g6 + TI_FLAGS], %l0
- sethi %hi(_TIF_USER_WORK_MASK), %o0
- or %o0, %lo(_TIF_USER_WORK_MASK), %o0
- andcc %l0, %o0, %g0
- sethi %hi(TSTATE_PEF), %o0
- be,pt %xcc, user_nowork
- andcc %l1, %o0, %g0
- andcc %l0, _TIF_NEED_RESCHED, %g0
- bne,pn %xcc, __handle_preemption
- andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0
- bne,pn %xcc, __handle_signal
-__handle_signal_continue:
- ldub [%g6 + TI_WSAVED], %o2
- brnz,pn %o2, __handle_user_windows
- nop
-__handle_user_windows_continue:
- ldx [%g6 + TI_FLAGS], %l5
- andcc %l5, _TIF_PERFCTR, %g0
- sethi %hi(TSTATE_PEF), %o0
- bne,pn %xcc, __handle_perfctrs
-__handle_perfctrs_continue:
- andcc %l1, %o0, %g0
-
- /* This fpdepth clear is necessary for non-syscall rtraps only */
-user_nowork:
- bne,pn %xcc, __handle_userfpu
- stb %g0, [%g6 + TI_FPDEPTH]
-__handle_userfpu_continue:
-
-rt_continue: ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1
- ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2
-
- ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3
- ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4
- ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5
- brz,pt %l3, 1f
- mov %g6, %l2
-
- /* Must do this before thread reg is clobbered below. */
- LOAD_PER_CPU_BASE(%g5, %g6, %i0, %i1, %i2)
-1:
- ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6
- ldx [%sp + PTREGS_OFF + PT_V9_G7], %g7
-
- /* Normal globals are restored, go to trap globals. */
-661: wrpr %g0, RTRAP_PSTATE_AG_IRQOFF, %pstate
- nop
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
- SET_GL(1)
- .previous
-
- mov %l2, %g6
-
- ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0
- ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1
-
- ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2
- ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3
- ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4
- ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5
- ldx [%sp + PTREGS_OFF + PT_V9_I6], %i6
- ldx [%sp + PTREGS_OFF + PT_V9_I7], %i7
- ldx [%sp + PTREGS_OFF + PT_V9_TPC], %l2
- ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %o2
-
- ld [%sp + PTREGS_OFF + PT_V9_Y], %o3
- wr %o3, %g0, %y
- wrpr %l4, 0x0, %pil
- wrpr %g0, 0x1, %tl
- andn %l1, TSTATE_SYSCALL, %l1
- wrpr %l1, %g0, %tstate
- wrpr %l2, %g0, %tpc
- wrpr %o2, %g0, %tnpc
-
- brnz,pn %l3, kern_rtt
- mov PRIMARY_CONTEXT, %l7
-
-661: ldxa [%l7 + %l7] ASI_DMMU, %l0
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- ldxa [%l7 + %l7] ASI_MMU, %l0
- .previous
-
- sethi %hi(sparc64_kern_pri_nuc_bits), %l1
- ldx [%l1 + %lo(sparc64_kern_pri_nuc_bits)], %l1
- or %l0, %l1, %l0
-
-661: stxa %l0, [%l7] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %l0, [%l7] ASI_MMU
- .previous
-
- sethi %hi(KERNBASE), %l7
- flush %l7
- rdpr %wstate, %l1
- rdpr %otherwin, %l2
- srl %l1, 3, %l1
-
- wrpr %l2, %g0, %canrestore
- wrpr %l1, %g0, %wstate
- brnz,pt %l2, user_rtt_restore
- wrpr %g0, %g0, %otherwin
-
- ldx [%g6 + TI_FLAGS], %g3
- wr %g0, ASI_AIUP, %asi
- rdpr %cwp, %g1
- andcc %g3, _TIF_32BIT, %g0
- sub %g1, 1, %g1
- bne,pt %xcc, user_rtt_fill_32bit
- wrpr %g1, %cwp
- ba,a,pt %xcc, user_rtt_fill_64bit
-
-user_rtt_fill_fixup:
- rdpr %cwp, %g1
- add %g1, 1, %g1
- wrpr %g1, 0x0, %cwp
-
- rdpr %wstate, %g2
- sll %g2, 3, %g2
- wrpr %g2, 0x0, %wstate
-
- /* We know %canrestore and %otherwin are both zero. */
-
- sethi %hi(sparc64_kern_pri_context), %g2
- ldx [%g2 + %lo(sparc64_kern_pri_context)], %g2
- mov PRIMARY_CONTEXT, %g1
-
-661: stxa %g2, [%g1] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g2, [%g1] ASI_MMU
- .previous
-
- sethi %hi(KERNBASE), %g1
- flush %g1
-
- or %g4, FAULT_CODE_WINFIXUP, %g4
- stb %g4, [%g6 + TI_FAULT_CODE]
- stx %g5, [%g6 + TI_FAULT_ADDR]
-
- mov %g6, %l1
- wrpr %g0, 0x0, %tl
-
-661: nop
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- SET_GL(0)
- .previous
-
- wrpr %g0, RTRAP_PSTATE, %pstate
-
- mov %l1, %g6
- ldx [%g6 + TI_TASK], %g4
- LOAD_PER_CPU_BASE(%g5, %g6, %g1, %g2, %g3)
- call do_sparc64_fault
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
-
-user_rtt_pre_restore:
- add %g1, 1, %g1
- wrpr %g1, 0x0, %cwp
-
-user_rtt_restore:
- restore
- rdpr %canrestore, %g1
- wrpr %g1, 0x0, %cleanwin
- retry
- nop
-
-kern_rtt: rdpr %canrestore, %g1
- brz,pn %g1, kern_rtt_fill
- nop
-kern_rtt_restore:
- stw %g0, [%sp + PTREGS_OFF + PT_V9_MAGIC]
- restore
- retry
-
-to_kernel:
-#ifdef CONFIG_PREEMPT
- ldsw [%g6 + TI_PRE_COUNT], %l5
- brnz %l5, kern_fpucheck
- ldx [%g6 + TI_FLAGS], %l5
- andcc %l5, _TIF_NEED_RESCHED, %g0
- be,pt %xcc, kern_fpucheck
- nop
- cmp %l4, 0
- bne,pn %xcc, kern_fpucheck
- sethi %hi(PREEMPT_ACTIVE), %l6
- stw %l6, [%g6 + TI_PRE_COUNT]
- call schedule
- nop
- ba,pt %xcc, rtrap
- stw %g0, [%g6 + TI_PRE_COUNT]
-#endif
-kern_fpucheck: ldub [%g6 + TI_FPDEPTH], %l5
- brz,pt %l5, rt_continue
- srl %l5, 1, %o0
- add %g6, TI_FPSAVED, %l6
- ldub [%l6 + %o0], %l2
- sub %l5, 2, %l5
-
- add %g6, TI_GSR, %o1
- andcc %l2, (FPRS_FEF|FPRS_DU), %g0
- be,pt %icc, 2f
- and %l2, FPRS_DL, %l6
- andcc %l2, FPRS_FEF, %g0
- be,pn %icc, 5f
- sll %o0, 3, %o5
- rd %fprs, %g1
-
- wr %g1, FPRS_FEF, %fprs
- ldx [%o1 + %o5], %g1
- add %g6, TI_XFSR, %o1
- sll %o0, 8, %o2
- add %g6, TI_FPREGS, %o3
- brz,pn %l6, 1f
- add %g6, TI_FPREGS+0x40, %o4
-
- membar #Sync
- ldda [%o3 + %o2] ASI_BLK_P, %f0
- ldda [%o4 + %o2] ASI_BLK_P, %f16
- membar #Sync
-1: andcc %l2, FPRS_DU, %g0
- be,pn %icc, 1f
- wr %g1, 0, %gsr
- add %o2, 0x80, %o2
- membar #Sync
- ldda [%o3 + %o2] ASI_BLK_P, %f32
- ldda [%o4 + %o2] ASI_BLK_P, %f48
-1: membar #Sync
- ldx [%o1 + %o5], %fsr
-2: stb %l5, [%g6 + TI_FPDEPTH]
- ba,pt %xcc, rt_continue
- nop
-5: wr %g0, FPRS_FEF, %fprs
- sll %o0, 8, %o2
-
- add %g6, TI_FPREGS+0x80, %o3
- add %g6, TI_FPREGS+0xc0, %o4
- membar #Sync
- ldda [%o3 + %o2] ASI_BLK_P, %f32
- ldda [%o4 + %o2] ASI_BLK_P, %f48
- membar #Sync
- wr %g0, FPRS_DU, %fprs
- ba,pt %xcc, rt_continue
- stb %l5, [%g6 + TI_FPDEPTH]
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c
deleted file mode 100644
index c8b03a4f68bf..000000000000
--- a/arch/sparc64/kernel/setup.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * linux/arch/sparc64/kernel/setup.c
- *
- * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <asm/smp.h>
-#include <linux/user.h>
-#include <linux/screen_info.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/syscalls.h>
-#include <linux/kdev_t.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/inet.h>
-#include <linux/console.h>
-#include <linux/root_dev.h>
-#include <linux/interrupt.h>
-#include <linux/cpu.h>
-#include <linux/initrd.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/processor.h>
-#include <asm/oplib.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/idprom.h>
-#include <asm/head.h>
-#include <asm/starfire.h>
-#include <asm/mmu_context.h>
-#include <asm/timer.h>
-#include <asm/sections.h>
-#include <asm/setup.h>
-#include <asm/mmu.h>
-#include <asm/ns87303.h>
-
-#ifdef CONFIG_IP_PNP
-#include <net/ipconfig.h>
-#endif
-
-#include "entry.h"
-
-/* Used to synchronize accesses to NatSemi SUPER I/O chip configure
- * operations in asm/ns87303.h
- */
-DEFINE_SPINLOCK(ns87303_lock);
-
-struct screen_info screen_info = {
- 0, 0, /* orig-x, orig-y */
- 0, /* unused */
- 0, /* orig-video-page */
- 0, /* orig-video-mode */
- 128, /* orig-video-cols */
- 0, 0, 0, /* unused, ega_bx, unused */
- 54, /* orig-video-lines */
- 0, /* orig-video-isVGA */
- 16 /* orig-video-points */
-};
-
-static void
-prom_console_write(struct console *con, const char *s, unsigned n)
-{
- prom_write(s, n);
-}
-
-/* Exported for mm/init.c:paging_init. */
-unsigned long cmdline_memory_size = 0;
-
-static struct console prom_early_console = {
- .name = "earlyprom",
- .write = prom_console_write,
- .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
- .index = -1,
-};
-
-/*
- * Process kernel command line switches that are specific to the
- * SPARC or that require special low-level processing.
- */
-static void __init process_switch(char c)
-{
- switch (c) {
- case 'd':
- case 's':
- break;
- case 'h':
- prom_printf("boot_flags_init: Halt!\n");
- prom_halt();
- break;
- case 'p':
- /* Just ignore, this behavior is now the default. */
- break;
- case 'P':
- /* Force UltraSPARC-III P-Cache on. */
- if (tlb_type != cheetah) {
- printk("BOOT: Ignoring P-Cache force option.\n");
- break;
- }
- cheetah_pcache_forced_on = 1;
- add_taint(TAINT_MACHINE_CHECK);
- cheetah_enable_pcache();
- break;
-
- default:
- printk("Unknown boot switch (-%c)\n", c);
- break;
- }
-}
-
-static void __init boot_flags_init(char *commands)
-{
- while (*commands) {
- /* Move to the start of the next "argument". */
- while (*commands && *commands == ' ')
- commands++;
-
- /* Process any command switches, otherwise skip it. */
- if (*commands == '\0')
- break;
- if (*commands == '-') {
- commands++;
- while (*commands && *commands != ' ')
- process_switch(*commands++);
- continue;
- }
- if (!strncmp(commands, "mem=", 4)) {
- /*
- * "mem=XXX[kKmM]" overrides the PROM-reported
- * memory size.
- */
- cmdline_memory_size = simple_strtoul(commands + 4,
- &commands, 0);
- if (*commands == 'K' || *commands == 'k') {
- cmdline_memory_size <<= 10;
- commands++;
- } else if (*commands=='M' || *commands=='m') {
- cmdline_memory_size <<= 20;
- commands++;
- }
- }
- while (*commands && *commands != ' ')
- commands++;
- }
-}
-
-extern unsigned short root_flags;
-extern unsigned short root_dev;
-extern unsigned short ram_flags;
-#define RAMDISK_IMAGE_START_MASK 0x07FF
-#define RAMDISK_PROMPT_FLAG 0x8000
-#define RAMDISK_LOAD_FLAG 0x4000
-
-extern int root_mountflags;
-
-char reboot_command[COMMAND_LINE_SIZE];
-
-static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 };
-
-void __init per_cpu_patch(void)
-{
- struct cpuid_patch_entry *p;
- unsigned long ver;
- int is_jbus;
-
- if (tlb_type == spitfire && !this_is_starfire)
- return;
-
- is_jbus = 0;
- if (tlb_type != hypervisor) {
- __asm__ ("rdpr %%ver, %0" : "=r" (ver));
- is_jbus = ((ver >> 32UL) == __JALAPENO_ID ||
- (ver >> 32UL) == __SERRANO_ID);
- }
-
- p = &__cpuid_patch;
- while (p < &__cpuid_patch_end) {
- unsigned long addr = p->addr;
- unsigned int *insns;
-
- switch (tlb_type) {
- case spitfire:
- insns = &p->starfire[0];
- break;
- case cheetah:
- case cheetah_plus:
- if (is_jbus)
- insns = &p->cheetah_jbus[0];
- else
- insns = &p->cheetah_safari[0];
- break;
- case hypervisor:
- insns = &p->sun4v[0];
- break;
- default:
- prom_printf("Unknown cpu type, halting.\n");
- prom_halt();
- };
-
- *(unsigned int *) (addr + 0) = insns[0];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 0));
-
- *(unsigned int *) (addr + 4) = insns[1];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 4));
-
- *(unsigned int *) (addr + 8) = insns[2];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 8));
-
- *(unsigned int *) (addr + 12) = insns[3];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 12));
-
- p++;
- }
-}
-
-void __init sun4v_patch(void)
-{
- extern void sun4v_hvapi_init(void);
- struct sun4v_1insn_patch_entry *p1;
- struct sun4v_2insn_patch_entry *p2;
-
- if (tlb_type != hypervisor)
- return;
-
- p1 = &__sun4v_1insn_patch;
- while (p1 < &__sun4v_1insn_patch_end) {
- unsigned long addr = p1->addr;
-
- *(unsigned int *) (addr + 0) = p1->insn;
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 0));
-
- p1++;
- }
-
- p2 = &__sun4v_2insn_patch;
- while (p2 < &__sun4v_2insn_patch_end) {
- unsigned long addr = p2->addr;
-
- *(unsigned int *) (addr + 0) = p2->insns[0];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 0));
-
- *(unsigned int *) (addr + 4) = p2->insns[1];
- wmb();
- __asm__ __volatile__("flush %0" : : "r" (addr + 4));
-
- p2++;
- }
-
- sun4v_hvapi_init();
-}
-
-#ifdef CONFIG_SMP
-void __init boot_cpu_id_too_large(int cpu)
-{
- prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n",
- cpu, NR_CPUS);
- prom_halt();
-}
-#endif
-
-void __init setup_arch(char **cmdline_p)
-{
- /* Initialize PROM console and command line. */
- *cmdline_p = prom_getbootargs();
- strcpy(boot_command_line, *cmdline_p);
- parse_early_param();
-
- boot_flags_init(*cmdline_p);
- register_console(&prom_early_console);
-
- if (tlb_type == hypervisor)
- printk("ARCH: SUN4V\n");
- else
- printk("ARCH: SUN4U\n");
-
-#ifdef CONFIG_DUMMY_CONSOLE
- conswitchp = &dummy_con;
-#elif defined(CONFIG_PROM_CONSOLE)
- conswitchp = &prom_con;
-#endif
-
- idprom_init();
-
- if (!root_flags)
- root_mountflags &= ~MS_RDONLY;
- ROOT_DEV = old_decode_dev(root_dev);
-#ifdef CONFIG_BLK_DEV_RAM
- rd_image_start = ram_flags & RAMDISK_IMAGE_START_MASK;
- rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
- rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
-#endif
-
- task_thread_info(&init_task)->kregs = &fake_swapper_regs;
-
-#ifdef CONFIG_IP_PNP
- if (!ic_set_manually) {
- int chosen = prom_finddevice ("/chosen");
- u32 cl, sv, gw;
-
- cl = prom_getintdefault (chosen, "client-ip", 0);
- sv = prom_getintdefault (chosen, "server-ip", 0);
- gw = prom_getintdefault (chosen, "gateway-ip", 0);
- if (cl && sv) {
- ic_myaddr = cl;
- ic_servaddr = sv;
- if (gw)
- ic_gateway = gw;
-#if defined(CONFIG_IP_PNP_BOOTP) || defined(CONFIG_IP_PNP_RARP)
- ic_proto_enabled = 0;
-#endif
- }
- }
-#endif
-
- /* Get boot processor trap_block[] setup. */
- init_cur_cpu_trap(current_thread_info());
-
- paging_init();
-}
-
-/* BUFFER is PAGE_SIZE bytes long. */
-
-extern void smp_info(struct seq_file *);
-extern void smp_bogo(struct seq_file *);
-extern void mmu_info(struct seq_file *);
-
-unsigned int dcache_parity_tl1_occurred;
-unsigned int icache_parity_tl1_occurred;
-
-int ncpus_probed;
-
-static int show_cpuinfo(struct seq_file *m, void *__unused)
-{
- seq_printf(m,
- "cpu\t\t: %s\n"
- "fpu\t\t: %s\n"
- "prom\t\t: %s\n"
- "type\t\t: %s\n"
- "ncpus probed\t: %d\n"
- "ncpus active\t: %d\n"
- "D$ parity tl1\t: %u\n"
- "I$ parity tl1\t: %u\n"
-#ifndef CONFIG_SMP
- "Cpu0ClkTck\t: %016lx\n"
-#endif
- ,
- sparc_cpu_type,
- sparc_fpu_type,
- prom_version,
- ((tlb_type == hypervisor) ?
- "sun4v" :
- "sun4u"),
- ncpus_probed,
- num_online_cpus(),
- dcache_parity_tl1_occurred,
- icache_parity_tl1_occurred
-#ifndef CONFIG_SMP
- , cpu_data(0).clock_tick
-#endif
- );
-#ifdef CONFIG_SMP
- smp_bogo(m);
-#endif
- mmu_info(m);
-#ifdef CONFIG_SMP
- smp_info(m);
-#endif
- return 0;
-}
-
-static void *c_start(struct seq_file *m, loff_t *pos)
-{
- /* The pointer we are returning is arbitrary,
- * it just has to be non-NULL and not IS_ERR
- * in the success case.
- */
- return *pos == 0 ? &c_start : NULL;
-}
-
-static void *c_next(struct seq_file *m, void *v, loff_t *pos)
-{
- ++*pos;
- return c_start(m, pos);
-}
-
-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 = show_cpuinfo,
-};
-
-extern int stop_a_enabled;
-
-void sun_do_break(void)
-{
- if (!stop_a_enabled)
- return;
-
- prom_printf("\n");
- flush_user_windows();
-
- prom_cmdline();
-}
-
-int stop_a_enabled = 1;
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
deleted file mode 100644
index a97b8822c22c..000000000000
--- a/arch/sparc64/kernel/smp.c
+++ /dev/null
@@ -1,1409 +0,0 @@
-/* smp.c: Sparc64 SMP support.
- *
- * Copyright (C) 1997, 2007, 2008 David S. Miller (davem@davemloft.net)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/threads.h>
-#include <linux/smp.h>
-#include <linux/interrupt.h>
-#include <linux/kernel_stat.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/cache.h>
-#include <linux/jiffies.h>
-#include <linux/profile.h>
-#include <linux/lmb.h>
-#include <linux/cpu.h>
-
-#include <asm/head.h>
-#include <asm/ptrace.h>
-#include <asm/atomic.h>
-#include <asm/tlbflush.h>
-#include <asm/mmu_context.h>
-#include <asm/cpudata.h>
-#include <asm/hvtramp.h>
-#include <asm/io.h>
-#include <asm/timer.h>
-
-#include <asm/irq.h>
-#include <asm/irq_regs.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/oplib.h>
-#include <asm/uaccess.h>
-#include <asm/starfire.h>
-#include <asm/tlb.h>
-#include <asm/sections.h>
-#include <asm/prom.h>
-#include <asm/mdesc.h>
-#include <asm/ldc.h>
-#include <asm/hypervisor.h>
-
-int sparc64_multi_core __read_mostly;
-
-DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE;
-cpumask_t cpu_core_map[NR_CPUS] __read_mostly =
- { [0 ... NR_CPUS-1] = CPU_MASK_NONE };
-
-EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);
-EXPORT_SYMBOL(cpu_core_map);
-
-static cpumask_t smp_commenced_mask;
-
-void smp_info(struct seq_file *m)
-{
- int i;
-
- seq_printf(m, "State:\n");
- for_each_online_cpu(i)
- seq_printf(m, "CPU%d:\t\tonline\n", i);
-}
-
-void smp_bogo(struct seq_file *m)
-{
- int i;
-
- for_each_online_cpu(i)
- seq_printf(m,
- "Cpu%dClkTck\t: %016lx\n",
- i, cpu_data(i).clock_tick);
-}
-
-extern void setup_sparc64_timer(void);
-
-static volatile unsigned long callin_flag = 0;
-
-void __cpuinit smp_callin(void)
-{
- int cpuid = hard_smp_processor_id();
-
- __local_per_cpu_offset = __per_cpu_offset(cpuid);
-
- if (tlb_type == hypervisor)
- sun4v_ktsb_register();
-
- __flush_tlb_all();
-
- setup_sparc64_timer();
-
- if (cheetah_pcache_forced_on)
- cheetah_enable_pcache();
-
- local_irq_enable();
-
- callin_flag = 1;
- __asm__ __volatile__("membar #Sync\n\t"
- "flush %%g6" : : : "memory");
-
- /* Clear this or we will die instantly when we
- * schedule back to this idler...
- */
- current_thread_info()->new_child = 0;
-
- /* Attach to the address space of init_task. */
- atomic_inc(&init_mm.mm_count);
- current->active_mm = &init_mm;
-
- /* inform the notifiers about the new cpu */
- notify_cpu_starting(cpuid);
-
- while (!cpu_isset(cpuid, smp_commenced_mask))
- rmb();
-
- ipi_call_lock();
- cpu_set(cpuid, cpu_online_map);
- ipi_call_unlock();
-
- /* idle thread is expected to have preempt disabled */
- preempt_disable();
-}
-
-void cpu_panic(void)
-{
- printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id());
- panic("SMP bolixed\n");
-}
-
-/* This tick register synchronization scheme is taken entirely from
- * the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit.
- *
- * The only change I've made is to rework it so that the master
- * initiates the synchonization instead of the slave. -DaveM
- */
-
-#define MASTER 0
-#define SLAVE (SMP_CACHE_BYTES/sizeof(unsigned long))
-
-#define NUM_ROUNDS 64 /* magic value */
-#define NUM_ITERS 5 /* likewise */
-
-static DEFINE_SPINLOCK(itc_sync_lock);
-static unsigned long go[SLAVE + 1];
-
-#define DEBUG_TICK_SYNC 0
-
-static inline long get_delta (long *rt, long *master)
-{
- unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
- unsigned long tcenter, t0, t1, tm;
- unsigned long i;
-
- for (i = 0; i < NUM_ITERS; i++) {
- t0 = tick_ops->get_tick();
- go[MASTER] = 1;
- membar_storeload();
- while (!(tm = go[SLAVE]))
- rmb();
- go[SLAVE] = 0;
- wmb();
- t1 = tick_ops->get_tick();
-
- if (t1 - t0 < best_t1 - best_t0)
- best_t0 = t0, best_t1 = t1, best_tm = tm;
- }
-
- *rt = best_t1 - best_t0;
- *master = best_tm - best_t0;
-
- /* average best_t0 and best_t1 without overflow: */
- tcenter = (best_t0/2 + best_t1/2);
- if (best_t0 % 2 + best_t1 % 2 == 2)
- tcenter++;
- return tcenter - best_tm;
-}
-
-void smp_synchronize_tick_client(void)
-{
- long i, delta, adj, adjust_latency = 0, done = 0;
- unsigned long flags, rt, master_time_stamp, bound;
-#if DEBUG_TICK_SYNC
- struct {
- long rt; /* roundtrip time */
- long master; /* master's timestamp */
- long diff; /* difference between midpoint and master's timestamp */
- long lat; /* estimate of itc adjustment latency */
- } t[NUM_ROUNDS];
-#endif
-
- go[MASTER] = 1;
-
- while (go[MASTER])
- rmb();
-
- local_irq_save(flags);
- {
- for (i = 0; i < NUM_ROUNDS; i++) {
- delta = get_delta(&rt, &master_time_stamp);
- if (delta == 0) {
- done = 1; /* let's lock on to this... */
- bound = rt;
- }
-
- if (!done) {
- if (i > 0) {
- adjust_latency += -delta;
- adj = -delta + adjust_latency/4;
- } else
- adj = -delta;
-
- tick_ops->add_tick(adj);
- }
-#if DEBUG_TICK_SYNC
- t[i].rt = rt;
- t[i].master = master_time_stamp;
- t[i].diff = delta;
- t[i].lat = adjust_latency/4;
-#endif
- }
- }
- local_irq_restore(flags);
-
-#if DEBUG_TICK_SYNC
- for (i = 0; i < NUM_ROUNDS; i++)
- printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
- t[i].rt, t[i].master, t[i].diff, t[i].lat);
-#endif
-
- printk(KERN_INFO "CPU %d: synchronized TICK with master CPU "
- "(last diff %ld cycles, maxerr %lu cycles)\n",
- smp_processor_id(), delta, rt);
-}
-
-static void smp_start_sync_tick_client(int cpu);
-
-static void smp_synchronize_one_tick(int cpu)
-{
- unsigned long flags, i;
-
- go[MASTER] = 0;
-
- smp_start_sync_tick_client(cpu);
-
- /* wait for client to be ready */
- while (!go[MASTER])
- rmb();
-
- /* now let the client proceed into his loop */
- go[MASTER] = 0;
- membar_storeload();
-
- spin_lock_irqsave(&itc_sync_lock, flags);
- {
- for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) {
- while (!go[MASTER])
- rmb();
- go[MASTER] = 0;
- wmb();
- go[SLAVE] = tick_ops->get_tick();
- membar_storeload();
- }
- }
- spin_unlock_irqrestore(&itc_sync_lock, flags);
-}
-
-#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
-/* XXX Put this in some common place. XXX */
-static unsigned long kimage_addr_to_ra(void *p)
-{
- unsigned long val = (unsigned long) p;
-
- return kern_base + (val - KERNBASE);
-}
-
-static void __cpuinit ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg)
-{
- extern unsigned long sparc64_ttable_tl0;
- extern unsigned long kern_locked_tte_data;
- struct hvtramp_descr *hdesc;
- unsigned long trampoline_ra;
- struct trap_per_cpu *tb;
- u64 tte_vaddr, tte_data;
- unsigned long hv_err;
- int i;
-
- hdesc = kzalloc(sizeof(*hdesc) +
- (sizeof(struct hvtramp_mapping) *
- num_kernel_image_mappings - 1),
- GFP_KERNEL);
- if (!hdesc) {
- printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate "
- "hvtramp_descr.\n");
- return;
- }
-
- hdesc->cpu = cpu;
- hdesc->num_mappings = num_kernel_image_mappings;
-
- tb = &trap_block[cpu];
- tb->hdesc = hdesc;
-
- hdesc->fault_info_va = (unsigned long) &tb->fault_info;
- hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info);
-
- hdesc->thread_reg = thread_reg;
-
- tte_vaddr = (unsigned long) KERNBASE;
- tte_data = kern_locked_tte_data;
-
- for (i = 0; i < hdesc->num_mappings; i++) {
- hdesc->maps[i].vaddr = tte_vaddr;
- hdesc->maps[i].tte = tte_data;
- tte_vaddr += 0x400000;
- tte_data += 0x400000;
- }
-
- trampoline_ra = kimage_addr_to_ra(hv_cpu_startup);
-
- hv_err = sun4v_cpu_start(cpu, trampoline_ra,
- kimage_addr_to_ra(&sparc64_ttable_tl0),
- __pa(hdesc));
- if (hv_err)
- printk(KERN_ERR "ldom_startcpu_cpuid: sun4v_cpu_start() "
- "gives error %lu\n", hv_err);
-}
-#endif
-
-extern unsigned long sparc64_cpu_startup;
-
-/* The OBP cpu startup callback truncates the 3rd arg cookie to
- * 32-bits (I think) so to be safe we have it read the pointer
- * contained here so we work on >4GB machines. -DaveM
- */
-static struct thread_info *cpu_new_thread = NULL;
-
-static int __cpuinit smp_boot_one_cpu(unsigned int cpu)
-{
- struct trap_per_cpu *tb = &trap_block[cpu];
- unsigned long entry =
- (unsigned long)(&sparc64_cpu_startup);
- unsigned long cookie =
- (unsigned long)(&cpu_new_thread);
- struct task_struct *p;
- int timeout, ret;
-
- p = fork_idle(cpu);
- if (IS_ERR(p))
- return PTR_ERR(p);
- callin_flag = 0;
- cpu_new_thread = task_thread_info(p);
-
- if (tlb_type == hypervisor) {
-#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
- if (ldom_domaining_enabled)
- ldom_startcpu_cpuid(cpu,
- (unsigned long) cpu_new_thread);
- else
-#endif
- prom_startcpu_cpuid(cpu, entry, cookie);
- } else {
- struct device_node *dp = of_find_node_by_cpuid(cpu);
-
- prom_startcpu(dp->node, entry, cookie);
- }
-
- for (timeout = 0; timeout < 50000; timeout++) {
- if (callin_flag)
- break;
- udelay(100);
- }
-
- if (callin_flag) {
- ret = 0;
- } else {
- printk("Processor %d is stuck.\n", cpu);
- ret = -ENODEV;
- }
- cpu_new_thread = NULL;
-
- if (tb->hdesc) {
- kfree(tb->hdesc);
- tb->hdesc = NULL;
- }
-
- return ret;
-}
-
-static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
-{
- u64 result, target;
- int stuck, tmp;
-
- if (this_is_starfire) {
- /* map to real upaid */
- cpu = (((cpu & 0x3c) << 1) |
- ((cpu & 0x40) >> 4) |
- (cpu & 0x3));
- }
-
- target = (cpu << 14) | 0x70;
-again:
- /* Ok, this is the real Spitfire Errata #54.
- * One must read back from a UDB internal register
- * after writes to the UDB interrupt dispatch, but
- * before the membar Sync for that write.
- * So we use the high UDB control register (ASI 0x7f,
- * ADDR 0x20) for the dummy read. -DaveM
- */
- tmp = 0x40;
- __asm__ __volatile__(
- "wrpr %1, %2, %%pstate\n\t"
- "stxa %4, [%0] %3\n\t"
- "stxa %5, [%0+%8] %3\n\t"
- "add %0, %8, %0\n\t"
- "stxa %6, [%0+%8] %3\n\t"
- "membar #Sync\n\t"
- "stxa %%g0, [%7] %3\n\t"
- "membar #Sync\n\t"
- "mov 0x20, %%g1\n\t"
- "ldxa [%%g1] 0x7f, %%g0\n\t"
- "membar #Sync"
- : "=r" (tmp)
- : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_INTR_W),
- "r" (data0), "r" (data1), "r" (data2), "r" (target),
- "r" (0x10), "0" (tmp)
- : "g1");
-
- /* NOTE: PSTATE_IE is still clear. */
- stuck = 100000;
- do {
- __asm__ __volatile__("ldxa [%%g0] %1, %0"
- : "=r" (result)
- : "i" (ASI_INTR_DISPATCH_STAT));
- if (result == 0) {
- __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
- : : "r" (pstate));
- return;
- }
- stuck -= 1;
- if (stuck == 0)
- break;
- } while (result & 0x1);
- __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
- : : "r" (pstate));
- if (stuck == 0) {
- printk("CPU[%d]: mondo stuckage result[%016lx]\n",
- smp_processor_id(), result);
- } else {
- udelay(2);
- goto again;
- }
-}
-
-static void spitfire_xcall_deliver(struct trap_per_cpu *tb, int cnt)
-{
- u64 *mondo, data0, data1, data2;
- u16 *cpu_list;
- u64 pstate;
- int i;
-
- __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
- cpu_list = __va(tb->cpu_list_pa);
- mondo = __va(tb->cpu_mondo_block_pa);
- data0 = mondo[0];
- data1 = mondo[1];
- data2 = mondo[2];
- for (i = 0; i < cnt; i++)
- spitfire_xcall_helper(data0, data1, data2, pstate, cpu_list[i]);
-}
-
-/* Cheetah now allows to send the whole 64-bytes of data in the interrupt
- * packet, but we have no use for that. However we do take advantage of
- * the new pipelining feature (ie. dispatch to multiple cpus simultaneously).
- */
-static void cheetah_xcall_deliver(struct trap_per_cpu *tb, int cnt)
-{
- int nack_busy_id, is_jbus, need_more;
- u64 *mondo, pstate, ver, busy_mask;
- u16 *cpu_list;
-
- cpu_list = __va(tb->cpu_list_pa);
- mondo = __va(tb->cpu_mondo_block_pa);
-
- /* Unfortunately, someone at Sun had the brilliant idea to make the
- * busy/nack fields hard-coded by ITID number for this Ultra-III
- * derivative processor.
- */
- __asm__ ("rdpr %%ver, %0" : "=r" (ver));
- is_jbus = ((ver >> 32) == __JALAPENO_ID ||
- (ver >> 32) == __SERRANO_ID);
-
- __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
-
-retry:
- need_more = 0;
- __asm__ __volatile__("wrpr %0, %1, %%pstate\n\t"
- : : "r" (pstate), "i" (PSTATE_IE));
-
- /* Setup the dispatch data registers. */
- __asm__ __volatile__("stxa %0, [%3] %6\n\t"
- "stxa %1, [%4] %6\n\t"
- "stxa %2, [%5] %6\n\t"
- "membar #Sync\n\t"
- : /* no outputs */
- : "r" (mondo[0]), "r" (mondo[1]), "r" (mondo[2]),
- "r" (0x40), "r" (0x50), "r" (0x60),
- "i" (ASI_INTR_W));
-
- nack_busy_id = 0;
- busy_mask = 0;
- {
- int i;
-
- for (i = 0; i < cnt; i++) {
- u64 target, nr;
-
- nr = cpu_list[i];
- if (nr == 0xffff)
- continue;
-
- target = (nr << 14) | 0x70;
- if (is_jbus) {
- busy_mask |= (0x1UL << (nr * 2));
- } else {
- target |= (nack_busy_id << 24);
- busy_mask |= (0x1UL <<
- (nack_busy_id * 2));
- }
- __asm__ __volatile__(
- "stxa %%g0, [%0] %1\n\t"
- "membar #Sync\n\t"
- : /* no outputs */
- : "r" (target), "i" (ASI_INTR_W));
- nack_busy_id++;
- if (nack_busy_id == 32) {
- need_more = 1;
- break;
- }
- }
- }
-
- /* Now, poll for completion. */
- {
- u64 dispatch_stat, nack_mask;
- long stuck;
-
- stuck = 100000 * nack_busy_id;
- nack_mask = busy_mask << 1;
- do {
- __asm__ __volatile__("ldxa [%%g0] %1, %0"
- : "=r" (dispatch_stat)
- : "i" (ASI_INTR_DISPATCH_STAT));
- if (!(dispatch_stat & (busy_mask | nack_mask))) {
- __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
- : : "r" (pstate));
- if (unlikely(need_more)) {
- int i, this_cnt = 0;
- for (i = 0; i < cnt; i++) {
- if (cpu_list[i] == 0xffff)
- continue;
- cpu_list[i] = 0xffff;
- this_cnt++;
- if (this_cnt == 32)
- break;
- }
- goto retry;
- }
- return;
- }
- if (!--stuck)
- break;
- } while (dispatch_stat & busy_mask);
-
- __asm__ __volatile__("wrpr %0, 0x0, %%pstate"
- : : "r" (pstate));
-
- if (dispatch_stat & busy_mask) {
- /* Busy bits will not clear, continue instead
- * of freezing up on this cpu.
- */
- printk("CPU[%d]: mondo stuckage result[%016lx]\n",
- smp_processor_id(), dispatch_stat);
- } else {
- int i, this_busy_nack = 0;
-
- /* Delay some random time with interrupts enabled
- * to prevent deadlock.
- */
- udelay(2 * nack_busy_id);
-
- /* Clear out the mask bits for cpus which did not
- * NACK us.
- */
- for (i = 0; i < cnt; i++) {
- u64 check_mask, nr;
-
- nr = cpu_list[i];
- if (nr == 0xffff)
- continue;
-
- if (is_jbus)
- check_mask = (0x2UL << (2*nr));
- else
- check_mask = (0x2UL <<
- this_busy_nack);
- if ((dispatch_stat & check_mask) == 0)
- cpu_list[i] = 0xffff;
- this_busy_nack += 2;
- if (this_busy_nack == 64)
- break;
- }
-
- goto retry;
- }
- }
-}
-
-/* Multi-cpu list version. */
-static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
-{
- int retries, this_cpu, prev_sent, i, saw_cpu_error;
- unsigned long status;
- u16 *cpu_list;
-
- this_cpu = smp_processor_id();
-
- cpu_list = __va(tb->cpu_list_pa);
-
- saw_cpu_error = 0;
- retries = 0;
- prev_sent = 0;
- do {
- int forward_progress, n_sent;
-
- status = sun4v_cpu_mondo_send(cnt,
- tb->cpu_list_pa,
- tb->cpu_mondo_block_pa);
-
- /* HV_EOK means all cpus received the xcall, we're done. */
- if (likely(status == HV_EOK))
- break;
-
- /* First, see if we made any forward progress.
- *
- * The hypervisor indicates successful sends by setting
- * cpu list entries to the value 0xffff.
- */
- n_sent = 0;
- for (i = 0; i < cnt; i++) {
- if (likely(cpu_list[i] == 0xffff))
- n_sent++;
- }
-
- forward_progress = 0;
- if (n_sent > prev_sent)
- forward_progress = 1;
-
- prev_sent = n_sent;
-
- /* If we get a HV_ECPUERROR, then one or more of the cpus
- * in the list are in error state. Use the cpu_state()
- * hypervisor call to find out which cpus are in error state.
- */
- if (unlikely(status == HV_ECPUERROR)) {
- for (i = 0; i < cnt; i++) {
- long err;
- u16 cpu;
-
- cpu = cpu_list[i];
- if (cpu == 0xffff)
- continue;
-
- err = sun4v_cpu_state(cpu);
- if (err == HV_CPU_STATE_ERROR) {
- saw_cpu_error = (cpu + 1);
- cpu_list[i] = 0xffff;
- }
- }
- } else if (unlikely(status != HV_EWOULDBLOCK))
- goto fatal_mondo_error;
-
- /* Don't bother rewriting the CPU list, just leave the
- * 0xffff and non-0xffff entries in there and the
- * hypervisor will do the right thing.
- *
- * Only advance timeout state if we didn't make any
- * forward progress.
- */
- if (unlikely(!forward_progress)) {
- if (unlikely(++retries > 10000))
- goto fatal_mondo_timeout;
-
- /* Delay a little bit to let other cpus catch up
- * on their cpu mondo queue work.
- */
- udelay(2 * cnt);
- }
- } while (1);
-
- if (unlikely(saw_cpu_error))
- goto fatal_mondo_cpu_error;
-
- return;
-
-fatal_mondo_cpu_error:
- printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus "
- "(including %d) were in error state\n",
- this_cpu, saw_cpu_error - 1);
- return;
-
-fatal_mondo_timeout:
- printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward "
- " progress after %d retries.\n",
- this_cpu, retries);
- goto dump_cpu_list_and_out;
-
-fatal_mondo_error:
- printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n",
- this_cpu, status);
- printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) "
- "mondo_block_pa(%lx)\n",
- this_cpu, cnt, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
-
-dump_cpu_list_and_out:
- printk(KERN_CRIT "CPU[%d]: CPU list [ ", this_cpu);
- for (i = 0; i < cnt; i++)
- printk("%u ", cpu_list[i]);
- printk("]\n");
-}
-
-static void (*xcall_deliver_impl)(struct trap_per_cpu *, int);
-
-static void xcall_deliver(u64 data0, u64 data1, u64 data2, const cpumask_t *mask)
-{
- struct trap_per_cpu *tb;
- int this_cpu, i, cnt;
- unsigned long flags;
- u16 *cpu_list;
- u64 *mondo;
-
- /* We have to do this whole thing with interrupts fully disabled.
- * Otherwise if we send an xcall from interrupt context it will
- * corrupt both our mondo block and cpu list state.
- *
- * One consequence of this is that we cannot use timeout mechanisms
- * that depend upon interrupts being delivered locally. So, for
- * example, we cannot sample jiffies and expect it to advance.
- *
- * Fortunately, udelay() uses %stick/%tick so we can use that.
- */
- local_irq_save(flags);
-
- this_cpu = smp_processor_id();
- tb = &trap_block[this_cpu];
-
- mondo = __va(tb->cpu_mondo_block_pa);
- mondo[0] = data0;
- mondo[1] = data1;
- mondo[2] = data2;
- wmb();
-
- cpu_list = __va(tb->cpu_list_pa);
-
- /* Setup the initial cpu list. */
- cnt = 0;
- for_each_cpu_mask_nr(i, *mask) {
- if (i == this_cpu || !cpu_online(i))
- continue;
- cpu_list[cnt++] = i;
- }
-
- if (cnt)
- xcall_deliver_impl(tb, cnt);
-
- local_irq_restore(flags);
-}
-
-/* Send cross call to all processors mentioned in MASK_P
- * except self. Really, there are only two cases currently,
- * "&cpu_online_map" and "&mm->cpu_vm_mask".
- */
-static void smp_cross_call_masked(unsigned long *func, u32 ctx, u64 data1, u64 data2, const cpumask_t *mask)
-{
- u64 data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff));
-
- xcall_deliver(data0, data1, data2, mask);
-}
-
-/* Send cross call to all processors except self. */
-static void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2)
-{
- smp_cross_call_masked(func, ctx, data1, data2, &cpu_online_map);
-}
-
-extern unsigned long xcall_sync_tick;
-
-static void smp_start_sync_tick_client(int cpu)
-{
- xcall_deliver((u64) &xcall_sync_tick, 0, 0,
- &cpumask_of_cpu(cpu));
-}
-
-extern unsigned long xcall_call_function;
-
-void arch_send_call_function_ipi(cpumask_t mask)
-{
- xcall_deliver((u64) &xcall_call_function, 0, 0, &mask);
-}
-
-extern unsigned long xcall_call_function_single;
-
-void arch_send_call_function_single_ipi(int cpu)
-{
- xcall_deliver((u64) &xcall_call_function_single, 0, 0,
- &cpumask_of_cpu(cpu));
-}
-
-void smp_call_function_client(int irq, struct pt_regs *regs)
-{
- clear_softint(1 << irq);
- generic_smp_call_function_interrupt();
-}
-
-void smp_call_function_single_client(int irq, struct pt_regs *regs)
-{
- clear_softint(1 << irq);
- generic_smp_call_function_single_interrupt();
-}
-
-static void tsb_sync(void *info)
-{
- struct trap_per_cpu *tp = &trap_block[raw_smp_processor_id()];
- struct mm_struct *mm = info;
-
- /* It is not valid to test "currrent->active_mm == mm" here.
- *
- * The value of "current" is not changed atomically with
- * switch_mm(). But that's OK, we just need to check the
- * current cpu's trap block PGD physical address.
- */
- if (tp->pgd_paddr == __pa(mm->pgd))
- tsb_context_switch(mm);
-}
-
-void smp_tsb_sync(struct mm_struct *mm)
-{
- smp_call_function_mask(mm->cpu_vm_mask, tsb_sync, mm, 1);
-}
-
-extern unsigned long xcall_flush_tlb_mm;
-extern unsigned long xcall_flush_tlb_pending;
-extern unsigned long xcall_flush_tlb_kernel_range;
-extern unsigned long xcall_fetch_glob_regs;
-extern unsigned long xcall_receive_signal;
-extern unsigned long xcall_new_mmu_context_version;
-#ifdef CONFIG_KGDB
-extern unsigned long xcall_kgdb_capture;
-#endif
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-extern unsigned long xcall_flush_dcache_page_cheetah;
-#endif
-extern unsigned long xcall_flush_dcache_page_spitfire;
-
-#ifdef CONFIG_DEBUG_DCFLUSH
-extern atomic_t dcpage_flushes;
-extern atomic_t dcpage_flushes_xcall;
-#endif
-
-static inline void __local_flush_dcache_page(struct page *page)
-{
-#ifdef DCACHE_ALIASING_POSSIBLE
- __flush_dcache_page(page_address(page),
- ((tlb_type == spitfire) &&
- page_mapping(page) != NULL));
-#else
- if (page_mapping(page) != NULL &&
- tlb_type == spitfire)
- __flush_icache_page(__pa(page_address(page)));
-#endif
-}
-
-void smp_flush_dcache_page_impl(struct page *page, int cpu)
-{
- int this_cpu;
-
- if (tlb_type == hypervisor)
- return;
-
-#ifdef CONFIG_DEBUG_DCFLUSH
- atomic_inc(&dcpage_flushes);
-#endif
-
- this_cpu = get_cpu();
-
- if (cpu == this_cpu) {
- __local_flush_dcache_page(page);
- } else if (cpu_online(cpu)) {
- void *pg_addr = page_address(page);
- u64 data0 = 0;
-
- if (tlb_type == spitfire) {
- data0 = ((u64)&xcall_flush_dcache_page_spitfire);
- if (page_mapping(page) != NULL)
- data0 |= ((u64)1 << 32);
- } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-#ifdef DCACHE_ALIASING_POSSIBLE
- data0 = ((u64)&xcall_flush_dcache_page_cheetah);
-#endif
- }
- if (data0) {
- xcall_deliver(data0, __pa(pg_addr),
- (u64) pg_addr, &cpumask_of_cpu(cpu));
-#ifdef CONFIG_DEBUG_DCFLUSH
- atomic_inc(&dcpage_flushes_xcall);
-#endif
- }
- }
-
- put_cpu();
-}
-
-void flush_dcache_page_all(struct mm_struct *mm, struct page *page)
-{
- void *pg_addr;
- int this_cpu;
- u64 data0;
-
- if (tlb_type == hypervisor)
- return;
-
- this_cpu = get_cpu();
-
-#ifdef CONFIG_DEBUG_DCFLUSH
- atomic_inc(&dcpage_flushes);
-#endif
- data0 = 0;
- pg_addr = page_address(page);
- if (tlb_type == spitfire) {
- data0 = ((u64)&xcall_flush_dcache_page_spitfire);
- if (page_mapping(page) != NULL)
- data0 |= ((u64)1 << 32);
- } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-#ifdef DCACHE_ALIASING_POSSIBLE
- data0 = ((u64)&xcall_flush_dcache_page_cheetah);
-#endif
- }
- if (data0) {
- xcall_deliver(data0, __pa(pg_addr),
- (u64) pg_addr, &cpu_online_map);
-#ifdef CONFIG_DEBUG_DCFLUSH
- atomic_inc(&dcpage_flushes_xcall);
-#endif
- }
- __local_flush_dcache_page(page);
-
- put_cpu();
-}
-
-void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs)
-{
- struct mm_struct *mm;
- unsigned long flags;
-
- clear_softint(1 << irq);
-
- /* See if we need to allocate a new TLB context because
- * the version of the one we are using is now out of date.
- */
- mm = current->active_mm;
- if (unlikely(!mm || (mm == &init_mm)))
- return;
-
- spin_lock_irqsave(&mm->context.lock, flags);
-
- if (unlikely(!CTX_VALID(mm->context)))
- get_new_mmu_context(mm);
-
- spin_unlock_irqrestore(&mm->context.lock, flags);
-
- load_secondary_context(mm);
- __flush_tlb_mm(CTX_HWBITS(mm->context),
- SECONDARY_CONTEXT);
-}
-
-void smp_new_mmu_context_version(void)
-{
- smp_cross_call(&xcall_new_mmu_context_version, 0, 0, 0);
-}
-
-#ifdef CONFIG_KGDB
-void kgdb_roundup_cpus(unsigned long flags)
-{
- smp_cross_call(&xcall_kgdb_capture, 0, 0, 0);
-}
-#endif
-
-void smp_fetch_global_regs(void)
-{
- smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
-}
-
-/* We know that the window frames of the user have been flushed
- * to the stack before we get here because all callers of us
- * are flush_tlb_*() routines, and these run after flush_cache_*()
- * which performs the flushw.
- *
- * The SMP TLB coherency scheme we use works as follows:
- *
- * 1) mm->cpu_vm_mask is a bit mask of which cpus an address
- * space has (potentially) executed on, this is the heuristic
- * we use to avoid doing cross calls.
- *
- * Also, for flushing from kswapd and also for clones, we
- * use cpu_vm_mask as the list of cpus to make run the TLB.
- *
- * 2) TLB context numbers are shared globally across all processors
- * in the system, this allows us to play several games to avoid
- * cross calls.
- *
- * One invariant is that when a cpu switches to a process, and
- * that processes tsk->active_mm->cpu_vm_mask does not have the
- * current cpu's bit set, that tlb context is flushed locally.
- *
- * If the address space is non-shared (ie. mm->count == 1) we avoid
- * cross calls when we want to flush the currently running process's
- * tlb state. This is done by clearing all cpu bits except the current
- * processor's in current->active_mm->cpu_vm_mask and performing the
- * flush locally only. This will force any subsequent cpus which run
- * this task to flush the context from the local tlb if the process
- * migrates to another cpu (again).
- *
- * 3) For shared address spaces (threads) and swapping we bite the
- * bullet for most cases and perform the cross call (but only to
- * the cpus listed in cpu_vm_mask).
- *
- * The performance gain from "optimizing" away the cross call for threads is
- * questionable (in theory the big win for threads is the massive sharing of
- * address space state across processors).
- */
-
-/* This currently is only used by the hugetlb arch pre-fault
- * hook on UltraSPARC-III+ and later when changing the pagesize
- * bits of the context register for an address space.
- */
-void smp_flush_tlb_mm(struct mm_struct *mm)
-{
- u32 ctx = CTX_HWBITS(mm->context);
- int cpu = get_cpu();
-
- if (atomic_read(&mm->mm_users) == 1) {
- mm->cpu_vm_mask = cpumask_of_cpu(cpu);
- goto local_flush_and_out;
- }
-
- smp_cross_call_masked(&xcall_flush_tlb_mm,
- ctx, 0, 0,
- &mm->cpu_vm_mask);
-
-local_flush_and_out:
- __flush_tlb_mm(ctx, SECONDARY_CONTEXT);
-
- put_cpu();
-}
-
-void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long *vaddrs)
-{
- u32 ctx = CTX_HWBITS(mm->context);
- int cpu = get_cpu();
-
- if (mm == current->active_mm && atomic_read(&mm->mm_users) == 1)
- mm->cpu_vm_mask = cpumask_of_cpu(cpu);
- else
- smp_cross_call_masked(&xcall_flush_tlb_pending,
- ctx, nr, (unsigned long) vaddrs,
- &mm->cpu_vm_mask);
-
- __flush_tlb_pending(ctx, nr, vaddrs);
-
- put_cpu();
-}
-
-void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end)
-{
- start &= PAGE_MASK;
- end = PAGE_ALIGN(end);
- if (start != end) {
- smp_cross_call(&xcall_flush_tlb_kernel_range,
- 0, start, end);
-
- __flush_tlb_kernel_range(start, end);
- }
-}
-
-/* CPU capture. */
-/* #define CAPTURE_DEBUG */
-extern unsigned long xcall_capture;
-
-static atomic_t smp_capture_depth = ATOMIC_INIT(0);
-static atomic_t smp_capture_registry = ATOMIC_INIT(0);
-static unsigned long penguins_are_doing_time;
-
-void smp_capture(void)
-{
- int result = atomic_add_ret(1, &smp_capture_depth);
-
- if (result == 1) {
- int ncpus = num_online_cpus();
-
-#ifdef CAPTURE_DEBUG
- printk("CPU[%d]: Sending penguins to jail...",
- smp_processor_id());
-#endif
- penguins_are_doing_time = 1;
- membar_storestore_loadstore();
- atomic_inc(&smp_capture_registry);
- smp_cross_call(&xcall_capture, 0, 0, 0);
- while (atomic_read(&smp_capture_registry) != ncpus)
- rmb();
-#ifdef CAPTURE_DEBUG
- printk("done\n");
-#endif
- }
-}
-
-void smp_release(void)
-{
- if (atomic_dec_and_test(&smp_capture_depth)) {
-#ifdef CAPTURE_DEBUG
- printk("CPU[%d]: Giving pardon to "
- "imprisoned penguins\n",
- smp_processor_id());
-#endif
- penguins_are_doing_time = 0;
- membar_storeload_storestore();
- atomic_dec(&smp_capture_registry);
- }
-}
-
-/* Imprisoned penguins run with %pil == 15, but PSTATE_IE set, so they
- * can service tlb flush xcalls...
- */
-extern void prom_world(int);
-
-void smp_penguin_jailcell(int irq, struct pt_regs *regs)
-{
- clear_softint(1 << irq);
-
- preempt_disable();
-
- __asm__ __volatile__("flushw");
- prom_world(1);
- atomic_inc(&smp_capture_registry);
- membar_storeload_storestore();
- while (penguins_are_doing_time)
- rmb();
- atomic_dec(&smp_capture_registry);
- prom_world(0);
-
- preempt_enable();
-}
-
-/* /proc/profile writes can call this, don't __init it please. */
-int setup_profiling_timer(unsigned int multiplier)
-{
- return -EINVAL;
-}
-
-void __init smp_prepare_cpus(unsigned int max_cpus)
-{
-}
-
-void __devinit smp_prepare_boot_cpu(void)
-{
-}
-
-void __init smp_setup_processor_id(void)
-{
- if (tlb_type == spitfire)
- xcall_deliver_impl = spitfire_xcall_deliver;
- else if (tlb_type == cheetah || tlb_type == cheetah_plus)
- xcall_deliver_impl = cheetah_xcall_deliver;
- else
- xcall_deliver_impl = hypervisor_xcall_deliver;
-}
-
-void __devinit smp_fill_in_sib_core_maps(void)
-{
- unsigned int i;
-
- for_each_present_cpu(i) {
- unsigned int j;
-
- cpus_clear(cpu_core_map[i]);
- if (cpu_data(i).core_id == 0) {
- cpu_set(i, cpu_core_map[i]);
- continue;
- }
-
- for_each_present_cpu(j) {
- if (cpu_data(i).core_id ==
- cpu_data(j).core_id)
- cpu_set(j, cpu_core_map[i]);
- }
- }
-
- for_each_present_cpu(i) {
- unsigned int j;
-
- cpus_clear(per_cpu(cpu_sibling_map, i));
- if (cpu_data(i).proc_id == -1) {
- cpu_set(i, per_cpu(cpu_sibling_map, i));
- continue;
- }
-
- for_each_present_cpu(j) {
- if (cpu_data(i).proc_id ==
- cpu_data(j).proc_id)
- cpu_set(j, per_cpu(cpu_sibling_map, i));
- }
- }
-}
-
-int __cpuinit __cpu_up(unsigned int cpu)
-{
- int ret = smp_boot_one_cpu(cpu);
-
- if (!ret) {
- cpu_set(cpu, smp_commenced_mask);
- while (!cpu_isset(cpu, cpu_online_map))
- mb();
- if (!cpu_isset(cpu, cpu_online_map)) {
- ret = -ENODEV;
- } else {
- /* On SUN4V, writes to %tick and %stick are
- * not allowed.
- */
- if (tlb_type != hypervisor)
- smp_synchronize_one_tick(cpu);
- }
- }
- return ret;
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-void cpu_play_dead(void)
-{
- int cpu = smp_processor_id();
- unsigned long pstate;
-
- idle_task_exit();
-
- if (tlb_type == hypervisor) {
- struct trap_per_cpu *tb = &trap_block[cpu];
-
- sun4v_cpu_qconf(HV_CPU_QUEUE_CPU_MONDO,
- tb->cpu_mondo_pa, 0);
- sun4v_cpu_qconf(HV_CPU_QUEUE_DEVICE_MONDO,
- tb->dev_mondo_pa, 0);
- sun4v_cpu_qconf(HV_CPU_QUEUE_RES_ERROR,
- tb->resum_mondo_pa, 0);
- sun4v_cpu_qconf(HV_CPU_QUEUE_NONRES_ERROR,
- tb->nonresum_mondo_pa, 0);
- }
-
- cpu_clear(cpu, smp_commenced_mask);
- membar_safe("#Sync");
-
- local_irq_disable();
-
- __asm__ __volatile__(
- "rdpr %%pstate, %0\n\t"
- "wrpr %0, %1, %%pstate"
- : "=r" (pstate)
- : "i" (PSTATE_IE));
-
- while (1)
- barrier();
-}
-
-int __cpu_disable(void)
-{
- int cpu = smp_processor_id();
- cpuinfo_sparc *c;
- int i;
-
- for_each_cpu_mask(i, cpu_core_map[cpu])
- cpu_clear(cpu, cpu_core_map[i]);
- cpus_clear(cpu_core_map[cpu]);
-
- for_each_cpu_mask(i, per_cpu(cpu_sibling_map, cpu))
- cpu_clear(cpu, per_cpu(cpu_sibling_map, i));
- cpus_clear(per_cpu(cpu_sibling_map, cpu));
-
- c = &cpu_data(cpu);
-
- c->core_id = 0;
- c->proc_id = -1;
-
- smp_wmb();
-
- /* Make sure no interrupts point to this cpu. */
- fixup_irqs();
-
- local_irq_enable();
- mdelay(1);
- local_irq_disable();
-
- ipi_call_lock();
- cpu_clear(cpu, cpu_online_map);
- ipi_call_unlock();
-
- return 0;
-}
-
-void __cpu_die(unsigned int cpu)
-{
- int i;
-
- for (i = 0; i < 100; i++) {
- smp_rmb();
- if (!cpu_isset(cpu, smp_commenced_mask))
- break;
- msleep(100);
- }
- if (cpu_isset(cpu, smp_commenced_mask)) {
- printk(KERN_ERR "CPU %u didn't die...\n", cpu);
- } else {
-#if defined(CONFIG_SUN_LDOMS)
- unsigned long hv_err;
- int limit = 100;
-
- do {
- hv_err = sun4v_cpu_stop(cpu);
- if (hv_err == HV_EOK) {
- cpu_clear(cpu, cpu_present_map);
- break;
- }
- } while (--limit > 0);
- if (limit <= 0) {
- printk(KERN_ERR "sun4v_cpu_stop() fails err=%lu\n",
- hv_err);
- }
-#endif
- }
-}
-#endif
-
-void __init smp_cpus_done(unsigned int max_cpus)
-{
-}
-
-void smp_send_reschedule(int cpu)
-{
- xcall_deliver((u64) &xcall_receive_signal, 0, 0,
- &cpumask_of_cpu(cpu));
-}
-
-void smp_receive_signal_client(int irq, struct pt_regs *regs)
-{
- clear_softint(1 << irq);
-}
-
-/* This is a nop because we capture all other cpus
- * anyways when making the PROM active.
- */
-void smp_send_stop(void)
-{
-}
-
-unsigned long __per_cpu_base __read_mostly;
-unsigned long __per_cpu_shift __read_mostly;
-
-EXPORT_SYMBOL(__per_cpu_base);
-EXPORT_SYMBOL(__per_cpu_shift);
-
-void __init real_setup_per_cpu_areas(void)
-{
- unsigned long paddr, goal, size, i;
- char *ptr;
-
- /* Copy section for each CPU (we discard the original) */
- goal = PERCPU_ENOUGH_ROOM;
-
- __per_cpu_shift = PAGE_SHIFT;
- for (size = PAGE_SIZE; size < goal; size <<= 1UL)
- __per_cpu_shift++;
-
- paddr = lmb_alloc(size * NR_CPUS, PAGE_SIZE);
- if (!paddr) {
- prom_printf("Cannot allocate per-cpu memory.\n");
- prom_halt();
- }
-
- ptr = __va(paddr);
- __per_cpu_base = ptr - __per_cpu_start;
-
- for (i = 0; i < NR_CPUS; i++, ptr += size)
- memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
-
- /* Setup %g5 for the boot cpu. */
- __local_per_cpu_offset = __per_cpu_offset(smp_processor_id());
-}
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
deleted file mode 100644
index 30bba8b0a3b0..000000000000
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
- *
- * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-/* Tell string.h we don't want memcpy etc. as cpp defines */
-#define EXPORT_SYMTAB_STROPS
-#define PROMLIB_INTERNAL
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/sched.h>
-#include <linux/in6.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/fs_struct.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/syscalls.h>
-#include <linux/percpu.h>
-#include <linux/init.h>
-#include <linux/rwsem.h>
-#include <net/compat.h>
-
-#include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/auxio.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/idprom.h>
-#include <asm/elf.h>
-#include <asm/head.h>
-#include <asm/smp.h>
-#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-#include <asm/checksum.h>
-#include <asm/fpumacro.h>
-#include <asm/pgalloc.h>
-#include <asm/cacheflush.h>
-#ifdef CONFIG_SBUS
-#include <asm/dma.h>
-#endif
-#include <asm/ns87303.h>
-#include <asm/timer.h>
-#include <asm/cpudata.h>
-#include <asm/ftrace.h>
-
-struct poll {
- int fd;
- short events;
- short revents;
-};
-
-extern void die_if_kernel(char *str, struct pt_regs *regs);
-extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
-extern void *__bzero(void *, size_t);
-extern void *__memscan_zero(void *, size_t);
-extern void *__memscan_generic(void *, int, size_t);
-extern int __memcmp(const void *, const void *, __kernel_size_t);
-extern __kernel_size_t strlen(const char *);
-extern void sys_sigsuspend(void);
-extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
-extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
-extern long sparc32_open(const char __user * filename, int flags, int mode);
-extern int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
- unsigned long pfn, unsigned long size, pgprot_t prot);
-
-extern int __ashrdi3(int, int);
-
-extern int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs);
-
-extern void xor_vis_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_vis_3(unsigned long, unsigned long *, unsigned long *,
- unsigned long *);
-extern void xor_vis_4(unsigned long, unsigned long *, unsigned long *,
- unsigned long *, unsigned long *);
-extern void xor_vis_5(unsigned long, unsigned long *, unsigned long *,
- unsigned long *, unsigned long *, unsigned long *);
-
-extern void xor_niagara_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_niagara_3(unsigned long, unsigned long *, unsigned long *,
- unsigned long *);
-extern void xor_niagara_4(unsigned long, unsigned long *, unsigned long *,
- unsigned long *, unsigned long *);
-extern void xor_niagara_5(unsigned long, unsigned long *, unsigned long *,
- unsigned long *, unsigned long *, unsigned long *);
-
-/* Per-CPU information table */
-EXPORT_PER_CPU_SYMBOL(__cpu_data);
-
-/* used by various drivers */
-#ifdef CONFIG_SMP
-/* Out of line rw-locking implementation. */
-EXPORT_SYMBOL(__read_lock);
-EXPORT_SYMBOL(__read_unlock);
-EXPORT_SYMBOL(__write_lock);
-EXPORT_SYMBOL(__write_unlock);
-EXPORT_SYMBOL(__write_trylock);
-#endif /* CONFIG_SMP */
-
-#ifdef CONFIG_MCOUNT
-EXPORT_SYMBOL(_mcount);
-#endif
-
-EXPORT_SYMBOL(sparc64_get_clock_tick);
-
-/* RW semaphores */
-EXPORT_SYMBOL(__down_read);
-EXPORT_SYMBOL(__down_read_trylock);
-EXPORT_SYMBOL(__down_write);
-EXPORT_SYMBOL(__down_write_trylock);
-EXPORT_SYMBOL(__up_read);
-EXPORT_SYMBOL(__up_write);
-EXPORT_SYMBOL(__downgrade_write);
-
-/* Atomic counter implementation. */
-EXPORT_SYMBOL(atomic_add);
-EXPORT_SYMBOL(atomic_add_ret);
-EXPORT_SYMBOL(atomic_sub);
-EXPORT_SYMBOL(atomic_sub_ret);
-EXPORT_SYMBOL(atomic64_add);
-EXPORT_SYMBOL(atomic64_add_ret);
-EXPORT_SYMBOL(atomic64_sub);
-EXPORT_SYMBOL(atomic64_sub_ret);
-
-/* Atomic bit operations. */
-EXPORT_SYMBOL(test_and_set_bit);
-EXPORT_SYMBOL(test_and_clear_bit);
-EXPORT_SYMBOL(test_and_change_bit);
-EXPORT_SYMBOL(set_bit);
-EXPORT_SYMBOL(clear_bit);
-EXPORT_SYMBOL(change_bit);
-
-EXPORT_SYMBOL(__flushw_user);
-
-EXPORT_SYMBOL(tlb_type);
-EXPORT_SYMBOL(sun4v_chip_type);
-EXPORT_SYMBOL(get_fb_unmapped_area);
-EXPORT_SYMBOL(flush_icache_range);
-
-EXPORT_SYMBOL(flush_dcache_page);
-#ifdef DCACHE_ALIASING_POSSIBLE
-EXPORT_SYMBOL(__flush_dcache_range);
-#endif
-
-#ifdef CONFIG_SUN_AUXIO
-EXPORT_SYMBOL(auxio_set_led);
-EXPORT_SYMBOL(auxio_set_lte);
-#endif
-#ifdef CONFIG_SBUS
-EXPORT_SYMBOL(sbus_set_sbus64);
-#endif
-EXPORT_SYMBOL(outsb);
-EXPORT_SYMBOL(outsw);
-EXPORT_SYMBOL(outsl);
-EXPORT_SYMBOL(insb);
-EXPORT_SYMBOL(insw);
-EXPORT_SYMBOL(insl);
-#ifdef CONFIG_PCI
-EXPORT_SYMBOL(pci_alloc_consistent);
-EXPORT_SYMBOL(pci_free_consistent);
-EXPORT_SYMBOL(pci_map_single);
-EXPORT_SYMBOL(pci_unmap_single);
-EXPORT_SYMBOL(pci_map_sg);
-EXPORT_SYMBOL(pci_unmap_sg);
-EXPORT_SYMBOL(pci_dma_sync_single_for_cpu);
-EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu);
-EXPORT_SYMBOL(pci_dma_supported);
-#endif
-
-/* I/O device mmaping on Sparc64. */
-EXPORT_SYMBOL(io_remap_pfn_range);
-
-EXPORT_SYMBOL(dump_fpu);
-EXPORT_SYMBOL(put_fs_struct);
-
-/* math-emu wants this */
-EXPORT_SYMBOL(die_if_kernel);
-
-/* Kernel thread creation. */
-EXPORT_SYMBOL(kernel_thread);
-
-/* prom symbols */
-EXPORT_SYMBOL(idprom);
-EXPORT_SYMBOL(prom_root_node);
-EXPORT_SYMBOL(prom_getchild);
-EXPORT_SYMBOL(prom_getsibling);
-EXPORT_SYMBOL(prom_searchsiblings);
-EXPORT_SYMBOL(prom_firstprop);
-EXPORT_SYMBOL(prom_nextprop);
-EXPORT_SYMBOL(prom_getproplen);
-EXPORT_SYMBOL(prom_getproperty);
-EXPORT_SYMBOL(prom_node_has_property);
-EXPORT_SYMBOL(prom_setprop);
-EXPORT_SYMBOL(saved_command_line);
-EXPORT_SYMBOL(prom_finddevice);
-EXPORT_SYMBOL(prom_feval);
-EXPORT_SYMBOL(prom_getbool);
-EXPORT_SYMBOL(prom_getstring);
-EXPORT_SYMBOL(prom_getint);
-EXPORT_SYMBOL(prom_getintdefault);
-EXPORT_SYMBOL(__prom_getchild);
-EXPORT_SYMBOL(__prom_getsibling);
-
-/* sparc library symbols */
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(__strlen_user);
-EXPORT_SYMBOL(__strnlen_user);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(_clear_page);
-EXPORT_SYMBOL(clear_user_page);
-EXPORT_SYMBOL(copy_user_page);
-EXPORT_SYMBOL(__bzero);
-EXPORT_SYMBOL(__memscan_zero);
-EXPORT_SYMBOL(__memscan_generic);
-EXPORT_SYMBOL(__memcmp);
-EXPORT_SYMBOL(__memset);
-
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(__csum_partial_copy_from_user);
-EXPORT_SYMBOL(__csum_partial_copy_to_user);
-EXPORT_SYMBOL(ip_fast_csum);
-
-/* Moving data to/from/in userspace. */
-EXPORT_SYMBOL(___copy_to_user);
-EXPORT_SYMBOL(___copy_from_user);
-EXPORT_SYMBOL(___copy_in_user);
-EXPORT_SYMBOL(copy_to_user_fixup);
-EXPORT_SYMBOL(copy_from_user_fixup);
-EXPORT_SYMBOL(copy_in_user_fixup);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__clear_user);
-
-/* Various address conversion macros use this. */
-EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
-
-/* No version information on this, heavily used in inline asm,
- * and will always be 'void __ret_efault(void)'.
- */
-EXPORT_SYMBOL(__ret_efault);
-
-/* No version information on these, as gcc produces such symbols. */
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(strncmp);
-
-void VISenter(void);
-/* RAID code needs this */
-EXPORT_SYMBOL(VISenter);
-
-/* for input/keybdev */
-EXPORT_SYMBOL(sun_do_break);
-EXPORT_SYMBOL(stop_a_enabled);
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-EXPORT_SYMBOL(do_BUG);
-#endif
-
-/* for ns8703 */
-EXPORT_SYMBOL(ns87303_lock);
-
-EXPORT_SYMBOL(tick_ops);
-
-EXPORT_SYMBOL(xor_vis_2);
-EXPORT_SYMBOL(xor_vis_3);
-EXPORT_SYMBOL(xor_vis_4);
-EXPORT_SYMBOL(xor_vis_5);
-
-EXPORT_SYMBOL(xor_niagara_2);
-EXPORT_SYMBOL(xor_niagara_3);
-EXPORT_SYMBOL(xor_niagara_4);
-EXPORT_SYMBOL(xor_niagara_5);
-
-EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
diff --git a/arch/sparc64/kernel/spiterrs.S b/arch/sparc64/kernel/spiterrs.S
deleted file mode 100644
index ef902c6f8e3c..000000000000
--- a/arch/sparc64/kernel/spiterrs.S
+++ /dev/null
@@ -1,245 +0,0 @@
- /* We need to carefully read the error status, ACK the errors,
- * prevent recursive traps, and pass the information on to C
- * code for logging.
- *
- * We pass the AFAR in as-is, and we encode the status
- * information as described in asm-sparc64/sfafsr.h
- */
- .type __spitfire_access_error,#function
-__spitfire_access_error:
- /* Disable ESTATE error reporting so that we do not take
- * recursive traps and RED state the processor.
- */
- stxa %g0, [%g0] ASI_ESTATE_ERROR_EN
- membar #Sync
-
- mov UDBE_UE, %g1
- ldxa [%g0] ASI_AFSR, %g4 ! Get AFSR
-
- /* __spitfire_cee_trap branches here with AFSR in %g4 and
- * UDBE_CE in %g1. It only clears ESTATE_ERR_CE in the ESTATE
- * Error Enable register.
- */
-__spitfire_cee_trap_continue:
- ldxa [%g0] ASI_AFAR, %g5 ! Get AFAR
-
- rdpr %tt, %g3
- and %g3, 0x1ff, %g3 ! Paranoia
- sllx %g3, SFSTAT_TRAP_TYPE_SHIFT, %g3
- or %g4, %g3, %g4
- rdpr %tl, %g3
- cmp %g3, 1
- mov 1, %g3
- bleu %xcc, 1f
- sllx %g3, SFSTAT_TL_GT_ONE_SHIFT, %g3
-
- or %g4, %g3, %g4
-
- /* Read in the UDB error register state, clearing the sticky
- * error bits as-needed. We only clear them if the UE bit is
- * set. Likewise, __spitfire_cee_trap below will only do so
- * if the CE bit is set.
- *
- * NOTE: UltraSparc-I/II have high and low UDB error
- * registers, corresponding to the two UDB units
- * present on those chips. UltraSparc-IIi only
- * has a single UDB, called "SDB" in the manual.
- * For IIi the upper UDB register always reads
- * as zero so for our purposes things will just
- * work with the checks below.
- */
-1: ldxa [%g0] ASI_UDBH_ERROR_R, %g3
- and %g3, 0x3ff, %g7 ! Paranoia
- sllx %g7, SFSTAT_UDBH_SHIFT, %g7
- or %g4, %g7, %g4
- andcc %g3, %g1, %g3 ! UDBE_UE or UDBE_CE
- be,pn %xcc, 1f
- nop
- stxa %g3, [%g0] ASI_UDB_ERROR_W
- membar #Sync
-
-1: mov 0x18, %g3
- ldxa [%g3] ASI_UDBL_ERROR_R, %g3
- and %g3, 0x3ff, %g7 ! Paranoia
- sllx %g7, SFSTAT_UDBL_SHIFT, %g7
- or %g4, %g7, %g4
- andcc %g3, %g1, %g3 ! UDBE_UE or UDBE_CE
- be,pn %xcc, 1f
- nop
- mov 0x18, %g7
- stxa %g3, [%g7] ASI_UDB_ERROR_W
- membar #Sync
-
-1: /* Ok, now that we've latched the error state, clear the
- * sticky bits in the AFSR.
- */
- stxa %g4, [%g0] ASI_AFSR
- membar #Sync
-
- rdpr %tl, %g2
- cmp %g2, 1
- rdpr %pil, %g2
- bleu,pt %xcc, 1f
- wrpr %g0, 15, %pil
-
- ba,pt %xcc, etraptl1
- rd %pc, %g7
-
- ba,pt %xcc, 2f
- nop
-
-1: ba,pt %xcc, etrap_irq
- rd %pc, %g7
-
-2:
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- mov %l4, %o1
- mov %l5, %o2
- call spitfire_access_error
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
- .size __spitfire_access_error,.-__spitfire_access_error
-
- /* This is the trap handler entry point for ECC correctable
- * errors. They are corrected, but we listen for the trap so
- * that the event can be logged.
- *
- * Disrupting errors are either:
- * 1) single-bit ECC errors during UDB reads to system
- * memory
- * 2) data parity errors during write-back events
- *
- * As far as I can make out from the manual, the CEE trap is
- * only for correctable errors during memory read accesses by
- * the front-end of the processor.
- *
- * The code below is only for trap level 1 CEE events, as it
- * is the only situation where we can safely record and log.
- * For trap level >1 we just clear the CE bit in the AFSR and
- * return.
- *
- * This is just like __spiftire_access_error above, but it
- * specifically handles correctable errors. If an
- * uncorrectable error is indicated in the AFSR we will branch
- * directly above to __spitfire_access_error to handle it
- * instead. Uncorrectable therefore takes priority over
- * correctable, and the error logging C code will notice this
- * case by inspecting the trap type.
- */
- .type __spitfire_cee_trap,#function
-__spitfire_cee_trap:
- ldxa [%g0] ASI_AFSR, %g4 ! Get AFSR
- mov 1, %g3
- sllx %g3, SFAFSR_UE_SHIFT, %g3
- andcc %g4, %g3, %g0 ! Check for UE
- bne,pn %xcc, __spitfire_access_error
- nop
-
- /* Ok, in this case we only have a correctable error.
- * Indicate we only wish to capture that state in register
- * %g1, and we only disable CE error reporting unlike UE
- * handling which disables all errors.
- */
- ldxa [%g0] ASI_ESTATE_ERROR_EN, %g3
- andn %g3, ESTATE_ERR_CE, %g3
- stxa %g3, [%g0] ASI_ESTATE_ERROR_EN
- membar #Sync
-
- /* Preserve AFSR in %g4, indicate UDB state to capture in %g1 */
- ba,pt %xcc, __spitfire_cee_trap_continue
- mov UDBE_CE, %g1
- .size __spitfire_cee_trap,.-__spitfire_cee_trap
-
- .type __spitfire_data_access_exception_tl1,#function
-__spitfire_data_access_exception_tl1:
- rdpr %pstate, %g4
- wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
- mov TLB_SFSR, %g3
- mov DMMU_SFAR, %g5
- ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
- ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
- stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
- membar #Sync
- rdpr %tt, %g3
- cmp %g3, 0x80 ! first win spill/fill trap
- blu,pn %xcc, 1f
- cmp %g3, 0xff ! last win spill/fill trap
- bgu,pn %xcc, 1f
- nop
- ba,pt %xcc, winfix_dax
- rdpr %tpc, %g3
-1: sethi %hi(109f), %g7
- ba,pt %xcc, etraptl1
-109: or %g7, %lo(109b), %g7
- mov %l4, %o1
- mov %l5, %o2
- call spitfire_data_access_exception_tl1
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
- .size __spitfire_data_access_exception_tl1,.-__spitfire_data_access_exception_tl1
-
- .type __spitfire_data_access_exception,#function
-__spitfire_data_access_exception:
- rdpr %pstate, %g4
- wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
- mov TLB_SFSR, %g3
- mov DMMU_SFAR, %g5
- ldxa [%g3] ASI_DMMU, %g4 ! Get SFSR
- ldxa [%g5] ASI_DMMU, %g5 ! Get SFAR
- stxa %g0, [%g3] ASI_DMMU ! Clear SFSR.FaultValid bit
- membar #Sync
- sethi %hi(109f), %g7
- ba,pt %xcc, etrap
-109: or %g7, %lo(109b), %g7
- mov %l4, %o1
- mov %l5, %o2
- call spitfire_data_access_exception
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
- .size __spitfire_data_access_exception,.-__spitfire_data_access_exception
-
- .type __spitfire_insn_access_exception_tl1,#function
-__spitfire_insn_access_exception_tl1:
- rdpr %pstate, %g4
- wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
- mov TLB_SFSR, %g3
- ldxa [%g3] ASI_IMMU, %g4 ! Get SFSR
- rdpr %tpc, %g5 ! IMMU has no SFAR, use TPC
- stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
- membar #Sync
- sethi %hi(109f), %g7
- ba,pt %xcc, etraptl1
-109: or %g7, %lo(109b), %g7
- mov %l4, %o1
- mov %l5, %o2
- call spitfire_insn_access_exception_tl1
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
- .size __spitfire_insn_access_exception_tl1,.-__spitfire_insn_access_exception_tl1
-
- .type __spitfire_insn_access_exception,#function
-__spitfire_insn_access_exception:
- rdpr %pstate, %g4
- wrpr %g4, PSTATE_MG|PSTATE_AG, %pstate
- mov TLB_SFSR, %g3
- ldxa [%g3] ASI_IMMU, %g4 ! Get SFSR
- rdpr %tpc, %g5 ! IMMU has no SFAR, use TPC
- stxa %g0, [%g3] ASI_IMMU ! Clear FaultValid bit
- membar #Sync
- sethi %hi(109f), %g7
- ba,pt %xcc, etrap
-109: or %g7, %lo(109b), %g7
- mov %l4, %o1
- mov %l5, %o2
- call spitfire_insn_access_exception
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
- .size __spitfire_insn_access_exception,.-__spitfire_insn_access_exception
diff --git a/arch/sparc64/kernel/stacktrace.c b/arch/sparc64/kernel/stacktrace.c
deleted file mode 100644
index 4e21d4a57d3b..000000000000
--- a/arch/sparc64/kernel/stacktrace.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <linux/sched.h>
-#include <linux/stacktrace.h>
-#include <linux/thread_info.h>
-#include <linux/module.h>
-#include <asm/ptrace.h>
-#include <asm/stacktrace.h>
-
-#include "kstack.h"
-
-void save_stack_trace(struct stack_trace *trace)
-{
- struct thread_info *tp = task_thread_info(current);
- unsigned long ksp, fp;
-
- stack_trace_flush();
-
- __asm__ __volatile__(
- "mov %%fp, %0"
- : "=r" (ksp)
- );
-
- fp = ksp + STACK_BIAS;
- do {
- struct sparc_stackf *sf;
- struct pt_regs *regs;
- unsigned long pc;
-
- if (!kstack_valid(tp, fp))
- break;
-
- sf = (struct sparc_stackf *) fp;
- regs = (struct pt_regs *) (sf + 1);
-
- if (kstack_is_trap_frame(tp, regs)) {
- if (!(regs->tstate & TSTATE_PRIV))
- break;
- pc = regs->tpc;
- fp = regs->u_regs[UREG_I6] + STACK_BIAS;
- } else {
- pc = sf->callers_pc;
- fp = (unsigned long)sf->fp + STACK_BIAS;
- }
-
- if (trace->skip > 0)
- trace->skip--;
- else
- trace->entries[trace->nr_entries++] = pc;
- } while (trace->nr_entries < trace->max_entries);
-}
-EXPORT_SYMBOL_GPL(save_stack_trace);
diff --git a/arch/sparc64/kernel/sun4v_ivec.S b/arch/sparc64/kernel/sun4v_ivec.S
deleted file mode 100644
index e2f8e1b4882a..000000000000
--- a/arch/sparc64/kernel/sun4v_ivec.S
+++ /dev/null
@@ -1,341 +0,0 @@
-/* sun4v_ivec.S: Sun4v interrupt vector handling.
- *
- * Copyright (C) 2006 <davem@davemloft.net>
- */
-
-#include <asm/cpudata.h>
-#include <asm/intr_queue.h>
-#include <asm/pil.h>
-
- .text
- .align 32
-
-sun4v_cpu_mondo:
- /* Head offset in %g2, tail offset in %g4.
- * If they are the same, no work.
- */
- mov INTRQ_CPU_MONDO_HEAD, %g2
- ldxa [%g2] ASI_QUEUE, %g2
- mov INTRQ_CPU_MONDO_TAIL, %g4
- ldxa [%g4] ASI_QUEUE, %g4
- cmp %g2, %g4
- be,pn %xcc, sun4v_cpu_mondo_queue_empty
- nop
-
- /* Get &trap_block[smp_processor_id()] into %g4. */
- ldxa [%g0] ASI_SCRATCHPAD, %g4
- sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
-
- /* Get CPU mondo queue base phys address into %g7. */
- ldx [%g4 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
-
- /* Now get the cross-call arguments and handler PC, same
- * layout as sun4u:
- *
- * 1st 64-bit word: low half is 32-bit PC, put into %g3 and jmpl to it
- * high half is context arg to MMU flushes, into %g5
- * 2nd 64-bit word: 64-bit arg, load into %g1
- * 3rd 64-bit word: 64-bit arg, load into %g7
- */
- ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g3
- add %g2, 0x8, %g2
- srlx %g3, 32, %g5
- ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
- add %g2, 0x8, %g2
- srl %g3, 0, %g3
- ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g7
- add %g2, 0x40 - 0x8 - 0x8, %g2
-
- /* Update queue head pointer. */
- lduw [%g4 + TRAP_PER_CPU_CPU_MONDO_QMASK], %g4
- and %g2, %g4, %g2
-
- mov INTRQ_CPU_MONDO_HEAD, %g4
- stxa %g2, [%g4] ASI_QUEUE
- membar #Sync
-
- jmpl %g3, %g0
- nop
-
-sun4v_cpu_mondo_queue_empty:
- retry
-
-sun4v_dev_mondo:
- /* Head offset in %g2, tail offset in %g4. */
- mov INTRQ_DEVICE_MONDO_HEAD, %g2
- ldxa [%g2] ASI_QUEUE, %g2
- mov INTRQ_DEVICE_MONDO_TAIL, %g4
- ldxa [%g4] ASI_QUEUE, %g4
- cmp %g2, %g4
- be,pn %xcc, sun4v_dev_mondo_queue_empty
- nop
-
- /* Get &trap_block[smp_processor_id()] into %g4. */
- ldxa [%g0] ASI_SCRATCHPAD, %g4
- sub %g4, TRAP_PER_CPU_FAULT_INFO, %g4
-
- /* Get DEV mondo queue base phys address into %g5. */
- ldx [%g4 + TRAP_PER_CPU_DEV_MONDO_PA], %g5
-
- /* Load IVEC into %g3. */
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- add %g2, 0x40, %g2
-
- /* XXX There can be a full 64-byte block of data here.
- * XXX This is how we can get at MSI vector data.
- * XXX Current we do not capture this, but when we do we'll
- * XXX need to add a 64-byte storage area in the struct ino_bucket
- * XXX or the struct irq_desc.
- */
-
- /* Update queue head pointer, this frees up some registers. */
- lduw [%g4 + TRAP_PER_CPU_DEV_MONDO_QMASK], %g4
- and %g2, %g4, %g2
-
- mov INTRQ_DEVICE_MONDO_HEAD, %g4
- stxa %g2, [%g4] ASI_QUEUE
- membar #Sync
-
- TRAP_LOAD_IRQ_WORK_PA(%g1, %g4)
-
- /* For VIRQs, cookie is encoded as ~bucket_phys_addr */
- brlz,pt %g3, 1f
- xnor %g3, %g0, %g4
-
- /* Get __pa(&ivector_table[IVEC]) into %g4. */
- sethi %hi(ivector_table_pa), %g4
- ldx [%g4 + %lo(ivector_table_pa)], %g4
- sllx %g3, 4, %g3
- add %g4, %g3, %g4
-
-1: ldx [%g1], %g2
- stxa %g2, [%g4] ASI_PHYS_USE_EC
- stx %g4, [%g1]
-
- /* Signal the interrupt by setting (1 << pil) in %softint. */
- wr %g0, 1 << PIL_DEVICE_IRQ, %set_softint
-
-sun4v_dev_mondo_queue_empty:
- retry
-
-sun4v_res_mondo:
- /* Head offset in %g2, tail offset in %g4. */
- mov INTRQ_RESUM_MONDO_HEAD, %g2
- ldxa [%g2] ASI_QUEUE, %g2
- mov INTRQ_RESUM_MONDO_TAIL, %g4
- ldxa [%g4] ASI_QUEUE, %g4
- cmp %g2, %g4
- be,pn %xcc, sun4v_res_mondo_queue_empty
- nop
-
- /* Get &trap_block[smp_processor_id()] into %g3. */
- ldxa [%g0] ASI_SCRATCHPAD, %g3
- sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
-
- /* Get RES mondo queue base phys address into %g5. */
- ldx [%g3 + TRAP_PER_CPU_RESUM_MONDO_PA], %g5
-
- /* Get RES kernel buffer base phys address into %g7. */
- ldx [%g3 + TRAP_PER_CPU_RESUM_KBUF_PA], %g7
-
- /* If the first word is non-zero, queue is full. */
- ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
- brnz,pn %g1, sun4v_res_mondo_queue_full
- nop
-
- lduw [%g3 + TRAP_PER_CPU_RESUM_QMASK], %g4
-
- /* Remember this entry's offset in %g1. */
- mov %g2, %g1
-
- /* Copy 64-byte queue entry into kernel buffer. */
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
-
- /* Update queue head pointer. */
- and %g2, %g4, %g2
-
- mov INTRQ_RESUM_MONDO_HEAD, %g4
- stxa %g2, [%g4] ASI_QUEUE
- membar #Sync
-
- /* Disable interrupts and save register state so we can call
- * C code. The etrap handling will leave %g4 in %l4 for us
- * when it's done.
- */
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- mov %g1, %g4
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- /* Log the event. */
- add %sp, PTREGS_OFF, %o0
- call sun4v_resum_error
- mov %l4, %o1
-
- /* Return from trap. */
- ba,pt %xcc, rtrap_irq
- nop
-
-sun4v_res_mondo_queue_empty:
- retry
-
-sun4v_res_mondo_queue_full:
- /* The queue is full, consolidate our damage by setting
- * the head equal to the tail. We'll just trap again otherwise.
- * Call C code to log the event.
- */
- mov INTRQ_RESUM_MONDO_HEAD, %g2
- stxa %g4, [%g2] ASI_QUEUE
- membar #Sync
-
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- call sun4v_resum_overflow
- add %sp, PTREGS_OFF, %o0
-
- ba,pt %xcc, rtrap_irq
- nop
-
-sun4v_nonres_mondo:
- /* Head offset in %g2, tail offset in %g4. */
- mov INTRQ_NONRESUM_MONDO_HEAD, %g2
- ldxa [%g2] ASI_QUEUE, %g2
- mov INTRQ_NONRESUM_MONDO_TAIL, %g4
- ldxa [%g4] ASI_QUEUE, %g4
- cmp %g2, %g4
- be,pn %xcc, sun4v_nonres_mondo_queue_empty
- nop
-
- /* Get &trap_block[smp_processor_id()] into %g3. */
- ldxa [%g0] ASI_SCRATCHPAD, %g3
- sub %g3, TRAP_PER_CPU_FAULT_INFO, %g3
-
- /* Get RES mondo queue base phys address into %g5. */
- ldx [%g3 + TRAP_PER_CPU_NONRESUM_MONDO_PA], %g5
-
- /* Get RES kernel buffer base phys address into %g7. */
- ldx [%g3 + TRAP_PER_CPU_NONRESUM_KBUF_PA], %g7
-
- /* If the first word is non-zero, queue is full. */
- ldxa [%g7 + %g2] ASI_PHYS_USE_EC, %g1
- brnz,pn %g1, sun4v_nonres_mondo_queue_full
- nop
-
- lduw [%g3 + TRAP_PER_CPU_NONRESUM_QMASK], %g4
-
- /* Remember this entry's offset in %g1. */
- mov %g2, %g1
-
- /* Copy 64-byte queue entry into kernel buffer. */
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
- ldxa [%g5 + %g2] ASI_PHYS_USE_EC, %g3
- stxa %g3, [%g7 + %g2] ASI_PHYS_USE_EC
- add %g2, 0x08, %g2
-
- /* Update queue head pointer. */
- and %g2, %g4, %g2
-
- mov INTRQ_NONRESUM_MONDO_HEAD, %g4
- stxa %g2, [%g4] ASI_QUEUE
- membar #Sync
-
- /* Disable interrupts and save register state so we can call
- * C code. The etrap handling will leave %g4 in %l4 for us
- * when it's done.
- */
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- mov %g1, %g4
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- /* Log the event. */
- add %sp, PTREGS_OFF, %o0
- call sun4v_nonresum_error
- mov %l4, %o1
-
- /* Return from trap. */
- ba,pt %xcc, rtrap_irq
- nop
-
-sun4v_nonres_mondo_queue_empty:
- retry
-
-sun4v_nonres_mondo_queue_full:
- /* The queue is full, consolidate our damage by setting
- * the head equal to the tail. We'll just trap again otherwise.
- * Call C code to log the event.
- */
- mov INTRQ_NONRESUM_MONDO_HEAD, %g2
- stxa %g4, [%g2] ASI_QUEUE
- membar #Sync
-
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- ba,pt %xcc, etrap_irq
- rd %pc, %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- call sun4v_nonresum_overflow
- add %sp, PTREGS_OFF, %o0
-
- ba,pt %xcc, rtrap_irq
- nop
diff --git a/arch/sparc64/kernel/sysfs.c b/arch/sparc64/kernel/sysfs.c
deleted file mode 100644
index 84e5ce146713..000000000000
--- a/arch/sparc64/kernel/sysfs.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/* sysfs.c: Toplogy sysfs support code for sparc64.
- *
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
- */
-#include <linux/sysdev.h>
-#include <linux/cpu.h>
-#include <linux/smp.h>
-#include <linux/percpu.h>
-#include <linux/init.h>
-
-#include <asm/hypervisor.h>
-#include <asm/spitfire.h>
-
-static DEFINE_PER_CPU(struct hv_mmu_statistics, mmu_stats) __attribute__((aligned(64)));
-
-#define SHOW_MMUSTAT_ULONG(NAME) \
-static ssize_t show_##NAME(struct sys_device *dev, \
- struct sysdev_attribute *attr, char *buf) \
-{ \
- struct hv_mmu_statistics *p = &per_cpu(mmu_stats, dev->id); \
- return sprintf(buf, "%lu\n", p->NAME); \
-} \
-static SYSDEV_ATTR(NAME, 0444, show_##NAME, NULL)
-
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_8k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_8k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_64k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_64k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_4mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_4mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctx0_256mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctx0_256mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_8k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_8k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_64k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_64k_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_4mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_4mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_hits_ctxnon0_256mb_tte);
-SHOW_MMUSTAT_ULONG(immu_tsb_ticks_ctxnon0_256mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_8k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_8k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_64k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_64k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_4mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_4mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctx0_256mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctx0_256mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_8k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_8k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_64k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_64k_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_4mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_4mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_hits_ctxnon0_256mb_tte);
-SHOW_MMUSTAT_ULONG(dmmu_tsb_ticks_ctxnon0_256mb_tte);
-
-static struct attribute *mmu_stat_attrs[] = {
- &attr_immu_tsb_hits_ctx0_8k_tte.attr,
- &attr_immu_tsb_ticks_ctx0_8k_tte.attr,
- &attr_immu_tsb_hits_ctx0_64k_tte.attr,
- &attr_immu_tsb_ticks_ctx0_64k_tte.attr,
- &attr_immu_tsb_hits_ctx0_4mb_tte.attr,
- &attr_immu_tsb_ticks_ctx0_4mb_tte.attr,
- &attr_immu_tsb_hits_ctx0_256mb_tte.attr,
- &attr_immu_tsb_ticks_ctx0_256mb_tte.attr,
- &attr_immu_tsb_hits_ctxnon0_8k_tte.attr,
- &attr_immu_tsb_ticks_ctxnon0_8k_tte.attr,
- &attr_immu_tsb_hits_ctxnon0_64k_tte.attr,
- &attr_immu_tsb_ticks_ctxnon0_64k_tte.attr,
- &attr_immu_tsb_hits_ctxnon0_4mb_tte.attr,
- &attr_immu_tsb_ticks_ctxnon0_4mb_tte.attr,
- &attr_immu_tsb_hits_ctxnon0_256mb_tte.attr,
- &attr_immu_tsb_ticks_ctxnon0_256mb_tte.attr,
- &attr_dmmu_tsb_hits_ctx0_8k_tte.attr,
- &attr_dmmu_tsb_ticks_ctx0_8k_tte.attr,
- &attr_dmmu_tsb_hits_ctx0_64k_tte.attr,
- &attr_dmmu_tsb_ticks_ctx0_64k_tte.attr,
- &attr_dmmu_tsb_hits_ctx0_4mb_tte.attr,
- &attr_dmmu_tsb_ticks_ctx0_4mb_tte.attr,
- &attr_dmmu_tsb_hits_ctx0_256mb_tte.attr,
- &attr_dmmu_tsb_ticks_ctx0_256mb_tte.attr,
- &attr_dmmu_tsb_hits_ctxnon0_8k_tte.attr,
- &attr_dmmu_tsb_ticks_ctxnon0_8k_tte.attr,
- &attr_dmmu_tsb_hits_ctxnon0_64k_tte.attr,
- &attr_dmmu_tsb_ticks_ctxnon0_64k_tte.attr,
- &attr_dmmu_tsb_hits_ctxnon0_4mb_tte.attr,
- &attr_dmmu_tsb_ticks_ctxnon0_4mb_tte.attr,
- &attr_dmmu_tsb_hits_ctxnon0_256mb_tte.attr,
- &attr_dmmu_tsb_ticks_ctxnon0_256mb_tte.attr,
- NULL,
-};
-
-static struct attribute_group mmu_stat_group = {
- .attrs = mmu_stat_attrs,
- .name = "mmu_stats",
-};
-
-/* XXX convert to rusty's on_one_cpu */
-static unsigned long run_on_cpu(unsigned long cpu,
- unsigned long (*func)(unsigned long),
- unsigned long arg)
-{
- cpumask_t old_affinity = current->cpus_allowed;
- unsigned long ret;
-
- /* should return -EINVAL to userspace */
- if (set_cpus_allowed(current, cpumask_of_cpu(cpu)))
- return 0;
-
- ret = func(arg);
-
- set_cpus_allowed(current, old_affinity);
-
- return ret;
-}
-
-static unsigned long read_mmustat_enable(unsigned long junk)
-{
- unsigned long ra = 0;
-
- sun4v_mmustat_info(&ra);
-
- return ra != 0;
-}
-
-static unsigned long write_mmustat_enable(unsigned long val)
-{
- unsigned long ra, orig_ra;
-
- if (val)
- ra = __pa(&per_cpu(mmu_stats, smp_processor_id()));
- else
- ra = 0UL;
-
- return sun4v_mmustat_conf(ra, &orig_ra);
-}
-
-static ssize_t show_mmustat_enable(struct sys_device *s,
- struct sysdev_attribute *attr, char *buf)
-{
- unsigned long val = run_on_cpu(s->id, read_mmustat_enable, 0);
- return sprintf(buf, "%lx\n", val);
-}
-
-static ssize_t store_mmustat_enable(struct sys_device *s,
- struct sysdev_attribute *attr, const char *buf,
- size_t count)
-{
- unsigned long val, err;
- int ret = sscanf(buf, "%ld", &val);
-
- if (ret != 1)
- return -EINVAL;
-
- err = run_on_cpu(s->id, write_mmustat_enable, val);
- if (err)
- return -EIO;
-
- return count;
-}
-
-static SYSDEV_ATTR(mmustat_enable, 0644, show_mmustat_enable, store_mmustat_enable);
-
-static int mmu_stats_supported;
-
-static int register_mmu_stats(struct sys_device *s)
-{
- if (!mmu_stats_supported)
- return 0;
- sysdev_create_file(s, &attr_mmustat_enable);
- return sysfs_create_group(&s->kobj, &mmu_stat_group);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-static void unregister_mmu_stats(struct sys_device *s)
-{
- if (!mmu_stats_supported)
- return;
- sysfs_remove_group(&s->kobj, &mmu_stat_group);
- sysdev_remove_file(s, &attr_mmustat_enable);
-}
-#endif
-
-#define SHOW_CPUDATA_ULONG_NAME(NAME, MEMBER) \
-static ssize_t show_##NAME(struct sys_device *dev, \
- struct sysdev_attribute *attr, char *buf) \
-{ \
- cpuinfo_sparc *c = &cpu_data(dev->id); \
- return sprintf(buf, "%lu\n", c->MEMBER); \
-}
-
-#define SHOW_CPUDATA_UINT_NAME(NAME, MEMBER) \
-static ssize_t show_##NAME(struct sys_device *dev, \
- struct sysdev_attribute *attr, char *buf) \
-{ \
- cpuinfo_sparc *c = &cpu_data(dev->id); \
- return sprintf(buf, "%u\n", c->MEMBER); \
-}
-
-SHOW_CPUDATA_ULONG_NAME(clock_tick, clock_tick);
-SHOW_CPUDATA_UINT_NAME(l1_dcache_size, dcache_size);
-SHOW_CPUDATA_UINT_NAME(l1_dcache_line_size, dcache_line_size);
-SHOW_CPUDATA_UINT_NAME(l1_icache_size, icache_size);
-SHOW_CPUDATA_UINT_NAME(l1_icache_line_size, icache_line_size);
-SHOW_CPUDATA_UINT_NAME(l2_cache_size, ecache_size);
-SHOW_CPUDATA_UINT_NAME(l2_cache_line_size, ecache_line_size);
-
-static struct sysdev_attribute cpu_core_attrs[] = {
- _SYSDEV_ATTR(clock_tick, 0444, show_clock_tick, NULL),
- _SYSDEV_ATTR(l1_dcache_size, 0444, show_l1_dcache_size, NULL),
- _SYSDEV_ATTR(l1_dcache_line_size, 0444, show_l1_dcache_line_size, NULL),
- _SYSDEV_ATTR(l1_icache_size, 0444, show_l1_icache_size, NULL),
- _SYSDEV_ATTR(l1_icache_line_size, 0444, show_l1_icache_line_size, NULL),
- _SYSDEV_ATTR(l2_cache_size, 0444, show_l2_cache_size, NULL),
- _SYSDEV_ATTR(l2_cache_line_size, 0444, show_l2_cache_line_size, NULL),
-};
-
-static DEFINE_PER_CPU(struct cpu, cpu_devices);
-
-static void register_cpu_online(unsigned int cpu)
-{
- struct cpu *c = &per_cpu(cpu_devices, cpu);
- struct sys_device *s = &c->sysdev;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
- sysdev_create_file(s, &cpu_core_attrs[i]);
-
- register_mmu_stats(s);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-static void unregister_cpu_online(unsigned int cpu)
-{
- struct cpu *c = &per_cpu(cpu_devices, cpu);
- struct sys_device *s = &c->sysdev;
- int i;
-
- unregister_mmu_stats(s);
- for (i = 0; i < ARRAY_SIZE(cpu_core_attrs); i++)
- sysdev_remove_file(s, &cpu_core_attrs[i]);
-}
-#endif
-
-static int __cpuinit sysfs_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned int)(long)hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- register_cpu_online(cpu);
- break;
-#ifdef CONFIG_HOTPLUG_CPU
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- unregister_cpu_online(cpu);
- break;
-#endif
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block __cpuinitdata sysfs_cpu_nb = {
- .notifier_call = sysfs_cpu_notify,
-};
-
-static void __init check_mmu_stats(void)
-{
- unsigned long dummy1, err;
-
- if (tlb_type != hypervisor)
- return;
-
- err = sun4v_mmustat_info(&dummy1);
- if (!err)
- mmu_stats_supported = 1;
-}
-
-static void register_nodes(void)
-{
-#ifdef CONFIG_NUMA
- int i;
-
- for (i = 0; i < MAX_NUMNODES; i++)
- register_one_node(i);
-#endif
-}
-
-static int __init topology_init(void)
-{
- int cpu;
-
- register_nodes();
-
- check_mmu_stats();
-
- register_cpu_notifier(&sysfs_cpu_nb);
-
- for_each_possible_cpu(cpu) {
- struct cpu *c = &per_cpu(cpu_devices, cpu);
-
- register_cpu(c, cpu);
- if (cpu_online(cpu))
- register_cpu_online(cpu);
- }
-
- return 0;
-}
-
-subsys_initcall(topology_init);
diff --git a/arch/sparc64/kernel/trampoline.S b/arch/sparc64/kernel/trampoline.S
deleted file mode 100644
index 83abd5ae88a4..000000000000
--- a/arch/sparc64/kernel/trampoline.S
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * trampoline.S: Jump start slave processors on sparc64.
- *
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <linux/init.h>
-
-#include <asm/head.h>
-#include <asm/asi.h>
-#include <asm/lsu.h>
-#include <asm/dcr.h>
-#include <asm/dcu.h>
-#include <asm/pstate.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/spitfire.h>
-#include <asm/processor.h>
-#include <asm/thread_info.h>
-#include <asm/mmu.h>
-#include <asm/hypervisor.h>
-#include <asm/cpudata.h>
-
- .data
- .align 8
-call_method:
- .asciz "call-method"
- .align 8
-itlb_load:
- .asciz "SUNW,itlb-load"
- .align 8
-dtlb_load:
- .asciz "SUNW,dtlb-load"
-
- /* XXX __cpuinit this thing XXX */
-#define TRAMP_STACK_SIZE 1024
- .align 16
-tramp_stack:
- .skip TRAMP_STACK_SIZE
-
- __CPUINIT
- .align 8
- .globl sparc64_cpu_startup, sparc64_cpu_startup_end
-sparc64_cpu_startup:
- BRANCH_IF_SUN4V(g1, niagara_startup)
- BRANCH_IF_CHEETAH_BASE(g1, g5, cheetah_startup)
- BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1, g5, cheetah_plus_startup)
-
- ba,pt %xcc, spitfire_startup
- nop
-
-cheetah_plus_startup:
- /* Preserve OBP chosen DCU and DCR register settings. */
- ba,pt %xcc, cheetah_generic_startup
- nop
-
-cheetah_startup:
- mov DCR_BPE | DCR_RPE | DCR_SI | DCR_IFPOE | DCR_MS, %g1
- wr %g1, %asr18
-
- sethi %uhi(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g5
- or %g5, %ulo(DCU_ME|DCU_RE|DCU_HPE|DCU_SPE|DCU_SL|DCU_WE), %g5
- sllx %g5, 32, %g5
- or %g5, DCU_DM | DCU_IM | DCU_DC | DCU_IC, %g5
- stxa %g5, [%g0] ASI_DCU_CONTROL_REG
- membar #Sync
- /* fallthru */
-
-cheetah_generic_startup:
- mov TSB_EXTENSION_P, %g3
- stxa %g0, [%g3] ASI_DMMU
- stxa %g0, [%g3] ASI_IMMU
- membar #Sync
-
- mov TSB_EXTENSION_S, %g3
- stxa %g0, [%g3] ASI_DMMU
- membar #Sync
-
- mov TSB_EXTENSION_N, %g3
- stxa %g0, [%g3] ASI_DMMU
- stxa %g0, [%g3] ASI_IMMU
- membar #Sync
- /* fallthru */
-
-niagara_startup:
- /* Disable STICK_INT interrupts. */
- sethi %hi(0x80000000), %g5
- sllx %g5, 32, %g5
- wr %g5, %asr25
-
- ba,pt %xcc, startup_continue
- nop
-
-spitfire_startup:
- mov (LSU_CONTROL_IC | LSU_CONTROL_DC | LSU_CONTROL_IM | LSU_CONTROL_DM), %g1
- stxa %g1, [%g0] ASI_LSU_CONTROL
- membar #Sync
-
-startup_continue:
- mov %o0, %l0
- BRANCH_IF_SUN4V(g1, niagara_lock_tlb)
-
- sethi %hi(0x80000000), %g2
- sllx %g2, 32, %g2
- wr %g2, 0, %tick_cmpr
-
- /* Call OBP by hand to lock KERNBASE into i/d tlbs.
- * We lock 'num_kernel_image_mappings' consequetive entries.
- */
- sethi %hi(prom_entry_lock), %g2
-1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
- membar #StoreLoad | #StoreStore
- brnz,pn %g1, 1b
- nop
-
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x10], %l2
- add %l2, -(192 + 128), %sp
- flushw
-
- /* Setup the loop variables:
- * %l3: VADDR base
- * %l4: TTE base
- * %l5: Loop iterator, iterates from 0 to 'num_kernel_image_mappings'
- * %l6: Number of TTE entries to map
- * %l7: Highest TTE entry number, we count down
- */
- sethi %hi(KERNBASE), %l3
- sethi %hi(kern_locked_tte_data), %l4
- ldx [%l4 + %lo(kern_locked_tte_data)], %l4
- clr %l5
- sethi %hi(num_kernel_image_mappings), %l6
- lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
- add %l6, 1, %l6
-
- mov 15, %l7
- BRANCH_IF_ANY_CHEETAH(g1,g5,2f)
-
- mov 63, %l7
-2:
-
-3:
- /* Lock into I-MMU */
- sethi %hi(call_method), %g2
- or %g2, %lo(call_method), %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 5, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- sethi %hi(itlb_load), %g2
- or %g2, %lo(itlb_load), %g2
- stx %g2, [%sp + 2047 + 128 + 0x18]
- sethi %hi(prom_mmu_ihandle_cache), %g2
- lduw [%g2 + %lo(prom_mmu_ihandle_cache)], %g2
- stx %g2, [%sp + 2047 + 128 + 0x20]
-
- /* Each TTE maps 4MB, convert index to offset. */
- sllx %l5, 22, %g1
-
- add %l3, %g1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x28] ! VADDR
- add %l4, %g1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x30] ! TTE
-
- /* TTE index is highest minus loop index. */
- sub %l7, %l5, %g2
- stx %g2, [%sp + 2047 + 128 + 0x38]
-
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
- /* Lock into D-MMU */
- sethi %hi(call_method), %g2
- or %g2, %lo(call_method), %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 5, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- sethi %hi(dtlb_load), %g2
- or %g2, %lo(dtlb_load), %g2
- stx %g2, [%sp + 2047 + 128 + 0x18]
- sethi %hi(prom_mmu_ihandle_cache), %g2
- lduw [%g2 + %lo(prom_mmu_ihandle_cache)], %g2
- stx %g2, [%sp + 2047 + 128 + 0x20]
-
- /* Each TTE maps 4MB, convert index to offset. */
- sllx %l5, 22, %g1
-
- add %l3, %g1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x28] ! VADDR
- add %l4, %g1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x30] ! TTE
-
- /* TTE index is highest minus loop index. */
- sub %l7, %l5, %g2
- stx %g2, [%sp + 2047 + 128 + 0x38]
-
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
- add %l5, 1, %l5
- cmp %l5, %l6
- bne,pt %xcc, 3b
- nop
-
- sethi %hi(prom_entry_lock), %g2
- stb %g0, [%g2 + %lo(prom_entry_lock)]
- membar #StoreStore | #StoreLoad
-
- ba,pt %xcc, after_lock_tlb
- nop
-
-niagara_lock_tlb:
- sethi %hi(KERNBASE), %l3
- sethi %hi(kern_locked_tte_data), %l4
- ldx [%l4 + %lo(kern_locked_tte_data)], %l4
- clr %l5
- sethi %hi(num_kernel_image_mappings), %l6
- lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
- add %l6, 1, %l6
-
-1:
- mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
- sllx %l5, 22, %g2
- add %l3, %g2, %o0
- clr %o1
- add %l4, %g2, %o2
- mov HV_MMU_IMMU, %o3
- ta HV_FAST_TRAP
-
- mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
- sllx %l5, 22, %g2
- add %l3, %g2, %o0
- clr %o1
- add %l4, %g2, %o2
- mov HV_MMU_DMMU, %o3
- ta HV_FAST_TRAP
-
- add %l5, 1, %l5
- cmp %l5, %l6
- bne,pt %xcc, 1b
- nop
-
-after_lock_tlb:
- wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
- wr %g0, 0, %fprs
-
- wr %g0, ASI_P, %asi
-
- mov PRIMARY_CONTEXT, %g7
-
-661: stxa %g0, [%g7] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g0, [%g7] ASI_MMU
- .previous
-
- membar #Sync
- mov SECONDARY_CONTEXT, %g7
-
-661: stxa %g0, [%g7] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g0, [%g7] ASI_MMU
- .previous
-
- membar #Sync
-
- /* Everything we do here, until we properly take over the
- * trap table, must be done with extreme care. We cannot
- * make any references to %g6 (current thread pointer),
- * %g4 (current task pointer), or %g5 (base of current cpu's
- * per-cpu area) until we properly take over the trap table
- * from the firmware and hypervisor.
- *
- * Get onto temporary stack which is in the locked kernel image.
- */
- sethi %hi(tramp_stack), %g1
- or %g1, %lo(tramp_stack), %g1
- add %g1, TRAMP_STACK_SIZE, %g1
- sub %g1, STACKFRAME_SZ + STACK_BIAS + 256, %sp
- mov 0, %fp
-
- /* Put garbage in these registers to trap any access to them. */
- set 0xdeadbeef, %g4
- set 0xdeadbeef, %g5
- set 0xdeadbeef, %g6
-
- call init_irqwork_curcpu
- nop
-
- sethi %hi(tlb_type), %g3
- lduw [%g3 + %lo(tlb_type)], %g2
- cmp %g2, 3
- bne,pt %icc, 1f
- nop
-
- call hard_smp_processor_id
- nop
-
- call sun4v_register_mondo_queues
- nop
-
-1: call init_cur_cpu_trap
- ldx [%l0], %o0
-
- /* Start using proper page size encodings in ctx register. */
- sethi %hi(sparc64_kern_pri_context), %g3
- ldx [%g3 + %lo(sparc64_kern_pri_context)], %g2
- mov PRIMARY_CONTEXT, %g1
-
-661: stxa %g2, [%g1] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g2, [%g1] ASI_MMU
- .previous
-
- membar #Sync
-
- wrpr %g0, 0, %wstate
-
- sethi %hi(prom_entry_lock), %g2
-1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
- membar #StoreLoad | #StoreStore
- brnz,pn %g1, 1b
- nop
-
- /* As a hack, put &init_thread_union into %g6.
- * prom_world() loads from here to restore the %asi
- * register.
- */
- sethi %hi(init_thread_union), %g6
- or %g6, %lo(init_thread_union), %g6
-
- sethi %hi(is_sun4v), %o0
- lduw [%o0 + %lo(is_sun4v)], %o0
- brz,pt %o0, 2f
- nop
-
- TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
- add %g2, TRAP_PER_CPU_FAULT_INFO, %g2
- stxa %g2, [%g0] ASI_SCRATCHPAD
-
- /* Compute physical address:
- *
- * paddr = kern_base + (mmfsa_vaddr - KERNBASE)
- */
- sethi %hi(KERNBASE), %g3
- sub %g2, %g3, %g2
- sethi %hi(kern_base), %g3
- ldx [%g3 + %lo(kern_base)], %g3
- add %g2, %g3, %o1
- sethi %hi(sparc64_ttable_tl0), %o0
-
- set prom_set_trap_table_name, %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 2, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 0, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- stx %o0, [%sp + 2047 + 128 + 0x18]
- stx %o1, [%sp + 2047 + 128 + 0x20]
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
- ba,pt %xcc, 3f
- nop
-
-2: sethi %hi(sparc64_ttable_tl0), %o0
- set prom_set_trap_table_name, %g2
- stx %g2, [%sp + 2047 + 128 + 0x00]
- mov 1, %g2
- stx %g2, [%sp + 2047 + 128 + 0x08]
- mov 0, %g2
- stx %g2, [%sp + 2047 + 128 + 0x10]
- stx %o0, [%sp + 2047 + 128 + 0x18]
- sethi %hi(p1275buf), %g2
- or %g2, %lo(p1275buf), %g2
- ldx [%g2 + 0x08], %o1
- call %o1
- add %sp, (2047 + 128), %o0
-
-3: sethi %hi(prom_entry_lock), %g2
- stb %g0, [%g2 + %lo(prom_entry_lock)]
- membar #StoreStore | #StoreLoad
-
- ldx [%l0], %g6
- ldx [%g6 + TI_TASK], %g4
-
- mov 1, %g5
- sllx %g5, THREAD_SHIFT, %g5
- sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
- add %g6, %g5, %sp
- mov 0, %fp
-
- rdpr %pstate, %o1
- or %o1, PSTATE_IE, %o1
- wrpr %o1, 0, %pstate
-
- call smp_callin
- nop
- call cpu_idle
- mov 0, %o0
- call cpu_panic
- nop
-1: b,a,pt %xcc, 1b
-
- .align 8
-sparc64_cpu_startup_end:
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
deleted file mode 100644
index 81ccd22e78d4..000000000000
--- a/arch/sparc64/kernel/traps.c
+++ /dev/null
@@ -1,2601 +0,0 @@
-/* arch/sparc64/kernel/traps.c
- *
- * Copyright (C) 1995,1997,2008 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1997,1999,2000 Jakub Jelinek (jakub@redhat.com)
- */
-
-/*
- * I like traps on v9, :))))
- */
-
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/linkage.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/smp.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/kdebug.h>
-
-#include <asm/smp.h>
-#include <asm/delay.h>
-#include <asm/system.h>
-#include <asm/ptrace.h>
-#include <asm/oplib.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/unistd.h>
-#include <asm/uaccess.h>
-#include <asm/fpumacro.h>
-#include <asm/lsu.h>
-#include <asm/dcu.h>
-#include <asm/estate.h>
-#include <asm/chafsr.h>
-#include <asm/sfafsr.h>
-#include <asm/psrcompat.h>
-#include <asm/processor.h>
-#include <asm/timer.h>
-#include <asm/head.h>
-#include <asm/prom.h>
-#include <asm/memctrl.h>
-
-#include "entry.h"
-#include "kstack.h"
-
-/* When an irrecoverable trap occurs at tl > 0, the trap entry
- * code logs the trap state registers at every level in the trap
- * stack. It is found at (pt_regs + sizeof(pt_regs)) and the layout
- * is as follows:
- */
-struct tl1_traplog {
- struct {
- unsigned long tstate;
- unsigned long tpc;
- unsigned long tnpc;
- unsigned long tt;
- } trapstack[4];
- unsigned long tl;
-};
-
-static void dump_tl1_traplog(struct tl1_traplog *p)
-{
- int i, limit;
-
- printk(KERN_EMERG "TRAPLOG: Error at trap level 0x%lx, "
- "dumping track stack.\n", p->tl);
-
- limit = (tlb_type == hypervisor) ? 2 : 4;
- for (i = 0; i < limit; i++) {
- printk(KERN_EMERG
- "TRAPLOG: Trap level %d TSTATE[%016lx] TPC[%016lx] "
- "TNPC[%016lx] TT[%lx]\n",
- i + 1,
- p->trapstack[i].tstate, p->trapstack[i].tpc,
- p->trapstack[i].tnpc, p->trapstack[i].tt);
- printk("TRAPLOG: TPC<%pS>\n", (void *) p->trapstack[i].tpc);
- }
-}
-
-void bad_trap(struct pt_regs *regs, long lvl)
-{
- char buffer[32];
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "bad trap", regs,
- 0, lvl, SIGTRAP) == NOTIFY_STOP)
- return;
-
- if (lvl < 0x100) {
- sprintf(buffer, "Bad hw trap %lx at tl0\n", lvl);
- die_if_kernel(buffer, regs);
- }
-
- lvl -= 0x100;
- if (regs->tstate & TSTATE_PRIV) {
- sprintf(buffer, "Kernel bad sw trap %lx", lvl);
- die_if_kernel(buffer, regs);
- }
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_ILLTRP;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = lvl;
- force_sig_info(SIGILL, &info, current);
-}
-
-void bad_trap_tl1(struct pt_regs *regs, long lvl)
-{
- char buffer[32];
-
- if (notify_die(DIE_TRAP_TL1, "bad trap tl1", regs,
- 0, lvl, SIGTRAP) == NOTIFY_STOP)
- return;
-
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
-
- sprintf (buffer, "Bad trap %lx at tl>0", lvl);
- die_if_kernel (buffer, regs);
-}
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-void do_BUG(const char *file, int line)
-{
- bust_spinlocks(1);
- printk("kernel BUG at %s:%d!\n", file, line);
-}
-#endif
-
-static DEFINE_SPINLOCK(dimm_handler_lock);
-static dimm_printer_t dimm_handler;
-
-static int sprintf_dimm(int synd_code, unsigned long paddr, char *buf, int buflen)
-{
- unsigned long flags;
- int ret = -ENODEV;
-
- spin_lock_irqsave(&dimm_handler_lock, flags);
- if (dimm_handler) {
- ret = dimm_handler(synd_code, paddr, buf, buflen);
- } else if (tlb_type == spitfire) {
- if (prom_getunumber(synd_code, paddr, buf, buflen) == -1)
- ret = -EINVAL;
- else
- ret = 0;
- } else
- ret = -ENODEV;
- spin_unlock_irqrestore(&dimm_handler_lock, flags);
-
- return ret;
-}
-
-int register_dimm_printer(dimm_printer_t func)
-{
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&dimm_handler_lock, flags);
- if (!dimm_handler)
- dimm_handler = func;
- else
- ret = -EEXIST;
- spin_unlock_irqrestore(&dimm_handler_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(register_dimm_printer);
-
-void unregister_dimm_printer(dimm_printer_t func)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dimm_handler_lock, flags);
- if (dimm_handler == func)
- dimm_handler = NULL;
- spin_unlock_irqrestore(&dimm_handler_lock, flags);
-}
-EXPORT_SYMBOL_GPL(unregister_dimm_printer);
-
-void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "instruction access exception", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- printk("spitfire_insn_access_exception: SFSR[%016lx] "
- "SFAR[%016lx], going.\n", sfsr, sfar);
- die_if_kernel("Iax", regs);
- }
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_MAPERR;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = 0;
- force_sig_info(SIGSEGV, &info, current);
-}
-
-void spitfire_insn_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
-{
- if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- spitfire_insn_access_exception(regs, sfsr, sfar);
-}
-
-void sun4v_insn_access_exception(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
-{
- unsigned short type = (type_ctx >> 16);
- unsigned short ctx = (type_ctx & 0xffff);
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "instruction access exception", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- printk("sun4v_insn_access_exception: ADDR[%016lx] "
- "CTX[%04x] TYPE[%04x], going.\n",
- addr, ctx, type);
- die_if_kernel("Iax", regs);
- }
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_MAPERR;
- info.si_addr = (void __user *) addr;
- info.si_trapno = 0;
- force_sig_info(SIGSEGV, &info, current);
-}
-
-void sun4v_insn_access_exception_tl1(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
-{
- if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- sun4v_insn_access_exception(regs, addr, type_ctx);
-}
-
-void spitfire_data_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "data access exception", regs,
- 0, 0x30, SIGTRAP) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- /* Test if this comes from uaccess places. */
- const struct exception_table_entry *entry;
-
- entry = search_exception_tables(regs->tpc);
- if (entry) {
- /* Ouch, somebody is trying VM hole tricks on us... */
-#ifdef DEBUG_EXCEPTIONS
- printk("Exception: PC<%016lx> faddr<UNKNOWN>\n", regs->tpc);
- printk("EX_TABLE: insn<%016lx> fixup<%016lx>\n",
- regs->tpc, entry->fixup);
-#endif
- regs->tpc = entry->fixup;
- regs->tnpc = regs->tpc + 4;
- return;
- }
- /* Shit... */
- printk("spitfire_data_access_exception: SFSR[%016lx] "
- "SFAR[%016lx], going.\n", sfsr, sfar);
- die_if_kernel("Dax", regs);
- }
-
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_MAPERR;
- info.si_addr = (void __user *)sfar;
- info.si_trapno = 0;
- force_sig_info(SIGSEGV, &info, current);
-}
-
-void spitfire_data_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
-{
- if (notify_die(DIE_TRAP_TL1, "data access exception tl1", regs,
- 0, 0x30, SIGTRAP) == NOTIFY_STOP)
- return;
-
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- spitfire_data_access_exception(regs, sfsr, sfar);
-}
-
-void sun4v_data_access_exception(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
-{
- unsigned short type = (type_ctx >> 16);
- unsigned short ctx = (type_ctx & 0xffff);
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "data access exception", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- printk("sun4v_data_access_exception: ADDR[%016lx] "
- "CTX[%04x] TYPE[%04x], going.\n",
- addr, ctx, type);
- die_if_kernel("Dax", regs);
- }
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- info.si_code = SEGV_MAPERR;
- info.si_addr = (void __user *) addr;
- info.si_trapno = 0;
- force_sig_info(SIGSEGV, &info, current);
-}
-
-void sun4v_data_access_exception_tl1(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
-{
- if (notify_die(DIE_TRAP_TL1, "data access exception tl1", regs,
- 0, 0x8, SIGTRAP) == NOTIFY_STOP)
- return;
-
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- sun4v_data_access_exception(regs, addr, type_ctx);
-}
-
-#ifdef CONFIG_PCI
-#include "pci_impl.h"
-#endif
-
-/* When access exceptions happen, we must do this. */
-static void spitfire_clean_and_reenable_l1_caches(void)
-{
- unsigned long va;
-
- if (tlb_type != spitfire)
- BUG();
-
- /* Clean 'em. */
- for (va = 0; va < (PAGE_SIZE << 1); va += 32) {
- spitfire_put_icache_tag(va, 0x0);
- spitfire_put_dcache_tag(va, 0x0);
- }
-
- /* Re-enable in LSU. */
- __asm__ __volatile__("flush %%g6\n\t"
- "membar #Sync\n\t"
- "stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (LSU_CONTROL_IC | LSU_CONTROL_DC |
- LSU_CONTROL_IM | LSU_CONTROL_DM),
- "i" (ASI_LSU_CONTROL)
- : "memory");
-}
-
-static void spitfire_enable_estate_errors(void)
-{
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (ESTATE_ERR_ALL),
- "i" (ASI_ESTATE_ERROR_EN));
-}
-
-static char ecc_syndrome_table[] = {
- 0x4c, 0x40, 0x41, 0x48, 0x42, 0x48, 0x48, 0x49,
- 0x43, 0x48, 0x48, 0x49, 0x48, 0x49, 0x49, 0x4a,
- 0x44, 0x48, 0x48, 0x20, 0x48, 0x39, 0x4b, 0x48,
- 0x48, 0x25, 0x31, 0x48, 0x28, 0x48, 0x48, 0x2c,
- 0x45, 0x48, 0x48, 0x21, 0x48, 0x3d, 0x04, 0x48,
- 0x48, 0x4b, 0x35, 0x48, 0x2d, 0x48, 0x48, 0x29,
- 0x48, 0x00, 0x01, 0x48, 0x0a, 0x48, 0x48, 0x4b,
- 0x0f, 0x48, 0x48, 0x4b, 0x48, 0x49, 0x49, 0x48,
- 0x46, 0x48, 0x48, 0x2a, 0x48, 0x3b, 0x27, 0x48,
- 0x48, 0x4b, 0x33, 0x48, 0x22, 0x48, 0x48, 0x2e,
- 0x48, 0x19, 0x1d, 0x48, 0x1b, 0x4a, 0x48, 0x4b,
- 0x1f, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48,
- 0x48, 0x4b, 0x24, 0x48, 0x07, 0x48, 0x48, 0x36,
- 0x4b, 0x48, 0x48, 0x3e, 0x48, 0x30, 0x38, 0x48,
- 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x16, 0x48,
- 0x48, 0x12, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b,
- 0x47, 0x48, 0x48, 0x2f, 0x48, 0x3f, 0x4b, 0x48,
- 0x48, 0x06, 0x37, 0x48, 0x23, 0x48, 0x48, 0x2b,
- 0x48, 0x05, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x32,
- 0x26, 0x48, 0x48, 0x3a, 0x48, 0x34, 0x3c, 0x48,
- 0x48, 0x11, 0x15, 0x48, 0x13, 0x4a, 0x48, 0x4b,
- 0x17, 0x48, 0x4a, 0x4b, 0x48, 0x4b, 0x4b, 0x48,
- 0x49, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x1e, 0x48,
- 0x48, 0x1a, 0x4b, 0x48, 0x49, 0x48, 0x48, 0x4b,
- 0x48, 0x08, 0x0d, 0x48, 0x02, 0x48, 0x48, 0x49,
- 0x03, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x4b, 0x48,
- 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x10, 0x48,
- 0x48, 0x14, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b,
- 0x49, 0x48, 0x48, 0x49, 0x48, 0x4b, 0x18, 0x48,
- 0x48, 0x1c, 0x4b, 0x48, 0x4b, 0x48, 0x48, 0x4b,
- 0x4a, 0x0c, 0x09, 0x48, 0x0e, 0x48, 0x48, 0x4b,
- 0x0b, 0x48, 0x48, 0x4b, 0x48, 0x4b, 0x4b, 0x4a
-};
-
-static char *syndrome_unknown = "<Unknown>";
-
-static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long bit)
-{
- unsigned short scode;
- char memmod_str[64], *p;
-
- if (udbl & bit) {
- scode = ecc_syndrome_table[udbl & 0xff];
- if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
- p = syndrome_unknown;
- else
- p = memmod_str;
- printk(KERN_WARNING "CPU[%d]: UDBL Syndrome[%x] "
- "Memory Module \"%s\"\n",
- smp_processor_id(), scode, p);
- }
-
- if (udbh & bit) {
- scode = ecc_syndrome_table[udbh & 0xff];
- if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
- p = syndrome_unknown;
- else
- p = memmod_str;
- printk(KERN_WARNING "CPU[%d]: UDBH Syndrome[%x] "
- "Memory Module \"%s\"\n",
- smp_processor_id(), scode, p);
- }
-
-}
-
-static void spitfire_cee_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, int tl1, struct pt_regs *regs)
-{
-
- printk(KERN_WARNING "CPU[%d]: Correctable ECC Error "
- "AFSR[%lx] AFAR[%016lx] UDBL[%lx] UDBH[%lx] TL>1[%d]\n",
- smp_processor_id(), afsr, afar, udbl, udbh, tl1);
-
- spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_CE);
-
- /* We always log it, even if someone is listening for this
- * trap.
- */
- notify_die(DIE_TRAP, "Correctable ECC Error", regs,
- 0, TRAP_TYPE_CEE, SIGTRAP);
-
- /* The Correctable ECC Error trap does not disable I/D caches. So
- * we only have to restore the ESTATE Error Enable register.
- */
- spitfire_enable_estate_errors();
-}
-
-static void spitfire_ue_log(unsigned long afsr, unsigned long afar, unsigned long udbh, unsigned long udbl, unsigned long tt, int tl1, struct pt_regs *regs)
-{
- siginfo_t info;
-
- printk(KERN_WARNING "CPU[%d]: Uncorrectable Error AFSR[%lx] "
- "AFAR[%lx] UDBL[%lx] UDBH[%ld] TT[%lx] TL>1[%d]\n",
- smp_processor_id(), afsr, afar, udbl, udbh, tt, tl1);
-
- /* XXX add more human friendly logging of the error status
- * XXX as is implemented for cheetah
- */
-
- spitfire_log_udb_syndrome(afar, udbh, udbl, UDBE_UE);
-
- /* We always log it, even if someone is listening for this
- * trap.
- */
- notify_die(DIE_TRAP, "Uncorrectable Error", regs,
- 0, tt, SIGTRAP);
-
- if (regs->tstate & TSTATE_PRIV) {
- if (tl1)
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("UE", regs);
- }
-
- /* XXX need more intelligent processing here, such as is implemented
- * XXX for cheetah errors, in fact if the E-cache still holds the
- * XXX line with bad parity this will loop
- */
-
- spitfire_clean_and_reenable_l1_caches();
- spitfire_enable_estate_errors();
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_OBJERR;
- info.si_addr = (void *)0;
- info.si_trapno = 0;
- force_sig_info(SIGBUS, &info, current);
-}
-
-void spitfire_access_error(struct pt_regs *regs, unsigned long status_encoded, unsigned long afar)
-{
- unsigned long afsr, tt, udbh, udbl;
- int tl1;
-
- afsr = (status_encoded & SFSTAT_AFSR_MASK) >> SFSTAT_AFSR_SHIFT;
- tt = (status_encoded & SFSTAT_TRAP_TYPE) >> SFSTAT_TRAP_TYPE_SHIFT;
- tl1 = (status_encoded & SFSTAT_TL_GT_ONE) ? 1 : 0;
- udbl = (status_encoded & SFSTAT_UDBL_MASK) >> SFSTAT_UDBL_SHIFT;
- udbh = (status_encoded & SFSTAT_UDBH_MASK) >> SFSTAT_UDBH_SHIFT;
-
-#ifdef CONFIG_PCI
- if (tt == TRAP_TYPE_DAE &&
- pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
- spitfire_clean_and_reenable_l1_caches();
- spitfire_enable_estate_errors();
-
- pci_poke_faulted = 1;
- regs->tnpc = regs->tpc + 4;
- return;
- }
-#endif
-
- if (afsr & SFAFSR_UE)
- spitfire_ue_log(afsr, afar, udbh, udbl, tt, tl1, regs);
-
- if (tt == TRAP_TYPE_CEE) {
- /* Handle the case where we took a CEE trap, but ACK'd
- * only the UE state in the UDB error registers.
- */
- if (afsr & SFAFSR_UE) {
- if (udbh & UDBE_CE) {
- __asm__ __volatile__(
- "stxa %0, [%1] %2\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (udbh & UDBE_CE),
- "r" (0x0), "i" (ASI_UDB_ERROR_W));
- }
- if (udbl & UDBE_CE) {
- __asm__ __volatile__(
- "stxa %0, [%1] %2\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (udbl & UDBE_CE),
- "r" (0x18), "i" (ASI_UDB_ERROR_W));
- }
- }
-
- spitfire_cee_log(afsr, afar, udbh, udbl, tl1, regs);
- }
-}
-
-int cheetah_pcache_forced_on;
-
-void cheetah_enable_pcache(void)
-{
- unsigned long dcr;
-
- printk("CHEETAH: Enabling P-Cache on cpu %d.\n",
- smp_processor_id());
-
- __asm__ __volatile__("ldxa [%%g0] %1, %0"
- : "=r" (dcr)
- : "i" (ASI_DCU_CONTROL_REG));
- dcr |= (DCU_PE | DCU_HPE | DCU_SPE | DCU_SL);
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (dcr), "i" (ASI_DCU_CONTROL_REG));
-}
-
-/* Cheetah error trap handling. */
-static unsigned long ecache_flush_physbase;
-static unsigned long ecache_flush_linesize;
-static unsigned long ecache_flush_size;
-
-/* This table is ordered in priority of errors and matches the
- * AFAR overwrite policy as well.
- */
-
-struct afsr_error_table {
- unsigned long mask;
- const char *name;
-};
-
-static const char CHAFSR_PERR_msg[] =
- "System interface protocol error";
-static const char CHAFSR_IERR_msg[] =
- "Internal processor error";
-static const char CHAFSR_ISAP_msg[] =
- "System request parity error on incoming addresss";
-static const char CHAFSR_UCU_msg[] =
- "Uncorrectable E-cache ECC error for ifetch/data";
-static const char CHAFSR_UCC_msg[] =
- "SW Correctable E-cache ECC error for ifetch/data";
-static const char CHAFSR_UE_msg[] =
- "Uncorrectable system bus data ECC error for read";
-static const char CHAFSR_EDU_msg[] =
- "Uncorrectable E-cache ECC error for stmerge/blkld";
-static const char CHAFSR_EMU_msg[] =
- "Uncorrectable system bus MTAG error";
-static const char CHAFSR_WDU_msg[] =
- "Uncorrectable E-cache ECC error for writeback";
-static const char CHAFSR_CPU_msg[] =
- "Uncorrectable ECC error for copyout";
-static const char CHAFSR_CE_msg[] =
- "HW corrected system bus data ECC error for read";
-static const char CHAFSR_EDC_msg[] =
- "HW corrected E-cache ECC error for stmerge/blkld";
-static const char CHAFSR_EMC_msg[] =
- "HW corrected system bus MTAG ECC error";
-static const char CHAFSR_WDC_msg[] =
- "HW corrected E-cache ECC error for writeback";
-static const char CHAFSR_CPC_msg[] =
- "HW corrected ECC error for copyout";
-static const char CHAFSR_TO_msg[] =
- "Unmapped error from system bus";
-static const char CHAFSR_BERR_msg[] =
- "Bus error response from system bus";
-static const char CHAFSR_IVC_msg[] =
- "HW corrected system bus data ECC error for ivec read";
-static const char CHAFSR_IVU_msg[] =
- "Uncorrectable system bus data ECC error for ivec read";
-static struct afsr_error_table __cheetah_error_table[] = {
- { CHAFSR_PERR, CHAFSR_PERR_msg },
- { CHAFSR_IERR, CHAFSR_IERR_msg },
- { CHAFSR_ISAP, CHAFSR_ISAP_msg },
- { CHAFSR_UCU, CHAFSR_UCU_msg },
- { CHAFSR_UCC, CHAFSR_UCC_msg },
- { CHAFSR_UE, CHAFSR_UE_msg },
- { CHAFSR_EDU, CHAFSR_EDU_msg },
- { CHAFSR_EMU, CHAFSR_EMU_msg },
- { CHAFSR_WDU, CHAFSR_WDU_msg },
- { CHAFSR_CPU, CHAFSR_CPU_msg },
- { CHAFSR_CE, CHAFSR_CE_msg },
- { CHAFSR_EDC, CHAFSR_EDC_msg },
- { CHAFSR_EMC, CHAFSR_EMC_msg },
- { CHAFSR_WDC, CHAFSR_WDC_msg },
- { CHAFSR_CPC, CHAFSR_CPC_msg },
- { CHAFSR_TO, CHAFSR_TO_msg },
- { CHAFSR_BERR, CHAFSR_BERR_msg },
- /* These two do not update the AFAR. */
- { CHAFSR_IVC, CHAFSR_IVC_msg },
- { CHAFSR_IVU, CHAFSR_IVU_msg },
- { 0, NULL },
-};
-static const char CHPAFSR_DTO_msg[] =
- "System bus unmapped error for prefetch/storequeue-read";
-static const char CHPAFSR_DBERR_msg[] =
- "System bus error for prefetch/storequeue-read";
-static const char CHPAFSR_THCE_msg[] =
- "Hardware corrected E-cache Tag ECC error";
-static const char CHPAFSR_TSCE_msg[] =
- "SW handled correctable E-cache Tag ECC error";
-static const char CHPAFSR_TUE_msg[] =
- "Uncorrectable E-cache Tag ECC error";
-static const char CHPAFSR_DUE_msg[] =
- "System bus uncorrectable data ECC error due to prefetch/store-fill";
-static struct afsr_error_table __cheetah_plus_error_table[] = {
- { CHAFSR_PERR, CHAFSR_PERR_msg },
- { CHAFSR_IERR, CHAFSR_IERR_msg },
- { CHAFSR_ISAP, CHAFSR_ISAP_msg },
- { CHAFSR_UCU, CHAFSR_UCU_msg },
- { CHAFSR_UCC, CHAFSR_UCC_msg },
- { CHAFSR_UE, CHAFSR_UE_msg },
- { CHAFSR_EDU, CHAFSR_EDU_msg },
- { CHAFSR_EMU, CHAFSR_EMU_msg },
- { CHAFSR_WDU, CHAFSR_WDU_msg },
- { CHAFSR_CPU, CHAFSR_CPU_msg },
- { CHAFSR_CE, CHAFSR_CE_msg },
- { CHAFSR_EDC, CHAFSR_EDC_msg },
- { CHAFSR_EMC, CHAFSR_EMC_msg },
- { CHAFSR_WDC, CHAFSR_WDC_msg },
- { CHAFSR_CPC, CHAFSR_CPC_msg },
- { CHAFSR_TO, CHAFSR_TO_msg },
- { CHAFSR_BERR, CHAFSR_BERR_msg },
- { CHPAFSR_DTO, CHPAFSR_DTO_msg },
- { CHPAFSR_DBERR, CHPAFSR_DBERR_msg },
- { CHPAFSR_THCE, CHPAFSR_THCE_msg },
- { CHPAFSR_TSCE, CHPAFSR_TSCE_msg },
- { CHPAFSR_TUE, CHPAFSR_TUE_msg },
- { CHPAFSR_DUE, CHPAFSR_DUE_msg },
- /* These two do not update the AFAR. */
- { CHAFSR_IVC, CHAFSR_IVC_msg },
- { CHAFSR_IVU, CHAFSR_IVU_msg },
- { 0, NULL },
-};
-static const char JPAFSR_JETO_msg[] =
- "System interface protocol error, hw timeout caused";
-static const char JPAFSR_SCE_msg[] =
- "Parity error on system snoop results";
-static const char JPAFSR_JEIC_msg[] =
- "System interface protocol error, illegal command detected";
-static const char JPAFSR_JEIT_msg[] =
- "System interface protocol error, illegal ADTYPE detected";
-static const char JPAFSR_OM_msg[] =
- "Out of range memory error has occurred";
-static const char JPAFSR_ETP_msg[] =
- "Parity error on L2 cache tag SRAM";
-static const char JPAFSR_UMS_msg[] =
- "Error due to unsupported store";
-static const char JPAFSR_RUE_msg[] =
- "Uncorrectable ECC error from remote cache/memory";
-static const char JPAFSR_RCE_msg[] =
- "Correctable ECC error from remote cache/memory";
-static const char JPAFSR_BP_msg[] =
- "JBUS parity error on returned read data";
-static const char JPAFSR_WBP_msg[] =
- "JBUS parity error on data for writeback or block store";
-static const char JPAFSR_FRC_msg[] =
- "Foreign read to DRAM incurring correctable ECC error";
-static const char JPAFSR_FRU_msg[] =
- "Foreign read to DRAM incurring uncorrectable ECC error";
-static struct afsr_error_table __jalapeno_error_table[] = {
- { JPAFSR_JETO, JPAFSR_JETO_msg },
- { JPAFSR_SCE, JPAFSR_SCE_msg },
- { JPAFSR_JEIC, JPAFSR_JEIC_msg },
- { JPAFSR_JEIT, JPAFSR_JEIT_msg },
- { CHAFSR_PERR, CHAFSR_PERR_msg },
- { CHAFSR_IERR, CHAFSR_IERR_msg },
- { CHAFSR_ISAP, CHAFSR_ISAP_msg },
- { CHAFSR_UCU, CHAFSR_UCU_msg },
- { CHAFSR_UCC, CHAFSR_UCC_msg },
- { CHAFSR_UE, CHAFSR_UE_msg },
- { CHAFSR_EDU, CHAFSR_EDU_msg },
- { JPAFSR_OM, JPAFSR_OM_msg },
- { CHAFSR_WDU, CHAFSR_WDU_msg },
- { CHAFSR_CPU, CHAFSR_CPU_msg },
- { CHAFSR_CE, CHAFSR_CE_msg },
- { CHAFSR_EDC, CHAFSR_EDC_msg },
- { JPAFSR_ETP, JPAFSR_ETP_msg },
- { CHAFSR_WDC, CHAFSR_WDC_msg },
- { CHAFSR_CPC, CHAFSR_CPC_msg },
- { CHAFSR_TO, CHAFSR_TO_msg },
- { CHAFSR_BERR, CHAFSR_BERR_msg },
- { JPAFSR_UMS, JPAFSR_UMS_msg },
- { JPAFSR_RUE, JPAFSR_RUE_msg },
- { JPAFSR_RCE, JPAFSR_RCE_msg },
- { JPAFSR_BP, JPAFSR_BP_msg },
- { JPAFSR_WBP, JPAFSR_WBP_msg },
- { JPAFSR_FRC, JPAFSR_FRC_msg },
- { JPAFSR_FRU, JPAFSR_FRU_msg },
- /* These two do not update the AFAR. */
- { CHAFSR_IVU, CHAFSR_IVU_msg },
- { 0, NULL },
-};
-static struct afsr_error_table *cheetah_error_table;
-static unsigned long cheetah_afsr_errors;
-
-struct cheetah_err_info *cheetah_error_log;
-
-static inline struct cheetah_err_info *cheetah_get_error_log(unsigned long afsr)
-{
- struct cheetah_err_info *p;
- int cpu = smp_processor_id();
-
- if (!cheetah_error_log)
- return NULL;
-
- p = cheetah_error_log + (cpu * 2);
- if ((afsr & CHAFSR_TL1) != 0UL)
- p++;
-
- return p;
-}
-
-extern unsigned int tl0_icpe[], tl1_icpe[];
-extern unsigned int tl0_dcpe[], tl1_dcpe[];
-extern unsigned int tl0_fecc[], tl1_fecc[];
-extern unsigned int tl0_cee[], tl1_cee[];
-extern unsigned int tl0_iae[], tl1_iae[];
-extern unsigned int tl0_dae[], tl1_dae[];
-extern unsigned int cheetah_plus_icpe_trap_vector[], cheetah_plus_icpe_trap_vector_tl1[];
-extern unsigned int cheetah_plus_dcpe_trap_vector[], cheetah_plus_dcpe_trap_vector_tl1[];
-extern unsigned int cheetah_fecc_trap_vector[], cheetah_fecc_trap_vector_tl1[];
-extern unsigned int cheetah_cee_trap_vector[], cheetah_cee_trap_vector_tl1[];
-extern unsigned int cheetah_deferred_trap_vector[], cheetah_deferred_trap_vector_tl1[];
-
-void __init cheetah_ecache_flush_init(void)
-{
- unsigned long largest_size, smallest_linesize, order, ver;
- int i, sz;
-
- /* Scan all cpu device tree nodes, note two values:
- * 1) largest E-cache size
- * 2) smallest E-cache line size
- */
- largest_size = 0UL;
- smallest_linesize = ~0UL;
-
- for (i = 0; i < NR_CPUS; i++) {
- unsigned long val;
-
- val = cpu_data(i).ecache_size;
- if (!val)
- continue;
-
- if (val > largest_size)
- largest_size = val;
-
- val = cpu_data(i).ecache_line_size;
- if (val < smallest_linesize)
- smallest_linesize = val;
-
- }
-
- if (largest_size == 0UL || smallest_linesize == ~0UL) {
- prom_printf("cheetah_ecache_flush_init: Cannot probe cpu E-cache "
- "parameters.\n");
- prom_halt();
- }
-
- ecache_flush_size = (2 * largest_size);
- ecache_flush_linesize = smallest_linesize;
-
- ecache_flush_physbase = find_ecache_flush_span(ecache_flush_size);
-
- if (ecache_flush_physbase == ~0UL) {
- prom_printf("cheetah_ecache_flush_init: Cannot find %d byte "
- "contiguous physical memory.\n",
- ecache_flush_size);
- prom_halt();
- }
-
- /* Now allocate error trap reporting scoreboard. */
- sz = NR_CPUS * (2 * sizeof(struct cheetah_err_info));
- for (order = 0; order < MAX_ORDER; order++) {
- if ((PAGE_SIZE << order) >= sz)
- break;
- }
- cheetah_error_log = (struct cheetah_err_info *)
- __get_free_pages(GFP_KERNEL, order);
- if (!cheetah_error_log) {
- prom_printf("cheetah_ecache_flush_init: Failed to allocate "
- "error logging scoreboard (%d bytes).\n", sz);
- prom_halt();
- }
- memset(cheetah_error_log, 0, PAGE_SIZE << order);
-
- /* Mark all AFSRs as invalid so that the trap handler will
- * log new new information there.
- */
- for (i = 0; i < 2 * NR_CPUS; i++)
- cheetah_error_log[i].afsr = CHAFSR_INVALID;
-
- __asm__ ("rdpr %%ver, %0" : "=r" (ver));
- if ((ver >> 32) == __JALAPENO_ID ||
- (ver >> 32) == __SERRANO_ID) {
- cheetah_error_table = &__jalapeno_error_table[0];
- cheetah_afsr_errors = JPAFSR_ERRORS;
- } else if ((ver >> 32) == 0x003e0015) {
- cheetah_error_table = &__cheetah_plus_error_table[0];
- cheetah_afsr_errors = CHPAFSR_ERRORS;
- } else {
- cheetah_error_table = &__cheetah_error_table[0];
- cheetah_afsr_errors = CHAFSR_ERRORS;
- }
-
- /* Now patch trap tables. */
- memcpy(tl0_fecc, cheetah_fecc_trap_vector, (8 * 4));
- memcpy(tl1_fecc, cheetah_fecc_trap_vector_tl1, (8 * 4));
- memcpy(tl0_cee, cheetah_cee_trap_vector, (8 * 4));
- memcpy(tl1_cee, cheetah_cee_trap_vector_tl1, (8 * 4));
- memcpy(tl0_iae, cheetah_deferred_trap_vector, (8 * 4));
- memcpy(tl1_iae, cheetah_deferred_trap_vector_tl1, (8 * 4));
- memcpy(tl0_dae, cheetah_deferred_trap_vector, (8 * 4));
- memcpy(tl1_dae, cheetah_deferred_trap_vector_tl1, (8 * 4));
- if (tlb_type == cheetah_plus) {
- memcpy(tl0_dcpe, cheetah_plus_dcpe_trap_vector, (8 * 4));
- memcpy(tl1_dcpe, cheetah_plus_dcpe_trap_vector_tl1, (8 * 4));
- memcpy(tl0_icpe, cheetah_plus_icpe_trap_vector, (8 * 4));
- memcpy(tl1_icpe, cheetah_plus_icpe_trap_vector_tl1, (8 * 4));
- }
- flushi(PAGE_OFFSET);
-}
-
-static void cheetah_flush_ecache(void)
-{
- unsigned long flush_base = ecache_flush_physbase;
- unsigned long flush_linesize = ecache_flush_linesize;
- unsigned long flush_size = ecache_flush_size;
-
- __asm__ __volatile__("1: subcc %0, %4, %0\n\t"
- " bne,pt %%xcc, 1b\n\t"
- " ldxa [%2 + %0] %3, %%g0\n\t"
- : "=&r" (flush_size)
- : "0" (flush_size), "r" (flush_base),
- "i" (ASI_PHYS_USE_EC), "r" (flush_linesize));
-}
-
-static void cheetah_flush_ecache_line(unsigned long physaddr)
-{
- unsigned long alias;
-
- physaddr &= ~(8UL - 1UL);
- physaddr = (ecache_flush_physbase +
- (physaddr & ((ecache_flush_size>>1UL) - 1UL)));
- alias = physaddr + (ecache_flush_size >> 1UL);
- __asm__ __volatile__("ldxa [%0] %2, %%g0\n\t"
- "ldxa [%1] %2, %%g0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (physaddr), "r" (alias),
- "i" (ASI_PHYS_USE_EC));
-}
-
-/* Unfortunately, the diagnostic access to the I-cache tags we need to
- * use to clear the thing interferes with I-cache coherency transactions.
- *
- * So we must only flush the I-cache when it is disabled.
- */
-static void __cheetah_flush_icache(void)
-{
- unsigned int icache_size, icache_line_size;
- unsigned long addr;
-
- icache_size = local_cpu_data().icache_size;
- icache_line_size = local_cpu_data().icache_line_size;
-
- /* Clear the valid bits in all the tags. */
- for (addr = 0; addr < icache_size; addr += icache_line_size) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (addr | (2 << 3)),
- "i" (ASI_IC_TAG));
- }
-}
-
-static void cheetah_flush_icache(void)
-{
- unsigned long dcu_save;
-
- /* Save current DCU, disable I-cache. */
- __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
- "or %0, %2, %%g1\n\t"
- "stxa %%g1, [%%g0] %1\n\t"
- "membar #Sync"
- : "=r" (dcu_save)
- : "i" (ASI_DCU_CONTROL_REG), "i" (DCU_IC)
- : "g1");
-
- __cheetah_flush_icache();
-
- /* Restore DCU register */
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (dcu_save), "i" (ASI_DCU_CONTROL_REG));
-}
-
-static void cheetah_flush_dcache(void)
-{
- unsigned int dcache_size, dcache_line_size;
- unsigned long addr;
-
- dcache_size = local_cpu_data().dcache_size;
- dcache_line_size = local_cpu_data().dcache_line_size;
-
- for (addr = 0; addr < dcache_size; addr += dcache_line_size) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (addr), "i" (ASI_DCACHE_TAG));
- }
-}
-
-/* In order to make the even parity correct we must do two things.
- * First, we clear DC_data_parity and set DC_utag to an appropriate value.
- * Next, we clear out all 32-bytes of data for that line. Data of
- * all-zero + tag parity value of zero == correct parity.
- */
-static void cheetah_plus_zap_dcache_parity(void)
-{
- unsigned int dcache_size, dcache_line_size;
- unsigned long addr;
-
- dcache_size = local_cpu_data().dcache_size;
- dcache_line_size = local_cpu_data().dcache_line_size;
-
- for (addr = 0; addr < dcache_size; addr += dcache_line_size) {
- unsigned long tag = (addr >> 14);
- unsigned long line;
-
- __asm__ __volatile__("membar #Sync\n\t"
- "stxa %0, [%1] %2\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (tag), "r" (addr),
- "i" (ASI_DCACHE_UTAG));
- for (line = addr; line < addr + dcache_line_size; line += 8)
- __asm__ __volatile__("membar #Sync\n\t"
- "stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (line),
- "i" (ASI_DCACHE_DATA));
- }
-}
-
-/* Conversion tables used to frob Cheetah AFSR syndrome values into
- * something palatable to the memory controller driver get_unumber
- * routine.
- */
-#define MT0 137
-#define MT1 138
-#define MT2 139
-#define NONE 254
-#define MTC0 140
-#define MTC1 141
-#define MTC2 142
-#define MTC3 143
-#define C0 128
-#define C1 129
-#define C2 130
-#define C3 131
-#define C4 132
-#define C5 133
-#define C6 134
-#define C7 135
-#define C8 136
-#define M2 144
-#define M3 145
-#define M4 146
-#define M 147
-static unsigned char cheetah_ecc_syntab[] = {
-/*00*/NONE, C0, C1, M2, C2, M2, M3, 47, C3, M2, M2, 53, M2, 41, 29, M,
-/*01*/C4, M, M, 50, M2, 38, 25, M2, M2, 33, 24, M2, 11, M, M2, 16,
-/*02*/C5, M, M, 46, M2, 37, 19, M2, M, 31, 32, M, 7, M2, M2, 10,
-/*03*/M2, 40, 13, M2, 59, M, M2, 66, M, M2, M2, 0, M2, 67, 71, M,
-/*04*/C6, M, M, 43, M, 36, 18, M, M2, 49, 15, M, 63, M2, M2, 6,
-/*05*/M2, 44, 28, M2, M, M2, M2, 52, 68, M2, M2, 62, M2, M3, M3, M4,
-/*06*/M2, 26, 106, M2, 64, M, M2, 2, 120, M, M2, M3, M, M3, M3, M4,
-/*07*/116, M2, M2, M3, M2, M3, M, M4, M2, 58, 54, M2, M, M4, M4, M3,
-/*08*/C7, M2, M, 42, M, 35, 17, M2, M, 45, 14, M2, 21, M2, M2, 5,
-/*09*/M, 27, M, M, 99, M, M, 3, 114, M2, M2, 20, M2, M3, M3, M,
-/*0a*/M2, 23, 113, M2, 112, M2, M, 51, 95, M, M2, M3, M2, M3, M3, M2,
-/*0b*/103, M, M2, M3, M2, M3, M3, M4, M2, 48, M, M, 73, M2, M, M3,
-/*0c*/M2, 22, 110, M2, 109, M2, M, 9, 108, M2, M, M3, M2, M3, M3, M,
-/*0d*/102, M2, M, M, M2, M3, M3, M, M2, M3, M3, M2, M, M4, M, M3,
-/*0e*/98, M, M2, M3, M2, M, M3, M4, M2, M3, M3, M4, M3, M, M, M,
-/*0f*/M2, M3, M3, M, M3, M, M, M, 56, M4, M, M3, M4, M, M, M,
-/*10*/C8, M, M2, 39, M, 34, 105, M2, M, 30, 104, M, 101, M, M, 4,
-/*11*/M, M, 100, M, 83, M, M2, 12, 87, M, M, 57, M2, M, M3, M,
-/*12*/M2, 97, 82, M2, 78, M2, M2, 1, 96, M, M, M, M, M, M3, M2,
-/*13*/94, M, M2, M3, M2, M, M3, M, M2, M, 79, M, 69, M, M4, M,
-/*14*/M2, 93, 92, M, 91, M, M2, 8, 90, M2, M2, M, M, M, M, M4,
-/*15*/89, M, M, M3, M2, M3, M3, M, M, M, M3, M2, M3, M2, M, M3,
-/*16*/86, M, M2, M3, M2, M, M3, M, M2, M, M3, M, M3, M, M, M3,
-/*17*/M, M, M3, M2, M3, M2, M4, M, 60, M, M2, M3, M4, M, M, M2,
-/*18*/M2, 88, 85, M2, 84, M, M2, 55, 81, M2, M2, M3, M2, M3, M3, M4,
-/*19*/77, M, M, M, M2, M3, M, M, M2, M3, M3, M4, M3, M2, M, M,
-/*1a*/74, M, M2, M3, M, M, M3, M, M, M, M3, M, M3, M, M4, M3,
-/*1b*/M2, 70, 107, M4, 65, M2, M2, M, 127, M, M, M, M2, M3, M3, M,
-/*1c*/80, M2, M2, 72, M, 119, 118, M, M2, 126, 76, M, 125, M, M4, M3,
-/*1d*/M2, 115, 124, M, 75, M, M, M3, 61, M, M4, M, M4, M, M, M,
-/*1e*/M, 123, 122, M4, 121, M4, M, M3, 117, M2, M2, M3, M4, M3, M, M,
-/*1f*/111, M, M, M, M4, M3, M3, M, M, M, M3, M, M3, M2, M, M
-};
-static unsigned char cheetah_mtag_syntab[] = {
- NONE, MTC0,
- MTC1, NONE,
- MTC2, NONE,
- NONE, MT0,
- MTC3, NONE,
- NONE, MT1,
- NONE, MT2,
- NONE, NONE
-};
-
-/* Return the highest priority error conditon mentioned. */
-static inline unsigned long cheetah_get_hipri(unsigned long afsr)
-{
- unsigned long tmp = 0;
- int i;
-
- for (i = 0; cheetah_error_table[i].mask; i++) {
- if ((tmp = (afsr & cheetah_error_table[i].mask)) != 0UL)
- return tmp;
- }
- return tmp;
-}
-
-static const char *cheetah_get_string(unsigned long bit)
-{
- int i;
-
- for (i = 0; cheetah_error_table[i].mask; i++) {
- if ((bit & cheetah_error_table[i].mask) != 0UL)
- return cheetah_error_table[i].name;
- }
- return "???";
-}
-
-static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info,
- unsigned long afsr, unsigned long afar, int recoverable)
-{
- unsigned long hipri;
- char unum[256];
-
- printk("%s" "ERROR(%d): Cheetah error trap taken afsr[%016lx] afar[%016lx] TL1(%d)\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- afsr, afar,
- (afsr & CHAFSR_TL1) ? 1 : 0);
- printk("%s" "ERROR(%d): TPC[%lx] TNPC[%lx] O7[%lx] TSTATE[%lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- regs->tpc, regs->tnpc, regs->u_regs[UREG_I7], regs->tstate);
- printk("%s" "ERROR(%d): ",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id());
- printk("TPC<%pS>\n", (void *) regs->tpc);
- printk("%s" "ERROR(%d): M_SYND(%lx), E_SYND(%lx)%s%s\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT,
- (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT,
- (afsr & CHAFSR_ME) ? ", Multiple Errors" : "",
- (afsr & CHAFSR_PRIV) ? ", Privileged" : "");
- hipri = cheetah_get_hipri(afsr);
- printk("%s" "ERROR(%d): Highest priority error (%016lx) \"%s\"\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- hipri, cheetah_get_string(hipri));
-
- /* Try to get unumber if relevant. */
-#define ESYND_ERRORS (CHAFSR_IVC | CHAFSR_IVU | \
- CHAFSR_CPC | CHAFSR_CPU | \
- CHAFSR_UE | CHAFSR_CE | \
- CHAFSR_EDC | CHAFSR_EDU | \
- CHAFSR_UCC | CHAFSR_UCU | \
- CHAFSR_WDU | CHAFSR_WDC)
-#define MSYND_ERRORS (CHAFSR_EMC | CHAFSR_EMU)
- if (afsr & ESYND_ERRORS) {
- int syndrome;
- int ret;
-
- syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT;
- syndrome = cheetah_ecc_syntab[syndrome];
- ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
- if (ret != -1)
- printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT),
- smp_processor_id(), unum);
- } else if (afsr & MSYND_ERRORS) {
- int syndrome;
- int ret;
-
- syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT;
- syndrome = cheetah_mtag_syntab[syndrome];
- ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
- if (ret != -1)
- printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT),
- smp_processor_id(), unum);
- }
-
- /* Now dump the cache snapshots. */
- printk("%s" "ERROR(%d): D-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- (int) info->dcache_index,
- info->dcache_tag,
- info->dcache_utag,
- info->dcache_stag);
- printk("%s" "ERROR(%d): D-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- info->dcache_data[0],
- info->dcache_data[1],
- info->dcache_data[2],
- info->dcache_data[3]);
- printk("%s" "ERROR(%d): I-cache idx[%x] tag[%016lx] utag[%016lx] stag[%016lx] "
- "u[%016lx] l[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- (int) info->icache_index,
- info->icache_tag,
- info->icache_utag,
- info->icache_stag,
- info->icache_upper,
- info->icache_lower);
- printk("%s" "ERROR(%d): I-cache INSN0[%016lx] INSN1[%016lx] INSN2[%016lx] INSN3[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- info->icache_data[0],
- info->icache_data[1],
- info->icache_data[2],
- info->icache_data[3]);
- printk("%s" "ERROR(%d): I-cache INSN4[%016lx] INSN5[%016lx] INSN6[%016lx] INSN7[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- info->icache_data[4],
- info->icache_data[5],
- info->icache_data[6],
- info->icache_data[7]);
- printk("%s" "ERROR(%d): E-cache idx[%x] tag[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- (int) info->ecache_index, info->ecache_tag);
- printk("%s" "ERROR(%d): E-cache data0[%016lx] data1[%016lx] data2[%016lx] data3[%016lx]\n",
- (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(),
- info->ecache_data[0],
- info->ecache_data[1],
- info->ecache_data[2],
- info->ecache_data[3]);
-
- afsr = (afsr & ~hipri) & cheetah_afsr_errors;
- while (afsr != 0UL) {
- unsigned long bit = cheetah_get_hipri(afsr);
-
- printk("%s" "ERROR: Multiple-error (%016lx) \"%s\"\n",
- (recoverable ? KERN_WARNING : KERN_CRIT),
- bit, cheetah_get_string(bit));
-
- afsr &= ~bit;
- }
-
- if (!recoverable)
- printk(KERN_CRIT "ERROR: This condition is not recoverable.\n");
-}
-
-static int cheetah_recheck_errors(struct cheetah_err_info *logp)
-{
- unsigned long afsr, afar;
- int ret = 0;
-
- __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
- : "=r" (afsr)
- : "i" (ASI_AFSR));
- if ((afsr & cheetah_afsr_errors) != 0) {
- if (logp != NULL) {
- __asm__ __volatile__("ldxa [%%g0] %1, %0\n\t"
- : "=r" (afar)
- : "i" (ASI_AFAR));
- logp->afsr = afsr;
- logp->afar = afar;
- }
- ret = 1;
- }
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync\n\t"
- : : "r" (afsr), "i" (ASI_AFSR));
-
- return ret;
-}
-
-void cheetah_fecc_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
-{
- struct cheetah_err_info local_snapshot, *p;
- int recoverable;
-
- /* Flush E-cache */
- cheetah_flush_ecache();
-
- p = cheetah_get_error_log(afsr);
- if (!p) {
- prom_printf("ERROR: Early Fast-ECC error afsr[%016lx] afar[%016lx]\n",
- afsr, afar);
- prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
- smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
- prom_halt();
- }
-
- /* Grab snapshot of logged error. */
- memcpy(&local_snapshot, p, sizeof(local_snapshot));
-
- /* If the current trap snapshot does not match what the
- * trap handler passed along into our args, big trouble.
- * In such a case, mark the local copy as invalid.
- *
- * Else, it matches and we mark the afsr in the non-local
- * copy as invalid so we may log new error traps there.
- */
- if (p->afsr != afsr || p->afar != afar)
- local_snapshot.afsr = CHAFSR_INVALID;
- else
- p->afsr = CHAFSR_INVALID;
-
- cheetah_flush_icache();
- cheetah_flush_dcache();
-
- /* Re-enable I-cache/D-cache */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_DCU_CONTROL_REG),
- "i" (DCU_DC | DCU_IC)
- : "g1");
-
- /* Re-enable error reporting */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_ESTATE_ERROR_EN),
- "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
- : "g1");
-
- /* Decide if we can continue after handling this trap and
- * logging the error.
- */
- recoverable = 1;
- if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
- recoverable = 0;
-
- /* Re-check AFSR/AFAR. What we are looking for here is whether a new
- * error was logged while we had error reporting traps disabled.
- */
- if (cheetah_recheck_errors(&local_snapshot)) {
- unsigned long new_afsr = local_snapshot.afsr;
-
- /* If we got a new asynchronous error, die... */
- if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
- CHAFSR_WDU | CHAFSR_CPU |
- CHAFSR_IVU | CHAFSR_UE |
- CHAFSR_BERR | CHAFSR_TO))
- recoverable = 0;
- }
-
- /* Log errors. */
- cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
-
- if (!recoverable)
- panic("Irrecoverable Fast-ECC error trap.\n");
-
- /* Flush E-cache to kick the error trap handlers out. */
- cheetah_flush_ecache();
-}
-
-/* Try to fix a correctable error by pushing the line out from
- * the E-cache. Recheck error reporting registers to see if the
- * problem is intermittent.
- */
-static int cheetah_fix_ce(unsigned long physaddr)
-{
- unsigned long orig_estate;
- unsigned long alias1, alias2;
- int ret;
-
- /* Make sure correctable error traps are disabled. */
- __asm__ __volatile__("ldxa [%%g0] %2, %0\n\t"
- "andn %0, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %2\n\t"
- "membar #Sync"
- : "=&r" (orig_estate)
- : "i" (ESTATE_ERROR_CEEN),
- "i" (ASI_ESTATE_ERROR_EN)
- : "g1");
-
- /* We calculate alias addresses that will force the
- * cache line in question out of the E-cache. Then
- * we bring it back in with an atomic instruction so
- * that we get it in some modified/exclusive state,
- * then we displace it again to try and get proper ECC
- * pushed back into the system.
- */
- physaddr &= ~(8UL - 1UL);
- alias1 = (ecache_flush_physbase +
- (physaddr & ((ecache_flush_size >> 1) - 1)));
- alias2 = alias1 + (ecache_flush_size >> 1);
- __asm__ __volatile__("ldxa [%0] %3, %%g0\n\t"
- "ldxa [%1] %3, %%g0\n\t"
- "casxa [%2] %3, %%g0, %%g0\n\t"
- "membar #StoreLoad | #StoreStore\n\t"
- "ldxa [%0] %3, %%g0\n\t"
- "ldxa [%1] %3, %%g0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (alias1), "r" (alias2),
- "r" (physaddr), "i" (ASI_PHYS_USE_EC));
-
- /* Did that trigger another error? */
- if (cheetah_recheck_errors(NULL)) {
- /* Try one more time. */
- __asm__ __volatile__("ldxa [%0] %1, %%g0\n\t"
- "membar #Sync"
- : : "r" (physaddr), "i" (ASI_PHYS_USE_EC));
- if (cheetah_recheck_errors(NULL))
- ret = 2;
- else
- ret = 1;
- } else {
- /* No new error, intermittent problem. */
- ret = 0;
- }
-
- /* Restore error enables. */
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : : "r" (orig_estate), "i" (ASI_ESTATE_ERROR_EN));
-
- return ret;
-}
-
-/* Return non-zero if PADDR is a valid physical memory address. */
-static int cheetah_check_main_memory(unsigned long paddr)
-{
- unsigned long vaddr = PAGE_OFFSET + paddr;
-
- if (vaddr > (unsigned long) high_memory)
- return 0;
-
- return kern_addr_valid(vaddr);
-}
-
-void cheetah_cee_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
-{
- struct cheetah_err_info local_snapshot, *p;
- int recoverable, is_memory;
-
- p = cheetah_get_error_log(afsr);
- if (!p) {
- prom_printf("ERROR: Early CEE error afsr[%016lx] afar[%016lx]\n",
- afsr, afar);
- prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
- smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
- prom_halt();
- }
-
- /* Grab snapshot of logged error. */
- memcpy(&local_snapshot, p, sizeof(local_snapshot));
-
- /* If the current trap snapshot does not match what the
- * trap handler passed along into our args, big trouble.
- * In such a case, mark the local copy as invalid.
- *
- * Else, it matches and we mark the afsr in the non-local
- * copy as invalid so we may log new error traps there.
- */
- if (p->afsr != afsr || p->afar != afar)
- local_snapshot.afsr = CHAFSR_INVALID;
- else
- p->afsr = CHAFSR_INVALID;
-
- is_memory = cheetah_check_main_memory(afar);
-
- if (is_memory && (afsr & CHAFSR_CE) != 0UL) {
- /* XXX Might want to log the results of this operation
- * XXX somewhere... -DaveM
- */
- cheetah_fix_ce(afar);
- }
-
- {
- int flush_all, flush_line;
-
- flush_all = flush_line = 0;
- if ((afsr & CHAFSR_EDC) != 0UL) {
- if ((afsr & cheetah_afsr_errors) == CHAFSR_EDC)
- flush_line = 1;
- else
- flush_all = 1;
- } else if ((afsr & CHAFSR_CPC) != 0UL) {
- if ((afsr & cheetah_afsr_errors) == CHAFSR_CPC)
- flush_line = 1;
- else
- flush_all = 1;
- }
-
- /* Trap handler only disabled I-cache, flush it. */
- cheetah_flush_icache();
-
- /* Re-enable I-cache */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_DCU_CONTROL_REG),
- "i" (DCU_IC)
- : "g1");
-
- if (flush_all)
- cheetah_flush_ecache();
- else if (flush_line)
- cheetah_flush_ecache_line(afar);
- }
-
- /* Re-enable error reporting */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_ESTATE_ERROR_EN),
- "i" (ESTATE_ERROR_CEEN)
- : "g1");
-
- /* Decide if we can continue after handling this trap and
- * logging the error.
- */
- recoverable = 1;
- if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
- recoverable = 0;
-
- /* Re-check AFSR/AFAR */
- (void) cheetah_recheck_errors(&local_snapshot);
-
- /* Log errors. */
- cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
-
- if (!recoverable)
- panic("Irrecoverable Correctable-ECC error trap.\n");
-}
-
-void cheetah_deferred_handler(struct pt_regs *regs, unsigned long afsr, unsigned long afar)
-{
- struct cheetah_err_info local_snapshot, *p;
- int recoverable, is_memory;
-
-#ifdef CONFIG_PCI
- /* Check for the special PCI poke sequence. */
- if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) {
- cheetah_flush_icache();
- cheetah_flush_dcache();
-
- /* Re-enable I-cache/D-cache */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_DCU_CONTROL_REG),
- "i" (DCU_DC | DCU_IC)
- : "g1");
-
- /* Re-enable error reporting */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_ESTATE_ERROR_EN),
- "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
- : "g1");
-
- (void) cheetah_recheck_errors(NULL);
-
- pci_poke_faulted = 1;
- regs->tpc += 4;
- regs->tnpc = regs->tpc + 4;
- return;
- }
-#endif
-
- p = cheetah_get_error_log(afsr);
- if (!p) {
- prom_printf("ERROR: Early deferred error afsr[%016lx] afar[%016lx]\n",
- afsr, afar);
- prom_printf("ERROR: CPU(%d) TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n",
- smp_processor_id(), regs->tpc, regs->tnpc, regs->tstate);
- prom_halt();
- }
-
- /* Grab snapshot of logged error. */
- memcpy(&local_snapshot, p, sizeof(local_snapshot));
-
- /* If the current trap snapshot does not match what the
- * trap handler passed along into our args, big trouble.
- * In such a case, mark the local copy as invalid.
- *
- * Else, it matches and we mark the afsr in the non-local
- * copy as invalid so we may log new error traps there.
- */
- if (p->afsr != afsr || p->afar != afar)
- local_snapshot.afsr = CHAFSR_INVALID;
- else
- p->afsr = CHAFSR_INVALID;
-
- is_memory = cheetah_check_main_memory(afar);
-
- {
- int flush_all, flush_line;
-
- flush_all = flush_line = 0;
- if ((afsr & CHAFSR_EDU) != 0UL) {
- if ((afsr & cheetah_afsr_errors) == CHAFSR_EDU)
- flush_line = 1;
- else
- flush_all = 1;
- } else if ((afsr & CHAFSR_BERR) != 0UL) {
- if ((afsr & cheetah_afsr_errors) == CHAFSR_BERR)
- flush_line = 1;
- else
- flush_all = 1;
- }
-
- cheetah_flush_icache();
- cheetah_flush_dcache();
-
- /* Re-enable I/D caches */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_DCU_CONTROL_REG),
- "i" (DCU_IC | DCU_DC)
- : "g1");
-
- if (flush_all)
- cheetah_flush_ecache();
- else if (flush_line)
- cheetah_flush_ecache_line(afar);
- }
-
- /* Re-enable error reporting */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_ESTATE_ERROR_EN),
- "i" (ESTATE_ERROR_NCEEN | ESTATE_ERROR_CEEN)
- : "g1");
-
- /* Decide if we can continue after handling this trap and
- * logging the error.
- */
- recoverable = 1;
- if (afsr & (CHAFSR_PERR | CHAFSR_IERR | CHAFSR_ISAP))
- recoverable = 0;
-
- /* Re-check AFSR/AFAR. What we are looking for here is whether a new
- * error was logged while we had error reporting traps disabled.
- */
- if (cheetah_recheck_errors(&local_snapshot)) {
- unsigned long new_afsr = local_snapshot.afsr;
-
- /* If we got a new asynchronous error, die... */
- if (new_afsr & (CHAFSR_EMU | CHAFSR_EDU |
- CHAFSR_WDU | CHAFSR_CPU |
- CHAFSR_IVU | CHAFSR_UE |
- CHAFSR_BERR | CHAFSR_TO))
- recoverable = 0;
- }
-
- /* Log errors. */
- cheetah_log_errors(regs, &local_snapshot, afsr, afar, recoverable);
-
- /* "Recoverable" here means we try to yank the page from ever
- * being newly used again. This depends upon a few things:
- * 1) Must be main memory, and AFAR must be valid.
- * 2) If we trapped from user, OK.
- * 3) Else, if we trapped from kernel we must find exception
- * table entry (ie. we have to have been accessing user
- * space).
- *
- * If AFAR is not in main memory, or we trapped from kernel
- * and cannot find an exception table entry, it is unacceptable
- * to try and continue.
- */
- if (recoverable && is_memory) {
- if ((regs->tstate & TSTATE_PRIV) == 0UL) {
- /* OK, usermode access. */
- recoverable = 1;
- } else {
- const struct exception_table_entry *entry;
-
- entry = search_exception_tables(regs->tpc);
- if (entry) {
- /* OK, kernel access to userspace. */
- recoverable = 1;
-
- } else {
- /* BAD, privileged state is corrupted. */
- recoverable = 0;
- }
-
- if (recoverable) {
- if (pfn_valid(afar >> PAGE_SHIFT))
- get_page(pfn_to_page(afar >> PAGE_SHIFT));
- else
- recoverable = 0;
-
- /* Only perform fixup if we still have a
- * recoverable condition.
- */
- if (recoverable) {
- regs->tpc = entry->fixup;
- regs->tnpc = regs->tpc + 4;
- }
- }
- }
- } else {
- recoverable = 0;
- }
-
- if (!recoverable)
- panic("Irrecoverable deferred error trap.\n");
-}
-
-/* Handle a D/I cache parity error trap. TYPE is encoded as:
- *
- * Bit0: 0=dcache,1=icache
- * Bit1: 0=recoverable,1=unrecoverable
- *
- * The hardware has disabled both the I-cache and D-cache in
- * the %dcr register.
- */
-void cheetah_plus_parity_error(int type, struct pt_regs *regs)
-{
- if (type & 0x1)
- __cheetah_flush_icache();
- else
- cheetah_plus_zap_dcache_parity();
- cheetah_flush_dcache();
-
- /* Re-enable I-cache/D-cache */
- __asm__ __volatile__("ldxa [%%g0] %0, %%g1\n\t"
- "or %%g1, %1, %%g1\n\t"
- "stxa %%g1, [%%g0] %0\n\t"
- "membar #Sync"
- : /* no outputs */
- : "i" (ASI_DCU_CONTROL_REG),
- "i" (DCU_DC | DCU_IC)
- : "g1");
-
- if (type & 0x2) {
- printk(KERN_EMERG "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
- smp_processor_id(),
- (type & 0x1) ? 'I' : 'D',
- regs->tpc);
- printk(KERN_EMERG "TPC<%pS>\n", (void *) regs->tpc);
- panic("Irrecoverable Cheetah+ parity error.");
- }
-
- printk(KERN_WARNING "CPU[%d]: Cheetah+ %c-cache parity error at TPC[%016lx]\n",
- smp_processor_id(),
- (type & 0x1) ? 'I' : 'D',
- regs->tpc);
- printk(KERN_WARNING "TPC<%pS>\n", (void *) regs->tpc);
-}
-
-struct sun4v_error_entry {
- u64 err_handle;
- u64 err_stick;
-
- u32 err_type;
-#define SUN4V_ERR_TYPE_UNDEFINED 0
-#define SUN4V_ERR_TYPE_UNCORRECTED_RES 1
-#define SUN4V_ERR_TYPE_PRECISE_NONRES 2
-#define SUN4V_ERR_TYPE_DEFERRED_NONRES 3
-#define SUN4V_ERR_TYPE_WARNING_RES 4
-
- u32 err_attrs;
-#define SUN4V_ERR_ATTRS_PROCESSOR 0x00000001
-#define SUN4V_ERR_ATTRS_MEMORY 0x00000002
-#define SUN4V_ERR_ATTRS_PIO 0x00000004
-#define SUN4V_ERR_ATTRS_INT_REGISTERS 0x00000008
-#define SUN4V_ERR_ATTRS_FPU_REGISTERS 0x00000010
-#define SUN4V_ERR_ATTRS_USER_MODE 0x01000000
-#define SUN4V_ERR_ATTRS_PRIV_MODE 0x02000000
-#define SUN4V_ERR_ATTRS_RES_QUEUE_FULL 0x80000000
-
- u64 err_raddr;
- u32 err_size;
- u16 err_cpu;
- u16 err_pad;
-};
-
-static atomic_t sun4v_resum_oflow_cnt = ATOMIC_INIT(0);
-static atomic_t sun4v_nonresum_oflow_cnt = ATOMIC_INIT(0);
-
-static const char *sun4v_err_type_to_str(u32 type)
-{
- switch (type) {
- case SUN4V_ERR_TYPE_UNDEFINED:
- return "undefined";
- case SUN4V_ERR_TYPE_UNCORRECTED_RES:
- return "uncorrected resumable";
- case SUN4V_ERR_TYPE_PRECISE_NONRES:
- return "precise nonresumable";
- case SUN4V_ERR_TYPE_DEFERRED_NONRES:
- return "deferred nonresumable";
- case SUN4V_ERR_TYPE_WARNING_RES:
- return "warning resumable";
- default:
- return "unknown";
- };
-}
-
-static void sun4v_log_error(struct pt_regs *regs, struct sun4v_error_entry *ent, int cpu, const char *pfx, atomic_t *ocnt)
-{
- int cnt;
-
- printk("%s: Reporting on cpu %d\n", pfx, cpu);
- printk("%s: err_handle[%lx] err_stick[%lx] err_type[%08x:%s]\n",
- pfx,
- ent->err_handle, ent->err_stick,
- ent->err_type,
- sun4v_err_type_to_str(ent->err_type));
- printk("%s: err_attrs[%08x:%s %s %s %s %s %s %s %s]\n",
- pfx,
- ent->err_attrs,
- ((ent->err_attrs & SUN4V_ERR_ATTRS_PROCESSOR) ?
- "processor" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_MEMORY) ?
- "memory" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_PIO) ?
- "pio" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_INT_REGISTERS) ?
- "integer-regs" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_FPU_REGISTERS) ?
- "fpu-regs" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_USER_MODE) ?
- "user" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_PRIV_MODE) ?
- "privileged" : ""),
- ((ent->err_attrs & SUN4V_ERR_ATTRS_RES_QUEUE_FULL) ?
- "queue-full" : ""));
- printk("%s: err_raddr[%016lx] err_size[%u] err_cpu[%u]\n",
- pfx,
- ent->err_raddr, ent->err_size, ent->err_cpu);
-
- show_regs(regs);
-
- if ((cnt = atomic_read(ocnt)) != 0) {
- atomic_set(ocnt, 0);
- wmb();
- printk("%s: Queue overflowed %d times.\n",
- pfx, cnt);
- }
-}
-
-/* We run with %pil set to 15 and PSTATE_IE enabled in %pstate.
- * Log the event and clear the first word of the entry.
- */
-void sun4v_resum_error(struct pt_regs *regs, unsigned long offset)
-{
- struct sun4v_error_entry *ent, local_copy;
- struct trap_per_cpu *tb;
- unsigned long paddr;
- int cpu;
-
- cpu = get_cpu();
-
- tb = &trap_block[cpu];
- paddr = tb->resum_kernel_buf_pa + offset;
- ent = __va(paddr);
-
- memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry));
-
- /* We have a local copy now, so release the entry. */
- ent->err_handle = 0;
- wmb();
-
- put_cpu();
-
- if (ent->err_type == SUN4V_ERR_TYPE_WARNING_RES) {
- /* If err_type is 0x4, it's a powerdown request. Do
- * not do the usual resumable error log because that
- * makes it look like some abnormal error.
- */
- printk(KERN_INFO "Power down request...\n");
- kill_cad_pid(SIGINT, 1);
- return;
- }
-
- sun4v_log_error(regs, &local_copy, cpu,
- KERN_ERR "RESUMABLE ERROR",
- &sun4v_resum_oflow_cnt);
-}
-
-/* If we try to printk() we'll probably make matters worse, by trying
- * to retake locks this cpu already holds or causing more errors. So
- * just bump a counter, and we'll report these counter bumps above.
- */
-void sun4v_resum_overflow(struct pt_regs *regs)
-{
- atomic_inc(&sun4v_resum_oflow_cnt);
-}
-
-/* We run with %pil set to 15 and PSTATE_IE enabled in %pstate.
- * Log the event, clear the first word of the entry, and die.
- */
-void sun4v_nonresum_error(struct pt_regs *regs, unsigned long offset)
-{
- struct sun4v_error_entry *ent, local_copy;
- struct trap_per_cpu *tb;
- unsigned long paddr;
- int cpu;
-
- cpu = get_cpu();
-
- tb = &trap_block[cpu];
- paddr = tb->nonresum_kernel_buf_pa + offset;
- ent = __va(paddr);
-
- memcpy(&local_copy, ent, sizeof(struct sun4v_error_entry));
-
- /* We have a local copy now, so release the entry. */
- ent->err_handle = 0;
- wmb();
-
- put_cpu();
-
-#ifdef CONFIG_PCI
- /* Check for the special PCI poke sequence. */
- if (pci_poke_in_progress && pci_poke_cpu == cpu) {
- pci_poke_faulted = 1;
- regs->tpc += 4;
- regs->tnpc = regs->tpc + 4;
- return;
- }
-#endif
-
- sun4v_log_error(regs, &local_copy, cpu,
- KERN_EMERG "NON-RESUMABLE ERROR",
- &sun4v_nonresum_oflow_cnt);
-
- panic("Non-resumable error.");
-}
-
-/* If we try to printk() we'll probably make matters worse, by trying
- * to retake locks this cpu already holds or causing more errors. So
- * just bump a counter, and we'll report these counter bumps above.
- */
-void sun4v_nonresum_overflow(struct pt_regs *regs)
-{
- /* XXX Actually even this can make not that much sense. Perhaps
- * XXX we should just pull the plug and panic directly from here?
- */
- atomic_inc(&sun4v_nonresum_oflow_cnt);
-}
-
-unsigned long sun4v_err_itlb_vaddr;
-unsigned long sun4v_err_itlb_ctx;
-unsigned long sun4v_err_itlb_pte;
-unsigned long sun4v_err_itlb_error;
-
-void sun4v_itlb_error_report(struct pt_regs *regs, int tl)
-{
- if (tl > 1)
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
-
- printk(KERN_EMERG "SUN4V-ITLB: Error at TPC[%lx], tl %d\n",
- regs->tpc, tl);
- printk(KERN_EMERG "SUN4V-ITLB: TPC<%pS>\n", (void *) regs->tpc);
- printk(KERN_EMERG "SUN4V-ITLB: O7[%lx]\n", regs->u_regs[UREG_I7]);
- printk(KERN_EMERG "SUN4V-ITLB: O7<%pS>\n",
- (void *) regs->u_regs[UREG_I7]);
- printk(KERN_EMERG "SUN4V-ITLB: vaddr[%lx] ctx[%lx] "
- "pte[%lx] error[%lx]\n",
- sun4v_err_itlb_vaddr, sun4v_err_itlb_ctx,
- sun4v_err_itlb_pte, sun4v_err_itlb_error);
-
- prom_halt();
-}
-
-unsigned long sun4v_err_dtlb_vaddr;
-unsigned long sun4v_err_dtlb_ctx;
-unsigned long sun4v_err_dtlb_pte;
-unsigned long sun4v_err_dtlb_error;
-
-void sun4v_dtlb_error_report(struct pt_regs *regs, int tl)
-{
- if (tl > 1)
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
-
- printk(KERN_EMERG "SUN4V-DTLB: Error at TPC[%lx], tl %d\n",
- regs->tpc, tl);
- printk(KERN_EMERG "SUN4V-DTLB: TPC<%pS>\n", (void *) regs->tpc);
- printk(KERN_EMERG "SUN4V-DTLB: O7[%lx]\n", regs->u_regs[UREG_I7]);
- printk(KERN_EMERG "SUN4V-DTLB: O7<%pS>\n",
- (void *) regs->u_regs[UREG_I7]);
- printk(KERN_EMERG "SUN4V-DTLB: vaddr[%lx] ctx[%lx] "
- "pte[%lx] error[%lx]\n",
- sun4v_err_dtlb_vaddr, sun4v_err_dtlb_ctx,
- sun4v_err_dtlb_pte, sun4v_err_dtlb_error);
-
- prom_halt();
-}
-
-void hypervisor_tlbop_error(unsigned long err, unsigned long op)
-{
- printk(KERN_CRIT "SUN4V: TLB hv call error %lu for op %lu\n",
- err, op);
-}
-
-void hypervisor_tlbop_error_xcall(unsigned long err, unsigned long op)
-{
- printk(KERN_CRIT "SUN4V: XCALL TLB hv call error %lu for op %lu\n",
- err, op);
-}
-
-void do_fpe_common(struct pt_regs *regs)
-{
- if (regs->tstate & TSTATE_PRIV) {
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- } else {
- unsigned long fsr = current_thread_info()->xfsr[0];
- siginfo_t info;
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = 0;
- info.si_code = __SI_FAULT;
- if ((fsr & 0x1c000) == (1 << 14)) {
- if (fsr & 0x10)
- info.si_code = FPE_FLTINV;
- else if (fsr & 0x08)
- info.si_code = FPE_FLTOVF;
- else if (fsr & 0x04)
- info.si_code = FPE_FLTUND;
- else if (fsr & 0x02)
- info.si_code = FPE_FLTDIV;
- else if (fsr & 0x01)
- info.si_code = FPE_FLTRES;
- }
- force_sig_info(SIGFPE, &info, current);
- }
-}
-
-void do_fpieee(struct pt_regs *regs)
-{
- if (notify_die(DIE_TRAP, "fpu exception ieee", regs,
- 0, 0x24, SIGFPE) == NOTIFY_STOP)
- return;
-
- do_fpe_common(regs);
-}
-
-extern int do_mathemu(struct pt_regs *, struct fpustate *);
-
-void do_fpother(struct pt_regs *regs)
-{
- struct fpustate *f = FPUSTATE;
- int ret = 0;
-
- if (notify_die(DIE_TRAP, "fpu exception other", regs,
- 0, 0x25, SIGFPE) == NOTIFY_STOP)
- return;
-
- switch ((current_thread_info()->xfsr[0] & 0x1c000)) {
- case (2 << 14): /* unfinished_FPop */
- case (3 << 14): /* unimplemented_FPop */
- ret = do_mathemu(regs, f);
- break;
- }
- if (ret)
- return;
- do_fpe_common(regs);
-}
-
-void do_tof(struct pt_regs *regs)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs,
- 0, 0x26, SIGEMT) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV)
- die_if_kernel("Penguin overflow trap from kernel mode", regs);
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGEMT;
- info.si_errno = 0;
- info.si_code = EMT_TAGOVF;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = 0;
- force_sig_info(SIGEMT, &info, current);
-}
-
-void do_div0(struct pt_regs *regs)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "integer division by zero", regs,
- 0, 0x28, SIGFPE) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV)
- die_if_kernel("TL0: Kernel divide by zero.", regs);
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = FPE_INTDIV;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = 0;
- force_sig_info(SIGFPE, &info, current);
-}
-
-static void instruction_dump(unsigned int *pc)
-{
- int i;
-
- if ((((unsigned long) pc) & 3))
- return;
-
- printk("Instruction DUMP:");
- for (i = -3; i < 6; i++)
- printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
- printk("\n");
-}
-
-static void user_instruction_dump(unsigned int __user *pc)
-{
- int i;
- unsigned int buf[9];
-
- if ((((unsigned long) pc) & 3))
- return;
-
- if (copy_from_user(buf, pc - 3, sizeof(buf)))
- return;
-
- printk("Instruction DUMP:");
- for (i = 0; i < 9; i++)
- printk("%c%08x%c",i==3?' ':'<',buf[i],i==3?' ':'>');
- printk("\n");
-}
-
-void show_stack(struct task_struct *tsk, unsigned long *_ksp)
-{
- unsigned long fp, thread_base, ksp;
- struct thread_info *tp;
- int count = 0;
-
- ksp = (unsigned long) _ksp;
- if (!tsk)
- tsk = current;
- tp = task_thread_info(tsk);
- if (ksp == 0UL) {
- if (tsk == current)
- asm("mov %%fp, %0" : "=r" (ksp));
- else
- ksp = tp->ksp;
- }
- if (tp == current_thread_info())
- flushw_all();
-
- fp = ksp + STACK_BIAS;
- thread_base = (unsigned long) tp;
-
- printk("Call Trace:\n");
- do {
- struct sparc_stackf *sf;
- struct pt_regs *regs;
- unsigned long pc;
-
- if (!kstack_valid(tp, fp))
- break;
- sf = (struct sparc_stackf *) fp;
- regs = (struct pt_regs *) (sf + 1);
-
- if (kstack_is_trap_frame(tp, regs)) {
- if (!(regs->tstate & TSTATE_PRIV))
- break;
- pc = regs->tpc;
- fp = regs->u_regs[UREG_I6] + STACK_BIAS;
- } else {
- pc = sf->callers_pc;
- fp = (unsigned long)sf->fp + STACK_BIAS;
- }
-
- printk(" [%016lx] %pS\n", pc, (void *) pc);
- } while (++count < 16);
-}
-
-void dump_stack(void)
-{
- show_stack(current, NULL);
-}
-
-EXPORT_SYMBOL(dump_stack);
-
-static inline int is_kernel_stack(struct task_struct *task,
- struct reg_window *rw)
-{
- unsigned long rw_addr = (unsigned long) rw;
- unsigned long thread_base, thread_end;
-
- if (rw_addr < PAGE_OFFSET) {
- if (task != &init_task)
- return 0;
- }
-
- thread_base = (unsigned long) task_stack_page(task);
- thread_end = thread_base + sizeof(union thread_union);
- if (rw_addr >= thread_base &&
- rw_addr < thread_end &&
- !(rw_addr & 0x7UL))
- return 1;
-
- return 0;
-}
-
-static inline struct reg_window *kernel_stack_up(struct reg_window *rw)
-{
- unsigned long fp = rw->ins[6];
-
- if (!fp)
- return NULL;
-
- return (struct reg_window *) (fp + STACK_BIAS);
-}
-
-void die_if_kernel(char *str, struct pt_regs *regs)
-{
- static int die_counter;
- int count = 0;
-
- /* Amuse the user. */
- printk(
-" \\|/ ____ \\|/\n"
-" \"@'/ .. \\`@\"\n"
-" /_| \\__/ |_\\\n"
-" \\__U_/\n");
-
- printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
- notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV);
- __asm__ __volatile__("flushw");
- show_regs(regs);
- add_taint(TAINT_DIE);
- if (regs->tstate & TSTATE_PRIV) {
- struct reg_window *rw = (struct reg_window *)
- (regs->u_regs[UREG_FP] + STACK_BIAS);
-
- /* Stop the back trace when we hit userland or we
- * find some badly aligned kernel stack.
- */
- while (rw &&
- count++ < 30&&
- is_kernel_stack(current, rw)) {
- printk("Caller[%016lx]: %pS\n", rw->ins[7],
- (void *) rw->ins[7]);
-
- rw = kernel_stack_up(rw);
- }
- instruction_dump ((unsigned int *) regs->tpc);
- } else {
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- user_instruction_dump ((unsigned int __user *) regs->tpc);
- }
- if (regs->tstate & TSTATE_PRIV)
- do_exit(SIGKILL);
- do_exit(SIGSEGV);
-}
-
-#define VIS_OPCODE_MASK ((0x3 << 30) | (0x3f << 19))
-#define VIS_OPCODE_VAL ((0x2 << 30) | (0x36 << 19))
-
-extern int handle_popc(u32 insn, struct pt_regs *regs);
-extern int handle_ldf_stq(u32 insn, struct pt_regs *regs);
-
-void do_illegal_instruction(struct pt_regs *regs)
-{
- unsigned long pc = regs->tpc;
- unsigned long tstate = regs->tstate;
- u32 insn;
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "illegal instruction", regs,
- 0, 0x10, SIGILL) == NOTIFY_STOP)
- return;
-
- if (tstate & TSTATE_PRIV)
- die_if_kernel("Kernel illegal instruction", regs);
- if (test_thread_flag(TIF_32BIT))
- pc = (u32)pc;
- if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
- if ((insn & 0xc1ffc000) == 0x81700000) /* POPC */ {
- if (handle_popc(insn, regs))
- return;
- } else if ((insn & 0xc1580000) == 0xc1100000) /* LDQ/STQ */ {
- if (handle_ldf_stq(insn, regs))
- return;
- } else if (tlb_type == hypervisor) {
- if ((insn & VIS_OPCODE_MASK) == VIS_OPCODE_VAL) {
- if (!vis_emul(regs, insn))
- return;
- } else {
- struct fpustate *f = FPUSTATE;
-
- /* XXX maybe verify XFSR bits like
- * XXX do_fpother() does?
- */
- if (do_mathemu(regs, f))
- return;
- }
- }
- }
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_ILLOPC;
- info.si_addr = (void __user *)pc;
- info.si_trapno = 0;
- force_sig_info(SIGILL, &info, current);
-}
-
-extern void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn);
-
-void mem_address_unaligned(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "memory address unaligned", regs,
- 0, 0x34, SIGSEGV) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
- return;
- }
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_ADRALN;
- info.si_addr = (void __user *)sfar;
- info.si_trapno = 0;
- force_sig_info(SIGBUS, &info, current);
-}
-
-void sun4v_do_mna(struct pt_regs *regs, unsigned long addr, unsigned long type_ctx)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "memory address unaligned", regs,
- 0, 0x34, SIGSEGV) == NOTIFY_STOP)
- return;
-
- if (regs->tstate & TSTATE_PRIV) {
- kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
- return;
- }
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_ADRALN;
- info.si_addr = (void __user *) addr;
- info.si_trapno = 0;
- force_sig_info(SIGBUS, &info, current);
-}
-
-void do_privop(struct pt_regs *regs)
-{
- siginfo_t info;
-
- if (notify_die(DIE_TRAP, "privileged operation", regs,
- 0, 0x11, SIGILL) == NOTIFY_STOP)
- return;
-
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
- info.si_signo = SIGILL;
- info.si_errno = 0;
- info.si_code = ILL_PRVOPC;
- info.si_addr = (void __user *)regs->tpc;
- info.si_trapno = 0;
- force_sig_info(SIGILL, &info, current);
-}
-
-void do_privact(struct pt_regs *regs)
-{
- do_privop(regs);
-}
-
-/* Trap level 1 stuff or other traps we should never see... */
-void do_cee(struct pt_regs *regs)
-{
- die_if_kernel("TL0: Cache Error Exception", regs);
-}
-
-void do_cee_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Cache Error Exception", regs);
-}
-
-void do_dae_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Data Access Exception", regs);
-}
-
-void do_iae_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Instruction Access Exception", regs);
-}
-
-void do_div0_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: DIV0 Exception", regs);
-}
-
-void do_fpdis_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: FPU Disabled", regs);
-}
-
-void do_fpieee_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: FPU IEEE Exception", regs);
-}
-
-void do_fpother_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: FPU Other Exception", regs);
-}
-
-void do_ill_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Illegal Instruction Exception", regs);
-}
-
-void do_irq_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: IRQ Exception", regs);
-}
-
-void do_lddfmna_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: LDDF Exception", regs);
-}
-
-void do_stdfmna_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: STDF Exception", regs);
-}
-
-void do_paw(struct pt_regs *regs)
-{
- die_if_kernel("TL0: Phys Watchpoint Exception", regs);
-}
-
-void do_paw_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Phys Watchpoint Exception", regs);
-}
-
-void do_vaw(struct pt_regs *regs)
-{
- die_if_kernel("TL0: Virt Watchpoint Exception", regs);
-}
-
-void do_vaw_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Virt Watchpoint Exception", regs);
-}
-
-void do_tof_tl1(struct pt_regs *regs)
-{
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Tag Overflow Exception", regs);
-}
-
-void do_getpsr(struct pt_regs *regs)
-{
- regs->u_regs[UREG_I0] = tstate_to_psr(regs->tstate);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- if (test_thread_flag(TIF_32BIT)) {
- regs->tpc &= 0xffffffff;
- regs->tnpc &= 0xffffffff;
- }
-}
-
-struct trap_per_cpu trap_block[NR_CPUS];
-
-/* This can get invoked before sched_init() so play it super safe
- * and use hard_smp_processor_id().
- */
-void notrace init_cur_cpu_trap(struct thread_info *t)
-{
- int cpu = hard_smp_processor_id();
- struct trap_per_cpu *p = &trap_block[cpu];
-
- p->thread = t;
- p->pgd_paddr = 0;
-}
-
-extern void thread_info_offsets_are_bolixed_dave(void);
-extern void trap_per_cpu_offsets_are_bolixed_dave(void);
-extern void tsb_config_offsets_are_bolixed_dave(void);
-
-/* Only invoked on boot processor. */
-void __init trap_init(void)
-{
- /* Compile time sanity check. */
- if (TI_TASK != offsetof(struct thread_info, task) ||
- TI_FLAGS != offsetof(struct thread_info, flags) ||
- TI_CPU != offsetof(struct thread_info, cpu) ||
- TI_FPSAVED != offsetof(struct thread_info, fpsaved) ||
- TI_KSP != offsetof(struct thread_info, ksp) ||
- TI_FAULT_ADDR != offsetof(struct thread_info, fault_address) ||
- TI_KREGS != offsetof(struct thread_info, kregs) ||
- TI_UTRAPS != offsetof(struct thread_info, utraps) ||
- TI_EXEC_DOMAIN != offsetof(struct thread_info, exec_domain) ||
- TI_REG_WINDOW != offsetof(struct thread_info, reg_window) ||
- TI_RWIN_SPTRS != offsetof(struct thread_info, rwbuf_stkptrs) ||
- TI_GSR != offsetof(struct thread_info, gsr) ||
- TI_XFSR != offsetof(struct thread_info, xfsr) ||
- TI_USER_CNTD0 != offsetof(struct thread_info, user_cntd0) ||
- TI_USER_CNTD1 != offsetof(struct thread_info, user_cntd1) ||
- TI_KERN_CNTD0 != offsetof(struct thread_info, kernel_cntd0) ||
- TI_KERN_CNTD1 != offsetof(struct thread_info, kernel_cntd1) ||
- TI_PCR != offsetof(struct thread_info, pcr_reg) ||
- TI_PRE_COUNT != offsetof(struct thread_info, preempt_count) ||
- TI_NEW_CHILD != offsetof(struct thread_info, new_child) ||
- TI_SYS_NOERROR != offsetof(struct thread_info, syscall_noerror) ||
- TI_RESTART_BLOCK != offsetof(struct thread_info, restart_block) ||
- TI_KUNA_REGS != offsetof(struct thread_info, kern_una_regs) ||
- TI_KUNA_INSN != offsetof(struct thread_info, kern_una_insn) ||
- TI_FPREGS != offsetof(struct thread_info, fpregs) ||
- (TI_FPREGS & (64 - 1)))
- thread_info_offsets_are_bolixed_dave();
-
- if (TRAP_PER_CPU_THREAD != offsetof(struct trap_per_cpu, thread) ||
- (TRAP_PER_CPU_PGD_PADDR !=
- offsetof(struct trap_per_cpu, pgd_paddr)) ||
- (TRAP_PER_CPU_CPU_MONDO_PA !=
- offsetof(struct trap_per_cpu, cpu_mondo_pa)) ||
- (TRAP_PER_CPU_DEV_MONDO_PA !=
- offsetof(struct trap_per_cpu, dev_mondo_pa)) ||
- (TRAP_PER_CPU_RESUM_MONDO_PA !=
- offsetof(struct trap_per_cpu, resum_mondo_pa)) ||
- (TRAP_PER_CPU_RESUM_KBUF_PA !=
- offsetof(struct trap_per_cpu, resum_kernel_buf_pa)) ||
- (TRAP_PER_CPU_NONRESUM_MONDO_PA !=
- offsetof(struct trap_per_cpu, nonresum_mondo_pa)) ||
- (TRAP_PER_CPU_NONRESUM_KBUF_PA !=
- offsetof(struct trap_per_cpu, nonresum_kernel_buf_pa)) ||
- (TRAP_PER_CPU_FAULT_INFO !=
- offsetof(struct trap_per_cpu, fault_info)) ||
- (TRAP_PER_CPU_CPU_MONDO_BLOCK_PA !=
- offsetof(struct trap_per_cpu, cpu_mondo_block_pa)) ||
- (TRAP_PER_CPU_CPU_LIST_PA !=
- offsetof(struct trap_per_cpu, cpu_list_pa)) ||
- (TRAP_PER_CPU_TSB_HUGE !=
- offsetof(struct trap_per_cpu, tsb_huge)) ||
- (TRAP_PER_CPU_TSB_HUGE_TEMP !=
- offsetof(struct trap_per_cpu, tsb_huge_temp)) ||
- (TRAP_PER_CPU_IRQ_WORKLIST_PA !=
- offsetof(struct trap_per_cpu, irq_worklist_pa)) ||
- (TRAP_PER_CPU_CPU_MONDO_QMASK !=
- offsetof(struct trap_per_cpu, cpu_mondo_qmask)) ||
- (TRAP_PER_CPU_DEV_MONDO_QMASK !=
- offsetof(struct trap_per_cpu, dev_mondo_qmask)) ||
- (TRAP_PER_CPU_RESUM_QMASK !=
- offsetof(struct trap_per_cpu, resum_qmask)) ||
- (TRAP_PER_CPU_NONRESUM_QMASK !=
- offsetof(struct trap_per_cpu, nonresum_qmask)))
- trap_per_cpu_offsets_are_bolixed_dave();
-
- if ((TSB_CONFIG_TSB !=
- offsetof(struct tsb_config, tsb)) ||
- (TSB_CONFIG_RSS_LIMIT !=
- offsetof(struct tsb_config, tsb_rss_limit)) ||
- (TSB_CONFIG_NENTRIES !=
- offsetof(struct tsb_config, tsb_nentries)) ||
- (TSB_CONFIG_REG_VAL !=
- offsetof(struct tsb_config, tsb_reg_val)) ||
- (TSB_CONFIG_MAP_VADDR !=
- offsetof(struct tsb_config, tsb_map_vaddr)) ||
- (TSB_CONFIG_MAP_PTE !=
- offsetof(struct tsb_config, tsb_map_pte)))
- tsb_config_offsets_are_bolixed_dave();
-
- /* Attach to the address space of init_task. On SMP we
- * do this in smp.c:smp_callin for other cpus.
- */
- atomic_inc(&init_mm.mm_count);
- current->active_mm = &init_mm;
-}
diff --git a/arch/sparc64/kernel/tsb.S b/arch/sparc64/kernel/tsb.S
deleted file mode 100644
index c499214b501d..000000000000
--- a/arch/sparc64/kernel/tsb.S
+++ /dev/null
@@ -1,552 +0,0 @@
-/* tsb.S: Sparc64 TSB table handling.
- *
- * Copyright (C) 2006 David S. Miller <davem@davemloft.net>
- */
-
-
-#include <asm/tsb.h>
-#include <asm/hypervisor.h>
-#include <asm/page.h>
-#include <asm/cpudata.h>
-#include <asm/mmu.h>
-
- .text
- .align 32
-
- /* Invoked from TLB miss handler, we are in the
- * MMU global registers and they are setup like
- * this:
- *
- * %g1: TSB entry pointer
- * %g2: available temporary
- * %g3: FAULT_CODE_{D,I}TLB
- * %g4: available temporary
- * %g5: available temporary
- * %g6: TAG TARGET
- * %g7: available temporary, will be loaded by us with
- * the physical address base of the linux page
- * tables for the current address space
- */
-tsb_miss_dtlb:
- mov TLB_TAG_ACCESS, %g4
- ba,pt %xcc, tsb_miss_page_table_walk
- ldxa [%g4] ASI_DMMU, %g4
-
-tsb_miss_itlb:
- mov TLB_TAG_ACCESS, %g4
- ba,pt %xcc, tsb_miss_page_table_walk
- ldxa [%g4] ASI_IMMU, %g4
-
- /* At this point we have:
- * %g1 -- PAGE_SIZE TSB entry address
- * %g3 -- FAULT_CODE_{D,I}TLB
- * %g4 -- missing virtual address
- * %g6 -- TAG TARGET (vaddr >> 22)
- */
-tsb_miss_page_table_walk:
- TRAP_LOAD_TRAP_BLOCK(%g7, %g5)
-
- /* Before committing to a full page table walk,
- * check the huge page TSB.
- */
-#ifdef CONFIG_HUGETLB_PAGE
-
-661: ldx [%g7 + TRAP_PER_CPU_TSB_HUGE], %g5
- nop
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- mov SCRATCHPAD_UTSBREG2, %g5
- ldxa [%g5] ASI_SCRATCHPAD, %g5
- .previous
-
- cmp %g5, -1
- be,pt %xcc, 80f
- nop
-
- /* We need an aligned pair of registers containing 2 values
- * which can be easily rematerialized. %g6 and %g7 foot the
- * bill just nicely. We'll save %g6 away into %g2 for the
- * huge page TSB TAG comparison.
- *
- * Perform a huge page TSB lookup.
- */
- mov %g6, %g2
- and %g5, 0x7, %g6
- mov 512, %g7
- andn %g5, 0x7, %g5
- sllx %g7, %g6, %g7
- srlx %g4, HPAGE_SHIFT, %g6
- sub %g7, 1, %g7
- and %g6, %g7, %g6
- sllx %g6, 4, %g6
- add %g5, %g6, %g5
-
- TSB_LOAD_QUAD(%g5, %g6)
- cmp %g6, %g2
- be,a,pt %xcc, tsb_tlb_reload
- mov %g7, %g5
-
- /* No match, remember the huge page TSB entry address,
- * and restore %g6 and %g7.
- */
- TRAP_LOAD_TRAP_BLOCK(%g7, %g6)
- srlx %g4, 22, %g6
-80: stx %g5, [%g7 + TRAP_PER_CPU_TSB_HUGE_TEMP]
-
-#endif
-
- ldx [%g7 + TRAP_PER_CPU_PGD_PADDR], %g7
-
- /* At this point we have:
- * %g1 -- TSB entry address
- * %g3 -- FAULT_CODE_{D,I}TLB
- * %g4 -- missing virtual address
- * %g6 -- TAG TARGET (vaddr >> 22)
- * %g7 -- page table physical address
- *
- * We know that both the base PAGE_SIZE TSB and the HPAGE_SIZE
- * TSB both lack a matching entry.
- */
-tsb_miss_page_table_walk_sun4v_fastpath:
- USER_PGTABLE_WALK_TL1(%g4, %g7, %g5, %g2, tsb_do_fault)
-
- /* Load and check PTE. */
- ldxa [%g5] ASI_PHYS_USE_EC, %g5
- brgez,pn %g5, tsb_do_fault
- nop
-
-#ifdef CONFIG_HUGETLB_PAGE
-661: sethi %uhi(_PAGE_SZALL_4U), %g7
- sllx %g7, 32, %g7
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- mov _PAGE_SZALL_4V, %g7
- nop
- .previous
-
- and %g5, %g7, %g2
-
-661: sethi %uhi(_PAGE_SZHUGE_4U), %g7
- sllx %g7, 32, %g7
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- mov _PAGE_SZHUGE_4V, %g7
- nop
- .previous
-
- cmp %g2, %g7
- bne,pt %xcc, 60f
- nop
-
- /* It is a huge page, use huge page TSB entry address we
- * calculated above.
- */
- TRAP_LOAD_TRAP_BLOCK(%g7, %g2)
- ldx [%g7 + TRAP_PER_CPU_TSB_HUGE_TEMP], %g2
- cmp %g2, -1
- movne %xcc, %g2, %g1
-60:
-#endif
-
- /* At this point we have:
- * %g1 -- TSB entry address
- * %g3 -- FAULT_CODE_{D,I}TLB
- * %g5 -- valid PTE
- * %g6 -- TAG TARGET (vaddr >> 22)
- */
-tsb_reload:
- TSB_LOCK_TAG(%g1, %g2, %g7)
- TSB_WRITE(%g1, %g5, %g6)
-
- /* Finally, load TLB and return from trap. */
-tsb_tlb_reload:
- cmp %g3, FAULT_CODE_DTLB
- bne,pn %xcc, tsb_itlb_load
- nop
-
-tsb_dtlb_load:
-
-661: stxa %g5, [%g0] ASI_DTLB_DATA_IN
- retry
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- nop
- nop
- .previous
-
- /* For sun4v the ASI_DTLB_DATA_IN store and the retry
- * instruction get nop'd out and we get here to branch
- * to the sun4v tlb load code. The registers are setup
- * as follows:
- *
- * %g4: vaddr
- * %g5: PTE
- * %g6: TAG
- *
- * The sun4v TLB load wants the PTE in %g3 so we fix that
- * up here.
- */
- ba,pt %xcc, sun4v_dtlb_load
- mov %g5, %g3
-
-tsb_itlb_load:
- /* Executable bit must be set. */
-661: andcc %g5, _PAGE_EXEC_4U, %g0
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- andcc %g5, _PAGE_EXEC_4V, %g0
- .previous
-
- be,pn %xcc, tsb_do_fault
- nop
-
-661: stxa %g5, [%g0] ASI_ITLB_DATA_IN
- retry
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- nop
- nop
- .previous
-
- /* For sun4v the ASI_ITLB_DATA_IN store and the retry
- * instruction get nop'd out and we get here to branch
- * to the sun4v tlb load code. The registers are setup
- * as follows:
- *
- * %g4: vaddr
- * %g5: PTE
- * %g6: TAG
- *
- * The sun4v TLB load wants the PTE in %g3 so we fix that
- * up here.
- */
- ba,pt %xcc, sun4v_itlb_load
- mov %g5, %g3
-
- /* No valid entry in the page tables, do full fault
- * processing.
- */
-
- .globl tsb_do_fault
-tsb_do_fault:
- cmp %g3, FAULT_CODE_DTLB
-
-661: rdpr %pstate, %g5
- wrpr %g5, PSTATE_AG | PSTATE_MG, %pstate
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- SET_GL(1)
- ldxa [%g0] ASI_SCRATCHPAD, %g4
- .previous
-
- bne,pn %xcc, tsb_do_itlb_fault
- nop
-
-tsb_do_dtlb_fault:
- rdpr %tl, %g3
- cmp %g3, 1
-
-661: mov TLB_TAG_ACCESS, %g4
- ldxa [%g4] ASI_DMMU, %g5
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- ldx [%g4 + HV_FAULT_D_ADDR_OFFSET], %g5
- nop
- .previous
-
- be,pt %xcc, sparc64_realfault_common
- mov FAULT_CODE_DTLB, %g4
- ba,pt %xcc, winfix_trampoline
- nop
-
-tsb_do_itlb_fault:
- rdpr %tpc, %g5
- ba,pt %xcc, sparc64_realfault_common
- mov FAULT_CODE_ITLB, %g4
-
- .globl sparc64_realfault_common
-sparc64_realfault_common:
- /* fault code in %g4, fault address in %g5, etrap will
- * preserve these two values in %l4 and %l5 respectively
- */
- ba,pt %xcc, etrap ! Save trap state
-1: rd %pc, %g7 ! ...
- stb %l4, [%g6 + TI_FAULT_CODE] ! Save fault code
- stx %l5, [%g6 + TI_FAULT_ADDR] ! Save fault address
- call do_sparc64_fault ! Call fault handler
- add %sp, PTREGS_OFF, %o0 ! Compute pt_regs arg
- ba,pt %xcc, rtrap ! Restore cpu state
- nop ! Delay slot (fill me)
-
-winfix_trampoline:
- rdpr %tpc, %g3 ! Prepare winfixup TNPC
- or %g3, 0x7c, %g3 ! Compute branch offset
- wrpr %g3, %tnpc ! Write it into TNPC
- done ! Trap return
-
- /* Insert an entry into the TSB.
- *
- * %o0: TSB entry pointer (virt or phys address)
- * %o1: tag
- * %o2: pte
- */
- .align 32
- .globl __tsb_insert
-__tsb_insert:
- rdpr %pstate, %o5
- wrpr %o5, PSTATE_IE, %pstate
- TSB_LOCK_TAG(%o0, %g2, %g3)
- TSB_WRITE(%o0, %o2, %o1)
- wrpr %o5, %pstate
- retl
- nop
- .size __tsb_insert, .-__tsb_insert
-
- /* Flush the given TSB entry if it has the matching
- * tag.
- *
- * %o0: TSB entry pointer (virt or phys address)
- * %o1: tag
- */
- .align 32
- .globl tsb_flush
- .type tsb_flush,#function
-tsb_flush:
- sethi %hi(TSB_TAG_LOCK_HIGH), %g2
-1: TSB_LOAD_TAG(%o0, %g1)
- srlx %g1, 32, %o3
- andcc %o3, %g2, %g0
- bne,pn %icc, 1b
- membar #LoadLoad
- cmp %g1, %o1
- mov 1, %o3
- bne,pt %xcc, 2f
- sllx %o3, TSB_TAG_INVALID_BIT, %o3
- TSB_CAS_TAG(%o0, %g1, %o3)
- cmp %g1, %o3
- bne,pn %xcc, 1b
- nop
-2: retl
- TSB_MEMBAR
- .size tsb_flush, .-tsb_flush
-
- /* Reload MMU related context switch state at
- * schedule() time.
- *
- * %o0: page table physical address
- * %o1: TSB base config pointer
- * %o2: TSB huge config pointer, or NULL if none
- * %o3: Hypervisor TSB descriptor physical address
- *
- * We have to run this whole thing with interrupts
- * disabled so that the current cpu doesn't change
- * due to preemption.
- */
- .align 32
- .globl __tsb_context_switch
- .type __tsb_context_switch,#function
-__tsb_context_switch:
- rdpr %pstate, %g1
- wrpr %g1, PSTATE_IE, %pstate
-
- TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
-
- stx %o0, [%g2 + TRAP_PER_CPU_PGD_PADDR]
-
- ldx [%o1 + TSB_CONFIG_REG_VAL], %o0
- brz,pt %o2, 1f
- mov -1, %g3
-
- ldx [%o2 + TSB_CONFIG_REG_VAL], %g3
-
-1: stx %g3, [%g2 + TRAP_PER_CPU_TSB_HUGE]
-
- sethi %hi(tlb_type), %g2
- lduw [%g2 + %lo(tlb_type)], %g2
- cmp %g2, 3
- bne,pt %icc, 50f
- nop
-
- /* Hypervisor TSB switch. */
- mov SCRATCHPAD_UTSBREG1, %o5
- stxa %o0, [%o5] ASI_SCRATCHPAD
- mov SCRATCHPAD_UTSBREG2, %o5
- stxa %g3, [%o5] ASI_SCRATCHPAD
-
- mov 2, %o0
- cmp %g3, -1
- move %xcc, 1, %o0
-
- mov HV_FAST_MMU_TSB_CTXNON0, %o5
- mov %o3, %o1
- ta HV_FAST_TRAP
-
- /* Finish up. */
- ba,pt %xcc, 9f
- nop
-
- /* SUN4U TSB switch. */
-50: mov TSB_REG, %o5
- stxa %o0, [%o5] ASI_DMMU
- membar #Sync
- stxa %o0, [%o5] ASI_IMMU
- membar #Sync
-
-2: ldx [%o1 + TSB_CONFIG_MAP_VADDR], %o4
- brz %o4, 9f
- ldx [%o1 + TSB_CONFIG_MAP_PTE], %o5
-
- sethi %hi(sparc64_highest_unlocked_tlb_ent), %g2
- mov TLB_TAG_ACCESS, %g3
- lduw [%g2 + %lo(sparc64_highest_unlocked_tlb_ent)], %g2
- stxa %o4, [%g3] ASI_DMMU
- membar #Sync
- sllx %g2, 3, %g2
- stxa %o5, [%g2] ASI_DTLB_DATA_ACCESS
- membar #Sync
-
- brz,pt %o2, 9f
- nop
-
- ldx [%o2 + TSB_CONFIG_MAP_VADDR], %o4
- ldx [%o2 + TSB_CONFIG_MAP_PTE], %o5
- mov TLB_TAG_ACCESS, %g3
- stxa %o4, [%g3] ASI_DMMU
- membar #Sync
- sub %g2, (1 << 3), %g2
- stxa %o5, [%g2] ASI_DTLB_DATA_ACCESS
- membar #Sync
-
-9:
- wrpr %g1, %pstate
-
- retl
- nop
- .size __tsb_context_switch, .-__tsb_context_switch
-
-#define TSB_PASS_BITS ((1 << TSB_TAG_LOCK_BIT) | \
- (1 << TSB_TAG_INVALID_BIT))
-
- .align 32
- .globl copy_tsb
- .type copy_tsb,#function
-copy_tsb: /* %o0=old_tsb_base, %o1=old_tsb_size
- * %o2=new_tsb_base, %o3=new_tsb_size
- */
- sethi %uhi(TSB_PASS_BITS), %g7
- srlx %o3, 4, %o3
- add %o0, %o1, %g1 /* end of old tsb */
- sllx %g7, 32, %g7
- sub %o3, 1, %o3 /* %o3 == new tsb hash mask */
-
-661: prefetcha [%o0] ASI_N, #one_read
- .section .tsb_phys_patch, "ax"
- .word 661b
- prefetcha [%o0] ASI_PHYS_USE_EC, #one_read
- .previous
-
-90: andcc %o0, (64 - 1), %g0
- bne 1f
- add %o0, 64, %o5
-
-661: prefetcha [%o5] ASI_N, #one_read
- .section .tsb_phys_patch, "ax"
- .word 661b
- prefetcha [%o5] ASI_PHYS_USE_EC, #one_read
- .previous
-
-1: TSB_LOAD_QUAD(%o0, %g2) /* %g2/%g3 == TSB entry */
- andcc %g2, %g7, %g0 /* LOCK or INVALID set? */
- bne,pn %xcc, 80f /* Skip it */
- sllx %g2, 22, %o4 /* TAG --> VADDR */
-
- /* This can definitely be computed faster... */
- srlx %o0, 4, %o5 /* Build index */
- and %o5, 511, %o5 /* Mask index */
- sllx %o5, PAGE_SHIFT, %o5 /* Put into vaddr position */
- or %o4, %o5, %o4 /* Full VADDR. */
- srlx %o4, PAGE_SHIFT, %o4 /* Shift down to create index */
- and %o4, %o3, %o4 /* Mask with new_tsb_nents-1 */
- sllx %o4, 4, %o4 /* Shift back up into tsb ent offset */
- TSB_STORE(%o2 + %o4, %g2) /* Store TAG */
- add %o4, 0x8, %o4 /* Advance to TTE */
- TSB_STORE(%o2 + %o4, %g3) /* Store TTE */
-
-80: add %o0, 16, %o0
- cmp %o0, %g1
- bne,pt %xcc, 90b
- nop
-
- retl
- TSB_MEMBAR
- .size copy_tsb, .-copy_tsb
-
- /* Set the invalid bit in all TSB entries. */
- .align 32
- .globl tsb_init
- .type tsb_init,#function
-tsb_init: /* %o0 = TSB vaddr, %o1 = size in bytes */
- prefetch [%o0 + 0x000], #n_writes
- mov 1, %g1
- prefetch [%o0 + 0x040], #n_writes
- sllx %g1, TSB_TAG_INVALID_BIT, %g1
- prefetch [%o0 + 0x080], #n_writes
-1: prefetch [%o0 + 0x0c0], #n_writes
- stx %g1, [%o0 + 0x00]
- stx %g1, [%o0 + 0x10]
- stx %g1, [%o0 + 0x20]
- stx %g1, [%o0 + 0x30]
- prefetch [%o0 + 0x100], #n_writes
- stx %g1, [%o0 + 0x40]
- stx %g1, [%o0 + 0x50]
- stx %g1, [%o0 + 0x60]
- stx %g1, [%o0 + 0x70]
- prefetch [%o0 + 0x140], #n_writes
- stx %g1, [%o0 + 0x80]
- stx %g1, [%o0 + 0x90]
- stx %g1, [%o0 + 0xa0]
- stx %g1, [%o0 + 0xb0]
- prefetch [%o0 + 0x180], #n_writes
- stx %g1, [%o0 + 0xc0]
- stx %g1, [%o0 + 0xd0]
- stx %g1, [%o0 + 0xe0]
- stx %g1, [%o0 + 0xf0]
- subcc %o1, 0x100, %o1
- bne,pt %xcc, 1b
- add %o0, 0x100, %o0
- retl
- nop
- nop
- nop
- .size tsb_init, .-tsb_init
-
- .globl NGtsb_init
- .type NGtsb_init,#function
-NGtsb_init:
- rd %asi, %g2
- mov 1, %g1
- wr %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
- sllx %g1, TSB_TAG_INVALID_BIT, %g1
-1: stxa %g1, [%o0 + 0x00] %asi
- stxa %g1, [%o0 + 0x10] %asi
- stxa %g1, [%o0 + 0x20] %asi
- stxa %g1, [%o0 + 0x30] %asi
- stxa %g1, [%o0 + 0x40] %asi
- stxa %g1, [%o0 + 0x50] %asi
- stxa %g1, [%o0 + 0x60] %asi
- stxa %g1, [%o0 + 0x70] %asi
- stxa %g1, [%o0 + 0x80] %asi
- stxa %g1, [%o0 + 0x90] %asi
- stxa %g1, [%o0 + 0xa0] %asi
- stxa %g1, [%o0 + 0xb0] %asi
- stxa %g1, [%o0 + 0xc0] %asi
- stxa %g1, [%o0 + 0xd0] %asi
- stxa %g1, [%o0 + 0xe0] %asi
- stxa %g1, [%o0 + 0xf0] %asi
- subcc %o1, 0x100, %o1
- bne,pt %xcc, 1b
- add %o0, 0x100, %o0
- membar #Sync
- retl
- wr %g2, 0x0, %asi
- .size NGtsb_init, .-NGtsb_init
diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S
deleted file mode 100644
index 1ade3d6fb7fc..000000000000
--- a/arch/sparc64/kernel/ttable.S
+++ /dev/null
@@ -1,266 +0,0 @@
-/* ttable.S: Sparc V9 Trap Table(s) with SpitFire/Cheetah/SUN4V extensions.
- *
- * Copyright (C) 1996, 2001, 2006 David S. Miller (davem@davemloft.net)
- */
-
-
- .globl sparc64_ttable_tl0, sparc64_ttable_tl1
- .globl tl0_icpe, tl1_icpe
- .globl tl0_dcpe, tl1_dcpe
- .globl tl0_fecc, tl1_fecc
- .globl tl0_cee, tl1_cee
- .globl tl0_iae, tl1_iae
- .globl tl0_dae, tl1_dae
-
-sparc64_ttable_tl0:
-tl0_resv000: BOOT_KERNEL BTRAP(0x1) BTRAP(0x2) BTRAP(0x3)
-tl0_resv004: BTRAP(0x4) BTRAP(0x5) BTRAP(0x6) BTRAP(0x7)
-tl0_iax: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_insn_access_exception)
-tl0_itsb_4v: SUN4V_ITSB_MISS
-tl0_iae: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_access_error)
-tl0_resv00b: BTRAP(0xb) BTRAP(0xc) BTRAP(0xd) BTRAP(0xe) BTRAP(0xf)
-tl0_ill: membar #Sync
- TRAP_7INSNS(do_illegal_instruction)
-tl0_privop: TRAP(do_privop)
-tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17)
-tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d)
-tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f)
-tl0_fpdis: TRAP_NOSAVE(do_fpdis)
-tl0_fpieee: TRAP_SAVEFPU(do_fpieee)
-tl0_fpother: TRAP_NOSAVE(do_fpother_check_fitos)
-tl0_tof: TRAP(do_tof)
-tl0_cwin: CLEAN_WINDOW
-tl0_div0: TRAP(do_div0)
-tl0_resv029: BTRAP(0x29) BTRAP(0x2a) BTRAP(0x2b) BTRAP(0x2c) BTRAP(0x2d) BTRAP(0x2e)
-tl0_resv02f: BTRAP(0x2f)
-tl0_dax: TRAP_NOSAVE(__spitfire_data_access_exception)
-tl0_dtsb_4v: SUN4V_DTSB_MISS
-tl0_dae: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_access_error)
-tl0_resv033: BTRAP(0x33)
-tl0_mna: TRAP_NOSAVE(do_mna)
-tl0_lddfmna: TRAP_NOSAVE(do_lddfmna)
-tl0_stdfmna: TRAP_NOSAVE(do_stdfmna)
-tl0_privact: TRAP_NOSAVE(__do_privact)
-tl0_resv038: BTRAP(0x38) BTRAP(0x39) BTRAP(0x3a) BTRAP(0x3b) BTRAP(0x3c) BTRAP(0x3d)
-tl0_resv03e: BTRAP(0x3e) BTRAP(0x3f) BTRAP(0x40)
-#ifdef CONFIG_SMP
-tl0_irq1: TRAP_IRQ(smp_call_function_client, 1)
-tl0_irq2: TRAP_IRQ(smp_receive_signal_client, 2)
-tl0_irq3: TRAP_IRQ(smp_penguin_jailcell, 3)
-tl0_irq4: TRAP_IRQ(smp_new_mmu_context_version_client, 4)
-#else
-tl0_irq1: BTRAP(0x41)
-tl0_irq2: BTRAP(0x42)
-tl0_irq3: BTRAP(0x43)
-tl0_irq4: BTRAP(0x44)
-#endif
-tl0_irq5: TRAP_IRQ(handler_irq, 5)
-#ifdef CONFIG_SMP
-tl0_irq6: TRAP_IRQ(smp_call_function_single_client, 6)
-#else
-tl0_irq6: BTRAP(0x46)
-#endif
-tl0_irq7: BTRAP(0x47) BTRAP(0x48) BTRAP(0x49)
-tl0_irq10: BTRAP(0x4a) BTRAP(0x4b) BTRAP(0x4c) BTRAP(0x4d)
-tl0_irq14: TRAP_IRQ(timer_interrupt, 14)
-tl0_irq15: TRAP_IRQ(handler_irq, 15)
-tl0_resv050: BTRAP(0x50) BTRAP(0x51) BTRAP(0x52) BTRAP(0x53) BTRAP(0x54) BTRAP(0x55)
-tl0_resv056: BTRAP(0x56) BTRAP(0x57) BTRAP(0x58) BTRAP(0x59) BTRAP(0x5a) BTRAP(0x5b)
-tl0_resv05c: BTRAP(0x5c) BTRAP(0x5d) BTRAP(0x5e) BTRAP(0x5f)
-tl0_ivec: TRAP_IVEC
-tl0_paw: TRAP(do_paw)
-tl0_vaw: TRAP(do_vaw)
-tl0_cee: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_cee_trap)
-tl0_iamiss:
-#include "itlb_miss.S"
-tl0_damiss:
-#include "dtlb_miss.S"
-tl0_daprot:
-#include "dtlb_prot.S"
-tl0_fecc: BTRAP(0x70) /* Fast-ECC on Cheetah */
-tl0_dcpe: BTRAP(0x71) /* D-cache Parity Error on Cheetah+ */
-tl0_icpe: BTRAP(0x72) /* I-cache Parity Error on Cheetah+ */
-tl0_resv073: BTRAP(0x73) BTRAP(0x74) BTRAP(0x75)
-tl0_resv076: BTRAP(0x76) BTRAP(0x77) BTRAP(0x78) BTRAP(0x79) BTRAP(0x7a) BTRAP(0x7b)
-tl0_cpu_mondo: TRAP_NOSAVE(sun4v_cpu_mondo)
-tl0_dev_mondo: TRAP_NOSAVE(sun4v_dev_mondo)
-tl0_res_mondo: TRAP_NOSAVE(sun4v_res_mondo)
-tl0_nres_mondo: TRAP_NOSAVE(sun4v_nonres_mondo)
-tl0_s0n: SPILL_0_NORMAL
-tl0_s1n: SPILL_1_NORMAL
-tl0_s2n: SPILL_2_NORMAL
-tl0_s3n: SPILL_0_NORMAL_ETRAP
-tl0_s4n: SPILL_1_GENERIC_ETRAP
-tl0_s5n: SPILL_1_GENERIC_ETRAP_FIXUP
-tl0_s6n: SPILL_2_GENERIC_ETRAP
-tl0_s7n: SPILL_2_GENERIC_ETRAP_FIXUP
-tl0_s0o: SPILL_0_OTHER
-tl0_s1o: SPILL_1_OTHER
-tl0_s2o: SPILL_2_OTHER
-tl0_s3o: SPILL_3_OTHER
-tl0_s4o: SPILL_4_OTHER
-tl0_s5o: SPILL_5_OTHER
-tl0_s6o: SPILL_6_OTHER
-tl0_s7o: SPILL_7_OTHER
-tl0_f0n: FILL_0_NORMAL
-tl0_f1n: FILL_1_NORMAL
-tl0_f2n: FILL_2_NORMAL
-tl0_f3n: FILL_3_NORMAL
-tl0_f4n: FILL_4_NORMAL
-tl0_f5n: FILL_0_NORMAL_RTRAP
-tl0_f6n: FILL_1_GENERIC_RTRAP
-tl0_f7n: FILL_2_GENERIC_RTRAP
-tl0_f0o: FILL_0_OTHER
-tl0_f1o: FILL_1_OTHER
-tl0_f2o: FILL_2_OTHER
-tl0_f3o: FILL_3_OTHER
-tl0_f4o: FILL_4_OTHER
-tl0_f5o: FILL_5_OTHER
-tl0_f6o: FILL_6_OTHER
-tl0_f7o: FILL_7_OTHER
-tl0_resv100: BTRAP(0x100)
-tl0_bkpt: BREAKPOINT_TRAP
-tl0_divz: TRAP(do_div0)
-tl0_flushw: FLUSH_WINDOW_TRAP
-tl0_resv104: BTRAP(0x104) BTRAP(0x105) BTRAP(0x106) BTRAP(0x107) BTRAP(0x108)
-tl0_resv109: BTRAP(0x109) BTRAP(0x10a) BTRAP(0x10b) BTRAP(0x10c) BTRAP(0x10d)
-tl0_resv10e: BTRAP(0x10e) BTRAP(0x10f)
-tl0_linux32: LINUX_32BIT_SYSCALL_TRAP
-tl0_oldlinux64: LINUX_64BIT_SYSCALL_TRAP
-tl0_resv112: TRAP_UTRAP(UT_TRAP_INSTRUCTION_18,0x112) TRAP_UTRAP(UT_TRAP_INSTRUCTION_19,0x113)
-tl0_resv114: TRAP_UTRAP(UT_TRAP_INSTRUCTION_20,0x114) TRAP_UTRAP(UT_TRAP_INSTRUCTION_21,0x115)
-tl0_resv116: TRAP_UTRAP(UT_TRAP_INSTRUCTION_22,0x116) TRAP_UTRAP(UT_TRAP_INSTRUCTION_23,0x117)
-tl0_resv118: TRAP_UTRAP(UT_TRAP_INSTRUCTION_24,0x118) TRAP_UTRAP(UT_TRAP_INSTRUCTION_25,0x119)
-tl0_resv11a: TRAP_UTRAP(UT_TRAP_INSTRUCTION_26,0x11a) TRAP_UTRAP(UT_TRAP_INSTRUCTION_27,0x11b)
-tl0_resv11c: TRAP_UTRAP(UT_TRAP_INSTRUCTION_28,0x11c) TRAP_UTRAP(UT_TRAP_INSTRUCTION_29,0x11d)
-tl0_resv11e: TRAP_UTRAP(UT_TRAP_INSTRUCTION_30,0x11e) TRAP_UTRAP(UT_TRAP_INSTRUCTION_31,0x11f)
-tl0_getcc: GETCC_TRAP
-tl0_setcc: SETCC_TRAP
-tl0_getpsr: TRAP(do_getpsr)
-tl0_resv123: BTRAP(0x123) BTRAP(0x124) BTRAP(0x125) BTRAP(0x126) BTRAP(0x127)
-tl0_resv128: BTRAP(0x128) BTRAP(0x129) BTRAP(0x12a) BTRAP(0x12b) BTRAP(0x12c)
-tl0_resv12d: BTRAP(0x12d) BTRAP(0x12e) BTRAP(0x12f) BTRAP(0x130) BTRAP(0x131)
-tl0_resv132: BTRAP(0x132) BTRAP(0x133) BTRAP(0x134) BTRAP(0x135) BTRAP(0x136)
-tl0_resv137: BTRAP(0x137) BTRAP(0x138) BTRAP(0x139) BTRAP(0x13a) BTRAP(0x13b)
-tl0_resv13c: BTRAP(0x13c) BTRAP(0x13d) BTRAP(0x13e) BTRAP(0x13f) BTRAP(0x140)
-tl0_resv141: BTRAP(0x141) BTRAP(0x142) BTRAP(0x143) BTRAP(0x144) BTRAP(0x145)
-tl0_resv146: BTRAP(0x146) BTRAP(0x147) BTRAP(0x148) BTRAP(0x149) BTRAP(0x14a)
-tl0_resv14b: BTRAP(0x14b) BTRAP(0x14c) BTRAP(0x14d) BTRAP(0x14e) BTRAP(0x14f)
-tl0_resv150: BTRAP(0x150) BTRAP(0x151) BTRAP(0x152) BTRAP(0x153) BTRAP(0x154)
-tl0_resv155: BTRAP(0x155) BTRAP(0x156) BTRAP(0x157) BTRAP(0x158) BTRAP(0x159)
-tl0_resv15a: BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e)
-tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163)
-tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168)
-tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c)
-tl0_linux64: LINUX_64BIT_SYSCALL_TRAP
-tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context)
-tl0_resv170: KPROBES_TRAP(0x170) KPROBES_TRAP(0x171) KGDB_TRAP(0x172)
-tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177)
-tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c)
-tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f)
-#define BTRAPS(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) BTRAP(x+4) BTRAP(x+5) BTRAP(x+6) BTRAP(x+7)
-tl0_resv180: BTRAPS(0x180) BTRAPS(0x188)
-tl0_resv190: BTRAPS(0x190) BTRAPS(0x198)
-tl0_resv1a0: BTRAPS(0x1a0) BTRAPS(0x1a8)
-tl0_resv1b0: BTRAPS(0x1b0) BTRAPS(0x1b8)
-tl0_resv1c0: BTRAPS(0x1c0) BTRAPS(0x1c8)
-tl0_resv1d0: BTRAPS(0x1d0) BTRAPS(0x1d8)
-tl0_resv1e0: BTRAPS(0x1e0) BTRAPS(0x1e8)
-tl0_resv1f0: BTRAPS(0x1f0) BTRAPS(0x1f8)
-
-sparc64_ttable_tl1:
-tl1_resv000: BOOT_KERNEL BTRAPTL1(0x1) BTRAPTL1(0x2) BTRAPTL1(0x3)
-tl1_resv004: BTRAPTL1(0x4) BTRAPTL1(0x5) BTRAPTL1(0x6) BTRAPTL1(0x7)
-tl1_iax: TRAP_NOSAVE(__spitfire_insn_access_exception_tl1)
-tl1_itsb_4v: SUN4V_ITSB_MISS
-tl1_iae: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_access_error)
-tl1_resv00b: BTRAPTL1(0xb) BTRAPTL1(0xc) BTRAPTL1(0xd) BTRAPTL1(0xe) BTRAPTL1(0xf)
-tl1_ill: TRAPTL1(do_ill_tl1)
-tl1_privop: BTRAPTL1(0x11)
-tl1_resv012: BTRAPTL1(0x12) BTRAPTL1(0x13) BTRAPTL1(0x14) BTRAPTL1(0x15)
-tl1_resv016: BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19)
-tl1_resv01a: BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d)
-tl1_resv01e: BTRAPTL1(0x1e) BTRAPTL1(0x1f)
-tl1_fpdis: TRAP_NOSAVE(do_fpdis)
-tl1_fpieee: TRAPTL1(do_fpieee_tl1)
-tl1_fpother: TRAPTL1(do_fpother_tl1)
-tl1_tof: TRAPTL1(do_tof_tl1)
-tl1_cwin: CLEAN_WINDOW
-tl1_div0: TRAPTL1(do_div0_tl1)
-tl1_resv029: BTRAPTL1(0x29) BTRAPTL1(0x2a) BTRAPTL1(0x2b) BTRAPTL1(0x2c)
-tl1_resv02d: BTRAPTL1(0x2d) BTRAPTL1(0x2e) BTRAPTL1(0x2f)
-tl1_dax: TRAP_NOSAVE(__spitfire_data_access_exception_tl1)
-tl1_dtsb_4v: SUN4V_DTSB_MISS
-tl1_dae: membar #Sync
- TRAP_NOSAVE_7INSNS(__spitfire_access_error)
-tl1_resv033: BTRAPTL1(0x33)
-tl1_mna: TRAP_NOSAVE(do_mna)
-tl1_lddfmna: TRAPTL1(do_lddfmna_tl1)
-tl1_stdfmna: TRAPTL1(do_stdfmna_tl1)
-tl1_privact: BTRAPTL1(0x37)
-tl1_resv038: BTRAPTL1(0x38) BTRAPTL1(0x39) BTRAPTL1(0x3a) BTRAPTL1(0x3b)
-tl1_resv03c: BTRAPTL1(0x3c) BTRAPTL1(0x3d) BTRAPTL1(0x3e) BTRAPTL1(0x3f)
-tl1_resv040: BTRAPTL1(0x40)
-tl1_irq1: TRAP_IRQ(do_irq_tl1, 1) TRAP_IRQ(do_irq_tl1, 2) TRAP_IRQ(do_irq_tl1, 3)
-tl1_irq4: TRAP_IRQ(do_irq_tl1, 4) TRAP_IRQ(do_irq_tl1, 5) TRAP_IRQ(do_irq_tl1, 6)
-tl1_irq7: TRAP_IRQ(do_irq_tl1, 7) TRAP_IRQ(do_irq_tl1, 8) TRAP_IRQ(do_irq_tl1, 9)
-tl1_irq10: TRAP_IRQ(do_irq_tl1, 10) TRAP_IRQ(do_irq_tl1, 11)
-tl1_irq12: TRAP_IRQ(do_irq_tl1, 12) TRAP_IRQ(do_irq_tl1, 13)
-tl1_irq14: TRAP_IRQ(do_irq_tl1, 14) TRAP_IRQ(do_irq_tl1, 15)
-tl1_resv050: BTRAPTL1(0x50) BTRAPTL1(0x51) BTRAPTL1(0x52) BTRAPTL1(0x53)
-tl1_resv054: BTRAPTL1(0x54) BTRAPTL1(0x55) BTRAPTL1(0x56) BTRAPTL1(0x57)
-tl1_resv058: BTRAPTL1(0x58) BTRAPTL1(0x59) BTRAPTL1(0x5a) BTRAPTL1(0x5b)
-tl1_resv05c: BTRAPTL1(0x5c) BTRAPTL1(0x5d) BTRAPTL1(0x5e) BTRAPTL1(0x5f)
-tl1_ivec: TRAP_IVEC
-tl1_paw: TRAPTL1(do_paw_tl1)
-tl1_vaw: TRAPTL1(do_vaw_tl1)
-tl1_cee: BTRAPTL1(0x63)
-tl1_iamiss: BTRAPTL1(0x64) BTRAPTL1(0x65) BTRAPTL1(0x66) BTRAPTL1(0x67)
-tl1_damiss:
-#include "dtlb_miss.S"
-tl1_daprot:
-#include "dtlb_prot.S"
-tl1_fecc: BTRAPTL1(0x70) /* Fast-ECC on Cheetah */
-tl1_dcpe: BTRAPTL1(0x71) /* D-cache Parity Error on Cheetah+ */
-tl1_icpe: BTRAPTL1(0x72) /* I-cache Parity Error on Cheetah+ */
-tl1_resv073: BTRAPTL1(0x73)
-tl1_resv074: BTRAPTL1(0x74) BTRAPTL1(0x75) BTRAPTL1(0x76) BTRAPTL1(0x77)
-tl1_resv078: BTRAPTL1(0x78) BTRAPTL1(0x79) BTRAPTL1(0x7a) BTRAPTL1(0x7b)
-tl1_resv07c: BTRAPTL1(0x7c) BTRAPTL1(0x7d) BTRAPTL1(0x7e) BTRAPTL1(0x7f)
-tl1_s0n: SPILL_0_NORMAL
-tl1_s1n: SPILL_1_NORMAL
-tl1_s2n: SPILL_2_NORMAL
-tl1_s3n: SPILL_3_NORMAL
-tl1_s4n: SPILL_4_NORMAL
-tl1_s5n: SPILL_5_NORMAL
-tl1_s6n: SPILL_6_NORMAL
-tl1_s7n: SPILL_7_NORMAL
-tl1_s0o: SPILL_0_OTHER
-tl1_s1o: SPILL_1_OTHER
-tl1_s2o: SPILL_2_OTHER
-tl1_s3o: SPILL_3_OTHER
-tl1_s4o: SPILL_4_OTHER
-tl1_s5o: SPILL_5_OTHER
-tl1_s6o: SPILL_6_OTHER
-tl1_s7o: SPILL_7_OTHER
-tl1_f0n: FILL_0_NORMAL
-tl1_f1n: FILL_1_NORMAL
-tl1_f2n: FILL_2_NORMAL
-tl1_f3n: FILL_3_NORMAL
-tl1_f4n: FILL_4_NORMAL
-tl1_f5n: FILL_5_NORMAL
-tl1_f6n: FILL_6_NORMAL
-tl1_f7n: FILL_7_NORMAL
-tl1_f0o: FILL_0_OTHER
-tl1_f1o: FILL_1_OTHER
-tl1_f2o: FILL_2_OTHER
-tl1_f3o: FILL_3_OTHER
-tl1_f4o: FILL_4_OTHER
-tl1_f5o: FILL_5_OTHER
-tl1_f6o: FILL_6_OTHER
-tl1_f7o: FILL_7_OTHER
diff --git a/arch/sparc64/kernel/vmlinux.lds.S b/arch/sparc64/kernel/vmlinux.lds.S
deleted file mode 100644
index 01f809617e5e..000000000000
--- a/arch/sparc64/kernel/vmlinux.lds.S
+++ /dev/null
@@ -1,147 +0,0 @@
-/* ld script to make UltraLinux kernel */
-
-#include <asm/page.h>
-#include <asm-generic/vmlinux.lds.h>
-
-OUTPUT_FORMAT("elf64-sparc", "elf64-sparc", "elf64-sparc")
-OUTPUT_ARCH(sparc:v9a)
-ENTRY(_start)
-
-jiffies = jiffies_64;
-SECTIONS
-{
- swapper_low_pmd_dir = 0x0000000000402000;
- . = 0x4000;
- .text 0x0000000000404000 : {
- _text = .;
- TEXT_TEXT
- SCHED_TEXT
- LOCK_TEXT
- KPROBES_TEXT
- *(.gnu.warning)
- } = 0
- _etext = .;
- PROVIDE (etext = .);
-
- RO_DATA(PAGE_SIZE)
- .data : {
- DATA_DATA
- CONSTRUCTORS
- }
- .data1 : {
- *(.data1)
- }
- . = ALIGN(64);
- .data.cacheline_aligned : {
- *(.data.cacheline_aligned)
- }
- . = ALIGN(64);
- .data.read_mostly : {
- *(.data.read_mostly)
- }
- _edata = .;
- PROVIDE (edata = .);
- .fixup : {
- *(.fixup)
- }
- . = ALIGN(16);
- __ex_table : {
- __start___ex_table = .;
- *(__ex_table)
- __stop___ex_table = .;
- }
- NOTES
-
- . = ALIGN(PAGE_SIZE);
- .init.text : {
- __init_begin = .;
- _sinittext = .;
- INIT_TEXT
- _einittext = .;
- }
- .init.data : {
- INIT_DATA
- }
- . = ALIGN(16);
- .init.setup : {
- __setup_start = .;
- *(.init.setup)
- __setup_end = .;
- }
- .initcall.init : {
- __initcall_start = .;
- INITCALLS
- __initcall_end = .;
- }
- .con_initcall.init : {
- __con_initcall_start = .;
- *(.con_initcall.init)
- __con_initcall_end = .;
- }
- SECURITY_INIT
-
- . = ALIGN(4);
- .tsb_ldquad_phys_patch : {
- __tsb_ldquad_phys_patch = .;
- *(.tsb_ldquad_phys_patch)
- __tsb_ldquad_phys_patch_end = .;
- }
-
- .tsb_phys_patch : {
- __tsb_phys_patch = .;
- *(.tsb_phys_patch)
- __tsb_phys_patch_end = .;
- }
-
- .cpuid_patch : {
- __cpuid_patch = .;
- *(.cpuid_patch)
- __cpuid_patch_end = .;
- }
-
- .sun4v_1insn_patch : {
- __sun4v_1insn_patch = .;
- *(.sun4v_1insn_patch)
- __sun4v_1insn_patch_end = .;
- }
- .sun4v_2insn_patch : {
- __sun4v_2insn_patch = .;
- *(.sun4v_2insn_patch)
- __sun4v_2insn_patch_end = .;
- }
-
-#ifdef CONFIG_BLK_DEV_INITRD
- . = ALIGN(PAGE_SIZE);
- .init.ramfs : {
- __initramfs_start = .;
- *(.init.ramfs)
- __initramfs_end = .;
- }
-#endif
-
- PERCPU(PAGE_SIZE)
-
- . = ALIGN(PAGE_SIZE);
- __init_end = .;
- __bss_start = .;
- .sbss : {
- *(.sbss)
- *(.scommon)
- }
- .bss : {
- *(.dynbss)
- *(.bss)
- *(COMMON)
- }
- _end = . ;
- PROVIDE (end = .);
-
- /DISCARD/ : {
- EXIT_TEXT
- EXIT_DATA
- *(.exitcall.exit)
- }
-
- STABS_DEBUG
- DWARF_DEBUG
-}
diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile
deleted file mode 100644
index f095e13910bc..000000000000
--- a/arch/sparc64/lib/Makefile
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Makefile for Sparc64 library files..
-#
-
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS := -Werror
-
-lib-y := PeeCeeI.o copy_page.o clear_page.o strlen.o strncmp.o \
- memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \
- bzero.o csum_copy.o csum_copy_from_user.o csum_copy_to_user.o \
- VISsave.o atomic.o bitops.o \
- U1memcpy.o U1copy_from_user.o U1copy_to_user.o \
- U3memcpy.o U3copy_from_user.o U3copy_to_user.o U3patch.o \
- NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o NGpatch.o \
- NGpage.o NGbzero.o \
- NG2memcpy.o NG2copy_from_user.o NG2copy_to_user.o NG2patch.o \
- NG2page.o \
- GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o GENpatch.o \
- GENpage.o GENbzero.o \
- copy_in_user.o user_fixup.o memmove.o \
- mcount.o ipcsum.o rwsem.o xor.o
-
-obj-y += iomap.o
diff --git a/arch/sparc64/lib/atomic.S b/arch/sparc64/lib/atomic.S
deleted file mode 100644
index 70ac4186f62b..000000000000
--- a/arch/sparc64/lib/atomic.S
+++ /dev/null
@@ -1,164 +0,0 @@
-/* atomic.S: These things are too big to do inline.
- *
- * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/backoff.h>
-
- .text
-
- /* Two versions of the atomic routines, one that
- * does not return a value and does not perform
- * memory barriers, and a second which returns
- * a value and does the barriers.
- */
- .globl atomic_add
- .type atomic_add,#function
-atomic_add: /* %o0 = increment, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
-1: lduw [%o1], %g1
- add %g1, %o0, %g7
- cas [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic_add, .-atomic_add
-
- .globl atomic_sub
- .type atomic_sub,#function
-atomic_sub: /* %o0 = decrement, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
-1: lduw [%o1], %g1
- sub %g1, %o0, %g7
- cas [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic_sub, .-atomic_sub
-
- /* On SMP we need to use memory barriers to ensure
- * correct memory operation ordering, nop these out
- * for uniprocessor.
- */
-#ifdef CONFIG_SMP
-
-#define ATOMIC_PRE_BARRIER membar #StoreLoad | #LoadLoad;
-#define ATOMIC_POST_BARRIER \
- ba,pt %xcc, 80b; \
- membar #StoreLoad | #StoreStore
-
-80: retl
- nop
-#else
-#define ATOMIC_PRE_BARRIER
-#define ATOMIC_POST_BARRIER
-#endif
-
- .globl atomic_add_ret
- .type atomic_add_ret,#function
-atomic_add_ret: /* %o0 = increment, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
- ATOMIC_PRE_BARRIER
-1: lduw [%o1], %g1
- add %g1, %o0, %g7
- cas [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 2f
- add %g7, %o0, %g7
- sra %g7, 0, %o0
- ATOMIC_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic_add_ret, .-atomic_add_ret
-
- .globl atomic_sub_ret
- .type atomic_sub_ret,#function
-atomic_sub_ret: /* %o0 = decrement, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
- ATOMIC_PRE_BARRIER
-1: lduw [%o1], %g1
- sub %g1, %o0, %g7
- cas [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 2f
- sub %g7, %o0, %g7
- sra %g7, 0, %o0
- ATOMIC_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic_sub_ret, .-atomic_sub_ret
-
- .globl atomic64_add
- .type atomic64_add,#function
-atomic64_add: /* %o0 = increment, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
-1: ldx [%o1], %g1
- add %g1, %o0, %g7
- casx [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %xcc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic64_add, .-atomic64_add
-
- .globl atomic64_sub
- .type atomic64_sub,#function
-atomic64_sub: /* %o0 = decrement, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
-1: ldx [%o1], %g1
- sub %g1, %o0, %g7
- casx [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %xcc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic64_sub, .-atomic64_sub
-
- .globl atomic64_add_ret
- .type atomic64_add_ret,#function
-atomic64_add_ret: /* %o0 = increment, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
- ATOMIC_PRE_BARRIER
-1: ldx [%o1], %g1
- add %g1, %o0, %g7
- casx [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %xcc, 2f
- add %g7, %o0, %g7
- mov %g7, %o0
- ATOMIC_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic64_add_ret, .-atomic64_add_ret
-
- .globl atomic64_sub_ret
- .type atomic64_sub_ret,#function
-atomic64_sub_ret: /* %o0 = decrement, %o1 = atomic_ptr */
- BACKOFF_SETUP(%o2)
- ATOMIC_PRE_BARRIER
-1: ldx [%o1], %g1
- sub %g1, %o0, %g7
- casx [%o1], %g1, %g7
- cmp %g1, %g7
- bne,pn %xcc, 2f
- sub %g7, %o0, %g7
- mov %g7, %o0
- ATOMIC_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o2, %o3, 1b)
- .size atomic64_sub_ret, .-atomic64_sub_ret
diff --git a/arch/sparc64/lib/bitops.S b/arch/sparc64/lib/bitops.S
deleted file mode 100644
index 6b015a6eefb5..000000000000
--- a/arch/sparc64/lib/bitops.S
+++ /dev/null
@@ -1,165 +0,0 @@
-/* bitops.S: Sparc64 atomic bit operations.
- *
- * Copyright (C) 2000, 2007 David S. Miller (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/backoff.h>
-
- .text
-
- /* On SMP we need to use memory barriers to ensure
- * correct memory operation ordering, nop these out
- * for uniprocessor.
- */
-
-#ifdef CONFIG_SMP
-#define BITOP_PRE_BARRIER membar #StoreLoad | #LoadLoad
-#define BITOP_POST_BARRIER \
- ba,pt %xcc, 80b; \
- membar #StoreLoad | #StoreStore
-
-80: retl
- nop
-#else
-#define BITOP_PRE_BARRIER
-#define BITOP_POST_BARRIER
-#endif
-
- .globl test_and_set_bit
- .type test_and_set_bit,#function
-test_and_set_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- BITOP_PRE_BARRIER
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- or %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- and %g7, %o2, %g2
- clr %o0
- movrne %g2, 1, %o0
- BITOP_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size test_and_set_bit, .-test_and_set_bit
-
- .globl test_and_clear_bit
- .type test_and_clear_bit,#function
-test_and_clear_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- BITOP_PRE_BARRIER
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- andn %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- and %g7, %o2, %g2
- clr %o0
- movrne %g2, 1, %o0
- BITOP_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size test_and_clear_bit, .-test_and_clear_bit
-
- .globl test_and_change_bit
- .type test_and_change_bit,#function
-test_and_change_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- BITOP_PRE_BARRIER
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- xor %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- and %g7, %o2, %g2
- clr %o0
- movrne %g2, 1, %o0
- BITOP_POST_BARRIER
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size test_and_change_bit, .-test_and_change_bit
-
- .globl set_bit
- .type set_bit,#function
-set_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- or %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size set_bit, .-set_bit
-
- .globl clear_bit
- .type clear_bit,#function
-clear_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- andn %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size clear_bit, .-clear_bit
-
- .globl change_bit
- .type change_bit,#function
-change_bit: /* %o0=nr, %o1=addr */
- BACKOFF_SETUP(%o3)
- srlx %o0, 6, %g1
- mov 1, %o2
- sllx %g1, 3, %g3
- and %o0, 63, %g2
- sllx %o2, %g2, %o2
- add %o1, %g3, %o1
-1: ldx [%o1], %g7
- xor %g7, %o2, %g1
- casx [%o1], %g7, %g1
- cmp %g7, %g1
- bne,pn %xcc, 2f
- nop
- retl
- nop
-2: BACKOFF_SPIN(%o3, %o4, 1b)
- .size change_bit, .-change_bit
diff --git a/arch/sparc64/lib/iomap.c b/arch/sparc64/lib/iomap.c
deleted file mode 100644
index 7120ebbd4d03..000000000000
--- a/arch/sparc64/lib/iomap.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Implement the sparc64 iomap interfaces
- */
-#include <linux/pci.h>
-#include <linux/module.h>
-#include <asm/io.h>
-
-/* Create a virtual mapping cookie for an IO port range */
-void __iomem *ioport_map(unsigned long port, unsigned int nr)
-{
- return (void __iomem *) (unsigned long) port;
-}
-
-void ioport_unmap(void __iomem *addr)
-{
- /* Nothing to do */
-}
-EXPORT_SYMBOL(ioport_map);
-EXPORT_SYMBOL(ioport_unmap);
-
-/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
-void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
-{
- resource_size_t start = pci_resource_start(dev, bar);
- resource_size_t len = pci_resource_len(dev, bar);
- unsigned long flags = pci_resource_flags(dev, bar);
-
- if (!len || !start)
- return NULL;
- if (maxlen && len > maxlen)
- len = maxlen;
- if (flags & IORESOURCE_IO)
- return ioport_map(start, len);
- if (flags & IORESOURCE_MEM) {
- if (flags & IORESOURCE_CACHEABLE)
- return ioremap(start, len);
- return ioremap_nocache(start, len);
- }
- /* What? */
- return NULL;
-}
-
-void pci_iounmap(struct pci_dev *dev, void __iomem * addr)
-{
- /* nothing to do */
-}
-EXPORT_SYMBOL(pci_iomap);
-EXPORT_SYMBOL(pci_iounmap);
diff --git a/arch/sparc64/lib/memcmp.S b/arch/sparc64/lib/memcmp.S
deleted file mode 100644
index d3fdaa898566..000000000000
--- a/arch/sparc64/lib/memcmp.S
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Sparc64 optimized memcmp code.
- *
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 2000 David S. Miller (davem@redhat.com)
- */
-
- .text
- .align 32
- .globl __memcmp, memcmp
-__memcmp:
-memcmp:
- cmp %o2, 0 ! IEU1 Group
-loop: be,pn %icc, ret_0 ! CTI
- nop ! IEU0
- ldub [%o0], %g7 ! LSU Group
- ldub [%o1], %g3 ! LSU Group
- sub %o2, 1, %o2 ! IEU0
- add %o0, 1, %o0 ! IEU1
- add %o1, 1, %o1 ! IEU0 Group
- subcc %g7, %g3, %g3 ! IEU1 Group
- be,pt %icc, loop ! CTI
- cmp %o2, 0 ! IEU1 Group
-
-ret_n0: retl
- mov %g3, %o0
-ret_0: retl
- mov 0, %o0
diff --git a/arch/sparc64/lib/rwsem.S b/arch/sparc64/lib/rwsem.S
deleted file mode 100644
index 1a4cc5654de4..000000000000
--- a/arch/sparc64/lib/rwsem.S
+++ /dev/null
@@ -1,170 +0,0 @@
-/* rwsem.S: RW semaphore assembler.
- *
- * Written by David S. Miller (davem@redhat.com), 2001.
- * Derived from asm-i386/rwsem.h
- */
-
-#include <asm/rwsem-const.h>
-
- .section .sched.text, "ax"
-
- .globl __down_read
-__down_read:
-1: lduw [%o0], %g1
- add %g1, 1, %g7
- cas [%o0], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 1b
- add %g7, 1, %g7
- cmp %g7, 0
- membar #StoreLoad | #StoreStore
- bl,pn %icc, 3f
- nop
-2:
- retl
- nop
-3:
- save %sp, -192, %sp
- call rwsem_down_read_failed
- mov %i0, %o0
- ret
- restore
- .size __down_read, .-__down_read
-
- .globl __down_read_trylock
-__down_read_trylock:
-1: lduw [%o0], %g1
- add %g1, 1, %g7
- cmp %g7, 0
- bl,pn %icc, 2f
- mov 0, %o1
- cas [%o0], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 1b
- mov 1, %o1
- membar #StoreLoad | #StoreStore
-2: retl
- mov %o1, %o0
- .size __down_read_trylock, .-__down_read_trylock
-
- .globl __down_write
-__down_write:
- sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
- or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
-1:
- lduw [%o0], %g3
- add %g3, %g1, %g7
- cas [%o0], %g3, %g7
- cmp %g3, %g7
- bne,pn %icc, 1b
- cmp %g7, 0
- membar #StoreLoad | #StoreStore
- bne,pn %icc, 3f
- nop
-2: retl
- nop
-3:
- save %sp, -192, %sp
- call rwsem_down_write_failed
- mov %i0, %o0
- ret
- restore
- .size __down_write, .-__down_write
-
- .globl __down_write_trylock
-__down_write_trylock:
- sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
- or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
-1:
- lduw [%o0], %g3
- cmp %g3, 0
- bne,pn %icc, 2f
- mov 0, %o1
- add %g3, %g1, %g7
- cas [%o0], %g3, %g7
- cmp %g3, %g7
- bne,pn %icc, 1b
- mov 1, %o1
- membar #StoreLoad | #StoreStore
-2: retl
- mov %o1, %o0
- .size __down_write_trylock, .-__down_write_trylock
-
- .globl __up_read
-__up_read:
-1:
- lduw [%o0], %g1
- sub %g1, 1, %g7
- cas [%o0], %g1, %g7
- cmp %g1, %g7
- bne,pn %icc, 1b
- cmp %g7, 0
- membar #StoreLoad | #StoreStore
- bl,pn %icc, 3f
- nop
-2: retl
- nop
-3: sethi %hi(RWSEM_ACTIVE_MASK), %g1
- sub %g7, 1, %g7
- or %g1, %lo(RWSEM_ACTIVE_MASK), %g1
- andcc %g7, %g1, %g0
- bne,pn %icc, 2b
- nop
- save %sp, -192, %sp
- call rwsem_wake
- mov %i0, %o0
- ret
- restore
- .size __up_read, .-__up_read
-
- .globl __up_write
-__up_write:
- sethi %hi(RWSEM_ACTIVE_WRITE_BIAS), %g1
- or %g1, %lo(RWSEM_ACTIVE_WRITE_BIAS), %g1
-1:
- lduw [%o0], %g3
- sub %g3, %g1, %g7
- cas [%o0], %g3, %g7
- cmp %g3, %g7
- bne,pn %icc, 1b
- sub %g7, %g1, %g7
- cmp %g7, 0
- membar #StoreLoad | #StoreStore
- bl,pn %icc, 3f
- nop
-2:
- retl
- nop
-3:
- save %sp, -192, %sp
- call rwsem_wake
- mov %i0, %o0
- ret
- restore
- .size __up_write, .-__up_write
-
- .globl __downgrade_write
-__downgrade_write:
- sethi %hi(RWSEM_WAITING_BIAS), %g1
- or %g1, %lo(RWSEM_WAITING_BIAS), %g1
-1:
- lduw [%o0], %g3
- sub %g3, %g1, %g7
- cas [%o0], %g3, %g7
- cmp %g3, %g7
- bne,pn %icc, 1b
- sub %g7, %g1, %g7
- cmp %g7, 0
- membar #StoreLoad | #StoreStore
- bl,pn %icc, 3f
- nop
-2:
- retl
- nop
-3:
- save %sp, -192, %sp
- call rwsem_downgrade_wake
- mov %i0, %o0
- ret
- restore
- .size __downgrade_write, .-__downgrade_write
diff --git a/arch/sparc64/lib/strlen.S b/arch/sparc64/lib/strlen.S
deleted file mode 100644
index e9ba1920d818..000000000000
--- a/arch/sparc64/lib/strlen.S
+++ /dev/null
@@ -1,80 +0,0 @@
-/* strlen.S: Sparc64 optimized strlen code
- * Hand optimized from GNU libc's strlen
- * Copyright (C) 1991,1996 Free Software Foundation
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#define LO_MAGIC 0x01010101
-#define HI_MAGIC 0x80808080
-
- .align 32
- .globl strlen
- .type strlen,#function
-strlen:
- mov %o0, %o1
- andcc %o0, 3, %g0
- be,pt %icc, 9f
- sethi %hi(HI_MAGIC), %o4
- ldub [%o0], %o5
- brz,pn %o5, 11f
- add %o0, 1, %o0
- andcc %o0, 3, %g0
- be,pn %icc, 4f
- or %o4, %lo(HI_MAGIC), %o3
- ldub [%o0], %o5
- brz,pn %o5, 12f
- add %o0, 1, %o0
- andcc %o0, 3, %g0
- be,pt %icc, 5f
- sethi %hi(LO_MAGIC), %o4
- ldub [%o0], %o5
- brz,pn %o5, 13f
- add %o0, 1, %o0
- ba,pt %icc, 8f
- or %o4, %lo(LO_MAGIC), %o2
-9:
- or %o4, %lo(HI_MAGIC), %o3
-4:
- sethi %hi(LO_MAGIC), %o4
-5:
- or %o4, %lo(LO_MAGIC), %o2
-8:
- ld [%o0], %o5
-2:
- sub %o5, %o2, %o4
- andcc %o4, %o3, %g0
- be,pt %icc, 8b
- add %o0, 4, %o0
-
- /* Check every byte. */
- srl %o5, 24, %g7
- andcc %g7, 0xff, %g0
- be,pn %icc, 1f
- add %o0, -4, %o4
- srl %o5, 16, %g7
- andcc %g7, 0xff, %g0
- be,pn %icc, 1f
- add %o4, 1, %o4
- srl %o5, 8, %g7
- andcc %g7, 0xff, %g0
- be,pn %icc, 1f
- add %o4, 1, %o4
- andcc %o5, 0xff, %g0
- bne,a,pt %icc, 2b
- ld [%o0], %o5
- add %o4, 1, %o4
-1:
- retl
- sub %o4, %o1, %o0
-11:
- retl
- mov 0, %o0
-12:
- retl
- mov 1, %o0
-13:
- retl
- mov 2, %o0
-
- .size strlen, .-strlen
diff --git a/arch/sparc64/math-emu/Makefile b/arch/sparc64/math-emu/Makefile
deleted file mode 100644
index cc5cb9baf6aa..000000000000
--- a/arch/sparc64/math-emu/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the FPU instruction emulation.
-#
-
-obj-y := math.o
-
-EXTRA_CFLAGS = -Iinclude/math-emu -w
diff --git a/arch/sparc64/math-emu/math.c b/arch/sparc64/math-emu/math.c
deleted file mode 100644
index add053e0f3b3..000000000000
--- a/arch/sparc64/math-emu/math.c
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * arch/sparc64/math-emu/math.c
- *
- * Copyright (C) 1997,1999 Jakub Jelinek (jj@ultra.linux.cz)
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
- *
- * Emulation routines originate from soft-fp package, which is part
- * of glibc and has appropriate copyrights in it.
- */
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-
-#include <asm/fpumacro.h>
-#include <asm/ptrace.h>
-#include <asm/uaccess.h>
-
-#include "sfp-util.h"
-#include <math-emu/soft-fp.h>
-#include <math-emu/single.h>
-#include <math-emu/double.h>
-#include <math-emu/quad.h>
-
-/* QUAD - ftt == 3 */
-#define FMOVQ 0x003
-#define FNEGQ 0x007
-#define FABSQ 0x00b
-#define FSQRTQ 0x02b
-#define FADDQ 0x043
-#define FSUBQ 0x047
-#define FMULQ 0x04b
-#define FDIVQ 0x04f
-#define FDMULQ 0x06e
-#define FQTOX 0x083
-#define FXTOQ 0x08c
-#define FQTOS 0x0c7
-#define FQTOD 0x0cb
-#define FITOQ 0x0cc
-#define FSTOQ 0x0cd
-#define FDTOQ 0x0ce
-#define FQTOI 0x0d3
-/* SUBNORMAL - ftt == 2 */
-#define FSQRTS 0x029
-#define FSQRTD 0x02a
-#define FADDS 0x041
-#define FADDD 0x042
-#define FSUBS 0x045
-#define FSUBD 0x046
-#define FMULS 0x049
-#define FMULD 0x04a
-#define FDIVS 0x04d
-#define FDIVD 0x04e
-#define FSMULD 0x069
-#define FSTOX 0x081
-#define FDTOX 0x082
-#define FDTOS 0x0c6
-#define FSTOD 0x0c9
-#define FSTOI 0x0d1
-#define FDTOI 0x0d2
-#define FXTOS 0x084 /* Only Ultra-III generates this. */
-#define FXTOD 0x088 /* Only Ultra-III generates this. */
-#if 0 /* Optimized inline in sparc64/kernel/entry.S */
-#define FITOS 0x0c4 /* Only Ultra-III generates this. */
-#endif
-#define FITOD 0x0c8 /* Only Ultra-III generates this. */
-/* FPOP2 */
-#define FCMPQ 0x053
-#define FCMPEQ 0x057
-#define FMOVQ0 0x003
-#define FMOVQ1 0x043
-#define FMOVQ2 0x083
-#define FMOVQ3 0x0c3
-#define FMOVQI 0x103
-#define FMOVQX 0x183
-#define FMOVQZ 0x027
-#define FMOVQLE 0x047
-#define FMOVQLZ 0x067
-#define FMOVQNZ 0x0a7
-#define FMOVQGZ 0x0c7
-#define FMOVQGE 0x0e7
-
-#define FSR_TEM_SHIFT 23UL
-#define FSR_TEM_MASK (0x1fUL << FSR_TEM_SHIFT)
-#define FSR_AEXC_SHIFT 5UL
-#define FSR_AEXC_MASK (0x1fUL << FSR_AEXC_SHIFT)
-#define FSR_CEXC_SHIFT 0UL
-#define FSR_CEXC_MASK (0x1fUL << FSR_CEXC_SHIFT)
-
-/* All routines returning an exception to raise should detect
- * such exceptions _before_ rounding to be consistent with
- * the behavior of the hardware in the implemented cases
- * (and thus with the recommendations in the V9 architecture
- * manual).
- *
- * We return 0 if a SIGFPE should be sent, 1 otherwise.
- */
-static inline int record_exception(struct pt_regs *regs, int eflag)
-{
- u64 fsr = current_thread_info()->xfsr[0];
- int would_trap;
-
- /* Determine if this exception would have generated a trap. */
- would_trap = (fsr & ((long)eflag << FSR_TEM_SHIFT)) != 0UL;
-
- /* If trapping, we only want to signal one bit. */
- if(would_trap != 0) {
- eflag &= ((fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT);
- if((eflag & (eflag - 1)) != 0) {
- if(eflag & FP_EX_INVALID)
- eflag = FP_EX_INVALID;
- else if(eflag & FP_EX_OVERFLOW)
- eflag = FP_EX_OVERFLOW;
- else if(eflag & FP_EX_UNDERFLOW)
- eflag = FP_EX_UNDERFLOW;
- else if(eflag & FP_EX_DIVZERO)
- eflag = FP_EX_DIVZERO;
- else if(eflag & FP_EX_INEXACT)
- eflag = FP_EX_INEXACT;
- }
- }
-
- /* Set CEXC, here is the rule:
- *
- * In general all FPU ops will set one and only one
- * bit in the CEXC field, this is always the case
- * when the IEEE exception trap is enabled in TEM.
- */
- fsr &= ~(FSR_CEXC_MASK);
- fsr |= ((long)eflag << FSR_CEXC_SHIFT);
-
- /* Set the AEXC field, rule is:
- *
- * If a trap would not be generated, the
- * CEXC just generated is OR'd into the
- * existing value of AEXC.
- */
- if(would_trap == 0)
- fsr |= ((long)eflag << FSR_AEXC_SHIFT);
-
- /* If trapping, indicate fault trap type IEEE. */
- if(would_trap != 0)
- fsr |= (1UL << 14);
-
- current_thread_info()->xfsr[0] = fsr;
-
- /* If we will not trap, advance the program counter over
- * the instruction being handled.
- */
- if(would_trap == 0) {
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- }
-
- return (would_trap ? 0 : 1);
-}
-
-typedef union {
- u32 s;
- u64 d;
- u64 q[2];
-} *argp;
-
-int do_mathemu(struct pt_regs *regs, struct fpustate *f)
-{
- unsigned long pc = regs->tpc;
- unsigned long tstate = regs->tstate;
- u32 insn = 0;
- int type = 0;
- /* ftt tells which ftt it may happen in, r is rd, b is rs2 and a is rs1. The *u arg tells
- whether the argument should be packed/unpacked (0 - do not unpack/pack, 1 - unpack/pack)
- non-u args tells the size of the argument (0 - no argument, 1 - single, 2 - double, 3 - quad */
-#define TYPE(ftt, r, ru, b, bu, a, au) type = (au << 2) | (a << 0) | (bu << 5) | (b << 3) | (ru << 8) | (r << 6) | (ftt << 9)
- int freg;
- static u64 zero[2] = { 0L, 0L };
- int flags;
- FP_DECL_EX;
- FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
- FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
- FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
- int IR;
- long XR, xfsr;
-
- if (tstate & TSTATE_PRIV)
- die_if_kernel("unfinished/unimplemented FPop from kernel", regs);
- if (test_thread_flag(TIF_32BIT))
- pc = (u32)pc;
- if (get_user(insn, (u32 __user *) pc) != -EFAULT) {
- if ((insn & 0xc1f80000) == 0x81a00000) /* FPOP1 */ {
- switch ((insn >> 5) & 0x1ff) {
- /* QUAD - ftt == 3 */
- case FMOVQ:
- case FNEGQ:
- case FABSQ: TYPE(3,3,0,3,0,0,0); break;
- case FSQRTQ: TYPE(3,3,1,3,1,0,0); break;
- case FADDQ:
- case FSUBQ:
- case FMULQ:
- case FDIVQ: TYPE(3,3,1,3,1,3,1); break;
- case FDMULQ: TYPE(3,3,1,2,1,2,1); break;
- case FQTOX: TYPE(3,2,0,3,1,0,0); break;
- case FXTOQ: TYPE(3,3,1,2,0,0,0); break;
- case FQTOS: TYPE(3,1,1,3,1,0,0); break;
- case FQTOD: TYPE(3,2,1,3,1,0,0); break;
- case FITOQ: TYPE(3,3,1,1,0,0,0); break;
- case FSTOQ: TYPE(3,3,1,1,1,0,0); break;
- case FDTOQ: TYPE(3,3,1,2,1,0,0); break;
- case FQTOI: TYPE(3,1,0,3,1,0,0); break;
-
- /* We can get either unimplemented or unfinished
- * for these cases. Pre-Niagara systems generate
- * unfinished fpop for SUBNORMAL cases, and Niagara
- * always gives unimplemented fpop for fsqrt{s,d}.
- */
- case FSQRTS: {
- unsigned long x = current_thread_info()->xfsr[0];
-
- x = (x >> 14) & 0xf;
- TYPE(x,1,1,1,1,0,0);
- break;
- }
-
- case FSQRTD: {
- unsigned long x = current_thread_info()->xfsr[0];
-
- x = (x >> 14) & 0xf;
- TYPE(x,2,1,2,1,0,0);
- break;
- }
-
- /* SUBNORMAL - ftt == 2 */
- case FADDD:
- case FSUBD:
- case FMULD:
- case FDIVD: TYPE(2,2,1,2,1,2,1); break;
- case FADDS:
- case FSUBS:
- case FMULS:
- case FDIVS: TYPE(2,1,1,1,1,1,1); break;
- case FSMULD: TYPE(2,2,1,1,1,1,1); break;
- case FSTOX: TYPE(2,2,0,1,1,0,0); break;
- case FDTOX: TYPE(2,2,0,2,1,0,0); break;
- case FDTOS: TYPE(2,1,1,2,1,0,0); break;
- case FSTOD: TYPE(2,2,1,1,1,0,0); break;
- case FSTOI: TYPE(2,1,0,1,1,0,0); break;
- case FDTOI: TYPE(2,1,0,2,1,0,0); break;
-
- /* Only Ultra-III generates these */
- case FXTOS: TYPE(2,1,1,2,0,0,0); break;
- case FXTOD: TYPE(2,2,1,2,0,0,0); break;
-#if 0 /* Optimized inline in sparc64/kernel/entry.S */
- case FITOS: TYPE(2,1,1,1,0,0,0); break;
-#endif
- case FITOD: TYPE(2,2,1,1,0,0,0); break;
- }
- }
- else if ((insn & 0xc1f80000) == 0x81a80000) /* FPOP2 */ {
- IR = 2;
- switch ((insn >> 5) & 0x1ff) {
- case FCMPQ: TYPE(3,0,0,3,1,3,1); break;
- case FCMPEQ: TYPE(3,0,0,3,1,3,1); break;
- /* Now the conditional fmovq support */
- case FMOVQ0:
- case FMOVQ1:
- case FMOVQ2:
- case FMOVQ3:
- /* fmovq %fccX, %fY, %fZ */
- if (!((insn >> 11) & 3))
- XR = current_thread_info()->xfsr[0] >> 10;
- else
- XR = current_thread_info()->xfsr[0] >> (30 + ((insn >> 10) & 0x6));
- XR &= 3;
- IR = 0;
- switch ((insn >> 14) & 0x7) {
- /* case 0: IR = 0; break; */ /* Never */
- case 1: if (XR) IR = 1; break; /* Not Equal */
- case 2: if (XR == 1 || XR == 2) IR = 1; break; /* Less or Greater */
- case 3: if (XR & 1) IR = 1; break; /* Unordered or Less */
- case 4: if (XR == 1) IR = 1; break; /* Less */
- case 5: if (XR & 2) IR = 1; break; /* Unordered or Greater */
- case 6: if (XR == 2) IR = 1; break; /* Greater */
- case 7: if (XR == 3) IR = 1; break; /* Unordered */
- }
- if ((insn >> 14) & 8)
- IR ^= 1;
- break;
- case FMOVQI:
- case FMOVQX:
- /* fmovq %[ix]cc, %fY, %fZ */
- XR = regs->tstate >> 32;
- if ((insn >> 5) & 0x80)
- XR >>= 4;
- XR &= 0xf;
- IR = 0;
- freg = ((XR >> 2) ^ XR) & 2;
- switch ((insn >> 14) & 0x7) {
- /* case 0: IR = 0; break; */ /* Never */
- case 1: if (XR & 4) IR = 1; break; /* Equal */
- case 2: if ((XR & 4) || freg) IR = 1; break; /* Less or Equal */
- case 3: if (freg) IR = 1; break; /* Less */
- case 4: if (XR & 5) IR = 1; break; /* Less or Equal Unsigned */
- case 5: if (XR & 1) IR = 1; break; /* Carry Set */
- case 6: if (XR & 8) IR = 1; break; /* Negative */
- case 7: if (XR & 2) IR = 1; break; /* Overflow Set */
- }
- if ((insn >> 14) & 8)
- IR ^= 1;
- break;
- case FMOVQZ:
- case FMOVQLE:
- case FMOVQLZ:
- case FMOVQNZ:
- case FMOVQGZ:
- case FMOVQGE:
- freg = (insn >> 14) & 0x1f;
- if (!freg)
- XR = 0;
- else if (freg < 16)
- XR = regs->u_regs[freg];
- else if (test_thread_flag(TIF_32BIT)) {
- struct reg_window32 __user *win32;
- flushw_user ();
- win32 = (struct reg_window32 __user *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
- get_user(XR, &win32->locals[freg - 16]);
- } else {
- struct reg_window __user *win;
- flushw_user ();
- win = (struct reg_window __user *)(regs->u_regs[UREG_FP] + STACK_BIAS);
- get_user(XR, &win->locals[freg - 16]);
- }
- IR = 0;
- switch ((insn >> 10) & 3) {
- case 1: if (!XR) IR = 1; break; /* Register Zero */
- case 2: if (XR <= 0) IR = 1; break; /* Register Less Than or Equal to Zero */
- case 3: if (XR < 0) IR = 1; break; /* Register Less Than Zero */
- }
- if ((insn >> 10) & 4)
- IR ^= 1;
- break;
- }
- if (IR == 0) {
- /* The fmov test was false. Do a nop instead */
- current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- return 1;
- } else if (IR == 1) {
- /* Change the instruction into plain fmovq */
- insn = (insn & 0x3e00001f) | 0x81a00060;
- TYPE(3,3,0,3,0,0,0);
- }
- }
- }
- if (type) {
- argp rs1 = NULL, rs2 = NULL, rd = NULL;
-
- freg = (current_thread_info()->xfsr[0] >> 14) & 0xf;
- if (freg != (type >> 9))
- goto err;
- current_thread_info()->xfsr[0] &= ~0x1c000;
- freg = ((insn >> 14) & 0x1f);
- switch (type & 0x3) {
- case 3: if (freg & 2) {
- current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
- goto err;
- }
- case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
- case 1: rs1 = (argp)&f->regs[freg];
- flags = (freg < 32) ? FPRS_DL : FPRS_DU;
- if (!(current_thread_info()->fpsaved[0] & flags))
- rs1 = (argp)&zero;
- break;
- }
- switch (type & 0x7) {
- case 7: FP_UNPACK_QP (QA, rs1); break;
- case 6: FP_UNPACK_DP (DA, rs1); break;
- case 5: FP_UNPACK_SP (SA, rs1); break;
- }
- freg = (insn & 0x1f);
- switch ((type >> 3) & 0x3) {
- case 3: if (freg & 2) {
- current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
- goto err;
- }
- case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
- case 1: rs2 = (argp)&f->regs[freg];
- flags = (freg < 32) ? FPRS_DL : FPRS_DU;
- if (!(current_thread_info()->fpsaved[0] & flags))
- rs2 = (argp)&zero;
- break;
- }
- switch ((type >> 3) & 0x7) {
- case 7: FP_UNPACK_QP (QB, rs2); break;
- case 6: FP_UNPACK_DP (DB, rs2); break;
- case 5: FP_UNPACK_SP (SB, rs2); break;
- }
- freg = ((insn >> 25) & 0x1f);
- switch ((type >> 6) & 0x3) {
- case 3: if (freg & 2) {
- current_thread_info()->xfsr[0] |= (6 << 14) /* invalid_fp_register */;
- goto err;
- }
- case 2: freg = ((freg & 1) << 5) | (freg & 0x1e);
- case 1: rd = (argp)&f->regs[freg];
- flags = (freg < 32) ? FPRS_DL : FPRS_DU;
- if (!(current_thread_info()->fpsaved[0] & FPRS_FEF)) {
- current_thread_info()->fpsaved[0] = FPRS_FEF;
- current_thread_info()->gsr[0] = 0;
- }
- if (!(current_thread_info()->fpsaved[0] & flags)) {
- if (freg < 32)
- memset(f->regs, 0, 32*sizeof(u32));
- else
- memset(f->regs+32, 0, 32*sizeof(u32));
- }
- current_thread_info()->fpsaved[0] |= flags;
- break;
- }
- switch ((insn >> 5) & 0x1ff) {
- /* + */
- case FADDS: FP_ADD_S (SR, SA, SB); break;
- case FADDD: FP_ADD_D (DR, DA, DB); break;
- case FADDQ: FP_ADD_Q (QR, QA, QB); break;
- /* - */
- case FSUBS: FP_SUB_S (SR, SA, SB); break;
- case FSUBD: FP_SUB_D (DR, DA, DB); break;
- case FSUBQ: FP_SUB_Q (QR, QA, QB); break;
- /* * */
- case FMULS: FP_MUL_S (SR, SA, SB); break;
- case FSMULD: FP_CONV (D, S, 1, 1, DA, SA);
- FP_CONV (D, S, 1, 1, DB, SB);
- case FMULD: FP_MUL_D (DR, DA, DB); break;
- case FDMULQ: FP_CONV (Q, D, 2, 1, QA, DA);
- FP_CONV (Q, D, 2, 1, QB, DB);
- case FMULQ: FP_MUL_Q (QR, QA, QB); break;
- /* / */
- case FDIVS: FP_DIV_S (SR, SA, SB); break;
- case FDIVD: FP_DIV_D (DR, DA, DB); break;
- case FDIVQ: FP_DIV_Q (QR, QA, QB); break;
- /* sqrt */
- case FSQRTS: FP_SQRT_S (SR, SB); break;
- case FSQRTD: FP_SQRT_D (DR, DB); break;
- case FSQRTQ: FP_SQRT_Q (QR, QB); break;
- /* mov */
- case FMOVQ: rd->q[0] = rs2->q[0]; rd->q[1] = rs2->q[1]; break;
- case FABSQ: rd->q[0] = rs2->q[0] & 0x7fffffffffffffffUL; rd->q[1] = rs2->q[1]; break;
- case FNEGQ: rd->q[0] = rs2->q[0] ^ 0x8000000000000000UL; rd->q[1] = rs2->q[1]; break;
- /* float to int */
- case FSTOI: FP_TO_INT_S (IR, SB, 32, 1); break;
- case FDTOI: FP_TO_INT_D (IR, DB, 32, 1); break;
- case FQTOI: FP_TO_INT_Q (IR, QB, 32, 1); break;
- case FSTOX: FP_TO_INT_S (XR, SB, 64, 1); break;
- case FDTOX: FP_TO_INT_D (XR, DB, 64, 1); break;
- case FQTOX: FP_TO_INT_Q (XR, QB, 64, 1); break;
- /* int to float */
- case FITOQ: IR = rs2->s; FP_FROM_INT_Q (QR, IR, 32, int); break;
- case FXTOQ: XR = rs2->d; FP_FROM_INT_Q (QR, XR, 64, long); break;
- /* Only Ultra-III generates these */
- case FXTOS: XR = rs2->d; FP_FROM_INT_S (SR, XR, 64, long); break;
- case FXTOD: XR = rs2->d; FP_FROM_INT_D (DR, XR, 64, long); break;
-#if 0 /* Optimized inline in sparc64/kernel/entry.S */
- case FITOS: IR = rs2->s; FP_FROM_INT_S (SR, IR, 32, int); break;
-#endif
- case FITOD: IR = rs2->s; FP_FROM_INT_D (DR, IR, 32, int); break;
- /* float to float */
- case FSTOD: FP_CONV (D, S, 1, 1, DR, SB); break;
- case FSTOQ: FP_CONV (Q, S, 2, 1, QR, SB); break;
- case FDTOQ: FP_CONV (Q, D, 2, 1, QR, DB); break;
- case FDTOS: FP_CONV (S, D, 1, 1, SR, DB); break;
- case FQTOS: FP_CONV (S, Q, 1, 2, SR, QB); break;
- case FQTOD: FP_CONV (D, Q, 1, 2, DR, QB); break;
- /* comparison */
- case FCMPQ:
- case FCMPEQ:
- FP_CMP_Q(XR, QB, QA, 3);
- if (XR == 3 &&
- (((insn >> 5) & 0x1ff) == FCMPEQ ||
- FP_ISSIGNAN_Q(QA) ||
- FP_ISSIGNAN_Q(QB)))
- FP_SET_EXCEPTION (FP_EX_INVALID);
- }
- if (!FP_INHIBIT_RESULTS) {
- switch ((type >> 6) & 0x7) {
- case 0: xfsr = current_thread_info()->xfsr[0];
- if (XR == -1) XR = 2;
- switch (freg & 3) {
- /* fcc0, 1, 2, 3 */
- case 0: xfsr &= ~0xc00; xfsr |= (XR << 10); break;
- case 1: xfsr &= ~0x300000000UL; xfsr |= (XR << 32); break;
- case 2: xfsr &= ~0xc00000000UL; xfsr |= (XR << 34); break;
- case 3: xfsr &= ~0x3000000000UL; xfsr |= (XR << 36); break;
- }
- current_thread_info()->xfsr[0] = xfsr;
- break;
- case 1: rd->s = IR; break;
- case 2: rd->d = XR; break;
- case 5: FP_PACK_SP (rd, SR); break;
- case 6: FP_PACK_DP (rd, DR); break;
- case 7: FP_PACK_QP (rd, QR); break;
- }
- }
-
- if(_fex != 0)
- return record_exception(regs, _fex);
-
- /* Success and no exceptions detected. */
- current_thread_info()->xfsr[0] &= ~(FSR_CEXC_MASK);
- regs->tpc = regs->tnpc;
- regs->tnpc += 4;
- return 1;
- }
-err: return 0;
-}
diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile
deleted file mode 100644
index 68d04c0370f4..000000000000
--- a/arch/sparc64/mm/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# Makefile for the linux Sparc64-specific parts of the memory manager.
-#
-
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS := -Werror
-
-obj-y := ultra.o tlb.o tsb.o fault.o init.o generic.o
-
-obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
deleted file mode 100644
index 185f34679110..000000000000
--- a/arch/sparc64/mm/init.c
+++ /dev/null
@@ -1,2362 +0,0 @@
-/*
- * arch/sparc64/mm/init.c
- *
- * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-#include <linux/mm.h>
-#include <linux/hugetlb.h>
-#include <linux/slab.h>
-#include <linux/initrd.h>
-#include <linux/swap.h>
-#include <linux/pagemap.h>
-#include <linux/poison.h>
-#include <linux/fs.h>
-#include <linux/seq_file.h>
-#include <linux/kprobes.h>
-#include <linux/cache.h>
-#include <linux/sort.h>
-#include <linux/percpu.h>
-#include <linux/lmb.h>
-#include <linux/mmzone.h>
-
-#include <asm/head.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
-#include <asm/oplib.h>
-#include <asm/iommu.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/mmu_context.h>
-#include <asm/tlbflush.h>
-#include <asm/dma.h>
-#include <asm/starfire.h>
-#include <asm/tlb.h>
-#include <asm/spitfire.h>
-#include <asm/sections.h>
-#include <asm/tsb.h>
-#include <asm/hypervisor.h>
-#include <asm/prom.h>
-#include <asm/mdesc.h>
-#include <asm/cpudata.h>
-#include <asm/irq.h>
-
-#include "init.h"
-
-unsigned long kern_linear_pte_xor[2] __read_mostly;
-
-/* A bitmap, one bit for every 256MB of physical memory. If the bit
- * is clear, we should use a 4MB page (via kern_linear_pte_xor[0]) else
- * if set we should use a 256MB page (via kern_linear_pte_xor[1]).
- */
-unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
-
-#ifndef CONFIG_DEBUG_PAGEALLOC
-/* A special kernel TSB for 4MB and 256MB linear mappings.
- * Space is allocated for this right after the trap table
- * in arch/sparc64/kernel/head.S
- */
-extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
-#endif
-
-#define MAX_BANKS 32
-
-static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;
-static int pavail_ents __initdata;
-
-static int cmp_p64(const void *a, const void *b)
-{
- const struct linux_prom64_registers *x = a, *y = b;
-
- if (x->phys_addr > y->phys_addr)
- return 1;
- if (x->phys_addr < y->phys_addr)
- return -1;
- return 0;
-}
-
-static void __init read_obp_memory(const char *property,
- struct linux_prom64_registers *regs,
- int *num_ents)
-{
- int node = prom_finddevice("/memory");
- int prop_size = prom_getproplen(node, property);
- int ents, ret, i;
-
- ents = prop_size / sizeof(struct linux_prom64_registers);
- if (ents > MAX_BANKS) {
- prom_printf("The machine has more %s property entries than "
- "this kernel can support (%d).\n",
- property, MAX_BANKS);
- prom_halt();
- }
-
- ret = prom_getproperty(node, property, (char *) regs, prop_size);
- if (ret == -1) {
- prom_printf("Couldn't get %s property from /memory.\n");
- prom_halt();
- }
-
- /* Sanitize what we got from the firmware, by page aligning
- * everything.
- */
- for (i = 0; i < ents; i++) {
- unsigned long base, size;
-
- base = regs[i].phys_addr;
- size = regs[i].reg_size;
-
- size &= PAGE_MASK;
- if (base & ~PAGE_MASK) {
- unsigned long new_base = PAGE_ALIGN(base);
-
- size -= new_base - base;
- if ((long) size < 0L)
- size = 0UL;
- base = new_base;
- }
- if (size == 0UL) {
- /* If it is empty, simply get rid of it.
- * This simplifies the logic of the other
- * functions that process these arrays.
- */
- memmove(&regs[i], &regs[i + 1],
- (ents - i - 1) * sizeof(regs[0]));
- i--;
- ents--;
- continue;
- }
- regs[i].phys_addr = base;
- regs[i].reg_size = size;
- }
-
- *num_ents = ents;
-
- sort(regs, ents, sizeof(struct linux_prom64_registers),
- cmp_p64, NULL);
-}
-
-unsigned long *sparc64_valid_addr_bitmap __read_mostly;
-
-/* Kernel physical address base and size in bytes. */
-unsigned long kern_base __read_mostly;
-unsigned long kern_size __read_mostly;
-
-/* Initial ramdisk setup */
-extern unsigned long sparc_ramdisk_image64;
-extern unsigned int sparc_ramdisk_image;
-extern unsigned int sparc_ramdisk_size;
-
-struct page *mem_map_zero __read_mostly;
-EXPORT_SYMBOL(mem_map_zero);
-
-unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly;
-
-unsigned long sparc64_kern_pri_context __read_mostly;
-unsigned long sparc64_kern_pri_nuc_bits __read_mostly;
-unsigned long sparc64_kern_sec_context __read_mostly;
-
-int num_kernel_image_mappings;
-
-#ifdef CONFIG_DEBUG_DCFLUSH
-atomic_t dcpage_flushes = ATOMIC_INIT(0);
-#ifdef CONFIG_SMP
-atomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);
-#endif
-#endif
-
-inline void flush_dcache_page_impl(struct page *page)
-{
- BUG_ON(tlb_type == hypervisor);
-#ifdef CONFIG_DEBUG_DCFLUSH
- atomic_inc(&dcpage_flushes);
-#endif
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- __flush_dcache_page(page_address(page),
- ((tlb_type == spitfire) &&
- page_mapping(page) != NULL));
-#else
- if (page_mapping(page) != NULL &&
- tlb_type == spitfire)
- __flush_icache_page(__pa(page_address(page)));
-#endif
-}
-
-#define PG_dcache_dirty PG_arch_1
-#define PG_dcache_cpu_shift 32UL
-#define PG_dcache_cpu_mask \
- ((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)
-
-#define dcache_dirty_cpu(page) \
- (((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)
-
-static inline void set_dcache_dirty(struct page *page, int this_cpu)
-{
- unsigned long mask = this_cpu;
- unsigned long non_cpu_bits;
-
- non_cpu_bits = ~(PG_dcache_cpu_mask << PG_dcache_cpu_shift);
- mask = (mask << PG_dcache_cpu_shift) | (1UL << PG_dcache_dirty);
-
- __asm__ __volatile__("1:\n\t"
- "ldx [%2], %%g7\n\t"
- "and %%g7, %1, %%g1\n\t"
- "or %%g1, %0, %%g1\n\t"
- "casx [%2], %%g7, %%g1\n\t"
- "cmp %%g7, %%g1\n\t"
- "membar #StoreLoad | #StoreStore\n\t"
- "bne,pn %%xcc, 1b\n\t"
- " nop"
- : /* no outputs */
- : "r" (mask), "r" (non_cpu_bits), "r" (&page->flags)
- : "g1", "g7");
-}
-
-static inline void clear_dcache_dirty_cpu(struct page *page, unsigned long cpu)
-{
- unsigned long mask = (1UL << PG_dcache_dirty);
-
- __asm__ __volatile__("! test_and_clear_dcache_dirty\n"
- "1:\n\t"
- "ldx [%2], %%g7\n\t"
- "srlx %%g7, %4, %%g1\n\t"
- "and %%g1, %3, %%g1\n\t"
- "cmp %%g1, %0\n\t"
- "bne,pn %%icc, 2f\n\t"
- " andn %%g7, %1, %%g1\n\t"
- "casx [%2], %%g7, %%g1\n\t"
- "cmp %%g7, %%g1\n\t"
- "membar #StoreLoad | #StoreStore\n\t"
- "bne,pn %%xcc, 1b\n\t"
- " nop\n"
- "2:"
- : /* no outputs */
- : "r" (cpu), "r" (mask), "r" (&page->flags),
- "i" (PG_dcache_cpu_mask),
- "i" (PG_dcache_cpu_shift)
- : "g1", "g7");
-}
-
-static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long pte)
-{
- unsigned long tsb_addr = (unsigned long) ent;
-
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
- tsb_addr = __pa(tsb_addr);
-
- __tsb_insert(tsb_addr, tag, pte);
-}
-
-unsigned long _PAGE_ALL_SZ_BITS __read_mostly;
-unsigned long _PAGE_SZBITS __read_mostly;
-
-void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
-{
- struct mm_struct *mm;
- struct tsb *tsb;
- unsigned long tag, flags;
- unsigned long tsb_index, tsb_hash_shift;
-
- if (tlb_type != hypervisor) {
- unsigned long pfn = pte_pfn(pte);
- unsigned long pg_flags;
- struct page *page;
-
- if (pfn_valid(pfn) &&
- (page = pfn_to_page(pfn), page_mapping(page)) &&
- ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {
- int cpu = ((pg_flags >> PG_dcache_cpu_shift) &
- PG_dcache_cpu_mask);
- int this_cpu = get_cpu();
-
- /* This is just to optimize away some function calls
- * in the SMP case.
- */
- if (cpu == this_cpu)
- flush_dcache_page_impl(page);
- else
- smp_flush_dcache_page_impl(page, cpu);
-
- clear_dcache_dirty_cpu(page, cpu);
-
- put_cpu();
- }
- }
-
- mm = vma->vm_mm;
-
- tsb_index = MM_TSB_BASE;
- tsb_hash_shift = PAGE_SHIFT;
-
- spin_lock_irqsave(&mm->context.lock, flags);
-
-#ifdef CONFIG_HUGETLB_PAGE
- if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL) {
- if ((tlb_type == hypervisor &&
- (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||
- (tlb_type != hypervisor &&
- (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U)) {
- tsb_index = MM_TSB_HUGE;
- tsb_hash_shift = HPAGE_SHIFT;
- }
- }
-#endif
-
- tsb = mm->context.tsb_block[tsb_index].tsb;
- tsb += ((address >> tsb_hash_shift) &
- (mm->context.tsb_block[tsb_index].tsb_nentries - 1UL));
- tag = (address >> 22UL);
- tsb_insert(tsb, tag, pte_val(pte));
-
- spin_unlock_irqrestore(&mm->context.lock, flags);
-}
-
-void flush_dcache_page(struct page *page)
-{
- struct address_space *mapping;
- int this_cpu;
-
- if (tlb_type == hypervisor)
- return;
-
- /* Do not bother with the expensive D-cache flush if it
- * is merely the zero page. The 'bigcore' testcase in GDB
- * causes this case to run millions of times.
- */
- if (page == ZERO_PAGE(0))
- return;
-
- this_cpu = get_cpu();
-
- mapping = page_mapping(page);
- if (mapping && !mapping_mapped(mapping)) {
- int dirty = test_bit(PG_dcache_dirty, &page->flags);
- if (dirty) {
- int dirty_cpu = dcache_dirty_cpu(page);
-
- if (dirty_cpu == this_cpu)
- goto out;
- smp_flush_dcache_page_impl(page, dirty_cpu);
- }
- set_dcache_dirty(page, this_cpu);
- } else {
- /* We could delay the flush for the !page_mapping
- * case too. But that case is for exec env/arg
- * pages and those are %99 certainly going to get
- * faulted into the tlb (and thus flushed) anyways.
- */
- flush_dcache_page_impl(page);
- }
-
-out:
- put_cpu();
-}
-
-void __kprobes flush_icache_range(unsigned long start, unsigned long end)
-{
- /* Cheetah and Hypervisor platform cpus have coherent I-cache. */
- if (tlb_type == spitfire) {
- unsigned long kaddr;
-
- /* This code only runs on Spitfire cpus so this is
- * why we can assume _PAGE_PADDR_4U.
- */
- for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {
- unsigned long paddr, mask = _PAGE_PADDR_4U;
-
- if (kaddr >= PAGE_OFFSET)
- paddr = kaddr & mask;
- else {
- pgd_t *pgdp = pgd_offset_k(kaddr);
- pud_t *pudp = pud_offset(pgdp, kaddr);
- pmd_t *pmdp = pmd_offset(pudp, kaddr);
- pte_t *ptep = pte_offset_kernel(pmdp, kaddr);
-
- paddr = pte_val(*ptep) & mask;
- }
- __flush_icache_page(paddr);
- }
- }
-}
-
-void mmu_info(struct seq_file *m)
-{
- if (tlb_type == cheetah)
- seq_printf(m, "MMU Type\t: Cheetah\n");
- else if (tlb_type == cheetah_plus)
- seq_printf(m, "MMU Type\t: Cheetah+\n");
- else if (tlb_type == spitfire)
- seq_printf(m, "MMU Type\t: Spitfire\n");
- else if (tlb_type == hypervisor)
- seq_printf(m, "MMU Type\t: Hypervisor (sun4v)\n");
- else
- seq_printf(m, "MMU Type\t: ???\n");
-
-#ifdef CONFIG_DEBUG_DCFLUSH
- seq_printf(m, "DCPageFlushes\t: %d\n",
- atomic_read(&dcpage_flushes));
-#ifdef CONFIG_SMP
- seq_printf(m, "DCPageFlushesXC\t: %d\n",
- atomic_read(&dcpage_flushes_xcall));
-#endif /* CONFIG_SMP */
-#endif /* CONFIG_DEBUG_DCFLUSH */
-}
-
-struct linux_prom_translation prom_trans[512] __read_mostly;
-unsigned int prom_trans_ents __read_mostly;
-
-unsigned long kern_locked_tte_data;
-
-/* The obp translations are saved based on 8k pagesize, since obp can
- * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS ->
- * HI_OBP_ADDRESS range are handled in ktlb.S.
- */
-static inline int in_obp_range(unsigned long vaddr)
-{
- return (vaddr >= LOW_OBP_ADDRESS &&
- vaddr < HI_OBP_ADDRESS);
-}
-
-static int cmp_ptrans(const void *a, const void *b)
-{
- const struct linux_prom_translation *x = a, *y = b;
-
- if (x->virt > y->virt)
- return 1;
- if (x->virt < y->virt)
- return -1;
- return 0;
-}
-
-/* Read OBP translations property into 'prom_trans[]'. */
-static void __init read_obp_translations(void)
-{
- int n, node, ents, first, last, i;
-
- node = prom_finddevice("/virtual-memory");
- n = prom_getproplen(node, "translations");
- if (unlikely(n == 0 || n == -1)) {
- prom_printf("prom_mappings: Couldn't get size.\n");
- prom_halt();
- }
- if (unlikely(n > sizeof(prom_trans))) {
- prom_printf("prom_mappings: Size %Zd is too big.\n", n);
- prom_halt();
- }
-
- if ((n = prom_getproperty(node, "translations",
- (char *)&prom_trans[0],
- sizeof(prom_trans))) == -1) {
- prom_printf("prom_mappings: Couldn't get property.\n");
- prom_halt();
- }
-
- n = n / sizeof(struct linux_prom_translation);
-
- ents = n;
-
- sort(prom_trans, ents, sizeof(struct linux_prom_translation),
- cmp_ptrans, NULL);
-
- /* Now kick out all the non-OBP entries. */
- for (i = 0; i < ents; i++) {
- if (in_obp_range(prom_trans[i].virt))
- break;
- }
- first = i;
- for (; i < ents; i++) {
- if (!in_obp_range(prom_trans[i].virt))
- break;
- }
- last = i;
-
- for (i = 0; i < (last - first); i++) {
- struct linux_prom_translation *src = &prom_trans[i + first];
- struct linux_prom_translation *dest = &prom_trans[i];
-
- *dest = *src;
- }
- for (; i < ents; i++) {
- struct linux_prom_translation *dest = &prom_trans[i];
- dest->virt = dest->size = dest->data = 0x0UL;
- }
-
- prom_trans_ents = last - first;
-
- if (tlb_type == spitfire) {
- /* Clear diag TTE bits. */
- for (i = 0; i < prom_trans_ents; i++)
- prom_trans[i].data &= ~0x0003fe0000000000UL;
- }
-}
-
-static void __init hypervisor_tlb_lock(unsigned long vaddr,
- unsigned long pte,
- unsigned long mmu)
-{
- unsigned long ret = sun4v_mmu_map_perm_addr(vaddr, 0, pte, mmu);
-
- if (ret != 0) {
- prom_printf("hypervisor_tlb_lock[%lx:%lx:%lx:%lx]: "
- "errors with %lx\n", vaddr, 0, pte, mmu, ret);
- prom_halt();
- }
-}
-
-static unsigned long kern_large_tte(unsigned long paddr);
-
-static void __init remap_kernel(void)
-{
- unsigned long phys_page, tte_vaddr, tte_data;
- int i, tlb_ent = sparc64_highest_locked_tlbent();
-
- tte_vaddr = (unsigned long) KERNBASE;
- phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
- tte_data = kern_large_tte(phys_page);
-
- kern_locked_tte_data = tte_data;
-
- /* Now lock us into the TLBs via Hypervisor or OBP. */
- if (tlb_type == hypervisor) {
- for (i = 0; i < num_kernel_image_mappings; i++) {
- hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_DMMU);
- hypervisor_tlb_lock(tte_vaddr, tte_data, HV_MMU_IMMU);
- tte_vaddr += 0x400000;
- tte_data += 0x400000;
- }
- } else {
- for (i = 0; i < num_kernel_image_mappings; i++) {
- prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
- prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
- tte_vaddr += 0x400000;
- tte_data += 0x400000;
- }
- sparc64_highest_unlocked_tlb_ent = tlb_ent - i;
- }
- if (tlb_type == cheetah_plus) {
- sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 |
- CTX_CHEETAH_PLUS_NUC);
- sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC;
- sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0;
- }
-}
-
-
-static void __init inherit_prom_mappings(void)
-{
- /* Now fixup OBP's idea about where we really are mapped. */
- printk("Remapping the kernel... ");
- remap_kernel();
- printk("done.\n");
-}
-
-void prom_world(int enter)
-{
- if (!enter)
- set_fs((mm_segment_t) { get_thread_current_ds() });
-
- __asm__ __volatile__("flushw");
-}
-
-void __flush_dcache_range(unsigned long start, unsigned long end)
-{
- unsigned long va;
-
- if (tlb_type == spitfire) {
- int n = 0;
-
- for (va = start; va < end; va += 32) {
- spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
- if (++n >= 512)
- break;
- }
- } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
- start = __pa(start);
- end = __pa(end);
- for (va = start; va < end; va += 32)
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (va),
- "i" (ASI_DCACHE_INVALIDATE));
- }
-}
-
-/* get_new_mmu_context() uses "cache + 1". */
-DEFINE_SPINLOCK(ctx_alloc_lock);
-unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;
-#define MAX_CTX_NR (1UL << CTX_NR_BITS)
-#define CTX_BMAP_SLOTS BITS_TO_LONGS(MAX_CTX_NR)
-DECLARE_BITMAP(mmu_context_bmap, MAX_CTX_NR);
-
-/* Caller does TLB context flushing on local CPU if necessary.
- * The caller also ensures that CTX_VALID(mm->context) is false.
- *
- * We must be careful about boundary cases so that we never
- * let the user have CTX 0 (nucleus) or we ever use a CTX
- * version of zero (and thus NO_CONTEXT would not be caught
- * by version mis-match tests in mmu_context.h).
- *
- * Always invoked with interrupts disabled.
- */
-void get_new_mmu_context(struct mm_struct *mm)
-{
- unsigned long ctx, new_ctx;
- unsigned long orig_pgsz_bits;
- unsigned long flags;
- int new_version;
-
- spin_lock_irqsave(&ctx_alloc_lock, flags);
- orig_pgsz_bits = (mm->context.sparc64_ctx_val & CTX_PGSZ_MASK);
- ctx = (tlb_context_cache + 1) & CTX_NR_MASK;
- new_ctx = find_next_zero_bit(mmu_context_bmap, 1 << CTX_NR_BITS, ctx);
- new_version = 0;
- if (new_ctx >= (1 << CTX_NR_BITS)) {
- new_ctx = find_next_zero_bit(mmu_context_bmap, ctx, 1);
- if (new_ctx >= ctx) {
- int i;
- new_ctx = (tlb_context_cache & CTX_VERSION_MASK) +
- CTX_FIRST_VERSION;
- if (new_ctx == 1)
- new_ctx = CTX_FIRST_VERSION;
-
- /* Don't call memset, for 16 entries that's just
- * plain silly...
- */
- mmu_context_bmap[0] = 3;
- mmu_context_bmap[1] = 0;
- mmu_context_bmap[2] = 0;
- mmu_context_bmap[3] = 0;
- for (i = 4; i < CTX_BMAP_SLOTS; i += 4) {
- mmu_context_bmap[i + 0] = 0;
- mmu_context_bmap[i + 1] = 0;
- mmu_context_bmap[i + 2] = 0;
- mmu_context_bmap[i + 3] = 0;
- }
- new_version = 1;
- goto out;
- }
- }
- mmu_context_bmap[new_ctx>>6] |= (1UL << (new_ctx & 63));
- new_ctx |= (tlb_context_cache & CTX_VERSION_MASK);
-out:
- tlb_context_cache = new_ctx;
- mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
- spin_unlock_irqrestore(&ctx_alloc_lock, flags);
-
- if (unlikely(new_version))
- smp_new_mmu_context_version();
-}
-
-static int numa_enabled = 1;
-static int numa_debug;
-
-static int __init early_numa(char *p)
-{
- if (!p)
- return 0;
-
- if (strstr(p, "off"))
- numa_enabled = 0;
-
- if (strstr(p, "debug"))
- numa_debug = 1;
-
- return 0;
-}
-early_param("numa", early_numa);
-
-#define numadbg(f, a...) \
-do { if (numa_debug) \
- printk(KERN_INFO f, ## a); \
-} while (0)
-
-static void __init find_ramdisk(unsigned long phys_base)
-{
-#ifdef CONFIG_BLK_DEV_INITRD
- if (sparc_ramdisk_image || sparc_ramdisk_image64) {
- unsigned long ramdisk_image;
-
- /* Older versions of the bootloader only supported a
- * 32-bit physical address for the ramdisk image
- * location, stored at sparc_ramdisk_image. Newer
- * SILO versions set sparc_ramdisk_image to zero and
- * provide a full 64-bit physical address at
- * sparc_ramdisk_image64.
- */
- ramdisk_image = sparc_ramdisk_image;
- if (!ramdisk_image)
- ramdisk_image = sparc_ramdisk_image64;
-
- /* Another bootloader quirk. The bootloader normalizes
- * the physical address to KERNBASE, so we have to
- * factor that back out and add in the lowest valid
- * physical page address to get the true physical address.
- */
- ramdisk_image -= KERNBASE;
- ramdisk_image += phys_base;
-
- numadbg("Found ramdisk at physical address 0x%lx, size %u\n",
- ramdisk_image, sparc_ramdisk_size);
-
- initrd_start = ramdisk_image;
- initrd_end = ramdisk_image + sparc_ramdisk_size;
-
- lmb_reserve(initrd_start, sparc_ramdisk_size);
-
- initrd_start += PAGE_OFFSET;
- initrd_end += PAGE_OFFSET;
- }
-#endif
-}
-
-struct node_mem_mask {
- unsigned long mask;
- unsigned long val;
- unsigned long bootmem_paddr;
-};
-static struct node_mem_mask node_masks[MAX_NUMNODES];
-static int num_node_masks;
-
-int numa_cpu_lookup_table[NR_CPUS];
-cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES];
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-
-struct mdesc_mblock {
- u64 base;
- u64 size;
- u64 offset; /* RA-to-PA */
-};
-static struct mdesc_mblock *mblocks;
-static int num_mblocks;
-
-static unsigned long ra_to_pa(unsigned long addr)
-{
- int i;
-
- for (i = 0; i < num_mblocks; i++) {
- struct mdesc_mblock *m = &mblocks[i];
-
- if (addr >= m->base &&
- addr < (m->base + m->size)) {
- addr += m->offset;
- break;
- }
- }
- return addr;
-}
-
-static int find_node(unsigned long addr)
-{
- int i;
-
- addr = ra_to_pa(addr);
- for (i = 0; i < num_node_masks; i++) {
- struct node_mem_mask *p = &node_masks[i];
-
- if ((addr & p->mask) == p->val)
- return i;
- }
- return -1;
-}
-
-static unsigned long nid_range(unsigned long start, unsigned long end,
- int *nid)
-{
- *nid = find_node(start);
- start += PAGE_SIZE;
- while (start < end) {
- int n = find_node(start);
-
- if (n != *nid)
- break;
- start += PAGE_SIZE;
- }
-
- if (start > end)
- start = end;
-
- return start;
-}
-#else
-static unsigned long nid_range(unsigned long start, unsigned long end,
- int *nid)
-{
- *nid = 0;
- return end;
-}
-#endif
-
-/* This must be invoked after performing all of the necessary
- * add_active_range() calls for 'nid'. We need to be able to get
- * correct data from get_pfn_range_for_nid().
- */
-static void __init allocate_node_data(int nid)
-{
- unsigned long paddr, num_pages, start_pfn, end_pfn;
- struct pglist_data *p;
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
- paddr = lmb_alloc_nid(sizeof(struct pglist_data),
- SMP_CACHE_BYTES, nid, nid_range);
- if (!paddr) {
- prom_printf("Cannot allocate pglist_data for nid[%d]\n", nid);
- prom_halt();
- }
- NODE_DATA(nid) = __va(paddr);
- memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
-
- NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
-#endif
-
- p = NODE_DATA(nid);
-
- get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
- p->node_start_pfn = start_pfn;
- p->node_spanned_pages = end_pfn - start_pfn;
-
- if (p->node_spanned_pages) {
- num_pages = bootmem_bootmap_pages(p->node_spanned_pages);
-
- paddr = lmb_alloc_nid(num_pages << PAGE_SHIFT, PAGE_SIZE, nid,
- nid_range);
- if (!paddr) {
- prom_printf("Cannot allocate bootmap for nid[%d]\n",
- nid);
- prom_halt();
- }
- node_masks[nid].bootmem_paddr = paddr;
- }
-}
-
-static void init_node_masks_nonnuma(void)
-{
- int i;
-
- numadbg("Initializing tables for non-numa.\n");
-
- node_masks[0].mask = node_masks[0].val = 0;
- num_node_masks = 1;
-
- for (i = 0; i < NR_CPUS; i++)
- numa_cpu_lookup_table[i] = 0;
-
- numa_cpumask_lookup_table[0] = CPU_MASK_ALL;
-}
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
-struct pglist_data *node_data[MAX_NUMNODES];
-
-EXPORT_SYMBOL(numa_cpu_lookup_table);
-EXPORT_SYMBOL(numa_cpumask_lookup_table);
-EXPORT_SYMBOL(node_data);
-
-struct mdesc_mlgroup {
- u64 node;
- u64 latency;
- u64 match;
- u64 mask;
-};
-static struct mdesc_mlgroup *mlgroups;
-static int num_mlgroups;
-
-static int scan_pio_for_cfg_handle(struct mdesc_handle *md, u64 pio,
- u32 cfg_handle)
-{
- u64 arc;
-
- mdesc_for_each_arc(arc, md, pio, MDESC_ARC_TYPE_FWD) {
- u64 target = mdesc_arc_target(md, arc);
- const u64 *val;
-
- val = mdesc_get_property(md, target,
- "cfg-handle", NULL);
- if (val && *val == cfg_handle)
- return 0;
- }
- return -ENODEV;
-}
-
-static int scan_arcs_for_cfg_handle(struct mdesc_handle *md, u64 grp,
- u32 cfg_handle)
-{
- u64 arc, candidate, best_latency = ~(u64)0;
-
- candidate = MDESC_NODE_NULL;
- mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
- u64 target = mdesc_arc_target(md, arc);
- const char *name = mdesc_node_name(md, target);
- const u64 *val;
-
- if (strcmp(name, "pio-latency-group"))
- continue;
-
- val = mdesc_get_property(md, target, "latency", NULL);
- if (!val)
- continue;
-
- if (*val < best_latency) {
- candidate = target;
- best_latency = *val;
- }
- }
-
- if (candidate == MDESC_NODE_NULL)
- return -ENODEV;
-
- return scan_pio_for_cfg_handle(md, candidate, cfg_handle);
-}
-
-int of_node_to_nid(struct device_node *dp)
-{
- const struct linux_prom64_registers *regs;
- struct mdesc_handle *md;
- u32 cfg_handle;
- int count, nid;
- u64 grp;
-
- /* This is the right thing to do on currently supported
- * SUN4U NUMA platforms as well, as the PCI controller does
- * not sit behind any particular memory controller.
- */
- if (!mlgroups)
- return -1;
-
- regs = of_get_property(dp, "reg", NULL);
- if (!regs)
- return -1;
-
- cfg_handle = (regs->phys_addr >> 32UL) & 0x0fffffff;
-
- md = mdesc_grab();
-
- count = 0;
- nid = -1;
- mdesc_for_each_node_by_name(md, grp, "group") {
- if (!scan_arcs_for_cfg_handle(md, grp, cfg_handle)) {
- nid = count;
- break;
- }
- count++;
- }
-
- mdesc_release(md);
-
- return nid;
-}
-
-static void __init add_node_ranges(void)
-{
- int i;
-
- for (i = 0; i < lmb.memory.cnt; i++) {
- unsigned long size = lmb_size_bytes(&lmb.memory, i);
- unsigned long start, end;
-
- start = lmb.memory.region[i].base;
- end = start + size;
- while (start < end) {
- unsigned long this_end;
- int nid;
-
- this_end = nid_range(start, end, &nid);
-
- numadbg("Adding active range nid[%d] "
- "start[%lx] end[%lx]\n",
- nid, start, this_end);
-
- add_active_range(nid,
- start >> PAGE_SHIFT,
- this_end >> PAGE_SHIFT);
-
- start = this_end;
- }
- }
-}
-
-static int __init grab_mlgroups(struct mdesc_handle *md)
-{
- unsigned long paddr;
- int count = 0;
- u64 node;
-
- mdesc_for_each_node_by_name(md, node, "memory-latency-group")
- count++;
- if (!count)
- return -ENOENT;
-
- paddr = lmb_alloc(count * sizeof(struct mdesc_mlgroup),
- SMP_CACHE_BYTES);
- if (!paddr)
- return -ENOMEM;
-
- mlgroups = __va(paddr);
- num_mlgroups = count;
-
- count = 0;
- mdesc_for_each_node_by_name(md, node, "memory-latency-group") {
- struct mdesc_mlgroup *m = &mlgroups[count++];
- const u64 *val;
-
- m->node = node;
-
- val = mdesc_get_property(md, node, "latency", NULL);
- m->latency = *val;
- val = mdesc_get_property(md, node, "address-match", NULL);
- m->match = *val;
- val = mdesc_get_property(md, node, "address-mask", NULL);
- m->mask = *val;
-
- numadbg("MLGROUP[%d]: node[%lx] latency[%lx] "
- "match[%lx] mask[%lx]\n",
- count - 1, m->node, m->latency, m->match, m->mask);
- }
-
- return 0;
-}
-
-static int __init grab_mblocks(struct mdesc_handle *md)
-{
- unsigned long paddr;
- int count = 0;
- u64 node;
-
- mdesc_for_each_node_by_name(md, node, "mblock")
- count++;
- if (!count)
- return -ENOENT;
-
- paddr = lmb_alloc(count * sizeof(struct mdesc_mblock),
- SMP_CACHE_BYTES);
- if (!paddr)
- return -ENOMEM;
-
- mblocks = __va(paddr);
- num_mblocks = count;
-
- count = 0;
- mdesc_for_each_node_by_name(md, node, "mblock") {
- struct mdesc_mblock *m = &mblocks[count++];
- const u64 *val;
-
- val = mdesc_get_property(md, node, "base", NULL);
- m->base = *val;
- val = mdesc_get_property(md, node, "size", NULL);
- m->size = *val;
- val = mdesc_get_property(md, node,
- "address-congruence-offset", NULL);
- m->offset = *val;
-
- numadbg("MBLOCK[%d]: base[%lx] size[%lx] offset[%lx]\n",
- count - 1, m->base, m->size, m->offset);
- }
-
- return 0;
-}
-
-static void __init numa_parse_mdesc_group_cpus(struct mdesc_handle *md,
- u64 grp, cpumask_t *mask)
-{
- u64 arc;
-
- cpus_clear(*mask);
-
- mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_BACK) {
- u64 target = mdesc_arc_target(md, arc);
- const char *name = mdesc_node_name(md, target);
- const u64 *id;
-
- if (strcmp(name, "cpu"))
- continue;
- id = mdesc_get_property(md, target, "id", NULL);
- if (*id < NR_CPUS)
- cpu_set(*id, *mask);
- }
-}
-
-static struct mdesc_mlgroup * __init find_mlgroup(u64 node)
-{
- int i;
-
- for (i = 0; i < num_mlgroups; i++) {
- struct mdesc_mlgroup *m = &mlgroups[i];
- if (m->node == node)
- return m;
- }
- return NULL;
-}
-
-static int __init numa_attach_mlgroup(struct mdesc_handle *md, u64 grp,
- int index)
-{
- struct mdesc_mlgroup *candidate = NULL;
- u64 arc, best_latency = ~(u64)0;
- struct node_mem_mask *n;
-
- mdesc_for_each_arc(arc, md, grp, MDESC_ARC_TYPE_FWD) {
- u64 target = mdesc_arc_target(md, arc);
- struct mdesc_mlgroup *m = find_mlgroup(target);
- if (!m)
- continue;
- if (m->latency < best_latency) {
- candidate = m;
- best_latency = m->latency;
- }
- }
- if (!candidate)
- return -ENOENT;
-
- if (num_node_masks != index) {
- printk(KERN_ERR "Inconsistent NUMA state, "
- "index[%d] != num_node_masks[%d]\n",
- index, num_node_masks);
- return -EINVAL;
- }
-
- n = &node_masks[num_node_masks++];
-
- n->mask = candidate->mask;
- n->val = candidate->match;
-
- numadbg("NUMA NODE[%d]: mask[%lx] val[%lx] (latency[%lx])\n",
- index, n->mask, n->val, candidate->latency);
-
- return 0;
-}
-
-static int __init numa_parse_mdesc_group(struct mdesc_handle *md, u64 grp,
- int index)
-{
- cpumask_t mask;
- int cpu;
-
- numa_parse_mdesc_group_cpus(md, grp, &mask);
-
- for_each_cpu_mask(cpu, mask)
- numa_cpu_lookup_table[cpu] = index;
- numa_cpumask_lookup_table[index] = mask;
-
- if (numa_debug) {
- printk(KERN_INFO "NUMA GROUP[%d]: cpus [ ", index);
- for_each_cpu_mask(cpu, mask)
- printk("%d ", cpu);
- printk("]\n");
- }
-
- return numa_attach_mlgroup(md, grp, index);
-}
-
-static int __init numa_parse_mdesc(void)
-{
- struct mdesc_handle *md = mdesc_grab();
- int i, err, count;
- u64 node;
-
- node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
- if (node == MDESC_NODE_NULL) {
- mdesc_release(md);
- return -ENOENT;
- }
-
- err = grab_mblocks(md);
- if (err < 0)
- goto out;
-
- err = grab_mlgroups(md);
- if (err < 0)
- goto out;
-
- count = 0;
- mdesc_for_each_node_by_name(md, node, "group") {
- err = numa_parse_mdesc_group(md, node, count);
- if (err < 0)
- break;
- count++;
- }
-
- add_node_ranges();
-
- for (i = 0; i < num_node_masks; i++) {
- allocate_node_data(i);
- node_set_online(i);
- }
-
- err = 0;
-out:
- mdesc_release(md);
- return err;
-}
-
-static int __init numa_parse_jbus(void)
-{
- unsigned long cpu, index;
-
- /* NUMA node id is encoded in bits 36 and higher, and there is
- * a 1-to-1 mapping from CPU ID to NUMA node ID.
- */
- index = 0;
- for_each_present_cpu(cpu) {
- numa_cpu_lookup_table[cpu] = index;
- numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu);
- node_masks[index].mask = ~((1UL << 36UL) - 1UL);
- node_masks[index].val = cpu << 36UL;
-
- index++;
- }
- num_node_masks = index;
-
- add_node_ranges();
-
- for (index = 0; index < num_node_masks; index++) {
- allocate_node_data(index);
- node_set_online(index);
- }
-
- return 0;
-}
-
-static int __init numa_parse_sun4u(void)
-{
- if (tlb_type == cheetah || tlb_type == cheetah_plus) {
- unsigned long ver;
-
- __asm__ ("rdpr %%ver, %0" : "=r" (ver));
- if ((ver >> 32UL) == __JALAPENO_ID ||
- (ver >> 32UL) == __SERRANO_ID)
- return numa_parse_jbus();
- }
- return -1;
-}
-
-static int __init bootmem_init_numa(void)
-{
- int err = -1;
-
- numadbg("bootmem_init_numa()\n");
-
- if (numa_enabled) {
- if (tlb_type == hypervisor)
- err = numa_parse_mdesc();
- else
- err = numa_parse_sun4u();
- }
- return err;
-}
-
-#else
-
-static int bootmem_init_numa(void)
-{
- return -1;
-}
-
-#endif
-
-static void __init bootmem_init_nonnuma(void)
-{
- unsigned long top_of_ram = lmb_end_of_DRAM();
- unsigned long total_ram = lmb_phys_mem_size();
- unsigned int i;
-
- numadbg("bootmem_init_nonnuma()\n");
-
- printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n",
- top_of_ram, total_ram);
- printk(KERN_INFO "Memory hole size: %ldMB\n",
- (top_of_ram - total_ram) >> 20);
-
- init_node_masks_nonnuma();
-
- for (i = 0; i < lmb.memory.cnt; i++) {
- unsigned long size = lmb_size_bytes(&lmb.memory, i);
- unsigned long start_pfn, end_pfn;
-
- if (!size)
- continue;
-
- start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT;
- end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i);
- add_active_range(0, start_pfn, end_pfn);
- }
-
- allocate_node_data(0);
-
- node_set_online(0);
-}
-
-static void __init reserve_range_in_node(int nid, unsigned long start,
- unsigned long end)
-{
- numadbg(" reserve_range_in_node(nid[%d],start[%lx],end[%lx]\n",
- nid, start, end);
- while (start < end) {
- unsigned long this_end;
- int n;
-
- this_end = nid_range(start, end, &n);
- if (n == nid) {
- numadbg(" MATCH reserving range [%lx:%lx]\n",
- start, this_end);
- reserve_bootmem_node(NODE_DATA(nid), start,
- (this_end - start), BOOTMEM_DEFAULT);
- } else
- numadbg(" NO MATCH, advancing start to %lx\n",
- this_end);
-
- start = this_end;
- }
-}
-
-static void __init trim_reserved_in_node(int nid)
-{
- int i;
-
- numadbg(" trim_reserved_in_node(%d)\n", nid);
-
- for (i = 0; i < lmb.reserved.cnt; i++) {
- unsigned long start = lmb.reserved.region[i].base;
- unsigned long size = lmb_size_bytes(&lmb.reserved, i);
- unsigned long end = start + size;
-
- reserve_range_in_node(nid, start, end);
- }
-}
-
-static void __init bootmem_init_one_node(int nid)
-{
- struct pglist_data *p;
-
- numadbg("bootmem_init_one_node(%d)\n", nid);
-
- p = NODE_DATA(nid);
-
- if (p->node_spanned_pages) {
- unsigned long paddr = node_masks[nid].bootmem_paddr;
- unsigned long end_pfn;
-
- end_pfn = p->node_start_pfn + p->node_spanned_pages;
-
- numadbg(" init_bootmem_node(%d, %lx, %lx, %lx)\n",
- nid, paddr >> PAGE_SHIFT, p->node_start_pfn, end_pfn);
-
- init_bootmem_node(p, paddr >> PAGE_SHIFT,
- p->node_start_pfn, end_pfn);
-
- numadbg(" free_bootmem_with_active_regions(%d, %lx)\n",
- nid, end_pfn);
- free_bootmem_with_active_regions(nid, end_pfn);
-
- trim_reserved_in_node(nid);
-
- numadbg(" sparse_memory_present_with_active_regions(%d)\n",
- nid);
- sparse_memory_present_with_active_regions(nid);
- }
-}
-
-static unsigned long __init bootmem_init(unsigned long phys_base)
-{
- unsigned long end_pfn;
- int nid;
-
- end_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
- max_pfn = max_low_pfn = end_pfn;
- min_low_pfn = (phys_base >> PAGE_SHIFT);
-
- if (bootmem_init_numa() < 0)
- bootmem_init_nonnuma();
-
- /* XXX cpu notifier XXX */
-
- for_each_online_node(nid)
- bootmem_init_one_node(nid);
-
- sparse_init();
-
- return end_pfn;
-}
-
-static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
-static int pall_ents __initdata;
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-static unsigned long __ref kernel_map_range(unsigned long pstart,
- unsigned long pend, pgprot_t prot)
-{
- unsigned long vstart = PAGE_OFFSET + pstart;
- unsigned long vend = PAGE_OFFSET + pend;
- unsigned long alloc_bytes = 0UL;
-
- if ((vstart & ~PAGE_MASK) || (vend & ~PAGE_MASK)) {
- prom_printf("kernel_map: Unaligned physmem[%lx:%lx]\n",
- vstart, vend);
- prom_halt();
- }
-
- while (vstart < vend) {
- unsigned long this_end, paddr = __pa(vstart);
- pgd_t *pgd = pgd_offset_k(vstart);
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
-
- pud = pud_offset(pgd, vstart);
- if (pud_none(*pud)) {
- pmd_t *new;
-
- new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
- alloc_bytes += PAGE_SIZE;
- pud_populate(&init_mm, pud, new);
- }
-
- pmd = pmd_offset(pud, vstart);
- if (!pmd_present(*pmd)) {
- pte_t *new;
-
- new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
- alloc_bytes += PAGE_SIZE;
- pmd_populate_kernel(&init_mm, pmd, new);
- }
-
- pte = pte_offset_kernel(pmd, vstart);
- this_end = (vstart + PMD_SIZE) & PMD_MASK;
- if (this_end > vend)
- this_end = vend;
-
- while (vstart < this_end) {
- pte_val(*pte) = (paddr | pgprot_val(prot));
-
- vstart += PAGE_SIZE;
- paddr += PAGE_SIZE;
- pte++;
- }
- }
-
- return alloc_bytes;
-}
-
-extern unsigned int kvmap_linear_patch[1];
-#endif /* CONFIG_DEBUG_PAGEALLOC */
-
-static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
-{
- const unsigned long shift_256MB = 28;
- const unsigned long mask_256MB = ((1UL << shift_256MB) - 1UL);
- const unsigned long size_256MB = (1UL << shift_256MB);
-
- while (start < end) {
- long remains;
-
- remains = end - start;
- if (remains < size_256MB)
- break;
-
- if (start & mask_256MB) {
- start = (start + size_256MB) & ~mask_256MB;
- continue;
- }
-
- while (remains >= size_256MB) {
- unsigned long index = start >> shift_256MB;
-
- __set_bit(index, kpte_linear_bitmap);
-
- start += size_256MB;
- remains -= size_256MB;
- }
- }
-}
-
-static void __init init_kpte_bitmap(void)
-{
- unsigned long i;
-
- for (i = 0; i < pall_ents; i++) {
- unsigned long phys_start, phys_end;
-
- phys_start = pall[i].phys_addr;
- phys_end = phys_start + pall[i].reg_size;
-
- mark_kpte_bitmap(phys_start, phys_end);
- }
-}
-
-static void __init kernel_physical_mapping_init(void)
-{
-#ifdef CONFIG_DEBUG_PAGEALLOC
- unsigned long i, mem_alloced = 0UL;
-
- for (i = 0; i < pall_ents; i++) {
- unsigned long phys_start, phys_end;
-
- phys_start = pall[i].phys_addr;
- phys_end = phys_start + pall[i].reg_size;
-
- mem_alloced += kernel_map_range(phys_start, phys_end,
- PAGE_KERNEL);
- }
-
- printk("Allocated %ld bytes for kernel page tables.\n",
- mem_alloced);
-
- kvmap_linear_patch[0] = 0x01000000; /* nop */
- flushi(&kvmap_linear_patch[0]);
-
- __flush_tlb_all();
-#endif
-}
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
-void kernel_map_pages(struct page *page, int numpages, int enable)
-{
- unsigned long phys_start = page_to_pfn(page) << PAGE_SHIFT;
- unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
-
- kernel_map_range(phys_start, phys_end,
- (enable ? PAGE_KERNEL : __pgprot(0)));
-
- flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
- PAGE_OFFSET + phys_end);
-
- /* we should perform an IPI and flush all tlbs,
- * but that can deadlock->flush only current cpu.
- */
- __flush_tlb_kernel_range(PAGE_OFFSET + phys_start,
- PAGE_OFFSET + phys_end);
-}
-#endif
-
-unsigned long __init find_ecache_flush_span(unsigned long size)
-{
- int i;
-
- for (i = 0; i < pavail_ents; i++) {
- if (pavail[i].reg_size >= size)
- return pavail[i].phys_addr;
- }
-
- return ~0UL;
-}
-
-static void __init tsb_phys_patch(void)
-{
- struct tsb_ldquad_phys_patch_entry *pquad;
- struct tsb_phys_patch_entry *p;
-
- pquad = &__tsb_ldquad_phys_patch;
- while (pquad < &__tsb_ldquad_phys_patch_end) {
- unsigned long addr = pquad->addr;
-
- if (tlb_type == hypervisor)
- *(unsigned int *) addr = pquad->sun4v_insn;
- else
- *(unsigned int *) addr = pquad->sun4u_insn;
- wmb();
- __asm__ __volatile__("flush %0"
- : /* no outputs */
- : "r" (addr));
-
- pquad++;
- }
-
- p = &__tsb_phys_patch;
- while (p < &__tsb_phys_patch_end) {
- unsigned long addr = p->addr;
-
- *(unsigned int *) addr = p->insn;
- wmb();
- __asm__ __volatile__("flush %0"
- : /* no outputs */
- : "r" (addr));
-
- p++;
- }
-}
-
-/* Don't mark as init, we give this to the Hypervisor. */
-#ifndef CONFIG_DEBUG_PAGEALLOC
-#define NUM_KTSB_DESCR 2
-#else
-#define NUM_KTSB_DESCR 1
-#endif
-static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
-
-static void __init sun4v_ktsb_init(void)
-{
- unsigned long ktsb_pa;
-
- /* First KTSB for PAGE_SIZE mappings. */
- ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
-
- switch (PAGE_SIZE) {
- case 8 * 1024:
- default:
- ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_8K;
- ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_8K;
- break;
-
- case 64 * 1024:
- ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_64K;
- ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_64K;
- break;
-
- case 512 * 1024:
- ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_512K;
- ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_512K;
- break;
-
- case 4 * 1024 * 1024:
- ktsb_descr[0].pgsz_idx = HV_PGSZ_IDX_4MB;
- ktsb_descr[0].pgsz_mask = HV_PGSZ_MASK_4MB;
- break;
- };
-
- ktsb_descr[0].assoc = 1;
- ktsb_descr[0].num_ttes = KERNEL_TSB_NENTRIES;
- ktsb_descr[0].ctx_idx = 0;
- ktsb_descr[0].tsb_base = ktsb_pa;
- ktsb_descr[0].resv = 0;
-
-#ifndef CONFIG_DEBUG_PAGEALLOC
- /* Second KTSB for 4MB/256MB mappings. */
- ktsb_pa = (kern_base +
- ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
-
- ktsb_descr[1].pgsz_idx = HV_PGSZ_IDX_4MB;
- ktsb_descr[1].pgsz_mask = (HV_PGSZ_MASK_4MB |
- HV_PGSZ_MASK_256MB);
- ktsb_descr[1].assoc = 1;
- ktsb_descr[1].num_ttes = KERNEL_TSB4M_NENTRIES;
- ktsb_descr[1].ctx_idx = 0;
- ktsb_descr[1].tsb_base = ktsb_pa;
- ktsb_descr[1].resv = 0;
-#endif
-}
-
-void __cpuinit sun4v_ktsb_register(void)
-{
- unsigned long pa, ret;
-
- pa = kern_base + ((unsigned long)&ktsb_descr[0] - KERNBASE);
-
- ret = sun4v_mmu_tsb_ctx0(NUM_KTSB_DESCR, pa);
- if (ret != 0) {
- prom_printf("hypervisor_mmu_tsb_ctx0[%lx]: "
- "errors with %lx\n", pa, ret);
- prom_halt();
- }
-}
-
-/* paging_init() sets up the page tables */
-
-static unsigned long last_valid_pfn;
-pgd_t swapper_pg_dir[2048];
-
-static void sun4u_pgprot_init(void);
-static void sun4v_pgprot_init(void);
-
-/* Dummy function */
-void __init setup_per_cpu_areas(void)
-{
-}
-
-void __init paging_init(void)
-{
- unsigned long end_pfn, shift, phys_base;
- unsigned long real_end, i;
-
- /* These build time checkes make sure that the dcache_dirty_cpu()
- * page->flags usage will work.
- *
- * When a page gets marked as dcache-dirty, we store the
- * cpu number starting at bit 32 in the page->flags. Also,
- * functions like clear_dcache_dirty_cpu use the cpu mask
- * in 13-bit signed-immediate instruction fields.
- */
-
- /*
- * Page flags must not reach into upper 32 bits that are used
- * for the cpu number
- */
- BUILD_BUG_ON(NR_PAGEFLAGS > 32);
-
- /*
- * The bit fields placed in the high range must not reach below
- * the 32 bit boundary. Otherwise we cannot place the cpu field
- * at the 32 bit boundary.
- */
- BUILD_BUG_ON(SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH +
- ilog2(roundup_pow_of_two(NR_CPUS)) > 32);
-
- BUILD_BUG_ON(NR_CPUS > 4096);
-
- kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
- kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
-
- /* Invalidate both kernel TSBs. */
- memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
-#ifndef CONFIG_DEBUG_PAGEALLOC
- memset(swapper_4m_tsb, 0x40, sizeof(swapper_4m_tsb));
-#endif
-
- if (tlb_type == hypervisor)
- sun4v_pgprot_init();
- else
- sun4u_pgprot_init();
-
- if (tlb_type == cheetah_plus ||
- tlb_type == hypervisor)
- tsb_phys_patch();
-
- if (tlb_type == hypervisor) {
- sun4v_patch_tlb_handlers();
- sun4v_ktsb_init();
- }
-
- lmb_init();
-
- /* Find available physical memory...
- *
- * Read it twice in order to work around a bug in openfirmware.
- * The call to grab this table itself can cause openfirmware to
- * allocate memory, which in turn can take away some space from
- * the list of available memory. Reading it twice makes sure
- * we really do get the final value.
- */
- read_obp_translations();
- read_obp_memory("reg", &pall[0], &pall_ents);
- read_obp_memory("available", &pavail[0], &pavail_ents);
- read_obp_memory("available", &pavail[0], &pavail_ents);
-
- phys_base = 0xffffffffffffffffUL;
- for (i = 0; i < pavail_ents; i++) {
- phys_base = min(phys_base, pavail[i].phys_addr);
- lmb_add(pavail[i].phys_addr, pavail[i].reg_size);
- }
-
- lmb_reserve(kern_base, kern_size);
-
- find_ramdisk(phys_base);
-
- lmb_enforce_memory_limit(cmdline_memory_size);
-
- lmb_analyze();
- lmb_dump_all();
-
- set_bit(0, mmu_context_bmap);
-
- shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE);
-
- real_end = (unsigned long)_end;
- num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22);
- printk("Kernel: Using %d locked TLB entries for main kernel image.\n",
- num_kernel_image_mappings);
-
- /* Set kernel pgd to upper alias so physical page computations
- * work.
- */
- init_mm.pgd += ((shift) / (sizeof(pgd_t)));
-
- memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
-
- /* Now can init the kernel/bad page tables. */
- pud_set(pud_offset(&swapper_pg_dir[0], 0),
- swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
-
- inherit_prom_mappings();
-
- init_kpte_bitmap();
-
- /* Ok, we can use our TLB miss and window trap handlers safely. */
- setup_tba();
-
- __flush_tlb_all();
-
- if (tlb_type == hypervisor)
- sun4v_ktsb_register();
-
- /* We must setup the per-cpu areas before we pull in the
- * PROM and the MDESC. The code there fills in cpu and
- * other information into per-cpu data structures.
- */
- real_setup_per_cpu_areas();
-
- prom_build_devicetree();
-
- if (tlb_type == hypervisor)
- sun4v_mdesc_init();
-
- /* Once the OF device tree and MDESC have been setup, we know
- * the list of possible cpus. Therefore we can allocate the
- * IRQ stacks.
- */
- for_each_possible_cpu(i) {
- /* XXX Use node local allocations... XXX */
- softirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
- hardirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE));
- }
-
- /* Setup bootmem... */
- last_valid_pfn = end_pfn = bootmem_init(phys_base);
-
-#ifndef CONFIG_NEED_MULTIPLE_NODES
- max_mapnr = last_valid_pfn;
-#endif
- kernel_physical_mapping_init();
-
- {
- unsigned long max_zone_pfns[MAX_NR_ZONES];
-
- memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
-
- max_zone_pfns[ZONE_NORMAL] = end_pfn;
-
- free_area_init_nodes(max_zone_pfns);
- }
-
- printk("Booting Linux...\n");
-}
-
-int __init page_in_phys_avail(unsigned long paddr)
-{
- int i;
-
- paddr &= PAGE_MASK;
-
- for (i = 0; i < pavail_ents; i++) {
- unsigned long start, end;
-
- start = pavail[i].phys_addr;
- end = start + pavail[i].reg_size;
-
- if (paddr >= start && paddr < end)
- return 1;
- }
- if (paddr >= kern_base && paddr < (kern_base + kern_size))
- return 1;
-#ifdef CONFIG_BLK_DEV_INITRD
- if (paddr >= __pa(initrd_start) &&
- paddr < __pa(PAGE_ALIGN(initrd_end)))
- return 1;
-#endif
-
- return 0;
-}
-
-static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
-static int pavail_rescan_ents __initdata;
-
-/* Certain OBP calls, such as fetching "available" properties, can
- * claim physical memory. So, along with initializing the valid
- * address bitmap, what we do here is refetch the physical available
- * memory list again, and make sure it provides at least as much
- * memory as 'pavail' does.
- */
-static void __init setup_valid_addr_bitmap_from_pavail(void)
-{
- int i;
-
- read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
-
- for (i = 0; i < pavail_ents; i++) {
- unsigned long old_start, old_end;
-
- old_start = pavail[i].phys_addr;
- old_end = old_start + pavail[i].reg_size;
- while (old_start < old_end) {
- int n;
-
- for (n = 0; n < pavail_rescan_ents; n++) {
- unsigned long new_start, new_end;
-
- new_start = pavail_rescan[n].phys_addr;
- new_end = new_start +
- pavail_rescan[n].reg_size;
-
- if (new_start <= old_start &&
- new_end >= (old_start + PAGE_SIZE)) {
- set_bit(old_start >> 22,
- sparc64_valid_addr_bitmap);
- goto do_next_page;
- }
- }
-
- prom_printf("mem_init: Lost memory in pavail\n");
- prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
- pavail[i].phys_addr,
- pavail[i].reg_size);
- prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
- pavail_rescan[i].phys_addr,
- pavail_rescan[i].reg_size);
- prom_printf("mem_init: Cannot continue, aborting.\n");
- prom_halt();
-
- do_next_page:
- old_start += PAGE_SIZE;
- }
- }
-}
-
-void __init mem_init(void)
-{
- unsigned long codepages, datapages, initpages;
- unsigned long addr, last;
- int i;
-
- i = last_valid_pfn >> ((22 - PAGE_SHIFT) + 6);
- i += 1;
- sparc64_valid_addr_bitmap = (unsigned long *) alloc_bootmem(i << 3);
- if (sparc64_valid_addr_bitmap == NULL) {
- prom_printf("mem_init: Cannot alloc valid_addr_bitmap.\n");
- prom_halt();
- }
- memset(sparc64_valid_addr_bitmap, 0, i << 3);
-
- addr = PAGE_OFFSET + kern_base;
- last = PAGE_ALIGN(kern_size) + addr;
- while (addr < last) {
- set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
- addr += PAGE_SIZE;
- }
-
- setup_valid_addr_bitmap_from_pavail();
-
- high_memory = __va(last_valid_pfn << PAGE_SHIFT);
-
-#ifdef CONFIG_NEED_MULTIPLE_NODES
- for_each_online_node(i) {
- if (NODE_DATA(i)->node_spanned_pages != 0) {
- totalram_pages +=
- free_all_bootmem_node(NODE_DATA(i));
- }
- }
-#else
- totalram_pages = free_all_bootmem();
-#endif
-
- /* We subtract one to account for the mem_map_zero page
- * allocated below.
- */
- totalram_pages -= 1;
- num_physpages = totalram_pages;
-
- /*
- * Set up the zero page, mark it reserved, so that page count
- * is not manipulated when freeing the page from user ptes.
- */
- mem_map_zero = alloc_pages(GFP_KERNEL|__GFP_ZERO, 0);
- if (mem_map_zero == NULL) {
- prom_printf("paging_init: Cannot alloc zero page.\n");
- prom_halt();
- }
- SetPageReserved(mem_map_zero);
-
- codepages = (((unsigned long) _etext) - ((unsigned long) _start));
- codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT;
- datapages = (((unsigned long) _edata) - ((unsigned long) _etext));
- datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT;
- initpages = (((unsigned long) __init_end) - ((unsigned long) __init_begin));
- initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT;
-
- printk("Memory: %luk available (%ldk kernel code, %ldk data, %ldk init) [%016lx,%016lx]\n",
- nr_free_pages() << (PAGE_SHIFT-10),
- codepages << (PAGE_SHIFT-10),
- datapages << (PAGE_SHIFT-10),
- initpages << (PAGE_SHIFT-10),
- PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT));
-
- if (tlb_type == cheetah || tlb_type == cheetah_plus)
- cheetah_ecache_flush_init();
-}
-
-void free_initmem(void)
-{
- unsigned long addr, initend;
- int do_free = 1;
-
- /* If the physical memory maps were trimmed by kernel command
- * line options, don't even try freeing this initmem stuff up.
- * The kernel image could have been in the trimmed out region
- * and if so the freeing below will free invalid page structs.
- */
- if (cmdline_memory_size)
- do_free = 0;
-
- /*
- * The init section is aligned to 8k in vmlinux.lds. Page align for >8k pagesizes.
- */
- addr = PAGE_ALIGN((unsigned long)(__init_begin));
- initend = (unsigned long)(__init_end) & PAGE_MASK;
- for (; addr < initend; addr += PAGE_SIZE) {
- unsigned long page;
- struct page *p;
-
- page = (addr +
- ((unsigned long) __va(kern_base)) -
- ((unsigned long) KERNBASE));
- memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
-
- if (do_free) {
- p = virt_to_page(page);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- num_physpages++;
- totalram_pages++;
- }
- }
-}
-
-#ifdef CONFIG_BLK_DEV_INITRD
-void free_initrd_mem(unsigned long start, unsigned long end)
-{
- if (start < end)
- printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
- for (; start < end; start += PAGE_SIZE) {
- struct page *p = virt_to_page(start);
-
- ClearPageReserved(p);
- init_page_count(p);
- __free_page(p);
- num_physpages++;
- totalram_pages++;
- }
-}
-#endif
-
-#define _PAGE_CACHE_4U (_PAGE_CP_4U | _PAGE_CV_4U)
-#define _PAGE_CACHE_4V (_PAGE_CP_4V | _PAGE_CV_4V)
-#define __DIRTY_BITS_4U (_PAGE_MODIFIED_4U | _PAGE_WRITE_4U | _PAGE_W_4U)
-#define __DIRTY_BITS_4V (_PAGE_MODIFIED_4V | _PAGE_WRITE_4V | _PAGE_W_4V)
-#define __ACCESS_BITS_4U (_PAGE_ACCESSED_4U | _PAGE_READ_4U | _PAGE_R)
-#define __ACCESS_BITS_4V (_PAGE_ACCESSED_4V | _PAGE_READ_4V | _PAGE_R)
-
-pgprot_t PAGE_KERNEL __read_mostly;
-EXPORT_SYMBOL(PAGE_KERNEL);
-
-pgprot_t PAGE_KERNEL_LOCKED __read_mostly;
-pgprot_t PAGE_COPY __read_mostly;
-
-pgprot_t PAGE_SHARED __read_mostly;
-EXPORT_SYMBOL(PAGE_SHARED);
-
-unsigned long pg_iobits __read_mostly;
-
-unsigned long _PAGE_IE __read_mostly;
-EXPORT_SYMBOL(_PAGE_IE);
-
-unsigned long _PAGE_E __read_mostly;
-EXPORT_SYMBOL(_PAGE_E);
-
-unsigned long _PAGE_CACHE __read_mostly;
-EXPORT_SYMBOL(_PAGE_CACHE);
-
-#ifdef CONFIG_SPARSEMEM_VMEMMAP
-unsigned long vmemmap_table[VMEMMAP_SIZE];
-
-int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
-{
- unsigned long vstart = (unsigned long) start;
- unsigned long vend = (unsigned long) (start + nr);
- unsigned long phys_start = (vstart - VMEMMAP_BASE);
- unsigned long phys_end = (vend - VMEMMAP_BASE);
- unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
- unsigned long end = VMEMMAP_ALIGN(phys_end);
- unsigned long pte_base;
-
- pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
- _PAGE_CP_4U | _PAGE_CV_4U |
- _PAGE_P_4U | _PAGE_W_4U);
- if (tlb_type == hypervisor)
- pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4V |
- _PAGE_CP_4V | _PAGE_CV_4V |
- _PAGE_P_4V | _PAGE_W_4V);
-
- for (; addr < end; addr += VMEMMAP_CHUNK) {
- unsigned long *vmem_pp =
- vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
- void *block;
-
- if (!(*vmem_pp & _PAGE_VALID)) {
- block = vmemmap_alloc_block(1UL << 22, node);
- if (!block)
- return -ENOMEM;
-
- *vmem_pp = pte_base | __pa(block);
-
- printk(KERN_INFO "[%p-%p] page_structs=%lu "
- "node=%d entry=%lu/%lu\n", start, block, nr,
- node,
- addr >> VMEMMAP_CHUNK_SHIFT,
- VMEMMAP_SIZE >> VMEMMAP_CHUNK_SHIFT);
- }
- }
- return 0;
-}
-#endif /* CONFIG_SPARSEMEM_VMEMMAP */
-
-static void prot_init_common(unsigned long page_none,
- unsigned long page_shared,
- unsigned long page_copy,
- unsigned long page_readonly,
- unsigned long page_exec_bit)
-{
- PAGE_COPY = __pgprot(page_copy);
- PAGE_SHARED = __pgprot(page_shared);
-
- protection_map[0x0] = __pgprot(page_none);
- protection_map[0x1] = __pgprot(page_readonly & ~page_exec_bit);
- protection_map[0x2] = __pgprot(page_copy & ~page_exec_bit);
- protection_map[0x3] = __pgprot(page_copy & ~page_exec_bit);
- protection_map[0x4] = __pgprot(page_readonly);
- protection_map[0x5] = __pgprot(page_readonly);
- protection_map[0x6] = __pgprot(page_copy);
- protection_map[0x7] = __pgprot(page_copy);
- protection_map[0x8] = __pgprot(page_none);
- protection_map[0x9] = __pgprot(page_readonly & ~page_exec_bit);
- protection_map[0xa] = __pgprot(page_shared & ~page_exec_bit);
- protection_map[0xb] = __pgprot(page_shared & ~page_exec_bit);
- protection_map[0xc] = __pgprot(page_readonly);
- protection_map[0xd] = __pgprot(page_readonly);
- protection_map[0xe] = __pgprot(page_shared);
- protection_map[0xf] = __pgprot(page_shared);
-}
-
-static void __init sun4u_pgprot_init(void)
-{
- unsigned long page_none, page_shared, page_copy, page_readonly;
- unsigned long page_exec_bit;
-
- PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
- _PAGE_CACHE_4U | _PAGE_P_4U |
- __ACCESS_BITS_4U | __DIRTY_BITS_4U |
- _PAGE_EXEC_4U);
- PAGE_KERNEL_LOCKED = __pgprot (_PAGE_PRESENT_4U | _PAGE_VALID |
- _PAGE_CACHE_4U | _PAGE_P_4U |
- __ACCESS_BITS_4U | __DIRTY_BITS_4U |
- _PAGE_EXEC_4U | _PAGE_L_4U);
-
- _PAGE_IE = _PAGE_IE_4U;
- _PAGE_E = _PAGE_E_4U;
- _PAGE_CACHE = _PAGE_CACHE_4U;
-
- pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4U | __DIRTY_BITS_4U |
- __ACCESS_BITS_4U | _PAGE_E_4U);
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
- kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^
- 0xfffff80000000000UL;
-#else
- kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
- 0xfffff80000000000UL;
-#endif
- kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
- _PAGE_P_4U | _PAGE_W_4U);
-
- /* XXX Should use 256MB on Panther. XXX */
- kern_linear_pte_xor[1] = kern_linear_pte_xor[0];
-
- _PAGE_SZBITS = _PAGE_SZBITS_4U;
- _PAGE_ALL_SZ_BITS = (_PAGE_SZ4MB_4U | _PAGE_SZ512K_4U |
- _PAGE_SZ64K_4U | _PAGE_SZ8K_4U |
- _PAGE_SZ32MB_4U | _PAGE_SZ256MB_4U);
-
-
- page_none = _PAGE_PRESENT_4U | _PAGE_ACCESSED_4U | _PAGE_CACHE_4U;
- page_shared = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
- __ACCESS_BITS_4U | _PAGE_WRITE_4U | _PAGE_EXEC_4U);
- page_copy = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
- __ACCESS_BITS_4U | _PAGE_EXEC_4U);
- page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4U | _PAGE_CACHE_4U |
- __ACCESS_BITS_4U | _PAGE_EXEC_4U);
-
- page_exec_bit = _PAGE_EXEC_4U;
-
- prot_init_common(page_none, page_shared, page_copy, page_readonly,
- page_exec_bit);
-}
-
-static void __init sun4v_pgprot_init(void)
-{
- unsigned long page_none, page_shared, page_copy, page_readonly;
- unsigned long page_exec_bit;
-
- PAGE_KERNEL = __pgprot (_PAGE_PRESENT_4V | _PAGE_VALID |
- _PAGE_CACHE_4V | _PAGE_P_4V |
- __ACCESS_BITS_4V | __DIRTY_BITS_4V |
- _PAGE_EXEC_4V);
- PAGE_KERNEL_LOCKED = PAGE_KERNEL;
-
- _PAGE_IE = _PAGE_IE_4V;
- _PAGE_E = _PAGE_E_4V;
- _PAGE_CACHE = _PAGE_CACHE_4V;
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
- kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
- 0xfffff80000000000UL;
-#else
- kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
- 0xfffff80000000000UL;
-#endif
- kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
- _PAGE_P_4V | _PAGE_W_4V);
-
-#ifdef CONFIG_DEBUG_PAGEALLOC
- kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
- 0xfffff80000000000UL;
-#else
- kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
- 0xfffff80000000000UL;
-#endif
- kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
- _PAGE_P_4V | _PAGE_W_4V);
-
- pg_iobits = (_PAGE_VALID | _PAGE_PRESENT_4V | __DIRTY_BITS_4V |
- __ACCESS_BITS_4V | _PAGE_E_4V);
-
- _PAGE_SZBITS = _PAGE_SZBITS_4V;
- _PAGE_ALL_SZ_BITS = (_PAGE_SZ16GB_4V | _PAGE_SZ2GB_4V |
- _PAGE_SZ256MB_4V | _PAGE_SZ32MB_4V |
- _PAGE_SZ4MB_4V | _PAGE_SZ512K_4V |
- _PAGE_SZ64K_4V | _PAGE_SZ8K_4V);
-
- page_none = _PAGE_PRESENT_4V | _PAGE_ACCESSED_4V | _PAGE_CACHE_4V;
- page_shared = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
- __ACCESS_BITS_4V | _PAGE_WRITE_4V | _PAGE_EXEC_4V);
- page_copy = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
- __ACCESS_BITS_4V | _PAGE_EXEC_4V);
- page_readonly = (_PAGE_VALID | _PAGE_PRESENT_4V | _PAGE_CACHE_4V |
- __ACCESS_BITS_4V | _PAGE_EXEC_4V);
-
- page_exec_bit = _PAGE_EXEC_4V;
-
- prot_init_common(page_none, page_shared, page_copy, page_readonly,
- page_exec_bit);
-}
-
-unsigned long pte_sz_bits(unsigned long sz)
-{
- if (tlb_type == hypervisor) {
- switch (sz) {
- case 8 * 1024:
- default:
- return _PAGE_SZ8K_4V;
- case 64 * 1024:
- return _PAGE_SZ64K_4V;
- case 512 * 1024:
- return _PAGE_SZ512K_4V;
- case 4 * 1024 * 1024:
- return _PAGE_SZ4MB_4V;
- };
- } else {
- switch (sz) {
- case 8 * 1024:
- default:
- return _PAGE_SZ8K_4U;
- case 64 * 1024:
- return _PAGE_SZ64K_4U;
- case 512 * 1024:
- return _PAGE_SZ512K_4U;
- case 4 * 1024 * 1024:
- return _PAGE_SZ4MB_4U;
- };
- }
-}
-
-pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space, unsigned long page_size)
-{
- pte_t pte;
-
- pte_val(pte) = page | pgprot_val(pgprot_noncached(prot));
- pte_val(pte) |= (((unsigned long)space) << 32);
- pte_val(pte) |= pte_sz_bits(page_size);
-
- return pte;
-}
-
-static unsigned long kern_large_tte(unsigned long paddr)
-{
- unsigned long val;
-
- val = (_PAGE_VALID | _PAGE_SZ4MB_4U |
- _PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U |
- _PAGE_EXEC_4U | _PAGE_L_4U | _PAGE_W_4U);
- if (tlb_type == hypervisor)
- val = (_PAGE_VALID | _PAGE_SZ4MB_4V |
- _PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V |
- _PAGE_EXEC_4V | _PAGE_W_4V);
-
- return val | paddr;
-}
-
-/* If not locked, zap it. */
-void __flush_tlb_all(void)
-{
- unsigned long pstate;
- int i;
-
- __asm__ __volatile__("flushw\n\t"
- "rdpr %%pstate, %0\n\t"
- "wrpr %0, %1, %%pstate"
- : "=r" (pstate)
- : "i" (PSTATE_IE));
- if (tlb_type == hypervisor) {
- sun4v_mmu_demap_all();
- } else if (tlb_type == spitfire) {
- for (i = 0; i < 64; i++) {
- /* Spitfire Errata #32 workaround */
- /* NOTE: Always runs on spitfire, so no
- * cheetah+ page size encodings.
- */
- __asm__ __volatile__("stxa %0, [%1] %2\n\t"
- "flush %%g6"
- : /* No outputs */
- : "r" (0),
- "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
-
- if (!(spitfire_get_dtlb_data(i) & _PAGE_L_4U)) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
- spitfire_put_dtlb_data(i, 0x0UL);
- }
-
- /* Spitfire Errata #32 workaround */
- /* NOTE: Always runs on spitfire, so no
- * cheetah+ page size encodings.
- */
- __asm__ __volatile__("stxa %0, [%1] %2\n\t"
- "flush %%g6"
- : /* No outputs */
- : "r" (0),
- "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU));
-
- if (!(spitfire_get_itlb_data(i) & _PAGE_L_4U)) {
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
- spitfire_put_itlb_data(i, 0x0UL);
- }
- }
- } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
- cheetah_flush_dtlb_all();
- cheetah_flush_itlb_all();
- }
- __asm__ __volatile__("wrpr %0, 0, %%pstate"
- : : "r" (pstate));
-}
diff --git a/arch/sparc64/mm/tsb.c b/arch/sparc64/mm/tsb.c
deleted file mode 100644
index 587f8efb2e05..000000000000
--- a/arch/sparc64/mm/tsb.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/* arch/sparc64/mm/tsb.c
- *
- * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net>
- */
-
-#include <linux/kernel.h>
-#include <linux/preempt.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/tlbflush.h>
-#include <asm/tlb.h>
-#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/tsb.h>
-#include <asm/oplib.h>
-
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
-
-static inline unsigned long tsb_hash(unsigned long vaddr, unsigned long hash_shift, unsigned long nentries)
-{
- vaddr >>= hash_shift;
- return vaddr & (nentries - 1);
-}
-
-static inline int tag_compare(unsigned long tag, unsigned long vaddr)
-{
- return (tag == (vaddr >> 22));
-}
-
-/* TSB flushes need only occur on the processor initiating the address
- * space modification, not on each cpu the address space has run on.
- * Only the TLB flush needs that treatment.
- */
-
-void flush_tsb_kernel_range(unsigned long start, unsigned long end)
-{
- unsigned long v;
-
- for (v = start; v < end; v += PAGE_SIZE) {
- unsigned long hash = tsb_hash(v, PAGE_SHIFT,
- KERNEL_TSB_NENTRIES);
- struct tsb *ent = &swapper_tsb[hash];
-
- if (tag_compare(ent->tag, v)) {
- ent->tag = (1UL << TSB_TAG_INVALID_BIT);
- membar_storeload_storestore();
- }
- }
-}
-
-static void __flush_tsb_one(struct mmu_gather *mp, unsigned long hash_shift, unsigned long tsb, unsigned long nentries)
-{
- unsigned long i;
-
- for (i = 0; i < mp->tlb_nr; i++) {
- unsigned long v = mp->vaddrs[i];
- unsigned long tag, ent, hash;
-
- v &= ~0x1UL;
-
- hash = tsb_hash(v, hash_shift, nentries);
- ent = tsb + (hash * sizeof(struct tsb));
- tag = (v >> 22UL);
-
- tsb_flush(ent, tag);
- }
-}
-
-void flush_tsb_user(struct mmu_gather *mp)
-{
- struct mm_struct *mm = mp->mm;
- unsigned long nentries, base, flags;
-
- spin_lock_irqsave(&mm->context.lock, flags);
-
- base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
- nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
- base = __pa(base);
- __flush_tsb_one(mp, PAGE_SHIFT, base, nentries);
-
-#ifdef CONFIG_HUGETLB_PAGE
- if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
- base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
- nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
- base = __pa(base);
- __flush_tsb_one(mp, HPAGE_SHIFT, base, nentries);
- }
-#endif
- spin_unlock_irqrestore(&mm->context.lock, flags);
-}
-
-#if defined(CONFIG_SPARC64_PAGE_SIZE_8KB)
-#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_8K
-#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_8K
-#elif defined(CONFIG_SPARC64_PAGE_SIZE_64KB)
-#define HV_PGSZ_IDX_BASE HV_PGSZ_IDX_64K
-#define HV_PGSZ_MASK_BASE HV_PGSZ_MASK_64K
-#else
-#error Broken base page size setting...
-#endif
-
-#ifdef CONFIG_HUGETLB_PAGE
-#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
-#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_64K
-#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_64K
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
-#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_512K
-#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_512K
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
-#define HV_PGSZ_IDX_HUGE HV_PGSZ_IDX_4MB
-#define HV_PGSZ_MASK_HUGE HV_PGSZ_MASK_4MB
-#else
-#error Broken huge page size setting...
-#endif
-#endif
-
-static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsigned long tsb_bytes)
-{
- unsigned long tsb_reg, base, tsb_paddr;
- unsigned long page_sz, tte;
-
- mm->context.tsb_block[tsb_idx].tsb_nentries =
- tsb_bytes / sizeof(struct tsb);
-
- base = TSBMAP_BASE;
- tte = pgprot_val(PAGE_KERNEL_LOCKED);
- tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb);
- BUG_ON(tsb_paddr & (tsb_bytes - 1UL));
-
- /* Use the smallest page size that can map the whole TSB
- * in one TLB entry.
- */
- switch (tsb_bytes) {
- case 8192 << 0:
- tsb_reg = 0x0UL;
-#ifdef DCACHE_ALIASING_POSSIBLE
- base += (tsb_paddr & 8192);
-#endif
- page_sz = 8192;
- break;
-
- case 8192 << 1:
- tsb_reg = 0x1UL;
- page_sz = 64 * 1024;
- break;
-
- case 8192 << 2:
- tsb_reg = 0x2UL;
- page_sz = 64 * 1024;
- break;
-
- case 8192 << 3:
- tsb_reg = 0x3UL;
- page_sz = 64 * 1024;
- break;
-
- case 8192 << 4:
- tsb_reg = 0x4UL;
- page_sz = 512 * 1024;
- break;
-
- case 8192 << 5:
- tsb_reg = 0x5UL;
- page_sz = 512 * 1024;
- break;
-
- case 8192 << 6:
- tsb_reg = 0x6UL;
- page_sz = 512 * 1024;
- break;
-
- case 8192 << 7:
- tsb_reg = 0x7UL;
- page_sz = 4 * 1024 * 1024;
- break;
-
- default:
- printk(KERN_ERR "TSB[%s:%d]: Impossible TSB size %lu, killing process.\n",
- current->comm, current->pid, tsb_bytes);
- do_exit(SIGSEGV);
- };
- tte |= pte_sz_bits(page_sz);
-
- if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
- /* Physical mapping, no locked TLB entry for TSB. */
- tsb_reg |= tsb_paddr;
-
- mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
- mm->context.tsb_block[tsb_idx].tsb_map_vaddr = 0;
- mm->context.tsb_block[tsb_idx].tsb_map_pte = 0;
- } else {
- tsb_reg |= base;
- tsb_reg |= (tsb_paddr & (page_sz - 1UL));
- tte |= (tsb_paddr & ~(page_sz - 1UL));
-
- mm->context.tsb_block[tsb_idx].tsb_reg_val = tsb_reg;
- mm->context.tsb_block[tsb_idx].tsb_map_vaddr = base;
- mm->context.tsb_block[tsb_idx].tsb_map_pte = tte;
- }
-
- /* Setup the Hypervisor TSB descriptor. */
- if (tlb_type == hypervisor) {
- struct hv_tsb_descr *hp = &mm->context.tsb_descr[tsb_idx];
-
- switch (tsb_idx) {
- case MM_TSB_BASE:
- hp->pgsz_idx = HV_PGSZ_IDX_BASE;
- break;
-#ifdef CONFIG_HUGETLB_PAGE
- case MM_TSB_HUGE:
- hp->pgsz_idx = HV_PGSZ_IDX_HUGE;
- break;
-#endif
- default:
- BUG();
- };
- hp->assoc = 1;
- hp->num_ttes = tsb_bytes / 16;
- hp->ctx_idx = 0;
- switch (tsb_idx) {
- case MM_TSB_BASE:
- hp->pgsz_mask = HV_PGSZ_MASK_BASE;
- break;
-#ifdef CONFIG_HUGETLB_PAGE
- case MM_TSB_HUGE:
- hp->pgsz_mask = HV_PGSZ_MASK_HUGE;
- break;
-#endif
- default:
- BUG();
- };
- hp->tsb_base = tsb_paddr;
- hp->resv = 0;
- }
-}
-
-static struct kmem_cache *tsb_caches[8] __read_mostly;
-
-static const char *tsb_cache_names[8] = {
- "tsb_8KB",
- "tsb_16KB",
- "tsb_32KB",
- "tsb_64KB",
- "tsb_128KB",
- "tsb_256KB",
- "tsb_512KB",
- "tsb_1MB",
-};
-
-void __init pgtable_cache_init(void)
-{
- unsigned long i;
-
- for (i = 0; i < 8; i++) {
- unsigned long size = 8192 << i;
- const char *name = tsb_cache_names[i];
-
- tsb_caches[i] = kmem_cache_create(name,
- size, size,
- 0, NULL);
- if (!tsb_caches[i]) {
- prom_printf("Could not create %s cache\n", name);
- prom_halt();
- }
- }
-}
-
-/* When the RSS of an address space exceeds tsb_rss_limit for a TSB,
- * do_sparc64_fault() invokes this routine to try and grow it.
- *
- * When we reach the maximum TSB size supported, we stick ~0UL into
- * tsb_rss_limit for that TSB so the grow checks in do_sparc64_fault()
- * will not trigger any longer.
- *
- * The TSB can be anywhere from 8K to 1MB in size, in increasing powers
- * of two. The TSB must be aligned to it's size, so f.e. a 512K TSB
- * must be 512K aligned. It also must be physically contiguous, so we
- * cannot use vmalloc().
- *
- * The idea here is to grow the TSB when the RSS of the process approaches
- * the number of entries that the current TSB can hold at once. Currently,
- * we trigger when the RSS hits 3/4 of the TSB capacity.
- */
-void tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss)
-{
- unsigned long max_tsb_size = 1 * 1024 * 1024;
- unsigned long new_size, old_size, flags;
- struct tsb *old_tsb, *new_tsb;
- unsigned long new_cache_index, old_cache_index;
- unsigned long new_rss_limit;
- gfp_t gfp_flags;
-
- if (max_tsb_size > (PAGE_SIZE << MAX_ORDER))
- max_tsb_size = (PAGE_SIZE << MAX_ORDER);
-
- new_cache_index = 0;
- for (new_size = 8192; new_size < max_tsb_size; new_size <<= 1UL) {
- unsigned long n_entries = new_size / sizeof(struct tsb);
-
- n_entries = (n_entries * 3) / 4;
- if (n_entries > rss)
- break;
-
- new_cache_index++;
- }
-
- if (new_size == max_tsb_size)
- new_rss_limit = ~0UL;
- else
- new_rss_limit = ((new_size / sizeof(struct tsb)) * 3) / 4;
-
-retry_tsb_alloc:
- gfp_flags = GFP_KERNEL;
- if (new_size > (PAGE_SIZE * 2))
- gfp_flags = __GFP_NOWARN | __GFP_NORETRY;
-
- new_tsb = kmem_cache_alloc_node(tsb_caches[new_cache_index],
- gfp_flags, numa_node_id());
- if (unlikely(!new_tsb)) {
- /* Not being able to fork due to a high-order TSB
- * allocation failure is very bad behavior. Just back
- * down to a 0-order allocation and force no TSB
- * growing for this address space.
- */
- if (mm->context.tsb_block[tsb_index].tsb == NULL &&
- new_cache_index > 0) {
- new_cache_index = 0;
- new_size = 8192;
- new_rss_limit = ~0UL;
- goto retry_tsb_alloc;
- }
-
- /* If we failed on a TSB grow, we are under serious
- * memory pressure so don't try to grow any more.
- */
- if (mm->context.tsb_block[tsb_index].tsb != NULL)
- mm->context.tsb_block[tsb_index].tsb_rss_limit = ~0UL;
- return;
- }
-
- /* Mark all tags as invalid. */
- tsb_init(new_tsb, new_size);
-
- /* Ok, we are about to commit the changes. If we are
- * growing an existing TSB the locking is very tricky,
- * so WATCH OUT!
- *
- * We have to hold mm->context.lock while committing to the
- * new TSB, this synchronizes us with processors in
- * flush_tsb_user() and switch_mm() for this address space.
- *
- * But even with that lock held, processors run asynchronously
- * accessing the old TSB via TLB miss handling. This is OK
- * because those actions are just propagating state from the
- * Linux page tables into the TSB, page table mappings are not
- * being changed. If a real fault occurs, the processor will
- * synchronize with us when it hits flush_tsb_user(), this is
- * also true for the case where vmscan is modifying the page
- * tables. The only thing we need to be careful with is to
- * skip any locked TSB entries during copy_tsb().
- *
- * When we finish committing to the new TSB, we have to drop
- * the lock and ask all other cpus running this address space
- * to run tsb_context_switch() to see the new TSB table.
- */
- spin_lock_irqsave(&mm->context.lock, flags);
-
- old_tsb = mm->context.tsb_block[tsb_index].tsb;
- old_cache_index =
- (mm->context.tsb_block[tsb_index].tsb_reg_val & 0x7UL);
- old_size = (mm->context.tsb_block[tsb_index].tsb_nentries *
- sizeof(struct tsb));
-
-
- /* Handle multiple threads trying to grow the TSB at the same time.
- * One will get in here first, and bump the size and the RSS limit.
- * The others will get in here next and hit this check.
- */
- if (unlikely(old_tsb &&
- (rss < mm->context.tsb_block[tsb_index].tsb_rss_limit))) {
- spin_unlock_irqrestore(&mm->context.lock, flags);
-
- kmem_cache_free(tsb_caches[new_cache_index], new_tsb);
- return;
- }
-
- mm->context.tsb_block[tsb_index].tsb_rss_limit = new_rss_limit;
-
- if (old_tsb) {
- extern void copy_tsb(unsigned long old_tsb_base,
- unsigned long old_tsb_size,
- unsigned long new_tsb_base,
- unsigned long new_tsb_size);
- unsigned long old_tsb_base = (unsigned long) old_tsb;
- unsigned long new_tsb_base = (unsigned long) new_tsb;
-
- if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
- old_tsb_base = __pa(old_tsb_base);
- new_tsb_base = __pa(new_tsb_base);
- }
- copy_tsb(old_tsb_base, old_size, new_tsb_base, new_size);
- }
-
- mm->context.tsb_block[tsb_index].tsb = new_tsb;
- setup_tsb_params(mm, tsb_index, new_size);
-
- spin_unlock_irqrestore(&mm->context.lock, flags);
-
- /* If old_tsb is NULL, we're being invoked for the first time
- * from init_new_context().
- */
- if (old_tsb) {
- /* Reload it on the local cpu. */
- tsb_context_switch(mm);
-
- /* Now force other processors to do the same. */
- preempt_disable();
- smp_tsb_sync(mm);
- preempt_enable();
-
- /* Now it is safe to free the old tsb. */
- kmem_cache_free(tsb_caches[old_cache_index], old_tsb);
- }
-}
-
-int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
-{
-#ifdef CONFIG_HUGETLB_PAGE
- unsigned long huge_pte_count;
-#endif
- unsigned int i;
-
- spin_lock_init(&mm->context.lock);
-
- mm->context.sparc64_ctx_val = 0UL;
-
-#ifdef CONFIG_HUGETLB_PAGE
- /* We reset it to zero because the fork() page copying
- * will re-increment the counters as the parent PTEs are
- * copied into the child address space.
- */
- huge_pte_count = mm->context.huge_pte_count;
- mm->context.huge_pte_count = 0;
-#endif
-
- /* copy_mm() copies over the parent's mm_struct before calling
- * us, so we need to zero out the TSB pointer or else tsb_grow()
- * will be confused and think there is an older TSB to free up.
- */
- for (i = 0; i < MM_NUM_TSBS; i++)
- mm->context.tsb_block[i].tsb = NULL;
-
- /* If this is fork, inherit the parent's TSB size. We would
- * grow it to that size on the first page fault anyways.
- */
- tsb_grow(mm, MM_TSB_BASE, get_mm_rss(mm));
-
-#ifdef CONFIG_HUGETLB_PAGE
- if (unlikely(huge_pte_count))
- tsb_grow(mm, MM_TSB_HUGE, huge_pte_count);
-#endif
-
- if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
- return -ENOMEM;
-
- return 0;
-}
-
-static void tsb_destroy_one(struct tsb_config *tp)
-{
- unsigned long cache_index;
-
- if (!tp->tsb)
- return;
- cache_index = tp->tsb_reg_val & 0x7UL;
- kmem_cache_free(tsb_caches[cache_index], tp->tsb);
- tp->tsb = NULL;
- tp->tsb_reg_val = 0UL;
-}
-
-void destroy_context(struct mm_struct *mm)
-{
- unsigned long flags, i;
-
- for (i = 0; i < MM_NUM_TSBS; i++)
- tsb_destroy_one(&mm->context.tsb_block[i]);
-
- spin_lock_irqsave(&ctx_alloc_lock, flags);
-
- if (CTX_VALID(mm->context)) {
- unsigned long nr = CTX_NRBITS(mm->context);
- mmu_context_bmap[nr>>6] &= ~(1UL << (nr & 63));
- }
-
- spin_unlock_irqrestore(&ctx_alloc_lock, flags);
-}
diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S
deleted file mode 100644
index 86773e89dc1b..000000000000
--- a/arch/sparc64/mm/ultra.S
+++ /dev/null
@@ -1,769 +0,0 @@
-/*
- * ultra.S: Don't expand these all over the place...
- *
- * Copyright (C) 1997, 2000, 2008 David S. Miller (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <asm/spitfire.h>
-#include <asm/mmu_context.h>
-#include <asm/mmu.h>
-#include <asm/pil.h>
-#include <asm/head.h>
-#include <asm/thread_info.h>
-#include <asm/cacheflush.h>
-#include <asm/hypervisor.h>
-#include <asm/cpudata.h>
-
- /* Basically, most of the Spitfire vs. Cheetah madness
- * has to do with the fact that Cheetah does not support
- * IMMU flushes out of the secondary context. Someone needs
- * to throw a south lake birthday party for the folks
- * in Microelectronics who refused to fix this shit.
- */
-
- /* This file is meant to be read efficiently by the CPU, not humans.
- * Staraj sie tego nikomu nie pierdolnac...
- */
- .text
- .align 32
- .globl __flush_tlb_mm
-__flush_tlb_mm: /* 18 insns */
- /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
- ldxa [%o1] ASI_DMMU, %g2
- cmp %g2, %o0
- bne,pn %icc, __spitfire_flush_tlb_mm_slow
- mov 0x50, %g3
- stxa %g0, [%g3] ASI_DMMU_DEMAP
- stxa %g0, [%g3] ASI_IMMU_DEMAP
- sethi %hi(KERNBASE), %g3
- flush %g3
- retl
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
-
- .align 32
- .globl __flush_tlb_pending
-__flush_tlb_pending: /* 26 insns */
- /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
- rdpr %pstate, %g7
- sllx %o1, 3, %o1
- andn %g7, PSTATE_IE, %g2
- wrpr %g2, %pstate
- mov SECONDARY_CONTEXT, %o4
- ldxa [%o4] ASI_DMMU, %g2
- stxa %o0, [%o4] ASI_DMMU
-1: sub %o1, (1 << 3), %o1
- ldx [%o2 + %o1], %o3
- andcc %o3, 1, %g0
- andn %o3, 1, %o3
- be,pn %icc, 2f
- or %o3, 0x10, %o3
- stxa %g0, [%o3] ASI_IMMU_DEMAP
-2: stxa %g0, [%o3] ASI_DMMU_DEMAP
- membar #Sync
- brnz,pt %o1, 1b
- nop
- stxa %g2, [%o4] ASI_DMMU
- sethi %hi(KERNBASE), %o4
- flush %o4
- retl
- wrpr %g7, 0x0, %pstate
- nop
- nop
- nop
- nop
-
- .align 32
- .globl __flush_tlb_kernel_range
-__flush_tlb_kernel_range: /* 16 insns */
- /* %o0=start, %o1=end */
- cmp %o0, %o1
- be,pn %xcc, 2f
- sethi %hi(PAGE_SIZE), %o4
- sub %o1, %o0, %o3
- sub %o3, %o4, %o3
- or %o0, 0x20, %o0 ! Nucleus
-1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
- stxa %g0, [%o0 + %o3] ASI_IMMU_DEMAP
- membar #Sync
- brnz,pt %o3, 1b
- sub %o3, %o4, %o3
-2: sethi %hi(KERNBASE), %o3
- flush %o3
- retl
- nop
- nop
-
-__spitfire_flush_tlb_mm_slow:
- rdpr %pstate, %g1
- wrpr %g1, PSTATE_IE, %pstate
- stxa %o0, [%o1] ASI_DMMU
- stxa %g0, [%g3] ASI_DMMU_DEMAP
- stxa %g0, [%g3] ASI_IMMU_DEMAP
- flush %g6
- stxa %g2, [%o1] ASI_DMMU
- sethi %hi(KERNBASE), %o1
- flush %o1
- retl
- wrpr %g1, 0, %pstate
-
-/*
- * The following code flushes one page_size worth.
- */
- .section .kprobes.text, "ax"
- .align 32
- .globl __flush_icache_page
-__flush_icache_page: /* %o0 = phys_page */
- membar #StoreStore
- srlx %o0, PAGE_SHIFT, %o0
- sethi %uhi(PAGE_OFFSET), %g1
- sllx %o0, PAGE_SHIFT, %o0
- sethi %hi(PAGE_SIZE), %g2
- sllx %g1, 32, %g1
- add %o0, %g1, %o0
-1: subcc %g2, 32, %g2
- bne,pt %icc, 1b
- flush %o0 + %g2
- retl
- nop
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-
-#if (PAGE_SHIFT != 13)
-#error only page shift of 13 is supported by dcache flush
-#endif
-
-#define DTAG_MASK 0x3
-
- /* This routine is Spitfire specific so the hardcoded
- * D-cache size and line-size are OK.
- */
- .align 64
- .globl __flush_dcache_page
-__flush_dcache_page: /* %o0=kaddr, %o1=flush_icache */
- sethi %uhi(PAGE_OFFSET), %g1
- sllx %g1, 32, %g1
- sub %o0, %g1, %o0 ! physical address
- srlx %o0, 11, %o0 ! make D-cache TAG
- sethi %hi(1 << 14), %o2 ! D-cache size
- sub %o2, (1 << 5), %o2 ! D-cache line size
-1: ldxa [%o2] ASI_DCACHE_TAG, %o3 ! load D-cache TAG
- andcc %o3, DTAG_MASK, %g0 ! Valid?
- be,pn %xcc, 2f ! Nope, branch
- andn %o3, DTAG_MASK, %o3 ! Clear valid bits
- cmp %o3, %o0 ! TAG match?
- bne,pt %xcc, 2f ! Nope, branch
- nop
- stxa %g0, [%o2] ASI_DCACHE_TAG ! Invalidate TAG
- membar #Sync
-2: brnz,pt %o2, 1b
- sub %o2, (1 << 5), %o2 ! D-cache line size
-
- /* The I-cache does not snoop local stores so we
- * better flush that too when necessary.
- */
- brnz,pt %o1, __flush_icache_page
- sllx %o0, 11, %o0
- retl
- nop
-
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- .previous
-
- /* Cheetah specific versions, patched at boot time. */
-__cheetah_flush_tlb_mm: /* 19 insns */
- rdpr %pstate, %g7
- andn %g7, PSTATE_IE, %g2
- wrpr %g2, 0x0, %pstate
- wrpr %g0, 1, %tl
- mov PRIMARY_CONTEXT, %o2
- mov 0x40, %g3
- ldxa [%o2] ASI_DMMU, %g2
- srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o1
- sllx %o1, CTX_PGSZ1_NUC_SHIFT, %o1
- or %o0, %o1, %o0 /* Preserve nucleus page size fields */
- stxa %o0, [%o2] ASI_DMMU
- stxa %g0, [%g3] ASI_DMMU_DEMAP
- stxa %g0, [%g3] ASI_IMMU_DEMAP
- stxa %g2, [%o2] ASI_DMMU
- sethi %hi(KERNBASE), %o2
- flush %o2
- wrpr %g0, 0, %tl
- retl
- wrpr %g7, 0x0, %pstate
-
-__cheetah_flush_tlb_pending: /* 27 insns */
- /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
- rdpr %pstate, %g7
- sllx %o1, 3, %o1
- andn %g7, PSTATE_IE, %g2
- wrpr %g2, 0x0, %pstate
- wrpr %g0, 1, %tl
- mov PRIMARY_CONTEXT, %o4
- ldxa [%o4] ASI_DMMU, %g2
- srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o3
- sllx %o3, CTX_PGSZ1_NUC_SHIFT, %o3
- or %o0, %o3, %o0 /* Preserve nucleus page size fields */
- stxa %o0, [%o4] ASI_DMMU
-1: sub %o1, (1 << 3), %o1
- ldx [%o2 + %o1], %o3
- andcc %o3, 1, %g0
- be,pn %icc, 2f
- andn %o3, 1, %o3
- stxa %g0, [%o3] ASI_IMMU_DEMAP
-2: stxa %g0, [%o3] ASI_DMMU_DEMAP
- membar #Sync
- brnz,pt %o1, 1b
- nop
- stxa %g2, [%o4] ASI_DMMU
- sethi %hi(KERNBASE), %o4
- flush %o4
- wrpr %g0, 0, %tl
- retl
- wrpr %g7, 0x0, %pstate
-
-#ifdef DCACHE_ALIASING_POSSIBLE
-__cheetah_flush_dcache_page: /* 11 insns */
- sethi %uhi(PAGE_OFFSET), %g1
- sllx %g1, 32, %g1
- sub %o0, %g1, %o0
- sethi %hi(PAGE_SIZE), %o4
-1: subcc %o4, (1 << 5), %o4
- stxa %g0, [%o0 + %o4] ASI_DCACHE_INVALIDATE
- membar #Sync
- bne,pt %icc, 1b
- nop
- retl /* I-cache flush never needed on Cheetah, see callers. */
- nop
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- /* Hypervisor specific versions, patched at boot time. */
-__hypervisor_tlb_tl0_error:
- save %sp, -192, %sp
- mov %i0, %o0
- call hypervisor_tlbop_error
- mov %i1, %o1
- ret
- restore
-
-__hypervisor_flush_tlb_mm: /* 10 insns */
- mov %o0, %o2 /* ARG2: mmu context */
- mov 0, %o0 /* ARG0: CPU lists unimplemented */
- mov 0, %o1 /* ARG1: CPU lists unimplemented */
- mov HV_MMU_ALL, %o3 /* ARG3: flags */
- mov HV_FAST_MMU_DEMAP_CTX, %o5
- ta HV_FAST_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
- mov HV_FAST_MMU_DEMAP_CTX, %o1
- retl
- nop
-
-__hypervisor_flush_tlb_pending: /* 16 insns */
- /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
- sllx %o1, 3, %g1
- mov %o2, %g2
- mov %o0, %g3
-1: sub %g1, (1 << 3), %g1
- ldx [%g2 + %g1], %o0 /* ARG0: vaddr + IMMU-bit */
- mov %g3, %o1 /* ARG1: mmu context */
- mov HV_MMU_ALL, %o2 /* ARG2: flags */
- srlx %o0, PAGE_SHIFT, %o0
- sllx %o0, PAGE_SHIFT, %o0
- ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
- mov HV_MMU_UNMAP_ADDR_TRAP, %o1
- brnz,pt %g1, 1b
- nop
- retl
- nop
-
-__hypervisor_flush_tlb_kernel_range: /* 16 insns */
- /* %o0=start, %o1=end */
- cmp %o0, %o1
- be,pn %xcc, 2f
- sethi %hi(PAGE_SIZE), %g3
- mov %o0, %g1
- sub %o1, %g1, %g2
- sub %g2, %g3, %g2
-1: add %g1, %g2, %o0 /* ARG0: virtual address */
- mov 0, %o1 /* ARG1: mmu context */
- mov HV_MMU_ALL, %o2 /* ARG2: flags */
- ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
- mov HV_MMU_UNMAP_ADDR_TRAP, %o1
- brnz,pt %g2, 1b
- sub %g2, %g3, %g2
-2: retl
- nop
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- /* XXX Niagara and friends have an 8K cache, so no aliasing is
- * XXX possible, but nothing explicit in the Hypervisor API
- * XXX guarantees this.
- */
-__hypervisor_flush_dcache_page: /* 2 insns */
- retl
- nop
-#endif
-
-tlb_patch_one:
-1: lduw [%o1], %g1
- stw %g1, [%o0]
- flush %o0
- subcc %o2, 1, %o2
- add %o1, 4, %o1
- bne,pt %icc, 1b
- add %o0, 4, %o0
- retl
- nop
-
- .globl cheetah_patch_cachetlbops
-cheetah_patch_cachetlbops:
- save %sp, -128, %sp
-
- sethi %hi(__flush_tlb_mm), %o0
- or %o0, %lo(__flush_tlb_mm), %o0
- sethi %hi(__cheetah_flush_tlb_mm), %o1
- or %o1, %lo(__cheetah_flush_tlb_mm), %o1
- call tlb_patch_one
- mov 19, %o2
-
- sethi %hi(__flush_tlb_pending), %o0
- or %o0, %lo(__flush_tlb_pending), %o0
- sethi %hi(__cheetah_flush_tlb_pending), %o1
- or %o1, %lo(__cheetah_flush_tlb_pending), %o1
- call tlb_patch_one
- mov 27, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- sethi %hi(__flush_dcache_page), %o0
- or %o0, %lo(__flush_dcache_page), %o0
- sethi %hi(__cheetah_flush_dcache_page), %o1
- or %o1, %lo(__cheetah_flush_dcache_page), %o1
- call tlb_patch_one
- mov 11, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- ret
- restore
-
-#ifdef CONFIG_SMP
- /* These are all called by the slaves of a cross call, at
- * trap level 1, with interrupts fully disabled.
- *
- * Register usage:
- * %g5 mm->context (all tlb flushes)
- * %g1 address arg 1 (tlb page and range flushes)
- * %g7 address arg 2 (tlb range flush only)
- *
- * %g6 scratch 1
- * %g2 scratch 2
- * %g3 scratch 3
- * %g4 scratch 4
- */
- .align 32
- .globl xcall_flush_tlb_mm
-xcall_flush_tlb_mm: /* 21 insns */
- mov PRIMARY_CONTEXT, %g2
- ldxa [%g2] ASI_DMMU, %g3
- srlx %g3, CTX_PGSZ1_NUC_SHIFT, %g4
- sllx %g4, CTX_PGSZ1_NUC_SHIFT, %g4
- or %g5, %g4, %g5 /* Preserve nucleus page size fields */
- stxa %g5, [%g2] ASI_DMMU
- mov 0x40, %g4
- stxa %g0, [%g4] ASI_DMMU_DEMAP
- stxa %g0, [%g4] ASI_IMMU_DEMAP
- stxa %g3, [%g2] ASI_DMMU
- retry
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
-
- .globl xcall_flush_tlb_pending
-xcall_flush_tlb_pending: /* 21 insns */
- /* %g5=context, %g1=nr, %g7=vaddrs[] */
- sllx %g1, 3, %g1
- mov PRIMARY_CONTEXT, %g4
- ldxa [%g4] ASI_DMMU, %g2
- srlx %g2, CTX_PGSZ1_NUC_SHIFT, %g4
- sllx %g4, CTX_PGSZ1_NUC_SHIFT, %g4
- or %g5, %g4, %g5
- mov PRIMARY_CONTEXT, %g4
- stxa %g5, [%g4] ASI_DMMU
-1: sub %g1, (1 << 3), %g1
- ldx [%g7 + %g1], %g5
- andcc %g5, 0x1, %g0
- be,pn %icc, 2f
-
- andn %g5, 0x1, %g5
- stxa %g0, [%g5] ASI_IMMU_DEMAP
-2: stxa %g0, [%g5] ASI_DMMU_DEMAP
- membar #Sync
- brnz,pt %g1, 1b
- nop
- stxa %g2, [%g4] ASI_DMMU
- retry
- nop
-
- .globl xcall_flush_tlb_kernel_range
-xcall_flush_tlb_kernel_range: /* 25 insns */
- sethi %hi(PAGE_SIZE - 1), %g2
- or %g2, %lo(PAGE_SIZE - 1), %g2
- andn %g1, %g2, %g1
- andn %g7, %g2, %g7
- sub %g7, %g1, %g3
- add %g2, 1, %g2
- sub %g3, %g2, %g3
- or %g1, 0x20, %g1 ! Nucleus
-1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
- stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP
- membar #Sync
- brnz,pt %g3, 1b
- sub %g3, %g2, %g3
- retry
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
-
- /* This runs in a very controlled environment, so we do
- * not need to worry about BH races etc.
- */
- .globl xcall_sync_tick
-xcall_sync_tick:
-
-661: rdpr %pstate, %g2
- wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- nop
- nop
- .previous
-
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- sethi %hi(109f), %g7
- b,pt %xcc, etrap_irq
-109: or %g7, %lo(109b), %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- call smp_synchronize_tick_client
- nop
- b rtrap_xcall
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-
- .globl xcall_fetch_glob_regs
-xcall_fetch_glob_regs:
- sethi %hi(global_reg_snapshot), %g1
- or %g1, %lo(global_reg_snapshot), %g1
- __GET_CPUID(%g2)
- sllx %g2, 6, %g3
- add %g1, %g3, %g1
- rdpr %tstate, %g7
- stx %g7, [%g1 + GR_SNAP_TSTATE]
- rdpr %tpc, %g7
- stx %g7, [%g1 + GR_SNAP_TPC]
- rdpr %tnpc, %g7
- stx %g7, [%g1 + GR_SNAP_TNPC]
- stx %o7, [%g1 + GR_SNAP_O7]
- stx %i7, [%g1 + GR_SNAP_I7]
- /* Don't try this at home kids... */
- rdpr %cwp, %g2
- sub %g2, 1, %g7
- wrpr %g7, %cwp
- mov %i7, %g7
- wrpr %g2, %cwp
- stx %g7, [%g1 + GR_SNAP_RPC]
- sethi %hi(trap_block), %g7
- or %g7, %lo(trap_block), %g7
- sllx %g2, TRAP_BLOCK_SZ_SHIFT, %g2
- add %g7, %g2, %g7
- ldx [%g7 + TRAP_PER_CPU_THREAD], %g3
- membar #StoreStore
- stx %g3, [%g1 + GR_SNAP_THREAD]
- retry
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- .align 32
- .globl xcall_flush_dcache_page_cheetah
-xcall_flush_dcache_page_cheetah: /* %g1 == physical page address */
- sethi %hi(PAGE_SIZE), %g3
-1: subcc %g3, (1 << 5), %g3
- stxa %g0, [%g1 + %g3] ASI_DCACHE_INVALIDATE
- membar #Sync
- bne,pt %icc, 1b
- nop
- retry
- nop
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- .globl xcall_flush_dcache_page_spitfire
-xcall_flush_dcache_page_spitfire: /* %g1 == physical page address
- %g7 == kernel page virtual address
- %g5 == (page->mapping != NULL) */
-#ifdef DCACHE_ALIASING_POSSIBLE
- srlx %g1, (13 - 2), %g1 ! Form tag comparitor
- sethi %hi(L1DCACHE_SIZE), %g3 ! D$ size == 16K
- sub %g3, (1 << 5), %g3 ! D$ linesize == 32
-1: ldxa [%g3] ASI_DCACHE_TAG, %g2
- andcc %g2, 0x3, %g0
- be,pn %xcc, 2f
- andn %g2, 0x3, %g2
- cmp %g2, %g1
-
- bne,pt %xcc, 2f
- nop
- stxa %g0, [%g3] ASI_DCACHE_TAG
- membar #Sync
-2: cmp %g3, 0
- bne,pt %xcc, 1b
- sub %g3, (1 << 5), %g3
-
- brz,pn %g5, 2f
-#endif /* DCACHE_ALIASING_POSSIBLE */
- sethi %hi(PAGE_SIZE), %g3
-
-1: flush %g7
- subcc %g3, (1 << 5), %g3
- bne,pt %icc, 1b
- add %g7, (1 << 5), %g7
-
-2: retry
- nop
- nop
-
- /* %g5: error
- * %g6: tlb op
- */
-__hypervisor_tlb_xcall_error:
- mov %g5, %g4
- mov %g6, %g5
- ba,pt %xcc, etrap
- rd %pc, %g7
- mov %l4, %o0
- call hypervisor_tlbop_error_xcall
- mov %l5, %o1
- ba,a,pt %xcc, rtrap
-
- .globl __hypervisor_xcall_flush_tlb_mm
-__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
- /* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
- mov %o0, %g2
- mov %o1, %g3
- mov %o2, %g4
- mov %o3, %g1
- mov %o5, %g7
- clr %o0 /* ARG0: CPU lists unimplemented */
- clr %o1 /* ARG1: CPU lists unimplemented */
- mov %g5, %o2 /* ARG2: mmu context */
- mov HV_MMU_ALL, %o3 /* ARG3: flags */
- mov HV_FAST_MMU_DEMAP_CTX, %o5
- ta HV_FAST_TRAP
- mov HV_FAST_MMU_DEMAP_CTX, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
- mov %o0, %g5
- mov %g2, %o0
- mov %g3, %o1
- mov %g4, %o2
- mov %g1, %o3
- mov %g7, %o5
- membar #Sync
- retry
-
- .globl __hypervisor_xcall_flush_tlb_pending
-__hypervisor_xcall_flush_tlb_pending: /* 21 insns */
- /* %g5=ctx, %g1=nr, %g7=vaddrs[], %g2,%g3,%g4,g6=scratch */
- sllx %g1, 3, %g1
- mov %o0, %g2
- mov %o1, %g3
- mov %o2, %g4
-1: sub %g1, (1 << 3), %g1
- ldx [%g7 + %g1], %o0 /* ARG0: virtual address */
- mov %g5, %o1 /* ARG1: mmu context */
- mov HV_MMU_ALL, %o2 /* ARG2: flags */
- srlx %o0, PAGE_SHIFT, %o0
- sllx %o0, PAGE_SHIFT, %o0
- ta HV_MMU_UNMAP_ADDR_TRAP
- mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,a,pn %o0, __hypervisor_tlb_xcall_error
- mov %o0, %g5
- brnz,pt %g1, 1b
- nop
- mov %g2, %o0
- mov %g3, %o1
- mov %g4, %o2
- membar #Sync
- retry
-
- .globl __hypervisor_xcall_flush_tlb_kernel_range
-__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
- /* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
- sethi %hi(PAGE_SIZE - 1), %g2
- or %g2, %lo(PAGE_SIZE - 1), %g2
- andn %g1, %g2, %g1
- andn %g7, %g2, %g7
- sub %g7, %g1, %g3
- add %g2, 1, %g2
- sub %g3, %g2, %g3
- mov %o0, %g2
- mov %o1, %g4
- mov %o2, %g7
-1: add %g1, %g3, %o0 /* ARG0: virtual address */
- mov 0, %o1 /* ARG1: mmu context */
- mov HV_MMU_ALL, %o2 /* ARG2: flags */
- ta HV_MMU_UNMAP_ADDR_TRAP
- mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
- mov %o0, %g5
- sethi %hi(PAGE_SIZE), %o2
- brnz,pt %g3, 1b
- sub %g3, %o2, %g3
- mov %g2, %o0
- mov %g4, %o1
- mov %g7, %o2
- membar #Sync
- retry
-
- /* These just get rescheduled to PIL vectors. */
- .globl xcall_call_function
-xcall_call_function:
- wr %g0, (1 << PIL_SMP_CALL_FUNC), %set_softint
- retry
-
- .globl xcall_call_function_single
-xcall_call_function_single:
- wr %g0, (1 << PIL_SMP_CALL_FUNC_SNGL), %set_softint
- retry
-
- .globl xcall_receive_signal
-xcall_receive_signal:
- wr %g0, (1 << PIL_SMP_RECEIVE_SIGNAL), %set_softint
- retry
-
- .globl xcall_capture
-xcall_capture:
- wr %g0, (1 << PIL_SMP_CAPTURE), %set_softint
- retry
-
- .globl xcall_new_mmu_context_version
-xcall_new_mmu_context_version:
- wr %g0, (1 << PIL_SMP_CTX_NEW_VERSION), %set_softint
- retry
-
-#ifdef CONFIG_KGDB
- .globl xcall_kgdb_capture
-xcall_kgdb_capture:
-661: rdpr %pstate, %g2
- wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate
- .section .sun4v_2insn_patch, "ax"
- .word 661b
- nop
- nop
- .previous
-
- rdpr %pil, %g2
- wrpr %g0, 15, %pil
- sethi %hi(109f), %g7
- ba,pt %xcc, etrap_irq
-109: or %g7, %lo(109b), %g7
-#ifdef CONFIG_TRACE_IRQFLAGS
- call trace_hardirqs_off
- nop
-#endif
- call smp_kgdb_capture_client
- add %sp, PTREGS_OFF, %o0
- /* Has to be a non-v9 branch due to the large distance. */
- ba rtrap_xcall
- ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
-#endif
-
-#endif /* CONFIG_SMP */
-
-
- .globl hypervisor_patch_cachetlbops
-hypervisor_patch_cachetlbops:
- save %sp, -128, %sp
-
- sethi %hi(__flush_tlb_mm), %o0
- or %o0, %lo(__flush_tlb_mm), %o0
- sethi %hi(__hypervisor_flush_tlb_mm), %o1
- or %o1, %lo(__hypervisor_flush_tlb_mm), %o1
- call tlb_patch_one
- mov 10, %o2
-
- sethi %hi(__flush_tlb_pending), %o0
- or %o0, %lo(__flush_tlb_pending), %o0
- sethi %hi(__hypervisor_flush_tlb_pending), %o1
- or %o1, %lo(__hypervisor_flush_tlb_pending), %o1
- call tlb_patch_one
- mov 16, %o2
-
- sethi %hi(__flush_tlb_kernel_range), %o0
- or %o0, %lo(__flush_tlb_kernel_range), %o0
- sethi %hi(__hypervisor_flush_tlb_kernel_range), %o1
- or %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
- call tlb_patch_one
- mov 16, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- sethi %hi(__flush_dcache_page), %o0
- or %o0, %lo(__flush_dcache_page), %o0
- sethi %hi(__hypervisor_flush_dcache_page), %o1
- or %o1, %lo(__hypervisor_flush_dcache_page), %o1
- call tlb_patch_one
- mov 2, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
-#ifdef CONFIG_SMP
- sethi %hi(xcall_flush_tlb_mm), %o0
- or %o0, %lo(xcall_flush_tlb_mm), %o0
- sethi %hi(__hypervisor_xcall_flush_tlb_mm), %o1
- or %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
- call tlb_patch_one
- mov 21, %o2
-
- sethi %hi(xcall_flush_tlb_pending), %o0
- or %o0, %lo(xcall_flush_tlb_pending), %o0
- sethi %hi(__hypervisor_xcall_flush_tlb_pending), %o1
- or %o1, %lo(__hypervisor_xcall_flush_tlb_pending), %o1
- call tlb_patch_one
- mov 21, %o2
-
- sethi %hi(xcall_flush_tlb_kernel_range), %o0
- or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
- sethi %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
- or %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
- call tlb_patch_one
- mov 25, %o2
-#endif /* CONFIG_SMP */
-
- ret
- restore
diff --git a/arch/sparc64/oprofile/Makefile b/arch/sparc64/oprofile/Makefile
deleted file mode 100644
index e9feca1ca28b..000000000000
--- a/arch/sparc64/oprofile/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-obj-$(CONFIG_OPROFILE) += oprofile.o
-
-DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
- oprof.o cpu_buffer.o buffer_sync.o \
- event_buffer.o oprofile_files.o \
- oprofilefs.o oprofile_stats.o \
- timer_int.o )
-
-oprofile-y := $(DRIVER_OBJS) init.o
diff --git a/arch/sparc64/oprofile/init.c b/arch/sparc64/oprofile/init.c
deleted file mode 100644
index 17bb6035069b..000000000000
--- a/arch/sparc64/oprofile/init.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @file init.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon <levon@movementarian.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-
-int __init oprofile_arch_init(struct oprofile_operations *ops)
-{
- return -ENODEV;
-}
-
-
-void oprofile_arch_exit(void)
-{
-}
diff --git a/arch/sparc64/prom/Makefile b/arch/sparc64/prom/Makefile
deleted file mode 100644
index 8c94483ca54d..000000000000
--- a/arch/sparc64/prom/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# Makefile for the Sun Boot PROM interface library under
-# Linux.
-#
-
-EXTRA_AFLAGS := -ansi
-EXTRA_CFLAGS := -Werror
-
-lib-y := bootstr.o devops.o init.o misc.o \
- tree.o console.o printf.o p1275.o cif.o
diff --git a/arch/sparc64/prom/printf.c b/arch/sparc64/prom/printf.c
deleted file mode 100644
index 660943ee4c2a..000000000000
--- a/arch/sparc64/prom/printf.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * printf.c: Internal prom library printf facility.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com)
- *
- * We used to warn all over the code: DO NOT USE prom_printf(),
- * and yet people do. Anton's banking code was outputting banks
- * with prom_printf for most of the 2.4 lifetime. Since an effective
- * stick is not available, we deployed a carrot: an early printk
- * through PROM by means of -p boot option. This ought to fix it.
- * USE printk; if you need, deploy -p.
- */
-
-#include <linux/kernel.h>
-
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-
-static char ppbuf[1024];
-
-void
-prom_write(const char *buf, unsigned int n)
-{
- char ch;
-
- while (n != 0) {
- --n;
- if ((ch = *buf++) == '\n')
- prom_putchar('\r');
- prom_putchar(ch);
- }
-}
-
-void
-prom_printf(const char *fmt, ...)
-{
- va_list args;
- int i;
-
- va_start(args, fmt);
- i = vscnprintf(ppbuf, sizeof(ppbuf), fmt, args);
- va_end(args);
-
- prom_write(ppbuf, i);
-}
diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c
index d53ff52bb404..b4a1522f2157 100644
--- a/arch/um/drivers/daemon_kern.c
+++ b/arch/um/drivers/daemon_kern.c
@@ -22,7 +22,7 @@ static void daemon_init(struct net_device *dev, void *data)
struct daemon_data *dpri;
struct daemon_init *init = data;
- pri = dev->priv;
+ pri = netdev_priv(dev);
dpri = (struct daemon_data *) pri->user;
dpri->sock_type = init->sock_type;
dpri->ctl_sock = init->ctl_sock;
diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c
index 8c4378a76d63..ffc6416d5ed7 100644
--- a/arch/um/drivers/mcast_kern.c
+++ b/arch/um/drivers/mcast_kern.c
@@ -28,7 +28,7 @@ static void mcast_init(struct net_device *dev, void *data)
struct mcast_data *dpri;
struct mcast_init *init = data;
- pri = dev->priv;
+ pri = netdev_priv(dev);
dpri = (struct mcast_data *) pri->user;
dpri->addr = init->addr;
dpri->port = init->port;
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 8f44ebb0dec8..e14629c87de4 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -161,7 +161,8 @@ void mconsole_proc(struct mc_request *req)
goto out_kill;
}
- file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
+ file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
+ current_cred());
if (IS_ERR(file)) {
mconsole_reply(req, "Failed to open file", 1, 0);
goto out_kill;
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 5b4ca8d93682..fde510b664d3 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -76,7 +76,7 @@ out:
static int uml_net_rx(struct net_device *dev)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
int pkt_len;
struct sk_buff *skb;
@@ -119,7 +119,7 @@ static void uml_dev_close(struct work_struct *work)
static irqreturn_t uml_net_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
int err;
if (!netif_running(dev))
@@ -150,7 +150,7 @@ out:
static int uml_net_open(struct net_device *dev)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
int err;
if (lp->fd >= 0) {
@@ -195,7 +195,7 @@ out:
static int uml_net_close(struct net_device *dev)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
netif_stop_queue(dev);
@@ -213,7 +213,7 @@ static int uml_net_close(struct net_device *dev)
static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
unsigned long flags;
int len;
@@ -250,7 +250,7 @@ static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *uml_net_get_stats(struct net_device *dev)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
return &lp->stats;
}
@@ -267,7 +267,7 @@ static void uml_net_tx_timeout(struct net_device *dev)
static int uml_net_set_mac(struct net_device *dev, void *addr)
{
- struct uml_net_private *lp = dev->priv;
+ struct uml_net_private *lp = netdev_priv(dev);
struct sockaddr *hwaddr = addr;
spin_lock_irq(&lp->lock);
@@ -368,7 +368,7 @@ static void net_device_release(struct device *dev)
{
struct uml_net *device = dev->driver_data;
struct net_device *netdev = device->dev;
- struct uml_net_private *lp = netdev->priv;
+ struct uml_net_private *lp = netdev_priv(netdev);
if (lp->remove != NULL)
(*lp->remove)(&lp->user);
@@ -418,14 +418,9 @@ static void eth_configure(int n, void *init, char *mac,
setup_etheraddr(mac, device->mac, dev->name);
- printk(KERN_INFO "Netdevice %d ", n);
- printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
- device->mac[0], device->mac[1],
- device->mac[2], device->mac[3],
- device->mac[4], device->mac[5]);
- printk(": ");
+ printk(KERN_INFO "Netdevice %d (%pM) : ", n, device->mac);
- lp = dev->priv;
+ lp = netdev_priv(dev);
/* This points to the transport private data. It's still clear, but we
* must memset it to 0 *now*. Let's help the drivers. */
memset(lp, 0, size);
@@ -735,7 +730,7 @@ static int net_remove(int n, char **error_out)
return -ENODEV;
dev = device->dev;
- lp = dev->priv;
+ lp = netdev_priv(dev);
if (lp->fd > 0)
return -EBUSY;
unregister_netdev(dev);
@@ -766,7 +761,7 @@ static int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
if (dev->open != uml_net_open)
return NOTIFY_DONE;
- lp = dev->priv;
+ lp = netdev_priv(dev);
proc = NULL;
switch (event) {
diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c
index 3a750dd39be1..2860525f8ff6 100644
--- a/arch/um/drivers/pcap_kern.c
+++ b/arch/um/drivers/pcap_kern.c
@@ -21,7 +21,7 @@ void pcap_init(struct net_device *dev, void *data)
struct pcap_data *ppri;
struct pcap_init *init = data;
- pri = dev->priv;
+ pri = netdev_priv(dev);
ppri = (struct pcap_data *) pri->user;
ppri->host_if = init->host_if;
ppri->promisc = init->promisc;
diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c
index d19faec7046e..5ec17563142e 100644
--- a/arch/um/drivers/slip_kern.c
+++ b/arch/um/drivers/slip_kern.c
@@ -19,7 +19,7 @@ static void slip_init(struct net_device *dev, void *data)
struct slip_data *spri;
struct slip_init *init = data;
- private = dev->priv;
+ private = netdev_priv(dev);
spri = (struct slip_data *) private->user;
memset(spri->name, 0, sizeof(spri->name));
diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c
index d987af277db9..f15a6e7654f3 100644
--- a/arch/um/drivers/slirp_kern.c
+++ b/arch/um/drivers/slirp_kern.c
@@ -22,7 +22,7 @@ void slirp_init(struct net_device *dev, void *data)
struct slirp_init *init = data;
int i;
- private = dev->priv;
+ private = netdev_priv(dev);
spri = (struct slirp_data *) private->user;
spri->argw = init->argw;
diff --git a/arch/um/drivers/vde_kern.c b/arch/um/drivers/vde_kern.c
index add7e722defb..1b852bffdebc 100644
--- a/arch/um/drivers/vde_kern.c
+++ b/arch/um/drivers/vde_kern.c
@@ -19,7 +19,7 @@ static void vde_init(struct net_device *dev, void *data)
struct uml_net_private *pri;
struct vde_data *vpri;
- pri = dev->priv;
+ pri = netdev_priv(dev);
vpri = (struct vde_data *) pri->user;
vpri->vde_switch = init->vde_switch;
diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c
index 046a131f6104..7f6f9a71aae4 100644
--- a/arch/um/os-Linux/drivers/ethertap_kern.c
+++ b/arch/um/os-Linux/drivers/ethertap_kern.c
@@ -22,7 +22,7 @@ static void etap_init(struct net_device *dev, void *data)
struct ethertap_data *epri;
struct ethertap_init *init = data;
- pri = dev->priv;
+ pri = netdev_priv(dev);
epri = (struct ethertap_data *) pri->user;
epri->dev_name = init->dev_name;
epri->gate_addr = init->gate_addr;
diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c
index 6b9e33d5de20..4048800e4696 100644
--- a/arch/um/os-Linux/drivers/tuntap_kern.c
+++ b/arch/um/os-Linux/drivers/tuntap_kern.c
@@ -21,7 +21,7 @@ static void tuntap_init(struct net_device *dev, void *data)
struct tuntap_data *tpri;
struct tuntap_init *init = data;
- pri = dev->priv;
+ pri = netdev_priv(dev);
tpri = (struct tuntap_data *) pri->user;
tpri->dev_name = init->dev_name;
tpri->fixed_config = (init->dev_name != NULL);
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0ca2eb7573cd..249d1e0824b5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -19,6 +19,8 @@ config X86_64
config X86
def_bool y
select HAVE_AOUT if X86_32
+ select HAVE_READQ
+ select HAVE_WRITEQ
select HAVE_UNSTABLE_SCHED_CLOCK
select HAVE_IDE
select HAVE_OPROFILE
@@ -90,6 +92,10 @@ config GENERIC_IOMAP
config GENERIC_BUG
def_bool y
depends on BUG
+ select GENERIC_BUG_RELATIVE_POINTERS if X86_64
+
+config GENERIC_BUG_RELATIVE_POINTERS
+ bool
config GENERIC_HWEIGHT
def_bool y
@@ -244,16 +250,19 @@ config X86_HAS_BOOT_CPU_ID
config SPARSE_IRQ
bool "Support sparse irq numbering"
depends on PCI_MSI || HT_IRQ
- default y
help
- This enables support for sparse irq, esp for msi/msi-x. You may need
- if you have lots of cards supports msi-x installed.
+ This enables support for sparse irqs. This is useful for distro
+ kernels that want to define a high CONFIG_NR_CPUS value but still
+ want to have low kernel memory footprint on smaller machines.
- If you don't know what to do here, say Y.
+ ( Sparse IRQs can also be beneficial on NUMA boxes, as they spread
+ out the irq_desc[] array in a more NUMA-friendly way. )
+
+ If you don't know what to do here, say N.
config NUMA_MIGRATE_IRQ_DESC
bool "Move irq desc when changing irq smp_affinity"
- depends on SPARSE_IRQ && SMP
+ depends on SPARSE_IRQ && NUMA
default n
help
This enables moving irq_desc to cpu/node that irq will use handled.
@@ -264,21 +273,13 @@ config X86_FIND_SMP_CONFIG
def_bool y
depends on X86_MPPARSE || X86_VOYAGER
-if ACPI
config X86_MPPARSE
- def_bool y
- bool "Enable MPS table"
+ bool "Enable MPS table" if ACPI
+ default y
depends on X86_LOCAL_APIC
help
For old smp systems that do not have proper acpi support. Newer systems
(esp with 64bit cpus) with acpi support, MADT and DSDT will override it
-endif
-
-if !ACPI
-config X86_MPPARSE
- def_bool y
- depends on X86_LOCAL_APIC
-endif
choice
prompt "Subarchitecture Type"
@@ -500,7 +501,7 @@ config HPET_TIMER
The HPET provides a stable time base on SMP
systems, unlike the TSC, but it is more expensive to access,
as it is off-chip. You can find the HPET spec at
- <http://www.intel.com/hardwaredesign/hpetspec.htm>.
+ <http://www.intel.com/hardwaredesign/hpetspec_1.pdf>.
You can safely choose Y here. However, HPET will only be
activated if the platform and the BIOS support this feature.
@@ -587,7 +588,7 @@ config AMD_IOMMU
# need this always selected by IOMMU for the VIA workaround
config SWIOTLB
- bool
+ 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
@@ -679,6 +680,30 @@ config X86_VISWS_APIC
def_bool y
depends on X86_32 && X86_VISWS
+config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
+ bool "Reroute for broken boot IRQs"
+ default n
+ depends on X86_IO_APIC
+ help
+ This option enables a workaround that fixes a source of
+ spurious interrupts. This is recommended when threaded
+ interrupt handling is used on systems where the generation of
+ superfluous "boot interrupts" cannot be disabled.
+
+ Some chipsets generate a legacy INTx "boot IRQ" when the IRQ
+ entry in the chipset's IO-APIC is masked (as, e.g. the RT
+ kernel does during interrupt handling). On chipsets where this
+ boot IRQ generation cannot be disabled, this workaround keeps
+ the original IRQ line masked so that only the equivalent "boot
+ IRQ" is delivered to the CPUs. The workaround also tells the
+ kernel to set up the IRQ handler on the boot IRQ line. In this
+ way only one interrupt is delivered to the kernel. Otherwise
+ the spurious second interrupt may cause the kernel to bring
+ down (vital) interrupt lines.
+
+ Only affects "broken" chipsets. Interrupt sharing may be
+ increased on these systems.
+
config X86_MCE
bool "Machine Check Exception"
depends on !X86_VOYAGER
@@ -975,24 +1000,37 @@ config X86_PAE
config ARCH_PHYS_ADDR_T_64BIT
def_bool X86_64 || X86_PAE
+config DIRECT_GBPAGES
+ bool "Enable 1GB pages for kernel pagetables" if EMBEDDED
+ default y
+ depends on X86_64
+ help
+ Allow the kernel linear mapping to use 1GB pages on CPUs that
+ support it. This can improve the kernel's performance a tiny bit by
+ reducing TLB pressure. If in doubt, say "Y".
+
# Common NUMA Features
config NUMA
- bool "Numa Memory Allocation and Scheduler Support (EXPERIMENTAL)"
+ bool "Numa Memory Allocation and Scheduler Support"
depends on SMP
depends on X86_64 || (X86_32 && HIGHMEM64G && (X86_NUMAQ || X86_BIGSMP || X86_SUMMIT && ACPI) && EXPERIMENTAL)
default n if X86_PC
default y if (X86_NUMAQ || X86_SUMMIT || X86_BIGSMP)
help
Enable NUMA (Non Uniform Memory Access) support.
+
The kernel will try to allocate memory used by a CPU on the
local memory controller of the CPU and add some more
NUMA awareness to the kernel.
- For 32-bit this is currently highly experimental and should be only
- used for kernel development. It might also cause boot failures.
- For 64-bit this is recommended on all multiprocessor Opteron systems.
- If the system is EM64T, you should say N unless your system is
- EM64T NUMA.
+ For 64-bit this is recommended if the system is Intel Core i7
+ (or later), AMD Opteron, or EM64T NUMA.
+
+ For 32-bit this is only needed on (rare) 32-bit-only platforms
+ that support NUMA topologies, such as NUMAQ / Summit, or if you
+ boot a 32-bit kernel on a 64-bit NUMA platform.
+
+ Otherwise, you should say N.
comment "NUMA (Summit) requires SMP, 64GB highmem support, ACPI"
depends on X86_32 && X86_SUMMIT && (!HIGHMEM64G || !ACPI)
@@ -1512,6 +1550,10 @@ config ARCH_ENABLE_MEMORY_HOTPLUG
def_bool y
depends on X86_64 || (X86_32 && HIGHMEM)
+config ARCH_ENABLE_MEMORY_HOTREMOVE
+ def_bool y
+ depends on MEMORY_HOTPLUG
+
config HAVE_ARCH_EARLY_PFN_TO_NID
def_bool X86_64
depends on NUMA
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index fa013f529b74..10d6cc3fd052 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -114,18 +114,6 @@ config DEBUG_RODATA
data. This is recommended so that we can catch kernel bugs sooner.
If in doubt, say "Y".
-config DIRECT_GBPAGES
- bool "Enable gbpages-mapped kernel pagetables"
- depends on DEBUG_KERNEL && EXPERIMENTAL && X86_64
- help
- Enable gigabyte pages support (if the CPU supports it). This can
- improve the kernel's performance a tiny bit by reducing TLB
- pressure.
-
- This is experimental code.
-
- If in doubt, say "N".
-
config DEBUG_RODATA_TEST
bool "Testcase for the DEBUG_RODATA feature"
depends on DEBUG_RODATA
@@ -303,10 +291,10 @@ config OPTIMIZE_INLINING
developers have marked 'inline'. Doing so takes away freedom from gcc to
do what it thinks is best, which is desirable for the gcc 3.x series of
compilers. The gcc 4.x series have a rewritten inlining algorithm and
- disabling this option will generate a smaller kernel there. Hopefully
- this algorithm is so good that allowing gcc4 to make the decision can
- become the default in the future, until then this option is there to
- test gcc for this.
+ enabling this option will generate a smaller kernel there. Hopefully
+ this algorithm is so good that allowing gcc 4.x and above to make the
+ decision will become the default in the future. Until then this option
+ is there to test gcc for this.
If unsure, say N.
diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c
index b939cb476dec..5d4742ed4aa2 100644
--- a/arch/x86/boot/video-vga.c
+++ b/arch/x86/boot/video-vga.c
@@ -34,7 +34,7 @@ static struct mode_info cga_modes[] = {
{ VIDEO_80x25, 80, 25, 0 },
};
-__videocard video_vga;
+static __videocard video_vga;
/* Set basic 80x25 mode */
static u8 vga_set_basic_mode(void)
@@ -259,7 +259,7 @@ static int vga_probe(void)
return mode_count[adapter];
}
-__videocard video_vga = {
+static __videocard video_vga = {
.card_name = "VGA",
.probe = vga_probe,
.set_mode = vga_set_mode,
diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c
index 83598b23093a..3bef2c1febe9 100644
--- a/arch/x86/boot/video.c
+++ b/arch/x86/boot/video.c
@@ -226,7 +226,7 @@ static unsigned int mode_menu(void)
#ifdef CONFIG_VIDEO_RETAIN
/* Save screen content to the heap */
-struct saved_screen {
+static struct saved_screen {
int x, y;
int curx, cury;
u16 *data;
diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
index 13b8c86ae985..b30a08ed8eb4 100644
--- a/arch/x86/configs/i386_defconfig
+++ b/arch/x86/configs/i386_defconfig
@@ -77,7 +77,7 @@ CONFIG_AUDIT=y
CONFIG_AUDITSYSCALL=y
CONFIG_AUDIT_TREE=y
# CONFIG_IKCONFIG is not set
-CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_CGROUP_NS=y
@@ -298,7 +298,7 @@ CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
# CONFIG_KEXEC_JUMP is not set
CONFIG_PHYSICAL_START=0x1000000
-CONFIG_RELOCATABLE=y
+# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x200000
CONFIG_HOTPLUG_CPU=y
# CONFIG_COMPAT_VDSO is not set
diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
index f0a03d7a7d63..0e7dbc0a3e46 100644
--- a/arch/x86/configs/x86_64_defconfig
+++ b/arch/x86/configs/x86_64_defconfig
@@ -77,7 +77,7 @@ CONFIG_AUDIT=y
CONFIG_AUDITSYSCALL=y
CONFIG_AUDIT_TREE=y
# CONFIG_IKCONFIG is not set
-CONFIG_LOG_BUF_SHIFT=17
+CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_CGROUP_NS=y
@@ -298,7 +298,7 @@ CONFIG_SCHED_HRTICK=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_PHYSICAL_START=0x1000000
-CONFIG_RELOCATABLE=y
+# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x200000
CONFIG_HOTPLUG_CPU=y
# CONFIG_COMPAT_VDSO is not set
diff --git a/arch/x86/crypto/crc32c-intel.c b/arch/x86/crypto/crc32c-intel.c
index 070afc5b6c94..b9d00261703c 100644
--- a/arch/x86/crypto/crc32c-intel.c
+++ b/arch/x86/crypto/crc32c-intel.c
@@ -6,13 +6,22 @@
* Intel(R) 64 and IA-32 Architectures Software Developer's Manual
* Volume 2A: Instruction Set Reference, A-M
*
- * Copyright (c) 2008 Austin Zhang <austin_zhang@linux.intel.com>
- * Copyright (c) 2008 Kent Liu <kent.liu@intel.com>
+ * Copyright (C) 2008 Intel Corporation
+ * Authors: Austin Zhang <austin_zhang@linux.intel.com>
+ * Kent Liu <kent.liu@intel.com>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
+ * 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/init.h>
@@ -75,99 +84,92 @@ static u32 __pure crc32c_intel_le_hw(u32 crc, unsigned char const *p, size_t len
* If your algorithm starts with ~0, then XOR with ~0 before you set
* the seed.
*/
-static int crc32c_intel_setkey(struct crypto_ahash *hash, const u8 *key,
+static int crc32c_intel_setkey(struct crypto_shash *hash, const u8 *key,
unsigned int keylen)
{
- u32 *mctx = crypto_ahash_ctx(hash);
+ u32 *mctx = crypto_shash_ctx(hash);
if (keylen != sizeof(u32)) {
- crypto_ahash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ crypto_shash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
*mctx = le32_to_cpup((__le32 *)key);
return 0;
}
-static int crc32c_intel_init(struct ahash_request *req)
+static int crc32c_intel_init(struct shash_desc *desc)
{
- u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
- u32 *crcp = ahash_request_ctx(req);
+ u32 *mctx = crypto_shash_ctx(desc->tfm);
+ u32 *crcp = shash_desc_ctx(desc);
*crcp = *mctx;
return 0;
}
-static int crc32c_intel_update(struct ahash_request *req)
+static int crc32c_intel_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
{
- struct crypto_hash_walk walk;
- u32 *crcp = ahash_request_ctx(req);
- u32 crc = *crcp;
- int nbytes;
-
- for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
- nbytes = crypto_hash_walk_done(&walk, 0))
- crc = crc32c_intel_le_hw(crc, walk.data, nbytes);
+ u32 *crcp = shash_desc_ctx(desc);
- *crcp = crc;
+ *crcp = crc32c_intel_le_hw(*crcp, data, len);
return 0;
}
-static int crc32c_intel_final(struct ahash_request *req)
+static int __crc32c_intel_finup(u32 *crcp, const u8 *data, unsigned int len,
+ u8 *out)
{
- u32 *crcp = ahash_request_ctx(req);
-
- *(__le32 *)req->result = ~cpu_to_le32p(crcp);
+ *(__le32 *)out = ~cpu_to_le32(crc32c_intel_le_hw(*crcp, data, len));
return 0;
}
-static int crc32c_intel_digest(struct ahash_request *req)
+static int crc32c_intel_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
{
- struct crypto_hash_walk walk;
- u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
- u32 crc = *mctx;
- int nbytes;
+ return __crc32c_intel_finup(shash_desc_ctx(desc), data, len, out);
+}
- for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
- nbytes = crypto_hash_walk_done(&walk, 0))
- crc = crc32c_intel_le_hw(crc, walk.data, nbytes);
+static int crc32c_intel_final(struct shash_desc *desc, u8 *out)
+{
+ u32 *crcp = shash_desc_ctx(desc);
- *(__le32 *)req->result = ~cpu_to_le32(crc);
+ *(__le32 *)out = ~cpu_to_le32p(crcp);
return 0;
}
+static int crc32c_intel_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return __crc32c_intel_finup(crypto_shash_ctx(desc->tfm), data, len,
+ out);
+}
+
static int crc32c_intel_cra_init(struct crypto_tfm *tfm)
{
u32 *key = crypto_tfm_ctx(tfm);
*key = ~0;
- tfm->crt_ahash.reqsize = sizeof(u32);
-
return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "crc32c",
- .cra_driver_name = "crc32c-intel",
- .cra_priority = 200,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_ctxsize = sizeof(u32),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_init = crc32c_intel_cra_init,
- .cra_type = &crypto_ahash_type,
- .cra_u = {
- .ahash = {
- .digestsize = CHKSUM_DIGEST_SIZE,
- .setkey = crc32c_intel_setkey,
- .init = crc32c_intel_init,
- .update = crc32c_intel_update,
- .final = crc32c_intel_final,
- .digest = crc32c_intel_digest,
- }
+static struct shash_alg alg = {
+ .setkey = crc32c_intel_setkey,
+ .init = crc32c_intel_init,
+ .update = crc32c_intel_update,
+ .final = crc32c_intel_final,
+ .finup = crc32c_intel_finup,
+ .digest = crc32c_intel_digest,
+ .descsize = sizeof(u32),
+ .digestsize = CHKSUM_DIGEST_SIZE,
+ .base = {
+ .cra_name = "crc32c",
+ .cra_driver_name = "crc32c-intel",
+ .cra_priority = 200,
+ .cra_blocksize = CHKSUM_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(u32),
+ .cra_module = THIS_MODULE,
+ .cra_init = crc32c_intel_cra_init,
}
};
@@ -175,14 +177,14 @@ static struct crypto_alg alg = {
static int __init crc32c_intel_mod_init(void)
{
if (cpu_has_xmm4_2)
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
else
return -ENODEV;
}
static void __exit crc32c_intel_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(crc32c_intel_mod_init);
@@ -194,4 +196,3 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("crc32c");
MODULE_ALIAS("crc32c-intel");
-
diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c
index 127ec3f07214..2a4d073d2cf1 100644
--- a/arch/x86/ia32/ia32_aout.c
+++ b/arch/x86/ia32/ia32_aout.c
@@ -327,7 +327,7 @@ static int load_aout_binary(struct linux_binprm *bprm, struct pt_regs *regs)
current->mm->cached_hole_size = 0;
current->mm->mmap = NULL;
- compute_creds(bprm);
+ install_exec_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
if (N_MAGIC(ex) == OMAGIC) {
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index 4bc02b23674b..b195f85526e3 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -32,6 +32,8 @@
#include <asm/proto.h>
#include <asm/vdso.h>
+#include <asm/sigframe.h>
+
#define DEBUG_SIG 0
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
@@ -41,7 +43,6 @@
X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
X86_EFLAGS_CF)
-asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
@@ -173,47 +174,28 @@ asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
/*
* Do a signal return; undo the signal stack.
*/
+#define COPY(x) { \
+ err |= __get_user(regs->x, &sc->x); \
+}
-struct sigframe
-{
- u32 pretcode;
- int sig;
- struct sigcontext_ia32 sc;
- struct _fpstate_ia32 fpstate_unused; /* look at kernel/sigframe.h */
- unsigned int extramask[_COMPAT_NSIG_WORDS-1];
- char retcode[8];
- /* fp state follows here */
-};
-
-struct rt_sigframe
-{
- u32 pretcode;
- int sig;
- u32 pinfo;
- u32 puc;
- compat_siginfo_t info;
- struct ucontext_ia32 uc;
- char retcode[8];
- /* fp state follows here */
-};
-
-#define COPY(x) { \
- unsigned int reg; \
- err |= __get_user(reg, &sc->x); \
- regs->x = reg; \
+#define COPY_SEG_CPL3(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp | 3; \
}
-#define RELOAD_SEG(seg,mask) \
- { unsigned int cur; \
- unsigned short pre; \
- err |= __get_user(pre, &sc->seg); \
- savesegment(seg, cur); \
- pre |= mask; \
- if (pre != cur) loadsegment(seg, pre); }
+#define RELOAD_SEG(seg) { \
+ unsigned int cur, pre; \
+ err |= __get_user(pre, &sc->seg); \
+ savesegment(seg, cur); \
+ pre |= 3; \
+ if (pre != cur) \
+ loadsegment(seg, pre); \
+}
static int ia32_restore_sigcontext(struct pt_regs *regs,
struct sigcontext_ia32 __user *sc,
- unsigned int *peax)
+ unsigned int *pax)
{
unsigned int tmpflags, gs, oldgs, err = 0;
void __user *buf;
@@ -240,18 +222,16 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
if (gs != oldgs)
load_gs_index(gs);
- RELOAD_SEG(fs, 3);
- RELOAD_SEG(ds, 3);
- RELOAD_SEG(es, 3);
+ RELOAD_SEG(fs);
+ RELOAD_SEG(ds);
+ RELOAD_SEG(es);
COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
COPY(dx); COPY(cx); COPY(ip);
/* Don't touch extended registers */
- err |= __get_user(regs->cs, &sc->cs);
- regs->cs |= 3;
- err |= __get_user(regs->ss, &sc->ss);
- regs->ss |= 3;
+ COPY_SEG_CPL3(cs);
+ COPY_SEG_CPL3(ss);
err |= __get_user(tmpflags, &sc->flags);
regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
@@ -262,15 +242,13 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
buf = compat_ptr(tmp);
err |= restore_i387_xstate_ia32(buf);
- err |= __get_user(tmp, &sc->ax);
- *peax = tmp;
-
+ err |= __get_user(*pax, &sc->ax);
return err;
}
asmlinkage long sys32_sigreturn(struct pt_regs *regs)
{
- struct sigframe __user *frame = (struct sigframe __user *)(regs->sp-8);
+ struct sigframe_ia32 __user *frame = (struct sigframe_ia32 __user *)(regs->sp-8);
sigset_t set;
unsigned int ax;
@@ -300,12 +278,12 @@ badframe:
asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
{
- struct rt_sigframe __user *frame;
+ struct rt_sigframe_ia32 __user *frame;
sigset_t set;
unsigned int ax;
struct pt_regs tregs;
- frame = (struct rt_sigframe __user *)(regs->sp - 4);
+ frame = (struct rt_sigframe_ia32 __user *)(regs->sp - 4);
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
@@ -359,20 +337,15 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
err |= __put_user(regs->dx, &sc->dx);
err |= __put_user(regs->cx, &sc->cx);
err |= __put_user(regs->ax, &sc->ax);
- err |= __put_user(regs->cs, &sc->cs);
- err |= __put_user(regs->ss, &sc->ss);
err |= __put_user(current->thread.trap_no, &sc->trapno);
err |= __put_user(current->thread.error_code, &sc->err);
err |= __put_user(regs->ip, &sc->ip);
+ err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
err |= __put_user(regs->flags, &sc->flags);
err |= __put_user(regs->sp, &sc->sp_at_signal);
+ err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
- tmp = save_i387_xstate_ia32(fpstate);
- if (tmp < 0)
- err = -EFAULT;
- else
- err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
- &sc->fpstate);
+ err |= __put_user(ptr_to_compat(fpstate), &sc->fpstate);
/* non-iBCS2 extensions.. */
err |= __put_user(mask, &sc->oldmask);
@@ -400,7 +373,7 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
}
/* This is the legacy signal stack switching. */
- else if ((regs->ss & 0xffff) != __USER_DS &&
+ else if ((regs->ss & 0xffff) != __USER32_DS &&
!(ka->sa.sa_flags & SA_RESTORER) &&
ka->sa.sa_restorer)
sp = (unsigned long) ka->sa.sa_restorer;
@@ -408,6 +381,8 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
if (used_math()) {
sp = sp - sig_xstate_ia32_size;
*fpstate = (struct _fpstate_ia32 *) sp;
+ if (save_i387_xstate_ia32(*fpstate) < 0)
+ return (void __user *) -1L;
}
sp -= frame_size;
@@ -420,7 +395,7 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
int ia32_setup_frame(int sig, struct k_sigaction *ka,
compat_sigset_t *set, struct pt_regs *regs)
{
- struct sigframe __user *frame;
+ struct sigframe_ia32 __user *frame;
void __user *restorer;
int err = 0;
void __user *fpstate = NULL;
@@ -430,12 +405,10 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
u16 poplmovl;
u32 val;
u16 int80;
- u16 pad;
} __attribute__((packed)) code = {
0xb858, /* popl %eax ; movl $...,%eax */
__NR_ia32_sigreturn,
0x80cd, /* int $0x80 */
- 0,
};
frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
@@ -471,7 +444,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.
*/
- err |= __copy_to_user(frame->retcode, &code, 8);
+ err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
if (err)
return -EFAULT;
@@ -501,7 +474,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
compat_sigset_t *set, struct pt_regs *regs)
{
- struct rt_sigframe __user *frame;
+ struct rt_sigframe_ia32 __user *frame;
void __user *restorer;
int err = 0;
void __user *fpstate = NULL;
@@ -511,8 +484,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
u8 movl;
u32 val;
u16 int80;
- u16 pad;
- u8 pad2;
+ u8 pad;
} __attribute__((packed)) code = {
0xb8,
__NR_ia32_rt_sigreturn,
@@ -559,7 +531,7 @@ 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.
*/
- err |= __copy_to_user(frame->retcode, &code, 8);
+ err |= __put_user(*((u64 *)&code), (u64 *)frame->retcode);
if (err)
return -EFAULT;
@@ -572,11 +544,6 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->dx = (unsigned long) &frame->info;
regs->cx = (unsigned long) &frame->uc;
- /* Make -mregparm=3 work */
- regs->ax = sig;
- regs->dx = (unsigned long) &frame->info;
- regs->cx = (unsigned long) &frame->uc;
-
loadsegment(ds, __USER32_DS);
loadsegment(es, __USER32_DS);
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
index 360010322711..9fa9dcdf344b 100644
--- a/arch/x86/include/asm/bitops.h
+++ b/arch/x86/include/asm/bitops.h
@@ -168,7 +168,15 @@ static inline void __change_bit(int nr, volatile unsigned long *addr)
*/
static inline void change_bit(int nr, volatile unsigned long *addr)
{
- asm volatile(LOCK_PREFIX "btc %1,%0" : ADDR : "Ir" (nr));
+ if (IS_IMMEDIATE(nr)) {
+ asm volatile(LOCK_PREFIX "xorb %1,%0"
+ : CONST_MASK_ADDR(nr, addr)
+ : "iq" ((u8)CONST_MASK(nr)));
+ } else {
+ asm volatile(LOCK_PREFIX "btc %1,%0"
+ : BITOP_ADDR(addr)
+ : "Ir" (nr));
+ }
}
/**
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index 3def2065fcea..d9cf1cd156d2 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -9,7 +9,7 @@
#ifdef CONFIG_X86_32
# define __BUG_C0 "2:\t.long 1b, %c0\n"
#else
-# define __BUG_C0 "2:\t.quad 1b, %c0\n"
+# define __BUG_C0 "2:\t.long 1b - 2b, %c0 - 2b\n"
#endif
#define BUG() \
diff --git a/arch/x86/include/asm/byteorder.h b/arch/x86/include/asm/byteorder.h
index e02ae2d89acf..f110ad417df3 100644
--- a/arch/x86/include/asm/byteorder.h
+++ b/arch/x86/include/asm/byteorder.h
@@ -4,26 +4,33 @@
#include <asm/types.h>
#include <linux/compiler.h>
-#ifdef __GNUC__
+#define __LITTLE_ENDIAN
-#ifdef __i386__
-
-static inline __attribute_const__ __u32 ___arch__swab32(__u32 x)
+static inline __attribute_const__ __u32 __arch_swab32(__u32 val)
{
-#ifdef CONFIG_X86_BSWAP
- asm("bswap %0" : "=r" (x) : "0" (x));
-#else
+#ifdef __i386__
+# ifdef CONFIG_X86_BSWAP
+ asm("bswap %0" : "=r" (val) : "0" (val));
+# else
asm("xchgb %b0,%h0\n\t" /* swap lower bytes */
"rorl $16,%0\n\t" /* swap words */
"xchgb %b0,%h0" /* swap higher bytes */
- : "=q" (x)
- : "0" (x));
+ : "=q" (val)
+ : "0" (val));
+# endif
+
+#else /* __i386__ */
+ asm("bswapl %0"
+ : "=r" (val)
+ : "0" (val));
#endif
- return x;
+ return val;
}
+#define __arch_swab32 __arch_swab32
-static inline __attribute_const__ __u64 ___arch__swab64(__u64 val)
+static inline __attribute_const__ __u64 __arch_swab64(__u64 val)
{
+#ifdef __i386__
union {
struct {
__u32 a;
@@ -32,50 +39,27 @@ static inline __attribute_const__ __u64 ___arch__swab64(__u64 val)
__u64 u;
} v;
v.u = val;
-#ifdef CONFIG_X86_BSWAP
+# ifdef CONFIG_X86_BSWAP
asm("bswapl %0 ; bswapl %1 ; xchgl %0,%1"
: "=r" (v.s.a), "=r" (v.s.b)
: "0" (v.s.a), "1" (v.s.b));
-#else
- v.s.a = ___arch__swab32(v.s.a);
- v.s.b = ___arch__swab32(v.s.b);
+# else
+ v.s.a = __arch_swab32(v.s.a);
+ v.s.b = __arch_swab32(v.s.b);
asm("xchgl %0,%1"
: "=r" (v.s.a), "=r" (v.s.b)
: "0" (v.s.a), "1" (v.s.b));
-#endif
+# endif
return v.u;
-}
-
#else /* __i386__ */
-
-static inline __attribute_const__ __u64 ___arch__swab64(__u64 x)
-{
asm("bswapq %0"
- : "=r" (x)
- : "0" (x));
- return x;
-}
-
-static inline __attribute_const__ __u32 ___arch__swab32(__u32 x)
-{
- asm("bswapl %0"
- : "=r" (x)
- : "0" (x));
- return x;
-}
-
+ : "=r" (val)
+ : "0" (val));
+ return val;
#endif
+}
+#define __arch_swab64 __arch_swab64
-/* Do not define swab16. Gcc is smart enough to recognize "C" version and
- convert it into rotation or exhange. */
-
-#define __arch__swab64(x) ___arch__swab64(x)
-#define __arch__swab32(x) ___arch__swab32(x)
-
-#define __BYTEORDER_HAS_U64__
-
-#endif /* __GNUC__ */
-
-#include <linux/byteorder/little_endian.h>
+#include <linux/byteorder.h>
#endif /* _ASM_X86_BYTEORDER_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index cfdf8c2c5c31..ea408dcba513 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -80,7 +80,6 @@
#define X86_FEATURE_UP (3*32+ 9) /* smp kernel running on up */
#define X86_FEATURE_FXSAVE_LEAK (3*32+10) /* "" FXSAVE leaks FOP/FIP/FOP */
#define X86_FEATURE_ARCH_PERFMON (3*32+11) /* Intel Architectural PerfMon */
-#define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */
#define X86_FEATURE_PEBS (3*32+12) /* Precise-Event Based Sampling */
#define X86_FEATURE_BTS (3*32+13) /* Branch Trace Store */
#define X86_FEATURE_SYSCALL32 (3*32+14) /* "" syscall in ia32 userspace */
@@ -92,6 +91,8 @@
#define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */
#define X86_FEATURE_AMDC1E (3*32+21) /* AMD C1E detected */
#define X86_FEATURE_XTOPOLOGY (3*32+22) /* cpu topology enum extensions */
+#define X86_FEATURE_TSC_RELIABLE (3*32+23) /* TSC is known to be reliable */
+#define X86_FEATURE_NONSTOP_TSC (3*32+24) /* TSC does not stop in C states */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
@@ -117,6 +118,7 @@
#define X86_FEATURE_XSAVE (4*32+26) /* XSAVE/XRSTOR/XSETBV/XGETBV */
#define X86_FEATURE_OSXSAVE (4*32+27) /* "" XSAVE enabled in the OS */
#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */
+#define X86_FEATURE_HYPERVISOR (4*32+31) /* Running on a hypervisor */
/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */
#define X86_FEATURE_XSTORE (5*32+ 2) /* "rng" RNG present (xstore) */
@@ -237,6 +239,7 @@ 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_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR)
#if defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_64)
# define cpu_has_invlpg 1
diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h
index 097794ff6b79..4035357f5b9d 100644
--- a/arch/x86/include/asm/dma-mapping.h
+++ b/arch/x86/include/asm/dma-mapping.h
@@ -65,18 +65,16 @@ static inline struct dma_mapping_ops *get_dma_ops(struct device *dev)
return dma_ops;
else
return dev->archdata.dma_ops;
-#endif /* _ASM_X86_DMA_MAPPING_H */
+#endif
}
/* Make sure we keep the same behaviour */
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
-#ifdef CONFIG_X86_64
struct dma_mapping_ops *ops = get_dma_ops(dev);
if (ops->mapping_error)
return ops->mapping_error(dev, dma_addr);
-#endif
return (dma_addr == bad_dma_address);
}
diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h
index 99b6c39774a4..a8f672ba100c 100644
--- a/arch/x86/include/asm/ds.h
+++ b/arch/x86/include/asm/ds.h
@@ -6,13 +6,13 @@
* precise-event based sampling (PEBS).
*
* It manages:
- * - per-thread and per-cpu allocation of BTS and PEBS
+ * - DS and BTS hardware configuration
* - buffer overflow handling (to be done)
* - buffer access
*
- * It assumes:
- * - get_task_struct on all traced tasks
- * - current is allowed to trace tasks
+ * It does not do:
+ * - security checking (is the caller allowed to trace the task)
+ * - buffer allocation (memory accounting)
*
*
* Copyright (C) 2007-2008 Intel Corporation.
@@ -31,6 +31,7 @@
#ifdef CONFIG_X86_DS
struct task_struct;
+struct ds_context;
struct ds_tracer;
struct bts_tracer;
struct pebs_tracer;
@@ -38,6 +39,38 @@ struct pebs_tracer;
typedef void (*bts_ovfl_callback_t)(struct bts_tracer *);
typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *);
+
+/*
+ * A list of features plus corresponding macros to talk about them in
+ * the ds_request function's flags parameter.
+ *
+ * We use the enum to index an array of corresponding control bits;
+ * we use the macro to index a flags bit-vector.
+ */
+enum ds_feature {
+ dsf_bts = 0,
+ dsf_bts_kernel,
+#define BTS_KERNEL (1 << dsf_bts_kernel)
+ /* trace kernel-mode branches */
+
+ dsf_bts_user,
+#define BTS_USER (1 << dsf_bts_user)
+ /* trace user-mode branches */
+
+ dsf_bts_overflow,
+ dsf_bts_max,
+ dsf_pebs = dsf_bts_max,
+
+ dsf_pebs_max,
+ dsf_ctl_max = dsf_pebs_max,
+ dsf_bts_timestamps = dsf_ctl_max,
+#define BTS_TIMESTAMPS (1 << dsf_bts_timestamps)
+ /* add timestamps into BTS trace */
+
+#define BTS_USER_FLAGS (BTS_KERNEL | BTS_USER | BTS_TIMESTAMPS)
+};
+
+
/*
* Request BTS or PEBS
*
@@ -58,92 +91,135 @@ typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *);
* NULL if cyclic buffer requested
* th: the interrupt threshold in records from the end of the buffer;
* -1 if no interrupt threshold is requested.
+ * flags: a bit-mask of the above flags
*/
extern struct bts_tracer *ds_request_bts(struct task_struct *task,
void *base, size_t size,
- bts_ovfl_callback_t ovfl, size_t th);
+ bts_ovfl_callback_t ovfl,
+ size_t th, unsigned int flags);
extern struct pebs_tracer *ds_request_pebs(struct task_struct *task,
void *base, size_t size,
pebs_ovfl_callback_t ovfl,
- size_t th);
+ size_t th, unsigned int flags);
/*
* Release BTS or PEBS resources
- *
- * Returns 0 on success; -Eerrno otherwise
+ * Suspend and resume BTS or PEBS tracing
*
* tracer: the tracer handle returned from ds_request_~()
*/
-extern int ds_release_bts(struct bts_tracer *tracer);
-extern int ds_release_pebs(struct pebs_tracer *tracer);
+extern void ds_release_bts(struct bts_tracer *tracer);
+extern void ds_suspend_bts(struct bts_tracer *tracer);
+extern void ds_resume_bts(struct bts_tracer *tracer);
+extern void ds_release_pebs(struct pebs_tracer *tracer);
+extern void ds_suspend_pebs(struct pebs_tracer *tracer);
+extern void ds_resume_pebs(struct pebs_tracer *tracer);
+
/*
- * Get the (array) index of the write pointer.
- * (assuming an array of BTS/PEBS records)
- *
- * Returns 0 on success; -Eerrno on error
+ * The raw DS buffer state as it is used for BTS and PEBS recording.
*
- * tracer: the tracer handle returned from ds_request_~()
- * pos (out): will hold the result
+ * This is the low-level, arch-dependent interface for working
+ * directly on the raw trace data.
*/
-extern int ds_get_bts_index(struct bts_tracer *tracer, size_t *pos);
-extern int ds_get_pebs_index(struct pebs_tracer *tracer, size_t *pos);
+struct ds_trace {
+ /* the number of bts/pebs records */
+ size_t n;
+ /* the size of a bts/pebs record in bytes */
+ size_t size;
+ /* pointers into the raw buffer:
+ - to the first entry */
+ void *begin;
+ /* - one beyond the last entry */
+ void *end;
+ /* - one beyond the newest entry */
+ void *top;
+ /* - the interrupt threshold */
+ void *ith;
+ /* flags given on ds_request() */
+ unsigned int flags;
+};
/*
- * Get the (array) index one record beyond the end of the array.
- * (assuming an array of BTS/PEBS records)
- *
- * Returns 0 on success; -Eerrno on error
- *
- * tracer: the tracer handle returned from ds_request_~()
- * pos (out): will hold the result
+ * An arch-independent view on branch trace data.
*/
-extern int ds_get_bts_end(struct bts_tracer *tracer, size_t *pos);
-extern int ds_get_pebs_end(struct pebs_tracer *tracer, size_t *pos);
+enum bts_qualifier {
+ bts_invalid,
+#define BTS_INVALID bts_invalid
+
+ bts_branch,
+#define BTS_BRANCH bts_branch
+
+ bts_task_arrives,
+#define BTS_TASK_ARRIVES bts_task_arrives
+
+ bts_task_departs,
+#define BTS_TASK_DEPARTS bts_task_departs
+
+ bts_qual_bit_size = 4,
+ bts_qual_max = (1 << bts_qual_bit_size),
+};
+
+struct bts_struct {
+ __u64 qualifier;
+ union {
+ /* BTS_BRANCH */
+ struct {
+ __u64 from;
+ __u64 to;
+ } lbr;
+ /* BTS_TASK_ARRIVES or BTS_TASK_DEPARTS */
+ struct {
+ __u64 jiffies;
+ pid_t pid;
+ } timestamp;
+ } variant;
+};
+
/*
- * Provide a pointer to the BTS/PEBS record at parameter index.
- * (assuming an array of BTS/PEBS records)
- *
- * The pointer points directly into the buffer. The user is
- * responsible for copying the record.
+ * The BTS state.
*
- * Returns the size of a single record on success; -Eerrno on error
- *
- * tracer: the tracer handle returned from ds_request_~()
- * index: the index of the requested record
- * record (out): pointer to the requested record
+ * This gives access to the raw DS state and adds functions to provide
+ * an arch-independent view of the BTS data.
*/
-extern int ds_access_bts(struct bts_tracer *tracer,
- size_t index, const void **record);
-extern int ds_access_pebs(struct pebs_tracer *tracer,
- size_t index, const void **record);
+struct bts_trace {
+ struct ds_trace ds;
+
+ int (*read)(struct bts_tracer *tracer, const void *at,
+ struct bts_struct *out);
+ int (*write)(struct bts_tracer *tracer, const struct bts_struct *in);
+};
+
/*
- * Write one or more BTS/PEBS records at the write pointer index and
- * advance the write pointer.
- *
- * If size is not a multiple of the record size, trailing bytes are
- * zeroed out.
+ * The PEBS state.
*
- * May result in one or more overflow notifications.
- *
- * If called during overflow handling, that is, with index >=
- * interrupt threshold, the write will wrap around.
+ * This gives access to the raw DS state and the PEBS-specific counter
+ * reset value.
+ */
+struct pebs_trace {
+ struct ds_trace ds;
+
+ /* the PEBS reset value */
+ unsigned long long reset_value;
+};
+
+
+/*
+ * Read the BTS or PEBS trace.
*
- * An overflow notification is given if and when the interrupt
- * threshold is reached during or after the write.
+ * Returns a view on the trace collected for the parameter tracer.
*
- * Returns the number of bytes written or -Eerrno.
+ * The view remains valid as long as the traced task is not running or
+ * the tracer is suspended.
+ * Writes into the trace buffer are not reflected.
*
* tracer: the tracer handle returned from ds_request_~()
- * buffer: the buffer to write
- * size: the size of the buffer
*/
-extern int ds_write_bts(struct bts_tracer *tracer,
- const void *buffer, size_t size);
-extern int ds_write_pebs(struct pebs_tracer *tracer,
- const void *buffer, size_t size);
+extern const struct bts_trace *ds_read_bts(struct bts_tracer *tracer);
+extern const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer);
+
/*
* Reset the write pointer of the BTS/PEBS buffer.
@@ -156,27 +232,6 @@ extern int ds_reset_bts(struct bts_tracer *tracer);
extern int ds_reset_pebs(struct pebs_tracer *tracer);
/*
- * Clear the BTS/PEBS buffer and reset the write pointer.
- * The entire buffer will be zeroed out.
- *
- * Returns 0 on success; -Eerrno on error
- *
- * tracer: the tracer handle returned from ds_request_~()
- */
-extern int ds_clear_bts(struct bts_tracer *tracer);
-extern int ds_clear_pebs(struct pebs_tracer *tracer);
-
-/*
- * Provide the PEBS counter reset value.
- *
- * Returns 0 on success; -Eerrno on error
- *
- * tracer: the tracer handle returned from ds_request_pebs()
- * value (out): the counter reset value
- */
-extern int ds_get_pebs_reset(struct pebs_tracer *tracer, u64 *value);
-
-/*
* Set the PEBS counter reset value.
*
* Returns 0 on success; -Eerrno on error
@@ -192,35 +247,26 @@ extern int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value);
struct cpuinfo_x86;
extern void __cpuinit ds_init_intel(struct cpuinfo_x86 *);
-
-
/*
- * The DS context - part of struct thread_struct.
+ * Context switch work
*/
-#define MAX_SIZEOF_DS (12 * 8)
-
-struct ds_context {
- /* pointer to the DS configuration; goes into MSR_IA32_DS_AREA */
- unsigned char ds[MAX_SIZEOF_DS];
- /* the owner of the BTS and PEBS configuration, respectively */
- struct ds_tracer *owner[2];
- /* use count */
- unsigned long count;
- /* a pointer to the context location inside the thread_struct
- * or the per_cpu context array */
- struct ds_context **this;
- /* a pointer to the task owning this context, or NULL, if the
- * context is owned by a cpu */
- struct task_struct *task;
-};
+extern void ds_switch_to(struct task_struct *prev, struct task_struct *next);
-/* called by exit_thread() to free leftover contexts */
-extern void ds_free(struct ds_context *context);
+/*
+ * Task clone/init and cleanup work
+ */
+extern void ds_copy_thread(struct task_struct *tsk, struct task_struct *father);
+extern void ds_exit_thread(struct task_struct *tsk);
#else /* CONFIG_X86_DS */
struct cpuinfo_x86;
static inline void __cpuinit ds_init_intel(struct cpuinfo_x86 *ignored) {}
+static inline void ds_switch_to(struct task_struct *prev,
+ struct task_struct *next) {}
+static inline void ds_copy_thread(struct task_struct *tsk,
+ struct task_struct *father) {}
+static inline void ds_exit_thread(struct task_struct *tsk) {}
#endif /* CONFIG_X86_DS */
#endif /* _ASM_X86_DS_H */
diff --git a/arch/x86/include/asm/dwarf2.h b/arch/x86/include/asm/dwarf2.h
index 804b6e6be929..3afc5e87cfdd 100644
--- a/arch/x86/include/asm/dwarf2.h
+++ b/arch/x86/include/asm/dwarf2.h
@@ -6,56 +6,91 @@
#endif
/*
- Macros for dwarf2 CFI unwind table entries.
- See "as.info" for details on these pseudo ops. Unfortunately
- they are only supported in very new binutils, so define them
- away for older version.
+ * Macros for dwarf2 CFI unwind table entries.
+ * See "as.info" for details on these pseudo ops. Unfortunately
+ * they are only supported in very new binutils, so define them
+ * away for older version.
*/
#ifdef CONFIG_AS_CFI
-#define CFI_STARTPROC .cfi_startproc
-#define CFI_ENDPROC .cfi_endproc
-#define CFI_DEF_CFA .cfi_def_cfa
-#define CFI_DEF_CFA_REGISTER .cfi_def_cfa_register
-#define CFI_DEF_CFA_OFFSET .cfi_def_cfa_offset
-#define CFI_ADJUST_CFA_OFFSET .cfi_adjust_cfa_offset
-#define CFI_OFFSET .cfi_offset
-#define CFI_REL_OFFSET .cfi_rel_offset
-#define CFI_REGISTER .cfi_register
-#define CFI_RESTORE .cfi_restore
-#define CFI_REMEMBER_STATE .cfi_remember_state
-#define CFI_RESTORE_STATE .cfi_restore_state
-#define CFI_UNDEFINED .cfi_undefined
+#define CFI_STARTPROC .cfi_startproc
+#define CFI_ENDPROC .cfi_endproc
+#define CFI_DEF_CFA .cfi_def_cfa
+#define CFI_DEF_CFA_REGISTER .cfi_def_cfa_register
+#define CFI_DEF_CFA_OFFSET .cfi_def_cfa_offset
+#define CFI_ADJUST_CFA_OFFSET .cfi_adjust_cfa_offset
+#define CFI_OFFSET .cfi_offset
+#define CFI_REL_OFFSET .cfi_rel_offset
+#define CFI_REGISTER .cfi_register
+#define CFI_RESTORE .cfi_restore
+#define CFI_REMEMBER_STATE .cfi_remember_state
+#define CFI_RESTORE_STATE .cfi_restore_state
+#define CFI_UNDEFINED .cfi_undefined
#ifdef CONFIG_AS_CFI_SIGNAL_FRAME
-#define CFI_SIGNAL_FRAME .cfi_signal_frame
+#define CFI_SIGNAL_FRAME .cfi_signal_frame
#else
#define CFI_SIGNAL_FRAME
#endif
#else
-/* Due to the structure of pre-exisiting code, don't use assembler line
- comment character # to ignore the arguments. Instead, use a dummy macro. */
+/*
+ * Due to the structure of pre-exisiting code, don't use assembler line
+ * comment character # to ignore the arguments. Instead, use a dummy macro.
+ */
.macro cfi_ignore a=0, b=0, c=0, d=0
.endm
-#define CFI_STARTPROC cfi_ignore
-#define CFI_ENDPROC cfi_ignore
-#define CFI_DEF_CFA cfi_ignore
+#define CFI_STARTPROC cfi_ignore
+#define CFI_ENDPROC cfi_ignore
+#define CFI_DEF_CFA cfi_ignore
#define CFI_DEF_CFA_REGISTER cfi_ignore
#define CFI_DEF_CFA_OFFSET cfi_ignore
#define CFI_ADJUST_CFA_OFFSET cfi_ignore
-#define CFI_OFFSET cfi_ignore
-#define CFI_REL_OFFSET cfi_ignore
-#define CFI_REGISTER cfi_ignore
-#define CFI_RESTORE cfi_ignore
-#define CFI_REMEMBER_STATE cfi_ignore
-#define CFI_RESTORE_STATE cfi_ignore
-#define CFI_UNDEFINED cfi_ignore
-#define CFI_SIGNAL_FRAME cfi_ignore
+#define CFI_OFFSET cfi_ignore
+#define CFI_REL_OFFSET cfi_ignore
+#define CFI_REGISTER cfi_ignore
+#define CFI_RESTORE cfi_ignore
+#define CFI_REMEMBER_STATE cfi_ignore
+#define CFI_RESTORE_STATE cfi_ignore
+#define CFI_UNDEFINED cfi_ignore
+#define CFI_SIGNAL_FRAME cfi_ignore
#endif
+/*
+ * An attempt to make CFI annotations more or less
+ * correct and shorter. It is implied that you know
+ * what you're doing if you use them.
+ */
+#ifdef __ASSEMBLY__
+#ifdef CONFIG_X86_64
+ .macro pushq_cfi reg
+ pushq \reg
+ CFI_ADJUST_CFA_OFFSET 8
+ .endm
+
+ .macro popq_cfi reg
+ popq \reg
+ CFI_ADJUST_CFA_OFFSET -8
+ .endm
+
+ .macro movq_cfi reg offset=0
+ movq %\reg, \offset(%rsp)
+ CFI_REL_OFFSET \reg, \offset
+ .endm
+
+ .macro movq_cfi_restore offset reg
+ movq \offset(%rsp), %\reg
+ CFI_RESTORE \reg
+ .endm
+#else /*!CONFIG_X86_64*/
+
+ /* 32bit defenitions are missed yet */
+
+#endif /*!CONFIG_X86_64*/
+#endif /*__ASSEMBLY__*/
+
#endif /* _ASM_X86_DWARF2_H */
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 40ca1bea7916..f51a3ddde01a 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -325,7 +325,7 @@ struct linux_binprm;
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
- int executable_stack);
+ int uses_interp);
extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
#define compat_arch_setup_additional_pages syscall32_setup_pages
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 7e61b4ceb9a4..b55b4a7fbefd 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -1,6 +1,33 @@
#ifndef _ASM_X86_FTRACE_H
#define _ASM_X86_FTRACE_H
+#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)
+ .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
+ .endm
+
+#endif
+
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_ADDR ((long)(mcount))
#define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */
@@ -46,7 +73,7 @@ struct ftrace_ret_stack {
/*
* Primary handler of a function return.
* It relays on ftrace_return_to_handler.
- * Defined in entry32.S
+ * Defined in entry_32/64.S
*/
extern void return_to_handler(void);
diff --git a/arch/x86/include/asm/gart.h b/arch/x86/include/asm/gart.h
index 74252264433d..6cfdafa409d8 100644
--- a/arch/x86/include/asm/gart.h
+++ b/arch/x86/include/asm/gart.h
@@ -29,6 +29,39 @@ extern int fix_aperture;
#define AMD64_GARTCACHECTL 0x9c
#define AMD64_GARTEN (1<<0)
+#ifdef CONFIG_GART_IOMMU
+extern int gart_iommu_aperture;
+extern int gart_iommu_aperture_allowed;
+extern int gart_iommu_aperture_disabled;
+
+extern void early_gart_iommu_check(void);
+extern void gart_iommu_init(void);
+extern void gart_iommu_shutdown(void);
+extern void __init gart_parse_options(char *);
+extern void gart_iommu_hole_init(void);
+
+#else
+#define gart_iommu_aperture 0
+#define gart_iommu_aperture_allowed 0
+#define gart_iommu_aperture_disabled 1
+
+static inline void early_gart_iommu_check(void)
+{
+}
+static inline void gart_iommu_init(void)
+{
+}
+static inline void gart_iommu_shutdown(void)
+{
+}
+static inline void gart_parse_options(char *options)
+{
+}
+static inline void gart_iommu_hole_init(void)
+{
+}
+#endif
+
extern int agp_amd64_init(void);
static inline void enable_gart_translation(struct pci_dev *dev, u64 addr)
diff --git a/arch/x86/include/asm/hardirq_32.h b/arch/x86/include/asm/hardirq_32.h
index 5ca135e72f2b..cf7954d1405f 100644
--- a/arch/x86/include/asm/hardirq_32.h
+++ b/arch/x86/include/asm/hardirq_32.h
@@ -22,6 +22,8 @@ DECLARE_PER_CPU(irq_cpustat_t, irq_stat);
#define __ARCH_IRQ_STAT
#define __IRQ_STAT(cpu, member) (per_cpu(irq_stat, cpu).member)
+#define inc_irq_stat(member) (__get_cpu_var(irq_stat).member++)
+
void ack_bad_irq(unsigned int irq);
#include <linux/irq_cpustat.h>
diff --git a/arch/x86/include/asm/hardirq_64.h b/arch/x86/include/asm/hardirq_64.h
index 1ba381fc51d3..b5a6b5d56704 100644
--- a/arch/x86/include/asm/hardirq_64.h
+++ b/arch/x86/include/asm/hardirq_64.h
@@ -11,6 +11,8 @@
#define __ARCH_IRQ_STAT 1
+#define inc_irq_stat(member) add_pda(member, 1)
+
#define local_softirq_pending() read_pda(__softirq_pending)
#define __ARCH_SET_SOFTIRQ_PENDING 1
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h
index b97aecb0b61d..8de644b6b959 100644
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -109,9 +109,7 @@ extern asmlinkage void smp_invalidate_interrupt(struct pt_regs *);
#endif
#endif
-#ifdef CONFIG_X86_32
-extern void (*const interrupt[NR_VECTORS])(void);
-#endif
+extern void (*__initconst interrupt[NR_VECTORS-FIRST_EXTERNAL_VECTOR])(void);
typedef int vector_irq_t[NR_VECTORS];
DECLARE_PER_CPU(vector_irq_t, vector_irq);
diff --git a/arch/x86/include/asm/hypervisor.h b/arch/x86/include/asm/hypervisor.h
new file mode 100644
index 000000000000..369f5c5d09a1
--- /dev/null
+++ b/arch/x86/include/asm/hypervisor.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008, VMware, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef ASM_X86__HYPERVISOR_H
+#define ASM_X86__HYPERVISOR_H
+
+extern unsigned long get_hypervisor_tsc_freq(void);
+extern void init_hypervisor(struct cpuinfo_x86 *c);
+
+#endif
diff --git a/arch/x86/include/asm/ia32.h b/arch/x86/include/asm/ia32.h
index 97989c0e534c..50ca486fd88c 100644
--- a/arch/x86/include/asm/ia32.h
+++ b/arch/x86/include/asm/ia32.h
@@ -129,24 +129,6 @@ typedef struct compat_siginfo {
} _sifields;
} compat_siginfo_t;
-struct sigframe32 {
- u32 pretcode;
- int sig;
- struct sigcontext_ia32 sc;
- struct _fpstate_ia32 fpstate;
- unsigned int extramask[_COMPAT_NSIG_WORDS-1];
-};
-
-struct rt_sigframe32 {
- u32 pretcode;
- int sig;
- u32 pinfo;
- u32 puc;
- compat_siginfo_t info;
- struct ucontext_ia32 uc;
- struct _fpstate_ia32 fpstate;
-};
-
struct ustat32 {
__u32 f_tfree;
compat_ino_t f_tinode;
diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h
index 44c89c3a23e9..38d87379e270 100644
--- a/arch/x86/include/asm/idle.h
+++ b/arch/x86/include/asm/idle.h
@@ -8,8 +8,13 @@ struct notifier_block;
void idle_notifier_register(struct notifier_block *n);
void idle_notifier_unregister(struct notifier_block *n);
+#ifdef CONFIG_X86_64
void enter_idle(void);
void exit_idle(void);
+#else /* !CONFIG_X86_64 */
+static inline void enter_idle(void) { }
+static inline void exit_idle(void) { }
+#endif /* CONFIG_X86_64 */
void c1e_remove_cpu(int cpu);
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index ac2abc88cd95..05cfed4485fa 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -4,6 +4,7 @@
#define ARCH_HAS_IOREMAP_WC
#include <linux/compiler.h>
+#include <asm-generic/int-ll64.h>
#define build_mmio_read(name, size, type, reg, barrier) \
static inline type name(const volatile void __iomem *addr) \
@@ -45,21 +46,39 @@ build_mmio_write(__writel, "l", unsigned int, "r", )
#define mmiowb() barrier()
#ifdef CONFIG_X86_64
+
build_mmio_read(readq, "q", unsigned long, "=r", :"memory")
-build_mmio_read(__readq, "q", unsigned long, "=r", )
build_mmio_write(writeq, "q", unsigned long, "r", :"memory")
-build_mmio_write(__writeq, "q", unsigned long, "r", )
-#define readq_relaxed(a) __readq(a)
-#define __raw_readq __readq
-#define __raw_writeq writeq
+#else
+
+static inline __u64 readq(const volatile void __iomem *addr)
+{
+ const volatile u32 __iomem *p = addr;
+ u32 low, high;
+
+ low = readl(p);
+ high = readl(p + 1);
+
+ return low + ((u64)high << 32);
+}
+
+static inline void writeq(__u64 val, volatile void __iomem *addr)
+{
+ writel(val, addr);
+ writel(val >> 32, addr+4);
+}
-/* Let people know we have them */
-#define readq readq
-#define writeq writeq
#endif
-extern int iommu_bio_merge;
+#define readq_relaxed(a) readq(a)
+
+#define __raw_readq(a) readq(a)
+#define __raw_writeq(val, addr) writeq(val, addr)
+
+/* Let people know that we have them */
+#define readq readq
+#define writeq writeq
#ifdef CONFIG_X86_32
# include "io_32.h"
diff --git a/arch/x86/include/asm/io_64.h b/arch/x86/include/asm/io_64.h
index fea325a1122f..563c16270ba6 100644
--- a/arch/x86/include/asm/io_64.h
+++ b/arch/x86/include/asm/io_64.h
@@ -232,8 +232,6 @@ void memset_io(volatile void __iomem *a, int b, size_t c);
#define flush_write_buffers()
-#define BIO_VMERGE_BOUNDARY iommu_bio_merge
-
/*
* Convert a virtual cached pointer to an uncached pointer
*/
diff --git a/arch/x86/include/asm/io_apic.h b/arch/x86/include/asm/io_apic.h
index 25d527ca1362..7a1f44ac1f17 100644
--- a/arch/x86/include/asm/io_apic.h
+++ b/arch/x86/include/asm/io_apic.h
@@ -156,11 +156,21 @@ extern int sis_apic_bug;
/* 1 if "noapic" boot option passed */
extern int skip_ioapic_setup;
+/* 1 if "noapic" boot option passed */
+extern int noioapicquirk;
+
+/* -1 if "noapic" boot option passed */
+extern int noioapicreroute;
+
/* 1 if the timer IRQ uses the '8259A Virtual Wire' mode */
extern int timer_through_8259;
static inline void disable_ioapic_setup(void)
{
+#ifdef CONFIG_PCI
+ noioapicquirk = 1;
+ noioapicreroute = -1;
+#endif
skip_ioapic_setup = 1;
}
diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h
index 0b500c5b6446..a6ee9e6f530f 100644
--- a/arch/x86/include/asm/iommu.h
+++ b/arch/x86/include/asm/iommu.h
@@ -7,42 +7,7 @@ extern struct dma_mapping_ops nommu_dma_ops;
extern int force_iommu, no_iommu;
extern int iommu_detected;
-extern unsigned long iommu_nr_pages(unsigned long addr, unsigned long len);
-
/* 10 seconds */
#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
-#ifdef CONFIG_GART_IOMMU
-extern int gart_iommu_aperture;
-extern int gart_iommu_aperture_allowed;
-extern int gart_iommu_aperture_disabled;
-
-extern void early_gart_iommu_check(void);
-extern void gart_iommu_init(void);
-extern void gart_iommu_shutdown(void);
-extern void __init gart_parse_options(char *);
-extern void gart_iommu_hole_init(void);
-
-#else
-#define gart_iommu_aperture 0
-#define gart_iommu_aperture_allowed 0
-#define gart_iommu_aperture_disabled 1
-
-static inline void early_gart_iommu_check(void)
-{
-}
-static inline void gart_iommu_init(void)
-{
-}
-static inline void gart_iommu_shutdown(void)
-{
-}
-static inline void gart_parse_options(char *options)
-{
-}
-static inline void gart_iommu_hole_init(void)
-{
-}
-#endif
-
#endif /* _ASM_X86_IOMMU_H */
diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h
index 4bb732e45a85..592688ed04d3 100644
--- a/arch/x86/include/asm/irq.h
+++ b/arch/x86/include/asm/irq.h
@@ -31,10 +31,6 @@ static inline int irq_canonicalize(int irq)
# endif
#endif
-#ifdef CONFIG_IRQBALANCE
-extern int irqbalance_disable(char *str);
-#endif
-
#ifdef CONFIG_HOTPLUG_CPU
#include <linux/cpumask.h>
extern void fixup_irqs(void);
diff --git a/arch/x86/include/asm/irq_regs_32.h b/arch/x86/include/asm/irq_regs_32.h
index af2f02d27fc7..86afd7473457 100644
--- a/arch/x86/include/asm/irq_regs_32.h
+++ b/arch/x86/include/asm/irq_regs_32.h
@@ -9,6 +9,8 @@
#include <asm/percpu.h>
+#define ARCH_HAS_OWN_IRQ_REGS
+
DECLARE_PER_CPU(struct pt_regs *, irq_regs);
static inline struct pt_regs *get_irq_regs(void)
diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h
index f61ee8f937e4..5d98d0b68ffc 100644
--- a/arch/x86/include/asm/linkage.h
+++ b/arch/x86/include/asm/linkage.h
@@ -57,5 +57,65 @@
#define __ALIGN_STR ".align 16,0x90"
#endif
+/*
+ * to check ENTRY_X86/END_X86 and
+ * KPROBE_ENTRY_X86/KPROBE_END_X86
+ * unbalanced-missed-mixed appearance
+ */
+#define __set_entry_x86 .set ENTRY_X86_IN, 0
+#define __unset_entry_x86 .set ENTRY_X86_IN, 1
+#define __set_kprobe_x86 .set KPROBE_X86_IN, 0
+#define __unset_kprobe_x86 .set KPROBE_X86_IN, 1
+
+#define __macro_err_x86 .error "ENTRY_X86/KPROBE_X86 unbalanced,missed,mixed"
+
+#define __check_entry_x86 \
+ .ifdef ENTRY_X86_IN; \
+ .ifeq ENTRY_X86_IN; \
+ __macro_err_x86; \
+ .abort; \
+ .endif; \
+ .endif
+
+#define __check_kprobe_x86 \
+ .ifdef KPROBE_X86_IN; \
+ .ifeq KPROBE_X86_IN; \
+ __macro_err_x86; \
+ .abort; \
+ .endif; \
+ .endif
+
+#define __check_entry_kprobe_x86 \
+ __check_entry_x86; \
+ __check_kprobe_x86
+
+#define ENTRY_KPROBE_FINAL_X86 __check_entry_kprobe_x86
+
+#define ENTRY_X86(name) \
+ __check_entry_kprobe_x86; \
+ __set_entry_x86; \
+ .globl name; \
+ __ALIGN; \
+ name:
+
+#define END_X86(name) \
+ __unset_entry_x86; \
+ __check_entry_kprobe_x86; \
+ .size name, .-name
+
+#define KPROBE_ENTRY_X86(name) \
+ __check_entry_kprobe_x86; \
+ __set_kprobe_x86; \
+ .pushsection .kprobes.text, "ax"; \
+ .globl name; \
+ __ALIGN; \
+ name:
+
+#define KPROBE_END_X86(name) \
+ __unset_kprobe_x86; \
+ __check_entry_kprobe_x86; \
+ .size name, .-name; \
+ .popsection
+
#endif /* _ASM_X86_LINKAGE_H */
diff --git a/arch/x86/include/asm/mmu_context_32.h b/arch/x86/include/asm/mmu_context_32.h
index 8e10015781fb..7e98ce1d2c0e 100644
--- a/arch/x86/include/asm/mmu_context_32.h
+++ b/arch/x86/include/asm/mmu_context_32.h
@@ -4,9 +4,8 @@
static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
#ifdef CONFIG_SMP
- unsigned cpu = smp_processor_id();
- if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK)
- per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_LAZY;
+ if (x86_read_percpu(cpu_tlbstate.state) == TLBSTATE_OK)
+ x86_write_percpu(cpu_tlbstate.state, TLBSTATE_LAZY);
#endif
}
@@ -20,8 +19,8 @@ static inline void switch_mm(struct mm_struct *prev,
/* stop flush ipis for the previous mm */
cpu_clear(cpu, prev->cpu_vm_mask);
#ifdef CONFIG_SMP
- per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
- per_cpu(cpu_tlbstate, cpu).active_mm = next;
+ x86_write_percpu(cpu_tlbstate.state, TLBSTATE_OK);
+ x86_write_percpu(cpu_tlbstate.active_mm, next);
#endif
cpu_set(cpu, next->cpu_vm_mask);
@@ -36,8 +35,8 @@ static inline void switch_mm(struct mm_struct *prev,
}
#ifdef CONFIG_SMP
else {
- per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
- BUG_ON(per_cpu(cpu_tlbstate, cpu).active_mm != next);
+ x86_write_percpu(cpu_tlbstate.state, TLBSTATE_OK);
+ BUG_ON(x86_read_percpu(cpu_tlbstate.active_mm) != next);
if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) {
/* We were in lazy tlb mode and leave_mm disabled
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index e38859d577a1..cb58643947b9 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -85,7 +85,9 @@
/* AMD64 MSRs. Not complete. See the architecture manual for a more
complete list. */
+#define MSR_AMD64_PATCH_LEVEL 0x0000008b
#define MSR_AMD64_NB_CFG 0xc001001f
+#define MSR_AMD64_PATCH_LOADER 0xc0010020
#define MSR_AMD64_IBSFETCHCTL 0xc0011030
#define MSR_AMD64_IBSFETCHLINAD 0xc0011031
#define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index c2a812ebde89..638bf6241807 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -22,10 +22,10 @@ static inline unsigned long long native_read_tscp(unsigned int *aux)
}
/*
- * i386 calling convention returns 64-bit value in edx:eax, while
- * x86_64 returns at rax. Also, the "A" constraint does not really
- * mean rdx:rax in x86_64, so we need specialized behaviour for each
- * architecture
+ * both i386 and x86_64 returns 64-bit value in edx:eax, but gcc's "A"
+ * constraint has different meanings. For i386, "A" means exactly
+ * edx:eax, while for x86_64 it doesn't mean rdx:rax or edx:eax. Instead,
+ * it means rax *or* rdx.
*/
#ifdef CONFIG_X86_64
#define DECLARE_ARGS(val, low, high) unsigned low, high
@@ -85,7 +85,8 @@ static inline void native_write_msr(unsigned int msr,
asm volatile("wrmsr" : : "c" (msr), "a"(low), "d" (high) : "memory");
}
-static inline int native_write_msr_safe(unsigned int msr,
+/* Can be uninlined because referenced by paravirt */
+notrace static inline int native_write_msr_safe(unsigned int msr,
unsigned low, unsigned high)
{
int err;
@@ -181,10 +182,10 @@ static inline int rdmsrl_amd_safe(unsigned msr, unsigned long long *p)
}
#define rdtscl(low) \
- ((low) = (u32)native_read_tsc())
+ ((low) = (u32)__native_read_tsc())
#define rdtscll(val) \
- ((val) = native_read_tsc())
+ ((val) = __native_read_tsc())
#define rdpmc(counter, low, high) \
do { \
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
index 875b38edf193..a977de23cb4d 100644
--- a/arch/x86/include/asm/pci.h
+++ b/arch/x86/include/asm/pci.h
@@ -19,6 +19,8 @@ struct pci_sysdata {
};
extern int pci_routeirq;
+extern int noioapicquirk;
+extern int noioapicreroute;
/* scan a bus after allocating a pci_sysdata for it */
extern struct pci_bus *pci_scan_bus_on_node(int busno, struct pci_ops *ops,
@@ -82,6 +84,8 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev,
static inline void early_quirks(void) { }
#endif
+extern void pci_iommu_alloc(void);
+
#endif /* __KERNEL__ */
#ifdef CONFIG_X86_32
@@ -98,9 +102,9 @@ static inline void early_quirks(void) { }
#ifdef CONFIG_NUMA
/* Returns the node based on pci bus */
-static inline int __pcibus_to_node(struct pci_bus *bus)
+static inline int __pcibus_to_node(const struct pci_bus *bus)
{
- struct pci_sysdata *sd = bus->sysdata;
+ const struct pci_sysdata *sd = bus->sysdata;
return sd->node;
}
@@ -109,6 +113,12 @@ static inline cpumask_t __pcibus_to_cpumask(struct pci_bus *bus)
{
return node_to_cpumask(__pcibus_to_node(bus));
}
+
+static inline const struct cpumask *
+cpumask_of_pcibus(const struct pci_bus *bus)
+{
+ return cpumask_of_node(__pcibus_to_node(bus));
+}
#endif
#endif /* _ASM_X86_PCI_H */
diff --git a/arch/x86/include/asm/pci_64.h b/arch/x86/include/asm/pci_64.h
index d02d936840a3..4da207982777 100644
--- a/arch/x86/include/asm/pci_64.h
+++ b/arch/x86/include/asm/pci_64.h
@@ -23,7 +23,6 @@ extern int (*pci_config_write)(int seg, int bus, int dev, int fn,
int reg, int len, u32 value);
extern void dma32_reserve_bootmem(void);
-extern void pci_iommu_alloc(void);
/* The PCI address space does equal the physical memory
* address space. The networking and block device layers use
diff --git a/arch/x86/include/asm/pgtable-2level.h b/arch/x86/include/asm/pgtable-2level.h
index b17edfd23628..e0d199fe1d83 100644
--- a/arch/x86/include/asm/pgtable-2level.h
+++ b/arch/x86/include/asm/pgtable-2level.h
@@ -56,23 +56,55 @@ static inline pte_t native_ptep_get_and_clear(pte_t *xp)
#define pte_none(x) (!(x).pte_low)
/*
- * Bits 0, 6 and 7 are taken, split up the 29 bits of offset
- * into this range:
+ * Bits _PAGE_BIT_PRESENT, _PAGE_BIT_FILE and _PAGE_BIT_PROTNONE are taken,
+ * split up the 29 bits of offset into this range:
*/
#define PTE_FILE_MAX_BITS 29
+#define PTE_FILE_SHIFT1 (_PAGE_BIT_PRESENT + 1)
+#if _PAGE_BIT_FILE < _PAGE_BIT_PROTNONE
+#define PTE_FILE_SHIFT2 (_PAGE_BIT_FILE + 1)
+#define PTE_FILE_SHIFT3 (_PAGE_BIT_PROTNONE + 1)
+#else
+#define PTE_FILE_SHIFT2 (_PAGE_BIT_PROTNONE + 1)
+#define PTE_FILE_SHIFT3 (_PAGE_BIT_FILE + 1)
+#endif
+#define PTE_FILE_BITS1 (PTE_FILE_SHIFT2 - PTE_FILE_SHIFT1 - 1)
+#define PTE_FILE_BITS2 (PTE_FILE_SHIFT3 - PTE_FILE_SHIFT2 - 1)
#define pte_to_pgoff(pte) \
- ((((pte).pte_low >> 1) & 0x1f) + (((pte).pte_low >> 8) << 5))
+ ((((pte).pte_low >> PTE_FILE_SHIFT1) \
+ & ((1U << PTE_FILE_BITS1) - 1)) \
+ + ((((pte).pte_low >> PTE_FILE_SHIFT2) \
+ & ((1U << PTE_FILE_BITS2) - 1)) << PTE_FILE_BITS1) \
+ + (((pte).pte_low >> PTE_FILE_SHIFT3) \
+ << (PTE_FILE_BITS1 + PTE_FILE_BITS2)))
#define pgoff_to_pte(off) \
- ((pte_t) { .pte_low = (((off) & 0x1f) << 1) + \
- (((off) >> 5) << 8) + _PAGE_FILE })
+ ((pte_t) { .pte_low = \
+ (((off) & ((1U << PTE_FILE_BITS1) - 1)) << PTE_FILE_SHIFT1) \
+ + ((((off) >> PTE_FILE_BITS1) & ((1U << PTE_FILE_BITS2) - 1)) \
+ << PTE_FILE_SHIFT2) \
+ + (((off) >> (PTE_FILE_BITS1 + PTE_FILE_BITS2)) \
+ << PTE_FILE_SHIFT3) \
+ + _PAGE_FILE })
/* Encode and de-code a swap entry */
-#define __swp_type(x) (((x).val >> 1) & 0x1f)
-#define __swp_offset(x) ((x).val >> 8)
-#define __swp_entry(type, offset) \
- ((swp_entry_t) { ((type) << 1) | ((offset) << 8) })
+#if _PAGE_BIT_FILE < _PAGE_BIT_PROTNONE
+#define SWP_TYPE_BITS (_PAGE_BIT_FILE - _PAGE_BIT_PRESENT - 1)
+#define SWP_OFFSET_SHIFT (_PAGE_BIT_PROTNONE + 1)
+#else
+#define SWP_TYPE_BITS (_PAGE_BIT_PROTNONE - _PAGE_BIT_PRESENT - 1)
+#define SWP_OFFSET_SHIFT (_PAGE_BIT_FILE + 1)
+#endif
+
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS)
+
+#define __swp_type(x) (((x).val >> (_PAGE_BIT_PRESENT + 1)) \
+ & ((1U << SWP_TYPE_BITS) - 1))
+#define __swp_offset(x) ((x).val >> SWP_OFFSET_SHIFT)
+#define __swp_entry(type, offset) ((swp_entry_t) { \
+ ((type) << (_PAGE_BIT_PRESENT + 1)) \
+ | ((offset) << SWP_OFFSET_SHIFT) })
#define __pte_to_swp_entry(pte) ((swp_entry_t) { (pte).pte_low })
#define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val })
diff --git a/arch/x86/include/asm/pgtable-3level.h b/arch/x86/include/asm/pgtable-3level.h
index 52597aeadfff..447da43cddb3 100644
--- a/arch/x86/include/asm/pgtable-3level.h
+++ b/arch/x86/include/asm/pgtable-3level.h
@@ -166,6 +166,7 @@ static inline int pte_none(pte_t pte)
#define PTE_FILE_MAX_BITS 32
/* Encode and de-code a swap entry */
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > 5)
#define __swp_type(x) (((x).val) & 0x1f)
#define __swp_offset(x) ((x).val >> 5)
#define __swp_entry(type, offset) ((swp_entry_t){(type) | (offset) << 5})
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index c012f3b11671..83e69f4a37f0 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -10,7 +10,6 @@
#define _PAGE_BIT_PCD 4 /* page cache disabled */
#define _PAGE_BIT_ACCESSED 5 /* was accessed (raised by CPU) */
#define _PAGE_BIT_DIRTY 6 /* was written to (raised by CPU) */
-#define _PAGE_BIT_FILE 6
#define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page */
#define _PAGE_BIT_PAT 7 /* on 4KB pages */
#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
@@ -22,6 +21,12 @@
#define _PAGE_BIT_CPA_TEST _PAGE_BIT_UNUSED1
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
+/* If _PAGE_BIT_PRESENT is clear, we use these: */
+/* - if the user mapped it with PROT_NONE; pte_present gives true */
+#define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBAL
+/* - set: nonlinear file mapping, saved PTE; unset:swap */
+#define _PAGE_BIT_FILE _PAGE_BIT_DIRTY
+
#define _PAGE_PRESENT (_AT(pteval_t, 1) << _PAGE_BIT_PRESENT)
#define _PAGE_RW (_AT(pteval_t, 1) << _PAGE_BIT_RW)
#define _PAGE_USER (_AT(pteval_t, 1) << _PAGE_BIT_USER)
@@ -46,11 +51,8 @@
#define _PAGE_NX (_AT(pteval_t, 0))
#endif
-/* If _PAGE_PRESENT is clear, we use these: */
-#define _PAGE_FILE _PAGE_DIRTY /* nonlinear file mapping,
- * saved PTE; unset:swap */
-#define _PAGE_PROTNONE _PAGE_PSE /* if the user mapped it with PROT_NONE;
- pte_present gives true */
+#define _PAGE_FILE (_AT(pteval_t, 1) << _PAGE_BIT_FILE)
+#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
_PAGE_ACCESSED | _PAGE_DIRTY)
@@ -158,8 +160,19 @@
#define PGD_IDENT_ATTR 0x001 /* PRESENT (no other attributes) */
#endif
+/*
+ * Macro to mark a page protection value as UC-
+ */
+#define pgprot_noncached(prot) \
+ ((boot_cpu_data.x86 > 3) \
+ ? (__pgprot(pgprot_val(prot) | _PAGE_CACHE_UC_MINUS)) \
+ : (prot))
+
#ifndef __ASSEMBLY__
+#define pgprot_writecombine pgprot_writecombine
+extern pgprot_t pgprot_writecombine(pgprot_t prot);
+
/*
* ZERO_PAGE is a global shared page that is always zero: used
* for zero-mapped memory areas etc..
@@ -329,6 +342,9 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
#ifndef __ASSEMBLY__
+/* Indicate that x86 has its own track and untrack pfn vma functions */
+#define __HAVE_PFNMAP_TRACKING
+
#define __HAVE_PHYS_MEM_ACCESS_PROT
struct file;
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h
index f9d5889b336b..72b020deb46b 100644
--- a/arch/x86/include/asm/pgtable_32.h
+++ b/arch/x86/include/asm/pgtable_32.h
@@ -101,15 +101,6 @@ extern unsigned long pg0[];
#endif
/*
- * Macro to mark a page protection value as "uncacheable".
- * On processors which do not support it, this is a no-op.
- */
-#define pgprot_noncached(prot) \
- ((boot_cpu_data.x86 > 3) \
- ? (__pgprot(pgprot_val(prot) | _PAGE_PCD | _PAGE_PWT)) \
- : (prot))
-
-/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
*/
diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h
index 545a0e042bb2..ba09289accaa 100644
--- a/arch/x86/include/asm/pgtable_64.h
+++ b/arch/x86/include/asm/pgtable_64.h
@@ -146,7 +146,7 @@ static inline void native_pgd_clear(pgd_t *pgd)
#define PGDIR_MASK (~(PGDIR_SIZE - 1))
-#define MAXMEM _AC(0x00003fffffffffff, UL)
+#define MAXMEM _AC(__AC(1, UL) << MAX_PHYSMEM_BITS, UL)
#define VMALLOC_START _AC(0xffffc20000000000, UL)
#define VMALLOC_END _AC(0xffffe1ffffffffff, UL)
#define VMEMMAP_START _AC(0xffffe20000000000, UL)
@@ -177,12 +177,6 @@ static inline int pmd_bad(pmd_t pmd)
#define pages_to_mb(x) ((x) >> (20 - PAGE_SHIFT)) /* FIXME: is this right? */
/*
- * Macro to mark a page protection value as "uncacheable".
- */
-#define pgprot_noncached(prot) \
- (__pgprot(pgprot_val((prot)) | _PAGE_PCD | _PAGE_PWT))
-
-/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
*/
@@ -250,10 +244,22 @@ static inline int pud_large(pud_t pte)
extern int direct_gbpages;
/* Encode and de-code a swap entry */
-#define __swp_type(x) (((x).val >> 1) & 0x3f)
-#define __swp_offset(x) ((x).val >> 8)
-#define __swp_entry(type, offset) ((swp_entry_t) { ((type) << 1) | \
- ((offset) << 8) })
+#if _PAGE_BIT_FILE < _PAGE_BIT_PROTNONE
+#define SWP_TYPE_BITS (_PAGE_BIT_FILE - _PAGE_BIT_PRESENT - 1)
+#define SWP_OFFSET_SHIFT (_PAGE_BIT_PROTNONE + 1)
+#else
+#define SWP_TYPE_BITS (_PAGE_BIT_PROTNONE - _PAGE_BIT_PRESENT - 1)
+#define SWP_OFFSET_SHIFT (_PAGE_BIT_FILE + 1)
+#endif
+
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS)
+
+#define __swp_type(x) (((x).val >> (_PAGE_BIT_PRESENT + 1)) \
+ & ((1U << SWP_TYPE_BITS) - 1))
+#define __swp_offset(x) ((x).val >> SWP_OFFSET_SHIFT)
+#define __swp_entry(type, offset) ((swp_entry_t) { \
+ ((type) << (_PAGE_BIT_PRESENT + 1)) \
+ | ((offset) << SWP_OFFSET_SHIFT) })
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val((pte)) })
#define __swp_entry_to_pte(x) ((pte_t) { .pte = (x).val })
diff --git a/arch/x86/include/asm/prctl.h b/arch/x86/include/asm/prctl.h
index fe681147a4f7..a8894647dd9a 100644
--- a/arch/x86/include/asm/prctl.h
+++ b/arch/x86/include/asm/prctl.h
@@ -6,5 +6,8 @@
#define ARCH_GET_FS 0x1003
#define ARCH_GET_GS 0x1004
+#ifdef CONFIG_X86_64
+extern long sys_arch_prctl(int, unsigned long);
+#endif /* CONFIG_X86_64 */
#endif /* _ASM_X86_PRCTL_H */
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 5ca01e383269..091cd8855f2e 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -110,6 +110,7 @@ struct cpuinfo_x86 {
/* Index into per_cpu list: */
u16 cpu_index;
#endif
+ unsigned int x86_hyper_vendor;
} __attribute__((__aligned__(SMP_CACHE_BYTES)));
#define X86_VENDOR_INTEL 0
@@ -123,6 +124,9 @@ struct cpuinfo_x86 {
#define X86_VENDOR_UNKNOWN 0xff
+#define X86_HYPER_VENDOR_NONE 0
+#define X86_HYPER_VENDOR_VMWARE 1
+
/*
* capabilities of CPUs
*/
@@ -752,6 +756,19 @@ extern void switch_to_new_gdt(void);
extern void cpu_init(void);
extern void init_gdt(int cpu);
+static inline unsigned long get_debugctlmsr(void)
+{
+ unsigned long debugctlmsr = 0;
+
+#ifndef CONFIG_X86_DEBUGCTLMSR
+ if (boot_cpu_data.x86 < 6)
+ return 0;
+#endif
+ rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
+
+ return debugctlmsr;
+}
+
static inline void update_debugctlmsr(unsigned long debugctlmsr)
{
#ifndef CONFIG_X86_DEBUGCTLMSR
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index eefb0594b058..6d34d954c228 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -6,7 +6,6 @@
#include <asm/processor-flags.h>
#ifdef __KERNEL__
-#include <asm/ds.h> /* the DS BTS struct is used for ptrace too */
#include <asm/segment.h>
#endif
@@ -128,34 +127,6 @@ struct pt_regs {
#endif /* !__i386__ */
-#ifdef CONFIG_X86_PTRACE_BTS
-/* a branch trace record entry
- *
- * In order to unify the interface between various processor versions,
- * we use the below data structure for all processors.
- */
-enum bts_qualifier {
- BTS_INVALID = 0,
- BTS_BRANCH,
- BTS_TASK_ARRIVES,
- BTS_TASK_DEPARTS
-};
-
-struct bts_struct {
- __u64 qualifier;
- union {
- /* BTS_BRANCH */
- struct {
- __u64 from_ip;
- __u64 to_ip;
- } lbr;
- /* BTS_TASK_ARRIVES or
- BTS_TASK_DEPARTS */
- __u64 jiffies;
- } variant;
-};
-#endif /* CONFIG_X86_PTRACE_BTS */
-
#ifdef __KERNEL__
#include <linux/init.h>
@@ -163,13 +134,6 @@ struct bts_struct {
struct cpuinfo_x86;
struct task_struct;
-#ifdef CONFIG_X86_PTRACE_BTS
-extern void __cpuinit ptrace_bts_init_intel(struct cpuinfo_x86 *);
-extern void ptrace_bts_take_timestamp(struct task_struct *, enum bts_qualifier);
-#else
-#define ptrace_bts_init_intel(config) do {} while (0)
-#endif /* CONFIG_X86_PTRACE_BTS */
-
extern unsigned long profile_pc(struct pt_regs *regs);
extern unsigned long
@@ -271,6 +235,13 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
extern int do_set_thread_area(struct task_struct *p, int idx,
struct user_desc __user *info, int can_allocate);
+extern void x86_ptrace_untrace(struct task_struct *);
+extern void x86_ptrace_fork(struct task_struct *child,
+ unsigned long clone_flags);
+
+#define arch_ptrace_untrace(tsk) x86_ptrace_untrace(tsk)
+#define arch_ptrace_fork(child, flags) x86_ptrace_fork(child, flags)
+
#endif /* __KERNEL__ */
#endif /* !__ASSEMBLY__ */
diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
index 294daeb3a006..4fcd53fd5f43 100644
--- a/arch/x86/include/asm/setup.h
+++ b/arch/x86/include/asm/setup.h
@@ -8,6 +8,10 @@
/* Interrupt control for vSMPowered x86_64 systems */
void vsmp_init(void);
+
+void setup_bios_corruption_check(void);
+
+
#ifdef CONFIG_X86_VISWS
extern void visws_early_detect(void);
extern int is_visws_box(void);
diff --git a/arch/x86/include/asm/sigframe.h b/arch/x86/include/asm/sigframe.h
new file mode 100644
index 000000000000..4e0fe26d27d3
--- /dev/null
+++ b/arch/x86/include/asm/sigframe.h
@@ -0,0 +1,70 @@
+#ifndef _ASM_X86_SIGFRAME_H
+#define _ASM_X86_SIGFRAME_H
+
+#include <asm/sigcontext.h>
+#include <asm/siginfo.h>
+#include <asm/ucontext.h>
+
+#ifdef CONFIG_X86_32
+#define sigframe_ia32 sigframe
+#define rt_sigframe_ia32 rt_sigframe
+#define sigcontext_ia32 sigcontext
+#define _fpstate_ia32 _fpstate
+#define ucontext_ia32 ucontext
+#else /* !CONFIG_X86_32 */
+
+#ifdef CONFIG_IA32_EMULATION
+#include <asm/ia32.h>
+#endif /* CONFIG_IA32_EMULATION */
+
+#endif /* CONFIG_X86_32 */
+
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
+struct sigframe_ia32 {
+ u32 pretcode;
+ int sig;
+ struct sigcontext_ia32 sc;
+ /*
+ * fpstate is unused. fpstate is moved/allocated after
+ * retcode[] below. This movement allows to have the FP state and the
+ * future state extensions (xsave) stay together.
+ * And at the same time retaining the unused fpstate, prevents changing
+ * the offset of extramask[] in the sigframe and thus prevent any
+ * legacy application accessing/modifying it.
+ */
+ struct _fpstate_ia32 fpstate_unused;
+#ifdef CONFIG_IA32_EMULATION
+ unsigned int extramask[_COMPAT_NSIG_WORDS-1];
+#else /* !CONFIG_IA32_EMULATION */
+ unsigned long extramask[_NSIG_WORDS-1];
+#endif /* CONFIG_IA32_EMULATION */
+ char retcode[8];
+ /* fp state follows here */
+};
+
+struct rt_sigframe_ia32 {
+ u32 pretcode;
+ int sig;
+ u32 pinfo;
+ u32 puc;
+#ifdef CONFIG_IA32_EMULATION
+ compat_siginfo_t info;
+#else /* !CONFIG_IA32_EMULATION */
+ struct siginfo info;
+#endif /* CONFIG_IA32_EMULATION */
+ struct ucontext_ia32 uc;
+ char retcode[8];
+ /* fp state follows here */
+};
+#endif /* defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) */
+
+#ifdef CONFIG_X86_64
+struct rt_sigframe {
+ char __user *pretcode;
+ struct ucontext uc;
+ struct siginfo info;
+ /* fp state follows here */
+};
+#endif /* CONFIG_X86_64 */
+
+#endif /* _ASM_X86_SIGFRAME_H */
diff --git a/arch/x86/include/asm/signal.h b/arch/x86/include/asm/signal.h
index 96ac44f275da..7761a5d554bb 100644
--- a/arch/x86/include/asm/signal.h
+++ b/arch/x86/include/asm/signal.h
@@ -121,6 +121,10 @@ typedef unsigned long sigset_t;
#ifndef __ASSEMBLY__
+# ifdef __KERNEL__
+extern void do_notify_resume(struct pt_regs *, void *, __u32);
+# endif /* __KERNEL__ */
+
#ifdef __i386__
# ifdef __KERNEL__
struct old_sigaction {
@@ -141,8 +145,6 @@ struct k_sigaction {
struct sigaction sa;
};
-extern void do_notify_resume(struct pt_regs *, void *, __u32);
-
# else /* __KERNEL__ */
/* Here we must cater to libcs that poke about in kernel headers. */
diff --git a/arch/x86/include/asm/sparsemem.h b/arch/x86/include/asm/sparsemem.h
index be44f7dab395..e3cc3c063ec5 100644
--- a/arch/x86/include/asm/sparsemem.h
+++ b/arch/x86/include/asm/sparsemem.h
@@ -27,7 +27,7 @@
#else /* CONFIG_X86_32 */
# define SECTION_SIZE_BITS 27 /* matt - 128 is convenient right now */
# define MAX_PHYSADDR_BITS 44
-# define MAX_PHYSMEM_BITS 44
+# define MAX_PHYSMEM_BITS 44 /* Can be max 45 bits */
#endif
#endif /* CONFIG_SPARSEMEM */
diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h
index 87803da44010..9c6797c3e56c 100644
--- a/arch/x86/include/asm/syscalls.h
+++ b/arch/x86/include/asm/syscalls.h
@@ -19,6 +19,13 @@
/* kernel/ioport.c */
asmlinkage long sys_ioperm(unsigned long, unsigned long, int);
+/* kernel/ldt.c */
+asmlinkage int sys_modify_ldt(int, void __user *, unsigned long);
+
+/* kernel/tls.c */
+asmlinkage int sys_set_thread_area(struct user_desc __user *);
+asmlinkage int sys_get_thread_area(struct user_desc __user *);
+
/* X86_32 only */
#ifdef CONFIG_X86_32
/* kernel/process_32.c */
@@ -33,14 +40,11 @@ asmlinkage int sys_sigaction(int, const struct old_sigaction __user *,
struct old_sigaction __user *);
asmlinkage int sys_sigaltstack(unsigned long);
asmlinkage unsigned long sys_sigreturn(unsigned long);
-asmlinkage int sys_rt_sigreturn(unsigned long);
+asmlinkage int sys_rt_sigreturn(struct pt_regs);
/* kernel/ioport.c */
asmlinkage long sys_iopl(unsigned long);
-/* kernel/ldt.c */
-asmlinkage int sys_modify_ldt(int, void __user *, unsigned long);
-
/* kernel/sys_i386_32.c */
asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long);
@@ -54,10 +58,6 @@ asmlinkage int sys_uname(struct old_utsname __user *);
struct oldold_utsname;
asmlinkage int sys_olduname(struct oldold_utsname __user *);
-/* kernel/tls.c */
-asmlinkage int sys_set_thread_area(struct user_desc __user *);
-asmlinkage int sys_get_thread_area(struct user_desc __user *);
-
/* kernel/vm86_32.c */
asmlinkage int sys_vm86old(struct pt_regs);
asmlinkage int sys_vm86(struct pt_regs);
diff --git a/arch/x86/include/asm/system.h b/arch/x86/include/asm/system.h
index 07c3e4048991..8e626ea33a1a 100644
--- a/arch/x86/include/asm/system.h
+++ b/arch/x86/include/asm/system.h
@@ -17,12 +17,12 @@
# define AT_VECTOR_SIZE_ARCH 1
#endif
-#ifdef CONFIG_X86_32
-
struct task_struct; /* one of the stranger aspects of C forward declarations */
struct task_struct *__switch_to(struct task_struct *prev,
struct task_struct *next);
+#ifdef CONFIG_X86_32
+
/*
* Saving eflags is important. It switches not only IOPL between tasks,
* it also protects other tasks from NT leaking through sysenter etc.
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index 0921b4018c11..98789647baa9 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -26,7 +26,7 @@ struct exec_domain;
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
- unsigned long flags; /* low level flags */
+ __u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable,
@@ -93,7 +93,6 @@ struct thread_info {
#define TIF_FORCED_TF 24 /* true if TF in eflags artificially */
#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
-#define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
@@ -115,7 +114,6 @@ struct thread_info {
#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
-#define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
/* work to do in syscall_trace_enter() */
#define _TIF_WORK_SYSCALL_ENTRY \
@@ -141,8 +139,7 @@ struct thread_info {
/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \
- (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_BTS_TRACE_TS| \
- _TIF_NOTSC)
+ (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_NOTSC)
#define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW
#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index 79e31e9dcdda..4e2f2e0aab27 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -61,13 +61,19 @@ static inline int cpu_to_node(int cpu)
*
* Side note: this function creates the returned cpumask on the stack
* so with a high NR_CPUS count, excessive stack space is used. The
- * node_to_cpumask_ptr function should be used whenever possible.
+ * cpumask_of_node function should be used whenever possible.
*/
static inline cpumask_t node_to_cpumask(int node)
{
return node_to_cpumask_map[node];
}
+/* Returns a bitmask of CPUs on Node 'node'. */
+static inline const struct cpumask *cpumask_of_node(int node)
+{
+ return &node_to_cpumask_map[node];
+}
+
#else /* CONFIG_X86_64 */
/* Mappings between node number and cpus on that node. */
@@ -82,7 +88,7 @@ DECLARE_EARLY_PER_CPU(int, x86_cpu_to_node_map);
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
extern int cpu_to_node(int cpu);
extern int early_cpu_to_node(int cpu);
-extern const cpumask_t *_node_to_cpumask_ptr(int node);
+extern const cpumask_t *cpumask_of_node(int node);
extern cpumask_t node_to_cpumask(int node);
#else /* !CONFIG_DEBUG_PER_CPU_MAPS */
@@ -103,7 +109,7 @@ static inline int early_cpu_to_node(int cpu)
}
/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
-static inline const cpumask_t *_node_to_cpumask_ptr(int node)
+static inline const cpumask_t *cpumask_of_node(int node)
{
return &node_to_cpumask_map[node];
}
@@ -116,12 +122,15 @@ static inline cpumask_t node_to_cpumask(int node)
#endif /* !CONFIG_DEBUG_PER_CPU_MAPS */
-/* Replace default node_to_cpumask_ptr with optimized version */
+/*
+ * Replace default node_to_cpumask_ptr with optimized version
+ * Deprecated: use "const struct cpumask *mask = cpumask_of_node(node)"
+ */
#define node_to_cpumask_ptr(v, node) \
- const cpumask_t *v = _node_to_cpumask_ptr(node)
+ const cpumask_t *v = cpumask_of_node(node)
#define node_to_cpumask_ptr_next(v, node) \
- v = _node_to_cpumask_ptr(node)
+ v = cpumask_of_node(node)
#endif /* CONFIG_X86_64 */
@@ -187,7 +196,7 @@ extern int __node_distance(int, int);
#define cpu_to_node(cpu) 0
#define early_cpu_to_node(cpu) 0
-static inline const cpumask_t *_node_to_cpumask_ptr(int node)
+static inline const cpumask_t *cpumask_of_node(int node)
{
return &cpu_online_map;
}
@@ -200,12 +209,15 @@ static inline int node_to_first_cpu(int node)
return first_cpu(cpu_online_map);
}
-/* Replace default node_to_cpumask_ptr with optimized version */
+/*
+ * Replace default node_to_cpumask_ptr with optimized version
+ * Deprecated: use "const struct cpumask *mask = cpumask_of_node(node)"
+ */
#define node_to_cpumask_ptr(v, node) \
- const cpumask_t *v = _node_to_cpumask_ptr(node)
+ const cpumask_t *v = cpumask_of_node(node)
#define node_to_cpumask_ptr_next(v, node) \
- v = _node_to_cpumask_ptr(node)
+ v = cpumask_of_node(node)
#endif
#include <asm-generic/topology.h>
@@ -214,12 +226,12 @@ static inline int node_to_first_cpu(int node)
/* Returns the number of the first CPU on Node 'node'. */
static inline int node_to_first_cpu(int node)
{
- node_to_cpumask_ptr(mask, node);
- return first_cpu(*mask);
+ return cpumask_first(cpumask_of_node(node));
}
#endif
extern cpumask_t cpu_coregroup_map(int cpu);
+extern const struct cpumask *cpu_coregroup_mask(int cpu);
#ifdef ENABLE_TOPO_DEFINES
#define topology_physical_package_id(cpu) (cpu_data(cpu).phys_proc_id)
diff --git a/arch/x86/include/asm/trampoline.h b/arch/x86/include/asm/trampoline.h
index fa0d79facdbc..780ba0ab94f9 100644
--- a/arch/x86/include/asm/trampoline.h
+++ b/arch/x86/include/asm/trampoline.h
@@ -3,6 +3,7 @@
#ifndef __ASSEMBLY__
+#ifdef CONFIG_X86_TRAMPOLINE
/*
* Trampoline 80x86 program as an array.
*/
@@ -13,8 +14,14 @@ extern unsigned char *trampoline_base;
extern unsigned long init_rsp;
extern unsigned long initial_code;
+#define TRAMPOLINE_SIZE roundup(trampoline_end - trampoline_data, PAGE_SIZE)
#define TRAMPOLINE_BASE 0x6000
+
extern unsigned long setup_trampoline(void);
+extern void __init reserve_trampoline_memory(void);
+#else
+static inline void reserve_trampoline_memory(void) {};
+#endif /* CONFIG_X86_TRAMPOLINE */
#endif /* __ASSEMBLY__ */
diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h
index 45dee286e45c..2ee0a3bceedf 100644
--- a/arch/x86/include/asm/traps.h
+++ b/arch/x86/include/asm/traps.h
@@ -46,6 +46,10 @@ dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long);
dotraplinkage void do_invalid_TSS(struct pt_regs *, long);
dotraplinkage void do_segment_not_present(struct pt_regs *, long);
dotraplinkage void do_stack_segment(struct pt_regs *, long);
+#ifdef CONFIG_X86_64
+dotraplinkage void do_double_fault(struct pt_regs *, long);
+asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *);
+#endif
dotraplinkage void do_general_protection(struct pt_regs *, long);
dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long);
@@ -72,10 +76,13 @@ static inline int get_si_code(unsigned long condition)
extern int panic_on_unrecovered_nmi;
extern int kstack_depth_to_print;
-#ifdef CONFIG_X86_32
void math_error(void __user *);
-unsigned long patch_espfix_desc(unsigned long, unsigned long);
asmlinkage void math_emulate(long);
+#ifdef CONFIG_X86_32
+unsigned long patch_espfix_desc(unsigned long, unsigned long);
+#else
+asmlinkage void smp_thermal_interrupt(void);
+asmlinkage void mce_threshold_interrupt(void);
#endif
#endif /* _ASM_X86_TRAPS_H */
diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h
index 9cd83a8e40d5..38ae163cc91b 100644
--- a/arch/x86/include/asm/tsc.h
+++ b/arch/x86/include/asm/tsc.h
@@ -34,8 +34,6 @@ static inline cycles_t get_cycles(void)
static __always_inline cycles_t vget_cycles(void)
{
- cycles_t cycles;
-
/*
* We only do VDSOs on TSC capable CPUs, so this shouldnt
* access boot_cpu_data (which is not VDSO-safe):
@@ -44,11 +42,7 @@ static __always_inline cycles_t vget_cycles(void)
if (!cpu_has_tsc)
return 0;
#endif
- rdtsc_barrier();
- cycles = (cycles_t)__native_read_tsc();
- rdtsc_barrier();
-
- return cycles;
+ return (cycles_t)__native_read_tsc();
}
extern void tsc_init(void);
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 99192bb55a53..4340055b7559 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -352,14 +352,14 @@ do { \
#define __put_user_nocheck(x, ptr, size) \
({ \
- long __pu_err; \
+ int __pu_err; \
__put_user_size((x), (ptr), (size), __pu_err, -EFAULT); \
__pu_err; \
})
#define __get_user_nocheck(x, ptr, size) \
({ \
- long __gu_err; \
+ int __gu_err; \
unsigned long __gu_val; \
__get_user_size(__gu_val, (ptr), (size), __gu_err, -EFAULT); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
diff --git a/arch/x86/include/asm/uv/bios.h b/arch/x86/include/asm/uv/bios.h
index d931d3b7e6f7..7ed17ff502b9 100644
--- a/arch/x86/include/asm/uv/bios.h
+++ b/arch/x86/include/asm/uv/bios.h
@@ -32,13 +32,18 @@
enum uv_bios_cmd {
UV_BIOS_COMMON,
UV_BIOS_GET_SN_INFO,
- UV_BIOS_FREQ_BASE
+ UV_BIOS_FREQ_BASE,
+ UV_BIOS_WATCHLIST_ALLOC,
+ UV_BIOS_WATCHLIST_FREE,
+ UV_BIOS_MEMPROTECT,
+ UV_BIOS_GET_PARTITION_ADDR
};
/*
* Status values returned from a BIOS call.
*/
enum {
+ BIOS_STATUS_MORE_PASSES = 1,
BIOS_STATUS_SUCCESS = 0,
BIOS_STATUS_UNIMPLEMENTED = -ENOSYS,
BIOS_STATUS_EINVAL = -EINVAL,
@@ -71,6 +76,21 @@ union partition_info_u {
};
};
+union uv_watchlist_u {
+ u64 val;
+ struct {
+ u64 blade : 16,
+ size : 32,
+ filler : 16;
+ };
+};
+
+enum uv_memprotect {
+ UV_MEMPROT_RESTRICT_ACCESS,
+ UV_MEMPROT_ALLOW_AMO,
+ UV_MEMPROT_ALLOW_RW
+};
+
/*
* bios calls have 6 parameters
*/
@@ -80,14 +100,20 @@ extern s64 uv_bios_call_reentrant(enum uv_bios_cmd, u64, u64, u64, u64, u64);
extern s64 uv_bios_get_sn_info(int, int *, long *, long *, long *);
extern s64 uv_bios_freq_base(u64, u64 *);
+extern int uv_bios_mq_watchlist_alloc(int, unsigned long, unsigned int,
+ unsigned long *);
+extern int uv_bios_mq_watchlist_free(int, int);
+extern s64 uv_bios_change_memprotect(u64, u64, enum uv_memprotect);
+extern s64 uv_bios_reserved_page_pa(u64, u64 *, u64 *, u64 *);
extern void uv_bios_init(void);
+extern unsigned long sn_rtc_cycles_per_second;
extern int uv_type;
extern long sn_partition_id;
-extern long uv_coherency_id;
-extern long uv_region_size;
-#define partition_coherence_id() (uv_coherency_id)
+extern long sn_coherency_id;
+extern long sn_region_size;
+#define partition_coherence_id() (sn_coherency_id)
extern struct kobject *sgi_uv_kobj; /* /sys/firmware/sgi_uv */
diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h
index 7a5782610b2b..777327ef05c1 100644
--- a/arch/x86/include/asm/uv/uv_hub.h
+++ b/arch/x86/include/asm/uv/uv_hub.h
@@ -113,25 +113,37 @@
*/
#define UV_MAX_NASID_VALUE (UV_MAX_NUMALINK_NODES * 2)
+struct uv_scir_s {
+ struct timer_list timer;
+ unsigned long offset;
+ unsigned long last;
+ unsigned long idle_on;
+ unsigned long idle_off;
+ unsigned char state;
+ unsigned char enabled;
+};
+
/*
* The following defines attributes of the HUB chip. These attributes are
* frequently referenced and are kept in the per-cpu data areas of each cpu.
* They are kept together in a struct to minimize cache misses.
*/
struct uv_hub_info_s {
- unsigned long global_mmr_base;
- unsigned long gpa_mask;
- unsigned long gnode_upper;
- unsigned long lowmem_remap_top;
- unsigned long lowmem_remap_base;
- unsigned short pnode;
- unsigned short pnode_mask;
- unsigned short coherency_domain_number;
- unsigned short numa_blade_id;
- unsigned char blade_processor_id;
- unsigned char m_val;
- unsigned char n_val;
+ unsigned long global_mmr_base;
+ unsigned long gpa_mask;
+ unsigned long gnode_upper;
+ unsigned long lowmem_remap_top;
+ unsigned long lowmem_remap_base;
+ unsigned short pnode;
+ unsigned short pnode_mask;
+ unsigned short coherency_domain_number;
+ unsigned short numa_blade_id;
+ unsigned char blade_processor_id;
+ unsigned char m_val;
+ unsigned char n_val;
+ struct uv_scir_s scir;
};
+
DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
#define uv_hub_info (&__get_cpu_var(__uv_hub_info))
#define uv_cpu_hub_info(cpu) (&per_cpu(__uv_hub_info, cpu))
@@ -163,6 +175,30 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
#define UV_APIC_PNODE_SHIFT 6
+/* Local Bus from cpu's perspective */
+#define LOCAL_BUS_BASE 0x1c00000
+#define LOCAL_BUS_SIZE (4 * 1024 * 1024)
+
+/*
+ * System Controller Interface Reg
+ *
+ * Note there are NO leds on a UV system. This register is only
+ * used by the system controller to monitor system-wide operation.
+ * There are 64 regs per node. With Nahelem cpus (2 cores per node,
+ * 8 cpus per core, 2 threads per cpu) there are 32 cpu threads on
+ * a node.
+ *
+ * The window is located at top of ACPI MMR space
+ */
+#define SCIR_WINDOW_COUNT 64
+#define SCIR_LOCAL_MMR_BASE (LOCAL_BUS_BASE + \
+ LOCAL_BUS_SIZE - \
+ SCIR_WINDOW_COUNT)
+
+#define SCIR_CPU_HEARTBEAT 0x01 /* timer interrupt */
+#define SCIR_CPU_ACTIVITY 0x02 /* not idle */
+#define SCIR_CPU_HB_INTERVAL (HZ) /* once per second */
+
/*
* Macros for converting between kernel virtual addresses, socket local physical
* addresses, and UV global physical addresses.
@@ -174,7 +210,7 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr)
{
if (paddr < uv_hub_info->lowmem_remap_top)
- paddr += uv_hub_info->lowmem_remap_base;
+ paddr |= uv_hub_info->lowmem_remap_base;
return paddr | uv_hub_info->gnode_upper;
}
@@ -182,19 +218,7 @@ static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr)
/* socket virtual --> UV global physical address */
static inline unsigned long uv_gpa(void *v)
{
- return __pa(v) | uv_hub_info->gnode_upper;
-}
-
-/* socket virtual --> UV global physical address */
-static inline void *uv_vgpa(void *v)
-{
- return (void *)uv_gpa(v);
-}
-
-/* UV global physical address --> socket virtual */
-static inline void *uv_va(unsigned long gpa)
-{
- return __va(gpa & uv_hub_info->gpa_mask);
+ return uv_soc_phys_ram_to_gpa(__pa(v));
}
/* pnode, offset --> socket virtual */
@@ -277,6 +301,16 @@ static inline void uv_write_local_mmr(unsigned long offset, unsigned long val)
*uv_local_mmr_address(offset) = val;
}
+static inline unsigned char uv_read_local_mmr8(unsigned long offset)
+{
+ return *((unsigned char *)uv_local_mmr_address(offset));
+}
+
+static inline void uv_write_local_mmr8(unsigned long offset, unsigned char val)
+{
+ *((unsigned char *)uv_local_mmr_address(offset)) = val;
+}
+
/*
* Structures and definitions for converting between cpu, node, pnode, and blade
* numbers.
@@ -351,5 +385,20 @@ static inline int uv_num_possible_blades(void)
return uv_possible_blades;
}
-#endif /* _ASM_X86_UV_UV_HUB_H */
+/* Update SCIR state */
+static inline void uv_set_scir_bits(unsigned char value)
+{
+ if (uv_hub_info->scir.state != value) {
+ uv_hub_info->scir.state = value;
+ uv_write_local_mmr8(uv_hub_info->scir.offset, value);
+ }
+}
+static inline void uv_set_cpu_scir_bits(int cpu, unsigned char value)
+{
+ if (uv_cpu_hub_info(cpu)->scir.state != value) {
+ uv_cpu_hub_info(cpu)->scir.state = value;
+ uv_write_local_mmr8(uv_cpu_hub_info(cpu)->scir.offset, value);
+ }
+}
+#endif /* _ASM_X86_UV_UV_HUB_H */
diff --git a/arch/x86/include/asm/vmware.h b/arch/x86/include/asm/vmware.h
new file mode 100644
index 000000000000..c11b7e100d83
--- /dev/null
+++ b/arch/x86/include/asm/vmware.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008, VMware, 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef ASM_X86__VMWARE_H
+#define ASM_X86__VMWARE_H
+
+extern unsigned long vmware_get_tsc_khz(void);
+extern int vmware_platform(void);
+extern void vmware_set_feature_bits(struct cpuinfo_x86 *c);
+
+#endif
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 3f6000d95fe2..5e79ca694326 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -33,8 +33,14 @@
#ifndef _ASM_X86_XEN_HYPERCALL_H
#define _ASM_X86_XEN_HYPERCALL_H
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h
index a38d25ac87d2..81fbd735aec4 100644
--- a/arch/x86/include/asm/xen/hypervisor.h
+++ b/arch/x86/include/asm/xen/hypervisor.h
@@ -33,39 +33,10 @@
#ifndef _ASM_X86_XEN_HYPERVISOR_H
#define _ASM_X86_XEN_HYPERVISOR_H
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#include <xen/interface/xen.h>
-#include <xen/interface/version.h>
-
-#include <asm/ptrace.h>
-#include <asm/page.h>
-#include <asm/desc.h>
-#if defined(__i386__)
-# ifdef CONFIG_X86_PAE
-# include <asm-generic/pgtable-nopud.h>
-# else
-# include <asm-generic/pgtable-nopmd.h>
-# endif
-#endif
-#include <asm/xen/hypercall.h>
-
/* arch/i386/kernel/setup.c */
extern struct shared_info *HYPERVISOR_shared_info;
extern struct start_info *xen_start_info;
-/* arch/i386/mach-xen/evtchn.c */
-/* Force a proper event-channel callback from Xen. */
-extern void force_evtchn_callback(void);
-
-/* Turn jiffies into Xen system time. */
-u64 jiffies_to_st(unsigned long jiffies);
-
-
-#define MULTI_UVMFLAGS_INDEX 3
-#define MULTI_UVMDOMID_INDEX 4
-
enum xen_domain_type {
XEN_NATIVE,
XEN_PV_DOMAIN,
@@ -74,9 +45,15 @@ enum xen_domain_type {
extern enum xen_domain_type xen_domain_type;
+#ifdef CONFIG_XEN
#define xen_domain() (xen_domain_type != XEN_NATIVE)
-#define xen_pv_domain() (xen_domain_type == XEN_PV_DOMAIN)
+#else
+#define xen_domain() (0)
+#endif
+
+#define xen_pv_domain() (xen_domain() && xen_domain_type == XEN_PV_DOMAIN)
+#define xen_hvm_domain() (xen_domain() && xen_domain_type == XEN_HVM_DOMAIN)
+
#define xen_initial_domain() (xen_pv_domain() && xen_start_info->flags & SIF_INITDOMAIN)
-#define xen_hvm_domain() (xen_domain_type == XEN_HVM_DOMAIN)
#endif /* _ASM_X86_XEN_HYPERVISOR_H */
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
index bc628998a1b9..7ef617ef1df3 100644
--- a/arch/x86/include/asm/xen/page.h
+++ b/arch/x86/include/asm/xen/page.h
@@ -1,11 +1,16 @@
#ifndef _ASM_X86_XEN_PAGE_H
#define _ASM_X86_XEN_PAGE_H
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
#include <linux/pfn.h>
#include <asm/uaccess.h>
+#include <asm/page.h>
#include <asm/pgtable.h>
+#include <xen/interface/xen.h>
#include <xen/features.h>
/* Xen machine address */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 1cad9318d217..d364df03c1d6 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -12,6 +12,7 @@ CFLAGS_REMOVE_tsc.o = -pg
CFLAGS_REMOVE_rtc.o = -pg
CFLAGS_REMOVE_paravirt-spinlocks.o = -pg
CFLAGS_REMOVE_ftrace.o = -pg
+CFLAGS_REMOVE_early_printk.o = -pg
endif
#
@@ -23,7 +24,7 @@ CFLAGS_vsyscall_64.o := $(PROFILING) -g0 $(nostackp)
CFLAGS_hpet.o := $(nostackp)
CFLAGS_tsc.o := $(nostackp)
-obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o
+obj-y := process_$(BITS).o signal.o entry_$(BITS).o
obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time_$(BITS).o ioport.o ldt.o dumpstack.o
obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o
@@ -106,6 +107,10 @@ microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o
obj-$(CONFIG_MICROCODE) += microcode.o
+obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
+
+obj-$(CONFIG_SWIOTLB) += pci-swiotlb_64.o # NB rename without _64
+
###
# 64 bit specific files
ifeq ($(CONFIG_X86_64),y)
@@ -119,7 +124,6 @@ ifeq ($(CONFIG_X86_64),y)
obj-$(CONFIG_GART_IOMMU) += pci-gart_64.o aperture_64.o
obj-$(CONFIG_CALGARY_IOMMU) += pci-calgary_64.o tce_64.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu_init.o amd_iommu.o
- obj-$(CONFIG_SWIOTLB) += pci-swiotlb_64.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconf-fam10h_64.o
endif
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c
index a7b6dec6fc3f..2e2da717b350 100644
--- a/arch/x86/kernel/amd_iommu.c
+++ b/arch/x86/kernel/amd_iommu.c
@@ -24,6 +24,7 @@
#include <linux/iommu-helper.h>
#include <asm/proto.h>
#include <asm/iommu.h>
+#include <asm/gart.h>
#include <asm/amd_iommu_types.h>
#include <asm/amd_iommu.h>
@@ -235,8 +236,9 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
status &= ~MMIO_STATUS_COM_WAIT_INT_MASK;
writel(status, iommu->mmio_base + MMIO_STATUS_OFFSET);
- if (unlikely((i == EXIT_LOOP_COUNT) && printk_ratelimit()))
- printk(KERN_WARNING "AMD IOMMU: Completion wait loop failed\n");
+ if (unlikely(i == EXIT_LOOP_COUNT))
+ panic("AMD IOMMU: Completion wait loop failed\n");
+
out:
spin_unlock_irqrestore(&iommu->lock, flags);
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c
index 30ae2701b3df..c625800c55ca 100644
--- a/arch/x86/kernel/amd_iommu_init.c
+++ b/arch/x86/kernel/amd_iommu_init.c
@@ -28,6 +28,7 @@
#include <asm/amd_iommu_types.h>
#include <asm/amd_iommu.h>
#include <asm/iommu.h>
+#include <asm/gart.h>
/*
* definitions for the ACPI scanning code
@@ -427,6 +428,10 @@ static u8 * __init alloc_command_buffer(struct amd_iommu *iommu)
memcpy_toio(iommu->mmio_base + MMIO_CMD_BUF_OFFSET,
&entry, sizeof(entry));
+ /* set head and tail to zero manually */
+ writel(0x00, iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
+ writel(0x00, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
+
iommu_feature_enable(iommu, CONTROL_CMDBUF_EN);
return cmd_buf;
@@ -1074,7 +1079,8 @@ int __init amd_iommu_init(void)
goto free;
/* IOMMU rlookup table - find the IOMMU for a specific device */
- amd_iommu_rlookup_table = (void *)__get_free_pages(GFP_KERNEL,
+ amd_iommu_rlookup_table = (void *)__get_free_pages(
+ GFP_KERNEL | __GFP_ZERO,
get_order(rlookup_table_size));
if (amd_iommu_rlookup_table == NULL)
goto free;
diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c
index 9a32b37ee2ee..676debfc1702 100644
--- a/arch/x86/kernel/aperture_64.c
+++ b/arch/x86/kernel/aperture_64.c
@@ -1,8 +1,9 @@
/*
* Firmware replacement code.
*
- * Work around broken BIOSes that don't set an aperture or only set the
- * aperture in the AGP bridge.
+ * Work around broken BIOSes that don't set an aperture, only set the
+ * aperture in the AGP bridge, or set too small aperture.
+ *
* If all fails map the aperture over some low memory. This is cheaper than
* doing bounce buffering. The memory is lost. This is done at early boot
* because only the bootmem allocator can allocate 32+MB.
diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c
index b9019271af62..6b7f824db160 100644
--- a/arch/x86/kernel/apic.c
+++ b/arch/x86/kernel/apic.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/dmar.h>
+#include <linux/ftrace.h>
#include <asm/atomic.h>
#include <asm/smp.h>
@@ -775,11 +776,7 @@ static void local_apic_timer_interrupt(void)
/*
* the NMI deadlock-detector uses this.
*/
-#ifdef CONFIG_X86_64
- add_pda(apic_timer_irqs, 1);
-#else
- per_cpu(irq_stat, cpu).apic_timer_irqs++;
-#endif
+ inc_irq_stat(apic_timer_irqs);
evt->event_handler(evt);
}
@@ -792,7 +789,7 @@ static void local_apic_timer_interrupt(void)
* [ if a single-CPU system runs an SMP kernel then we call the local
* interrupt as well. Thus we cannot inline the local irq ... ]
*/
-void smp_apic_timer_interrupt(struct pt_regs *regs)
+void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
@@ -806,9 +803,7 @@ void smp_apic_timer_interrupt(struct pt_regs *regs)
* Besides, if we don't timer interrupts ignore the global
* interrupt lock, which is the WrongThing (tm) to do.
*/
-#ifdef CONFIG_X86_64
exit_idle();
-#endif
irq_enter();
local_apic_timer_interrupt();
irq_exit();
@@ -1666,9 +1661,7 @@ void smp_spurious_interrupt(struct pt_regs *regs)
{
u32 v;
-#ifdef CONFIG_X86_64
exit_idle();
-#endif
irq_enter();
/*
* Check if this really is a spurious interrupt and ACK it
@@ -1679,14 +1672,11 @@ void smp_spurious_interrupt(struct pt_regs *regs)
if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
ack_APIC_irq();
-#ifdef CONFIG_X86_64
- add_pda(irq_spurious_count, 1);
-#else
+ inc_irq_stat(irq_spurious_count);
+
/* see sw-dev-man vol 3, chapter 7.4.13.5 */
pr_info("spurious APIC interrupt on CPU#%d, "
"should never happen.\n", smp_processor_id());
- __get_cpu_var(irq_stat).irq_spurious_count++;
-#endif
irq_exit();
}
@@ -1697,9 +1687,7 @@ void smp_error_interrupt(struct pt_regs *regs)
{
u32 v, v1;
-#ifdef CONFIG_X86_64
exit_idle();
-#endif
irq_enter();
/* First tickle the hardware, only then report what went on. -- REW */
v = apic_read(APIC_ESR);
diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c
index 6649d09ad88f..ee4df08feee6 100644
--- a/arch/x86/kernel/asm-offsets_32.c
+++ b/arch/x86/kernel/asm-offsets_32.c
@@ -11,7 +11,7 @@
#include <linux/suspend.h>
#include <linux/kbuild.h>
#include <asm/ucontext.h>
-#include "sigframe.h"
+#include <asm/sigframe.h>
#include <asm/pgtable.h>
#include <asm/fixmap.h>
#include <asm/processor.h>
diff --git a/arch/x86/kernel/asm-offsets_64.c b/arch/x86/kernel/asm-offsets_64.c
index 7fcf63d22f8b..1d41d3f1edbc 100644
--- a/arch/x86/kernel/asm-offsets_64.c
+++ b/arch/x86/kernel/asm-offsets_64.c
@@ -20,6 +20,8 @@
#include <xen/interface/xen.h>
+#include <asm/sigframe.h>
+
#define __NO_STUBS 1
#undef __SYSCALL
#undef _ASM_X86_UNISTD_64_H
@@ -87,7 +89,7 @@ int main(void)
BLANK();
#undef ENTRY
DEFINE(IA32_RT_SIGFRAME_sigcontext,
- offsetof (struct rt_sigframe32, uc.uc_mcontext));
+ offsetof (struct rt_sigframe_ia32, uc.uc_mcontext));
BLANK();
#endif
DEFINE(pbe_address, offsetof(struct pbe, address));
diff --git a/arch/x86/kernel/bios_uv.c b/arch/x86/kernel/bios_uv.c
index f0dfe6f17e7e..2a0a2a3cac26 100644
--- a/arch/x86/kernel/bios_uv.c
+++ b/arch/x86/kernel/bios_uv.c
@@ -69,10 +69,10 @@ s64 uv_bios_call_reentrant(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3,
long sn_partition_id;
EXPORT_SYMBOL_GPL(sn_partition_id);
-long uv_coherency_id;
-EXPORT_SYMBOL_GPL(uv_coherency_id);
-long uv_region_size;
-EXPORT_SYMBOL_GPL(uv_region_size);
+long sn_coherency_id;
+EXPORT_SYMBOL_GPL(sn_coherency_id);
+long sn_region_size;
+EXPORT_SYMBOL_GPL(sn_region_size);
int uv_type;
@@ -100,6 +100,56 @@ s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher,
return ret;
}
+int
+uv_bios_mq_watchlist_alloc(int blade, unsigned long addr, unsigned int mq_size,
+ unsigned long *intr_mmr_offset)
+{
+ union uv_watchlist_u size_blade;
+ u64 watchlist;
+ s64 ret;
+
+ size_blade.size = mq_size;
+ size_blade.blade = blade;
+
+ /*
+ * bios returns watchlist number or negative error number.
+ */
+ ret = (int)uv_bios_call_irqsave(UV_BIOS_WATCHLIST_ALLOC, addr,
+ size_blade.val, (u64)intr_mmr_offset,
+ (u64)&watchlist, 0);
+ if (ret < BIOS_STATUS_SUCCESS)
+ return ret;
+
+ return watchlist;
+}
+EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_alloc);
+
+int
+uv_bios_mq_watchlist_free(int blade, int watchlist_num)
+{
+ return (int)uv_bios_call_irqsave(UV_BIOS_WATCHLIST_FREE,
+ blade, watchlist_num, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(uv_bios_mq_watchlist_free);
+
+s64
+uv_bios_change_memprotect(u64 paddr, u64 len, enum uv_memprotect perms)
+{
+ return uv_bios_call_irqsave(UV_BIOS_MEMPROTECT, paddr, len,
+ perms, 0, 0);
+}
+EXPORT_SYMBOL_GPL(uv_bios_change_memprotect);
+
+s64
+uv_bios_reserved_page_pa(u64 buf, u64 *cookie, u64 *addr, u64 *len)
+{
+ s64 ret;
+
+ ret = uv_bios_call_irqsave(UV_BIOS_GET_PARTITION_ADDR, (u64)cookie,
+ (u64)addr, buf, (u64)len, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(uv_bios_reserved_page_pa);
s64 uv_bios_freq_base(u64 clock_type, u64 *ticks_per_second)
{
diff --git a/arch/x86/kernel/check.c b/arch/x86/kernel/check.c
new file mode 100644
index 000000000000..2ac0ab71412a
--- /dev/null
+++ b/arch/x86/kernel/check.c
@@ -0,0 +1,161 @@
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/workqueue.h>
+#include <asm/e820.h>
+#include <asm/proto.h>
+
+/*
+ * Some BIOSes seem to corrupt the low 64k of memory during events
+ * like suspend/resume and unplugging an HDMI cable. Reserve all
+ * remaining free memory in that area and fill it with a distinct
+ * pattern.
+ */
+#define MAX_SCAN_AREAS 8
+
+static int __read_mostly memory_corruption_check = -1;
+
+static unsigned __read_mostly corruption_check_size = 64*1024;
+static unsigned __read_mostly corruption_check_period = 60; /* seconds */
+
+static struct e820entry scan_areas[MAX_SCAN_AREAS];
+static int num_scan_areas;
+
+
+static __init int set_corruption_check(char *arg)
+{
+ char *end;
+
+ memory_corruption_check = simple_strtol(arg, &end, 10);
+
+ return (*end == 0) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check", set_corruption_check);
+
+static __init int set_corruption_check_period(char *arg)
+{
+ char *end;
+
+ corruption_check_period = simple_strtoul(arg, &end, 10);
+
+ return (*end == 0) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check_period", set_corruption_check_period);
+
+static __init int set_corruption_check_size(char *arg)
+{
+ char *end;
+ unsigned size;
+
+ size = memparse(arg, &end);
+
+ if (*end == '\0')
+ corruption_check_size = size;
+
+ return (size == corruption_check_size) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check_size", set_corruption_check_size);
+
+
+void __init setup_bios_corruption_check(void)
+{
+ u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */
+
+ if (memory_corruption_check == -1) {
+ memory_corruption_check =
+#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
+ 1
+#else
+ 0
+#endif
+ ;
+ }
+
+ if (corruption_check_size == 0)
+ memory_corruption_check = 0;
+
+ if (!memory_corruption_check)
+ return;
+
+ corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
+
+ while (addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
+ u64 size;
+ addr = find_e820_area_size(addr, &size, PAGE_SIZE);
+
+ if (addr == 0)
+ break;
+
+ if ((addr + size) > corruption_check_size)
+ size = corruption_check_size - addr;
+
+ if (size == 0)
+ break;
+
+ e820_update_range(addr, size, E820_RAM, E820_RESERVED);
+ scan_areas[num_scan_areas].addr = addr;
+ scan_areas[num_scan_areas].size = size;
+ num_scan_areas++;
+
+ /* Assume we've already mapped this early memory */
+ memset(__va(addr), 0, size);
+
+ addr += size;
+ }
+
+ printk(KERN_INFO "Scanning %d areas for low memory corruption\n",
+ num_scan_areas);
+ update_e820();
+}
+
+
+void check_for_bios_corruption(void)
+{
+ int i;
+ int corruption = 0;
+
+ if (!memory_corruption_check)
+ return;
+
+ for (i = 0; i < num_scan_areas; i++) {
+ unsigned long *addr = __va(scan_areas[i].addr);
+ unsigned long size = scan_areas[i].size;
+
+ for (; size; addr++, size -= sizeof(unsigned long)) {
+ if (!*addr)
+ continue;
+ printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n",
+ addr, __pa(addr), *addr);
+ corruption = 1;
+ *addr = 0;
+ }
+ }
+
+ WARN_ONCE(corruption, KERN_ERR "Memory corruption detected in low memory\n");
+}
+
+static void check_corruption(struct work_struct *dummy);
+static DECLARE_DELAYED_WORK(bios_check_work, check_corruption);
+
+static void check_corruption(struct work_struct *dummy)
+{
+ check_for_bios_corruption();
+ schedule_delayed_work(&bios_check_work,
+ round_jiffies_relative(corruption_check_period*HZ));
+}
+
+static int start_periodic_check_for_corruption(void)
+{
+ if (!memory_corruption_check || corruption_check_period == 0)
+ return 0;
+
+ printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
+ corruption_check_period);
+
+ /* First time we run the checks right away */
+ schedule_delayed_work(&bios_check_work, 0);
+ return 0;
+}
+
+module_init(start_periodic_check_for_corruption);
+
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 82ec6075c057..82db7f45e2de 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -2,8 +2,14 @@
# Makefile for x86-compatible CPU details and quirks
#
+# Don't trace early stages of a secondary CPU boot
+ifdef CONFIG_FUNCTION_TRACER
+CFLAGS_REMOVE_common.o = -pg
+endif
+
obj-y := intel_cacheinfo.o addon_cpuid_features.o
obj-y += proc.o capflags.o powerflags.o common.o
+obj-y += vmware.o hypervisor.o
obj-$(CONFIG_X86_32) += bugs.o cmpxchg.o
obj-$(CONFIG_X86_64) += bugs_64.o
diff --git a/arch/x86/kernel/cpu/addon_cpuid_features.c b/arch/x86/kernel/cpu/addon_cpuid_features.c
index ef8f831af823..2cf23634b6d9 100644
--- a/arch/x86/kernel/cpu/addon_cpuid_features.c
+++ b/arch/x86/kernel/cpu/addon_cpuid_features.c
@@ -120,9 +120,17 @@ void __cpuinit detect_extended_topology(struct cpuinfo_x86 *c)
c->cpu_core_id = phys_pkg_id(c->initial_apicid, ht_mask_width)
& core_select_mask;
c->phys_proc_id = phys_pkg_id(c->initial_apicid, core_plus_mask_width);
+ /*
+ * Reinit the apicid, now that we have extended initial_apicid.
+ */
+ c->apicid = phys_pkg_id(c->initial_apicid, 0);
#else
c->cpu_core_id = phys_pkg_id(ht_mask_width) & core_select_mask;
c->phys_proc_id = phys_pkg_id(core_plus_mask_width);
+ /*
+ * Reinit the apicid, now that we have extended initial_apicid.
+ */
+ c->apicid = phys_pkg_id(0);
#endif
c->x86_max_cores = (core_level_siblings / smp_num_siblings);
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 8f1e31db2ad5..7c878f6aa919 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -283,9 +283,14 @@ static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
{
early_init_amd_mc(c);
- /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */
- if (c->x86_power & (1<<8))
+ /*
+ * c->x86_power is 8000_0007 edx. Bit 8 is TSC runs at constant rate
+ * with P/T states and does not stop in deep C-states
+ */
+ if (c->x86_power & (1 << 8)) {
set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+ set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC);
+ }
#ifdef CONFIG_X86_64
set_cpu_cap(c, X86_FEATURE_SYSCALL32);
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index b9c9ea0217a9..42e0853030cb 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -36,6 +36,7 @@
#include <asm/proto.h>
#include <asm/sections.h>
#include <asm/setup.h>
+#include <asm/hypervisor.h>
#include "cpu.h"
@@ -703,6 +704,7 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
detect_ht(c);
#endif
+ init_hypervisor(c);
/*
* On SMP, boot_cpu_data holds the common feature set between
* all CPUs; so make sure that we indicate which features are
@@ -862,7 +864,7 @@ EXPORT_SYMBOL(_cpu_pda);
struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table };
-char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss;
+static char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss;
void __cpuinit pda_init(int cpu)
{
@@ -903,8 +905,8 @@ void __cpuinit pda_init(int cpu)
}
}
-char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ +
- DEBUG_STKSZ] __page_aligned_bss;
+static char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ +
+ DEBUG_STKSZ] __page_aligned_bss;
extern asmlinkage void ignore_sysret(void);
diff --git a/arch/x86/kernel/cpu/hypervisor.c b/arch/x86/kernel/cpu/hypervisor.c
new file mode 100644
index 000000000000..fb5b86af0b01
--- /dev/null
+++ b/arch/x86/kernel/cpu/hypervisor.c
@@ -0,0 +1,58 @@
+/*
+ * Common hypervisor code
+ *
+ * Copyright (C) 2008, VMware, Inc.
+ * Author : Alok N Kataria <akataria@vmware.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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <asm/processor.h>
+#include <asm/vmware.h>
+#include <asm/hypervisor.h>
+
+static inline void __cpuinit
+detect_hypervisor_vendor(struct cpuinfo_x86 *c)
+{
+ if (vmware_platform()) {
+ c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE;
+ } else {
+ c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
+ }
+}
+
+unsigned long get_hypervisor_tsc_freq(void)
+{
+ if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE)
+ return vmware_get_tsc_khz();
+ return 0;
+}
+
+static inline void __cpuinit
+hypervisor_set_feature_bits(struct cpuinfo_x86 *c)
+{
+ if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE) {
+ vmware_set_feature_bits(c);
+ return;
+ }
+}
+
+void __cpuinit init_hypervisor(struct cpuinfo_x86 *c)
+{
+ detect_hypervisor_vendor(c);
+ hypervisor_set_feature_bits(c);
+}
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 816f27f289b1..8ea6929e974c 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -11,7 +11,6 @@
#include <asm/pgtable.h>
#include <asm/msr.h>
#include <asm/uaccess.h>
-#include <asm/ptrace.h>
#include <asm/ds.h>
#include <asm/bugs.h>
@@ -41,6 +40,16 @@ static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
if (c->x86 == 15 && c->x86_cache_alignment == 64)
c->x86_cache_alignment = 128;
#endif
+
+ /*
+ * c->x86_power is 8000_0007 edx. Bit 8 is TSC runs at constant rate
+ * with P/T states and does not stop in deep C-states
+ */
+ if (c->x86_power & (1 << 8)) {
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+ set_cpu_cap(c, X86_FEATURE_NONSTOP_TSC);
+ }
+
}
#ifdef CONFIG_X86_32
@@ -242,6 +251,13 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c)
intel_workarounds(c);
+ /*
+ * Detect the extended topology information if available. This
+ * will reinitialise the initial_apicid which will be used
+ * in init_intel_cacheinfo()
+ */
+ detect_extended_topology(c);
+
l2 = init_intel_cacheinfo(c);
if (c->cpuid_level > 9) {
unsigned eax = cpuid_eax(10);
@@ -309,10 +325,6 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c)
set_cpu_cap(c, X86_FEATURE_P3);
#endif
- if (cpu_has_bts)
- ptrace_bts_init_intel(c);
-
- detect_extended_topology(c);
if (!cpu_has(c, X86_FEATURE_XTOPOLOGY)) {
/*
* let's use the legacy cpuid vector 0x1 and 0x4 for topology
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index 7bd00a565672..48533d77be78 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -641,20 +641,17 @@ static inline ssize_t show_shared_cpu_list(struct _cpuid4_info *leaf, char *buf)
return show_shared_cpu_map_func(leaf, 1, buf);
}
-static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf) {
- switch(this_leaf->eax.split.type) {
- case CACHE_TYPE_DATA:
+static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf)
+{
+ switch (this_leaf->eax.split.type) {
+ case CACHE_TYPE_DATA:
return sprintf(buf, "Data\n");
- break;
- case CACHE_TYPE_INST:
+ case CACHE_TYPE_INST:
return sprintf(buf, "Instruction\n");
- break;
- case CACHE_TYPE_UNIFIED:
+ case CACHE_TYPE_UNIFIED:
return sprintf(buf, "Unified\n");
- break;
- default:
+ default:
return sprintf(buf, "Unknown\n");
- break;
}
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c
index 4b031a4ac856..1c838032fd37 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_64.c
@@ -510,12 +510,9 @@ static void __cpuinit mce_cpu_features(struct cpuinfo_x86 *c)
*/
void __cpuinit mcheck_init(struct cpuinfo_x86 *c)
{
- static cpumask_t mce_cpus = CPU_MASK_NONE;
-
mce_cpu_quirks(c);
if (mce_dont_init ||
- cpu_test_and_set(smp_processor_id(), mce_cpus) ||
!mce_available(c))
return;
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd_64.c b/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
index a1de80f368f1..a5a5e0530370 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
@@ -248,7 +248,7 @@ asmlinkage void mce_threshold_interrupt(void)
}
}
out:
- add_pda(irq_threshold_count, 1);
+ inc_irq_stat(irq_threshold_count);
irq_exit();
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel_64.c b/arch/x86/kernel/cpu/mcheck/mce_intel_64.c
index c17eaf5dd6dd..4b48f251fd39 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_intel_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_intel_64.c
@@ -26,7 +26,7 @@ asmlinkage void smp_thermal_interrupt(void)
if (therm_throt_process(msr_val & 1))
mce_log_therm_throt_event(smp_processor_id(), msr_val);
- add_pda(irq_thermal_count, 1);
+ inc_irq_stat(irq_thermal_count);
irq_exit();
}
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c
index c78c04821ea1..1159e269e596 100644
--- a/arch/x86/kernel/cpu/mtrr/main.c
+++ b/arch/x86/kernel/cpu/mtrr/main.c
@@ -803,6 +803,7 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
}
static struct res_range __initdata range[RANGE_NUM];
+static int __initdata nr_range;
#ifdef CONFIG_MTRR_SANITIZER
@@ -1206,39 +1207,43 @@ struct mtrr_cleanup_result {
#define PSHIFT (PAGE_SHIFT - 10)
static struct mtrr_cleanup_result __initdata result[NUM_RESULT];
-static struct res_range __initdata range_new[RANGE_NUM];
static unsigned long __initdata min_loss_pfn[RANGE_NUM];
-static int __init mtrr_cleanup(unsigned address_bits)
+static void __init print_out_mtrr_range_state(void)
{
- unsigned long extra_remove_base, extra_remove_size;
- unsigned long base, size, def, dummy;
- mtrr_type type;
- int nr_range, nr_range_new;
- u64 chunk_size, gran_size;
- unsigned long range_sums, range_sums_new;
- int index_good;
- int num_reg_good;
int i;
+ char start_factor = 'K', size_factor = 'K';
+ unsigned long start_base, size_base;
+ mtrr_type type;
- /* extra one for all 0 */
- int num[MTRR_NUM_TYPES + 1];
+ for (i = 0; i < num_var_ranges; i++) {
- if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
- return 0;
- rdmsr(MTRRdefType_MSR, def, dummy);
- def &= 0xff;
- if (def != MTRR_TYPE_UNCACHABLE)
- return 0;
+ size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10);
+ if (!size_base)
+ continue;
- /* get it and store it aside */
- memset(range_state, 0, sizeof(range_state));
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
- range_state[i].base_pfn = base;
- range_state[i].size_pfn = size;
- range_state[i].type = type;
+ size_base = to_size_factor(size_base, &size_factor),
+ start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10);
+ start_base = to_size_factor(start_base, &start_factor),
+ type = range_state[i].type;
+
+ printk(KERN_DEBUG "reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
+ i, start_base, start_factor,
+ size_base, size_factor,
+ (type == MTRR_TYPE_UNCACHABLE) ? "UC" :
+ ((type == MTRR_TYPE_WRPROT) ? "WP" :
+ ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other"))
+ );
}
+}
+
+static int __init mtrr_need_cleanup(void)
+{
+ int i;
+ mtrr_type type;
+ unsigned long size;
+ /* extra one for all 0 */
+ int num[MTRR_NUM_TYPES + 1];
/* check entries number */
memset(num, 0, sizeof(num));
@@ -1263,29 +1268,133 @@ static int __init mtrr_cleanup(unsigned address_bits)
num_var_ranges - num[MTRR_NUM_TYPES])
return 0;
- /* print original var MTRRs at first, for debugging: */
- printk(KERN_DEBUG "original variable MTRRs\n");
- for (i = 0; i < num_var_ranges; i++) {
- char start_factor = 'K', size_factor = 'K';
- unsigned long start_base, size_base;
+ return 1;
+}
- size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10);
- if (!size_base)
- continue;
+static unsigned long __initdata range_sums;
+static void __init mtrr_calc_range_state(u64 chunk_size, u64 gran_size,
+ unsigned long extra_remove_base,
+ unsigned long extra_remove_size,
+ int i)
+{
+ int num_reg;
+ static struct res_range range_new[RANGE_NUM];
+ static int nr_range_new;
+ unsigned long range_sums_new;
+
+ /* convert ranges to var ranges state */
+ num_reg = x86_setup_var_mtrrs(range, nr_range,
+ chunk_size, gran_size);
+
+ /* we got new setting in range_state, check it */
+ memset(range_new, 0, sizeof(range_new));
+ nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
+ extra_remove_base, extra_remove_size);
+ range_sums_new = sum_ranges(range_new, nr_range_new);
+
+ result[i].chunk_sizek = chunk_size >> 10;
+ result[i].gran_sizek = gran_size >> 10;
+ result[i].num_reg = num_reg;
+ if (range_sums < range_sums_new) {
+ result[i].lose_cover_sizek =
+ (range_sums_new - range_sums) << PSHIFT;
+ result[i].bad = 1;
+ } else
+ result[i].lose_cover_sizek =
+ (range_sums - range_sums_new) << PSHIFT;
- size_base = to_size_factor(size_base, &size_factor),
- start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10);
- start_base = to_size_factor(start_base, &start_factor),
- type = range_state[i].type;
+ /* double check it */
+ if (!result[i].bad && !result[i].lose_cover_sizek) {
+ if (nr_range_new != nr_range ||
+ memcmp(range, range_new, sizeof(range)))
+ result[i].bad = 1;
+ }
- printk(KERN_DEBUG "reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
- i, start_base, start_factor,
- size_base, size_factor,
- (type == MTRR_TYPE_UNCACHABLE) ? "UC" :
- ((type == MTRR_TYPE_WRPROT) ? "WP" :
- ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other"))
- );
+ if (!result[i].bad && (range_sums - range_sums_new <
+ min_loss_pfn[num_reg])) {
+ min_loss_pfn[num_reg] =
+ range_sums - range_sums_new;
}
+}
+
+static void __init mtrr_print_out_one_result(int i)
+{
+ char gran_factor, chunk_factor, lose_factor;
+ unsigned long gran_base, chunk_base, lose_base;
+
+ gran_base = to_size_factor(result[i].gran_sizek, &gran_factor),
+ chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor),
+ lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor),
+ printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t",
+ result[i].bad ? "*BAD*" : " ",
+ gran_base, gran_factor, chunk_base, chunk_factor);
+ printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n",
+ result[i].num_reg, result[i].bad ? "-" : "",
+ lose_base, lose_factor);
+}
+
+static int __init mtrr_search_optimal_index(void)
+{
+ int i;
+ int num_reg_good;
+ int index_good;
+
+ if (nr_mtrr_spare_reg >= num_var_ranges)
+ nr_mtrr_spare_reg = num_var_ranges - 1;
+ num_reg_good = -1;
+ for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) {
+ if (!min_loss_pfn[i])
+ num_reg_good = i;
+ }
+
+ index_good = -1;
+ if (num_reg_good != -1) {
+ for (i = 0; i < NUM_RESULT; i++) {
+ if (!result[i].bad &&
+ result[i].num_reg == num_reg_good &&
+ !result[i].lose_cover_sizek) {
+ index_good = i;
+ break;
+ }
+ }
+ }
+
+ return index_good;
+}
+
+
+static int __init mtrr_cleanup(unsigned address_bits)
+{
+ unsigned long extra_remove_base, extra_remove_size;
+ unsigned long base, size, def, dummy;
+ mtrr_type type;
+ u64 chunk_size, gran_size;
+ int index_good;
+ int i;
+
+ if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
+ return 0;
+ rdmsr(MTRRdefType_MSR, def, dummy);
+ def &= 0xff;
+ if (def != MTRR_TYPE_UNCACHABLE)
+ return 0;
+
+ /* get it and store it aside */
+ memset(range_state, 0, sizeof(range_state));
+ for (i = 0; i < num_var_ranges; i++) {
+ mtrr_if->get(i, &base, &size, &type);
+ range_state[i].base_pfn = base;
+ range_state[i].size_pfn = size;
+ range_state[i].type = type;
+ }
+
+ /* check if we need handle it and can handle it */
+ if (!mtrr_need_cleanup())
+ return 0;
+
+ /* print original var MTRRs at first, for debugging: */
+ printk(KERN_DEBUG "original variable MTRRs\n");
+ print_out_mtrr_range_state();
memset(range, 0, sizeof(range));
extra_remove_size = 0;
@@ -1309,176 +1418,64 @@ static int __init mtrr_cleanup(unsigned address_bits)
range_sums >> (20 - PAGE_SHIFT));
if (mtrr_chunk_size && mtrr_gran_size) {
- int num_reg;
- char gran_factor, chunk_factor, lose_factor;
- unsigned long gran_base, chunk_base, lose_base;
-
- debug_print++;
- /* convert ranges to var ranges state */
- num_reg = x86_setup_var_mtrrs(range, nr_range, mtrr_chunk_size,
- mtrr_gran_size);
+ i = 0;
+ mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size,
+ extra_remove_base, extra_remove_size, i);
- /* we got new setting in range_state, check it */
- memset(range_new, 0, sizeof(range_new));
- nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
- extra_remove_base,
- extra_remove_size);
- range_sums_new = sum_ranges(range_new, nr_range_new);
+ mtrr_print_out_one_result(i);
- i = 0;
- result[i].chunk_sizek = mtrr_chunk_size >> 10;
- result[i].gran_sizek = mtrr_gran_size >> 10;
- result[i].num_reg = num_reg;
- if (range_sums < range_sums_new) {
- result[i].lose_cover_sizek =
- (range_sums_new - range_sums) << PSHIFT;
- result[i].bad = 1;
- } else
- result[i].lose_cover_sizek =
- (range_sums - range_sums_new) << PSHIFT;
-
- gran_base = to_size_factor(result[i].gran_sizek, &gran_factor),
- chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor),
- lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor),
- printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t",
- result[i].bad?"*BAD*":" ",
- gran_base, gran_factor, chunk_base, chunk_factor);
- printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n",
- result[i].num_reg, result[i].bad?"-":"",
- lose_base, lose_factor);
if (!result[i].bad) {
set_var_mtrr_all(address_bits);
return 1;
}
printk(KERN_INFO "invalid mtrr_gran_size or mtrr_chunk_size, "
"will find optimal one\n");
- debug_print--;
- memset(result, 0, sizeof(result[0]));
}
i = 0;
memset(min_loss_pfn, 0xff, sizeof(min_loss_pfn));
memset(result, 0, sizeof(result));
for (gran_size = (1ULL<<16); gran_size < (1ULL<<32); gran_size <<= 1) {
- char gran_factor;
- unsigned long gran_base;
-
- if (debug_print)
- gran_base = to_size_factor(gran_size >> 10, &gran_factor);
for (chunk_size = gran_size; chunk_size < (1ULL<<32);
chunk_size <<= 1) {
- int num_reg;
- if (debug_print) {
- char chunk_factor;
- unsigned long chunk_base;
-
- chunk_base = to_size_factor(chunk_size>>10, &chunk_factor),
- printk(KERN_INFO "\n");
- printk(KERN_INFO "gran_size: %ld%c chunk_size: %ld%c \n",
- gran_base, gran_factor, chunk_base, chunk_factor);
- }
if (i >= NUM_RESULT)
continue;
- /* convert ranges to var ranges state */
- num_reg = x86_setup_var_mtrrs(range, nr_range,
- chunk_size, gran_size);
-
- /* we got new setting in range_state, check it */
- memset(range_new, 0, sizeof(range_new));
- nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
- extra_remove_base, extra_remove_size);
- range_sums_new = sum_ranges(range_new, nr_range_new);
-
- result[i].chunk_sizek = chunk_size >> 10;
- result[i].gran_sizek = gran_size >> 10;
- result[i].num_reg = num_reg;
- if (range_sums < range_sums_new) {
- result[i].lose_cover_sizek =
- (range_sums_new - range_sums) << PSHIFT;
- result[i].bad = 1;
- } else
- result[i].lose_cover_sizek =
- (range_sums - range_sums_new) << PSHIFT;
-
- /* double check it */
- if (!result[i].bad && !result[i].lose_cover_sizek) {
- if (nr_range_new != nr_range ||
- memcmp(range, range_new, sizeof(range)))
- result[i].bad = 1;
+ mtrr_calc_range_state(chunk_size, gran_size,
+ extra_remove_base, extra_remove_size, i);
+ if (debug_print) {
+ mtrr_print_out_one_result(i);
+ printk(KERN_INFO "\n");
}
- if (!result[i].bad && (range_sums - range_sums_new <
- min_loss_pfn[num_reg])) {
- min_loss_pfn[num_reg] =
- range_sums - range_sums_new;
- }
i++;
}
}
- /* print out all */
- for (i = 0; i < NUM_RESULT; i++) {
- char gran_factor, chunk_factor, lose_factor;
- unsigned long gran_base, chunk_base, lose_base;
-
- gran_base = to_size_factor(result[i].gran_sizek, &gran_factor),
- chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor),
- lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor),
- printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t",
- result[i].bad?"*BAD*":" ",
- gran_base, gran_factor, chunk_base, chunk_factor);
- printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n",
- result[i].num_reg, result[i].bad?"-":"",
- lose_base, lose_factor);
- }
-
/* try to find the optimal index */
- if (nr_mtrr_spare_reg >= num_var_ranges)
- nr_mtrr_spare_reg = num_var_ranges - 1;
- num_reg_good = -1;
- for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) {
- if (!min_loss_pfn[i])
- num_reg_good = i;
- }
-
- index_good = -1;
- if (num_reg_good != -1) {
- for (i = 0; i < NUM_RESULT; i++) {
- if (!result[i].bad &&
- result[i].num_reg == num_reg_good &&
- !result[i].lose_cover_sizek) {
- index_good = i;
- break;
- }
- }
- }
+ index_good = mtrr_search_optimal_index();
if (index_good != -1) {
- char gran_factor, chunk_factor, lose_factor;
- unsigned long gran_base, chunk_base, lose_base;
-
printk(KERN_INFO "Found optimal setting for mtrr clean up\n");
i = index_good;
- gran_base = to_size_factor(result[i].gran_sizek, &gran_factor),
- chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor),
- lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor),
- printk(KERN_INFO "gran_size: %ld%c \tchunk_size: %ld%c \t",
- gran_base, gran_factor, chunk_base, chunk_factor);
- printk(KERN_CONT "num_reg: %d \tlose RAM: %ld%c\n",
- result[i].num_reg, lose_base, lose_factor);
+ mtrr_print_out_one_result(i);
+
/* convert ranges to var ranges state */
chunk_size = result[i].chunk_sizek;
chunk_size <<= 10;
gran_size = result[i].gran_sizek;
gran_size <<= 10;
- debug_print++;
x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
- debug_print--;
set_var_mtrr_all(address_bits);
+ printk(KERN_DEBUG "New variable MTRRs\n");
+ print_out_mtrr_range_state();
return 1;
+ } else {
+ /* print out all */
+ for (i = 0; i < NUM_RESULT; i++)
+ mtrr_print_out_one_result(i);
}
printk(KERN_INFO "mtrr_cleanup: can not find optimal value\n");
@@ -1562,7 +1559,6 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
{
unsigned long i, base, size, highest_pfn = 0, def, dummy;
mtrr_type type;
- int nr_range;
u64 total_trim_size;
/* extra one for all 0 */
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
new file mode 100644
index 000000000000..284c399e3234
--- /dev/null
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -0,0 +1,112 @@
+/*
+ * VMware Detection code.
+ *
+ * Copyright (C) 2008, VMware, Inc.
+ * Author : Alok N Kataria <akataria@vmware.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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/dmi.h>
+#include <asm/div64.h>
+#include <asm/vmware.h>
+
+#define CPUID_VMWARE_INFO_LEAF 0x40000000
+#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
+#define VMWARE_HYPERVISOR_PORT 0x5658
+
+#define VMWARE_PORT_CMD_GETVERSION 10
+#define VMWARE_PORT_CMD_GETHZ 45
+
+#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
+ __asm__("inl (%%dx)" : \
+ "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) : \
+ "0"(VMWARE_HYPERVISOR_MAGIC), \
+ "1"(VMWARE_PORT_CMD_##cmd), \
+ "2"(VMWARE_HYPERVISOR_PORT), "3"(UINT_MAX) : \
+ "memory");
+
+static inline int __vmware_platform(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ VMWARE_PORT(GETVERSION, eax, ebx, ecx, edx);
+ return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC;
+}
+
+static unsigned long __vmware_get_tsc_khz(void)
+{
+ uint64_t tsc_hz;
+ uint32_t eax, ebx, ecx, edx;
+
+ VMWARE_PORT(GETHZ, eax, ebx, ecx, edx);
+
+ if (ebx == UINT_MAX)
+ return 0;
+ tsc_hz = eax | (((uint64_t)ebx) << 32);
+ do_div(tsc_hz, 1000);
+ BUG_ON(tsc_hz >> 32);
+ return tsc_hz;
+}
+
+/*
+ * While checking the dmi string infomation, just checking the product
+ * serial key should be enough, as this will always have a VMware
+ * specific string when running under VMware hypervisor.
+ */
+int vmware_platform(void)
+{
+ if (cpu_has_hypervisor) {
+ unsigned int eax, ebx, ecx, edx;
+ char hyper_vendor_id[13];
+
+ cpuid(CPUID_VMWARE_INFO_LEAF, &eax, &ebx, &ecx, &edx);
+ memcpy(hyper_vendor_id + 0, &ebx, 4);
+ memcpy(hyper_vendor_id + 4, &ecx, 4);
+ memcpy(hyper_vendor_id + 8, &edx, 4);
+ hyper_vendor_id[12] = '\0';
+ if (!strcmp(hyper_vendor_id, "VMwareVMware"))
+ return 1;
+ } else if (dmi_available && dmi_name_in_serial("VMware") &&
+ __vmware_platform())
+ return 1;
+
+ return 0;
+}
+
+unsigned long vmware_get_tsc_khz(void)
+{
+ BUG_ON(!vmware_platform());
+ return __vmware_get_tsc_khz();
+}
+
+/*
+ * VMware hypervisor takes care of exporting a reliable TSC to the guest.
+ * Still, due to timing difference when running on virtual cpus, the TSC can
+ * be marked as unstable in some cases. For example, the TSC sync check at
+ * bootup can fail due to a marginal offset between vcpus' TSCs (though the
+ * TSCs do not drift from each other). Also, the ACPI PM timer clocksource
+ * is not suitable as a watchdog when running on a hypervisor because the
+ * kernel may miss a wrap of the counter if the vcpu is descheduled for a
+ * long time. To skip these checks at runtime we set these capability bits,
+ * so that the kernel could just trust the hypervisor with providing a
+ * reliable virtual TSC that is suitable for timekeeping.
+ */
+void __cpuinit vmware_set_feature_bits(struct cpuinfo_x86 *c)
+{
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+ set_cpu_cap(c, X86_FEATURE_TSC_RELIABLE);
+}
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c
index 19a8c2c0389f..da91701a2348 100644
--- a/arch/x86/kernel/ds.c
+++ b/arch/x86/kernel/ds.c
@@ -6,13 +6,13 @@
* precise-event based sampling (PEBS).
*
* It manages:
- * - per-thread and per-cpu allocation of BTS and PEBS
+ * - DS and BTS hardware configuration
* - buffer overflow handling (to be done)
* - buffer access
*
- * It assumes:
- * - get_task_struct on all traced tasks
- * - current is allowed to trace tasks
+ * It does not do:
+ * - security checking (is the caller allowed to trace the task)
+ * - buffer allocation (memory accounting)
*
*
* Copyright (C) 2007-2008 Intel Corporation.
@@ -34,15 +34,30 @@
* The configuration for a particular DS hardware implementation.
*/
struct ds_configuration {
- /* the size of the DS structure in bytes */
- unsigned char sizeof_ds;
- /* the size of one pointer-typed field in the DS structure in bytes;
- this covers the first 8 fields related to buffer management. */
+ /* the name of the configuration */
+ const char *name;
+ /* the size of one pointer-typed field in the DS structure and
+ in the BTS and PEBS buffers in bytes;
+ this covers the first 8 DS fields related to buffer management. */
unsigned char sizeof_field;
/* the size of a BTS/PEBS record in bytes */
unsigned char sizeof_rec[2];
+ /* a series of bit-masks to control various features indexed
+ * by enum ds_feature */
+ unsigned long ctl[dsf_ctl_max];
};
-static struct ds_configuration ds_cfg;
+static DEFINE_PER_CPU(struct ds_configuration, ds_cfg_array);
+
+#define ds_cfg per_cpu(ds_cfg_array, smp_processor_id())
+
+#define MAX_SIZEOF_DS (12 * 8) /* maximal size of a DS configuration */
+#define MAX_SIZEOF_BTS (3 * 8) /* maximal size of a BTS record */
+#define DS_ALIGNMENT (1 << 3) /* BTS and PEBS buffer alignment */
+
+#define BTS_CONTROL \
+ (ds_cfg.ctl[dsf_bts] | ds_cfg.ctl[dsf_bts_kernel] | ds_cfg.ctl[dsf_bts_user] |\
+ ds_cfg.ctl[dsf_bts_overflow])
+
/*
* A BTS or PEBS tracer.
@@ -61,6 +76,8 @@ struct ds_tracer {
struct bts_tracer {
/* the common DS part */
struct ds_tracer ds;
+ /* the trace including the DS configuration */
+ struct bts_trace trace;
/* buffer overflow notification function */
bts_ovfl_callback_t ovfl;
};
@@ -68,6 +85,8 @@ struct bts_tracer {
struct pebs_tracer {
/* the common DS part */
struct ds_tracer ds;
+ /* the trace including the DS configuration */
+ struct pebs_trace trace;
/* buffer overflow notification function */
pebs_ovfl_callback_t ovfl;
};
@@ -134,13 +153,11 @@ static inline void ds_set(unsigned char *base, enum ds_qualifier qual,
(*(unsigned long *)base) = value;
}
-#define DS_ALIGNMENT (1 << 3) /* BTS and PEBS buffer alignment */
-
/*
* Locking is done only for allocating BTS or PEBS resources.
*/
-static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock);
+static DEFINE_SPINLOCK(ds_lock);
/*
@@ -156,27 +173,32 @@ static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock);
* >0 number of per-thread tracers
* <0 number of per-cpu tracers
*
- * The below functions to get and put tracers and to check the
- * allocation type require the ds_lock to be held by the caller.
- *
* Tracers essentially gives the number of ds contexts for a certain
* type of allocation.
*/
-static long tracers;
+static atomic_t tracers = ATOMIC_INIT(0);
static inline void get_tracer(struct task_struct *task)
{
- tracers += (task ? 1 : -1);
+ if (task)
+ atomic_inc(&tracers);
+ else
+ atomic_dec(&tracers);
}
static inline void put_tracer(struct task_struct *task)
{
- tracers -= (task ? 1 : -1);
+ if (task)
+ atomic_dec(&tracers);
+ else
+ atomic_inc(&tracers);
}
static inline int check_tracer(struct task_struct *task)
{
- return (task ? (tracers >= 0) : (tracers <= 0));
+ return task ?
+ (atomic_read(&tracers) >= 0) :
+ (atomic_read(&tracers) <= 0);
}
@@ -190,46 +212,66 @@ static inline int check_tracer(struct task_struct *task)
* Contexts are use-counted. They are allocated on first access and
* deallocated when the last user puts the context.
*/
-static DEFINE_PER_CPU(struct ds_context *, system_context);
+struct ds_context {
+ /* pointer to the DS configuration; goes into MSR_IA32_DS_AREA */
+ unsigned char ds[MAX_SIZEOF_DS];
+ /* the owner of the BTS and PEBS configuration, respectively */
+ struct bts_tracer *bts_master;
+ struct pebs_tracer *pebs_master;
+ /* use count */
+ unsigned long count;
+ /* a pointer to the context location inside the thread_struct
+ * or the per_cpu context array */
+ struct ds_context **this;
+ /* a pointer to the task owning this context, or NULL, if the
+ * context is owned by a cpu */
+ struct task_struct *task;
+};
+
+static DEFINE_PER_CPU(struct ds_context *, system_context_array);
+
+#define system_context per_cpu(system_context_array, smp_processor_id())
-#define this_system_context per_cpu(system_context, smp_processor_id())
static inline struct ds_context *ds_get_context(struct task_struct *task)
{
struct ds_context **p_context =
- (task ? &task->thread.ds_ctx : &this_system_context);
- struct ds_context *context = *p_context;
+ (task ? &task->thread.ds_ctx : &system_context);
+ struct ds_context *context = NULL;
+ struct ds_context *new_context = NULL;
unsigned long irq;
- if (!context) {
- context = kzalloc(sizeof(*context), GFP_KERNEL);
- if (!context)
- return NULL;
+ /* Chances are small that we already have a context. */
+ new_context = kzalloc(sizeof(*new_context), GFP_KERNEL);
+ if (!new_context)
+ return NULL;
- spin_lock_irqsave(&ds_lock, irq);
+ spin_lock_irqsave(&ds_lock, irq);
- if (*p_context) {
- kfree(context);
+ context = *p_context;
+ if (!context) {
+ context = new_context;
- context = *p_context;
- } else {
- *p_context = context;
+ context->this = p_context;
+ context->task = task;
+ context->count = 0;
- context->this = p_context;
- context->task = task;
+ if (task)
+ set_tsk_thread_flag(task, TIF_DS_AREA_MSR);
- if (task)
- set_tsk_thread_flag(task, TIF_DS_AREA_MSR);
+ if (!task || (task == current))
+ wrmsrl(MSR_IA32_DS_AREA, (unsigned long)context->ds);
- if (!task || (task == current))
- wrmsrl(MSR_IA32_DS_AREA,
- (unsigned long)context->ds);
- }
- spin_unlock_irqrestore(&ds_lock, irq);
+ *p_context = context;
}
context->count++;
+ spin_unlock_irqrestore(&ds_lock, irq);
+
+ if (context != new_context)
+ kfree(new_context);
+
return context;
}
@@ -242,8 +284,10 @@ static inline void ds_put_context(struct ds_context *context)
spin_lock_irqsave(&ds_lock, irq);
- if (--context->count)
- goto out;
+ if (--context->count) {
+ spin_unlock_irqrestore(&ds_lock, irq);
+ return;
+ }
*(context->this) = NULL;
@@ -253,14 +297,14 @@ static inline void ds_put_context(struct ds_context *context)
if (!context->task || (context->task == current))
wrmsrl(MSR_IA32_DS_AREA, 0);
- kfree(context);
- out:
spin_unlock_irqrestore(&ds_lock, irq);
+
+ kfree(context);
}
/*
- * Handle a buffer overflow
+ * Call the tracer's callback on a buffer overflow.
*
* context: the ds context
* qual: the buffer type
@@ -268,30 +312,247 @@ static inline void ds_put_context(struct ds_context *context)
static void ds_overflow(struct ds_context *context, enum ds_qualifier qual)
{
switch (qual) {
- case ds_bts: {
- struct bts_tracer *tracer =
- container_of(context->owner[qual],
- struct bts_tracer, ds);
- if (tracer->ovfl)
- tracer->ovfl(tracer);
- }
+ case ds_bts:
+ if (context->bts_master &&
+ context->bts_master->ovfl)
+ context->bts_master->ovfl(context->bts_master);
break;
- case ds_pebs: {
- struct pebs_tracer *tracer =
- container_of(context->owner[qual],
- struct pebs_tracer, ds);
- if (tracer->ovfl)
- tracer->ovfl(tracer);
+ case ds_pebs:
+ if (context->pebs_master &&
+ context->pebs_master->ovfl)
+ context->pebs_master->ovfl(context->pebs_master);
+ break;
+ }
+}
+
+
+/*
+ * Write raw data into the BTS or PEBS buffer.
+ *
+ * The remainder of any partially written record is zeroed out.
+ *
+ * context: the DS context
+ * qual: the buffer type
+ * record: the data to write
+ * size: the size of the data
+ */
+static int ds_write(struct ds_context *context, enum ds_qualifier qual,
+ const void *record, size_t size)
+{
+ int bytes_written = 0;
+
+ if (!record)
+ return -EINVAL;
+
+ while (size) {
+ unsigned long base, index, end, write_end, int_th;
+ unsigned long write_size, adj_write_size;
+
+ /*
+ * write as much as possible without producing an
+ * overflow interrupt.
+ *
+ * interrupt_threshold must either be
+ * - bigger than absolute_maximum or
+ * - point to a record between buffer_base and absolute_maximum
+ *
+ * index points to a valid record.
+ */
+ base = ds_get(context->ds, qual, ds_buffer_base);
+ index = ds_get(context->ds, qual, ds_index);
+ end = ds_get(context->ds, qual, ds_absolute_maximum);
+ int_th = ds_get(context->ds, qual, ds_interrupt_threshold);
+
+ write_end = min(end, int_th);
+
+ /* if we are already beyond the interrupt threshold,
+ * we fill the entire buffer */
+ if (write_end <= index)
+ write_end = end;
+
+ if (write_end <= index)
+ break;
+
+ write_size = min((unsigned long) size, write_end - index);
+ memcpy((void *)index, record, write_size);
+
+ record = (const char *)record + write_size;
+ size -= write_size;
+ bytes_written += write_size;
+
+ adj_write_size = write_size / ds_cfg.sizeof_rec[qual];
+ adj_write_size *= ds_cfg.sizeof_rec[qual];
+
+ /* zero out trailing bytes */
+ memset((char *)index + write_size, 0,
+ adj_write_size - write_size);
+ index += adj_write_size;
+
+ if (index >= end)
+ index = base;
+ ds_set(context->ds, qual, ds_index, index);
+
+ if (index >= int_th)
+ ds_overflow(context, qual);
+ }
+
+ return bytes_written;
+}
+
+
+/*
+ * Branch Trace Store (BTS) uses the following format. Different
+ * architectures vary in the size of those fields.
+ * - source linear address
+ * - destination linear address
+ * - flags
+ *
+ * Later architectures use 64bit pointers throughout, whereas earlier
+ * architectures use 32bit pointers in 32bit mode.
+ *
+ * We compute the base address for the first 8 fields based on:
+ * - the field size stored in the DS configuration
+ * - the relative field position
+ *
+ * In order to store additional information in the BTS buffer, we use
+ * a special source address to indicate that the record requires
+ * special interpretation.
+ *
+ * Netburst indicated via a bit in the flags field whether the branch
+ * was predicted; this is ignored.
+ *
+ * We use two levels of abstraction:
+ * - the raw data level defined here
+ * - an arch-independent level defined in ds.h
+ */
+
+enum bts_field {
+ bts_from,
+ bts_to,
+ bts_flags,
+
+ bts_qual = bts_from,
+ bts_jiffies = bts_to,
+ bts_pid = bts_flags,
+
+ bts_qual_mask = (bts_qual_max - 1),
+ bts_escape = ((unsigned long)-1 & ~bts_qual_mask)
+};
+
+static inline unsigned long bts_get(const char *base, enum bts_field field)
+{
+ base += (ds_cfg.sizeof_field * field);
+ return *(unsigned long *)base;
+}
+
+static inline void bts_set(char *base, enum bts_field field, unsigned long val)
+{
+ base += (ds_cfg.sizeof_field * field);;
+ (*(unsigned long *)base) = val;
+}
+
+
+/*
+ * The raw BTS data is architecture dependent.
+ *
+ * For higher-level users, we give an arch-independent view.
+ * - ds.h defines struct bts_struct
+ * - bts_read translates one raw bts record into a bts_struct
+ * - bts_write translates one bts_struct into the raw format and
+ * writes it into the top of the parameter tracer's buffer.
+ *
+ * return: bytes read/written on success; -Eerrno, otherwise
+ */
+static int bts_read(struct bts_tracer *tracer, const void *at,
+ struct bts_struct *out)
+{
+ if (!tracer)
+ return -EINVAL;
+
+ if (at < tracer->trace.ds.begin)
+ return -EINVAL;
+
+ if (tracer->trace.ds.end < (at + tracer->trace.ds.size))
+ return -EINVAL;
+
+ memset(out, 0, sizeof(*out));
+ if ((bts_get(at, bts_qual) & ~bts_qual_mask) == bts_escape) {
+ out->qualifier = (bts_get(at, bts_qual) & bts_qual_mask);
+ out->variant.timestamp.jiffies = bts_get(at, bts_jiffies);
+ out->variant.timestamp.pid = bts_get(at, bts_pid);
+ } else {
+ out->qualifier = bts_branch;
+ out->variant.lbr.from = bts_get(at, bts_from);
+ out->variant.lbr.to = bts_get(at, bts_to);
+
+ if (!out->variant.lbr.from && !out->variant.lbr.to)
+ out->qualifier = bts_invalid;
}
+
+ return ds_cfg.sizeof_rec[ds_bts];
+}
+
+static int bts_write(struct bts_tracer *tracer, const struct bts_struct *in)
+{
+ unsigned char raw[MAX_SIZEOF_BTS];
+
+ if (!tracer)
+ return -EINVAL;
+
+ if (MAX_SIZEOF_BTS < ds_cfg.sizeof_rec[ds_bts])
+ return -EOVERFLOW;
+
+ switch (in->qualifier) {
+ case bts_invalid:
+ bts_set(raw, bts_from, 0);
+ bts_set(raw, bts_to, 0);
+ bts_set(raw, bts_flags, 0);
+ break;
+ case bts_branch:
+ bts_set(raw, bts_from, in->variant.lbr.from);
+ bts_set(raw, bts_to, in->variant.lbr.to);
+ bts_set(raw, bts_flags, 0);
break;
+ case bts_task_arrives:
+ case bts_task_departs:
+ bts_set(raw, bts_qual, (bts_escape | in->qualifier));
+ bts_set(raw, bts_jiffies, in->variant.timestamp.jiffies);
+ bts_set(raw, bts_pid, in->variant.timestamp.pid);
+ break;
+ default:
+ return -EINVAL;
}
+
+ return ds_write(tracer->ds.context, ds_bts, raw,
+ ds_cfg.sizeof_rec[ds_bts]);
}
-static void ds_install_ds_config(struct ds_context *context,
- enum ds_qualifier qual,
- void *base, size_t size, size_t ith)
+static void ds_write_config(struct ds_context *context,
+ struct ds_trace *cfg, enum ds_qualifier qual)
{
+ unsigned char *ds = context->ds;
+
+ ds_set(ds, qual, ds_buffer_base, (unsigned long)cfg->begin);
+ ds_set(ds, qual, ds_index, (unsigned long)cfg->top);
+ ds_set(ds, qual, ds_absolute_maximum, (unsigned long)cfg->end);
+ ds_set(ds, qual, ds_interrupt_threshold, (unsigned long)cfg->ith);
+}
+
+static void ds_read_config(struct ds_context *context,
+ struct ds_trace *cfg, enum ds_qualifier qual)
+{
+ unsigned char *ds = context->ds;
+
+ cfg->begin = (void *)ds_get(ds, qual, ds_buffer_base);
+ cfg->top = (void *)ds_get(ds, qual, ds_index);
+ cfg->end = (void *)ds_get(ds, qual, ds_absolute_maximum);
+ cfg->ith = (void *)ds_get(ds, qual, ds_interrupt_threshold);
+}
+
+static void ds_init_ds_trace(struct ds_trace *trace, enum ds_qualifier qual,
+ void *base, size_t size, size_t ith,
+ unsigned int flags) {
unsigned long buffer, adj;
/* adjust the buffer address and size to meet alignment
@@ -308,32 +569,30 @@ static void ds_install_ds_config(struct ds_context *context,
buffer += adj;
size -= adj;
- size /= ds_cfg.sizeof_rec[qual];
- size *= ds_cfg.sizeof_rec[qual];
+ trace->n = size / ds_cfg.sizeof_rec[qual];
+ trace->size = ds_cfg.sizeof_rec[qual];
- ds_set(context->ds, qual, ds_buffer_base, buffer);
- ds_set(context->ds, qual, ds_index, buffer);
- ds_set(context->ds, qual, ds_absolute_maximum, buffer + size);
+ size = (trace->n * trace->size);
+ trace->begin = (void *)buffer;
+ trace->top = trace->begin;
+ trace->end = (void *)(buffer + size);
/* The value for 'no threshold' is -1, which will set the
* threshold outside of the buffer, just like we want it.
*/
- ds_set(context->ds, qual,
- ds_interrupt_threshold, buffer + size - ith);
+ trace->ith = (void *)(buffer + size - ith);
+
+ trace->flags = flags;
}
-static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual,
- struct task_struct *task,
- void *base, size_t size, size_t th)
+
+static int ds_request(struct ds_tracer *tracer, struct ds_trace *trace,
+ enum ds_qualifier qual, struct task_struct *task,
+ void *base, size_t size, size_t th, unsigned int flags)
{
struct ds_context *context;
- unsigned long irq;
int error;
- error = -EOPNOTSUPP;
- if (!ds_cfg.sizeof_ds)
- goto out;
-
error = -EINVAL;
if (!base)
goto out;
@@ -360,43 +619,26 @@ static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual,
goto out;
tracer->context = context;
+ ds_init_ds_trace(trace, qual, base, size, th, flags);
- spin_lock_irqsave(&ds_lock, irq);
-
- error = -EPERM;
- if (!check_tracer(task))
- goto out_unlock;
- get_tracer(task);
-
- error = -EPERM;
- if (context->owner[qual])
- goto out_put_tracer;
- context->owner[qual] = tracer;
-
- spin_unlock_irqrestore(&ds_lock, irq);
-
-
- ds_install_ds_config(context, qual, base, size, th);
-
- return 0;
-
- out_put_tracer:
- put_tracer(task);
- out_unlock:
- spin_unlock_irqrestore(&ds_lock, irq);
- ds_put_context(context);
- tracer->context = NULL;
+ error = 0;
out:
return error;
}
struct bts_tracer *ds_request_bts(struct task_struct *task,
void *base, size_t size,
- bts_ovfl_callback_t ovfl, size_t th)
+ bts_ovfl_callback_t ovfl, size_t th,
+ unsigned int flags)
{
struct bts_tracer *tracer;
+ unsigned long irq;
int error;
+ error = -EOPNOTSUPP;
+ if (!ds_cfg.ctl[dsf_bts])
+ goto out;
+
/* buffer overflow notification is not yet implemented */
error = -EOPNOTSUPP;
if (ovfl)
@@ -408,12 +650,40 @@ struct bts_tracer *ds_request_bts(struct task_struct *task,
goto out;
tracer->ovfl = ovfl;
- error = ds_request(&tracer->ds, ds_bts, task, base, size, th);
+ error = ds_request(&tracer->ds, &tracer->trace.ds,
+ ds_bts, task, base, size, th, flags);
if (error < 0)
goto out_tracer;
+
+ spin_lock_irqsave(&ds_lock, irq);
+
+ error = -EPERM;
+ if (!check_tracer(task))
+ goto out_unlock;
+ get_tracer(task);
+
+ error = -EPERM;
+ if (tracer->ds.context->bts_master)
+ goto out_put_tracer;
+ tracer->ds.context->bts_master = tracer;
+
+ spin_unlock_irqrestore(&ds_lock, irq);
+
+
+ tracer->trace.read = bts_read;
+ tracer->trace.write = bts_write;
+
+ ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts);
+ ds_resume_bts(tracer);
+
return tracer;
+ out_put_tracer:
+ put_tracer(task);
+ out_unlock:
+ spin_unlock_irqrestore(&ds_lock, irq);
+ ds_put_context(tracer->ds.context);
out_tracer:
kfree(tracer);
out:
@@ -422,9 +692,11 @@ struct bts_tracer *ds_request_bts(struct task_struct *task,
struct pebs_tracer *ds_request_pebs(struct task_struct *task,
void *base, size_t size,
- pebs_ovfl_callback_t ovfl, size_t th)
+ pebs_ovfl_callback_t ovfl, size_t th,
+ unsigned int flags)
{
struct pebs_tracer *tracer;
+ unsigned long irq;
int error;
/* buffer overflow notification is not yet implemented */
@@ -438,300 +710,171 @@ struct pebs_tracer *ds_request_pebs(struct task_struct *task,
goto out;
tracer->ovfl = ovfl;
- error = ds_request(&tracer->ds, ds_pebs, task, base, size, th);
+ error = ds_request(&tracer->ds, &tracer->trace.ds,
+ ds_pebs, task, base, size, th, flags);
if (error < 0)
goto out_tracer;
+ spin_lock_irqsave(&ds_lock, irq);
+
+ error = -EPERM;
+ if (!check_tracer(task))
+ goto out_unlock;
+ get_tracer(task);
+
+ error = -EPERM;
+ if (tracer->ds.context->pebs_master)
+ goto out_put_tracer;
+ tracer->ds.context->pebs_master = tracer;
+
+ spin_unlock_irqrestore(&ds_lock, irq);
+
+ ds_write_config(tracer->ds.context, &tracer->trace.ds, ds_bts);
+ ds_resume_pebs(tracer);
+
return tracer;
+ out_put_tracer:
+ put_tracer(task);
+ out_unlock:
+ spin_unlock_irqrestore(&ds_lock, irq);
+ ds_put_context(tracer->ds.context);
out_tracer:
kfree(tracer);
out:
return ERR_PTR(error);
}
-static void ds_release(struct ds_tracer *tracer, enum ds_qualifier qual)
-{
- BUG_ON(tracer->context->owner[qual] != tracer);
- tracer->context->owner[qual] = NULL;
-
- put_tracer(tracer->context->task);
- ds_put_context(tracer->context);
-}
-
-int ds_release_bts(struct bts_tracer *tracer)
+void ds_release_bts(struct bts_tracer *tracer)
{
if (!tracer)
- return -EINVAL;
+ return;
- ds_release(&tracer->ds, ds_bts);
- kfree(tracer);
+ ds_suspend_bts(tracer);
- return 0;
-}
+ WARN_ON_ONCE(tracer->ds.context->bts_master != tracer);
+ tracer->ds.context->bts_master = NULL;
-int ds_release_pebs(struct pebs_tracer *tracer)
-{
- if (!tracer)
- return -EINVAL;
+ put_tracer(tracer->ds.context->task);
+ ds_put_context(tracer->ds.context);
- ds_release(&tracer->ds, ds_pebs);
kfree(tracer);
-
- return 0;
}
-static size_t ds_get_index(struct ds_context *context, enum ds_qualifier qual)
+void ds_suspend_bts(struct bts_tracer *tracer)
{
- unsigned long base, index;
-
- base = ds_get(context->ds, qual, ds_buffer_base);
- index = ds_get(context->ds, qual, ds_index);
-
- return (index - base) / ds_cfg.sizeof_rec[qual];
-}
+ struct task_struct *task;
-int ds_get_bts_index(struct bts_tracer *tracer, size_t *pos)
-{
if (!tracer)
- return -EINVAL;
-
- if (!pos)
- return -EINVAL;
-
- *pos = ds_get_index(tracer->ds.context, ds_bts);
-
- return 0;
-}
+ return;
-int ds_get_pebs_index(struct pebs_tracer *tracer, size_t *pos)
-{
- if (!tracer)
- return -EINVAL;
+ task = tracer->ds.context->task;
- if (!pos)
- return -EINVAL;
+ if (!task || (task == current))
+ update_debugctlmsr(get_debugctlmsr() & ~BTS_CONTROL);
- *pos = ds_get_index(tracer->ds.context, ds_pebs);
+ if (task) {
+ task->thread.debugctlmsr &= ~BTS_CONTROL;
- return 0;
+ if (!task->thread.debugctlmsr)
+ clear_tsk_thread_flag(task, TIF_DEBUGCTLMSR);
+ }
}
-static size_t ds_get_end(struct ds_context *context, enum ds_qualifier qual)
+void ds_resume_bts(struct bts_tracer *tracer)
{
- unsigned long base, max;
+ struct task_struct *task;
+ unsigned long control;
- base = ds_get(context->ds, qual, ds_buffer_base);
- max = ds_get(context->ds, qual, ds_absolute_maximum);
-
- return (max - base) / ds_cfg.sizeof_rec[qual];
-}
-
-int ds_get_bts_end(struct bts_tracer *tracer, size_t *pos)
-{
if (!tracer)
- return -EINVAL;
-
- if (!pos)
- return -EINVAL;
-
- *pos = ds_get_end(tracer->ds.context, ds_bts);
-
- return 0;
-}
-
-int ds_get_pebs_end(struct pebs_tracer *tracer, size_t *pos)
-{
- if (!tracer)
- return -EINVAL;
-
- if (!pos)
- return -EINVAL;
-
- *pos = ds_get_end(tracer->ds.context, ds_pebs);
-
- return 0;
-}
-
-static int ds_access(struct ds_context *context, enum ds_qualifier qual,
- size_t index, const void **record)
-{
- unsigned long base, idx;
-
- if (!record)
- return -EINVAL;
-
- base = ds_get(context->ds, qual, ds_buffer_base);
- idx = base + (index * ds_cfg.sizeof_rec[qual]);
-
- if (idx > ds_get(context->ds, qual, ds_absolute_maximum))
- return -EINVAL;
+ return;
- *record = (const void *)idx;
+ task = tracer->ds.context->task;
- return ds_cfg.sizeof_rec[qual];
-}
+ control = ds_cfg.ctl[dsf_bts];
+ if (!(tracer->trace.ds.flags & BTS_KERNEL))
+ control |= ds_cfg.ctl[dsf_bts_kernel];
+ if (!(tracer->trace.ds.flags & BTS_USER))
+ control |= ds_cfg.ctl[dsf_bts_user];
-int ds_access_bts(struct bts_tracer *tracer, size_t index,
- const void **record)
-{
- if (!tracer)
- return -EINVAL;
+ if (task) {
+ task->thread.debugctlmsr |= control;
+ set_tsk_thread_flag(task, TIF_DEBUGCTLMSR);
+ }
- return ds_access(tracer->ds.context, ds_bts, index, record);
+ if (!task || (task == current))
+ update_debugctlmsr(get_debugctlmsr() | control);
}
-int ds_access_pebs(struct pebs_tracer *tracer, size_t index,
- const void **record)
+void ds_release_pebs(struct pebs_tracer *tracer)
{
if (!tracer)
- return -EINVAL;
-
- return ds_access(tracer->ds.context, ds_pebs, index, record);
-}
-
-static int ds_write(struct ds_context *context, enum ds_qualifier qual,
- const void *record, size_t size)
-{
- int bytes_written = 0;
-
- if (!record)
- return -EINVAL;
-
- while (size) {
- unsigned long base, index, end, write_end, int_th;
- unsigned long write_size, adj_write_size;
-
- /*
- * write as much as possible without producing an
- * overflow interrupt.
- *
- * interrupt_threshold must either be
- * - bigger than absolute_maximum or
- * - point to a record between buffer_base and absolute_maximum
- *
- * index points to a valid record.
- */
- base = ds_get(context->ds, qual, ds_buffer_base);
- index = ds_get(context->ds, qual, ds_index);
- end = ds_get(context->ds, qual, ds_absolute_maximum);
- int_th = ds_get(context->ds, qual, ds_interrupt_threshold);
-
- write_end = min(end, int_th);
-
- /* if we are already beyond the interrupt threshold,
- * we fill the entire buffer */
- if (write_end <= index)
- write_end = end;
-
- if (write_end <= index)
- break;
-
- write_size = min((unsigned long) size, write_end - index);
- memcpy((void *)index, record, write_size);
-
- record = (const char *)record + write_size;
- size -= write_size;
- bytes_written += write_size;
-
- adj_write_size = write_size / ds_cfg.sizeof_rec[qual];
- adj_write_size *= ds_cfg.sizeof_rec[qual];
-
- /* zero out trailing bytes */
- memset((char *)index + write_size, 0,
- adj_write_size - write_size);
- index += adj_write_size;
-
- if (index >= end)
- index = base;
- ds_set(context->ds, qual, ds_index, index);
+ return;
- if (index >= int_th)
- ds_overflow(context, qual);
- }
+ ds_suspend_pebs(tracer);
- return bytes_written;
-}
+ WARN_ON_ONCE(tracer->ds.context->pebs_master != tracer);
+ tracer->ds.context->pebs_master = NULL;
-int ds_write_bts(struct bts_tracer *tracer, const void *record, size_t size)
-{
- if (!tracer)
- return -EINVAL;
+ put_tracer(tracer->ds.context->task);
+ ds_put_context(tracer->ds.context);
- return ds_write(tracer->ds.context, ds_bts, record, size);
+ kfree(tracer);
}
-int ds_write_pebs(struct pebs_tracer *tracer, const void *record, size_t size)
+void ds_suspend_pebs(struct pebs_tracer *tracer)
{
- if (!tracer)
- return -EINVAL;
- return ds_write(tracer->ds.context, ds_pebs, record, size);
}
-static void ds_reset_or_clear(struct ds_context *context,
- enum ds_qualifier qual, int clear)
+void ds_resume_pebs(struct pebs_tracer *tracer)
{
- unsigned long base, end;
-
- base = ds_get(context->ds, qual, ds_buffer_base);
- end = ds_get(context->ds, qual, ds_absolute_maximum);
- if (clear)
- memset((void *)base, 0, end - base);
-
- ds_set(context->ds, qual, ds_index, base);
}
-int ds_reset_bts(struct bts_tracer *tracer)
+const struct bts_trace *ds_read_bts(struct bts_tracer *tracer)
{
if (!tracer)
- return -EINVAL;
+ return NULL;
- ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 0);
-
- return 0;
+ ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_bts);
+ return &tracer->trace;
}
-int ds_reset_pebs(struct pebs_tracer *tracer)
+const struct pebs_trace *ds_read_pebs(struct pebs_tracer *tracer)
{
if (!tracer)
- return -EINVAL;
+ return NULL;
- ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 0);
+ ds_read_config(tracer->ds.context, &tracer->trace.ds, ds_pebs);
+ tracer->trace.reset_value =
+ *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8));
- return 0;
+ return &tracer->trace;
}
-int ds_clear_bts(struct bts_tracer *tracer)
+int ds_reset_bts(struct bts_tracer *tracer)
{
if (!tracer)
return -EINVAL;
- ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 1);
-
- return 0;
-}
-
-int ds_clear_pebs(struct pebs_tracer *tracer)
-{
- if (!tracer)
- return -EINVAL;
+ tracer->trace.ds.top = tracer->trace.ds.begin;
- ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 1);
+ ds_set(tracer->ds.context->ds, ds_bts, ds_index,
+ (unsigned long)tracer->trace.ds.top);
return 0;
}
-int ds_get_pebs_reset(struct pebs_tracer *tracer, u64 *value)
+int ds_reset_pebs(struct pebs_tracer *tracer)
{
if (!tracer)
return -EINVAL;
- if (!value)
- return -EINVAL;
+ tracer->trace.ds.top = tracer->trace.ds.begin;
- *value = *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8));
+ ds_set(tracer->ds.context->ds, ds_bts, ds_index,
+ (unsigned long)tracer->trace.ds.top);
return 0;
}
@@ -746,35 +889,59 @@ int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value)
return 0;
}
-static const struct ds_configuration ds_cfg_var = {
- .sizeof_ds = sizeof(long) * 12,
- .sizeof_field = sizeof(long),
- .sizeof_rec[ds_bts] = sizeof(long) * 3,
+static const struct ds_configuration ds_cfg_netburst = {
+ .name = "netburst",
+ .ctl[dsf_bts] = (1 << 2) | (1 << 3),
+ .ctl[dsf_bts_kernel] = (1 << 5),
+ .ctl[dsf_bts_user] = (1 << 6),
+
+ .sizeof_field = sizeof(long),
+ .sizeof_rec[ds_bts] = sizeof(long) * 3,
#ifdef __i386__
- .sizeof_rec[ds_pebs] = sizeof(long) * 10
+ .sizeof_rec[ds_pebs] = sizeof(long) * 10,
#else
- .sizeof_rec[ds_pebs] = sizeof(long) * 18
+ .sizeof_rec[ds_pebs] = sizeof(long) * 18,
#endif
};
-static const struct ds_configuration ds_cfg_64 = {
- .sizeof_ds = 8 * 12,
- .sizeof_field = 8,
- .sizeof_rec[ds_bts] = 8 * 3,
+static const struct ds_configuration ds_cfg_pentium_m = {
+ .name = "pentium m",
+ .ctl[dsf_bts] = (1 << 6) | (1 << 7),
+
+ .sizeof_field = sizeof(long),
+ .sizeof_rec[ds_bts] = sizeof(long) * 3,
#ifdef __i386__
- .sizeof_rec[ds_pebs] = 8 * 10
+ .sizeof_rec[ds_pebs] = sizeof(long) * 10,
#else
- .sizeof_rec[ds_pebs] = 8 * 18
+ .sizeof_rec[ds_pebs] = sizeof(long) * 18,
#endif
};
+static const struct ds_configuration ds_cfg_core2 = {
+ .name = "core 2",
+ .ctl[dsf_bts] = (1 << 6) | (1 << 7),
+ .ctl[dsf_bts_kernel] = (1 << 9),
+ .ctl[dsf_bts_user] = (1 << 10),
+
+ .sizeof_field = 8,
+ .sizeof_rec[ds_bts] = 8 * 3,
+ .sizeof_rec[ds_pebs] = 8 * 18,
+};
-static inline void
+static void
ds_configure(const struct ds_configuration *cfg)
{
+ memset(&ds_cfg, 0, sizeof(ds_cfg));
ds_cfg = *cfg;
- printk(KERN_INFO "DS available\n");
+ printk(KERN_INFO "[ds] using %s configuration\n", ds_cfg.name);
+
+ if (!cpu_has_bts) {
+ ds_cfg.ctl[dsf_bts] = 0;
+ printk(KERN_INFO "[ds] bts not available\n");
+ }
+ if (!cpu_has_pebs)
+ printk(KERN_INFO "[ds] pebs not available\n");
- BUG_ON(MAX_SIZEOF_DS < ds_cfg.sizeof_ds);
+ WARN_ON_ONCE(MAX_SIZEOF_DS < (12 * ds_cfg.sizeof_field));
}
void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
@@ -787,10 +954,10 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
break;
case 0xD:
case 0xE: /* Pentium M */
- ds_configure(&ds_cfg_var);
+ ds_configure(&ds_cfg_pentium_m);
break;
default: /* Core2, Atom, ... */
- ds_configure(&ds_cfg_64);
+ ds_configure(&ds_cfg_core2);
break;
}
break;
@@ -799,7 +966,7 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
case 0x0:
case 0x1:
case 0x2: /* Netburst */
- ds_configure(&ds_cfg_var);
+ ds_configure(&ds_cfg_netburst);
break;
default:
/* sorry, don't know about them */
@@ -812,14 +979,52 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
}
}
-void ds_free(struct ds_context *context)
+/*
+ * Change the DS configuration from tracing prev to tracing next.
+ */
+void ds_switch_to(struct task_struct *prev, struct task_struct *next)
{
- /* This is called when the task owning the parameter context
- * is dying. There should not be any user of that context left
- * to disturb us, anymore. */
- unsigned long leftovers = context->count;
- while (leftovers--) {
- put_tracer(context->task);
- ds_put_context(context);
+ struct ds_context *prev_ctx = prev->thread.ds_ctx;
+ struct ds_context *next_ctx = next->thread.ds_ctx;
+
+ if (prev_ctx) {
+ update_debugctlmsr(0);
+
+ if (prev_ctx->bts_master &&
+ (prev_ctx->bts_master->trace.ds.flags & BTS_TIMESTAMPS)) {
+ struct bts_struct ts = {
+ .qualifier = bts_task_departs,
+ .variant.timestamp.jiffies = jiffies_64,
+ .variant.timestamp.pid = prev->pid
+ };
+ bts_write(prev_ctx->bts_master, &ts);
+ }
+ }
+
+ if (next_ctx) {
+ if (next_ctx->bts_master &&
+ (next_ctx->bts_master->trace.ds.flags & BTS_TIMESTAMPS)) {
+ struct bts_struct ts = {
+ .qualifier = bts_task_arrives,
+ .variant.timestamp.jiffies = jiffies_64,
+ .variant.timestamp.pid = next->pid
+ };
+ bts_write(next_ctx->bts_master, &ts);
+ }
+
+ wrmsrl(MSR_IA32_DS_AREA, (unsigned long)next_ctx->ds);
}
+
+ update_debugctlmsr(next->thread.debugctlmsr);
+}
+
+void ds_copy_thread(struct task_struct *tsk, struct task_struct *father)
+{
+ clear_tsk_thread_flag(tsk, TIF_DS_AREA_MSR);
+ tsk->thread.ds_ctx = NULL;
+}
+
+void ds_exit_thread(struct task_struct *tsk)
+{
+ WARN_ON(tsk->thread.ds_ctx);
}
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index 7aafeb5263ef..65a13943e098 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -677,22 +677,6 @@ struct early_res {
};
static struct early_res early_res[MAX_EARLY_RES] __initdata = {
{ 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
-#if defined(CONFIG_X86_64) && defined(CONFIG_X86_TRAMPOLINE)
- { TRAMPOLINE_BASE, TRAMPOLINE_BASE + 2 * PAGE_SIZE, "TRAMPOLINE" },
-#endif
-#if defined(CONFIG_X86_32) && defined(CONFIG_SMP)
- /*
- * But first pinch a few for the stack/trampoline stuff
- * FIXME: Don't need the extra page at 4K, but need to fix
- * trampoline before removing it. (see the GDT stuff)
- */
- { PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE" },
- /*
- * Has to be in very low memory so we can execute
- * real-mode AP code.
- */
- { TRAMPOLINE_BASE, TRAMPOLINE_BASE + PAGE_SIZE, "TRAMPOLINE" },
-#endif
{}
};
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 1b894b72c0f5..744aa7fc49d5 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -17,6 +17,7 @@
#include <asm/io_apic.h>
#include <asm/apic.h>
#include <asm/iommu.h>
+#include <asm/gart.h>
static void __init fix_hypertransport_config(int num, int slot, int func)
{
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index 34ad997d3834..23b138e31e9c 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -875,49 +875,6 @@ static struct console early_dbgp_console = {
};
#endif
-/* Console interface to a host file on AMD's SimNow! */
-
-static int simnow_fd;
-
-enum {
- MAGIC1 = 0xBACCD00A,
- MAGIC2 = 0xCA110000,
- XOPEN = 5,
- XWRITE = 4,
-};
-
-static noinline long simnow(long cmd, long a, long b, long c)
-{
- long ret;
-
- asm volatile("cpuid" :
- "=a" (ret) :
- "b" (a), "c" (b), "d" (c), "0" (MAGIC1), "D" (cmd + MAGIC2));
- return ret;
-}
-
-static void __init simnow_init(char *str)
-{
- char *fn = "klog";
-
- if (*str == '=')
- fn = ++str;
- /* error ignored */
- simnow_fd = simnow(XOPEN, (unsigned long)fn, O_WRONLY|O_APPEND|O_CREAT, 0644);
-}
-
-static void simnow_write(struct console *con, const char *s, unsigned n)
-{
- simnow(XWRITE, simnow_fd, (unsigned long)s, n);
-}
-
-static struct console simnow_console = {
- .name = "simnow",
- .write = simnow_write,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
/* Direct interface for emergencies */
static struct console *early_console = &early_vga_console;
static int __initdata early_console_initialized;
@@ -960,10 +917,6 @@ static int __init setup_early_printk(char *buf)
max_ypos = boot_params.screen_info.orig_video_lines;
current_ypos = boot_params.screen_info.orig_y;
early_console = &early_vga_console;
- } else if (!strncmp(buf, "simnow", 6)) {
- simnow_init(buf + 6);
- early_console = &simnow_console;
- keep_early = 1;
#ifdef CONFIG_EARLY_PRINTK_DBGP
} else if (!strncmp(buf, "dbgp", 4)) {
if (early_dbgp_init(buf+4) < 0)
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 43ceb3f454bf..d6f0490a7391 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -619,28 +619,37 @@ END(syscall_badsys)
27:;
/*
- * Build the entry stubs and pointer table with
- * some assembler magic.
+ * Build the entry stubs and pointer table with some assembler magic.
+ * We pack 7 stubs into a single 32-byte chunk, which will fit in a
+ * single cache line on all modern x86 implementations.
*/
-.section .rodata,"a"
+.section .init.rodata,"a"
ENTRY(interrupt)
.text
-
+ .p2align 5
+ .p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
RING0_INT_FRAME
-vector=0
-.rept NR_VECTORS
- ALIGN
- .if vector
+vector=FIRST_EXTERNAL_VECTOR
+.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
+ .balign 32
+ .rept 7
+ .if vector < NR_VECTORS
+ .if vector <> FIRST_EXTERNAL_VECTOR
CFI_ADJUST_CFA_OFFSET -4
- .endif
-1: pushl $~(vector)
+ .endif
+1: pushl $(~vector+0x80) /* Note: always in signed byte range */
CFI_ADJUST_CFA_OFFSET 4
- jmp common_interrupt
- .previous
+ .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
+ jmp 2f
+ .endif
+ .previous
.long 1b
- .text
+ .text
vector=vector+1
+ .endif
+ .endr
+2: jmp common_interrupt
.endr
END(irq_entries_start)
@@ -652,8 +661,9 @@ END(interrupt)
* the CPU automatically disables interrupts when executing an IRQ vector,
* so IRQ-flags tracing has to follow that:
*/
- ALIGN
+ .p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
+ addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */
SAVE_ALL
TRACE_IRQS_OFF
movl %esp,%eax
@@ -678,65 +688,6 @@ ENDPROC(name)
/* The include is where all of the SMP etc. interrupts come from */
#include "entry_arch.h"
-KPROBE_ENTRY(page_fault)
- RING0_EC_FRAME
- pushl $do_page_fault
- CFI_ADJUST_CFA_OFFSET 4
- ALIGN
-error_code:
- /* the function address is in %fs's slot on the stack */
- pushl %es
- CFI_ADJUST_CFA_OFFSET 4
- /*CFI_REL_OFFSET es, 0*/
- pushl %ds
- CFI_ADJUST_CFA_OFFSET 4
- /*CFI_REL_OFFSET ds, 0*/
- pushl %eax
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET eax, 0
- pushl %ebp
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET ebp, 0
- pushl %edi
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET edi, 0
- pushl %esi
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET esi, 0
- pushl %edx
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET edx, 0
- pushl %ecx
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET ecx, 0
- pushl %ebx
- CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET ebx, 0
- cld
- pushl %fs
- CFI_ADJUST_CFA_OFFSET 4
- /*CFI_REL_OFFSET fs, 0*/
- movl $(__KERNEL_PERCPU), %ecx
- movl %ecx, %fs
- UNWIND_ESPFIX_STACK
- popl %ecx
- CFI_ADJUST_CFA_OFFSET -4
- /*CFI_REGISTER es, ecx*/
- movl PT_FS(%esp), %edi # get the function address
- movl PT_ORIG_EAX(%esp), %edx # get the error code
- movl $-1, PT_ORIG_EAX(%esp) # no syscall to restart
- mov %ecx, PT_FS(%esp)
- /*CFI_REL_OFFSET fs, ES*/
- movl $(__USER_DS), %ecx
- movl %ecx, %ds
- movl %ecx, %es
- TRACE_IRQS_OFF
- movl %esp,%eax # pt_regs pointer
- call *%edi
- jmp ret_from_exception
- CFI_ENDPROC
-KPROBE_END(page_fault)
-
ENTRY(coprocessor_error)
RING0_INT_FRAME
pushl $0
@@ -767,140 +718,6 @@ ENTRY(device_not_available)
CFI_ENDPROC
END(device_not_available)
-/*
- * Debug traps and NMI can happen at the one SYSENTER instruction
- * that sets up the real kernel stack. Check here, since we can't
- * allow the wrong stack to be used.
- *
- * "TSS_sysenter_sp0+12" is because the NMI/debug handler will have
- * already pushed 3 words if it hits on the sysenter instruction:
- * eflags, cs and eip.
- *
- * We just load the right stack, and push the three (known) values
- * by hand onto the new stack - while updating the return eip past
- * the instruction that would have done it for sysenter.
- */
-#define FIX_STACK(offset, ok, label) \
- cmpw $__KERNEL_CS,4(%esp); \
- jne ok; \
-label: \
- movl TSS_sysenter_sp0+offset(%esp),%esp; \
- CFI_DEF_CFA esp, 0; \
- CFI_UNDEFINED eip; \
- pushfl; \
- CFI_ADJUST_CFA_OFFSET 4; \
- pushl $__KERNEL_CS; \
- CFI_ADJUST_CFA_OFFSET 4; \
- pushl $sysenter_past_esp; \
- CFI_ADJUST_CFA_OFFSET 4; \
- CFI_REL_OFFSET eip, 0
-
-KPROBE_ENTRY(debug)
- RING0_INT_FRAME
- cmpl $ia32_sysenter_target,(%esp)
- jne debug_stack_correct
- FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn)
-debug_stack_correct:
- pushl $-1 # mark this as an int
- CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- TRACE_IRQS_OFF
- xorl %edx,%edx # error code 0
- movl %esp,%eax # pt_regs pointer
- call do_debug
- jmp ret_from_exception
- CFI_ENDPROC
-KPROBE_END(debug)
-
-/*
- * NMI is doubly nasty. It can happen _while_ we're handling
- * a debug fault, and the debug fault hasn't yet been able to
- * clear up the stack. So we first check whether we got an
- * NMI on the sysenter entry path, but after that we need to
- * check whether we got an NMI on the debug path where the debug
- * fault happened on the sysenter path.
- */
-KPROBE_ENTRY(nmi)
- RING0_INT_FRAME
- pushl %eax
- CFI_ADJUST_CFA_OFFSET 4
- movl %ss, %eax
- cmpw $__ESPFIX_SS, %ax
- popl %eax
- CFI_ADJUST_CFA_OFFSET -4
- je nmi_espfix_stack
- cmpl $ia32_sysenter_target,(%esp)
- je nmi_stack_fixup
- pushl %eax
- CFI_ADJUST_CFA_OFFSET 4
- movl %esp,%eax
- /* Do not access memory above the end of our stack page,
- * it might not exist.
- */
- andl $(THREAD_SIZE-1),%eax
- cmpl $(THREAD_SIZE-20),%eax
- popl %eax
- CFI_ADJUST_CFA_OFFSET -4
- jae nmi_stack_correct
- cmpl $ia32_sysenter_target,12(%esp)
- je nmi_debug_stack_check
-nmi_stack_correct:
- /* We have a RING0_INT_FRAME here */
- pushl %eax
- CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- TRACE_IRQS_OFF
- xorl %edx,%edx # zero error code
- movl %esp,%eax # pt_regs pointer
- call do_nmi
- jmp restore_nocheck_notrace
- CFI_ENDPROC
-
-nmi_stack_fixup:
- RING0_INT_FRAME
- FIX_STACK(12,nmi_stack_correct, 1)
- jmp nmi_stack_correct
-
-nmi_debug_stack_check:
- /* We have a RING0_INT_FRAME here */
- cmpw $__KERNEL_CS,16(%esp)
- jne nmi_stack_correct
- cmpl $debug,(%esp)
- jb nmi_stack_correct
- cmpl $debug_esp_fix_insn,(%esp)
- ja nmi_stack_correct
- FIX_STACK(24,nmi_stack_correct, 1)
- jmp nmi_stack_correct
-
-nmi_espfix_stack:
- /* We have a RING0_INT_FRAME here.
- *
- * create the pointer to lss back
- */
- pushl %ss
- CFI_ADJUST_CFA_OFFSET 4
- pushl %esp
- CFI_ADJUST_CFA_OFFSET 4
- addw $4, (%esp)
- /* copy the iret frame of 12 bytes */
- .rept 3
- pushl 16(%esp)
- CFI_ADJUST_CFA_OFFSET 4
- .endr
- pushl %eax
- CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- TRACE_IRQS_OFF
- FIXUP_ESPFIX_STACK # %eax == %esp
- xorl %edx,%edx # zero error code
- call do_nmi
- RESTORE_REGS
- lss 12+4(%esp), %esp # back to espfix stack
- CFI_ADJUST_CFA_OFFSET -24
- jmp irq_return
- CFI_ENDPROC
-KPROBE_END(nmi)
-
#ifdef CONFIG_PARAVIRT
ENTRY(native_iret)
iret
@@ -916,19 +733,6 @@ ENTRY(native_irq_enable_sysexit)
END(native_irq_enable_sysexit)
#endif
-KPROBE_ENTRY(int3)
- RING0_INT_FRAME
- pushl $-1 # mark this as an int
- CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- TRACE_IRQS_OFF
- xorl %edx,%edx # zero error code
- movl %esp,%eax # pt_regs pointer
- call do_int3
- jmp ret_from_exception
- CFI_ENDPROC
-KPROBE_END(int3)
-
ENTRY(overflow)
RING0_INT_FRAME
pushl $0
@@ -993,14 +797,6 @@ ENTRY(stack_segment)
CFI_ENDPROC
END(stack_segment)
-KPROBE_ENTRY(general_protection)
- RING0_EC_FRAME
- pushl $do_general_protection
- CFI_ADJUST_CFA_OFFSET 4
- jmp error_code
- CFI_ENDPROC
-KPROBE_END(general_protection)
-
ENTRY(alignment_check)
RING0_EC_FRAME
pushl $do_alignment_check
@@ -1051,6 +847,7 @@ ENTRY(kernel_thread_helper)
push %eax
CFI_ADJUST_CFA_OFFSET 4
call do_exit
+ ud2 # padding for call trace
CFI_ENDPROC
ENDPROC(kernel_thread_helper)
@@ -1259,3 +1056,227 @@ return_to_handler:
#include "syscall_table_32.S"
syscall_table_size=(.-sys_call_table)
+
+/*
+ * Some functions should be protected against kprobes
+ */
+ .pushsection .kprobes.text, "ax"
+
+ENTRY(page_fault)
+ RING0_EC_FRAME
+ pushl $do_page_fault
+ CFI_ADJUST_CFA_OFFSET 4
+ ALIGN
+error_code:
+ /* the function address is in %fs's slot on the stack */
+ pushl %es
+ CFI_ADJUST_CFA_OFFSET 4
+ /*CFI_REL_OFFSET es, 0*/
+ pushl %ds
+ CFI_ADJUST_CFA_OFFSET 4
+ /*CFI_REL_OFFSET ds, 0*/
+ pushl %eax
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET eax, 0
+ pushl %ebp
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET ebp, 0
+ pushl %edi
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET edi, 0
+ pushl %esi
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET esi, 0
+ pushl %edx
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET edx, 0
+ pushl %ecx
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET ecx, 0
+ pushl %ebx
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET ebx, 0
+ cld
+ pushl %fs
+ CFI_ADJUST_CFA_OFFSET 4
+ /*CFI_REL_OFFSET fs, 0*/
+ movl $(__KERNEL_PERCPU), %ecx
+ movl %ecx, %fs
+ UNWIND_ESPFIX_STACK
+ popl %ecx
+ CFI_ADJUST_CFA_OFFSET -4
+ /*CFI_REGISTER es, ecx*/
+ movl PT_FS(%esp), %edi # get the function address
+ movl PT_ORIG_EAX(%esp), %edx # get the error code
+ movl $-1, PT_ORIG_EAX(%esp) # no syscall to restart
+ mov %ecx, PT_FS(%esp)
+ /*CFI_REL_OFFSET fs, ES*/
+ movl $(__USER_DS), %ecx
+ movl %ecx, %ds
+ movl %ecx, %es
+ TRACE_IRQS_OFF
+ movl %esp,%eax # pt_regs pointer
+ call *%edi
+ jmp ret_from_exception
+ CFI_ENDPROC
+END(page_fault)
+
+/*
+ * Debug traps and NMI can happen at the one SYSENTER instruction
+ * that sets up the real kernel stack. Check here, since we can't
+ * allow the wrong stack to be used.
+ *
+ * "TSS_sysenter_sp0+12" is because the NMI/debug handler will have
+ * already pushed 3 words if it hits on the sysenter instruction:
+ * eflags, cs and eip.
+ *
+ * We just load the right stack, and push the three (known) values
+ * by hand onto the new stack - while updating the return eip past
+ * the instruction that would have done it for sysenter.
+ */
+#define FIX_STACK(offset, ok, label) \
+ cmpw $__KERNEL_CS,4(%esp); \
+ jne ok; \
+label: \
+ movl TSS_sysenter_sp0+offset(%esp),%esp; \
+ CFI_DEF_CFA esp, 0; \
+ CFI_UNDEFINED eip; \
+ pushfl; \
+ CFI_ADJUST_CFA_OFFSET 4; \
+ pushl $__KERNEL_CS; \
+ CFI_ADJUST_CFA_OFFSET 4; \
+ pushl $sysenter_past_esp; \
+ CFI_ADJUST_CFA_OFFSET 4; \
+ CFI_REL_OFFSET eip, 0
+
+ENTRY(debug)
+ RING0_INT_FRAME
+ cmpl $ia32_sysenter_target,(%esp)
+ jne debug_stack_correct
+ FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn)
+debug_stack_correct:
+ pushl $-1 # mark this as an int
+ CFI_ADJUST_CFA_OFFSET 4
+ SAVE_ALL
+ TRACE_IRQS_OFF
+ xorl %edx,%edx # error code 0
+ movl %esp,%eax # pt_regs pointer
+ call do_debug
+ jmp ret_from_exception
+ CFI_ENDPROC
+END(debug)
+
+/*
+ * NMI is doubly nasty. It can happen _while_ we're handling
+ * a debug fault, and the debug fault hasn't yet been able to
+ * clear up the stack. So we first check whether we got an
+ * NMI on the sysenter entry path, but after that we need to
+ * check whether we got an NMI on the debug path where the debug
+ * fault happened on the sysenter path.
+ */
+ENTRY(nmi)
+ RING0_INT_FRAME
+ pushl %eax
+ CFI_ADJUST_CFA_OFFSET 4
+ movl %ss, %eax
+ cmpw $__ESPFIX_SS, %ax
+ popl %eax
+ CFI_ADJUST_CFA_OFFSET -4
+ je nmi_espfix_stack
+ cmpl $ia32_sysenter_target,(%esp)
+ je nmi_stack_fixup
+ pushl %eax
+ CFI_ADJUST_CFA_OFFSET 4
+ movl %esp,%eax
+ /* Do not access memory above the end of our stack page,
+ * it might not exist.
+ */
+ andl $(THREAD_SIZE-1),%eax
+ cmpl $(THREAD_SIZE-20),%eax
+ popl %eax
+ CFI_ADJUST_CFA_OFFSET -4
+ jae nmi_stack_correct
+ cmpl $ia32_sysenter_target,12(%esp)
+ je nmi_debug_stack_check
+nmi_stack_correct:
+ /* We have a RING0_INT_FRAME here */
+ pushl %eax
+ CFI_ADJUST_CFA_OFFSET 4
+ SAVE_ALL
+ TRACE_IRQS_OFF
+ xorl %edx,%edx # zero error code
+ movl %esp,%eax # pt_regs pointer
+ call do_nmi
+ jmp restore_nocheck_notrace
+ CFI_ENDPROC
+
+nmi_stack_fixup:
+ RING0_INT_FRAME
+ FIX_STACK(12,nmi_stack_correct, 1)
+ jmp nmi_stack_correct
+
+nmi_debug_stack_check:
+ /* We have a RING0_INT_FRAME here */
+ cmpw $__KERNEL_CS,16(%esp)
+ jne nmi_stack_correct
+ cmpl $debug,(%esp)
+ jb nmi_stack_correct
+ cmpl $debug_esp_fix_insn,(%esp)
+ ja nmi_stack_correct
+ FIX_STACK(24,nmi_stack_correct, 1)
+ jmp nmi_stack_correct
+
+nmi_espfix_stack:
+ /* We have a RING0_INT_FRAME here.
+ *
+ * create the pointer to lss back
+ */
+ pushl %ss
+ CFI_ADJUST_CFA_OFFSET 4
+ pushl %esp
+ CFI_ADJUST_CFA_OFFSET 4
+ addw $4, (%esp)
+ /* copy the iret frame of 12 bytes */
+ .rept 3
+ pushl 16(%esp)
+ CFI_ADJUST_CFA_OFFSET 4
+ .endr
+ pushl %eax
+ CFI_ADJUST_CFA_OFFSET 4
+ SAVE_ALL
+ TRACE_IRQS_OFF
+ FIXUP_ESPFIX_STACK # %eax == %esp
+ xorl %edx,%edx # zero error code
+ call do_nmi
+ RESTORE_REGS
+ lss 12+4(%esp), %esp # back to espfix stack
+ CFI_ADJUST_CFA_OFFSET -24
+ jmp irq_return
+ CFI_ENDPROC
+END(nmi)
+
+ENTRY(int3)
+ RING0_INT_FRAME
+ pushl $-1 # mark this as an int
+ CFI_ADJUST_CFA_OFFSET 4
+ SAVE_ALL
+ TRACE_IRQS_OFF
+ xorl %edx,%edx # zero error code
+ movl %esp,%eax # pt_regs pointer
+ call do_int3
+ jmp ret_from_exception
+ CFI_ENDPROC
+END(int3)
+
+ENTRY(general_protection)
+ RING0_EC_FRAME
+ pushl $do_general_protection
+ CFI_ADJUST_CFA_OFFSET 4
+ jmp error_code
+ CFI_ENDPROC
+END(general_protection)
+
+/*
+ * End of kprobes section
+ */
+ .popsection
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index 54e0bbdccb99..e28c7a987793 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -11,15 +11,15 @@
*
* NOTE: This code handles signal-recognition, which happens every time
* after an interrupt and after each system call.
- *
- * Normal syscalls and interrupts don't save a full stack frame, this is
+ *
+ * Normal syscalls and interrupts don't save a full stack frame, this is
* only done for syscall tracing, signals or fork/exec et.al.
- *
- * A note on terminology:
- * - top of stack: Architecture defined interrupt frame from SS to RIP
- * at the top of the kernel process stack.
+ *
+ * A note on terminology:
+ * - top of stack: Architecture defined interrupt frame from SS to RIP
+ * at the top of the kernel process stack.
* - partial stack frame: partially saved registers upto R11.
- * - full stack frame: Like partial stack frame, but all register saved.
+ * - full stack frame: Like partial stack frame, but all register saved.
*
* Some macro usage:
* - CFI macros are used to generate dwarf2 unwind information for better
@@ -60,7 +60,6 @@
#define __AUDIT_ARCH_LE 0x40000000
.code64
-
#ifdef CONFIG_FUNCTION_TRACER
#ifdef CONFIG_DYNAMIC_FTRACE
ENTRY(mcount)
@@ -71,15 +70,7 @@ ENTRY(ftrace_caller)
cmpl $0, function_trace_stop
jne ftrace_stub
- /* 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)
+ MCOUNT_SAVE_FRAME
movq 0x38(%rsp), %rdi
movq 8(%rbp), %rsi
@@ -89,14 +80,7 @@ ENTRY(ftrace_caller)
ftrace_call:
call ftrace_stub
- 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
+ MCOUNT_RESTORE_FRAME
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl ftrace_graph_call
@@ -130,15 +114,7 @@ ftrace_stub:
retq
trace:
- /* 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)
+ MCOUNT_SAVE_FRAME
movq 0x38(%rsp), %rdi
movq 8(%rbp), %rsi
@@ -146,14 +122,7 @@ trace:
call *ftrace_trace_function
- 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
+ MCOUNT_RESTORE_FRAME
jmp ftrace_stub
END(mcount)
@@ -165,14 +134,7 @@ ENTRY(ftrace_graph_caller)
cmpl $0, function_trace_stop
jne ftrace_stub
- 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)
+ MCOUNT_SAVE_FRAME
leaq 8(%rbp), %rdi
movq 0x38(%rsp), %rsi
@@ -180,14 +142,8 @@ ENTRY(ftrace_graph_caller)
call prepare_ftrace_return
- 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
+ MCOUNT_RESTORE_FRAME
+
retq
END(ftrace_graph_caller)
@@ -225,7 +181,7 @@ return_to_handler:
#ifndef CONFIG_PREEMPT
#define retint_kernel retint_restore_args
-#endif
+#endif
#ifdef CONFIG_PARAVIRT
ENTRY(native_usergs_sysret64)
@@ -244,29 +200,29 @@ ENTRY(native_usergs_sysret64)
.endm
/*
- * C code is not supposed to know about undefined top of stack. Every time
- * a C function with an pt_regs argument is called from the SYSCALL based
+ * C code is not supposed to know about undefined top of stack. Every time
+ * a C function with an pt_regs argument is called from the SYSCALL based
* fast path FIXUP_TOP_OF_STACK is needed.
* RESTORE_TOP_OF_STACK syncs the syscall state after any possible ptregs
* manipulation.
- */
-
- /* %rsp:at FRAMEEND */
- .macro FIXUP_TOP_OF_STACK tmp
- movq %gs:pda_oldrsp,\tmp
- movq \tmp,RSP(%rsp)
- movq $__USER_DS,SS(%rsp)
- movq $__USER_CS,CS(%rsp)
- movq $-1,RCX(%rsp)
- movq R11(%rsp),\tmp /* get eflags */
- movq \tmp,EFLAGS(%rsp)
+ */
+
+ /* %rsp:at FRAMEEND */
+ .macro FIXUP_TOP_OF_STACK tmp offset=0
+ movq %gs:pda_oldrsp,\tmp
+ movq \tmp,RSP+\offset(%rsp)
+ movq $__USER_DS,SS+\offset(%rsp)
+ movq $__USER_CS,CS+\offset(%rsp)
+ movq $-1,RCX+\offset(%rsp)
+ movq R11+\offset(%rsp),\tmp /* get eflags */
+ movq \tmp,EFLAGS+\offset(%rsp)
.endm
- .macro RESTORE_TOP_OF_STACK tmp,offset=0
- movq RSP-\offset(%rsp),\tmp
- movq \tmp,%gs:pda_oldrsp
- movq EFLAGS-\offset(%rsp),\tmp
- movq \tmp,R11-\offset(%rsp)
+ .macro RESTORE_TOP_OF_STACK tmp offset=0
+ movq RSP+\offset(%rsp),\tmp
+ movq \tmp,%gs:pda_oldrsp
+ movq EFLAGS+\offset(%rsp),\tmp
+ movq \tmp,R11+\offset(%rsp)
.endm
.macro FAKE_STACK_FRAME child_rip
@@ -278,7 +234,7 @@ ENTRY(native_usergs_sysret64)
pushq %rax /* rsp */
CFI_ADJUST_CFA_OFFSET 8
CFI_REL_OFFSET rsp,0
- pushq $(1<<9) /* eflags - interrupts on */
+ pushq $X86_EFLAGS_IF /* eflags - interrupts on */
CFI_ADJUST_CFA_OFFSET 8
/*CFI_REL_OFFSET rflags,0*/
pushq $__KERNEL_CS /* cs */
@@ -296,62 +252,184 @@ ENTRY(native_usergs_sysret64)
CFI_ADJUST_CFA_OFFSET -(6*8)
.endm
- .macro CFI_DEFAULT_STACK start=1
+/*
+ * initial frame state for interrupts (and exceptions without error code)
+ */
+ .macro EMPTY_FRAME start=1 offset=0
.if \start
- CFI_STARTPROC simple
+ CFI_STARTPROC simple
CFI_SIGNAL_FRAME
- CFI_DEF_CFA rsp,SS+8
+ CFI_DEF_CFA rsp,8+\offset
.else
- CFI_DEF_CFA_OFFSET SS+8
+ CFI_DEF_CFA_OFFSET 8+\offset
.endif
- CFI_REL_OFFSET r15,R15
- CFI_REL_OFFSET r14,R14
- CFI_REL_OFFSET r13,R13
- CFI_REL_OFFSET r12,R12
- CFI_REL_OFFSET rbp,RBP
- CFI_REL_OFFSET rbx,RBX
- CFI_REL_OFFSET r11,R11
- CFI_REL_OFFSET r10,R10
- CFI_REL_OFFSET r9,R9
- CFI_REL_OFFSET r8,R8
- CFI_REL_OFFSET rax,RAX
- CFI_REL_OFFSET rcx,RCX
- CFI_REL_OFFSET rdx,RDX
- CFI_REL_OFFSET rsi,RSI
- CFI_REL_OFFSET rdi,RDI
- CFI_REL_OFFSET rip,RIP
- /*CFI_REL_OFFSET cs,CS*/
- /*CFI_REL_OFFSET rflags,EFLAGS*/
- CFI_REL_OFFSET rsp,RSP
- /*CFI_REL_OFFSET ss,SS*/
.endm
+
/*
- * A newly forked process directly context switches into this.
- */
-/* rdi: prev */
+ * initial frame state for interrupts (and exceptions without error code)
+ */
+ .macro INTR_FRAME start=1 offset=0
+ EMPTY_FRAME \start, SS+8+\offset-RIP
+ /*CFI_REL_OFFSET ss, SS+\offset-RIP*/
+ CFI_REL_OFFSET rsp, RSP+\offset-RIP
+ /*CFI_REL_OFFSET rflags, EFLAGS+\offset-RIP*/
+ /*CFI_REL_OFFSET cs, CS+\offset-RIP*/
+ CFI_REL_OFFSET rip, RIP+\offset-RIP
+ .endm
+
+/*
+ * initial frame state for exceptions with error code (and interrupts
+ * with vector already pushed)
+ */
+ .macro XCPT_FRAME start=1 offset=0
+ INTR_FRAME \start, RIP+\offset-ORIG_RAX
+ /*CFI_REL_OFFSET orig_rax, ORIG_RAX-ORIG_RAX*/
+ .endm
+
+/*
+ * frame that enables calling into C.
+ */
+ .macro PARTIAL_FRAME start=1 offset=0
+ XCPT_FRAME \start, ORIG_RAX+\offset-ARGOFFSET
+ CFI_REL_OFFSET rdi, RDI+\offset-ARGOFFSET
+ CFI_REL_OFFSET rsi, RSI+\offset-ARGOFFSET
+ CFI_REL_OFFSET rdx, RDX+\offset-ARGOFFSET
+ CFI_REL_OFFSET rcx, RCX+\offset-ARGOFFSET
+ CFI_REL_OFFSET rax, RAX+\offset-ARGOFFSET
+ CFI_REL_OFFSET r8, R8+\offset-ARGOFFSET
+ CFI_REL_OFFSET r9, R9+\offset-ARGOFFSET
+ CFI_REL_OFFSET r10, R10+\offset-ARGOFFSET
+ CFI_REL_OFFSET r11, R11+\offset-ARGOFFSET
+ .endm
+
+/*
+ * frame that enables passing a complete pt_regs to a C function.
+ */
+ .macro DEFAULT_FRAME start=1 offset=0
+ PARTIAL_FRAME \start, R11+\offset-R15
+ CFI_REL_OFFSET rbx, RBX+\offset
+ CFI_REL_OFFSET rbp, RBP+\offset
+ CFI_REL_OFFSET r12, R12+\offset
+ CFI_REL_OFFSET r13, R13+\offset
+ CFI_REL_OFFSET r14, R14+\offset
+ CFI_REL_OFFSET r15, R15+\offset
+ .endm
+
+/* save partial stack frame */
+ENTRY(save_args)
+ XCPT_FRAME
+ cld
+ movq_cfi rdi, RDI+16-ARGOFFSET
+ movq_cfi rsi, RSI+16-ARGOFFSET
+ movq_cfi rdx, RDX+16-ARGOFFSET
+ movq_cfi rcx, RCX+16-ARGOFFSET
+ movq_cfi rax, RAX+16-ARGOFFSET
+ movq_cfi r8, R8+16-ARGOFFSET
+ movq_cfi r9, R9+16-ARGOFFSET
+ movq_cfi r10, R10+16-ARGOFFSET
+ movq_cfi r11, R11+16-ARGOFFSET
+
+ leaq -ARGOFFSET+16(%rsp),%rdi /* arg1 for handler */
+ movq_cfi rbp, 8 /* push %rbp */
+ leaq 8(%rsp), %rbp /* mov %rsp, %ebp */
+ testl $3, CS(%rdi)
+ je 1f
+ SWAPGS
+ /*
+ * irqcount is used to check if a CPU is already on an interrupt stack
+ * or not. While this is essentially redundant with preempt_count it is
+ * a little cheaper to use a separate counter in the PDA (short of
+ * moving irq_enter into assembly, which would be too much work)
+ */
+1: incl %gs:pda_irqcount
+ jne 2f
+ popq_cfi %rax /* move return address... */
+ mov %gs:pda_irqstackptr,%rsp
+ EMPTY_FRAME 0
+ pushq_cfi %rax /* ... to the new stack */
+ /*
+ * We entered an interrupt context - irqs are off:
+ */
+2: TRACE_IRQS_OFF
+ ret
+ CFI_ENDPROC
+END(save_args)
+
+ENTRY(save_rest)
+ 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
+ movq_cfi r12, R12+16
+ movq_cfi r13, R13+16
+ movq_cfi r14, R14+16
+ movq_cfi r15, R15+16
+ movq %r11, 8(%rsp) /* return address */
+ FIXUP_TOP_OF_STACK %r11, 16
+ ret
+ CFI_ENDPROC
+END(save_rest)
+
+/* save complete stack frame */
+ENTRY(save_paranoid)
+ XCPT_FRAME 1 RDI+8
+ cld
+ movq_cfi rdi, RDI+8
+ movq_cfi rsi, RSI+8
+ movq_cfi rdx, RDX+8
+ movq_cfi rcx, RCX+8
+ movq_cfi rax, RAX+8
+ movq_cfi r8, R8+8
+ movq_cfi r9, R9+8
+ movq_cfi r10, R10+8
+ movq_cfi r11, R11+8
+ movq_cfi rbx, RBX+8
+ movq_cfi rbp, RBP+8
+ movq_cfi r12, R12+8
+ movq_cfi r13, R13+8
+ movq_cfi r14, R14+8
+ movq_cfi r15, R15+8
+ movl $1,%ebx
+ movl $MSR_GS_BASE,%ecx
+ rdmsr
+ testl %edx,%edx
+ js 1f /* negative -> in kernel */
+ SWAPGS
+ xorl %ebx,%ebx
+1: ret
+ CFI_ENDPROC
+END(save_paranoid)
+
+/*
+ * A newly forked process directly context switches into this address.
+ *
+ * rdi: prev task we switched from
+ */
ENTRY(ret_from_fork)
- CFI_DEFAULT_STACK
+ DEFAULT_FRAME
+
push kernel_eflags(%rip)
CFI_ADJUST_CFA_OFFSET 8
- popf # reset kernel eflags
+ popf # reset kernel eflags
CFI_ADJUST_CFA_OFFSET -8
- call schedule_tail
+
+ call schedule_tail # rdi: 'prev' task parameter
+
GET_THREAD_INFO(%rcx)
- testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%rcx)
- jnz rff_trace
-rff_action:
+
+ CFI_REMEMBER_STATE
RESTORE_REST
- testl $3,CS-ARGOFFSET(%rsp) # from kernel_thread?
+
+ testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread?
je int_ret_from_sys_call
- testl $_TIF_IA32,TI_flags(%rcx)
+
+ testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET
jnz int_ret_from_sys_call
- RESTORE_TOP_OF_STACK %rdi,ARGOFFSET
- jmp ret_from_sys_call
-rff_trace:
- movq %rsp,%rdi
- call syscall_trace_leave
- GET_THREAD_INFO(%rcx)
- jmp rff_action
+
+ RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET
+ jmp ret_from_sys_call # go to the SYSRET fastpath
+
+ CFI_RESTORE_STATE
CFI_ENDPROC
END(ret_from_fork)
@@ -361,20 +439,20 @@ END(ret_from_fork)
* SYSCALL does not save anything on the stack and does not change the
* stack pointer.
*/
-
+
/*
- * Register setup:
+ * Register setup:
* rax system call number
* rdi arg0
- * rcx return address for syscall/sysret, C arg3
+ * rcx return address for syscall/sysret, C arg3
* rsi arg1
- * rdx arg2
+ * rdx arg2
* r10 arg3 (--> moved to rcx for C)
* r8 arg4
* r9 arg5
* r11 eflags for syscall/sysret, temporary for C
- * r12-r15,rbp,rbx saved by C code, not touched.
- *
+ * r12-r15,rbp,rbx saved by C code, not touched.
+ *
* Interrupts are off on entry.
* Only called from user space.
*
@@ -384,7 +462,7 @@ END(ret_from_fork)
* When user can change the frames always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
- */
+ */
ENTRY(system_call)
CFI_STARTPROC simple
@@ -400,7 +478,7 @@ ENTRY(system_call)
*/
ENTRY(system_call_after_swapgs)
- movq %rsp,%gs:pda_oldrsp
+ movq %rsp,%gs:pda_oldrsp
movq %gs:pda_kernelstack,%rsp
/*
* No need to follow this irqs off/on section - it's straight
@@ -408,7 +486,7 @@ ENTRY(system_call_after_swapgs)
*/
ENABLE_INTERRUPTS(CLBR_NONE)
SAVE_ARGS 8,1
- movq %rax,ORIG_RAX-ARGOFFSET(%rsp)
+ movq %rax,ORIG_RAX-ARGOFFSET(%rsp)
movq %rcx,RIP-ARGOFFSET(%rsp)
CFI_REL_OFFSET rip,RIP-ARGOFFSET
GET_THREAD_INFO(%rcx)
@@ -422,19 +500,19 @@ system_call_fastpath:
movq %rax,RAX-ARGOFFSET(%rsp)
/*
* Syscall return path ending with SYSRET (fast path)
- * Has incomplete stack frame and undefined top of stack.
- */
+ * Has incomplete stack frame and undefined top of stack.
+ */
ret_from_sys_call:
movl $_TIF_ALLWORK_MASK,%edi
/* edi: flagmask */
-sysret_check:
+sysret_check:
LOCKDEP_SYS_EXIT
GET_THREAD_INFO(%rcx)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
movl TI_flags(%rcx),%edx
andl %edi,%edx
- jnz sysret_careful
+ jnz sysret_careful
CFI_REMEMBER_STATE
/*
* sysretq will re-enable interrupts:
@@ -449,7 +527,7 @@ sysret_check:
CFI_RESTORE_STATE
/* Handle reschedules */
- /* edx: work, edi: workmask */
+ /* edx: work, edi: workmask */
sysret_careful:
bt $TIF_NEED_RESCHED,%edx
jnc sysret_signal
@@ -462,7 +540,7 @@ sysret_careful:
CFI_ADJUST_CFA_OFFSET -8
jmp sysret_check
- /* Handle a signal */
+ /* Handle a signal */
sysret_signal:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
@@ -471,17 +549,20 @@ sysret_signal:
jc sysret_audit
#endif
/* edx: work flags (arg3) */
- leaq do_notify_resume(%rip),%rax
leaq -ARGOFFSET(%rsp),%rdi # &pt_regs -> arg1
xorl %esi,%esi # oldset -> arg2
- call ptregscall_common
+ SAVE_REST
+ FIXUP_TOP_OF_STACK %r11
+ call do_notify_resume
+ RESTORE_TOP_OF_STACK %r11
+ RESTORE_REST
movl $_TIF_WORK_MASK,%edi
/* Use IRET because user could have changed frame. This
works because ptregscall_common has called FIXUP_TOP_OF_STACK. */
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
jmp int_with_check
-
+
badsys:
movq $-ENOSYS,RAX-ARGOFFSET(%rsp)
jmp ret_from_sys_call
@@ -520,7 +601,7 @@ sysret_audit:
#endif /* CONFIG_AUDITSYSCALL */
/* Do syscall tracing */
-tracesys:
+tracesys:
#ifdef CONFIG_AUDITSYSCALL
testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%rcx)
jz auditsys
@@ -543,8 +624,8 @@ tracesys:
call *sys_call_table(,%rax,8)
movq %rax,RAX-ARGOFFSET(%rsp)
/* Use IRET because user could have changed frame */
-
-/*
+
+/*
* Syscall return path ending with IRET.
* Has correct top of stack, but partial stack frame.
*/
@@ -588,18 +669,18 @@ int_very_careful:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
SAVE_REST
- /* Check for syscall exit trace */
+ /* Check for syscall exit trace */
testl $_TIF_WORK_SYSCALL_EXIT,%edx
jz int_signal
pushq %rdi
CFI_ADJUST_CFA_OFFSET 8
- leaq 8(%rsp),%rdi # &ptregs -> arg1
+ leaq 8(%rsp),%rdi # &ptregs -> arg1
call syscall_trace_leave
popq %rdi
CFI_ADJUST_CFA_OFFSET -8
andl $~(_TIF_WORK_SYSCALL_EXIT|_TIF_SYSCALL_EMU),%edi
jmp int_restore_rest
-
+
int_signal:
testl $_TIF_DO_NOTIFY_MASK,%edx
jz 1f
@@ -614,22 +695,24 @@ int_restore_rest:
jmp int_with_check
CFI_ENDPROC
END(system_call)
-
-/*
+
+/*
* Certain special system calls that need to save a complete full stack frame.
- */
-
+ */
.macro PTREGSCALL label,func,arg
- .globl \label
-\label:
- leaq \func(%rip),%rax
- leaq -ARGOFFSET+8(%rsp),\arg /* 8 for return address */
- jmp ptregscall_common
+ENTRY(\label)
+ PARTIAL_FRAME 1 8 /* offset 8: return address */
+ subq $REST_SKIP, %rsp
+ CFI_ADJUST_CFA_OFFSET REST_SKIP
+ call save_rest
+ DEFAULT_FRAME 0 8 /* offset 8: return address */
+ leaq 8(%rsp), \arg /* pt_regs pointer */
+ call \func
+ jmp ptregscall_common
+ CFI_ENDPROC
END(\label)
.endm
- CFI_STARTPROC
-
PTREGSCALL stub_clone, sys_clone, %r8
PTREGSCALL stub_fork, sys_fork, %rdi
PTREGSCALL stub_vfork, sys_vfork, %rdi
@@ -637,25 +720,18 @@ END(\label)
PTREGSCALL stub_iopl, sys_iopl, %rsi
ENTRY(ptregscall_common)
- popq %r11
- CFI_ADJUST_CFA_OFFSET -8
- CFI_REGISTER rip, r11
- SAVE_REST
- movq %r11, %r15
- CFI_REGISTER rip, r15
- FIXUP_TOP_OF_STACK %r11
- call *%rax
- RESTORE_TOP_OF_STACK %r11
- movq %r15, %r11
- CFI_REGISTER rip, r11
- RESTORE_REST
- pushq %r11
- CFI_ADJUST_CFA_OFFSET 8
- CFI_REL_OFFSET rip, 0
- ret
+ DEFAULT_FRAME 1 8 /* offset 8: return address */
+ RESTORE_TOP_OF_STACK %r11, 8
+ movq_cfi_restore R15+8, r15
+ movq_cfi_restore R14+8, r14
+ movq_cfi_restore R13+8, r13
+ movq_cfi_restore R12+8, r12
+ movq_cfi_restore RBP+8, rbp
+ movq_cfi_restore RBX+8, rbx
+ ret $REST_SKIP /* pop extended registers */
CFI_ENDPROC
END(ptregscall_common)
-
+
ENTRY(stub_execve)
CFI_STARTPROC
popq %r11
@@ -671,11 +747,11 @@ ENTRY(stub_execve)
jmp int_ret_from_sys_call
CFI_ENDPROC
END(stub_execve)
-
+
/*
* sigreturn is special because it needs to restore all registers on return.
* This cannot be done with SYSRET, so use the IRET return path instead.
- */
+ */
ENTRY(stub_rt_sigreturn)
CFI_STARTPROC
addq $8, %rsp
@@ -691,70 +767,70 @@ ENTRY(stub_rt_sigreturn)
END(stub_rt_sigreturn)
/*
- * initial frame state for interrupts and exceptions
+ * Build the entry stubs and pointer table with some assembler magic.
+ * We pack 7 stubs into a single 32-byte chunk, which will fit in a
+ * single cache line on all modern x86 implementations.
*/
- .macro _frame ref
- CFI_STARTPROC simple
- CFI_SIGNAL_FRAME
- CFI_DEF_CFA rsp,SS+8-\ref
- /*CFI_REL_OFFSET ss,SS-\ref*/
- CFI_REL_OFFSET rsp,RSP-\ref
- /*CFI_REL_OFFSET rflags,EFLAGS-\ref*/
- /*CFI_REL_OFFSET cs,CS-\ref*/
- CFI_REL_OFFSET rip,RIP-\ref
- .endm
+ .section .init.rodata,"a"
+ENTRY(interrupt)
+ .text
+ .p2align 5
+ .p2align CONFIG_X86_L1_CACHE_SHIFT
+ENTRY(irq_entries_start)
+ INTR_FRAME
+vector=FIRST_EXTERNAL_VECTOR
+.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
+ .balign 32
+ .rept 7
+ .if vector < NR_VECTORS
+ .if vector <> FIRST_EXTERNAL_VECTOR
+ CFI_ADJUST_CFA_OFFSET -8
+ .endif
+1: pushq $(~vector+0x80) /* Note: always in signed byte range */
+ CFI_ADJUST_CFA_OFFSET 8
+ .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
+ jmp 2f
+ .endif
+ .previous
+ .quad 1b
+ .text
+vector=vector+1
+ .endif
+ .endr
+2: jmp common_interrupt
+.endr
+ CFI_ENDPROC
+END(irq_entries_start)
-/* initial frame state for interrupts (and exceptions without error code) */
-#define INTR_FRAME _frame RIP
-/* initial frame state for exceptions with error code (and interrupts with
- vector already pushed) */
-#define XCPT_FRAME _frame ORIG_RAX
+.previous
+END(interrupt)
+.previous
-/*
+/*
* Interrupt entry/exit.
*
* Interrupt entry points save only callee clobbered registers in fast path.
- *
- * Entry runs with interrupts off.
- */
+ *
+ * Entry runs with interrupts off.
+ */
-/* 0(%rsp): interrupt number */
+/* 0(%rsp): ~(interrupt number) */
.macro interrupt func
- cld
- SAVE_ARGS
- leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler
- pushq %rbp
- /*
- * Save rbp twice: One is for marking the stack frame, as usual, and the
- * other, to fill pt_regs properly. This is because bx comes right
- * before the last saved register in that structure, and not bp. If the
- * base pointer were in the place bx is today, this would not be needed.
- */
- movq %rbp, -8(%rsp)
- CFI_ADJUST_CFA_OFFSET 8
- CFI_REL_OFFSET rbp, 0
- movq %rsp,%rbp
- CFI_DEF_CFA_REGISTER rbp
- testl $3,CS(%rdi)
- je 1f
- SWAPGS
- /* irqcount is used to check if a CPU is already on an interrupt
- stack or not. While this is essentially redundant with preempt_count
- it is a little cheaper to use a separate counter in the PDA
- (short of moving irq_enter into assembly, which would be too
- much work) */
-1: incl %gs:pda_irqcount
- cmoveq %gs:pda_irqstackptr,%rsp
- push %rbp # backlink for old unwinder
- /*
- * We entered an interrupt context - irqs are off:
- */
- TRACE_IRQS_OFF
+ subq $10*8, %rsp
+ CFI_ADJUST_CFA_OFFSET 10*8
+ call save_args
+ PARTIAL_FRAME 0
call \func
.endm
-ENTRY(common_interrupt)
+ /*
+ * The interrupt stubs push (~vector+0x80) onto the stack and
+ * then jump to common_interrupt.
+ */
+ .p2align CONFIG_X86_L1_CACHE_SHIFT
+common_interrupt:
XCPT_FRAME
+ addq $-0x80,(%rsp) /* Adjust vector to [-256,-1] range */
interrupt do_IRQ
/* 0(%rsp): oldrsp-ARGOFFSET */
ret_from_intr:
@@ -768,12 +844,12 @@ exit_intr:
GET_THREAD_INFO(%rcx)
testl $3,CS-ARGOFFSET(%rsp)
je retint_kernel
-
+
/* Interrupt came from user space */
/*
* Has a correct top of stack, but a partial stack frame
* %rcx: thread info. Interrupts off.
- */
+ */
retint_with_reschedule:
movl $_TIF_WORK_MASK,%edi
retint_check:
@@ -846,20 +922,20 @@ retint_careful:
pushq %rdi
CFI_ADJUST_CFA_OFFSET 8
call schedule
- popq %rdi
+ popq %rdi
CFI_ADJUST_CFA_OFFSET -8
GET_THREAD_INFO(%rcx)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
jmp retint_check
-
+
retint_signal:
testl $_TIF_DO_NOTIFY_MASK,%edx
jz retint_swapgs
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
SAVE_REST
- movq $-1,ORIG_RAX(%rsp)
+ movq $-1,ORIG_RAX(%rsp)
xorl %esi,%esi # oldset
movq %rsp,%rdi # &pt_regs
call do_notify_resume
@@ -881,324 +957,211 @@ ENTRY(retint_kernel)
jnc retint_restore_args
call preempt_schedule_irq
jmp exit_intr
-#endif
+#endif
CFI_ENDPROC
END(common_interrupt)
-
+
/*
* APIC interrupts.
- */
- .macro apicinterrupt num,func
+ */
+.macro apicinterrupt num sym do_sym
+ENTRY(\sym)
INTR_FRAME
pushq $~(\num)
CFI_ADJUST_CFA_OFFSET 8
- interrupt \func
+ interrupt \do_sym
jmp ret_from_intr
CFI_ENDPROC
- .endm
-
-ENTRY(thermal_interrupt)
- apicinterrupt THERMAL_APIC_VECTOR,smp_thermal_interrupt
-END(thermal_interrupt)
-
-ENTRY(threshold_interrupt)
- apicinterrupt THRESHOLD_APIC_VECTOR,mce_threshold_interrupt
-END(threshold_interrupt)
-
-#ifdef CONFIG_SMP
-ENTRY(reschedule_interrupt)
- apicinterrupt RESCHEDULE_VECTOR,smp_reschedule_interrupt
-END(reschedule_interrupt)
-
- .macro INVALIDATE_ENTRY num
-ENTRY(invalidate_interrupt\num)
- apicinterrupt INVALIDATE_TLB_VECTOR_START+\num,smp_invalidate_interrupt
-END(invalidate_interrupt\num)
- .endm
+END(\sym)
+.endm
- INVALIDATE_ENTRY 0
- INVALIDATE_ENTRY 1
- INVALIDATE_ENTRY 2
- INVALIDATE_ENTRY 3
- INVALIDATE_ENTRY 4
- INVALIDATE_ENTRY 5
- INVALIDATE_ENTRY 6
- INVALIDATE_ENTRY 7
-
-ENTRY(call_function_interrupt)
- apicinterrupt CALL_FUNCTION_VECTOR,smp_call_function_interrupt
-END(call_function_interrupt)
-ENTRY(call_function_single_interrupt)
- apicinterrupt CALL_FUNCTION_SINGLE_VECTOR,smp_call_function_single_interrupt
-END(call_function_single_interrupt)
-ENTRY(irq_move_cleanup_interrupt)
- apicinterrupt IRQ_MOVE_CLEANUP_VECTOR,smp_irq_move_cleanup_interrupt
-END(irq_move_cleanup_interrupt)
+#ifdef CONFIG_SMP
+apicinterrupt IRQ_MOVE_CLEANUP_VECTOR \
+ irq_move_cleanup_interrupt smp_irq_move_cleanup_interrupt
#endif
-ENTRY(apic_timer_interrupt)
- apicinterrupt LOCAL_TIMER_VECTOR,smp_apic_timer_interrupt
-END(apic_timer_interrupt)
+apicinterrupt UV_BAU_MESSAGE \
+ uv_bau_message_intr1 uv_bau_message_interrupt
+apicinterrupt LOCAL_TIMER_VECTOR \
+ apic_timer_interrupt smp_apic_timer_interrupt
+
+#ifdef CONFIG_SMP
+apicinterrupt INVALIDATE_TLB_VECTOR_START+0 \
+ invalidate_interrupt0 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+1 \
+ invalidate_interrupt1 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+2 \
+ invalidate_interrupt2 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+3 \
+ invalidate_interrupt3 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+4 \
+ invalidate_interrupt4 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+5 \
+ invalidate_interrupt5 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+6 \
+ invalidate_interrupt6 smp_invalidate_interrupt
+apicinterrupt INVALIDATE_TLB_VECTOR_START+7 \
+ invalidate_interrupt7 smp_invalidate_interrupt
+#endif
-ENTRY(uv_bau_message_intr1)
- apicinterrupt 220,uv_bau_message_interrupt
-END(uv_bau_message_intr1)
+apicinterrupt THRESHOLD_APIC_VECTOR \
+ threshold_interrupt mce_threshold_interrupt
+apicinterrupt THERMAL_APIC_VECTOR \
+ thermal_interrupt smp_thermal_interrupt
+
+#ifdef CONFIG_SMP
+apicinterrupt CALL_FUNCTION_SINGLE_VECTOR \
+ call_function_single_interrupt smp_call_function_single_interrupt
+apicinterrupt CALL_FUNCTION_VECTOR \
+ call_function_interrupt smp_call_function_interrupt
+apicinterrupt RESCHEDULE_VECTOR \
+ reschedule_interrupt smp_reschedule_interrupt
+#endif
-ENTRY(error_interrupt)
- apicinterrupt ERROR_APIC_VECTOR,smp_error_interrupt
-END(error_interrupt)
+apicinterrupt ERROR_APIC_VECTOR \
+ error_interrupt smp_error_interrupt
+apicinterrupt SPURIOUS_APIC_VECTOR \
+ spurious_interrupt smp_spurious_interrupt
-ENTRY(spurious_interrupt)
- apicinterrupt SPURIOUS_APIC_VECTOR,smp_spurious_interrupt
-END(spurious_interrupt)
-
/*
* Exception entry points.
- */
- .macro zeroentry sym
+ */
+.macro zeroentry sym do_sym
+ENTRY(\sym)
INTR_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq $0 /* push error code/oldrax */
- CFI_ADJUST_CFA_OFFSET 8
- pushq %rax /* push real oldrax to the rdi slot */
- CFI_ADJUST_CFA_OFFSET 8
- CFI_REL_OFFSET rax,0
- leaq \sym(%rip),%rax
- jmp error_entry
+ pushq_cfi $-1 /* ORIG_RAX: no syscall to restart */
+ subq $15*8,%rsp
+ CFI_ADJUST_CFA_OFFSET 15*8
+ call error_entry
+ DEFAULT_FRAME 0
+ movq %rsp,%rdi /* pt_regs pointer */
+ xorl %esi,%esi /* no error code */
+ call \do_sym
+ jmp error_exit /* %ebx: no swapgs flag */
CFI_ENDPROC
- .endm
+END(\sym)
+.endm
- .macro errorentry sym
- XCPT_FRAME
+.macro paranoidzeroentry sym do_sym
+ENTRY(\sym)
+ INTR_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq %rax
+ pushq $-1 /* ORIG_RAX: no syscall to restart */
CFI_ADJUST_CFA_OFFSET 8
- CFI_REL_OFFSET rax,0
- leaq \sym(%rip),%rax
- jmp error_entry
+ subq $15*8, %rsp
+ call save_paranoid
+ TRACE_IRQS_OFF
+ movq %rsp,%rdi /* pt_regs pointer */
+ xorl %esi,%esi /* no error code */
+ call \do_sym
+ jmp paranoid_exit /* %ebx: no swapgs flag */
CFI_ENDPROC
- .endm
+END(\sym)
+.endm
- /* error code is on the stack already */
- /* handle NMI like exceptions that can happen everywhere */
- .macro paranoidentry sym, ist=0, irqtrace=1
- SAVE_ALL
- cld
- movl $1,%ebx
- movl $MSR_GS_BASE,%ecx
- rdmsr
- testl %edx,%edx
- js 1f
- SWAPGS
- xorl %ebx,%ebx
-1:
- .if \ist
- movq %gs:pda_data_offset, %rbp
- .endif
- .if \irqtrace
- TRACE_IRQS_OFF
- .endif
- movq %rsp,%rdi
- movq ORIG_RAX(%rsp),%rsi
- movq $-1,ORIG_RAX(%rsp)
- .if \ist
- subq $EXCEPTION_STKSZ, per_cpu__init_tss + TSS_ist + (\ist - 1) * 8(%rbp)
- .endif
- call \sym
- .if \ist
- addq $EXCEPTION_STKSZ, per_cpu__init_tss + TSS_ist + (\ist - 1) * 8(%rbp)
- .endif
- DISABLE_INTERRUPTS(CLBR_NONE)
- .if \irqtrace
+.macro paranoidzeroentry_ist sym do_sym ist
+ENTRY(\sym)
+ INTR_FRAME
+ PARAVIRT_ADJUST_EXCEPTION_FRAME
+ pushq $-1 /* ORIG_RAX: no syscall to restart */
+ CFI_ADJUST_CFA_OFFSET 8
+ subq $15*8, %rsp
+ call save_paranoid
TRACE_IRQS_OFF
- .endif
- .endm
+ movq %rsp,%rdi /* pt_regs pointer */
+ xorl %esi,%esi /* no error code */
+ movq %gs:pda_data_offset, %rbp
+ subq $EXCEPTION_STKSZ, per_cpu__init_tss + TSS_ist + (\ist - 1) * 8(%rbp)
+ call \do_sym
+ addq $EXCEPTION_STKSZ, per_cpu__init_tss + TSS_ist + (\ist - 1) * 8(%rbp)
+ jmp paranoid_exit /* %ebx: no swapgs flag */
+ CFI_ENDPROC
+END(\sym)
+.endm
- /*
- * "Paranoid" exit path from exception stack.
- * Paranoid because this is used by NMIs and cannot take
- * any kernel state for granted.
- * We don't do kernel preemption checks here, because only
- * NMI should be common and it does not enable IRQs and
- * cannot get reschedule ticks.
- *
- * "trace" is 0 for the NMI handler only, because irq-tracing
- * is fundamentally NMI-unsafe. (we cannot change the soft and
- * hard flags at once, atomically)
- */
- .macro paranoidexit trace=1
- /* ebx: no swapgs flag */
-paranoid_exit\trace:
- testl %ebx,%ebx /* swapgs needed? */
- jnz paranoid_restore\trace
- testl $3,CS(%rsp)
- jnz paranoid_userspace\trace
-paranoid_swapgs\trace:
- .if \trace
- TRACE_IRQS_IRETQ 0
- .endif
- SWAPGS_UNSAFE_STACK
-paranoid_restore\trace:
- RESTORE_ALL 8
- jmp irq_return
-paranoid_userspace\trace:
- GET_THREAD_INFO(%rcx)
- movl TI_flags(%rcx),%ebx
- andl $_TIF_WORK_MASK,%ebx
- jz paranoid_swapgs\trace
- movq %rsp,%rdi /* &pt_regs */
- call sync_regs
- movq %rax,%rsp /* switch stack for scheduling */
- testl $_TIF_NEED_RESCHED,%ebx
- jnz paranoid_schedule\trace
- movl %ebx,%edx /* arg3: thread flags */
- .if \trace
- TRACE_IRQS_ON
- .endif
- ENABLE_INTERRUPTS(CLBR_NONE)
- xorl %esi,%esi /* arg2: oldset */
- movq %rsp,%rdi /* arg1: &pt_regs */
- call do_notify_resume
- DISABLE_INTERRUPTS(CLBR_NONE)
- .if \trace
- TRACE_IRQS_OFF
- .endif
- jmp paranoid_userspace\trace
-paranoid_schedule\trace:
- .if \trace
- TRACE_IRQS_ON
- .endif
- ENABLE_INTERRUPTS(CLBR_ANY)
- call schedule
- DISABLE_INTERRUPTS(CLBR_ANY)
- .if \trace
- TRACE_IRQS_OFF
- .endif
- jmp paranoid_userspace\trace
+.macro errorentry sym do_sym
+ENTRY(\sym)
+ XCPT_FRAME
+ PARAVIRT_ADJUST_EXCEPTION_FRAME
+ subq $15*8,%rsp
+ CFI_ADJUST_CFA_OFFSET 15*8
+ call error_entry
+ DEFAULT_FRAME 0
+ movq %rsp,%rdi /* pt_regs pointer */
+ movq ORIG_RAX(%rsp),%rsi /* get error code */
+ movq $-1,ORIG_RAX(%rsp) /* no syscall to restart */
+ call \do_sym
+ jmp error_exit /* %ebx: no swapgs flag */
CFI_ENDPROC
- .endm
+END(\sym)
+.endm
-/*
- * Exception entry point. This expects an error code/orig_rax on the stack
- * and the exception handler in %rax.
- */
-KPROBE_ENTRY(error_entry)
- _frame RDI
- CFI_REL_OFFSET rax,0
- /* rdi slot contains rax, oldrax contains error code */
- cld
- subq $14*8,%rsp
- CFI_ADJUST_CFA_OFFSET (14*8)
- movq %rsi,13*8(%rsp)
- CFI_REL_OFFSET rsi,RSI
- movq 14*8(%rsp),%rsi /* load rax from rdi slot */
- CFI_REGISTER rax,rsi
- movq %rdx,12*8(%rsp)
- CFI_REL_OFFSET rdx,RDX
- movq %rcx,11*8(%rsp)
- CFI_REL_OFFSET rcx,RCX
- movq %rsi,10*8(%rsp) /* store rax */
- CFI_REL_OFFSET rax,RAX
- movq %r8, 9*8(%rsp)
- CFI_REL_OFFSET r8,R8
- movq %r9, 8*8(%rsp)
- CFI_REL_OFFSET r9,R9
- movq %r10,7*8(%rsp)
- CFI_REL_OFFSET r10,R10
- movq %r11,6*8(%rsp)
- CFI_REL_OFFSET r11,R11
- movq %rbx,5*8(%rsp)
- CFI_REL_OFFSET rbx,RBX
- movq %rbp,4*8(%rsp)
- CFI_REL_OFFSET rbp,RBP
- movq %r12,3*8(%rsp)
- CFI_REL_OFFSET r12,R12
- movq %r13,2*8(%rsp)
- CFI_REL_OFFSET r13,R13
- movq %r14,1*8(%rsp)
- CFI_REL_OFFSET r14,R14
- movq %r15,(%rsp)
- CFI_REL_OFFSET r15,R15
- xorl %ebx,%ebx
- testl $3,CS(%rsp)
- je error_kernelspace
-error_swapgs:
- SWAPGS
-error_sti:
- TRACE_IRQS_OFF
- movq %rdi,RDI(%rsp)
- CFI_REL_OFFSET rdi,RDI
- movq %rsp,%rdi
- movq ORIG_RAX(%rsp),%rsi /* get error code */
- movq $-1,ORIG_RAX(%rsp)
- call *%rax
- /* ebx: no swapgs flag (1: don't need swapgs, 0: need it) */
-error_exit:
- movl %ebx,%eax
- RESTORE_REST
- DISABLE_INTERRUPTS(CLBR_NONE)
+ /* error code is on the stack already */
+.macro paranoiderrorentry sym do_sym
+ENTRY(\sym)
+ XCPT_FRAME
+ PARAVIRT_ADJUST_EXCEPTION_FRAME
+ subq $15*8,%rsp
+ CFI_ADJUST_CFA_OFFSET 15*8
+ call save_paranoid
+ DEFAULT_FRAME 0
TRACE_IRQS_OFF
- GET_THREAD_INFO(%rcx)
- testl %eax,%eax
- jne retint_kernel
- LOCKDEP_SYS_EXIT_IRQ
- movl TI_flags(%rcx),%edx
- movl $_TIF_WORK_MASK,%edi
- andl %edi,%edx
- jnz retint_careful
- jmp retint_swapgs
+ movq %rsp,%rdi /* pt_regs pointer */
+ movq ORIG_RAX(%rsp),%rsi /* get error code */
+ movq $-1,ORIG_RAX(%rsp) /* no syscall to restart */
+ call \do_sym
+ jmp paranoid_exit /* %ebx: no swapgs flag */
CFI_ENDPROC
+END(\sym)
+.endm
-error_kernelspace:
- incl %ebx
- /* There are two places in the kernel that can potentially fault with
- usergs. Handle them here. The exception handlers after
- iret run with kernel gs again, so don't set the user space flag.
- B stepping K8s sometimes report an truncated RIP for IRET
- exceptions returning to compat mode. Check for these here too. */
- leaq irq_return(%rip),%rcx
- cmpq %rcx,RIP(%rsp)
- je error_swapgs
- movl %ecx,%ecx /* zero extend */
- cmpq %rcx,RIP(%rsp)
- je error_swapgs
- cmpq $gs_change,RIP(%rsp)
- je error_swapgs
- jmp error_sti
-KPROBE_END(error_entry)
-
- /* Reload gs selector with exception handling */
- /* edi: new selector */
+zeroentry divide_error do_divide_error
+zeroentry overflow do_overflow
+zeroentry bounds do_bounds
+zeroentry invalid_op do_invalid_op
+zeroentry device_not_available do_device_not_available
+paranoiderrorentry double_fault do_double_fault
+zeroentry coprocessor_segment_overrun do_coprocessor_segment_overrun
+errorentry invalid_TSS do_invalid_TSS
+errorentry segment_not_present do_segment_not_present
+zeroentry spurious_interrupt_bug do_spurious_interrupt_bug
+zeroentry coprocessor_error do_coprocessor_error
+errorentry alignment_check do_alignment_check
+zeroentry simd_coprocessor_error do_simd_coprocessor_error
+
+ /* Reload gs selector with exception handling */
+ /* edi: new selector */
ENTRY(native_load_gs_index)
CFI_STARTPROC
pushf
CFI_ADJUST_CFA_OFFSET 8
DISABLE_INTERRUPTS(CLBR_ANY | ~(CLBR_RDI))
- SWAPGS
-gs_change:
- movl %edi,%gs
+ SWAPGS
+gs_change:
+ movl %edi,%gs
2: mfence /* workaround */
SWAPGS
- popf
+ popf
CFI_ADJUST_CFA_OFFSET -8
- ret
+ ret
CFI_ENDPROC
-ENDPROC(native_load_gs_index)
-
- .section __ex_table,"a"
- .align 8
- .quad gs_change,bad_gs
- .previous
- .section .fixup,"ax"
+END(native_load_gs_index)
+
+ .section __ex_table,"a"
+ .align 8
+ .quad gs_change,bad_gs
+ .previous
+ .section .fixup,"ax"
/* running with kernelgs */
-bad_gs:
+bad_gs:
SWAPGS /* switch back to user gs */
xorl %eax,%eax
- movl %eax,%gs
- jmp 2b
- .previous
-
+ movl %eax,%gs
+ jmp 2b
+ .previous
+
/*
* Create a kernel thread.
*
@@ -1221,7 +1184,7 @@ ENTRY(kernel_thread)
xorl %r8d,%r8d
xorl %r9d,%r9d
-
+
# clone now
call do_fork
movq %rax,RAX(%rsp)
@@ -1232,15 +1195,15 @@ ENTRY(kernel_thread)
* so internally to the x86_64 port you can rely on kernel_thread()
* not to reschedule the child before returning, this avoids the need
* of hacks for example to fork off the per-CPU idle tasks.
- * [Hopefully no generic code relies on the reschedule -AK]
+ * [Hopefully no generic code relies on the reschedule -AK]
*/
RESTORE_ALL
UNFAKE_STACK_FRAME
ret
CFI_ENDPROC
-ENDPROC(kernel_thread)
-
-child_rip:
+END(kernel_thread)
+
+ENTRY(child_rip)
pushq $0 # fake return address
CFI_STARTPROC
/*
@@ -1253,8 +1216,9 @@ child_rip:
# exit
mov %eax, %edi
call do_exit
+ ud2 # padding for call trace
CFI_ENDPROC
-ENDPROC(child_rip)
+END(child_rip)
/*
* execve(). This function needs to use IRET, not SYSRET, to set up all state properly.
@@ -1274,10 +1238,10 @@ ENDPROC(child_rip)
ENTRY(kernel_execve)
CFI_STARTPROC
FAKE_STACK_FRAME $0
- SAVE_ALL
+ SAVE_ALL
movq %rsp,%rcx
call sys_execve
- movq %rax, RAX(%rsp)
+ movq %rax, RAX(%rsp)
RESTORE_REST
testq %rax,%rax
je int_ret_from_sys_call
@@ -1285,129 +1249,7 @@ ENTRY(kernel_execve)
UNFAKE_STACK_FRAME
ret
CFI_ENDPROC
-ENDPROC(kernel_execve)
-
-KPROBE_ENTRY(page_fault)
- errorentry do_page_fault
-KPROBE_END(page_fault)
-
-ENTRY(coprocessor_error)
- zeroentry do_coprocessor_error
-END(coprocessor_error)
-
-ENTRY(simd_coprocessor_error)
- zeroentry do_simd_coprocessor_error
-END(simd_coprocessor_error)
-
-ENTRY(device_not_available)
- zeroentry do_device_not_available
-END(device_not_available)
-
- /* runs on exception stack */
-KPROBE_ENTRY(debug)
- INTR_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq $0
- CFI_ADJUST_CFA_OFFSET 8
- paranoidentry do_debug, DEBUG_STACK
- paranoidexit
-KPROBE_END(debug)
-
- /* runs on exception stack */
-KPROBE_ENTRY(nmi)
- INTR_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq $-1
- CFI_ADJUST_CFA_OFFSET 8
- paranoidentry do_nmi, 0, 0
-#ifdef CONFIG_TRACE_IRQFLAGS
- paranoidexit 0
-#else
- jmp paranoid_exit1
- CFI_ENDPROC
-#endif
-KPROBE_END(nmi)
-
-KPROBE_ENTRY(int3)
- INTR_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq $0
- CFI_ADJUST_CFA_OFFSET 8
- paranoidentry do_int3, DEBUG_STACK
- jmp paranoid_exit1
- CFI_ENDPROC
-KPROBE_END(int3)
-
-ENTRY(overflow)
- zeroentry do_overflow
-END(overflow)
-
-ENTRY(bounds)
- zeroentry do_bounds
-END(bounds)
-
-ENTRY(invalid_op)
- zeroentry do_invalid_op
-END(invalid_op)
-
-ENTRY(coprocessor_segment_overrun)
- zeroentry do_coprocessor_segment_overrun
-END(coprocessor_segment_overrun)
-
- /* runs on exception stack */
-ENTRY(double_fault)
- XCPT_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- paranoidentry do_double_fault
- jmp paranoid_exit1
- CFI_ENDPROC
-END(double_fault)
-
-ENTRY(invalid_TSS)
- errorentry do_invalid_TSS
-END(invalid_TSS)
-
-ENTRY(segment_not_present)
- errorentry do_segment_not_present
-END(segment_not_present)
-
- /* runs on exception stack */
-ENTRY(stack_segment)
- XCPT_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- paranoidentry do_stack_segment
- jmp paranoid_exit1
- CFI_ENDPROC
-END(stack_segment)
-
-KPROBE_ENTRY(general_protection)
- errorentry do_general_protection
-KPROBE_END(general_protection)
-
-ENTRY(alignment_check)
- errorentry do_alignment_check
-END(alignment_check)
-
-ENTRY(divide_error)
- zeroentry do_divide_error
-END(divide_error)
-
-ENTRY(spurious_interrupt_bug)
- zeroentry do_spurious_interrupt_bug
-END(spurious_interrupt_bug)
-
-#ifdef CONFIG_X86_MCE
- /* runs on exception stack */
-ENTRY(machine_check)
- INTR_FRAME
- PARAVIRT_ADJUST_EXCEPTION_FRAME
- pushq $0
- CFI_ADJUST_CFA_OFFSET 8
- paranoidentry do_machine_check
- jmp paranoid_exit1
- CFI_ENDPROC
-END(machine_check)
-#endif
+END(kernel_execve)
/* Call softirq on interrupt stack. Interrupts are off. */
ENTRY(call_softirq)
@@ -1427,40 +1269,33 @@ ENTRY(call_softirq)
decl %gs:pda_irqcount
ret
CFI_ENDPROC
-ENDPROC(call_softirq)
-
-KPROBE_ENTRY(ignore_sysret)
- CFI_STARTPROC
- mov $-ENOSYS,%eax
- sysret
- CFI_ENDPROC
-ENDPROC(ignore_sysret)
+END(call_softirq)
#ifdef CONFIG_XEN
-ENTRY(xen_hypervisor_callback)
- zeroentry xen_do_hypervisor_callback
-END(xen_hypervisor_callback)
+zeroentry xen_hypervisor_callback xen_do_hypervisor_callback
/*
-# A note on the "critical region" in our callback handler.
-# We want to avoid stacking callback handlers due to events occurring
-# during handling of the last event. To do this, we keep events disabled
-# until we've done all processing. HOWEVER, we must enable events before
-# popping the stack frame (can't be done atomically) and so it would still
-# be possible to get enough handler activations to overflow the stack.
-# Although unlikely, bugs of that kind are hard to track down, so we'd
-# like to avoid the possibility.
-# So, on entry to the handler we detect whether we interrupted an
-# existing activation in its critical region -- if so, we pop the current
-# activation and restart the handler using the previous one.
-*/
+ * A note on the "critical region" in our callback handler.
+ * We want to avoid stacking callback handlers due to events occurring
+ * during handling of the last event. To do this, we keep events disabled
+ * until we've done all processing. HOWEVER, we must enable events before
+ * popping the stack frame (can't be done atomically) and so it would still
+ * be possible to get enough handler activations to overflow the stack.
+ * Although unlikely, bugs of that kind are hard to track down, so we'd
+ * like to avoid the possibility.
+ * So, on entry to the handler we detect whether we interrupted an
+ * existing activation in its critical region -- if so, we pop the current
+ * activation and restart the handler using the previous one.
+ */
ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs)
CFI_STARTPROC
-/* Since we don't modify %rdi, evtchn_do_upall(struct *pt_regs) will
- see the correct pointer to the pt_regs */
+/*
+ * Since we don't modify %rdi, evtchn_do_upall(struct *pt_regs) will
+ * see the correct pointer to the pt_regs
+ */
movq %rdi, %rsp # we don't return, adjust the stack frame
CFI_ENDPROC
- CFI_DEFAULT_STACK
+ DEFAULT_FRAME
11: incl %gs:pda_irqcount
movq %rsp,%rbp
CFI_DEF_CFA_REGISTER rbp
@@ -1475,23 +1310,26 @@ ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs)
END(do_hypervisor_callback)
/*
-# Hypervisor uses this for application faults while it executes.
-# We get here for two reasons:
-# 1. Fault while reloading DS, ES, FS or GS
-# 2. Fault while executing IRET
-# Category 1 we do not need to fix up as Xen has already reloaded all segment
-# registers that could be reloaded and zeroed the others.
-# Category 2 we fix up by killing the current process. We cannot use the
-# normal Linux return path in this case because if we use the IRET hypercall
-# to pop the stack frame we end up in an infinite loop of failsafe callbacks.
-# We distinguish between categories by comparing each saved segment register
-# with its current contents: any discrepancy means we in category 1.
-*/
+ * Hypervisor uses this for application faults while it executes.
+ * We get here for two reasons:
+ * 1. Fault while reloading DS, ES, FS or GS
+ * 2. Fault while executing IRET
+ * Category 1 we do not need to fix up as Xen has already reloaded all segment
+ * registers that could be reloaded and zeroed the others.
+ * Category 2 we fix up by killing the current process. We cannot use the
+ * normal Linux return path in this case because if we use the IRET hypercall
+ * to pop the stack frame we end up in an infinite loop of failsafe callbacks.
+ * We distinguish between categories by comparing each saved segment register
+ * with its current contents: any discrepancy means we in category 1.
+ */
ENTRY(xen_failsafe_callback)
- framesz = (RIP-0x30) /* workaround buggy gas */
- _frame framesz
- CFI_REL_OFFSET rcx, 0
- CFI_REL_OFFSET r11, 8
+ INTR_FRAME 1 (6*8)
+ /*CFI_REL_OFFSET gs,GS*/
+ /*CFI_REL_OFFSET fs,FS*/
+ /*CFI_REL_OFFSET es,ES*/
+ /*CFI_REL_OFFSET ds,DS*/
+ CFI_REL_OFFSET r11,8
+ CFI_REL_OFFSET rcx,0
movw %ds,%cx
cmpw %cx,0x10(%rsp)
CFI_REMEMBER_STATE
@@ -1512,12 +1350,9 @@ ENTRY(xen_failsafe_callback)
CFI_RESTORE r11
addq $0x30,%rsp
CFI_ADJUST_CFA_OFFSET -0x30
- pushq $0
- CFI_ADJUST_CFA_OFFSET 8
- pushq %r11
- CFI_ADJUST_CFA_OFFSET 8
- pushq %rcx
- CFI_ADJUST_CFA_OFFSET 8
+ pushq_cfi $0 /* RIP */
+ pushq_cfi %r11
+ pushq_cfi %rcx
jmp general_protection
CFI_RESTORE_STATE
1: /* Segment mismatch => Category 1 (Bad segment). Retry the IRET. */
@@ -1527,11 +1362,223 @@ ENTRY(xen_failsafe_callback)
CFI_RESTORE r11
addq $0x30,%rsp
CFI_ADJUST_CFA_OFFSET -0x30
- pushq $0
- CFI_ADJUST_CFA_OFFSET 8
+ pushq_cfi $0
SAVE_ALL
jmp error_exit
CFI_ENDPROC
END(xen_failsafe_callback)
#endif /* CONFIG_XEN */
+
+/*
+ * Some functions should be protected against kprobes
+ */
+ .pushsection .kprobes.text, "ax"
+
+paranoidzeroentry_ist debug do_debug DEBUG_STACK
+paranoidzeroentry_ist int3 do_int3 DEBUG_STACK
+paranoiderrorentry stack_segment do_stack_segment
+errorentry general_protection do_general_protection
+errorentry page_fault do_page_fault
+#ifdef CONFIG_X86_MCE
+paranoidzeroentry machine_check do_machine_check
+#endif
+
+ /*
+ * "Paranoid" exit path from exception stack.
+ * Paranoid because this is used by NMIs and cannot take
+ * any kernel state for granted.
+ * We don't do kernel preemption checks here, because only
+ * NMI should be common and it does not enable IRQs and
+ * cannot get reschedule ticks.
+ *
+ * "trace" is 0 for the NMI handler only, because irq-tracing
+ * is fundamentally NMI-unsafe. (we cannot change the soft and
+ * hard flags at once, atomically)
+ */
+
+ /* ebx: no swapgs flag */
+ENTRY(paranoid_exit)
+ INTR_FRAME
+ DISABLE_INTERRUPTS(CLBR_NONE)
+ TRACE_IRQS_OFF
+ testl %ebx,%ebx /* swapgs needed? */
+ jnz paranoid_restore
+ testl $3,CS(%rsp)
+ jnz paranoid_userspace
+paranoid_swapgs:
+ TRACE_IRQS_IRETQ 0
+ SWAPGS_UNSAFE_STACK
+paranoid_restore:
+ RESTORE_ALL 8
+ jmp irq_return
+paranoid_userspace:
+ GET_THREAD_INFO(%rcx)
+ movl TI_flags(%rcx),%ebx
+ andl $_TIF_WORK_MASK,%ebx
+ jz paranoid_swapgs
+ movq %rsp,%rdi /* &pt_regs */
+ call sync_regs
+ movq %rax,%rsp /* switch stack for scheduling */
+ testl $_TIF_NEED_RESCHED,%ebx
+ jnz paranoid_schedule
+ movl %ebx,%edx /* arg3: thread flags */
+ TRACE_IRQS_ON
+ ENABLE_INTERRUPTS(CLBR_NONE)
+ xorl %esi,%esi /* arg2: oldset */
+ movq %rsp,%rdi /* arg1: &pt_regs */
+ call do_notify_resume
+ DISABLE_INTERRUPTS(CLBR_NONE)
+ TRACE_IRQS_OFF
+ jmp paranoid_userspace
+paranoid_schedule:
+ TRACE_IRQS_ON
+ ENABLE_INTERRUPTS(CLBR_ANY)
+ call schedule
+ DISABLE_INTERRUPTS(CLBR_ANY)
+ TRACE_IRQS_OFF
+ jmp paranoid_userspace
+ CFI_ENDPROC
+END(paranoid_exit)
+
+/*
+ * Exception entry point. This expects an error code/orig_rax on the stack.
+ * returns in "no swapgs flag" in %ebx.
+ */
+ENTRY(error_entry)
+ XCPT_FRAME
+ CFI_ADJUST_CFA_OFFSET 15*8
+ /* oldrax contains error code */
+ cld
+ movq_cfi rdi, RDI+8
+ movq_cfi rsi, RSI+8
+ movq_cfi rdx, RDX+8
+ movq_cfi rcx, RCX+8
+ movq_cfi rax, RAX+8
+ movq_cfi r8, R8+8
+ movq_cfi r9, R9+8
+ movq_cfi r10, R10+8
+ movq_cfi r11, R11+8
+ movq_cfi rbx, RBX+8
+ movq_cfi rbp, RBP+8
+ movq_cfi r12, R12+8
+ movq_cfi r13, R13+8
+ movq_cfi r14, R14+8
+ movq_cfi r15, R15+8
+ xorl %ebx,%ebx
+ testl $3,CS+8(%rsp)
+ je error_kernelspace
+error_swapgs:
+ SWAPGS
+error_sti:
+ TRACE_IRQS_OFF
+ ret
+ CFI_ENDPROC
+
+/*
+ * There are two places in the kernel that can potentially fault with
+ * usergs. Handle them here. The exception handlers after iret run with
+ * kernel gs again, so don't set the user space flag. B stepping K8s
+ * sometimes report an truncated RIP for IRET exceptions returning to
+ * compat mode. Check for these here too.
+ */
+error_kernelspace:
+ incl %ebx
+ leaq irq_return(%rip),%rcx
+ cmpq %rcx,RIP+8(%rsp)
+ je error_swapgs
+ movl %ecx,%ecx /* zero extend */
+ cmpq %rcx,RIP+8(%rsp)
+ je error_swapgs
+ cmpq $gs_change,RIP+8(%rsp)
+ je error_swapgs
+ jmp error_sti
+END(error_entry)
+
+
+/* ebx: no swapgs flag (1: don't need swapgs, 0: need it) */
+ENTRY(error_exit)
+ DEFAULT_FRAME
+ movl %ebx,%eax
+ RESTORE_REST
+ DISABLE_INTERRUPTS(CLBR_NONE)
+ TRACE_IRQS_OFF
+ GET_THREAD_INFO(%rcx)
+ testl %eax,%eax
+ jne retint_kernel
+ LOCKDEP_SYS_EXIT_IRQ
+ movl TI_flags(%rcx),%edx
+ movl $_TIF_WORK_MASK,%edi
+ andl %edi,%edx
+ jnz retint_careful
+ jmp retint_swapgs
+ CFI_ENDPROC
+END(error_exit)
+
+
+ /* runs on exception stack */
+ENTRY(nmi)
+ INTR_FRAME
+ PARAVIRT_ADJUST_EXCEPTION_FRAME
+ pushq_cfi $-1
+ subq $15*8, %rsp
+ CFI_ADJUST_CFA_OFFSET 15*8
+ call save_paranoid
+ DEFAULT_FRAME 0
+ /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
+ movq %rsp,%rdi
+ movq $-1,%rsi
+ call do_nmi
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* paranoidexit; without TRACE_IRQS_OFF */
+ /* ebx: no swapgs flag */
+ DISABLE_INTERRUPTS(CLBR_NONE)
+ testl %ebx,%ebx /* swapgs needed? */
+ jnz nmi_restore
+ testl $3,CS(%rsp)
+ jnz nmi_userspace
+nmi_swapgs:
+ SWAPGS_UNSAFE_STACK
+nmi_restore:
+ RESTORE_ALL 8
+ jmp irq_return
+nmi_userspace:
+ GET_THREAD_INFO(%rcx)
+ movl TI_flags(%rcx),%ebx
+ andl $_TIF_WORK_MASK,%ebx
+ jz nmi_swapgs
+ movq %rsp,%rdi /* &pt_regs */
+ call sync_regs
+ movq %rax,%rsp /* switch stack for scheduling */
+ testl $_TIF_NEED_RESCHED,%ebx
+ jnz nmi_schedule
+ movl %ebx,%edx /* arg3: thread flags */
+ ENABLE_INTERRUPTS(CLBR_NONE)
+ xorl %esi,%esi /* arg2: oldset */
+ movq %rsp,%rdi /* arg1: &pt_regs */
+ call do_notify_resume
+ DISABLE_INTERRUPTS(CLBR_NONE)
+ jmp nmi_userspace
+nmi_schedule:
+ ENABLE_INTERRUPTS(CLBR_ANY)
+ call schedule
+ DISABLE_INTERRUPTS(CLBR_ANY)
+ jmp nmi_userspace
+ CFI_ENDPROC
+#else
+ jmp paranoid_exit
+ CFI_ENDPROC
+#endif
+END(nmi)
+
+ENTRY(ignore_sysret)
+ CFI_STARTPROC
+ mov $-ENOSYS,%eax
+ sysret
+ CFI_ENDPROC
+END(ignore_sysret)
+
+/*
+ * End of kprobes section
+ */
+ .popsection
diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c
index 0e88be11227d..b193e082f6ce 100644
--- a/arch/x86/kernel/genx2apic_uv_x.c
+++ b/arch/x86/kernel/genx2apic_uv_x.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/threads.h>
+#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -17,6 +18,9 @@
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/hardirq.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <asm/current.h>
#include <asm/smp.h>
#include <asm/ipi.h>
#include <asm/genapic.h>
@@ -383,6 +387,103 @@ static __init void uv_rtc_init(void)
}
/*
+ * percpu heartbeat timer
+ */
+static void uv_heartbeat(unsigned long ignored)
+{
+ struct timer_list *timer = &uv_hub_info->scir.timer;
+ unsigned char bits = uv_hub_info->scir.state;
+
+ /* flip heartbeat bit */
+ bits ^= SCIR_CPU_HEARTBEAT;
+
+ /* is this cpu idle? */
+ if (idle_cpu(raw_smp_processor_id()))
+ bits &= ~SCIR_CPU_ACTIVITY;
+ else
+ bits |= SCIR_CPU_ACTIVITY;
+
+ /* update system controller interface reg */
+ uv_set_scir_bits(bits);
+
+ /* enable next timer period */
+ mod_timer(timer, jiffies + SCIR_CPU_HB_INTERVAL);
+}
+
+static void __cpuinit uv_heartbeat_enable(int cpu)
+{
+ if (!uv_cpu_hub_info(cpu)->scir.enabled) {
+ struct timer_list *timer = &uv_cpu_hub_info(cpu)->scir.timer;
+
+ uv_set_cpu_scir_bits(cpu, SCIR_CPU_HEARTBEAT|SCIR_CPU_ACTIVITY);
+ setup_timer(timer, uv_heartbeat, cpu);
+ timer->expires = jiffies + SCIR_CPU_HB_INTERVAL;
+ add_timer_on(timer, cpu);
+ uv_cpu_hub_info(cpu)->scir.enabled = 1;
+ }
+
+ /* check boot cpu */
+ if (!uv_cpu_hub_info(0)->scir.enabled)
+ uv_heartbeat_enable(0);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static void __cpuinit uv_heartbeat_disable(int cpu)
+{
+ if (uv_cpu_hub_info(cpu)->scir.enabled) {
+ uv_cpu_hub_info(cpu)->scir.enabled = 0;
+ del_timer(&uv_cpu_hub_info(cpu)->scir.timer);
+ }
+ uv_set_cpu_scir_bits(cpu, 0xff);
+}
+
+/*
+ * cpu hotplug notifier
+ */
+static __cpuinit int uv_scir_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ long cpu = (long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ uv_heartbeat_enable(cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ uv_heartbeat_disable(cpu);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static __init void uv_scir_register_cpu_notifier(void)
+{
+ hotcpu_notifier(uv_scir_cpu_notify, 0);
+}
+
+#else /* !CONFIG_HOTPLUG_CPU */
+
+static __init void uv_scir_register_cpu_notifier(void)
+{
+}
+
+static __init int uv_init_heartbeat(void)
+{
+ int cpu;
+
+ if (is_uv_system())
+ for_each_online_cpu(cpu)
+ uv_heartbeat_enable(cpu);
+ return 0;
+}
+
+late_initcall(uv_init_heartbeat);
+
+#endif /* !CONFIG_HOTPLUG_CPU */
+
+/*
* Called on each cpu to initialize the per_cpu UV data area.
* ZZZ hotplug not supported yet
*/
@@ -455,7 +556,7 @@ void __init uv_system_init(void)
uv_bios_init();
uv_bios_get_sn_info(0, &uv_type, &sn_partition_id,
- &uv_coherency_id, &uv_region_size);
+ &sn_coherency_id, &sn_region_size);
uv_rtc_init();
for_each_present_cpu(cpu) {
@@ -466,8 +567,7 @@ void __init uv_system_init(void)
uv_blade_info[blade].nr_possible_cpus++;
uv_cpu_hub_info(cpu)->lowmem_remap_base = lowmem_redir_base;
- uv_cpu_hub_info(cpu)->lowmem_remap_top =
- lowmem_redir_base + lowmem_redir_size;
+ uv_cpu_hub_info(cpu)->lowmem_remap_top = lowmem_redir_size;
uv_cpu_hub_info(cpu)->m_val = m_val;
uv_cpu_hub_info(cpu)->n_val = m_val;
uv_cpu_hub_info(cpu)->numa_blade_id = blade;
@@ -477,7 +577,8 @@ void __init uv_system_init(void)
uv_cpu_hub_info(cpu)->gpa_mask = (1 << (m_val + n_val)) - 1;
uv_cpu_hub_info(cpu)->gnode_upper = gnode_upper;
uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base;
- uv_cpu_hub_info(cpu)->coherency_domain_number = uv_coherency_id;
+ uv_cpu_hub_info(cpu)->coherency_domain_number = sn_coherency_id;
+ uv_cpu_hub_info(cpu)->scir.offset = SCIR_LOCAL_MMR_BASE + lcpu;
uv_node_to_blade[nid] = blade;
uv_cpu_to_blade[cpu] = blade;
max_pnode = max(pnode, max_pnode);
@@ -494,4 +595,6 @@ void __init uv_system_init(void)
map_mmioh_high(max_pnode);
uv_cpu_init();
+ uv_scir_register_cpu_notifier();
+ proc_mkdir("sgi_uv", NULL);
}
diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c
index 1dcb0f13897e..3e66bd364a9d 100644
--- a/arch/x86/kernel/head.c
+++ b/arch/x86/kernel/head.c
@@ -35,7 +35,6 @@ void __init reserve_ebda_region(void)
/* start of EBDA area */
ebda_addr = get_bios_ebda();
- printk(KERN_INFO "BIOS EBDA/lowmem at: %08x/%08x\n", ebda_addr, lowmem);
/* Fixup: bios puts an EBDA in the top 64K segment */
/* of conventional memory, but does not adjust lowmem. */
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index fa1d25dd83e3..ac108d1fe182 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -12,9 +12,12 @@
#include <asm/sections.h>
#include <asm/e820.h>
#include <asm/bios_ebda.h>
+#include <asm/trampoline.h>
void __init i386_start_kernel(void)
{
+ reserve_trampoline_memory();
+
reserve_early(__pa_symbol(&_text), __pa_symbol(&_end), "TEXT DATA BSS");
#ifdef CONFIG_BLK_DEV_INITRD
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index d16084f90649..388e05a5fc17 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -24,6 +24,7 @@
#include <asm/kdebug.h>
#include <asm/e820.h>
#include <asm/bios_ebda.h>
+#include <asm/trampoline.h>
/* boot cpu pda */
static struct x8664_pda _boot_cpu_pda __read_mostly;
@@ -120,6 +121,8 @@ void __init x86_64_start_reservations(char *real_mode_data)
{
copy_bootdata(__va(real_mode_data));
+ reserve_trampoline_memory();
+
reserve_early(__pa_symbol(&_text), __pa_symbol(&_end), "TEXT DATA BSS");
#ifdef CONFIG_BLK_DEV_INITRD
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index e76d7e272974..cd759ad90690 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -33,7 +33,9 @@
* HPET address is set in acpi/boot.c, when an ACPI entry exists
*/
unsigned long hpet_address;
-unsigned long hpet_num_timers;
+#ifdef CONFIG_PCI_MSI
+static unsigned long hpet_num_timers;
+#endif
static void __iomem *hpet_virt_address;
struct hpet_dev {
@@ -811,7 +813,7 @@ int __init hpet_enable(void)
out_nohpet:
hpet_clear_mapping();
- boot_hpet_disable = 1;
+ hpet_address = 0;
return 0;
}
@@ -834,10 +836,11 @@ static __init int hpet_late_init(void)
hpet_address = force_hpet_address;
hpet_enable();
- if (!hpet_virt_address)
- return -ENODEV;
}
+ if (!hpet_virt_address)
+ return -ENODEV;
+
hpet_reserve_platform_timers(hpet_readl(HPET_ID));
for_each_online_cpu(cpu) {
diff --git a/arch/x86/kernel/init_task.c b/arch/x86/kernel/init_task.c
index a4f93b4120c1..d39918076bb4 100644
--- a/arch/x86/kernel/init_task.c
+++ b/arch/x86/kernel/init_task.c
@@ -14,7 +14,6 @@ static struct fs_struct init_fs = INIT_FS;
static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
struct mm_struct init_mm = INIT_MM(init_mm);
-EXPORT_UNUSED_SYMBOL(init_mm); /* will be removed in 2.6.26 */
/*
* Initial thread structure.
diff --git a/arch/x86/kernel/io_apic.c b/arch/x86/kernel/io_apic.c
index 1cbf7c8d46e0..3e070bb961d7 100644
--- a/arch/x86/kernel/io_apic.c
+++ b/arch/x86/kernel/io_apic.c
@@ -2472,10 +2472,9 @@ static void set_ir_ioapic_affinity_irq(unsigned int irq,
asmlinkage void smp_irq_move_cleanup_interrupt(void)
{
unsigned vector, me;
+
ack_APIC_irq();
-#ifdef CONFIG_X86_64
exit_idle();
-#endif
irq_enter();
me = smp_processor_id();
@@ -2520,7 +2519,7 @@ static void irq_complete_move(struct irq_desc **descp)
if (likely(!cfg->move_desc_pending))
return;
- /* domain is not change, but affinity is changed */
+ /* domain has not changed, but affinity did */
me = smp_processor_id();
if (cpu_isset(me, desc->affinity)) {
*descp = desc = move_irq_desc(desc, me);
diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c
index fca2991443f5..6383d50f82ea 100644
--- a/arch/x86/kernel/irq_64.c
+++ b/arch/x86/kernel/irq_64.c
@@ -13,12 +13,12 @@
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/ftrace.h>
#include <asm/uaccess.h>
#include <asm/io_apic.h>
#include <asm/idle.h>
#include <asm/smp.h>
-#ifdef CONFIG_DEBUG_STACKOVERFLOW
/*
* Probabilistic stack overflow check:
*
@@ -28,26 +28,25 @@
*/
static inline void stack_overflow_check(struct pt_regs *regs)
{
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
u64 curbase = (u64)task_stack_page(current);
- static unsigned long warned = -60*HZ;
-
- if (regs->sp >= curbase && regs->sp <= curbase + THREAD_SIZE &&
- regs->sp < curbase + sizeof(struct thread_info) + 128 &&
- time_after(jiffies, warned + 60*HZ)) {
- printk("do_IRQ: %s near stack overflow (cur:%Lx,sp:%lx)\n",
- current->comm, curbase, regs->sp);
- show_stack(NULL,NULL);
- warned = jiffies;
- }
-}
+
+ WARN_ONCE(regs->sp >= curbase &&
+ regs->sp <= curbase + THREAD_SIZE &&
+ regs->sp < curbase + sizeof(struct thread_info) +
+ sizeof(struct pt_regs) + 128,
+
+ "do_IRQ: %s near stack overflow (cur:%Lx,sp:%lx)\n",
+ current->comm, curbase, regs->sp);
#endif
+}
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
-asmlinkage unsigned int do_IRQ(struct pt_regs *regs)
+asmlinkage unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
struct irq_desc *desc;
@@ -60,9 +59,7 @@ asmlinkage unsigned int do_IRQ(struct pt_regs *regs)
irq_enter();
irq = __get_cpu_var(vector_irq)[vector];
-#ifdef CONFIG_DEBUG_STACKOVERFLOW
stack_overflow_check(regs);
-#endif
desc = irq_to_desc(irq);
if (likely(desc))
diff --git a/arch/x86/kernel/irqinit_32.c b/arch/x86/kernel/irqinit_32.c
index 61aa2a1004b5..84723295f88a 100644
--- a/arch/x86/kernel/irqinit_32.c
+++ b/arch/x86/kernel/irqinit_32.c
@@ -140,7 +140,7 @@ void __init native_init_IRQ(void)
for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
/* SYSCALL_VECTOR was reserved in trap_init. */
if (i != SYSCALL_VECTOR)
- set_intr_gate(i, interrupt[i]);
+ set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);
}
diff --git a/arch/x86/kernel/irqinit_64.c b/arch/x86/kernel/irqinit_64.c
index 1020919efe1c..31ebfe38e96c 100644
--- a/arch/x86/kernel/irqinit_64.c
+++ b/arch/x86/kernel/irqinit_64.c
@@ -24,41 +24,6 @@
#include <asm/i8259.h>
/*
- * Common place to define all x86 IRQ vectors
- *
- * This builds up the IRQ handler stubs using some ugly macros in irq.h
- *
- * These macros create the low-level assembly IRQ routines that save
- * register context and call do_IRQ(). do_IRQ() then does all the
- * operations that are needed to keep the AT (or SMP IOAPIC)
- * interrupt-controller happy.
- */
-
-#define IRQ_NAME2(nr) nr##_interrupt(void)
-#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
-
-/*
- * SMP has a few special interrupts for IPI messages
- */
-
-#define BUILD_IRQ(nr) \
- asmlinkage void IRQ_NAME(nr); \
- asm("\n.text\n.p2align\n" \
- "IRQ" #nr "_interrupt:\n\t" \
- "push $~(" #nr ") ; " \
- "jmp common_interrupt\n" \
- ".previous");
-
-#define BI(x,y) \
- BUILD_IRQ(x##y)
-
-#define BUILD_16_IRQS(x) \
- BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
- BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
- BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
- BI(x,c) BI(x,d) BI(x,e) BI(x,f)
-
-/*
* ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
* (these are usually mapped to vectors 0x30-0x3f)
*/
@@ -73,37 +38,6 @@
*
* (these are usually mapped into the 0x30-0xff vector range)
*/
- BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
-BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
-BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
-BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)
-
-#undef BUILD_16_IRQS
-#undef BI
-
-
-#define IRQ(x,y) \
- IRQ##x##y##_interrupt
-
-#define IRQLIST_16(x) \
- IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
- IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
- IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
- IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
-
-/* for the irq vectors */
-static void (*__initdata interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
- IRQLIST_16(0x2), IRQLIST_16(0x3),
- IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
- IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
- IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
-};
-
-#undef IRQ
-#undef IRQLIST_16
-
-
-
/*
* IRQ2 is cascade interrupt to second interrupt controller
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 5f8e5d75a254..c25fdb382292 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -10,7 +10,7 @@
* This driver allows to upgrade microcode on AMD
* family 0x10 and 0x11 processors.
*
- * Licensed unter the terms of the GNU General Public
+ * Licensed under the terms of the GNU General Public
* License version 2. See file COPYING for details.
*/
@@ -32,9 +32,9 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
+#include <linux/uaccess.h>
#include <asm/msr.h>
-#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/microcode.h>
@@ -47,43 +47,38 @@ MODULE_LICENSE("GPL v2");
#define UCODE_UCODE_TYPE 0x00000001
struct equiv_cpu_entry {
- unsigned int installed_cpu;
- unsigned int fixed_errata_mask;
- unsigned int fixed_errata_compare;
- unsigned int equiv_cpu;
-};
+ u32 installed_cpu;
+ u32 fixed_errata_mask;
+ u32 fixed_errata_compare;
+ u16 equiv_cpu;
+ u16 res;
+} __attribute__((packed));
struct microcode_header_amd {
- unsigned int data_code;
- unsigned int patch_id;
- unsigned char mc_patch_data_id[2];
- unsigned char mc_patch_data_len;
- unsigned char init_flag;
- unsigned int mc_patch_data_checksum;
- unsigned int nb_dev_id;
- unsigned int sb_dev_id;
- unsigned char processor_rev_id[2];
- unsigned char nb_rev_id;
- unsigned char sb_rev_id;
- unsigned char bios_api_rev;
- unsigned char reserved1[3];
- unsigned int match_reg[8];
-};
+ u32 data_code;
+ u32 patch_id;
+ u16 mc_patch_data_id;
+ u8 mc_patch_data_len;
+ u8 init_flag;
+ u32 mc_patch_data_checksum;
+ u32 nb_dev_id;
+ u32 sb_dev_id;
+ u16 processor_rev_id;
+ u8 nb_rev_id;
+ u8 sb_rev_id;
+ u8 bios_api_rev;
+ u8 reserved1[3];
+ u32 match_reg[8];
+} __attribute__((packed));
struct microcode_amd {
struct microcode_header_amd hdr;
unsigned int mpb[0];
};
-#define UCODE_MAX_SIZE (2048)
-#define DEFAULT_UCODE_DATASIZE (896)
-#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd))
-#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
-#define DWSIZE (sizeof(u32))
-/* For now we support a fixed ucode total size only */
-#define get_totalsize(mc) \
- ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \
- + MC_HEADER_SIZE)
+#define UCODE_MAX_SIZE 2048
+#define UCODE_CONTAINER_SECTION_HDR 8
+#define UCODE_CONTAINER_HEADER_SIZE 12
/* serialize access to the physical write */
static DEFINE_SPINLOCK(microcode_update_lock);
@@ -93,31 +88,24 @@ static struct equiv_cpu_entry *equiv_cpu_table;
static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
+ u32 dummy;
memset(csig, 0, sizeof(*csig));
-
if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
- printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
- cpu);
+ printk(KERN_WARNING "microcode: CPU%d: AMD CPU family 0x%x not "
+ "supported\n", cpu, c->x86);
return -1;
}
-
- asm volatile("movl %1, %%ecx; rdmsr"
- : "=a" (csig->rev)
- : "i" (0x0000008B) : "ecx");
-
- printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n",
- csig->rev);
-
+ rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy);
+ printk(KERN_INFO "microcode: CPU%d: patch_level=0x%x\n", cpu, csig->rev);
return 0;
}
static int get_matching_microcode(int cpu, void *mc, int rev)
{
struct microcode_header_amd *mc_header = mc;
- struct pci_dev *nb_pci_dev, *sb_pci_dev;
unsigned int current_cpu_id;
- unsigned int equiv_cpu_id = 0x00;
+ u16 equiv_cpu_id = 0;
unsigned int i = 0;
BUG_ON(equiv_cpu_table == NULL);
@@ -132,57 +120,25 @@ static int get_matching_microcode(int cpu, void *mc, int rev)
}
if (!equiv_cpu_id) {
- printk(KERN_ERR "microcode: CPU%d cpu_id "
- "not found in equivalent cpu table \n", cpu);
+ printk(KERN_WARNING "microcode: CPU%d: cpu revision "
+ "not listed in equivalent cpu table\n", cpu);
return 0;
}
- if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) {
- printk(KERN_ERR
- "microcode: CPU%d patch does not match "
- "(patch is %x, cpu extended is %x) \n",
- cpu, mc_header->processor_rev_id[0],
- (equiv_cpu_id & 0xff));
+ if (mc_header->processor_rev_id != equiv_cpu_id) {
+ printk(KERN_ERR "microcode: CPU%d: patch mismatch "
+ "(processor_rev_id: %x, equiv_cpu_id: %x)\n",
+ cpu, mc_header->processor_rev_id, equiv_cpu_id);
return 0;
}
- if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) {
- printk(KERN_ERR "microcode: CPU%d patch does not match "
- "(patch is %x, cpu base id is %x) \n",
- cpu, mc_header->processor_rev_id[1],
- ((equiv_cpu_id >> 16) & 0xff));
-
+ /* ucode might be chipset specific -- currently we don't support this */
+ if (mc_header->nb_dev_id || mc_header->sb_dev_id) {
+ printk(KERN_ERR "microcode: CPU%d: loading of chipset "
+ "specific code not yet supported\n", cpu);
return 0;
}
- /* ucode may be northbridge specific */
- if (mc_header->nb_dev_id) {
- nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- (mc_header->nb_dev_id & 0xff),
- NULL);
- if ((!nb_pci_dev) ||
- (mc_header->nb_rev_id != nb_pci_dev->revision)) {
- printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu);
- pci_dev_put(nb_pci_dev);
- return 0;
- }
- pci_dev_put(nb_pci_dev);
- }
-
- /* ucode may be southbridge specific */
- if (mc_header->sb_dev_id) {
- sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
- (mc_header->sb_dev_id & 0xff),
- NULL);
- if ((!sb_pci_dev) ||
- (mc_header->sb_rev_id != sb_pci_dev->revision)) {
- printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu);
- pci_dev_put(sb_pci_dev);
- return 0;
- }
- pci_dev_put(sb_pci_dev);
- }
-
if (mc_header->patch_id <= rev)
return 0;
@@ -192,12 +148,10 @@ static int get_matching_microcode(int cpu, void *mc, int rev)
static void apply_microcode_amd(int cpu)
{
unsigned long flags;
- unsigned int eax, edx;
- unsigned int rev;
+ 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;
- unsigned long addr;
/* We should bind the task to the CPU */
BUG_ON(cpu_num != cpu);
@@ -206,42 +160,34 @@ static void apply_microcode_amd(int cpu)
return;
spin_lock_irqsave(&microcode_update_lock, flags);
-
- addr = (unsigned long)&mc_amd->hdr.data_code;
- edx = (unsigned int)(((unsigned long)upper_32_bits(addr)));
- eax = (unsigned int)(((unsigned long)lower_32_bits(addr)));
-
- asm volatile("movl %0, %%ecx; wrmsr" :
- : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx");
-
+ wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
/* get patch id after patching */
- asm volatile("movl %1, %%ecx; rdmsr"
- : "=a" (rev)
- : "i" (0x0000008B) : "ecx");
-
+ rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
spin_unlock_irqrestore(&microcode_update_lock, flags);
/* check current patch id and patch's id for match */
if (rev != mc_amd->hdr.patch_id) {
- printk(KERN_ERR "microcode: CPU%d update from revision "
- "0x%x to 0x%x failed\n", cpu_num,
- mc_amd->hdr.patch_id, rev);
+ printk(KERN_ERR "microcode: CPU%d: update failed "
+ "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id);
return;
}
- printk(KERN_INFO "microcode: CPU%d updated from revision "
- "0x%x to 0x%x \n",
- cpu_num, uci->cpu_sig.rev, mc_amd->hdr.patch_id);
+ printk(KERN_INFO "microcode: CPU%d: updated (new patch_level=0x%x)\n",
+ cpu, rev);
uci->cpu_sig.rev = rev;
}
-static void * get_next_ucode(u8 *buf, unsigned int size,
- int (*get_ucode_data)(void *, const void *, size_t),
- unsigned int *mc_size)
+static int get_ucode_data(void *to, const u8 *from, size_t n)
+{
+ memcpy(to, from, n);
+ return 0;
+}
+
+static void *get_next_ucode(const u8 *buf, unsigned int size,
+ unsigned int *mc_size)
{
unsigned int total_size;
-#define UCODE_CONTAINER_SECTION_HDR 8
u8 section_hdr[UCODE_CONTAINER_SECTION_HDR];
void *mc;
@@ -249,39 +195,37 @@ static void * get_next_ucode(u8 *buf, unsigned int size,
return NULL;
if (section_hdr[0] != UCODE_UCODE_TYPE) {
- printk(KERN_ERR "microcode: error! "
- "Wrong microcode payload type field\n");
+ printk(KERN_ERR "microcode: error: invalid type field in "
+ "container file section header\n");
return NULL;
}
total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8));
- printk(KERN_INFO "microcode: size %u, total_size %u\n",
- size, total_size);
+ printk(KERN_DEBUG "microcode: size %u, total_size %u\n",
+ size, total_size);
if (total_size > size || total_size > UCODE_MAX_SIZE) {
- printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
+ printk(KERN_ERR "microcode: error: size mismatch\n");
return NULL;
}
mc = vmalloc(UCODE_MAX_SIZE);
if (mc) {
memset(mc, 0, UCODE_MAX_SIZE);
- if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, total_size)) {
+ if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR,
+ total_size)) {
vfree(mc);
mc = NULL;
} else
*mc_size = total_size + UCODE_CONTAINER_SECTION_HDR;
}
-#undef UCODE_CONTAINER_SECTION_HDR
return mc;
}
-static int install_equiv_cpu_table(u8 *buf,
- int (*get_ucode_data)(void *, const void *, size_t))
+static int install_equiv_cpu_table(const u8 *buf)
{
-#define UCODE_CONTAINER_HEADER_SIZE 12
u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE];
unsigned int *buf_pos = (unsigned int *)container_hdr;
unsigned long size;
@@ -292,14 +236,15 @@ static int install_equiv_cpu_table(u8 *buf,
size = buf_pos[2];
if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
- printk(KERN_ERR "microcode: error! "
- "Wrong microcode equivalnet cpu table\n");
+ printk(KERN_ERR "microcode: error: invalid type field in "
+ "container file section header\n");
return 0;
}
equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
if (!equiv_cpu_table) {
- printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n");
+ printk(KERN_ERR "microcode: failed to allocate "
+ "equivalent CPU table\n");
return 0;
}
@@ -310,7 +255,6 @@ static int install_equiv_cpu_table(u8 *buf,
}
return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */
-#undef UCODE_CONTAINER_HEADER_SIZE
}
static void free_equiv_cpu_table(void)
@@ -321,18 +265,20 @@ static void free_equiv_cpu_table(void)
}
}
-static int generic_load_microcode(int cpu, void *data, size_t size,
- int (*get_ucode_data)(void *, const void *, size_t))
+static int generic_load_microcode(int cpu, const u8 *data, size_t size)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- u8 *ucode_ptr = data, *new_mc = NULL, *mc;
+ const u8 *ucode_ptr = data;
+ void *new_mc = NULL;
+ void *mc;
int new_rev = uci->cpu_sig.rev;
unsigned int leftover;
unsigned long offset;
- offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data);
+ offset = install_equiv_cpu_table(ucode_ptr);
if (!offset) {
- printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
+ printk(KERN_ERR "microcode: failed to create "
+ "equivalent cpu table\n");
return -EINVAL;
}
@@ -343,7 +289,7 @@ static int generic_load_microcode(int cpu, void *data, size_t size,
unsigned int uninitialized_var(mc_size);
struct microcode_header_amd *mc_header;
- mc = get_next_ucode(ucode_ptr, leftover, get_ucode_data, &mc_size);
+ mc = get_next_ucode(ucode_ptr, leftover, &mc_size);
if (!mc)
break;
@@ -353,7 +299,7 @@ static int generic_load_microcode(int cpu, void *data, size_t size,
vfree(new_mc);
new_rev = mc_header->patch_id;
new_mc = mc;
- } else
+ } else
vfree(mc);
ucode_ptr += mc_size;
@@ -365,9 +311,9 @@ static int generic_load_microcode(int cpu, void *data, size_t size,
if (uci->mc)
vfree(uci->mc);
uci->mc = new_mc;
- pr_debug("microcode: CPU%d found a matching microcode update with"
- " version 0x%x (current=0x%x)\n",
- cpu, new_rev, uci->cpu_sig.rev);
+ pr_debug("microcode: CPU%d found a matching microcode "
+ "update with version 0x%x (current=0x%x)\n",
+ cpu, new_rev, uci->cpu_sig.rev);
} else
vfree(new_mc);
}
@@ -377,12 +323,6 @@ static int generic_load_microcode(int cpu, void *data, size_t size,
return (int)leftover;
}
-static int get_ucode_fw(void *to, const void *from, size_t n)
-{
- memcpy(to, from, n);
- return 0;
-}
-
static int request_microcode_fw(int cpu, struct device *device)
{
const char *fw_name = "amd-ucode/microcode_amd.bin";
@@ -394,12 +334,11 @@ static int request_microcode_fw(int cpu, struct device *device)
ret = request_firmware(&firmware, fw_name, device);
if (ret) {
- printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name);
+ printk(KERN_ERR "microcode: failed to load file %s\n", fw_name);
return ret;
}
- ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
- &get_ucode_fw);
+ ret = generic_load_microcode(cpu, firmware->data, firmware->size);
release_firmware(firmware);
@@ -408,8 +347,8 @@ static int request_microcode_fw(int cpu, struct device *device)
static int request_microcode_user(int cpu, const void __user *buf, size_t size)
{
- printk(KERN_WARNING "microcode: AMD microcode update via /dev/cpu/microcode"
- "is not supported\n");
+ printk(KERN_INFO "microcode: AMD microcode update via "
+ "/dev/cpu/microcode not supported\n");
return -1;
}
@@ -433,3 +372,4 @@ struct microcode_ops * __init init_amd_microcode(void)
{
return &microcode_amd_ops;
}
+
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index 82fb2809ce32..c9b721ba968c 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -99,7 +99,7 @@ MODULE_LICENSE("GPL");
#define MICROCODE_VERSION "2.00"
-struct microcode_ops *microcode_ops;
+static struct microcode_ops *microcode_ops;
/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
static DEFINE_MUTEX(microcode_mutex);
@@ -203,7 +203,7 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
#endif
/* fake device for request_firmware */
-struct platform_device *microcode_pdev;
+static struct platform_device *microcode_pdev;
static ssize_t reload_store(struct sys_device *dev,
struct sysdev_attribute *attr,
@@ -272,13 +272,18 @@ static struct attribute_group mc_attr_group = {
.name = "microcode",
};
-static void microcode_fini_cpu(int cpu)
+static void __microcode_fini_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- mutex_lock(&microcode_mutex);
microcode_ops->microcode_fini_cpu(cpu);
uci->valid = 0;
+}
+
+static void microcode_fini_cpu(int cpu)
+{
+ mutex_lock(&microcode_mutex);
+ __microcode_fini_cpu(cpu);
mutex_unlock(&microcode_mutex);
}
@@ -306,12 +311,16 @@ static int microcode_resume_cpu(int cpu)
* to this cpu (a bit of paranoia):
*/
if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
- microcode_fini_cpu(cpu);
+ __microcode_fini_cpu(cpu);
+ printk(KERN_ERR "failed to collect_cpu_info for resuming cpu #%d\n",
+ cpu);
return -1;
}
- if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) {
- microcode_fini_cpu(cpu);
+ if ((nsig.sig != uci->cpu_sig.sig) || (nsig.pf != uci->cpu_sig.pf)) {
+ __microcode_fini_cpu(cpu);
+ printk(KERN_ERR "cached ucode doesn't match the resuming cpu #%d\n",
+ cpu);
/* Should we look for a new ucode here? */
return 1;
}
@@ -319,7 +328,7 @@ static int microcode_resume_cpu(int cpu)
return 0;
}
-void microcode_update_cpu(int cpu)
+static void microcode_update_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
int err = 0;
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
index 622dc4a21784..b7f4c929e615 100644
--- a/arch/x86/kernel/microcode_intel.c
+++ b/arch/x86/kernel/microcode_intel.c
@@ -155,6 +155,7 @@ static DEFINE_SPINLOCK(microcode_update_lock);
static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
{
struct cpuinfo_x86 *c = &cpu_data(cpu_num);
+ unsigned long flags;
unsigned int val[2];
memset(csig, 0, sizeof(*csig));
@@ -174,11 +175,16 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
csig->pf = 1 << ((val[1] >> 18) & 7);
}
+ /* serialize access to the physical write to MSR 0x79 */
+ spin_lock_irqsave(&microcode_update_lock, flags);
+
wrmsr(MSR_IA32_UCODE_REV, 0, 0);
/* see notes above for revision 1.07. Apparent chip bug */
sync_core();
/* get the current revision from MSR 0x8B */
rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev);
+ spin_unlock_irqrestore(&microcode_update_lock, flags);
+
pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
csig->sig, csig->pf, csig->rev);
@@ -465,7 +471,7 @@ static void microcode_fini_cpu(int cpu)
uci->mc = NULL;
}
-struct microcode_ops microcode_intel_ops = {
+static struct microcode_ops microcode_intel_ops = {
.request_microcode_user = request_microcode_user,
.request_microcode_fw = request_microcode_fw,
.collect_cpu_info = collect_cpu_info,
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 13316cf57cdb..8bd1bf9622a7 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -208,12 +208,17 @@ static int __init setup_nmi_watchdog(char *str)
++str;
}
- get_option(&str, &nmi);
-
- if (nmi >= NMI_INVALID)
- return 0;
+ if (!strncmp(str, "lapic", 5))
+ nmi_watchdog = NMI_LOCAL_APIC;
+ else if (!strncmp(str, "ioapic", 6))
+ nmi_watchdog = NMI_IO_APIC;
+ else {
+ get_option(&str, &nmi);
+ if (nmi >= NMI_INVALID)
+ return 0;
+ nmi_watchdog = nmi;
+ }
- nmi_watchdog = nmi;
return 1;
}
__setup("nmi_watchdog=", setup_nmi_watchdog);
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index 192624820217..19a1044a0cd9 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -6,6 +6,7 @@
#include <asm/proto.h>
#include <asm/dma.h>
#include <asm/iommu.h>
+#include <asm/gart.h>
#include <asm/calgary.h>
#include <asm/amd_iommu.h>
@@ -30,11 +31,6 @@ int no_iommu __read_mostly;
/* Set this to 1 if there is a HW IOMMU in the system */
int iommu_detected __read_mostly = 0;
-/* This tells the BIO block layer to assume merging. Default to off
- because we cannot guarantee merging later. */
-int iommu_bio_merge __read_mostly = 0;
-EXPORT_SYMBOL(iommu_bio_merge);
-
dma_addr_t bad_dma_address __read_mostly = 0;
EXPORT_SYMBOL(bad_dma_address);
@@ -105,11 +101,15 @@ static void __init dma32_free_bootmem(void)
dma32_bootmem_ptr = NULL;
dma32_bootmem_size = 0;
}
+#endif
void __init pci_iommu_alloc(void)
{
+#ifdef CONFIG_X86_64
/* free the range so iommu could get some range less than 4G */
dma32_free_bootmem();
+#endif
+
/*
* The order of these functions is important for
* fall-back/fail-over reasons
@@ -125,15 +125,6 @@ void __init pci_iommu_alloc(void)
pci_swiotlb_init();
}
-unsigned long iommu_nr_pages(unsigned long addr, unsigned long len)
-{
- unsigned long size = roundup((addr & ~PAGE_MASK) + len, PAGE_SIZE);
-
- return size >> PAGE_SHIFT;
-}
-EXPORT_SYMBOL(iommu_nr_pages);
-#endif
-
void *dma_generic_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_addr, gfp_t flag)
{
@@ -188,7 +179,6 @@ static __init int iommu_setup(char *p)
}
if (!strncmp(p, "biomerge", 8)) {
- iommu_bio_merge = 4096;
iommu_merge = 1;
force_iommu = 1;
}
@@ -300,8 +290,8 @@ fs_initcall(pci_iommu_init);
static __devinit void via_no_dac(struct pci_dev *dev)
{
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) {
- printk(KERN_INFO "PCI: VIA PCI bridge detected."
- "Disabling DAC.\n");
+ printk(KERN_INFO
+ "PCI: VIA PCI bridge detected. Disabling DAC.\n");
forbid_dac = 1;
}
}
diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c
index ba7ad83e20a8..a35eaa379ff6 100644
--- a/arch/x86/kernel/pci-gart_64.c
+++ b/arch/x86/kernel/pci-gart_64.c
@@ -745,10 +745,8 @@ void __init gart_iommu_init(void)
unsigned long scratch;
long i;
- if (cache_k8_northbridges() < 0 || num_k8_northbridges == 0) {
- printk(KERN_INFO "PCI-GART: No AMD GART found.\n");
+ if (cache_k8_northbridges() < 0 || num_k8_northbridges == 0)
return;
- }
#ifndef CONFIG_AGP_AMD64
no_agp = 1;
diff --git a/arch/x86/kernel/pci-swiotlb_64.c b/arch/x86/kernel/pci-swiotlb_64.c
index 3c539d111abb..242c3440687f 100644
--- a/arch/x86/kernel/pci-swiotlb_64.c
+++ b/arch/x86/kernel/pci-swiotlb_64.c
@@ -3,6 +3,8 @@
#include <linux/pci.h>
#include <linux/cache.h>
#include <linux/module.h>
+#include <linux/swiotlb.h>
+#include <linux/bootmem.h>
#include <linux/dma-mapping.h>
#include <asm/iommu.h>
@@ -11,6 +13,31 @@
int swiotlb __read_mostly;
+void *swiotlb_alloc_boot(size_t size, unsigned long nslabs)
+{
+ return alloc_bootmem_low_pages(size);
+}
+
+void *swiotlb_alloc(unsigned order, unsigned long nslabs)
+{
+ return (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, order);
+}
+
+dma_addr_t swiotlb_phys_to_bus(phys_addr_t paddr)
+{
+ return paddr;
+}
+
+phys_addr_t swiotlb_bus_to_phys(dma_addr_t baddr)
+{
+ return baddr;
+}
+
+int __weak swiotlb_arch_range_needs_mapping(void *ptr, size_t size)
+{
+ return 0;
+}
+
static dma_addr_t
swiotlb_map_single_phys(struct device *hwdev, phys_addr_t paddr, size_t size,
int direction)
@@ -50,8 +77,10 @@ struct dma_mapping_ops swiotlb_dma_ops = {
void __init pci_swiotlb_init(void)
{
/* don't initialize swiotlb if iommu=off (no_iommu=1) */
+#ifdef CONFIG_X86_64
if (!iommu_detected && !no_iommu && max_pfn > MAX_DMA32_PFN)
swiotlb = 1;
+#endif
if (swiotlb_force)
swiotlb = 1;
if (swiotlb) {
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 95d811a9594f..e68bb9e30864 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -1,6 +1,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <asm/idle.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/sched.h>
@@ -302,7 +303,7 @@ static void c1e_idle(void)
rdmsr(MSR_K8_INT_PENDING_MSG, lo, hi);
if (lo & K8_INTP_C1E_ACTIVE_MASK) {
c1e_detected = 1;
- if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
+ if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
mark_tsc_unstable("TSC halt in AMD C1E");
printk(KERN_INFO "System has AMD C1E enabled\n");
set_cpu_cap(&boot_cpu_data, X86_FEATURE_AMDC1E);
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 24c2276aa453..3ba155d24884 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -60,6 +60,7 @@
#include <asm/idle.h>
#include <asm/syscalls.h>
#include <asm/smp.h>
+#include <asm/ds.h>
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
@@ -251,14 +252,8 @@ void exit_thread(void)
tss->x86_tss.io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
put_cpu();
}
-#ifdef CONFIG_X86_DS
- /* Free any DS contexts that have not been properly released. */
- if (unlikely(current->thread.ds_ctx)) {
- /* we clear debugctl to make sure DS is not used. */
- update_debugctlmsr(0);
- ds_free(current->thread.ds_ctx);
- }
-#endif /* CONFIG_X86_DS */
+
+ ds_exit_thread(current);
}
void flush_thread(void)
@@ -340,6 +335,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
kfree(p->thread.io_bitmap_ptr);
p->thread.io_bitmap_max = 0;
}
+
+ ds_copy_thread(p, current);
+
+ clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
+ p->thread.debugctlmsr = 0;
+
return err;
}
@@ -420,48 +421,19 @@ int set_tsc_mode(unsigned int val)
return 0;
}
-#ifdef CONFIG_X86_DS
-static int update_debugctl(struct thread_struct *prev,
- struct thread_struct *next, unsigned long debugctl)
-{
- unsigned long ds_prev = 0;
- unsigned long ds_next = 0;
-
- if (prev->ds_ctx)
- ds_prev = (unsigned long)prev->ds_ctx->ds;
- if (next->ds_ctx)
- ds_next = (unsigned long)next->ds_ctx->ds;
-
- if (ds_next != ds_prev) {
- /* we clear debugctl to make sure DS
- * is not in use when we change it */
- debugctl = 0;
- update_debugctlmsr(0);
- wrmsr(MSR_IA32_DS_AREA, ds_next, 0);
- }
- return debugctl;
-}
-#else
-static int update_debugctl(struct thread_struct *prev,
- struct thread_struct *next, unsigned long debugctl)
-{
- return debugctl;
-}
-#endif /* CONFIG_X86_DS */
-
static noinline void
__switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
struct tss_struct *tss)
{
struct thread_struct *prev, *next;
- unsigned long debugctl;
prev = &prev_p->thread;
next = &next_p->thread;
- debugctl = update_debugctl(prev, next, prev->debugctlmsr);
-
- if (next->debugctlmsr != debugctl)
+ if (test_tsk_thread_flag(next_p, TIF_DS_AREA_MSR) ||
+ test_tsk_thread_flag(prev_p, TIF_DS_AREA_MSR))
+ ds_switch_to(prev_p, next_p);
+ else if (next->debugctlmsr != prev->debugctlmsr)
update_debugctlmsr(next->debugctlmsr);
if (test_tsk_thread_flag(next_p, TIF_DEBUG)) {
@@ -483,15 +455,6 @@ __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
hard_enable_TSC();
}
-#ifdef CONFIG_X86_PTRACE_BTS
- if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS))
- ptrace_bts_take_timestamp(prev_p, BTS_TASK_DEPARTS);
-
- if (test_tsk_thread_flag(next_p, TIF_BTS_TRACE_TS))
- ptrace_bts_take_timestamp(next_p, BTS_TASK_ARRIVES);
-#endif /* CONFIG_X86_PTRACE_BTS */
-
-
if (!test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) {
/*
* Disable the bitmap via an invalid offset. We still cache
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index fbb321d53d34..416fb9282f4f 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -53,6 +53,7 @@
#include <asm/ia32.h>
#include <asm/idle.h>
#include <asm/syscalls.h>
+#include <asm/ds.h>
asmlinkage extern void ret_from_fork(void);
@@ -236,14 +237,8 @@ void exit_thread(void)
t->io_bitmap_max = 0;
put_cpu();
}
-#ifdef CONFIG_X86_DS
- /* Free any DS contexts that have not been properly released. */
- if (unlikely(t->ds_ctx)) {
- /* we clear debugctl to make sure DS is not used. */
- update_debugctlmsr(0);
- ds_free(t->ds_ctx);
- }
-#endif /* CONFIG_X86_DS */
+
+ ds_exit_thread(current);
}
void flush_thread(void)
@@ -373,6 +368,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
if (err)
goto out;
}
+
+ ds_copy_thread(p, me);
+
+ clear_tsk_thread_flag(p, TIF_DEBUGCTLMSR);
+ p->thread.debugctlmsr = 0;
+
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
@@ -471,35 +472,14 @@ static inline void __switch_to_xtra(struct task_struct *prev_p,
struct tss_struct *tss)
{
struct thread_struct *prev, *next;
- unsigned long debugctl;
prev = &prev_p->thread,
next = &next_p->thread;
- debugctl = prev->debugctlmsr;
-
-#ifdef CONFIG_X86_DS
- {
- unsigned long ds_prev = 0, ds_next = 0;
-
- if (prev->ds_ctx)
- ds_prev = (unsigned long)prev->ds_ctx->ds;
- if (next->ds_ctx)
- ds_next = (unsigned long)next->ds_ctx->ds;
-
- if (ds_next != ds_prev) {
- /*
- * We clear debugctl to make sure DS
- * is not in use when we change it:
- */
- debugctl = 0;
- update_debugctlmsr(0);
- wrmsrl(MSR_IA32_DS_AREA, ds_next);
- }
- }
-#endif /* CONFIG_X86_DS */
-
- if (next->debugctlmsr != debugctl)
+ if (test_tsk_thread_flag(next_p, TIF_DS_AREA_MSR) ||
+ test_tsk_thread_flag(prev_p, TIF_DS_AREA_MSR))
+ ds_switch_to(prev_p, next_p);
+ else if (next->debugctlmsr != prev->debugctlmsr)
update_debugctlmsr(next->debugctlmsr);
if (test_tsk_thread_flag(next_p, TIF_DEBUG)) {
@@ -534,14 +514,6 @@ static inline void __switch_to_xtra(struct task_struct *prev_p,
*/
memset(tss->io_bitmap, 0xff, prev->io_bitmap_max);
}
-
-#ifdef CONFIG_X86_PTRACE_BTS
- if (test_tsk_thread_flag(prev_p, TIF_BTS_TRACE_TS))
- ptrace_bts_take_timestamp(prev_p, BTS_TASK_DEPARTS);
-
- if (test_tsk_thread_flag(next_p, TIF_BTS_TRACE_TS))
- ptrace_bts_take_timestamp(next_p, BTS_TASK_ARRIVES);
-#endif /* CONFIG_X86_PTRACE_BTS */
}
/*
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 2c8ec1ba75e6..0a5df5f82fb9 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -581,153 +581,91 @@ static int ioperm_get(struct task_struct *target,
}
#ifdef CONFIG_X86_PTRACE_BTS
-/*
- * The configuration for a particular BTS hardware implementation.
- */
-struct bts_configuration {
- /* the size of a BTS record in bytes; at most BTS_MAX_RECORD_SIZE */
- unsigned char sizeof_bts;
- /* the size of a field in the BTS record in bytes */
- unsigned char sizeof_field;
- /* a bitmask to enable/disable BTS in DEBUGCTL MSR */
- unsigned long debugctl_mask;
-};
-static struct bts_configuration bts_cfg;
-
-#define BTS_MAX_RECORD_SIZE (8 * 3)
-
-
-/*
- * Branch Trace Store (BTS) uses the following format. Different
- * architectures vary in the size of those fields.
- * - source linear address
- * - destination linear address
- * - flags
- *
- * Later architectures use 64bit pointers throughout, whereas earlier
- * architectures use 32bit pointers in 32bit mode.
- *
- * We compute the base address for the first 8 fields based on:
- * - the field size stored in the DS configuration
- * - the relative field position
- *
- * In order to store additional information in the BTS buffer, we use
- * a special source address to indicate that the record requires
- * special interpretation.
- *
- * Netburst indicated via a bit in the flags field whether the branch
- * was predicted; this is ignored.
- */
-
-enum bts_field {
- bts_from = 0,
- bts_to,
- bts_flags,
-
- bts_escape = (unsigned long)-1,
- bts_qual = bts_to,
- bts_jiffies = bts_flags
-};
-
-static inline unsigned long bts_get(const char *base, enum bts_field field)
-{
- base += (bts_cfg.sizeof_field * field);
- return *(unsigned long *)base;
-}
-
-static inline void bts_set(char *base, enum bts_field field, unsigned long val)
-{
- base += (bts_cfg.sizeof_field * field);;
- (*(unsigned long *)base) = val;
-}
-
-/*
- * Translate a BTS record from the raw format into the bts_struct format
- *
- * out (out): bts_struct interpretation
- * raw: raw BTS record
- */
-static void ptrace_bts_translate_record(struct bts_struct *out, const void *raw)
-{
- memset(out, 0, sizeof(*out));
- if (bts_get(raw, bts_from) == bts_escape) {
- out->qualifier = bts_get(raw, bts_qual);
- out->variant.jiffies = bts_get(raw, bts_jiffies);
- } else {
- out->qualifier = BTS_BRANCH;
- out->variant.lbr.from_ip = bts_get(raw, bts_from);
- out->variant.lbr.to_ip = bts_get(raw, bts_to);
- }
-}
-
static int ptrace_bts_read_record(struct task_struct *child, size_t index,
struct bts_struct __user *out)
{
- struct bts_struct ret;
- const void *bts_record;
- size_t bts_index, bts_end;
+ const struct bts_trace *trace;
+ struct bts_struct bts;
+ const unsigned char *at;
int error;
- error = ds_get_bts_end(child->bts, &bts_end);
- if (error < 0)
- return error;
+ trace = ds_read_bts(child->bts);
+ if (!trace)
+ return -EPERM;
- if (bts_end <= index)
- return -EINVAL;
+ at = trace->ds.top - ((index + 1) * trace->ds.size);
+ if ((void *)at < trace->ds.begin)
+ at += (trace->ds.n * trace->ds.size);
- error = ds_get_bts_index(child->bts, &bts_index);
- if (error < 0)
- return error;
-
- /* translate the ptrace bts index into the ds bts index */
- bts_index += bts_end - (index + 1);
- if (bts_end <= bts_index)
- bts_index -= bts_end;
+ if (!trace->read)
+ return -EOPNOTSUPP;
- error = ds_access_bts(child->bts, bts_index, &bts_record);
+ error = trace->read(child->bts, at, &bts);
if (error < 0)
return error;
- ptrace_bts_translate_record(&ret, bts_record);
-
- if (copy_to_user(out, &ret, sizeof(ret)))
+ if (copy_to_user(out, &bts, sizeof(bts)))
return -EFAULT;
- return sizeof(ret);
+ return sizeof(bts);
}
static int ptrace_bts_drain(struct task_struct *child,
long size,
struct bts_struct __user *out)
{
- struct bts_struct ret;
- const unsigned char *raw;
- size_t end, i;
- int error;
+ const struct bts_trace *trace;
+ const unsigned char *at;
+ int error, drained = 0;
- error = ds_get_bts_index(child->bts, &end);
- if (error < 0)
- return error;
+ trace = ds_read_bts(child->bts);
+ if (!trace)
+ return -EPERM;
- if (size < (end * sizeof(struct bts_struct)))
+ if (!trace->read)
+ return -EOPNOTSUPP;
+
+ if (size < (trace->ds.top - trace->ds.begin))
return -EIO;
- error = ds_access_bts(child->bts, 0, (const void **)&raw);
- if (error < 0)
- return error;
+ for (at = trace->ds.begin; (void *)at < trace->ds.top;
+ out++, drained++, at += trace->ds.size) {
+ struct bts_struct bts;
+ int error;
- for (i = 0; i < end; i++, out++, raw += bts_cfg.sizeof_bts) {
- ptrace_bts_translate_record(&ret, raw);
+ error = trace->read(child->bts, at, &bts);
+ if (error < 0)
+ return error;
- if (copy_to_user(out, &ret, sizeof(ret)))
+ if (copy_to_user(out, &bts, sizeof(bts)))
return -EFAULT;
}
- error = ds_clear_bts(child->bts);
+ memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
+
+ error = ds_reset_bts(child->bts);
if (error < 0)
return error;
- return end;
+ return drained;
+}
+
+static int ptrace_bts_allocate_buffer(struct task_struct *child, size_t size)
+{
+ child->bts_buffer = alloc_locked_buffer(size);
+ if (!child->bts_buffer)
+ return -ENOMEM;
+
+ child->bts_size = size;
+
+ return 0;
+}
+
+static void ptrace_bts_free_buffer(struct task_struct *child)
+{
+ free_locked_buffer(child->bts_buffer, child->bts_size);
+ child->bts_buffer = NULL;
+ child->bts_size = 0;
}
static int ptrace_bts_config(struct task_struct *child,
@@ -735,136 +673,86 @@ static int ptrace_bts_config(struct task_struct *child,
const struct ptrace_bts_config __user *ucfg)
{
struct ptrace_bts_config cfg;
- int error = 0;
-
- error = -EOPNOTSUPP;
- if (!bts_cfg.sizeof_bts)
- goto errout;
+ unsigned int flags = 0;
- error = -EIO;
if (cfg_size < sizeof(cfg))
- goto errout;
+ return -EIO;
- error = -EFAULT;
if (copy_from_user(&cfg, ucfg, sizeof(cfg)))
- goto errout;
-
- error = -EINVAL;
- if ((cfg.flags & PTRACE_BTS_O_SIGNAL) &&
- !(cfg.flags & PTRACE_BTS_O_ALLOC))
- goto errout;
-
- if (cfg.flags & PTRACE_BTS_O_ALLOC) {
- bts_ovfl_callback_t ovfl = NULL;
- unsigned int sig = 0;
-
- error = -EINVAL;
- if (cfg.size < (10 * bts_cfg.sizeof_bts))
- goto errout;
+ return -EFAULT;
- if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
- if (!cfg.signal)
- goto errout;
+ if (child->bts) {
+ ds_release_bts(child->bts);
+ child->bts = NULL;
+ }
- error = -EOPNOTSUPP;
- goto errout;
+ if (cfg.flags & PTRACE_BTS_O_SIGNAL) {
+ if (!cfg.signal)
+ return -EINVAL;
- sig = cfg.signal;
- }
+ return -EOPNOTSUPP;
- if (child->bts) {
- (void)ds_release_bts(child->bts);
- kfree(child->bts_buffer);
+ child->thread.bts_ovfl_signal = cfg.signal;
+ }
- child->bts = NULL;
- child->bts_buffer = NULL;
- }
+ if ((cfg.flags & PTRACE_BTS_O_ALLOC) &&
+ (cfg.size != child->bts_size)) {
+ int error;
- error = -ENOMEM;
- child->bts_buffer = kzalloc(cfg.size, GFP_KERNEL);
- if (!child->bts_buffer)
- goto errout;
-
- child->bts = ds_request_bts(child, child->bts_buffer, cfg.size,
- ovfl, /* th = */ (size_t)-1);
- if (IS_ERR(child->bts)) {
- error = PTR_ERR(child->bts);
- kfree(child->bts_buffer);
- child->bts = NULL;
- child->bts_buffer = NULL;
- goto errout;
- }
+ ptrace_bts_free_buffer(child);
- child->thread.bts_ovfl_signal = sig;
+ error = ptrace_bts_allocate_buffer(child, cfg.size);
+ if (error < 0)
+ return error;
}
- error = -EINVAL;
- if (!child->thread.ds_ctx && cfg.flags)
- goto errout;
-
if (cfg.flags & PTRACE_BTS_O_TRACE)
- child->thread.debugctlmsr |= bts_cfg.debugctl_mask;
- else
- child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask;
+ flags |= BTS_USER;
if (cfg.flags & PTRACE_BTS_O_SCHED)
- set_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
- else
- clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
+ flags |= BTS_TIMESTAMPS;
- error = sizeof(cfg);
+ child->bts = ds_request_bts(child, child->bts_buffer, child->bts_size,
+ /* ovfl = */ NULL, /* th = */ (size_t)-1,
+ flags);
+ if (IS_ERR(child->bts)) {
+ int error = PTR_ERR(child->bts);
-out:
- if (child->thread.debugctlmsr)
- set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
- else
- clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
+ ptrace_bts_free_buffer(child);
+ child->bts = NULL;
- return error;
+ return error;
+ }
-errout:
- child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask;
- clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
- goto out;
+ return sizeof(cfg);
}
static int ptrace_bts_status(struct task_struct *child,
long cfg_size,
struct ptrace_bts_config __user *ucfg)
{
+ const struct bts_trace *trace;
struct ptrace_bts_config cfg;
- size_t end;
- const void *base, *max;
- int error;
if (cfg_size < sizeof(cfg))
return -EIO;
- error = ds_get_bts_end(child->bts, &end);
- if (error < 0)
- return error;
-
- error = ds_access_bts(child->bts, /* index = */ 0, &base);
- if (error < 0)
- return error;
-
- error = ds_access_bts(child->bts, /* index = */ end, &max);
- if (error < 0)
- return error;
+ trace = ds_read_bts(child->bts);
+ if (!trace)
+ return -EPERM;
memset(&cfg, 0, sizeof(cfg));
- cfg.size = (max - base);
+ cfg.size = trace->ds.end - trace->ds.begin;
cfg.signal = child->thread.bts_ovfl_signal;
cfg.bts_size = sizeof(struct bts_struct);
if (cfg.signal)
cfg.flags |= PTRACE_BTS_O_SIGNAL;
- if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) &&
- child->thread.debugctlmsr & bts_cfg.debugctl_mask)
+ if (trace->ds.flags & BTS_USER)
cfg.flags |= PTRACE_BTS_O_TRACE;
- if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS))
+ if (trace->ds.flags & BTS_TIMESTAMPS)
cfg.flags |= PTRACE_BTS_O_SCHED;
if (copy_to_user(ucfg, &cfg, sizeof(cfg)))
@@ -873,106 +761,77 @@ static int ptrace_bts_status(struct task_struct *child,
return sizeof(cfg);
}
-static int ptrace_bts_write_record(struct task_struct *child,
- const struct bts_struct *in)
+static int ptrace_bts_clear(struct task_struct *child)
{
- unsigned char bts_record[BTS_MAX_RECORD_SIZE];
+ const struct bts_trace *trace;
- BUG_ON(BTS_MAX_RECORD_SIZE < bts_cfg.sizeof_bts);
+ trace = ds_read_bts(child->bts);
+ if (!trace)
+ return -EPERM;
- memset(bts_record, 0, bts_cfg.sizeof_bts);
- switch (in->qualifier) {
- case BTS_INVALID:
- break;
+ memset(trace->ds.begin, 0, trace->ds.n * trace->ds.size);
- case BTS_BRANCH:
- bts_set(bts_record, bts_from, in->variant.lbr.from_ip);
- bts_set(bts_record, bts_to, in->variant.lbr.to_ip);
- break;
+ return ds_reset_bts(child->bts);
+}
- case BTS_TASK_ARRIVES:
- case BTS_TASK_DEPARTS:
- bts_set(bts_record, bts_from, bts_escape);
- bts_set(bts_record, bts_qual, in->qualifier);
- bts_set(bts_record, bts_jiffies, in->variant.jiffies);
- break;
+static int ptrace_bts_size(struct task_struct *child)
+{
+ const struct bts_trace *trace;
- default:
- return -EINVAL;
- }
+ trace = ds_read_bts(child->bts);
+ if (!trace)
+ return -EPERM;
- return ds_write_bts(child->bts, bts_record, bts_cfg.sizeof_bts);
+ return (trace->ds.top - trace->ds.begin) / trace->ds.size;
}
-void ptrace_bts_take_timestamp(struct task_struct *tsk,
- enum bts_qualifier qualifier)
+static void ptrace_bts_fork(struct task_struct *tsk)
{
- struct bts_struct rec = {
- .qualifier = qualifier,
- .variant.jiffies = jiffies_64
- };
-
- ptrace_bts_write_record(tsk, &rec);
+ tsk->bts = NULL;
+ tsk->bts_buffer = NULL;
+ tsk->bts_size = 0;
+ tsk->thread.bts_ovfl_signal = 0;
}
-static const struct bts_configuration bts_cfg_netburst = {
- .sizeof_bts = sizeof(long) * 3,
- .sizeof_field = sizeof(long),
- .debugctl_mask = (1<<2)|(1<<3)|(1<<5)
-};
+static void ptrace_bts_untrace(struct task_struct *child)
+{
+ if (unlikely(child->bts)) {
+ ds_release_bts(child->bts);
+ child->bts = NULL;
-static const struct bts_configuration bts_cfg_pentium_m = {
- .sizeof_bts = sizeof(long) * 3,
- .sizeof_field = sizeof(long),
- .debugctl_mask = (1<<6)|(1<<7)
-};
+ /* We cannot update total_vm and locked_vm since
+ child's mm is already gone. But we can reclaim the
+ memory. */
+ kfree(child->bts_buffer);
+ child->bts_buffer = NULL;
+ child->bts_size = 0;
+ }
+}
-static const struct bts_configuration bts_cfg_core2 = {
- .sizeof_bts = 8 * 3,
- .sizeof_field = 8,
- .debugctl_mask = (1<<6)|(1<<7)|(1<<9)
-};
+static void ptrace_bts_detach(struct task_struct *child)
+{
+ if (unlikely(child->bts)) {
+ ds_release_bts(child->bts);
+ child->bts = NULL;
-static inline void bts_configure(const struct bts_configuration *cfg)
+ ptrace_bts_free_buffer(child);
+ }
+}
+#else
+static inline void ptrace_bts_fork(struct task_struct *tsk) {}
+static inline void ptrace_bts_detach(struct task_struct *child) {}
+static inline void ptrace_bts_untrace(struct task_struct *child) {}
+#endif /* CONFIG_X86_PTRACE_BTS */
+
+void x86_ptrace_fork(struct task_struct *child, unsigned long clone_flags)
{
- bts_cfg = *cfg;
+ ptrace_bts_fork(child);
}
-void __cpuinit ptrace_bts_init_intel(struct cpuinfo_x86 *c)
+void x86_ptrace_untrace(struct task_struct *child)
{
- switch (c->x86) {
- case 0x6:
- switch (c->x86_model) {
- case 0 ... 0xC:
- /* sorry, don't know about them */
- break;
- case 0xD:
- case 0xE: /* Pentium M */
- bts_configure(&bts_cfg_pentium_m);
- break;
- default: /* Core2, Atom, ... */
- bts_configure(&bts_cfg_core2);
- break;
- }
- break;
- case 0xF:
- switch (c->x86_model) {
- case 0x0:
- case 0x1:
- case 0x2: /* Netburst */
- bts_configure(&bts_cfg_netburst);
- break;
- default:
- /* sorry, don't know about them */
- break;
- }
- break;
- default:
- /* sorry, don't know about them */
- break;
- }
+ ptrace_bts_untrace(child);
}
-#endif /* CONFIG_X86_PTRACE_BTS */
/*
* Called by kernel/ptrace.c when detaching..
@@ -985,19 +844,7 @@ void ptrace_disable(struct task_struct *child)
#ifdef TIF_SYSCALL_EMU
clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif
-#ifdef CONFIG_X86_PTRACE_BTS
- if (child->bts) {
- (void)ds_release_bts(child->bts);
- kfree(child->bts_buffer);
- child->bts_buffer = NULL;
-
- child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask;
- if (!child->thread.debugctlmsr)
- clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
-
- clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS);
- }
-#endif /* CONFIG_X86_PTRACE_BTS */
+ ptrace_bts_detach(child);
}
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
@@ -1128,16 +975,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
(child, data, (struct ptrace_bts_config __user *)addr);
break;
- case PTRACE_BTS_SIZE: {
- size_t size;
-
- ret = ds_get_bts_index(child->bts, &size);
- if (ret == 0) {
- BUG_ON(size != (int) size);
- ret = (int) size;
- }
+ case PTRACE_BTS_SIZE:
+ ret = ptrace_bts_size(child);
break;
- }
case PTRACE_BTS_GET:
ret = ptrace_bts_read_record
@@ -1145,7 +985,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
case PTRACE_BTS_CLEAR:
- ret = ds_clear_bts(child->bts);
+ ret = ptrace_bts_clear(child);
break;
case PTRACE_BTS_DRAIN:
@@ -1408,6 +1248,14 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
case PTRACE_GET_THREAD_AREA:
case PTRACE_SET_THREAD_AREA:
+#ifdef CONFIG_X86_PTRACE_BTS
+ case PTRACE_BTS_CONFIG:
+ case PTRACE_BTS_STATUS:
+ case PTRACE_BTS_SIZE:
+ case PTRACE_BTS_GET:
+ case PTRACE_BTS_CLEAR:
+ case PTRACE_BTS_DRAIN:
+#endif /* CONFIG_X86_PTRACE_BTS */
return arch_ptrace(child, request, addr, data);
default:
diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c
index 67465ed89310..309949e9e1c1 100644
--- a/arch/x86/kernel/quirks.c
+++ b/arch/x86/kernel/quirks.c
@@ -168,6 +168,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31,
ich_force_enable_hpet);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1,
ich_force_enable_hpet);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4,
+ ich_force_enable_hpet);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7,
ich_force_enable_hpet);
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 32fe42f26e79..ae0d8042cf69 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -93,11 +93,13 @@
#include <asm/desc.h>
#include <asm/dma.h>
#include <asm/iommu.h>
+#include <asm/gart.h>
#include <asm/mmu_context.h>
#include <asm/proto.h>
#include <mach_apic.h>
#include <asm/paravirt.h>
+#include <asm/hypervisor.h>
#include <asm/percpu.h>
#include <asm/topology.h>
@@ -448,6 +450,7 @@ static void __init reserve_early_setup_data(void)
* @size: Size of the crashkernel memory to reserve.
* Returns the base address on success, and -1ULL on failure.
*/
+static
unsigned long long __init find_and_reserve_crashkernel(unsigned long long size)
{
const unsigned long long alignment = 16<<20; /* 16M */
@@ -600,157 +603,7 @@ static struct x86_quirks default_x86_quirks __initdata = {
struct x86_quirks *x86_quirks __initdata = &default_x86_quirks;
-/*
- * Some BIOSes seem to corrupt the low 64k of memory during events
- * like suspend/resume and unplugging an HDMI cable. Reserve all
- * remaining free memory in that area and fill it with a distinct
- * pattern.
- */
-#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
-#define MAX_SCAN_AREAS 8
-
-static int __read_mostly memory_corruption_check = -1;
-
-static unsigned __read_mostly corruption_check_size = 64*1024;
-static unsigned __read_mostly corruption_check_period = 60; /* seconds */
-
-static struct e820entry scan_areas[MAX_SCAN_AREAS];
-static int num_scan_areas;
-
-
-static int set_corruption_check(char *arg)
-{
- char *end;
-
- memory_corruption_check = simple_strtol(arg, &end, 10);
-
- return (*end == 0) ? 0 : -EINVAL;
-}
-early_param("memory_corruption_check", set_corruption_check);
-
-static int set_corruption_check_period(char *arg)
-{
- char *end;
-
- corruption_check_period = simple_strtoul(arg, &end, 10);
-
- return (*end == 0) ? 0 : -EINVAL;
-}
-early_param("memory_corruption_check_period", set_corruption_check_period);
-
-static int set_corruption_check_size(char *arg)
-{
- char *end;
- unsigned size;
-
- size = memparse(arg, &end);
-
- if (*end == '\0')
- corruption_check_size = size;
-
- return (size == corruption_check_size) ? 0 : -EINVAL;
-}
-early_param("memory_corruption_check_size", set_corruption_check_size);
-
-
-static void __init setup_bios_corruption_check(void)
-{
- u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */
-
- if (memory_corruption_check == -1) {
- memory_corruption_check =
-#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
- 1
-#else
- 0
-#endif
- ;
- }
-
- if (corruption_check_size == 0)
- memory_corruption_check = 0;
-
- if (!memory_corruption_check)
- return;
-
- corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
-
- while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
- u64 size;
- addr = find_e820_area_size(addr, &size, PAGE_SIZE);
-
- if (addr == 0)
- break;
-
- if ((addr + size) > corruption_check_size)
- size = corruption_check_size - addr;
-
- if (size == 0)
- break;
-
- e820_update_range(addr, size, E820_RAM, E820_RESERVED);
- scan_areas[num_scan_areas].addr = addr;
- scan_areas[num_scan_areas].size = size;
- num_scan_areas++;
-
- /* Assume we've already mapped this early memory */
- memset(__va(addr), 0, size);
-
- addr += size;
- }
-
- printk(KERN_INFO "Scanning %d areas for low memory corruption\n",
- num_scan_areas);
- update_e820();
-}
-
-static struct timer_list periodic_check_timer;
-
-void check_for_bios_corruption(void)
-{
- int i;
- int corruption = 0;
-
- if (!memory_corruption_check)
- return;
-
- for(i = 0; i < num_scan_areas; i++) {
- unsigned long *addr = __va(scan_areas[i].addr);
- unsigned long size = scan_areas[i].size;
-
- for(; size; addr++, size -= sizeof(unsigned long)) {
- if (!*addr)
- continue;
- printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n",
- addr, __pa(addr), *addr);
- corruption = 1;
- *addr = 0;
- }
- }
-
- WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n");
-}
-
-static void periodic_check_for_corruption(unsigned long data)
-{
- check_for_bios_corruption();
- mod_timer(&periodic_check_timer, round_jiffies(jiffies + corruption_check_period*HZ));
-}
-
-void start_periodic_check_for_corruption(void)
-{
- if (!memory_corruption_check || corruption_check_period == 0)
- return;
-
- printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
- corruption_check_period);
-
- init_timer(&periodic_check_timer);
- periodic_check_timer.function = &periodic_check_for_corruption;
- periodic_check_for_corruption(0);
-}
-#endif
-
+#ifdef CONFIG_X86_RESERVE_LOW_64K
static int __init dmi_low_memory_corruption(const struct dmi_system_id *d)
{
printk(KERN_NOTICE
@@ -762,6 +615,7 @@ static int __init dmi_low_memory_corruption(const struct dmi_system_id *d)
return 0;
}
+#endif
/* List of systems that have known low memory corruption BIOS problems */
static struct dmi_system_id __initdata bad_bios_dmi_table[] = {
@@ -920,6 +774,12 @@ void __init setup_arch(char **cmdline_p)
dmi_check_system(bad_bios_dmi_table);
+ /*
+ * VMware detection requires dmi to be available, so this
+ * needs to be done after dmi_scan_machine, for the BP.
+ */
+ init_hypervisor(&boot_cpu_data);
+
#ifdef CONFIG_X86_32
probe_roms();
#endif
diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c
index 0b63b08e7530..49f3f709ee1f 100644
--- a/arch/x86/kernel/setup_percpu.c
+++ b/arch/x86/kernel/setup_percpu.c
@@ -339,25 +339,25 @@ static const cpumask_t cpu_mask_none;
/*
* Returns a pointer to the bitmask of CPUs on Node 'node'.
*/
-const cpumask_t *_node_to_cpumask_ptr(int node)
+const cpumask_t *cpumask_of_node(int node)
{
if (node_to_cpumask_map == NULL) {
printk(KERN_WARNING
- "_node_to_cpumask_ptr(%d): no node_to_cpumask_map!\n",
+ "cpumask_of_node(%d): no node_to_cpumask_map!\n",
node);
dump_stack();
return (const cpumask_t *)&cpu_online_map;
}
if (node >= nr_node_ids) {
printk(KERN_WARNING
- "_node_to_cpumask_ptr(%d): node > nr_node_ids(%d)\n",
+ "cpumask_of_node(%d): node > nr_node_ids(%d)\n",
node, nr_node_ids);
dump_stack();
return &cpu_mask_none;
}
return &node_to_cpumask_map[node];
}
-EXPORT_SYMBOL(_node_to_cpumask_ptr);
+EXPORT_SYMBOL(cpumask_of_node);
/*
* Returns a bitmask of CPUs on Node 'node'.
diff --git a/arch/x86/kernel/sigframe.h b/arch/x86/kernel/sigframe.h
deleted file mode 100644
index cc673aa55ce4..000000000000
--- a/arch/x86/kernel/sigframe.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifdef CONFIG_X86_32
-struct sigframe {
- char __user *pretcode;
- int sig;
- struct sigcontext sc;
- /*
- * fpstate is unused. fpstate is moved/allocated after
- * retcode[] below. This movement allows to have the FP state and the
- * future state extensions (xsave) stay together.
- * And at the same time retaining the unused fpstate, prevents changing
- * the offset of extramask[] in the sigframe and thus prevent any
- * legacy application accessing/modifying it.
- */
- struct _fpstate fpstate_unused;
- unsigned long extramask[_NSIG_WORDS-1];
- char retcode[8];
- /* fp state follows here */
-};
-
-struct rt_sigframe {
- char __user *pretcode;
- int sig;
- struct siginfo __user *pinfo;
- void __user *puc;
- struct siginfo info;
- struct ucontext uc;
- char retcode[8];
- /* fp state follows here */
-};
-#else
-struct rt_sigframe {
- char __user *pretcode;
- struct ucontext uc;
- struct siginfo info;
- /* fp state follows here */
-};
-
-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
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
new file mode 100644
index 000000000000..89bb7668041d
--- /dev/null
+++ b/arch/x86/kernel/signal.c
@@ -0,0 +1,909 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen SuSE Labs
+ *
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes
+ * 2000-2002 x86-64 support by Andi Kleen
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/tracehook.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <linux/uaccess.h>
+
+#include <asm/processor.h>
+#include <asm/ucontext.h>
+#include <asm/i387.h>
+#include <asm/vdso.h>
+
+#ifdef CONFIG_X86_64
+#include <asm/proto.h>
+#include <asm/ia32_unistd.h>
+#include <asm/mce.h>
+#endif /* CONFIG_X86_64 */
+
+#include <asm/syscall.h>
+#include <asm/syscalls.h>
+
+#include <asm/sigframe.h>
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+#define __FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
+ X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
+ X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
+ X86_EFLAGS_CF)
+
+#ifdef CONFIG_X86_32
+# define FIX_EFLAGS (__FIX_EFLAGS | X86_EFLAGS_RF)
+#else
+# define FIX_EFLAGS __FIX_EFLAGS
+#endif
+
+#define COPY(x) { \
+ err |= __get_user(regs->x, &sc->x); \
+}
+
+#define COPY_SEG(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp; \
+}
+
+#define COPY_SEG_CPL3(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp | 3; \
+}
+
+#define GET_SEG(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ loadsegment(seg, tmp); \
+}
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
+ unsigned long *pax)
+{
+ void __user *buf;
+ unsigned int tmpflags;
+ unsigned int err = 0;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+#ifdef CONFIG_X86_32
+ GET_SEG(gs);
+ COPY_SEG(fs);
+ COPY_SEG(es);
+ COPY_SEG(ds);
+#endif /* CONFIG_X86_32 */
+
+ COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
+ COPY(dx); COPY(cx); COPY(ip);
+
+#ifdef CONFIG_X86_64
+ COPY(r8);
+ COPY(r9);
+ COPY(r10);
+ COPY(r11);
+ COPY(r12);
+ COPY(r13);
+ COPY(r14);
+ COPY(r15);
+#endif /* CONFIG_X86_64 */
+
+#ifdef CONFIG_X86_32
+ COPY_SEG_CPL3(cs);
+ COPY_SEG_CPL3(ss);
+#else /* !CONFIG_X86_32 */
+ /* Kernel saves and restores only the CS segment register on signals,
+ * which is the bare minimum needed to allow mixed 32/64-bit code.
+ * App's signal handler can save/restore other segments if needed. */
+ COPY_SEG_CPL3(cs);
+#endif /* CONFIG_X86_32 */
+
+ err |= __get_user(tmpflags, &sc->flags);
+ regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
+ regs->orig_ax = -1; /* disable syscall checks */
+
+ err |= __get_user(buf, &sc->fpstate);
+ err |= restore_i387_xstate(buf);
+
+ err |= __get_user(*pax, &sc->ax);
+ return err;
+}
+
+static int
+setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
+ struct pt_regs *regs, unsigned long mask)
+{
+ int err = 0;
+
+#ifdef CONFIG_X86_32
+ {
+ unsigned int tmp;
+
+ savesegment(gs, tmp);
+ err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
+ }
+ err |= __put_user(regs->fs, (unsigned int __user *)&sc->fs);
+ err |= __put_user(regs->es, (unsigned int __user *)&sc->es);
+ err |= __put_user(regs->ds, (unsigned int __user *)&sc->ds);
+#endif /* CONFIG_X86_32 */
+
+ err |= __put_user(regs->di, &sc->di);
+ err |= __put_user(regs->si, &sc->si);
+ err |= __put_user(regs->bp, &sc->bp);
+ err |= __put_user(regs->sp, &sc->sp);
+ err |= __put_user(regs->bx, &sc->bx);
+ err |= __put_user(regs->dx, &sc->dx);
+ err |= __put_user(regs->cx, &sc->cx);
+ err |= __put_user(regs->ax, &sc->ax);
+#ifdef CONFIG_X86_64
+ err |= __put_user(regs->r8, &sc->r8);
+ err |= __put_user(regs->r9, &sc->r9);
+ err |= __put_user(regs->r10, &sc->r10);
+ err |= __put_user(regs->r11, &sc->r11);
+ err |= __put_user(regs->r12, &sc->r12);
+ err |= __put_user(regs->r13, &sc->r13);
+ err |= __put_user(regs->r14, &sc->r14);
+ err |= __put_user(regs->r15, &sc->r15);
+#endif /* CONFIG_X86_64 */
+
+ err |= __put_user(current->thread.trap_no, &sc->trapno);
+ err |= __put_user(current->thread.error_code, &sc->err);
+ err |= __put_user(regs->ip, &sc->ip);
+#ifdef CONFIG_X86_32
+ err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
+ err |= __put_user(regs->flags, &sc->flags);
+ err |= __put_user(regs->sp, &sc->sp_at_signal);
+ err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
+#else /* !CONFIG_X86_32 */
+ err |= __put_user(regs->flags, &sc->flags);
+ err |= __put_user(regs->cs, &sc->cs);
+ err |= __put_user(0, &sc->gs);
+ err |= __put_user(0, &sc->fs);
+#endif /* CONFIG_X86_32 */
+
+ err |= __put_user(fpstate, &sc->fpstate);
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+ err |= __put_user(current->thread.cr2, &sc->cr2);
+
+ return err;
+}
+
+/*
+ * Set up a signal frame.
+ */
+#ifdef CONFIG_X86_32
+static const struct {
+ u16 poplmovl;
+ u32 val;
+ u16 int80;
+} __attribute__((packed)) retcode = {
+ 0xb858, /* popl %eax; movl $..., %eax */
+ __NR_sigreturn,
+ 0x80cd, /* int $0x80 */
+};
+
+static const struct {
+ u8 movl;
+ u32 val;
+ u16 int80;
+ u8 pad;
+} __attribute__((packed)) rt_retcode = {
+ 0xb8, /* movl $..., %eax */
+ __NR_rt_sigreturn,
+ 0x80cd, /* int $0x80 */
+ 0
+};
+
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
+ void **fpstate)
+{
+ unsigned long sp;
+
+ /* Default to using normal stack */
+ sp = regs->sp;
+
+ /*
+ * If we are on the alternate signal stack and would overflow it, don't.
+ * Return an always-bogus address instead so we will die with SIGSEGV.
+ */
+ if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size)))
+ return (void __user *) -1L;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (sas_ss_flags(sp) == 0)
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ } else {
+ /* This is the legacy signal stack switching. */
+ if ((regs->ss & 0xffff) != __USER_DS &&
+ !(ka->sa.sa_flags & SA_RESTORER) &&
+ ka->sa.sa_restorer)
+ sp = (unsigned long) ka->sa.sa_restorer;
+ }
+
+ if (used_math()) {
+ sp = sp - sig_xstate_size;
+ *fpstate = (struct _fpstate *) sp;
+ if (save_i387_xstate(*fpstate) < 0)
+ return (void __user *)-1L;
+ }
+
+ sp -= frame_size;
+ /*
+ * Align the stack pointer according to the i386 ABI,
+ * i.e. so that on function entry ((sp + 4) & 15) == 0.
+ */
+ sp = ((sp + 4) & -16ul) - 4;
+
+ return (void __user *) sp;
+}
+
+static int
+__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct sigframe __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 (__put_user(sig, &frame->sig))
+ return -EFAULT;
+
+ if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
+ return -EFAULT;
+
+ if (_NSIG_WORDS > 1) {
+ if (__copy_to_user(&frame->extramask, &set->sig[1],
+ sizeof(frame->extramask)))
+ return -EFAULT;
+ }
+
+ if (current->mm->context.vdso)
+ restorer = VDSO32_SYMBOL(current->mm->context.vdso, sigreturn);
+ else
+ restorer = &frame->retcode;
+ if (ka->sa.sa_flags & SA_RESTORER)
+ restorer = ka->sa.sa_restorer;
+
+ /* Set up to return from userspace. */
+ err |= __put_user(restorer, &frame->pretcode);
+
+ /*
+ * This is popl %eax ; movl $__NR_sigreturn, %eax ; int $0x80
+ *
+ * WE DO NOT USE IT ANY MORE! It's only left here for historical
+ * reasons and because gdb uses it as a signature to notice
+ * signal handler stack frames.
+ */
+ err |= __put_user(*((u64 *)&retcode), (u64 *)frame->retcode);
+
+ if (err)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+ regs->sp = (unsigned long)frame;
+ regs->ip = (unsigned long)ka->sa.sa_handler;
+ regs->ax = (unsigned long)sig;
+ regs->dx = 0;
+ regs->cx = 0;
+
+ regs->ds = __USER_DS;
+ regs->es = __USER_DS;
+ regs->ss = __USER_DS;
+ regs->cs = __USER_CS;
+
+ return 0;
+}
+
+static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __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;
+
+ err |= __put_user(sig, &frame->sig);
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ return -EFAULT;
+
+ /* Create the ucontext. */
+ if (cpu_has_xsave)
+ err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->sp),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(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));
+ if (err)
+ return -EFAULT;
+
+ /* Set up to return from userspace. */
+ restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
+ if (ka->sa.sa_flags & SA_RESTORER)
+ restorer = ka->sa.sa_restorer;
+ err |= __put_user(restorer, &frame->pretcode);
+
+ /*
+ * This is movl $__NR_rt_sigreturn, %ax ; int $0x80
+ *
+ * WE DO NOT USE IT ANY MORE! It's only left here for historical
+ * reasons and because gdb uses it as a signature to notice
+ * signal handler stack frames.
+ */
+ err |= __put_user(*((u64 *)&rt_retcode), (u64 *)frame->retcode);
+
+ if (err)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+ regs->sp = (unsigned long)frame;
+ regs->ip = (unsigned long)ka->sa.sa_handler;
+ regs->ax = (unsigned long)sig;
+ regs->dx = (unsigned long)&frame->info;
+ regs->cx = (unsigned long)&frame->uc;
+
+ regs->ds = __USER_DS;
+ regs->es = __USER_DS;
+ regs->ss = __USER_DS;
+ regs->cs = __USER_CS;
+
+ return 0;
+}
+#else /* !CONFIG_X86_32 */
+/*
+ * Determine which stack to use..
+ */
+static void __user *
+get_stack(struct k_sigaction *ka, unsigned long sp, unsigned long size)
+{
+ /* Default to using normal stack - redzone*/
+ sp -= 128;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (sas_ss_flags(sp) == 0)
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ return (void __user *)round_down(sp - size, 64);
+}
+
+static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ void __user *fp = NULL;
+ int err = 0;
+ struct task_struct *me = current;
+
+ if (used_math()) {
+ fp = get_stack(ka, regs->sp, sig_xstate_size);
+ frame = (void __user *)round_down(
+ (unsigned long)fp - sizeof(struct rt_sigframe), 16) - 8;
+
+ if (save_i387_xstate(fp) < 0)
+ return -EFAULT;
+ } else
+ frame = get_stack(ka, regs->sp, sizeof(struct rt_sigframe)) - 8;
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ if (ka->sa.sa_flags & SA_SIGINFO) {
+ if (copy_siginfo_to_user(&frame->info, info))
+ return -EFAULT;
+ }
+
+ /* Create the ucontext. */
+ if (cpu_has_xsave)
+ err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->sp),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(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. */
+ /* x86-64 should always use SA_RESTORER. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
+ } else {
+ /* could use a vstub here */
+ return -EFAULT;
+ }
+
+ if (err)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+ regs->di = sig;
+ /* In case the signal handler was declared without prototypes */
+ regs->ax = 0;
+
+ /* This also works for non SA_SIGINFO handlers because they expect the
+ next argument after the signal number on the stack. */
+ regs->si = (unsigned long)&frame->info;
+ regs->dx = (unsigned long)&frame->uc;
+ regs->ip = (unsigned long) ka->sa.sa_handler;
+
+ regs->sp = (unsigned long)frame;
+
+ /* Set up the CS register to run signal handlers in 64-bit mode,
+ even if the handler happens to be interrupting 32-bit code. */
+ regs->cs = __USER_CS;
+
+ return 0;
+}
+#endif /* CONFIG_X86_32 */
+
+#ifdef CONFIG_X86_32
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int
+sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+{
+ mask &= _BLOCKABLE;
+ spin_lock_irq(&current->sighand->siglock);
+ current->saved_sigmask = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_restore_sigmask();
+
+ return -ERESTARTNOHAND;
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction __user *act,
+ struct old_sigaction __user *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ 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(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_X86_32 */
+
+#ifdef CONFIG_X86_32
+asmlinkage int sys_sigaltstack(unsigned long bx)
+{
+ /*
+ * This is needed to make gcc realize it doesn't own the
+ * "struct pt_regs"
+ */
+ struct pt_regs *regs = (struct pt_regs *)&bx;
+ const stack_t __user *uss = (const stack_t __user *)bx;
+ stack_t __user *uoss = (stack_t __user *)regs->cx;
+
+ return do_sigaltstack(uss, uoss, regs->sp);
+}
+#else /* !CONFIG_X86_32 */
+asmlinkage long
+sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
+ struct pt_regs *regs)
+{
+ return do_sigaltstack(uss, uoss, regs->sp);
+}
+#endif /* CONFIG_X86_32 */
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+#ifdef CONFIG_X86_32
+asmlinkage unsigned long sys_sigreturn(unsigned long __unused)
+{
+ struct sigframe __user *frame;
+ struct pt_regs *regs;
+ unsigned long ax;
+ sigset_t set;
+
+ regs = (struct pt_regs *) &__unused;
+ frame = (struct sigframe __user *)(regs->sp - 8);
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->sc, &ax))
+ goto badframe;
+ return ax;
+
+badframe:
+ signal_fault(regs, frame, "sigreturn");
+
+ return 0;
+}
+#endif /* CONFIG_X86_32 */
+
+static long do_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ unsigned long ax;
+ sigset_t set;
+
+ frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
+ goto badframe;
+
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
+ goto badframe;
+
+ return ax;
+
+badframe:
+ signal_fault(regs, frame, "rt_sigreturn");
+ return 0;
+}
+
+#ifdef CONFIG_X86_32
+asmlinkage int sys_rt_sigreturn(struct pt_regs regs)
+{
+ return do_rt_sigreturn(&regs);
+}
+#else /* !CONFIG_X86_32 */
+asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
+{
+ return do_rt_sigreturn(regs);
+}
+#endif /* CONFIG_X86_32 */
+
+/*
+ * OK, we're invoking a handler:
+ */
+static int signr_convert(int sig)
+{
+#ifdef CONFIG_X86_32
+ struct thread_info *info = current_thread_info();
+
+ if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32)
+ return info->exec_domain->signal_invmap[sig];
+#endif /* CONFIG_X86_32 */
+ 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 */
+
+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,
+ sigset_t *set, struct pt_regs *regs)
+{
+ int usig = signr_convert(sig);
+ int ret;
+
+ /* Set up the stack frame */
+ if (is_ia32) {
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
+ else
+ ret = ia32_setup_frame(usig, ka, set, regs);
+ } else
+ ret = __setup_rt_frame(sig, ka, info, set, regs);
+
+ if (ret) {
+ force_sigsegv(sig, current);
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+static int
+handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
+ sigset_t *oldset, struct pt_regs *regs)
+{
+ int ret;
+
+ /* Are we from a system call? */
+ if (syscall_get_nr(current, regs) >= 0) {
+ /* If so, check system call restarting.. */
+ switch (syscall_get_error(current, regs)) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ regs->ax = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->ax = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->ax = regs->orig_ax;
+ regs->ip -= 2;
+ break;
+ }
+ }
+
+ /*
+ * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
+ * flag so that register information in the sigcontext is correct.
+ */
+ if (unlikely(regs->flags & X86_EFLAGS_TF) &&
+ likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
+ regs->flags &= ~X86_EFLAGS_TF;
+
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
+
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_X86_64
+ /*
+ * This has nothing to do with segment registers,
+ * despite the name. This magic affects uaccess.h
+ * macros' behavior. Reset it to the normal setting.
+ */
+ set_fs(USER_DS);
+#endif
+
+ /*
+ * Clear the direction flag as per the ABI for function entry.
+ */
+ regs->flags &= ~X86_EFLAGS_DF;
+
+ /*
+ * Clear TF when entering the signal handler, but
+ * notify any tracer that was single-stepping it.
+ * The tracer may want to single-step inside the
+ * handler too.
+ */
+ regs->flags &= ~X86_EFLAGS_TF;
+
+ spin_lock_irq(&current->sighand->siglock);
+ sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(&current->blocked, sig);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+
+ tracehook_signal_handler(sig, info, ka, regs,
+ test_thread_flag(TIF_SINGLESTEP));
+
+ return 0;
+}
+
+#ifdef CONFIG_X86_32
+#define NR_restart_syscall __NR_restart_syscall
+#else /* !CONFIG_X86_32 */
+#define NR_restart_syscall \
+ test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
+#endif /* CONFIG_X86_32 */
+
+/*
+ * 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.
+ */
+static void do_signal(struct pt_regs *regs)
+{
+ struct k_sigaction ka;
+ siginfo_t info;
+ int signr;
+ sigset_t *oldset;
+
+ /*
+ * We want the common case to go fast, which is why we may in certain
+ * cases get here from kernel mode. Just return without doing anything
+ * if so.
+ * X86_32: vm86 regs switched out by assembly code before reaching
+ * here, so testing against kernel CS suffices.
+ */
+ if (!user_mode(regs))
+ return;
+
+ if (current_thread_info()->status & TS_RESTORE_SIGMASK)
+ oldset = &current->saved_sigmask;
+ else
+ oldset = &current->blocked;
+
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /*
+ * Re-enable any watchpoints before delivering the
+ * signal to user space. The processor register will
+ * have been cleared if the watchpoint triggered
+ * inside the kernel.
+ */
+ if (current->thread.debugreg7)
+ set_debugreg(current->thread.debugreg7, 7);
+
+ /* Whee! Actually deliver the signal. */
+ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
+ /*
+ * A signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TS_RESTORE_SIGMASK flag.
+ */
+ current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+ }
+ return;
+ }
+
+ /* Did we come from a system call? */
+ if (syscall_get_nr(current, regs) >= 0) {
+ /* Restart the system call - no handlers present */
+ switch (syscall_get_error(current, regs)) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs->ax = regs->orig_ax;
+ regs->ip -= 2;
+ break;
+
+ case -ERESTART_RESTARTBLOCK:
+ regs->ax = NR_restart_syscall;
+ regs->ip -= 2;
+ break;
+ }
+ }
+
+ /*
+ * If there's no signal to deliver, we just put the saved sigmask
+ * back.
+ */
+ if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
+ current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
+ sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+ }
+}
+
+/*
+ * notification of userspace execution resumption
+ * - triggered by the TIF_WORK_MASK flags
+ */
+void
+do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
+{
+#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
+ /* notify userspace of pending MCEs */
+ if (thread_info_flags & _TIF_MCE_NOTIFY)
+ mce_notify_user();
+#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
+
+ /* deal with pending signal delivery */
+ if (thread_info_flags & _TIF_SIGPENDING)
+ do_signal(regs);
+
+ if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
+
+#ifdef CONFIG_X86_32
+ clear_thread_flag(TIF_IRET);
+#endif /* CONFIG_X86_32 */
+}
+
+void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
+{
+ struct task_struct *me = current;
+
+ if (show_unhandled_signals && printk_ratelimit()) {
+ printk("%s"
+ "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
+ task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG,
+ me->comm, me->pid, where, frame,
+ regs->ip, regs->sp, regs->orig_ax);
+ print_vma_addr(" in ", regs->ip);
+ printk(KERN_CONT "\n");
+ }
+
+ force_sig(SIGSEGV, me);
+}
diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
deleted file mode 100644
index d6dd057d0f22..000000000000
--- a/arch/x86/kernel/signal_32.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
- * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes
- */
-#include <linux/list.h>
-
-#include <linux/personality.h>
-#include <linux/binfmts.h>
-#include <linux/suspend.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/signal.h>
-#include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/tracehook.h>
-#include <linux/elf.h>
-#include <linux/smp.h>
-#include <linux/mm.h>
-
-#include <asm/processor.h>
-#include <asm/ucontext.h>
-#include <asm/uaccess.h>
-#include <asm/i387.h>
-#include <asm/vdso.h>
-#include <asm/syscall.h>
-#include <asm/syscalls.h>
-
-#include "sigframe.h"
-
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-#define __FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
- X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
- X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
- X86_EFLAGS_CF)
-
-#ifdef CONFIG_X86_32
-# define FIX_EFLAGS (__FIX_EFLAGS | X86_EFLAGS_RF)
-#else
-# define FIX_EFLAGS __FIX_EFLAGS
-#endif
-
-/*
- * Atomically swap in the new signal mask, and wait for a signal.
- */
-asmlinkage int
-sys_sigsuspend(int history0, int history1, old_sigset_t mask)
-{
- mask &= _BLOCKABLE;
- spin_lock_irq(&current->sighand->siglock);
- current->saved_sigmask = current->blocked;
- siginitset(&current->blocked, mask);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- set_restore_sigmask();
-
- return -ERESTARTNOHAND;
-}
-
-asmlinkage int
-sys_sigaction(int sig, const struct old_sigaction __user *act,
- struct old_sigaction __user *oact)
-{
- struct k_sigaction new_ka, old_ka;
- int ret;
-
- if (act) {
- old_sigset_t mask;
-
- if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
- __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
- __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
- return -EFAULT;
-
- __get_user(new_ka.sa.sa_flags, &act->sa_flags);
- __get_user(mask, &act->sa_mask);
- 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(old_ka.sa.sa_handler, &oact->sa_handler) ||
- __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
- return -EFAULT;
-
- __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
- __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
- }
-
- return ret;
-}
-
-asmlinkage int sys_sigaltstack(unsigned long bx)
-{
- /*
- * This is needed to make gcc realize it doesn't own the
- * "struct pt_regs"
- */
- struct pt_regs *regs = (struct pt_regs *)&bx;
- const stack_t __user *uss = (const stack_t __user *)bx;
- stack_t __user *uoss = (stack_t __user *)regs->cx;
-
- return do_sigaltstack(uss, uoss, regs->sp);
-}
-
-#define COPY(x) { \
- err |= __get_user(regs->x, &sc->x); \
-}
-
-#define COPY_SEG(seg) { \
- unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- regs->seg = tmp; \
-}
-
-#define COPY_SEG_STRICT(seg) { \
- unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- regs->seg = tmp | 3; \
-}
-
-#define GET_SEG(seg) { \
- unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- loadsegment(seg, tmp); \
-}
-
-/*
- * Do a signal return; undo the signal stack.
- */
-static int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
- unsigned long *pax)
-{
- void __user *buf;
- unsigned int tmpflags;
- unsigned int err = 0;
-
- /* Always make any pending restarted system calls return -EINTR */
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
- GET_SEG(gs);
- COPY_SEG(fs);
- COPY_SEG(es);
- COPY_SEG(ds);
- COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
- COPY(dx); COPY(cx); COPY(ip);
- COPY_SEG_STRICT(cs);
- COPY_SEG_STRICT(ss);
-
- err |= __get_user(tmpflags, &sc->flags);
- regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
- regs->orig_ax = -1; /* disable syscall checks */
-
- err |= __get_user(buf, &sc->fpstate);
- err |= restore_i387_xstate(buf);
-
- err |= __get_user(*pax, &sc->ax);
- return err;
-}
-
-asmlinkage unsigned long sys_sigreturn(unsigned long __unused)
-{
- struct sigframe __user *frame;
- struct pt_regs *regs;
- unsigned long ax;
- sigset_t set;
-
- regs = (struct pt_regs *) &__unused;
- frame = (struct sigframe __user *)(regs->sp - 8);
-
- if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
- goto badframe;
- if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1
- && __copy_from_user(&set.sig[1], &frame->extramask,
- sizeof(frame->extramask))))
- goto badframe;
-
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(&current->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- if (restore_sigcontext(regs, &frame->sc, &ax))
- goto badframe;
- return ax;
-
-badframe:
- if (show_unhandled_signals && printk_ratelimit()) {
- printk("%s%s[%d] bad frame in sigreturn frame:"
- "%p ip:%lx sp:%lx oeax:%lx",
- task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG,
- current->comm, task_pid_nr(current), frame, regs->ip,
- regs->sp, regs->orig_ax);
- print_vma_addr(" in ", regs->ip);
- printk(KERN_CONT "\n");
- }
-
- force_sig(SIGSEGV, current);
-
- return 0;
-}
-
-static long do_rt_sigreturn(struct pt_regs *regs)
-{
- struct rt_sigframe __user *frame;
- unsigned long ax;
- sigset_t set;
-
- frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
- if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
- goto badframe;
- if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
- goto badframe;
-
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(&current->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
- goto badframe;
-
- if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
- goto badframe;
-
- return ax;
-
-badframe:
- signal_fault(regs, frame, "rt_sigreturn");
- return 0;
-}
-
-asmlinkage int sys_rt_sigreturn(unsigned long __unused)
-{
- struct pt_regs *regs = (struct pt_regs *)&__unused;
-
- return do_rt_sigreturn(regs);
-}
-
-/*
- * Set up a signal frame.
- */
-static int
-setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
- struct pt_regs *regs, unsigned long mask)
-{
- int tmp, err = 0;
-
- err |= __put_user(regs->fs, (unsigned int __user *)&sc->fs);
- savesegment(gs, tmp);
- err |= __put_user(tmp, (unsigned int __user *)&sc->gs);
-
- err |= __put_user(regs->es, (unsigned int __user *)&sc->es);
- err |= __put_user(regs->ds, (unsigned int __user *)&sc->ds);
- err |= __put_user(regs->di, &sc->di);
- err |= __put_user(regs->si, &sc->si);
- err |= __put_user(regs->bp, &sc->bp);
- err |= __put_user(regs->sp, &sc->sp);
- err |= __put_user(regs->bx, &sc->bx);
- err |= __put_user(regs->dx, &sc->dx);
- err |= __put_user(regs->cx, &sc->cx);
- err |= __put_user(regs->ax, &sc->ax);
- err |= __put_user(current->thread.trap_no, &sc->trapno);
- err |= __put_user(current->thread.error_code, &sc->err);
- err |= __put_user(regs->ip, &sc->ip);
- err |= __put_user(regs->cs, (unsigned int __user *)&sc->cs);
- err |= __put_user(regs->flags, &sc->flags);
- err |= __put_user(regs->sp, &sc->sp_at_signal);
- err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
-
- tmp = save_i387_xstate(fpstate);
- if (tmp < 0)
- err = 1;
- else
- err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);
-
- /* non-iBCS2 extensions.. */
- err |= __put_user(mask, &sc->oldmask);
- err |= __put_user(current->thread.cr2, &sc->cr2);
-
- return err;
-}
-
-/*
- * Determine which stack to use..
- */
-static inline void __user *
-get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
- void **fpstate)
-{
- unsigned long sp;
-
- /* Default to using normal stack */
- sp = regs->sp;
-
- /*
- * If we are on the alternate signal stack and would overflow it, don't.
- * Return an always-bogus address instead so we will die with SIGSEGV.
- */
- if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size)))
- return (void __user *) -1L;
-
- /* This is the X/Open sanctioned signal stack switching. */
- if (ka->sa.sa_flags & SA_ONSTACK) {
- if (sas_ss_flags(sp) == 0)
- sp = current->sas_ss_sp + current->sas_ss_size;
- } else {
- /* This is the legacy signal stack switching. */
- if ((regs->ss & 0xffff) != __USER_DS &&
- !(ka->sa.sa_flags & SA_RESTORER) &&
- ka->sa.sa_restorer)
- sp = (unsigned long) ka->sa.sa_restorer;
- }
-
- if (used_math()) {
- sp = sp - sig_xstate_size;
- *fpstate = (struct _fpstate *) sp;
- }
-
- sp -= frame_size;
- /*
- * Align the stack pointer according to the i386 ABI,
- * i.e. so that on function entry ((sp + 4) & 15) == 0.
- */
- sp = ((sp + 4) & -16ul) - 4;
-
- return (void __user *) sp;
-}
-
-static int
-__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
- struct pt_regs *regs)
-{
- struct sigframe __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 (__put_user(sig, &frame->sig))
- return -EFAULT;
-
- if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
- return -EFAULT;
-
- if (_NSIG_WORDS > 1) {
- if (__copy_to_user(&frame->extramask, &set->sig[1],
- sizeof(frame->extramask)))
- return -EFAULT;
- }
-
- if (current->mm->context.vdso)
- restorer = VDSO32_SYMBOL(current->mm->context.vdso, sigreturn);
- else
- restorer = &frame->retcode;
- if (ka->sa.sa_flags & SA_RESTORER)
- restorer = ka->sa.sa_restorer;
-
- /* Set up to return from userspace. */
- err |= __put_user(restorer, &frame->pretcode);
-
- /*
- * This is popl %eax ; movl $__NR_sigreturn, %eax ; int $0x80
- *
- * WE DO NOT USE IT ANY MORE! It's only left here for historical
- * reasons and because gdb uses it as a signature to notice
- * signal handler stack frames.
- */
- err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
- err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
- err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
-
- if (err)
- return -EFAULT;
-
- /* Set up registers for signal handler */
- regs->sp = (unsigned long)frame;
- regs->ip = (unsigned long)ka->sa.sa_handler;
- regs->ax = (unsigned long)sig;
- regs->dx = 0;
- regs->cx = 0;
-
- regs->ds = __USER_DS;
- regs->es = __USER_DS;
- regs->ss = __USER_DS;
- regs->cs = __USER_CS;
-
- return 0;
-}
-
-static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
-{
- struct rt_sigframe __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;
-
- err |= __put_user(sig, &frame->sig);
- err |= __put_user(&frame->info, &frame->pinfo);
- err |= __put_user(&frame->uc, &frame->puc);
- err |= copy_siginfo_to_user(&frame->info, info);
- if (err)
- return -EFAULT;
-
- /* Create the ucontext. */
- if (cpu_has_xsave)
- err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
- else
- err |= __put_user(0, &frame->uc.uc_flags);
- err |= __put_user(0, &frame->uc.uc_link);
- err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
- err |= __put_user(sas_ss_flags(regs->sp),
- &frame->uc.uc_stack.ss_flags);
- err |= __put_user(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));
- if (err)
- return -EFAULT;
-
- /* Set up to return from userspace. */
- restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
- if (ka->sa.sa_flags & SA_RESTORER)
- restorer = ka->sa.sa_restorer;
- err |= __put_user(restorer, &frame->pretcode);
-
- /*
- * This is movl $__NR_rt_sigreturn, %ax ; int $0x80
- *
- * WE DO NOT USE IT ANY MORE! It's only left here for historical
- * reasons and because gdb uses it as a signature to notice
- * signal handler stack frames.
- */
- err |= __put_user(0xb8, (char __user *)(frame->retcode+0));
- err |= __put_user(__NR_rt_sigreturn, (int __user *)(frame->retcode+1));
- err |= __put_user(0x80cd, (short __user *)(frame->retcode+5));
-
- if (err)
- return -EFAULT;
-
- /* Set up registers for signal handler */
- regs->sp = (unsigned long)frame;
- regs->ip = (unsigned long)ka->sa.sa_handler;
- regs->ax = (unsigned long)sig;
- regs->dx = (unsigned long)&frame->info;
- regs->cx = (unsigned long)&frame->uc;
-
- regs->ds = __USER_DS;
- regs->es = __USER_DS;
- regs->ss = __USER_DS;
- regs->cs = __USER_CS;
-
- return 0;
-}
-
-/*
- * OK, we're invoking a handler:
- */
-static int signr_convert(int sig)
-{
- struct thread_info *info = current_thread_info();
-
- if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32)
- return info->exec_domain->signal_invmap[sig];
- return sig;
-}
-
-#define is_ia32 1
-#define ia32_setup_frame __setup_frame
-#define ia32_setup_rt_frame __setup_rt_frame
-
-static int
-setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
-{
- int usig = signr_convert(sig);
- int ret;
-
- /* Set up the stack frame */
- if (is_ia32) {
- if (ka->sa.sa_flags & SA_SIGINFO)
- ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
- else
- ret = ia32_setup_frame(usig, ka, set, regs);
- } else
- ret = __setup_rt_frame(sig, ka, info, set, regs);
-
- if (ret) {
- force_sigsegv(sig, current);
- return -EFAULT;
- }
-
- return ret;
-}
-
-static int
-handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
- sigset_t *oldset, struct pt_regs *regs)
-{
- int ret;
-
- /* Are we from a system call? */
- if (syscall_get_nr(current, regs) >= 0) {
- /* If so, check system call restarting.. */
- switch (syscall_get_error(current, regs)) {
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- regs->ax = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART)) {
- regs->ax = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- regs->ax = regs->orig_ax;
- regs->ip -= 2;
- break;
- }
- }
-
- /*
- * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
- * flag so that register information in the sigcontext is correct.
- */
- if (unlikely(regs->flags & X86_EFLAGS_TF) &&
- likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
- regs->flags &= ~X86_EFLAGS_TF;
-
- ret = setup_rt_frame(sig, ka, info, oldset, regs);
-
- if (ret)
- return ret;
-
-#ifdef CONFIG_X86_64
- /*
- * This has nothing to do with segment registers,
- * despite the name. This magic affects uaccess.h
- * macros' behavior. Reset it to the normal setting.
- */
- set_fs(USER_DS);
-#endif
-
- /*
- * Clear the direction flag as per the ABI for function entry.
- */
- regs->flags &= ~X86_EFLAGS_DF;
-
- /*
- * Clear TF when entering the signal handler, but
- * notify any tracer that was single-stepping it.
- * The tracer may want to single-step inside the
- * handler too.
- */
- regs->flags &= ~X86_EFLAGS_TF;
-
- spin_lock_irq(&current->sighand->siglock);
- sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(&current->blocked, sig);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- tracehook_signal_handler(sig, info, ka, regs,
- test_thread_flag(TIF_SINGLESTEP));
-
- return 0;
-}
-
-#define NR_restart_syscall __NR_restart_syscall
-/*
- * 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.
- */
-static void do_signal(struct pt_regs *regs)
-{
- struct k_sigaction ka;
- siginfo_t info;
- int signr;
- sigset_t *oldset;
-
- /*
- * We want the common case to go fast, which is why we may in certain
- * cases get here from kernel mode. Just return without doing anything
- * if so.
- * X86_32: vm86 regs switched out by assembly code before reaching
- * here, so testing against kernel CS suffices.
- */
- if (!user_mode(regs))
- return;
-
- if (current_thread_info()->status & TS_RESTORE_SIGMASK)
- oldset = &current->saved_sigmask;
- else
- oldset = &current->blocked;
-
- signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- if (signr > 0) {
- /*
- * Re-enable any watchpoints before delivering the
- * signal to user space. The processor register will
- * have been cleared if the watchpoint triggered
- * inside the kernel.
- */
- if (current->thread.debugreg7)
- set_debugreg(current->thread.debugreg7, 7);
-
- /* Whee! Actually deliver the signal. */
- if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
- /*
- * A signal was successfully delivered; the saved
- * sigmask will have been stored in the signal frame,
- * and will be restored by sigreturn, so we can simply
- * clear the TS_RESTORE_SIGMASK flag.
- */
- current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
- }
- return;
- }
-
- /* Did we come from a system call? */
- if (syscall_get_nr(current, regs) >= 0) {
- /* Restart the system call - no handlers present */
- switch (syscall_get_error(current, regs)) {
- case -ERESTARTNOHAND:
- case -ERESTARTSYS:
- case -ERESTARTNOINTR:
- regs->ax = regs->orig_ax;
- regs->ip -= 2;
- break;
-
- case -ERESTART_RESTARTBLOCK:
- regs->ax = NR_restart_syscall;
- regs->ip -= 2;
- break;
- }
- }
-
- /*
- * If there's no signal to deliver, we just put the saved sigmask
- * back.
- */
- if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
- current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
- sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
- }
-}
-
-/*
- * notification of userspace execution resumption
- * - triggered by the TIF_WORK_MASK flags
- */
-void
-do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
-{
-#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
- /* notify userspace of pending MCEs */
- if (thread_info_flags & _TIF_MCE_NOTIFY)
- mce_notify_user();
-#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
-
- /* deal with pending signal delivery */
- if (thread_info_flags & _TIF_SIGPENDING)
- do_signal(regs);
-
- if (thread_info_flags & _TIF_NOTIFY_RESUME) {
- clear_thread_flag(TIF_NOTIFY_RESUME);
- tracehook_notify_resume(regs);
- }
-
-#ifdef CONFIG_X86_32
- clear_thread_flag(TIF_IRET);
-#endif /* CONFIG_X86_32 */
-}
-
-void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
-{
- struct task_struct *me = current;
-
- if (show_unhandled_signals && printk_ratelimit()) {
- printk(KERN_INFO
- "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
- me->comm, me->pid, where, frame,
- regs->ip, regs->sp, regs->orig_ax);
- print_vma_addr(" in ", regs->ip);
- printk(KERN_CONT "\n");
- }
-
- force_sig(SIGSEGV, me);
-}
diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
deleted file mode 100644
index a5c9627f4db9..000000000000
--- a/arch/x86/kernel/signal_64.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Copyright (C) 1991, 1992 Linus Torvalds
- * Copyright (C) 2000, 2001, 2002 Andi Kleen SuSE Labs
- *
- * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
- * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes
- * 2000-2002 x86-64 support by Andi Kleen
- */
-
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/wait.h>
-#include <linux/ptrace.h>
-#include <linux/tracehook.h>
-#include <linux/unistd.h>
-#include <linux/stddef.h>
-#include <linux/personality.h>
-#include <linux/compiler.h>
-#include <linux/uaccess.h>
-
-#include <asm/processor.h>
-#include <asm/ucontext.h>
-#include <asm/i387.h>
-#include <asm/proto.h>
-#include <asm/ia32_unistd.h>
-#include <asm/mce.h>
-#include <asm/syscall.h>
-#include <asm/syscalls.h>
-#include "sigframe.h"
-
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
-#define __FIX_EFLAGS (X86_EFLAGS_AC | X86_EFLAGS_OF | \
- X86_EFLAGS_DF | X86_EFLAGS_TF | X86_EFLAGS_SF | \
- X86_EFLAGS_ZF | X86_EFLAGS_AF | X86_EFLAGS_PF | \
- X86_EFLAGS_CF)
-
-#ifdef CONFIG_X86_32
-# define FIX_EFLAGS (__FIX_EFLAGS | X86_EFLAGS_RF)
-#else
-# define FIX_EFLAGS __FIX_EFLAGS
-#endif
-
-asmlinkage long
-sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
- struct pt_regs *regs)
-{
- return do_sigaltstack(uss, uoss, regs->sp);
-}
-
-#define COPY(x) { \
- err |= __get_user(regs->x, &sc->x); \
-}
-
-#define COPY_SEG_STRICT(seg) { \
- unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- regs->seg = tmp | 3; \
-}
-
-/*
- * Do a signal return; undo the signal stack.
- */
-static int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
- unsigned long *pax)
-{
- void __user *buf;
- unsigned int tmpflags;
- unsigned int err = 0;
-
- /* Always make any pending restarted system calls return -EINTR */
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
-
- COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
- COPY(dx); COPY(cx); COPY(ip);
- COPY(r8);
- COPY(r9);
- COPY(r10);
- COPY(r11);
- COPY(r12);
- COPY(r13);
- COPY(r14);
- COPY(r15);
-
- /* Kernel saves and restores only the CS segment register on signals,
- * which is the bare minimum needed to allow mixed 32/64-bit code.
- * App's signal handler can save/restore other segments if needed. */
- COPY_SEG_STRICT(cs);
-
- err |= __get_user(tmpflags, &sc->flags);
- regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
- regs->orig_ax = -1; /* disable syscall checks */
-
- err |= __get_user(buf, &sc->fpstate);
- err |= restore_i387_xstate(buf);
-
- err |= __get_user(*pax, &sc->ax);
- return err;
-}
-
-static long do_rt_sigreturn(struct pt_regs *regs)
-{
- struct rt_sigframe __user *frame;
- unsigned long ax;
- sigset_t set;
-
- frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
- if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
- goto badframe;
- if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
- goto badframe;
-
- sigdelsetmask(&set, ~_BLOCKABLE);
- spin_lock_irq(&current->sighand->siglock);
- current->blocked = set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax))
- goto badframe;
-
- if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
- goto badframe;
-
- return ax;
-
-badframe:
- signal_fault(regs, frame, "rt_sigreturn");
- return 0;
-}
-
-asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
-{
- return do_rt_sigreturn(regs);
-}
-
-/*
- * Set up a signal frame.
- */
-
-static inline int
-setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
- unsigned long mask, struct task_struct *me)
-{
- int err = 0;
-
- err |= __put_user(regs->cs, &sc->cs);
- err |= __put_user(0, &sc->gs);
- err |= __put_user(0, &sc->fs);
-
- err |= __put_user(regs->di, &sc->di);
- err |= __put_user(regs->si, &sc->si);
- err |= __put_user(regs->bp, &sc->bp);
- err |= __put_user(regs->sp, &sc->sp);
- err |= __put_user(regs->bx, &sc->bx);
- err |= __put_user(regs->dx, &sc->dx);
- err |= __put_user(regs->cx, &sc->cx);
- err |= __put_user(regs->ax, &sc->ax);
- err |= __put_user(regs->r8, &sc->r8);
- err |= __put_user(regs->r9, &sc->r9);
- err |= __put_user(regs->r10, &sc->r10);
- err |= __put_user(regs->r11, &sc->r11);
- err |= __put_user(regs->r12, &sc->r12);
- err |= __put_user(regs->r13, &sc->r13);
- err |= __put_user(regs->r14, &sc->r14);
- err |= __put_user(regs->r15, &sc->r15);
- err |= __put_user(me->thread.trap_no, &sc->trapno);
- err |= __put_user(me->thread.error_code, &sc->err);
- err |= __put_user(regs->ip, &sc->ip);
- err |= __put_user(regs->flags, &sc->flags);
- err |= __put_user(mask, &sc->oldmask);
- err |= __put_user(me->thread.cr2, &sc->cr2);
-
- return err;
-}
-
-/*
- * Determine which stack to use..
- */
-
-static void __user *
-get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size)
-{
- unsigned long sp;
-
- /* Default to using normal stack - redzone*/
- sp = regs->sp - 128;
-
- /* This is the X/Open sanctioned signal stack switching. */
- if (ka->sa.sa_flags & SA_ONSTACK) {
- if (sas_ss_flags(sp) == 0)
- sp = current->sas_ss_sp + current->sas_ss_size;
- }
-
- return (void __user *)round_down(sp - size, 64);
-}
-
-static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
-{
- struct rt_sigframe __user *frame;
- void __user *fp = NULL;
- int err = 0;
- struct task_struct *me = current;
-
- if (used_math()) {
- fp = get_stack(ka, regs, sig_xstate_size);
- frame = (void __user *)round_down(
- (unsigned long)fp - sizeof(struct rt_sigframe), 16) - 8;
-
- if (save_i387_xstate(fp) < 0)
- return -EFAULT;
- } else
- frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8;
-
- if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- return -EFAULT;
-
- if (ka->sa.sa_flags & SA_SIGINFO) {
- if (copy_siginfo_to_user(&frame->info, info))
- return -EFAULT;
- }
-
- /* Create the ucontext. */
- if (cpu_has_xsave)
- err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
- else
- err |= __put_user(0, &frame->uc.uc_flags);
- err |= __put_user(0, &frame->uc.uc_link);
- err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
- err |= __put_user(sas_ss_flags(regs->sp),
- &frame->uc.uc_stack.ss_flags);
- err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0], me);
- err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate);
- if (sizeof(*set) == 16) {
- __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
- __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
- } else
- 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. */
- /* x86-64 should always use SA_RESTORER. */
- if (ka->sa.sa_flags & SA_RESTORER) {
- err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
- } else {
- /* could use a vstub here */
- return -EFAULT;
- }
-
- if (err)
- return -EFAULT;
-
- /* Set up registers for signal handler */
- regs->di = sig;
- /* In case the signal handler was declared without prototypes */
- regs->ax = 0;
-
- /* This also works for non SA_SIGINFO handlers because they expect the
- next argument after the signal number on the stack. */
- regs->si = (unsigned long)&frame->info;
- regs->dx = (unsigned long)&frame->uc;
- regs->ip = (unsigned long) ka->sa.sa_handler;
-
- regs->sp = (unsigned long)frame;
-
- /* Set up the CS register to run signal handlers in 64-bit mode,
- even if the handler happens to be interrupting 32-bit code. */
- regs->cs = __USER_CS;
-
- return 0;
-}
-
-/*
- * OK, we're invoking a handler
- */
-static int signr_convert(int sig)
-{
- return sig;
-}
-
-#ifdef CONFIG_IA32_EMULATION
-#define is_ia32 test_thread_flag(TIF_IA32)
-#else
-#define is_ia32 0
-#endif
-
-static int
-setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
-{
- int usig = signr_convert(sig);
- int ret;
-
- /* Set up the stack frame */
- if (is_ia32) {
- if (ka->sa.sa_flags & SA_SIGINFO)
- ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
- else
- ret = ia32_setup_frame(usig, ka, set, regs);
- } else
- ret = __setup_rt_frame(sig, ka, info, set, regs);
-
- if (ret) {
- force_sigsegv(sig, current);
- return -EFAULT;
- }
-
- return ret;
-}
-
-static int
-handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
- sigset_t *oldset, struct pt_regs *regs)
-{
- int ret;
-
- /* Are we from a system call? */
- if (syscall_get_nr(current, regs) >= 0) {
- /* If so, check system call restarting.. */
- switch (syscall_get_error(current, regs)) {
- case -ERESTART_RESTARTBLOCK:
- case -ERESTARTNOHAND:
- regs->ax = -EINTR;
- break;
-
- case -ERESTARTSYS:
- if (!(ka->sa.sa_flags & SA_RESTART)) {
- regs->ax = -EINTR;
- break;
- }
- /* fallthrough */
- case -ERESTARTNOINTR:
- regs->ax = regs->orig_ax;
- regs->ip -= 2;
- break;
- }
- }
-
- /*
- * If TF is set due to a debugger (TIF_FORCED_TF), clear the TF
- * flag so that register information in the sigcontext is correct.
- */
- if (unlikely(regs->flags & X86_EFLAGS_TF) &&
- likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
- regs->flags &= ~X86_EFLAGS_TF;
-
- ret = setup_rt_frame(sig, ka, info, oldset, regs);
-
- if (ret)
- return ret;
-
-#ifdef CONFIG_X86_64
- /*
- * This has nothing to do with segment registers,
- * despite the name. This magic affects uaccess.h
- * macros' behavior. Reset it to the normal setting.
- */
- set_fs(USER_DS);
-#endif
-
- /*
- * Clear the direction flag as per the ABI for function entry.
- */
- regs->flags &= ~X86_EFLAGS_DF;
-
- /*
- * Clear TF when entering the signal handler, but
- * notify any tracer that was single-stepping it.
- * The tracer may want to single-step inside the
- * handler too.
- */
- regs->flags &= ~X86_EFLAGS_TF;
-
- spin_lock_irq(&current->sighand->siglock);
- sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(&current->blocked, sig);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
- tracehook_signal_handler(sig, info, ka, regs,
- test_thread_flag(TIF_SINGLESTEP));
-
- return 0;
-}
-
-#define NR_restart_syscall \
- test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
-/*
- * 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.
- */
-static void do_signal(struct pt_regs *regs)
-{
- struct k_sigaction ka;
- siginfo_t info;
- int signr;
- sigset_t *oldset;
-
- /*
- * We want the common case to go fast, which is why we may in certain
- * cases get here from kernel mode. Just return without doing anything
- * if so.
- * X86_32: vm86 regs switched out by assembly code before reaching
- * here, so testing against kernel CS suffices.
- */
- if (!user_mode(regs))
- return;
-
- if (current_thread_info()->status & TS_RESTORE_SIGMASK)
- oldset = &current->saved_sigmask;
- else
- oldset = &current->blocked;
-
- signr = get_signal_to_deliver(&info, &ka, regs, NULL);
- if (signr > 0) {
- /*
- * Re-enable any watchpoints before delivering the
- * signal to user space. The processor register will
- * have been cleared if the watchpoint triggered
- * inside the kernel.
- */
- if (current->thread.debugreg7)
- set_debugreg(current->thread.debugreg7, 7);
-
- /* Whee! Actually deliver the signal. */
- if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
- /*
- * A signal was successfully delivered; the saved
- * sigmask will have been stored in the signal frame,
- * and will be restored by sigreturn, so we can simply
- * clear the TS_RESTORE_SIGMASK flag.
- */
- current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
- }
- return;
- }
-
- /* Did we come from a system call? */
- if (syscall_get_nr(current, regs) >= 0) {
- /* Restart the system call - no handlers present */
- switch (syscall_get_error(current, regs)) {
- case -ERESTARTNOHAND:
- case -ERESTARTSYS:
- case -ERESTARTNOINTR:
- regs->ax = regs->orig_ax;
- regs->ip -= 2;
- break;
-
- case -ERESTART_RESTARTBLOCK:
- regs->ax = NR_restart_syscall;
- regs->ip -= 2;
- break;
- }
- }
-
- /*
- * If there's no signal to deliver, we just put the saved sigmask
- * back.
- */
- if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
- current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
- sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
- }
-}
-
-/*
- * notification of userspace execution resumption
- * - triggered by the TIF_WORK_MASK flags
- */
-void
-do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
-{
-#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
- /* notify userspace of pending MCEs */
- if (thread_info_flags & _TIF_MCE_NOTIFY)
- mce_notify_user();
-#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
-
- /* deal with pending signal delivery */
- if (thread_info_flags & _TIF_SIGPENDING)
- do_signal(regs);
-
- if (thread_info_flags & _TIF_NOTIFY_RESUME) {
- clear_thread_flag(TIF_NOTIFY_RESUME);
- tracehook_notify_resume(regs);
- }
-
-#ifdef CONFIG_X86_32
- clear_thread_flag(TIF_IRET);
-#endif /* CONFIG_X86_32 */
-}
-
-void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
-{
- struct task_struct *me = current;
-
- if (show_unhandled_signals && printk_ratelimit()) {
- printk(KERN_INFO
- "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
- me->comm, me->pid, where, frame,
- regs->ip, regs->sp, regs->orig_ax);
- print_vma_addr(" in ", regs->ip);
- printk(KERN_CONT "\n");
- }
-
- force_sig(SIGSEGV, me);
-}
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 49ed667b06f3..beea2649a240 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -165,11 +165,7 @@ static void native_smp_send_stop(void)
void smp_reschedule_interrupt(struct pt_regs *regs)
{
ack_APIC_irq();
-#ifdef CONFIG_X86_32
- __get_cpu_var(irq_stat).irq_resched_count++;
-#else
- add_pda(irq_resched_count, 1);
-#endif
+ inc_irq_stat(irq_resched_count);
}
void smp_call_function_interrupt(struct pt_regs *regs)
@@ -177,11 +173,7 @@ void smp_call_function_interrupt(struct pt_regs *regs)
ack_APIC_irq();
irq_enter();
generic_smp_call_function_interrupt();
-#ifdef CONFIG_X86_32
- __get_cpu_var(irq_stat).irq_call_count++;
-#else
- add_pda(irq_call_count, 1);
-#endif
+ inc_irq_stat(irq_call_count);
irq_exit();
}
@@ -190,11 +182,7 @@ void smp_call_function_single_interrupt(struct pt_regs *regs)
ack_APIC_irq();
irq_enter();
generic_smp_call_function_single_interrupt();
-#ifdef CONFIG_X86_32
- __get_cpu_var(irq_stat).irq_call_count++;
-#else
- add_pda(irq_call_count, 1);
-#endif
+ inc_irq_stat(irq_call_count);
irq_exit();
}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 1a9941b11150..9e177a4077ee 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -282,7 +282,7 @@ static int __cpuinitdata unsafe_smp;
/*
* Activate a secondary processor.
*/
-static void __cpuinit start_secondary(void *unused)
+notrace static void __cpuinit start_secondary(void *unused)
{
/*
* Don't put *anything* before cpu_init(), SMP booting is too
@@ -496,7 +496,7 @@ void __cpuinit set_cpu_sibling_map(int cpu)
}
/* maps the cpu to the sched domain representing multi-core */
-cpumask_t cpu_coregroup_map(int cpu)
+const struct cpumask *cpu_coregroup_mask(int cpu)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
/*
@@ -504,9 +504,14 @@ cpumask_t cpu_coregroup_map(int cpu)
* And for power savings, we return cpu_core_map
*/
if (sched_mc_power_savings || sched_smt_power_savings)
- return per_cpu(cpu_core_map, cpu);
+ return &per_cpu(cpu_core_map, cpu);
else
- return c->llc_shared_map;
+ return &c->llc_shared_map;
+}
+
+cpumask_t cpu_coregroup_map(int cpu)
+{
+ return *cpu_coregroup_mask(cpu);
}
static void impress_friends(void)
@@ -1075,8 +1080,10 @@ static int __init smp_sanity_check(unsigned max_cpus)
#endif
if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) {
- printk(KERN_WARNING "weird, boot CPU (#%d) not listed"
- "by the BIOS.\n", hard_smp_processor_id());
+ printk(KERN_WARNING
+ "weird, boot CPU (#%d) not listed by the BIOS.\n",
+ hard_smp_processor_id());
+
physid_set(hard_smp_processor_id(), phys_cpu_present_map);
}
diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c
index 77b400f06ea2..65309e4cb1c0 100644
--- a/arch/x86/kernel/time_32.c
+++ b/arch/x86/kernel/time_32.c
@@ -75,7 +75,7 @@ EXPORT_SYMBOL(profile_pc);
irqreturn_t timer_interrupt(int irq, void *dev_id)
{
/* Keep nmi watchdog up to date */
- per_cpu(irq_stat, smp_processor_id()).irq0_irqs++;
+ inc_irq_stat(irq0_irqs);
#ifdef CONFIG_X86_IO_APIC
if (timer_ack) {
diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c
index cb19d650c216..891e7a7c4334 100644
--- a/arch/x86/kernel/time_64.c
+++ b/arch/x86/kernel/time_64.c
@@ -49,9 +49,9 @@ unsigned long profile_pc(struct pt_regs *regs)
}
EXPORT_SYMBOL(profile_pc);
-irqreturn_t timer_interrupt(int irq, void *dev_id)
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
- add_pda(irq0_irqs, 1);
+ inc_irq_stat(irq0_irqs);
global_clock_event->event_handler(global_clock_event);
@@ -80,6 +80,8 @@ unsigned long __init calibrate_cpu(void)
break;
no_ctr_free = (i == 4);
if (no_ctr_free) {
+ WARN(1, KERN_WARNING "Warning: AMD perfctrs busy ... "
+ "cpu_khz value may be incorrect.\n");
i = 3;
rdmsrl(MSR_K7_EVNTSEL3, evntsel3);
wrmsrl(MSR_K7_EVNTSEL3, 0);
diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c
index 174ea90d1cbd..ce5054642247 100644
--- a/arch/x86/kernel/tlb_32.c
+++ b/arch/x86/kernel/tlb_32.c
@@ -34,9 +34,8 @@ static DEFINE_SPINLOCK(tlbstate_lock);
*/
void leave_mm(int cpu)
{
- if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK)
- BUG();
- cpu_clear(cpu, per_cpu(cpu_tlbstate, cpu).active_mm->cpu_vm_mask);
+ BUG_ON(x86_read_percpu(cpu_tlbstate.state) == TLBSTATE_OK);
+ cpu_clear(cpu, x86_read_percpu(cpu_tlbstate.active_mm)->cpu_vm_mask);
load_cr3(swapper_pg_dir);
}
EXPORT_SYMBOL_GPL(leave_mm);
@@ -104,8 +103,8 @@ void smp_invalidate_interrupt(struct pt_regs *regs)
* BUG();
*/
- if (flush_mm == per_cpu(cpu_tlbstate, cpu).active_mm) {
- if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) {
+ if (flush_mm == x86_read_percpu(cpu_tlbstate.active_mm)) {
+ if (x86_read_percpu(cpu_tlbstate.state) == TLBSTATE_OK) {
if (flush_va == TLB_FLUSH_ALL)
local_flush_tlb();
else
@@ -119,7 +118,7 @@ void smp_invalidate_interrupt(struct pt_regs *regs)
smp_mb__after_clear_bit();
out:
put_cpu_no_resched();
- __get_cpu_var(irq_stat).irq_tlb_count++;
+ inc_irq_stat(irq_tlb_count);
}
void native_flush_tlb_others(const cpumask_t *cpumaskp, struct mm_struct *mm,
@@ -238,7 +237,7 @@ static void do_flush_tlb_all(void *info)
unsigned long cpu = smp_processor_id();
__flush_tlb_all();
- if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_LAZY)
+ if (x86_read_percpu(cpu_tlbstate.state) == TLBSTATE_LAZY)
leave_mm(cpu);
}
diff --git a/arch/x86/kernel/tlb_64.c b/arch/x86/kernel/tlb_64.c
index de6f1bda0c50..f8be6f1d2e48 100644
--- a/arch/x86/kernel/tlb_64.c
+++ b/arch/x86/kernel/tlb_64.c
@@ -154,7 +154,7 @@ asmlinkage void smp_invalidate_interrupt(struct pt_regs *regs)
out:
ack_APIC_irq();
cpu_clear(cpu, f->flush_cpumask);
- add_pda(irq_tlb_count, 1);
+ inc_irq_stat(irq_tlb_count);
}
void native_flush_tlb_others(const cpumask_t *cpumaskp, struct mm_struct *mm,
diff --git a/arch/x86/kernel/tlb_uv.c b/arch/x86/kernel/tlb_uv.c
index 04431f34fd16..6a00e5faaa74 100644
--- a/arch/x86/kernel/tlb_uv.c
+++ b/arch/x86/kernel/tlb_uv.c
@@ -566,14 +566,10 @@ static int __init uv_ptc_init(void)
if (!is_uv_system())
return 0;
- if (!proc_mkdir("sgi_uv", NULL))
- return -EINVAL;
-
proc_uv_ptc = create_proc_entry(UV_PTC_BASENAME, 0444, NULL);
if (!proc_uv_ptc) {
printk(KERN_ERR "unable to create %s proc entry\n",
UV_PTC_BASENAME);
- remove_proc_entry("sgi_uv", NULL);
return -EINVAL;
}
proc_uv_ptc->proc_fops = &proc_uv_ptc_operations;
diff --git a/arch/x86/kernel/trampoline.c b/arch/x86/kernel/trampoline.c
index 1106fac6024d..808031a5ba19 100644
--- a/arch/x86/kernel/trampoline.c
+++ b/arch/x86/kernel/trampoline.c
@@ -1,10 +1,26 @@
#include <linux/io.h>
#include <asm/trampoline.h>
+#include <asm/e820.h>
/* ready for x86_64 and x86 */
unsigned char *trampoline_base = __va(TRAMPOLINE_BASE);
+void __init reserve_trampoline_memory(void)
+{
+#ifdef CONFIG_X86_32
+ /*
+ * But first pinch a few for the stack/trampoline stuff
+ * FIXME: Don't need the extra page at 4K, but need to fix
+ * trampoline before removing it. (see the GDT stuff)
+ */
+ reserve_early(PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE");
+#endif
+ /* Has to be in very low memory so we can execute real-mode AP code. */
+ reserve_early(TRAMPOLINE_BASE, TRAMPOLINE_BASE + TRAMPOLINE_SIZE,
+ "TRAMPOLINE");
+}
+
/*
* Currently trivial. Write the real->protected mode
* bootstrap into the page concerned. The caller
@@ -12,7 +28,6 @@ unsigned char *trampoline_base = __va(TRAMPOLINE_BASE);
*/
unsigned long setup_trampoline(void)
{
- memcpy(trampoline_base, trampoline_data,
- trampoline_end - trampoline_data);
+ memcpy(trampoline_base, trampoline_data, TRAMPOLINE_SIZE);
return virt_to_phys(trampoline_base);
}
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 4a6dff39a470..2d1f4c7e4052 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -481,11 +481,7 @@ do_nmi(struct pt_regs *regs, long error_code)
{
nmi_enter();
-#ifdef CONFIG_X86_32
- { int cpu; cpu = smp_processor_id(); ++nmi_count(cpu); }
-#else
- add_pda(__nmi_count, 1);
-#endif
+ inc_irq_stat(__nmi_count);
if (!ignore_nmis)
default_do_nmi(regs);
@@ -664,7 +660,7 @@ void math_error(void __user *ip)
{
struct task_struct *task;
siginfo_t info;
- unsigned short cwd, swd;
+ unsigned short cwd, swd, err;
/*
* Save the info for the exception handler and clear the error.
@@ -675,7 +671,6 @@ void math_error(void __user *ip)
task->thread.error_code = 0;
info.si_signo = SIGFPE;
info.si_errno = 0;
- info.si_code = __SI_FAULT;
info.si_addr = ip;
/*
* (~cwd & swd) will mask out exceptions that are not set to unmasked
@@ -689,34 +684,31 @@ void math_error(void __user *ip)
*/
cwd = get_fpu_cwd(task);
swd = get_fpu_swd(task);
- switch (swd & ~cwd & 0x3f) {
- case 0x000: /* No unmasked exception */
+
+ err = swd & ~cwd & 0x3f;
+
#ifdef CONFIG_X86_32
+ if (!err)
return;
#endif
- default: /* Multiple exceptions */
- break;
- case 0x001: /* Invalid Op */
+
+ if (err & 0x001) { /* Invalid op */
/*
* swd & 0x240 == 0x040: Stack Underflow
* swd & 0x240 == 0x240: Stack Overflow
* User must clear the SF bit (0x40) if set
*/
info.si_code = FPE_FLTINV;
- break;
- case 0x002: /* Denormalize */
- case 0x010: /* Underflow */
- info.si_code = FPE_FLTUND;
- break;
- case 0x004: /* Zero Divide */
+ } else if (err & 0x004) { /* Divide by Zero */
info.si_code = FPE_FLTDIV;
- break;
- case 0x008: /* Overflow */
+ } else if (err & 0x008) { /* Overflow */
info.si_code = FPE_FLTOVF;
- break;
- case 0x020: /* Precision */
+ } else if (err & 0x012) { /* Denormal, Underflow */
+ info.si_code = FPE_FLTUND;
+ } else if (err & 0x020) { /* Precision */
info.si_code = FPE_FLTRES;
- break;
+ } else {
+ info.si_code = __SI_FAULT|SI_KERNEL; /* WTF? */
}
force_sig_info(SIGFPE, &info, task);
}
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 424093b157d3..599e58168631 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -15,6 +15,7 @@
#include <asm/vgtod.h>
#include <asm/time.h>
#include <asm/delay.h>
+#include <asm/hypervisor.h>
unsigned int cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz);
@@ -31,6 +32,7 @@ static int tsc_unstable;
erroneous rdtsc usage on !cpu_has_tsc processors */
static int tsc_disabled = -1;
+static int tsc_clocksource_reliable;
/*
* Scheduler clock - returns current time in nanosec units.
*/
@@ -98,6 +100,15 @@ int __init notsc_setup(char *str)
__setup("notsc", notsc_setup);
+static int __init tsc_setup(char *str)
+{
+ if (!strcmp(str, "reliable"))
+ tsc_clocksource_reliable = 1;
+ return 1;
+}
+
+__setup("tsc=", tsc_setup);
+
#define MAX_RETRIES 5
#define SMI_TRESHOLD 50000
@@ -352,9 +363,15 @@ unsigned long native_calibrate_tsc(void)
{
u64 tsc1, tsc2, delta, ref1, ref2;
unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
- unsigned long flags, latch, ms, fast_calibrate;
+ unsigned long flags, latch, ms, fast_calibrate, tsc_khz;
int hpet = is_hpet_enabled(), i, loopmin;
+ tsc_khz = get_hypervisor_tsc_freq();
+ if (tsc_khz) {
+ printk(KERN_INFO "TSC: Frequency read from the hypervisor\n");
+ return tsc_khz;
+ }
+
local_irq_save(flags);
fast_calibrate = quick_pit_calibrate();
local_irq_restore(flags);
@@ -731,24 +748,21 @@ static struct dmi_system_id __initdata bad_tsc_dmi_table[] = {
{}
};
-/*
- * Geode_LX - the OLPC CPU has a possibly a very reliable TSC
- */
+static void __init check_system_tsc_reliable(void)
+{
#ifdef CONFIG_MGEODE_LX
-/* RTSC counts during suspend */
+ /* RTSC counts during suspend */
#define RTSC_SUSP 0x100
-
-static void __init check_geode_tsc_reliable(void)
-{
unsigned long res_low, res_high;
rdmsr_safe(MSR_GEODE_BUSCONT_CONF0, &res_low, &res_high);
+ /* Geode_LX - the OLPC CPU has a possibly a very reliable TSC */
if (res_low & RTSC_SUSP)
- clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
-}
-#else
-static inline void check_geode_tsc_reliable(void) { }
+ tsc_clocksource_reliable = 1;
#endif
+ if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
+ tsc_clocksource_reliable = 1;
+}
/*
* Make an educated guess if the TSC is trustworthy and synchronized
@@ -783,6 +797,8 @@ static void __init init_tsc_clocksource(void)
{
clocksource_tsc.mult = clocksource_khz2mult(tsc_khz,
clocksource_tsc.shift);
+ if (tsc_clocksource_reliable)
+ clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
/* lower the rating if we already know its unstable: */
if (check_tsc_unstable()) {
clocksource_tsc.rating = 0;
@@ -843,7 +859,7 @@ void __init tsc_init(void)
if (unsynchronized_tsc())
mark_tsc_unstable("TSCs unsynchronized");
- check_geode_tsc_reliable();
+ check_system_tsc_reliable();
init_tsc_clocksource();
}
diff --git a/arch/x86/kernel/tsc_sync.c b/arch/x86/kernel/tsc_sync.c
index 1c0dfbca87c1..bf36328f6ef9 100644
--- a/arch/x86/kernel/tsc_sync.c
+++ b/arch/x86/kernel/tsc_sync.c
@@ -112,6 +112,12 @@ void __cpuinit check_tsc_sync_source(int cpu)
if (unsynchronized_tsc())
return;
+ if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) {
+ printk(KERN_INFO
+ "Skipping synchronization checks as TSC is reliable.\n");
+ return;
+ }
+
printk(KERN_INFO "checking TSC synchronization [CPU#%d -> CPU#%d]:",
smp_processor_id(), cpu);
@@ -165,7 +171,7 @@ void __cpuinit check_tsc_sync_target(void)
{
int cpus = 2;
- if (unsynchronized_tsc())
+ if (unsynchronized_tsc() || boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
return;
/*
diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c
index 22fd6577156a..23206ba16874 100644
--- a/arch/x86/kernel/vmi_32.c
+++ b/arch/x86/kernel/vmi_32.c
@@ -266,109 +266,6 @@ static void vmi_nop(void)
{
}
-#ifdef CONFIG_DEBUG_PAGE_TYPE
-
-#ifdef CONFIG_X86_PAE
-#define MAX_BOOT_PTS (2048+4+1)
-#else
-#define MAX_BOOT_PTS (1024+1)
-#endif
-
-/*
- * During boot, mem_map is not yet available in paging_init, so stash
- * all the boot page allocations here.
- */
-static struct {
- u32 pfn;
- int type;
-} boot_page_allocations[MAX_BOOT_PTS];
-static int num_boot_page_allocations;
-static int boot_allocations_applied;
-
-void vmi_apply_boot_page_allocations(void)
-{
- int i;
- BUG_ON(!mem_map);
- for (i = 0; i < num_boot_page_allocations; i++) {
- struct page *page = pfn_to_page(boot_page_allocations[i].pfn);
- page->type = boot_page_allocations[i].type;
- page->type = boot_page_allocations[i].type &
- ~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
- }
- boot_allocations_applied = 1;
-}
-
-static void record_page_type(u32 pfn, int type)
-{
- BUG_ON(num_boot_page_allocations >= MAX_BOOT_PTS);
- boot_page_allocations[num_boot_page_allocations].pfn = pfn;
- boot_page_allocations[num_boot_page_allocations].type = type;
- num_boot_page_allocations++;
-}
-
-static void check_zeroed_page(u32 pfn, int type, struct page *page)
-{
- u32 *ptr;
- int i;
- int limit = PAGE_SIZE / sizeof(int);
-
- if (page_address(page))
- ptr = (u32 *)page_address(page);
- else
- ptr = (u32 *)__va(pfn << PAGE_SHIFT);
- /*
- * When cloning the root in non-PAE mode, only the userspace
- * pdes need to be zeroed.
- */
- if (type & VMI_PAGE_CLONE)
- limit = KERNEL_PGD_BOUNDARY;
- for (i = 0; i < limit; i++)
- BUG_ON(ptr[i]);
-}
-
-/*
- * We stash the page type into struct page so we can verify the page
- * types are used properly.
- */
-static void vmi_set_page_type(u32 pfn, int type)
-{
- /* PAE can have multiple roots per page - don't track */
- if (PTRS_PER_PMD > 1 && (type & VMI_PAGE_PDP))
- return;
-
- if (boot_allocations_applied) {
- struct page *page = pfn_to_page(pfn);
- if (type != VMI_PAGE_NORMAL)
- BUG_ON(page->type);
- else
- BUG_ON(page->type == VMI_PAGE_NORMAL);
- page->type = type & ~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
- if (type & VMI_PAGE_ZEROED)
- check_zeroed_page(pfn, type, page);
- } else {
- record_page_type(pfn, type);
- }
-}
-
-static void vmi_check_page_type(u32 pfn, int type)
-{
- /* PAE can have multiple roots per page - skip checks */
- if (PTRS_PER_PMD > 1 && (type & VMI_PAGE_PDP))
- return;
-
- type &= ~(VMI_PAGE_ZEROED | VMI_PAGE_CLONE);
- if (boot_allocations_applied) {
- struct page *page = pfn_to_page(pfn);
- BUG_ON((page->type ^ type) & VMI_PAGE_PAE);
- BUG_ON(type == VMI_PAGE_NORMAL && page->type);
- BUG_ON((type & page->type) == 0);
- }
-}
-#else
-#define vmi_set_page_type(p,t) do { } while (0)
-#define vmi_check_page_type(p,t) do { } while (0)
-#endif
-
#ifdef CONFIG_HIGHPTE
static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type)
{
@@ -395,7 +292,6 @@ static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type)
static void vmi_allocate_pte(struct mm_struct *mm, unsigned long pfn)
{
- vmi_set_page_type(pfn, VMI_PAGE_L1);
vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0);
}
@@ -406,27 +302,22 @@ static void vmi_allocate_pmd(struct mm_struct *mm, unsigned long pfn)
* It is called only for swapper_pg_dir, which already has
* data on it.
*/
- vmi_set_page_type(pfn, VMI_PAGE_L2);
vmi_ops.allocate_page(pfn, VMI_PAGE_L2, 0, 0, 0);
}
static void vmi_allocate_pmd_clone(unsigned long pfn, unsigned long clonepfn, unsigned long start, unsigned long count)
{
- vmi_set_page_type(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE);
- vmi_check_page_type(clonepfn, VMI_PAGE_L2);
vmi_ops.allocate_page(pfn, VMI_PAGE_L2 | VMI_PAGE_CLONE, clonepfn, start, count);
}
static void vmi_release_pte(unsigned long pfn)
{
vmi_ops.release_page(pfn, VMI_PAGE_L1);
- vmi_set_page_type(pfn, VMI_PAGE_NORMAL);
}
static void vmi_release_pmd(unsigned long pfn)
{
vmi_ops.release_page(pfn, VMI_PAGE_L2);
- vmi_set_page_type(pfn, VMI_PAGE_NORMAL);
}
/*
@@ -450,26 +341,22 @@ static void vmi_release_pmd(unsigned long pfn)
static void vmi_update_pte(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.update_pte(ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_update_pte_defer(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.update_pte(ptep, vmi_flags_addr_defer(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_set_pte(pte_t *ptep, pte_t pte)
{
/* XXX because of set_pmd_pte, this can be called on PT or PD layers */
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE | VMI_PAGE_PD);
vmi_ops.set_pte(pte, ptep, VMI_PAGE_PT);
}
static void vmi_set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
{
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
@@ -477,10 +364,8 @@ static void vmi_set_pmd(pmd_t *pmdp, pmd_t pmdval)
{
#ifdef CONFIG_X86_PAE
const pte_t pte = { .pte = pmdval.pmd };
- vmi_check_page_type(__pa(pmdp) >> PAGE_SHIFT, VMI_PAGE_PMD);
#else
const pte_t pte = { pmdval.pud.pgd.pgd };
- vmi_check_page_type(__pa(pmdp) >> PAGE_SHIFT, VMI_PAGE_PGD);
#endif
vmi_ops.set_pte(pte, (pte_t *)pmdp, VMI_PAGE_PD);
}
@@ -502,7 +387,6 @@ static void vmi_set_pte_atomic(pte_t *ptep, pte_t pteval)
static void vmi_set_pte_present(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte)
{
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr_defer(mm, addr, VMI_PAGE_PT, 1));
}
@@ -510,21 +394,18 @@ static void vmi_set_pud(pud_t *pudp, pud_t pudval)
{
/* Um, eww */
const pte_t pte = { .pte = pudval.pgd.pgd };
- vmi_check_page_type(__pa(pudp) >> PAGE_SHIFT, VMI_PAGE_PGD);
vmi_ops.set_pte(pte, (pte_t *)pudp, VMI_PAGE_PDP);
}
static void vmi_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
const pte_t pte = { .pte = 0 };
- vmi_check_page_type(__pa(ptep) >> PAGE_SHIFT, VMI_PAGE_PTE);
vmi_ops.set_pte(pte, ptep, vmi_flags_addr(mm, addr, VMI_PAGE_PT, 0));
}
static void vmi_pmd_clear(pmd_t *pmd)
{
const pte_t pte = { .pte = 0 };
- vmi_check_page_type(__pa(pmd) >> PAGE_SHIFT, VMI_PAGE_PMD);
vmi_ops.set_pte(pte, (pte_t *)pmd, VMI_PAGE_PD);
}
#endif
diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S
index a9b8560adbc2..82c67559dde7 100644
--- a/arch/x86/kernel/vmlinux_32.lds.S
+++ b/arch/x86/kernel/vmlinux_32.lds.S
@@ -44,6 +44,7 @@ SECTIONS
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
+ IRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
_etext = .; /* End of text section */
diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S
index 46e05447405b..1a614c0e6bef 100644
--- a/arch/x86/kernel/vmlinux_64.lds.S
+++ b/arch/x86/kernel/vmlinux_64.lds.S
@@ -35,6 +35,7 @@ SECTIONS
SCHED_TEXT
LOCK_TEXT
KPROBES_TEXT
+ IRQENTRY_TEXT
*(.fixup)
*(.gnu.warning)
_etext = .; /* End of text section */
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 6f3d3d4cd973..44153afc9067 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -131,7 +131,16 @@ static __always_inline void do_vgettimeofday(struct timeval * tv)
gettimeofday(tv,NULL);
return;
}
+
+ /*
+ * Surround the RDTSC by barriers, to make sure it's not
+ * speculated to outside the seqlock critical section and
+ * does not cause time warps:
+ */
+ rdtsc_barrier();
now = vread();
+ rdtsc_barrier();
+
base = __vsyscall_gtod_data.clock.cycle_last;
mask = __vsyscall_gtod_data.clock.mask;
mult = __vsyscall_gtod_data.clock.mult;
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c
index 104c8220a383..a7ed208f81e3 100644
--- a/arch/x86/lguest/boot.c
+++ b/arch/x86/lguest/boot.c
@@ -590,7 +590,8 @@ static void __init lguest_init_IRQ(void)
* a straightforward 1 to 1 mapping, so force that here. */
__get_cpu_var(vector_irq)[vector] = i;
if (vector != SYSCALL_VECTOR) {
- set_intr_gate(vector, interrupt[vector]);
+ set_intr_gate(vector,
+ interrupt[vector-FIRST_EXTERNAL_VECTOR]);
set_irq_chip_and_handler_name(i, &lguest_irq_controller,
handle_level_irq,
"level");
diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/i386_head.S
index 5c7cef34c9e7..10b9bd35a8ff 100644
--- a/arch/x86/lguest/i386_head.S
+++ b/arch/x86/lguest/i386_head.S
@@ -30,21 +30,6 @@ ENTRY(lguest_entry)
movl $lguest_data - __PAGE_OFFSET, %edx
int $LGUEST_TRAP_ENTRY
- /* The Host put the toplevel pagetable in lguest_data.pgdir. The movsl
- * instruction uses %esi implicitly as the source for the copy we're
- * about to do. */
- movl lguest_data - __PAGE_OFFSET + LGUEST_DATA_pgdir, %esi
-
- /* Copy first 32 entries of page directory to __PAGE_OFFSET entries.
- * This means the first 128M of kernel memory will be mapped at
- * PAGE_OFFSET where the kernel expects to run. This will get it far
- * enough through boot to switch to its own pagetables. */
- movl $32, %ecx
- movl %esi, %edi
- addl $((__PAGE_OFFSET >> 22) * 4), %edi
- rep
- movsl
-
/* Set up the initial stack so we can run C code. */
movl $(init_thread_union+THREAD_SIZE),%esp
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 21e996a70d68..57ec8c86a877 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -393,7 +393,7 @@ static void show_fault_oops(struct pt_regs *regs, unsigned long error_code,
if (pte && pte_present(*pte) && !pte_exec(*pte))
printk(KERN_CRIT "kernel tried to execute "
"NX-protected page - exploit attempt? "
- "(uid: %d)\n", current->uid);
+ "(uid: %d)\n", current_uid());
}
#endif
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index c483f4242079..8655b5bb0963 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
+#include <linux/pci.h>
#include <linux/pfn.h>
#include <linux/poison.h>
#include <linux/bootmem.h>
@@ -67,7 +68,7 @@ static unsigned long __meminitdata table_top;
static int __initdata after_init_bootmem;
-static __init void *alloc_low_page(unsigned long *phys)
+static __init void *alloc_low_page(void)
{
unsigned long pfn = table_end++;
void *adr;
@@ -77,7 +78,6 @@ static __init void *alloc_low_page(unsigned long *phys)
adr = __va(pfn * PAGE_SIZE);
memset(adr, 0, PAGE_SIZE);
- *phys = pfn * PAGE_SIZE;
return adr;
}
@@ -92,16 +92,17 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd)
pmd_t *pmd_table;
#ifdef CONFIG_X86_PAE
- unsigned long phys;
if (!(pgd_val(*pgd) & _PAGE_PRESENT)) {
if (after_init_bootmem)
pmd_table = (pmd_t *)alloc_bootmem_low_pages(PAGE_SIZE);
else
- pmd_table = (pmd_t *)alloc_low_page(&phys);
+ pmd_table = (pmd_t *)alloc_low_page();
paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT);
set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
pud = pud_offset(pgd, 0);
BUG_ON(pmd_table != pmd_offset(pud, 0));
+
+ return pmd_table;
}
#endif
pud = pud_offset(pgd, 0);
@@ -126,10 +127,8 @@ static pte_t * __init one_page_table_init(pmd_t *pmd)
if (!page_table)
page_table =
(pte_t *)alloc_bootmem_low_pages(PAGE_SIZE);
- } else {
- unsigned long phys;
- page_table = (pte_t *)alloc_low_page(&phys);
- }
+ } else
+ page_table = (pte_t *)alloc_low_page();
paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT);
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
@@ -969,7 +968,7 @@ void __init mem_init(void)
int codesize, reservedpages, datasize, initsize;
int tmp;
- start_periodic_check_for_corruption();
+ pci_iommu_alloc();
#ifdef CONFIG_FLATMEM
BUG_ON(!mem_map);
@@ -1040,11 +1039,25 @@ void __init mem_init(void)
(unsigned long)&_text, (unsigned long)&_etext,
((unsigned long)&_etext - (unsigned long)&_text) >> 10);
+ /*
+ * Check boundaries twice: Some fundamental inconsistencies can
+ * be detected at build time already.
+ */
+#define __FIXADDR_TOP (-PAGE_SIZE)
+#ifdef CONFIG_HIGHMEM
+ BUILD_BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);
+ BUILD_BUG_ON(VMALLOC_END > PKMAP_BASE);
+#endif
+#define high_memory (-128UL << 20)
+ BUILD_BUG_ON(VMALLOC_START >= VMALLOC_END);
+#undef high_memory
+#undef __FIXADDR_TOP
+
#ifdef CONFIG_HIGHMEM
BUG_ON(PKMAP_BASE + LAST_PKMAP*PAGE_SIZE > FIXADDR_START);
BUG_ON(VMALLOC_END > PKMAP_BASE);
#endif
- BUG_ON(VMALLOC_START > VMALLOC_END);
+ BUG_ON(VMALLOC_START >= VMALLOC_END);
BUG_ON((unsigned long)high_memory > VMALLOC_START);
if (boot_cpu_data.wp_works_ok < 0)
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 9db01db6e3cd..9f7a0d24d42a 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -902,8 +902,6 @@ void __init mem_init(void)
long codesize, reservedpages, datasize, initsize;
unsigned long absent_pages;
- start_periodic_check_for_corruption();
-
pci_iommu_alloc();
/* clear_bss() already clear the empty_zero_page */
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index d4c4307ff3e0..bd85d42819e1 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -223,7 +223,8 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
* Check if the request spans more than any BAR in the iomem resource
* tree.
*/
- WARN_ON(iomem_map_sanity_check(phys_addr, size));
+ WARN_ONCE(iomem_map_sanity_check(phys_addr, size),
+ KERN_INFO "Info: mapping multiple BARs. Your kernel is fine.");
/*
* Don't allow anybody to remap normal RAM that we're using..
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index eb1bf000d12e..85cbd3cd3723 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -596,6 +596,242 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_prot)
free_memtype(addr, addr + size);
}
+/*
+ * Internal interface to reserve a range of physical memory with prot.
+ * Reserved non RAM regions only and after successful reserve_memtype,
+ * this func also keeps identity mapping (if any) in sync with this new prot.
+ */
+static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
+{
+ int is_ram = 0;
+ int id_sz, ret;
+ unsigned long flags;
+ unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK);
+
+ is_ram = pagerange_is_ram(paddr, paddr + size);
+
+ if (is_ram != 0) {
+ /*
+ * For mapping RAM pages, drivers need to call
+ * set_memory_[uc|wc|wb] directly, for reserve and free, before
+ * setting up the PTE.
+ */
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ ret = reserve_memtype(paddr, paddr + size, want_flags, &flags);
+ if (ret)
+ return ret;
+
+ if (flags != want_flags) {
+ free_memtype(paddr, paddr + size);
+ printk(KERN_ERR
+ "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n",
+ current->comm, current->pid,
+ cattr_name(want_flags),
+ (unsigned long long)paddr,
+ (unsigned long long)(paddr + size),
+ cattr_name(flags));
+ return -EINVAL;
+ }
+
+ /* Need to keep identity mapping in sync */
+ if (paddr >= __pa(high_memory))
+ return 0;
+
+ id_sz = (__pa(high_memory) < paddr + size) ?
+ __pa(high_memory) - paddr :
+ size;
+
+ if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) {
+ free_memtype(paddr, paddr + size);
+ printk(KERN_ERR
+ "%s:%d reserve_pfn_range ioremap_change_attr failed %s "
+ "for %Lx-%Lx\n",
+ current->comm, current->pid,
+ cattr_name(flags),
+ (unsigned long long)paddr,
+ (unsigned long long)(paddr + size));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Internal interface to free a range of physical memory.
+ * Frees non RAM regions only.
+ */
+static void free_pfn_range(u64 paddr, unsigned long size)
+{
+ int is_ram;
+
+ is_ram = pagerange_is_ram(paddr, paddr + size);
+ if (is_ram == 0)
+ free_memtype(paddr, paddr + size);
+}
+
+/*
+ * track_pfn_vma_copy is called when vma that is covering the pfnmap gets
+ * copied through copy_page_range().
+ *
+ * If the vma has a linear pfn mapping for the entire range, we get the prot
+ * from pte and reserve the entire vma range with single reserve_pfn_range call.
+ * Otherwise, we reserve the entire vma range, my ging through the PTEs page
+ * by page to get physical address and protection.
+ */
+int track_pfn_vma_copy(struct vm_area_struct *vma)
+{
+ int retval = 0;
+ unsigned long i, j;
+ resource_size_t paddr;
+ unsigned long prot;
+ unsigned long vma_start = vma->vm_start;
+ unsigned long vma_end = vma->vm_end;
+ unsigned long vma_size = vma_end - vma_start;
+
+ if (!pat_enabled)
+ return 0;
+
+ if (is_linear_pfn_mapping(vma)) {
+ /*
+ * reserve the whole chunk covered by vma. We need the
+ * starting address and protection from pte.
+ */
+ if (follow_phys(vma, vma_start, 0, &prot, &paddr)) {
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+ return reserve_pfn_range(paddr, vma_size, __pgprot(prot));
+ }
+
+ /* reserve entire vma page by page, using pfn and prot from pte */
+ for (i = 0; i < vma_size; i += PAGE_SIZE) {
+ if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
+ continue;
+
+ retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot));
+ if (retval)
+ goto cleanup_ret;
+ }
+ return 0;
+
+cleanup_ret:
+ /* Reserve error: Cleanup partial reservation and return error */
+ for (j = 0; j < i; j += PAGE_SIZE) {
+ if (follow_phys(vma, vma_start + j, 0, &prot, &paddr))
+ continue;
+
+ free_pfn_range(paddr, PAGE_SIZE);
+ }
+
+ return retval;
+}
+
+/*
+ * track_pfn_vma_new is called when a _new_ pfn mapping is being established
+ * for physical range indicated by pfn and size.
+ *
+ * prot is passed in as a parameter for the new mapping. If the vma has a
+ * linear pfn mapping for the entire range reserve the entire vma range with
+ * single reserve_pfn_range call.
+ * Otherwise, we look t the pfn and size and reserve only the specified range
+ * page by page.
+ *
+ * Note that this function can be called with caller trying to map only a
+ * subrange/page inside the vma.
+ */
+int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
+ unsigned long pfn, unsigned long size)
+{
+ int retval = 0;
+ unsigned long i, j;
+ resource_size_t base_paddr;
+ resource_size_t paddr;
+ unsigned long vma_start = vma->vm_start;
+ unsigned long vma_end = vma->vm_end;
+ unsigned long vma_size = vma_end - vma_start;
+
+ if (!pat_enabled)
+ return 0;
+
+ if (is_linear_pfn_mapping(vma)) {
+ /* reserve the whole chunk starting from vm_pgoff */
+ paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
+ return reserve_pfn_range(paddr, vma_size, prot);
+ }
+
+ /* reserve page by page using pfn and size */
+ base_paddr = (resource_size_t)pfn << PAGE_SHIFT;
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ paddr = base_paddr + i;
+ retval = reserve_pfn_range(paddr, PAGE_SIZE, prot);
+ if (retval)
+ goto cleanup_ret;
+ }
+ return 0;
+
+cleanup_ret:
+ /* Reserve error: Cleanup partial reservation and return error */
+ for (j = 0; j < i; j += PAGE_SIZE) {
+ paddr = base_paddr + j;
+ free_pfn_range(paddr, PAGE_SIZE);
+ }
+
+ return retval;
+}
+
+/*
+ * untrack_pfn_vma is called while unmapping a pfnmap for a region.
+ * untrack can be called for a specific region indicated by pfn and size or
+ * can be for the entire vma (in which case size can be zero).
+ */
+void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
+ unsigned long size)
+{
+ unsigned long i;
+ resource_size_t paddr;
+ unsigned long prot;
+ unsigned long vma_start = vma->vm_start;
+ unsigned long vma_end = vma->vm_end;
+ unsigned long vma_size = vma_end - vma_start;
+
+ if (!pat_enabled)
+ return;
+
+ if (is_linear_pfn_mapping(vma)) {
+ /* free the whole chunk starting from vm_pgoff */
+ paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
+ free_pfn_range(paddr, vma_size);
+ return;
+ }
+
+ if (size != 0 && size != vma_size) {
+ /* free page by page, using pfn and size */
+ paddr = (resource_size_t)pfn << PAGE_SHIFT;
+ for (i = 0; i < size; i += PAGE_SIZE) {
+ paddr = paddr + i;
+ free_pfn_range(paddr, PAGE_SIZE);
+ }
+ } else {
+ /* free entire vma, page by page, using the pfn from pte */
+ for (i = 0; i < vma_size; i += PAGE_SIZE) {
+ if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
+ continue;
+
+ free_pfn_range(paddr, PAGE_SIZE);
+ }
+ }
+}
+
+pgprot_t pgprot_writecombine(pgprot_t prot)
+{
+ if (pat_enabled)
+ return __pgprot(pgprot_val(prot) | _PAGE_CACHE_WC);
+ else
+ return pgprot_noncached(prot);
+}
+
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT)
/* get Nth element of the linked list */
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
index 509513760a6e..98658f25f542 100644
--- a/arch/x86/oprofile/op_model_amd.c
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -65,11 +65,13 @@ static unsigned long reset_value[NUM_COUNTERS];
#define IBS_FETCH_BEGIN 3
#define IBS_OP_BEGIN 4
-/* The function interface needs to be fixed, something like add
- data. Should then be added to linux/oprofile.h. */
+/*
+ * The function interface needs to be fixed, something like add
+ * data. Should then be added to linux/oprofile.h.
+ */
extern void
-oprofile_add_ibs_sample(struct pt_regs *const regs,
- unsigned int *const ibs_sample, int ibs_code);
+oprofile_add_ibs_sample(struct pt_regs * const regs,
+ unsigned int * const ibs_sample, int ibs_code);
struct ibs_fetch_sample {
/* MSRC001_1031 IBS Fetch Linear Address Register */
@@ -104,11 +106,6 @@ struct ibs_op_sample {
unsigned int ibs_dc_phys_high;
};
-/*
- * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+
-*/
-static void clear_ibs_nmi(void);
-
static int ibs_allowed; /* AMD Family10h and later */
struct op_ibs_config {
@@ -223,7 +220,7 @@ op_amd_handle_ibs(struct pt_regs * const regs,
(unsigned int *)&ibs_fetch,
IBS_FETCH_BEGIN);
- /*reenable the IRQ */
+ /* reenable the IRQ */
rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
high &= ~IBS_FETCH_HIGH_VALID_BIT;
high |= IBS_FETCH_HIGH_ENABLE;
@@ -331,8 +328,10 @@ static void op_amd_stop(struct op_msrs const * const msrs)
unsigned int low, high;
int i;
- /* Subtle: stop on all counters to avoid race with
- * setting our pm callback */
+ /*
+ * Subtle: stop on all counters to avoid race with setting our
+ * pm callback
+ */
for (i = 0 ; i < NUM_COUNTERS ; ++i) {
if (!reset_value[i])
continue;
@@ -343,13 +342,15 @@ static void op_amd_stop(struct op_msrs const * const msrs)
#ifdef CONFIG_OPROFILE_IBS
if (ibs_allowed && ibs_config.fetch_enabled) {
- low = 0; /* clear max count and enable */
+ /* clear max count and enable */
+ low = 0;
high = 0;
wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
}
if (ibs_allowed && ibs_config.op_enabled) {
- low = 0; /* clear max count and enable */
+ /* clear max count and enable */
+ low = 0;
high = 0;
wrmsr(MSR_AMD64_IBSOPCTL, low, high);
}
@@ -370,18 +371,7 @@ static void op_amd_shutdown(struct op_msrs const * const msrs)
}
}
-#ifndef CONFIG_OPROFILE_IBS
-
-/* no IBS support */
-
-static int op_amd_init(struct oprofile_operations *ops)
-{
- return 0;
-}
-
-static void op_amd_exit(void) {}
-
-#else
+#ifdef CONFIG_OPROFILE_IBS
static u8 ibs_eilvt_off;
@@ -395,7 +385,7 @@ static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
}
-static int pfm_amd64_setup_eilvt(void)
+static int init_ibs_nmi(void)
{
#define IBSCTL_LVTOFFSETVAL (1 << 8)
#define IBSCTL 0x1cc
@@ -443,18 +433,22 @@ static int pfm_amd64_setup_eilvt(void)
return 0;
}
-/*
- * initialize the APIC for the IBS interrupts
- * if available (AMD Family10h rev B0 and later)
- */
-static void setup_ibs(void)
+/* uninitialize the APIC for the IBS interrupts if needed */
+static void clear_ibs_nmi(void)
+{
+ if (ibs_allowed)
+ on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
+}
+
+/* initialize the APIC for the IBS interrupts if available */
+static void ibs_init(void)
{
ibs_allowed = boot_cpu_has(X86_FEATURE_IBS);
if (!ibs_allowed)
return;
- if (pfm_amd64_setup_eilvt()) {
+ if (init_ibs_nmi()) {
ibs_allowed = 0;
return;
}
@@ -462,14 +456,12 @@ static void setup_ibs(void)
printk(KERN_INFO "oprofile: AMD IBS detected\n");
}
-
-/*
- * unitialize the APIC for the IBS interrupts if needed on AMD Family10h
- * rev B0 and later */
-static void clear_ibs_nmi(void)
+static void ibs_exit(void)
{
- if (ibs_allowed)
- on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
+ if (!ibs_allowed)
+ return;
+
+ clear_ibs_nmi();
}
static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
@@ -519,7 +511,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
static int op_amd_init(struct oprofile_operations *ops)
{
- setup_ibs();
+ ibs_init();
create_arch_files = ops->create_files;
ops->create_files = setup_ibs_files;
return 0;
@@ -527,10 +519,21 @@ static int op_amd_init(struct oprofile_operations *ops)
static void op_amd_exit(void)
{
- clear_ibs_nmi();
+ ibs_exit();
}
-#endif
+#else
+
+/* no IBS support */
+
+static int op_amd_init(struct oprofile_operations *ops)
+{
+ return 0;
+}
+
+static void op_amd_exit(void) {}
+
+#endif /* CONFIG_OPROFILE_IBS */
struct op_x86_model_spec const op_amd_spec = {
.init = op_amd_init,
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index b67732bbb85a..bb1a01f089e2 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -23,6 +23,12 @@ unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
unsigned int pci_early_dump_regs;
static int pci_bf_sort;
int pci_routeirq;
+int noioapicquirk;
+#ifdef CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS
+int noioapicreroute = 0;
+#else
+int noioapicreroute = 1;
+#endif
int pcibios_last_bus = -1;
unsigned long pirq_table_addr;
struct pci_bus *pci_root_bus;
@@ -519,6 +525,17 @@ char * __devinit pcibios_setup(char *str)
} else if (!strcmp(str, "skip_isa_align")) {
pci_probe |= PCI_CAN_SKIP_ISA_ALIGN;
return NULL;
+ } else if (!strcmp(str, "noioapicquirk")) {
+ noioapicquirk = 1;
+ return NULL;
+ } else if (!strcmp(str, "ioapicreroute")) {
+ if (noioapicreroute != -1)
+ noioapicreroute = 0;
+ return NULL;
+ } else if (!strcmp(str, "noioapicreroute")) {
+ if (noioapicreroute != -1)
+ noioapicreroute = 1;
+ return NULL;
}
return str;
}
diff --git a/arch/x86/scripts/strip-symbols b/arch/x86/scripts/strip-symbols
new file mode 100644
index 000000000000..a2f1ccb827c7
--- /dev/null
+++ b/arch/x86/scripts/strip-symbols
@@ -0,0 +1 @@
+__cpu_vendor_dev_X86_VENDOR_*
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index 513f330c5832..1241f118ab56 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -310,7 +310,7 @@ int __init sysenter_setup(void)
}
/* Setup a VMA at program startup for the vsyscall page */
-int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
unsigned long addr;
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 257ba4a10abf..9c98cc6ba978 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -98,7 +98,7 @@ static unsigned long vdso_addr(unsigned long start, unsigned len)
/* Setup a VMA at program startup for the vsyscall page.
Not called for compat tasks */
-int arch_setup_additional_pages(struct linux_binprm *bprm, int exstack)
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
unsigned long addr;
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 5e4686d70f62..bea215230b20 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -28,6 +28,7 @@
#include <linux/console.h>
#include <xen/interface/xen.h>
+#include <xen/interface/version.h>
#include <xen/interface/physdev.h>
#include <xen/interface/vcpu.h>
#include <xen/features.h>
@@ -793,7 +794,7 @@ static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high)
ret = 0;
- switch(msr) {
+ switch (msr) {
#ifdef CONFIG_X86_64
unsigned which;
u64 base;
@@ -1453,7 +1454,7 @@ static __init void xen_map_identity_early(pmd_t *pmd, unsigned long max_pfn)
ident_pte = 0;
pfn = 0;
- for(pmdidx = 0; pmdidx < PTRS_PER_PMD && pfn < max_pfn; pmdidx++) {
+ for (pmdidx = 0; pmdidx < PTRS_PER_PMD && pfn < max_pfn; pmdidx++) {
pte_t *pte_page;
/* Reuse or allocate a page of ptes */
@@ -1471,7 +1472,7 @@ static __init void xen_map_identity_early(pmd_t *pmd, unsigned long max_pfn)
}
/* Install mappings */
- for(pteidx = 0; pteidx < PTRS_PER_PTE; pteidx++, pfn++) {
+ for (pteidx = 0; pteidx < PTRS_PER_PTE; pteidx++, pfn++) {
pte_t pte;
if (pfn > max_pfn_mapped)
@@ -1485,7 +1486,7 @@ static __init void xen_map_identity_early(pmd_t *pmd, unsigned long max_pfn)
}
}
- for(pteidx = 0; pteidx < ident_pte; pteidx += PTRS_PER_PTE)
+ for (pteidx = 0; pteidx < ident_pte; pteidx += PTRS_PER_PTE)
set_page_prot(&level1_ident_pgt[pteidx], PAGE_KERNEL_RO);
set_page_prot(pmd, PAGE_KERNEL_RO);
@@ -1499,7 +1500,7 @@ static void convert_pfn_mfn(void *v)
/* All levels are converted the same way, so just treat them
as ptes. */
- for(i = 0; i < PTRS_PER_PTE; i++)
+ for (i = 0; i < PTRS_PER_PTE; i++)
pte[i] = xen_make_pte(pte[i].pte);
}
@@ -1514,7 +1515,8 @@ static void convert_pfn_mfn(void *v)
* of the physical mapping once some sort of allocator has been set
* up.
*/
-static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn)
+static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd,
+ unsigned long max_pfn)
{
pud_t *l3;
pmd_t *l2;
@@ -1577,7 +1579,8 @@ static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pf
#else /* !CONFIG_X86_64 */
static pmd_t level2_kernel_pgt[PTRS_PER_PMD] __page_aligned_bss;
-static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn)
+static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd,
+ unsigned long max_pfn)
{
pmd_t *kernel_pmd;
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index e59e53b11e2b..503c240e26c7 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -154,13 +154,13 @@ void xen_setup_mfn_list_list(void)
{
unsigned pfn, idx;
- for(pfn = 0; pfn < MAX_DOMAIN_PAGES; pfn += P2M_ENTRIES_PER_PAGE) {
+ for (pfn = 0; pfn < MAX_DOMAIN_PAGES; pfn += P2M_ENTRIES_PER_PAGE) {
unsigned topidx = p2m_top_index(pfn);
p2m_top_mfn[topidx] = virt_to_mfn(p2m_top[topidx]);
}
- for(idx = 0; idx < ARRAY_SIZE(p2m_top_mfn_list); idx++) {
+ for (idx = 0; idx < ARRAY_SIZE(p2m_top_mfn_list); idx++) {
unsigned topidx = idx * P2M_ENTRIES_PER_PAGE;
p2m_top_mfn_list[idx] = virt_to_mfn(&p2m_top_mfn[topidx]);
}
@@ -179,7 +179,7 @@ void __init xen_build_dynamic_phys_to_machine(void)
unsigned long max_pfn = min(MAX_DOMAIN_PAGES, xen_start_info->nr_pages);
unsigned pfn;
- for(pfn = 0; pfn < max_pfn; pfn += P2M_ENTRIES_PER_PAGE) {
+ for (pfn = 0; pfn < max_pfn; pfn += P2M_ENTRIES_PER_PAGE) {
unsigned topidx = p2m_top_index(pfn);
p2m_top[topidx] = &mfn_list[pfn];
@@ -207,7 +207,7 @@ static void alloc_p2m(unsigned long **pp, unsigned long *mfnp)
p = (void *)__get_free_page(GFP_KERNEL | __GFP_NOFAIL);
BUG_ON(p == NULL);
- for(i = 0; i < P2M_ENTRIES_PER_PAGE; i++)
+ for (i = 0; i < P2M_ENTRIES_PER_PAGE; i++)
p[i] = INVALID_P2M_ENTRY;
if (cmpxchg(pp, p2m_missing, p) != p2m_missing)
@@ -407,7 +407,8 @@ out:
preempt_enable();
}
-pte_t xen_ptep_modify_prot_start(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
+pte_t xen_ptep_modify_prot_start(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
{
/* Just return the pte as-is. We preserve the bits on commit */
return *ptep;
@@ -878,7 +879,8 @@ static void __xen_pgd_pin(struct mm_struct *mm, pgd_t *pgd)
if (user_pgd) {
xen_pin_page(mm, virt_to_page(user_pgd), PT_PGD);
- xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(user_pgd)));
+ xen_do_pin(MMUEXT_PIN_L4_TABLE,
+ PFN_DOWN(__pa(user_pgd)));
}
}
#else /* CONFIG_X86_32 */
@@ -993,7 +995,8 @@ static void __xen_pgd_unpin(struct mm_struct *mm, pgd_t *pgd)
pgd_t *user_pgd = xen_get_user_pgd(pgd);
if (user_pgd) {
- xen_do_pin(MMUEXT_UNPIN_TABLE, PFN_DOWN(__pa(user_pgd)));
+ xen_do_pin(MMUEXT_UNPIN_TABLE,
+ PFN_DOWN(__pa(user_pgd)));
xen_unpin_page(mm, virt_to_page(user_pgd), PT_PGD);
}
}
diff --git a/arch/x86/xen/multicalls.c b/arch/x86/xen/multicalls.c
index 8ea8a0d0b0de..c738644b5435 100644
--- a/arch/x86/xen/multicalls.c
+++ b/arch/x86/xen/multicalls.c
@@ -154,7 +154,7 @@ void xen_mc_flush(void)
ret, smp_processor_id());
dump_stack();
for (i = 0; i < b->mcidx; i++) {
- printk(" call %2d/%d: op=%lu arg=[%lx] result=%ld\n",
+ printk(KERN_DEBUG " call %2d/%d: op=%lu arg=[%lx] result=%ld\n",
i+1, b->mcidx,
b->debug[i].op,
b->debug[i].args[0],
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c
index d67901083888..15c6c68db6a2 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -28,6 +28,9 @@
/* These are code, but not functions. Defined in entry.S */
extern const char xen_hypervisor_callback[];
extern const char xen_failsafe_callback[];
+extern void xen_sysenter_target(void);
+extern void xen_syscall_target(void);
+extern void xen_syscall32_target(void);
/**
@@ -110,7 +113,6 @@ static __cpuinit int register_callback(unsigned type, const void *func)
void __cpuinit xen_enable_sysenter(void)
{
- extern void xen_sysenter_target(void);
int ret;
unsigned sysenter_feature;
@@ -132,8 +134,6 @@ void __cpuinit xen_enable_syscall(void)
{
#ifdef CONFIG_X86_64
int ret;
- extern void xen_syscall_target(void);
- extern void xen_syscall32_target(void);
ret = register_callback(CALLBACKTYPE_syscall, xen_syscall_target);
if (ret != 0) {
@@ -160,7 +160,8 @@ void __init xen_arch_setup(void)
HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_writable_pagetables);
if (!xen_feature(XENFEAT_auto_translated_physmap))
- HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3);
+ HYPERVISOR_vm_assist(VMASST_CMD_enable,
+ VMASST_TYPE_pae_extended_cr3);
if (register_callback(CALLBACKTYPE_event, xen_hypervisor_callback) ||
register_callback(CALLBACKTYPE_failsafe, xen_failsafe_callback))
diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c
index 11a20adc1409..64f057d89e73 100644
--- a/arch/xtensa/platforms/iss/network.c
+++ b/arch/xtensa/platforms/iss/network.c
@@ -365,7 +365,7 @@ static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
static int iss_net_rx(struct net_device *dev)
{
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
int pkt_len;
struct sk_buff *skb;
@@ -456,7 +456,7 @@ static void iss_net_timer(unsigned long priv)
static int iss_net_open(struct net_device *dev)
{
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
char addr[sizeof "255.255.255.255\0"];
int err;
@@ -496,7 +496,7 @@ out:
static int iss_net_close(struct net_device *dev)
{
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
printk("iss_net_close!\n");
netif_stop_queue(dev);
spin_lock(&lp->lock);
@@ -515,7 +515,7 @@ printk("iss_net_close!\n");
static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
unsigned long flags;
int len;
@@ -551,7 +551,7 @@ static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *iss_net_get_stats(struct net_device *dev)
{
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
return &lp->stats;
}
@@ -578,7 +578,7 @@ static void iss_net_tx_timeout(struct net_device *dev)
static int iss_net_set_mac(struct net_device *dev, void *addr)
{
#if 0
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
struct sockaddr *hwaddr = addr;
spin_lock(&lp->lock);
@@ -592,7 +592,7 @@ static int iss_net_set_mac(struct net_device *dev, void *addr)
static int iss_net_change_mtu(struct net_device *dev, int new_mtu)
{
#if 0
- struct iss_net_private *lp = dev->priv;
+ struct iss_net_private *lp = netdev_priv(dev);
int err = 0;
spin_lock(&lp->lock);
@@ -636,7 +636,7 @@ static int iss_net_configure(int index, char *init)
/* Initialize private element. */
- lp = dev->priv;
+ lp = netdev_priv(dev);
*lp = ((struct iss_net_private) {
.device_list = LIST_HEAD_INIT(lp->device_list),
.opened_list = LIST_HEAD_INIT(lp->opened_list),
@@ -660,10 +660,7 @@ static int iss_net_configure(int index, char *init)
printk(KERN_INFO "Netdevice %d ", index);
if (lp->have_mac)
- printk("(%02x:%02x:%02x:%02x:%02x:%02x) ",
- lp->mac[0], lp->mac[1],
- lp->mac[2], lp->mac[3],
- lp->mac[4], lp->mac[5]);
+ printk("(%pM) ", lp->mac);
printk(": ");
/* sysfs register */
diff --git a/block/Kconfig b/block/Kconfig
index 290b219fad9c..ac0956f77785 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -24,21 +24,17 @@ menuconfig BLOCK
if BLOCK
config LBD
- bool "Support for Large Block Devices"
+ bool "Support for large block devices and files"
depends on !64BIT
help
- Enable block devices of size 2TB and larger.
+ Enable block devices or files of size 2TB and larger.
This option is required to support the full capacity of large
(2TB+) block devices, including RAID, disk, Network Block Device,
Logical Volume Manager (LVM) and loopback.
-
- For example, RAID devices are frequently bigger than the capacity
- of the largest individual hard drive.
-
- This option is not required if you have individual disk drives
- which total 2TB+ and you are not aggregating the capacity into
- a large block device (e.g. using RAID or LVM).
+
+ This option also enables support for single files larger than
+ 2TB.
If unsure, say N.
@@ -58,15 +54,6 @@ config BLK_DEV_IO_TRACE
If unsure, say N.
-config LSF
- bool "Support for Large Single Files"
- depends on !64BIT
- help
- Say Y here if you want to be able to handle very large files (2TB
- and larger), otherwise say N.
-
- If unsure, say Y.
-
config BLK_DEV_BSG
bool "Block layer SG support v4 (EXPERIMENTAL)"
depends on EXPERIMENTAL
diff --git a/block/as-iosched.c b/block/as-iosched.c
index 71f0abb219ee..631f6f44460a 100644
--- a/block/as-iosched.c
+++ b/block/as-iosched.c
@@ -1339,12 +1339,12 @@ static int as_may_queue(struct request_queue *q, int rw)
return ret;
}
-static void as_exit_queue(elevator_t *e)
+static void as_exit_queue(struct elevator_queue *e)
{
struct as_data *ad = e->elevator_data;
del_timer_sync(&ad->antic_timer);
- kblockd_flush_work(&ad->antic_work);
+ cancel_work_sync(&ad->antic_work);
BUG_ON(!list_empty(&ad->fifo_list[REQ_SYNC]));
BUG_ON(!list_empty(&ad->fifo_list[REQ_ASYNC]));
@@ -1409,7 +1409,7 @@ as_var_store(unsigned long *var, const char *page, size_t count)
return count;
}
-static ssize_t est_time_show(elevator_t *e, char *page)
+static ssize_t est_time_show(struct elevator_queue *e, char *page)
{
struct as_data *ad = e->elevator_data;
int pos = 0;
@@ -1427,7 +1427,7 @@ static ssize_t est_time_show(elevator_t *e, char *page)
}
#define SHOW_FUNCTION(__FUNC, __VAR) \
-static ssize_t __FUNC(elevator_t *e, char *page) \
+static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct as_data *ad = e->elevator_data; \
return as_var_show(jiffies_to_msecs((__VAR)), (page)); \
@@ -1440,7 +1440,7 @@ SHOW_FUNCTION(as_write_batch_expire_show, ad->batch_expire[REQ_ASYNC]);
#undef SHOW_FUNCTION
#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \
-static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \
+static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \
{ \
struct as_data *ad = e->elevator_data; \
int ret = as_var_store(__PTR, (page), count); \
diff --git a/block/blk-barrier.c b/block/blk-barrier.c
index 6e72d661ae42..8eba4e43bb0c 100644
--- a/block/blk-barrier.c
+++ b/block/blk-barrier.c
@@ -24,8 +24,8 @@
int blk_queue_ordered(struct request_queue *q, unsigned ordered,
prepare_flush_fn *prepare_flush_fn)
{
- if (ordered & (QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_POSTFLUSH) &&
- prepare_flush_fn == NULL) {
+ if (!prepare_flush_fn && (ordered & (QUEUE_ORDERED_DO_PREFLUSH |
+ QUEUE_ORDERED_DO_POSTFLUSH))) {
printk(KERN_ERR "%s: prepare_flush_fn required\n", __func__);
return -EINVAL;
}
@@ -88,7 +88,7 @@ unsigned blk_ordered_req_seq(struct request *rq)
return QUEUE_ORDSEQ_DONE;
}
-void blk_ordered_complete_seq(struct request_queue *q, unsigned seq, int error)
+bool blk_ordered_complete_seq(struct request_queue *q, unsigned seq, int error)
{
struct request *rq;
@@ -99,7 +99,7 @@ void blk_ordered_complete_seq(struct request_queue *q, unsigned seq, int error)
q->ordseq |= seq;
if (blk_ordered_cur_seq(q) != QUEUE_ORDSEQ_DONE)
- return;
+ return false;
/*
* Okay, sequence complete.
@@ -109,6 +109,8 @@ void blk_ordered_complete_seq(struct request_queue *q, unsigned seq, int error)
if (__blk_end_request(rq, q->orderr, blk_rq_bytes(rq)))
BUG();
+
+ return true;
}
static void pre_flush_end_io(struct request *rq, int error)
@@ -134,7 +136,7 @@ static void queue_flush(struct request_queue *q, unsigned which)
struct request *rq;
rq_end_io_fn *end_io;
- if (which == QUEUE_ORDERED_PREFLUSH) {
+ if (which == QUEUE_ORDERED_DO_PREFLUSH) {
rq = &q->pre_flush_rq;
end_io = pre_flush_end_io;
} else {
@@ -151,80 +153,110 @@ static void queue_flush(struct request_queue *q, unsigned which)
elv_insert(q, rq, ELEVATOR_INSERT_FRONT);
}
-static inline struct request *start_ordered(struct request_queue *q,
- struct request *rq)
+static inline bool start_ordered(struct request_queue *q, struct request **rqp)
{
+ struct request *rq = *rqp;
+ unsigned skip = 0;
+
q->orderr = 0;
q->ordered = q->next_ordered;
q->ordseq |= QUEUE_ORDSEQ_STARTED;
/*
- * Prep proxy barrier request.
+ * For an empty barrier, there's no actual BAR request, which
+ * in turn makes POSTFLUSH unnecessary. Mask them off.
*/
+ if (!rq->hard_nr_sectors) {
+ q->ordered &= ~(QUEUE_ORDERED_DO_BAR |
+ QUEUE_ORDERED_DO_POSTFLUSH);
+ /*
+ * Empty barrier on a write-through device w/ ordered
+ * tag has no command to issue and without any command
+ * to issue, ordering by tag can't be used. Drain
+ * instead.
+ */
+ if ((q->ordered & QUEUE_ORDERED_BY_TAG) &&
+ !(q->ordered & QUEUE_ORDERED_DO_PREFLUSH)) {
+ q->ordered &= ~QUEUE_ORDERED_BY_TAG;
+ q->ordered |= QUEUE_ORDERED_BY_DRAIN;
+ }
+ }
+
+ /* stash away the original request */
elv_dequeue_request(q, rq);
q->orig_bar_rq = rq;
- rq = &q->bar_rq;
- blk_rq_init(q, rq);
- if (bio_data_dir(q->orig_bar_rq->bio) == WRITE)
- rq->cmd_flags |= REQ_RW;
- if (q->ordered & QUEUE_ORDERED_FUA)
- rq->cmd_flags |= REQ_FUA;
- init_request_from_bio(rq, q->orig_bar_rq->bio);
- rq->end_io = bar_end_io;
+ rq = NULL;
/*
* Queue ordered sequence. As we stack them at the head, we
* need to queue in reverse order. Note that we rely on that
* no fs request uses ELEVATOR_INSERT_FRONT and thus no fs
- * request gets inbetween ordered sequence. If this request is
- * an empty barrier, we don't need to do a postflush ever since
- * there will be no data written between the pre and post flush.
- * Hence a single flush will suffice.
+ * request gets inbetween ordered sequence.
*/
- if ((q->ordered & QUEUE_ORDERED_POSTFLUSH) && !blk_empty_barrier(rq))
- queue_flush(q, QUEUE_ORDERED_POSTFLUSH);
- else
- q->ordseq |= QUEUE_ORDSEQ_POSTFLUSH;
+ if (q->ordered & QUEUE_ORDERED_DO_POSTFLUSH) {
+ queue_flush(q, QUEUE_ORDERED_DO_POSTFLUSH);
+ rq = &q->post_flush_rq;
+ } else
+ skip |= QUEUE_ORDSEQ_POSTFLUSH;
- elv_insert(q, rq, ELEVATOR_INSERT_FRONT);
+ if (q->ordered & QUEUE_ORDERED_DO_BAR) {
+ rq = &q->bar_rq;
+
+ /* initialize proxy request and queue it */
+ blk_rq_init(q, rq);
+ if (bio_data_dir(q->orig_bar_rq->bio) == WRITE)
+ rq->cmd_flags |= REQ_RW;
+ if (q->ordered & QUEUE_ORDERED_DO_FUA)
+ rq->cmd_flags |= REQ_FUA;
+ init_request_from_bio(rq, q->orig_bar_rq->bio);
+ rq->end_io = bar_end_io;
- if (q->ordered & QUEUE_ORDERED_PREFLUSH) {
- queue_flush(q, QUEUE_ORDERED_PREFLUSH);
+ elv_insert(q, rq, ELEVATOR_INSERT_FRONT);
+ } else
+ skip |= QUEUE_ORDSEQ_BAR;
+
+ if (q->ordered & QUEUE_ORDERED_DO_PREFLUSH) {
+ queue_flush(q, QUEUE_ORDERED_DO_PREFLUSH);
rq = &q->pre_flush_rq;
} else
- q->ordseq |= QUEUE_ORDSEQ_PREFLUSH;
+ skip |= QUEUE_ORDSEQ_PREFLUSH;
- if ((q->ordered & QUEUE_ORDERED_TAG) || q->in_flight == 0)
- q->ordseq |= QUEUE_ORDSEQ_DRAIN;
- else
+ if ((q->ordered & QUEUE_ORDERED_BY_DRAIN) && q->in_flight)
rq = NULL;
+ else
+ skip |= QUEUE_ORDSEQ_DRAIN;
+
+ *rqp = rq;
- return rq;
+ /*
+ * Complete skipped sequences. If whole sequence is complete,
+ * return false to tell elevator that this request is gone.
+ */
+ return !blk_ordered_complete_seq(q, skip, 0);
}
-int blk_do_ordered(struct request_queue *q, struct request **rqp)
+bool blk_do_ordered(struct request_queue *q, struct request **rqp)
{
struct request *rq = *rqp;
const int is_barrier = blk_fs_request(rq) && blk_barrier_rq(rq);
if (!q->ordseq) {
if (!is_barrier)
- return 1;
+ return true;
- if (q->next_ordered != QUEUE_ORDERED_NONE) {
- *rqp = start_ordered(q, rq);
- return 1;
- } else {
+ if (q->next_ordered != QUEUE_ORDERED_NONE)
+ return start_ordered(q, rqp);
+ else {
/*
- * This can happen when the queue switches to
- * ORDERED_NONE while this request is on it.
+ * Queue ordering not supported. Terminate
+ * with prejudice.
*/
elv_dequeue_request(q, rq);
if (__blk_end_request(rq, -EOPNOTSUPP,
blk_rq_bytes(rq)))
BUG();
*rqp = NULL;
- return 0;
+ return false;
}
}
@@ -235,9 +267,9 @@ int blk_do_ordered(struct request_queue *q, struct request **rqp)
/* Special requests are not subject to ordering rules. */
if (!blk_fs_request(rq) &&
rq != &q->pre_flush_rq && rq != &q->post_flush_rq)
- return 1;
+ return true;
- if (q->ordered & QUEUE_ORDERED_TAG) {
+ if (q->ordered & QUEUE_ORDERED_BY_TAG) {
/* Ordered by tag. Blocking the next barrier is enough. */
if (is_barrier && rq != &q->bar_rq)
*rqp = NULL;
@@ -248,7 +280,7 @@ int blk_do_ordered(struct request_queue *q, struct request **rqp)
*rqp = NULL;
}
- return 1;
+ return true;
}
static void bio_end_empty_barrier(struct bio *bio, int err)
diff --git a/block/blk-core.c b/block/blk-core.c
index 561e8a1b43a4..a824e49c0d0a 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -153,6 +153,9 @@ static void req_bio_endio(struct request *rq, struct bio *bio,
nbytes = bio->bi_size;
}
+ if (unlikely(rq->cmd_flags & REQ_QUIET))
+ set_bit(BIO_QUIET, &bio->bi_flags);
+
bio->bi_size -= nbytes;
bio->bi_sector += (nbytes >> 9);
@@ -265,8 +268,7 @@ void __generic_unplug_device(struct request_queue *q)
{
if (unlikely(blk_queue_stopped(q)))
return;
-
- if (!blk_remove_plug(q))
+ if (!blk_remove_plug(q) && !blk_queue_nonrot(q))
return;
q->request_fn(q);
@@ -404,7 +406,8 @@ EXPORT_SYMBOL(blk_stop_queue);
void blk_sync_queue(struct request_queue *q)
{
del_timer_sync(&q->unplug_timer);
- kblockd_flush_work(&q->unplug_work);
+ del_timer_sync(&q->timeout);
+ cancel_work_sync(&q->unplug_work);
}
EXPORT_SYMBOL(blk_sync_queue);
@@ -1135,7 +1138,7 @@ void init_request_from_bio(struct request *req, struct bio *bio)
static int __make_request(struct request_queue *q, struct bio *bio)
{
struct request *req;
- int el_ret, nr_sectors, barrier, discard, err;
+ int el_ret, nr_sectors;
const unsigned short prio = bio_prio(bio);
const int sync = bio_sync(bio);
int rw_flags;
@@ -1149,22 +1152,9 @@ static int __make_request(struct request_queue *q, struct bio *bio)
*/
blk_queue_bounce(q, &bio);
- barrier = bio_barrier(bio);
- if (unlikely(barrier) && bio_has_data(bio) &&
- (q->next_ordered == QUEUE_ORDERED_NONE)) {
- err = -EOPNOTSUPP;
- goto end_io;
- }
-
- discard = bio_discard(bio);
- if (unlikely(discard) && !q->prepare_discard_fn) {
- err = -EOPNOTSUPP;
- goto end_io;
- }
-
spin_lock_irq(q->queue_lock);
- if (unlikely(barrier) || elv_queue_empty(q))
+ if (unlikely(bio_barrier(bio)) || elv_queue_empty(q))
goto get_rq;
el_ret = elv_merge(q, &req, bio);
@@ -1250,18 +1240,14 @@ get_rq:
if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) ||
bio_flagged(bio, BIO_CPU_AFFINE))
req->cpu = blk_cpu_to_group(smp_processor_id());
- if (elv_queue_empty(q))
+ if (!blk_queue_nonrot(q) && elv_queue_empty(q))
blk_plug_device(q);
add_request(q, req);
out:
- if (sync)
+ if (sync || blk_queue_nonrot(q))
__generic_unplug_device(q);
spin_unlock_irq(q->queue_lock);
return 0;
-
-end_io:
- bio_endio(bio, err);
- return 0;
}
/*
@@ -1414,15 +1400,13 @@ static inline void __generic_make_request(struct bio *bio)
char b[BDEVNAME_SIZE];
q = bdev_get_queue(bio->bi_bdev);
- if (!q) {
+ if (unlikely(!q)) {
printk(KERN_ERR
"generic_make_request: Trying to access "
"nonexistent block-device %s (%Lu)\n",
bdevname(bio->bi_bdev, b),
(long long) bio->bi_sector);
-end_io:
- bio_endio(bio, err);
- break;
+ goto end_io;
}
if (unlikely(nr_sectors > q->max_hw_sectors)) {
@@ -1459,14 +1443,19 @@ end_io:
if (bio_check_eod(bio, nr_sectors))
goto end_io;
- if ((bio_empty_barrier(bio) && !q->prepare_flush_fn) ||
- (bio_discard(bio) && !q->prepare_discard_fn)) {
+
+ if (bio_discard(bio) && !q->prepare_discard_fn) {
err = -EOPNOTSUPP;
goto end_io;
}
ret = q->make_request_fn(q, bio);
} while (ret);
+
+ return;
+
+end_io:
+ bio_endio(bio, err);
}
/*
@@ -1716,14 +1705,6 @@ static int __end_that_request_first(struct request *req, int error,
while ((bio = req->bio) != NULL) {
int nbytes;
- /*
- * For an empty barrier request, the low level driver must
- * store a potential error location in ->sector. We pass
- * that back up in ->bi_sector.
- */
- if (blk_empty_barrier(req))
- bio->bi_sector = req->sector;
-
if (nr_bytes >= bio->bi_size) {
req->bio = bio->bi_next;
nbytes = bio->bi_size;
@@ -2143,12 +2124,6 @@ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work)
}
EXPORT_SYMBOL(kblockd_schedule_work);
-void kblockd_flush_work(struct work_struct *work)
-{
- cancel_work_sync(work);
-}
-EXPORT_SYMBOL(kblockd_flush_work);
-
int __init blk_dev_init(void)
{
kblockd_workqueue = create_workqueue("kblockd");
diff --git a/block/blk-settings.c b/block/blk-settings.c
index afa55e14e278..59fd05d9f1d5 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -319,9 +319,9 @@ void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b)
t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors);
t->seg_boundary_mask = min_not_zero(t->seg_boundary_mask, b->seg_boundary_mask);
- t->max_phys_segments = min(t->max_phys_segments, b->max_phys_segments);
- t->max_hw_segments = min(t->max_hw_segments, b->max_hw_segments);
- t->max_segment_size = min(t->max_segment_size, b->max_segment_size);
+ t->max_phys_segments = min_not_zero(t->max_phys_segments, b->max_phys_segments);
+ t->max_hw_segments = min_not_zero(t->max_hw_segments, b->max_hw_segments);
+ t->max_segment_size = min_not_zero(t->max_segment_size, b->max_segment_size);
t->hardsect_size = max(t->hardsect_size, b->hardsect_size);
if (!t->queue_lock)
WARN_ON_ONCE(1);
diff --git a/block/blk-softirq.c b/block/blk-softirq.c
index e660d26ca656..ce0efc6b26dc 100644
--- a/block/blk-softirq.c
+++ b/block/blk-softirq.c
@@ -161,7 +161,7 @@ void blk_complete_request(struct request *req)
}
EXPORT_SYMBOL(blk_complete_request);
-__init int blk_softirq_init(void)
+static __init int blk_softirq_init(void)
{
int i;
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 21e275d7eed9..a29cb788e408 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -88,9 +88,7 @@ queue_ra_store(struct request_queue *q, const char *page, size_t count)
unsigned long ra_kb;
ssize_t ret = queue_var_store(&ra_kb, page, count);
- spin_lock_irq(q->queue_lock);
q->backing_dev_info.ra_pages = ra_kb >> (PAGE_CACHE_SHIFT - 10);
- spin_unlock_irq(q->queue_lock);
return ret;
}
@@ -117,10 +115,7 @@ queue_max_sectors_store(struct request_queue *q, const char *page, size_t count)
if (max_sectors_kb > max_hw_sectors_kb || max_sectors_kb < page_kb)
return -EINVAL;
- /*
- * Take the queue lock to update the readahead and max_sectors
- * values synchronously:
- */
+
spin_lock_irq(q->queue_lock);
q->max_sectors = max_sectors_kb << 1;
spin_unlock_irq(q->queue_lock);
diff --git a/block/blk-tag.c b/block/blk-tag.c
index c0d419e84ce7..3c518e3303ae 100644
--- a/block/blk-tag.c
+++ b/block/blk-tag.c
@@ -158,7 +158,6 @@ fail:
/**
* blk_init_tags - initialize the tag info for an external tag map
* @depth: the maximum queue depth supported
- * @tags: the tag to use
**/
struct blk_queue_tag *blk_init_tags(int depth)
{
diff --git a/block/blk-timeout.c b/block/blk-timeout.c
index 69185ea9fae2..a09535377a94 100644
--- a/block/blk-timeout.c
+++ b/block/blk-timeout.c
@@ -73,11 +73,7 @@ ssize_t part_timeout_store(struct device *dev, struct device_attribute *attr,
*/
void blk_delete_timer(struct request *req)
{
- struct request_queue *q = req->q;
-
list_del_init(&req->timeout_list);
- if (list_empty(&q->timeout_list))
- del_timer(&q->timeout);
}
static void blk_rq_timed_out(struct request *req)
@@ -111,7 +107,7 @@ static void blk_rq_timed_out(struct request *req)
void blk_rq_timed_out_timer(unsigned long data)
{
struct request_queue *q = (struct request_queue *) data;
- unsigned long flags, uninitialized_var(next), next_set = 0;
+ unsigned long flags, next = 0;
struct request *rq, *tmp;
spin_lock_irqsave(q->queue_lock, flags);
@@ -126,15 +122,18 @@ void blk_rq_timed_out_timer(unsigned long data)
if (blk_mark_rq_complete(rq))
continue;
blk_rq_timed_out(rq);
+ } else {
+ if (!next || time_after(next, rq->deadline))
+ next = rq->deadline;
}
- if (!next_set) {
- next = rq->deadline;
- next_set = 1;
- } else if (time_after(next, rq->deadline))
- next = rq->deadline;
}
- if (next_set && !list_empty(&q->timeout_list))
+ /*
+ * next can never be 0 here with the list non-empty, since we always
+ * bump ->deadline to 1 so we can detect if the timer was ever added
+ * or not. See comment in blk_add_timer()
+ */
+ if (next)
mod_timer(&q->timeout, round_jiffies_up(next));
spin_unlock_irqrestore(q->queue_lock, flags);
diff --git a/block/blk.h b/block/blk.h
index d2e49af90db5..6e1ed40534e9 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -99,8 +99,8 @@ static inline int queue_congestion_off_threshold(struct request_queue *q)
static inline int blk_cpu_to_group(int cpu)
{
#ifdef CONFIG_SCHED_MC
- cpumask_t mask = cpu_coregroup_map(cpu);
- return first_cpu(mask);
+ const struct cpumask *mask = cpu_coregroup_mask(cpu);
+ return cpumask_first(mask);
#elif defined(CONFIG_SCHED_SMT)
return first_cpu(per_cpu(cpu_sibling_map, cpu));
#else
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 6a062eebbd15..e8525fa72823 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1136,12 +1136,8 @@ static int cfq_dispatch_requests(struct request_queue *q, int force)
if (cfq_class_idle(cfqq))
max_dispatch = 1;
- if (cfqq->dispatched >= max_dispatch) {
- if (cfqd->busy_queues > 1)
- break;
- if (cfqq->dispatched >= 4 * max_dispatch)
- break;
- }
+ if (cfqq->dispatched >= max_dispatch && cfqd->busy_queues > 1)
+ break;
if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq))
break;
@@ -1318,7 +1314,15 @@ static void cfq_exit_single_io_context(struct io_context *ioc,
unsigned long flags;
spin_lock_irqsave(q->queue_lock, flags);
- __cfq_exit_single_io_context(cfqd, cic);
+
+ /*
+ * Ensure we get a fresh copy of the ->key to prevent
+ * race between exiting task and queue
+ */
+ smp_read_barrier_depends();
+ if (cic->key)
+ __cfq_exit_single_io_context(cfqd, cic);
+
spin_unlock_irqrestore(q->queue_lock, flags);
}
}
@@ -2160,7 +2164,7 @@ out_cont:
static void cfq_shutdown_timer_wq(struct cfq_data *cfqd)
{
del_timer_sync(&cfqd->idle_slice_timer);
- kblockd_flush_work(&cfqd->unplug_work);
+ cancel_work_sync(&cfqd->unplug_work);
}
static void cfq_put_async_queues(struct cfq_data *cfqd)
@@ -2178,7 +2182,7 @@ static void cfq_put_async_queues(struct cfq_data *cfqd)
cfq_put_queue(cfqd->async_idle_cfqq);
}
-static void cfq_exit_queue(elevator_t *e)
+static void cfq_exit_queue(struct elevator_queue *e)
{
struct cfq_data *cfqd = e->elevator_data;
struct request_queue *q = cfqd->queue;
@@ -2288,7 +2292,7 @@ cfq_var_store(unsigned int *var, const char *page, size_t count)
}
#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
-static ssize_t __FUNC(elevator_t *e, char *page) \
+static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct cfq_data *cfqd = e->elevator_data; \
unsigned int __data = __VAR; \
@@ -2308,7 +2312,7 @@ SHOW_FUNCTION(cfq_slice_async_rq_show, cfqd->cfq_slice_async_rq, 0);
#undef SHOW_FUNCTION
#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
-static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \
+static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \
{ \
struct cfq_data *cfqd = e->elevator_data; \
unsigned int __data; \
diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c
index 67eb93cff699..f87615dea46b 100644
--- a/block/compat_ioctl.c
+++ b/block/compat_ioctl.c
@@ -774,9 +774,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
- lock_kernel();
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
- unlock_kernel();
return 0;
case BLKGETSIZE:
size = bdev->bd_inode->i_size;
diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c
index fd311179f44c..c4d991d4adef 100644
--- a/block/deadline-iosched.c
+++ b/block/deadline-iosched.c
@@ -334,7 +334,7 @@ static int deadline_queue_empty(struct request_queue *q)
&& list_empty(&dd->fifo_list[READ]);
}
-static void deadline_exit_queue(elevator_t *e)
+static void deadline_exit_queue(struct elevator_queue *e)
{
struct deadline_data *dd = e->elevator_data;
@@ -387,7 +387,7 @@ deadline_var_store(int *var, const char *page, size_t count)
}
#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \
-static ssize_t __FUNC(elevator_t *e, char *page) \
+static ssize_t __FUNC(struct elevator_queue *e, char *page) \
{ \
struct deadline_data *dd = e->elevator_data; \
int __data = __VAR; \
@@ -403,7 +403,7 @@ SHOW_FUNCTION(deadline_fifo_batch_show, dd->fifo_batch, 0);
#undef SHOW_FUNCTION
#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \
-static ssize_t __FUNC(elevator_t *e, const char *page, size_t count) \
+static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \
{ \
struct deadline_data *dd = e->elevator_data; \
int __data; \
diff --git a/block/elevator.c b/block/elevator.c
index 86836dd179c0..98259eda0ef6 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -65,7 +65,7 @@ DEFINE_TRACE(block_rq_issue);
static int elv_iosched_allow_merge(struct request *rq, struct bio *bio)
{
struct request_queue *q = rq->q;
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_allow_merge_fn)
return e->ops->elevator_allow_merge_fn(q, rq, bio);
@@ -208,13 +208,13 @@ __setup("elevator=", elevator_setup);
static struct kobj_type elv_ktype;
-static elevator_t *elevator_alloc(struct request_queue *q,
+static struct elevator_queue *elevator_alloc(struct request_queue *q,
struct elevator_type *e)
{
- elevator_t *eq;
+ struct elevator_queue *eq;
int i;
- eq = kmalloc_node(sizeof(elevator_t), GFP_KERNEL | __GFP_ZERO, q->node);
+ eq = kmalloc_node(sizeof(*eq), GFP_KERNEL | __GFP_ZERO, q->node);
if (unlikely(!eq))
goto err;
@@ -240,8 +240,9 @@ err:
static void elevator_release(struct kobject *kobj)
{
- elevator_t *e = container_of(kobj, elevator_t, kobj);
+ struct elevator_queue *e;
+ e = container_of(kobj, struct elevator_queue, kobj);
elevator_put(e->elevator_type);
kfree(e->hash);
kfree(e);
@@ -297,7 +298,7 @@ int elevator_init(struct request_queue *q, char *name)
}
EXPORT_SYMBOL(elevator_init);
-void elevator_exit(elevator_t *e)
+void elevator_exit(struct elevator_queue *e)
{
mutex_lock(&e->sysfs_lock);
if (e->ops->elevator_exit_fn)
@@ -311,7 +312,7 @@ EXPORT_SYMBOL(elevator_exit);
static void elv_activate_rq(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_activate_req_fn)
e->ops->elevator_activate_req_fn(q, rq);
@@ -319,7 +320,7 @@ static void elv_activate_rq(struct request_queue *q, struct request *rq)
static void elv_deactivate_rq(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_deactivate_req_fn)
e->ops->elevator_deactivate_req_fn(q, rq);
@@ -338,7 +339,7 @@ static void elv_rqhash_del(struct request_queue *q, struct request *rq)
static void elv_rqhash_add(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
BUG_ON(ELV_ON_HASH(rq));
hlist_add_head(&rq->hash, &e->hash[ELV_HASH_FN(rq_hash_key(rq))]);
@@ -352,7 +353,7 @@ static void elv_rqhash_reposition(struct request_queue *q, struct request *rq)
static struct request *elv_rqhash_find(struct request_queue *q, sector_t offset)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
struct hlist_head *hash_list = &e->hash[ELV_HASH_FN(offset)];
struct hlist_node *entry, *next;
struct request *rq;
@@ -494,7 +495,7 @@ EXPORT_SYMBOL(elv_dispatch_add_tail);
int elv_merge(struct request_queue *q, struct request **req, struct bio *bio)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
struct request *__rq;
int ret;
@@ -529,7 +530,7 @@ int elv_merge(struct request_queue *q, struct request **req, struct bio *bio)
void elv_merged_request(struct request_queue *q, struct request *rq, int type)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_merged_fn)
e->ops->elevator_merged_fn(q, rq, type);
@@ -543,7 +544,7 @@ void elv_merged_request(struct request_queue *q, struct request *rq, int type)
void elv_merge_requests(struct request_queue *q, struct request *rq,
struct request *next)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_merge_req_fn)
e->ops->elevator_merge_req_fn(q, rq, next);
@@ -755,14 +756,6 @@ struct request *elv_next_request(struct request_queue *q)
int ret;
while ((rq = __elv_next_request(q)) != NULL) {
- /*
- * Kill the empty barrier place holder, the driver must
- * not ever see it.
- */
- if (blk_empty_barrier(rq)) {
- __blk_end_request(rq, 0, blk_rq_bytes(rq));
- continue;
- }
if (!(rq->cmd_flags & REQ_STARTED)) {
/*
* This is the first time the device driver
@@ -854,7 +847,7 @@ void elv_dequeue_request(struct request_queue *q, struct request *rq)
int elv_queue_empty(struct request_queue *q)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (!list_empty(&q->queue_head))
return 0;
@@ -868,7 +861,7 @@ EXPORT_SYMBOL(elv_queue_empty);
struct request *elv_latter_request(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_latter_req_fn)
return e->ops->elevator_latter_req_fn(q, rq);
@@ -877,7 +870,7 @@ struct request *elv_latter_request(struct request_queue *q, struct request *rq)
struct request *elv_former_request(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_former_req_fn)
return e->ops->elevator_former_req_fn(q, rq);
@@ -886,7 +879,7 @@ struct request *elv_former_request(struct request_queue *q, struct request *rq)
int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_set_req_fn)
return e->ops->elevator_set_req_fn(q, rq, gfp_mask);
@@ -897,7 +890,7 @@ int elv_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
void elv_put_request(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_put_req_fn)
e->ops->elevator_put_req_fn(rq);
@@ -905,7 +898,7 @@ void elv_put_request(struct request_queue *q, struct request *rq)
int elv_may_queue(struct request_queue *q, int rw)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
if (e->ops->elevator_may_queue_fn)
return e->ops->elevator_may_queue_fn(q, rw);
@@ -928,7 +921,7 @@ EXPORT_SYMBOL(elv_abort_queue);
void elv_completed_request(struct request_queue *q, struct request *rq)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
/*
* request is released from the driver, io must be done
@@ -944,10 +937,14 @@ void elv_completed_request(struct request_queue *q, struct request *rq)
* drained for flush sequence.
*/
if (unlikely(q->ordseq)) {
- struct request *first_rq = list_entry_rq(q->queue_head.next);
- if (q->in_flight == 0 &&
+ struct request *next = NULL;
+
+ if (!list_empty(&q->queue_head))
+ next = list_entry_rq(q->queue_head.next);
+
+ if (!q->in_flight &&
blk_ordered_cur_seq(q) == QUEUE_ORDSEQ_DRAIN &&
- blk_ordered_req_seq(first_rq) > QUEUE_ORDSEQ_DRAIN) {
+ (!next || blk_ordered_req_seq(next) > QUEUE_ORDSEQ_DRAIN)) {
blk_ordered_complete_seq(q, QUEUE_ORDSEQ_DRAIN, 0);
blk_start_queueing(q);
}
@@ -959,13 +956,14 @@ void elv_completed_request(struct request_queue *q, struct request *rq)
static ssize_t
elv_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
- elevator_t *e = container_of(kobj, elevator_t, kobj);
struct elv_fs_entry *entry = to_elv(attr);
+ struct elevator_queue *e;
ssize_t error;
if (!entry->show)
return -EIO;
+ e = container_of(kobj, struct elevator_queue, kobj);
mutex_lock(&e->sysfs_lock);
error = e->ops ? entry->show(e, page) : -ENOENT;
mutex_unlock(&e->sysfs_lock);
@@ -976,13 +974,14 @@ static ssize_t
elv_attr_store(struct kobject *kobj, struct attribute *attr,
const char *page, size_t length)
{
- elevator_t *e = container_of(kobj, elevator_t, kobj);
struct elv_fs_entry *entry = to_elv(attr);
+ struct elevator_queue *e;
ssize_t error;
if (!entry->store)
return -EIO;
+ e = container_of(kobj, struct elevator_queue, kobj);
mutex_lock(&e->sysfs_lock);
error = e->ops ? entry->store(e, page, length) : -ENOENT;
mutex_unlock(&e->sysfs_lock);
@@ -1001,7 +1000,7 @@ static struct kobj_type elv_ktype = {
int elv_register_queue(struct request_queue *q)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
int error;
error = kobject_add(&e->kobj, &q->kobj, "%s", "iosched");
@@ -1019,7 +1018,7 @@ int elv_register_queue(struct request_queue *q)
return error;
}
-static void __elv_unregister_queue(elevator_t *e)
+static void __elv_unregister_queue(struct elevator_queue *e)
{
kobject_uevent(&e->kobj, KOBJ_REMOVE);
kobject_del(&e->kobj);
@@ -1082,7 +1081,7 @@ EXPORT_SYMBOL_GPL(elv_unregister);
*/
static int elevator_switch(struct request_queue *q, struct elevator_type *new_e)
{
- elevator_t *old_elevator, *e;
+ struct elevator_queue *old_elevator, *e;
void *data;
/*
@@ -1188,7 +1187,7 @@ ssize_t elv_iosched_store(struct request_queue *q, const char *name,
ssize_t elv_iosched_show(struct request_queue *q, char *name)
{
- elevator_t *e = q->elevator;
+ struct elevator_queue *e = q->elevator;
struct elevator_type *elv = e->elevator_type;
struct elevator_type *__e;
int len = 0;
diff --git a/block/genhd.c b/block/genhd.c
index 2f7feda61e35..d84a7df1e2a0 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -181,6 +181,12 @@ void disk_part_iter_exit(struct disk_part_iter *piter)
}
EXPORT_SYMBOL_GPL(disk_part_iter_exit);
+static inline int sector_in_part(struct hd_struct *part, sector_t sector)
+{
+ return part->start_sect <= sector &&
+ sector < part->start_sect + part->nr_sects;
+}
+
/**
* disk_map_sector_rcu - map sector to partition
* @disk: gendisk of interest
@@ -199,16 +205,22 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit);
struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
{
struct disk_part_tbl *ptbl;
+ struct hd_struct *part;
int i;
ptbl = rcu_dereference(disk->part_tbl);
+ part = rcu_dereference(ptbl->last_lookup);
+ if (part && sector_in_part(part, sector))
+ return part;
+
for (i = 1; i < ptbl->len; i++) {
- struct hd_struct *part = rcu_dereference(ptbl->part[i]);
+ part = rcu_dereference(ptbl->part[i]);
- if (part && part->start_sect <= sector &&
- sector < part->start_sect + part->nr_sects)
+ if (part && sector_in_part(part, sector)) {
+ rcu_assign_pointer(ptbl->last_lookup, part);
return part;
+ }
}
return &disk->part0;
}
@@ -888,8 +900,11 @@ static void disk_replace_part_tbl(struct gendisk *disk,
struct disk_part_tbl *old_ptbl = disk->part_tbl;
rcu_assign_pointer(disk->part_tbl, new_ptbl);
- if (old_ptbl)
+
+ if (old_ptbl) {
+ rcu_assign_pointer(old_ptbl->last_lookup, NULL);
call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
+ }
}
/**
diff --git a/block/ioctl.c b/block/ioctl.c
index d03985b04d67..0f22e629b13c 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -323,9 +323,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
return -ENOTTY;
- lock_kernel();
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
- unlock_kernel();
return 0;
case BLKBSZSET:
/* set the logical block size */
diff --git a/block/noop-iosched.c b/block/noop-iosched.c
index c23e02969650..3a0d369d08c7 100644
--- a/block/noop-iosched.c
+++ b/block/noop-iosched.c
@@ -76,7 +76,7 @@ static void *noop_init_queue(struct request_queue *q)
return nd;
}
-static void noop_exit_queue(elevator_t *e)
+static void noop_exit_queue(struct elevator_queue *e)
{
struct noop_data *nd = e->elevator_data;
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index d0bb92cbefb9..ee9c67d7e1be 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -60,7 +60,7 @@ static int scsi_get_bus(struct request_queue *q, int __user *p)
static int sg_get_timeout(struct request_queue *q)
{
- return q->sg_timeout / (HZ / USER_HZ);
+ return jiffies_to_clock_t(q->sg_timeout);
}
static int sg_set_timeout(struct request_queue *q, int __user *p)
@@ -68,7 +68,7 @@ static int sg_set_timeout(struct request_queue *q, int __user *p)
int timeout, err = get_user(timeout, p);
if (!err)
- q->sg_timeout = timeout * (HZ / USER_HZ);
+ q->sg_timeout = clock_t_to_jiffies(timeout);
return err;
}
diff --git a/crypto/Kconfig b/crypto/Kconfig
index dc20a34ba5ef..8dde4fcf99c9 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -102,6 +102,7 @@ config CRYPTO_NULL
tristate "Null algorithms"
select CRYPTO_ALGAPI
select CRYPTO_BLKCIPHER
+ select CRYPTO_HASH
help
These are 'Null' algorithms, used by IPsec, which do nothing.
@@ -256,12 +257,10 @@ comment "Digest"
config CRYPTO_CRC32C
tristate "CRC32c CRC algorithm"
select CRYPTO_HASH
- select LIBCRC32C
help
Castagnoli, et al Cyclic Redundancy-Check Algorithm. Used
by iSCSI for header and data digests and by others.
- See Castagnoli93. This implementation uses lib/libcrc32c.
- Module will be crc32c.
+ See Castagnoli93. Module will be crc32c.
config CRYPTO_CRC32C_INTEL
tristate "CRC32c INTEL hardware acceleration"
@@ -277,19 +276,19 @@ config CRYPTO_CRC32C_INTEL
config CRYPTO_MD4
tristate "MD4 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
MD4 message digest algorithm (RFC1320).
config CRYPTO_MD5
tristate "MD5 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
MD5 message digest algorithm (RFC1321).
config CRYPTO_MICHAEL_MIC
tristate "Michael MIC keyed digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
Michael MIC is used for message integrity protection in TKIP
(IEEE 802.11i). This algorithm is required for TKIP, but it
@@ -298,7 +297,7 @@ config CRYPTO_MICHAEL_MIC
config CRYPTO_RMD128
tristate "RIPEMD-128 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
RIPEMD-128 (ISO/IEC 10118-3:2004).
@@ -311,7 +310,7 @@ config CRYPTO_RMD128
config CRYPTO_RMD160
tristate "RIPEMD-160 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
RIPEMD-160 (ISO/IEC 10118-3:2004).
@@ -328,7 +327,7 @@ config CRYPTO_RMD160
config CRYPTO_RMD256
tristate "RIPEMD-256 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
RIPEMD-256 is an optional extension of RIPEMD-128 with a
256 bit hash. It is intended for applications that require
@@ -340,7 +339,7 @@ config CRYPTO_RMD256
config CRYPTO_RMD320
tristate "RIPEMD-320 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
RIPEMD-320 is an optional extension of RIPEMD-160 with a
320 bit hash. It is intended for applications that require
@@ -352,13 +351,13 @@ config CRYPTO_RMD320
config CRYPTO_SHA1
tristate "SHA1 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
config CRYPTO_SHA256
tristate "SHA224 and SHA256 digest algorithm"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
SHA256 secure hash standard (DFIPS 180-2).
@@ -370,7 +369,7 @@ config CRYPTO_SHA256
config CRYPTO_SHA512
tristate "SHA384 and SHA512 digest algorithms"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
SHA512 secure hash standard (DFIPS 180-2).
@@ -382,7 +381,7 @@ config CRYPTO_SHA512
config CRYPTO_TGR192
tristate "Tiger digest algorithms"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
Tiger hash algorithm 192, 160 and 128-bit hashes
@@ -395,7 +394,7 @@ config CRYPTO_TGR192
config CRYPTO_WP512
tristate "Whirlpool digest algorithms"
- select CRYPTO_ALGAPI
+ select CRYPTO_HASH
help
Whirlpool hash algorithm 512, 384 and 256-bit hashes
diff --git a/crypto/Makefile b/crypto/Makefile
index cd4a4ed078ff..46b08bf2035f 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
crypto_hash-objs := hash.o
crypto_hash-objs += ahash.o
+crypto_hash-objs += shash.o
obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
cryptomgr-objs := algboss.o testmgr.o
diff --git a/crypto/aes_generic.c b/crypto/aes_generic.c
index 136dc98d8a03..b8b66ec3883b 100644
--- a/crypto/aes_generic.c
+++ b/crypto/aes_generic.c
@@ -60,102 +60,1068 @@ static inline u8 byte(const u32 x, const unsigned n)
return x >> (n << 3);
}
-static u8 pow_tab[256] __initdata;
-static u8 log_tab[256] __initdata;
-static u8 sbx_tab[256] __initdata;
-static u8 isb_tab[256] __initdata;
-static u32 rco_tab[10];
-
-u32 crypto_ft_tab[4][256];
-u32 crypto_fl_tab[4][256];
-u32 crypto_it_tab[4][256];
-u32 crypto_il_tab[4][256];
-
-EXPORT_SYMBOL_GPL(crypto_ft_tab);
-EXPORT_SYMBOL_GPL(crypto_fl_tab);
-EXPORT_SYMBOL_GPL(crypto_it_tab);
-EXPORT_SYMBOL_GPL(crypto_il_tab);
-
-static inline u8 __init f_mult(u8 a, u8 b)
-{
- u8 aa = log_tab[a], cc = aa + log_tab[b];
-
- return pow_tab[cc + (cc < aa ? 1 : 0)];
-}
-
-#define ff_mult(a, b) (a && b ? f_mult(a, b) : 0)
-
-static void __init gen_tabs(void)
-{
- u32 i, t;
- u8 p, q;
-
- /*
- * log and power tables for GF(2**8) finite field with
- * 0x011b as modular polynomial - the simplest primitive
- * root is 0x03, used here to generate the tables
- */
-
- for (i = 0, p = 1; i < 256; ++i) {
- pow_tab[i] = (u8) p;
- log_tab[p] = (u8) i;
-
- p ^= (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+static const u32 rco_tab[10] = { 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 };
+
+const u32 crypto_ft_tab[4][256] = {
+ {
+ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6,
+ 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591,
+ 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56,
+ 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec,
+ 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
+ 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+ 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45,
+ 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b,
+ 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
+ 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
+ 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9,
+ 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
+ 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d,
+ 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f,
+ 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea,
+ 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34,
+ 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
+ 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d,
+ 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
+ 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1,
+ 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6,
+ 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972,
+ 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+ 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
+ 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511,
+ 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe,
+ 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b,
+ 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05,
+ 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
+ 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142,
+ 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf,
+ 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+ 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e,
+ 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
+ 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
+ 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3,
+ 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b,
+ 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
+ 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
+ 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14,
+ 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+ 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4,
+ 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2,
+ 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
+ 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949,
+ 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf,
+ 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
+ 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c,
+ 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
+ 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+ 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f,
+ 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc,
+ 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
+ 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
+ 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27,
+ 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
+ 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433,
+ 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9,
+ 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a,
+ 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0,
+ 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
+ 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
+ }, {
+ 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d,
+ 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154,
+ 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d,
+ 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a,
+ 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
+ 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+ 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea,
+ 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b,
+ 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a,
+ 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
+ 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908,
+ 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
+ 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e,
+ 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5,
+ 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f,
+ 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e,
+ 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
+ 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce,
+ 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
+ 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c,
+ 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed,
+ 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b,
+ 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+ 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
+ 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194,
+ 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81,
+ 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3,
+ 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a,
+ 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
+ 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263,
+ 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d,
+ 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+ 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39,
+ 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
+ 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
+ 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f,
+ 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83,
+ 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c,
+ 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
+ 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e,
+ 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+ 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6,
+ 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b,
+ 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
+ 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0,
+ 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25,
+ 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
+ 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72,
+ 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
+ 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+ 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85,
+ 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa,
+ 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
+ 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
+ 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9,
+ 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233,
+ 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7,
+ 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920,
+ 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17,
+ 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8,
+ 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11,
+ 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
+ }, {
+ 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b,
+ 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5,
+ 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b,
+ 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76,
+ 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
+ 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+ 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf,
+ 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0,
+ 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26,
+ 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
+ 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1,
+ 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
+ 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3,
+ 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a,
+ 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75,
+ 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a,
+ 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
+ 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3,
+ 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
+ 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced,
+ 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b,
+ 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39,
+ 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+ 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
+ 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485,
+ 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f,
+ 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8,
+ 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f,
+ 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
+ 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321,
+ 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2,
+ 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+ 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917,
+ 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
+ 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
+ 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc,
+ 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388,
+ 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14,
+ 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
+ 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a,
+ 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+ 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662,
+ 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79,
+ 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
+ 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9,
+ 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea,
+ 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
+ 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e,
+ 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
+ 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+ 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a,
+ 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66,
+ 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
+ 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
+ 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e,
+ 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311,
+ 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794,
+ 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9,
+ 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d,
+ 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868,
+ 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f,
+ 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
+ }, {
+ 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b,
+ 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5,
+ 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b,
+ 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676,
+ 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
+ 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+ 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf,
+ 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0,
+ 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626,
+ 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
+ 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1,
+ 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
+ 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3,
+ 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a,
+ 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575,
+ 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a,
+ 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
+ 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3,
+ 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
+ 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded,
+ 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b,
+ 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939,
+ 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+ 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
+ 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585,
+ 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f,
+ 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8,
+ 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f,
+ 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
+ 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121,
+ 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2,
+ 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+ 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717,
+ 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
+ 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
+ 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc,
+ 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888,
+ 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414,
+ 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
+ 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a,
+ 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+ 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262,
+ 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979,
+ 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
+ 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9,
+ 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea,
+ 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
+ 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e,
+ 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
+ 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+ 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a,
+ 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666,
+ 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
+ 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
+ 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e,
+ 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111,
+ 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494,
+ 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9,
+ 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d,
+ 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868,
+ 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f,
+ 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
}
+};
- log_tab[1] = 0;
-
- for (i = 0, p = 1; i < 10; ++i) {
- rco_tab[i] = p;
-
- p = (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+const u32 crypto_fl_tab[4][256] = {
+ {
+ 0x00000063, 0x0000007c, 0x00000077, 0x0000007b,
+ 0x000000f2, 0x0000006b, 0x0000006f, 0x000000c5,
+ 0x00000030, 0x00000001, 0x00000067, 0x0000002b,
+ 0x000000fe, 0x000000d7, 0x000000ab, 0x00000076,
+ 0x000000ca, 0x00000082, 0x000000c9, 0x0000007d,
+ 0x000000fa, 0x00000059, 0x00000047, 0x000000f0,
+ 0x000000ad, 0x000000d4, 0x000000a2, 0x000000af,
+ 0x0000009c, 0x000000a4, 0x00000072, 0x000000c0,
+ 0x000000b7, 0x000000fd, 0x00000093, 0x00000026,
+ 0x00000036, 0x0000003f, 0x000000f7, 0x000000cc,
+ 0x00000034, 0x000000a5, 0x000000e5, 0x000000f1,
+ 0x00000071, 0x000000d8, 0x00000031, 0x00000015,
+ 0x00000004, 0x000000c7, 0x00000023, 0x000000c3,
+ 0x00000018, 0x00000096, 0x00000005, 0x0000009a,
+ 0x00000007, 0x00000012, 0x00000080, 0x000000e2,
+ 0x000000eb, 0x00000027, 0x000000b2, 0x00000075,
+ 0x00000009, 0x00000083, 0x0000002c, 0x0000001a,
+ 0x0000001b, 0x0000006e, 0x0000005a, 0x000000a0,
+ 0x00000052, 0x0000003b, 0x000000d6, 0x000000b3,
+ 0x00000029, 0x000000e3, 0x0000002f, 0x00000084,
+ 0x00000053, 0x000000d1, 0x00000000, 0x000000ed,
+ 0x00000020, 0x000000fc, 0x000000b1, 0x0000005b,
+ 0x0000006a, 0x000000cb, 0x000000be, 0x00000039,
+ 0x0000004a, 0x0000004c, 0x00000058, 0x000000cf,
+ 0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb,
+ 0x00000043, 0x0000004d, 0x00000033, 0x00000085,
+ 0x00000045, 0x000000f9, 0x00000002, 0x0000007f,
+ 0x00000050, 0x0000003c, 0x0000009f, 0x000000a8,
+ 0x00000051, 0x000000a3, 0x00000040, 0x0000008f,
+ 0x00000092, 0x0000009d, 0x00000038, 0x000000f5,
+ 0x000000bc, 0x000000b6, 0x000000da, 0x00000021,
+ 0x00000010, 0x000000ff, 0x000000f3, 0x000000d2,
+ 0x000000cd, 0x0000000c, 0x00000013, 0x000000ec,
+ 0x0000005f, 0x00000097, 0x00000044, 0x00000017,
+ 0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d,
+ 0x00000064, 0x0000005d, 0x00000019, 0x00000073,
+ 0x00000060, 0x00000081, 0x0000004f, 0x000000dc,
+ 0x00000022, 0x0000002a, 0x00000090, 0x00000088,
+ 0x00000046, 0x000000ee, 0x000000b8, 0x00000014,
+ 0x000000de, 0x0000005e, 0x0000000b, 0x000000db,
+ 0x000000e0, 0x00000032, 0x0000003a, 0x0000000a,
+ 0x00000049, 0x00000006, 0x00000024, 0x0000005c,
+ 0x000000c2, 0x000000d3, 0x000000ac, 0x00000062,
+ 0x00000091, 0x00000095, 0x000000e4, 0x00000079,
+ 0x000000e7, 0x000000c8, 0x00000037, 0x0000006d,
+ 0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9,
+ 0x0000006c, 0x00000056, 0x000000f4, 0x000000ea,
+ 0x00000065, 0x0000007a, 0x000000ae, 0x00000008,
+ 0x000000ba, 0x00000078, 0x00000025, 0x0000002e,
+ 0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6,
+ 0x000000e8, 0x000000dd, 0x00000074, 0x0000001f,
+ 0x0000004b, 0x000000bd, 0x0000008b, 0x0000008a,
+ 0x00000070, 0x0000003e, 0x000000b5, 0x00000066,
+ 0x00000048, 0x00000003, 0x000000f6, 0x0000000e,
+ 0x00000061, 0x00000035, 0x00000057, 0x000000b9,
+ 0x00000086, 0x000000c1, 0x0000001d, 0x0000009e,
+ 0x000000e1, 0x000000f8, 0x00000098, 0x00000011,
+ 0x00000069, 0x000000d9, 0x0000008e, 0x00000094,
+ 0x0000009b, 0x0000001e, 0x00000087, 0x000000e9,
+ 0x000000ce, 0x00000055, 0x00000028, 0x000000df,
+ 0x0000008c, 0x000000a1, 0x00000089, 0x0000000d,
+ 0x000000bf, 0x000000e6, 0x00000042, 0x00000068,
+ 0x00000041, 0x00000099, 0x0000002d, 0x0000000f,
+ 0x000000b0, 0x00000054, 0x000000bb, 0x00000016,
+ }, {
+ 0x00006300, 0x00007c00, 0x00007700, 0x00007b00,
+ 0x0000f200, 0x00006b00, 0x00006f00, 0x0000c500,
+ 0x00003000, 0x00000100, 0x00006700, 0x00002b00,
+ 0x0000fe00, 0x0000d700, 0x0000ab00, 0x00007600,
+ 0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00,
+ 0x0000fa00, 0x00005900, 0x00004700, 0x0000f000,
+ 0x0000ad00, 0x0000d400, 0x0000a200, 0x0000af00,
+ 0x00009c00, 0x0000a400, 0x00007200, 0x0000c000,
+ 0x0000b700, 0x0000fd00, 0x00009300, 0x00002600,
+ 0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00,
+ 0x00003400, 0x0000a500, 0x0000e500, 0x0000f100,
+ 0x00007100, 0x0000d800, 0x00003100, 0x00001500,
+ 0x00000400, 0x0000c700, 0x00002300, 0x0000c300,
+ 0x00001800, 0x00009600, 0x00000500, 0x00009a00,
+ 0x00000700, 0x00001200, 0x00008000, 0x0000e200,
+ 0x0000eb00, 0x00002700, 0x0000b200, 0x00007500,
+ 0x00000900, 0x00008300, 0x00002c00, 0x00001a00,
+ 0x00001b00, 0x00006e00, 0x00005a00, 0x0000a000,
+ 0x00005200, 0x00003b00, 0x0000d600, 0x0000b300,
+ 0x00002900, 0x0000e300, 0x00002f00, 0x00008400,
+ 0x00005300, 0x0000d100, 0x00000000, 0x0000ed00,
+ 0x00002000, 0x0000fc00, 0x0000b100, 0x00005b00,
+ 0x00006a00, 0x0000cb00, 0x0000be00, 0x00003900,
+ 0x00004a00, 0x00004c00, 0x00005800, 0x0000cf00,
+ 0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00,
+ 0x00004300, 0x00004d00, 0x00003300, 0x00008500,
+ 0x00004500, 0x0000f900, 0x00000200, 0x00007f00,
+ 0x00005000, 0x00003c00, 0x00009f00, 0x0000a800,
+ 0x00005100, 0x0000a300, 0x00004000, 0x00008f00,
+ 0x00009200, 0x00009d00, 0x00003800, 0x0000f500,
+ 0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100,
+ 0x00001000, 0x0000ff00, 0x0000f300, 0x0000d200,
+ 0x0000cd00, 0x00000c00, 0x00001300, 0x0000ec00,
+ 0x00005f00, 0x00009700, 0x00004400, 0x00001700,
+ 0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00,
+ 0x00006400, 0x00005d00, 0x00001900, 0x00007300,
+ 0x00006000, 0x00008100, 0x00004f00, 0x0000dc00,
+ 0x00002200, 0x00002a00, 0x00009000, 0x00008800,
+ 0x00004600, 0x0000ee00, 0x0000b800, 0x00001400,
+ 0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00,
+ 0x0000e000, 0x00003200, 0x00003a00, 0x00000a00,
+ 0x00004900, 0x00000600, 0x00002400, 0x00005c00,
+ 0x0000c200, 0x0000d300, 0x0000ac00, 0x00006200,
+ 0x00009100, 0x00009500, 0x0000e400, 0x00007900,
+ 0x0000e700, 0x0000c800, 0x00003700, 0x00006d00,
+ 0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900,
+ 0x00006c00, 0x00005600, 0x0000f400, 0x0000ea00,
+ 0x00006500, 0x00007a00, 0x0000ae00, 0x00000800,
+ 0x0000ba00, 0x00007800, 0x00002500, 0x00002e00,
+ 0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600,
+ 0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00,
+ 0x00004b00, 0x0000bd00, 0x00008b00, 0x00008a00,
+ 0x00007000, 0x00003e00, 0x0000b500, 0x00006600,
+ 0x00004800, 0x00000300, 0x0000f600, 0x00000e00,
+ 0x00006100, 0x00003500, 0x00005700, 0x0000b900,
+ 0x00008600, 0x0000c100, 0x00001d00, 0x00009e00,
+ 0x0000e100, 0x0000f800, 0x00009800, 0x00001100,
+ 0x00006900, 0x0000d900, 0x00008e00, 0x00009400,
+ 0x00009b00, 0x00001e00, 0x00008700, 0x0000e900,
+ 0x0000ce00, 0x00005500, 0x00002800, 0x0000df00,
+ 0x00008c00, 0x0000a100, 0x00008900, 0x00000d00,
+ 0x0000bf00, 0x0000e600, 0x00004200, 0x00006800,
+ 0x00004100, 0x00009900, 0x00002d00, 0x00000f00,
+ 0x0000b000, 0x00005400, 0x0000bb00, 0x00001600,
+ }, {
+ 0x00630000, 0x007c0000, 0x00770000, 0x007b0000,
+ 0x00f20000, 0x006b0000, 0x006f0000, 0x00c50000,
+ 0x00300000, 0x00010000, 0x00670000, 0x002b0000,
+ 0x00fe0000, 0x00d70000, 0x00ab0000, 0x00760000,
+ 0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000,
+ 0x00fa0000, 0x00590000, 0x00470000, 0x00f00000,
+ 0x00ad0000, 0x00d40000, 0x00a20000, 0x00af0000,
+ 0x009c0000, 0x00a40000, 0x00720000, 0x00c00000,
+ 0x00b70000, 0x00fd0000, 0x00930000, 0x00260000,
+ 0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000,
+ 0x00340000, 0x00a50000, 0x00e50000, 0x00f10000,
+ 0x00710000, 0x00d80000, 0x00310000, 0x00150000,
+ 0x00040000, 0x00c70000, 0x00230000, 0x00c30000,
+ 0x00180000, 0x00960000, 0x00050000, 0x009a0000,
+ 0x00070000, 0x00120000, 0x00800000, 0x00e20000,
+ 0x00eb0000, 0x00270000, 0x00b20000, 0x00750000,
+ 0x00090000, 0x00830000, 0x002c0000, 0x001a0000,
+ 0x001b0000, 0x006e0000, 0x005a0000, 0x00a00000,
+ 0x00520000, 0x003b0000, 0x00d60000, 0x00b30000,
+ 0x00290000, 0x00e30000, 0x002f0000, 0x00840000,
+ 0x00530000, 0x00d10000, 0x00000000, 0x00ed0000,
+ 0x00200000, 0x00fc0000, 0x00b10000, 0x005b0000,
+ 0x006a0000, 0x00cb0000, 0x00be0000, 0x00390000,
+ 0x004a0000, 0x004c0000, 0x00580000, 0x00cf0000,
+ 0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000,
+ 0x00430000, 0x004d0000, 0x00330000, 0x00850000,
+ 0x00450000, 0x00f90000, 0x00020000, 0x007f0000,
+ 0x00500000, 0x003c0000, 0x009f0000, 0x00a80000,
+ 0x00510000, 0x00a30000, 0x00400000, 0x008f0000,
+ 0x00920000, 0x009d0000, 0x00380000, 0x00f50000,
+ 0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000,
+ 0x00100000, 0x00ff0000, 0x00f30000, 0x00d20000,
+ 0x00cd0000, 0x000c0000, 0x00130000, 0x00ec0000,
+ 0x005f0000, 0x00970000, 0x00440000, 0x00170000,
+ 0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000,
+ 0x00640000, 0x005d0000, 0x00190000, 0x00730000,
+ 0x00600000, 0x00810000, 0x004f0000, 0x00dc0000,
+ 0x00220000, 0x002a0000, 0x00900000, 0x00880000,
+ 0x00460000, 0x00ee0000, 0x00b80000, 0x00140000,
+ 0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000,
+ 0x00e00000, 0x00320000, 0x003a0000, 0x000a0000,
+ 0x00490000, 0x00060000, 0x00240000, 0x005c0000,
+ 0x00c20000, 0x00d30000, 0x00ac0000, 0x00620000,
+ 0x00910000, 0x00950000, 0x00e40000, 0x00790000,
+ 0x00e70000, 0x00c80000, 0x00370000, 0x006d0000,
+ 0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000,
+ 0x006c0000, 0x00560000, 0x00f40000, 0x00ea0000,
+ 0x00650000, 0x007a0000, 0x00ae0000, 0x00080000,
+ 0x00ba0000, 0x00780000, 0x00250000, 0x002e0000,
+ 0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000,
+ 0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000,
+ 0x004b0000, 0x00bd0000, 0x008b0000, 0x008a0000,
+ 0x00700000, 0x003e0000, 0x00b50000, 0x00660000,
+ 0x00480000, 0x00030000, 0x00f60000, 0x000e0000,
+ 0x00610000, 0x00350000, 0x00570000, 0x00b90000,
+ 0x00860000, 0x00c10000, 0x001d0000, 0x009e0000,
+ 0x00e10000, 0x00f80000, 0x00980000, 0x00110000,
+ 0x00690000, 0x00d90000, 0x008e0000, 0x00940000,
+ 0x009b0000, 0x001e0000, 0x00870000, 0x00e90000,
+ 0x00ce0000, 0x00550000, 0x00280000, 0x00df0000,
+ 0x008c0000, 0x00a10000, 0x00890000, 0x000d0000,
+ 0x00bf0000, 0x00e60000, 0x00420000, 0x00680000,
+ 0x00410000, 0x00990000, 0x002d0000, 0x000f0000,
+ 0x00b00000, 0x00540000, 0x00bb0000, 0x00160000,
+ }, {
+ 0x63000000, 0x7c000000, 0x77000000, 0x7b000000,
+ 0xf2000000, 0x6b000000, 0x6f000000, 0xc5000000,
+ 0x30000000, 0x01000000, 0x67000000, 0x2b000000,
+ 0xfe000000, 0xd7000000, 0xab000000, 0x76000000,
+ 0xca000000, 0x82000000, 0xc9000000, 0x7d000000,
+ 0xfa000000, 0x59000000, 0x47000000, 0xf0000000,
+ 0xad000000, 0xd4000000, 0xa2000000, 0xaf000000,
+ 0x9c000000, 0xa4000000, 0x72000000, 0xc0000000,
+ 0xb7000000, 0xfd000000, 0x93000000, 0x26000000,
+ 0x36000000, 0x3f000000, 0xf7000000, 0xcc000000,
+ 0x34000000, 0xa5000000, 0xe5000000, 0xf1000000,
+ 0x71000000, 0xd8000000, 0x31000000, 0x15000000,
+ 0x04000000, 0xc7000000, 0x23000000, 0xc3000000,
+ 0x18000000, 0x96000000, 0x05000000, 0x9a000000,
+ 0x07000000, 0x12000000, 0x80000000, 0xe2000000,
+ 0xeb000000, 0x27000000, 0xb2000000, 0x75000000,
+ 0x09000000, 0x83000000, 0x2c000000, 0x1a000000,
+ 0x1b000000, 0x6e000000, 0x5a000000, 0xa0000000,
+ 0x52000000, 0x3b000000, 0xd6000000, 0xb3000000,
+ 0x29000000, 0xe3000000, 0x2f000000, 0x84000000,
+ 0x53000000, 0xd1000000, 0x00000000, 0xed000000,
+ 0x20000000, 0xfc000000, 0xb1000000, 0x5b000000,
+ 0x6a000000, 0xcb000000, 0xbe000000, 0x39000000,
+ 0x4a000000, 0x4c000000, 0x58000000, 0xcf000000,
+ 0xd0000000, 0xef000000, 0xaa000000, 0xfb000000,
+ 0x43000000, 0x4d000000, 0x33000000, 0x85000000,
+ 0x45000000, 0xf9000000, 0x02000000, 0x7f000000,
+ 0x50000000, 0x3c000000, 0x9f000000, 0xa8000000,
+ 0x51000000, 0xa3000000, 0x40000000, 0x8f000000,
+ 0x92000000, 0x9d000000, 0x38000000, 0xf5000000,
+ 0xbc000000, 0xb6000000, 0xda000000, 0x21000000,
+ 0x10000000, 0xff000000, 0xf3000000, 0xd2000000,
+ 0xcd000000, 0x0c000000, 0x13000000, 0xec000000,
+ 0x5f000000, 0x97000000, 0x44000000, 0x17000000,
+ 0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000,
+ 0x64000000, 0x5d000000, 0x19000000, 0x73000000,
+ 0x60000000, 0x81000000, 0x4f000000, 0xdc000000,
+ 0x22000000, 0x2a000000, 0x90000000, 0x88000000,
+ 0x46000000, 0xee000000, 0xb8000000, 0x14000000,
+ 0xde000000, 0x5e000000, 0x0b000000, 0xdb000000,
+ 0xe0000000, 0x32000000, 0x3a000000, 0x0a000000,
+ 0x49000000, 0x06000000, 0x24000000, 0x5c000000,
+ 0xc2000000, 0xd3000000, 0xac000000, 0x62000000,
+ 0x91000000, 0x95000000, 0xe4000000, 0x79000000,
+ 0xe7000000, 0xc8000000, 0x37000000, 0x6d000000,
+ 0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000,
+ 0x6c000000, 0x56000000, 0xf4000000, 0xea000000,
+ 0x65000000, 0x7a000000, 0xae000000, 0x08000000,
+ 0xba000000, 0x78000000, 0x25000000, 0x2e000000,
+ 0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000,
+ 0xe8000000, 0xdd000000, 0x74000000, 0x1f000000,
+ 0x4b000000, 0xbd000000, 0x8b000000, 0x8a000000,
+ 0x70000000, 0x3e000000, 0xb5000000, 0x66000000,
+ 0x48000000, 0x03000000, 0xf6000000, 0x0e000000,
+ 0x61000000, 0x35000000, 0x57000000, 0xb9000000,
+ 0x86000000, 0xc1000000, 0x1d000000, 0x9e000000,
+ 0xe1000000, 0xf8000000, 0x98000000, 0x11000000,
+ 0x69000000, 0xd9000000, 0x8e000000, 0x94000000,
+ 0x9b000000, 0x1e000000, 0x87000000, 0xe9000000,
+ 0xce000000, 0x55000000, 0x28000000, 0xdf000000,
+ 0x8c000000, 0xa1000000, 0x89000000, 0x0d000000,
+ 0xbf000000, 0xe6000000, 0x42000000, 0x68000000,
+ 0x41000000, 0x99000000, 0x2d000000, 0x0f000000,
+ 0xb0000000, 0x54000000, 0xbb000000, 0x16000000,
}
+};
- for (i = 0; i < 256; ++i) {
- p = (i ? pow_tab[255 - log_tab[i]] : 0);
- q = ((p >> 7) | (p << 1)) ^ ((p >> 6) | (p << 2));
- p ^= 0x63 ^ q ^ ((q >> 6) | (q << 2));
- sbx_tab[i] = p;
- isb_tab[p] = (u8) i;
+const u32 crypto_it_tab[4][256] = {
+ {
+ 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a,
+ 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b,
+ 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5,
+ 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5,
+ 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
+ 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+ 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295,
+ 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e,
+ 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927,
+ 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
+ 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362,
+ 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9,
+ 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52,
+ 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566,
+ 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed,
+ 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e,
+ 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4,
+ 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4,
+ 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
+ 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d,
+ 0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060,
+ 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967,
+ 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+ 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
+ 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c,
+ 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36,
+ 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624,
+ 0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b,
+ 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
+ 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12,
+ 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14,
+ 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
+ 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b,
+ 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
+ 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684,
+ 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7,
+ 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177,
+ 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947,
+ 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
+ 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498,
+ 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+ 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54,
+ 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382,
+ 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
+ 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb,
+ 0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83,
+ 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef,
+ 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029,
+ 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
+ 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
+ 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117,
+ 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4,
+ 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546,
+ 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
+ 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d,
+ 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb,
+ 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a,
+ 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773,
+ 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2,
+ 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff,
+ 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664,
+ 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0,
+ }, {
+ 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96,
+ 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x03e34b93,
+ 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525,
+ 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f,
+ 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
+ 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
+ 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da,
+ 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44,
+ 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd,
+ 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
+ 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245,
+ 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994,
+ 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7,
+ 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a,
+ 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c,
+ 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1,
+ 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a,
+ 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475,
+ 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
+ 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46,
+ 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff,
+ 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777,
+ 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
+ 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
+ 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e,
+ 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627,
+ 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a,
+ 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e,
+ 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
+ 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d,
+ 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8,
+ 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
+ 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34,
+ 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
+ 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420,
+ 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d,
+ 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0,
+ 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722,
+ 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
+ 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836,
+ 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
+ 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462,
+ 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5,
+ 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
+ 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b,
+ 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8,
+ 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6,
+ 0x9be7bad9, 0x366f4ace, 0x099fead4, 0x7cb029d6,
+ 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
+ 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
+ 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f,
+ 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df,
+ 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f,
+ 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
+ 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13,
+ 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89,
+ 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c,
+ 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf,
+ 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f,
+ 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41,
+ 0x01a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490,
+ 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042,
+ }, {
+ 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e,
+ 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303,
+ 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c,
+ 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3,
+ 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
+ 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
+ 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59,
+ 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8,
+ 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71,
+ 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
+ 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f,
+ 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b,
+ 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8,
+ 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab,
+ 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82,
+ 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2,
+ 0xda65cdf4, 0x0506d5be, 0x34d11f62, 0xa6c48afe,
+ 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb,
+ 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
+ 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd,
+ 0x5491b58d, 0xc471055d, 0x06046fd4, 0x5060ff15,
+ 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e,
+ 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
+ 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
+ 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72,
+ 0x0efdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739,
+ 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e,
+ 0x0a0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91,
+ 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
+ 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17,
+ 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9,
+ 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
+ 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e,
+ 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
+ 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011,
+ 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1,
+ 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3,
+ 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264,
+ 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
+ 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b,
+ 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
+ 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246,
+ 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af,
+ 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
+ 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb,
+ 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a,
+ 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8,
+ 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c,
+ 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
+ 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
+ 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6,
+ 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04,
+ 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51,
+ 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
+ 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347,
+ 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c,
+ 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1,
+ 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37,
+ 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40,
+ 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195,
+ 0xa8397101, 0x0c08deb3, 0xb4d89ce4, 0x566490c1,
+ 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257,
+ }, {
+ 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27,
+ 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3,
+ 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02,
+ 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362,
+ 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
+ 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
+ 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952,
+ 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9,
+ 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9,
+ 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
+ 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53,
+ 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08,
+ 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b,
+ 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55,
+ 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216,
+ 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269,
+ 0x65cdf4da, 0x06d5be05, 0xd11f6234, 0xc48afea6,
+ 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6,
+ 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
+ 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6,
+ 0x91b58d54, 0x71055dc4, 0x046fd406, 0x60ff1550,
+ 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9,
+ 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
+ 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
+ 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a,
+ 0xfdfbff0e, 0x0f563885, 0x3d1ed5ae, 0x3627392d,
+ 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36,
+ 0x0cb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b,
+ 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
+ 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b,
+ 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e,
+ 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
+ 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb,
+ 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
+ 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6,
+ 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129,
+ 0x1d4b2f9e, 0xdcf330b2, 0x0dec5286, 0x77d0e3c1,
+ 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9,
+ 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
+ 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4,
+ 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
+ 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e,
+ 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3,
+ 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
+ 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b,
+ 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f,
+ 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815,
+ 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0,
+ 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
+ 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
+ 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691,
+ 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496,
+ 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165,
+ 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
+ 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6,
+ 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13,
+ 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147,
+ 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7,
+ 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3,
+ 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d,
+ 0x397101a8, 0x08deb30c, 0xd89ce4b4, 0x6490c156,
+ 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8,
}
+};
- for (i = 0; i < 256; ++i) {
- p = sbx_tab[i];
-
- t = p;
- crypto_fl_tab[0][i] = t;
- crypto_fl_tab[1][i] = rol32(t, 8);
- crypto_fl_tab[2][i] = rol32(t, 16);
- crypto_fl_tab[3][i] = rol32(t, 24);
-
- t = ((u32) ff_mult(2, p)) |
- ((u32) p << 8) |
- ((u32) p << 16) | ((u32) ff_mult(3, p) << 24);
-
- crypto_ft_tab[0][i] = t;
- crypto_ft_tab[1][i] = rol32(t, 8);
- crypto_ft_tab[2][i] = rol32(t, 16);
- crypto_ft_tab[3][i] = rol32(t, 24);
-
- p = isb_tab[i];
-
- t = p;
- crypto_il_tab[0][i] = t;
- crypto_il_tab[1][i] = rol32(t, 8);
- crypto_il_tab[2][i] = rol32(t, 16);
- crypto_il_tab[3][i] = rol32(t, 24);
-
- t = ((u32) ff_mult(14, p)) |
- ((u32) ff_mult(9, p) << 8) |
- ((u32) ff_mult(13, p) << 16) |
- ((u32) ff_mult(11, p) << 24);
-
- crypto_it_tab[0][i] = t;
- crypto_it_tab[1][i] = rol32(t, 8);
- crypto_it_tab[2][i] = rol32(t, 16);
- crypto_it_tab[3][i] = rol32(t, 24);
+const u32 crypto_il_tab[4][256] = {
+ {
+ 0x00000052, 0x00000009, 0x0000006a, 0x000000d5,
+ 0x00000030, 0x00000036, 0x000000a5, 0x00000038,
+ 0x000000bf, 0x00000040, 0x000000a3, 0x0000009e,
+ 0x00000081, 0x000000f3, 0x000000d7, 0x000000fb,
+ 0x0000007c, 0x000000e3, 0x00000039, 0x00000082,
+ 0x0000009b, 0x0000002f, 0x000000ff, 0x00000087,
+ 0x00000034, 0x0000008e, 0x00000043, 0x00000044,
+ 0x000000c4, 0x000000de, 0x000000e9, 0x000000cb,
+ 0x00000054, 0x0000007b, 0x00000094, 0x00000032,
+ 0x000000a6, 0x000000c2, 0x00000023, 0x0000003d,
+ 0x000000ee, 0x0000004c, 0x00000095, 0x0000000b,
+ 0x00000042, 0x000000fa, 0x000000c3, 0x0000004e,
+ 0x00000008, 0x0000002e, 0x000000a1, 0x00000066,
+ 0x00000028, 0x000000d9, 0x00000024, 0x000000b2,
+ 0x00000076, 0x0000005b, 0x000000a2, 0x00000049,
+ 0x0000006d, 0x0000008b, 0x000000d1, 0x00000025,
+ 0x00000072, 0x000000f8, 0x000000f6, 0x00000064,
+ 0x00000086, 0x00000068, 0x00000098, 0x00000016,
+ 0x000000d4, 0x000000a4, 0x0000005c, 0x000000cc,
+ 0x0000005d, 0x00000065, 0x000000b6, 0x00000092,
+ 0x0000006c, 0x00000070, 0x00000048, 0x00000050,
+ 0x000000fd, 0x000000ed, 0x000000b9, 0x000000da,
+ 0x0000005e, 0x00000015, 0x00000046, 0x00000057,
+ 0x000000a7, 0x0000008d, 0x0000009d, 0x00000084,
+ 0x00000090, 0x000000d8, 0x000000ab, 0x00000000,
+ 0x0000008c, 0x000000bc, 0x000000d3, 0x0000000a,
+ 0x000000f7, 0x000000e4, 0x00000058, 0x00000005,
+ 0x000000b8, 0x000000b3, 0x00000045, 0x00000006,
+ 0x000000d0, 0x0000002c, 0x0000001e, 0x0000008f,
+ 0x000000ca, 0x0000003f, 0x0000000f, 0x00000002,
+ 0x000000c1, 0x000000af, 0x000000bd, 0x00000003,
+ 0x00000001, 0x00000013, 0x0000008a, 0x0000006b,
+ 0x0000003a, 0x00000091, 0x00000011, 0x00000041,
+ 0x0000004f, 0x00000067, 0x000000dc, 0x000000ea,
+ 0x00000097, 0x000000f2, 0x000000cf, 0x000000ce,
+ 0x000000f0, 0x000000b4, 0x000000e6, 0x00000073,
+ 0x00000096, 0x000000ac, 0x00000074, 0x00000022,
+ 0x000000e7, 0x000000ad, 0x00000035, 0x00000085,
+ 0x000000e2, 0x000000f9, 0x00000037, 0x000000e8,
+ 0x0000001c, 0x00000075, 0x000000df, 0x0000006e,
+ 0x00000047, 0x000000f1, 0x0000001a, 0x00000071,
+ 0x0000001d, 0x00000029, 0x000000c5, 0x00000089,
+ 0x0000006f, 0x000000b7, 0x00000062, 0x0000000e,
+ 0x000000aa, 0x00000018, 0x000000be, 0x0000001b,
+ 0x000000fc, 0x00000056, 0x0000003e, 0x0000004b,
+ 0x000000c6, 0x000000d2, 0x00000079, 0x00000020,
+ 0x0000009a, 0x000000db, 0x000000c0, 0x000000fe,
+ 0x00000078, 0x000000cd, 0x0000005a, 0x000000f4,
+ 0x0000001f, 0x000000dd, 0x000000a8, 0x00000033,
+ 0x00000088, 0x00000007, 0x000000c7, 0x00000031,
+ 0x000000b1, 0x00000012, 0x00000010, 0x00000059,
+ 0x00000027, 0x00000080, 0x000000ec, 0x0000005f,
+ 0x00000060, 0x00000051, 0x0000007f, 0x000000a9,
+ 0x00000019, 0x000000b5, 0x0000004a, 0x0000000d,
+ 0x0000002d, 0x000000e5, 0x0000007a, 0x0000009f,
+ 0x00000093, 0x000000c9, 0x0000009c, 0x000000ef,
+ 0x000000a0, 0x000000e0, 0x0000003b, 0x0000004d,
+ 0x000000ae, 0x0000002a, 0x000000f5, 0x000000b0,
+ 0x000000c8, 0x000000eb, 0x000000bb, 0x0000003c,
+ 0x00000083, 0x00000053, 0x00000099, 0x00000061,
+ 0x00000017, 0x0000002b, 0x00000004, 0x0000007e,
+ 0x000000ba, 0x00000077, 0x000000d6, 0x00000026,
+ 0x000000e1, 0x00000069, 0x00000014, 0x00000063,
+ 0x00000055, 0x00000021, 0x0000000c, 0x0000007d,
+ }, {
+ 0x00005200, 0x00000900, 0x00006a00, 0x0000d500,
+ 0x00003000, 0x00003600, 0x0000a500, 0x00003800,
+ 0x0000bf00, 0x00004000, 0x0000a300, 0x00009e00,
+ 0x00008100, 0x0000f300, 0x0000d700, 0x0000fb00,
+ 0x00007c00, 0x0000e300, 0x00003900, 0x00008200,
+ 0x00009b00, 0x00002f00, 0x0000ff00, 0x00008700,
+ 0x00003400, 0x00008e00, 0x00004300, 0x00004400,
+ 0x0000c400, 0x0000de00, 0x0000e900, 0x0000cb00,
+ 0x00005400, 0x00007b00, 0x00009400, 0x00003200,
+ 0x0000a600, 0x0000c200, 0x00002300, 0x00003d00,
+ 0x0000ee00, 0x00004c00, 0x00009500, 0x00000b00,
+ 0x00004200, 0x0000fa00, 0x0000c300, 0x00004e00,
+ 0x00000800, 0x00002e00, 0x0000a100, 0x00006600,
+ 0x00002800, 0x0000d900, 0x00002400, 0x0000b200,
+ 0x00007600, 0x00005b00, 0x0000a200, 0x00004900,
+ 0x00006d00, 0x00008b00, 0x0000d100, 0x00002500,
+ 0x00007200, 0x0000f800, 0x0000f600, 0x00006400,
+ 0x00008600, 0x00006800, 0x00009800, 0x00001600,
+ 0x0000d400, 0x0000a400, 0x00005c00, 0x0000cc00,
+ 0x00005d00, 0x00006500, 0x0000b600, 0x00009200,
+ 0x00006c00, 0x00007000, 0x00004800, 0x00005000,
+ 0x0000fd00, 0x0000ed00, 0x0000b900, 0x0000da00,
+ 0x00005e00, 0x00001500, 0x00004600, 0x00005700,
+ 0x0000a700, 0x00008d00, 0x00009d00, 0x00008400,
+ 0x00009000, 0x0000d800, 0x0000ab00, 0x00000000,
+ 0x00008c00, 0x0000bc00, 0x0000d300, 0x00000a00,
+ 0x0000f700, 0x0000e400, 0x00005800, 0x00000500,
+ 0x0000b800, 0x0000b300, 0x00004500, 0x00000600,
+ 0x0000d000, 0x00002c00, 0x00001e00, 0x00008f00,
+ 0x0000ca00, 0x00003f00, 0x00000f00, 0x00000200,
+ 0x0000c100, 0x0000af00, 0x0000bd00, 0x00000300,
+ 0x00000100, 0x00001300, 0x00008a00, 0x00006b00,
+ 0x00003a00, 0x00009100, 0x00001100, 0x00004100,
+ 0x00004f00, 0x00006700, 0x0000dc00, 0x0000ea00,
+ 0x00009700, 0x0000f200, 0x0000cf00, 0x0000ce00,
+ 0x0000f000, 0x0000b400, 0x0000e600, 0x00007300,
+ 0x00009600, 0x0000ac00, 0x00007400, 0x00002200,
+ 0x0000e700, 0x0000ad00, 0x00003500, 0x00008500,
+ 0x0000e200, 0x0000f900, 0x00003700, 0x0000e800,
+ 0x00001c00, 0x00007500, 0x0000df00, 0x00006e00,
+ 0x00004700, 0x0000f100, 0x00001a00, 0x00007100,
+ 0x00001d00, 0x00002900, 0x0000c500, 0x00008900,
+ 0x00006f00, 0x0000b700, 0x00006200, 0x00000e00,
+ 0x0000aa00, 0x00001800, 0x0000be00, 0x00001b00,
+ 0x0000fc00, 0x00005600, 0x00003e00, 0x00004b00,
+ 0x0000c600, 0x0000d200, 0x00007900, 0x00002000,
+ 0x00009a00, 0x0000db00, 0x0000c000, 0x0000fe00,
+ 0x00007800, 0x0000cd00, 0x00005a00, 0x0000f400,
+ 0x00001f00, 0x0000dd00, 0x0000a800, 0x00003300,
+ 0x00008800, 0x00000700, 0x0000c700, 0x00003100,
+ 0x0000b100, 0x00001200, 0x00001000, 0x00005900,
+ 0x00002700, 0x00008000, 0x0000ec00, 0x00005f00,
+ 0x00006000, 0x00005100, 0x00007f00, 0x0000a900,
+ 0x00001900, 0x0000b500, 0x00004a00, 0x00000d00,
+ 0x00002d00, 0x0000e500, 0x00007a00, 0x00009f00,
+ 0x00009300, 0x0000c900, 0x00009c00, 0x0000ef00,
+ 0x0000a000, 0x0000e000, 0x00003b00, 0x00004d00,
+ 0x0000ae00, 0x00002a00, 0x0000f500, 0x0000b000,
+ 0x0000c800, 0x0000eb00, 0x0000bb00, 0x00003c00,
+ 0x00008300, 0x00005300, 0x00009900, 0x00006100,
+ 0x00001700, 0x00002b00, 0x00000400, 0x00007e00,
+ 0x0000ba00, 0x00007700, 0x0000d600, 0x00002600,
+ 0x0000e100, 0x00006900, 0x00001400, 0x00006300,
+ 0x00005500, 0x00002100, 0x00000c00, 0x00007d00,
+ }, {
+ 0x00520000, 0x00090000, 0x006a0000, 0x00d50000,
+ 0x00300000, 0x00360000, 0x00a50000, 0x00380000,
+ 0x00bf0000, 0x00400000, 0x00a30000, 0x009e0000,
+ 0x00810000, 0x00f30000, 0x00d70000, 0x00fb0000,
+ 0x007c0000, 0x00e30000, 0x00390000, 0x00820000,
+ 0x009b0000, 0x002f0000, 0x00ff0000, 0x00870000,
+ 0x00340000, 0x008e0000, 0x00430000, 0x00440000,
+ 0x00c40000, 0x00de0000, 0x00e90000, 0x00cb0000,
+ 0x00540000, 0x007b0000, 0x00940000, 0x00320000,
+ 0x00a60000, 0x00c20000, 0x00230000, 0x003d0000,
+ 0x00ee0000, 0x004c0000, 0x00950000, 0x000b0000,
+ 0x00420000, 0x00fa0000, 0x00c30000, 0x004e0000,
+ 0x00080000, 0x002e0000, 0x00a10000, 0x00660000,
+ 0x00280000, 0x00d90000, 0x00240000, 0x00b20000,
+ 0x00760000, 0x005b0000, 0x00a20000, 0x00490000,
+ 0x006d0000, 0x008b0000, 0x00d10000, 0x00250000,
+ 0x00720000, 0x00f80000, 0x00f60000, 0x00640000,
+ 0x00860000, 0x00680000, 0x00980000, 0x00160000,
+ 0x00d40000, 0x00a40000, 0x005c0000, 0x00cc0000,
+ 0x005d0000, 0x00650000, 0x00b60000, 0x00920000,
+ 0x006c0000, 0x00700000, 0x00480000, 0x00500000,
+ 0x00fd0000, 0x00ed0000, 0x00b90000, 0x00da0000,
+ 0x005e0000, 0x00150000, 0x00460000, 0x00570000,
+ 0x00a70000, 0x008d0000, 0x009d0000, 0x00840000,
+ 0x00900000, 0x00d80000, 0x00ab0000, 0x00000000,
+ 0x008c0000, 0x00bc0000, 0x00d30000, 0x000a0000,
+ 0x00f70000, 0x00e40000, 0x00580000, 0x00050000,
+ 0x00b80000, 0x00b30000, 0x00450000, 0x00060000,
+ 0x00d00000, 0x002c0000, 0x001e0000, 0x008f0000,
+ 0x00ca0000, 0x003f0000, 0x000f0000, 0x00020000,
+ 0x00c10000, 0x00af0000, 0x00bd0000, 0x00030000,
+ 0x00010000, 0x00130000, 0x008a0000, 0x006b0000,
+ 0x003a0000, 0x00910000, 0x00110000, 0x00410000,
+ 0x004f0000, 0x00670000, 0x00dc0000, 0x00ea0000,
+ 0x00970000, 0x00f20000, 0x00cf0000, 0x00ce0000,
+ 0x00f00000, 0x00b40000, 0x00e60000, 0x00730000,
+ 0x00960000, 0x00ac0000, 0x00740000, 0x00220000,
+ 0x00e70000, 0x00ad0000, 0x00350000, 0x00850000,
+ 0x00e20000, 0x00f90000, 0x00370000, 0x00e80000,
+ 0x001c0000, 0x00750000, 0x00df0000, 0x006e0000,
+ 0x00470000, 0x00f10000, 0x001a0000, 0x00710000,
+ 0x001d0000, 0x00290000, 0x00c50000, 0x00890000,
+ 0x006f0000, 0x00b70000, 0x00620000, 0x000e0000,
+ 0x00aa0000, 0x00180000, 0x00be0000, 0x001b0000,
+ 0x00fc0000, 0x00560000, 0x003e0000, 0x004b0000,
+ 0x00c60000, 0x00d20000, 0x00790000, 0x00200000,
+ 0x009a0000, 0x00db0000, 0x00c00000, 0x00fe0000,
+ 0x00780000, 0x00cd0000, 0x005a0000, 0x00f40000,
+ 0x001f0000, 0x00dd0000, 0x00a80000, 0x00330000,
+ 0x00880000, 0x00070000, 0x00c70000, 0x00310000,
+ 0x00b10000, 0x00120000, 0x00100000, 0x00590000,
+ 0x00270000, 0x00800000, 0x00ec0000, 0x005f0000,
+ 0x00600000, 0x00510000, 0x007f0000, 0x00a90000,
+ 0x00190000, 0x00b50000, 0x004a0000, 0x000d0000,
+ 0x002d0000, 0x00e50000, 0x007a0000, 0x009f0000,
+ 0x00930000, 0x00c90000, 0x009c0000, 0x00ef0000,
+ 0x00a00000, 0x00e00000, 0x003b0000, 0x004d0000,
+ 0x00ae0000, 0x002a0000, 0x00f50000, 0x00b00000,
+ 0x00c80000, 0x00eb0000, 0x00bb0000, 0x003c0000,
+ 0x00830000, 0x00530000, 0x00990000, 0x00610000,
+ 0x00170000, 0x002b0000, 0x00040000, 0x007e0000,
+ 0x00ba0000, 0x00770000, 0x00d60000, 0x00260000,
+ 0x00e10000, 0x00690000, 0x00140000, 0x00630000,
+ 0x00550000, 0x00210000, 0x000c0000, 0x007d0000,
+ }, {
+ 0x52000000, 0x09000000, 0x6a000000, 0xd5000000,
+ 0x30000000, 0x36000000, 0xa5000000, 0x38000000,
+ 0xbf000000, 0x40000000, 0xa3000000, 0x9e000000,
+ 0x81000000, 0xf3000000, 0xd7000000, 0xfb000000,
+ 0x7c000000, 0xe3000000, 0x39000000, 0x82000000,
+ 0x9b000000, 0x2f000000, 0xff000000, 0x87000000,
+ 0x34000000, 0x8e000000, 0x43000000, 0x44000000,
+ 0xc4000000, 0xde000000, 0xe9000000, 0xcb000000,
+ 0x54000000, 0x7b000000, 0x94000000, 0x32000000,
+ 0xa6000000, 0xc2000000, 0x23000000, 0x3d000000,
+ 0xee000000, 0x4c000000, 0x95000000, 0x0b000000,
+ 0x42000000, 0xfa000000, 0xc3000000, 0x4e000000,
+ 0x08000000, 0x2e000000, 0xa1000000, 0x66000000,
+ 0x28000000, 0xd9000000, 0x24000000, 0xb2000000,
+ 0x76000000, 0x5b000000, 0xa2000000, 0x49000000,
+ 0x6d000000, 0x8b000000, 0xd1000000, 0x25000000,
+ 0x72000000, 0xf8000000, 0xf6000000, 0x64000000,
+ 0x86000000, 0x68000000, 0x98000000, 0x16000000,
+ 0xd4000000, 0xa4000000, 0x5c000000, 0xcc000000,
+ 0x5d000000, 0x65000000, 0xb6000000, 0x92000000,
+ 0x6c000000, 0x70000000, 0x48000000, 0x50000000,
+ 0xfd000000, 0xed000000, 0xb9000000, 0xda000000,
+ 0x5e000000, 0x15000000, 0x46000000, 0x57000000,
+ 0xa7000000, 0x8d000000, 0x9d000000, 0x84000000,
+ 0x90000000, 0xd8000000, 0xab000000, 0x00000000,
+ 0x8c000000, 0xbc000000, 0xd3000000, 0x0a000000,
+ 0xf7000000, 0xe4000000, 0x58000000, 0x05000000,
+ 0xb8000000, 0xb3000000, 0x45000000, 0x06000000,
+ 0xd0000000, 0x2c000000, 0x1e000000, 0x8f000000,
+ 0xca000000, 0x3f000000, 0x0f000000, 0x02000000,
+ 0xc1000000, 0xaf000000, 0xbd000000, 0x03000000,
+ 0x01000000, 0x13000000, 0x8a000000, 0x6b000000,
+ 0x3a000000, 0x91000000, 0x11000000, 0x41000000,
+ 0x4f000000, 0x67000000, 0xdc000000, 0xea000000,
+ 0x97000000, 0xf2000000, 0xcf000000, 0xce000000,
+ 0xf0000000, 0xb4000000, 0xe6000000, 0x73000000,
+ 0x96000000, 0xac000000, 0x74000000, 0x22000000,
+ 0xe7000000, 0xad000000, 0x35000000, 0x85000000,
+ 0xe2000000, 0xf9000000, 0x37000000, 0xe8000000,
+ 0x1c000000, 0x75000000, 0xdf000000, 0x6e000000,
+ 0x47000000, 0xf1000000, 0x1a000000, 0x71000000,
+ 0x1d000000, 0x29000000, 0xc5000000, 0x89000000,
+ 0x6f000000, 0xb7000000, 0x62000000, 0x0e000000,
+ 0xaa000000, 0x18000000, 0xbe000000, 0x1b000000,
+ 0xfc000000, 0x56000000, 0x3e000000, 0x4b000000,
+ 0xc6000000, 0xd2000000, 0x79000000, 0x20000000,
+ 0x9a000000, 0xdb000000, 0xc0000000, 0xfe000000,
+ 0x78000000, 0xcd000000, 0x5a000000, 0xf4000000,
+ 0x1f000000, 0xdd000000, 0xa8000000, 0x33000000,
+ 0x88000000, 0x07000000, 0xc7000000, 0x31000000,
+ 0xb1000000, 0x12000000, 0x10000000, 0x59000000,
+ 0x27000000, 0x80000000, 0xec000000, 0x5f000000,
+ 0x60000000, 0x51000000, 0x7f000000, 0xa9000000,
+ 0x19000000, 0xb5000000, 0x4a000000, 0x0d000000,
+ 0x2d000000, 0xe5000000, 0x7a000000, 0x9f000000,
+ 0x93000000, 0xc9000000, 0x9c000000, 0xef000000,
+ 0xa0000000, 0xe0000000, 0x3b000000, 0x4d000000,
+ 0xae000000, 0x2a000000, 0xf5000000, 0xb0000000,
+ 0xc8000000, 0xeb000000, 0xbb000000, 0x3c000000,
+ 0x83000000, 0x53000000, 0x99000000, 0x61000000,
+ 0x17000000, 0x2b000000, 0x04000000, 0x7e000000,
+ 0xba000000, 0x77000000, 0xd6000000, 0x26000000,
+ 0xe1000000, 0x69000000, 0x14000000, 0x63000000,
+ 0x55000000, 0x21000000, 0x0c000000, 0x7d000000,
}
-}
+};
+
+EXPORT_SYMBOL_GPL(crypto_ft_tab);
+EXPORT_SYMBOL_GPL(crypto_fl_tab);
+EXPORT_SYMBOL_GPL(crypto_it_tab);
+EXPORT_SYMBOL_GPL(crypto_il_tab);
/* initialise the key schedule from the user supplied key */
@@ -491,7 +1457,6 @@ static struct crypto_alg aes_alg = {
static int __init aes_init(void)
{
- gen_tabs();
return crypto_register_alg(&aes_alg);
}
diff --git a/crypto/ahash.c b/crypto/ahash.c
index 27128f2c687a..ba5292d69ebd 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -112,6 +112,22 @@ int crypto_hash_walk_first(struct ahash_request *req,
}
EXPORT_SYMBOL_GPL(crypto_hash_walk_first);
+int crypto_hash_walk_first_compat(struct hash_desc *hdesc,
+ struct crypto_hash_walk *walk,
+ struct scatterlist *sg, unsigned int len)
+{
+ walk->total = len;
+
+ if (!walk->total)
+ return 0;
+
+ walk->alignmask = crypto_hash_alignmask(hdesc->tfm);
+ walk->sg = sg;
+ walk->flags = hdesc->flags;
+
+ return hash_walk_new_entry(walk);
+}
+
static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen)
{
@@ -146,6 +162,26 @@ static int ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
return ahash->setkey(tfm, key, keylen);
}
+static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ return -ENOSYS;
+}
+
+int crypto_ahash_import(struct ahash_request *req, const u8 *in)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct ahash_alg *alg = crypto_ahash_alg(tfm);
+
+ memcpy(ahash_request_ctx(req), in, crypto_ahash_reqsize(tfm));
+
+ if (alg->reinit)
+ alg->reinit(req);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_ahash_import);
+
static unsigned int crypto_ahash_ctxsize(struct crypto_alg *alg, u32 type,
u32 mask)
{
@@ -164,7 +200,7 @@ static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
crt->update = alg->update;
crt->final = alg->final;
crt->digest = alg->digest;
- crt->setkey = ahash_setkey;
+ crt->setkey = alg->setkey ? ahash_setkey : ahash_nosetkey;
crt->digestsize = alg->digestsize;
return 0;
diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
index 72db0fd763cc..0fac8ffc2fb7 100644
--- a/crypto/ansi_cprng.c
+++ b/crypto/ansi_cprng.c
@@ -161,7 +161,7 @@ static int _get_more_prng_bytes(struct prng_context *ctx)
/*
* Now update our DT value
*/
- for (i = 0; i < DEFAULT_BLK_SZ; i++) {
+ for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) {
ctx->DT[i] += 1;
if (ctx->DT[i] != 0)
break;
@@ -223,9 +223,10 @@ remainder:
}
/*
- * Copy up to the next whole block size
+ * Copy any data less than an entire block
*/
if (byte_count < DEFAULT_BLK_SZ) {
+empty_rbuf:
for (; ctx->rand_data_valid < DEFAULT_BLK_SZ;
ctx->rand_data_valid++) {
*ptr = ctx->rand_data[ctx->rand_data_valid];
@@ -240,18 +241,22 @@ remainder:
* Now copy whole blocks
*/
for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
- if (_get_more_prng_bytes(ctx) < 0) {
- memset(buf, 0, nbytes);
- err = -EINVAL;
- goto done;
+ if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
+ if (_get_more_prng_bytes(ctx) < 0) {
+ memset(buf, 0, nbytes);
+ err = -EINVAL;
+ goto done;
+ }
}
+ if (ctx->rand_data_valid > 0)
+ goto empty_rbuf;
memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
ctx->rand_data_valid += DEFAULT_BLK_SZ;
ptr += DEFAULT_BLK_SZ;
}
/*
- * Now copy any extra partial data
+ * Now go back and get any remaining partial block
*/
if (byte_count)
goto remainder;
@@ -349,15 +354,25 @@ static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
return get_prng_bytes(rdata, dlen, prng);
}
+/*
+ * This is the cprng_registered reset method the seed value is
+ * interpreted as the tuple { V KEY DT}
+ * V and KEY are required during reset, and DT is optional, detected
+ * as being present by testing the length of the seed
+ */
static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
{
struct prng_context *prng = crypto_rng_ctx(tfm);
- u8 *key = seed + DEFAULT_PRNG_KSZ;
+ u8 *key = seed + DEFAULT_BLK_SZ;
+ u8 *dt = NULL;
if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
return -EINVAL;
- reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, NULL);
+ if (slen >= (2 * DEFAULT_BLK_SZ + DEFAULT_PRNG_KSZ))
+ dt = key + DEFAULT_PRNG_KSZ;
+
+ reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, dt);
if (prng->flags & PRNG_NEED_RESET)
return -EINVAL;
@@ -379,7 +394,7 @@ static struct crypto_alg rng_alg = {
.rng = {
.rng_make_random = cprng_get_random,
.rng_reset = cprng_reset,
- .seedsize = DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ,
+ .seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
}
}
};
diff --git a/crypto/api.c b/crypto/api.c
index 0444d242e985..9975a7bd246c 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -300,8 +300,8 @@ static void crypto_exit_ops(struct crypto_tfm *tfm)
const struct crypto_type *type = tfm->__crt_alg->cra_type;
if (type) {
- if (type->exit)
- type->exit(tfm);
+ if (tfm->exit)
+ tfm->exit(tfm);
return;
}
@@ -379,17 +379,16 @@ struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
if (err)
goto out_free_tfm;
- if (alg->cra_init && (err = alg->cra_init(tfm))) {
- if (err == -EAGAIN)
- crypto_shoot_alg(alg);
+ if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
goto cra_init_failed;
- }
goto out;
cra_init_failed:
crypto_exit_ops(tfm);
out_free_tfm:
+ if (err == -EAGAIN)
+ crypto_shoot_alg(alg);
kfree(tfm);
out_err:
tfm = ERR_PTR(err);
@@ -404,6 +403,9 @@ EXPORT_SYMBOL_GPL(__crypto_alloc_tfm);
* @type: Type of algorithm
* @mask: Mask for type comparison
*
+ * This function should not be used by new algorithm types.
+ * Plesae use crypto_alloc_tfm instead.
+ *
* crypto_alloc_base() will first attempt to locate an already loaded
* algorithm. If that fails and the kernel supports dynamically loadable
* modules, it will then attempt to load a module of the same name or
@@ -450,6 +452,111 @@ err:
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(crypto_alloc_base);
+
+struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
+ const struct crypto_type *frontend)
+{
+ char *mem;
+ struct crypto_tfm *tfm = NULL;
+ unsigned int tfmsize;
+ unsigned int total;
+ int err = -ENOMEM;
+
+ tfmsize = frontend->tfmsize;
+ total = tfmsize + sizeof(*tfm) + frontend->extsize(alg, frontend);
+
+ mem = kzalloc(total, GFP_KERNEL);
+ if (mem == NULL)
+ goto out_err;
+
+ tfm = (struct crypto_tfm *)(mem + tfmsize);
+ tfm->__crt_alg = alg;
+
+ err = frontend->init_tfm(tfm, frontend);
+ if (err)
+ goto out_free_tfm;
+
+ if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
+ goto cra_init_failed;
+
+ goto out;
+
+cra_init_failed:
+ crypto_exit_ops(tfm);
+out_free_tfm:
+ if (err == -EAGAIN)
+ crypto_shoot_alg(alg);
+ kfree(mem);
+out_err:
+ tfm = ERR_PTR(err);
+out:
+ return tfm;
+}
+EXPORT_SYMBOL_GPL(crypto_create_tfm);
+
+/*
+ * crypto_alloc_tfm - Locate algorithm and allocate transform
+ * @alg_name: Name of algorithm
+ * @frontend: Frontend algorithm type
+ * @type: Type of algorithm
+ * @mask: Mask for type comparison
+ *
+ * crypto_alloc_tfm() will first attempt to locate an already loaded
+ * algorithm. If that fails and the kernel supports dynamically loadable
+ * modules, it will then attempt to load a module of the same name or
+ * alias. If that fails it will send a query to any loaded crypto manager
+ * to construct an algorithm on the fly. A refcount is grabbed on the
+ * algorithm which is then associated with the new transform.
+ *
+ * The returned transform is of a non-determinate type. Most people
+ * should use one of the more specific allocation functions such as
+ * crypto_alloc_blkcipher.
+ *
+ * In case of error the return value is an error pointer.
+ */
+struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
+ const struct crypto_type *frontend,
+ u32 type, u32 mask)
+{
+ struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
+ struct crypto_tfm *tfm;
+ int err;
+
+ type &= frontend->maskclear;
+ mask &= frontend->maskclear;
+ type |= frontend->type;
+ mask |= frontend->maskset;
+
+ lookup = frontend->lookup ?: crypto_alg_mod_lookup;
+
+ for (;;) {
+ struct crypto_alg *alg;
+
+ alg = lookup(alg_name, type, mask);
+ if (IS_ERR(alg)) {
+ err = PTR_ERR(alg);
+ goto err;
+ }
+
+ tfm = crypto_create_tfm(alg, frontend);
+ if (!IS_ERR(tfm))
+ return tfm;
+
+ crypto_mod_put(alg);
+ err = PTR_ERR(tfm);
+
+err:
+ if (err != -EAGAIN)
+ break;
+ if (signal_pending(current)) {
+ err = -EINTR;
+ break;
+ }
+ }
+
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
/*
* crypto_free_tfm - Free crypto transform
@@ -469,7 +576,7 @@ void crypto_free_tfm(struct crypto_tfm *tfm)
alg = tfm->__crt_alg;
size = sizeof(*tfm) + alg->cra_ctxsize;
- if (alg->cra_exit)
+ if (!tfm->exit && alg->cra_exit)
alg->cra_exit(tfm);
crypto_exit_ops(tfm);
crypto_mod_put(alg);
diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c
index c029d3eb9ef0..595b78672b36 100644
--- a/crypto/async_tx/async_xor.c
+++ b/crypto/async_tx/async_xor.c
@@ -53,10 +53,17 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
int xor_src_cnt;
dma_addr_t dma_dest;
- dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_FROM_DEVICE);
- for (i = 0; i < src_cnt; i++)
+ /* map the dest bidrectional in case it is re-used as a source */
+ dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL);
+ for (i = 0; i < src_cnt; i++) {
+ /* only map the dest once */
+ if (unlikely(src_list[i] == dest)) {
+ dma_src[i] = dma_dest;
+ continue;
+ }
dma_src[i] = dma_map_page(dma->dev, src_list[i], offset,
len, DMA_TO_DEVICE);
+ }
while (src_cnt) {
async_flags = flags;
diff --git a/crypto/authenc.c b/crypto/authenc.c
index fd9f06c63d76..40b6e9ec9e3a 100644
--- a/crypto/authenc.c
+++ b/crypto/authenc.c
@@ -11,6 +11,7 @@
*/
#include <crypto/aead.h>
+#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
@@ -431,6 +432,8 @@ static struct crypto_instance *crypto_authenc_alloc(struct rtattr **tb)
inst->alg.cra_aead.ivsize = enc->cra_ablkcipher.ivsize;
inst->alg.cra_aead.maxauthsize = auth->cra_type == &crypto_hash_type ?
auth->cra_hash.digestsize :
+ auth->cra_type ?
+ __crypto_shash_alg(auth)->digestsize :
auth->cra_digest.dia_digestsize;
inst->alg.cra_ctxsize = sizeof(struct crypto_authenc_ctx);
diff --git a/crypto/camellia.c b/crypto/camellia.c
index 493fee7e0a8b..964635d163f4 100644
--- a/crypto/camellia.c
+++ b/crypto/camellia.c
@@ -35,6 +35,8 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
static const u32 camellia_sp1110[256] = {
0x70707000,0x82828200,0x2c2c2c00,0xececec00,
@@ -335,20 +337,6 @@ static const u32 camellia_sp4404[256] = {
/*
* macros
*/
-#define GETU32(v, pt) \
- do { \
- /* latest breed of gcc is clever enough to use move */ \
- memcpy(&(v), (pt), 4); \
- (v) = be32_to_cpu(v); \
- } while(0)
-
-/* rotation right shift 1byte */
-#define ROR8(x) (((x) >> 8) + ((x) << 24))
-/* rotation left shift 1bit */
-#define ROL1(x) (((x) << 1) + ((x) >> 31))
-/* rotation left shift 1byte */
-#define ROL8(x) (((x) << 8) + ((x) >> 24))
-
#define ROLDQ(ll, lr, rl, rr, w0, w1, bits) \
do { \
w0 = ll; \
@@ -383,7 +371,7 @@ static const u32 camellia_sp4404[256] = {
^ camellia_sp3033[(u8)(il >> 8)] \
^ camellia_sp4404[(u8)(il )]; \
yl ^= yr; \
- yr = ROR8(yr); \
+ yr = ror32(yr, 8); \
yr ^= yl; \
} while(0)
@@ -405,7 +393,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
subL[7] ^= subL[1]; subR[7] ^= subR[1];
subL[1] ^= subR[1] & ~subR[9];
dw = subL[1] & subL[9],
- subR[1] ^= ROL1(dw); /* modified for FLinv(kl2) */
+ subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl2) */
/* round 8 */
subL[11] ^= subL[1]; subR[11] ^= subR[1];
/* round 10 */
@@ -414,7 +402,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
subL[15] ^= subL[1]; subR[15] ^= subR[1];
subL[1] ^= subR[1] & ~subR[17];
dw = subL[1] & subL[17],
- subR[1] ^= ROL1(dw); /* modified for FLinv(kl4) */
+ subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl4) */
/* round 14 */
subL[19] ^= subL[1]; subR[19] ^= subR[1];
/* round 16 */
@@ -430,7 +418,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
} else {
subL[1] ^= subR[1] & ~subR[25];
dw = subL[1] & subL[25],
- subR[1] ^= ROL1(dw); /* modified for FLinv(kl6) */
+ subR[1] ^= rol32(dw, 1); /* modified for FLinv(kl6) */
/* round 20 */
subL[27] ^= subL[1]; subR[27] ^= subR[1];
/* round 22 */
@@ -450,7 +438,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
subL[26] ^= kw4l; subR[26] ^= kw4r;
kw4l ^= kw4r & ~subR[24];
dw = kw4l & subL[24],
- kw4r ^= ROL1(dw); /* modified for FL(kl5) */
+ kw4r ^= rol32(dw, 1); /* modified for FL(kl5) */
}
/* round 17 */
subL[22] ^= kw4l; subR[22] ^= kw4r;
@@ -460,7 +448,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
subL[18] ^= kw4l; subR[18] ^= kw4r;
kw4l ^= kw4r & ~subR[16];
dw = kw4l & subL[16],
- kw4r ^= ROL1(dw); /* modified for FL(kl3) */
+ kw4r ^= rol32(dw, 1); /* modified for FL(kl3) */
/* round 11 */
subL[14] ^= kw4l; subR[14] ^= kw4r;
/* round 9 */
@@ -469,7 +457,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
subL[10] ^= kw4l; subR[10] ^= kw4r;
kw4l ^= kw4r & ~subR[8];
dw = kw4l & subL[8],
- kw4r ^= ROL1(dw); /* modified for FL(kl1) */
+ kw4r ^= rol32(dw, 1); /* modified for FL(kl1) */
/* round 5 */
subL[6] ^= kw4l; subR[6] ^= kw4r;
/* round 3 */
@@ -494,7 +482,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
SUBKEY_R(6) = subR[5] ^ subR[7];
tl = subL[10] ^ (subR[10] & ~subR[8]);
dw = tl & subL[8], /* FL(kl1) */
- tr = subR[10] ^ ROL1(dw);
+ tr = subR[10] ^ rol32(dw, 1);
SUBKEY_L(7) = subL[6] ^ tl; /* round 6 */
SUBKEY_R(7) = subR[6] ^ tr;
SUBKEY_L(8) = subL[8]; /* FL(kl1) */
@@ -503,7 +491,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
SUBKEY_R(9) = subR[9];
tl = subL[7] ^ (subR[7] & ~subR[9]);
dw = tl & subL[9], /* FLinv(kl2) */
- tr = subR[7] ^ ROL1(dw);
+ tr = subR[7] ^ rol32(dw, 1);
SUBKEY_L(10) = tl ^ subL[11]; /* round 7 */
SUBKEY_R(10) = tr ^ subR[11];
SUBKEY_L(11) = subL[10] ^ subL[12]; /* round 8 */
@@ -516,7 +504,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
SUBKEY_R(14) = subR[13] ^ subR[15];
tl = subL[18] ^ (subR[18] & ~subR[16]);
dw = tl & subL[16], /* FL(kl3) */
- tr = subR[18] ^ ROL1(dw);
+ tr = subR[18] ^ rol32(dw, 1);
SUBKEY_L(15) = subL[14] ^ tl; /* round 12 */
SUBKEY_R(15) = subR[14] ^ tr;
SUBKEY_L(16) = subL[16]; /* FL(kl3) */
@@ -525,7 +513,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
SUBKEY_R(17) = subR[17];
tl = subL[15] ^ (subR[15] & ~subR[17]);
dw = tl & subL[17], /* FLinv(kl4) */
- tr = subR[15] ^ ROL1(dw);
+ tr = subR[15] ^ rol32(dw, 1);
SUBKEY_L(18) = tl ^ subL[19]; /* round 13 */
SUBKEY_R(18) = tr ^ subR[19];
SUBKEY_L(19) = subL[18] ^ subL[20]; /* round 14 */
@@ -544,7 +532,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
} else {
tl = subL[26] ^ (subR[26] & ~subR[24]);
dw = tl & subL[24], /* FL(kl5) */
- tr = subR[26] ^ ROL1(dw);
+ tr = subR[26] ^ rol32(dw, 1);
SUBKEY_L(23) = subL[22] ^ tl; /* round 18 */
SUBKEY_R(23) = subR[22] ^ tr;
SUBKEY_L(24) = subL[24]; /* FL(kl5) */
@@ -553,7 +541,7 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
SUBKEY_R(25) = subR[25];
tl = subL[23] ^ (subR[23] & ~subR[25]);
dw = tl & subL[25], /* FLinv(kl6) */
- tr = subR[23] ^ ROL1(dw);
+ tr = subR[23] ^ rol32(dw, 1);
SUBKEY_L(26) = tl ^ subL[27]; /* round 19 */
SUBKEY_R(26) = tr ^ subR[27];
SUBKEY_L(27) = subL[26] ^ subL[28]; /* round 20 */
@@ -573,17 +561,17 @@ static void camellia_setup_tail(u32 *subkey, u32 *subL, u32 *subR, int max)
/* apply the inverse of the last half of P-function */
i = 2;
do {
- dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = ROL8(dw);/* round 1 */
+ dw = SUBKEY_L(i + 0) ^ SUBKEY_R(i + 0); dw = rol32(dw, 8);/* round 1 */
SUBKEY_R(i + 0) = SUBKEY_L(i + 0) ^ dw; SUBKEY_L(i + 0) = dw;
- dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = ROL8(dw);/* round 2 */
+ dw = SUBKEY_L(i + 1) ^ SUBKEY_R(i + 1); dw = rol32(dw, 8);/* round 2 */
SUBKEY_R(i + 1) = SUBKEY_L(i + 1) ^ dw; SUBKEY_L(i + 1) = dw;
- dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = ROL8(dw);/* round 3 */
+ dw = SUBKEY_L(i + 2) ^ SUBKEY_R(i + 2); dw = rol32(dw, 8);/* round 3 */
SUBKEY_R(i + 2) = SUBKEY_L(i + 2) ^ dw; SUBKEY_L(i + 2) = dw;
- dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = ROL8(dw);/* round 4 */
+ dw = SUBKEY_L(i + 3) ^ SUBKEY_R(i + 3); dw = rol32(dw, 8);/* round 4 */
SUBKEY_R(i + 3) = SUBKEY_L(i + 3) ^ dw; SUBKEY_L(i + 3) = dw;
- dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = ROL8(dw);/* round 5 */
+ dw = SUBKEY_L(i + 4) ^ SUBKEY_R(i + 4); dw = rol32(dw, 8);/* round 5 */
SUBKEY_R(i + 4) = SUBKEY_L(i + 4) ^ dw; SUBKEY_L(i + 4) = dw;
- dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = ROL8(dw);/* round 6 */
+ dw = SUBKEY_L(i + 5) ^ SUBKEY_R(i + 5); dw = rol32(dw, 8);/* round 6 */
SUBKEY_R(i + 5) = SUBKEY_L(i + 5) ^ dw; SUBKEY_L(i + 5) = dw;
i += 8;
} while (i < max);
@@ -599,10 +587,10 @@ static void camellia_setup128(const unsigned char *key, u32 *subkey)
/**
* k == kll || klr || krl || krr (|| is concatenation)
*/
- GETU32(kll, key );
- GETU32(klr, key + 4);
- GETU32(krl, key + 8);
- GETU32(krr, key + 12);
+ kll = get_unaligned_be32(key);
+ klr = get_unaligned_be32(key + 4);
+ krl = get_unaligned_be32(key + 8);
+ krr = get_unaligned_be32(key + 12);
/* generate KL dependent subkeys */
/* kw1 */
@@ -707,14 +695,14 @@ static void camellia_setup256(const unsigned char *key, u32 *subkey)
* key = (kll || klr || krl || krr || krll || krlr || krrl || krrr)
* (|| is concatenation)
*/
- GETU32(kll, key );
- GETU32(klr, key + 4);
- GETU32(krl, key + 8);
- GETU32(krr, key + 12);
- GETU32(krll, key + 16);
- GETU32(krlr, key + 20);
- GETU32(krrl, key + 24);
- GETU32(krrr, key + 28);
+ kll = get_unaligned_be32(key);
+ klr = get_unaligned_be32(key + 4);
+ krl = get_unaligned_be32(key + 8);
+ krr = get_unaligned_be32(key + 12);
+ krll = get_unaligned_be32(key + 16);
+ krlr = get_unaligned_be32(key + 20);
+ krrl = get_unaligned_be32(key + 24);
+ krrr = get_unaligned_be32(key + 28);
/* generate KL dependent subkeys */
/* kw1 */
@@ -870,13 +858,13 @@ static void camellia_setup192(const unsigned char *key, u32 *subkey)
t0 &= ll; \
t2 |= rr; \
rl ^= t2; \
- lr ^= ROL1(t0); \
+ lr ^= rol32(t0, 1); \
t3 = krl; \
t1 = klr; \
t3 &= rl; \
t1 |= lr; \
ll ^= t1; \
- rr ^= ROL1(t3); \
+ rr ^= rol32(t3, 1); \
} while(0)
#define CAMELLIA_ROUNDSM(xl, xr, kl, kr, yl, yr, il, ir) \
@@ -892,7 +880,7 @@ static void camellia_setup192(const unsigned char *key, u32 *subkey)
il ^= kl; \
ir ^= il ^ kr; \
yl ^= ir; \
- yr ^= ROR8(il) ^ ir; \
+ yr ^= ror32(il, 8) ^ ir; \
} while(0)
/* max = 24: 128bit encrypt, max = 32: 256bit encrypt */
diff --git a/crypto/crc32c.c b/crypto/crc32c.c
index a882d9e4e63e..973bc2cfab2e 100644
--- a/crypto/crc32c.c
+++ b/crypto/crc32c.c
@@ -3,8 +3,29 @@
*
* CRC32C chksum
*
- * This module file is a wrapper to invoke the lib/crc32c routines.
+ *@Article{castagnoli-crc,
+ * author = { Guy Castagnoli and Stefan Braeuer and Martin Herrman},
+ * title = {{Optimization of Cyclic Redundancy-Check Codes with 24
+ * and 32 Parity Bits}},
+ * journal = IEEE Transactions on Communication,
+ * year = {1993},
+ * volume = {41},
+ * number = {6},
+ * pages = {},
+ * month = {June},
+ *}
+ * Used by the iSCSI driver, possibly others, and derived from the
+ * the iscsi-crc.c module of the linux-iscsi driver at
+ * http://linux-iscsi.sourceforge.net.
*
+ * Following the example of lib/crc32, this function is intended to be
+ * flexible and useful for all users. Modules that currently have their
+ * own crc32c, but hopefully may be able to use this one are:
+ * net/sctp (please add all your doco to here if you change to
+ * use this one!)
+ * <endoflist>
+ *
+ * Copyright (c) 2004 Cisco Systems, Inc.
* Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,208 +39,217 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
-#include <linux/crc32c.h>
#include <linux/kernel.h>
#define CHKSUM_BLOCK_SIZE 1
#define CHKSUM_DIGEST_SIZE 4
struct chksum_ctx {
- u32 crc;
u32 key;
};
+struct chksum_desc_ctx {
+ u32 crc;
+};
+
/*
- * Steps through buffer one byte at at time, calculates reflected
- * crc using table.
+ * This is the CRC-32C table
+ * Generated with:
+ * width = 32 bits
+ * poly = 0x1EDC6F41
+ * reflect input bytes = true
+ * reflect output bytes = true
*/
-static void chksum_init(struct crypto_tfm *tfm)
-{
- struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
-
- mctx->crc = mctx->key;
-}
+static const u32 crc32c_table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
/*
- * Setting the seed allows arbitrary accumulators and flexible XOR policy
- * If your algorithm starts with ~0, then XOR with ~0 before you set
- * the seed.
+ * Steps through buffer one byte at at time, calculates reflected
+ * crc using table.
*/
-static int chksum_setkey(struct crypto_tfm *tfm, const u8 *key,
- unsigned int keylen)
-{
- struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
- if (keylen != sizeof(mctx->crc)) {
- tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
- return -EINVAL;
- }
- mctx->key = le32_to_cpu(*(__le32 *)key);
- return 0;
-}
-
-static void chksum_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int length)
+static u32 crc32c(u32 crc, const u8 *data, unsigned int length)
{
- struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
+ while (length--)
+ crc = crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
- mctx->crc = crc32c(mctx->crc, data, length);
+ return crc;
}
-static void chksum_final(struct crypto_tfm *tfm, u8 *out)
-{
- struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
-
- *(__le32 *)out = ~cpu_to_le32(mctx->crc);
-}
+/*
+ * Steps through buffer one byte at at time, calculates reflected
+ * crc using table.
+ */
-static int crc32c_cra_init_old(struct crypto_tfm *tfm)
+static int chksum_init(struct shash_desc *desc)
{
- struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
+ struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
+
+ ctx->crc = mctx->key;
- mctx->key = ~0;
return 0;
}
-static struct crypto_alg old_alg = {
- .cra_name = "crc32c",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct chksum_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(old_alg.cra_list),
- .cra_init = crc32c_cra_init_old,
- .cra_u = {
- .digest = {
- .dia_digestsize= CHKSUM_DIGEST_SIZE,
- .dia_setkey = chksum_setkey,
- .dia_init = chksum_init,
- .dia_update = chksum_update,
- .dia_final = chksum_final
- }
- }
-};
-
/*
* Setting the seed allows arbitrary accumulators and flexible XOR policy
* If your algorithm starts with ~0, then XOR with ~0 before you set
* the seed.
*/
-static int crc32c_setkey(struct crypto_ahash *hash, const u8 *key,
+static int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
unsigned int keylen)
{
- u32 *mctx = crypto_ahash_ctx(hash);
+ struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
- if (keylen != sizeof(u32)) {
- crypto_ahash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ if (keylen != sizeof(mctx->key)) {
+ crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
- *mctx = le32_to_cpup((__le32 *)key);
+ mctx->key = le32_to_cpu(*(__le32 *)key);
return 0;
}
-static int crc32c_init(struct ahash_request *req)
+static int chksum_update(struct shash_desc *desc, const u8 *data,
+ unsigned int length)
{
- u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
- u32 *crcp = ahash_request_ctx(req);
+ struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
- *crcp = *mctx;
+ ctx->crc = crc32c(ctx->crc, data, length);
return 0;
}
-static int crc32c_update(struct ahash_request *req)
+static int chksum_final(struct shash_desc *desc, u8 *out)
{
- struct crypto_hash_walk walk;
- u32 *crcp = ahash_request_ctx(req);
- u32 crc = *crcp;
- int nbytes;
-
- for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
- nbytes = crypto_hash_walk_done(&walk, 0))
- crc = crc32c(crc, walk.data, nbytes);
+ struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
- *crcp = crc;
+ *(__le32 *)out = ~cpu_to_le32p(&ctx->crc);
return 0;
}
-static int crc32c_final(struct ahash_request *req)
+static int __chksum_finup(u32 *crcp, const u8 *data, unsigned int len, u8 *out)
{
- u32 *crcp = ahash_request_ctx(req);
-
- *(__le32 *)req->result = ~cpu_to_le32p(crcp);
+ *(__le32 *)out = ~cpu_to_le32(crc32c(*crcp, data, len));
return 0;
}
-static int crc32c_digest(struct ahash_request *req)
+static int chksum_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
{
- struct crypto_hash_walk walk;
- u32 *mctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
- u32 crc = *mctx;
- int nbytes;
+ struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
- for (nbytes = crypto_hash_walk_first(req, &walk); nbytes;
- nbytes = crypto_hash_walk_done(&walk, 0))
- crc = crc32c(crc, walk.data, nbytes);
-
- *(__le32 *)req->result = ~cpu_to_le32(crc);
- return 0;
+ return __chksum_finup(&ctx->crc, data, len, out);
}
-static int crc32c_cra_init(struct crypto_tfm *tfm)
+static int chksum_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int length, u8 *out)
{
- u32 *key = crypto_tfm_ctx(tfm);
+ struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
- *key = ~0;
+ return __chksum_finup(&mctx->key, data, length, out);
+}
- tfm->crt_ahash.reqsize = sizeof(u32);
+static int crc32c_cra_init(struct crypto_tfm *tfm)
+{
+ struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
+ mctx->key = ~0;
return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "crc32c",
- .cra_driver_name = "crc32c-generic",
- .cra_priority = 100,
- .cra_flags = CRYPTO_ALG_TYPE_AHASH,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_ctxsize = sizeof(u32),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_init = crc32c_cra_init,
- .cra_type = &crypto_ahash_type,
- .cra_u = {
- .ahash = {
- .digestsize = CHKSUM_DIGEST_SIZE,
- .setkey = crc32c_setkey,
- .init = crc32c_init,
- .update = crc32c_update,
- .final = crc32c_final,
- .digest = crc32c_digest,
- }
+static struct shash_alg alg = {
+ .digestsize = CHKSUM_DIGEST_SIZE,
+ .setkey = chksum_setkey,
+ .init = chksum_init,
+ .update = chksum_update,
+ .final = chksum_final,
+ .finup = chksum_finup,
+ .digest = chksum_digest,
+ .descsize = sizeof(struct chksum_desc_ctx),
+ .base = {
+ .cra_name = "crc32c",
+ .cra_driver_name = "crc32c-generic",
+ .cra_priority = 100,
+ .cra_blocksize = CHKSUM_BLOCK_SIZE,
+ .cra_alignmask = 3,
+ .cra_ctxsize = sizeof(struct chksum_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_init = crc32c_cra_init,
}
};
static int __init crc32c_mod_init(void)
{
- int err;
-
- err = crypto_register_alg(&old_alg);
- if (err)
- return err;
-
- err = crypto_register_alg(&alg);
- if (err)
- crypto_unregister_alg(&old_alg);
-
- return err;
+ return crypto_register_shash(&alg);
}
static void __exit crc32c_mod_fini(void)
{
- crypto_unregister_alg(&alg);
- crypto_unregister_alg(&old_alg);
+ crypto_unregister_shash(&alg);
}
module_init(crc32c_mod_init);
diff --git a/crypto/crypto_null.c b/crypto/crypto_null.c
index 1f7d53013a22..cb71c9122bc0 100644
--- a/crypto/crypto_null.c
+++ b/crypto/crypto_null.c
@@ -17,6 +17,7 @@
*
*/
+#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -38,15 +39,31 @@ static int null_compress(struct crypto_tfm *tfm, const u8 *src,
return 0;
}
-static void null_init(struct crypto_tfm *tfm)
-{ }
+static int null_init(struct shash_desc *desc)
+{
+ return 0;
+}
-static void null_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int len)
-{ }
+static int null_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ return 0;
+}
-static void null_final(struct crypto_tfm *tfm, u8 *out)
-{ }
+static int null_final(struct shash_desc *desc, u8 *out)
+{
+ return 0;
+}
+
+static int null_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return 0;
+}
+
+static int null_hash_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{ return 0; }
static int null_setkey(struct crypto_tfm *tfm, const u8 *key,
unsigned int keylen)
@@ -89,19 +106,20 @@ static struct crypto_alg compress_null = {
.coa_decompress = null_compress } }
};
-static struct crypto_alg digest_null = {
- .cra_name = "digest_null",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = NULL_BLOCK_SIZE,
- .cra_ctxsize = 0,
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(digest_null.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = NULL_DIGEST_SIZE,
- .dia_setkey = null_setkey,
- .dia_init = null_init,
- .dia_update = null_update,
- .dia_final = null_final } }
+static struct shash_alg digest_null = {
+ .digestsize = NULL_DIGEST_SIZE,
+ .setkey = null_hash_setkey,
+ .init = null_init,
+ .update = null_update,
+ .finup = null_digest,
+ .digest = null_digest,
+ .final = null_final,
+ .base = {
+ .cra_name = "digest_null",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = NULL_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static struct crypto_alg cipher_null = {
@@ -154,7 +172,7 @@ static int __init crypto_null_mod_init(void)
if (ret < 0)
goto out_unregister_cipher;
- ret = crypto_register_alg(&digest_null);
+ ret = crypto_register_shash(&digest_null);
if (ret < 0)
goto out_unregister_skcipher;
@@ -166,7 +184,7 @@ out:
return ret;
out_unregister_digest:
- crypto_unregister_alg(&digest_null);
+ crypto_unregister_shash(&digest_null);
out_unregister_skcipher:
crypto_unregister_alg(&skcipher_null);
out_unregister_cipher:
@@ -177,7 +195,7 @@ out_unregister_cipher:
static void __exit crypto_null_mod_fini(void)
{
crypto_unregister_alg(&compress_null);
- crypto_unregister_alg(&digest_null);
+ crypto_unregister_shash(&digest_null);
crypto_unregister_alg(&skcipher_null);
crypto_unregister_alg(&cipher_null);
}
diff --git a/crypto/des_generic.c b/crypto/des_generic.c
index 5d0e4580f998..5bd3ee345a64 100644
--- a/crypto/des_generic.c
+++ b/crypto/des_generic.c
@@ -868,9 +868,10 @@ static int des3_ede_setkey(struct crypto_tfm *tfm, const u8 *key,
u32 *flags = &tfm->crt_flags;
if (unlikely(!((K[0] ^ K[2]) | (K[1] ^ K[3])) ||
- !((K[2] ^ K[4]) | (K[3] ^ K[5]))))
+ !((K[2] ^ K[4]) | (K[3] ^ K[5]))) &&
+ (*flags & CRYPTO_TFM_REQ_WEAK_KEY))
{
- *flags |= CRYPTO_TFM_RES_BAD_KEY_SCHED;
+ *flags |= CRYPTO_TFM_RES_WEAK_KEY;
return -EINVAL;
}
diff --git a/crypto/fcrypt.c b/crypto/fcrypt.c
index 1302f4cae337..b82d61f4e26c 100644
--- a/crypto/fcrypt.c
+++ b/crypto/fcrypt.c
@@ -73,7 +73,7 @@ do { \
* /afs/transarc.com/public/afsps/afs.rel31b.export-src/rxkad/sboxes.h
*/
#undef Z
-#define Z(x) __constant_cpu_to_be32(x << 3)
+#define Z(x) cpu_to_be32(x << 3)
static const __be32 sbox0[256] = {
Z(0xea), Z(0x7f), Z(0xb2), Z(0x64), Z(0x9d), Z(0xb0), Z(0xd9), Z(0x11),
Z(0xcd), Z(0x86), Z(0x86), Z(0x91), Z(0x0a), Z(0xb2), Z(0x93), Z(0x06),
@@ -110,7 +110,7 @@ static const __be32 sbox0[256] = {
};
#undef Z
-#define Z(x) __constant_cpu_to_be32((x << 27) | (x >> 5))
+#define Z(x) cpu_to_be32((x << 27) | (x >> 5))
static const __be32 sbox1[256] = {
Z(0x77), Z(0x14), Z(0xa6), Z(0xfe), Z(0xb2), Z(0x5e), Z(0x8c), Z(0x3e),
Z(0x67), Z(0x6c), Z(0xa1), Z(0x0d), Z(0xc2), Z(0xa2), Z(0xc1), Z(0x85),
@@ -147,7 +147,7 @@ static const __be32 sbox1[256] = {
};
#undef Z
-#define Z(x) __constant_cpu_to_be32(x << 11)
+#define Z(x) cpu_to_be32(x << 11)
static const __be32 sbox2[256] = {
Z(0xf0), Z(0x37), Z(0x24), Z(0x53), Z(0x2a), Z(0x03), Z(0x83), Z(0x86),
Z(0xd1), Z(0xec), Z(0x50), Z(0xf0), Z(0x42), Z(0x78), Z(0x2f), Z(0x6d),
@@ -184,7 +184,7 @@ static const __be32 sbox2[256] = {
};
#undef Z
-#define Z(x) __constant_cpu_to_be32(x << 19)
+#define Z(x) cpu_to_be32(x << 19)
static const __be32 sbox3[256] = {
Z(0xa9), Z(0x2a), Z(0x48), Z(0x51), Z(0x84), Z(0x7e), Z(0x49), Z(0xe2),
Z(0xb5), Z(0xb7), Z(0x42), Z(0x33), Z(0x7d), Z(0x5d), Z(0xa6), Z(0x12),
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 7ff2d6a8c7d0..0ad39c374963 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -16,7 +16,7 @@
*
*/
-#include <crypto/algapi.h>
+#include <crypto/internal/hash.h>
#include <crypto/scatterwalk.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -238,9 +238,11 @@ static struct crypto_instance *hmac_alloc(struct rtattr **tb)
return ERR_CAST(alg);
inst = ERR_PTR(-EINVAL);
- ds = (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
- CRYPTO_ALG_TYPE_HASH ? alg->cra_hash.digestsize :
- alg->cra_digest.dia_digestsize;
+ ds = alg->cra_type == &crypto_hash_type ?
+ alg->cra_hash.digestsize :
+ alg->cra_type ?
+ __crypto_shash_alg(alg)->digestsize :
+ alg->cra_digest.dia_digestsize;
if (ds > alg->cra_blocksize)
goto out_put_alg;
diff --git a/crypto/internal.h b/crypto/internal.h
index 8ef72d76092e..3c19a27a7563 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -109,6 +109,8 @@ void crypto_alg_tested(const char *name, int err);
void crypto_shoot_alg(struct crypto_alg *alg);
struct crypto_tfm *__crypto_alloc_tfm(struct crypto_alg *alg, u32 type,
u32 mask);
+struct crypto_tfm *crypto_create_tfm(struct crypto_alg *alg,
+ const struct crypto_type *frontend);
int crypto_register_instance(struct crypto_template *tmpl,
struct crypto_instance *inst);
diff --git a/crypto/md4.c b/crypto/md4.c
index 3c19aa0750fd..7fca1f59a4f5 100644
--- a/crypto/md4.c
+++ b/crypto/md4.c
@@ -20,8 +20,8 @@
* (at your option) any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
-#include <linux/crypto.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
@@ -58,7 +58,7 @@ static inline u32 H(u32 x, u32 y, u32 z)
{
return x ^ y ^ z;
}
-
+
#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (u32)0x5A827999,s))
#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (u32)0x6ED9EBA1,s))
@@ -148,24 +148,26 @@ static void md4_transform(u32 *hash, u32 const *in)
static inline void md4_transform_helper(struct md4_ctx *ctx)
{
- le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32));
+ le32_to_cpu_array(ctx->block, ARRAY_SIZE(ctx->block));
md4_transform(ctx->hash, ctx->block);
}
-static void md4_init(struct crypto_tfm *tfm)
+static int md4_init(struct shash_desc *desc)
{
- struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md4_ctx *mctx = shash_desc_ctx(desc);
mctx->hash[0] = 0x67452301;
mctx->hash[1] = 0xefcdab89;
mctx->hash[2] = 0x98badcfe;
mctx->hash[3] = 0x10325476;
mctx->byte_count = 0;
+
+ return 0;
}
-static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int md4_update(struct shash_desc *desc, const u8 *data, unsigned int len)
{
- struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md4_ctx *mctx = shash_desc_ctx(desc);
const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
mctx->byte_count += len;
@@ -173,7 +175,7 @@ static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
if (avail > len) {
memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
data, len);
- return;
+ return 0;
}
memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
@@ -191,11 +193,13 @@ static void md4_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
}
memcpy(mctx->block, data, len);
+
+ return 0;
}
-static void md4_final(struct crypto_tfm *tfm, u8 *out)
+static int md4_final(struct shash_desc *desc, u8 *out)
{
- struct md4_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md4_ctx *mctx = shash_desc_ctx(desc);
const unsigned int offset = mctx->byte_count & 0x3f;
char *p = (char *)mctx->block + offset;
int padding = 56 - (offset + 1);
@@ -214,33 +218,35 @@ static void md4_final(struct crypto_tfm *tfm, u8 *out)
le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
sizeof(u64)) / sizeof(u32));
md4_transform(mctx->hash, mctx->block);
- cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
+ cpu_to_le32_array(mctx->hash, ARRAY_SIZE(mctx->hash));
memcpy(out, mctx->hash, sizeof(mctx->hash));
memset(mctx, 0, sizeof(*mctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "md4",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = MD4_HMAC_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct md4_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = MD4_DIGEST_SIZE,
- .dia_init = md4_init,
- .dia_update = md4_update,
- .dia_final = md4_final } }
+static struct shash_alg alg = {
+ .digestsize = MD4_DIGEST_SIZE,
+ .init = md4_init,
+ .update = md4_update,
+ .final = md4_final,
+ .descsize = sizeof(struct md4_ctx),
+ .base = {
+ .cra_name = "md4",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = MD4_HMAC_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init md4_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit md4_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(md4_mod_init);
diff --git a/crypto/md5.c b/crypto/md5.c
index 39268f3d2f1d..83eb52961750 100644
--- a/crypto/md5.c
+++ b/crypto/md5.c
@@ -15,10 +15,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
-#include <linux/crypto.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -147,20 +147,22 @@ static inline void md5_transform_helper(struct md5_ctx *ctx)
md5_transform(ctx->hash, ctx->block);
}
-static void md5_init(struct crypto_tfm *tfm)
+static int md5_init(struct shash_desc *desc)
{
- struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md5_ctx *mctx = shash_desc_ctx(desc);
mctx->hash[0] = 0x67452301;
mctx->hash[1] = 0xefcdab89;
mctx->hash[2] = 0x98badcfe;
mctx->hash[3] = 0x10325476;
mctx->byte_count = 0;
+
+ return 0;
}
-static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int md5_update(struct shash_desc *desc, const u8 *data, unsigned int len)
{
- struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md5_ctx *mctx = shash_desc_ctx(desc);
const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
mctx->byte_count += len;
@@ -168,7 +170,7 @@ static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
if (avail > len) {
memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
data, len);
- return;
+ return 0;
}
memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
@@ -186,11 +188,13 @@ static void md5_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
}
memcpy(mctx->block, data, len);
+
+ return 0;
}
-static void md5_final(struct crypto_tfm *tfm, u8 *out)
+static int md5_final(struct shash_desc *desc, u8 *out)
{
- struct md5_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct md5_ctx *mctx = shash_desc_ctx(desc);
const unsigned int offset = mctx->byte_count & 0x3f;
char *p = (char *)mctx->block + offset;
int padding = 56 - (offset + 1);
@@ -212,30 +216,32 @@ static void md5_final(struct crypto_tfm *tfm, u8 *out)
cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32));
memcpy(out, mctx->hash, sizeof(mctx->hash));
memset(mctx, 0, sizeof(*mctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "md5",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct md5_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = MD5_DIGEST_SIZE,
- .dia_init = md5_init,
- .dia_update = md5_update,
- .dia_final = md5_final } }
+static struct shash_alg alg = {
+ .digestsize = MD5_DIGEST_SIZE,
+ .init = md5_init,
+ .update = md5_update,
+ .final = md5_final,
+ .descsize = sizeof(struct md5_ctx),
+ .base = {
+ .cra_name = "md5",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init md5_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit md5_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(md5_mod_init);
diff --git a/crypto/michael_mic.c b/crypto/michael_mic.c
index 9e917b8011b1..079b761bc70d 100644
--- a/crypto/michael_mic.c
+++ b/crypto/michael_mic.c
@@ -9,23 +9,25 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
+#include <crypto/internal/hash.h>
#include <asm/byteorder.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
-#include <linux/crypto.h>
#include <linux/types.h>
struct michael_mic_ctx {
+ u32 l, r;
+};
+
+struct michael_mic_desc_ctx {
u8 pending[4];
size_t pending_len;
u32 l, r;
};
-
static inline u32 xswap(u32 val)
{
return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
@@ -45,17 +47,22 @@ do { \
} while (0)
-static void michael_init(struct crypto_tfm *tfm)
+static int michael_init(struct shash_desc *desc)
{
- struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
+ struct michael_mic_ctx *ctx = crypto_shash_ctx(desc->tfm);
mctx->pending_len = 0;
+ mctx->l = ctx->l;
+ mctx->r = ctx->r;
+
+ return 0;
}
-static void michael_update(struct crypto_tfm *tfm, const u8 *data,
+static int michael_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
- struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
const __le32 *src;
if (mctx->pending_len) {
@@ -68,7 +75,7 @@ static void michael_update(struct crypto_tfm *tfm, const u8 *data,
len -= flen;
if (mctx->pending_len < 4)
- return;
+ return 0;
src = (const __le32 *)mctx->pending;
mctx->l ^= le32_to_cpup(src);
@@ -88,12 +95,14 @@ static void michael_update(struct crypto_tfm *tfm, const u8 *data,
mctx->pending_len = len;
memcpy(mctx->pending, src, len);
}
+
+ return 0;
}
-static void michael_final(struct crypto_tfm *tfm, u8 *out)
+static int michael_final(struct shash_desc *desc, u8 *out)
{
- struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct michael_mic_desc_ctx *mctx = shash_desc_ctx(desc);
u8 *data = mctx->pending;
__le32 *dst = (__le32 *)out;
@@ -119,17 +128,20 @@ static void michael_final(struct crypto_tfm *tfm, u8 *out)
dst[0] = cpu_to_le32(mctx->l);
dst[1] = cpu_to_le32(mctx->r);
+
+ return 0;
}
-static int michael_setkey(struct crypto_tfm *tfm, const u8 *key,
+static int michael_setkey(struct crypto_shash *tfm, const u8 *key,
unsigned int keylen)
{
- struct michael_mic_ctx *mctx = crypto_tfm_ctx(tfm);
+ struct michael_mic_ctx *mctx = crypto_shash_ctx(tfm);
+
const __le32 *data = (const __le32 *)key;
if (keylen != 8) {
- tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
@@ -138,33 +150,31 @@ static int michael_setkey(struct crypto_tfm *tfm, const u8 *key,
return 0;
}
-
-static struct crypto_alg michael_mic_alg = {
- .cra_name = "michael_mic",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = 8,
- .cra_ctxsize = sizeof(struct michael_mic_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(michael_mic_alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = 8,
- .dia_init = michael_init,
- .dia_update = michael_update,
- .dia_final = michael_final,
- .dia_setkey = michael_setkey } }
+static struct shash_alg alg = {
+ .digestsize = 8,
+ .setkey = michael_setkey,
+ .init = michael_init,
+ .update = michael_update,
+ .final = michael_final,
+ .descsize = sizeof(struct michael_mic_desc_ctx),
+ .base = {
+ .cra_name = "michael_mic",
+ .cra_blocksize = 8,
+ .cra_alignmask = 3,
+ .cra_ctxsize = sizeof(struct michael_mic_ctx),
+ .cra_module = THIS_MODULE,
+ }
};
-
static int __init michael_mic_init(void)
{
- return crypto_register_alg(&michael_mic_alg);
+ return crypto_register_shash(&alg);
}
static void __exit michael_mic_exit(void)
{
- crypto_unregister_alg(&michael_mic_alg);
+ crypto_unregister_shash(&alg);
}
diff --git a/crypto/proc.c b/crypto/proc.c
index 37a13d05636d..5dc07e442fca 100644
--- a/crypto/proc.c
+++ b/crypto/proc.c
@@ -94,6 +94,17 @@ static int c_show(struct seq_file *m, void *p)
seq_printf(m, "selftest : %s\n",
(alg->cra_flags & CRYPTO_ALG_TESTED) ?
"passed" : "unknown");
+
+ if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
+ seq_printf(m, "type : larval\n");
+ seq_printf(m, "flags : 0x%x\n", alg->cra_flags);
+ goto out;
+ }
+
+ if (alg->cra_type && alg->cra_type->show) {
+ alg->cra_type->show(m, alg);
+ goto out;
+ }
switch (alg->cra_flags & (CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_LARVAL)) {
case CRYPTO_ALG_TYPE_CIPHER:
@@ -115,16 +126,11 @@ static int c_show(struct seq_file *m, void *p)
seq_printf(m, "type : compression\n");
break;
default:
- if (alg->cra_flags & CRYPTO_ALG_LARVAL) {
- seq_printf(m, "type : larval\n");
- seq_printf(m, "flags : 0x%x\n", alg->cra_flags);
- } else if (alg->cra_type && alg->cra_type->show)
- alg->cra_type->show(m, alg);
- else
- seq_printf(m, "type : unknown\n");
+ seq_printf(m, "type : unknown\n");
break;
}
+out:
seq_putc(m, '\n');
return 0;
}
diff --git a/crypto/rmd128.c b/crypto/rmd128.c
index 5de6fa2a76fb..1ceb6735aa53 100644
--- a/crypto/rmd128.c
+++ b/crypto/rmd128.c
@@ -13,11 +13,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -218,9 +217,9 @@ static void rmd128_transform(u32 *state, const __le32 *in)
return;
}
-static void rmd128_init(struct crypto_tfm *tfm)
+static int rmd128_init(struct shash_desc *desc)
{
- struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd128_ctx *rctx = shash_desc_ctx(desc);
rctx->byte_count = 0;
@@ -230,12 +229,14 @@ static void rmd128_init(struct crypto_tfm *tfm)
rctx->state[3] = RMD_H3;
memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+ return 0;
}
-static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int len)
+static int rmd128_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
{
- struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd128_ctx *rctx = shash_desc_ctx(desc);
const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
rctx->byte_count += len;
@@ -244,7 +245,7 @@ static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
if (avail > len) {
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
data, len);
- return;
+ goto out;
}
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -262,12 +263,15 @@ static void rmd128_update(struct crypto_tfm *tfm, const u8 *data,
}
memcpy(rctx->buffer, data, len);
+
+out:
+ return 0;
}
/* Add padding and return the message digest. */
-static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd128_final(struct shash_desc *desc, u8 *out)
{
- struct rmd128_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd128_ctx *rctx = shash_desc_ctx(desc);
u32 i, index, padlen;
__le64 bits;
__le32 *dst = (__le32 *)out;
@@ -278,10 +282,10 @@ static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64 */
index = rctx->byte_count & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- rmd128_update(tfm, padding, padlen);
+ rmd128_update(desc, padding, padlen);
/* Append length */
- rmd128_update(tfm, (const u8 *)&bits, sizeof(bits));
+ rmd128_update(desc, (const u8 *)&bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 4; i++)
@@ -289,31 +293,32 @@ static void rmd128_final(struct crypto_tfm *tfm, u8 *out)
/* Wipe context */
memset(rctx, 0, sizeof(*rctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "rmd128",
- .cra_driver_name = "rmd128",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = RMD128_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct rmd128_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = RMD128_DIGEST_SIZE,
- .dia_init = rmd128_init,
- .dia_update = rmd128_update,
- .dia_final = rmd128_final } }
+static struct shash_alg alg = {
+ .digestsize = RMD128_DIGEST_SIZE,
+ .init = rmd128_init,
+ .update = rmd128_update,
+ .final = rmd128_final,
+ .descsize = sizeof(struct rmd128_ctx),
+ .base = {
+ .cra_name = "rmd128",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = RMD128_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init rmd128_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit rmd128_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(rmd128_mod_init);
@@ -321,5 +326,3 @@ module_exit(rmd128_mod_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RIPEMD-128 Message Digest");
-
-MODULE_ALIAS("rmd128");
diff --git a/crypto/rmd160.c b/crypto/rmd160.c
index f001ec775e1f..472261fc913f 100644
--- a/crypto/rmd160.c
+++ b/crypto/rmd160.c
@@ -13,11 +13,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -261,9 +260,9 @@ static void rmd160_transform(u32 *state, const __le32 *in)
return;
}
-static void rmd160_init(struct crypto_tfm *tfm)
+static int rmd160_init(struct shash_desc *desc)
{
- struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd160_ctx *rctx = shash_desc_ctx(desc);
rctx->byte_count = 0;
@@ -274,12 +273,14 @@ static void rmd160_init(struct crypto_tfm *tfm)
rctx->state[4] = RMD_H4;
memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+ return 0;
}
-static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int len)
+static int rmd160_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
{
- struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd160_ctx *rctx = shash_desc_ctx(desc);
const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
rctx->byte_count += len;
@@ -288,7 +289,7 @@ static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
if (avail > len) {
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
data, len);
- return;
+ goto out;
}
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -306,12 +307,15 @@ static void rmd160_update(struct crypto_tfm *tfm, const u8 *data,
}
memcpy(rctx->buffer, data, len);
+
+out:
+ return 0;
}
/* Add padding and return the message digest. */
-static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd160_final(struct shash_desc *desc, u8 *out)
{
- struct rmd160_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd160_ctx *rctx = shash_desc_ctx(desc);
u32 i, index, padlen;
__le64 bits;
__le32 *dst = (__le32 *)out;
@@ -322,10 +326,10 @@ static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64 */
index = rctx->byte_count & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- rmd160_update(tfm, padding, padlen);
+ rmd160_update(desc, padding, padlen);
/* Append length */
- rmd160_update(tfm, (const u8 *)&bits, sizeof(bits));
+ rmd160_update(desc, (const u8 *)&bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 5; i++)
@@ -333,31 +337,32 @@ static void rmd160_final(struct crypto_tfm *tfm, u8 *out)
/* Wipe context */
memset(rctx, 0, sizeof(*rctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "rmd160",
- .cra_driver_name = "rmd160",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = RMD160_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct rmd160_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = RMD160_DIGEST_SIZE,
- .dia_init = rmd160_init,
- .dia_update = rmd160_update,
- .dia_final = rmd160_final } }
+static struct shash_alg alg = {
+ .digestsize = RMD160_DIGEST_SIZE,
+ .init = rmd160_init,
+ .update = rmd160_update,
+ .final = rmd160_final,
+ .descsize = sizeof(struct rmd160_ctx),
+ .base = {
+ .cra_name = "rmd160",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = RMD160_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init rmd160_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit rmd160_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(rmd160_mod_init);
@@ -365,5 +370,3 @@ module_exit(rmd160_mod_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RIPEMD-160 Message Digest");
-
-MODULE_ALIAS("rmd160");
diff --git a/crypto/rmd256.c b/crypto/rmd256.c
index e3de5b4cb47f..72eafa8d2e7b 100644
--- a/crypto/rmd256.c
+++ b/crypto/rmd256.c
@@ -13,11 +13,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -233,9 +232,9 @@ static void rmd256_transform(u32 *state, const __le32 *in)
return;
}
-static void rmd256_init(struct crypto_tfm *tfm)
+static int rmd256_init(struct shash_desc *desc)
{
- struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd256_ctx *rctx = shash_desc_ctx(desc);
rctx->byte_count = 0;
@@ -249,12 +248,14 @@ static void rmd256_init(struct crypto_tfm *tfm)
rctx->state[7] = RMD_H8;
memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+ return 0;
}
-static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int len)
+static int rmd256_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
{
- struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd256_ctx *rctx = shash_desc_ctx(desc);
const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
rctx->byte_count += len;
@@ -263,7 +264,7 @@ static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
if (avail > len) {
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
data, len);
- return;
+ goto out;
}
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -281,12 +282,15 @@ static void rmd256_update(struct crypto_tfm *tfm, const u8 *data,
}
memcpy(rctx->buffer, data, len);
+
+out:
+ return 0;
}
/* Add padding and return the message digest. */
-static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd256_final(struct shash_desc *desc, u8 *out)
{
- struct rmd256_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd256_ctx *rctx = shash_desc_ctx(desc);
u32 i, index, padlen;
__le64 bits;
__le32 *dst = (__le32 *)out;
@@ -297,10 +301,10 @@ static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64 */
index = rctx->byte_count & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- rmd256_update(tfm, padding, padlen);
+ rmd256_update(desc, padding, padlen);
/* Append length */
- rmd256_update(tfm, (const u8 *)&bits, sizeof(bits));
+ rmd256_update(desc, (const u8 *)&bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 8; i++)
@@ -308,31 +312,32 @@ static void rmd256_final(struct crypto_tfm *tfm, u8 *out)
/* Wipe context */
memset(rctx, 0, sizeof(*rctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "rmd256",
- .cra_driver_name = "rmd256",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = RMD256_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct rmd256_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = RMD256_DIGEST_SIZE,
- .dia_init = rmd256_init,
- .dia_update = rmd256_update,
- .dia_final = rmd256_final } }
+static struct shash_alg alg = {
+ .digestsize = RMD256_DIGEST_SIZE,
+ .init = rmd256_init,
+ .update = rmd256_update,
+ .final = rmd256_final,
+ .descsize = sizeof(struct rmd256_ctx),
+ .base = {
+ .cra_name = "rmd256",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = RMD256_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init rmd256_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit rmd256_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(rmd256_mod_init);
@@ -340,5 +345,3 @@ module_exit(rmd256_mod_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RIPEMD-256 Message Digest");
-
-MODULE_ALIAS("rmd256");
diff --git a/crypto/rmd320.c b/crypto/rmd320.c
index b143d66e42c8..86becaba2f05 100644
--- a/crypto/rmd320.c
+++ b/crypto/rmd320.c
@@ -13,11 +13,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
-#include <linux/cryptohash.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -280,9 +279,9 @@ static void rmd320_transform(u32 *state, const __le32 *in)
return;
}
-static void rmd320_init(struct crypto_tfm *tfm)
+static int rmd320_init(struct shash_desc *desc)
{
- struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd320_ctx *rctx = shash_desc_ctx(desc);
rctx->byte_count = 0;
@@ -298,12 +297,14 @@ static void rmd320_init(struct crypto_tfm *tfm)
rctx->state[9] = RMD_H9;
memset(rctx->buffer, 0, sizeof(rctx->buffer));
+
+ return 0;
}
-static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
- unsigned int len)
+static int rmd320_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
{
- struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd320_ctx *rctx = shash_desc_ctx(desc);
const u32 avail = sizeof(rctx->buffer) - (rctx->byte_count & 0x3f);
rctx->byte_count += len;
@@ -312,7 +313,7 @@ static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
if (avail > len) {
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
data, len);
- return;
+ goto out;
}
memcpy((char *)rctx->buffer + (sizeof(rctx->buffer) - avail),
@@ -330,12 +331,15 @@ static void rmd320_update(struct crypto_tfm *tfm, const u8 *data,
}
memcpy(rctx->buffer, data, len);
+
+out:
+ return 0;
}
/* Add padding and return the message digest. */
-static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
+static int rmd320_final(struct shash_desc *desc, u8 *out)
{
- struct rmd320_ctx *rctx = crypto_tfm_ctx(tfm);
+ struct rmd320_ctx *rctx = shash_desc_ctx(desc);
u32 i, index, padlen;
__le64 bits;
__le32 *dst = (__le32 *)out;
@@ -346,10 +350,10 @@ static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64 */
index = rctx->byte_count & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- rmd320_update(tfm, padding, padlen);
+ rmd320_update(desc, padding, padlen);
/* Append length */
- rmd320_update(tfm, (const u8 *)&bits, sizeof(bits));
+ rmd320_update(desc, (const u8 *)&bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 10; i++)
@@ -357,31 +361,32 @@ static void rmd320_final(struct crypto_tfm *tfm, u8 *out)
/* Wipe context */
memset(rctx, 0, sizeof(*rctx));
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "rmd320",
- .cra_driver_name = "rmd320",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = RMD320_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct rmd320_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = RMD320_DIGEST_SIZE,
- .dia_init = rmd320_init,
- .dia_update = rmd320_update,
- .dia_final = rmd320_final } }
+static struct shash_alg alg = {
+ .digestsize = RMD320_DIGEST_SIZE,
+ .init = rmd320_init,
+ .update = rmd320_update,
+ .final = rmd320_final,
+ .descsize = sizeof(struct rmd320_ctx),
+ .base = {
+ .cra_name = "rmd320",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = RMD320_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init rmd320_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit rmd320_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(rmd320_mod_init);
@@ -389,5 +394,3 @@ module_exit(rmd320_mod_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RIPEMD-320 Message Digest");
-
-MODULE_ALIAS("rmd320");
diff --git a/crypto/salsa20_generic.c b/crypto/salsa20_generic.c
index b07d55981741..eac10c11685c 100644
--- a/crypto/salsa20_generic.c
+++ b/crypto/salsa20_generic.c
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/crypto.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include <crypto/algapi.h>
#include <asm/byteorder.h>
@@ -42,10 +43,6 @@ D. J. Bernstein
Public domain.
*/
-#define ROTATE(v,n) (((v) << (n)) | ((v) >> (32 - (n))))
-#define XOR(v,w) ((v) ^ (w))
-#define PLUS(v,w) (((v) + (w)))
-#define PLUSONE(v) (PLUS((v),1))
#define U32TO8_LITTLE(p, v) \
{ (p)[0] = (v >> 0) & 0xff; (p)[1] = (v >> 8) & 0xff; \
(p)[2] = (v >> 16) & 0xff; (p)[3] = (v >> 24) & 0xff; }
@@ -65,41 +62,41 @@ static void salsa20_wordtobyte(u8 output[64], const u32 input[16])
memcpy(x, input, sizeof(x));
for (i = 20; i > 0; i -= 2) {
- x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 0],x[12]), 7));
- x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[ 4],x[ 0]), 9));
- x[12] = XOR(x[12],ROTATE(PLUS(x[ 8],x[ 4]),13));
- x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[12],x[ 8]),18));
- x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 5],x[ 1]), 7));
- x[13] = XOR(x[13],ROTATE(PLUS(x[ 9],x[ 5]), 9));
- x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[13],x[ 9]),13));
- x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 1],x[13]),18));
- x[14] = XOR(x[14],ROTATE(PLUS(x[10],x[ 6]), 7));
- x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[14],x[10]), 9));
- x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 2],x[14]),13));
- x[10] = XOR(x[10],ROTATE(PLUS(x[ 6],x[ 2]),18));
- x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[15],x[11]), 7));
- x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 3],x[15]), 9));
- x[11] = XOR(x[11],ROTATE(PLUS(x[ 7],x[ 3]),13));
- x[15] = XOR(x[15],ROTATE(PLUS(x[11],x[ 7]),18));
- x[ 1] = XOR(x[ 1],ROTATE(PLUS(x[ 0],x[ 3]), 7));
- x[ 2] = XOR(x[ 2],ROTATE(PLUS(x[ 1],x[ 0]), 9));
- x[ 3] = XOR(x[ 3],ROTATE(PLUS(x[ 2],x[ 1]),13));
- x[ 0] = XOR(x[ 0],ROTATE(PLUS(x[ 3],x[ 2]),18));
- x[ 6] = XOR(x[ 6],ROTATE(PLUS(x[ 5],x[ 4]), 7));
- x[ 7] = XOR(x[ 7],ROTATE(PLUS(x[ 6],x[ 5]), 9));
- x[ 4] = XOR(x[ 4],ROTATE(PLUS(x[ 7],x[ 6]),13));
- x[ 5] = XOR(x[ 5],ROTATE(PLUS(x[ 4],x[ 7]),18));
- x[11] = XOR(x[11],ROTATE(PLUS(x[10],x[ 9]), 7));
- x[ 8] = XOR(x[ 8],ROTATE(PLUS(x[11],x[10]), 9));
- x[ 9] = XOR(x[ 9],ROTATE(PLUS(x[ 8],x[11]),13));
- x[10] = XOR(x[10],ROTATE(PLUS(x[ 9],x[ 8]),18));
- x[12] = XOR(x[12],ROTATE(PLUS(x[15],x[14]), 7));
- x[13] = XOR(x[13],ROTATE(PLUS(x[12],x[15]), 9));
- x[14] = XOR(x[14],ROTATE(PLUS(x[13],x[12]),13));
- x[15] = XOR(x[15],ROTATE(PLUS(x[14],x[13]),18));
+ x[ 4] ^= rol32((x[ 0] + x[12]), 7);
+ x[ 8] ^= rol32((x[ 4] + x[ 0]), 9);
+ x[12] ^= rol32((x[ 8] + x[ 4]), 13);
+ x[ 0] ^= rol32((x[12] + x[ 8]), 18);
+ x[ 9] ^= rol32((x[ 5] + x[ 1]), 7);
+ x[13] ^= rol32((x[ 9] + x[ 5]), 9);
+ x[ 1] ^= rol32((x[13] + x[ 9]), 13);
+ x[ 5] ^= rol32((x[ 1] + x[13]), 18);
+ x[14] ^= rol32((x[10] + x[ 6]), 7);
+ x[ 2] ^= rol32((x[14] + x[10]), 9);
+ x[ 6] ^= rol32((x[ 2] + x[14]), 13);
+ x[10] ^= rol32((x[ 6] + x[ 2]), 18);
+ x[ 3] ^= rol32((x[15] + x[11]), 7);
+ x[ 7] ^= rol32((x[ 3] + x[15]), 9);
+ x[11] ^= rol32((x[ 7] + x[ 3]), 13);
+ x[15] ^= rol32((x[11] + x[ 7]), 18);
+ x[ 1] ^= rol32((x[ 0] + x[ 3]), 7);
+ x[ 2] ^= rol32((x[ 1] + x[ 0]), 9);
+ x[ 3] ^= rol32((x[ 2] + x[ 1]), 13);
+ x[ 0] ^= rol32((x[ 3] + x[ 2]), 18);
+ x[ 6] ^= rol32((x[ 5] + x[ 4]), 7);
+ x[ 7] ^= rol32((x[ 6] + x[ 5]), 9);
+ x[ 4] ^= rol32((x[ 7] + x[ 6]), 13);
+ x[ 5] ^= rol32((x[ 4] + x[ 7]), 18);
+ x[11] ^= rol32((x[10] + x[ 9]), 7);
+ x[ 8] ^= rol32((x[11] + x[10]), 9);
+ x[ 9] ^= rol32((x[ 8] + x[11]), 13);
+ x[10] ^= rol32((x[ 9] + x[ 8]), 18);
+ x[12] ^= rol32((x[15] + x[14]), 7);
+ x[13] ^= rol32((x[12] + x[15]), 9);
+ x[14] ^= rol32((x[13] + x[12]), 13);
+ x[15] ^= rol32((x[14] + x[13]), 18);
}
for (i = 0; i < 16; ++i)
- x[i] = PLUS(x[i],input[i]);
+ x[i] += input[i];
for (i = 0; i < 16; ++i)
U32TO8_LITTLE(output + 4 * i,x[i]);
}
@@ -150,9 +147,9 @@ static void salsa20_encrypt_bytes(struct salsa20_ctx *ctx, u8 *dst,
while (bytes) {
salsa20_wordtobyte(buf, ctx->input);
- ctx->input[8] = PLUSONE(ctx->input[8]);
+ ctx->input[8]++;
if (!ctx->input[8])
- ctx->input[9] = PLUSONE(ctx->input[9]);
+ ctx->input[9]++;
if (bytes <= 64) {
crypto_xor(dst, buf, bytes);
diff --git a/crypto/sha1_generic.c b/crypto/sha1_generic.c
index c7c6899e1fca..9efef20454cb 100644
--- a/crypto/sha1_generic.c
+++ b/crypto/sha1_generic.c
@@ -16,10 +16,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
#include <linux/cryptohash.h>
#include <linux/types.h>
#include <crypto/sha.h>
@@ -31,9 +31,10 @@ struct sha1_ctx {
u8 buffer[64];
};
-static void sha1_init(struct crypto_tfm *tfm)
+static int sha1_init(struct shash_desc *desc)
{
- struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha1_ctx *sctx = shash_desc_ctx(desc);
+
static const struct sha1_ctx initstate = {
0,
{ SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 },
@@ -41,12 +42,14 @@ static void sha1_init(struct crypto_tfm *tfm)
};
*sctx = initstate;
+
+ return 0;
}
-static void sha1_update(struct crypto_tfm *tfm, const u8 *data,
+static int sha1_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
- struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha1_ctx *sctx = shash_desc_ctx(desc);
unsigned int partial, done;
const u8 *src;
@@ -74,13 +77,15 @@ static void sha1_update(struct crypto_tfm *tfm, const u8 *data,
partial = 0;
}
memcpy(sctx->buffer + partial, src, len - done);
+
+ return 0;
}
/* Add padding and return the message digest. */
-static void sha1_final(struct crypto_tfm *tfm, u8 *out)
+static int sha1_final(struct shash_desc *desc, u8 *out)
{
- struct sha1_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha1_ctx *sctx = shash_desc_ctx(desc);
__be32 *dst = (__be32 *)out;
u32 i, index, padlen;
__be64 bits;
@@ -91,10 +96,10 @@ static void sha1_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64 */
index = sctx->count & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64+56) - index);
- sha1_update(tfm, padding, padlen);
+ sha1_update(desc, padding, padlen);
/* Append length */
- sha1_update(tfm, (const u8 *)&bits, sizeof(bits));
+ sha1_update(desc, (const u8 *)&bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 5; i++)
@@ -102,32 +107,33 @@ static void sha1_final(struct crypto_tfm *tfm, u8 *out)
/* Wipe context */
memset(sctx, 0, sizeof *sctx);
+
+ return 0;
}
-static struct crypto_alg alg = {
- .cra_name = "sha1",
- .cra_driver_name= "sha1-generic",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA1_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct sha1_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(alg.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA1_DIGEST_SIZE,
- .dia_init = sha1_init,
- .dia_update = sha1_update,
- .dia_final = sha1_final } }
+static struct shash_alg alg = {
+ .digestsize = SHA1_DIGEST_SIZE,
+ .init = sha1_init,
+ .update = sha1_update,
+ .final = sha1_final,
+ .descsize = sizeof(struct sha1_ctx),
+ .base = {
+ .cra_name = "sha1",
+ .cra_driver_name= "sha1-generic",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init sha1_generic_mod_init(void)
{
- return crypto_register_alg(&alg);
+ return crypto_register_shash(&alg);
}
static void __exit sha1_generic_mod_fini(void)
{
- crypto_unregister_alg(&alg);
+ crypto_unregister_shash(&alg);
}
module_init(sha1_generic_mod_init);
diff --git a/crypto/sha256_generic.c b/crypto/sha256_generic.c
index 5a8dd47558e5..caa3542e6ce8 100644
--- a/crypto/sha256_generic.c
+++ b/crypto/sha256_generic.c
@@ -17,10 +17,10 @@
* any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crypto.h>
#include <linux/types.h>
#include <crypto/sha.h>
#include <asm/byteorder.h>
@@ -69,7 +69,7 @@ static void sha256_transform(u32 *state, const u8 *input)
/* now blend */
for (i = 16; i < 64; i++)
BLEND_OP(i, W);
-
+
/* load the state into our registers */
a=state[0]; b=state[1]; c=state[2]; d=state[3];
e=state[4]; f=state[5]; g=state[6]; h=state[7];
@@ -220,9 +220,9 @@ static void sha256_transform(u32 *state, const u8 *input)
}
-static void sha224_init(struct crypto_tfm *tfm)
+static int sha224_init(struct shash_desc *desc)
{
- struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha256_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA224_H0;
sctx->state[1] = SHA224_H1;
sctx->state[2] = SHA224_H2;
@@ -233,11 +233,13 @@ static void sha224_init(struct crypto_tfm *tfm)
sctx->state[7] = SHA224_H7;
sctx->count[0] = 0;
sctx->count[1] = 0;
+
+ return 0;
}
-static void sha256_init(struct crypto_tfm *tfm)
+static int sha256_init(struct shash_desc *desc)
{
- struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha256_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA256_H0;
sctx->state[1] = SHA256_H1;
sctx->state[2] = SHA256_H2;
@@ -247,12 +249,14 @@ static void sha256_init(struct crypto_tfm *tfm)
sctx->state[6] = SHA256_H6;
sctx->state[7] = SHA256_H7;
sctx->count[0] = sctx->count[1] = 0;
+
+ return 0;
}
-static void sha256_update(struct crypto_tfm *tfm, const u8 *data,
+static int sha256_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
- struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha256_ctx *sctx = shash_desc_ctx(desc);
unsigned int i, index, part_len;
/* Compute number of bytes mod 128 */
@@ -277,14 +281,16 @@ static void sha256_update(struct crypto_tfm *tfm, const u8 *data,
} else {
i = 0;
}
-
+
/* Buffer remaining input */
memcpy(&sctx->buf[index], &data[i], len-i);
+
+ return 0;
}
-static void sha256_final(struct crypto_tfm *tfm, u8 *out)
+static int sha256_final(struct shash_desc *desc, u8 *out)
{
- struct sha256_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha256_ctx *sctx = shash_desc_ctx(desc);
__be32 *dst = (__be32 *)out;
__be32 bits[2];
unsigned int index, pad_len;
@@ -298,10 +304,10 @@ static void sha256_final(struct crypto_tfm *tfm, u8 *out)
/* Pad out to 56 mod 64. */
index = (sctx->count[0] >> 3) & 0x3f;
pad_len = (index < 56) ? (56 - index) : ((64+56) - index);
- sha256_update(tfm, padding, pad_len);
+ sha256_update(desc, padding, pad_len);
/* Append length (before padding) */
- sha256_update(tfm, (const u8 *)bits, sizeof(bits));
+ sha256_update(desc, (const u8 *)bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 8; i++)
@@ -309,71 +315,73 @@ static void sha256_final(struct crypto_tfm *tfm, u8 *out)
/* Zeroize sensitive information. */
memset(sctx, 0, sizeof(*sctx));
+
+ return 0;
}
-static void sha224_final(struct crypto_tfm *tfm, u8 *hash)
+static int sha224_final(struct shash_desc *desc, u8 *hash)
{
u8 D[SHA256_DIGEST_SIZE];
- sha256_final(tfm, D);
+ sha256_final(desc, D);
memcpy(hash, D, SHA224_DIGEST_SIZE);
memset(D, 0, SHA256_DIGEST_SIZE);
+
+ return 0;
}
-static struct crypto_alg sha256 = {
- .cra_name = "sha256",
- .cra_driver_name= "sha256-generic",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA256_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct sha256_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(sha256.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA256_DIGEST_SIZE,
- .dia_init = sha256_init,
- .dia_update = sha256_update,
- .dia_final = sha256_final } }
+static struct shash_alg sha256 = {
+ .digestsize = SHA256_DIGEST_SIZE,
+ .init = sha256_init,
+ .update = sha256_update,
+ .final = sha256_final,
+ .descsize = sizeof(struct sha256_ctx),
+ .base = {
+ .cra_name = "sha256",
+ .cra_driver_name= "sha256-generic",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg sha224 = {
- .cra_name = "sha224",
- .cra_driver_name = "sha224-generic",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA224_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct sha256_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(sha224.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA224_DIGEST_SIZE,
- .dia_init = sha224_init,
- .dia_update = sha256_update,
- .dia_final = sha224_final } }
+static struct shash_alg sha224 = {
+ .digestsize = SHA224_DIGEST_SIZE,
+ .init = sha224_init,
+ .update = sha256_update,
+ .final = sha224_final,
+ .descsize = sizeof(struct sha256_ctx),
+ .base = {
+ .cra_name = "sha224",
+ .cra_driver_name= "sha224-generic",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init sha256_generic_mod_init(void)
{
int ret = 0;
- ret = crypto_register_alg(&sha224);
+ ret = crypto_register_shash(&sha224);
if (ret < 0)
return ret;
- ret = crypto_register_alg(&sha256);
+ ret = crypto_register_shash(&sha256);
if (ret < 0)
- crypto_unregister_alg(&sha224);
+ crypto_unregister_shash(&sha224);
return ret;
}
static void __exit sha256_generic_mod_fini(void)
{
- crypto_unregister_alg(&sha224);
- crypto_unregister_alg(&sha256);
+ crypto_unregister_shash(&sha224);
+ crypto_unregister_shash(&sha256);
}
module_init(sha256_generic_mod_init);
diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c
index bc3686138aeb..3bea38d12242 100644
--- a/crypto/sha512_generic.c
+++ b/crypto/sha512_generic.c
@@ -10,7 +10,7 @@
* later version.
*
*/
-
+#include <crypto/internal/hash.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>
@@ -18,16 +18,17 @@
#include <linux/crypto.h>
#include <linux/types.h>
#include <crypto/sha.h>
-
+#include <linux/percpu.h>
#include <asm/byteorder.h>
struct sha512_ctx {
u64 state[8];
u32 count[4];
u8 buf[128];
- u64 W[80];
};
+static DEFINE_PER_CPU(u64[80], msg_schedule);
+
static inline u64 Ch(u64 x, u64 y, u64 z)
{
return z ^ (x & (y ^ z));
@@ -89,11 +90,12 @@ static inline void BLEND_OP(int I, u64 *W)
}
static void
-sha512_transform(u64 *state, u64 *W, const u8 *input)
+sha512_transform(u64 *state, const u8 *input)
{
u64 a, b, c, d, e, f, g, h, t1, t2;
int i;
+ u64 *W = get_cpu_var(msg_schedule);
/* load the input */
for (i = 0; i < 16; i++)
@@ -132,12 +134,14 @@ sha512_transform(u64 *state, u64 *W, const u8 *input)
/* erase our data */
a = b = c = d = e = f = g = h = t1 = t2 = 0;
+ memset(W, 0, sizeof(__get_cpu_var(msg_schedule)));
+ put_cpu_var(msg_schedule);
}
-static void
-sha512_init(struct crypto_tfm *tfm)
+static int
+sha512_init(struct shash_desc *desc)
{
- struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha512_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA512_H0;
sctx->state[1] = SHA512_H1;
sctx->state[2] = SHA512_H2;
@@ -147,12 +151,14 @@ sha512_init(struct crypto_tfm *tfm)
sctx->state[6] = SHA512_H6;
sctx->state[7] = SHA512_H7;
sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
+
+ return 0;
}
-static void
-sha384_init(struct crypto_tfm *tfm)
+static int
+sha384_init(struct shash_desc *desc)
{
- struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha512_ctx *sctx = shash_desc_ctx(desc);
sctx->state[0] = SHA384_H0;
sctx->state[1] = SHA384_H1;
sctx->state[2] = SHA384_H2;
@@ -162,12 +168,14 @@ sha384_init(struct crypto_tfm *tfm)
sctx->state[6] = SHA384_H6;
sctx->state[7] = SHA384_H7;
sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0;
+
+ return 0;
}
-static void
-sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
+static int
+sha512_update(struct shash_desc *desc, const u8 *data, unsigned int len)
{
- struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha512_ctx *sctx = shash_desc_ctx(desc);
unsigned int i, index, part_len;
@@ -187,10 +195,10 @@ sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
/* Transform as many times as possible. */
if (len >= part_len) {
memcpy(&sctx->buf[index], data, part_len);
- sha512_transform(sctx->state, sctx->W, sctx->buf);
+ sha512_transform(sctx->state, sctx->buf);
for (i = part_len; i + 127 < len; i+=128)
- sha512_transform(sctx->state, sctx->W, &data[i]);
+ sha512_transform(sctx->state, &data[i]);
index = 0;
} else {
@@ -200,14 +208,13 @@ sha512_update(struct crypto_tfm *tfm, const u8 *data, unsigned int len)
/* Buffer remaining input */
memcpy(&sctx->buf[index], &data[i], len - i);
- /* erase our data */
- memset(sctx->W, 0, sizeof(sctx->W));
+ return 0;
}
-static void
-sha512_final(struct crypto_tfm *tfm, u8 *hash)
+static int
+sha512_final(struct shash_desc *desc, u8 *hash)
{
- struct sha512_ctx *sctx = crypto_tfm_ctx(tfm);
+ struct sha512_ctx *sctx = shash_desc_ctx(desc);
static u8 padding[128] = { 0x80, };
__be64 *dst = (__be64 *)hash;
__be32 bits[4];
@@ -223,10 +230,10 @@ sha512_final(struct crypto_tfm *tfm, u8 *hash)
/* Pad out to 112 mod 128. */
index = (sctx->count[0] >> 3) & 0x7f;
pad_len = (index < 112) ? (112 - index) : ((128+112) - index);
- sha512_update(tfm, padding, pad_len);
+ sha512_update(desc, padding, pad_len);
/* Append length (before padding) */
- sha512_update(tfm, (const u8 *)bits, sizeof(bits));
+ sha512_update(desc, (const u8 *)bits, sizeof(bits));
/* Store state in digest */
for (i = 0; i < 8; i++)
@@ -234,66 +241,66 @@ sha512_final(struct crypto_tfm *tfm, u8 *hash)
/* Zeroize sensitive information. */
memset(sctx, 0, sizeof(struct sha512_ctx));
+
+ return 0;
}
-static void sha384_final(struct crypto_tfm *tfm, u8 *hash)
+static int sha384_final(struct shash_desc *desc, u8 *hash)
{
- u8 D[64];
+ u8 D[64];
+
+ sha512_final(desc, D);
- sha512_final(tfm, D);
+ memcpy(hash, D, 48);
+ memset(D, 0, 64);
- memcpy(hash, D, 48);
- memset(D, 0, 64);
+ return 0;
}
-static struct crypto_alg sha512 = {
- .cra_name = "sha512",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA512_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct sha512_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(sha512.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA512_DIGEST_SIZE,
- .dia_init = sha512_init,
- .dia_update = sha512_update,
- .dia_final = sha512_final }
- }
+static struct shash_alg sha512 = {
+ .digestsize = SHA512_DIGEST_SIZE,
+ .init = sha512_init,
+ .update = sha512_update,
+ .final = sha512_final,
+ .descsize = sizeof(struct sha512_ctx),
+ .base = {
+ .cra_name = "sha512",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg sha384 = {
- .cra_name = "sha384",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = SHA384_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct sha512_ctx),
- .cra_alignmask = 3,
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(sha384.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = SHA384_DIGEST_SIZE,
- .dia_init = sha384_init,
- .dia_update = sha512_update,
- .dia_final = sha384_final }
- }
+static struct shash_alg sha384 = {
+ .digestsize = SHA384_DIGEST_SIZE,
+ .init = sha384_init,
+ .update = sha512_update,
+ .final = sha384_final,
+ .descsize = sizeof(struct sha512_ctx),
+ .base = {
+ .cra_name = "sha384",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init sha512_generic_mod_init(void)
{
int ret = 0;
- if ((ret = crypto_register_alg(&sha384)) < 0)
+ if ((ret = crypto_register_shash(&sha384)) < 0)
goto out;
- if ((ret = crypto_register_alg(&sha512)) < 0)
- crypto_unregister_alg(&sha384);
+ if ((ret = crypto_register_shash(&sha512)) < 0)
+ crypto_unregister_shash(&sha384);
out:
return ret;
}
static void __exit sha512_generic_mod_fini(void)
{
- crypto_unregister_alg(&sha384);
- crypto_unregister_alg(&sha512);
+ crypto_unregister_shash(&sha384);
+ crypto_unregister_shash(&sha512);
}
module_init(sha512_generic_mod_init);
diff --git a/crypto/shash.c b/crypto/shash.c
new file mode 100644
index 000000000000..c9df367332ff
--- /dev/null
+++ b/crypto/shash.c
@@ -0,0 +1,508 @@
+/*
+ * Synchronous Cryptographic Hash operations.
+ *
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+#include <crypto/scatterwalk.h>
+#include <crypto/internal/hash.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+
+static const struct crypto_type crypto_shash_type;
+
+static inline struct crypto_shash *__crypto_shash_cast(struct crypto_tfm *tfm)
+{
+ return container_of(tfm, struct crypto_shash, base);
+}
+
+#include "internal.h"
+
+static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+ unsigned long absize;
+ u8 *buffer, *alignbuffer;
+ int err;
+
+ absize = keylen + (alignmask & ~(CRYPTO_MINALIGN - 1));
+ buffer = kmalloc(absize, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
+ memcpy(alignbuffer, key, keylen);
+ err = shash->setkey(tfm, alignbuffer, keylen);
+ memset(alignbuffer, 0, keylen);
+ kfree(buffer);
+ return err;
+}
+
+int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+ if (!shash->setkey)
+ return -ENOSYS;
+
+ if ((unsigned long)key & alignmask)
+ return shash_setkey_unaligned(tfm, key, keylen);
+
+ return shash->setkey(tfm, key, keylen);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_setkey);
+
+static inline unsigned int shash_align_buffer_size(unsigned len,
+ unsigned long mask)
+{
+ return len + (mask & ~(__alignof__(u8 __attribute__ ((aligned))) - 1));
+}
+
+static int shash_update_unaligned(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+ unsigned int unaligned_len = alignmask + 1 -
+ ((unsigned long)data & alignmask);
+ u8 buf[shash_align_buffer_size(unaligned_len, alignmask)]
+ __attribute__ ((aligned));
+
+ memcpy(buf, data, unaligned_len);
+
+ return shash->update(desc, buf, unaligned_len) ?:
+ shash->update(desc, data + unaligned_len, len - unaligned_len);
+}
+
+int crypto_shash_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+ if ((unsigned long)data & alignmask)
+ return shash_update_unaligned(desc, data, len);
+
+ return shash->update(desc, data, len);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_update);
+
+static int shash_final_unaligned(struct shash_desc *desc, u8 *out)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned int ds = crypto_shash_digestsize(tfm);
+ u8 buf[shash_align_buffer_size(ds, alignmask)]
+ __attribute__ ((aligned));
+ int err;
+
+ err = shash->final(desc, buf);
+ memcpy(out, buf, ds);
+ return err;
+}
+
+int crypto_shash_final(struct shash_desc *desc, u8 *out)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+ if ((unsigned long)out & alignmask)
+ return shash_final_unaligned(desc, out);
+
+ return shash->final(desc, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_final);
+
+static int shash_finup_unaligned(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return crypto_shash_update(desc, data, len) ?:
+ crypto_shash_final(desc, out);
+}
+
+int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+ if (((unsigned long)data | (unsigned long)out) & alignmask ||
+ !shash->finup)
+ return shash_finup_unaligned(desc, data, len, out);
+
+ return shash->finup(desc, data, len, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_finup);
+
+static int shash_digest_unaligned(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return crypto_shash_init(desc) ?:
+ crypto_shash_update(desc, data, len) ?:
+ crypto_shash_final(desc, out);
+}
+
+int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *shash = crypto_shash_alg(tfm);
+ unsigned long alignmask = crypto_shash_alignmask(tfm);
+
+ if (((unsigned long)data | (unsigned long)out) & alignmask ||
+ !shash->digest)
+ return shash_digest_unaligned(desc, data, len, out);
+
+ return shash->digest(desc, data, len, out);
+}
+EXPORT_SYMBOL_GPL(crypto_shash_digest);
+
+int crypto_shash_import(struct shash_desc *desc, const u8 *in)
+{
+ struct crypto_shash *tfm = desc->tfm;
+ struct shash_alg *alg = crypto_shash_alg(tfm);
+
+ memcpy(shash_desc_ctx(desc), in, crypto_shash_descsize(tfm));
+
+ if (alg->reinit)
+ alg->reinit(desc);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_shash_import);
+
+static int shash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct crypto_shash **ctx = crypto_ahash_ctx(tfm);
+
+ return crypto_shash_setkey(*ctx, key, keylen);
+}
+
+static int shash_async_init(struct ahash_request *req)
+{
+ struct crypto_shash **ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+ struct shash_desc *desc = ahash_request_ctx(req);
+
+ desc->tfm = *ctx;
+ desc->flags = req->base.flags;
+
+ return crypto_shash_init(desc);
+}
+
+static int shash_async_update(struct ahash_request *req)
+{
+ struct shash_desc *desc = ahash_request_ctx(req);
+ struct crypto_hash_walk walk;
+ int nbytes;
+
+ for (nbytes = crypto_hash_walk_first(req, &walk); nbytes > 0;
+ nbytes = crypto_hash_walk_done(&walk, nbytes))
+ nbytes = crypto_shash_update(desc, walk.data, nbytes);
+
+ return nbytes;
+}
+
+static int shash_async_final(struct ahash_request *req)
+{
+ return crypto_shash_final(ahash_request_ctx(req), req->result);
+}
+
+static int shash_async_digest(struct ahash_request *req)
+{
+ struct scatterlist *sg = req->src;
+ unsigned int offset = sg->offset;
+ unsigned int nbytes = req->nbytes;
+ int err;
+
+ if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+ struct crypto_shash **ctx =
+ crypto_ahash_ctx(crypto_ahash_reqtfm(req));
+ struct shash_desc *desc = ahash_request_ctx(req);
+ void *data;
+
+ desc->tfm = *ctx;
+ desc->flags = req->base.flags;
+
+ data = crypto_kmap(sg_page(sg), 0);
+ err = crypto_shash_digest(desc, data + offset, nbytes,
+ req->result);
+ crypto_kunmap(data, 0);
+ crypto_yield(desc->flags);
+ goto out;
+ }
+
+ err = shash_async_init(req);
+ if (err)
+ goto out;
+
+ err = shash_async_update(req);
+ if (err)
+ goto out;
+
+ err = shash_async_final(req);
+
+out:
+ return err;
+}
+
+static void crypto_exit_shash_ops_async(struct crypto_tfm *tfm)
+{
+ struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(*ctx);
+}
+
+static int crypto_init_shash_ops_async(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *calg = tfm->__crt_alg;
+ struct shash_alg *alg = __crypto_shash_alg(calg);
+ struct ahash_tfm *crt = &tfm->crt_ahash;
+ struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
+ struct crypto_shash *shash;
+
+ if (!crypto_mod_get(calg))
+ return -EAGAIN;
+
+ shash = __crypto_shash_cast(crypto_create_tfm(
+ calg, &crypto_shash_type));
+ if (IS_ERR(shash)) {
+ crypto_mod_put(calg);
+ return PTR_ERR(shash);
+ }
+
+ *ctx = shash;
+ tfm->exit = crypto_exit_shash_ops_async;
+
+ crt->init = shash_async_init;
+ crt->update = shash_async_update;
+ crt->final = shash_async_final;
+ crt->digest = shash_async_digest;
+ crt->setkey = shash_async_setkey;
+
+ crt->digestsize = alg->digestsize;
+ crt->reqsize = sizeof(struct shash_desc) + crypto_shash_descsize(shash);
+
+ return 0;
+}
+
+static int shash_compat_setkey(struct crypto_hash *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct shash_desc *desc = crypto_hash_ctx(tfm);
+
+ return crypto_shash_setkey(desc->tfm, key, keylen);
+}
+
+static int shash_compat_init(struct hash_desc *hdesc)
+{
+ struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+
+ desc->flags = hdesc->flags;
+
+ return crypto_shash_init(desc);
+}
+
+static int shash_compat_update(struct hash_desc *hdesc, struct scatterlist *sg,
+ unsigned int len)
+{
+ struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+ struct crypto_hash_walk walk;
+ int nbytes;
+
+ for (nbytes = crypto_hash_walk_first_compat(hdesc, &walk, sg, len);
+ nbytes > 0; nbytes = crypto_hash_walk_done(&walk, nbytes))
+ nbytes = crypto_shash_update(desc, walk.data, nbytes);
+
+ return nbytes;
+}
+
+static int shash_compat_final(struct hash_desc *hdesc, u8 *out)
+{
+ return crypto_shash_final(crypto_hash_ctx(hdesc->tfm), out);
+}
+
+static int shash_compat_digest(struct hash_desc *hdesc, struct scatterlist *sg,
+ unsigned int nbytes, u8 *out)
+{
+ unsigned int offset = sg->offset;
+ int err;
+
+ if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+ struct shash_desc *desc = crypto_hash_ctx(hdesc->tfm);
+ void *data;
+
+ desc->flags = hdesc->flags;
+
+ data = crypto_kmap(sg_page(sg), 0);
+ err = crypto_shash_digest(desc, data + offset, nbytes, out);
+ crypto_kunmap(data, 0);
+ crypto_yield(desc->flags);
+ goto out;
+ }
+
+ err = shash_compat_init(hdesc);
+ if (err)
+ goto out;
+
+ err = shash_compat_update(hdesc, sg, nbytes);
+ if (err)
+ goto out;
+
+ err = shash_compat_final(hdesc, out);
+
+out:
+ return err;
+}
+
+static void crypto_exit_shash_ops_compat(struct crypto_tfm *tfm)
+{
+ struct shash_desc *desc= crypto_tfm_ctx(tfm);
+
+ crypto_free_shash(desc->tfm);
+}
+
+static int crypto_init_shash_ops_compat(struct crypto_tfm *tfm)
+{
+ struct hash_tfm *crt = &tfm->crt_hash;
+ struct crypto_alg *calg = tfm->__crt_alg;
+ struct shash_alg *alg = __crypto_shash_alg(calg);
+ struct shash_desc *desc = crypto_tfm_ctx(tfm);
+ struct crypto_shash *shash;
+
+ shash = __crypto_shash_cast(crypto_create_tfm(
+ calg, &crypto_shash_type));
+ if (IS_ERR(shash))
+ return PTR_ERR(shash);
+
+ desc->tfm = shash;
+ tfm->exit = crypto_exit_shash_ops_compat;
+
+ crt->init = shash_compat_init;
+ crt->update = shash_compat_update;
+ crt->final = shash_compat_final;
+ crt->digest = shash_compat_digest;
+ crt->setkey = shash_compat_setkey;
+
+ crt->digestsize = alg->digestsize;
+
+ return 0;
+}
+
+static int crypto_init_shash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+ switch (mask & CRYPTO_ALG_TYPE_MASK) {
+ case CRYPTO_ALG_TYPE_HASH_MASK:
+ return crypto_init_shash_ops_compat(tfm);
+ case CRYPTO_ALG_TYPE_AHASH_MASK:
+ return crypto_init_shash_ops_async(tfm);
+ }
+
+ return -EINVAL;
+}
+
+static unsigned int crypto_shash_ctxsize(struct crypto_alg *alg, u32 type,
+ u32 mask)
+{
+ struct shash_alg *salg = __crypto_shash_alg(alg);
+
+ switch (mask & CRYPTO_ALG_TYPE_MASK) {
+ case CRYPTO_ALG_TYPE_HASH_MASK:
+ return sizeof(struct shash_desc) + salg->descsize;
+ case CRYPTO_ALG_TYPE_AHASH_MASK:
+ return sizeof(struct crypto_shash *);
+ }
+
+ return 0;
+}
+
+static int crypto_shash_init_tfm(struct crypto_tfm *tfm,
+ const struct crypto_type *frontend)
+{
+ if (frontend->type != CRYPTO_ALG_TYPE_SHASH)
+ return -EINVAL;
+ return 0;
+}
+
+static unsigned int crypto_shash_extsize(struct crypto_alg *alg,
+ const struct crypto_type *frontend)
+{
+ return alg->cra_ctxsize;
+}
+
+static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
+ __attribute__ ((unused));
+static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
+{
+ struct shash_alg *salg = __crypto_shash_alg(alg);
+
+ seq_printf(m, "type : shash\n");
+ seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
+ seq_printf(m, "digestsize : %u\n", salg->digestsize);
+ seq_printf(m, "descsize : %u\n", salg->descsize);
+}
+
+static const struct crypto_type crypto_shash_type = {
+ .ctxsize = crypto_shash_ctxsize,
+ .extsize = crypto_shash_extsize,
+ .init = crypto_init_shash_ops,
+ .init_tfm = crypto_shash_init_tfm,
+#ifdef CONFIG_PROC_FS
+ .show = crypto_shash_show,
+#endif
+ .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+ .maskset = CRYPTO_ALG_TYPE_MASK,
+ .type = CRYPTO_ALG_TYPE_SHASH,
+ .tfmsize = offsetof(struct crypto_shash, base),
+};
+
+struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
+ u32 mask)
+{
+ return __crypto_shash_cast(
+ crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask));
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_shash);
+
+int crypto_register_shash(struct shash_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ if (alg->digestsize > PAGE_SIZE / 8 ||
+ alg->descsize > PAGE_SIZE / 8)
+ return -EINVAL;
+
+ base->cra_type = &crypto_shash_type;
+ base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+ base->cra_flags |= CRYPTO_ALG_TYPE_SHASH;
+
+ return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_shash);
+
+int crypto_unregister_shash(struct shash_alg *alg)
+{
+ return crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_shash);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Synchronous cryptographic hash type");
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index b828c6cf1b1d..a75f11ffb957 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -843,6 +843,14 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
goto out;
}
+ if (dlen != ctemplate[i].outlen) {
+ printk(KERN_ERR "alg: comp: Compression test %d "
+ "failed for %s: output len = %d\n", i + 1, algo,
+ dlen);
+ ret = -EINVAL;
+ goto out;
+ }
+
if (memcmp(result, ctemplate[i].output, dlen)) {
printk(KERN_ERR "alg: comp: Compression test %d "
"failed for %s\n", i + 1, algo);
@@ -853,7 +861,7 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
}
for (i = 0; i < dtcount; i++) {
- int ilen, ret, dlen = COMP_BUF_SIZE;
+ int ilen, dlen = COMP_BUF_SIZE;
memset(result, 0, sizeof (result));
@@ -867,6 +875,14 @@ static int test_comp(struct crypto_comp *tfm, struct comp_testvec *ctemplate,
goto out;
}
+ if (dlen != dtemplate[i].outlen) {
+ printk(KERN_ERR "alg: comp: Decompression test %d "
+ "failed for %s: output len = %d\n", i + 1, algo,
+ dlen);
+ ret = -EINVAL;
+ goto out;
+ }
+
if (memcmp(result, dtemplate[i].output, dlen)) {
printk(KERN_ERR "alg: comp: Decompression test %d "
"failed for %s\n", i + 1, algo);
@@ -1010,6 +1026,55 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
return err;
}
+static int alg_test_crc32c(const struct alg_test_desc *desc,
+ const char *driver, u32 type, u32 mask)
+{
+ struct crypto_shash *tfm;
+ u32 val;
+ int err;
+
+ err = alg_test_hash(desc, driver, type, mask);
+ if (err)
+ goto out;
+
+ tfm = crypto_alloc_shash(driver, type, mask);
+ if (IS_ERR(tfm)) {
+ printk(KERN_ERR "alg: crc32c: Failed to load transform for %s: "
+ "%ld\n", driver, PTR_ERR(tfm));
+ err = PTR_ERR(tfm);
+ goto out;
+ }
+
+ do {
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(tfm)];
+ } sdesc;
+
+ sdesc.shash.tfm = tfm;
+ sdesc.shash.flags = 0;
+
+ *(u32 *)sdesc.ctx = le32_to_cpu(420553207);
+ err = crypto_shash_final(&sdesc.shash, (u8 *)&val);
+ if (err) {
+ printk(KERN_ERR "alg: crc32c: Operation failed for "
+ "%s: %d\n", driver, err);
+ break;
+ }
+
+ if (val != ~420553207) {
+ printk(KERN_ERR "alg: crc32c: Test failed for %s: "
+ "%d\n", driver, val);
+ err = -EINVAL;
+ }
+ } while (0);
+
+ crypto_free_shash(tfm);
+
+out:
+ return err;
+}
+
/* Please keep this list sorted by algorithm name. */
static const struct alg_test_desc alg_test_descs[] = {
{
@@ -1134,7 +1199,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "crc32c",
- .test = alg_test_hash,
+ .test = alg_test_crc32c,
.suite = {
.hash = {
.vecs = crc32c_tv_template,
@@ -1801,6 +1866,7 @@ static int alg_find_test(const char *alg)
int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
{
int i;
+ int rc;
if ((type & CRYPTO_ALG_TYPE_MASK) == CRYPTO_ALG_TYPE_CIPHER) {
char nalg[CRYPTO_MAX_ALG_NAME];
@@ -1820,8 +1886,12 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask)
if (i < 0)
goto notest;
- return alg_test_descs[i].test(alg_test_descs + i, driver,
+ rc = alg_test_descs[i].test(alg_test_descs + i, driver,
type, mask);
+ if (fips_enabled && rc)
+ panic("%s: %s alg self test failed in fips mode!\n", driver, alg);
+
+ return rc;
notest:
printk(KERN_INFO "alg: No test for %s (%s)\n", alg, driver);
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index dee94d9ecfba..132953e144d3 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -8349,7 +8349,7 @@ struct comp_testvec {
/*
* Deflate test vectors (null-terminated strings).
- * Params: winbits=11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
+ * Params: winbits=-11, Z_DEFAULT_COMPRESSION, MAX_MEM_LEVEL.
*/
#define DEFLATE_COMP_TEST_VECTORS 2
#define DEFLATE_DECOMP_TEST_VECTORS 2
diff --git a/crypto/tgr192.c b/crypto/tgr192.c
index a92414f24beb..cbca4f208c9f 100644
--- a/crypto/tgr192.c
+++ b/crypto/tgr192.c
@@ -21,11 +21,11 @@
* (at your option) any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/byteorder.h>
-#include <linux/crypto.h>
#include <linux/types.h>
#define TGR192_DIGEST_SIZE 24
@@ -495,24 +495,26 @@ static void tgr192_transform(struct tgr192_ctx *tctx, const u8 * data)
tctx->c = c;
}
-static void tgr192_init(struct crypto_tfm *tfm)
+static int tgr192_init(struct shash_desc *desc)
{
- struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+ struct tgr192_ctx *tctx = shash_desc_ctx(desc);
tctx->a = 0x0123456789abcdefULL;
tctx->b = 0xfedcba9876543210ULL;
tctx->c = 0xf096a5b4c3b2e187ULL;
tctx->nblocks = 0;
tctx->count = 0;
+
+ return 0;
}
/* Update the message digest with the contents
* of INBUF with length INLEN. */
-static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
+static int tgr192_update(struct shash_desc *desc, const u8 *inbuf,
unsigned int len)
{
- struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+ struct tgr192_ctx *tctx = shash_desc_ctx(desc);
if (tctx->count == 64) { /* flush the buffer */
tgr192_transform(tctx, tctx->hash);
@@ -520,15 +522,15 @@ static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
tctx->nblocks++;
}
if (!inbuf) {
- return;
+ return 0;
}
if (tctx->count) {
for (; len && tctx->count < 64; len--) {
tctx->hash[tctx->count++] = *inbuf++;
}
- tgr192_update(tfm, NULL, 0);
+ tgr192_update(desc, NULL, 0);
if (!len) {
- return;
+ return 0;
}
}
@@ -543,20 +545,22 @@ static void tgr192_update(struct crypto_tfm *tfm, const u8 *inbuf,
for (; len && tctx->count < 64; len--) {
tctx->hash[tctx->count++] = *inbuf++;
}
+
+ return 0;
}
/* The routine terminates the computation */
-static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr192_final(struct shash_desc *desc, u8 * out)
{
- struct tgr192_ctx *tctx = crypto_tfm_ctx(tfm);
+ struct tgr192_ctx *tctx = shash_desc_ctx(desc);
__be64 *dst = (__be64 *)out;
__be64 *be64p;
__le32 *le32p;
u32 t, msb, lsb;
- tgr192_update(tfm, NULL, 0); /* flush */ ;
+ tgr192_update(desc, NULL, 0); /* flush */ ;
msb = 0;
t = tctx->nblocks;
@@ -584,7 +588,7 @@ static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
while (tctx->count < 64) {
tctx->hash[tctx->count++] = 0;
}
- tgr192_update(tfm, NULL, 0); /* flush */ ;
+ tgr192_update(desc, NULL, 0); /* flush */ ;
memset(tctx->hash, 0, 56); /* fill next block with zeroes */
}
/* append the 64 bit count */
@@ -598,91 +602,94 @@ static void tgr192_final(struct crypto_tfm *tfm, u8 * out)
dst[0] = be64p[0] = cpu_to_be64(tctx->a);
dst[1] = be64p[1] = cpu_to_be64(tctx->b);
dst[2] = be64p[2] = cpu_to_be64(tctx->c);
+
+ return 0;
}
-static void tgr160_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr160_final(struct shash_desc *desc, u8 * out)
{
u8 D[64];
- tgr192_final(tfm, D);
+ tgr192_final(desc, D);
memcpy(out, D, TGR160_DIGEST_SIZE);
memset(D, 0, TGR192_DIGEST_SIZE);
+
+ return 0;
}
-static void tgr128_final(struct crypto_tfm *tfm, u8 * out)
+static int tgr128_final(struct shash_desc *desc, u8 * out)
{
u8 D[64];
- tgr192_final(tfm, D);
+ tgr192_final(desc, D);
memcpy(out, D, TGR128_DIGEST_SIZE);
memset(D, 0, TGR192_DIGEST_SIZE);
+
+ return 0;
}
-static struct crypto_alg tgr192 = {
- .cra_name = "tgr192",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = TGR192_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct tgr192_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 7,
- .cra_list = LIST_HEAD_INIT(tgr192.cra_list),
- .cra_u = {.digest = {
- .dia_digestsize = TGR192_DIGEST_SIZE,
- .dia_init = tgr192_init,
- .dia_update = tgr192_update,
- .dia_final = tgr192_final}}
+static struct shash_alg tgr192 = {
+ .digestsize = TGR192_DIGEST_SIZE,
+ .init = tgr192_init,
+ .update = tgr192_update,
+ .final = tgr192_final,
+ .descsize = sizeof(struct tgr192_ctx),
+ .base = {
+ .cra_name = "tgr192",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = TGR192_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg tgr160 = {
- .cra_name = "tgr160",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = TGR192_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct tgr192_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 7,
- .cra_list = LIST_HEAD_INIT(tgr160.cra_list),
- .cra_u = {.digest = {
- .dia_digestsize = TGR160_DIGEST_SIZE,
- .dia_init = tgr192_init,
- .dia_update = tgr192_update,
- .dia_final = tgr160_final}}
+static struct shash_alg tgr160 = {
+ .digestsize = TGR160_DIGEST_SIZE,
+ .init = tgr192_init,
+ .update = tgr192_update,
+ .final = tgr160_final,
+ .descsize = sizeof(struct tgr192_ctx),
+ .base = {
+ .cra_name = "tgr160",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = TGR192_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg tgr128 = {
- .cra_name = "tgr128",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = TGR192_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct tgr192_ctx),
- .cra_module = THIS_MODULE,
- .cra_alignmask = 7,
- .cra_list = LIST_HEAD_INIT(tgr128.cra_list),
- .cra_u = {.digest = {
- .dia_digestsize = TGR128_DIGEST_SIZE,
- .dia_init = tgr192_init,
- .dia_update = tgr192_update,
- .dia_final = tgr128_final}}
+static struct shash_alg tgr128 = {
+ .digestsize = TGR128_DIGEST_SIZE,
+ .init = tgr192_init,
+ .update = tgr192_update,
+ .final = tgr128_final,
+ .descsize = sizeof(struct tgr192_ctx),
+ .base = {
+ .cra_name = "tgr128",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = TGR192_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init tgr192_mod_init(void)
{
int ret = 0;
- ret = crypto_register_alg(&tgr192);
+ ret = crypto_register_shash(&tgr192);
if (ret < 0) {
goto out;
}
- ret = crypto_register_alg(&tgr160);
+ ret = crypto_register_shash(&tgr160);
if (ret < 0) {
- crypto_unregister_alg(&tgr192);
+ crypto_unregister_shash(&tgr192);
goto out;
}
- ret = crypto_register_alg(&tgr128);
+ ret = crypto_register_shash(&tgr128);
if (ret < 0) {
- crypto_unregister_alg(&tgr192);
- crypto_unregister_alg(&tgr160);
+ crypto_unregister_shash(&tgr192);
+ crypto_unregister_shash(&tgr160);
}
out:
return ret;
@@ -690,9 +697,9 @@ static int __init tgr192_mod_init(void)
static void __exit tgr192_mod_fini(void)
{
- crypto_unregister_alg(&tgr192);
- crypto_unregister_alg(&tgr160);
- crypto_unregister_alg(&tgr128);
+ crypto_unregister_shash(&tgr192);
+ crypto_unregister_shash(&tgr160);
+ crypto_unregister_shash(&tgr128);
}
MODULE_ALIAS("tgr160");
diff --git a/crypto/wp512.c b/crypto/wp512.c
index bff28560d66d..723427273687 100644
--- a/crypto/wp512.c
+++ b/crypto/wp512.c
@@ -19,11 +19,11 @@
* (at your option) any later version.
*
*/
+#include <crypto/internal/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/byteorder.h>
-#include <linux/crypto.h>
#include <linux/types.h>
#define WP512_DIGEST_SIZE 64
@@ -980,8 +980,8 @@ static void wp512_process_buffer(struct wp512_ctx *wctx) {
}
-static void wp512_init(struct crypto_tfm *tfm) {
- struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+static int wp512_init(struct shash_desc *desc) {
+ struct wp512_ctx *wctx = shash_desc_ctx(desc);
int i;
memset(wctx->bitLength, 0, 32);
@@ -990,12 +990,14 @@ static void wp512_init(struct crypto_tfm *tfm) {
for (i = 0; i < 8; i++) {
wctx->hash[i] = 0L;
}
+
+ return 0;
}
-static void wp512_update(struct crypto_tfm *tfm, const u8 *source,
+static int wp512_update(struct shash_desc *desc, const u8 *source,
unsigned int len)
{
- struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+ struct wp512_ctx *wctx = shash_desc_ctx(desc);
int sourcePos = 0;
unsigned int bits_len = len * 8; // convert to number of bits
int sourceGap = (8 - ((int)bits_len & 7)) & 7;
@@ -1051,11 +1053,12 @@ static void wp512_update(struct crypto_tfm *tfm, const u8 *source,
wctx->bufferBits = bufferBits;
wctx->bufferPos = bufferPos;
+ return 0;
}
-static void wp512_final(struct crypto_tfm *tfm, u8 *out)
+static int wp512_final(struct shash_desc *desc, u8 *out)
{
- struct wp512_ctx *wctx = crypto_tfm_ctx(tfm);
+ struct wp512_ctx *wctx = shash_desc_ctx(desc);
int i;
u8 *buffer = wctx->buffer;
u8 *bitLength = wctx->bitLength;
@@ -1084,89 +1087,95 @@ static void wp512_final(struct crypto_tfm *tfm, u8 *out)
digest[i] = cpu_to_be64(wctx->hash[i]);
wctx->bufferBits = bufferBits;
wctx->bufferPos = bufferPos;
+
+ return 0;
}
-static void wp384_final(struct crypto_tfm *tfm, u8 *out)
+static int wp384_final(struct shash_desc *desc, u8 *out)
{
u8 D[64];
- wp512_final(tfm, D);
+ wp512_final(desc, D);
memcpy (out, D, WP384_DIGEST_SIZE);
memset (D, 0, WP512_DIGEST_SIZE);
+
+ return 0;
}
-static void wp256_final(struct crypto_tfm *tfm, u8 *out)
+static int wp256_final(struct shash_desc *desc, u8 *out)
{
u8 D[64];
- wp512_final(tfm, D);
+ wp512_final(desc, D);
memcpy (out, D, WP256_DIGEST_SIZE);
memset (D, 0, WP512_DIGEST_SIZE);
+
+ return 0;
}
-static struct crypto_alg wp512 = {
- .cra_name = "wp512",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = WP512_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct wp512_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(wp512.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = WP512_DIGEST_SIZE,
- .dia_init = wp512_init,
- .dia_update = wp512_update,
- .dia_final = wp512_final } }
+static struct shash_alg wp512 = {
+ .digestsize = WP512_DIGEST_SIZE,
+ .init = wp512_init,
+ .update = wp512_update,
+ .final = wp512_final,
+ .descsize = sizeof(struct wp512_ctx),
+ .base = {
+ .cra_name = "wp512",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = WP512_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg wp384 = {
- .cra_name = "wp384",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = WP512_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct wp512_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(wp384.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = WP384_DIGEST_SIZE,
- .dia_init = wp512_init,
- .dia_update = wp512_update,
- .dia_final = wp384_final } }
+static struct shash_alg wp384 = {
+ .digestsize = WP384_DIGEST_SIZE,
+ .init = wp512_init,
+ .update = wp512_update,
+ .final = wp384_final,
+ .descsize = sizeof(struct wp512_ctx),
+ .base = {
+ .cra_name = "wp384",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = WP512_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
-static struct crypto_alg wp256 = {
- .cra_name = "wp256",
- .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
- .cra_blocksize = WP512_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct wp512_ctx),
- .cra_module = THIS_MODULE,
- .cra_list = LIST_HEAD_INIT(wp256.cra_list),
- .cra_u = { .digest = {
- .dia_digestsize = WP256_DIGEST_SIZE,
- .dia_init = wp512_init,
- .dia_update = wp512_update,
- .dia_final = wp256_final } }
+static struct shash_alg wp256 = {
+ .digestsize = WP256_DIGEST_SIZE,
+ .init = wp512_init,
+ .update = wp512_update,
+ .final = wp256_final,
+ .descsize = sizeof(struct wp512_ctx),
+ .base = {
+ .cra_name = "wp256",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = WP512_BLOCK_SIZE,
+ .cra_module = THIS_MODULE,
+ }
};
static int __init wp512_mod_init(void)
{
int ret = 0;
- ret = crypto_register_alg(&wp512);
+ ret = crypto_register_shash(&wp512);
if (ret < 0)
goto out;
- ret = crypto_register_alg(&wp384);
+ ret = crypto_register_shash(&wp384);
if (ret < 0)
{
- crypto_unregister_alg(&wp512);
+ crypto_unregister_shash(&wp512);
goto out;
}
- ret = crypto_register_alg(&wp256);
+ ret = crypto_register_shash(&wp256);
if (ret < 0)
{
- crypto_unregister_alg(&wp512);
- crypto_unregister_alg(&wp384);
+ crypto_unregister_shash(&wp512);
+ crypto_unregister_shash(&wp384);
}
out:
return ret;
@@ -1174,9 +1183,9 @@ out:
static void __exit wp512_mod_fini(void)
{
- crypto_unregister_alg(&wp512);
- crypto_unregister_alg(&wp384);
- crypto_unregister_alg(&wp256);
+ crypto_unregister_shash(&wp512);
+ crypto_unregister_shash(&wp384);
+ crypto_unregister_shash(&wp256);
}
MODULE_ALIAS("wp384");
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 7edf6d913c13..765fd1c56cd6 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -688,14 +688,6 @@ void __init acpi_early_init(void)
if (acpi_disabled)
return;
- /*
- * ACPI CA initializes acpi_dbg_level to non-zero, which means
- * we get debug output merely by turning on CONFIG_ACPI_DEBUG.
- * Turn it off so we don't get output unless the user specifies
- * acpi.debug_level.
- */
- acpi_dbg_level = 0;
-
printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION);
/* enable workarounds, unless strict ACPI spec. compliance */
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 11acaee14d66..bf79d83bdfbb 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -384,6 +384,27 @@ acpi_pci_free_irq(struct acpi_prt_entry *entry,
return irq;
}
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+ struct pci_bus *bus_it;
+
+ for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+ if (!bus_it->self)
+ return 0;
+
+ printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor,
+ bus_it->self->device);
+
+ if (bus_it->self->irq_reroute_variant)
+ return bus_it->self->irq_reroute_variant;
+ }
+ return 0;
+}
+#endif /* CONFIG_X86_IO_APIC */
+
/*
* acpi_pci_irq_lookup
* success: return IRQ >= 0
@@ -413,6 +434,41 @@ acpi_pci_irq_lookup(struct pci_bus *bus,
}
ret = func(entry, triggering, polarity, link);
+
+#ifdef CONFIG_X86_IO_APIC
+ /*
+ * Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the
+ * IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel
+ * does during interrupt handling). When this INTx generation cannot be
+ * disabled, we reroute these interrupts to their legacy equivalent to
+ * get rid of spurious interrupts.
+ */
+ if (!noioapicquirk) {
+ switch (bridge_has_boot_interrupt_variant(bus)) {
+ case 0:
+ /* no rerouting necessary */
+ break;
+
+ case INTEL_IRQ_REROUTE_VARIANT:
+ /*
+ * Remap according to INTx routing table in 6700PXH
+ * specs, intel order number 302628-002, section
+ * 2.15.2. Other chipsets (80332, ...) have the same
+ * mapping and are handled here as well.
+ */
+ printk(KERN_INFO "pci irq %d -> rerouted to legacy "
+ "irq %d\n", ret, (ret % 4) + 16);
+ ret = (ret % 4) + 16;
+ break;
+
+ default:
+ printk(KERN_INFO "not rerouting irq %d to legacy irq: "
+ "unknown mapping\n", ret);
+ break;
+ }
+ }
+#endif /* CONFIG_X86_IO_APIC */
+
return ret;
}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 5f8d746a9b81..38aca048e951 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -374,15 +374,15 @@ static int tsc_halts_in_c(int state)
{
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD:
+ case X86_VENDOR_INTEL:
/*
* AMD Fam10h TSC will tick in all
* C/P/S0/S1 states when this bit is set.
*/
- if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
+ if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
return 0;
+
/*FALL THROUGH*/
- case X86_VENDOR_INTEL:
- /* Several cases known where TSC halts in C2 too */
default:
return state > ACPI_STATE_C1;
}
diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c
index 670551b95e56..17ed5ac840f7 100644
--- a/drivers/acpi/utilities/utglobal.c
+++ b/drivers/acpi/utilities/utglobal.c
@@ -64,7 +64,7 @@ u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT;
/* Debug switch - layer (component) mask */
-u32 acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS;
+u32 acpi_dbg_layer = 0;
u32 acpi_gbl_nesting_level = 0;
/* Debugger globals */
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 421b7c71e72d..1a7be96d627b 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -456,7 +456,8 @@ config PATA_MARVELL
config PATA_MPC52xx
tristate "Freescale MPC52xx SoC internal IDE"
- depends on PPC_MPC52xx
+ depends on PPC_MPC52xx && PPC_BESTCOMM
+ select PPC_BESTCOMM_ATA
help
This option enables support for integrated IDE controller
of the Freescale MPC52xx SoC.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index a67b8e7c712d..656448c7fef9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1119,14 +1119,14 @@ static void ahci_start_port(struct ata_port *ap)
/* turn on LEDs */
if (ap->flags & ATA_FLAG_EM) {
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
ahci_transmit_led_message(ap, emp->led_state, 4);
}
}
if (ap->flags & ATA_FLAG_SW_ACTIVITY)
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
ahci_init_sw_activity(link);
}
@@ -1361,7 +1361,7 @@ static ssize_t ahci_led_show(struct ata_port *ap, char *buf)
struct ahci_em_priv *emp;
int rc = 0;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
emp = &pp->em_priv[link->pmp];
rc += sprintf(buf, "%lx\n", emp->led_state);
}
@@ -1941,7 +1941,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
u32 serror;
/* determine active link */
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
if (ata_link_active(link))
break;
if (!link)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 5c33767e66de..dc48a6398abe 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -57,10 +57,7 @@ static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
if (pdev->vendor == PCI_VENDOR_ID_CENATEK)
dma_enabled = 0xFF;
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
/* We don't really care */
dev->pio_mode = XFER_PIO_0;
dev->dma_mode = XFER_MW_DMA_0;
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index c11936e13dd3..5fdf1678d0cc 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -1072,20 +1072,13 @@ static int piix_broken_suspend(void)
* matching is necessary because dmi_system_id.matches is
* limited to four entries.
*/
- if (dmi_get_system_info(DMI_SYS_VENDOR) &&
- dmi_get_system_info(DMI_PRODUCT_NAME) &&
- dmi_get_system_info(DMI_PRODUCT_VERSION) &&
- dmi_get_system_info(DMI_PRODUCT_SERIAL) &&
- dmi_get_system_info(DMI_BOARD_VENDOR) &&
- dmi_get_system_info(DMI_BOARD_NAME) &&
- dmi_get_system_info(DMI_BOARD_VERSION) &&
- !strcmp(dmi_get_system_info(DMI_SYS_VENDOR), "TOSHIBA") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_NAME), "000000") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_VERSION), "000000") &&
- !strcmp(dmi_get_system_info(DMI_PRODUCT_SERIAL), "000000") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_VENDOR), "TOSHIBA") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_NAME), "Portable PC") &&
- !strcmp(dmi_get_system_info(DMI_BOARD_VERSION), "Version A0"))
+ if (dmi_match(DMI_SYS_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_PRODUCT_NAME, "000000") &&
+ dmi_match(DMI_PRODUCT_VERSION, "000000") &&
+ dmi_match(DMI_PRODUCT_SERIAL, "000000") &&
+ dmi_match(DMI_BOARD_VENDOR, "TOSHIBA") &&
+ dmi_match(DMI_BOARD_NAME, "Portable PC") &&
+ dmi_match(DMI_BOARD_VERSION, "Version A0"))
return 1;
return 0;
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index c012307d0ba6..ef02e488d468 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -89,7 +89,7 @@ void ata_acpi_associate_sata_port(struct ata_port *ap)
ap->link.device->acpi_handle = NULL;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
acpi_integer adr = SATA_ADR(ap->port_no, link->pmp);
link->device->acpi_handle =
@@ -129,8 +129,8 @@ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev)
struct ata_link *tlink;
struct ata_device *tdev;
- ata_port_for_each_link(tlink, ap)
- ata_link_for_each_dev(tdev, tlink)
+ ata_for_each_link(tlink, ap, EDGE)
+ ata_for_each_dev(tdev, tlink, ALL)
tdev->flags |= ATA_DFLAG_DETACH;
}
@@ -588,12 +588,9 @@ int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
unsigned long xfer_mask, udma_mask;
- if (!ata_dev_enabled(dev))
- continue;
-
xfer_mask = ata_acpi_gtm_xfermask(dev, gtm);
ata_unpack_xfermask(xfer_mask, NULL, NULL, &udma_mask);
@@ -893,7 +890,7 @@ void ata_acpi_on_resume(struct ata_port *ap)
* use values set by _STM. Cache _GTF result and
* schedule _GTF.
*/
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
ata_acpi_clear_gtf(dev);
if (ata_dev_enabled(dev) &&
ata_dev_get_GTF(dev, NULL) >= 0)
@@ -904,7 +901,7 @@ void ata_acpi_on_resume(struct ata_port *ap)
* there's no reason to evaluate IDE _GTF early
* without _STM. Clear cache and schedule _GTF.
*/
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
ata_acpi_clear_gtf(dev);
if (ata_dev_enabled(dev))
dev->flags |= ATA_DFLAG_ACPI_PENDING;
@@ -932,8 +929,8 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state)
if (state.event == PM_EVENT_ON)
acpi_bus_set_power(ap->acpi_handle, ACPI_STATE_D0);
- ata_link_for_each_dev(dev, &ap->link) {
- if (dev->acpi_handle && ata_dev_enabled(dev))
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
+ if (dev->acpi_handle)
acpi_bus_set_power(dev->acpi_handle,
state.event == PM_EVENT_ON ?
ACPI_STATE_D0 : ACPI_STATE_D3);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bc6695e3c848..fecca4223f8e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -163,43 +163,119 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-/*
- * Iterator helpers. Don't use directly.
+/**
+ * ata_link_next - link iteration helper
+ * @link: the previous link, NULL to start
+ * @ap: ATA port containing links to iterate
+ * @mode: iteration mode, one of ATA_LITER_*
+ *
+ * LOCKING:
+ * Host lock or EH context.
*
- * LOCKING:
- * Host lock or EH context.
+ * RETURNS:
+ * Pointer to the next link.
*/
-struct ata_link *__ata_port_next_link(struct ata_port *ap,
- struct ata_link *link, bool dev_only)
+struct ata_link *ata_link_next(struct ata_link *link, struct ata_port *ap,
+ enum ata_link_iter_mode mode)
{
+ BUG_ON(mode != ATA_LITER_EDGE &&
+ mode != ATA_LITER_PMP_FIRST && mode != ATA_LITER_HOST_FIRST);
+
/* NULL link indicates start of iteration */
- if (!link) {
- if (dev_only && sata_pmp_attached(ap))
- return ap->pmp_link;
- return &ap->link;
- }
+ if (!link)
+ switch (mode) {
+ case ATA_LITER_EDGE:
+ case ATA_LITER_PMP_FIRST:
+ if (sata_pmp_attached(ap))
+ return ap->pmp_link;
+ /* fall through */
+ case ATA_LITER_HOST_FIRST:
+ return &ap->link;
+ }
- /* we just iterated over the host master link, what's next? */
- if (link == &ap->link) {
- if (!sata_pmp_attached(ap)) {
- if (unlikely(ap->slave_link) && !dev_only)
+ /* we just iterated over the host link, what's next? */
+ if (link == &ap->link)
+ switch (mode) {
+ case ATA_LITER_HOST_FIRST:
+ if (sata_pmp_attached(ap))
+ return ap->pmp_link;
+ /* fall through */
+ case ATA_LITER_PMP_FIRST:
+ if (unlikely(ap->slave_link))
return ap->slave_link;
+ /* fall through */
+ case ATA_LITER_EDGE:
return NULL;
}
- return ap->pmp_link;
- }
/* slave_link excludes PMP */
if (unlikely(link == ap->slave_link))
return NULL;
- /* iterate to the next PMP link */
+ /* we were over a PMP link */
if (++link < ap->pmp_link + ap->nr_pmp_links)
return link;
+
+ if (mode == ATA_LITER_PMP_FIRST)
+ return &ap->link;
+
return NULL;
}
/**
+ * ata_dev_next - device iteration helper
+ * @dev: the previous device, NULL to start
+ * @link: ATA link containing devices to iterate
+ * @mode: iteration mode, one of ATA_DITER_*
+ *
+ * LOCKING:
+ * Host lock or EH context.
+ *
+ * RETURNS:
+ * Pointer to the next device.
+ */
+struct ata_device *ata_dev_next(struct ata_device *dev, struct ata_link *link,
+ enum ata_dev_iter_mode mode)
+{
+ BUG_ON(mode != ATA_DITER_ENABLED && mode != ATA_DITER_ENABLED_REVERSE &&
+ mode != ATA_DITER_ALL && mode != ATA_DITER_ALL_REVERSE);
+
+ /* NULL dev indicates start of iteration */
+ if (!dev)
+ switch (mode) {
+ case ATA_DITER_ENABLED:
+ case ATA_DITER_ALL:
+ dev = link->device;
+ goto check;
+ case ATA_DITER_ENABLED_REVERSE:
+ case ATA_DITER_ALL_REVERSE:
+ dev = link->device + ata_link_max_devices(link) - 1;
+ goto check;
+ }
+
+ next:
+ /* move to the next one */
+ switch (mode) {
+ case ATA_DITER_ENABLED:
+ case ATA_DITER_ALL:
+ if (++dev < link->device + ata_link_max_devices(link))
+ goto check;
+ return NULL;
+ case ATA_DITER_ENABLED_REVERSE:
+ case ATA_DITER_ALL_REVERSE:
+ if (--dev >= link->device)
+ goto check;
+ return NULL;
+ }
+
+ check:
+ if ((mode == ATA_DITER_ENABLED || mode == ATA_DITER_ENABLED_REVERSE) &&
+ !ata_dev_enabled(dev))
+ goto next;
+ return dev;
+}
+
+/**
* ata_dev_phys_link - find physical link for a device
* @dev: ATA device to look up physical link for
*
@@ -1107,8 +1183,8 @@ static void ata_lpm_enable(struct ata_host *host)
for (i = 0; i < host->n_ports; i++) {
ap = host->ports[i];
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link)
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL)
ata_dev_disable_pm(dev);
}
}
@@ -2594,11 +2670,11 @@ int ata_bus_probe(struct ata_port *ap)
ata_port_probe(ap);
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
tries[dev->devno] = ATA_PROBE_MAX_TRIES;
retry:
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
@@ -2620,7 +2696,7 @@ int ata_bus_probe(struct ata_port *ap)
/* reset and determine device classes */
ap->ops->phy_reset(ap);
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
if (!(ap->flags & ATA_FLAG_DISABLED) &&
dev->class != ATA_DEV_UNKNOWN)
classes[dev->devno] = dev->class;
@@ -2636,7 +2712,7 @@ int ata_bus_probe(struct ata_port *ap)
specific sequence bass-ackwards so that PDIAG- is released by
the slave device */
- ata_link_for_each_dev_reverse(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL_REVERSE) {
if (tries[dev->devno])
dev->class = classes[dev->devno];
@@ -2653,24 +2729,19 @@ int ata_bus_probe(struct ata_port *ap)
if (ap->ops->cable_detect)
ap->cbl = ap->ops->cable_detect(ap);
- /* We may have SATA bridge glue hiding here irrespective of the
- reported cable types and sensed types */
- ata_link_for_each_dev(dev, &ap->link) {
- if (!ata_dev_enabled(dev))
- continue;
- /* SATA drives indicate we have a bridge. We don't know which
- end of the link the bridge is which is a problem */
+ /* We may have SATA bridge glue hiding here irrespective of
+ * the reported cable types and sensed types. When SATA
+ * drives indicate we have a bridge, we don't know which end
+ * of the link the bridge is which is a problem.
+ */
+ ata_for_each_dev(dev, &ap->link, ENABLED)
if (ata_id_is_sata(dev->id))
ap->cbl = ATA_CBL_SATA;
- }
/* After the identify sequence we can now set up the devices. We do
this in the normal order so that the user doesn't get confused */
- ata_link_for_each_dev(dev, &ap->link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, &ap->link, ENABLED) {
ap->link.eh_context.i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev);
ap->link.eh_context.i.flags &= ~ATA_EHI_PRINTINFO;
@@ -2683,9 +2754,8 @@ int ata_bus_probe(struct ata_port *ap)
if (rc)
goto fail;
- ata_link_for_each_dev(dev, &ap->link)
- if (ata_dev_enabled(dev))
- return 0;
+ ata_for_each_dev(dev, &ap->link, ENABLED)
+ return 0;
/* no device present, disable port */
ata_port_disable(ap);
@@ -3331,13 +3401,10 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
int rc = 0, used_dma = 0, found = 0;
/* step 1: calculate xfer_mask */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
unsigned long pio_mask, dma_mask;
unsigned int mode_mask;
- if (!ata_dev_enabled(dev))
- continue;
-
mode_mask = ATA_DMA_MASK_ATA;
if (dev->class == ATA_DEV_ATAPI)
mode_mask = ATA_DMA_MASK_ATAPI;
@@ -3366,10 +3433,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
goto out;
/* step 2: always set host PIO timings */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
if (dev->pio_mode == 0xff) {
ata_dev_printk(dev, KERN_WARNING, "no PIO support\n");
rc = -EINVAL;
@@ -3383,8 +3447,8 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
}
/* step 3: set host DMA timings */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev) || !ata_dma_enabled(dev))
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!ata_dma_enabled(dev))
continue;
dev->xfer_mode = dev->dma_mode;
@@ -3394,11 +3458,7 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
}
/* step 4: update devices' xfer mode */
- ata_link_for_each_dev(dev, link) {
- /* don't update suspended devices' xfer mode */
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
rc = ata_dev_set_mode(dev);
if (rc)
goto out;
@@ -4048,6 +4108,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "Maxtor 7V300F0", "VA111630", ATA_HORKAGE_NONCQ },
{ "ST380817AS", "3.42", ATA_HORKAGE_NONCQ },
{ "ST3160023AS", "3.42", ATA_HORKAGE_NONCQ },
+ { "OCZ CORE_SSD", "02.10104", ATA_HORKAGE_NONCQ },
/* Seagate NCQ + FLUSH CACHE firmware bug */
{ "ST31500341AS", "SD15", ATA_HORKAGE_NONCQ |
@@ -4263,9 +4324,9 @@ static int cable_is_40wire(struct ata_port *ap)
* - if you have a non detect capable drive you don't want it
* to colour the choice
*/
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev) && !ata_is_40wire(dev))
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!ata_is_40wire(dev))
return 0;
}
}
@@ -4672,7 +4733,6 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
/**
* ata_qc_new_init - Request an available ATA command, and initialize it
* @dev: Device from whom we request an available command structure
- * @tag: command tag
*
* LOCKING:
* None.
@@ -5218,7 +5278,7 @@ static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
}
ap->pflags |= ATA_PFLAG_PM_PENDING;
- __ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, HOST_FIRST) {
link->eh_info.action |= action;
link->eh_info.flags |= ehi_flags;
}
@@ -6047,8 +6107,6 @@ int ata_host_activate(struct ata_host *host, int irq,
static void ata_port_detach(struct ata_port *ap)
{
unsigned long flags;
- struct ata_link *link;
- struct ata_device *dev;
if (!ap->ops->error_handler)
goto skip_eh;
@@ -6056,28 +6114,15 @@ static void ata_port_detach(struct ata_port *ap)
/* tell EH we're leaving & flush EH */
spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_UNLOADING;
+ ata_port_schedule_eh(ap);
spin_unlock_irqrestore(ap->lock, flags);
+ /* wait till EH commits suicide */
ata_port_wait_eh(ap);
- /* EH is now guaranteed to see UNLOADING - EH context belongs
- * to us. Restore SControl and disable all existing devices.
- */
- __ata_port_for_each_link(link, ap) {
- sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
- ata_link_for_each_dev(dev, link)
- ata_dev_disable(dev);
- }
+ /* it better be dead now */
+ WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED));
- /* Final freeze & EH. All in-flight commands are aborted. EH
- * will be skipped and retrials will be terminated with bad
- * target.
- */
- spin_lock_irqsave(ap->lock, flags);
- ata_port_freeze(ap); /* won't be thawed */
- spin_unlock_irqrestore(ap->lock, flags);
-
- ata_port_wait_eh(ap);
cancel_rearming_delayed_work(&ap->hotplug_task);
skip_eh:
@@ -6528,7 +6573,8 @@ EXPORT_SYMBOL_GPL(ata_base_port_ops);
EXPORT_SYMBOL_GPL(sata_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_ops);
EXPORT_SYMBOL_GPL(ata_dummy_port_info);
-EXPORT_SYMBOL_GPL(__ata_port_next_link);
+EXPORT_SYMBOL_GPL(ata_link_next);
+EXPORT_SYMBOL_GPL(ata_dev_next);
EXPORT_SYMBOL_GPL(ata_std_bios_param);
EXPORT_SYMBOL_GPL(ata_host_init);
EXPORT_SYMBOL_GPL(ata_host_alloc);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 32da9a93ce44..8147a8386370 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -422,7 +422,7 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
if (!dev) {
ehi->action &= ~action;
- ata_link_for_each_dev(tdev, link)
+ ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] &= ~action;
} else {
/* doesn't make sense for port-wide EH actions */
@@ -430,7 +430,7 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
/* break ehi->action into ehi->dev_action */
if (ehi->action & action) {
- ata_link_for_each_dev(tdev, link)
+ ata_for_each_dev(tdev, link, ALL)
ehi->dev_action[tdev->devno] |=
ehi->action & action;
ehi->action &= ~action;
@@ -491,6 +491,31 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
return ret;
}
+static void ata_eh_unload(struct ata_port *ap)
+{
+ struct ata_link *link;
+ struct ata_device *dev;
+ unsigned long flags;
+
+ /* Restore SControl IPM and SPD for the next driver and
+ * disable attached devices.
+ */
+ ata_for_each_link(link, ap, PMP_FIRST) {
+ sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
+ ata_for_each_dev(dev, link, ALL)
+ ata_dev_disable(dev);
+ }
+
+ /* freeze and set UNLOADED */
+ spin_lock_irqsave(ap->lock, flags);
+
+ ata_port_freeze(ap); /* won't be thawed */
+ ap->pflags &= ~ATA_PFLAG_EH_PENDING; /* clear pending from freeze */
+ ap->pflags |= ATA_PFLAG_UNLOADED;
+
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
/**
* ata_scsi_error - SCSI layer error handler callback
* @host: SCSI host on which error occurred
@@ -592,7 +617,7 @@ void ata_scsi_error(struct Scsi_Host *host)
/* fetch & clear EH info */
spin_lock_irqsave(ap->lock, flags);
- __ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, HOST_FIRST) {
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
@@ -600,12 +625,9 @@ void ata_scsi_error(struct Scsi_Host *host)
link->eh_context.i = link->eh_info;
memset(&link->eh_info, 0, sizeof(link->eh_info));
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
int devno = dev->devno;
- if (!ata_dev_enabled(dev))
- continue;
-
ehc->saved_xfer_mode[devno] = dev->xfer_mode;
if (ata_ncq_enabled(dev))
ehc->saved_ncq_enabled |= 1 << devno;
@@ -621,8 +643,13 @@ void ata_scsi_error(struct Scsi_Host *host)
/* invoke EH, skip if unloading or suspended */
if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
ap->ops->error_handler(ap);
- else
+ else {
+ /* if unloading, commence suicide */
+ if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
+ !(ap->pflags & ATA_PFLAG_UNLOADED))
+ ata_eh_unload(ap);
ata_eh_finish(ap);
+ }
/* process port suspend request */
ata_eh_handle_port_suspend(ap);
@@ -644,7 +671,7 @@ void ata_scsi_error(struct Scsi_Host *host)
}
/* this run is complete, make sure EH info is clear */
- __ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, HOST_FIRST)
memset(&link->eh_info, 0, sizeof(link->eh_info));
/* Clear host_eh_scheduled while holding ap->lock such
@@ -1025,7 +1052,7 @@ int sata_async_notification(struct ata_port *ap)
struct ata_link *link;
/* check and notify ATAPI AN */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(sntf & (1 << link->pmp)))
continue;
@@ -2005,7 +2032,7 @@ void ata_eh_autopsy(struct ata_port *ap)
{
struct ata_link *link;
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
ata_eh_link_autopsy(link);
/* Handle the frigging slave link. Autopsy is done similarly
@@ -2219,7 +2246,7 @@ void ata_eh_report(struct ata_port *ap)
{
struct ata_link *link;
- __ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, HOST_FIRST)
ata_eh_link_report(link);
}
@@ -2230,7 +2257,7 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
struct ata_device *dev;
if (clear_classes)
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_UNKNOWN;
return reset(link, classes, deadline);
@@ -2294,7 +2321,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* If we issue an SRST then an ATA drive (not ATAPI)
* may change configuration and be in PIO0 timing. If
* we do a hard reset (or are coming from power on)
@@ -2355,7 +2382,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
"port disabled. ignoring.\n");
ehc->i.action &= ~ATA_EH_RESET;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
rc = 0;
@@ -2369,7 +2396,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
* bang classes and return.
*/
if (reset && !(ehc->i.action & ATA_EH_RESET)) {
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
classes[dev->devno] = ATA_DEV_NONE;
rc = 0;
goto out;
@@ -2454,7 +2481,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
/*
* Post-reset processing
*/
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* After the reset, the device state is PIO 0 and the
* controller state is undefined. Reset also wakes up
* drives from sleeping mode.
@@ -2510,7 +2537,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
* can be reliably detected and retried.
*/
nr_unknown = 0;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
/* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */
if (classes[dev->devno] == ATA_DEV_UNKNOWN) {
classes[dev->devno] = ATA_DEV_NONE;
@@ -2619,8 +2646,8 @@ static inline void ata_eh_pull_park_action(struct ata_port *ap)
spin_lock_irqsave(ap->lock, flags);
INIT_COMPLETION(ap->park_req_pending);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
struct ata_eh_info *ehi = &link->eh_info;
link->eh_context.i.dev_action[dev->devno] |=
@@ -2675,7 +2702,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
* be done backwards such that PDIAG- is released by the slave
* device before the master device is identified.
*/
- ata_link_for_each_dev_reverse(dev, link) {
+ ata_for_each_dev(dev, link, ALL_REVERSE) {
unsigned int action = ata_eh_dev_action(dev);
unsigned int readid_flags = 0;
@@ -2744,7 +2771,7 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
/* Configure new devices forward such that user doesn't see
* device detection messages backwards.
*/
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (!(new_mask & (1 << dev->devno)) ||
dev->class == ATA_DEV_PMP)
continue;
@@ -2793,10 +2820,7 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
int rc;
/* if data transfer is verified, clear DUBIOUS_XFER on ering top */
- ata_link_for_each_dev(dev, link) {
- if (!ata_dev_enabled(dev))
- continue;
-
+ ata_for_each_dev(dev, link, ENABLED) {
if (!(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) {
struct ata_ering_entry *ent;
@@ -2813,14 +2837,11 @@ int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
rc = ata_do_set_mode(link, r_failed_dev);
/* if transfer mode has changed, set DUBIOUS_XFER on device */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct ata_eh_context *ehc = &link->eh_context;
u8 saved_xfer_mode = ehc->saved_xfer_mode[dev->devno];
u8 saved_ncq = !!(ehc->saved_ncq_enabled & (1 << dev->devno));
- if (!ata_dev_enabled(dev))
- continue;
-
if (dev->xfer_mode != saved_xfer_mode ||
ata_ncq_enabled(dev) != saved_ncq)
dev->flags |= ATA_DFLAG_DUBIOUS_XFER;
@@ -2881,9 +2902,8 @@ static int ata_link_nr_enabled(struct ata_link *link)
struct ata_device *dev;
int cnt = 0;
- ata_link_for_each_dev(dev, link)
- if (ata_dev_enabled(dev))
- cnt++;
+ ata_for_each_dev(dev, link, ENABLED)
+ cnt++;
return cnt;
}
@@ -2892,7 +2912,7 @@ static int ata_link_nr_vacant(struct ata_link *link)
struct ata_device *dev;
int cnt = 0;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
if (dev->class == ATA_DEV_UNKNOWN)
cnt++;
return cnt;
@@ -2918,7 +2938,7 @@ static int ata_eh_skip_recovery(struct ata_link *link)
return 0;
/* skip if class codes for all vacant slots are ATA_DEV_NONE */
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (dev->class == ATA_DEV_UNKNOWN &&
ehc->classes[dev->devno] != ATA_DEV_NONE)
return 0;
@@ -3026,7 +3046,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
DPRINTK("ENTER\n");
/* prep for recovery */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* re-enable link? */
@@ -3038,7 +3058,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
}
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (link->flags & ATA_LFLAG_NO_RETRY)
ehc->tries[dev->devno] = 1;
else
@@ -3068,19 +3088,19 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
goto out;
/* prep for EH */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* skip EH if possible. */
if (ata_eh_skip_recovery(link))
ehc->i.action = 0;
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;
}
/* reset */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
if (!(ehc->i.action & ATA_EH_RESET))
@@ -3105,8 +3125,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_eh_pull_park_action(ap);
deadline = jiffies;
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
struct ata_eh_context *ehc = &link->eh_context;
unsigned long tmp;
@@ -3134,8 +3154,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
deadline = wait_for_completion_timeout(&ap->park_req_pending,
deadline - now);
} while (deadline);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
if (!(link->eh_context.unloaded_mask &
(1 << dev->devno)))
continue;
@@ -3146,7 +3166,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
}
/* the rest */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
/* revalidate existing devices and attach new ones */
@@ -3172,7 +3192,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
* disrupting the current users of the device.
*/
if (ehc->i.flags & ATA_EHI_DID_RESET) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (dev->class != ATA_DEV_ATAPI)
continue;
rc = atapi_eh_clear_ua(dev);
@@ -3183,7 +3203,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
/* configure link power saving */
if (ehc->i.action & ATA_EH_LPM)
- ata_link_for_each_dev(dev, link)
+ ata_for_each_dev(dev, link, ALL)
ata_dev_enable_pm(dev, ap->pm_policy);
/* this link is okay now */
@@ -3288,7 +3308,7 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
rc = ata_eh_recover(ap, prereset, softreset, hardreset, postreset,
NULL);
if (rc) {
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
}
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index b65db309c181..98ca07a2db87 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -321,7 +321,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
if (vendor == 0x1095 && devid == 0x3726) {
/* sil3726 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* Class code report is unreliable and SRST
* times out under certain configurations.
*/
@@ -336,7 +336,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
}
} else if (vendor == 0x1095 && devid == 0x4723) {
/* sil4723 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* class code report is unreliable */
if (link->pmp < 2)
link->flags |= ATA_LFLAG_ASSUME_ATA;
@@ -348,7 +348,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
}
} else if (vendor == 0x1095 && devid == 0x4726) {
/* sil4726 quirks */
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
/* Class code report is unreliable and SRST
* times out under certain configurations.
* Config device can be at port 0 or 5 and
@@ -450,7 +450,7 @@ int sata_pmp_attach(struct ata_device *dev)
if (ap->ops->pmp_attach)
ap->ops->pmp_attach(ap);
- ata_port_for_each_link(tlink, ap)
+ ata_for_each_link(tlink, ap, EDGE)
sata_link_init_spd(tlink);
ata_acpi_associate_sata_port(ap);
@@ -487,7 +487,7 @@ static void sata_pmp_detach(struct ata_device *dev)
if (ap->ops->pmp_detach)
ap->ops->pmp_detach(ap);
- ata_port_for_each_link(tlink, ap)
+ ata_for_each_link(tlink, ap, EDGE)
ata_eh_detach_dev(tlink->device);
spin_lock_irqsave(ap->lock, flags);
@@ -700,7 +700,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
}
/* PMP is reset, SErrors cannot be trusted, scan all */
- ata_port_for_each_link(tlink, ap) {
+ ata_for_each_link(tlink, ap, EDGE) {
struct ata_eh_context *ehc = &tlink->eh_context;
ehc->i.probe_mask |= ATA_ALL_DEVICES;
@@ -768,7 +768,7 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
spin_lock_irqsave(ap->lock, flags);
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(link->flags & ATA_LFLAG_DISABLED))
continue;
@@ -852,7 +852,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
int cnt, rc;
pmp_tries = ATA_EH_PMP_TRIES;
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
retry:
@@ -861,7 +861,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
rc = ata_eh_recover(ap, ops->prereset, ops->softreset,
ops->hardreset, ops->postreset, NULL);
if (rc) {
- ata_link_for_each_dev(dev, &ap->link)
+ ata_for_each_dev(dev, &ap->link, ALL)
ata_dev_disable(dev);
return rc;
}
@@ -870,7 +870,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
return 0;
/* new PMP online */
- ata_port_for_each_link(link, ap)
+ ata_for_each_link(link, ap, EDGE)
link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
/* fall through */
@@ -942,7 +942,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
}
cnt = 0;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
if (!(gscr_error & (1 << link->pmp)))
continue;
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 47c7afcb36f2..4040d8b53216 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -517,7 +517,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
- sensebuf, (10*HZ), 5, 0);
+ sensebuf, (10*HZ), 5, 0, NULL);
if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
u8 *desc = sensebuf + 8;
@@ -603,7 +603,7 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg)
/* Good values for timeout and retries? Values below
from scsi_ioctl_send_command() for default case... */
cmd_result = scsi_execute(scsidev, scsi_cmd, DMA_NONE, NULL, 0,
- sensebuf, (10*HZ), 5, 0);
+ sensebuf, (10*HZ), 5, 0, NULL);
if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
u8 *desc = sensebuf + 8;
@@ -3229,12 +3229,12 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
return;
repeat:
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct scsi_device *sdev;
int channel = 0, id = 0;
- if (!ata_dev_enabled(dev) || dev->sdev)
+ if (dev->sdev)
continue;
if (ata_is_host_link(link))
@@ -3255,9 +3255,9 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync)
* failure occurred, scan would have failed silently. Check
* whether all devices are attached.
*/
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev) && !dev->sdev)
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ if (!dev->sdev)
goto exit_loop;
}
}
@@ -3381,7 +3381,7 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
struct ata_port *ap = link->ap;
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
unsigned long flags;
if (!(dev->flags & ATA_DFLAG_DETACHED))
@@ -3496,7 +3496,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
if (devno == SCAN_WILD_CARD) {
struct ata_link *link;
- ata_port_for_each_link(link, ap) {
+ ata_for_each_link(link, ap, EDGE) {
struct ata_eh_info *ehi = &link->eh_info;
ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET;
@@ -3544,11 +3544,11 @@ void ata_scsi_dev_rescan(struct work_struct *work)
spin_lock_irqsave(ap->lock, flags);
- ata_port_for_each_link(link, ap) {
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ENABLED) {
struct scsi_device *sdev = dev->sdev;
- if (!ata_dev_enabled(dev) || !sdev)
+ if (!sdev)
continue;
if (scsi_device_get(sdev))
continue;
diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c
index 1266924c11f9..1050fed96b2b 100644
--- a/drivers/ata/pata_bf54x.c
+++ b/drivers/ata/pata_bf54x.c
@@ -356,7 +356,6 @@ static void bfin_set_piomode(struct ata_port *ap, struct ata_device *adev)
* bfin_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: um
- * @udma: udma mode, 0 - 6
*
* Set UDMA mode for device.
*
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 860ede526282..f828a29d7756 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -465,24 +465,22 @@ static int it821x_smart_set_mode(struct ata_link *link, struct ata_device **unus
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = XFER_PIO_0;
- dev->dma_mode = XFER_MW_DMA_0;
- /* We do need the right mode information for DMA or PIO
- and this comes from the current configuration flags */
- if (ata_id_has_dma(dev->id)) {
- ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
- dev->xfer_mode = XFER_MW_DMA_0;
- dev->xfer_shift = ATA_SHIFT_MWDMA;
- dev->flags &= ~ATA_DFLAG_PIO;
- } else {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = XFER_PIO_0;
+ dev->dma_mode = XFER_MW_DMA_0;
+ /* We do need the right mode information for DMA or PIO
+ and this comes from the current configuration flags */
+ if (ata_id_has_dma(dev->id)) {
+ ata_dev_printk(dev, KERN_INFO, "configured for DMA\n");
+ dev->xfer_mode = XFER_MW_DMA_0;
+ dev->xfer_shift = ATA_SHIFT_MWDMA;
+ dev->flags &= ~ATA_DFLAG_PIO;
+ } else {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
}
return 0;
diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index 2014253f6c88..b173c157ab00 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -30,14 +30,12 @@ static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
return 0;
}
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 930c2208640b..6c1d778b63a9 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -194,15 +194,12 @@ static int legacy_set_mode(struct ata_link *link, struct ata_device **unused)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- ata_dev_printk(dev, KERN_INFO,
- "configured for PIO\n");
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
}
return 0;
}
@@ -641,7 +638,6 @@ static void qdi6500_set_piomode(struct ata_port *ap, struct ata_device *adev)
* qdi6580dp_set_piomode - PIO setup for dual channel
* @ap: Port
* @adev: Device
- * @irq: interrupt line
*
* In dual channel mode the 6580 has one clock per channel and we have
* to software clockswitch in qc_issue.
@@ -1028,7 +1024,7 @@ static __init int legacy_init_one(struct legacy_probe *probe)
/* Nothing found means we drop the port as its probably not there */
ret = -ENODEV;
- ata_link_for_each_dev(dev, &ap->link) {
+ ata_for_each_dev(dev, &ap->link, ALL) {
if (!ata_dev_absent(dev)) {
legacy_host[probe->slot] = host;
ld->platform_dev = pdev;
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index a9e827356d06..50ae6d13078a 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -6,6 +6,9 @@
* Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com>
* Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt
*
+ * UDMA support based on patches by Freescale (Bernard Kuhn, John Rigby),
+ * Domen Puncer and Tim Yamin.
+ *
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
@@ -17,28 +20,46 @@
#include <linux/delay.h>
#include <linux/libata.h>
#include <linux/of_platform.h>
+#include <linux/types.h>
-#include <asm/types.h>
+#include <asm/cacheflush.h>
#include <asm/prom.h>
#include <asm/mpc52xx.h>
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/bestcomm_priv.h>
+#include <sysdev/bestcomm/ata.h>
#define DRV_NAME "mpc52xx_ata"
-#define DRV_VERSION "0.1.2"
-
/* Private structures used by the driver */
struct mpc52xx_ata_timings {
u32 pio1;
u32 pio2;
+ u32 mdma1;
+ u32 mdma2;
+ u32 udma1;
+ u32 udma2;
+ u32 udma3;
+ u32 udma4;
+ u32 udma5;
+ int using_udma;
};
struct mpc52xx_ata_priv {
unsigned int ipb_period;
- struct mpc52xx_ata __iomem * ata_regs;
+ struct mpc52xx_ata __iomem *ata_regs;
+ phys_addr_t ata_regs_pa;
int ata_irq;
struct mpc52xx_ata_timings timings[2];
int csel;
+
+ /* DMA */
+ struct bcom_task *dmatsk;
+ const struct udmaspec *udmaspec;
+ const struct mdmaspec *mdmaspec;
+ int mpc52xx_ata_dma_last_write;
+ int waiting_for_dma;
};
@@ -53,6 +74,107 @@ static const int ataspec_ta[5] = { 35, 35, 35, 35, 35};
#define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c)))
+/* ======================================================================== */
+
+/* ATAPI-4 MDMA specs (in clocks) */
+struct mdmaspec {
+ u32 t0M;
+ u32 td;
+ u32 th;
+ u32 tj;
+ u32 tkw;
+ u32 tm;
+ u32 tn;
+};
+
+static const struct mdmaspec mdmaspec66[3] = {
+ { .t0M = 32, .td = 15, .th = 2, .tj = 2, .tkw = 15, .tm = 4, .tn = 1 },
+ { .t0M = 10, .td = 6, .th = 1, .tj = 1, .tkw = 4, .tm = 2, .tn = 1 },
+ { .t0M = 8, .td = 5, .th = 1, .tj = 1, .tkw = 2, .tm = 2, .tn = 1 },
+};
+
+static const struct mdmaspec mdmaspec132[3] = {
+ { .t0M = 64, .td = 29, .th = 3, .tj = 3, .tkw = 29, .tm = 7, .tn = 2 },
+ { .t0M = 20, .td = 11, .th = 2, .tj = 1, .tkw = 7, .tm = 4, .tn = 1 },
+ { .t0M = 16, .td = 10, .th = 2, .tj = 1, .tkw = 4, .tm = 4, .tn = 1 },
+};
+
+/* ATAPI-4 UDMA specs (in clocks) */
+struct udmaspec {
+ u32 tcyc;
+ u32 t2cyc;
+ u32 tds;
+ u32 tdh;
+ u32 tdvs;
+ u32 tdvh;
+ u32 tfs;
+ u32 tli;
+ u32 tmli;
+ u32 taz;
+ u32 tzah;
+ u32 tenv;
+ u32 tsr;
+ u32 trfs;
+ u32 trp;
+ u32 tack;
+ u32 tss;
+};
+
+static const struct udmaspec udmaspec66[6] = {
+ { .tcyc = 8, .t2cyc = 16, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
+ .tfs = 16, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 3, .trfs = 5, .trp = 11, .tack = 2, .tss = 4,
+ },
+ { .tcyc = 5, .t2cyc = 11, .tds = 1, .tdh = 1, .tdvs = 4, .tdvh = 1,
+ .tfs = 14, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 5, .trp = 9, .tack = 2, .tss = 4,
+ },
+ { .tcyc = 4, .t2cyc = 8, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
+ .tfs = 12, .tli = 10, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
+ },
+ { .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 2, .tdvh = 1,
+ .tfs = 9, .tli = 7, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
+ },
+ { .tcyc = 2, .t2cyc = 4, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+ .tfs = 8, .tli = 8, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 4, .trp = 7, .tack = 2, .tss = 4,
+ },
+ { .tcyc = 2, .t2cyc = 2, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+ .tfs = 6, .tli = 5, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 4, .trp = 6, .tack = 2, .tss = 4,
+ },
+};
+
+static const struct udmaspec udmaspec132[6] = {
+ { .tcyc = 15, .t2cyc = 31, .tds = 2, .tdh = 1, .tdvs = 10, .tdvh = 1,
+ .tfs = 30, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+ .tsr = 7, .trfs = 10, .trp = 22, .tack = 3, .tss = 7,
+ },
+ { .tcyc = 10, .t2cyc = 21, .tds = 2, .tdh = 1, .tdvs = 7, .tdvh = 1,
+ .tfs = 27, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+ .tsr = 4, .trfs = 10, .trp = 17, .tack = 3, .tss = 7,
+ },
+ { .tcyc = 6, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 5, .tdvh = 1,
+ .tfs = 23, .tli = 20, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+ .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7,
+ },
+ { .tcyc = 7, .t2cyc = 12, .tds = 1, .tdh = 1, .tdvs = 3, .tdvh = 1,
+ .tfs = 15, .tli = 13, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+ .tsr = 3, .trfs = 8, .trp = 14, .tack = 3, .tss = 7,
+ },
+ { .tcyc = 2, .t2cyc = 5, .tds = 0, .tdh = 0, .tdvs = 1, .tdvh = 1,
+ .tfs = 16, .tli = 14, .tmli = 2, .taz = 1, .tzah = 2, .tenv = 2,
+ .tsr = 2, .trfs = 7, .trp = 13, .tack = 2, .tss = 6,
+ },
+ { .tcyc = 3, .t2cyc = 6, .tds = 1, .tdh = 1, .tdvs = 1, .tdvh = 1,
+ .tfs = 12, .tli = 10, .tmli = 3, .taz = 2, .tzah = 3, .tenv = 3,
+ .tsr = 3, .trfs = 7, .trp = 12, .tack = 3, .tss = 7,
+ },
+};
+
+/* ======================================================================== */
/* Bit definitions inside the registers */
#define MPC52xx_ATA_HOSTCONF_SMR 0x80000000UL /* State machine reset */
@@ -66,6 +188,7 @@ static const int ataspec_ta[5] = { 35, 35, 35, 35, 35};
#define MPC52xx_ATA_HOSTSTAT_WERR 0x01000000UL /* Write Error */
#define MPC52xx_ATA_FIFOSTAT_EMPTY 0x01 /* FIFO Empty */
+#define MPC52xx_ATA_FIFOSTAT_ERROR 0x40 /* FIFO Error */
#define MPC52xx_ATA_DMAMODE_WRITE 0x01 /* Write DMA */
#define MPC52xx_ATA_DMAMODE_READ 0x02 /* Read DMA */
@@ -75,6 +198,8 @@ static const int ataspec_ta[5] = { 35, 35, 35, 35, 35};
#define MPC52xx_ATA_DMAMODE_FR 0x20 /* FIFO Reset */
#define MPC52xx_ATA_DMAMODE_HUT 0x40 /* Host UDMA burst terminate */
+#define MAX_DMA_BUFFERS 128
+#define MAX_DMA_BUFFER_SIZE 0x20000u
/* Structure of the hardware registers */
struct mpc52xx_ata {
@@ -140,7 +265,6 @@ struct mpc52xx_ata {
/* MPC52xx low level hw control */
-
static int
mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio)
{
@@ -148,7 +272,7 @@ mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio)
unsigned int ipb_period = priv->ipb_period;
unsigned int t0, t1, t2_8, t2_16, t2i, t4, ta;
- if ((pio<0) || (pio>4))
+ if ((pio < 0) || (pio > 4))
return -EINVAL;
t0 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t0[pio]);
@@ -165,6 +289,43 @@ mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio)
return 0;
}
+static int
+mpc52xx_ata_compute_mdma_timings(struct mpc52xx_ata_priv *priv, int dev,
+ int speed)
+{
+ struct mpc52xx_ata_timings *t = &priv->timings[dev];
+ const struct mdmaspec *s = &priv->mdmaspec[speed];
+
+ if (speed < 0 || speed > 2)
+ return -EINVAL;
+
+ t->mdma1 = (s->t0M << 24) | (s->td << 16) | (s->tkw << 8) | (s->tm);
+ t->mdma2 = (s->th << 24) | (s->tj << 16) | (s->tn << 8);
+ t->using_udma = 0;
+
+ return 0;
+}
+
+static int
+mpc52xx_ata_compute_udma_timings(struct mpc52xx_ata_priv *priv, int dev,
+ int speed)
+{
+ struct mpc52xx_ata_timings *t = &priv->timings[dev];
+ const struct udmaspec *s = &priv->udmaspec[speed];
+
+ if (speed < 0 || speed > 2)
+ return -EINVAL;
+
+ t->udma1 = (s->t2cyc << 24) | (s->tcyc << 16) | (s->tds << 8) | s->tdh;
+ t->udma2 = (s->tdvs << 24) | (s->tdvh << 16) | (s->tfs << 8) | s->tli;
+ t->udma3 = (s->tmli << 24) | (s->taz << 16) | (s->tenv << 8) | s->tsr;
+ t->udma4 = (s->tss << 24) | (s->trfs << 16) | (s->trp << 8) | s->tack;
+ t->udma5 = (s->tzah << 24);
+ t->using_udma = 1;
+
+ return 0;
+}
+
static void
mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
{
@@ -173,14 +334,13 @@ mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device)
out_be32(&regs->pio1, timing->pio1);
out_be32(&regs->pio2, timing->pio2);
- out_be32(&regs->mdma1, 0);
- out_be32(&regs->mdma2, 0);
- out_be32(&regs->udma1, 0);
- out_be32(&regs->udma2, 0);
- out_be32(&regs->udma3, 0);
- out_be32(&regs->udma4, 0);
- out_be32(&regs->udma5, 0);
-
+ out_be32(&regs->mdma1, timing->mdma1);
+ out_be32(&regs->mdma2, timing->mdma2);
+ out_be32(&regs->udma1, timing->udma1);
+ out_be32(&regs->udma2, timing->udma2);
+ out_be32(&regs->udma3, timing->udma3);
+ out_be32(&regs->udma4, timing->udma4);
+ out_be32(&regs->udma5, timing->udma5);
priv->csel = device;
}
@@ -208,7 +368,7 @@ mpc52xx_ata_hw_init(struct mpc52xx_ata_priv *priv)
/* Set the time slot to 1us */
tslot = CALC_CLKCYC(priv->ipb_period, 1000000);
- out_be32(&regs->share_cnt, tslot << 16 );
+ out_be32(&regs->share_cnt, tslot << 16);
/* Init timings to PIO0 */
memset(priv->timings, 0x00, 2*sizeof(struct mpc52xx_ata_timings));
@@ -237,13 +397,37 @@ mpc52xx_ata_set_piomode(struct ata_port *ap, struct ata_device *adev)
rv = mpc52xx_ata_compute_pio_timings(priv, adev->devno, pio);
if (rv) {
- printk(KERN_ERR DRV_NAME
- ": Trying to select invalid PIO mode %d\n", pio);
+ dev_err(ap->dev, "error: invalid PIO mode: %d\n", pio);
+ return;
+ }
+
+ mpc52xx_ata_apply_timings(priv, adev->devno);
+}
+
+static void
+mpc52xx_ata_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+ int rv;
+
+ if (adev->dma_mode >= XFER_UDMA_0) {
+ int dma = adev->dma_mode - XFER_UDMA_0;
+ rv = mpc52xx_ata_compute_udma_timings(priv, adev->devno, dma);
+ } else {
+ int dma = adev->dma_mode - XFER_MW_DMA_0;
+ rv = mpc52xx_ata_compute_mdma_timings(priv, adev->devno, dma);
+ }
+
+ if (rv) {
+ dev_alert(ap->dev,
+ "Trying to select invalid DMA mode %d\n",
+ adev->dma_mode);
return;
}
mpc52xx_ata_apply_timings(priv, adev->devno);
}
+
static void
mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device)
{
@@ -252,7 +436,173 @@ mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device)
if (device != priv->csel)
mpc52xx_ata_apply_timings(priv, device);
- ata_sff_dev_select(ap,device);
+ ata_sff_dev_select(ap, device);
+}
+
+static int
+mpc52xx_ata_build_dmatable(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+ struct bcom_ata_bd *bd;
+ unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE), si;
+ struct scatterlist *sg;
+ int count = 0;
+
+ if (read)
+ bcom_ata_rx_prepare(priv->dmatsk);
+ else
+ bcom_ata_tx_prepare(priv->dmatsk);
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ dma_addr_t cur_addr = sg_dma_address(sg);
+ u32 cur_len = sg_dma_len(sg);
+
+ while (cur_len) {
+ unsigned int tc = min(cur_len, MAX_DMA_BUFFER_SIZE);
+ bd = (struct bcom_ata_bd *)
+ bcom_prepare_next_buffer(priv->dmatsk);
+
+ if (read) {
+ bd->status = tc;
+ bd->src_pa = (__force u32) priv->ata_regs_pa +
+ offsetof(struct mpc52xx_ata, fifo_data);
+ bd->dst_pa = (__force u32) cur_addr;
+ } else {
+ bd->status = tc;
+ bd->src_pa = (__force u32) cur_addr;
+ bd->dst_pa = (__force u32) priv->ata_regs_pa +
+ offsetof(struct mpc52xx_ata, fifo_data);
+ }
+
+ bcom_submit_next_buffer(priv->dmatsk, NULL);
+
+ cur_addr += tc;
+ cur_len -= tc;
+ count++;
+
+ if (count > MAX_DMA_BUFFERS) {
+ dev_alert(ap->dev, "dma table"
+ "too small\n");
+ goto use_pio_instead;
+ }
+ }
+ }
+ return 1;
+
+ use_pio_instead:
+ bcom_ata_reset_bd(priv->dmatsk);
+ return 0;
+}
+
+static void
+mpc52xx_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+ struct mpc52xx_ata __iomem *regs = priv->ata_regs;
+
+ unsigned int read = !(qc->tf.flags & ATA_TFLAG_WRITE);
+ u8 dma_mode;
+
+ if (!mpc52xx_ata_build_dmatable(qc))
+ dev_alert(ap->dev, "%s: %i, return 1?\n",
+ __func__, __LINE__);
+
+ /* Check FIFO is OK... */
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+ __func__, in_8(&priv->ata_regs->fifo_status));
+
+ if (read) {
+ dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_READ |
+ MPC52xx_ATA_DMAMODE_FE;
+
+ /* Setup FIFO if direction changed */
+ if (priv->mpc52xx_ata_dma_last_write != 0) {
+ priv->mpc52xx_ata_dma_last_write = 0;
+
+ /* Configure FIFO with granularity to 7 */
+ out_8(&regs->fifo_control, 7);
+ out_be16(&regs->fifo_alarm, 128);
+
+ /* Set FIFO Reset bit (FR) */
+ out_8(&regs->dma_mode, MPC52xx_ATA_DMAMODE_FR);
+ }
+ } else {
+ dma_mode = MPC52xx_ATA_DMAMODE_IE | MPC52xx_ATA_DMAMODE_WRITE;
+
+ /* Setup FIFO if direction changed */
+ if (priv->mpc52xx_ata_dma_last_write != 1) {
+ priv->mpc52xx_ata_dma_last_write = 1;
+
+ /* Configure FIFO with granularity to 4 */
+ out_8(&regs->fifo_control, 4);
+ out_be16(&regs->fifo_alarm, 128);
+ }
+ }
+
+ if (priv->timings[qc->dev->devno].using_udma)
+ dma_mode |= MPC52xx_ATA_DMAMODE_UDMA;
+
+ out_8(&regs->dma_mode, dma_mode);
+ priv->waiting_for_dma = ATA_DMA_ACTIVE;
+
+ ata_wait_idle(ap);
+ ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void
+mpc52xx_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+ bcom_set_task_auto_start(priv->dmatsk->tasknum, priv->dmatsk->tasknum);
+ bcom_enable(priv->dmatsk);
+}
+
+static void
+mpc52xx_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+ bcom_disable(priv->dmatsk);
+ bcom_ata_reset_bd(priv->dmatsk);
+ priv->waiting_for_dma = 0;
+
+ /* Check FIFO is OK... */
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR)
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+ __func__, in_8(&priv->ata_regs->fifo_status));
+}
+
+static u8
+mpc52xx_bmdma_status(struct ata_port *ap)
+{
+ struct mpc52xx_ata_priv *priv = ap->host->private_data;
+
+ /* Check FIFO is OK... */
+ if (in_8(&priv->ata_regs->fifo_status) & MPC52xx_ATA_FIFOSTAT_ERROR) {
+ dev_alert(ap->dev, "%s: FIFO error detected: 0x%02x!\n",
+ __func__, in_8(&priv->ata_regs->fifo_status));
+ return priv->waiting_for_dma | ATA_DMA_ERR;
+ }
+
+ return priv->waiting_for_dma;
+}
+
+static irqreturn_t
+mpc52xx_ata_task_irq(int irq, void *vpriv)
+{
+ struct mpc52xx_ata_priv *priv = vpriv;
+ while (bcom_buffer_done(priv->dmatsk))
+ bcom_retrieve_buffer(priv->dmatsk, NULL, NULL);
+
+ priv->waiting_for_dma |= ATA_DMA_INTR;
+
+ return IRQ_HANDLED;
}
static struct scsi_host_template mpc52xx_ata_sht = {
@@ -262,14 +612,18 @@ static struct scsi_host_template mpc52xx_ata_sht = {
static struct ata_port_operations mpc52xx_ata_port_ops = {
.inherits = &ata_sff_port_ops,
.sff_dev_select = mpc52xx_ata_dev_select,
- .cable_detect = ata_cable_40wire,
.set_piomode = mpc52xx_ata_set_piomode,
- .post_internal_cmd = ATA_OP_NULL,
+ .set_dmamode = mpc52xx_ata_set_dmamode,
+ .bmdma_setup = mpc52xx_bmdma_setup,
+ .bmdma_start = mpc52xx_bmdma_start,
+ .bmdma_stop = mpc52xx_bmdma_stop,
+ .bmdma_status = mpc52xx_bmdma_status,
+ .qc_prep = ata_noop_qc_prep,
};
static int __devinit
mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv,
- unsigned long raw_ata_regs)
+ unsigned long raw_ata_regs, int mwdma_mask, int udma_mask)
{
struct ata_host *host;
struct ata_port *ap;
@@ -281,9 +635,9 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv,
ap = host->ports[0];
ap->flags |= ATA_FLAG_SLAVE_POSS;
- ap->pio_mask = 0x1f; /* Up to PIO4 */
- ap->mwdma_mask = 0x00; /* No MWDMA */
- ap->udma_mask = 0x00; /* No UDMA */
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = mwdma_mask;
+ ap->udma_mask = udma_mask;
ap->ops = &mpc52xx_ata_port_ops;
host->private_data = priv;
@@ -330,89 +684,139 @@ mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match)
{
unsigned int ipb_freq;
struct resource res_mem;
- int ata_irq;
+ int ata_irq = 0;
struct mpc52xx_ata __iomem *ata_regs;
- struct mpc52xx_ata_priv *priv;
- int rv;
+ struct mpc52xx_ata_priv *priv = NULL;
+ int rv, ret, task_irq = 0;
+ int mwdma_mask = 0, udma_mask = 0;
+ const __be32 *prop;
+ int proplen;
+ struct bcom_task *dmatsk = NULL;
/* Get ipb frequency */
ipb_freq = mpc52xx_find_ipb_freq(op->node);
if (!ipb_freq) {
- printk(KERN_ERR DRV_NAME ": "
- "Unable to find IPB Bus frequency\n" );
+ dev_err(&op->dev, "could not determine IPB bus frequency\n");
return -ENODEV;
}
- /* Get IRQ and register */
+ /* Get device base address from device tree, request the region
+ * and ioremap it. */
rv = of_address_to_resource(op->node, 0, &res_mem);
if (rv) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while parsing device node resource\n" );
+ dev_err(&op->dev, "could not determine device base address\n");
return rv;
}
- ata_irq = irq_of_parse_and_map(op->node, 0);
- if (ata_irq == NO_IRQ) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while mapping the irq\n");
- return -EINVAL;
- }
-
- /* Request mem region */
if (!devm_request_mem_region(&op->dev, res_mem.start,
- sizeof(struct mpc52xx_ata), DRV_NAME)) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while requesting mem region\n");
- rv = -EBUSY;
- goto err;
+ sizeof(*ata_regs), DRV_NAME)) {
+ dev_err(&op->dev, "error requesting register region\n");
+ return -EBUSY;
}
- /* Remap registers */
- ata_regs = devm_ioremap(&op->dev, res_mem.start,
- sizeof(struct mpc52xx_ata));
+ ata_regs = devm_ioremap(&op->dev, res_mem.start, sizeof(*ata_regs));
if (!ata_regs) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while mapping register set\n");
+ dev_err(&op->dev, "error mapping device registers\n");
rv = -ENOMEM;
goto err;
}
+ /*
+ * By default, all DMA modes are disabled for the MPC5200. Some
+ * boards don't have the required signals routed to make DMA work.
+ * Also, the MPC5200B has a silicon bug that causes data corruption
+ * with UDMA if it is used at the same time as the LocalPlus bus.
+ *
+ * Instead of trying to guess what modes are usable, check the
+ * ATA device tree node to find out what DMA modes work on the board.
+ * UDMA/MWDMA modes can also be forced by adding "libata.force=<mode>"
+ * to the kernel boot parameters.
+ *
+ * The MPC5200 ATA controller supports MWDMA modes 0, 1 and 2 and
+ * UDMA modes 0, 1 and 2.
+ */
+ prop = of_get_property(op->node, "mwdma-mode", &proplen);
+ if ((prop) && (proplen >= 4))
+ mwdma_mask = 0x7 & ((1 << (*prop + 1)) - 1);
+ prop = of_get_property(op->node, "udma-mode", &proplen);
+ if ((prop) && (proplen >= 4))
+ udma_mask = 0x7 & ((1 << (*prop + 1)) - 1);
+
+ ata_irq = irq_of_parse_and_map(op->node, 0);
+ if (ata_irq == NO_IRQ) {
+ dev_err(&op->dev, "error mapping irq\n");
+ return -EINVAL;
+ }
+
/* Prepare our private structure */
- priv = devm_kzalloc(&op->dev, sizeof(struct mpc52xx_ata_priv),
- GFP_ATOMIC);
+ priv = devm_kzalloc(&op->dev, sizeof(*priv), GFP_ATOMIC);
if (!priv) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while allocating private structure\n");
+ dev_err(&op->dev, "error allocating private structure\n");
rv = -ENOMEM;
goto err;
}
priv->ipb_period = 1000000000 / (ipb_freq / 1000);
priv->ata_regs = ata_regs;
+ priv->ata_regs_pa = res_mem.start;
priv->ata_irq = ata_irq;
priv->csel = -1;
+ priv->mpc52xx_ata_dma_last_write = -1;
+
+ if (ipb_freq/1000000 == 66) {
+ priv->mdmaspec = mdmaspec66;
+ priv->udmaspec = udmaspec66;
+ } else {
+ priv->mdmaspec = mdmaspec132;
+ priv->udmaspec = udmaspec132;
+ }
+
+ /* Allocate a BestComm task for DMA */
+ dmatsk = bcom_ata_init(MAX_DMA_BUFFERS, MAX_DMA_BUFFER_SIZE);
+ if (!dmatsk) {
+ dev_err(&op->dev, "bestcomm initialization failed\n");
+ rv = -ENOMEM;
+ goto err;
+ }
+
+ task_irq = bcom_get_task_irq(dmatsk);
+ ret = request_irq(task_irq, &mpc52xx_ata_task_irq, IRQF_DISABLED,
+ "ATA task", priv);
+ if (ret) {
+ dev_err(&op->dev, "error requesting DMA IRQ\n");
+ goto err;
+ }
+ priv->dmatsk = dmatsk;
/* Init the hw */
rv = mpc52xx_ata_hw_init(priv);
if (rv) {
- printk(KERN_ERR DRV_NAME ": Error during HW init\n");
+ dev_err(&op->dev, "error initializing hardware\n");
goto err;
}
/* Register ourselves to libata */
- rv = mpc52xx_ata_init_one(&op->dev, priv, res_mem.start);
+ rv = mpc52xx_ata_init_one(&op->dev, priv, res_mem.start,
+ mwdma_mask, udma_mask);
if (rv) {
- printk(KERN_ERR DRV_NAME ": "
- "Error while registering to ATA layer\n");
- return rv;
+ dev_err(&op->dev, "error registering with ATA layer\n");
+ goto err;
}
- /* Done */
return 0;
- /* Error path */
-err:
- irq_dispose_mapping(ata_irq);
+ err:
+ devm_release_mem_region(&op->dev, res_mem.start, sizeof(*ata_regs));
+ if (ata_irq)
+ irq_dispose_mapping(ata_irq);
+ if (task_irq)
+ irq_dispose_mapping(task_irq);
+ if (dmatsk)
+ bcom_ata_release(dmatsk);
+ if (ata_regs)
+ devm_iounmap(&op->dev, ata_regs);
+ if (priv)
+ devm_kfree(&op->dev, priv);
return rv;
}
@@ -420,10 +824,23 @@ static int
mpc52xx_ata_remove(struct of_device *op)
{
struct mpc52xx_ata_priv *priv;
+ int task_irq;
+ /* Deregister the ATA interface */
priv = mpc52xx_ata_remove_one(&op->dev);
+
+ /* Clean up DMA */
+ task_irq = bcom_get_task_irq(priv->dmatsk);
+ irq_dispose_mapping(task_irq);
+ bcom_ata_release(priv->dmatsk);
irq_dispose_mapping(priv->ata_irq);
+ /* Clear up IO allocations */
+ devm_iounmap(&op->dev, priv->ata_regs);
+ devm_release_mem_region(&op->dev, priv->ata_regs_pa,
+ sizeof(*priv->ata_regs));
+ devm_kfree(&op->dev, priv);
+
return 0;
}
@@ -447,7 +864,7 @@ mpc52xx_ata_resume(struct of_device *op)
rv = mpc52xx_ata_hw_init(priv);
if (rv) {
- printk(KERN_ERR DRV_NAME ": Error during HW init\n");
+ dev_err(host->dev, "error initializing hardware\n");
return rv;
}
@@ -507,5 +924,4 @@ MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
MODULE_DESCRIPTION("Freescale MPC52xx IDE/ATA libata driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(of, mpc52xx_ata_of_match);
-MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index c0dbc46a348e..2c1a91c40c1a 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -116,7 +116,6 @@ static void oldpiix_set_piomode (struct ata_port *ap, struct ata_device *adev)
* oldpiix_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: Device to program
- * @isich: True if the device is an ICH and has IOCFG registers
*
* Set MWDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 0e1c2c1134d3..e94efccaa482 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -281,7 +281,6 @@ static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long
* pdc2027x_set_piomode - Initialize host controller PATA PIO timings
* @ap: Port to configure
* @adev: um
- * @pio: PIO mode, 0 - 4
*
* Set PIO mode for device.
*
@@ -326,7 +325,6 @@ static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev)
* pdc2027x_set_dmamode - Initialize host controller PATA UDMA timings
* @ap: Port to configure
* @adev: um
- * @udma: udma mode, XFER_UDMA_0 to XFER_UDMA_6
*
* Set UDMA mode for device.
*
@@ -406,23 +404,20 @@ static int pdc2027x_set_mode(struct ata_link *link, struct ata_device **r_failed
if (rc < 0)
return rc;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
+ ata_for_each_dev(dev, link, ENABLED) {
+ pdc2027x_set_piomode(ap, dev);
- pdc2027x_set_piomode(ap, dev);
+ /*
+ * Enable prefetch if the device support PIO only.
+ */
+ if (dev->xfer_shift == ATA_SHIFT_PIO) {
+ u32 ctcr1 = ioread32(dev_mmio(ap, dev, PDC_CTCR1));
+ ctcr1 |= (1 << 25);
+ iowrite32(ctcr1, dev_mmio(ap, dev, PDC_CTCR1));
- /*
- * Enable prefetch if the device support PIO only.
- */
- if (dev->xfer_shift == ATA_SHIFT_PIO) {
- u32 ctcr1 = ioread32(dev_mmio(ap, dev, PDC_CTCR1));
- ctcr1 |= (1 << 25);
- iowrite32(ctcr1, dev_mmio(ap, dev, PDC_CTCR1));
-
- PDPRINTK("Turn on prefetch\n");
- } else {
- pdc2027x_set_dmamode(ap, dev);
- }
+ PDPRINTK("Turn on prefetch\n");
+ } else {
+ pdc2027x_set_dmamode(ap, dev);
}
}
return 0;
diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c
index 77e4e3b17f54..6afa07a37648 100644
--- a/drivers/ata/pata_platform.c
+++ b/drivers/ata/pata_platform.c
@@ -34,14 +34,12 @@ static int pata_platform_set_mode(struct ata_link *link, struct ata_device **unu
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 0b0aa452de14..695d44ae52c6 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -81,7 +81,6 @@ static void radisys_set_piomode (struct ata_port *ap, struct ata_device *adev)
* radisys_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: Device to program
- * @isich: True if the device is an ICH and has IOCFG registers
*
* Set MWDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index 7dfd1f3f6f3a..46d6bc1bf1e9 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -38,15 +38,13 @@ static int rz1000_set_mode(struct ata_link *link, struct ata_device **unused)
{
struct ata_device *dev;
- ata_link_for_each_dev(dev, link) {
- if (ata_dev_enabled(dev)) {
- /* We don't really care */
- dev->pio_mode = XFER_PIO_0;
- dev->xfer_mode = XFER_PIO_0;
- dev->xfer_shift = ATA_SHIFT_PIO;
- dev->flags |= ATA_DFLAG_PIO;
- ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
- }
+ ata_for_each_dev(dev, link, ENABLED) {
+ /* We don't really care */
+ dev->pio_mode = XFER_PIO_0;
+ dev->xfer_mode = XFER_PIO_0;
+ dev->xfer_shift = ATA_SHIFT_PIO;
+ dev->flags |= ATA_DFLAG_PIO;
+ ata_dev_printk(dev, KERN_INFO, "configured for PIO\n");
}
return 0;
}
diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c
index cf3707e516a2..d447f1cb46ec 100644
--- a/drivers/ata/pata_scc.c
+++ b/drivers/ata/pata_scc.c
@@ -210,7 +210,6 @@ static void scc_set_piomode (struct ata_port *ap, struct ata_device *adev)
* scc_set_dmamode - Initialize host controller PATA DMA timings
* @ap: Port whose timings we are configuring
* @adev: um
- * @udma: udma mode, 0 - 6
*
* Set UDMA mode for device.
*
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index 72e41c9f969b..8d2fd9dd40c7 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -138,7 +138,6 @@ static struct sv_cable_table cable_detect[] = {
/**
* serverworks_cable_detect - cable detection
* @ap: ATA port
- * @deadline: deadline jiffies for the operation
*
* Perform cable detection according to the device and subvendor
* identifications
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index e4be55e047f6..27ceb42a774b 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -112,7 +112,6 @@ static int sis_133_cable_detect(struct ata_port *ap)
/**
* sis_66_cable_detect - check for 40/80 pin
* @ap: Port
- * @deadline: deadline jiffies for the operation
*
* Perform cable detection on the UDMA66, UDMA100 and early UDMA133
* SiS IDE controllers.
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 2b24ae58b52e..86918634a4c5 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -1836,7 +1836,6 @@ static void mv_unexpected_intr(struct ata_port *ap, int edma_was_enabled)
/**
* mv_err_intr - Handle error interrupts on the port
* @ap: ATA channel to manipulate
- * @qc: affected command (non-NCQ), or NULL
*
* Most cases require a full reset of the chip's state machine,
* which also performs a COMRESET.
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 031d7b7dee34..564c142b03b0 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -46,7 +46,9 @@
#include <linux/libata.h>
#define DRV_NAME "sata_sil"
-#define DRV_VERSION "2.3"
+#define DRV_VERSION "2.4"
+
+#define SIL_DMA_BOUNDARY 0x7fffffffUL
enum {
SIL_MMIO_BAR = 5,
@@ -118,6 +120,10 @@ static void sil_dev_config(struct ata_device *dev);
static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed);
+static void sil_qc_prep(struct ata_queued_cmd *qc);
+static void sil_bmdma_setup(struct ata_queued_cmd *qc);
+static void sil_bmdma_start(struct ata_queued_cmd *qc);
+static void sil_bmdma_stop(struct ata_queued_cmd *qc);
static void sil_freeze(struct ata_port *ap);
static void sil_thaw(struct ata_port *ap);
@@ -167,13 +173,22 @@ static struct pci_driver sil_pci_driver = {
};
static struct scsi_host_template sil_sht = {
- ATA_BMDMA_SHT(DRV_NAME),
+ ATA_BASE_SHT(DRV_NAME),
+ /** These controllers support Large Block Transfer which allows
+ transfer chunks up to 2GB and which cross 64KB boundaries,
+ therefore the DMA limits are more relaxed than standard ATA SFF. */
+ .dma_boundary = SIL_DMA_BOUNDARY,
+ .sg_tablesize = ATA_MAX_PRD
};
static struct ata_port_operations sil_ops = {
.inherits = &ata_bmdma_port_ops,
.dev_config = sil_dev_config,
.set_mode = sil_set_mode,
+ .bmdma_setup = sil_bmdma_setup,
+ .bmdma_start = sil_bmdma_start,
+ .bmdma_stop = sil_bmdma_stop,
+ .qc_prep = sil_qc_prep,
.freeze = sil_freeze,
.thaw = sil_thaw,
.scr_read = sil_scr_read,
@@ -249,6 +264,83 @@ module_param(slow_down, int, 0444);
MODULE_PARM_DESC(slow_down, "Sledgehammer used to work around random problems, by limiting commands to 15 sectors (0=off, 1=on)");
+static void sil_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *mmio_base = ap->host->iomap[SIL_MMIO_BAR];
+ void __iomem *bmdma2 = mmio_base + sil_port[ap->port_no].bmdma2;
+
+ /* clear start/stop bit - can safely always write 0 */
+ iowrite8(0, bmdma2);
+
+ /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+ ata_sff_dma_pause(ap);
+}
+
+static void sil_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *bmdma = ap->ioaddr.bmdma_addr;
+
+ /* load PRD table addr. */
+ iowrite32(ap->prd_dma, bmdma + ATA_DMA_TABLE_OFS);
+
+ /* issue r/w command */
+ ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+static void sil_bmdma_start(struct ata_queued_cmd *qc)
+{
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ struct ata_port *ap = qc->ap;
+ void __iomem *mmio_base = ap->host->iomap[SIL_MMIO_BAR];
+ void __iomem *bmdma2 = mmio_base + sil_port[ap->port_no].bmdma2;
+ u8 dmactl = ATA_DMA_START;
+
+ /* set transfer direction, start host DMA transaction
+ Note: For Large Block Transfer to work, the DMA must be started
+ using the bmdma2 register. */
+ if (!rw)
+ dmactl |= ATA_DMA_WR;
+ iowrite8(dmactl, bmdma2);
+}
+
+/* The way God intended PCI IDE scatter/gather lists to look and behave... */
+static void sil_fill_sg(struct ata_queued_cmd *qc)
+{
+ struct scatterlist *sg;
+ struct ata_port *ap = qc->ap;
+ struct ata_prd *prd, *last_prd = NULL;
+ unsigned int si;
+
+ prd = &ap->prd[0];
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ /* Note h/w doesn't support 64-bit, so we unconditionally
+ * truncate dma_addr_t to u32.
+ */
+ u32 addr = (u32) sg_dma_address(sg);
+ u32 sg_len = sg_dma_len(sg);
+
+ prd->addr = cpu_to_le32(addr);
+ prd->flags_len = cpu_to_le32(sg_len);
+ VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, sg_len);
+
+ last_prd = prd;
+ prd++;
+ }
+
+ if (likely(last_prd))
+ last_prd->flags_len |= cpu_to_le32(ATA_PRD_EOT);
+}
+
+static void sil_qc_prep(struct ata_queued_cmd *qc)
+{
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ sil_fill_sg(qc);
+}
+
static unsigned char sil_get_device_cache_line(struct pci_dev *pdev)
{
u8 cache_line = 0;
@@ -278,7 +370,7 @@ static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed)
if (rc)
return rc;
- ata_link_for_each_dev(dev, link) {
+ ata_for_each_dev(dev, link, ALL) {
if (!ata_dev_enabled(dev))
dev_mode[dev->devno] = 0; /* PIO0/1/2 */
else if (dev->flags & ATA_DFLAG_PIO)
diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig
index f197a193633a..191b85e857e0 100644
--- a/drivers/atm/Kconfig
+++ b/drivers/atm/Kconfig
@@ -391,4 +391,10 @@ config ATM_HE_USE_SUNI
Support for the S/UNI-Ultra and S/UNI-622 found in the ForeRunner
HE cards. This driver provides carrier detection some statistics.
+config ATM_SOLOS
+ tristate "Solos ADSL2+ PCI Multiport card driver"
+ depends on PCI
+ help
+ Support for the Solos multiport ADSL2+ card.
+
endif # ATM
diff --git a/drivers/atm/Makefile b/drivers/atm/Makefile
index 0bfb31748ecf..62c3cc1075ae 100644
--- a/drivers/atm/Makefile
+++ b/drivers/atm/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_ATM_IA) += iphase.o suni.o
obj-$(CONFIG_ATM_FORE200E) += fore_200e.o
obj-$(CONFIG_ATM_ENI) += eni.o suni.o
obj-$(CONFIG_ATM_IDT77252) += idt77252.o
+obj-$(CONFIG_ATM_SOLOS) += solos-pci.o
ifeq ($(CONFIG_ATM_NICSTAR_USE_SUNI),y)
obj-$(CONFIG_ATM_NICSTAR) += suni.o
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
new file mode 100644
index 000000000000..72fc0f799a64
--- /dev/null
+++ b/drivers/atm/solos-pci.c
@@ -0,0 +1,790 @@
+/*
+ * Driver for the Solos PCI ADSL2+ card, designed to support Linux by
+ * Traverse Technologies -- http://www.traverse.com.au/
+ * Xrio Limited -- http://www.xrio.com/
+ *
+ *
+ * Copyright © 2008 Traverse Technologies
+ * Copyright © 2008 Intel Corporation
+ *
+ * Authors: Nathan Williams <nathan@traverse.com.au>
+ * David Woodhouse <dwmw2@infradead.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 DEBUG
+#define VERBOSE_DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/skbuff.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+
+#define VERSION "0.04"
+#define PTAG "solos-pci"
+
+#define CONFIG_RAM_SIZE 128
+#define FLAGS_ADDR 0x7C
+#define IRQ_EN_ADDR 0x78
+#define FPGA_VER 0x74
+#define IRQ_CLEAR 0x70
+#define BUG_FLAG 0x6C
+
+#define DATA_RAM_SIZE 32768
+#define BUF_SIZE 4096
+
+#define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2)
+#define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE)
+
+static int debug = 0;
+static int atmdebug = 0;
+
+struct pkt_hdr {
+ __le16 size;
+ __le16 vpi;
+ __le16 vci;
+ __le16 type;
+};
+
+#define PKT_DATA 0
+#define PKT_COMMAND 1
+#define PKT_POPEN 3
+#define PKT_PCLOSE 4
+
+struct solos_card {
+ void __iomem *config_regs;
+ void __iomem *buffers;
+ int nr_ports;
+ struct pci_dev *dev;
+ struct atm_dev *atmdev[4];
+ struct tasklet_struct tlet;
+ spinlock_t tx_lock;
+ spinlock_t tx_queue_lock;
+ spinlock_t cli_queue_lock;
+ struct sk_buff_head tx_queue[4];
+ struct sk_buff_head cli_queue[4];
+};
+
+#define SOLOS_CHAN(atmdev) ((int)(unsigned long)(atmdev)->phy_data)
+
+MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>");
+MODULE_DESCRIPTION("Solos PCI driver");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(debug, "Enable Loopback");
+MODULE_PARM_DESC(atmdebug, "Print ATM data");
+module_param(debug, int, 0444);
+module_param(atmdebug, int, 0444);
+
+static int opens;
+
+static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
+ struct atm_vcc *vcc);
+static int fpga_tx(struct solos_card *);
+static irqreturn_t solos_irq(int irq, void *dev_id);
+static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci);
+static int list_vccs(int vci);
+static int atm_init(struct solos_card *);
+static void atm_remove(struct solos_card *);
+static int send_command(struct solos_card *card, int dev, const char *buf, size_t size);
+static void solos_bh(unsigned long);
+static int print_buffer(struct sk_buff *buf);
+
+static inline void solos_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ if (vcc->pop)
+ vcc->pop(vcc, skb);
+ else
+ dev_kfree_skb_any(skb);
+}
+
+static ssize_t console_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ struct sk_buff *skb;
+
+ spin_lock(&card->cli_queue_lock);
+ skb = skb_dequeue(&card->cli_queue[SOLOS_CHAN(atmdev)]);
+ spin_unlock(&card->cli_queue_lock);
+ if(skb == NULL)
+ return sprintf(buf, "No data.\n");
+
+ memcpy(buf, skb->data, skb->len);
+ dev_dbg(&card->dev->dev, "len: %d\n", skb->len);
+
+ kfree_skb(skb);
+ return skb->len;
+}
+
+static int send_command(struct solos_card *card, int dev, const char *buf, size_t size)
+{
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+// dev_dbg(&card->dev->dev, "size: %d\n", size);
+
+ if (size > (BUF_SIZE - sizeof(*header))) {
+ dev_dbg(&card->dev->dev, "Command is too big. Dropping request\n");
+ return 0;
+ }
+ skb = alloc_skb(size + sizeof(*header), GFP_ATOMIC);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in send_command()\n");
+ return 0;
+ }
+
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(size);
+ header->vpi = cpu_to_le16(0);
+ header->vci = cpu_to_le16(0);
+ header->type = cpu_to_le16(PKT_COMMAND);
+
+ memcpy(skb_put(skb, size), buf, size);
+
+ fpga_queue(card, dev, skb, NULL);
+
+ return 0;
+}
+
+static ssize_t console_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct atm_dev *atmdev = container_of(dev, struct atm_dev, class_dev);
+ struct solos_card *card = atmdev->dev_data;
+ int err;
+
+ err = send_command(card, SOLOS_CHAN(atmdev), buf, count);
+
+ return err?:count;
+}
+
+static DEVICE_ATTR(console, 0644, console_show, console_store);
+
+static irqreturn_t solos_irq(int irq, void *dev_id)
+{
+ struct solos_card *card = dev_id;
+ int handled = 1;
+
+ //ACK IRQ
+ iowrite32(0, card->config_regs + IRQ_CLEAR);
+ //Disable IRQs from FPGA
+ iowrite32(0, card->config_regs + IRQ_EN_ADDR);
+
+ /* If we only do it when the device is open, we lose console
+ messages */
+ if (1 || opens)
+ tasklet_schedule(&card->tlet);
+
+ //Enable IRQs from FPGA
+ iowrite32(1, card->config_regs + IRQ_EN_ADDR);
+ return IRQ_RETVAL(handled);
+}
+
+void solos_bh(unsigned long card_arg)
+{
+ struct solos_card *card = (void *)card_arg;
+ int port;
+ uint32_t card_flags;
+ uint32_t tx_mask;
+ uint32_t rx_done = 0;
+
+ card_flags = ioread32(card->config_regs + FLAGS_ADDR);
+
+ /* The TX bits are set if the channel is busy; clear if not. We want to
+ invoke fpga_tx() unless _all_ the bits for active channels are set */
+ tx_mask = (1 << card->nr_ports) - 1;
+ if ((card_flags & tx_mask) != tx_mask)
+ fpga_tx(card);
+
+ for (port = 0; port < card->nr_ports; port++) {
+ if (card_flags & (0x10 << port)) {
+ struct pkt_hdr header;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ int size;
+
+ rx_done |= 0x10 << port;
+
+ memcpy_fromio(&header, RX_BUF(card, port), sizeof(header));
+
+ size = le16_to_cpu(header.size);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff for RX\n");
+ continue;
+ }
+
+ memcpy_fromio(skb_put(skb, size),
+ RX_BUF(card, port) + sizeof(header),
+ size);
+
+ if (atmdebug) {
+ dev_info(&card->dev->dev, "Received: device %d\n", port);
+ dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n",
+ size, le16_to_cpu(header.vpi),
+ le16_to_cpu(header.vci));
+ print_buffer(skb);
+ }
+
+ switch (le16_to_cpu(header.type)) {
+ case PKT_DATA:
+ vcc = find_vcc(card->atmdev[port], le16_to_cpu(header.vpi),
+ le16_to_cpu(header.vci));
+ if (!vcc) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Received packet for unknown VCI.VPI %d.%d on port %d\n",
+ le16_to_cpu(header.vci), le16_to_cpu(header.vpi),
+ port);
+ continue;
+ }
+ atm_charge(vcc, skb->truesize);
+ vcc->push(vcc, skb);
+ atomic_inc(&vcc->stats->rx);
+ break;
+
+ case PKT_COMMAND:
+ default: /* FIXME: Not really, surely? */
+ spin_lock(&card->cli_queue_lock);
+ if (skb_queue_len(&card->cli_queue[port]) > 10) {
+ if (net_ratelimit())
+ dev_warn(&card->dev->dev, "Dropping console response on port %d\n",
+ port);
+ } else
+ skb_queue_tail(&card->cli_queue[port], skb);
+ spin_unlock(&card->cli_queue_lock);
+ break;
+ }
+ }
+ }
+ if (rx_done)
+ iowrite32(rx_done, card->config_regs + FLAGS_ADDR);
+
+ return;
+}
+
+static struct atm_vcc *find_vcc(struct atm_dev *dev, short vpi, int vci)
+{
+ struct hlist_head *head;
+ struct atm_vcc *vcc = NULL;
+ struct hlist_node *node;
+ struct sock *s;
+
+ read_lock(&vcc_sklist_lock);
+ head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)];
+ sk_for_each(s, node, head) {
+ vcc = atm_sk(s);
+ if (vcc->dev == dev && vcc->vci == vci &&
+ vcc->vpi == vpi && vcc->qos.rxtp.traffic_class != ATM_NONE)
+ goto out;
+ }
+ vcc = NULL;
+ out:
+ read_unlock(&vcc_sklist_lock);
+ return vcc;
+}
+
+static int list_vccs(int vci)
+{
+ struct hlist_head *head;
+ struct atm_vcc *vcc;
+ struct hlist_node *node;
+ struct sock *s;
+ int num_found = 0;
+ int i;
+
+ read_lock(&vcc_sklist_lock);
+ if (vci != 0){
+ head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)];
+ sk_for_each(s, node, head) {
+ num_found ++;
+ vcc = atm_sk(s);
+ printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n",
+ vcc->dev->number,
+ vcc->vpi,
+ vcc->vci);
+ }
+ } else {
+ for(i=0; i<32; i++){
+ head = &vcc_hash[i];
+ sk_for_each(s, node, head) {
+ num_found ++;
+ vcc = atm_sk(s);
+ printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n",
+ vcc->dev->number,
+ vcc->vpi,
+ vcc->vci);
+ }
+ }
+ }
+ read_unlock(&vcc_sklist_lock);
+ return num_found;
+}
+
+
+static int popen(struct atm_vcc *vcc)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+ skb = alloc_skb(sizeof(*header), GFP_ATOMIC);
+ if (!skb && net_ratelimit()) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n");
+ return -ENOMEM;
+ }
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(sizeof(*header));
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_POPEN);
+
+ fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL);
+
+// dev_dbg(&card->dev->dev, "Open for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev));
+ set_bit(ATM_VF_ADDR, &vcc->flags); // accept the vpi / vci
+ set_bit(ATM_VF_READY, &vcc->flags);
+ list_vccs(0);
+
+ if (!opens)
+ iowrite32(1, card->config_regs + IRQ_EN_ADDR);
+
+ opens++; //count open PVCs
+
+ return 0;
+}
+
+static void pclose(struct atm_vcc *vcc)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ struct sk_buff *skb;
+ struct pkt_hdr *header;
+
+ skb = alloc_skb(sizeof(*header), GFP_ATOMIC);
+ if (!skb) {
+ dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n");
+ return;
+ }
+ header = (void *)skb_put(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(sizeof(*header));
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_PCLOSE);
+
+ fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL);
+
+// dev_dbg(&card->dev->dev, "Close for vpi %d and vci %d on interface %d\n", vcc->vpi, vcc->vci, SOLOS_CHAN(vcc->dev));
+ if (!--opens)
+ iowrite32(0, card->config_regs + IRQ_EN_ADDR);
+
+ clear_bit(ATM_VF_ADDR, &vcc->flags);
+ clear_bit(ATM_VF_READY, &vcc->flags);
+
+ return;
+}
+
+static int print_buffer(struct sk_buff *buf)
+{
+ int len,i;
+ char msg[500];
+ char item[10];
+
+ len = buf->len;
+ for (i = 0; i < len; i++){
+ if(i % 8 == 0)
+ sprintf(msg, "%02X: ", i);
+
+ sprintf(item,"%02X ",*(buf->data + i));
+ strcat(msg, item);
+ if(i % 8 == 7) {
+ sprintf(item, "\n");
+ strcat(msg, item);
+ printk(KERN_DEBUG "%s", msg);
+ }
+ }
+ if (i % 8 != 0) {
+ sprintf(item, "\n");
+ strcat(msg, item);
+ printk(KERN_DEBUG "%s", msg);
+ }
+ printk(KERN_DEBUG "\n");
+
+ return 0;
+}
+
+static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb,
+ struct atm_vcc *vcc)
+{
+ int old_len;
+
+ *(void **)skb->cb = vcc;
+
+ spin_lock(&card->tx_queue_lock);
+ old_len = skb_queue_len(&card->tx_queue[port]);
+ skb_queue_tail(&card->tx_queue[port], skb);
+ spin_unlock(&card->tx_queue_lock);
+
+ /* If TX might need to be started, do so */
+ if (!old_len)
+ fpga_tx(card);
+}
+
+static int fpga_tx(struct solos_card *card)
+{
+ uint32_t tx_pending;
+ uint32_t tx_started = 0;
+ struct sk_buff *skb;
+ struct atm_vcc *vcc;
+ unsigned char port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->tx_lock, flags);
+
+ tx_pending = ioread32(card->config_regs + FLAGS_ADDR);
+
+ dev_vdbg(&card->dev->dev, "TX Flags are %X\n", tx_pending);
+
+ for (port = 0; port < card->nr_ports; port++) {
+ if (!(tx_pending & (1 << port))) {
+
+ spin_lock(&card->tx_queue_lock);
+ skb = skb_dequeue(&card->tx_queue[port]);
+ spin_unlock(&card->tx_queue_lock);
+
+ if (!skb)
+ continue;
+
+ if (atmdebug) {
+ dev_info(&card->dev->dev, "Transmitted: port %d\n",
+ port);
+ print_buffer(skb);
+ }
+ memcpy_toio(TX_BUF(card, port), skb->data, skb->len);
+
+ vcc = *(void **)skb->cb;
+
+ if (vcc) {
+ atomic_inc(&vcc->stats->tx);
+ solos_pop(vcc, skb);
+ } else
+ dev_kfree_skb_irq(skb);
+
+ tx_started |= 1 << port; //Set TX full flag
+ }
+ }
+ if (tx_started)
+ iowrite32(tx_started, card->config_regs + FLAGS_ADDR);
+
+ spin_unlock_irqrestore(&card->tx_lock, flags);
+ return 0;
+}
+
+static int psend(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct solos_card *card = vcc->dev->dev_data;
+ struct sk_buff *skb2 = NULL;
+ struct pkt_hdr *header;
+
+ //dev_dbg(&card->dev->dev, "psend called.\n");
+ //dev_dbg(&card->dev->dev, "dev,vpi,vci = %d,%d,%d\n",SOLOS_CHAN(vcc->dev),vcc->vpi,vcc->vci);
+
+ if (debug) {
+ skb2 = atm_alloc_charge(vcc, skb->len, GFP_ATOMIC);
+ if (skb2) {
+ memcpy(skb2->data, skb->data, skb->len);
+ skb_put(skb2, skb->len);
+ vcc->push(vcc, skb2);
+ atomic_inc(&vcc->stats->rx);
+ }
+ atomic_inc(&vcc->stats->tx);
+ solos_pop(vcc, skb);
+ return 0;
+ }
+
+ if (skb->len > (BUF_SIZE - sizeof(*header))) {
+ dev_warn(&card->dev->dev, "Length of PDU is too large. Dropping PDU.\n");
+ solos_pop(vcc, skb);
+ return 0;
+ }
+
+ if (!skb_clone_writable(skb, sizeof(*header))) {
+ int expand_by = 0;
+ int ret;
+
+ if (skb_headroom(skb) < sizeof(*header))
+ expand_by = sizeof(*header) - skb_headroom(skb);
+
+ ret = pskb_expand_head(skb, expand_by, 0, GFP_ATOMIC);
+ if (ret) {
+ solos_pop(vcc, skb);
+ return ret;
+ }
+ }
+
+ header = (void *)skb_push(skb, sizeof(*header));
+
+ header->size = cpu_to_le16(skb->len);
+ header->vpi = cpu_to_le16(vcc->vpi);
+ header->vci = cpu_to_le16(vcc->vci);
+ header->type = cpu_to_le16(PKT_DATA);
+
+ fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, vcc);
+
+ return 0;
+}
+
+static struct atmdev_ops fpga_ops = {
+ .open = popen,
+ .close = pclose,
+ .ioctl = NULL,
+ .getsockopt = NULL,
+ .setsockopt = NULL,
+ .send = psend,
+ .send_oam = NULL,
+ .phy_put = NULL,
+ .phy_get = NULL,
+ .change_qos = NULL,
+ .proc_read = NULL,
+ .owner = THIS_MODULE
+};
+
+static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ int err, i;
+ uint16_t fpga_ver;
+ uint8_t major_ver, minor_ver;
+ uint32_t data32;
+ struct solos_card *card;
+
+ if (debug)
+ return 0;
+
+ card = kzalloc(sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+
+ err = pci_enable_device(dev);
+ if (err) {
+ dev_warn(&dev->dev, "Failed to enable PCI device\n");
+ goto out;
+ }
+
+ err = pci_request_regions(dev, "solos");
+ if (err) {
+ dev_warn(&dev->dev, "Failed to request regions\n");
+ goto out;
+ }
+
+ card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE);
+ if (!card->config_regs) {
+ dev_warn(&dev->dev, "Failed to ioremap config registers\n");
+ goto out_release_regions;
+ }
+ card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE);
+ if (!card->buffers) {
+ dev_warn(&dev->dev, "Failed to ioremap data buffers\n");
+ goto out_unmap_config;
+ }
+
+// for(i=0;i<64 ;i+=4){
+// data32=ioread32(card->buffers + i);
+// dev_dbg(&card->dev->dev, "%08lX\n",(unsigned long)data32);
+// }
+
+ //Fill Config Mem with zeros
+ for(i = 0; i < 128; i += 4)
+ iowrite32(0, card->config_regs + i);
+
+ //Set RX empty flags
+ iowrite32(0xF0, card->config_regs + FLAGS_ADDR);
+
+ data32 = ioread32(card->config_regs + FPGA_VER);
+ fpga_ver = (data32 & 0x0000FFFF);
+ major_ver = ((data32 & 0xFF000000) >> 24);
+ minor_ver = ((data32 & 0x00FF0000) >> 16);
+ dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n",
+ major_ver, minor_ver, fpga_ver);
+
+ card->nr_ports = 2; /* FIXME: Detect daughterboard */
+
+ err = atm_init(card);
+ if (err)
+ goto out_unmap_both;
+
+ pci_set_drvdata(dev, card);
+ tasklet_init(&card->tlet, solos_bh, (unsigned long)card);
+ spin_lock_init(&card->tx_lock);
+ spin_lock_init(&card->tx_queue_lock);
+ spin_lock_init(&card->cli_queue_lock);
+/*
+ // Set Loopback mode
+ data32 = 0x00010000;
+ iowrite32(data32,card->config_regs + FLAGS_ADDR);
+*/
+/*
+ // Fill Buffers with zeros
+ for (i = 0; i < BUF_SIZE * 8; i += 4)
+ iowrite32(0, card->buffers + i);
+*/
+/*
+ for(i = 0; i < (BUF_SIZE * 1); i += 4)
+ iowrite32(0x12345678, card->buffers + i + (0*BUF_SIZE));
+ for(i = 0; i < (BUF_SIZE * 1); i += 4)
+ iowrite32(0xabcdef98, card->buffers + i + (1*BUF_SIZE));
+
+ // Read Config Memory
+ printk(KERN_DEBUG "Reading Config MEM\n");
+ i = 0;
+ for(i = 0; i < 16; i++) {
+ data32=ioread32(card->buffers + i*(BUF_SIZE/2));
+ printk(KERN_ALERT "Addr: %lX Data: %08lX\n",
+ (unsigned long)(addr_start + i*(BUF_SIZE/2)),
+ (unsigned long)data32);
+ }
+*/
+ //dev_dbg(&card->dev->dev, "Requesting IRQ: %d\n",dev->irq);
+ err = request_irq(dev->irq, solos_irq, IRQF_DISABLED|IRQF_SHARED,
+ "solos-pci", card);
+ if (err)
+ dev_dbg(&card->dev->dev, "Failed to request interrupt IRQ: %d\n", dev->irq);
+
+ // Enable IRQs
+ iowrite32(1, card->config_regs + IRQ_EN_ADDR);
+
+ return 0;
+
+ out_unmap_both:
+ pci_iounmap(dev, card->config_regs);
+ out_unmap_config:
+ pci_iounmap(dev, card->buffers);
+ out_release_regions:
+ pci_release_regions(dev);
+ out:
+ return err;
+}
+
+static int atm_init(struct solos_card *card)
+{
+ int i;
+
+ opens = 0;
+
+ for (i = 0; i < card->nr_ports; i++) {
+ skb_queue_head_init(&card->tx_queue[i]);
+ skb_queue_head_init(&card->cli_queue[i]);
+
+ card->atmdev[i] = atm_dev_register("solos-pci", &fpga_ops, -1, NULL);
+ if (!card->atmdev[i]) {
+ dev_err(&card->dev->dev, "Could not register ATM device %d\n", i);
+ atm_remove(card);
+ return -ENODEV;
+ }
+ if (device_create_file(&card->atmdev[i]->class_dev, &dev_attr_console))
+ dev_err(&card->dev->dev, "Could not register console for ATM device %d\n", i);
+
+ dev_info(&card->dev->dev, "Registered ATM device %d\n", card->atmdev[i]->number);
+
+ card->atmdev[i]->ci_range.vpi_bits = 8;
+ card->atmdev[i]->ci_range.vci_bits = 16;
+ card->atmdev[i]->dev_data = card;
+ card->atmdev[i]->phy_data = (void *)(unsigned long)i;
+ }
+ return 0;
+}
+
+static void atm_remove(struct solos_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->nr_ports; i++) {
+ if (card->atmdev[i]) {
+ dev_info(&card->dev->dev, "Unregistering ATM device %d\n", card->atmdev[i]->number);
+ atm_dev_deregister(card->atmdev[i]);
+ }
+ }
+}
+
+static void fpga_remove(struct pci_dev *dev)
+{
+ struct solos_card *card = pci_get_drvdata(dev);
+
+ if (debug)
+ return;
+
+ atm_remove(card);
+
+ dev_vdbg(&dev->dev, "Freeing IRQ\n");
+ // Disable IRQs from FPGA
+ iowrite32(0, card->config_regs + IRQ_EN_ADDR);
+ free_irq(dev->irq, card);
+ tasklet_kill(&card->tlet);
+
+ // iowrite32(0x01,pciregs);
+ dev_vdbg(&dev->dev, "Unmapping PCI resource\n");
+ pci_iounmap(dev, card->buffers);
+ pci_iounmap(dev, card->config_regs);
+
+ dev_vdbg(&dev->dev, "Releasing PCI Region\n");
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+
+ pci_set_drvdata(dev, NULL);
+ kfree(card);
+// dev_dbg(&card->dev->dev, "fpga_remove\n");
+ return;
+}
+
+static struct pci_device_id fpga_pci_tbl[] __devinitdata = {
+ { 0x10ee, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci,fpga_pci_tbl);
+
+static struct pci_driver fpga_driver = {
+ .name = "solos",
+ .id_table = fpga_pci_tbl,
+ .probe = fpga_probe,
+ .remove = fpga_remove,
+};
+
+
+static int __init solos_pci_init(void)
+{
+ printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION);
+ return pci_register_driver(&fpga_driver);
+}
+
+static void __exit solos_pci_exit(void)
+{
+ pci_unregister_driver(&fpga_driver);
+ printk(KERN_INFO "Solos PCI Driver %s Unloaded\n", VERSION);
+}
+
+module_init(solos_pci_init);
+module_exit(solos_pci_exit);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 4259072f5bd0..2aef96f20b30 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -128,10 +128,54 @@ print_cpus_func(online);
print_cpus_func(possible);
print_cpus_func(present);
+/*
+ * Print values for NR_CPUS and offlined cpus
+ */
+static ssize_t print_cpus_kernel_max(struct sysdev_class *class, char *buf)
+{
+ int n = snprintf(buf, PAGE_SIZE-2, "%d\n", CONFIG_NR_CPUS - 1);
+ return n;
+}
+static SYSDEV_CLASS_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
+
+/* arch-optional setting to enable display of offline cpus >= nr_cpu_ids */
+unsigned int total_cpus;
+
+static ssize_t print_cpus_offline(struct sysdev_class *class, char *buf)
+{
+ int n = 0, len = PAGE_SIZE-2;
+ cpumask_var_t offline;
+
+ /* display offline cpus < nr_cpu_ids */
+ if (!alloc_cpumask_var(&offline, GFP_KERNEL))
+ return -ENOMEM;
+ cpumask_complement(offline, cpu_online_mask);
+ n = cpulist_scnprintf(buf, len, offline);
+ free_cpumask_var(offline);
+
+ /* display offline cpus >= nr_cpu_ids */
+ if (total_cpus && nr_cpu_ids < total_cpus) {
+ if (n && n < len)
+ buf[n++] = ',';
+
+ if (nr_cpu_ids == total_cpus-1)
+ n += snprintf(&buf[n], len - n, "%d", nr_cpu_ids);
+ else
+ n += snprintf(&buf[n], len - n, "%d-%d",
+ nr_cpu_ids, total_cpus-1);
+ }
+
+ n += snprintf(&buf[n], len - n, "\n");
+ return n;
+}
+static SYSDEV_CLASS_ATTR(offline, 0444, print_cpus_offline, NULL);
+
static struct sysdev_class_attribute *cpu_state_attr[] = {
&attr_online_map,
&attr_possible_map,
&attr_present_map,
+ &attr_kernel_max,
+ &attr_offline,
};
static int cpu_states_init(void)
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
index 93f3690396a5..c237527b1aa5 100644
--- a/drivers/block/aoe/aoe.h
+++ b/drivers/block/aoe/aoe.h
@@ -200,4 +200,3 @@ void aoenet_xmit(struct sk_buff_head *);
int is_aoe_netif(struct net_device *ifp);
int set_aoe_iflist(const char __user *str, size_t size);
-unsigned long long mac_addr(char addr[6]);
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 1747dd272cd4..2307a271bdc9 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -37,7 +37,7 @@ static ssize_t aoedisk_show_mac(struct device *dev,
if (t == NULL)
return snprintf(page, PAGE_SIZE, "none\n");
- return snprintf(page, PAGE_SIZE, "%012llx\n", mac_addr(t->addr));
+ return snprintf(page, PAGE_SIZE, "%pm\n", t->addr);
}
static ssize_t aoedisk_show_netif(struct device *dev,
struct device_attribute *attr, char *page)
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index 71ff78c9e4d6..45c5a33daf49 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -349,11 +349,9 @@ resend(struct aoedev *d, struct aoetgt *t, struct frame *f)
ah = (struct aoe_atahdr *) (h+1);
snprintf(buf, sizeof buf,
- "%15s e%ld.%d oldtag=%08x@%08lx newtag=%08x "
- "s=%012llx d=%012llx nout=%d\n",
+ "%15s e%ld.%d oldtag=%08x@%08lx newtag=%08x s=%pm d=%pm nout=%d\n",
"retransmit", d->aoemajor, d->aoeminor, f->tag, jiffies, n,
- mac_addr(h->src),
- mac_addr(h->dst), t->nout);
+ h->src, h->dst, t->nout);
aoechr_error(buf);
f->tag = n;
@@ -544,10 +542,10 @@ rexmit_timer(ulong vp)
printk(KERN_INFO
"aoe: e%ld.%d: "
"too many lost jumbo on "
- "%s:%012llx - "
+ "%s:%pm - "
"falling back to %d frames.\n",
d->aoemajor, d->aoeminor,
- ifp->nd->name, mac_addr(t->addr),
+ ifp->nd->name, t->addr,
DEFAULTBCNT);
ifp->maxbcnt = 0;
}
@@ -672,8 +670,8 @@ ataid_complete(struct aoedev *d, struct aoetgt *t, unsigned char *id)
if (d->ssize != ssize)
printk(KERN_INFO
- "aoe: %012llx e%ld.%d v%04x has %llu sectors\n",
- mac_addr(t->addr),
+ "aoe: %pm e%ld.%d v%04x has %llu sectors\n",
+ t->addr,
d->aoemajor, d->aoeminor,
d->fw_ver, (long long)ssize);
d->ssize = ssize;
@@ -775,8 +773,8 @@ aoecmd_ata_rsp(struct sk_buff *skb)
n = get_unaligned_be32(&hin->tag);
t = gettgt(d, hin->src);
if (t == NULL) {
- printk(KERN_INFO "aoe: can't find target e%ld.%d:%012llx\n",
- d->aoemajor, d->aoeminor, mac_addr(hin->src));
+ printk(KERN_INFO "aoe: can't find target e%ld.%d:%pm\n",
+ d->aoemajor, d->aoeminor, hin->src);
spin_unlock_irqrestore(&d->lock, flags);
return;
}
@@ -1036,10 +1034,10 @@ aoecmd_cfg_rsp(struct sk_buff *skb)
n = n ? n * 512 : DEFAULTBCNT;
if (n != ifp->maxbcnt) {
printk(KERN_INFO
- "aoe: e%ld.%d: setting %d%s%s:%012llx\n",
+ "aoe: e%ld.%d: setting %d%s%s:%pm\n",
d->aoemajor, d->aoeminor, n,
" byte data frames on ", ifp->nd->name,
- mac_addr(t->addr));
+ t->addr);
ifp->maxbcnt = n;
}
}
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
index 9157d64270cb..30de5b1c647e 100644
--- a/drivers/block/aoe/aoenet.c
+++ b/drivers/block/aoe/aoenet.c
@@ -83,17 +83,6 @@ set_aoe_iflist(const char __user *user_str, size_t size)
return 0;
}
-unsigned long long
-mac_addr(char addr[6])
-{
- __be64 n = 0;
- char *p = (char *) &n;
-
- memcpy(p + 2, addr, 6); /* (sizeof addr != 6) */
-
- return (unsigned long long) __be64_to_cpu(n);
-}
-
void
aoenet_xmit(struct sk_buff_head *queue)
{
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 9364dc554257..01e69383d9c0 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -164,7 +164,7 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static int cciss_revalidate(struct gendisk *disk);
static int rebuild_lun_table(ctlr_info_t *h, int first_time);
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all);
static void cciss_read_capacity(int ctlr, int logvol, int withirq,
@@ -215,31 +215,17 @@ static struct block_device_operations cciss_fops = {
/*
* Enqueuing and dequeuing functions for cmdlists.
*/
-static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c)
+static inline void addQ(struct hlist_head *list, CommandList_struct *c)
{
- if (*Qptr == NULL) {
- *Qptr = c;
- c->next = c->prev = c;
- } else {
- c->prev = (*Qptr)->prev;
- c->next = (*Qptr);
- (*Qptr)->prev->next = c;
- (*Qptr)->prev = c;
- }
+ hlist_add_head(&c->list, list);
}
-static inline CommandList_struct *removeQ(CommandList_struct **Qptr,
- CommandList_struct *c)
+static inline void removeQ(CommandList_struct *c)
{
- if (c && c->next != c) {
- if (*Qptr == c)
- *Qptr = c->next;
- c->prev->next = c->next;
- c->next->prev = c->prev;
- } else {
- *Qptr = NULL;
- }
- return c;
+ if (WARN_ON(hlist_unhashed(&c->list)))
+ return;
+
+ hlist_del_init(&c->list);
}
#include "cciss_scsi.c" /* For SCSI tape support */
@@ -506,6 +492,7 @@ static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
c->cmdindex = i;
}
+ INIT_HLIST_NODE(&c->list);
c->busaddr = (__u32) cmd_dma_handle;
temp64.val = (__u64) err_dma_handle;
c->ErrDesc.Addr.lower = temp64.val32.lower;
@@ -1492,8 +1479,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time)
* which keeps the interrupt handler from starting
* the queue.
*/
- ret = deregister_disk(h->gendisk[drv_index],
- &h->drv[drv_index], 0);
+ ret = deregister_disk(h, drv_index, 0);
h->drv[drv_index].busy_configuring = 0;
}
@@ -1693,6 +1679,11 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
for (i = 0; i <= h->highest_lun; i++) {
int j;
drv_found = 0;
+
+ /* skip holes in the array from already deleted drives */
+ if (h->drv[i].raid_level == -1)
+ continue;
+
for (j = 0; j < num_luns; j++) {
memcpy(&lunid, &ld_buff->LUN[j][0], 4);
lunid = le32_to_cpu(lunid);
@@ -1706,8 +1697,7 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time)
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
h->drv[i].busy_configuring = 1;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- return_code = deregister_disk(h->gendisk[i],
- &h->drv[i], 1);
+ return_code = deregister_disk(h, i, 1);
h->drv[i].busy_configuring = 0;
}
}
@@ -1777,15 +1767,19 @@ mem_msg:
* the highest_lun should be left unchanged and the LunID
* should not be cleared.
*/
-static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
+static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all)
{
int i;
- ctlr_info_t *h = get_host(disk);
+ struct gendisk *disk;
+ drive_info_struct *drv;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+ drv = &h->drv[drv_index];
+ disk = h->gendisk[drv_index];
+
/* make sure logical volume is NOT is use */
if (clear_all || (h->gendisk[0] == disk)) {
if (drv->usage_count > 1)
@@ -2543,7 +2537,8 @@ static void start_io(ctlr_info_t *h)
{
CommandList_struct *c;
- while ((c = h->reqQ) != NULL) {
+ while (!hlist_empty(&h->reqQ)) {
+ c = hlist_entry(h->reqQ.first, CommandList_struct, list);
/* can't do anything if fifo is full */
if ((h->access.fifo_full(h))) {
printk(KERN_WARNING "cciss: fifo full\n");
@@ -2551,14 +2546,14 @@ static void start_io(ctlr_info_t *h)
}
/* Get the first entry from the Request Q */
- removeQ(&(h->reqQ), c);
+ removeQ(c);
h->Qdepth--;
/* Tell the controller execute command */
h->access.submit_command(h, c);
/* Put job onto the completed Q */
- addQ(&(h->cmpQ), c);
+ addQ(&h->cmpQ, c);
}
}
@@ -2571,7 +2566,7 @@ static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
memset(c->err_info, 0, sizeof(ErrorInfo_struct));
/* add it to software queue and then send it to the controller */
- addQ(&(h->reqQ), c);
+ addQ(&h->reqQ, c);
h->Qdepth++;
if (h->Qdepth > h->maxQsinceinit)
h->maxQsinceinit = h->Qdepth;
@@ -2892,7 +2887,7 @@ static void do_cciss_request(struct request_queue *q)
spin_lock_irq(q->queue_lock);
- addQ(&(h->reqQ), c);
+ addQ(&h->reqQ, c);
h->Qdepth++;
if (h->Qdepth > h->maxQsinceinit)
h->maxQsinceinit = h->Qdepth;
@@ -2980,16 +2975,12 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
a = c->busaddr;
} else {
+ struct hlist_node *tmp;
+
a &= ~3;
- if ((c = h->cmpQ) == NULL) {
- printk(KERN_WARNING
- "cciss: Completion of %08x ignored\n",
- a1);
- continue;
- }
- while (c->busaddr != a) {
- c = c->next;
- if (c == h->cmpQ)
+ c = NULL;
+ hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+ if (c->busaddr == a)
break;
}
}
@@ -2997,8 +2988,8 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
* If we've found the command, take it off the
* completion Q and free it
*/
- if (c->busaddr == a) {
- removeQ(&h->cmpQ, c);
+ if (c && c->busaddr == a) {
+ removeQ(c);
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
} else if (c->cmd_type == CMD_IOCTL_PEND) {
@@ -3418,6 +3409,8 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
return -1;
hba[i]->busy_initializing = 1;
+ INIT_HLIST_HEAD(&hba[i]->cmpQ);
+ INIT_HLIST_HEAD(&hba[i]->reqQ);
if (cciss_pci_init(hba[i], pdev) != 0)
goto clean1;
@@ -3725,15 +3718,17 @@ static void fail_all_cmds(unsigned long ctlr)
pci_disable_device(h->pdev); /* Make sure it is really dead. */
/* move everything off the request queue onto the completed queue */
- while ((c = h->reqQ) != NULL) {
- removeQ(&(h->reqQ), c);
+ while (!hlist_empty(&h->reqQ)) {
+ c = hlist_entry(h->reqQ.first, CommandList_struct, list);
+ removeQ(c);
h->Qdepth--;
- addQ(&(h->cmpQ), c);
+ addQ(&h->cmpQ, c);
}
/* Now, fail everything on the completed queue with a HW error */
- while ((c = h->cmpQ) != NULL) {
- removeQ(&h->cmpQ, c);
+ while (!hlist_empty(&h->cmpQ)) {
+ c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
+ removeQ(c);
c->err_info->CommandStatus = CMD_HARDWARE_ERR;
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 24a7efa993ab..15e2b84734e3 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -89,8 +89,8 @@ struct ctlr_info
struct access_method access;
/* queue and queue Info */
- CommandList_struct *reqQ;
- CommandList_struct *cmpQ;
+ struct hlist_head reqQ;
+ struct hlist_head cmpQ;
unsigned int Qdepth;
unsigned int maxQsinceinit;
unsigned int maxSG;
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index 43bf5593b59b..24e22dea1a99 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -265,8 +265,7 @@ typedef struct _CommandList_struct {
int ctlr;
int cmd_type;
long cmdindex;
- struct _CommandList_struct *prev;
- struct _CommandList_struct *next;
+ struct hlist_node list;
struct request * rq;
struct completion *waiting;
int retry_count;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 5c4ee70d5cf3..edbaac6c0573 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -624,20 +624,38 @@ static int loop_switch(struct loop_device *lo, struct file *file)
}
/*
+ * Helper to flush the IOs in loop, but keeping loop thread running
+ */
+static int loop_flush(struct loop_device *lo)
+{
+ /* loop not yet configured, no running thread, nothing to flush */
+ if (!lo->lo_thread)
+ return 0;
+
+ return loop_switch(lo, NULL);
+}
+
+/*
* Do the actual switch; called from the BIO completion routine
*/
static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
{
struct file *file = p->file;
struct file *old_file = lo->lo_backing_file;
- struct address_space *mapping = file->f_mapping;
+ struct address_space *mapping;
+
+ /* if no new file, only flush of queued bios requested */
+ if (!file)
+ goto out;
+ mapping = file->f_mapping;
mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
lo->lo_backing_file = file;
lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
lo->old_gfp_mask = mapping_gfp_mask(mapping);
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+out:
complete(&p->wait);
}
@@ -901,6 +919,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
kthread_stop(lo->lo_thread);
+ lo->lo_queue->unplug_fn = NULL;
lo->lo_backing_file = NULL;
loop_release_xfer(lo);
@@ -936,8 +955,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
{
int err;
struct loop_func_table *xfer;
+ uid_t uid = current_uid();
- if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid &&
+ if (lo->lo_encrypt_key_size &&
+ lo->lo_key_owner != uid &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
if (lo->lo_state != Lo_bound)
@@ -992,7 +1013,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (info->lo_encrypt_key_size) {
memcpy(lo->lo_encrypt_key, info->lo_encrypt_key,
info->lo_encrypt_key_size);
- lo->lo_key_owner = current->uid;
+ lo->lo_key_owner = uid;
}
return 0;
@@ -1343,11 +1364,25 @@ static int lo_release(struct gendisk *disk, fmode_t mode)
struct loop_device *lo = disk->private_data;
mutex_lock(&lo->lo_ctl_mutex);
- --lo->lo_refcnt;
- if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) && !lo->lo_refcnt)
+ if (--lo->lo_refcnt)
+ goto out;
+
+ if (lo->lo_flags & LO_FLAGS_AUTOCLEAR) {
+ /*
+ * In autoclear mode, stop the loop thread
+ * and remove configuration after last close.
+ */
loop_clr_fd(lo, NULL);
+ } else {
+ /*
+ * Otherwise keep thread (if running) and config,
+ * but flush possible ongoing bios in thread.
+ */
+ loop_flush(lo);
+ }
+out:
mutex_unlock(&lo->lo_ctl_mutex);
return 0;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index d3a91cacee8c..7bcc1d8bc967 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -722,7 +722,6 @@ static int __init nbd_init(void)
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = alloc_disk(1 << part_shift);
- elevator_t *old_e;
if (!disk)
goto out;
nbd_dev[i].disk = disk;
@@ -736,11 +735,10 @@ static int __init nbd_init(void)
put_disk(disk);
goto out;
}
- old_e = disk->queue->elevator;
- if (elevator_init(disk->queue, "deadline") == 0 ||
- elevator_init(disk->queue, "noop") == 0) {
- elevator_exit(old_e);
- }
+ /*
+ * Tell the block layer that we are not a rotational device
+ */
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue);
}
if (register_blkdev(NBD_MAJOR, "nbd")) {
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 85d79a02d487..5d34764c8a87 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -6,7 +6,6 @@
#include <linux/virtio_blk.h>
#include <linux/scatterlist.h>
-#define VIRTIO_MAX_SG (3+MAX_PHYS_SEGMENTS)
#define PART_BITS 4
static int major, index;
@@ -26,8 +25,11 @@ struct virtio_blk
mempool_t *pool;
+ /* What host tells us, plus 2 for header & tailer. */
+ unsigned int sg_elems;
+
/* Scatterlist: can be too big for stack. */
- struct scatterlist sg[VIRTIO_MAX_SG];
+ struct scatterlist sg[/*sg_elems*/];
};
struct virtblk_req
@@ -97,8 +99,6 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
if (blk_barrier_rq(vbr->req))
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
- /* This init could be done at vblk creation time */
- sg_init_table(vblk->sg, VIRTIO_MAX_SG);
sg_set_buf(&vblk->sg[0], &vbr->out_hdr, sizeof(vbr->out_hdr));
num = blk_rq_map_sg(q, vbr->req, vblk->sg+1);
sg_set_buf(&vblk->sg[num+1], &vbr->status, sizeof(vbr->status));
@@ -130,7 +130,7 @@ static void do_virtblk_request(struct request_queue *q)
while ((req = elv_next_request(q)) != NULL) {
vblk = req->rq_disk->private_data;
- BUG_ON(req->nr_phys_segments > ARRAY_SIZE(vblk->sg));
+ BUG_ON(req->nr_phys_segments + 2 > vblk->sg_elems);
/* If this request fails, stop queue and wait for something to
finish to restart it. */
@@ -196,12 +196,22 @@ static int virtblk_probe(struct virtio_device *vdev)
int err;
u64 cap;
u32 v;
- u32 blk_size;
+ u32 blk_size, sg_elems;
if (index_to_minor(index) >= 1 << MINORBITS)
return -ENOSPC;
- vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL);
+ /* We need to know how many segments before we allocate. */
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
+ offsetof(struct virtio_blk_config, seg_max),
+ &sg_elems);
+ if (err)
+ sg_elems = 1;
+
+ /* We need an extra sg elements at head and tail. */
+ sg_elems += 2;
+ vdev->priv = vblk = kmalloc(sizeof(*vblk) +
+ sizeof(vblk->sg[0]) * sg_elems, GFP_KERNEL);
if (!vblk) {
err = -ENOMEM;
goto out;
@@ -210,6 +220,8 @@ static int virtblk_probe(struct virtio_device *vdev)
INIT_LIST_HEAD(&vblk->reqs);
spin_lock_init(&vblk->lock);
vblk->vdev = vdev;
+ vblk->sg_elems = sg_elems;
+ sg_init_table(vblk->sg, vblk->sg_elems);
/* We expect one virtqueue, for output. */
vblk->vq = vdev->config->find_vq(vdev, 0, blk_done);
@@ -237,6 +249,8 @@ static int virtblk_probe(struct virtio_device *vdev)
goto out_put_disk;
}
+ queue_flag_set_unlocked(QUEUE_FLAG_VIRT, vblk->disk->queue);
+
if (index < 26) {
sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26);
} else if (index < (26 + 1) * 26) {
@@ -277,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev)
}
set_capacity(vblk->disk, cap);
+ /* We can handle whatever the host told us to handle. */
+ blk_queue_max_phys_segments(vblk->disk->queue, vblk->sg_elems-2);
+ blk_queue_max_hw_segments(vblk->disk->queue, vblk->sg_elems-2);
+
+ /* No real sector limit. */
+ blk_queue_max_sectors(vblk->disk->queue, -1U);
+
/* Host can optionally specify maximum segment size and number of
* segments. */
err = virtio_config_val(vdev, VIRTIO_BLK_F_SIZE_MAX,
@@ -284,12 +305,8 @@ static int virtblk_probe(struct virtio_device *vdev)
&v);
if (!err)
blk_queue_max_segment_size(vblk->disk->queue, v);
-
- err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX,
- offsetof(struct virtio_blk_config, seg_max),
- &v);
- if (!err)
- blk_queue_max_hw_segments(vblk->disk->queue, v);
+ else
+ blk_queue_max_segment_size(vblk->disk->queue, -1U);
/* Host can optionally specify the block size of the device */
err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 2d19f0cc47f2..918ef725de41 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -338,18 +338,12 @@ wait:
static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
{
struct request_queue *rq;
- elevator_t *old_e;
rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
if (rq == NULL)
return -1;
- old_e = rq->elevator;
- if (IS_ERR_VALUE(elevator_init(rq, "noop")))
- printk(KERN_WARNING
- "blkfront: Switch elevator failed, use default\n");
- else
- elevator_exit(old_e);
+ queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
/* Hard sector size and max sectors impersonate the equiv. hardware. */
blk_queue_hardsect_size(rq, sector_size);
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 7cb4029a5375..1164837bb781 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -2,26 +2,6 @@
menu "Bluetooth device drivers"
depends on BT
-config BT_HCIUSB
- tristate "HCI USB driver (old version)"
- depends on USB && BT_HCIBTUSB=n
- help
- Bluetooth HCI USB driver.
- This driver is required if you want to use Bluetooth devices with
- USB interface.
-
- Say Y here to compile support for Bluetooth USB devices into the
- kernel or say M to compile it as module (hci_usb).
-
-config BT_HCIUSB_SCO
- bool "SCO (voice) support"
- depends on BT_HCIUSB
- help
- This option enables the SCO support in the HCI USB driver. You need this
- to transmit voice data with your Bluetooth USB device.
-
- Say Y here to compile support for SCO over HCI USB.
-
config BT_HCIBTUSB
tristate "HCI USB driver"
depends on USB
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 77444afbf107..16930f93d1ca 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -2,7 +2,6 @@
# Makefile for the Linux Bluetooth HCI device drivers.
#
-obj-$(CONFIG_BT_HCIUSB) += hci_usb.o
obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
obj-$(CONFIG_BT_HCIUART) += hci_uart.o
obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index ee40201c7278..eafd4af0746e 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -37,11 +37,6 @@
#include <net/bluetooth/bluetooth.h>
-#ifndef CONFIG_BT_HCIBCM203X_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.2"
static struct usb_device_id bcm203x_table[] = {
@@ -199,7 +194,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
return -EIO;
}
- BT_DBG("minidrv data %p size %d", firmware->data, firmware->size);
+ BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
size = max_t(uint, firmware->size, 4096);
@@ -227,7 +222,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
return -EIO;
}
- BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+ BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
data->fw_data = kmalloc(firmware->size, GFP_KERNEL);
if (!data->fw_data) {
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 90a094634630..d3f14bee0f19 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -38,11 +38,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCIBFUSB_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.2"
static struct usb_driver bfusb_driver;
@@ -221,7 +216,7 @@ static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
struct sk_buff *skb;
int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
- BT_DBG("bfusb %p urb %p", bfusb, urb);
+ BT_DBG("bfusb %p urb %p", data, urb);
if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
return -ENOMEM;
@@ -354,7 +349,7 @@ static void bfusb_rx_complete(struct urb *urb)
int count = urb->actual_length;
int err, hdr, len;
- BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
read_lock(&data->lock);
@@ -691,7 +686,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
goto error;
}
- BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+ BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
BT_ERR("Firmware loading failed");
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index b936d8ce2728..c115285867c3 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -35,11 +35,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCIBPA10X_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "0.10"
static struct usb_device_id bpa10x_table[] = {
@@ -489,6 +484,8 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *
hdev->owner = THIS_MODULE;
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+
err = hci_register_dev(hdev);
if (err < 0) {
hci_free_dev(hdev);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index b3e4d07a4ac2..ff195c230825 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -502,15 +502,15 @@ static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
memset(b, 0, sizeof(b));
memcpy(b, ptr + 2, 2);
- size = simple_strtol(b, NULL, 16);
+ size = simple_strtoul(b, NULL, 16);
memset(b, 0, sizeof(b));
memcpy(b, ptr + 4, 8);
- addr = simple_strtol(b, NULL, 16);
+ addr = simple_strtoul(b, NULL, 16);
memset(b, 0, sizeof(b));
memcpy(b, ptr + (size * 2) + 2, 2);
- fcs = simple_strtol(b, NULL, 16);
+ fcs = simple_strtoul(b, NULL, 16);
memset(b, 0, sizeof(b));
for (tmp = 0, i = 0; i < size; i++) {
@@ -530,7 +530,7 @@ static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
memset(b, 0, sizeof(b));
for (i = 0; i < (size - 4) / 2; i++) {
memcpy(b, ptr + (i * 4) + 12, 4);
- tmp = simple_strtol(b, NULL, 16);
+ tmp = simple_strtoul(b, NULL, 16);
bt3c_put(iobase, tmp);
}
}
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index cda6c7cc944b..7e298275c8f6 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -37,11 +37,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCIBTSDIO_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "0.1"
static const struct sdio_device_id btsdio_table[] = {
@@ -91,6 +86,7 @@ static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
if (err < 0) {
+ skb_pull(skb, 4);
sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
return err;
}
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index af472e052732..b5fbda6d490a 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -35,31 +35,25 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-//#define CONFIG_BT_HCIBTUSB_DEBUG
-#ifndef CONFIG_BT_HCIBTUSB_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
-#define VERSION "0.3"
+#define VERSION "0.4"
static int ignore_dga;
static int ignore_csr;
static int ignore_sniffer;
static int disable_scofix;
static int force_scofix;
-static int reset;
+
+static int reset = 1;
static struct usb_driver btusb_driver;
#define BTUSB_IGNORE 0x01
-#define BTUSB_RESET 0x02
-#define BTUSB_DIGIANSWER 0x04
-#define BTUSB_CSR 0x08
-#define BTUSB_SNIFFER 0x10
-#define BTUSB_BCM92035 0x20
-#define BTUSB_BROKEN_ISOC 0x40
-#define BTUSB_WRONG_SCO_MTU 0x80
+#define BTUSB_DIGIANSWER 0x02
+#define BTUSB_CSR 0x04
+#define BTUSB_SNIFFER 0x08
+#define BTUSB_BCM92035 0x10
+#define BTUSB_BROKEN_ISOC 0x20
+#define BTUSB_WRONG_SCO_MTU 0x40
static struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -79,7 +73,7 @@ static struct usb_device_id btusb_table[] = {
{ USB_DEVICE(0x0bdb, 0x1002) },
/* Canyon CN-BTU1 with HID interfaces */
- { USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x0c10, 0x0000) },
{ } /* Terminating entry */
};
@@ -94,52 +88,37 @@ static struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
/* Broadcom BCM2035 */
- { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
/* Broadcom BCM2045 */
- { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
-
- /* Broadcom BCM2046 */
- { USB_DEVICE(0x0a5c, 0x2146), .driver_info = BTUSB_RESET },
- { USB_DEVICE(0x0a5c, 0x2151), .driver_info = BTUSB_RESET },
-
- /* Apple MacBook Pro with Broadcom chip */
- { USB_DEVICE(0x05ac, 0x820f), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_WRONG_SCO_MTU },
/* IBM/Lenovo ThinkPad with Broadcom chip */
- { USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
-
- /* Targus ACB10US */
- { USB_DEVICE(0x0a5c, 0x2100), .driver_info = BTUSB_RESET },
- { USB_DEVICE(0x0a5c, 0x2154), .driver_info = BTUSB_RESET },
-
- /* ANYCOM Bluetooth USB-200 and USB-250 */
- { USB_DEVICE(0x0a5c, 0x2111), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_WRONG_SCO_MTU },
/* HP laptop with Broadcom chip */
- { USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_WRONG_SCO_MTU },
/* Dell laptop with Broadcom chip */
- { USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_WRONG_SCO_MTU },
- /* Dell Wireless 370 */
- { USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ /* Dell Wireless 370 and 410 devices */
+ { USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_WRONG_SCO_MTU },
- /* Dell Wireless 410 */
- { USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ /* Belkin F8T012 and F8T013 devices */
+ { USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_WRONG_SCO_MTU },
- /* Microsoft Wireless Transceiver for Bluetooth 2.0 */
- { USB_DEVICE(0x045e, 0x009c), .driver_info = BTUSB_RESET },
+ /* Asus WL-BTD202 device */
+ { USB_DEVICE(0x0b05, 0x1715), .driver_info = BTUSB_WRONG_SCO_MTU },
/* Kensington Bluetooth USB adapter */
- { USB_DEVICE(0x047d, 0x105d), .driver_info = BTUSB_RESET },
- { USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
-
- /* ISSC Bluetooth Adapter v3.1 */
- { USB_DEVICE(0x1131, 0x1001), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_WRONG_SCO_MTU },
/* RTX Telecom based adapters with buggy SCO support */
{ USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
@@ -148,13 +127,6 @@ static struct usb_device_id blacklist_table[] = {
/* CONWISE Technology based adapters with buggy SCO support */
{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
- /* Belkin F8T012 and F8T013 devices */
- { USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
- { USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
-
- /* Belkin F8T016 device */
- { USB_DEVICE(0x050d, 0x016a), .driver_info = BTUSB_RESET },
-
/* Digianswer devices */
{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
{ USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
@@ -197,7 +169,10 @@ struct btusb_data {
struct usb_endpoint_descriptor *isoc_tx_ep;
struct usb_endpoint_descriptor *isoc_rx_ep;
+ __u8 cmdreq_type;
+
int isoc_altsetting;
+ int suspend_count;
};
static void btusb_intr_complete(struct urb *urb)
@@ -236,7 +211,7 @@ static void btusb_intr_complete(struct urb *urb)
}
}
-static int btusb_submit_intr_urb(struct hci_dev *hdev)
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
{
struct btusb_data *data = hdev->driver_data;
struct urb *urb;
@@ -249,13 +224,13 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
if (!data->intr_ep)
return -ENODEV;
- urb = usb_alloc_urb(0, GFP_ATOMIC);
+ urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
- buf = kmalloc(size, GFP_ATOMIC);
+ buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
@@ -271,7 +246,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev)
usb_anchor_urb(urb, &data->intr_anchor);
- err = usb_submit_urb(urb, GFP_ATOMIC);
+ err = usb_submit_urb(urb, mem_flags);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
@@ -319,7 +294,7 @@ static void btusb_bulk_complete(struct urb *urb)
}
}
-static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
{
struct btusb_data *data = hdev->driver_data;
struct urb *urb;
@@ -332,13 +307,13 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
if (!data->bulk_rx_ep)
return -ENODEV;
- urb = usb_alloc_urb(0, GFP_KERNEL);
+ urb = usb_alloc_urb(0, mem_flags);
if (!urb)
return -ENOMEM;
size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
- buf = kmalloc(size, GFP_KERNEL);
+ buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
@@ -353,7 +328,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev)
usb_anchor_urb(urb, &data->bulk_anchor);
- err = usb_submit_urb(urb, GFP_KERNEL);
+ err = usb_submit_urb(urb, mem_flags);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
@@ -430,7 +405,7 @@ static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
urb->number_of_packets = i;
}
-static int btusb_submit_isoc_urb(struct hci_dev *hdev)
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
{
struct btusb_data *data = hdev->driver_data;
struct urb *urb;
@@ -443,14 +418,14 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
if (!data->isoc_rx_ep)
return -ENODEV;
- urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
if (!urb)
return -ENOMEM;
size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
BTUSB_MAX_ISOC_FRAMES;
- buf = kmalloc(size, GFP_KERNEL);
+ buf = kmalloc(size, mem_flags);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
@@ -473,7 +448,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev)
usb_anchor_urb(urb, &data->isoc_anchor);
- err = usb_submit_urb(urb, GFP_KERNEL);
+ err = usb_submit_urb(urb, mem_flags);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
@@ -520,7 +495,7 @@ static int btusb_open(struct hci_dev *hdev)
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
return 0;
- err = btusb_submit_intr_urb(hdev);
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
if (err < 0) {
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -589,7 +564,7 @@ static int btusb_send_frame(struct sk_buff *skb)
return -ENOMEM;
}
- dr->bRequestType = USB_TYPE_CLASS;
+ dr->bRequestType = data->cmdreq_type;
dr->bRequest = 0;
dr->wIndex = 0;
dr->wValue = 0;
@@ -680,8 +655,19 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
BT_DBG("%s evt %d", hdev->name, evt);
- if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
- schedule_work(&data->work);
+ if (hdev->conn_hash.acl_num > 0) {
+ if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ else
+ btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+ }
+ } else {
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ usb_unlink_anchored_urbs(&data->bulk_anchor);
+ }
+
+ schedule_work(&data->work);
}
static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
@@ -732,18 +718,6 @@ static void btusb_work(struct work_struct *work)
struct btusb_data *data = container_of(work, struct btusb_data, work);
struct hci_dev *hdev = data->hdev;
- if (hdev->conn_hash.acl_num > 0) {
- if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
- if (btusb_submit_bulk_urb(hdev) < 0)
- clear_bit(BTUSB_BULK_RUNNING, &data->flags);
- else
- btusb_submit_bulk_urb(hdev);
- }
- } else {
- clear_bit(BTUSB_BULK_RUNNING, &data->flags);
- usb_kill_anchored_urbs(&data->bulk_anchor);
- }
-
if (hdev->conn_hash.sco_num > 0) {
if (data->isoc_altsetting != 2) {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -754,10 +728,10 @@ static void btusb_work(struct work_struct *work)
}
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
- if (btusb_submit_isoc_urb(hdev) < 0)
+ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
else
- btusb_submit_isoc_urb(hdev);
+ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
}
} else {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
@@ -828,6 +802,8 @@ static int btusb_probe(struct usb_interface *intf,
return -ENODEV;
}
+ data->cmdreq_type = USB_TYPE_CLASS;
+
data->udev = interface_to_usbdev(intf);
data->intf = intf;
@@ -862,11 +838,11 @@ static int btusb_probe(struct usb_interface *intf,
hdev->owner = THIS_MODULE;
- /* interface numbers are hardcoded in the spec */
+ /* Interface numbers are hardcoded in the specification */
data->isoc = usb_ifnum_to_if(data->udev, 1);
- if (reset || id->driver_info & BTUSB_RESET)
- set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+ if (!reset)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
if (!disable_scofix)
@@ -876,9 +852,23 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_BROKEN_ISOC)
data->isoc = NULL;
+ if (id->driver_info & BTUSB_DIGIANSWER) {
+ data->cmdreq_type = USB_TYPE_VENDOR;
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_CSR) {
+ struct usb_device *udev = data->udev;
+
+ /* Old firmware would otherwise execute USB reset */
+ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
+ }
+
if (id->driver_info & BTUSB_SNIFFER) {
struct usb_device *udev = data->udev;
+ /* New sniffer firmware has crippled HCI interface */
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
@@ -949,10 +939,71 @@ static void btusb_disconnect(struct usb_interface *intf)
hci_free_dev(hdev);
}
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ if (data->suspend_count++)
+ return 0;
+
+ cancel_work_sync(&data->work);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+ usb_kill_anchored_urbs(&data->intr_anchor);
+
+ return 0;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+ int err;
+
+ BT_DBG("intf %p", intf);
+
+ if (--data->suspend_count)
+ return 0;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ return err;
+ }
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ if (btusb_submit_bulk_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ else
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ return 0;
+}
+
static struct usb_driver btusb_driver = {
.name = "btusb",
.probe = btusb_probe,
.disconnect = btusb_disconnect,
+ .suspend = btusb_suspend,
+ .resume = btusb_resume,
.id_table = btusb_table,
};
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 7938062c1cc7..894b2cb11ea6 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -47,11 +47,6 @@
#include "hci_uart.h"
-#ifndef CONFIG_BT_HCIUART_DEBUG
-#undef BT_DBG
-#define BT_DBG( A... )
-#endif
-
#define VERSION "0.3"
static int txcrc = 1;
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index bfbae14cf93d..b0fafb055996 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -46,11 +46,6 @@
#include "hci_uart.h"
-#ifndef CONFIG_BT_HCIUART_DEBUG
-#undef BT_DBG
-#define BT_DBG( A... )
-#endif
-
#define VERSION "1.2"
struct h4_struct {
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 4426bb552bd9..af761dc434f6 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -46,11 +46,6 @@
#include "hci_uart.h"
-#ifndef CONFIG_BT_HCIUART_DEBUG
-#undef BT_DBG
-#define BT_DBG( A... )
-#endif
-
#define VERSION "2.2"
static int reset = 0;
@@ -399,8 +394,8 @@ static int hci_uart_register_dev(struct hci_uart *hu)
hdev->owner = THIS_MODULE;
- if (reset)
- set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+ if (!reset)
+ set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks);
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
deleted file mode 100644
index 3c453924f838..000000000000
--- a/drivers/bluetooth/hci_usb.c
+++ /dev/null
@@ -1,1136 +0,0 @@
-/*
- HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
- Copyright (C) 2000-2001 Qualcomm Incorporated
- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
- Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.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;
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
- SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * Bluetooth HCI USB driver.
- * Based on original USB Bluetooth driver for Linux kernel
- * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
- * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
- *
- */
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/unistd.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/skbuff.h>
-
-#include <linux/usb.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-
-#include "hci_usb.h"
-
-#ifndef CONFIG_BT_HCIUSB_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
-#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET
-#undef URB_ZERO_PACKET
-#define URB_ZERO_PACKET 0
-#endif
-
-static int ignore_dga;
-static int ignore_csr;
-static int ignore_sniffer;
-static int disable_scofix;
-static int force_scofix;
-static int reset;
-
-#ifdef CONFIG_BT_HCIUSB_SCO
-static int isoc = 2;
-#endif
-
-#define VERSION "2.10"
-
-static struct usb_driver hci_usb_driver;
-
-static struct usb_device_id bluetooth_ids[] = {
- /* Generic Bluetooth USB device */
- { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
-
- /* AVM BlueFRITZ! USB v2.0 */
- { USB_DEVICE(0x057c, 0x3800) },
-
- /* Bluetooth Ultraport Module from IBM */
- { USB_DEVICE(0x04bf, 0x030a) },
-
- /* ALPS Modules with non-standard id */
- { USB_DEVICE(0x044e, 0x3001) },
- { USB_DEVICE(0x044e, 0x3002) },
-
- /* Ericsson with non-standard id */
- { USB_DEVICE(0x0bdb, 0x1002) },
-
- /* Canyon CN-BTU1 with HID interfaces */
- { USB_DEVICE(0x0c10, 0x0000), .driver_info = HCI_RESET },
-
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, bluetooth_ids);
-
-static struct usb_device_id blacklist_ids[] = {
- /* CSR BlueCore devices */
- { USB_DEVICE(0x0a12, 0x0001), .driver_info = HCI_CSR },
-
- /* Broadcom BCM2033 without firmware */
- { USB_DEVICE(0x0a5c, 0x2033), .driver_info = HCI_IGNORE },
-
- /* Broadcom BCM2035 */
- { USB_DEVICE(0x0a5c, 0x2035), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x200a), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_BCM92035 },
-
- /* Broadcom BCM2045 */
- { USB_DEVICE(0x0a5c, 0x2039), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x2101), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* IBM/Lenovo ThinkPad with Broadcom chip */
- { USB_DEVICE(0x0a5c, 0x201e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- { USB_DEVICE(0x0a5c, 0x2110), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* Targus ACB10US */
- { USB_DEVICE(0x0a5c, 0x2100), .driver_info = HCI_RESET },
-
- /* ANYCOM Bluetooth USB-200 and USB-250 */
- { USB_DEVICE(0x0a5c, 0x2111), .driver_info = HCI_RESET },
-
- /* HP laptop with Broadcom chip */
- { USB_DEVICE(0x03f0, 0x171d), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* Dell laptop with Broadcom chip */
- { USB_DEVICE(0x413c, 0x8126), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- /* Dell Wireless 370 */
- { USB_DEVICE(0x413c, 0x8156), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- /* Dell Wireless 410 */
- { USB_DEVICE(0x413c, 0x8152), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* Broadcom 2046 */
- { USB_DEVICE(0x0a5c, 0x2151), .driver_info = HCI_RESET },
-
- /* Microsoft Wireless Transceiver for Bluetooth 2.0 */
- { USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_RESET },
-
- /* Kensington Bluetooth USB adapter */
- { USB_DEVICE(0x047d, 0x105d), .driver_info = HCI_RESET },
- { USB_DEVICE(0x047d, 0x105e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* ISSC Bluetooth Adapter v3.1 */
- { USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },
-
- /* RTX Telecom based adapters with buggy SCO support */
- { USB_DEVICE(0x0400, 0x0807), .driver_info = HCI_BROKEN_ISOC },
- { USB_DEVICE(0x0400, 0x080a), .driver_info = HCI_BROKEN_ISOC },
-
- /* CONWISE Technology based adapters with buggy SCO support */
- { USB_DEVICE(0x0e5e, 0x6622), .driver_info = HCI_BROKEN_ISOC },
-
- /* Belkin F8T012 and F8T013 devices */
- { USB_DEVICE(0x050d, 0x0012), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
- { USB_DEVICE(0x050d, 0x0013), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
-
- /* Digianswer devices */
- { USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },
- { USB_DEVICE(0x08fd, 0x0002), .driver_info = HCI_IGNORE },
-
- /* CSR BlueCore Bluetooth Sniffer */
- { USB_DEVICE(0x0a12, 0x0002), .driver_info = HCI_SNIFFER },
-
- /* Frontline ComProbe Bluetooth Sniffer */
- { USB_DEVICE(0x16d3, 0x0002), .driver_info = HCI_SNIFFER },
-
- { } /* Terminating entry */
-};
-
-static struct _urb *_urb_alloc(int isoc, gfp_t gfp)
-{
- struct _urb *_urb = kmalloc(sizeof(struct _urb) +
- sizeof(struct usb_iso_packet_descriptor) * isoc, gfp);
- if (_urb) {
- memset(_urb, 0, sizeof(*_urb));
- usb_init_urb(&_urb->urb);
- }
- return _urb;
-}
-
-static struct _urb *_urb_dequeue(struct _urb_queue *q)
-{
- struct _urb *_urb = NULL;
- unsigned long flags;
- spin_lock_irqsave(&q->lock, flags);
- {
- struct list_head *head = &q->head;
- struct list_head *next = head->next;
- if (next != head) {
- _urb = list_entry(next, struct _urb, list);
- list_del(next); _urb->queue = NULL;
- }
- }
- spin_unlock_irqrestore(&q->lock, flags);
- return _urb;
-}
-
-static void hci_usb_rx_complete(struct urb *urb);
-static void hci_usb_tx_complete(struct urb *urb);
-
-#define __pending_tx(husb, type) (&husb->pending_tx[type-1])
-#define __pending_q(husb, type) (&husb->pending_q[type-1])
-#define __completed_q(husb, type) (&husb->completed_q[type-1])
-#define __transmit_q(husb, type) (&husb->transmit_q[type-1])
-
-static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
-{
- return _urb_dequeue(__completed_q(husb, type));
-}
-
-#ifdef CONFIG_BT_HCIUSB_SCO
-static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
-{
- int offset = 0, i;
-
- BT_DBG("len %d mtu %d", len, mtu);
-
- for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {
- urb->iso_frame_desc[i].offset = offset;
- urb->iso_frame_desc[i].length = mtu;
- BT_DBG("desc %d offset %d len %d", i, offset, mtu);
- }
- if (len && i < HCI_MAX_ISOC_FRAMES) {
- urb->iso_frame_desc[i].offset = offset;
- urb->iso_frame_desc[i].length = len;
- BT_DBG("desc %d offset %d len %d", i, offset, len);
- i++;
- }
- urb->number_of_packets = i;
-}
-#endif
-
-static int hci_usb_intr_rx_submit(struct hci_usb *husb)
-{
- struct _urb *_urb;
- struct urb *urb;
- int err, pipe, interval, size;
- void *buf;
-
- BT_DBG("%s", husb->hdev->name);
-
- size = le16_to_cpu(husb->intr_in_ep->desc.wMaxPacketSize);
-
- buf = kmalloc(size, GFP_ATOMIC);
- if (!buf)
- return -ENOMEM;
-
- _urb = _urb_alloc(0, GFP_ATOMIC);
- if (!_urb) {
- kfree(buf);
- return -ENOMEM;
- }
- _urb->type = HCI_EVENT_PKT;
- _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
-
- urb = &_urb->urb;
- pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->desc.bEndpointAddress);
- interval = husb->intr_in_ep->desc.bInterval;
- usb_fill_int_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);
-
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err) {
- BT_ERR("%s intr rx submit failed urb %p err %d",
- husb->hdev->name, urb, err);
- _urb_unlink(_urb);
- kfree(_urb);
- kfree(buf);
- }
- return err;
-}
-
-static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
-{
- struct _urb *_urb;
- struct urb *urb;
- int err, pipe, size = HCI_MAX_FRAME_SIZE;
- void *buf;
-
- buf = kmalloc(size, GFP_ATOMIC);
- if (!buf)
- return -ENOMEM;
-
- _urb = _urb_alloc(0, GFP_ATOMIC);
- if (!_urb) {
- kfree(buf);
- return -ENOMEM;
- }
- _urb->type = HCI_ACLDATA_PKT;
- _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
-
- urb = &_urb->urb;
- pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->desc.bEndpointAddress);
- usb_fill_bulk_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);
- urb->transfer_flags = 0;
-
- BT_DBG("%s urb %p", husb->hdev->name, urb);
-
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err) {
- BT_ERR("%s bulk rx submit failed urb %p err %d",
- husb->hdev->name, urb, err);
- _urb_unlink(_urb);
- kfree(_urb);
- kfree(buf);
- }
- return err;
-}
-
-#ifdef CONFIG_BT_HCIUSB_SCO
-static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
-{
- struct _urb *_urb;
- struct urb *urb;
- int err, mtu, size;
- void *buf;
-
- mtu = le16_to_cpu(husb->isoc_in_ep->desc.wMaxPacketSize);
- size = mtu * HCI_MAX_ISOC_FRAMES;
-
- buf = kmalloc(size, GFP_ATOMIC);
- if (!buf)
- return -ENOMEM;
-
- _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
- if (!_urb) {
- kfree(buf);
- return -ENOMEM;
- }
- _urb->type = HCI_SCODATA_PKT;
- _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
-
- urb = &_urb->urb;
-
- urb->context = husb;
- urb->dev = husb->udev;
- urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->desc.bEndpointAddress);
- urb->complete = hci_usb_rx_complete;
-
- urb->interval = husb->isoc_in_ep->desc.bInterval;
-
- urb->transfer_buffer_length = size;
- urb->transfer_buffer = buf;
- urb->transfer_flags = URB_ISO_ASAP;
-
- __fill_isoc_desc(urb, size, mtu);
-
- BT_DBG("%s urb %p", husb->hdev->name, urb);
-
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err) {
- BT_ERR("%s isoc rx submit failed urb %p err %d",
- husb->hdev->name, urb, err);
- _urb_unlink(_urb);
- kfree(_urb);
- kfree(buf);
- }
- return err;
-}
-#endif
-
-/* Initialize device */
-static int hci_usb_open(struct hci_dev *hdev)
-{
- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
- int i, err;
- unsigned long flags;
-
- BT_DBG("%s", hdev->name);
-
- if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
- return 0;
-
- write_lock_irqsave(&husb->completion_lock, flags);
-
- err = hci_usb_intr_rx_submit(husb);
- if (!err) {
- for (i = 0; i < HCI_MAX_BULK_RX; i++)
- hci_usb_bulk_rx_submit(husb);
-
-#ifdef CONFIG_BT_HCIUSB_SCO
- if (husb->isoc_iface)
- for (i = 0; i < HCI_MAX_ISOC_RX; i++)
- hci_usb_isoc_rx_submit(husb);
-#endif
- } else {
- clear_bit(HCI_RUNNING, &hdev->flags);
- }
-
- write_unlock_irqrestore(&husb->completion_lock, flags);
- return err;
-}
-
-/* Reset device */
-static int hci_usb_flush(struct hci_dev *hdev)
-{
- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
- int i;
-
- BT_DBG("%s", hdev->name);
-
- for (i = 0; i < 4; i++)
- skb_queue_purge(&husb->transmit_q[i]);
- return 0;
-}
-
-static void hci_usb_unlink_urbs(struct hci_usb *husb)
-{
- int i;
-
- BT_DBG("%s", husb->hdev->name);
-
- for (i = 0; i < 4; i++) {
- struct _urb *_urb;
- struct urb *urb;
-
- /* Kill pending requests */
- while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {
- urb = &_urb->urb;
- BT_DBG("%s unlinking _urb %p type %d urb %p",
- husb->hdev->name, _urb, _urb->type, urb);
- usb_kill_urb(urb);
- _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
- }
-
- /* Release completed requests */
- while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {
- urb = &_urb->urb;
- BT_DBG("%s freeing _urb %p type %d urb %p",
- husb->hdev->name, _urb, _urb->type, urb);
- kfree(urb->setup_packet);
- kfree(urb->transfer_buffer);
- kfree(_urb);
- }
- }
-}
-
-/* Close device */
-static int hci_usb_close(struct hci_dev *hdev)
-{
- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
- unsigned long flags;
-
- if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
- return 0;
-
- BT_DBG("%s", hdev->name);
-
- /* Synchronize with completion handlers */
- write_lock_irqsave(&husb->completion_lock, flags);
- write_unlock_irqrestore(&husb->completion_lock, flags);
-
- hci_usb_unlink_urbs(husb);
- hci_usb_flush(hdev);
- return 0;
-}
-
-static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
-{
- struct urb *urb = &_urb->urb;
- int err;
-
- BT_DBG("%s urb %p type %d", husb->hdev->name, urb, _urb->type);
-
- _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err) {
- BT_ERR("%s tx submit failed urb %p type %d err %d",
- husb->hdev->name, urb, _urb->type, err);
- _urb_unlink(_urb);
- _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
- } else
- atomic_inc(__pending_tx(husb, _urb->type));
-
- return err;
-}
-
-static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
-{
- struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
- struct usb_ctrlrequest *dr;
- struct urb *urb;
-
- if (!_urb) {
- _urb = _urb_alloc(0, GFP_ATOMIC);
- if (!_urb)
- return -ENOMEM;
- _urb->type = bt_cb(skb)->pkt_type;
-
- dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
- if (!dr) {
- kfree(_urb);
- return -ENOMEM;
- }
- } else
- dr = (void *) _urb->urb.setup_packet;
-
- dr->bRequestType = husb->ctrl_req;
- dr->bRequest = 0;
- dr->wIndex = 0;
- dr->wValue = 0;
- dr->wLength = __cpu_to_le16(skb->len);
-
- urb = &_urb->urb;
- usb_fill_control_urb(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),
- (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);
-
- BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
-
- _urb->priv = skb;
- return __tx_submit(husb, _urb);
-}
-
-static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
-{
- struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
- struct urb *urb;
- int pipe;
-
- if (!_urb) {
- _urb = _urb_alloc(0, GFP_ATOMIC);
- if (!_urb)
- return -ENOMEM;
- _urb->type = bt_cb(skb)->pkt_type;
- }
-
- urb = &_urb->urb;
- pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->desc.bEndpointAddress);
- usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, skb->len,
- hci_usb_tx_complete, husb);
- urb->transfer_flags = URB_ZERO_PACKET;
-
- BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
-
- _urb->priv = skb;
- return __tx_submit(husb, _urb);
-}
-
-#ifdef CONFIG_BT_HCIUSB_SCO
-static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
-{
- struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
- struct urb *urb;
-
- if (!_urb) {
- _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
- if (!_urb)
- return -ENOMEM;
- _urb->type = bt_cb(skb)->pkt_type;
- }
-
- BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
-
- urb = &_urb->urb;
-
- urb->context = husb;
- urb->dev = husb->udev;
- urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->desc.bEndpointAddress);
- urb->complete = hci_usb_tx_complete;
- urb->transfer_flags = URB_ISO_ASAP;
-
- urb->interval = husb->isoc_out_ep->desc.bInterval;
-
- urb->transfer_buffer = skb->data;
- urb->transfer_buffer_length = skb->len;
-
- __fill_isoc_desc(urb, skb->len, le16_to_cpu(husb->isoc_out_ep->desc.wMaxPacketSize));
-
- _urb->priv = skb;
- return __tx_submit(husb, _urb);
-}
-#endif
-
-static void hci_usb_tx_process(struct hci_usb *husb)
-{
- struct sk_buff_head *q;
- struct sk_buff *skb;
-
- BT_DBG("%s", husb->hdev->name);
-
- do {
- clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
-
- /* Process command queue */
- q = __transmit_q(husb, HCI_COMMAND_PKT);
- if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) &&
- (skb = skb_dequeue(q))) {
- if (hci_usb_send_ctrl(husb, skb) < 0)
- skb_queue_head(q, skb);
- }
-
-#ifdef CONFIG_BT_HCIUSB_SCO
- /* Process SCO queue */
- q = __transmit_q(husb, HCI_SCODATA_PKT);
- if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
- (skb = skb_dequeue(q))) {
- if (hci_usb_send_isoc(husb, skb) < 0)
- skb_queue_head(q, skb);
- }
-#endif
-
- /* Process ACL queue */
- q = __transmit_q(husb, HCI_ACLDATA_PKT);
- while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
- (skb = skb_dequeue(q))) {
- if (hci_usb_send_bulk(husb, skb) < 0) {
- skb_queue_head(q, skb);
- break;
- }
- }
- } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
-}
-
-static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
-{
- /* Serialize TX queue processing to avoid data reordering */
- if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
- hci_usb_tx_process(husb);
- clear_bit(HCI_USB_TX_PROCESS, &husb->state);
- } else
- set_bit(HCI_USB_TX_WAKEUP, &husb->state);
-}
-
-/* Send frames from HCI layer */
-static int hci_usb_send_frame(struct sk_buff *skb)
-{
- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
- struct hci_usb *husb;
-
- if (!hdev) {
- BT_ERR("frame for uknown device (hdev=NULL)");
- return -ENODEV;
- }
-
- if (!test_bit(HCI_RUNNING, &hdev->flags))
- return -EBUSY;
-
- BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
-
- husb = (struct hci_usb *) hdev->driver_data;
-
- switch (bt_cb(skb)->pkt_type) {
- case HCI_COMMAND_PKT:
- hdev->stat.cmd_tx++;
- break;
-
- case HCI_ACLDATA_PKT:
- hdev->stat.acl_tx++;
- break;
-
-#ifdef CONFIG_BT_HCIUSB_SCO
- case HCI_SCODATA_PKT:
- hdev->stat.sco_tx++;
- break;
-#endif
-
- default:
- kfree_skb(skb);
- return 0;
- }
-
- read_lock(&husb->completion_lock);
-
- skb_queue_tail(__transmit_q(husb, bt_cb(skb)->pkt_type), skb);
- hci_usb_tx_wakeup(husb);
-
- read_unlock(&husb->completion_lock);
- return 0;
-}
-
-static void hci_usb_rx_complete(struct urb *urb)
-{
- struct _urb *_urb = container_of(urb, struct _urb, urb);
- struct hci_usb *husb = (void *) urb->context;
- struct hci_dev *hdev = husb->hdev;
- int err, count = urb->actual_length;
-
- BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
- _urb->type, urb->status, count, urb->transfer_flags);
-
- read_lock(&husb->completion_lock);
-
- if (!test_bit(HCI_RUNNING, &hdev->flags))
- goto unlock;
-
- if (urb->status || !count)
- goto resubmit;
-
- if (_urb->type == HCI_SCODATA_PKT) {
-#ifdef CONFIG_BT_HCIUSB_SCO
- int i;
- for (i=0; i < urb->number_of_packets; i++) {
- BT_DBG("desc %d status %d offset %d len %d", i,
- urb->iso_frame_desc[i].status,
- urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].actual_length);
-
- if (!urb->iso_frame_desc[i].status) {
- husb->hdev->stat.byte_rx += urb->iso_frame_desc[i].actual_length;
- hci_recv_fragment(husb->hdev, _urb->type,
- urb->transfer_buffer + urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].actual_length);
- }
- }
-#else
- ;
-#endif
- } else {
- husb->hdev->stat.byte_rx += count;
- err = hci_recv_fragment(husb->hdev, _urb->type, urb->transfer_buffer, count);
- if (err < 0) {
- BT_ERR("%s corrupted packet: type %d count %d",
- husb->hdev->name, _urb->type, count);
- hdev->stat.err_rx++;
- }
- }
-
-resubmit:
- urb->dev = husb->udev;
- err = usb_submit_urb(urb, GFP_ATOMIC);
- BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
- _urb->type, err);
-
-unlock:
- read_unlock(&husb->completion_lock);
-}
-
-static void hci_usb_tx_complete(struct urb *urb)
-{
- struct _urb *_urb = container_of(urb, struct _urb, urb);
- struct hci_usb *husb = (void *) urb->context;
- struct hci_dev *hdev = husb->hdev;
-
- BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,
- urb->status, urb->transfer_flags);
-
- atomic_dec(__pending_tx(husb, _urb->type));
-
- urb->transfer_buffer = NULL;
- kfree_skb((struct sk_buff *) _urb->priv);
-
- if (!test_bit(HCI_RUNNING, &hdev->flags))
- return;
-
- if (!urb->status)
- hdev->stat.byte_tx += urb->transfer_buffer_length;
- else
- hdev->stat.err_tx++;
-
- read_lock(&husb->completion_lock);
-
- _urb_unlink(_urb);
- _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
-
- hci_usb_tx_wakeup(husb);
-
- read_unlock(&husb->completion_lock);
-}
-
-static void hci_usb_destruct(struct hci_dev *hdev)
-{
- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
-
- BT_DBG("%s", hdev->name);
-
- kfree(husb);
-}
-
-static void hci_usb_notify(struct hci_dev *hdev, unsigned int evt)
-{
- BT_DBG("%s evt %d", hdev->name, evt);
-}
-
-static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct usb_host_endpoint *bulk_out_ep = NULL;
- struct usb_host_endpoint *bulk_in_ep = NULL;
- struct usb_host_endpoint *intr_in_ep = NULL;
- struct usb_host_endpoint *ep;
- struct usb_host_interface *uif;
- struct usb_interface *isoc_iface;
- struct hci_usb *husb;
- struct hci_dev *hdev;
- int i, e, size, isoc_ifnum, isoc_alts;
-
- BT_DBG("udev %p intf %p", udev, intf);
-
- if (!id->driver_info) {
- const struct usb_device_id *match;
- match = usb_match_id(intf, blacklist_ids);
- if (match)
- id = match;
- }
-
- if (id->driver_info & HCI_IGNORE)
- return -ENODEV;
-
- if (ignore_dga && id->driver_info & HCI_DIGIANSWER)
- return -ENODEV;
-
- if (ignore_csr && id->driver_info & HCI_CSR)
- return -ENODEV;
-
- if (ignore_sniffer && id->driver_info & HCI_SNIFFER)
- return -ENODEV;
-
- if (intf->cur_altsetting->desc.bInterfaceNumber > 0)
- return -ENODEV;
-
- /* Find endpoints that we need */
- uif = intf->cur_altsetting;
- for (e = 0; e < uif->desc.bNumEndpoints; e++) {
- ep = &uif->endpoint[e];
-
- switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_INT:
- if (ep->desc.bEndpointAddress & USB_DIR_IN)
- intr_in_ep = ep;
- break;
-
- case USB_ENDPOINT_XFER_BULK:
- if (ep->desc.bEndpointAddress & USB_DIR_IN)
- bulk_in_ep = ep;
- else
- bulk_out_ep = ep;
- break;
- }
- }
-
- if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
- BT_DBG("Bulk endpoints not found");
- goto done;
- }
-
- if (!(husb = kzalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
- BT_ERR("Can't allocate: control structure");
- goto done;
- }
-
- husb->udev = udev;
- husb->bulk_out_ep = bulk_out_ep;
- husb->bulk_in_ep = bulk_in_ep;
- husb->intr_in_ep = intr_in_ep;
-
- if (id->driver_info & HCI_DIGIANSWER)
- husb->ctrl_req = USB_TYPE_VENDOR;
- else
- husb->ctrl_req = USB_TYPE_CLASS;
-
- /* Find isochronous endpoints that we can use */
- size = 0;
- isoc_iface = NULL;
- isoc_alts = 0;
- isoc_ifnum = 1;
-
-#ifdef CONFIG_BT_HCIUSB_SCO
- if (isoc && !(id->driver_info & (HCI_BROKEN_ISOC | HCI_SNIFFER)))
- isoc_iface = usb_ifnum_to_if(udev, isoc_ifnum);
-
- if (isoc_iface) {
- int a;
- struct usb_host_endpoint *isoc_out_ep = NULL;
- struct usb_host_endpoint *isoc_in_ep = NULL;
-
- for (a = 0; a < isoc_iface->num_altsetting; a++) {
- uif = &isoc_iface->altsetting[a];
- for (e = 0; e < uif->desc.bNumEndpoints; e++) {
- ep = &uif->endpoint[e];
-
- switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_ISOC:
- if (le16_to_cpu(ep->desc.wMaxPacketSize) < size ||
- uif->desc.bAlternateSetting != isoc)
- break;
- size = le16_to_cpu(ep->desc.wMaxPacketSize);
-
- isoc_alts = uif->desc.bAlternateSetting;
-
- if (ep->desc.bEndpointAddress & USB_DIR_IN)
- isoc_in_ep = ep;
- else
- isoc_out_ep = ep;
- break;
- }
- }
- }
-
- if (!isoc_in_ep || !isoc_out_ep)
- BT_DBG("Isoc endpoints not found");
- else {
- BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
- if (usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb) != 0)
- BT_ERR("Can't claim isoc interface");
- else if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
- BT_ERR("Can't set isoc interface settings");
- husb->isoc_iface = isoc_iface;
- usb_driver_release_interface(&hci_usb_driver, isoc_iface);
- husb->isoc_iface = NULL;
- } else {
- husb->isoc_iface = isoc_iface;
- husb->isoc_in_ep = isoc_in_ep;
- husb->isoc_out_ep = isoc_out_ep;
- }
- }
- }
-#endif
-
- rwlock_init(&husb->completion_lock);
-
- for (i = 0; i < 4; i++) {
- skb_queue_head_init(&husb->transmit_q[i]);
- _urb_queue_init(&husb->pending_q[i]);
- _urb_queue_init(&husb->completed_q[i]);
- }
-
- /* Initialize and register HCI device */
- hdev = hci_alloc_dev();
- if (!hdev) {
- BT_ERR("Can't allocate HCI device");
- goto probe_error;
- }
-
- husb->hdev = hdev;
-
- hdev->type = HCI_USB;
- hdev->driver_data = husb;
- SET_HCIDEV_DEV(hdev, &intf->dev);
-
- hdev->open = hci_usb_open;
- hdev->close = hci_usb_close;
- hdev->flush = hci_usb_flush;
- hdev->send = hci_usb_send_frame;
- hdev->destruct = hci_usb_destruct;
- hdev->notify = hci_usb_notify;
-
- hdev->owner = THIS_MODULE;
-
- if (reset || id->driver_info & HCI_RESET)
- set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
-
- if (force_scofix || id->driver_info & HCI_WRONG_SCO_MTU) {
- if (!disable_scofix)
- set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
- }
-
- if (id->driver_info & HCI_SNIFFER) {
- if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
- set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
- }
-
- if (id->driver_info & HCI_BCM92035) {
- unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
- struct sk_buff *skb;
-
- skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
- if (skb) {
- memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
- skb_queue_tail(&hdev->driver_init, skb);
- }
- }
-
- if (hci_register_dev(hdev) < 0) {
- BT_ERR("Can't register HCI device");
- hci_free_dev(hdev);
- goto probe_error;
- }
-
- usb_set_intfdata(intf, husb);
- return 0;
-
-probe_error:
- if (husb->isoc_iface)
- usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
- kfree(husb);
-
-done:
- return -EIO;
-}
-
-static void hci_usb_disconnect(struct usb_interface *intf)
-{
- struct hci_usb *husb = usb_get_intfdata(intf);
- struct hci_dev *hdev;
-
- if (!husb || intf == husb->isoc_iface)
- return;
-
- usb_set_intfdata(intf, NULL);
- hdev = husb->hdev;
-
- BT_DBG("%s", hdev->name);
-
- hci_usb_close(hdev);
-
- if (husb->isoc_iface)
- usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
-
- if (hci_unregister_dev(hdev) < 0)
- BT_ERR("Can't unregister HCI device %s", hdev->name);
-
- hci_free_dev(hdev);
-}
-
-static int hci_usb_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct hci_usb *husb = usb_get_intfdata(intf);
- struct list_head killed;
- unsigned long flags;
- int i;
-
- if (!husb || intf == husb->isoc_iface)
- return 0;
-
- hci_suspend_dev(husb->hdev);
-
- INIT_LIST_HEAD(&killed);
-
- for (i = 0; i < 4; i++) {
- struct _urb_queue *q = &husb->pending_q[i];
- struct _urb *_urb, *_tmp;
-
- while ((_urb = _urb_dequeue(q))) {
- /* reset queue since _urb_dequeue sets it to NULL */
- _urb->queue = q;
- usb_kill_urb(&_urb->urb);
- list_add(&_urb->list, &killed);
- }
-
- spin_lock_irqsave(&q->lock, flags);
-
- list_for_each_entry_safe(_urb, _tmp, &killed, list) {
- list_move_tail(&_urb->list, &q->head);
- }
-
- spin_unlock_irqrestore(&q->lock, flags);
- }
-
- return 0;
-}
-
-static int hci_usb_resume(struct usb_interface *intf)
-{
- struct hci_usb *husb = usb_get_intfdata(intf);
- unsigned long flags;
- int i, err = 0;
-
- if (!husb || intf == husb->isoc_iface)
- return 0;
-
- for (i = 0; i < 4; i++) {
- struct _urb_queue *q = &husb->pending_q[i];
- struct _urb *_urb;
-
- spin_lock_irqsave(&q->lock, flags);
-
- list_for_each_entry(_urb, &q->head, list) {
- err = usb_submit_urb(&_urb->urb, GFP_ATOMIC);
- if (err)
- break;
- }
-
- spin_unlock_irqrestore(&q->lock, flags);
-
- if (err)
- return -EIO;
- }
-
- hci_resume_dev(husb->hdev);
-
- return 0;
-}
-
-static struct usb_driver hci_usb_driver = {
- .name = "hci_usb",
- .probe = hci_usb_probe,
- .disconnect = hci_usb_disconnect,
- .suspend = hci_usb_suspend,
- .resume = hci_usb_resume,
- .id_table = bluetooth_ids,
-};
-
-static int __init hci_usb_init(void)
-{
- int err;
-
- BT_INFO("HCI USB driver ver %s", VERSION);
-
- if ((err = usb_register(&hci_usb_driver)) < 0)
- BT_ERR("Failed to register HCI USB driver");
-
- return err;
-}
-
-static void __exit hci_usb_exit(void)
-{
- usb_deregister(&hci_usb_driver);
-}
-
-module_init(hci_usb_init);
-module_exit(hci_usb_exit);
-
-module_param(ignore_dga, bool, 0644);
-MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
-
-module_param(ignore_csr, bool, 0644);
-MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
-
-module_param(ignore_sniffer, bool, 0644);
-MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
-
-module_param(disable_scofix, bool, 0644);
-MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
-
-module_param(force_scofix, bool, 0644);
-MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
-
-module_param(reset, bool, 0644);
-MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
-
-#ifdef CONFIG_BT_HCIUSB_SCO
-module_param(isoc, int, 0644);
-MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support");
-#endif
-
-MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
-MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION);
-MODULE_VERSION(VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
deleted file mode 100644
index 8e659914523f..000000000000
--- a/drivers/bluetooth/hci_usb.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
- Copyright (C) 2000-2001 Qualcomm Incorporated
- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
- Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.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;
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
- SOFTWARE IS DISCLAIMED.
-*/
-
-/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
-#define HCI_DEV_CLASS 0xe0 /* Wireless class */
-#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
-#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
-
-#define HCI_IGNORE 0x01
-#define HCI_RESET 0x02
-#define HCI_DIGIANSWER 0x04
-#define HCI_CSR 0x08
-#define HCI_SNIFFER 0x10
-#define HCI_BCM92035 0x20
-#define HCI_BROKEN_ISOC 0x40
-#define HCI_WRONG_SCO_MTU 0x80
-
-#define HCI_MAX_IFACE_NUM 3
-
-#define HCI_MAX_BULK_TX 4
-#define HCI_MAX_BULK_RX 1
-
-#define HCI_MAX_ISOC_RX 2
-#define HCI_MAX_ISOC_TX 2
-
-#define HCI_MAX_ISOC_FRAMES 10
-
-struct _urb_queue {
- struct list_head head;
- spinlock_t lock;
-};
-
-struct _urb {
- struct list_head list;
- struct _urb_queue *queue;
- int type;
- void *priv;
- struct urb urb;
-};
-
-static inline void _urb_queue_init(struct _urb_queue *q)
-{
- INIT_LIST_HEAD(&q->head);
- spin_lock_init(&q->lock);
-}
-
-static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
-{
- unsigned long flags;
- spin_lock_irqsave(&q->lock, flags);
- /* _urb_unlink needs to know which spinlock to use, thus smp_mb(). */
- _urb->queue = q; smp_mb(); list_add(&_urb->list, &q->head);
- spin_unlock_irqrestore(&q->lock, flags);
-}
-
-static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
-{
- unsigned long flags;
- spin_lock_irqsave(&q->lock, flags);
- /* _urb_unlink needs to know which spinlock to use, thus smp_mb(). */
- _urb->queue = q; smp_mb(); list_add_tail(&_urb->list, &q->head);
- spin_unlock_irqrestore(&q->lock, flags);
-}
-
-static inline void _urb_unlink(struct _urb *_urb)
-{
- struct _urb_queue *q;
- unsigned long flags;
-
- smp_mb();
- q = _urb->queue;
- /* If q is NULL, it will die at easy-to-debug NULL pointer dereference.
- No need to BUG(). */
- spin_lock_irqsave(&q->lock, flags);
- list_del(&_urb->list); _urb->queue = NULL;
- spin_unlock_irqrestore(&q->lock, flags);
-}
-
-struct hci_usb {
- struct hci_dev *hdev;
-
- unsigned long state;
-
- struct usb_device *udev;
-
- struct usb_host_endpoint *bulk_in_ep;
- struct usb_host_endpoint *bulk_out_ep;
- struct usb_host_endpoint *intr_in_ep;
-
- struct usb_interface *isoc_iface;
- struct usb_host_endpoint *isoc_out_ep;
- struct usb_host_endpoint *isoc_in_ep;
-
- __u8 ctrl_req;
-
- struct sk_buff_head transmit_q[4];
-
- rwlock_t completion_lock;
-
- atomic_t pending_tx[4]; /* Number of pending requests */
- struct _urb_queue pending_q[4]; /* Pending requests */
- struct _urb_queue completed_q[4]; /* Completed requests */
-};
-
-/* States */
-#define HCI_USB_TX_PROCESS 1
-#define HCI_USB_TX_WAKEUP 2
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 7320a71b6368..0bbefba6469c 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -40,11 +40,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCIVHCI_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.2"
static int minor = MISC_DYNAMIC_MINOR;
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 7d2e91cccb13..cceace61ef28 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -1712,29 +1712,30 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai)
return 0;
}
-static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
unsigned char buf[21], *base;
struct dvd_layer *layer;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
int ret, layer_num = s->physical.layer_num;
if (layer_num >= DVD_LAYERS)
return -EINVAL;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[6] = layer_num;
- cgc.cmd[7] = s->type;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[6] = layer_num;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[9] = cgc->buflen & 0xff;
/*
* refrain from reporting errors on non-existing layers (mainly)
*/
- cgc.quiet = 1;
+ cgc->quiet = 1;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
return ret;
base = &buf[4];
@@ -1762,21 +1763,22 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s)
return 0;
}
-static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret;
u_char buf[8];
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[6] = s->copyright.layer_num;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = cgc.buflen >> 8;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ init_cdrom_command(cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[6] = s->copyright.layer_num;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = cgc->buflen >> 8;
+ cgc->cmd[9] = cgc->buflen & 0xff;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
return ret;
s->copyright.cpst = buf[4];
@@ -1785,79 +1787,89 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s)
return 0;
}
-static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret, size;
u_char *buf;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->disckey.value) + 4;
- if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = size >> 8;
- cgc.cmd[9] = size & 0xff;
- cgc.cmd[10] = s->disckey.agid << 6;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = size >> 8;
+ cgc->cmd[9] = size & 0xff;
+ cgc->cmd[10] = s->disckey.agid << 6;
- if (!(ret = cdo->generic_packet(cdi, &cgc)))
+ ret = cdo->generic_packet(cdi, cgc);
+ if (!ret)
memcpy(s->disckey.value, &buf[4], sizeof(s->disckey.value));
kfree(buf);
return ret;
}
-static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
- int ret;
- u_char buf[4 + 188];
- struct packet_command cgc;
+ int ret, size = 4 + 188;
+ u_char *buf;
struct cdrom_device_ops *cdo = cdi->ops;
- init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[9] = cgc.buflen & 0xff;
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
- if ((ret = cdo->generic_packet(cdi, &cgc)))
- return ret;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[9] = cgc->buflen & 0xff;
+
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
+ goto out;
s->bca.len = buf[0] << 8 | buf[1];
if (s->bca.len < 12 || s->bca.len > 188) {
cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
memcpy(s->bca.value, &buf[4], s->bca.len);
-
- return 0;
+ ret = 0;
+out:
+ kfree(buf);
+ return ret;
}
-static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
int ret = 0, size;
u_char *buf;
- struct packet_command cgc;
struct cdrom_device_ops *cdo = cdi->ops;
size = sizeof(s->manufact.value) + 4;
- if ((buf = kmalloc(size, GFP_KERNEL)) == NULL)
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
- cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE;
- cgc.cmd[7] = s->type;
- cgc.cmd[8] = size >> 8;
- cgc.cmd[9] = size & 0xff;
+ init_cdrom_command(cgc, buf, size, CGC_DATA_READ);
+ cgc->cmd[0] = GPCMD_READ_DVD_STRUCTURE;
+ cgc->cmd[7] = s->type;
+ cgc->cmd[8] = size >> 8;
+ cgc->cmd[9] = size & 0xff;
- if ((ret = cdo->generic_packet(cdi, &cgc))) {
- kfree(buf);
- return ret;
- }
+ ret = cdo->generic_packet(cdi, cgc);
+ if (ret)
+ goto out;
s->manufact.len = buf[0] << 8 | buf[1];
if (s->manufact.len < 0 || s->manufact.len > 2048) {
@@ -1868,27 +1880,29 @@ static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s)
memcpy(s->manufact.value, &buf[4], s->manufact.len);
}
+out:
kfree(buf);
return ret;
}
-static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s)
+static int dvd_read_struct(struct cdrom_device_info *cdi, dvd_struct *s,
+ struct packet_command *cgc)
{
switch (s->type) {
case DVD_STRUCT_PHYSICAL:
- return dvd_read_physical(cdi, s);
+ return dvd_read_physical(cdi, s, cgc);
case DVD_STRUCT_COPYRIGHT:
- return dvd_read_copyright(cdi, s);
+ return dvd_read_copyright(cdi, s, cgc);
case DVD_STRUCT_DISCKEY:
- return dvd_read_disckey(cdi, s);
+ return dvd_read_disckey(cdi, s, cgc);
case DVD_STRUCT_BCA:
- return dvd_read_bca(cdi, s);
+ return dvd_read_bca(cdi, s, cgc);
case DVD_STRUCT_MANUFACT:
- return dvd_read_manufact(cdi, s);
+ return dvd_read_manufact(cdi, s, cgc);
default:
cdinfo(CD_WARNING, ": Invalid DVD structure read requested (%d)\n",
@@ -2787,271 +2801,360 @@ static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
return cdo->generic_packet(cdi, &cgc);
}
-static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
- unsigned long arg)
-{
- struct cdrom_device_ops *cdo = cdi->ops;
- struct packet_command cgc;
+static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc,
+ int cmd)
+{
struct request_sense sense;
- unsigned char buffer[32];
- int ret = 0;
-
- memset(&cgc, 0, sizeof(cgc));
+ struct cdrom_msf msf;
+ int blocksize = 0, format = 0, lba;
+ int ret;
- /* build a unified command and queue it through
- cdo->generic_packet() */
switch (cmd) {
case CDROMREADRAW:
+ blocksize = CD_FRAMESIZE_RAW;
+ break;
case CDROMREADMODE1:
- case CDROMREADMODE2: {
- struct cdrom_msf msf;
- int blocksize = 0, format = 0, lba;
-
- switch (cmd) {
- case CDROMREADRAW:
- blocksize = CD_FRAMESIZE_RAW;
- break;
- case CDROMREADMODE1:
- blocksize = CD_FRAMESIZE;
- format = 2;
- break;
- case CDROMREADMODE2:
- blocksize = CD_FRAMESIZE_RAW0;
- break;
- }
- IOCTL_IN(arg, struct cdrom_msf, msf);
- lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0);
- /* FIXME: we need upper bound checking, too!! */
- if (lba < 0)
- return -EINVAL;
- cgc.buffer = kmalloc(blocksize, GFP_KERNEL);
- if (cgc.buffer == NULL)
- return -ENOMEM;
- memset(&sense, 0, sizeof(sense));
- cgc.sense = &sense;
- cgc.data_direction = CGC_DATA_READ;
- ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
- if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) {
- /*
- * SCSI-II devices are not required to support
- * READ_CD, so let's try switching block size
- */
- /* FIXME: switch back again... */
- if ((ret = cdrom_switch_blocksize(cdi, blocksize))) {
- kfree(cgc.buffer);
- return ret;
- }
- cgc.sense = NULL;
- ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1);
- ret |= cdrom_switch_blocksize(cdi, blocksize);
- }
- if (!ret && copy_to_user((char __user *)arg, cgc.buffer, blocksize))
- ret = -EFAULT;
- kfree(cgc.buffer);
+ blocksize = CD_FRAMESIZE;
+ format = 2;
+ break;
+ case CDROMREADMODE2:
+ blocksize = CD_FRAMESIZE_RAW0;
+ break;
+ }
+ IOCTL_IN(arg, struct cdrom_msf, msf);
+ lba = msf_to_lba(msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
+ /* FIXME: we need upper bound checking, too!! */
+ if (lba < 0)
+ return -EINVAL;
+
+ cgc->buffer = kmalloc(blocksize, GFP_KERNEL);
+ if (cgc->buffer == NULL)
+ return -ENOMEM;
+
+ memset(&sense, 0, sizeof(sense));
+ cgc->sense = &sense;
+ cgc->data_direction = CGC_DATA_READ;
+ ret = cdrom_read_block(cdi, cgc, lba, 1, format, blocksize);
+ if (ret && sense.sense_key == 0x05 &&
+ sense.asc == 0x20 &&
+ sense.ascq == 0x00) {
+ /*
+ * SCSI-II devices are not required to support
+ * READ_CD, so let's try switching block size
+ */
+ /* FIXME: switch back again... */
+ ret = cdrom_switch_blocksize(cdi, blocksize);
+ if (ret)
+ goto out;
+ cgc->sense = NULL;
+ ret = cdrom_read_cd(cdi, cgc, lba, blocksize, 1);
+ ret |= cdrom_switch_blocksize(cdi, blocksize);
+ }
+ if (!ret && copy_to_user(arg, cgc->buffer, blocksize))
+ ret = -EFAULT;
+out:
+ kfree(cgc->buffer);
+ return ret;
+}
+
+static noinline int mmc_ioctl_cdrom_read_audio(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ struct cdrom_read_audio ra;
+ int lba;
+
+ IOCTL_IN(arg, struct cdrom_read_audio, ra);
+
+ if (ra.addr_format == CDROM_MSF)
+ lba = msf_to_lba(ra.addr.msf.minute,
+ ra.addr.msf.second,
+ ra.addr.msf.frame);
+ else if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+ else
+ return -EINVAL;
+
+ /* FIXME: we need upper bound checking, too!! */
+ if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES)
+ return -EINVAL;
+
+ return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes);
+}
+
+static noinline int mmc_ioctl_cdrom_subchannel(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ struct cdrom_subchnl q;
+ u_char requested, back;
+ IOCTL_IN(arg, struct cdrom_subchnl, q);
+ requested = q.cdsc_format;
+ if (!((requested == CDROM_MSF) ||
+ (requested == CDROM_LBA)))
+ return -EINVAL;
+ q.cdsc_format = CDROM_MSF;
+ ret = cdrom_read_subchannel(cdi, &q, 0);
+ if (ret)
return ret;
- }
- case CDROMREADAUDIO: {
- struct cdrom_read_audio ra;
- int lba;
-
- IOCTL_IN(arg, struct cdrom_read_audio, ra);
-
- if (ra.addr_format == CDROM_MSF)
- lba = msf_to_lba(ra.addr.msf.minute,
- ra.addr.msf.second,
- ra.addr.msf.frame);
- else if (ra.addr_format == CDROM_LBA)
- lba = ra.addr.lba;
- else
- return -EINVAL;
+ back = q.cdsc_format; /* local copy */
+ sanitize_format(&q.cdsc_absaddr, &back, requested);
+ sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
+ IOCTL_OUT(arg, struct cdrom_subchnl, q);
+ /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
+ return 0;
+}
- /* FIXME: we need upper bound checking, too!! */
- if (lba < 0 || ra.nframes <= 0 || ra.nframes > CD_FRAMES)
- return -EINVAL;
+static noinline int mmc_ioctl_cdrom_play_msf(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ struct cdrom_msf msf;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
+ IOCTL_IN(arg, struct cdrom_msf, msf);
+ cgc->cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+ cgc->cmd[3] = msf.cdmsf_min0;
+ cgc->cmd[4] = msf.cdmsf_sec0;
+ cgc->cmd[5] = msf.cdmsf_frame0;
+ cgc->cmd[6] = msf.cdmsf_min1;
+ cgc->cmd[7] = msf.cdmsf_sec1;
+ cgc->cmd[8] = msf.cdmsf_frame1;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- return cdrom_read_cdda(cdi, ra.buf, lba, ra.nframes);
- }
- case CDROMSUBCHNL: {
- struct cdrom_subchnl q;
- u_char requested, back;
- IOCTL_IN(arg, struct cdrom_subchnl, q);
- requested = q.cdsc_format;
- if (!((requested == CDROM_MSF) ||
- (requested == CDROM_LBA)))
- return -EINVAL;
- q.cdsc_format = CDROM_MSF;
- if ((ret = cdrom_read_subchannel(cdi, &q, 0)))
- return ret;
- back = q.cdsc_format; /* local copy */
- sanitize_format(&q.cdsc_absaddr, &back, requested);
- sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
- IOCTL_OUT(arg, struct cdrom_subchnl, q);
- /* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */
- return 0;
- }
- case CDROMPLAYMSF: {
- struct cdrom_msf msf;
- cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
- IOCTL_IN(arg, struct cdrom_msf, msf);
- cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
- cgc.cmd[3] = msf.cdmsf_min0;
- cgc.cmd[4] = msf.cdmsf_sec0;
- cgc.cmd[5] = msf.cdmsf_frame0;
- cgc.cmd[6] = msf.cdmsf_min1;
- cgc.cmd[7] = msf.cdmsf_sec1;
- cgc.cmd[8] = msf.cdmsf_frame1;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
- case CDROMPLAYBLK: {
- struct cdrom_blk blk;
- cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
- IOCTL_IN(arg, struct cdrom_blk, blk);
- cgc.cmd[0] = GPCMD_PLAY_AUDIO_10;
- cgc.cmd[2] = (blk.from >> 24) & 0xff;
- cgc.cmd[3] = (blk.from >> 16) & 0xff;
- cgc.cmd[4] = (blk.from >> 8) & 0xff;
- cgc.cmd[5] = blk.from & 0xff;
- cgc.cmd[7] = (blk.len >> 8) & 0xff;
- cgc.cmd[8] = blk.len & 0xff;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
- case CDROMVOLCTRL:
- case CDROMVOLREAD: {
- struct cdrom_volctrl volctrl;
- char mask[sizeof(buffer)];
- unsigned short offset;
+static noinline int mmc_ioctl_cdrom_play_blk(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ struct cdrom_blk blk;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
+ IOCTL_IN(arg, struct cdrom_blk, blk);
+ cgc->cmd[0] = GPCMD_PLAY_AUDIO_10;
+ cgc->cmd[2] = (blk.from >> 24) & 0xff;
+ cgc->cmd[3] = (blk.from >> 16) & 0xff;
+ cgc->cmd[4] = (blk.from >> 8) & 0xff;
+ cgc->cmd[5] = blk.from & 0xff;
+ cgc->cmd[7] = (blk.len >> 8) & 0xff;
+ cgc->cmd[8] = blk.len & 0xff;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
+
+static noinline int mmc_ioctl_cdrom_volume(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc,
+ unsigned int cmd)
+{
+ struct cdrom_volctrl volctrl;
+ unsigned char buffer[32];
+ char mask[sizeof(buffer)];
+ unsigned short offset;
+ int ret;
- cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
+ cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");
- IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
+ IOCTL_IN(arg, struct cdrom_volctrl, volctrl);
- cgc.buffer = buffer;
- cgc.buflen = 24;
- if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
- return ret;
+ cgc->buffer = buffer;
+ cgc->buflen = 24;
+ ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 0);
+ if (ret)
+ return ret;
- /* originally the code depended on buffer[1] to determine
- how much data is available for transfer. buffer[1] is
- unfortunately ambigious and the only reliable way seem
- to be to simply skip over the block descriptor... */
- offset = 8 + be16_to_cpu(*(__be16 *)(buffer+6));
-
- if (offset + 16 > sizeof(buffer))
- return -E2BIG;
-
- if (offset + 16 > cgc.buflen) {
- cgc.buflen = offset+16;
- ret = cdrom_mode_sense(cdi, &cgc,
- GPMODE_AUDIO_CTL_PAGE, 0);
- if (ret)
- return ret;
- }
+ /* originally the code depended on buffer[1] to determine
+ how much data is available for transfer. buffer[1] is
+ unfortunately ambigious and the only reliable way seem
+ to be to simply skip over the block descriptor... */
+ offset = 8 + be16_to_cpu(*(__be16 *)(buffer + 6));
+
+ if (offset + 16 > sizeof(buffer))
+ return -E2BIG;
+
+ if (offset + 16 > cgc->buflen) {
+ cgc->buflen = offset + 16;
+ ret = cdrom_mode_sense(cdi, cgc,
+ GPMODE_AUDIO_CTL_PAGE, 0);
+ if (ret)
+ return ret;
+ }
- /* sanity check */
- if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
- buffer[offset+1] < 14)
- return -EINVAL;
+ /* sanity check */
+ if ((buffer[offset] & 0x3f) != GPMODE_AUDIO_CTL_PAGE ||
+ buffer[offset + 1] < 14)
+ return -EINVAL;
- /* now we have the current volume settings. if it was only
- a CDROMVOLREAD, return these values */
- if (cmd == CDROMVOLREAD) {
- volctrl.channel0 = buffer[offset+9];
- volctrl.channel1 = buffer[offset+11];
- volctrl.channel2 = buffer[offset+13];
- volctrl.channel3 = buffer[offset+15];
- IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
- return 0;
- }
+ /* now we have the current volume settings. if it was only
+ a CDROMVOLREAD, return these values */
+ if (cmd == CDROMVOLREAD) {
+ volctrl.channel0 = buffer[offset+9];
+ volctrl.channel1 = buffer[offset+11];
+ volctrl.channel2 = buffer[offset+13];
+ volctrl.channel3 = buffer[offset+15];
+ IOCTL_OUT(arg, struct cdrom_volctrl, volctrl);
+ return 0;
+ }
- /* get the volume mask */
- cgc.buffer = mask;
- if ((ret = cdrom_mode_sense(cdi, &cgc,
- GPMODE_AUDIO_CTL_PAGE, 1)))
- return ret;
+ /* get the volume mask */
+ cgc->buffer = mask;
+ ret = cdrom_mode_sense(cdi, cgc, GPMODE_AUDIO_CTL_PAGE, 1);
+ if (ret)
+ return ret;
- buffer[offset+9] = volctrl.channel0 & mask[offset+9];
- buffer[offset+11] = volctrl.channel1 & mask[offset+11];
- buffer[offset+13] = volctrl.channel2 & mask[offset+13];
- buffer[offset+15] = volctrl.channel3 & mask[offset+15];
+ buffer[offset + 9] = volctrl.channel0 & mask[offset + 9];
+ buffer[offset + 11] = volctrl.channel1 & mask[offset + 11];
+ buffer[offset + 13] = volctrl.channel2 & mask[offset + 13];
+ buffer[offset + 15] = volctrl.channel3 & mask[offset + 15];
- /* set volume */
- cgc.buffer = buffer + offset - 8;
- memset(cgc.buffer, 0, 8);
- return cdrom_mode_select(cdi, &cgc);
- }
+ /* set volume */
+ cgc->buffer = buffer + offset - 8;
+ memset(cgc->buffer, 0, 8);
+ return cdrom_mode_select(cdi, cgc);
+}
- case CDROMSTART:
- case CDROMSTOP: {
- cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
- cgc.cmd[0] = GPCMD_START_STOP_UNIT;
- cgc.cmd[1] = 1;
- cgc.cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
+static noinline int mmc_ioctl_cdrom_start_stop(struct cdrom_device_info *cdi,
+ struct packet_command *cgc,
+ int cmd)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ cdinfo(CD_DO_IOCTL, "entering CDROMSTART/CDROMSTOP\n");
+ cgc->cmd[0] = GPCMD_START_STOP_UNIT;
+ cgc->cmd[1] = 1;
+ cgc->cmd[4] = (cmd == CDROMSTART) ? 1 : 0;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- case CDROMPAUSE:
- case CDROMRESUME: {
- cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
- cgc.cmd[0] = GPCMD_PAUSE_RESUME;
- cgc.cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
- cgc.data_direction = CGC_DATA_NONE;
- return cdo->generic_packet(cdi, &cgc);
- }
+static noinline int mmc_ioctl_cdrom_pause_resume(struct cdrom_device_info *cdi,
+ struct packet_command *cgc,
+ int cmd)
+{
+ struct cdrom_device_ops *cdo = cdi->ops;
+ cdinfo(CD_DO_IOCTL, "entering CDROMPAUSE/CDROMRESUME\n");
+ cgc->cmd[0] = GPCMD_PAUSE_RESUME;
+ cgc->cmd[8] = (cmd == CDROMRESUME) ? 1 : 0;
+ cgc->data_direction = CGC_DATA_NONE;
+ return cdo->generic_packet(cdi, cgc);
+}
- case DVD_READ_STRUCT: {
- dvd_struct *s;
- int size = sizeof(dvd_struct);
- if (!CDROM_CAN(CDC_DVD))
- return -ENOSYS;
- if ((s = kmalloc(size, GFP_KERNEL)) == NULL)
- return -ENOMEM;
- cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
- if (copy_from_user(s, (dvd_struct __user *)arg, size)) {
- kfree(s);
- return -EFAULT;
- }
- if ((ret = dvd_read_struct(cdi, s))) {
- kfree(s);
- return ret;
- }
- if (copy_to_user((dvd_struct __user *)arg, s, size))
- ret = -EFAULT;
+static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi,
+ void __user *arg,
+ struct packet_command *cgc)
+{
+ int ret;
+ dvd_struct *s;
+ int size = sizeof(dvd_struct);
+
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+
+ s = kmalloc(size, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ cdinfo(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
+ if (copy_from_user(s, arg, size)) {
kfree(s);
+ return -EFAULT;
+ }
+
+ ret = dvd_read_struct(cdi, s, cgc);
+ if (ret)
+ goto out;
+
+ if (copy_to_user(arg, s, size))
+ ret = -EFAULT;
+out:
+ kfree(s);
+ return ret;
+}
+
+static noinline int mmc_ioctl_dvd_auth(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ dvd_authinfo ai;
+ if (!CDROM_CAN(CDC_DVD))
+ return -ENOSYS;
+ cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
+ IOCTL_IN(arg, dvd_authinfo, ai);
+ ret = dvd_do_auth(cdi, &ai);
+ if (ret)
return ret;
- }
+ IOCTL_OUT(arg, dvd_authinfo, ai);
+ return 0;
+}
- case DVD_AUTH: {
- dvd_authinfo ai;
- if (!CDROM_CAN(CDC_DVD))
- return -ENOSYS;
- cdinfo(CD_DO_IOCTL, "entering DVD_AUTH\n");
- IOCTL_IN(arg, dvd_authinfo, ai);
- if ((ret = dvd_do_auth (cdi, &ai)))
- return ret;
- IOCTL_OUT(arg, dvd_authinfo, ai);
- return 0;
- }
+static noinline int mmc_ioctl_cdrom_next_writable(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ long next = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
+ ret = cdrom_get_next_writable(cdi, &next);
+ if (ret)
+ return ret;
+ IOCTL_OUT(arg, long, next);
+ return 0;
+}
- case CDROM_NEXT_WRITABLE: {
- long next = 0;
- cdinfo(CD_DO_IOCTL, "entering CDROM_NEXT_WRITABLE\n");
- if ((ret = cdrom_get_next_writable(cdi, &next)))
- return ret;
- IOCTL_OUT(arg, long, next);
- return 0;
- }
- case CDROM_LAST_WRITTEN: {
- long last = 0;
- cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
- if ((ret = cdrom_get_last_written(cdi, &last)))
- return ret;
- IOCTL_OUT(arg, long, last);
- return 0;
- }
- } /* switch */
+static noinline int mmc_ioctl_cdrom_last_written(struct cdrom_device_info *cdi,
+ void __user *arg)
+{
+ int ret;
+ long last = 0;
+ cdinfo(CD_DO_IOCTL, "entering CDROM_LAST_WRITTEN\n");
+ ret = cdrom_get_last_written(cdi, &last);
+ if (ret)
+ return ret;
+ IOCTL_OUT(arg, long, last);
+ return 0;
+}
+
+static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
+ unsigned long arg)
+{
+ struct packet_command cgc;
+ void __user *userptr = (void __user *)arg;
+
+ memset(&cgc, 0, sizeof(cgc));
+
+ /* build a unified command and queue it through
+ cdo->generic_packet() */
+ switch (cmd) {
+ case CDROMREADRAW:
+ case CDROMREADMODE1:
+ case CDROMREADMODE2:
+ return mmc_ioctl_cdrom_read_data(cdi, userptr, &cgc, cmd);
+ case CDROMREADAUDIO:
+ return mmc_ioctl_cdrom_read_audio(cdi, userptr);
+ case CDROMSUBCHNL:
+ return mmc_ioctl_cdrom_subchannel(cdi, userptr);
+ case CDROMPLAYMSF:
+ return mmc_ioctl_cdrom_play_msf(cdi, userptr, &cgc);
+ case CDROMPLAYBLK:
+ return mmc_ioctl_cdrom_play_blk(cdi, userptr, &cgc);
+ case CDROMVOLCTRL:
+ case CDROMVOLREAD:
+ return mmc_ioctl_cdrom_volume(cdi, userptr, &cgc, cmd);
+ case CDROMSTART:
+ case CDROMSTOP:
+ return mmc_ioctl_cdrom_start_stop(cdi, &cgc, cmd);
+ case CDROMPAUSE:
+ case CDROMRESUME:
+ return mmc_ioctl_cdrom_pause_resume(cdi, &cgc, cmd);
+ case DVD_READ_STRUCT:
+ return mmc_ioctl_dvd_read_struct(cdi, userptr, &cgc);
+ case DVD_AUTH:
+ return mmc_ioctl_dvd_auth(cdi, userptr);
+ case CDROM_NEXT_WRITABLE:
+ return mmc_ioctl_cdrom_next_writable(cdi, userptr);
+ case CDROM_LAST_WRITTEN:
+ return mmc_ioctl_cdrom_last_written(cdi, userptr);
+ }
return -ENOTTY;
}
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 43d6ba83a191..c602b547cc6e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -622,6 +622,16 @@ config HVC_BEAT
help
Toshiba's Cell Reference Set Beat Console device driver
+config HVC_IUCV
+ bool "z/VM IUCV Hypervisor console support (VM only)"
+ depends on S390
+ select HVC_DRIVER
+ select IUCV
+ default y
+ help
+ This driver provides a Hypervisor console (HVC) back-end to access
+ a Linux (console) terminal via a z/VM IUCV communication path.
+
config HVC_XEN
bool "Xen Hypervisor Console support"
depends on XEN
@@ -631,6 +641,12 @@ config HVC_XEN
help
Xen virtual console device driver
+config HVC_UDBG
+ bool "udbg based fake hypervisor console"
+ depends on PPC && EXPERIMENTAL
+ select HVC_DRIVER
+ default n
+
config VIRTIO_CONSOLE
tristate "Virtio console"
depends on VIRTIO
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 438f71317c5c..9caf5b5ad1c0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -50,6 +50,8 @@ obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
+obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
+obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index 9cf6e9bb017e..c7714185f831 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -40,6 +40,8 @@
#define PCI_DEVICE_ID_INTEL_Q45_IG 0x2E12
#define PCI_DEVICE_ID_INTEL_G45_HB 0x2E20
#define PCI_DEVICE_ID_INTEL_G45_IG 0x2E22
+#define PCI_DEVICE_ID_INTEL_G41_HB 0x2E30
+#define PCI_DEVICE_ID_INTEL_G41_IG 0x2E32
/* cover 915 and 945 variants */
#define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
@@ -63,7 +65,8 @@
#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_E_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q45_HB || \
agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \
- agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB)
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \
+ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB)
extern int agp_memory_reserved;
@@ -1196,6 +1199,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
case PCI_DEVICE_ID_INTEL_IGD_E_HB:
case PCI_DEVICE_ID_INTEL_Q45_HB:
case PCI_DEVICE_ID_INTEL_G45_HB:
+ case PCI_DEVICE_ID_INTEL_G41_HB:
*gtt_offset = *gtt_size = MB(2);
break;
default:
@@ -2156,13 +2160,15 @@ static const struct intel_driver_description {
{ PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33",
NULL, &intel_g33_driver },
{ PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0,
- "Mobile Intel? GM45 Express", NULL, &intel_i965_driver },
+ "Mobile Intel® GM45 Express", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_IGD_E_HB, PCI_DEVICE_ID_INTEL_IGD_E_IG, 0,
"Intel Integrated Graphics Device", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0,
"Q45/Q43", NULL, &intel_i965_driver },
{ PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0,
"G45/G43", NULL, &intel_i965_driver },
+ { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0,
+ "G41", NULL, &intel_i965_driver },
{ 0, 0, 0, NULL, NULL, NULL }
};
@@ -2360,6 +2366,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
ID(PCI_DEVICE_ID_INTEL_IGD_E_HB),
ID(PCI_DEVICE_ID_INTEL_Q45_HB),
ID(PCI_DEVICE_ID_INTEL_G45_HB),
+ ID(PCI_DEVICE_ID_INTEL_G41_HB),
{ }
};
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index 456f54db73e2..977dfb1096a0 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -60,6 +60,8 @@ struct bsr_dev {
unsigned bsr_num; /* bsr id number for its type */
int bsr_minor;
+ struct list_head bsr_list;
+
dev_t bsr_dev;
struct cdev bsr_cdev;
struct device *bsr_device;
@@ -67,8 +69,8 @@ struct bsr_dev {
};
-static unsigned num_bsr_devs;
-static struct bsr_dev *bsr_devs;
+static unsigned total_bsr_devs;
+static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
static struct class *bsr_class;
static int bsr_major;
@@ -146,24 +148,25 @@ const static struct file_operations bsr_fops = {
static void bsr_cleanup_devs(void)
{
- int i;
- for (i=0 ; i < num_bsr_devs; i++) {
- struct bsr_dev *cur = bsr_devs + i;
+ struct bsr_dev *cur, *n;
+
+ list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
if (cur->bsr_device) {
cdev_del(&cur->bsr_cdev);
device_del(cur->bsr_device);
}
+ list_del(&cur->bsr_list);
+ kfree(cur);
}
-
- kfree(bsr_devs);
}
-static int bsr_create_devs(struct device_node *bn)
+static int bsr_add_node(struct device_node *bn)
{
- int bsr_stride_len, bsr_bytes_len;
+ int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
const u32 *bsr_stride;
const u32 *bsr_bytes;
unsigned i;
+ int ret = -ENODEV;
bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
@@ -171,35 +174,36 @@ static int bsr_create_devs(struct device_node *bn)
if (!bsr_stride || !bsr_bytes ||
(bsr_stride_len != bsr_bytes_len)) {
printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
- return -ENODEV;
+ return ret;
}
num_bsr_devs = bsr_bytes_len / sizeof(u32);
- /* only a warning, its informational since we'll fail and exit */
- WARN_ON(num_bsr_devs > BSR_MAX_DEVS);
-
- bsr_devs = kzalloc(sizeof(struct bsr_dev) * num_bsr_devs, GFP_KERNEL);
- if (!bsr_devs)
- return -ENOMEM;
-
for (i = 0 ; i < num_bsr_devs; i++) {
- struct bsr_dev *cur = bsr_devs + i;
+ struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
+ GFP_KERNEL);
struct resource res;
int result;
+ if (!cur) {
+ printk(KERN_ERR "Unable to alloc bsr dev\n");
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
result = of_address_to_resource(bn, i, &res);
if (result < 0) {
- printk(KERN_ERR "bsr of-node has invalid reg property\n");
- goto out_err;
+ printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
+ kfree(cur);
+ continue;
}
- cur->bsr_minor = i;
+ cur->bsr_minor = i + total_bsr_devs;
cur->bsr_addr = res.start;
cur->bsr_len = res.end - res.start + 1;
cur->bsr_bytes = bsr_bytes[i];
cur->bsr_stride = bsr_stride[i];
- cur->bsr_dev = MKDEV(bsr_major, i);
+ cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
switch(cur->bsr_bytes) {
case 8:
@@ -220,14 +224,15 @@ static int bsr_create_devs(struct device_node *bn)
}
cur->bsr_num = bsr_types[cur->bsr_type];
- bsr_types[cur->bsr_type] = cur->bsr_num + 1;
snprintf(cur->bsr_name, 32, "bsr%d_%d",
cur->bsr_bytes, cur->bsr_num);
cdev_init(&cur->bsr_cdev, &bsr_fops);
result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
- if (result)
+ if (result) {
+ kfree(cur);
goto out_err;
+ }
cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
cur, cur->bsr_name);
@@ -235,16 +240,37 @@ static int bsr_create_devs(struct device_node *bn)
printk(KERN_ERR "device_create failed for %s\n",
cur->bsr_name);
cdev_del(&cur->bsr_cdev);
+ kfree(cur);
goto out_err;
}
+
+ bsr_types[cur->bsr_type] = cur->bsr_num + 1;
+ list_add_tail(&cur->bsr_list, &bsr_devs);
}
+ total_bsr_devs += num_bsr_devs;
+
return 0;
out_err:
bsr_cleanup_devs();
- return -ENODEV;
+ return ret;
+}
+
+static int bsr_create_devs(struct device_node *bn)
+{
+ int ret;
+
+ while (bn) {
+ ret = bsr_add_node(bn);
+ if (ret) {
+ of_node_put(bn);
+ return ret;
+ }
+ bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
+ }
+ return 0;
}
static int __init bsr_init(void)
@@ -254,7 +280,7 @@ static int __init bsr_init(void)
int ret = -ENODEV;
int result;
- np = of_find_compatible_node(NULL, "ibm,bsr", "ibm,bsr");
+ np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
if (!np)
goto out_err;
@@ -272,10 +298,10 @@ static int __init bsr_init(void)
goto out_err_2;
}
- if ((ret = bsr_create_devs(np)) < 0)
+ if ((ret = bsr_create_devs(np)) < 0) {
+ np = NULL;
goto out_err_3;
-
- of_node_put(np);
+ }
return 0;
diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c
index 74e9cd81b5b2..61f0146e215d 100644
--- a/drivers/char/ds1620.c
+++ b/drivers/char/ds1620.c
@@ -43,52 +43,51 @@ static const char *fan_state[] = { "off", "on", "on (hardwired)" };
* chance that the WaveArtist driver could touch these bits to
* enable or disable the speaker.
*/
-extern spinlock_t gpio_lock;
extern unsigned int system_rev;
static inline void netwinder_ds1620_set_clk(int clk)
{
- gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
+ nw_gpio_modify_op(GPIO_DSCLK, clk ? GPIO_DSCLK : 0);
}
static inline void netwinder_ds1620_set_data(int dat)
{
- gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
+ nw_gpio_modify_op(GPIO_DATA, dat ? GPIO_DATA : 0);
}
static inline int netwinder_ds1620_get_data(void)
{
- return gpio_read() & GPIO_DATA;
+ return nw_gpio_read() & GPIO_DATA;
}
static inline void netwinder_ds1620_set_data_dir(int dir)
{
- gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
+ nw_gpio_modify_io(GPIO_DATA, dir ? GPIO_DATA : 0);
}
static inline void netwinder_ds1620_reset(void)
{
- cpld_modify(CPLD_DS_ENABLE, 0);
- cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
+ nw_cpld_modify(CPLD_DS_ENABLE, 0);
+ nw_cpld_modify(CPLD_DS_ENABLE, CPLD_DS_ENABLE);
}
static inline void netwinder_lock(unsigned long *flags)
{
- spin_lock_irqsave(&gpio_lock, *flags);
+ spin_lock_irqsave(&nw_gpio_lock, *flags);
}
static inline void netwinder_unlock(unsigned long *flags)
{
- spin_unlock_irqrestore(&gpio_lock, *flags);
+ spin_unlock_irqrestore(&nw_gpio_lock, *flags);
}
static inline void netwinder_set_fan(int i)
{
unsigned long flags;
- spin_lock_irqsave(&gpio_lock, flags);
- gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_gpio_modify_op(GPIO_FAN, i ? GPIO_FAN : 0);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
static inline int netwinder_get_fan(void)
@@ -96,7 +95,7 @@ static inline int netwinder_get_fan(void)
if ((system_rev & 0xf000) == 0x4000)
return FAN_ALWAYS_ON;
- return (gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
+ return (nw_gpio_read() & GPIO_FAN) ? FAN_ON : FAN_OFF;
}
/*
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 53fdc7ff3870..32b8bbf5003e 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -46,7 +46,7 @@
/*
* The High Precision Event Timer driver.
* This driver is closely modelled after the rtc.c driver.
- * http://www.intel.com/hardwaredesign/hpetspec.htm
+ * http://www.intel.com/hardwaredesign/hpetspec_1.pdf
*/
#define HPET_USER_FREQ (64)
#define HPET_DRIFT (500)
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 5b819b12675a..0587b66d6fc7 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -642,8 +642,11 @@ int hvc_poll(struct hvc_struct *hp)
/* Handle the SysRq Hack */
/* XXX should support a sequence */
if (buf[i] == '\x0f') { /* ^O */
- sysrq_pressed = 1;
- continue;
+ /* if ^O is pressed again, reset
+ * sysrq_pressed and flip ^O char */
+ sysrq_pressed = !sysrq_pressed;
+ if (sysrq_pressed)
+ continue;
} else if (sysrq_pressed) {
handle_sysrq(buf[i], tty);
sysrq_pressed = 0;
@@ -689,11 +692,10 @@ EXPORT_SYMBOL_GPL(hvc_poll);
*/
void hvc_resize(struct hvc_struct *hp, struct winsize ws)
{
- if ((hp->ws.ws_row != ws.ws_row) || (hp->ws.ws_col != ws.ws_col)) {
- hp->ws = ws;
- schedule_work(&hp->tty_resize);
- }
+ hp->ws = ws;
+ schedule_work(&hp->tty_resize);
}
+EXPORT_SYMBOL_GPL(hvc_resize);
/*
* This kthread is either polling or interrupt driven. This is determined by
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 8297dbc2e6ec..3c85d78c975c 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -48,7 +48,7 @@ struct hvc_struct {
spinlock_t lock;
int index;
struct tty_struct *tty;
- unsigned int count;
+ int count;
int do_wakeup;
char *outbuf;
int outbuf_size;
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index b74a2f8ab908..449727b6166d 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -575,8 +575,10 @@ static int __init hvc_find_vtys(void)
* of console adapters.
*/
if ((num_found >= MAX_NR_HVC_CONSOLES) ||
- (num_found >= VTTY_PORTS))
+ (num_found >= VTTY_PORTS)) {
+ of_node_put(vty);
break;
+ }
vtermno = of_get_property(vty, "reg", NULL);
if (!vtermno)
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
new file mode 100644
index 000000000000..5ea7d7713fca
--- /dev/null
+++ b/drivers/char/hvc_iucv.c
@@ -0,0 +1,850 @@
+/*
+ * hvc_iucv.c - z/VM IUCV back-end for the Hypervisor Console (HVC)
+ *
+ * This back-end for HVC provides terminal access via
+ * z/VM IUCV communication paths.
+ *
+ * Copyright IBM Corp. 2008.
+ *
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#define KMSG_COMPONENT "hvc_iucv"
+
+#include <linux/types.h>
+#include <asm/ebcdic.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <net/iucv/iucv.h>
+
+#include "hvc_console.h"
+
+
+/* HVC backend for z/VM IUCV */
+#define HVC_IUCV_MAGIC 0xc9e4c3e5
+#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS
+#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4)
+
+/* IUCV TTY message */
+#define MSG_VERSION 0x02 /* Message version */
+#define MSG_TYPE_ERROR 0x01 /* Error message */
+#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */
+#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */
+#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */
+#define MSG_TYPE_DATA 0x10 /* Terminal data */
+
+#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data))
+struct iucv_tty_msg {
+ u8 version; /* Message version */
+ u8 type; /* Message type */
+#define MSG_MAX_DATALEN (~(u16)0)
+ u16 datalen; /* Payload length */
+ u8 data[]; /* Payload buffer */
+} __attribute__((packed));
+
+enum iucv_state_t {
+ IUCV_DISCONN = 0,
+ IUCV_CONNECTED = 1,
+ IUCV_SEVERED = 2,
+};
+
+enum tty_state_t {
+ TTY_CLOSED = 0,
+ TTY_OPENED = 1,
+};
+
+struct hvc_iucv_private {
+ struct hvc_struct *hvc; /* HVC console struct reference */
+ u8 srv_name[8]; /* IUCV service name (ebcdic) */
+ enum iucv_state_t iucv_state; /* IUCV connection status */
+ enum tty_state_t tty_state; /* TTY status */
+ struct iucv_path *path; /* IUCV path pointer */
+ spinlock_t lock; /* hvc_iucv_private lock */
+ struct list_head tty_outqueue; /* outgoing IUCV messages */
+ struct list_head tty_inqueue; /* incoming IUCV messages */
+};
+
+struct iucv_tty_buffer {
+ struct list_head list; /* list pointer */
+ struct iucv_message msg; /* store an incoming IUCV message */
+ size_t offset; /* data buffer offset */
+ struct iucv_tty_msg *mbuf; /* buffer to store input/output data */
+};
+
+/* IUCV callback handler */
+static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]);
+static void hvc_iucv_path_severed(struct iucv_path *, u8[16]);
+static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *);
+static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *);
+
+
+/* Kernel module parameters */
+static unsigned long hvc_iucv_devices;
+
+/* Array of allocated hvc iucv tty lines... */
+static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES];
+
+/* Kmem cache and mempool for iucv_tty_buffer elements */
+static struct kmem_cache *hvc_iucv_buffer_cache;
+static mempool_t *hvc_iucv_mempool;
+
+/* IUCV handler callback functions */
+static struct iucv_handler hvc_iucv_handler = {
+ .path_pending = hvc_iucv_path_pending,
+ .path_severed = hvc_iucv_path_severed,
+ .message_complete = hvc_iucv_msg_complete,
+ .message_pending = hvc_iucv_msg_pending,
+};
+
+
+/**
+ * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance.
+ * @num: The HVC virtual terminal number (vtermno)
+ *
+ * This function returns the struct hvc_iucv_private instance that corresponds
+ * to the HVC virtual terminal number specified as parameter @num.
+ */
+struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
+{
+ if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
+ return NULL;
+ return hvc_iucv_table[num - HVC_IUCV_MAGIC];
+}
+
+/**
+ * alloc_tty_buffer() - Returns a new struct iucv_tty_buffer element.
+ * @size: Size of the internal buffer used to store data.
+ * @flags: Memory allocation flags passed to mempool.
+ *
+ * This function allocates a new struct iucv_tty_buffer element and, optionally,
+ * allocates an internal data buffer with the specified size @size.
+ * Note: The total message size arises from the internal buffer size and the
+ * members of the iucv_tty_msg structure.
+ *
+ * The function returns NULL if memory allocation has failed.
+ */
+static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags)
+{
+ struct iucv_tty_buffer *bufp;
+
+ bufp = mempool_alloc(hvc_iucv_mempool, flags);
+ if (!bufp)
+ return NULL;
+ memset(bufp, 0, sizeof(struct iucv_tty_buffer));
+
+ if (size > 0) {
+ bufp->msg.length = MSG_SIZE(size);
+ bufp->mbuf = kmalloc(bufp->msg.length, flags);
+ if (!bufp->mbuf) {
+ mempool_free(bufp, hvc_iucv_mempool);
+ return NULL;
+ }
+ bufp->mbuf->version = MSG_VERSION;
+ bufp->mbuf->type = MSG_TYPE_DATA;
+ bufp->mbuf->datalen = (u16) size;
+ }
+ return bufp;
+}
+
+/**
+ * destroy_tty_buffer() - destroy struct iucv_tty_buffer element.
+ * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL.
+ *
+ * The destroy_tty_buffer() function frees the internal data buffer and returns
+ * the struct iucv_tty_buffer element back to the mempool for freeing.
+ */
+static void destroy_tty_buffer(struct iucv_tty_buffer *bufp)
+{
+ kfree(bufp->mbuf);
+ mempool_free(bufp, hvc_iucv_mempool);
+}
+
+/**
+ * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element.
+ * @list: List head pointer to a list containing struct iucv_tty_buffer
+ * elements.
+ *
+ * Calls destroy_tty_buffer() for each struct iucv_tty_buffer element in the
+ * list @list.
+ */
+static void destroy_tty_buffer_list(struct list_head *list)
+{
+ struct iucv_tty_buffer *ent, *next;
+
+ list_for_each_entry_safe(ent, next, list, list) {
+ list_del(&ent->list);
+ destroy_tty_buffer(ent);
+ }
+}
+
+/**
+ * hvc_iucv_write() - Receive IUCV message write data to HVC console buffer.
+ * @priv: Pointer to hvc_iucv_private structure.
+ * @buf: HVC console buffer for writing received terminal data.
+ * @count: HVC console buffer size.
+ * @has_more_data: Pointer to an int variable.
+ *
+ * The function picks up pending messages from the input queue and receives
+ * the message data that is then written to the specified buffer @buf.
+ * If the buffer size @count is less than the data message size, then the
+ * message is kept on the input queue and @has_more_data is set to 1.
+ * If the message data has been entirely written, the message is removed from
+ * the input queue.
+ *
+ * The function returns the number of bytes written to the terminal, zero if
+ * there are no pending data messages available or if there is no established
+ * IUCV path.
+ * If the IUCV path has been severed, then -EPIPE is returned to cause a
+ * hang up (that is issued by the HVC console layer).
+ */
+static int hvc_iucv_write(struct hvc_iucv_private *priv,
+ char *buf, int count, int *has_more_data)
+{
+ struct iucv_tty_buffer *rb;
+ int written;
+ int rc;
+
+ /* Immediately return if there is no IUCV connection */
+ if (priv->iucv_state == IUCV_DISCONN)
+ return 0;
+
+ /* If the IUCV path has been severed, return -EPIPE to inform the
+ * hvc console layer to hang up the tty device. */
+ if (priv->iucv_state == IUCV_SEVERED)
+ return -EPIPE;
+
+ /* check if there are pending messages */
+ if (list_empty(&priv->tty_inqueue))
+ return 0;
+
+ /* receive a iucv message and flip data to the tty (ldisc) */
+ rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list);
+
+ written = 0;
+ if (!rb->mbuf) { /* message not yet received ... */
+ /* allocate mem to store msg data; if no memory is available
+ * then leave the buffer on the list and re-try later */
+ rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC);
+ if (!rb->mbuf)
+ return -ENOMEM;
+
+ rc = __iucv_message_receive(priv->path, &rb->msg, 0,
+ rb->mbuf, rb->msg.length, NULL);
+ switch (rc) {
+ case 0: /* Successful */
+ break;
+ case 2: /* No message found */
+ case 9: /* Message purged */
+ break;
+ default:
+ written = -EIO;
+ }
+ /* remove buffer if an error has occured or received data
+ * is not correct */
+ if (rc || (rb->mbuf->version != MSG_VERSION) ||
+ (rb->msg.length != MSG_SIZE(rb->mbuf->datalen)))
+ goto out_remove_buffer;
+ }
+
+ switch (rb->mbuf->type) {
+ case MSG_TYPE_DATA:
+ written = min_t(int, rb->mbuf->datalen - rb->offset, count);
+ memcpy(buf, rb->mbuf->data + rb->offset, written);
+ if (written < (rb->mbuf->datalen - rb->offset)) {
+ rb->offset += written;
+ *has_more_data = 1;
+ goto out_written;
+ }
+ break;
+
+ case MSG_TYPE_WINSIZE:
+ if (rb->mbuf->datalen != sizeof(struct winsize))
+ break;
+ hvc_resize(priv->hvc, *((struct winsize *)rb->mbuf->data));
+ break;
+
+ case MSG_TYPE_ERROR: /* ignored ... */
+ case MSG_TYPE_TERMENV: /* ignored ... */
+ case MSG_TYPE_TERMIOS: /* ignored ... */
+ break;
+ }
+
+out_remove_buffer:
+ list_del(&rb->list);
+ destroy_tty_buffer(rb);
+ *has_more_data = !list_empty(&priv->tty_inqueue);
+
+out_written:
+ return written;
+}
+
+/**
+ * hvc_iucv_get_chars() - HVC get_chars operation.
+ * @vtermno: HVC virtual terminal number.
+ * @buf: Pointer to a buffer to store data
+ * @count: Size of buffer available for writing
+ *
+ * The hvc_console thread calls this method to read characters from
+ * the terminal backend. If an IUCV communication path has been established,
+ * pending IUCV messages are received and data is copied into buffer @buf
+ * up to @count bytes.
+ *
+ * Locking: The routine gets called under an irqsave() spinlock; and
+ * the routine locks the struct hvc_iucv_private->lock to call
+ * helper functions.
+ */
+static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count)
+{
+ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
+ int written;
+ int has_more_data;
+
+ if (count <= 0)
+ return 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+ has_more_data = 0;
+ written = hvc_iucv_write(priv, buf, count, &has_more_data);
+ spin_unlock(&priv->lock);
+
+ /* if there are still messages on the queue... schedule another run */
+ if (has_more_data)
+ hvc_kick();
+
+ return written;
+}
+
+/**
+ * hvc_iucv_send() - Send an IUCV message containing terminal data.
+ * @priv: Pointer to struct hvc_iucv_private instance.
+ * @buf: Buffer containing data to send.
+ * @size: Size of buffer and amount of data to send.
+ *
+ * If an IUCV communication path is established, the function copies the buffer
+ * data to a newly allocated struct iucv_tty_buffer element, sends the data and
+ * puts the element to the outqueue.
+ *
+ * If there is no IUCV communication path established, the function returns 0.
+ * If an existing IUCV communicaton path has been severed, the function returns
+ * -EPIPE (can be passed to HVC layer to cause a tty hangup).
+ */
+static int hvc_iucv_send(struct hvc_iucv_private *priv, const char *buf,
+ int count)
+{
+ struct iucv_tty_buffer *sb;
+ int rc;
+ u16 len;
+
+ if (priv->iucv_state == IUCV_SEVERED)
+ return -EPIPE;
+
+ if (priv->iucv_state == IUCV_DISCONN)
+ return 0;
+
+ len = min_t(u16, MSG_MAX_DATALEN, count);
+
+ /* allocate internal buffer to store msg data and also compute total
+ * message length */
+ sb = alloc_tty_buffer(len, GFP_ATOMIC);
+ if (!sb)
+ return -ENOMEM;
+
+ sb->mbuf->datalen = len;
+ memcpy(sb->mbuf->data, buf, len);
+
+ list_add_tail(&sb->list, &priv->tty_outqueue);
+
+ rc = __iucv_message_send(priv->path, &sb->msg, 0, 0,
+ (void *) sb->mbuf, sb->msg.length);
+ if (rc) {
+ list_del(&sb->list);
+ destroy_tty_buffer(sb);
+ len = 0;
+ }
+
+ return len;
+}
+
+/**
+ * hvc_iucv_put_chars() - HVC put_chars operation.
+ * @vtermno: HVC virtual terminal number.
+ * @buf: Pointer to an buffer to read data from
+ * @count: Size of buffer available for reading
+ *
+ * The hvc_console thread calls this method to write characters from
+ * to the terminal backend.
+ * The function calls hvc_iucv_send() under the lock of the
+ * struct hvc_iucv_private instance that corresponds to the tty @vtermno.
+ *
+ * Locking: The method gets called under an irqsave() spinlock; and
+ * locks struct hvc_iucv_private->lock.
+ */
+static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count)
+{
+ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
+ int sent;
+
+ if (count <= 0)
+ return 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+ sent = hvc_iucv_send(priv, buf, count);
+ spin_unlock(&priv->lock);
+
+ return sent;
+}
+
+/**
+ * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc): the index of an struct
+ * hvc_iucv_private instance.
+ *
+ * The function sets the tty state to TTY_OPEN for the struct hvc_iucv_private
+ * instance that is derived from @id. Always returns 0.
+ *
+ * Locking: struct hvc_iucv_private->lock, spin_lock_bh
+ */
+static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return 0;
+
+ spin_lock_bh(&priv->lock);
+ priv->tty_state = TTY_OPENED;
+ spin_unlock_bh(&priv->lock);
+
+ return 0;
+}
+
+/**
+ * hvc_iucv_cleanup() - Clean up function if the tty portion is finally closed.
+ * @priv: Pointer to the struct hvc_iucv_private instance.
+ *
+ * The functions severs the established IUCV communication path (if any), and
+ * destroy struct iucv_tty_buffer elements from the in- and outqueue. Finally,
+ * the functions resets the states to TTY_CLOSED and IUCV_DISCONN.
+ */
+static void hvc_iucv_cleanup(struct hvc_iucv_private *priv)
+{
+ destroy_tty_buffer_list(&priv->tty_outqueue);
+ destroy_tty_buffer_list(&priv->tty_inqueue);
+
+ priv->tty_state = TTY_CLOSED;
+ priv->iucv_state = IUCV_DISCONN;
+}
+
+/**
+ * hvc_iucv_notifier_hangup() - HVC notifier for tty hangups.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc): the index of an struct
+ * hvc_iucv_private instance.
+ *
+ * This routine notifies the HVC backend that a tty hangup (carrier loss,
+ * virtual or otherwise) has occured.
+ *
+ * The HVC backend for z/VM IUCV ignores virtual hangups (vhangup()), to keep
+ * an existing IUCV communication path established.
+ * (Background: vhangup() is called from user space (by getty or login) to
+ * disable writing to the tty by other applications).
+ *
+ * If the tty has been opened (e.g. getty) and an established IUCV path has been
+ * severed (we caused the tty hangup in that case), then the functions invokes
+ * hvc_iucv_cleanup() to clean up.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return;
+
+ spin_lock_bh(&priv->lock);
+ /* NOTE: If the hangup was scheduled by ourself (from the iucv
+ * path_servered callback [IUCV_SEVERED]), then we have to
+ * finally clean up the tty backend structure and set state to
+ * TTY_CLOSED.
+ *
+ * If the tty was hung up otherwise (e.g. vhangup()), then we
+ * ignore this hangup and keep an established IUCV path open...
+ * (...the reason is that we are not able to connect back to the
+ * client if we disconnect on hang up) */
+ priv->tty_state = TTY_CLOSED;
+
+ if (priv->iucv_state == IUCV_SEVERED)
+ hvc_iucv_cleanup(priv);
+ spin_unlock_bh(&priv->lock);
+}
+
+/**
+ * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc):
+ * the index of an struct hvc_iucv_private instance.
+ *
+ * This routine notifies the HVC backend that the last tty device file
+ * descriptor has been closed.
+ * The function calls hvc_iucv_cleanup() to clean up the struct hvc_iucv_private
+ * instance.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+ struct iucv_path *path;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return;
+
+ spin_lock_bh(&priv->lock);
+ path = priv->path; /* save reference to IUCV path */
+ priv->path = NULL;
+ hvc_iucv_cleanup(priv);
+ spin_unlock_bh(&priv->lock);
+
+ /* sever IUCV path outside of priv->lock due to lock ordering of:
+ * priv->lock <--> iucv_table_lock */
+ if (path) {
+ iucv_path_sever(path, NULL);
+ iucv_path_free(path);
+ }
+}
+
+/**
+ * hvc_iucv_path_pending() - IUCV handler to process a connection request.
+ * @path: Pending path (struct iucv_path)
+ * @ipvmid: Originator z/VM system identifier
+ * @ipuser: User specified data for this path
+ * (AF_IUCV: port/service name and originator port)
+ *
+ * The function uses the @ipuser data to check to determine if the pending
+ * path belongs to a terminal managed by this HVC backend.
+ * If the check is successful, then an additional check is done to ensure
+ * that a terminal cannot be accessed multiple times (only one connection
+ * to a terminal is allowed). In that particular case, the pending path is
+ * severed. If it is the first connection, the pending path is accepted and
+ * associated to the struct hvc_iucv_private. The iucv state is updated to
+ * reflect that a communication path has been established.
+ *
+ * Returns 0 if the path belongs to a terminal managed by the this HVC backend;
+ * otherwise returns -ENODEV in order to dispatch this path to other handlers.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static int hvc_iucv_path_pending(struct iucv_path *path,
+ u8 ipvmid[8], u8 ipuser[16])
+{
+ struct hvc_iucv_private *priv;
+ u8 nuser_data[16];
+ int i, rc;
+
+ priv = NULL;
+ for (i = 0; i < hvc_iucv_devices; i++)
+ if (hvc_iucv_table[i] &&
+ (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) {
+ priv = hvc_iucv_table[i];
+ break;
+ }
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+
+ /* If the terminal is already connected or being severed, then sever
+ * this path to enforce that there is only ONE established communication
+ * path per terminal. */
+ if (priv->iucv_state != IUCV_DISCONN) {
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+ goto out_path_handled;
+ }
+
+ /* accept path */
+ memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */
+ memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */
+ path->msglim = 0xffff; /* IUCV MSGLIMIT */
+ path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */
+ rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv);
+ if (rc) {
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+ goto out_path_handled;
+ }
+ priv->path = path;
+ priv->iucv_state = IUCV_CONNECTED;
+
+out_path_handled:
+ spin_unlock(&priv->lock);
+ return 0;
+}
+
+/**
+ * hvc_iucv_path_severed() - IUCV handler to process a path sever.
+ * @path: Pending path (struct iucv_path)
+ * @ipuser: User specified data for this path
+ * (AF_IUCV: port/service name and originator port)
+ *
+ * The function also severs the path (as required by the IUCV protocol) and
+ * sets the iucv state to IUCV_SEVERED for the associated struct
+ * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty
+ * hangup (hvc_iucv_get_chars() / hvc_iucv_write()).
+ *
+ * If tty portion of the HVC is closed then clean up the outqueue in addition.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+{
+ struct hvc_iucv_private *priv = path->private;
+
+ spin_lock(&priv->lock);
+ priv->iucv_state = IUCV_SEVERED;
+
+ /* NOTE: If the tty has not yet been opened by a getty program
+ * (e.g. to see console messages), then cleanup the
+ * hvc_iucv_private structure to allow re-connects.
+ *
+ * If the tty has been opened, the get_chars() callback returns
+ * -EPIPE to signal the hvc console layer to hang up the tty. */
+ priv->path = NULL;
+ if (priv->tty_state == TTY_CLOSED)
+ hvc_iucv_cleanup(priv);
+ spin_unlock(&priv->lock);
+
+ /* finally sever path (outside of priv->lock due to lock ordering) */
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+}
+
+/**
+ * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message.
+ * @path: Pending path (struct iucv_path)
+ * @msg: Pointer to the IUCV message
+ *
+ * The function stores an incoming message on the input queue for later
+ * processing (by hvc_iucv_get_chars() / hvc_iucv_write()).
+ * However, if the tty has not yet been opened, the message is rejected.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_msg_pending(struct iucv_path *path,
+ struct iucv_message *msg)
+{
+ struct hvc_iucv_private *priv = path->private;
+ struct iucv_tty_buffer *rb;
+
+ spin_lock(&priv->lock);
+
+ /* reject messages if tty has not yet been opened */
+ if (priv->tty_state == TTY_CLOSED) {
+ iucv_message_reject(path, msg);
+ goto unlock_return;
+ }
+
+ /* allocate buffer an empty buffer element */
+ rb = alloc_tty_buffer(0, GFP_ATOMIC);
+ if (!rb) {
+ iucv_message_reject(path, msg);
+ goto unlock_return; /* -ENOMEM */
+ }
+ rb->msg = *msg;
+
+ list_add_tail(&rb->list, &priv->tty_inqueue);
+
+ hvc_kick(); /* wakup hvc console thread */
+
+unlock_return:
+ spin_unlock(&priv->lock);
+}
+
+/**
+ * hvc_iucv_msg_complete() - IUCV handler to process message completion
+ * @path: Pending path (struct iucv_path)
+ * @msg: Pointer to the IUCV message
+ *
+ * The function is called upon completion of message delivery and the
+ * message is removed from the outqueue. Additional delivery information
+ * can be found in msg->audit: rejected messages (0x040000 (IPADRJCT)) and
+ * purged messages (0x010000 (IPADPGNR)).
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_msg_complete(struct iucv_path *path,
+ struct iucv_message *msg)
+{
+ struct hvc_iucv_private *priv = path->private;
+ struct iucv_tty_buffer *ent, *next;
+ LIST_HEAD(list_remove);
+
+ spin_lock(&priv->lock);
+ list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list)
+ if (ent->msg.id == msg->id) {
+ list_move(&ent->list, &list_remove);
+ break;
+ }
+ spin_unlock(&priv->lock);
+ destroy_tty_buffer_list(&list_remove);
+}
+
+
+/* HVC operations */
+static struct hv_ops hvc_iucv_ops = {
+ .get_chars = hvc_iucv_get_chars,
+ .put_chars = hvc_iucv_put_chars,
+ .notifier_add = hvc_iucv_notifier_add,
+ .notifier_del = hvc_iucv_notifier_del,
+ .notifier_hangup = hvc_iucv_notifier_hangup,
+};
+
+/**
+ * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
+ * @id: hvc_iucv_table index
+ *
+ * This function allocates a new hvc_iucv_private struct and put the
+ * instance into hvc_iucv_table at index @id.
+ * Returns 0 on success; otherwise non-zero.
+ */
+static int __init hvc_iucv_alloc(int id)
+{
+ struct hvc_iucv_private *priv;
+ char name[9];
+ int rc;
+
+ priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->tty_outqueue);
+ INIT_LIST_HEAD(&priv->tty_inqueue);
+
+ /* Finally allocate hvc */
+ priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id,
+ HVC_IUCV_MAGIC + id, &hvc_iucv_ops, PAGE_SIZE);
+ if (IS_ERR(priv->hvc)) {
+ rc = PTR_ERR(priv->hvc);
+ kfree(priv);
+ return rc;
+ }
+
+ /* setup iucv related information */
+ snprintf(name, 9, "ihvc%-4d", id);
+ memcpy(priv->srv_name, name, 8);
+ ASCEBC(priv->srv_name, 8);
+
+ hvc_iucv_table[id] = priv;
+ return 0;
+}
+
+/**
+ * hvc_iucv_init() - Initialization of HVC backend for z/VM IUCV
+ */
+static int __init hvc_iucv_init(void)
+{
+ int rc, i;
+
+ if (!MACHINE_IS_VM) {
+ pr_warning("The z/VM IUCV Hypervisor console cannot be "
+ "used without z/VM.\n");
+ return -ENODEV;
+ }
+
+ if (!hvc_iucv_devices)
+ return -ENODEV;
+
+ if (hvc_iucv_devices > MAX_HVC_IUCV_LINES)
+ return -EINVAL;
+
+ hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT,
+ sizeof(struct iucv_tty_buffer),
+ 0, 0, NULL);
+ if (!hvc_iucv_buffer_cache) {
+ pr_err("Not enough memory for driver initialization "
+ "(rs=%d).\n", 1);
+ return -ENOMEM;
+ }
+
+ hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR,
+ hvc_iucv_buffer_cache);
+ if (!hvc_iucv_mempool) {
+ pr_err("Not enough memory for driver initialization "
+ "(rs=%d).\n", 2);
+ kmem_cache_destroy(hvc_iucv_buffer_cache);
+ return -ENOMEM;
+ }
+
+ /* allocate hvc_iucv_private structs */
+ for (i = 0; i < hvc_iucv_devices; i++) {
+ rc = hvc_iucv_alloc(i);
+ if (rc) {
+ pr_err("Could not create new z/VM IUCV HVC backend "
+ "rc=%d.\n", rc);
+ goto out_error_hvc;
+ }
+ }
+
+ /* register IUCV callback handler */
+ rc = iucv_register(&hvc_iucv_handler, 0);
+ if (rc) {
+ pr_err("Could not register iucv handler (rc=%d).\n", rc);
+ goto out_error_iucv;
+ }
+
+ return 0;
+
+out_error_iucv:
+ iucv_unregister(&hvc_iucv_handler, 0);
+out_error_hvc:
+ for (i = 0; i < hvc_iucv_devices; i++)
+ if (hvc_iucv_table[i]) {
+ if (hvc_iucv_table[i]->hvc)
+ hvc_remove(hvc_iucv_table[i]->hvc);
+ kfree(hvc_iucv_table[i]);
+ }
+ mempool_destroy(hvc_iucv_mempool);
+ kmem_cache_destroy(hvc_iucv_buffer_cache);
+ return rc;
+}
+
+/**
+ * hvc_iucv_console_init() - Early console initialization
+ */
+static int __init hvc_iucv_console_init(void)
+{
+ if (!MACHINE_IS_VM || !hvc_iucv_devices)
+ return -ENODEV;
+ return hvc_instantiate(HVC_IUCV_MAGIC, 0, &hvc_iucv_ops);
+}
+
+/**
+ * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter
+ * @val: Parameter value (numeric)
+ */
+static int __init hvc_iucv_config(char *val)
+{
+ return strict_strtoul(val, 10, &hvc_iucv_devices);
+}
+
+
+module_init(hvc_iucv_init);
+console_initcall(hvc_iucv_console_init);
+__setup("hvc_iucv=", hvc_iucv_config);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HVC back-end for z/VM IUCV.");
+MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c
new file mode 100644
index 000000000000..bd63ba878a56
--- /dev/null
+++ b/drivers/char/hvc_udbg.c
@@ -0,0 +1,96 @@
+/*
+ * udbg interface to hvc_console.c
+ *
+ * (C) Copyright David Gibson, IBM Corporation 2008.
+ *
+ * This program is free software; you can redistribute 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/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+
+#include <asm/udbg.h>
+
+#include "hvc_console.h"
+
+struct hvc_struct *hvc_udbg_dev;
+
+static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ udbg_putc(buf[i]);
+
+ return i;
+}
+
+static int hvc_udbg_get(uint32_t vtermno, char *buf, int count)
+{
+ int i, c;
+
+ if (!udbg_getc_poll)
+ return 0;
+
+ for (i = 0; i < count; i++) {
+ if ((c = udbg_getc_poll()) == -1)
+ break;
+ buf[i] = c;
+ }
+
+ return i;
+}
+
+static struct hv_ops hvc_udbg_ops = {
+ .get_chars = hvc_udbg_get,
+ .put_chars = hvc_udbg_put,
+};
+
+static int __init hvc_udbg_init(void)
+{
+ struct hvc_struct *hp;
+
+ BUG_ON(hvc_udbg_dev);
+
+ hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16);
+ if (IS_ERR(hp))
+ return PTR_ERR(hp);
+
+ hvc_udbg_dev = hp;
+
+ return 0;
+}
+module_init(hvc_udbg_init);
+
+static void __exit hvc_udbg_exit(void)
+{
+ if (hvc_udbg_dev)
+ hvc_remove(hvc_udbg_dev);
+}
+module_exit(hvc_udbg_exit);
+
+static int __init hvc_udbg_console_init(void)
+{
+ hvc_instantiate(0, 0, &hvc_udbg_ops);
+ add_preferred_console("hvc", 0, NULL);
+
+ return 0;
+}
+console_initcall(hvc_udbg_console_init);
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 019e0b58593d..bd62dc86b47d 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -153,8 +153,10 @@ static int hvc_find_vtys(void)
/* We have statically defined space for only a certain number
* of console adapters.
*/
- if (num_found >= MAX_NR_HVC_CONSOLES)
+ if (num_found >= MAX_NR_HVC_CONSOLES) {
+ of_node_put(vty);
break;
+ }
vtermno = of_get_property(vty, "reg", NULL);
if (!vtermno)
diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c
index 473d9b14439a..6e6eb445d374 100644
--- a/drivers/char/hvcs.c
+++ b/drivers/char/hvcs.c
@@ -269,7 +269,7 @@ struct hvcs_struct {
unsigned int index;
struct tty_struct *tty;
- unsigned int open_count;
+ int open_count;
/*
* Used to tell the driver kernel_thread what operations need to take
diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c
index 59c6f9ab94e4..af055287271a 100644
--- a/drivers/char/hvsi.c
+++ b/drivers/char/hvsi.c
@@ -75,7 +75,7 @@ struct hvsi_struct {
spinlock_t lock;
int index;
struct tty_struct *tty;
- unsigned int count;
+ int count;
uint8_t throttle_buf[128];
uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */
/* inbuf is for packet reassembly. leave a little room for leftovers. */
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 8054ee839b3c..88cee4099be9 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -32,9 +32,10 @@
* added changelog
* 1.2 Erik Gilling: Cobalt Networks support
* Tim Hockin: general cleanup, Cobalt support
+ * 1.3 Wim Van Sebroeck: convert PRINT_PROC to seq_file
*/
-#define NVRAM_VERSION "1.2"
+#define NVRAM_VERSION "1.3"
#include <linux/module.h>
#include <linux/smp_lock.h>
@@ -46,7 +47,7 @@
/* select machine configuration */
#if defined(CONFIG_ATARI)
# define MACH ATARI
-#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and others?? */
+#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and ?? */
# define MACH PC
#else
# error Cannot build nvram driver for this machine configuration.
@@ -106,10 +107,11 @@
#include <linux/mc146818rtc.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
#include <asm/system.h>
static DEFINE_SPINLOCK(nvram_state_lock);
@@ -122,8 +124,8 @@ static int mach_check_checksum(void);
static void mach_set_checksum(void);
#ifdef CONFIG_PROC_FS
-static int mach_proc_infos(unsigned char *contents, char *buffer, int *len,
- off_t *begin, off_t offset, int size);
+static void mach_proc_infos(unsigned char *contents, struct seq_file *seq,
+ void *offset);
#endif
/*
@@ -133,18 +135,17 @@ static int mach_proc_infos(unsigned char *contents, char *buffer, int *len,
*
* It is worth noting that these functions all access bytes of general
* purpose memory in the NVRAM - that is to say, they all add the
- * NVRAM_FIRST_BYTE offset. Pass them offsets into NVRAM as if you did not
+ * NVRAM_FIRST_BYTE offset. Pass them offsets into NVRAM as if you did not
* know about the RTC cruft.
*/
-unsigned char
-__nvram_read_byte(int i)
+unsigned char __nvram_read_byte(int i)
{
return CMOS_READ(NVRAM_FIRST_BYTE + i);
}
+EXPORT_SYMBOL(__nvram_read_byte);
-unsigned char
-nvram_read_byte(int i)
+unsigned char nvram_read_byte(int i)
{
unsigned long flags;
unsigned char c;
@@ -154,16 +155,16 @@ nvram_read_byte(int i)
spin_unlock_irqrestore(&rtc_lock, flags);
return c;
}
+EXPORT_SYMBOL(nvram_read_byte);
/* This races nicely with trying to read with checksum checking (nvram_read) */
-void
-__nvram_write_byte(unsigned char c, int i)
+void __nvram_write_byte(unsigned char c, int i)
{
CMOS_WRITE(c, NVRAM_FIRST_BYTE + i);
}
+EXPORT_SYMBOL(__nvram_write_byte);
-void
-nvram_write_byte(unsigned char c, int i)
+void nvram_write_byte(unsigned char c, int i)
{
unsigned long flags;
@@ -171,15 +172,15 @@ nvram_write_byte(unsigned char c, int i)
__nvram_write_byte(c, i);
spin_unlock_irqrestore(&rtc_lock, flags);
}
+EXPORT_SYMBOL(nvram_write_byte);
-int
-__nvram_check_checksum(void)
+int __nvram_check_checksum(void)
{
return mach_check_checksum();
}
+EXPORT_SYMBOL(__nvram_check_checksum);
-int
-nvram_check_checksum(void)
+int nvram_check_checksum(void)
{
unsigned long flags;
int rv;
@@ -189,16 +190,15 @@ nvram_check_checksum(void)
spin_unlock_irqrestore(&rtc_lock, flags);
return rv;
}
+EXPORT_SYMBOL(nvram_check_checksum);
-static void
-__nvram_set_checksum(void)
+static void __nvram_set_checksum(void)
{
mach_set_checksum();
}
#if 0
-void
-nvram_set_checksum(void)
+void nvram_set_checksum(void)
{
unsigned long flags;
@@ -212,7 +212,7 @@ nvram_set_checksum(void)
* The are the file operation function for user access to /dev/nvram
*/
-static loff_t nvram_llseek(struct file *file,loff_t offset, int origin )
+static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
lock_kernel();
switch (origin) {
@@ -230,8 +230,8 @@ static loff_t nvram_llseek(struct file *file,loff_t offset, int origin )
return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
}
-static ssize_t
-nvram_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+static ssize_t nvram_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
{
unsigned char contents[NVRAM_BYTES];
unsigned i = *ppos;
@@ -254,13 +254,13 @@ nvram_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
return tmp - contents;
- checksum_err:
+checksum_err:
spin_unlock_irq(&rtc_lock);
return -EIO;
}
-static ssize_t
-nvram_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+static ssize_t nvram_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
{
unsigned char contents[NVRAM_BYTES];
unsigned i = *ppos;
@@ -287,14 +287,13 @@ nvram_write(struct file *file, const char __user *buf, size_t count, loff_t *ppo
return tmp - contents;
- checksum_err:
+checksum_err:
spin_unlock_irq(&rtc_lock);
return -EIO;
}
-static int
-nvram_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int nvram_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
int i;
@@ -315,7 +314,7 @@ nvram_ioctl(struct inode *inode, struct file *file,
return 0;
case NVRAM_SETCKS:
- /* just set checksum, contents unchanged (maybe useful after
+ /* just set checksum, contents unchanged (maybe useful after
* checksum garbaged somehow...) */
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -330,8 +329,7 @@ nvram_ioctl(struct inode *inode, struct file *file,
}
}
-static int
-nvram_open(struct inode *inode, struct file *file)
+static int nvram_open(struct inode *inode, struct file *file)
{
lock_kernel();
spin_lock(&nvram_state_lock);
@@ -356,8 +354,7 @@ nvram_open(struct inode *inode, struct file *file)
return 0;
}
-static int
-nvram_release(struct inode *inode, struct file *file)
+static int nvram_release(struct inode *inode, struct file *file)
{
spin_lock(&nvram_state_lock);
@@ -375,48 +372,47 @@ nvram_release(struct inode *inode, struct file *file)
}
#ifndef CONFIG_PROC_FS
-static int
-nvram_read_proc(char *buffer, char **start, off_t offset,
- int size, int *eof, void *data)
+static int nvram_add_proc_fs(void)
{
return 0;
}
+
#else
-static int
-nvram_read_proc(char *buffer, char **start, off_t offset,
- int size, int *eof, void *data)
+static int nvram_proc_read(struct seq_file *seq, void *offset)
{
unsigned char contents[NVRAM_BYTES];
- int i, len = 0;
- off_t begin = 0;
+ int i = 0;
spin_lock_irq(&rtc_lock);
for (i = 0; i < NVRAM_BYTES; ++i)
contents[i] = __nvram_read_byte(i);
spin_unlock_irq(&rtc_lock);
- *eof = mach_proc_infos(contents, buffer, &len, &begin, offset, size);
+ mach_proc_infos(contents, seq, offset);
- if (offset >= begin + len)
- return 0;
- *start = buffer + (offset - begin);
- return (size < begin + len - offset) ? size : begin + len - offset;
+ return 0;
+}
+static int nvram_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, nvram_proc_read, NULL);
}
-/* This macro frees the machine specific function from bounds checking and
- * this like that... */
-#define PRINT_PROC(fmt,args...) \
- do { \
- *len += sprintf(buffer+*len, fmt, ##args); \
- if (*begin + *len > offset + size) \
- return 0; \
- if (*begin + *len < offset) { \
- *begin += *len; \
- *len = 0; \
- } \
- } while(0)
+static const struct file_operations nvram_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = nvram_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int nvram_add_proc_fs(void)
+{
+ if (!proc_create("driver/nvram", 0, NULL, &nvram_proc_fops))
+ return -ENOMEM;
+ return 0;
+}
#endif /* CONFIG_PROC_FS */
@@ -436,8 +432,7 @@ static struct miscdevice nvram_dev = {
&nvram_fops
};
-static int __init
-nvram_init(void)
+static int __init nvram_init(void)
{
int ret;
@@ -451,23 +446,21 @@ nvram_init(void)
NVRAM_MINOR);
goto out;
}
- if (!create_proc_read_entry("driver/nvram", 0, NULL, nvram_read_proc,
- NULL)) {
+ ret = nvram_add_proc_fs();
+ if (ret) {
printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n");
- ret = -ENOMEM;
goto outmisc;
}
ret = 0;
printk(KERN_INFO "Non-volatile memory driver v" NVRAM_VERSION "\n");
- out:
+out:
return ret;
- outmisc:
+outmisc:
misc_deregister(&nvram_dev);
goto out;
}
-static void __exit
-nvram_cleanup_module(void)
+static void __exit nvram_cleanup_module(void)
{
remove_proc_entry("driver/nvram", NULL);
misc_deregister(&nvram_dev);
@@ -482,8 +475,7 @@ module_exit(nvram_cleanup_module);
#if MACH == PC
-static int
-pc_check_checksum(void)
+static int pc_check_checksum(void)
{
int i;
unsigned short sum = 0;
@@ -493,11 +485,10 @@ pc_check_checksum(void)
sum += __nvram_read_byte(i);
expect = __nvram_read_byte(PC_CKS_LOC)<<8 |
__nvram_read_byte(PC_CKS_LOC+1);
- return ((sum & 0xffff) == expect);
+ return (sum & 0xffff) == expect;
}
-static void
-pc_set_checksum(void)
+static void pc_set_checksum(void)
{
int i;
unsigned short sum = 0;
@@ -522,9 +513,8 @@ static char *gfx_types[] = {
"monochrome",
};
-static int
-pc_proc_infos(unsigned char *nvram, char *buffer, int *len,
- off_t *begin, off_t offset, int size)
+static void pc_proc_infos(unsigned char *nvram, struct seq_file *seq,
+ void *offset)
{
int checksum;
int type;
@@ -533,56 +523,57 @@ pc_proc_infos(unsigned char *nvram, char *buffer, int *len,
checksum = __nvram_check_checksum();
spin_unlock_irq(&rtc_lock);
- PRINT_PROC("Checksum status: %svalid\n", checksum ? "" : "not ");
+ seq_printf(seq, "Checksum status: %svalid\n", checksum ? "" : "not ");
- PRINT_PROC("# floppies : %d\n",
+ seq_printf(seq, "# floppies : %d\n",
(nvram[6] & 1) ? (nvram[6] >> 6) + 1 : 0);
- PRINT_PROC("Floppy 0 type : ");
+ seq_printf(seq, "Floppy 0 type : ");
type = nvram[2] >> 4;
if (type < ARRAY_SIZE(floppy_types))
- PRINT_PROC("%s\n", floppy_types[type]);
+ seq_printf(seq, "%s\n", floppy_types[type]);
else
- PRINT_PROC("%d (unknown)\n", type);
- PRINT_PROC("Floppy 1 type : ");
+ seq_printf(seq, "%d (unknown)\n", type);
+ seq_printf(seq, "Floppy 1 type : ");
type = nvram[2] & 0x0f;
if (type < ARRAY_SIZE(floppy_types))
- PRINT_PROC("%s\n", floppy_types[type]);
+ seq_printf(seq, "%s\n", floppy_types[type]);
else
- PRINT_PROC("%d (unknown)\n", type);
+ seq_printf(seq, "%d (unknown)\n", type);
- PRINT_PROC("HD 0 type : ");
+ seq_printf(seq, "HD 0 type : ");
type = nvram[4] >> 4;
if (type)
- PRINT_PROC("%02x\n", type == 0x0f ? nvram[11] : type);
+ seq_printf(seq, "%02x\n", type == 0x0f ? nvram[11] : type);
else
- PRINT_PROC("none\n");
+ seq_printf(seq, "none\n");
- PRINT_PROC("HD 1 type : ");
+ seq_printf(seq, "HD 1 type : ");
type = nvram[4] & 0x0f;
if (type)
- PRINT_PROC("%02x\n", type == 0x0f ? nvram[12] : type);
+ seq_printf(seq, "%02x\n", type == 0x0f ? nvram[12] : type);
else
- PRINT_PROC("none\n");
+ seq_printf(seq, "none\n");
- PRINT_PROC("HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
+ seq_printf(seq, "HD type 48 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
nvram[18] | (nvram[19] << 8),
nvram[20], nvram[25],
nvram[21] | (nvram[22] << 8), nvram[23] | (nvram[24] << 8));
- PRINT_PROC("HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
+ seq_printf(seq, "HD type 49 data: %d/%d/%d C/H/S, precomp %d, lz %d\n",
nvram[39] | (nvram[40] << 8),
nvram[41], nvram[46],
nvram[42] | (nvram[43] << 8), nvram[44] | (nvram[45] << 8));
- PRINT_PROC("DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8));
- PRINT_PROC("Extended memory: %d kB (configured), %d kB (tested)\n",
+ seq_printf(seq, "DOS base memory: %d kB\n", nvram[7] | (nvram[8] << 8));
+ seq_printf(seq, "Extended memory: %d kB (configured), %d kB (tested)\n",
nvram[9] | (nvram[10] << 8), nvram[34] | (nvram[35] << 8));
- PRINT_PROC("Gfx adapter : %s\n", gfx_types[(nvram[6] >> 4) & 3]);
+ seq_printf(seq, "Gfx adapter : %s\n",
+ gfx_types[(nvram[6] >> 4) & 3]);
- PRINT_PROC("FPU : %sinstalled\n",
+ seq_printf(seq, "FPU : %sinstalled\n",
(nvram[6] & 2) ? "" : "not ");
- return 1;
+ return;
}
#endif
@@ -590,20 +581,18 @@ pc_proc_infos(unsigned char *nvram, char *buffer, int *len,
#if MACH == ATARI
-static int
-atari_check_checksum(void)
+static int atari_check_checksum(void)
{
int i;
unsigned char sum = 0;
for (i = ATARI_CKS_RANGE_START; i <= ATARI_CKS_RANGE_END; ++i)
sum += __nvram_read_byte(i);
- return (__nvram_read_byte(ATARI_CKS_LOC) == (~sum & 0xff) &&
- __nvram_read_byte(ATARI_CKS_LOC + 1) == (sum & 0xff));
+ return (__nvram_read_byte(ATARI_CKS_LOC) == (~sum & 0xff)) &&
+ (__nvram_read_byte(ATARI_CKS_LOC + 1) == (sum & 0xff));
}
-static void
-atari_set_checksum(void)
+static void atari_set_checksum(void)
{
int i;
unsigned char sum = 0;
@@ -654,82 +643,75 @@ static char *colors[] = {
"2", "4", "16", "256", "65536", "??", "??", "??"
};
-static int
-atari_proc_infos(unsigned char *nvram, char *buffer, int *len,
- off_t *begin, off_t offset, int size)
+static void atari_proc_infos(unsigned char *nvram, struct seq_file *seq,
+ void *offset)
{
int checksum = nvram_check_checksum();
int i;
unsigned vmode;
- PRINT_PROC("Checksum status : %svalid\n", checksum ? "" : "not ");
+ seq_printf(seq, "Checksum status : %svalid\n", checksum ? "" : "not ");
- PRINT_PROC("Boot preference : ");
+ seq_printf(seq, "Boot preference : ");
for (i = ARRAY_SIZE(boot_prefs) - 1; i >= 0; --i) {
if (nvram[1] == boot_prefs[i].val) {
- PRINT_PROC("%s\n", boot_prefs[i].name);
+ seq_printf(seq, "%s\n", boot_prefs[i].name);
break;
}
}
if (i < 0)
- PRINT_PROC("0x%02x (undefined)\n", nvram[1]);
+ seq_printf(seq, "0x%02x (undefined)\n", nvram[1]);
- PRINT_PROC("SCSI arbitration : %s\n",
+ seq_printf(seq, "SCSI arbitration : %s\n",
(nvram[16] & 0x80) ? "on" : "off");
- PRINT_PROC("SCSI host ID : ");
+ seq_printf(seq, "SCSI host ID : ");
if (nvram[16] & 0x80)
- PRINT_PROC("%d\n", nvram[16] & 7);
+ seq_printf(seq, "%d\n", nvram[16] & 7);
else
- PRINT_PROC("n/a\n");
+ seq_printf(seq, "n/a\n");
/* the following entries are defined only for the Falcon */
if ((atari_mch_cookie >> 16) != ATARI_MCH_FALCON)
- return 1;
+ return;
- PRINT_PROC("OS language : ");
+ seq_printf(seq, "OS language : ");
if (nvram[6] < ARRAY_SIZE(languages))
- PRINT_PROC("%s\n", languages[nvram[6]]);
+ seq_printf(seq, "%s\n", languages[nvram[6]]);
else
- PRINT_PROC("%u (undefined)\n", nvram[6]);
- PRINT_PROC("Keyboard language: ");
+ seq_printf(seq, "%u (undefined)\n", nvram[6]);
+ seq_printf(seq, "Keyboard language: ");
if (nvram[7] < ARRAY_SIZE(languages))
- PRINT_PROC("%s\n", languages[nvram[7]]);
+ seq_printf(seq, "%s\n", languages[nvram[7]]);
else
- PRINT_PROC("%u (undefined)\n", nvram[7]);
- PRINT_PROC("Date format : ");
- PRINT_PROC(dateformat[nvram[8] & 7],
+ seq_printf(seq, "%u (undefined)\n", nvram[7]);
+ seq_printf(seq, "Date format : ");
+ seq_printf(seq, dateformat[nvram[8] & 7],
nvram[9] ? nvram[9] : '/', nvram[9] ? nvram[9] : '/');
- PRINT_PROC(", %dh clock\n", nvram[8] & 16 ? 24 : 12);
- PRINT_PROC("Boot delay : ");
+ seq_printf(seq, ", %dh clock\n", nvram[8] & 16 ? 24 : 12);
+ seq_printf(seq, "Boot delay : ");
if (nvram[10] == 0)
- PRINT_PROC("default");
+ seq_printf(seq, "default");
else
- PRINT_PROC("%ds%s\n", nvram[10],
+ seq_printf(seq, "%ds%s\n", nvram[10],
nvram[10] < 8 ? ", no memory test" : "");
vmode = (nvram[14] << 8) || nvram[15];
- PRINT_PROC("Video mode : %s colors, %d columns, %s %s monitor\n",
+ seq_printf(seq,
+ "Video mode : %s colors, %d columns, %s %s monitor\n",
colors[vmode & 7],
vmode & 8 ? 80 : 40,
vmode & 16 ? "VGA" : "TV", vmode & 32 ? "PAL" : "NTSC");
- PRINT_PROC(" %soverscan, compat. mode %s%s\n",
+ seq_printf(seq, " %soverscan, compat. mode %s%s\n",
vmode & 64 ? "" : "no ",
vmode & 128 ? "on" : "off",
vmode & 256 ?
(vmode & 16 ? ", line doubling" : ", half screen") : "");
- return 1;
+ return;
}
#endif
#endif /* MACH == ATARI */
MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(__nvram_read_byte);
-EXPORT_SYMBOL(nvram_read_byte);
-EXPORT_SYMBOL(__nvram_write_byte);
-EXPORT_SYMBOL(nvram_write_byte);
-EXPORT_SYMBOL(__nvram_check_checksum);
-EXPORT_SYMBOL(nvram_check_checksum);
MODULE_ALIAS_MISCDEV(NVRAM_MINOR);
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index 006be92ee3f3..8c7df5ba088f 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -58,8 +58,6 @@ static volatile unsigned char *FLASH_BASE;
static int gbFlashSize = KFLASH_SIZE;
static DEFINE_MUTEX(nwflash_mutex);
-extern spinlock_t gpio_lock;
-
static int get_flash_id(void)
{
volatile unsigned int c1, c2;
@@ -616,9 +614,9 @@ static void kick_open(void)
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(1, 1);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c
index 5787249934c8..34ab6d798f81 100644
--- a/drivers/char/tty_audit.c
+++ b/drivers/char/tty_audit.c
@@ -67,6 +67,29 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf)
tty_audit_buf_free(buf);
}
+static void tty_audit_log(const char *description, struct task_struct *tsk,
+ uid_t loginuid, unsigned sessionid, int major,
+ int minor, unsigned char *data, size_t size)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
+ if (ab) {
+ char name[sizeof(tsk->comm)];
+ uid_t uid = task_uid(tsk);
+
+ audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u "
+ "major=%d minor=%d comm=", description,
+ tsk->pid, uid, loginuid, sessionid,
+ major, minor);
+ get_task_comm(name, tsk);
+ audit_log_untrustedstring(ab, name);
+ audit_log_format(ab, " data=");
+ audit_log_n_hex(ab, data, size);
+ audit_log_end(ab);
+ }
+}
+
/**
* tty_audit_buf_push - Push buffered data out
*
@@ -77,25 +100,12 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid,
unsigned int sessionid,
struct tty_audit_buf *buf)
{
- struct audit_buffer *ab;
-
if (buf->valid == 0)
return;
if (audit_enabled == 0)
return;
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
- if (ab) {
- char name[sizeof(tsk->comm)];
-
- audit_log_format(ab, "tty pid=%u uid=%u auid=%u ses=%u "
- "major=%d minor=%d comm=", tsk->pid, tsk->uid,
- loginuid, sessionid, buf->major, buf->minor);
- get_task_comm(name, tsk);
- audit_log_untrustedstring(ab, name);
- audit_log_format(ab, " data=");
- audit_log_n_hex(ab, buf->data, buf->valid);
- audit_log_end(ab);
- }
+ tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor,
+ buf->data, buf->valid);
buf->valid = 0;
}
@@ -150,6 +160,42 @@ void tty_audit_fork(struct signal_struct *sig)
}
/**
+ * tty_audit_tiocsti - Log TIOCSTI
+ */
+void tty_audit_tiocsti(struct tty_struct *tty, char ch)
+{
+ struct tty_audit_buf *buf;
+ int major, minor, should_audit;
+
+ spin_lock_irq(&current->sighand->siglock);
+ should_audit = current->signal->audit_tty;
+ buf = current->signal->tty_audit_buf;
+ if (buf)
+ atomic_inc(&buf->count);
+ spin_unlock_irq(&current->sighand->siglock);
+
+ major = tty->driver->major;
+ minor = tty->driver->minor_start + tty->index;
+ if (buf) {
+ mutex_lock(&buf->mutex);
+ if (buf->major == major && buf->minor == minor)
+ tty_audit_buf_push_current(buf);
+ mutex_unlock(&buf->mutex);
+ tty_audit_buf_put(buf);
+ }
+
+ if (should_audit && audit_enabled) {
+ uid_t auid;
+ unsigned int sessionid;
+
+ auid = audit_get_loginuid(current);
+ sessionid = audit_get_sessionid(current);
+ tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major,
+ minor, &ch, 1);
+ }
+}
+
+/**
* tty_audit_push_task - Flush task's pending audit data
*/
void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 1412a8d1e58d..db15f9ba7c0b 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -2018,6 +2018,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
return -EPERM;
if (get_user(ch, p))
return -EFAULT;
+ tty_audit_tiocsti(tty, ch);
ld = tty_ldisc_ref_wait(tty);
ld->ops->receive_buf(tty, &ch, &mbz, 1);
tty_ldisc_deref(ld);
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 3fb0d2c88ba5..ff6f5a4b58fb 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -138,12 +138,33 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
}
/*
+ * virtio console configuration. This supports:
+ * - console resize
+ */
+static void virtcons_apply_config(struct virtio_device *dev)
+{
+ struct winsize ws;
+
+ if (virtio_has_feature(dev, VIRTIO_CONSOLE_F_SIZE)) {
+ dev->config->get(dev,
+ offsetof(struct virtio_console_config, cols),
+ &ws.ws_col, sizeof(u16));
+ dev->config->get(dev,
+ offsetof(struct virtio_console_config, rows),
+ &ws.ws_row, sizeof(u16));
+ hvc_resize(hvc, ws);
+ }
+}
+
+/*
* we support only one console, the hvc struct is a global var
- * There is no need to do anything
+ * We set the configuration at this point, since we now have a tty
*/
static int notifier_add_vio(struct hvc_struct *hp, int data)
{
hp->irq_requested = 1;
+ virtcons_apply_config(vdev);
+
return 0;
}
@@ -234,11 +255,18 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
+static unsigned int features[] = {
+ VIRTIO_CONSOLE_F_SIZE,
+};
+
static struct virtio_driver virtio_console = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = virtcons_probe,
+ .config_changed = virtcons_apply_config,
};
static int __init init(void)
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index c20171078d1d..e1129fad96dd 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -57,11 +57,6 @@ u32 acpi_pm_read_verified(void)
return v2;
}
-static cycle_t acpi_pm_read_slow(void)
-{
- return (cycle_t)acpi_pm_read_verified();
-}
-
static cycle_t acpi_pm_read(void)
{
return (cycle_t)read_pmtmr();
@@ -88,6 +83,11 @@ static int __init acpi_pm_good_setup(char *__str)
}
__setup("acpi_pm_good", acpi_pm_good_setup);
+static cycle_t acpi_pm_read_slow(void)
+{
+ return (cycle_t)acpi_pm_read_verified();
+}
+
static inline void acpi_pm_need_workaround(void)
{
clocksource_acpi_pm.read = acpi_pm_read_slow;
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 5c9f67f98d10..c5afc98e2675 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -106,6 +106,7 @@ void proc_id_connector(struct task_struct *task, int which_id)
struct proc_event *ev;
__u8 buffer[CN_PROC_MSG_SIZE];
struct timespec ts;
+ const struct cred *cred;
if (atomic_read(&proc_event_num_listeners) < 1)
return;
@@ -115,14 +116,19 @@ void proc_id_connector(struct task_struct *task, int which_id)
ev->what = which_id;
ev->event_data.id.process_pid = task->pid;
ev->event_data.id.process_tgid = task->tgid;
+ rcu_read_lock();
+ cred = __task_cred(task);
if (which_id == PROC_EVENT_UID) {
- ev->event_data.id.r.ruid = task->uid;
- ev->event_data.id.e.euid = task->euid;
+ ev->event_data.id.r.ruid = cred->uid;
+ ev->event_data.id.e.euid = cred->euid;
} else if (which_id == PROC_EVENT_GID) {
- ev->event_data.id.r.rgid = task->gid;
- ev->event_data.id.e.egid = task->egid;
- } else
+ ev->event_data.id.r.rgid = cred->gid;
+ ev->event_data.id.e.egid = cred->egid;
+ } else {
+ rcu_read_unlock();
return;
+ }
+ rcu_read_unlock();
get_seq(&msg->seq, &ev->cpu);
ktime_get_ts(&ts); /* get high res monotonic timestamp */
put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 4d22b21bd3e3..0c79fe7f1567 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -38,9 +38,6 @@
#include <asm/kmap_types.h>
-#undef dprintk
-
-#define HIFN_TEST
//#define HIFN_DEBUG
#ifdef HIFN_DEBUG
@@ -363,14 +360,14 @@ static atomic_t hifn_dev_number;
#define HIFN_NAMESIZE 32
#define HIFN_MAX_RESULT_ORDER 5
-#define HIFN_D_CMD_RSIZE 24*4
-#define HIFN_D_SRC_RSIZE 80*4
-#define HIFN_D_DST_RSIZE 80*4
-#define HIFN_D_RES_RSIZE 24*4
+#define HIFN_D_CMD_RSIZE 24*1
+#define HIFN_D_SRC_RSIZE 80*1
+#define HIFN_D_DST_RSIZE 80*1
+#define HIFN_D_RES_RSIZE 24*1
#define HIFN_D_DST_DALIGN 4
-#define HIFN_QUEUE_LENGTH HIFN_D_CMD_RSIZE-1
+#define HIFN_QUEUE_LENGTH (HIFN_D_CMD_RSIZE - 1)
#define AES_MIN_KEY_SIZE 16
#define AES_MAX_KEY_SIZE 32
@@ -406,8 +403,6 @@ struct hifn_dma {
u8 command_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_COMMAND];
u8 result_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_RESULT];
- u64 test_src, test_dst;
-
/*
* Our current positions for insertion and removal from the descriptor
* rings.
@@ -434,9 +429,6 @@ struct hifn_device
struct pci_dev *pdev;
void __iomem *bar[3];
- unsigned long result_mem;
- dma_addr_t dst;
-
void *desc_virt;
dma_addr_t desc_dma;
@@ -446,8 +438,6 @@ struct hifn_device
spinlock_t lock;
- void *priv;
-
u32 flags;
int active, started;
struct delayed_work work;
@@ -657,12 +647,17 @@ struct ablkcipher_walk
struct hifn_context
{
- u8 key[HIFN_MAX_CRYPT_KEY_LENGTH], *iv;
+ u8 key[HIFN_MAX_CRYPT_KEY_LENGTH];
struct hifn_device *dev;
- unsigned int keysize, ivsize;
+ unsigned int keysize;
+};
+
+struct hifn_request_context
+{
+ u8 *iv;
+ unsigned int ivsize;
u8 op, type, mode, unused;
struct ablkcipher_walk walk;
- atomic_t sg_num;
};
#define crypto_alg_to_hifn(a) container_of(a, struct hifn_crypto_alg, alg)
@@ -1168,7 +1163,8 @@ static int hifn_setup_crypto_command(struct hifn_device *dev,
}
static int hifn_setup_cmd_desc(struct hifn_device *dev,
- struct hifn_context *ctx, void *priv, unsigned int nbytes)
+ struct hifn_context *ctx, struct hifn_request_context *rctx,
+ void *priv, unsigned int nbytes)
{
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int cmd_len, sa_idx;
@@ -1179,7 +1175,7 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
buf_pos = buf = dma->command_bufs[dma->cmdi];
mask = 0;
- switch (ctx->op) {
+ switch (rctx->op) {
case ACRYPTO_OP_DECRYPT:
mask = HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE;
break;
@@ -1196,15 +1192,15 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
buf_pos += hifn_setup_base_command(dev, buf_pos, nbytes,
nbytes, mask, dev->snum);
- if (ctx->op == ACRYPTO_OP_ENCRYPT || ctx->op == ACRYPTO_OP_DECRYPT) {
+ if (rctx->op == ACRYPTO_OP_ENCRYPT || rctx->op == ACRYPTO_OP_DECRYPT) {
u16 md = 0;
if (ctx->keysize)
md |= HIFN_CRYPT_CMD_NEW_KEY;
- if (ctx->iv && ctx->mode != ACRYPTO_MODE_ECB)
+ if (rctx->iv && rctx->mode != ACRYPTO_MODE_ECB)
md |= HIFN_CRYPT_CMD_NEW_IV;
- switch (ctx->mode) {
+ switch (rctx->mode) {
case ACRYPTO_MODE_ECB:
md |= HIFN_CRYPT_CMD_MODE_ECB;
break;
@@ -1221,7 +1217,7 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
goto err_out;
}
- switch (ctx->type) {
+ switch (rctx->type) {
case ACRYPTO_TYPE_AES_128:
if (ctx->keysize != 16)
goto err_out;
@@ -1256,17 +1252,18 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
buf_pos += hifn_setup_crypto_command(dev, buf_pos,
nbytes, nbytes, ctx->key, ctx->keysize,
- ctx->iv, ctx->ivsize, md);
+ rctx->iv, rctx->ivsize, md);
}
dev->sa[sa_idx] = priv;
+ dev->started++;
cmd_len = buf_pos - buf;
dma->cmdr[dma->cmdi].l = __cpu_to_le32(cmd_len | HIFN_D_VALID |
HIFN_D_LAST | HIFN_D_MASKDONEIRQ);
if (++dma->cmdi == HIFN_D_CMD_RSIZE) {
- dma->cmdr[dma->cmdi].l = __cpu_to_le32(HIFN_MAX_COMMAND |
+ dma->cmdr[dma->cmdi].l = __cpu_to_le32(
HIFN_D_VALID | HIFN_D_LAST |
HIFN_D_MASKDONEIRQ | HIFN_D_JUMP);
dma->cmdi = 0;
@@ -1284,7 +1281,7 @@ err_out:
}
static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
- unsigned int offset, unsigned int size)
+ unsigned int offset, unsigned int size, int last)
{
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int idx;
@@ -1296,12 +1293,12 @@ static int hifn_setup_src_desc(struct hifn_device *dev, struct page *page,
dma->srcr[idx].p = __cpu_to_le32(addr);
dma->srcr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
- HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+ HIFN_D_MASKDONEIRQ | (last ? HIFN_D_LAST : 0));
if (++idx == HIFN_D_SRC_RSIZE) {
dma->srcr[idx].l = __cpu_to_le32(HIFN_D_VALID |
- HIFN_D_JUMP |
- HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+ HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
+ (last ? HIFN_D_LAST : 0));
idx = 0;
}
@@ -1342,7 +1339,7 @@ static void hifn_setup_res_desc(struct hifn_device *dev)
}
static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
- unsigned offset, unsigned size)
+ unsigned offset, unsigned size, int last)
{
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int idx;
@@ -1353,12 +1350,12 @@ static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
idx = dma->dsti;
dma->dstr[idx].p = __cpu_to_le32(addr);
dma->dstr[idx].l = __cpu_to_le32(size | HIFN_D_VALID |
- HIFN_D_MASKDONEIRQ | HIFN_D_LAST);
+ HIFN_D_MASKDONEIRQ | (last ? HIFN_D_LAST : 0));
if (++idx == HIFN_D_DST_RSIZE) {
dma->dstr[idx].l = __cpu_to_le32(HIFN_D_VALID |
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ |
- HIFN_D_LAST);
+ (last ? HIFN_D_LAST : 0));
idx = 0;
}
dma->dsti = idx;
@@ -1370,16 +1367,52 @@ static void hifn_setup_dst_desc(struct hifn_device *dev, struct page *page,
}
}
-static int hifn_setup_dma(struct hifn_device *dev, struct page *spage, unsigned int soff,
- struct page *dpage, unsigned int doff, unsigned int nbytes, void *priv,
- struct hifn_context *ctx)
+static int hifn_setup_dma(struct hifn_device *dev,
+ struct hifn_context *ctx, struct hifn_request_context *rctx,
+ struct scatterlist *src, struct scatterlist *dst,
+ unsigned int nbytes, void *priv)
{
- dprintk("%s: spage: %p, soffset: %u, dpage: %p, doffset: %u, nbytes: %u, priv: %p, ctx: %p.\n",
- dev->name, spage, soff, dpage, doff, nbytes, priv, ctx);
+ struct scatterlist *t;
+ struct page *spage, *dpage;
+ unsigned int soff, doff;
+ unsigned int n, len;
- hifn_setup_src_desc(dev, spage, soff, nbytes);
- hifn_setup_cmd_desc(dev, ctx, priv, nbytes);
- hifn_setup_dst_desc(dev, dpage, doff, nbytes);
+ n = nbytes;
+ while (n) {
+ spage = sg_page(src);
+ soff = src->offset;
+ len = min(src->length, n);
+
+ hifn_setup_src_desc(dev, spage, soff, len, n - len == 0);
+
+ src++;
+ n -= len;
+ }
+
+ t = &rctx->walk.cache[0];
+ n = nbytes;
+ while (n) {
+ if (t->length && rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+ BUG_ON(!sg_page(t));
+ dpage = sg_page(t);
+ doff = 0;
+ len = t->length;
+ } else {
+ BUG_ON(!sg_page(dst));
+ dpage = sg_page(dst);
+ doff = dst->offset;
+ len = dst->length;
+ }
+ len = min(len, n);
+
+ hifn_setup_dst_desc(dev, dpage, doff, len, n - len == 0);
+
+ dst++;
+ t++;
+ n -= len;
+ }
+
+ hifn_setup_cmd_desc(dev, ctx, rctx, priv, nbytes);
hifn_setup_res_desc(dev);
return 0;
}
@@ -1424,32 +1457,26 @@ static void ablkcipher_walk_exit(struct ablkcipher_walk *w)
w->num = 0;
}
-static int ablkcipher_add(void *daddr, unsigned int *drestp, struct scatterlist *src,
+static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst,
unsigned int size, unsigned int *nbytesp)
{
unsigned int copy, drest = *drestp, nbytes = *nbytesp;
int idx = 0;
- void *saddr;
if (drest < size || size > nbytes)
return -EINVAL;
while (size) {
- copy = min(drest, min(size, src->length));
-
- saddr = kmap_atomic(sg_page(src), KM_SOFTIRQ1);
- memcpy(daddr, saddr + src->offset, copy);
- kunmap_atomic(saddr, KM_SOFTIRQ1);
+ copy = min(drest, min(size, dst->length));
size -= copy;
drest -= copy;
nbytes -= copy;
- daddr += copy;
dprintk("%s: copy: %u, size: %u, drest: %u, nbytes: %u.\n",
__func__, copy, size, drest, nbytes);
- src++;
+ dst++;
idx++;
}
@@ -1462,8 +1489,7 @@ static int ablkcipher_add(void *daddr, unsigned int *drestp, struct scatterlist
static int ablkcipher_walk(struct ablkcipher_request *req,
struct ablkcipher_walk *w)
{
- struct scatterlist *src, *dst, *t;
- void *daddr;
+ struct scatterlist *dst, *t;
unsigned int nbytes = req->nbytes, offset, copy, diff;
int idx, tidx, err;
@@ -1473,26 +1499,22 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
if (idx >= w->num && (w->flags & ASYNC_FLAGS_MISALIGNED))
return -EINVAL;
- src = &req->src[idx];
dst = &req->dst[idx];
- dprintk("\n%s: slen: %u, dlen: %u, soff: %u, doff: %u, offset: %u, "
- "nbytes: %u.\n",
- __func__, src->length, dst->length, src->offset,
- dst->offset, offset, nbytes);
+ dprintk("\n%s: dlen: %u, doff: %u, offset: %u, nbytes: %u.\n",
+ __func__, dst->length, dst->offset, offset, nbytes);
if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
!IS_ALIGNED(dst->length, HIFN_D_DST_DALIGN) ||
offset) {
- unsigned slen = min(src->length - offset, nbytes);
+ unsigned slen = min(dst->length - offset, nbytes);
unsigned dlen = PAGE_SIZE;
t = &w->cache[idx];
- daddr = kmap_atomic(sg_page(t), KM_SOFTIRQ0);
- err = ablkcipher_add(daddr, &dlen, src, slen, &nbytes);
+ err = ablkcipher_add(&dlen, dst, slen, &nbytes);
if (err < 0)
- goto err_out_unmap;
+ return err;
idx += err;
@@ -1528,21 +1550,19 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
} else {
copy += diff + nbytes;
- src = &req->src[idx];
+ dst = &req->dst[idx];
- err = ablkcipher_add(daddr + slen, &dlen, src, nbytes, &nbytes);
+ err = ablkcipher_add(&dlen, dst, nbytes, &nbytes);
if (err < 0)
- goto err_out_unmap;
+ return err;
idx += err;
}
t->length = copy;
t->offset = offset;
-
- kunmap_atomic(daddr, KM_SOFTIRQ0);
} else {
- nbytes -= min(src->length, nbytes);
+ nbytes -= min(dst->length, nbytes);
idx++;
}
@@ -1550,26 +1570,22 @@ static int ablkcipher_walk(struct ablkcipher_request *req,
}
return tidx;
-
-err_out_unmap:
- kunmap_atomic(daddr, KM_SOFTIRQ0);
- return err;
}
static int hifn_setup_session(struct ablkcipher_request *req)
{
struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
struct hifn_device *dev = ctx->dev;
- struct page *spage, *dpage;
- unsigned long soff, doff, dlen, flags;
- unsigned int nbytes = req->nbytes, idx = 0, len;
+ unsigned long dlen, flags;
+ unsigned int nbytes = req->nbytes, idx = 0;
int err = -EINVAL, sg_num;
- struct scatterlist *src, *dst, *t;
+ struct scatterlist *dst;
- if (ctx->iv && !ctx->ivsize && ctx->mode != ACRYPTO_MODE_ECB)
+ if (rctx->iv && !rctx->ivsize && rctx->mode != ACRYPTO_MODE_ECB)
goto err_out_exit;
- ctx->walk.flags = 0;
+ rctx->walk.flags = 0;
while (nbytes) {
dst = &req->dst[idx];
@@ -1577,27 +1593,23 @@ static int hifn_setup_session(struct ablkcipher_request *req)
if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
!IS_ALIGNED(dlen, HIFN_D_DST_DALIGN))
- ctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
+ rctx->walk.flags |= ASYNC_FLAGS_MISALIGNED;
nbytes -= dlen;
idx++;
}
- if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
- err = ablkcipher_walk_init(&ctx->walk, idx, GFP_ATOMIC);
+ if (rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
+ err = ablkcipher_walk_init(&rctx->walk, idx, GFP_ATOMIC);
if (err < 0)
return err;
}
- nbytes = req->nbytes;
- idx = 0;
-
- sg_num = ablkcipher_walk(req, &ctx->walk);
+ sg_num = ablkcipher_walk(req, &rctx->walk);
if (sg_num < 0) {
err = sg_num;
goto err_out_exit;
}
- atomic_set(&ctx->sg_num, sg_num);
spin_lock_irqsave(&dev->lock, flags);
if (dev->started + sg_num > HIFN_QUEUE_LENGTH) {
@@ -1605,37 +1617,11 @@ static int hifn_setup_session(struct ablkcipher_request *req)
goto err_out;
}
- dev->snum++;
- dev->started += sg_num;
-
- while (nbytes) {
- src = &req->src[idx];
- dst = &req->dst[idx];
- t = &ctx->walk.cache[idx];
-
- if (t->length) {
- spage = dpage = sg_page(t);
- soff = doff = 0;
- len = t->length;
- } else {
- spage = sg_page(src);
- soff = src->offset;
-
- dpage = sg_page(dst);
- doff = dst->offset;
-
- len = dst->length;
- }
-
- idx++;
-
- err = hifn_setup_dma(dev, spage, soff, dpage, doff, nbytes,
- req, ctx);
- if (err)
- goto err_out;
+ err = hifn_setup_dma(dev, ctx, rctx, req->src, req->dst, req->nbytes, req);
+ if (err)
+ goto err_out;
- nbytes -= min(len, nbytes);
- }
+ dev->snum++;
dev->active = HIFN_DEFAULT_ACTIVE_NUM;
spin_unlock_irqrestore(&dev->lock, flags);
@@ -1645,12 +1631,13 @@ static int hifn_setup_session(struct ablkcipher_request *req)
err_out:
spin_unlock_irqrestore(&dev->lock, flags);
err_out_exit:
- if (err)
- dprintk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
+ if (err) {
+ printk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
"type: %u, err: %d.\n",
- dev->name, ctx->iv, ctx->ivsize,
+ dev->name, rctx->iv, rctx->ivsize,
ctx->key, ctx->keysize,
- ctx->mode, ctx->op, ctx->type, err);
+ rctx->mode, rctx->op, rctx->type, err);
+ }
return err;
}
@@ -1660,31 +1647,33 @@ static int hifn_test(struct hifn_device *dev, int encdec, u8 snum)
int n, err;
u8 src[16];
struct hifn_context ctx;
+ struct hifn_request_context rctx;
u8 fips_aes_ecb_from_zero[16] = {
0x66, 0xE9, 0x4B, 0xD4,
0xEF, 0x8A, 0x2C, 0x3B,
0x88, 0x4C, 0xFA, 0x59,
0xCA, 0x34, 0x2B, 0x2E};
+ struct scatterlist sg;
memset(src, 0, sizeof(src));
memset(ctx.key, 0, sizeof(ctx.key));
ctx.dev = dev;
ctx.keysize = 16;
- ctx.ivsize = 0;
- ctx.iv = NULL;
- ctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
- ctx.mode = ACRYPTO_MODE_ECB;
- ctx.type = ACRYPTO_TYPE_AES_128;
- atomic_set(&ctx.sg_num, 1);
-
- err = hifn_setup_dma(dev,
- virt_to_page(src), offset_in_page(src),
- virt_to_page(src), offset_in_page(src),
- sizeof(src), NULL, &ctx);
+ rctx.ivsize = 0;
+ rctx.iv = NULL;
+ rctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
+ rctx.mode = ACRYPTO_MODE_ECB;
+ rctx.type = ACRYPTO_TYPE_AES_128;
+ rctx.walk.cache[0].length = 0;
+
+ sg_init_one(&sg, &src, sizeof(src));
+
+ err = hifn_setup_dma(dev, &ctx, &rctx, &sg, &sg, sizeof(src), NULL);
if (err)
goto err_out;
+ dev->started = 0;
msleep(200);
dprintk("%s: decoded: ", dev->name);
@@ -1711,6 +1700,7 @@ static int hifn_start_device(struct hifn_device *dev)
{
int err;
+ dev->started = dev->active = 0;
hifn_reset_dma(dev, 1);
err = hifn_enable_crypto(dev);
@@ -1764,90 +1754,65 @@ static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset
return idx;
}
-static void hifn_process_ready(struct ablkcipher_request *req, int error)
+static inline void hifn_complete_sa(struct hifn_device *dev, int i)
{
- struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
- struct hifn_device *dev;
-
- dprintk("%s: req: %p, ctx: %p.\n", __func__, req, ctx);
+ unsigned long flags;
- dev = ctx->dev;
- dprintk("%s: req: %p, started: %d, sg_num: %d.\n",
- __func__, req, dev->started, atomic_read(&ctx->sg_num));
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->sa[i] = NULL;
+ dev->started--;
+ if (dev->started < 0)
+ printk("%s: started: %d.\n", __func__, dev->started);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ BUG_ON(dev->started < 0);
+}
- if (--dev->started < 0)
- BUG();
+static void hifn_process_ready(struct ablkcipher_request *req, int error)
+{
+ struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
- if (atomic_dec_and_test(&ctx->sg_num)) {
+ if (rctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
unsigned int nbytes = req->nbytes;
int idx = 0, err;
struct scatterlist *dst, *t;
void *saddr;
- if (ctx->walk.flags & ASYNC_FLAGS_MISALIGNED) {
- while (nbytes) {
- t = &ctx->walk.cache[idx];
- dst = &req->dst[idx];
-
- dprintk("\n%s: sg_page(t): %p, t->length: %u, "
- "sg_page(dst): %p, dst->length: %u, "
- "nbytes: %u.\n",
- __func__, sg_page(t), t->length,
- sg_page(dst), dst->length, nbytes);
+ while (nbytes) {
+ t = &rctx->walk.cache[idx];
+ dst = &req->dst[idx];
- if (!t->length) {
- nbytes -= min(dst->length, nbytes);
- idx++;
- continue;
- }
+ dprintk("\n%s: sg_page(t): %p, t->length: %u, "
+ "sg_page(dst): %p, dst->length: %u, "
+ "nbytes: %u.\n",
+ __func__, sg_page(t), t->length,
+ sg_page(dst), dst->length, nbytes);
- saddr = kmap_atomic(sg_page(t), KM_IRQ1);
+ if (!t->length) {
+ nbytes -= min(dst->length, nbytes);
+ idx++;
+ continue;
+ }
- err = ablkcipher_get(saddr, &t->length, t->offset,
- dst, nbytes, &nbytes);
- if (err < 0) {
- kunmap_atomic(saddr, KM_IRQ1);
- break;
- }
+ saddr = kmap_atomic(sg_page(t), KM_SOFTIRQ0);
- idx += err;
- kunmap_atomic(saddr, KM_IRQ1);
+ err = ablkcipher_get(saddr, &t->length, t->offset,
+ dst, nbytes, &nbytes);
+ if (err < 0) {
+ kunmap_atomic(saddr, KM_SOFTIRQ0);
+ break;
}
- ablkcipher_walk_exit(&ctx->walk);
+ idx += err;
+ kunmap_atomic(saddr, KM_SOFTIRQ0);
}
- req->base.complete(&req->base, error);
+ ablkcipher_walk_exit(&rctx->walk);
}
-}
-static void hifn_check_for_completion(struct hifn_device *dev, int error)
-{
- int i;
- struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
-
- for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
- struct hifn_desc *d = &dma->resr[i];
-
- if (!(d->l & __cpu_to_le32(HIFN_D_VALID)) && dev->sa[i]) {
- dev->success++;
- dev->reset = 0;
- hifn_process_ready(dev->sa[i], error);
- dev->sa[i] = NULL;
- }
-
- if (d->l & __cpu_to_le32(HIFN_D_DESTOVER | HIFN_D_OVER))
- if (printk_ratelimit())
- printk("%s: overflow detected [d: %u, o: %u] "
- "at %d resr: l: %08x, p: %08x.\n",
- dev->name,
- !!(d->l & __cpu_to_le32(HIFN_D_DESTOVER)),
- !!(d->l & __cpu_to_le32(HIFN_D_OVER)),
- i, d->l, d->p);
- }
+ req->base.complete(&req->base, error);
}
-static void hifn_clear_rings(struct hifn_device *dev)
+static void hifn_clear_rings(struct hifn_device *dev, int error)
{
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int i, u;
@@ -1864,21 +1829,26 @@ static void hifn_clear_rings(struct hifn_device *dev)
if (dma->resr[i].l & __cpu_to_le32(HIFN_D_VALID))
break;
- if (i != HIFN_D_RES_RSIZE)
- u--;
+ if (dev->sa[i]) {
+ dev->success++;
+ dev->reset = 0;
+ hifn_process_ready(dev->sa[i], error);
+ hifn_complete_sa(dev, i);
+ }
- if (++i == (HIFN_D_RES_RSIZE + 1))
+ if (++i == HIFN_D_RES_RSIZE)
i = 0;
+ u--;
}
dma->resk = i; dma->resu = u;
i = dma->srck; u = dma->srcu;
while (u != 0) {
- if (i == HIFN_D_SRC_RSIZE)
- i = 0;
if (dma->srcr[i].l & __cpu_to_le32(HIFN_D_VALID))
break;
- i++, u--;
+ if (++i == HIFN_D_SRC_RSIZE)
+ i = 0;
+ u--;
}
dma->srck = i; dma->srcu = u;
@@ -1886,20 +1856,19 @@ static void hifn_clear_rings(struct hifn_device *dev)
while (u != 0) {
if (dma->cmdr[i].l & __cpu_to_le32(HIFN_D_VALID))
break;
- if (i != HIFN_D_CMD_RSIZE)
- u--;
- if (++i == (HIFN_D_CMD_RSIZE + 1))
+ if (++i == HIFN_D_CMD_RSIZE)
i = 0;
+ u--;
}
dma->cmdk = i; dma->cmdu = u;
i = dma->dstk; u = dma->dstu;
while (u != 0) {
- if (i == HIFN_D_DST_RSIZE)
- i = 0;
if (dma->dstr[i].l & __cpu_to_le32(HIFN_D_VALID))
break;
- i++, u--;
+ if (++i == HIFN_D_DST_RSIZE)
+ i = 0;
+ u--;
}
dma->dstk = i; dma->dstu = u;
@@ -1944,30 +1913,39 @@ static void hifn_work(struct work_struct *work)
} else
dev->active--;
- if (dev->prev_success == dev->success && dev->started)
+ if ((dev->prev_success == dev->success) && dev->started)
reset = 1;
dev->prev_success = dev->success;
spin_unlock_irqrestore(&dev->lock, flags);
if (reset) {
- dprintk("%s: r: %08x, active: %d, started: %d, "
- "success: %lu: reset: %d.\n",
- dev->name, r, dev->active, dev->started,
- dev->success, reset);
-
if (++dev->reset >= 5) {
- dprintk("%s: really hard reset.\n", dev->name);
+ int i;
+ struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
+
+ printk("%s: r: %08x, active: %d, started: %d, "
+ "success: %lu: qlen: %u/%u, reset: %d.\n",
+ dev->name, r, dev->active, dev->started,
+ dev->success, dev->queue.qlen, dev->queue.max_qlen,
+ reset);
+
+ printk("%s: res: ", __func__);
+ for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
+ printk("%x.%p ", dma->resr[i].l, dev->sa[i]);
+ if (dev->sa[i]) {
+ hifn_process_ready(dev->sa[i], -ENODEV);
+ hifn_complete_sa(dev, i);
+ }
+ }
+ printk("\n");
+
hifn_reset_dma(dev, 1);
hifn_stop_device(dev);
hifn_start_device(dev);
dev->reset = 0;
}
- spin_lock_irqsave(&dev->lock, flags);
- hifn_check_for_completion(dev, -EBUSY);
- hifn_clear_rings(dev);
- dev->started = 0;
- spin_unlock_irqrestore(&dev->lock, flags);
+ tasklet_schedule(&dev->tasklet);
}
schedule_delayed_work(&dev->work, HZ);
@@ -1984,8 +1962,8 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
dprintk("%s: 1 dmacsr: %08x, dmareg: %08x, res: %08x [%d], "
"i: %d.%d.%d.%d, u: %d.%d.%d.%d.\n",
dev->name, dmacsr, dev->dmareg, dmacsr & dev->dmareg, dma->cmdi,
- dma->cmdu, dma->srcu, dma->dstu, dma->resu,
- dma->cmdi, dma->srci, dma->dsti, dma->resi);
+ dma->cmdi, dma->srci, dma->dsti, dma->resi,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu);
if ((dmacsr & dev->dmareg) == 0)
return IRQ_NONE;
@@ -2002,11 +1980,10 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
if (restart) {
u32 puisr = hifn_read_0(dev, HIFN_0_PUISR);
- if (printk_ratelimit())
- printk("%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
- dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
- !!(dmacsr & HIFN_DMACSR_D_OVER),
- puisr, !!(puisr & HIFN_PUISR_DSTOVER));
+ printk(KERN_WARNING "%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
+ dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
+ !!(dmacsr & HIFN_DMACSR_D_OVER),
+ puisr, !!(puisr & HIFN_PUISR_DSTOVER));
if (!!(puisr & HIFN_PUISR_DSTOVER))
hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
hifn_write_1(dev, HIFN_1_DMA_CSR, dmacsr & (HIFN_DMACSR_R_OVER |
@@ -2016,12 +1993,11 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
if (restart) {
- if (printk_ratelimit())
- printk("%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
- dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
- !!(dmacsr & HIFN_DMACSR_S_ABORT),
- !!(dmacsr & HIFN_DMACSR_D_ABORT),
- !!(dmacsr & HIFN_DMACSR_R_ABORT));
+ printk(KERN_WARNING "%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
+ dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
+ !!(dmacsr & HIFN_DMACSR_S_ABORT),
+ !!(dmacsr & HIFN_DMACSR_D_ABORT),
+ !!(dmacsr & HIFN_DMACSR_R_ABORT));
hifn_reset_dma(dev, 1);
hifn_init_dma(dev);
hifn_init_registers(dev);
@@ -2034,7 +2010,6 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
}
tasklet_schedule(&dev->tasklet);
- hifn_clear_rings(dev);
return IRQ_HANDLED;
}
@@ -2048,21 +2023,25 @@ static void hifn_flush(struct hifn_device *dev)
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int i;
- spin_lock_irqsave(&dev->lock, flags);
for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
struct hifn_desc *d = &dma->resr[i];
if (dev->sa[i]) {
hifn_process_ready(dev->sa[i],
(d->l & __cpu_to_le32(HIFN_D_VALID))?-ENODEV:0);
+ hifn_complete_sa(dev, i);
}
}
+ spin_lock_irqsave(&dev->lock, flags);
while ((async_req = crypto_dequeue_request(&dev->queue))) {
ctx = crypto_tfm_ctx(async_req->tfm);
req = container_of(async_req, struct ablkcipher_request, base);
+ spin_unlock_irqrestore(&dev->lock, flags);
hifn_process_ready(req, -ENODEV);
+
+ spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -2121,6 +2100,7 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
u8 type, u8 mode)
{
struct hifn_context *ctx = crypto_tfm_ctx(req->base.tfm);
+ struct hifn_request_context *rctx = ablkcipher_request_ctx(req);
unsigned ivsize;
ivsize = crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req));
@@ -2141,11 +2121,11 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
type = ACRYPTO_TYPE_AES_256;
}
- ctx->op = op;
- ctx->mode = mode;
- ctx->type = type;
- ctx->iv = req->info;
- ctx->ivsize = ivsize;
+ rctx->op = op;
+ rctx->mode = mode;
+ rctx->type = type;
+ rctx->iv = req->info;
+ rctx->ivsize = ivsize;
/*
* HEAVY TODO: needs to kick Herbert XU to write documentation.
@@ -2158,7 +2138,7 @@ static int hifn_setup_crypto_req(struct ablkcipher_request *req, u8 op,
static int hifn_process_queue(struct hifn_device *dev)
{
- struct crypto_async_request *async_req;
+ struct crypto_async_request *async_req, *backlog;
struct hifn_context *ctx;
struct ablkcipher_request *req;
unsigned long flags;
@@ -2166,12 +2146,16 @@ static int hifn_process_queue(struct hifn_device *dev)
while (dev->started < HIFN_QUEUE_LENGTH) {
spin_lock_irqsave(&dev->lock, flags);
+ backlog = crypto_get_backlog(&dev->queue);
async_req = crypto_dequeue_request(&dev->queue);
spin_unlock_irqrestore(&dev->lock, flags);
if (!async_req)
break;
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
ctx = crypto_tfm_ctx(async_req->tfm);
req = container_of(async_req, struct ablkcipher_request, base);
@@ -2496,7 +2480,7 @@ static int hifn_cra_init(struct crypto_tfm *tfm)
struct hifn_context *ctx = crypto_tfm_ctx(tfm);
ctx->dev = ha->dev;
-
+ tfm->crt_ablkcipher.reqsize = sizeof(struct hifn_request_context);
return 0;
}
@@ -2574,7 +2558,10 @@ static void hifn_tasklet_callback(unsigned long data)
* (like dev->success), but they are used in process
* context or update is atomic (like setting dev->sa[i] to NULL).
*/
- hifn_check_for_completion(dev, 0);
+ hifn_clear_rings(dev, 0);
+
+ if (dev->started < HIFN_QUEUE_LENGTH && dev->queue.qlen)
+ hifn_process_queue(dev);
}
static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -2631,22 +2618,11 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_out_unmap_bars;
}
- dev->result_mem = __get_free_pages(GFP_KERNEL, HIFN_MAX_RESULT_ORDER);
- if (!dev->result_mem) {
- dprintk("Failed to allocate %d pages for result_mem.\n",
- HIFN_MAX_RESULT_ORDER);
- goto err_out_unmap_bars;
- }
- memset((void *)dev->result_mem, 0, PAGE_SIZE*(1<<HIFN_MAX_RESULT_ORDER));
-
- dev->dst = pci_map_single(pdev, (void *)dev->result_mem,
- PAGE_SIZE << HIFN_MAX_RESULT_ORDER, PCI_DMA_FROMDEVICE);
-
dev->desc_virt = pci_alloc_consistent(pdev, sizeof(struct hifn_dma),
&dev->desc_dma);
if (!dev->desc_virt) {
dprintk("Failed to allocate descriptor rings.\n");
- goto err_out_free_result_pages;
+ goto err_out_unmap_bars;
}
memset(dev->desc_virt, 0, sizeof(struct hifn_dma));
@@ -2706,11 +2682,6 @@ err_out_free_desc:
pci_free_consistent(pdev, sizeof(struct hifn_dma),
dev->desc_virt, dev->desc_dma);
-err_out_free_result_pages:
- pci_unmap_single(pdev, dev->dst, PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
- PCI_DMA_FROMDEVICE);
- free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
-
err_out_unmap_bars:
for (i=0; i<3; ++i)
if (dev->bar[i])
@@ -2748,10 +2719,6 @@ static void hifn_remove(struct pci_dev *pdev)
pci_free_consistent(pdev, sizeof(struct hifn_dma),
dev->desc_virt, dev->desc_dma);
- pci_unmap_single(pdev, dev->dst,
- PAGE_SIZE << HIFN_MAX_RESULT_ORDER,
- PCI_DMA_FROMDEVICE);
- free_pages(dev->result_mem, HIFN_MAX_RESULT_ORDER);
for (i=0; i<3; ++i)
if (dev->bar[i])
iounmap(dev->bar[i]);
@@ -2782,6 +2749,11 @@ static int __devinit hifn_init(void)
unsigned int freq;
int err;
+ if (sizeof(dma_addr_t) > 4) {
+ printk(KERN_INFO "HIFN supports only 32-bit addresses.\n");
+ return -EINVAL;
+ }
+
if (strncmp(hifn_pll_ref, "ext", 3) &&
strncmp(hifn_pll_ref, "pci", 3)) {
printk(KERN_ERR "hifn795x: invalid hifn_pll_ref clock, "
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index bf2917d197a0..856b3cc25583 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -15,6 +15,8 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/percpu.h>
+#include <linux/smp.h>
#include <asm/byteorder.h>
#include <asm/i387.h>
#include "padlock.h"
@@ -49,6 +51,8 @@ struct aes_ctx {
u32 *D;
};
+static DEFINE_PER_CPU(struct cword *, last_cword);
+
/* Tells whether the ACE is capable to generate
the extended key for a given key_len. */
static inline int
@@ -89,6 +93,7 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
const __le32 *key = (const __le32 *)in_key;
u32 *flags = &tfm->crt_flags;
struct crypto_aes_ctx gen_aes;
+ int cpu;
if (key_len % 8) {
*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
@@ -118,7 +123,7 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
/* Don't generate extended keys if the hardware can do it. */
if (aes_hw_extkey_available(key_len))
- return 0;
+ goto ok;
ctx->D = ctx->d_data;
ctx->cword.encrypt.keygen = 1;
@@ -131,15 +136,30 @@ static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
memcpy(ctx->E, gen_aes.key_enc, AES_MAX_KEYLENGTH);
memcpy(ctx->D, gen_aes.key_dec, AES_MAX_KEYLENGTH);
+
+ok:
+ for_each_online_cpu(cpu)
+ if (&ctx->cword.encrypt == per_cpu(last_cword, cpu) ||
+ &ctx->cword.decrypt == per_cpu(last_cword, cpu))
+ per_cpu(last_cword, cpu) = NULL;
+
return 0;
}
/* ====== Encryption/decryption routines ====== */
/* These are the real call to PadLock. */
-static inline void padlock_reset_key(void)
+static inline void padlock_reset_key(struct cword *cword)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (cword != per_cpu(last_cword, cpu))
+ asm volatile ("pushfl; popfl");
+}
+
+static inline void padlock_store_cword(struct cword *cword)
{
- asm volatile ("pushfl; popfl");
+ per_cpu(last_cword, raw_smp_processor_id()) = cword;
}
/*
@@ -149,7 +169,7 @@ static inline void padlock_reset_key(void)
*/
static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key,
- void *control_word)
+ struct cword *control_word)
{
asm volatile (".byte 0xf3,0x0f,0xa7,0xc8" /* rep xcryptecb */
: "+S"(input), "+D"(output)
@@ -213,22 +233,24 @@ static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct aes_ctx *ctx = aes_ctx(tfm);
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.encrypt);
ts_state = irq_ts_save();
aes_crypt(in, out, ctx->E, &ctx->cword.encrypt);
irq_ts_restore(ts_state);
+ padlock_store_cword(&ctx->cword.encrypt);
}
static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
struct aes_ctx *ctx = aes_ctx(tfm);
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.encrypt);
ts_state = irq_ts_save();
aes_crypt(in, out, ctx->D, &ctx->cword.decrypt);
irq_ts_restore(ts_state);
+ padlock_store_cword(&ctx->cword.encrypt);
}
static struct crypto_alg aes_alg = {
@@ -261,7 +283,7 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
int err;
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.encrypt);
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -276,6 +298,8 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
}
irq_ts_restore(ts_state);
+ padlock_store_cword(&ctx->cword.encrypt);
+
return err;
}
@@ -288,7 +312,7 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
int err;
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.decrypt);
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -302,6 +326,9 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
err = blkcipher_walk_done(desc, &walk, nbytes);
}
irq_ts_restore(ts_state);
+
+ padlock_store_cword(&ctx->cword.encrypt);
+
return err;
}
@@ -336,7 +363,7 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
int err;
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.encrypt);
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -353,6 +380,8 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
}
irq_ts_restore(ts_state);
+ padlock_store_cword(&ctx->cword.decrypt);
+
return err;
}
@@ -365,7 +394,7 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
int err;
int ts_state;
- padlock_reset_key();
+ padlock_reset_key(&ctx->cword.encrypt);
blkcipher_walk_init(&walk, dst, src, nbytes);
err = blkcipher_walk_virt(desc, &walk);
@@ -380,6 +409,9 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
}
irq_ts_restore(ts_state);
+
+ padlock_store_cword(&ctx->cword.encrypt);
+
return err;
}
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 24607669a52b..a3918c16b3db 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -127,7 +127,6 @@ struct talitos_private {
/* request callback tasklet */
struct tasklet_struct done_task;
- struct tasklet_struct error_task;
/* list of registered algorithms */
struct list_head alg_list;
@@ -138,6 +137,7 @@ struct talitos_private {
/* .features flag */
#define TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT 0x00000001
+#define TALITOS_FTR_HW_AUTH_CHECK 0x00000002
/*
* map virtual single (contiguous) pointer to h/w descriptor pointer
@@ -184,6 +184,11 @@ static int reset_channel(struct device *dev, int ch)
setbits32(priv->reg + TALITOS_CCCR_LO(ch), TALITOS_CCCR_LO_CDWE |
TALITOS_CCCR_LO_CDIE);
+ /* and ICCR writeback, if available */
+ if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+ setbits32(priv->reg + TALITOS_CCCR_LO(ch),
+ TALITOS_CCCR_LO_IWSE);
+
return 0;
}
@@ -239,6 +244,11 @@ static int init_device(struct device *dev)
setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
+ /* disable integrity check error interrupts (use writeback instead) */
+ if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
+ setbits32(priv->reg + TALITOS_MDEUICR_LO,
+ TALITOS_MDEUICR_LO_ICE);
+
return 0;
}
@@ -370,6 +380,12 @@ static void talitos_done(unsigned long data)
for (ch = 0; ch < priv->num_channels; ch++)
flush_channel(dev, ch, 0, 0);
+
+ /* At this point, all completed channels have been processed.
+ * Unmask done interrupts for channels completed later on.
+ */
+ setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
+ setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
}
/*
@@ -469,16 +485,13 @@ static void report_eu_error(struct device *dev, int ch, struct talitos_desc *des
/*
* recover from error interrupts
*/
-static void talitos_error(unsigned long data)
+static void talitos_error(unsigned long data, u32 isr, u32 isr_lo)
{
struct device *dev = (struct device *)data;
struct talitos_private *priv = dev_get_drvdata(dev);
unsigned int timeout = TALITOS_TIMEOUT;
int ch, error, reset_dev = 0, reset_ch = 0;
- u32 isr, isr_lo, v, v_lo;
-
- isr = in_be32(priv->reg + TALITOS_ISR);
- isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
+ u32 v, v_lo;
for (ch = 0; ch < priv->num_channels; ch++) {
/* skip channels without errors */
@@ -560,16 +573,19 @@ static irqreturn_t talitos_interrupt(int irq, void *data)
isr = in_be32(priv->reg + TALITOS_ISR);
isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);
-
- /* ack */
+ /* Acknowledge interrupt */
out_be32(priv->reg + TALITOS_ICR, isr);
out_be32(priv->reg + TALITOS_ICR_LO, isr_lo);
if (unlikely((isr & ~TALITOS_ISR_CHDONE) || isr_lo))
- talitos_error((unsigned long)data);
+ talitos_error((unsigned long)data, isr, isr_lo);
else
- if (likely(isr & TALITOS_ISR_CHDONE))
+ if (likely(isr & TALITOS_ISR_CHDONE)) {
+ /* mask further done interrupts. */
+ clrbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_DONE);
+ /* done_task will unmask done interrupts at exit */
tasklet_schedule(&priv->done_task);
+ }
return (isr || isr_lo) ? IRQ_HANDLED : IRQ_NONE;
}
@@ -802,7 +818,7 @@ static void ipsec_esp_encrypt_done(struct device *dev,
aead_request_complete(areq, err);
}
-static void ipsec_esp_decrypt_done(struct device *dev,
+static void ipsec_esp_decrypt_swauth_done(struct device *dev,
struct talitos_desc *desc, void *context,
int err)
{
@@ -834,6 +850,27 @@ static void ipsec_esp_decrypt_done(struct device *dev,
aead_request_complete(req, err);
}
+static void ipsec_esp_decrypt_hwauth_done(struct device *dev,
+ struct talitos_desc *desc, void *context,
+ int err)
+{
+ struct aead_request *req = context;
+ struct ipsec_esp_edesc *edesc =
+ container_of(desc, struct ipsec_esp_edesc, desc);
+
+ ipsec_esp_unmap(dev, edesc, req);
+
+ /* check ICV auth status */
+ if (!err)
+ if ((desc->hdr_lo & DESC_HDR_LO_ICCR1_MASK) !=
+ DESC_HDR_LO_ICCR1_PASS)
+ err = -EBADMSG;
+
+ kfree(edesc);
+
+ aead_request_complete(req, err);
+}
+
/*
* convert scatterlist to SEC h/w link table format
* stop at cryptlen bytes
@@ -887,6 +924,7 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
unsigned int authsize = ctx->authsize;
unsigned int ivsize;
int sg_count, ret;
+ int sg_link_tbl_len;
/* hmac key */
map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
@@ -924,33 +962,19 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
if (sg_count == 1) {
desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
} else {
- sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
+ sg_link_tbl_len = cryptlen;
+
+ if ((edesc->desc.hdr & DESC_HDR_MODE1_MDEU_CICV) &&
+ (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
+ sg_link_tbl_len = cryptlen + authsize;
+ }
+ sg_count = sg_to_link_tbl(areq->src, sg_count, sg_link_tbl_len,
&edesc->link_tbl[0]);
if (sg_count > 1) {
- struct talitos_ptr *link_tbl_ptr =
- &edesc->link_tbl[sg_count-1];
- struct scatterlist *sg;
- struct talitos_private *priv = dev_get_drvdata(dev);
-
desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
desc->ptr[4].ptr = cpu_to_be32(edesc->dma_link_tbl);
dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
edesc->dma_len, DMA_BIDIRECTIONAL);
- /* If necessary for this SEC revision,
- * add a link table entry for ICV.
- */
- if ((priv->features &
- TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT) &&
- (edesc->desc.hdr & DESC_HDR_MODE0_ENCRYPT) == 0) {
- link_tbl_ptr->j_extent = 0;
- link_tbl_ptr++;
- link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
- link_tbl_ptr->len = cpu_to_be16(authsize);
- sg = sg_last(areq->src, edesc->src_nents ? : 1);
- link_tbl_ptr->ptr = cpu_to_be32(
- (char *)sg_dma_address(sg)
- + sg->length - authsize);
- }
} else {
/* Only one segment now, so no link tbl needed */
desc->ptr[4].ptr = cpu_to_be32(sg_dma_address(areq->src));
@@ -975,13 +999,9 @@ static int ipsec_esp(struct ipsec_esp_edesc *edesc, struct aead_request *areq,
desc->ptr[5].ptr = cpu_to_be32((struct talitos_ptr *)
edesc->dma_link_tbl +
edesc->src_nents + 1);
- if (areq->src == areq->dst) {
- memcpy(link_tbl_ptr, &edesc->link_tbl[0],
- edesc->src_nents * sizeof(struct talitos_ptr));
- } else {
- sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
- link_tbl_ptr);
- }
+ sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
+ link_tbl_ptr);
+
/* Add an entry to the link table for ICV data */
link_tbl_ptr += sg_count - 1;
link_tbl_ptr->j_extent = 0;
@@ -1106,11 +1126,14 @@ static int aead_authenc_encrypt(struct aead_request *req)
return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_encrypt_done);
}
+
+
static int aead_authenc_decrypt(struct aead_request *req)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct talitos_ctx *ctx = crypto_aead_ctx(authenc);
unsigned int authsize = ctx->authsize;
+ struct talitos_private *priv = dev_get_drvdata(ctx->dev);
struct ipsec_esp_edesc *edesc;
struct scatterlist *sg;
void *icvdata;
@@ -1122,22 +1145,39 @@ static int aead_authenc_decrypt(struct aead_request *req)
if (IS_ERR(edesc))
return PTR_ERR(edesc);
- /* stash incoming ICV for later cmp with ICV generated by the h/w */
- if (edesc->dma_len)
- icvdata = &edesc->link_tbl[edesc->src_nents +
- edesc->dst_nents + 2];
- else
- icvdata = &edesc->link_tbl[0];
+ if ((priv->features & TALITOS_FTR_HW_AUTH_CHECK) &&
+ (((!edesc->src_nents && !edesc->dst_nents) ||
+ priv->features & TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT))) {
+
+ /* decrypt and check the ICV */
+ edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND |
+ DESC_HDR_MODE1_MDEU_CICV;
+
+ /* reset integrity check result bits */
+ edesc->desc.hdr_lo = 0;
+
+ return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_hwauth_done);
+
+ } else {
+
+ /* Have to check the ICV with software */
- sg = sg_last(req->src, edesc->src_nents ? : 1);
+ edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+
+ /* stash incoming ICV for later cmp with ICV generated by the h/w */
+ if (edesc->dma_len)
+ icvdata = &edesc->link_tbl[edesc->src_nents +
+ edesc->dst_nents + 2];
+ else
+ icvdata = &edesc->link_tbl[0];
- memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
- ctx->authsize);
+ sg = sg_last(req->src, edesc->src_nents ? : 1);
- /* decrypt */
- edesc->desc.hdr = ctx->desc_hdr_template | DESC_HDR_DIR_INBOUND;
+ memcpy(icvdata, (char *)sg_virt(sg) + sg->length - ctx->authsize,
+ ctx->authsize);
- return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_done);
+ return ipsec_esp(edesc, req, NULL, 0, ipsec_esp_decrypt_swauth_done);
+ }
}
static int aead_authenc_givencrypt(
@@ -1391,7 +1431,6 @@ static int talitos_remove(struct of_device *ofdev)
}
tasklet_kill(&priv->done_task);
- tasklet_kill(&priv->error_task);
iounmap(priv->reg);
@@ -1451,10 +1490,9 @@ static int talitos_probe(struct of_device *ofdev,
priv->ofdev = ofdev;
- INIT_LIST_HEAD(&priv->alg_list);
-
tasklet_init(&priv->done_task, talitos_done, (unsigned long)dev);
- tasklet_init(&priv->error_task, talitos_error, (unsigned long)dev);
+
+ INIT_LIST_HEAD(&priv->alg_list);
priv->irq = irq_of_parse_and_map(np, 0);
@@ -1508,6 +1546,9 @@ static int talitos_probe(struct of_device *ofdev,
if (of_device_is_compatible(np, "fsl,sec3.0"))
priv->features |= TALITOS_FTR_SRC_LINK_TBL_LEN_INCLUDES_EXTENT;
+ if (of_device_is_compatible(np, "fsl,sec2.1"))
+ priv->features |= TALITOS_FTR_HW_AUTH_CHECK;
+
priv->head_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
GFP_KERNEL);
priv->tail_lock = kmalloc(sizeof(spinlock_t) * priv->num_channels,
@@ -1551,7 +1592,7 @@ static int talitos_probe(struct of_device *ofdev,
goto err_out;
}
for (i = 0; i < priv->num_channels; i++)
- atomic_set(&priv->submit_count[i], -priv->chfifo_len);
+ atomic_set(&priv->submit_count[i], -(priv->chfifo_len - 1));
priv->head = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
priv->tail = kzalloc(sizeof(int) * priv->num_channels, GFP_KERNEL);
diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h
index c48a405abf70..575981f0cfda 100644
--- a/drivers/crypto/talitos.h
+++ b/drivers/crypto/talitos.h
@@ -37,7 +37,8 @@
#define TALITOS_MCR_LO 0x1038
#define TALITOS_MCR_SWR 0x1 /* s/w reset */
#define TALITOS_IMR 0x1008 /* interrupt mask register */
-#define TALITOS_IMR_INIT 0x10fff /* enable channel IRQs */
+#define TALITOS_IMR_INIT 0x100ff /* enable channel IRQs */
+#define TALITOS_IMR_DONE 0x00055 /* done IRQs */
#define TALITOS_IMR_LO 0x100C
#define TALITOS_IMR_LO_INIT 0x20000 /* allow RNGU error IRQs */
#define TALITOS_ISR 0x1010 /* interrupt status register */
@@ -55,6 +56,7 @@
#define TALITOS_CCCR_CONT 0x2 /* channel continue */
#define TALITOS_CCCR_RESET 0x1 /* channel reset */
#define TALITOS_CCCR_LO(ch) (ch * TALITOS_CH_STRIDE + 0x110c)
+#define TALITOS_CCCR_LO_IWSE 0x80 /* chan. ICCR writeback enab. */
#define TALITOS_CCCR_LO_CDWE 0x10 /* chan. done writeback enab. */
#define TALITOS_CCCR_LO_NT 0x4 /* notification type */
#define TALITOS_CCCR_LO_CDIE 0x2 /* channel done IRQ enable */
@@ -102,6 +104,9 @@
#define TALITOS_AESUISR_LO 0x4034
#define TALITOS_MDEUISR 0x6030 /* message digest unit */
#define TALITOS_MDEUISR_LO 0x6034
+#define TALITOS_MDEUICR 0x6038 /* interrupt control */
+#define TALITOS_MDEUICR_LO 0x603c
+#define TALITOS_MDEUICR_LO_ICE 0x4000 /* integrity check IRQ enable */
#define TALITOS_AFEUISR 0x8030 /* arc4 unit */
#define TALITOS_AFEUISR_LO 0x8034
#define TALITOS_RNGUISR 0xa030 /* random number unit */
@@ -129,31 +134,34 @@
*/
/* written back when done */
-#define DESC_HDR_DONE __constant_cpu_to_be32(0xff000000)
+#define DESC_HDR_DONE cpu_to_be32(0xff000000)
+#define DESC_HDR_LO_ICCR1_MASK cpu_to_be32(0x00180000)
+#define DESC_HDR_LO_ICCR1_PASS cpu_to_be32(0x00080000)
+#define DESC_HDR_LO_ICCR1_FAIL cpu_to_be32(0x00100000)
/* primary execution unit select */
-#define DESC_HDR_SEL0_MASK __constant_cpu_to_be32(0xf0000000)
-#define DESC_HDR_SEL0_AFEU __constant_cpu_to_be32(0x10000000)
-#define DESC_HDR_SEL0_DEU __constant_cpu_to_be32(0x20000000)
-#define DESC_HDR_SEL0_MDEUA __constant_cpu_to_be32(0x30000000)
-#define DESC_HDR_SEL0_MDEUB __constant_cpu_to_be32(0xb0000000)
-#define DESC_HDR_SEL0_RNG __constant_cpu_to_be32(0x40000000)
-#define DESC_HDR_SEL0_PKEU __constant_cpu_to_be32(0x50000000)
-#define DESC_HDR_SEL0_AESU __constant_cpu_to_be32(0x60000000)
-#define DESC_HDR_SEL0_KEU __constant_cpu_to_be32(0x70000000)
-#define DESC_HDR_SEL0_CRCU __constant_cpu_to_be32(0x80000000)
+#define DESC_HDR_SEL0_MASK cpu_to_be32(0xf0000000)
+#define DESC_HDR_SEL0_AFEU cpu_to_be32(0x10000000)
+#define DESC_HDR_SEL0_DEU cpu_to_be32(0x20000000)
+#define DESC_HDR_SEL0_MDEUA cpu_to_be32(0x30000000)
+#define DESC_HDR_SEL0_MDEUB cpu_to_be32(0xb0000000)
+#define DESC_HDR_SEL0_RNG cpu_to_be32(0x40000000)
+#define DESC_HDR_SEL0_PKEU cpu_to_be32(0x50000000)
+#define DESC_HDR_SEL0_AESU cpu_to_be32(0x60000000)
+#define DESC_HDR_SEL0_KEU cpu_to_be32(0x70000000)
+#define DESC_HDR_SEL0_CRCU cpu_to_be32(0x80000000)
/* primary execution unit mode (MODE0) and derivatives */
-#define DESC_HDR_MODE0_ENCRYPT __constant_cpu_to_be32(0x00100000)
-#define DESC_HDR_MODE0_AESU_CBC __constant_cpu_to_be32(0x00200000)
-#define DESC_HDR_MODE0_DEU_CBC __constant_cpu_to_be32(0x00400000)
-#define DESC_HDR_MODE0_DEU_3DES __constant_cpu_to_be32(0x00200000)
-#define DESC_HDR_MODE0_MDEU_INIT __constant_cpu_to_be32(0x01000000)
-#define DESC_HDR_MODE0_MDEU_HMAC __constant_cpu_to_be32(0x00800000)
-#define DESC_HDR_MODE0_MDEU_PAD __constant_cpu_to_be32(0x00400000)
-#define DESC_HDR_MODE0_MDEU_MD5 __constant_cpu_to_be32(0x00200000)
-#define DESC_HDR_MODE0_MDEU_SHA256 __constant_cpu_to_be32(0x00100000)
-#define DESC_HDR_MODE0_MDEU_SHA1 __constant_cpu_to_be32(0x00000000)
+#define DESC_HDR_MODE0_ENCRYPT cpu_to_be32(0x00100000)
+#define DESC_HDR_MODE0_AESU_CBC cpu_to_be32(0x00200000)
+#define DESC_HDR_MODE0_DEU_CBC cpu_to_be32(0x00400000)
+#define DESC_HDR_MODE0_DEU_3DES cpu_to_be32(0x00200000)
+#define DESC_HDR_MODE0_MDEU_INIT cpu_to_be32(0x01000000)
+#define DESC_HDR_MODE0_MDEU_HMAC cpu_to_be32(0x00800000)
+#define DESC_HDR_MODE0_MDEU_PAD cpu_to_be32(0x00400000)
+#define DESC_HDR_MODE0_MDEU_MD5 cpu_to_be32(0x00200000)
+#define DESC_HDR_MODE0_MDEU_SHA256 cpu_to_be32(0x00100000)
+#define DESC_HDR_MODE0_MDEU_SHA1 cpu_to_be32(0x00000000)
#define DESC_HDR_MODE0_MDEU_MD5_HMAC (DESC_HDR_MODE0_MDEU_MD5 | \
DESC_HDR_MODE0_MDEU_HMAC)
#define DESC_HDR_MODE0_MDEU_SHA256_HMAC (DESC_HDR_MODE0_MDEU_SHA256 | \
@@ -162,18 +170,19 @@
DESC_HDR_MODE0_MDEU_HMAC)
/* secondary execution unit select (SEL1) */
-#define DESC_HDR_SEL1_MASK __constant_cpu_to_be32(0x000f0000)
-#define DESC_HDR_SEL1_MDEUA __constant_cpu_to_be32(0x00030000)
-#define DESC_HDR_SEL1_MDEUB __constant_cpu_to_be32(0x000b0000)
-#define DESC_HDR_SEL1_CRCU __constant_cpu_to_be32(0x00080000)
+#define DESC_HDR_SEL1_MASK cpu_to_be32(0x000f0000)
+#define DESC_HDR_SEL1_MDEUA cpu_to_be32(0x00030000)
+#define DESC_HDR_SEL1_MDEUB cpu_to_be32(0x000b0000)
+#define DESC_HDR_SEL1_CRCU cpu_to_be32(0x00080000)
/* secondary execution unit mode (MODE1) and derivatives */
-#define DESC_HDR_MODE1_MDEU_INIT __constant_cpu_to_be32(0x00001000)
-#define DESC_HDR_MODE1_MDEU_HMAC __constant_cpu_to_be32(0x00000800)
-#define DESC_HDR_MODE1_MDEU_PAD __constant_cpu_to_be32(0x00000400)
-#define DESC_HDR_MODE1_MDEU_MD5 __constant_cpu_to_be32(0x00000200)
-#define DESC_HDR_MODE1_MDEU_SHA256 __constant_cpu_to_be32(0x00000100)
-#define DESC_HDR_MODE1_MDEU_SHA1 __constant_cpu_to_be32(0x00000000)
+#define DESC_HDR_MODE1_MDEU_CICV cpu_to_be32(0x00004000)
+#define DESC_HDR_MODE1_MDEU_INIT cpu_to_be32(0x00001000)
+#define DESC_HDR_MODE1_MDEU_HMAC cpu_to_be32(0x00000800)
+#define DESC_HDR_MODE1_MDEU_PAD cpu_to_be32(0x00000400)
+#define DESC_HDR_MODE1_MDEU_MD5 cpu_to_be32(0x00000200)
+#define DESC_HDR_MODE1_MDEU_SHA256 cpu_to_be32(0x00000100)
+#define DESC_HDR_MODE1_MDEU_SHA1 cpu_to_be32(0x00000000)
#define DESC_HDR_MODE1_MDEU_MD5_HMAC (DESC_HDR_MODE1_MDEU_MD5 | \
DESC_HDR_MODE1_MDEU_HMAC)
#define DESC_HDR_MODE1_MDEU_SHA256_HMAC (DESC_HDR_MODE1_MDEU_SHA256 | \
@@ -182,16 +191,16 @@
DESC_HDR_MODE1_MDEU_HMAC)
/* direction of overall data flow (DIR) */
-#define DESC_HDR_DIR_INBOUND __constant_cpu_to_be32(0x00000002)
+#define DESC_HDR_DIR_INBOUND cpu_to_be32(0x00000002)
/* request done notification (DN) */
-#define DESC_HDR_DONE_NOTIFY __constant_cpu_to_be32(0x00000001)
+#define DESC_HDR_DONE_NOTIFY cpu_to_be32(0x00000001)
/* descriptor types */
-#define DESC_HDR_TYPE_AESU_CTR_NONSNOOP __constant_cpu_to_be32(0 << 3)
-#define DESC_HDR_TYPE_IPSEC_ESP __constant_cpu_to_be32(1 << 3)
-#define DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU __constant_cpu_to_be32(2 << 3)
-#define DESC_HDR_TYPE_HMAC_SNOOP_NO_AFEU __constant_cpu_to_be32(4 << 3)
+#define DESC_HDR_TYPE_AESU_CTR_NONSNOOP cpu_to_be32(0 << 3)
+#define DESC_HDR_TYPE_IPSEC_ESP cpu_to_be32(1 << 3)
+#define DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU cpu_to_be32(2 << 3)
+#define DESC_HDR_TYPE_HMAC_SNOOP_NO_AFEU cpu_to_be32(4 << 3)
/* link table extent field bits */
#define DESC_PTR_LNKTBL_JUMP 0x80
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 5317e08221ec..657996517374 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -388,7 +388,10 @@ int dma_async_device_register(struct dma_device *device)
init_completion(&device->done);
kref_init(&device->refcount);
+
+ mutex_lock(&dma_list_mutex);
device->dev_id = id++;
+ mutex_unlock(&dma_list_mutex);
/* represent channels in sysfs. Probably want devs too */
list_for_each_entry(chan, &device->channels, device_node) {
diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c
index ecd743f7cc61..6607fdd00b1c 100644
--- a/drivers/dma/ioat_dma.c
+++ b/drivers/dma/ioat_dma.c
@@ -1341,10 +1341,12 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan)
*/
#define IOAT_TEST_SIZE 2000
+DECLARE_COMPLETION(test_completion);
static void ioat_dma_test_callback(void *dma_async_param)
{
printk(KERN_ERR "ioatdma: ioat_dma_test_callback(%p)\n",
dma_async_param);
+ complete(&test_completion);
}
/**
@@ -1410,7 +1412,8 @@ static int ioat_dma_self_test(struct ioatdma_device *device)
goto free_resources;
}
device->common.device_issue_pending(dma_chan);
- msleep(1);
+
+ wait_for_completion_timeout(&test_completion, msecs_to_jiffies(3000));
if (device->common.device_is_tx_complete(dma_chan, cookie, NULL, NULL)
!= DMA_SUCCESS) {
diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c
index c7a9306d951d..6be317262200 100644
--- a/drivers/dma/iop-adma.c
+++ b/drivers/dma/iop-adma.c
@@ -85,18 +85,28 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc,
enum dma_ctrl_flags flags = desc->async_tx.flags;
u32 src_cnt;
dma_addr_t addr;
+ dma_addr_t dest;
+ src_cnt = unmap->unmap_src_cnt;
+ dest = iop_desc_get_dest_addr(unmap, iop_chan);
if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
- addr = iop_desc_get_dest_addr(unmap, iop_chan);
- dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE);
+ enum dma_data_direction dir;
+
+ if (src_cnt > 1) /* is xor? */
+ dir = DMA_BIDIRECTIONAL;
+ else
+ dir = DMA_FROM_DEVICE;
+
+ dma_unmap_page(dev, dest, len, dir);
}
if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
- src_cnt = unmap->unmap_src_cnt;
while (src_cnt--) {
addr = iop_desc_get_src_addr(unmap,
iop_chan,
src_cnt);
+ if (addr == dest)
+ continue;
dma_unmap_page(dev, addr, len,
DMA_TO_DEVICE);
}
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 0328da020a10..bcda17426411 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -311,17 +311,26 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc,
enum dma_ctrl_flags flags = desc->async_tx.flags;
u32 src_cnt;
dma_addr_t addr;
+ dma_addr_t dest;
+ src_cnt = unmap->unmap_src_cnt;
+ dest = mv_desc_get_dest_addr(unmap);
if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
- addr = mv_desc_get_dest_addr(unmap);
- dma_unmap_page(dev, addr, len, DMA_FROM_DEVICE);
+ enum dma_data_direction dir;
+
+ if (src_cnt > 1) /* is xor ? */
+ dir = DMA_BIDIRECTIONAL;
+ else
+ dir = DMA_FROM_DEVICE;
+ dma_unmap_page(dev, dest, len, dir);
}
if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
- src_cnt = unmap->unmap_src_cnt;
while (src_cnt--) {
addr = mv_desc_get_src_addr(unmap,
src_cnt);
+ if (addr == dest)
+ continue;
dma_unmap_page(dev, addr, len,
DMA_TO_DEVICE);
}
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index e0dbd388757f..e2667a8c2997 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -161,7 +161,7 @@ config EDAC_PASEMI
config EDAC_CELL
tristate "Cell Broadband Engine memory controller"
- depends on EDAC_MM_EDAC && PPC_CELL_NATIVE
+ depends on EDAC_MM_EDAC && PPC_CELL_COMMON
help
Support for error detection and correction on the
Cell Broadband Engine internal memory controller
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index 5fcd3d89c75d..4041e9143283 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -394,6 +394,12 @@ static void edac_device_workq_function(struct work_struct *work_req)
mutex_lock(&device_ctls_mutex);
+ /* If we are being removed, bail out immediately */
+ if (edac_dev->op_state == OP_OFFLINE) {
+ mutex_unlock(&device_ctls_mutex);
+ return;
+ }
+
/* Only poll controllers that are running polled and have a check */
if ((edac_dev->op_state == OP_RUNNING_POLL) &&
(edac_dev->edac_check != NULL)) {
@@ -585,14 +591,14 @@ struct edac_device_ctl_info *edac_device_del_device(struct device *dev)
/* mark this instance as OFFLINE */
edac_dev->op_state = OP_OFFLINE;
- /* clear workq processing on this instance */
- edac_device_workq_teardown(edac_dev);
-
/* deregister from global list */
del_edac_device_from_global_list(edac_dev);
mutex_unlock(&device_ctls_mutex);
+ /* clear workq processing on this instance */
+ edac_device_workq_teardown(edac_dev);
+
/* Tear down the sysfs entries for this instance */
edac_device_remove_sysfs(edac_dev);
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 8daf4793ac32..78b989d202a3 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -467,6 +467,17 @@ const char *dmi_get_system_info(int field)
}
EXPORT_SYMBOL(dmi_get_system_info);
+/**
+ * dmi_name_in_serial - Check if string is in the DMI product serial
+ * information.
+ */
+int dmi_name_in_serial(const char *str)
+{
+ int f = DMI_PRODUCT_SERIAL;
+ if (dmi_ident[f] && strstr(dmi_ident[f], str))
+ return 1;
+ return 0;
+}
/**
* dmi_name_in_vendors - Check if string is anywhere in the DMI vendor information.
@@ -571,3 +582,19 @@ int dmi_walk(void (*decode)(const struct dmi_header *))
return 0;
}
EXPORT_SYMBOL_GPL(dmi_walk);
+
+/**
+ * dmi_match - compare a string to the dmi field (if exists)
+ *
+ * Returns true if the requested field equals to the str (including NULL).
+ */
+bool dmi_match(enum dmi_field f, const char *str)
+{
+ const char *info = dmi_get_system_info(f);
+
+ if (info == NULL || str == NULL)
+ return info == str;
+
+ return !strcmp(info, str);
+}
+EXPORT_SYMBOL_GPL(dmi_match);
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 4353414a0b77..3ab3e4a41d67 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -284,15 +284,12 @@ static ssize_t sprintf_ipaddr(char *buf, u8 *ip)
/*
* IPV4
*/
- str += sprintf(buf, NIPQUAD_FMT, ip[12],
- ip[13], ip[14], ip[15]);
+ str += sprintf(buf, "%pI4", ip + 12);
} else {
/*
* IPv6
*/
- str += sprintf(str, NIP6_FMT, ntohs(ip[0]), ntohs(ip[1]),
- ntohs(ip[2]), ntohs(ip[3]), ntohs(ip[4]),
- ntohs(ip[5]), ntohs(ip[6]), ntohs(ip[7]));
+ str += sprintf(str, "%pI6", ip);
}
str += sprintf(str, "\n");
return str - buf;
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index a8b33c2ec8d2..5130b72d593c 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -7,6 +7,8 @@
menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU
+ select I2C
+ select I2C_ALGOBIT
help
Kernel-level support for the Direct Rendering Infrastructure (DRI)
introduced in XFree86 4.0. If you say Y here, you need to select
@@ -65,6 +67,10 @@ config DRM_I830
will load the correct one.
config DRM_I915
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ depends on FB
tristate "i915 driver"
help
Choose this option if you have a system that has Intel 830M, 845G,
@@ -76,6 +82,17 @@ config DRM_I915
endchoice
+config DRM_I915_KMS
+ bool "Enable modesetting on intel by default"
+ depends on DRM_I915
+ help
+ Choose this option if you want kernel modesetting enabled by default,
+ and you have a new enough userspace to support this. Running old
+ userspaces with this enabled will cause pain. Note that this causes
+ the driver to bind to PCI devices, which precludes loading things
+ like intelfb.
+
+
config DRM_MGA
tristate "Matrox g200/g400"
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 74da99495e21..30022c4a5c12 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -9,7 +9,8 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
- drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o
+ drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o \
+ drm_crtc.o drm_crtc_helper.o drm_modes.o drm_edid.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index a73462723d2d..ca7a9ef5007b 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -45,14 +45,15 @@
* the one with matching magic number, while holding the drm_device::struct_mutex
* lock.
*/
-static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic)
+static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic)
{
struct drm_file *retval = NULL;
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
mutex_lock(&dev->struct_mutex);
- if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
retval = pt->priv;
}
@@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic
* associated the magic number hash key in drm_device::magiclist, while holding
* the drm_device::struct_mutex lock.
*/
-static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
+static int drm_add_magic(struct drm_master *master, struct drm_file *priv,
drm_magic_t magic)
{
struct drm_magic_entry *entry;
-
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
@@ -83,11 +84,10 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
return -ENOMEM;
memset(entry, 0, sizeof(*entry));
entry->priv = priv;
-
entry->hash_item.key = (unsigned long)magic;
mutex_lock(&dev->struct_mutex);
- drm_ht_insert_item(&dev->magiclist, &entry->hash_item);
- list_add_tail(&entry->head, &dev->magicfree);
+ drm_ht_insert_item(&master->magiclist, &entry->hash_item);
+ list_add_tail(&entry->head, &master->magicfree);
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -102,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
* Searches and unlinks the entry in drm_device::magiclist with the magic
* number hash key, while holding the drm_device::struct_mutex lock.
*/
-static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic)
+static int drm_remove_magic(struct drm_master *master, drm_magic_t magic)
{
struct drm_magic_entry *pt;
struct drm_hash_item *hash;
+ struct drm_device *dev = master->minor->dev;
DRM_DEBUG("%d\n", magic);
mutex_lock(&dev->struct_mutex);
- if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
- drm_ht_remove_item(&dev->magiclist, hash);
+ drm_ht_remove_item(&master->magiclist, hash);
list_del(&pt->head);
mutex_unlock(&dev->struct_mutex);
@@ -153,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
++sequence; /* reserve 0 */
auth->magic = sequence++;
spin_unlock(&lock);
- } while (drm_find_file(dev, auth->magic));
+ } while (drm_find_file(file_priv->master, auth->magic));
file_priv->magic = auth->magic;
- drm_add_magic(dev, file_priv, auth->magic);
+ drm_add_magic(file_priv->master, file_priv, auth->magic);
}
DRM_DEBUG("%u\n", auth->magic);
@@ -181,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data,
struct drm_file *file;
DRM_DEBUG("%u\n", auth->magic);
- if ((file = drm_find_file(dev, auth->magic))) {
+ if ((file = drm_find_file(file_priv->master, auth->magic))) {
file->authenticated = 1;
- drm_remove_magic(dev, auth->magic);
+ drm_remove_magic(file_priv->master, auth->magic);
return 0;
}
return -EINVAL;
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index bde64b84166e..72c667f9bee1 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -54,9 +54,9 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
{
struct drm_map_list *entry;
list_for_each_entry(entry, &dev->maplist, head) {
- if (entry->map && map->type == entry->map->type &&
+ if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) &&
((entry->map->offset == map->offset) ||
- (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) {
+ ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
return entry;
}
}
@@ -210,12 +210,12 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
map->offset = (unsigned long)map->handle;
if (map->flags & _DRM_CONTAINS_LOCK) {
/* Prevent a 2nd X Server from creating a 2nd lock */
- if (dev->lock.hw_lock != NULL) {
+ if (dev->primary->master->lock.hw_lock != NULL) {
vfree(map->handle);
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return -EBUSY;
}
- dev->sigdata.lock = dev->lock.hw_lock = map->handle; /* Pointer to lock */
+ dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle; /* Pointer to lock */
}
break;
case _DRM_AGP: {
@@ -262,6 +262,9 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
case _DRM_SCATTER_GATHER:
if (!dev->sg) {
@@ -319,6 +322,7 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
list->user_token = list->hash.key << PAGE_SHIFT;
mutex_unlock(&dev->struct_mutex);
+ list->master = dev->primary->master;
*maplist = list;
return 0;
}
@@ -345,7 +349,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
struct drm_map_list *maplist;
int err;
- if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP))
+ if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
return -EPERM;
err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -380,10 +384,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
struct drm_map_list *r_list = NULL, *list_t;
drm_dma_handle_t dmah;
int found = 0;
+ struct drm_master *master;
/* Find the list entry for the map and remove it */
list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
if (r_list->map == map) {
+ master = r_list->master;
list_del(&r_list->head);
drm_ht_remove_key(&dev->map_hash,
r_list->user_token >> PAGE_SHIFT);
@@ -409,6 +415,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
break;
case _DRM_SHM:
vfree(map->handle);
+ if (master) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL; /* SHM removed */
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
break;
case _DRM_AGP:
case _DRM_SCATTER_GATHER:
@@ -419,11 +432,15 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
dmah.size = map->size;
__drm_pci_free(dev, &dmah);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
return 0;
}
+EXPORT_SYMBOL(drm_rmmap_locked);
int drm_rmmap(struct drm_device *dev, drm_local_map_t *map)
{
diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c
index d505f695421f..809ec0f03452 100644
--- a/drivers/gpu/drm/drm_context.c
+++ b/drivers/gpu/drm/drm_context.c
@@ -256,12 +256,13 @@ static int drm_context_switch(struct drm_device * dev, int old, int new)
* hardware lock is held, clears the drm_device::context_flag and wakes up
* drm_device::context_wait.
*/
-static int drm_context_switch_complete(struct drm_device * dev, int new)
+static int drm_context_switch_complete(struct drm_device *dev,
+ struct drm_file *file_priv, int new)
{
dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
dev->last_switch = jiffies;
- if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+ if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {
DRM_ERROR("Lock isn't held after context switch\n");
}
@@ -420,7 +421,7 @@ int drm_newctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- drm_context_switch_complete(dev, ctx->handle);
+ drm_context_switch_complete(dev, file_priv, ctx->handle);
return 0;
}
@@ -442,9 +443,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
struct drm_ctx *ctx = data;
DRM_DEBUG("%d\n", ctx->handle);
- if (ctx->handle == DRM_KERNEL_CONTEXT + 1) {
- file_priv->remove_auth_on_close = 1;
- }
if (ctx->handle != DRM_KERNEL_CONTEXT) {
if (dev->driver->context_dtor)
dev->driver->context_dtor(dev, ctx->handle);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
new file mode 100644
index 000000000000..53c87254be4c
--- /dev/null
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -0,0 +1,2446 @@
+/*
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2008 Red Hat Inc.
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+#include <linux/list.h>
+#include "drm.h"
+#include "drmP.h"
+#include "drm_crtc.h"
+
+struct drm_prop_enum_list {
+ int type;
+ char *name;
+};
+
+/* Avoid boilerplate. I'm tired of typing. */
+#define DRM_ENUM_NAME_FN(fnname, list) \
+ char *fnname(int val) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(list); i++) { \
+ if (list[i].type == val) \
+ return list[i].name; \
+ } \
+ return "(unknown)"; \
+ }
+
+/*
+ * Global properties
+ */
+static struct drm_prop_enum_list drm_dpms_enum_list[] =
+{ { DRM_MODE_DPMS_ON, "On" },
+ { DRM_MODE_DPMS_STANDBY, "Standby" },
+ { DRM_MODE_DPMS_SUSPEND, "Suspend" },
+ { DRM_MODE_DPMS_OFF, "Off" }
+};
+
+DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
+
+/*
+ * Optional properties
+ */
+static struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
+{
+ { DRM_MODE_SCALE_NON_GPU, "Non-GPU" },
+ { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" },
+ { DRM_MODE_SCALE_NO_SCALE, "No scale" },
+ { DRM_MODE_SCALE_ASPECT, "Aspect" },
+};
+
+static struct drm_prop_enum_list drm_dithering_mode_enum_list[] =
+{
+ { DRM_MODE_DITHERING_OFF, "Off" },
+ { DRM_MODE_DITHERING_ON, "On" },
+};
+
+/*
+ * Non-global properties, but "required" for certain connectors.
+ */
+static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
+ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
+};
+
+DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
+
+static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */
+ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */
+};
+
+DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
+ drm_dvi_i_subconnector_enum_list)
+
+static struct drm_prop_enum_list drm_tv_select_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
+
+static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
+{
+ { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */
+ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
+ drm_tv_subconnector_enum_list)
+
+struct drm_conn_prop_enum_list {
+ int type;
+ char *name;
+ int count;
+};
+
+/*
+ * Connector and encoder types.
+ */
+static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
+{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 },
+ { DRM_MODE_CONNECTOR_VGA, "VGA", 0 },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 },
+ { DRM_MODE_CONNECTOR_Composite, "Composite", 0 },
+ { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 },
+ { DRM_MODE_CONNECTOR_Component, "Component", 0 },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 },
+};
+
+static struct drm_prop_enum_list drm_encoder_enum_list[] =
+{ { DRM_MODE_ENCODER_NONE, "None" },
+ { DRM_MODE_ENCODER_DAC, "DAC" },
+ { DRM_MODE_ENCODER_TMDS, "TMDS" },
+ { DRM_MODE_ENCODER_LVDS, "LVDS" },
+ { DRM_MODE_ENCODER_TVDAC, "TV" },
+};
+
+char *drm_get_encoder_name(struct drm_encoder *encoder)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d",
+ drm_encoder_enum_list[encoder->encoder_type].name,
+ encoder->base.id);
+ return buf;
+}
+
+char *drm_get_connector_name(struct drm_connector *connector)
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%s-%d",
+ drm_connector_enum_list[connector->connector_type].name,
+ connector->connector_type_id);
+ return buf;
+}
+EXPORT_SYMBOL(drm_get_connector_name);
+
+char *drm_get_connector_status_name(enum drm_connector_status status)
+{
+ if (status == connector_status_connected)
+ return "connected";
+ else if (status == connector_status_disconnected)
+ return "disconnected";
+ else
+ return "unknown";
+}
+
+/**
+ * drm_mode_object_get - allocate a new identifier
+ * @dev: DRM device
+ * @ptr: object pointer, used to generate unique ID
+ * @type: object type
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Create a unique identifier based on @ptr in @dev's identifier space. Used
+ * for tracking modes, CRTCs and connectors.
+ *
+ * RETURNS:
+ * New unique (relative to other objects in @dev) integer identifier for the
+ * object.
+ */
+static int drm_mode_object_get(struct drm_device *dev,
+ struct drm_mode_object *obj, uint32_t obj_type)
+{
+ int new_id = 0;
+ int ret;
+
+ WARN(!mutex_is_locked(&dev->mode_config.mutex),
+ "%s called w/o mode_config lock\n", __FUNCTION__);
+again:
+ if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) {
+ DRM_ERROR("Ran out memory getting a mode number\n");
+ return -EINVAL;
+ }
+
+ ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
+ if (ret == -EAGAIN)
+ goto again;
+
+ obj->id = new_id;
+ obj->type = obj_type;
+ return 0;
+}
+
+/**
+ * drm_mode_object_put - free an identifer
+ * @dev: DRM device
+ * @id: ID to free
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Free @id from @dev's unique identifier pool.
+ */
+static void drm_mode_object_put(struct drm_device *dev,
+ struct drm_mode_object *object)
+{
+ idr_remove(&dev->mode_config.crtc_idr, object->id);
+}
+
+void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
+{
+ struct drm_mode_object *obj;
+
+ obj = idr_find(&dev->mode_config.crtc_idr, id);
+ if (!obj || (obj->type != type) || (obj->id != id))
+ return NULL;
+
+ return obj;
+}
+EXPORT_SYMBOL(drm_mode_object_find);
+
+/**
+ * drm_crtc_from_fb - find the CRTC structure associated with an fb
+ * @dev: DRM device
+ * @fb: framebuffer in question
+ *
+ * LOCKING:
+ * Caller must hold mode_config lock.
+ *
+ * Find CRTC in the mode_config structure that matches @fb.
+ *
+ * RETURNS:
+ * Pointer to the CRTC or NULL if it wasn't found.
+ */
+struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev,
+ struct drm_framebuffer *fb)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ return crtc;
+ }
+ return NULL;
+}
+
+/**
+ * drm_framebuffer_init - initialize a framebuffer
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Allocates an ID for the framebuffer's parent mode object, sets its mode
+ * functions & device file and adds it to the master fd list.
+ *
+ * RETURNS:
+ * Zero on success, error code on falure.
+ */
+int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs)
+{
+ int ret;
+
+ ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
+ if (ret) {
+ return ret;
+ }
+
+ fb->dev = dev;
+ fb->funcs = funcs;
+ dev->mode_config.num_fb++;
+ list_add(&fb->head, &dev->mode_config.fb_list);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_framebuffer_init);
+
+/**
+ * drm_framebuffer_cleanup - remove a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes
+ * it, setting it to NULL.
+ */
+void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
+{
+ struct drm_device *dev = fb->dev;
+ struct drm_crtc *crtc;
+
+ /* remove from any CRTC */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc->fb == fb)
+ crtc->fb = NULL;
+ }
+
+ drm_mode_object_put(dev, &fb->base);
+ list_del(&fb->head);
+ dev->mode_config.num_fb--;
+}
+EXPORT_SYMBOL(drm_framebuffer_cleanup);
+
+/**
+ * drm_crtc_init - Initialise a new CRTC object
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @funcs: callbacks for the new CRTC
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Inits a new object created as base part of an driver crtc object.
+ */
+void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs)
+{
+ crtc->dev = dev;
+ crtc->funcs = funcs;
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
+ dev->mode_config.num_crtc++;
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_crtc_init);
+
+/**
+ * drm_crtc_cleanup - Cleans up the core crtc usage.
+ * @crtc: CRTC to cleanup
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Cleanup @crtc. Removes from drm modesetting space
+ * does NOT free object, caller does that.
+ */
+void drm_crtc_cleanup(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+
+ if (crtc->gamma_store) {
+ kfree(crtc->gamma_store);
+ crtc->gamma_store = NULL;
+ }
+
+ drm_mode_object_put(dev, &crtc->base);
+ list_del(&crtc->head);
+ dev->mode_config.num_crtc--;
+}
+EXPORT_SYMBOL(drm_crtc_cleanup);
+
+/**
+ * drm_mode_probed_add - add a mode to a connector's probed mode list
+ * @connector: connector the new mode
+ * @mode: mode data
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Add @mode to @connector's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ list_add(&mode->head, &connector->probed_modes);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @connector: connector list to modify
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Remove @mode from @connector's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ list_del(&mode->head);
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @name: user visible name of the connector
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ */
+void drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
+{
+ mutex_lock(&dev->mode_config.mutex);
+
+ connector->dev = dev;
+ connector->funcs = funcs;
+ drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ connector->connector_type = connector_type;
+ connector->connector_type_id =
+ ++drm_connector_enum_list[connector_type].count; /* TODO */
+ INIT_LIST_HEAD(&connector->user_modes);
+ INIT_LIST_HEAD(&connector->probed_modes);
+ INIT_LIST_HEAD(&connector->modes);
+ connector->edid_blob_ptr = NULL;
+
+ list_add_tail(&connector->head, &dev->mode_config.connector_list);
+ dev->mode_config.num_connector++;
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.edid_property, 0);
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.dpms_property, 0);
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_init);
+
+/**
+ * drm_connector_cleanup - cleans up an initialised connector
+ * @connector: connector to cleanup
+ *
+ * LOCKING:
+ * Caller must hold @dev's mode_config lock.
+ *
+ * Cleans up the connector but doesn't free the object.
+ */
+void drm_connector_cleanup(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ drm_mode_remove(connector, mode);
+
+ list_for_each_entry_safe(mode, t, &connector->user_modes, head)
+ drm_mode_remove(connector, mode);
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &connector->base);
+ list_del(&connector->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_connector_cleanup);
+
+void drm_encoder_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ const struct drm_encoder_funcs *funcs,
+ int encoder_type)
+{
+ mutex_lock(&dev->mode_config.mutex);
+
+ encoder->dev = dev;
+
+ drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->encoder_type = encoder_type;
+ encoder->funcs = funcs;
+
+ list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
+ dev->mode_config.num_encoder++;
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_init);
+
+void drm_encoder_cleanup(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &encoder->base);
+ list_del(&encoder->head);
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_encoder_cleanup);
+
+/**
+ * drm_mode_create - create a new display mode
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold DRM mode_config lock.
+ *
+ * Create a new drm_display_mode, give it an ID, and return it.
+ *
+ * RETURNS:
+ * Pointer to new mode on success, NULL on error.
+ */
+struct drm_display_mode *drm_mode_create(struct drm_device *dev)
+{
+ struct drm_display_mode *nmode;
+
+ nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL);
+ if (!nmode)
+ return NULL;
+
+ drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_create);
+
+/**
+ * drm_mode_destroy - remove a mode
+ * @dev: DRM device
+ * @mode: mode to remove
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free @mode's unique identifier, then free it.
+ */
+void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ drm_mode_object_put(dev, &mode->base);
+
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_destroy);
+
+static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
+{
+ struct drm_property *edid;
+ struct drm_property *dpms;
+ int i;
+
+ /*
+ * Standard properties (apply to all connectors)
+ */
+ edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+ DRM_MODE_PROP_IMMUTABLE,
+ "EDID", 0);
+ dev->mode_config.edid_property = edid;
+
+ dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
+ drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type,
+ drm_dpms_enum_list[i].name);
+ dev->mode_config.dpms_property = dpms;
+
+ return 0;
+}
+
+/**
+ * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
+ * @dev: DRM device
+ *
+ * Called by a driver the first time a DVI-I connector is made.
+ */
+int drm_mode_create_dvi_i_properties(struct drm_device *dev)
+{
+ struct drm_property *dvi_i_selector;
+ struct drm_property *dvi_i_subconnector;
+ int i;
+
+ if (dev->mode_config.dvi_i_select_subconnector_property)
+ return 0;
+
+ dvi_i_selector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector",
+ ARRAY_SIZE(drm_dvi_i_select_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++)
+ drm_property_add_enum(dvi_i_selector, i,
+ drm_dvi_i_select_enum_list[i].type,
+ drm_dvi_i_select_enum_list[i].name);
+ dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
+
+ dvi_i_subconnector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM |
+ DRM_MODE_PROP_IMMUTABLE,
+ "subconnector",
+ ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++)
+ drm_property_add_enum(dvi_i_subconnector, i,
+ drm_dvi_i_subconnector_enum_list[i].type,
+ drm_dvi_i_subconnector_enum_list[i].name);
+ dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
+
+/**
+ * drm_create_tv_properties - create TV specific connector properties
+ * @dev: DRM device
+ * @num_modes: number of different TV formats (modes) supported
+ * @modes: array of pointers to strings containing name of each format
+ *
+ * Called by a driver's TV initialization routine, this function creates
+ * the TV specific connector properties for a given device. Caller is
+ * responsible for allocating a list of format names and passing them to
+ * this routine.
+ */
+int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
+ char *modes[])
+{
+ struct drm_property *tv_selector;
+ struct drm_property *tv_subconnector;
+ int i;
+
+ if (dev->mode_config.tv_select_subconnector_property)
+ return 0;
+
+ /*
+ * Basic connector properties
+ */
+ tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector",
+ ARRAY_SIZE(drm_tv_select_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++)
+ drm_property_add_enum(tv_selector, i,
+ drm_tv_select_enum_list[i].type,
+ drm_tv_select_enum_list[i].name);
+ dev->mode_config.tv_select_subconnector_property = tv_selector;
+
+ tv_subconnector =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM |
+ DRM_MODE_PROP_IMMUTABLE, "subconnector",
+ ARRAY_SIZE(drm_tv_subconnector_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++)
+ drm_property_add_enum(tv_subconnector, i,
+ drm_tv_subconnector_enum_list[i].type,
+ drm_tv_subconnector_enum_list[i].name);
+ dev->mode_config.tv_subconnector_property = tv_subconnector;
+
+ /*
+ * Other, TV specific properties: margins & TV modes.
+ */
+ dev->mode_config.tv_left_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "left margin", 2);
+ dev->mode_config.tv_left_margin_property->values[0] = 0;
+ dev->mode_config.tv_left_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_right_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "right margin", 2);
+ dev->mode_config.tv_right_margin_property->values[0] = 0;
+ dev->mode_config.tv_right_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_top_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "top margin", 2);
+ dev->mode_config.tv_top_margin_property->values[0] = 0;
+ dev->mode_config.tv_top_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_bottom_margin_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "bottom margin", 2);
+ dev->mode_config.tv_bottom_margin_property->values[0] = 0;
+ dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+
+ dev->mode_config.tv_mode_property =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "mode", num_modes);
+ for (i = 0; i < num_modes; i++)
+ drm_property_add_enum(dev->mode_config.tv_mode_property, i,
+ i, modes[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
+
+/**
+ * drm_mode_create_scaling_mode_property - create scaling mode property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_scaling_mode_property(struct drm_device *dev)
+{
+ struct drm_property *scaling_mode;
+ int i;
+
+ if (dev->mode_config.scaling_mode_property)
+ return 0;
+
+ scaling_mode =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+ ARRAY_SIZE(drm_scaling_mode_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++)
+ drm_property_add_enum(scaling_mode, i,
+ drm_scaling_mode_enum_list[i].type,
+ drm_scaling_mode_enum_list[i].name);
+
+ dev->mode_config.scaling_mode_property = scaling_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
+
+/**
+ * drm_mode_create_dithering_property - create dithering property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_dithering_property(struct drm_device *dev)
+{
+ struct drm_property *dithering_mode;
+ int i;
+
+ if (dev->mode_config.dithering_mode_property)
+ return 0;
+
+ dithering_mode =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering",
+ ARRAY_SIZE(drm_dithering_mode_enum_list));
+ for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++)
+ drm_property_add_enum(dithering_mode, i,
+ drm_dithering_mode_enum_list[i].type,
+ drm_dithering_mode_enum_list[i].name);
+ dev->mode_config.dithering_mode_property = dithering_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dithering_property);
+
+/**
+ * drm_mode_config_init - initialize DRM mode_configuration structure
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * None, should happen single threaded at init time.
+ *
+ * Initialize @dev's mode_config structure, used for tracking the graphics
+ * configuration of @dev.
+ */
+void drm_mode_config_init(struct drm_device *dev)
+{
+ mutex_init(&dev->mode_config.mutex);
+ INIT_LIST_HEAD(&dev->mode_config.fb_list);
+ INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);
+ INIT_LIST_HEAD(&dev->mode_config.crtc_list);
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ INIT_LIST_HEAD(&dev->mode_config.encoder_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_list);
+ INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
+ idr_init(&dev->mode_config.crtc_idr);
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_create_standard_connector_properties(dev);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ /* Just to be sure */
+ dev->mode_config.num_fb = 0;
+ dev->mode_config.num_connector = 0;
+ dev->mode_config.num_crtc = 0;
+ dev->mode_config.num_encoder = 0;
+}
+EXPORT_SYMBOL(drm_mode_config_init);
+
+int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
+{
+ uint32_t total_objects = 0;
+
+ total_objects += dev->mode_config.num_crtc;
+ total_objects += dev->mode_config.num_connector;
+ total_objects += dev->mode_config.num_encoder;
+
+ if (total_objects == 0)
+ return -EINVAL;
+
+ group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
+ if (!group->id_list)
+ return -ENOMEM;
+
+ group->num_crtcs = 0;
+ group->num_connectors = 0;
+ group->num_encoders = 0;
+ return 0;
+}
+
+int drm_mode_group_init_legacy_group(struct drm_device *dev,
+ struct drm_mode_group *group)
+{
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
+
+ if ((ret = drm_mode_group_init(dev, group)))
+ return ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ group->id_list[group->num_crtcs++] = crtc->base.id;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders++] =
+ encoder->base.id;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders +
+ group->num_connectors++] = connector->base.id;
+
+ return 0;
+}
+
+/**
+ * drm_mode_config_cleanup - free up DRM mode_config info
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Free up all the connectors and CRTCs associated with this DRM device, then
+ * free up the framebuffers and associated buffer objects.
+ *
+ * FIXME: cleanup any dangling user buffer objects too
+ */
+void drm_mode_config_cleanup(struct drm_device *dev)
+{
+ struct drm_connector *connector, *ot;
+ struct drm_crtc *crtc, *ct;
+ struct drm_encoder *encoder, *enct;
+ struct drm_framebuffer *fb, *fbt;
+ struct drm_property *property, *pt;
+
+ list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
+ head) {
+ encoder->funcs->destroy(encoder);
+ }
+
+ list_for_each_entry_safe(connector, ot,
+ &dev->mode_config.connector_list, head) {
+ connector->funcs->destroy(connector);
+ }
+
+ list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
+ head) {
+ drm_property_destroy(dev, property);
+ }
+
+ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
+ fb->funcs->destroy(fb);
+ }
+
+ list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
+ crtc->funcs->destroy(crtc);
+ }
+
+}
+EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+/**
+ * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
+ * @out: drm_mode_modeinfo struct to return to the user
+ * @in: drm_display_mode to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
+ * the user.
+ */
+void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+ struct drm_display_mode *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode
+ * @out: drm_display_mode to return to the user
+ * @in: drm_mode_modeinfo to use
+ *
+ * LOCKING:
+ * None.
+ *
+ * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
+ * the caller.
+ */
+void drm_crtc_convert_umode(struct drm_display_mode *out,
+ struct drm_mode_modeinfo *in)
+{
+ out->clock = in->clock;
+ out->hdisplay = in->hdisplay;
+ out->hsync_start = in->hsync_start;
+ out->hsync_end = in->hsync_end;
+ out->htotal = in->htotal;
+ out->hskew = in->hskew;
+ out->vdisplay = in->vdisplay;
+ out->vsync_start = in->vsync_start;
+ out->vsync_end = in->vsync_end;
+ out->vtotal = in->vtotal;
+ out->vscan = in->vscan;
+ out->vrefresh = in->vrefresh;
+ out->flags = in->flags;
+ out->type = in->type;
+ strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
+ out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+}
+
+/**
+ * drm_mode_getresources - get graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, connector and framebuffer configuration.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getresources(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_card_res *card_res = data;
+ struct list_head *lh;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int ret = 0;
+ int connector_count = 0;
+ int crtc_count = 0;
+ int fb_count = 0;
+ int encoder_count = 0;
+ int copied = 0, i;
+ uint32_t __user *fb_id;
+ uint32_t __user *crtc_id;
+ uint32_t __user *connector_id;
+ uint32_t __user *encoder_id;
+ struct drm_mode_group *mode_group;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /*
+ * For the non-control nodes we need to limit the list of resources
+ * by IDs in the group list for this node
+ */
+ list_for_each(lh, &file_priv->fbs)
+ fb_count++;
+
+ mode_group = &file_priv->master->minor->mode_group;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+
+ list_for_each(lh, &dev->mode_config.crtc_list)
+ crtc_count++;
+
+ list_for_each(lh, &dev->mode_config.connector_list)
+ connector_count++;
+
+ list_for_each(lh, &dev->mode_config.encoder_list)
+ encoder_count++;
+ } else {
+
+ crtc_count = mode_group->num_crtcs;
+ connector_count = mode_group->num_connectors;
+ encoder_count = mode_group->num_encoders;
+ }
+
+ card_res->max_height = dev->mode_config.max_height;
+ card_res->min_height = dev->mode_config.min_height;
+ card_res->max_width = dev->mode_config.max_width;
+ card_res->min_width = dev->mode_config.min_width;
+
+ /* handle this in 4 parts */
+ /* FBs */
+ if (card_res->count_fbs >= fb_count) {
+ copied = 0;
+ fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
+ list_for_each_entry(fb, &file_priv->fbs, head) {
+ if (put_user(fb->base.id, fb_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ card_res->count_fbs = fb_count;
+
+ /* CRTCs */
+ if (card_res->count_crtcs >= crtc_count) {
+ copied = 0;
+ crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list,
+ head) {
+ DRM_DEBUG("CRTC ID is %d\n", crtc->base.id);
+ if (put_user(crtc->base.id, crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = 0; i < mode_group->num_crtcs; i++) {
+ if (put_user(mode_group->id_list[i],
+ crtc_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_crtcs = crtc_count;
+
+ /* Encoders */
+ if (card_res->count_encoders >= encoder_count) {
+ copied = 0;
+ encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(encoder,
+ &dev->mode_config.encoder_list,
+ head) {
+ DRM_DEBUG("ENCODER ID is %d\n",
+ encoder->base.id);
+ if (put_user(encoder->base.id, encoder_id +
+ copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) {
+ if (put_user(mode_group->id_list[i],
+ encoder_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+
+ }
+ }
+ card_res->count_encoders = encoder_count;
+
+ /* Connectors */
+ if (card_res->count_connectors >= connector_count) {
+ copied = 0;
+ connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
+ if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list,
+ head) {
+ DRM_DEBUG("CONNECTOR ID is %d\n",
+ connector->base.id);
+ if (put_user(connector->base.id,
+ connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ } else {
+ int start = mode_group->num_crtcs +
+ mode_group->num_encoders;
+ for (i = start; i < start + mode_group->num_connectors; i++) {
+ if (put_user(mode_group->id_list[i],
+ connector_id + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ card_res->count_connectors = connector_count;
+
+ DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs,
+ card_res->count_connectors, card_res->count_encoders);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getcrtc - get CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a CRTC configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc *crtc_resp = data;
+ struct drm_crtc *crtc;
+ struct drm_mode_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ crtc_resp->x = crtc->x;
+ crtc_resp->y = crtc->y;
+ crtc_resp->gamma_size = crtc->gamma_size;
+ if (crtc->fb)
+ crtc_resp->fb_id = crtc->fb->base.id;
+ else
+ crtc_resp->fb_id = 0;
+
+ if (crtc->enabled) {
+
+ drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode);
+ crtc_resp->mode_valid = 1;
+
+ } else {
+ crtc_resp->mode_valid = 0;
+ }
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getconnector - get connector configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Construct a connector configuration structure to return to the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getconnector(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_get_connector *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ int mode_count = 0;
+ int props_count = 0;
+ int encoders_count = 0;
+ int ret = 0;
+ int copied = 0;
+ int i;
+ struct drm_mode_modeinfo u_mode;
+ struct drm_mode_modeinfo __user *mode_ptr;
+ uint32_t __user *prop_ptr;
+ uint64_t __user *prop_values;
+ uint32_t __user *encoder_ptr;
+
+ memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+ DRM_DEBUG("connector id %d:\n", out_resp->connector_id);
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ props_count++;
+ }
+ }
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ encoders_count++;
+ }
+ }
+
+ if (out_resp->count_modes == 0) {
+ connector->funcs->fill_modes(connector,
+ dev->mode_config.max_width,
+ dev->mode_config.max_height);
+ }
+
+ /* delayed so we get modes regardless of pre-fill_modes state */
+ list_for_each_entry(mode, &connector->modes, head)
+ mode_count++;
+
+ out_resp->connector_id = connector->base.id;
+ out_resp->connector_type = connector->connector_type;
+ out_resp->connector_type_id = connector->connector_type_id;
+ out_resp->mm_width = connector->display_info.width_mm;
+ out_resp->mm_height = connector->display_info.height_mm;
+ out_resp->subpixel = connector->display_info.subpixel_order;
+ out_resp->connection = connector->status;
+ if (connector->encoder)
+ out_resp->encoder_id = connector->encoder->base.id;
+ else
+ out_resp->encoder_id = 0;
+
+ /*
+ * This ioctl is called twice, once to determine how much space is
+ * needed, and the 2nd time to fill it.
+ */
+ if ((out_resp->count_modes >= mode_count) && mode_count) {
+ copied = 0;
+ mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
+ list_for_each_entry(mode, &connector->modes, head) {
+ drm_crtc_convert_to_umode(&u_mode, mode);
+ if (copy_to_user(mode_ptr + copied,
+ &u_mode, sizeof(u_mode))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ out_resp->count_modes = mode_count;
+
+ if ((out_resp->count_props >= props_count) && props_count) {
+ copied = 0;
+ prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr);
+ prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] != 0) {
+ if (put_user(connector->property_ids[i],
+ prop_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (put_user(connector->property_values[i],
+ prop_values + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_props = props_count;
+
+ if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+ copied = 0;
+ encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr);
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] != 0) {
+ if (put_user(connector->encoder_ids[i],
+ encoder_ptr + copied)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ copied++;
+ }
+ }
+ }
+ out_resp->count_encoders = encoders_count;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_getencoder(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_get_encoder *enc_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, enc_resp->encoder_id,
+ DRM_MODE_OBJECT_ENCODER);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ encoder = obj_to_encoder(obj);
+
+ if (encoder->crtc)
+ enc_resp->crtc_id = encoder->crtc->base.id;
+ else
+ enc_resp->crtc_id = 0;
+ enc_resp->encoder_type = encoder->encoder_type;
+ enc_resp->encoder_id = encoder->base.id;
+ enc_resp->possible_crtcs = encoder->possible_crtcs;
+ enc_resp->possible_clones = encoder->possible_clones;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_setcrtc - set CRTC configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Build a new CRTC configuration based on user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_setcrtc(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_mode_crtc *crtc_req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc, *crtcfb;
+ struct drm_connector **connector_set = NULL, *connector;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_display_mode *mode = NULL;
+ struct drm_mode_set set;
+ uint32_t __user *set_connectors_ptr;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_req->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (crtc_req->mode_valid) {
+ /* If we have a mode we need a framebuffer. */
+ /* If we pass -1, set the mode with the currently bound fb */
+ if (crtc_req->fb_id == -1) {
+ list_for_each_entry(crtcfb,
+ &dev->mode_config.crtc_list, head) {
+ if (crtcfb == crtc) {
+ DRM_DEBUG("Using current fb for setmode\n");
+ fb = crtc->fb;
+ }
+ }
+ } else {
+ obj = drm_mode_object_find(dev, crtc_req->fb_id,
+ DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+ }
+
+ mode = drm_mode_create(dev);
+ drm_crtc_convert_umode(mode, &crtc_req->mode);
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ if (crtc_req->count_connectors == 0 && mode) {
+ DRM_DEBUG("Count connectors is 0 but mode set\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0 && !mode && !fb) {
+ DRM_DEBUG("Count connectors is %d but no mode or fb set\n",
+ crtc_req->count_connectors);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (crtc_req->count_connectors > 0) {
+ u32 out_id;
+
+ /* Avoid unbounded kernel memory allocation */
+ if (crtc_req->count_connectors > config->num_connector) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ connector_set = kmalloc(crtc_req->count_connectors *
+ sizeof(struct drm_connector *),
+ GFP_KERNEL);
+ if (!connector_set) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < crtc_req->count_connectors; i++) {
+ set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr;
+ if (get_user(out_id, &set_connectors_ptr[i])) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_id,
+ DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ DRM_DEBUG("Connector id %d unknown\n", out_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ connector_set[i] = connector;
+ }
+ }
+
+ set.crtc = crtc;
+ set.x = crtc_req->x;
+ set.y = crtc_req->y;
+ set.mode = mode;
+ set.connectors = connector_set;
+ set.num_connectors = crtc_req->count_connectors;
+ set.fb =fb;
+ ret = crtc->funcs->set_config(&set);
+
+out:
+ kfree(connector_set);
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_cursor *req = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!req->flags) {
+ DRM_ERROR("no operation set\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc_id);
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ if (req->flags & DRM_MODE_CURSOR_BO) {
+ if (!crtc->funcs->cursor_set) {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -ENXIO;
+ goto out;
+ }
+ /* Turns off the cursor if handle is 0 */
+ ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
+ req->width, req->height);
+ }
+
+ if (req->flags & DRM_MODE_CURSOR_MOVE) {
+ if (crtc->funcs->cursor_move) {
+ ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
+ } else {
+ DRM_ERROR("crtc does not support cursor\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_addfb - add an FB to the graphics configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Add a new FB to the specified CRTC, given a user request.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ if ((config->min_width > r->width) || (r->width > config->max_width)) {
+ DRM_ERROR("mode new framebuffer width not within limits\n");
+ return -EINVAL;
+ }
+ if ((config->min_height > r->height) || (r->height > config->max_height)) {
+ DRM_ERROR("mode new framebuffer height not within limits\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ /* TODO check buffer is sufficently large */
+ /* TODO setup destructor callback */
+
+ fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
+ if (!fb) {
+ DRM_ERROR("could not create framebuffer\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ r->fb_id = fb->base.id;
+ list_add(&fb->filp_head, &file_priv->fbs);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_rmfb - remove an FB from the configuration
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Remove the FB specified by the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_framebuffer *fbl = NULL;
+ uint32_t *id = data;
+ int ret = 0;
+ int found = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
+ /* TODO check that we realy get a framebuffer back. */
+ if (!obj) {
+ DRM_ERROR("mode invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+ if (fb == fbl)
+ found = 1;
+
+ if (!found) {
+ DRM_ERROR("tried to remove a fb that we didn't own\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* TODO release all crtc connected to the framebuffer */
+ /* TODO unhock the destructor from the buffer object */
+
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_mode_getfb - get FB info
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * LOCKING:
+ * Caller? (FIXME)
+ *
+ * Lookup the FB given its ID and return info about it.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_fb_cmd *r = data;
+ struct drm_mode_object *obj;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ DRM_ERROR("invalid framebuffer id\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ fb = obj_to_fb(obj);
+
+ r->height = fb->height;
+ r->width = fb->width;
+ r->depth = fb->depth;
+ r->bpp = fb->bits_per_pixel;
+ r->pitch = fb->pitch;
+ fb->funcs->create_handle(fb, file_priv, &r->handle);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+/**
+ * drm_fb_release - remove and free the FBs on this file
+ * @filp: file * from the ioctl
+ *
+ * LOCKING:
+ * Takes mode config lock.
+ *
+ * Destroy all the FBs associated with @filp.
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+void drm_fb_release(struct file *filp)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_framebuffer *fb, *tfb;
+
+ mutex_lock(&dev->mode_config.mutex);
+ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+ list_del(&fb->filp_head);
+ fb->funcs->destroy(fb);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+/**
+ * drm_mode_attachmode - add a mode to the user mode list
+ * @dev: DRM device
+ * @connector: connector to add the mode to
+ * @mode: mode to add
+ *
+ * Add @mode to @connector's user mode list.
+ */
+static int drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int ret = 0;
+
+ list_add_tail(&mode->head, &connector->user_modes);
+ return ret;
+}
+
+int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+ int ret = 0;
+ struct drm_display_mode *dup_mode;
+ int need_dup = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ break;
+ if (connector->encoder->crtc == crtc) {
+ if (need_dup)
+ dup_mode = drm_mode_duplicate(dev, mode);
+ else
+ dup_mode = mode;
+ ret = drm_mode_attachmode(dev, connector, dup_mode);
+ if (ret)
+ return ret;
+ need_dup = 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_attachmode_crtc);
+
+static int drm_mode_detachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int found = 0;
+ int ret = 0;
+ struct drm_display_mode *match_mode, *t;
+
+ list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) {
+ if (drm_mode_equal(match_mode, mode)) {
+ list_del(&match_mode->head);
+ drm_mode_destroy(dev, match_mode);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_mode_detachmode(dev, connector, mode);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_detachmode_crtc);
+
+/**
+ * drm_fb_attachmode - Attach a user mode to an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * This attaches a user specified mode to an connector.
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_attachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+ struct drm_mode_object *obj;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ mode = drm_mode_create(dev);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ drm_crtc_convert_umode(mode, umode);
+
+ ret = drm_mode_attachmode(dev, connector, mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+
+/**
+ * drm_fb_detachmode - Detach a user specified mode from an connector
+ * @inode: inode from the ioctl
+ * @filp: file * from the ioctl
+ * @cmd: cmd from ioctl
+ * @arg: arg from ioctl
+ *
+ * Called by the user via ioctl.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_detachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_mode_cmd *mode_cmd = data;
+ struct drm_connector *connector;
+ struct drm_display_mode mode;
+ struct drm_mode_modeinfo *umode = &mode_cmd->mode;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ drm_crtc_convert_umode(&mode, umode);
+ ret = drm_mode_detachmode(dev, connector, &mode);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+ const char *name, int num_values)
+{
+ struct drm_property *property = NULL;
+
+ property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
+ if (!property)
+ return NULL;
+
+ if (num_values) {
+ property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
+ if (!property->values)
+ goto fail;
+ }
+
+ drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ property->flags = flags;
+ property->num_values = num_values;
+ INIT_LIST_HEAD(&property->enum_blob_list);
+
+ if (name)
+ strncpy(property->name, name, DRM_PROP_NAME_LEN);
+
+ list_add_tail(&property->head, &dev->mode_config.property_list);
+ return property;
+fail:
+ kfree(property);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_property_create);
+
+int drm_property_add_enum(struct drm_property *property, int index,
+ uint64_t value, const char *name)
+{
+ struct drm_property_enum *prop_enum;
+
+ if (!(property->flags & DRM_MODE_PROP_ENUM))
+ return -EINVAL;
+
+ if (!list_empty(&property->enum_blob_list)) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+ if (prop_enum->value == value) {
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ return 0;
+ }
+ }
+ }
+
+ prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
+ if (!prop_enum)
+ return -ENOMEM;
+
+ strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+ prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ prop_enum->value = value;
+
+ property->values[index] = value;
+ list_add_tail(&prop_enum->head, &property->enum_blob_list);
+ return 0;
+}
+EXPORT_SYMBOL(drm_property_add_enum);
+
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
+{
+ struct drm_property_enum *prop_enum, *pt;
+
+ list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) {
+ list_del(&prop_enum->head);
+ kfree(prop_enum);
+ }
+
+ if (property->num_values)
+ kfree(property->values);
+ drm_mode_object_put(dev, &property->base);
+ list_del(&property->head);
+ kfree(property);
+}
+EXPORT_SYMBOL(drm_property_destroy);
+
+int drm_connector_attach_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t init_val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == 0) {
+ connector->property_ids[i] = property->base.id;
+ connector->property_values[i] = init_val;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_property);
+
+int drm_connector_property_set_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t value)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ connector->property_values[i] = value;
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_set_value);
+
+int drm_connector_property_get_value(struct drm_connector *connector,
+ struct drm_property *property, uint64_t *val)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == property->base.id) {
+ *val = connector->property_values[i];
+ break;
+ }
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_property_get_value);
+
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_property *out_resp = data;
+ struct drm_property *property;
+ int enum_count = 0;
+ int blob_count = 0;
+ int value_count = 0;
+ int ret = 0, i;
+ int copied;
+ struct drm_property_enum *prop_enum;
+ struct drm_mode_property_enum __user *enum_ptr;
+ struct drm_property_blob *prop_blob;
+ uint32_t *blob_id_ptr;
+ uint64_t __user *values_ptr;
+ uint32_t __user *blob_length_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head)
+ enum_count++;
+ } else if (property->flags & DRM_MODE_PROP_BLOB) {
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head)
+ blob_count++;
+ }
+
+ value_count = property->num_values;
+
+ strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
+ out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+ out_resp->flags = property->flags;
+
+ if ((out_resp->count_values >= value_count) && value_count) {
+ values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr;
+ for (i = 0; i < value_count; i++) {
+ if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ }
+ out_resp->count_values = value_count;
+
+ if (property->flags & DRM_MODE_PROP_ENUM) {
+ if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
+ copied = 0;
+ enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr;
+ list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
+
+ if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (copy_to_user(&enum_ptr[copied].name,
+ &prop_enum->name, DRM_PROP_NAME_LEN)) {
+ ret = -EFAULT;
+ goto done;
+ }
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = enum_count;
+ }
+
+ if (property->flags & DRM_MODE_PROP_BLOB) {
+ if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
+ copied = 0;
+ blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr;
+ blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr;
+
+ list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
+ if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ if (put_user(prop_blob->length, blob_length_ptr + copied)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ copied++;
+ }
+ }
+ out_resp->count_enum_blobs = blob_count;
+ }
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length,
+ void *data)
+{
+ struct drm_property_blob *blob;
+
+ if (!length || !data)
+ return NULL;
+
+ blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+ if (!blob)
+ return NULL;
+
+ blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ blob->length = length;
+
+ memcpy(blob->data, data, length);
+
+ drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+
+ list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
+ return blob;
+}
+
+static void drm_property_destroy_blob(struct drm_device *dev,
+ struct drm_property_blob *blob)
+{
+ drm_mode_object_put(dev, &blob->base);
+ list_del(&blob->head);
+ kfree(blob);
+}
+
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_object *obj;
+ struct drm_mode_get_blob *out_resp = data;
+ struct drm_property_blob *blob;
+ int ret = 0;
+ void *blob_ptr;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
+ if (!obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ blob = obj_to_blob(obj);
+
+ if (out_resp->length == blob->length) {
+ blob_ptr = (void *)(unsigned long)out_resp->data;
+ if (copy_to_user(blob_ptr, blob->data, blob->length)){
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+ out_resp->length = blob->length;
+
+done:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+ struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0;
+
+ if (connector->edid_blob_ptr)
+ drm_property_destroy_blob(dev, connector->edid_blob_ptr);
+
+ /* Delete edid, when there is none. */
+ if (!edid) {
+ connector->edid_blob_ptr = NULL;
+ ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0);
+ return ret;
+ }
+
+ connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid);
+
+ ret = drm_connector_property_set_value(connector,
+ dev->mode_config.edid_property,
+ connector->edid_blob_ptr->base.id);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
+
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_connector_set_property *out_resp = data;
+ struct drm_mode_object *obj;
+ struct drm_property *property;
+ struct drm_connector *connector;
+ int ret = -EINVAL;
+ int i;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj) {
+ goto out;
+ }
+ connector = obj_to_connector(obj);
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
+ if (connector->property_ids[i] == out_resp->prop_id)
+ break;
+ }
+
+ if (i == DRM_CONNECTOR_MAX_PROPERTY) {
+ goto out;
+ }
+
+ obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
+ if (!obj) {
+ goto out;
+ }
+ property = obj_to_property(obj);
+
+ if (property->flags & DRM_MODE_PROP_IMMUTABLE)
+ goto out;
+
+ if (property->flags & DRM_MODE_PROP_RANGE) {
+ if (out_resp->value < property->values[0])
+ goto out;
+
+ if (out_resp->value > property->values[1])
+ goto out;
+ } else {
+ int found = 0;
+ for (i = 0; i < property->num_values; i++) {
+ if (property->values[i] == out_resp->value) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ goto out;
+ }
+ }
+
+ if (connector->funcs->set_property)
+ ret = connector->funcs->set_property(connector, property, out_resp->value);
+
+ /* store the property value if succesful */
+ if (!ret)
+ drm_connector_property_set_value(connector, property, out_resp->value);
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == 0) {
+ connector->encoder_ids[i] = encoder->base.id;
+ return 0;
+ }
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
+
+void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder)
+{
+ int i;
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ if (connector->encoder_ids[i] == encoder->base.id) {
+ connector->encoder_ids[i] = 0;
+ if (connector->encoder == encoder)
+ connector->encoder = NULL;
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
+
+bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size)
+{
+ crtc->gamma_size = gamma_size;
+
+ crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
+ if (!crtc->gamma_store) {
+ crtc->gamma_size = 0;
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+
+}
+
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_mode_crtc_lut *crtc_lut = data;
+ struct drm_mode_object *obj;
+ struct drm_crtc *crtc;
+ void *r_base, *g_base, *b_base;
+ int size;
+ int ret = 0;
+
+ mutex_lock(&dev->mode_config.mutex);
+ obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ ret = -EINVAL;
+ goto out;
+ }
+ crtc = obj_to_crtc(obj);
+
+ /* memcpy into gamma store */
+ if (crtc_lut->gamma_size != crtc->gamma_size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ size = crtc_lut->gamma_size * (sizeof(uint16_t));
+ r_base = crtc->gamma_store;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ g_base = r_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ b_base = g_base + size;
+ if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
+ ret = -EFAULT;
+ goto out;
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
new file mode 100644
index 000000000000..d8a982b71296
--- /dev/null
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ *
+ * DRM core CRTC related functions
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Authors:
+ * Keith Packard
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/*
+ * Detailed mode info for 800x600@60Hz
+ */
+static struct drm_display_mode std_mode[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+};
+
+/**
+ * drm_helper_probe_connector_modes - get complete set of display modes
+ * @dev: DRM device
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Based on @dev's mode_config layout, scan all the connectors and try to detect
+ * modes on them. Modes will first be added to the connector's probed_modes
+ * list, then culled (based on validity and the @maxX, @maxY parameters) and
+ * put into the normal modes list.
+ *
+ * Intended to be used either at bootup time or when major configuration
+ * changes have occurred.
+ *
+ * FIXME: take into account monitor limits
+ */
+void drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_display_mode *mode, *t;
+ struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+ int ret;
+
+ DRM_DEBUG("%s\n", drm_get_connector_name(connector));
+ /* set all modes to the unverified state */
+ list_for_each_entry_safe(mode, t, &connector->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ connector->status = connector->funcs->detect(connector);
+
+ if (connector->status == connector_status_disconnected) {
+ DRM_DEBUG("%s is disconnected\n",
+ drm_get_connector_name(connector));
+ /* TODO set EDID to NULL */
+ return;
+ }
+
+ ret = (*connector_funcs->get_modes)(connector);
+
+ if (ret) {
+ drm_mode_connector_list_update(connector);
+ }
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &connector->modes, maxX,
+ maxY, 0);
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ if (mode->status == MODE_OK)
+ mode->status = connector_funcs->mode_valid(connector,
+ mode);
+ }
+
+
+ drm_mode_prune_invalid(dev, &connector->modes, true);
+
+ if (list_empty(&connector->modes)) {
+ struct drm_display_mode *stdmode;
+
+ DRM_DEBUG("No valid modes on %s\n",
+ drm_get_connector_name(connector));
+
+ /* Should we do this here ???
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add a standard
+ * 640x480@60Hz mode and carry on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+ drm_mode_probed_add(connector, stdmode);
+ drm_mode_list_concat(&connector->probed_modes,
+ &connector->modes);
+
+ DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+ drm_get_connector_name(connector));
+ }
+
+ drm_mode_sort(&connector->modes);
+
+ DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector));
+ list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(mode);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
+
+void drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
+ uint32_t maxY)
+{
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ drm_helper_probe_single_connector_modes(connector, maxX, maxY);
+ }
+}
+EXPORT_SYMBOL(drm_helper_probe_connector_modes);
+
+
+/**
+ * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config
+ * @crtc: CRTC to check
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Walk @crtc's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @crtc is part of the mode_config, false otherwise.
+ */
+bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder;
+ struct drm_device *dev = crtc->dev;
+ /* FIXME: Locking around list access? */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ if (encoder->crtc == crtc)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_helper_crtc_in_use);
+
+/**
+ * drm_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled
+ * by calling its dpms function, which should power it off.
+ */
+void drm_helper_disable_unused_functions(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder_funcs = encoder->helper_private;
+ if (!encoder->crtc)
+ (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ if (!crtc->enabled) {
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+ crtc->fb = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_helper_disable_unused_functions);
+
+static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_width(mode) > width ||
+ drm_mode_height(mode) > height)
+ continue;
+ if (mode->type & DRM_MODE_TYPE_PREFERRED)
+ return mode;
+ }
+ return NULL;
+}
+
+static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
+{
+ bool enable;
+
+ if (strict) {
+ enable = connector->status == connector_status_connected;
+ } else {
+ enable = connector->status != connector_status_disconnected;
+ }
+ return enable;
+}
+
+static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
+{
+ bool any_enabled = false;
+ struct drm_connector *connector;
+ int i = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ enabled[i] = drm_connector_enabled(connector, true);
+ any_enabled |= enabled[i];
+ i++;
+ }
+
+ if (any_enabled)
+ return;
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ enabled[i] = drm_connector_enabled(connector, false);
+ i++;
+ }
+}
+
+static bool drm_target_preferred(struct drm_device *dev,
+ struct drm_display_mode **modes,
+ bool *enabled, int width, int height)
+{
+ struct drm_connector *connector;
+ int i = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+
+ if (enabled[i] == false) {
+ i++;
+ continue;
+ }
+
+ modes[i] = drm_has_preferred_mode(connector, width, height);
+ if (!modes[i]) {
+ list_for_each_entry(modes[i], &connector->modes, head)
+ break;
+ }
+ i++;
+ }
+ return true;
+}
+
+static int drm_pick_crtcs(struct drm_device *dev,
+ struct drm_crtc **best_crtcs,
+ struct drm_display_mode **modes,
+ int n, int width, int height)
+{
+ int c, o;
+ struct drm_connector *connector;
+ struct drm_connector_helper_funcs *connector_funcs;
+ struct drm_encoder *encoder;
+ struct drm_crtc *best_crtc;
+ int my_score, best_score, score;
+ struct drm_crtc **crtcs, *crtc;
+
+ if (n == dev->mode_config.num_connector)
+ return 0;
+ c = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (c == n)
+ break;
+ c++;
+ }
+
+ best_crtcs[n] = NULL;
+ best_crtc = NULL;
+ best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
+ if (modes[n] == NULL)
+ return best_score;
+
+ crtcs = kmalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!crtcs)
+ return best_score;
+
+ my_score = 1;
+ if (connector->status == connector_status_connected)
+ my_score++;
+ if (drm_has_preferred_mode(connector, width, height))
+ my_score++;
+
+ connector_funcs = connector->helper_private;
+ encoder = connector_funcs->best_encoder(connector);
+ if (!encoder)
+ goto out;
+
+ connector->encoder = encoder;
+
+ /* select a crtc for this connector and then attempt to configure
+ remaining connectors */
+ c = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+ if ((connector->encoder->possible_crtcs & (1 << c)) == 0) {
+ c++;
+ continue;
+ }
+
+ for (o = 0; o < n; o++)
+ if (best_crtcs[o] == crtc)
+ break;
+
+ if (o < n) {
+ /* ignore cloning for now */
+ c++;
+ continue;
+ }
+
+ crtcs[n] = crtc;
+ memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *));
+ score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1,
+ width, height);
+ if (score > best_score) {
+ best_crtc = crtc;
+ best_score = score;
+ memcpy(best_crtcs, crtcs,
+ dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *));
+ }
+ c++;
+ }
+out:
+ kfree(crtcs);
+ return best_score;
+}
+
+static void drm_setup_crtcs(struct drm_device *dev)
+{
+ struct drm_crtc **crtcs;
+ struct drm_display_mode **modes;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ bool *enabled;
+ int width, height;
+ int i, ret;
+
+ width = dev->mode_config.max_width;
+ height = dev->mode_config.max_height;
+
+ /* clean out all the encoder/crtc combos */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ encoder->crtc = NULL;
+ }
+
+ crtcs = kcalloc(dev->mode_config.num_connector,
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ modes = kcalloc(dev->mode_config.num_connector,
+ sizeof(struct drm_display_mode *), GFP_KERNEL);
+ enabled = kcalloc(dev->mode_config.num_connector,
+ sizeof(bool), GFP_KERNEL);
+
+ drm_enable_connectors(dev, enabled);
+
+ ret = drm_target_preferred(dev, modes, enabled, width, height);
+ if (!ret)
+ DRM_ERROR("Unable to find initial modes\n");
+
+ drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_display_mode *mode = modes[i];
+ struct drm_crtc *crtc = crtcs[i];
+
+ if (connector->encoder == NULL) {
+ i++;
+ continue;
+ }
+
+ if (mode && crtc) {
+ crtc->desired_mode = mode;
+ connector->encoder->crtc = crtc;
+ } else
+ connector->encoder->crtc = NULL;
+ i++;
+ }
+
+ kfree(crtcs);
+ kfree(modes);
+ kfree(enabled);
+}
+/**
+ * drm_crtc_set_mode - set a mode
+ * @crtc: CRTC to program
+ * @mode: mode to use
+ * @x: width of mode
+ * @y: height of mode
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance
+ * to fixup or reject the mode prior to trying to set it.
+ *
+ * RETURNS:
+ * True if the mode was set successfully, or false otherwise.
+ */
+bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_display_mode *adjusted_mode, saved_mode;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ int saved_x, saved_y;
+ struct drm_encoder *encoder;
+ bool ret = true;
+
+ adjusted_mode = drm_mode_duplicate(dev, mode);
+
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+
+ if (!crtc->enabled)
+ return true;
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+
+ /* Update crtc values up front so the driver can rely on them for mode
+ * setting.
+ */
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+
+ if (drm_mode_equal(&saved_mode, &crtc->mode)) {
+ if (saved_x != crtc->x || saved_y != crtc->y) {
+ crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y,
+ old_fb);
+ goto done;
+ }
+ }
+
+ /* Pass our mode to the connectors and the CRTC to give them a chance to
+ * adjust it according to limitations or connector properties, and also
+ * a chance to reject the mode entirely.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
+ adjusted_mode))) {
+ goto done;
+ }
+ }
+
+ if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
+ goto done;
+ }
+
+ /* Prepare the encoders and CRTCs before setting the mode. */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+ encoder_funcs = encoder->helper_private;
+ /* Disable the encoders as the first thing we do. */
+ encoder_funcs->prepare(encoder);
+ }
+
+ crtc_funcs->prepare(crtc);
+
+ /* Set up the DPLL and any encoders state that needs to adjust or depend
+ * on the DPLL.
+ */
+ crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
+ mode->name, mode->base.id);
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+ }
+
+ /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+ crtc_funcs->commit(crtc);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->commit(encoder);
+
+ }
+
+ /* XXX free adjustedmode */
+ drm_mode_destroy(dev, adjusted_mode);
+ /* FIXME: add subpixel order */
+done:
+ if (!ret) {
+ crtc->mode = saved_mode;
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_mode);
+
+
+/**
+ * drm_crtc_helper_set_config - set a new config from userspace
+ * @crtc: CRTC to setup
+ * @crtc_info: user provided configuration
+ * @new_mode: new mode to set
+ * @connector_set: set of connectors for the new config
+ * @fb: new framebuffer
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Setup a new configuration, provided by the user in @crtc_info, and enable
+ * it.
+ *
+ * RETURNS:
+ * Zero. (FIXME)
+ */
+int drm_crtc_helper_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *dev;
+ struct drm_crtc **save_crtcs, *new_crtc;
+ struct drm_encoder **save_encoders, *new_encoder;
+ struct drm_framebuffer *old_fb;
+ bool save_enabled;
+ bool changed = false;
+ bool flip_or_move = false;
+ struct drm_connector *connector;
+ int count = 0, ro, fail = 0;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int ret = 0;
+
+ DRM_DEBUG("\n");
+
+ if (!set)
+ return -EINVAL;
+
+ if (!set->crtc)
+ return -EINVAL;
+
+ if (!set->crtc->helper_private)
+ return -EINVAL;
+
+ crtc_funcs = set->crtc->helper_private;
+
+ DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n",
+ set->crtc, set->crtc->base.id, set->fb, set->connectors,
+ (int)set->num_connectors, set->x, set->y);
+
+ dev = set->crtc->dev;
+
+ /* save previous config */
+ save_enabled = set->crtc->enabled;
+
+ /* this is meant to be num_connector not num_crtc */
+ save_crtcs = kzalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_crtc *), GFP_KERNEL);
+ if (!save_crtcs)
+ return -ENOMEM;
+
+ save_encoders = kzalloc(dev->mode_config.num_connector *
+ sizeof(struct drm_encoders *), GFP_KERNEL);
+ if (!save_encoders) {
+ kfree(save_crtcs);
+ return -ENOMEM;
+ }
+
+ /* We should be able to check here if the fb has the same properties
+ * and then just flip_or_move it */
+ if (set->crtc->fb != set->fb) {
+ /* if we have no fb then its a change not a flip */
+ if (set->crtc->fb == NULL)
+ changed = true;
+ else
+ flip_or_move = true;
+ }
+
+ if (set->x != set->crtc->x || set->y != set->crtc->y)
+ flip_or_move = true;
+
+ if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
+ DRM_DEBUG("modes are different\n");
+ drm_mode_debug_printmodeline(&set->crtc->mode);
+ drm_mode_debug_printmodeline(set->mode);
+ changed = true;
+ }
+
+ /* a) traverse passed in connector list and get encoders for them */
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+ save_encoders[count++] = connector->encoder;
+ new_encoder = connector->encoder;
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector) {
+ new_encoder = connector_funcs->best_encoder(connector);
+ /* if we can't get an encoder for a connector
+ we are setting now - then fail */
+ if (new_encoder == NULL)
+ /* don't break so fail path works correct */
+ fail = 1;
+ break;
+ }
+ }
+
+ if (new_encoder != connector->encoder) {
+ changed = true;
+ connector->encoder = new_encoder;
+ }
+ }
+
+ if (fail) {
+ ret = -EINVAL;
+ goto fail_no_encoder;
+ }
+
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+
+ save_crtcs[count++] = connector->encoder->crtc;
+
+ if (connector->encoder->crtc == set->crtc)
+ new_crtc = NULL;
+ else
+ new_crtc = connector->encoder->crtc;
+
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] == connector)
+ new_crtc = set->crtc;
+ }
+ if (new_crtc != connector->encoder->crtc) {
+ changed = true;
+ connector->encoder->crtc = new_crtc;
+ }
+ }
+
+ /* mode_set_base is not a required function */
+ if (flip_or_move && !crtc_funcs->mode_set_base)
+ changed = true;
+
+ if (changed) {
+ old_fb = set->crtc->fb;
+ set->crtc->fb = set->fb;
+ set->crtc->enabled = (set->mode != NULL);
+ if (set->mode != NULL) {
+ DRM_DEBUG("attempting to set mode from userspace\n");
+ drm_mode_debug_printmodeline(set->mode);
+ if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
+ set->x, set->y,
+ old_fb)) {
+ ret = -EINVAL;
+ goto fail_set_mode;
+ }
+ /* TODO are these needed? */
+ set->crtc->desired_x = set->x;
+ set->crtc->desired_y = set->y;
+ set->crtc->desired_mode = set->mode;
+ }
+ drm_helper_disable_unused_functions(dev);
+ } else if (flip_or_move) {
+ old_fb = set->crtc->fb;
+ if (set->crtc->fb != set->fb)
+ set->crtc->fb = set->fb;
+ crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb);
+ }
+
+ kfree(save_encoders);
+ kfree(save_crtcs);
+ return 0;
+
+fail_set_mode:
+ set->crtc->enabled = save_enabled;
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->encoder->crtc = save_crtcs[count++];
+fail_no_encoder:
+ kfree(save_crtcs);
+ count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ connector->encoder = save_encoders[count++];
+ }
+ kfree(save_encoders);
+ return ret;
+}
+EXPORT_SYMBOL(drm_crtc_helper_set_config);
+
+bool drm_helper_plugged_event(struct drm_device *dev)
+{
+ DRM_DEBUG("\n");
+
+ drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
+ dev->mode_config.max_height);
+
+ drm_setup_crtcs(dev);
+
+ /* alert the driver fb layer */
+ dev->mode_config.funcs->fb_changed(dev);
+
+ /* FIXME: send hotplug event */
+ return true;
+}
+/**
+ * drm_initial_config - setup a sane initial connector configuration
+ * @dev: DRM device
+ * @can_grow: this configuration is growable
+ *
+ * LOCKING:
+ * Called at init time, must take mode config lock.
+ *
+ * Scan the CRTCs and connectors and try to put together an initial setup.
+ * At the moment, this is a cloned configuration across all heads with
+ * a new framebuffer object as the backing store.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
+bool drm_helper_initial_config(struct drm_device *dev, bool can_grow)
+{
+ int ret = false;
+
+ drm_helper_plugged_event(dev);
+ return ret;
+}
+EXPORT_SYMBOL(drm_helper_initial_config);
+
+/**
+ * drm_hotplug_stage_two
+ * @dev DRM device
+ * @connector hotpluged connector
+ *
+ * LOCKING.
+ * Caller must hold mode config lock, function might grab struct lock.
+ *
+ * Stage two of a hotplug.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
+ */
+int drm_helper_hotplug_stage_two(struct drm_device *dev)
+{
+ drm_helper_plugged_event(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
+
+int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ fb->width = mode_cmd->width;
+ fb->height = mode_cmd->height;
+ fb->pitch = mode_cmd->pitch;
+ fb->bits_per_pixel = mode_cmd->bpp;
+ fb->depth = mode_cmd->depth;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
+
+int drm_helper_resume_force_mode(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+ if (!crtc->enabled)
+ continue;
+
+ ret = drm_crtc_helper_set_mode(crtc, &crtc->mode,
+ crtc->x, crtc->y, crtc->fb);
+
+ if (ret == false)
+ DRM_ERROR("failed to set mode on crtc %p\n", crtc);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(drm_helper_resume_force_mode);
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 996097acb5e7..febb517ee679 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -74,6 +74,9 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+
DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -123,6 +126,23 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
};
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
@@ -138,8 +158,6 @@ static struct drm_ioctl_desc drm_ioctls[] = {
*/
int drm_lastclose(struct drm_device * dev)
{
- struct drm_magic_entry *pt, *next;
- struct drm_map_list *r_list, *list_t;
struct drm_vma_entry *vma, *vma_temp;
int i;
@@ -149,13 +167,7 @@ int drm_lastclose(struct drm_device * dev)
dev->driver->lastclose(dev);
DRM_DEBUG("driver lastclose completed\n");
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
-
- if (dev->irq_enabled)
+ if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
drm_irq_uninstall(dev);
mutex_lock(&dev->struct_mutex);
@@ -164,18 +176,9 @@ int drm_lastclose(struct drm_device * dev)
drm_drawable_free_all(dev);
del_timer(&dev->timer);
- /* Clear pid list */
- if (dev->magicfree.next) {
- list_for_each_entry_safe(pt, next, &dev->magicfree, head) {
- list_del(&pt->head);
- drm_ht_remove_item(&dev->magiclist, &pt->hash_item);
- drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
- }
- drm_ht_remove(&dev->magiclist);
- }
-
/* Clear AGP information */
- if (drm_core_has_AGP(dev) && dev->agp) {
+ if (drm_core_has_AGP(dev) && dev->agp &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
struct drm_agp_mem *entry, *tempe;
/* Remove AGP resources, but leave dev->agp
@@ -194,7 +197,8 @@ int drm_lastclose(struct drm_device * dev)
dev->agp->acquired = 0;
dev->agp->enabled = 0;
}
- if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) {
+ if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_sg_cleanup(dev->sg);
dev->sg = NULL;
}
@@ -205,13 +209,6 @@ int drm_lastclose(struct drm_device * dev)
drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
}
- list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
- if (!(r_list->map->flags & _DRM_DRIVER)) {
- drm_rmmap_locked(dev, r_list->map);
- r_list = NULL;
- }
- }
-
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {
for (i = 0; i < dev->queue_count; i++) {
if (dev->queuelist[i]) {
@@ -228,14 +225,11 @@ int drm_lastclose(struct drm_device * dev)
}
dev->queue_count = 0;
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !drm_core_check_feature(dev, DRIVER_MODESET))
drm_dma_takedown(dev);
- if (dev->lock.hw_lock) {
- dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
- dev->lock.file_priv = NULL;
- wake_up_interruptible(&dev->lock.lock_queue);
- }
+ dev->dev_mapping = NULL;
mutex_unlock(&dev->struct_mutex);
DRM_DEBUG("lastclose completed\n");
@@ -263,6 +257,8 @@ int drm_init(struct drm_driver *driver)
DRM_DEBUG("\n");
+ INIT_LIST_HEAD(&driver->device_list);
+
for (i = 0; driver->pci_driver.id_table[i].vendor != 0; i++) {
pid = (struct pci_device_id *)&driver->pci_driver.id_table[i];
@@ -329,35 +325,24 @@ static void drm_cleanup(struct drm_device * dev)
drm_ht_remove(&dev->map_hash);
drm_ctxbitmap_cleanup(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_put_minor(&dev->control);
+
+ if (dev->driver->driver_features & DRIVER_GEM)
+ drm_gem_destroy(dev);
+
drm_put_minor(&dev->primary);
if (drm_put_dev(dev))
DRM_ERROR("Cannot unload module\n");
}
-static int drm_minors_cleanup(int id, void *ptr, void *data)
-{
- struct drm_minor *minor = ptr;
- struct drm_device *dev;
- struct drm_driver *driver = data;
-
- dev = minor->dev;
- if (minor->dev->driver != driver)
- return 0;
-
- if (minor->type != DRM_MINOR_LEGACY)
- return 0;
-
- if (dev)
- pci_dev_put(dev->pdev);
- drm_cleanup(dev);
- return 1;
-}
-
void drm_exit(struct drm_driver *driver)
{
+ struct drm_device *dev, *tmp;
DRM_DEBUG("\n");
- idr_for_each(&drm_minors_idr, &drm_minors_cleanup, driver);
+ list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item)
+ drm_cleanup(dev);
DRM_INFO("Module unloaded\n");
}
@@ -503,7 +488,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
retcode = -EINVAL;
} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
((ioctl->flags & DRM_AUTH) && !file_priv->authenticated) ||
- ((ioctl->flags & DRM_MASTER) && !file_priv->master)) {
+ ((ioctl->flags & DRM_MASTER) && !file_priv->is_master)) {
retcode = -EACCES;
} else {
if (cmd & (IOC_IN | IOC_OUT)) {
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
new file mode 100644
index 000000000000..0fbb0da342cb
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 2006 Luc Verhaegen (quirks list)
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
+ * FB layer.
+ * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm_edid.h"
+
+/*
+ * TODO:
+ * - support EDID 1.4 (incl. CE blocks)
+ */
+
+/*
+ * EDID blocks out in the wild have a variety of bugs, try to collect
+ * them here (note that userspace may work around broken monitors first,
+ * but fixes should make their way here so that the kernel "just works"
+ * on as many displays as possible).
+ */
+
+/* First detailed mode wrong, use largest 60Hz mode */
+#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0)
+/* Reported 135MHz pixel clock is too high, needs adjustment */
+#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1)
+/* Prefer the largest mode at 75 Hz */
+#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2)
+/* Detail timing is in cm not mm */
+#define EDID_QUIRK_DETAILED_IN_CM (1 << 3)
+/* Detailed timing descriptors have bogus size values, so just take the
+ * maximum size and use that.
+ */
+#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4)
+/* Monitor forgot to set the first detailed is preferred bit. */
+#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
+/* use +hsync +vsync for detailed mode */
+#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+
+static struct edid_quirk {
+ char *vendor;
+ int product_id;
+ u32 quirks;
+} edid_quirk_list[] = {
+ /* Acer AL1706 */
+ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Acer F51 */
+ { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Unknown Acer */
+ { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Belinea 10 15 55 */
+ { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
+ { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
+
+ /* Envision Peripherals, Inc. EN-7100e */
+ { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
+
+ /* Funai Electronics PM36B */
+ { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
+ EDID_QUIRK_DETAILED_IN_CM },
+
+ /* LG Philips LCD LP154W01-A5 */
+ { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+ { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
+
+ /* Philips 107p5 CRT */
+ { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Proview AY765C */
+ { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
+
+ /* Samsung SyncMaster 205BW. Note: irony */
+ { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
+ /* Samsung SyncMaster 22[5-6]BW */
+ { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
+ { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
+};
+
+
+/* Valid EDID header has these bytes */
+static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+/**
+ * edid_is_valid - sanity check EDID data
+ * @edid: EDID data
+ *
+ * Sanity check the EDID block by looking at the header, the version number
+ * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
+ * valid.
+ */
+static bool edid_is_valid(struct edid *edid)
+{
+ int i;
+ u8 csum = 0;
+ u8 *raw_edid = (u8 *)edid;
+
+ if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+ goto bad;
+ if (edid->version != 1) {
+ DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
+ goto bad;
+ }
+ if (edid->revision <= 0 || edid->revision > 3) {
+ DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision);
+ goto bad;
+ }
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ csum += raw_edid[i];
+ if (csum) {
+ DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
+ goto bad;
+ }
+
+ return 1;
+
+bad:
+ if (raw_edid) {
+ DRM_ERROR("Raw EDID:\n");
+ print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH);
+ printk("\n");
+ }
+ return 0;
+}
+
+/**
+ * edid_vendor - match a string against EDID's obfuscated vendor field
+ * @edid: EDID to match
+ * @vendor: vendor string
+ *
+ * Returns true if @vendor is in @edid, false otherwise
+ */
+static bool edid_vendor(struct edid *edid, char *vendor)
+{
+ char edid_vendor[3];
+
+ edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
+ edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
+ ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
+ edid_vendor[2] = (edid->mfg_id[2] & 0x1f) + '@';
+
+ return !strncmp(edid_vendor, vendor, 3);
+}
+
+/**
+ * edid_get_quirks - return quirk flags for a given EDID
+ * @edid: EDID to process
+ *
+ * This tells subsequent routines what fixes they need to apply.
+ */
+static u32 edid_get_quirks(struct edid *edid)
+{
+ struct edid_quirk *quirk;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
+ quirk = &edid_quirk_list[i];
+
+ if (edid_vendor(edid, quirk->vendor) &&
+ (EDID_PRODUCT_ID(edid) == quirk->product_id))
+ return quirk->quirks;
+ }
+
+ return 0;
+}
+
+#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
+#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
+
+
+/**
+ * edid_fixup_preferred - set preferred modes based on quirk list
+ * @connector: has mode list to fix up
+ * @quirks: quirks list
+ *
+ * Walk the mode list for @connector, clearing the preferred status
+ * on existing modes and setting it anew for the right mode ala @quirks.
+ */
+static void edid_fixup_preferred(struct drm_connector *connector,
+ u32 quirks)
+{
+ struct drm_display_mode *t, *cur_mode, *preferred_mode;
+ int target_refresh = 0;
+
+ if (list_empty(&connector->probed_modes))
+ return;
+
+ if (quirks & EDID_QUIRK_PREFER_LARGE_60)
+ target_refresh = 60;
+ if (quirks & EDID_QUIRK_PREFER_LARGE_75)
+ target_refresh = 75;
+
+ preferred_mode = list_first_entry(&connector->probed_modes,
+ struct drm_display_mode, head);
+
+ list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
+ cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
+
+ if (cur_mode == preferred_mode)
+ continue;
+
+ /* Largest mode is preferred */
+ if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
+ preferred_mode = cur_mode;
+
+ /* At a given size, try to get closest to target refresh */
+ if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
+ MODE_REFRESH_DIFF(cur_mode, target_refresh) <
+ MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
+ preferred_mode = cur_mode;
+ }
+ }
+
+ preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
+}
+
+/**
+ * drm_mode_std - convert standard mode info (width, height, refresh) into mode
+ * @t: standard timing params
+ *
+ * Take the standard timing params (in this case width, aspect, and refresh)
+ * and convert them into a real mode using CVT.
+ *
+ * Punts for now, but should eventually use the FB layer's CVT based mode
+ * generation code.
+ */
+struct drm_display_mode *drm_mode_std(struct drm_device *dev,
+ struct std_timing *t)
+{
+ struct drm_display_mode *mode;
+ int hsize = t->hsize * 8 + 248, vsize;
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ if (t->aspect_ratio == 0)
+ vsize = (hsize * 10) / 16;
+ else if (t->aspect_ratio == 1)
+ vsize = (hsize * 3) / 4;
+ else if (t->aspect_ratio == 2)
+ vsize = (hsize * 4) / 5;
+ else
+ vsize = (hsize * 9) / 16;
+
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+/**
+ * drm_mode_detailed - create a new mode from an EDID detailed timing section
+ * @dev: DRM device (needed to create new mode)
+ * @edid: EDID block
+ * @timing: EDID detailed timing info
+ * @quirks: quirks to apply
+ *
+ * An EDID detailed timing block contains enough info for us to create and
+ * return a new struct drm_display_mode.
+ */
+static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
+ struct edid *edid,
+ struct detailed_timing *timing,
+ u32 quirks)
+{
+ struct drm_display_mode *mode;
+ struct detailed_pixel_timing *pt = &timing->data.pixel_data;
+
+ if (pt->stereo) {
+ printk(KERN_WARNING "stereo mode not supported\n");
+ return NULL;
+ }
+ if (!pt->separate_sync) {
+ printk(KERN_WARNING "integrated sync not supported\n");
+ return NULL;
+ }
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
+ timing->pixel_clock = 1088;
+
+ mode->clock = timing->pixel_clock * 10;
+
+ mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo;
+ mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) |
+ pt->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start +
+ ((pt->hsync_pulse_width_hi << 8) |
+ pt->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo);
+
+ mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo;
+ mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) |
+ pt->vsync_offset_lo);
+ mode->vsync_end = mode->vsync_start +
+ ((pt->vsync_pulse_width_hi << 8) |
+ pt->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo);
+
+ drm_mode_set_name(mode);
+
+ if (pt->interlaced)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+ if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
+ pt->hsync_positive = 1;
+ pt->vsync_positive = 1;
+ }
+
+ mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+
+ mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8);
+ mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8);
+
+ if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
+ mode->width_mm *= 10;
+ mode->height_mm *= 10;
+ }
+
+ if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
+ mode->width_mm = edid->width_cm * 10;
+ mode->height_mm = edid->height_cm * 10;
+ }
+
+ return mode;
+}
+
+/*
+ * Detailed mode info for the EDID "established modes" data to use.
+ */
+static struct drm_display_mode edid_est_modes[] = {
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
+ 968, 1056, 0, 600, 601, 605, 628, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824,
+ 896, 1024, 0, 600, 601, 603, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656,
+ 720, 840, 0, 480, 481, 484, 500, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704,
+ 768, 864, 0, 480, 483, 486, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */
+ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738,
+ 846, 900, 0, 400, 421, 423, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */
+ { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738,
+ 846, 900, 0, 400, 412, 414, 449, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040,
+ 1136, 1312, 0, 768, 769, 772, 800, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048,
+ 1184, 1328, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+ 1184, 1344, 0, 768, 771, 777, 806, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
+ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
+ 1208, 1264, 0, 768, 768, 776, 817, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
+ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
+ 928, 1152, 0, 624, 625, 628, 667, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816,
+ 896, 1056, 0, 600, 601, 604, 625, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */
+ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856,
+ 976, 1040, 0, 600, 637, 643, 666, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */
+ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
+ 1344, 1600, 0, 864, 865, 868, 900, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
+};
+
+#define EDID_EST_TIMINGS 16
+#define EDID_STD_TIMINGS 8
+#define EDID_DETAILED_TIMINGS 4
+
+/**
+ * add_established_modes - get est. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Each EDID block contains a bitmap of the supported "established modes" list
+ * (defined above). Tease them out and add them to the global modes list.
+ */
+static int add_established_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ unsigned long est_bits = edid->established_timings.t1 |
+ (edid->established_timings.t2 << 8) |
+ ((edid->established_timings.mfg_rsvd & 0x80) << 9);
+ int i, modes = 0;
+
+ for (i = 0; i <= EDID_EST_TIMINGS; i++)
+ if (est_bits & (1<<i)) {
+ struct drm_display_mode *newmode;
+ newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_standard_modes - get std. modes from EDID and add them
+ * @edid: EDID block to scan
+ *
+ * Standard modes can be calculated using the CVT standard. Grab them from
+ * @edid, calculate them, and add them to the list.
+ */
+static int add_standard_modes(struct drm_connector *connector, struct edid *edid)
+{
+ struct drm_device *dev = connector->dev;
+ int i, modes = 0;
+
+ for (i = 0; i < EDID_STD_TIMINGS; i++) {
+ struct std_timing *t = &edid->standard_timings[i];
+ struct drm_display_mode *newmode;
+
+ /* If std timings bytes are 1, 1 it's empty */
+ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1)
+ continue;
+
+ newmode = drm_mode_std(dev, &edid->standard_timings[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+
+ return modes;
+}
+
+/**
+ * add_detailed_modes - get detailed mode info from EDID data
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+ *
+ * Some of the detailed timing sections may contain mode information. Grab
+ * it and add it to the list.
+ */
+static int add_detailed_info(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+{
+ struct drm_device *dev = connector->dev;
+ int i, j, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+ struct detailed_non_pixel *data = &timing->data.other_data;
+ struct drm_display_mode *newmode;
+
+ /* EDID up to and including 1.2 may put monitor info here */
+ if (edid->version == 1 && edid->revision < 3)
+ continue;
+
+ /* Detailed mode timing */
+ if (timing->pixel_clock) {
+ newmode = drm_mode_detailed(dev, edid, timing, quirks);
+ if (!newmode)
+ continue;
+
+ /* First detailed mode is preferred */
+ if (i == 0 && edid->preferred_timing)
+ newmode->type |= DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector, newmode);
+
+ modes++;
+ continue;
+ }
+
+ /* Other timing or info */
+ switch (data->type) {
+ case EDID_DETAIL_MONITOR_SERIAL:
+ break;
+ case EDID_DETAIL_MONITOR_STRING:
+ break;
+ case EDID_DETAIL_MONITOR_RANGE:
+ /* Get monitor range data */
+ break;
+ case EDID_DETAIL_MONITOR_NAME:
+ break;
+ case EDID_DETAIL_MONITOR_CPDATA:
+ break;
+ case EDID_DETAIL_STD_MODES:
+ /* Five modes per detailed section */
+ for (j = 0; j < 5; i++) {
+ struct std_timing *std;
+ struct drm_display_mode *newmode;
+
+ std = &data->data.timings[j];
+ newmode = drm_mode_std(dev, std);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return modes;
+}
+
+#define DDC_ADDR 0x50
+
+unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
+{
+ unsigned char start = 0x0;
+ unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .flags = 0,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = buf,
+ }
+ };
+
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_info(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_do_probe_ddc_edid);
+
+static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ algo_data->setscl(algo_data->data, 1);
+
+ for (i = 0; i < 1; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 1);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = drm_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 1);
+ algo_data->setscl(algo_data->data, 1);
+
+ return edid;
+}
+
+/**
+ * drm_get_edid - get EDID data, if available
+ * @connector: connector we're probing
+ * @adapter: i2c adapter to use for DDC
+ *
+ * Poke the given connector's i2c channel to grab EDID data if possible.
+ *
+ * Return edid data or NULL if we couldn't find any.
+ */
+struct edid *drm_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter)
+{
+ struct edid *edid;
+
+ edid = (struct edid *)drm_ddc_read(adapter);
+ if (!edid) {
+ dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n",
+ drm_get_connector_name(connector));
+ return NULL;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ kfree(edid);
+ return NULL;
+ }
+
+ connector->display_info.raw_edid = (char *)edid;
+
+ return edid;
+}
+EXPORT_SYMBOL(drm_get_edid);
+
+/**
+ * drm_add_edid_modes - add modes from EDID data, if available
+ * @connector: connector we're probing
+ * @edid: edid data
+ *
+ * Add the specified modes to the connector's mode list.
+ *
+ * Return number of modes added or 0 if we couldn't find any.
+ */
+int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+{
+ int num_modes = 0;
+ u32 quirks;
+
+ if (edid == NULL) {
+ return 0;
+ }
+ if (!edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ return 0;
+ }
+
+ quirks = edid_get_quirks(edid);
+
+ num_modes += add_established_modes(connector, edid);
+ num_modes += add_standard_modes(connector, edid);
+ num_modes += add_detailed_info(connector, edid, quirks);
+
+ if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
+ edid_fixup_preferred(connector, quirks);
+
+ connector->display_info.serration_vsync = edid->serration_vsync;
+ connector->display_info.sync_on_green = edid->sync_on_green;
+ connector->display_info.composite_sync = edid->composite_sync;
+ connector->display_info.separate_syncs = edid->separate_syncs;
+ connector->display_info.blank_to_black = edid->blank_to_black;
+ connector->display_info.video_level = edid->video_level;
+ connector->display_info.digital = edid->digital;
+ connector->display_info.width_mm = edid->width_cm * 10;
+ connector->display_info.height_mm = edid->height_cm * 10;
+ connector->display_info.gamma = edid->gamma;
+ connector->display_info.gtf_supported = edid->default_gtf;
+ connector->display_info.standard_color = edid->standard_color;
+ connector->display_info.display_type = edid->display_type;
+ connector->display_info.active_off_supported = edid->pm_active_off;
+ connector->display_info.suspend_supported = edid->pm_suspend;
+ connector->display_info.standby_supported = edid->pm_standby;
+ connector->display_info.gamma = edid->gamma;
+
+ return num_modes;
+}
+EXPORT_SYMBOL(drm_add_edid_modes);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 78eeed5caaff..3733e36d135e 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -35,7 +35,6 @@
*/
#include "drmP.h"
-#include "drm_sarea.h"
#include <linux/poll.h>
#include <linux/smp_lock.h>
@@ -44,10 +43,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
static int drm_setup(struct drm_device * dev)
{
- drm_local_map_t *map;
int i;
int ret;
- u32 sareapage;
if (dev->driver->firstopen) {
ret = dev->driver->firstopen(dev);
@@ -55,20 +52,14 @@ static int drm_setup(struct drm_device * dev)
return ret;
}
- dev->magicfree.next = NULL;
-
- /* prebuild the SAREA */
- sareapage = max_t(unsigned, SAREA_MAX, PAGE_SIZE);
- i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
- if (i != 0)
- return i;
-
atomic_set(&dev->ioctl_count, 0);
atomic_set(&dev->vma_count, 0);
- dev->buf_use = 0;
- atomic_set(&dev->buf_alloc, 0);
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) {
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !drm_core_check_feature(dev, DRIVER_MODESET)) {
+ dev->buf_use = 0;
+ atomic_set(&dev->buf_alloc, 0);
+
i = drm_dma_setup(dev);
if (i < 0)
return i;
@@ -77,16 +68,12 @@ static int drm_setup(struct drm_device * dev)
for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
atomic_set(&dev->counts[i], 0);
- drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER);
- INIT_LIST_HEAD(&dev->magicfree);
-
dev->sigdata.lock = NULL;
- init_waitqueue_head(&dev->lock.lock_queue);
+
dev->queue_count = 0;
dev->queue_reserved = 0;
dev->queue_slots = 0;
dev->queuelist = NULL;
- dev->irq_enabled = 0;
dev->context_flag = 0;
dev->interrupt_flag = 0;
dev->dma_flag = 0;
@@ -147,10 +134,20 @@ int drm_open(struct inode *inode, struct file *filp)
spin_lock(&dev->count_lock);
if (!dev->open_count++) {
spin_unlock(&dev->count_lock);
- return drm_setup(dev);
+ retcode = drm_setup(dev);
+ goto out;
}
spin_unlock(&dev->count_lock);
}
+out:
+ mutex_lock(&dev->struct_mutex);
+ if (minor->type == DRM_MINOR_LEGACY) {
+ BUG_ON((dev->dev_mapping != NULL) &&
+ (dev->dev_mapping != inode->i_mapping));
+ if (dev->dev_mapping == NULL)
+ dev->dev_mapping = inode->i_mapping;
+ }
+ mutex_unlock(&dev->struct_mutex);
return retcode;
}
@@ -255,6 +252,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->lock_count = 0;
INIT_LIST_HEAD(&priv->lhead);
+ INIT_LIST_HEAD(&priv->fbs);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_open(dev, priv);
@@ -265,10 +263,42 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
goto out_free;
}
+
+ /* if there is no current master make this fd it */
mutex_lock(&dev->struct_mutex);
- if (list_empty(&dev->filelist))
- priv->master = 1;
+ if (!priv->minor->master) {
+ /* create a new master */
+ priv->minor->master = drm_master_create(priv->minor);
+ if (!priv->minor->master) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ priv->is_master = 1;
+ /* take another reference for the copy in the local file priv */
+ priv->master = drm_master_get(priv->minor->master);
+
+ priv->authenticated = 1;
+
+ mutex_unlock(&dev->struct_mutex);
+ if (dev->driver->master_create) {
+ ret = dev->driver->master_create(dev, priv->master);
+ if (ret) {
+ mutex_lock(&dev->struct_mutex);
+ /* drop both references if this fails */
+ drm_master_put(&priv->minor->master);
+ drm_master_put(&priv->master);
+ mutex_unlock(&dev->struct_mutex);
+ goto out_free;
+ }
+ }
+ } else {
+ /* get a reference to the master */
+ priv->master = drm_master_get(priv->minor->master);
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+ mutex_lock(&dev->struct_mutex);
list_add(&priv->lhead, &dev->filelist);
mutex_unlock(&dev->struct_mutex);
@@ -314,6 +344,74 @@ int drm_fasync(int fd, struct file *filp, int on)
}
EXPORT_SYMBOL(drm_fasync);
+/*
+ * Reclaim locked buffers; note that this may be a bad idea if the current
+ * context doesn't have the hw lock...
+ */
+static void drm_reclaim_locked_buffers(struct drm_device *dev, struct file *f)
+{
+ struct drm_file *file_priv = f->private_data;
+
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ } else {
+ unsigned long _end = jiffies + 3 * DRM_HZ;
+ int locked = 0;
+
+ drm_idlelock_take(&file_priv->master->lock);
+
+ /*
+ * Wait for a while.
+ */
+ do {
+ spin_lock_bh(&file_priv->master->lock.spinlock);
+ locked = file_priv->master->lock.idle_has_lock;
+ spin_unlock_bh(&file_priv->master->lock.spinlock);
+ if (locked)
+ break;
+ schedule();
+ } while (!time_after_eq(jiffies, _end));
+
+ if (!locked) {
+ DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
+ "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
+ "\tI will go on reclaiming the buffers anyway.\n");
+ }
+
+ dev->driver->reclaim_buffers_locked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
+ }
+}
+
+static void drm_master_release(struct drm_device *dev, struct file *filp)
+{
+ struct drm_file *file_priv = filp->private_data;
+
+ if (dev->driver->reclaim_buffers_locked &&
+ file_priv->master->lock.hw_lock)
+ drm_reclaim_locked_buffers(dev, filp);
+
+ if (dev->driver->reclaim_buffers_idlelocked &&
+ file_priv->master->lock.hw_lock) {
+ drm_idlelock_take(&file_priv->master->lock);
+ dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
+ drm_idlelock_release(&file_priv->master->lock);
+ }
+
+
+ if (drm_i_have_hw_lock(dev, file_priv)) {
+ DRM_DEBUG("File %p released, freeing lock for context %d\n",
+ filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ drm_lock_free(&file_priv->master->lock,
+ _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ }
+
+ if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
+ !dev->driver->reclaim_buffers_locked) {
+ dev->driver->reclaim_buffers(dev, file_priv);
+ }
+}
+
/**
* Release file.
*
@@ -348,60 +446,9 @@ int drm_release(struct inode *inode, struct file *filp)
(long)old_encode_dev(file_priv->minor->device),
dev->open_count);
- if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
- if (drm_i_have_hw_lock(dev, file_priv)) {
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- } else {
- unsigned long endtime = jiffies + 3 * DRM_HZ;
- int locked = 0;
-
- drm_idlelock_take(&dev->lock);
-
- /*
- * Wait for a while.
- */
-
- do{
- spin_lock_bh(&dev->lock.spinlock);
- locked = dev->lock.idle_has_lock;
- spin_unlock_bh(&dev->lock.spinlock);
- if (locked)
- break;
- schedule();
- } while (!time_after_eq(jiffies, endtime));
-
- if (!locked) {
- DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
- "\tdriver to use reclaim_buffers_idlelocked() instead.\n"
- "\tI will go on reclaiming the buffers anyway.\n");
- }
-
- dev->driver->reclaim_buffers_locked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
- }
- }
-
- if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) {
-
- drm_idlelock_take(&dev->lock);
- dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
- drm_idlelock_release(&dev->lock);
-
- }
-
- if (drm_i_have_hw_lock(dev, file_priv)) {
- DRM_DEBUG("File %p released, freeing lock for context %d\n",
- filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
-
- drm_lock_free(&dev->lock,
- _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
- }
-
-
- if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
- !dev->driver->reclaim_buffers_locked) {
- dev->driver->reclaim_buffers(dev, file_priv);
- }
+ /* if the master has gone away we can't do anything with the lock */
+ if (file_priv->minor->master)
+ drm_master_release(dev, filp);
if (dev->driver->driver_features & DRIVER_GEM)
drm_gem_release(dev, file_priv);
@@ -428,12 +475,24 @@ int drm_release(struct inode *inode, struct file *filp)
mutex_unlock(&dev->ctxlist_mutex);
mutex_lock(&dev->struct_mutex);
- if (file_priv->remove_auth_on_close == 1) {
+
+ if (file_priv->is_master) {
struct drm_file *temp;
+ list_for_each_entry(temp, &dev->filelist, lhead) {
+ if ((temp->master == file_priv->master) &&
+ (temp != file_priv))
+ temp->authenticated = 0;
+ }
- list_for_each_entry(temp, &dev->filelist, lhead)
- temp->authenticated = 0;
+ if (file_priv->minor->master == file_priv->master) {
+ /* drop the reference held my the minor */
+ drm_master_put(&file_priv->minor->master);
+ }
}
+
+ /* drop the reference held my the file priv */
+ drm_master_put(&file_priv->master);
+ file_priv->is_master = 0;
list_del(&file_priv->lhead);
mutex_unlock(&dev->struct_mutex);
@@ -448,9 +507,9 @@ int drm_release(struct inode *inode, struct file *filp)
atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
spin_lock(&dev->count_lock);
if (!--dev->open_count) {
- if (atomic_read(&dev->ioctl_count) || dev->blocked) {
- DRM_ERROR("Device busy: %d %d\n",
- atomic_read(&dev->ioctl_count), dev->blocked);
+ if (atomic_read(&dev->ioctl_count)) {
+ DRM_ERROR("Device busy: %d\n",
+ atomic_read(&dev->ioctl_count));
spin_unlock(&dev->count_lock);
unlock_kernel();
return -EBUSY;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index ccd1afdede02..9da581452874 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -64,6 +64,13 @@
* up at a later date, and as our interface with shmfs for memory allocation.
*/
+/*
+ * We make up offsets for buffer objects so we can recognize them at
+ * mmap time.
+ */
+#define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1)
+#define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16)
+
/**
* Initialize the GEM device fields
*/
@@ -71,6 +78,8 @@
int
drm_gem_init(struct drm_device *dev)
{
+ struct drm_gem_mm *mm;
+
spin_lock_init(&dev->object_name_lock);
idr_init(&dev->object_name_idr);
atomic_set(&dev->object_count, 0);
@@ -79,9 +88,41 @@ drm_gem_init(struct drm_device *dev)
atomic_set(&dev->pin_memory, 0);
atomic_set(&dev->gtt_count, 0);
atomic_set(&dev->gtt_memory, 0);
+
+ mm = drm_calloc(1, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ if (!mm) {
+ DRM_ERROR("out of memory\n");
+ return -ENOMEM;
+ }
+
+ dev->mm_private = mm;
+
+ if (drm_ht_create(&mm->offset_hash, 19)) {
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ return -ENOMEM;
+ }
+
+ if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START,
+ DRM_FILE_PAGE_OFFSET_SIZE)) {
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ drm_ht_remove(&mm->offset_hash);
+ return -ENOMEM;
+ }
+
return 0;
}
+void
+drm_gem_destroy(struct drm_device *dev)
+{
+ struct drm_gem_mm *mm = dev->mm_private;
+
+ drm_mm_takedown(&mm->offset_manager);
+ drm_ht_remove(&mm->offset_hash);
+ drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM);
+ dev->mm_private = NULL;
+}
+
/**
* Allocate a GEM object of the specified size with shmfs backing store
*/
@@ -419,3 +460,73 @@ drm_gem_object_handle_free(struct kref *kref)
}
EXPORT_SYMBOL(drm_gem_object_handle_free);
+/**
+ * drm_gem_mmap - memory map routine for GEM objects
+ * @filp: DRM file pointer
+ * @vma: VMA for the area to be mapped
+ *
+ * If a driver supports GEM object mapping, mmap calls on the DRM file
+ * descriptor will end up here.
+ *
+ * If we find the object based on the offset passed in (vma->vm_pgoff will
+ * contain the fake offset we created when the GTT map ioctl was called on
+ * the object), we set up the driver fault handler so that any accesses
+ * to the object can be trapped, to perform migration, GTT binding, surface
+ * register allocation, or performance monitoring.
+ */
+int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map *map = NULL;
+ struct drm_gem_object *obj;
+ struct drm_hash_item *hash;
+ unsigned long prot;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
+ mutex_unlock(&dev->struct_mutex);
+ return drm_mmap(filp, vma);
+ }
+
+ map = drm_hash_entry(hash, struct drm_map_list, hash)->map;
+ if (!map ||
+ ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) {
+ ret = -EPERM;
+ goto out_unlock;
+ }
+
+ /* Check for valid size. */
+ if (map->size < vma->vm_end - vma->vm_start) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ obj = map->handle;
+ if (!obj->dev->driver->gem_vm_ops) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND;
+ vma->vm_ops = obj->dev->driver->gem_vm_ops;
+ vma->vm_private_data = map->handle;
+ /* FIXME: use pgprot_writecombine when available */
+ prot = pgprot_val(vma->vm_page_prot);
+#ifdef CONFIG_X86
+ prot |= _PAGE_CACHE_WC;
+#endif
+ vma->vm_page_prot = __pgprot(prot);
+
+ vma->vm_file = filp; /* Needed for drm_vm_open() */
+ drm_vm_open_locked(vma);
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_gem_mmap);
diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
index 33160673a7b7..af539f7d87dd 100644
--- a/drivers/gpu/drm/drm_hashtab.c
+++ b/drivers/gpu/drm/drm_hashtab.c
@@ -127,6 +127,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
}
return 0;
}
+EXPORT_SYMBOL(drm_ht_insert_item);
/*
* Just insert an item and return any "bits" bit key that hasn't been
@@ -188,6 +189,7 @@ int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
ht->fill--;
return 0;
}
+EXPORT_SYMBOL(drm_ht_remove_item);
void drm_ht_remove(struct drm_open_hash *ht)
{
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 16829fb3089d..1fad76289e66 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -53,12 +53,13 @@ int drm_getunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
- if (u->unique_len >= dev->unique_len) {
- if (copy_to_user(u->unique, dev->unique, dev->unique_len))
+ if (u->unique_len >= master->unique_len) {
+ if (copy_to_user(u->unique, master->unique, master->unique_len))
return -EFAULT;
}
- u->unique_len = dev->unique_len;
+ u->unique_len = master->unique_len;
return 0;
}
@@ -81,36 +82,38 @@ int drm_setunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
+ struct drm_master *master = file_priv->master;
int domain, bus, slot, func, ret;
- if (dev->unique_len || dev->unique)
+ if (master->unique_len || master->unique)
return -EBUSY;
if (!u->unique_len || u->unique_len > 1024)
return -EINVAL;
- dev->unique_len = u->unique_len;
- dev->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER);
- if (!dev->unique)
+ master->unique_len = u->unique_len;
+ master->unique_size = u->unique_len + 1;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
+ if (!master->unique)
return -ENOMEM;
- if (copy_from_user(dev->unique, u->unique, dev->unique_len))
+ if (copy_from_user(master->unique, u->unique, master->unique_len))
return -EFAULT;
- dev->unique[dev->unique_len] = '\0';
+ master->unique[master->unique_len] = '\0';
dev->devname =
drm_alloc(strlen(dev->driver->pci_driver.name) +
- strlen(dev->unique) + 2, DRM_MEM_DRIVER);
+ strlen(master->unique) + 2, DRM_MEM_DRIVER);
if (!dev->devname)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
/* Return error if the busid submitted doesn't match the device's actual
* busid.
*/
- ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
+ ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
if (ret != 3)
return -EINVAL;
domain = bus >> 8;
@@ -125,34 +128,38 @@ int drm_setunique(struct drm_device *dev, void *data,
return 0;
}
-static int drm_set_busid(struct drm_device * dev)
+static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
{
+ struct drm_master *master = file_priv->master;
int len;
- if (dev->unique != NULL)
- return 0;
+ if (master->unique != NULL)
+ return -EBUSY;
- dev->unique_len = 40;
- dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
- if (dev->unique == NULL)
+ master->unique_len = 40;
+ master->unique_size = master->unique_len;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
+ if (master->unique == NULL)
return -ENOMEM;
- len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
- drm_get_pci_domain(dev), dev->pdev->bus->number,
+ len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d",
+ drm_get_pci_domain(dev),
+ dev->pdev->bus->number,
PCI_SLOT(dev->pdev->devfn),
PCI_FUNC(dev->pdev->devfn));
-
- if (len > dev->unique_len)
- DRM_ERROR("Unique buffer overflowed\n");
+ if (len >= master->unique_len)
+ DRM_ERROR("buffer overflow");
+ else
+ master->unique_len = len;
dev->devname =
- drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
+ drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len +
2, DRM_MEM_DRIVER);
if (dev->devname == NULL)
return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
- dev->unique);
+ master->unique);
return 0;
}
@@ -276,7 +283,7 @@ int drm_getstats(struct drm_device *dev, void *data,
for (i = 0; i < dev->counters; i++) {
if (dev->types[i] == _DRM_STAT_LOCK)
stats->data[i].value =
- (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
+ (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0);
else
stats->data[i].value = atomic_read(&dev->counts[i]);
stats->data[i].type = dev->types[i];
@@ -318,7 +325,7 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri
/*
* Version 1.1 includes tying of DRM to specific device
*/
- drm_set_busid(dev);
+ drm_set_busid(dev, file_priv);
}
}
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 1e787f894b3c..724e505873cf 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -116,6 +116,9 @@ void drm_vblank_cleanup(struct drm_device *dev)
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
DRM_MEM_DRIVER);
+ drm_free(dev->last_vblank_wait,
+ sizeof(*dev->last_vblank_wait) * dev->num_crtcs,
+ DRM_MEM_DRIVER);
drm_free(dev->vblank_inmodeset, sizeof(*dev->vblank_inmodeset) *
dev->num_crtcs, DRM_MEM_DRIVER);
@@ -161,6 +164,11 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
if (!dev->last_vblank)
goto err;
+ dev->last_vblank_wait = drm_calloc(num_crtcs, sizeof(u32),
+ DRM_MEM_DRIVER);
+ if (!dev->last_vblank_wait)
+ goto err;
+
dev->vblank_inmodeset = drm_calloc(num_crtcs, sizeof(int),
DRM_MEM_DRIVER);
if (!dev->vblank_inmodeset)
@@ -305,6 +313,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_INST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
if (dev->if_version < DRM_IF_VERSION(1, 2) &&
ctl->irq != dev->pdev->irq)
return -EINVAL;
@@ -312,6 +322,8 @@ int drm_control(struct drm_device *dev, void *data,
case DRM_UNINST_HANDLER:
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
return drm_irq_uninstall(dev);
default:
return -EINVAL;
@@ -427,6 +439,45 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_vblank_put);
/**
+ * drm_vblank_pre_modeset - account for vblanks across mode sets
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ * @post: post or pre mode set?
+ *
+ * Account for vblank events across mode setting events, which will likely
+ * reset the hardware frame counter.
+ */
+void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
+{
+ /*
+ * To avoid all the problems that might happen if interrupts
+ * were enabled/disabled around or between these calls, we just
+ * have the kernel take a reference on the CRTC (just once though
+ * to avoid corrupting the count if multiple, mismatch calls occur),
+ * so that interrupts remain enabled in the interim.
+ */
+ if (!dev->vblank_inmodeset[crtc]) {
+ dev->vblank_inmodeset[crtc] = 1;
+ drm_vblank_get(dev, crtc);
+ }
+}
+EXPORT_SYMBOL(drm_vblank_pre_modeset);
+
+void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
+{
+ unsigned long irqflags;
+
+ if (dev->vblank_inmodeset[crtc]) {
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ dev->vblank_disable_allowed = 1;
+ dev->vblank_inmodeset[crtc] = 0;
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ drm_vblank_put(dev, crtc);
+ }
+}
+EXPORT_SYMBOL(drm_vblank_post_modeset);
+
+/**
* drm_modeset_ctl - handle vblank event counter changes across mode switch
* @DRM_IOCTL_ARGS: standard ioctl arguments
*
@@ -441,7 +492,6 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_modeset_ctl *modeset = data;
- unsigned long irqflags;
int crtc, ret = 0;
/* If drm_vblank_init() hasn't been called yet, just no-op */
@@ -454,28 +504,12 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
goto out;
}
- /*
- * To avoid all the problems that might happen if interrupts
- * were enabled/disabled around or between these calls, we just
- * have the kernel take a reference on the CRTC (just once though
- * to avoid corrupting the count if multiple, mismatch calls occur),
- * so that interrupts remain enabled in the interim.
- */
switch (modeset->cmd) {
case _DRM_PRE_MODESET:
- if (!dev->vblank_inmodeset[crtc]) {
- dev->vblank_inmodeset[crtc] = 1;
- drm_vblank_get(dev, crtc);
- }
+ drm_vblank_pre_modeset(dev, crtc);
break;
case _DRM_POST_MODESET:
- if (dev->vblank_inmodeset[crtc]) {
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- dev->vblank_disable_allowed = 1;
- dev->vblank_inmodeset[crtc] = 0;
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- drm_vblank_put(dev, crtc);
- }
+ drm_vblank_post_modeset(dev, crtc);
break;
default:
ret = -EINVAL;
@@ -616,6 +650,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
} else {
DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
vblwait->request.sequence, crtc);
+ dev->last_vblank_wait[crtc] = vblwait->request.sequence;
DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
((drm_vblank_count(dev, crtc)
- vblwait->request.sequence) <= (1 << 23)));
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index 1cfa72031f8f..46e7b28f0707 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -52,6 +52,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
DECLARE_WAITQUEUE(entry, current);
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
int ret = 0;
++file_priv->lock_count;
@@ -64,26 +65,27 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
lock->context, task_pid_nr(current),
- dev->lock.hw_lock->lock, lock->flags);
+ master->lock.hw_lock->lock, lock->flags);
if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
if (lock->context < 0)
return -EINVAL;
- add_wait_queue(&dev->lock.lock_queue, &entry);
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters++;
- spin_unlock_bh(&dev->lock.spinlock);
+ add_wait_queue(&master->lock.lock_queue, &entry);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters++;
+ spin_unlock_bh(&master->lock.spinlock);
+
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
- if (!dev->lock.hw_lock) {
+ if (!master->lock.hw_lock) {
/* Device has been unregistered */
ret = -EINTR;
break;
}
- if (drm_lock_take(&dev->lock, lock->context)) {
- dev->lock.file_priv = file_priv;
- dev->lock.lock_time = jiffies;
+ if (drm_lock_take(&master->lock, lock->context)) {
+ master->lock.file_priv = file_priv;
+ master->lock.lock_time = jiffies;
atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
break; /* Got lock */
}
@@ -95,11 +97,11 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
break;
}
}
- spin_lock_bh(&dev->lock.spinlock);
- dev->lock.user_waiters--;
- spin_unlock_bh(&dev->lock.spinlock);
+ spin_lock_bh(&master->lock.spinlock);
+ master->lock.user_waiters--;
+ spin_unlock_bh(&master->lock.spinlock);
__set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->lock.lock_queue, &entry);
+ remove_wait_queue(&master->lock.lock_queue, &entry);
DRM_DEBUG("%d %s\n", lock->context,
ret ? "interrupted" : "has lock");
@@ -108,14 +110,14 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
/* don't set the block all signals on the master process for now
* really probably not the correct answer but lets us debug xkb
* xserver for now */
- if (!file_priv->master) {
+ if (!file_priv->is_master) {
sigemptyset(&dev->sigmask);
sigaddset(&dev->sigmask, SIGSTOP);
sigaddset(&dev->sigmask, SIGTSTP);
sigaddset(&dev->sigmask, SIGTTIN);
sigaddset(&dev->sigmask, SIGTTOU);
dev->sigdata.context = lock->context;
- dev->sigdata.lock = dev->lock.hw_lock;
+ dev->sigdata.lock = master->lock.hw_lock;
block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
}
@@ -154,6 +156,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_lock *lock = data;
+ struct drm_master *master = file_priv->master;
if (lock->context == DRM_KERNEL_CONTEXT) {
DRM_ERROR("Process %d using kernel context %d\n",
@@ -169,7 +172,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
if (dev->driver->kernel_context_switch_unlock)
dev->driver->kernel_context_switch_unlock(dev);
else {
- if (drm_lock_free(&dev->lock,lock->context)) {
+ if (drm_lock_free(&master->lock, lock->context)) {
/* FIXME: Should really bail out here. */
}
}
@@ -379,9 +382,10 @@ EXPORT_SYMBOL(drm_idlelock_release);
int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
{
- return (file_priv->lock_count && dev->lock.hw_lock &&
- _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
- dev->lock.file_priv == file_priv);
+ struct drm_master *master = file_priv->master;
+ return (file_priv->lock_count && master->lock.hw_lock &&
+ _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
+ master->lock.file_priv == file_priv);
}
EXPORT_SYMBOL(drm_i_have_hw_lock);
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 217ad7dc7076..367c590ffbba 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -296,3 +296,4 @@ void drm_mm_takedown(struct drm_mm * mm)
drm_free(entry, sizeof(*entry), DRM_MEM_MM);
}
+EXPORT_SYMBOL(drm_mm_takedown);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
new file mode 100644
index 000000000000..c9b80fdd4630
--- /dev/null
+++ b/drivers/gpu/drm/drm_modes.c
@@ -0,0 +1,576 @@
+/*
+ * The list_sort function is (presumably) licensed under the GPL (see the
+ * top level "COPYING" file for details).
+ *
+ * The remainder of this file is:
+ *
+ * Copyright © 1997-2003 by The XFree86 Project, Inc.
+ * Copyright © 2007 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of the copyright holder(s)
+ * and author(s) shall not be used in advertising or otherwise to promote
+ * the sale, use or other dealings in this Software without prior written
+ * authorization from the copyright holder(s) and author(s).
+ */
+
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+/**
+ * drm_mode_debug_printmodeline - debug print a mode
+ * @dev: DRM device
+ * @mode: mode to print
+ *
+ * LOCKING:
+ * None.
+ *
+ * Describe @mode using DRM_DEBUG.
+ */
+void drm_mode_debug_printmodeline(struct drm_display_mode *mode)
+{
+ DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->base.id, mode->name, mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+}
+EXPORT_SYMBOL(drm_mode_debug_printmodeline);
+
+/**
+ * drm_mode_set_name - set the name on a mode
+ * @mode: name will be set in this mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Set the name of @mode to a standard format.
+ */
+void drm_mode_set_name(struct drm_display_mode *mode)
+{
+ snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay,
+ mode->vdisplay);
+}
+EXPORT_SYMBOL(drm_mode_set_name);
+
+/**
+ * drm_mode_list_concat - move modes from one list to another
+ * @head: source list
+ * @new: dst list
+ *
+ * LOCKING:
+ * Caller must ensure both lists are locked.
+ *
+ * Move all the modes from @head to @new.
+ */
+void drm_mode_list_concat(struct list_head *head, struct list_head *new)
+{
+
+ struct list_head *entry, *tmp;
+
+ list_for_each_safe(entry, tmp, head) {
+ list_move_tail(entry, new);
+ }
+}
+EXPORT_SYMBOL(drm_mode_list_concat);
+
+/**
+ * drm_mode_width - get the width of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's width (hdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->hdisplay
+ */
+int drm_mode_width(struct drm_display_mode *mode)
+{
+ return mode->hdisplay;
+
+}
+EXPORT_SYMBOL(drm_mode_width);
+
+/**
+ * drm_mode_height - get the height of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's height (vdisplay) value.
+ *
+ * FIXME: is this needed?
+ *
+ * RETURNS:
+ * @mode->vdisplay
+ */
+int drm_mode_height(struct drm_display_mode *mode)
+{
+ return mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_mode_height);
+
+/**
+ * drm_mode_vrefresh - get the vrefresh of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @mode's vrefresh rate or calculate it if necessary.
+ *
+ * FIXME: why is this needed? shouldn't vrefresh be set already?
+ *
+ * RETURNS:
+ * Vertical refresh rate of @mode x 1000. For precision reasons.
+ */
+int drm_mode_vrefresh(struct drm_display_mode *mode)
+{
+ int refresh = 0;
+ unsigned int calc_val;
+
+ if (mode->vrefresh > 0)
+ refresh = mode->vrefresh;
+ else if (mode->htotal > 0 && mode->vtotal > 0) {
+ /* work out vrefresh the value will be x1000 */
+ calc_val = (mode->clock * 1000);
+
+ calc_val /= mode->htotal;
+ calc_val *= 1000;
+ calc_val /= mode->vtotal;
+
+ refresh = calc_val;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ refresh *= 2;
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ refresh /= 2;
+ if (mode->vscan > 1)
+ refresh /= mode->vscan;
+ }
+ return refresh;
+}
+EXPORT_SYMBOL(drm_mode_vrefresh);
+
+/**
+ * drm_mode_set_crtcinfo - set CRTC modesetting parameters
+ * @p: mode
+ * @adjust_flags: unused? (FIXME)
+ *
+ * LOCKING:
+ * None.
+ *
+ * Setup the CRTC modesetting parameters for @p, adjusting if necessary.
+ */
+void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
+{
+ if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN))
+ return;
+
+ p->crtc_hdisplay = p->hdisplay;
+ p->crtc_hsync_start = p->hsync_start;
+ p->crtc_hsync_end = p->hsync_end;
+ p->crtc_htotal = p->htotal;
+ p->crtc_hskew = p->hskew;
+ p->crtc_vdisplay = p->vdisplay;
+ p->crtc_vsync_start = p->vsync_start;
+ p->crtc_vsync_end = p->vsync_end;
+ p->crtc_vtotal = p->vtotal;
+
+ if (p->flags & DRM_MODE_FLAG_INTERLACE) {
+ if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
+ p->crtc_vdisplay /= 2;
+ p->crtc_vsync_start /= 2;
+ p->crtc_vsync_end /= 2;
+ p->crtc_vtotal /= 2;
+ }
+
+ p->crtc_vtotal |= 1;
+ }
+
+ if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
+ p->crtc_vdisplay *= 2;
+ p->crtc_vsync_start *= 2;
+ p->crtc_vsync_end *= 2;
+ p->crtc_vtotal *= 2;
+ }
+
+ if (p->vscan > 1) {
+ p->crtc_vdisplay *= p->vscan;
+ p->crtc_vsync_start *= p->vscan;
+ p->crtc_vsync_end *= p->vscan;
+ p->crtc_vtotal *= p->vscan;
+ }
+
+ p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay);
+ p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
+ p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
+ p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
+
+ p->crtc_hadjusted = false;
+ p->crtc_vadjusted = false;
+}
+EXPORT_SYMBOL(drm_mode_set_crtcinfo);
+
+
+/**
+ * drm_mode_duplicate - allocate and duplicate an existing mode
+ * @m: mode to duplicate
+ *
+ * LOCKING:
+ * None.
+ *
+ * Just allocate a new mode, copy the existing mode into it, and return
+ * a pointer to it. Used to create new instances of established modes.
+ */
+struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode)
+{
+ struct drm_display_mode *nmode;
+ int new_id;
+
+ nmode = drm_mode_create(dev);
+ if (!nmode)
+ return NULL;
+
+ new_id = nmode->base.id;
+ *nmode = *mode;
+ nmode->base.id = new_id;
+ INIT_LIST_HEAD(&nmode->head);
+ return nmode;
+}
+EXPORT_SYMBOL(drm_mode_duplicate);
+
+/**
+ * drm_mode_equal - test modes for equality
+ * @mode1: first mode
+ * @mode2: second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Check to see if @mode1 and @mode2 are equivalent.
+ *
+ * RETURNS:
+ * True if the modes are equal, false otherwise.
+ */
+bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2)
+{
+ /* do clock check convert to PICOS so fb modes get matched
+ * the same */
+ if (mode1->clock && mode2->clock) {
+ if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock))
+ return false;
+ } else if (mode1->clock != mode2->clock)
+ return false;
+
+ if (mode1->hdisplay == mode2->hdisplay &&
+ mode1->hsync_start == mode2->hsync_start &&
+ mode1->hsync_end == mode2->hsync_end &&
+ mode1->htotal == mode2->htotal &&
+ mode1->hskew == mode2->hskew &&
+ mode1->vdisplay == mode2->vdisplay &&
+ mode1->vsync_start == mode2->vsync_start &&
+ mode1->vsync_end == mode2->vsync_end &&
+ mode1->vtotal == mode2->vtotal &&
+ mode1->vscan == mode2->vscan &&
+ mode1->flags == mode2->flags)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(drm_mode_equal);
+
+/**
+ * drm_mode_validate_size - make sure modes adhere to size constraints
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @maxX: maximum width
+ * @maxY: maximum height
+ * @maxPitch: max pitch
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * The DRM device (@dev) has size and pitch limits. Here we validate the
+ * modes we probed for @dev against those limits and set their status as
+ * necessary.
+ */
+void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch)
+{
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, mode_list, head) {
+ if (maxPitch > 0 && mode->hdisplay > maxPitch)
+ mode->status = MODE_BAD_WIDTH;
+
+ if (maxX > 0 && mode->hdisplay > maxX)
+ mode->status = MODE_VIRTUAL_X;
+
+ if (maxY > 0 && mode->vdisplay > maxY)
+ mode->status = MODE_VIRTUAL_Y;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_size);
+
+/**
+ * drm_mode_validate_clocks - validate modes against clock limits
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @min: minimum clock rate array
+ * @max: maximum clock rate array
+ * @n_ranges: number of clock ranges (size of arrays)
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Some code may need to check a mode list against the clock limits of the
+ * device in question. This function walks the mode list, testing to make
+ * sure each mode falls within a given range (defined by @min and @max
+ * arrays) and sets @mode->status as needed.
+ */
+void drm_mode_validate_clocks(struct drm_device *dev,
+ struct list_head *mode_list,
+ int *min, int *max, int n_ranges)
+{
+ struct drm_display_mode *mode;
+ int i;
+
+ list_for_each_entry(mode, mode_list, head) {
+ bool good = false;
+ for (i = 0; i < n_ranges; i++) {
+ if (mode->clock >= min[i] && mode->clock <= max[i]) {
+ good = true;
+ break;
+ }
+ }
+ if (!good)
+ mode->status = MODE_CLOCK_RANGE;
+ }
+}
+EXPORT_SYMBOL(drm_mode_validate_clocks);
+
+/**
+ * drm_mode_prune_invalid - remove invalid modes from mode list
+ * @dev: DRM device
+ * @mode_list: list of modes to check
+ * @verbose: be verbose about it
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Once mode list generation is complete, a caller can use this routine to
+ * remove invalid modes from a mode list. If any of the modes have a
+ * status other than %MODE_OK, they are removed from @mode_list and freed.
+ */
+void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose)
+{
+ struct drm_display_mode *mode, *t;
+
+ list_for_each_entry_safe(mode, t, mode_list, head) {
+ if (mode->status != MODE_OK) {
+ list_del(&mode->head);
+ if (verbose) {
+ drm_mode_debug_printmodeline(mode);
+ DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status);
+ }
+ drm_mode_destroy(dev, mode);
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_prune_invalid);
+
+/**
+ * drm_mode_compare - compare modes for favorability
+ * @lh_a: list_head for first mode
+ * @lh_b: list_head for second mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Compare two modes, given by @lh_a and @lh_b, returning a value indicating
+ * which is better.
+ *
+ * RETURNS:
+ * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
+ * positive if @lh_b is better than @lh_a.
+ */
+static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
+{
+ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
+ struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
+ int diff;
+
+ diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) -
+ ((a->type & DRM_MODE_TYPE_PREFERRED) != 0);
+ if (diff)
+ return diff;
+ diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay;
+ if (diff)
+ return diff;
+ diff = b->clock - a->clock;
+ return diff;
+}
+
+/* FIXME: what we don't have a list sort function? */
+/* list sort from Mark J Roberts (mjr@znex.org) */
+void list_sort(struct list_head *head,
+ int (*cmp)(struct list_head *a, struct list_head *b))
+{
+ struct list_head *p, *q, *e, *list, *tail, *oldhead;
+ int insize, nmerges, psize, qsize, i;
+
+ list = head->next;
+ list_del(head);
+ insize = 1;
+ for (;;) {
+ p = oldhead = list;
+ list = tail = NULL;
+ nmerges = 0;
+
+ while (p) {
+ nmerges++;
+ q = p;
+ psize = 0;
+ for (i = 0; i < insize; i++) {
+ psize++;
+ q = q->next == oldhead ? NULL : q->next;
+ if (!q)
+ break;
+ }
+
+ qsize = insize;
+ while (psize > 0 || (qsize > 0 && q)) {
+ if (!psize) {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ } else if (!qsize || !q) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else if (cmp(p, q) <= 0) {
+ e = p;
+ p = p->next;
+ psize--;
+ if (p == oldhead)
+ p = NULL;
+ } else {
+ e = q;
+ q = q->next;
+ qsize--;
+ if (q == oldhead)
+ q = NULL;
+ }
+ if (tail)
+ tail->next = e;
+ else
+ list = e;
+ e->prev = tail;
+ tail = e;
+ }
+ p = q;
+ }
+
+ tail->next = list;
+ list->prev = tail;
+
+ if (nmerges <= 1)
+ break;
+
+ insize *= 2;
+ }
+
+ head->next = list;
+ head->prev = list->prev;
+ list->prev->next = head;
+ list->prev = head;
+}
+
+/**
+ * drm_mode_sort - sort mode list
+ * @mode_list: list to sort
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * Sort @mode_list by favorability, putting good modes first.
+ */
+void drm_mode_sort(struct list_head *mode_list)
+{
+ list_sort(mode_list, drm_mode_compare);
+}
+EXPORT_SYMBOL(drm_mode_sort);
+
+/**
+ * drm_mode_connector_list_update - update the mode list for the connector
+ * @connector: the connector to update
+ *
+ * LOCKING:
+ * Caller must hold a lock protecting @mode_list.
+ *
+ * This moves the modes from the @connector probed_modes list
+ * to the actual mode list. It compares the probed mode against the current
+ * list and only adds different modes. All modes unverified after this point
+ * will be removed by the prune invalid modes.
+ */
+void drm_mode_connector_list_update(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+ struct drm_display_mode *pmode, *pt;
+ int found_it;
+
+ list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
+ head) {
+ found_it = 0;
+ /* go through current modes checking for the new probed mode */
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (drm_mode_equal(pmode, mode)) {
+ found_it = 1;
+ /* if equal delete the probed mode */
+ mode->status = pmode->status;
+ list_del(&pmode->head);
+ drm_mode_destroy(connector->dev, pmode);
+ break;
+ }
+ }
+
+ if (!found_it) {
+ list_move_tail(&pmode->head, &connector->modes);
+ }
+ }
+}
+EXPORT_SYMBOL(drm_mode_connector_list_update);
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index ae73b7f7249a..8df849f66830 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -49,6 +49,8 @@ static int drm_queues_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
static int drm_bufs_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
+static int drm_vblank_info(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data);
static int drm_gem_name_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
static int drm_gem_object_info(char *buf, char **start, off_t offset,
@@ -72,6 +74,7 @@ static struct drm_proc_list {
{"clients", drm_clients_info, 0},
{"queues", drm_queues_info, 0},
{"bufs", drm_bufs_info, 0},
+ {"vblank", drm_vblank_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
{"gem_objects", drm_gem_object_info, DRIVER_GEM},
#if DRM_DEBUG_CODE
@@ -195,6 +198,7 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_master *master = minor->master;
struct drm_device *dev = minor->dev;
int len = 0;
@@ -203,13 +207,16 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
return 0;
}
+ if (!master)
+ return 0;
+
*start = &buf[offset];
*eof = 0;
- if (dev->unique) {
+ if (master->unique) {
DRM_PROC_PRINT("%s %s %s\n",
dev->driver->pci_driver.name,
- pci_name(dev->pdev), dev->unique);
+ pci_name(dev->pdev), master->unique);
} else {
DRM_PROC_PRINT("%s %s\n", dev->driver->pci_driver.name,
pci_name(dev->pdev));
@@ -454,6 +461,66 @@ static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
}
/**
+ * Called when "/proc/dri/.../vblank" is read.
+ *
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ */
+static int drm__vblank_info(char *buf, char **start, off_t offset, int request,
+ int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ int len = 0;
+ int crtc;
+
+ if (offset > DRM_PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = &buf[offset];
+ *eof = 0;
+
+ for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+ DRM_PROC_PRINT("CRTC %d enable: %d\n",
+ crtc, atomic_read(&dev->vblank_refcount[crtc]));
+ DRM_PROC_PRINT("CRTC %d counter: %d\n",
+ crtc, drm_vblank_count(dev, crtc));
+ DRM_PROC_PRINT("CRTC %d last wait: %d\n",
+ crtc, dev->last_vblank_wait[crtc]);
+ DRM_PROC_PRINT("CRTC %d in modeset: %d\n",
+ crtc, dev->vblank_inmodeset[crtc]);
+ }
+
+ if (len > request + offset)
+ return request;
+ *eof = 1;
+ return len - offset;
+}
+
+/**
+ * Simply calls _vblank_info() while holding the drm_device::struct_mutex lock.
+ */
+static int drm_vblank_info(char *buf, char **start, off_t offset, int request,
+ int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm__vblank_info(buf, start, offset, request, eof, data);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+/**
* Called when "/proc/dri/.../clients" is read.
*
* \param buf output buffer.
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 66c96ec66672..5ca132afa4f2 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -57,6 +57,14 @@ static int drm_minor_get_id(struct drm_device *dev, int type)
int ret;
int base = 0, limit = 63;
+ if (type == DRM_MINOR_CONTROL) {
+ base += 64;
+ limit = base + 127;
+ } else if (type == DRM_MINOR_RENDER) {
+ base += 128;
+ limit = base + 255;
+ }
+
again:
if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Out of memory expanding drawable idr\n");
@@ -79,6 +87,104 @@ again:
return new_id;
}
+struct drm_master *drm_master_create(struct drm_minor *minor)
+{
+ struct drm_master *master;
+
+ master = drm_calloc(1, sizeof(*master), DRM_MEM_DRIVER);
+ if (!master)
+ return NULL;
+
+ kref_init(&master->refcount);
+ spin_lock_init(&master->lock.spinlock);
+ init_waitqueue_head(&master->lock.lock_queue);
+ drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
+ INIT_LIST_HEAD(&master->magicfree);
+ master->minor = minor;
+
+ list_add_tail(&master->head, &minor->master_list);
+
+ return master;
+}
+
+struct drm_master *drm_master_get(struct drm_master *master)
+{
+ kref_get(&master->refcount);
+ return master;
+}
+
+static void drm_master_destroy(struct kref *kref)
+{
+ struct drm_master *master = container_of(kref, struct drm_master, refcount);
+ struct drm_magic_entry *pt, *next;
+ struct drm_device *dev = master->minor->dev;
+
+ list_del(&master->head);
+
+ if (dev->driver->master_destroy)
+ dev->driver->master_destroy(dev, master);
+
+ if (master->unique) {
+ drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER);
+ master->unique = NULL;
+ master->unique_len = 0;
+ }
+
+ list_for_each_entry_safe(pt, next, &master->magicfree, head) {
+ list_del(&pt->head);
+ drm_ht_remove_item(&master->magiclist, &pt->hash_item);
+ drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+ }
+
+ drm_ht_remove(&master->magiclist);
+
+ if (master->lock.hw_lock) {
+ if (dev->sigdata.lock == master->lock.hw_lock)
+ dev->sigdata.lock = NULL;
+ master->lock.hw_lock = NULL;
+ master->lock.file_priv = NULL;
+ wake_up_interruptible(&master->lock.lock_queue);
+ }
+
+ drm_free(master, sizeof(*master), DRM_MEM_DRIVER);
+}
+
+void drm_master_put(struct drm_master **master)
+{
+ kref_put(&(*master)->refcount, drm_master_destroy);
+ *master = NULL;
+}
+
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->master)
+ return -EINVAL;
+
+ if (!file_priv->minor->master &&
+ file_priv->minor->master != file_priv->master) {
+ mutex_lock(&dev->struct_mutex);
+ file_priv->minor->master = drm_master_get(file_priv->master);
+ mutex_lock(&dev->struct_mutex);
+ }
+
+ return 0;
+}
+
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ if (!file_priv->master)
+ return -EINVAL;
+ mutex_lock(&dev->struct_mutex);
+ drm_master_put(&file_priv->minor->master);
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
const struct pci_device_id *ent,
struct drm_driver *driver)
@@ -92,7 +198,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
spin_lock_init(&dev->count_lock);
spin_lock_init(&dev->drw_lock);
- spin_lock_init(&dev->lock.spinlock);
init_timer(&dev->timer);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
@@ -140,9 +245,6 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
}
}
- if (dev->driver->load)
- if ((retcode = dev->driver->load(dev, ent->driver_data)))
- goto error_out_unreg;
retcode = drm_ctxbitmap_init(dev);
if (retcode) {
@@ -200,6 +302,7 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
new_minor->device = MKDEV(DRM_MAJOR, minor_id);
new_minor->dev = dev;
new_minor->index = minor_id;
+ INIT_LIST_HEAD(&new_minor->master_list);
idr_replace(&drm_minors_idr, new_minor, minor_id);
@@ -267,8 +370,30 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
goto err_g2;
}
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_g2;
+ }
+
if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
- goto err_g2;
+ goto err_g3;
+
+ if (dev->driver->load) {
+ ret = dev->driver->load(dev, ent->driver_data);
+ if (ret)
+ goto err_g3;
+ }
+
+ /* setup the grouping for the legacy output */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+ if (ret)
+ goto err_g3;
+ }
+
+ list_add_tail(&dev->driver_item, &driver->device_list);
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel,
@@ -276,6 +401,8 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
return 0;
+err_g3:
+ drm_put_minor(&dev->primary);
err_g2:
pci_disable_device(pdev);
err_g1:
@@ -297,11 +424,6 @@ int drm_put_dev(struct drm_device * dev)
{
DRM_DEBUG("release primary %s\n", dev->driver->pci_driver.name);
- if (dev->unique) {
- drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
- dev->unique = NULL;
- dev->unique_len = 0;
- }
if (dev->devname) {
drm_free(dev->devname, strlen(dev->devname) + 1,
DRM_MEM_DRIVER);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1611b9bcbe7f..65d72d094c81 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -20,6 +20,7 @@
#include "drmP.h"
#define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
+#define to_drm_connector(d) container_of(d, struct drm_connector, kdev)
/**
* drm_sysfs_suspend - DRM class suspend hook
@@ -34,7 +35,7 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
- if (drm_dev->driver->suspend)
+ if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->suspend)
return drm_dev->driver->suspend(drm_dev, state);
return 0;
@@ -52,7 +53,7 @@ static int drm_sysfs_resume(struct device *dev)
struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
- if (drm_dev->driver->resume)
+ if (drm_minor->type == DRM_MINOR_LEGACY && drm_dev->driver->resume)
return drm_dev->driver->resume(drm_dev);
return 0;
@@ -144,6 +145,323 @@ static void drm_sysfs_device_release(struct device *dev)
return;
}
+/*
+ * Connector properties
+ */
+static ssize_t status_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ enum drm_connector_status status;
+
+ status = connector->funcs->detect(connector);
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_connector_status_name(status));
+}
+
+static ssize_t dpms_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ uint64_t dpms_status;
+ int ret;
+
+ ret = drm_connector_property_get_value(connector,
+ dev->mode_config.dpms_property,
+ &dpms_status);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s",
+ drm_get_dpms_name((int)dpms_status));
+}
+
+static ssize_t enabled_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+
+ return snprintf(buf, PAGE_SIZE, connector->encoder ? "enabled" :
+ "disabled");
+}
+
+static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *connector_dev = container_of(kobj, struct device, kobj);
+ struct drm_connector *connector = to_drm_connector(connector_dev);
+ unsigned char *edid;
+ size_t size;
+
+ if (!connector->edid_blob_ptr)
+ return 0;
+
+ edid = connector->edid_blob_ptr->data;
+ size = connector->edid_blob_ptr->length;
+ if (!edid)
+ return 0;
+
+ if (off >= size)
+ return 0;
+
+ if (off + count > size)
+ count = size - off;
+ memcpy(buf, edid + off, count);
+
+ return count;
+}
+
+static ssize_t modes_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_display_mode *mode;
+ int written = 0;
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ written += snprintf(buf + written, PAGE_SIZE - written, "%s\n",
+ mode->name);
+ }
+
+ return written;
+}
+
+static ssize_t subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int is_tv = 0;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_subconnector_property;
+ is_tv = 1;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
+ drm_get_tv_subconnector_name((int)subconnector) :
+ drm_get_dvi_i_subconnector_name((int)subconnector));
+}
+
+static ssize_t select_subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = to_drm_connector(device);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int is_tv = 0;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_select_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_select_subconnector_property;
+ is_tv = 1;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find select subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", is_tv ?
+ drm_get_tv_select_name((int)subconnector) :
+ drm_get_dvi_i_select_name((int)subconnector));
+}
+
+static struct device_attribute connector_attrs[] = {
+ __ATTR_RO(status),
+ __ATTR_RO(enabled),
+ __ATTR_RO(dpms),
+ __ATTR_RO(modes),
+};
+
+/* These attributes are for both DVI-I connectors and all types of tv-out. */
+static struct device_attribute connector_attrs_opt1[] = {
+ __ATTR_RO(subconnector),
+ __ATTR_RO(select_subconnector),
+};
+
+static struct bin_attribute edid_attr = {
+ .attr.name = "edid",
+ .size = 128,
+ .read = edid_show,
+};
+
+/**
+ * drm_sysfs_connector_add - add an connector to sysfs
+ * @connector: connector to add
+ *
+ * Create an connector device in sysfs, along with its associated connector
+ * properties (so far, connection status, dpms, mode list & edid) and
+ * generate a hotplug event so userspace knows there's a new connector
+ * available.
+ *
+ * Note:
+ * This routine should only be called *once* for each DRM minor registered.
+ * A second call for an already registered device will trigger the BUG_ON
+ * below.
+ */
+int drm_sysfs_connector_add(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ int ret = 0, i, j;
+
+ /* We shouldn't get called more than once for the same connector */
+ BUG_ON(device_is_registered(&connector->kdev));
+
+ connector->kdev.parent = &dev->primary->kdev;
+ connector->kdev.class = drm_class;
+ connector->kdev.release = drm_sysfs_device_release;
+
+ DRM_DEBUG("adding \"%s\" to sysfs\n",
+ drm_get_connector_name(connector));
+
+ snprintf(connector->kdev.bus_id, BUS_ID_SIZE, "card%d-%s",
+ dev->primary->index, drm_get_connector_name(connector));
+ ret = device_register(&connector->kdev);
+
+ if (ret) {
+ DRM_ERROR("failed to register connector device: %d\n", ret);
+ goto out;
+ }
+
+ /* Standard attributes */
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs[i]);
+ if (ret)
+ goto err_out_files;
+ }
+
+ /* Optional attributes */
+ /*
+ * In the long run it maybe a good idea to make one set of
+ * optionals per connector type.
+ */
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
+ if (ret)
+ goto err_out_files;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
+ if (ret)
+ goto err_out_files;
+
+ /* Let userspace know we have a new connector */
+ drm_sysfs_hotplug_event(dev);
+
+ return 0;
+
+err_out_files:
+ if (i > 0)
+ for (j = 0; j < i; j++)
+ device_remove_file(&connector->kdev,
+ &connector_attrs[i]);
+ device_unregister(&connector->kdev);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(drm_sysfs_connector_add);
+
+/**
+ * drm_sysfs_connector_remove - remove an connector device from sysfs
+ * @connector: connector to remove
+ *
+ * Remove @connector and its associated attributes from sysfs. Note that
+ * the device model core will take care of sending the "remove" uevent
+ * at this time, so we don't need to do it.
+ *
+ * Note:
+ * This routine should only be called if the connector was previously
+ * successfully registered. If @connector hasn't been registered yet,
+ * you'll likely see a panic somewhere deep in sysfs code when called.
+ */
+void drm_sysfs_connector_remove(struct drm_connector *connector)
+{
+ int i;
+
+ DRM_DEBUG("removing \"%s\" from sysfs\n",
+ drm_get_connector_name(connector));
+
+ for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
+ device_remove_file(&connector->kdev, &connector_attrs[i]);
+ sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
+ device_unregister(&connector->kdev);
+}
+EXPORT_SYMBOL(drm_sysfs_connector_remove);
+
+/**
+ * drm_sysfs_hotplug_event - generate a DRM uevent
+ * @dev: DRM device
+ *
+ * Send a uevent for the DRM device specified by @dev. Currently we only
+ * set HOTPLUG=1 in the uevent environment, but this could be expanded to
+ * deal with other types of events.
+ */
+void drm_sysfs_hotplug_event(struct drm_device *dev)
+{
+ char *event_string = "HOTPLUG=1";
+ char *envp[] = { event_string, NULL };
+
+ DRM_DEBUG("generating hotplug event\n");
+
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
+}
+
/**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver
* @dev: DRM device to be added
@@ -163,7 +481,12 @@ int drm_sysfs_device_add(struct drm_minor *minor)
minor->kdev.class = drm_class;
minor->kdev.release = drm_sysfs_device_release;
minor->kdev.devt = minor->device;
- minor_str = "card%d";
+ if (minor->type == DRM_MINOR_CONTROL)
+ minor_str = "controlD%d";
+ else if (minor->type == DRM_MINOR_RENDER)
+ minor_str = "renderD%d";
+ else
+ minor_str = "card%d";
snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index c234c6f24a8d..3ffae021d280 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -267,6 +267,9 @@ static void drm_vm_shm_close(struct vm_area_struct *vma)
dmah.size = map->size;
__drm_pci_free(dev, &dmah);
break;
+ case _DRM_GEM:
+ DRM_ERROR("tried to rmmap GEM object\n");
+ break;
}
drm_free(map, sizeof(*map), DRM_MEM_MAPS);
}
@@ -399,7 +402,7 @@ static struct vm_operations_struct drm_vm_sg_ops = {
* Create a new drm_vma_entry structure as the \p vma private data entry and
* add it to drm_device::vmalist.
*/
-static void drm_vm_open_locked(struct vm_area_struct *vma)
+void drm_vm_open_locked(struct vm_area_struct *vma)
{
struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->minor->dev;
@@ -540,7 +543,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs);
* according to the mapping type and remaps the pages. Finally sets the file
* pointer and calls vm_open().
*/
-static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
+int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d8fb5d8ee7ea..dd57a5bd4572 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -8,7 +8,22 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
i915_gem.o \
i915_gem_debug.o \
i915_gem_proc.o \
- i915_gem_tiling.o
+ i915_gem_tiling.o \
+ intel_display.o \
+ intel_crt.o \
+ intel_lvds.o \
+ intel_bios.o \
+ intel_sdvo.o \
+ intel_modes.o \
+ intel_i2c.o \
+ intel_fb.o \
+ intel_tv.o \
+ intel_dvo.o \
+ dvo_ch7xxx.o \
+ dvo_ch7017.o \
+ dvo_ivch.o \
+ dvo_tfp410.o \
+ dvo_sil164.o
i915-$(CONFIG_ACPI) += i915_opregion.o
i915-$(CONFIG_COMPAT) += i915_ioc32.o
diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h
new file mode 100644
index 000000000000..e747ac42fe3a
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2006 Eric Anholt
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _INTEL_DVO_H
+#define _INTEL_DVO_H
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+
+struct intel_dvo_device {
+ char *name;
+ int type;
+ /* DVOA/B/C output register */
+ u32 dvo_reg;
+ /* GPIO register used for i2c bus to control this device */
+ u32 gpio;
+ int slave_addr;
+ struct intel_i2c_chan *i2c_bus;
+
+ const struct intel_dvo_dev_ops *dev_ops;
+ void *dev_priv;
+
+ struct drm_display_mode *panel_fixed_mode;
+ bool panel_wants_dither;
+};
+
+struct intel_dvo_dev_ops {
+ /*
+ * Initialize the device at startup time.
+ * Returns NULL if the device does not exist.
+ */
+ bool (*init)(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus);
+
+ /*
+ * Called to allow the output a chance to create properties after the
+ * RandR objects have been created.
+ */
+ void (*create_resources)(struct intel_dvo_device *dvo);
+
+ /*
+ * Turn on/off output or set intermediate power levels if available.
+ *
+ * Unsupported intermediate modes drop to the lower power setting.
+ * If the mode is DPMSModeOff, the output must be disabled,
+ * as the DPLL may be disabled afterwards.
+ */
+ void (*dpms)(struct intel_dvo_device *dvo, int mode);
+
+ /*
+ * Saves the output's state for restoration on VT switch.
+ */
+ void (*save)(struct intel_dvo_device *dvo);
+
+ /*
+ * Restore's the output's state at VT switch.
+ */
+ void (*restore)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for testing a video mode for a given output.
+ *
+ * This function should only check for cases where a mode can't
+ * be supported on the output specifically, and not represent
+ * generic CRTC limitations.
+ *
+ * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
+ */
+ int (*mode_valid)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode);
+
+ /*
+ * Callback to adjust the mode to be set in the CRTC.
+ *
+ * This allows an output to adjust the clock or even the entire set of
+ * timings, which is used for panels with fixed timings or for
+ * buses with clock limitations.
+ */
+ bool (*mode_fixup)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Callback for preparing mode changes on an output
+ */
+ void (*prepare)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for committing mode changes on an output
+ */
+ void (*commit)(struct intel_dvo_device *dvo);
+
+ /*
+ * Callback for setting up a video mode after fixups have been made.
+ *
+ * This is only called while the output is disabled. The dpms callback
+ * must be all that's necessary for the output, to turn the output on
+ * after this function is called.
+ */
+ void (*mode_set)(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+
+ /*
+ * Probe for a connected output, and return detect_status.
+ */
+ enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
+
+ /**
+ * Query the device for the modes it provides.
+ *
+ * This function may also update MonInfo, mm_width, and mm_height.
+ *
+ * \return singly-linked list of modes or NULL if no modes found.
+ */
+ struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo);
+
+ /**
+ * Clean up driver-specific bits of the output
+ */
+ void (*destroy) (struct intel_dvo_device *dvo);
+
+ /**
+ * Debugging hook to dump device registers to log file
+ */
+ void (*dump_regs)(struct intel_dvo_device *dvo);
+};
+
+extern struct intel_dvo_dev_ops sil164_ops;
+extern struct intel_dvo_dev_ops ch7xxx_ops;
+extern struct intel_dvo_dev_ops ivch_ops;
+extern struct intel_dvo_dev_ops tfp410_ops;
+extern struct intel_dvo_dev_ops ch7017_ops;
+
+#endif /* _INTEL_DVO_H */
diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
new file mode 100644
index 000000000000..03d4b4973b02
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ch7017.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+#define CH7017_TV_DISPLAY_MODE 0x00
+#define CH7017_FLICKER_FILTER 0x01
+#define CH7017_VIDEO_BANDWIDTH 0x02
+#define CH7017_TEXT_ENHANCEMENT 0x03
+#define CH7017_START_ACTIVE_VIDEO 0x04
+#define CH7017_HORIZONTAL_POSITION 0x05
+#define CH7017_VERTICAL_POSITION 0x06
+#define CH7017_BLACK_LEVEL 0x07
+#define CH7017_CONTRAST_ENHANCEMENT 0x08
+#define CH7017_TV_PLL 0x09
+#define CH7017_TV_PLL_M 0x0a
+#define CH7017_TV_PLL_N 0x0b
+#define CH7017_SUB_CARRIER_0 0x0c
+#define CH7017_CIV_CONTROL 0x10
+#define CH7017_CIV_0 0x11
+#define CH7017_CHROMA_BOOST 0x14
+#define CH7017_CLOCK_MODE 0x1c
+#define CH7017_INPUT_CLOCK 0x1d
+#define CH7017_GPIO_CONTROL 0x1e
+#define CH7017_INPUT_DATA_FORMAT 0x1f
+#define CH7017_CONNECTION_DETECT 0x20
+#define CH7017_DAC_CONTROL 0x21
+#define CH7017_BUFFERED_CLOCK_OUTPUT 0x22
+#define CH7017_DEFEAT_VSYNC 0x47
+#define CH7017_TEST_PATTERN 0x48
+
+#define CH7017_POWER_MANAGEMENT 0x49
+/** Enables the TV output path. */
+#define CH7017_TV_EN (1 << 0)
+#define CH7017_DAC0_POWER_DOWN (1 << 1)
+#define CH7017_DAC1_POWER_DOWN (1 << 2)
+#define CH7017_DAC2_POWER_DOWN (1 << 3)
+#define CH7017_DAC3_POWER_DOWN (1 << 4)
+/** Powers down the TV out block, and DAC0-3 */
+#define CH7017_TV_POWER_DOWN_EN (1 << 5)
+
+#define CH7017_VERSION_ID 0x4a
+
+#define CH7017_DEVICE_ID 0x4b
+#define CH7017_DEVICE_ID_VALUE 0x1b
+#define CH7018_DEVICE_ID_VALUE 0x1a
+#define CH7019_DEVICE_ID_VALUE 0x19
+
+#define CH7017_XCLK_D2_ADJUST 0x53
+#define CH7017_UP_SCALER_COEFF_0 0x55
+#define CH7017_UP_SCALER_COEFF_1 0x56
+#define CH7017_UP_SCALER_COEFF_2 0x57
+#define CH7017_UP_SCALER_COEFF_3 0x58
+#define CH7017_UP_SCALER_COEFF_4 0x59
+#define CH7017_UP_SCALER_VERTICAL_INC_0 0x5a
+#define CH7017_UP_SCALER_VERTICAL_INC_1 0x5b
+#define CH7017_GPIO_INVERT 0x5c
+#define CH7017_UP_SCALER_HORIZONTAL_INC_0 0x5d
+#define CH7017_UP_SCALER_HORIZONTAL_INC_1 0x5e
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT 0x5f
+/**< Low bits of horizontal active pixel input */
+
+#define CH7017_ACTIVE_INPUT_LINE_OUTPUT 0x60
+/** High bits of horizontal active pixel input */
+#define CH7017_LVDS_HAP_INPUT_MASK (0x7 << 0)
+/** High bits of vertical active line output */
+#define CH7017_LVDS_VAL_HIGH_MASK (0x7 << 3)
+
+#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT 0x61
+/**< Low bits of vertical active line output */
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT 0x62
+/**< Low bits of horizontal active pixel output */
+
+#define CH7017_LVDS_POWER_DOWN 0x63
+/** High bits of horizontal active pixel output */
+#define CH7017_LVDS_HAP_HIGH_MASK (0x7 << 0)
+/** Enables the LVDS power down state transition */
+#define CH7017_LVDS_POWER_DOWN_EN (1 << 6)
+/** Enables the LVDS upscaler */
+#define CH7017_LVDS_UPSCALER_EN (1 << 7)
+#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
+
+#define CH7017_LVDS_ENCODING 0x64
+#define CH7017_LVDS_DITHER_2D (1 << 2)
+#define CH7017_LVDS_DITHER_DIS (1 << 3)
+#define CH7017_LVDS_DUAL_CHANNEL_EN (1 << 4)
+#define CH7017_LVDS_24_BIT (1 << 5)
+
+#define CH7017_LVDS_ENCODING_2 0x65
+
+#define CH7017_LVDS_PLL_CONTROL 0x66
+/** Enables the LVDS panel output path */
+#define CH7017_LVDS_PANEN (1 << 0)
+/** Enables the LVDS panel backlight */
+#define CH7017_LVDS_BKLEN (1 << 3)
+
+#define CH7017_POWER_SEQUENCING_T1 0x67
+#define CH7017_POWER_SEQUENCING_T2 0x68
+#define CH7017_POWER_SEQUENCING_T3 0x69
+#define CH7017_POWER_SEQUENCING_T4 0x6a
+#define CH7017_POWER_SEQUENCING_T5 0x6b
+#define CH7017_GPIO_DRIVER_TYPE 0x6c
+#define CH7017_GPIO_DATA 0x6d
+#define CH7017_GPIO_DIRECTION_CONTROL 0x6e
+
+#define CH7017_LVDS_PLL_FEEDBACK_DIV 0x71
+# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
+# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
+# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
+
+#define CH7017_LVDS_PLL_VCO_CONTROL 0x72
+# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
+# define CH7017_LVDS_PLL_VCO_SHIFT 4
+# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
+
+#define CH7017_OUTPUTS_ENABLE 0x73
+# define CH7017_CHARGE_PUMP_LOW 0x0
+# define CH7017_CHARGE_PUMP_HIGH 0x3
+# define CH7017_LVDS_CHANNEL_A (1 << 3)
+# define CH7017_LVDS_CHANNEL_B (1 << 4)
+# define CH7017_TV_DAC_A (1 << 5)
+# define CH7017_TV_DAC_B (1 << 6)
+# define CH7017_DDC_SELECT_DC2 (1 << 7)
+
+#define CH7017_LVDS_OUTPUT_AMPLITUDE 0x74
+#define CH7017_LVDS_PLL_EMI_REDUCTION 0x75
+#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
+
+#define CH7017_LVDS_CONTROL_2 0x78
+# define CH7017_LOOP_FILTER_SHIFT 5
+# define CH7017_PHASE_DETECTOR_SHIFT 0
+
+#define CH7017_BANG_LIMIT_CONTROL 0x7f
+
+struct ch7017_priv {
+ uint8_t save_hapi;
+ uint8_t save_vali;
+ uint8_t save_valo;
+ uint8_t save_ailo;
+ uint8_t save_lvds_pll_vco;
+ uint8_t save_feedback_div;
+ uint8_t save_lvds_control_2;
+ uint8_t save_outputs_enable;
+ uint8_t save_lvds_power_down;
+ uint8_t save_power_management;
+};
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo);
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
+
+static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *val= in_buf[0];
+ return true;
+ };
+
+ return false;
+}
+
+static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
+{
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = val;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ return false;
+}
+
+/** Probes for a CH7017 on the given bus and slave address. */
+static bool ch7017_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ch7017_priv *priv;
+ uint8_t val;
+
+ priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+
+ if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
+ goto fail;
+
+ if (val != CH7017_DEVICE_ID_VALUE &&
+ val != CH7018_DEVICE_ID_VALUE &&
+ val != CH7019_DEVICE_ID_VALUE) {
+ DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
+ val, i2cbus->adapter.name,i2cbus->slave_addr);
+ goto fail;
+ }
+
+ return true;
+fail:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_unknown;
+}
+
+static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 160000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7017_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
+ uint8_t outputs_enable, lvds_control_2, lvds_power_down;
+ uint8_t horizontal_active_pixel_input;
+ uint8_t horizontal_active_pixel_output, vertical_active_line_output;
+ uint8_t active_input_line_output;
+
+ DRM_DEBUG("Registers before mode setting\n");
+ ch7017_dump_regs(dvo);
+
+ /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
+ if (mode->clock < 100000) {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ } else {
+ outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
+ lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+ (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+ lvds_pll_feedback_div = 35;
+ lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
+ (0 << CH7017_PHASE_DETECTOR_SHIFT);
+ if (1) { /* XXX: dual channel panel detection. Assume yes for now. */
+ outputs_enable |= CH7017_LVDS_CHANNEL_B;
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ } else {
+ lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+ (1 << CH7017_LVDS_PLL_VCO_SHIFT) |
+ (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+ }
+ }
+
+ horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
+
+ vertical_active_line_output = mode->vdisplay & 0x00ff;
+ horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
+
+ active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
+ (((mode->vdisplay & 0x0700) >> 8) << 3);
+
+ lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
+ (mode->hdisplay & 0x0700) >> 8;
+
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
+ horizontal_active_pixel_input);
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
+ horizontal_active_pixel_output);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
+ vertical_active_line_output);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
+ active_input_line_output);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
+
+ /* Turn the LVDS back on with new settings. */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
+
+ DRM_DEBUG("Registers after mode setting\n");
+ ch7017_dump_regs(dvo);
+}
+
+/* set the CH7017 power state */
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t val;
+
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
+
+ /* Turn off TV/VGA, and never turn it on since we don't support it. */
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
+ CH7017_DAC0_POWER_DOWN |
+ CH7017_DAC1_POWER_DOWN |
+ CH7017_DAC2_POWER_DOWN |
+ CH7017_DAC3_POWER_DOWN |
+ CH7017_TV_POWER_DOWN_EN);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ /* Turn on the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val & ~CH7017_LVDS_POWER_DOWN_EN);
+ } else {
+ /* Turn off the LVDS */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+ val | CH7017_LVDS_POWER_DOWN_EN);
+ }
+
+ /* XXX: Should actually wait for update power status somehow */
+ udelay(20000);
+}
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+#define DUMP(reg) \
+do { \
+ ch7017_read(dvo, reg, &val); \
+ DRM_DEBUG(#reg ": %02x\n", val); \
+} while (0)
+
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
+ DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
+ DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
+ DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
+ DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
+ DUMP(CH7017_LVDS_CONTROL_2);
+ DUMP(CH7017_OUTPUTS_ENABLE);
+ DUMP(CH7017_LVDS_POWER_DOWN);
+}
+
+static void ch7017_save(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi);
+ ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo);
+ ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo);
+ ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco);
+ ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div);
+ ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2);
+ ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable);
+ ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down);
+ ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management);
+}
+
+static void ch7017_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ /* Power down before changing mode */
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
+
+ ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
+ ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
+ ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo);
+ ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco);
+ ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div);
+ ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2);
+ ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable);
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down);
+ ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management);
+}
+
+static void ch7017_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7017_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7017_ops = {
+ .init = ch7017_init,
+ .detect = ch7017_detect,
+ .mode_valid = ch7017_mode_valid,
+ .mode_set = ch7017_mode_set,
+ .dpms = ch7017_dpms,
+ .dump_regs = ch7017_dump_regs,
+ .save = ch7017_save,
+ .restore = ch7017_restore,
+ .destroy = ch7017_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
new file mode 100644
index 000000000000..d2fd95dbd034
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -0,0 +1,368 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define CH7xxx_REG_VID 0x4a
+#define CH7xxx_REG_DID 0x4b
+
+#define CH7011_VID 0x83 /* 7010 as well */
+#define CH7009A_VID 0x84
+#define CH7009B_VID 0x85
+#define CH7301_VID 0x95
+
+#define CH7xxx_VID 0x84
+#define CH7xxx_DID 0x17
+
+#define CH7xxx_NUM_REGS 0x4c
+
+#define CH7xxx_CM 0x1c
+#define CH7xxx_CM_XCM (1<<0)
+#define CH7xxx_CM_MCP (1<<2)
+#define CH7xxx_INPUT_CLOCK 0x1d
+#define CH7xxx_GPIO 0x1e
+#define CH7xxx_GPIO_HPIR (1<<3)
+#define CH7xxx_IDF 0x1f
+
+#define CH7xxx_IDF_HSP (1<<3)
+#define CH7xxx_IDF_VSP (1<<4)
+
+#define CH7xxx_CONNECTION_DETECT 0x20
+#define CH7xxx_CDET_DVI (1<<5)
+
+#define CH7301_DAC_CNTL 0x21
+#define CH7301_HOTPLUG 0x23
+#define CH7xxx_TCTL 0x31
+#define CH7xxx_TVCO 0x32
+#define CH7xxx_TPCP 0x33
+#define CH7xxx_TPD 0x34
+#define CH7xxx_TPVT 0x35
+#define CH7xxx_TLPF 0x36
+#define CH7xxx_TCT 0x37
+#define CH7301_TEST_PATTERN 0x48
+
+#define CH7xxx_PM 0x49
+#define CH7xxx_PM_FPD (1<<0)
+#define CH7301_PM_DACPD0 (1<<1)
+#define CH7301_PM_DACPD1 (1<<2)
+#define CH7301_PM_DACPD2 (1<<3)
+#define CH7xxx_PM_DVIL (1<<6)
+#define CH7xxx_PM_DVIP (1<<7)
+
+#define CH7301_SYNC_POLARITY 0x56
+#define CH7301_SYNC_RGB_YUV (1<<0)
+#define CH7301_SYNC_POL_DVI (1<<5)
+
+/** @file
+ * driver for the Chrontel 7xxx DVI chip over DVO.
+ */
+
+static struct ch7xxx_id_struct {
+ uint8_t vid;
+ char *name;
+} ch7xxx_ids[] = {
+ { CH7011_VID, "CH7011" },
+ { CH7009A_VID, "CH7009A" },
+ { CH7009B_VID, "CH7009B" },
+ { CH7301_VID, "CH7301" },
+};
+
+struct ch7xxx_reg_state {
+ uint8_t regs[CH7xxx_NUM_REGS];
+};
+
+struct ch7xxx_priv {
+ bool quiet;
+
+ struct ch7xxx_reg_state save_reg;
+ struct ch7xxx_reg_state mode_reg;
+ uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
+ uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
+};
+
+static void ch7xxx_save(struct intel_dvo_device *dvo);
+
+static char *ch7xxx_get_id(uint8_t vid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
+ if (ch7xxx_ids[i].vid == vid)
+ return ch7xxx_ids[i].name;
+ }
+
+ return NULL;
+}
+
+/** Reads an 8 bit register */
+static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes an 8 bit register */
+static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!ch7xxx->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static bool ch7xxx_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the CH7xxx chip on the specified i2c bus */
+ struct ch7xxx_priv *ch7xxx;
+ uint8_t vendor, device;
+ char *name;
+
+ ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
+ if (ch7xxx == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = ch7xxx;
+ ch7xxx->quiet = true;
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
+ goto out;
+
+ name = ch7xxx_get_id(vendor);
+ if (!name) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+
+ if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
+ goto out;
+
+ if (device != CH7xxx_DID) {
+ DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+ vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ ch7xxx->quiet = false;
+ DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+ name, vendor, device);
+ return true;
+out:
+ kfree(ch7xxx);
+ return false;
+}
+
+static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t cdet, orig_pm, pm;
+
+ ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
+
+ pm = orig_pm;
+ pm &= ~CH7xxx_PM_FPD;
+ pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, pm);
+
+ ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
+
+ ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
+
+ if (cdet & CH7xxx_CDET_DVI)
+ return connector_status_connected;
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 165000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint8_t tvco, tpcp, tpd, tlpf, idf;
+
+ if (mode->clock <= 65000) {
+ tvco = 0x23;
+ tpcp = 0x08;
+ tpd = 0x16;
+ tlpf = 0x60;
+ } else {
+ tvco = 0x2d;
+ tpcp = 0x06;
+ tpd = 0x26;
+ tlpf = 0xa0;
+ }
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
+ ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
+ ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
+
+ ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
+
+ idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ idf |= CH7xxx_IDF_HSP;
+
+ ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
+}
+
+/* set the CH7xxx power state */
+static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ if (mode == DRM_MODE_DPMS_ON)
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
+ else
+ ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
+}
+
+static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+ int i;
+
+ for (i = 0; i < CH7xxx_NUM_REGS; i++) {
+ if ((i % 8) == 0 )
+ DRM_DEBUG("\n %02X: ", i);
+ DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
+ }
+}
+
+static void ch7xxx_save(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+
+ ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL);
+ ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP);
+ ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD);
+ ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT);
+ ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF);
+ ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM);
+ ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF);
+}
+
+static void ch7xxx_restore(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL);
+ ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP);
+ ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD);
+ ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT);
+ ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF);
+ ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF);
+ ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM);
+}
+
+static void ch7xxx_destroy(struct intel_dvo_device *dvo)
+{
+ struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+ if (ch7xxx) {
+ kfree(ch7xxx);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ch7xxx_ops = {
+ .init = ch7xxx_init,
+ .detect = ch7xxx_detect,
+ .mode_valid = ch7xxx_mode_valid,
+ .mode_set = ch7xxx_mode_set,
+ .dpms = ch7xxx_dpms,
+ .dump_regs = ch7xxx_dump_regs,
+ .save = ch7xxx_save,
+ .restore = ch7xxx_restore,
+ .destroy = ch7xxx_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
new file mode 100644
index 000000000000..0c8d375e8e37
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+/*
+ * register definitions for the i82807aa.
+ *
+ * Documentation on this chipset can be found in datasheet #29069001 at
+ * intel.com.
+ */
+
+/*
+ * VCH Revision & GMBus Base Addr
+ */
+#define VR00 0x00
+# define VR00_BASE_ADDRESS_MASK 0x007f
+
+/*
+ * Functionality Enable
+ */
+#define VR01 0x01
+
+/*
+ * Enable the panel fitter
+ */
+# define VR01_PANEL_FIT_ENABLE (1 << 3)
+/*
+ * Enables the LCD display.
+ *
+ * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
+ */
+# define VR01_LCD_ENABLE (1 << 2)
+/** Enables the DVO repeater. */
+# define VR01_DVO_BYPASS_ENABLE (1 << 1)
+/** Enables the DVO clock */
+# define VR01_DVO_ENABLE (1 << 0)
+
+/*
+ * LCD Interface Format
+ */
+#define VR10 0x10
+/** Enables LVDS output instead of CMOS */
+# define VR10_LVDS_ENABLE (1 << 4)
+/** Enables 18-bit LVDS output. */
+# define VR10_INTERFACE_1X18 (0 << 2)
+/** Enables 24-bit LVDS or CMOS output */
+# define VR10_INTERFACE_1X24 (1 << 2)
+/** Enables 2x18-bit LVDS or CMOS output. */
+# define VR10_INTERFACE_2X18 (2 << 2)
+/** Enables 2x24-bit LVDS output */
+# define VR10_INTERFACE_2X24 (3 << 2)
+
+/*
+ * VR20 LCD Horizontal Display Size
+ */
+#define VR20 0x20
+
+/*
+ * LCD Vertical Display Size
+ */
+#define VR21 0x20
+
+/*
+ * Panel power down status
+ */
+#define VR30 0x30
+/** Read only bit indicating that the panel is not in a safe poweroff state. */
+# define VR30_PANEL_ON (1 << 15)
+
+#define VR40 0x40
+# define VR40_STALL_ENABLE (1 << 13)
+# define VR40_VERTICAL_INTERP_ENABLE (1 << 12)
+# define VR40_ENHANCED_PANEL_FITTING (1 << 11)
+# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
+# define VR40_AUTO_RATIO_ENABLE (1 << 9)
+# define VR40_CLOCK_GATING_ENABLE (1 << 8)
+
+/*
+ * Panel Fitting Vertical Ratio
+ * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
+ */
+#define VR41 0x41
+
+/*
+ * Panel Fitting Horizontal Ratio
+ * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
+ */
+#define VR42 0x42
+
+/*
+ * Horizontal Image Size
+ */
+#define VR43 0x43
+
+/* VR80 GPIO 0
+ */
+#define VR80 0x80
+#define VR81 0x81
+#define VR82 0x82
+#define VR83 0x83
+#define VR84 0x84
+#define VR85 0x85
+#define VR86 0x86
+#define VR87 0x87
+
+/* VR88 GPIO 8
+ */
+#define VR88 0x88
+
+/* Graphics BIOS scratch 0
+ */
+#define VR8E 0x8E
+# define VR8E_PANEL_TYPE_MASK (0xf << 0)
+# define VR8E_PANEL_INTERFACE_CMOS (0 << 4)
+# define VR8E_PANEL_INTERFACE_LVDS (1 << 4)
+# define VR8E_FORCE_DEFAULT_PANEL (1 << 5)
+
+/* Graphics BIOS scratch 1
+ */
+#define VR8F 0x8F
+# define VR8F_VCH_PRESENT (1 << 0)
+# define VR8F_DISPLAY_CONN (1 << 1)
+# define VR8F_POWER_MASK (0x3c)
+# define VR8F_POWER_POS (2)
+
+
+struct ivch_priv {
+ bool quiet;
+
+ uint16_t width, height;
+
+ uint16_t save_VR01;
+ uint16_t save_VR40;
+};
+
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo);
+
+/**
+ * Reads a register on the ivch.
+ *
+ * Each of the 256 registers are 16 bits long.
+ */
+static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[1];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 0,
+ },
+ {
+ .addr = 0,
+ .flags = I2C_M_NOSTART,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD | I2C_M_NOSTART,
+ .len = 2,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) {
+ *data = (in_buf[1] << 8) | in_buf[0];
+ return true;
+ };
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+/** Writes a 16-bit register on the ivch */
+static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[3];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 3,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = data & 0xff;
+ out_buf[2] = data >> 8;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!priv->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/** Probes the given bus and slave address for an ivch */
+static bool ivch_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ struct ivch_priv *priv;
+ uint16_t temp;
+
+ priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = priv;
+ priv->quiet = true;
+
+ if (!ivch_read(dvo, VR00, &temp))
+ goto out;
+ priv->quiet = false;
+
+ /* Since the identification bits are probably zeroes, which doesn't seem
+ * very unique, check that the value in the base address field matches
+ * the address it's responding on.
+ */
+ if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
+ DRM_DEBUG("ivch detect failed due to address mismatch "
+ "(%d vs %d)\n",
+ (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
+ goto out;
+ }
+
+ ivch_read(dvo, VR20, &priv->width);
+ ivch_read(dvo, VR21, &priv->height);
+
+ return true;
+
+out:
+ kfree(priv);
+ return false;
+}
+
+static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
+{
+ return connector_status_connected;
+}
+
+static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock > 112000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+/** Sets the power state of the panel connected to the ivch */
+static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int i;
+ uint16_t vr01, vr30, backlight;
+
+ /* Set the new power state of the panel. */
+ if (!ivch_read(dvo, VR01, &vr01))
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ backlight = 1;
+ else
+ backlight = 0;
+ ivch_write(dvo, VR80, backlight);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
+ else
+ vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
+
+ ivch_write(dvo, VR01, vr01);
+
+ /* Wait for the panel to make its state transition */
+ for (i = 0; i < 100; i++) {
+ if (!ivch_read(dvo, VR30, &vr30))
+ break;
+
+ if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
+ break;
+ udelay(1000);
+ }
+ /* wait some more; vch may fail to resync sometimes without this */
+ udelay(16 * 1000);
+}
+
+static void ivch_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ uint16_t vr40 = 0;
+ uint16_t vr01;
+
+ vr01 = 0;
+ vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
+ VR40_HORIZONTAL_INTERP_ENABLE);
+
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay) {
+ uint16_t x_ratio, y_ratio;
+
+ vr01 |= VR01_PANEL_FIT_ENABLE;
+ vr40 |= VR40_CLOCK_GATING_ENABLE;
+ x_ratio = (((mode->hdisplay - 1) << 16) /
+ (adjusted_mode->hdisplay - 1)) >> 2;
+ y_ratio = (((mode->vdisplay - 1) << 16) /
+ (adjusted_mode->vdisplay - 1)) >> 2;
+ ivch_write (dvo, VR42, x_ratio);
+ ivch_write (dvo, VR41, y_ratio);
+ } else {
+ vr01 &= ~VR01_PANEL_FIT_ENABLE;
+ vr40 &= ~VR40_CLOCK_GATING_ENABLE;
+ }
+ vr40 &= ~VR40_AUTO_RATIO_ENABLE;
+
+ ivch_write(dvo, VR01, vr01);
+ ivch_write(dvo, VR40, vr40);
+
+ ivch_dump_regs(dvo);
+}
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint16_t val;
+
+ ivch_read(dvo, VR00, &val);
+ DRM_DEBUG("VR00: 0x%04x\n", val);
+ ivch_read(dvo, VR01, &val);
+ DRM_DEBUG("VR01: 0x%04x\n", val);
+ ivch_read(dvo, VR30, &val);
+ DRM_DEBUG("VR30: 0x%04x\n", val);
+ ivch_read(dvo, VR40, &val);
+ DRM_DEBUG("VR40: 0x%04x\n", val);
+
+ /* GPIO registers */
+ ivch_read(dvo, VR80, &val);
+ DRM_DEBUG("VR80: 0x%04x\n", val);
+ ivch_read(dvo, VR81, &val);
+ DRM_DEBUG("VR81: 0x%04x\n", val);
+ ivch_read(dvo, VR82, &val);
+ DRM_DEBUG("VR82: 0x%04x\n", val);
+ ivch_read(dvo, VR83, &val);
+ DRM_DEBUG("VR83: 0x%04x\n", val);
+ ivch_read(dvo, VR84, &val);
+ DRM_DEBUG("VR84: 0x%04x\n", val);
+ ivch_read(dvo, VR85, &val);
+ DRM_DEBUG("VR85: 0x%04x\n", val);
+ ivch_read(dvo, VR86, &val);
+ DRM_DEBUG("VR86: 0x%04x\n", val);
+ ivch_read(dvo, VR87, &val);
+ DRM_DEBUG("VR87: 0x%04x\n", val);
+ ivch_read(dvo, VR88, &val);
+ DRM_DEBUG("VR88: 0x%04x\n", val);
+
+ /* Scratch register 0 - AIM Panel type */
+ ivch_read(dvo, VR8E, &val);
+ DRM_DEBUG("VR8E: 0x%04x\n", val);
+
+ /* Scratch register 1 - Status register */
+ ivch_read(dvo, VR8F, &val);
+ DRM_DEBUG("VR8F: 0x%04x\n", val);
+}
+
+static void ivch_save(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_read(dvo, VR01, &priv->save_VR01);
+ ivch_read(dvo, VR40, &priv->save_VR40);
+}
+
+static void ivch_restore(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ ivch_write(dvo, VR01, priv->save_VR01);
+ ivch_write(dvo, VR40, priv->save_VR40);
+}
+
+static void ivch_destroy(struct intel_dvo_device *dvo)
+{
+ struct ivch_priv *priv = dvo->dev_priv;
+
+ if (priv) {
+ kfree(priv);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops ivch_ops= {
+ .init = ivch_init,
+ .dpms = ivch_dpms,
+ .save = ivch_save,
+ .restore = ivch_restore,
+ .mode_valid = ivch_mode_valid,
+ .mode_set = ivch_mode_set,
+ .detect = ivch_detect,
+ .dump_regs = ivch_dump_regs,
+ .destroy = ivch_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
new file mode 100644
index 000000000000..033a4bb070b2
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -0,0 +1,302 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define SIL164_VID 0x0001
+#define SIL164_DID 0x0006
+
+#define SIL164_VID_LO 0x00
+#define SIL164_VID_HI 0x01
+#define SIL164_DID_LO 0x02
+#define SIL164_DID_HI 0x03
+#define SIL164_REV 0x04
+#define SIL164_RSVD 0x05
+#define SIL164_FREQ_LO 0x06
+#define SIL164_FREQ_HI 0x07
+
+#define SIL164_REG8 0x08
+#define SIL164_8_VEN (1<<5)
+#define SIL164_8_HEN (1<<4)
+#define SIL164_8_DSEL (1<<3)
+#define SIL164_8_BSEL (1<<2)
+#define SIL164_8_EDGE (1<<1)
+#define SIL164_8_PD (1<<0)
+
+#define SIL164_REG9 0x09
+#define SIL164_9_VLOW (1<<7)
+#define SIL164_9_MSEL_MASK (0x7<<4)
+#define SIL164_9_TSEL (1<<3)
+#define SIL164_9_RSEN (1<<2)
+#define SIL164_9_HTPLG (1<<1)
+#define SIL164_9_MDI (1<<0)
+
+#define SIL164_REGC 0x0c
+
+struct sil164_save_rec {
+ uint8_t reg8;
+ uint8_t reg9;
+ uint8_t regc;
+};
+
+struct sil164_priv {
+ //I2CDevRec d;
+ bool quiet;
+ struct sil164_save_rec save_regs;
+ struct sil164_save_rec mode_regs;
+};
+
+#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
+
+static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!sil->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+/* Silicon Image 164 driver for chip on i2c bus */
+static bool sil164_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the SIL164 chip on the specified i2c bus */
+ struct sil164_priv *sil;
+ unsigned char ch;
+
+ sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL);
+ if (sil == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = sil;
+ sil->quiet = true;
+
+ if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_VID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
+ goto out;
+
+ if (ch != (SIL164_DID & 0xff)) {
+ DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ sil->quiet = false;
+
+ DRM_DEBUG("init sil164 dvo controller successfully!\n");
+ return true;
+
+out:
+ kfree(sil);
+ return false;
+}
+
+static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo)
+{
+ uint8_t reg9;
+
+ sil164_readb(dvo, SIL164_REG9, &reg9);
+
+ if (reg9 & SIL164_9_HTPLG)
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void sil164_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock
+ * dependencies in the mode setup, we can just leave the
+ * registers alone and everything will work fine.
+ */
+ /* recommended programming sequence from doc */
+ /*sil164_writeb(sil, 0x08, 0x30);
+ sil164_writeb(sil, 0x09, 0x00);
+ sil164_writeb(sil, 0x0a, 0x90);
+ sil164_writeb(sil, 0x0c, 0x89);
+ sil164_writeb(sil, 0x08, 0x31);*/
+ /* don't do much */
+ return;
+}
+
+/* set the SIL164 power state */
+static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ int ret;
+ unsigned char ch;
+
+ ret = sil164_readb(dvo, SIL164_REG8, &ch);
+ if (ret == false)
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ ch |= SIL164_8_PD;
+ else
+ ch &= ~SIL164_8_PD;
+
+ sil164_writeb(dvo, SIL164_REG8, ch);
+ return;
+}
+
+static void sil164_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val;
+
+ sil164_readb(dvo, SIL164_FREQ_LO, &val);
+ DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_FREQ_HI, &val);
+ DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG8, &val);
+ DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG9, &val);
+ DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REGC, &val);
+ DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
+}
+
+static void sil164_save(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil= dvo->dev_priv;
+
+ if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9))
+ return;
+
+ if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc))
+ return;
+
+ return;
+}
+
+static void sil164_restore(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1);
+
+ sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9);
+ sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc);
+ sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8);
+}
+
+static void sil164_destroy(struct intel_dvo_device *dvo)
+{
+ struct sil164_priv *sil = dvo->dev_priv;
+
+ if (sil) {
+ kfree(sil);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops sil164_ops = {
+ .init = sil164_init,
+ .detect = sil164_detect,
+ .mode_valid = sil164_mode_valid,
+ .mode_set = sil164_mode_set,
+ .dpms = sil164_dpms,
+ .dump_regs = sil164_dump_regs,
+ .save = sil164_save,
+ .restore = sil164_restore,
+ .destroy = sil164_destroy,
+};
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
new file mode 100644
index 000000000000..207fda806ebf
--- /dev/null
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright © 2007 Dave Mueller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Dave Mueller <dave.mueller@gmx.ch>
+ *
+ */
+
+#include "dvo.h"
+
+/* register definitions according to the TFP410 data sheet */
+#define TFP410_VID 0x014C
+#define TFP410_DID 0x0410
+
+#define TFP410_VID_LO 0x00
+#define TFP410_VID_HI 0x01
+#define TFP410_DID_LO 0x02
+#define TFP410_DID_HI 0x03
+#define TFP410_REV 0x04
+
+#define TFP410_CTL_1 0x08
+#define TFP410_CTL_1_TDIS (1<<6)
+#define TFP410_CTL_1_VEN (1<<5)
+#define TFP410_CTL_1_HEN (1<<4)
+#define TFP410_CTL_1_DSEL (1<<3)
+#define TFP410_CTL_1_BSEL (1<<2)
+#define TFP410_CTL_1_EDGE (1<<1)
+#define TFP410_CTL_1_PD (1<<0)
+
+#define TFP410_CTL_2 0x09
+#define TFP410_CTL_2_VLOW (1<<7)
+#define TFP410_CTL_2_MSEL_MASK (0x7<<4)
+#define TFP410_CTL_2_MSEL (1<<4)
+#define TFP410_CTL_2_TSEL (1<<3)
+#define TFP410_CTL_2_RSEN (1<<2)
+#define TFP410_CTL_2_HTPLG (1<<1)
+#define TFP410_CTL_2_MDI (1<<0)
+
+#define TFP410_CTL_3 0x0A
+#define TFP410_CTL_3_DK_MASK (0x7<<5)
+#define TFP410_CTL_3_DK (1<<5)
+#define TFP410_CTL_3_DKEN (1<<4)
+#define TFP410_CTL_3_CTL_MASK (0x7<<1)
+#define TFP410_CTL_3_CTL (1<<1)
+
+#define TFP410_USERCFG 0x0B
+
+#define TFP410_DE_DLY 0x32
+
+#define TFP410_DE_CTL 0x33
+#define TFP410_DE_CTL_DEGEN (1<<6)
+#define TFP410_DE_CTL_VSPOL (1<<5)
+#define TFP410_DE_CTL_HSPOL (1<<4)
+#define TFP410_DE_CTL_DEDLY8 (1<<0)
+
+#define TFP410_DE_TOP 0x34
+
+#define TFP410_DE_CNT_LO 0x36
+#define TFP410_DE_CNT_HI 0x37
+
+#define TFP410_DE_LIN_LO 0x38
+#define TFP410_DE_LIN_HI 0x39
+
+#define TFP410_H_RES_LO 0x3A
+#define TFP410_H_RES_HI 0x3B
+
+#define TFP410_V_RES_LO 0x3C
+#define TFP410_V_RES_HI 0x3D
+
+struct tfp410_save_rec {
+ uint8_t ctl1;
+ uint8_t ctl2;
+};
+
+struct tfp410_priv {
+ bool quiet;
+
+ struct tfp410_save_rec saved_reg;
+ struct tfp410_save_rec mode_reg;
+};
+
+static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ u8 out_buf[2];
+ u8 in_buf[2];
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = i2cbus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = in_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+ *ch = in_buf[0];
+ return true;
+ };
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+ return false;
+}
+
+static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+ struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+ uint8_t out_buf[2];
+ struct i2c_msg msg = {
+ .addr = i2cbus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+ return true;
+
+ if (!tfp->quiet) {
+ DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, i2cbus->slave_addr);
+ }
+
+ return false;
+}
+
+static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
+{
+ uint8_t ch1, ch2;
+
+ if (tfp410_readb(dvo, addr+0, &ch1) &&
+ tfp410_readb(dvo, addr+1, &ch2))
+ return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
+
+ return -1;
+}
+
+/* Ti TFP410 driver for chip on i2c bus */
+static bool tfp410_init(struct intel_dvo_device *dvo,
+ struct intel_i2c_chan *i2cbus)
+{
+ /* this will detect the tfp410 chip on the specified i2c bus */
+ struct tfp410_priv *tfp;
+ int id;
+
+ tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL);
+ if (tfp == NULL)
+ return false;
+
+ dvo->i2c_bus = i2cbus;
+ dvo->i2c_bus->slave_addr = dvo->slave_addr;
+ dvo->dev_priv = tfp;
+ tfp->quiet = true;
+
+ if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
+ DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+
+ if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
+ DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
+ id, i2cbus->adapter.name, i2cbus->slave_addr);
+ goto out;
+ }
+ tfp->quiet = false;
+ return true;
+out:
+ kfree(tfp);
+ return false;
+}
+
+static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
+{
+ enum drm_connector_status ret = connector_status_disconnected;
+ uint8_t ctl2;
+
+ if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
+ if (ctl2 & TFP410_CTL_2_HTPLG)
+ ret = connector_status_connected;
+ else
+ ret = connector_status_disconnected;
+ }
+
+ return ret;
+}
+
+static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static void tfp410_mode_set(struct intel_dvo_device *dvo,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* As long as the basics are set up, since we don't have clock dependencies
+ * in the mode setup, we can just leave the registers alone and everything
+ * will work fine.
+ */
+ /* don't do much */
+ return;
+}
+
+/* set the tfp410 power state */
+static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
+{
+ uint8_t ctl1;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ ctl1 |= TFP410_CTL_1_PD;
+ else
+ ctl1 &= ~TFP410_CTL_1_PD;
+
+ tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
+}
+
+static void tfp410_dump_regs(struct intel_dvo_device *dvo)
+{
+ uint8_t val, val2;
+
+ tfp410_readb(dvo, TFP410_REV, &val);
+ DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_1, &val);
+ DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_2, &val);
+ DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_3, &val);
+ DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_USERCFG, &val);
+ DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_DLY, &val);
+ DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CTL, &val);
+ DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_TOP, &val);
+ DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
+ DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
+ DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_H_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
+ DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_V_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
+ DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+}
+
+static void tfp410_save(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1))
+ return;
+
+ if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2))
+ return;
+}
+
+static void tfp410_restore(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ /* Restore it powered down initially */
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1);
+
+ tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2);
+ tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1);
+}
+
+static void tfp410_destroy(struct intel_dvo_device *dvo)
+{
+ struct tfp410_priv *tfp = dvo->dev_priv;
+
+ if (tfp) {
+ kfree(tfp);
+ dvo->dev_priv = NULL;
+ }
+}
+
+struct intel_dvo_dev_ops tfp410_ops = {
+ .init = tfp410_init,
+ .detect = tfp410_detect,
+ .mode_valid = tfp410_mode_valid,
+ .mode_set = tfp410_mode_set,
+ .dpms = tfp410_dpms,
+ .dump_regs = tfp410_dump_regs,
+ .save = tfp410_save,
+ .restore = tfp410_restore,
+ .destroy = tfp410_destroy,
+};
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 553dd4bc3075..3d7082af5b72 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -28,6 +28,8 @@
#include "drmP.h"
#include "drm.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
@@ -39,6 +41,7 @@
int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
u32 acthd_reg = IS_I965G(dev) ? ACTHD_I965 : ACTHD;
u32 last_acthd = I915_READ(acthd_reg);
@@ -55,8 +58,8 @@ int i915_wait_ring(struct drm_device * dev, int n, const char *caller)
if (ring->space >= n)
return 0;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
if (ring->head != last_head)
i = 0;
@@ -121,16 +124,28 @@ static void i915_free_hws(struct drm_device *dev)
void i915_kernel_lost_context(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
+ /*
+ * We should never lose context on the ring with modesetting
+ * as we don't expose it to userspace
+ */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
ring->space = ring->head - (ring->tail + 8);
if (ring->space < 0)
ring->space += ring->Size;
- if (ring->head == ring->tail && dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (ring->head == ring->tail && master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}
static int i915_dma_cleanup(struct drm_device * dev)
@@ -154,25 +169,13 @@ static int i915_dma_cleanup(struct drm_device * dev)
if (I915_NEED_GFX_HWS(dev))
i915_free_hws(dev);
- dev_priv->sarea = NULL;
- dev_priv->sarea_priv = NULL;
-
return 0;
}
static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
{
drm_i915_private_t *dev_priv = dev->dev_private;
-
- dev_priv->sarea = drm_getsarea(dev);
- if (!dev_priv->sarea) {
- DRM_ERROR("can not find sarea!\n");
- i915_dma_cleanup(dev);
- return -EINVAL;
- }
-
- dev_priv->sarea_priv = (drm_i915_sarea_t *)
- ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
if (init->ring_size != 0) {
if (dev_priv->ring.ring_obj != NULL) {
@@ -207,7 +210,8 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
dev_priv->back_offset = init->back_offset;
dev_priv->front_offset = init->front_offset;
dev_priv->current_page = 0;
- dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->pf_current_page = 0;
/* Allow hardware batchbuffers unless told otherwise.
*/
@@ -222,11 +226,6 @@ static int i915_dma_resume(struct drm_device * dev)
DRM_DEBUG("%s\n", __func__);
- if (!dev_priv->sarea) {
- DRM_ERROR("can not find sarea!\n");
- return -EINVAL;
- }
-
if (dev_priv->ring.map.handle == NULL) {
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
@@ -435,13 +434,14 @@ i915_emit_box(struct drm_device *dev,
static void i915_emit_breadcrumb(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
RING_LOCALS;
dev_priv->counter++;
if (dev_priv->counter > 0x7FFFFFFFUL)
dev_priv->counter = 0;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -537,15 +537,17 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
static int i915_dispatch_flip(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv =
+ dev->primary->master->driver_priv;
RING_LOCALS;
- if (!dev_priv->sarea_priv)
+ if (!master_priv->sarea_priv)
return -EINVAL;
DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n",
__func__,
dev_priv->current_page,
- dev_priv->sarea_priv->pf_current_page);
+ master_priv->sarea_priv->pf_current_page);
i915_kernel_lost_context(dev);
@@ -572,7 +574,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
OUT_RING(0);
ADVANCE_LP_RING();
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter++;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -581,7 +583,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
OUT_RING(0);
ADVANCE_LP_RING();
- dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;
+ master_priv->sarea_priv->pf_current_page = dev_priv->current_page;
return 0;
}
@@ -611,8 +613,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- dev_priv->sarea_priv;
+ master_priv->sarea_priv;
drm_i915_batchbuffer_t *batch = data;
int ret;
@@ -644,8 +647,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
- dev_priv->sarea_priv;
+ master_priv->sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data;
int ret;
@@ -717,7 +721,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
value = dev->pci_device;
break;
case I915_PARAM_HAS_GEM:
- value = 1;
+ value = dev_priv->has_gem;
break;
default:
DRM_ERROR("Unknown parameter %d\n", param->param);
@@ -774,6 +778,11 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return -EINVAL;
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ WARN(1, "tried to set status page when mode setting active\n");
+ return 0;
+ }
+
printk(KERN_DEBUG "set status page addr 0x%08x\n", (u32)hws->addr);
dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12);
@@ -802,6 +811,214 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
return 0;
}
+/**
+ * i915_probe_agp - get AGP bootup configuration
+ * @pdev: PCI device
+ * @aperture_size: returns AGP aperture configured size
+ * @preallocated_size: returns size of BIOS preallocated AGP space
+ *
+ * Since Intel integrated graphics are UMA, the BIOS has to set aside
+ * some RAM for the framebuffer at early boot. This code figures out
+ * how much was set aside so we can use it for our own purposes.
+ */
+static int i915_probe_agp(struct drm_device *dev, unsigned long *aperture_size,
+ unsigned long *preallocated_size)
+{
+ struct pci_dev *bridge_dev;
+ u16 tmp = 0;
+ unsigned long overhead;
+
+ bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
+ if (!bridge_dev) {
+ DRM_ERROR("bridge device not found\n");
+ return -1;
+ }
+
+ /* Get the fb aperture size and "stolen" memory amount. */
+ pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);
+ pci_dev_put(bridge_dev);
+
+ *aperture_size = 1024 * 1024;
+ *preallocated_size = 1024 * 1024;
+
+ switch (dev->pdev->device) {
+ case PCI_DEVICE_ID_INTEL_82830_CGC:
+ case PCI_DEVICE_ID_INTEL_82845G_IG:
+ case PCI_DEVICE_ID_INTEL_82855GM_IG:
+ case PCI_DEVICE_ID_INTEL_82865_IG:
+ if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
+ *aperture_size *= 64;
+ else
+ *aperture_size *= 128;
+ break;
+ default:
+ /* 9xx supports large sizes, just look at the length */
+ *aperture_size = pci_resource_len(dev->pdev, 2);
+ break;
+ }
+
+ /*
+ * Some of the preallocated space is taken by the GTT
+ * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
+ */
+ if (IS_G4X(dev))
+ overhead = 4096;
+ else
+ overhead = (*aperture_size / 1024) + 4096;
+
+ switch (tmp & INTEL_855_GMCH_GMS_MASK) {
+ case INTEL_855_GMCH_GMS_STOLEN_1M:
+ break; /* 1M already */
+ case INTEL_855_GMCH_GMS_STOLEN_4M:
+ *preallocated_size *= 4;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_8M:
+ *preallocated_size *= 8;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_16M:
+ *preallocated_size *= 16;
+ break;
+ case INTEL_855_GMCH_GMS_STOLEN_32M:
+ *preallocated_size *= 32;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_48M:
+ *preallocated_size *= 48;
+ break;
+ case INTEL_915G_GMCH_GMS_STOLEN_64M:
+ *preallocated_size *= 64;
+ break;
+ case INTEL_855_GMCH_GMS_DISABLED:
+ DRM_ERROR("video memory is disabled\n");
+ return -1;
+ default:
+ DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
+ tmp & INTEL_855_GMCH_GMS_MASK);
+ return -1;
+ }
+ *preallocated_size -= overhead;
+
+ return 0;
+}
+
+static int i915_load_modeset_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long agp_size, prealloc_size;
+ int fb_bar = IS_I9XX(dev) ? 2 : 0;
+ int ret = 0;
+
+ dev->devname = kstrdup(DRIVER_NAME, GFP_KERNEL);
+ if (!dev->devname) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
+ 0xff000000;
+
+ DRM_DEBUG("*** fb base 0x%08lx\n", dev->mode_config.fb_base);
+
+ if (IS_MOBILE(dev) || (IS_I9XX(dev) && !IS_I965G(dev) && !IS_G33(dev)))
+ dev_priv->cursor_needs_physical = true;
+ else
+ dev_priv->cursor_needs_physical = false;
+
+ ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
+ if (ret)
+ goto kfree_devname;
+
+ /* Basic memrange allocator for stolen space (aka vram) */
+ drm_mm_init(&dev_priv->vram, 0, prealloc_size);
+
+ /* Let GEM Manage from end of prealloc space to end of aperture */
+ i915_gem_do_init(dev, prealloc_size, agp_size);
+
+ ret = i915_gem_init_ringbuffer(dev);
+ if (ret)
+ goto kfree_devname;
+
+ dev_priv->mm.gtt_mapping =
+ io_mapping_create_wc(dev->agp->base,
+ dev->agp->agp_info.aper_size * 1024*1024);
+
+ /* Allow hardware batchbuffers unless told otherwise.
+ */
+ dev_priv->allow_batchbuffer = 1;
+
+ ret = intel_init_bios(dev);
+ if (ret)
+ DRM_INFO("failed to find VBIOS tables\n");
+
+ ret = drm_irq_install(dev);
+ if (ret)
+ goto destroy_ringbuffer;
+
+ /* FIXME: re-add hotplug support */
+#if 0
+ ret = drm_hotplug_init(dev);
+ if (ret)
+ goto destroy_ringbuffer;
+#endif
+
+ /* Always safe in the mode setting case. */
+ /* FIXME: do pre/post-mode set stuff in core KMS code */
+ dev->vblank_disable_allowed = 1;
+
+ /*
+ * Initialize the hardware status page IRQ location.
+ */
+
+ I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
+
+ intel_modeset_init(dev);
+
+ drm_helper_initial_config(dev, false);
+
+ return 0;
+
+destroy_ringbuffer:
+ i915_gem_cleanup_ringbuffer(dev);
+kfree_devname:
+ kfree(dev->devname);
+out:
+ return ret;
+}
+
+int i915_master_create(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_i915_master_private *master_priv;
+
+ master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER);
+ if (!master_priv)
+ return -ENOMEM;
+
+ master->driver_priv = master_priv;
+ return 0;
+}
+
+void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_i915_master_private *master_priv = master->driver_priv;
+
+ if (!master_priv)
+ return;
+
+ drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER);
+
+ master->driver_priv = NULL;
+}
+
+/**
+ * i915_driver_load - setup chip and create an initial config
+ * @dev: DRM device
+ * @flags: startup flags
+ *
+ * The driver load routine has to do several things:
+ * - drive output discovery via intel_modeset_init()
+ * - initialize the memory manager
+ * - allocate initial config memory
+ * - setup the DRM framebuffer with the allocated memory
+ */
int i915_driver_load(struct drm_device *dev, unsigned long flags)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -829,6 +1046,19 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
size = drm_get_resource_len(dev, mmio_bar);
dev_priv->regs = ioremap(base, size);
+ if (!dev_priv->regs) {
+ DRM_ERROR("failed to map registers\n");
+ ret = -EIO;
+ goto free_priv;
+ }
+
+#ifdef CONFIG_HIGHMEM64G
+ /* don't enable GEM on PAE - needs agp + set_memory_* interface fixes */
+ dev_priv->has_gem = 0;
+#else
+ /* enable GEM by default */
+ dev_priv->has_gem = 1;
+#endif
i915_gem_load(dev);
@@ -836,7 +1066,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (!I915_NEED_GFX_HWS(dev)) {
ret = i915_init_phys_hws(dev);
if (ret != 0)
- return ret;
+ goto out_rmmap;
}
/* On the 945G/GM, the chipset reports the MSI capability on the
@@ -856,6 +1086,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
intel_opregion_init(dev);
spin_lock_init(&dev_priv->user_irq_lock);
+ dev_priv->user_irq_refcount = 0;
ret = drm_vblank_init(dev, I915_NUM_PIPE);
@@ -864,6 +1095,20 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
return ret;
}
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = i915_load_modeset_init(dev);
+ if (ret < 0) {
+ DRM_ERROR("failed to init modeset\n");
+ goto out_rmmap;
+ }
+ }
+
+ return 0;
+
+out_rmmap:
+ iounmap(dev_priv->regs);
+free_priv:
+ drm_free(dev_priv, sizeof(struct drm_i915_private), DRM_MEM_DRIVER);
return ret;
}
@@ -871,16 +1116,29 @@ int i915_driver_unload(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ io_mapping_free(dev_priv->mm.gtt_mapping);
+ drm_irq_uninstall(dev);
+ }
+
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
- i915_free_hws(dev);
-
if (dev_priv->regs != NULL)
iounmap(dev_priv->regs);
intel_opregion_free(dev);
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intel_modeset_cleanup(dev);
+
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_cleanup_ringbuffer(dev);
+ mutex_unlock(&dev->struct_mutex);
+ drm_mm_takedown(&dev_priv->vram);
+ i915_gem_lastclose(dev);
+ }
+
drm_free(dev->dev_private, sizeof(drm_i915_private_t),
DRM_MEM_DRIVER);
@@ -906,12 +1164,26 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv)
return 0;
}
+/**
+ * i915_driver_lastclose - clean up after all DRM clients have exited
+ * @dev: DRM device
+ *
+ * Take care of cleaning up after all DRM clients have exited. In the
+ * mode setting case, we want to restore the kernel's initial mode (just
+ * in case the last client left us in a bad state).
+ *
+ * Additionally, in the non-mode setting case, we'll tear down the AGP
+ * and DMA structures, since the kernel won't be using them, and clea
+ * up any GEM state.
+ */
void i915_driver_lastclose(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- if (!dev_priv)
+ if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intelfb_restore();
return;
+ }
i915_gem_lastclose(dev);
@@ -924,7 +1196,8 @@ void i915_driver_lastclose(struct drm_device * dev)
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- i915_mem_release(dev, file_priv, dev_priv->agp_heap);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ i915_mem_release(dev, file_priv, dev_priv->agp_heap);
}
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv)
@@ -964,6 +1237,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index a80ead215282..f8b3df0926c0 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -33,11 +33,22 @@
#include "i915_drv.h"
#include "drm_pciids.h"
+#include <linux/console.h>
+
+static unsigned int i915_modeset = -1;
+module_param_named(modeset, i915_modeset, int, 0400);
+
+unsigned int i915_fbpercrtc = 0;
+module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
static struct pci_device_id pciidlist[] = {
i915_PCI_IDS
};
+#if defined(CONFIG_DRM_I915_KMS)
+MODULE_DEVICE_TABLE(pci, pciidlist);
+#endif
+
static int i915_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -81,6 +92,10 @@ static int i915_resume(struct drm_device *dev)
return 0;
}
+static struct vm_operations_struct i915_gem_vm_ops = {
+ .fault = i915_gem_fault,
+};
+
static struct drm_driver driver = {
/* don't use mtrr's here, the Xserver or user space app should
* deal with them for intel hardware.
@@ -107,17 +122,20 @@ static struct drm_driver driver = {
.reclaim_buffers = drm_core_reclaim_buffers,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
+ .master_create = i915_master_create,
+ .master_destroy = i915_master_destroy,
.proc_init = i915_gem_proc_init,
.proc_cleanup = i915_gem_proc_cleanup,
.gem_init_object = i915_gem_init_object,
.gem_free_object = i915_gem_free_object,
+ .gem_vm_ops = &i915_gem_vm_ops,
.ioctls = i915_ioctls,
.fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_gem_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
#ifdef CONFIG_COMPAT
@@ -141,6 +159,28 @@ static struct drm_driver driver = {
static int __init i915_init(void)
{
driver.num_ioctls = i915_max_ioctl;
+
+ /*
+ * If CONFIG_DRM_I915_KMS is set, default to KMS unless
+ * explicitly disabled with the module pararmeter.
+ *
+ * Otherwise, just follow the parameter (defaulting to off).
+ *
+ * Allow optional vga_text_mode_force boot option to override
+ * the default behavior.
+ */
+#if defined(CONFIG_DRM_I915_KMS)
+ if (i915_modeset != 0)
+ driver.driver_features |= DRIVER_MODESET;
+#endif
+ if (i915_modeset == 1)
+ driver.driver_features |= DRIVER_MODESET;
+
+#ifdef CONFIG_VGA_CONSOLE
+ if (vgacon_text_force() && i915_modeset == -1)
+ driver.driver_features &= ~DRIVER_MODESET;
+#endif
+
return drm_init(&driver);
}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index adc972cc6bfc..4756e5cd6b5e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -31,6 +31,7 @@
#define _I915_DRV_H_
#include "i915_reg.h"
+#include "intel_bios.h"
#include <linux/io-mapping.h>
/* General customization:
@@ -103,13 +104,23 @@ struct intel_opregion {
int enabled;
};
+struct drm_i915_master_private {
+ drm_local_map_t *sarea;
+ struct _drm_i915_sarea *sarea_priv;
+};
+#define I915_FENCE_REG_NONE -1
+
+struct drm_i915_fence_reg {
+ struct drm_gem_object *obj;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
+ int has_gem;
+
void __iomem *regs;
- drm_local_map_t *sarea;
- drm_i915_sarea_t *sarea_priv;
drm_i915_ring_buffer_t ring;
drm_dma_handle_t *status_page_dmah;
@@ -142,8 +153,30 @@ typedef struct drm_i915_private {
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
int vblank_pipe;
+ bool cursor_needs_physical;
+
+ struct drm_mm vram;
+
+ int irq_enabled;
+
struct intel_opregion opregion;
+ /* LVDS info */
+ int backlight_duty_cycle; /* restore backlight to this value */
+ bool panel_wants_dither;
+ struct drm_display_mode *panel_fixed_mode;
+ struct drm_display_mode *vbt_mode; /* if any */
+
+ /* Feature bits from the VBIOS */
+ unsigned int int_tv_support:1;
+ unsigned int lvds_dither:1;
+ unsigned int lvds_vbt:1;
+ unsigned int int_crt_support:1;
+
+ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
+ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
+ int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+
/* Register state */
u8 saveLBB;
u32 saveDSPACNTR;
@@ -362,6 +395,21 @@ struct drm_i915_gem_object {
* This is the same as gtt_space->start
*/
uint32_t gtt_offset;
+ /**
+ * Required alignment for the object
+ */
+ uint32_t gtt_alignment;
+ /**
+ * Fake offset for use by mmap(2)
+ */
+ uint64_t mmap_offset;
+
+ /**
+ * Fence register bits (if any) for this object. Will be set
+ * as needed when mapped into the GTT.
+ * Protected by dev->struct_mutex.
+ */
+ int fence_reg;
/** Boolean whether this object has a valid gtt offset. */
int gtt_bound;
@@ -374,6 +422,7 @@ struct drm_i915_gem_object {
/** Current tiling mode for the object. */
uint32_t tiling_mode;
+ uint32_t stride;
/** AGP mapping type (AGP_USER_MEMORY or AGP_USER_CACHED_MEMORY */
uint32_t agp_type;
@@ -383,6 +432,10 @@ struct drm_i915_gem_object {
* flags which individual pages are valid.
*/
uint8_t *page_cpu_valid;
+
+ /** User space pin count and filp owning the pin */
+ uint32_t user_pin_count;
+ struct drm_file *pin_filp;
};
/**
@@ -412,8 +465,19 @@ struct drm_i915_file_private {
} mm;
};
+enum intel_chip_family {
+ CHIP_I8XX = 0x01,
+ CHIP_I9XX = 0x02,
+ CHIP_I915 = 0x04,
+ CHIP_I965 = 0x08,
+};
+
extern struct drm_ioctl_desc i915_ioctls[];
extern int i915_max_ioctl;
+extern unsigned int i915_fbpercrtc;
+
+extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
+extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master);
/* i915_dma.c */
extern void i915_kernel_lost_context(struct drm_device * dev);
@@ -439,6 +503,7 @@ extern int i915_irq_wait(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_user_irq_get(struct drm_device *dev);
void i915_user_irq_put(struct drm_device *dev);
+extern void i915_enable_interrupt (struct drm_device *dev);
extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
extern void i915_driver_irq_preinstall(struct drm_device * dev);
@@ -485,6 +550,8 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
@@ -521,6 +588,16 @@ uint32_t i915_get_gem_seqno(struct drm_device *dev);
void i915_gem_retire_requests(struct drm_device *dev);
void i915_gem_retire_work_handler(struct work_struct *work);
void i915_gem_clflush_object(struct drm_gem_object *obj);
+int i915_gem_object_set_domain(struct drm_gem_object *obj,
+ uint32_t read_domains,
+ uint32_t write_domain);
+int i915_gem_init_ringbuffer(struct drm_device *dev);
+void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end);
+int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
+ int write);
/* i915_gem_tiling.c */
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -559,6 +636,10 @@ static inline void opregion_asle_intr(struct drm_device *dev) { return; }
static inline void opregion_enable_asle(struct drm_device *dev) { return; }
#endif
+/* modesetting */
+extern void intel_modeset_init(struct drm_device *dev);
+extern void intel_modeset_cleanup(struct drm_device *dev);
+
/**
* Lock test for when it's just for synchronization of ring access.
*
@@ -576,6 +657,13 @@ static inline void opregion_enable_asle(struct drm_device *dev) { return; }
#define I915_WRITE16(reg, val) writel(val, dev_priv->regs + (reg))
#define I915_READ8(reg) readb(dev_priv->regs + (reg))
#define I915_WRITE8(reg, val) writeb(val, dev_priv->regs + (reg))
+#ifdef writeq
+#define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
+#else
+#define I915_WRITE64(reg, val) (writel(val, dev_priv->regs + (reg)), \
+ writel(upper_32_bits(val), dev_priv->regs + \
+ (reg) + 4))
+#endif
#define I915_VERBOSE 0
@@ -658,7 +746,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \
(dev)->pci_device == 0x2E12 || \
- (dev)->pci_device == 0x2E22)
+ (dev)->pci_device == 0x2E22 || \
+ IS_GM45(dev))
#define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \
(dev)->pci_device == 0x29B2 || \
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index ad672d854828..cc2ca5561feb 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -30,6 +30,7 @@
#include "i915_drm.h"
#include "i915_drv.h"
#include <linux/swap.h>
+#include <linux/pci.h>
#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT))
@@ -40,8 +41,6 @@ i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj,
static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj);
static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj);
-static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
- int write);
static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj,
int write);
static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
@@ -51,34 +50,43 @@ static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *o
static int i915_gem_object_get_page_list(struct drm_gem_object *obj);
static void i915_gem_object_free_page_list(struct drm_gem_object *obj);
static int i915_gem_object_wait_rendering(struct drm_gem_object *obj);
+static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
+ unsigned alignment);
+static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
+static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
+static int i915_gem_evict_something(struct drm_device *dev);
+
+int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
-static void
-i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+ if (start >= end ||
+ (start & (PAGE_SIZE - 1)) != 0 ||
+ (end & (PAGE_SIZE - 1)) != 0) {
+ return -EINVAL;
+ }
+
+ drm_mm_init(&dev_priv->mm.gtt_space, start,
+ end - start);
+
+ dev->gtt_total = (uint32_t) (end - start);
+
+ return 0;
+}
int
i915_gem_init_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_init *args = data;
+ int ret;
mutex_lock(&dev->struct_mutex);
-
- if (args->gtt_start >= args->gtt_end ||
- (args->gtt_start & (PAGE_SIZE - 1)) != 0 ||
- (args->gtt_end & (PAGE_SIZE - 1)) != 0) {
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
- }
-
- drm_mm_init(&dev_priv->mm.gtt_space, args->gtt_start,
- args->gtt_end - args->gtt_start);
-
- dev->gtt_total = (uint32_t) (args->gtt_end - args->gtt_start);
-
+ ret = i915_gem_do_init(dev, args->gtt_start, args->gtt_end);
mutex_unlock(&dev->struct_mutex);
- return 0;
+ return ret;
}
int
@@ -529,6 +537,252 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
return 0;
}
+/**
+ * i915_gem_fault - fault a page into the GTT
+ * vma: VMA in question
+ * vmf: fault info
+ *
+ * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped
+ * from userspace. The fault handler takes care of binding the object to
+ * the GTT (if needed), allocating and programming a fence register (again,
+ * only if needed based on whether the old reg is still valid or the object
+ * is tiled) and inserting a new PTE into the faulting process.
+ *
+ * Note that the faulting process may involve evicting existing objects
+ * from the GTT and/or fence registers to make room. So performance may
+ * suffer if the GTT working set is large or there are few fence registers
+ * left.
+ */
+int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ pgoff_t page_offset;
+ unsigned long pfn;
+ int ret = 0;
+
+ /* We don't use vmf->pgoff since that has the fake offset */
+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+ PAGE_SHIFT;
+
+ /* Now bind it into the GTT if needed */
+ mutex_lock(&dev->struct_mutex);
+ if (!obj_priv->gtt_space) {
+ ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return VM_FAULT_SIGBUS;
+ }
+ list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+ }
+
+ /* Need a new fence register? */
+ if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+ obj_priv->tiling_mode != I915_TILING_NONE)
+ i915_gem_object_get_fence_reg(obj);
+
+ pfn = ((dev->agp->base + obj_priv->gtt_offset) >> PAGE_SHIFT) +
+ page_offset;
+
+ /* Finally, remap it using the new GTT offset */
+ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ switch (ret) {
+ case -ENOMEM:
+ case -EAGAIN:
+ return VM_FAULT_OOM;
+ case -EFAULT:
+ case -EBUSY:
+ DRM_ERROR("can't insert pfn?? fault or busy...\n");
+ return VM_FAULT_SIGBUS;
+ default:
+ return VM_FAULT_NOPAGE;
+ }
+}
+
+/**
+ * i915_gem_create_mmap_offset - create a fake mmap offset for an object
+ * @obj: obj in question
+ *
+ * GEM memory mapping works by handing back to userspace a fake mmap offset
+ * it can use in a subsequent mmap(2) call. The DRM core code then looks
+ * up the object based on the offset and sets up the various memory mapping
+ * structures.
+ *
+ * This routine allocates and attaches a fake offset for @obj.
+ */
+static int
+i915_gem_create_mmap_offset(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_map_list *list;
+ struct drm_map *map;
+ int ret = 0;
+
+ /* Set the object up for mmap'ing */
+ list = &obj->map_list;
+ list->map = drm_calloc(1, sizeof(struct drm_map_list),
+ DRM_MEM_DRIVER);
+ if (!list->map)
+ return -ENOMEM;
+
+ map = list->map;
+ map->type = _DRM_GEM;
+ map->size = obj->size;
+ map->handle = obj;
+
+ /* Get a DRM GEM mmap offset allocated... */
+ list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
+ obj->size / PAGE_SIZE, 0, 0);
+ if (!list->file_offset_node) {
+ DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
+ ret = -ENOMEM;
+ goto out_free_list;
+ }
+
+ list->file_offset_node = drm_mm_get_block(list->file_offset_node,
+ obj->size / PAGE_SIZE, 0);
+ if (!list->file_offset_node) {
+ ret = -ENOMEM;
+ goto out_free_list;
+ }
+
+ list->hash.key = list->file_offset_node->start;
+ if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
+ DRM_ERROR("failed to add to map hash\n");
+ goto out_free_mm;
+ }
+
+ /* By now we should be all set, any drm_mmap request on the offset
+ * below will get to our mmap & fault handler */
+ obj_priv->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT;
+
+ return 0;
+
+out_free_mm:
+ drm_mm_put_block(list->file_offset_node);
+out_free_list:
+ drm_free(list->map, sizeof(struct drm_map_list), DRM_MEM_DRIVER);
+
+ return ret;
+}
+
+/**
+ * i915_gem_get_gtt_alignment - return required GTT alignment for an object
+ * @obj: object to check
+ *
+ * Return the required GTT alignment for an object, taking into account
+ * potential fence register mapping if needed.
+ */
+static uint32_t
+i915_gem_get_gtt_alignment(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int start, i;
+
+ /*
+ * Minimum alignment is 4k (GTT page size), but might be greater
+ * if a fence register is needed for the object.
+ */
+ if (IS_I965G(dev) || obj_priv->tiling_mode == I915_TILING_NONE)
+ return 4096;
+
+ /*
+ * Previous chips need to be aligned to the size of the smallest
+ * fence register that can contain the object.
+ */
+ if (IS_I9XX(dev))
+ start = 1024*1024;
+ else
+ start = 512*1024;
+
+ for (i = start; i < obj->size; i <<= 1)
+ ;
+
+ return i;
+}
+
+/**
+ * i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing
+ * @dev: DRM device
+ * @data: GTT mapping ioctl data
+ * @file_priv: GEM object info
+ *
+ * Simply returns the fake offset to userspace so it can mmap it.
+ * The mmap call will end up in drm_gem_mmap(), which will set things
+ * up so we can get faults in the handler above.
+ *
+ * The fault handler will take care of binding the object into the GTT
+ * (since it may have been evicted to make room for something), allocating
+ * a fence register, and mapping the appropriate aperture address into
+ * userspace.
+ */
+int
+i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_mmap_gtt *args = data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
+ int ret;
+
+ if (!(dev->driver->driver_features & DRIVER_GEM))
+ return -ENODEV;
+
+ obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ if (obj == NULL)
+ return -EBADF;
+
+ mutex_lock(&dev->struct_mutex);
+
+ obj_priv = obj->driver_private;
+
+ if (!obj_priv->mmap_offset) {
+ ret = i915_gem_create_mmap_offset(obj);
+ if (ret)
+ return ret;
+ }
+
+ args->offset = obj_priv->mmap_offset;
+
+ obj_priv->gtt_alignment = i915_gem_get_gtt_alignment(obj);
+
+ /* Make sure the alignment is correct for fence regs etc */
+ if (obj_priv->agp_mem &&
+ (obj_priv->gtt_offset & (obj_priv->gtt_alignment - 1))) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+
+ /*
+ * Pull it into the GTT so that we have a page list (makes the
+ * initial fault faster and any subsequent flushing possible).
+ */
+ if (!obj_priv->agp_mem) {
+ ret = i915_gem_object_bind_to_gtt(obj, obj_priv->gtt_alignment);
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ list_add(&obj_priv->list, &dev_priv->mm.inactive_list);
+ }
+
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
static void
i915_gem_object_free_page_list(struct drm_gem_object *obj)
{
@@ -726,6 +980,7 @@ i915_gem_retire_request(struct drm_device *dev,
*/
if (obj_priv->last_rendering_seqno != request->seqno)
return;
+
#if WATCH_LRU
DRM_INFO("%s: retire %d moves to inactive list %p\n",
__func__, request->seqno, obj);
@@ -956,6 +1211,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ loff_t offset;
int ret = 0;
#if WATCH_BUF
@@ -991,6 +1247,14 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
BUG_ON(obj_priv->active);
+ /* blow away mappings if mapped through GTT */
+ offset = ((loff_t) obj->map_list.hash.key) << PAGE_SHIFT;
+ if (dev->dev_mapping)
+ unmap_mapping_range(dev->dev_mapping, offset, obj->size, 1);
+
+ if (obj_priv->fence_reg != I915_FENCE_REG_NONE)
+ i915_gem_clear_fence_reg(obj);
+
i915_gem_object_free_page_list(obj);
if (obj_priv->gtt_space) {
@@ -1149,6 +1413,204 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
return 0;
}
+static void i965_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint64_t val;
+
+ val = (uint64_t)((obj_priv->gtt_offset + obj->size - 4096) &
+ 0xfffff000) << 32;
+ val |= obj_priv->gtt_offset & 0xfffff000;
+ val |= ((obj_priv->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I965_FENCE_TILING_Y_SHIFT;
+ val |= I965_FENCE_REG_VALID;
+
+ I915_WRITE64(FENCE_REG_965_0 + (regnum * 8), val);
+}
+
+static void i915_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint32_t val;
+ uint32_t pitch_val;
+
+ if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
+ (obj_priv->gtt_offset & (obj->size - 1))) {
+ WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__);
+ return;
+ }
+
+ if (obj_priv->tiling_mode == I915_TILING_Y && (IS_I945G(dev) ||
+ IS_I945GM(dev) ||
+ IS_G33(dev)))
+ pitch_val = (obj_priv->stride / 128) - 1;
+ else
+ pitch_val = (obj_priv->stride / 512) - 1;
+
+ val = obj_priv->gtt_offset;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I830_FENCE_TILING_Y_SHIFT;
+ val |= I915_FENCE_SIZE_BITS(obj->size);
+ val |= pitch_val << I830_FENCE_PITCH_SHIFT;
+ val |= I830_FENCE_REG_VALID;
+
+ I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
+}
+
+static void i830_write_fence_reg(struct drm_i915_fence_reg *reg)
+{
+ struct drm_gem_object *obj = reg->obj;
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int regnum = obj_priv->fence_reg;
+ uint32_t val;
+ uint32_t pitch_val;
+
+ if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) ||
+ (obj_priv->gtt_offset & (obj->size - 1))) {
+ WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__);
+ return;
+ }
+
+ pitch_val = (obj_priv->stride / 128) - 1;
+
+ val = obj_priv->gtt_offset;
+ if (obj_priv->tiling_mode == I915_TILING_Y)
+ val |= 1 << I830_FENCE_TILING_Y_SHIFT;
+ val |= I830_FENCE_SIZE_BITS(obj->size);
+ val |= pitch_val << I830_FENCE_PITCH_SHIFT;
+ val |= I830_FENCE_REG_VALID;
+
+ I915_WRITE(FENCE_REG_830_0 + (regnum * 4), val);
+
+}
+
+/**
+ * i915_gem_object_get_fence_reg - set up a fence reg for an object
+ * @obj: object to map through a fence reg
+ *
+ * When mapping objects through the GTT, userspace wants to be able to write
+ * to them without having to worry about swizzling if the object is tiled.
+ *
+ * This function walks the fence regs looking for a free one for @obj,
+ * stealing one if it can't find any.
+ *
+ * It then sets up the reg based on the object's properties: address, pitch
+ * and tiling format.
+ */
+static void
+i915_gem_object_get_fence_reg(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ struct drm_i915_fence_reg *reg = NULL;
+ int i, ret;
+
+ switch (obj_priv->tiling_mode) {
+ case I915_TILING_NONE:
+ WARN(1, "allocating a fence for non-tiled object?\n");
+ break;
+ case I915_TILING_X:
+ WARN(obj_priv->stride & (512 - 1),
+ "object is X tiled but has non-512B pitch\n");
+ break;
+ case I915_TILING_Y:
+ WARN(obj_priv->stride & (128 - 1),
+ "object is Y tiled but has non-128B pitch\n");
+ break;
+ }
+
+ /* First try to find a free reg */
+ for (i = dev_priv->fence_reg_start; i < dev_priv->num_fence_regs; i++) {
+ reg = &dev_priv->fence_regs[i];
+ if (!reg->obj)
+ break;
+ }
+
+ /* None available, try to steal one or wait for a user to finish */
+ if (i == dev_priv->num_fence_regs) {
+ struct drm_i915_gem_object *old_obj_priv = NULL;
+ loff_t offset;
+
+try_again:
+ /* Could try to use LRU here instead... */
+ for (i = dev_priv->fence_reg_start;
+ i < dev_priv->num_fence_regs; i++) {
+ reg = &dev_priv->fence_regs[i];
+ old_obj_priv = reg->obj->driver_private;
+ if (!old_obj_priv->pin_count)
+ break;
+ }
+
+ /*
+ * Now things get ugly... we have to wait for one of the
+ * objects to finish before trying again.
+ */
+ if (i == dev_priv->num_fence_regs) {
+ ret = i915_gem_object_wait_rendering(reg->obj);
+ if (ret) {
+ WARN(ret, "wait_rendering failed: %d\n", ret);
+ return;
+ }
+ goto try_again;
+ }
+
+ /*
+ * Zap this virtual mapping so we can set up a fence again
+ * for this object next time we need it.
+ */
+ offset = ((loff_t) reg->obj->map_list.hash.key) << PAGE_SHIFT;
+ if (dev->dev_mapping)
+ unmap_mapping_range(dev->dev_mapping, offset,
+ reg->obj->size, 1);
+ old_obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ }
+
+ obj_priv->fence_reg = i;
+ reg->obj = obj;
+
+ if (IS_I965G(dev))
+ i965_write_fence_reg(reg);
+ else if (IS_I9XX(dev))
+ i915_write_fence_reg(reg);
+ else
+ i830_write_fence_reg(reg);
+}
+
+/**
+ * i915_gem_clear_fence_reg - clear out fence register info
+ * @obj: object to clear
+ *
+ * Zeroes out the fence register itself and clears out the associated
+ * data structures in dev_priv and obj_priv.
+ */
+static void
+i915_gem_clear_fence_reg(struct drm_gem_object *obj)
+{
+ struct drm_device *dev = obj->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ if (IS_I965G(dev))
+ I915_WRITE64(FENCE_REG_965_0 + (obj_priv->fence_reg * 8), 0);
+ else
+ I915_WRITE(FENCE_REG_830_0 + (obj_priv->fence_reg * 4), 0);
+
+ dev_priv->fence_regs[obj_priv->fence_reg].obj = NULL;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
+}
+
/**
* Finds free space in the GTT aperture and binds the object there.
*/
@@ -1307,7 +1769,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
* This function returns when the move is complete, including waiting on
* flushes to occur.
*/
-static int
+int
i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
@@ -2029,13 +2491,15 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
/* error other than GTT full, or we've already tried again */
if (ret != -ENOMEM || pin_tries >= 1) {
- DRM_ERROR("Failed to pin buffers %d\n", ret);
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Failed to pin buffers %d\n", ret);
goto err;
}
/* unpin all of our buffers */
for (i = 0; i < pinned; i++)
i915_gem_object_unpin(object_list[i]);
+ pinned = 0;
/* evict everyone we can from the aperture */
ret = i915_gem_evict_everything(dev);
@@ -2149,13 +2613,12 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
"back to user (%d)\n",
args->buffer_count, ret);
err:
- if (object_list != NULL) {
- for (i = 0; i < pinned; i++)
- i915_gem_object_unpin(object_list[i]);
+ for (i = 0; i < pinned; i++)
+ i915_gem_object_unpin(object_list[i]);
+
+ for (i = 0; i < args->buffer_count; i++)
+ drm_gem_object_unreference(object_list[i]);
- for (i = 0; i < args->buffer_count; i++)
- drm_gem_object_unreference(object_list[i]);
- }
mutex_unlock(&dev->struct_mutex);
pre_mutex_err:
@@ -2178,7 +2641,8 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
if (obj_priv->gtt_space == NULL) {
ret = i915_gem_object_bind_to_gtt(obj, alignment);
if (ret != 0) {
- DRM_ERROR("Failure to bind: %d", ret);
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Failure to bind: %d", ret);
return ret;
}
}
@@ -2249,11 +2713,22 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
}
obj_priv = obj->driver_private;
- ret = i915_gem_object_pin(obj, args->alignment);
- if (ret != 0) {
- drm_gem_object_unreference(obj);
+ if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != file_priv) {
+ DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n",
+ args->handle);
mutex_unlock(&dev->struct_mutex);
- return ret;
+ return -EINVAL;
+ }
+
+ obj_priv->user_pin_count++;
+ obj_priv->pin_filp = file_priv;
+ if (obj_priv->user_pin_count == 1) {
+ ret = i915_gem_object_pin(obj, args->alignment);
+ if (ret != 0) {
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
}
/* XXX - flush the CPU caches for pinned objects
@@ -2273,6 +2748,7 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_pin *args = data;
struct drm_gem_object *obj;
+ struct drm_i915_gem_object *obj_priv;
mutex_lock(&dev->struct_mutex);
@@ -2284,7 +2760,19 @@ i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
return -EBADF;
}
- i915_gem_object_unpin(obj);
+ obj_priv = obj->driver_private;
+ if (obj_priv->pin_filp != file_priv) {
+ DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n",
+ args->handle);
+ drm_gem_object_unreference(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
+ }
+ obj_priv->user_pin_count--;
+ if (obj_priv->user_pin_count == 0) {
+ obj_priv->pin_filp = NULL;
+ i915_gem_object_unpin(obj);
+ }
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
@@ -2309,7 +2797,14 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
}
obj_priv = obj->driver_private;
- args->busy = obj_priv->active;
+ /* Don't count being on the flushing list against the object being
+ * done. Otherwise, a buffer left on the flushing list but not getting
+ * flushed (because nobody's flushing that domain) won't ever return
+ * unbusy and get reused by libdrm's bo cache. The other expected
+ * consumer of this interface, OpenGL's occlusion queries, also specs
+ * that the objects get unbusy "eventually" without any interference.
+ */
+ args->busy = obj_priv->active && obj_priv->last_rendering_seqno != 0;
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
@@ -2344,12 +2839,18 @@ int i915_gem_init_object(struct drm_gem_object *obj)
obj->driver_private = obj_priv;
obj_priv->obj = obj;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj_priv->list);
+
return 0;
}
void i915_gem_free_object(struct drm_gem_object *obj)
{
+ struct drm_device *dev = obj->dev;
+ struct drm_gem_mm *mm = dev->mm_private;
+ struct drm_map_list *list;
+ struct drm_map *map;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
while (obj_priv->pin_count > 0)
@@ -2357,6 +2858,20 @@ void i915_gem_free_object(struct drm_gem_object *obj)
i915_gem_object_unbind(obj);
+ list = &obj->map_list;
+ drm_ht_remove_item(&mm->offset_hash, &list->hash);
+
+ if (list->file_offset_node) {
+ drm_mm_put_block(list->file_offset_node);
+ list->file_offset_node = NULL;
+ }
+
+ map = list->map;
+ if (map) {
+ drm_free(map, sizeof(*map), DRM_MEM_DRIVER);
+ list->map = NULL;
+ }
+
drm_free(obj_priv->page_cpu_valid, 1, DRM_MEM_DRIVER);
drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
}
@@ -2425,8 +2940,7 @@ i915_gem_idle(struct drm_device *dev)
*/
i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT),
~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
- seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU |
- I915_GEM_DOMAIN_GTT));
+ seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU);
if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
@@ -2553,12 +3067,13 @@ i915_gem_init_hws(struct drm_device *dev)
return 0;
}
-static int
+int
i915_gem_init_ringbuffer(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
+ drm_i915_ring_buffer_t *ring = &dev_priv->ring;
int ret;
u32 head;
@@ -2580,24 +3095,24 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
}
/* Set up the kernel mapping for the ring. */
- dev_priv->ring.Size = obj->size;
- dev_priv->ring.tail_mask = obj->size - 1;
+ ring->Size = obj->size;
+ ring->tail_mask = obj->size - 1;
- dev_priv->ring.map.offset = dev->agp->base + obj_priv->gtt_offset;
- dev_priv->ring.map.size = obj->size;
- dev_priv->ring.map.type = 0;
- dev_priv->ring.map.flags = 0;
- dev_priv->ring.map.mtrr = 0;
+ ring->map.offset = dev->agp->base + obj_priv->gtt_offset;
+ ring->map.size = obj->size;
+ ring->map.type = 0;
+ ring->map.flags = 0;
+ ring->map.mtrr = 0;
- drm_core_ioremap_wc(&dev_priv->ring.map, dev);
- if (dev_priv->ring.map.handle == NULL) {
+ drm_core_ioremap_wc(&ring->map, dev);
+ if (ring->map.handle == NULL) {
DRM_ERROR("Failed to map ringbuffer.\n");
memset(&dev_priv->ring, 0, sizeof(dev_priv->ring));
drm_gem_object_unreference(obj);
return -EINVAL;
}
- dev_priv->ring.ring_obj = obj;
- dev_priv->ring.virtual_start = dev_priv->ring.map.handle;
+ ring->ring_obj = obj;
+ ring->virtual_start = ring->map.handle;
/* Stop the ring if it's running. */
I915_WRITE(PRB0_CTL, 0);
@@ -2645,12 +3160,20 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
}
/* Update our cache of the ring state */
- i915_kernel_lost_context(dev);
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ i915_kernel_lost_context(dev);
+ else {
+ ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
+ ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
+ ring->space = ring->head - (ring->tail + 8);
+ if (ring->space < 0)
+ ring->space += ring->Size;
+ }
return 0;
}
-static void
+void
i915_gem_cleanup_ringbuffer(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -2688,6 +3211,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
if (dev_priv->mm.wedged) {
DRM_ERROR("Reenabling wedged hardware, good luck\n");
dev_priv->mm.wedged = 0;
@@ -2721,6 +3247,9 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
ret = i915_gem_idle(dev);
drm_irq_uninstall(dev);
@@ -2751,5 +3280,13 @@ i915_gem_load(struct drm_device *dev)
i915_gem_retire_work_handler);
dev_priv->mm.next_gem_seqno = 1;
+ /* Old X drivers will take 0-2 for front, back, depth buffers */
+ dev_priv->fence_reg_start = 3;
+
+ if (IS_I965G(dev))
+ dev_priv->num_fence_regs = 16;
+ else
+ dev_priv->num_fence_regs = 8;
+
i915_gem_detect_bit_6_swizzle(dev);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_proc.c b/drivers/gpu/drm/i915/i915_gem_proc.c
index e8d5abe1250e..4d1b9de0cd8b 100644
--- a/drivers/gpu/drm/i915/i915_gem_proc.c
+++ b/drivers/gpu/drm/i915/i915_gem_proc.c
@@ -250,6 +250,39 @@ static int i915_interrupt_info(char *buf, char **start, off_t offset,
return len - offset;
}
+static int i915_hws_info(char *buf, char **start, off_t offset,
+ int request, int *eof, void *data)
+{
+ struct drm_minor *minor = (struct drm_minor *) data;
+ struct drm_device *dev = minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ int len = 0, i;
+ volatile u32 *hws;
+
+ if (offset > DRM_PROC_LIMIT) {
+ *eof = 1;
+ return 0;
+ }
+
+ hws = (volatile u32 *)dev_priv->hw_status_page;
+ if (hws == NULL) {
+ *eof = 1;
+ return 0;
+ }
+
+ *start = &buf[offset];
+ *eof = 0;
+ for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) {
+ DRM_PROC_PRINT("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i * 4,
+ hws[i], hws[i + 1], hws[i + 2], hws[i + 3]);
+ }
+ if (len > request + offset)
+ return request;
+ *eof = 1;
+ return len - offset;
+}
+
static struct drm_proc_list {
/** file name */
const char *name;
@@ -262,6 +295,7 @@ static struct drm_proc_list {
{"i915_gem_request", i915_gem_request_info},
{"i915_gem_seqno", i915_gem_seqno_info},
{"i915_gem_interrupt", i915_interrupt_info},
+ {"i915_gem_hws", i915_hws_info},
};
#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list)
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index a8cb69469c64..241f39b7f460 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -208,6 +208,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
}
}
obj_priv->tiling_mode = args->tiling_mode;
+ obj_priv->stride = args->stride;
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 69b9a42da95e..0cadafbef411 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -30,6 +30,7 @@
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
+#include "intel_drv.h"
#define MAX_NOPID ((u32)~0)
@@ -51,6 +52,15 @@
#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \
I915_INTERRUPT_ENABLE_VAR)
+#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\
+ PIPE_VBLANK_INTERRUPT_STATUS)
+
+#define I915_PIPE_VBLANK_ENABLE (PIPE_START_VBLANK_INTERRUPT_ENABLE |\
+ PIPE_VBLANK_INTERRUPT_ENABLE)
+
+#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \
+ DRM_I915_VBLANK_PIPE_B)
+
void
i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
{
@@ -168,6 +178,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv;
u32 iir, new_iir;
u32 pipea_stats, pipeb_stats;
u32 vblank_status;
@@ -200,6 +211,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT);
+
/*
* Clear the PIPE(A|B)STAT regs before the IIR
*/
@@ -222,9 +234,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
+ if (dev->primary->master) {
+ master_priv = dev->primary->master->driver_priv;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch =
+ READ_BREADCRUMB(dev_priv);
+ }
if (iir & I915_USER_INTERRUPT) {
dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
@@ -269,6 +284,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
static int i915_emit_irq(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
RING_LOCALS;
i915_kernel_lost_context(dev);
@@ -278,8 +294,8 @@ static int i915_emit_irq(struct drm_device * dev)
dev_priv->counter++;
if (dev_priv->counter > 0x7FFFFFFFUL)
dev_priv->counter = 1;
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_enqueue = dev_priv->counter;
BEGIN_LP_RING(4);
OUT_RING(MI_STORE_DWORD_INDEX);
@@ -317,21 +333,20 @@ void i915_user_irq_put(struct drm_device *dev)
static int i915_wait_irq(struct drm_device * dev, int irq_nr)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
int ret = 0;
DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
READ_BREADCRUMB(dev_priv));
if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
- if (dev_priv->sarea_priv) {
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
- }
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return 0;
}
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
i915_user_irq_get(dev);
DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
@@ -343,10 +358,6 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
}
- if (dev_priv->sarea_priv)
- dev_priv->sarea_priv->last_dispatch =
- READ_BREADCRUMB(dev_priv);
-
return ret;
}
@@ -427,6 +438,14 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
}
+void i915_enable_interrupt (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ opregion_enable_asle(dev);
+ dev_priv->irq_enabled = 1;
+}
+
+
/* Set the vblank monitor pipe
*/
int i915_vblank_pipe_set(struct drm_device *dev, void *data,
@@ -487,6 +506,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ atomic_set(&dev_priv->irq_received, 0);
+
I915_WRITE(HWSTAM, 0xeffe);
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
index 6126a60dc9cb..96e271986d2a 100644
--- a/drivers/gpu/drm/i915/i915_mem.c
+++ b/drivers/gpu/drm/i915/i915_mem.c
@@ -46,7 +46,8 @@
static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ drm_i915_sarea_t *sarea_priv = master_priv->sarea_priv;
struct drm_tex_region *list;
unsigned shift, nr;
unsigned start;
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
index 13ae731a33db..ff012835a386 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -257,8 +257,8 @@ void opregion_enable_asle(struct drm_device *dev)
static struct intel_opregion *system_opregion;
-int intel_opregion_video_event(struct notifier_block *nb, unsigned long val,
- void *data)
+static int intel_opregion_video_event(struct notifier_block *nb,
+ unsigned long val, void *data)
{
/* The only video events relevant to opregion are 0x80. These indicate
either a docking event, lid switch or display switch request. In
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 9d24aaeb8a45..47e6bafeb743 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -175,9 +175,26 @@
#define DISPLAY_PLANE_B (1<<20)
/*
- * Instruction and interrupt control regs
+ * Fence registers
*/
+#define FENCE_REG_830_0 0x2000
+#define I830_FENCE_START_MASK 0x07f80000
+#define I830_FENCE_TILING_Y_SHIFT 12
+#define I830_FENCE_SIZE_BITS(size) ((get_order(size >> 19) - 1) << 8)
+#define I830_FENCE_PITCH_SHIFT 4
+#define I830_FENCE_REG_VALID (1<<0)
+
+#define I915_FENCE_START_MASK 0x0ff00000
+#define I915_FENCE_SIZE_BITS(size) ((get_order(size >> 20) - 1) << 8)
+#define FENCE_REG_965_0 0x03000
+#define I965_FENCE_PITCH_SHIFT 2
+#define I965_FENCE_TILING_Y_SHIFT 1
+#define I965_FENCE_REG_VALID (1<<0)
+
+/*
+ * Instruction and interrupt control regs
+ */
#define PRB0_TAIL 0x02030
#define PRB0_HEAD 0x02034
#define PRB0_START 0x02038
@@ -245,6 +262,7 @@
#define CM0_RC_OP_FLUSH_DISABLE (1<<0)
#define GFX_FLSH_CNTL 0x02170 /* 915+ only */
+
/*
* Framebuffer compression (915+ only)
*/
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
new file mode 100644
index 000000000000..4ca82a025525
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_bios.h"
+
+
+static void *
+find_section(struct bdb_header *bdb, int section_id)
+{
+ u8 *base = (u8 *)bdb;
+ int index = 0;
+ u16 total, current_size;
+ u8 current_id;
+
+ /* skip to first section */
+ index += bdb->header_size;
+ total = bdb->bdb_size;
+
+ /* walk the sections looking for section_id */
+ while (index < total) {
+ current_id = *(base + index);
+ index++;
+ current_size = *((u16 *)(base + index));
+ index += 2;
+ if (current_id == section_id)
+ return base + index;
+ index += current_size;
+ }
+
+ return NULL;
+}
+
+/* Try to find panel data */
+static void
+parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
+{
+ struct bdb_lvds_options *lvds_options;
+ struct bdb_lvds_lfp_data *lvds_lfp_data;
+ struct bdb_lvds_lfp_data_entry *entry;
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
+
+ /* Defaults if we can't find VBT info */
+ dev_priv->lvds_dither = 0;
+ dev_priv->lvds_vbt = 0;
+
+ lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
+ if (!lvds_options)
+ return;
+
+ dev_priv->lvds_dither = lvds_options->pixel_dither;
+ if (lvds_options->panel_type == 0xff)
+ return;
+
+ lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
+ if (!lvds_lfp_data)
+ return;
+
+ dev_priv->lvds_vbt = 1;
+
+ entry = &lvds_lfp_data->data[lvds_options->panel_type];
+ dvo_timing = &entry->dvo_timing;
+
+ panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode),
+ DRM_MEM_DRIVER);
+
+ panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
+ dvo_timing->hactive_lo;
+ panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
+ panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
+ dvo_timing->hsync_pulse_width;
+ panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
+ ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
+
+ panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
+ dvo_timing->vactive_lo;
+ panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
+ dvo_timing->vsync_off;
+ panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
+ dvo_timing->vsync_pulse_width;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
+ ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
+ panel_fixed_mode->clock = dvo_timing->clock * 10;
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(panel_fixed_mode);
+
+ dev_priv->vbt_mode = panel_fixed_mode;
+
+ DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
+ return;
+}
+
+static void
+parse_general_features(struct drm_i915_private *dev_priv,
+ struct bdb_header *bdb)
+{
+ struct bdb_general_features *general;
+
+ /* Set sensible defaults in case we can't find the general block */
+ dev_priv->int_tv_support = 1;
+ dev_priv->int_crt_support = 1;
+
+ general = find_section(bdb, BDB_GENERAL_FEATURES);
+ if (general) {
+ dev_priv->int_tv_support = general->int_tv_support;
+ dev_priv->int_crt_support = general->int_crt_support;
+ }
+}
+
+/**
+ * intel_init_bios - initialize VBIOS settings & find VBT
+ * @dev: DRM device
+ *
+ * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
+ * to appropriate values.
+ *
+ * VBT existence is a sanity check that is relied on by other i830_bios.c code.
+ * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
+ * feed an updated VBT back through that, compared to what we'll fetch using
+ * this method of groping around in the BIOS data.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+bool
+intel_init_bios(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct pci_dev *pdev = dev->pdev;
+ struct vbt_header *vbt = NULL;
+ struct bdb_header *bdb;
+ u8 __iomem *bios;
+ size_t size;
+ int i;
+
+ bios = pci_map_rom(pdev, &size);
+ if (!bios)
+ return -1;
+
+ /* Scour memory looking for the VBT signature */
+ for (i = 0; i + 4 < size; i++) {
+ if (!memcmp(bios + i, "$VBT", 4)) {
+ vbt = (struct vbt_header *)(bios + i);
+ break;
+ }
+ }
+
+ if (!vbt) {
+ DRM_ERROR("VBT signature missing\n");
+ pci_unmap_rom(pdev, bios);
+ return -1;
+ }
+
+ bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
+
+ /* Grab useful general definitions */
+ parse_general_features(dev_priv, bdb);
+ parse_panel_data(dev_priv, bdb);
+
+ pci_unmap_rom(pdev, bios);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
new file mode 100644
index 000000000000..5ea715ace3a0
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -0,0 +1,405 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#ifndef _I830_BIOS_H_
+#define _I830_BIOS_H_
+
+#include "drmP.h"
+
+struct vbt_header {
+ u8 signature[20]; /**< Always starts with 'VBT$' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 vbt_size; /**< in bytes */
+ u8 vbt_checksum;
+ u8 reserved0;
+ u32 bdb_offset; /**< from beginning of VBT */
+ u32 aim_offset[4]; /**< from beginning of VBT */
+} __attribute__((packed));
+
+struct bdb_header {
+ u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */
+ u16 version; /**< decimal */
+ u16 header_size; /**< in bytes */
+ u16 bdb_size; /**< in bytes */
+};
+
+/* strictly speaking, this is a "skip" block, but it has interesting info */
+struct vbios_data {
+ u8 type; /* 0 == desktop, 1 == mobile */
+ u8 relstage;
+ u8 chipset;
+ u8 lvds_present:1;
+ u8 tv_present:1;
+ u8 rsvd2:6; /* finish byte */
+ u8 rsvd3[4];
+ u8 signon[155];
+ u8 copyright[61];
+ u16 code_segment;
+ u8 dos_boot_mode;
+ u8 bandwidth_percent;
+ u8 rsvd4; /* popup memory size */
+ u8 resize_pci_bios;
+ u8 rsvd5; /* is crt already on ddc2 */
+} __attribute__((packed));
+
+/*
+ * There are several types of BIOS data blocks (BDBs), each block has
+ * an ID and size in the first 3 bytes (ID in first, size in next 2).
+ * Known types are listed below.
+ */
+#define BDB_GENERAL_FEATURES 1
+#define BDB_GENERAL_DEFINITIONS 2
+#define BDB_OLD_TOGGLE_LIST 3
+#define BDB_MODE_SUPPORT_LIST 4
+#define BDB_GENERIC_MODE_TABLE 5
+#define BDB_EXT_MMIO_REGS 6
+#define BDB_SWF_IO 7
+#define BDB_SWF_MMIO 8
+#define BDB_DOT_CLOCK_TABLE 9
+#define BDB_MODE_REMOVAL_TABLE 10
+#define BDB_CHILD_DEVICE_TABLE 11
+#define BDB_DRIVER_FEATURES 12
+#define BDB_DRIVER_PERSISTENCE 13
+#define BDB_EXT_TABLE_PTRS 14
+#define BDB_DOT_CLOCK_OVERRIDE 15
+#define BDB_DISPLAY_SELECT 16
+/* 17 rsvd */
+#define BDB_DRIVER_ROTATION 18
+#define BDB_DISPLAY_REMOVE 19
+#define BDB_OEM_CUSTOM 20
+#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */
+#define BDB_SDVO_LVDS_OPTIONS 22
+#define BDB_SDVO_PANEL_DTDS 23
+#define BDB_SDVO_LVDS_PNP_IDS 24
+#define BDB_SDVO_LVDS_POWER_SEQ 25
+#define BDB_TV_OPTIONS 26
+#define BDB_LVDS_OPTIONS 40
+#define BDB_LVDS_LFP_DATA_PTRS 41
+#define BDB_LVDS_LFP_DATA 42
+#define BDB_LVDS_BACKLIGHT 43
+#define BDB_LVDS_POWER 44
+#define BDB_SKIP 254 /* VBIOS private block, ignore */
+
+struct bdb_general_features {
+ /* bits 1 */
+ u8 panel_fitting:2;
+ u8 flexaim:1;
+ u8 msg_enable:1;
+ u8 clear_screen:3;
+ u8 color_flip:1;
+
+ /* bits 2 */
+ u8 download_ext_vbt:1;
+ u8 enable_ssc:1;
+ u8 ssc_freq:1;
+ u8 enable_lfp_on_override:1;
+ u8 disable_ssc_ddt:1;
+ u8 rsvd8:3; /* finish byte */
+
+ /* bits 3 */
+ u8 disable_smooth_vision:1;
+ u8 single_dvi:1;
+ u8 rsvd9:6; /* finish byte */
+
+ /* bits 4 */
+ u8 legacy_monitor_detect;
+
+ /* bits 5 */
+ u8 int_crt_support:1;
+ u8 int_tv_support:1;
+ u8 rsvd11:6; /* finish byte */
+} __attribute__((packed));
+
+struct bdb_general_definitions {
+ /* DDC GPIO */
+ u8 crt_ddc_gmbus_pin;
+
+ /* DPMS bits */
+ u8 dpms_acpi:1;
+ u8 skip_boot_crt_detect:1;
+ u8 dpms_aim:1;
+ u8 rsvd1:5; /* finish byte */
+
+ /* boot device bits */
+ u8 boot_display[2];
+ u8 child_dev_size;
+
+ /* device info */
+ u8 tv_or_lvds_info[33];
+ u8 dev1[33];
+ u8 dev2[33];
+ u8 dev3[33];
+ u8 dev4[33];
+ /* may be another device block here on some platforms */
+};
+
+struct bdb_lvds_options {
+ u8 panel_type;
+ u8 rsvd1;
+ /* LVDS capabilities, stored in a dword */
+ u8 rsvd2:1;
+ u8 lvds_edid:1;
+ u8 pixel_dither:1;
+ u8 pfit_ratio_auto:1;
+ u8 pfit_gfx_mode_enhanced:1;
+ u8 pfit_text_mode_enhanced:1;
+ u8 pfit_mode:2;
+ u8 rsvd4;
+} __attribute__((packed));
+
+/* LFP pointer table contains entries to the struct below */
+struct bdb_lvds_lfp_data_ptr {
+ u16 fp_timing_offset; /* offsets are from start of bdb */
+ u8 fp_table_size;
+ u16 dvo_timing_offset;
+ u8 dvo_table_size;
+ u16 panel_pnp_id_offset;
+ u8 pnp_table_size;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_ptrs {
+ u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */
+ struct bdb_lvds_lfp_data_ptr ptr[16];
+} __attribute__((packed));
+
+/* LFP data has 3 blocks per entry */
+struct lvds_fp_timing {
+ u16 x_res;
+ u16 y_res;
+ u32 lvds_reg;
+ u32 lvds_reg_val;
+ u32 pp_on_reg;
+ u32 pp_on_reg_val;
+ u32 pp_off_reg;
+ u32 pp_off_reg_val;
+ u32 pp_cycle_reg;
+ u32 pp_cycle_reg_val;
+ u32 pfit_reg;
+ u32 pfit_reg_val;
+ u16 terminator;
+} __attribute__((packed));
+
+struct lvds_dvo_timing {
+ u16 clock; /**< In 10khz */
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_off_lo;
+ u8 hsync_pulse_width;
+ u8 vsync_pulse_width:4;
+ u8 vsync_off:4;
+ u8 rsvd0:6;
+ u8 hsync_off_hi:2;
+ u8 h_image;
+ u8 v_image;
+ u8 max_hv;
+ u8 h_border;
+ u8 v_border;
+ u8 rsvd1:3;
+ u8 digital:2;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 rsvd2:1;
+} __attribute__((packed));
+
+struct lvds_pnp_id {
+ u16 mfg_name;
+ u16 product_code;
+ u32 serial;
+ u8 mfg_week;
+ u8 mfg_year;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data_entry {
+ struct lvds_fp_timing fp_timing;
+ struct lvds_dvo_timing dvo_timing;
+ struct lvds_pnp_id pnp_id;
+} __attribute__((packed));
+
+struct bdb_lvds_lfp_data {
+ struct bdb_lvds_lfp_data_entry data[16];
+} __attribute__((packed));
+
+struct aimdb_header {
+ char signature[16];
+ char oem_device[20];
+ u16 aimdb_version;
+ u16 aimdb_header_size;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct aimdb_block {
+ u8 aimdb_id;
+ u16 aimdb_size;
+} __attribute__((packed));
+
+struct vch_panel_data {
+ u16 fp_timing_offset;
+ u8 fp_timing_size;
+ u16 dvo_timing_offset;
+ u8 dvo_timing_size;
+ u16 text_fitting_offset;
+ u8 text_fitting_size;
+ u16 graphics_fitting_offset;
+ u8 graphics_fitting_size;
+} __attribute__((packed));
+
+struct vch_bdb_22 {
+ struct aimdb_block aimdb_block;
+ struct vch_panel_data panels[16];
+} __attribute__((packed));
+
+bool intel_init_bios(struct drm_device *dev);
+
+/*
+ * Driver<->VBIOS interaction occurs through scratch bits in
+ * GR18 & SWF*.
+ */
+
+/* GR18 bits are set on display switch and hotkey events */
+#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */
+#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */
+#define GR18_HK_NONE (0x0<<3)
+#define GR18_HK_LFP_STRETCH (0x1<<3)
+#define GR18_HK_TOGGLE_DISP (0x2<<3)
+#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */
+#define GR18_HK_POPUP_DISABLED (0x6<<3)
+#define GR18_HK_POPUP_ENABLED (0x7<<3)
+#define GR18_HK_PFIT (0x8<<3)
+#define GR18_HK_APM_CHANGE (0xa<<3)
+#define GR18_HK_MULTIPLE (0xc<<3)
+#define GR18_USER_INT_EN (1<<2)
+#define GR18_A0000_FLUSH_EN (1<<1)
+#define GR18_SMM_EN (1<<0)
+
+/* Set by driver, cleared by VBIOS */
+#define SWF00_YRES_SHIFT 16
+#define SWF00_XRES_SHIFT 0
+#define SWF00_RES_MASK 0xffff
+
+/* Set by VBIOS at boot time and driver at runtime */
+#define SWF01_TV2_FORMAT_SHIFT 8
+#define SWF01_TV1_FORMAT_SHIFT 0
+#define SWF01_TV_FORMAT_MASK 0xffff
+
+#define SWF10_VBIOS_BLC_I2C_EN (1<<29)
+#define SWF10_GTT_OVERRIDE_EN (1<<28)
+#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */
+#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24)
+#define SWF10_OLD_TOGGLE 0x0
+#define SWF10_TOGGLE_LIST_1 0x1
+#define SWF10_TOGGLE_LIST_2 0x2
+#define SWF10_TOGGLE_LIST_3 0x3
+#define SWF10_TOGGLE_LIST_4 0x4
+#define SWF10_PANNING_EN (1<<23)
+#define SWF10_DRIVER_LOADED (1<<22)
+#define SWF10_EXTENDED_DESKTOP (1<<21)
+#define SWF10_EXCLUSIVE_MODE (1<<20)
+#define SWF10_OVERLAY_EN (1<<19)
+#define SWF10_PLANEB_HOLDOFF (1<<18)
+#define SWF10_PLANEA_HOLDOFF (1<<17)
+#define SWF10_VGA_HOLDOFF (1<<16)
+#define SWF10_ACTIVE_DISP_MASK 0xffff
+#define SWF10_PIPEB_LFP2 (1<<15)
+#define SWF10_PIPEB_EFP2 (1<<14)
+#define SWF10_PIPEB_TV2 (1<<13)
+#define SWF10_PIPEB_CRT2 (1<<12)
+#define SWF10_PIPEB_LFP (1<<11)
+#define SWF10_PIPEB_EFP (1<<10)
+#define SWF10_PIPEB_TV (1<<9)
+#define SWF10_PIPEB_CRT (1<<8)
+#define SWF10_PIPEA_LFP2 (1<<7)
+#define SWF10_PIPEA_EFP2 (1<<6)
+#define SWF10_PIPEA_TV2 (1<<5)
+#define SWF10_PIPEA_CRT2 (1<<4)
+#define SWF10_PIPEA_LFP (1<<3)
+#define SWF10_PIPEA_EFP (1<<2)
+#define SWF10_PIPEA_TV (1<<1)
+#define SWF10_PIPEA_CRT (1<<0)
+
+#define SWF11_MEMORY_SIZE_SHIFT 16
+#define SWF11_SV_TEST_EN (1<<15)
+#define SWF11_IS_AGP (1<<14)
+#define SWF11_DISPLAY_HOLDOFF (1<<13)
+#define SWF11_DPMS_REDUCED (1<<12)
+#define SWF11_IS_VBE_MODE (1<<11)
+#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */
+#define SWF11_DPMS_MASK 0x07
+#define SWF11_DPMS_OFF (1<<2)
+#define SWF11_DPMS_SUSPEND (1<<1)
+#define SWF11_DPMS_STANDBY (1<<0)
+#define SWF11_DPMS_ON 0
+
+#define SWF14_GFX_PFIT_EN (1<<31)
+#define SWF14_TEXT_PFIT_EN (1<<30)
+#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */
+#define SWF14_POPUP_EN (1<<28)
+#define SWF14_DISPLAY_HOLDOFF (1<<27)
+#define SWF14_DISP_DETECT_EN (1<<26)
+#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */
+#define SWF14_DRIVER_STATUS (1<<24)
+#define SWF14_OS_TYPE_WIN9X (1<<23)
+#define SWF14_OS_TYPE_WINNT (1<<22)
+/* 21:19 rsvd */
+#define SWF14_PM_TYPE_MASK 0x00070000
+#define SWF14_PM_ACPI_VIDEO (0x4 << 16)
+#define SWF14_PM_ACPI (0x3 << 16)
+#define SWF14_PM_APM_12 (0x2 << 16)
+#define SWF14_PM_APM_11 (0x1 << 16)
+#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */
+ /* if GR18 indicates a display switch */
+#define SWF14_DS_PIPEB_LFP2_EN (1<<15)
+#define SWF14_DS_PIPEB_EFP2_EN (1<<14)
+#define SWF14_DS_PIPEB_TV2_EN (1<<13)
+#define SWF14_DS_PIPEB_CRT2_EN (1<<12)
+#define SWF14_DS_PIPEB_LFP_EN (1<<11)
+#define SWF14_DS_PIPEB_EFP_EN (1<<10)
+#define SWF14_DS_PIPEB_TV_EN (1<<9)
+#define SWF14_DS_PIPEB_CRT_EN (1<<8)
+#define SWF14_DS_PIPEA_LFP2_EN (1<<7)
+#define SWF14_DS_PIPEA_EFP2_EN (1<<6)
+#define SWF14_DS_PIPEA_TV2_EN (1<<5)
+#define SWF14_DS_PIPEA_CRT2_EN (1<<4)
+#define SWF14_DS_PIPEA_LFP_EN (1<<3)
+#define SWF14_DS_PIPEA_EFP_EN (1<<2)
+#define SWF14_DS_PIPEA_TV_EN (1<<1)
+#define SWF14_DS_PIPEA_CRT_EN (1<<0)
+ /* if GR18 indicates a panel fitting request */
+#define SWF14_PFIT_EN (1<<0) /* 0 means disable */
+ /* if GR18 indicates an APM change request */
+#define SWF14_APM_HIBERNATE 0x4
+#define SWF14_APM_SUSPEND 0x3
+#define SWF14_APM_STANDBY 0x1
+#define SWF14_APM_RESTORE 0x0
+
+#endif /* _I830_BIOS_H_ */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
new file mode 100644
index 000000000000..dcaed3466e83
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ temp = I915_READ(ADPA);
+ temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
+ temp &= ~ADPA_DAC_ENABLE;
+
+ switch(mode) {
+ case DRM_MODE_DPMS_ON:
+ temp |= ADPA_DAC_ENABLE;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ case DRM_MODE_DPMS_OFF:
+ temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+ break;
+ }
+
+ I915_WRITE(ADPA, temp);
+}
+
+static int intel_crt_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->clock > 400000 || mode->clock < 25000)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static bool intel_crt_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void intel_crt_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int dpll_md_reg;
+ u32 adpa, dpll_md;
+
+ if (intel_crtc->pipe == 0)
+ dpll_md_reg = DPLL_A_MD;
+ else
+ dpll_md_reg = DPLL_B_MD;
+
+ /*
+ * Disable separate mode multiplier used when cloning SDVO to CRT
+ * XXX this needs to be adjusted when we really are cloning
+ */
+ if (IS_I965G(dev)) {
+ dpll_md = I915_READ(dpll_md_reg);
+ I915_WRITE(dpll_md_reg,
+ dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+ }
+
+ adpa = 0;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ if (intel_crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
+
+ I915_WRITE(ADPA, adpa);
+}
+
+/**
+ * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
+ *
+ * Not for i915G/i915GM
+ *
+ * \return true if CRT is connected.
+ * \return false if CRT is disconnected.
+ */
+static bool intel_crt_detect_hotplug(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp;
+
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ temp = I915_READ(PORT_HOTPLUG_EN);
+
+ I915_WRITE(PORT_HOTPLUG_EN,
+ temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+
+ if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
+ CRT_HOTPLUG_MONITOR_COLOR)
+ return true;
+
+ return false;
+}
+
+static bool intel_crt_detect_ddc(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* CRT should always be at 0, but check anyway */
+ if (intel_output->type != INTEL_OUTPUT_ANALOG)
+ return false;
+
+ return intel_ddc_probe(intel_output);
+}
+
+static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
+ if (intel_crt_detect_hotplug(connector))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+ }
+
+ if (intel_crt_detect_ddc(connector))
+ return connector_status_connected;
+
+ /* TODO use load detect */
+ return connector_status_unknown;
+}
+
+static void intel_crt_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static int intel_crt_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ return intel_ddc_get_modes(intel_output);
+}
+
+static int intel_crt_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (property == dev->mode_config.dpms_property && connector->encoder)
+ intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf));
+
+ return 0;
+}
+
+/*
+ * Routines for controlling stuff on the analog port
+ */
+
+static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
+ .dpms = intel_crt_dpms,
+ .mode_fixup = intel_crt_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .commit = intel_encoder_commit,
+ .mode_set = intel_crt_mode_set,
+};
+
+static const struct drm_connector_funcs intel_crt_connector_funcs = {
+ .detect = intel_crt_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_crt_destroy,
+ .set_property = intel_crt_set_property,
+};
+
+static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
+ .mode_valid = intel_crt_mode_valid,
+ .get_modes = intel_crt_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_crt_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_crt_enc_funcs = {
+ .destroy = intel_crt_enc_destroy,
+};
+
+void intel_crt_init(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ connector = &intel_output->base;
+ drm_connector_init(dev, &intel_output->base,
+ &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
+ DRM_MODE_ENCODER_DAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ return;
+ }
+
+ intel_output->type = INTEL_OUTPUT_ANALOG;
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
+ drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
new file mode 100644
index 000000000000..e5c1c80d1f90
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -0,0 +1,1618 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+#include "drm_crtc_helper.h"
+
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+
+typedef struct {
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
+} intel_clock_t;
+
+typedef struct {
+ int min, max;
+} intel_range_t;
+
+typedef struct {
+ int dot_limit;
+ int p2_slow, p2_fast;
+} intel_p2_t;
+
+#define INTEL_P2_NUM 2
+
+typedef struct {
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN 25000
+#define I8XX_DOT_MAX 350000
+#define I8XX_VCO_MIN 930000
+#define I8XX_VCO_MAX 1400000
+#define I8XX_N_MIN 3
+#define I8XX_N_MAX 16
+#define I8XX_M_MIN 96
+#define I8XX_M_MAX 140
+#define I8XX_M1_MIN 18
+#define I8XX_M1_MAX 26
+#define I8XX_M2_MIN 6
+#define I8XX_M2_MAX 16
+#define I8XX_P_MIN 4
+#define I8XX_P_MAX 128
+#define I8XX_P1_MIN 2
+#define I8XX_P1_MAX 33
+#define I8XX_P1_LVDS_MIN 1
+#define I8XX_P1_LVDS_MAX 6
+#define I8XX_P2_SLOW 4
+#define I8XX_P2_FAST 2
+#define I8XX_P2_LVDS_SLOW 14
+#define I8XX_P2_LVDS_FAST 14 /* No fast option */
+#define I8XX_P2_SLOW_LIMIT 165000
+
+#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MAX 400000
+#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MAX 2800000
+#define I9XX_N_MIN 3
+#define I9XX_N_MAX 8
+#define I9XX_M_MIN 70
+#define I9XX_M_MAX 120
+#define I9XX_M1_MIN 10
+#define I9XX_M1_MAX 20
+#define I9XX_M2_MIN 5
+#define I9XX_M2_MAX 9
+#define I9XX_P_SDVO_DAC_MIN 5
+#define I9XX_P_SDVO_DAC_MAX 80
+#define I9XX_P_LVDS_MIN 7
+#define I9XX_P_LVDS_MAX 98
+#define I9XX_P1_MIN 1
+#define I9XX_P1_MAX 8
+#define I9XX_P2_SDVO_DAC_SLOW 10
+#define I9XX_P2_SDVO_DAC_FAST 5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
+#define I9XX_P2_LVDS_SLOW 14
+#define I9XX_P2_LVDS_FAST 7
+#define I9XX_P2_LVDS_SLOW_LIMIT 112000
+
+#define INTEL_LIMIT_I8XX_DVO_DAC 0
+#define INTEL_LIMIT_I8XX_LVDS 1
+#define INTEL_LIMIT_I9XX_SDVO_DAC 2
+#define INTEL_LIMIT_I9XX_LVDS 3
+
+static const intel_limit_t intel_limits[] = {
+ { /* INTEL_LIMIT_I8XX_DVO_DAC */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ },
+ { /* INTEL_LIMIT_I8XX_LVDS */
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+ .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX },
+ .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX },
+ .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX },
+ .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX },
+ .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX },
+ .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX },
+ .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_SDVO_DAC */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ },
+ { /* INTEL_LIMIT_I9XX_LVDS */
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+ .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX },
+ .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* The single-channel range is 25-112Mhz, and dual-channel
+ * is 80-224Mhz. Prefer single channel as much as possible.
+ */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ },
+};
+
+static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ const intel_limit_t *limit;
+
+ if (IS_I9XX(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+ } else {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
+ else
+ limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
+ }
+ return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(struct drm_device *dev, int refclk,
+ intel_clock_t *clock)
+{
+ if (IS_I9XX(dev))
+ i9xx_clock (refclk, clock);
+ else
+ i8xx_clock (refclk, clock);
+}
+
+/**
+ * Returns whether any output on the specified pipe is of the specified type
+ */
+bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *l_entry;
+
+ list_for_each_entry(l_entry, &mode_config->connector_list, head) {
+ if (l_entry->encoder &&
+ l_entry->encoder->crtc == crtc) {
+ struct intel_output *intel_output = to_intel_output(l_entry);
+ if (intel_output->type == type)
+ return true;
+ }
+ }
+ return false;
+}
+
+#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; }
+/**
+ * Returns whether the given set of divisors are valid for a given refclk with
+ * the given connectors.
+ */
+
+static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+{
+ const intel_limit_t *limit = intel_limit (crtc);
+
+ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
+ INTELPllInvalid ("p1 out of range\n");
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ INTELPllInvalid ("p out of range\n");
+ if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
+ INTELPllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ INTELPllInvalid ("m1 out of range\n");
+ if (clock->m1 <= clock->m2)
+ INTELPllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid ("m out of range\n");
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ INTELPllInvalid ("n out of range\n");
+ if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+ INTELPllInvalid ("vco out of range\n");
+ /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
+ * connector, etc., rather than just a single range.
+ */
+ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+ INTELPllInvalid ("dot out of range\n");
+
+ return true;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
+ int refclk, intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_clock_t clock;
+ const intel_limit_t *limit = intel_limit(crtc);
+ int err = target;
+
+ if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ (I915_READ(LVDS) & LVDS_PORT_EN) != 0) {
+ /*
+ * For LVDS, if the panel is on, just rely on its current
+ * settings for dual-channel. We haven't figured out how to
+ * reliably set up different single/dual channel state, if we
+ * even can.
+ */
+ if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+ clock.p2 = limit->p2.p2_slow;
+ } else {
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
+ }
+
+ memset (best_clock, 0, sizeof (*best_clock));
+
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+ for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
+ clock.m2 <= limit->m2.max; clock.m2++) {
+ for (clock.n = limit->n.min; clock.n <= limit->n.max;
+ clock.n++) {
+ for (clock.p1 = limit->p1.min;
+ clock.p1 <= limit->p1.max; clock.p1++) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+
+ if (!intel_PLL_is_valid(crtc, &clock))
+ continue;
+
+ this_err = abs(clock.dot - target);
+ if (this_err < err) {
+ *best_clock = clock;
+ err = this_err;
+ }
+ }
+ }
+ }
+ }
+
+ return (err != target);
+}
+
+void
+intel_wait_for_vblank(struct drm_device *dev)
+{
+ /* Wait for 20ms, i.e. one cycle at 50hz. */
+ udelay(20000);
+}
+
+static void
+intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_master_private *master_priv;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_i915_gem_object *obj_priv;
+ struct drm_gem_object *obj;
+ int pipe = intel_crtc->pipe;
+ unsigned long Start, Offset;
+ int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
+ int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+ int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ u32 dspcntr, alignment;
+
+ /* no fb bound */
+ if (!crtc->fb) {
+ DRM_DEBUG("No FB bound\n");
+ return;
+ }
+
+ intel_fb = to_intel_framebuffer(crtc->fb);
+ obj = intel_fb->obj;
+ obj_priv = obj->driver_private;
+
+ switch (obj_priv->tiling_mode) {
+ case I915_TILING_NONE:
+ alignment = 64 * 1024;
+ break;
+ case I915_TILING_X:
+ if (IS_I9XX(dev))
+ alignment = 1024 * 1024;
+ else
+ alignment = 512 * 1024;
+ break;
+ case I915_TILING_Y:
+ /* FIXME: Is this true? */
+ DRM_ERROR("Y tiled not allowed for scan out buffers\n");
+ return;
+ default:
+ BUG();
+ }
+
+ if (i915_gem_object_pin(intel_fb->obj, alignment))
+ return;
+
+ i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
+
+ Start = obj_priv->gtt_offset;
+ Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
+ I915_WRITE(dspstride, crtc->fb->pitch);
+
+ dspcntr = I915_READ(dspcntr_reg);
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (crtc->fb->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 24:
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+ return;
+ }
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+ if (IS_I965G(dev)) {
+ I915_WRITE(dspbase, Offset);
+ I915_READ(dspbase);
+ I915_WRITE(dspsurf, Start);
+ I915_READ(dspsurf);
+ } else {
+ I915_WRITE(dspbase, Start + Offset);
+ I915_READ(dspbase);
+ }
+
+ intel_wait_for_vblank(dev);
+
+ if (old_fb) {
+ intel_fb = to_intel_framebuffer(old_fb);
+ i915_gem_object_unpin(intel_fb->obj);
+ }
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->pipeA_x = x;
+ master_priv->sarea_priv->pipeA_y = y;
+ break;
+ case 1:
+ master_priv->sarea_priv->pipeB_x = x;
+ master_priv->sarea_priv->pipeB_y = y;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+}
+
+
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_master_private *master_priv;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 temp;
+ bool enabled;
+
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ /* Enable the DPLL */
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ I915_WRITE(dpll_reg, temp);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+ }
+
+ /* Enable the pipe */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0)
+ I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+
+ /* Enable the plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ intel_crtc_load_lut(crtc);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, true); TODO
+ break;
+ case DRM_MODE_DPMS_OFF:
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+ //intel_crtc_dpms_video(crtc, FALSE); TODO
+
+ /* Disable the VGA plane that we never use */
+ I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ I915_READ(dspbase_reg);
+ }
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ /* Next, disable display pipes */
+ temp = I915_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+ I915_READ(pipeconf_reg);
+ }
+
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ temp = I915_READ(dpll_reg);
+ if ((temp & DPLL_VCO_ENABLE) != 0) {
+ I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ }
+
+ /* Wait for the clocks to turn off. */
+ udelay(150);
+ break;
+ }
+
+ if (!dev->primary->master)
+ return;
+
+ master_priv = dev->primary->master->driver_priv;
+ if (!master_priv->sarea_priv)
+ return;
+
+ enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
+
+ switch (pipe) {
+ case 0:
+ master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ case 1:
+ master_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0;
+ master_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0;
+ break;
+ default:
+ DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+ break;
+ }
+
+ intel_crtc->dpms_mode = mode;
+}
+
+static void intel_crtc_prepare (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void intel_crtc_commit (struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+void intel_encoder_prepare (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of prepare see intel_lvds_prepare */
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+void intel_encoder_commit (struct drm_encoder *encoder)
+{
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ /* lvds has its own version of commit see intel_lvds_commit */
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+
+/** Returns the core display clock speed for i830 - i945 */
+static int intel_get_core_clock_speed(struct drm_device *dev)
+{
+
+ /* Core clock values taken from the published datasheets.
+ * The 830 may go up to 166 Mhz, which we should check.
+ */
+ if (IS_I945G(dev))
+ return 400000;
+ else if (IS_I915G(dev))
+ return 333000;
+ else if (IS_I945GM(dev) || IS_845G(dev))
+ return 200000;
+ else if (IS_I915GM(dev)) {
+ u16 gcfgc = 0;
+
+ pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+ if (gcfgc & GC_LOW_FREQUENCY_ENABLE)
+ return 133000;
+ else {
+ switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
+ case GC_DISPLAY_CLOCK_333_MHZ:
+ return 333000;
+ default:
+ case GC_DISPLAY_CLOCK_190_200_MHZ:
+ return 190000;
+ }
+ }
+ } else if (IS_I865G(dev))
+ return 266000;
+ else if (IS_I855(dev)) {
+ u16 hpllcc = 0;
+ /* Assume that the hardware is in the high speed state. This
+ * should be the default.
+ */
+ switch (hpllcc & GC_CLOCK_CONTROL_MASK) {
+ case GC_CLOCK_133_200:
+ case GC_CLOCK_100_200:
+ return 200000;
+ case GC_CLOCK_166_250:
+ return 250000;
+ case GC_CLOCK_100_133:
+ return 133000;
+ }
+ } else /* 852, 830 */
+ return 133000;
+
+ return 0; /* Silence gcc warning */
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int intel_panel_fitter_pipe (struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pfit_control;
+
+ /* i830 doesn't have a panel fitter */
+ if (IS_I830(dev))
+ return -1;
+
+ pfit_control = I915_READ(PFIT_CONTROL);
+
+ /* See if the panel fitter is in use */
+ if ((pfit_control & PFIT_ENABLE) == 0)
+ return -1;
+
+ /* 965 can place panel fitter on either pipe */
+ if (IS_I965G(dev))
+ return (pfit_control >> 29) & 0x3;
+
+ /* older chips can only use pipe 1 */
+ return 1;
+}
+
+static void intel_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int fp_reg = (pipe == 0) ? FPA0 : FPB0;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
+ int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+ int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+ int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+ int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+ int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+ int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+ int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+ int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+ int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ int refclk;
+ intel_clock_t clock;
+ u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+ bool ok, is_sdvo = false, is_dvo = false;
+ bool is_crt = false, is_lvds = false, is_tv = false;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_connector *connector;
+
+ drm_vblank_pre_modeset(dev, pipe);
+
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (!connector->encoder || connector->encoder->crtc != crtc)
+ continue;
+
+ switch (intel_output->type) {
+ case INTEL_OUTPUT_LVDS:
+ is_lvds = true;
+ break;
+ case INTEL_OUTPUT_SDVO:
+ is_sdvo = true;
+ break;
+ case INTEL_OUTPUT_DVO:
+ is_dvo = true;
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ is_tv = true;
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ is_crt = true;
+ break;
+ }
+ }
+
+ if (IS_I9XX(dev)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+
+ ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+ if (!ok) {
+ DRM_ERROR("Couldn't find PLL settings for mode!\n");
+ return;
+ }
+
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+ dpll = DPLL_VGA_MODE_DIS;
+ if (IS_I9XX(dev)) {
+ if (is_lvds)
+ dpll |= DPLLB_MODE_LVDS;
+ else
+ dpll |= DPLLB_MODE_DAC_SERIAL;
+ if (is_sdvo) {
+ dpll |= DPLL_DVO_HIGH_SPEED;
+ if (IS_I945G(dev) || IS_I945GM(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+ }
+ }
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << 16;
+ switch (clock.p2) {
+ case 5:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+ break;
+ case 7:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+ break;
+ case 10:
+ dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+ break;
+ case 14:
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+ if (IS_I965G(dev))
+ dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+ } else {
+ if (is_lvds) {
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ } else {
+ if (clock.p1 == 2)
+ dpll |= PLL_P1_DIVIDE_BY_TWO;
+ else
+ dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ if (clock.p2 == 4)
+ dpll |= PLL_P2_DIVIDE_BY_4;
+ }
+ }
+
+ if (is_tv) {
+ /* XXX: just matching BIOS for now */
+/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
+ dpll |= 3;
+ }
+ else
+ dpll |= PLL_REF_INPUT_DREFCLK;
+
+ /* setup pipeconf */
+ pipeconf = I915_READ(pipeconf_reg);
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ if (pipe == 0)
+ dspcntr |= DISPPLANE_SEL_PIPE_A;
+ else
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+ if (pipe == 0 && !IS_I965G(dev)) {
+ /* Enable pixel doubling when the dot clock is > 90% of the (display)
+ * core speed.
+ *
+ * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
+ * pipe == 0 check?
+ */
+ if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
+ pipeconf |= PIPEACONF_DOUBLE_WIDE;
+ else
+ pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
+ }
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+ pipeconf |= PIPEACONF_ENABLE;
+ dpll |= DPLL_VCO_ENABLE;
+
+
+ /* Disable the panel fitter if it was on our pipe */
+ if (intel_panel_fitter_pipe(dev) == pipe)
+ I915_WRITE(PFIT_CONTROL, 0);
+
+ DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(mode);
+
+
+ if (dpll & DPLL_VCO_ENABLE) {
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+ I915_READ(dpll_reg);
+ 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.
+ */
+ if (is_lvds) {
+ u32 lvds = I915_READ(LVDS);
+
+ lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (clock.p2 == 7)
+ lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
+ */
+
+ I915_WRITE(LVDS, lvds);
+ I915_READ(LVDS);
+ }
+
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll);
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ if (IS_I965G(dev)) {
+ int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+ ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+ } else {
+ /* write it again -- the BIOS does, after all */
+ I915_WRITE(dpll_reg, dpll);
+ }
+ I915_READ(dpll_reg);
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+ I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+ I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+ /* pipesrc and dspsize control the size that is scaled from, which should
+ * always be the user's requested size.
+ */
+ I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+ I915_WRITE(dsppos_reg, 0);
+ I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_READ(pipeconf_reg);
+
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(dspcntr_reg, dspcntr);
+
+ /* Flush the plane changes */
+ intel_pipe_set_base(crtc, x, y, old_fb);
+
+ drm_vblank_post_modeset(dev, pipe);
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+void intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
+ int i;
+
+ /* The clocks have to be on to load the palette. */
+ if (!crtc->enabled)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ I915_WRITE(palreg + 4 * i,
+ (intel_crtc->lut_r[i] << 16) |
+ (intel_crtc->lut_g[i] << 8) |
+ intel_crtc->lut_b[i]);
+ }
+}
+
+static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+ uint32_t handle,
+ uint32_t width, uint32_t height)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_gem_object *bo;
+ struct drm_i915_gem_object *obj_priv;
+ int pipe = intel_crtc->pipe;
+ uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
+ uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
+ uint32_t temp;
+ size_t addr;
+
+ DRM_DEBUG("\n");
+
+ /* if we want to turn off the cursor ignore width and height */
+ if (!handle) {
+ DRM_DEBUG("cursor off\n");
+ /* turn of the cursor */
+ temp = 0;
+ temp |= CURSOR_MODE_DISABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, 0);
+ return 0;
+ }
+
+ /* Currently we only support 64x64 cursors */
+ if (width != 64 || height != 64) {
+ DRM_ERROR("we currently only support 64x64 cursors\n");
+ return -EINVAL;
+ }
+
+ bo = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!bo)
+ return -ENOENT;
+
+ obj_priv = bo->driver_private;
+
+ if (bo->size < width * height * 4) {
+ DRM_ERROR("buffer is to small\n");
+ drm_gem_object_unreference(bo);
+ return -ENOMEM;
+ }
+
+ if (dev_priv->cursor_needs_physical) {
+ addr = dev->agp->base + obj_priv->gtt_offset;
+ } else {
+ addr = obj_priv->gtt_offset;
+ }
+
+ intel_crtc->cursor_addr = addr;
+ temp = 0;
+ /* set the pipe for the cursor */
+ temp |= (pipe << 28);
+ temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+
+ I915_WRITE(control, temp);
+ I915_WRITE(base, addr);
+
+ return 0;
+}
+
+static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ uint32_t temp = 0;
+ uint32_t adder;
+
+ if (x < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+ x = -x;
+ }
+ if (y < 0) {
+ temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+ y = -y;
+ }
+
+ temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
+ temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+
+ adder = intel_crtc->cursor_addr;
+ I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
+ I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder);
+
+ return 0;
+}
+
+/** Sets the color ramps on behalf of RandR */
+void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ intel_crtc->lut_r[regno] = red >> 8;
+ intel_crtc->lut_g[regno] = green >> 8;
+ intel_crtc->lut_b[regno] = blue >> 8;
+}
+
+static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, uint32_t size)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int i;
+
+ if (size != 256)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = red[i] >> 8;
+ intel_crtc->lut_g[i] = green[i] >> 8;
+ intel_crtc->lut_b[i] = blue[i] >> 8;
+ }
+
+ intel_crtc_load_lut(crtc);
+}
+
+/**
+ * Get a pipe with a simple mode set on it for doing load-based monitor
+ * detection.
+ *
+ * It will be up to the load-detect code to adjust the pipe as appropriate for
+ * its requirements. The pipe will be connected to no other outputs.
+ *
+ * Currently this code will only succeed if there is a pipe with no outputs
+ * configured for it. In the future, it could choose to temporarily disable
+ * some outputs to free up a pipe for its use.
+ *
+ * \return crtc, or NULL if no pipes are available.
+ */
+
+/* VESA 640x480x72Hz mode to set on the pipe */
+static struct drm_display_mode load_detect_mode = {
+ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
+ 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+};
+
+struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode)
+{
+ struct intel_crtc *intel_crtc;
+ struct drm_crtc *possible_crtc;
+ struct drm_crtc *supported_crtc =NULL;
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_crtc *crtc = NULL;
+ struct drm_device *dev = encoder->dev;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs;
+ int i = -1;
+
+ /*
+ * Algorithm gets a little messy:
+ * - if the connector already has an assigned crtc, use it (but make
+ * sure it's on first)
+ * - try to find the first unused crtc that can drive this connector,
+ * and use that if we find one
+ * - if there are no unused crtcs available, try to use the first
+ * one we found that supports the connector
+ */
+
+ /* See if we already have a CRTC for this connector */
+ if (encoder->crtc) {
+ crtc = encoder->crtc;
+ /* Make sure the crtc and connector are running */
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+ }
+ return crtc;
+ }
+
+ /* Find an unused one (if possible) */
+ list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+ i++;
+ if (!(encoder->possible_crtcs & (1 << i)))
+ continue;
+ if (!possible_crtc->enabled) {
+ crtc = possible_crtc;
+ break;
+ }
+ if (!supported_crtc)
+ supported_crtc = possible_crtc;
+ }
+
+ /*
+ * If we didn't find an unused CRTC, don't use any.
+ */
+ if (!crtc) {
+ return NULL;
+ }
+
+ encoder->crtc = crtc;
+ intel_output->load_detect_temp = true;
+
+ intel_crtc = to_intel_crtc(crtc);
+ *dpms_mode = intel_crtc->dpms_mode;
+
+ if (!crtc->enabled) {
+ if (!mode)
+ mode = &load_detect_mode;
+ drm_crtc_helper_set_mode(crtc, mode, 0, 0, crtc->fb);
+ } else {
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
+ crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ }
+
+ /* Add this connector to the crtc */
+ encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->mode);
+ encoder_funcs->commit(encoder);
+ }
+ /* let the connector get through one full cycle before testing */
+ intel_wait_for_vblank(dev);
+
+ return crtc;
+}
+
+void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ if (intel_output->load_detect_temp) {
+ encoder->crtc = NULL;
+ intel_output->load_detect_temp = false;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+ drm_helper_disable_unused_functions(dev);
+ }
+
+ /* Switch crtc and output back off if necessary */
+ if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
+ if (encoder->crtc == crtc)
+ encoder_funcs->dpms(encoder, dpms_mode);
+ crtc_funcs->dpms(crtc, dpms_mode);
+ }
+}
+
+/* Returns the clock of the currently programmed mode of the given pipe. */
+static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B);
+ u32 fp;
+ intel_clock_t clock;
+
+ if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+ fp = I915_READ((pipe == 0) ? FPA0 : FPB0);
+ else
+ fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
+
+ clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ if (IS_I9XX(dev)) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+
+ switch (dpll & DPLL_MODE_MASK) {
+ case DPLLB_MODE_DAC_SERIAL:
+ clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
+ 5 : 10;
+ break;
+ case DPLLB_MODE_LVDS:
+ clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
+ 7 : 14;
+ break;
+ default:
+ DRM_DEBUG("Unknown DPLL mode %08x in programmed "
+ "mode\n", (int)(dpll & DPLL_MODE_MASK));
+ return 0;
+ }
+
+ /* XXX: Handle the 100Mhz refclk */
+ i9xx_clock(96000, &clock);
+ } else {
+ bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
+
+ if (is_lvds) {
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+ clock.p2 = 14;
+
+ if ((dpll & PLL_REF_INPUT_MASK) ==
+ PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+ /* XXX: might not be 66MHz */
+ i8xx_clock(66000, &clock);
+ } else
+ i8xx_clock(48000, &clock);
+ } else {
+ if (dpll & PLL_P1_DIVIDE_BY_TWO)
+ clock.p1 = 2;
+ else {
+ clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
+ }
+ if (dpll & PLL_P2_DIVIDE_BY_4)
+ clock.p2 = 4;
+ else
+ clock.p2 = 2;
+
+ i8xx_clock(48000, &clock);
+ }
+ }
+
+ /* XXX: It would be nice to validate the clocks, but we can't reuse
+ * i830PllIsValid() because it relies on the xf86_config connector
+ * configuration being accurate, which it isn't necessarily.
+ */
+
+ return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ struct drm_display_mode *mode;
+ int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B);
+ int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B);
+ int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B);
+ int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B);
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ mode->clock = intel_crtc_clock_get(dev, crtc);
+ mode->hdisplay = (htot & 0xffff) + 1;
+ mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
+ mode->hsync_start = (hsync & 0xffff) + 1;
+ mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
+ mode->vdisplay = (vtot & 0xffff) + 1;
+ mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
+ mode->vsync_start = (vsync & 0xffff) + 1;
+ mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ return mode;
+}
+
+static void intel_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ drm_crtc_cleanup(crtc);
+ kfree(intel_crtc);
+}
+
+static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+ .mode_set = intel_crtc_mode_set,
+ .mode_set_base = intel_pipe_set_base,
+ .prepare = intel_crtc_prepare,
+ .commit = intel_crtc_commit,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .cursor_set = intel_crtc_cursor_set,
+ .cursor_move = intel_crtc_cursor_move,
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
+};
+
+
+static void intel_crtc_init(struct drm_device *dev, int pipe)
+{
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+ if (intel_crtc == NULL)
+ return;
+
+ drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+
+ drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
+ intel_crtc->pipe = pipe;
+ for (i = 0; i < 256; i++) {
+ intel_crtc->lut_r[i] = i;
+ intel_crtc->lut_g[i] = i;
+ intel_crtc->lut_b[i] = i;
+ }
+
+ intel_crtc->cursor_addr = 0;
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
+ drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+ intel_crtc->mode_set.crtc = &intel_crtc->base;
+ intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1);
+ intel_crtc->mode_set.num_connectors = 0;
+
+ if (i915_fbpercrtc) {
+
+
+
+ }
+}
+
+struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
+{
+ struct drm_crtc *crtc = NULL;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ if (intel_crtc->pipe == pipe)
+ break;
+ }
+ return crtc;
+}
+
+static int intel_connector_clones(struct drm_device *dev, int type_mask)
+{
+ int index_mask = 0;
+ struct drm_connector *connector;
+ int entry = 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ if (type_mask & (1 << intel_output->type))
+ index_mask |= (1 << entry);
+ entry++;
+ }
+ return index_mask;
+}
+
+
+static void intel_setup_outputs(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ intel_crt_init(dev);
+
+ /* Set up integrated LVDS */
+ if (IS_MOBILE(dev) && !IS_I830(dev))
+ intel_lvds_init(dev);
+
+ if (IS_I9XX(dev)) {
+ intel_sdvo_init(dev, SDVOB);
+ intel_sdvo_init(dev, SDVOC);
+ } else
+ intel_dvo_init(dev);
+
+ if (IS_I9XX(dev) && !IS_I915G(dev))
+ intel_tv_init(dev);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_encoder *encoder = &intel_output->enc;
+ int crtc_mask = 0, clone_mask = 0;
+
+ /* valid crtcs */
+ switch(intel_output->type) {
+ case INTEL_OUTPUT_DVO:
+ case INTEL_OUTPUT_SDVO:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_ANALOG:
+ crtc_mask = ((1 << 0)|
+ (1 << 1));
+ clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
+ (1 << INTEL_OUTPUT_DVO) |
+ (1 << INTEL_OUTPUT_SDVO));
+ break;
+ case INTEL_OUTPUT_LVDS:
+ crtc_mask = (1 << 1);
+ clone_mask = (1 << INTEL_OUTPUT_LVDS);
+ break;
+ case INTEL_OUTPUT_TVOUT:
+ crtc_mask = ((1 << 0) |
+ (1 << 1));
+ clone_mask = (1 << INTEL_OUTPUT_TVOUT);
+ break;
+ }
+ encoder->possible_crtcs = crtc_mask;
+ encoder->possible_clones = intel_connector_clones(dev, clone_mask);
+ }
+}
+
+static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_device *dev = fb->dev;
+
+ if (fb->fbdev)
+ intelfb_remove(dev, fb);
+
+ drm_framebuffer_cleanup(fb);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(intel_fb->obj);
+ mutex_unlock(&dev->struct_mutex);
+
+ kfree(intel_fb);
+}
+
+static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_gem_object *object = intel_fb->obj;
+
+ return drm_gem_handle_create(file_priv, object, handle);
+}
+
+static const struct drm_framebuffer_funcs intel_fb_funcs = {
+ .destroy = intel_user_framebuffer_destroy,
+ .create_handle = intel_user_framebuffer_create_handle,
+};
+
+int intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_framebuffer **fb,
+ struct drm_gem_object *obj)
+{
+ struct intel_framebuffer *intel_fb;
+ int ret;
+
+ intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+ if (!intel_fb)
+ return -ENOMEM;
+
+ ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
+ if (ret) {
+ DRM_ERROR("framebuffer init failed %d\n", ret);
+ return ret;
+ }
+
+ drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
+
+ intel_fb->obj = obj;
+
+ *fb = &intel_fb->base;
+
+ return 0;
+}
+
+
+static struct drm_framebuffer *
+intel_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *filp,
+ struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct drm_framebuffer *fb;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
+ if (!obj)
+ return NULL;
+
+ ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
+ if (ret) {
+ drm_gem_object_unreference(obj);
+ return NULL;
+ }
+
+ return fb;
+}
+
+static const struct drm_mode_config_funcs intel_mode_funcs = {
+ .fb_create = intel_user_framebuffer_create,
+ .fb_changed = intelfb_probe,
+};
+
+void intel_modeset_init(struct drm_device *dev)
+{
+ int num_pipe;
+ int i;
+
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.funcs = (void *)&intel_mode_funcs;
+
+ if (IS_I965G(dev)) {
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
+ } else {
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+ }
+
+ /* set memory base */
+ if (IS_I9XX(dev))
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
+ else
+ dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
+
+ if (IS_MOBILE(dev) || IS_I9XX(dev))
+ num_pipe = 2;
+ else
+ num_pipe = 1;
+ DRM_DEBUG("%d display pipe%s available.\n",
+ num_pipe, num_pipe > 1 ? "s" : "");
+
+ for (i = 0; i < num_pipe; i++) {
+ intel_crtc_init(dev, i);
+ }
+
+ intel_setup_outputs(dev);
+}
+
+void intel_modeset_cleanup(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
+
+
+/* current intel driver doesn't take advantage of encoders
+ always give back the encoder for the connector
+*/
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ return &intel_output->enc;
+}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
new file mode 100644
index 000000000000..407edd5bf582
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_crtc.h"
+
+#include "drm_crtc_helper.h"
+/*
+ * Display related stuff
+ */
+
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+/* maximum connectors per crtcs in the mode set */
+#define INTELFB_CONN_LIMIT 4
+
+#define INTEL_I2C_BUS_DVO 1
+#define INTEL_I2C_BUS_SDVO 2
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+
+#define INTEL_DVO_CHIP_NONE 0
+#define INTEL_DVO_CHIP_LVDS 1
+#define INTEL_DVO_CHIP_TMDS 2
+#define INTEL_DVO_CHIP_TVOUT 4
+
+struct intel_i2c_chan {
+ struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */
+ u32 reg; /* GPIO reg */
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+ u8 slave_addr;
+};
+
+struct intel_framebuffer {
+ struct drm_framebuffer base;
+ struct drm_gem_object *obj;
+};
+
+
+struct intel_output {
+ struct drm_connector base;
+
+ struct drm_encoder enc;
+ int type;
+ struct intel_i2c_chan *i2c_bus; /* for control functions */
+ struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */
+ bool load_detect_temp;
+ void *dev_priv;
+};
+
+struct intel_crtc {
+ struct drm_crtc base;
+ int pipe;
+ int plane;
+ uint32_t cursor_addr;
+ u8 lut_r[256], lut_g[256], lut_b[256];
+ int dpms_mode;
+ struct intel_framebuffer *fbdev_fb;
+ /* a mode_set for fbdev users on this crtc */
+ struct drm_mode_set mode_set;
+};
+
+#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
+#define to_intel_output(x) container_of(x, struct intel_output, base)
+#define enc_to_intel_output(x) container_of(x, struct intel_output, enc)
+#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
+
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name);
+void intel_i2c_destroy(struct intel_i2c_chan *chan);
+int intel_ddc_get_modes(struct intel_output *intel_output);
+extern bool intel_ddc_probe(struct intel_output *intel_output);
+
+extern void intel_crt_init(struct drm_device *dev);
+extern void intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void intel_dvo_init(struct drm_device *dev);
+extern void intel_tv_init(struct drm_device *dev);
+extern void intel_lvds_init(struct drm_device *dev);
+
+extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+extern void intel_encoder_prepare (struct drm_encoder *encoder);
+extern void intel_encoder_commit (struct drm_encoder *encoder);
+
+extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
+
+extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
+ struct drm_crtc *crtc);
+extern void intel_wait_for_vblank(struct drm_device *dev);
+extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
+extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
+ struct drm_display_mode *mode,
+ int *dpms_mode);
+extern void intel_release_load_detect_pipe(struct intel_output *intel_output,
+ int dpms_mode);
+
+extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
+extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
+extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable);
+extern int intelfb_probe(struct drm_device *dev);
+extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc);
+extern void intelfb_restore(void);
+extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno);
+
+extern int intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_framebuffer **fb,
+ struct drm_gem_object *obj);
+#endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
new file mode 100644
index 000000000000..8b8d6e65cd3f
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "dvo.h"
+
+#define SIL164_ADDR 0x38
+#define CH7xxx_ADDR 0x76
+#define TFP410_ADDR 0x38
+
+static struct intel_dvo_device intel_dvo_devices[] = {
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "sil164",
+ .dvo_reg = DVOC,
+ .slave_addr = SIL164_ADDR,
+ .dev_ops = &sil164_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "ch7xxx",
+ .dvo_reg = DVOC,
+ .slave_addr = CH7xxx_ADDR,
+ .dev_ops = &ch7xxx_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ivch",
+ .dvo_reg = DVOA,
+ .slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */
+ .dev_ops = &ivch_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_TMDS,
+ .name = "tfp410",
+ .dvo_reg = DVOC,
+ .slave_addr = TFP410_ADDR,
+ .dev_ops = &tfp410_ops,
+ },
+ {
+ .type = INTEL_DVO_CHIP_LVDS,
+ .name = "ch7017",
+ .dvo_reg = DVOC,
+ .slave_addr = 0x75,
+ .gpio = GPIOE,
+ .dev_ops = &ch7017_ops,
+ }
+};
+
+static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ u32 dvo_reg = dvo->dvo_reg;
+ u32 temp = I915_READ(dvo_reg);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+ I915_READ(dvo_reg);
+ dvo->dev_ops->dpms(dvo, mode);
+ } else {
+ dvo->dev_ops->dpms(dvo, mode);
+ I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
+ I915_READ(dvo_reg);
+ }
+}
+
+static void intel_dvo_save(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* Each output should probably just save the registers it touches,
+ * but for now, use more overkill.
+ */
+ dev_priv->saveDVOA = I915_READ(DVOA);
+ dev_priv->saveDVOB = I915_READ(DVOB);
+ dev_priv->saveDVOC = I915_READ(DVOC);
+
+ dvo->dev_ops->save(dvo);
+}
+
+static void intel_dvo_restore(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ dvo->dev_ops->restore(dvo);
+
+ I915_WRITE(DVOA, dev_priv->saveDVOA);
+ I915_WRITE(DVOB, dev_priv->saveDVOB);
+ I915_WRITE(DVOC, dev_priv->saveDVOC);
+}
+
+static int intel_dvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ /* XXX: Validate clock range */
+
+ if (dvo->panel_fixed_mode) {
+ if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return dvo->dev_ops->mode_valid(dvo, mode);
+}
+
+static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dvo->panel_fixed_mode != NULL) {
+#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x
+ C(hdisplay);
+ C(hsync_start);
+ C(hsync_end);
+ C(htotal);
+ C(vdisplay);
+ C(vsync_start);
+ C(vsync_end);
+ C(vtotal);
+ C(clock);
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+#undef C
+ }
+
+ if (dvo->dev_ops->mode_fixup)
+ return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode);
+
+ return true;
+}
+
+static void intel_dvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = intel_crtc->pipe;
+ u32 dvo_val;
+ u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg;
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+
+ switch (dvo_reg) {
+ case DVOA:
+ default:
+ dvo_srcdim_reg = DVOA_SRCDIM;
+ break;
+ case DVOB:
+ dvo_srcdim_reg = DVOB_SRCDIM;
+ break;
+ case DVOC:
+ dvo_srcdim_reg = DVOC_SRCDIM;
+ break;
+ }
+
+ dvo->dev_ops->mode_set(dvo, mode, adjusted_mode);
+
+ /* Save the data order, since I don't know what it should be set to. */
+ dvo_val = I915_READ(dvo_reg) &
+ (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
+ dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE |
+ DVO_BLANK_ACTIVE_HIGH;
+
+ if (pipe == 1)
+ dvo_val |= DVO_PIPE_B_SELECT;
+ dvo_val |= DVO_PIPE_STALL;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
+
+ I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
+
+ /*I915_WRITE(DVOB_SRCDIM,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/
+ I915_WRITE(dvo_srcdim_reg,
+ (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+ (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT));
+ /*I915_WRITE(DVOB, dvo_val);*/
+ I915_WRITE(dvo_reg, dvo_val);
+}
+
+/**
+ * Detect the output connection on our DVO device.
+ *
+ * Unimplemented.
+ */
+static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ return dvo->dev_ops->detect(dvo);
+}
+
+static int intel_dvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ /* We should probably have an i2c driver get_modes function for those
+ * devices which will have a fixed set of modes determined by the chip
+ * (TV-out, for example), but for now with just TMDS and LVDS,
+ * that's not the case.
+ */
+ intel_ddc_get_modes(intel_output);
+ if (!list_empty(&connector->probed_modes))
+ return 1;
+
+
+ if (dvo->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+ mode = drm_mode_duplicate(connector->dev, dvo->panel_fixed_mode);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void intel_dvo_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+ if (dvo) {
+ if (dvo->dev_ops->destroy)
+ dvo->dev_ops->destroy(dvo);
+ if (dvo->panel_fixed_mode)
+ kfree(dvo->panel_fixed_mode);
+ /* no need, in i830_dvoices[] now */
+ //kfree(dvo);
+ }
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+#ifdef RANDR_GET_CRTC_INTERFACE
+static struct drm_crtc *intel_dvo_get_crtc(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT);
+
+ return intel_pipe_to_crtc(pScrn, pipe);
+}
+#endif
+
+static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
+ .dpms = intel_dvo_dpms,
+ .mode_fixup = intel_dvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_dvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_dvo_connector_funcs = {
+ .save = intel_dvo_save,
+ .restore = intel_dvo_restore,
+ .detect = intel_dvo_detect,
+ .destroy = intel_dvo_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
+ .mode_valid = intel_dvo_mode_valid,
+ .get_modes = intel_dvo_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
+ .destroy = intel_dvo_enc_destroy,
+};
+
+
+/**
+ * Attempts to get a fixed panel timing for LVDS (currently only the i830).
+ *
+ * Other chips with DVO LVDS will need to extend this to deal with the LVDS
+ * chip being on DVOB/C and having multiple pipes.
+ */
+static struct drm_display_mode *
+intel_dvo_get_current_mode (struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dvo_device *dvo = intel_output->dev_priv;
+ uint32_t dvo_reg = dvo->dvo_reg;
+ uint32_t dvo_val = I915_READ(dvo_reg);
+ struct drm_display_mode *mode = NULL;
+
+ /* If the DVO port is active, that'll be the LVDS, so we can pull out
+ * its timings to get how the BIOS set up the panel.
+ */
+ if (dvo_val & DVO_ENABLE) {
+ struct drm_crtc *crtc;
+ int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
+
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+ if (crtc) {
+ mode = intel_crtc_mode_get(dev, crtc);
+
+ if (mode) {
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+ if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ }
+ }
+ }
+ return mode;
+}
+
+void intel_dvo_init(struct drm_device *dev)
+{
+ struct intel_output *intel_output;
+ struct intel_dvo_device *dvo;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int ret = 0;
+ int i;
+ int gpio_inited = 0;
+ int encoder_type = DRM_MODE_ENCODER_NONE;
+ intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output)
+ return;
+
+ /* Set up the DDC bus */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D");
+ if (!intel_output->ddc_bus)
+ goto free_intel;
+
+ /* Now, try to find a controller */
+ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
+ struct drm_connector *connector = &intel_output->base;
+ int gpio;
+
+ dvo = &intel_dvo_devices[i];
+
+ /* Allow the I2C driver info to specify the GPIO to be used in
+ * special cases, but otherwise default to what's defined
+ * in the spec.
+ */
+ if (dvo->gpio != 0)
+ gpio = dvo->gpio;
+ else if (dvo->type == INTEL_DVO_CHIP_LVDS)
+ gpio = GPIOB;
+ else
+ gpio = GPIOE;
+
+ /* Set up the I2C bus necessary for the chip we're probing.
+ * It appears that everything is on GPIOE except for panels
+ * on i830 laptops, which are on GPIOB (DVOA).
+ */
+ if (gpio_inited != gpio) {
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+ if (!(i2cbus = intel_i2c_create(dev, gpio,
+ gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) {
+ continue;
+ }
+ gpio_inited = gpio;
+ }
+
+ if (dvo->dev_ops!= NULL)
+ ret = dvo->dev_ops->init(dvo, i2cbus);
+ else
+ ret = false;
+
+ if (!ret)
+ continue;
+
+ intel_output->type = INTEL_OUTPUT_DVO;
+ switch (dvo->type) {
+ case INTEL_DVO_CHIP_TMDS:
+ drm_connector_init(dev, connector,
+ &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_DVII);
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ break;
+ case INTEL_DVO_CHIP_LVDS:
+ drm_connector_init(dev, connector,
+ &intel_dvo_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ encoder_type = DRM_MODE_ENCODER_LVDS;
+ break;
+ }
+
+ drm_connector_helper_add(connector,
+ &intel_dvo_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ intel_output->dev_priv = dvo;
+ intel_output->i2c_bus = i2cbus;
+
+ drm_encoder_init(dev, &intel_output->enc,
+ &intel_dvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc,
+ &intel_dvo_helper_funcs);
+
+ drm_mode_connector_attach_encoder(&intel_output->base,
+ &intel_output->enc);
+ if (dvo->type == INTEL_DVO_CHIP_LVDS) {
+ /* For our LVDS chipsets, we should hopefully be able
+ * to dig the fixed panel mode out of the BIOS data.
+ * However, it's in a different format from the BIOS
+ * data on chipsets with integrated LVDS (stored in AIM
+ * headers, likely), so for now, just get the current
+ * mode being output through DVO.
+ */
+ dvo->panel_fixed_mode =
+ intel_dvo_get_current_mode(connector);
+ dvo->panel_wants_dither = true;
+ }
+
+ drm_sysfs_connector_add(connector);
+ return;
+ }
+
+ intel_i2c_destroy(intel_output->ddc_bus);
+ /* Didn't find a chip, so tear down. */
+ if (i2cbus != NULL)
+ intel_i2c_destroy(i2cbus);
+free_intel:
+ kfree(intel_output);
+}
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
new file mode 100644
index 000000000000..afd1217b8a02
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * David Airlie
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+struct intelfb_par {
+ struct drm_device *dev;
+ struct drm_display_mode *our_mode;
+ struct intel_framebuffer *intel_fb;
+ int crtc_count;
+ /* crtc currently bound to this */
+ uint32_t crtc_ids[2];
+};
+
+static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_mode_set *modeset = &intel_crtc->mode_set;
+ struct drm_framebuffer *fb = modeset->fb;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+
+ if (regno > 255)
+ return 1;
+
+ if (fb->depth == 8) {
+ intel_crtc_fb_gamma_set(crtc, red, green, blue, regno);
+ return 0;
+ }
+
+ if (regno < 16) {
+ switch (fb->depth) {
+ case 15:
+ fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int intelfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct intel_framebuffer *intel_fb = par->intel_fb;
+ struct drm_framebuffer *fb = &intel_fb->base;
+ int depth;
+
+ if (var->pixclock == -1 || !var->pixclock)
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > fb->width || var->yres > fb->height) {
+ DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n",var->xres,var->yres,fb->width,fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ depth = var->bits_per_pixel;
+ break;
+ }
+
+ switch (depth) {
+ case 8:
+ var->red.offset = 0;
+ var->green.offset = 0;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 15:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ var->transp.offset = 15;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* this will let fbcon do the mode init */
+/* FIXME: take mode config lock? */
+static int intelfb_set_par(struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct fb_var_screeninfo *var = &info->var;
+ int i;
+
+ DRM_DEBUG("%d %d\n", var->xres, var->pixclock);
+
+ if (var->pixclock != -1) {
+
+ DRM_ERROR("PIXEL CLCOK SET\n");
+ return -EINVAL;
+ } else {
+ struct drm_crtc *crtc;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ if (crtc->fb == intel_crtc->mode_set.fb) {
+ mutex_lock(&dev->mode_config.mutex);
+ ret = crtc->funcs->set_config(&intel_crtc->mode_set);
+ mutex_unlock(&dev->mode_config.mutex);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+ }
+}
+
+static int intelfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_mode_set *modeset;
+ struct drm_crtc *crtc;
+ struct intel_crtc *intel_crtc;
+ int ret = 0;
+ int i;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ if (i == par->crtc_count)
+ continue;
+
+ intel_crtc = to_intel_crtc(crtc);
+ modeset = &intel_crtc->mode_set;
+
+ modeset->x = var->xoffset;
+ modeset->y = var->yoffset;
+
+ if (modeset->num_connectors) {
+ mutex_lock(&dev->mode_config.mutex);
+ ret = crtc->funcs->set_config(modeset);
+ mutex_unlock(&dev->mode_config.mutex);
+ if (!ret) {
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void intelfb_on(struct fb_info *info)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int i;
+
+ /*
+ * For each CRTC in this fb, find all associated encoders
+ * and turn them off, then turn off the CRTC.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+
+ /* Found a CRTC on this fb, now find encoders */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+ }
+ }
+ }
+}
+
+static void intelfb_off(struct fb_info *info, int dpms_mode)
+{
+ struct intelfb_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ int i;
+
+ /*
+ * For each CRTC in this fb, find all associated encoders
+ * and turn them off, then turn off the CRTC.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ for (i = 0; i < par->crtc_count; i++)
+ if (crtc->base.id == par->crtc_ids[i])
+ break;
+
+ /* Found a CRTC on this fb, now find encoders */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+ struct drm_encoder_helper_funcs *encoder_funcs;
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->dpms(encoder, dpms_mode);
+ }
+ }
+ if (dpms_mode == DRM_MODE_DPMS_OFF)
+ crtc_funcs->dpms(crtc, dpms_mode);
+ }
+}
+
+static int intelfb_blank(int blank, struct fb_info *info)
+{
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ intelfb_on(info);
+ break;
+ case FB_BLANK_NORMAL:
+ intelfb_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ intelfb_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ intelfb_off(info, DRM_MODE_DPMS_SUSPEND);
+ break;
+ case FB_BLANK_POWERDOWN:
+ intelfb_off(info, DRM_MODE_DPMS_OFF);
+ break;
+ }
+ return 0;
+}
+
+static struct fb_ops intelfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = intelfb_check_var,
+ .fb_set_par = intelfb_set_par,
+ .fb_setcolreg = intelfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_pan_display = intelfb_pan_display,
+ .fb_blank = intelfb_blank,
+};
+
+/**
+ * Curretly it is assumed that the old framebuffer is reused.
+ *
+ * LOCKING
+ * caller should hold the mode config lock.
+ *
+ */
+int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct fb_info *info;
+ struct drm_framebuffer *fb;
+ struct drm_display_mode *mode = crtc->desired_mode;
+
+ fb = crtc->fb;
+ if (!fb)
+ return 1;
+
+ info = fb->fbdev;
+ if (!info)
+ return 1;
+
+ if (!mode)
+ return 1;
+
+ info->var.xres = mode->hdisplay;
+ info->var.right_margin = mode->hsync_start - mode->hdisplay;
+ info->var.hsync_len = mode->hsync_end - mode->hsync_start;
+ info->var.left_margin = mode->htotal - mode->hsync_end;
+ info->var.yres = mode->vdisplay;
+ info->var.lower_margin = mode->vsync_start - mode->vdisplay;
+ info->var.vsync_len = mode->vsync_end - mode->vsync_start;
+ info->var.upper_margin = mode->vtotal - mode->vsync_end;
+ info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
+ /* avoid overflow */
+ info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
+
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_resize);
+
+static struct drm_mode_set kernelfb_mode;
+
+static int intelfb_panic(struct notifier_block *n, unsigned long ununsed,
+ void *panic_str)
+{
+ DRM_ERROR("panic occurred, switching back to text console\n");
+
+ intelfb_restore();
+ return 0;
+}
+
+static struct notifier_block paniced = {
+ .notifier_call = intelfb_panic,
+};
+
+static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
+ uint32_t fb_height, uint32_t surface_width,
+ uint32_t surface_height,
+ struct intel_framebuffer **intel_fb_p)
+{
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_framebuffer *fb;
+ struct intel_framebuffer *intel_fb;
+ struct drm_mode_fb_cmd mode_cmd;
+ struct drm_gem_object *fbo = NULL;
+ struct drm_i915_gem_object *obj_priv;
+ struct device *device = &dev->pdev->dev;
+ int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
+
+ mode_cmd.width = surface_width;
+ mode_cmd.height = surface_height;
+
+ mode_cmd.bpp = 32;
+ mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
+ mode_cmd.depth = 24;
+
+ size = mode_cmd.pitch * mode_cmd.height;
+ size = ALIGN(size, PAGE_SIZE);
+ fbo = drm_gem_object_alloc(dev, size);
+ if (!fbo) {
+ printk(KERN_ERR "failed to allocate framebuffer\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ obj_priv = fbo->driver_private;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = i915_gem_object_pin(fbo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin fb: %d\n", ret);
+ goto out_unref;
+ }
+
+ /* Flush everything out, we'll be doing GTT only from now on */
+ i915_gem_object_set_to_gtt_domain(fbo, 1);
+
+ ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo);
+ if (ret) {
+ DRM_ERROR("failed to allocate fb.\n");
+ goto out_unref;
+ }
+
+ list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
+
+ intel_fb = to_intel_framebuffer(fb);
+ *intel_fb_p = intel_fb;
+
+ info = framebuffer_alloc(sizeof(struct intelfb_par), device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out_unref;
+ }
+
+ par = info->par;
+
+ strcpy(info->fix.id, "inteldrmfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 1; /* doing it in hw */
+ info->fix.ypanstep = 1; /* doing it in hw */
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_I830;
+ info->fix.type_aux = 0;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->fbops = &intelfb_ops;
+
+ info->fix.line_length = fb->pitch;
+ info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
+ info->fix.smem_len = size;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
+ size);
+ if (!info->screen_base) {
+ ret = -ENOSPC;
+ goto out_unref;
+ }
+ info->screen_size = size;
+
+// memset(info->screen_base, 0, size);
+
+ info->pseudo_palette = fb->pseudo_palette;
+ info->var.xres_virtual = fb->width;
+ info->var.yres_virtual = fb->height;
+ info->var.bits_per_pixel = fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+
+ info->var.xres = fb_width;
+ info->var.yres = fb_height;
+
+ /* FIXME: we really shouldn't expose mmio space at all */
+ info->fix.mmio_start = pci_resource_start(dev->pdev, mmio_bar);
+ info->fix.mmio_len = pci_resource_len(dev->pdev, mmio_bar);
+
+ info->pixmap.size = 64*1024;
+ info->pixmap.buf_align = 8;
+ info->pixmap.access_align = 32;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+ info->pixmap.scan_align = 1;
+
+ switch(fb->depth) {
+ case 8:
+ info->var.red.offset = 0;
+ info->var.green.offset = 0;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8; /* 8bit DAC */
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 5;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ break;
+ }
+
+ fb->fbdev = info;
+
+ par->intel_fb = intel_fb;
+ par->dev = dev;
+
+ /* To allow resizeing without swapping buffers */
+ printk("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
+ intel_fb->base.height, obj_priv->gtt_offset, fbo);
+
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+
+out_unref:
+ drm_gem_object_unreference(fbo);
+ mutex_unlock(&dev->struct_mutex);
+out:
+ return ret;
+}
+
+static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset;
+ unsigned int width, height;
+ int new_fb = 0;
+ int ret, i, conn_count;
+
+ if (!drm_helper_crtc_in_use(crtc))
+ return 0;
+
+ if (!crtc->desired_mode)
+ return 0;
+
+ width = crtc->desired_mode->hdisplay;
+ height = crtc->desired_mode->vdisplay;
+
+ /* is there an fb bound to this crtc already */
+ if (!intel_crtc->mode_set.fb) {
+ ret = intelfb_create(dev, width, height, width, height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ fb = intel_crtc->mode_set.fb;
+ intel_fb = to_intel_framebuffer(fb);
+ if ((intel_fb->base.width < width) || (intel_fb->base.height < height))
+ return -EINVAL;
+ }
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder)
+ if (connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count] = connector;
+ conn_count++;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[0] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+
+ par->crtc_count = 1;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+
+ /* Switch back to kernel console on panic */
+ kernelfb_mode = *modeset;
+ atomic_notifier_chain_register(&panic_notifier_list, &paniced);
+ printk(KERN_INFO "registered panic notifier\n");
+
+ return 0;
+}
+
+static int intelfb_multi_fb_probe(struct drm_device *dev)
+{
+
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ ret = intelfb_multi_fb_probe_crtc(dev, crtc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int intelfb_single_fb_probe(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+ unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
+ unsigned int surface_width = 0, surface_height = 0;
+ int new_fb = 0;
+ int crtc_count = 0;
+ int ret, i, conn_count = 0;
+ struct intel_framebuffer *intel_fb;
+ struct fb_info *info;
+ struct intelfb_par *par;
+ struct drm_mode_set *modeset = NULL;
+
+ DRM_DEBUG("\n");
+
+ /* Get a count of crtcs now in use and new min/maxes width/heights */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (!drm_helper_crtc_in_use(crtc))
+ continue;
+
+ crtc_count++;
+ if (!crtc->desired_mode)
+ continue;
+
+ /* Smallest mode determines console size... */
+ if (crtc->desired_mode->hdisplay < fb_width)
+ fb_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay < fb_height)
+ fb_height = crtc->desired_mode->vdisplay;
+
+ /* ... but largest for memory allocation dimensions */
+ if (crtc->desired_mode->hdisplay > surface_width)
+ surface_width = crtc->desired_mode->hdisplay;
+
+ if (crtc->desired_mode->vdisplay > surface_height)
+ surface_height = crtc->desired_mode->vdisplay;
+ }
+
+ if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
+ /* hmm everyone went away - assume VGA cable just fell out
+ and will come back later. */
+ DRM_DEBUG("no CRTCs available?\n");
+ return 0;
+ }
+
+//fail
+ /* Find the fb for our new config */
+ if (list_empty(&dev->mode_config.fb_kernel_list)) {
+ DRM_DEBUG("creating new fb (console size %dx%d, "
+ "buffer size %dx%d)\n", fb_width, fb_height,
+ surface_width, surface_height);
+ ret = intelfb_create(dev, fb_width, fb_height, surface_width,
+ surface_height, &intel_fb);
+ if (ret)
+ return -EINVAL;
+ new_fb = 1;
+ } else {
+ struct drm_framebuffer *fb;
+
+ fb = list_first_entry(&dev->mode_config.fb_kernel_list,
+ struct drm_framebuffer, filp_head);
+ intel_fb = to_intel_framebuffer(fb);
+
+ /* if someone hotplugs something bigger than we have already
+ * allocated, we are pwned. As really we can't resize an
+ * fbdev that is in the wild currently due to fbdev not really
+ * being designed for the lower layers moving stuff around
+ * under it.
+ * - so in the grand style of things - punt.
+ */
+ if ((fb->width < surface_width) ||
+ (fb->height < surface_height)) {
+ DRM_ERROR("fb not large enough for console\n");
+ return -EINVAL;
+ }
+ }
+// fail
+
+ info = intel_fb->base.fbdev;
+ par = info->par;
+
+ crtc_count = 0;
+ /*
+ * For each CRTC, set up the connector list for the CRTC's mode
+ * set configuration.
+ */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ modeset = &intel_crtc->mode_set;
+ modeset->fb = &intel_fb->base;
+ conn_count = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ head) {
+ if (!connector->encoder)
+ continue;
+
+ if(connector->encoder->crtc == modeset->crtc) {
+ modeset->connectors[conn_count++] = connector;
+ if (conn_count > INTELFB_CONN_LIMIT)
+ BUG();
+ }
+ }
+
+ /* Zero out remaining connector pointers */
+ for (i = conn_count; i < INTELFB_CONN_LIMIT; i++)
+ modeset->connectors[i] = NULL;
+
+ par->crtc_ids[crtc_count++] = crtc->base.id;
+
+ modeset->num_connectors = conn_count;
+ if (modeset->mode != modeset->crtc->desired_mode)
+ modeset->mode = modeset->crtc->desired_mode;
+ }
+ par->crtc_count = crtc_count;
+
+ if (new_fb) {
+ info->var.pixclock = -1;
+ if (register_framebuffer(info) < 0)
+ return -EINVAL;
+ } else
+ intelfb_set_par(info);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+
+ /* Switch back to kernel console on panic */
+ kernelfb_mode = *modeset;
+ atomic_notifier_chain_register(&panic_notifier_list, &paniced);
+ printk(KERN_INFO "registered panic notifier\n");
+
+ return 0;
+}
+
+/**
+ * intelfb_restore - restore the framebuffer console (kernel) config
+ *
+ * Restore's the kernel's fbcon mode, used for lastclose & panic paths.
+ */
+void intelfb_restore(void)
+{
+ drm_crtc_helper_set_config(&kernelfb_mode);
+}
+
+static void intelfb_sysrq(int dummy1, struct tty_struct *dummy3)
+{
+ intelfb_restore();
+}
+
+static struct sysrq_key_op sysrq_intelfb_restore_op = {
+ .handler = intelfb_sysrq,
+ .help_msg = "force fb",
+ .action_msg = "force restore of fb console",
+};
+
+int intelfb_probe(struct drm_device *dev)
+{
+ int ret;
+
+ DRM_DEBUG("\n");
+
+ /* something has changed in the lower levels of hell - deal with it
+ here */
+
+ /* two modes : a) 1 fb to rule all crtcs.
+ b) one fb per crtc.
+ two actions 1) new connected device
+ 2) device removed.
+ case a/1 : if the fb surface isn't big enough - resize the surface fb.
+ if the fb size isn't big enough - resize fb into surface.
+ if everything big enough configure the new crtc/etc.
+ case a/2 : undo the configuration
+ possibly resize down the fb to fit the new configuration.
+ case b/1 : see if it is on a new crtc - setup a new fb and add it.
+ case b/2 : teardown the new fb.
+ */
+
+ /* mode a first */
+ /* search for an fb */
+ if (i915_fbpercrtc == 1) {
+ ret = intelfb_multi_fb_probe(dev);
+ } else {
+ ret = intelfb_single_fb_probe(dev);
+ }
+
+ register_sysrq_key('g', &sysrq_intelfb_restore_op);
+
+ return ret;
+}
+EXPORT_SYMBOL(intelfb_probe);
+
+int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+{
+ struct fb_info *info;
+
+ if (!fb)
+ return -EINVAL;
+
+ info = fb->fbdev;
+
+ if (info) {
+ unregister_framebuffer(info);
+ iounmap(info->screen_base);
+ framebuffer_release(info);
+ }
+
+ atomic_notifier_chain_unregister(&panic_notifier_list, &paniced);
+ memset(&kernelfb_mode, 0, sizeof(struct drm_mode_set));
+ return 0;
+}
+EXPORT_SYMBOL(intelfb_remove);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
new file mode 100644
index 000000000000..a5a2f5339e9e
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/*
+ * Intel GPIO access functions
+ */
+
+#define I2C_RISEFALL_TIME 20
+
+static int get_clock(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_CLOCK_VAL_IN) != 0);
+}
+
+static int get_data(void *data)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 val;
+
+ val = I915_READ(chan->reg);
+ return ((val & GPIO_DATA_VAL_IN) != 0);
+}
+
+static void set_clock(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, clock_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
+ else
+ clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
+ GPIO_CLOCK_VAL_MASK;
+ I915_WRITE(chan->reg, reserved | clock_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+static void set_data(void *data, int state_high)
+{
+ struct intel_i2c_chan *chan = data;
+ struct drm_device *dev = chan->drm_dev;
+ struct drm_i915_private *dev_priv = chan->drm_dev->dev_private;
+ u32 reserved = 0, data_bits;
+
+ /* On most chips, these bits must be preserved in software. */
+ if (!IS_I830(dev) && !IS_845G(dev))
+ reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
+ GPIO_CLOCK_PULLUP_DISABLE);
+
+ if (state_high)
+ data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
+ else
+ data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
+ GPIO_DATA_VAL_MASK;
+
+ I915_WRITE(chan->reg, reserved | data_bits);
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+}
+
+/**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+ * @output: driver specific output device
+ * @reg: GPIO reg to use
+ * @name: name for this bus
+ *
+ * Creates and registers a new i2c bus with the Linux i2c layer, for use
+ * in output probing and control (e.g. DDC or SDVO control functions).
+ *
+ * Possible values for @reg include:
+ * %GPIOA
+ * %GPIOB
+ * %GPIOC
+ * %GPIOD
+ * %GPIOE
+ * %GPIOF
+ * %GPIOG
+ * %GPIOH
+ * see PRM for details on how these different busses are used.
+ */
+struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ const char *name)
+{
+ struct intel_i2c_chan *chan;
+
+ chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL);
+ if (!chan)
+ goto out_free;
+
+ chan->drm_dev = dev;
+ chan->reg = reg;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
+ chan->adapter.owner = THIS_MODULE;
+#ifndef I2C_HW_B_INTELFB
+#define I2C_HW_B_INTELFB I2C_HW_B_I810
+#endif
+ chan->adapter.id = I2C_HW_B_INTELFB;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &dev->pdev->dev;
+ chan->algo.setsda = set_data;
+ chan->algo.setscl = set_clock;
+ chan->algo.getsda = get_data;
+ chan->algo.getscl = get_clock;
+ chan->algo.udelay = 20;
+ chan->algo.timeout = usecs_to_jiffies(2200);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ if(i2c_bit_add_bus(&chan->adapter))
+ goto out_free;
+
+ /* JJJ: raise SCL and SDA? */
+ set_data(chan, 1);
+ set_clock(chan, 1);
+ udelay(20);
+
+ return chan;
+
+out_free:
+ kfree(chan);
+ return NULL;
+}
+
+/**
+ * intel_i2c_destroy - unregister and free i2c bus resources
+ * @output: channel to free
+ *
+ * Unregister the adapter from the i2c layer, then free the structure.
+ */
+void intel_i2c_destroy(struct intel_i2c_chan *chan)
+{
+ if (!chan)
+ return;
+
+ i2c_del_adapter(&chan->adapter);
+ kfree(chan);
+}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
new file mode 100644
index 000000000000..ccecfaf6307b
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+/**
+ * Sets the backlight level.
+ *
+ * \param level backlight level, from 0 to intel_lvds_get_max_backlight().
+ */
+static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 blc_pwm_ctl;
+
+ blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
+ I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl |
+ (level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
+}
+
+/**
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+}
+
+/**
+ * Sets the power state for the panel.
+ */
+static void intel_lvds_set_power(struct drm_device *dev, bool on)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_status;
+
+ if (on) {
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
+ POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while ((pp_status & PP_ON) == 0);
+
+ intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle);
+ } else {
+ intel_lvds_set_backlight(dev, 0);
+
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) &
+ ~POWER_TARGET_ON);
+ do {
+ pp_status = I915_READ(PP_STATUS);
+ } while (pp_status & PP_ON);
+ }
+}
+
+static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+
+ /* XXX: We never power down the LVDS pairs. */
+}
+
+static void intel_lvds_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->savePP_ON = I915_READ(PP_ON_DELAYS);
+ dev_priv->savePP_OFF = I915_READ(PP_OFF_DELAYS);
+ dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL);
+ dev_priv->savePP_DIVISOR = I915_READ(PP_DIVISOR);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ /*
+ * If the light is off at server startup, just make it full brightness
+ */
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+}
+
+static void intel_lvds_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(PP_ON_DELAYS, dev_priv->savePP_ON);
+ I915_WRITE(PP_OFF_DELAYS, dev_priv->savePP_OFF);
+ I915_WRITE(PP_DIVISOR, dev_priv->savePP_DIVISOR);
+ I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL);
+ if (dev_priv->savePP_CONTROL & POWER_TARGET_ON)
+ intel_lvds_set_power(dev, true);
+ else
+ intel_lvds_set_power(dev, false);
+}
+
+static int intel_lvds_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode;
+
+ if (fixed_mode) {
+ if (mode->hdisplay > fixed_mode->hdisplay)
+ return MODE_PANEL;
+ if (mode->vdisplay > fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ struct drm_encoder *tmp_encoder;
+
+ /* Should never happen!! */
+ if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
+ printk(KERN_ERR "Can't support LVDS on pipe A\n");
+ return false;
+ }
+
+ /* Should never happen!! */
+ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
+ if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "encoder on the same pipe\n");
+ return false;
+ }
+ }
+
+ /*
+ * If we have timings from the BIOS for the panel, put them in
+ * to the adjusted mode. The CRTC will be set up for this mode,
+ * with the panel scaling set up to source from the H/VDisplay
+ * of the original mode.
+ */
+ if (dev_priv->panel_fixed_mode != NULL) {
+ adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay;
+ adjusted_mode->hsync_start =
+ dev_priv->panel_fixed_mode->hsync_start;
+ adjusted_mode->hsync_end =
+ dev_priv->panel_fixed_mode->hsync_end;
+ adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal;
+ adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay;
+ adjusted_mode->vsync_start =
+ dev_priv->panel_fixed_mode->vsync_start;
+ adjusted_mode->vsync_end =
+ dev_priv->panel_fixed_mode->vsync_end;
+ adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal;
+ adjusted_mode->clock = dev_priv->panel_fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ /*
+ * XXX: It would be nice to support lower refresh rates on the
+ * panels to reduce power consumption, and perhaps match the
+ * user's requested refresh rate.
+ */
+
+ return true;
+}
+
+static void intel_lvds_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL);
+ dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ BACKLIGHT_DUTY_CYCLE_MASK);
+
+ intel_lvds_set_power(dev, false);
+}
+
+static void intel_lvds_commit( struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight_duty_cycle == 0)
+ dev_priv->backlight_duty_cycle =
+ intel_lvds_get_max_backlight(dev);
+
+ intel_lvds_set_power(dev, true);
+}
+
+static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ u32 pfit_control;
+
+ /*
+ * The LVDS pin pair will already have been turned on in the
+ * intel_crtc_mode_set since it has a large impact on the DPLL
+ * settings.
+ */
+
+ /*
+ * Enable automatic panel scaling so that non-native modes fill the
+ * screen. Should be enabled before the pipe is enabled, according to
+ * register description and PRM.
+ */
+ if (mode->hdisplay != adjusted_mode->hdisplay ||
+ mode->vdisplay != adjusted_mode->vdisplay)
+ pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
+ HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
+ HORIZ_INTERP_BILINEAR);
+ else
+ pfit_control = 0;
+
+ if (!IS_I965G(dev)) {
+ if (dev_priv->panel_wants_dither)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+ }
+ else
+ pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
+
+ I915_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/**
+ * Detect the LVDS connection.
+ *
+ * This always returns CONNECTOR_STATUS_CONNECTED. This connector should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector)
+{
+ return connector_status_connected;
+}
+
+/**
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int intel_lvds_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
+
+ ret = intel_ddc_get_modes(intel_output);
+
+ if (ret)
+ return ret;
+
+ /* Didn't get an EDID, so
+ * Set wide sync ranges so we get all modes
+ * handed to valid_mode for checking
+ */
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+
+ if (dev_priv->panel_fixed_mode != NULL) {
+ struct drm_display_mode *mode;
+
+ mutex_unlock(&dev->mode_config.mutex);
+ mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
+ drm_mode_probed_add(connector, mode);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_lvds_destroy - unregister and free LVDS structures
+ * @connector: connector to free
+ *
+ * Unregister the DDC bus for this connector then free the driver private
+ * structure.
+ */
+static void intel_lvds_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
+ .dpms = intel_lvds_dpms,
+ .mode_fixup = intel_lvds_mode_fixup,
+ .prepare = intel_lvds_prepare,
+ .mode_set = intel_lvds_mode_set,
+ .commit = intel_lvds_commit,
+};
+
+static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
+ .get_modes = intel_lvds_get_modes,
+ .mode_valid = intel_lvds_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static const struct drm_connector_funcs intel_lvds_connector_funcs = {
+ .save = intel_lvds_save,
+ .restore = intel_lvds_restore,
+ .detect = intel_lvds_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_lvds_destroy,
+};
+
+
+static void intel_lvds_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
+ .destroy = intel_lvds_enc_destroy,
+};
+
+
+
+/**
+ * intel_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void intel_lvds_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
+ struct drm_crtc *crtc;
+ u32 lvds;
+ int pipe;
+
+ intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+ encoder = &intel_output->enc;
+ drm_connector_init(dev, &intel_output->base, &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_lvds_enc_funcs,
+ DRM_MODE_ENCODER_LVDS);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ intel_output->type = INTEL_OUTPUT_LVDS;
+
+ drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
+ drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+
+ /*
+ * LVDS discovery:
+ * 1) check for EDID on DDC
+ * 2) check for VBT data
+ * 3) check to see if LVDS is already on
+ * if none of the above, no panel
+ * 4) make sure lid is open
+ * if closed, act like it's not there for now
+ */
+
+ /* Set up the DDC bus. */
+ intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
+ if (!intel_output->ddc_bus) {
+ dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
+ "failed.\n");
+ goto failed;
+ }
+
+ /*
+ * Attempt to get the fixed panel mode from DDC. Assume that the
+ * preferred mode is the right one.
+ */
+ intel_ddc_get_modes(intel_output);
+
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ mutex_lock(&dev->mode_config.mutex);
+ if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ mutex_unlock(&dev->mode_config.mutex);
+ goto out; /* FIXME: check for quirks */
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+ }
+
+ /* Failed to get EDID, what about VBT? */
+ if (dev_priv->vbt_mode) {
+ mutex_lock(&dev->mode_config.mutex);
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, dev_priv->vbt_mode);
+ mutex_unlock(&dev->mode_config.mutex);
+ }
+
+ /*
+ * If we didn't get EDID, try checking if the panel is already turned
+ * on. If so, assume that whatever is currently programmed is the
+ * correct mode.
+ */
+ lvds = I915_READ(LVDS);
+ pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ crtc = intel_get_crtc_from_pipe(dev, pipe);
+
+ if (crtc && (lvds & LVDS_PORT_EN)) {
+ dev_priv->panel_fixed_mode = intel_crtc_mode_get(dev, crtc);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ goto out; /* FIXME: check for quirks */
+ }
+ }
+
+ /* If we still don't have a mode after all that, give up. */
+ if (!dev_priv->panel_fixed_mode)
+ goto failed;
+
+ /* FIXME: detect aopen & mac mini type stuff automatically? */
+ /*
+ * Blacklist machines with BIOSes that list an LVDS panel without
+ * actually having one.
+ */
+ if (IS_I945GM(dev)) {
+ /* aopen mini pc */
+ if (dev->pdev->subsystem_vendor == 0xa0a0)
+ goto failed;
+
+ if ((dev->pdev->subsystem_vendor == 0x8086) &&
+ (dev->pdev->subsystem_device == 0x7270)) {
+ /* It's a Mac Mini or Macbook Pro.
+ *
+ * Apple hardware is out to get us. The macbook pro
+ * has a real LVDS panel, but the mac mini does not,
+ * and they have the same device IDs. We'll
+ * distinguish by panel size, on the assumption
+ * that Apple isn't about to make any machines with an
+ * 800x600 display.
+ */
+
+ if (dev_priv->panel_fixed_mode != NULL &&
+ dev_priv->panel_fixed_mode->hdisplay == 800 &&
+ dev_priv->panel_fixed_mode->vdisplay == 600) {
+ DRM_DEBUG("Suspected Mac Mini, ignoring the LVDS\n");
+ goto failed;
+ }
+ }
+ }
+
+
+out:
+ drm_sysfs_connector_add(connector);
+ return;
+
+failed:
+ DRM_DEBUG("No LVDS modes found, disabling.\n");
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
new file mode 100644
index 000000000000..e42019e5d661
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include "drmP.h"
+#include "intel_drv.h"
+
+/**
+ * intel_ddc_probe
+ *
+ */
+bool intel_ddc_probe(struct intel_output *intel_output)
+{
+ u8 out_buf[] = { 0x0, 0x0};
+ u8 buf[2];
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2);
+ if (ret == 2)
+ return true;
+
+ return false;
+}
+
+/**
+ * intel_ddc_get_modes - get modelist from monitor
+ * @connector: DRM connector device to use
+ *
+ * Fetch the EDID information from @connector using the DDC bus.
+ */
+int intel_ddc_get_modes(struct intel_output *intel_output)
+{
+ struct edid *edid;
+ int ret = 0;
+
+ edid = drm_get_edid(&intel_output->base,
+ &intel_output->ddc_bus->adapter);
+ if (edid) {
+ drm_mode_connector_update_edid_property(&intel_output->base,
+ edid);
+ ret = drm_add_edid_modes(&intel_output->base, edid);
+ kfree(edid);
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
new file mode 100644
index 000000000000..fbbaa4f414a0
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright © 2006-2007 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "intel_sdvo_regs.h"
+
+#undef SDVO_DEBUG
+
+struct intel_sdvo_priv {
+ struct intel_i2c_chan *i2c_bus;
+ int slaveaddr;
+ int output_device;
+
+ u16 active_outputs;
+
+ struct intel_sdvo_caps caps;
+ int pixel_clock_min, pixel_clock_max;
+
+ int save_sdvo_mult;
+ u16 save_active_outputs;
+ struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+ struct intel_sdvo_dtd save_output_dtd[16];
+ u32 save_SDVOX;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+static void intel_sdvo_write_sdvox(struct intel_output *intel_output, u32 val)
+{
+ struct drm_device *dev = intel_output->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 bval = val, cval = val;
+ int i;
+
+ if (sdvo_priv->output_device == SDVOB) {
+ cval = I915_READ(SDVOC);
+ } else {
+ bval = I915_READ(SDVOB);
+ }
+ /*
+ * Write the registers twice for luck. Sometimes,
+ * writing them only once doesn't appear to 'stick'.
+ * The BIOS does this too. Yay, magic
+ */
+ for (i = 0; i < 2; i++)
+ {
+ I915_WRITE(SDVOB, bval);
+ I915_READ(SDVOB);
+ I915_WRITE(SDVOC, cval);
+ I915_READ(SDVOC);
+ }
+}
+
+static bool intel_sdvo_read_byte(struct intel_output *intel_output, u8 addr,
+ u8 *ch)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u8 out_buf[2];
+ u8 buf[2];
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = out_buf,
+ },
+ {
+ .addr = sdvo_priv->i2c_bus->slave_addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = 0;
+
+ if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2)
+ {
+ *ch = buf[0];
+ return true;
+ }
+
+ DRM_DEBUG("i2c transfer returned %d\n", ret);
+ return false;
+}
+
+static bool intel_sdvo_write_byte(struct intel_output *intel_output, int addr,
+ u8 ch)
+{
+ u8 out_buf[2];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = intel_output->i2c_bus->slave_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = out_buf,
+ }
+ };
+
+ out_buf[0] = addr;
+ out_buf[1] = ch;
+
+ if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1)
+ {
+ return true;
+ }
+ return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+const static struct _sdvo_cmd_name {
+ u8 cmd;
+ char *name;
+} sdvo_cmd_names[] = {
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+ SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
+};
+
+#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+#ifdef SDVO_DEBUG
+static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int i;
+
+ DRM_DEBUG("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+ for (i = 0; i < args_len; i++)
+ printk("%02X ", ((u8 *)args)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) {
+ if (cmd == sdvo_cmd_names[i].cmd) {
+ printk("(%s)", sdvo_cmd_names[i].name);
+ break;
+ }
+ }
+ if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0]))
+ printk("(%02X)",cmd);
+ printk("\n");
+}
+#else
+#define intel_sdvo_debug_write(o, c, a, l)
+#endif
+
+static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+{
+ int i;
+
+ intel_sdvo_debug_write(intel_output, cmd, args, args_len);
+
+ for (i = 0; i < args_len; i++) {
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_ARG_0 - i,
+ ((u8*)args)[i]);
+ }
+
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
+}
+
+#ifdef SDVO_DEBUG
+static const char *cmd_status_names[] = {
+ "Power on",
+ "Success",
+ "Not supported",
+ "Invalid arg",
+ "Pending",
+ "Target not specified",
+ "Scaling not supported"
+};
+
+static void intel_sdvo_debug_response(struct intel_output *intel_output,
+ void *response, int response_len,
+ u8 status)
+{
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ DRM_DEBUG("%s: R: ", SDVO_NAME(sdvo_priv));
+ for (i = 0; i < response_len; i++)
+ printk("%02X ", ((u8 *)response)[i]);
+ for (; i < 8; i++)
+ printk(" ");
+ if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+ printk("(%s)", cmd_status_names[status]);
+ else
+ printk("(??? %d)", status);
+ printk("\n");
+}
+#else
+#define intel_sdvo_debug_response(o, r, l, s)
+#endif
+
+static u8 intel_sdvo_read_response(struct intel_output *intel_output,
+ void *response, int response_len)
+{
+ int i;
+ u8 status;
+ u8 retry = 50;
+
+ while (retry--) {
+ /* Read the command response */
+ for (i = 0; i < response_len; i++) {
+ intel_sdvo_read_byte(intel_output,
+ SDVO_I2C_RETURN_0 + i,
+ &((u8 *)response)[i]);
+ }
+
+ /* read the return status */
+ intel_sdvo_read_byte(intel_output, SDVO_I2C_CMD_STATUS,
+ &status);
+
+ intel_sdvo_debug_response(intel_output, response, response_len,
+ status);
+ if (status != SDVO_CMD_STATUS_PENDING)
+ return status;
+
+ mdelay(50);
+ }
+
+ return status;
+}
+
+static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+ if (mode->clock >= 100000)
+ return 1;
+ else if (mode->clock >= 50000)
+ return 2;
+ else
+ return 4;
+}
+
+/**
+ * Don't check status code from this as it switches the bus back to the
+ * SDVO chips which defeats the purpose of doing a bus switch in the first
+ * place.
+ */
+static void intel_sdvo_set_control_bus_switch(struct intel_output *intel_output,
+ u8 target)
+{
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1);
+}
+
+static bool intel_sdvo_set_target_input(struct intel_output *intel_output, bool target_0, bool target_1)
+{
+ struct intel_sdvo_set_target_input_args targets = {0};
+ u8 status;
+
+ if (target_0 && target_1)
+ return SDVO_CMD_STATUS_NOTSUPP;
+
+ if (target_1)
+ targets.target_1 = 1;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_INPUT, &targets,
+ sizeof(targets));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool intel_sdvo_get_trained_inputs(struct intel_output *intel_output, bool *input_1, bool *input_2)
+{
+ struct intel_sdvo_get_trained_inputs_response response;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, sizeof(response));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ *input_1 = response.input0_trained;
+ *input_2 = response.input1_trained;
+ return true;
+}
+
+static bool intel_sdvo_get_active_outputs(struct intel_output *intel_output,
+ u16 *outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, outputs, sizeof(*outputs));
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_active_outputs(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs,
+ sizeof(outputs));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output,
+ int mode)
+{
+ u8 status, state = SDVO_ENCODER_STATE_ON;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ state = SDVO_ENCODER_STATE_ON;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ state = SDVO_ENCODER_STATE_STANDBY;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ state = SDVO_ENCODER_STATE_SUSPEND;
+ break;
+ case DRM_MODE_DPMS_OFF:
+ state = SDVO_ENCODER_STATE_OFF;
+ break;
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+ sizeof(state));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_input_pixel_clock_range(struct intel_output *intel_output,
+ int *clock_min,
+ int *clock_max)
+{
+ struct intel_sdvo_pixel_clock_range clocks;
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
+ NULL, 0);
+
+ status = intel_sdvo_read_response(intel_output, &clocks, sizeof(clocks));
+
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ /* Convert the values from units of 10 kHz to kHz. */
+ *clock_min = clocks.min * 10;
+ *clock_max = clocks.max * 10;
+
+ return true;
+}
+
+static bool intel_sdvo_set_target_output(struct intel_output *intel_output,
+ u16 outputs)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs,
+ sizeof(outputs));
+
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ return (status == SDVO_CMD_STATUS_SUCCESS);
+}
+
+static bool intel_sdvo_get_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part1,
+ sizeof(dtd->part1));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &dtd->part2,
+ sizeof(dtd->part2));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_get_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_get_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_get_timing(intel_output,
+ SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_timing(struct intel_output *intel_output, u8 cmd,
+ struct intel_sdvo_dtd *dtd)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, cmd, &dtd->part1, sizeof(dtd->part1));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ intel_sdvo_write_cmd(intel_output, cmd + 1, &dtd->part2, sizeof(dtd->part2));
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_set_input_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
+}
+
+static bool intel_sdvo_set_output_timing(struct intel_output *intel_output,
+ struct intel_sdvo_dtd *dtd)
+{
+ return intel_sdvo_set_timing(intel_output,
+ SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
+}
+
+
+static int intel_sdvo_get_clock_rate_mult(struct intel_output *intel_output)
+{
+ u8 response, status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 1);
+
+ if (status != SDVO_CMD_STATUS_SUCCESS) {
+ DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+ return SDVO_CLOCK_RATE_MULT_1X;
+ } else {
+ DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+ }
+
+ return response;
+}
+
+static bool intel_sdvo_set_clock_rate_mult(struct intel_output *intel_output, u8 val)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
+ status = intel_sdvo_read_response(intel_output, NULL, 0);
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO
+ * device will be told of the multiplier during mode_set.
+ */
+ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
+ return true;
+}
+
+static void intel_sdvo_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u16 width, height;
+ u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ u16 h_sync_offset, v_sync_offset;
+ u32 sdvox;
+ struct intel_sdvo_dtd output_dtd;
+ int sdvo_pixel_multiply;
+
+ if (!mode)
+ return;
+
+ width = mode->crtc_hdisplay;
+ height = mode->crtc_vdisplay;
+
+ /* do some mode translations */
+ h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
+ h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+ v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
+ v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+
+ h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
+ v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
+
+ output_dtd.part1.clock = mode->clock / 10;
+ output_dtd.part1.h_active = width & 0xff;
+ output_dtd.part1.h_blank = h_blank_len & 0xff;
+ output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+ ((h_blank_len >> 8) & 0xf);
+ output_dtd.part1.v_active = height & 0xff;
+ output_dtd.part1.v_blank = v_blank_len & 0xff;
+ output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+ ((v_blank_len >> 8) & 0xf);
+
+ output_dtd.part2.h_sync_off = h_sync_offset;
+ output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+ output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+ (v_sync_len & 0xf);
+ output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
+ ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
+ ((v_sync_len & 0x30) >> 4);
+
+ output_dtd.part2.dtd_flags = 0x18;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ output_dtd.part2.dtd_flags |= 0x2;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ output_dtd.part2.dtd_flags |= 0x4;
+
+ output_dtd.part2.sdvo_flags = 0;
+ output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+ output_dtd.part2.reserved = 0;
+
+ /* Set the output timing to the screen */
+ intel_sdvo_set_target_output(intel_output, sdvo_priv->active_outputs);
+ intel_sdvo_set_output_timing(intel_output, &output_dtd);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ /* We would like to use i830_sdvo_create_preferred_input_timing() to
+ * provide the device with a timing it can support, if it supports that
+ * feature. However, presumably we would need to adjust the CRTC to
+ * output the preferred timing, and we don't support that currently.
+ */
+ intel_sdvo_set_input_timing(intel_output, &output_dtd);
+
+ switch (intel_sdvo_get_pixel_multiplier(mode)) {
+ case 1:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_1X);
+ break;
+ case 2:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_2X);
+ break;
+ case 4:
+ intel_sdvo_set_clock_rate_mult(intel_output,
+ SDVO_CLOCK_RATE_MULT_4X);
+ break;
+ }
+
+ /* Set the SDVO control regs. */
+ if (0/*IS_I965GM(dev)*/) {
+ sdvox = SDVO_BORDER_ENABLE;
+ } else {
+ sdvox = I915_READ(sdvo_priv->output_device);
+ switch (sdvo_priv->output_device) {
+ case SDVOB:
+ sdvox &= SDVOB_PRESERVE_MASK;
+ break;
+ case SDVOC:
+ sdvox &= SDVOC_PRESERVE_MASK;
+ break;
+ }
+ sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+ }
+ if (intel_crtc->pipe == 1)
+ sdvox |= SDVO_PIPE_B_SELECT;
+
+ sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode);
+ if (IS_I965G(dev)) {
+ /* done in crtc_mode_set as the dpll_md reg must be written
+ early */
+ } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+ /* done in crtc_mode_set as it lives inside the
+ dpll register */
+ } else {
+ sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
+ }
+
+ intel_sdvo_write_sdvox(intel_output, sdvox);
+}
+
+static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ u32 temp;
+
+ if (mode != DRM_MODE_DPMS_ON) {
+ intel_sdvo_set_active_outputs(intel_output, 0);
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+
+ if (mode == DRM_MODE_DPMS_OFF) {
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) != 0) {
+ intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE);
+ }
+ }
+ } else {
+ bool input1, input2;
+ int i;
+ u8 status;
+
+ temp = I915_READ(sdvo_priv->output_device);
+ if ((temp & SDVO_ENABLE) == 0)
+ intel_sdvo_write_sdvox(intel_output, temp | SDVO_ENABLE);
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1,
+ &input2);
+
+
+ /* Warn if the device reported failure to sync.
+ * A lot of SDVO devices fail to notify of sync, but it's
+ * a given it the status is a success, we succeeded.
+ */
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ if (0)
+ intel_sdvo_set_encoder_power_state(intel_output, mode);
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->active_outputs);
+ }
+ return;
+}
+
+static void intel_sdvo_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+
+ sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(intel_output);
+ intel_sdvo_get_active_outputs(intel_output, &sdvo_priv->save_active_outputs);
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_get_input_timing(intel_output,
+ &sdvo_priv->save_input_dtd_2);
+ }
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output)
+ {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_get_output_timing(intel_output,
+ &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device);
+}
+
+static void intel_sdvo_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+ int o;
+ int i;
+ bool input1, input2;
+ u8 status;
+
+ intel_sdvo_set_active_outputs(intel_output, 0);
+
+ for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++)
+ {
+ u16 this_output = (1 << o);
+ if (sdvo_priv->caps.output_flags & this_output) {
+ intel_sdvo_set_target_output(intel_output, this_output);
+ intel_sdvo_set_output_timing(intel_output, &sdvo_priv->save_output_dtd[o]);
+ }
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+ intel_sdvo_set_target_input(intel_output, true, false);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_1);
+ }
+
+ if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+ intel_sdvo_set_target_input(intel_output, false, true);
+ intel_sdvo_set_input_timing(intel_output, &sdvo_priv->save_input_dtd_2);
+ }
+
+ intel_sdvo_set_clock_rate_mult(intel_output, sdvo_priv->save_sdvo_mult);
+
+ I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+
+ if (sdvo_priv->save_SDVOX & SDVO_ENABLE)
+ {
+ for (i = 0; i < 2; i++)
+ intel_wait_for_vblank(dev);
+ status = intel_sdvo_get_trained_inputs(intel_output, &input1, &input2);
+ if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+ DRM_DEBUG("First %s output reported failure to sync\n",
+ SDVO_NAME(sdvo_priv));
+ }
+
+ intel_sdvo_set_active_outputs(intel_output, sdvo_priv->save_active_outputs);
+}
+
+static int intel_sdvo_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (sdvo_priv->pixel_clock_min > mode->clock)
+ return MODE_CLOCK_LOW;
+
+ if (sdvo_priv->pixel_clock_max < mode->clock)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static bool intel_sdvo_get_capabilities(struct intel_output *intel_output, struct intel_sdvo_caps *caps)
+{
+ u8 status;
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, caps, sizeof(*caps));
+ if (status != SDVO_CMD_STATUS_SUCCESS)
+ return false;
+
+ return true;
+}
+
+struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB)
+{
+ struct drm_connector *connector = NULL;
+ struct intel_output *iout = NULL;
+ struct intel_sdvo_priv *sdvo;
+
+ /* find the sdvo connector */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ iout = to_intel_output(connector);
+
+ if (iout->type != INTEL_OUTPUT_SDVO)
+ continue;
+
+ sdvo = iout->dev_priv;
+
+ if (sdvo->output_device == SDVOB && sdvoB)
+ return connector;
+
+ if (sdvo->output_device == SDVOC && !sdvoB)
+ return connector;
+
+ }
+
+ return NULL;
+}
+
+int intel_sdvo_supports_hotplug(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output;
+ DRM_DEBUG("\n");
+
+ if (!connector)
+ return 0;
+
+ intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (response[0] !=0)
+ return 1;
+
+ return 0;
+}
+
+void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+
+ if (on) {
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ } else {
+ response[0] = 0;
+ response[1] = 0;
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
+ }
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
+ intel_sdvo_read_response(intel_output, &response, 2);
+}
+
+static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector)
+{
+ u8 response[2];
+ u8 status;
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ intel_sdvo_write_cmd(intel_output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+ if ((response[0] != 0) || (response[1] != 0))
+ return connector_status_connected;
+ else
+ return connector_status_disconnected;
+}
+
+static int intel_sdvo_get_modes(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ /* set the bus switch and get the modes */
+ intel_sdvo_set_control_bus_switch(intel_output, SDVO_CONTROL_BUS_DDC2);
+ intel_ddc_get_modes(intel_output);
+
+ if (list_empty(&connector->probed_modes))
+ return 0;
+ return 1;
+}
+
+static void intel_sdvo_destroy(struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ if (intel_output->i2c_bus)
+ intel_i2c_destroy(intel_output->i2c_bus);
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+}
+
+static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
+ .dpms = intel_sdvo_dpms,
+ .mode_fixup = intel_sdvo_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_sdvo_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
+ .save = intel_sdvo_save,
+ .restore = intel_sdvo_restore,
+ .detect = intel_sdvo_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = intel_sdvo_destroy,
+};
+
+static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
+ .get_modes = intel_sdvo_get_modes,
+ .mode_valid = intel_sdvo_mode_valid,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
+ .destroy = intel_sdvo_enc_destroy,
+};
+
+
+void intel_sdvo_init(struct drm_device *dev, int output_device)
+{
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_sdvo_priv *sdvo_priv;
+ struct intel_i2c_chan *i2cbus = NULL;
+ int connector_type;
+ u8 ch[0x40];
+ int i;
+ int encoder_type, output_id;
+
+ intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL);
+ if (!intel_output) {
+ return;
+ }
+
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_sdvo_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown);
+ drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs);
+ sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_SDVO;
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ /* setup the DDC bus. */
+ if (output_device == SDVOB)
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+ else
+ i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+ if (!i2cbus)
+ goto err_connector;
+
+ sdvo_priv->i2c_bus = i2cbus;
+
+ if (output_device == SDVOB) {
+ output_id = 1;
+ sdvo_priv->i2c_bus->slave_addr = 0x38;
+ } else {
+ output_id = 2;
+ sdvo_priv->i2c_bus->slave_addr = 0x39;
+ }
+
+ sdvo_priv->output_device = output_device;
+ intel_output->i2c_bus = i2cbus;
+ intel_output->dev_priv = sdvo_priv;
+
+
+ /* Read the regs to test if we can talk to the device */
+ for (i = 0; i < 0x40; i++) {
+ if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) {
+ DRM_DEBUG("No SDVO device found on SDVO%c\n",
+ output_device == SDVOB ? 'B' : 'C');
+ goto err_i2c;
+ }
+ }
+
+ intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
+
+ memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs));
+
+ /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
+ if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_DAC;
+ connector_type = DRM_MODE_CONNECTOR_VGA;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1)
+ {
+ sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector_type = DRM_MODE_CONNECTOR_DVID;
+ }
+ else
+ {
+ unsigned char bytes[2];
+
+ memcpy (bytes, &sdvo_priv->caps.output_flags, 2);
+ DRM_DEBUG("%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+ SDVO_NAME(sdvo_priv),
+ bytes[0], bytes[1]);
+ goto err_i2c;
+ }
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type);
+ drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs);
+ connector->connector_type = connector_type;
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ drm_sysfs_connector_add(connector);
+
+ /* Set the input timing to the screen. Assume always input 0. */
+ intel_sdvo_set_target_input(intel_output, true, false);
+
+ intel_sdvo_get_input_pixel_clock_range(intel_output,
+ &sdvo_priv->pixel_clock_min,
+ &sdvo_priv->pixel_clock_max);
+
+
+ DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, "
+ "clock range %dMHz - %dMHz, "
+ "input 1: %c, input 2: %c, "
+ "output 1: %c, output 2: %c\n",
+ SDVO_NAME(sdvo_priv),
+ sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+ sdvo_priv->caps.device_rev_id,
+ sdvo_priv->pixel_clock_min / 1000,
+ sdvo_priv->pixel_clock_max / 1000,
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+ (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+ /* check currently supported outputs */
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+ sdvo_priv->caps.output_flags &
+ (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+
+ intel_output->ddc_bus = i2cbus;
+
+ return;
+
+err_i2c:
+ intel_i2c_destroy(intel_output->i2c_bus);
+err_connector:
+ drm_connector_cleanup(connector);
+ kfree(intel_output);
+
+ return;
+}
diff --git a/drivers/gpu/drm/i915/intel_sdvo_regs.h b/drivers/gpu/drm/i915/intel_sdvo_regs.h
new file mode 100644
index 000000000000..861a43f8693c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_sdvo_regs.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+/**
+ * @file SDVO command definitions and structures.
+ */
+
+#define SDVO_OUTPUT_FIRST (0)
+#define SDVO_OUTPUT_TMDS0 (1 << 0)
+#define SDVO_OUTPUT_RGB0 (1 << 1)
+#define SDVO_OUTPUT_CVBS0 (1 << 2)
+#define SDVO_OUTPUT_SVID0 (1 << 3)
+#define SDVO_OUTPUT_YPRPB0 (1 << 4)
+#define SDVO_OUTPUT_SCART0 (1 << 5)
+#define SDVO_OUTPUT_LVDS0 (1 << 6)
+#define SDVO_OUTPUT_TMDS1 (1 << 8)
+#define SDVO_OUTPUT_RGB1 (1 << 9)
+#define SDVO_OUTPUT_CVBS1 (1 << 10)
+#define SDVO_OUTPUT_SVID1 (1 << 11)
+#define SDVO_OUTPUT_YPRPB1 (1 << 12)
+#define SDVO_OUTPUT_SCART1 (1 << 13)
+#define SDVO_OUTPUT_LVDS1 (1 << 14)
+#define SDVO_OUTPUT_LAST (14)
+
+struct intel_sdvo_caps {
+ u8 vendor_id;
+ u8 device_id;
+ u8 device_rev_id;
+ u8 sdvo_version_major;
+ u8 sdvo_version_minor;
+ unsigned int sdvo_inputs_mask:2;
+ unsigned int smooth_scaling:1;
+ unsigned int sharp_scaling:1;
+ unsigned int up_scaling:1;
+ unsigned int down_scaling:1;
+ unsigned int stall_support:1;
+ unsigned int pad:1;
+ u16 output_flags;
+} __attribute__((packed));
+
+/** This matches the EDID DTD structure, more or less */
+struct intel_sdvo_dtd {
+ struct {
+ u16 clock; /**< pixel clock, in 10kHz units */
+ u8 h_active; /**< lower 8 bits (pixels) */
+ u8 h_blank; /**< lower 8 bits (pixels) */
+ u8 h_high; /**< upper 4 bits each h_active, h_blank */
+ u8 v_active; /**< lower 8 bits (lines) */
+ u8 v_blank; /**< lower 8 bits (lines) */
+ u8 v_high; /**< upper 4 bits each v_active, v_blank */
+ } part1;
+
+ struct {
+ u8 h_sync_off; /**< lower 8 bits, from hblank start */
+ u8 h_sync_width; /**< lower 8 bits (pixels) */
+ /** lower 4 bits each vsync offset, vsync width */
+ u8 v_sync_off_width;
+ /**
+ * 2 high bits of hsync offset, 2 high bits of hsync width,
+ * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+ */
+ u8 sync_off_width_high;
+ u8 dtd_flags;
+ u8 sdvo_flags;
+ /** bits 6-7 of vsync offset at bits 6-7 */
+ u8 v_sync_off_high;
+ u8 reserved;
+ } part2;
+} __attribute__((packed));
+
+struct intel_sdvo_pixel_clock_range {
+ u16 min; /**< pixel clock, in 10kHz units */
+ u16 max; /**< pixel clock, in 10kHz units */
+} __attribute__((packed));
+
+struct intel_sdvo_preferred_input_timing_args {
+ u16 clock;
+ u16 width;
+ u16 height;
+} __attribute__((packed));
+
+/* I2C registers for SDVO */
+#define SDVO_I2C_ARG_0 0x07
+#define SDVO_I2C_ARG_1 0x06
+#define SDVO_I2C_ARG_2 0x05
+#define SDVO_I2C_ARG_3 0x04
+#define SDVO_I2C_ARG_4 0x03
+#define SDVO_I2C_ARG_5 0x02
+#define SDVO_I2C_ARG_6 0x01
+#define SDVO_I2C_ARG_7 0x00
+#define SDVO_I2C_OPCODE 0x08
+#define SDVO_I2C_CMD_STATUS 0x09
+#define SDVO_I2C_RETURN_0 0x0a
+#define SDVO_I2C_RETURN_1 0x0b
+#define SDVO_I2C_RETURN_2 0x0c
+#define SDVO_I2C_RETURN_3 0x0d
+#define SDVO_I2C_RETURN_4 0x0e
+#define SDVO_I2C_RETURN_5 0x0f
+#define SDVO_I2C_RETURN_6 0x10
+#define SDVO_I2C_RETURN_7 0x11
+#define SDVO_I2C_VENDOR_BEGIN 0x20
+
+/* Status results */
+#define SDVO_CMD_STATUS_POWER_ON 0x0
+#define SDVO_CMD_STATUS_SUCCESS 0x1
+#define SDVO_CMD_STATUS_NOTSUPP 0x2
+#define SDVO_CMD_STATUS_INVALID_ARG 0x3
+#define SDVO_CMD_STATUS_PENDING 0x4
+#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5
+#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6
+
+/* SDVO commands, argument/result registers */
+
+#define SDVO_CMD_RESET 0x01
+
+/** Returns a struct intel_sdvo_caps */
+#define SDVO_CMD_GET_DEVICE_CAPS 0x02
+
+#define SDVO_CMD_GET_FIRMWARE_REV 0x86
+# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0
+# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1
+# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2
+
+/**
+ * Reports which inputs are trained (managed to sync).
+ *
+ * Devices must have trained within 2 vsyncs of a mode change.
+ */
+#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
+struct intel_sdvo_get_trained_inputs_response {
+ unsigned int input0_trained:1;
+ unsigned int input1_trained:1;
+ unsigned int pad:6;
+} __attribute__((packed));
+
+/** Returns a struct intel_sdvo_output_flags of active outputs. */
+#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04
+
+/**
+ * Sets the current set of active outputs.
+ *
+ * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP
+ * on multi-output devices.
+ */
+#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05
+
+/**
+ * Returns the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Returns two struct intel_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_GET_IN_OUT_MAP 0x06
+
+/**
+ * Sets the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Takes two struct i380_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_SET_IN_OUT_MAP 0x07
+
+/**
+ * Returns a struct intel_sdvo_output_flags of attached displays.
+ */
+#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b
+
+/**
+ * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
+ */
+#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c
+
+/**
+ * Takes a struct intel_sdvo_output_flags.
+ */
+#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d
+
+/**
+ * Returns a struct intel_sdvo_output_flags of displays with hot plug
+ * interrupts enabled.
+ */
+#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e
+
+#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
+struct intel_sdvo_get_interrupt_event_source_response {
+ u16 interrupt_status;
+ unsigned int ambient_light_interrupt:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Selects which input is affected by future input commands.
+ *
+ * Commands affected include SET_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
+ */
+#define SDVO_CMD_SET_TARGET_INPUT 0x10
+struct intel_sdvo_set_target_input_args {
+ unsigned int target_1:1;
+ unsigned int pad:7;
+} __attribute__((packed));
+
+/**
+ * Takes a struct intel_sdvo_output_flags of which outputs are targetted by
+ * future output commands.
+ *
+ * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
+ * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
+ */
+#define SDVO_CMD_SET_TARGET_OUTPUT 0x11
+
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19
+/* Part 1 */
+# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2
+# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3
+# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4
+# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5
+# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6
+# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7
+/* Part 2 */
+# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0
+# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1
+# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2
+# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4
+# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7)
+# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5)
+# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3)
+# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1)
+# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5
+# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7)
+# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6)
+# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6)
+# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4)
+# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6
+
+/**
+ * Generates a DTD based on the given width, height, and flags.
+ *
+ * This will be supported by any device supporting scaling or interlaced
+ * modes.
+ */
+#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0)
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1)
+
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c
+
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d
+/** Returns a struct intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e
+
+/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
+#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f
+
+/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20
+/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
+# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
+# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
+
+#define SDVO_CMD_GET_TV_FORMAT 0x28
+
+#define SDVO_CMD_SET_TV_FORMAT 0x29
+
+#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
+#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b
+#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c
+# define SDVO_ENCODER_STATE_ON (1 << 0)
+# define SDVO_ENCODER_STATE_STANDBY (1 << 1)
+# define SDVO_ENCODER_STATE_SUSPEND (1 << 2)
+# define SDVO_ENCODER_STATE_OFF (1 << 3)
+
+#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93
+
+#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a
+# define SDVO_CONTROL_BUS_PROM 0x0
+# define SDVO_CONTROL_BUS_DDC1 0x1
+# define SDVO_CONTROL_BUS_DDC2 0x2
+# define SDVO_CONTROL_BUS_DDC3 0x3
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
new file mode 100644
index 000000000000..fbb35dc56f5c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -0,0 +1,1725 @@
+/*
+ * Copyright © 2006-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ *
+ */
+
+/** @file
+ * Integrated TV-out support for the 915GM and 945GM.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+
+enum tv_margin {
+ TV_MARGIN_LEFT, TV_MARGIN_TOP,
+ TV_MARGIN_RIGHT, TV_MARGIN_BOTTOM
+};
+
+/** Private structure for the integrated TV support */
+struct intel_tv_priv {
+ int type;
+ char *tv_format;
+ int margin[4];
+ u32 save_TV_H_CTL_1;
+ u32 save_TV_H_CTL_2;
+ u32 save_TV_H_CTL_3;
+ u32 save_TV_V_CTL_1;
+ u32 save_TV_V_CTL_2;
+ u32 save_TV_V_CTL_3;
+ u32 save_TV_V_CTL_4;
+ u32 save_TV_V_CTL_5;
+ u32 save_TV_V_CTL_6;
+ u32 save_TV_V_CTL_7;
+ u32 save_TV_SC_CTL_1, save_TV_SC_CTL_2, save_TV_SC_CTL_3;
+
+ u32 save_TV_CSC_Y;
+ u32 save_TV_CSC_Y2;
+ u32 save_TV_CSC_U;
+ u32 save_TV_CSC_U2;
+ u32 save_TV_CSC_V;
+ u32 save_TV_CSC_V2;
+ u32 save_TV_CLR_KNOBS;
+ u32 save_TV_CLR_LEVEL;
+ u32 save_TV_WIN_POS;
+ u32 save_TV_WIN_SIZE;
+ u32 save_TV_FILTER_CTL_1;
+ u32 save_TV_FILTER_CTL_2;
+ u32 save_TV_FILTER_CTL_3;
+
+ u32 save_TV_H_LUMA[60];
+ u32 save_TV_H_CHROMA[60];
+ u32 save_TV_V_LUMA[43];
+ u32 save_TV_V_CHROMA[43];
+
+ u32 save_TV_DAC;
+ u32 save_TV_CTL;
+};
+
+struct video_levels {
+ int blank, black, burst;
+};
+
+struct color_conversion {
+ u16 ry, gy, by, ay;
+ u16 ru, gu, bu, au;
+ u16 rv, gv, bv, av;
+};
+
+static const u32 filter_table[] = {
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0xB1403000, 0x2E203500, 0x35002E20, 0x3000B140,
+ 0x35A0B160, 0x2DC02E80, 0xB1403480, 0xB1603000,
+ 0x2EA03640, 0x34002D80, 0x3000B120, 0x36E0B160,
+ 0x2D202EF0, 0xB1203380, 0xB1603000, 0x2F303780,
+ 0x33002CC0, 0x3000B100, 0x3820B160, 0x2C802F50,
+ 0xB10032A0, 0xB1603000, 0x2F9038C0, 0x32202C20,
+ 0x3000B0E0, 0x3980B160, 0x2BC02FC0, 0xB0E031C0,
+ 0xB1603000, 0x2FF03A20, 0x31602B60, 0xB020B0C0,
+ 0x3AE0B160, 0x2B001810, 0xB0C03120, 0xB140B020,
+ 0x18283BA0, 0x30C02A80, 0xB020B0A0, 0x3C60B140,
+ 0x2A201838, 0xB0A03080, 0xB120B020, 0x18383D20,
+ 0x304029C0, 0xB040B080, 0x3DE0B100, 0x29601848,
+ 0xB0803000, 0xB100B040, 0x18483EC0, 0xB0402900,
+ 0xB040B060, 0x3F80B0C0, 0x28801858, 0xB060B080,
+ 0xB0A0B060, 0x18602820, 0xB0A02820, 0x0000B060,
+ 0x36403000, 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100, 0x36403000,
+ 0x2D002CC0, 0x30003640, 0x2D0036C0,
+ 0x35C02CC0, 0x37403000, 0x2C802D40, 0x30003540,
+ 0x2D8037C0, 0x34C02C40, 0x38403000, 0x2BC02E00,
+ 0x30003440, 0x2E2038C0, 0x34002B80, 0x39803000,
+ 0x2B402E40, 0x30003380, 0x2E603A00, 0x33402B00,
+ 0x3A803040, 0x2A802EA0, 0x30403300, 0x2EC03B40,
+ 0x32802A40, 0x3C003040, 0x2A002EC0, 0x30803240,
+ 0x2EC03C80, 0x320029C0, 0x3D403080, 0x29402F00,
+ 0x308031C0, 0x2F203DC0, 0x31802900, 0x3E8030C0,
+ 0x28802F40, 0x30C03140, 0x2F203F40, 0x31402840,
+ 0x28003100, 0x28002F00, 0x00003100,
+};
+
+/*
+ * Color conversion values have 3 separate fixed point formats:
+ *
+ * 10 bit fields (ay, au)
+ * 1.9 fixed point (b.bbbbbbbbb)
+ * 11 bit fields (ry, by, ru, gu, gv)
+ * exp.mantissa (ee.mmmmmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmmmmm)
+ * ee = 01 = 10^-2 (0.0mmmmmmmmm)
+ * ee = 10 = 10^-3 (0.00mmmmmmmmm)
+ * ee = 11 = 10^-4 (0.000mmmmmmmmm)
+ * 12 bit fields (gy, rv, bu)
+ * exp.mantissa (eee.mmmmmmmmm)
+ * eee = 000 = 10^-1 (0.mmmmmmmmm)
+ * eee = 001 = 10^-2 (0.0mmmmmmmmm)
+ * eee = 010 = 10^-3 (0.00mmmmmmmmm)
+ * eee = 011 = 10^-4 (0.000mmmmmmmmm)
+ * eee = 100 = reserved
+ * eee = 101 = reserved
+ * eee = 110 = reserved
+ * eee = 111 = 10^0 (m.mmmmmmmm) (only usable for 1.0 representation)
+ *
+ * Saturation and contrast are 8 bits, with their own representation:
+ * 8 bit field (saturation, contrast)
+ * exp.mantissa (ee.mmmmmm)
+ * ee = 00 = 10^-1 (0.mmmmmm)
+ * ee = 01 = 10^0 (m.mmmmm)
+ * ee = 10 = 10^1 (mm.mmmm)
+ * ee = 11 = 10^2 (mmm.mmm)
+ *
+ * Simple conversion function:
+ *
+ * static u32
+ * float_to_csc_11(float f)
+ * {
+ * u32 exp;
+ * u32 mant;
+ * u32 ret;
+ *
+ * if (f < 0)
+ * f = -f;
+ *
+ * if (f >= 1) {
+ * exp = 0x7;
+ * mant = 1 << 8;
+ * } else {
+ * for (exp = 0; exp < 3 && f < 0.5; exp++)
+ * f *= 2.0;
+ * mant = (f * (1 << 9) + 0.5);
+ * if (mant >= (1 << 9))
+ * mant = (1 << 9) - 1;
+ * }
+ * ret = (exp << 9) | mant;
+ * return ret;
+ * }
+ */
+
+/*
+ * Behold, magic numbers! If we plant them they might grow a big
+ * s-video cable to the sky... or something.
+ *
+ * Pre-converted to appropriate hex value.
+ */
+
+/*
+ * PAL & NTSC values for composite & s-video connections
+ */
+static const struct color_conversion ntsc_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion ntsc_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion ntsc_j_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0119,
+ .ru = 0x074c, .gu = 0x0546, .bu = 0x05ec, .au = 0x0f00,
+ .rv = 0x035a, .gv = 0x0322, .bv = 0x06e1, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_composite = {
+ .blank = 225, .black = 225, .burst = 113,
+};
+
+static const struct color_conversion ntsc_j_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x014c,
+ .ru = 0x0788, .gu = 0x0581, .bu = 0x0322, .au = 0x0f00,
+ .rv = 0x0399, .gv = 0x0356, .bv = 0x070a, .av = 0x0f00,
+};
+
+static const struct video_levels ntsc_j_levels_svideo = {
+ .blank = 266, .black = 266, .burst = 133,
+};
+
+static const struct color_conversion pal_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0113,
+ .ru = 0x0745, .gu = 0x053f, .bu = 0x05e1, .au = 0x0f00,
+ .rv = 0x0353, .gv = 0x031c, .bv = 0x06dc, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_composite = {
+ .blank = 237, .black = 237, .burst = 118,
+};
+
+static const struct color_conversion pal_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0145,
+ .ru = 0x0780, .gu = 0x0579, .bu = 0x031c, .au = 0x0f00,
+ .rv = 0x0390, .gv = 0x034f, .bv = 0x0705, .av = 0x0f00,
+};
+
+static const struct video_levels pal_levels_svideo = {
+ .blank = 280, .black = 280, .burst = 139,
+};
+
+static const struct color_conversion pal_m_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_composite = {
+ .blank = 225, .black = 267, .burst = 113,
+};
+
+static const struct color_conversion pal_m_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_m_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 133,
+};
+
+static const struct color_conversion pal_n_csc_composite = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0104,
+ .ru = 0x0733, .gu = 0x052d, .bu = 0x05c7, .au = 0x0f00,
+ .rv = 0x0340, .gv = 0x030c, .bv = 0x06d0, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_composite = {
+ .blank = 225, .black = 267, .burst = 118,
+};
+
+static const struct color_conversion pal_n_csc_svideo = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0134,
+ .ru = 0x076a, .gu = 0x0564, .bu = 0x030d, .au = 0x0f00,
+ .rv = 0x037a, .gv = 0x033d, .bv = 0x06f6, .av = 0x0f00,
+};
+
+static const struct video_levels pal_n_levels_svideo = {
+ .blank = 266, .black = 316, .burst = 139,
+};
+
+/*
+ * Component connections
+ */
+static const struct color_conversion sdtv_csc_yprpb = {
+ .ry = 0x0332, .gy = 0x012d, .by = 0x07d3, .ay = 0x0146,
+ .ru = 0x0559, .gu = 0x0353, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03ad, .bv = 0x074d, .av = 0x0f00,
+};
+
+static const struct color_conversion sdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct color_conversion hdtv_csc_yprpb = {
+ .ry = 0x05b3, .gy = 0x016e, .by = 0x0728, .ay = 0x0146,
+ .ru = 0x07d5, .gu = 0x038b, .bu = 0x0100, .au = 0x0f00,
+ .rv = 0x0100, .gv = 0x03d1, .bv = 0x06bc, .av = 0x0f00,
+};
+
+static const struct color_conversion hdtv_csc_rgb = {
+ .ry = 0x0000, .gy = 0x0f00, .by = 0x0000, .ay = 0x0166,
+ .ru = 0x0000, .gu = 0x0000, .bu = 0x0f00, .au = 0x0166,
+ .rv = 0x0f00, .gv = 0x0000, .bv = 0x0000, .av = 0x0166,
+};
+
+static const struct video_levels component_levels = {
+ .blank = 279, .black = 279, .burst = 0,
+};
+
+
+struct tv_mode {
+ char *name;
+ int clock;
+ int refresh; /* in millihertz (for precision) */
+ u32 oversample;
+ int hsync_end, hblank_start, hblank_end, htotal;
+ bool progressive, trilevel_sync, component_only;
+ int vsync_start_f1, vsync_start_f2, vsync_len;
+ bool veq_ena;
+ int veq_start_f1, veq_start_f2, veq_len;
+ int vi_end_f1, vi_end_f2, nbr_end;
+ bool burst_ena;
+ int hburst_start, hburst_len;
+ int vburst_start_f1, vburst_end_f1;
+ int vburst_start_f2, vburst_end_f2;
+ int vburst_start_f3, vburst_end_f3;
+ int vburst_start_f4, vburst_end_f4;
+ /*
+ * subcarrier programming
+ */
+ int dda2_size, dda3_size, dda1_inc, dda2_inc, dda3_inc;
+ u32 sc_reset;
+ bool pal_burst;
+ /*
+ * blank/black levels
+ */
+ const struct video_levels *composite_levels, *svideo_levels;
+ const struct color_conversion *composite_color, *svideo_color;
+ const u32 *filter_table;
+ int max_srcw;
+};
+
+
+/*
+ * Sub carrier DDA
+ *
+ * I think this works as follows:
+ *
+ * subcarrier freq = pixel_clock * (dda1_inc + dda2_inc / dda2_size) / 4096
+ *
+ * Presumably, when dda3 is added in, it gets to adjust the dda2_inc value
+ *
+ * So,
+ * dda1_ideal = subcarrier/pixel * 4096
+ * dda1_inc = floor (dda1_ideal)
+ * dda2 = dda1_ideal - dda1_inc
+ *
+ * then pick a ratio for dda2 that gives the closest approximation. If
+ * you can't get close enough, you can play with dda3 as well. This
+ * seems likely to happen when dda2 is small as the jumps would be larger
+ *
+ * To invert this,
+ *
+ * pixel_clock = subcarrier * 4096 / (dda1_inc + dda2_inc / dda2_size)
+ *
+ * The constants below were all computed using a 107.520MHz clock
+ */
+
+/**
+ * Register programming values for TV modes.
+ *
+ * These values account for -1s required.
+ */
+
+const static struct tv_mode tv_modes[] = {
+ {
+ .name = "NTSC-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-443",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 4.43MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = 8,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &ntsc_m_levels_composite,
+ .composite_color = &ntsc_m_csc_composite,
+ .svideo_levels = &ntsc_m_levels_svideo,
+ .svideo_color = &ntsc_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "NTSC-J",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &ntsc_j_levels_composite,
+ .composite_color = &ntsc_j_csc_composite,
+ .svideo_levels = &ntsc_j_levels_svideo,
+ .svideo_color = &ntsc_j_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "PAL-M",
+ .clock = 107520,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ /* 525 Lines, 60 Fields, 15.734KHz line, Sub-Carrier 3.580MHz */
+ .hsync_end = 64, .hblank_end = 124,
+ .hblank_start = 836, .htotal = 857,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 20, .vi_end_f2 = 21,
+ .nbr_end = 240,
+
+ .burst_ena = true,
+ .hburst_start = 72, .hburst_len = 34,
+ .vburst_start_f1 = 9, .vburst_end_f1 = 240,
+ .vburst_start_f2 = 10, .vburst_end_f2 = 240,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 240,
+ .vburst_start_f4 = 10, .vburst_end_f4 = 240,
+
+ /* desired 3.5800000 actual 3.5800000 clock 107.52 */
+ .dda1_inc = 136,
+ .dda2_inc = 7624, .dda2_size = 20013,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_4,
+ .pal_burst = false,
+
+ .composite_levels = &pal_m_levels_composite,
+ .composite_color = &pal_m_csc_composite,
+ .svideo_levels = &pal_m_levels_svideo,
+ .svideo_color = &pal_m_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL-N",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = false, .trilevel_sync = false,
+
+
+ .vsync_start_f1 = 6, .vsync_start_f2 = 7,
+ .vsync_len = 6,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 18,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = true,
+ .hburst_start = 73, .hburst_len = 34,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &pal_n_levels_composite,
+ .composite_color = &pal_n_csc_composite,
+ .svideo_levels = &pal_n_levels_svideo,
+ .svideo_color = &pal_n_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ /* 625 Lines, 50 Fields, 15.625KHz line, Sub-Carrier 4.434MHz */
+ .name = "PAL",
+ .clock = 107520,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_8X,
+ .component_only = 0,
+
+ .hsync_end = 64, .hblank_end = 128,
+ .hblank_start = 844, .htotal = 863,
+
+ .progressive = false, .trilevel_sync = false,
+
+ .vsync_start_f1 = 5, .vsync_start_f2 = 6,
+ .vsync_len = 5,
+
+ .veq_ena = true, .veq_start_f1 = 0,
+ .veq_start_f2 = 1, .veq_len = 15,
+
+ .vi_end_f1 = 24, .vi_end_f2 = 25,
+ .nbr_end = 286,
+
+ .burst_ena = true,
+ .hburst_start = 73, .hburst_len = 32,
+ .vburst_start_f1 = 8, .vburst_end_f1 = 285,
+ .vburst_start_f2 = 8, .vburst_end_f2 = 286,
+ .vburst_start_f3 = 9, .vburst_end_f3 = 286,
+ .vburst_start_f4 = 9, .vburst_end_f4 = 285,
+
+ /* desired 4.4336180 actual 4.4336180 clock 107.52 */
+ .dda1_inc = 168,
+ .dda2_inc = 18557, .dda2_size = 20625,
+ .dda3_inc = 0, .dda3_size = 0,
+ .sc_reset = TV_SC_RESET_EVERY_8,
+ .pal_burst = true,
+
+ .composite_levels = &pal_levels_composite,
+ .composite_color = &pal_csc_composite,
+ .svideo_levels = &pal_levels_svideo,
+ .svideo_color = &pal_csc_svideo,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@59.94Hz",
+ .clock = 107520,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 857,
+
+ .progressive = true,.trilevel_sync = false,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "480p@60Hz",
+ .clock = 107520,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 122,
+ .hblank_start = 842, .htotal = 856,
+
+ .progressive = true,.trilevel_sync = false,
+
+ .vsync_start_f1 = 12, .vsync_start_f2 = 12,
+ .vsync_len = 12,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 44, .vi_end_f2 = 44,
+ .nbr_end = 496,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "576p",
+ .clock = 107520,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_4X,
+ .component_only = 1,
+
+ .hsync_end = 64, .hblank_end = 139,
+ .hblank_start = 859, .htotal = 863,
+
+ .progressive = true, .trilevel_sync = false,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 48, .vi_end_f2 = 48,
+ .nbr_end = 575,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@60Hz",
+ .clock = 148800,
+ .refresh = 60000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1649,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@59.94Hz",
+ .clock = 148800,
+ .refresh = 59940,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1651,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "720p@50Hz",
+ .clock = 148800,
+ .refresh = 50000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 80, .hblank_end = 300,
+ .hblank_start = 1580, .htotal = 1979,
+
+ .progressive = true, .trilevel_sync = true,
+
+ .vsync_start_f1 = 10, .vsync_start_f2 = 10,
+ .vsync_len = 10,
+
+ .veq_ena = false,
+
+ .vi_end_f1 = 29, .vi_end_f2 = 29,
+ .nbr_end = 719,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ .max_srcw = 800
+ },
+ {
+ .name = "1080i@50Hz",
+ .clock = 148800,
+ .refresh = 25000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2639,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@60Hz",
+ .clock = 148800,
+ .refresh = 30000,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2199,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+ {
+ .name = "1080i@59.94Hz",
+ .clock = 148800,
+ .refresh = 29970,
+ .oversample = TV_OVERSAMPLE_2X,
+ .component_only = 1,
+
+ .hsync_end = 88, .hblank_end = 235,
+ .hblank_start = 2155, .htotal = 2200,
+
+ .progressive = false, .trilevel_sync = true,
+
+ .vsync_start_f1 = 4, .vsync_start_f2 = 5,
+ .vsync_len = 10,
+
+ .veq_ena = true, .veq_start_f1 = 4,
+ .veq_start_f2 = 4, .veq_len = 10,
+
+
+ .vi_end_f1 = 21, .vi_end_f2 = 22,
+ .nbr_end = 539,
+
+ .burst_ena = false,
+
+ .filter_table = filter_table,
+ },
+};
+
+#define NUM_TV_MODES sizeof(tv_modes) / sizeof (tv_modes[0])
+
+static void
+intel_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ switch(mode) {
+ case DRM_MODE_DPMS_ON:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
+ break;
+ }
+}
+
+static void
+intel_tv_save(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int i;
+
+ tv_priv->save_TV_H_CTL_1 = I915_READ(TV_H_CTL_1);
+ tv_priv->save_TV_H_CTL_2 = I915_READ(TV_H_CTL_2);
+ tv_priv->save_TV_H_CTL_3 = I915_READ(TV_H_CTL_3);
+ tv_priv->save_TV_V_CTL_1 = I915_READ(TV_V_CTL_1);
+ tv_priv->save_TV_V_CTL_2 = I915_READ(TV_V_CTL_2);
+ tv_priv->save_TV_V_CTL_3 = I915_READ(TV_V_CTL_3);
+ tv_priv->save_TV_V_CTL_4 = I915_READ(TV_V_CTL_4);
+ tv_priv->save_TV_V_CTL_5 = I915_READ(TV_V_CTL_5);
+ tv_priv->save_TV_V_CTL_6 = I915_READ(TV_V_CTL_6);
+ tv_priv->save_TV_V_CTL_7 = I915_READ(TV_V_CTL_7);
+ tv_priv->save_TV_SC_CTL_1 = I915_READ(TV_SC_CTL_1);
+ tv_priv->save_TV_SC_CTL_2 = I915_READ(TV_SC_CTL_2);
+ tv_priv->save_TV_SC_CTL_3 = I915_READ(TV_SC_CTL_3);
+
+ tv_priv->save_TV_CSC_Y = I915_READ(TV_CSC_Y);
+ tv_priv->save_TV_CSC_Y2 = I915_READ(TV_CSC_Y2);
+ tv_priv->save_TV_CSC_U = I915_READ(TV_CSC_U);
+ tv_priv->save_TV_CSC_U2 = I915_READ(TV_CSC_U2);
+ tv_priv->save_TV_CSC_V = I915_READ(TV_CSC_V);
+ tv_priv->save_TV_CSC_V2 = I915_READ(TV_CSC_V2);
+ tv_priv->save_TV_CLR_KNOBS = I915_READ(TV_CLR_KNOBS);
+ tv_priv->save_TV_CLR_LEVEL = I915_READ(TV_CLR_LEVEL);
+ tv_priv->save_TV_WIN_POS = I915_READ(TV_WIN_POS);
+ tv_priv->save_TV_WIN_SIZE = I915_READ(TV_WIN_SIZE);
+ tv_priv->save_TV_FILTER_CTL_1 = I915_READ(TV_FILTER_CTL_1);
+ tv_priv->save_TV_FILTER_CTL_2 = I915_READ(TV_FILTER_CTL_2);
+ tv_priv->save_TV_FILTER_CTL_3 = I915_READ(TV_FILTER_CTL_3);
+
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_LUMA[i] = I915_READ(TV_H_LUMA_0 + (i <<2));
+ for (i = 0; i < 60; i++)
+ tv_priv->save_TV_H_CHROMA[i] = I915_READ(TV_H_CHROMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_LUMA[i] = I915_READ(TV_V_LUMA_0 + (i <<2));
+ for (i = 0; i < 43; i++)
+ tv_priv->save_TV_V_CHROMA[i] = I915_READ(TV_V_CHROMA_0 + (i <<2));
+
+ tv_priv->save_TV_DAC = I915_READ(TV_DAC);
+ tv_priv->save_TV_CTL = I915_READ(TV_CTL);
+}
+
+static void
+intel_tv_restore(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_crtc *crtc = connector->encoder->crtc;
+ struct intel_crtc *intel_crtc;
+ int i;
+
+ /* FIXME: No CRTC? */
+ if (!crtc)
+ return;
+
+ intel_crtc = to_intel_crtc(crtc);
+ I915_WRITE(TV_H_CTL_1, tv_priv->save_TV_H_CTL_1);
+ I915_WRITE(TV_H_CTL_2, tv_priv->save_TV_H_CTL_2);
+ I915_WRITE(TV_H_CTL_3, tv_priv->save_TV_H_CTL_3);
+ I915_WRITE(TV_V_CTL_1, tv_priv->save_TV_V_CTL_1);
+ I915_WRITE(TV_V_CTL_2, tv_priv->save_TV_V_CTL_2);
+ I915_WRITE(TV_V_CTL_3, tv_priv->save_TV_V_CTL_3);
+ I915_WRITE(TV_V_CTL_4, tv_priv->save_TV_V_CTL_4);
+ I915_WRITE(TV_V_CTL_5, tv_priv->save_TV_V_CTL_5);
+ I915_WRITE(TV_V_CTL_6, tv_priv->save_TV_V_CTL_6);
+ I915_WRITE(TV_V_CTL_7, tv_priv->save_TV_V_CTL_7);
+ I915_WRITE(TV_SC_CTL_1, tv_priv->save_TV_SC_CTL_1);
+ I915_WRITE(TV_SC_CTL_2, tv_priv->save_TV_SC_CTL_2);
+ I915_WRITE(TV_SC_CTL_3, tv_priv->save_TV_SC_CTL_3);
+
+ I915_WRITE(TV_CSC_Y, tv_priv->save_TV_CSC_Y);
+ I915_WRITE(TV_CSC_Y2, tv_priv->save_TV_CSC_Y2);
+ I915_WRITE(TV_CSC_U, tv_priv->save_TV_CSC_U);
+ I915_WRITE(TV_CSC_U2, tv_priv->save_TV_CSC_U2);
+ I915_WRITE(TV_CSC_V, tv_priv->save_TV_CSC_V);
+ I915_WRITE(TV_CSC_V2, tv_priv->save_TV_CSC_V2);
+ I915_WRITE(TV_CLR_KNOBS, tv_priv->save_TV_CLR_KNOBS);
+ I915_WRITE(TV_CLR_LEVEL, tv_priv->save_TV_CLR_LEVEL);
+
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ if (!IS_I9XX(dev)) {
+ /* Wait for vblank for the disable to take effect */
+ intel_wait_for_vblank(dev);
+ }
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, tv_priv->save_TV_FILTER_CTL_1);
+ I915_WRITE(TV_FILTER_CTL_2, tv_priv->save_TV_FILTER_CTL_2);
+ I915_WRITE(TV_FILTER_CTL_3, tv_priv->save_TV_FILTER_CTL_3);
+ I915_WRITE(TV_WIN_POS, tv_priv->save_TV_WIN_POS);
+ I915_WRITE(TV_WIN_SIZE, tv_priv->save_TV_WIN_SIZE);
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i <<2), tv_priv->save_TV_H_LUMA[i]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i <<2), tv_priv->save_TV_H_CHROMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i <<2), tv_priv->save_TV_V_LUMA[i]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i <<2), tv_priv->save_TV_V_CHROMA[i]);
+
+ I915_WRITE(TV_DAC, tv_priv->save_TV_DAC);
+ I915_WRITE(TV_CTL, tv_priv->save_TV_CTL);
+}
+
+static const struct tv_mode *
+intel_tv_mode_lookup (char *tv_format)
+{
+ int i;
+
+ for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) {
+ const struct tv_mode *tv_mode = &tv_modes[i];
+
+ if (!strcmp(tv_format, tv_mode->name))
+ return tv_mode;
+ }
+ return NULL;
+}
+
+static const struct tv_mode *
+intel_tv_mode_find (struct intel_output *intel_output)
+{
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+
+ return intel_tv_mode_lookup(tv_priv->tv_format);
+}
+
+static enum drm_mode_status
+intel_tv_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+
+ /* Ensure TV refresh is close to desired refresh */
+ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode)) < 1)
+ return MODE_OK;
+ return MODE_CLOCK_RANGE;
+}
+
+
+static bool
+intel_tv_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *drm_config = &dev->mode_config;
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ const struct tv_mode *tv_mode = intel_tv_mode_find (intel_output);
+ struct drm_encoder *other_encoder;
+
+ if (!tv_mode)
+ return false;
+
+ /* FIXME: lock encoder list */
+ list_for_each_entry(other_encoder, &drm_config->encoder_list, head) {
+ if (other_encoder != encoder &&
+ other_encoder->crtc == encoder->crtc)
+ return false;
+ }
+
+ adjusted_mode->clock = tv_mode->clock;
+ return true;
+}
+
+static void
+intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_output *intel_output = enc_to_intel_output(encoder);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ u32 tv_ctl;
+ u32 hctl1, hctl2, hctl3;
+ u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+ u32 scctl1, scctl2, scctl3;
+ int i, j;
+ const struct video_levels *video_levels;
+ const struct color_conversion *color_conversion;
+ bool burst_ena;
+
+ if (!tv_mode)
+ return; /* can't happen (mode_prepare prevents this) */
+
+ tv_ctl = 0;
+
+ switch (tv_priv->type) {
+ default:
+ case DRM_MODE_CONNECTOR_Unknown:
+ case DRM_MODE_CONNECTOR_Composite:
+ tv_ctl |= TV_ENC_OUTPUT_COMPOSITE;
+ video_levels = tv_mode->composite_levels;
+ color_conversion = tv_mode->composite_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ case DRM_MODE_CONNECTOR_Component:
+ tv_ctl |= TV_ENC_OUTPUT_COMPONENT;
+ video_levels = &component_levels;
+ if (tv_mode->burst_ena)
+ color_conversion = &sdtv_csc_yprpb;
+ else
+ color_conversion = &hdtv_csc_yprpb;
+ burst_ena = false;
+ break;
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ tv_ctl |= TV_ENC_OUTPUT_SVIDEO;
+ video_levels = tv_mode->svideo_levels;
+ color_conversion = tv_mode->svideo_color;
+ burst_ena = tv_mode->burst_ena;
+ break;
+ }
+ hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+ (tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+ hctl2 = (tv_mode->hburst_start << 16) |
+ (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+
+ if (burst_ena)
+ hctl2 |= TV_BURST_ENA;
+
+ hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+ (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+ vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+ (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+ (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+ vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+ (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+ (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+ vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+ (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+ (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+
+ if (tv_mode->veq_ena)
+ vctl3 |= TV_EQUAL_ENA;
+
+ vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+ (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+ vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+ (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+ vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+ (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+ vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+ (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+
+ if (intel_crtc->pipe == 1)
+ tv_ctl |= TV_ENC_PIPEB_SELECT;
+ tv_ctl |= tv_mode->oversample;
+
+ if (tv_mode->progressive)
+ tv_ctl |= TV_PROGRESSIVE;
+ if (tv_mode->trilevel_sync)
+ tv_ctl |= TV_TRILEVEL_SYNC;
+ if (tv_mode->pal_burst)
+ tv_ctl |= TV_PAL_BURST;
+ scctl1 = 0;
+ /* dda1 implies valid video levels */
+ if (tv_mode->dda1_inc) {
+ scctl1 |= TV_SC_DDA1_EN;
+ scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
+ }
+
+ if (tv_mode->dda2_inc)
+ scctl1 |= TV_SC_DDA2_EN;
+
+ if (tv_mode->dda3_inc)
+ scctl1 |= TV_SC_DDA3_EN;
+
+ scctl1 |= tv_mode->sc_reset;
+ scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
+
+ scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
+ tv_mode->dda2_inc << TV_SCDDA2_INC_SHIFT;
+
+ scctl3 = tv_mode->dda3_size << TV_SCDDA3_SIZE_SHIFT |
+ tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
+
+ /* Enable two fixes for the chips that need them. */
+ if (dev->pci_device < 0x2772)
+ tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
+
+ I915_WRITE(TV_H_CTL_1, hctl1);
+ I915_WRITE(TV_H_CTL_2, hctl2);
+ I915_WRITE(TV_H_CTL_3, hctl3);
+ I915_WRITE(TV_V_CTL_1, vctl1);
+ I915_WRITE(TV_V_CTL_2, vctl2);
+ I915_WRITE(TV_V_CTL_3, vctl3);
+ I915_WRITE(TV_V_CTL_4, vctl4);
+ I915_WRITE(TV_V_CTL_5, vctl5);
+ I915_WRITE(TV_V_CTL_6, vctl6);
+ I915_WRITE(TV_V_CTL_7, vctl7);
+ I915_WRITE(TV_SC_CTL_1, scctl1);
+ I915_WRITE(TV_SC_CTL_2, scctl2);
+ I915_WRITE(TV_SC_CTL_3, scctl3);
+
+ if (color_conversion) {
+ I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+ color_conversion->gy);
+ I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) |
+ color_conversion->ay);
+ I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+ color_conversion->gu);
+ I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+ color_conversion->au);
+ I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+ color_conversion->gv);
+ I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+ color_conversion->av);
+ }
+
+ I915_WRITE(TV_CLR_KNOBS, 0x00606000);
+ if (video_levels)
+ I915_WRITE(TV_CLR_LEVEL,
+ ((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
+ (video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
+ {
+ int pipeconf_reg = (intel_crtc->pipe == 0) ?
+ PIPEACONF : PIPEBCONF;
+ int dspcntr_reg = (intel_crtc->plane == 0) ?
+ DSPACNTR : DSPBCNTR;
+ int pipeconf = I915_READ(pipeconf_reg);
+ int dspcntr = I915_READ(dspcntr_reg);
+ int dspbase_reg = (intel_crtc->plane == 0) ?
+ DSPAADDR : DSPBADDR;
+ int xpos = 0x0, ypos = 0x0;
+ unsigned int xsize, ysize;
+ /* Pipe must be off here */
+ I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+
+ /* Wait for vblank for the disable to take effect */
+ if (!IS_I9XX(dev))
+ intel_wait_for_vblank(dev);
+
+ I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE);
+ /* Wait for vblank for the disable to take effect. */
+ intel_wait_for_vblank(dev);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
+ xsize = tv_mode->hblank_start - tv_mode->hblank_end;
+ if (tv_mode->progressive)
+ ysize = tv_mode->nbr_end + 1;
+ else
+ ysize = 2*tv_mode->nbr_end + 1;
+
+ xpos += tv_priv->margin[TV_MARGIN_LEFT];
+ ypos += tv_priv->margin[TV_MARGIN_TOP];
+ xsize -= (tv_priv->margin[TV_MARGIN_LEFT] +
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ ysize -= (tv_priv->margin[TV_MARGIN_TOP] +
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+ I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
+ I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
+
+ I915_WRITE(pipeconf_reg, pipeconf);
+ I915_WRITE(dspcntr_reg, dspcntr);
+ /* Flush the plane changes */
+ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+ }
+
+ j = 0;
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 60; i++)
+ I915_WRITE(TV_H_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_LUMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ for (i = 0; i < 43; i++)
+ I915_WRITE(TV_V_CHROMA_0 + (i<<2), tv_mode->filter_table[j++]);
+ I915_WRITE(TV_DAC, 0);
+ I915_WRITE(TV_CTL, tv_ctl);
+}
+
+static const struct drm_display_mode reported_modes[] = {
+ {
+ .name = "NTSC 480i",
+ .clock = 107520,
+ .hdisplay = 1280,
+ .hsync_start = 1368,
+ .hsync_end = 1496,
+ .htotal = 1712,
+
+ .vdisplay = 1024,
+ .vsync_start = 1027,
+ .vsync_end = 1034,
+ .vtotal = 1104,
+ .type = DRM_MODE_TYPE_DRIVER,
+ },
+};
+
+/**
+ * Detects TV presence by checking for load.
+ *
+ * Requires that the current pipe's DPLL is active.
+
+ * \return true if TV is connected.
+ * \return false if TV is disconnected.
+ */
+static int
+intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
+{
+ struct drm_encoder *encoder = &intel_output->enc;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long irqflags;
+ u32 tv_ctl, save_tv_ctl;
+ u32 tv_dac, save_tv_dac;
+ int type = DRM_MODE_CONNECTOR_Unknown;
+
+ tv_dac = I915_READ(TV_DAC);
+
+ /* Disable TV interrupts around load detect or we'll recurse */
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ i915_disable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE |
+ PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+
+ /*
+ * Detect TV by polling)
+ */
+ if (intel_output->load_detect_temp) {
+ /* TV not currently running, prod it with destructive detect */
+ save_tv_dac = tv_dac;
+ tv_ctl = I915_READ(TV_CTL);
+ save_tv_ctl = tv_ctl;
+ tv_ctl &= ~TV_ENC_ENABLE;
+ tv_ctl &= ~TV_TEST_MODE_MASK;
+ tv_ctl |= TV_TEST_MODE_MONITOR_DETECT;
+ tv_dac &= ~TVDAC_SENSE_MASK;
+ tv_dac |= (TVDAC_STATE_CHG_EN |
+ TVDAC_A_SENSE_CTL |
+ TVDAC_B_SENSE_CTL |
+ TVDAC_C_SENSE_CTL |
+ DAC_CTL_OVERRIDE |
+ DAC_A_0_7_V |
+ DAC_B_0_7_V |
+ DAC_C_0_7_V);
+ I915_WRITE(TV_CTL, tv_ctl);
+ I915_WRITE(TV_DAC, tv_dac);
+ intel_wait_for_vblank(dev);
+ tv_dac = I915_READ(TV_DAC);
+ I915_WRITE(TV_DAC, save_tv_dac);
+ I915_WRITE(TV_CTL, save_tv_ctl);
+ }
+ /*
+ * A B C
+ * 0 1 1 Composite
+ * 1 0 X svideo
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+ DRM_DEBUG("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+ DRM_DEBUG("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+ DRM_DEBUG("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+ DRM_DEBUG("No TV connection detected\n");
+ type = -1;
+ }
+
+ /* Restore interrupt config */
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ i915_enable_pipestat(dev_priv, 0, PIPE_HOTPLUG_INTERRUPT_ENABLE |
+ PIPE_HOTPLUG_TV_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+
+ return type;
+}
+
+/**
+ * Detect the TV connection.
+ *
+ * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure
+ * we have a pipe programmed in order to probe the TV.
+ */
+static enum drm_connector_status
+intel_tv_detect(struct drm_connector *connector)
+{
+ struct drm_crtc *crtc;
+ struct drm_display_mode mode;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ struct drm_encoder *encoder = &intel_output->enc;
+ int dpms_mode;
+ int type = tv_priv->type;
+
+ mode = reported_modes[0];
+ drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
+
+ if (encoder->crtc) {
+ type = intel_tv_detect_type(encoder->crtc, intel_output);
+ } else {
+ crtc = intel_get_load_detect_pipe(intel_output, &mode, &dpms_mode);
+ if (crtc) {
+ type = intel_tv_detect_type(crtc, intel_output);
+ intel_release_load_detect_pipe(intel_output, dpms_mode);
+ } else
+ type = -1;
+ }
+
+ if (type < 0)
+ return connector_status_disconnected;
+
+ return connector_status_connected;
+}
+
+static struct input_res {
+ char *name;
+ int w, h;
+} input_res_table[] =
+{
+ {"640x480", 640, 480},
+ {"800x600", 800, 600},
+ {"1024x768", 1024, 768},
+ {"1280x1024", 1280, 1024},
+ {"848x480", 848, 480},
+ {"1280x720", 1280, 720},
+ {"1920x1080", 1920, 1080},
+};
+
+/**
+ * Stub get_modes function.
+ *
+ * This should probably return a set of fixed modes, unless we can figure out
+ * how to probe modes off of TV connections.
+ */
+
+static int
+intel_tv_get_modes(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode_ptr;
+ struct intel_output *intel_output = to_intel_output(connector);
+ const struct tv_mode *tv_mode = intel_tv_mode_find(intel_output);
+ int j;
+
+ for (j = 0; j < sizeof(input_res_table) / sizeof(input_res_table[0]);
+ j++) {
+ struct input_res *input = &input_res_table[j];
+ unsigned int hactive_s = input->w;
+ unsigned int vactive_s = input->h;
+
+ if (tv_mode->max_srcw && input->w > tv_mode->max_srcw)
+ continue;
+
+ if (input->w > 1024 && (!tv_mode->progressive
+ && !tv_mode->component_only))
+ continue;
+
+ mode_ptr = drm_calloc(1, sizeof(struct drm_display_mode),
+ DRM_MEM_DRIVER);
+ strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN);
+
+ mode_ptr->hdisplay = hactive_s;
+ mode_ptr->hsync_start = hactive_s + 1;
+ mode_ptr->hsync_end = hactive_s + 64;
+ if (mode_ptr->hsync_end <= mode_ptr->hsync_start)
+ mode_ptr->hsync_end = mode_ptr->hsync_start + 1;
+ mode_ptr->htotal = hactive_s + 96;
+
+ mode_ptr->vdisplay = vactive_s;
+ mode_ptr->vsync_start = vactive_s + 1;
+ mode_ptr->vsync_end = vactive_s + 32;
+ if (mode_ptr->vsync_end <= mode_ptr->vsync_start)
+ mode_ptr->vsync_end = mode_ptr->vsync_start + 1;
+ mode_ptr->vtotal = vactive_s + 33;
+
+ mode_ptr->clock = (int) (tv_mode->refresh *
+ mode_ptr->vtotal *
+ mode_ptr->htotal / 1000) / 1000;
+
+ mode_ptr->type = DRM_MODE_TYPE_DRIVER;
+ drm_mode_probed_add(connector, mode_ptr);
+ }
+
+ return 0;
+}
+
+static void
+intel_tv_destroy (struct drm_connector *connector)
+{
+ struct intel_output *intel_output = to_intel_output(connector);
+
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ drm_free(intel_output, sizeof(struct intel_output) + sizeof(struct intel_tv_priv),
+ DRM_MEM_DRIVER);
+}
+
+
+static int
+intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = connector->dev;
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_tv_priv *tv_priv = intel_output->dev_priv;
+ int ret = 0;
+
+ ret = drm_connector_property_set_value(connector, property, val);
+ if (ret < 0)
+ goto out;
+
+ if (property == dev->mode_config.tv_left_margin_property)
+ tv_priv->margin[TV_MARGIN_LEFT] = val;
+ else if (property == dev->mode_config.tv_right_margin_property)
+ tv_priv->margin[TV_MARGIN_RIGHT] = val;
+ else if (property == dev->mode_config.tv_top_margin_property)
+ tv_priv->margin[TV_MARGIN_TOP] = val;
+ else if (property == dev->mode_config.tv_bottom_margin_property)
+ tv_priv->margin[TV_MARGIN_BOTTOM] = val;
+ else if (property == dev->mode_config.tv_mode_property) {
+ if (val >= NUM_TV_MODES) {
+ ret = -EINVAL;
+ goto out;
+ }
+ tv_priv->tv_format = tv_modes[val].name;
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ intel_tv_mode_set(&intel_output->enc, NULL, NULL);
+out:
+ return ret;
+}
+
+static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
+ .dpms = intel_tv_dpms,
+ .mode_fixup = intel_tv_mode_fixup,
+ .prepare = intel_encoder_prepare,
+ .mode_set = intel_tv_mode_set,
+ .commit = intel_encoder_commit,
+};
+
+static const struct drm_connector_funcs intel_tv_connector_funcs = {
+ .save = intel_tv_save,
+ .restore = intel_tv_restore,
+ .detect = intel_tv_detect,
+ .destroy = intel_tv_destroy,
+ .set_property = intel_tv_set_property,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
+ .mode_valid = intel_tv_mode_valid,
+ .get_modes = intel_tv_get_modes,
+ .best_encoder = intel_best_encoder,
+};
+
+static void intel_tv_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs intel_tv_enc_funcs = {
+ .destroy = intel_tv_enc_destroy,
+};
+
+
+void
+intel_tv_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_connector *connector;
+ struct intel_output *intel_output;
+ struct intel_tv_priv *tv_priv;
+ u32 tv_dac_on, tv_dac_off, save_tv_dac;
+ char **tv_format_names;
+ int i, initial_mode = 0;
+
+ if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+ return;
+
+ /* Even if we have an encoder we may not have a connector */
+ if (!dev_priv->int_tv_support)
+ return;
+
+ /*
+ * Sanity check the TV output by checking to see if the
+ * DAC register holds a value
+ */
+ save_tv_dac = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac | TVDAC_STATE_CHG_EN);
+ tv_dac_on = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN);
+ tv_dac_off = I915_READ(TV_DAC);
+
+ I915_WRITE(TV_DAC, save_tv_dac);
+
+ /*
+ * If the register does not hold the state change enable
+ * bit, (either as a 0 or a 1), assume it doesn't really
+ * exist
+ */
+ if ((tv_dac_on & TVDAC_STATE_CHG_EN) == 0 ||
+ (tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
+ return;
+
+ intel_output = drm_calloc(1, sizeof(struct intel_output) +
+ sizeof(struct intel_tv_priv), DRM_MEM_DRIVER);
+ if (!intel_output) {
+ return;
+ }
+ connector = &intel_output->base;
+
+ drm_connector_init(dev, connector, &intel_tv_connector_funcs,
+ DRM_MODE_CONNECTOR_SVIDEO);
+
+ drm_encoder_init(dev, &intel_output->enc, &intel_tv_enc_funcs,
+ DRM_MODE_ENCODER_TVDAC);
+
+ drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc);
+ tv_priv = (struct intel_tv_priv *)(intel_output + 1);
+ intel_output->type = INTEL_OUTPUT_TVOUT;
+ intel_output->enc.possible_crtcs = ((1 << 0) | (1 << 1));
+ intel_output->enc.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
+ intel_output->dev_priv = tv_priv;
+ tv_priv->type = DRM_MODE_CONNECTOR_Unknown;
+
+ /* BIOS margin values */
+ tv_priv->margin[TV_MARGIN_LEFT] = 54;
+ tv_priv->margin[TV_MARGIN_TOP] = 36;
+ tv_priv->margin[TV_MARGIN_RIGHT] = 46;
+ tv_priv->margin[TV_MARGIN_BOTTOM] = 37;
+
+ tv_priv->tv_format = kstrdup(tv_modes[initial_mode].name, GFP_KERNEL);
+
+ drm_encoder_helper_add(&intel_output->enc, &intel_tv_helper_funcs);
+ drm_connector_helper_add(connector, &intel_tv_connector_helper_funcs);
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ /* Create TV properties then attach current values */
+ tv_format_names = drm_alloc(sizeof(char *) * NUM_TV_MODES,
+ DRM_MEM_DRIVER);
+ if (!tv_format_names)
+ goto out;
+ for (i = 0; i < NUM_TV_MODES; i++)
+ tv_format_names[i] = tv_modes[i].name;
+ drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
+
+ drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
+ initial_mode);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_left_margin_property,
+ tv_priv->margin[TV_MARGIN_LEFT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_top_margin_property,
+ tv_priv->margin[TV_MARGIN_TOP]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_right_margin_property,
+ tv_priv->margin[TV_MARGIN_RIGHT]);
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_bottom_margin_property,
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+out:
+ drm_sysfs_connector_add(connector);
+}
diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
index 4b27d9abb7bc..cace3964feeb 100644
--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
+++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
@@ -860,12 +860,12 @@ static __inline__ void r300_pacify(drm_radeon_private_t *dev_priv)
* The actual age emit is done by r300_do_cp_cmdbuf, which is why you must
* be careful about how this function is called.
*/
-static void r300_discard_buffer(struct drm_device * dev, struct drm_buf * buf)
+static void r300_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf)
{
- drm_radeon_private_t *dev_priv = dev->dev_private;
drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
- buf_priv->age = ++dev_priv->sarea_priv->last_dispatch;
+ buf_priv->age = ++master_priv->sarea_priv->last_dispatch;
buf->pending = 1;
buf->used = 0;
}
@@ -1027,6 +1027,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
drm_radeon_kcmd_buffer_t *cmdbuf)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf = NULL;
int emit_dispatch_age = 0;
@@ -1134,7 +1135,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
}
emit_dispatch_age = 1;
- r300_discard_buffer(dev, buf);
+ r300_discard_buffer(dev, file_priv->master, buf);
break;
case R300_CMD_WAIT:
@@ -1189,7 +1190,7 @@ int r300_do_cp_cmdbuf(struct drm_device *dev,
/* Emit the vertex buffer age */
BEGIN_RING(2);
- RADEON_DISPATCH_AGE(dev_priv->sarea_priv->last_dispatch);
+ RADEON_DISPATCH_AGE(master_priv->sarea_priv->last_dispatch);
ADVANCE_RING();
}
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index dcebb4bee7aa..63212d7bbc28 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -31,6 +31,7 @@
#include "drmP.h"
#include "drm.h"
+#include "drm_sarea.h"
#include "radeon_drm.h"
#include "radeon_drv.h"
#include "r300_reg.h"
@@ -667,15 +668,14 @@ static void radeon_cp_init_ring_buffer(struct drm_device * dev,
RADEON_WRITE(RADEON_BUS_CNTL, tmp);
} /* PCIE cards appears to not need this */
- dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0;
- RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame);
+ dev_priv->scratch[0] = 0;
+ RADEON_WRITE(RADEON_LAST_FRAME_REG, 0);
- dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0;
- RADEON_WRITE(RADEON_LAST_DISPATCH_REG,
- dev_priv->sarea_priv->last_dispatch);
+ dev_priv->scratch[1] = 0;
+ RADEON_WRITE(RADEON_LAST_DISPATCH_REG, 0);
- dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0;
- RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear);
+ dev_priv->scratch[2] = 0;
+ RADEON_WRITE(RADEON_LAST_CLEAR_REG, 0);
radeon_do_wait_for_idle(dev_priv);
@@ -871,9 +871,11 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on)
}
}
-static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
+static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
+ struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
DRM_DEBUG("\n");
@@ -998,8 +1000,8 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
dev_priv->buffers_offset = init->buffers_offset;
dev_priv->gart_textures_offset = init->gart_textures_offset;
- dev_priv->sarea = drm_getsarea(dev);
- if (!dev_priv->sarea) {
+ master_priv->sarea = drm_getsarea(dev);
+ if (!master_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
radeon_do_cleanup_cp(dev);
return -EINVAL;
@@ -1035,10 +1037,6 @@ static int radeon_do_init_cp(struct drm_device * dev, drm_radeon_init_t * init)
}
}
- dev_priv->sarea_priv =
- (drm_radeon_sarea_t *) ((u8 *) dev_priv->sarea->handle +
- init->sarea_priv_offset);
-
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
drm_core_ioremap(dev_priv->cp_ring, dev);
@@ -1329,7 +1327,7 @@ int radeon_cp_init(struct drm_device *dev, void *data, struct drm_file *file_pri
case RADEON_INIT_CP:
case RADEON_INIT_R200_CP:
case RADEON_INIT_R300_CP:
- return radeon_do_init_cp(dev, init);
+ return radeon_do_init_cp(dev, init, file_priv);
case RADEON_CLEANUP_CP:
return radeon_do_cleanup_cp(dev);
}
@@ -1768,6 +1766,51 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
return ret;
}
+int radeon_master_create(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_radeon_master_private *master_priv;
+ unsigned long sareapage;
+ int ret;
+
+ master_priv = drm_calloc(1, sizeof(*master_priv), DRM_MEM_DRIVER);
+ if (!master_priv)
+ return -ENOMEM;
+
+ /* prebuild the SAREA */
+ sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE);
+ ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK|_DRM_DRIVER,
+ &master_priv->sarea);
+ if (ret) {
+ DRM_ERROR("SAREA setup failed\n");
+ return ret;
+ }
+ master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea);
+ master_priv->sarea_priv->pfCurrentPage = 0;
+
+ master->driver_priv = master_priv;
+ return 0;
+}
+
+void radeon_master_destroy(struct drm_device *dev, struct drm_master *master)
+{
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+
+ if (!master_priv)
+ return;
+
+ if (master_priv->sarea_priv &&
+ master_priv->sarea_priv->pfCurrentPage != 0)
+ radeon_cp_dispatch_flip(dev, master);
+
+ master_priv->sarea_priv = NULL;
+ if (master_priv->sarea)
+ drm_rmmap_locked(dev, master_priv->sarea);
+
+ drm_free(master_priv, sizeof(*master_priv), DRM_MEM_DRIVER);
+
+ master->driver_priv = NULL;
+}
+
/* Create mappings for registers and framebuffer so userland doesn't necessarily
* have to find them.
*/
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 71af746a4e47..fef207881f45 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -96,6 +96,8 @@ static struct drm_driver driver = {
.enable_vblank = radeon_enable_vblank,
.disable_vblank = radeon_disable_vblank,
.dri_library_name = dri_library_name,
+ .master_create = radeon_master_create,
+ .master_destroy = radeon_master_destroy,
.irq_preinstall = radeon_driver_irq_preinstall,
.irq_postinstall = radeon_driver_irq_postinstall,
.irq_uninstall = radeon_driver_irq_uninstall,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index 3bbb871b25d5..490bc7ceef60 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -226,9 +226,13 @@ struct radeon_virt_surface {
#define RADEON_FLUSH_EMITED (1 < 0)
#define RADEON_PURGE_EMITED (1 < 1)
+struct drm_radeon_master_private {
+ drm_local_map_t *sarea;
+ drm_radeon_sarea_t *sarea_priv;
+};
+
typedef struct drm_radeon_private {
drm_radeon_ring_buffer_t ring;
- drm_radeon_sarea_t *sarea_priv;
u32 fb_location;
u32 fb_size;
@@ -409,6 +413,9 @@ extern int radeon_driver_open(struct drm_device *dev,
extern long radeon_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
+extern int radeon_master_create(struct drm_device *dev, struct drm_master *master);
+extern void radeon_master_destroy(struct drm_device *dev, struct drm_master *master);
+extern void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master);
/* r300_cmdbuf.c */
extern void r300_init_reg_flags(struct drm_device *dev);
@@ -1335,8 +1342,9 @@ do { \
} while (0)
#define VB_AGE_TEST_WITH_RETURN( dev_priv ) \
-do { \
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv; \
+do { \
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv; \
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv; \
if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) { \
int __ret = radeon_do_cp_idle( dev_priv ); \
if ( __ret ) return __ret; \
diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c
index 99be11418ac2..8289e16419a8 100644
--- a/drivers/gpu/drm/radeon/radeon_irq.c
+++ b/drivers/gpu/drm/radeon/radeon_irq.c
@@ -44,7 +44,7 @@ void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state)
else
dev_priv->irq_enable_reg &= ~mask;
- if (!dev->irq_enabled)
+ if (dev->irq_enabled)
RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
}
@@ -57,7 +57,7 @@ static void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state)
else
dev_priv->r500_disp_irq_reg &= ~mask;
- if (!dev->irq_enabled)
+ if (dev->irq_enabled)
RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg);
}
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 5d7153fcc7b0..ef940a079dcb 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -742,13 +742,14 @@ static struct {
*/
static void radeon_clear_box(drm_radeon_private_t * dev_priv,
+ struct drm_radeon_master_private *master_priv,
int x, int y, int w, int h, int r, int g, int b)
{
u32 color;
RING_LOCALS;
- x += dev_priv->sarea_priv->boxes[0].x1;
- y += dev_priv->sarea_priv->boxes[0].y1;
+ x += master_priv->sarea_priv->boxes[0].x1;
+ y += master_priv->sarea_priv->boxes[0].y1;
switch (dev_priv->color_fmt) {
case RADEON_COLOR_FORMAT_RGB565:
@@ -776,7 +777,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv,
RADEON_GMC_SRC_DATATYPE_COLOR |
RADEON_ROP3_P | RADEON_GMC_CLR_CMP_CNTL_DIS);
- if (dev_priv->sarea_priv->pfCurrentPage == 1) {
+ if (master_priv->sarea_priv->pfCurrentPage == 1) {
OUT_RING(dev_priv->front_pitch_offset);
} else {
OUT_RING(dev_priv->back_pitch_offset);
@@ -790,7 +791,7 @@ static void radeon_clear_box(drm_radeon_private_t * dev_priv,
ADVANCE_RING();
}
-static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
+static void radeon_cp_performance_boxes(drm_radeon_private_t *dev_priv, struct drm_radeon_master_private *master_priv)
{
/* Collapse various things into a wait flag -- trying to
* guess if userspase slept -- better just to have them tell us.
@@ -807,12 +808,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
/* Purple box for page flipping
*/
if (dev_priv->stats.boxes & RADEON_BOX_FLIP)
- radeon_clear_box(dev_priv, 4, 4, 8, 8, 255, 0, 255);
+ radeon_clear_box(dev_priv, master_priv, 4, 4, 8, 8, 255, 0, 255);
/* Red box if we have to wait for idle at any point
*/
if (dev_priv->stats.boxes & RADEON_BOX_WAIT_IDLE)
- radeon_clear_box(dev_priv, 16, 4, 8, 8, 255, 0, 0);
+ radeon_clear_box(dev_priv, master_priv, 16, 4, 8, 8, 255, 0, 0);
/* Blue box: lost context?
*/
@@ -820,12 +821,12 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
/* Yellow box for texture swaps
*/
if (dev_priv->stats.boxes & RADEON_BOX_TEXTURE_LOAD)
- radeon_clear_box(dev_priv, 40, 4, 8, 8, 255, 255, 0);
+ radeon_clear_box(dev_priv, master_priv, 40, 4, 8, 8, 255, 255, 0);
/* Green box if hardware never idles (as far as we can tell)
*/
if (!(dev_priv->stats.boxes & RADEON_BOX_DMA_IDLE))
- radeon_clear_box(dev_priv, 64, 4, 8, 8, 0, 255, 0);
+ radeon_clear_box(dev_priv, master_priv, 64, 4, 8, 8, 0, 255, 0);
/* Draw bars indicating number of buffers allocated
* (not a great measure, easily confused)
@@ -834,7 +835,7 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
if (dev_priv->stats.requested_bufs > 100)
dev_priv->stats.requested_bufs = 100;
- radeon_clear_box(dev_priv, 4, 16,
+ radeon_clear_box(dev_priv, master_priv, 4, 16,
dev_priv->stats.requested_bufs, 4,
196, 128, 128);
}
@@ -848,11 +849,13 @@ static void radeon_cp_performance_boxes(drm_radeon_private_t * dev_priv)
*/
static void radeon_cp_dispatch_clear(struct drm_device * dev,
+ struct drm_master *master,
drm_radeon_clear_t * clear,
drm_radeon_clear_rect_t * depth_boxes)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
drm_radeon_depth_clear_t *depth_clear = &dev_priv->depth_clear;
int nbox = sarea_priv->nbox;
struct drm_clip_rect *pbox = sarea_priv->boxes;
@@ -864,7 +867,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
dev_priv->stats.clears++;
- if (dev_priv->sarea_priv->pfCurrentPage == 1) {
+ if (sarea_priv->pfCurrentPage == 1) {
unsigned int tmp = flags;
flags &= ~(RADEON_FRONT | RADEON_BACK);
@@ -890,7 +893,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
int x = pbox[i].x1;
@@ -967,7 +970,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
* we haven't touched any "normal" state - still need this?
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
if ((dev_priv->flags & RADEON_HAS_HIERZ)
&& (flags & RADEON_USE_HIERZ)) {
@@ -1214,7 +1217,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
@@ -1285,7 +1288,7 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
/* Make sure we restore the 3D state next time.
*/
- dev_priv->sarea_priv->ctx_owner = 0;
+ sarea_priv->ctx_owner = 0;
for (i = 0; i < nbox; i++) {
@@ -1328,20 +1331,21 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
* wait on this value before performing the clear ioctl. We
* need this because the card's so damned fast...
*/
- dev_priv->sarea_priv->last_clear++;
+ sarea_priv->last_clear++;
BEGIN_RING(4);
- RADEON_CLEAR_AGE(dev_priv->sarea_priv->last_clear);
+ RADEON_CLEAR_AGE(sarea_priv->last_clear);
RADEON_WAIT_UNTIL_IDLE();
ADVANCE_RING();
}
-static void radeon_cp_dispatch_swap(struct drm_device * dev)
+static void radeon_cp_dispatch_swap(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int nbox = sarea_priv->nbox;
struct drm_clip_rect *pbox = sarea_priv->boxes;
int i;
@@ -1351,7 +1355,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
/* Do some trivial performance monitoring...
*/
if (dev_priv->do_boxes)
- radeon_cp_performance_boxes(dev_priv);
+ radeon_cp_performance_boxes(dev_priv, master_priv);
/* Wait for the 3D stream to idle before dispatching the bitblt.
* This will prevent data corruption between the two streams.
@@ -1385,7 +1389,7 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
/* Make this work even if front & back are flipped:
*/
OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1));
- if (dev_priv->sarea_priv->pfCurrentPage == 0) {
+ if (sarea_priv->pfCurrentPage == 0) {
OUT_RING(dev_priv->back_pitch_offset);
OUT_RING(dev_priv->front_pitch_offset);
} else {
@@ -1405,31 +1409,32 @@ static void radeon_cp_dispatch_swap(struct drm_device * dev)
* throttle the framerate by waiting for this value before
* performing the swapbuffer ioctl.
*/
- dev_priv->sarea_priv->last_frame++;
+ sarea_priv->last_frame++;
BEGIN_RING(4);
- RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame);
+ RADEON_FRAME_AGE(sarea_priv->last_frame);
RADEON_WAIT_UNTIL_2D_IDLE();
ADVANCE_RING();
}
-static void radeon_cp_dispatch_flip(struct drm_device * dev)
+void radeon_cp_dispatch_flip(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- struct drm_sarea *sarea = (struct drm_sarea *) dev_priv->sarea->handle;
- int offset = (dev_priv->sarea_priv->pfCurrentPage == 1)
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ struct drm_sarea *sarea = (struct drm_sarea *)master_priv->sarea->handle;
+ int offset = (master_priv->sarea_priv->pfCurrentPage == 1)
? dev_priv->front_offset : dev_priv->back_offset;
RING_LOCALS;
DRM_DEBUG("pfCurrentPage=%d\n",
- dev_priv->sarea_priv->pfCurrentPage);
+ master_priv->sarea_priv->pfCurrentPage);
/* Do some trivial performance monitoring...
*/
if (dev_priv->do_boxes) {
dev_priv->stats.boxes |= RADEON_BOX_FLIP;
- radeon_cp_performance_boxes(dev_priv);
+ radeon_cp_performance_boxes(dev_priv, master_priv);
}
/* Update the frame offsets for both CRTCs
@@ -1441,7 +1446,7 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev)
((sarea->frame.y * dev_priv->front_pitch +
sarea->frame.x * (dev_priv->color_fmt - 2)) & ~7)
+ offset);
- OUT_RING_REG(RADEON_CRTC2_OFFSET, dev_priv->sarea_priv->crtc2_base
+ OUT_RING_REG(RADEON_CRTC2_OFFSET, master_priv->sarea_priv->crtc2_base
+ offset);
ADVANCE_RING();
@@ -1450,13 +1455,13 @@ static void radeon_cp_dispatch_flip(struct drm_device * dev)
* throttle the framerate by waiting for this value before
* performing the swapbuffer ioctl.
*/
- dev_priv->sarea_priv->last_frame++;
- dev_priv->sarea_priv->pfCurrentPage =
- 1 - dev_priv->sarea_priv->pfCurrentPage;
+ master_priv->sarea_priv->last_frame++;
+ master_priv->sarea_priv->pfCurrentPage =
+ 1 - master_priv->sarea_priv->pfCurrentPage;
BEGIN_RING(2);
- RADEON_FRAME_AGE(dev_priv->sarea_priv->last_frame);
+ RADEON_FRAME_AGE(master_priv->sarea_priv->last_frame);
ADVANCE_RING();
}
@@ -1494,11 +1499,13 @@ typedef struct {
} drm_radeon_tcl_prim_t;
static void radeon_cp_dispatch_vertex(struct drm_device * dev,
+ struct drm_file *file_priv,
struct drm_buf * buf,
drm_radeon_tcl_prim_t * prim)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int offset = dev_priv->gart_buffers_offset + buf->offset + prim->start;
int numverts = (int)prim->numverts;
int nbox = sarea_priv->nbox;
@@ -1539,13 +1546,14 @@ static void radeon_cp_dispatch_vertex(struct drm_device * dev,
} while (i < nbox);
}
-static void radeon_cp_discard_buffer(struct drm_device * dev, struct drm_buf * buf)
+static void radeon_cp_discard_buffer(struct drm_device *dev, struct drm_master *master, struct drm_buf *buf)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
RING_LOCALS;
- buf_priv->age = ++dev_priv->sarea_priv->last_dispatch;
+ buf_priv->age = ++master_priv->sarea_priv->last_dispatch;
/* Emit the vertex buffer age */
BEGIN_RING(2);
@@ -1590,12 +1598,14 @@ static void radeon_cp_dispatch_indirect(struct drm_device * dev,
}
}
-static void radeon_cp_dispatch_indices(struct drm_device * dev,
+static void radeon_cp_dispatch_indices(struct drm_device *dev,
+ struct drm_master *master,
struct drm_buf * elt_buf,
drm_radeon_tcl_prim_t * prim)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
int offset = dev_priv->gart_buffers_offset + prim->offset;
u32 *data;
int dwords;
@@ -1870,7 +1880,7 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev,
ADVANCE_RING();
COMMIT_RING();
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
/* Update the input parameters for next time */
image->y += height;
@@ -2110,7 +2120,8 @@ static int radeon_surface_free(struct drm_device *dev, void *data, struct drm_fi
static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
drm_radeon_clear_t *clear = data;
drm_radeon_clear_rect_t depth_boxes[RADEON_NR_SAREA_CLIPRECTS];
DRM_DEBUG("\n");
@@ -2126,7 +2137,7 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *
sarea_priv->nbox * sizeof(depth_boxes[0])))
return -EFAULT;
- radeon_cp_dispatch_clear(dev, clear, depth_boxes);
+ radeon_cp_dispatch_clear(dev, file_priv->master, clear, depth_boxes);
COMMIT_RING();
return 0;
@@ -2134,9 +2145,10 @@ static int radeon_cp_clear(struct drm_device *dev, void *data, struct drm_file *
/* Not sure why this isn't set all the time:
*/
-static int radeon_do_init_pageflip(struct drm_device * dev)
+static int radeon_do_init_pageflip(struct drm_device *dev, struct drm_master *master)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = master->driver_priv;
RING_LOCALS;
DRM_DEBUG("\n");
@@ -2153,8 +2165,8 @@ static int radeon_do_init_pageflip(struct drm_device * dev)
dev_priv->page_flipping = 1;
- if (dev_priv->sarea_priv->pfCurrentPage != 1)
- dev_priv->sarea_priv->pfCurrentPage = 0;
+ if (master_priv->sarea_priv->pfCurrentPage != 1)
+ master_priv->sarea_priv->pfCurrentPage = 0;
return 0;
}
@@ -2172,9 +2184,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f
RING_SPACE_TEST_WITH_RETURN(dev_priv);
if (!dev_priv->page_flipping)
- radeon_do_init_pageflip(dev);
+ radeon_do_init_pageflip(dev, file_priv->master);
- radeon_cp_dispatch_flip(dev);
+ radeon_cp_dispatch_flip(dev, file_priv->master);
COMMIT_RING();
return 0;
@@ -2183,7 +2195,9 @@ static int radeon_cp_flip(struct drm_device *dev, void *data, struct drm_file *f
static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("\n");
LOCK_TEST_WITH_RETURN(dev, file_priv);
@@ -2193,8 +2207,8 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f
if (sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS)
sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
- radeon_cp_dispatch_swap(dev);
- dev_priv->sarea_priv->ctx_owner = 0;
+ radeon_cp_dispatch_swap(dev, file_priv->master);
+ sarea_priv->ctx_owner = 0;
COMMIT_RING();
return 0;
@@ -2203,7 +2217,8 @@ static int radeon_cp_swap(struct drm_device *dev, void *data, struct drm_file *f
static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_vertex_t *vertex = data;
@@ -2211,6 +2226,8 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d count=%d discard=%d\n",
DRM_CURRENTPID, vertex->idx, vertex->count, vertex->discard);
@@ -2263,13 +2280,13 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
prim.finish = vertex->count; /* unused */
prim.prim = vertex->prim;
prim.numverts = vertex->count;
- prim.vc_format = dev_priv->sarea_priv->vc_format;
+ prim.vc_format = sarea_priv->vc_format;
- radeon_cp_dispatch_vertex(dev, buf, &prim);
+ radeon_cp_dispatch_vertex(dev, file_priv, buf, &prim);
}
if (vertex->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2279,7 +2296,8 @@ static int radeon_cp_vertex(struct drm_device *dev, void *data, struct drm_file
static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_indices_t *elts = data;
@@ -2288,6 +2306,8 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d start=%d end=%d discard=%d\n",
DRM_CURRENTPID, elts->idx, elts->start, elts->end,
elts->discard);
@@ -2353,11 +2373,11 @@ static int radeon_cp_indices(struct drm_device *dev, void *data, struct drm_file
prim.prim = elts->prim;
prim.offset = 0; /* offset from start of dma buffers */
prim.numverts = RADEON_MAX_VB_VERTS; /* duh */
- prim.vc_format = dev_priv->sarea_priv->vc_format;
+ prim.vc_format = sarea_priv->vc_format;
- radeon_cp_dispatch_indices(dev, buf, &prim);
+ radeon_cp_dispatch_indices(dev, file_priv->master, buf, &prim);
if (elts->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2468,7 +2488,7 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil
*/
radeon_cp_dispatch_indirect(dev, buf, indirect->start, indirect->end);
if (indirect->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2478,7 +2498,8 @@ static int radeon_cp_indirect(struct drm_device *dev, void *data, struct drm_fil
static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
+ drm_radeon_sarea_t *sarea_priv;
struct drm_device_dma *dma = dev->dma;
struct drm_buf *buf;
drm_radeon_vertex2_t *vertex = data;
@@ -2487,6 +2508,8 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
LOCK_TEST_WITH_RETURN(dev, file_priv);
+ sarea_priv = master_priv->sarea_priv;
+
DRM_DEBUG("pid=%d index=%d discard=%d\n",
DRM_CURRENTPID, vertex->idx, vertex->discard);
@@ -2547,12 +2570,12 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
tclprim.offset = prim.numverts * 64;
tclprim.numverts = RADEON_MAX_VB_VERTS; /* duh */
- radeon_cp_dispatch_indices(dev, buf, &tclprim);
+ radeon_cp_dispatch_indices(dev, file_priv->master, buf, &tclprim);
} else {
tclprim.numverts = prim.numverts;
tclprim.offset = 0; /* not used */
- radeon_cp_dispatch_vertex(dev, buf, &tclprim);
+ radeon_cp_dispatch_vertex(dev, file_priv, buf, &tclprim);
}
if (sarea_priv->nbox == 1)
@@ -2560,7 +2583,7 @@ static int radeon_cp_vertex2(struct drm_device *dev, void *data, struct drm_file
}
if (vertex->discard) {
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
}
COMMIT_RING();
@@ -2909,7 +2932,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file
goto err;
}
- radeon_cp_discard_buffer(dev, buf);
+ radeon_cp_discard_buffer(dev, file_priv->master, buf);
break;
case RADEON_CMD_PACKET3:
@@ -3020,7 +3043,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
*/
case RADEON_PARAM_SAREA_HANDLE:
/* The lock is the first dword in the sarea. */
- value = (long)dev->lock.hw_lock;
+ /* no users of this parameter */
break;
#endif
case RADEON_PARAM_GART_TEX_HANDLE:
@@ -3064,6 +3087,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
+ struct drm_radeon_master_private *master_priv = file_priv->master->driver_priv;
drm_radeon_setparam_t *sp = data;
struct drm_radeon_driver_file_fields *radeon_priv;
@@ -3078,12 +3102,14 @@ static int radeon_cp_setparam(struct drm_device *dev, void *data, struct drm_fil
DRM_DEBUG("color tiling disabled\n");
dev_priv->front_pitch_offset &= ~RADEON_DST_TILE_MACRO;
dev_priv->back_pitch_offset &= ~RADEON_DST_TILE_MACRO;
- dev_priv->sarea_priv->tiling_enabled = 0;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->tiling_enabled = 0;
} else if (sp->value == 1) {
DRM_DEBUG("color tiling enabled\n");
dev_priv->front_pitch_offset |= RADEON_DST_TILE_MACRO;
dev_priv->back_pitch_offset |= RADEON_DST_TILE_MACRO;
- dev_priv->sarea_priv->tiling_enabled = 1;
+ if (master_priv->sarea_priv)
+ master_priv->sarea_priv->tiling_enabled = 1;
}
break;
case RADEON_SETPARAM_PCIGART_LOCATION:
@@ -3129,14 +3155,6 @@ void radeon_driver_preclose(struct drm_device *dev, struct drm_file *file_priv)
void radeon_driver_lastclose(struct drm_device *dev)
{
- if (dev->dev_private) {
- drm_radeon_private_t *dev_priv = dev->dev_private;
-
- if (dev_priv->sarea_priv &&
- dev_priv->sarea_priv->pfCurrentPage != 0)
- radeon_cp_dispatch_flip(dev);
- }
-
radeon_do_release(dev);
}
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index 906f9b9d715d..587f5b2380d4 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -1016,7 +1016,7 @@ static int i2c_pxa_probe(struct platform_device *dev)
snprintf(i2c->adap.name, sizeof(i2c->adap.name), "pxa_i2c-i2c.%u",
i2c->adap.nr);
- i2c->clk = clk_get(&dev->dev, "I2CCLK");
+ i2c->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(i2c->clk)) {
ret = PTR_ERR(i2c->clk);
goto eclk;
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index b7434d24904e..c39079f9c73f 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -40,8 +40,8 @@
#include <asm/io.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c/regs-iic.h>
-#include <asm/plat-s3c/iic.h>
+#include <plat/regs-iic.h>
+#include <plat/iic.h>
/* i2c controller state */
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 3384a717fec0..6c3d60b939bf 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -160,9 +160,39 @@ struct sh_mobile_i2c_data {
static void activate_ch(struct sh_mobile_i2c_data *pd)
{
+ unsigned long i2c_clk;
+ u_int32_t num;
+ u_int32_t denom;
+ u_int32_t tmp;
+
/* Make sure the clock is enabled */
clk_enable(pd->clk);
+ /* Get clock rate after clock is enabled */
+ i2c_clk = clk_get_rate(pd->clk);
+
+ /* Calculate the value for iccl. From the data sheet:
+ * iccl = (p clock / transfer rate) * (L / (L + H))
+ * where L and H are the SCL low/high ratio (5/4 in this case).
+ * We also round off the result.
+ */
+ num = i2c_clk * 5;
+ denom = NORMAL_SPEED * 9;
+ tmp = num * 10 / denom;
+ if (tmp % 10 >= 5)
+ pd->iccl = (u_int8_t)((num/denom) + 1);
+ else
+ pd->iccl = (u_int8_t)(num/denom);
+
+ /* Calculate the value for icch. From the data sheet:
+ icch = (p clock / transfer rate) * (H / (L + H)) */
+ num = i2c_clk * 4;
+ tmp = num * 10 / denom;
+ if (tmp % 10 >= 5)
+ pd->icch = (u_int8_t)((num/denom) + 1);
+ else
+ pd->icch = (u_int8_t)(num/denom);
+
/* Enable channel and configure rx ack */
iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
@@ -459,40 +489,6 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = {
.master_xfer = sh_mobile_i2c_xfer,
};
-static void sh_mobile_i2c_setup_channel(struct platform_device *dev)
-{
- struct sh_mobile_i2c_data *pd = platform_get_drvdata(dev);
- unsigned long peripheral_clk = clk_get_rate(pd->clk);
- u_int32_t num;
- u_int32_t denom;
- u_int32_t tmp;
-
- spin_lock_init(&pd->lock);
- init_waitqueue_head(&pd->wait);
-
- /* Calculate the value for iccl. From the data sheet:
- * iccl = (p clock / transfer rate) * (L / (L + H))
- * where L and H are the SCL low/high ratio (5/4 in this case).
- * We also round off the result.
- */
- num = peripheral_clk * 5;
- denom = NORMAL_SPEED * 9;
- tmp = num * 10 / denom;
- if (tmp % 10 >= 5)
- pd->iccl = (u_int8_t)((num/denom) + 1);
- else
- pd->iccl = (u_int8_t)(num/denom);
-
- /* Calculate the value for icch. From the data sheet:
- icch = (p clock / transfer rate) * (H / (L + H)) */
- num = peripheral_clk * 4;
- tmp = num * 10 / denom;
- if (tmp % 10 >= 5)
- pd->icch = (u_int8_t)((num/denom) + 1);
- else
- pd->icch = (u_int8_t)(num/denom);
-}
-
static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
{
struct resource *res;
@@ -533,6 +529,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
struct sh_mobile_i2c_data *pd;
struct i2c_adapter *adap;
struct resource *res;
+ char clk_name[8];
int size;
int ret;
@@ -542,9 +539,10 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
return -ENOMEM;
}
- pd->clk = clk_get(&dev->dev, "peripheral_clk");
+ snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id);
+ pd->clk = clk_get(&dev->dev, clk_name);
if (IS_ERR(pd->clk)) {
- dev_err(&dev->dev, "cannot get peripheral clock\n");
+ dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name);
ret = PTR_ERR(pd->clk);
goto err;
}
@@ -586,7 +584,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
strlcpy(adap->name, dev->name, sizeof(adap->name));
- sh_mobile_i2c_setup_channel(dev);
+ spin_lock_init(&pd->lock);
+ init_waitqueue_head(&pd->wait);
ret = i2c_add_numbered_adapter(adap);
if (ret < 0) {
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index e6857e01d1ba..c9f21e3d4ead 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -62,6 +62,9 @@ config IDE_TIMINGS
config IDE_ATAPI
bool
+config IDE_LEGACY
+ bool
+
config BLK_DEV_IDE_SATA
bool "Support for SATA (deprecated; conflicts with libata SATA driver)"
default n
@@ -724,7 +727,7 @@ config BLK_DEV_IDE_TX4939
config IDE_ARM
tristate "ARM IDE support"
- depends on ARM && (ARCH_CLPS7500 || ARCH_RPC || ARCH_SHARK)
+ depends on ARM && (ARCH_RPC || ARCH_SHARK)
default y
config BLK_DEV_IDE_ICSIDE
@@ -856,6 +859,7 @@ config BLK_DEV_4DRIVES
config BLK_DEV_ALI14XX
tristate "ALI M14xx support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "ali14xx.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -866,6 +870,7 @@ config BLK_DEV_ALI14XX
config BLK_DEV_DTC2278
tristate "DTC-2278 support"
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "dtc2278.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -876,6 +881,7 @@ config BLK_DEV_DTC2278
config BLK_DEV_HT6560B
tristate "Holtek HT6560B support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "ht6560b.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -886,6 +892,7 @@ config BLK_DEV_HT6560B
config BLK_DEV_QD65XX
tristate "QDI QD65xx support"
select IDE_TIMINGS
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "qd65xx.probe" kernel
boot parameter. It permits faster I/O speeds to be set. See the
@@ -894,6 +901,7 @@ config BLK_DEV_QD65XX
config BLK_DEV_UMC8672
tristate "UMC-8672 support"
+ select IDE_LEGACY
help
This driver is enabled at runtime using the "umc8672.probe" kernel
boot parameter. It enables support for the secondary IDE interface
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 7818d402b188..177e3f8523ed 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -5,7 +5,7 @@
EXTRA_CFLAGS += -Idrivers/ide
ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
- ide-taskfile.o ide-park.o ide-pio-blacklist.o
+ ide-taskfile.o ide-pm.o ide-park.o ide-pio-blacklist.o
# core IDE code
ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
@@ -15,6 +15,7 @@ ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o
ide-core-$(CONFIG_BLK_DEV_IDEDMA_SFF) += ide-dma-sff.o
ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o
ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o
+ide-core-$(CONFIG_IDE_LEGACY) += ide-legacy.o
obj-$(CONFIG_IDE) += ide-core.o
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 935385c77e06..3623bf013bcf 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -424,10 +424,10 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
.name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
- .chipset = ide_cmd646,
.port_ops = &cmd64x_port_ops,
.dma_ops = &cmd648_dma_ops,
- .host_flags = IDE_HFLAG_ABUSE_PREFETCH,
+ .host_flags = IDE_HFLAG_SERIALIZE |
+ IDE_HFLAG_ABUSE_PREFETCH,
.pio_mask = ATA_PIO5,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
diff --git a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c
index 53f079cc00af..d8ede85fe17f 100644
--- a/drivers/ide/cs5530.c
+++ b/drivers/ide/cs5530.c
@@ -81,11 +81,12 @@ static u8 cs5530_udma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
ide_drive_t *mate = ide_get_pair_dev(drive);
- u16 *mateid = mate->id;
+ u16 *mateid;
u8 mask = hwif->ultra_mask;
if (mate == NULL)
goto out;
+ mateid = mate->id;
if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
index 5297f07d2933..d37baf8ecc5f 100644
--- a/drivers/ide/cy82c693.c
+++ b/drivers/ide/cy82c693.c
@@ -292,7 +292,6 @@ static const struct ide_port_info cy82c693_chipset __devinitdata = {
.name = DRV_NAME,
.init_iops = init_iops_cy82c693,
.port_ops = &cy82c693_port_ops,
- .chipset = ide_cy82c693,
.host_flags = IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.swdma_mask = ATA_SWDMA2,
diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c
index 691506886561..59bd0be9dcb3 100644
--- a/drivers/ide/gayle.c
+++ b/drivers/ide/gayle.c
@@ -117,6 +117,10 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
hw->chipset = ide_generic;
}
+static const struct ide_port_info gayle_port_info = {
+ .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA,
+};
+
/*
* Probe for a Gayle IDE interface (and optionally for an IDE doubler)
*/
@@ -178,7 +182,7 @@ found:
hws[i] = &hw[i];
}
- rc = ide_host_add(NULL, hws, NULL);
+ rc = ide_host_add(&gayle_port_info, hws, NULL);
if (rc)
release_mem_region(res_start, res_n);
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index f5afd46ed51c..b18e10d99d2e 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -135,7 +135,6 @@
/* various tuning parameters */
#define HPT_RESET_STATE_ENGINE
#undef HPT_DELAY_INTERRUPT
-#define HPT_SERIALIZE_IO 0
static const char *quirk_drives[] = {
"QUANTUM FIREBALLlct08 08",
@@ -1288,7 +1287,6 @@ static u8 hpt3xx_cable_detect(ide_hwif_t *hwif)
static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
{
struct hpt_info *info = hpt3xx_get_info(hwif->dev);
- int serialize = HPT_SERIALIZE_IO;
u8 chip_type = info->chip_type;
/* Cache the channel's MISC. control registers' offset */
@@ -1305,13 +1303,9 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
* Clock is shared between the channels,
* so we'll have to serialize them... :-(
*/
- serialize = 1;
+ hwif->host->host_flags |= IDE_HFLAG_SERIALIZE;
hwif->rw_disk = &hpt3xxn_rw_disk;
}
-
- /* Serialize access to this device if needed */
- if (serialize && hwif->mate)
- hwif->serialized = hwif->mate->serialized = 1;
}
static int __devinit init_dma_hpt366(ide_hwif_t *hwif,
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index 244a8a052ce8..fd4a36433050 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -615,10 +615,10 @@ void ide_acpi_push_timing(ide_hwif_t *hwif)
in_params[0].buffer.length = sizeof(struct GTM_buffer);
in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm;
in_params[1].type = ACPI_TYPE_BUFFER;
- in_params[1].buffer.length = sizeof(ATA_ID_WORDS * 2);
+ in_params[1].buffer.length = ATA_ID_WORDS * 2;
in_params[1].buffer.pointer = (u8 *)&master->idbuff;
in_params[2].type = ACPI_TYPE_BUFFER;
- in_params[2].buffer.length = sizeof(ATA_ID_WORDS * 2);
+ in_params[2].buffer.length = ATA_ID_WORDS * 2;
in_params[2].buffer.pointer = (u8 *)&slave->idbuff;
/* Output buffer: _STM has no output */
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 42ab6d8715f2..5daa4dd1b018 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -262,7 +262,6 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate)
struct request *failed = (struct request *) rq->buffer;
struct cdrom_info *info = drive->driver_data;
void *sense = &info->sense_data;
- unsigned long flags;
if (failed) {
if (failed->sense) {
@@ -278,11 +277,9 @@ static void cdrom_end_request(ide_drive_t *drive, int uptodate)
failed->hard_nr_sectors))
BUG();
} else {
- spin_lock_irqsave(&ide_lock, flags);
- if (__blk_end_request(failed, -EIO,
- failed->data_len))
+ if (blk_end_request(failed, -EIO,
+ failed->data_len))
BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
}
} else
cdrom_analyze_sense_data(drive, NULL, sense);
@@ -317,7 +314,8 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 st)
static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
{
ide_hwif_t *hwif = drive->hwif;
- struct request *rq = hwif->hwgroup->rq;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
int stat, err, sense_key;
/* check for errors */
@@ -426,16 +424,17 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
if (time_after(jiffies, info->write_timeout))
do_end_request = 1;
else {
+ struct request_queue *q = drive->queue;
unsigned long flags;
/*
* take a breather relying on the unplug
* timer to kick us again
*/
- spin_lock_irqsave(&ide_lock, flags);
- blk_plug_device(drive->queue);
- spin_unlock_irqrestore(&ide_lock,
- flags);
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_plug_device(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
return 1;
}
}
@@ -504,12 +503,14 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
end_request:
if (stat & ATA_ERR) {
+ struct request_queue *q = drive->queue;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(q->queue_lock, flags);
blkdev_dequeue_request(rq);
- HWGROUP(drive)->rq = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ hwgroup->rq = NULL;
cdrom_queue_request_sense(drive, rq->sense, rq);
} else
@@ -773,52 +774,6 @@ static ide_startstop_t cdrom_start_rw_cont(ide_drive_t *drive)
return cdrom_transfer_packet_command(drive, rq, cdrom_newpc_intr);
}
-#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */
-#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */
-#define IDECD_SEEK_TIMEOUT (2 * WAIT_CMD) /* 20 sec */
-
-static ide_startstop_t cdrom_seek_intr(ide_drive_t *drive)
-{
- struct cdrom_info *info = drive->driver_data;
- int stat;
- static int retry = 10;
-
- ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
-
- if (cdrom_decode_status(drive, 0, &stat))
- return ide_stopped;
-
- drive->atapi_flags |= IDE_AFLAG_SEEKING;
-
- if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) {
- if (--retry == 0)
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
- }
- return ide_stopped;
-}
-
-static void ide_cd_prepare_seek_request(ide_drive_t *drive, struct request *rq)
-{
- sector_t frame = rq->sector;
-
- ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
-
- sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS);
-
- memset(rq->cmd, 0, BLK_MAX_CDB);
- rq->cmd[0] = GPCMD_SEEK;
- put_unaligned(cpu_to_be32(frame), (unsigned int *) &rq->cmd[2]);
-
- rq->timeout = ATAPI_WAIT_PC;
-}
-
-static ide_startstop_t cdrom_start_seek_continuation(ide_drive_t *drive)
-{
- struct request *rq = drive->hwif->hwgroup->rq;
-
- return cdrom_transfer_packet_command(drive, rq, &cdrom_seek_intr);
-}
-
/*
* Fix up a possibly partially-processed request so that we can start it over
* entirely, or even put it back on the request queue.
@@ -950,7 +905,8 @@ static int cdrom_newpc_intr_dummy_cb(struct request *rq)
static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- struct request *rq = HWGROUP(drive)->rq;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
xfer_func_t *xferfunc;
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, stat, thislen, uptodate = 0;
@@ -1148,17 +1104,15 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
end_request:
if (blk_pc_request(rq)) {
- unsigned long flags;
unsigned int dlen = rq->data_len;
if (dma)
rq->data_len = 0;
- spin_lock_irqsave(&ide_lock, flags);
- if (__blk_end_request(rq, 0, dlen))
+ if (blk_end_request(rq, 0, dlen))
BUG();
- HWGROUP(drive)->rq = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
+
+ hwgroup->rq = NULL;
} else {
if (!uptodate)
rq->cmd_flags |= REQ_FAILED;
@@ -1260,7 +1214,6 @@ static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
sector_t block)
{
- struct cdrom_info *info = drive->driver_data;
ide_handler_t *fn;
int xferlen;
@@ -1270,44 +1223,14 @@ static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq,
(unsigned long long)block);
if (blk_fs_request(rq)) {
- if (drive->atapi_flags & IDE_AFLAG_SEEKING) {
- ide_hwif_t *hwif = drive->hwif;
- unsigned long elapsed = jiffies - info->start_seek;
- int stat = hwif->tp_ops->read_status(hwif);
-
- if ((stat & ATA_DSC) != ATA_DSC) {
- if (elapsed < IDECD_SEEK_TIMEOUT) {
- ide_stall_queue(drive,
- IDECD_SEEK_TIMER);
- return ide_stopped;
- }
- printk(KERN_ERR PFX "%s: DSC timeout\n",
- drive->name);
- }
- drive->atapi_flags &= ~IDE_AFLAG_SEEKING;
- }
- if (rq_data_dir(rq) == READ &&
- IDE_LARGE_SEEK(info->last_block, block,
- IDECD_SEEK_THRESHOLD) &&
- (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)) {
- xferlen = 0;
- fn = cdrom_start_seek_continuation;
+ xferlen = 32768;
+ fn = cdrom_start_rw_cont;
- drive->dma = 0;
- info->start_seek = jiffies;
-
- ide_cd_prepare_seek_request(drive, rq);
- } else {
- xferlen = 32768;
- fn = cdrom_start_rw_cont;
-
- if (cdrom_start_rw(drive, rq) == ide_stopped)
- return ide_stopped;
+ if (cdrom_start_rw(drive, rq) == ide_stopped)
+ return ide_stopped;
- if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped)
- return ide_stopped;
- }
- info->last_block = block;
+ if (ide_cd_prepare_rw_request(drive, rq) == ide_stopped)
+ return ide_stopped;
} else if (blk_sense_request(rq) || blk_pc_request(rq) ||
rq->cmd_type == REQ_TYPE_ATA_PC) {
xferlen = rq->data_len;
@@ -1908,13 +1831,6 @@ static ide_proc_entry_t idecd_proc[] = {
{ NULL, 0, NULL, NULL }
};
-ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP);
-
-static const struct ide_proc_devset idecd_settings[] = {
- IDE_PROC_DEVSET(dsc_overlap, 0, 1),
- { 0 },
-};
-
static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive)
{
return idecd_proc;
@@ -1922,7 +1838,7 @@ static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive)
static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive)
{
- return idecd_settings;
+ return NULL;
}
#endif
@@ -2022,11 +1938,6 @@ static int ide_cdrom_setup(ide_drive_t *drive)
/* set correct block size */
blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE);
- if (drive->next != drive)
- drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
- else
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
-
if (ide_cdrom_register(drive, nslots)) {
printk(KERN_ERR PFX "%s: %s failed to register device with the"
" cdrom driver.\n", drive->name, __func__);
@@ -2063,7 +1974,6 @@ static void ide_cd_release(struct kref *kref)
kfree(info->toc);
if (devinfo->handle == drive)
unregister_cdrom(devinfo);
- drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
drive->driver_data = NULL;
blk_queue_prep_rq(drive->queue, NULL);
g->private_data = NULL;
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
index 5882b9a9ea8b..d5ce3362dbd1 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -88,8 +88,6 @@ struct cdrom_info {
struct request_sense sense_data;
struct request request_sense_request;
- unsigned long last_block;
- unsigned long start_seek;
u8 max_speed; /* Max speed of the drive. */
u8 current_speed; /* Current speed of the drive. */
diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c
index cac431f0df17..f6d2d44d8a9a 100644
--- a/drivers/ide/ide-dma-sff.c
+++ b/drivers/ide/ide-dma-sff.c
@@ -98,10 +98,10 @@ int ide_build_dmatable(ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = drive->hwif;
__le32 *table = (__le32 *)hwif->dmatable_cpu;
- unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
unsigned int count = 0;
int i;
struct scatterlist *sg;
+ u8 is_trm290 = !!(hwif->host_flags & IDE_HFLAG_TRM290);
hwif->sg_nents = ide_build_sglist(drive, rq);
if (hwif->sg_nents == 0)
@@ -176,15 +176,10 @@ int ide_dma_setup(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
struct request *rq = hwif->hwgroup->rq;
- unsigned int reading;
+ unsigned int reading = rq_data_dir(rq) ? 0 : ATA_DMA_WR;
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
u8 dma_stat;
- if (rq_data_dir(rq))
- reading = 0;
- else
- reading = 1 << 3;
-
/* fall back to pio! */
if (!ide_build_dmatable(drive, rq)) {
ide_map_sg(drive, rq);
@@ -209,10 +204,11 @@ int ide_dma_setup(ide_drive_t *drive)
/* clear INTR & ERROR flags */
if (mmio)
- writeb(dma_stat | 6,
+ writeb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
(void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+ outb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
+ hwif->dma_base + ATA_DMA_STATUS);
drive->waiting_for_dma = 1;
return 0;
@@ -246,14 +242,13 @@ static int dma_timer_expiry(ide_drive_t *drive)
hwif->hwgroup->expiry = NULL; /* one free ride for now */
- /* 1 dmaing, 2 error, 4 intr */
- if (dma_stat & 2) /* ERROR */
+ if (dma_stat & ATA_DMA_ERR) /* ERROR */
return -1;
- if (dma_stat & 1) /* DMAing */
+ if (dma_stat & ATA_DMA_ACTIVE) /* DMAing */
return WAIT_CMD;
- if (dma_stat & 4) /* Got an Interrupt */
+ if (dma_stat & ATA_DMA_INTR) /* Got an Interrupt */
return WAIT_CMD;
return 0; /* Status is unknown -- reset the bus */
@@ -279,12 +274,11 @@ void ide_dma_start(ide_drive_t *drive)
*/
if (hwif->host_flags & IDE_HFLAG_MMIO) {
dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* start DMA */
- writeb(dma_cmd | 1,
+ writeb(dma_cmd | ATA_DMA_START,
(void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
} else {
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd | ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD);
}
wmb();
@@ -296,19 +290,18 @@ int ide_dma_end(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
- u8 dma_stat = 0, dma_cmd = 0;
+ u8 dma_stat = 0, dma_cmd = 0, mask;
drive->waiting_for_dma = 0;
+ /* stop DMA */
if (mmio) {
- /* get DMA command mode */
dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* stop DMA */
- writeb(dma_cmd & ~1,
+ writeb(dma_cmd & ~ATA_DMA_START,
(void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
} else {
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd & ~ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD);
}
/* get DMA status */
@@ -316,16 +309,21 @@ int ide_dma_end(ide_drive_t *drive)
if (mmio)
/* clear the INTR & ERROR bits */
- writeb(dma_stat | 6,
+ writeb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
(void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+ outb(dma_stat | ATA_DMA_ERR | ATA_DMA_INTR,
+ hwif->dma_base + ATA_DMA_STATUS);
/* purge DMA mappings */
ide_destroy_dmatable(drive);
- /* verify good DMA status */
wmb();
- return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
+
+ /* verify good DMA status */
+ mask = ATA_DMA_ACTIVE | ATA_DMA_ERR | ATA_DMA_INTR;
+ if ((dma_stat & mask) != ATA_DMA_INTR)
+ return 0x10 | dma_stat;
+ return 0;
}
EXPORT_SYMBOL_GPL(ide_dma_end);
@@ -335,11 +333,7 @@ int ide_dma_test_irq(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
- /* return 1 if INTR asserted */
- if ((dma_stat & 4) == 4)
- return 1;
-
- return 0;
+ return (dma_stat & ATA_DMA_INTR) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(ide_dma_test_irq);
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index cc35d6dbd410..ecacc008fdaf 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -84,11 +84,11 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
ide_dma_on(drive);
}
- if (!__blk_end_request(rq, error, nr_bytes)) {
- if (dequeue)
- HWGROUP(drive)->rq = NULL;
+ if (!blk_end_request(rq, error, nr_bytes))
ret = 0;
- }
+
+ if (ret == 0 && dequeue)
+ drive->hwif->hwgroup->rq = NULL;
return ret;
}
@@ -107,16 +107,7 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
{
unsigned int nr_bytes = nr_sectors << 9;
- struct request *rq;
- unsigned long flags;
- int ret = 1;
-
- /*
- * room for locking improvements here, the calls below don't
- * need the queue lock held at all
- */
- spin_lock_irqsave(&ide_lock, flags);
- rq = HWGROUP(drive)->rq;
+ struct request *rq = drive->hwif->hwgroup->rq;
if (!nr_bytes) {
if (blk_pc_request(rq))
@@ -125,105 +116,10 @@ int ide_end_request (ide_drive_t *drive, int uptodate, int nr_sectors)
nr_bytes = rq->hard_cur_sectors << 9;
}
- ret = __ide_end_request(drive, rq, uptodate, nr_bytes, 1);
-
- spin_unlock_irqrestore(&ide_lock, flags);
- return ret;
+ return __ide_end_request(drive, rq, uptodate, nr_bytes, 1);
}
EXPORT_SYMBOL(ide_end_request);
-static void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
-
-#ifdef DEBUG_PM
- printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
- drive->name, pm->pm_step);
-#endif
- if (drive->media != ide_disk)
- return;
-
- switch (pm->pm_step) {
- case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
- if (pm->pm_state == PM_EVENT_FREEZE)
- pm->pm_step = IDE_PM_COMPLETED;
- else
- pm->pm_step = IDE_PM_STANDBY;
- break;
- case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
- pm->pm_step = IDE_PM_COMPLETED;
- break;
- case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
- pm->pm_step = IDE_PM_IDLE;
- break;
- case IDE_PM_IDLE: /* Resume step 2 (idle)*/
- pm->pm_step = IDE_PM_RESTORE_DMA;
- break;
- }
-}
-
-static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
- ide_task_t *args = rq->special;
-
- memset(args, 0, sizeof(*args));
-
- switch (pm->pm_step) {
- case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
- if (drive->media != ide_disk)
- break;
- /* Not supported? Switch to next step now. */
- if (ata_id_flush_enabled(drive->id) == 0 ||
- (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
- ide_complete_power_step(drive, rq);
- return ide_stopped;
- }
- if (ata_id_flush_ext_enabled(drive->id))
- args->tf.command = ATA_CMD_FLUSH_EXT;
- else
- args->tf.command = ATA_CMD_FLUSH;
- goto out_do_tf;
- case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
- args->tf.command = ATA_CMD_STANDBYNOW1;
- goto out_do_tf;
- case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
- ide_set_max_pio(drive);
- /*
- * skip IDE_PM_IDLE for ATAPI devices
- */
- if (drive->media != ide_disk)
- pm->pm_step = IDE_PM_RESTORE_DMA;
- else
- ide_complete_power_step(drive, rq);
- return ide_stopped;
- case IDE_PM_IDLE: /* Resume step 2 (idle) */
- args->tf.command = ATA_CMD_IDLEIMMEDIATE;
- goto out_do_tf;
- case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
- /*
- * Right now, all we do is call ide_set_dma(drive),
- * we could be smarter and check for current xfer_speed
- * in struct drive etc...
- */
- if (drive->hwif->dma_ops == NULL)
- break;
- /*
- * TODO: respect IDE_DFLAG_USING_DMA
- */
- ide_set_dma(drive);
- break;
- }
-
- pm->pm_step = IDE_PM_COMPLETED;
- return ide_stopped;
-
-out_do_tf:
- args->tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- args->data_phase = TASKFILE_NO_DATA;
- return do_rw_taskfile(drive, args);
-}
-
/**
* ide_end_dequeued_request - complete an IDE I/O
* @drive: IDE device for the I/O
@@ -242,48 +138,12 @@ out_do_tf:
int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
int uptodate, int nr_sectors)
{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&ide_lock, flags);
BUG_ON(!blk_rq_started(rq));
- ret = __ide_end_request(drive, rq, uptodate, nr_sectors << 9, 0);
- spin_unlock_irqrestore(&ide_lock, flags);
- return ret;
+ return __ide_end_request(drive, rq, uptodate, nr_sectors << 9, 0);
}
EXPORT_SYMBOL_GPL(ide_end_dequeued_request);
-
-/**
- * ide_complete_pm_request - end the current Power Management request
- * @drive: target drive
- * @rq: request
- *
- * This function cleans up the current PM request and stops the queue
- * if necessary.
- */
-static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
-{
- unsigned long flags;
-
-#ifdef DEBUG_PM
- printk("%s: completing PM request, %s\n", drive->name,
- blk_pm_suspend_request(rq) ? "suspend" : "resume");
-#endif
- spin_lock_irqsave(&ide_lock, flags);
- if (blk_pm_suspend_request(rq)) {
- blk_stop_queue(drive->queue);
- } else {
- drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
- blk_start_queue(drive->queue);
- }
- HWGROUP(drive)->rq = NULL;
- if (__blk_end_request(rq, 0, 0))
- BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
-}
-
/**
* ide_end_drive_cmd - end an explicit drive command
* @drive: command
@@ -300,19 +160,12 @@ static void ide_complete_pm_request (ide_drive_t *drive, struct request *rq)
void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
{
- unsigned long flags;
- struct request *rq;
-
- spin_lock_irqsave(&ide_lock, flags);
- rq = HWGROUP(drive)->rq;
- spin_unlock_irqrestore(&ide_lock, flags);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
+ struct request *rq = hwgroup->rq;
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *task = (ide_task_t *)rq->special;
- if (rq->errors == 0)
- rq->errors = !OK_STAT(stat, ATA_DRDY, BAD_STAT);
-
if (task) {
struct ide_taskfile *tf = &task->tf;
@@ -333,15 +186,14 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
return;
}
- spin_lock_irqsave(&ide_lock, flags);
- HWGROUP(drive)->rq = NULL;
+ hwgroup->rq = NULL;
+
rq->errors = err;
- if (unlikely(__blk_end_request(rq, (rq->errors ? -EIO : 0),
- blk_rq_bytes(rq))))
+
+ if (unlikely(blk_end_request(rq, (rq->errors ? -EIO : 0),
+ blk_rq_bytes(rq))))
BUG();
- spin_unlock_irqrestore(&ide_lock, flags);
}
-
EXPORT_SYMBOL(ide_end_drive_cmd);
static void ide_kill_rq(ide_drive_t *drive, struct request *rq)
@@ -720,40 +572,6 @@ static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
}
}
-static void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
-{
- struct request_pm_state *pm = rq->data;
-
- if (blk_pm_suspend_request(rq) &&
- pm->pm_step == IDE_PM_START_SUSPEND)
- /* Mark drive blocked when starting the suspend sequence. */
- drive->dev_flags |= IDE_DFLAG_BLOCKED;
- else if (blk_pm_resume_request(rq) &&
- pm->pm_step == IDE_PM_START_RESUME) {
- /*
- * The first thing we do on wakeup is to wait for BSY bit to
- * go away (with a looong timeout) as a drive on this hwif may
- * just be POSTing itself.
- * We do that before even selecting as the "other" device on
- * the bus may be broken enough to walk on our toes at this
- * point.
- */
- ide_hwif_t *hwif = drive->hwif;
- int rc;
-#ifdef DEBUG_PM
- printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name);
-#endif
- rc = ide_wait_not_busy(hwif, 35000);
- if (rc)
- printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
- SELECT_DRIVE(drive);
- hwif->tp_ops->set_irq(hwif, 1);
- rc = ide_wait_not_busy(hwif, 100000);
- if (rc)
- printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
- }
-}
-
/**
* start_request - start of I/O and command issuing for IDE
*
@@ -927,7 +745,7 @@ repeat:
/*
* Issue a new request to a drive from hwgroup
- * Caller must have already done spin_lock_irqsave(&ide_lock, ..);
+ * Caller must have already done spin_lock_irqsave(&hwgroup->lock, ..);
*
* A hwgroup is a serialized group of IDE interfaces. Usually there is
* exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
@@ -939,7 +757,7 @@ repeat:
* possibly along with many other devices. This is especially common in
* PCI-based systems with off-board IDE controller cards.
*
- * The IDE driver uses the single global ide_lock spinlock to protect
+ * The IDE driver uses a per-hwgroup spinlock to protect
* access to the request queues, and to protect the hwgroup->busy flag.
*
* The first thread into the driver for a particular hwgroup sets the
@@ -955,7 +773,7 @@ repeat:
* will start the next request from the queue. If no more work remains,
* the driver will clear the hwgroup->busy flag and exit.
*
- * The ide_lock (spinlock) is used to protect all access to the
+ * The per-hwgroup spinlock is used to protect all access to the
* hwgroup->busy flag, but is otherwise not needed for most processing in
* the driver. This makes the driver much more friendlier to shared IRQs
* than previous designs, while remaining 100% (?) SMP safe and capable.
@@ -968,7 +786,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
ide_startstop_t startstop;
int loops = 0;
- /* caller must own ide_lock */
+ /* caller must own hwgroup->lock */
BUG_ON(!irqs_disabled());
while (!hwgroup->busy) {
@@ -1023,12 +841,12 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
}
again:
hwif = HWIF(drive);
- if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) {
+ if (hwif != hwgroup->hwif) {
/*
* set nIEN for previous hwif, drives in the
* quirk_list may not like intr setups/cleanups
*/
- if (drive->quirk_list != 1)
+ if (drive->quirk_list == 0)
hwif->tp_ops->set_irq(hwif, 0);
}
hwgroup->hwif = hwif;
@@ -1036,11 +854,6 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
drive->service_start = jiffies;
- if (blk_queue_plugged(drive->queue)) {
- printk(KERN_ERR "ide: huh? queue was plugged!\n");
- break;
- }
-
/*
* we know that the queue isn't empty, but this can happen
* if the q->prep_rq_fn() decides to kill a request
@@ -1090,11 +903,11 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
*/
if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
disable_irq_nosync(hwif->irq);
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
local_irq_enable_in_hardirq();
/* allow other IRQs while we start this request */
startstop = start_request(drive, rq);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (masked_irq != IDE_NO_IRQ && hwif->irq != masked_irq)
enable_irq(hwif->irq);
if (startstop == ide_stopped)
@@ -1192,7 +1005,7 @@ void ide_timer_expiry (unsigned long data)
unsigned long flags;
unsigned long wait = -1;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
if (((handler = hwgroup->handler) == NULL) ||
(hwgroup->req_gen != hwgroup->req_gen_timer)) {
@@ -1225,7 +1038,7 @@ void ide_timer_expiry (unsigned long data)
hwgroup->timer.expires = jiffies + wait;
hwgroup->req_gen_timer = hwgroup->req_gen;
add_timer(&hwgroup->timer);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return;
}
}
@@ -1235,7 +1048,7 @@ void ide_timer_expiry (unsigned long data)
* the handler() function, which means we need to
* globally mask the specific IRQ:
*/
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
hwif = HWIF(drive);
/* disable_irq_nosync ?? */
disable_irq(hwif->irq);
@@ -1259,14 +1072,14 @@ void ide_timer_expiry (unsigned long data)
hwif->tp_ops->read_status(hwif));
}
drive->service_time = jiffies - drive->service_start;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
enable_irq(hwif->irq);
if (startstop == ide_stopped)
hwgroup->busy = 0;
}
}
ide_do_request(hwgroup, IDE_NO_IRQ);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
/**
@@ -1359,18 +1172,16 @@ irqreturn_t ide_intr (int irq, void *dev_id)
{
unsigned long flags;
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
- ide_hwif_t *hwif;
+ ide_hwif_t *hwif = hwgroup->hwif;
ide_drive_t *drive;
ide_handler_t *handler;
ide_startstop_t startstop;
+ irqreturn_t irq_ret = IRQ_NONE;
- spin_lock_irqsave(&ide_lock, flags);
- hwif = hwgroup->hwif;
+ spin_lock_irqsave(&hwgroup->lock, flags);
- if (!ide_ack_intr(hwif)) {
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
- }
+ if (!ide_ack_intr(hwif))
+ goto out;
if ((handler = hwgroup->handler) == NULL || hwgroup->polling) {
/*
@@ -1406,9 +1217,9 @@ irqreturn_t ide_intr (int irq, void *dev_id)
(void)hwif->tp_ops->read_status(hwif);
#endif /* CONFIG_BLK_DEV_IDEPCI */
}
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
+ goto out;
}
+
drive = hwgroup->drive;
if (!drive) {
/*
@@ -1417,10 +1228,10 @@ irqreturn_t ide_intr (int irq, void *dev_id)
*
* [Note - this can occur if the drive is hot unplugged]
*/
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_HANDLED;
+ goto out_handled;
}
- if (!drive_is_ready(drive)) {
+
+ if (!drive_is_ready(drive))
/*
* This happens regularly when we share a PCI IRQ with
* another device. Unfortunately, it can also happen
@@ -1428,9 +1239,8 @@ irqreturn_t ide_intr (int irq, void *dev_id)
* their status register is up to date. Hopefully we have
* enough advance overhead that the latter isn't a problem.
*/
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_NONE;
- }
+ goto out;
+
if (!hwgroup->busy) {
hwgroup->busy = 1; /* paranoia */
printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
@@ -1438,7 +1248,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
hwgroup->handler = NULL;
hwgroup->req_gen++;
del_timer(&hwgroup->timer);
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
if (hwif->port_ops && hwif->port_ops->clear_irq)
hwif->port_ops->clear_irq(drive);
@@ -1449,7 +1259,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
/* service this interrupt, may set handler for next interrupt */
startstop = handler(drive);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/*
* Note that handler() may have set things up for another
* interrupt to occur soon, but it cannot happen until
@@ -1467,8 +1277,11 @@ irqreturn_t ide_intr (int irq, void *dev_id)
"on exit\n", drive->name);
}
}
- spin_unlock_irqrestore(&ide_lock, flags);
- return IRQ_HANDLED;
+out_handled:
+ irq_ret = IRQ_HANDLED;
+out:
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
+ return irq_ret;
}
/**
@@ -1488,16 +1301,17 @@ irqreturn_t ide_intr (int irq, void *dev_id)
void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
+ struct request_queue *q = drive->queue;
unsigned long flags;
- ide_hwgroup_t *hwgroup = HWGROUP(drive);
- spin_lock_irqsave(&ide_lock, flags);
hwgroup->rq = NULL;
- __elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 0);
- blk_start_queueing(drive->queue);
- spin_unlock_irqrestore(&ide_lock, flags);
-}
+ spin_lock_irqsave(q->queue_lock, flags);
+ __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
+ blk_start_queueing(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+}
EXPORT_SYMBOL(ide_do_drive_cmd);
void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma)
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
index fcde16bb53a7..28232c64c346 100644
--- a/drivers/ide/ide-ioctls.c
+++ b/drivers/ide/ide-ioctls.c
@@ -19,7 +19,6 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
const struct ide_ioctl_devset *s)
{
const struct ide_devset *ds;
- unsigned long flags;
int err = -EOPNOTSUPP;
for (; (ds = s->setting); s++) {
@@ -33,9 +32,7 @@ int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
read_val:
mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
err = ds->get(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
mutex_unlock(&ide_setting_mtx);
return err >= 0 ? put_user(err, (long __user *)arg) : err;
@@ -98,7 +95,7 @@ static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg)
return -EPERM;
if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) &&
- (drive->media == ide_disk || drive->media == ide_floppy ||
+ (drive->media != ide_tape ||
(drive->dev_flags & IDE_DFLAG_SCSI)))
return -EPERM;
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index c41c3b9b6f02..ad8bd6539283 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -835,10 +835,12 @@ static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
unsigned int timeout, ide_expiry_t *expiry)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+
+ spin_lock_irqsave(&hwgroup->lock, flags);
__ide_set_handler(drive, handler, timeout, expiry);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL(ide_set_handler);
@@ -860,10 +862,11 @@ EXPORT_SYMBOL(ide_set_handler);
void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
unsigned timeout, ide_expiry_t *expiry)
{
+ ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
unsigned long flags;
- ide_hwif_t *hwif = HWIF(drive);
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
__ide_set_handler(drive, handler, timeout, expiry);
hwif->tp_ops->exec_command(hwif, cmd);
/*
@@ -873,19 +876,20 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
* FIXME: we could skip this delay with care on non shared devices
*/
ndelay(400);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL(ide_execute_command);
void ide_execute_pkt_cmd(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
unsigned long flags;
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
hwif->tp_ops->exec_command(hwif, ATA_CMD_PACKET);
ndelay(400);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
}
EXPORT_SYMBOL_GPL(ide_execute_pkt_cmd);
@@ -1076,22 +1080,16 @@ static void pre_reset(ide_drive_t *drive)
*/
static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
{
- unsigned int unit;
- unsigned long flags, timeout;
- ide_hwif_t *hwif;
- ide_hwgroup_t *hwgroup;
- struct ide_io_ports *io_ports;
- const struct ide_tp_ops *tp_ops;
+ ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ const struct ide_tp_ops *tp_ops = hwif->tp_ops;
const struct ide_port_ops *port_ops;
+ unsigned long flags, timeout;
+ unsigned int unit;
DEFINE_WAIT(wait);
- spin_lock_irqsave(&ide_lock, flags);
- hwif = HWIF(drive);
- hwgroup = HWGROUP(drive);
-
- io_ports = &hwif->io_ports;
-
- tp_ops = hwif->tp_ops;
+ spin_lock_irqsave(&hwgroup->lock, flags);
/* We must not reset with running handlers */
BUG_ON(hwgroup->handler != NULL);
@@ -1106,7 +1104,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
hwgroup->polling = 1;
__ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return ide_started;
}
@@ -1129,9 +1127,9 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
if (time_before_eq(timeout, now))
break;
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
timeout = schedule_timeout_uninterruptible(timeout - now);
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
} while (timeout);
finish_wait(&ide_park_wq, &wait);
@@ -1143,7 +1141,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
pre_reset(&hwif->drives[unit]);
if (io_ports->ctl_addr == 0) {
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
ide_complete_drive_reset(drive, -ENXIO);
return ide_stopped;
}
@@ -1179,7 +1177,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
if (port_ops && port_ops->resetproc)
port_ops->resetproc(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
return ide_started;
}
diff --git a/drivers/ide/ide-legacy.c b/drivers/ide/ide-legacy.c
new file mode 100644
index 000000000000..8c5dcbf22547
--- /dev/null
+++ b/drivers/ide/ide-legacy.c
@@ -0,0 +1,58 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+
+static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
+ u8 port_no, const struct ide_port_info *d,
+ unsigned long config)
+{
+ unsigned long base, ctl;
+ int irq;
+
+ if (port_no == 0) {
+ base = 0x1f0;
+ ctl = 0x3f6;
+ irq = 14;
+ } else {
+ base = 0x170;
+ ctl = 0x376;
+ irq = 15;
+ }
+
+ if (!request_region(base, 8, d->name)) {
+ printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
+ d->name, base, base + 7);
+ return;
+ }
+
+ if (!request_region(ctl, 1, d->name)) {
+ printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
+ d->name, ctl);
+ release_region(base, 8);
+ return;
+ }
+
+ ide_std_init_ports(hw, base, ctl);
+ hw->irq = irq;
+ hw->chipset = d->chipset;
+ hw->config = config;
+
+ hws[port_no] = hw;
+}
+
+int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
+{
+ hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
+
+ memset(&hw, 0, sizeof(hw));
+
+ if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0)
+ ide_legacy_init_one(hws, &hw[0], 0, d, config);
+ ide_legacy_init_one(hws, &hw[1], 1, d, config);
+
+ if (hws[0] == NULL && hws[1] == NULL &&
+ (d->host_flags & IDE_HFLAG_SINGLE))
+ return -ENOENT;
+
+ return ide_host_add(d, hws, NULL);
+}
+EXPORT_SYMBOL_GPL(ide_legacy_device_add);
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 9fc4cfb2a272..9f6e33d8a8b2 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -43,7 +43,6 @@ const char *ide_xfer_verbose(u8 mode)
return s;
}
-
EXPORT_SYMBOL(ide_xfer_verbose);
/**
@@ -87,7 +86,7 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
* This is used by most chipset support modules when "auto-tuning".
*/
-u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
+u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
{
u16 *id = drive->id;
int pio_mode = -1, overridden = 0;
@@ -131,7 +130,6 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
return pio_mode;
}
-
EXPORT_SYMBOL_GPL(ide_get_best_pio_mode);
/* req_pio == "255" for auto-tune */
@@ -162,7 +160,6 @@ void ide_set_pio(ide_drive_t *drive, u8 req_pio)
(void)ide_set_pio_mode(drive, XFER_PIO_0 + pio);
}
-
EXPORT_SYMBOL_GPL(ide_set_pio);
/**
@@ -173,7 +170,7 @@ EXPORT_SYMBOL_GPL(ide_set_pio);
* Enable or disable bounce buffering for the device. Drives move
* between PIO and DMA and that changes the rules we need.
*/
-
+
void ide_toggle_bounce(ide_drive_t *drive, int on)
{
u64 addr = BLK_BOUNCE_HIGH; /* dma64_addr_t */
@@ -243,14 +240,13 @@ int ide_set_dma_mode(ide_drive_t *drive, const u8 mode)
return ide_config_drive_speed(drive, mode);
}
}
-
EXPORT_SYMBOL_GPL(ide_set_dma_mode);
/**
* ide_set_xfer_rate - set transfer rate
* @drive: drive to set
* @rate: speed to attempt to set
- *
+ *
* General helper for setting the speed of an IDE device. This
* function knows about user enforced limits from the configuration
* which ->set_pio_mode/->set_dma_mode does not.
@@ -277,21 +273,16 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate)
static void ide_dump_opcode(ide_drive_t *drive)
{
- struct request *rq;
+ struct request *rq = drive->hwif->hwgroup->rq;
ide_task_t *task = NULL;
- spin_lock(&ide_lock);
- rq = NULL;
- if (HWGROUP(drive))
- rq = HWGROUP(drive)->rq;
- spin_unlock(&ide_lock);
if (!rq)
return;
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
task = rq->special;
- printk("ide: failed opcode was: ");
+ printk(KERN_ERR "ide: failed opcode was: ");
if (task == NULL)
printk(KERN_CONT "unknown\n");
else
@@ -329,44 +320,55 @@ static void ide_dump_sector(ide_drive_t *drive)
drive->hwif->tp_ops->tf_read(drive, &task);
if (lba48 || (tf->device & ATA_LBA))
- printk(", LBAsect=%llu",
+ printk(KERN_CONT ", LBAsect=%llu",
(unsigned long long)ide_get_lba_addr(tf, lba48));
else
- printk(", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
- tf->device & 0xf, tf->lbal);
+ printk(KERN_CONT ", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam,
+ tf->device & 0xf, tf->lbal);
}
static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
{
- printk("{ ");
- if (err & ATA_ABORTED) printk("DriveStatusError ");
+ printk(KERN_ERR "{ ");
+ if (err & ATA_ABORTED)
+ printk(KERN_CONT "DriveStatusError ");
if (err & ATA_ICRC)
- printk((err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
- if (err & ATA_UNC) printk("UncorrectableError ");
- if (err & ATA_IDNF) printk("SectorIdNotFound ");
- if (err & ATA_TRK0NF) printk("TrackZeroNotFound ");
- if (err & ATA_AMNF) printk("AddrMarkNotFound ");
- printk("}");
+ printk(KERN_CONT "%s",
+ (err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
+ if (err & ATA_UNC)
+ printk(KERN_CONT "UncorrectableError ");
+ if (err & ATA_IDNF)
+ printk(KERN_CONT "SectorIdNotFound ");
+ if (err & ATA_TRK0NF)
+ printk(KERN_CONT "TrackZeroNotFound ");
+ if (err & ATA_AMNF)
+ printk(KERN_CONT "AddrMarkNotFound ");
+ printk(KERN_CONT "}");
if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK ||
(err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) {
ide_dump_sector(drive);
if (HWGROUP(drive) && HWGROUP(drive)->rq)
- printk(", sector=%llu",
+ printk(KERN_CONT ", sector=%llu",
(unsigned long long)HWGROUP(drive)->rq->sector);
}
- printk("\n");
+ printk(KERN_CONT "\n");
}
static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
{
- printk("{ ");
- if (err & ATAPI_ILI) printk("IllegalLengthIndication ");
- if (err & ATAPI_EOM) printk("EndOfMedia ");
- if (err & ATA_ABORTED) printk("AbortedCommand ");
- if (err & ATA_MCR) printk("MediaChangeRequested ");
- if (err & ATAPI_LFS) printk("LastFailedSense=0x%02x ",
- (err & ATAPI_LFS) >> 4);
- printk("}\n");
+ printk(KERN_ERR "{ ");
+ if (err & ATAPI_ILI)
+ printk(KERN_CONT "IllegalLengthIndication ");
+ if (err & ATAPI_EOM)
+ printk(KERN_CONT "EndOfMedia ");
+ if (err & ATA_ABORTED)
+ printk(KERN_CONT "AbortedCommand ");
+ if (err & ATA_MCR)
+ printk(KERN_CONT "MediaChangeRequested ");
+ if (err & ATAPI_LFS)
+ printk(KERN_CONT "LastFailedSense=0x%02x ",
+ (err & ATAPI_LFS) >> 4);
+ printk(KERN_CONT "}\n");
}
/**
@@ -382,34 +384,37 @@ static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
u8 ide_dump_status(ide_drive_t *drive, const char *msg, u8 stat)
{
- unsigned long flags;
u8 err = 0;
- local_irq_save(flags);
- printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
+ printk(KERN_ERR "%s: %s: status=0x%02x { ", drive->name, msg, stat);
if (stat & ATA_BUSY)
- printk("Busy ");
+ printk(KERN_CONT "Busy ");
else {
- if (stat & ATA_DRDY) printk("DriveReady ");
- if (stat & ATA_DF) printk("DeviceFault ");
- if (stat & ATA_DSC) printk("SeekComplete ");
- if (stat & ATA_DRQ) printk("DataRequest ");
- if (stat & ATA_CORR) printk("CorrectedError ");
- if (stat & ATA_IDX) printk("Index ");
- if (stat & ATA_ERR) printk("Error ");
+ if (stat & ATA_DRDY)
+ printk(KERN_CONT "DriveReady ");
+ if (stat & ATA_DF)
+ printk(KERN_CONT "DeviceFault ");
+ if (stat & ATA_DSC)
+ printk(KERN_CONT "SeekComplete ");
+ if (stat & ATA_DRQ)
+ printk(KERN_CONT "DataRequest ");
+ if (stat & ATA_CORR)
+ printk(KERN_CONT "CorrectedError ");
+ if (stat & ATA_IDX)
+ printk(KERN_CONT "Index ");
+ if (stat & ATA_ERR)
+ printk(KERN_CONT "Error ");
}
- printk("}\n");
+ printk(KERN_CONT "}\n");
if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) {
err = ide_read_error(drive);
- printk("%s: %s: error=0x%02x ", drive->name, msg, err);
+ printk(KERN_ERR "%s: %s: error=0x%02x ", drive->name, msg, err);
if (drive->media == ide_disk)
ide_dump_ata_error(drive, err);
else
ide_dump_atapi_error(drive, err);
}
ide_dump_opcode(drive);
- local_irq_restore(flags);
return err;
}
-
EXPORT_SYMBOL(ide_dump_status);
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c
index 03b00e57e93f..63d01c55f865 100644
--- a/drivers/ide/ide-park.c
+++ b/drivers/ide/ide-park.c
@@ -7,17 +7,16 @@ DECLARE_WAIT_QUEUE_HEAD(ide_park_wq);
static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
{
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
struct request_queue *q = drive->queue;
struct request *rq;
int rc;
timeout += jiffies;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (drive->dev_flags & IDE_DFLAG_PARKED) {
- ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
- int reset_timer;
+ int reset_timer = time_before(timeout, drive->sleep);
- reset_timer = time_before(timeout, drive->sleep);
drive->sleep = timeout;
wake_up_all(&ide_park_wq);
if (reset_timer && hwgroup->sleeping &&
@@ -26,10 +25,10 @@ static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
hwgroup->busy = 0;
blk_start_queueing(q);
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
return;
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
rq = blk_get_request(q, READ, __GFP_WAIT);
rq->cmd[0] = REQ_PARK_HEADS;
@@ -62,20 +61,21 @@ ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
unsigned long now;
unsigned int msecs;
if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
return -EOPNOTSUPP;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
now = jiffies;
if (drive->dev_flags & IDE_DFLAG_PARKED &&
time_after(drive->sleep, now))
msecs = jiffies_to_msecs(drive->sleep - now);
else
msecs = 0;
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
return snprintf(buf, 20, "%u\n", msecs);
}
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
new file mode 100644
index 000000000000..8282c6086e6a
--- /dev/null
+++ b/drivers/ide/ide-pm.c
@@ -0,0 +1,235 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+
+int generic_ide_suspend(struct device *dev, pm_message_t mesg)
+{
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ struct request *rq;
+ struct request_pm_state rqpm;
+ ide_task_t args;
+ int ret;
+
+ /* call ACPI _GTM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL)
+ ide_acpi_get_timing(hwif);
+
+ memset(&rqpm, 0, sizeof(rqpm));
+ memset(&args, 0, sizeof(args));
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_SUSPEND;
+ rq->special = &args;
+ rq->data = &rqpm;
+ rqpm.pm_step = IDE_PM_START_SUSPEND;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ rqpm.pm_state = mesg.event;
+
+ ret = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
+
+ /* call ACPI _PS3 only after both devices are suspended */
+ if (ret == 0 && ((drive->dn & 1) || pair == NULL))
+ ide_acpi_set_state(hwif, 0);
+
+ return ret;
+}
+
+int generic_ide_resume(struct device *dev)
+{
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ struct request *rq;
+ struct request_pm_state rqpm;
+ ide_task_t args;
+ int err;
+
+ /* call ACPI _PS0 / _STM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL) {
+ ide_acpi_set_state(hwif, 1);
+ ide_acpi_push_timing(hwif);
+ }
+
+ ide_acpi_exec_tfs(drive);
+
+ memset(&rqpm, 0, sizeof(rqpm));
+ memset(&args, 0, sizeof(args));
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_RESUME;
+ rq->cmd_flags |= REQ_PREEMPT;
+ rq->special = &args;
+ rq->data = &rqpm;
+ rqpm.pm_step = IDE_PM_START_RESUME;
+ rqpm.pm_state = PM_EVENT_ON;
+
+ err = blk_execute_rq(drive->queue, NULL, rq, 1);
+ blk_put_request(rq);
+
+ if (err == 0 && dev->driver) {
+ ide_driver_t *drv = to_ide_driver(dev->driver);
+
+ if (drv->resume)
+ drv->resume(drive);
+ }
+
+ return err;
+}
+
+void ide_complete_power_step(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+
+#ifdef DEBUG_PM
+ printk(KERN_INFO "%s: complete_power_step(step: %d)\n",
+ drive->name, pm->pm_step);
+#endif
+ if (drive->media != ide_disk)
+ return;
+
+ switch (pm->pm_step) {
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
+ if (pm->pm_state == PM_EVENT_FREEZE)
+ pm->pm_step = IDE_PM_COMPLETED;
+ else
+ pm->pm_step = IDE_PM_STANDBY;
+ break;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ pm->pm_step = IDE_PM_COMPLETED;
+ break;
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
+ pm->pm_step = IDE_PM_IDLE;
+ break;
+ case IDE_PM_IDLE: /* Resume step 2 (idle)*/
+ pm->pm_step = IDE_PM_RESTORE_DMA;
+ break;
+ }
+}
+
+ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+ ide_task_t *args = rq->special;
+
+ memset(args, 0, sizeof(*args));
+
+ switch (pm->pm_step) {
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
+ if (drive->media != ide_disk)
+ break;
+ /* Not supported? Switch to next step now. */
+ if (ata_id_flush_enabled(drive->id) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
+ ide_complete_power_step(drive, rq);
+ return ide_stopped;
+ }
+ if (ata_id_flush_ext_enabled(drive->id))
+ args->tf.command = ATA_CMD_FLUSH_EXT;
+ else
+ args->tf.command = ATA_CMD_FLUSH;
+ goto out_do_tf;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ args->tf.command = ATA_CMD_STANDBYNOW1;
+ goto out_do_tf;
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
+ ide_set_max_pio(drive);
+ /*
+ * skip IDE_PM_IDLE for ATAPI devices
+ */
+ if (drive->media != ide_disk)
+ pm->pm_step = IDE_PM_RESTORE_DMA;
+ else
+ ide_complete_power_step(drive, rq);
+ return ide_stopped;
+ case IDE_PM_IDLE: /* Resume step 2 (idle) */
+ args->tf.command = ATA_CMD_IDLEIMMEDIATE;
+ goto out_do_tf;
+ case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
+ /*
+ * Right now, all we do is call ide_set_dma(drive),
+ * we could be smarter and check for current xfer_speed
+ * in struct drive etc...
+ */
+ if (drive->hwif->dma_ops == NULL)
+ break;
+ /*
+ * TODO: respect IDE_DFLAG_USING_DMA
+ */
+ ide_set_dma(drive);
+ break;
+ }
+
+ pm->pm_step = IDE_PM_COMPLETED;
+ return ide_stopped;
+
+out_do_tf:
+ args->tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ args->data_phase = TASKFILE_NO_DATA;
+ return do_rw_taskfile(drive, args);
+}
+
+/**
+ * ide_complete_pm_request - end the current Power Management request
+ * @drive: target drive
+ * @rq: request
+ *
+ * This function cleans up the current PM request and stops the queue
+ * if necessary.
+ */
+void ide_complete_pm_request(ide_drive_t *drive, struct request *rq)
+{
+ struct request_queue *q = drive->queue;
+ unsigned long flags;
+
+#ifdef DEBUG_PM
+ printk("%s: completing PM request, %s\n", drive->name,
+ blk_pm_suspend_request(rq) ? "suspend" : "resume");
+#endif
+ spin_lock_irqsave(q->queue_lock, flags);
+ if (blk_pm_suspend_request(rq)) {
+ blk_stop_queue(q);
+ } else {
+ drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
+ blk_start_queue(q);
+ }
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ drive->hwif->hwgroup->rq = NULL;
+
+ if (blk_end_request(rq, 0, 0))
+ BUG();
+}
+
+void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
+{
+ struct request_pm_state *pm = rq->data;
+
+ if (blk_pm_suspend_request(rq) &&
+ pm->pm_step == IDE_PM_START_SUSPEND)
+ /* Mark drive blocked when starting the suspend sequence. */
+ drive->dev_flags |= IDE_DFLAG_BLOCKED;
+ else if (blk_pm_resume_request(rq) &&
+ pm->pm_step == IDE_PM_START_RESUME) {
+ /*
+ * The first thing we do on wakeup is to wait for BSY bit to
+ * go away (with a looong timeout) as a drive on this hwif may
+ * just be POSTing itself.
+ * We do that before even selecting as the "other" device on
+ * the bus may be broken enough to walk on our toes at this
+ * point.
+ */
+ ide_hwif_t *hwif = drive->hwif;
+ int rc;
+#ifdef DEBUG_PM
+ printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name);
+#endif
+ rc = ide_wait_not_busy(hwif, 35000);
+ if (rc)
+ printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
+ SELECT_DRIVE(drive);
+ hwif->tp_ops->set_irq(hwif, 1);
+ rc = ide_wait_not_busy(hwif, 100000);
+ if (rc)
+ printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
+ }
+}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index c55bdbd22314..a64ec259f3d1 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -110,20 +110,22 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
* read and parse the results. This function is run with
* interrupts disabled.
*/
-
-static inline void do_identify (ide_drive_t *drive, u8 cmd)
+
+static void do_identify(ide_drive_t *drive, u8 cmd)
{
ide_hwif_t *hwif = HWIF(drive);
u16 *id = drive->id;
char *m = (char *)&id[ATA_ID_PROD];
+ unsigned long flags;
int bswap = 1, is_cfa;
+ /* local CPU only; some systems need this */
+ local_irq_save(flags);
/* read 512 bytes of id info */
hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);
+ local_irq_restore(flags);
drive->dev_flags |= IDE_DFLAG_ID_READ;
-
- local_irq_enable();
#ifdef DEBUG
printk(KERN_INFO "%s: dumping identify data\n", drive->name);
ide_dump_identify((u8 *)id);
@@ -306,17 +308,12 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd)
s = tp_ops->read_status(hwif);
if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) {
- unsigned long flags;
-
- /* local CPU only; some systems need this */
- local_irq_save(flags);
/* drive returned ID */
do_identify(drive, cmd);
/* drive responded with ID */
rc = 0;
/* clear drive IRQ */
(void)tp_ops->read_status(hwif);
- local_irq_restore(flags);
} else {
/* drive refused ID */
rc = 2;
@@ -554,8 +551,8 @@ static void enable_nest (ide_drive_t *drive)
* 1 device was found
* (note: IDE_DFLAG_PRESENT might still be not set)
*/
-
-static inline u8 probe_for_drive (ide_drive_t *drive)
+
+static u8 probe_for_drive(ide_drive_t *drive)
{
char *m;
@@ -642,7 +639,7 @@ static int ide_register_port(ide_hwif_t *hwif)
int ret;
/* register with global device tree */
- strlcpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE);
+ dev_set_name(&hwif->gendev, hwif->name);
hwif->gendev.driver_data = hwif;
if (hwif->gendev.parent == NULL) {
if (hwif->dev)
@@ -864,31 +861,6 @@ static void ide_port_tune_devices(ide_hwif_t *hwif)
}
/*
- * save_match() is used to simplify logic in init_irq() below.
- *
- * A loophole here is that we may not know about a particular
- * hwif's irq until after that hwif is actually probed/initialized..
- * This could be a problem for the case where an hwif is on a
- * dual interface that requires serialization (eg. cmd640) and another
- * hwif using one of the same irqs is initialized beforehand.
- *
- * This routine detects and reports such situations, but does not fix them.
- */
-static void save_match(ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
-{
- ide_hwif_t *m = *match;
-
- if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
- if (!new->hwgroup)
- return;
- printk(KERN_WARNING "%s: potential IRQ problem with %s and %s\n",
- hwif->name, new->name, m->name);
- }
- if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
- *match = new;
-}
-
-/*
* init request queue
*/
static int ide_init_queue(ide_drive_t *drive)
@@ -906,7 +878,8 @@ static int ide_init_queue(ide_drive_t *drive)
* do not.
*/
- q = blk_init_queue_node(do_ide_request, &ide_lock, hwif_to_node(hwif));
+ q = blk_init_queue_node(do_ide_request, &hwif->hwgroup->lock,
+ hwif_to_node(hwif));
if (!q)
return 1;
@@ -947,7 +920,7 @@ static void ide_add_drive_to_hwgroup(ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
if (!hwgroup->drive) {
/* first drive for hwgroup. */
drive->next = drive;
@@ -957,7 +930,7 @@ static void ide_add_drive_to_hwgroup(ide_drive_t *drive)
drive->next = hwgroup->drive->next;
hwgroup->drive->next = drive;
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
}
/*
@@ -1002,7 +975,7 @@ void ide_remove_port_from_hwgroup(ide_hwif_t *hwif)
ide_ports[hwif->index] = NULL;
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/*
* Remove us from the hwgroup, and free
* the hwgroup if we were the only member
@@ -1030,7 +1003,7 @@ void ide_remove_port_from_hwgroup(ide_hwif_t *hwif)
}
BUG_ON(hwgroup->hwif == hwif);
}
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
}
/*
@@ -1051,27 +1024,13 @@ static int init_irq (ide_hwif_t *hwif)
mutex_lock(&ide_cfg_mtx);
hwif->hwgroup = NULL;
- /*
- * Group up with any other hwifs that share our irq(s).
- */
for (index = 0; index < MAX_HWIFS; index++) {
ide_hwif_t *h = ide_ports[index];
if (h && h->hwgroup) { /* scan only initialized ports */
- if (hwif->irq == h->irq) {
- hwif->sharing_irq = h->sharing_irq = 1;
- if (hwif->chipset != ide_pci ||
- h->chipset != ide_pci) {
- save_match(hwif, h, &match);
- }
- }
- if (hwif->serialized) {
- if (hwif->mate && hwif->mate->irq == h->irq)
- save_match(hwif, h, &match);
- }
- if (h->serialized) {
- if (h->mate && hwif->irq == h->mate->irq)
- save_match(hwif, h, &match);
+ if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) {
+ if (hwif->host == h->host)
+ match = h;
}
}
}
@@ -1092,17 +1051,19 @@ static int init_irq (ide_hwif_t *hwif)
* linked list, the first entry is the hwif that owns
* hwgroup->handler - do not change that.
*/
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
hwif->next = hwgroup->hwif->next;
hwgroup->hwif->next = hwif;
BUG_ON(hwif->next == hwif);
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
} else {
hwgroup = kmalloc_node(sizeof(*hwgroup), GFP_KERNEL|__GFP_ZERO,
hwif_to_node(hwif));
if (hwgroup == NULL)
goto out_up;
+ spin_lock_init(&hwgroup->lock);
+
hwif->hwgroup = hwgroup;
hwgroup->hwif = hwif->next = hwif;
@@ -1122,8 +1083,7 @@ static int init_irq (ide_hwif_t *hwif)
sa = IRQF_SHARED;
#endif /* __mc68000__ */
- if (hwif->chipset == ide_pci || hwif->chipset == ide_cmd646 ||
- hwif->chipset == ide_ali14xx)
+ if (hwif->chipset == ide_pci)
sa = IRQF_SHARED;
if (io_ports->ctl_addr)
@@ -1150,8 +1110,7 @@ static int init_irq (ide_hwif_t *hwif)
io_ports->data_addr, hwif->irq);
#endif /* __mc68000__ */
if (match)
- printk(KERN_CONT " (%sed with %s)",
- hwif->sharing_irq ? "shar" : "serializ", match->name);
+ printk(KERN_CONT " (serialized with %s)", match->name);
printk(KERN_CONT "\n");
mutex_unlock(&ide_cfg_mtx);
@@ -1263,20 +1222,21 @@ static void ide_remove_drive_from_hwgroup(ide_drive_t *drive)
static void drive_release_dev (struct device *dev)
{
ide_drive_t *drive = container_of(dev, ide_drive_t, gendev);
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
ide_proc_unregister_device(drive);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
ide_remove_drive_from_hwgroup(drive);
kfree(drive->id);
drive->id = NULL;
drive->dev_flags &= ~IDE_DFLAG_PRESENT;
/* Messed up locking ... */
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
blk_cleanup_queue(drive->queue);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
drive->queue = NULL;
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
complete(&drive->gendev_rel_comp);
}
@@ -1352,7 +1312,7 @@ static void hwif_register_devices(ide_hwif_t *hwif)
if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
- snprintf(dev->bus_id, BUS_ID_SIZE, "%u.%u", hwif->index, i);
+ dev_set_name(dev, "%u.%u", hwif->index, i);
dev->parent = &hwif->gendev;
dev->bus = &ide_bus_type;
dev->driver_data = drive;
@@ -1436,13 +1396,11 @@ static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
}
if ((d->host_flags & IDE_HFLAG_SERIALIZE) ||
- ((d->host_flags & IDE_HFLAG_SERIALIZE_DMA) && hwif->dma_base)) {
- if (hwif->mate)
- hwif->mate->serialized = hwif->serialized = 1;
- }
+ ((d->host_flags & IDE_HFLAG_SERIALIZE_DMA) && hwif->dma_base))
+ hwif->host->host_flags |= IDE_HFLAG_SERIALIZE;
- if (d->host_flags & IDE_HFLAG_RQSIZE_256)
- hwif->rqsize = 256;
+ if (d->max_sectors)
+ hwif->rqsize = d->max_sectors;
/* call chipset specific routine for each enabled port */
if (d->init_hwif)
@@ -1794,59 +1752,3 @@ void ide_port_scan(ide_hwif_t *hwif)
ide_proc_port_register_devices(hwif);
}
EXPORT_SYMBOL_GPL(ide_port_scan);
-
-static void ide_legacy_init_one(hw_regs_t **hws, hw_regs_t *hw,
- u8 port_no, const struct ide_port_info *d,
- unsigned long config)
-{
- unsigned long base, ctl;
- int irq;
-
- if (port_no == 0) {
- base = 0x1f0;
- ctl = 0x3f6;
- irq = 14;
- } else {
- base = 0x170;
- ctl = 0x376;
- irq = 15;
- }
-
- if (!request_region(base, 8, d->name)) {
- printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n",
- d->name, base, base + 7);
- return;
- }
-
- if (!request_region(ctl, 1, d->name)) {
- printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n",
- d->name, ctl);
- release_region(base, 8);
- return;
- }
-
- ide_std_init_ports(hw, base, ctl);
- hw->irq = irq;
- hw->chipset = d->chipset;
- hw->config = config;
-
- hws[port_no] = hw;
-}
-
-int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config)
-{
- hw_regs_t hw[2], *hws[] = { NULL, NULL, NULL, NULL };
-
- memset(&hw, 0, sizeof(hw));
-
- if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0)
- ide_legacy_init_one(hws, &hw[0], 0, d, config);
- ide_legacy_init_one(hws, &hw[1], 1, d, config);
-
- if (hws[0] == NULL && hws[1] == NULL &&
- (d->host_flags & IDE_HFLAG_SINGLE))
- return -ENOENT;
-
- return ide_host_add(d, hws, NULL);
-}
-EXPORT_SYMBOL_GPL(ide_legacy_device_add);
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index f3cddd1b2f8f..a14e2938e4f3 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -46,10 +46,6 @@ static int proc_ide_read_imodel
case ide_qd65xx: name = "qd65xx"; break;
case ide_umc8672: name = "umc8672"; break;
case ide_ht6560b: name = "ht6560b"; break;
- case ide_rz1000: name = "rz1000"; break;
- case ide_trm290: name = "trm290"; break;
- case ide_cmd646: name = "cmd646"; break;
- case ide_cy82c693: name = "cy82c693"; break;
case ide_4drives: name = "4drives"; break;
case ide_pmac: name = "mac-io"; break;
case ide_au1xxx: name = "au1xxx"; break;
@@ -155,13 +151,8 @@ static int ide_read_setting(ide_drive_t *drive,
const struct ide_devset *ds = setting->setting;
int val = -EINVAL;
- if (ds->get) {
- unsigned long flags;
-
- spin_lock_irqsave(&ide_lock, flags);
+ if (ds->get)
val = ds->get(drive);
- spin_unlock_irqrestore(&ide_lock, flags);
- }
return val;
}
@@ -583,31 +574,19 @@ EXPORT_SYMBOL(ide_proc_register_driver);
* Clean up the driver specific /proc files and IDE settings
* for a given drive.
*
- * Takes ide_setting_mtx and ide_lock.
- * Caller must hold none of the locks.
+ * Takes ide_setting_mtx.
*/
void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver)
{
- unsigned long flags;
-
ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
/*
- * ide_setting_mtx protects the settings list
- * ide_lock protects the use of settings
- *
- * so we need to hold both, ide_settings_sem because we want to
- * modify the settings list, and ide_lock because we cannot take
- * a setting out that is being used.
- *
- * OTOH both ide_{read,write}_setting are only ever used under
- * ide_setting_mtx.
+ * ide_setting_mtx protects both the settings list and the use
+ * of settings (we cannot take a setting out that is being used).
*/
drive->settings = NULL;
- spin_unlock_irqrestore(&ide_lock, flags);
mutex_unlock(&ide_setting_mtx);
}
EXPORT_SYMBOL(ide_proc_unregister_driver);
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 04f8f13cb9d7..f0f09f702e9c 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -74,9 +74,6 @@ static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR,
DEFINE_MUTEX(ide_cfg_mtx);
-__cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock);
-EXPORT_SYMBOL(ide_lock);
-
static void ide_port_init_devices_data(ide_hwif_t *);
/*
@@ -130,7 +127,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
}
}
-/* Called with ide_lock held. */
static void __ide_port_unregister_devices(ide_hwif_t *hwif)
{
int i;
@@ -139,10 +135,8 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif)
ide_drive_t *drive = &hwif->drives[i];
if (drive->dev_flags & IDE_DFLAG_PRESENT) {
- spin_unlock_irq(&ide_lock);
device_unregister(&drive->gendev);
wait_for_completion(&drive->gendev_rel_comp);
- spin_lock_irq(&ide_lock);
}
}
}
@@ -150,11 +144,9 @@ static void __ide_port_unregister_devices(ide_hwif_t *hwif)
void ide_port_unregister_devices(ide_hwif_t *hwif)
{
mutex_lock(&ide_cfg_mtx);
- spin_lock_irq(&ide_lock);
__ide_port_unregister_devices(hwif);
hwif->present = 0;
ide_port_init_devices_data(hwif);
- spin_unlock_irq(&ide_lock);
mutex_unlock(&ide_cfg_mtx);
}
EXPORT_SYMBOL_GPL(ide_port_unregister_devices);
@@ -192,12 +184,10 @@ void ide_unregister(ide_hwif_t *hwif)
mutex_lock(&ide_cfg_mtx);
- spin_lock_irq(&ide_lock);
if (hwif->present) {
__ide_port_unregister_devices(hwif);
hwif->present = 0;
}
- spin_unlock_irq(&ide_lock);
ide_proc_unregister_port(hwif);
@@ -340,6 +330,7 @@ static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
static int set_pio_mode(ide_drive_t *drive, int arg)
{
ide_hwif_t *hwif = drive->hwif;
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
const struct ide_port_ops *port_ops = hwif->port_ops;
if (arg < 0 || arg > 255)
@@ -354,9 +345,9 @@ static int set_pio_mode(ide_drive_t *drive, int arg)
unsigned long flags;
/* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
- spin_lock_irqsave(&ide_lock, flags);
+ spin_lock_irqsave(&hwgroup->lock, flags);
port_ops->set_pio_mode(drive, arg);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&hwgroup->lock, flags);
} else
port_ops->set_pio_mode(drive, arg);
} else {
@@ -397,80 +388,6 @@ ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
ide_ext_devset_rw_sync(using_dma, using_dma);
__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
-static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
-{
- ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
- ide_hwif_t *hwif = HWIF(drive);
- struct request *rq;
- struct request_pm_state rqpm;
- ide_task_t args;
- int ret;
-
- /* call ACPI _GTM only once */
- if ((drive->dn & 1) == 0 || pair == NULL)
- ide_acpi_get_timing(hwif);
-
- memset(&rqpm, 0, sizeof(rqpm));
- memset(&args, 0, sizeof(args));
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_SUSPEND;
- rq->special = &args;
- rq->data = &rqpm;
- rqpm.pm_step = IDE_PM_START_SUSPEND;
- if (mesg.event == PM_EVENT_PRETHAW)
- mesg.event = PM_EVENT_FREEZE;
- rqpm.pm_state = mesg.event;
-
- ret = blk_execute_rq(drive->queue, NULL, rq, 0);
- blk_put_request(rq);
-
- /* call ACPI _PS3 only after both devices are suspended */
- if (ret == 0 && ((drive->dn & 1) || pair == NULL))
- ide_acpi_set_state(hwif, 0);
-
- return ret;
-}
-
-static int generic_ide_resume(struct device *dev)
-{
- ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
- ide_hwif_t *hwif = HWIF(drive);
- struct request *rq;
- struct request_pm_state rqpm;
- ide_task_t args;
- int err;
-
- /* call ACPI _PS0 / _STM only once */
- if ((drive->dn & 1) == 0 || pair == NULL) {
- ide_acpi_set_state(hwif, 1);
- ide_acpi_push_timing(hwif);
- }
-
- ide_acpi_exec_tfs(drive);
-
- memset(&rqpm, 0, sizeof(rqpm));
- memset(&args, 0, sizeof(args));
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_PM_RESUME;
- rq->cmd_flags |= REQ_PREEMPT;
- rq->special = &args;
- rq->data = &rqpm;
- rqpm.pm_step = IDE_PM_START_RESUME;
- rqpm.pm_state = PM_EVENT_ON;
-
- err = blk_execute_rq(drive->queue, NULL, rq, 1);
- blk_put_request(rq);
-
- if (err == 0 && dev->driver) {
- ide_driver_t *drv = to_ide_driver(dev->driver);
-
- if (drv->resume)
- drv->resume(drive);
- }
-
- return err;
-}
-
/**
* ide_device_get - get an additional reference to a ide_drive_t
* @drive: device to get a reference to
diff --git a/drivers/ide/ide_arm.c b/drivers/ide/ide_arm.c
index f728f2927b5a..bdcac94d7c1f 100644
--- a/drivers/ide/ide_arm.c
+++ b/drivers/ide/ide_arm.c
@@ -15,15 +15,8 @@
#define DRV_NAME "ide_arm"
-#ifdef CONFIG_ARCH_CLPS7500
-# include <mach/hardware.h>
-#
-# define IDE_ARM_IO (ISASLOT_IO + 0x1f0)
-# define IDE_ARM_IRQ IRQ_ISA_14
-#else
-# define IDE_ARM_IO 0x1f0
-# define IDE_ARM_IRQ IRQ_HARDDISK
-#endif
+#define IDE_ARM_IO 0x1f0
+#define IDE_ARM_IRQ IRQ_HARDDISK
static int __init ide_arm_init(void)
{
diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c
index 43f97cc1d30e..3c60064f1d4f 100644
--- a/drivers/ide/macide.c
+++ b/drivers/ide/macide.c
@@ -18,7 +18,6 @@
#include <linux/delay.h>
#include <linux/ide.h>
-#include <asm/machw.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
#include <asm/mac_baboon.h>
diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c
index 799557c25eef..624e62e5cc9a 100644
--- a/drivers/ide/pdc202xx_old.c
+++ b/drivers/ide/pdc202xx_old.c
@@ -350,16 +350,17 @@ static const struct ide_dma_ops pdc2026x_dma_ops = {
.dma_timeout = pdc202xx_dma_timeout,
};
-#define DECLARE_PDC2026X_DEV(udma, extra_flags) \
+#define DECLARE_PDC2026X_DEV(udma, sectors) \
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_pdc202xx, \
.port_ops = &pdc2026x_port_ops, \
.dma_ops = &pdc2026x_dma_ops, \
- .host_flags = IDE_HFLAGS_PDC202XX | extra_flags, \
+ .host_flags = IDE_HFLAGS_PDC202XX, \
.pio_mask = ATA_PIO4, \
.mwdma_mask = ATA_MWDMA2, \
.udma_mask = udma, \
+ .max_sectors = sectors, \
}
static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
@@ -376,8 +377,8 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
/* 1: PDC2026{2,3} */
DECLARE_PDC2026X_DEV(ATA_UDMA4, 0),
- /* 2: PDC2026{5,7} */
- DECLARE_PDC2026X_DEV(ATA_UDMA5, IDE_HFLAG_RQSIZE_256),
+ /* 2: PDC2026{5,7}: UDMA5, limit LBA48 requests to 256 sectors */
+ DECLARE_PDC2026X_DEV(ATA_UDMA5, 256),
};
/**
diff --git a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c
index 7daf0135cbac..a6414a884eb1 100644
--- a/drivers/ide/rz1000.c
+++ b/drivers/ide/rz1000.c
@@ -22,34 +22,48 @@
#define DRV_NAME "rz1000"
-static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif)
+static int __devinit rz1000_disable_readahead(struct pci_dev *dev)
{
- struct pci_dev *dev = to_pci_dev(hwif->dev);
u16 reg;
if (!pci_read_config_word (dev, 0x40, &reg) &&
!pci_write_config_word(dev, 0x40, reg & 0xdfff)) {
printk(KERN_INFO "%s: disabled chipset read-ahead "
- "(buggy RZ1000/RZ1001)\n", hwif->name);
+ "(buggy RZ1000/RZ1001)\n", pci_name(dev));
+ return 0;
} else {
- if (hwif->mate)
- hwif->mate->serialized = hwif->serialized = 1;
- hwif->host_flags |= IDE_HFLAG_NO_UNMASK_IRQS;
printk(KERN_INFO "%s: serialized, disabled unmasking "
- "(buggy RZ1000/RZ1001)\n", hwif->name);
+ "(buggy RZ1000/RZ1001)\n", pci_name(dev));
+ return 1;
}
}
static const struct ide_port_info rz1000_chipset __devinitdata = {
.name = DRV_NAME,
- .init_hwif = init_hwif_rz1000,
- .chipset = ide_rz1000,
.host_flags = IDE_HFLAG_NO_DMA,
};
static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_pci_init_one(dev, &rz1000_chipset, NULL);
+ struct ide_port_info d = rz1000_chipset;
+ int rc;
+
+ rc = pci_enable_device(dev);
+ if (rc)
+ return rc;
+
+ if (rz1000_disable_readahead(dev)) {
+ d.host_flags |= IDE_HFLAG_SERIALIZE;
+ d.host_flags |= IDE_HFLAG_NO_UNMASK_IRQS;
+ }
+
+ return ide_pci_init_one(dev, &d, NULL);
+}
+
+static void rz1000_remove(struct pci_dev *dev)
+{
+ ide_pci_remove(dev);
+ pci_disable_device(dev);
}
static const struct pci_device_id rz1000_pci_tbl[] = {
@@ -63,7 +77,7 @@ static struct pci_driver rz1000_pci_driver = {
.name = "RZ1000_IDE",
.id_table = rz1000_pci_tbl,
.probe = rz1000_init_one,
- .remove = ide_pci_remove,
+ .remove = rz1000_remove,
};
static int __init rz1000_ide_init(void)
diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c
index f1a8758e3a99..ec7f766ef5e4 100644
--- a/drivers/ide/sc1200.c
+++ b/drivers/ide/sc1200.c
@@ -104,11 +104,12 @@ static u8 sc1200_udma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
ide_drive_t *mate = ide_get_pair_dev(drive);
- u16 *mateid = mate->id;
+ u16 *mateid;
u8 mask = hwif->ultra_mask;
if (mate == NULL)
goto out;
+ mateid = mate->id;
if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c
index 75ea61526566..2a5ea90cf8b8 100644
--- a/drivers/ide/trm290.c
+++ b/drivers/ide/trm290.c
@@ -328,10 +328,10 @@ static struct ide_dma_ops trm290_dma_ops = {
static const struct ide_port_info trm290_chipset __devinitdata = {
.name = DRV_NAME,
.init_hwif = init_hwif_trm290,
- .chipset = ide_trm290,
.port_ops = &trm290_port_ops,
.dma_ops = &trm290_dma_ops,
- .host_flags = IDE_HFLAG_NO_ATAPI_DMA |
+ .host_flags = IDE_HFLAG_TRM290 |
+ IDE_HFLAG_NO_ATAPI_DMA |
#if 0 /* play it safe for now */
IDE_HFLAG_TRUST_BIOS_FOR_DMA |
#endif
diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c
index 9120063e8f87..13b63e7fa353 100644
--- a/drivers/ide/tx4938ide.c
+++ b/drivers/ide/tx4938ide.c
@@ -181,7 +181,7 @@ static void tx4938ide_input_data_swap(ide_drive_t *drive, struct request *rq,
while (count--)
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static void tx4938ide_output_data_swap(ide_drive_t *drive, struct request *rq,
@@ -195,7 +195,7 @@ static void tx4938ide_output_data_swap(ide_drive_t *drive, struct request *rq,
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
ptr++;
}
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static const struct ide_tp_ops tx4938ide_tp_ops = {
diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c
index bafb7d1a22e2..97cd9e0f66f6 100644
--- a/drivers/ide/tx4939ide.c
+++ b/drivers/ide/tx4939ide.c
@@ -259,6 +259,12 @@ static int tx4939ide_build_dmatable(ide_drive_t *drive, struct request *rq)
bcount = 0x10000 - (cur_addr & 0xffff);
if (bcount > cur_len)
bcount = cur_len;
+ /*
+ * This workaround for zero count seems required.
+ * (standard ide_build_dmatable do it too)
+ */
+ if ((bcount & 0xffff) == 0x0000)
+ bcount = 0x8000;
*table++ = bcount & 0xffff;
*table++ = cur_addr;
cur_addr += bcount;
@@ -558,7 +564,7 @@ static void tx4939ide_input_data_swap(ide_drive_t *drive, struct request *rq,
while (count--)
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static void tx4939ide_output_data_swap(ide_drive_t *drive, struct request *rq,
@@ -572,7 +578,7 @@ static void tx4939ide_output_data_swap(ide_drive_t *drive, struct request *rq,
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
ptr++;
}
- __ide_flush_dcache_range((unsigned long)buf, count * 2);
+ __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
}
static const struct ide_tp_ops tx4939ide_tp_ops = {
diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c
index 1da076e0c917..e29978cf6197 100644
--- a/drivers/ide/umc8672.c
+++ b/drivers/ide/umc8672.c
@@ -107,18 +107,21 @@ static void umc_set_speeds(u8 speeds[])
static void umc_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = drive->hwif;
- unsigned long flags;
+ ide_hwgroup_t *mate_hwgroup = hwif->mate ? hwif->mate->hwgroup : NULL;
+ unsigned long uninitialized_var(flags);
printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
drive->name, pio, pio_to_umc[pio]);
- spin_lock_irqsave(&ide_lock, flags);
- if (hwif->mate && hwif->mate->hwgroup->handler) {
+ if (mate_hwgroup)
+ spin_lock_irqsave(&mate_hwgroup->lock, flags);
+ if (mate_hwgroup && mate_hwgroup->handler) {
printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n");
} else {
current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
umc_set_speeds(current_speeds);
}
- spin_unlock_irqrestore(&ide_lock, flags);
+ if (mate_hwgroup)
+ spin_unlock_irqrestore(&mate_hwgroup->lock, flags);
}
static const struct ide_port_ops umc8672_port_ops = {
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index a5dc78ae62d4..dd0db67bf8d7 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -37,6 +37,7 @@ config INFINIBAND_USER_MEM
config INFINIBAND_ADDR_TRANS
bool
depends on INET
+ depends on !(INFINIBAND = y && IPV6 = m)
default y
source "drivers/infiniband/hw/mthca/Kconfig"
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 09a2bec7fd32..ce511d8748ce 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -41,6 +41,8 @@
#include <net/neighbour.h>
#include <net/route.h>
#include <net/netevent.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
#include <rdma/ib_addr.h>
MODULE_AUTHOR("Sean Hefty");
@@ -49,8 +51,8 @@ MODULE_LICENSE("Dual BSD/GPL");
struct addr_req {
struct list_head list;
- struct sockaddr src_addr;
- struct sockaddr dst_addr;
+ struct sockaddr_storage src_addr;
+ struct sockaddr_storage dst_addr;
struct rdma_dev_addr *addr;
struct rdma_addr_client *client;
void *context;
@@ -113,15 +115,33 @@ EXPORT_SYMBOL(rdma_copy_addr);
int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
{
struct net_device *dev;
- __be32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
- int ret;
+ int ret = -EADDRNOTAVAIL;
- dev = ip_dev_find(&init_net, ip);
- if (!dev)
- return -EADDRNOTAVAIL;
+ switch (addr->sa_family) {
+ case AF_INET:
+ dev = ip_dev_find(&init_net,
+ ((struct sockaddr_in *) addr)->sin_addr.s_addr);
- ret = rdma_copy_addr(dev_addr, dev, NULL);
- dev_put(dev);
+ if (!dev)
+ return ret;
+
+ ret = rdma_copy_addr(dev_addr, dev, NULL);
+ dev_put(dev);
+ break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ for_each_netdev(&init_net, dev) {
+ if (ipv6_chk_addr(&init_net,
+ &((struct sockaddr_in6 *) addr)->sin6_addr,
+ dev, 1)) {
+ ret = rdma_copy_addr(dev_addr, dev, NULL);
+ break;
+ }
+ }
+ break;
+#endif
+ }
return ret;
}
EXPORT_SYMBOL(rdma_translate_ip);
@@ -156,22 +176,46 @@ static void queue_req(struct addr_req *req)
mutex_unlock(&lock);
}
-static void addr_send_arp(struct sockaddr_in *dst_in)
+static void addr_send_arp(struct sockaddr *dst_in)
{
struct rtable *rt;
struct flowi fl;
- __be32 dst_ip = dst_in->sin_addr.s_addr;
memset(&fl, 0, sizeof fl);
- fl.nl_u.ip4_u.daddr = dst_ip;
- if (ip_route_output_key(&init_net, &rt, &fl))
- return;
- neigh_event_send(rt->u.dst.neighbour, NULL);
- ip_rt_put(rt);
+ switch (dst_in->sa_family) {
+ case AF_INET:
+ fl.nl_u.ip4_u.daddr =
+ ((struct sockaddr_in *) dst_in)->sin_addr.s_addr;
+
+ if (ip_route_output_key(&init_net, &rt, &fl))
+ return;
+
+ neigh_event_send(rt->u.dst.neighbour, NULL);
+ ip_rt_put(rt);
+ break;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ {
+ struct dst_entry *dst;
+
+ fl.nl_u.ip6_u.daddr =
+ ((struct sockaddr_in6 *) dst_in)->sin6_addr;
+
+ dst = ip6_route_output(&init_net, NULL, &fl);
+ if (!dst)
+ return;
+
+ neigh_event_send(dst->neighbour, NULL);
+ dst_release(dst);
+ break;
+ }
+#endif
+ }
}
-static int addr_resolve_remote(struct sockaddr_in *src_in,
+static int addr4_resolve_remote(struct sockaddr_in *src_in,
struct sockaddr_in *dst_in,
struct rdma_dev_addr *addr)
{
@@ -220,10 +264,60 @@ out:
return ret;
}
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
+ struct sockaddr_in6 *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ struct flowi fl;
+ struct neighbour *neigh;
+ struct dst_entry *dst;
+ int ret = -ENODATA;
+
+ memset(&fl, 0, sizeof fl);
+ fl.nl_u.ip6_u.daddr = dst_in->sin6_addr;
+ fl.nl_u.ip6_u.saddr = src_in->sin6_addr;
+
+ dst = ip6_route_output(&init_net, NULL, &fl);
+ if (!dst)
+ return ret;
+
+ if (dst->dev->flags & IFF_NOARP) {
+ ret = rdma_copy_addr(addr, dst->dev, NULL);
+ } else {
+ neigh = dst->neighbour;
+ if (neigh && (neigh->nud_state & NUD_VALID))
+ ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
+ }
+
+ dst_release(dst);
+ return ret;
+}
+#else
+static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
+ struct sockaddr_in6 *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ return -EADDRNOTAVAIL;
+}
+#endif
+
+static int addr_resolve_remote(struct sockaddr *src_in,
+ struct sockaddr *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ if (src_in->sa_family == AF_INET) {
+ return addr4_resolve_remote((struct sockaddr_in *) src_in,
+ (struct sockaddr_in *) dst_in, addr);
+ } else
+ return addr6_resolve_remote((struct sockaddr_in6 *) src_in,
+ (struct sockaddr_in6 *) dst_in, addr);
+}
+
static void process_req(struct work_struct *work)
{
struct addr_req *req, *temp_req;
- struct sockaddr_in *src_in, *dst_in;
+ struct sockaddr *src_in, *dst_in;
struct list_head done_list;
INIT_LIST_HEAD(&done_list);
@@ -231,8 +325,8 @@ static void process_req(struct work_struct *work)
mutex_lock(&lock);
list_for_each_entry_safe(req, temp_req, &req_list, list) {
if (req->status == -ENODATA) {
- src_in = (struct sockaddr_in *) &req->src_addr;
- dst_in = (struct sockaddr_in *) &req->dst_addr;
+ src_in = (struct sockaddr *) &req->src_addr;
+ dst_in = (struct sockaddr *) &req->dst_addr;
req->status = addr_resolve_remote(src_in, dst_in,
req->addr);
if (req->status && time_after_eq(jiffies, req->timeout))
@@ -251,41 +345,86 @@ static void process_req(struct work_struct *work)
list_for_each_entry_safe(req, temp_req, &done_list, list) {
list_del(&req->list);
- req->callback(req->status, &req->src_addr, req->addr,
- req->context);
+ req->callback(req->status, (struct sockaddr *) &req->src_addr,
+ req->addr, req->context);
put_client(req->client);
kfree(req);
}
}
-static int addr_resolve_local(struct sockaddr_in *src_in,
- struct sockaddr_in *dst_in,
+static int addr_resolve_local(struct sockaddr *src_in,
+ struct sockaddr *dst_in,
struct rdma_dev_addr *addr)
{
struct net_device *dev;
- __be32 src_ip = src_in->sin_addr.s_addr;
- __be32 dst_ip = dst_in->sin_addr.s_addr;
int ret;
- dev = ip_dev_find(&init_net, dst_ip);
- if (!dev)
- return -EADDRNOTAVAIL;
+ switch (dst_in->sa_family) {
+ case AF_INET:
+ {
+ __be32 src_ip = ((struct sockaddr_in *) src_in)->sin_addr.s_addr;
+ __be32 dst_ip = ((struct sockaddr_in *) dst_in)->sin_addr.s_addr;
+
+ dev = ip_dev_find(&init_net, dst_ip);
+ if (!dev)
+ return -EADDRNOTAVAIL;
+
+ if (ipv4_is_zeronet(src_ip)) {
+ src_in->sa_family = dst_in->sa_family;
+ ((struct sockaddr_in *) src_in)->sin_addr.s_addr = dst_ip;
+ ret = rdma_copy_addr(addr, dev, dev->dev_addr);
+ } else if (ipv4_is_loopback(src_ip)) {
+ ret = rdma_translate_ip(dst_in, addr);
+ if (!ret)
+ memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ } else {
+ ret = rdma_translate_ip(src_in, addr);
+ if (!ret)
+ memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ }
+ dev_put(dev);
+ break;
+ }
- if (ipv4_is_zeronet(src_ip)) {
- src_in->sin_family = dst_in->sin_family;
- src_in->sin_addr.s_addr = dst_ip;
- ret = rdma_copy_addr(addr, dev, dev->dev_addr);
- } else if (ipv4_is_loopback(src_ip)) {
- ret = rdma_translate_ip((struct sockaddr *)dst_in, addr);
- if (!ret)
- memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
- } else {
- ret = rdma_translate_ip((struct sockaddr *)src_in, addr);
- if (!ret)
- memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ {
+ struct in6_addr *a;
+
+ for_each_netdev(&init_net, dev)
+ if (ipv6_chk_addr(&init_net,
+ &((struct sockaddr_in6 *) addr)->sin6_addr,
+ dev, 1))
+ break;
+
+ if (!dev)
+ return -EADDRNOTAVAIL;
+
+ a = &((struct sockaddr_in6 *) src_in)->sin6_addr;
+
+ if (ipv6_addr_any(a)) {
+ src_in->sa_family = dst_in->sa_family;
+ ((struct sockaddr_in6 *) src_in)->sin6_addr =
+ ((struct sockaddr_in6 *) dst_in)->sin6_addr;
+ ret = rdma_copy_addr(addr, dev, dev->dev_addr);
+ } else if (ipv6_addr_loopback(a)) {
+ ret = rdma_translate_ip(dst_in, addr);
+ if (!ret)
+ memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ } else {
+ ret = rdma_translate_ip(src_in, addr);
+ if (!ret)
+ memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
+ }
+ break;
+ }
+#endif
+
+ default:
+ ret = -EADDRNOTAVAIL;
+ break;
}
- dev_put(dev);
return ret;
}
@@ -296,7 +435,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
struct rdma_dev_addr *addr, void *context),
void *context)
{
- struct sockaddr_in *src_in, *dst_in;
+ struct sockaddr *src_in, *dst_in;
struct addr_req *req;
int ret = 0;
@@ -313,8 +452,8 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
req->client = client;
atomic_inc(&client->refcount);
- src_in = (struct sockaddr_in *) &req->src_addr;
- dst_in = (struct sockaddr_in *) &req->dst_addr;
+ src_in = (struct sockaddr *) &req->src_addr;
+ dst_in = (struct sockaddr *) &req->dst_addr;
req->status = addr_resolve_local(src_in, dst_in, addr);
if (req->status == -EADDRNOTAVAIL)
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index d951896ff7fc..2a2e50871b40 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -42,6 +42,7 @@
#include <linux/inetdevice.h>
#include <net/tcp.h>
+#include <net/ipv6.h>
#include <rdma/rdma_cm.h>
#include <rdma/rdma_cm_ib.h>
@@ -636,7 +637,12 @@ static inline int cma_zero_addr(struct sockaddr *addr)
static inline int cma_loopback_addr(struct sockaddr *addr)
{
- return ipv4_is_loopback(((struct sockaddr_in *) addr)->sin_addr.s_addr);
+ if (addr->sa_family == AF_INET)
+ return ipv4_is_loopback(
+ ((struct sockaddr_in *) addr)->sin_addr.s_addr);
+ else
+ return ipv6_addr_loopback(
+ &((struct sockaddr_in6 *) addr)->sin6_addr);
}
static inline int cma_any_addr(struct sockaddr *addr)
@@ -1467,10 +1473,10 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv)
static int cma_bind_any(struct rdma_cm_id *id, sa_family_t af)
{
- struct sockaddr_in addr_in;
+ struct sockaddr_storage addr_in;
memset(&addr_in, 0, sizeof addr_in);
- addr_in.sin_family = af;
+ addr_in.ss_family = af;
return rdma_bind_addr(id, (struct sockaddr *) &addr_in);
}
@@ -2073,7 +2079,7 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
struct rdma_id_private *id_priv;
int ret;
- if (addr->sa_family != AF_INET)
+ if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
return -EAFNOSUPPORT;
id_priv = container_of(id, struct rdma_id_private, id);
@@ -2113,31 +2119,59 @@ EXPORT_SYMBOL(rdma_bind_addr);
static int cma_format_hdr(void *hdr, enum rdma_port_space ps,
struct rdma_route *route)
{
- struct sockaddr_in *src4, *dst4;
struct cma_hdr *cma_hdr;
struct sdp_hh *sdp_hdr;
- src4 = (struct sockaddr_in *) &route->addr.src_addr;
- dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
-
- switch (ps) {
- case RDMA_PS_SDP:
- sdp_hdr = hdr;
- if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
- return -EINVAL;
- sdp_set_ip_ver(sdp_hdr, 4);
- sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
- sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
- sdp_hdr->port = src4->sin_port;
- break;
- default:
- cma_hdr = hdr;
- cma_hdr->cma_version = CMA_VERSION;
- cma_set_ip_ver(cma_hdr, 4);
- cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
- cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
- cma_hdr->port = src4->sin_port;
- break;
+ if (route->addr.src_addr.ss_family == AF_INET) {
+ struct sockaddr_in *src4, *dst4;
+
+ src4 = (struct sockaddr_in *) &route->addr.src_addr;
+ dst4 = (struct sockaddr_in *) &route->addr.dst_addr;
+
+ switch (ps) {
+ case RDMA_PS_SDP:
+ sdp_hdr = hdr;
+ if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
+ return -EINVAL;
+ sdp_set_ip_ver(sdp_hdr, 4);
+ sdp_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+ sdp_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+ sdp_hdr->port = src4->sin_port;
+ break;
+ default:
+ cma_hdr = hdr;
+ cma_hdr->cma_version = CMA_VERSION;
+ cma_set_ip_ver(cma_hdr, 4);
+ cma_hdr->src_addr.ip4.addr = src4->sin_addr.s_addr;
+ cma_hdr->dst_addr.ip4.addr = dst4->sin_addr.s_addr;
+ cma_hdr->port = src4->sin_port;
+ break;
+ }
+ } else {
+ struct sockaddr_in6 *src6, *dst6;
+
+ src6 = (struct sockaddr_in6 *) &route->addr.src_addr;
+ dst6 = (struct sockaddr_in6 *) &route->addr.dst_addr;
+
+ switch (ps) {
+ case RDMA_PS_SDP:
+ sdp_hdr = hdr;
+ if (sdp_get_majv(sdp_hdr->sdp_version) != SDP_MAJ_VERSION)
+ return -EINVAL;
+ sdp_set_ip_ver(sdp_hdr, 6);
+ sdp_hdr->src_addr.ip6 = src6->sin6_addr;
+ sdp_hdr->dst_addr.ip6 = dst6->sin6_addr;
+ sdp_hdr->port = src6->sin6_port;
+ break;
+ default:
+ cma_hdr = hdr;
+ cma_hdr->cma_version = CMA_VERSION;
+ cma_set_ip_ver(cma_hdr, 6);
+ cma_hdr->src_addr.ip6 = src6->sin6_addr;
+ cma_hdr->dst_addr.ip6 = dst6->sin6_addr;
+ cma_hdr->port = src6->sin6_port;
+ break;
+ }
}
return 0;
}
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 4d1042115598..4f4d1bb9f069 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -262,15 +262,7 @@ static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr,
if (ret)
return ret;
- return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
- be16_to_cpu(((__be16 *) gid.raw)[0]),
- be16_to_cpu(((__be16 *) gid.raw)[1]),
- be16_to_cpu(((__be16 *) gid.raw)[2]),
- be16_to_cpu(((__be16 *) gid.raw)[3]),
- be16_to_cpu(((__be16 *) gid.raw)[4]),
- be16_to_cpu(((__be16 *) gid.raw)[5]),
- be16_to_cpu(((__be16 *) gid.raw)[6]),
- be16_to_cpu(((__be16 *) gid.raw)[7]));
+ return sprintf(buf, "%pI6\n", gid.raw);
}
static ssize_t show_port_pkey(struct ib_port *p, struct port_attribute *attr,
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index 69580e282af0..5119d6508181 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -653,7 +653,7 @@ static int c2_service_destroy(struct iw_cm_id *cm_id)
static int c2_pseudo_up(struct net_device *netdev)
{
struct in_device *ind;
- struct c2_dev *c2dev = netdev->priv;
+ struct c2_dev *c2dev = netdev->ml_priv;
ind = in_dev_get(netdev);
if (!ind)
@@ -678,7 +678,7 @@ static int c2_pseudo_up(struct net_device *netdev)
static int c2_pseudo_down(struct net_device *netdev)
{
struct in_device *ind;
- struct c2_dev *c2dev = netdev->priv;
+ struct c2_dev *c2dev = netdev->ml_priv;
ind = in_dev_get(netdev);
if (!ind)
@@ -746,14 +746,14 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
/* change ethxxx to iwxxx */
strcpy(name, "iw");
strcat(name, &c2dev->netdev->name[3]);
- netdev = alloc_netdev(sizeof(*netdev), name, setup);
+ netdev = alloc_netdev(0, name, setup);
if (!netdev) {
printk(KERN_ERR PFX "%s - etherdev alloc failed",
__func__);
return NULL;
}
- netdev->priv = c2dev;
+ netdev->ml_priv = c2dev;
SET_NETDEV_DEV(netdev, &c2dev->pcidev->dev);
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 7fc35cf0cddf..c825142a2fb7 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -175,6 +175,13 @@ struct ehca_queue_map {
unsigned int next_wqe_idx; /* Idx to first wqe to be flushed */
};
+/* function to calculate the next index for the qmap */
+static inline unsigned int next_index(unsigned int cur_index, unsigned int limit)
+{
+ unsigned int temp = cur_index + 1;
+ return (temp == limit) ? 0 : temp;
+}
+
struct ehca_qp {
union {
struct ib_qp ib_qp;
diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c
index 49660dfa1867..523e733c630e 100644
--- a/drivers/infiniband/hw/ehca/ehca_eq.c
+++ b/drivers/infiniband/hw/ehca/ehca_eq.c
@@ -113,7 +113,7 @@ int ehca_create_eq(struct ehca_shca *shca,
if (h_ret != H_SUCCESS || vpage)
goto create_eq_exit2;
} else {
- if (h_ret != H_PAGE_REGISTERED || !vpage)
+ if (h_ret != H_PAGE_REGISTERED)
goto create_eq_exit2;
}
}
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 757035ea246f..3128a5090dbd 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -659,12 +659,12 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
WARN_ON_ONCE(!in_interrupt());
if (ehca_debug_level >= 3)
- ehca_dmp(&cpu_online_map, sizeof(cpumask_t), "");
+ ehca_dmp(cpu_online_mask, cpumask_size(), "");
spin_lock_irqsave(&pool->last_cpu_lock, flags);
- cpu = next_cpu_nr(pool->last_cpu, cpu_online_map);
+ cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
if (cpu >= nr_cpu_ids)
- cpu = first_cpu(cpu_online_map);
+ cpu = cpumask_first(cpu_online_mask);
pool->last_cpu = cpu;
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
@@ -855,7 +855,7 @@ static int __cpuinit comp_pool_callback(struct notifier_block *nfb,
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, any_online_cpu(cpu_online_map));
+ kthread_bind(cct->task, cpumask_any(cpu_online_mask));
destroy_comp_task(pool, cpu);
break;
case CPU_ONLINE:
@@ -902,7 +902,7 @@ int ehca_create_comp_pool(void)
return -ENOMEM;
spin_lock_init(&pool->last_cpu_lock);
- pool->last_cpu = any_online_cpu(cpu_online_map);
+ 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) {
@@ -934,10 +934,9 @@ void ehca_destroy_comp_pool(void)
unregister_hotcpu_notifier(&comp_pool_callback_nb);
- for (i = 0; i < NR_CPUS; i++) {
- if (cpu_online(i))
- destroy_comp_task(pool, i);
- }
+ for_each_online_cpu(i)
+ destroy_comp_task(pool, i);
+
free_percpu(pool->cpu_comp_tasks);
kfree(pool);
}
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index bec7e0249358..3b77b674cbf6 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -717,6 +717,7 @@ static int __devinit ehca_probe(struct of_device *dev,
const u64 *handle;
struct ib_pd *ibpd;
int ret, i, eq_size;
+ unsigned long flags;
handle = of_get_property(dev->node, "ibm,hca-handle", NULL);
if (!handle) {
@@ -830,9 +831,9 @@ static int __devinit ehca_probe(struct of_device *dev,
ehca_err(&shca->ib_device,
"Cannot create device attributes ret=%d", ret);
- spin_lock(&shca_list_lock);
+ spin_lock_irqsave(&shca_list_lock, flags);
list_add(&shca->shca_list, &shca_list);
- spin_unlock(&shca_list_lock);
+ spin_unlock_irqrestore(&shca_list_lock, flags);
return 0;
@@ -878,6 +879,7 @@ probe1:
static int __devexit ehca_remove(struct of_device *dev)
{
struct ehca_shca *shca = dev->dev.driver_data;
+ unsigned long flags;
int ret;
sysfs_remove_group(&dev->dev.kobj, &ehca_dev_attr_grp);
@@ -915,9 +917,9 @@ static int __devexit ehca_remove(struct of_device *dev)
ib_dealloc_device(&shca->ib_device);
- spin_lock(&shca_list_lock);
+ spin_lock_irqsave(&shca_list_lock, flags);
list_del(&shca->shca_list);
- spin_unlock(&shca_list_lock);
+ spin_unlock_irqrestore(&shca_list_lock, flags);
return ret;
}
@@ -975,6 +977,7 @@ static int ehca_mem_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
static unsigned long ehca_dmem_warn_time;
+ unsigned long flags;
switch (action) {
case MEM_CANCEL_OFFLINE:
@@ -985,12 +988,12 @@ static int ehca_mem_notifier(struct notifier_block *nb,
case MEM_GOING_ONLINE:
case MEM_GOING_OFFLINE:
/* only ok if no hca is attached to the lpar */
- spin_lock(&shca_list_lock);
+ spin_lock_irqsave(&shca_list_lock, flags);
if (list_empty(&shca_list)) {
- spin_unlock(&shca_list_lock);
+ spin_unlock_irqrestore(&shca_list_lock, flags);
return NOTIFY_OK;
} else {
- spin_unlock(&shca_list_lock);
+ spin_unlock_irqrestore(&shca_list_lock, flags);
if (printk_timed_ratelimit(&ehca_dmem_warn_time,
30 * 1000))
ehca_gen_err("DMEM operations are not allowed"
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index cadbf0cdd910..f161cf173dbe 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -1138,14 +1138,14 @@ static int calc_left_cqes(u64 wqe_p, struct ipz_queue *ipz_queue,
return -EFAULT;
}
- tail_idx = (qmap->tail + 1) % qmap->entries;
+ tail_idx = next_index(qmap->tail, qmap->entries);
wqe_idx = q_ofs / ipz_queue->qe_size;
/* check all processed wqes, whether a cqe is requested or not */
while (tail_idx != wqe_idx) {
if (qmap->map[tail_idx].cqe_req)
qmap->left_to_poll++;
- tail_idx = (tail_idx + 1) % qmap->entries;
+ tail_idx = next_index(tail_idx, qmap->entries);
}
/* save index in queue, where we have to start flushing */
qmap->next_wqe_idx = wqe_idx;
@@ -1195,14 +1195,14 @@ static int check_for_left_cqes(struct ehca_qp *my_qp, struct ehca_shca *shca)
} else {
spin_lock_irqsave(&my_qp->send_cq->spinlock, flags);
my_qp->sq_map.left_to_poll = 0;
- my_qp->sq_map.next_wqe_idx = (my_qp->sq_map.tail + 1) %
- my_qp->sq_map.entries;
+ my_qp->sq_map.next_wqe_idx = next_index(my_qp->sq_map.tail,
+ my_qp->sq_map.entries);
spin_unlock_irqrestore(&my_qp->send_cq->spinlock, flags);
spin_lock_irqsave(&my_qp->recv_cq->spinlock, flags);
my_qp->rq_map.left_to_poll = 0;
- my_qp->rq_map.next_wqe_idx = (my_qp->rq_map.tail + 1) %
- my_qp->rq_map.entries;
+ my_qp->rq_map.next_wqe_idx = next_index(my_qp->rq_map.tail,
+ my_qp->rq_map.entries);
spin_unlock_irqrestore(&my_qp->recv_cq->spinlock, flags);
}
diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c
index 00a648f4316c..c7112686782f 100644
--- a/drivers/infiniband/hw/ehca/ehca_reqs.c
+++ b/drivers/infiniband/hw/ehca/ehca_reqs.c
@@ -726,13 +726,13 @@ repoll:
* set left_to_poll to 0 because in error state, we will not
* get any additional CQEs
*/
- my_qp->sq_map.next_wqe_idx = (my_qp->sq_map.tail + 1) %
- my_qp->sq_map.entries;
+ my_qp->sq_map.next_wqe_idx = next_index(my_qp->sq_map.tail,
+ my_qp->sq_map.entries);
my_qp->sq_map.left_to_poll = 0;
ehca_add_to_err_list(my_qp, 1);
- my_qp->rq_map.next_wqe_idx = (my_qp->rq_map.tail + 1) %
- my_qp->rq_map.entries;
+ my_qp->rq_map.next_wqe_idx = next_index(my_qp->rq_map.tail,
+ my_qp->rq_map.entries);
my_qp->rq_map.left_to_poll = 0;
if (HAS_RQ(my_qp))
ehca_add_to_err_list(my_qp, 0);
@@ -860,9 +860,8 @@ static int generate_flush_cqes(struct ehca_qp *my_qp, struct ib_cq *cq,
/* mark as reported and advance next_wqe pointer */
qmap_entry->reported = 1;
- qmap->next_wqe_idx++;
- if (qmap->next_wqe_idx == qmap->entries)
- qmap->next_wqe_idx = 0;
+ qmap->next_wqe_idx = next_index(qmap->next_wqe_idx,
+ qmap->entries);
qmap_entry = &qmap->map[qmap->next_wqe_idx];
wc++; nr++;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index ad0aab60b051..69c0ce321b4e 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -661,6 +661,8 @@ bail:
static void __devexit cleanup_device(struct ipath_devdata *dd)
{
int port;
+ struct ipath_portdata **tmp;
+ unsigned long flags;
if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
/* can't do anything more with chip; needs re-init */
@@ -742,20 +744,21 @@ static void __devexit cleanup_device(struct ipath_devdata *dd)
/*
* free any resources still in use (usually just kernel ports)
- * at unload; we do for portcnt, not cfgports, because cfgports
- * could have changed while we were loaded.
+ * at unload; we do for portcnt, because that's what we allocate.
+ * We acquire lock to be really paranoid that ipath_pd isn't being
+ * accessed from some interrupt-related code (that should not happen,
+ * but best to be sure).
*/
+ spin_lock_irqsave(&dd->ipath_uctxt_lock, flags);
+ tmp = dd->ipath_pd;
+ dd->ipath_pd = NULL;
+ spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags);
for (port = 0; port < dd->ipath_portcnt; port++) {
- struct ipath_portdata *pd = dd->ipath_pd[port];
- dd->ipath_pd[port] = NULL;
+ struct ipath_portdata *pd = tmp[port];
+ tmp[port] = NULL; /* debugging paranoia */
ipath_free_pddata(dd, pd);
}
- kfree(dd->ipath_pd);
- /*
- * debuggability, in case some cleanup path tries to use it
- * after this
- */
- dd->ipath_pd = NULL;
+ kfree(tmp);
}
static void __devexit ipath_remove_one(struct pci_dev *pdev)
@@ -2586,6 +2589,7 @@ int ipath_reset_device(int unit)
{
int ret, i;
struct ipath_devdata *dd = ipath_lookup(unit);
+ unsigned long flags;
if (!dd) {
ret = -ENODEV;
@@ -2611,18 +2615,21 @@ int ipath_reset_device(int unit)
goto bail;
}
+ spin_lock_irqsave(&dd->ipath_uctxt_lock, flags);
if (dd->ipath_pd)
for (i = 1; i < dd->ipath_cfgports; i++) {
- if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) {
- ipath_dbg("unit %u port %d is in use "
- "(PID %u cmd %s), can't reset\n",
- unit, i,
- pid_nr(dd->ipath_pd[i]->port_pid),
- dd->ipath_pd[i]->port_comm);
- ret = -EBUSY;
- goto bail;
- }
+ if (!dd->ipath_pd[i] || !dd->ipath_pd[i]->port_cnt)
+ continue;
+ spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags);
+ ipath_dbg("unit %u port %d is in use "
+ "(PID %u cmd %s), can't reset\n",
+ unit, i,
+ pid_nr(dd->ipath_pd[i]->port_pid),
+ dd->ipath_pd[i]->port_comm);
+ ret = -EBUSY;
+ goto bail;
}
+ spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags);
if (dd->ipath_flags & IPATH_HAS_SEND_DMA)
teardown_sdma(dd);
@@ -2656,9 +2663,12 @@ static int ipath_signal_procs(struct ipath_devdata *dd, int sig)
{
int i, sub, any = 0;
struct pid *pid;
+ unsigned long flags;
if (!dd->ipath_pd)
return 0;
+
+ spin_lock_irqsave(&dd->ipath_uctxt_lock, flags);
for (i = 1; i < dd->ipath_cfgports; i++) {
if (!dd->ipath_pd[i] || !dd->ipath_pd[i]->port_cnt)
continue;
@@ -2682,6 +2692,7 @@ static int ipath_signal_procs(struct ipath_devdata *dd, int sig)
any++;
}
}
+ spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags);
return any;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 1af1f3a907c6..23173982b32c 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -223,8 +223,13 @@ static int ipath_get_base_info(struct file *fp,
(unsigned long long) kinfo->spi_subport_rcvhdr_base);
}
- kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
- dd->ipath_palign;
+ /*
+ * All user buffers are 2KB buffers. If we ever support
+ * giving 4KB buffers to user processes, this will need some
+ * work.
+ */
+ kinfo->spi_pioindex = (kinfo->spi_piobufbase -
+ (dd->ipath_piobufbase & 0xffffffff)) / dd->ipath_palign;
kinfo->spi_pioalign = dd->ipath_palign;
kinfo->spi_qpair = IPATH_KD_QP;
@@ -1674,7 +1679,7 @@ static int find_best_unit(struct file *fp,
* InfiniPath chip to that processor (we assume reasonable connectivity,
* for now). This code assumes that if affinity has been set
* before this point, that at most one cpu is set; for now this
- * is reasonable. I check for both cpus_empty() and cpus_full(),
+ * is reasonable. I check for both cpumask_empty() and cpumask_full(),
* in case some kernel variant sets none of the bits when no
* affinity is set. 2.6.11 and 12 kernels have all present
* cpus set. Some day we'll have to fix it up further to handle
@@ -1683,11 +1688,11 @@ static int find_best_unit(struct file *fp,
* information. There may be some issues with dual core numbering
* as well. This needs more work prior to release.
*/
- if (!cpus_empty(current->cpus_allowed) &&
- !cpus_full(current->cpus_allowed)) {
+ if (!cpumask_empty(&current->cpus_allowed) &&
+ !cpumask_full(&current->cpus_allowed)) {
int ncpus = num_online_cpus(), curcpu = -1, nset = 0;
for (i = 0; i < ncpus; i++)
- if (cpu_isset(i, current->cpus_allowed)) {
+ if (cpumask_test_cpu(i, &current->cpus_allowed)) {
ipath_cdbg(PROC, "%s[%u] affinity set for "
"cpu %d/%d\n", current->comm,
current->pid, i, ncpus);
@@ -2041,7 +2046,9 @@ static int ipath_close(struct inode *in, struct file *fp)
struct ipath_filedata *fd;
struct ipath_portdata *pd;
struct ipath_devdata *dd;
+ unsigned long flags;
unsigned port;
+ struct pid *pid;
ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n",
(long)in->i_rdev, fp->private_data);
@@ -2074,14 +2081,13 @@ static int ipath_close(struct inode *in, struct file *fp)
mutex_unlock(&ipath_mutex);
goto bail;
}
+ /* early; no interrupt users after this */
+ spin_lock_irqsave(&dd->ipath_uctxt_lock, flags);
port = pd->port_port;
-
- if (pd->port_hdrqfull) {
- ipath_cdbg(PROC, "%s[%u] had %u rcvhdrqfull errors "
- "during run\n", pd->port_comm, pid_nr(pd->port_pid),
- pd->port_hdrqfull);
- pd->port_hdrqfull = 0;
- }
+ dd->ipath_pd[port] = NULL;
+ pid = pd->port_pid;
+ pd->port_pid = NULL;
+ spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags);
if (pd->port_rcvwait_to || pd->port_piowait_to
|| pd->port_rcvnowait || pd->port_pionowait) {
@@ -2138,13 +2144,11 @@ static int ipath_close(struct inode *in, struct file *fp)
unlock_expected_tids(pd);
ipath_stats.sps_ports--;
ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
- pd->port_comm, pid_nr(pd->port_pid),
+ pd->port_comm, pid_nr(pid),
dd->ipath_unit, port);
}
- put_pid(pd->port_pid);
- pd->port_pid = NULL;
- dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
+ put_pid(pid);
mutex_unlock(&ipath_mutex);
ipath_free_pddata(dd, pd); /* after releasing the mutex */
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
index 8bb5170b4e41..53912c327bfe 100644
--- a/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -86,7 +86,7 @@ static int create_file(const char *name, mode_t mode,
*dentry = NULL;
mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name));
- if (!IS_ERR(dentry))
+ if (!IS_ERR(*dentry))
error = ipathfs_mknod(parent->d_inode, *dentry,
mode, fops, data);
else
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index 421cc2af891f..fbf8c5379ea8 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -721,6 +721,12 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_HWE_SERDESPLLFAILED);
}
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_iblinkerrrecovcnt);
+
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1);
@@ -810,6 +816,36 @@ static void ipath_pe_quiet_serdes(struct ipath_devdata *dd)
{
u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
+ if (dd->ibsymdelta || dd->iblnkerrdelta ||
+ dd->ibdeltainprog) {
+ u64 diagc;
+ /* enable counter writes */
+ diagc = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwdiagctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl,
+ diagc | INFINIPATH_DC_COUNTERWREN);
+
+ if (dd->ibsymdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->ibsymsnap;
+ val -= dd->ibsymdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt, val);
+ }
+ if (dd->iblnkerrdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->iblnkerrsnap;
+ val -= dd->iblnkerrdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt, val);
+ }
+
+ /* and disable counter writes */
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl, diagc);
+ }
val |= INFINIPATH_SERDC0_TXIDLE;
ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n",
(unsigned long long) val);
@@ -1749,6 +1785,31 @@ static void ipath_pe_config_jint(struct ipath_devdata *dd, u16 a, u16 b)
static int ipath_pe_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
{
+ if (ibup) {
+ if (dd->ibdeltainprog) {
+ dd->ibdeltainprog = 0;
+ dd->ibsymdelta +=
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt) -
+ dd->ibsymsnap;
+ dd->iblnkerrdelta +=
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt) -
+ dd->iblnkerrsnap;
+ }
+ } else {
+ dd->ipath_lli_counter = 0;
+ if (!dd->ibdeltainprog) {
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap =
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap =
+ ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ }
+ }
+
ipath_setup_pe_setextled(dd, ipath_ib_linkstate(dd, ibcs),
ipath_ib_linktrstate(dd, ibcs));
return 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_iba7220.c b/drivers/infiniband/hw/ipath/ipath_iba7220.c
index 9839e20119bc..b2a9d4c155d1 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba7220.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba7220.c
@@ -951,6 +951,12 @@ static int ipath_7220_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_HWE_SERDESPLLFAILED);
}
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap =
+ ipath_read_creg32(dd, dd->ipath_cregs->cr_iblinkerrrecovcnt);
+
if (!dd->ipath_ibcddrctrl) {
/* not on re-init after reset */
dd->ipath_ibcddrctrl =
@@ -1084,6 +1090,37 @@ static void ipath_7220_config_jint(struct ipath_devdata *dd,
static void ipath_7220_quiet_serdes(struct ipath_devdata *dd)
{
u64 val;
+ if (dd->ibsymdelta || dd->iblnkerrdelta ||
+ dd->ibdeltainprog) {
+ u64 diagc;
+ /* enable counter writes */
+ diagc = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwdiagctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl,
+ diagc | INFINIPATH_DC_COUNTERWREN);
+
+ if (dd->ibsymdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->ibsymsnap;
+ val -= dd->ibsymdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt, val);
+ }
+ if (dd->iblnkerrdelta || dd->ibdeltainprog) {
+ val = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ if (dd->ibdeltainprog)
+ val -= val - dd->iblnkerrsnap;
+ val -= dd->iblnkerrdelta;
+ ipath_write_creg(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt, val);
+ }
+
+ /* and disable counter writes */
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwdiagctrl, diagc);
+ }
+
dd->ipath_flags &= ~IPATH_IB_AUTONEG_INPROG;
wake_up(&dd->ipath_autoneg_wait);
cancel_delayed_work(&dd->ipath_autoneg_work);
@@ -2325,7 +2362,7 @@ static void try_auto_neg(struct ipath_devdata *dd)
static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
{
- int ret = 0;
+ int ret = 0, symadj = 0;
u32 ltstate = ipath_ib_linkstate(dd, ibcs);
dd->ipath_link_width_active =
@@ -2368,6 +2405,13 @@ static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
ipath_dbg("DDR negotiation try, %u/%u\n",
dd->ipath_autoneg_tries,
IPATH_AUTONEG_TRIES);
+ if (!dd->ibdeltainprog) {
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ }
try_auto_neg(dd);
ret = 1; /* no other IB status change processing */
} else if ((dd->ipath_flags & IPATH_IB_AUTONEG_INPROG)
@@ -2388,6 +2432,7 @@ static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
set_speed_fast(dd,
dd->ipath_link_speed_enabled);
wake_up(&dd->ipath_autoneg_wait);
+ symadj = 1;
} else if (dd->ipath_flags & IPATH_IB_AUTONEG_FAILED) {
/*
* clear autoneg failure flag, and do setup
@@ -2403,22 +2448,28 @@ static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
IBA7220_IBC_IBTA_1_2_MASK;
ipath_write_kreg(dd,
IPATH_KREG_OFFSET(IBNCModeCtrl), 0);
+ symadj = 1;
}
}
/*
- * if we are in 1X, and are in autoneg width, it
- * could be due to an xgxs problem, so if we haven't
+ * if we are in 1X on rev1 only, and are in autoneg width,
+ * it could be due to an xgxs problem, so if we haven't
* already tried, try twice to get to 4X; if we
* tried, and couldn't, report it, since it will
* probably not be what is desired.
*/
- if ((dd->ipath_link_width_enabled & (IB_WIDTH_1X |
+ if (dd->ipath_minrev == 1 &&
+ (dd->ipath_link_width_enabled & (IB_WIDTH_1X |
IB_WIDTH_4X)) == (IB_WIDTH_1X | IB_WIDTH_4X)
&& dd->ipath_link_width_active == IB_WIDTH_1X
&& dd->ipath_x1_fix_tries < 3) {
- if (++dd->ipath_x1_fix_tries == 3)
+ if (++dd->ipath_x1_fix_tries == 3) {
dev_info(&dd->pcidev->dev,
"IB link is in 1X mode\n");
+ if (!(dd->ipath_flags &
+ IPATH_IB_AUTONEG_INPROG))
+ symadj = 1;
+ }
else {
ipath_cdbg(VERBOSE, "IB 1X in "
"auto-width, try %u to be "
@@ -2429,7 +2480,8 @@ static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
dd->ipath_f_xgxs_reset(dd);
ret = 1; /* skip other processing */
}
- }
+ } else if (!(dd->ipath_flags & IPATH_IB_AUTONEG_INPROG))
+ symadj = 1;
if (!ret) {
dd->delay_mult = rate_to_delay
@@ -2440,6 +2492,25 @@ static int ipath_7220_ib_updown(struct ipath_devdata *dd, int ibup, u64 ibcs)
}
}
+ if (symadj) {
+ if (dd->ibdeltainprog) {
+ dd->ibdeltainprog = 0;
+ dd->ibsymdelta += ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt) -
+ dd->ibsymsnap;
+ dd->iblnkerrdelta += ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt) -
+ dd->iblnkerrsnap;
+ }
+ } else if (!ibup && !dd->ibdeltainprog
+ && !(dd->ipath_flags & IPATH_IB_AUTONEG_INPROG)) {
+ dd->ibdeltainprog = 1;
+ dd->ibsymsnap = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_ibsymbolerrcnt);
+ dd->iblnkerrsnap = ipath_read_creg32(dd,
+ dd->ipath_cregs->cr_iblinkerrrecovcnt);
+ }
+
if (!ret)
ipath_setup_7220_setextled(dd, ipath_ib_linkstate(dd, ibcs),
ltstate);
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 3e5baa43fc82..64aeefbd2a5d 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -229,6 +229,7 @@ static int init_chip_first(struct ipath_devdata *dd)
spin_lock_init(&dd->ipath_kernel_tid_lock);
spin_lock_init(&dd->ipath_user_tid_lock);
spin_lock_init(&dd->ipath_sendctrl_lock);
+ spin_lock_init(&dd->ipath_uctxt_lock);
spin_lock_init(&dd->ipath_sdma_lock);
spin_lock_init(&dd->ipath_gpio_lock);
spin_lock_init(&dd->ipath_eep_st_lock);
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index 0bd8bcb184a1..6ba4861dd6ac 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -355,6 +355,19 @@ struct ipath_devdata {
/* errors masked because they occur too fast */
ipath_err_t ipath_maskederrs;
u64 ipath_lastlinkrecov; /* link recoveries at last ACTIVE */
+ /* these 5 fields are used to establish deltas for IB Symbol
+ * errors and linkrecovery errors. They can be reported on
+ * some chips during link negotiation prior to INIT, and with
+ * DDR when faking DDR negotiations with non-IBTA switches.
+ * The chip counters are adjusted at driver unload if there is
+ * a non-zero delta.
+ */
+ u64 ibdeltainprog;
+ u64 ibsymdelta;
+ u64 ibsymsnap;
+ u64 iblnkerrdelta;
+ u64 iblnkerrsnap;
+
/* time in jiffies at which to re-enable maskederrs */
unsigned long ipath_unmasktime;
/* count of egrfull errors, combined for all ports */
@@ -464,6 +477,8 @@ struct ipath_devdata {
spinlock_t ipath_kernel_tid_lock;
spinlock_t ipath_user_tid_lock;
spinlock_t ipath_sendctrl_lock;
+ /* around ipath_pd and (user ports) port_cnt use (intr vs free) */
+ spinlock_t ipath_uctxt_lock;
/*
* IPATH_STATUS_*,
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index 8f32b17a5eed..c0e933fec218 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -132,6 +132,7 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
* (see ipath_get_dma_mr and ipath_dma.c).
*/
if (sge->lkey == 0) {
+ /* always a kernel port, no locking needed */
struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
if (pd->user) {
@@ -211,6 +212,7 @@ int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
* (see ipath_get_dma_mr and ipath_dma.c).
*/
if (rkey == 0) {
+ /* always a kernel port, no locking needed */
struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
if (pd->user) {
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
index be4fc9ada8e7..17a123197477 100644
--- a/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -348,6 +348,7 @@ bail:
*/
static int get_pkeys(struct ipath_devdata *dd, u16 * pkeys)
{
+ /* always a kernel port, no locking needed */
struct ipath_portdata *pd = dd->ipath_pd[0];
memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys));
@@ -730,6 +731,7 @@ static int set_pkeys(struct ipath_devdata *dd, u16 *pkeys)
int i;
int changed = 0;
+ /* always a kernel port, no locking needed */
pd = dd->ipath_pd[0];
for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) {
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
index 4715911101e4..3a5a89b609c4 100644
--- a/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -745,6 +745,7 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
struct ipath_swqe *swq = NULL;
struct ipath_ibdev *dev;
size_t sz;
+ size_t sg_list_sz;
struct ib_qp *ret;
if (init_attr->create_flags) {
@@ -789,19 +790,31 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
goto bail;
}
sz = sizeof(*qp);
+ sg_list_sz = 0;
if (init_attr->srq) {
struct ipath_srq *srq = to_isrq(init_attr->srq);
- sz += sizeof(*qp->r_sg_list) *
- srq->rq.max_sge;
- } else
- sz += sizeof(*qp->r_sg_list) *
- init_attr->cap.max_recv_sge;
- qp = kmalloc(sz, GFP_KERNEL);
+ if (srq->rq.max_sge > 1)
+ sg_list_sz = sizeof(*qp->r_sg_list) *
+ (srq->rq.max_sge - 1);
+ } else if (init_attr->cap.max_recv_sge > 1)
+ sg_list_sz = sizeof(*qp->r_sg_list) *
+ (init_attr->cap.max_recv_sge - 1);
+ qp = kmalloc(sz + sg_list_sz, GFP_KERNEL);
if (!qp) {
ret = ERR_PTR(-ENOMEM);
goto bail_swq;
}
+ if (sg_list_sz && (init_attr->qp_type == IB_QPT_UD ||
+ init_attr->qp_type == IB_QPT_SMI ||
+ init_attr->qp_type == IB_QPT_GSI)) {
+ qp->r_ud_sg_list = kmalloc(sg_list_sz, GFP_KERNEL);
+ if (!qp->r_ud_sg_list) {
+ ret = ERR_PTR(-ENOMEM);
+ goto bail_qp;
+ }
+ } else
+ qp->r_ud_sg_list = NULL;
if (init_attr->srq) {
sz = 0;
qp->r_rq.size = 0;
@@ -818,7 +831,7 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
qp->r_rq.size * sz);
if (!qp->r_rq.wq) {
ret = ERR_PTR(-ENOMEM);
- goto bail_qp;
+ goto bail_sg_list;
}
}
@@ -848,7 +861,7 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
if (err) {
ret = ERR_PTR(err);
vfree(qp->r_rq.wq);
- goto bail_qp;
+ goto bail_sg_list;
}
qp->ip = NULL;
qp->s_tx = NULL;
@@ -925,6 +938,8 @@ bail_ip:
vfree(qp->r_rq.wq);
ipath_free_qp(&dev->qp_table, qp);
free_qpn(&dev->qp_table, qp->ibqp.qp_num);
+bail_sg_list:
+ kfree(qp->r_ud_sg_list);
bail_qp:
kfree(qp);
bail_swq:
@@ -989,6 +1004,7 @@ int ipath_destroy_qp(struct ib_qp *ibqp)
kref_put(&qp->ip->ref, ipath_release_mmap_info);
else
vfree(qp->r_rq.wq);
+ kfree(qp->r_ud_sg_list);
vfree(qp->s_wq);
kfree(qp);
return 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index 7b93cda1a4bd..9170710b950d 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -573,9 +573,8 @@ int ipath_make_rc_req(struct ipath_qp *qp)
ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len);
qp->s_state = OP(RDMA_READ_REQUEST);
hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
- bth2 = qp->s_psn++ & IPATH_PSN_MASK;
- if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
- qp->s_next_psn = qp->s_psn;
+ bth2 = qp->s_psn & IPATH_PSN_MASK;
+ qp->s_psn = wqe->lpsn + 1;
ss = NULL;
len = 0;
qp->s_cur++;
diff --git a/drivers/infiniband/hw/ipath/ipath_sdma.c b/drivers/infiniband/hw/ipath/ipath_sdma.c
index 284c9bca517e..8e255adf5d9b 100644
--- a/drivers/infiniband/hw/ipath/ipath_sdma.c
+++ b/drivers/infiniband/hw/ipath/ipath_sdma.c
@@ -698,10 +698,8 @@ retry:
addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr,
tx->map_len, DMA_TO_DEVICE);
- if (dma_mapping_error(&dd->pcidev->dev, addr)) {
- ret = -EIO;
- goto unlock;
- }
+ if (dma_mapping_error(&dd->pcidev->dev, addr))
+ goto ioerr;
dwoffset = tx->map_len >> 2;
make_sdma_desc(dd, sdmadesc, (u64) addr, dwoffset, 0);
@@ -741,6 +739,8 @@ retry:
dw = (len + 3) >> 2;
addr = dma_map_single(&dd->pcidev->dev, sge->vaddr, dw << 2,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&dd->pcidev->dev, addr))
+ goto unmap;
make_sdma_desc(dd, sdmadesc, (u64) addr, dw, dwoffset);
/* SDmaUseLargeBuf has to be set in every descriptor */
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_USELARGEBUF)
@@ -798,7 +798,18 @@ retry:
list_add_tail(&tx->txreq.list, &dd->ipath_sdma_activelist);
if (tx->txreq.flags & IPATH_SDMA_TXREQ_F_VL15)
vl15_watchdog_enq(dd);
-
+ goto unlock;
+
+unmap:
+ while (tail != dd->ipath_sdma_descq_tail) {
+ if (!tail)
+ tail = dd->ipath_sdma_descq_cnt - 1;
+ else
+ tail--;
+ unmap_desc(dd, tail);
+ }
+ioerr:
+ ret = -EIO;
unlock:
spin_unlock_irqrestore(&dd->ipath_sdma_lock, flags);
fail:
diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c
index c8e3d65f0de8..f63e143e3292 100644
--- a/drivers/infiniband/hw/ipath/ipath_stats.c
+++ b/drivers/infiniband/hw/ipath/ipath_stats.c
@@ -112,6 +112,14 @@ u64 ipath_snap_cntr(struct ipath_devdata *dd, ipath_creg creg)
dd->ipath_lastrpkts = val;
}
val64 = dd->ipath_rpkts;
+ } else if (creg == dd->ipath_cregs->cr_ibsymbolerrcnt) {
+ if (dd->ibdeltainprog)
+ val64 -= val64 - dd->ibsymsnap;
+ val64 -= dd->ibsymdelta;
+ } else if (creg == dd->ipath_cregs->cr_iblinkerrrecovcnt) {
+ if (dd->ibdeltainprog)
+ val64 -= val64 - dd->iblnkerrsnap;
+ val64 -= dd->iblnkerrdelta;
} else
val64 = (u64) val;
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 729446f56aab..91c74cc797ae 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -70,8 +70,6 @@ static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
goto done;
}
- rsge.sg_list = NULL;
-
/*
* Check that the qkey matches (except for QP0, see 9.6.1.4.1).
* Qkeys with the high order bit set mean use the
@@ -115,21 +113,6 @@ static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
rq = &qp->r_rq;
}
- if (rq->max_sge > 1) {
- /*
- * XXX We could use GFP_KERNEL if ipath_do_send()
- * was always called from the tasklet instead of
- * from ipath_post_send().
- */
- rsge.sg_list = kmalloc((rq->max_sge - 1) *
- sizeof(struct ipath_sge),
- GFP_ATOMIC);
- if (!rsge.sg_list) {
- dev->n_pkt_drops++;
- goto drop;
- }
- }
-
/*
* Get the next work request entry to find where to put the data.
* Note that it is safe to drop the lock after changing rq->tail
@@ -147,6 +130,7 @@ static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
goto drop;
}
wqe = get_rwqe_ptr(rq, tail);
+ rsge.sg_list = qp->r_ud_sg_list;
if (!ipath_init_sge(qp, wqe, &rlen, &rsge)) {
spin_unlock_irqrestore(&rq->lock, flags);
dev->n_pkt_drops++;
@@ -242,7 +226,6 @@ static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
swqe->wr.send_flags & IB_SEND_SOLICITED);
drop:
- kfree(rsge.sg_list);
if (atomic_dec_and_test(&qp->refcount))
wake_up(&qp->wait);
done:;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index eabc4247860b..cdf0e6abd34d 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -1852,7 +1852,7 @@ unsigned ipath_get_npkeys(struct ipath_devdata *dd)
}
/**
- * ipath_get_pkey - return the indexed PKEY from the port 0 PKEY table
+ * ipath_get_pkey - return the indexed PKEY from the port PKEY table
* @dd: the infinipath device
* @index: the PKEY index
*/
@@ -1860,6 +1860,7 @@ unsigned ipath_get_pkey(struct ipath_devdata *dd, unsigned index)
{
unsigned ret;
+ /* always a kernel port, no locking needed */
if (index >= ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys))
ret = 0;
else
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index 9d12ae8a778e..11e3f613df93 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -431,6 +431,7 @@ struct ipath_qp {
u32 s_lsn; /* limit sequence number (credit) */
struct ipath_swqe *s_wq; /* send work queue */
struct ipath_swqe *s_wqe;
+ struct ipath_sge *r_ud_sg_list;
struct ipath_rq r_rq; /* receive work queue */
struct ipath_sge r_sg_list[0]; /* verified SGEs */
};
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 18308494a195..a3c5af1d7ec0 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -222,7 +222,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
}
err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
- cq->db.dma, &cq->mcq, 0);
+ cq->db.dma, &cq->mcq, vector, 0);
if (err)
goto err_dbmap;
@@ -325,15 +325,17 @@ static int mlx4_ib_get_outstanding_cqes(struct mlx4_ib_cq *cq)
static void mlx4_ib_cq_resize_copy_cqes(struct mlx4_ib_cq *cq)
{
- struct mlx4_cqe *cqe;
+ struct mlx4_cqe *cqe, *new_cqe;
int i;
i = cq->mcq.cons_index;
cqe = get_cqe(cq, i & cq->ibcq.cqe);
while ((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) != MLX4_CQE_OPCODE_RESIZE) {
- memcpy(get_cqe_from_buf(&cq->resize_buf->buf,
- (i + 1) & cq->resize_buf->cqe),
- get_cqe(cq, i & cq->ibcq.cqe), sizeof(struct mlx4_cqe));
+ new_cqe = get_cqe_from_buf(&cq->resize_buf->buf,
+ (i + 1) & cq->resize_buf->cqe);
+ memcpy(new_cqe, get_cqe(cq, i & cq->ibcq.cqe), sizeof(struct mlx4_cqe));
+ new_cqe->owner_sr_opcode = (cqe->owner_sr_opcode & ~MLX4_CQE_OWNER_MASK) |
+ (((i + 1) & (cq->resize_buf->cqe + 1)) ? MLX4_CQE_OWNER_MASK : 0);
cqe = get_cqe(cq, ++i & cq->ibcq.cqe);
}
++cq->mcq.cons_index;
@@ -697,7 +699,7 @@ repoll:
}
wc->slid = be16_to_cpu(cqe->rlid);
- wc->sl = be16_to_cpu(cqe->sl_vid >> 12);
+ wc->sl = be16_to_cpu(cqe->sl_vid) >> 12;
g_mlpath_rqpn = be32_to_cpu(cqe->g_mlpath_rqpn);
wc->src_qp = g_mlpath_rqpn & 0xffffff;
wc->dlid_path_bits = (g_mlpath_rqpn >> 24) & 0x7f;
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 2e80f8f47b02..dcefe1fceb5c 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -578,7 +578,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
ibdev->num_ports++;
ibdev->ib_dev.phys_port_cnt = ibdev->num_ports;
- ibdev->ib_dev.num_comp_vectors = 1;
+ ibdev->ib_dev.num_comp_vectors = dev->caps.num_comp_vectors;
ibdev->ib_dev.dma_device = &dev->pdev->dev;
ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_ABI_VERSION;
diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c
index 3f5f94879208..d4c81053e439 100644
--- a/drivers/infiniband/hw/mthca/mthca_mcg.c
+++ b/drivers/infiniband/hw/mthca/mthca_mcg.c
@@ -87,17 +87,7 @@ static int find_mgm(struct mthca_dev *dev,
}
if (0)
- mthca_dbg(dev, "Hash for %04x:%04x:%04x:%04x:"
- "%04x:%04x:%04x:%04x is %04x\n",
- be16_to_cpu(((__be16 *) gid)[0]),
- be16_to_cpu(((__be16 *) gid)[1]),
- be16_to_cpu(((__be16 *) gid)[2]),
- be16_to_cpu(((__be16 *) gid)[3]),
- be16_to_cpu(((__be16 *) gid)[4]),
- be16_to_cpu(((__be16 *) gid)[5]),
- be16_to_cpu(((__be16 *) gid)[6]),
- be16_to_cpu(((__be16 *) gid)[7]),
- *hash);
+ mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
*index = *hash;
*prev = -1;
@@ -264,16 +254,7 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
goto out;
if (index == -1) {
- mthca_err(dev, "MGID %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
- "not found\n",
- be16_to_cpu(((__be16 *) gid->raw)[0]),
- be16_to_cpu(((__be16 *) gid->raw)[1]),
- be16_to_cpu(((__be16 *) gid->raw)[2]),
- be16_to_cpu(((__be16 *) gid->raw)[3]),
- be16_to_cpu(((__be16 *) gid->raw)[4]),
- be16_to_cpu(((__be16 *) gid->raw)[5]),
- be16_to_cpu(((__be16 *) gid->raw)[6]),
- be16_to_cpu(((__be16 *) gid->raw)[7]));
+ mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
err = -EINVAL;
goto out;
}
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c
index aa1dc41f04c8..b9611ade9eab 100644
--- a/drivers/infiniband/hw/nes/nes.c
+++ b/drivers/infiniband/hw/nes/nes.c
@@ -142,14 +142,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
struct nes_device *nesdev;
struct net_device *netdev;
struct nes_vnic *nesvnic;
- unsigned int addr;
- unsigned int mask;
-
- addr = ntohl(ifa->ifa_address);
- mask = ntohl(ifa->ifa_mask);
- nes_debug(NES_DBG_NETDEV, "nes_inetaddr_event: ip address " NIPQUAD_FMT
- ", netmask " NIPQUAD_FMT ".\n",
- HIPQUAD(addr), HIPQUAD(mask));
+
+ nes_debug(NES_DBG_NETDEV, "nes_inetaddr_event: ip address %pI4, netmask %pI4.\n",
+ &ifa->ifa_address, &ifa->ifa_mask);
list_for_each_entry(nesdev, &nes_dev_list, list) {
nes_debug(NES_DBG_NETDEV, "Nesdev list entry = 0x%p. (%s)\n",
nesdev, nesdev->netdev[0]->name);
@@ -360,10 +355,8 @@ struct ib_qp *nes_get_qp(struct ib_device *device, int qpn)
*/
static void nes_print_macaddr(struct net_device *netdev)
{
- DECLARE_MAC_BUF(mac);
-
- nes_debug(NES_DBG_INIT, "%s: %s, IRQ %u\n",
- netdev->name, print_mac(mac, netdev->dev_addr), netdev->irq);
+ nes_debug(NES_DBG_INIT, "%s: %pM, IRQ %u\n",
+ netdev->name, netdev->dev_addr, netdev->irq);
}
/**
diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h
index 1595dc7bba9d..13a5bb1a7bcf 100644
--- a/drivers/infiniband/hw/nes/nes.h
+++ b/drivers/infiniband/hw/nes/nes.h
@@ -137,14 +137,18 @@
#ifdef CONFIG_INFINIBAND_NES_DEBUG
#define nes_debug(level, fmt, args...) \
+do { \
if (level & nes_debug_level) \
- printk(KERN_ERR PFX "%s[%u]: " fmt, __func__, __LINE__, ##args)
-
-#define assert(expr) \
-if (!(expr)) { \
- printk(KERN_ERR PFX "Assertion failed! %s, %s, %s, line %d\n", \
- #expr, __FILE__, __func__, __LINE__); \
-}
+ printk(KERN_ERR PFX "%s[%u]: " fmt, __func__, __LINE__, ##args); \
+} while (0)
+
+#define assert(expr) \
+do { \
+ if (!(expr)) { \
+ printk(KERN_ERR PFX "Assertion failed! %s, %s, %s, line %d\n", \
+ #expr, __FILE__, __func__, __LINE__); \
+ } \
+} while (0)
#define NES_EVENT_TIMEOUT 1200000
#else
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 2caf9da81ad5..a812db243477 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -86,15 +86,14 @@ static int mini_cm_accept(struct nes_cm_core *, struct ietf_mpa_frame *,
struct nes_cm_node *);
static int mini_cm_reject(struct nes_cm_core *, struct ietf_mpa_frame *,
struct nes_cm_node *);
-static void mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
+static int mini_cm_recv_pkt(struct nes_cm_core *, struct nes_vnic *,
struct sk_buff *);
static int mini_cm_dealloc_core(struct nes_cm_core *);
static int mini_cm_get(struct nes_cm_core *);
static int mini_cm_set(struct nes_cm_core *, u32, u32);
-static struct sk_buff *form_cm_frame(struct sk_buff *, struct nes_cm_node *,
+static void form_cm_frame(struct sk_buff *, struct nes_cm_node *,
void *, u32, void *, u32, u8);
-static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node);
static int add_ref_cm_node(struct nes_cm_node *);
static int rem_ref_cm_node(struct nes_cm_core *, struct nes_cm_node *);
@@ -251,7 +250,7 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 len)
* form_cm_frame - get a free packet and build empty frame Use
* node info to build.
*/
-static struct sk_buff *form_cm_frame(struct sk_buff *skb,
+static void form_cm_frame(struct sk_buff *skb,
struct nes_cm_node *cm_node, void *options, u32 optionsize,
void *data, u32 datasize, u8 flags)
{
@@ -339,7 +338,6 @@ static struct sk_buff *form_cm_frame(struct sk_buff *skb,
skb_shinfo(skb)->nr_frags = 0;
cm_packets_created++;
- return skb;
}
@@ -356,7 +354,6 @@ static void print_core(struct nes_cm_core *core)
nes_debug(NES_DBG_CM, "State : %u \n", core->state);
- nes_debug(NES_DBG_CM, "Tx Free cnt : %u \n", skb_queue_len(&core->tx_free_list));
nes_debug(NES_DBG_CM, "Listen Nodes : %u \n", atomic_read(&core->listen_node_cnt));
nes_debug(NES_DBG_CM, "Active Nodes : %u \n", atomic_read(&core->node_cnt));
@@ -381,8 +378,6 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
int ret = 0;
u32 was_timer_set;
- if (!cm_node)
- return -EINVAL;
new_send = kzalloc(sizeof(*new_send), GFP_ATOMIC);
if (!new_send)
return -1;
@@ -459,13 +454,23 @@ static void nes_cm_timer_tick(unsigned long pass)
int ret = NETDEV_TX_OK;
enum nes_cm_node_state last_state;
+ struct list_head timer_list;
+ INIT_LIST_HEAD(&timer_list);
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_node, list_core_temp,
- &cm_core->connected_nodes) {
+ &cm_core->connected_nodes) {
cm_node = container_of(list_node, struct nes_cm_node, list);
- add_ref_cm_node(cm_node);
- spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+ if (!list_empty(&cm_node->recv_list) || (cm_node->send_entry)) {
+ add_ref_cm_node(cm_node);
+ list_add(&cm_node->timer_entry, &timer_list);
+ }
+ }
+ spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
+ list_for_each_safe(list_node, list_core_temp, &timer_list) {
+ cm_node = container_of(list_node, struct nes_cm_node,
+ timer_entry);
spin_lock_irqsave(&cm_node->recv_list_lock, flags);
list_for_each_safe(list_core, list_node_temp,
&cm_node->recv_list) {
@@ -519,7 +524,7 @@ static void nes_cm_timer_tick(unsigned long pass)
do {
send_entry = cm_node->send_entry;
if (!send_entry)
- continue;
+ break;
if (time_after(send_entry->timetosend, jiffies)) {
if (cm_node->state != NES_CM_STATE_TSA) {
if ((nexttimeout >
@@ -528,18 +533,18 @@ static void nes_cm_timer_tick(unsigned long pass)
nexttimeout =
send_entry->timetosend;
settimer = 1;
- continue;
+ break;
}
} else {
free_retrans_entry(cm_node);
- continue;
+ break;
}
}
if ((cm_node->state == NES_CM_STATE_TSA) ||
(cm_node->state == NES_CM_STATE_CLOSED)) {
free_retrans_entry(cm_node);
- continue;
+ break;
}
if (!send_entry->retranscount ||
@@ -557,7 +562,7 @@ static void nes_cm_timer_tick(unsigned long pass)
NES_CM_EVENT_ABORTED);
spin_lock_irqsave(&cm_node->retrans_list_lock,
flags);
- continue;
+ break;
}
atomic_inc(&send_entry->skb->users);
cm_packets_retrans++;
@@ -583,7 +588,7 @@ static void nes_cm_timer_tick(unsigned long pass)
send_entry->retrycount--;
nexttimeout = jiffies + NES_SHORT_TIME;
settimer = 1;
- continue;
+ break;
} else {
cm_packets_sent++;
}
@@ -615,14 +620,12 @@ static void nes_cm_timer_tick(unsigned long pass)
spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
rem_ref_cm_node(cm_node->cm_core, cm_node);
- spin_lock_irqsave(&cm_core->ht_lock, flags);
if (ret != NETDEV_TX_OK) {
nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n",
cm_node);
break;
}
}
- spin_unlock_irqrestore(&cm_core->ht_lock, flags);
if (settimer) {
if (!timer_pending(&cm_core->tcp_timer)) {
@@ -683,7 +686,7 @@ static int send_syn(struct nes_cm_node *cm_node, u32 sendack,
optionssize += 1;
if (!skb)
- skb = get_free_pkt(cm_node);
+ skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
return -1;
@@ -708,7 +711,7 @@ static int send_reset(struct nes_cm_node *cm_node, struct sk_buff *skb)
int flags = SET_RST | SET_ACK;
if (!skb)
- skb = get_free_pkt(cm_node);
+ skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
return -1;
@@ -729,7 +732,7 @@ static int send_ack(struct nes_cm_node *cm_node, struct sk_buff *skb)
int ret;
if (!skb)
- skb = get_free_pkt(cm_node);
+ skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
@@ -752,7 +755,7 @@ static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb)
/* if we didn't get a frame get one */
if (!skb)
- skb = get_free_pkt(cm_node);
+ skb = dev_alloc_skb(MAX_CM_BUFFER);
if (!skb) {
nes_debug(NES_DBG_CM, "Failed to get a Free pkt\n");
@@ -767,64 +770,20 @@ static int send_fin(struct nes_cm_node *cm_node, struct sk_buff *skb)
/**
- * get_free_pkt
- */
-static struct sk_buff *get_free_pkt(struct nes_cm_node *cm_node)
-{
- struct sk_buff *skb, *new_skb;
-
- /* check to see if we need to repopulate the free tx pkt queue */
- if (skb_queue_len(&cm_node->cm_core->tx_free_list) < NES_CM_FREE_PKT_LO_WATERMARK) {
- while (skb_queue_len(&cm_node->cm_core->tx_free_list) <
- cm_node->cm_core->free_tx_pkt_max) {
- /* replace the frame we took, we won't get it back */
- new_skb = dev_alloc_skb(cm_node->cm_core->mtu);
- BUG_ON(!new_skb);
- /* add a replacement frame to the free tx list head */
- skb_queue_head(&cm_node->cm_core->tx_free_list, new_skb);
- }
- }
-
- skb = skb_dequeue(&cm_node->cm_core->tx_free_list);
-
- return skb;
-}
-
-
-/**
- * make_hashkey - generate hash key from node tuple
- */
-static inline int make_hashkey(u16 loc_port, nes_addr_t loc_addr, u16 rem_port,
- nes_addr_t rem_addr)
-{
- u32 hashkey = 0;
-
- hashkey = loc_addr + rem_addr + loc_port + rem_port;
- hashkey = (hashkey % NES_CM_HASHTABLE_SIZE);
-
- return hashkey;
-}
-
-
-/**
* find_node - find a cm node that matches the reference cm node
*/
static struct nes_cm_node *find_node(struct nes_cm_core *cm_core,
u16 rem_port, nes_addr_t rem_addr, u16 loc_port, nes_addr_t loc_addr)
{
unsigned long flags;
- u32 hashkey;
struct list_head *hte;
struct nes_cm_node *cm_node;
- /* make a hash index key for this packet */
- hashkey = make_hashkey(loc_port, loc_addr, rem_port, rem_addr);
-
/* get a handle on the hte */
hte = &cm_core->connected_nodes;
- nes_debug(NES_DBG_CM, "Searching for an owner node: " NIPQUAD_FMT ":%x from core %p->%p\n",
- HIPQUAD(loc_addr), loc_port, cm_core, hte);
+ nes_debug(NES_DBG_CM, "Searching for an owner node: %pI4:%x from core %p->%p\n",
+ &loc_addr, loc_port, cm_core, hte);
/* walk list and find cm_node associated with this session ID */
spin_lock_irqsave(&cm_core->ht_lock, flags);
@@ -873,8 +832,8 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
}
spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
- nes_debug(NES_DBG_CM, "Unable to find listener for " NIPQUAD_FMT ":%x\n",
- HIPQUAD(dst_addr), dst_port);
+ nes_debug(NES_DBG_CM, "Unable to find listener for %pI4:%x\n",
+ &dst_addr, dst_port);
/* no listener */
return NULL;
@@ -887,7 +846,6 @@ static struct nes_cm_listener *find_listener(struct nes_cm_core *cm_core,
static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node)
{
unsigned long flags;
- u32 hashkey;
struct list_head *hte;
if (!cm_node || !cm_core)
@@ -896,11 +854,6 @@ static int add_hte_node(struct nes_cm_core *cm_core, struct nes_cm_node *cm_node
nes_debug(NES_DBG_CM, "Adding Node %p to Active Connection HT\n",
cm_node);
- /* first, make an index into our hash table */
- hashkey = make_hashkey(cm_node->loc_port, cm_node->loc_addr,
- cm_node->rem_port, cm_node->rem_addr);
- cm_node->hashkey = hashkey;
-
spin_lock_irqsave(&cm_core->ht_lock, flags);
/* get a handle on the hash table element (list head for this slot) */
@@ -925,28 +878,36 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
struct list_head *list_pos = NULL;
struct list_head *list_temp = NULL;
struct nes_cm_node *cm_node = NULL;
+ struct list_head reset_list;
nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, "
"refcnt=%d\n", listener, free_hanging_nodes,
atomic_read(&listener->ref_count));
/* free non-accelerated child nodes for this listener */
+ INIT_LIST_HEAD(&reset_list);
if (free_hanging_nodes) {
spin_lock_irqsave(&cm_core->ht_lock, flags);
list_for_each_safe(list_pos, list_temp,
- &g_cm_core->connected_nodes) {
+ &g_cm_core->connected_nodes) {
cm_node = container_of(list_pos, struct nes_cm_node,
list);
if ((cm_node->listener == listener) &&
- (!cm_node->accelerated)) {
- cleanup_retrans_entry(cm_node);
- spin_unlock_irqrestore(&cm_core->ht_lock,
- flags);
- send_reset(cm_node, NULL);
- spin_lock_irqsave(&cm_core->ht_lock, flags);
+ (!cm_node->accelerated)) {
+ add_ref_cm_node(cm_node);
+ list_add(&cm_node->reset_entry, &reset_list);
}
}
spin_unlock_irqrestore(&cm_core->ht_lock, flags);
}
+
+ list_for_each_safe(list_pos, list_temp, &reset_list) {
+ cm_node = container_of(list_pos, struct nes_cm_node,
+ reset_entry);
+ cleanup_retrans_entry(cm_node);
+ send_reset(cm_node, NULL);
+ rem_ref_cm_node(cm_node->cm_core, cm_node);
+ }
+
spin_lock_irqsave(&cm_core->listen_list_lock, flags);
if (!atomic_dec_return(&listener->ref_count)) {
list_del(&listener->list);
@@ -1027,7 +988,6 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
struct flowi fl;
struct neighbour *neigh;
int rc = -1;
- DECLARE_MAC_BUF(mac);
memset(&fl, 0, sizeof fl);
fl.nl_u.ip4_u.daddr = htonl(dst_ip);
@@ -1041,8 +1001,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
if (neigh) {
if (neigh->nud_state & NUD_VALID) {
nes_debug(NES_DBG_CM, "Neighbor MAC address for 0x%08X"
- " is %s, Gateway is 0x%08X \n", dst_ip,
- print_mac(mac, neigh->ha), ntohl(rt->rt_gateway));
+ " is %pM, Gateway is 0x%08X \n", dst_ip,
+ neigh->ha, ntohl(rt->rt_gateway));
nes_manage_arp_cache(nesvnic->netdev, neigh->ha,
dst_ip, NES_ARP_ADD);
rc = nes_arp_table(nesvnic->nesdev, dst_ip, NULL,
@@ -1071,7 +1031,6 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
int arpindex = 0;
struct nes_device *nesdev;
struct nes_adapter *nesadapter;
- DECLARE_MAC_BUF(mac);
/* create an hte and cm_node for this instance */
cm_node = kzalloc(sizeof(*cm_node), GFP_ATOMIC);
@@ -1084,10 +1043,9 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->loc_port = cm_info->loc_port;
cm_node->rem_port = cm_info->rem_port;
cm_node->send_write0 = send_first;
- nes_debug(NES_DBG_CM, "Make node addresses : loc = " NIPQUAD_FMT
- ":%x, rem = " NIPQUAD_FMT ":%x\n",
- HIPQUAD(cm_node->loc_addr), cm_node->loc_port,
- HIPQUAD(cm_node->rem_addr), cm_node->rem_port);
+ nes_debug(NES_DBG_CM, "Make node addresses : loc = %pI4:%x, rem = %pI4:%x\n",
+ &cm_node->loc_addr, cm_node->loc_port,
+ &cm_node->rem_addr, cm_node->rem_port);
cm_node->listener = listener;
cm_node->netdev = nesvnic->netdev;
cm_node->cm_id = cm_info->cm_id;
@@ -1126,7 +1084,10 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
cm_node->loopbackpartner = NULL;
/* get the mac addr for the remote node */
- arpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
+ if (ipv4_is_loopback(htonl(cm_node->rem_addr)))
+ arpindex = nes_arp_table(nesdev, ntohl(nesvnic->local_ipaddr), NULL, NES_ARP_RESOLVE);
+ else
+ arpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
if (arpindex < 0) {
arpindex = nes_addr_resolve_neigh(nesvnic, cm_info->rem_addr);
if (arpindex < 0) {
@@ -1137,8 +1098,8 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
/* copy the mac addr to node context */
memcpy(cm_node->rem_mac, nesadapter->arp_table[arpindex].mac_addr, ETH_ALEN);
- nes_debug(NES_DBG_CM, "Remote mac addr from arp table: %s\n",
- print_mac(mac, cm_node->rem_mac));
+ nes_debug(NES_DBG_CM, "Remote mac addr from arp table: %pM\n",
+ cm_node->rem_mac);
add_hte_node(cm_core, cm_node);
atomic_inc(&cm_nodes_created);
@@ -1306,7 +1267,6 @@ static void drop_packet(struct sk_buff *skb)
static void handle_fin_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
struct tcphdr *tcph)
{
- atomic_inc(&cm_resets_recvd);
nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
"refcnt=%d\n", cm_node, cm_node->state,
atomic_read(&cm_node->ref_count));
@@ -1344,6 +1304,7 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
{
int reset = 0; /* whether to send reset in case of err.. */
+ int passive_state;
atomic_inc(&cm_resets_recvd);
nes_debug(NES_DBG_CM, "Received Reset, cm_node = %p, state = %u."
" refcnt=%d\n", cm_node, cm_node->state,
@@ -1357,7 +1318,14 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
cm_node->listener, cm_node->state);
active_open_err(cm_node, skb, reset);
break;
- /* For PASSIVE open states, remove the cm_node event */
+ case NES_CM_STATE_MPAREQ_RCVD:
+ passive_state = atomic_add_return(1, &cm_node->passive_state);
+ if (passive_state == NES_SEND_RESET_EVENT)
+ create_event(cm_node, NES_CM_EVENT_RESET);
+ cleanup_retrans_entry(cm_node);
+ cm_node->state = NES_CM_STATE_CLOSED;
+ dev_kfree_skb_any(skb);
+ break;
case NES_CM_STATE_ESTABLISHED:
case NES_CM_STATE_SYN_RCVD:
case NES_CM_STATE_LISTENING:
@@ -1365,7 +1333,14 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
passive_open_err(cm_node, skb, reset);
break;
case NES_CM_STATE_TSA:
+ active_open_err(cm_node, skb, reset);
+ break;
+ case NES_CM_STATE_CLOSED:
+ cleanup_retrans_entry(cm_node);
+ drop_packet(skb);
+ break;
default:
+ drop_packet(skb);
break;
}
}
@@ -1394,6 +1369,9 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb,
dev_kfree_skb_any(skb);
if (type == NES_CM_EVENT_CONNECTED)
cm_node->state = NES_CM_STATE_TSA;
+ else
+ atomic_set(&cm_node->passive_state,
+ NES_PASSIVE_STATE_INDICATED);
create_event(cm_node, type);
}
@@ -1474,7 +1452,7 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
int optionsize;
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
- skb_pull(skb, tcph->doff << 2);
+ skb_trim(skb, 0);
inc_sequence = ntohl(tcph->seq);
switch (cm_node->state) {
@@ -1507,6 +1485,10 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
cm_node->state = NES_CM_STATE_SYN_RCVD;
send_syn(cm_node, 1, skb);
break;
+ case NES_CM_STATE_CLOSED:
+ cleanup_retrans_entry(cm_node);
+ send_reset(cm_node, skb);
+ break;
case NES_CM_STATE_TSA:
case NES_CM_STATE_ESTABLISHED:
case NES_CM_STATE_FIN_WAIT1:
@@ -1515,7 +1497,6 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_LAST_ACK:
case NES_CM_STATE_CLOSING:
case NES_CM_STATE_UNKNOWN:
- case NES_CM_STATE_CLOSED:
default:
drop_packet(skb);
break;
@@ -1531,7 +1512,7 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
int optionsize;
optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
- skb_pull(skb, tcph->doff << 2);
+ skb_trim(skb, 0);
inc_sequence = ntohl(tcph->seq);
switch (cm_node->state) {
case NES_CM_STATE_SYN_SENT:
@@ -1555,6 +1536,12 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
/* passive open, so should not be here */
passive_open_err(cm_node, skb, 1);
break;
+ case NES_CM_STATE_LISTENING:
+ case NES_CM_STATE_CLOSED:
+ cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
+ cleanup_retrans_entry(cm_node);
+ send_reset(cm_node, skb);
+ break;
case NES_CM_STATE_ESTABLISHED:
case NES_CM_STATE_FIN_WAIT1:
case NES_CM_STATE_FIN_WAIT2:
@@ -1562,7 +1549,6 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
case NES_CM_STATE_TSA:
case NES_CM_STATE_CLOSING:
case NES_CM_STATE_UNKNOWN:
- case NES_CM_STATE_CLOSED:
case NES_CM_STATE_MPAREQ_SENT:
default:
drop_packet(skb);
@@ -1577,6 +1563,13 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
u32 inc_sequence;
u32 rem_seq_ack;
u32 rem_seq;
+ int ret;
+ int optionsize;
+ u32 temp_seq = cm_node->tcp_cntxt.loc_seq_num;
+
+ optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
+ cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
+
if (check_seq(cm_node, tcph, skb))
return;
@@ -1589,7 +1582,18 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
switch (cm_node->state) {
case NES_CM_STATE_SYN_RCVD:
/* Passive OPEN */
+ ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 1);
+ if (ret)
+ break;
cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
+ cm_node->tcp_cntxt.loc_seq_num = temp_seq;
+ if (cm_node->tcp_cntxt.rem_ack_num !=
+ cm_node->tcp_cntxt.loc_seq_num) {
+ nes_debug(NES_DBG_CM, "rem_ack_num != loc_seq_num\n");
+ cleanup_retrans_entry(cm_node);
+ send_reset(cm_node, skb);
+ return;
+ }
cm_node->state = NES_CM_STATE_ESTABLISHED;
if (datasize) {
cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
@@ -1621,11 +1625,15 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
dev_kfree_skb_any(skb);
}
break;
+ case NES_CM_STATE_LISTENING:
+ case NES_CM_STATE_CLOSED:
+ cleanup_retrans_entry(cm_node);
+ send_reset(cm_node, skb);
+ break;
case NES_CM_STATE_FIN_WAIT1:
case NES_CM_STATE_SYN_SENT:
case NES_CM_STATE_FIN_WAIT2:
case NES_CM_STATE_TSA:
- case NES_CM_STATE_CLOSED:
case NES_CM_STATE_MPAREQ_RCVD:
case NES_CM_STATE_LAST_ACK:
case NES_CM_STATE_CLOSING:
@@ -1648,9 +1656,9 @@ static int handle_tcp_options(struct nes_cm_node *cm_node, struct tcphdr *tcph,
nes_debug(NES_DBG_CM, "%s: Node %p, Sending RESET\n",
__func__, cm_node);
if (passive)
- passive_open_err(cm_node, skb, 0);
+ passive_open_err(cm_node, skb, 1);
else
- active_open_err(cm_node, skb, 0);
+ active_open_err(cm_node, skb, 1);
return 1;
}
}
@@ -1970,6 +1978,7 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
struct ietf_mpa_frame *mpa_frame, struct nes_cm_node *cm_node)
{
int ret = 0;
+ int passive_state;
nes_debug(NES_DBG_CM, "%s cm_node=%p type=%d state=%d\n",
__func__, cm_node, cm_node->tcp_cntxt.client, cm_node->state);
@@ -1977,9 +1986,13 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
if (cm_node->tcp_cntxt.client)
return ret;
cleanup_retrans_entry(cm_node);
- cm_node->state = NES_CM_STATE_CLOSED;
- ret = send_reset(cm_node, NULL);
+ passive_state = atomic_add_return(1, &cm_node->passive_state);
+ cm_node->state = NES_CM_STATE_CLOSED;
+ if (passive_state == NES_SEND_RESET_EVENT)
+ rem_ref_cm_node(cm_core, cm_node);
+ else
+ ret = send_reset(cm_node, NULL);
return ret;
}
@@ -2037,7 +2050,7 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
* recv_pkt - recv an ETHERNET packet, and process it through CM
* node state machine
*/
-static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
+static int mini_cm_recv_pkt(struct nes_cm_core *cm_core,
struct nes_vnic *nesvnic, struct sk_buff *skb)
{
struct nes_cm_node *cm_node = NULL;
@@ -2045,33 +2058,24 @@ static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
struct iphdr *iph;
struct tcphdr *tcph;
struct nes_cm_info nfo;
+ int skb_handled = 1;
if (!skb)
- return;
+ return 0;
if (skb->len < sizeof(struct iphdr) + sizeof(struct tcphdr)) {
- dev_kfree_skb_any(skb);
- return;
+ return 0;
}
iph = (struct iphdr *)skb->data;
tcph = (struct tcphdr *)(skb->data + sizeof(struct iphdr));
- skb_reset_network_header(skb);
- skb_set_transport_header(skb, sizeof(*tcph));
- if (!tcph) {
- dev_kfree_skb_any(skb);
- return;
- }
- skb->len = ntohs(iph->tot_len);
nfo.loc_addr = ntohl(iph->daddr);
nfo.loc_port = ntohs(tcph->dest);
nfo.rem_addr = ntohl(iph->saddr);
nfo.rem_port = ntohs(tcph->source);
- nes_debug(NES_DBG_CM, "Received packet: dest=" NIPQUAD_FMT
- ":0x%04X src=" NIPQUAD_FMT ":0x%04X\n",
- NIPQUAD(iph->daddr), tcph->dest,
- NIPQUAD(iph->saddr), tcph->source);
+ nes_debug(NES_DBG_CM, "Received packet: dest=%pI4:0x%04X src=%pI4:0x%04X\n",
+ &iph->daddr, tcph->dest, &iph->saddr, tcph->source);
do {
cm_node = find_node(cm_core,
@@ -2082,23 +2086,21 @@ static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
/* Only type of packet accepted are for */
/* the PASSIVE open (syn only) */
if ((!tcph->syn) || (tcph->ack)) {
- cm_packets_dropped++;
+ skb_handled = 0;
break;
}
listener = find_listener(cm_core, nfo.loc_addr,
nfo.loc_port,
NES_CM_LISTENER_ACTIVE_STATE);
- if (listener) {
- nfo.cm_id = listener->cm_id;
- nfo.conn_type = listener->conn_type;
- } else {
- nes_debug(NES_DBG_CM, "Unable to find listener "
- "for the pkt\n");
- cm_packets_dropped++;
- dev_kfree_skb_any(skb);
+ if (!listener) {
+ nfo.cm_id = NULL;
+ nfo.conn_type = 0;
+ nes_debug(NES_DBG_CM, "Unable to find listener for the pkt\n");
+ skb_handled = 0;
break;
}
-
+ nfo.cm_id = listener->cm_id;
+ nfo.conn_type = listener->conn_type;
cm_node = make_cm_node(cm_core, nesvnic, &nfo,
listener);
if (!cm_node) {
@@ -2124,9 +2126,13 @@ static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
dev_kfree_skb_any(skb);
break;
}
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, sizeof(*tcph));
+ skb->len = ntohs(iph->tot_len);
process_packet(cm_node, skb, cm_core);
rem_ref_cm_node(cm_core, cm_node);
} while (0);
+ return skb_handled;
}
@@ -2135,10 +2141,7 @@ static void mini_cm_recv_pkt(struct nes_cm_core *cm_core,
*/
static struct nes_cm_core *nes_cm_alloc_core(void)
{
- int i;
-
struct nes_cm_core *cm_core;
- struct sk_buff *skb = NULL;
/* setup the CM core */
/* alloc top level core control structure */
@@ -2156,19 +2159,6 @@ static struct nes_cm_core *nes_cm_alloc_core(void)
atomic_set(&cm_core->events_posted, 0);
- /* init the packet lists */
- skb_queue_head_init(&cm_core->tx_free_list);
-
- for (i = 0; i < NES_CM_DEFAULT_FRAME_CNT; i++) {
- skb = dev_alloc_skb(cm_core->mtu);
- if (!skb) {
- kfree(cm_core);
- return NULL;
- }
- /* add 'raw' skb to free frame list */
- skb_queue_head(&cm_core->tx_free_list, skb);
- }
-
cm_core->api = &nes_cm_api;
spin_lock_init(&cm_core->ht_lock);
@@ -2397,7 +2387,6 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
atomic_inc(&cm_disconnects);
cm_event.event = IW_CM_EVENT_DISCONNECT;
if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) {
- issued_disconnect_reset = 1;
cm_event.status = IW_CM_EVENT_STATUS_RESET;
nes_debug(NES_DBG_CM, "Generating a CM "
"Disconnect Event (status reset) for "
@@ -2547,6 +2536,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
struct nes_v4_quad nes_quad;
u32 crc_value;
int ret;
+ int passive_state;
ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
if (!ibqp)
@@ -2714,8 +2704,6 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
conn_param->private_data_len +
sizeof(struct ietf_mpa_frame));
- attr.qp_state = IB_QPS_RTS;
- nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
/* notify OF layer that accept event was successfull */
cm_id->add_ref(cm_id);
@@ -2728,6 +2716,8 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
cm_event.private_data = NULL;
cm_event.private_data_len = 0;
ret = cm_id->event_handler(cm_id, &cm_event);
+ attr.qp_state = IB_QPS_RTS;
+ nes_modify_qp(&nesqp->ibqp, &attr, IB_QP_STATE, NULL);
if (cm_node->loopbackpartner) {
cm_node->loopbackpartner->mpa_frame_size =
nesqp->private_data_len;
@@ -2740,6 +2730,9 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
printk(KERN_ERR "%s[%u] OFA CM event_handler returned, "
"ret=%d\n", __func__, __LINE__, ret);
+ passive_state = atomic_add_return(1, &cm_node->passive_state);
+ if (passive_state == NES_SEND_RESET_EVENT)
+ create_event(cm_node, NES_CM_EVENT_RESET);
return 0;
}
@@ -2943,15 +2936,16 @@ int nes_destroy_listen(struct iw_cm_id *cm_id)
*/
int nes_cm_recv(struct sk_buff *skb, struct net_device *netdevice)
{
+ int rc = 0;
cm_packets_received++;
if ((g_cm_core) && (g_cm_core->api)) {
- g_cm_core->api->recv_pkt(g_cm_core, netdev_priv(netdevice), skb);
+ rc = g_cm_core->api->recv_pkt(g_cm_core, netdev_priv(netdevice), skb);
} else {
nes_debug(NES_DBG_CM, "Unable to process packet for CM,"
" cm is not setup properly.\n");
}
- return 0;
+ return rc;
}
@@ -3222,6 +3216,18 @@ static void cm_event_reset(struct nes_cm_event *event)
cm_event.private_data_len = 0;
ret = cm_id->event_handler(cm_id, &cm_event);
+ cm_id->add_ref(cm_id);
+ atomic_inc(&cm_closes);
+ cm_event.event = IW_CM_EVENT_CLOSE;
+ cm_event.status = IW_CM_EVENT_STATUS_OK;
+ cm_event.provider_data = cm_id->provider_data;
+ cm_event.local_addr = cm_id->local_addr;
+ cm_event.remote_addr = cm_id->remote_addr;
+ cm_event.private_data = NULL;
+ cm_event.private_data_len = 0;
+ nes_debug(NES_DBG_CM, "NODE %p Generating CLOSE\n", event->cm_node);
+ ret = cm_id->event_handler(cm_id, &cm_event);
+
nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret);
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h
index 367b3d290140..fafa35042ebd 100644
--- a/drivers/infiniband/hw/nes/nes_cm.h
+++ b/drivers/infiniband/hw/nes/nes_cm.h
@@ -76,6 +76,10 @@ enum nes_timer_type {
NES_TIMER_TYPE_CLOSE,
};
+#define NES_PASSIVE_STATE_INDICATED 0
+#define NES_DO_NOT_SEND_RESET_EVENT 1
+#define NES_SEND_RESET_EVENT 2
+
#define MAX_NES_IFS 4
#define SET_ACK 1
@@ -161,6 +165,8 @@ struct nes_timer_entry {
#define NES_CM_DEF_SEQ2 0x18ed5740
#define NES_CM_DEF_LOCAL_ID2 0xb807
+#define MAX_CM_BUFFER 512
+
typedef u32 nes_addr_t;
@@ -254,8 +260,6 @@ struct nes_cm_listener {
/* per connection node and node state information */
struct nes_cm_node {
- u32 hashkey;
-
nes_addr_t loc_addr, rem_addr;
u16 loc_port, rem_port;
@@ -292,7 +296,10 @@ struct nes_cm_node {
int apbvt_set;
int accept_pend;
int freed;
+ struct list_head timer_entry;
+ struct list_head reset_entry;
struct nes_qp *nesqp;
+ atomic_t passive_state;
};
/* structure for client or CM to fill when making CM api calls. */
@@ -350,7 +357,6 @@ struct nes_cm_core {
u32 mtu;
u32 free_tx_pkt_max;
u32 rx_pkt_posted;
- struct sk_buff_head tx_free_list;
atomic_t ht_node_cnt;
struct list_head connected_nodes;
/* struct list_head hashtable[NES_CM_HASHTABLE_SIZE]; */
@@ -390,7 +396,7 @@ struct nes_cm_ops {
struct nes_cm_node *);
int (*reject)(struct nes_cm_core *, struct ietf_mpa_frame *,
struct nes_cm_node *);
- void (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
+ int (*recv_pkt)(struct nes_cm_core *, struct nes_vnic *,
struct sk_buff *);
int (*destroy_cm_core)(struct nes_cm_core *);
int (*get)(struct nes_cm_core *);
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c
index 7c49cc882d75..5d139db1b771 100644
--- a/drivers/infiniband/hw/nes/nes_hw.c
+++ b/drivers/infiniband/hw/nes/nes_hw.c
@@ -2541,7 +2541,7 @@ static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic
{
struct nes_vnic *nesvnic = container_of(cq, struct nes_vnic, nic_cq);
- netif_rx_schedule(nesdev->netdev[nesvnic->netdev_index], &nesvnic->napi);
+ netif_rx_schedule(&nesvnic->napi);
}
@@ -2700,27 +2700,33 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)
pkt_type, (pkt_type & NES_PKT_TYPE_APBVT_MASK)); */
if ((pkt_type & NES_PKT_TYPE_APBVT_MASK) == NES_PKT_TYPE_APBVT_BITS) {
- nes_cm_recv(rx_skb, nesvnic->netdev);
+ if (nes_cm_recv(rx_skb, nesvnic->netdev))
+ rx_skb = NULL;
+ }
+ if (rx_skb == NULL)
+ goto skip_rx_indicate0;
+
+
+ if ((cqe_misc & NES_NIC_CQE_TAG_VALID) &&
+ (nesvnic->vlan_grp != NULL)) {
+ vlan_tag = (u16)(le32_to_cpu(
+ cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX])
+ >> 16);
+ nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
+ nesvnic->netdev->name, vlan_tag);
+ if (nes_use_lro)
+ lro_vlan_hwaccel_receive_skb(&nesvnic->lro_mgr, rx_skb,
+ nesvnic->vlan_grp, vlan_tag, NULL);
+ else
+ nes_vlan_rx(rx_skb, nesvnic->vlan_grp, vlan_tag);
} else {
- if ((cqe_misc & NES_NIC_CQE_TAG_VALID) && (nesvnic->vlan_grp != NULL)) {
- vlan_tag = (u16)(le32_to_cpu(
- cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX])
- >> 16);
- nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",
- nesvnic->netdev->name, vlan_tag);
- if (nes_use_lro)
- lro_vlan_hwaccel_receive_skb(&nesvnic->lro_mgr, rx_skb,
- nesvnic->vlan_grp, vlan_tag, NULL);
- else
- nes_vlan_rx(rx_skb, nesvnic->vlan_grp, vlan_tag);
- } else {
- if (nes_use_lro)
- lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
- else
- nes_netif_rx(rx_skb);
- }
+ if (nes_use_lro)
+ lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL);
+ else
+ nes_netif_rx(rx_skb);
}
+skip_rx_indicate0:
nesvnic->netdev->last_rx = jiffies;
/* nesvnic->netstats.rx_packets++; */
/* nesvnic->netstats.rx_bytes += rx_pkt_size; */
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index 730358637bb6..57a47cf7e513 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -99,7 +99,6 @@ static int nics_per_function = 1;
static int nes_netdev_poll(struct napi_struct *napi, int budget)
{
struct nes_vnic *nesvnic = container_of(napi, struct nes_vnic, napi);
- struct net_device *netdev = nesvnic->netdev;
struct nes_device *nesdev = nesvnic->nesdev;
struct nes_hw_nic_cq *nescq = &nesvnic->nic_cq;
@@ -112,7 +111,7 @@ static int nes_netdev_poll(struct napi_struct *napi, int budget)
nes_nic_ce_handler(nesdev, nescq);
if (nescq->cqes_pending == 0) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
/* clear out completed cqes and arm */
nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT |
nescq->cq_number | (nescq->cqe_allocs_pending << 16));
@@ -797,14 +796,13 @@ static int nes_netdev_set_mac_address(struct net_device *netdev, void *p)
int i;
u32 macaddr_low;
u16 macaddr_high;
- DECLARE_MAC_BUF(mac);
if (!is_valid_ether_addr(mac_addr->sa_data))
return -EADDRNOTAVAIL;
memcpy(netdev->dev_addr, mac_addr->sa_data, netdev->addr_len);
- printk(PFX "%s: Address length = %d, Address = %s\n",
- __func__, netdev->addr_len, print_mac(mac, mac_addr->sa_data));
+ printk(PFX "%s: Address length = %d, Address = %pM\n",
+ __func__, netdev->addr_len, mac_addr->sa_data);
macaddr_high = ((u16)netdev->dev_addr[0]) << 8;
macaddr_high += (u16)netdev->dev_addr[1];
macaddr_low = ((u32)netdev->dev_addr[2]) << 24;
@@ -909,9 +907,8 @@ static void nes_netdev_set_multicast_list(struct net_device *netdev)
if (mc_index >= max_pft_entries_avaiable)
break;
if (multicast_addr) {
- DECLARE_MAC_BUF(mac);
- nes_debug(NES_DBG_NIC_RX, "Assigning MC Address %s to register 0x%04X nic_idx=%d\n",
- print_mac(mac, multicast_addr->dmi_addr),
+ nes_debug(NES_DBG_NIC_RX, "Assigning MC Address %pM to register 0x%04X nic_idx=%d\n",
+ multicast_addr->dmi_addr,
perfect_filter_register_address+(mc_index * 8),
mc_nic_index);
macaddr_high = ((u16)multicast_addr->dmi_addr[0]) << 8;
diff --git a/drivers/infiniband/hw/nes/nes_utils.c b/drivers/infiniband/hw/nes/nes_utils.c
index fb8cbd71a2ef..aa9b7348c728 100644
--- a/drivers/infiniband/hw/nes/nes_utils.c
+++ b/drivers/infiniband/hw/nes/nes_utils.c
@@ -540,11 +540,14 @@ struct nes_cqp_request *nes_get_cqp_request(struct nes_device *nesdev)
if (!list_empty(&nesdev->cqp_avail_reqs)) {
spin_lock_irqsave(&nesdev->cqp.lock, flags);
- cqp_request = list_entry(nesdev->cqp_avail_reqs.next,
+ if (!list_empty(&nesdev->cqp_avail_reqs)) {
+ cqp_request = list_entry(nesdev->cqp_avail_reqs.next,
struct nes_cqp_request, list);
- list_del_init(&cqp_request->list);
+ list_del_init(&cqp_request->list);
+ }
spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
- } else {
+ }
+ if (cqp_request == NULL) {
cqp_request = kzalloc(sizeof(struct nes_cqp_request), GFP_KERNEL);
if (cqp_request) {
cqp_request->dynamic = 1;
@@ -679,9 +682,8 @@ int nes_arp_table(struct nes_device *nesdev, u32 ip_addr, u8 *mac_addr, u32 acti
/* DELETE or RESOLVE */
if (arp_index == nesadapter->arp_table_size) {
- nes_debug(NES_DBG_NETDEV, "MAC for " NIPQUAD_FMT " not in ARP table - cannot %s\n",
- HIPQUAD(ip_addr),
- action == NES_ARP_RESOLVE ? "resolve" : "delete");
+ nes_debug(NES_DBG_NETDEV, "MAC for %pI4 not in ARP table - cannot %s\n",
+ &ip_addr, action == NES_ARP_RESOLVE ? "resolve" : "delete");
return -1;
}
diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c
index d36c9a0bf1bb..4fdb72454f94 100644
--- a/drivers/infiniband/hw/nes/nes_verbs.c
+++ b/drivers/infiniband/hw/nes/nes_verbs.c
@@ -1695,13 +1695,8 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
/* use 4k pbl */
nes_debug(NES_DBG_CQ, "pbl_entries=%u, use a 4k PBL\n", pbl_entries);
if (nesadapter->free_4kpbl == 0) {
- if (cqp_request->dynamic) {
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- kfree(cqp_request);
- } else {
- list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- }
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_free_cqp_request(nesdev, cqp_request);
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
@@ -1717,13 +1712,8 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
/* use 256 byte pbl */
nes_debug(NES_DBG_CQ, "pbl_entries=%u, use a 256 byte PBL\n", pbl_entries);
if (nesadapter->free_256pbl == 0) {
- if (cqp_request->dynamic) {
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- kfree(cqp_request);
- } else {
- list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- }
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_free_cqp_request(nesdev, cqp_request);
if (!context)
pci_free_consistent(nesdev->pcidev, nescq->cq_mem_size, mem,
nescq->hw_cq.cq_pbase);
@@ -1928,13 +1918,8 @@ static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
/* Two level PBL */
if ((pbl_count+1) > nesadapter->free_4kpbl) {
nes_debug(NES_DBG_MR, "Out of 4KB Pbls for two level request.\n");
- if (cqp_request->dynamic) {
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- kfree(cqp_request);
- } else {
- list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- }
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_free_cqp_request(nesdev, cqp_request);
return -ENOMEM;
} else {
nesadapter->free_4kpbl -= pbl_count+1;
@@ -1942,13 +1927,8 @@ static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
} else if (residual_page_count > 32) {
if (pbl_count > nesadapter->free_4kpbl) {
nes_debug(NES_DBG_MR, "Out of 4KB Pbls.\n");
- if (cqp_request->dynamic) {
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- kfree(cqp_request);
- } else {
- list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- }
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_free_cqp_request(nesdev, cqp_request);
return -ENOMEM;
} else {
nesadapter->free_4kpbl -= pbl_count;
@@ -1956,13 +1936,8 @@ static int nes_reg_mr(struct nes_device *nesdev, struct nes_pd *nespd,
} else {
if (pbl_count > nesadapter->free_256pbl) {
nes_debug(NES_DBG_MR, "Out of 256B Pbls.\n");
- if (cqp_request->dynamic) {
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- kfree(cqp_request);
- } else {
- list_add_tail(&cqp_request->list, &nesdev->cqp_avail_reqs);
- spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
- }
+ spin_unlock_irqrestore(&nesadapter->pbl_lock, flags);
+ nes_free_cqp_request(nesdev, cqp_request);
return -ENOMEM;
} else {
nesadapter->free_256pbl -= pbl_count;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index e0c7dfabf2b4..753a983a5fdc 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -732,29 +732,6 @@ extern int ipoib_debug_level;
do { (void) (priv); } while (0)
#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG_DATA */
-
-#define IPOIB_GID_FMT "%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:" \
- "%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x:%2.2x%2.2x"
-
-#define IPOIB_GID_RAW_ARG(gid) ((u8 *)(gid))[0], \
- ((u8 *)(gid))[1], \
- ((u8 *)(gid))[2], \
- ((u8 *)(gid))[3], \
- ((u8 *)(gid))[4], \
- ((u8 *)(gid))[5], \
- ((u8 *)(gid))[6], \
- ((u8 *)(gid))[7], \
- ((u8 *)(gid))[8], \
- ((u8 *)(gid))[9], \
- ((u8 *)(gid))[10],\
- ((u8 *)(gid))[11],\
- ((u8 *)(gid))[12],\
- ((u8 *)(gid))[13],\
- ((u8 *)(gid))[14],\
- ((u8 *)(gid))[15]
-
-#define IPOIB_GID_ARG(gid) IPOIB_GID_RAW_ARG((gid).raw)
-
#define IPOIB_QPN(ha) (be32_to_cpup((__be32 *) ha) & 0xffffff)
#endif /* _IPOIB_H */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 7b14c2c39500..47d588ba2a7f 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1128,8 +1128,8 @@ static int ipoib_cm_tx_init(struct ipoib_cm_tx *p, u32 qpn,
goto err_send_cm;
}
- ipoib_dbg(priv, "Request connection 0x%x for gid " IPOIB_GID_FMT " qpn 0x%x\n",
- p->qp->qp_num, IPOIB_GID_ARG(pathrec->dgid), qpn);
+ ipoib_dbg(priv, "Request connection 0x%x for gid %pI6 qpn 0x%x\n",
+ p->qp->qp_num, pathrec->dgid.raw, qpn);
return 0;
@@ -1276,8 +1276,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
list_move(&tx->list, &priv->cm.reap_list);
queue_work(ipoib_workqueue, &priv->cm.reap_task);
- ipoib_dbg(priv, "Reap connection for gid " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(tx->neigh->dgid));
+ ipoib_dbg(priv, "Reap connection for gid %pI6\n",
+ tx->neigh->dgid.raw);
tx->neigh = NULL;
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 28eb6f03c588..a1925810be3c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -446,11 +446,11 @@ poll_more:
if (dev->features & NETIF_F_LRO)
lro_flush_all(&priv->lro.lro_mgr);
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
if (unlikely(ib_req_notify_cq(priv->recv_cq,
IB_CQ_NEXT_COMP |
IB_CQ_REPORT_MISSED_EVENTS)) &&
- netif_rx_reschedule(dev, napi))
+ netif_rx_reschedule(napi))
goto poll_more;
}
@@ -462,7 +462,7 @@ void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr)
struct net_device *dev = dev_ptr;
struct ipoib_dev_priv *priv = netdev_priv(dev);
- netif_rx_schedule(dev, &priv->napi);
+ netif_rx_schedule(&priv->napi);
}
static void drain_tx_cq(struct net_device *dev)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 85257f6b9576..19e06bc38b39 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -360,9 +360,9 @@ void ipoib_mark_paths_invalid(struct net_device *dev)
spin_lock_irq(&priv->lock);
list_for_each_entry_safe(path, tp, &priv->path_list, list) {
- ipoib_dbg(priv, "mark path LID 0x%04x GID " IPOIB_GID_FMT " invalid\n",
+ ipoib_dbg(priv, "mark path LID 0x%04x GID %pI6 invalid\n",
be16_to_cpu(path->pathrec.dlid),
- IPOIB_GID_ARG(path->pathrec.dgid));
+ path->pathrec.dgid.raw);
path->valid = 0;
}
@@ -414,11 +414,11 @@ static void path_rec_completion(int status,
unsigned long flags;
if (!status)
- ipoib_dbg(priv, "PathRec LID 0x%04x for GID " IPOIB_GID_FMT "\n",
- be16_to_cpu(pathrec->dlid), IPOIB_GID_ARG(pathrec->dgid));
+ ipoib_dbg(priv, "PathRec LID 0x%04x for GID %pI6\n",
+ be16_to_cpu(pathrec->dlid), pathrec->dgid.raw);
else
- ipoib_dbg(priv, "PathRec status %d for GID " IPOIB_GID_FMT "\n",
- status, IPOIB_GID_ARG(path->pathrec.dgid));
+ ipoib_dbg(priv, "PathRec status %d for GID %pI6\n",
+ status, path->pathrec.dgid.raw);
skb_queue_head_init(&skqueue);
@@ -528,8 +528,8 @@ static int path_rec_start(struct net_device *dev,
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- ipoib_dbg(priv, "Start path record lookup for " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(path->pathrec.dgid));
+ ipoib_dbg(priv, "Start path record lookup for %pI6\n",
+ path->pathrec.dgid.raw);
init_completion(&path->done);
@@ -766,12 +766,11 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
if ((be16_to_cpup((__be16 *) skb->data) != ETH_P_ARP) &&
(be16_to_cpup((__be16 *) skb->data) != ETH_P_RARP)) {
- ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x "
- IPOIB_GID_FMT "\n",
+ ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n",
skb->dst ? "neigh" : "dst",
be16_to_cpup((__be16 *) skb->data),
IPOIB_QPN(phdr->hwaddr),
- IPOIB_GID_RAW_ARG(phdr->hwaddr + 4));
+ phdr->hwaddr + 4);
dev_kfree_skb_any(skb);
++dev->stats.tx_dropped;
return NETDEV_TX_OK;
@@ -847,9 +846,9 @@ static void ipoib_neigh_cleanup(struct neighbour *n)
else
return;
ipoib_dbg(priv,
- "neigh_cleanup for %06x " IPOIB_GID_FMT "\n",
+ "neigh_cleanup for %06x %pI6\n",
IPOIB_QPN(n->ha),
- IPOIB_GID_RAW_ARG(n->ha + 4));
+ n->ha + 4);
spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index d9d1223c3fd5..a2eb3b9789eb 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -71,9 +71,8 @@ static void ipoib_mcast_free(struct ipoib_mcast *mcast)
struct ipoib_neigh *neigh, *tmp;
int tx_dropped = 0;
- ipoib_dbg_mcast(netdev_priv(dev),
- "deleting multicast group " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_dbg_mcast(netdev_priv(dev), "deleting multicast group %pI6\n",
+ mcast->mcmember.mgid.raw);
spin_lock_irq(&priv->lock);
@@ -205,9 +204,8 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
if (test_and_set_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
- ipoib_warn(priv, "multicast group " IPOIB_GID_FMT
- " already attached\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_warn(priv, "multicast group %pI6 already attached\n",
+ mcast->mcmember.mgid.raw);
return 0;
}
@@ -215,9 +213,8 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
ret = ipoib_mcast_attach(dev, be16_to_cpu(mcast->mcmember.mlid),
&mcast->mcmember.mgid, set_qkey);
if (ret < 0) {
- ipoib_warn(priv, "couldn't attach QP to multicast group "
- IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_warn(priv, "couldn't attach QP to multicast group %pI6\n",
+ mcast->mcmember.mgid.raw);
clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags);
return ret;
@@ -248,9 +245,8 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
mcast->ah = ah;
spin_unlock_irq(&priv->lock);
- ipoib_dbg_mcast(priv, "MGID " IPOIB_GID_FMT
- " AV %p, LID 0x%04x, SL %d\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid),
+ ipoib_dbg_mcast(priv, "MGID %pI6 AV %p, LID 0x%04x, SL %d\n",
+ mcast->mcmember.mgid.raw,
mcast->ah->ah,
be16_to_cpu(mcast->mcmember.mlid),
mcast->mcmember.sl);
@@ -295,9 +291,8 @@ ipoib_mcast_sendonly_join_complete(int status,
if (status) {
if (mcast->logcount++ < 20)
- ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for "
- IPOIB_GID_FMT ", status %d\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid), status);
+ ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for %pI6, status %d\n",
+ mcast->mcmember.mgid.raw, status);
/* Flush out any queued packets */
netif_tx_lock_bh(dev);
@@ -356,9 +351,8 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
ipoib_warn(priv, "ib_sa_join_multicast failed (ret = %d)\n",
ret);
} else {
- ipoib_dbg_mcast(priv, "no multicast record for " IPOIB_GID_FMT
- ", starting join\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_dbg_mcast(priv, "no multicast record for %pI6, starting join\n",
+ mcast->mcmember.mgid.raw);
}
return ret;
@@ -386,9 +380,8 @@ static int ipoib_mcast_join_complete(int status,
struct net_device *dev = mcast->dev;
struct ipoib_dev_priv *priv = netdev_priv(dev);
- ipoib_dbg_mcast(priv, "join completion for " IPOIB_GID_FMT
- " (status %d)\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid), status);
+ ipoib_dbg_mcast(priv, "join completion for %pI6 (status %d)\n",
+ mcast->mcmember.mgid.raw, status);
/* We trap for port events ourselves. */
if (status == -ENETRESET)
@@ -417,15 +410,11 @@ static int ipoib_mcast_join_complete(int status,
if (mcast->logcount++ < 20) {
if (status == -ETIMEDOUT) {
- ipoib_dbg_mcast(priv, "multicast join failed for " IPOIB_GID_FMT
- ", status %d\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid),
- status);
+ ipoib_dbg_mcast(priv, "multicast join failed for %pI6, status %d\n",
+ mcast->mcmember.mgid.raw, status);
} else {
- ipoib_warn(priv, "multicast join failed for "
- IPOIB_GID_FMT ", status %d\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid),
- status);
+ ipoib_warn(priv, "multicast join failed for %pI6, status %d\n",
+ mcast->mcmember.mgid.raw, status);
}
}
@@ -457,8 +446,7 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
ib_sa_comp_mask comp_mask;
int ret = 0;
- ipoib_dbg_mcast(priv, "joining MGID " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_dbg_mcast(priv, "joining MGID %pI6\n", mcast->mcmember.mgid.raw);
rec.mgid = mcast->mcmember.mgid;
rec.port_gid = priv->local_gid;
@@ -643,8 +631,8 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
ib_sa_free_multicast(mcast->mc);
if (test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
- ipoib_dbg_mcast(priv, "leaving MGID " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_dbg_mcast(priv, "leaving MGID %pI6\n",
+ mcast->mcmember.mgid.raw);
/* Remove ourselves from the multicast group */
ret = ib_detach_mcast(priv->qp, &mcast->mcmember.mgid,
@@ -675,8 +663,8 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
mcast = __ipoib_mcast_find(dev, mgid);
if (!mcast) {
/* Let's create a new send only group now */
- ipoib_dbg_mcast(priv, "setting up send only multicast group for "
- IPOIB_GID_FMT "\n", IPOIB_GID_RAW_ARG(mgid));
+ ipoib_dbg_mcast(priv, "setting up send only multicast group for %pI6\n",
+ mgid);
mcast = ipoib_mcast_alloc(dev, 0);
if (!mcast) {
@@ -809,14 +797,14 @@ void ipoib_mcast_restart_task(struct work_struct *work)
/* ignore group which is directly joined by userspace */
if (test_bit(IPOIB_FLAG_UMCAST, &priv->flags) &&
!ib_sa_get_mcmember_rec(priv->ca, priv->port, &mgid, &rec)) {
- ipoib_dbg_mcast(priv, "ignoring multicast entry for mgid "
- IPOIB_GID_FMT "\n", IPOIB_GID_ARG(mgid));
+ ipoib_dbg_mcast(priv, "ignoring multicast entry for mgid %pI6\n",
+ mgid.raw);
continue;
}
/* Not found or send-only group, let's add a new entry */
- ipoib_dbg_mcast(priv, "adding multicast entry for mgid "
- IPOIB_GID_FMT "\n", IPOIB_GID_ARG(mgid));
+ ipoib_dbg_mcast(priv, "adding multicast entry for mgid %pI6\n",
+ mgid.raw);
nmcast = ipoib_mcast_alloc(dev, 0);
if (!nmcast) {
@@ -849,8 +837,8 @@ void ipoib_mcast_restart_task(struct work_struct *work)
list_for_each_entry_safe(mcast, tmcast, &priv->multicast_list, list) {
if (!test_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags) &&
!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
- ipoib_dbg_mcast(priv, "deleting multicast group " IPOIB_GID_FMT "\n",
- IPOIB_GID_ARG(mcast->mcmember.mgid));
+ ipoib_dbg_mcast(priv, "deleting multicast group %pI6\n",
+ mcast->mcmember.mgid.raw);
rb_erase(&mcast->rb_node, &priv->multicast_tree);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 1e5b6446231d..12876392516e 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -119,6 +119,14 @@ error:
iscsi_conn_failure(conn, rc);
}
+static int iscsi_iser_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
+{
+ struct iscsi_iser_task *iser_task = task->dd_data;
+
+ task->hdr = (struct iscsi_hdr *)&iser_task->desc.iscsi_header;
+ task->hdr_max = sizeof(iser_task->desc.iscsi_header);
+ return 0;
+}
/**
* iscsi_iser_task_init - Initialize task
@@ -180,25 +188,26 @@ static int
iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
struct iscsi_task *task)
{
- struct iscsi_data hdr;
+ struct iscsi_r2t_info *r2t = &task->unsol_r2t;
+ struct iscsi_data hdr;
int error = 0;
/* Send data-out PDUs while there's still unsolicited data to send */
- while (task->unsol_count > 0) {
- iscsi_prep_unsolicit_data_pdu(task, &hdr);
+ while (iscsi_task_has_unsol_data(task)) {
+ iscsi_prep_data_out_pdu(task, r2t, &hdr);
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
- hdr.itt, task->data_count);
+ hdr.itt, r2t->data_count);
/* the buffer description has been passed with the command */
/* Send the command */
error = iser_send_data_out(conn, task, &hdr);
if (error) {
- task->unsol_datasn--;
+ r2t->datasn--;
goto iscsi_iser_task_xmit_unsol_data_exit;
}
- task->unsol_count -= task->data_count;
+ r2t->sent += r2t->data_count;
debug_scsi("Need to send %d more as data-out PDUs\n",
- task->unsol_count);
+ r2t->data_length - r2t->sent);
}
iscsi_iser_task_xmit_unsol_data_exit:
@@ -220,7 +229,7 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
task->itt, scsi_bufflen(task->sc),
- task->imm_count, task->unsol_count);
+ task->imm_count, task->unsol_r2t.data_length);
}
debug_scsi("task deq [cid %d itt 0x%x]\n",
@@ -235,7 +244,7 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
}
/* Send unsolicited data-out PDU(s) if necessary */
- if (task->unsol_count)
+ if (iscsi_task_has_unsol_data(task))
error = iscsi_iser_task_xmit_unsol_data(conn, task);
iscsi_iser_task_xmit_exit:
@@ -244,13 +253,15 @@ iscsi_iser_task_xmit(struct iscsi_task *task)
return error;
}
-static void
-iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
+static void iscsi_iser_cleanup_task(struct iscsi_task *task)
{
struct iscsi_iser_task *iser_task = task->dd_data;
- /* mgmt tasks do not need special cleanup */
- if (!task->sc)
+ /*
+ * mgmt tasks do not need special cleanup and we do not
+ * allocate anything in the init task callout
+ */
+ if (!task->sc || task->state == ISCSI_TASK_PENDING)
return;
if (iser_task->status == ISER_TASK_STATUS_STARTED) {
@@ -391,9 +402,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
- int i;
- struct iscsi_task *task;
- struct iscsi_iser_task *iser_task;
struct iser_conn *ib_conn;
shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
@@ -430,13 +438,6 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep,
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
- /* libiscsi setup itts, data and pool so just set desc fields */
- for (i = 0; i < session->cmds_max; i++) {
- task = session->cmds[i];
- iser_task = task->dd_data;
- task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
- task->hdr_max = sizeof(iser_task->desc.iscsi_header);
- }
return cls_session;
remove_host:
@@ -652,6 +653,7 @@ static struct iscsi_transport iscsi_iser_transport = {
.init_task = iscsi_iser_task_init,
.xmit_task = iscsi_iser_task_xmit,
.cleanup_task = iscsi_iser_cleanup_task,
+ .alloc_pdu = iscsi_iser_pdu_alloc,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 81a82628a5f1..861119593f2b 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -252,6 +252,9 @@ struct iser_conn {
wait_queue_head_t wait; /* waitq for conn/disconn */
atomic_t post_recv_buf_count; /* posted rx count */
atomic_t post_send_buf_count; /* posted tx count */
+ atomic_t unexpected_pdu_count;/* count of received *
+ * unexpected pdus *
+ * not yet retired */
char name[ISER_OBJECT_NAME_SIZE];
struct iser_page_vec *page_vec; /* represents SG to fmr maps*
* maps serialized as tx is*/
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index cdd283189047..e209cb8dd948 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -183,14 +183,8 @@ static int iser_post_receive_control(struct iscsi_conn *conn)
struct iser_regd_buf *regd_data;
struct iser_dto *recv_dto = NULL;
struct iser_device *device = iser_conn->ib_conn->device;
- int rx_data_size, err = 0;
-
- rx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
- if (rx_desc == NULL) {
- iser_err("Failed to alloc desc for post recv\n");
- return -ENOMEM;
- }
- rx_desc->type = ISCSI_RX;
+ int rx_data_size, err;
+ int posts, outstanding_unexp_pdus;
/* for the login sequence we must support rx of upto 8K; login is done
* after conn create/bind (connect) and conn stop/bind (reconnect),
@@ -201,46 +195,80 @@ static int iser_post_receive_control(struct iscsi_conn *conn)
else /* FIXME till user space sets conn->max_recv_dlength correctly */
rx_data_size = 128;
- rx_desc->data = kmalloc(rx_data_size, GFP_NOIO);
- if (rx_desc->data == NULL) {
- iser_err("Failed to alloc data buf for post recv\n");
- err = -ENOMEM;
- goto post_rx_kmalloc_failure;
- }
+ outstanding_unexp_pdus =
+ atomic_xchg(&iser_conn->ib_conn->unexpected_pdu_count, 0);
- recv_dto = &rx_desc->dto;
- recv_dto->ib_conn = iser_conn->ib_conn;
- recv_dto->regd_vector_len = 0;
+ /*
+ * in addition to the response buffer, replace those consumed by
+ * unexpected pdus.
+ */
+ for (posts = 0; posts < 1 + outstanding_unexp_pdus; posts++) {
+ rx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
+ if (rx_desc == NULL) {
+ iser_err("Failed to alloc desc for post recv %d\n",
+ posts);
+ err = -ENOMEM;
+ goto post_rx_cache_alloc_failure;
+ }
+ rx_desc->type = ISCSI_RX;
+ rx_desc->data = kmalloc(rx_data_size, GFP_NOIO);
+ if (rx_desc->data == NULL) {
+ iser_err("Failed to alloc data buf for post recv %d\n",
+ posts);
+ err = -ENOMEM;
+ goto post_rx_kmalloc_failure;
+ }
- regd_hdr = &rx_desc->hdr_regd_buf;
- memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
- regd_hdr->device = device;
- regd_hdr->virt_addr = rx_desc; /* == &rx_desc->iser_header */
- regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
+ recv_dto = &rx_desc->dto;
+ recv_dto->ib_conn = iser_conn->ib_conn;
+ recv_dto->regd_vector_len = 0;
- iser_reg_single(device, regd_hdr, DMA_FROM_DEVICE);
+ regd_hdr = &rx_desc->hdr_regd_buf;
+ memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
+ regd_hdr->device = device;
+ regd_hdr->virt_addr = rx_desc; /* == &rx_desc->iser_header */
+ regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
- iser_dto_add_regd_buff(recv_dto, regd_hdr, 0, 0);
+ iser_reg_single(device, regd_hdr, DMA_FROM_DEVICE);
- regd_data = &rx_desc->data_regd_buf;
- memset(regd_data, 0, sizeof(struct iser_regd_buf));
- regd_data->device = device;
- regd_data->virt_addr = rx_desc->data;
- regd_data->data_size = rx_data_size;
+ iser_dto_add_regd_buff(recv_dto, regd_hdr, 0, 0);
- iser_reg_single(device, regd_data, DMA_FROM_DEVICE);
+ regd_data = &rx_desc->data_regd_buf;
+ memset(regd_data, 0, sizeof(struct iser_regd_buf));
+ regd_data->device = device;
+ regd_data->virt_addr = rx_desc->data;
+ regd_data->data_size = rx_data_size;
- iser_dto_add_regd_buff(recv_dto, regd_data, 0, 0);
+ iser_reg_single(device, regd_data, DMA_FROM_DEVICE);
- err = iser_post_recv(rx_desc);
- if (!err)
- return 0;
+ iser_dto_add_regd_buff(recv_dto, regd_data, 0, 0);
+
+ err = iser_post_recv(rx_desc);
+ if (err) {
+ iser_err("Failed iser_post_recv for post %d\n", posts);
+ goto post_rx_post_recv_failure;
+ }
+ }
+ /* all posts successful */
+ return 0;
- /* iser_post_recv failed */
+post_rx_post_recv_failure:
iser_dto_buffs_release(recv_dto);
kfree(rx_desc->data);
post_rx_kmalloc_failure:
kmem_cache_free(ig.desc_cache, rx_desc);
+post_rx_cache_alloc_failure:
+ if (posts > 0) {
+ /*
+ * response buffer posted, but did not replace all unexpected
+ * pdu recv bufs. Ignore error, retry occurs next send
+ */
+ outstanding_unexp_pdus -= (posts - 1);
+ err = 0;
+ }
+ atomic_add(outstanding_unexp_pdus,
+ &iser_conn->ib_conn->unexpected_pdu_count);
+
return err;
}
@@ -274,8 +302,10 @@ int iser_conn_set_full_featured_mode(struct iscsi_conn *conn)
struct iscsi_iser_conn *iser_conn = conn->dd_data;
int i;
- /* no need to keep it in a var, we are after login so if this should
- * be negotiated, by now the result should be available here */
+ /*
+ * FIXME this value should be declared to the target during login with
+ * the MaxOutstandingUnexpectedPDUs key when supported
+ */
int initial_post_recv_bufs_num = ISER_MAX_RX_MISC_PDUS;
iser_dbg("Initially post: %d\n", initial_post_recv_bufs_num);
@@ -323,8 +353,7 @@ int iser_send_command(struct iscsi_conn *conn,
unsigned long edtl;
int err = 0;
struct iser_data_buf *data_buf;
-
- struct iscsi_cmd *hdr = task->hdr;
+ struct iscsi_cmd *hdr = (struct iscsi_cmd *)task->hdr;
struct scsi_cmnd *sc = task->sc;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
@@ -363,7 +392,7 @@ int iser_send_command(struct iscsi_conn *conn,
err = iser_prepare_write_cmd(task,
task->imm_count,
task->imm_count +
- task->unsol_count,
+ task->unsol_r2t.data_length,
edtl);
if (err)
goto send_command_error;
@@ -478,6 +507,7 @@ int iser_send_control(struct iscsi_conn *conn,
int err = 0;
struct iser_regd_buf *regd_buf;
struct iser_device *device;
+ unsigned char opcode;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
@@ -512,10 +542,15 @@ int iser_send_control(struct iscsi_conn *conn,
data_seg_len);
}
- if (iser_post_receive_control(conn) != 0) {
- iser_err("post_rcv_buff failed!\n");
- err = -ENOMEM;
- goto send_control_error;
+ opcode = task->hdr->opcode & ISCSI_OPCODE_MASK;
+
+ /* post recv buffer for response if one is expected */
+ if (!(opcode == ISCSI_OP_NOOP_OUT && task->hdr->itt == RESERVED_ITT)) {
+ if (iser_post_receive_control(conn) != 0) {
+ iser_err("post_rcv_buff failed!\n");
+ err = -ENOMEM;
+ goto send_control_error;
+ }
}
err = iser_post_send(mdesc);
@@ -586,6 +621,20 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
* parallel to the execution of iser_conn_term. So the code that waits *
* for the posted rx bufs refcount to become zero handles everything */
atomic_dec(&conn->ib_conn->post_recv_buf_count);
+
+ /*
+ * if an unexpected PDU was received then the recv wr consumed must
+ * be replaced, this is done in the next send of a control-type PDU
+ */
+ if (opcode == ISCSI_OP_NOOP_IN && hdr->itt == RESERVED_ITT) {
+ /* nop-in with itt = 0xffffffff */
+ atomic_inc(&conn->ib_conn->unexpected_pdu_count);
+ }
+ else if (opcode == ISCSI_OP_ASYNC_EVENT) {
+ /* asyncronous message */
+ atomic_inc(&conn->ib_conn->unexpected_pdu_count);
+ }
+ /* a reject PDU consumes the recv buf posted for the response */
}
void iser_snd_completion(struct iser_desc *tx_desc)
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index 26ff6214a81f..319b188145be 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -498,6 +498,7 @@ void iser_conn_init(struct iser_conn *ib_conn)
init_waitqueue_head(&ib_conn->wait);
atomic_set(&ib_conn->post_recv_buf_count, 0);
atomic_set(&ib_conn->post_send_buf_count, 0);
+ atomic_set(&ib_conn->unexpected_pdu_count, 0);
atomic_set(&ib_conn->refcount, 1);
INIT_LIST_HEAD(&ib_conn->conn_list);
spin_lock_init(&ib_conn->lock);
@@ -515,14 +516,14 @@ int iser_connect(struct iser_conn *ib_conn,
struct sockaddr *src, *dst;
int err = 0;
- sprintf(ib_conn->name,"%d.%d.%d.%d:%d",
- NIPQUAD(dst_addr->sin_addr.s_addr), dst_addr->sin_port);
+ sprintf(ib_conn->name, "%pI4:%d",
+ &dst_addr->sin_addr.s_addr, dst_addr->sin_port);
/* the device is known only --after-- address resolution */
ib_conn->device = NULL;
- iser_err("connecting to: %d.%d.%d.%d, port 0x%x\n",
- NIPQUAD(dst_addr->sin_addr), dst_addr->sin_port);
+ iser_err("connecting to: %pI4, port 0x%x\n",
+ &dst_addr->sin_addr, dst_addr->sin_port);
ib_conn->state = ISER_CONN_PENDING;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 5b8b533f2908..7c13db885bf6 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1514,15 +1514,7 @@ static ssize_t show_dgid(struct device *dev, struct device_attribute *attr,
target->state == SRP_TARGET_REMOVED)
return -ENODEV;
- return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[0]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[1]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[2]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[3]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[4]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[5]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[6]),
- be16_to_cpu(((__be16 *) target->path.dgid.raw)[7]));
+ return sprintf(buf, "%pI6\n", target->path.dgid.raw);
}
static ssize_t show_orig_dgid(struct device *dev,
@@ -1534,15 +1526,7 @@ static ssize_t show_orig_dgid(struct device *dev,
target->state == SRP_TARGET_REMOVED)
return -ENODEV;
- return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
- be16_to_cpu(target->orig_dgid[0]),
- be16_to_cpu(target->orig_dgid[1]),
- be16_to_cpu(target->orig_dgid[2]),
- be16_to_cpu(target->orig_dgid[3]),
- be16_to_cpu(target->orig_dgid[4]),
- be16_to_cpu(target->orig_dgid[5]),
- be16_to_cpu(target->orig_dgid[6]),
- be16_to_cpu(target->orig_dgid[7]));
+ return sprintf(buf, "%pI6\n", target->orig_dgid);
}
static ssize_t show_zero_req_lim(struct device *dev,
@@ -1883,19 +1867,12 @@ static ssize_t srp_create_target(struct device *dev,
shost_printk(KERN_DEBUG, target->scsi_host, PFX
"new target: id_ext %016llx ioc_guid %016llx pkey %04x "
- "service_id %016llx dgid %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ "service_id %016llx dgid %pI6\n",
(unsigned long long) be64_to_cpu(target->id_ext),
(unsigned long long) be64_to_cpu(target->ioc_guid),
be16_to_cpu(target->path.pkey),
(unsigned long long) be64_to_cpu(target->service_id),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[0]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[2]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[4]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[6]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[8]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[10]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[12]),
- (int) be16_to_cpu(*(__be16 *) &target->path.dgid.raw[14]));
+ target->path.dgid.raw);
ret = srp_create_target_ib(target);
if (ret)
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 69e674ecf19a..db22fd9b4cf2 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -101,7 +101,7 @@ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
if (cpu_is_omap24xx()) {
int i;
for (i = 0; i < omap_kp->rows; i++)
- disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ disable_irq(gpio_to_irq(row_gpios[i]));
} else
/* disable keyboard interrupt and schedule for handling */
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
@@ -224,7 +224,7 @@ static void omap_kp_tasklet(unsigned long data)
if (cpu_is_omap24xx()) {
int i;
for (i = 0; i < omap_kp_data->rows; i++)
- enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ enable_irq(gpio_to_irq(row_gpios[i]));
} else {
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
kp_cur_group = -1;
@@ -397,7 +397,7 @@ static int __init omap_kp_probe(struct platform_device *pdev)
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
} else {
for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
- if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
+ if (request_irq(gpio_to_irq(row_gpios[irq_idx]),
omap_kp_interrupt,
IRQF_TRIGGER_FALLING,
"omap-keypad", omap_kp) < 0)
@@ -438,7 +438,7 @@ static int omap_kp_remove(struct platform_device *pdev)
gpio_free(col_gpios[i]);
for (i = 0; i < omap_kp->rows; i++) {
gpio_free(row_gpios[i]);
- free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
+ free_irq(gpio_to_irq(row_gpios[i]), 0);
}
} else {
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
index 6d30c6d334c3..0d2fc64a5e1c 100644
--- a/drivers/input/keyboard/pxa27x_keypad.c
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -475,7 +475,7 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
goto failed_free_mem;
}
- keypad->clk = clk_get(&pdev->dev, "KBDCLK");
+ keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk);
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index c600ab7f93e8..5c8a1bcf7ca7 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
+#include <linux/clk.h>
#include <linux/io.h>
#include <asm/sh_keysc.h>
@@ -39,6 +40,7 @@ static const struct {
struct sh_keysc_priv {
void __iomem *iomem_base;
+ struct clk *clk;
unsigned long last_keys;
struct input_dev *input;
struct sh_keysc_info pdata;
@@ -125,6 +127,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
struct sh_keysc_info *pdata;
struct resource *res;
struct input_dev *input;
+ char clk_name[8];
int i, k;
int irq, error;
@@ -165,11 +168,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
goto err1;
}
+ snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
+ priv->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ error = PTR_ERR(priv->clk);
+ goto err2;
+ }
+
priv->input = input_allocate_device();
if (!priv->input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
- goto err2;
+ goto err3;
}
input = priv->input;
@@ -187,7 +198,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
- goto err3;
+ goto err4;
}
for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
@@ -199,18 +210,22 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
- goto err4;
+ goto err5;
}
+ clk_enable(priv->clk);
+
iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
return 0;
- err4:
+ err5:
free_irq(irq, pdev);
- err3:
+ err4:
input_free_device(input);
+ err3:
+ clk_put(priv->clk);
err2:
iounmap(priv->iomem_base);
err1:
@@ -230,6 +245,9 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
free_irq(platform_get_irq(pdev, 0), pdev);
iounmap(priv->iomem_base);
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+
platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 27d70d326ff3..da3c3a5d2689 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -79,7 +79,7 @@ config SERIO_PARKBD
config SERIO_RPCKBD
tristate "Acorn RiscPC keyboard controller"
- depends on ARCH_ACORN || ARCH_CLPS7500
+ depends on ARCH_ACORN
default y
help
Say Y here if you have the Acorn RiscPC and want to use an AT
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index b9b7fc6ff1eb..e1ece89fe922 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -697,7 +697,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
struct ads7846 *ts = container_of(handle, struct ads7846, timer);
int status = 0;
- spin_lock_irq(&ts->lock);
+ spin_lock(&ts->lock);
if (unlikely(!get_pendown_state(ts) ||
device_suspended(&ts->spi->dev))) {
@@ -728,7 +728,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
}
- spin_unlock_irq(&ts->lock);
+ spin_unlock(&ts->lock);
return HRTIMER_NORESTART;
}
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index ba648750a8d9..1d11e2be9ef8 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -31,7 +31,7 @@
#include <linux/interrupt.h>
#include <linux/wm97xx.h>
#include <linux/io.h>
-#include <mach/pxa-regs.h>
+#include <mach/regs-ac97.h>
#define VERSION "0.13"
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
index 550e80f390a6..0aa66ec4cbdd 100644
--- a/drivers/isdn/capi/capifs.c
+++ b/drivers/isdn/capi/capifs.c
@@ -156,8 +156,8 @@ void capifs_new_ncci(unsigned int number, dev_t device)
if (!inode)
return;
inode->i_ino = number+2;
- inode->i_uid = config.setuid ? config.uid : current->fsuid;
- inode->i_gid = config.setgid ? config.gid : current->fsgid;
+ inode->i_uid = config.setuid ? config.uid : current_fsuid();
+ inode->i_gid = config.setgid ? config.gid : current_fsgid();
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device);
//inode->i_op = &capifs_file_inode_operations;
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index c2bd97d29273..2a4ce96f04bd 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -17,8 +17,6 @@
#include <linux/crc-ccitt.h>
#include <linux/bitrev.h>
-//#define GIG_M10x_STUFF_VOICE_DATA
-
/* check if byte must be stuffed/escaped
* I'm not sure which data should be encoded.
* Therefore I will go the hard way and decode every value
@@ -147,19 +145,17 @@ static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
}
byte_stuff:
c ^= PPP_TRANS;
-#ifdef CONFIG_GIGASET_DEBUG
if (unlikely(!muststuff(c)))
gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
-#endif
} else if (unlikely(c == PPP_FLAG)) {
if (unlikely(inputstate & INS_skip_frame)) {
- if (!(inputstate & INS_have_data)) { /* 7E 7E */
#ifdef CONFIG_GIGASET_DEBUG
+ if (!(inputstate & INS_have_data)) { /* 7E 7E */
++bcs->emptycount;
-#endif
} else
gig_dbg(DEBUG_HDLC,
"7e----------------------------");
+#endif
/* end of frame */
error = 1;
@@ -226,11 +222,9 @@ byte_stuff:
}
break;
-#ifdef CONFIG_GIGASET_DEBUG
} else if (unlikely(muststuff(c))) {
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
-#endif
}
/* add character */
@@ -394,20 +388,16 @@ void gigaset_m10x_input(struct inbuf_t *inbuf)
inbuf->inputstate &= ~INS_DLE_char;
switch (c) {
case 'X': /*begin of command*/
-#ifdef CONFIG_GIGASET_DEBUG
if (inbuf->inputstate & INS_command)
- dev_err(cs->dev,
+ dev_warn(cs->dev,
"received <DLE> 'X' in command mode\n");
-#endif
inbuf->inputstate |=
INS_command | INS_DLE_command;
break;
case '.': /*end of command*/
-#ifdef CONFIG_GIGASET_DEBUG
if (!(inbuf->inputstate & INS_command))
- dev_err(cs->dev,
+ dev_warn(cs->dev,
"received <DLE> '.' in hdlc mode\n");
-#endif
inbuf->inputstate &= cs->dle ?
~(INS_DLE_command|INS_command)
: ~INS_DLE_command;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 3f11910c7ccd..18dd8aacbe8d 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -2067,7 +2067,7 @@ static int gigaset_initbcshw(struct bc_state *bcs)
bcs->hw.bas = ubc = kmalloc(sizeof(struct bas_bc_state), GFP_KERNEL);
if (!ubc) {
- err("could not allocate bas_bc_state");
+ pr_err("out of memory\n");
return 0;
}
@@ -2081,7 +2081,7 @@ static int gigaset_initbcshw(struct bc_state *bcs)
ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL;
ubc->numsub = 0;
if (!(ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL))) {
- err("could not allocate isochronous output buffer");
+ pr_err("out of memory\n");
kfree(ubc);
bcs->hw.bas = NULL;
return 0;
@@ -2136,8 +2136,10 @@ static int gigaset_initcshw(struct cardstate *cs)
struct bas_cardstate *ucs;
cs->hw.bas = ucs = kmalloc(sizeof *ucs, GFP_KERNEL);
- if (!ucs)
+ if (!ucs) {
+ pr_err("out of memory\n");
return 0;
+ }
ucs->urb_cmd_in = NULL;
ucs->urb_cmd_out = NULL;
@@ -2503,12 +2505,11 @@ static int __init bas_gigaset_init(void)
/* register this driver with the USB subsystem */
result = usb_register(&gigaset_usb_driver);
if (result < 0) {
- err("usb_register failed (error %d)", -result);
+ pr_err("error %d registering USB driver\n", -result);
goto error;
}
- info(DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ pr_info(DRIVER_DESC "\n");
return 0;
error:
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index 9d3ce7718e58..0048ce98bfa8 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -580,7 +580,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
} else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
skb_reserve(bcs->skb, HW_HDR_LEN);
else {
- warn("could not allocate skb");
+ pr_err("out of memory\n");
bcs->inputstate |= INS_skip_frame;
}
@@ -634,20 +634,20 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
gig_dbg(DEBUG_INIT, "allocating cs");
if (!(cs = alloc_cs(drv))) {
- err("maximum number of devices exceeded");
+ pr_err("maximum number of devices exceeded\n");
return NULL;
}
gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL);
if (!cs->bcs) {
- err("out of memory");
+ pr_err("out of memory\n");
goto error;
}
gig_dbg(DEBUG_INIT, "allocating inbuf");
cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
if (!cs->inbuf) {
- err("out of memory");
+ pr_err("out of memory\n");
goto error;
}
@@ -690,7 +690,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
for (i = 0; i < channels; ++i) {
gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
- err("could not allocate channel %d data", i);
+ pr_err("could not allocate channel %d data\n", i);
goto error;
}
}
@@ -720,17 +720,15 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
gig_dbg(DEBUG_INIT, "setting up iif");
if (!gigaset_register_to_LL(cs, modulename)) {
- err("register_isdn failed");
+ pr_err("error registering ISDN device\n");
goto error;
}
make_valid(cs, VALID_ID);
++cs->cs_init;
gig_dbg(DEBUG_INIT, "setting up hw");
- if (!cs->ops->initcshw(cs)) {
- err("could not allocate device specific data");
+ if (!cs->ops->initcshw(cs))
goto error;
- }
++cs->cs_init;
@@ -836,7 +834,7 @@ static void cleanup_cs(struct cardstate *cs)
for (i = 0; i < cs->channels; ++i) {
gigaset_freebcs(cs->bcs + i);
if (!gigaset_initbcs(cs->bcs + i, cs, i))
- break; //FIXME error handling
+ pr_err("could not allocate channel %d data\n", i);
}
if (cs->waiting) {
@@ -1120,8 +1118,7 @@ static int __init gigaset_init_module(void)
if (gigaset_debuglevel == 1)
gigaset_debuglevel = DEBUG_DEFAULT;
- info(DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ pr_info(DRIVER_DESC "\n");
return 0;
}
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 5cbf64d850ee..e582a4887bc1 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -203,15 +203,6 @@ struct reply_t gigaset_tab_nocid_m10x[]= /* with dle mode */
{EV_TIMEOUT, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
{RSP_ERROR, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
{RSP_OK, 121,121, -1, 0, 0, {ACT_GOTVER, ACT_INIT}},
-#if 0
- {EV_TIMEOUT, 120,121, -1, 130, 5, {ACT_FAILVER}, "^SGCI=1\r"},
- {RSP_ERROR, 120,121, -1, 130, 5, {ACT_FAILVER}, "^SGCI=1\r"},
- {RSP_OK, 121,121, -1, 130, 5, {ACT_GOTVER}, "^SGCI=1\r"},
-
- {RSP_OK, 130,130, -1, 0, 0, {ACT_INIT}},
- {RSP_ERROR, 130,130, -1, 0, 0, {ACT_FAILINIT}},
- {EV_TIMEOUT, 130,130, -1, 0, 0, {ACT_FAILINIT}},
-#endif
/* leave dle mode */
{RSP_INIT, 0, 0,SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"},
@@ -260,10 +251,6 @@ struct reply_t gigaset_tab_nocid_m10x[]= /* with dle mode */
{RSP_INIT, 0, 0,SEQ_NOCID, 0, 0, {ACT_ABORTCID}},
/* reset */
-#if 0
- {RSP_INIT, 0, 0,SEQ_SHUTDOWN, 503, 5, {0}, "^SGCI=0\r"},
- {RSP_OK, 503,503, -1, 504, 5, {0}, "Z\r"},
-#endif
{RSP_INIT, 0, 0,SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"},
{RSP_OK, 504,504, -1, 0, 0, {ACT_SDOWN}},
{RSP_ERROR, 501,599, -1, 0, 0, {ACT_FAILSDOWN}},
@@ -391,24 +378,6 @@ struct reply_t gigaset_tab_cid_m10x[] = /* for M10x */
};
-#if 0
-static struct reply_t tab_nocid[]= /* no dle mode */ //FIXME
-{
- /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
-
- {RSP_ANY, -1, -1, -1, -1,-1, ACT_WARN, NULL},
- {RSP_LAST,0,0,0,0,0,0}
-};
-
-static struct reply_t tab_cid[] = /* no dle mode */ //FIXME
-{
- /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
-
- {RSP_ANY, -1, -1, -1, -1,-1, ACT_WARN, NULL},
- {RSP_LAST,0,0,0,0,0,0}
-};
-#endif
-
static const struct resp_type_t resp_type[] =
{
/*{"", RSP_EMPTY, RT_NOTHING},*/
@@ -665,13 +634,8 @@ void gigaset_handle_modem_response(struct cardstate *cs)
dev_err(cs->dev, "out of memory\n");
++curarg;
}
-#ifdef CONFIG_GIGASET_DEBUG
- if (!event->ptr)
- gig_dbg(DEBUG_CMD, "string==NULL");
- else
- gig_dbg(DEBUG_CMD, "string==%s",
- (char *) event->ptr);
-#endif
+ gig_dbg(DEBUG_CMD, "string==%s",
+ event->ptr ? (char *) event->ptr : "NULL");
break;
case RT_ZCAU:
event->parameter = -1;
@@ -697,9 +661,7 @@ void gigaset_handle_modem_response(struct cardstate *cs)
++curarg;
} else
event->parameter = -1;
-#ifdef CONFIG_GIGASET_DEBUG
gig_dbg(DEBUG_CMD, "parameter==%d", event->parameter);
-#endif
break;
}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index 003752954993..747178f03d2c 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -16,6 +16,9 @@
#ifndef GIGASET_H
#define GIGASET_H
+/* define global prefix for pr_ macros in linux/kernel.h */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/types.h>
@@ -97,23 +100,6 @@ enum debuglevel {
activated */
};
-/* Kernel message macros for situations where dev_printk and friends cannot be
- * used for lack of reliable access to a device structure.
- * linux/usb.h already contains these but in an obsolete form which clutters
- * the log needlessly, and according to the USB maintainer those should be
- * removed rather than fixed anyway.
- */
-#undef err
-#undef info
-#undef warn
-
-#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
- format "\n" , ## arg)
-#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \
- format "\n" , ## arg)
-#define warn(format, arg...) printk(KERN_WARNING KBUILD_MODNAME ": " \
- format "\n" , ## arg)
-
#ifdef CONFIG_GIGASET_DEBUG
#define gig_dbg(level, format, arg...) \
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 3c127a8cbaf2..69a702f0db93 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -42,7 +42,7 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
unsigned skblen;
if (!(cs = gigaset_get_cs_by_id(driverID))) {
- err("%s: invalid driver ID (%d)", __func__, driverID);
+ pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
return -ENODEV;
}
if (channel < 0 || channel >= cs->channels) {
@@ -119,7 +119,7 @@ static int command_from_LL(isdn_ctrl *cntrl)
gigaset_debugdrivers();
if (!cs) {
- err("%s: invalid driver ID (%d)", __func__, cntrl->driver);
+ pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
return -ENODEV;
}
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index 521951a898ec..311e7ca0fb01 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -107,7 +107,7 @@ static int if_config(struct cardstate *cs, int *arg)
return -EBUSY;
if (!cs->connected) {
- err("not connected!");
+ pr_err("%s: not connected\n", __func__);
return -ENODEV;
}
@@ -143,9 +143,6 @@ static const struct tty_operations if_ops = {
.set_termios = if_set_termios,
.throttle = if_throttle,
.unthrottle = if_unthrottle,
-#if 0
- .break_ctl = serial_break,
-#endif
.tiocmget = if_tiocmget,
.tiocmset = if_tiocmset,
};
@@ -188,7 +185,7 @@ static void if_close(struct tty_struct *tty, struct file *filp)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return;
}
@@ -222,7 +219,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file,
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -297,7 +294,7 @@ static int if_tiocmget(struct tty_struct *tty, struct file *file)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -323,7 +320,7 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file,
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -354,7 +351,7 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -388,7 +385,7 @@ static int if_write_room(struct tty_struct *tty)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -420,7 +417,7 @@ static int if_chars_in_buffer(struct tty_struct *tty)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
@@ -451,7 +448,7 @@ static void if_throttle(struct tty_struct *tty)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return;
}
@@ -474,7 +471,7 @@ static void if_unthrottle(struct tty_struct *tty)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return;
}
@@ -501,7 +498,7 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
- err("cs==NULL in %s", __func__);
+ pr_err("%s: no cardstate\n", __func__);
return;
}
@@ -565,29 +562,6 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
cs->ops->set_line_ctrl(cs, cflag);
-#if 0
- //FIXME this hangs M101 [ts 2005-03-09]
- //FIXME do we need this?
- /*
- * Set flow control: well, I do not really now how to handle DTR/RTS.
- * Just do what we have seen with SniffUSB on Win98.
- */
- /* Drop DTR/RTS if no flow control otherwise assert */
- gig_dbg(DEBUG_IF, "%u: control_state %x",
- cs->minor_index, control_state);
- new_state = control_state;
- if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
- new_state |= TIOCM_DTR | TIOCM_RTS;
- else
- new_state &= ~(TIOCM_DTR | TIOCM_RTS);
- if (new_state != control_state) {
- gig_dbg(DEBUG_IF, "%u: new_state %x",
- cs->minor_index, new_state);
- gigaset_set_modem_ctrl(cs, control_state, new_state);
- control_state = new_state;
- }
-#endif
-
/* save off the modified port settings */
cs->control_state = control_state;
@@ -701,7 +675,7 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
ret = tty_register_driver(tty);
if (ret < 0) {
- warn("failed to register tty driver (error %d)", ret);
+ pr_err("error %d registering tty driver\n", ret);
goto error;
}
gig_dbg(DEBUG_IF, "tty driver initialized");
@@ -709,7 +683,7 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
return;
enomem:
- warn("could not allocate tty structures");
+ pr_err("out of memory\n");
error:
if (drv->tty)
put_tty_driver(drv->tty);
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index fbce5222d83c..b171e75cb52e 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -88,11 +88,9 @@ static inline int isowbuf_startwrite(struct isowbuf_t *iwb)
__func__);
return 0;
}
-#ifdef CONFIG_GIGASET_DEBUG
gig_dbg(DEBUG_ISO,
"%s: acquired iso write semaphore, data[write]=%02x, nbits=%d",
__func__, iwb->data[iwb->write], iwb->wbits);
-#endif
return 1;
}
@@ -173,13 +171,13 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
__func__, read, write, limit);
#ifdef CONFIG_GIGASET_DEBUG
if (unlikely(size < 0 || size > BAS_OUTBUFPAD)) {
- err("invalid size %d", size);
+ pr_err("invalid size %d\n", size);
return -EINVAL;
}
src = iwb->read;
if (unlikely(limit > BAS_OUTBUFSIZE + BAS_OUTBUFPAD ||
(read < src && limit >= src))) {
- err("isoc write buffer frame reservation violated");
+ pr_err("isoc write buffer frame reservation violated\n");
return -EFAULT;
}
#endif
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 07052ed2a0c5..ac245e7e96a5 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -16,7 +16,6 @@
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
-#include <linux/poll.h>
#include <linux/completion.h>
/* Version Information */
@@ -408,7 +407,7 @@ static int gigaset_initcshw(struct cardstate *cs)
int rc;
if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {
- err("%s: out of memory!", __func__);
+ pr_err("out of memory\n");
return 0;
}
@@ -416,7 +415,7 @@ static int gigaset_initcshw(struct cardstate *cs)
cs->hw.ser->dev.id = cs->minor_index;
cs->hw.ser->dev.dev.release = gigaset_device_release;
if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) {
- err("error %d registering platform device", rc);
+ pr_err("error %d registering platform device\n", rc);
kfree(cs->hw.ser);
cs->hw.ser = NULL;
return 0;
@@ -514,11 +513,10 @@ gigaset_tty_open(struct tty_struct *tty)
gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
- info(DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ pr_info(DRIVER_DESC "\n");
if (!driver) {
- err("%s: no driver structure", __func__);
+ pr_err("%s: no driver structure\n", __func__);
return -ENODEV;
}
@@ -571,11 +569,10 @@ gigaset_tty_close(struct tty_struct *tty)
}
/* prevent other callers from entering ldisc methods */
- /* FIXME: should use the tty state flags */
tty->disc_data = NULL;
if (!cs->hw.ser)
- err("%s: no hw cardstate", __func__);
+ pr_err("%s: no hw cardstate\n", __func__);
else {
/* wait for running methods to finish */
if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
@@ -673,18 +670,6 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
}
/*
- * Poll on the tty.
- * Unused, always return zero.
- *
- * FIXME: should probably return an exception - especially on hangup
- */
-static unsigned int
-gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
-{
- return 0;
-}
-
-/*
* Called by the tty driver when a block of data has been received.
* Will not be re-entered while running but other ldisc functions
* may be called in parallel.
@@ -773,7 +758,6 @@ static struct tty_ldisc_ops gigaset_ldisc = {
.read = gigaset_tty_read,
.write = gigaset_tty_write,
.ioctl = gigaset_tty_ioctl,
- .poll = gigaset_tty_poll,
.receive_buf = gigaset_tty_receive,
.write_wakeup = gigaset_tty_wakeup,
};
@@ -788,7 +772,7 @@ static int __init ser_gigaset_init(void)
gig_dbg(DEBUG_INIT, "%s", __func__);
if ((rc = platform_driver_register(&device_driver)) != 0) {
- err("error %d registering platform driver", rc);
+ pr_err("error %d registering platform driver\n", rc);
return rc;
}
@@ -799,7 +783,7 @@ static int __init ser_gigaset_init(void)
goto error;
if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) {
- err("error %d registering line discipline", rc);
+ pr_err("error %d registering line discipline\n", rc);
goto error;
}
@@ -826,7 +810,7 @@ static void __exit ser_gigaset_exit(void)
}
if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0)
- err("error %d unregistering line discipline", rc);
+ pr_err("error %d unregistering line discipline\n", rc);
platform_driver_unregister(&device_driver);
}
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 4661830a49db..fba61f670527 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -407,7 +407,7 @@ static void gigaset_read_int_callback(struct urb *urb)
spin_lock_irqsave(&cs->lock, flags);
if (!cs->connected) {
spin_unlock_irqrestore(&cs->lock, flags);
- err("%s: disconnected", __func__);
+ pr_err("%s: disconnected\n", __func__);
return;
}
r = usb_submit_urb(urb, GFP_ATOMIC);
@@ -440,7 +440,7 @@ static void gigaset_write_bulk_callback(struct urb *urb)
spin_lock_irqsave(&cs->lock, flags);
if (!cs->connected) {
- err("%s: not connected", __func__);
+ pr_err("%s: disconnected\n", __func__);
} else {
cs->hw.usb->busy = 0;
tasklet_schedule(&cs->write_tasklet);
@@ -612,8 +612,10 @@ static int gigaset_initcshw(struct cardstate *cs)
cs->hw.usb = ucs =
kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
- if (!ucs)
+ if (!ucs) {
+ pr_err("out of memory\n");
return 0;
+ }
ucs->bchars[0] = 0;
ucs->bchars[1] = 0;
@@ -936,13 +938,11 @@ static int __init usb_gigaset_init(void)
/* register this driver with the USB subsystem */
result = usb_register(&gigaset_usb_driver);
if (result < 0) {
- err("usb_gigaset: usb_register failed (error %d)",
- -result);
+ pr_err("error %d registering USB driver\n", -result);
goto error;
}
- info(DRIVER_AUTHOR);
- info(DRIVER_DESC);
+ pr_info(DRIVER_DESC "\n");
return 0;
error:
diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c
index 10760b3c5eb5..b029d130eb21 100644
--- a/drivers/isdn/hardware/eicon/di.c
+++ b/drivers/isdn/hardware/eicon/di.c
@@ -353,13 +353,13 @@ void scom_clear_int(ADAPTER * a)
/*------------------------------------------------------------------*/
/* return code handler */
/*------------------------------------------------------------------*/
-byte isdn_rc(ADAPTER * a,
- byte Rc,
- byte Id,
- byte Ch,
- word Ref,
- dword extended_info_type,
- dword extended_info)
+static byte isdn_rc(ADAPTER *a,
+ byte Rc,
+ byte Id,
+ byte Ch,
+ word Ref,
+ dword extended_info_type,
+ dword extended_info)
{
ENTITY * this;
byte e_no;
@@ -555,13 +555,13 @@ byte isdn_rc(ADAPTER * a,
/*------------------------------------------------------------------*/
/* indication handler */
/*------------------------------------------------------------------*/
-byte isdn_ind(ADAPTER * a,
- byte Ind,
- byte Id,
- byte Ch,
- PBUFFER * RBuffer,
- byte MInd,
- word MLength)
+static byte isdn_ind(ADAPTER *a,
+ byte Ind,
+ byte Id,
+ byte Ch,
+ PBUFFER *RBuffer,
+ byte MInd,
+ word MLength)
{
ENTITY * this;
word clength;
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
index 599fed88222d..4cc94f200b72 100644
--- a/drivers/isdn/hardware/eicon/message.c
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -592,7 +592,7 @@ word api_put(APPL * appl, CAPI_MSG * msg)
/* api_parse function, check the format of api messages */
/*------------------------------------------------------------------*/
-word api_parse(byte * msg, word length, byte * format, API_PARSE * parms)
+static word api_parse(byte *msg, word length, byte *format, API_PARSE *parms)
{
word i;
word p;
@@ -631,7 +631,7 @@ word api_parse(byte * msg, word length, byte * format, API_PARSE * parms)
return false;
}
-void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
+static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
{
word i, j, n = 0;
byte *p;
@@ -663,7 +663,7 @@ void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
out->parms[i].length = 0;
}
-void api_load_msg(API_SAVE *in, API_PARSE *out)
+static void api_load_msg(API_SAVE *in, API_PARSE *out)
{
word i;
@@ -3414,7 +3414,8 @@ byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci,
return false;
}
-byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+static byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
+ PLCI *plci, APPL *appl, API_PARSE *parms)
{
word command;
word i;
@@ -3742,7 +3743,8 @@ byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * p
}
-byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a,
+ PLCI *plci, APPL *appl, API_PARSE *msg)
{
word indication;
@@ -4074,7 +4076,8 @@ capi_callback_suffix:
}
-void control_rc(PLCI * plci, byte req, byte rc, byte ch, byte global_req, byte nl_rc)
+static void control_rc(PLCI *plci, byte req, byte rc, byte ch, byte global_req,
+ byte nl_rc)
{
dword Id;
dword rId;
@@ -4740,7 +4743,7 @@ void control_rc(PLCI * plci, byte req, byte rc, byte ch, byte global_req, byte
}
}
-void data_rc(PLCI * plci, byte ch)
+static void data_rc(PLCI *plci, byte ch)
{
dword Id;
DIVA_CAPI_ADAPTER * a;
@@ -4776,7 +4779,7 @@ void data_rc(PLCI * plci, byte ch)
}
}
-void data_ack(PLCI * plci, byte ch)
+static void data_ack(PLCI *plci, byte ch)
{
dword Id;
DIVA_CAPI_ADAPTER * a;
@@ -4802,7 +4805,7 @@ void data_ack(PLCI * plci, byte ch)
}
}
-void sig_ind(PLCI * plci)
+static void sig_ind(PLCI *plci)
{
dword x_Id;
dword Id;
@@ -6170,7 +6173,7 @@ static void SendSetupInfo(APPL * appl, PLCI * plci, dword Id, byte * * par
}
-void SendInfo(PLCI * plci, dword Id, byte * * parms, byte iesent)
+static void SendInfo(PLCI *plci, dword Id, byte **parms, byte iesent)
{
word i;
word j;
@@ -6346,7 +6349,8 @@ void SendInfo(PLCI * plci, dword Id, byte * * parms, byte iesent)
}
-byte SendMultiIE(PLCI * plci, dword Id, byte * * parms, byte ie_type, dword info_mask, byte setupParse)
+static byte SendMultiIE(PLCI *plci, dword Id, byte **parms, byte ie_type,
+ dword info_mask, byte setupParse)
{
word i;
word j;
@@ -6465,7 +6469,7 @@ static void SendSSExtInd(APPL * appl, PLCI * plci, dword Id, byte * * parm
}
};
-void nl_ind(PLCI * plci)
+static void nl_ind(PLCI *plci)
{
byte ch;
word ncci;
@@ -7247,7 +7251,7 @@ void nl_ind(PLCI * plci)
/* find a free PLCI */
/*------------------------------------------------------------------*/
-word get_plci(DIVA_CAPI_ADAPTER * a)
+static word get_plci(DIVA_CAPI_ADAPTER *a)
{
word i,j;
PLCI * plci;
@@ -7406,7 +7410,7 @@ static void add_ie(PLCI * plci, byte code, byte * p, word p_length)
/* put a unstructured data into the buffer */
/*------------------------------------------------------------------*/
-void add_d(PLCI * plci, word length, byte * p)
+static void add_d(PLCI *plci, word length, byte *p)
{
word i;
@@ -7424,7 +7428,7 @@ void add_d(PLCI * plci, word length, byte * p)
/* parameter buffer */
/*------------------------------------------------------------------*/
-void add_ai(PLCI * plci, API_PARSE * ai)
+static void add_ai(PLCI *plci, API_PARSE *ai)
{
word i;
API_PARSE ai_parms[5];
@@ -7445,7 +7449,8 @@ void add_ai(PLCI * plci, API_PARSE * ai)
/* put parameter for b1 protocol in the parameter buffer */
/*------------------------------------------------------------------*/
-word add_b1(PLCI * plci, API_PARSE * bp, word b_channel_info, word b1_facilities)
+static word add_b1(PLCI *plci, API_PARSE *bp, word b_channel_info,
+ word b1_facilities)
{
API_PARSE bp_parms[8];
API_PARSE mdm_cfg[9];
@@ -7909,7 +7914,7 @@ word add_b1(PLCI * plci, API_PARSE * bp, word b_channel_info, word b1_faciliti
/* put parameter for b2 and B3 protocol in the parameter buffer */
/*------------------------------------------------------------------*/
-word add_b23(PLCI * plci, API_PARSE * bp)
+static word add_b23(PLCI *plci, API_PARSE *bp)
{
word i, fax_control_bits;
byte pos, len;
@@ -8706,7 +8711,7 @@ void sig_req(PLCI * plci, byte req, byte Id)
/* send a request for the network layer entity */
/*------------------------------------------------------------------*/
-void nl_req_ncci(PLCI * plci, byte req, byte ncci)
+static void nl_req_ncci(PLCI *plci, byte req, byte ncci)
{
if(!plci) return;
if(plci->adapter->adapter_disabled) return;
@@ -8728,7 +8733,7 @@ void nl_req_ncci(PLCI * plci, byte req, byte ncci)
plci->req_in_start = plci->req_in;
}
-void send_req(PLCI * plci)
+static void send_req(PLCI *plci)
{
ENTITY * e;
word l;
@@ -8863,7 +8868,7 @@ void send_data(PLCI * plci)
}
}
-void listen_check(DIVA_CAPI_ADAPTER * a)
+static void listen_check(DIVA_CAPI_ADAPTER *a)
{
word i,j;
PLCI * plci;
@@ -8906,7 +8911,7 @@ void listen_check(DIVA_CAPI_ADAPTER * a)
/* functions for all parameters sent in INDs */
/*------------------------------------------------------------------*/
-void IndParse(PLCI * plci, word * parms_id, byte ** parms, byte multiIEsize)
+static void IndParse(PLCI *plci, word *parms_id, byte **parms, byte multiIEsize)
{
word ploc; /* points to current location within packet */
byte w;
@@ -8991,7 +8996,7 @@ void IndParse(PLCI * plci, word * parms_id, byte ** parms, byte multiIEsize)
/* try to match a cip from received BC and HLC */
/*------------------------------------------------------------------*/
-byte ie_compare(byte * ie1, byte * ie2)
+static byte ie_compare(byte *ie1, byte *ie2)
{
word i;
if(!ie1 || ! ie2) return false;
@@ -9000,7 +9005,7 @@ byte ie_compare(byte * ie1, byte * ie2)
return true;
}
-word find_cip(DIVA_CAPI_ADAPTER * a, byte * bc, byte * hlc)
+static word find_cip(DIVA_CAPI_ADAPTER *a, byte *bc, byte *hlc)
{
word i;
word j;
@@ -9068,7 +9073,7 @@ static byte AddInfo(byte **add_i,
/* voice and codec features */
/*------------------------------------------------------------------*/
-void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER * a)
+static void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER *a)
{
byte voice_chi[] = "\x02\x18\x01";
byte channel;
@@ -9086,7 +9091,7 @@ void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER * a)
}
}
-void VoiceChannelOff(PLCI *plci)
+static void VoiceChannelOff(PLCI *plci)
{
dbug(1,dprintf("ExtDevOFF"));
add_p(plci,FTY,"\x02\x01\x08"); /* B Off */
@@ -9099,7 +9104,8 @@ void VoiceChannelOff(PLCI *plci)
}
-word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, byte hook_listen)
+static word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl,
+ byte hook_listen)
{
word j;
PLCI *splci;
@@ -9195,7 +9201,7 @@ word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, byte ho
}
-void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci)
+static void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci)
{
dbug(1,dprintf("CodecIdCheck"));
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
index a33d87afc843..7bbf7300593d 100644
--- a/drivers/isdn/hardware/mISDN/hfc_multi.h
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -162,8 +162,8 @@ struct hfc_multi {
void (*write_fifo)(struct hfc_multi *hc, u_char *data,
int len);
u_long pci_origmembase, plx_origmembase, dsp_origmembase;
- u_char *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
- u_char *plx_membase; /* PLX memory */
+ void __iomem *pci_membase; /* PCI memory */
+ void __iomem *plx_membase; /* PLX memory */
u_char *dsp_membase; /* DSP on PLX */
u_long pci_iobase; /* PCI IO */
struct hfcm_hw hw; /* remember data of write-only-registers */
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 1eac03f39d00..c63e2f49da8a 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -171,9 +171,8 @@ static int (*unregister_interrupt)(void);
static int interrupt_registered;
static struct hfc_multi *syncmaster;
-int plxsd_master; /* if we have a master card (yet) */
+static int plxsd_master; /* if we have a master card (yet) */
static spinlock_t plx_lock; /* may not acquire other lock inside */
-EXPORT_SYMBOL(plx_lock);
#define TYP_E1 1
#define TYP_4S 4
@@ -422,7 +421,7 @@ HFC_wait_debug(struct hfc_multi *hc, const char *function, int line)
#endif
/* write fifo data (REGIO) */
-void
+static void
write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
{
outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
@@ -443,7 +442,7 @@ write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
}
}
/* write fifo data (PCIMEM) */
-void
+static void
write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
{
while (len>>2) {
@@ -465,7 +464,7 @@ write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
}
}
/* read fifo data (REGIO) */
-void
+static void
read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
{
outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
@@ -487,7 +486,7 @@ read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
}
/* read fifo data (PCIMEM) */
-void
+static void
read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
{
while (len>>2) {
@@ -706,7 +705,7 @@ vpm_out(struct hfc_multi *c, int which, unsigned short addr,
}
-void
+static void
vpm_init(struct hfc_multi *wc)
{
unsigned char reg;
@@ -789,7 +788,8 @@ vpm_init(struct hfc_multi *wc)
}
}
-void
+#ifdef UNUSED
+static void
vpm_check(struct hfc_multi *hctmp)
{
unsigned char gpi2;
@@ -799,6 +799,7 @@ vpm_check(struct hfc_multi *hctmp)
if ((gpi2 & 0x3) != 0x3)
printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
}
+#endif /* UNUSED */
/*
@@ -812,7 +813,7 @@ vpm_check(struct hfc_multi *hctmp)
*
*/
-void
+static void
vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
{
unsigned int timeslot;
@@ -844,7 +845,7 @@ vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
vpm_out(hc, unit, timeslot, 0x7e);
}
-void
+static void
vpm_echocan_off(struct hfc_multi *hc, int ch)
{
unsigned int timeslot;
@@ -887,8 +888,9 @@ vpm_echocan_off(struct hfc_multi *hc, int ch)
static inline void
hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
{
- struct hfc_multi *hc, *next, *pcmmaster = 0;
- u_int *plx_acc_32, pv;
+ struct hfc_multi *hc, *next, *pcmmaster = NULL;
+ void __iomem *plx_acc_32;
+ u_int pv;
u_long flags;
spin_lock_irqsave(&HFClock, flags);
@@ -916,7 +918,7 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
/* Disable sync of all cards */
list_for_each_entry_safe(hc, next, &HFClist, list) {
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv &= ~PLX_SYNC_O_EN;
writel(pv, plx_acc_32);
@@ -938,7 +940,7 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
"interface.\n", hc->id, hc);
/* Enable new sync master */
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_SYNC_O_EN;
writel(pv, plx_acc_32);
@@ -968,7 +970,7 @@ hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
"QUARTZ is automatically "
"enabled by HFC-%dS\n", hc->type);
}
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_SYNC_O_EN;
writel(pv, plx_acc_32);
@@ -1013,7 +1015,8 @@ plxsd_checksync(struct hfc_multi *hc, int rm)
static void
release_io_hfcmulti(struct hfc_multi *hc)
{
- u_int *plx_acc_32, pv;
+ void __iomem *plx_acc_32;
+ u_int pv;
u_long plx_flags;
if (debug & DEBUG_HFCMULTI_INIT)
@@ -1033,7 +1036,7 @@ release_io_hfcmulti(struct hfc_multi *hc)
printk(KERN_DEBUG "%s: release PLXSD card %d\n",
__func__, hc->id + 1);
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
writel(PLX_GPIOC_INIT, plx_acc_32);
pv = readl(plx_acc_32);
/* Termination off */
@@ -1055,9 +1058,9 @@ release_io_hfcmulti(struct hfc_multi *hc)
test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
if (hc->pci_membase)
- iounmap((void *)hc->pci_membase);
+ iounmap(hc->pci_membase);
if (hc->plx_membase)
- iounmap((void *)hc->plx_membase);
+ iounmap(hc->plx_membase);
if (hc->pci_iobase)
release_region(hc->pci_iobase, 8);
@@ -1080,7 +1083,8 @@ init_chip(struct hfc_multi *hc)
u_long flags, val, val2 = 0, rev;
int i, err = 0;
u_char r_conf_en, rval;
- u_int *plx_acc_32, pv;
+ void __iomem *plx_acc_32;
+ u_int pv;
u_long plx_flags, hfc_flags;
int plx_count;
struct hfc_multi *pos, *next, *plx_last_hc;
@@ -1154,7 +1158,7 @@ init_chip(struct hfc_multi *hc)
printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
__func__, hc->id + 1);
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
writel(PLX_GPIOC_INIT, plx_acc_32);
pv = readl(plx_acc_32);
/* The first and the last cards are terminating the PCM bus */
@@ -1190,8 +1194,7 @@ init_chip(struct hfc_multi *hc)
"we disable termination\n",
__func__, plx_last_hc->id + 1);
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
- + PLX_GPIOC);
+ plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv &= ~PLX_TERM_ON;
writel(pv, plx_acc_32);
@@ -1240,7 +1243,7 @@ init_chip(struct hfc_multi *hc)
/* Speech Design PLX bridge pcm and sync mode */
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
/* Connect PCM */
if (hc->hw.r_pcm_md0 & V_PCM_MD) {
@@ -1352,8 +1355,7 @@ controller_fail:
/* retry with master clock */
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(hc->plx_membase +
- PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
pv |= PLX_SYNC_O_EN;
@@ -1389,7 +1391,7 @@ controller_fail:
if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
plxsd_master = 1;
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_DSP_RES_N;
writel(pv, plx_acc_32);
@@ -2586,7 +2588,8 @@ hfcmulti_interrupt(int intno, void *dev_id)
struct dchannel *dch;
u_char r_irq_statech, status, r_irq_misc, r_irq_oview;
int i;
- u_short *plx_acc, wval;
+ void __iomem *plx_acc;
+ u_short wval;
u_char e1_syncsta, temp;
u_long flags;
@@ -2606,7 +2609,7 @@ hfcmulti_interrupt(int intno, void *dev_id)
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
spin_lock_irqsave(&plx_lock, flags);
- plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+ plx_acc = hc->plx_membase + PLX_INTCSR;
wval = readw(plx_acc);
spin_unlock_irqrestore(&plx_lock, flags);
if (!(wval & PLX_INTCSR_LINTI1_STATUS))
@@ -4091,7 +4094,7 @@ init_card(struct hfc_multi *hc)
{
int err = -EIO;
u_long flags;
- u_short *plx_acc;
+ void __iomem *plx_acc;
u_long plx_flags;
if (debug & DEBUG_HFCMULTI_INIT)
@@ -4113,7 +4116,7 @@ init_card(struct hfc_multi *hc)
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+ plx_acc = hc->plx_membase + PLX_INTCSR;
writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
plx_acc); /* enable PCI & LINT1 irq */
spin_unlock_irqrestore(&plx_lock, plx_flags);
@@ -4162,7 +4165,7 @@ init_card(struct hfc_multi *hc)
error:
if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
spin_lock_irqsave(&plx_lock, plx_flags);
- plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+ plx_acc = hc->plx_membase + PLX_INTCSR;
writew(0x00, plx_acc); /*disable IRQs*/
spin_unlock_irqrestore(&plx_lock, plx_flags);
}
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
index 3f2a0a20c19b..7ee5bd9f2bb4 100644
--- a/drivers/isdn/hysdn/hysdn_net.c
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -76,7 +76,7 @@ static int
net_open(struct net_device *dev)
{
struct in_device *in_dev;
- hysdn_card *card = dev->priv;
+ hysdn_card *card = dev->ml_priv;
int i;
netif_start_queue(dev); /* start tx-queueing */
@@ -159,7 +159,7 @@ net_send_packet(struct sk_buff *skb, struct net_device *dev)
spin_unlock_irq(&lp->lock);
if (lp->sk_count <= 3) {
- schedule_work(&((hysdn_card *) dev->priv)->irq_queue);
+ schedule_work(&((hysdn_card *) dev->ml_priv)->irq_queue);
}
return (0); /* success */
} /* net_send_packet */
@@ -295,7 +295,7 @@ hysdn_net_create(hysdn_card * card)
kfree(dev);
return (i);
}
- dev->priv = card; /* remember pointer to own data structure */
+ dev->ml_priv = card; /* remember pointer to own data structure */
card->netif = dev; /* setup the local pointer */
if (card->debug_flags & LOG_NET_INIT)
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
index 484299b031f8..8f9f4912de32 100644
--- a/drivers/isdn/hysdn/hysdn_procconf.c
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -246,7 +246,8 @@ hysdn_conf_open(struct inode *ino, struct file *filep)
}
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
- filep->f_uid, filep->f_gid, filep->f_mode);
+ filep->f_cred->fsuid, filep->f_cred->fsgid,
+ filep->f_mode);
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write boot file or conf line */
@@ -331,7 +332,8 @@ hysdn_conf_close(struct inode *ino, struct file *filep)
}
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
- filep->f_uid, filep->f_gid, filep->f_mode);
+ filep->f_cred->fsuid, filep->f_cred->fsgid,
+ filep->f_mode);
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write boot file or conf line */
diff --git a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c
index 0193b6f7c70c..46048e55f241 100644
--- a/drivers/isdn/i4l/isdn_concap.c
+++ b/drivers/isdn/i4l/isdn_concap.c
@@ -42,7 +42,7 @@
static int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
{
struct net_device *ndev = concap -> net_dev;
- isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
+ isdn_net_dev *nd = ((isdn_net_local *) netdev_priv(ndev))->netdev;
isdn_net_local *lp = isdn_net_get_locked_lp(nd);
IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
@@ -61,7 +61,7 @@ static int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *
static int isdn_concap_dl_connect_req(struct concap_proto *concap)
{
struct net_device *ndev = concap -> net_dev;
- isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(ndev);
int ret;
IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 1bfc55d7a26c..023ea11d2f9e 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -120,7 +120,7 @@ static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
return 0;
if (lp->master)
- nd = ((isdn_net_local *) lp->master->priv)->netdev;
+ nd = ISDN_MASTER_PRIV(lp)->netdev;
else
nd = lp->netdev;
@@ -213,9 +213,9 @@ isdn_net_reset(struct net_device *dev)
{
#ifdef CONFIG_ISDN_X25
struct concap_device_ops * dops =
- ( (isdn_net_local *) dev->priv ) -> dops;
+ ((isdn_net_local *) netdev_priv(dev))->dops;
struct concap_proto * cprot =
- ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+ ((isdn_net_local *) netdev_priv(dev))->netdev->cprot;
#endif
#ifdef CONFIG_ISDN_X25
if( cprot && cprot -> pops && dops )
@@ -250,11 +250,11 @@ isdn_net_open(struct net_device *dev)
}
/* If this interface has slaves, start them also */
-
- if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ p = MASTER_TO_SLAVE(dev);
+ if (p) {
while (p) {
isdn_net_reset(p);
- p = (((isdn_net_local *) p->priv)->slave);
+ p = MASTER_TO_SLAVE(p);
}
}
isdn_lock_drivers();
@@ -483,7 +483,7 @@ isdn_net_stat_callback(int idx, isdn_ctrl *c)
isdn_net_ciscohdlck_connected(lp);
if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
if (lp->master) { /* is lp a slave? */
- isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
+ isdn_net_dev *nd = ISDN_MASTER_PRIV(lp)->netdev;
isdn_net_add_to_bundle(nd, lp);
}
}
@@ -823,7 +823,7 @@ isdn_net_dial(void)
void
isdn_net_hangup(struct net_device *d)
{
- isdn_net_local *lp = (isdn_net_local *) d->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(d);
isdn_ctrl cmd;
#ifdef CONFIG_ISDN_X25
struct concap_proto *cprot = lp->netdev->cprot;
@@ -832,7 +832,7 @@ isdn_net_hangup(struct net_device *d)
if (lp->flags & ISDN_NET_CONNECTED) {
if (lp->slave != NULL) {
- isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
+ isdn_net_local *slp = ISDN_SLAVE_PRIV(lp);
if (slp->flags & ISDN_NET_CONNECTED) {
printk(KERN_INFO
"isdn_net: hang up slave %s before %s\n",
@@ -865,8 +865,8 @@ isdn_net_hangup(struct net_device *d)
}
typedef struct {
- unsigned short source;
- unsigned short dest;
+ __be16 source;
+ __be16 dest;
} ip_ports;
static void
@@ -890,15 +890,15 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
proto = ETH_P_IP;
switch (lp->p_encap) {
case ISDN_NET_ENCAP_IPTYP:
- proto = ntohs(*(unsigned short *) &buf[0]);
+ proto = ntohs(*(__be16 *)&buf[0]);
p = &buf[2];
break;
case ISDN_NET_ENCAP_ETHER:
- proto = ntohs(*(unsigned short *) &buf[12]);
+ proto = ntohs(*(__be16 *)&buf[12]);
p = &buf[14];
break;
case ISDN_NET_ENCAP_CISCOHDLC:
- proto = ntohs(*(unsigned short *) &buf[2]);
+ proto = ntohs(*(__be16 *)&buf[2]);
p = &buf[4];
break;
#ifdef CONFIG_ISDN_PPP
@@ -942,18 +942,12 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
strcpy(addinfo, " IDP");
break;
}
- printk(KERN_INFO
- "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
-
- p[12], p[13], p[14], p[15],
- p[16], p[17], p[18], p[19],
- addinfo);
+ printk(KERN_INFO "OPEN: %pI4 -> %pI4%s\n",
+ p + 12, p + 16, addinfo);
break;
case ETH_P_ARP:
- printk(KERN_INFO
- "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
- p[14], p[15], p[16], p[17],
- p[24], p[25], p[26], p[27]);
+ printk(KERN_INFO "OPEN: ARP %pI4 -> *.*.*.* ?%pI4\n",
+ p + 14, p + 24);
break;
}
}
@@ -1054,10 +1048,10 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
{
isdn_net_dev *nd;
isdn_net_local *slp;
- isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(ndev);
int retv = 0;
- if (((isdn_net_local *) (ndev->priv))->master) {
+ if (((isdn_net_local *) netdev_priv(ndev))->master) {
printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
dev_kfree_skb(skb);
return 0;
@@ -1069,7 +1063,7 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
return isdn_ppp_xmit(skb, ndev);
}
#endif
- nd = ((isdn_net_local *) ndev->priv)->netdev;
+ nd = ((isdn_net_local *) netdev_priv(ndev))->netdev;
lp = isdn_net_get_locked_lp(nd);
if (!lp) {
printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
@@ -1096,9 +1090,9 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
} else {
/* subsequent overload: if slavedelay exceeded, start dialing */
if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
- slp = lp->slave->priv;
+ slp = ISDN_SLAVE_PRIV(lp);
if (!(slp->flags & ISDN_NET_CONNECTED)) {
- isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+ isdn_net_force_dial_lp(ISDN_SLAVE_PRIV(lp));
}
}
}
@@ -1118,7 +1112,7 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
static void
isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
{
- isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev);
if (!skb)
return;
if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
@@ -1133,7 +1127,7 @@ isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
static void isdn_net_tx_timeout(struct net_device * ndev)
{
- isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(ndev);
printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
if (!lp->dialstate){
@@ -1167,7 +1161,7 @@ static void isdn_net_tx_timeout(struct net_device * ndev)
static int
isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
- isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(ndev);
#ifdef CONFIG_ISDN_X25
struct concap_proto * cprot = lp -> netdev -> cprot;
/* At this point hard_start_xmit() passes control to the encapsulation
@@ -1316,7 +1310,7 @@ isdn_net_close(struct net_device *dev)
struct net_device *p;
#ifdef CONFIG_ISDN_X25
struct concap_proto * cprot =
- ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+ ((isdn_net_local *) netdev_priv(dev))->netdev->cprot;
/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
#endif
@@ -1324,17 +1318,18 @@ isdn_net_close(struct net_device *dev)
if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
#endif
netif_stop_queue(dev);
- if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ p = MASTER_TO_SLAVE(dev);
+ if (p) {
/* If this interface has slaves, stop them also */
while (p) {
#ifdef CONFIG_ISDN_X25
- cprot = ( (isdn_net_local *) p->priv )
+ cprot = ((isdn_net_local *) netdev_priv(p))
-> netdev -> cprot;
if( cprot && cprot -> pops )
cprot -> pops -> close( cprot );
#endif
isdn_net_hangup(p);
- p = (((isdn_net_local *) p->priv)->slave);
+ p = MASTER_TO_SLAVE(p);
}
}
isdn_net_hangup(dev);
@@ -1348,7 +1343,7 @@ isdn_net_close(struct net_device *dev)
static struct net_device_stats *
isdn_net_get_stats(struct net_device *dev)
{
- isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev);
return &lp->stats;
}
@@ -1361,7 +1356,7 @@ isdn_net_get_stats(struct net_device *dev)
* This is normal practice and works for any 'now in use' protocol.
*/
-static unsigned short
+static __be16
isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
{
struct ethhdr *eth;
@@ -1427,7 +1422,7 @@ isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
static int
isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev);
unsigned long len = 0;
unsigned long expires = 0;
int tmp = 0;
@@ -1539,15 +1534,16 @@ isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
p = skb_put(skb, 4 + 14);
/* cisco header */
- p += put_u8 (p, CISCO_ADDR_UNICAST);
- p += put_u8 (p, CISCO_CTRL);
- p += put_u16(p, CISCO_TYPE_SLARP);
+ *(u8 *)(p + 0) = CISCO_ADDR_UNICAST;
+ *(u8 *)(p + 1) = CISCO_CTRL;
+ *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP);
/* slarp keepalive */
- p += put_u32(p, CISCO_SLARP_KEEPALIVE);
- p += put_u32(p, lp->cisco_myseq);
- p += put_u32(p, lp->cisco_yourseq);
- p += put_u16(p, 0xffff); // reliablity, always 0xffff
+ *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_KEEPALIVE);
+ *(__be32 *)(p + 8) = cpu_to_be32(lp->cisco_myseq);
+ *(__be32 *)(p + 12) = cpu_to_be32(lp->cisco_yourseq);
+ *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliablity, always 0xffff
+ p += 18;
isdn_net_write_super(lp, skb);
@@ -1569,15 +1565,16 @@ isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
p = skb_put(skb, 4 + 14);
/* cisco header */
- p += put_u8 (p, CISCO_ADDR_UNICAST);
- p += put_u8 (p, CISCO_CTRL);
- p += put_u16(p, CISCO_TYPE_SLARP);
+ *(u8 *)(p + 0) = CISCO_ADDR_UNICAST;
+ *(u8 *)(p + 1) = CISCO_CTRL;
+ *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP);
/* slarp request */
- p += put_u32(p, CISCO_SLARP_REQUEST);
- p += put_u32(p, 0); // address
- p += put_u32(p, 0); // netmask
- p += put_u16(p, 0); // unused
+ *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_REQUEST);
+ *(__be32 *)(p + 8) = cpu_to_be32(0); // address
+ *(__be32 *)(p + 12) = cpu_to_be32(0); // netmask
+ *(__be16 *)(p + 16) = cpu_to_be16(0); // unused
+ p += 18;
isdn_net_write_super(lp, skb);
}
@@ -1634,18 +1631,17 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
p = skb_put(skb, 4 + 14);
/* cisco header */
- p += put_u8 (p, CISCO_ADDR_UNICAST);
- p += put_u8 (p, CISCO_CTRL);
- p += put_u16(p, CISCO_TYPE_SLARP);
+ *(u8 *)(p + 0) = CISCO_ADDR_UNICAST;
+ *(u8 *)(p + 1) = CISCO_CTRL;
+ *(__be16 *)(p + 2) = cpu_to_be16(CISCO_TYPE_SLARP);
/* slarp reply, send own ip/netmask; if values are nonsense remote
* should think we are unable to provide it with an address via SLARP */
- p += put_u32(p, CISCO_SLARP_REPLY);
- *(__be32 *)p = addr; // address
- p += 4;
- *(__be32 *)p = mask; // netmask
- p += 4;
- p += put_u16(p, 0); // unused
+ *(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_REPLY);
+ *(__be32 *)(p + 8) = addr; // address
+ *(__be32 *)(p + 12) = mask; // netmask
+ *(__be16 *)(p + 16) = cpu_to_be16(0); // unused
+ p += 18;
isdn_net_write_super(lp, skb);
}
@@ -1656,44 +1652,39 @@ isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
unsigned char *p;
int period;
u32 code;
- u32 my_seq, addr;
- u32 your_seq, mask;
- u32 local;
+ u32 my_seq;
+ u32 your_seq;
+ __be32 local;
+ __be32 *addr, *mask;
u16 unused;
if (skb->len < 14)
return;
p = skb->data;
- p += get_u32(p, &code);
-
+ code = be32_to_cpup((__be32 *)p);
+ p += 4;
+
switch (code) {
case CISCO_SLARP_REQUEST:
lp->cisco_yourseq = 0;
isdn_net_ciscohdlck_slarp_send_reply(lp);
break;
case CISCO_SLARP_REPLY:
- addr = ntohl(*(u32 *)p);
- mask = ntohl(*(u32 *)(p+4));
- if (mask != 0xfffffffc)
+ addr = (__be32 *)p;
+ mask = (__be32 *)(p + 4);
+ if (*mask != cpu_to_be32(0xfffffffc))
goto slarp_reply_out;
- if ((addr & 3) == 0 || (addr & 3) == 3)
+ if ((*addr & cpu_to_be32(3)) == cpu_to_be32(0) ||
+ (*addr & cpu_to_be32(3)) == cpu_to_be32(3))
goto slarp_reply_out;
- local = addr ^ 3;
- printk(KERN_INFO "%s: got slarp reply: "
- "remote ip: %d.%d.%d.%d, "
- "local ip: %d.%d.%d.%d "
- "mask: %d.%d.%d.%d\n",
- lp->netdev->dev->name,
- HIPQUAD(addr),
- HIPQUAD(local),
- HIPQUAD(mask));
+ local = *addr ^ cpu_to_be32(3);
+ printk(KERN_INFO "%s: got slarp reply: remote ip: %pI4, local ip: %pI4 mask: %pI4\n",
+ lp->netdev->dev->name, addr, &local, mask);
break;
slarp_reply_out:
- printk(KERN_INFO "%s: got invalid slarp "
- "reply (%d.%d.%d.%d/%d.%d.%d.%d) "
- "- ignored\n", lp->netdev->dev->name,
- HIPQUAD(addr), HIPQUAD(mask));
+ printk(KERN_INFO "%s: got invalid slarp reply (%pI4/%pI4) - ignored\n",
+ lp->netdev->dev->name, addr, mask);
break;
case CISCO_SLARP_KEEPALIVE:
period = (int)((jiffies - lp->cisco_last_slarp_in
@@ -1707,9 +1698,10 @@ isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
lp->cisco_keepalive_period);
}
lp->cisco_last_slarp_in = jiffies;
- p += get_u32(p, &my_seq);
- p += get_u32(p, &your_seq);
- p += get_u16(p, &unused);
+ my_seq = be32_to_cpup((__be32 *)(p + 0));
+ your_seq = be32_to_cpup((__be32 *)(p + 4));
+ unused = be16_to_cpup((__be16 *)(p + 8));
+ p += 10;
lp->cisco_yourseq = my_seq;
lp->cisco_mineseen = your_seq;
break;
@@ -1728,9 +1720,10 @@ isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
goto out_free;
p = skb->data;
- p += get_u8 (p, &addr);
- p += get_u8 (p, &ctrl);
- p += get_u16(p, &type);
+ addr = *(u8 *)(p + 0);
+ ctrl = *(u8 *)(p + 1);
+ type = be16_to_cpup((__be16 *)(p + 2));
+ p += 4;
skb_pull(skb, 4);
if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
@@ -1771,7 +1764,7 @@ isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
static void
isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
{
- isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(ndev);
isdn_net_local *olp = lp; /* original 'lp' */
#ifdef CONFIG_ISDN_X25
struct concap_proto *cprot = lp -> netdev -> cprot;
@@ -1785,7 +1778,7 @@ isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
* handle master's statistics and hangup-timeout
*/
ndev = lp->master;
- lp = (isdn_net_local *) ndev->priv;
+ lp = (isdn_net_local *) netdev_priv(ndev);
lp->stats.rx_packets++;
lp->stats.rx_bytes += skb->len;
}
@@ -1825,7 +1818,7 @@ isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
/* IP with type field */
olp->huptimer = 0;
lp->huptimer = 0;
- skb->protocol = *(unsigned short *) &(skb->data[0]);
+ skb->protocol = *(__be16 *)&(skb->data[0]);
skb_pull(skb, 2);
if (*(unsigned short *) skb->data == 0xFFFF)
skb->protocol = htons(ETH_P_802_3);
@@ -1886,7 +1879,7 @@ static int isdn_net_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr, unsigned plen)
{
- isdn_net_local *lp = dev->priv;
+ isdn_net_local *lp = netdev_priv(dev);
unsigned char *p;
ushort len = 0;
@@ -1907,20 +1900,21 @@ static int isdn_net_header(struct sk_buff *skb, struct net_device *dev,
break;
case ISDN_NET_ENCAP_IPTYP:
/* ethernet type field */
- *((ushort *) skb_push(skb, 2)) = htons(type);
+ *((__be16 *)skb_push(skb, 2)) = htons(type);
len = 2;
break;
case ISDN_NET_ENCAP_UIHDLC:
/* HDLC with UI-Frames (for ispa with -h1 option) */
- *((ushort *) skb_push(skb, 2)) = htons(0x0103);
+ *((__be16 *)skb_push(skb, 2)) = htons(0x0103);
len = 2;
break;
case ISDN_NET_ENCAP_CISCOHDLC:
case ISDN_NET_ENCAP_CISCOHDLCK:
p = skb_push(skb, 4);
- p += put_u8 (p, CISCO_ADDR_UNICAST);
- p += put_u8 (p, CISCO_CTRL);
- p += put_u16(p, type);
+ *(u8 *)(p + 0) = CISCO_ADDR_UNICAST;
+ *(u8 *)(p + 1) = CISCO_CTRL;
+ *(__be16 *)(p + 2) = cpu_to_be16(type);
+ p += 4;
len = 4;
break;
#ifdef CONFIG_ISDN_X25
@@ -1942,7 +1936,7 @@ static int
isdn_net_rebuild_header(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- isdn_net_local *lp = dev->priv;
+ isdn_net_local *lp = netdev_priv(dev);
int ret = 0;
if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
@@ -1972,7 +1966,7 @@ isdn_net_rebuild_header(struct sk_buff *skb)
static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
{
const struct net_device *dev = neigh->dev;
- isdn_net_local *lp = dev->priv;
+ isdn_net_local *lp = netdev_priv(dev);
if (lp->p_encap == ISDN_NET_ENCAP_ETHER)
return eth_header_cache(neigh, hh);
@@ -1983,9 +1977,9 @@ static void isdn_header_cache_update(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr)
{
- isdn_net_local *lp = dev->priv;
+ isdn_net_local *lp = netdev_priv(dev);
if (lp->p_encap == ISDN_NET_ENCAP_ETHER)
- return eth_header_cache_update(hh, dev, haddr);
+ eth_header_cache_update(hh, dev, haddr);
}
static const struct header_ops isdn_header_ops = {
@@ -2298,16 +2292,16 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
* it's master and parent slave is online. If not, reject the call.
*/
if (lp->master) {
- isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+ isdn_net_local *mlp = ISDN_MASTER_PRIV(lp);
printk(KERN_DEBUG "ICALLslv: %s\n", p->dev->name);
printk(KERN_DEBUG "master=%s\n", lp->master->name);
if (mlp->flags & ISDN_NET_CONNECTED) {
printk(KERN_DEBUG "master online\n");
/* Master is online, find parent-slave (master if first slave) */
while (mlp->slave) {
- if ((isdn_net_local *) mlp->slave->priv == lp)
+ if (ISDN_SLAVE_PRIV(mlp) == lp)
break;
- mlp = (isdn_net_local *) mlp->slave->priv;
+ mlp = ISDN_SLAVE_PRIV(mlp);
}
} else
printk(KERN_DEBUG "master offline\n");
@@ -2519,7 +2513,7 @@ isdn_net_force_dial(char *name)
*/
static void _isdn_setup(struct net_device *dev)
{
- isdn_net_local *lp = dev->priv;
+ isdn_net_local *lp = netdev_priv(dev);
dev->flags = IFF_NOARP | IFF_POINTOPOINT;
lp->p_encap = ISDN_NET_ENCAP_RAWIP;
@@ -2575,20 +2569,20 @@ isdn_net_new(char *name, struct net_device *master)
kfree(netdev);
return NULL;
}
- netdev->local = netdev->dev->priv;
+ netdev->local = netdev_priv(netdev->dev);
netdev->dev->init = isdn_net_init;
if (master) {
/* Device shall be a slave */
- struct net_device *p = (((isdn_net_local *) master->priv)->slave);
+ struct net_device *p = MASTER_TO_SLAVE(master);
struct net_device *q = master;
netdev->local->master = master;
/* Put device at end of slave-chain */
while (p) {
q = p;
- p = (((isdn_net_local *) p->priv)->slave);
+ p = MASTER_TO_SLAVE(p);
}
- ((isdn_net_local *) q->priv)->slave = netdev->dev;
+ MASTER_TO_SLAVE(q) = netdev->dev;
} else {
/* Device shall be a master */
/*
@@ -3086,7 +3080,7 @@ isdn_net_force_hangup(char *name)
/* If this interface has slaves, do a hangup for them also. */
while (q) {
isdn_net_hangup(q);
- q = (((isdn_net_local *) q->priv)->slave);
+ q = MASTER_TO_SLAVE(q);
}
isdn_net_hangup(p->dev);
return 0;
@@ -3116,8 +3110,10 @@ isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
if (p->local->master) {
/* It's a slave-device, so update master's slave-pointer if necessary */
- if (((isdn_net_local *) (p->local->master->priv))->slave == p->dev)
- ((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
+ if (((isdn_net_local *) ISDN_MASTER_PRIV(p->local))->slave ==
+ p->dev)
+ ((isdn_net_local *)ISDN_MASTER_PRIV(p->local))->slave =
+ p->local->slave;
} else {
/* Unregister only if it's a master-device */
unregister_netdev(p->dev);
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
index be4949715d55..74032d0881ef 100644
--- a/drivers/isdn/i4l/isdn_net.h
+++ b/drivers/isdn/i4l/isdn_net.h
@@ -56,6 +56,11 @@ extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
#define ISDN_NET_MAX_QUEUE_LENGTH 2
+#define ISDN_MASTER_PRIV(lp) ((isdn_net_local *) netdev_priv(lp->master))
+#define ISDN_SLAVE_PRIV(lp) ((isdn_net_local *) netdev_priv(lp->slave))
+#define MASTER_TO_SLAVE(master) \
+ (((isdn_net_local *) netdev_priv(master))->slave)
+
/*
* is this particular channel busy?
*/
@@ -126,7 +131,7 @@ static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
unsigned long flags;
if (lp->master)
- master_lp = (isdn_net_local *) lp->master->priv;
+ master_lp = ISDN_MASTER_PRIV(lp);
// printk(KERN_DEBUG "%s: lp:%s(%p) mlp:%s(%p) last(%p) next(%p) mndq(%p)\n",
// __func__, lp->name, lp, master_lp->name, master_lp, lp->last, lp->next, master_lp->netdev->queue);
@@ -145,46 +150,3 @@ static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
}
-static inline int
-put_u8(unsigned char *p, u8 x)
-{
- *p = x;
- return 1;
-}
-
-static inline int
-put_u16(unsigned char *p, u16 x)
-{
- *((u16 *)p) = htons(x);
- return 2;
-}
-
-static inline int
-put_u32(unsigned char *p, u32 x)
-{
- *((u32 *)p) = htonl(x);
- return 4;
-}
-
-static inline int
-get_u8(unsigned char *p, u8 *x)
-{
- *x = *p;
- return 1;
-}
-
-static inline int
-get_u16(unsigned char *p, u16 *x)
-{
- *x = ntohs(*((u16 *)p));
- return 2;
-}
-
-static inline int
-get_u32(unsigned char *p, u32 *x)
-{
- *x = ntohl(*((u32 *)p));
- return 4;
-}
-
-
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 77c280ef2eb6..a3551dd0324d 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -1040,7 +1040,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff
is = ippp_table[slot];
if (lp->master) { // FIXME?
- mlp = (isdn_net_local *) lp->master->priv;
+ mlp = ISDN_MASTER_PRIV(lp);
slot = mlp->ppp_slot;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
@@ -1223,7 +1223,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
struct ippp_struct *ipt,*ipts;
int slot, retval = 0;
- mlp = (isdn_net_local *) (netdev->priv);
+ mlp = (isdn_net_local *) netdev_priv(netdev);
nd = mlp->netdev; /* get master lp */
slot = mlp->ppp_slot;
@@ -1289,10 +1289,10 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
*skb_push(skb, 4) = 1; /* indicate outbound */
{
- u_int16_t *p = (u_int16_t *) skb->data;
+ __be16 *p = (__be16 *)skb->data;
p++;
- *p = htons(proto);
+ *p = htons(proto);
}
if (ipt->pass_filter
@@ -1487,10 +1487,10 @@ int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp)
*skb_pull(skb, IPPP_MAX_HEADER - 4) = 1; /* indicate outbound */
{
- u_int16_t *p = (u_int16_t *) skb->data;
+ __be16 *p = (__be16 *)skb->data;
p++;
- *p = htons(proto);
+ *p = htons(proto);
}
drop |= is->pass_filter
@@ -1810,14 +1810,14 @@ static u32 isdn_ppp_mp_get_seq( int short_seq,
if( !short_seq )
{
- seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
+ seq = ntohl(*(__be32 *)skb->data) & MP_LONGSEQ_MASK;
skb_push(skb,1);
}
else
{
/* convert 12-bit short seq number to 24-bit long one
*/
- seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
+ seq = ntohs(*(__be16 *)skb->data) & MP_SHORTSEQ_MASK;
/* check for seqence wrap */
if( !(seq & MP_SHORTSEQ_MAXBIT) &&
@@ -2013,7 +2013,7 @@ isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
{
struct ppp_stats __user *res = ifr->ifr_data;
struct ppp_stats t;
- isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev);
if (!access_ok(VERIFY_WRITE, res, sizeof(struct ppp_stats)))
return -EFAULT;
@@ -2052,7 +2052,7 @@ isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int error=0;
int len;
- isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev);
if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
@@ -2119,7 +2119,7 @@ isdn_ppp_dial_slave(char *name)
sdev = lp->slave;
while (sdev) {
- isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+ isdn_net_local *mlp = (isdn_net_local *) netdev_priv(sdev);
if (!(mlp->flags & ISDN_NET_CONNECTED))
break;
sdev = mlp->slave;
@@ -2127,7 +2127,7 @@ isdn_ppp_dial_slave(char *name)
if (!sdev)
return 2;
- isdn_net_dial_req((isdn_net_local *) sdev->priv);
+ isdn_net_dial_req((isdn_net_local *) netdev_priv(sdev));
return 0;
#else
return -1;
@@ -2150,10 +2150,10 @@ isdn_ppp_hangup_slave(char *name)
sdev = lp->slave;
while (sdev) {
- isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+ isdn_net_local *mlp = (isdn_net_local *) netdev_priv(sdev);
if (mlp->slave) { /* find last connected link in chain */
- isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
+ isdn_net_local *nlp = ISDN_SLAVE_PRIV(mlp);
if (!(nlp->flags & ISDN_NET_CONNECTED))
break;
@@ -2688,7 +2688,7 @@ static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
if(lp->master) {
- int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+ int slot = ISDN_MASTER_PRIV(lp)->ppp_slot;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: slot(%d) out of range\n",
__func__, slot);
@@ -2875,7 +2875,7 @@ static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct
isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
if (lp->master) {
- slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+ slot = ISDN_MASTER_PRIV(lp)->ppp_slot;
if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
printk(KERN_ERR "%s: slot(%d) out of range\n",
__func__, slot);
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
index 33068177b7c9..751665c448d0 100644
--- a/drivers/isdn/mISDN/core.c
+++ b/drivers/isdn/mISDN/core.c
@@ -26,12 +26,12 @@ MODULE_LICENSE("GPL");
module_param(debug, uint, S_IRUGO | S_IWUSR);
static LIST_HEAD(devices);
-DEFINE_RWLOCK(device_lock);
+static DEFINE_RWLOCK(device_lock);
static u64 device_ids;
#define MAX_DEVICE_ID 63
static LIST_HEAD(Bprotocols);
-DEFINE_RWLOCK(bp_lock);
+static DEFINE_RWLOCK(bp_lock);
struct mISDNdevice
*get_mdevice(u_int id)
@@ -192,7 +192,7 @@ mISDN_unregister_Bprotocol(struct Bprotocol *bp)
}
EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
-int
+static int
mISDNInit(void)
{
int err;
@@ -224,7 +224,7 @@ error:
return err;
}
-void mISDN_cleanup(void)
+static void mISDN_cleanup(void)
{
misdn_sock_cleanup();
mISDN_timer_cleanup();
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
index 1c2dd5694773..de3795e3f432 100644
--- a/drivers/isdn/mISDN/dsp_audio.c
+++ b/drivers/isdn/mISDN/dsp_audio.c
@@ -30,7 +30,7 @@ EXPORT_SYMBOL(dsp_audio_s16_to_law);
/* alaw -> ulaw */
u8 dsp_audio_alaw_to_ulaw[256];
/* ulaw -> alaw */
-u8 dsp_audio_ulaw_to_alaw[256];
+static u8 dsp_audio_ulaw_to_alaw[256];
u8 dsp_silence;
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
index c2f51cc50760..c884511e2d49 100644
--- a/drivers/isdn/mISDN/dsp_cmx.c
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -1540,11 +1540,13 @@ send_packet:
schedule_work(&dsp->workq);
}
-u32 samplecount;
+static u32 samplecount;
struct timer_list dsp_spl_tl;
u32 dsp_spl_jiffies; /* calculate the next time to fire */
-u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */
-struct timeval dsp_start_tv; /* time at start of calculation */
+#ifdef UNUSED
+static u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+#endif /* UNUSED */
+static struct timeval dsp_start_tv; /* time at start of calculation */
void
dsp_cmx_send(void *arg)
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 2f10ed82c0db..1dc21d803410 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -161,7 +161,7 @@
#include "core.h"
#include "dsp.h"
-const char *mISDN_dsp_revision = "2.0";
+static const char *mISDN_dsp_revision = "2.0";
static int debug;
static int options;
@@ -631,7 +631,6 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb)
int ret = 0;
u8 *digits;
int cont;
- struct sk_buff *nskb;
u_long flags;
hh = mISDN_HEAD_P(skb);
@@ -690,6 +689,7 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb)
digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
while (*digits) {
+ struct sk_buff *nskb;
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s: digit"
"(%c) to layer %s\n",
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
index eb892d9dd5c6..806a997fe7cc 100644
--- a/drivers/isdn/mISDN/dsp_hwec.c
+++ b/drivers/isdn/mISDN/dsp_hwec.c
@@ -43,7 +43,7 @@ static struct mISDN_dsp_element dsp_hwec_p = {
.free = NULL,
.process_tx = NULL,
.process_rx = NULL,
- .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
+ .num_args = ARRAY_SIZE(args),
.args = args,
};
struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
index 850260ab57d0..5ee6651b45b9 100644
--- a/drivers/isdn/mISDN/dsp_pipeline.c
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -249,7 +249,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
name = strsep(&tok, "(");
args = strsep(&tok, ")");
if (args && !*args)
- args = 0;
+ args = NULL;
list_for_each_entry_safe(entry, n, &dsp_elements, list)
if (!strcmp(entry->elem->name, name)) {
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
index 23dd0dd21524..7a9af66f4b19 100644
--- a/drivers/isdn/mISDN/dsp_tones.c
+++ b/drivers/isdn/mISDN/dsp_tones.c
@@ -231,120 +231,120 @@ dsp_audio_generate_ulaw_samples(void)
* tone sequence definition *
****************************/
-struct pattern {
+static struct pattern {
int tone;
u8 *data[10];
u32 *siz[10];
u32 seq[10];
} pattern[] = {
{TONE_GERMAN_DIALTONE,
- {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALTONE,
- {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALTONE,
- {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_DIALPBX,
- {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALPBX,
- {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALPBX,
- {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0},
- {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
+ {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL, NULL},
+ {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL, NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_RINGING,
- {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGING,
- {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGING,
- {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_RINGPBX,
- {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGPBX,
- {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGPBX,
- {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
- {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
+ {DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_BUSY,
- {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDBUSY,
- {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_BUSY,
- {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_HANGUP,
- {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDHANGUP,
- {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_HANGUP,
- {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_SPECIAL_INFO,
- {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
- {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
+ {DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_GASSENBESETZT,
- {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
- {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+ {DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_AUFSCHALTTON,
- {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
- {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+ {DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
+ {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
{0,
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};
@@ -467,7 +467,7 @@ dsp_tone_timeout(void *arg)
/* set next tone */
if (pat->data[index] == DATA_S)
- dsp_tone_hw_message(dsp, 0, 0);
+ dsp_tone_hw_message(dsp, NULL, 0);
else
dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
/* set timer */
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
index a2dc4570ef43..2ec4b28d9edc 100644
--- a/drivers/isdn/mISDN/l1oip_codec.c
+++ b/drivers/isdn/mISDN/l1oip_codec.c
@@ -49,6 +49,7 @@ NOTE: The bytes are handled as they are law-encoded.
#include <linux/vmalloc.h>
#include <linux/mISDNif.h>
#include "core.h"
+#include "l1oip.h"
/* definitions of codec. don't use calculations, code may run slower. */
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index e42150a57780..0884dd6892f8 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -469,7 +469,7 @@ l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase,
static void
l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
{
- u32 id;
+ u32 packet_id;
u8 channel;
u8 remotecodec;
u16 timebase;
@@ -508,7 +508,7 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
}
/* get id flag */
- id = (*buf>>4)&1;
+ packet_id = (*buf>>4)&1;
/* check coding */
remotecodec = (*buf) & 0x0f;
@@ -520,11 +520,11 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
buf++;
len--;
- /* check id */
- if (id) {
+ /* check packet_id */
+ if (packet_id) {
if (!hc->id) {
printk(KERN_WARNING "%s: packet error - packet has id "
- "0x%x, but we have not\n", __func__, id);
+ "0x%x, but we have not\n", __func__, packet_id);
return;
}
if (len < 4) {
@@ -532,16 +532,16 @@ l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
"short for ID value\n", __func__);
return;
}
- id = (*buf++) << 24;
- id += (*buf++) << 16;
- id += (*buf++) << 8;
- id += (*buf++);
+ packet_id = (*buf++) << 24;
+ packet_id += (*buf++) << 16;
+ packet_id += (*buf++) << 8;
+ packet_id += (*buf++);
len -= 4;
- if (id != hc->id) {
+ if (packet_id != hc->id) {
printk(KERN_WARNING "%s: packet error - ID mismatch, "
"got 0x%x, we 0x%x\n",
- __func__, id, hc->id);
+ __func__, packet_id, hc->id);
return;
}
} else {
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
index fced1a2755f8..b73e952d12cf 100644
--- a/drivers/isdn/mISDN/layer1.c
+++ b/drivers/isdn/mISDN/layer1.c
@@ -18,10 +18,11 @@
#include <linux/module.h>
#include <linux/mISDNhw.h>
+#include "core.h"
#include "layer1.h"
#include "fsm.h"
-static int *debug;
+static u_int *debug;
struct layer1 {
u_long Flags;
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
index a7915a156c04..d6e2863f224a 100644
--- a/drivers/isdn/mISDN/layer2.c
+++ b/drivers/isdn/mISDN/layer2.c
@@ -15,10 +15,12 @@
*
*/
+#include <linux/mISDNif.h>
+#include "core.h"
#include "fsm.h"
#include "layer2.h"
-static int *debug;
+static u_int *debug;
static
struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
@@ -465,7 +467,7 @@ IsRNR(u_char *data, struct layer2 *l2)
data[0] == RNR : (data[0] & 0xf) == RNR;
}
-int
+static int
iframe_error(struct layer2 *l2, struct sk_buff *skb)
{
u_int i;
@@ -483,7 +485,7 @@ iframe_error(struct layer2 *l2, struct sk_buff *skb)
return 0;
}
-int
+static int
super_error(struct layer2 *l2, struct sk_buff *skb)
{
if (skb->len != l2addrsize(l2) +
@@ -492,7 +494,7 @@ super_error(struct layer2 *l2, struct sk_buff *skb)
return 0;
}
-int
+static int
unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
{
int rsp = (*skb->data & 0x2) >> 1;
@@ -505,7 +507,7 @@ unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
return 0;
}
-int
+static int
UI_error(struct layer2 *l2, struct sk_buff *skb)
{
int rsp = *skb->data & 0x2;
@@ -518,7 +520,7 @@ UI_error(struct layer2 *l2, struct sk_buff *skb)
return 0;
}
-int
+static int
FRMR_error(struct layer2 *l2, struct sk_buff *skb)
{
u_int headers = l2addrsize(l2) + 1;
@@ -1065,7 +1067,7 @@ l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
}
}
-void
+static void
enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
{
struct sk_buff *skb;
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index e5a20f9542d1..37a2de18cfd0 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -18,7 +18,7 @@
#include <linux/mISDNif.h>
#include "core.h"
-static int *debug;
+static u_int *debug;
static struct proto mISDN_proto = {
.name = "misdn",
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
index 54cfddcc4784..d55b14ae4e99 100644
--- a/drivers/isdn/mISDN/stack.c
+++ b/drivers/isdn/mISDN/stack.c
@@ -36,7 +36,7 @@ _queue_message(struct mISDNstack *st, struct sk_buff *skb)
}
}
-int
+static int
mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
{
_queue_message(ch->st, skb);
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
index 6fbae42127bf..5c43d19e7c11 100644
--- a/drivers/isdn/mISDN/tei.c
+++ b/drivers/isdn/mISDN/tei.c
@@ -393,7 +393,7 @@ dl_unit_data(struct manager *mgr, struct sk_buff *skb)
return 0;
}
-unsigned int
+static unsigned int
random_ri(void)
{
u16 x;
@@ -1287,7 +1287,7 @@ create_teimanager(struct mISDNdevice *dev)
if (!mgr)
return -ENOMEM;
INIT_LIST_HEAD(&mgr->layer2);
- mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+ rwlock_init(&mgr->lock);
skb_queue_head_init(&mgr->sendq);
mgr->nextid = 1;
mgr->lastid = MISDN_ID_NONE;
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index 875fabe16e36..f2b32186d4a1 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -23,8 +23,9 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mISDNif.h>
+#include "core.h"
-static int *debug;
+static u_int *debug;
struct mISDNtimerdev {
@@ -85,7 +86,7 @@ mISDN_close(struct inode *ino, struct file *filep)
}
static ssize_t
-mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
{
struct mISDNtimerdev *dev = filep->private_data;
struct mISDNtimer *timer;
@@ -115,7 +116,7 @@ mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
timer = (struct mISDNtimer *)dev->expired.next;
list_del(&timer->list);
spin_unlock_irqrestore(&dev->lock, flags);
- if (put_user(timer->id, (int *)buf))
+ if (put_user(timer->id, (int __user *)buf))
ret = -EFAULT;
else
ret = sizeof(int);
@@ -274,7 +275,7 @@ static struct miscdevice mISDNtimer = {
};
int
-mISDN_inittimer(int *deb)
+mISDN_inittimer(u_int *deb)
{
int err;
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index 5faefeaf6790..f2c641e0bdde 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -164,7 +164,7 @@ void copy_gdt(const struct lg_cpu *cpu, struct desc_struct *gdt);
void copy_gdt_tls(const struct lg_cpu *cpu, struct desc_struct *gdt);
/* page_tables.c: */
-int init_guest_pagetable(struct lguest *lg, unsigned long pgtable);
+int init_guest_pagetable(struct lguest *lg);
void free_guest_pagetable(struct lguest *lg);
void guest_new_pagetable(struct lg_cpu *cpu, unsigned long pgtable);
void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 i);
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index a661bbdae3d6..915da6b8c924 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -250,7 +250,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* Figure out how many pages the ring will take, and map that memory */
lvq->pages = lguest_map((unsigned long)lvq->config.pfn << PAGE_SHIFT,
DIV_ROUND_UP(vring_size(lvq->config.num,
- PAGE_SIZE),
+ LGUEST_VRING_ALIGN),
PAGE_SIZE));
if (!lvq->pages) {
err = -ENOMEM;
@@ -259,8 +259,8 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
/* OK, tell virtio_ring.c to set up a virtqueue now we know its size
* and we've got a pointer to its pages. */
- vq = vring_new_virtqueue(lvq->config.num, vdev, lvq->pages,
- lg_notify, callback);
+ vq = vring_new_virtqueue(lvq->config.num, LGUEST_VRING_ALIGN,
+ vdev, lvq->pages, lg_notify, callback);
if (!vq) {
err = -ENOMEM;
goto unmap;
@@ -272,7 +272,7 @@ static struct virtqueue *lg_find_vq(struct virtio_device *vdev,
* the interrupt as a source of randomness: it'd be nice to have that
* back.. */
err = request_irq(lvq->config.irq, vring_interrupt, IRQF_SHARED,
- vdev->dev.bus_id, vq);
+ dev_name(&vdev->dev), vq);
if (err)
goto destroy_vring;
diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c
index e73a000473cc..34bc017b8b3c 100644
--- a/drivers/lguest/lguest_user.c
+++ b/drivers/lguest/lguest_user.c
@@ -146,7 +146,7 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
return 0;
}
-/*L:020 The initialization write supplies 4 pointer sized (32 or 64 bit)
+/*L:020 The initialization write supplies 3 pointer sized (32 or 64 bit)
* values (in addition to the LHREQ_INITIALIZE value). These are:
*
* base: The start of the Guest-physical memory inside the Launcher memory.
@@ -155,9 +155,6 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
* allowed to access. The Guest memory lives inside the Launcher, so it sets
* this to ensure the Guest can only reach its own memory.
*
- * pgdir: The (Guest-physical) address of the top of the initial Guest
- * pagetables (which are set up by the Launcher).
- *
* start: The first instruction to execute ("eip" in x86-speak).
*/
static int initialize(struct file *file, const unsigned long __user *input)
@@ -166,7 +163,7 @@ static int initialize(struct file *file, const unsigned long __user *input)
* Guest. */
struct lguest *lg;
int err;
- unsigned long args[4];
+ unsigned long args[3];
/* We grab the Big Lguest lock, which protects against multiple
* simultaneous initializations. */
@@ -192,14 +189,14 @@ static int initialize(struct file *file, const unsigned long __user *input)
lg->mem_base = (void __user *)args[0];
lg->pfn_limit = args[1];
- /* This is the first cpu (cpu 0) and it will start booting at args[3] */
- err = lg_cpu_start(&lg->cpus[0], 0, args[3]);
+ /* This is the first cpu (cpu 0) and it will start booting at args[2] */
+ err = lg_cpu_start(&lg->cpus[0], 0, args[2]);
if (err)
goto release_guest;
/* Initialize the Guest's shadow page tables, using the toplevel
* address the Launcher gave us. This allocates memory, so can fail. */
- err = init_guest_pagetable(lg, args[2]);
+ err = init_guest_pagetable(lg);
if (err)
goto free_regs;
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c
index 81d0c6053447..576a8318221c 100644
--- a/drivers/lguest/page_tables.c
+++ b/drivers/lguest/page_tables.c
@@ -14,6 +14,7 @@
#include <linux/percpu.h>
#include <asm/tlbflush.h>
#include <asm/uaccess.h>
+#include <asm/bootparam.h>
#include "lg.h"
/*M:008 We hold reference to pages, which prevents them from being swapped.
@@ -581,15 +582,82 @@ void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx)
release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx);
}
+/* Once we know how much memory we have we can construct simple identity
+ * (which set virtual == physical) and linear mappings
+ * which will get the Guest far enough into the boot to create its own.
+ *
+ * We lay them out of the way, just below the initrd (which is why we need to
+ * know its size here). */
+static unsigned long setup_pagetables(struct lguest *lg,
+ unsigned long mem,
+ unsigned long initrd_size)
+{
+ pgd_t __user *pgdir;
+ pte_t __user *linear;
+ unsigned int mapped_pages, i, linear_pages, phys_linear;
+ unsigned long mem_base = (unsigned long)lg->mem_base;
+
+ /* We have mapped_pages frames to map, so we need
+ * linear_pages page tables to map them. */
+ mapped_pages = mem / PAGE_SIZE;
+ linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE;
+
+ /* We put the toplevel page directory page at the top of memory. */
+ pgdir = (pgd_t *)(mem + mem_base - initrd_size - PAGE_SIZE);
+
+ /* Now we use the next linear_pages pages as pte pages */
+ linear = (void *)pgdir - linear_pages * PAGE_SIZE;
+
+ /* Linear mapping is easy: put every page's address into the
+ * mapping in order. */
+ for (i = 0; i < mapped_pages; i++) {
+ pte_t pte;
+ pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER));
+ if (copy_to_user(&linear[i], &pte, sizeof(pte)) != 0)
+ return -EFAULT;
+ }
+
+ /* The top level points to the linear page table pages above.
+ * We setup the identity and linear mappings here. */
+ phys_linear = (unsigned long)linear - mem_base;
+ for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
+ pgd_t pgd;
+ pgd = __pgd((phys_linear + i * sizeof(pte_t)) |
+ (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+ if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd))
+ || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET)
+ + i / PTRS_PER_PTE],
+ &pgd, sizeof(pgd)))
+ return -EFAULT;
+ }
+
+ /* We return the top level (guest-physical) address: remember where
+ * this is. */
+ return (unsigned long)pgdir - mem_base;
+}
+
/*H:500 (vii) Setting up the page tables initially.
*
* When a Guest is first created, the Launcher tells us where the toplevel of
* its first page table is. We set some things up here: */
-int init_guest_pagetable(struct lguest *lg, unsigned long pgtable)
+int init_guest_pagetable(struct lguest *lg)
{
+ u64 mem;
+ u32 initrd_size;
+ struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
+
+ /* Get the Guest memory size and the ramdisk size from the boot header
+ * located at lg->mem_base (Guest address 0). */
+ if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
+ || get_user(initrd_size, &boot->hdr.ramdisk_size))
+ return -EFAULT;
+
/* We start on the first shadow page table, and give it a blank PGD
* page. */
- lg->pgdirs[0].gpgdir = pgtable;
+ lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size);
+ if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir))
+ return lg->pgdirs[0].gpgdir;
lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
if (!lg->pgdirs[0].pgdir)
return -ENOMEM;
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c
index 741a93a3eb61..62dd1fdafecf 100644
--- a/drivers/macintosh/via-cuda.c
+++ b/drivers/macintosh/via-cuda.c
@@ -23,7 +23,6 @@
#else
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#endif
#include <asm/io.h>
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
index 6e6dd17ab572..817f37a875c9 100644
--- a/drivers/macintosh/via-macii.c
+++ b/drivers/macintosh/via-macii.c
@@ -33,7 +33,6 @@
#include <linux/init.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#include <asm/system.h>
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
index 2dc788042707..4d686c0bdea0 100644
--- a/drivers/macintosh/via-maciisi.c
+++ b/drivers/macintosh/via-maciisi.c
@@ -24,7 +24,6 @@
#include <linux/interrupt.h>
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
static volatile unsigned char *via;
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index d524dc245a2c..b40fb9b6c862 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -1814,7 +1814,7 @@ static int powerbook_sleep_grackle(void)
_set_L2CR(save_l2cr);
/* Restore userland MMU context */
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
/* Power things up */
pmu_unlock();
@@ -1903,7 +1903,7 @@ powerbook_sleep_Core99(void)
_set_L3CR(save_l3cr);
/* Restore userland MMU context */
- set_context(current->active_mm->context.id, current->active_mm->pgd);
+ switch_mmu_context(NULL, current->active_mm);
/* Tell PMU we are ready */
pmu_unlock();
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index b64741c95ac4..fb9fa614a0e8 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -35,7 +35,6 @@
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#include <asm/pgtable.h>
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index 7f2be4baaeda..7847e981ac33 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -87,11 +87,12 @@ struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
return NULL;
}
- len = i2c_smbus_read_word_data(&sat->i2c, 9);
- if (len < 0) {
+ err = i2c_smbus_read_word_data(&sat->i2c, 9);
+ if (err < 0) {
printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
return NULL;
}
+ len = err;
if (len == 0) {
printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
return NULL;
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index ac89a5deaca2..ab7c8e4a61f9 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -208,16 +208,19 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
*/
/* IO operations when bitmap is stored near all superblocks */
-static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long index)
+static struct page *read_sb_page(mddev_t *mddev, long offset,
+ struct page *page,
+ unsigned long index, int size)
{
/* choose a good rdev and read the page from there */
mdk_rdev_t *rdev;
struct list_head *tmp;
- struct page *page = alloc_page(GFP_KERNEL);
sector_t target;
if (!page)
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
return ERR_PTR(-ENOMEM);
rdev_for_each(rdev, tmp, mddev) {
@@ -227,7 +230,9 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde
target = rdev->sb_start + offset + index * (PAGE_SIZE/512);
- if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) {
+ if (sync_page_io(rdev->bdev, target,
+ roundup(size, bdev_hardsect_size(rdev->bdev)),
+ page, READ)) {
page->index = index;
attach_page_buffers(page, NULL); /* so that free_buffer will
* quietly no-op */
@@ -544,7 +549,9 @@ static int bitmap_read_sb(struct bitmap *bitmap)
bitmap->sb_page = read_page(bitmap->file, 0, bitmap, bytes);
} else {
- bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset, 0);
+ bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset,
+ NULL,
+ 0, sizeof(bitmap_super_t));
}
if (IS_ERR(bitmap->sb_page)) {
err = PTR_ERR(bitmap->sb_page);
@@ -957,11 +964,16 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
*/
page = bitmap->sb_page;
offset = sizeof(bitmap_super_t);
+ read_sb_page(bitmap->mddev, bitmap->offset,
+ page,
+ index, count);
} else if (file) {
page = read_page(file, index, bitmap, count);
offset = 0;
} else {
- page = read_sb_page(bitmap->mddev, bitmap->offset, index);
+ page = read_sb_page(bitmap->mddev, bitmap->offset,
+ NULL,
+ index, count);
offset = 0;
}
if (IS_ERR(page)) { /* read error */
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index ce26c84af064..3326750ec02c 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1060,7 +1060,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_page_pool;
}
- cc->bs = bioset_create(MIN_IOS, MIN_IOS);
+ cc->bs = bioset_create(MIN_IOS, 0);
if (!cc->bs) {
ti->error = "Cannot allocate crypt bioset";
goto bad_bs;
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 2fd6d4450637..a34338567a2a 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -56,7 +56,7 @@ struct dm_io_client *dm_io_client_create(unsigned num_pages)
if (!client->pool)
goto bad;
- client->bios = bioset_create(16, 16);
+ client->bios = bioset_create(16, 0);
if (!client->bios)
goto bad;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 343094c3feeb..421c9f02d8ca 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1093,7 +1093,7 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->tio_pool)
goto bad_tio_pool;
- md->bs = bioset_create(16, 16);
+ md->bs = bioset_create(16, 0);
if (!md->bs)
goto bad_no_bioset;
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 4952aeb5dd80..d8229a0e9a9c 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -2391,6 +2391,67 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel);
+/* Kworld Plus TV Analog Lite PCI IR
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = {
+ [0x0c] = KEY_PROG1, /* Kworld key */
+ [0x16] = KEY_CLOSECD, /* -> ) */
+ [0x1d] = KEY_POWER2,
+
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3, /* Two keys have the same code: 3 and left */
+ [0x03] = KEY_4, /* Two keys have the same code: 3 and right */
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x0a] = KEY_0,
+
+ [0x09] = KEY_AGAIN,
+ [0x14] = KEY_MUTE,
+
+ [0x20] = KEY_UP,
+ [0x21] = KEY_DOWN,
+ [0x0b] = KEY_ENTER,
+
+ [0x10] = KEY_CHANNELUP,
+ [0x11] = KEY_CHANNELDOWN,
+
+ /* Couldn't map key left/key right since those
+ conflict with '3' and '4' scancodes
+ I dunno what the original driver does
+ */
+
+ [0x13] = KEY_VOLUMEUP,
+ [0x12] = KEY_VOLUMEDOWN,
+
+ /* The lower part of the IR
+ There are several duplicated keycodes there.
+ Most of them conflict with digits.
+ Add mappings just to the unused scancodes.
+ Somehow, the original driver has a way to know,
+ but this doesn't seem to be on some GPIO.
+ Also, it is not related to the time between keyup
+ and keydown.
+ */
+ [0x19] = KEY_PAUSE, /* Timeshift */
+ [0x1a] = KEY_STOP,
+ [0x1b] = KEY_RECORD,
+
+ [0x22] = KEY_TEXT,
+
+ [0x15] = KEY_AUDIO, /* ((*)) */
+ [0x0f] = KEY_ZOOM,
+ [0x1c] = KEY_SHUFFLE, /* snapshot */
+
+ [0x18] = KEY_RED, /* B */
+ [0x23] = KEY_GREEN, /* C */
+};
+EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog);
+
IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
[0x20] = KEY_LIST,
[0x00] = KEY_POWER,
@@ -2511,3 +2572,35 @@ IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);
+
+/* ATI TV Wonder HD 600 USB
+ Devin Heitmueller <devin.heitmueller@gmail.com>
+ */
+IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_RECORD, /* Row 1 */
+ [0x01] = KEY_PLAYPAUSE,
+ [0x02] = KEY_STOP,
+ [0x03] = KEY_POWER,
+ [0x04] = KEY_PREVIOUS, /* Row 2 */
+ [0x05] = KEY_REWIND,
+ [0x06] = KEY_FORWARD,
+ [0x07] = KEY_NEXT,
+ [0x08] = KEY_EPG, /* Row 3 */
+ [0x09] = KEY_HOME,
+ [0x0a] = KEY_MENU,
+ [0x0b] = KEY_CHANNELUP,
+ [0x0c] = KEY_BACK, /* Row 4 */
+ [0x0d] = KEY_UP,
+ [0x0e] = KEY_INFO,
+ [0x0f] = KEY_CHANNELDOWN,
+ [0x10] = KEY_LEFT, /* Row 5 */
+ [0x11] = KEY_SELECT,
+ [0x12] = KEY_RIGHT,
+ [0x13] = KEY_VOLUMEUP,
+ [0x14] = KEY_LAST, /* Row 6 */
+ [0x15] = KEY_DOWN,
+ [0x16] = KEY_MUTE,
+ [0x17] = KEY_VOLUMEDOWN,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600);
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 127b0526a727..7d844af88384 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -313,7 +313,7 @@ static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
/*
DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
*/
- return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl);
+ return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl);
}
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index fe0bd55977e3..101b01dbb8ea 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -834,7 +834,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
* copying is done already, arg is a kernel pointer.
*/
-static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
@@ -1216,17 +1216,11 @@ static int __saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *a
#endif
default:
return v4l_compat_translate_ioctl(file, cmd, arg,
- __saa7146_video_do_ioctl);
+ saa7146_video_do_ioctl);
}
return 0;
}
-int saa7146_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __saa7146_video_do_ioctl(file, cmd, arg);
-}
-
/*********************************************************************************/
/* buffer handling functions */
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index a8878244bb3c..31522d2e318e 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -3598,7 +3598,7 @@ static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
76, 77, 91, 134, 135, 137, 147,
156, 166, 167, 168, 25 };
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
status += MXL_BlockInit(fe);
@@ -3630,7 +3630,7 @@ static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal,
*/
#endif
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
for (i = 0 ; i < *count; i++) {
RegNum[i] = RegAddr[i];
@@ -3648,7 +3648,7 @@ static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
u8 RegAddr[] = {43, 136};
- *count = sizeof(RegAddr) / sizeof(u8);
+ *count = ARRAY_SIZE(RegAddr);
for (i = 0; i < *count; i++) {
RegNum[i] = RegAddr[i];
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
index 4a74f65e759a..f4d931f14fad 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/common/tuners/tda827x.c
@@ -80,10 +80,11 @@ static void tda827x_set_std(struct dvb_frontend *fe,
mode = "xx";
}
- if (params->mode == V4L2_TUNER_RADIO)
+ if (params->mode == V4L2_TUNER_RADIO) {
priv->sgIF = 88; /* if frequency is 5.5 MHz */
-
- dprintk("setting tda827x to system %s\n", mode);
+ dprintk("setting tda827x to radio FM\n");
+ } else
+ dprintk("setting tda827x to system %s\n", mode);
}
@@ -199,7 +200,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->frequency = params->frequency;
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
return 0;
@@ -304,7 +305,7 @@ static int tda827xo_set_analog_params(struct dvb_frontend *fe,
reg2[1] = 0x08; /* Vsync en */
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = freq * 62500;
+ priv->frequency = params->frequency;
return 0;
}
@@ -591,7 +592,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = tuner_freq - if_freq; // FIXME
+ priv->frequency = params->frequency;
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
return 0;
@@ -691,7 +692,7 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe,
tuner_reg[1] = 0x19 + (priv->lpsel << 1);
i2c_transfer(priv->i2c_adap, &msg, 1);
- priv->frequency = freq * 62500;
+ priv->frequency = params->frequency;
return 0;
}
diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c
index c112bdd4e0f0..0ee79fd7c7a9 100644
--- a/drivers/media/common/tuners/tda8290.c
+++ b/drivers/media/common/tuners/tda8290.c
@@ -32,6 +32,9 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
+static int deemphasis_50;
+MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis");
+
/* ---------------------------------------------------------------------- */
struct tda8290_priv {
@@ -139,9 +142,34 @@ static void set_audio(struct dvb_frontend *fe,
mode = "xx";
}
- tuner_dbg("setting tda829x to system %s\n", mode);
+ if (params->mode == V4L2_TUNER_RADIO) {
+ priv->tda8290_easy_mode = 0x01; /* Start with MN values */
+ tuner_dbg("setting to radio FM\n");
+ } else {
+ tuner_dbg("setting tda829x to system %s\n", mode);
+ }
}
+struct {
+ unsigned char seq[2];
+} fm_mode[] = {
+ { { 0x01, 0x81} }, /* Put device into expert mode */
+ { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */
+ { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */
+ { { 0x05, 0x04} }, /* ADC headroom */
+ { { 0x06, 0x10} }, /* group delay flat */
+
+ { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */
+ { { 0x08, 0x00} },
+ { { 0x09, 0x80} },
+ { { 0x0a, 0xda} },
+ { { 0x0b, 0x4b} },
+ { { 0x0c, 0x68} },
+
+ { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */
+ { { 0x14, 0x00} }, /* disable auto mute if no video */
+};
+
static void tda8290_set_params(struct dvb_frontend *fe,
struct analog_parameters *params)
{
@@ -178,15 +206,30 @@ static void tda8290_set_params(struct dvb_frontend *fe,
tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2);
msleep(1);
- expert_mode[1] = priv->tda8290_easy_mode + 0x80;
- tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
- if (priv->tda8290_easy_mode & 0x60)
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
- else
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
+ if (params->mode == V4L2_TUNER_RADIO) {
+ int i;
+ unsigned char deemphasis[] = { 0x13, 1 };
+
+ /* FIXME: allow using a different deemphasis */
+
+ if (deemphasis_50)
+ deemphasis[1] = 2;
+
+ for (i = 0; i < ARRAY_SIZE(fm_mode); i++)
+ tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2);
+
+ tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2);
+ } else {
+ expert_mode[1] = priv->tda8290_easy_mode + 0x80;
+ tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
+ if (priv->tda8290_easy_mode & 0x60)
+ tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
+ else
+ tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
+ tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
+ }
tda8290_i2c_bridge(fe, 1);
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c
index ff1788cc5d48..544cdbe88a6c 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/common/tuners/tda9887.c
@@ -180,11 +180,10 @@ static struct tvnorm tvnorms[] = {
},{
.std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
.name = "SECAM-BGH",
- .b = ( cPositiveAmTV |
+ .b = ( cNegativeFmTV |
cQSS ),
.c = ( cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_5_5 |
+ .e = ( cAudioIF_5_5 |
cVideoIF_38_90 ),
},{
.std = V4L2_STD_SECAM_L,
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index b65e6803e6c6..1adce9ff52ce 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -28,6 +28,12 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(debug, "0 (default) powers device off when not used.\n"
+ "1 keep device energized and with tuner ready all the times.\n"
+ " Faster, but consumes more power and keeps the device hotter\n");
+
static char audio_std[8];
module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
MODULE_PARM_DESC(audio_std,
@@ -1091,6 +1097,34 @@ static int xc2028_set_params(struct dvb_frontend *fe,
T_DIGITAL_TV, type, 0, demod);
}
+static int xc2028_sleep(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc = 0;
+
+ /* Avoid firmware reload on slow devices */
+ if (no_poweroff)
+ return 0;
+
+ tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
+ if (debug > 1) {
+ tuner_dbg("Printing sleep stack trace:\n");
+ dump_stack();
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00});
+
+ priv->cur_fw.type = 0; /* need firmware reload */
+
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
static int xc2028_dvb_release(struct dvb_frontend *fe)
{
@@ -1171,6 +1205,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
.get_frequency = xc2028_get_frequency,
.get_rf_strength = xc2028_signal,
.set_params = xc2028_set_params,
+ .sleep = xc2028_sleep,
};
struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index e12d13e0cbe9..493ce93caf43 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -36,10 +36,6 @@ static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-static int xc5000_load_fw_on_attach;
-module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644);
-MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization.");
-
static DEFINE_MUTEX(xc5000_list_mutex);
static LIST_HEAD(hybrid_tuner_instance_list);
@@ -1017,9 +1013,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
sizeof(struct dvb_tuner_ops));
- if (xc5000_load_fw_on_attach)
- xc5000_init(fe);
-
return fe;
fail:
mutex_unlock(&xc5000_list_mutex);
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index 0bcd852576d6..40ebde53b3ce 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -2,6 +2,19 @@
# DVB device configuration
#
+config DVB_DYNAMIC_MINORS
+ bool "Dynamic DVB minor allocation"
+ depends on DVB_CORE
+ default n
+ help
+ If you say Y here, the DVB subsystem will use dynamic minor
+ allocation for any device that uses the DVB major number.
+ This means that you can have more than 4 of a single type
+ of device (like demuxes and frontends) per adapter, but udev
+ will be required to manage the device nodes.
+
+ If you are unsure about this, say N here.
+
menuconfig DVB_CAPTURE_DRIVERS
bool "DVB/ATSC adapters"
depends on DVB_CORE
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index 73dc2ee9b014..a8c6249c4099 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,11 +9,12 @@ config DVB_B2C2_FLEXCOP
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_BCM3510 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1420 if !DVB_FE_CUSTOMISE
select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+ select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE
help
Support for the digital TV receiver chip made by B2C2 Inc. included in
Technisats PCI cards and USB boxes.
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index a127a4175c40..5cded3708541 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -628,12 +628,14 @@ int flexcop_frontend_init(struct flexcop_device *fc)
}
/* try the cable dvb (stv0297) */
+ fc->fc_i2c_adap[0].no_base_addr = 1;
fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
if (fc->fe != NULL) {
fc->dev_type = FC_CABLE;
fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
goto fe_found;
}
+ fc->fc_i2c_adap[0].no_base_addr = 0;
/* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
fc->fe = dvb_attach(mt312_attach,
diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c
index 43a112ec6d44..f13783f08f0f 100644
--- a/drivers/media/dvb/b2c2/flexcop-i2c.c
+++ b/drivers/media/dvb/b2c2/flexcop-i2c.c
@@ -47,9 +47,13 @@ static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c,
int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */
ret;
- r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
ret = flexcop_i2c_operation(i2c->fc, &r100);
if (ret != 0) {
+ deb_i2c("Retrying operation\n");
+ r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
+ ret = flexcop_i2c_operation(i2c->fc, &r100);
+ }
+ if (ret != 0) {
deb_i2c("read failed. %d\n", ret);
return ret;
}
diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c
index 5f79c8dc3836..676413a915b4 100644
--- a/drivers/media/dvb/b2c2/flexcop.c
+++ b/drivers/media/dvb/b2c2/flexcop.c
@@ -270,7 +270,7 @@ int flexcop_device_initialize(struct flexcop_device *fc)
/* do the MAC address reading after initializing the dvb_adapter */
if (fc->get_mac_addr(fc, 0) == 0) {
u8 *b = fc->dvb_adapter.proposed_mac;
- info("MAC address = %02x:%02x:%02x:%02x:%02x:%02x", b[0],b[1],b[2],b[3],b[4],b[5]);
+ info("MAC address = %pM", b);
flexcop_set_mac_filter(fc,b);
flexcop_mac_filter_ctrl(fc,1);
} else
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index 7e9c090fc04e..27edb0ece587 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -8,7 +8,7 @@ config DVB_BT8XX
select DVB_OR51211 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
help
Support for PCI cards based on the Bt8xx PCI bridge. Examples are
the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index aa3db57d32d9..29e8f1546ab6 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -917,9 +917,7 @@ static int dst_get_mac(struct dst_state *state)
}
memset(&state->mac_address, '\0', 8);
memcpy(&state->mac_address, &state->rxbuffer, 6);
- dprintk(verbose, DST_ERROR, 1, "MAC Address=[%02x:%02x:%02x:%02x:%02x:%02x]",
- state->mac_address[0], state->mac_address[1], state->mac_address[2],
- state->mac_address[4], state->mac_address[5], state->mac_address[6]);
+ dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address);
return 0;
}
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index c1d92f838ca8..f48f73aff195 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -19,7 +19,6 @@
*
*/
-#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -368,7 +367,7 @@ static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
{
dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
- return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr);
+ return !dm1105dvb->ts_buf;
}
static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
@@ -697,8 +696,7 @@ static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
};
dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2);
- dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ dev_info(&dm1105dvb->pdev->dev, "MAC %pM\n", mac);
}
static int __devinit dm1105_probe(struct pci_dev *pdev,
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 7a421e9dba5a..171f9ca124f7 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -128,6 +128,7 @@ struct dvb_frontend_private {
unsigned int step_size;
int quality;
unsigned int check_wrapped;
+ enum dvbfe_search algo_status;
};
static void dvb_frontend_wakeup(struct dvb_frontend *fe);
@@ -516,6 +517,8 @@ static int dvb_frontend_thread(void *data)
struct dvb_frontend_private *fepriv = fe->frontend_priv;
unsigned long timeout;
fe_status_t s;
+ enum dvbfe_algo algo;
+
struct dvb_frontend_parameters *params;
dprintk("%s\n", __func__);
@@ -562,23 +565,80 @@ restart:
/* do an iteration of the tuning loop */
if (fe->ops.get_frontend_algo) {
- if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) {
- /* have we been asked to retune? */
- params = NULL;
+ algo = fe->ops.get_frontend_algo(fe);
+ switch (algo) {
+ case DVBFE_ALGO_HW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+ params = NULL; /* have we been asked to RETUNE ? */
+
if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
params = &fepriv->parameters;
fepriv->state = FESTATE_TUNED;
}
- fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
- if (s != fepriv->status) {
+ if (fe->ops.tune)
+ fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+ dprintk("%s: state changed, adding current state\n", __func__);
dvb_frontend_add_event(fe, s);
fepriv->status = s;
}
- } else
+ break;
+ case DVBFE_ALGO_SW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
dvb_frontend_swzigzag(fe);
- } else
+ break;
+ case DVBFE_ALGO_CUSTOM:
+ params = NULL; /* have we been asked to RETUNE ? */
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+ if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__);
+ params = &fepriv->parameters;
+ fepriv->state = FESTATE_TUNED;
+ }
+ /* Case where we are going to search for a carrier
+ * User asked us to retune again for some reason, possibly
+ * requesting a search with a new set of parameters
+ */
+ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+ if (fe->ops.search) {
+ fepriv->algo_status = fe->ops.search(fe, &fepriv->parameters);
+ /* We did do a search as was requested, the flags are
+ * now unset as well and has the flags wrt to search.
+ */
+ } else {
+ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+ }
+ }
+ /* Track the carrier if the search was successful */
+ if (fepriv->algo_status == DVBFE_ALGO_SEARCH_SUCCESS) {
+ if (fe->ops.track)
+ fe->ops.track(fe, &fepriv->parameters);
+ } else {
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ fepriv->delay = HZ / 2;
+ }
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s); /* update event list */
+ fepriv->status = s;
+ if (!(s & FE_HAS_LOCK)) {
+ fepriv->delay = HZ / 10;
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ } else {
+ fepriv->delay = 60 * HZ;
+ }
+ }
+ break;
+ default:
+ dprintk("%s: UNDEFINED ALGO !\n", __func__);
+ break;
+ }
+ } else {
dvb_frontend_swzigzag(fe);
+ }
}
if (dvb_powerdown_on_sleep) {
@@ -1226,6 +1286,9 @@ int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp,
dprintk("%s() Finalised property cache\n", __func__);
dtv_property_cache_submit(fe);
+ /* Request the search algorithm to search */
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
&fepriv->parameters);
break;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index db4a63b0a32e..e176da472d7a 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -69,6 +69,125 @@ struct analog_parameters {
u64 std;
};
+enum dvbfe_modcod {
+ DVBFE_MODCOD_DUMMY_PLFRAME = 0,
+ DVBFE_MODCOD_QPSK_1_4,
+ DVBFE_MODCOD_QPSK_1_3,
+ DVBFE_MODCOD_QPSK_2_5,
+ DVBFE_MODCOD_QPSK_1_2,
+ DVBFE_MODCOD_QPSK_3_5,
+ DVBFE_MODCOD_QPSK_2_3,
+ DVBFE_MODCOD_QPSK_3_4,
+ DVBFE_MODCOD_QPSK_4_5,
+ DVBFE_MODCOD_QPSK_5_6,
+ DVBFE_MODCOD_QPSK_8_9,
+ DVBFE_MODCOD_QPSK_9_10,
+ DVBFE_MODCOD_8PSK_3_5,
+ DVBFE_MODCOD_8PSK_2_3,
+ DVBFE_MODCOD_8PSK_3_4,
+ DVBFE_MODCOD_8PSK_5_6,
+ DVBFE_MODCOD_8PSK_8_9,
+ DVBFE_MODCOD_8PSK_9_10,
+ DVBFE_MODCOD_16APSK_2_3,
+ DVBFE_MODCOD_16APSK_3_4,
+ DVBFE_MODCOD_16APSK_4_5,
+ DVBFE_MODCOD_16APSK_5_6,
+ DVBFE_MODCOD_16APSK_8_9,
+ DVBFE_MODCOD_16APSK_9_10,
+ DVBFE_MODCOD_32APSK_3_4,
+ DVBFE_MODCOD_32APSK_4_5,
+ DVBFE_MODCOD_32APSK_5_6,
+ DVBFE_MODCOD_32APSK_8_9,
+ DVBFE_MODCOD_32APSK_9_10,
+ DVBFE_MODCOD_RESERVED_1,
+ DVBFE_MODCOD_BPSK_1_3,
+ DVBFE_MODCOD_BPSK_1_4,
+ DVBFE_MODCOD_RESERVED_2
+};
+
+enum tuner_param {
+ DVBFE_TUNER_FREQUENCY = (1 << 0),
+ DVBFE_TUNER_TUNERSTEP = (1 << 1),
+ DVBFE_TUNER_IFFREQ = (1 << 2),
+ DVBFE_TUNER_BANDWIDTH = (1 << 3),
+ DVBFE_TUNER_REFCLOCK = (1 << 4),
+ DVBFE_TUNER_IQSENSE = (1 << 5),
+ DVBFE_TUNER_DUMMY = (1 << 31)
+};
+
+/*
+ * ALGO_HW: (Hardware Algorithm)
+ * ----------------------------------------------------------------
+ * Devices that support this algorithm do everything in hardware
+ * and no software support is needed to handle them.
+ * Requesting these devices to LOCK is the only thing required,
+ * device is supposed to do everything in the hardware.
+ *
+ * ALGO_SW: (Software Algorithm)
+ * ----------------------------------------------------------------
+ * These are dumb devices, that require software to do everything
+ *
+ * ALGO_CUSTOM: (Customizable Agorithm)
+ * ----------------------------------------------------------------
+ * Devices having this algorithm can be customized to have specific
+ * algorithms in the frontend driver, rather than simply doing a
+ * software zig-zag. In this case the zigzag maybe hardware assisted
+ * or it maybe completely done in hardware. In all cases, usage of
+ * this algorithm, in conjunction with the search and track
+ * callbacks, utilizes the driver specific algorithm.
+ *
+ * ALGO_RECOVERY: (Recovery Algorithm)
+ * ----------------------------------------------------------------
+ * These devices have AUTO recovery capabilities from LOCK failure
+ */
+enum dvbfe_algo {
+ DVBFE_ALGO_HW = (1 << 0),
+ DVBFE_ALGO_SW = (1 << 1),
+ DVBFE_ALGO_CUSTOM = (1 << 2),
+ DVBFE_ALGO_RECOVERY = (1 << 31)
+};
+
+struct tuner_state {
+ u32 frequency;
+ u32 tunerstep;
+ u32 ifreq;
+ u32 bandwidth;
+ u32 iqsense;
+ u32 refclock;
+};
+
+/*
+ * search callback possible return status
+ *
+ * DVBFE_ALGO_SEARCH_SUCCESS
+ * The frontend search algorithm completed and returned succesfully
+ *
+ * DVBFE_ALGO_SEARCH_ASLEEP
+ * The frontend search algorithm is sleeping
+ *
+ * DVBFE_ALGO_SEARCH_FAILED
+ * The frontend search for a signal failed
+ *
+ * DVBFE_ALGO_SEARCH_INVALID
+ * The frontend search algorith was probably supplied with invalid
+ * parameters and the search is an invalid one
+ *
+ * DVBFE_ALGO_SEARCH_ERROR
+ * The frontend search algorithm failed due to some error
+ *
+ * DVBFE_ALGO_SEARCH_AGAIN
+ * The frontend search algorithm was requested to search again
+ */
+enum dvbfe_search {
+ DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0),
+ DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1),
+ DVBFE_ALGO_SEARCH_FAILED = (1 << 2),
+ DVBFE_ALGO_SEARCH_INVALID = (1 << 3),
+ DVBFE_ALGO_SEARCH_AGAIN = (1 << 4),
+ DVBFE_ALGO_SEARCH_ERROR = (1 << 31),
+};
+
+
struct dvb_tuner_ops {
struct dvb_tuner_info info;
@@ -99,6 +218,13 @@ struct dvb_tuner_ops {
* tuners which require sophisticated tuning loops, controlling each parameter seperately. */
int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+
+ /*
+ * These are provided seperately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter seperately.
+ */
+ int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+ int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
};
struct analog_demod_info {
@@ -142,7 +268,7 @@ struct dvb_frontend_ops {
unsigned int *delay,
fe_status_t *status);
/* get frontend tuning algorithm from the module */
- int (*get_frontend_algo)(struct dvb_frontend *fe);
+ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe);
/* these two are only used for the swzigzag code */
int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
@@ -167,6 +293,12 @@ struct dvb_frontend_ops {
int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
+ /* These callbacks are for devices that implement their own
+ * tuning algorithms, rather than a simple swzigzag
+ */
+ enum dvbfe_search (*search)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
+ int (*track)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
+
struct dvb_tuner_ops tuner_ops;
struct analog_demod_ops analog_ops;
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index c93019ca519e..03fd9dd5c685 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -345,7 +345,7 @@ static inline void reset_ule( struct dvb_net_priv *p )
*/
static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
unsigned long skipped = 0L;
const u8 *ts, *ts_end, *from_where = NULL;
u8 ts_remain = 0, how_much = 0, new_ts = 1;
@@ -460,8 +460,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
/* Drop partly decoded SNDU, reset state, resync on PUSI. */
if (priv->ule_skb) {
dev_kfree_skb( priv->ule_skb );
- ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
- ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+ priv->stats.rx_errors++;
+ priv->stats.rx_frame_errors++;
}
reset_ule(priv);
priv->need_pusi = 1;
@@ -573,7 +573,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
if (priv->ule_skb == NULL) {
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
dev->name);
- ((struct dvb_net_priv *)dev->priv)->stats.rx_dropped++;
+ priv->stats.rx_dropped++;
return;
}
@@ -800,7 +800,8 @@ static void dvb_net_sec(struct net_device *dev,
{
u8 *eth;
struct sk_buff *skb;
- struct net_device_stats *stats = &(((struct dvb_net_priv *) dev->priv)->stats);
+ struct net_device_stats *stats =
+ &((struct dvb_net_priv *) netdev_priv(dev))->stats;
int snap = 0;
/* note: pkt_len includes a 32bit checksum */
@@ -917,7 +918,7 @@ static int dvb_net_filter_sec_set(struct net_device *dev,
struct dmx_section_filter **secfilter,
u8 *mac, u8 *mac_mask)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
int ret;
*secfilter=NULL;
@@ -961,7 +962,7 @@ static int dvb_net_filter_sec_set(struct net_device *dev,
static int dvb_net_feed_start(struct net_device *dev)
{
int ret = 0, i;
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
struct dmx_demux *demux = priv->demux;
unsigned char *mac = (unsigned char *) dev->dev_addr;
@@ -1060,7 +1061,7 @@ error:
static int dvb_net_feed_stop(struct net_device *dev)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
int i, ret = 0;
dprintk("%s\n", __func__);
@@ -1113,7 +1114,7 @@ static int dvb_net_feed_stop(struct net_device *dev)
static int dvb_set_mc_filter (struct net_device *dev, struct dev_mc_list *mc)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
if (priv->multi_num == DVB_NET_MULTICAST_MAX)
return -ENOMEM;
@@ -1165,7 +1166,7 @@ static void wq_set_multicast_list (struct work_struct *work)
static void dvb_net_set_multicast_list (struct net_device *dev)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
schedule_work(&priv->set_multicast_list_wq);
}
@@ -1185,7 +1186,7 @@ static void wq_restart_net_feed (struct work_struct *work)
static int dvb_net_set_mac (struct net_device *dev, void *p)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
struct sockaddr *addr=p;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
@@ -1199,7 +1200,7 @@ static int dvb_net_set_mac (struct net_device *dev, void *p)
static int dvb_net_open(struct net_device *dev)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
priv->in_use++;
dvb_net_feed_start(dev);
@@ -1209,7 +1210,7 @@ static int dvb_net_open(struct net_device *dev)
static int dvb_net_stop(struct net_device *dev)
{
- struct dvb_net_priv *priv = dev->priv;
+ struct dvb_net_priv *priv = netdev_priv(dev);
priv->in_use--;
return dvb_net_feed_stop(dev);
@@ -1217,7 +1218,7 @@ static int dvb_net_stop(struct net_device *dev)
static struct net_device_stats * dvb_net_get_stats(struct net_device *dev)
{
- return &((struct dvb_net_priv*) dev->priv)->stats;
+ return &((struct dvb_net_priv *) netdev_priv(dev))->stats;
}
static const struct header_ops dvb_header_ops = {
@@ -1287,7 +1288,7 @@ static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
dvbnet->device[if_num] = net;
- priv = net->priv;
+ priv = netdev_priv(net);
priv->net = net;
priv->demux = dvbnet->demux;
priv->pid = pid;
@@ -1320,7 +1321,7 @@ static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num)
if (!dvbnet->state[num])
return -EINVAL;
- priv = net->priv;
+ priv = netdev_priv(net);
if (priv->in_use)
return -EBUSY;
@@ -1376,7 +1377,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
netdev = dvbnet->device[dvbnetif->if_num];
- priv_data = netdev->priv;
+ priv_data = netdev_priv(netdev);
dvbnetif->pid=priv_data->pid;
dvbnetif->feedtype=priv_data->feedtype;
break;
@@ -1427,7 +1428,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
netdev = dvbnet->device[dvbnetif->if_num];
- priv_data = netdev->priv;
+ priv_data = netdev_priv(netdev);
dvbnetif->pid=priv_data->pid;
break;
}
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index a113744a56cc..6c571d9f011c 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -50,33 +50,27 @@ static const char * const dnames[] = {
"net", "osd"
};
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+#define MAX_DVB_MINORS 256
+#define DVB_MAX_IDS MAX_DVB_MINORS
+#else
#define DVB_MAX_IDS 4
#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
+#endif
static struct class *dvb_class;
-static struct dvb_device* dvbdev_find_device (int minor)
-{
- struct dvb_adapter *adap;
-
- list_for_each_entry(adap, &dvb_adapter_list, list_head) {
- struct dvb_device *dev;
- list_for_each_entry(dev, &adap->device_list, list_head)
- if (nums2minor(adap->num, dev->type, dev->id) == minor)
- return dev;
- }
-
- return NULL;
-}
-
+static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
+static DECLARE_RWSEM(minor_rwsem);
static int dvb_device_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev;
lock_kernel();
- dvbdev = dvbdev_find_device (iminor(inode));
+ down_read(&minor_rwsem);
+ dvbdev = dvb_minors[iminor(inode)];
if (dvbdev && dvbdev->fops) {
int err = 0;
@@ -92,9 +86,11 @@ static int dvb_device_open(struct inode *inode, struct file *file)
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
+ up_read(&minor_rwsem);
unlock_kernel();
return err;
}
+ up_read(&minor_rwsem);
unlock_kernel();
return -ENODEV;
}
@@ -192,6 +188,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
struct dvb_device *dvbdev;
struct file_operations *dvbdevfops;
struct device *clsdev;
+ int minor;
int id;
mutex_lock(&dvbdev_register_lock);
@@ -231,11 +228,31 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
list_add_tail (&dvbdev->list_head, &adap->device_list);
+ down_write(&minor_rwsem);
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+ for (minor = 0; minor < MAX_DVB_MINORS; minor++)
+ if (dvb_minors[minor] == NULL)
+ break;
+
+ if (minor == MAX_DVB_MINORS) {
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ mutex_unlock(&dvbdev_register_lock);
+ return -EINVAL;
+ }
+#else
+ minor = nums2minor(adap->num, type, id);
+#endif
+
+ dvbdev->minor = minor;
+ dvb_minors[minor] = dvbdev;
+ up_write(&minor_rwsem);
+
mutex_unlock(&dvbdev_register_lock);
clsdev = device_create(dvb_class, adap->device,
- MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
- NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
+ MKDEV(DVB_MAJOR, minor),
+ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
if (IS_ERR(clsdev)) {
printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
__func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
@@ -243,8 +260,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
}
dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
- adap->num, dnames[type], id, nums2minor(adap->num, type, id),
- nums2minor(adap->num, type, id));
+ adap->num, dnames[type], id, minor, minor);
return 0;
}
@@ -256,8 +272,11 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
if (!dvbdev)
return;
- device_destroy(dvb_class, MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
- dvbdev->type, dvbdev->id)));
+ down_write(&minor_rwsem);
+ dvb_minors[dvbdev->minor] = NULL;
+ up_write(&minor_rwsem);
+
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
list_del (&dvbdev->list_head);
kfree (dvbdev->fops);
@@ -413,6 +432,15 @@ out:
return err;
}
+static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
+ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
+ return 0;
+}
+
static int __init init_dvbdev(void)
{
int retval;
@@ -434,6 +462,7 @@ static int __init init_dvbdev(void)
retval = PTR_ERR(dvb_class);
goto error;
}
+ dvb_class->dev_uevent = dvb_uevent;
return 0;
error:
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 574e336bac35..dca49cf962e8 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -74,6 +74,7 @@ struct dvb_device {
struct file_operations *fops;
struct dvb_adapter *adapter;
int type;
+ int minor;
u32 id;
/* in theory, 'users' can vanish now,
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 62b68c291d99..49f7b20c25d6 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -24,8 +24,8 @@ config DVB_USB_A800
tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
depends on DVB_USB
select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
@@ -34,7 +34,7 @@ config DVB_USB_DIBUSB_MB
depends on DVB_USB
select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_DIB3000MB
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
help
Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
@@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
depends on DVB_USB
select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
help
Support for USB2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
@@ -73,11 +73,11 @@ config DVB_USB_DIB0700
select DVB_DIB7000M
select DVB_DIB3000MC
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
select DVB_TUNER_DIB0070
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE
help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
USB bridge is also present in devices having the DiB7700 DVB-T-USB
@@ -95,7 +95,7 @@ config DVB_USB_UMT_010
depends on DVB_USB
select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
@@ -107,11 +107,11 @@ config DVB_USB_CXUSB
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
select DVB_DIB7000P if !DVB_FE_CUSTOMISE
select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode
@@ -124,9 +124,9 @@ config DVB_USB_M920X
tristate "Uli m920x DVB-T USB2.0 support"
depends on DVB_USB
select DVB_MT352 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
Currently, only devices with a product id of
@@ -137,7 +137,7 @@ config DVB_USB_GL861
tristate "Genesys Logic GL861 USB2.0 support"
depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
receiver with USB ID 0db0:5581.
@@ -146,7 +146,7 @@ config DVB_USB_AU6610
tristate "Alcor Micro AU6610 USB2.0 support"
depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
@@ -198,8 +198,8 @@ config DVB_USB_NOVA_T_USB2
tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
depends on DVB_USB
select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
@@ -235,8 +235,8 @@ config DVB_USB_OPERA1
config DVB_USB_AF9005
tristate "Afatech AF9005 DVB-T USB1.1 support"
depends on DVB_USB && EXPERIMENTAL
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
and the TerraTec Cinergy T USB XE (Rev.1)
@@ -284,7 +284,7 @@ config DVB_USB_DTV5100
tristate "AME DTV-5100 USB2.0 DVB-T support"
depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
@@ -293,9 +293,9 @@ config DVB_USB_AF9015
depends on DVB_USB && EXPERIMENTAL
select DVB_AF9013
select DVB_PLL if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
help
Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index e9ab0249d133..e1e9aa5c6b84 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -733,9 +733,19 @@ static int af9015_read_config(struct usb_device *udev)
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_mygictv);
break;
+ case AF9015_REMOTE_DIGITTRADE_DVB_T:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_digittrade;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_digittrade);
+ af9015_config.ir_table =
+ af9015_ir_table_digittrade;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_digittrade);
+ break;
}
} else {
- switch (udev->descriptor.idVendor) {
+ switch (le16_to_cpu(udev->descriptor.idVendor)) {
case USB_VID_LEADTEK:
af9015_properties[i].rc_key_map =
af9015_rc_keys_leadtek;
@@ -748,7 +758,7 @@ static int af9015_read_config(struct usb_device *udev)
break;
case USB_VID_VISIONPLUS:
if (udev->descriptor.idProduct ==
- USB_PID_AZUREWAVE_AD_TU700) {
+ cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
af9015_properties[i].rc_key_map =
af9015_rc_keys_twinhan;
af9015_properties[i].rc_key_map_size =
@@ -800,6 +810,16 @@ static int af9015_read_config(struct usb_device *udev)
ARRAY_SIZE(af9015_ir_table_msi);
}
break;
+ case USB_VID_AVERMEDIA:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_avermedia;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_avermedia);
+ af9015_config.ir_table =
+ af9015_ir_table_avermedia;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_avermedia);
+ break;
}
}
}
@@ -1191,6 +1211,7 @@ static struct usb_device_id af9015_usb_table[] = {
{USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
/* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1343,7 +1364,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 6,
+ .num_device_descs = 7,
.devices = {
{
.name = "Xtensions XD-380",
@@ -1375,6 +1396,12 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {&af9015_usb_table[15], NULL},
.warm_ids = {NULL},
},
+ {
+ .name = "KWorld USB DVB-T TV Stick II " \
+ "(VS-DVB-T 395U)",
+ .cold_ids = {&af9015_usb_table[16], NULL},
+ .warm_ids = {NULL},
+ },
}
}
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index 6c3c97293316..21c7782f4889 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -123,6 +123,7 @@ enum af9015_remote {
AF9015_REMOTE_A_LINK_DTU_M,
AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
AF9015_REMOTE_MYGICTV_U718,
+ AF9015_REMOTE_DIGITTRADE_DVB_T,
};
/* Leadtek WinFast DTV Dongle Gold */
@@ -520,4 +521,143 @@ static u8 af9015_ir_table_kworld[] = {
0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
};
+/* AverMedia Volar X */
+static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = {
+ { 0x05, 0x3d, KEY_PROG1 }, /* SOURCE */
+ { 0x05, 0x12, KEY_POWER }, /* POWER */
+ { 0x05, 0x1e, KEY_1 }, /* 1 */
+ { 0x05, 0x1f, KEY_2 }, /* 2 */
+ { 0x05, 0x20, KEY_3 }, /* 3 */
+ { 0x05, 0x21, KEY_4 }, /* 4 */
+ { 0x05, 0x22, KEY_5 }, /* 5 */
+ { 0x05, 0x23, KEY_6 }, /* 6 */
+ { 0x05, 0x24, KEY_7 }, /* 7 */
+ { 0x05, 0x25, KEY_8 }, /* 8 */
+ { 0x05, 0x26, KEY_9 }, /* 9 */
+ { 0x05, 0x3f, KEY_LEFT }, /* L / DISPLAY */
+ { 0x05, 0x27, KEY_0 }, /* 0 */
+ { 0x05, 0x0f, KEY_RIGHT }, /* R / CH RTN */
+ { 0x05, 0x18, KEY_PROG2 }, /* SNAP SHOT */
+ { 0x05, 0x1c, KEY_PROG3 }, /* 16-CH PREV */
+ { 0x05, 0x2d, KEY_VOLUMEDOWN }, /* VOL DOWN */
+ { 0x05, 0x3e, KEY_ZOOM }, /* FULL SCREEN */
+ { 0x05, 0x2e, KEY_VOLUMEUP }, /* VOL UP */
+ { 0x05, 0x10, KEY_MUTE }, /* MUTE */
+ { 0x05, 0x04, KEY_AUDIO }, /* AUDIO */
+ { 0x05, 0x15, KEY_RECORD }, /* RECORD */
+ { 0x05, 0x11, KEY_PLAY }, /* PLAY */
+ { 0x05, 0x16, KEY_STOP }, /* STOP */
+ { 0x05, 0x0c, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */
+ { 0x05, 0x05, KEY_BACK }, /* << / RED */
+ { 0x05, 0x09, KEY_FORWARD }, /* >> / YELLOW */
+ { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */
+ { 0x05, 0x0a, KEY_EPG }, /* EPG */
+ { 0x05, 0x13, KEY_MENU }, /* MENU */
+
+ { 0x05, 0x0e, KEY_CHANNELUP }, /* CH UP */
+ { 0x05, 0x0d, KEY_CHANNELDOWN }, /* CH DOWN */
+ { 0x05, 0x19, KEY_FIRST }, /* |<< / GREEN */
+ { 0x05, 0x08, KEY_LAST }, /* >>| / BLUE */
+};
+
+static u8 af9015_ir_table_avermedia[] = {
+ 0x02, 0xfd, 0x00, 0xff, 0x12, 0x05, 0x00,
+ 0x02, 0xfd, 0x01, 0xfe, 0x3d, 0x05, 0x00,
+ 0x02, 0xfd, 0x03, 0xfc, 0x17, 0x05, 0x00,
+ 0x02, 0xfd, 0x04, 0xfb, 0x0a, 0x05, 0x00,
+ 0x02, 0xfd, 0x05, 0xfa, 0x1e, 0x05, 0x00,
+ 0x02, 0xfd, 0x06, 0xf9, 0x1f, 0x05, 0x00,
+ 0x02, 0xfd, 0x07, 0xf8, 0x20, 0x05, 0x00,
+ 0x02, 0xfd, 0x09, 0xf6, 0x21, 0x05, 0x00,
+ 0x02, 0xfd, 0x0a, 0xf5, 0x22, 0x05, 0x00,
+ 0x02, 0xfd, 0x0b, 0xf4, 0x23, 0x05, 0x00,
+ 0x02, 0xfd, 0x0d, 0xf2, 0x24, 0x05, 0x00,
+ 0x02, 0xfd, 0x0e, 0xf1, 0x25, 0x05, 0x00,
+ 0x02, 0xfd, 0x0f, 0xf0, 0x26, 0x05, 0x00,
+ 0x02, 0xfd, 0x11, 0xee, 0x27, 0x05, 0x00,
+ 0x02, 0xfd, 0x08, 0xf7, 0x04, 0x05, 0x00,
+ 0x02, 0xfd, 0x0c, 0xf3, 0x3e, 0x05, 0x00,
+ 0x02, 0xfd, 0x10, 0xef, 0x1c, 0x05, 0x00,
+ 0x02, 0xfd, 0x12, 0xed, 0x3f, 0x05, 0x00,
+ 0x02, 0xfd, 0x13, 0xec, 0x0f, 0x05, 0x00,
+ 0x02, 0xfd, 0x14, 0xeb, 0x10, 0x05, 0x00,
+ 0x02, 0xfd, 0x15, 0xea, 0x13, 0x05, 0x00,
+ 0x02, 0xfd, 0x17, 0xe8, 0x18, 0x05, 0x00,
+ 0x02, 0xfd, 0x18, 0xe7, 0x11, 0x05, 0x00,
+ 0x02, 0xfd, 0x19, 0xe6, 0x15, 0x05, 0x00,
+ 0x02, 0xfd, 0x1a, 0xe5, 0x0c, 0x05, 0x00,
+ 0x02, 0xfd, 0x1b, 0xe4, 0x16, 0x05, 0x00,
+ 0x02, 0xfd, 0x1c, 0xe3, 0x09, 0x05, 0x00,
+ 0x02, 0xfd, 0x1d, 0xe2, 0x05, 0x05, 0x00,
+ 0x02, 0xfd, 0x1e, 0xe1, 0x2d, 0x05, 0x00,
+ 0x02, 0xfd, 0x1f, 0xe0, 0x2e, 0x05, 0x00,
+ 0x03, 0xfc, 0x00, 0xff, 0x08, 0x05, 0x00,
+ 0x03, 0xfc, 0x01, 0xfe, 0x19, 0x05, 0x00,
+ 0x03, 0xfc, 0x02, 0xfd, 0x0d, 0x05, 0x00,
+ 0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00,
+};
+
+/* Digittrade DVB-T USB Stick */
+static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = {
+ { 0x01, 0x0f, KEY_LAST }, /* RETURN */
+ { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */
+ { 0x01, 0x08, KEY_EPG }, /* EPG */
+ { 0x05, 0x13, KEY_POWER }, /* POWER */
+ { 0x01, 0x09, KEY_ZOOM }, /* FULLSCREEN */
+ { 0x00, 0x40, KEY_AUDIO }, /* DUAL SOUND */
+ { 0x00, 0x2c, KEY_PRINT }, /* SNAPSHOT */
+ { 0x05, 0x16, KEY_SUBTITLE }, /* SUBTITLE */
+ { 0x00, 0x52, KEY_CHANNELUP }, /* CH Up */
+ { 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */
+ { 0x00, 0x57, KEY_VOLUMEUP }, /* Vol Up */
+ { 0x00, 0x56, KEY_VOLUMEDOWN }, /* Vol Dn */
+ { 0x01, 0x10, KEY_MUTE }, /* MUTE */
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x01, 0x17, KEY_PLAYPAUSE }, /* TIMESHIFT */
+ { 0x01, 0x15, KEY_RECORD }, /* RECORD */
+ { 0x03, 0x13, KEY_PLAY }, /* PLAY */
+ { 0x01, 0x16, KEY_STOP }, /* STOP */
+ { 0x01, 0x13, KEY_PAUSE }, /* PAUSE */
+};
+
+static u8 af9015_ir_table_digittrade[] = {
+ 0x00, 0xff, 0x06, 0xf9, 0x13, 0x05, 0x00,
+ 0x00, 0xff, 0x4d, 0xb2, 0x17, 0x01, 0x00,
+ 0x00, 0xff, 0x1f, 0xe0, 0x2c, 0x00, 0x00,
+ 0x00, 0xff, 0x0a, 0xf5, 0x15, 0x01, 0x00,
+ 0x00, 0xff, 0x0e, 0xf1, 0x16, 0x01, 0x00,
+ 0x00, 0xff, 0x09, 0xf6, 0x09, 0x01, 0x00,
+ 0x00, 0xff, 0x01, 0xfe, 0x08, 0x01, 0x00,
+ 0x00, 0xff, 0x05, 0xfa, 0x10, 0x01, 0x00,
+ 0x00, 0xff, 0x02, 0xfd, 0x56, 0x00, 0x00,
+ 0x00, 0xff, 0x40, 0xbf, 0x57, 0x00, 0x00,
+ 0x00, 0xff, 0x19, 0xe6, 0x52, 0x00, 0x00,
+ 0x00, 0xff, 0x17, 0xe8, 0x51, 0x00, 0x00,
+ 0x00, 0xff, 0x10, 0xef, 0x0f, 0x01, 0x00,
+ 0x00, 0xff, 0x54, 0xab, 0x27, 0x00, 0x00,
+ 0x00, 0xff, 0x1b, 0xe4, 0x1e, 0x00, 0x00,
+ 0x00, 0xff, 0x11, 0xee, 0x1f, 0x00, 0x00,
+ 0x00, 0xff, 0x15, 0xea, 0x20, 0x00, 0x00,
+ 0x00, 0xff, 0x12, 0xed, 0x21, 0x00, 0x00,
+ 0x00, 0xff, 0x16, 0xe9, 0x22, 0x00, 0x00,
+ 0x00, 0xff, 0x4c, 0xb3, 0x23, 0x00, 0x00,
+ 0x00, 0xff, 0x48, 0xb7, 0x24, 0x00, 0x00,
+ 0x00, 0xff, 0x04, 0xfb, 0x25, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x26, 0x00, 0x00,
+ 0x00, 0xff, 0x1e, 0xe1, 0x13, 0x03, 0x00,
+ 0x00, 0xff, 0x1a, 0xe5, 0x13, 0x01, 0x00,
+ 0x00, 0xff, 0x03, 0xfc, 0x17, 0x05, 0x00,
+ 0x00, 0xff, 0x0d, 0xf2, 0x16, 0x05, 0x00,
+ 0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00,
+};
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index cd2edbcaa097..5017f08b14a6 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -153,7 +153,7 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret, inc, i = 0;
+ int ret = 0, inc, i = 0;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
index 3ac9f74e9fbf..80e37a0d0892 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
@@ -32,7 +32,6 @@
/* debug */
int dvb_usb_cinergyt2_debug;
-int disable_remote;
module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 "
@@ -45,7 +44,7 @@ struct cinergyt2_state {
};
/* We are missing a release hook with usb_device data */
-struct dvb_usb_device *cinergyt2_usb_device;
+static struct dvb_usb_device *cinergyt2_usb_device;
static struct dvb_usb_device_properties cinergyt2_properties;
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h
index 11d79eb384c8..84efe03771eb 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2.h
+++ b/drivers/media/dvb/dvb-usb/cinergyT2.h
@@ -70,11 +70,11 @@ struct dvbt_get_status_msg {
uint8_t bandwidth;
uint16_t tps;
uint8_t flags;
- uint16_t gain;
+ __le16 gain;
uint8_t snr;
- uint32_t viterbi_error_rate;
+ __le32 viterbi_error_rate;
uint32_t rs_error_rate;
- uint32_t uncorrected_block_count;
+ __le32 uncorrected_block_count;
uint8_t lock_bits;
uint8_t prev_lock_bits;
} __attribute__((packed));
@@ -82,9 +82,9 @@ struct dvbt_get_status_msg {
struct dvbt_set_parameters_msg {
uint8_t cmd;
- uint32_t freq;
+ __le32 freq;
uint8_t bandwidth;
- uint16_t tps;
+ __le16 tps;
uint8_t flags;
} __attribute__((packed));
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index f28d3ae59e04..391732788911 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -446,13 +446,13 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap)
== NULL ? -ENODEV : 0;
}
-#define DEFAULT_RC_INTERVAL 150
+#define DEFAULT_RC_INTERVAL 50
static u8 rc_request[] = { REQUEST_POLL_RC, 0 };
/* Number of keypresses to ignore before start repeating */
-#define RC_REPEAT_DELAY 2
-#define RC_REPEAT_DELAY_V1_20 5
+#define RC_REPEAT_DELAY 6
+#define RC_REPEAT_DELAY_V1_20 10
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
index ce8cd0c5d831..8a7d87bcd1d9 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
@@ -91,10 +91,7 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
if (adap->dev->props.read_mac_address) {
if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
- info("MAC address: %02x:%02x:%02x:%02x:%02x:%02x",adap->dvb_adap.proposed_mac[0],
- adap->dvb_adap.proposed_mac[1], adap->dvb_adap.proposed_mac[2],
- adap->dvb_adap.proposed_mac[3], adap->dvb_adap.proposed_mac[4],
- adap->dvb_adap.proposed_mac[5]);
+ info("MAC address: %pM",adap->dvb_adap.proposed_mac);
else
err("MAC address reading failed.");
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 7380b94b3b36..a4fca3fca5ee 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -96,6 +96,7 @@
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_395U 0xe396
#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 6286fbbe7fb5..c65f273ff313 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -9,7 +9,6 @@
*
* see Documentation/dvb/README.dvb-usb for more information
*/
-#include <linux/version.h>
#include "dw2102.h"
#include "si21xx.h"
#include "stv0299.h"
@@ -27,6 +26,10 @@
#define USB_PID_DW2104 0x2104
#endif
+#ifndef USB_PID_CINERGY_S
+#define USB_PID_CINERGY_S 0x0064
+#endif
+
#define DW210X_READ_MSG 0
#define DW210X_WRITE_MSG 1
@@ -578,6 +581,7 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(USB_VID_CYPRESS, 0x2101)},
{USB_DEVICE(USB_VID_CYPRESS, 0x2104)},
{USB_DEVICE(0x9022, 0xd650)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)},
{ }
};
@@ -647,6 +651,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
DW210X_WRITE_MSG);
break;
+ case USB_PID_CINERGY_S:
case USB_PID_DW2102:
dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
DW210X_WRITE_MSG);
@@ -655,7 +660,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
/* check STV0299 frontend */
dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
DW210X_READ_MSG);
- if (reset16[0] == 0xa1) {
+ if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) {
dw2102_properties.i2c_algo = &dw2102_i2c_algo;
dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
break;
@@ -726,7 +731,7 @@ static struct dvb_usb_device_properties dw2102_properties = {
},
}
},
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{"DVBWorld DVB-S 2102 USB2.0",
{&dw2102_table[0], NULL},
@@ -736,6 +741,10 @@ static struct dvb_usb_device_properties dw2102_properties = {
{&dw2102_table[1], NULL},
{NULL},
},
+ {"TerraTec Cinergy S USB",
+ {&dw2102_table[4], NULL},
+ {NULL},
+ },
}
};
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
index 262a858c3068..20eadf9318e0 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c
@@ -25,6 +25,20 @@ struct gp8psk_fe_state {
unsigned long status_check_interval;
};
+static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe)
+{
+ struct gp8psk_fe_state *st = fe->demodulator_priv;
+ u8 status;
+ gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1);
+ return status & bmDCtuned;
+}
+
+static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode)
+{
+ struct gp8psk_fe_state *state = fe->demodulator_priv;
+ return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0);
+}
+
static int gp8psk_fe_update_status(struct gp8psk_fe_state *st)
{
u8 buf[6];
@@ -99,39 +113,114 @@ static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_front
return 0;
}
+static int gp8psk_fe_set_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ deb_fe("%s(..)\n", __func__);
+ return 0;
+}
+
+static int gp8psk_fe_get_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ deb_fe("%s(..)\n", __func__);
+ return 0;
+}
+
+
static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep)
{
struct gp8psk_fe_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u8 cmd[10];
u32 freq = fep->frequency * 1000;
+ int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct);
+
+ deb_fe("%s()\n", __func__);
cmd[4] = freq & 0xff;
cmd[5] = (freq >> 8) & 0xff;
cmd[6] = (freq >> 16) & 0xff;
cmd[7] = (freq >> 24) & 0xff;
- switch(fe->ops.info.type) {
- case FE_QPSK:
- cmd[0] = fep->u.qpsk.symbol_rate & 0xff;
- cmd[1] = (fep->u.qpsk.symbol_rate >> 8) & 0xff;
- cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff;
- cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff;
- cmd[8] = ADV_MOD_DVB_QPSK;
- cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* Only QPSK is supported for DVB-S */
+ if (c->modulation != QPSK) {
+ deb_fe("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+ c->fec_inner = FEC_AUTO;
break;
+ case SYS_DVBS2:
+ deb_fe("%s: DVB-S2 delivery system selected\n", __func__);
+ break;
+
default:
- // other modes are unsuported right now
- cmd[0] = 0;
- cmd[1] = 0;
- cmd[2] = 0;
- cmd[3] = 0;
- cmd[8] = 0;
+ deb_fe("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ cmd[0] = c->symbol_rate & 0xff;
+ cmd[1] = (c->symbol_rate >> 8) & 0xff;
+ cmd[2] = (c->symbol_rate >> 16) & 0xff;
+ cmd[3] = (c->symbol_rate >> 24) & 0xff;
+ switch (c->modulation) {
+ case QPSK:
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ if (gp8psk_tuned_to_DCII(fe))
+ gp8psk_bcm4500_reload(state->d);
+ switch (c->fec_inner) {
+ case FEC_1_2:
+ cmd[9] = 0; break;
+ case FEC_2_3:
+ cmd[9] = 1; break;
+ case FEC_3_4:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_7_8:
+ cmd[9] = 4; break;
+ case FEC_AUTO:
+ cmd[9] = 5; break;
+ default:
+ cmd[9] = 5; break;
+ }
+ cmd[8] = ADV_MOD_DVB_QPSK;
+ break;
+ case PSK_8: /* PSK_8 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_8PSK;
+ switch (c->fec_inner) {
+ case FEC_2_3:
+ cmd[9] = 0; break;
+ case FEC_3_4:
+ cmd[9] = 1; break;
+ case FEC_3_5:
+ cmd[9] = 2; break;
+ case FEC_5_6:
+ cmd[9] = 3; break;
+ case FEC_8_9:
+ cmd[9] = 4; break;
+ default:
+ cmd[9] = 0; break;
+ }
+ break;
+ case QAM_16: /* QAM_16 is for compatibility with DN */
+ cmd[8] = ADV_MOD_TURBO_16QAM;
cmd[9] = 0;
break;
+ default: /* Unknown modulation */
+ deb_fe("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
}
- gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10);
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ gp8psk_set_tuner_mode(fe, 0);
+ gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10);
state->lock = 0;
state->next_status_check = jiffies;
@@ -140,13 +229,6 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe,
return 0;
}
-static int gp8psk_fe_get_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
-{
- return 0;
-}
-
-
static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
struct dvb_diseqc_master_cmd *m)
{
@@ -261,9 +343,13 @@ static struct dvb_frontend_ops gp8psk_fe_ops = {
.symbol_rate_max = 45000000,
.symbol_rate_tolerance = 500, /* ppm */
.caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ /*
+ * FE_CAN_QAM_16 is for compatibility
+ * (Myth incorrectly detects Turbo-QPSK as plain QAM-16)
+ */
+ FE_CAN_QPSK | FE_CAN_QAM_16
},
.release = gp8psk_fe_release,
@@ -271,8 +357,10 @@ static struct dvb_frontend_ops gp8psk_fe_ops = {
.init = NULL,
.sleep = NULL,
+ .set_property = gp8psk_fe_set_property,
+ .get_property = gp8psk_fe_get_property,
.set_frontend = gp8psk_fe_set_frontend,
- .get_frontend = gp8psk_fe_get_frontend,
+
.get_tune_settings = gp8psk_fe_get_tune_settings,
.read_status = gp8psk_fe_read_status,
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index d965a923f391..c1da962cc886 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -174,6 +174,22 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
+int gp8psk_bcm4500_reload(struct dvb_usb_device *d)
+{
+ u8 buf;
+ int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct);
+ /* Turn off 8psk power */
+ if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1))
+ return -EINVAL;
+ /* Turn On 8psk power */
+ if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1))
+ return -EINVAL;
+ /* load BCM4500 firmware */
+ if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM)
+ if (gp8psk_load_bcm4500fw(d))
+ return EINVAL;
+ return 0;
+}
static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h
index e5cd8149c23d..e83a57506cfa 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/dvb/dvb-usb/gp8psk.h
@@ -92,5 +92,6 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d);
extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
u16 index, u8 *b, int blen);
+extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d);
#endif
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c
index da93b9e982c0..9da2cc95ca13 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -156,7 +156,8 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
- stream->urb_list[i]->transfer_flags = 0;
+ stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
stream->urbs_initialized++;
}
return 0;
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 96b93e21a84b..00269560793a 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -12,6 +12,25 @@ config DVB_FE_CUSTOMISE
If unsure say N.
+comment "Multistandard (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_STB0899
+ tristate "STB0899 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want
+ to support this demodulator based frontends
+
+config DVB_STB6100
+ tristate "STB6100 based tuners"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A Silicon tuner from ST used in conjunction with the STB0899
+ demodulator. Say Y when you want to support this tuner.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
@@ -78,6 +97,13 @@ config DVB_TDA10086
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_TDA8261
+ tristate "Philips TDA8261 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
config DVB_VES1X93
tristate "VLSI VES1893 or VES1993 based"
depends on DVB_CORE && I2C
@@ -92,6 +118,14 @@ config DVB_TUNER_ITD1000
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_TUNER_CX24113
+ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+
config DVB_TDA826X
tristate "Philips TDA826X silicon tuner"
depends on DVB_CORE && I2C
@@ -345,6 +379,14 @@ config DVB_LGDT330X
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
+config DVB_LGDT3304
+ tristate "LG Electronics LGDT3304"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
config DVB_S5H1409
tristate "Samsung S5H1409 based"
depends on DVB_CORE && I2C
@@ -369,6 +411,17 @@ config DVB_S5H1411
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
+comment "ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_S921
+ tristate "Sharp S921 tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
+ Say Y when you want to support this frontend.
+
comment "Digital terrestrial only tuners/PLL"
depends on DVB_CORE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index aba79f4a63a7..af7bdf0ad4c7 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -5,8 +5,13 @@
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+s921-objs := s921_module.o s921_core.o
+stb0899-objs = stb0899_drv.o stb0899_algo.o
+
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_STB0899) += stb0899.o
+obj-$(CONFIG_DVB_STB6100) += stb6100.o
obj-$(CONFIG_DVB_SP8870) += sp8870.o
obj-$(CONFIG_DVB_CX22700) += cx22700.o
obj-$(CONFIG_DVB_CX24110) += cx24110.o
@@ -35,18 +40,21 @@ obj-$(CONFIG_DVB_OR51132) += or51132.o
obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
+obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
obj-$(CONFIG_DVB_ISL6405) += isl6405.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TDA8261) += tda8261.o
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
obj-$(CONFIG_DVB_AU8522) += au8522.o
obj-$(CONFIG_DVB_TDA10048) += tda10048.o
+obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
@@ -55,3 +63,5 @@ obj-$(CONFIG_DVB_CX24116) += cx24116.o
obj-$(CONFIG_DVB_SI21XX) += si21xx.o
obj-$(CONFIG_DVB_STV0288) += stv0288.o
obj-$(CONFIG_DVB_STB6000) += stb6000.o
+obj-$(CONFIG_DVB_S921) += s921.o
+
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
index 692b68a9e73b..b2b50fb4cfd3 100644
--- a/drivers/media/dvb/frontends/af9013.c
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -223,12 +223,12 @@ static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
int ret = 0;
u8 i = 0;
u8 buf[24];
- u32 ns_coeff1_2048nu;
- u32 ns_coeff1_8191nu;
- u32 ns_coeff1_8192nu;
- u32 ns_coeff1_8193nu;
- u32 ns_coeff2_2k;
- u32 ns_coeff2_8k;
+ u32 uninitialized_var(ns_coeff1_2048nu);
+ u32 uninitialized_var(ns_coeff1_8191nu);
+ u32 uninitialized_var(ns_coeff1_8192nu);
+ u32 uninitialized_var(ns_coeff1_8193nu);
+ u32 uninitialized_var(ns_coeff2_2k);
+ u32 uninitialized_var(ns_coeff2_8k);
deb_info("%s: adc_clock:%d bw:%d\n", __func__,
state->config.adc_clock, bw);
@@ -1009,7 +1009,7 @@ static int af9013_update_snr(struct dvb_frontend *fe)
int ret;
u8 buf[3], i, len;
u32 quant = 0;
- struct snr_table *snr_table;
+ struct snr_table *uninitialized_var(snr_table);
/* check if quantizer ready (for snr) */
ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
new file mode 100644
index 000000000000..f6e7b0380a5a
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24113.c
@@ -0,0 +1,616 @@
+/*
+ * Driver for Conexant CX24113/CX24128 Tuner (Satellite)
+ *
+ * Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
+ *
+ * Developed for BBTI / Technisat
+ *
+ * This program is free software; you can redistribute 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "cx24113.h"
+
+static int debug;
+
+#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
+#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0)
+
+#define dprintk(args...) \
+ do { \
+ if (debug) { \
+ printk(KERN_DEBUG "CX24113: %s: ", __func__); \
+ printk(args); \
+ } \
+ } while (0)
+
+struct cx24113_state {
+ struct i2c_adapter *i2c;
+ const struct cx24113_config *config;
+
+#define REV_CX24113 0x23
+ u8 rev;
+ u8 ver;
+
+ u8 icp_mode:1;
+
+#define ICP_LEVEL1 0
+#define ICP_LEVEL2 1
+#define ICP_LEVEL3 2
+#define ICP_LEVEL4 3
+ u8 icp_man:2;
+ u8 icp_auto_low:2;
+ u8 icp_auto_mlow:2;
+ u8 icp_auto_mhi:2;
+ u8 icp_auto_hi:2;
+ u8 icp_dig;
+
+#define LNA_MIN_GAIN 0
+#define LNA_MID_GAIN 1
+#define LNA_MAX_GAIN 2
+ u8 lna_gain:2;
+
+ u8 acp_on:1;
+
+ u8 vco_mode:2;
+ u8 vco_shift:1;
+#define VCOBANDSEL_6 0x80
+#define VCOBANDSEL_5 0x01
+#define VCOBANDSEL_4 0x02
+#define VCOBANDSEL_3 0x04
+#define VCOBANDSEL_2 0x08
+#define VCOBANDSEL_1 0x10
+ u8 vco_band;
+
+#define VCODIV4 4
+#define VCODIV2 2
+ u8 vcodiv;
+
+ u8 bs_delay:4;
+ u16 bs_freqcnt:13;
+ u16 bs_rdiv;
+ u8 prescaler_mode:1;
+
+ u8 rfvga_bias_ctrl;
+
+ s16 tuner_gain_thres;
+ u8 gain_level;
+
+ u32 frequency;
+
+ u8 refdiv;
+
+ u8 Fwindow_enabled;
+};
+
+static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->i2c_addr,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x,"
+ " data == 0x%02x)\n", __func__, err, reg, data);
+ return err;
+ }
+
+ return 0;
+}
+
+static int cx24113_readreg(struct cx24113_state *state, u8 reg)
+{
+ int ret;
+ u8 b;
+ struct i2c_msg msg[] = {
+ { .addr = state->config->i2c_addr,
+ .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = state->config->i2c_addr,
+ .flags = I2C_M_RD, .buf = &b, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+
+ return b;
+}
+
+static void cx24113_set_parameters(struct cx24113_state *state)
+{
+ u8 r;
+
+ r = cx24113_readreg(state, 0x10) & 0x82;
+ r |= state->icp_mode;
+ r |= state->icp_man << 4;
+ r |= state->icp_dig << 2;
+ r |= state->prescaler_mode << 5;
+ cx24113_writereg(state, 0x10, r);
+
+ r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2)
+ | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
+ cx24113_writereg(state, 0x11, r);
+
+ if (state->rev == REV_CX24113) {
+ r = cx24113_readreg(state, 0x20) & 0xec;
+ r |= state->lna_gain;
+ r |= state->rfvga_bias_ctrl << 4;
+ cx24113_writereg(state, 0x20, r);
+ }
+
+ r = cx24113_readreg(state, 0x12) & 0x03;
+ r |= state->acp_on << 2;
+ r |= state->bs_delay << 4;
+ cx24113_writereg(state, 0x12, r);
+
+ r = cx24113_readreg(state, 0x18) & 0x40;
+ r |= state->vco_shift;
+ if (state->vco_band == VCOBANDSEL_6)
+ r |= (1 << 7);
+ else
+ r |= (state->vco_band << 1);
+ cx24113_writereg(state, 0x18, r);
+
+ r = cx24113_readreg(state, 0x14) & 0x20;
+ r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
+ cx24113_writereg(state, 0x14, r);
+ cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff));
+
+ cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
+ r = (cx24113_readreg(state, 0x17) & 0x0f) |
+ ((state->bs_rdiv & 0x0f) << 4);
+ cx24113_writereg(state, 0x17, r);
+}
+
+#define VGA_0 0x00
+#define VGA_1 0x04
+#define VGA_2 0x02
+#define VGA_3 0x06
+#define VGA_4 0x01
+#define VGA_5 0x05
+#define VGA_6 0x03
+#define VGA_7 0x07
+
+#define RFVGA_0 0x00
+#define RFVGA_1 0x01
+#define RFVGA_2 0x02
+#define RFVGA_3 0x03
+
+static int cx24113_set_gain_settings(struct cx24113_state *state,
+ s16 power_estimation)
+{
+ u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
+ vga = cx24113_readreg(state, 0x1f) & 0x3f,
+ rfvga = cx24113_readreg(state, 0x20) & 0xf3;
+ u8 gain_level = power_estimation >= state->tuner_gain_thres;
+
+ dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
+ power_estimation, state->tuner_gain_thres,
+ state->gain_level, gain_level);
+
+ if (gain_level == state->gain_level)
+ return 0; /* nothing to be done */
+
+ ampout |= 0xf;
+
+ if (gain_level) {
+ rfvga |= RFVGA_0 << 2;
+ vga |= (VGA_7 << 3) | VGA_7;
+ } else {
+ rfvga |= RFVGA_2 << 2;
+ vga |= (VGA_6 << 3) | VGA_2;
+ }
+ state->gain_level = gain_level;
+
+ cx24113_writereg(state, 0x1d, ampout);
+ cx24113_writereg(state, 0x1f, vga);
+ cx24113_writereg(state, 0x20, rfvga);
+
+ return 1; /* did something */
+}
+
+static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
+{
+ u8 xtal = cx24113_readreg(state, 0x02);
+ if (state->rev == 0x43 && state->vcodiv == VCODIV4)
+ high = 1;
+
+ xtal &= ~0x2;
+ if (high)
+ xtal |= high << 1;
+ return cx24113_writereg(state, 0x02, xtal);
+}
+
+static int cx24113_enable(struct cx24113_state *state, u8 enable)
+{
+ u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
+ if (state->rev == REV_CX24113)
+ r21 |= (1 << 1);
+ return cx24113_writereg(state, 0x21, r21);
+}
+
+static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
+{
+ u8 r;
+
+ if (bandwidth_khz <= 19000)
+ r = 0x03 << 6;
+ else if (bandwidth_khz <= 25000)
+ r = 0x02 << 6;
+ else
+ r = 0x01 << 6;
+
+ dprintk("bandwidth to be set: %d\n", bandwidth_khz);
+ bandwidth_khz *= 10;
+ bandwidth_khz -= 10000;
+ bandwidth_khz /= 1000;
+ bandwidth_khz += 5;
+ bandwidth_khz /= 10;
+
+ dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
+
+ r |= bandwidth_khz & 0x3f;
+
+ return cx24113_writereg(state, 0x1e, r);
+}
+
+static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
+{
+ u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
+ return cx24113_writereg(state, 0x10, r);
+}
+
+static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
+ if (r)
+ *status |= TUNER_STATUS_LOCKED;
+ dprintk("PLL locked: %d\n", r);
+ return 0;
+}
+
+static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
+{
+ if (state->rev == 0x43 && state->vcodiv == VCODIV4)
+ refdiv = 2;
+ return state->refdiv = refdiv;
+}
+
+static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
+{
+ s32 N;
+ s64 F;
+ u8 R, r;
+ u8 vcodiv;
+ u8 factor;
+ s32 freq_hz = state->frequency * 1000;
+
+ if (state->config->xtal_khz < 20000)
+ factor = 1;
+ else
+ factor = 2;
+
+ if (state->rev == REV_CX24113) {
+ if (state->frequency >= 1100000)
+ vcodiv = VCODIV2;
+ else
+ vcodiv = VCODIV4;
+ } else {
+ if (state->frequency >= 1165000)
+ vcodiv = VCODIV2;
+ else
+ vcodiv = VCODIV4;
+ }
+ state->vcodiv = vcodiv;
+
+ dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
+ R = 0;
+ do {
+ R = cx24113_set_ref_div(state, R + 1);
+
+ /* calculate tuner PLL settings: */
+ N = (freq_hz / 100 * vcodiv) * R;
+ N /= (state->config->xtal_khz) * factor * 2;
+ N += 5; /* For round up. */
+ N /= 10;
+ N -= 32;
+ } while (N < 6 && R < 3);
+
+ if (N < 6) {
+ err("strange frequency: N < 6\n");
+ return;
+ }
+ F = freq_hz;
+ F *= (u64) (R * vcodiv * 262144);
+ dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+ do_div(F, state->config->xtal_khz*1000 * factor * 2);
+ dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+ F -= (N + 32) * 262144;
+
+ dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+
+ if (state->Fwindow_enabled) {
+ if (F > (262144 / 2 - 1638))
+ F = 262144 / 2 - 1638;
+ if (F < (-262144 / 2 + 1638))
+ F = -262144 / 2 + 1638;
+ if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
+ F = 0;
+ r = cx24113_readreg(state, 0x10);
+ cx24113_writereg(state, 0x10, r | (1 << 6));
+ }
+ }
+ dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
+
+ *n = (u16) N;
+ *f = (s32) F;
+}
+
+
+static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
+{
+ u8 reg;
+ cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
+
+ reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
+ cx24113_writereg(state, 0x1a, reg);
+
+ cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
+
+ reg = cx24113_readreg(state, 0x1c) & 0x1f;
+ cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
+
+ cx24113_set_Fref(state, r - 1);
+}
+
+static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
+{
+ u8 r = 1; /* or 2 */
+ u16 n = 6;
+ s32 f = 0;
+
+ r = cx24113_readreg(state, 0x14);
+ cx24113_writereg(state, 0x14, r & 0x3f);
+
+ r = cx24113_readreg(state, 0x10);
+ cx24113_writereg(state, 0x10, r & 0xbf);
+
+ state->frequency = frequency;
+
+ dprintk("tuning to frequency: %d\n", frequency);
+
+ cx24113_calc_pll_nf(state, &n, &f);
+ cx24113_set_nfr(state, n, f, state->refdiv);
+
+ r = cx24113_readreg(state, 0x18) & 0xbf;
+ if (state->vcodiv != VCODIV2)
+ r |= 1 << 6;
+ cx24113_writereg(state, 0x18, r);
+
+ /* The need for this sleep is not clear. But helps in some cases */
+ msleep(5);
+
+ r = cx24113_readreg(state, 0x1c) & 0xef;
+ cx24113_writereg(state, 0x1c, r | (1 << 4));
+ return 0;
+}
+
+static int cx24113_init(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ int ret;
+
+ state->tuner_gain_thres = -50;
+ state->gain_level = 255; /* to force a gain-setting initialization */
+ state->icp_mode = 0;
+
+ if (state->config->xtal_khz < 11000) {
+ state->icp_auto_hi = ICP_LEVEL4;
+ state->icp_auto_mhi = ICP_LEVEL4;
+ state->icp_auto_mlow = ICP_LEVEL3;
+ state->icp_auto_low = ICP_LEVEL3;
+ } else {
+ state->icp_auto_hi = ICP_LEVEL4;
+ state->icp_auto_mhi = ICP_LEVEL4;
+ state->icp_auto_mlow = ICP_LEVEL3;
+ state->icp_auto_low = ICP_LEVEL2;
+ }
+
+ state->icp_dig = ICP_LEVEL3;
+ state->icp_man = ICP_LEVEL1;
+ state->acp_on = 1;
+ state->vco_mode = 0;
+ state->vco_shift = 0;
+ state->vco_band = VCOBANDSEL_1;
+ state->bs_delay = 8;
+ state->bs_freqcnt = 0x0fff;
+ state->bs_rdiv = 0x0fff;
+ state->prescaler_mode = 0;
+ state->lna_gain = LNA_MAX_GAIN;
+ state->rfvga_bias_ctrl = 1;
+ state->Fwindow_enabled = 1;
+
+ cx24113_set_Fref(state, 0);
+ cx24113_enable(state, 0x3d);
+ cx24113_set_parameters(state);
+
+ cx24113_set_gain_settings(state, -30);
+
+ cx24113_set_bandwidth(state, 18025);
+ cx24113_set_clk_inversion(state, 1);
+
+ if (state->config->xtal_khz >= 40000)
+ ret = cx24113_writereg(state, 0x02,
+ (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
+ else
+ ret = cx24113_writereg(state, 0x02,
+ (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
+
+ return ret;
+}
+
+static int cx24113_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
+ u32 roll_off = 675;
+ u32 bw;
+
+ bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000;
+ bw += (10000000/100) + 5;
+ bw /= 10;
+ bw += 1000;
+ cx24113_set_bandwidth(state, bw);
+
+ cx24113_set_frequency(state, p->frequency);
+ msleep(5);
+ return cx24113_get_status(fe, &bw);
+}
+
+static s8 cx24113_agc_table[2][10] = {
+ {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2},
+ {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9},
+};
+
+void cx24113_agc_callback(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ s16 s, i;
+ if (!fe->ops.read_signal_strength)
+ return;
+
+ do {
+ /* this only works with the current CX24123 implementation */
+ fe->ops.read_signal_strength(fe, (u16 *) &s);
+ s >>= 8;
+ dprintk("signal strength: %d\n", s);
+ for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
+ if (cx24113_agc_table[state->gain_level][i] > s)
+ break;
+ s = -25 - i*5;
+ } while (cx24113_set_gain_settings(state, s));
+}
+EXPORT_SYMBOL(cx24113_agc_callback);
+
+static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ *frequency = state->frequency;
+ return 0;
+}
+
+static int cx24113_release(struct dvb_frontend *fe)
+{
+ struct cx24113_state *state = fe->tuner_priv;
+ dprintk("\n");
+ fe->tuner_priv = NULL;
+ kfree(state);
+ return 0;
+}
+
+static const struct dvb_tuner_ops cx24113_tuner_ops = {
+ .info = {
+ .name = "Conexant CX24113",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 125,
+ },
+
+ .release = cx24113_release,
+
+ .init = cx24113_init,
+ .sleep = NULL,
+
+ .set_params = cx24113_set_params,
+ .get_frequency = cx24113_get_frequency,
+ .get_bandwidth = NULL,
+ .get_status = cx24113_get_status,
+};
+
+struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
+ const struct cx24113_config *config, struct i2c_adapter *i2c)
+{
+ /* allocate memory for the internal state */
+ struct cx24113_state *state =
+ kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
+ int rc;
+ if (state == NULL) {
+ err("Unable to kmalloc\n");
+ goto error;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ info("trying to detect myself\n");
+
+ /* making a dummy read, because of some expected troubles
+ * after power on */
+ cx24113_readreg(state, 0x00);
+
+ rc = cx24113_readreg(state, 0x00);
+ if (rc < 0) {
+ info("CX24113 not found.\n");
+ goto error;
+ }
+ state->rev = rc;
+
+ switch (rc) {
+ case 0x43:
+ info("detected CX24113 variant\n");
+ break;
+ case REV_CX24113:
+ info("sucessfully detected\n");
+ break;
+ default:
+ err("unsupported device id: %x\n", state->rev);
+ goto error;
+ }
+ state->ver = cx24113_readreg(state, 0x01);
+ info("version: %x\n", state->ver);
+
+ /* create dvb_frontend */
+ memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = state;
+ return fe;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(cx24113_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
+MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/dvb/frontends/cx24113.h b/drivers/media/dvb/frontends/cx24113.h
index 5ab3dd11076b..5de0f7ffd8d2 100644
--- a/drivers/media/dvb/frontends/cx24113.h
+++ b/drivers/media/dvb/frontends/cx24113.h
@@ -16,7 +16,7 @@
*
* 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.=
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef CX24113_H
@@ -30,9 +30,13 @@ struct cx24113_config {
u32 xtal_khz;
};
-/* TODO: #if defined(CONFIG_DVB_TUNER_CX24113) || \
- * (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) */
+#if defined(CONFIG_DVB_TUNER_CX24113) || \
+ (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE))
+extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *,
+ const struct cx24113_config *config, struct i2c_adapter *i2c);
+extern void cx24113_agc_callback(struct dvb_frontend *fe);
+#else
static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
const struct cx24113_config *config, struct i2c_adapter *i2c)
{
@@ -44,5 +48,6 @@ static inline void cx24113_agc_callback(struct dvb_frontend *fe)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
}
+#endif
#endif /* CX24113_H */
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
index b144b308a4dd..9b6c89e93f16 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -106,7 +106,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
#define CX24116_HAS_SYNCLOCK (0x08)
#define CX24116_HAS_UNKNOWN1 (0x10)
#define CX24116_HAS_UNKNOWN2 (0x20)
-#define CX24116_STATUS_MASK (0x3f)
+#define CX24116_STATUS_MASK (0x0f)
#define CX24116_SIGNAL_MASK (0xc0)
#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
@@ -160,6 +160,7 @@ struct cx24116_tuning {
fe_spectral_inversion_t inversion;
fe_code_rate_t fec;
+ fe_delivery_system_t delsys;
fe_modulation_t modulation;
fe_pilot_t pilot;
fe_rolloff_t rolloff;
@@ -411,14 +412,15 @@ struct cx24116_modfec {
};
static int cx24116_lookup_fecmod(struct cx24116_state *state,
- fe_modulation_t m, fe_code_rate_t f)
+ fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f)
{
int i, ret = -EOPNOTSUPP;
dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) {
- if ((m == CX24116_MODFEC_MODES[i].modulation) &&
+ if ((d == CX24116_MODFEC_MODES[i].delivery_system) &&
+ (m == CX24116_MODFEC_MODES[i].modulation) &&
(f == CX24116_MODFEC_MODES[i].fec)) {
ret = i;
break;
@@ -429,13 +431,13 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state,
}
static int cx24116_set_fec(struct cx24116_state *state,
- fe_modulation_t mod, fe_code_rate_t fec)
+ fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec)
{
int ret = 0;
dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
- ret = cx24116_lookup_fecmod(state, mod, fec);
+ ret = cx24116_lookup_fecmod(state, delsys, mod, fec);
if (ret < 0)
return ret;
@@ -679,7 +681,8 @@ static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct cx24116_state *state = fe->demodulator_priv;
- int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) &
+ CX24116_STATUS_MASK;
dprintk("%s: status = 0x%02x\n", __func__, lock);
@@ -1205,7 +1208,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct cx24116_cmd cmd;
fe_status_t tunerstat;
- int i, status, ret, retune;
+ int i, status, ret, retune = 1;
dprintk("%s()\n", __func__);
@@ -1222,7 +1225,6 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
/* Pilot doesn't exist in DVB-S, turn bit off */
state->dnxt.pilot_val = CX24116_PILOT_OFF;
- retune = 1;
/* DVB-S only supports 0.35 */
if (c->rolloff != ROLLOFF_35) {
@@ -1250,7 +1252,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
case PILOT_AUTO: /* Not supported but emulated */
state->dnxt.pilot_val = (c->modulation == QPSK)
? CX24116_PILOT_OFF : CX24116_PILOT_ON;
- retune = 2;
+ retune++;
break;
case PILOT_OFF:
state->dnxt.pilot_val = CX24116_PILOT_OFF;
@@ -1287,6 +1289,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
__func__, c->delivery_system);
return -EOPNOTSUPP;
}
+ state->dnxt.delsys = c->delivery_system;
state->dnxt.modulation = c->modulation;
state->dnxt.frequency = c->frequency;
state->dnxt.pilot = c->pilot;
@@ -1297,7 +1300,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
return ret;
/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
- ret = cx24116_set_fec(state, c->modulation, c->fec_inner);
+ ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner);
if (ret != 0)
return ret;
@@ -1308,6 +1311,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe,
/* discard the 'current' tuning parameters and prepare to tune */
cx24116_clone_params(fe);
+ dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys);
dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation);
dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__,
@@ -1427,6 +1431,23 @@ tuned: /* Set/Reset B/W */
return ret;
}
+static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params,
+ unsigned int mode_flags, unsigned int *delay, fe_status_t *status)
+{
+ *delay = HZ / 5;
+ if (params) {
+ int ret = cx24116_set_frontend(fe, params);
+ if (ret)
+ return ret;
+ }
+ return cx24116_read_status(fe, status);
+}
+
+static int cx24116_get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
static struct dvb_frontend_ops cx24116_ops = {
.info = {
@@ -1458,6 +1479,8 @@ static struct dvb_frontend_ops cx24116_ops = {
.set_voltage = cx24116_set_voltage,
.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
.diseqc_send_burst = cx24116_diseqc_send_burst,
+ .get_frontend_algo = cx24116_get_algo,
+ .tune = cx24116_tune,
.set_property = cx24116_set_property,
.get_property = cx24116_get_property,
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
index 3e8126857127..aab8112e2db2 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -66,7 +66,8 @@ struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
return NULL;
}
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+static inline
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
int no_of_demods, u8 default_addr,
struct dib7000p_config cfg[])
{
@@ -74,13 +75,15 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
return -ENODEV;
}
-extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+static inline
+int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
-extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+static inline
+int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
index b9ca5c8d2dd9..ec4e08dbc699 100644
--- a/drivers/media/dvb/frontends/drx397xD.c
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -39,7 +39,7 @@ static const char mod_name[] = "drx397xD";
#define F_SET_0D4h 2
enum fw_ix {
-#define _FW_ENTRY(a, b) b
+#define _FW_ENTRY(a, b, c) b
#include "drx397xD_fw.h"
};
@@ -72,11 +72,11 @@ static struct {
int refcnt;
const u8 *data[ARRAY_SIZE(blob_name)];
} fw[] = {
-#define _FW_ENTRY(a, b) { \
- .name = a, \
- .file = 0, \
- .lock = RW_LOCK_UNLOCKED, \
- .refcnt = 0, \
+#define _FW_ENTRY(a, b, c) { \
+ .name = a, \
+ .file = 0, \
+ .lock = __RW_LOCK_UNLOCKED(fw[c].lock), \
+ .refcnt = 0, \
.data = { } }
#include "drx397xD_fw.h"
};
diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h
index 01de02a81cd4..c8b44c1e807f 100644
--- a/drivers/media/dvb/frontends/drx397xD_fw.h
+++ b/drivers/media/dvb/frontends/drx397xD_fw.h
@@ -18,8 +18,8 @@
*/
#ifdef _FW_ENTRY
- _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ),
- _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ),
+ _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0, DRXD_FW_A2 ),
+ _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1, DRXD_FW_B1 ),
#undef _FW_ENTRY
#endif /* _FW_ENTRY */
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index ea058153ebfa..9f6349964cda 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -311,7 +311,7 @@ static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
.count = 4,
.entries = {
{ 1250000, 500, 0xc4, 0x00},
- { 1550000, 500, 0xc4, 0x40},
+ { 1450000, 500, 0xc4, 0x40},
{ 2050000, 500, 0xc4, 0x80},
{ 2150000, 500, 0xc4, 0xc0},
},
diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c
new file mode 100644
index 000000000000..469ace5692c6
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3304.c
@@ -0,0 +1,378 @@
+/*
+ * Driver for LG ATSC lgdt3304 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "lgdt3304.h"
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"lgdt3304 debugging (default off)");
+
+#define dprintk(fmt, args...) if (debug) do {\
+ printk("lgdt3304 debug: " fmt, ##args); } while (0)
+
+struct lgdt3304_state
+{
+ struct dvb_frontend frontend;
+ fe_modulation_t current_modulation;
+ __u32 snr;
+ __u32 current_frequency;
+ __u8 addr;
+ struct i2c_adapter *i2c;
+};
+
+static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf
+ };
+ int i;
+ int err;
+
+ for (i=0; i<len-1; i+=3){
+ if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+ i2cmsgs.buf += 3;
+ }
+ return 0;
+}
+
+static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ struct i2c_msg i2cmsgs[2];
+ int ret;
+ __u8 buf;
+
+ __u8 regbuf[2] = { reg>>8, reg&0xff };
+
+ i2cmsgs[0].addr = state->addr;
+ i2cmsgs[0].flags = 0;
+ i2cmsgs[0].len = 2;
+ i2cmsgs[0].buf = regbuf;
+
+ i2cmsgs[1].addr = state->addr;
+ i2cmsgs[1].flags = I2C_M_RD;
+ i2cmsgs[1].len = 1;
+ i2cmsgs[1].buf = &buf;
+
+ if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, ret);
+ return ret;
+ }
+
+ return buf;
+}
+
+static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ char buffer[3] = { reg>>8, reg&0xff, val };
+ int ret;
+
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 3,
+ .buf=buffer
+ };
+ ret = i2c_transfer(state->i2c, &i2cmsgs, 1);
+ if (ret != 1) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int lgdt3304_soft_Reset(struct dvb_frontend *fe)
+{
+ lgdt3304_i2c_write_reg(fe, 0x0002, 0x9a);
+ lgdt3304_i2c_write_reg(fe, 0x0002, 0x9b);
+ mdelay(200);
+ return 0;
+}
+
+static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
+ int err = 0;
+
+ static __u8 lgdt3304_vsb8_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x13,
+ 0x00, 0x0d, 0x02,
+ 0x00, 0x0e, 0x02,
+ 0x00, 0x12, 0x32,
+ 0x00, 0x13, 0xc4,
+ 0x01, 0x12, 0x17,
+ 0x01, 0x13, 0x15,
+ 0x01, 0x14, 0x18,
+ 0x01, 0x15, 0xff,
+ 0x01, 0x16, 0x2c,
+ 0x02, 0x14, 0x67,
+ 0x02, 0x24, 0x8d,
+ 0x04, 0x27, 0x12,
+ 0x04, 0x28, 0x4f,
+ 0x03, 0x08, 0x80,
+ 0x03, 0x09, 0x00,
+ 0x03, 0x0d, 0x00,
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x14, 0xe1,
+ 0x05, 0x0e, 0x5b,
+ };
+
+ /* not yet tested .. */
+ static __u8 lgdt3304_qam64_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x18,
+ 0x00, 0x0d, 0x02,
+ //0x00, 0x0e, 0x02,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x00,
+ 0x03, 0x14, 0xe3,
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+ 0x05, 0x0e, 0x5b,
+ };
+
+
+ /* tested with KWorld a340 */
+ static __u8 lgdt3304_qam256_data[] = {
+ /* 16bit , 8bit */
+ /* regs , val */
+ 0x00, 0x00, 0x01, //0x19,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x80,
+ 0x00, 0x0d, 0x02,
+ 0x03, 0x14, 0xe3,
+
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+
+ 0x03, 0x0d, 0x14,
+ //0x05, 0x0e, 0x5b,
+ 0x01, 0x06, 0x4a,
+ 0x01, 0x07, 0x3d,
+ 0x01, 0x08, 0x70,
+ 0x01, 0x09, 0xa3,
+
+ 0x05, 0x04, 0xfd,
+
+ 0x00, 0x0d, 0x82,
+
+ 0x05, 0x0e, 0x5b,
+
+ 0x05, 0x0e, 0x5b,
+
+ 0x00, 0x02, 0x9a,
+
+ 0x00, 0x02, 0x9b,
+
+ 0x00, 0x00, 0x01,
+ 0x00, 0x12, 0x2a,
+ 0x00, 0x13, 0x80,
+ 0x00, 0x0d, 0x02,
+ 0x03, 0x14, 0xe3,
+
+ 0x03, 0x0e, 0x1c,
+ 0x03, 0x08, 0x66,
+ 0x03, 0x09, 0x66,
+ 0x03, 0x0a, 0x08,
+ 0x03, 0x0b, 0x9b,
+
+ 0x03, 0x0d, 0x14,
+ 0x01, 0x06, 0x4a,
+ 0x01, 0x07, 0x3d,
+ 0x01, 0x08, 0x70,
+ 0x01, 0x09, 0xa3,
+
+ 0x05, 0x04, 0xfd,
+
+ 0x00, 0x0d, 0x82,
+
+ 0x05, 0x0e, 0x5b,
+ };
+
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ if (state->current_modulation != param->u.vsb.modulation) {
+ switch(param->u.vsb.modulation) {
+ case VSB_8:
+ err = i2c_write_demod_bytes(fe, lgdt3304_vsb8_data,
+ sizeof(lgdt3304_vsb8_data));
+ break;
+ case QAM_64:
+ err = i2c_write_demod_bytes(fe, lgdt3304_qam64_data,
+ sizeof(lgdt3304_qam64_data));
+ break;
+ case QAM_256:
+ err = i2c_write_demod_bytes(fe, lgdt3304_qam256_data,
+ sizeof(lgdt3304_qam256_data));
+ break;
+ default:
+ break;
+ }
+
+ if (err) {
+ printk("%s error setting modulation\n", __FUNCTION__);
+ } else {
+ state->current_modulation = param->u.vsb.modulation;
+ }
+ }
+ state->current_frequency = param->frequency;
+
+ lgdt3304_soft_Reset(fe);
+
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, param);
+
+ return 0;
+}
+
+static int lgdt3304_init(struct dvb_frontend *fe) {
+ return 0;
+}
+
+static int lgdt3304_sleep(struct dvb_frontend *fe) {
+ return 0;
+}
+
+
+static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct lgdt3304_state *state = fe->demodulator_priv;
+ int r011d;
+ int qam_lck;
+
+ *status = 0;
+ dprintk("lgdt read status\n");
+
+ r011d = lgdt3304_i2c_read_reg(fe, 0x011d);
+
+ dprintk("%02x\n", r011d);
+
+ switch(state->current_modulation) {
+ case VSB_8:
+ if (r011d & 0x80) {
+ dprintk("VSB Locked\n");
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ }
+ break;
+ case QAM_64:
+ case QAM_256:
+ qam_lck = r011d & 0x7;
+ switch(qam_lck) {
+ case 0x0: dprintk("Unlock\n");
+ break;
+ case 0x4: dprintk("1st Lock in acquisition state\n");
+ break;
+ case 0x6: dprintk("2nd Lock in acquisition state\n");
+ break;
+ case 0x7: dprintk("Final Lock in good reception state\n");
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ break;
+ }
+ break;
+ default:
+ printk("%s unhandled modulation\n", __FUNCTION__);
+ }
+
+
+ return 0;
+}
+
+static int lgdt3304_read_ber(struct dvb_frontend *fe, __u32 *ber)
+{
+ dprintk("read ber\n");
+ return 0;
+}
+
+static int lgdt3304_read_snr(struct dvb_frontend *fe, __u16 *snr)
+{
+ dprintk("read snr\n");
+ return 0;
+}
+
+static int lgdt3304_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
+{
+ dprintk("read ucblocks\n");
+ return 0;
+}
+
+static void lgdt3304_release(struct dvb_frontend *fe)
+{
+ struct lgdt3304_state *state = (struct lgdt3304_state *)fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops demod_lgdt3304={
+ .info = {
+ .name = "LG 3304",
+ .type = FE_ATSC,
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+ .symbol_rate_min = 5056941,
+ .symbol_rate_max = 10762000,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .init = lgdt3304_init,
+ .sleep = lgdt3304_sleep,
+ .set_frontend = lgdt3304_set_parameters,
+ .read_snr = lgdt3304_read_snr,
+ .read_ber = lgdt3304_read_ber,
+ .read_status = lgdt3304_read_status,
+ .read_ucblocks = lgdt3304_read_ucblocks,
+ .release = lgdt3304_release,
+};
+
+struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c)
+{
+
+ struct lgdt3304_state *state;
+ state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL);
+ memset(state, 0x0, sizeof(struct lgdt3304_state));
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+
+ memcpy(&state->frontend.ops, &demod_lgdt3304, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+EXPORT_SYMBOL_GPL(lgdt3304_attach);
+MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
+MODULE_DESCRIPTION("LGE LGDT3304 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgdt3304.h b/drivers/media/dvb/frontends/lgdt3304.h
new file mode 100644
index 000000000000..fc409fe59acb
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3304.h
@@ -0,0 +1,45 @@
+/*
+ * Driver for DVB-T lgdt3304 demodulator
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@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.=
+ */
+
+#ifndef LGDT3304_H
+#define LGDT3304_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgdt3304_config
+{
+ /* demodulator's I2C address */
+ u8 i2c_address;
+};
+
+#if defined(CONFIG_DVB_LGDT3304) || (defined(CONFIG_DVB_LGDT3304_MODULE) && defined(MODULE))
+extern struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGDT */
+
+#endif /* LGDT3304_H */
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c
index 40644aacffcb..66e2dd6d6fe4 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb/frontends/s5h1411.c
@@ -874,6 +874,9 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
/* Note: Leaving the I2C gate open here. */
s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1);
+ /* Put the device into low-power mode until first use */
+ s5h1411_set_powerstate(&state->frontend, 1);
+
return &state->frontend;
error:
diff --git a/drivers/media/dvb/frontends/s921_core.c b/drivers/media/dvb/frontends/s921_core.c
new file mode 100644
index 000000000000..974b52be9aea
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_core.c
@@ -0,0 +1,216 @@
+/*
+ * Driver for Sharp s921 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "s921_core.h"
+
+static int s921_isdb_init(struct s921_isdb_t *dev);
+static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params);
+static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params);
+static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data);
+
+static u8 init_table[]={ 0x01, 0x40, 0x02, 0x00, 0x03, 0x40, 0x04, 0x01,
+ 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x5a, 0x0c, 0x00,
+ 0x0d, 0x00, 0x0f, 0x00, 0x13, 0x1b, 0x14, 0x80,
+ 0x15, 0x40, 0x17, 0x70, 0x18, 0x01, 0x19, 0x12,
+ 0x1a, 0x01, 0x1b, 0x12, 0x1c, 0xa0, 0x1d, 0x00,
+ 0x1e, 0x0a, 0x1f, 0x08, 0x20, 0x40, 0x21, 0xff,
+ 0x22, 0x4c, 0x23, 0x4e, 0x24, 0x4c, 0x25, 0x00,
+ 0x26, 0x00, 0x27, 0xf4, 0x28, 0x60, 0x29, 0x88,
+ 0x2a, 0x40, 0x2b, 0x40, 0x2c, 0xff, 0x2d, 0x00,
+ 0x2e, 0xff, 0x2f, 0x00, 0x30, 0x20, 0x31, 0x06,
+ 0x32, 0x0c, 0x34, 0x0f, 0x37, 0xfe, 0x38, 0x00,
+ 0x39, 0x63, 0x3a, 0x10, 0x3b, 0x10, 0x47, 0x00,
+ 0x49, 0xe5, 0x4b, 0x00, 0x50, 0xc0, 0x52, 0x20,
+ 0x54, 0x5a, 0x55, 0x5b, 0x56, 0x40, 0x57, 0x70,
+ 0x5c, 0x50, 0x5d, 0x00, 0x62, 0x17, 0x63, 0x2f,
+ 0x64, 0x6f, 0x68, 0x00, 0x69, 0x89, 0x6a, 0x00,
+ 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00,
+ 0x70, 0x00, 0x71, 0x00, 0x75, 0x00, 0x76, 0x30,
+ 0x77, 0x01, 0xaf, 0x00, 0xb0, 0xa0, 0xb2, 0x3d,
+ 0xb3, 0x25, 0xb4, 0x8b, 0xb5, 0x4b, 0xb6, 0x3f,
+ 0xb7, 0xff, 0xb8, 0xff, 0xb9, 0xfc, 0xba, 0x00,
+ 0xbb, 0x00, 0xbc, 0x00, 0xd0, 0x30, 0xe4, 0x84,
+ 0xf0, 0x48, 0xf1, 0x19, 0xf2, 0x5a, 0xf3, 0x8e,
+ 0xf4, 0x2d, 0xf5, 0x07, 0xf6, 0x5a, 0xf7, 0xba,
+ 0xf8, 0xd7 };
+
+static u8 c_table[]={ 0x58, 0x8a, 0x7b, 0x59, 0x8c, 0x7b, 0x5a, 0x8e, 0x5b,
+ 0x5b, 0x90, 0x5b, 0x5c, 0x92, 0x5b, 0x5d, 0x94, 0x5b,
+ 0x5e, 0x96, 0x5b, 0x5f, 0x98, 0x3b, 0x60, 0x9a, 0x3b,
+ 0x61, 0x9c, 0x3b, 0x62, 0x9e, 0x3b, 0x63, 0xa0, 0x3b,
+ 0x64, 0xa2, 0x1b, 0x65, 0xa4, 0x1b, 0x66, 0xa6, 0x1b,
+ 0x67, 0xa8, 0x1b, 0x68, 0xaa, 0x1b, 0x69, 0xac, 0x1b,
+ 0x6a, 0xae, 0x1b, 0x6b, 0xb0, 0x1b, 0x6c, 0xb2, 0x1b,
+ 0x6d, 0xb4, 0xfb, 0x6e, 0xb6, 0xfb, 0x6f, 0xb8, 0xfb,
+ 0x70, 0xba, 0xfb, 0x71, 0xbc, 0xdb, 0x72, 0xbe, 0xdb,
+ 0x73, 0xc0, 0xdb, 0x74, 0xc2, 0xdb, 0x75, 0xc4, 0xdb,
+ 0x76, 0xc6, 0xdb, 0x77, 0xc8, 0xbb, 0x78, 0xca, 0xbb,
+ 0x79, 0xcc, 0xbb, 0x7a, 0xce, 0xbb, 0x7b, 0xd0, 0xbb,
+ 0x7c, 0xd2, 0xbb, 0x7d, 0xd4, 0xbb, 0x7e, 0xd6, 0xbb,
+ 0x7f, 0xd8, 0xbb, 0x80, 0xda, 0x9b, 0x81, 0xdc, 0x9b,
+ 0x82, 0xde, 0x9b, 0x83, 0xe0, 0x9b, 0x84, 0xe2, 0x9b,
+ 0x85, 0xe4, 0x9b, 0x86, 0xe6, 0x9b, 0x87, 0xe8, 0x9b,
+ 0x88, 0xea, 0x9b, 0x89, 0xec, 0x9b };
+
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data) {
+ switch(cmd) {
+ case ISDB_T_CMD_INIT:
+ s921_isdb_init(dev);
+ break;
+ case ISDB_T_CMD_SET_PARAM:
+ s921_isdb_set_parameters(dev, data);
+ break;
+ case ISDB_T_CMD_TUNE:
+ s921_isdb_tune(dev, data);
+ break;
+ case ISDB_T_CMD_GET_STATUS:
+ s921_isdb_get_status(dev, data);
+ break;
+ default:
+ printk("unhandled command\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s921_isdb_init(struct s921_isdb_t *dev) {
+ unsigned int i;
+ unsigned int ret;
+ printk("isdb_init\n");
+ for (i = 0; i < sizeof(init_table); i+=2) {
+ ret = dev->i2c_write(dev->priv_dev, init_table[i], init_table[i+1]);
+ if (ret != 0) {
+ printk("i2c write failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s921_isdb_set_parameters(struct s921_isdb_t *dev, struct s921_isdb_t_transmission_mode_params *params) {
+
+ int ret;
+ /* auto is sufficient for now, lateron this should be reflected in an extra interface */
+
+
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb0, 0xa0); //mod_b2);
+ ret = dev->i2c_write(dev->priv_dev, 0xb2, 0x3d); //mod_b2);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb3, 0x25); //mod_b3);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb4, 0x8b); //mod_b4);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb5, 0x4b); //mod_b5);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb6, 0x3f); //mod_b6);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xb7, 0x3f); //mod_b7);
+ if (ret < 0)
+ return -EINVAL;
+
+ return E_OK;
+}
+
+static int s921_isdb_tune(struct s921_isdb_t *dev, struct s921_isdb_t_tune_params *params) {
+
+ int ret;
+ int index;
+
+ index = (params->frequency - 473143000)/6000000;
+
+ if (index > 48) {
+ return -EINVAL;
+ }
+
+ dev->i2c_write(dev->priv_dev, 0x47, 0x60);
+
+ ret = dev->i2c_write(dev->priv_dev, 0x68, 0x00);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x69, 0x89);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf0, 0x48);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf1, 0x19);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf2, c_table[index*3]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf3, c_table[index*3+1]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf4, c_table[index*3+2]);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf5, 0xae);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf6, 0xb7);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf7, 0xba);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0xf8, 0xd7);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x68, 0x0a);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = dev->i2c_write(dev->priv_dev, 0x69, 0x09);
+ if (ret < 0)
+ return -EINVAL;
+
+ dev->i2c_write(dev->priv_dev, 0x01, 0x40);
+ return 0;
+}
+
+static int s921_isdb_get_status(struct s921_isdb_t *dev, void *data) {
+ unsigned int *ret = (unsigned int*)data;
+ u8 ifagc_dt;
+ u8 rfagc_dt;
+
+ mdelay(10);
+ ifagc_dt = dev->i2c_read(dev->priv_dev, 0x81);
+ rfagc_dt = dev->i2c_read(dev->priv_dev, 0x82);
+ if (rfagc_dt == 0x40) {
+ *ret = 1;
+ }
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/s921_core.h b/drivers/media/dvb/frontends/s921_core.h
new file mode 100644
index 000000000000..de2f10a44e72
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_core.h
@@ -0,0 +1,114 @@
+#ifndef _S921_CORE_H
+#define _S921_CORE_H
+//#define u8 unsigned int
+//#define u32 unsigned int
+
+
+
+//#define EINVAL -1
+#define E_OK 0
+
+struct s921_isdb_t {
+ void *priv_dev;
+ int (*i2c_write)(void *dev, u8 reg, u8 val);
+ int (*i2c_read)(void *dev, u8 reg);
+};
+
+#define ISDB_T_CMD_INIT 0
+#define ISDB_T_CMD_SET_PARAM 1
+#define ISDB_T_CMD_TUNE 2
+#define ISDB_T_CMD_GET_STATUS 3
+
+struct s921_isdb_t_tune_params {
+ u32 frequency;
+};
+
+struct s921_isdb_t_status {
+};
+
+struct s921_isdb_t_transmission_mode_params {
+ u8 mode;
+ u8 layer_a_mode;
+#define ISDB_T_LA_MODE_1 0
+#define ISDB_T_LA_MODE_2 1
+#define ISDB_T_LA_MODE_3 2
+ u8 layer_a_carrier_modulation;
+#define ISDB_T_LA_CM_DQPSK 0
+#define ISDB_T_LA_CM_QPSK 1
+#define ISDB_T_LA_CM_16QAM 2
+#define ISDB_T_LA_CM_64QAM 3
+#define ISDB_T_LA_CM_NOLAYER 4
+ u8 layer_a_code_rate;
+#define ISDB_T_LA_CR_1_2 0
+#define ISDB_T_LA_CR_2_3 1
+#define ISDB_T_LA_CR_3_4 2
+#define ISDB_T_LA_CR_5_6 4
+#define ISDB_T_LA_CR_7_8 8
+#define ISDB_T_LA_CR_NOLAYER 16
+ u8 layer_a_time_interleave;
+#define ISDB_T_LA_TI_0 0
+#define ISDB_T_LA_TI_1 1
+#define ISDB_T_LA_TI_2 2
+#define ISDB_T_LA_TI_4 4
+#define ISDB_T_LA_TI_8 8
+#define ISDB_T_LA_TI_16 16
+#define ISDB_T_LA_TI_32 32
+ u8 layer_a_nseg;
+
+ u8 layer_b_mode;
+#define ISDB_T_LB_MODE_1 0
+#define ISDB_T_LB_MODE_2 1
+#define ISDB_T_LB_MODE_3 2
+ u8 layer_b_carrier_modulation;
+#define ISDB_T_LB_CM_DQPSK 0
+#define ISDB_T_LB_CM_QPSK 1
+#define ISDB_T_LB_CM_16QAM 2
+#define ISDB_T_LB_CM_64QAM 3
+#define ISDB_T_LB_CM_NOLAYER 4
+ u8 layer_b_code_rate;
+#define ISDB_T_LB_CR_1_2 0
+#define ISDB_T_LB_CR_2_3 1
+#define ISDB_T_LB_CR_3_4 2
+#define ISDB_T_LB_CR_5_6 4
+#define ISDB_T_LB_CR_7_8 8
+#define ISDB_T_LB_CR_NOLAYER 16
+ u8 layer_b_time_interleave;
+#define ISDB_T_LB_TI_0 0
+#define ISDB_T_LB_TI_1 1
+#define ISDB_T_LB_TI_2 2
+#define ISDB_T_LB_TI_4 4
+#define ISDB_T_LB_TI_8 8
+#define ISDB_T_LB_TI_16 16
+#define ISDB_T_LB_TI_32 32
+ u8 layer_b_nseg;
+
+ u8 layer_c_mode;
+#define ISDB_T_LC_MODE_1 0
+#define ISDB_T_LC_MODE_2 1
+#define ISDB_T_LC_MODE_3 2
+ u8 layer_c_carrier_modulation;
+#define ISDB_T_LC_CM_DQPSK 0
+#define ISDB_T_LC_CM_QPSK 1
+#define ISDB_T_LC_CM_16QAM 2
+#define ISDB_T_LC_CM_64QAM 3
+#define ISDB_T_LC_CM_NOLAYER 4
+ u8 layer_c_code_rate;
+#define ISDB_T_LC_CR_1_2 0
+#define ISDB_T_LC_CR_2_3 1
+#define ISDB_T_LC_CR_3_4 2
+#define ISDB_T_LC_CR_5_6 4
+#define ISDB_T_LC_CR_7_8 8
+#define ISDB_T_LC_CR_NOLAYER 16
+ u8 layer_c_time_interleave;
+#define ISDB_T_LC_TI_0 0
+#define ISDB_T_LC_TI_1 1
+#define ISDB_T_LC_TI_2 2
+#define ISDB_T_LC_TI_4 4
+#define ISDB_T_LC_TI_8 8
+#define ISDB_T_LC_TI_16 16
+#define ISDB_T_LC_TI_32 32
+ u8 layer_c_nseg;
+};
+
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
+#endif
diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c
new file mode 100644
index 000000000000..3cbb9cb2cf47
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_module.c
@@ -0,0 +1,190 @@
+/*
+ * Driver for Sharp s921 driver
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.de>
+ *
+ * All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "s921_module.h"
+#include "s921_core.h"
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"s921 debugging (default off)");
+
+#define dprintk(fmt, args...) if (debug) do {\
+ printk("s921 debug: " fmt, ##args); } while (0)
+
+struct s921_state
+{
+ struct dvb_frontend frontend;
+ fe_modulation_t current_modulation;
+ __u32 snr;
+ __u32 current_frequency;
+ __u8 addr;
+ struct s921_isdb_t dev;
+ struct i2c_adapter *i2c;
+};
+
+static int s921_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) {
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ struct s921_isdb_t_transmission_mode_params params;
+ struct s921_isdb_t_tune_params tune_params;
+
+ tune_params.frequency = param->frequency;
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_SET_PARAM, &params);
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_TUNE, &tune_params);
+ mdelay(100);
+ return 0;
+}
+
+static int s921_init(struct dvb_frontend *fe) {
+ printk("s921 init\n");
+ return 0;
+}
+
+static int s921_sleep(struct dvb_frontend *fe) {
+ printk("s921 sleep\n");
+ return 0;
+}
+
+static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ unsigned int ret;
+ mdelay(5);
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_GET_STATUS, &ret);
+ *status = 0;
+
+ printk("status: %02x\n", ret);
+ if (ret == 1) {
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ return 0;
+}
+
+static int s921_read_ber(struct dvb_frontend *fe, __u32 *ber)
+{
+ dprintk("read ber\n");
+ return 0;
+}
+
+static int s921_read_snr(struct dvb_frontend *fe, __u16 *snr)
+{
+ dprintk("read snr\n");
+ return 0;
+}
+
+static int s921_read_ucblocks(struct dvb_frontend *fe, __u32 *ucblocks)
+{
+ dprintk("read ucblocks\n");
+ return 0;
+}
+
+static void s921_release(struct dvb_frontend *fe)
+{
+ struct s921_state *state = (struct s921_state *)fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops demod_s921={
+ .info = {
+ .name = "SHARP S921",
+ .type = FE_OFDM,
+ .frequency_min = 473143000,
+ .frequency_max = 767143000,
+ .frequency_stepsize = 6000000,
+ .frequency_tolerance = 0,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+ .init = s921_init,
+ .sleep = s921_sleep,
+ .set_frontend = s921_set_parameters,
+ .read_snr = s921_read_snr,
+ .read_ber = s921_read_ber,
+ .read_status = s921_read_status,
+ .read_ucblocks = s921_read_ucblocks,
+ .release = s921_release,
+};
+
+static int s921_write(void *dev, u8 reg, u8 val) {
+ struct s921_state *state = dev;
+ char buf[2]={reg,val};
+ int err;
+ struct i2c_msg i2cmsgs = {
+ .addr = state->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf
+ };
+
+ if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) {
+ printk("%s i2c_transfer error %d\n", __FUNCTION__, err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int s921_read(void *dev, u8 reg) {
+ struct s921_state *state = dev;
+ u8 b1;
+ int ret;
+ struct i2c_msg msg[2] = { { .addr = state->addr,
+ .flags = 0,
+ .buf = &reg, .len = 1 },
+ { .addr = state->addr,
+ .flags = I2C_M_RD,
+ .buf = &b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2)
+ return ret;
+ return b1;
+}
+
+struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c)
+{
+
+ struct s921_state *state;
+ state = kzalloc(sizeof(struct s921_state), GFP_KERNEL);
+ memset(state, 0x0, sizeof(struct s921_state));
+
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+ state->dev.i2c_write = &s921_write;
+ state->dev.i2c_read = &s921_read;
+ state->dev.priv_dev = state;
+
+ s921_isdb_cmd(&state->dev, ISDB_T_CMD_INIT, NULL);
+
+ memcpy(&state->frontend.ops, &demod_s921, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+EXPORT_SYMBOL_GPL(s921_attach);
+MODULE_AUTHOR("Markus Rechberger <mrechberger@empiatech.com>");
+MODULE_DESCRIPTION("Sharp S921 ISDB-T 1Seg");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/s921_module.h b/drivers/media/dvb/frontends/s921_module.h
new file mode 100644
index 000000000000..78660424ba95
--- /dev/null
+++ b/drivers/media/dvb/frontends/s921_module.h
@@ -0,0 +1,49 @@
+/*
+ * Driver for DVB-T s921 demodulator
+ *
+ * Copyright (C) 2008 Markus Rechberger <mrechberger@sundtek.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.
+ *
+ * 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 S921_MODULE_H
+#define S921_MODULE_H
+
+#include <linux/dvb/frontend.h>
+#include "s921_core.h"
+
+int s921_isdb_init(struct s921_isdb_t *dev);
+int s921_isdb_cmd(struct s921_isdb_t *dev, u32 cmd, void *data);
+
+struct s921_config
+{
+ /* demodulator's I2C address */
+ u8 i2c_address;
+};
+
+#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) && defined(MODULE))
+extern struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* s921_attach(const struct s921_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_S921 */
+
+#endif /* S921_H */
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
index 3ddbe69c45ce..0bd16af8a6cd 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -8,7 +8,6 @@
* (at your option) any later version.
*
*/
-#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c
new file mode 100644
index 000000000000..ced9b7ae7d50
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_algo.c
@@ -0,0 +1,1519 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 "stb0899_drv.h"
+#include "stb0899_priv.h"
+#include "stb0899_reg.h"
+
+inline u32 stb0899_do_div(u64 n, u32 d)
+{
+ /* wrap do_div() for ease of use */
+
+ do_div(n, d);
+ return n;
+}
+
+/*
+ * stb0899_calc_srate
+ * Compute symbol rate
+ */
+static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr)
+{
+ u64 tmp;
+
+ /* srate = (SFR * master_clk) >> 20 */
+
+ /* sfr is of size 20 bit, stored with an offset of 4 bit */
+ tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2];
+ tmp &= ~0xf;
+ tmp *= master_clk;
+ tmp >>= 24;
+
+ return tmp;
+}
+
+/*
+ * stb0899_get_srate
+ * Get the current symbol rate
+ */
+u32 stb0899_get_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 sfr[3];
+
+ stb0899_read_regs(state, STB0899_SFRH, sfr, 3);
+
+ return stb0899_calc_srate(internal->master_clk, sfr);
+}
+
+/*
+ * stb0899_set_srate
+ * Set symbol frequency
+ * MasterClock: master clock frequency (hz)
+ * SymbolRate: symbol rate (bauds)
+ * return symbol frequency
+ */
+static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate)
+{
+ u32 tmp;
+ u8 sfr[3];
+
+ dprintk(state->verbose, FE_DEBUG, 1, "-->");
+ /*
+ * in order to have the maximum precision, the symbol rate entered into
+ * the chip is computed as the closest value of the "true value".
+ * In this purpose, the symbol rate value is rounded (1 is added on the bit
+ * below the LSB )
+ *
+ * srate = (SFR * master_clk) >> 20
+ * <=>
+ * SFR = srate << 20 / master_clk
+ *
+ * rounded:
+ * SFR = (srate << 21 + master_clk) / (2 * master_clk)
+ *
+ * stored as 20 bit number with an offset of 4 bit:
+ * sfr = SFR << 4;
+ */
+
+ tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk);
+ tmp <<= 4;
+
+ sfr[0] = tmp >> 16;
+ sfr[1] = tmp >> 8;
+ sfr[2] = tmp;
+
+ stb0899_write_regs(state, STB0899_SFRH, sfr, 3);
+
+ return srate;
+}
+
+/*
+ * stb0899_calc_derot_time
+ * Compute the amount of time needed by the derotator to lock
+ * SymbolRate: Symbol rate
+ * return: derotator time constant (ms)
+ */
+static long stb0899_calc_derot_time(long srate)
+{
+ if (srate > 0)
+ return (100000 / (srate / 1000));
+ else
+ return 0;
+}
+
+/*
+ * stb0899_carr_width
+ * Compute the width of the carrier
+ * return: width of carrier (kHz or Mhz)
+ */
+long stb0899_carr_width(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ return (internal->srate + (internal->srate * internal->rolloff) / 100);
+}
+
+/*
+ * stb0899_first_subrange
+ * Compute the first subrange of the search
+ */
+static void stb0899_first_subrange(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+ struct stb0899_config *config = state->config;
+
+ int range = 0;
+ u32 bandwidth = 0;
+
+ if (config->tuner_get_bandwidth) {
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+ config->tuner_get_bandwidth(&state->frontend, &bandwidth);
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+ range = bandwidth - stb0899_carr_width(state) / 2;
+ }
+
+ if (range > 0)
+ internal->sub_range = MIN(internal->srch_range, range);
+ else
+ internal->sub_range = 0;
+
+ internal->freq = params->freq;
+ internal->tuner_offst = 0L;
+ internal->sub_dir = 1;
+}
+
+/*
+ * stb0899_check_tmg
+ * check for timing lock
+ * internal.Ttiming: time to wait for loop lock
+ */
+static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ int lock;
+ u8 reg;
+ s8 timing;
+
+ msleep(internal->t_derot);
+
+ stb0899_write_reg(state, STB0899_RTF, 0xf2);
+ reg = stb0899_read_reg(state, STB0899_TLIR);
+ lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg);
+ timing = stb0899_read_reg(state, STB0899_RTF);
+
+ if (lock >= 42) {
+ if ((lock > 48) && (ABS(timing) >= 110)) {
+ internal->status = ANALOGCARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !");
+ } else {
+ internal->status = TIMINGOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !");
+ }
+ } else {
+ internal->status = NOTIMING;
+ dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !");
+ }
+ return internal->status;
+}
+
+/*
+ * stb0899_search_tmg
+ * perform a fs/2 zig-zag to find timing
+ */
+static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ short int derot_step, derot_freq = 0, derot_limit, next_loop = 3;
+ int index = 0;
+ u8 cfr[2];
+
+ internal->status = NOTIMING;
+
+ /* timing loop computation & symbol rate optimisation */
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_step = (params->srate / 2L) / internal->mclk;
+
+ while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) {
+ index++;
+ derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */
+
+ if (ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+ }
+ internal->direction = -internal->direction; /* Change zigzag direction */
+ }
+
+ if (internal->status == TIMINGOK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_carrier
+ * Check for carrier found
+ */
+static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 reg;
+
+ msleep(internal->t_derot); /* wait for derotator ok */
+
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ reg = stb0899_read_reg(state, STB0899_DSTATUS);
+ dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg);
+ if (STB0899_GETFIELD(CARRIER_FOUND, reg)) {
+ internal->status = CARRIEROK;
+ dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !");
+ } else {
+ internal->status = NOCARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_search_carrier
+ * Search for a QPSK carrier with the derotator
+ */
+static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3;
+ int index = 0;
+ u8 cfr[2];
+ u8 reg;
+
+ internal->status = NOCARRIER;
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_freq = internal->derot_freq;
+
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ do {
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk);
+ if (stb0899_check_carrier(state) == NOCARRIER) {
+ index++;
+ last_derot_freq = derot_freq;
+ derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */
+
+ if(ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+ }
+ }
+
+ internal->direction = -internal->direction; /* Change zigzag direction */
+ } while ((internal->status != CARRIEROK) && next_loop);
+
+ if (internal->status == CARRIEROK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq);
+ } else {
+ internal->derot_freq = last_derot_freq;
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_data
+ * Check for data found
+ */
+static enum stb0899_status stb0899_check_data(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ int lock = 0, index = 0, dataTime = 500, loop;
+ u8 reg;
+
+ internal->status = NODATA;
+
+ /* RESET FEC */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESACS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+ msleep(1);
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESACS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ if (params->srate <= 2000000)
+ dataTime = 2000;
+ else if (params->srate <= 5000000)
+ dataTime = 1500;
+ else if (params->srate <= 15000000)
+ dataTime = 1000;
+ else
+ dataTime = 500;
+
+ stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */
+ while (1) {
+ /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg);
+ loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg);
+
+ if (lock || loop || (index > dataTime))
+ break;
+ index++;
+ }
+
+ if (lock) { /* DATA LOCK indicator */
+ internal->status = DATAOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_search_data
+ * Search for a QPSK carrier with the derotator
+ */
+static enum stb0899_status stb0899_search_data(struct stb0899_state *state)
+{
+ short int derot_freq, derot_step, derot_limit, next_loop = 3;
+ u8 cfr[2];
+ u8 reg;
+ int index = 1;
+
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ derot_step = (params->srate / 4L) / internal->mclk;
+ derot_limit = (internal->sub_range / 2L) / internal->mclk;
+ derot_freq = internal->derot_freq;
+
+ do {
+ if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) {
+
+ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */
+ if (ABS(derot_freq) > derot_limit)
+ next_loop--;
+
+ if (next_loop) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk);
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq));
+ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq));
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */
+
+ stb0899_check_carrier(state);
+ index++;
+ }
+ }
+ internal->direction = -internal->direction; /* change zig zag direction */
+ } while ((internal->status != DATAOK) && next_loop);
+
+ if (internal->status == DATAOK) {
+ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */
+ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]);
+ dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_check_range
+ * check if the found frequency is in the correct range
+ */
+static enum stb0899_status stb0899_check_range(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ int range_offst, tp_freq;
+
+ range_offst = internal->srch_range / 2000;
+ tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000;
+
+ if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) {
+ internal->status = RANGEOK;
+ dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !");
+ } else {
+ internal->status = OUTOFRANGE;
+ dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !");
+ }
+
+ return internal->status;
+}
+
+/*
+ * NextSubRange
+ * Compute the next subrange of the search
+ */
+static void next_sub_range(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_params *params = &state->params;
+
+ long old_sub_range;
+
+ if (internal->sub_dir > 0) {
+ old_sub_range = internal->sub_range;
+ internal->sub_range = MIN((internal->srch_range / 2) -
+ (internal->tuner_offst + internal->sub_range / 2),
+ internal->sub_range);
+
+ if (internal->sub_range < 0)
+ internal->sub_range = 0;
+
+ internal->tuner_offst += (old_sub_range + internal->sub_range) / 2;
+ }
+
+ internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000;
+ internal->sub_dir = -internal->sub_dir;
+}
+
+/*
+ * stb0899_dvbs_algo
+ * Search for a signal, timing, carrier and data for a
+ * given frequency in a given range
+ */
+enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state)
+{
+ struct stb0899_params *params = &state->params;
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u8 bclc, reg;
+ u8 cfr[2];
+ u8 eq_const[10];
+ s32 clnI = 3;
+ u32 bandwidth = 0;
+
+ /* BETA values rated @ 99MHz */
+ s32 betaTab[5][4] = {
+ /* 5 10 20 30MBps */
+ { 37, 34, 32, 31 }, /* QPSK 1/2 */
+ { 37, 35, 33, 31 }, /* QPSK 2/3 */
+ { 37, 35, 33, 31 }, /* QPSK 3/4 */
+ { 37, 36, 33, 32 }, /* QPSK 5/6 */
+ { 37, 36, 33, 32 } /* QPSK 7/8 */
+ };
+
+ internal->direction = 1;
+
+ stb0899_set_srate(state, internal->master_clk, params->srate);
+ /* Carrier loop optimization versus symbol rate for acquisition*/
+ if (params->srate <= 5000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0x89);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x1c);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 0;
+ } else if (params->srate <= 15000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0xc9);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x22);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 1;
+ } else if(params->srate <= 25000000) {
+ stb0899_write_reg(state, STB0899_ACLC, 0x89);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x27);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 2;
+ } else {
+ stb0899_write_reg(state, STB0899_ACLC, 0xc8);
+ bclc = stb0899_read_reg(state, STB0899_BCLC);
+ STB0899_SETFIELD_VAL(BETA, bclc, 0x29);
+ stb0899_write_reg(state, STB0899_BCLC, bclc);
+ clnI = 3;
+ }
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition");
+ /* Set the timing loop to acquisition */
+ stb0899_write_reg(state, STB0899_RTC, 0x46);
+ stb0899_write_reg(state, STB0899_CFD, 0xee);
+
+ /* !! WARNING !!
+ * Do not read any status variables while acquisition,
+ * If any needed, read before the acquisition starts
+ * querying status while acquiring causes the
+ * acquisition to go bad and hence no locks.
+ */
+ dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d",
+ internal->derot_percent, params->srate, internal->mclk);
+
+ /* Initial calculations */
+ internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */
+ internal->t_derot = stb0899_calc_derot_time(params->srate);
+ internal->t_data = 500;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger");
+ /* RESET Stream merger */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /*
+ * Set KDIVIDER to an intermediate value between
+ * 1/2 and 7/8 for acquisition
+ */
+ reg = stb0899_read_reg(state, STB0899_DEMAPVIT);
+ STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60);
+ stb0899_write_reg(state, STB0899_DEMAPVIT, reg);
+
+ stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */
+ stb0899_write_reg(state, STB0899_VITSYNC, 0x19);
+
+ stb0899_first_subrange(state);
+ do {
+ /* Initialisations */
+ cfr[0] = cfr[1] = 0;
+ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */
+
+ stb0899_write_reg(state, STB0899_RTF, 0);
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ internal->derot_freq = 0;
+ internal->status = NOAGC1;
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ /* Move tuner to frequency */
+ dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency");
+ if (state->config->tuner_set_frequency)
+ state->config->tuner_set_frequency(&state->frontend, internal->freq);
+
+ if (state->config->tuner_get_frequency)
+ state->config->tuner_get_frequency(&state->frontend, &internal->freq);
+
+ msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */
+ dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq);
+ internal->status = AGC1OK;
+
+ /* There is signal in the band */
+ if (config->tuner_get_bandwidth)
+ config->tuner_get_bandwidth(&state->frontend, &bandwidth);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ if (params->srate <= bandwidth / 2)
+ stb0899_search_tmg(state); /* For low rates (SCPC) */
+ else
+ stb0899_check_tmg(state); /* For high rates (MCPC) */
+
+ if (internal->status == TIMINGOK) {
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "TIMING OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "CARRIER OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_search_data(state) == DATAOK) { /* Check for data */
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "DATA OK ! Derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ if (stb0899_check_range(state) == RANGEOK) {
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "RANGE OK ! derot freq=%d, mclk=%d",
+ internal->derot_freq, internal->mclk);
+
+ internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000);
+ reg = stb0899_read_reg(state, STB0899_PLPARM);
+ internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg);
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "freq=%d, internal resultant freq=%d",
+ params->freq, internal->freq);
+
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "internal puncture rate=%d",
+ internal->fecrate);
+ }
+ }
+ }
+ }
+ if (internal->status != RANGEOK)
+ next_sub_range(state);
+
+ } while (internal->sub_range && internal->status != RANGEOK);
+
+ /* Set the timing loop to tracking */
+ stb0899_write_reg(state, STB0899_RTC, 0x33);
+ stb0899_write_reg(state, STB0899_CFD, 0xf7);
+ /* if locked and range ok, set Kdiv */
+ if (internal->status == RANGEOK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !");
+ stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */
+ stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */
+
+ /*
+ * Carrier loop optimization versus
+ * symbol Rate/Puncture Rate for Tracking
+ */
+ reg = stb0899_read_reg(state, STB0899_BCLC);
+ switch (internal->fecrate) {
+ case STB0899_FEC_1_2: /* 13 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_2_3: /* 18 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 44);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_3_4: /* 21 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 60);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_5_6: /* 24 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 75);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ case STB0899_FEC_6_7: /* 25 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 88);
+ stb0899_write_reg(state, STB0899_ACLC, 0x88);
+ stb0899_write_reg(state, STB0899_BCLC, 0x9a);
+ break;
+ case STB0899_FEC_7_8: /* 26 */
+ stb0899_write_reg(state, STB0899_DEMAPVIT, 94);
+ STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]);
+ stb0899_write_reg(state, STB0899_BCLC, reg);
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate");
+ break;
+ }
+ /* release stream merger RESET */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /* disable carrier detector */
+ reg = stb0899_read_reg(state, STB0899_CFD);
+ STB0899_SETFIELD_VAL(CFD_ON, reg, 0);
+ stb0899_write_reg(state, STB0899_CFD, reg);
+
+ stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10);
+ }
+
+ return internal->status;
+}
+
+/*
+ * stb0899_dvbs2_config_uwp
+ * Configure UWP state machine
+ */
+static void stb0899_dvbs2_config_uwp(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+ u32 uwp1, uwp2, uwp3, reg;
+
+ uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
+ uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2);
+ uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3);
+
+ STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave);
+ STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant);
+ STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof);
+
+ STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse);
+ STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine);
+ STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold);
+
+ STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq);
+ STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO);
+ STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg);
+}
+
+/*
+ * stb0899_dvbs2_config_csm_auto
+ * Set CSM to AUTO mode
+ */
+static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state)
+{
+ u32 reg;
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg);
+}
+
+long Log2Int(int number)
+{
+ int i;
+
+ i = 0;
+ while ((1 << i) <= ABS(number))
+ i++;
+
+ if (number == 0)
+ i = 1;
+
+ return i - 1;
+}
+
+/*
+ * stb0899_dvbs2_calc_srate
+ * compute BTR_NOM_FREQ for the symbol rate
+ */
+static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq;
+ u32 master_clk, srate;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+ decim = 1 << dec_rate;
+ master_clk = internal->master_clk / 1000;
+ srate = internal->srate / 1000;
+
+ if (decim <= 4) {
+ intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk;
+ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk;
+ } else {
+ intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100;
+ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk;
+ }
+ btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk);
+
+ return btr_nom_freq;
+}
+
+/*
+ * stb0899_dvbs2_calc_dev
+ * compute the correction to be applied to symbol rate
+ */
+static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u32 dec_ratio, correction, master_clk, srate;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+
+ master_clk = internal->master_clk / 1000; /* for integer Caculation*/
+ srate = internal->srate / 1000; /* for integer Caculation*/
+ correction = (512 * master_clk) / (2 * dec_ratio * srate);
+
+ return correction;
+}
+
+/*
+ * stb0899_dvbs2_set_srate
+ * Set DVBS2 symbol rate
+ */
+static void stb0899_dvbs2_set_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq;
+ u32 correction, freq_adj, band_lim, decim_cntrl, reg;
+ u8 anti_alias;
+
+ /*set decimation to 1*/
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+
+ win_sel = 0;
+ if (dec_rate >= 5)
+ win_sel = dec_rate - 4;
+
+ decim = (1 << dec_rate);
+ /* (FSamp/Fsymbol *100) for integer Caculation */
+ f_sym = internal->master_clk / ((decim * internal->srate) / 1000);
+
+ if (f_sym <= 2250) /* don't band limit signal going into btr block*/
+ band_lim = 1;
+ else
+ band_lim = 0; /* band limit signal going into btr block*/
+
+ decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl);
+
+ if (f_sym <= 3450)
+ anti_alias = 0;
+ else if (f_sym <= 4250)
+ anti_alias = 1;
+ else
+ anti_alias = 2;
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias);
+ btr_nom_freq = stb0899_dvbs2_calc_srate(state);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq);
+
+ correction = stb0899_dvbs2_calc_dev(state);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL);
+ STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg);
+
+ /* scale UWP+CSM frequency to sample rate*/
+ freq_adj = internal->srate / (internal->master_clk / 4096);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj);
+}
+
+/*
+ * stb0899_dvbs2_set_btr_loopbw
+ * set bit timing loop bandwidth as a percentage of the symbol rate
+ */
+static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 sym_peak = 23, zeta = 707, loopbw_percent = 60;
+ s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft;
+ s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift;
+ u32 decim, K, wn, k_direct, k_indirect;
+ u32 reg;
+
+ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate);
+ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio;
+ dec_rate = Log2Int(dec_ratio);
+ decim = (1 << dec_rate);
+
+ sym_peak *= 576000;
+ K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000);
+ K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/
+
+ if (K != 0) {
+ K = sym_peak / K;
+ wn = (4 * zeta * zeta) + 1000000;
+ wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/
+
+ k_indirect = (wn * wn) / K;
+ k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/
+ k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/
+ k_direct *= 100;
+
+ k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2;
+ k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset;
+ k_btr1 = k_direct / (1 << k_direct_shift);
+ k_btr1 /= 10000;
+
+ k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/;
+ k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset;
+ k_btr0 = k_indirect * (1 << (-k_indirect_shift));
+ k_btr0 /= 1000000;
+
+ k_btr2_rshft = 0;
+ if (k_btr0_rshft > 15) {
+ k_btr2_rshft = k_btr0_rshft - 15;
+ k_btr0_rshft = 15;
+ }
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN);
+ STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft);
+ STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0);
+ STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft);
+ STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1);
+ STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg);
+ } else
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f);
+}
+
+/*
+ * stb0899_dvbs2_set_carr_freq
+ * set nominal frequency for carrier search
+ */
+static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk)
+{
+ struct stb0899_config *config = state->config;
+ s32 crl_nom_freq;
+ u32 reg;
+
+ crl_nom_freq = (1 << config->crl_nco_bits) / master_clk;
+ crl_nom_freq *= carr_freq;
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+}
+
+/*
+ * stb0899_dvbs2_init_calc
+ * Initialize DVBS2 UWP, CSM, carrier and timing loops
+ */
+static void stb0899_dvbs2_init_calc(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ s32 steps, step_size;
+ u32 range, reg;
+
+ /* config uwp and csm */
+ stb0899_dvbs2_config_uwp(state);
+ stb0899_dvbs2_config_csm_auto(state);
+
+ /* initialize BTR */
+ stb0899_dvbs2_set_srate(state);
+ stb0899_dvbs2_set_btr_loopbw(state);
+
+ if (internal->srate / 1000000 >= 15)
+ step_size = (1 << 17) / 5;
+ else if (internal->srate / 1000000 >= 10)
+ step_size = (1 << 17) / 7;
+ else if (internal->srate / 1000000 >= 5)
+ step_size = (1 << 17) / 10;
+ else
+ step_size = (1 << 17) / 4;
+
+ range = internal->srch_range / 1000000;
+ steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000));
+ steps = (steps + 6) / 10;
+ steps = (steps == 0) ? 1 : steps;
+ if (steps % 2 == 0)
+ stb0899_dvbs2_set_carr_freq(state, internal->center_freq -
+ (internal->step_size * (internal->srate / 20000000)),
+ (internal->master_clk) / 1000000);
+ else
+ stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000);
+
+ /*Set Carrier Search params (zigzag, num steps and freq step size*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2);
+ STB0899_SETFIELD_VAL(ZIGZAG, reg, 1);
+ STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps);
+ STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg);
+}
+
+/*
+ * stb0899_dvbs2_btr_init
+ * initialize the timing loop
+ */
+static void stb0899_dvbs2_btr_init(struct stb0899_state *state)
+{
+ u32 reg;
+
+ /* set enable BTR loopback */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL);
+ STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1);
+ STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg);
+
+ /* fix btr freq accum at 0 */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000);
+
+ /* fix btr freq accum at 0 */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000);
+}
+
+/*
+ * stb0899_dvbs2_reacquire
+ * trigger a DVB-S2 acquisition
+ */
+static void stb0899_dvbs2_reacquire(struct stb0899_state *state)
+{
+ u32 reg = 0;
+
+ /* demod soft reset */
+ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg);
+
+ /*Reset Timing Loop */
+ stb0899_dvbs2_btr_init(state);
+
+ /* reset Carrier loop */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30));
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30));
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0);
+
+ /*release demod soft reset */
+ reg = 0;
+ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg);
+
+ /* start acquisition process */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0);
+
+ /* equalizer Init */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1);
+
+ /*Start equilizer */
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0);
+ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0);
+ STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05);
+ STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+
+ /* RESET Packet delineator */
+ stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a);
+}
+
+/*
+ * stb0899_dvbs2_get_dmd_status
+ * get DVB-S2 Demod LOCK status
+ */
+static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout)
+{
+ int time = -10, lock = 0, uwp, csm;
+ u32 reg;
+
+ do {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS);
+ dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg);
+ if (STB0899_GETFIELD(IF_AGC_LOCK, reg))
+ dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !");
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
+ dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg);
+ uwp = STB0899_GETFIELD(UWP_LOCK, reg);
+ csm = STB0899_GETFIELD(CSM_LOCK, reg);
+ if (uwp && csm)
+ lock = 1;
+
+ time += 10;
+ msleep(10);
+
+ } while ((!lock) && (time <= timeout));
+
+ if (lock) {
+ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !");
+ return DVBS2_DEMOD_LOCK;
+ } else {
+ return DVBS2_DEMOD_NOLOCK;
+ }
+}
+
+/*
+ * stb0899_dvbs2_get_data_lock
+ * get FEC status
+ */
+static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout)
+{
+ int time = 0, lock = 0;
+ u8 reg;
+
+ while ((!lock) && (time < timeout)) {
+ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
+ dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg);
+ lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg);
+ time++;
+ }
+
+ return lock;
+}
+
+/*
+ * stb0899_dvbs2_get_fec_status
+ * get DVB-S2 FEC LOCK status
+ */
+static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout)
+{
+ int time = 0, Locked;
+
+ do {
+ Locked = stb0899_dvbs2_get_data_lock(state, 1);
+ time++;
+ msleep(1);
+
+ } while ((!Locked) && (time < timeout));
+
+ if (Locked) {
+ dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !");
+ return DVBS2_FEC_LOCK;
+ } else {
+ return DVBS2_FEC_NOLOCK;
+ }
+}
+
+
+/*
+ * stb0899_dvbs2_init_csm
+ * set parameters for manual mode
+ */
+static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod)
+{
+ struct stb0899_internal *internal = &state->internal;
+
+ s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80;
+ s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr;
+ u32 csm1, csm2, csm3, csm4;
+
+ if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) {
+ switch (modcod) {
+ case STB0899_QPSK_12:
+ gamma_acq = 25;
+ gamma_rho_acq = 2700;
+ gamma_trk = 12;
+ gamma_rho_trk = 180;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_35:
+ gamma_acq = 38;
+ gamma_rho_acq = 7182;
+ gamma_trk = 14;
+ gamma_rho_trk = 308;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_23:
+ gamma_acq = 42;
+ gamma_rho_acq = 9408;
+ gamma_trk = 17;
+ gamma_rho_trk = 476;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_34:
+ gamma_acq = 53;
+ gamma_rho_acq = 16642;
+ gamma_trk = 19;
+ gamma_rho_trk = 646;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_45:
+ gamma_acq = 53;
+ gamma_rho_acq = 17119;
+ gamma_trk = 22;
+ gamma_rho_trk = 880;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_56:
+ gamma_acq = 55;
+ gamma_rho_acq = 19250;
+ gamma_trk = 23;
+ gamma_rho_trk = 989;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_89:
+ gamma_acq = 60;
+ gamma_rho_acq = 24240;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ case STB0899_QPSK_910:
+ gamma_acq = 66;
+ gamma_rho_acq = 29634;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ default:
+ gamma_acq = 66;
+ gamma_rho_acq = 29634;
+ gamma_trk = 24;
+ gamma_rho_trk = 1176;
+ lock_count_thr = 8;
+ break;
+ }
+
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2);
+ csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3);
+ csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4);
+
+ STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass);
+ STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain);
+ STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift);
+ STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk);
+ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk);
+ STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr);
+ STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4);
+ }
+}
+
+/*
+ * stb0899_dvbs2_get_srate
+ * get DVB-S2 Symbol Rate
+ */
+static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg;
+ int div1, div2, rem1, rem2;
+
+ div1 = config->btr_nco_bits / 2;
+ div2 = config->btr_nco_bits - div1 - 1;
+
+ bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL);
+ decimRate = STB0899_GETFIELD(DECIM_RATE, reg);
+ decimRate = (1 << decimRate);
+
+ intval1 = internal->master_clk / (1 << div1);
+ intval2 = bTrNomFreq / (1 << div2);
+
+ rem1 = internal->master_clk % (1 << div1);
+ rem2 = bTrNomFreq % (1 << div2);
+ /* only for integer calculation */
+ srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1));
+ srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */
+
+ return srate;
+}
+
+/*
+ * stb0899_dvbs2_algo
+ * Search for signal, timing, carrier and data for a given
+ * frequency in a given range
+ */
+enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ enum stb0899_modcod modcod;
+
+ s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum;
+ int i = 0;
+ u32 reg, csm1;
+
+ if (internal->srate <= 2000000) {
+ searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */
+ FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */
+ } else if (internal->srate <= 5000000) {
+ searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */
+ FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */
+ } else if (internal->srate <= 10000000) {
+ searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs <SYMB <= 10Mbs */
+ FecLockTime = 80; /* 80 ms max time to lock FEC, 5Mbs< SYMB <= 10Mbs */
+ } else if (internal->srate <= 15000000) {
+ searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs <SYMB <= 15Mbs */
+ FecLockTime = 50; /* 50 ms max time to lock FEC, 10Mbs< SYMB <= 15Mbs */
+ } else if (internal->srate <= 20000000) {
+ searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */
+ FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */
+ } else if (internal->srate <= 25000000) {
+ searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */
+ FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */
+ } else {
+ searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */
+ FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */
+ }
+
+ /* Maintain Stream Merger in reset during acquisition */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ /* Move tuner to frequency */
+ if (state->config->tuner_set_frequency)
+ state->config->tuner_set_frequency(&state->frontend, internal->freq);
+ if (state->config->tuner_get_frequency)
+ state->config->tuner_get_frequency(&state->frontend, &internal->freq);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ /* Set IF AGC to acquisition */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4);
+ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2);
+ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg);
+
+ /* Initialisation */
+ stb0899_dvbs2_init_calc(state);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ switch (internal->inversion) {
+ case IQ_SWAP_OFF:
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0);
+ break;
+ case IQ_SWAP_ON:
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
+ break;
+ case IQ_SWAP_AUTO: /* use last successful search first */
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1);
+ break;
+ }
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
+ stb0899_dvbs2_reacquire(state);
+
+ /* Wait for demod lock (UWP and CSM) */
+ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
+
+ if (internal->status == DVBS2_DEMOD_LOCK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !");
+ i = 0;
+ /* Demod Locked, check FEC status */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+
+ /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ /* Read the frequency offset*/
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+ stb0899_dvbs2_reacquire(state);
+ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
+ i++;
+ }
+ }
+
+ if (internal->status != DVBS2_FEC_LOCK) {
+ if (internal->inversion == IQ_SWAP_AUTO) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg);
+ /* IQ Spectrum Inversion */
+ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg);
+ /* start acquistion process */
+ stb0899_dvbs2_reacquire(state);
+
+ /* Wait for demod lock (UWP and CSM) */
+ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime);
+ if (internal->status == DVBS2_DEMOD_LOCK) {
+ i = 0;
+ /* Demod Locked, check FEC */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+ /*try thrice for false locks, (UWP and CSM Locked but no FEC) */
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ /* Read the frequency offset*/
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ);
+ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg);
+
+ stb0899_dvbs2_reacquire(state);
+ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime);
+ i++;
+ }
+ }
+/*
+ if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED)
+ pParams->IQLocked = !iqSpectrum;
+*/
+ }
+ }
+ if (internal->status == DVBS2_FEC_LOCK) {
+ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !");
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2;
+ pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01;
+
+ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) &&
+ (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) &&
+ (pilots == 1)) {
+
+ stb0899_dvbs2_init_csm(state, pilots, modcod);
+ /* Wait for UWP,CSM and data LOCK 20ms max */
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+
+ i = 0;
+ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) {
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1);
+ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1);
+
+ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime);
+ i++;
+ }
+ }
+
+ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) &&
+ (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) &&
+ (pilots == 1)) {
+
+ /* Equalizer Disable update */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+ }
+
+ /* slow down the Equalizer once locked */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL);
+ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg);
+
+ /* Store signal parameters */
+ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ);
+
+ offsetfreq = offsetfreq / ((1 << 30) / 1000);
+ offsetfreq *= (internal->master_clk / 1000000);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2);
+ if (STB0899_GETFIELD(SPECTRUM_INVERT, reg))
+ offsetfreq *= -1;
+
+ internal->freq = internal->freq - offsetfreq;
+ internal->srate = stb0899_dvbs2_get_srate(state);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2;
+ internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01;
+ internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01;
+
+ /* Set IF AGC to tracking */
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3);
+
+ /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/
+ if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23))
+ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16);
+
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2);
+ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg);
+ }
+
+ /* Release Stream Merger Reset */
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESRS, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ return internal->status;
+}
diff --git a/drivers/media/dvb/frontends/stb0899_cfg.h b/drivers/media/dvb/frontends/stb0899_cfg.h
new file mode 100644
index 000000000000..0867906d3ff3
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_cfg.h
@@ -0,0 +1,287 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 __STB0899_CFG_H
+#define __STB0899_CFG_H
+
+static const struct stb0899_s2_reg stb0899_s2_init_2[] = {
+
+ { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */
+ { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */
+ { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */
+ { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */
+ { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */
+ { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */
+ { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */
+
+ { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */
+ { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */
+
+ { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */
+ { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */
+ { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */
+ { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */
+ { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */
+ { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */
+ { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */
+ { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */
+ { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */
+ { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */
+ { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */
+ { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */
+ { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */
+ { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */
+ { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */
+ { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */
+ { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */
+ { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */
+ { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */
+ { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */
+ { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */
+ { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */
+ { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */
+ { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */
+ { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */
+ { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */
+ { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */
+ { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */
+ { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */
+ { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */
+ { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */
+ { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */
+ { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */
+ { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */
+ { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */
+ { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */
+ { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */
+ { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */
+ { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */
+ { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */
+ { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */
+ { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */
+ { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */
+ { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */
+ { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */
+ { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */
+ { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */
+ { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */
+ { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */
+ { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */
+ { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */
+ { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */
+ { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */
+ { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */
+ { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */
+ { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */
+ { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */
+ { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */
+ { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */
+ { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */
+ { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */
+ { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */
+ { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */
+ { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */
+ { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */
+ { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */
+ { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */
+ { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */
+ { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */
+ { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */
+ { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */
+ { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */
+ { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */
+ { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */
+ { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */
+ { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */
+ { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */
+ { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */
+ { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */
+ { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */
+ { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */
+ { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */
+ { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */
+ { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */
+ { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */
+ { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */
+ { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */
+ { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */
+ { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */
+ { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */
+ { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */
+ { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */
+ { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */
+ { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */
+ { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */
+ { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */
+ { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */
+ { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */
+ { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */
+ { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */
+ { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */
+ { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */
+ { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */
+ { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */
+ { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */
+ { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */
+ { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */
+ { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */
+ { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */
+ { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */
+ { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */
+ { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */
+ { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */
+ { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */
+ { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */
+ { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */
+ { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */
+ { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */
+ { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */
+ { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/
+ { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/
+ { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/
+ { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */
+ { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */
+ { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */
+ { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */
+ { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */
+ { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */
+ { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */
+ { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */
+ { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */
+ { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */
+ { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */
+ { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/
+ { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */
+ { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */
+ { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */
+ { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */
+ { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */
+ { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */
+ { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */
+ { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */
+ { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */
+ { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */
+ { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/
+ { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */
+ { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */
+ { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */
+ { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */
+ { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */
+ { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */
+ { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */
+ { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */
+ { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */
+ { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */
+ { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/
+ { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */
+ { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */
+ { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */
+ { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */
+ { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */
+ { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */
+ { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */
+ { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */
+ { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */
+ { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */
+ { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/
+ { 0xffff , 0xffffffff , 0xffffffff },
+};
+static const struct stb0899_s2_reg stb0899_s2_init_4[] = {
+ { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */
+ { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */
+ { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */
+ { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */
+ { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */
+ { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */
+ { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */
+ { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */
+ { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */
+ { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */
+ { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */
+ { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */
+ { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */
+ { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */
+ { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */
+ { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */
+ { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */
+ { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */
+ { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */
+ { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */
+ { 0xffff , 0xffffffff , 0xffffffff },
+};
+
+static const struct stb0899_s1_reg stb0899_s1_init_5[] = {
+ { STB0899_TSTCK , 0x00 },
+ { STB0899_TSTRES , 0x00 },
+ { STB0899_TSTOUT , 0x00 },
+ { STB0899_TSTIN , 0x00 },
+ { STB0899_TSTSYS , 0x00 },
+ { STB0899_TSTCHIP , 0x00 },
+ { STB0899_TSTFREE , 0x00 },
+ { STB0899_TSTI2C , 0x00 },
+ { STB0899_BITSPEEDM , 0x00 },
+ { STB0899_BITSPEEDL , 0x00 },
+ { STB0899_TBUSBIT , 0x00 },
+ { STB0899_TSTDIS , 0x00 },
+ { STB0899_TSTDISRX , 0x00 },
+ { STB0899_TSTJETON , 0x00 },
+ { STB0899_TSTDCADJ , 0x00 },
+ { STB0899_TSTAGC1 , 0x00 },
+ { STB0899_TSTAGC1N , 0x00 },
+ { STB0899_TSTPOLYPH , 0x00 },
+ { STB0899_TSTR , 0x00 },
+ { STB0899_TSTAGC2 , 0x00 },
+ { STB0899_TSTCTL1 , 0x00 },
+ { STB0899_TSTCTL2 , 0x00 },
+ { STB0899_TSTCTL3 , 0x00 },
+ { STB0899_TSTDEMAP , 0x00 },
+ { STB0899_TSTDEMAP2 , 0x00 },
+ { STB0899_TSTDEMMON , 0x00 },
+ { STB0899_TSTRATE , 0x00 },
+ { STB0899_TSTSELOUT , 0x00 },
+ { STB0899_TSYNC , 0x00 },
+ { STB0899_TSTERR , 0x00 },
+ { STB0899_TSTRAM1 , 0x00 },
+ { STB0899_TSTVSELOUT , 0x00 },
+ { STB0899_TSTFORCEIN , 0x00 },
+ { STB0899_TSTRS1 , 0x00 },
+ { STB0899_TSTRS2 , 0x00 },
+ { STB0899_TSTRS3 , 0x00 },
+ { STB0899_GHOSTREG , 0x81 },
+ { 0xffff , 0xff },
+};
+
+#define STB0899_DVBS2_ESNO_AVE 3
+#define STB0899_DVBS2_ESNO_QUANT 32
+#define STB0899_DVBS2_AVFRAMES_COARSE 10
+#define STB0899_DVBS2_AVFRAMES_FINE 20
+#define STB0899_DVBS2_MISS_THRESHOLD 6
+#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125
+#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758
+#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350
+#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100
+
+#define STB0899_DVBS2_BTR_NCO_BITS 28
+#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15
+#define STB0899_DVBS2_CRL_NCO_BITS 30
+#define STB0899_DVBS2_LDPC_MAX_ITER 70
+
+#endif //__STB0899_CFG_H
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
new file mode 100644
index 000000000000..528820170228
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -0,0 +1,1684 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#include "stb0899_drv.h"
+#include "stb0899_priv.h"
+#include "stb0899_reg.h"
+
+static unsigned int verbose = 0;//1;
+module_param(verbose, int, 0644);
+
+/* C/N in dB/10, NIRM/NIRL */
+static const struct stb0899_tab stb0899_cn_tab[] = {
+ { 200, 2600 },
+ { 190, 2700 },
+ { 180, 2860 },
+ { 170, 3020 },
+ { 160, 3210 },
+ { 150, 3440 },
+ { 140, 3710 },
+ { 130, 4010 },
+ { 120, 4360 },
+ { 110, 4740 },
+ { 100, 5190 },
+ { 90, 5670 },
+ { 80, 6200 },
+ { 70, 6770 },
+ { 60, 7360 },
+ { 50, 7970 },
+ { 40, 8250 },
+ { 30, 9000 },
+ { 20, 9450 },
+ { 15, 9600 },
+};
+
+/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.0 dBm directly connected,
+ * -52.0 to -74.8 with extra attenuation.
+ * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm.
+ * Crude linear extrapolation below -84.8dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
+ { -950, -128 },
+ { -748, -94 },
+ { -745, -92 },
+ { -735, -90 },
+ { -720, -87 },
+ { -670, -77 },
+ { -640, -70 },
+ { -610, -62 },
+ { -600, -60 },
+ { -590, -56 },
+ { -560, -41 },
+ { -540, -25 },
+ { -530, -17 },
+ { -520, -11 },
+ { -500, 1 },
+ { -490, 6 },
+ { -480, 10 },
+ { -440, 22 },
+ { -420, 27 },
+ { -400, 31 },
+ { -380, 34 },
+ { -340, 40 },
+ { -320, 43 },
+ { -280, 48 },
+ { -250, 52 },
+ { -230, 55 },
+ { -180, 61 },
+ { -140, 66 },
+ { -90, 73 },
+ { -80, 74 },
+ { 500, 127 }
+};
+
+/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.1 dBm directly connected,
+ * -53.0 to -76.6 with extra attenuation.
+ * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm.
+ * Crude linear extrapolation below -76.6dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
+ { 700, 0 },
+ { -80, 3217 },
+ { -150, 3893 },
+ { -190, 4217 },
+ { -240, 4621 },
+ { -280, 4945 },
+ { -320, 5273 },
+ { -350, 5545 },
+ { -370, 5741 },
+ { -410, 6147 },
+ { -450, 6671 },
+ { -490, 7413 },
+ { -501, 7665 },
+ { -530, 8767 },
+ { -560, 10219 },
+ { -580, 10939 },
+ { -590, 11518 },
+ { -600, 11723 },
+ { -650, 12659 },
+ { -690, 13219 },
+ { -730, 13645 },
+ { -750, 13909 },
+ { -766, 14153 },
+ { -999, 16383 }
+};
+
+/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
+struct stb0899_tab stb0899_quant_tab[] = {
+ { 0, 0 },
+ { 0, 100 },
+ { 600, 200 },
+ { 950, 299 },
+ { 1200, 398 },
+ { 1400, 501 },
+ { 1560, 603 },
+ { 1690, 700 },
+ { 1810, 804 },
+ { 1910, 902 },
+ { 2000, 1000 },
+ { 2080, 1096 },
+ { 2160, 1202 },
+ { 2230, 1303 },
+ { 2350, 1496 },
+ { 2410, 1603 },
+ { 2460, 1698 },
+ { 2510, 1799 },
+ { 2600, 1995 },
+ { 2650, 2113 },
+ { 2690, 2213 },
+ { 2720, 2291 },
+ { 2760, 2399 },
+ { 2800, 2512 },
+ { 2860, 2692 },
+ { 2930, 2917 },
+ { 2960, 3020 },
+ { 3010, 3199 },
+ { 3040, 3311 },
+ { 3060, 3388 },
+ { 3120, 3631 },
+ { 3190, 3936 },
+ { 3400, 5012 },
+ { 3610, 6383 },
+ { 3800, 7943 },
+ { 4210, 12735 },
+ { 4500, 17783 },
+ { 4690, 22131 },
+ { 4810, 25410 }
+};
+
+/* DVB-S2 Es/N0 estimate in dB/100 vs read value */
+struct stb0899_tab stb0899_est_tab[] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 301, 2 },
+ { 1204, 16 },
+ { 1806, 64 },
+ { 2408, 256 },
+ { 2709, 512 },
+ { 3010, 1023 },
+ { 3311, 2046 },
+ { 3612, 4093 },
+ { 3823, 6653 },
+ { 3913, 8185 },
+ { 4010, 10233 },
+ { 4107, 12794 },
+ { 4214, 16368 },
+ { 4266, 18450 },
+ { 4311, 20464 },
+ { 4353, 22542 },
+ { 4391, 24604 },
+ { 4425, 26607 },
+ { 4457, 28642 },
+ { 4487, 30690 },
+ { 4515, 32734 },
+ { 4612, 40926 },
+ { 4692, 49204 },
+ { 4816, 65464 },
+ { 4913, 81846 },
+ { 4993, 98401 },
+ { 5060, 114815 },
+ { 5118, 131220 },
+ { 5200, 158489 },
+ { 5300, 199526 },
+ { 5400, 251189 },
+ { 5500, 316228 },
+ { 5600, 398107 },
+ { 5720, 524807 },
+ { 5721, 526017 },
+};
+
+int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int ret;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+ u8 buf;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 2
+ },{
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = &buf,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1,
+ "Read error, Reg=[0x%02x], Status=%d",
+ reg, ret);
+
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
+ reg, buf);
+
+ return (unsigned int)buf;
+}
+
+int stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int result;
+
+ result = _stb0899_read_reg(state, reg);
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ return result;
+}
+
+u32 _stb0899_read_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset)
+{
+ int status;
+ u32 data;
+ u8 buf[7] = { 0 };
+ u16 tmpaddr;
+
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 2
+ };
+
+ struct i2c_msg msg_r = {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = 4
+ };
+
+ tmpaddr = stb0899_reg_offset & 0xff00;
+ if (!(stb0899_reg_offset & 0x8))
+ tmpaddr = stb0899_reg_offset | 0x20;
+
+ buf_1[0] = GETBYTE(tmpaddr, BYTE1);
+ buf_1[1] = GETBYTE(tmpaddr, BYTE0);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+
+ goto err;
+ }
+
+ /* Dummy */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1)
+ goto err;
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1)
+ goto err;
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+
+ /* Actual */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ goto err;
+ }
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]);
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data);
+
+ return data;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset,
+ u32 stb0899_data)
+{
+ int status;
+
+ /* Base Address Setup */
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 6
+ };
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+ buf_1[2] = GETBYTE(stb0899_data, BYTE0);
+ buf_1[3] = GETBYTE(stb0899_data, BYTE1);
+ buf_1[4] = GETBYTE(stb0899_data, BYTE2);
+ buf_1[5] = GETBYTE(stb0899_data, BYTE3);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+ goto err;
+ }
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ return 0;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count)
+{
+ int status;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 2
+ },{
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = count
+ }
+ };
+
+ status = i2c_transfer(state->i2c, msg, 2);
+ if (status != 2) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n",
+ __func__, reg, count, status);
+ goto err;
+ }
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++) {
+ printk(" %02x", buf[i]);
+ }
+ printk("\n");
+ }
+
+ return 0;
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count)
+{
+ int ret;
+ u8 buf[2 + count];
+ struct i2c_msg i2c_msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2 + count
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ memcpy(&buf[2], data, count);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++)
+ printk(" %02x", data[i]);
+ printk("\n");
+ }
+ ret = i2c_transfer(state->i2c, &i2c_msg, 1);
+
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (ret != 1) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
+ reg, data[0], count, ret);
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data)
+{
+ return stb0899_write_regs(state, reg, &data, 1);
+}
+
+/*
+ * stb0899_get_mclk
+ * Get STB0899 master clock frequency
+ * ExtClk: external clock frequency (Hz)
+ */
+static u32 stb0899_get_mclk(struct stb0899_state *state)
+{
+ u32 mclk = 0, div = 0;
+
+ div = stb0899_read_reg(state, STB0899_NCOARSE);
+ mclk = (div + 1) * state->config->xtal_freq / 6;
+ dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk);
+
+ return mclk;
+}
+
+/*
+ * stb0899_set_mclk
+ * Set STB0899 master Clock frequency
+ * Mclk: demodulator master clock
+ * ExtClk: external clock frequency (Hz)
+ */
+static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 mdiv = 0;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config);
+ mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1;
+ dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv);
+
+ stb0899_write_reg(state, STB0899_NCOARSE, mdiv);
+ internal->master_clk = stb0899_get_mclk(state);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk);
+}
+
+static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable)
+{
+ struct stb0899_config *config = state->config;
+ const struct stb0899_postproc *postproc = config->postproc;
+
+ /* post process event */
+ if (postproc) {
+ if (enable) {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ } else {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ }
+ }
+ return 0;
+}
+
+static void stb0899_release(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+ kfree(state);
+}
+
+/*
+ * stb0899_get_alpha
+ * return: rolloff
+ */
+static int stb0899_get_alpha(struct stb0899_state *state)
+{
+ u8 mode_coeff;
+
+ mode_coeff = stb0899_read_reg(state, STB0899_DEMOD);
+
+ if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1)
+ return 20;
+ else
+ return 35;
+}
+
+/*
+ * stb0899_init_calc
+ */
+static void stb0899_init_calc(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ int master_clk;
+ u8 agc[2];
+ u8 agc1cn;
+ u32 reg;
+
+ /* Read registers (in burst mode) */
+ agc1cn = stb0899_read_reg(state, STB0899_AGC1CN);
+ stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */
+
+ /* Initial calculations */
+ master_clk = stb0899_get_mclk(state);
+ internal->t_agc1 = 0;
+ internal->t_agc2 = 0;
+ internal->master_clk = master_clk;
+ internal->mclk = master_clk / 65536L;
+ internal->rolloff = stb0899_get_alpha(state);
+
+ /* DVBS2 Initial calculations */
+ /* Set AGC value to the middle */
+ internal->agc_gain = 8154;
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA);
+ internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg);
+
+ internal->center_freq = 0;
+ internal->av_frame_coarse = 10;
+ internal->av_frame_fine = 20;
+ internal->step_size = 2;
+/*
+ if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO))
+ pParams->IQLocked = 0;
+ else
+ pParams->IQLocked = 1;
+*/
+}
+
+static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (1) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (!STB0899_GETFIELD(FIFOFULL, reg))
+ break;
+ if ((jiffies - start) > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out !!");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, i;
+
+ if (cmd->msg_len > 8)
+ return -EINVAL;
+
+ /* enable FIFO precharge */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ for (i = 0; i < cmd->msg_len; i++) {
+ /* wait for FIFO empty */
+ if (stb0899_wait_diseqc_fifo_empty(state, 10) < 0)
+ return -ETIMEDOUT;
+
+ stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ return 0;
+}
+
+static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(RXEND, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, length = 0, i;
+ int result;
+
+ if (stb0899_wait_diseqc_rxidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (STB0899_GETFIELD(RXEND, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST1);
+ length = STB0899_GETFIELD(FIFOBYTENBR, reg);
+
+ if (length > sizeof (reply->msg)) {
+ result = -EOVERFLOW;
+ goto exit;
+ }
+ reply->msg_len = length;
+
+ /* extract data */
+ for (i = 0; i < length; i++)
+ reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO);
+ }
+
+ return 0;
+exit:
+
+ return result;
+}
+
+static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(TXIDLE, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+ return 0;
+}
+
+static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, old_state;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ old_state = reg;
+ /* set to burst mode */
+ STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x02);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ switch (burst) {
+ case SEC_MINI_A:
+ /* unmodulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0x00);
+ break;
+ case SEC_MINI_B:
+ /* modulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0xff);
+ break;
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ /* restore state */
+ stb0899_write_reg(state, STB0899_DISCNTRL1, old_state);
+
+ return 0;
+}
+
+static int stb0899_diseqc_init(struct stb0899_state *state)
+{
+ struct dvb_diseqc_master_cmd tx_data;
+/*
+ struct dvb_diseqc_slave_reply rx_data;
+*/
+ u8 f22_tx, f22_rx, reg;
+
+ u32 mclk, tx_freq = 22000;/* count = 0, i; */
+ tx_data.msg[0] = 0xe2;
+ tx_data.msg_len = 3;
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL2);
+ STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL2, reg);
+
+ /* disable Tx spy */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ mclk = stb0899_get_mclk(state);
+ f22_tx = mclk / (tx_freq * 32);
+ stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */
+ state->rx_freq = 20000;
+ f22_rx = mclk / (state->rx_freq * 32);
+
+ return 0;
+}
+
+static int stb0899_sleep(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+/*
+ u8 reg;
+*/
+ dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+
+ return 0;
+}
+
+static int stb0899_wakeup(struct dvb_frontend *fe)
+{
+ int rc;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI)))
+ return rc;
+ /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00)))
+ return rc;
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00)))
+ return rc;
+
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1);
+
+ return 0;
+}
+
+static int stb0899_init(struct dvb_frontend *fe)
+{
+ int i;
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_config *config = state->config;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... ");
+
+ /* init device */
+ dprintk(state->verbose, FE_DEBUG, 1, "init device");
+ for (i = 0; config->init_dev[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod");
+ /* init S2 demod */
+ for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2DEMOD,
+ config->init_s2_demod[i].base_address,
+ config->init_s2_demod[i].offset,
+ config->init_s2_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod");
+ /* init S1 demod */
+ for (i = 0; config->init_s1_demod[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC");
+ /* init S2 fec */
+ for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2FEC,
+ config->init_s2_fec[i].base_address,
+ config->init_s2_fec[i].offset,
+ config->init_s2_fec[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init TST");
+ /* init test */
+ for (i = 0; config->init_tst[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data);
+
+ stb0899_init_calc(state);
+ stb0899_diseqc_init(state);
+
+ return 0;
+}
+
+static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val)
+{
+ int res = 0;
+ int min = 0, med;
+
+ if (val < tab[min].read)
+ res = tab[min].real;
+ else if (val >= tab[max].read)
+ res = tab[max].real;
+ else {
+ while ((max - min) > 1) {
+ med = (max + min) / 2;
+ if (val >= tab[min].read && val < tab[med].read)
+ max = med;
+ else
+ min = med;
+ }
+ res = ((val - tab[min].read) *
+ (tab[max].real - tab[min].real) /
+ (tab[max].read - tab[min].read)) +
+ tab[min].real;
+ }
+
+ return res;
+}
+
+static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ int val;
+ u32 reg;
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_AGCIQIN);
+ val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val);
+ *strength += 750;
+ dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm",
+ val & 0xff, *strength);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN);
+ val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
+ *strength += 750;
+ dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
+ val & 0x3fff, *strength);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ unsigned int val, quant, quantn = -1, est, estn = -1;
+ u8 buf[2];
+ u32 reg;
+
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ stb0899_read_regs(state, STB0899_NIRM, buf, 2);
+ val = MAKEWORD16(buf[0], buf[1]);
+
+ *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val);
+ dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n",
+ buf[0], buf[1], val, *snr);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
+ quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ est = STB0899_GETFIELD(ESN0_EST, reg);
+ if (est == 1)
+ val = 301; /* C/N = 30.1 dB */
+ else if (est == 2)
+ val = 270; /* C/N = 27.0 dB */
+ else {
+ /* quantn = 100 * log(quant^2) */
+ quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100);
+ /* estn = 100 * log(est) */
+ estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est);
+ /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */
+ val = (quantn - estn) / 10;
+ }
+ *snr = val;
+ dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm",
+ quant, quantn, est, estn, val);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+ u8 reg;
+ *status = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS");
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
+ *status |= FE_HAS_CARRIER | FE_HAS_LOCK;
+
+ reg = stb0899_read_reg(state, STB0899_PLPARM);
+ if (STB0899_GETFIELD(VITCURPUN, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC");
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2");
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
+ if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) {
+ *status |= FE_HAS_CARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER");
+
+ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
+ if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) {
+ *status |= FE_HAS_LOCK;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK");
+
+ }
+ if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) {
+ *status |= FE_HAS_VITERBI;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI");
+ }
+ if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) {
+ *status |= FE_HAS_SYNC;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * stb0899_get_error
+ * viterbi error for DVB-S/DSS
+ * packet error for DVB-S2
+ * Bit Error Rate or Packet Error Rate * 10 ^ 7
+ */
+static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 lsb, msb;
+ u32 i;
+
+ *ber = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ /* average 5 BER values */
+ for (i = 0; i < 5; i++) {
+ msleep(100);
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber += MAKEWORD16(msb, lsb);
+ }
+ *ber /= 5;
+ /* Viterbi Check */
+ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
+ /* Error Rate */
+ *ber *= 9766;
+ /* ber = ber * 10 ^ 7 */
+ *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ *ber /= 8;
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ /* Average 5 PER values */
+ for (i = 0; i < 5; i++) {
+ msleep(100);
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber += MAKEWORD16(msb, lsb);
+ }
+ /* ber = ber * 10 ^ 7 */
+ *ber *= 10000000;
+ *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00);
+ break;
+ case SEC_VOLTAGE_18:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ case SEC_VOLTAGE_OFF:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 div, reg;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ div = (internal->master_clk / 100) / 5632;
+ div = (div + 5) / 10;
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66);
+ reg = stb0899_read_reg(state, STB0899_ACRPRESC);
+ STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03);
+ stb0899_write_reg(state, STB0899_ACRPRESC, reg);
+ stb0899_write_reg(state, STB0899_ACRDIV1, div);
+ break;
+ case SEC_TONE_OFF:
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int i2c_stat;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT);
+ if (i2c_stat < 0)
+ goto err;
+
+ if (enable) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ...");
+ i2c_stat |= STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ } else {
+ dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ...");
+ i2c_stat &= ~STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed");
+ return -EREMOTEIO;
+}
+
+
+static inline void CONVERT32(u32 x, char *str)
+{
+ *str++ = (x >> 24) & 0xff;
+ *str++ = (x >> 16) & 0xff;
+ *str++ = (x >> 8) & 0xff;
+ *str++ = (x >> 0) & 0xff;
+ *str = '\0';
+}
+
+int stb0899_get_dev_id(struct stb0899_state *state)
+{
+ u8 chip_id, release;
+ u16 id;
+ u32 demod_ver = 0, fec_ver = 0;
+ char demod_str[5] = { 0 };
+ char fec_str[5] = { 0 };
+
+ id = stb0899_read_reg(state, STB0899_DEV_ID);
+ dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id);
+ chip_id = STB0899_GETFIELD(CHIP_ID, id);
+ release = STB0899_GETFIELD(CHIP_REL, id);
+
+ dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]",
+ chip_id, release);
+
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str);
+
+ demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID);
+ dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
+ fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
+ if (! (chip_id > 0)) {
+ dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
+
+ return -ENODEV;
+ }
+ dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver);
+
+ return 0;
+}
+
+static void stb0899_set_delivery(struct stb0899_state *state)
+{
+ u8 reg;
+ u8 stop_clk[2];
+
+ stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1);
+ stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S");
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x40);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x12);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ case SYS_DVBS2:
+ /* FECM/Viterbi OFF */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x42);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x40);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x02);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0);
+ break;
+ case SYS_DSS:
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xa1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x61);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ break;
+ }
+ STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0);
+ stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2);
+}
+
+/*
+ * stb0899_set_iterations
+ * set the LDPC iteration scale function
+ */
+static void stb0899_set_iterations(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ s32 iter_scale;
+ u32 reg;
+
+ iter_scale = 17 * (internal->master_clk / 1000);
+ iter_scale += 410000;
+ iter_scale /= (internal->srate / 1000000);
+ iter_scale /= 1000;
+
+ if (iter_scale > config->ldpc_max_iter)
+ iter_scale = config->ldpc_max_iter;
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, MAX_ITER);
+ STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
+}
+
+static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_params *i_params = &state->params;
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+ struct dtv_frontend_properties *props = &fe->dtv_property_cache;
+
+ u32 SearchRange, gain;
+
+ i_params->freq = p->frequency;
+ i_params->srate = p->u.qpsk.symbol_rate;
+ state->delsys = props->delivery_system;
+ dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys);
+
+ SearchRange = 10000000;
+ dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate);
+ /* checking Search Range is meaningless for a fixed 3 Mhz */
+ if (INRANGE(i_params->srate, 1000000, 45000000)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE");
+ stb0899_set_delivery(state);
+
+ if (state->config->tuner_set_rfsiggain) {
+ if (internal->srate > 15000000)
+ gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */
+ else if (internal->srate > 5000000)
+ gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */
+ else
+ gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */
+ state->config->tuner_set_rfsiggain(fe, gain);
+ }
+
+ if (i_params->srate <= 5000000)
+ stb0899_set_mclk(state, config->lo_clk);
+ else
+ stb0899_set_mclk(state, config->hi_clk);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system");
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ /*
+ * search = user search range +
+ * 500Khz +
+ * 2 * Tuner_step_size +
+ * 10% of the symbol rate
+ */
+ internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5);
+ internal->derot_percent = 30;
+
+ /* What to do for tuners having no bandwidth setup ? */
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10);
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ /* Set DVB-S1 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo ..");
+ if (stb0899_dvbs_algo(state) == RANGEOK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status);
+// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ case SYS_DVBS2:
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ internal->srch_range = SearchRange;
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange));
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+// pParams->SpectralInv = pSearch->IQ_Inversion;
+
+ /* Set DVB-S2 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c);
+
+ /* Set IterScale =f(MCLK,SYMB) */
+ stb0899_set_iterations(state);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo ..");
+ if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S2 LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ return DVBFE_ALGO_SEARCH_INVALID;
+ }
+ }
+
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+/*
+ * stb0899_track
+ * periodically check the signal level against a specified
+ * threshold level and perform derotator centering.
+ * called once we have a lock from a succesful search
+ * event.
+ *
+ * Will be called periodically called to maintain the
+ * lock.
+ *
+ * Will be used to get parameters as well as info from
+ * the decoded baseband header
+ *
+ * Once a new lock has established, the internal state
+ * frequency (internal->freq) is updated
+ */
+static int stb0899_track(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ return 0;
+}
+
+static int stb0899_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Get params");
+ p->u.qpsk.symbol_rate = internal->srate;
+
+ return 0;
+}
+
+static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static struct dvb_frontend_ops stb0899_ops = {
+
+ .info = {
+ .name = "STB0899 Multistandard",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 5000000,
+ .symbol_rate_max = 45000000,
+
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = stb0899_release,
+ .init = stb0899_init,
+ .sleep = stb0899_sleep,
+// .wakeup = stb0899_wakeup,
+
+ .i2c_gate_ctrl = stb0899_i2c_gate_ctrl,
+
+ .get_frontend_algo = stb0899_frontend_algo,
+ .search = stb0899_search,
+ .track = stb0899_track,
+ .get_frontend = stb0899_get_frontend,
+
+
+ .read_status = stb0899_read_status,
+ .read_snr = stb0899_read_snr,
+ .read_signal_strength = stb0899_read_signal_strength,
+ .read_ber = stb0899_read_ber,
+
+ .set_voltage = stb0899_set_voltage,
+ .set_tone = stb0899_set_tone,
+
+ .diseqc_send_master_cmd = stb0899_send_diseqc_msg,
+ .diseqc_recv_slave_reply = stb0899_recv_slave_reply,
+ .diseqc_send_burst = stb0899_send_diseqc_burst,
+};
+
+struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c)
+{
+ struct stb0899_state *state = NULL;
+ enum stb0899_inversion inversion;
+
+ state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ inversion = config->inversion;
+ state->verbose = &verbose;
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend.ops = stb0899_ops;
+ state->frontend.demodulator_priv = state;
+ state->internal.inversion = inversion;
+
+ stb0899_wakeup(&state->frontend);
+ if (stb0899_get_dev_id(state) == -ENODEV) {
+ printk("%s: Exiting .. !\n", __func__);
+ goto error;
+ }
+
+ printk("%s: Attaching STB0899 \n", __func__);
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stb0899_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STB0899 Multi-Std frontend");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb0899_drv.h b/drivers/media/dvb/frontends/stb0899_drv.h
new file mode 100644
index 000000000000..98b200ce0c34
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_drv.h
@@ -0,0 +1,162 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 __STB0899_DRV_H
+#define __STB0899_DRV_H
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dvb_frontend.h"
+
+#define STB0899_TSMODE_SERIAL 1
+#define STB0899_CLKPOL_FALLING 2
+#define STB0899_CLKNULL_PARITY 3
+#define STB0899_SYNC_FORCED 4
+#define STB0899_FECMODE_DSS 5
+
+struct stb0899_s1_reg {
+ u16 address;
+ u8 data;
+};
+
+struct stb0899_s2_reg {
+ u16 offset;
+ u32 base_address;
+ u32 data;
+};
+
+enum stb0899_inversion {
+ IQ_SWAP_OFF = 0,
+ IQ_SWAP_ON,
+ IQ_SWAP_AUTO
+};
+
+#define STB0899_GPIO00 0xf140
+#define STB0899_GPIO01 0xf141
+#define STB0899_GPIO02 0xf142
+#define STB0899_GPIO03 0xf143
+#define STB0899_GPIO04 0xf144
+#define STB0899_GPIO05 0xf145
+#define STB0899_GPIO06 0xf146
+#define STB0899_GPIO07 0xf147
+#define STB0899_GPIO08 0xf148
+#define STB0899_GPIO09 0xf149
+#define STB0899_GPIO10 0xf14a
+#define STB0899_GPIO11 0xf14b
+#define STB0899_GPIO12 0xf14c
+#define STB0899_GPIO13 0xf14d
+#define STB0899_GPIO14 0xf14e
+#define STB0899_GPIO15 0xf14f
+#define STB0899_GPIO16 0xf150
+#define STB0899_GPIO17 0xf151
+#define STB0899_GPIO18 0xf152
+#define STB0899_GPIO19 0xf153
+#define STB0899_GPIO20 0xf154
+
+#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */
+#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */
+
+#define STB0899_POSTPROC_GPIO_POWER 0x00
+#define STB0899_POSTPROC_GPIO_LOCK 0x01
+
+/*
+ * Post process output configuration control
+ * 1. POWER ON/OFF (index 0)
+ * 2. FE_HAS_LOCK/LOCK_LOSS (index 1)
+ *
+ * @gpio = one of the above listed GPIO's
+ * @level = output state: pulled up or low
+ */
+struct stb0899_postproc {
+ u16 gpio;
+ u8 level;
+};
+
+struct stb0899_config {
+ const struct stb0899_s1_reg *init_dev;
+ const struct stb0899_s2_reg *init_s2_demod;
+ const struct stb0899_s1_reg *init_s1_demod;
+ const struct stb0899_s2_reg *init_s2_fec;
+ const struct stb0899_s1_reg *init_tst;
+
+ const struct stb0899_postproc *postproc;
+
+ enum stb0899_inversion inversion;
+
+ u32 xtal_freq;
+
+ u8 demod_address;
+ u8 ts_output_mode;
+ u8 block_sync_mode;
+ u8 ts_pfbit_toggle;
+
+ u8 clock_polarity;
+ u8 data_clk_parity;
+ u8 fec_mode;
+ u8 data_output_ctl;
+ u8 data_fifo_mode;
+ u8 out_rate_comp;
+ u8 i2c_repeater;
+// int inversion;
+ int lo_clk;
+ int hi_clk;
+
+ u32 esno_ave;
+ u32 esno_quant;
+ u32 avframes_coarse;
+ u32 avframes_fine;
+ u32 miss_threshold;
+ u32 uwp_threshold_acq;
+ u32 uwp_threshold_track;
+ u32 uwp_threshold_sof;
+ u32 sof_search_timeout;
+
+ u32 btr_nco_bits;
+ u32 btr_gain_shift_offset;
+ u32 crl_nco_bits;
+ u32 ldpc_max_iter;
+
+ int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency);
+ int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+ int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain);
+};
+
+#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_STB0899
+
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb0899_priv.h b/drivers/media/dvb/frontends/stb0899_priv.h
new file mode 100644
index 000000000000..24619e3689db
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_priv.h
@@ -0,0 +1,267 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 __STB0899_PRIV_H
+#define __STB0899_PRIV_H
+
+#include "dvb_frontend.h"
+#include "stb0899_drv.h"
+
+#define FE_ERROR 0
+#define FE_NOTICE 1
+#define FE_INFO 2
+#define FE_DEBUG 3
+#define FE_DEBUGREG 4
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((*x > FE_ERROR) && (*x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_NOTICE) && (*x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_INFO) && (*x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((*x > FE_DEBUG) && (*x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (*x > y) \
+ printk(format, ##arg); \
+ } \
+} while(0)
+
+#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \
+ ((y <= val) && (val <= x)) ? 1 : 0)
+
+#define BYTE0 0
+#define BYTE1 8
+#define BYTE2 16
+#define BYTE3 24
+
+#define GETBYTE(x, y) (((x) >> (y)) & 0xff)
+#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#define MAKEWORD16(a, b) (((a) << 8) | (b))
+
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+#define MAX(x, y) ((x) >= (y) ? (x) : (y))
+#define ABS(x) ((x) >= 0 ? (x) : -(x))
+
+#define LSB(x) ((x & 0xff))
+#define MSB(y) ((y >> 8) & 0xff)
+
+
+#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1))
+
+
+#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \
+ offset))) | ((val & \
+ ((1 << width) - 1)) << offset)
+
+#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\
+ STB0899_OFFST_##bitf))) | \
+ (val << STB0899_OFFST_##bitf))
+
+
+enum stb0899_status {
+ NOAGC1 = 0,
+ AGC1OK,
+ NOTIMING,
+ ANALOGCARRIER,
+ TIMINGOK,
+ NOAGC2,
+ AGC2OK,
+ NOCARRIER,
+ CARRIEROK,
+ NODATA,
+ FALSELOCK,
+ DATAOK,
+ OUTOFRANGE,
+ RANGEOK,
+ DVBS2_DEMOD_LOCK,
+ DVBS2_DEMOD_NOLOCK,
+ DVBS2_FEC_LOCK,
+ DVBS2_FEC_NOLOCK
+};
+
+enum stb0899_modcod {
+ STB0899_DUMMY_PLF,
+ STB0899_QPSK_14,
+ STB0899_QPSK_13,
+ STB0899_QPSK_25,
+ STB0899_QPSK_12,
+ STB0899_QPSK_35,
+ STB0899_QPSK_23,
+ STB0899_QPSK_34,
+ STB0899_QPSK_45,
+ STB0899_QPSK_56,
+ STB0899_QPSK_89,
+ STB0899_QPSK_910,
+ STB0899_8PSK_35,
+ STB0899_8PSK_23,
+ STB0899_8PSK_34,
+ STB0899_8PSK_56,
+ STB0899_8PSK_89,
+ STB0899_8PSK_910,
+ STB0899_16APSK_23,
+ STB0899_16APSK_34,
+ STB0899_16APSK_45,
+ STB0899_16APSK_56,
+ STB0899_16APSK_89,
+ STB0899_16APSK_910,
+ STB0899_32APSK_34,
+ STB0899_32APSK_45,
+ STB0899_32APSK_56,
+ STB0899_32APSK_89,
+ STB0899_32APSK_910
+};
+
+enum stb0899_frame {
+ STB0899_LONG_FRAME,
+ STB0899_SHORT_FRAME
+};
+
+enum stb0899_alpha {
+ RRC_20,
+ RRC_25,
+ RRC_35
+};
+
+struct stb0899_tab {
+ s32 real;
+ s32 read;
+};
+
+enum stb0899_fec {
+ STB0899_FEC_1_2 = 13,
+ STB0899_FEC_2_3 = 18,
+ STB0899_FEC_3_4 = 21,
+ STB0899_FEC_5_6 = 24,
+ STB0899_FEC_6_7 = 25,
+ STB0899_FEC_7_8 = 26
+};
+
+struct stb0899_params {
+ u32 freq; /* Frequency */
+ u32 srate; /* Symbol rate */
+ enum fe_code_rate fecrate;
+};
+
+struct stb0899_internal {
+ u32 master_clk;
+ u32 freq; /* Demod internal Frequency */
+ u32 srate; /* Demod internal Symbol rate */
+ enum stb0899_fec fecrate; /* Demod internal FEC rate */
+ u32 srch_range; /* Demod internal Search Range */
+ u32 sub_range; /* Demod current sub range (Hz) */
+ u32 tuner_step; /* Tuner step (Hz) */
+ u32 tuner_offst; /* Relative offset to carrier (Hz) */
+ u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */
+
+ s32 mclk; /* Masterclock Divider factor (binary) */
+ s32 rolloff; /* Current RollOff of the filter (x100) */
+
+ s16 derot_freq; /* Current derotator frequency (Hz) */
+ s16 derot_percent;
+
+ s16 direction; /* Current derotator search direction */
+ s16 derot_step; /* Derotator step (binary value) */
+ s16 t_derot; /* Derotator time constant (ms) */
+ s16 t_data; /* Data recovery time constant (ms) */
+ s16 sub_dir; /* Direction of the next sub range */
+
+ s16 t_agc1; /* Agc1 time constant (ms) */
+ s16 t_agc2; /* Agc2 time constant (ms) */
+
+ u32 lock; /* Demod internal lock state */
+ enum stb0899_status status; /* Demod internal status */
+
+ /* DVB-S2 */
+ s32 agc_gain; /* RF AGC Gain */
+ s32 center_freq; /* Nominal carrier frequency */
+ s32 av_frame_coarse; /* Coarse carrier freq search frames */
+ s32 av_frame_fine; /* Fine carrier freq search frames */
+
+ s16 step_size; /* Carrier frequency search step size */
+
+ enum stb0899_alpha rrc_alpha;
+ enum stb0899_inversion inversion;
+ enum stb0899_modcod modcod;
+ u8 pilots; /* Pilots found */
+
+ enum stb0899_frame frame_length;
+ u8 v_status; /* VSTATUS */
+ u8 err_ctrl; /* ERRCTRLn */
+};
+
+struct stb0899_state {
+ struct i2c_adapter *i2c;
+ struct stb0899_config *config;
+ struct dvb_frontend frontend;
+
+ u32 *verbose; /* Cached module verbosity level */
+
+ struct stb0899_internal internal; /* Device internal parameters */
+
+ /* cached params from API */
+ enum fe_delivery_system delsys;
+ struct stb0899_params params;
+
+ u32 rx_freq; /* DiSEqC 2.0 receiver freq */
+ struct mutex search_lock;
+};
+/* stb0899.c */
+extern int stb0899_read_reg(struct stb0899_state *state,
+ unsigned int reg);
+
+extern u32 _stb0899_read_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset);
+
+extern int stb0899_read_regs(struct stb0899_state *state,
+ unsigned int reg, u8 *buf,
+ u32 count);
+
+extern int stb0899_write_regs(struct stb0899_state *state,
+ unsigned int reg, u8 *data,
+ u32 count);
+
+extern int stb0899_write_reg(struct stb0899_state *state,
+ unsigned int reg,
+ u8 data);
+
+extern int stb0899_write_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset,
+ u32 stb0899_data);
+
+extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+
+
+#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG))
+//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA))
+
+/* stb0899_algo.c */
+extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state);
+extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state);
+extern long stb0899_carr_width(struct stb0899_state *state);
+
+#endif //__STB0899_PRIV_H
diff --git a/drivers/media/dvb/frontends/stb0899_reg.h b/drivers/media/dvb/frontends/stb0899_reg.h
new file mode 100644
index 000000000000..ba1ed56304a0
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb0899_reg.h
@@ -0,0 +1,2027 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 __STB0899_REG_H
+#define __STB0899_REG_H
+
+/* S1 */
+#define STB0899_DEV_ID 0xf000
+#define STB0899_CHIP_ID (0x0f << 4)
+#define STB0899_OFFST_CHIP_ID 4
+#define STB0899_WIDTH_CHIP_ID 4
+#define STB0899_CHIP_REL (0x0f << 0)
+#define STB0899_OFFST_CHIP_REL 0
+#define STB0899_WIDTH_CHIP_REL 4
+
+#define STB0899_DEMOD 0xf40e
+#define STB0899_MODECOEFF (0x01 << 0)
+#define STB0899_OFFST_MODECOEFF 0
+#define STB0899_WIDTH_MODECOEFF 1
+
+#define STB0899_RCOMPC 0xf410
+#define STB0899_AGC1CN 0xf412
+#define STB0899_AGC1REF 0xf413
+#define STB0899_RTC 0xf417
+#define STB0899_TMGCFG 0xf418
+#define STB0899_AGC2REF 0xf419
+#define STB0899_TLSR 0xf41a
+
+#define STB0899_CFD 0xf41b
+#define STB0899_CFD_ON (0x01 << 7)
+#define STB0899_OFFST_CFD_ON 7
+#define STB0899_WIDTH_CFD_ON 1
+
+#define STB0899_ACLC 0xf41c
+
+#define STB0899_BCLC 0xf41d
+#define STB0899_OFFST_ALGO 6
+#define STB0899_WIDTH_ALGO_QPSK2 2
+#define STB0899_ALGO_QPSK2 (2 << 6)
+#define STB0899_ALGO_QPSK1 (1 << 6)
+#define STB0899_ALGO_BPSK (0 << 6)
+#define STB0899_OFFST_BETA 0
+#define STB0899_WIDTH_BETA 6
+
+#define STB0899_EQON 0xf41e
+#define STB0899_LDT 0xf41f
+#define STB0899_LDT2 0xf420
+#define STB0899_EQUALREF 0xf425
+#define STB0899_TMGRAMP 0xf426
+#define STB0899_TMGTHD 0xf427
+#define STB0899_IDCCOMP 0xf428
+#define STB0899_QDCCOMP 0xf429
+#define STB0899_POWERI 0xf42a
+#define STB0899_POWERQ 0xf42b
+#define STB0899_RCOMP 0xf42c
+
+#define STB0899_AGCIQIN 0xf42e
+#define STB0899_AGCIQVALUE (0xff << 0)
+#define STB0899_OFFST_AGCIQVALUE 0
+#define STB0899_WIDTH_AGCIQVALUE 8
+
+#define STB0899_AGC2I1 0xf436
+#define STB0899_AGC2I2 0xf437
+
+#define STB0899_TLIR 0xf438
+#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0)
+#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0
+#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8
+
+#define STB0899_RTF 0xf439
+#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0)
+#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0
+#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8
+
+#define STB0899_DSTATUS 0xf43a
+#define STB0899_CARRIER_FOUND (0x01 << 7)
+#define STB0899_OFFST_CARRIER_FOUND 7
+#define STB0899_WIDTH_CARRIER_FOUND 1
+#define STB0899_TMG_LOCK (0x01 << 6)
+#define STB0899_OFFST_TMG_LOCK 6
+#define STB0899_WIDTH_TMG_LOCK 1
+#define STB0899_DEMOD_LOCK (0x01 << 5)
+#define STB0899_OFFST_DEMOD_LOCK 5
+#define STB0899_WIDTH_DEMOD_LOCK 1
+#define STB0899_TMG_AUTO (0x01 << 4)
+#define STB0899_OFFST_TMG_AUTO 4
+#define STB0899_WIDTH_TMG_AUTO 1
+#define STB0899_END_MAIN (0x01 << 3)
+#define STB0899_OFFST_END_MAIN 3
+#define STB0899_WIDTH_END_MAIN 1
+
+#define STB0899_LDI 0xf43b
+#define STB0899_OFFST_LDI 0
+#define STB0899_WIDTH_LDI 8
+
+#define STB0899_CFRM 0xf43e
+#define STB0899_OFFST_CFRM 0
+#define STB0899_WIDTH_CFRM 8
+
+#define STB0899_CFRL 0xf43f
+#define STB0899_OFFST_CFRL 0
+#define STB0899_WIDTH_CFRL 8
+
+#define STB0899_NIRM 0xf440
+#define STB0899_OFFST_NIRM 0
+#define STB0899_WIDTH_NIRM 8
+
+#define STB0899_NIRL 0xf441
+#define STB0899_OFFST_NIRL 0
+#define STB0899_WIDTH_NIRL 8
+
+#define STB0899_ISYMB 0xf444
+#define STB0899_QSYMB 0xf445
+
+#define STB0899_SFRH 0xf446
+#define STB0899_OFFST_SFRH 0
+#define STB0899_WIDTH_SFRH 8
+
+#define STB0899_SFRM 0xf447
+#define STB0899_OFFST_SFRM 0
+#define STB0899_WIDTH_SFRM 8
+
+#define STB0899_SFRL 0xf448
+#define STB0899_OFFST_SFRL 4
+#define STB0899_WIDTH_SFRL 4
+
+#define STB0899_SFRUPH 0xf44c
+#define STB0899_SFRUPM 0xf44d
+#define STB0899_SFRUPL 0xf44e
+
+#define STB0899_EQUAI1 0xf4e0
+#define STB0899_EQUAQ1 0xf4e1
+#define STB0899_EQUAI2 0xf4e2
+#define STB0899_EQUAQ2 0xf4e3
+#define STB0899_EQUAI3 0xf4e4
+#define STB0899_EQUAQ3 0xf4e5
+#define STB0899_EQUAI4 0xf4e6
+#define STB0899_EQUAQ4 0xf4e7
+#define STB0899_EQUAI5 0xf4e8
+#define STB0899_EQUAQ5 0xf4e9
+
+#define STB0899_DSTATUS2 0xf50c
+#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7)
+#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7
+#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1
+#define STB0899_DS2_END_MAINLOOP (0x01 << 6)
+#define STB0899_OFFST_DS2_END_MAINLOOP 6
+#define STB0899_WIDTH_DS2_END_MAINLOOP 1
+#define STB0899_DS2_CFSYNC (0x01 << 5)
+#define STB0899_OFFST_DS2_CFSYNC 5
+#define STB0899_WIDTH_DS2_CFSYNC 1
+#define STB0899_DS2_TMGLOCK (0x01 << 4)
+#define STB0899_OFFST_DS2_TMGLOCK 4
+#define STB0899_WIDTH_DS2_TMGLOCK 1
+#define STB0899_DS2_DEMODWAIT (0x01 << 3)
+#define STB0899_OFFST_DS2_DEMODWAIT 3
+#define STB0899_WIDTH_DS2_DEMODWAIT 1
+#define STB0899_DS2_FECON (0x01 << 1)
+#define STB0899_OFFST_DS2_FECON 1
+#define STB0899_WIDTH_DS2_FECON 1
+
+/* S1 FEC */
+#define STB0899_VSTATUS 0xf50d
+#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7)
+#define STB0899_OFFST_VSTATUS_VITERBI_ON 7
+#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1
+#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6)
+#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6
+#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1
+#define STB0899_VSTATUS_PRFVIT (0x01 << 4)
+#define STB0899_OFFST_VSTATUS_PRFVIT 4
+#define STB0899_WIDTH_VSTATUS_PRFVIT 1
+#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3)
+#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3
+#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1
+
+#define STB0899_VERROR 0xf50f
+
+#define STB0899_IQSWAP 0xf523
+#define STB0899_SYM (0x01 << 3)
+#define STB0899_OFFST_SYM 3
+#define STB0899_WIDTH_SYM 1
+
+#define STB0899_FECAUTO1 0xf530
+#define STB0899_DSSSRCH (0x01 << 3)
+#define STB0899_OFFST_DSSSRCH 3
+#define STB0899_WIDTH_DSSSRCH 1
+#define STB0899_SYMSRCH (0x01 << 2)
+#define STB0899_OFFST_SYMSRCH 2
+#define STB0899_WIDTH_SYMSRCH 1
+#define STB0899_QPSKSRCH (0x01 << 1)
+#define STB0899_OFFST_QPSKSRCH 1
+#define STB0899_WIDTH_QPSKSRCH 1
+#define STB0899_BPSKSRCH (0x01 << 0)
+#define STB0899_OFFST_BPSKSRCH 0
+#define STB0899_WIDTH_BPSKSRCH 1
+
+#define STB0899_FECM 0xf533
+#define STB0899_FECM_NOT_DVB (0x01 << 7)
+#define STB0899_OFFST_FECM_NOT_DVB 7
+#define STB0899_WIDTH_FECM_NOT_DVB 1
+#define STB0899_FECM_RSVD1 (0x07 << 4)
+#define STB0899_OFFST_FECM_RSVD1 4
+#define STB0899_WIDTH_FECM_RSVD1 3
+#define STB0899_FECM_VITERBI_ON (0x01 << 3)
+#define STB0899_OFFST_FECM_VITERBI_ON 3
+#define STB0899_WIDTH_FECM_VITERBI_ON 1
+#define STB0899_FECM_RSVD0 (0x01 << 2)
+#define STB0899_OFFST_FECM_RSVD0 2
+#define STB0899_WIDTH_FECM_RSVD0 1
+#define STB0899_FECM_SYNCDIS (0x01 << 1)
+#define STB0899_OFFST_FECM_SYNCDIS 1
+#define STB0899_WIDTH_FECM_SYNCDIS 1
+#define STB0899_FECM_SYMI (0x01 << 0)
+#define STB0899_OFFST_FECM_SYMI 0
+#define STB0899_WIDTH_FECM_SYMI 1
+
+#define STB0899_VTH12 0xf534
+#define STB0899_VTH23 0xf535
+#define STB0899_VTH34 0xf536
+#define STB0899_VTH56 0xf537
+#define STB0899_VTH67 0xf538
+#define STB0899_VTH78 0xf539
+
+#define STB0899_PRVIT 0xf53c
+#define STB0899_PR_7_8 (0x01 << 5)
+#define STB0899_OFFST_PR_7_8 5
+#define STB0899_WIDTH_PR_7_8 1
+#define STB0899_PR_6_7 (0x01 << 4)
+#define STB0899_OFFST_PR_6_7 4
+#define STB0899_WIDTH_PR_6_7 1
+#define STB0899_PR_5_6 (0x01 << 3)
+#define STB0899_OFFST_PR_5_6 3
+#define STB0899_WIDTH_PR_5_6 1
+#define STB0899_PR_3_4 (0x01 << 2)
+#define STB0899_OFFST_PR_3_4 2
+#define STB0899_WIDTH_PR_3_4 1
+#define STB0899_PR_2_3 (0x01 << 1)
+#define STB0899_OFFST_PR_2_3 1
+#define STB0899_WIDTH_PR_2_3 1
+#define STB0899_PR_1_2 (0x01 << 0)
+#define STB0899_OFFST_PR_1_2 0
+#define STB0899_WIDTH_PR_1_2 1
+
+#define STB0899_VITSYNC 0xf53d
+#define STB0899_AM (0x01 << 7)
+#define STB0899_OFFST_AM 7
+#define STB0899_WIDTH_AM 1
+#define STB0899_FREEZE (0x01 << 6)
+#define STB0899_OFFST_FREEZE 6
+#define STB0899_WIDTH_FREEZE 1
+#define STB0899_SN_65536 (0x03 << 4)
+#define STB0899_OFFST_SN_65536 4
+#define STB0899_WIDTH_SN_65536 2
+#define STB0899_SN_16384 (0x01 << 5)
+#define STB0899_OFFST_SN_16384 5
+#define STB0899_WIDTH_SN_16384 1
+#define STB0899_SN_4096 (0x01 << 4)
+#define STB0899_OFFST_SN_4096 4
+#define STB0899_WIDTH_SN_4096 1
+#define STB0899_SN_1024 (0x00 << 4)
+#define STB0899_OFFST_SN_1024 4
+#define STB0899_WIDTH_SN_1024 0
+#define STB0899_TO_128 (0x03 << 2)
+#define STB0899_OFFST_TO_128 2
+#define STB0899_WIDTH_TO_128 2
+#define STB0899_TO_64 (0x01 << 3)
+#define STB0899_OFFST_TO_64 3
+#define STB0899_WIDTH_TO_64 1
+#define STB0899_TO_32 (0x01 << 2)
+#define STB0899_OFFST_TO_32 2
+#define STB0899_WIDTH_TO_32 1
+#define STB0899_TO_16 (0x00 << 2)
+#define STB0899_OFFST_TO_16 2
+#define STB0899_WIDTH_TO_16 0
+#define STB0899_HYST_128 (0x03 << 1)
+#define STB0899_OFFST_HYST_128 1
+#define STB0899_WIDTH_HYST_128 2
+#define STB0899_HYST_64 (0x01 << 1)
+#define STB0899_OFFST_HYST_64 1
+#define STB0899_WIDTH_HYST_64 1
+#define STB0899_HYST_32 (0x01 << 0)
+#define STB0899_OFFST_HYST_32 0
+#define STB0899_WIDTH_HYST_32 1
+#define STB0899_HYST_16 (0x00 << 0)
+#define STB0899_OFFST_HYST_16 0
+#define STB0899_WIDTH_HYST_16 0
+
+#define STB0899_RSULC 0xf548
+#define STB0899_ULDIL_ON (0x01 << 7)
+#define STB0899_OFFST_ULDIL_ON 7
+#define STB0899_WIDTH_ULDIL_ON 1
+#define STB0899_ULAUTO_ON (0x01 << 6)
+#define STB0899_OFFST_ULAUTO_ON 6
+#define STB0899_WIDTH_ULAUTO_ON 1
+#define STB0899_ULRS_ON (0x01 << 5)
+#define STB0899_OFFST_ULRS_ON 5
+#define STB0899_WIDTH_ULRS_ON 1
+#define STB0899_ULDESCRAM_ON (0x01 << 4)
+#define STB0899_OFFST_ULDESCRAM_ON 4
+#define STB0899_WIDTH_ULDESCRAM_ON 1
+#define STB0899_UL_DISABLE (0x01 << 2)
+#define STB0899_OFFST_UL_DISABLE 2
+#define STB0899_WIDTH_UL_DISABLE 1
+#define STB0899_NOFTHRESHOLD (0x01 << 0)
+#define STB0899_OFFST_NOFTHRESHOLD 0
+#define STB0899_WIDTH_NOFTHRESHOLD 1
+
+#define STB0899_RSLLC 0xf54a
+#define STB0899_DEMAPVIT 0xf583
+#define STB0899_DEMAPVIT_RSVD (0x01 << 7)
+#define STB0899_OFFST_DEMAPVIT_RSVD 7
+#define STB0899_WIDTH_DEMAPVIT_RSVD 1
+#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0)
+#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0
+#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7
+
+#define STB0899_PLPARM 0xf58c
+#define STB0899_VITMAPPING (0x07 << 5)
+#define STB0899_OFFST_VITMAPPING 5
+#define STB0899_WIDTH_VITMAPPING 3
+#define STB0899_VITMAPPING_BPSK (0x01 << 5)
+#define STB0899_OFFST_VITMAPPING_BPSK 5
+#define STB0899_WIDTH_VITMAPPING_BPSK 1
+#define STB0899_VITMAPPING_QPSK (0x00 << 5)
+#define STB0899_OFFST_VITMAPPING_QPSK 5
+#define STB0899_WIDTH_VITMAPPING_QPSK 0
+#define STB0899_VITCURPUN (0x1f << 0)
+#define STB0899_OFFST_VITCURPUN 0
+#define STB0899_WIDTH_VITCURPUN 5
+#define STB0899_VITCURPUN_1_2 (0x0d << 0)
+#define STB0899_VITCURPUN_2_3 (0x12 << 0)
+#define STB0899_VITCURPUN_3_4 (0x15 << 0)
+#define STB0899_VITCURPUN_5_6 (0x18 << 0)
+#define STB0899_VITCURPUN_6_7 (0x19 << 0)
+#define STB0899_VITCURPUN_7_8 (0x1a << 0)
+
+/* S2 DEMOD */
+#define STB0899_OFF0_DMD_STATUS 0xf300
+#define STB0899_BASE_DMD_STATUS 0x00000000
+#define STB0899_IF_AGC_LOCK (0x01 << 8)
+#define STB0899_OFFST_IF_AGC_LOCK 0
+#define STB0899_WIDTH_IF_AGC_LOCK 1
+
+#define STB0899_OFF0_CRL_FREQ 0xf304
+#define STB0899_BASE_CRL_FREQ 0x00000000
+#define STB0899_CARR_FREQ (0x3fffffff << 0)
+#define STB0899_OFFST_CARR_FREQ 0
+#define STB0899_WIDTH_CARR_FREQ 30
+
+#define STB0899_OFF0_BTR_FREQ 0xf308
+#define STB0899_BASE_BTR_FREQ 0x00000000
+#define STB0899_BTR_FREQ (0xfffffff << 0)
+#define STB0899_OFFST_BTR_FREQ 0
+#define STB0899_WIDTH_BTR_FREQ 28
+
+#define STB0899_OFF0_IF_AGC_GAIN 0xf30c
+#define STB0899_BASE_IF_AGC_GAIN 0x00000000
+#define STB0899_IF_AGC_GAIN (0x3fff < 0)
+#define STB0899_OFFST_IF_AGC_GAIN 0
+#define STB0899_WIDTH_IF_AGC_GAIN 14
+
+#define STB0899_OFF0_BB_AGC_GAIN 0xf310
+#define STB0899_BASE_BB_AGC_GAIN 0x00000000
+#define STB0899_BB_AGC_GAIN (0x3fff < 0)
+#define STB0899_OFFST_BB_AGC_GAIN 0
+#define STB0899_WIDTH_BB_AGC_GAIN 14
+
+#define STB0899_OFF0_DC_OFFSET 0xf314
+#define STB0899_BASE_DC_OFFSET 0x00000000
+#define STB0899_I (0xff < 8)
+#define STB0899_OFFST_I 8
+#define STB0899_WIDTH_I 8
+#define STB0899_Q (0xff < 0)
+#define STB0899_OFFST_Q 8
+#define STB0899_WIDTH_Q 8
+
+#define STB0899_OFF0_DMD_CNTRL 0xf31c
+#define STB0899_BASE_DMD_CNTRL 0x00000000
+#define STB0899_ADC0_PINS1IN (0x01 << 6)
+#define STB0899_OFFST_ADC0_PINS1IN 6
+#define STB0899_WIDTH_ADC0_PINS1IN 1
+#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3)
+#define STB0899_OFFST_IN2COMP1_OFFBIN0 3
+#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1
+#define STB0899_DC_COMP (0x01 << 2)
+#define STB0899_OFFST_DC_COMP 2
+#define STB0899_WIDTH_DC_COMP 1
+#define STB0899_MODMODE (0x03 << 0)
+#define STB0899_OFFST_MODMODE 0
+#define STB0899_WIDTH_MODMODE 2
+
+#define STB0899_OFF0_IF_AGC_CNTRL 0xf320
+#define STB0899_BASE_IF_AGC_CNTRL 0x00000000
+#define STB0899_IF_GAIN_INIT (0x3fff << 13)
+#define STB0899_OFFST_IF_GAIN_INIT 13
+#define STB0899_WIDTH_IF_GAIN_INIT 14
+#define STB0899_IF_GAIN_SENSE (0x01 << 12)
+#define STB0899_OFFST_IF_GAIN_SENSE 12
+#define STB0899_WIDTH_IF_GAIN_SENSE 1
+#define STB0899_IF_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_IF_LOOP_GAIN 8
+#define STB0899_WIDTH_IF_LOOP_GAIN 4
+#define STB0899_IF_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_IF_LD_GAIN_INIT 7
+#define STB0899_WIDTH_IF_LD_GAIN_INIT 1
+#define STB0899_IF_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_IF_AGC_REF 0
+#define STB0899_WIDTH_IF_AGC_REF 7
+
+#define STB0899_OFF0_BB_AGC_CNTRL 0xf324
+#define STB0899_BASE_BB_AGC_CNTRL 0x00000000
+#define STB0899_BB_GAIN_INIT (0x3fff << 12)
+#define STB0899_OFFST_BB_GAIN_INIT 12
+#define STB0899_WIDTH_BB_GAIN_INIT 14
+#define STB0899_BB_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_BB_LOOP_GAIN 8
+#define STB0899_WIDTH_BB_LOOP_GAIN 4
+#define STB0899_BB_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_BB_LD_GAIN_INIT 7
+#define STB0899_WIDTH_BB_LD_GAIN_INIT 1
+#define STB0899_BB_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_BB_AGC_REF 0
+#define STB0899_WIDTH_BB_AGC_REF 7
+
+#define STB0899_OFF0_CRL_CNTRL 0xf328
+#define STB0899_BASE_CRL_CNTRL 0x00000000
+#define STB0899_CRL_LOCK_CLEAR (0x01 << 5)
+#define STB0899_OFFST_CRL_LOCK_CLEAR 5
+#define STB0899_WIDTH_CRL_LOCK_CLEAR 1
+#define STB0899_CRL_SWPR_CLEAR (0x01 << 4)
+#define STB0899_OFFST_CRL_SWPR_CLEAR 4
+#define STB0899_WIDTH_CRL_SWPR_CLEAR 1
+#define STB0899_CRL_SWP_ENA (0x01 << 3)
+#define STB0899_OFFST_CRL_SWP_ENA 3
+#define STB0899_WIDTH_CRL_SWP_ENA 1
+#define STB0899_CRL_DET_SEL (0x01 << 2)
+#define STB0899_OFFST_CRL_DET_SEL 2
+#define STB0899_WIDTH_CRL_DET_SEL 1
+#define STB0899_CRL_SENSE (0x01 << 1)
+#define STB0899_OFFST_CRL_SENSE 1
+#define STB0899_WIDTH_CRL_SENSE 1
+#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0)
+#define STB0899_OFFST_CRL_PHSERR_CLEAR 0
+#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1
+
+#define STB0899_OFF0_CRL_PHS_INIT 0xf32c
+#define STB0899_BASE_CRL_PHS_INIT 0x00000000
+#define STB0899_CRL_PHS_INIT_31 (0x1 << 30)
+#define STB0899_OFFST_CRL_PHS_INIT_31 30
+#define STB0899_WIDTH_CRL_PHS_INIT_31 1
+#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24)
+#define STB0899_OFFST_CRL_LD_INIT_PHASE 24
+#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1
+#define STB0899_CRL_INIT_PHASE (0xffffff << 0)
+#define STB0899_OFFST_CRL_INIT_PHASE 0
+#define STB0899_WIDTH_CRL_INIT_PHASE 24
+
+#define STB0899_OFF0_CRL_FREQ_INIT 0xf330
+#define STB0899_BASE_CRL_FREQ_INIT 0x00000000
+#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30)
+#define STB0899_OFFST_CRL_FREQ_INIT_31 30
+#define STB0899_WIDTH_CRL_FREQ_INIT_31 1
+#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24)
+#define STB0899_OFFST_CRL_LD_FREQ_INIT 24
+#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1
+#define STB0899_CRL_FREQ_INIT (0xffffff << 0)
+#define STB0899_OFFST_CRL_FREQ_INIT 0
+#define STB0899_WIDTH_CRL_FREQ_INIT 24
+
+#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334
+#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000
+#define STB0899_KCRL2_RSHFT (0xf << 16)
+#define STB0899_OFFST_KCRL2_RSHFT 16
+#define STB0899_WIDTH_KCRL2_RSHFT 4
+#define STB0899_KCRL1 (0xf << 12)
+#define STB0899_OFFST_KCRL1 12
+#define STB0899_WIDTH_KCRL1 4
+#define STB0899_KCRL1_RSHFT (0xf << 8)
+#define STB0899_OFFST_KCRL1_RSHFT 8
+#define STB0899_WIDTH_KCRL1_RSHFT 4
+#define STB0899_KCRL0 (0xf << 4)
+#define STB0899_OFFST_KCRL0 4
+#define STB0899_WIDTH_KCRL0 4
+#define STB0899_KCRL0_RSHFT (0xf << 0)
+#define STB0899_OFFST_KCRL0_RSHFT 0
+#define STB0899_WIDTH_KCRL0_RSHFT 4
+
+#define STB0899_OFF0_CRL_NOM_FREQ 0xf338
+#define STB0899_BASE_CRL_NOM_FREQ 0x00000000
+#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_NOM_FREQ 0
+#define STB0899_WIDTH_CRL_NOM_FREQ 30
+
+#define STB0899_OFF0_CRL_SWP_RATE 0xf33c
+#define STB0899_BASE_CRL_SWP_RATE 0x00000000
+#define STB0899_CRL_SWP_RATE (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_SWP_RATE 0
+#define STB0899_WIDTH_CRL_SWP_RATE 30
+
+#define STB0899_OFF0_CRL_MAX_SWP 0xf340
+#define STB0899_BASE_CRL_MAX_SWP 0x00000000
+#define STB0899_CRL_MAX_SWP (0x3fffffff << 0)
+#define STB0899_OFFST_CRL_MAX_SWP 0
+#define STB0899_WIDTH_CRL_MAX_SWP 30
+
+#define STB0899_OFF0_CRL_LK_CNTRL 0xf344
+#define STB0899_BASE_CRL_LK_CNTRL 0x00000000
+
+#define STB0899_OFF0_DECIM_CNTRL 0xf348
+#define STB0899_BASE_DECIM_CNTRL 0x00000000
+#define STB0899_BAND_LIMIT_B (0x01 << 5)
+#define STB0899_OFFST_BAND_LIMIT_B 5
+#define STB0899_WIDTH_BAND_LIMIT_B 1
+#define STB0899_WIN_SEL (0x03 << 3)
+#define STB0899_OFFST_WIN_SEL 3
+#define STB0899_WIDTH_WIN_SEL 2
+#define STB0899_DECIM_RATE (0x07 << 0)
+#define STB0899_OFFST_DECIM_RATE 0
+#define STB0899_WIDTH_DECIM_RATE 3
+
+#define STB0899_OFF0_BTR_CNTRL 0xf34c
+#define STB0899_BASE_BTR_CNTRL 0x00000000
+#define STB0899_BTR_FREQ_CORR (0x7ff << 4)
+#define STB0899_OFFST_BTR_FREQ_CORR 4
+#define STB0899_WIDTH_BTR_FREQ_CORR 11
+#define STB0899_BTR_CLR_LOCK (0x01 << 3)
+#define STB0899_OFFST_BTR_CLR_LOCK 3
+#define STB0899_WIDTH_BTR_CLR_LOCK 1
+#define STB0899_BTR_SENSE (0x01 << 2)
+#define STB0899_OFFST_BTR_SENSE 2
+#define STB0899_WIDTH_BTR_SENSE 1
+#define STB0899_BTR_ERR_ENA (0x01 << 1)
+#define STB0899_OFFST_BTR_ERR_ENA 1
+#define STB0899_WIDTH_BTR_ERR_ENA 1
+#define STB0899_INTRP_PHS_SENSE (0x01 << 0)
+#define STB0899_OFFST_INTRP_PHS_SENSE 0
+#define STB0899_WIDTH_INTRP_PHS_SENSE 1
+
+#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350
+#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000
+#define STB0899_KBTR2_RSHFT (0x0f << 16)
+#define STB0899_OFFST_KBTR2_RSHFT 16
+#define STB0899_WIDTH_KBTR2_RSHFT 4
+#define STB0899_KBTR1 (0x0f << 12)
+#define STB0899_OFFST_KBTR1 12
+#define STB0899_WIDTH_KBTR1 4
+#define STB0899_KBTR1_RSHFT (0x0f << 8)
+#define STB0899_OFFST_KBTR1_RSHFT 8
+#define STB0899_WIDTH_KBTR1_RSHFT 4
+#define STB0899_KBTR0 (0x0f << 4)
+#define STB0899_OFFST_KBTR0 4
+#define STB0899_WIDTH_KBTR0 4
+#define STB0899_KBTR0_RSHFT (0x0f << 0)
+#define STB0899_OFFST_KBTR0_RSHFT 0
+#define STB0899_WIDTH_KBTR0_RSHFT 4
+
+#define STB0899_OFF0_BTR_PHS_INIT 0xf354
+#define STB0899_BASE_BTR_PHS_INIT 0x00000000
+#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28)
+#define STB0899_OFFST_BTR_LD_PHASE_INIT 28
+#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1
+#define STB0899_BTR_INIT_PHASE (0xfffffff << 0)
+#define STB0899_OFFST_BTR_INIT_PHASE 0
+#define STB0899_WIDTH_BTR_INIT_PHASE 28
+
+#define STB0899_OFF0_BTR_FREQ_INIT 0xf358
+#define STB0899_BASE_BTR_FREQ_INIT 0x00000000
+#define STB0899_BTR_LD_FREQ_INIT (1 << 28)
+#define STB0899_OFFST_BTR_LD_FREQ_INIT 28
+#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1
+#define STB0899_BTR_FREQ_INIT (0xfffffff << 0)
+#define STB0899_OFFST_BTR_FREQ_INIT 0
+#define STB0899_WIDTH_BTR_FREQ_INIT 28
+
+#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c
+#define STB0899_BASE_BTR_NOM_FREQ 0x00000000
+#define STB0899_BTR_NOM_FREQ (0xfffffff << 0)
+#define STB0899_OFFST_BTR_NOM_FREQ 0
+#define STB0899_WIDTH_BTR_NOM_FREQ 28
+
+#define STB0899_OFF0_BTR_LK_CNTRL 0xf360
+#define STB0899_BASE_BTR_LK_CNTRL 0x00000000
+#define STB0899_BTR_MIN_ENERGY (0x0f << 24)
+#define STB0899_OFFST_BTR_MIN_ENERGY 24
+#define STB0899_WIDTH_BTR_MIN_ENERGY 4
+#define STB0899_BTR_LOCK_TH_LO (0xff << 16)
+#define STB0899_OFFST_BTR_LOCK_TH_LO 16
+#define STB0899_WIDTH_BTR_LOCK_TH_LO 8
+#define STB0899_BTR_LOCK_TH_HI (0xff << 8)
+#define STB0899_OFFST_BTR_LOCK_TH_HI 8
+#define STB0899_WIDTH_BTR_LOCK_TH_HI 8
+#define STB0899_BTR_LOCK_GAIN (0x03 << 6)
+#define STB0899_OFFST_BTR_LOCK_GAIN 6
+#define STB0899_WIDTH_BTR_LOCK_GAIN 2
+#define STB0899_BTR_LOCK_LEAK (0x3f << 0)
+#define STB0899_OFFST_BTR_LOCK_LEAK 0
+#define STB0899_WIDTH_BTR_LOCK_LEAK 6
+
+#define STB0899_OFF0_DECN_CNTRL 0xf364
+#define STB0899_BASE_DECN_CNTRL 0x00000000
+
+#define STB0899_OFF0_TP_CNTRL 0xf368
+#define STB0899_BASE_TP_CNTRL 0x00000000
+
+#define STB0899_OFF0_TP_BUF_STATUS 0xf36c
+#define STB0899_BASE_TP_BUF_STATUS 0x00000000
+#define STB0899_TP_BUFFER_FULL (1 << 0)
+
+#define STB0899_OFF0_DC_ESTIM 0xf37c
+#define STB0899_BASE_DC_ESTIM 0x0000
+#define STB0899_I_DC_ESTIMATE (0xff << 8)
+#define STB0899_OFFST_I_DC_ESTIMATE 8
+#define STB0899_WIDTH_I_DC_ESTIMATE 8
+#define STB0899_Q_DC_ESTIMATE (0xff << 0)
+#define STB0899_OFFST_Q_DC_ESTIMATE 0
+#define STB0899_WIDTH_Q_DC_ESTIMATE 8
+
+#define STB0899_OFF0_FLL_CNTRL 0xf310
+#define STB0899_BASE_FLL_CNTRL 0x00000020
+#define STB0899_CRL_FLL_ACC (0x01 << 4)
+#define STB0899_OFFST_CRL_FLL_ACC 4
+#define STB0899_WIDTH_CRL_FLL_ACC 1
+#define STB0899_FLL_AVG_PERIOD (0x0f << 0)
+#define STB0899_OFFST_FLL_AVG_PERIOD 0
+#define STB0899_WIDTH_FLL_AVG_PERIOD 4
+
+#define STB0899_OFF0_FLL_FREQ_WD 0xf314
+#define STB0899_BASE_FLL_FREQ_WD 0x00000020
+#define STB0899_FLL_FREQ_WD (0xffffffff << 0)
+#define STB0899_OFFST_FLL_FREQ_WD 0
+#define STB0899_WIDTH_FLL_FREQ_WD 32
+
+#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358
+#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020
+#define STB0899_ANTI_ALIAS_SELB (0x03 << 0)
+#define STB0899_OFFST_ANTI_ALIAS_SELB 0
+#define STB0899_WIDTH_ANTI_ALIAS_SELB 2
+
+#define STB0899_OFF0_RRC_ALPHA 0xf35c
+#define STB0899_BASE_RRC_ALPHA 0x00000020
+#define STB0899_RRC_ALPHA (0x03 << 0)
+#define STB0899_OFFST_RRC_ALPHA 0
+#define STB0899_WIDTH_RRC_ALPHA 2
+
+#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360
+#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020
+#define STB0899_DC_ADAPT_LSHFT (0x077 << 0)
+#define STB0899_OFFST_DC_ADAPT_LSHFT 0
+#define STB0899_WIDTH_DC_ADAPT_LSHFT 3
+
+#define STB0899_OFF0_IMB_OFFSET 0xf364
+#define STB0899_BASE_IMB_OFFSET 0x00000020
+#define STB0899_PHS_IMB_COMP (0xff << 8)
+#define STB0899_OFFST_PHS_IMB_COMP 8
+#define STB0899_WIDTH_PHS_IMB_COMP 8
+#define STB0899_AMPL_IMB_COMP (0xff << 0)
+#define STB0899_OFFST_AMPL_IMB_COMP 0
+#define STB0899_WIDTH_AMPL_IMB_COMP 8
+
+#define STB0899_OFF0_IMB_ESTIMATE 0xf368
+#define STB0899_BASE_IMB_ESTIMATE 0x00000020
+#define STB0899_PHS_IMB_ESTIMATE (0xff << 8)
+#define STB0899_OFFST_PHS_IMB_ESTIMATE 8
+#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8
+#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0)
+#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0
+#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8
+
+#define STB0899_OFF0_IMB_CNTRL 0xf36c
+#define STB0899_BASE_IMB_CNTRL 0x00000020
+#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4)
+#define STB0899_OFFST_PHS_ADAPT_LSHFT 4
+#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3
+#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1)
+#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1
+#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3
+#define STB0899_IMB_COMP (0x01 << 0)
+#define STB0899_OFFST_IMB_COMP 0
+#define STB0899_WIDTH_IMB_COMP 1
+
+#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374
+#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020
+#define STB0899_IF_AGC_LOCK_TH (0xff << 11)
+#define STB0899_OFFST_IF_AGC_LOCK_TH 11
+#define STB0899_WIDTH_IF_AGC_LOCK_TH 8
+#define STB0899_IF_AGC_SD_DIV (0xff << 3)
+#define STB0899_OFFST_IF_AGC_SD_DIV 3
+#define STB0899_WIDTH_IF_AGC_SD_DIV 8
+#define STB0899_IF_AGC_DUMP_PER (0x07 << 0)
+#define STB0899_OFFST_IF_AGC_DUMP_PER 0
+#define STB0899_WIDTH_IF_AGC_DUMP_PER 3
+
+#define STB0899_OFF0_DMD_CNTRL2 0xf378
+#define STB0899_BASE_DMD_CNTRL2 0x00000020
+#define STB0899_SPECTRUM_INVERT (0x01 << 2)
+#define STB0899_OFFST_SPECTRUM_INVERT 2
+#define STB0899_WIDTH_SPECTRUM_INVERT 1
+#define STB0899_AGC_MODE (0x01 << 1)
+#define STB0899_OFFST_AGC_MODE 1
+#define STB0899_WIDTH_AGC_MODE 1
+#define STB0899_CRL_FREQ_ADJ (0x01 << 0)
+#define STB0899_OFFST_CRL_FREQ_ADJ 0
+#define STB0899_WIDTH_CRL_FREQ_ADJ 1
+
+#define STB0899_OFF0_TP_BUFFER 0xf300
+#define STB0899_BASE_TP_BUFFER 0x00000040
+#define STB0899_TP_BUFFER_IN (0xffff << 0)
+#define STB0899_OFFST_TP_BUFFER_IN 0
+#define STB0899_WIDTH_TP_BUFFER_IN 16
+
+#define STB0899_OFF0_TP_BUFFER1 0xf304
+#define STB0899_BASE_TP_BUFFER1 0x00000040
+#define STB0899_OFF0_TP_BUFFER2 0xf308
+#define STB0899_BASE_TP_BUFFER2 0x00000040
+#define STB0899_OFF0_TP_BUFFER3 0xf30c
+#define STB0899_BASE_TP_BUFFER3 0x00000040
+#define STB0899_OFF0_TP_BUFFER4 0xf310
+#define STB0899_BASE_TP_BUFFER4 0x00000040
+#define STB0899_OFF0_TP_BUFFER5 0xf314
+#define STB0899_BASE_TP_BUFFER5 0x00000040
+#define STB0899_OFF0_TP_BUFFER6 0xf318
+#define STB0899_BASE_TP_BUFFER6 0x00000040
+#define STB0899_OFF0_TP_BUFFER7 0xf31c
+#define STB0899_BASE_TP_BUFFER7 0x00000040
+#define STB0899_OFF0_TP_BUFFER8 0xf320
+#define STB0899_BASE_TP_BUFFER8 0x00000040
+#define STB0899_OFF0_TP_BUFFER9 0xf324
+#define STB0899_BASE_TP_BUFFER9 0x00000040
+#define STB0899_OFF0_TP_BUFFER10 0xf328
+#define STB0899_BASE_TP_BUFFER10 0x00000040
+#define STB0899_OFF0_TP_BUFFER11 0xf32c
+#define STB0899_BASE_TP_BUFFER11 0x00000040
+#define STB0899_OFF0_TP_BUFFER12 0xf330
+#define STB0899_BASE_TP_BUFFER12 0x00000040
+#define STB0899_OFF0_TP_BUFFER13 0xf334
+#define STB0899_BASE_TP_BUFFER13 0x00000040
+#define STB0899_OFF0_TP_BUFFER14 0xf338
+#define STB0899_BASE_TP_BUFFER14 0x00000040
+#define STB0899_OFF0_TP_BUFFER15 0xf33c
+#define STB0899_BASE_TP_BUFFER15 0x00000040
+#define STB0899_OFF0_TP_BUFFER16 0xf340
+#define STB0899_BASE_TP_BUFFER16 0x00000040
+#define STB0899_OFF0_TP_BUFFER17 0xf344
+#define STB0899_BASE_TP_BUFFER17 0x00000040
+#define STB0899_OFF0_TP_BUFFER18 0xf348
+#define STB0899_BASE_TP_BUFFER18 0x00000040
+#define STB0899_OFF0_TP_BUFFER19 0xf34c
+#define STB0899_BASE_TP_BUFFER19 0x00000040
+#define STB0899_OFF0_TP_BUFFER20 0xf350
+#define STB0899_BASE_TP_BUFFER20 0x00000040
+#define STB0899_OFF0_TP_BUFFER21 0xf354
+#define STB0899_BASE_TP_BUFFER21 0x00000040
+#define STB0899_OFF0_TP_BUFFER22 0xf358
+#define STB0899_BASE_TP_BUFFER22 0x00000040
+#define STB0899_OFF0_TP_BUFFER23 0xf35c
+#define STB0899_BASE_TP_BUFFER23 0x00000040
+#define STB0899_OFF0_TP_BUFFER24 0xf360
+#define STB0899_BASE_TP_BUFFER24 0x00000040
+#define STB0899_OFF0_TP_BUFFER25 0xf364
+#define STB0899_BASE_TP_BUFFER25 0x00000040
+#define STB0899_OFF0_TP_BUFFER26 0xf368
+#define STB0899_BASE_TP_BUFFER26 0x00000040
+#define STB0899_OFF0_TP_BUFFER27 0xf36c
+#define STB0899_BASE_TP_BUFFER27 0x00000040
+#define STB0899_OFF0_TP_BUFFER28 0xf370
+#define STB0899_BASE_TP_BUFFER28 0x00000040
+#define STB0899_OFF0_TP_BUFFER29 0xf374
+#define STB0899_BASE_TP_BUFFER29 0x00000040
+#define STB0899_OFF0_TP_BUFFER30 0xf378
+#define STB0899_BASE_TP_BUFFER30 0x00000040
+#define STB0899_OFF0_TP_BUFFER31 0xf37c
+#define STB0899_BASE_TP_BUFFER31 0x00000040
+#define STB0899_OFF0_TP_BUFFER32 0xf300
+#define STB0899_BASE_TP_BUFFER32 0x00000060
+#define STB0899_OFF0_TP_BUFFER33 0xf304
+#define STB0899_BASE_TP_BUFFER33 0x00000060
+#define STB0899_OFF0_TP_BUFFER34 0xf308
+#define STB0899_BASE_TP_BUFFER34 0x00000060
+#define STB0899_OFF0_TP_BUFFER35 0xf30c
+#define STB0899_BASE_TP_BUFFER35 0x00000060
+#define STB0899_OFF0_TP_BUFFER36 0xf310
+#define STB0899_BASE_TP_BUFFER36 0x00000060
+#define STB0899_OFF0_TP_BUFFER37 0xf314
+#define STB0899_BASE_TP_BUFFER37 0x00000060
+#define STB0899_OFF0_TP_BUFFER38 0xf318
+#define STB0899_BASE_TP_BUFFER38 0x00000060
+#define STB0899_OFF0_TP_BUFFER39 0xf31c
+#define STB0899_BASE_TP_BUFFER39 0x00000060
+#define STB0899_OFF0_TP_BUFFER40 0xf320
+#define STB0899_BASE_TP_BUFFER40 0x00000060
+#define STB0899_OFF0_TP_BUFFER41 0xf324
+#define STB0899_BASE_TP_BUFFER41 0x00000060
+#define STB0899_OFF0_TP_BUFFER42 0xf328
+#define STB0899_BASE_TP_BUFFER42 0x00000060
+#define STB0899_OFF0_TP_BUFFER43 0xf32c
+#define STB0899_BASE_TP_BUFFER43 0x00000060
+#define STB0899_OFF0_TP_BUFFER44 0xf330
+#define STB0899_BASE_TP_BUFFER44 0x00000060
+#define STB0899_OFF0_TP_BUFFER45 0xf334
+#define STB0899_BASE_TP_BUFFER45 0x00000060
+#define STB0899_OFF0_TP_BUFFER46 0xf338
+#define STB0899_BASE_TP_BUFFER46 0x00000060
+#define STB0899_OFF0_TP_BUFFER47 0xf33c
+#define STB0899_BASE_TP_BUFFER47 0x00000060
+#define STB0899_OFF0_TP_BUFFER48 0xf340
+#define STB0899_BASE_TP_BUFFER48 0x00000060
+#define STB0899_OFF0_TP_BUFFER49 0xf344
+#define STB0899_BASE_TP_BUFFER49 0x00000060
+#define STB0899_OFF0_TP_BUFFER50 0xf348
+#define STB0899_BASE_TP_BUFFER50 0x00000060
+#define STB0899_OFF0_TP_BUFFER51 0xf34c
+#define STB0899_BASE_TP_BUFFER51 0x00000060
+#define STB0899_OFF0_TP_BUFFER52 0xf350
+#define STB0899_BASE_TP_BUFFER52 0x00000060
+#define STB0899_OFF0_TP_BUFFER53 0xf354
+#define STB0899_BASE_TP_BUFFER53 0x00000060
+#define STB0899_OFF0_TP_BUFFER54 0xf358
+#define STB0899_BASE_TP_BUFFER54 0x00000060
+#define STB0899_OFF0_TP_BUFFER55 0xf35c
+#define STB0899_BASE_TP_BUFFER55 0x00000060
+#define STB0899_OFF0_TP_BUFFER56 0xf360
+#define STB0899_BASE_TP_BUFFER56 0x00000060
+#define STB0899_OFF0_TP_BUFFER57 0xf364
+#define STB0899_BASE_TP_BUFFER57 0x00000060
+#define STB0899_OFF0_TP_BUFFER58 0xf368
+#define STB0899_BASE_TP_BUFFER58 0x00000060
+#define STB0899_OFF0_TP_BUFFER59 0xf36c
+#define STB0899_BASE_TP_BUFFER59 0x00000060
+#define STB0899_OFF0_TP_BUFFER60 0xf370
+#define STB0899_BASE_TP_BUFFER60 0x00000060
+#define STB0899_OFF0_TP_BUFFER61 0xf374
+#define STB0899_BASE_TP_BUFFER61 0x00000060
+#define STB0899_OFF0_TP_BUFFER62 0xf378
+#define STB0899_BASE_TP_BUFFER62 0x00000060
+#define STB0899_OFF0_TP_BUFFER63 0xf37c
+#define STB0899_BASE_TP_BUFFER63 0x00000060
+
+#define STB0899_OFF0_RESET_CNTRL 0xf300
+#define STB0899_BASE_RESET_CNTRL 0x00000400
+#define STB0899_DVBS2_RESET (0x01 << 0)
+#define STB0899_OFFST_DVBS2_RESET 0
+#define STB0899_WIDTH_DVBS2_RESET 1
+
+#define STB0899_OFF0_ACM_ENABLE 0xf304
+#define STB0899_BASE_ACM_ENABLE 0x00000400
+#define STB0899_ACM_ENABLE 1
+
+#define STB0899_OFF0_DESCR_CNTRL 0xf30c
+#define STB0899_BASE_DESCR_CNTRL 0x00000400
+#define STB0899_OFFST_DESCR_CNTRL 0
+#define STB0899_WIDTH_DESCR_CNTRL 16
+
+#define STB0899_OFF0_UWP_CNTRL1 0xf320
+#define STB0899_BASE_UWP_CNTRL1 0x00000400
+#define STB0899_UWP_TH_SOF (0x7fff << 11)
+#define STB0899_OFFST_UWP_TH_SOF 11
+#define STB0899_WIDTH_UWP_TH_SOF 15
+#define STB0899_UWP_ESN0_QUANT (0xff << 3)
+#define STB0899_OFFST_UWP_ESN0_QUANT 3
+#define STB0899_WIDTH_UWP_ESN0_QUANT 8
+#define STB0899_UWP_ESN0_AVE (0x03 << 1)
+#define STB0899_OFFST_UWP_ESN0_AVE 1
+#define STB0899_WIDTH_UWP_ESN0_AVE 2
+#define STB0899_UWP_START (0x01 << 0)
+#define STB0899_OFFST_UWP_START 0
+#define STB0899_WIDTH_UWP_START 1
+
+#define STB0899_OFF0_UWP_CNTRL2 0xf324
+#define STB0899_BASE_UWP_CNTRL2 0x00000400
+#define STB0899_UWP_MISS_TH (0xff << 16)
+#define STB0899_OFFST_UWP_MISS_TH 16
+#define STB0899_WIDTH_UWP_MISS_TH 8
+#define STB0899_FE_FINE_TRK (0xff << 8)
+#define STB0899_OFFST_FE_FINE_TRK 8
+#define STB0899_WIDTH_FE_FINE_TRK 8
+#define STB0899_FE_COARSE_TRK (0xff << 0)
+#define STB0899_OFFST_FE_COARSE_TRK 0
+#define STB0899_WIDTH_FE_COARSE_TRK 8
+
+#define STB0899_OFF0_UWP_STAT1 0xf328
+#define STB0899_BASE_UWP_STAT1 0x00000400
+#define STB0899_UWP_STATE (0x03ff << 15)
+#define STB0899_OFFST_UWP_STATE 15
+#define STB0899_WIDTH_UWP_STATE 10
+#define STB0899_UW_MAX_PEAK (0x7fff << 0)
+#define STB0899_OFFST_UW_MAX_PEAK 0
+#define STB0899_WIDTH_UW_MAX_PEAK 15
+
+#define STB0899_OFF0_UWP_STAT2 0xf32c
+#define STB0899_BASE_UWP_STAT2 0x00000400
+#define STB0899_ESNO_EST (0x07ffff << 7)
+#define STB0899_OFFST_ESN0_EST 7
+#define STB0899_WIDTH_ESN0_EST 19
+#define STB0899_UWP_DECODE_MOD (0x7f << 0)
+#define STB0899_OFFST_UWP_DECODE_MOD 0
+#define STB0899_WIDTH_UWP_DECODE_MOD 7
+
+#define STB0899_OFF0_DMD_CORE_ID 0xf334
+#define STB0899_BASE_DMD_CORE_ID 0x00000400
+#define STB0899_CORE_ID (0xffffffff << 0)
+#define STB0899_OFFST_CORE_ID 0
+#define STB0899_WIDTH_CORE_ID 32
+
+#define STB0899_OFF0_DMD_VERSION_ID 0xf33c
+#define STB0899_BASE_DMD_VERSION_ID 0x00000400
+#define STB0899_VERSION_ID (0xff << 0)
+#define STB0899_OFFST_VERSION_ID 0
+#define STB0899_WIDTH_VERSION_ID 8
+
+#define STB0899_OFF0_DMD_STAT2 0xf340
+#define STB0899_BASE_DMD_STAT2 0x00000400
+#define STB0899_CSM_LOCK (0x01 << 1)
+#define STB0899_OFFST_CSM_LOCK 1
+#define STB0899_WIDTH_CSM_LOCK 1
+#define STB0899_UWP_LOCK (0x01 << 0)
+#define STB0899_OFFST_UWP_LOCK 0
+#define STB0899_WIDTH_UWP_LOCK 1
+
+#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344
+#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400
+#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0)
+#define STB0899_OFFST_FREQ_ADJ_SCALE 0
+#define STB0899_WIDTH_FREQ_ADJ_SCALE 12
+
+#define STB0899_OFF0_UWP_CNTRL3 0xf34c
+#define STB0899_BASE_UWP_CNTRL3 0x00000400
+#define STB0899_UWP_TH_TRACK (0x7fff << 15)
+#define STB0899_OFFST_UWP_TH_TRACK 15
+#define STB0899_WIDTH_UWP_TH_TRACK 15
+#define STB0899_UWP_TH_ACQ (0x7fff << 0)
+#define STB0899_OFFST_UWP_TH_ACQ 0
+#define STB0899_WIDTH_UWP_TH_ACQ 15
+
+#define STB0899_OFF0_SYM_CLK_SEL 0xf350
+#define STB0899_BASE_SYM_CLK_SEL 0x00000400
+#define STB0899_SYM_CLK_SEL (0x03 << 0)
+#define STB0899_OFFST_SYM_CLK_SEL 0
+#define STB0899_WIDTH_SYM_CLK_SEL 2
+
+#define STB0899_OFF0_SOF_SRCH_TO 0xf354
+#define STB0899_BASE_SOF_SRCH_TO 0x00000400
+#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0)
+#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0
+#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22
+
+#define STB0899_OFF0_ACQ_CNTRL1 0xf358
+#define STB0899_BASE_ACQ_CNTRL1 0x00000400
+#define STB0899_FE_FINE_ACQ (0xff << 8)
+#define STB0899_OFFST_FE_FINE_ACQ 8
+#define STB0899_WIDTH_FE_FINE_ACQ 8
+#define STB0899_FE_COARSE_ACQ (0xff << 0)
+#define STB0899_OFFST_FE_COARSE_ACQ 0
+#define STB0899_WIDTH_FE_COARSE_ACQ 8
+
+#define STB0899_OFF0_ACQ_CNTRL2 0xf35c
+#define STB0899_BASE_ACQ_CNTRL2 0x00000400
+#define STB0899_ZIGZAG (0x01 << 25)
+#define STB0899_OFFST_ZIGZAG 25
+#define STB0899_WIDTH_ZIGZAG 1
+#define STB0899_NUM_STEPS (0xff << 17)
+#define STB0899_OFFST_NUM_STEPS 17
+#define STB0899_WIDTH_NUM_STEPS 8
+#define STB0899_FREQ_STEPSIZE (0x1ffff << 0)
+#define STB0899_OFFST_FREQ_STEPSIZE 0
+#define STB0899_WIDTH_FREQ_STEPSIZE 17
+
+#define STB0899_OFF0_ACQ_CNTRL3 0xf360
+#define STB0899_BASE_ACQ_CNTRL3 0x00000400
+#define STB0899_THRESHOLD_SCL (0x3f << 23)
+#define STB0899_OFFST_THRESHOLD_SCL 23
+#define STB0899_WIDTH_THRESHOLD_SCL 6
+#define STB0899_UWP_TH_SRCH (0x7fff << 8)
+#define STB0899_OFFST_UWP_TH_SRCH 8
+#define STB0899_WIDTH_UWP_TH_SRCH 15
+#define STB0899_AUTO_REACQUIRE (0x01 << 7)
+#define STB0899_OFFST_AUTO_REACQUIRE 7
+#define STB0899_WIDTH_AUTO_REACQUIRE 1
+#define STB0899_TRACK_LOCK_SEL (0x01 << 6)
+#define STB0899_OFFST_TRACK_LOCK_SEL 6
+#define STB0899_WIDTH_TRACK_LOCK_SEL 1
+#define STB0899_ACQ_SEARCH_MODE (0x03 << 4)
+#define STB0899_OFFST_ACQ_SEARCH_MODE 4
+#define STB0899_WIDTH_ACQ_SEARCH_MODE 2
+#define STB0899_CONFIRM_FRAMES (0x0f << 0)
+#define STB0899_OFFST_CONFIRM_FRAMES 0
+#define STB0899_WIDTH_CONFIRM_FRAMES 4
+
+#define STB0899_OFF0_FE_SETTLE 0xf364
+#define STB0899_BASE_FE_SETTLE 0x00000400
+#define STB0899_SETTLING_TIME (0x3fffff << 0)
+#define STB0899_OFFST_SETTLING_TIME 0
+#define STB0899_WIDTH_SETTLING_TIME 22
+
+#define STB0899_OFF0_AC_DWELL 0xf368
+#define STB0899_BASE_AC_DWELL 0x00000400
+#define STB0899_DWELL_TIME (0x3fffff << 0)
+#define STB0899_OFFST_DWELL_TIME 0
+#define STB0899_WIDTH_DWELL_TIME 22
+
+#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c
+#define STB0899_BASE_ACQUIRE_TRIG 0x00000400
+#define STB0899_ACQUIRE (0x01 << 0)
+#define STB0899_OFFST_ACQUIRE 0
+#define STB0899_WIDTH_ACQUIRE 1
+
+#define STB0899_OFF0_LOCK_LOST 0xf370
+#define STB0899_BASE_LOCK_LOST 0x00000400
+#define STB0899_LOCK_LOST (0x01 << 0)
+#define STB0899_OFFST_LOCK_LOST 0
+#define STB0899_WIDTH_LOCK_LOST 1
+
+#define STB0899_OFF0_ACQ_STAT1 0xf374
+#define STB0899_BASE_ACQ_STAT1 0x00000400
+#define STB0899_STEP_FREQ (0x1fffff << 11)
+#define STB0899_OFFST_STEP_FREQ 11
+#define STB0899_WIDTH_STEP_FREQ 21
+#define STB0899_ACQ_STATE (0x07 << 8)
+#define STB0899_OFFST_ACQ_STATE 8
+#define STB0899_WIDTH_ACQ_STATE 3
+#define STB0899_UW_DETECT_COUNT (0xff << 0)
+#define STB0899_OFFST_UW_DETECT_COUNT 0
+#define STB0899_WIDTH_UW_DETECT_COUNT 8
+
+#define STB0899_OFF0_ACQ_TIMEOUT 0xf378
+#define STB0899_BASE_ACQ_TIMEOUT 0x00000400
+#define STB0899_ACQ_TIMEOUT (0x3fffff << 0)
+#define STB0899_OFFST_ACQ_TIMEOUT 0
+#define STB0899_WIDTH_ACQ_TIMEOUT 22
+
+#define STB0899_OFF0_ACQ_TIME 0xf37c
+#define STB0899_BASE_ACQ_TIME 0x00000400
+#define STB0899_ACQ_TIME_SYM (0xffffff << 0)
+#define STB0899_OFFST_ACQ_TIME_SYM 0
+#define STB0899_WIDTH_ACQ_TIME_SYM 24
+
+#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308
+#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440
+#define STB0899_FINAL_GAIN_INIT (0x3fff << 12)
+#define STB0899_OFFST_FINAL_GAIN_INIT 12
+#define STB0899_WIDTH_FINAL_GAIN_INIT 14
+#define STB0899_FINAL_LOOP_GAIN (0x0f << 8)
+#define STB0899_OFFST_FINAL_LOOP_GAIN 8
+#define STB0899_WIDTH_FINAL_LOOP_GAIN 4
+#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7)
+#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7
+#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1
+#define STB0899_FINAL_AGC_REF (0x7f << 0)
+#define STB0899_OFFST_FINAL_AGC_REF 0
+#define STB0899_WIDTH_FINAL_AGC_REF 7
+
+#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c
+#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440
+#define STB0899_FINAL_AGC_GAIN (0x3fff << 0)
+#define STB0899_OFFST_FINAL_AGC_GAIN 0
+#define STB0899_WIDTH_FINAL_AGC_GAIN 14
+
+#define STB0899_OFF0_EQUALIZER_INIT 0xf310
+#define STB0899_BASE_EQUALIZER_INIT 0x00000440
+#define STB0899_EQ_SRST (0x01 << 1)
+#define STB0899_OFFST_EQ_SRST 1
+#define STB0899_WIDTH_EQ_SRST 1
+#define STB0899_EQ_INIT (0x01 << 0)
+#define STB0899_OFFST_EQ_INIT 0
+#define STB0899_WIDTH_EQ_INIT 1
+
+#define STB0899_OFF0_EQ_CNTRL 0xf314
+#define STB0899_BASE_EQ_CNTRL 0x00000440
+#define STB0899_EQ_ADAPT_MODE (0x01 << 18)
+#define STB0899_OFFST_EQ_ADAPT_MODE 18
+#define STB0899_WIDTH_EQ_ADAPT_MODE 1
+#define STB0899_EQ_DELAY (0x0f << 14)
+#define STB0899_OFFST_EQ_DELAY 14
+#define STB0899_WIDTH_EQ_DELAY 4
+#define STB0899_EQ_QUANT_LEVEL (0xff << 6)
+#define STB0899_OFFST_EQ_QUANT_LEVEL 6
+#define STB0899_WIDTH_EQ_QUANT_LEVEL 8
+#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5)
+#define STB0899_OFFST_EQ_DISABLE_UPDATE 5
+#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1
+#define STB0899_EQ_BYPASS (0x01 << 4)
+#define STB0899_OFFST_EQ_BYPASS 4
+#define STB0899_WIDTH_EQ_BYPASS 1
+#define STB0899_EQ_SHIFT (0x0f << 0)
+#define STB0899_OFFST_EQ_SHIFT 0
+#define STB0899_WIDTH_EQ_SHIFT 4
+
+#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320
+#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324
+#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328
+#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c
+#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330
+#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334
+#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338
+#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c
+#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340
+#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344
+#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348
+#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440
+#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0
+#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350
+#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354
+#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358
+#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c
+#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360
+#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364
+#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368
+#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c
+#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370
+#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374
+#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378
+#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440
+#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0
+#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300
+#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304
+#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308
+#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c
+#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310
+#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314
+#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318
+#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c
+#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320
+#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324
+#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328
+#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460
+#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0
+#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12
+
+#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330
+#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334
+#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338
+#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c
+#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340
+#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344
+#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348
+#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c
+#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350
+#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354
+#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358
+#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460
+#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0)
+#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0
+#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12
+
+/* S2 FEC */
+#define STB0899_OFF0_BLOCK_LNGTH 0xfa04
+#define STB0899_BASE_BLOCK_LNGTH 0x00000000
+#define STB0899_BLOCK_LENGTH (0xff << 0)
+#define STB0899_OFFST_BLOCK_LENGTH 0
+#define STB0899_WIDTH_BLOCK_LENGTH 8
+
+#define STB0899_OFF0_ROW_STR 0xfa08
+#define STB0899_BASE_ROW_STR 0x00000000
+#define STB0899_ROW_STRIDE (0xff << 0)
+#define STB0899_OFFST_ROW_STRIDE 0
+#define STB0899_WIDTH_ROW_STRIDE 8
+
+#define STB0899_OFF0_MAX_ITER 0xfa0c
+#define STB0899_BASE_MAX_ITER 0x00000000
+#define STB0899_MAX_ITERATIONS (0xff << 0)
+#define STB0899_OFFST_MAX_ITERATIONS 0
+#define STB0899_WIDTH_MAX_ITERATIONS 8
+
+#define STB0899_OFF0_BN_END_ADDR 0xfa10
+#define STB0899_BASE_BN_END_ADDR 0x00000000
+#define STB0899_BN_END_ADDR (0x0fff << 0)
+#define STB0899_OFFST_BN_END_ADDR 0
+#define STB0899_WIDTH_BN_END_ADDR 12
+
+#define STB0899_OFF0_CN_END_ADDR 0xfa14
+#define STB0899_BASE_CN_END_ADDR 0x00000000
+#define STB0899_CN_END_ADDR (0x0fff << 0)
+#define STB0899_OFFST_CN_END_ADDR 0
+#define STB0899_WIDTH_CN_END_ADDR 12
+
+#define STB0899_OFF0_INFO_LENGTH 0xfa1c
+#define STB0899_BASE_INFO_LENGTH 0x00000000
+#define STB0899_INFO_LENGTH (0xff << 0)
+#define STB0899_OFFST_INFO_LENGTH 0
+#define STB0899_WIDTH_INFO_LENGTH 8
+
+#define STB0899_OFF0_BOT_ADDR 0xfa20
+#define STB0899_BASE_BOT_ADDR 0x00000000
+#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0)
+#define STB0899_OFFST_BOTTOM_BASE_ADDR 0
+#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10
+
+#define STB0899_OFF0_BCH_BLK_LN 0xfa24
+#define STB0899_BASE_BCH_BLK_LN 0x00000000
+#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0)
+#define STB0899_OFFST_BCH_BLOCK_LENGTH 0
+#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16
+
+#define STB0899_OFF0_BCH_T 0xfa28
+#define STB0899_BASE_BCH_T 0x00000000
+#define STB0899_BCH_T (0x0f << 0)
+#define STB0899_OFFST_BCH_T 0
+#define STB0899_WIDTH_BCH_T 4
+
+#define STB0899_OFF0_CNFG_MODE 0xfa00
+#define STB0899_BASE_CNFG_MODE 0x00000800
+#define STB0899_MODCOD (0x1f << 2)
+#define STB0899_OFFST_MODCOD 2
+#define STB0899_WIDTH_MODCOD 5
+#define STB0899_MODCOD_SEL (0x01 << 1)
+#define STB0899_OFFST_MODCOD_SEL 1
+#define STB0899_WIDTH_MODCOD_SEL 1
+#define STB0899_CONFIG_MODE (0x01 << 0)
+#define STB0899_OFFST_CONFIG_MODE 0
+#define STB0899_WIDTH_CONFIG_MODE 1
+
+#define STB0899_OFF0_LDPC_STAT 0xfa04
+#define STB0899_BASE_LDPC_STAT 0x00000800
+#define STB0899_ITERATION (0xff << 3)
+#define STB0899_OFFST_ITERATION 3
+#define STB0899_WIDTH_ITERATION 8
+#define STB0899_LDPC_DEC_STATE (0x07 << 0)
+#define STB0899_OFFST_LDPC_DEC_STATE 0
+#define STB0899_WIDTH_LDPC_DEC_STATE 3
+
+#define STB0899_OFF0_ITER_SCALE 0xfa08
+#define STB0899_BASE_ITER_SCALE 0x00000800
+#define STB0899_ITERATION_SCALE (0xff << 0)
+#define STB0899_OFFST_ITERATION_SCALE 0
+#define STB0899_WIDTH_ITERATION_SCALE 8
+
+#define STB0899_OFF0_INPUT_MODE 0xfa0c
+#define STB0899_BASE_INPUT_MODE 0x00000800
+#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0)
+#define STB0899_OFFST_SD_BLOCK1_STREAM0 0
+#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1
+
+#define STB0899_OFF0_LDPCDECRST 0xfa10
+#define STB0899_BASE_LDPCDECRST 0x00000800
+#define STB0899_LDPC_DEC_RST (0x01 << 0)
+#define STB0899_OFFST_LDPC_DEC_RST 0
+#define STB0899_WIDTH_LDPC_DEC_RST 1
+
+#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14
+#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800
+#define STB0899_CLKS_PER_BYTE (0x0f << 0)
+#define STB0899_OFFST_CLKS_PER_BYTE 0
+#define STB0899_WIDTH_CLKS_PER_BYTE 5
+
+#define STB0899_OFF0_BCH_ERRORS 0xfa18
+#define STB0899_BASE_BCH_ERRORS 0x00000800
+#define STB0899_BCH_ERRORS (0x0f << 0)
+#define STB0899_OFFST_BCH_ERRORS 0
+#define STB0899_WIDTH_BCH_ERRORS 4
+
+#define STB0899_OFF0_LDPC_ERRORS 0xfa1c
+#define STB0899_BASE_LDPC_ERRORS 0x00000800
+#define STB0899_LDPC_ERRORS (0xffff << 0)
+#define STB0899_OFFST_LDPC_ERRORS 0
+#define STB0899_WIDTH_LDPC_ERRORS 16
+
+#define STB0899_OFF0_BCH_MODE 0xfa20
+#define STB0899_BASE_BCH_MODE 0x00000800
+#define STB0899_BCH_CORRECT_N (0x01 << 1)
+#define STB0899_OFFST_BCH_CORRECT_N 1
+#define STB0899_WIDTH_BCH_CORRECT_N 1
+#define STB0899_FULL_BYPASS (0x01 << 0)
+#define STB0899_OFFST_FULL_BYPASS 0
+#define STB0899_WIDTH_FULL_BYPASS 1
+
+#define STB0899_OFF0_ERR_ACC_PER 0xfa24
+#define STB0899_BASE_ERR_ACC_PER 0x00000800
+#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0)
+#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0
+#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4
+
+#define STB0899_OFF0_BCH_ERR_ACC 0xfa28
+#define STB0899_BASE_BCH_ERR_ACC 0x00000800
+#define STB0899_BCH_ERR_ACCUM (0xff << 0)
+#define STB0899_OFFST_BCH_ERR_ACCUM 0
+#define STB0899_WIDTH_BCH_ERR_ACCUM 8
+
+#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c
+#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800
+#define STB0899_FEC_CORE_ID (0xffffffff << 0)
+#define STB0899_OFFST_FEC_CORE_ID 0
+#define STB0899_WIDTH_FEC_CORE_ID 32
+
+#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34
+#define STB0899_BASE_FEC_VER_ID_REG 0x00000800
+#define STB0899_FEC_VER_ID (0xff << 0)
+#define STB0899_OFFST_FEC_VER_ID 0
+#define STB0899_WIDTH_FEC_VER_ID 8
+
+#define STB0899_OFF0_FEC_TP_SEL 0xfa38
+#define STB0899_BASE_FEC_TP_SEL 0x00000800
+
+#define STB0899_OFF0_CSM_CNTRL1 0xf310
+#define STB0899_BASE_CSM_CNTRL1 0x00000400
+#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19)
+#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19
+#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1
+#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18)
+#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18
+#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1
+#define STB0899_CSM_AUTO_PARAM (0x01 << 17)
+#define STB0899_OFFST_CSM_AUTO_PARAM 17
+#define STB0899_WIDTH_CSM_AUTO_PARAM 1
+#define STB0899_FE_LOOP_SHIFT (0x07 << 14)
+#define STB0899_OFFST_FE_LOOP_SHIFT 14
+#define STB0899_WIDTH_FE_LOOP_SHIFT 3
+#define STB0899_CSM_AGC_SHIFT (0x07 << 11)
+#define STB0899_OFFST_CSM_AGC_SHIFT 11
+#define STB0899_WIDTH_CSM_AGC_SHIFT 3
+#define STB0899_CSM_AGC_GAIN (0x1ff << 2)
+#define STB0899_OFFST_CSM_AGC_GAIN 2
+#define STB0899_WIDTH_CSM_AGC_GAIN 9
+#define STB0899_CSM_TWO_PASS (0x01 << 1)
+#define STB0899_OFFST_CSM_TWO_PASS 1
+#define STB0899_WIDTH_CSM_TWO_PASS 1
+#define STB0899_CSM_DVT_TABLE (0x01 << 0)
+#define STB0899_OFFST_CSM_DVT_TABLE 0
+#define STB0899_WIDTH_CSM_DVT_TABLE 1
+
+#define STB0899_OFF0_CSM_CNTRL2 0xf314
+#define STB0899_BASE_CSM_CNTRL2 0x00000400
+#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9)
+#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9
+#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9
+#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0)
+#define STB0899_OFFST_CSM_GAMMA_ACQ 0
+#define STB0899_WIDTH_CSM_GAMMA_ACQ 9
+
+#define STB0899_OFF0_CSM_CNTRL3 0xf318
+#define STB0899_BASE_CSM_CNTRL3 0x00000400
+#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9)
+#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9
+#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9
+#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0)
+#define STB0899_OFFST_CSM_GAMMA_TRACK 0
+#define STB0899_WIDTH_CSM_GAMMA_TRACK 9
+
+#define STB0899_OFF0_CSM_CNTRL4 0xf31c
+#define STB0899_BASE_CSM_CNTRL4 0x00000400
+#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8)
+#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8
+#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4
+#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0)
+#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0
+#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8
+
+/* Check on chapter 8 page 42 */
+#define STB0899_ERRCTRL1 0xf574
+#define STB0899_ERRCTRL2 0xf575
+#define STB0899_ERRCTRL3 0xf576
+#define STB0899_ERR_SRC_S1 (0x1f << 3)
+#define STB0899_OFFST_ERR_SRC_S1 3
+#define STB0899_WIDTH_ERR_SRC_S1 5
+#define STB0899_ERR_SRC_S2 (0x0f << 0)
+#define STB0899_OFFST_ERR_SRC_S2 0
+#define STB0899_WIDTH_ERR_SRC_S2 4
+#define STB0899_NOE (0x07 << 0)
+#define STB0899_OFFST_NOE 0
+#define STB0899_WIDTH_NOE 3
+
+#define STB0899_ECNT1M 0xf524
+#define STB0899_ECNT1L 0xf525
+#define STB0899_ECNT2M 0xf526
+#define STB0899_ECNT2L 0xf527
+#define STB0899_ECNT3M 0xf528
+#define STB0899_ECNT3L 0xf529
+
+#define STB0899_DMONMSK1 0xf57b
+#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7)
+#define STB0899_DMONMSK1_FREE_14 (1 << 6)
+#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5)
+#define STB0899_DMONMSK1_FREE_12 (1 << 4)
+#define STB0899_DMONMSK1_FREE_11 (1 << 3)
+#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2)
+#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1)
+#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0)
+
+#define STB0899_DMONMSK0 0xf57c
+#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7)
+#define STB0899_DMONMSK0_FREE_6 (1 << 6)
+#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5)
+#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4)
+#define STB0899_DMONMSK0_FREE_3 (1 << 3)
+#define STB0899_DMONMSK0_FREE_2 (1 << 2)
+#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1)
+#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0)
+
+#define STB0899_TSULC 0xf549
+#define STB0899_ULNOSYNCBYTES (0x01 << 7)
+#define STB0899_OFFST_ULNOSYNCBYTES 7
+#define STB0899_WIDTH_ULNOSYNCBYTES 1
+#define STB0899_ULPARITY_ON (0x01 << 6)
+#define STB0899_OFFST_ULPARITY_ON 6
+#define STB0899_WIDTH_ULPARITY_ON 1
+#define STB0899_ULSYNCOUTRS (0x01 << 5)
+#define STB0899_OFFST_ULSYNCOUTRS 5
+#define STB0899_WIDTH_ULSYNCOUTRS 1
+#define STB0899_ULDSS_PACKETS (0x01 << 0)
+#define STB0899_OFFST_ULDSS_PACKETS 0
+#define STB0899_WIDTH_ULDSS_PACKETS 1
+
+#define STB0899_TSLPL 0xf54b
+#define STB0899_LLDVBS2_MODE (0x01 << 4)
+#define STB0899_OFFST_LLDVBS2_MODE 4
+#define STB0899_WIDTH_LLDVBS2_MODE 1
+#define STB0899_LLISSYI_ON (0x01 << 3)
+#define STB0899_OFFST_LLISSYI_ON 3
+#define STB0899_WIDTH_LLISSYI_ON 1
+#define STB0899_LLNPD_ON (0x01 << 2)
+#define STB0899_OFFST_LLNPD_ON 2
+#define STB0899_WIDTH_LLNPD_ON 1
+#define STB0899_LLCRC8_ON (0x01 << 1)
+#define STB0899_OFFST_LLCRC8_ON 1
+#define STB0899_WIDTH_LLCRC8_ON 1
+
+#define STB0899_TSCFGH 0xf54c
+#define STB0899_OUTRS_PS (0x01 << 6)
+#define STB0899_OFFST_OUTRS_PS 6
+#define STB0899_WIDTH_OUTRS_PS 1
+#define STB0899_SYNCBYTE (0x01 << 5)
+#define STB0899_OFFST_SYNCBYTE 5
+#define STB0899_WIDTH_SYNCBYTE 1
+#define STB0899_PFBIT (0x01 << 4)
+#define STB0899_OFFST_PFBIT 4
+#define STB0899_WIDTH_PFBIT 1
+#define STB0899_ERR_BIT (0x01 << 3)
+#define STB0899_OFFST_ERR_BIT 3
+#define STB0899_WIDTH_ERR_BIT 1
+#define STB0899_MPEG (0x01 << 2)
+#define STB0899_OFFST_MPEG 2
+#define STB0899_WIDTH_MPEG 1
+#define STB0899_CLK_POL (0x01 << 1)
+#define STB0899_OFFST_CLK_POL 1
+#define STB0899_WIDTH_CLK_POL 1
+#define STB0899_FORCE0 (0x01 << 0)
+#define STB0899_OFFST_FORCE0 0
+#define STB0899_WIDTH_FORCE0 1
+
+#define STB0899_TSCFGM 0xf54d
+#define STB0899_LLPRIORITY (0x01 << 3)
+#define STB0899_OFFST_LLPRIORIY 3
+#define STB0899_WIDTH_LLPRIORITY 1
+#define STB0899_EN188 (0x01 << 2)
+#define STB0899_OFFST_EN188 2
+#define STB0899_WIDTH_EN188 1
+
+#define STB0899_TSCFGL 0xf54e
+#define STB0899_DEL_ERRPCK (0x01 << 7)
+#define STB0899_OFFST_DEL_ERRPCK 7
+#define STB0899_WIDTH_DEL_ERRPCK 1
+#define STB0899_ERRFLAGSTD (0x01 << 5)
+#define STB0899_OFFST_ERRFLAGSTD 5
+#define STB0899_WIDTH_ERRFLAGSTD 1
+#define STB0899_MPEGERR (0x01 << 4)
+#define STB0899_OFFST_MPEGERR 4
+#define STB0899_WIDTH_MPEGERR 1
+#define STB0899_BCH_CHK (0x01 << 3)
+#define STB0899_OFFST_BCH_CHK 5
+#define STB0899_WIDTH_BCH_CHK 1
+#define STB0899_CRC8CHK (0x01 << 2)
+#define STB0899_OFFST_CRC8CHK 2
+#define STB0899_WIDTH_CRC8CHK 1
+#define STB0899_SPEC_INFO (0x01 << 1)
+#define STB0899_OFFST_SPEC_INFO 1
+#define STB0899_WIDTH_SPEC_INFO 1
+#define STB0899_LOW_PRIO_CLK (0x01 << 0)
+#define STB0899_OFFST_LOW_PRIO_CLK 0
+#define STB0899_WIDTH_LOW_PRIO_CLK 1
+#define STB0899_ERROR_NORM (0x00 << 0)
+#define STB0899_OFFST_ERROR_NORM 0
+#define STB0899_WIDTH_ERROR_NORM 0
+
+#define STB0899_TSOUT 0xf54f
+#define STB0899_RSSYNCDEL 0xf550
+#define STB0899_TSINHDELH 0xf551
+#define STB0899_TSINHDELM 0xf552
+#define STB0899_TSINHDELL 0xf553
+#define STB0899_TSLLSTKM 0xf55a
+#define STB0899_TSLLSTKL 0xf55b
+#define STB0899_TSULSTKM 0xf55c
+#define STB0899_TSULSTKL 0xf55d
+#define STB0899_TSSTATUS 0xf561
+
+#define STB0899_PDELCTRL 0xf600
+#define STB0899_INVERT_RES (0x01 << 7)
+#define STB0899_OFFST_INVERT_RES 7
+#define STB0899_WIDTH_INVERT_RES 1
+#define STB0899_FORCE_ACCEPTED (0x01 << 6)
+#define STB0899_OFFST_FORCE_ACCEPTED 6
+#define STB0899_WIDTH_FORCE_ACCEPTED 1
+#define STB0899_FILTER_EN (0x01 << 5)
+#define STB0899_OFFST_FILTER_EN 5
+#define STB0899_WIDTH_FILTER_EN 1
+#define STB0899_LOCKFALL_THRESH (0x01 << 4)
+#define STB0899_OFFST_LOCKFALL_THRESH 4
+#define STB0899_WIDTH_LOCKFALL_THRESH 1
+#define STB0899_HYST_EN (0x01 << 3)
+#define STB0899_OFFST_HYST_EN 3
+#define STB0899_WIDTH_HYST_EN 1
+#define STB0899_HYST_SWRST (0x01 << 2)
+#define STB0899_OFFST_HYST_SWRST 2
+#define STB0899_WIDTH_HYST_SWRST 1
+#define STB0899_ALGO_EN (0x01 << 1)
+#define STB0899_OFFST_ALGO_EN 1
+#define STB0899_WIDTH_ALGO_EN 1
+#define STB0899_ALGO_SWRST (0x01 << 0)
+#define STB0899_OFFST_ALGO_SWRST 0
+#define STB0899_WIDTH_ALGO_SWRST 1
+
+#define STB0899_PDELCTRL2 0xf601
+#define STB0899_BBHCTRL1 0xf602
+#define STB0899_BBHCTRL2 0xf603
+#define STB0899_HYSTTHRESH 0xf604
+
+#define STB0899_MATCSTM 0xf605
+#define STB0899_MATCSTL 0xf606
+#define STB0899_UPLCSTM 0xf607
+#define STB0899_UPLCSTL 0xf608
+#define STB0899_DFLCSTM 0xf609
+#define STB0899_DFLCSTL 0xf60a
+#define STB0899_SYNCCST 0xf60b
+#define STB0899_SYNCDCSTM 0xf60c
+#define STB0899_SYNCDCSTL 0xf60d
+#define STB0899_ISI_ENTRY 0xf60e
+#define STB0899_ISI_BIT_EN 0xf60f
+#define STB0899_MATSTRM 0xf610
+#define STB0899_MATSTRL 0xf611
+#define STB0899_UPLSTRM 0xf612
+#define STB0899_UPLSTRL 0xf613
+#define STB0899_DFLSTRM 0xf614
+#define STB0899_DFLSTRL 0xf615
+#define STB0899_SYNCSTR 0xf616
+#define STB0899_SYNCDSTRM 0xf617
+#define STB0899_SYNCDSTRL 0xf618
+
+#define STB0899_CFGPDELSTATUS1 0xf619
+#define STB0899_BADDFL (0x01 << 6)
+#define STB0899_OFFST_BADDFL 6
+#define STB0899_WIDTH_BADDFL 1
+#define STB0899_CONTINUOUS_STREAM (0x01 << 5)
+#define STB0899_OFFST_CONTINUOUS_STREAM 5
+#define STB0899_WIDTH_CONTINUOUS_STREAM 1
+#define STB0899_ACCEPTED_STREAM (0x01 << 4)
+#define STB0899_OFFST_ACCEPTED_STREAM 4
+#define STB0899_WIDTH_ACCEPTED_STREAM 1
+#define STB0899_BCH_ERRFLAG (0x01 << 3)
+#define STB0899_OFFST_BCH_ERRFLAG 3
+#define STB0899_WIDTH_BCH_ERRFLAG 1
+#define STB0899_CRCRES (0x01 << 2)
+#define STB0899_OFFST_CRCRES 2
+#define STB0899_WIDTH_CRCRES 1
+#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1)
+#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1
+#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1
+#define STB0899_1STLOCK (0x01 << 0)
+#define STB0899_OFFST_1STLOCK 0
+#define STB0899_WIDTH_1STLOCK 1
+
+#define STB0899_CFGPDELSTATUS2 0xf61a
+#define STB0899_BBFERRORM 0xf61b
+#define STB0899_BBFERRORL 0xf61c
+#define STB0899_UPKTERRORM 0xf61d
+#define STB0899_UPKTERRORL 0xf61e
+
+#define STB0899_TSTCK 0xff10
+
+#define STB0899_TSTRES 0xff11
+#define STB0899_FRESLDPC (0x01 << 7)
+#define STB0899_OFFST_FRESLDPC 7
+#define STB0899_WIDTH_FRESLDPC 1
+#define STB0899_FRESRS (0x01 << 6)
+#define STB0899_OFFST_FRESRS 6
+#define STB0899_WIDTH_FRESRS 1
+#define STB0899_FRESVIT (0x01 << 5)
+#define STB0899_OFFST_FRESVIT 5
+#define STB0899_WIDTH_FRESVIT 1
+#define STB0899_FRESMAS1_2 (0x01 << 4)
+#define STB0899_OFFST_FRESMAS1_2 4
+#define STB0899_WIDTH_FRESMAS1_2 1
+#define STB0899_FRESACS (0x01 << 3)
+#define STB0899_OFFST_FRESACS 3
+#define STB0899_WIDTH_FRESACS 1
+#define STB0899_FRESSYM (0x01 << 2)
+#define STB0899_OFFST_FRESSYM 2
+#define STB0899_WIDTH_FRESSYM 1
+#define STB0899_FRESMAS (0x01 << 1)
+#define STB0899_OFFST_FRESMAS 1
+#define STB0899_WIDTH_FRESMAS 1
+#define STB0899_FRESINT (0x01 << 0)
+#define STB0899_OFFST_FRESINIT 0
+#define STB0899_WIDTH_FRESINIT 1
+
+#define STB0899_TSTOUT 0xff12
+#define STB0899_EN_SIGNATURE (0x01 << 7)
+#define STB0899_OFFST_EN_SIGNATURE 7
+#define STB0899_WIDTH_EN_SIGNATURE 1
+#define STB0899_BCLK_CLK (0x01 << 6)
+#define STB0899_OFFST_BCLK_CLK 6
+#define STB0899_WIDTH_BCLK_CLK 1
+#define STB0899_SGNL_OUT (0x01 << 5)
+#define STB0899_OFFST_SGNL_OUT 5
+#define STB0899_WIDTH_SGNL_OUT 1
+#define STB0899_TS (0x01 << 4)
+#define STB0899_OFFST_TS 4
+#define STB0899_WIDTH_TS 1
+#define STB0899_CTEST (0x01 << 0)
+#define STB0899_OFFST_CTEST 0
+#define STB0899_WIDTH_CTEST 1
+
+#define STB0899_TSTIN 0xff13
+#define STB0899_TEST_IN (0x01 << 7)
+#define STB0899_OFFST_TEST_IN 7
+#define STB0899_WIDTH_TEST_IN 1
+#define STB0899_EN_ADC (0x01 << 6)
+#define STB0899_OFFST_EN_ADC 6
+#define STB0899_WIDTH_ENADC 1
+#define STB0899_SGN_ADC (0x01 << 5)
+#define STB0899_OFFST_SGN_ADC 5
+#define STB0899_WIDTH_SGN_ADC 1
+#define STB0899_BCLK_IN (0x01 << 4)
+#define STB0899_OFFST_BCLK_IN 4
+#define STB0899_WIDTH_BCLK_IN 1
+#define STB0899_JETONIN_MODE (0x01 << 3)
+#define STB0899_OFFST_JETONIN_MODE 3
+#define STB0899_WIDTH_JETONIN_MODE 1
+#define STB0899_BCLK_VALUE (0x01 << 2)
+#define STB0899_OFFST_BCLK_VALUE 2
+#define STB0899_WIDTH_BCLK_VALUE 1
+#define STB0899_SGNRST_T12 (0x01 << 1)
+#define STB0899_OFFST_SGNRST_T12 1
+#define STB0899_WIDTH_SGNRST_T12 1
+#define STB0899_LOWSP_ENAX (0x01 << 0)
+#define STB0899_OFFST_LOWSP_ENAX 0
+#define STB0899_WIDTH_LOWSP_ENAX 1
+
+#define STB0899_TSTSYS 0xff14
+#define STB0899_TSTCHIP 0xff15
+#define STB0899_TSTFREE 0xff16
+#define STB0899_TSTI2C 0xff17
+#define STB0899_BITSPEEDM 0xff1c
+#define STB0899_BITSPEEDL 0xff1d
+#define STB0899_TBUSBIT 0xff1e
+#define STB0899_TSTDIS 0xff24
+#define STB0899_TSTDISRX 0xff25
+#define STB0899_TSTJETON 0xff28
+#define STB0899_TSTDCADJ 0xff40
+#define STB0899_TSTAGC1 0xff41
+#define STB0899_TSTAGC1N 0xff42
+#define STB0899_TSTPOLYPH 0xff48
+#define STB0899_TSTR 0xff49
+#define STB0899_TSTAGC2 0xff4a
+#define STB0899_TSTCTL1 0xff4b
+#define STB0899_TSTCTL2 0xff4c
+#define STB0899_TSTCTL3 0xff4d
+#define STB0899_TSTDEMAP 0xff50
+#define STB0899_TSTDEMAP2 0xff51
+#define STB0899_TSTDEMMON 0xff52
+#define STB0899_TSTRATE 0xff53
+#define STB0899_TSTSELOUT 0xff54
+#define STB0899_TSYNC 0xff55
+#define STB0899_TSTERR 0xff56
+#define STB0899_TSTRAM1 0xff58
+#define STB0899_TSTVSELOUT 0xff59
+#define STB0899_TSTFORCEIN 0xff5a
+#define STB0899_TSTRS1 0xff5c
+#define STB0899_TSTRS2 0xff5d
+#define STB0899_TSTRS3 0xff53
+
+#define STB0899_INTBUFSTATUS 0xf200
+#define STB0899_INTBUFCTRL 0xf201
+#define STB0899_PCKLENUL 0xf55e
+#define STB0899_PCKLENLL 0xf55f
+#define STB0899_RSPCKLEN 0xf560
+
+/* 2 registers */
+#define STB0899_SYNCDCST 0xf60c
+
+/* DiSEqC */
+#define STB0899_DISCNTRL1 0xf0a0
+#define STB0899_TIMOFF (0x01 << 7)
+#define STB0899_OFFST_TIMOFF 7
+#define STB0899_WIDTH_TIMOFF 1
+#define STB0899_DISEQCRESET (0x01 << 6)
+#define STB0899_OFFST_DISEQCRESET 6
+#define STB0899_WIDTH_DISEQCRESET 1
+#define STB0899_TIMCMD (0x03 << 4)
+#define STB0899_OFFST_TIMCMD 4
+#define STB0899_WIDTH_TIMCMD 2
+#define STB0899_DISPRECHARGE (0x01 << 2)
+#define STB0899_OFFST_DISPRECHARGE 2
+#define STB0899_WIDTH_DISPRECHARGE 1
+#define STB0899_DISEQCMODE (0x03 << 0)
+#define STB0899_OFFST_DISEQCMODE 0
+#define STB0899_WIDTH_DISEQCMODE 2
+
+#define STB0899_DISCNTRL2 0xf0a1
+#define STB0899_RECEIVER_ON (0x01 << 7)
+#define STB0899_OFFST_RECEIVER_ON 7
+#define STB0899_WIDTH_RECEIVER_ON 1
+#define STB0899_IGNO_SHORT_22K (0x01 << 6)
+#define STB0899_OFFST_IGNO_SHORT_22K 6
+#define STB0899_WIDTH_IGNO_SHORT_22K 1
+#define STB0899_ONECHIP_TRX (0x01 << 5)
+#define STB0899_OFFST_ONECHIP_TRX 5
+#define STB0899_WIDTH_ONECHIP_TRX 1
+#define STB0899_EXT_ENVELOP (0x01 << 4)
+#define STB0899_OFFST_EXT_ENVELOP 4
+#define STB0899_WIDTH_EXT_ENVELOP 1
+#define STB0899_PIN_SELECT (0x03 << 2)
+#define STB0899_OFFST_PIN_SELCT 2
+#define STB0899_WIDTH_PIN_SELCT 2
+#define STB0899_IRQ_RXEND (0x01 << 1)
+#define STB0899_OFFST_IRQ_RXEND 1
+#define STB0899_WIDTH_IRQ_RXEND 1
+#define STB0899_IRQ_4NBYTES (0x01 << 0)
+#define STB0899_OFFST_IRQ_4NBYTES 0
+#define STB0899_WIDTH_IRQ_4NBYTES 1
+
+#define STB0899_DISRX_ST0 0xf0a4
+#define STB0899_RXEND (0x01 << 7)
+#define STB0899_OFFST_RXEND 7
+#define STB0899_WIDTH_RXEND 1
+#define STB0899_RXACTIVE (0x01 << 6)
+#define STB0899_OFFST_RXACTIVE 6
+#define STB0899_WIDTH_RXACTIVE 1
+#define STB0899_SHORT22K (0x01 << 5)
+#define STB0899_OFFST_SHORT22K 5
+#define STB0899_WIDTH_SHORT22K 1
+#define STB0899_CONTTONE (0x01 << 4)
+#define STB0899_OFFST_CONTTONE 4
+#define STB0899_WIDTH_CONTONE 1
+#define STB0899_4BFIFOREDY (0x01 << 3)
+#define STB0899_OFFST_4BFIFOREDY 3
+#define STB0899_WIDTH_4BFIFOREDY 1
+#define STB0899_FIFOEMPTY (0x01 << 2)
+#define STB0899_OFFST_FIFOEMPTY 2
+#define STB0899_WIDTH_FIFOEMPTY 1
+#define STB0899_ABORTTRX (0x01 << 0)
+#define STB0899_OFFST_ABORTTRX 0
+#define STB0899_WIDTH_ABORTTRX 1
+
+#define STB0899_DISRX_ST1 0xf0a5
+#define STB0899_RXFAIL (0x01 << 7)
+#define STB0899_OFFST_RXFAIL 7
+#define STB0899_WIDTH_RXFAIL 1
+#define STB0899_FIFOPFAIL (0x01 << 6)
+#define STB0899_OFFST_FIFOPFAIL 6
+#define STB0899_WIDTH_FIFOPFAIL 1
+#define STB0899_RXNONBYTES (0x01 << 5)
+#define STB0899_OFFST_RXNONBYTES 5
+#define STB0899_WIDTH_RXNONBYTES 1
+#define STB0899_FIFOOVF (0x01 << 4)
+#define STB0899_OFFST_FIFOOVF 4
+#define STB0899_WIDTH_FIFOOVF 1
+#define STB0899_FIFOBYTENBR (0x0f << 0)
+#define STB0899_OFFST_FIFOBYTENBR 0
+#define STB0899_WIDTH_FIFOBYTENBR 4
+
+#define STB0899_DISPARITY 0xf0a6
+
+#define STB0899_DISFIFO 0xf0a7
+
+#define STB0899_DISSTATUS 0xf0a8
+#define STB0899_FIFOFULL (0x01 << 6)
+#define STB0899_OFFST_FIFOFULL 6
+#define STB0899_WIDTH_FIFOFULL 1
+#define STB0899_TXIDLE (0x01 << 5)
+#define STB0899_OFFST_TXIDLE 5
+#define STB0899_WIDTH_TXIDLE 1
+#define STB0899_GAPBURST (0x01 << 4)
+#define STB0899_OFFST_GAPBURST 4
+#define STB0899_WIDTH_GAPBURST 1
+#define STB0899_TXFIFOBYTES (0x0f << 0)
+#define STB0899_OFFST_TXFIFOBYTES 0
+#define STB0899_WIDTH_TXFIFOBYTES 4
+#define STB0899_DISF22 0xf0a9
+
+#define STB0899_DISF22RX 0xf0aa
+
+/* General Purpose */
+#define STB0899_SYSREG 0xf101
+#define STB0899_ACRPRESC 0xf110
+#define STB0899_OFFST_RSVD2 7
+#define STB0899_WIDTH_RSVD2 1
+#define STB0899_OFFST_ACRPRESC 4
+#define STB0899_WIDTH_ACRPRESC 3
+#define STB0899_OFFST_RSVD1 3
+#define STB0899_WIDTH_RSVD1 1
+#define STB0899_OFFST_ACRPRESC2 0
+#define STB0899_WIDTH_ACRPRESC2 3
+
+#define STB0899_ACRDIV1 0xf111
+#define STB0899_ACRDIV2 0xf112
+#define STB0899_DACR1 0xf113
+#define STB0899_DACR2 0xf114
+#define STB0899_OUTCFG 0xf11c
+#define STB0899_MODECFG 0xf11d
+#define STB0899_NCOARSE 0xf1b3
+
+#define STB0899_SYNTCTRL 0xf1b6
+#define STB0899_STANDBY (0x01 << 7)
+#define STB0899_OFFST_STANDBY 7
+#define STB0899_WIDTH_STANDBY 1
+#define STB0899_BYPASSPLL (0x01 << 6)
+#define STB0899_OFFST_BYPASSPLL 6
+#define STB0899_WIDTH_BYPASSPLL 1
+#define STB0899_SEL1XRATIO (0x01 << 5)
+#define STB0899_OFFST_SEL1XRATIO 5
+#define STB0899_WIDTH_SEL1XRATIO 1
+#define STB0899_SELOSCI (0x01 << 1)
+#define STB0899_OFFST_SELOSCI 1
+#define STB0899_WIDTH_SELOSCI 1
+
+#define STB0899_FILTCTRL 0xf1b7
+#define STB0899_SYSCTRL 0xf1b8
+
+#define STB0899_STOPCLK1 0xf1c2
+#define STB0899_STOP_CKINTBUF108 (0x01 << 7)
+#define STB0899_OFFST_STOP_CKINTBUF108 7
+#define STB0899_WIDTH_STOP_CKINTBUF108 1
+#define STB0899_STOP_CKINTBUF216 (0x01 << 6)
+#define STB0899_OFFST_STOP_CKINTBUF216 6
+#define STB0899_WIDTH_STOP_CKINTBUF216 1
+#define STB0899_STOP_CHK8PSK (0x01 << 5)
+#define STB0899_OFFST_STOP_CHK8PSK 5
+#define STB0899_WIDTH_STOP_CHK8PSK 1
+#define STB0899_STOP_CKFEC108 (0x01 << 4)
+#define STB0899_OFFST_STOP_CKFEC108 4
+#define STB0899_WIDTH_STOP_CKFEC108 1
+#define STB0899_STOP_CKFEC216 (0x01 << 3)
+#define STB0899_OFFST_STOP_CKFEC216 3
+#define STB0899_WIDTH_STOP_CKFEC216 1
+#define STB0899_STOP_CKCORE216 (0x01 << 2)
+#define STB0899_OFFST_STOP_CKCORE216 2
+#define STB0899_WIDTH_STOP_CKCORE216 1
+#define STB0899_STOP_CKADCI108 (0x01 << 1)
+#define STB0899_OFFST_STOP_CKADCI108 1
+#define STB0899_WIDTH_STOP_CKADCI108 1
+#define STB0899_STOP_INVCKADCI108 (0x01 << 0)
+#define STB0899_OFFST_STOP_INVCKADCI108 0
+#define STB0899_WIDTH_STOP_INVCKADCI108 1
+
+#define STB0899_STOPCLK2 0xf1c3
+#define STB0899_STOP_CKS2DMD108 (0x01 << 2)
+#define STB0899_OFFST_STOP_CKS2DMD108 2
+#define STB0899_WIDTH_STOP_CKS2DMD108 1
+#define STB0899_STOP_CKPKDLIN108 (0x01 << 1)
+#define STB0899_OFFST_STOP_CKPKDLIN108 1
+#define STB0899_WIDTH_STOP_CKPKDLIN108 1
+#define STB0899_STOP_CKPKDLIN216 (0x01 << 0)
+#define STB0899_OFFST_STOP_CKPKDLIN216 0
+#define STB0899_WIDTH_STOP_CKPKDLIN216 1
+
+#define STB0899_TSTTNR1 0xf1e0
+#define STB0899_BYPASS_ADC (0x01 << 7)
+#define STB0899_OFFST_BYPASS_ADC 7
+#define STB0899_WIDTH_BYPASS_ADC 1
+#define STB0899_INVADCICKOUT (0x01 << 6)
+#define STB0899_OFFST_INVADCICKOUT 6
+#define STB0899_WIDTH_INVADCICKOUT 1
+#define STB0899_ADCTEST_VOLTAGE (0x03 << 4)
+#define STB0899_OFFST_ADCTEST_VOLTAGE 4
+#define STB0899_WIDTH_ADCTEST_VOLTAGE 1
+#define STB0899_ADC_RESET (0x01 << 3)
+#define STB0899_OFFST_ADC_RESET 3
+#define STB0899_WIDTH_ADC_RESET 1
+#define STB0899_TSTTNR1_2 (0x01 << 2)
+#define STB0899_OFFST_TSTTNR1_2 2
+#define STB0899_WIDTH_TSTTNR1_2 1
+#define STB0899_ADCPON (0x01 << 1)
+#define STB0899_OFFST_ADCPON 1
+#define STB0899_WIDTH_ADCPON 1
+#define STB0899_ADCIN_MODE (0x01 << 0)
+#define STB0899_OFFST_ADCIN_MODE 0
+#define STB0899_WIDTH_ADCIN_MODE 1
+
+#define STB0899_TSTTNR2 0xf1e1
+#define STB0899_TSTTNR2_7 (0x01 << 7)
+#define STB0899_OFFST_TSTTNR2_7 7
+#define STB0899_WIDTH_TSTTNR2_7 1
+#define STB0899_NOT_DISRX_WIRED (0x01 << 6)
+#define STB0899_OFFST_NOT_DISRX_WIRED 6
+#define STB0899_WIDTH_NOT_DISRX_WIRED 1
+#define STB0899_DISEQC_DCURRENT (0x01 << 5)
+#define STB0899_OFFST_DISEQC_DCURRENT 5
+#define STB0899_WIDTH_DISEQC_DCURRENT 1
+#define STB0899_DISEQC_ZCURRENT (0x01 << 4)
+#define STB0899_OFFST_DISEQC_ZCURRENT 4
+#define STB0899_WIDTH_DISEQC_ZCURRENT 1
+#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2)
+#define STB0899_OFFST_DISEQC_SINC_SOURCE 2
+#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2
+#define STB0899_SELIQSRC (0x03 << 0)
+#define STB0899_OFFST_SELIQSRC 0
+#define STB0899_WIDTH_SELIQSRC 2
+
+#define STB0899_TSTTNR3 0xf1e2
+
+#define STB0899_I2CCFG 0xf129
+#define STB0899_I2CCFGRSVD (0x0f << 4)
+#define STB0899_OFFST_I2CCFGRSVD 4
+#define STB0899_WIDTH_I2CCFGRSVD 4
+#define STB0899_I2CFASTMODE (0x01 << 3)
+#define STB0899_OFFST_I2CFASTMODE 3
+#define STB0899_WIDTH_I2CFASTMODE 1
+#define STB0899_STATUSWR (0x01 << 2)
+#define STB0899_OFFST_STATUSWR 2
+#define STB0899_WIDTH_STATUSWR 1
+#define STB0899_I2CADDRINC (0x03 << 0)
+#define STB0899_OFFST_I2CADDRINC 0
+#define STB0899_WIDTH_I2CADDRINC 2
+
+#define STB0899_I2CRPT 0xf12a
+#define STB0899_I2CTON (0x01 << 7)
+#define STB0899_OFFST_I2CTON 7
+#define STB0899_WIDTH_I2CTON 1
+#define STB0899_ENARPTLEVEL (0x01 << 6)
+#define STB0899_OFFST_ENARPTLEVEL 6
+#define STB0899_WIDTH_ENARPTLEVEL 2
+#define STB0899_SCLTDELAY (0x01 << 3)
+#define STB0899_OFFST_SCLTDELAY 3
+#define STB0899_WIDTH_SCLTDELAY 1
+#define STB0899_STOPENA (0x01 << 2)
+#define STB0899_OFFST_STOPENA 2
+#define STB0899_WIDTH_STOPENA 1
+#define STB0899_STOPSDAT2SDA (0x01 << 1)
+#define STB0899_OFFST_STOPSDAT2SDA 1
+#define STB0899_WIDTH_STOPSDAT2SDA 1
+
+#define STB0899_IOPVALUE8 0xf136
+#define STB0899_IOPVALUE7 0xf137
+#define STB0899_IOPVALUE6 0xf138
+#define STB0899_IOPVALUE5 0xf139
+#define STB0899_IOPVALUE4 0xf13a
+#define STB0899_IOPVALUE3 0xf13b
+#define STB0899_IOPVALUE2 0xf13c
+#define STB0899_IOPVALUE1 0xf13d
+#define STB0899_IOPVALUE0 0xf13e
+
+#define STB0899_GPIO00CFG 0xf140
+
+#define STB0899_GPIO01CFG 0xf141
+#define STB0899_GPIO02CFG 0xf142
+#define STB0899_GPIO03CFG 0xf143
+#define STB0899_GPIO04CFG 0xf144
+#define STB0899_GPIO05CFG 0xf145
+#define STB0899_GPIO06CFG 0xf146
+#define STB0899_GPIO07CFG 0xf147
+#define STB0899_GPIO08CFG 0xf148
+#define STB0899_GPIO09CFG 0xf149
+#define STB0899_GPIO10CFG 0xf14a
+#define STB0899_GPIO11CFG 0xf14b
+#define STB0899_GPIO12CFG 0xf14c
+#define STB0899_GPIO13CFG 0xf14d
+#define STB0899_GPIO14CFG 0xf14e
+#define STB0899_GPIO15CFG 0xf14f
+#define STB0899_GPIO16CFG 0xf150
+#define STB0899_GPIO17CFG 0xf151
+#define STB0899_GPIO18CFG 0xf152
+#define STB0899_GPIO19CFG 0xf153
+#define STB0899_GPIO20CFG 0xf154
+
+#define STB0899_SDATCFG 0xf155
+#define STB0899_SCLTCFG 0xf156
+#define STB0899_AGCRFCFG 0xf157
+#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */
+#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */
+#define STB0899_DIRCLKCFG 0xf15a
+#define STB0899_CLKOUT27CFG 0xf15b
+#define STB0899_STDBYCFG 0xf15c
+#define STB0899_CS0CFG 0xf15d
+#define STB0899_CS1CFG 0xf15e
+#define STB0899_DISEQCOCFG 0xf15f
+
+#define STB0899_GPIO32CFG 0xf160
+#define STB0899_GPIO33CFG 0xf161
+#define STB0899_GPIO34CFG 0xf162
+#define STB0899_GPIO35CFG 0xf163
+#define STB0899_GPIO36CFG 0xf164
+#define STB0899_GPIO37CFG 0xf165
+#define STB0899_GPIO38CFG 0xf166
+#define STB0899_GPIO39CFG 0xf167
+
+#define STB0899_IRQSTATUS_3 0xf120
+#define STB0899_IRQSTATUS_2 0xf121
+#define STB0899_IRQSTATUS_1 0xf122
+#define STB0899_IRQSTATUS_0 0xf123
+
+#define STB0899_IRQMSK_3 0xf124
+#define STB0899_IRQMSK_2 0xf125
+#define STB0899_IRQMSK_1 0xf126
+#define STB0899_IRQMSK_0 0xf127
+
+#define STB0899_IRQCFG 0xf128
+
+#define STB0899_GHOSTREG 0xf000
+
+#define STB0899_S2DEMOD 0xf3fc
+#define STB0899_S2FEC 0xfafc
+
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c
new file mode 100644
index 000000000000..ff39275ab49c
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100.c
@@ -0,0 +1,545 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include "dvb_frontend.h"
+#include "stb6100.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+
+
+#define FE_ERROR 0
+#define FE_NOTICE 1
+#define FE_INFO 2
+#define FE_DEBUG 3
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((x > FE_ERROR) && (x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_NOTICE) && (x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_INFO) && (x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > FE_DEBUG) && (x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (x > y) \
+ printk(format, ##arg); \
+ } \
+} while(0)
+
+struct stb6100_lkup {
+ u32 val_low;
+ u32 val_high;
+ u8 reg;
+};
+
+static int stb6100_release(struct dvb_frontend *fe);
+
+static const struct stb6100_lkup lkup[] = {
+ { 0, 950000, 0x0a },
+ { 950000, 1000000, 0x0a },
+ { 1000000, 1075000, 0x0c },
+ { 1075000, 1200000, 0x00 },
+ { 1200000, 1300000, 0x01 },
+ { 1300000, 1370000, 0x02 },
+ { 1370000, 1470000, 0x04 },
+ { 1470000, 1530000, 0x05 },
+ { 1530000, 1650000, 0x06 },
+ { 1650000, 1800000, 0x08 },
+ { 1800000, 1950000, 0x0a },
+ { 1950000, 2150000, 0x0c },
+ { 2150000, 9999999, 0x0c },
+ { 0, 0, 0x00 }
+};
+
+/* Register names for easy debugging. */
+static const char *stb6100_regnames[] = {
+ [STB6100_LD] = "LD",
+ [STB6100_VCO] = "VCO",
+ [STB6100_NI] = "NI",
+ [STB6100_NF_LSB] = "NF",
+ [STB6100_K] = "K",
+ [STB6100_G] = "G",
+ [STB6100_F] = "F",
+ [STB6100_DLB] = "DLB",
+ [STB6100_TEST1] = "TEST1",
+ [STB6100_FCCK] = "FCCK",
+ [STB6100_LPEN] = "LPEN",
+ [STB6100_TEST3] = "TEST3",
+};
+
+/* Template for normalisation, i.e. setting unused or undocumented
+ * bits as required according to the documentation.
+ */
+struct stb6100_regmask {
+ u8 mask;
+ u8 set;
+};
+
+static const struct stb6100_regmask stb6100_template[] = {
+ [STB6100_LD] = { 0xff, 0x00 },
+ [STB6100_VCO] = { 0xff, 0x00 },
+ [STB6100_NI] = { 0xff, 0x00 },
+ [STB6100_NF_LSB] = { 0xff, 0x00 },
+ [STB6100_K] = { 0xc7, 0x38 },
+ [STB6100_G] = { 0xef, 0x10 },
+ [STB6100_F] = { 0x1f, 0xc0 },
+ [STB6100_DLB] = { 0x38, 0xc4 },
+ [STB6100_TEST1] = { 0x00, 0x8f },
+ [STB6100_FCCK] = { 0x40, 0x0d },
+ [STB6100_LPEN] = { 0xf0, 0x0b },
+ [STB6100_TEST3] = { 0x00, 0xde },
+};
+
+static void stb6100_normalise_regs(u8 regs[])
+{
+ int i;
+
+ for (i = 0; i < STB6100_NUMREGS; i++)
+ regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set;
+}
+
+static int stb6100_read_regs(struct stb6100_state *state, u8 regs[])
+{
+ int rc;
+ struct i2c_msg msg = {
+ .addr = state->config->tuner_address,
+ .flags = I2C_M_RD,
+ .buf = regs,
+ .len = STB6100_NUMREGS
+ };
+
+ rc = i2c_transfer(state->i2c, &msg, 1);
+ if (unlikely(rc != 1)) {
+ dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]",
+ state->config->tuner_address, rc);
+
+ return -EREMOTEIO;
+ }
+ if (unlikely(verbose > FE_DEBUG)) {
+ int i;
+
+ dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address);
+ for (i = 0; i < STB6100_NUMREGS; i++)
+ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]);
+ }
+ return 0;
+}
+
+static int stb6100_read_reg(struct stb6100_state *state, u8 reg)
+{
+ u8 regs[STB6100_NUMREGS];
+ int rc;
+
+ if (unlikely(reg >= STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
+ return -EINVAL;
+ }
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+ return (unsigned int)regs[reg];
+}
+
+static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len)
+{
+ int rc;
+ u8 cmdbuf[len + 1];
+ struct i2c_msg msg = {
+ .addr = state->config->tuner_address,
+ .flags = 0,
+ .buf = cmdbuf,
+ .len = len + 1
+ };
+
+ if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d",
+ start, len);
+ return -EINVAL;
+ }
+ memcpy(&cmdbuf[1], buf, len);
+ cmdbuf[0] = start;
+
+ if (unlikely(verbose > FE_DEBUG)) {
+ int i;
+
+ dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len);
+ for (i = 0; i < len; i++)
+ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]);
+ }
+ rc = i2c_transfer(state->i2c, &msg, 1);
+ if (unlikely(rc != 1)) {
+ dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]",
+ (unsigned int)state->config->tuner_address, start, len, rc);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data)
+{
+ if (unlikely(reg >= STB6100_NUMREGS)) {
+ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
+ return -EREMOTEIO;
+ }
+ data = (data & stb6100_template[reg].mask) | stb6100_template[reg].set;
+ return stb6100_write_reg_range(state, &data, reg, 1);
+}
+
+static int stb6100_write_regs(struct stb6100_state *state, u8 regs[])
+{
+ stb6100_normalise_regs(regs);
+ return stb6100_write_reg_range(state, &regs[1], 1, STB6100_NUMREGS - 1);
+}
+
+static int stb6100_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ int rc;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ if ((rc = stb6100_read_reg(state, STB6100_LD)) < 0)
+ return rc;
+
+ return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0;
+}
+
+static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ int rc;
+ u8 f;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ if ((rc = stb6100_read_reg(state, STB6100_F)) < 0)
+ return rc;
+ f = rc & STB6100_F_F;
+
+ state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */
+
+ *bandwidth = state->bandwidth = state->status.bandwidth * 1000;
+ dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth);
+ return 0;
+}
+
+static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
+{
+ u32 tmp;
+ int rc;
+ struct stb6100_state *state = fe->tuner_priv;
+
+ dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth);
+
+ bandwidth /= 2; /* ZIF */
+
+ if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */
+ tmp = 31;
+ else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */
+ tmp = 0;
+ else /* if 5 < bw/2 < 36 */
+ tmp = (bandwidth + 500000) / 1000000 - 5;
+
+ /* Turn on LPF bandwidth setting clock control,
+ * set bandwidth, wait 10ms, turn off.
+ */
+ if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK)) < 0)
+ return rc;
+ if ((rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp)) < 0)
+ return rc;
+ msleep(1);
+ if ((rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d)) < 0)
+ return rc;
+
+ return 0;
+}
+
+static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ int rc;
+ u32 nint, nfrac, fvco;
+ int psd2, odiv;
+ struct stb6100_state *state = fe->tuner_priv;
+ u8 regs[STB6100_NUMREGS];
+
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+
+ odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT;
+ psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT;
+ nint = regs[STB6100_NI];
+ nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB];
+ fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2);
+ *frequency = state->frequency = fvco >> (odiv + 1);
+
+ dprintk(verbose, FE_DEBUG, 1,
+ "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u",
+ state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac);
+ return 0;
+}
+
+
+static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ int rc;
+ const struct stb6100_lkup *ptr;
+ struct stb6100_state *state = fe->tuner_priv;
+ struct dvb_frontend_parameters p;
+
+ u32 srate = 0, fvco, nint, nfrac;
+ u8 regs[STB6100_NUMREGS];
+ u8 g, psd2, odiv;
+
+ if ((rc = stb6100_read_regs(state, regs)) < 0)
+ return rc;
+
+ if (fe->ops.get_frontend) {
+ dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters");
+ fe->ops.get_frontend(fe, &p);
+ }
+ srate = p.u.qpsk.symbol_rate;
+
+ regs[STB6100_DLB] = 0xdc;
+ /* Disable LPEN */
+ regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL Loop disabled */
+
+ if ((rc = stb6100_write_regs(state, regs)) < 0)
+ return rc;
+
+ /* Baseband gain. */
+ if (srate >= 15000000)
+ g = 9; // +4 dB
+ else if (srate >= 5000000)
+ g = 11; // +8 dB
+ else
+ g = 14; // +14 dB
+
+ regs[STB6100_G] = (regs[STB6100_G] & ~STB6100_G_G) | g;
+ regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */
+ regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */
+
+ /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */
+ if (frequency <= 1075000)
+ odiv = 1;
+ else
+ odiv = 0;
+ regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_ODIV) | (odiv << STB6100_VCO_ODIV_SHIFT);
+
+ if ((frequency > 1075000) && (frequency <= 1325000))
+ psd2 = 0;
+ else
+ psd2 = 1;
+ regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT);
+
+ /* OSM */
+ for (ptr = lkup;
+ (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high);
+ ptr++);
+ if (ptr->val_high == 0) {
+ printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency);
+ return -EINVAL;
+ }
+ regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg;
+
+ /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */
+ fvco = frequency << (1 + odiv);
+ /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */
+ nint = fvco / (state->reference << psd2);
+ /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */
+ nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference;
+ dprintk(verbose, FE_DEBUG, 1,
+ "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u",
+ frequency, srate, (unsigned int)g, (unsigned int)odiv,
+ (unsigned int)psd2, state->reference,
+ ptr->reg, fvco, nint, nfrac);
+ regs[STB6100_NI] = nint;
+ regs[STB6100_NF_LSB] = nfrac;
+ regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB);
+ regs[STB6100_VCO] |= STB6100_VCO_OSCH; /* VCO search enabled */
+ regs[STB6100_VCO] |= STB6100_VCO_OCK; /* VCO search clock off */
+ regs[STB6100_FCCK] |= STB6100_FCCK_FCCK; /* LPF BW setting clock enabled */
+ regs[STB6100_LPEN] &= ~STB6100_LPEN_LPEN; /* PLL loop disabled */
+ /* Power up. */
+ regs[STB6100_LPEN] |= STB6100_LPEN_SYNP | STB6100_LPEN_OSCP | STB6100_LPEN_BEN;
+
+ msleep(2);
+ if ((rc = stb6100_write_regs(state, regs)) < 0)
+ return rc;
+
+ msleep(2);
+ regs[STB6100_LPEN] |= STB6100_LPEN_LPEN; /* PLL loop enabled */
+ if ((rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN])) < 0)
+ return rc;
+
+ regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */
+ if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0)
+ return rc;
+
+ msleep(10); /* wait for LO to lock */
+ regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */
+ regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */
+ if ((rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO])) < 0)
+ return rc;
+ regs[STB6100_FCCK] &= ~STB6100_FCCK_FCCK; /* LPF BW clock disabled */
+ stb6100_normalise_regs(regs);
+ if ((rc = stb6100_write_reg_range(state, &regs[1], 1, STB6100_NUMREGS - 3)) < 0)
+ return rc;
+
+ msleep(100);
+
+ return 0;
+}
+
+static int stb6100_sleep(struct dvb_frontend *fe)
+{
+ /* TODO: power down */
+ return 0;
+}
+
+static int stb6100_init(struct dvb_frontend *fe)
+{
+ struct stb6100_state *state = fe->tuner_priv;
+ struct tuner_state *status = &state->status;
+
+ status->tunerstep = 125000;
+ status->ifreq = 0;
+ status->refclock = 27000000; /* Hz */
+ status->iqsense = 1;
+ status->bandwidth = 36000; /* kHz */
+ state->bandwidth = status->bandwidth * 1000; /* MHz */
+ state->reference = status->refclock / 1000; /* kHz */
+
+ /* Set default bandwidth. */
+ return stb6100_set_bandwidth(fe, status->bandwidth);
+}
+
+static int stb6100_get_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *state)
+{
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ stb6100_get_frequency(fe, &state->frequency);
+ break;
+ case DVBFE_TUNER_TUNERSTEP:
+ break;
+ case DVBFE_TUNER_IFFREQ:
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ stb6100_get_bandwidth(fe, &state->bandwidth);
+ break;
+ case DVBFE_TUNER_REFCLOCK:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int stb6100_set_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *state)
+{
+ struct stb6100_state *tstate = fe->tuner_priv;
+
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ stb6100_set_frequency(fe, state->frequency);
+ tstate->frequency = state->frequency;
+ break;
+ case DVBFE_TUNER_TUNERSTEP:
+ break;
+ case DVBFE_TUNER_IFFREQ:
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ stb6100_set_bandwidth(fe, state->bandwidth);
+ tstate->bandwidth = state->bandwidth;
+ break;
+ case DVBFE_TUNER_REFCLOCK:
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct dvb_tuner_ops stb6100_ops = {
+ .info = {
+ .name = "STB6100 Silicon Tuner",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 0,
+ },
+
+ .init = stb6100_init,
+ .sleep = stb6100_sleep,
+ .get_status = stb6100_get_status,
+ .get_state = stb6100_get_state,
+ .set_state = stb6100_set_state,
+ .release = stb6100_release
+};
+
+struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stb6100_state *state = NULL;
+
+ state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend = fe;
+ state->reference = config->refclock / 1000; /* kHz */
+ fe->tuner_priv = state;
+ fe->ops.tuner_ops = stb6100_ops;
+
+ printk("%s: Attaching STB6100 \n", __func__);
+ return fe;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static int stb6100_release(struct dvb_frontend *fe)
+{
+ struct stb6100_state *state = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(state);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(stb6100_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STB6100 Silicon tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb/frontends/stb6100.h
new file mode 100644
index 000000000000..395d056599a6
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100.h
@@ -0,0 +1,115 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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 __STB_6100_REG_H
+#define __STB_6100_REG_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STB6100_LD 0x00
+#define STB6100_LD_LOCK (1 << 0)
+
+#define STB6100_VCO 0x01
+#define STB6100_VCO_OSCH (0x01 << 7)
+#define STB6100_VCO_OSCH_SHIFT 7
+#define STB6100_VCO_OCK (0x03 << 5)
+#define STB6100_VCO_OCK_SHIFT 5
+#define STB6100_VCO_ODIV (0x01 << 4)
+#define STB6100_VCO_ODIV_SHIFT 4
+#define STB6100_VCO_OSM (0x0f << 0)
+
+#define STB6100_NI 0x02
+#define STB6100_NF_LSB 0x03
+
+#define STB6100_K 0x04
+#define STB6100_K_PSD2 (0x01 << 2)
+#define STB6100_K_PSD2_SHIFT 2
+#define STB6100_K_NF_MSB (0x03 << 0)
+
+#define STB6100_G 0x05
+#define STB6100_G_G (0x0f << 0)
+#define STB6100_G_GCT (0x07 << 5)
+
+#define STB6100_F 0x06
+#define STB6100_F_F (0x1f << 0)
+
+#define STB6100_DLB 0x07
+
+#define STB6100_TEST1 0x08
+
+#define STB6100_FCCK 0x09
+#define STB6100_FCCK_FCCK (0x01 << 6)
+
+#define STB6100_LPEN 0x0a
+#define STB6100_LPEN_LPEN (0x01 << 4)
+#define STB6100_LPEN_SYNP (0x01 << 5)
+#define STB6100_LPEN_OSCP (0x01 << 6)
+#define STB6100_LPEN_BEN (0x01 << 7)
+
+#define STB6100_TEST3 0x0b
+
+#define STB6100_NUMREGS 0x0c
+
+
+#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \
+ ((y <= val) && (val <= x)) ? 1 : 0)
+
+#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0)
+
+struct stb6100_config {
+ u8 tuner_address;
+ u32 refclock;
+};
+
+struct stb6100_state {
+ struct i2c_adapter *i2c;
+
+ const struct stb6100_config *config;
+ struct dvb_tuner_ops ops;
+ struct dvb_frontend *frontend;
+ struct tuner_state status;
+
+ u32 frequency;
+ u32 srate;
+ u32 bandwidth;
+ u32 reference;
+};
+
+#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe,
+ struct stb6100_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_STB6100
+
+#endif
diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb/frontends/stb6100_cfg.h
new file mode 100644
index 000000000000..d3133405dc03
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100_cfg.h
@@ -0,0 +1,108 @@
+/*
+ STB6100 Silicon Tuner
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute 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.
+*/
+
+static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *frequency = t_state.frequency;
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ }
+ return 0;
+}
+
+static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.frequency = frequency;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ return 0;
+}
+
+static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = &fe->ops;
+ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *bandwidth = t_state.bandwidth;
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
+
+static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.bandwidth = bandwidth;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c
new file mode 100644
index 000000000000..b6d177799104
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261.c
@@ -0,0 +1,230 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@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.
+*/
+
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dvb_frontend.h"
+#include "tda8261.h"
+
+struct tda8261_state {
+ struct dvb_frontend *fe;
+ struct i2c_adapter *i2c;
+ const struct tda8261_config *config;
+
+ /* state cache */
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int tda8261_read(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 2 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ printk("%s: read error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_write(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ printk("%s: write error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ u8 result = 0;
+ int err = 0;
+
+ *status = 0;
+
+ if ((err = tda8261_read(state, &result)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if ((result >> 6) & 0x01) {
+ printk("%s: Tuner Phase Locked\n", __func__);
+ *status = 1;
+ }
+
+ return err;
+}
+
+static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
+static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
+
+static int tda8261_get_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ int err = 0;
+
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ tstate->frequency = state->frequency;
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */
+ break;
+ default:
+ printk("%s: Unknown parameter (param=%d)\n", __func__, param);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int tda8261_set_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ const struct tda8261_config *config = state->config;
+ u32 frequency, N, status = 0;
+ u8 buf[4];
+ int err = 0;
+
+ if (param & DVBFE_TUNER_FREQUENCY) {
+ /**
+ * N = Max VCO Frequency / Channel Spacing
+ * Max VCO Frequency = VCO frequency + (channel spacing - 1)
+ * (to account for half channel spacing on either side)
+ */
+ frequency = tstate->frequency;
+ if ((frequency < 950000) || (frequency > 2150000)) {
+ printk("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
+ return -EINVAL;
+ }
+ N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
+ printk("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
+ __func__, config->step_size, div_tab[config->step_size], N, N);
+
+ buf[0] = (N >> 8) & 0xff;
+ buf[1] = N & 0xff;
+ buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
+
+ if (frequency < 1450000)
+ buf[3] = 0x00;
+ if (frequency < 2000000)
+ buf[3] = 0x40;
+ if (frequency < 2150000)
+ buf[3] = 0x80;
+
+ /* Set params */
+ if ((err = tda8261_write(state, buf)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ /* sleep for some time */
+ printk("%s: Waiting to Phase LOCK\n", __func__);
+ msleep(20);
+ /* check status */
+ if ((err = tda8261_get_status(fe, &status)) < 0) {
+ printk("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if (status == 1) {
+ printk("%s: Tuner Phase locked: status=%d\n", __func__, status);
+ state->frequency = frequency; /* cache successful state */
+ } else {
+ printk("%s: No Phase lock: status=%d\n", __func__, status);
+ }
+ } else {
+ printk("%s: Unknown parameter (param=%d)\n", __func__, param);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tda8261_release(struct dvb_frontend *fe)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(state);
+ return 0;
+}
+
+static struct dvb_tuner_ops tda8261_ops = {
+
+ .info = {
+ .name = "TDA8261",
+// .tuner_name = NULL,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 0
+ },
+
+ .set_state = tda8261_set_state,
+ .get_state = tda8261_get_state,
+ .get_status = tda8261_get_status,
+ .release = tda8261_release
+};
+
+struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct tda8261_state *state = NULL;
+
+ if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL)
+ goto exit;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->fe = fe;
+ fe->tuner_priv = state;
+ fe->ops.tuner_ops = tda8261_ops;
+
+ fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
+// fe->ops.tuner_ops.tuner_name = &config->buf;
+
+// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n",
+// __func__, fe->ops.tuner_ops.tuner_name);
+ printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
+
+ return fe;
+
+exit:
+ kfree(state);
+ return NULL;
+}
+
+EXPORT_SYMBOL(tda8261_attach);
+MODULE_PARM_DESC(verbose, "Set verbosity level");
+
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb/frontends/tda8261.h
new file mode 100644
index 000000000000..006e45351b94
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261.h
@@ -0,0 +1,55 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@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.
+*/
+
+#ifndef __TDA8261_H
+#define __TDA8261_H
+
+enum tda8261_step {
+ TDA8261_STEP_2000 = 0, /* 2000 kHz */
+ TDA8261_STEP_1000, /* 1000 kHz */
+ TDA8261_STEP_500, /* 500 kHz */
+ TDA8261_STEP_250, /* 250 kHz */
+ TDA8261_STEP_125 /* 125 kHz */
+};
+
+struct tda8261_config {
+// u8 buf[16];
+ u8 addr;
+ enum tda8261_step step_size;
+};
+
+#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE))
+
+extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c);
+
+#else
+
+static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif //CONFIG_DVB_TDA8261
+
+#endif// __TDA8261_H
diff --git a/drivers/media/dvb/frontends/tda8261_cfg.h b/drivers/media/dvb/frontends/tda8261_cfg.h
new file mode 100644
index 000000000000..1af1ee49b542
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8261_cfg.h
@@ -0,0 +1,84 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@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.
+*/
+
+static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *frequency = t_state.frequency;
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ }
+ return 0;
+}
+
+static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state t_state;
+ int err = 0;
+
+ t_state.frequency = frequency;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ }
+ printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ return 0;
+}
+
+static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = &fe->ops;
+ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
+ struct tuner_state t_state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) {
+ printk("%s: Invalid parameter\n", __func__);
+ return err;
+ }
+ *bandwidth = t_state.bandwidth;
+ }
+ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 36a5a1c101d5..5506f80e180e 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -220,15 +220,18 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
/* These are extrapolated from the 7 and 8MHz values */
zl10353_single_write(fe, MCLK_RATIO, 0x97);
zl10353_single_write(fe, 0x64, 0x34);
+ zl10353_single_write(fe, 0xcc, 0xdd);
break;
case BANDWIDTH_7_MHZ:
zl10353_single_write(fe, MCLK_RATIO, 0x86);
zl10353_single_write(fe, 0x64, 0x35);
+ zl10353_single_write(fe, 0xcc, 0x73);
break;
case BANDWIDTH_8_MHZ:
default:
zl10353_single_write(fe, MCLK_RATIO, 0x75);
zl10353_single_write(fe, 0x64, 0x36);
+ zl10353_single_write(fe, 0xcc, 0x73);
}
zl10353_calc_nominal_rate(fe, op->bandwidth, &nominal_rate);
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
index a9653c63f4db..d101b304e9b0 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/dvb/pluto2/pluto2.c
@@ -560,8 +560,7 @@ static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac)
mac[4] = (val >> 8) & 0xff;
mac[5] = (val >> 0) & 0xff;
- dev_info(&pluto->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ dev_info(&pluto->pdev->dev, "MAC %pM\n", mac);
}
static int __devinit pluto_read_serial(struct pluto *pluto)
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index e98d6caf2c23..fd62e0b85621 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -38,6 +38,16 @@ struct usb_device_id smsusb_id_table[] = {
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
{ USB_DEVICE(0x2040, 0x1801),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
+ { USB_DEVICE(0x2040, 0x2000),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2009),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
+ { USB_DEVICE(0x2040, 0x200a),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2010),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2019),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
{ USB_DEVICE(0x2040, 0x5500),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0x5510),
@@ -96,6 +106,21 @@ static struct sms_board sms_boards[] = {
.name = "Hauppauge WinTV MiniStick",
.type = SMS_NOVA_B0,
.fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .led_power = 26,
+ .led_lo = 27,
+ .led_hi = 28,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .lna_ctrl = 29,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = {
+ .name = "Hauppauge WinTV MiniCard",
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .lna_ctrl = 1,
},
};
@@ -106,3 +131,88 @@ struct sms_board *sms_get_board(int id)
return &sms_boards[id];
}
+static int sms_set_gpio(struct smscore_device_t *coredev, u32 pin, int enable)
+{
+ int ret;
+ struct smscore_gpio_config gpioconfig = {
+ .direction = SMS_GPIO_DIRECTION_OUTPUT,
+ .pullupdown = SMS_GPIO_PULLUPDOWN_NONE,
+ .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL,
+ .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST,
+ .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA,
+ };
+
+ if (pin == 0)
+ return -EINVAL;
+
+ ret = smscore_configure_gpio(coredev, pin, &gpioconfig);
+
+ if (ret < 0)
+ return ret;
+
+ return smscore_set_gpio(coredev, pin, enable);
+}
+
+int sms_board_setup(struct smscore_device_t *coredev)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* turn off all LEDs */
+ sms_set_gpio(coredev, board->led_power, 0);
+ sms_set_gpio(coredev, board->led_hi, 0);
+ sms_set_gpio(coredev, board->led_lo, 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* turn off LNA */
+ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+}
+
+int sms_board_power(struct smscore_device_t *coredev, int onoff)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ /* power LED */
+ sms_set_gpio(coredev,
+ board->led_power, onoff ? 1 : 0);
+ break;
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* LNA */
+ sms_set_gpio(coredev,
+ board->lna_ctrl, onoff ? 1 : 0);
+ break;
+ }
+ return 0;
+}
+
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+{
+ int board_id = smscore_get_board_id(coredev);
+ struct sms_board *board = sms_get_board(board_id);
+
+ /* dont touch GPIO if LEDs are already set */
+ if (smscore_led_state(coredev, -1) == led)
+ return 0;
+
+ switch (board_id) {
+ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ sms_set_gpio(coredev,
+ board->led_lo, (led & SMS_LED_LO) ? 1 : 0);
+ sms_set_gpio(coredev,
+ board->led_hi, (led & SMS_LED_HI) ? 1 : 0);
+
+ smscore_led_state(coredev, led);
+ break;
+ }
+ return 0;
+}
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h
index c8f3da6f9bc1..8e0fe9fd2610 100644
--- a/drivers/media/dvb/siano/sms-cards.h
+++ b/drivers/media/dvb/siano/sms-cards.h
@@ -32,14 +32,27 @@
#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6
#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7
#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9
+#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10
struct sms_board {
enum sms_device_type_st type;
char *name, *fw[DEVICE_MODE_MAX];
+
+ /* gpios */
+ int led_power, led_hi, led_lo, lna_ctrl;
};
struct sms_board *sms_get_board(int id);
+int sms_board_setup(struct smscore_device_t *coredev);
+
+#define SMS_LED_OFF 0
+#define SMS_LED_LO 1
+#define SMS_LED_HI 2
+int sms_board_led_feedback(struct smscore_device_t *coredev, int led);
+int sms_board_power(struct smscore_device_t *coredev, int onoff);
+
extern struct usb_device_id smsusb_id_table[];
#endif /* __SMS_CARDS_H__ */
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index 6576fbb40fc6..cf613f22fb8d 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -91,6 +91,7 @@ struct smscore_device_t {
struct completion init_device_done, reload_start_done, resume_done;
int board_id;
+ int led_state;
};
void smscore_set_board_id(struct smscore_device_t *core, int id)
@@ -98,6 +99,13 @@ void smscore_set_board_id(struct smscore_device_t *core, int id)
core->board_id = id;
}
+int smscore_led_state(struct smscore_device_t *core, int led)
+{
+ if (led >= 0)
+ core->led_state = led;
+ return core->led_state;
+}
+
int smscore_get_board_id(struct smscore_device_t *core)
{
return core->board_id;
@@ -1187,6 +1195,76 @@ int smsclient_sendrequest(struct smscore_client_t *client,
}
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_gpio_config *pinconfig)
+{
+ struct {
+ struct SmsMsgHdr_ST hdr;
+ u32 data[6];
+ } msg;
+
+ if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
+ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msgDstId = HIF_TASK;
+ msg.hdr.msgFlags = 0;
+ msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ;
+ msg.hdr.msgLength = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = pinconfig->pullupdown;
+
+ /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */
+ msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;
+
+ switch (pinconfig->outputdriving) {
+ case SMS_GPIO_OUTPUTDRIVING_16mA:
+ msg.data[3] = 7; /* Nova - 16mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_12mA:
+ msg.data[3] = 5; /* Nova - 11mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_8mA:
+ msg.data[3] = 3; /* Nova - 7mA */
+ break;
+ case SMS_GPIO_OUTPUTDRIVING_4mA:
+ default:
+ msg.data[3] = 2; /* Nova - 4mA */
+ break;
+ }
+
+ msg.data[4] = pinconfig->direction;
+ msg.data[5] = 0;
+ } else /* TODO: SMS_DEVICE_FAMILY1 */
+ return -EINVAL;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)
+{
+ struct {
+ struct SmsMsgHdr_ST hdr;
+ u32 data[3];
+ } msg;
+
+ if (pin > MAX_GPIO_PIN_NUMBER)
+ return -EINVAL;
+
+ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ msg.hdr.msgDstId = HIF_TASK;
+ msg.hdr.msgFlags = 0;
+ msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ;
+ msg.hdr.msgLength = sizeof(msg);
+
+ msg.data[0] = pin;
+ msg.data[1] = level ? 1 : 0;
+ msg.data[2] = 0;
+
+ return coredev->sendrequest_handler(coredev->context,
+ &msg, sizeof(msg));
+}
+
static int __init smscore_module_init(void)
{
int rc = 0;
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
index 8d973f726fb8..760e233fcbc5 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/dvb/siano/smscoreapi.h
@@ -186,6 +186,8 @@ struct smsclient_params_t {
#define MSG_SW_RELOAD_EXEC_REQ 704
#define MSG_SW_RELOAD_EXEC_RES 705
#define MSG_SMS_SPI_INT_LINE_SET_REQ 710
+#define MSG_SMS_GPIO_CONFIG_EX_REQ 712
+#define MSG_SMS_GPIO_CONFIG_EX_RES 713
#define MSG_SMS_ISDBT_TUNE_REQ 776
#define MSG_SMS_ISDBT_TUNE_RES 777
@@ -341,6 +343,32 @@ struct SmsMsgStatisticsInfo_ST {
};
+struct smscore_gpio_config {
+#define SMS_GPIO_DIRECTION_INPUT 0
+#define SMS_GPIO_DIRECTION_OUTPUT 1
+ u8 direction;
+
+#define SMS_GPIO_PULLUPDOWN_NONE 0
+#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1
+#define SMS_GPIO_PULLUPDOWN_PULLUP 2
+#define SMS_GPIO_PULLUPDOWN_KEEPER 3
+ u8 pullupdown;
+
+#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0
+#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1
+ u8 inputcharacteristics;
+
+#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0
+#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1
+ u8 outputslewrate;
+
+#define SMS_GPIO_OUTPUTDRIVING_4mA 0
+#define SMS_GPIO_OUTPUTDRIVING_8mA 1
+#define SMS_GPIO_OUTPUTDRIVING_12mA 2
+#define SMS_GPIO_OUTPUTDRIVING_16mA 3
+ u8 outputdriving;
+};
+
struct smsdvb_client_t {
struct list_head entry;
@@ -353,7 +381,7 @@ struct smsdvb_client_t {
struct dvb_frontend frontend;
fe_status_t fe_status;
- int fe_ber, fe_snr, fe_signal_strength;
+ int fe_ber, fe_snr, fe_unc, fe_signal_strength;
struct completion tune_done, stat_done;
@@ -396,9 +424,15 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
extern void smscore_putbuffer(struct smscore_device_t *coredev,
struct smscore_buffer_t *cb);
+int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+ struct smscore_gpio_config *pinconfig);
+int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level);
+
void smscore_set_board_id(struct smscore_device_t *core, int id);
int smscore_get_board_id(struct smscore_device_t *core);
+int smscore_led_state(struct smscore_device_t *core, int led);
+
/* smsdvb.c */
int smsdvb_register(void);
void smsdvb_unregister(void);
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
index 8d490e133f35..2da953a4f4f5 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/dvb/siano/smsdvb.c
@@ -60,6 +60,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
client->fe_snr = p->Stat.SNR;
client->fe_ber = p->Stat.BER;
+ client->fe_unc = p->Stat.BERErrorCount;
if (p->Stat.InBandPwr < -95)
client->fe_signal_strength = 0;
@@ -72,6 +73,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
client->fe_status = 0;
client->fe_snr =
client->fe_ber =
+ client->fe_unc =
client->fe_signal_strength = 0;
}
@@ -165,8 +167,18 @@ static int smsdvb_send_statistics_request(struct smsdvb_client_t *client)
struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ,
DVBT_BDA_CONTROL_MSG_ID,
HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 };
- return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
- &client->stat_done);
+ int ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->stat_done);
+ if (ret < 0)
+ return ret;
+
+ if (client->fe_status & FE_HAS_LOCK)
+ sms_board_led_feedback(client->coredev,
+ (client->fe_unc == 0) ?
+ SMS_LED_HI : SMS_LED_LO);
+ else
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ return ret;
}
static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
@@ -217,6 +229,18 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
return rc;
}
+static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+ int rc = smsdvb_send_statistics_request(client);
+
+ if (!rc)
+ *ucblocks = client->fe_unc;
+
+ return rc;
+}
+
static int smsdvb_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *tune)
{
@@ -273,6 +297,28 @@ static int smsdvb_get_frontend(struct dvb_frontend *fe,
/* todo: */
memcpy(fep, &client->fe_params,
sizeof(struct dvb_frontend_parameters));
+
+ return 0;
+}
+
+static int smsdvb_init(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_power(client->coredev, 1);
+
+ return 0;
+}
+
+static int smsdvb_sleep(struct dvb_frontend *fe)
+{
+ struct smsdvb_client_t *client =
+ container_of(fe, struct smsdvb_client_t, frontend);
+
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
return 0;
}
@@ -308,6 +354,10 @@ static struct dvb_frontend_ops smsdvb_fe_ops = {
.read_ber = smsdvb_read_ber,
.read_signal_strength = smsdvb_read_signal_strength,
.read_snr = smsdvb_read_snr,
+ .read_ucblocks = smsdvb_read_ucblocks,
+
+ .init = smsdvb_init,
+ .sleep = smsdvb_sleep,
};
static int smsdvb_hotplug(struct smscore_device_t *coredev,
@@ -402,6 +452,8 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
sms_info("success");
+ sms_board_setup(coredev);
+
return 0;
client_error:
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index 87a3c24454b9..5d7ca3417719 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -432,11 +432,56 @@ static void smsusb_disconnect(struct usb_interface *intf)
smsusb_term_device(intf);
}
+static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct smsusb_device_t *dev =
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+ printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event);
+ smsusb_stop_streaming(dev);
+ return 0;
+}
+
+static int smsusb_resume(struct usb_interface *intf)
+{
+ int rc, i;
+ struct smsusb_device_t *dev =
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ printk(KERN_INFO "%s Entering.\n", __func__);
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
+ printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
+ intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
+ intf->cur_altsetting->endpoint[i].desc.bmAttributes,
+ intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+ if (intf->num_altsetting > 0) {
+ rc = usb_set_interface(udev,
+ intf->cur_altsetting->desc.
+ bInterfaceNumber, 0);
+ if (rc < 0) {
+ printk(KERN_INFO
+ "%s usb_set_interface failed, rc %d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
+
+ smsusb_start_streaming(dev);
+ return 0;
+}
+
static struct usb_driver smsusb_driver = {
.name = "sms1xxx",
.probe = smsusb_probe,
.disconnect = smsusb_disconnect,
.id_table = smsusb_id_table,
+
+ .suspend = smsusb_suspend,
+ .resume = smsusb_resume,
};
int smsusb_register(void)
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 867027ceab3e..ab0bcd208c78 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -104,9 +104,11 @@ config DVB_BUDGET_CI
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
select VIDEO_IR
help
Support for simple SAA7146 based DVB cards
@@ -131,6 +133,8 @@ config DVB_BUDGET_AV
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
select DVB_TDA10021 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+ select DVB_STB0899 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8261 if !DVB_FE_CUSTOMISE
select DVB_TUA6100 if !DVB_FE_CUSTOMISE
help
Support for simple SAA7146 based DVB cards
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 1032ea77837e..f996cef79ec1 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -35,6 +35,11 @@
#include "budget.h"
#include "stv0299.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "tda8261.h"
+#include "tda8261_cfg.h"
#include "tda1002x.h"
#include "tda1004x.h"
#include "tua6100.h"
@@ -882,6 +887,281 @@ static struct stv0299_config philips_sd1878_config = {
.set_symbol_rate = philips_sd1878_ci_set_symbol_rate,
};
+/* KNC1 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISFIFO , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x08 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x33 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xee },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+
+/* STB0899 demodulator config for the KNC1 and clones */
+static struct stb0899_config knc1_dvbs2_config = {
+ .init_dev = knc1_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = knc1_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */
+// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_OFF, /* 1 */
+
+ .lo_clk = 76500000,
+ .hi_clk = 90000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = tda8261_get_frequency,
+ .tuner_set_frequency = tda8261_set_frequency,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = tda8261_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+/*
+ * SD1878/SHA tuner config
+ * 1F, Single I/P, Horizontal mount, High Sensitivity
+ */
+static const struct tda8261_config sd1878c_config = {
+// .name = "SD1878/SHA",
+ .addr = 0x60,
+ .step_size = TDA8261_STEP_1000 /* kHz */
+};
+
static u8 read_pwm(struct budget_av *budget_av)
{
u8 b = 0xff;
@@ -905,8 +1185,11 @@ static u8 read_pwm(struct budget_av *budget_av)
#define SUBID_DVBS_TV_STAR 0x0014
#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015
#define SUBID_DVBS_TV_STAR_CI 0x0016
+#define SUBID_DVBS2_KNC1 0x0018
+#define SUBID_DVBS2_KNC1_OEM 0x0019
#define SUBID_DVBS_EASYWATCH_1 0x001a
#define SUBID_DVBS_EASYWATCH_2 0x001b
+#define SUBID_DVBS2_EASYWATCH 0x001d
#define SUBID_DVBS_EASYWATCH 0x001e
#define SUBID_DVBC_EASYWATCH 0x002a
@@ -941,6 +1224,9 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBT_KNC1_PLUS:
case SUBID_DVBC_EASYWATCH:
case SUBID_DVBC_KNC1_PLUS_MK3:
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
break;
}
@@ -993,7 +1279,14 @@ static void frontend_init(struct budget_av *budget_av)
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
}
break;
+ case SUBID_DVBS2_KNC1:
+ case SUBID_DVBS2_KNC1_OEM:
+ case SUBID_DVBS2_EASYWATCH:
+ budget_av->reinitialise_demod = 1;
+ if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap)))
+ dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap);
+ break;
case SUBID_DVBS_CINERGY1200:
fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
&budget_av->budget.i2c_adap);
@@ -1260,6 +1553,8 @@ static struct saa7146_ext_vv vv_data = {
static struct saa7146_extension budget_extension;
MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2);
+MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2);
MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
@@ -1290,6 +1585,9 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015),
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018),
+ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019),
+ MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d),
MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b),
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 0a5aad45435d..3507463fdac9 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -43,6 +43,11 @@
#include "stv0299.h"
#include "stv0297.h"
#include "tda1004x.h"
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
#include "lnbp21.h"
#include "bsbe1.h"
#include "bsru6.h"
@@ -1071,7 +1076,271 @@ static struct tda10023_config tda10023_config = {
.deltaf = 0xa511,
};
+/* TT S2-3200 DVB-S (STB0899) Inittab */
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
+
+ { STB0899_DEV_ID , 0x81 },
+ { STB0899_DISCNTRL1 , 0x32 },
+ { STB0899_DISCNTRL2 , 0x80 },
+ { STB0899_DISRX_ST0 , 0x04 },
+ { STB0899_DISRX_ST1 , 0x00 },
+ { STB0899_DISPARITY , 0x00 },
+ { STB0899_DISFIFO , 0x00 },
+ { STB0899_DISSTATUS , 0x20 },
+ { STB0899_DISF22 , 0x8c },
+ { STB0899_DISF22RX , 0x9a },
+ { STB0899_SYSREG , 0x0b },
+ { STB0899_ACRPRESC , 0x11 },
+ { STB0899_ACRDIV1 , 0x0a },
+ { STB0899_ACRDIV2 , 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG , 0x00 },
+ { STB0899_MODECFG , 0x00 },
+ { STB0899_IRQSTATUS_3 , 0x30 },
+ { STB0899_IRQSTATUS_2 , 0x00 },
+ { STB0899_IRQSTATUS_1 , 0x00 },
+ { STB0899_IRQSTATUS_0 , 0x00 },
+ { STB0899_IRQMSK_3 , 0xf3 },
+ { STB0899_IRQMSK_2 , 0xfc },
+ { STB0899_IRQMSK_1 , 0xff },
+ { STB0899_IRQMSK_0 , 0xff },
+ { STB0899_IRQCFG , 0x00 },
+ { STB0899_I2CCFG , 0x88 },
+ { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
+ { STB0899_IOPVALUE5 , 0x00 },
+ { STB0899_IOPVALUE4 , 0x20 },
+ { STB0899_IOPVALUE3 , 0xc9 },
+ { STB0899_IOPVALUE2 , 0x90 },
+ { STB0899_IOPVALUE1 , 0x40 },
+ { STB0899_IOPVALUE0 , 0x00 },
+ { STB0899_GPIO00CFG , 0x82 },
+ { STB0899_GPIO01CFG , 0x82 },
+ { STB0899_GPIO02CFG , 0x82 },
+ { STB0899_GPIO03CFG , 0x82 },
+ { STB0899_GPIO04CFG , 0x82 },
+ { STB0899_GPIO05CFG , 0x82 },
+ { STB0899_GPIO06CFG , 0x82 },
+ { STB0899_GPIO07CFG , 0x82 },
+ { STB0899_GPIO08CFG , 0x82 },
+ { STB0899_GPIO09CFG , 0x82 },
+ { STB0899_GPIO10CFG , 0x82 },
+ { STB0899_GPIO11CFG , 0x82 },
+ { STB0899_GPIO12CFG , 0x82 },
+ { STB0899_GPIO13CFG , 0x82 },
+ { STB0899_GPIO14CFG , 0x82 },
+ { STB0899_GPIO15CFG , 0x82 },
+ { STB0899_GPIO16CFG , 0x82 },
+ { STB0899_GPIO17CFG , 0x82 },
+ { STB0899_GPIO18CFG , 0x82 },
+ { STB0899_GPIO19CFG , 0x82 },
+ { STB0899_GPIO20CFG , 0x82 },
+ { STB0899_SDATCFG , 0xb8 },
+ { STB0899_SCLTCFG , 0xba },
+ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */
+ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */
+ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */
+ { STB0899_DIRCLKCFG , 0x82 },
+ { STB0899_CLKOUT27CFG , 0x7e },
+ { STB0899_STDBYCFG , 0x82 },
+ { STB0899_CS0CFG , 0x82 },
+ { STB0899_CS1CFG , 0x82 },
+ { STB0899_DISEQCOCFG , 0x20 },
+ { STB0899_GPIO32CFG , 0x82 },
+ { STB0899_GPIO33CFG , 0x82 },
+ { STB0899_GPIO34CFG , 0x82 },
+ { STB0899_GPIO35CFG , 0x82 },
+ { STB0899_GPIO36CFG , 0x82 },
+ { STB0899_GPIO37CFG , 0x82 },
+ { STB0899_GPIO38CFG , 0x82 },
+ { STB0899_GPIO39CFG , 0x82 },
+ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
+ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
+ { STB0899_FILTCTRL , 0x00 },
+ { STB0899_SYSCTRL , 0x00 },
+ { STB0899_STOPCLK1 , 0x20 },
+ { STB0899_STOPCLK2 , 0x00 },
+ { STB0899_INTBUFSTATUS , 0x00 },
+ { STB0899_INTBUFCTRL , 0x0a },
+ { 0xffff , 0xff },
+};
+
+static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
+ { STB0899_DEMOD , 0x00 },
+ { STB0899_RCOMPC , 0xc9 },
+ { STB0899_AGC1CN , 0x41 },
+ { STB0899_AGC1REF , 0x10 },
+ { STB0899_RTC , 0x7a },
+ { STB0899_TMGCFG , 0x4e },
+ { STB0899_AGC2REF , 0x34 },
+ { STB0899_TLSR , 0x84 },
+ { STB0899_CFD , 0xc7 },
+ { STB0899_ACLC , 0x87 },
+ { STB0899_BCLC , 0x94 },
+ { STB0899_EQON , 0x41 },
+ { STB0899_LDT , 0xdd },
+ { STB0899_LDT2 , 0xc9 },
+ { STB0899_EQUALREF , 0xb4 },
+ { STB0899_TMGRAMP , 0x10 },
+ { STB0899_TMGTHD , 0x30 },
+ { STB0899_IDCCOMP , 0xfb },
+ { STB0899_QDCCOMP , 0x03 },
+ { STB0899_POWERI , 0x3b },
+ { STB0899_POWERQ , 0x3d },
+ { STB0899_RCOMP , 0x81 },
+ { STB0899_AGCIQIN , 0x80 },
+ { STB0899_AGC2I1 , 0x04 },
+ { STB0899_AGC2I2 , 0xf5 },
+ { STB0899_TLIR , 0x25 },
+ { STB0899_RTF , 0x80 },
+ { STB0899_DSTATUS , 0x00 },
+ { STB0899_LDI , 0xca },
+ { STB0899_CFRM , 0xf1 },
+ { STB0899_CFRL , 0xf3 },
+ { STB0899_NIRM , 0x2a },
+ { STB0899_NIRL , 0x05 },
+ { STB0899_ISYMB , 0x17 },
+ { STB0899_QSYMB , 0xfa },
+ { STB0899_SFRH , 0x2f },
+ { STB0899_SFRM , 0x68 },
+ { STB0899_SFRL , 0x40 },
+ { STB0899_SFRUPH , 0x2f },
+ { STB0899_SFRUPM , 0x68 },
+ { STB0899_SFRUPL , 0x40 },
+ { STB0899_EQUAI1 , 0xfd },
+ { STB0899_EQUAQ1 , 0x04 },
+ { STB0899_EQUAI2 , 0x0f },
+ { STB0899_EQUAQ2 , 0xff },
+ { STB0899_EQUAI3 , 0xdf },
+ { STB0899_EQUAQ3 , 0xfa },
+ { STB0899_EQUAI4 , 0x37 },
+ { STB0899_EQUAQ4 , 0x0d },
+ { STB0899_EQUAI5 , 0xbd },
+ { STB0899_EQUAQ5 , 0xf7 },
+ { STB0899_DSTATUS2 , 0x00 },
+ { STB0899_VSTATUS , 0x00 },
+ { STB0899_VERROR , 0xff },
+ { STB0899_IQSWAP , 0x2a },
+ { STB0899_ECNT1M , 0x00 },
+ { STB0899_ECNT1L , 0x00 },
+ { STB0899_ECNT2M , 0x00 },
+ { STB0899_ECNT2L , 0x00 },
+ { STB0899_ECNT3M , 0x00 },
+ { STB0899_ECNT3L , 0x00 },
+ { STB0899_FECAUTO1 , 0x06 },
+ { STB0899_FECM , 0x01 },
+ { STB0899_VTH12 , 0xf0 },
+ { STB0899_VTH23 , 0xa0 },
+ { STB0899_VTH34 , 0x78 },
+ { STB0899_VTH56 , 0x4e },
+ { STB0899_VTH67 , 0x48 },
+ { STB0899_VTH78 , 0x38 },
+ { STB0899_PRVIT , 0xff },
+ { STB0899_VITSYNC , 0x19 },
+ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC , 0x42 },
+ { STB0899_RSLLC , 0x40 },
+ { STB0899_TSLPL , 0x12 },
+ { STB0899_TSCFGH , 0x0c },
+ { STB0899_TSCFGM , 0x00 },
+ { STB0899_TSCFGL , 0x0c },
+ { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL , 0x00 },
+ { STB0899_TSINHDELH , 0x02 },
+ { STB0899_TSINHDELM , 0x00 },
+ { STB0899_TSINHDELL , 0x00 },
+ { STB0899_TSLLSTKM , 0x00 },
+ { STB0899_TSLLSTKL , 0x00 },
+ { STB0899_TSULSTKM , 0x00 },
+ { STB0899_TSULSTKL , 0xab },
+ { STB0899_PCKLENUL , 0x00 },
+ { STB0899_PCKLENLL , 0xcc },
+ { STB0899_RSPCKLEN , 0xcc },
+ { STB0899_TSSTATUS , 0x80 },
+ { STB0899_ERRCTRL1 , 0xb6 },
+ { STB0899_ERRCTRL2 , 0x96 },
+ { STB0899_ERRCTRL3 , 0x89 },
+ { STB0899_DMONMSK1 , 0x27 },
+ { STB0899_DMONMSK0 , 0x03 },
+ { STB0899_DEMAPVIT , 0x5c },
+ { STB0899_PLPARM , 0x1f },
+ { STB0899_PDELCTRL , 0x48 },
+ { STB0899_PDELCTRL2 , 0x00 },
+ { STB0899_BBHCTRL1 , 0x00 },
+ { STB0899_BBHCTRL2 , 0x00 },
+ { STB0899_HYSTTHRESH , 0x77 },
+ { STB0899_MATCSTM , 0x00 },
+ { STB0899_MATCSTL , 0x00 },
+ { STB0899_UPLCSTM , 0x00 },
+ { STB0899_UPLCSTL , 0x00 },
+ { STB0899_DFLCSTM , 0x00 },
+ { STB0899_DFLCSTL , 0x00 },
+ { STB0899_SYNCCST , 0x00 },
+ { STB0899_SYNCDCSTM , 0x00 },
+ { STB0899_SYNCDCSTL , 0x00 },
+ { STB0899_ISI_ENTRY , 0x00 },
+ { STB0899_ISI_BIT_EN , 0x00 },
+ { STB0899_MATSTRM , 0x00 },
+ { STB0899_MATSTRL , 0x00 },
+ { STB0899_UPLSTRM , 0x00 },
+ { STB0899_UPLSTRL , 0x00 },
+ { STB0899_DFLSTRM , 0x00 },
+ { STB0899_DFLSTRL , 0x00 },
+ { STB0899_SYNCSTR , 0x00 },
+ { STB0899_SYNCDSTRM , 0x00 },
+ { STB0899_SYNCDSTRL , 0x00 },
+ { STB0899_CFGPDELSTATUS1 , 0x10 },
+ { STB0899_CFGPDELSTATUS2 , 0x00 },
+ { STB0899_BBFERRORM , 0x00 },
+ { STB0899_BBFERRORL , 0x00 },
+ { STB0899_UPKTERRORM , 0x00 },
+ { STB0899_UPKTERRORL , 0x00 },
+ { 0xffff , 0xff },
+};
+static struct stb0899_config tt3200_config = {
+ .init_dev = tt3200_stb0899_s1_init_1,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = tt3200_stb0899_s1_init_3,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .postproc = NULL,
+
+ .demod_address = 0x68,
+
+ .xtal_freq = 27000000,
+ .inversion = IQ_SWAP_ON, /* 1 */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL
+};
+
+struct stb6100_config tt3200_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
static void frontend_init(struct budget_ci *budget_ci)
{
@@ -1152,6 +1421,46 @@ static void frontend_init(struct budget_ci *budget_ci)
}
}
break;
+
+ case 0x1019: // TT S2-3200 PCI
+ /*
+ * NOTE! on some STB0899 versions, the internal PLL takes a longer time
+ * to settle, aka LOCK. On the older revisions of the chip, we don't see
+ * this, as a result on the newer chips the entire clock tree, will not
+ * be stable after a freshly POWER 'ed up situation.
+ * In this case, we should RESET the STB0899 (Active LOW) and wait for
+ * PLL stabilization.
+ *
+ * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is
+ * connected to the SAA7146 GPIO, GPIO2, Pin 142
+ */
+ /* Reset Demodulator */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO);
+ /* Wait for everything to die */
+ msleep(50);
+ /* Pull it up out of Reset state */
+ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI);
+ /* Wait for PLL to stabilize */
+ msleep(250);
+ /*
+ * PLL state should be stable now. Ideally, we should check
+ * for PLL LOCK status. But well, never mind!
+ */
+ budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap);
+ if (budget_ci->budget.dvb_frontend) {
+ if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
+ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) {
+ printk("%s: No LNBP21 found!\n", __FUNCTION__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ } else {
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+ }
+ }
+ break;
+
}
if (budget_ci->budget.dvb_frontend == NULL) {
@@ -1242,6 +1551,7 @@ MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT);
static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
@@ -1251,6 +1561,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a),
+ MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019),
{
.vendor = 0,
}
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index 86435bf16260..3ad0c6789ba7 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -103,6 +103,7 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_CIN1200C_MK3 15
#define BUDGET_KNC1C_MK3 16
#define BUDGET_KNC1CP_MK3 17
+#define BUDGET_KNC1S2 18
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index a5ca176a7b08..5474a22c1b22 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -1,5 +1,5 @@
-/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
- into both the USB and an analog audio input, so this thing
+/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
+ The device plugs into both the USB and an analog audio input, so this thing
only deals with initialisation and frequency setting, the
audio data has to be handled by a sound driver.
@@ -33,6 +33,10 @@
History:
+ Version 0.44:
+ Add suspend/resume functions, fix unplug of device,
+ a lot of cleanups and fixes by Alexey Klimov <klimov.linux@gmail.com>
+
Version 0.43:
Oliver Neukum: avoided DMA coherency issue
@@ -93,8 +97,8 @@
*/
#include <linux/version.h> /* for KERNEL_VERSION MACRO */
-#define DRIVER_VERSION "v0.41"
-#define RADIO_VERSION KERNEL_VERSION(0,4,1)
+#define DRIVER_VERSION "v0.44"
+#define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
static struct v4l2_queryctrl radio_qctrl[] = {
{
@@ -104,7 +108,27 @@ static struct v4l2_queryctrl radio_qctrl[] = {
.maximum = 1,
.default_value = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- }
+ },
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ { .id = V4L2_CID_AUDIO_VOLUME,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
};
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
@@ -125,12 +149,16 @@ devices, that would be 76 and 91. */
#define FREQ_MAX 108.0
#define FREQ_MUL 16000
+#define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void usb_dsbr100_disconnect(struct usb_interface *intf);
static int usb_dsbr100_open(struct inode *inode, struct file *file);
static int usb_dsbr100_close(struct inode *inode, struct file *file);
+static int usb_dsbr100_suspend(struct usb_interface *intf,
+ pm_message_t message);
+static int usb_dsbr100_resume(struct usb_interface *intf);
static int radio_nr = -1;
module_param(radio_nr, int, 0);
@@ -138,8 +166,9 @@ module_param(radio_nr, int, 0);
/* Data for one (physical) device */
struct dsbr100_device {
struct usb_device *usbdev;
- struct video_device *videodev;
+ struct video_device videodev;
u8 *transfer_buffer;
+ struct mutex lock; /* buffer locking */
int curfreq;
int stereo;
int users;
@@ -147,7 +176,6 @@ struct dsbr100_device {
int muted;
};
-
static struct usb_device_id usb_dsbr100_device_table [] = {
{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
{ } /* Terminating entry */
@@ -157,10 +185,14 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
/* USB subsystem interface */
static struct usb_driver usb_dsbr100_driver = {
- .name = "dsbr100",
- .probe = usb_dsbr100_probe,
- .disconnect = usb_dsbr100_disconnect,
- .id_table = usb_dsbr100_device_table,
+ .name = "dsbr100",
+ .probe = usb_dsbr100_probe,
+ .disconnect = usb_dsbr100_disconnect,
+ .id_table = usb_dsbr100_device_table,
+ .suspend = usb_dsbr100_suspend,
+ .resume = usb_dsbr100_resume,
+ .reset_resume = usb_dsbr100_resume,
+ .supports_autosuspend = 0,
};
/* Low-level device interface begins here */
@@ -168,95 +200,190 @@ static struct usb_driver usb_dsbr100_driver = {
/* switch on radio */
static int dsbr100_start(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=0;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0xC7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x01, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 0;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
-}
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
+}
/* switch off radio */
static int dsbr100_stop(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_ONOFF,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0)
- return -1;
- radio->muted=1;
+ int retval;
+ int request;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x16, 0x1C, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_ONOFF,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x00, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_ONOFF;
+ goto usb_control_msg_failed;
+ }
+
+ radio->muted = 1;
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
+
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
{
+ int retval;
+ int request;
+
freq = (freq / 16 * 80) / 1000 + 856;
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- DSB100_TUNE,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- (freq >> 8) & 0x00ff, freq & 0xff,
- radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 ||
- usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) {
- radio->stereo = -1;
- return -1;
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_TUNE,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ (freq >> 8) & 0x00ff, freq & 0xff,
+ radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = DSB100_TUNE;
+ goto usb_control_msg_failed;
}
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x96, 0xB7, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ USB_REQ_GET_STATUS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x00, 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
+ request = USB_REQ_GET_STATUS;
+ goto usb_control_msg_failed;
+ }
+
radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
+ mutex_unlock(&radio->lock);
return (radio->transfer_buffer)[0];
+
+usb_control_msg_failed:
+ radio->stereo = -1;
+ mutex_unlock(&radio->lock);
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, request);
+ return retval;
}
/* return the device status. This is, in effect, just whether it
sees a stereo signal or not. Pity. */
static void dsbr100_getstat(struct dsbr100_device *radio)
{
- if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
+ int retval;
+
+ mutex_lock(&radio->lock);
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
- 0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0)
+ 0x00 , 0x24, radio->transfer_buffer, 8, 300);
+
+ if (retval < 0) {
radio->stereo = -1;
- else
+ dev_err(&radio->usbdev->dev,
+ "%s - usb_control_msg returned %i, request %i\n",
+ __func__, retval, USB_REQ_GET_STATUS);
+ } else {
radio->stereo = !(radio->transfer_buffer[0] & 0x01);
-}
+ }
+ mutex_unlock(&radio->lock);
+}
/* USB subsystem interface begins here */
-/* handle unplugging of the device, release data structures
-if nothing keeps us from doing it. If something is still
-keeping us busy, the release callback of v4l will take care
-of releasing it. */
+/*
+ * Handle unplugging of the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_dsbr100_video_device_release
+ */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
usb_set_intfdata (intf, NULL);
- if (radio) {
- video_unregister_device(radio->videodev);
- radio->videodev = NULL;
- if (radio->users) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- } else {
- radio->removed = 1;
- }
- }
+
+ mutex_lock(&radio->lock);
+ radio->removed = 1;
+ mutex_unlock(&radio->lock);
+
+ video_unregister_device(&radio->videodev);
}
@@ -276,6 +403,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -297,6 +428,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct dsbr100_device *radio = video_drvdata(file);
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -307,9 +444,15 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
radio->curfreq = f->frequency;
- if (dsbr100_setfreq(radio, radio->curfreq) == -1)
+ retval = dsbr100_setfreq(radio, radio->curfreq);
+ if (retval < 0)
dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
return 0;
}
@@ -319,6 +462,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
@@ -343,6 +490,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
@@ -355,17 +506,24 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
- if (dsbr100_stop(radio) == -1) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
}
} else {
- if (dsbr100_start(radio) == -1) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not respond properly\n");
return -EBUSY;
@@ -417,7 +575,8 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
radio->users = 1;
radio->muted = 1;
- if (dsbr100_start(radio) < 0) {
+ retval = dsbr100_start(radio);
+ if (retval < 0) {
dev_warn(&radio->usbdev->dev,
"Radio did not start up properly\n");
radio->users = 0;
@@ -426,9 +585,9 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
}
retval = dsbr100_setfreq(radio, radio->curfreq);
-
- if (retval == -1)
- printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n");
+ if (retval < 0)
+ dev_warn(&radio->usbdev->dev,
+ "set frequency failed\n");
unlock_kernel();
return 0;
@@ -437,17 +596,62 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
struct dsbr100_device *radio = video_drvdata(file);
+ int retval;
if (!radio)
return -ENODEV;
+
radio->users = 0;
- if (radio->removed) {
- kfree(radio->transfer_buffer);
- kfree(radio);
+ if (!radio->removed) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
+ dev_warn(&radio->usbdev->dev,
+ "dsbr100_stop failed\n");
+ }
+
}
return 0;
}
+/* Suspend device - stop device. */
+static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct dsbr100_device *radio = usb_get_intfdata(intf);
+ int retval;
+
+ retval = dsbr100_stop(radio);
+ if (retval < 0)
+ dev_warn(&intf->dev, "dsbr100_stop failed\n");
+
+ dev_info(&intf->dev, "going into suspend..\n");
+
+ return 0;
+}
+
+/* Resume device - start device. */
+static int usb_dsbr100_resume(struct usb_interface *intf)
+{
+ struct dsbr100_device *radio = usb_get_intfdata(intf);
+ int retval;
+
+ retval = dsbr100_start(radio);
+ if (retval < 0)
+ dev_warn(&intf->dev, "dsbr100_start failed\n");
+
+ dev_info(&intf->dev, "coming out of suspend..\n");
+
+ return 0;
+}
+
+/* free data structures */
+static void usb_dsbr100_video_device_release(struct video_device *videodev)
+{
+ struct dsbr100_device *radio = videodev_to_radio(videodev);
+
+ kfree(radio->transfer_buffer);
+ kfree(radio);
+}
+
/* File system interface */
static const struct file_operations usb_dsbr100_fops = {
.owner = THIS_MODULE,
@@ -476,19 +680,19 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
};
/* V4L2 interface */
-static struct video_device dsbr100_videodev_template = {
+static struct video_device dsbr100_videodev_data = {
.name = "D-Link DSB-R 100",
.fops = &usb_dsbr100_fops,
.ioctl_ops = &usb_dsbr100_ioctl_ops,
- .release = video_device_release,
+ .release = usb_dsbr100_video_device_release,
};
-/* check if the device is present and register with v4l and
-usb if it is */
+/* check if the device is present and register with v4l and usb if it is */
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dsbr100_device *radio;
+ int retval;
radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
@@ -501,23 +705,18 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
kfree(radio);
return -ENOMEM;
}
- radio->videodev = video_device_alloc();
- if (!(radio->videodev)) {
- kfree(radio->transfer_buffer);
- kfree(radio);
- return -ENOMEM;
- }
- memcpy(radio->videodev, &dsbr100_videodev_template,
- sizeof(dsbr100_videodev_template));
+ mutex_init(&radio->lock);
+ radio->videodev = dsbr100_videodev_data;
+
radio->removed = 0;
radio->users = 0;
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = FREQ_MIN * FREQ_MUL;
- video_set_drvdata(radio->videodev, radio);
- if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) {
- dev_warn(&intf->dev, "Could not register video device\n");
- video_device_release(radio->videodev);
+ video_set_drvdata(&radio->videodev, radio);
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register video device\n");
kfree(radio->transfer_buffer);
kfree(radio);
return -EIO;
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 9305e958fc66..dd6d3dfcd7d2 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,7 +1,7 @@
/* radiotrack (radioreveal) driver for Linux radio support
* (c) 1997 M. Kirkwood
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* History:
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 0490a1fa999d..bfd37f38b9ab 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -23,7 +23,7 @@
* 2002-01-17 Adam Belay <ambx1@neo.rr.com>
* Updated to latest pnp code
*
- * 2003-01-31 Alan Cox <alan@redhat.com>
+ * 2003-01-31 Alan Cox <alan@lxorguk.ukuu.org.uk>
* Cleaned up locking, delay code, general odds and ends
*
* 2006-07-30 Hans J. Koch <koch@hjk-az.de>
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index d131a5d38128..e13118da307b 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -8,7 +8,7 @@
* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* TODO: Allow for more than one of these foolish entities :-)
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 256cbeffdcb6..e730eddb2bb5 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -72,6 +72,11 @@ MODULE_LICENSE("GPL");
#define USB_AMRADIO_VENDOR 0x07ca
#define USB_AMRADIO_PRODUCT 0xb800
+/* dev_warn macro with driver name */
+#define MR800_DRIVER_NAME "radio-mr800"
+#define amradio_dev_warn(dev, fmt, arg...) \
+ dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+
/* Probably USB_TIMEOUT should be modified in module parameter */
#define BUFFER_LENGTH 8
#define USB_TIMEOUT 500
@@ -154,14 +159,14 @@ MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
/* USB subsystem interface */
static struct usb_driver usb_amradio_driver = {
- .name = "radio-mr800",
+ .name = MR800_DRIVER_NAME,
.probe = usb_amradio_probe,
.disconnect = usb_amradio_disconnect,
.suspend = usb_amradio_suspend,
.resume = usb_amradio_resume,
.reset_resume = usb_amradio_resume,
.id_table = usb_amradio_device_table,
- .supports_autosuspend = 1,
+ .supports_autosuspend = 0,
};
/* switch on radio. Send 8 bytes to device. */
@@ -202,6 +207,10 @@ static int amradio_stop(struct amradio_device *radio)
int retval;
int size;
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
mutex_lock(&radio->lock);
radio->buffer[0] = 0x00;
@@ -235,6 +244,10 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
int size;
unsigned short freq_send = 0x13 + (freq >> 3) / 25;
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
mutex_lock(&radio->lock);
radio->buffer[0] = 0x00;
@@ -288,18 +301,12 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
{
struct amradio_device *radio = usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
+ mutex_lock(&radio->lock);
+ radio->removed = 1;
+ mutex_unlock(&radio->lock);
- if (radio) {
- video_unregister_device(radio->videodev);
- radio->videodev = NULL;
- if (radio->users) {
- kfree(radio->buffer);
- kfree(radio);
- } else {
- radio->removed = 1;
- }
- }
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(radio->videodev);
}
/* vidioc_querycap - query device capabilities */
@@ -320,6 +327,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
@@ -346,6 +357,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
if (v->index > 0)
return -EINVAL;
return 0;
@@ -357,9 +374,14 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
radio->curfreq = f->frequency;
if (amradio_setfreq(radio, radio->curfreq) < 0)
- warn("Set frequency failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
return 0;
}
@@ -369,6 +391,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
@@ -382,8 +408,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]),
- sizeof(*qc));
+ memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
return 0;
}
}
@@ -396,6 +421,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
@@ -410,16 +439,22 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
if (amradio_stop(radio) < 0) {
- warn("amradio_stop() failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
return -1;
}
} else {
if (amradio_start(radio) < 0) {
- warn("amradio_start() failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_start failed\n");
return -1;
}
}
@@ -475,30 +510,38 @@ static int usb_amradio_open(struct inode *inode, struct file *file)
radio->muted = 1;
if (amradio_start(radio) < 0) {
- warn("Radio did not start up properly");
+ amradio_dev_warn(&radio->videodev->dev,
+ "radio did not start up properly\n");
radio->users = 0;
unlock_kernel();
return -EIO;
}
if (amradio_setfreq(radio, radio->curfreq) < 0)
- warn("Set frequency failed");
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
unlock_kernel();
return 0;
}
-/*close device - free driver structures */
+/*close device */
static int usb_amradio_close(struct inode *inode, struct file *file)
{
struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ int retval;
if (!radio)
return -ENODEV;
+
radio->users = 0;
- if (radio->removed) {
- kfree(radio->buffer);
- kfree(radio);
+
+ if (!radio->removed) {
+ retval = amradio_stop(radio);
+ if (retval < 0)
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
}
+
return 0;
}
@@ -508,9 +551,9 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
struct amradio_device *radio = usb_get_intfdata(intf);
if (amradio_stop(radio) < 0)
- warn("amradio_stop() failed");
+ dev_warn(&intf->dev, "amradio_stop failed\n");
- info("radio-mr800: Going into suspend..");
+ dev_info(&intf->dev, "going into suspend..\n");
return 0;
}
@@ -521,9 +564,9 @@ static int usb_amradio_resume(struct usb_interface *intf)
struct amradio_device *radio = usb_get_intfdata(intf);
if (amradio_start(radio) < 0)
- warn("amradio_start() failed");
+ dev_warn(&intf->dev, "amradio_start failed\n");
- info("radio-mr800: Coming out of suspend..");
+ dev_info(&intf->dev, "coming out of suspend..\n");
return 0;
}
@@ -555,12 +598,24 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
};
+static void usb_amradio_device_release(struct video_device *videodev)
+{
+ struct amradio_device *radio = video_get_drvdata(videodev);
+
+ /* we call v4l to free radio->videodev */
+ video_device_release(videodev);
+
+ /* free rest memory */
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
/* V4L2 interface */
static struct video_device amradio_videodev_template = {
.name = "AverMedia MR 800 USB FM Radio",
.fops = &usb_amradio_fops,
.ioctl_ops = &usb_amradio_ioctl_ops,
- .release = video_device_release,
+ .release = usb_amradio_device_release,
};
/* check if the device is present and register with v4l and
@@ -602,7 +657,7 @@ static int usb_amradio_probe(struct usb_interface *intf,
video_set_drvdata(radio->videodev, radio);
if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
- warn("Could not register video device");
+ dev_warn(&intf->dev, "could not register video device\n");
video_device_release(radio->videodev);
kfree(radio->buffer);
kfree(radio);
@@ -617,9 +672,13 @@ static int __init amradio_init(void)
{
int retval = usb_register(&usb_amradio_driver);
- info(DRIVER_VERSION " " DRIVER_DESC);
+ pr_info(KBUILD_MODNAME
+ ": version " DRIVER_VERSION " " DRIVER_DESC "\n");
+
if (retval)
- err("usb_register failed. Error number %d", retval);
+ pr_err(KBUILD_MODNAME
+ ": usb_register failed. Error number %d\n", retval);
+
return retval;
}
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index a67079777419..7704f243b6f0 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -1,7 +1,7 @@
/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
- * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
* TODO: Allow for more than one of these foolish entities :-)
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 329c90bddadd..834d43651c70 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -3,7 +3,7 @@
* (c) 1997 M. Kirkwood
* (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
*
- * Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Made working and cleaned up functions <mikael.hedin@irf.se>
* Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
*
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 47102c2c8250..19cf3b8f67c4 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -184,7 +184,7 @@ config VIDEO_MSP3400
config VIDEO_CS5345
tristate "Cirrus Logic CS5345 audio ADC"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Cirrus Logic CS5345 24-bit, 192 kHz
stereo A/D converter.
@@ -204,7 +204,7 @@ config VIDEO_CS53L32A
config VIDEO_M52790
tristate "Mitsubishi M52790 A/V switch"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Mitsubishi M52790 A/V switch.
@@ -242,7 +242,7 @@ config VIDEO_WM8739
config VIDEO_VP27SMPX
tristate "Panasonic VP27s internal MPX"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the internal MPX of the Panasonic VP27s tuner.
@@ -361,6 +361,17 @@ config VIDEO_SAA7191
To compile this driver as a module, choose M here: the
module will be called saa7191.
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -387,7 +398,7 @@ comment "MPEG video encoders"
config VIDEO_CX2341X
tristate "Conexant CX2341x MPEG encoders"
- depends on VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_COMMON
+ depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON
---help---
Support for the Conexant CX23416 MPEG encoders
and CX23415 MPEG encoder/decoders.
@@ -725,10 +736,16 @@ config MT9M001_PCA9536_SWITCH
extender to switch between 8 and 10 bit datawidth modes
config SOC_CAMERA_MT9M111
- tristate "mt9m111 support"
+ tristate "mt9m111 and mt9m112 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9M111 and MT9M112 cameras from Micron
+
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support"
depends on SOC_CAMERA && I2C
help
- This driver supports MT9M111 cameras from Micron
+ This driver supports MT9T031 cameras from Micron.
config SOC_CAMERA_MT9V022
tristate "mt9v022 support"
@@ -744,12 +761,24 @@ config MT9V022_PCA9536_SWITCH
Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
extender to switch between 8 and 10 bit datawidth modes
+config SOC_CAMERA_TW9910
+ tristate "tw9910 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a tw9910 video driver
+
config SOC_CAMERA_PLATFORM
tristate "platform camera support"
depends on SOC_CAMERA
help
This is a generic SoC camera platform driver, useful for testing
+config SOC_CAMERA_OV772X
+ tristate "ov772x camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov772x camera driver
+
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
depends on VIDEO_DEV && PXA27x && SOC_CAMERA
@@ -759,11 +788,18 @@ config VIDEO_PXA27x
config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA
+ depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
select VIDEOBUF_DMA_CONTIG
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
#
# USB Multimedia device configuration
#
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 16962f3aa157..1611c33b1aee 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -8,9 +8,11 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o
stkwebcam-objs := stk-webcam.o stk-sensor.o
-videodev-objs := v4l2-dev.o v4l2-ioctl.o
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-obj-$(CONFIG_VIDEO_DEV) += videodev.o compat_ioctl32.o v4l2-int-device.o
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o
+
+obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o
obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
@@ -25,6 +27,7 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
+obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
@@ -66,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
@@ -129,11 +133,15 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
+obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
obj-$(CONFIG_VIDEO_AU0828) += au0828/
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index e09b00693230..2ba6abd92b6f 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -396,8 +396,7 @@ out_up:
return ret;
}
-static int ar_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int ar_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct ar_device *ar = video_get_drvdata(dev);
@@ -543,7 +542,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file,
static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, ar_do_ioctl);
+ return video_usercopy(file, cmd, arg, ar_do_ioctl);
}
#if USE_INT
diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c
deleted file mode 100644
index 216fc9680e80..000000000000
--- a/drivers/media/video/bt8xx/bt832.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/* Driver for Bt832 CMOS Camera Video Processor
- i2c-addresses: 0x88 or 0x8a
-
- The BT832 interfaces to a Quartzsight Digital Camera (352x288, 25 or 30 fps)
- via a 9 pin connector ( 4-wire SDATA, 2-wire i2c, SCLK, VCC, GND).
- It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly
- connected to bt848/bt878 GPIO pins on this purpose.
- (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets)
-
- Supported Cards:
- - Pixelview Rev.4E: 0x8a
- GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 !
-
- (c) Gunther Mayer, 2002
-
- STATUS:
- - detect chip and hexdump
- - reset chip and leave low power mode
- - detect camera present
-
- TODO:
- - make it work (find correct setup for Bt832 and Bt878)
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/types.h>
-#include <linux/videodev.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-
-#include "bttv.h"
-#include "bt832.h"
-
-MODULE_LICENSE("GPL");
-
-/* Addresses to scan */
-static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1,
- I2C_CLIENT_END };
-I2C_CLIENT_INSMOD;
-
-int debug; /* debug output */
-module_param(debug, int, 0644);
-
-/* ---------------------------------------------------------------------- */
-
-static int bt832_detach(struct i2c_client *client);
-
-
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-struct bt832 {
- struct i2c_client client;
-};
-
-int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf)
-{
- int i,rc;
- buf[0]=0x80; // start at register 0 with auto-increment
- if (1 != (rc = i2c_master_send(i2c_client_s,buf,1)))
- v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc);
-
- for(i=0;i<65;i++)
- buf[i]=0;
- if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65)))
- v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc);
-
- // Note: On READ the first byte is the current index
- // (e.g. 0x80, what we just wrote)
-
- if(debug>1) {
- int i;
- v4l_dbg(2, debug,i2c_client_s,"hexdump:");
- for(i=1;i<65;i++) {
- if(i!=1) {
- if(((i-1)%8)==0) printk(" ");
- if(((i-1)%16)==0) {
- printk("\n");
- v4l_dbg(2, debug,i2c_client_s,"hexdump:");
- }
- }
- printk(" %02x",buf[i]);
- }
- printk("\n");
- }
- return 0;
-}
-
-// Return: 1 (is a bt832), 0 (No bt832 here)
-int bt832_init(struct i2c_client *i2c_client_s)
-{
- unsigned char *buf;
- int rc;
-
- buf=kmalloc(65,GFP_KERNEL);
- if (!buf) {
- v4l_err(&t->client,
- "Unable to allocate memory. Detaching.\n");
- return 0;
- }
- bt832_hexdump(i2c_client_s,buf);
-
- if(buf[0x40] != 0x31) {
- v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]);
- kfree(buf);
- return 0;
- }
-
- v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n");
- buf[0]=BT832_VP_STATUS; // Reg.52
- buf[1]= 0x00;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // Leave low power mode:
- v4l_err(i2c_client_s,"leave low power mode.\n");
- buf[0]=BT832_CAM_SETUP0; //0x39 57
- buf[1]=0x08;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
- v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n");
- buf[0]=BT832_VP_STATUS; // Reg.52
- buf[1]= 0x00;
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // Enable Output
- v4l_info(i2c_client_s,"Enable Output\n");
- buf[0]=BT832_VP_CONTROL1; // Reg.40
- buf[1]= 0x27 & (~0x01); // Default | !skip
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc);
-
- bt832_hexdump(i2c_client_s,buf);
-
-
- // for testing (even works when no camera attached)
- v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n");
- buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42
- buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally
- if (2 != (rc = i2c_master_send(i2c_client_s,buf,2)))
- v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc);
-
- v4l_info(i2c_client_s,"Camera Present: %s\n",
- (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no");
-
- bt832_hexdump(i2c_client_s,buf);
- kfree(buf);
- return 1;
-}
-
-
-
-static int bt832_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct bt832 *t;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL)))
- return -ENOMEM;
- t->client = client_template;
- i2c_set_clientdata(&t->client, t);
- i2c_attach_client(&t->client);
-
- v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1);
-
- if(! bt832_init(&t->client)) {
- bt832_detach(&t->client);
- return -1;
- }
-
- return 0;
-}
-
-static int bt832_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, bt832_attach);
- return 0;
-}
-
-static int bt832_detach(struct i2c_client *client)
-{
- struct bt832 *t = i2c_get_clientdata(client);
-
- v4l_info(&t->client,"dettach\n");
- i2c_detach_client(client);
- kfree(t);
- return 0;
-}
-
-static int
-bt832_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- struct bt832 *t = i2c_get_clientdata(client);
-
- if (debug>1)
- v4l_i2c_print_ioctl(&t->client,cmd);
-
- switch (cmd) {
- case BT832_HEXDUMP: {
- unsigned char *buf;
- buf = kmalloc(65, GFP_KERNEL);
- if (!buf) {
- v4l_err(&t->client,
- "Unable to allocate memory\n");
- break;
- }
- bt832_hexdump(&t->client,buf);
- kfree(buf);
- }
- break;
- case BT832_REATTACH:
- v4l_info(&t->client,"re-attach\n");
- i2c_del_driver(&driver);
- i2c_add_driver(&driver);
- break;
- }
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver driver = {
- .driver = {
- .name = "bt832",
- },
- .id = 0, /* FIXME */
- .attach_adapter = bt832_probe,
- .detach_client = bt832_detach,
- .command = bt832_command,
-};
-static struct i2c_client client_template =
-{
- .name = "bt832",
- .driver = &driver,
-};
-
-
-static int __init bt832_init_module(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit bt832_cleanup_module(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(bt832_init_module);
-module_exit(bt832_cleanup_module);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/bt8xx/bt832.h b/drivers/media/video/bt8xx/bt832.h
deleted file mode 100644
index 1ce8fa71f7db..000000000000
--- a/drivers/media/video/bt8xx/bt832.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/* Bt832 CMOS Camera Video Processor (VP)
-
- The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS
- color digital camera directly to video capture devices via an 8-bit,
- 4:2:2 YUV or YCrCb video interface.
-
- i2c addresses: 0x88 or 0x8a
- */
-
-/* The 64 registers: */
-
-// Input Processor
-#define BT832_OFFSET 0
-#define BT832_RCOMP 1
-#define BT832_G1COMP 2
-#define BT832_G2COMP 3
-#define BT832_BCOMP 4
-// Exposures:
-#define BT832_FINEH 5
-#define BT832_FINEL 6
-#define BT832_COARSEH 7
-#define BT832_COARSEL 8
-#define BT832_CAMGAIN 9
-// Main Processor:
-#define BT832_M00 10
-#define BT832_M01 11
-#define BT832_M02 12
-#define BT832_M10 13
-#define BT832_M11 14
-#define BT832_M12 15
-#define BT832_M20 16
-#define BT832_M21 17
-#define BT832_M22 18
-#define BT832_APCOR 19
-#define BT832_GAMCOR 20
-// Level Accumulator Inputs
-#define BT832_VPCONTROL2 21
-#define BT832_ZONECODE0 22
-#define BT832_ZONECODE1 23
-#define BT832_ZONECODE2 24
-#define BT832_ZONECODE3 25
-// Level Accumulator Outputs:
-#define BT832_RACC 26
-#define BT832_GACC 27
-#define BT832_BACC 28
-#define BT832_BLACKACC 29
-#define BT832_EXP_AGC 30
-#define BT832_LACC0 31
-#define BT832_LACC1 32
-#define BT832_LACC2 33
-#define BT832_LACC3 34
-#define BT832_LACC4 35
-#define BT832_LACC5 36
-#define BT832_LACC6 37
-#define BT832_LACC7 38
-// System:
-#define BT832_VP_CONTROL0 39
-#define BT832_VP_CONTROL1 40
-#define BT832_THRESH 41
-#define BT832_VP_TESTCONTROL0 42
-#define BT832_VP_DMCODE 43
-#define BT832_ACB_CONFIG 44
-#define BT832_ACB_GNBASE 45
-#define BT832_ACB_MU 46
-#define BT832_CAM_TEST0 47
-#define BT832_AEC_CONFIG 48
-#define BT832_AEC_TL 49
-#define BT832_AEC_TC 50
-#define BT832_AEC_TH 51
-// Status:
-#define BT832_VP_STATUS 52
-#define BT832_VP_LINECOUNT 53
-#define BT832_CAM_DEVICEL 54 // e.g. 0x19
-#define BT832_CAM_DEVICEH 55 // e.g. 0x40 == 0x194 Mask0, 0x194 = 404 decimal (VVL-404 camera)
-#define BT832_CAM_STATUS 56
- #define BT832_56_CAMERA_PRESENT 0x20
-//Camera Setups:
-#define BT832_CAM_SETUP0 57
-#define BT832_CAM_SETUP1 58
-#define BT832_CAM_SETUP2 59
-#define BT832_CAM_SETUP3 60
-// System:
-#define BT832_DEFCOR 61
-#define BT832_VP_TESTCONTROL1 62
-#define BT832_DEVICE_ID 63
-# define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31
-
-/* STMicroelectronivcs VV5404 camera module
- i2c: 0x20: sensor address
- i2c: 0xa0: eeprom for ccd defect map
- */
-#define VV5404_device_h 0x00 // 0x19
-#define VV5404_device_l 0x01 // 0x40
-#define VV5404_status0 0x02
-#define VV5404_linecountc 0x03 // current line counter
-#define VV5404_linecountl 0x04
-#define VV5404_setup0 0x10
-#define VV5404_setup1 0x11
-#define VV5404_setup2 0x12
-#define VV5404_setup4 0x14
-#define VV5404_setup5 0x15
-#define VV5404_fine_h 0x20 // fine exposure
-#define VV5404_fine_l 0x21
-#define VV5404_coarse_h 0x22 //coarse exposure
-#define VV5404_coarse_l 0x23
-#define VV5404_gain 0x24 // ADC pre-amp gain setting
-#define VV5404_clk_div 0x25
-#define VV5404_cr 0x76 // control register
-#define VV5404_as0 0x77 // ADC setup register
-
-
-// IOCTL
-#define BT832_HEXDUMP _IOR('b',1,int)
-#define BT832_REATTACH _IOR('b',2,int)
-
-/* from BT8x8VXD/capdrv/dialogs.cpp */
-
-/*
-typedef enum { SVI, Logitech, Rockwell } CAMERA;
-
-static COMBOBOX_ENTRY gwCameraOptions[] =
-{
- { SVI, "Silicon Vision 512N" },
- { Logitech, "Logitech VideoMan 1.3" },
- { Rockwell, "Rockwell QuartzSight PCI 1.0" }
-};
-
-// SRAM table values
-//===========================================================================
-typedef enum { TGB_NTSC624, TGB_NTSC780, TGB_NTSC858, TGB_NTSC392 } TimeGenByte;
-
-BYTE SRAMTable[][ 60 ] =
-{
- // TGB_NTSC624
- {
- 0x33, // size of table = 51
- 0x0E, 0xC0, 0x00, 0x00, 0x90, 0x02, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x04, 0x12, 0x12, 0x05, 0x02, 0x13, 0x04, 0x19, 0x00,
- 0x04, 0x39, 0x00, 0x06, 0x59, 0x08, 0x03, 0x85, 0x08, 0x07,
- 0x03, 0x50, 0x00, 0x91, 0x40, 0x00, 0x11, 0x01, 0x01, 0x4D,
- 0x0D, 0x02, 0x03, 0x11, 0x01, 0x05, 0x37, 0x00, 0x37, 0x21, 0x00
- },
- // TGB_NTSC780
- {
- 0x33, // size of table = 51
- 0x0e, 0xc0, 0x00, 0x00, 0x90, 0xe2, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00,
- 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x85, 0x08, 0x97,
- 0x03, 0x50, 0x50, 0xaf, 0x40, 0x30, 0x5f, 0x01, 0xf1, 0x7f,
- 0x0d, 0xf2, 0x03, 0x11, 0xf1, 0x05, 0x37, 0x30, 0x85, 0x21, 0x50
- },
- // TGB_NTSC858
- {
- 0x33, // size of table = 51
- 0x0c, 0xc0, 0x00, 0x00, 0x90, 0xc2, 0x03, 0x10, 0x03, 0x06,
- 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00,
- 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x83, 0x08, 0x97,
- 0x03, 0x50, 0x30, 0xc0, 0x40, 0x30, 0x86, 0x01, 0x01, 0xa6,
- 0x0d, 0x62, 0x03, 0x11, 0x61, 0x05, 0x37, 0x30, 0xac, 0x21, 0x50
- },
- // TGB_NTSC392
- // This table has been modified to be used for Fusion Rev D
- {
- 0x2A, // size of table = 42
- 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
- 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
- 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
- 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
- 0x20, 0x00
- }
-};
-
-//===========================================================================
-// This is the structure of the camera specifications
-//===========================================================================
-typedef struct tag_cameraSpec
-{
- SignalFormat signal; // which digital signal format the camera has
- VideoFormat vidFormat; // video standard
- SyncVideoRef syncRef; // which sync video reference is used
- State syncOutput; // enable sync output for sync video input?
- DecInputClk iClk; // which input clock is used
- TimeGenByte tgb; // which timing generator byte does the camera use
- int HReset; // select 64, 48, 32, or 16 CLKx1 for HReset
- PLLFreq pllFreq; // what synthesized frequency to set PLL to
- VSIZEPARMS vSize; // video size the camera produces
- int lineCount; // expected total number of half-line per frame - 1
- BOOL interlace; // interlace signal?
-} CameraSpec;
-
-//===========================================================================
-// <UPDATE REQUIRED>
-// Camera specifications database. Update this table whenever camera spec
-// has been changed or added/deleted supported camera models
-//===========================================================================
-static CameraSpec dbCameraSpec[ N_CAMERAOPTIONS ] =
-{ // Silicon Vision 512N
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC624, 64, KHz19636,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 512, 0x64, 480, 0x13, 240 }, 0, TRUE
- },
- // Logitech VideoMan 1.3
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC780, 64, KHz24545,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 640, 0x80, 480, 0x1A, 240 }, 0, TRUE
- },
- // Rockwell QuartzSight
- // Note: Fusion Rev D (rev ID 0x02) and later supports 16 pixels for HReset which is preferable.
- // Use 32 for earlier version of hardware. Clkx1_HDELAY also changed from 0x27 to 0x20.
- { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC392, 16, KHz28636,
- // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace
- { 352, 0x20, 576, 0x08, 288 }, 607, FALSE
- }
-};
-*/
-
-/*
-The corresponding APIs required to be invoked are:
-SetConnector( ConCamera, TRUE/FALSE );
-SetSignalFormat( spec.signal );
-SetVideoFormat( spec.vidFormat );
-SetSyncVideoRef( spec.syncRef );
-SetEnableSyncOutput( spec.syncOutput );
-SetTimGenByte( SRAMTable[ spec.tgb ], SRAMTableSize[ spec.tgb ] );
-SetHReset( spec.HReset );
-SetPLL( spec.pllFreq );
-SetDecInputClock( spec.iClk );
-SetVideoInfo( spec.vSize );
-SetTotalLineCount( spec.lineCount );
-SetInterlaceMode( spec.interlace );
-*/
-
-/* from web:
- Video Sampling
-Digital video is a sampled form of analog video. The most common sampling schemes in use today are:
- Pixel Clock Horiz Horiz Vert
- Rate Total Active
-NTSC square pixel 12.27 MHz 780 640 525
-NTSC CCIR-601 13.5 MHz 858 720 525
-NTSC 4FSc 14.32 MHz 910 768 525
-PAL square pixel 14.75 MHz 944 768 625
-PAL CCIR-601 13.5 MHz 864 720 625
-PAL 4FSc 17.72 MHz 1135 948 625
-
-For the CCIR-601 standards, the sampling is based on a static orthogonal sampling grid. The luminance component (Y) is sampled at 13.5 MHz, while the two color difference signals, Cr and Cb are sampled at half that, or 6.75 MHz. The Cr and Cb samples are colocated with alternate Y samples, and they are taken at the same position on each line, such that one sample is coincident with the 50% point of the falling edge of analog sync. The samples are coded to either 8 or 10 bits per component.
-*/
-
-/* from DScaler:*/
-/*
-//===========================================================================
-// CCIR656 Digital Input Support: The tables were taken from DScaler proyect
-//
-// 13 Dec 2000 - Michael Eskin, Conexant Systems - Initial version
-//
-
-//===========================================================================
-// Timing generator SRAM table values for CCIR601 720x480 NTSC
-//===========================================================================
-// For NTSC CCIR656
-BYTE BtCard::SRAMTable_NTSC[] =
-{
- // SRAM Timing Table for NTSC
- 0x0c, 0xc0, 0x00,
- 0x00, 0x90, 0xc2,
- 0x03, 0x10, 0x03,
- 0x06, 0x10, 0x34,
- 0x12, 0x12, 0x65,
- 0x02, 0x13, 0x24,
- 0x19, 0x00, 0x24,
- 0x39, 0x00, 0x96,
- 0x59, 0x08, 0x93,
- 0x83, 0x08, 0x97,
- 0x03, 0x50, 0x30,
- 0xc0, 0x40, 0x30,
- 0x86, 0x01, 0x01,
- 0xa6, 0x0d, 0x62,
- 0x03, 0x11, 0x61,
- 0x05, 0x37, 0x30,
- 0xac, 0x21, 0x50
-};
-
-//===========================================================================
-// Timing generator SRAM table values for CCIR601 720x576 NTSC
-//===========================================================================
-// For PAL CCIR656
-BYTE BtCard::SRAMTable_PAL[] =
-{
- // SRAM Timing Table for PAL
- 0x36, 0x11, 0x01,
- 0x00, 0x90, 0x02,
- 0x05, 0x10, 0x04,
- 0x16, 0x14, 0x05,
- 0x11, 0x00, 0x04,
- 0x12, 0xc0, 0x00,
- 0x31, 0x00, 0x06,
- 0x51, 0x08, 0x03,
- 0x89, 0x08, 0x07,
- 0xc0, 0x44, 0x00,
- 0x81, 0x01, 0x01,
- 0xa9, 0x0d, 0x02,
- 0x02, 0x50, 0x03,
- 0x37, 0x3d, 0x00,
- 0xaf, 0x21, 0x00,
-};
-*/
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 13742b0bbe3e..d24dcc025e37 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -44,7 +44,6 @@
/* fwd decl */
static void boot_msp34xx(struct bttv *btv, int pin);
-static void boot_bt832(struct bttv *btv);
static void hauppauge_eeprom(struct bttv *btv);
static void avermedia_eeprom(struct bttv *btv);
static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
@@ -2217,9 +2216,9 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD009X1_MINIDIN] = {
+ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
/* M.Klahr@phytec.de */
- .name = "PHYTEC VD-009-X1 MiniDIN (bt878)",
+ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
.video_inputs = 4,
.audio_inputs = 0,
.tuner = UNSET, /* card has no tuner */
@@ -2227,14 +2226,14 @@ struct tvcard bttv_tvcards[] = {
.gpiomask = 0x00,
.muxsel = { 2, 3, 1, 0 },
.gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
- .needs_tvaudio = 1,
+ .needs_tvaudio = 0,
.pll = PLL_28,
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
},
- [BTTV_BOARD_VD009X1_COMBI] = {
- .name = "PHYTEC VD-009-X1 Combi (bt878)",
+ [BTTV_BOARD_VD009X1_VD011_COMBI] = {
+ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
.video_inputs = 4,
.audio_inputs = 0,
.tuner = UNSET, /* card has no tuner */
@@ -2242,7 +2241,7 @@ struct tvcard bttv_tvcards[] = {
.gpiomask = 0x00,
.muxsel = { 2, 3, 1, 1 },
.gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
- .needs_tvaudio = 1,
+ .needs_tvaudio = 0,
.pll = PLL_28,
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
@@ -3061,6 +3060,54 @@ struct tvcard bttv_tvcards[] = {
.pll = PLL_28,
.has_radio = 1,
.has_remote = 1,
+ },
+ [BTTV_BOARD_VD012] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = UNSET, /* card has no s-video */
+ .gpiomask = 0x00,
+ .muxsel = { 0, 2, 3, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X1] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X1 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = { 2, 3, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X2] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X2 (bt878)",
+ .video_inputs = 4,
+ .audio_inputs = 0,
+ .tuner = UNSET, /* card has no tuner */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = { 3, 2, 1 },
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
}
};
@@ -3673,13 +3720,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
if (bttv_tvcards[btv->c.type].audio_mode_gpio)
btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
- if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) {
- /* detect Bt832 chip for quartzsight digital camera */
- if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) ||
- (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0))
- boot_bt832(btv);
- }
-
if (!autoload)
return;
@@ -4075,10 +4115,6 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin)
"init [%d]\n", btv->c.nr, pin);
}
-static void __devinit boot_bt832(struct bttv *btv)
-{
-}
-
/* ----------------------------------------------------------------------- */
/* Imagenation L-Model PXC200 Framegrabber */
/* This is basically the same procedure as
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c
index dce6dae5740e..74c325e594a2 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/video/bt8xx/bttv-gpio.c
@@ -42,7 +42,7 @@ static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
int len = strlen(sub->wanted);
- if (0 == strncmp(dev->bus_id, sub->wanted, len))
+ if (0 == strncmp(dev_name(dev), sub->wanted, len))
return 1;
return 0;
}
@@ -91,15 +91,14 @@ int bttv_sub_add_device(struct bttv_core *core, char *name)
sub->dev.parent = &core->pci->dev;
sub->dev.bus = &bttv_sub_bus_type;
sub->dev.release = release_sub_device;
- snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d",
- name, core->nr);
+ dev_set_name(&sub->dev, "%s%d", name, core->nr);
err = device_register(&sub->dev);
if (0 != err) {
kfree(sub);
return err;
}
- printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id);
+ printk("bttv%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
list_add_tail(&sub->list,&core->subs);
return 0;
}
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 46cb90e0985b..529bf6cf634d 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -130,8 +130,8 @@
#define BTTV_BOARD_XGUARD 0x67
#define BTTV_BOARD_NEBULA_DIGITV 0x68
#define BTTV_BOARD_PV143 0x69
-#define BTTV_BOARD_VD009X1_MINIDIN 0x6a
-#define BTTV_BOARD_VD009X1_COMBI 0x6b
+#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a
+#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b
#define BTTV_BOARD_VD009_MINIDIN 0x6c
#define BTTV_BOARD_VD009_COMBI 0x6d
#define BTTV_BOARD_IVC100 0x6e
@@ -177,6 +177,10 @@
#define BTTV_BOARD_GEOVISION_GV600 0x96
#define BTTV_BOARD_KOZUMI_KTV_01C 0x97
#define BTTV_BOARD_ENLTV_FM_2 0x98
+#define BTTV_BOARD_VD012 0x99
+#define BTTV_BOARD_VD012_X1 0x9a
+#define BTTV_BOARD_VD012_X2 0x9b
+
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
@@ -308,7 +312,7 @@ struct bttv_sub_device {
struct bttv_sub_driver {
struct device_driver drv;
- char wanted[BUS_ID_SIZE];
+ char wanted[20];
int (*probe)(struct bttv_sub_device *sub);
void (*remove)(struct bttv_sub_device *sub);
};
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index b4d940b2e447..199a4d225caf 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -459,7 +459,7 @@ struct bttv {
};
/* our devices */
-#define BTTV_MAX 16
+#define BTTV_MAX 32
extern unsigned int bttv_num;
extern struct bttv bttvs[BTTV_MAX];
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index ace4ff9ea023..17f80d03f38e 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -706,8 +706,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l
* Video4linux interfacing
*/
-static int qcam_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam=(struct qcam_device *)dev;
@@ -867,7 +866,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file,
static int qcam_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+ return video_usercopy(file, cmd, arg, qcam_do_ioctl);
}
static ssize_t qcam_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 0f930d351466..21c71eb085db 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -500,8 +500,7 @@ static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long le
* Video4linux interfacing
*/
-static int qcam_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int qcam_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct qcam_device *qcam=(struct qcam_device *)dev;
@@ -667,9 +666,9 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file,
}
static int qcam_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+ return video_usercopy(file, cmd, arg, qcam_do_ioctl);
}
static ssize_t qcam_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c
deleted file mode 100644
index e6ca4012b5f0..000000000000
--- a/drivers/media/video/compat_ioctl32.c
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
- * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
- *
- * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
- * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
- * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
- *
- * These routines maintain argument size conversion between 32bit and 64bit
- * ioctls.
- */
-
-#include <linux/compat.h>
-#include <linux/videodev.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <media/v4l2-ioctl.h>
-
-#ifdef CONFIG_COMPAT
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-struct video_tuner32 {
- compat_int_t tuner;
- char name[32];
- compat_ulong_t rangelow, rangehigh;
- u32 flags; /* It is really u32 in videodev.h */
- u16 mode, signal;
-};
-
-static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
-{
- if(!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) ||
- get_user(kp->tuner, &up->tuner) ||
- copy_from_user(kp->name, up->name, 32) ||
- get_user(kp->rangelow, &up->rangelow) ||
- get_user(kp->rangehigh, &up->rangehigh) ||
- get_user(kp->flags, &up->flags) ||
- get_user(kp->mode, &up->mode) ||
- get_user(kp->signal, &up->signal))
- return -EFAULT;
- return 0;
-}
-
-static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
-{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) ||
- put_user(kp->tuner, &up->tuner) ||
- copy_to_user(up->name, kp->name, 32) ||
- put_user(kp->rangelow, &up->rangelow) ||
- put_user(kp->rangehigh, &up->rangehigh) ||
- put_user(kp->flags, &up->flags) ||
- put_user(kp->mode, &up->mode) ||
- put_user(kp->signal, &up->signal))
- return -EFAULT;
- return 0;
-}
-
-
-struct video_buffer32 {
- compat_caddr_t base;
- compat_int_t height, width, depth, bytesperline;
-};
-
-static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up)
-{
- u32 tmp;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct video_buffer32)) ||
- get_user(tmp, &up->base) ||
- get_user(kp->height, &up->height) ||
- get_user(kp->width, &up->width) ||
- get_user(kp->depth, &up->depth) ||
- get_user(kp->bytesperline, &up->bytesperline))
- return -EFAULT;
-
- /* This is actually a physical address stored
- * as a void pointer.
- */
- kp->base = (void *)(unsigned long) tmp;
-
- return 0;
-}
-
-static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up)
-{
- u32 tmp = (u32)((unsigned long)kp->base);
-
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) ||
- put_user(tmp, &up->base) ||
- put_user(kp->height, &up->height) ||
- put_user(kp->width, &up->width) ||
- put_user(kp->depth, &up->depth) ||
- put_user(kp->bytesperline, &up->bytesperline))
- return -EFAULT;
- return 0;
-}
-
-struct video_clip32 {
- s32 x, y, width, height; /* Its really s32 in videodev.h */
- compat_caddr_t next;
-};
-
-struct video_window32 {
- u32 x, y, width, height, chromakey, flags;
- compat_caddr_t clips;
- compat_int_t clipcount;
-};
-#endif
-
-static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret = -ENOIOCTLCMD;
-
- if (file->f_op->unlocked_ioctl)
- ret = file->f_op->unlocked_ioctl(file, cmd, arg);
- else if (file->f_op->ioctl) {
- lock_kernel();
- ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
- unlock_kernel();
- }
-
- return ret;
-}
-
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-/* You get back everything except the clips... */
-static int put_video_window32(struct video_window *kp, struct video_window32 __user *up)
-{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) ||
- put_user(kp->x, &up->x) ||
- put_user(kp->y, &up->y) ||
- put_user(kp->width, &up->width) ||
- put_user(kp->height, &up->height) ||
- put_user(kp->chromakey, &up->chromakey) ||
- put_user(kp->flags, &up->flags) ||
- put_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
- return 0;
-}
-#endif
-
-struct v4l2_clip32
-{
- struct v4l2_rect c;
- compat_caddr_t next;
-};
-
-struct v4l2_window32
-{
- struct v4l2_rect w;
- enum v4l2_field field;
- __u32 chromakey;
- compat_caddr_t clips; /* actually struct v4l2_clip32 * */
- __u32 clipcount;
- compat_caddr_t bitmap;
-};
-
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
- copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
- get_user(kp->field, &up->field) ||
- get_user(kp->chromakey, &up->chromakey) ||
- get_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
- if (kp->clipcount > 2048)
- return -EINVAL;
- if (kp->clipcount) {
- struct v4l2_clip32 __user *uclips;
- struct v4l2_clip __user *kclips;
- int n = kp->clipcount;
- compat_caddr_t p;
-
- if (get_user(p, &up->clips))
- return -EFAULT;
- uclips = compat_ptr(p);
- kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
- kp->clips = kclips;
- while (--n >= 0) {
- if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
- return -EFAULT;
- if (put_user(n ? kclips + 1 : NULL, &kclips->next))
- return -EFAULT;
- uclips += 1;
- kclips += 1;
- }
- } else
- kp->clips = NULL;
- return 0;
-}
-
-static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
- if (copy_to_user(&up->w, &kp->w, sizeof(up->w)) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->chromakey, &up->chromakey) ||
- put_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_format32
-{
- enum v4l2_buf_type type;
- union
- {
- struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE
- struct v4l2_window32 win; // V4L2_BUF_TYPE_VIDEO_OVERLAY
- struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE
- __u8 raw_data[200]; // user-defined
- } fmt;
-};
-
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
- get_user(kp->type, &up->type))
- return -EFAULT;
- switch (kp->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
- default:
- printk("compat_ioctl : unexpected VIDIOC_FMT type %d\n",
- kp->type);
- return -ENXIO;
- }
-}
-
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
- put_user(kp->type, &up->type))
- return -EFAULT;
- switch (kp->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
- default:
- return -ENXIO;
- }
-}
-
-static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_standard)))
- return -EFAULT;
- return 0;
-
-}
-
-static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_standard)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_standard32
-{
- __u32 index;
- __u32 id[2]; /* __u64 would get the alignment wrong */
- __u8 name[24];
- struct v4l2_fract frameperiod; /* Frames, not fields */
- __u32 framelines;
- __u32 reserved[4];
-};
-
-static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
- /* other fields are not set by the user, nor used by the driver */
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
- get_user(kp->index, &up->index))
- return -EFAULT;
- return 0;
-}
-
-static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
- put_user(kp->index, &up->index) ||
- copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
- copy_to_user(up->name, kp->name, 24) ||
- copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
- put_user(kp->framelines, &up->framelines) ||
- copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_tuner)))
- return -EFAULT;
- return 0;
-
-}
-
-static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_tuner)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_buffer32
-{
- __u32 index;
- enum v4l2_buf_type type;
- __u32 bytesused;
- __u32 flags;
- enum v4l2_field field;
- struct compat_timeval timestamp;
- struct v4l2_timecode timecode;
- __u32 sequence;
-
- /* memory location */
- enum v4l2_memory memory;
- union {
- __u32 offset;
- compat_long_t userptr;
- } m;
- __u32 length;
- __u32 input;
- __u32 reserved;
-};
-
-static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
-{
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
- get_user(kp->index, &up->index) ||
- get_user(kp->type, &up->type) ||
- get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory) ||
- get_user(kp->input, &up->input))
- return -EFAULT;
- switch(kp->memory) {
- case V4L2_MEMORY_MMAP:
- break;
- case V4L2_MEMORY_USERPTR:
- {
- compat_long_t tmp;
-
- if (get_user(kp->length, &up->length) ||
- get_user(tmp, &up->m.userptr))
- return -EFAULT;
-
- kp->m.userptr = (unsigned long)compat_ptr(tmp);
- }
- break;
- case V4L2_MEMORY_OVERLAY:
- if(get_user(kp->m.offset, &up->m.offset))
- return -EFAULT;
- break;
- }
- return 0;
-}
-
-static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
-{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
- put_user(kp->index, &up->index) ||
- put_user(kp->type, &up->type) ||
- put_user(kp->flags, &up->flags) ||
- put_user(kp->memory, &up->memory) ||
- put_user(kp->input, &up->input))
- return -EFAULT;
- switch(kp->memory) {
- case V4L2_MEMORY_MMAP:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.offset, &up->m.offset))
- return -EFAULT;
- break;
- case V4L2_MEMORY_USERPTR:
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.userptr, &up->m.userptr))
- return -EFAULT;
- break;
- case V4L2_MEMORY_OVERLAY:
- if (put_user(kp->m.offset, &up->m.offset))
- return -EFAULT;
- break;
- }
- if (put_user(kp->bytesused, &up->bytesused) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
- put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
- copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
- put_user(kp->sequence, &up->sequence) ||
- put_user(kp->reserved, &up->reserved))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_framebuffer32
-{
- __u32 capability;
- __u32 flags;
- compat_caddr_t base;
- struct v4l2_pix_format fmt;
-};
-
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
- u32 tmp;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
- get_user(tmp, &up->base) ||
- get_user(kp->capability, &up->capability) ||
- get_user(kp->flags, &up->flags))
- return -EFAULT;
- kp->base = compat_ptr(tmp);
- get_v4l2_pix_format(&kp->fmt, &up->fmt);
- return 0;
-}
-
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
- u32 tmp = (u32)((unsigned long)kp->base);
-
- if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
- put_user(tmp, &up->base) ||
- put_user(kp->capability, &up->capability) ||
- put_user(kp->flags, &up->flags))
- return -EFAULT;
- put_v4l2_pix_format(&kp->fmt, &up->fmt);
- return 0;
-}
-
-static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input) - 4))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input) - 4))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input)))
- return -EFAULT;
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-struct video_code32
-{
- char loadwhat[16]; /* name or tag of file being passed */
- compat_int_t datasize;
- unsigned char *data;
-};
-
-static inline int microcode32(struct video_code *kp, struct video_code32 __user *up)
-{
- if(!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) ||
- copy_from_user(kp->loadwhat, up->loadwhat, sizeof (up->loadwhat)) ||
- get_user(kp->datasize, &up->datasize) ||
- copy_from_user(kp->data, up->data, up->datasize))
- return -EFAULT;
- return 0;
-}
-
-#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32)
-#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32)
-#define VIDIOCGWIN32 _IOR('v',9, struct video_window32)
-#define VIDIOCSWIN32 _IOW('v',10, struct video_window32)
-#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32)
-#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32)
-#define VIDIOCGFREQ32 _IOR('v',14, u32)
-#define VIDIOCSFREQ32 _IOW('v',15, u32)
-#define VIDIOCSMICROCODE32 _IOW('v',27, struct video_code32)
-
-#endif
-
-/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */
-#define VIDIOC_ENUMINPUT32 VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4)
-#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32)
-#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32)
-#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32)
-#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
-#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
-/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */
-#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t)
-#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32)
-#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32)
-#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t)
-#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t)
-#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32)
-/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */
-#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control)
-#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t)
-#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t)
-#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32)
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-enum {
- MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip)
-};
-
-static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct video_window32 __user *up = compat_ptr(arg);
- struct video_window __user *vw;
- struct video_clip __user *p;
- int nclips;
- u32 n;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)))
- return -EFAULT;
-
- if (get_user(nclips, &up->clipcount))
- return -EFAULT;
-
- /* Peculiar interface... */
- if (nclips < 0)
- nclips = VIDEO_CLIPMAP_SIZE;
-
- if (nclips > MaxClips)
- return -ENOMEM;
-
- vw = compat_alloc_user_space(sizeof(struct video_window) +
- nclips * sizeof(struct video_clip));
-
- p = nclips ? (struct video_clip __user *)(vw + 1) : NULL;
-
- if (get_user(n, &up->x) || put_user(n, &vw->x) ||
- get_user(n, &up->y) || put_user(n, &vw->y) ||
- get_user(n, &up->width) || put_user(n, &vw->width) ||
- get_user(n, &up->height) || put_user(n, &vw->height) ||
- get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) ||
- get_user(n, &up->flags) || put_user(n, &vw->flags) ||
- get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) ||
- get_user(n, &up->clips) || put_user(p, &vw->clips))
- return -EFAULT;
-
- if (nclips) {
- struct video_clip32 __user *u = compat_ptr(n);
- int i;
- if (!u)
- return -EINVAL;
- for (i = 0; i < nclips; i++, u++, p++) {
- s32 v;
- if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) ||
- !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) ||
- get_user(v, &u->x) ||
- put_user(v, &p->x) ||
- get_user(v, &u->y) ||
- put_user(v, &p->y) ||
- get_user(v, &u->width) ||
- put_user(v, &p->width) ||
- get_user(v, &u->height) ||
- put_user(v, &p->height) ||
- put_user(NULL, &p->next))
- return -EFAULT;
- }
- }
-
- return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw);
-}
-#endif
-
-static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- union {
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- struct video_tuner vt;
- struct video_buffer vb;
- struct video_window vw;
- struct video_code vc;
- struct video_audio va;
-#endif
- struct v4l2_format v2f;
- struct v4l2_buffer v2b;
- struct v4l2_framebuffer v2fb;
- struct v4l2_standard v2s;
- struct v4l2_input v2i;
- struct v4l2_tuner v2t;
- unsigned long vx;
- } karg;
- void __user *up = compat_ptr(arg);
- int compatible_arg = 1;
- int err = 0;
- int realcmd = cmd;
-
- /* First, convert the command. */
- switch(cmd) {
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break;
- case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break;
- case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break;
- case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break;
- case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break;
- case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break;
- case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break;
- case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break;
-#endif
- case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break;
- case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break;
- case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break;
- case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break;
- case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break;
- case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break;
- case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break;
- case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break;
- case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break;
- case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break;
- case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break;
- case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break;
- case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break;
- case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break;
- case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break;
- case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break;
- };
-
- switch(cmd) {
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCSTUNER:
- case VIDIOCGTUNER:
- err = get_video_tuner32(&karg.vt, up);
- compatible_arg = 0;
-
- break;
-
- case VIDIOCSFBUF:
- err = get_video_buffer32(&karg.vb, up);
- compatible_arg = 0;
- break;
-
-
- case VIDIOCSFREQ:
-#endif
- case VIDIOC_S_INPUT:
- case VIDIOC_OVERLAY:
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- err = get_user(karg.vx, (u32 __user *)up);
- compatible_arg = 1;
- break;
-
- case VIDIOC_S_FBUF:
- err = get_v4l2_framebuffer32(&karg.v2fb, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT:
- err = get_v4l2_format32(&karg.v2f, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_QUERYBUF:
- case VIDIOC_QBUF:
- case VIDIOC_DQBUF:
- err = get_v4l2_buffer32(&karg.v2b, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMSTD:
- err = get_v4l2_standard(&karg.v2s, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMSTD32:
- err = get_v4l2_standard32(&karg.v2s, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMINPUT:
- err = get_v4l2_input(&karg.v2i, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMINPUT32:
- err = get_v4l2_input32(&karg.v2i, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- err = get_v4l2_tuner(&karg.v2t, up);
- compatible_arg = 0;
- break;
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGWIN:
- case VIDIOCGFBUF:
- case VIDIOCGFREQ:
-#endif
- case VIDIOC_G_FBUF:
- case VIDIOC_G_INPUT:
- compatible_arg = 0;
- break;
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCSMICROCODE:
- err = microcode32(&karg.vc, up);
- compatible_arg = 0;
- break;
-#endif
- };
- if(err)
- goto out;
-
- if(compatible_arg)
- err = native_ioctl(file, realcmd, (unsigned long)up);
- else {
- mm_segment_t old_fs = get_fs();
-
- set_fs(KERNEL_DS);
- err = native_ioctl(file, realcmd, (unsigned long) &karg);
- set_fs(old_fs);
- }
- if(err == 0) {
- switch(cmd) {
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGTUNER:
- err = put_video_tuner32(&karg.vt, up);
- break;
-
- case VIDIOCGWIN:
- err = put_video_window32(&karg.vw, up);
- break;
-
- case VIDIOCGFBUF:
- err = put_video_buffer32(&karg.vb, up);
- break;
-
-#endif
- case VIDIOC_G_FBUF:
- err = put_v4l2_framebuffer32(&karg.v2fb, up);
- break;
-
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT:
- err = put_v4l2_format32(&karg.v2f, up);
- break;
-
- case VIDIOC_QUERYBUF:
- case VIDIOC_QBUF:
- case VIDIOC_DQBUF:
- err = put_v4l2_buffer32(&karg.v2b, up);
- break;
-
- case VIDIOC_ENUMSTD:
- err = put_v4l2_standard(&karg.v2s, up);
- break;
-
- case VIDIOC_ENUMSTD32:
- err = put_v4l2_standard32(&karg.v2s, up);
- break;
-
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- err = put_v4l2_tuner(&karg.v2t, up);
- break;
-
- case VIDIOC_ENUMINPUT:
- err = put_v4l2_input(&karg.v2i, up);
- break;
-
- case VIDIOC_ENUMINPUT32:
- err = put_v4l2_input32(&karg.v2i, up);
- break;
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGFREQ:
-#endif
- case VIDIOC_G_INPUT:
- err = put_user(((u32)karg.vx), (u32 __user *)up);
- break;
- };
- }
-out:
- return err;
-}
-
-long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret = -ENOIOCTLCMD;
-
- if (!file->f_op->ioctl)
- return ret;
-
- switch (cmd) {
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCSWIN32:
- ret = do_set_window(file, cmd, arg);
- break;
- case VIDIOCGTUNER32:
- case VIDIOCSTUNER32:
- case VIDIOCGWIN32:
- case VIDIOCGFBUF32:
- case VIDIOCSFBUF32:
- case VIDIOCGFREQ32:
- case VIDIOCSFREQ32:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- case VIDIOCGVBIFMT:
- case VIDIOCSVBIFMT:
-#endif
- case VIDIOC_QUERYCAP:
- case VIDIOC_ENUM_FMT:
- case VIDIOC_G_FMT32:
- case VIDIOC_CROPCAP:
- case VIDIOC_S_CROP:
- case VIDIOC_S_FMT32:
- case VIDIOC_REQBUFS:
- case VIDIOC_QUERYBUF32:
- case VIDIOC_G_FBUF32:
- case VIDIOC_S_FBUF32:
- case VIDIOC_OVERLAY32:
- case VIDIOC_QBUF32:
- case VIDIOC_DQBUF32:
- case VIDIOC_STREAMON32:
- case VIDIOC_STREAMOFF32:
- case VIDIOC_G_PARM:
- case VIDIOC_S_PARM:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_ENUMSTD:
- case VIDIOC_ENUMSTD32:
- case VIDIOC_ENUMINPUT:
- case VIDIOC_ENUMINPUT32:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- case VIDIOC_S_CTRL32:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_QUERYCTRL:
- case VIDIOC_G_INPUT32:
- case VIDIOC_S_INPUT32:
- case VIDIOC_TRY_FMT32:
- case VIDIOC_S_HW_FREQ_SEEK:
- case VIDIOC_ENUM_FRAMESIZES:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- ret = do_video_ioctl(file, cmd, arg);
- break;
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- /* Little v, the video4linux ioctls (conflict?) */
- case VIDIOCGCAP:
- case VIDIOCGCHAN:
- case VIDIOCSCHAN:
- case VIDIOCGPICT:
- case VIDIOCSPICT:
- case VIDIOCCAPTURE:
- case VIDIOCKEY:
- case VIDIOCSYNC:
- case VIDIOCMCAPTURE:
- case VIDIOCGMBUF:
- case VIDIOCGUNIT:
- case VIDIOCGCAPTURE:
- case VIDIOCSCAPTURE:
-
- /* BTTV specific... */
- case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]):
- case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]):
- case _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int):
- case _IOW('v' , BASE_VIDIOCPRIVATE+3, char [16]): /* struct bttv_pll_info */
- case _IOR('v' , BASE_VIDIOCPRIVATE+4, int):
- case _IOR('v' , BASE_VIDIOCPRIVATE+5, int):
- case _IOR('v' , BASE_VIDIOCPRIVATE+6, int):
- case _IOR('v' , BASE_VIDIOCPRIVATE+7, int):
- ret = native_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
- break;
-#endif
- default:
- v4l_print_ioctl("compat_ioctl32", cmd);
- }
- return ret;
-}
-#else
-long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- return -ENOIOCTLCMD;
-}
-#endif
-EXPORT_SYMBOL_GPL(v4l_compat_ioctl32);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index 16c094f77852..028a400d2453 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -3333,8 +3333,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf,
return cam->decompressed_frame.count;
}
-static int cpia_do_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctlnr, void *arg)
+static int cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = file->private_data;
struct cam_data *cam = video_get_drvdata(dev);
@@ -3347,9 +3346,9 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&cam->busy_lock))
return -EINTR;
- //DBG("cpia_ioctl: %u\n", ioctlnr);
+ /* DBG("cpia_ioctl: %u\n", cmd); */
- switch (ioctlnr) {
+ switch (cmd) {
/* query capabilities */
case VIDIOCGCAP:
{
@@ -3724,7 +3723,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file,
static int cpia_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
+ return video_usercopy(file, cmd, arg, cpia_do_ioctl);
}
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
index 7e791b6923f9..1cc0df8befff 100644
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -25,7 +25,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
****************************************************************************/
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
index 73511a542077..dc5b07a20f69 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -25,7 +25,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
#include <linux/kernel.h>
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 1c6bd633f193..3c2d7eac1197 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -26,7 +26,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@redhat.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
****************************************************************************/
#include <linux/version.h>
@@ -1572,8 +1572,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file)
* cpia2_ioctl
*
*****************************************************************************/
-static int cpia2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctl_nr, void *arg)
+static int cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct camera_data *cam = video_drvdata(file);
int retval = 0;
@@ -1591,7 +1590,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
}
/* Priority check */
- switch (ioctl_nr) {
+ switch (cmd) {
case VIDIOCSWIN:
case VIDIOCMCAPTURE:
case VIDIOC_S_FMT:
@@ -1618,7 +1617,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
break;
}
- switch (ioctl_nr) {
+ switch (cmd) {
case VIDIOCGCAP: /* query capabilities */
retval = ioctl_cap_query(arg, cam);
break;
@@ -1683,7 +1682,7 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_ENUMINPUT:
case VIDIOC_G_INPUT:
case VIDIOC_S_INPUT:
- retval = ioctl_input(ioctl_nr, arg,cam);
+ retval = ioctl_input(cmd, arg, cam);
break;
case VIDIOC_ENUM_FMT:
@@ -1843,9 +1842,9 @@ static int cpia2_do_ioctl(struct inode *inode, struct file *file,
}
static int cpia2_ioctl(struct inode *inode, struct file *file,
- unsigned int ioctl_nr, unsigned long iarg)
+ unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, ioctl_nr, iarg, cpia2_do_ioctl);
+ return video_usercopy(file, cmd, arg, cpia2_do_ioctl);
}
/******************************************************************************
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
index a662b15d5b90..70fcd0d5de13 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/video/cs5345.c
@@ -23,9 +23,9 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-i2c-drv.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC");
MODULE_AUTHOR("Hans Verkuil");
@@ -40,111 +40,143 @@ MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
/* ----------------------------------------------------------------------- */
-static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
-static inline int cs5345_read(struct i2c_client *client, u8 reg)
+static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = cs5345_read(client, 0x09) & 7;
- route->input |= cs5345_read(client, 0x05) & 0x70;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if ((route->input & 0xf) > 6) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs5345_write(client, 0x09, route->input & 0xf);
- cs5345_write(client, 0x05, route->input & 0xf0);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = cs5345_read(client, 0x07) & 0x3f;
- if (ctrl->value >= 32)
- ctrl->value = ctrl->value - 64;
- break;
-
- case VIDIOC_S_CTRL:
- break;
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 24 || ctrl->value < -24)
- return -EINVAL;
- cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f);
- cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f);
- break;
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cs5345_read(client, reg->reg & 0x1f);
- else
- cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff);
- break;
+ if ((route->input & 0xf) > 6) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
+ return -EINVAL;
}
-#endif
+ cs5345_write(sd, 0x09, route->input & 0xf);
+ cs5345_write(sd, 0x05, route->input & 0xf0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS5345, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs5345_read(client, 0x09) & 7;
- u8 m = cs5345_read(client, 0x04);
- int vol = cs5345_read(client, 0x08) & 0x3f;
-
- v4l_info(client, "Input: %d%s\n", v,
- (m & 0x80) ? " (muted)" : "");
- if (vol >= 32)
- vol = vol - 64;
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
return -EINVAL;
+ ctrl->value = cs5345_read(sd, 0x07) & 0x3f;
+ if (ctrl->value >= 32)
+ ctrl->value = ctrl->value - 64;
+ return 0;
+}
+
+static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0);
+ return 0;
}
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 24 || ctrl->value < -24)
+ return -EINVAL;
+ cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f);
+ cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cs5345_read(sd, reg->reg & 0x1f);
+ return 0;
+}
+
+static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff);
return 0;
}
+#endif
+
+static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0);
+}
+
+static int cs5345_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs5345_read(sd, 0x09) & 7;
+ u8 m = cs5345_read(sd, 0x04);
+ int vol = cs5345_read(sd, 0x08) & 0x3f;
+
+ v4l2_info(sd, "Input: %d%s\n", v,
+ (m & 0x80) ? " (muted)" : "");
+ if (vol >= 32)
+ vol = vol - 64;
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs5345_core_ops = {
+ .log_status = cs5345_log_status,
+ .g_chip_ident = cs5345_g_chip_ident,
+ .g_ctrl = cs5345_g_ctrl,
+ .s_ctrl = cs5345_s_ctrl,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cs5345_g_register,
+ .s_register = cs5345_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops cs5345_audio_ops = {
+ .s_routing = cs5345_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs5345_ops = {
+ .core = &cs5345_core_ops,
+ .audio = &cs5345_audio_ops,
+};
/* ----------------------------------------------------------------------- */
static int cs5345_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
@@ -152,9 +184,25 @@ static int cs5345_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- cs5345_write(client, 0x02, 0x00);
- cs5345_write(client, 0x04, 0x01);
- cs5345_write(client, 0x09, 0x01);
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &cs5345_ops);
+
+ cs5345_write(sd, 0x02, 0x00);
+ cs5345_write(sd, 0x04, 0x01);
+ cs5345_write(sd, 0x09, 0x01);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs5345_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -171,5 +219,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_CS5345,
.command = cs5345_command,
.probe = cs5345_probe,
+ .remove = cs5345_remove,
.id_table = cs5345_id,
};
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index c4444500b330..cb65d519cf78 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -27,7 +27,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -47,84 +47,104 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
-static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value)
+static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
-static int cs53l32a_read(struct i2c_client *client, u8 reg)
+static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
-static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = (cs53l32a_read(client, 0x01) >> 4) & 3;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 2 physical inputs, but the second input can be
- placed in two modes, the first mode bypasses the PGA (gain),
- the second goes through the PGA. Hence there are three
- possible inputs to choose from. */
- if (route->input > 2) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs53l32a_write(client, 0x01, 0x01 + (route->input << 4));
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = (s8)cs53l32a_read(client, 0x04);
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 12 || ctrl->value < -96)
- return -EINVAL;
- cs53l32a_write(client, 0x04, (u8) ctrl->value);
- cs53l32a_write(client, 0x05, (u8) ctrl->value);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS53l32A, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs53l32a_read(client, 0x01);
- u8 m = cs53l32a_read(client, 0x03);
- s8 vol = cs53l32a_read(client, 0x04);
-
- v4l_info(client, "Input: %d%s\n", (v >> 4) & 3,
- (m & 0xC0) ? " (muted)" : "");
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+ /* There are 2 physical inputs, but the second input can be
+ placed in two modes, the first mode bypasses the PGA (gain),
+ the second goes through the PGA. Hence there are three
+ possible inputs to choose from. */
+ if (route->input > 2) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4));
+ return 0;
+}
+
+static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ ctrl->value = (s8)cs53l32a_read(sd, 0x04);
+ return 0;
+}
+
+static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 12 || ctrl->value < -96)
+ return -EINVAL;
+ cs53l32a_write(sd, 0x04, (u8) ctrl->value);
+ cs53l32a_write(sd, 0x05, (u8) ctrl->value);
return 0;
}
+static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client,
+ chip, V4L2_IDENT_CS53l32A, 0);
+}
+
+static int cs53l32a_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs53l32a_read(sd, 0x01);
+ u8 m = cs53l32a_read(sd, 0x03);
+ s8 vol = cs53l32a_read(sd, 0x04);
+
+ v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
+ (m & 0xC0) ? " (muted)" : "");
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ return 0;
+}
+
+static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
+ .log_status = cs53l32a_log_status,
+ .g_chip_ident = cs53l32a_g_chip_ident,
+ .g_ctrl = cs53l32a_g_ctrl,
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
+ .s_routing = cs53l32a_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs53l32a_ops = {
+ .core = &cs53l32a_core_ops,
+ .audio = &cs53l32a_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -137,6 +157,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int i;
/* Check if the adapter supports the needed features */
@@ -149,32 +170,46 @@ static int cs53l32a_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
+
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
- cs53l32a_write(client, 0x01, (u8) 0x21);
- cs53l32a_write(client, 0x02, (u8) 0x29);
- cs53l32a_write(client, 0x03, (u8) 0x30);
- cs53l32a_write(client, 0x04, (u8) 0x00);
- cs53l32a_write(client, 0x05, (u8) 0x00);
- cs53l32a_write(client, 0x06, (u8) 0x00);
- cs53l32a_write(client, 0x07, (u8) 0x00);
+ cs53l32a_write(sd, 0x01, (u8) 0x21);
+ cs53l32a_write(sd, 0x02, (u8) 0x29);
+ cs53l32a_write(sd, 0x03, (u8) 0x30);
+ cs53l32a_write(sd, 0x04, (u8) 0x00);
+ cs53l32a_write(sd, 0x05, (u8) 0x00);
+ cs53l32a_write(sd, 0x06, (u8) 0x00);
+ cs53l32a_write(sd, 0x07, (u8) 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
return 0;
}
+static int cs53l32a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
static const struct i2c_device_id cs53l32a_id[] = {
{ "cs53l32a", 0 },
{ }
@@ -185,6 +220,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "cs53l32a",
.driverid = I2C_DRIVERID_CS53L32A,
.command = cs53l32a_command,
+ .remove = cs53l32a_remove,
.probe = cs53l32a_probe,
.id_table = cs53l32a_id,
};
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index ef48565de7f1..8940b5387dec 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -9,7 +9,7 @@ config VIDEO_CX18
select VIDEO_CX2341X
select VIDEO_CS5345
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
---help---
This is a video4linux driver for Conexant cx23418 based
PCI combo video recorder devices.
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
index 0b55837880a7..a2f0ad570434 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -4,6 +4,7 @@
* Derived from cx25840-audio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,98 +31,165 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx18_av_write(cx, 0x127, 0x50);
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1408040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src3/4/6_ctl */
- /* 0x1.f77f = (4 * 15734.26) / 32000 */
+ /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */
cx18_av_write4(cx, 0x900, 0x0801f77f);
cx18_av_write4(cx, 0x904, 0x0801f77f);
cx18_av_write4(cx, 0x90c, 0x0801f77f);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
/* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11202fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1009040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18
+ */
+ cx18_av_write4(cx, 0x108, 0x180e040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e7 * 28,636,363.63 / 0x10 = 44100 * 384 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src3/4/6_ctl */
- /* 0x1.6d59 = (4 * 15734.26) / 44100 */
+ /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */
cx18_av_write4(cx, 0x900, 0x08016d59);
cx18_av_write4(cx, 0x904, 0x08016d59);
cx18_av_write4(cx, 0x90c, 0x08016d59);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */
+ cx18_av_write(cx, 0x127, 0x58);
+
/* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x112092ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x100a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16
+ */
+ cx18_av_write4(cx, 0x108, 0x160e040f);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x10 = 48000 * 384 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x005227ad);
/* src3/4/6_ctl */
- /* 0x1.4faa = (4 * 15734.26) / 48000 */
+ /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */
cx18_av_write4(cx, 0x900, 0x08014faa);
cx18_av_write4(cx, 0x904, 0x08014faa);
cx18_av_write4(cx, 0x90c, 0x08014faa);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, 0x127, 0x56);
+
/* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */
cx18_av_write4(cx, 0x12c, 0x11205fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
} else {
switch (freq) {
case 32000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1e08040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30
+ */
+ cx18_av_write4(cx, 0x108, 0x300d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0x8.9504318 * 28,636,363.63 / 0x1e = 32000 * 256 */
- cx18_av_write4(cx, 0x110, 0x012a0863);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.0000 = 32000/32000 */
@@ -133,27 +201,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08020000);
cx18_av_write4(cx, 0x90c, 0x08020000);
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx18_av_write(cx, 0x127, 0x54);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */
+ cx18_av_write(cx, 0x127, 0x70);
/* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11201fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x0d2ef8 = 107999.000 * 8 =
* ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa10d2ef8);
+ cx18_av_write4(cx, 0x128, 0xa00d2ef8);
break;
case 44100:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x1809040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24
+ */
+ cx18_av_write4(cx, 0x108, 0x240e040f);
- /* AUX_PLL_FRAC */
- /* 0x9.7635e74 * 28,636,363.63 / 0x18 = 44100 * 256 */
- cx18_av_write4(cx, 0x110, 0x00ec6bce);
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
+
+ /* AUX_PLL Fraction = 0x062a1f2 */
+ /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0062a1f2);
/* src1_ctl */
/* 0x1.60cd = 44100/32000 */
@@ -165,24 +240,34 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08017385);
cx18_av_write4(cx, 0x90c, 0x08017385);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */
+ cx18_av_write(cx, 0x127, 0x64);
+
/* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x112061ff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1d4bf8 = 239999.000 * 8 =
* ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11d4bf8);
+ cx18_av_write4(cx, 0x128, 0xa01d4bf8);
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx18_av_write4(cx, 0x108, 0x180a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20
+ */
+ cx18_av_write4(cx, 0x108, 0x200d040f);
+
+ /* VID_PLL Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/
+ cx18_av_write4(cx, 0x10c, 0x002be2fe);
- /* AUX_PLL_FRAC */
- /* 0xa.4c6b6ea * 28,636,363.63 / 0x18 = 48000 * 256 */
- cx18_av_write4(cx, 0x110, 0x0098d6dd);
+ /* AUX_PLL Fraction = 0x176740c */
+ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/
+ cx18_av_write4(cx, 0x110, 0x0176740c);
/* src1_ctl */
/* 0x1.8000 = 48000/32000 */
@@ -194,15 +279,18 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
cx18_av_write4(cx, 0x904, 0x08015555);
cx18_av_write4(cx, 0x90c, 0x08015555);
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */
+ cx18_av_write(cx, 0x127, 0x60);
+
/* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */
cx18_av_write4(cx, 0x12c, 0x11203fff);
/*
- * EN_AV_LOCK = 1
+ * EN_AV_LOCK = 0
* VID_COUNT = 0x1193f8 = 143999.000 * 8 =
* ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8
*/
- cx18_av_write4(cx, 0x128, 0xa11193f8);
+ cx18_av_write4(cx, 0x128, 0xa01193f8);
break;
}
}
@@ -215,12 +303,15 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq)
void cx18_av_audio_set_path(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
/* stop microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
/* assert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
+ v = cx18_av_read(cx, 0x810) | 0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
/* Mute everything to prevent the PFFT! */
cx18_av_write(cx, 0x8d3, 0x1f);
@@ -240,12 +331,14 @@ void cx18_av_audio_set_path(struct cx18 *cx)
set_audclk_freq(cx, state->audclk_freq);
/* deassert soft reset */
- cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
+ v = cx18_av_read(cx, 0x810) & ~0x01;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* When the microcontroller detects the
* audio format, it will unmute the lines */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -347,19 +440,23 @@ static int get_mute(struct cx18 *cx)
static void set_mute(struct cx18 *cx, int mute)
{
struct cx18_av_state *state = &cx->av_state;
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
/* Must turn off microcontroller in order to mute sound.
* Not sure if this is the best method, but it does work.
* If the microcontroller is running, then it will undo any
* changes to the mute register. */
+ v = cx18_av_read(cx, 0x803);
if (mute) {
/* disable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
} else {
/* enable microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
} else {
/* SRC1_MUTE_EN */
@@ -375,16 +472,26 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
switch (cmd) {
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ {
+ u8 v;
if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
- cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ v = cx18_av_read(cx, 0x803) & ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
cx18_av_write(cx, 0x8d3, 0x1f);
}
- cx18_av_and_or(cx, 0x810, ~0x1, 1);
+ v = cx18_av_read(cx, 0x810) | 0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
retval = set_audclk_freq(cx, *(u32 *)arg);
- cx18_av_and_or(cx, 0x810, ~0x1, 0);
- if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+
+ v = cx18_av_read(cx, 0x810) & ~0x1;
+ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+ v = cx18_av_read(cx, 0x803) | 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
return retval;
+ }
case VIDIOC_G_CTRL:
switch (ctrl->id) {
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 73f5141a42d1..0b1c84b4ddd6 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -4,6 +4,7 @@
* Derived from cx25840-core.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -36,12 +37,31 @@ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
return 0;
}
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask)
+{
+ u32 reg = 0xc40000 + (addr & ~3);
+ int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
+
+ x = (x & ~((u32)0xff << shift)) | ((u32)value << shift);
+ cx18_write_reg_expect(cx, x, reg,
+ ((u32)eval << shift), ((u32)mask << shift));
+ return 0;
+}
+
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg(cx, value, 0xc40000 + addr);
return 0;
}
+int
+cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask)
+{
+ cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask);
+ return 0;
+}
+
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
{
cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
@@ -61,11 +81,6 @@ u32 cx18_av_read4(struct cx18 *cx, u16 addr)
return cx18_read_reg(cx, 0xc40000 + addr);
}
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr)
-{
- return cx18_read_reg_noretry(cx, 0xc40000 + addr);
-}
-
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
u8 or_value)
{
@@ -98,14 +113,16 @@ static void cx18_av_initialize(struct cx18 *cx)
cx18_av_loadfw(cx);
/* Stop 8051 code execution */
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000,
+ 0x03000000, 0x13000000);
/* initallize the PLL by toggling sleep bit */
v = cx18_av_read4(cx, CXADEC_HOST_REG1);
- /* enable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
+ /* enable sleep mode - register appears to be read only... */
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe);
/* disable sleep mode */
- cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
+ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe,
+ v & 0xfffe, 0xffff);
/* initialize DLLs */
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
@@ -125,9 +142,10 @@ static void cx18_av_initialize(struct cx18 *cx)
v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
/* enable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F);
/* disable TUNE_FIL_RST */
- cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
+ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3,
+ v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F);
/* enable 656 output */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
@@ -251,10 +269,9 @@ void cx18_av_std_setup(struct cx18 *cx)
pll_int, pll_frac, pll_post);
if (pll_post) {
- int fin, fsc;
- int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+ int fin, fsc, pll;
- pll >>= 25;
+ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
pll /= pll_post;
CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
pll / 1000000, pll % 1000000);
@@ -324,6 +341,7 @@ static void input_change(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
v4l2_std_id std = state->std;
+ u8 v;
/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
@@ -333,31 +351,34 @@ static void input_change(struct cx18 *cx)
if (std & V4L2_STD_525_60) {
if (std == V4L2_STD_NTSC_M_JP) {
/* Japan uses EIAJ audio standard */
- cx18_av_write(cx, 0x808, 0xf7);
- cx18_av_write(cx, 0x80b, 0x02);
+ cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f);
} else if (std == V4L2_STD_NTSC_M_KR) {
/* South Korea uses A2 audio standard */
- cx18_av_write(cx, 0x808, 0xf8);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else {
/* Others use the BTSC audio standard */
- cx18_av_write(cx, 0x808, 0xf6);
- cx18_av_write(cx, 0x80b, 0x01);
+ cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f);
}
} else if (std & V4L2_STD_PAL) {
/* Follow tuner change procedure for PAL */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
} else if (std & V4L2_STD_SECAM) {
/* Select autodetect for SECAM */
- cx18_av_write(cx, 0x808, 0xff);
- cx18_av_write(cx, 0x80b, 0x03);
+ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff);
+ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f);
}
- if (cx18_av_read(cx, 0x803) & 0x10) {
+ v = cx18_av_read(cx, 0x803);
+ if (v & 0x10) {
/* restart audio decoder microcontroller */
- cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
- cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ v &= ~0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ v |= 0x10;
+ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
}
}
@@ -368,6 +389,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
vid_input <= CX18_AV_COMPOSITE8);
u8 reg;
+ u8 v;
CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
vid_input, aud_input);
@@ -413,16 +435,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
return -EINVAL;
}
- cx18_av_write(cx, 0x103, reg);
+ cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7);
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
+
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ v = cx18_av_read(cx, 0x102);
+ if (reg & 0x80)
+ v &= ~0x2;
+ else
+ v |= 0x2;
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx18_av_and_or(cx, 0x102, ~0x4, 4);
+ v |= 0x4;
else
- cx18_av_and_or(cx, 0x102, ~0x4, 0);
+ v &= ~0x4;
+ cx18_av_write_expect(cx, 0x102, v, v, 0x17);
+
/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
state->vid_input = vid_input;
@@ -799,40 +828,47 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
}
case VIDIOC_S_TUNER:
+ {
+ u8 v;
+
if (state->radio)
break;
+ v = cx18_av_read(cx, 0x809);
+ v &= ~0xf;
+
switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
break;
case V4L2_TUNER_MODE_STEREO:
case V4L2_TUNER_MODE_LANG1:
/* mono -> mono
stereo -> stereo
bilingual -> lang1 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
+ v |= 0x4;
break;
case V4L2_TUNER_MODE_LANG1_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang1/lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
+ v |= 0x7;
break;
case V4L2_TUNER_MODE_LANG2:
/* mono -> mono
stereo -> stereo
bilingual -> lang2 */
- cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
+ v |= 0x1;
break;
default:
return -EINVAL;
}
+ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
state->audmode = vt->audmode;
break;
+ }
case VIDIOC_G_FMT:
return get_v4lfmt(cx, (struct v4l2_format *)arg);
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index b67d8df20cc6..cf68a6039091 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -4,6 +4,7 @@
* Derived from cx25840-core.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -302,9 +303,11 @@ struct cx18_av_state {
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask);
+int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval,
+ u32 mask);
u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
-u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
index 522a035b2e8f..c64fd0a05a97 100644
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -2,6 +2,7 @@
* cx18 ADEC firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -43,11 +44,13 @@ int cx18_av_loadfw(struct cx18 *cx)
/* The firmware load often has byte errors, so allow for several
retries, both at byte level and at the firmware load level. */
while (retries1 < 5) {
- cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
- cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6);
+ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
- /* Reset the Mako core (Register is undocumented.) */
- cx18_av_write4(cx, 0x8100, 0x00010000);
+ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+ cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
/* Put the 8051 in reset and enable firmware upload */
cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
@@ -61,13 +64,12 @@ int cx18_av_loadfw(struct cx18 *cx)
int retries2;
int unrec_err = 0;
- for (retries2 = 0; retries2 < CX18_MAX_MMIO_RETRIES;
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
retries2++) {
cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
dl_control);
udelay(10);
- value = cx18_av_read4_noretry(cx,
- CXADEC_DL_CTL);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
if (value == dl_control)
break;
/* Check if we can correct the byte by changing
@@ -78,9 +80,7 @@ int cx18_av_loadfw(struct cx18 *cx)
break;
}
}
- cx18_log_write_retries(cx, retries2,
- cx->reg_mem + 0xc40000 + CXADEC_DL_CTL);
- if (unrec_err || retries2 >= CX18_MAX_MMIO_RETRIES)
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
break;
}
if (i == size)
@@ -93,7 +93,8 @@ int cx18_av_loadfw(struct cx18 *cx)
return -EIO;
}
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x13000000 | fw->size, 0x13000000, 0x13000000);
/* Output to the 416 */
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
@@ -118,7 +119,8 @@ int cx18_av_loadfw(struct cx18 *cx)
passthrough */
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
@@ -136,7 +138,7 @@ int cx18_av_loadfw(struct cx18 *cx)
v |= 0xFF; /* Auto by default */
v |= 0x400; /* Stereo by default */
v |= 0x14000000;
- cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
release_firmware(fw);
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
index 02fdf57bb678..1527ea4f6b06 100644
--- a/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
u8 lcr[24];
fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
svbi = &fmt->fmt.sliced;
- if (svbi->service_set == 0) {
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* raw VBI */
memset(svbi, 0, sizeof(*svbi));
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 5efe01ebe9db..e274043657dd 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -50,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = {
static const struct cx18_card cx18_card_hvr1600_esmt = {
.type = CX18_CARD_HVR_1600_ESMT,
.name = "Hauppauge HVR-1600",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -96,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
static const struct cx18_card cx18_card_hvr1600_samsung = {
.type = CX18_CARD_HVR_1600_SAMSUNG,
.name = "Hauppauge HVR-1600 (Preproduction)",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -151,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = {
static const struct cx18_card cx18_card_h900 = {
.type = CX18_CARD_COMPRO_H900,
.name = "Compro VideoMate H900",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
@@ -248,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
static const struct cx18_card cx18_card_cnxt_raptor_pal = {
.type = CX18_CARD_CNXT_RAPTOR_PAL,
.name = "Conexant Raptor PAL/SECAM",
- .comment = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index 32155f6e6fe4..6fa7bcb42dde 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -4,6 +4,7 @@
* Derived from ivtv-cards.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -47,8 +48,9 @@
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
-/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+ V4L2_CAP_VBI_CAPTURE)
+/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
struct cx18_card_video_input {
u8 video_type; /* video input type */
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index f46c7e5ed747..17edf305d649 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -259,6 +259,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
return err;
}
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ struct cx18_api_func_private priv;
struct cx2341x_mpeg_params p = cx->params;
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
c, VIDIOC_S_EXT_CTRLS);
@@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.fmt.pix.height = cx->params.height;
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
}
- err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+ priv.cx = cx;
+ priv.s = &cx->streams[id->type];
+ err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
cx->params = p;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 7874d9790a51..f50cf2167adc 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -75,48 +75,76 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
-static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned cardtype_c = 1;
static unsigned tuner_c = 1;
static unsigned radio_c = 1;
-static unsigned mmio_ndelay_c = 1;
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";
/* Buffers */
-static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+/* VBI bufsize based on standards supported by card tuner for now */
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = -1;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
static int cx18_pci_latency = 1;
-int cx18_retry_mmio = 1;
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
int cx18_debug;
module_param_array(tuner, int, &tuner_c, 0644);
module_param_array(radio, bool, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
-module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
module_param_named(debug, cx18_debug, int, 0644);
-module_param_named(retry_mmio, cx18_retry_mmio, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
module_param(cx18_pci_latency, int, 0644);
module_param(cx18_first_minor, int, 0644);
-module_param(enc_mpg_buffers, int, 0644);
module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
module_param(enc_yuv_buffers, int, 0644);
module_param(enc_vbi_buffers, int, 0644);
module_param(enc_pcm_buffers, int, 0644);
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+/* VBI bufsize based on standards supported by card tuner for now */
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
"\t\t\tsee tuner.h for values");
MODULE_PARM_DESC(radio,
@@ -152,28 +180,62 @@ MODULE_PARM_DESC(cx18_pci_latency,
"Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
"\t\t\tDefault: Yes");
MODULE_PARM_DESC(retry_mmio,
- "Check and retry memory mapped IO accesses\n"
- "\t\t\tDefault: 1 [Yes]");
+ "(Deprecated) MMIO writes are now always checked and retried\n"
+ "\t\t\tEffectively: 1 [Yes]");
MODULE_PARM_DESC(mmio_ndelay,
- "Delay (ns) for each CX23418 memory mapped IO access.\n"
- "\t\t\tTry larger values that are close to a multiple of the\n"
- "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY));
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+ "(Deprecated) MMIO accesses are now never purposely delayed\n"
+ "\t\t\tEffectively: 0 ns");
MODULE_PARM_DESC(enc_ts_buffers,
- "Encoder TS Buffers (in MB)\n"
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS));
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE));
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault is computed from other enc_idx_* parameters");
MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV Buffers (in MB)\n"
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI Buffers (in MB)\n"
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffers (in MB)\n"
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
@@ -187,7 +249,7 @@ MODULE_VERSION(CX18_VERSION);
/* Generic utility functions */
int cx18_msleep_timeout(unsigned int msecs, int intr)
{
- int timeout = msecs_to_jiffies(msecs);
+ long int timeout = msecs_to_jiffies(msecs);
int sig;
do {
@@ -366,20 +428,69 @@ static void cx18_process_options(struct cx18 *cx)
{
int i, j;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+ /* Except for VBI ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ /* User said to use 0 buffers */
+ if (cx->stream_buffers[i] == 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* User said to use 0 MB total */
+ if (cx->options.megabytes[i] <= 0) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /* VBI is computed later or user said buffer has size 0 */
+ if (cx->stream_buf_size[i] <= 0) {
+ if (i != CX18_ENC_STREAM_TYPE_VBI) {
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ }
+ continue;
+ }
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024;
+ }
+ cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+ }
+
cx->options.cardtype = cardtype[cx->num];
cx->options.tuner = tuner[cx->num];
cx->options.radio = radio[cx->num];
- if (mmio_ndelay[cx->num] < 0)
- cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY;
- else
- cx->options.mmio_ndelay = mmio_ndelay[cx->num];
-
cx->std = cx18_parse_std(cx);
if (cx->options.cardtype == -1) {
CX18_INFO("Ignore card\n");
@@ -440,22 +551,30 @@ done:
*/
static int __devinit cx18_init_struct1(struct cx18 *cx)
{
+ int i;
+
cx->base_addr = pci_resource_start(cx->dev, 0);
mutex_init(&cx->serialize_lock);
mutex_init(&cx->i2c_bus_lock[0]);
mutex_init(&cx->i2c_bus_lock[1]);
mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
spin_lock_init(&cx->lock);
cx->work_queue = create_singlethread_workqueue(cx->name);
if (cx->work_queue == NULL) {
- CX18_ERR("Could not create work queue\n");
- return -1;
+ CX18_ERR("Unable to create work hander thread\n");
+ return -ENOMEM;
}
- INIT_WORK(&cx->work, cx18_work_handler);
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ cx->epu_work_order[i].cx = cx;
+ cx->epu_work_order[i].str = cx->epu_debug_str;
+ INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler);
+ }
/* start counting open_id at 1 */
cx->open_id = 1;
@@ -472,20 +591,55 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
init_waitqueue_head(&cx->cap_w);
init_waitqueue_head(&cx->mb_apu_waitq);
init_waitqueue_head(&cx->mb_cpu_waitq);
- init_waitqueue_head(&cx->mb_epu_waitq);
- init_waitqueue_head(&cx->mb_hpu_waitq);
init_waitqueue_head(&cx->dma_waitq);
/* VBI */
- cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
- cx->vbi.raw_size = 1456;
- cx->vbi.raw_decoder_line_size = 1456;
- cx->vbi.raw_decoder_sav_odd_field = 0x20;
- cx->vbi.raw_decoder_sav_even_field = 0x60;
- cx->vbi.sliced_decoder_line_size = 272;
- cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
- cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+
+ /*
+ * The VBI line sizes depend on the pixel clock and the horiz rate
+ *
+ * (1/Fh)*(2*Fp) = Samples/line
+ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+ *
+ * Sliced VBI is sent as ancillary data during horizontal blanking
+ * Raw VBI is sent as active video samples during vertcal blanking
+ *
+ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+ * length of 720 pixels @ 4:2:2 sampling. Thus...
+ *
+ * For systems that use a 15.734 kHz horizontal rate, such as
+ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+ *
+ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ * For systems that use a 15.625 kHz horizontal rate, such as
+ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+ *
+ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+ *
+ */
+
+ /* FIXME: init these based on tuner std & modify when std changes */
+ /* CX18-AV-Core number of VBI samples output per horizontal line */
+ cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */
+ cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */
+
+ /* CX18-AV-Core VBI samples/line possibly rounded up */
+ cx->vbi.raw_size = 1444; /* Real max size is 1444 */
+ cx->vbi.sliced_size = 284; /* Real max size is 284 */
+
+ /*
+ * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode
+ * Task Field VerticalBlank HorizontalBlank 0 0 0 0
+ */
+ cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */
+ cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */
+ cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */
+ cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */
return 0;
}
@@ -518,6 +672,7 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
cx->av_state.aud_input = CX18_AV_AUDIO8;
cx->av_state.audclk_freq = 48000;
cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+ /* FIXME - 8 is NTSC value, investigate */
cx->av_state.vbi_line_offset = 8;
}
@@ -662,12 +817,9 @@ static int __devinit cx18_probe(struct pci_dev *dev,
/* PCI Device Setup */
retval = cx18_setup_pci(cx, dev, pci_id);
- if (retval != 0) {
- if (retval == -EIO)
- goto free_workqueue;
- else if (retval == -ENXIO)
- goto free_mem;
- }
+ if (retval != 0)
+ goto free_workqueue;
+
/* save cx in the pci struct for later use */
pci_set_drvdata(dev, cx);
@@ -726,6 +878,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_i2c;
}
cx18_init_memory(cx);
+ cx18_init_scb(cx);
/* Register IRQ */
retval = request_irq(cx->dev->irq, cx18_irq_handler,
@@ -739,8 +892,6 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx->std = V4L2_STD_NTSC_M;
if (cx->options.tuner == -1) {
- int i;
-
for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
if ((cx->std & cx->card->tuners[i].std) == 0)
continue;
@@ -777,13 +928,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
}
cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+ /*
+ * FIXME: setting the buffer size based on the tuner standard is
+ * suboptimal, as the CVBS and SVideo inputs could use a different std
+ * and the buffer could end up being too small in that case.
+ */
vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0)
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024
+ / vbi_buf_size;
+ else
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] =
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size
+ / (1024 * 1024);
+
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
@@ -844,7 +1005,6 @@ err:
if (retval == 0)
retval = -ENODEV;
CX18_ERR("Error %d on initialization\n", retval);
- cx18_log_statistics(cx);
i = cx->num;
spin_lock(&cx18_cards_lock);
@@ -923,6 +1083,13 @@ int cx18_init_on_first_open(struct cx18 *cx)
return 0;
}
+static void cx18_cancel_epu_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++)
+ cancel_work_sync(&cx->epu_work_order[i].work);
+}
+
static void cx18_remove(struct pci_dev *pci_dev)
{
struct cx18 *cx = pci_get_drvdata(pci_dev);
@@ -940,7 +1107,8 @@ static void cx18_remove(struct pci_dev *pci_dev)
cx18_halt_firmware(cx);
- flush_workqueue(cx->work_queue);
+ cx18_cancel_epu_work_orders(cx);
+
destroy_workqueue(cx->work_queue);
cx18_streams_cleanup(cx, 1);
@@ -955,7 +1123,6 @@ static void cx18_remove(struct pci_dev *pci_dev)
pci_disable_device(cx->dev);
- cx18_log_statistics(cx);
CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
}
@@ -1004,6 +1171,7 @@ static void module_cleanup(void)
continue;
kfree(cx18_cards[i]);
}
+
}
module_init(module_start);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index bbdd5f25041d..0d2edebc39b4 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -4,6 +4,7 @@
* Derived from ivtv-driver.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -64,9 +65,6 @@
# error "This driver requires kernel PCI support."
#endif
-/* Default delay to throttle mmio access to the CX23418 */
-#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */
-
#define CX18_MEM_OFFSET 0x00000000
#define CX18_MEM_SIZE 0x04000000
#define CX18_REG_OFFSET 0x02000000
@@ -117,6 +115,17 @@
#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
+/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
+
/* i2c stuff */
#define I2C_CLIENTS_MAX 16
@@ -176,7 +185,6 @@
#define CX18_MAX_PGM_INDEX (400)
-extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */
extern int cx18_debug;
@@ -185,7 +193,6 @@ struct cx18_options {
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
- unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */
};
/* per-buffer bit flags */
@@ -203,11 +210,8 @@ struct cx18_options {
#define CX18_F_I_EOS 4 /* End of encoder stream */
#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */
#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
-#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */
-#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */
#define CX18_F_I_INITED 21 /* set after first open */
#define CX18_F_I_FAILED 22 /* set if first open failed */
-#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */
/* These are the VBI types as they appear in the embedded VBI private packets. */
#define CX18_SLICED_TYPE_TELETEXT_B (1)
@@ -220,6 +224,7 @@ struct cx18_buffer {
dma_addr_t dma_handle;
u32 id;
unsigned long b_flags;
+ unsigned skipped;
char *buf;
u32 bytesused;
@@ -248,6 +253,27 @@ struct cx18_dvb {
struct cx18; /* forward reference */
struct cx18_scb; /* forward reference */
+
+#define CX18_MAX_MDL_ACKS 2
+#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
+
+#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
+#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2
+#define CX18_F_EWO_MB_STALE \
+ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC)
+
+struct cx18_epu_work_order {
+ struct work_struct work;
+ atomic_t pending;
+ struct cx18 *cx;
+ unsigned long flags;
+ int rpu;
+ struct cx18_mailbox mb;
+ struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS];
+ char *str;
+};
+
#define CX18_INVALID_TASK_HANDLE 0xffffffff
struct cx18_stream {
@@ -261,7 +287,7 @@ struct cx18_stream {
unsigned mdl_offset;
u32 id;
- spinlock_t qlock; /* locks access to the queues */
+ struct mutex qlock; /* locks access to the queues */
unsigned long s_flags; /* status flags, see above */
int dma; /* can be PCI_DMA_TODEVICE,
PCI_DMA_FROMDEVICE or
@@ -275,8 +301,8 @@ struct cx18_stream {
/* Buffer Queues */
struct cx18_queue q_free; /* free buffers */
- struct cx18_queue q_full; /* full buffers */
- struct cx18_queue q_io; /* waiting for I/O */
+ struct cx18_queue q_busy; /* busy buffers - in use by firmware */
+ struct cx18_queue q_full; /* full buffers - data for user apps */
/* DVB / Digital Transport */
struct cx18_dvb dvb;
@@ -353,12 +379,7 @@ struct cx18_i2c_algo_callback_data {
int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
};
-#define CX18_MAX_MMIO_RETRIES 10
-
-struct cx18_mmio_stats {
- atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1];
- atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1];
-};
+#define CX18_MAX_MMIO_WR_RETRIES 10
/* Struct to hold info about cx18 cards */
struct cx18 {
@@ -378,7 +399,9 @@ struct cx18 {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* Hardware description of the board */
unsigned mdl_offset;
- struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct cx18_scb __iomem *scb; /* pointer to SCB */
+ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
+ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct cx18_av_state av_state;
@@ -397,6 +420,7 @@ struct cx18 {
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
struct cx18_options options; /* User options */
+ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
unsigned long i_flags; /* global cx18 flags */
@@ -428,14 +452,17 @@ struct cx18 {
wait_queue_head_t mb_apu_waitq;
wait_queue_head_t mb_cpu_waitq;
- wait_queue_head_t mb_epu_waitq;
- wait_queue_head_t mb_hpu_waitq;
wait_queue_head_t cap_w;
/* when the current DMA is finished this queue is woken up */
wait_queue_head_t dma_waitq;
+ u32 sw1_irq_mask;
+ u32 sw2_irq_mask;
+ u32 hw2_irq_mask;
+
struct workqueue_struct *work_queue;
- struct work_struct work;
+ struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS];
+ char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */
/* i2c */
struct i2c_adapter i2c_adap[2];
@@ -450,9 +477,6 @@ struct cx18 {
u32 gpio_val;
struct mutex gpio_lock;
- /* Statistics */
- struct cx18_mmio_stats mmio_stats;
-
/* v4l2 and User settings */
/* codec settings */
@@ -481,4 +505,10 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
/* First-open initialization: load firmware, etc. */
int cx18_init_on_first_open(struct cx18 *cx);
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 4542e2e5e3d7..bd5e6f3fd4d0 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -2,6 +2,7 @@
* cx18 functions for DVB support
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -23,8 +24,6 @@
#include "cx18-dvb.h"
#include "cx18-io.h"
#include "cx18-streams.h"
-#include "cx18-queue.h"
-#include "cx18-scb.h"
#include "cx18-cards.h"
#include "s5h1409.h"
#include "mxl5005s.h"
@@ -109,20 +108,23 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
if (!demux->dmx.frontend)
return -EINVAL;
- if (stream) {
- mutex_lock(&stream->dvb.feedlock);
- if (stream->dvb.feeding++ == 0) {
- CX18_DEBUG_INFO("Starting Transport DMA\n");
- ret = cx18_start_v4l2_encode_stream(stream);
- if (ret < 0) {
- CX18_DEBUG_INFO(
- "Failed to start Transport DMA\n");
- stream->dvb.feeding--;
- }
- } else
- ret = 0;
- mutex_unlock(&stream->dvb.feedlock);
- }
+ if (!stream)
+ return -EINVAL;
+
+ mutex_lock(&stream->dvb.feedlock);
+ if (stream->dvb.feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ ret = cx18_start_v4l2_encode_stream(stream);
+ if (ret < 0) {
+ CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+ stream->dvb.feeding--;
+ if (stream->dvb.feeding == 0)
+ clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ }
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb.feedlock);
return ret;
}
@@ -215,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+ stream->dvb.dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024);
+
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
return ret;
@@ -302,24 +308,3 @@ static int dvb_register(struct cx18_stream *stream)
return ret;
}
-
-void cx18_dvb_work_handler(struct cx18 *cx)
-{
- struct cx18_buffer *buf;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS];
-
- while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) {
- if (s->dvb.enabled)
- dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
- buf->bytesused);
-
- cx18_enqueue(s, buf, &s->q_free);
- cx18_buf_sync_for_device(s, buf);
- if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */
- continue;
-
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
- }
-}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
index bbdcefc87f28..bf8d8f6f5455 100644
--- a/drivers/media/video/cx18/cx18-dvb.h
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -23,4 +23,3 @@
int cx18_dvb_register(struct cx18_stream *stream);
void cx18_dvb_unregister(struct cx18_stream *stream);
-void cx18_dvb_work_handler(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 5f9089907544..425271a29517 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -4,6 +4,7 @@
* Derived from ivtv-fileops.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -66,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
}
s->id = id->open_id;
- /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
- CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+ /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
(provided VBI insertion is on and sliced VBI is selected), for all
other streams we're done */
if (type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+ cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) {
vbi_type = CX18_ENC_STREAM_TYPE_VBI;
} else {
return 0;
@@ -185,8 +185,10 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
/* byteswap and process VBI data */
-/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
- cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
+ cx18_process_vbi_data(cx, buf,
+ s_vbi->dma_pts,
+ s_vbi->type);
+ cx18_stream_put_buf_fw(s_vbi, buf);
}
}
buf = &cx->vbi.sliced_mpeg_buf;
@@ -194,11 +196,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
return buf;
}
- /* do we have leftover data? */
- buf = cx18_dequeue(s, &s->q_io);
- if (buf)
- return buf;
-
/* do we have new data? */
buf = cx18_dequeue(s, &s->q_full);
if (buf) {
@@ -262,7 +259,7 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
if (len > ucount)
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -337,8 +334,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
frames should arrive one-by-one, so make sure we never output more
than one VBI frame at a time */
- if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set)
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
single_frame = 1;
for (;;) {
@@ -365,16 +361,10 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
tot_count - tot_written);
if (buf != &cx->vbi.sliced_mpeg_buf) {
- if (buf->readpos == buf->bytesused) {
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
- s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] -
- cx->enc_mem,
- 1, buf->id, s->buf_size);
- } else
- cx18_enqueue(s, buf, &s->q_io);
+ if (buf->readpos == buf->bytesused)
+ cx18_stream_put_buf_fw(s, buf);
+ else
+ cx18_push(s, buf, &s->q_full);
} else if (buf->readpos == buf->bytesused) {
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
@@ -518,7 +508,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers))
+ if (atomic_read(&s->q_full.buffers))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 51534428cd00..1fa95da1575e 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -2,6 +2,7 @@
* cx18 firmware functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -25,6 +26,7 @@
#include "cx18-irq.h"
#include "cx18-firmware.h"
#include "cx18-cards.h"
+#include "cx18-av-core.h"
#include <linux/firmware.h>
#define CX18_PROC_SOFT_RESET 0xc70010
@@ -121,6 +123,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
if (cx18_raw_readl(cx, dst) != *src) {
CX18_ERR("Mismatch at offset %x\n", i);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
dst++;
@@ -131,10 +134,12 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
size = fw->size;
release_firmware(fw);
+ cx18_setup_page(cx, SCB_OFFSET);
return size;
}
-static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 *entry_addr)
{
const struct firmware *fw = NULL;
int i, j;
@@ -149,9 +154,11 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
if (request_firmware(&fw, fn, &cx->dev->dev)) {
CX18_ERR("unable to open firmware %s\n", fn);
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
return -ENOMEM;
}
+ *entry_addr = 0;
src = (const u32 *)fw->data;
vers = fw->data + sizeof(seghdr);
sz = fw->size;
@@ -168,10 +175,12 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
}
CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
seghdr.addr + seghdr.size - 1);
+ if (*entry_addr == 0)
+ *entry_addr = seghdr.addr;
if (offset + seghdr.size > sz)
break;
for (i = 0; i < seghdr.size; i += 4096) {
- cx18_setup_page(cx, offset + i);
+ cx18_setup_page(cx, seghdr.addr + i);
for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
/* no need for endianness conversion on the ppc */
cx18_raw_writel(cx, src[(offset + j) / 4],
@@ -181,6 +190,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
CX18_ERR("Mismatch at offset %x\n",
offset + j);
release_firmware(fw);
+ cx18_setup_page(cx, 0);
return -EIO;
}
}
@@ -192,16 +202,17 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx)
fn, apu_version, fw->size);
size = fw->size;
release_firmware(fw);
- /* Clear bit0 for APU to start from 0 */
- cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
+ cx18_setup_page(cx, 0);
return size;
}
void cx18_halt_firmware(struct cx18 *cx)
{
CX18_DEBUG_INFO("Preparing for firmware halt.\n");
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
- cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL);
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+ 0x00000002, 0x00020002);
}
void cx18_init_power(struct cx18 *cx, int lowpwr)
@@ -211,9 +222,48 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
/* ADEC out of sleep */
- cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL);
-
- /* The fast clock is at 200/245 MHz */
+ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+ 0x00000000, 0x00020002);
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ /* the fast clock is at 200/245 MHz */
+ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
CX18_FAST_CLOCK_PLL_FRAC);
@@ -223,16 +273,36 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
/* set slow clock to 125/120 MHz */
- cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
- cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8,
+ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
CX18_SLOW_CLOCK_PLL_FRAC);
- cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
/* mpeg clock pll 54MHz */
+ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
- cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+ /*
+ * VDCLK Integer = 0x0f, Post Divider = 0x04
+ * AIMCLK Integer = 0x0e, Post Divider = 0x16
+ */
+ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+
+ /* VDCLK Fraction = 0x2be2fe */
+ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+
+ /* AIMCLK Fraction = 0x05227ad */
+ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
+ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+
/* Defaults */
/* APU = SC or SC/2 = 125/62.5 */
/* EPU = SC = 125 */
@@ -248,22 +318,34 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
/* VFC = disabled */
/* USB = disabled */
- cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004,
- CX18_CLOCK_SELECT1);
- cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006,
- CX18_CLOCK_SELECT2);
-
- cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
- cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+ if (lowpwr) {
+ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+ 0x00000020, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+ 0x00000004, 0xFFFFFFFF);
+ } else {
+ /* This doesn't explicitly set every clock select */
+ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+ 0x00000004, 0x00060006);
+ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+ 0x00000006, 0x00060006);
+ }
- cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1);
- cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2);
+ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+ 0x00000002, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+ 0x00000104, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+ 0x00009026, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+ 0x00003105, 0xFFFFFFFF);
}
void cx18_init_memory(struct cx18 *cx)
{
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00010001);
cx18_msleep_timeout(10, 0);
cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
@@ -282,13 +364,15 @@ void cx18_init_memory(struct cx18 *cx)
cx18_msleep_timeout(10, 0);
- cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00020002);
cx18_msleep_timeout(10, 0);
/* use power-down mode when idle */
cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
- cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN);
+ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+ 0x00000001, 0x00010001);
cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
@@ -307,51 +391,76 @@ void cx18_init_memory(struct cx18 *cx)
int cx18_firmware_init(struct cx18 *cx)
{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
/* Allow chip to control CLKRUN */
cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
- cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ /* Stop the firmware */
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
cx18_msleep_timeout(1, 0);
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
- /* Only if the processor is not running */
- if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
- int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
- cx->enc_mem, cx);
-
- cx18_write_enc(cx, 0xE51FF004, 0);
- cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */
- /* Start APU */
- cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET);
- cx18_msleep_timeout(500, 0);
-
- sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
- cx->enc_mem, cx);
-
- if (sz > 0) {
- int retries = 0;
-
- /* start the CPU */
- cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET);
- while (retries++ < 50) { /* Loop for max 500mS */
- if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
- & 1) == 0)
- break;
- cx18_msleep_timeout(10, 0);
- }
- cx18_msleep_timeout(200, 0);
- if (retries == 51) {
- CX18_ERR("Could not start the CPU\n");
- return -EIO;
- }
- }
- if (sz <= 0)
- return -EIO;
+ sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
}
+
+ /*
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
+ */
+ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
/* initialize GPIO */
- cx18_write_reg(cx, 0x14001400, 0xC78110);
+ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 0e560421989e..1a99329f33cb 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -47,15 +48,19 @@
static void gpio_write(struct cx18 *cx)
{
- u32 dir = cx->gpio_dir;
- u32 val = cx->gpio_val;
-
- cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
- cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff),
- CX18_REG_GPIO_OUT1);
- cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2);
- cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
- CX18_REG_GPIO_OUT2);
+ u32 dir_lo = cx->gpio_dir & 0xffff;
+ u32 val_lo = cx->gpio_val & 0xffff;
+ u32 dir_hi = cx->gpio_dir >> 16;
+ u32 val_hi = cx->gpio_val >> 16;
+
+ cx18_write_reg_expect(cx, dir_lo << 16,
+ CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+ cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+ CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+ cx18_write_reg_expect(cx, dir_hi << 16,
+ CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+ cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+ CX18_REG_GPIO_OUT2, val_hi, dir_hi);
}
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index beb7424b9944..39ffccc19d8a 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -4,6 +4,7 @@
* Derived from ivtv-gpio.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index aa09e557b195..8941f58bed7f 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -4,6 +4,7 @@
* Derived from ivtv-i2c.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -27,6 +28,7 @@
#include "cx18-gpio.h"
#include "cx18-av-core.h"
#include "cx18-i2c.h"
+#include "cx18-irq.h"
#define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008
@@ -160,9 +162,9 @@ static void cx18_setscl(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSCL_BIT, addr);
+ cx18_write_reg(cx, r | SETSCL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSCL_BIT, addr);
}
static void cx18_setsda(void *data, int state)
@@ -173,9 +175,9 @@ static void cx18_setsda(void *data, int state)
u32 r = cx18_read_reg(cx, addr);
if (state)
- cx18_write_reg_sync(cx, r | SETSDL_BIT, addr);
+ cx18_write_reg(cx, r | SETSDL_BIT, addr);
else
- cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr);
+ cx18_write_reg(cx, r & ~SETSDL_BIT, addr);
}
static int cx18_getscl(void *data)
@@ -396,30 +398,33 @@ int init_cx18_i2c(struct cx18 *cx)
if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
/* Reset/Unreset I2C hardware block */
/* Clock select 220MHz */
- cx18_write_reg(cx, 0x10000000, 0xc71004);
+ cx18_write_reg_expect(cx, 0x10000000, 0xc71004,
+ 0x00000000, 0x10001000);
/* Clock Enable */
- cx18_write_reg_sync(cx, 0x10001000, 0xc71024);
+ cx18_write_reg_expect(cx, 0x10001000, 0xc71024,
+ 0x00001000, 0x10001000);
}
/* courtesy of Steven Toth <stoth@hauppauge.com> */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0);
mdelay(10);
- cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
+ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0);
mdelay(10);
/* Set to edge-triggered intrs. */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8);
+ cx18_write_reg(cx, 0x00c00000, 0xc730c8);
/* Clear any stale intrs */
- cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4);
+ cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS,
+ ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT);
/* Hw I2C1 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
/* Hw I2C2 Clock Freq ~100kHz */
- cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
index 220fae8d4ad7..ec5b3d7bcc6b 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -24,179 +24,6 @@
#include "cx18-io.h"
#include "cx18-irq.h"
-void cx18_log_statistics(struct cx18 *cx)
-{
- int i;
-
- if (!(cx18_debug & CX18_DBGFLG_INFO))
- return;
-
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_write[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_write[i]));
- for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
- CX18_DEBUG_INFO("retried_read[%d] = %d\n", i,
- atomic_read(&cx->mmio_stats.retried_read[i]));
- return;
-}
-
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_raw_writel_noretry(cx, val, addr);
- if (val == cx18_raw_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_raw_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (val == cx18_readl_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask)
-{
- int i;
- eval &= mask;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writel_noretry(cx, val, addr);
- if (eval == (cx18_readl_noretry(cx, addr) & mask))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writew_noretry(cx, val, addr);
- if (val == cx18_readw_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr)
-{
- int i;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- cx18_writeb_noretry(cx, val, addr);
- if (val == cx18_readb_noretry(cx, addr))
- break;
- }
- cx18_log_write_retries(cx, i, addr);
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u32 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readl_noretry(cx, addr);
- if (val != 0xffffffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u16 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readw_noretry(cx, addr);
- if (val != 0xffff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
-{
- int i;
- u8 val;
- for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
- val = cx18_readb_noretry(cx, addr);
- if (val != 0xff) /* PCI bus read error */
- break;
- }
- cx18_log_read_retries(cx, i, addr);
- return val;
-}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len)
-{
- const u8 __iomem *src = from;
- u8 *dst = to;
-
- /* Align reads on the CX23418's addresses */
- if ((len > 0) && ((unsigned long) src & 1)) {
- *dst = cx18_readb(cx, src);
- len--;
- dst++;
- src++;
- }
- if ((len > 1) && ((unsigned long) src & 2)) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- while (len > 3) {
- *((u32 *)dst) = cx18_raw_readl(cx, src);
- len -= 4;
- dst += 4;
- src += 4;
- }
- if (len > 1) {
- *((u16 *)dst) = cx18_raw_readw(cx, src);
- len -= 2;
- dst += 2;
- src += 2;
- }
- if (len > 0)
- *dst = cx18_readb(cx, src);
-}
-
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
{
u8 __iomem *dst = addr;
@@ -230,32 +57,35 @@ void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
{
- u32 r;
- r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI);
+ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI);
}
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
{
- u32 r;
cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val);
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI);
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
}
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
{
+ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val;
+ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val)
+{
u32 r;
- r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI);
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU);
+ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU);
}
void cx18_setup_page(struct cx18 *cx, u32 addr)
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
index 425244453ea7..2635b3a8cc96 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -25,232 +25,125 @@
#include "cx18-driver.h"
-static inline void cx18_io_delay(struct cx18 *cx)
-{
- if (cx->options.mmio_ndelay)
- ndelay(cx->options.mmio_ndelay);
-}
-
/*
* Readback and retry of MMIO access for reliability:
* The concept was suggested by Steve Toth <stoth@linuxtv.org>.
* The implmentation is the fault of Andy Walls <awalls@radix.net>.
+ *
+ * *write* functions are implied to retry the mmio unless suffixed with _noretry
+ * *read* functions never retry the mmio (it never helps to do so)
*/
-/* Statistics gathering */
-static inline
-void cx18_log_write_retries(struct cx18 *cx, int i, const void __iomem *addr)
-{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_write[i]);
- return;
-}
-
-static inline
-void cx18_log_read_retries(struct cx18 *cx, int i, const void __iomem *addr)
+/* Non byteswapping memory mapped IO */
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (i > CX18_MAX_MMIO_RETRIES)
- i = CX18_MAX_MMIO_RETRIES;
- atomic_inc(&cx->mmio_stats.retried_read[i]);
- return;
+ return __raw_readl(addr);
}
-void cx18_log_statistics(struct cx18 *cx);
-
-/* Non byteswapping memory mapped IO */
static inline
void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
__raw_writel(val, addr);
- cx18_io_delay(cx);
}
-void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_raw_writel_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_raw_writel_noretry(cx, val, addr);
+ if (val == cx18_raw_readl(cx, addr))
+ break;
+ }
}
-
-static inline
-u32 cx18_raw_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = __raw_readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+/* Normal memory mapped IO */
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readl_retry(cx, addr);
-
- return cx18_raw_readl_noretry(cx, addr);
+ return readl(addr);
}
-
static inline
-u16 cx18_raw_readw_noretry(struct cx18 *cx, const void __iomem *addr)
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
{
- u16 ret = __raw_readw(addr);
- cx18_io_delay(cx);
- return ret;
+ writel(val, addr);
}
-u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr)
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- return cx18_raw_readw_retry(cx, addr);
-
- return cx18_raw_readw_noretry(cx, addr);
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ if (val == cx18_readl(cx, addr))
+ break;
+ }
}
-
-/* Normal memory mapped IO */
static inline
-void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
+ u32 eval, u32 mask)
{
- writel(val, addr);
- cx18_io_delay(cx);
+ int i;
+ u32 r;
+ eval &= mask;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ r = cx18_readl(cx, addr);
+ if (r == 0xffffffff && eval != 0xffffffff)
+ continue;
+ if (eval == (r & mask))
+ break;
+ }
}
-void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
-
-static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writel_retry(cx, val, addr);
- else
- cx18_writel_noretry(cx, val, addr);
+ return readw(addr);
}
-void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr,
- u32 eval, u32 mask);
-
static inline
void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
{
writew(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr);
-
static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writew_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writew_noretry(cx, val, addr);
+ if (val == cx18_readw(cx, addr))
+ break;
+ }
}
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+ return readb(addr);
+}
static inline
void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
{
writeb(val, addr);
- cx18_io_delay(cx);
}
-void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr);
-
static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
{
- if (cx18_retry_mmio)
- cx18_writeb_retry(cx, val, addr);
- else
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) {
cx18_writeb_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_readl_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u32 ret = readl(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readl_retry(cx, addr);
-
- return cx18_readl_noretry(cx, addr);
-}
-
-
-static inline u16 cx18_readw_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u16 ret = readw(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readw_retry(cx, addr);
-
- return cx18_readw_noretry(cx, addr);
-}
-
-
-static inline u8 cx18_readb_noretry(struct cx18 *cx, const void __iomem *addr)
-{
- u8 ret = readb(addr);
- cx18_io_delay(cx);
- return ret;
-}
-
-u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr);
-
-static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
-{
- if (cx18_retry_mmio)
- return cx18_readb_retry(cx, addr);
-
- return cx18_readb_noretry(cx, addr);
-}
-
-
-static inline
-u32 cx18_write_sync_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_noretry(cx, val, addr);
- return cx18_readl_noretry(cx, addr);
+ if (val == cx18_readb(cx, addr))
+ break;
+ }
}
static inline
-u32 cx18_write_sync_retry(struct cx18 *cx, u32 val, void __iomem *addr)
-{
- cx18_writel_retry(cx, val, addr);
- return cx18_readl_retry(cx, addr);
-}
-
-static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len)
{
- if (cx18_retry_mmio)
- return cx18_write_sync_retry(cx, val, addr);
-
- return cx18_write_sync_noretry(cx, val, addr);
+ memcpy_fromio(to, from, len);
}
-
-void cx18_memcpy_fromio(struct cx18 *cx, void *to,
- const void __iomem *from, unsigned int len);
void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
@@ -260,136 +153,39 @@ static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
cx18_writel_noretry(cx, val, cx->reg_mem + reg);
}
-static inline void cx18_write_reg_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- cx18_writel_retry(cx, val, cx->reg_mem + reg);
-}
-
static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
{
- if (cx18_retry_mmio)
- cx18_write_reg_retry(cx, val, reg);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-static inline void _cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
- u32 eval, u32 mask)
-{
- _cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
+ cx18_writel(cx, val, cx->reg_mem + reg);
}
static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg,
u32 eval, u32 mask)
{
- if (cx18_retry_mmio)
- _cx18_write_reg_expect(cx, val, reg, eval, mask);
- else
- cx18_write_reg_noretry(cx, val, reg);
-}
-
-
-static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_noretry(cx, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_read_reg_retry(struct cx18 *cx, u32 reg)
-{
- return cx18_readl_retry(cx, cx->reg_mem + reg);
+ cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask);
}
static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
{
- if (cx18_retry_mmio)
- return cx18_read_reg_retry(cx, reg);
-
- return cx18_read_reg_noretry(cx, reg);
-}
-
-
-static inline u32 cx18_write_reg_sync_noretry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_noretry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync_retry(struct cx18 *cx, u32 val, u32 reg)
-{
- return cx18_write_sync_retry(cx, val, cx->reg_mem + reg);
-}
-
-static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
-{
- if (cx18_retry_mmio)
- return cx18_write_reg_sync_retry(cx, val, reg);
-
- return cx18_write_reg_sync_noretry(cx, val, reg);
+ return cx18_readl(cx, cx->reg_mem + reg);
}
/* Access "encoder memory" region of CX23418 memory mapped I/O */
-static inline void cx18_write_enc_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline void cx18_write_enc_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- cx18_writel_retry(cx, val, cx->enc_mem + addr);
-}
-
static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
{
- if (cx18_retry_mmio)
- cx18_write_enc_retry(cx, val, addr);
- else
- cx18_write_enc_noretry(cx, val, addr);
-}
-
-
-static inline u32 cx18_read_enc_noretry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_noretry(cx, cx->enc_mem + addr);
-}
-
-static inline u32 cx18_read_enc_retry(struct cx18 *cx, u32 addr)
-{
- return cx18_readl_retry(cx, cx->enc_mem + addr);
+ cx18_writel(cx, val, cx->enc_mem + addr);
}
static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
{
- if (cx18_retry_mmio)
- return cx18_read_enc_retry(cx, addr);
-
- return cx18_read_enc_noretry(cx, addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_noretry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_noretry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync_retry(struct cx18 *cx, u32 val, u32 addr)
-{
- return cx18_write_sync_retry(cx, val, cx->enc_mem + addr);
-}
-
-static inline
-u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
-{
- if (cx18_retry_mmio)
- return cx18_write_enc_sync_retry(cx, val, addr);
-
- return cx18_write_enc_sync_noretry(cx, val, addr);
+ return cx18_readl(cx, cx->enc_mem + addr);
}
void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val);
void cx18_setup_page(struct cx18 *cx, u32 addr);
#endif /* CX18_IO_H */
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index f0ca50f5fdde..e6087486f889 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -237,13 +238,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
if (ret)
return ret;
- if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set &&
- atomic_read(&cx->ana_capturing) > 0)
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
cx->vbi.sliced_in->service_set = 0;
- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
return cx18_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -745,14 +745,12 @@ static int cx18_log_status(struct file *file, void *fh)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- (s->buffers - atomic_read(&s->q_free.buffers))
- * 100 / s->buffers,
+ atomic_read(&s->q_full.buffers) * 100 / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
- cx18_log_statistics(cx);
CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
index 2222f679d86d..08fe24e9510e 100644
--- a/drivers/media/video/cx18/cx18-ioctl.h
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -4,6 +4,7 @@
* Derived from ivtv-ioctl.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index 5fbfbd0f1493..af2f504eda2b 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -21,132 +22,9 @@
#include "cx18-driver.h"
#include "cx18-io.h"
-#include "cx18-firmware.h"
-#include "cx18-fileops.h"
-#include "cx18-queue.h"
#include "cx18-irq.h"
-#include "cx18-ioctl.h"
#include "cx18-mailbox.h"
-#include "cx18-vbi.h"
#include "cx18-scb.h"
-#include "cx18-dvb.h"
-
-void cx18_work_handler(struct work_struct *work)
-{
- struct cx18 *cx = container_of(work, struct cx18, work);
- if (test_and_clear_bit(CX18_F_I_WORK_INITED, &cx->i_flags)) {
- struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
- /* This thread must use the FIFO scheduler as it
- * is realtime sensitive. */
- sched_setscheduler(current, SCHED_FIFO, &param);
- }
- if (test_and_clear_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags))
- cx18_dvb_work_handler(cx);
-}
-
-static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- u32 handle = mb->args[0];
- struct cx18_stream *s = NULL;
- struct cx18_buffer *buf;
- u32 off;
- int i;
- int id;
-
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- s = &cx->streams[i];
- if ((handle == s->handle) && (s->dvb.enabled))
- break;
- if (s->v4l2dev && handle == s->handle)
- break;
- }
- if (i == CX18_MAX_STREAMS) {
- CX18_WARN("Got DMA done notification for unknown/inactive"
- " handle %d\n", handle);
- mb->error = CXERR_NOT_OPEN;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- return;
- }
-
- off = mb->args[1];
- if (mb->args[2] != 1)
- CX18_WARN("Ack struct = %d for %s\n",
- mb->args[2], s->name);
- id = cx18_read_enc(cx, off);
- buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
- CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
- if (buf) {
- cx18_buf_sync_for_cpu(s, buf);
- if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
- CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
- buf->bytesused);
-
- set_bit(CX18_F_I_WORK_HANDLER_DVB, &cx->i_flags);
- set_bit(CX18_F_I_HAVE_WORK, &cx->i_flags);
- } else
- set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
- } else {
- CX18_WARN("Could not find buf %d for stream %s\n",
- cx18_read_enc(cx, off), s->name);
- }
- mb->error = 0;
- mb->cmd = 0;
- cx18_mb_ack(cx, mb);
- wake_up(&cx->dma_waitq);
- if (s->id != -1)
- wake_up(&s->waitq);
-}
-
-static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
-{
- char str[256] = { 0 };
- char *p;
-
- if (mb->args[1]) {
- cx18_setup_page(cx, mb->args[1]);
- cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
- str[252] = 0;
- }
- cx18_mb_ack(cx, mb);
- CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
- p = strchr(str, '.');
- if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
- CX18_INFO("FW version: %s\n", p - 1);
-}
-
-static void epu_cmd(struct cx18 *cx, u32 sw1)
-{
- struct cx18_mailbox mb;
-
- if (sw1 & IRQ_CPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
- mb.error = 0;
-
- switch (mb.cmd) {
- case CX18_EPU_DMA_DONE:
- epu_dma_done(cx, &mb);
- break;
- case CX18_EPU_DEBUG:
- epu_debug(cx, &mb);
- break;
- default:
- CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n",
- mb.cmd);
- break;
- }
- }
-
- if (sw1 & IRQ_APU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-
- if (sw1 & IRQ_HPU_TO_EPU) {
- cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb));
- CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd);
- }
-}
static void xpu_ack(struct cx18 *cx, u32 sw2)
{
@@ -154,23 +32,24 @@ static void xpu_ack(struct cx18 *cx, u32 sw2)
wake_up(&cx->mb_cpu_waitq);
if (sw2 & IRQ_APU_TO_EPU_ACK)
wake_up(&cx->mb_apu_waitq);
- if (sw2 & IRQ_HPU_TO_EPU_ACK)
- wake_up(&cx->mb_hpu_waitq);
+}
+
+static void epu_cmd(struct cx18 *cx, u32 sw1)
+{
+ if (sw1 & IRQ_CPU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, CPU);
+ if (sw1 & IRQ_APU_TO_EPU)
+ cx18_api_epu_cmd_irq(cx, APU);
}
irqreturn_t cx18_irq_handler(int irq, void *dev_id)
{
struct cx18 *cx = (struct cx18 *)dev_id;
- u32 sw1, sw1_mask;
- u32 sw2, sw2_mask;
- u32 hw2, hw2_mask;
+ u32 sw1, sw2, hw2;
- sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
- sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask;
- sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
- sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
- hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
- hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
+ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask;
+ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask;
+ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask;
if (sw1)
cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1);
@@ -180,7 +59,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2);
if (sw1 || sw2 || hw2)
- CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+ CX18_DEBUG_HI_IRQ("received interrupts "
+ "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+
+ /*
+ * SW1 responses have to happen first. The sending XPU times out the
+ * incoming mailboxes on us rather rapidly.
+ */
+ if (sw1)
+ epu_cmd(cx, sw1);
/* To do: interrupt-based I2C handling
if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) {
@@ -190,11 +77,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id)
if (sw2)
xpu_ack(cx, sw2);
- if (sw1)
- epu_cmd(cx, sw1);
-
- if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags))
- queue_work(cx->work_queue, &cx->work);
-
return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE;
}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
index 6173ca3bc9e4..91f0b5278ef9 100644
--- a/drivers/media/video/cx18/cx18-irq.h
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -2,6 +2,7 @@
* cx18 interrupt handling
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -28,8 +29,7 @@
#define SW1_INT_ENABLE_PCI 0xc7311c
#define SW2_INT_SET 0xc73140
#define SW2_INT_STATUS 0xc73144
+#define SW2_INT_ENABLE_CPU 0xc73158
#define SW2_INT_ENABLE_PCI 0xc7315c
irqreturn_t cx18_irq_handler(int irq, void *dev_id);
-
-void cx18_work_handler(struct work_struct *work);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index acff7dfb60df..de5e723fdf44 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -26,15 +27,14 @@
#include "cx18-scb.h"
#include "cx18-irq.h"
#include "cx18-mailbox.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+
+static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
#define API_FAST (1 << 2) /* Short timeout */
#define API_SLOW (1 << 3) /* Additional 300ms timeout */
-#define APU 0
-#define CPU 1
-#define EPU 2
-#define HPU 3
-
struct cx18_api_info {
u32 cmd;
u8 flags; /* Flags, see above */
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = {
API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
- API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
API_ENTRY(0, 0, 0),
};
@@ -97,70 +98,175 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
return NULL;
}
-static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu,
- u32 *state, u32 *irq, u32 *req)
+static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
{
- struct cx18_mailbox __iomem *mb = NULL;
- int wait_count = 0;
- u32 ack;
-
- switch (rpu) {
- case APU:
- mb = &cx->scb->epu2apu_mb;
- *state = cx18_readl(cx, &cx->scb->apu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
- break;
+ char argstr[MAX_MB_ARGUMENTS*11+1];
+ char *p;
+ int i;
- case CPU:
- mb = &cx->scb->epu2cpu_mb;
- *state = cx18_readl(cx, &cx->scb->cpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
- break;
+ if (!(cx18_debug & CX18_DBGFLG_API))
+ return;
- case HPU:
- mb = &cx->scb->epu2hpu_mb;
- *state = cx18_readl(cx, &cx->scb->hpu_state);
- *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
- break;
+ for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
+ /* kernel snprintf() appends '\0' always */
+ snprintf(p, 12, " %#010x", mb->args[i]);
}
+ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+ "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
+}
- if (mb == NULL)
- return mb;
- do {
- *req = cx18_readl(cx, &mb->request);
- ack = cx18_readl(cx, &mb->ack);
- wait_count++;
- } while (*req != ack && wait_count < 600);
+/*
+ * Functions that run in a work_queue work handling context
+ */
- if (*req == ack) {
- (*req)++;
- if (*req == 0 || *req == 0xffffffff)
- *req = 1;
- return mb;
+static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_count, id;
+ struct cx18_mailbox *mb;
+ struct cx18_mdl_ack *mdl_ack;
+ struct cx18_stream *s;
+ struct cx18_buffer *buf;
+ int i;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ s = cx18_handle_to_stream(cx, handle);
+
+ if (s == NULL) {
+ CX18_WARN("Got DMA done notification for unknown/inactive"
+ " handle %d, %s mailbox seq no %d\n", handle,
+ (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ?
+ "stale" : "good", mb->request);
+ return;
}
- return NULL;
+
+ mdl_ack_count = mb->args[2];
+ mdl_ack = order->mdl_ack;
+ for (i = 0; i < mdl_ack_count; i++, mdl_ack++) {
+ id = mdl_ack->id;
+ /*
+ * Simple integrity check for processing a stale (and possibly
+ * inconsistent mailbox): make sure the buffer id is in the
+ * valid range for the stream.
+ *
+ * We go through the trouble of dealing with stale mailboxes
+ * because most of the time, the mailbox data is still valid and
+ * unchanged (and in practice the firmware ping-pongs the
+ * two mdl_ack buffers so mdl_acks are not stale).
+ *
+ * There are occasions when we get a half changed mailbox,
+ * which this check catches for a handle & id mismatch. If the
+ * handle and id do correspond, the worst case is that we
+ * completely lost the old buffer, but pick up the new buffer
+ * early (but the new mdl_ack is guaranteed to be good in this
+ * case as the firmware wouldn't point us to a new mdl_ack until
+ * it's filled in).
+ *
+ * cx18_queue_get buf() will detect the lost buffers
+ * and send them back to q_free for fw rotation eventually.
+ */
+ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
+ !(id >= s->mdl_offset &&
+ id < (s->mdl_offset + s->buffers))) {
+ CX18_WARN("Fell behind! Ignoring stale mailbox with "
+ " inconsistent data. Lost buffer for mailbox "
+ "seq no %d\n", mb->request);
+ break;
+ }
+ buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
+ if (buf == NULL) {
+ CX18_WARN("Could not find buf %d for stream %s\n",
+ id, s->name);
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ continue;
+ }
+
+ if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
+ CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
+ buf->bytesused);
+ dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
+ buf->bytesused);
+ }
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ /* Put back TS buffer, since it was removed from all queues */
+ if (s->type == CX18_ENC_STREAM_TYPE_TS)
+ cx18_stream_put_buf_fw(s, buf);
+ }
+ wake_up(&cx->dma_waitq);
+ if (s->id != -1)
+ wake_up(&s->waitq);
}
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
+static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order)
{
- const struct cx18_api_info *info = find_api_info(mb->cmd);
- struct cx18_mailbox __iomem *ack_mb;
- u32 ack_irq;
- u8 rpu = CPU;
+ char *p;
+ char *str = order->str;
- if (info == NULL && mb->cmd) {
- CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
- return -EINVAL;
- }
- if (info)
- rpu = info->rpu;
+ CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str);
+ p = strchr(str, '.');
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+ CX18_INFO("FW version: %s\n", p - 1);
+}
- switch (rpu) {
- case HPU:
- ack_irq = IRQ_EPU_TO_HPU_ACK;
- ack_mb = &cx->scb->hpu2epu_mb;
+static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ epu_dma_done(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ epu_debug(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static
+void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ atomic_set(&order->pending, 0);
+}
+
+void cx18_epu_work_handler(struct work_struct *work)
+{
+ struct cx18_epu_work_order *order =
+ container_of(work, struct cx18_epu_work_order, work);
+ struct cx18 *cx = order->cx;
+ epu_cmd(cx, order);
+ free_epu_work_order(cx, order);
+}
+
+
+/*
+ * Functions that run in an interrupt handling context
+ */
+
+static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ struct cx18_mailbox __iomem *ack_mb;
+ u32 ack_irq, req;
+
+ switch (order->rpu) {
case APU:
ack_irq = IRQ_EPU_TO_APU_ACK;
ack_mb = &cx->scb->apu2epu_mb;
@@ -170,26 +276,197 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
ack_mb = &cx->scb->cpu2epu_mb;
break;
default:
- CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
- return -EINVAL;
+ CX18_WARN("Unhandled RPU (%d) for command %x ack\n",
+ order->rpu, order->mb.cmd);
+ return;
}
- cx18_setup_page(cx, SCB_OFFSET);
- cx18_write_sync(cx, mb->request, &ack_mb->ack);
+ req = order->mb.request;
+ /* Don't ack if the RPU has gotten impatient and timed us out */
+ if (req != cx18_readl(cx, &ack_mb->request) ||
+ req == cx18_readl(cx, &ack_mb->ack)) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u) "
+ "while processing\n",
+ rpu_str[order->rpu], rpu_str[order->rpu], req);
+ order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC;
+ return;
+ }
+ cx18_writel(cx, req, &ack_mb->ack);
cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq);
- return 0;
+ return;
+}
+
+static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 handle, mdl_ack_offset, mdl_ack_count;
+ struct cx18_mailbox *mb;
+
+ mb = &order->mb;
+ handle = mb->args[0];
+ mdl_ack_offset = mb->args[1];
+ mdl_ack_count = mb->args[2];
+
+ if (handle == CX18_INVALID_TASK_HANDLE ||
+ mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) {
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return -1;
+ }
+
+ cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset,
+ sizeof(struct cx18_mdl_ack) * mdl_ack_count);
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+ return 1;
+}
+
+static
+int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ u32 str_offset;
+ char *str = order->str;
+
+ str[0] = '\0';
+ str_offset = order->mb.args[1];
+ if (str_offset) {
+ cx18_setup_page(cx, str_offset);
+ cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252);
+ str[252] = '\0';
+ cx18_setup_page(cx, SCB_OFFSET);
+ }
+
+ if ((order->flags & CX18_F_EWO_MB_STALE) == 0)
+ mb_ack_irq(cx, order);
+
+ return str_offset ? 1 : 0;
}
+static inline
+int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order)
+{
+ int ret = -1;
+
+ switch (order->rpu) {
+ case CPU:
+ {
+ switch (order->mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ ret = epu_dma_done_irq(cx, order);
+ break;
+ case CX18_EPU_DEBUG:
+ ret = epu_debug_irq(cx, order);
+ break;
+ default:
+ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ }
+ break;
+ }
+ case APU:
+ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n",
+ order->mb.cmd);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static inline
+struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx)
+{
+ int i;
+ struct cx18_epu_work_order *order = NULL;
+
+ for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) {
+ /*
+ * We only need "pending" atomic to inspect its contents,
+ * and need not do a check and set because:
+ * 1. Any work handler thread only clears "pending" and only
+ * on one, particular work order at a time, per handler thread.
+ * 2. "pending" is only set here, and we're serialized because
+ * we're called in an IRQ handler context.
+ */
+ if (atomic_read(&cx->epu_work_order[i].pending) == 0) {
+ order = &cx->epu_work_order[i];
+ atomic_set(&order->pending, 1);
+ break;
+ }
+ }
+ return order;
+}
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+{
+ struct cx18_mailbox __iomem *mb;
+ struct cx18_mailbox *order_mb;
+ struct cx18_epu_work_order *order;
+ int submit;
+
+ switch (rpu) {
+ case CPU:
+ mb = &cx->scb->cpu2epu_mb;
+ break;
+ case APU:
+ mb = &cx->scb->apu2epu_mb;
+ break;
+ default:
+ return;
+ }
+
+ order = alloc_epu_work_order_irq(cx);
+ if (order == NULL) {
+ CX18_WARN("Unable to find blank work order form to schedule "
+ "incoming mailbox command processing\n");
+ return;
+ }
+
+ order->flags = 0;
+ order->rpu = rpu;
+ order_mb = &order->mb;
+
+ /* mb->cmd and mb->args[0] through mb->args[2] */
+ cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32));
+ /* mb->request and mb->ack. N.B. we want to read mb->ack last */
+ cx18_memcpy_fromio(cx, &order_mb->request, &mb->request,
+ 2 * sizeof(u32));
+
+ if (order_mb->request == order_mb->ack) {
+ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our "
+ "incoming %s to EPU mailbox (sequence no. %u)"
+ "\n",
+ rpu_str[rpu], rpu_str[rpu], order_mb->request);
+ dump_mb(cx, order_mb, "incoming");
+ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+ }
+
+ /*
+ * Individual EPU command processing is responsible for ack-ing
+ * a non-stale mailbox as soon as possible
+ */
+ submit = epu_cmd_irq(cx, order);
+ if (submit > 0) {
+ queue_work(cx->work_queue, &order->work);
+ }
+}
+
+
+/*
+ * Functions called from a non-interrupt, non work_queue context
+ */
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
const struct cx18_api_info *info = find_api_info(cmd);
- u32 state = 0, irq = 0, req, oldreq, err;
+ u32 state, irq, req, ack, err;
struct cx18_mailbox __iomem *mb;
+ u32 __iomem *xpu_state;
wait_queue_head_t *waitq;
- int timeout = 100;
- int cnt = 0;
- int sig = 0;
+ struct mutex *mb_lock;
+ long int timeout, ret;
int i;
if (info == NULL) {
@@ -201,50 +478,104 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
CX18_DEBUG_HI_API("%s\n", info->name);
else
CX18_DEBUG_API("%s\n", info->name);
- cx18_setup_page(cx, SCB_OFFSET);
- mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
- if (mb == NULL) {
- CX18_ERR("mb %s busy\n", info->name);
- return -EBUSY;
+ switch (info->rpu) {
+ case APU:
+ waitq = &cx->mb_apu_waitq;
+ mb_lock = &cx->epu2apu_mb_lock;
+ irq = IRQ_EPU_TO_APU;
+ mb = &cx->scb->epu2apu_mb;
+ xpu_state = &cx->scb->apu_state;
+ break;
+ case CPU:
+ waitq = &cx->mb_cpu_waitq;
+ mb_lock = &cx->epu2cpu_mb_lock;
+ irq = IRQ_EPU_TO_CPU;
+ mb = &cx->scb->epu2cpu_mb;
+ xpu_state = &cx->scb->cpu_state;
+ break;
+ default:
+ CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
+ return -EINVAL;
}
- oldreq = req - 1;
+ mutex_lock(mb_lock);
+ /*
+ * Wait for an in-use mailbox to complete
+ *
+ * If the XPU is responding with Ack's, the mailbox shouldn't be in
+ * a busy state, since we serialize access to it on our end.
+ *
+ * If the wait for ack after sending a previous command was interrupted
+ * by a signal, we may get here and find a busy mailbox. After waiting,
+ * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
+ */
+ state = cx18_readl(cx, xpu_state);
+ req = cx18_readl(cx, &mb->request);
+ timeout = msecs_to_jiffies(10);
+ ret = wait_event_timeout(*waitq,
+ (ack = cx18_readl(cx, &mb->ack)) == req,
+ timeout);
+ if (req != ack) {
+ /* waited long enough, make the mbox "not busy" from our end */
+ cx18_writel(cx, req, &mb->ack);
+ CX18_ERR("mbox was found stuck busy when setting up for %s; "
+ "clearing busy and trying to proceed\n", info->name);
+ } else if (ret != timeout)
+ CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
+ jiffies_to_msecs(timeout-ret));
+
+ /* Build the outgoing mailbox */
+ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;
+
cx18_writel(cx, cmd, &mb->cmd);
for (i = 0; i < args; i++)
cx18_writel(cx, data[i], &mb->args[i]);
cx18_writel(cx, 0, &mb->error);
cx18_writel(cx, req, &mb->request);
+ cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */
- switch (info->rpu) {
- case APU: waitq = &cx->mb_apu_waitq; break;
- case CPU: waitq = &cx->mb_cpu_waitq; break;
- case EPU: waitq = &cx->mb_epu_waitq; break;
- case HPU: waitq = &cx->mb_hpu_waitq; break;
- default: return -EINVAL;
- }
- if (info->flags & API_FAST)
- timeout /= 2;
+ /*
+ * Notify the XPU and wait for it to send an Ack back
+ */
+ timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);
+
+ CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
+ irq, info->name);
cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);
- while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
- && cnt < 660) {
- if (cnt > 200 && !in_atomic())
- sig = cx18_msleep_timeout(10, 1);
- cnt++;
- }
- if (sig)
- return -EINTR;
- if (cnt == 660) {
- cx18_writel(cx, oldreq, &mb->request);
- CX18_ERR("mb %s failed\n", info->name);
+ ret = wait_event_timeout(
+ *waitq,
+ cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
+ timeout);
+
+ if (ret == 0) {
+ /* Timed out */
+ mutex_unlock(mb_lock);
+ CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU "
+ "acknowledgement\n",
+ info->name, jiffies_to_msecs(timeout));
return -EINVAL;
}
+
+ if (ret != timeout)
+ CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
+ jiffies_to_msecs(timeout-ret), info->name);
+
+ /* Collect data returned by the XPU */
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
data[i] = cx18_readl(cx, &mb->args[i]);
err = cx18_readl(cx, &mb->error);
- if (!in_atomic() && (info->flags & API_SLOW))
+ mutex_unlock(mb_lock);
+
+ /*
+ * Wait for XPU to perform extra actions for the caller in some cases.
+ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
+ * back in a burst shortly thereafter
+ */
+ if (info->flags & API_SLOW)
cx18_msleep_timeout(300, 0);
+
if (err)
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
info->name);
@@ -253,12 +584,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
- int res = cx18_api_call(cx, cmd, args, data);
-
- /* Allow a single retry, probably already too late though.
- If there is no free mailbox then that is usually an indication
- of a more serious problem. */
- return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
+ return cx18_api_call(cx, cmd, args, data);
}
static int cx18_set_filter_param(struct cx18_stream *s)
@@ -281,8 +607,9 @@ static int cx18_set_filter_param(struct cx18_stream *s)
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA])
{
- struct cx18 *cx = priv;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ struct cx18_api_func_private *api_priv = priv;
+ struct cx18 *cx = api_priv->cx;
+ struct cx18_stream *s = api_priv->s;
switch (cmd) {
case CX2341X_ENC_SET_OUTPUT_PORT:
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index d995641536b3..ce2b6686aa00 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -2,6 +2,7 @@
* cx18 mailbox functions
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -30,8 +31,24 @@
#define MB_RESERVED_HANDLE_0 0
#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
struct cx18;
+/*
+ * This structure is used by CPU to provide completed buffers information
+ * Its structure is dictrated by the layout of the SCB, required by the
+ * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
+ * for mailbox work order scheduling
+ */
+struct cx18_mdl_ack {
+ u32 id; /* ID of a completed MDL */
+ u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+};
+
/* The cx18_mailbox struct is the mailbox structure which is used for passing
messages between processors */
struct cx18_mailbox {
@@ -62,12 +79,22 @@ struct cx18_mailbox {
u32 error;
};
+struct cx18_stream;
+
+struct cx18_api_func_private {
+ struct cx18 *cx;
+ struct cx18_stream *s;
+};
+
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int args, ...);
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA]);
-long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
+
+void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu);
+
+void cx18_epu_work_handler(struct work_struct *work);
#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 174682c2582f..8d9441e88c4e 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -41,91 +42,126 @@ void cx18_queue_init(struct cx18_queue *q)
q->bytesused = 0;
}
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q)
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front)
{
- unsigned long flags = 0;
-
- /* clear the buffer if it is going to be enqueued to the free queue */
- if (q == &s->q_free) {
+ /* clear the buffer if it is not to be enqueued to the full queue */
+ if (q != &s->q_full) {
buf->bytesused = 0;
buf->readpos = 0;
buf->b_flags = 0;
+ buf->skipped = 0;
}
- spin_lock_irqsave(&s->qlock, flags);
- list_add_tail(&buf->list, &q->list);
- atomic_inc(&q->buffers);
+
+ mutex_lock(&s->qlock);
+
+ /* q_busy is restricted to a max buffer count imposed by firmware */
+ if (q == &s->q_busy &&
+ atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ q = &s->q_free;
+
+ if (to_front)
+ list_add(&buf->list, &q->list); /* LIFO */
+ else
+ list_add_tail(&buf->list, &q->list); /* FIFO */
q->bytesused += buf->bytesused - buf->readpos;
- spin_unlock_irqrestore(&s->qlock, flags);
+ atomic_inc(&q->buffers);
+
+ mutex_unlock(&s->qlock);
+ return q;
}
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
{
struct cx18_buffer *buf = NULL;
- unsigned long flags = 0;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
if (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_del_init(q->list.next);
- atomic_dec(&q->buffers);
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_del_init(&buf->list);
q->bytesused -= buf->bytesused - buf->readpos;
+ buf->skipped = 0;
+ atomic_dec(&q->buffers);
}
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
return buf;
}
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused)
{
struct cx18 *cx = s->cx;
- struct list_head *p;
-
- spin_lock(&s->qlock);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf =
- list_entry(p, struct cx18_buffer, list);
-
- if (buf->id != id)
+ struct cx18_buffer *buf;
+ struct cx18_buffer *tmp;
+ struct cx18_buffer *ret = NULL;
+
+ mutex_lock(&s->qlock);
+ list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
+ if (buf->id != id) {
+ buf->skipped++;
+ if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
+ /* buffer must have fallen out of rotation */
+ CX18_WARN("Skipped %s, buffer %d, %d "
+ "times - it must have dropped out of "
+ "rotation\n", s->name, buf->id,
+ buf->skipped);
+ /* move it to q_free */
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags =
+ buf->skipped = 0;
+ atomic_dec(&s->q_busy.buffers);
+ atomic_inc(&s->q_free.buffers);
+ }
continue;
+ }
buf->bytesused = bytesused;
- atomic_dec(&s->q_free.buffers);
- atomic_inc(&s->q_full.buffers);
- s->q_full.bytesused += buf->bytesused;
- list_move_tail(&buf->list, &s->q_full.list);
+ /* Sync the buffer before we release the qlock */
+ cx18_buf_sync_for_cpu(s, buf);
+ if (s->type == CX18_ENC_STREAM_TYPE_TS) {
+ /*
+ * TS doesn't use q_full. As we pull the buffer off of
+ * the queue here, the caller will have to put it back.
+ */
+ list_del_init(&buf->list);
+ } else {
+ /* Move buffer from q_busy to q_full */
+ list_move_tail(&buf->list, &s->q_full.list);
+ set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ s->q_full.bytesused += buf->bytesused;
+ atomic_inc(&s->q_full.buffers);
+ }
+ atomic_dec(&s->q_busy.buffers);
- spin_unlock(&s->qlock);
- return buf;
+ ret = buf;
+ break;
}
- spin_unlock(&s->qlock);
- CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
- return NULL;
+ mutex_unlock(&s->qlock);
+ return ret;
}
/* Move all buffers of a queue to q_free, while flushing the buffers */
static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
{
- unsigned long flags;
struct cx18_buffer *buf;
if (q == &s->q_free)
return;
- spin_lock_irqsave(&s->qlock, flags);
+ mutex_lock(&s->qlock);
while (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_move_tail(q->list.next, &s->q_free.list);
- buf->bytesused = buf->readpos = buf->b_flags = 0;
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
atomic_inc(&s->q_free.buffers);
}
cx18_queue_init(q);
- spin_unlock_irqrestore(&s->qlock, flags);
+ mutex_unlock(&s->qlock);
}
void cx18_flush_queues(struct cx18_stream *s)
{
- cx18_queue_flush(s, &s->q_io);
+ cx18_queue_flush(s, &s->q_busy);
cx18_queue_flush(s, &s->q_full);
}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 7f93bb13c09f..456cec3bc28f 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -4,6 +4,7 @@
* Derived from ivtv-queue.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -42,11 +43,26 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
void cx18_buf_swap(struct cx18_buffer *buf);
/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+}
+
void cx18_queue_init(struct cx18_queue *q);
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q);
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
-struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
+struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused);
void cx18_flush_queues(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
index f56d3772aa67..34b4d03c55cd 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -117,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx)
cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
&cx->scb->ipc_offset);
- cx18_writel(cx, 1, &cx->scb->hpu_state);
cx18_writel(cx, 1, &cx->scb->epu_state);
}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
index 594713bbed68..1dc1c431f5a1 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -2,6 +2,7 @@
* cx18 System Control Block initialization
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -85,12 +86,6 @@ struct cx18_mdl {
u32 length; /* Length of the buffer segment */
};
-/* This structure is used by CPU to provide completed buffers information */
-struct cx18_mdl_ack {
- u32 id; /* ID of a completed MDL */
- u32 data_used; /* Total data filled in the MDL for buffer 'id' */
-};
-
struct cx18_scb {
/* These fields form the System Control Block which is used at boot time
for localizing the IPC data as well as the code positions for all
@@ -276,7 +271,7 @@ struct cx18_scb {
struct cx18_mailbox hpu2epu_mb;
struct cx18_mailbox ppu2epu_mb;
- struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2];
+ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
struct cx18_mdl cpu_mdl[1];
};
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index e5ff7705b7a1..63c336c95ff5 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -110,7 +111,6 @@ static void cx18_stream_init(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
struct video_device *dev = s->v4l2dev;
- u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
/* we need to keep v4l2dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
@@ -123,21 +123,15 @@ static void cx18_stream_init(struct cx18 *cx, int type)
s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
- if (s->buf_size)
- s->buffers = max_size / s->buf_size;
- if (s->buffers > 63) {
- /* Each stream has a maximum of 63 buffers,
- ensure we do not exceed that. */
- s->buffers = 63;
- s->buf_size = (max_size / s->buffers) & ~0xfff;
- }
- spin_lock_init(&s->qlock);
+
+ mutex_init(&s->qlock);
init_waitqueue_head(&s->waitq);
s->id = -1;
cx18_queue_init(&s->q_free);
+ cx18_queue_init(&s->q_busy);
cx18_queue_init(&s->q_full);
- cx18_queue_init(&s->q_io);
}
static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -167,7 +161,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
- cx->options.megabytes[type] == 0) {
+ cx->stream_buffers[type] == 0) {
CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
return 0;
}
@@ -267,8 +261,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s (%d MB)\n",
- num, s->name, cx->options.megabytes[type]);
+ CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]/1024);
break;
case VFL_TYPE_RADIO:
@@ -277,10 +272,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
break;
case VFL_TYPE_VBI:
- if (cx->options.megabytes[type])
- CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
- num,
- s->name, cx->options.megabytes[type]);
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device vbi%d for %s "
+ "(%d x %d bytes)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
num, s->name);
@@ -344,7 +340,7 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
static void cx18_vbi_setup(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
- int raw = cx->vbi.sliced_in->service_set == 0;
+ int raw = cx18_raw_vbi(cx);
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
@@ -362,8 +358,7 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
/* determine number of lines and total number of VBI bytes.
- A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
- The '- 1' byte is probably an unused U or V byte. Or something...
+ A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720
A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
header, 42 data bytes + checksum (to be confirmed) */
if (raw) {
@@ -381,14 +376,15 @@ static void cx18_vbi_setup(struct cx18_stream *s)
/* Lines per field */
data[1] = (lines / 2) | ((lines / 2) << 16);
/* bytes per line */
- data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+ data[2] = (raw ? cx->vbi.raw_decoder_line_size
+ : cx->vbi.sliced_decoder_line_size);
/* Every X number of frames a VBI interrupt arrives
(frames as in 25 or 30 fps) */
data[3] = 1;
/* Setup VBI for the cx25840 digitizer */
if (raw) {
data[4] = 0x20602060;
- data[5] = 0x30703070;
+ data[5] = 0x307090d0;
} else {
data[4] = 0xB0F0B0F0;
data[5] = 0xA0E0A0E0;
@@ -401,11 +397,52 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, buf, &s->q_free);
+
+ q = cx18_enqueue(s, buf, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max buffers it can handle */
+
+ cx18_buf_sync_for_device(s, buf);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ return q;
+}
+
+void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_buffer *buf;
+
+ if (atomic_read(&s->q_free.buffers) == 0 ||
+ atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ buf = cx18_dequeue(s, &s->q_free);
+ if (buf == NULL)
+ break;
+ q = cx18_stream_put_buf_fw(s, buf);
+ } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
{
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
- struct list_head *p;
+ struct cx18_buffer *buf;
int ts = 0;
int captype = 0;
@@ -434,8 +471,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_PCM;
break;
case CX18_ENC_STREAM_TYPE_VBI:
- captype = cx->vbi.sliced_in->service_set ?
- CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
cx->vbi.frame = 0;
cx->vbi.inserted_frame = 0;
memset(cx->vbi.sliced_mpeg_size,
@@ -457,6 +494,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
+ struct cx18_api_func_private priv;
+
/* Stuff from Windows, we don't know what it is */
cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
@@ -476,7 +515,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
/* Setup API for Stream */
- cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+ priv.cx = cx;
+ priv.s = s;
+ cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
}
if (atomic_read(&cx->tot_capturing) == 0) {
@@ -488,16 +529,17 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
-
+ /* Init all the cpu_mdls for this stream */
+ cx18_flush_queues(s);
+ mutex_lock(&s->qlock);
+ list_for_each_entry(buf, &s->q_free.list, list) {
cx18_writel(cx, buf->dma_handle,
&cx->scb->cpu_mdl[buf->id].paddr);
cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
}
+ mutex_unlock(&s->qlock);
+ cx18_stream_load_fw_queue(s);
+
/* begin_capture */
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
CX18_DEBUG_WARN("Error starting capture!\n");
@@ -506,9 +548,15 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
else
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
return -EINVAL;
}
@@ -560,9 +608,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
}
- /* Tell the CX23418 it can't use our buffers anymore */
- cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
-
if (s->type != CX18_ENC_STREAM_TYPE_TS)
atomic_dec(&cx->ana_capturing);
atomic_dec(&cx->tot_capturing);
@@ -570,6 +615,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
/* Clear capture and no-read bits */
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
s->handle = CX18_INVALID_TASK_HANDLE;
@@ -595,3 +643,21 @@ u32 cx18_find_handle(struct cx18 *cx)
}
return CX18_INVALID_TASK_HANDLE;
}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+ int i;
+ struct cx18_stream *s;
+
+ if (handle == CX18_INVALID_TASK_HANDLE)
+ return NULL;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+ if (s->v4l2dev || s->dvb.enabled)
+ return s;
+ }
+ return NULL;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index f327e947b24f..420e0a172945 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -4,6 +4,7 @@
* Derived from ivtv-streams.h
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.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
@@ -22,11 +23,15 @@
*/
u32 cx18_find_handle(struct cx18 *cx);
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle);
int cx18_streams_setup(struct cx18 *cx);
int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
/* Capture related */
+void cx18_stream_load_fw_queue(struct cx18_stream *s);
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf);
int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index 22e76ee3f447..fb595bd548e8 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -160,11 +160,14 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
return;
/* Raw VBI data */
- if (cx->vbi.sliced_in->service_set == 0) {
+ if (cx18_raw_vbi(cx)) {
u8 type;
cx18_buf_swap(buf);
+ /* Skip 12 bytes of header that gets stuffed in */
+ size -= 12;
+ memcpy(p, &buf->buf[12], size);
type = p[3];
size = buf->bytesused = compress_raw_buf(cx, p, size);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 9f6be2d457fb..84c0ff13b607 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -25,7 +25,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 1
+#define CX18_DRIVER_VERSION_PATCHLEVEL 4
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 668f968d7761..601f3a2ab742 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -44,6 +44,7 @@
/* All commands for CPU have the following mask set */
#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000)
#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
@@ -71,6 +72,11 @@
0/zero/NULL means "I have nothing to say" */
#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+/* Reads memory/registers (32-bit)
+ IN[0] - Address
+ OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003)
+
/* Description: This command starts streaming with the set channel type
IN[0] - Task handle. Handle of the task to start
ReturnCode - One of the ERR_CAPTURE_... */
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index 8c1b7fa47a41..00f1e2e8889e 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -11,16 +11,16 @@ config VIDEO_CX23885
select VIDEO_CX25840
select VIDEO_CX2341X
select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE
select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
- select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
---help---
This is a video4linux driver for Conexant 23885 based
TV cards.
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 00831f3ef8f5..798d24024353 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -2,7 +2,7 @@
*
* Support for a cx23417 mpeg encoder via cx23885 host port.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
* (c) 2008 Steven Toth <stoth@linuxtv.org>
* - CX23885/7/8 support
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index dac5ccc9ba72..caa098beeecf 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -158,6 +158,10 @@ struct cx23885_board cx23885_boards[] = {
.name = "Leadtek Winfast PxDVR3200 H",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -237,6 +241,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x107d,
.subdevice = 0x6681,
.card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+ }, {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -390,6 +398,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* Tuner Reset Command */
bitmask = 0x04;
break;
@@ -530,6 +539,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x000f000f);
break;
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* GPIO-2 xc3028 tuner reset */
/* The following GPIO's are on the internal AVCore (cx25840) */
@@ -630,6 +640,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -644,6 +655,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
request_module("cx25840");
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index e1aac07b3158..1c454128a9df 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -502,6 +502,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(zl10353_attach,
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 1d53f54cd943..67828029fc69 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -66,6 +66,7 @@
#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig
index de515dadadc2..451133ad41ff 100644
--- a/drivers/media/video/cx25840/Kconfig
+++ b/drivers/media/video/cx25840/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_CX25840
tristate "Conexant CX2584x audio/video decoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
---help---
Support for the Conexant CX2584x audio/video decoders.
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index d6421e1e8f6a..d199d80ea0a3 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -25,7 +25,7 @@
static int set_audclk_freq(struct i2c_client *client, u32 freq)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
@@ -193,7 +193,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
void cx25840_audio_set_path(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
/* assert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x01);
@@ -235,7 +235,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
static int get_volume(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0)
@@ -252,7 +252,7 @@ static int get_volume(struct i2c_client *client)
static void set_volume(struct i2c_client *client, int volume)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0) {
@@ -340,14 +340,14 @@ static void set_balance(struct i2c_client *client, int balance)
static int get_mute(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
return state->unmute_volume >= 0;
}
static void set_mute(struct i2c_client *client, int mute)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (mute && state->unmute_volume == -1) {
int vol = get_volume(client);
@@ -365,7 +365,7 @@ static void set_mute(struct i2c_client *client, int mute)
int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_control *ctrl = arg;
int retval;
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 4da8cd74f00e..2ad277189da8 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -191,7 +191,7 @@ static void cx25840_work_handler(struct work_struct *work)
static void cx25840_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* datasheet startup in numbered steps, refer to page 3-77 */
@@ -259,7 +259,7 @@ static void cx25840_initialize(struct i2c_client *client)
static void cx23885_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* Internal Reset */
@@ -350,7 +350,7 @@ static void cx23885_initialize(struct i2c_client *client)
void cx25840_std_setup(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
int hblank, hactive, burst, vblank, vactive, sc;
int vblank656, src_decimation;
@@ -497,7 +497,7 @@ void cx25840_std_setup(struct i2c_client *client)
static void input_change(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
@@ -551,7 +551,7 @@ static void input_change(struct i2c_client *client)
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
enum cx25840_audio_input aud_input)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
vid_input <= CX25840_COMPOSITE8);
u8 reg;
@@ -671,7 +671,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
static int set_v4lstd(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
@@ -720,9 +720,10 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -786,9 +787,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -823,21 +825,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
/* ----------------------------------------------------------------------- */
-static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
switch (fmt->type) {
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
default:
return -EINVAL;
}
-
return 0;
}
-static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_pix_format *pix;
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
int is_50Hz = !(state->std & V4L2_STD_525_60);
@@ -914,7 +918,7 @@ static void log_video_status(struct i2c_client *client)
"0xD", "0xE", "0xF"
};
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
u8 gen_stat1 = cx25840_read(client, 0x40d);
u8 gen_stat2 = cx25840_read(client, 0x40e);
@@ -944,7 +948,7 @@ static void log_video_status(struct i2c_client *client)
static void log_audio_status(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 download_ctl = cx25840_read(client, 0x803);
u8 mod_det_stat0 = cx25840_read(client, 0x804);
u8 mod_det_stat1 = cx25840_read(client, 0x805);
@@ -1097,21 +1101,12 @@ static void log_audio_status(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int cx25840_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int cx25840_init(struct v4l2_subdev *sd, u32 val)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
- struct v4l2_routing *route = arg;
-
- /* ignore these commands */
- switch (cmd) {
- case TUNER_SET_TYPE_ADDR:
- return 0;
- }
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
if (!state->is_initialized) {
- v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd);
/* initialize on first use */
state->is_initialized = 1;
if (state->is_cx25836)
@@ -1121,50 +1116,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
else
cx25840_initialize(client);
}
+ return 0;
+}
- switch (cmd) {
#ifdef CONFIG_VIDEO_ADV_DEBUG
- /* ioctls to allow direct access to the
- * cx25840 registers for testing */
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cx25840_read(client, reg->reg & 0x0fff);
- else
- cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
- break;
- }
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ return 0;
+}
+
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_INT_DECODE_VBI_LINE:
- return cx25840_vbi(client, cmd, arg);
+static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return cx25840_audio(client, cmd, arg);
+ return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi);
+}
+
+static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq);
+}
+
+static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_STREAMON:
- v4l_dbg(1, cx25840_debug, client, "enable output\n");
+ v4l_dbg(1, cx25840_debug, client, "%s output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
if (state->is_cx23885) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
cx25840_write(client, 0x115,
- state->is_cx25836 ? 0x0c : 0x8c);
+ state->is_cx25836 ? 0x0c : 0x8c);
cx25840_write(client, 0x116,
- state->is_cx25836 ? 0x04 : 0x07);
+ state->is_cx25836 ? 0x04 : 0x07);
}
- break;
-
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, cx25840_debug, client, "disable output\n");
+ } else {
if (state->is_cx23885) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
@@ -1172,133 +1186,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
cx25840_write(client, 0x115, 0x00);
cx25840_write(client, 0x116, 0x00);
}
- break;
+ }
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- log_video_status(client);
- if (!state->is_cx25836)
- log_audio_status(client);
- break;
+static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_CTRL:
- return get_v4lctrl(client, (struct v4l2_control *)arg);
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ break;
+ }
+ if (state->is_cx25836)
+ return -EINVAL;
- case VIDIOC_S_CTRL:
- return set_v4lctrl(client, (struct v4l2_control *)arg);
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ return v4l2_ctrl_query_fill(qc, 0, 65535,
+ 65535 / 100, state->default_volume);
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- break;
- }
- if (state->is_cx25836)
- return -EINVAL;
+ if (state->radio == 0 && state->std == std)
+ return 0;
+ state->radio = 0;
+ state->std = std;
+ return set_v4lstd(client);
+}
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
- }
- return -EINVAL;
- }
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = state->std;
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_S_STD:
- if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
- return 0;
- state->radio = 0;
- state->std = *(v4l2_std_id *)arg;
- return set_v4lstd(client);
+static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ return set_input(client, route->input, state->aud_input);
+}
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->vid_input;
- route->output = 0;
- break;
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_VIDEO_ROUTING:
- return set_input(client, route->input, state->aud_input);
+ if (state->is_cx25836)
+ return -EINVAL;
+ return set_input(client, state->vid_input, route->input);
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- route->input = state->aud_input;
- route->output = 0;
- break;
+static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- return set_input(client, state->vid_input, route->input);
+ if (!state->is_cx25836)
+ input_change(client);
+ return 0;
+}
- case VIDIOC_S_FREQUENCY:
- if (!state->is_cx25836) {
- input_change(client);
- }
- break;
+static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
- case VIDIOC_G_TUNER:
- {
- u8 vpres = cx25840_read(client, 0x40e) & 0x20;
- u8 mode;
- int val = 0;
+ if (state->radio)
+ return 0;
- if (state->radio)
- break;
+ vt->signal = vpres ? 0xffff : 0x0;
+ if (state->is_cx25836)
+ return 0;
- vt->signal = vpres ? 0xffff : 0x0;
- if (state->is_cx25836)
- break;
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- vt->capability |=
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ mode = cx25840_read(client, 0x804);
- mode = cx25840_read(client, 0x804);
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
- /* get rxsubchans and audmode */
- if ((mode & 0xf) == 1)
- val |= V4L2_TUNER_SUB_STEREO;
- else
- val |= V4L2_TUNER_SUB_MONO;
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- if (mode == 2 || mode == 4)
- val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
- if (mode & 0x10)
- val |= V4L2_TUNER_SUB_SAP;
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
- vt->rxsubchans = val;
- vt->audmode = state->audmode;
- break;
- }
+static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_TUNER:
- if (state->radio || state->is_cx25836)
- break;
+ if (state->radio || state->is_cx25836)
+ return 0;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
@@ -1326,41 +1343,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
break;
default:
return -EINVAL;
- }
- state->audmode = vt->audmode;
- break;
+ }
+ state->audmode = vt->audmode;
+ return 0;
+}
- case VIDIOC_G_FMT:
- return get_v4lfmt(client, (struct v4l2_format *)arg);
+static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_FMT:
- return set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->is_cx25836)
+ cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+}
- case VIDIOC_INT_RESET:
- if (state->is_cx25836)
- cx25836_initialize(client);
- else if (state->is_cx23885)
- cx23885_initialize(client);
- else
- cx25840_initialize(client);
- break;
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
- default:
- return -EINVAL;
- }
+static int cx25840_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ log_video_status(client);
+ if (!state->is_cx25836)
+ log_audio_status(client);
return 0;
}
+static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cx25840_core_ops = {
+ .log_status = cx25840_log_status,
+ .g_chip_ident = cx25840_g_chip_ident,
+ .g_ctrl = cx25840_g_ctrl,
+ .s_ctrl = cx25840_s_ctrl,
+ .queryctrl = cx25840_queryctrl,
+ .reset = cx25840_reset,
+ .init = cx25840_init,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx25840_g_register,
+ .s_register = cx25840_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
+ .s_frequency = cx25840_s_frequency,
+ .s_std = cx25840_s_std,
+ .s_radio = cx25840_s_radio,
+ .g_tuner = cx25840_g_tuner,
+ .s_tuner = cx25840_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
+ .s_clock_freq = cx25840_s_clock_freq,
+ .s_routing = cx25840_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_routing = cx25840_s_video_routing,
+ .g_fmt = cx25840_g_fmt,
+ .s_fmt = cx25840_s_fmt,
+ .decode_vbi_line = cx25840_decode_vbi_line,
+ .s_stream = cx25840_s_stream,
+};
+
+static const struct v4l2_subdev_ops cx25840_ops = {
+ .core = &cx25840_core_ops,
+ .tuner = &cx25840_tuner_ops,
+ .audio = &cx25840_audio_ops,
+ .video = &cx25840_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int cx25840_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct cx25840_state *state;
+ struct v4l2_subdev *sd;
u32 id;
u16 device_id;
@@ -1392,10 +1468,11 @@ static int cx25840_probe(struct i2c_client *client,
}
state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
- if (state == NULL) {
+ if (state == NULL)
return -ENOMEM;
- }
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
/* Note: revision '(device_id & 0x0f) == 2' was never built. The
marking skips from 0x1 == 22 to 0x3 == 23. */
v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
@@ -1403,7 +1480,6 @@ static int cx25840_probe(struct i2c_client *client,
(device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
client->addr << 1, client->adapter->name);
- i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
@@ -1430,7 +1506,10 @@ static int cx25840_probe(struct i2c_client *client,
static int cx25840_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index b87337e590b4..be0558277ca3 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -22,6 +22,7 @@
#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
#include <linux/i2c.h>
/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
@@ -34,6 +35,7 @@
struct cx25840_state {
struct i2c_client *c;
+ struct v4l2_subdev sd;
int pvr150_workaround;
int radio;
v4l2_std_id std;
@@ -53,6 +55,11 @@ struct cx25840_state {
struct work_struct fw_work; /* work entry for fw load */
};
+static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx25840_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 8d489a4b9570..0b2dceb74108 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -91,7 +91,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
const struct firmware *fw = NULL;
u8 buffer[FWSEND];
const u8 *ptr;
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index 58e6ef1c28a0..03f09b288eb8 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -84,7 +84,7 @@ static int decode_vps(u8 * dst, u8 * p)
int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_format *fmt;
struct v4l2_sliced_vbi_format *svbi;
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 0b9e5fac6239..b0f837588e01 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -56,12 +56,12 @@ config VIDEO_CX88_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_STV0288 if !DVB_FE_CUSTOMISE
select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 06f171ab6149..66c755c116dc 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -742,7 +742,6 @@ static int __devinit snd_cx88_create(struct snd_card *card,
core = cx88_core_get(pci);
if (NULL == core) {
err = -EINVAL;
- kfree (chip);
return err;
}
@@ -812,7 +811,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
err = snd_cx88_create(card, pci, &chip);
if (err < 0)
- return (err);
+ goto error;
err = snd_cx88_pcm(chip, 0, "CX88 Digital");
if (err < 0)
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index d3ae5b4dfca7..e162a70748c5 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -3,7 +3,7 @@
* Support for a cx23416 mpeg encoder via cx2388x host port.
* "blackbird" reference design.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
*
* (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
@@ -39,7 +39,7 @@
#include "cx88.h"
MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
-MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
static unsigned int mpegbufs = 32;
@@ -1244,8 +1244,16 @@ static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
* We're being given access to re-arrange the GPIOs.
* Take the bus off the cx22702 and put the cx23416 on it.
*/
- cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */
- cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ udelay(1000);
break;
default:
err = -ENODEV;
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 5bcbb4cc7c2a..733ede34f93a 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1237,7 +1237,6 @@ static const struct cx88_board cx88_boards[] = {
},
},
[CX88_BOARD_WINFAST_DTV2000H] = {
- /* video inputs and radio still in testing */
.name = "WinFast DTV2000 H",
.tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
.radio_type = UNSET,
@@ -1251,7 +1250,35 @@ static const struct cx88_board cx88_boards[] = {
.gpio1 = 0x00008203,
.gpio2 = 0x00017304,
.gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 2,
+ .gpio0 = 0x0001d503,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d503,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
}},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
.mpeg = CX88_MPEG_DVB,
},
[CX88_BOARD_GENIATECH_DVBS] = {
@@ -1847,6 +1874,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_TBS_8910] = {
+ .name = "TBS 8910 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_TBS_8920] = {
.name = "TBS 8920 DVB-S/S2",
.tuner_type = TUNER_ABSENT,
@@ -1859,6 +1898,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_PROF_6200] = {
+ .name = "Prof 6200 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_PROF_7300] = {
.name = "PROF 7300 DVB-S/S2",
.tuner_type = UNSET,
@@ -1871,6 +1922,18 @@ static const struct cx88_board cx88_boards[] = {
} },
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_SATTRADE_ST4200] = {
+ .name = "SATTRADE ST4200 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -1897,7 +1960,11 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = PCI_VENDOR_ID_ATI,
.subdevice = 0x00f8,
.card = CX88_BOARD_ATI_WONDER_PRO,
- },{
+ }, {
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f9,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
.subvendor = 0x107d,
.subdevice = 0x6611,
.card = CX88_BOARD_WINFAST2000XP_EXPERT,
@@ -2257,13 +2324,25 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x2011,
.card = CX88_BOARD_OMICOM_SS4_PCI,
}, {
+ .subvendor = 0x8910,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8910,
+ }, {
.subvendor = 0x8920,
.subdevice = 0x8888,
.card = CX88_BOARD_TBS_8920,
}, {
+ .subvendor = 0xb022,
+ .subdevice = 0x3022,
+ .card = CX88_BOARD_PROF_6200,
+ }, {
.subvendor = 0xB033,
.subdevice = 0x3033,
.card = CX88_BOARD_PROF_7300,
+ }, {
+ .subvendor = 0xb200,
+ .subdevice = 0x4200,
+ .card = CX88_BOARD_SATTRADE_ST4200,
},
};
@@ -2874,8 +2953,11 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_TEVII_S420:
case CX88_BOARD_TEVII_S460:
case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8910:
case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_6200:
case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
cx_write(MO_SRST_IO, 0);
msleep(100);
cx_write(MO_SRST_IO, 1);
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 60705b08bfe8..b045874ad04f 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -844,6 +844,9 @@ static int set_tvaudio(struct cx88_core *core)
} else if (V4L2_STD_SECAM_L & norm) {
core->tvaudio = WW_L;
+ } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
+ core->tvaudio = WW_BG;
+
} else if (V4L2_STD_SECAM_DK & norm) {
core->tvaudio = WW_DK;
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 309ca5e68063..da4dd4913d9f 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -406,7 +406,7 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
cx_write(MO_GP0_IO, 0x00006060);
break;
case SEC_VOLTAGE_OFF:
- printk("LNB Voltage SEC_VOLTAGE_off\n");
+ printk("LNB Voltage SEC_VOLTAGE_off\n");
break;
}
@@ -606,7 +606,7 @@ static int dvb_register(struct cx8802_dev *dev)
/* Get the first frontend */
fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
if (!fe0)
- return -EINVAL;
+ goto frontend_detach;
/* multi-frontend gate control is undefined or defaults to fe0 */
dev->frontends.gate = 0;
@@ -653,38 +653,35 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
/* DVB-S init */
fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &hauppauge_novas_config,
- &dev->core->i2c_adap);
+ &hauppauge_novas_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
- dprintk( 1, "%s(): HVR3000 - DVB-S LNB Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-S Init: failed\n", __func__);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
- /* DVB-T init */
+ /* MFE frontend 2 */
fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (fe1) {
- dev->frontends.gate = 2;
- mfe_shared = 1;
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
- &dev->core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3)) {
- dprintk( 1, "%s(): HVR3000 - DVB-T misc Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-T Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR3000 - DVB-T Init: can't find frontend 2.\n", __func__);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
}
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
@@ -998,50 +995,51 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
/* DVB-S/S2 Init */
fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- if(!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
- dprintk( 1, "%s(): HVR4000 - DVB-S LNB Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-S Init: failed\n", __func__);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
- /* DVB-T Init */
+ /* MFE frontend 2 */
fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (fe1) {
- dev->frontends.gate = 2;
- mfe_shared = 1;
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
- &dev->core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3)) {
- dprintk( 1, "%s(): HVR4000 - DVB-T misc Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-T Init: failed\n", __func__);
- }
- } else {
- dprintk( 1, "%s(): HVR4000 - DVB-T Init: can't find frontend 2.\n", __func__);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T Init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
}
break;
case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
if (fe0->dvb.frontend) {
- dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00);
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
}
break;
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_TBS_8910:
case CX88_BOARD_TEVII_S420:
fe0->dvb.frontend = dvb_attach(stv0299_attach,
&tevii_tuner_sharp_config,
@@ -1070,21 +1068,18 @@ static int dvb_register(struct cx8802_dev *dev)
fe0->dvb.frontend = dvb_attach(cx24116_attach,
&tevii_s460_config,
&core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ if (fe0->dvb.frontend != NULL)
fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- }
break;
case CX88_BOARD_OMICOM_SS4_PCI:
case CX88_BOARD_TBS_8920:
case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
fe0->dvb.frontend = dvb_attach(cx24116_attach,
&hauppauge_hvr4000_config,
&core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ if (fe0->dvb.frontend != NULL)
fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- }
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
@@ -1092,11 +1087,11 @@ static int dvb_register(struct cx8802_dev *dev)
break;
}
- if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
+ if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
printk(KERN_ERR
"%s/2: frontend initialization failed\n",
core->name);
- return -EINVAL;
+ goto frontend_detach;
}
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = cx88_tuner_callback;
@@ -1133,40 +1128,44 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
* on the bus. Take the bus from the cx23416 and enable the
* cx22702 demod
*/
- cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
udelay(1000);
break;
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR4000:
- if(core->dvbdev->frontends.active_fe_id == 1) {
- /* DVB-S/S2 Enabled */
-
- /* Toggle reset on cx22702 leaving i2c active */
- cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */
- cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */
- udelay(1000);
-
- cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ switch (core->dvbdev->frontends.active_fe_id) {
+ case 1: /* DVB-S/S2 Enabled */
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ /* Take the cx24116/cx24123 out of reset */
+ cx_write(MO_SRST_IO, 1);
core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
- } else
- if (core->dvbdev->frontends.active_fe_id == 2) {
- /* DVB-T Enabled */
-
+ break;
+ case 2: /* DVB-T Enabled */
/* Put the cx24116/cx24123 into reset */
cx_write(MO_SRST_IO, 0);
-
- /* cx22702 out of reset and enable it */
- cx_set(MO_GP0_IO, 0x00000080);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
- udelay(1000);
+ break;
}
+ udelay(1000);
break;
default:
@@ -1199,8 +1198,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv)
{
struct cx88_core *core = drv->core;
struct cx8802_dev *dev = drv->core->dvbdev;
- int err, i;
- struct videobuf_dvb_frontend *fe;
+ int err;
dprintk( 1, "%s\n", __func__);
dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
@@ -1216,31 +1214,47 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv)
/* If vp3054 isn't enabled, a stub will just return 0 */
err = vp3054_i2c_probe(dev);
if (0 != err)
- goto fail_core;
+ goto fail_probe;
/* dvb stuff */
printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
dev->ts_gen_cntrl = 0x0c;
- for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
- if (!fe) {
- printk(KERN_ERR "%s() failed to get frontend(%d)\n", __func__, i);
- continue;
+ err = -ENODEV;
+ if (core->board.num_frontends) {
+ struct videobuf_dvb_frontend *fe;
+ int i;
+
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+ if (fe == NULL) {
+ printk(KERN_ERR "%s() failed to get frontend(%d)\n",
+ __func__, i);
+ goto fail_probe;
+ }
+ videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_TOP,
+ sizeof(struct cx88_buffer),
+ dev);
+ /* init struct videobuf_dvb */
+ fe->dvb.name = dev->core->name;
}
- videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_TOP,
- sizeof(struct cx88_buffer),
- dev);
- /* init struct videobuf_dvb */
- fe->dvb.name = dev->core->name;
+ } else {
+ /* no frontends allocated */
+ printk(KERN_ERR "%s/2 .num_frontends should be non-zero\n",
+ core->name);
+ goto fail_core;
}
err = dvb_register(dev);
- if (err != 0)
+ if (err)
+ /* frontends/adapter de-allocated in dvb_register */
printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
core->name, err);
+ return err;
+fail_probe:
+ videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
fail_core:
return err;
}
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 3ebdcd1d83f8..a04fee235db6 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -3,7 +3,7 @@
* Support for the mpeg transport stream transfers
* PCI function #2 of the cx2388x.
*
- * (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ * (c) 2004 Jelle Foks <jelle@foks.us>
* (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
* (c) 2004 Gerd Knorr <kraxel@bytesex.org>
*
@@ -34,7 +34,7 @@
/* ------------------------------------------------------------------ */
MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
-MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index f4240965be32..20649b25f7ba 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -53,12 +53,11 @@
/* ----------------------------------------------------------- */
/* defines and enums */
-/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
-#define CX88_NORMS (\
- V4L2_STD_NTSC_M| V4L2_STD_NTSC_M_JP| V4L2_STD_NTSC_443 | \
- V4L2_STD_PAL_BG| V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
- V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
- V4L2_STD_PAL_60| V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK )
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
+#define CX88_NORMS (V4L2_STD_ALL \
+ & ~V4L2_STD_PAL_H \
+ & ~V4L2_STD_NTSC_M_KR \
+ & ~V4L2_STD_SECAM_LC)
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
@@ -229,6 +228,9 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TEVII_S420 73
#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
#define CX88_BOARD_PROF_7300 75
+#define CX88_BOARD_SATTRADE_ST4200 76
+#define CX88_BOARD_TBS_8910 77
+#define CX88_BOARD_PROF_6200 78
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 7a8d49ef646e..15c03f0e69ad 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -424,11 +424,12 @@ static int em28xx_audio_init(struct em28xx *dev)
struct snd_pcm *pcm;
struct snd_card *card;
static int devnr;
- int ret, err;
+ int err;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
@@ -449,7 +450,12 @@ static int em28xx_audio_init(struct em28xx *dev)
}
spin_lock_init(&adev->slock);
- ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
pcm->info_flags = 0;
pcm->private_data = dev;
@@ -461,7 +467,7 @@ static int em28xx_audio_init(struct em28xx *dev)
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
- return -ENOMEM;
+ return err;
}
adev->sndcard = card;
adev->udev = dev->udev;
@@ -475,9 +481,10 @@ static int em28xx_audio_fini(struct em28xx *dev)
if (dev == NULL)
return 0;
- if (dev->has_audio_class) {
+ if (dev->has_alsa_audio != 1) {
/* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module */
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index d65d0572403b..e776699b62f9 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -37,6 +37,8 @@
#include "em28xx.h"
+#define DRIVER_NAME "em28xx"
+
static int tuner = -1;
module_param(tuner, int, 0444);
MODULE_PARM_DESC(tuner, "tuner type");
@@ -45,122 +47,177 @@ static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
+static unsigned long em28xx_devused;
+
struct em28xx_hash_table {
unsigned long hash;
unsigned int model;
unsigned int tuner;
};
+/*
+ * Reset sequences for analog/digital modes
+ */
+
+/* Reset for the most [analog] boards */
+static struct em28xx_reg_seq default_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq default_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 analog */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+ {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
+ {0x05, 0xff, 0x10, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0x0f, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+ {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+
+/* Board - EM2870 Kworld 355u
+ Analog - No input analog */
+
+/* Callback for the most boards */
+static struct em28xx_reg_seq default_tuner_gpio[] = {
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/*
+ * Board definitions
+ */
struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "Unknown EM2750/EM2751 webcam grabber",
- .vchannels = 1,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2800_BOARD_UNKNOWN] = {
.name = "Unknown EM2800 video grabber",
.is_em2800 = 1,
- .vchannels = 2,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_UNKNOWN] = {
- .name = "Unknown EM2750/28xx video grabber",
- .is_em2800 = 0,
- .tuner_type = TUNER_ABSENT,
+ .name = "Unknown EM2750/28xx video grabber",
+ .tuner_type = TUNER_ABSENT,
},
[EM2750_BOARD_DLCW_130] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
.name = "Huaqi DLCW-130",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
.input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2820_BOARD_KWORLD_PVRTV2800RF] = {
.name = "Kworld PVR TV 2800 RF",
- .is_em2800 = 0,
- .vchannels = 2,
.tuner_type = TUNER_TEMIC_PAL,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_TERRATEC_CINERGY_250] = {
.name = "Terratec Cinergy 250 USB",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_USB_2] = {
.name = "Pinnacle PCTV USB 2",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
.name = "Hauppauge WinTV USB 2",
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE|
TDA9887_PORT2_ACTIVE,
.decoder = EM28XX_TVP5150,
.has_msp34xx = 1,
- /*FIXME: S-Video not tested */
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = MSP_INPUT_DEFAULT,
@@ -174,327 +231,305 @@ struct em28xx_board em28xx_boards[] = {
[EM2820_BOARD_DLINK_USB_TV] = {
.name = "D-Link DUB-T210 TV Tuner",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
.name = "Hercules Smart TV USB 2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
+ .decoder = EM28XX_SAA711X,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
.name = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_GADMEI_UTV310] = {
.name = "Gadmei UTV310",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
.name = "Leadtek Winfast USB II Deluxe",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7114,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
- .vmux = 2,
- .amux = 0,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = 1,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = 9,
- .amux = 1,
- } },
- },
- [EM2820_BOARD_PINNACLE_DVC_100] = {
- .name = "Pinnacle Dazzle DVC 100",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .decoder = EM28XX_SAA7113,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
- .name = "Videology 20K14XUSB USB2.0",
+ .name = "Videology 20K14XUSB USB2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2821_BOARD_PROLINK_PLAYTV_USB2] = {
.name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */
.tda9887_conf = TDA9887_PRESENT, /* unknown? */
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2821_BOARD_SUPERCOMP_USB_2] = {
.name = "Supercomp USB 2.0 TV",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_PHILIPS_FM1236_MK3,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE |
TDA9887_PORT2_ACTIVE,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2821_BOARD_USBGEAR_VD204] = {
- .name = "Usbgear VD204v9",
+ .name = "Usbgear VD204v9",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_NETGMBH_CAM] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
- .name = "NetGMBH Cam",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
- .input = { {
+ .name = "NetGMBH Cam",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* This is a webcam */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2860_BOARD_TYPHOON_DVD_MAKER] = {
- .name = "Typhoon DVD Maker",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .name = "Typhoon DVD Maker",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_GADMEI_UTV330] = {
.name = "Gadmei UTV330",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2860_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Cinergy A Hybrid XS",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2861_BOARD_KWORLD_PVRTV_300U] = {
.name = "KWorld PVRTV 300U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
.name = "Yakumo MovieMixer",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 1,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2861_BOARD_PLEXTOR_PX_TV100U] = {
.name = "Plextor ConvertX PX-TV100U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_TNF_5335MF,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
+
+ /* Those boards with em2870 are DVB Only*/
+
[EM2870_BOARD_TERRATEC_XS] = {
.name = "Terratec Cinergy T XS",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
},
[EM2870_BOARD_TERRATEC_XS_MT2060] = {
.name = "Terratec Cinergy T XS (MT2060)",
@@ -505,6 +540,7 @@ struct em28xx_board em28xx_boards[] = {
.name = "Kworld 350 U DVB-T",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
},
[EM2870_BOARD_KWORLD_355U] = {
.name = "Kworld 355 U DVB-T",
@@ -514,164 +550,216 @@ struct em28xx_board em28xx_boards[] = {
.name = "Pinnacle PCTV DVB-T",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_ABSENT, /* MT2060 */
+ /* djh - I have serious doubts this is right... */
+ .xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_10MHZ,
},
[EM2870_BOARD_COMPRO_VIDEOMATE] = {
.name = "Compro, VideoMate U3",
.valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_ABSENT, /* MT2060 */
},
+
[EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
.name = "Terratec Hybrid XS Secam",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.has_msp34xx = 1,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
.name = "Hauppauge WinTV HVR 900",
- .vchannels = 3,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_dvb = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
.name = "Hauppauge WinTV HVR 900 (R2)",
- .vchannels = 3,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+ .name = "Hauppauge WinTV HVR 850",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_hauppauge_new,
+ .decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
.name = "Hauppauge WinTV HVR 950",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_hauppauge_new,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
.name = "Pinnacle PCTV HD Pro Stick",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_pinnacle_pctv_hd,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
.name = "AMD ATI TV Wonder HD 600",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
- .has_12mhz_i2s = 1,
.has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = ir_codes_ati_tv_wonder_hd_600,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2880_BOARD_TERRATEC_HYBRID_XS] = {
- .name = "Terratec Hybrid XS",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_XC2028,
- .decoder = EM28XX_TVP5150,
+ .name = "Terratec Hybrid XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
.has_dvb = 1,
+ .dvb_gpio = default_analog,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
/* maybe there's a reason behind it why Terratec sells the Hybrid XS
@@ -679,172 +767,190 @@ struct em28xx_board em28xx_boards[] = {
maybe we'll need it lateron */
[EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
.name = "Terratec Prodigy XS",
- .vchannels = 3,
- .tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2820_BOARD_MSI_VOX_USB_2] = {
.name = "MSI VOX USB 2.0",
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT |
TDA9887_PORT1_ACTIVE |
TDA9887_PORT2_ACTIVE,
.max_range_640_480 = 1,
-
- .decoder = EM28XX_SAA7114,
+ .decoder = EM28XX_SAA711X,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE4,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_TERRATEC_CINERGY_200] = {
.name = "Terratec Cinergy 200 USB",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_GRABBEEX_USB2800] = {
.name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
.is_em2800 = 1,
- .vchannels = 2,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
.name = "Leadtek Winfast USB II",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_KWORLD_USB2800] = {
.name = "Kworld USB2800",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_PHILIPS_FCV1236D,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PINNACLE_DVC_90] = {
.name = "Pinnacle Dazzle DVC 90/DVC 100",
- .vchannels = 3,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2800_BOARD_VGEAR_POCKETTV] = {
.name = "V-Gear PocketTV",
.is_em2800 = 1,
- .vchannels = 3,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+ .name = "Pixelview PlayTV Box 4 USB 2.0",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
.name = "Pixelview Prolink PlayTV USB 2.0",
- .vchannels = 3,
+ .has_snapshot_button = 1,
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_YMEC_TVF_5533MF,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = SAA7115_COMPOSITE0,
@@ -856,228 +962,252 @@ struct em28xx_board em28xx_boards[] = {
} },
},
[EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = {
- .name = "PointNix Intra-Oral Camera",
+ .name = "PointNix Intra-Oral Camera",
.has_snapshot_button = 1,
- .vchannels = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA7113,
- .input = { {
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
.type = EM28XX_VMUX_SVIDEO,
.vmux = SAA7115_SVIDEO3,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
} },
},
[EM2880_BOARD_MSI_DIGIVOX_AD] = {
.name = "MSI DigiVox A/D",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
} },
},
[EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
.name = "MSI DigiVox A/D II",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
} },
},
[EM2880_BOARD_KWORLD_DVB_305U] = {
.name = "KWorld DVB-T 305U",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2880_BOARD_KWORLD_DVB_310U] = {
.name = "KWorld DVB-T 310U",
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.has_dvb = 1,
+ .dvb_gpio = default_digital,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_AC97_LINE_IN,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, { /* S-video has not been tested yet */
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_AC97_LINE_IN,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2881_BOARD_DNT_DA2_HYBRID] = {
.name = "DNT DA2 Hybrid",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
.name = "Pinnacle Hybrid Pro",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
} },
},
[EM2882_BOARD_PINNACLE_HYBRID_PRO] = {
.name = "Pinnacle Hybrid Pro (2)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.mts_firmware = 1,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2882_BOARD_KWORLD_VS_DVBT] = {
.name = "Kworld VS-DVB-T 323UR",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
} },
},
[EM2882_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Hybrid XS (em2882)",
.valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2883_BOARD_KWORLD_HYBRID_A316] = {
.name = "Kworld PlusTV HD Hybrid 330",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .vchannels = 3,
- .is_em2800 = 0,
.tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
- .amux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
- .amux = 1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
} },
},
[EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
.name = "Compro VideoMate ForYou/Stereo",
- .vchannels = 2,
.tuner_type = TUNER_LG_PAL_NEW_TAPC,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
- .input = { {
+ .input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
@@ -1101,7 +1231,7 @@ struct usb_device_id em28xx_id_table [] = {
{ USB_DEVICE(0xeb1a, 0x2820),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2821),
- .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 },
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2860),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2861),
@@ -1164,8 +1294,8 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
{ USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */
.driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
- { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651f),
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
{ USB_DEVICE(0x0438, 0xb002),
.driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
{ USB_DEVICE(0x2001, 0xf112),
@@ -1189,78 +1319,12 @@ struct usb_device_id em28xx_id_table [] = {
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
/*
- * Reset sequences for analog/digital modes
- */
-
-/* Reset for the most [analog] boards */
-static struct em28xx_reg_seq default_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq default_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 analog */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
- {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
- {0x05, 0xff, 0x10, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x04, 0x0f, 10},
- {EM2880_R04_GPO, 0x0c, 0x0f, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
- {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = {
- {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board - EM2870 Kworld 355u
- Analog - No input analog */
-static struct em28xx_reg_seq em2870_kworld_355u_digital[] = {
- {EM2880_R04_GPO, 0x01, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/* Callback for the most boards */
-static struct em28xx_reg_seq default_callback[] = {
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Callback for EM2882 TERRATEC HYBRID XS */
-static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, 0xff, 6},
- {EM28XX_R08_GPIO, 0x3e, ~EM_GPIO_4, 6},
- {EM2880_R04_GPO, 0x04, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/*
* EEPROM hash table for devices with generic USB IDs
*/
static struct em28xx_hash_table em28xx_eeprom_hash [] = {
/* P/N: SA 60002070465 Tuner: TVF7533-MF */
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
};
@@ -1282,27 +1346,26 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
if (command != XC2028_TUNER_RESET)
return 0;
- if (dev->mode == EM28XX_ANALOG_MODE)
- rc = em28xx_gpio_set(dev, dev->tun_analog_gpio);
- else
- rc = em28xx_gpio_set(dev, dev->tun_digital_gpio);
+ rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
return rc;
}
EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
-static void em28xx_set_model(struct em28xx *dev)
+static void inline em28xx_set_model(struct em28xx *dev)
{
- dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
- dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx;
- dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
- dev->decoder = em28xx_boards[dev->model].decoder;
- dev->video_inputs = em28xx_boards[dev->model].vchannels;
- dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
- dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
- dev->has_dvb = em28xx_boards[dev->model].has_dvb;
- dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button;
- dev->valid = em28xx_boards[dev->model].valid;
+ memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+
+ /* Those are the default values for the majority of boards
+ Use those values if not specified otherwise at boards entry
+ */
+ if (!dev->board.xclk)
+ dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ;
+
+ if (!dev->board.i2c_speed)
+ dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ;
}
/* Since em28xx_pre_card_setup() requires a proper dev->model,
@@ -1312,205 +1375,126 @@ void em28xx_pre_card_setup(struct em28xx *dev)
{
int rc;
- rc = em28xx_read_reg(dev, EM2880_R04_GPO);
- if (rc >= 0)
- dev->reg_gpo = rc;
+ em28xx_set_model(dev);
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
if (rc > 0) {
- switch (rc) {
+ dev->chip_id = rc;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
case CHIP_ID_EM2860:
em28xx_info("chip ID is em2860\n");
break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
case CHIP_ID_EM2883:
em28xx_info("chip ID is em2882/em2883\n");
dev->wait_after_write = 0;
break;
default:
- em28xx_info("em28xx chip ID = %d\n", rc);
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
}
}
- em28xx_set_model(dev);
-
- /* request some modules */
- switch (dev->model) {
- case EM2880_BOARD_TERRATEC_PRODIGY_XS:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
- case EM2860_BOARD_TERRATEC_HYBRID_XS:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- case EM2882_BOARD_PINNACLE_HYBRID_PRO:
- case EM2883_BOARD_KWORLD_HYBRID_A316:
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
- dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
-
- case EM2882_BOARD_TERRATEC_HYBRID_XS:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* should be added ir_codes here */
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
- dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = em2882_terratec_hybrid_xs_digital;
- break;
-
- case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- case EM2870_BOARD_TERRATEC_XS:
- case EM2881_BOARD_PINNACLE_HYBRID_PRO:
- case EM2880_BOARD_KWORLD_DVB_310U:
- case EM2870_BOARD_KWORLD_350U:
- case EM2881_BOARD_DNT_DA2_HYBRID:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital
- and analog commands. If this commands doesn't work,
- add this timer. */
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = default_analog;
- dev->digital_gpio = default_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
- case EM2880_BOARD_MSI_DIGIVOX_AD:
- case EM2880_BOARD_MSI_DIGIVOX_AD_II:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->analog_gpio = em2880_msi_digivox_ad_analog;
- dev->digital_gpio = em2880_msi_digivox_ad_digital;
- dev->tun_analog_gpio = default_callback;
- dev->tun_digital_gpio = default_callback;
- break;
+ /* Prepopulate cached GPO register content */
+ rc = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (rc >= 0)
+ dev->reg_gpo = rc;
- case EM2750_BOARD_UNKNOWN:
- case EM2750_BOARD_DLCW_130:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1);
- break;
+ /* Set the initial XCLK and I2C clock values based on the board
+ definition */
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ msleep(50);
+ /* request some modules */
+ switch (dev->model) {
case EM2861_BOARD_PLEXTOR_PX_TV100U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* FIXME guess */
/* Turn on analog audio output */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
break;
-
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x4c", 1);
- msleep(10);
- em28xx_write_regs(dev, 0x08, "\x6d", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
msleep(10);
- em28xx_write_regs(dev, 0x08, "\x7d", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
msleep(10);
break;
-
- case EM2870_BOARD_KWORLD_355U:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- msleep(50);
-
- /* Sets GPO/GPIO sequences for this device */
- dev->digital_gpio = em2870_kworld_355u_digital;
- break;
-
case EM2870_BOARD_COMPRO_VIDEOMATE:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* TODO: someone can do some cleanup here...
not everything's needed */
- em28xx_write_regs(dev, 0x04, "\x00", 1);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
msleep(10);
- em28xx_write_regs(dev, 0x04, "\x01", 1);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
msleep(10);
- em28xx_write_regs(dev, 0x08, "\xfd", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xdc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfc", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
mdelay(70);
break;
-
case EM2870_BOARD_TERRATEC_XS_MT2060:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* this device needs some gpio writes to get the DVB-T
demod work */
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xde", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
mdelay(70);
- dev->em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
break;
-
case EM2870_BOARD_PINNACLE_PCTV_DVB:
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
/* this device needs some gpio writes to get the
DVB-T demod work */
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xde", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
mdelay(70);
- em28xx_write_regs(dev, 0x08, "\xfe", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
mdelay(70);
- /* switch em2880 rc protocol */
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x22", 1);
- /* should be added ir_codes here */
break;
-
case EM2820_BOARD_GADMEI_UTV310:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* Turn on analog audio output */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
- break;
-
- case EM2860_BOARD_GADMEI_UTV330:
- /* Turn on IR */
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* should be added ir_codes here */
- break;
-
case EM2820_BOARD_MSI_VOX_USB_2:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- /* enables audio for that device */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1);
+ /* enables audio for that devices */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
break;
}
- em28xx_gpio_set(dev, dev->tun_analog_gpio);
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
/* Unlock device */
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
}
static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
@@ -1536,6 +1520,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
ctl->demod = XC3028_FE_DEFAULT;
ctl->fname = XC3028L_DEFAULT_FIRMWARE;
break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
/* FIXME: Better to specify the needed IF */
@@ -1712,12 +1697,15 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_set_model(dev);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+ if (em28xx_boards[dev->model].tuner_addr)
+ dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
/* request some modules */
switch (dev->model) {
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
{
struct tveeprom tv;
@@ -1733,7 +1721,7 @@ void em28xx_card_setup(struct em28xx *dev)
if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
dev->i2s_speed = 2048000;
- dev->has_msp34xx = 1;
+ dev->board.has_msp34xx = 1;
}
#ifdef CONFIG_MODULES
if (tv.has_ir)
@@ -1743,7 +1731,7 @@ void em28xx_card_setup(struct em28xx *dev)
}
case EM2820_BOARD_KWORLD_PVRTV2800RF:
/* GPIO enables sound on KWORLD PVR TV 2800RF */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
break;
case EM2820_BOARD_UNKNOWN:
case EM2800_BOARD_UNKNOWN:
@@ -1766,10 +1754,10 @@ void em28xx_card_setup(struct em28xx *dev)
break;
}
- if (dev->has_snapshot_button)
+ if (dev->board.has_snapshot_button)
em28xx_register_snapshot_button(dev);
- if (dev->valid == EM28XX_BOARD_NOT_VALIDATED) {
+ if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
em28xx_errdev("\n\n");
em28xx_errdev("The support for this board weren't "
"valid yet.\n");
@@ -1784,15 +1772,433 @@ void em28xx_card_setup(struct em28xx *dev)
#ifdef CONFIG_MODULES
/* request some modules */
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
request_module("msp3400");
- if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114)
+ if (dev->board.decoder == EM28XX_SAA711X)
request_module("saa7115");
- if (dev->decoder == EM28XX_TVP5150)
+ if (dev->board.decoder == EM28XX_TVP5150)
request_module("tvp5150");
- if (dev->tuner_type != TUNER_ABSENT)
+ if (dev->board.tuner_type != TUNER_ABSENT)
request_module("tuner");
#endif
em28xx_config_tuner(dev);
+
+ em28xx_ir_init(dev);
+}
+
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else if (dev->has_alsa_audio)
+ request_module("em28xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("em28xx-dvb");
+}
+
+static void request_modules(struct em28xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_release_resources(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev)
+ em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
+ /*FIXME: I2C IR should be disconnected */
+
+ em28xx_release_analog_resources(dev);
+
+ em28xx_remove_from_devlist(dev);
+
+ em28xx_i2c_unregister(dev);
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ em28xx_devused &= ~(1 << dev->devno);
+};
+
+/*
+ * em28xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
+ int minor)
+{
+ struct em28xx *dev = *devhandle;
+ int retval = -ENOMEM;
+ int errCode;
+
+ dev->udev = udev;
+ mutex_init(&dev->ctrl_urb_lock);
+ spin_lock_init(&dev->slock);
+ init_waitqueue_head(&dev->open);
+ init_waitqueue_head(&dev->wait_frame);
+ init_waitqueue_head(&dev->wait_stream);
+
+ dev->em28xx_write_regs = em28xx_write_regs;
+ dev->em28xx_read_reg = em28xx_read_reg;
+ dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+ dev->em28xx_write_regs_req = em28xx_write_regs_req;
+ dev->em28xx_read_reg_req = em28xx_read_reg_req;
+ dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+
+ em28xx_pre_card_setup(dev);
+
+ if (!dev->board.is_em2800) {
+ /* Sets I2C speed to 100 KHz */
+ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req failed!"
+ " retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ /* register i2c bus */
+ errCode = em28xx_i2c_register(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
+
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ errCode = em28xx_audio_setup(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n",
+ __func__, errCode);
+ }
+
+ /* wake i2c devices */
+ em28xx_wake_i2c(dev);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+
+
+ if (dev->board.has_msp34xx) {
+ /* Send a reset to other chips via gpio */
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(1) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+
+ errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - "
+ "msp34xx(2) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
+ msleep(3);
+ }
+
+ em28xx_add_into_devlist(dev);
+
+ retval = em28xx_register_analog_devices(dev);
+ if (retval < 0) {
+ em28xx_release_resources(dev);
+ goto fail_reg_devices;
+ }
+
+ em28xx_init_extension(dev);
+
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
+ return 0;
+
+fail_reg_devices:
+ return retval;
+}
+
+/*
+ * em28xx_usb_probe()
+ * checks for supported devices
+ */
+static int em28xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ const struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *udev;
+ struct usb_interface *uif;
+ struct em28xx *dev = NULL;
+ int retval = -ENODEV;
+ int i, nr, ifnum, isoc_pipe;
+ char *speed;
+ char descr[255] = "";
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+
+ /* Check to see next free device and mark as used */
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ em28xx_devused |= 1<<nr;
+
+ /* Don't register audio interfaces */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+ "interface %i, class %i\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+
+ /* check if the device has the iso in endpoint at the correct place */
+ if (usb_endpoint_xfer_isoc(endpoint)
+ &&
+ (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) {
+ /* It's a newer em2874/em2875 device */
+ isoc_pipe = 0;
+ } else {
+ int check_interface = 1;
+ isoc_pipe = 1;
+ endpoint = &interface->cur_altsetting->endpoint[1].desc;
+ if (usb_endpoint_type(endpoint) !=
+ USB_ENDPOINT_XFER_ISOC)
+ check_interface = 0;
+
+ if (usb_endpoint_dir_out(endpoint))
+ check_interface = 0;
+
+ if (!check_interface) {
+ em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
+ "interface %i, class %i found.\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ em28xx_err(DRIVER_NAME " This is an anciliary "
+ "interface not used by the driver\n");
+
+ em28xx_devused &= ~(1<<nr);
+ return -ENODEV;
+ }
+ }
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_UNKNOWN:
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ if (udev->manufacturer)
+ strlcpy(descr, udev->manufacturer, sizeof(descr));
+
+ if (udev->product) {
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+ strlcat(descr, udev->product, sizeof(descr));
+ }
+ if (*descr)
+ strlcat(descr, " ", sizeof(descr));
+
+ printk(DRIVER_NAME ": New device %s@ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ descr,
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting->desc.bInterfaceNumber);
+
+ if (nr >= EM28XX_MAXBOARDS) {
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ em28xx_err(DRIVER_NAME ": out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ return -ENOMEM;
+ }
+
+ snprintf(dev->name, 29, "em28xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->alt = -1;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ /* compute alternate max packet sizes */
+ uif = udev->actconfig->interface[0];
+
+ dev->num_alt = uif->num_altsetting;
+ dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
+
+ if (dev->alt_max_pkt_size == NULL) {
+ em28xx_errdev("out of memory!\n");
+ em28xx_devused &= ~(1<<nr);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < dev->num_alt ; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
+ dev->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ }
+
+ if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ dev->model = card[nr];
+
+ /* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ retval = em28xx_init_dev(&dev, udev, nr);
+ if (retval) {
+ em28xx_devused &= ~(1<<dev->devno);
+ kfree(dev);
+
+ return retval;
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ request_modules(dev);
+
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+/*
+ * em28xx_usb_disconnect()
+ * called when the device gets diconencted
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void em28xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ wake_up_interruptible_all(&dev->open);
+
+ if (dev->users) {
+ em28xx_warn
+ ("device /dev/video%d is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ dev->vdev->num);
+
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_uninit_isoc(dev);
+ dev->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_frame);
+ wake_up_interruptible(&dev->wait_stream);
+ } else {
+ dev->state |= DEV_DISCONNECTED;
+ em28xx_release_resources(dev);
+ }
+
+ em28xx_close_extension(dev);
+
+ mutex_unlock(&dev->lock);
+
+ if (!dev->users) {
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ }
}
+
+static struct usb_driver em28xx_usb_driver = {
+ .name = "em28xx",
+ .probe = em28xx_usb_probe,
+ .disconnect = em28xx_usb_disconnect,
+ .id_table = em28xx_id_table,
+};
+
+static int __init em28xx_module_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&em28xx_usb_driver);
+ if (result)
+ em28xx_err(DRIVER_NAME
+ " usb_register failed. Error number %d.\n", result);
+
+ printk(KERN_INFO DRIVER_NAME " driver loaded\n");
+
+ return result;
+}
+
+static void __exit em28xx_module_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&em28xx_usb_driver);
+}
+
+module_init(em28xx_module_init);
+module_exit(em28xx_module_exit);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 15e2b525310d..f8504518586a 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
#include "em28xx.h"
@@ -66,7 +67,8 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
char *buf, int len)
{
- int ret, byte;
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -74,10 +76,18 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
if (len > URB_MAX_CTRL_SIZE)
return -EINVAL;
- em28xx_regdbg("req=%02x, reg=%02x ", req, reg);
+ if (reg_debug) {
+ printk( KERN_DEBUG "(pipe 0x%08x): "
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+ }
mutex_lock(&dev->ctrl_urb_lock);
- ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req,
+ ret = usb_control_msg(dev->udev, pipe, req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, reg, dev->urb_buf, len, HZ);
if (ret < 0) {
@@ -93,7 +103,9 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
mutex_unlock(&dev->ctrl_urb_lock);
if (reg_debug) {
- printk("%02x values: ", ret);
+ int byte;
+
+ printk("<<<");
for (byte = 0; byte < len; byte++)
printk(" %02x", (unsigned char)buf[byte]);
printk("\n");
@@ -108,28 +120,12 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
*/
int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
{
- u8 val;
int ret;
+ u8 val;
- if (dev->state & DEV_DISCONNECTED)
- return(-ENODEV);
-
- em28xx_regdbg("req=%02x, reg=%02x:", req, reg);
-
- mutex_lock(&dev->ctrl_urb_lock);
- ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0000, reg, dev->urb_buf, 1, HZ);
- val = dev->urb_buf[0];
- mutex_unlock(&dev->ctrl_urb_lock);
-
- if (ret < 0) {
- printk(" failed!\n");
+ ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
+ if (ret < 0)
return ret;
- }
-
- if (reg_debug)
- printk("%02x\n", (unsigned char) val);
return val;
}
@@ -147,6 +143,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len)
{
int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -154,17 +151,25 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
return -EINVAL;
- em28xx_regdbg("req=%02x reg=%02x:", req, reg);
if (reg_debug) {
- int i;
- for (i = 0; i < len; ++i)
- printk(" %02x", (unsigned char)buf[i]);
+ int byte;
+
+ printk( KERN_DEBUG "(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
printk("\n");
}
mutex_lock(&dev->ctrl_urb_lock);
memcpy(dev->urb_buf, buf, len);
- ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req,
+ ret = usb_control_msg(dev->udev, pipe, req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, reg, dev->urb_buf, len, HZ);
mutex_unlock(&dev->ctrl_urb_lock);
@@ -187,15 +192,21 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
Not sure what happens on reading GPO register.
*/
if (rc >= 0) {
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
dev->reg_gpo = buf[0];
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
dev->reg_gpio = buf[0];
}
return rc;
}
+/* Write a single register */
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
+{
+ return em28xx_write_regs(dev, reg, &val, 1);
+}
+
/*
* em28xx_write_reg_bits()
* sets only some bits (specified by bitmask) of a register, by first reading
@@ -208,9 +219,9 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
u8 newval;
/* Uses cache for gpo/gpio registers */
- if (reg == EM2880_R04_GPO)
+ if (reg == dev->reg_gpo_num)
oldval = dev->reg_gpo;
- else if (reg == EM28XX_R08_GPIO)
+ else if (reg == dev->reg_gpio_num)
oldval = dev->reg_gpio;
else
oldval = em28xx_read_reg(dev, reg);
@@ -224,15 +235,70 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
}
/*
+ * em28xx_is_ac97_ready()
+ * Checks if ac97 is ready
+ */
+static int em28xx_is_ac97_ready(struct em28xx *dev)
+{
+ int ret, i;
+
+ /* Wait up to 50 ms for AC97 command to complete */
+ for (i = 0; i < 10; i++, msleep(5)) {
+ ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & 0x01))
+ return 0;
+ }
+
+ em28xx_warn("AC97 command still being executed: not handled properly!\n");
+ return -EBUSY;
+}
+
+/*
+ * em28xx_read_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_read_ac97(struct em28xx *dev, u8 reg)
+{
+ int ret;
+ u8 addr = (reg & 0x7f) | 0x80;
+ u16 val;
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
+ (u8 *)&val, sizeof(val));
+
+ if (ret < 0)
+ return ret;
+ return le16_to_cpu(val);
+}
+
+/*
* em28xx_write_ac97()
* write a 16 bit value to the specified AC97 address (LSB first!)
*/
-static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
{
- int ret, i;
+ int ret;
u8 addr = reg & 0x7f;
+ __le16 value;
- ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2);
+ value = cpu_to_le16(val);
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
if (ret < 0)
return ret;
@@ -240,58 +306,74 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val)
if (ret < 0)
return ret;
- /* Wait up to 50 ms for AC97 command to complete */
- for (i = 0; i < 10; i++) {
- ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
- if (ret < 0)
- return ret;
+ return 0;
+}
- if (!(ret & 0x01))
- return 0;
- msleep(5);
+struct em28xx_vol_table {
+ enum em28xx_amux mux;
+ u8 reg;
+};
+
+static struct em28xx_vol_table inputs[] = {
+ { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL },
+ { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL },
+ { EM28XX_AMUX_PHONE, AC97_PHONE_VOL },
+ { EM28XX_AMUX_MIC, AC97_MIC_VOL },
+ { EM28XX_AMUX_CD, AC97_CD_VOL },
+ { EM28XX_AMUX_AUX, AC97_AUX_VOL },
+ { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL },
+};
+
+static int set_ac97_input(struct em28xx *dev)
+{
+ int ret, i;
+ enum em28xx_amux amux = dev->ctl_ainput;
+
+ /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+ em28xx should point to LINE IN, while AC97 should use VIDEO
+ */
+ if (amux == EM28XX_AMUX_VIDEO2)
+ amux = EM28XX_AMUX_VIDEO;
+
+ /* Mute all entres but the one that were selected */
+ for (i = 0; i < ARRAY_SIZE(inputs); i++) {
+ if (amux == inputs[i].mux)
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
+ else
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
+
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ inputs[i].reg);
}
- em28xx_warn("AC97 command still being executed: not handled properly!\n");
return 0;
}
static int em28xx_set_audio_source(struct em28xx *dev)
{
- static char *enable = "\x08\x08";
- static char *disable = "\x08\x88";
- char *video = enable, *line = disable;
int ret;
u8 input;
- if (dev->is_em2800) {
- if (dev->ctl_ainput)
- input = EM2800_AUDIO_SRC_LINE;
- else
+ if (dev->board.is_em2800) {
+ if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
input = EM2800_AUDIO_SRC_TUNER;
+ else
+ input = EM2800_AUDIO_SRC_LINE;
ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
if (ret < 0)
return ret;
}
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
input = EM28XX_AUDIO_SRC_TUNER;
else {
switch (dev->ctl_ainput) {
case EM28XX_AMUX_VIDEO:
input = EM28XX_AUDIO_SRC_TUNER;
break;
- case EM28XX_AMUX_LINE_IN:
+ default:
input = EM28XX_AUDIO_SRC_LINE;
- video = disable;
- line = enable;
- break;
- case EM28XX_AMUX_AC97_VIDEO:
- input = EM28XX_AUDIO_SRC_LINE;
- break;
- case EM28XX_AMUX_AC97_LINE_IN:
- input = EM28XX_AUDIO_SRC_LINE;
- video = disable;
- line = enable;
break;
}
}
@@ -301,41 +383,50 @@ static int em28xx_set_audio_source(struct em28xx *dev)
return ret;
msleep(5);
- /* Sets AC97 mixer registers
- This is seems to be needed, even for non-ac97 configs
- */
- ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line);
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ break;
+ default:
+ ret = set_ac97_input(dev);
+ }
return ret;
}
+struct em28xx_vol_table outputs[] = {
+ { EM28XX_AOUT_MASTER, AC97_MASTER_VOL },
+ { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL },
+ { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL },
+ { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL },
+ { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL },
+};
+
int em28xx_audio_analog_set(struct em28xx *dev)
{
- int ret;
- char s[2] = { 0x00, 0x00 };
- u8 xclk = 0x07;
-
- s[0] |= 0x1f - dev->volume;
- s[1] |= 0x1f - dev->volume;
-
- /* Mute */
- s[1] |= 0x80;
- ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s);
+ int ret, i;
+ u8 xclk;
- if (ret < 0)
- return ret;
+ if (!dev->audio_mode.has_audio)
+ return 0;
- if (dev->has_12mhz_i2s)
- xclk |= 0x20;
+ /* It is assumed that all devices use master volume for output.
+ It would be possible to use also line output.
+ */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ /* Mute all outputs */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
+ xclk = dev->board.xclk & 0x7f;
if (!dev->mute)
xclk |= 0x80;
- ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7);
+ ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
if (ret < 0)
return ret;
msleep(10);
@@ -343,36 +434,169 @@ int em28xx_audio_analog_set(struct em28xx *dev)
/* Selects the proper audio input */
ret = em28xx_set_audio_source(dev);
- /* Unmute device */
- if (!dev->mute)
- s[1] &= ~0x80;
- ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s);
+ /* Sets volume */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ int vol;
+
+ /* LSB: left channel - both channels with the same level */
+ vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
+
+ /* Mute device, if needed */
+ if (dev->mute)
+ vol |= 0x8000;
+
+ /* Sets volume */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ if (dev->ctl_aoutput & outputs[i].mux)
+ ret = em28xx_write_ac97(dev, outputs[i].reg,
+ vol);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
return ret;
}
EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
-int em28xx_colorlevels_set_default(struct em28xx *dev)
+int em28xx_audio_setup(struct em28xx *dev)
{
- em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */
- em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */
- em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */
- em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1);
+ int vid1, vid2, feat, cfg;
+ u32 vid;
+
+ if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874) {
+ /* Digital only device - don't load any alsa module */
+ dev->audio_mode.has_audio = 0;
+ dev->has_audio_class = 0;
+ dev->has_alsa_audio = 0;
+ return 0;
+ }
+
+ /* If device doesn't support Usb Audio Class, use vendor class */
+ if (!dev->has_audio_class)
+ dev->has_alsa_audio = 1;
+
+ dev->audio_mode.has_audio = 1;
- em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1);
- em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1);
- em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1);
- return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1);
+ /* See how this device is configured */
+ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
+ if (cfg < 0)
+ cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
+ else
+ em28xx_info("Config register raw data: 0x%02x\n", cfg);
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
+ em28xx_info("I2S Audio (3 sample rates)\n");
+ dev->audio_mode.i2s_3rates = 1;
+ }
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_5_SAMPRATES) {
+ em28xx_info("I2S Audio (5 sample rates)\n");
+ dev->audio_mode.i2s_5rates = 1;
+ }
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
+ /* Skip the code that does AC97 vendor detection */
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ goto init_audio;
+ }
+
+ dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
+
+ vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
+ if (vid1 < 0) {
+ /* Device likely doesn't support AC97 */
+ em28xx_warn("AC97 chip type couldn't be determined\n");
+ goto init_audio;
+ }
+
+ vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
+ if (vid2 < 0)
+ goto init_audio;
+
+ vid = vid1 << 16 | vid2;
+
+ dev->audio_mode.ac97_vendor_id = vid;
+ em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
+
+ feat = em28xx_read_ac97(dev, AC97_RESET);
+ if (feat < 0)
+ goto init_audio;
+
+ dev->audio_mode.ac97_feat = feat;
+ em28xx_warn("AC97 features = 0x%04x\n", feat);
+
+ /* Try to identify what audio processor we have */
+ if ((vid == 0xffffffff) && (feat == 0x6a90))
+ dev->audio_mode.ac97 = EM28XX_AC97_EM202;
+ else if ((vid >> 8) == 0x838476)
+ dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
+
+init_audio:
+ /* Reports detected AC97 processor */
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ em28xx_info("No AC97 audio processor\n");
+ break;
+ case EM28XX_AC97_EM202:
+ em28xx_info("Empia 202 AC97 audio processor detected\n");
+ break;
+ case EM28XX_AC97_SIGMATEL:
+ em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
+ dev->audio_mode.ac97_vendor_id & 0xff);
+ break;
+ case EM28XX_AC97_OTHER:
+ em28xx_warn("Unknown AC97 audio processor detected!\n");
+ break;
+ default:
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_setup);
+
+int em28xx_colorlevels_set_default(struct em28xx *dev)
+{
+ em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */
+ em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */
+ em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */
+ em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00);
+
+ em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
+ em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
+ return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
}
int em28xx_capture_start(struct em28xx *dev, int start)
{
int rc;
+
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (!start) {
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+ /* Enable Transport Stream */
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
@@ -382,28 +606,37 @@ int em28xx_capture_start(struct em28xx *dev, int start)
if (!start) {
/* disable video capture */
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
return rc;
}
/* enable video capture */
- rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
if (dev->mode == EM28XX_ANALOG_MODE)
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
else
- rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1);
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
msleep(6);
return rc;
}
-int em28xx_outfmt_set_yuv422(struct em28xx *dev)
+int em28xx_set_outfmt(struct em28xx *dev)
{
- em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1);
- em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1);
- return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1);
+ int ret;
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
+ dev->format->reg | 0x20, 0x3f);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10);
+ if (ret < 0)
+ return ret;
+
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -440,7 +673,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
{
u8 mode;
/* the em2800 scaler only supports scaling down to 50% */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
else {
u8 buf[2];
@@ -464,7 +697,7 @@ int em28xx_resolution_set(struct em28xx *dev)
width = norm_maxw(dev);
height = norm_maxh(dev) >> 1;
- em28xx_outfmt_set_yuv422(dev);
+ em28xx_set_outfmt(dev);
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
@@ -519,12 +752,14 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
if (!gpio)
return rc;
- dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
- if (dev->mode == EM28XX_ANALOG_MODE)
- dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
- else
- dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
- msleep(6);
+ if (dev->mode != EM28XX_SUSPEND) {
+ em28xx_write_reg(dev, 0x48, 0x00);
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+ msleep(6);
+ }
/* Send GPIO reset sequences specified at board entry */
while (gpio->sleep >= 0) {
@@ -549,17 +784,20 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
if (dev->mode == set_mode)
return 0;
- if (set_mode == EM28XX_MODE_UNDEFINED) {
+ if (set_mode == EM28XX_SUSPEND) {
dev->mode = set_mode;
- return 0;
+
+ /* FIXME: add suspend support for ac97 */
+
+ return em28xx_gpio_set(dev, dev->board.suspend_gpio);
}
dev->mode = set_mode;
if (dev->mode == EM28XX_DIGITAL_MODE)
- return em28xx_gpio_set(dev, dev->digital_gpio);
+ return em28xx_gpio_set(dev, dev->board.dvb_gpio);
else
- return em28xx_gpio_set(dev, dev->analog_gpio);
+ return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
}
EXPORT_SYMBOL_GPL(em28xx_set_mode);
@@ -738,3 +976,145 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
return 0;
}
EXPORT_SYMBOL_GPL(em28xx_init_isoc);
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+void em28xx_wake_i2c(struct em28xx *dev)
+{
+ struct v4l2_routing route;
+ int zero = 0;
+
+ route.input = INPUT(dev->ctl_input)->vmux;
+ route.output = 0;
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero);
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
+}
+
+/*
+ * Device control list
+ */
+
+static LIST_HEAD(em28xx_devlist);
+static DEFINE_MUTEX(em28xx_devlist_mutex);
+
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio)
+{
+ struct em28xx *h, *dev = NULL;
+ int minor = iminor(inode);
+
+ *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ *has_radio = 0;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(h, &em28xx_devlist, devlist) {
+ if (h->vdev->minor == minor)
+ dev = h;
+ if (h->vbi_dev->minor == minor) {
+ dev = h;
+ *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ dev = h;
+ *has_radio = 1;
+ }
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+
+ return dev;
+}
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_remove_from_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+void em28xx_add_into_devlist(struct em28xx *dev)
+{
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+};
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(em28xx_extension_devlist);
+static DEFINE_MUTEX(em28xx_extension_devlist_lock);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ mutex_lock(&em28xx_extension_devlist_lock);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->init(dev);
+ }
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->fini(dev);
+ }
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_extension_devlist_lock);
+ mutex_unlock(&em28xx_devlist_mutex);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+void em28xx_init_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
+
+void em28xx_close_extension(struct em28xx *dev)
+{
+ struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_extension_devlist_lock);
+ if (!list_empty(&em28xx_extension_devlist)) {
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ }
+ mutex_unlock(&em28xx_extension_devlist_lock);
+}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index c99e2383b7ec..d38cb21834d9 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -161,7 +161,7 @@ static int stop_streaming(struct em28xx_dvb *dvb)
em28xx_uninit_isoc(dev);
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
return 0;
}
@@ -215,7 +215,7 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
if (acquire)
return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
else
- return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ return em28xx_set_mode(dev, EM28XX_SUSPEND);
}
/* ------------------------------------------------------------------ */
@@ -393,7 +393,7 @@ static int dvb_init(struct em28xx *dev)
int result = 0;
struct em28xx_dvb *dvb;
- if (!dev->has_dvb) {
+ if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
@@ -409,8 +409,10 @@ static int dvb_init(struct em28xx *dev)
em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
/* init frontend */
switch (dev->model) {
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ case EM2883_BOARD_KWORLD_HYBRID_A316:
case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
dvb->frontend = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
@@ -466,12 +468,12 @@ static int dvb_init(struct em28xx *dev)
if (result < 0)
goto out_free;
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
return 0;
out_free:
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
kfree(dvb);
dev->dvb = NULL;
return result;
@@ -479,7 +481,7 @@ out_free:
static int dvb_fini(struct em28xx *dev)
{
- if (!dev->has_dvb) {
+ if (!dev->board.has_dvb) {
/* This device does not support the extension */
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 2360c61ddca9..d69f0efcc9aa 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -250,7 +250,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
if (!msgs[i].len) { /* no len: check only for device presence */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_check_for_device(dev, addr);
else
rc = em28xx_i2c_check_for_device(dev, addr);
@@ -261,7 +261,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
} else if (msgs[i].flags & I2C_M_RD) {
/* read bytes */
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_recv_bytes(dev, addr,
msgs[i].buf,
msgs[i].len);
@@ -279,7 +279,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
for (byte = 0; byte < msgs[i].len; byte++)
printk(" %02x", msgs[i].buf[byte]);
}
- if (dev->is_em2800)
+ if (dev->board.is_em2800)
rc = em2800_i2c_send_bytes(dev, addr,
msgs[i].buf,
msgs[i].len);
@@ -332,6 +332,17 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
struct em28xx_eeprom *em_eeprom = (void *)eedata;
int i, err, size = len, block;
+ if (dev->chip_id == CHIP_ID_EM2874) {
+ /* Empia switched to a 16-bit addressable eeprom in newer
+ devices. While we could certainly write a routine to read
+ the eeprom, there is nothing of use in there that cannot be
+ accessed through registers, and there is the risk that we
+ could corrupt the eeprom (since a 16-bit read call is
+ interpreted as a write call by 8-bit eeproms).
+ */
+ return 0;
+ }
+
dev->i2c_client.addr = 0xa0 >> 1;
/* Check if board has eeprom */
@@ -377,47 +388,49 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
if (em_eeprom->id == 0x9567eb1a)
dev->hash = em28xx_hash_mem(eedata, len, 32);
- printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n",
- em_eeprom->id, dev->hash);
- printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID,
- em_eeprom->product_ID);
+ printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n",
+ dev->name, em_eeprom->id, dev->hash);
+
+ printk(KERN_INFO "%s: EEPROM info:\n", dev->name);
switch (em_eeprom->chip_conf >> 4 & 0x3) {
case 0:
- printk(KERN_INFO "No audio on board.\n");
+ printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name);
break;
case 1:
- printk(KERN_INFO "AC97 audio (5 sample rates)\n");
+ printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n",
+ dev->name);
break;
case 2:
- printk(KERN_INFO "I2S audio, sample rate=32k\n");
+ printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name);
break;
case 3:
- printk(KERN_INFO "I2S audio, 3 sample rates\n");
+ printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name);
break;
}
if (em_eeprom->chip_conf & 1 << 3)
- printk(KERN_INFO "USB Remote wakeup capable\n");
+ printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name);
if (em_eeprom->chip_conf & 1 << 2)
- printk(KERN_INFO "USB Self power capable\n");
+ printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name);
switch (em_eeprom->chip_conf & 0x3) {
case 0:
- printk(KERN_INFO "500mA max power\n");
+ printk(KERN_INFO "%s:\t500mA max power\n", dev->name);
break;
case 1:
- printk(KERN_INFO "400mA max power\n");
+ printk(KERN_INFO "%s:\t400mA max power\n", dev->name);
break;
case 2:
- printk(KERN_INFO "300mA max power\n");
+ printk(KERN_INFO "%s:\t300mA max power\n", dev->name);
break;
case 3:
- printk(KERN_INFO "200mA max power\n");
+ printk(KERN_INFO "%s:\t200mA max power\n", dev->name);
break;
}
- printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
+ printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n",
+ dev->name,
em_eeprom->string_idx_table,
em_eeprom->string1,
em_eeprom->string2,
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index eab3d9511af3..42bbaf64aceb 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -38,12 +38,48 @@ static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
-#define dprintk(fmt, arg...) \
+#define i2cdprintk(fmt, arg...) \
if (ir_debug) { \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
}
-/* ----------------------------------------------------------------------- */
+#define dprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_ir_poll_result {
+ unsigned int toggle_bit:1;
+ unsigned int read_count:7;
+ u8 rc_address;
+ u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */
+};
+
+struct em28xx_IR {
+ struct em28xx *dev;
+ struct input_dev *input;
+ struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct work_struct work;
+ struct timer_list timer;
+ unsigned int last_toggle:1;
+ unsigned int last_readcount;
+ unsigned int repeat_interval;
+
+ int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
@@ -51,7 +87,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
/* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
@@ -59,7 +95,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
down, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */
- dprintk("key %02x\n", b);
+ i2cdprintk("key %02x\n", b);
if (b == 0xff)
return 0;
@@ -73,7 +109,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
@@ -97,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
((buf[0]&0x10)>>3) | /* 0000 0010 */
((buf[0]&0x20)>>5); /* 0000 0001 */
- dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
code, buf[0]);
/* return key */
@@ -114,11 +149,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
/* poll IR chip */
if (3 != i2c_master_recv(&ir->c, buf, 3)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
- dprintk("key %02x\n", buf[2]&0x3f);
+ i2cdprintk("key %02x\n", buf[2]&0x3f);
if (buf[0] != 0x00)
return 0;
@@ -128,6 +163,260 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+/* This is for the em2860/em2880 */
+static int default_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[3] = { 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x45, 0x46 and 0x47
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x45[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x45[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x46) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x47) */
+ poll_result->rc_data[0] = msg[2];
+
+ return 0;
+}
+
+static int em2874_polling_getkey(struct em28xx_IR *ir,
+ struct em28xx_ir_poll_result *poll_result)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u8 msg[5] = { 0, 0, 0, 0, 0 };
+
+ /* Read key toggle, brand, and key code
+ on registers 0x51-55
+ */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
+ msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ /* Infrared toggle (Reg 0x51[7]) */
+ poll_result->toggle_bit = (msg[0] >> 7);
+
+ /* Infrared read count (Reg 0x51[6:0] */
+ poll_result->read_count = (msg[0] & 0x7f);
+
+ /* Remote Control Address (Reg 0x52) */
+ poll_result->rc_address = msg[1];
+
+ /* Remote Control Data (Reg 0x53-55) */
+ poll_result->rc_data[0] = msg[2];
+ poll_result->rc_data[1] = msg[3];
+ poll_result->rc_data[2] = msg[4];
+
+ return 0;
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+ int result;
+ int do_sendkey = 0;
+ struct em28xx_ir_poll_result poll_result;
+
+ /* read the registers containing the IR status */
+ result = ir->get_key(ir, &poll_result);
+ if (result < 0) {
+ dprintk("ir->get_key() failed %d\n", result);
+ return;
+ }
+
+ dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
+ poll_result.toggle_bit, poll_result.read_count,
+ ir->last_readcount, poll_result.rc_data[0]);
+
+ if (ir->dev->chip_id == CHIP_ID_EM2874) {
+ /* The em2874 clears the readcount field every time the
+ register is read. The em2860/2880 datasheet says that it
+ is supposed to clear the readcount, but it doesn't. So with
+ the em2874, we are looking for a non-zero read count as
+ opposed to a readcount that is incrementing */
+ ir->last_readcount = 0;
+ }
+
+ if (poll_result.read_count == 0) {
+ /* The button has not been pressed since the last read */
+ } else if (ir->last_toggle != poll_result.toggle_bit) {
+ /* A button has been pressed */
+ dprintk("button has been pressed\n");
+ ir->last_toggle = poll_result.toggle_bit;
+ ir->repeat_interval = 0;
+ do_sendkey = 1;
+ } else if (poll_result.toggle_bit == ir->last_toggle &&
+ poll_result.read_count > 0 &&
+ poll_result.read_count != ir->last_readcount) {
+ /* The button is still being held down */
+ dprintk("button being held down\n");
+
+ /* Debouncer for first keypress */
+ if (ir->repeat_interval++ > 9) {
+ /* Start repeating after 1 second */
+ do_sendkey = 1;
+ }
+ }
+
+ if (do_sendkey) {
+ dprintk("sending keypress\n");
+ ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
+ poll_result.rc_data[0]);
+ ir_input_nokey(ir->input, &ir->ir);
+ }
+
+ ir->last_readcount = poll_result.read_count;
+ return;
+}
+
+static void ir_timer(unsigned long data)
+{
+ struct em28xx_IR *ir = (struct em28xx_IR *)data;
+
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_work(struct work_struct *work)
+{
+ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+
+ em28xx_ir_handle_key(ir);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+void em28xx_ir_start(struct em28xx_IR *ir)
+{
+ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+ INIT_WORK(&ir->work, em28xx_ir_work);
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_stop(struct em28xx_IR *ir)
+{
+ del_timer_sync(&ir->timer);
+ flush_scheduled_work();
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct input_dev *input_dev;
+ u8 ir_config;
+ int err = -ENOMEM;
+
+ if (dev->board.ir_codes == NULL) {
+ /* No remote control support */
+ return 0;
+ }
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev)
+ goto err_out_free;
+
+ ir->input = input_dev;
+
+ /* Setup the proper handler based on the chip */
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2860:
+ case CHIP_ID_EM2883:
+ ir->get_key = default_polling_getkey;
+ break;
+ case CHIP_ID_EM2874:
+ ir->get_key = em2874_polling_getkey;
+ /* For now we only support RC5, so enable it */
+ ir_config = EM2874_IR_RC5;
+ em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
+ break;
+ default:
+ printk("Unrecognized em28xx chip id: IR not supported\n");
+ goto err_out_free;
+ }
+
+ /* This is how often we ask the chip for IR information */
+ ir->polling = 100; /* ms */
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.version = 1;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+ input_dev->dev.parent = &dev->udev->dev;
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+
+ em28xx_ir_start(ir);
+
+ /* all done */
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+ err_out_stop:
+ em28xx_ir_stop(ir);
+ dev->ir = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ /* skip detach on non attached boards */
+ if (!ir)
+ return 0;
+
+ em28xx_ir_stop(ir);
+ input_unregister_device(ir->input);
+ kfree(ir);
+
+ /* done */
+ dev->ir = NULL;
+ return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
static void em28xx_query_sbutton(struct work_struct *work)
{
/* Poll the register and see if the button is depressed */
@@ -210,9 +499,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
}
return;
}
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index fac1ab23f621..65dcb91bdcc2 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -17,17 +17,58 @@
/* em28xx registers */
+#define EM28XX_R00_CHIPCFG 0x00
+
+/* em28xx Chip Configuration 0x00 */
+#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80
+#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40
+#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x30
+#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x20
+#define EM28XX_CHIPCFG_AC97 0x10
+#define EM28XX_CHIPCFG_AUDIOMASK 0x30
+
/* GPIO/GPO registers */
#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */
#define EM28XX_R06_I2C_CLK 0x06
+
+/* em28xx I2C Clock Register (0x06) */
+#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80
+#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40
+#define EM28XX_I2C_EEPROM_ON_BOARD 0x08
+#define EM28XX_I2C_EEPROM_KEY_VALID 0x04
+#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */
+#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */
+#define EM28XX_I2C_FREQ_25_KHZ 0x02
+#define EM28XX_I2C_FREQ_400_KHZ 0x01
+#define EM28XX_I2C_FREQ_100_KHZ 0x00
+
+
#define EM28XX_R0A_CHIPID 0x0a
#define EM28XX_R0C_USBSUSP 0x0c /* */
#define EM28XX_R0E_AUDIOSRC 0x0e
#define EM28XX_R0F_XCLK 0x0f
+/* em28xx XCLK Register (0x0f) */
+#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */
+#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */
+#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */
+#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10
+#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */
+#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01
+#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02
+#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03
+#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04
+#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05
+#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06
+#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07
+#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08
+#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09
+#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a
+#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
+
#define EM28XX_R10_VINMODE 0x10
#define EM28XX_R11_VINCTRL 0x11
#define EM28XX_R12_VINENABLE 0x12 /* */
@@ -56,6 +97,19 @@
#define EM28XX_R26_COMPR 0x26
#define EM28XX_R27_OUTFMT 0x27
+/* em28xx Output Format Register (0x27) */
+#define EM28XX_OUTFMT_RGB_8_RGRG 0x00
+#define EM28XX_OUTFMT_RGB_8_GRGR 0x01
+#define EM28XX_OUTFMT_RGB_8_GBGB 0x02
+#define EM28XX_OUTFMT_RGB_8_BGBG 0x03
+#define EM28XX_OUTFMT_RGB_16_656 0x04
+#define EM28XX_OUTFMT_RGB_8_BAYER 0x08 /* Pattern in Reg 0x10[1-0] */
+#define EM28XX_OUTFMT_YUV211 0x10
+#define EM28XX_OUTFMT_YUV422_Y0UY1V 0x14
+#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15
+#define EM28XX_OUTFMT_YUV411 0x18
+
+
#define EM28XX_R28_XMIN 0x28
#define EM28XX_R29_XMAX 0x29
#define EM28XX_R2A_YMIN 0x2a
@@ -71,10 +125,32 @@
#define EM28XX_R42_AC97ADDR 0x42
#define EM28XX_R43_AC97BUSY 0x43
-/* em202 registers */
-#define EM28XX_R02_MASTER_AC97 0x02
-#define EM28XX_R10_LINE_IN_AC97 0x10
-#define EM28XX_R14_VIDEO_AC97 0x14
+#define EM28XX_R45_IR 0x45
+ /* 0x45 bit 7 - parity bit
+ bits 6-0 - count
+ 0x46 IR brand
+ 0x47 IR data
+ */
+
+/* em2874 registers */
+#define EM2874_R50_IR_CONFIG 0x50
+#define EM2874_R51_IR 0x51
+#define EM2874_R5F_TS_ENABLE 0x5f
+#define EM2874_R80_GPIO 0x80
+
+/* em2874 IR config register (0x50) */
+#define EM2874_IR_NEC 0x00
+#define EM2874_IR_RC5 0x04
+#define EM2874_IR_RC5_MODE_0 0x08
+#define EM2874_IR_RC5_MODE_6A 0x0b
+
+/* em2874 Transport Stream Enable Register (0x5f) */
+#define EM2874_TS1_CAPTURE_ENABLE (1 << 0)
+#define EM2874_TS1_FILTER_ENABLE (1 << 1)
+#define EM2874_TS1_NULL_DISCARD (1 << 2)
+#define EM2874_TS2_CAPTURE_ENABLE (1 << 4)
+#define EM2874_TS2_FILTER_ENABLE (1 << 5)
+#define EM2874_TS2_NULL_DISCARD (1 << 6)
/* register settings */
#define EM2800_AUDIO_SRC_TUNER 0x0d
@@ -84,6 +160,75 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
+ CHIP_ID_EM2820 = 18,
+ CHIP_ID_EM2840 = 20,
+ CHIP_ID_EM2750 = 33,
CHIP_ID_EM2860 = 34,
+ CHIP_ID_EM2870 = 35,
CHIP_ID_EM2883 = 36,
+ CHIP_ID_EM2874 = 65,
};
+
+/*
+ * Registers used by em202 and other AC97 chips
+ */
+
+/* Standard AC97 registers */
+#define AC97_RESET 0x00
+
+ /* Output volumes */
+#define AC97_MASTER_VOL 0x02
+#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */
+#define AC97_MASTER_MONO_VOL 0x06
+
+ /* Input volumes */
+#define AC97_PC_BEEP_VOL 0x0a
+#define AC97_PHONE_VOL 0x0c
+#define AC97_MIC_VOL 0x0e
+#define AC97_LINEIN_VOL 0x10
+#define AC97_CD_VOL 0x12
+#define AC97_VIDEO_VOL 0x14
+#define AC97_AUX_VOL 0x16
+#define AC97_PCM_OUT_VOL 0x18
+
+ /* capture registers */
+#define AC97_RECORD_SELECT 0x1a
+#define AC97_RECORD_GAIN 0x1c
+
+ /* control registers */
+#define AC97_GENERAL_PURPOSE 0x20
+#define AC97_3D_CTRL 0x22
+#define AC97_AUD_INT_AND_PAG 0x24
+#define AC97_POWER_DOWN_CTRL 0x26
+#define AC97_EXT_AUD_ID 0x28
+#define AC97_EXT_AUD_CTRL 0x2a
+
+/* Supported rate varies for each AC97 device
+ if write an unsupported value, it will return the closest one
+ */
+#define AC97_PCM_OUT_FRONT_SRATE 0x2c
+#define AC97_PCM_OUT_SURR_SRATE 0x2e
+#define AC97_PCM_OUT_LFE_SRATE 0x30
+#define AC97_PCM_IN_SRATE 0x32
+
+ /* For devices with more than 2 channels, extra output volumes */
+#define AC97_LFE_MASTER_VOL 0x36
+#define AC97_SURR_MASTER_VOL 0x38
+
+ /* Digital SPDIF output control */
+#define AC97_SPDIF_OUT_CTRL 0x3a
+
+ /* Vendor ID identifier */
+#define AC97_VENDOR_ID1 0x7c
+#define AC97_VENDOR_ID2 0x7e
+
+/* EMP202 vendor registers */
+#define EM202_EXT_MODEM_CTRL 0x3e
+#define EM202_GPIO_CONF 0x4c
+#define EM202_GPIO_POLARITY 0x4e
+#define EM202_GPIO_STICKY 0x50
+#define EM202_GPIO_MASK 0x52
+#define EM202_GPIO_STATUS 0x54
+#define EM202_SPDIF_OUT_SEL 0x6a
+#define EM202_ANTIPOP 0x72
+#define EM202_EAPD_GPIO_ACCESS 0x74
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 610f535a257c..53527536481e 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -39,6 +39,7 @@
#include "em28xx.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
#include <media/msp3400.h>
#include <media/tuner.h>
@@ -47,9 +48,8 @@
"Mauro Carvalho Chehab <mchehab@infradead.org>, " \
"Sascha Sommer <saschasommer@freenet.de>"
-#define DRIVER_NAME "em28xx"
#define DRIVER_DESC "Empia em28xx based USB video device driver"
-#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
+#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1)
#define em28xx_videodbg(fmt, arg...) do {\
if (video_debug) \
@@ -72,19 +72,13 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-static LIST_HEAD(em28xx_devlist);
-static DEFINE_MUTEX(em28xx_devlist_mutex);
-
-static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-module_param_array(card, int, NULL, 0444);
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);
-MODULE_PARM_DESC(card, "card type");
MODULE_PARM_DESC(video_nr, "video device numbers");
MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
MODULE_PARM_DESC(radio_nr, "radio device numbers");
@@ -93,8 +87,15 @@ static unsigned int video_debug;
module_param(video_debug, int, 0644);
MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
-static unsigned long em28xx_devused;
+/* supported video standards */
+static struct em28xx_fmt format[] = {
+ {
+ .name = "16bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ },
+};
/* supported controls */
/* Common to all boards */
@@ -120,8 +121,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
}
};
-static struct usb_driver em28xx_usb_driver;
-
/* ------------------------------------------------------------------
DMA and thread functions
------------------------------------------------------------------*/
@@ -386,16 +385,18 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
struct em28xx *dev = fh->dev;
struct v4l2_frequency f;
- *size = 16 * fh->dev->width * fh->dev->height >> 3;
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+
if (0 == *count)
*count = EM28XX_DEF_BUF;
if (*count < EM28XX_MIN_BUF)
*count = EM28XX_MIN_BUF;
- /* Ask tuner to go to analog mode */
+ /* Ask tuner to go to analog or radio mode */
memset(&f, 0, sizeof(f));
f.frequency = dev->ctl_freq;
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
@@ -438,9 +439,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx *dev = fh->dev;
int rc = 0, urb_init = 0;
- /* FIXME: It assumes depth = 16 */
- /* The only currently supported format is 16 bits/pixel */
- buf->vb.size = 16 * dev->width * dev->height >> 3;
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
@@ -508,55 +507,6 @@ static struct videobuf_queue_ops em28xx_video_qops = {
/********************* v4l2 interface **************************************/
-/*
- * em28xx_config()
- * inits registers with sane defaults
- */
-static int em28xx_config(struct em28xx *dev)
-{
- int retval;
-
- /* Sets I2C speed to 100 KHz */
- if (!dev->is_em2800) {
- retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
- /* enable vbi capturing */
-
-/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */
-/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */
- em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1);
-
- dev->mute = 1; /* maybe not the right place... */
- dev->volume = 0x1f;
-
- em28xx_outfmt_set_yuv422(dev);
- em28xx_colorlevels_set_default(dev);
- em28xx_compression_disable(dev);
-
- return 0;
-}
-
-/*
- * em28xx_config_i2c()
- * configure i2c attached devices
- */
-static void em28xx_config_i2c(struct em28xx *dev)
-{
- struct v4l2_routing route;
-
- route.input = INPUT(dev->ctl_input)->vmux;
- route.output = 0;
- em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
- em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
-}
-
static void video_mux(struct em28xx *dev, int index)
{
struct v4l2_routing route;
@@ -565,10 +515,14 @@ static void video_mux(struct em28xx *dev, int index)
route.output = 0;
dev->ctl_input = index;
dev->ctl_ainput = INPUT(index)->amux;
+ dev->ctl_aoutput = INPUT(index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
- if (dev->has_msp34xx) {
+ if (dev->board.has_msp34xx) {
if (dev->i2s_speed) {
em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ,
&dev->i2s_speed);
@@ -594,12 +548,10 @@ static int res_get(struct em28xx_fh *fh)
return rc;
if (dev->stream_on)
- return -EINVAL;
+ return -EBUSY;
- mutex_lock(&dev->lock);
dev->stream_on = 1;
fh->stream_on = 1;
- mutex_unlock(&dev->lock);
return rc;
}
@@ -612,10 +564,8 @@ static void res_free(struct em28xx_fh *fh)
{
struct em28xx *dev = fh->dev;
- mutex_lock(&dev->lock);
fh->stream_on = 0;
dev->stream_on = 0;
- mutex_unlock(&dev->lock);
}
/*
@@ -702,8 +652,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
@@ -715,6 +665,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -725,24 +686,30 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
unsigned int maxw = norm_maxw(dev);
unsigned int maxh = norm_maxh(dev);
unsigned int hscale, vscale;
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
/* width must even because of the YUYV format
height must be even because of interlacing */
height &= 0xfffe;
- width &= 0xfffe;
+ width &= 0xfffe;
- if (height < 32)
+ if (unlikely(height < 32))
height = 32;
- if (height > maxh)
+ if (unlikely(height > maxh))
height = maxh;
- if (width < 48)
+ if (unlikely(width < 48))
width = 48;
- if (width > maxw)
+ if (unlikely(width > maxw))
width = maxw;
- mutex_lock(&dev->lock);
-
- if (dev->is_em2800) {
+ if (dev->board.is_em2800) {
/* the em2800 can only scale down to 50% */
if (height % (maxh / 2))
height = maxh;
@@ -765,13 +732,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.width = width;
f->fmt.pix.height = height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- f->fmt.pix.bytesperline = width * 2;
- f->fmt.pix.sizeimage = width * 2 * height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- mutex_unlock(&dev->lock);
return 0;
}
@@ -781,14 +747,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
+ struct em28xx_fmt *fmt;
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+
vidioc_try_fmt_vid_cap(file, priv, f);
- mutex_lock(&dev->lock);
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ rc = -EINVAL;
+ goto out;
+ }
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
em28xx_errdev("%s queue busy\n", __func__);
@@ -805,6 +778,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
/* set new image size */
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
+ dev->format = fmt;
get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
em28xx_set_alternate(dev);
@@ -830,15 +804,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
mutex_lock(&dev->lock);
dev->norm = *norm;
- mutex_unlock(&dev->lock);
/* Adjusts width/height, if needed */
f.fmt.pix.width = dev->width;
f.fmt.pix.height = dev->height;
vidioc_try_fmt_vid_cap(file, priv, &f);
- mutex_lock(&dev->lock);
-
/* set new image size */
dev->width = f.fmt.pix.width;
dev->height = f.fmt.pix.height;
@@ -927,20 +898,38 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- unsigned int index = a->index;
-
- if (a->index > 1)
- return -EINVAL;
-
- index = dev->ctl_ainput;
- if (index == 0)
+ switch (a->index) {
+ case EM28XX_AMUX_VIDEO:
strcpy(a->name, "Television");
- else
+ break;
+ case EM28XX_AMUX_LINE_IN:
strcpy(a->name, "Line In");
+ break;
+ case EM28XX_AMUX_VIDEO2:
+ strcpy(a->name, "Television alt");
+ break;
+ case EM28XX_AMUX_PHONE:
+ strcpy(a->name, "Phone");
+ break;
+ case EM28XX_AMUX_MIC:
+ strcpy(a->name, "Mic");
+ break;
+ case EM28XX_AMUX_CD:
+ strcpy(a->name, "CD");
+ break;
+ case EM28XX_AMUX_AUX:
+ strcpy(a->name, "Aux");
+ break;
+ case EM28XX_AMUX_PCM_OUT:
+ strcpy(a->name, "PCM");
+ break;
+ default:
+ return -EINVAL;
+ }
+ a->index = dev->ctl_ainput;
a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
return 0;
}
@@ -950,9 +939,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
+ mutex_lock(&dev->lock);
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ dev->ctl_aoutput = INPUT(a->index)->aout;
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -973,7 +968,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
qc->id = id;
- if (!dev->has_msp34xx) {
+ if (!dev->board.has_msp34xx) {
for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
if (qc->id && qc->id == em28xx_qctrl[i].id) {
memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
@@ -1001,17 +996,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
rc = check_dev(dev);
if (rc < 0)
return rc;
- mutex_lock(&dev->lock);
+ rc = 0;
- if (!dev->has_msp34xx)
- rc = em28xx_get_ctrl(dev, ctrl);
- else
- rc = -EINVAL;
+ mutex_lock(&dev->lock);
- if (rc == -EINVAL) {
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
- rc = 0;
- }
+ else
+ rc = em28xx_get_ctrl(dev, ctrl);
mutex_unlock(&dev->lock);
return rc;
@@ -1031,7 +1023,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->has_msp34xx)
+ if (dev->board.has_msp34xx)
em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl);
else {
rc = 1;
@@ -1111,8 +1103,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
+ mutex_lock(&dev->lock);
f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1142,6 +1136,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1158,6 +1153,21 @@ static int em28xx_reg_len(int reg)
}
}
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip);
+
+ return 0;
+}
+
+
static int vidioc_g_register(struct file *file, void *priv,
struct v4l2_register *reg)
{
@@ -1165,19 +1175,43 @@ static int vidioc_g_register(struct file *file, void *priv,
struct em28xx *dev = fh->dev;
int ret;
- if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ ret = em28xx_read_ac97(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+ }
+ /* Match host */
if (em28xx_reg_len(reg->reg) == 1) {
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+
if (ret < 0)
return ret;
reg->val = ret;
} else {
__le64 val = 0;
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
reg->reg, (char *)&val, 2);
+ mutex_unlock(&dev->lock);
if (ret < 0)
return ret;
@@ -1193,11 +1227,35 @@ static int vidioc_s_register(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
__le64 buf;
+ int rc;
+ switch (reg->match_type) {
+ case V4L2_CHIP_MATCH_AC97:
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_ac97(dev, reg->reg, reg->val);
+ mutex_unlock(&dev->lock);
+
+ return rc;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* Not supported yet */
+ return -EINVAL;
+ default:
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+ }
+
+ /* Match host */
buf = cpu_to_le64(reg->val);
- return em28xx_write_regs(dev, reg->reg, (char *)&buf,
- em28xx_reg_len(reg->reg));
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
#endif
@@ -1234,10 +1292,15 @@ static int vidioc_streamon(struct file *file, void *priv,
return rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+
+ if (likely(rc >= 0))
+ rc = videobuf_streamon(&fh->vb_vidq);
- return (videobuf_streamon(&fh->vb_vidq));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
static int vidioc_streamoff(struct file *file, void *priv,
@@ -1256,9 +1319,13 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (type != fh->type)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
videobuf_streamoff(&fh->vb_vidq);
res_free(fh);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1270,7 +1337,7 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
@@ -1287,15 +1354,13 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmtd)
+ struct v4l2_fmtdesc *f)
{
- if (fmtd->index != 0)
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
return -EINVAL;
- fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmtd->description, "Packed YUY2");
- fmtd->pixelformat = V4L2_PIX_FMT_YUYV;
- memset(fmtd->reserved, 0, sizeof(fmtd->reserved));
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
return 0;
}
@@ -1423,7 +1488,7 @@ static int radio_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info));
+ strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
cap->version = EM28XX_VERSION_CODE;
cap->capabilities = V4L2_CAP_TUNER;
@@ -1441,7 +1506,10 @@ static int radio_g_tuner(struct file *file, void *priv,
strcpy(t->name, "Radio");
t->type = V4L2_TUNER_RADIO;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1473,7 +1541,9 @@ static int radio_s_tuner(struct file *file, void *priv,
if (0 != t->index)
return -EINVAL;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1515,28 +1585,13 @@ static int radio_queryctrl(struct file *file, void *priv,
static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
- int errCode = 0, radio = 0;
- struct em28xx *h, *dev = NULL;
+ int errCode = 0, radio;
+ struct em28xx *dev;
+ enum v4l2_buf_type fh_type;
struct em28xx_fh *fh;
- enum v4l2_buf_type fh_type = 0;
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(h, &em28xx_devlist, devlist) {
- if (h->vdev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- if (h->vbi_dev->minor == minor) {
- dev = h;
- fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- }
- if (h->radio_dev &&
- h->radio_dev->minor == minor) {
- radio = 1;
- dev = h;
- }
- }
- mutex_unlock(&em28xx_devlist_mutex);
+ dev = em28xx_get_device(inode, &fh_type, &radio);
+
if (NULL == dev)
return -ENODEV;
@@ -1570,7 +1625,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
/* Needed, since GPIO might have disabled power of
some i2c device
*/
- em28xx_config_i2c(dev);
+ em28xx_wake_i2c(dev);
}
if (fh->radio) {
@@ -1594,16 +1649,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
* unregisters the v4l2,i2c and usb devices
* called when the device gets disconected or at module unload
*/
-static void em28xx_release_resources(struct em28xx *dev)
+void em28xx_release_analog_resources(struct em28xx *dev)
{
/*FIXME: I2C IR should be disconnected */
- em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->num, dev->vbi_dev->num);
- list_del(&dev->devlist);
- if (dev->sbutton_input_dev)
- em28xx_deregister_snapshot_button(dev);
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
@@ -1612,6 +1662,8 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
+ em28xx_info("V4L2 device /dev/vbi%d deregistered\n",
+ dev->vbi_dev->num);
if (-1 != dev->vbi_dev->minor)
video_unregister_device(dev->vbi_dev);
else
@@ -1619,17 +1671,14 @@ static void em28xx_release_resources(struct em28xx *dev)
dev->vbi_dev = NULL;
}
if (dev->vdev) {
+ em28xx_info("V4L2 device /dev/video%d deregistered\n",
+ dev->vdev->num);
if (-1 != dev->vdev->minor)
video_unregister_device(dev->vdev);
else
video_device_release(dev->vdev);
dev->vdev = NULL;
}
- em28xx_i2c_unregister(dev);
- usb_put_dev(dev->udev);
-
- /* Mark device as unused */
- em28xx_devused &= ~(1<<dev->devno);
}
/*
@@ -1646,11 +1695,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
+ mutex_lock(&dev->lock);
if (res_check(fh))
res_free(fh);
- mutex_lock(&dev->lock);
-
if (dev->users == 1) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
@@ -1664,9 +1712,12 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
return 0;
}
+ /* Save some power by putting tuner to sleep */
+ em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
/* do this before setting alternate! */
em28xx_uninit_isoc(dev);
- em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
/* set alternate 0 */
dev->alt = 0;
@@ -1705,8 +1756,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (unlikely(res_get(fh)))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
@@ -1728,7 +1783,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
if (rc < 0)
return rc;
- if (unlikely(res_get(fh) < 0))
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
return POLLERR;
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
@@ -1746,13 +1805,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
struct em28xx *dev = fh->dev;
int rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
-
rc = check_dev(dev);
if (rc < 0)
return rc;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+ mutex_unlock(&dev->lock);
+
+ if (unlikely(rc < 0))
+ return rc;
+
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
@@ -1809,6 +1872,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
#endif
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
@@ -1864,44 +1928,6 @@ static struct video_device em28xx_radio_template = {
/******************************** usb interface ******************************/
-static LIST_HEAD(em28xx_extension_devlist);
-static DEFINE_MUTEX(em28xx_extension_devlist_lock);
-
-int em28xx_register_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- mutex_lock(&em28xx_extension_devlist_lock);
- list_add_tail(&ops->next, &em28xx_extension_devlist);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->init(dev);
- }
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
- return 0;
-}
-EXPORT_SYMBOL(em28xx_register_extension);
-
-void em28xx_unregister_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- if (dev)
- ops->fini(dev);
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
- list_del(&ops->next);
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-}
-EXPORT_SYMBOL(em28xx_unregister_extension);
static struct video_device *em28xx_vdev_init(struct em28xx *dev,
const struct video_device *template,
@@ -1924,11 +1950,43 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
return vfd;
}
-
-static int register_analog_devices(struct em28xx *dev)
+int em28xx_register_analog_devices(struct em28xx *dev)
{
int ret;
+ printk(KERN_INFO "%s: v4l2 driver version %d.%d.%d\n",
+ dev->name,
+ (EM28XX_VERSION_CODE >> 16) & 0xff,
+ (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+ video_mux(dev, 0);
+
+ /* enable vbi capturing */
+
+/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */
+ em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
+
+ dev->mute = 1; /* maybe not the right place... */
+ dev->volume = 0x1f;
+
+ em28xx_set_outfmt(dev);
+ em28xx_colorlevels_set_default(dev);
+ em28xx_compression_disable(dev);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+ dev->hscale = 0;
+ dev->vscale = 0;
+
+ /* FIXME: This is a very bad hack! Not all devices have TV on input 2 */
+ dev->ctl_input = 2;
+
/* allocate and fill video video_device struct */
dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
if (!dev->vdev) {
@@ -1977,383 +2035,3 @@ static int register_analog_devices(struct em28xx *dev)
return 0;
}
-
-
-/*
- * em28xx_init_dev()
- * allocates and inits the device structs, registers i2c bus and v4l device
- */
-static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
- int minor)
-{
- struct em28xx_ops *ops = NULL;
- struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
- int errCode;
- unsigned int maxh, maxw;
-
- dev->udev = udev;
- mutex_init(&dev->lock);
- mutex_init(&dev->ctrl_urb_lock);
- spin_lock_init(&dev->slock);
- init_waitqueue_head(&dev->open);
- init_waitqueue_head(&dev->wait_frame);
- init_waitqueue_head(&dev->wait_stream);
-
- dev->em28xx_write_regs = em28xx_write_regs;
- dev->em28xx_read_reg = em28xx_read_reg;
- dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
- dev->em28xx_write_regs_req = em28xx_write_regs_req;
- dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->is_em2800 = em28xx_boards[dev->model].is_em2800;
-
- em28xx_pre_card_setup(dev);
-
- errCode = em28xx_config(dev);
- if (errCode) {
- em28xx_errdev("error configuring device\n");
- return -ENOMEM;
- }
-
- /* register i2c bus */
- errCode = em28xx_i2c_register(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
-
- /* Configure audio */
- errCode = em28xx_audio_analog_set(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* configure the device */
- em28xx_config_i2c(dev);
-
- /* set default norm */
- dev->norm = em28xx_video_template.current_norm;
-
- maxw = norm_maxw(dev);
- maxh = norm_maxh(dev);
-
- /* set default image size */
- dev->width = maxw;
- dev->height = maxh;
- dev->interlaced = EM28XX_INTERLACED_DEFAULT;
- dev->hscale = 0;
- dev->vscale = 0;
- dev->ctl_input = 2;
-
- errCode = em28xx_config(dev);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_config - errCode [%d]!\n",
- __func__, errCode);
- return errCode;
- }
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
-
-
- if (dev->has_msp34xx) {
- /* Send a reset to other chips via gpio */
- errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
-
- errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
- if (errCode < 0) {
- em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n",
- __func__, errCode);
- return errCode;
- }
- msleep(3);
- }
-
- video_mux(dev, 0);
-
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- retval = register_analog_devices(dev);
- if (retval < 0) {
- em28xx_release_resources(dev);
- mutex_unlock(&em28xx_devlist_mutex);
- goto fail_reg_devices;
- }
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->id)
- ops->init(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
- mutex_unlock(&em28xx_devlist_mutex);
-
- return 0;
-
-fail_reg_devices:
- mutex_unlock(&dev->lock);
- return retval;
-}
-
-#if defined(CONFIG_MODULES) && defined(MODULE)
-static void request_module_async(struct work_struct *work)
-{
- struct em28xx *dev = container_of(work,
- struct em28xx, request_module_wk);
-
- if (dev->has_audio_class)
- request_module("snd-usb-audio");
- else
- request_module("em28xx-alsa");
-
- if (dev->has_dvb)
- request_module("em28xx-dvb");
-}
-
-static void request_modules(struct em28xx *dev)
-{
- INIT_WORK(&dev->request_module_wk, request_module_async);
- schedule_work(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-/*
- * em28xx_usb_probe()
- * checks for supported devices
- */
-static int em28xx_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- const struct usb_endpoint_descriptor *endpoint;
- struct usb_device *udev;
- struct usb_interface *uif;
- struct em28xx *dev = NULL;
- int retval = -ENODEV;
- int i, nr, ifnum;
-
- udev = usb_get_dev(interface_to_usbdev(interface));
- ifnum = interface->altsetting[0].desc.bInterfaceNumber;
-
- /* Check to see next free device and mark as used */
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- em28xx_devused |= 1<<nr;
-
- /* Don't register audio interfaces */
- if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n",
- udev->descriptor.idVendor,
- udev->descriptor.idProduct,
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- endpoint = &interface->cur_altsetting->endpoint[1].desc;
-
- /* check if the device has the iso in endpoint at the correct place */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_ISOC) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
- em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENODEV;
- }
-
- if (nr >= EM28XX_MAXBOARDS) {
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- em28xx_err(DRIVER_NAME ": out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- return -ENOMEM;
- }
-
- snprintf(dev->name, 29, "em28xx #%d", nr);
- dev->devno = nr;
- dev->model = id->driver_info;
- dev->alt = -1;
-
- /* Checks if audio is provided by some interface */
- for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
- uif = udev->config->interface[i];
- if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev->has_audio_class = 1;
- break;
- }
- }
-
- printk(KERN_INFO DRIVER_NAME " %s usb audio class\n",
- dev->has_audio_class ? "Has" : "Doesn't have");
-
- /* compute alternate max packet sizes */
- uif = udev->actconfig->interface[0];
-
- dev->num_alt = uif->num_altsetting;
- em28xx_info("Alternate settings: %i\n", dev->num_alt);
-/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */
- dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
-
- if (dev->alt_max_pkt_size == NULL) {
- em28xx_errdev("out of memory!\n");
- em28xx_devused &= ~(1<<nr);
- kfree(dev);
- return -ENOMEM;
- }
-
- for (i = 0; i < dev->num_alt ; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
- wMaxPacketSize);
- dev->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- em28xx_info("Alternate setting %i, max size= %i\n", i,
- dev->alt_max_pkt_size[i]);
- }
-
- if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
- dev->model = card[nr];
-
- /* allocate device struct */
- retval = em28xx_init_dev(&dev, udev, nr);
- if (retval) {
- em28xx_devused &= ~(1<<dev->devno);
- kfree(dev);
-
- return retval;
- }
-
- em28xx_info("Found %s\n", em28xx_boards[dev->model].name);
-
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
- request_modules(dev);
-
- return 0;
-}
-
-/*
- * em28xx_usb_disconnect()
- * called when the device gets diconencted
- * video device will be unregistered on v4l2_close in case it is still open
- */
-static void em28xx_usb_disconnect(struct usb_interface *interface)
-{
- struct em28xx *dev;
- struct em28xx_ops *ops = NULL;
-
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-
- if (!dev)
- return;
-
- em28xx_info("disconnecting %s\n", dev->vdev->name);
-
- /* wait until all current v4l2 io is finished then deallocate
- resources */
- mutex_lock(&dev->lock);
-
- wake_up_interruptible_all(&dev->open);
-
- if (dev->users) {
- em28xx_warn
- ("device /dev/video%d is open! Deregistration and memory "
- "deallocation are deferred on close.\n",
- dev->vdev->num);
-
- dev->state |= DEV_MISCONFIGURED;
- em28xx_uninit_isoc(dev);
- dev->state |= DEV_DISCONNECTED;
- wake_up_interruptible(&dev->wait_frame);
- wake_up_interruptible(&dev->wait_stream);
- } else {
- dev->state |= DEV_DISCONNECTED;
- em28xx_release_resources(dev);
- }
- mutex_unlock(&dev->lock);
-
- mutex_lock(&em28xx_extension_devlist_lock);
- if (!list_empty(&em28xx_extension_devlist)) {
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- ops->fini(dev);
- }
- }
- mutex_unlock(&em28xx_extension_devlist_lock);
-
- if (!dev->users) {
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
- }
-}
-
-static struct usb_driver em28xx_usb_driver = {
- .name = "em28xx",
- .probe = em28xx_usb_probe,
- .disconnect = em28xx_usb_disconnect,
- .id_table = em28xx_id_table,
-};
-
-static int __init em28xx_module_init(void)
-{
- int result;
-
- printk(KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded\n",
- (EM28XX_VERSION_CODE >> 16) & 0xff,
- (EM28XX_VERSION_CODE >> 8) & 0xff, EM28XX_VERSION_CODE & 0xff);
-#ifdef SNAPSHOT
- printk(KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d\n",
- SNAPSHOT / 10000, (SNAPSHOT / 100) % 100, SNAPSHOT % 100);
-#endif
-
- /* register this driver with the USB subsystem */
- result = usb_register(&em28xx_usb_driver);
- if (result)
- em28xx_err(DRIVER_NAME
- " usb_register failed. Error number %d.\n", result);
-
- return result;
-}
-
-static void __exit em28xx_module_exit(void)
-{
- /* deregister this driver with the USB subsystem */
- usb_deregister(&em28xx_usb_driver);
-}
-
-module_init(em28xx_module_init);
-module_exit(em28xx_module_exit);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 5956e9b3062f..b5eddc26388e 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -67,7 +67,6 @@
#define EM2820_BOARD_HERCULES_SMART_TV_USB2 26
#define EM2820_BOARD_PINNACLE_USB_2_FM1216ME 27
#define EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE 28
-#define EM2820_BOARD_PINNACLE_DVC_100 29
#define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30
#define EM2821_BOARD_USBGEAR_VD204 31
#define EM2821_BOARD_SUPERCOMP_USB_2 32
@@ -97,6 +96,8 @@
#define EM2882_BOARD_PINNACLE_HYBRID_PRO 56
#define EM2883_BOARD_KWORLD_HYBRID_A316 57
#define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
+#define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -159,7 +160,7 @@
#define EM2800_I2C_WRITE_TIMEOUT 20
enum em28xx_mode {
- EM28XX_MODE_UNDEFINED,
+ EM28XX_SUSPEND,
EM28XX_ANALOG_MODE,
EM28XX_DIGITAL_MODE,
};
@@ -207,9 +208,12 @@ struct em28xx_usb_isoc_ctl {
};
+/* Struct to enumberate video formats */
struct em28xx_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
+ int depth;
+ int reg;
};
/* buffer for one video frame */
@@ -255,54 +259,105 @@ enum enum28xx_itype {
EM28XX_RADIO,
};
+enum em28xx_ac97_mode {
+ EM28XX_NO_AC97 = 0,
+ EM28XX_AC97_EM202,
+ EM28XX_AC97_SIGMATEL,
+ EM28XX_AC97_OTHER,
+};
+
+struct em28xx_audio_mode {
+ enum em28xx_ac97_mode ac97;
+
+ u16 ac97_feat;
+ u32 ac97_vendor_id;
+
+ unsigned int has_audio:1;
+
+ unsigned int i2s_3rates:1;
+ unsigned int i2s_5rates:1;
+};
+
+/* em28xx has two audio inputs: tuner and line in.
+ However, on most devices, an auxiliary AC97 codec device is used.
+ The AC97 device may have several different inputs and outputs,
+ depending on their model. So, it is possible to use AC97 mixer to
+ address more than two different entries.
+ */
enum em28xx_amux {
- EM28XX_AMUX_VIDEO,
- EM28XX_AMUX_LINE_IN,
- EM28XX_AMUX_AC97_VIDEO,
- EM28XX_AMUX_AC97_LINE_IN,
+ /* This is the only entry for em28xx tuner input */
+ EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */
+
+ EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */
+
+ /* Some less-common mixer setups */
+ EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */
+ EM28XX_AMUX_PHONE,
+ EM28XX_AMUX_MIC,
+ EM28XX_AMUX_CD,
+ EM28XX_AMUX_AUX,
+ EM28XX_AMUX_PCM_OUT,
+};
+
+enum em28xx_aout {
+ EM28XX_AOUT_MASTER = 1 << 0,
+ EM28XX_AOUT_LINE = 1 << 1,
+ EM28XX_AOUT_MONO = 1 << 2,
+ EM28XX_AOUT_LFE = 1 << 3,
+ EM28XX_AOUT_SURR = 1 << 4,
+};
+
+struct em28xx_reg_seq {
+ int reg;
+ unsigned char val, mask;
+ int sleep;
};
struct em28xx_input {
enum enum28xx_itype type;
unsigned int vmux;
enum em28xx_amux amux;
+ enum em28xx_aout aout;
+ struct em28xx_reg_seq *gpio;
};
#define INPUT(nr) (&em28xx_boards[dev->model].input[nr])
enum em28xx_decoder {
+ EM28XX_NODECODER,
EM28XX_TVP5150,
- EM28XX_SAA7113,
- EM28XX_SAA7114
-};
-
-struct em28xx_reg_seq {
- int reg;
- unsigned char val, mask;
- int sleep;
+ EM28XX_SAA711X,
};
struct em28xx_board {
char *name;
int vchannels;
int tuner_type;
+ int tuner_addr;
/* i2c flags */
unsigned int tda9887_conf;
+ /* GPIO sequences */
+ struct em28xx_reg_seq *dvb_gpio;
+ struct em28xx_reg_seq *suspend_gpio;
+ struct em28xx_reg_seq *tuner_gpio;
+
unsigned int is_em2800:1;
unsigned int has_msp34xx:1;
unsigned int mts_firmware:1;
- unsigned int has_12mhz_i2s:1;
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int has_snapshot_button:1;
unsigned int valid:1;
+ unsigned char xclk, i2c_speed;
+
enum em28xx_decoder decoder;
struct em28xx_input input[MAX_EM28XX_INPUT];
struct em28xx_input radio;
+ IR_KEYTAB_TYPE *ir_codes;
};
struct em28xx_eeprom {
@@ -369,32 +424,26 @@ struct em28xx {
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
- unsigned int is_em2800:1;
- unsigned int has_msp34xx:1;
- unsigned int has_tda9887:1;
+ enum em28xx_chip_id chip_id;
+
+ struct em28xx_board board;
+
unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
- unsigned int has_12mhz_i2s:1;
- unsigned int max_range_640_480:1;
- unsigned int has_dvb:1;
- unsigned int has_snapshot_button:1;
- unsigned int valid:1; /* report for validated boards */
+ unsigned int has_alsa_audio:1;
- /* Some older em28xx chips needs a waiting time after writing */
- unsigned int wait_after_write;
+ struct em28xx_fmt *format;
- /* GPIO sequences for analog and digital mode */
- struct em28xx_reg_seq *analog_gpio, *digital_gpio;
+ struct em28xx_IR *ir;
- /* GPIO sequences for tuner callbacks */
- struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio;
+ /* Some older em28xx chips needs a waiting time after writing */
+ unsigned int wait_after_write;
- int video_inputs; /* number of video inputs */
struct list_head devlist;
u32 i2s_speed; /* I2S speed for audio digital stream */
- enum em28xx_decoder decoder;
+ struct em28xx_audio_mode audio_mode;
int tuner_type; /* type of the tuner */
int tuner_addr; /* tuner address */
@@ -409,6 +458,7 @@ struct em28xx {
int ctl_freq; /* selected frequency */
unsigned int ctl_input; /* selected input */
unsigned int ctl_ainput;/* selected audio input */
+ unsigned int ctl_aoutput;/* selected audio output */
int mute;
int volume;
/* frame properties */
@@ -469,6 +519,9 @@ struct em28xx {
enum em28xx_mode mode;
+ /* register numbers for GPO/GPIO registers */
+ u16 reg_gpo_num, reg_gpio_num;
+
/* Caches GPO and GPIO registers */
unsigned char reg_gpo, reg_gpio;
@@ -508,11 +561,17 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg);
int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
int len);
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
+
+int em28xx_read_ac97(struct em28xx *dev, u8 reg);
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val);
+
int em28xx_audio_analog_set(struct em28xx *dev);
+int em28xx_audio_setup(struct em28xx *dev);
int em28xx_colorlevels_set_default(struct em28xx *dev);
int em28xx_capture_start(struct em28xx *dev, int start);
-int em28xx_outfmt_set_yuv422(struct em28xx *dev);
+int em28xx_set_outfmt(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
@@ -521,10 +580,20 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
void em28xx_uninit_isoc(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
-
-/* Provided by em28xx-video.c */
+void em28xx_wake_i2c(struct em28xx *dev);
+void em28xx_remove_from_devlist(struct em28xx *dev);
+void em28xx_add_into_devlist(struct em28xx *dev);
+struct em28xx *em28xx_get_device(struct inode *inode,
+ enum v4l2_buf_type *fh_type,
+ int *has_radio);
int em28xx_register_extension(struct em28xx_ops *dev);
void em28xx_unregister_extension(struct em28xx_ops *dev);
+void em28xx_init_extension(struct em28xx *dev);
+void em28xx_close_extension(struct em28xx *dev);
+
+/* Provided by em28xx-video.c */
+int em28xx_register_analog_devices(struct em28xx *dev);
+void em28xx_release_analog_resources(struct em28xx *dev);
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device *udev, int model);
@@ -535,9 +604,9 @@ extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
+void em28xx_release_resources(struct em28xx *dev);
/* Provided by em28xx-input.c */
-/* TODO: Check if the standard get_key handlers on ir-common can be used */
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -545,6 +614,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
void em28xx_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_snapshot_button(struct em28xx *dev);
+int em28xx_ir_init(struct em28xx *dev);
+int em28xx_ir_fini(struct em28xx *dev);
+
/* printk macros */
#define em28xx_err(fmt, arg...) do {\
@@ -564,7 +636,7 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
static inline int em28xx_compression_disable(struct em28xx *dev)
{
/* side effect of disabling scaler and mixer */
- return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1);
+ return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00);
}
static inline int em28xx_contrast_get(struct em28xx *dev)
@@ -636,7 +708,7 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val)
/*FIXME: maxw should be dependent of alt mode */
static inline unsigned int norm_maxw(struct em28xx *dev)
{
- if (dev->max_range_640_480)
+ if (dev->board.max_range_640_480)
return 640;
else
return 720;
@@ -644,7 +716,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev)
static inline unsigned int norm_maxh(struct em28xx *dev)
{
- if (dev->max_range_640_480)
+ if (dev->board.max_range_640_480)
return 480;
else
return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index 9d0ef96c23ff..83c07112c59d 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -1581,7 +1581,7 @@ et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 6b557c057fac..ee6a691dff22 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -12,12 +12,13 @@ menuconfig USB_GSPCA
"Video For Linux" to use this driver.
To compile this driver as modules, choose M here: the
- modules will be called gspca_main.
+ module will be called gspca_main.
if USB_GSPCA && VIDEO_V4L2
source "drivers/media/video/gspca/m5602/Kconfig"
+source "drivers/media/video/gspca/stv06xx/Kconfig"
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"
@@ -64,6 +65,16 @@ config USB_GSPCA_OV519
To compile this driver as a module, choose M here: the
module will be called gspca_ov519.
+config USB_GSPCA_OV534
+ tristate "OV534 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip.
+ (e.g. Sony Playstation EYE)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534.
+
config USB_GSPCA_PAC207
tristate "Pixart PAC207 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -83,10 +94,11 @@ config USB_GSPCA_PAC7311
module will be called gspca_pac7311.
config USB_GSPCA_SONIXB
- tristate "SN9C102 USB Camera Driver"
+ tristate "SONIX Bayer USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the SONIXB chip.
+ Say Y here if you want support for cameras based on the Sonix
+ chips with Bayer format (SN9C101, SN9C102 and SN9C103).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixb.
@@ -95,7 +107,8 @@ config USB_GSPCA_SONIXJ
tristate "SONIX JPEG USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the SONIXJ chip.
+ Say Y here if you want support for cameras based on the Sonix
+ chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
To compile this driver as a module, choose M here: the
module will be called gspca_sonixj
@@ -171,7 +184,7 @@ config USB_GSPCA_SUNPLUS
SPCA504(abc) SPCA533 SPCA536 chips.
To compile this driver as a module, choose M here: the
- module will be called gspca_spca5xx.
+ module will be called gspca_sunplus.
config USB_GSPCA_T613
tristate "T613 (JPEG Compliance) USB Camera Driver"
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 22734f5a6c32..bd8d9ee40504 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
@@ -27,6 +28,7 @@ gspca_etoms-objs := etoms.o
gspca_finepix-objs := finepix.o
gspca_mars-objs := mars.o
gspca_ov519-objs := ov519.o
+gspca_ov534-objs := ov534.o
gspca_pac207-objs := pac207.o
gspca_pac7311-objs := pac7311.o
gspca_sonixb-objs := sonixb.o
@@ -45,4 +47,4 @@ gspca_vc032x-objs := vc032x.o
gspca_zc3xx-objs := zc3xx.o
obj-$(CONFIG_USB_M5602) += m5602/
-
+obj-$(CONFIG_USB_STV06XX) += stv06xx/
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index de28354ea5ba..1753f5bb3544 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -93,7 +93,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 3be30b420a26..f3cd8ff5cc92 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -112,7 +112,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240,
@@ -125,7 +125,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0}, */
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 607942fd7970..afc8b2dd307b 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -72,7 +72,7 @@ struct usb_fpix {
}
/* These cameras only support 320x200. */
-static struct v4l2_pix_format fpix_mode[1] = {
+static const struct v4l2_pix_format fpix_mode[1] = {
{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -314,9 +314,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
int ret;
int size_ret;
- /* Reset bulk in endpoint */
- usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
-
/* Init the device */
memset(gspca_dev->usb_buf, 0, 12);
gspca_dev->usb_buf[0] = 0xc6;
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 748a87e82e44..8b9f3bde5740 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -30,7 +30,6 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/io.h>
-#include <linux/kref.h>
#include <asm/page.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
@@ -45,7 +44,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 4, 0)
static int video_nr = -1;
@@ -150,8 +149,11 @@ static void fill_frame(struct gspca_dev *gspca_dev,
/* check the packet status and length */
len = urb->iso_frame_desc[i].actual_length;
- if (len == 0)
+ if (len == 0) {
+ if (gspca_dev->empty_packet == 0)
+ gspca_dev->empty_packet = 1;
continue;
+ }
st = urb->iso_frame_desc[i].status;
if (st) {
PDEBUG(D_ERR,
@@ -170,7 +172,6 @@ static void fill_frame(struct gspca_dev *gspca_dev,
}
/* resubmit the URB */
- urb->status = 0;
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
@@ -200,11 +201,18 @@ static void bulk_irq(struct urb *urb
{
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
struct gspca_frame *frame;
+ int st;
PDEBUG(D_PACK, "bulk irq");
if (!gspca_dev->streaming)
return;
- if (urb->status != 0 && urb->status != -ECONNRESET) {
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ urb->status = 0;
+ break;
+ default:
#ifdef CONFIG_PM
if (!gspca_dev->frozen)
#endif
@@ -223,6 +231,13 @@ static void bulk_irq(struct urb *urb
urb->transfer_buffer,
urb->actual_length);
}
+
+ /* resubmit the URB */
+ if (gspca_dev->cam.bulk_nurbs != 0) {
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
+ }
}
/*
@@ -285,7 +300,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
frame->v4l2_buf.bytesused = frame->data_end - frame->data;
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE;
- atomic_inc(&gspca_dev->nevent);
wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
i = (gspca_dev->fr_i + 1) % gspca_dev->nframes;
gspca_dev->fr_i = i;
@@ -379,7 +393,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence = 0;
- atomic_set(&gspca_dev->nevent, 0);
return 0;
}
@@ -520,11 +533,14 @@ static int create_urbs(struct gspca_dev *gspca_dev,
nurbs = DEF_NURBS;
} else { /* bulk */
npkt = 0;
- bsize = gspca_dev->cam. bulk_size;
+ bsize = gspca_dev->cam.bulk_size;
if (bsize == 0)
bsize = psize;
PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
- nurbs = 1;
+ if (gspca_dev->cam.bulk_nurbs != 0)
+ nurbs = gspca_dev->cam.bulk_nurbs;
+ else
+ nurbs = 1;
}
gspca_dev->nurbs = nurbs;
@@ -597,6 +613,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
if (ret < 0)
goto out;
+ /* clear the bulk endpoint */
+ if (gspca_dev->alt == 0) /* if bulk transfer */
+ usb_clear_halt(gspca_dev->dev,
+ usb_rcvintpipe(gspca_dev->dev,
+ gspca_dev->cam.epaddr));
+
/* start the cam */
ret = gspca_dev->sd_desc->start(gspca_dev);
if (ret < 0) {
@@ -604,10 +626,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
goto out;
}
gspca_dev->streaming = 1;
- atomic_set(&gspca_dev->nevent, 0);
- /* bulk transfers are started by the subdriver */
- if (gspca_dev->alt == 0)
+ /* some bulk transfers are started by the subdriver */
+ if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0)
break;
/* submit the URBs */
@@ -618,8 +639,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
"usb_submit_urb [%d] err %d", n, ret);
gspca_dev->streaming = 0;
destroy_urbs(gspca_dev);
- if (ret == -ENOSPC)
+ if (ret == -ENOSPC) {
+ msleep(20); /* wait for kill
+ * complete */
break; /* try the previous alt */
+ }
goto out;
}
}
@@ -637,7 +661,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
if (ret < 0)
- PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret);
+ PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret);
return ret;
}
@@ -645,7 +669,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
static void gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = 0;
- atomic_set(&gspca_dev->nevent, 0);
if (gspca_dev->present
&& gspca_dev->sd_desc->stopN)
gspca_dev->sd_desc->stopN(gspca_dev);
@@ -727,7 +750,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
if (fmtdesc->index == index)
break; /* new format */
index++;
- if (index >= sizeof fmt_tb / sizeof fmt_tb[0])
+ if (index >= ARRAY_SIZE(fmt_tb))
return -EINVAL;
}
}
@@ -752,8 +775,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int mode;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
mode = gspca_dev->curr_mode;
memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode],
sizeof fmt->fmt.pix);
@@ -765,8 +786,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
{
int w, h, mode, mode2;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
w = fmt->fmt.pix.width;
h = fmt->fmt.pix.height;
@@ -846,11 +865,11 @@ out:
return ret;
}
-static void gspca_delete(struct kref *kref)
+static void gspca_release(struct video_device *vfd)
{
- struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref);
+ struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
- PDEBUG(D_STREAM, "device deleted");
+ PDEBUG(D_STREAM, "device released");
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
@@ -862,7 +881,7 @@ static int dev_open(struct inode *inode, struct file *file)
int ret;
PDEBUG(D_STREAM, "%s open", current->comm);
- gspca_dev = video_drvdata(file);
+ gspca_dev = (struct gspca_dev *) video_devdata(file);
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
if (!gspca_dev->present) {
@@ -883,17 +902,14 @@ static int dev_open(struct inode *inode, struct file *file)
gspca_dev->users++;
- /* one more user */
- kref_get(&gspca_dev->kref);
-
file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
/* activate the v4l2 debug */
if (gspca_debug & D_V4L2)
- gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
| V4L2_DEBUG_IOCTL_ARG;
else
- gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
| V4L2_DEBUG_IOCTL_ARG);
#endif
ret = 0;
@@ -932,8 +948,6 @@ static int dev_close(struct inode *inode, struct file *file)
PDEBUG(D_STREAM, "close done");
- kref_put(&gspca_dev->kref, gspca_delete);
-
return 0;
}
@@ -1053,6 +1067,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
return -EINVAL;
}
+/*fixme: have an audio flag in gspca_dev?*/
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ memset(audio, 0, sizeof *audio);
+ strcpy(audio->name, "Microphone");
+ return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ strcpy(audio->name, "Microphone");
+ audio->capability = 0;
+ audio->mode = 0;
+ return 0;
+}
+
static int vidioc_querymenu(struct file *file, void *priv,
struct v4l2_querymenu *qmenu)
{
@@ -1096,8 +1139,6 @@ static int vidioc_reqbufs(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int i, ret = 0;
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
switch (rb->memory) {
case GSPCA_MEMORY_READ: /* (internal call) */
case V4L2_MEMORY_MMAP:
@@ -1162,8 +1203,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
struct gspca_frame *frame;
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
- || v4l2_buf->index < 0
+ if (v4l2_buf->index < 0
|| v4l2_buf->index >= gspca_dev->nframes)
return -EINVAL;
@@ -1186,7 +1226,8 @@ static int vidioc_streamon(struct file *file, void *priv,
ret = -ENODEV;
goto out;
}
- if (gspca_dev->nframes == 0) {
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
ret = -EINVAL;
goto out;
}
@@ -1236,7 +1277,6 @@ static int vidioc_streamoff(struct file *file, void *priv,
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence = 0;
- atomic_set(&gspca_dev->nevent, 0);
ret = 0;
out:
mutex_unlock(&gspca_dev->queue_lock);
@@ -1264,10 +1304,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int ret;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
if (!gspca_dev->sd_desc->set_jcomp)
return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
mutex_unlock(&gspca_dev->usb_lock);
return ret;
@@ -1281,6 +1321,17 @@ static int vidioc_g_parm(struct file *filp, void *priv,
memset(parm, 0, sizeof *parm);
parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+ if (gspca_dev->sd_desc->get_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1295,6 +1346,17 @@ static int vidioc_s_parm(struct file *filp, void *priv,
parm->parm.capture.readbuffers = gspca_dev->nbufread;
else
gspca_dev->nbufread = n;
+
+ if (gspca_dev->sd_desc->set_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1440,33 +1502,22 @@ static int frame_wait(struct gspca_dev *gspca_dev,
i = gspca_dev->fr_o;
j = gspca_dev->fr_queue[i];
frame = &gspca_dev->frame[j];
- if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) {
- atomic_dec(&gspca_dev->nevent);
- goto ok;
- }
- if (nonblock_ing) /* no frame yet */
- return -EAGAIN;
- /* wait till a frame is ready */
- for (;;) {
+ if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) {
+ if (nonblock_ing)
+ return -EAGAIN;
+
+ /* wait till a frame is ready */
ret = wait_event_interruptible_timeout(gspca_dev->wq,
- atomic_read(&gspca_dev->nevent) > 0,
- msecs_to_jiffies(3000));
- if (ret <= 0) {
- if (ret < 0)
- return ret; /* interrupt */
- return -EIO; /* timeout */
- }
- atomic_dec(&gspca_dev->nevent);
- if (!gspca_dev->streaming || !gspca_dev->present)
+ (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) ||
+ !gspca_dev->streaming || !gspca_dev->present,
+ msecs_to_jiffies(3000));
+ if (ret < 0)
+ return ret;
+ if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present)
return -EIO;
- i = gspca_dev->fr_o;
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)
- break;
}
-ok:
+
gspca_dev->fr_o = (i + 1) % gspca_dev->nframes;
PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d",
gspca_dev->fr_q,
@@ -1494,8 +1545,6 @@ static int vidioc_dqbuf(struct file *file, void *priv,
int i, ret;
PDEBUG(D_FRAM, "dqbuf");
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (v4l2_buf->memory != gspca_dev->memory)
return -EINVAL;
@@ -1550,8 +1599,6 @@ static int vidioc_qbuf(struct file *file, void *priv,
int i, index, ret;
PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
@@ -1761,7 +1808,7 @@ static struct file_operations dev_fops = {
.release = dev_close,
.read = dev_read,
.mmap = dev_mmap,
- .ioctl = video_ioctl2,
+ .unlocked_ioctl = __video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
#endif
@@ -1781,6 +1828,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_enumaudio = vidioc_enumaudio,
.vidioc_querymenu = vidioc_querymenu,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
@@ -1802,7 +1852,7 @@ static struct video_device gspca_template = {
.name = "gspca main driver",
.fops = &dev_fops,
.ioctl_ops = &dev_ioctl_ops,
- .release = video_device_release,
+ .release = gspca_release,
.minor = -1,
};
@@ -1840,7 +1890,6 @@ int gspca_dev_probe(struct usb_interface *intf,
err("couldn't kzalloc gspca struct");
return -ENOMEM;
}
- kref_init(&gspca_dev->kref);
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
err("out of memory");
@@ -1852,12 +1901,13 @@ int gspca_dev_probe(struct usb_interface *intf,
gspca_dev->nbalt = intf->num_altsetting;
gspca_dev->sd_desc = sd_desc;
gspca_dev->nbufread = 2;
+ gspca_dev->empty_packet = -1; /* don't check the empty packets */
/* configure the subdriver and initialize the USB device */
- ret = gspca_dev->sd_desc->config(gspca_dev, id);
+ ret = sd_desc->config(gspca_dev, id);
if (ret < 0)
goto out;
- ret = gspca_dev->sd_desc->init(gspca_dev);
+ ret = sd_desc->init(gspca_dev);
if (ret < 0)
goto out;
ret = gspca_set_alt0(gspca_dev);
@@ -1871,18 +1921,15 @@ int gspca_dev_probe(struct usb_interface *intf,
init_waitqueue_head(&gspca_dev->wq);
/* init video stuff */
- gspca_dev->vdev = video_device_alloc();
- memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template);
- gspca_dev->vdev->parent = &dev->dev;
+ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
+ gspca_dev->vdev.parent = &dev->dev;
gspca_dev->module = module;
gspca_dev->present = 1;
- video_set_drvdata(gspca_dev->vdev, gspca_dev);
- ret = video_register_device(gspca_dev->vdev,
+ ret = video_register_device(&gspca_dev->vdev,
VFL_TYPE_GRABBER,
video_nr);
if (ret < 0) {
err("video_register_device err %d", ret);
- video_device_release(gspca_dev->vdev);
goto out;
}
@@ -1906,15 +1953,14 @@ void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
-
-/* We don't want people trying to open up the device */
- video_unregister_device(gspca_dev->vdev);
-
gspca_dev->present = 0;
gspca_dev->streaming = 0;
- kref_put(&gspca_dev->kref, gspca_delete);
+ usb_set_intfdata(intf, NULL);
+
+ /* release the device */
+ /* (this will call gspca_release() immediatly or on last close) */
+ video_unregister_device(&gspca_dev->vdev);
PDEBUG(D_PROBE, "disconnect complete");
}
@@ -1992,7 +2038,7 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
desired lumination fast (with the risc of a slight overshoot) */
steps = abs(desired_avg_lum - avg_lum) / deadzone;
- PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
avg_lum, desired_avg_lum, steps);
for (i = 0; i < steps; i++) {
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index d25e8d69373b..c90af9cb1e07 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -56,8 +56,12 @@ extern int gspca_debug;
/* device information - set at probe time */
struct cam {
int bulk_size; /* buffer size when image transfer by bulk */
- struct v4l2_pix_format *cam_mode; /* size nmodes */
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
+ __u8 bulk_nurbs; /* number of URBs in bulk mode
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
__u8 epaddr;
};
@@ -70,6 +74,8 @@ typedef void (*cam_v_op) (struct gspca_dev *);
typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
typedef int (*cam_jpg_op) (struct gspca_dev *,
struct v4l2_jpegcompression *);
+typedef int (*cam_streamparm_op) (struct gspca_dev *,
+ struct v4l2_streamparm *);
typedef int (*cam_qmnu_op) (struct gspca_dev *,
struct v4l2_querymenu *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
@@ -102,6 +108,8 @@ struct sd_desc {
cam_jpg_op get_jcomp;
cam_jpg_op set_jcomp;
cam_qmnu_op querymenu;
+ cam_streamparm_op get_streamparm;
+ cam_streamparm_op set_streamparm;
};
/* packet types when moving from iso buf to frame buf */
@@ -120,10 +128,9 @@ struct gspca_frame {
};
struct gspca_dev {
- struct video_device *vdev;
+ struct video_device vdev; /* !! must be the first item */
struct module *module; /* subdriver handling the device */
struct usb_device *dev;
- struct kref kref;
struct file *capt_file; /* file doing video capture */
struct cam cam; /* device information */
@@ -142,22 +149,20 @@ struct gspca_dev {
char fr_q; /* next frame to queue */
char fr_o; /* next frame to dequeue */
signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
- char last_packet_type;
+ __u8 last_packet_type;
+ __s8 empty_packet; /* if (-1) don't check empty packets */
+ __u8 streaming;
- __u8 iface; /* USB interface number */
- __u8 alt; /* USB alternate setting */
__u8 curr_mode; /* current camera mode */
__u32 pixfmt; /* current mode parameters */
__u16 width;
__u16 height;
+ __u32 sequence; /* frame sequence number */
- atomic_t nevent; /* number of frames done */
wait_queue_head_t wq; /* wait queue */
struct mutex usb_lock; /* usb exchange protection */
struct mutex read_lock; /* read protection */
struct mutex queue_lock; /* ISOC queue protection */
- __u32 sequence; /* frame sequence number */
- char streaming;
#ifdef CONFIG_PM
char frozen; /* suspend - resume */
#endif
@@ -166,6 +171,8 @@ struct gspca_dev {
char nbufread; /* number of buffers for read() */
char nurbs; /* number of allocated URBs */
char memory; /* memory type (V4L2_MEMORY_xxx) */
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
__u8 nbalt; /* number of USB alternate settings */
};
diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h
index 1a37ae4bc82d..a3f3b7a0c7e7 100644
--- a/drivers/media/video/gspca/m5602/m5602_bridge.h
+++ b/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -25,59 +25,59 @@
/*****************************************************************************/
-#define M5602_XB_SENSOR_TYPE 0x00
-#define M5602_XB_SENSOR_CTRL 0x01
-#define M5602_XB_LINE_OF_FRAME_H 0x02
-#define M5602_XB_LINE_OF_FRAME_L 0x03
-#define M5602_XB_PIX_OF_LINE_H 0x04
-#define M5602_XB_PIX_OF_LINE_L 0x05
-#define M5602_XB_VSYNC_PARA 0x06
-#define M5602_XB_HSYNC_PARA 0x07
-#define M5602_XB_TEST_MODE_1 0x08
-#define M5602_XB_TEST_MODE_2 0x09
-#define M5602_XB_SIG_INI 0x0a
-#define M5602_XB_DS_PARA 0x0e
-#define M5602_XB_TRIG_PARA 0x0f
-#define M5602_XB_CLK_PD 0x10
-#define M5602_XB_MCU_CLK_CTRL 0x12
-#define M5602_XB_MCU_CLK_DIV 0x13
-#define M5602_XB_SEN_CLK_CTRL 0x14
-#define M5602_XB_SEN_CLK_DIV 0x15
-#define M5602_XB_AUD_CLK_CTRL 0x16
-#define M5602_XB_AUD_CLK_DIV 0x17
-#define M5602_XB_DEVCTR1 0x41
-#define M5602_XB_EPSETR0 0x42
-#define M5602_XB_EPAFCTR 0x47
-#define M5602_XB_EPBFCTR 0x49
-#define M5602_XB_EPEFCTR 0x4f
-#define M5602_XB_TEST_REG 0x53
-#define M5602_XB_ALT2SIZE 0x54
-#define M5602_XB_ALT3SIZE 0x55
-#define M5602_XB_OBSFRAME 0x56
-#define M5602_XB_PWR_CTL 0x59
-#define M5602_XB_ADC_CTRL 0x60
-#define M5602_XB_ADC_DATA 0x61
-#define M5602_XB_MISC_CTRL 0x62
-#define M5602_XB_SNAPSHOT 0x63
-#define M5602_XB_SCRATCH_1 0x64
-#define M5602_XB_SCRATCH_2 0x65
-#define M5602_XB_SCRATCH_3 0x66
-#define M5602_XB_SCRATCH_4 0x67
-#define M5602_XB_I2C_CTRL 0x68
-#define M5602_XB_I2C_CLK_DIV 0x69
-#define M5602_XB_I2C_DEV_ADDR 0x6a
-#define M5602_XB_I2C_REG_ADDR 0x6b
-#define M5602_XB_I2C_DATA 0x6c
-#define M5602_XB_I2C_STATUS 0x6d
-#define M5602_XB_GPIO_DAT_H 0x70
-#define M5602_XB_GPIO_DAT_L 0x71
-#define M5602_XB_GPIO_DIR_H 0x72
-#define M5602_XB_GPIO_DIR_L 0x73
-#define M5602_XB_GPIO_EN_H 0x74
-#define M5602_XB_GPIO_EN_L 0x75
-#define M5602_XB_GPIO_DAT 0x76
-#define M5602_XB_GPIO_DIR 0x77
-#define M5602_XB_MISC_CTL 0x70
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_MISC_CTL 0x70
#define I2C_BUSY 0x80
@@ -90,13 +90,7 @@
#define M5602_ISOC_ENDPOINT_ADDR 0x81
#define M5602_INTR_ENDPOINT_ADDR 0x82
-#define M5602_MAX_FRAMES 32
-#define M5602_URBS 2
-#define M5602_ISOC_PACKETS 14
-
-#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS)
#define M5602_URB_MSG_TIMEOUT 5000
-#define M5602_FRAME_TIMEOUT 2
/*****************************************************************************/
@@ -115,7 +109,6 @@ static const unsigned char sensor_urb_skeleton[] = {
0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
};
-/* m5602 device descriptor, currently it just wraps the m5602_camera struct */
struct sd {
struct gspca_dev gspca_dev;
@@ -140,4 +133,10 @@ int m5602_read_bridge(
int m5602_write_bridge(
struct sd *sd, u8 address, u8 i2c_data);
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index fd6ce384b487..ed906fe31287 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -24,7 +24,7 @@
/* Kernel module parameters */
int force_sensor;
-int dump_bridge;
+static int dump_bridge;
int dump_sensor;
static const __devinitdata struct usb_device_id m5602_table[] = {
@@ -80,6 +80,97 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
return (err < 0) ? err : 0;
}
+int m5602_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ if (!len || len > sd->sensor->i2c_regW)
+ return -EINVAL;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ if (sd->sensor->i2c_regW == 1) {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, len);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+ if (err < 0)
+ goto out;
+ } else {
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+ }
+
+ for (i = 0; (i < len) && !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(D_CONF, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+out:
+ return err;
+}
+
+int m5602_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > sd->sensor->i2c_regW || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
/* Dump all the registers of the m5602 bridge,
unfortunately this breaks the camera until it's power cycled */
static void m5602_dump_bridge(struct sd *sd)
@@ -150,11 +241,15 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
/* Send start command to the camera */
const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+
+ if (sd->sensor->start)
+ sd->sensor->start(sd);
+
memcpy(buf, buffer, sizeof(buffer));
err = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0x04, 0x40, 0x19, 0x0000, buf,
- 4, M5602_URB_MSG_TIMEOUT);
+ sizeof(buffer), M5602_URB_MSG_TIMEOUT);
PDEBUG(D_STREAM, "Transfer started");
return (err < 0) ? err : 0;
@@ -284,6 +379,7 @@ static int __init mod_m5602_init(void)
PDEBUG(D_PROBE, "registered");
return 0;
}
+
static void __exit mod_m5602_exit(void)
{
usb_deregister(&sd_driver);
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
index fb700c2d055a..c0e71c331454 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -18,6 +18,8 @@
#include "m5602_mt9m111.h"
+static void mt9m111_dump_registers(struct sd *sd);
+
int mt9m111_probe(struct sd *sd)
{
u8 data[2] = {0x00, 0x00};
@@ -44,12 +46,12 @@ int mt9m111_probe(struct sd *sd)
} else {
data[0] = preinit_mt9m111[i][2];
data[1] = preinit_mt9m111[i][3];
- mt9m111_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_mt9m111[i][1], data, 2);
}
}
- if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ if (m5602_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
return -ENODEV;
if ((data[0] == 0x14) && (data[1] == 0x3a)) {
@@ -72,7 +74,7 @@ int mt9m111_init(struct sd *sd)
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) {
u8 data[2];
if (init_mt9m111[i][0] == BRIDGE) {
@@ -82,7 +84,7 @@ int mt9m111_init(struct sd *sd)
} else {
data[0] = init_mt9m111[i][2];
data[1] = init_mt9m111[i][3];
- err = mt9m111_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_mt9m111[i][1], data, 2);
}
}
@@ -104,12 +106,12 @@ int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_ROWS;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -121,19 +123,19 @@ int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfe) | val;
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -142,12 +144,12 @@ int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
*val = data[0] & MT9M111_RMB_MIRROR_COLS;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -159,19 +161,19 @@ int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
- err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
if (err < 0)
goto out;
data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02);
- err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
data, 2);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -180,7 +182,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2] = {0x00, 0x00};
struct sd *sd = (struct sd *) gspca_dev;
- err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
+ err = m5602_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
tmp = ((data[1] << 8) | data[0]);
*val = ((tmp & (1 << 10)) * 2) |
@@ -190,7 +192,7 @@ int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -200,7 +202,7 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
/* Set the correct page map */
- err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
if (err < 0)
goto out;
@@ -225,90 +227,13 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
data[1], data[0]);
- err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
data, 2);
out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len) {
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a);
- if (err < 0)
- goto out;
-
- for (i = 0; i < len && !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x contains 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger
- than 16 bits has yet been seen, nor with 0 :p*/
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
+ return err;
}
-void mt9m111_dump_registers(struct sd *sd)
+static void mt9m111_dump_registers(struct sd *sd)
{
u8 address, value[2] = {0x00, 0x00};
@@ -316,27 +241,27 @@ void mt9m111_dump_registers(struct sd *sd)
info("Dumping the mt9m111 sensor core registers");
value[1] = MT9M111_SENSOR_CORE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 color pipeline registers");
value[1] = MT9M111_COLORPIPE;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
info("Dumping the mt9m111 camera control registers");
value[1] = MT9M111_CAMERA_CONTROL;
- mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ m5602_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
for (address = 0; address < 0xff; address++) {
- mt9m111_read_sensor(sd, address, value, 2);
+ m5602_read_sensor(sd, address, value, 2);
info("register 0x%x contains 0x%x%x",
address, value[0], value[1]);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
index 315209d5aeef..e795ab7a36c9 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -87,14 +87,6 @@ int mt9m111_probe(struct sd *sd);
int mt9m111_init(struct sd *sd);
int mt9m111_power_down(struct sd *sd);
-int mt9m111_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-int mt9m111_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void mt9m111_dump_registers(struct sd *sd);
-
int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -106,14 +98,12 @@ static struct m5602_sensor mt9m111 = {
.name = "MT9M111",
.i2c_slave_id = 0xba,
+ .i2c_regW = 2,
.probe = mt9m111_probe,
.init = mt9m111_init,
.power_down = mt9m111_power_down,
- .read_sensor = mt9m111_read_sensor,
- .write_sensor = mt9m111_write_sensor,
-
.nctrls = 3,
.ctrls = {
{
@@ -1003,7 +993,7 @@ static const unsigned char init_mt9m111[][4] =
{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, /* 639*/
{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
index 837c7e47661c..c908a8d6970a 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -18,77 +18,57 @@
#include "m5602_ov9650.h"
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- /* The ov9650 registers have a max depth of one byte */
- if (len > 1 || !len)
- return -EINVAL;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- ov9650.i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The ov9650 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+/* Vertically and horizontally flips the image if matched, needed for machines
+ where the sensor is mounted upside down */
+static
+ const
+ struct dmi_system_id ov9650_flip_dmi_table[] = {
+ {
+ .ident = "ASUS A6VC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+ }
+ },
+ {
+ .ident = "ASUS A6VM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+ }
+ },
+ {
+ .ident = "ASUS A6JC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+ }
+ },
+ {
+ .ident = "ASUS A6Ja",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
+ }
+ },
+ {
+ .ident = "ASUS A6Kt",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+ }
+ },
+ {
+ .ident = "Alienware Aurora m9700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
+ }
+ },
+ { }
+};
- return (err < 0) ? err : 0;
-}
+static void ov9650_dump_registers(struct sd *sd);
int ov9650_probe(struct sd *sd)
{
@@ -110,16 +90,16 @@ int ov9650_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
u8 data = preinit_ov9650[i][2];
if (preinit_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_ov9650[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_ov9650[i][1], data);
}
- if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
return -ENODEV;
- if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x96) && (ver_id == 0x52)) {
@@ -148,34 +128,90 @@ int ov9650_init(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
data = init_ov9650[i][2];
if (init_ov9650[i][0] == SENSOR)
- err = ov9650_write_sensor(sd, init_ov9650[i][1],
+ err = m5602_write_sensor(sd, init_ov9650[i][1],
&data, 1);
else
err = m5602_write_bridge(sd, init_ov9650[i][1], data);
}
- if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+ if (dmi_check_system(ov9650_flip_dmi_table) && !err) {
info("vflip quirk active");
data = 0x30;
- err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &data, 1);
}
+ return err;
+}
+
+int ov9650_start(struct sd *sd)
+{
+ int i, err = 0;
+ struct cam *cam = &sd->gspca_dev.cam;
- return (err < 0) ? err : 0;
+ for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
+ u8 data = res_init_ov9650[i][1];
+ err = m5602_write_bridge(sd, res_init_ov9650[i][0], data);
+ }
+ if (err < 0)
+ return err;
+
+ switch (cam->cam_mode[sd->gspca_dev.curr_mode].width)
+ {
+ case 640:
+ PDEBUG(D_V4L2, "Configuring camera for VGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(VGA_ov9650) && !err; i++) {
+ u8 data = VGA_ov9650[i][2];
+ if (VGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ VGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, VGA_ov9650[i][1], data);
+ }
+ break;
+
+ case 352:
+ PDEBUG(D_V4L2, "Configuring camera for CIF mode");
+
+ for (i = 0; i < ARRAY_SIZE(CIF_ov9650) && !err; i++) {
+ u8 data = CIF_ov9650[i][2];
+ if (CIF_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ CIF_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, CIF_ov9650[i][1], data);
+ }
+ break;
+
+ case 320:
+ PDEBUG(D_V4L2, "Configuring camera for QVGA mode");
+
+ for (i = 0; i < ARRAY_SIZE(QVGA_ov9650) && !err; i++) {
+ u8 data = QVGA_ov9650[i][2];
+ if (QVGA_ov9650[i][0] == SENSOR)
+ err = m5602_write_sensor(sd,
+ QVGA_ov9650[i][1], &data, 1);
+ else
+ err = m5602_write_bridge(sd, QVGA_ov9650[i][1], data);
+ }
+ break;
+ }
+ return err;
}
int ov9650_power_down(struct sd *sd)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+ int i, err = 0;
+ for (i = 0; i < ARRAY_SIZE(power_down_ov9650) && !err; i++) {
u8 data = power_down_ov9650[i][2];
if (power_down_ov9650[i][0] == SENSOR)
- ov9650_write_sensor(sd,
+ err = m5602_write_sensor(sd,
power_down_ov9650[i][1], &data, 1);
else
- m5602_write_bridge(sd, power_down_ov9650[i][1], data);
+ err = m5602_write_bridge(sd, power_down_ov9650[i][1],
+ data);
}
- return 0;
+ return err;
}
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -184,24 +220,24 @@ int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
if (err < 0)
goto out;
*val = i2c_data & 0x03;
- err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data << 2);
- err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
if (err < 0)
goto out;
*val |= (i2c_data & 0x3f) << 10;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -215,24 +251,24 @@ int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
/* The 6 MSBs */
i2c_data = (val >> 10) & 0x3f;
- err = ov9650_write_sensor(sd, OV9650_AECHM,
+ err = m5602_write_sensor(sd, OV9650_AECHM,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 8 middle bits */
i2c_data = (val >> 2) & 0xff;
- err = ov9650_write_sensor(sd, OV9650_AECH,
+ err = m5602_write_sensor(sd, OV9650_AECH,
&i2c_data, 1);
if (err < 0)
goto out;
/* The 2 LSBs */
i2c_data = val & 0x03;
- err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -241,13 +277,13 @@ int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -259,16 +295,16 @@ int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
/* The 2 MSB */
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) |
(i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return err;
}
int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -277,12 +313,12 @@ int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_RED, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -295,9 +331,9 @@ int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -306,12 +342,12 @@ int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -324,9 +360,9 @@ int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
val & 0xff);
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -335,14 +371,14 @@ int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1;
else
*val = (i2c_data & OV9650_HFLIP) >> 5;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -352,20 +388,20 @@ int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
if (dmi_check_system(ov9650_flip_dmi_table))
i2c_data = ((i2c_data & 0xdf) |
- (((val ? 0 : 1) & 0x01) << 5));
+ (((val ? 0 : 1) & 0x01) << 5));
else
i2c_data = ((i2c_data & 0xdf) |
- ((val & 0x01) << 5));
+ ((val & 0x01) << 5));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -374,14 +410,14 @@ int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (dmi_check_system(ov9650_flip_dmi_table))
*val = ((i2c_data & 0x10) >> 4) ? 0 : 1;
else
*val = (i2c_data & 0x10) >> 4;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -391,7 +427,7 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
if (err < 0)
goto out;
@@ -402,9 +438,9 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = ((i2c_data & 0xef) |
((val & 0x01) << 4));
- err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -413,16 +449,16 @@ int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data & 0x03) << 8;
- err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -435,22 +471,22 @@ int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
/* Read the OV9650_VREF register first to avoid
corrupting the VREF high and low bits */
- err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* Mask away all uninteresting bits */
i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F);
- err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
if (err < 0)
goto out;
/* The 8 LSBs */
i2c_data = val & 0xff;
- err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -459,11 +495,11 @@ int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AWB_EN) >> 1;
PDEBUG(D_V4L2, "Read auto white balance %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -473,14 +509,14 @@ int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -489,11 +525,11 @@ int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
*val = (i2c_data & OV9650_AGC_EN) >> 2;
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -503,23 +539,23 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
- err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
if (err < 0)
goto out;
i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
- err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void ov9650_dump_registers(struct sd *sd)
+static void ov9650_dump_registers(struct sd *sd)
{
int address;
info("Dumping the ov9650 register state");
for (address = 0; address < 0xa9; address++) {
u8 value;
- ov9650_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -531,9 +567,9 @@ void ov9650_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- ov9650_read_sensor(sd, address, &old_value, 1);
- ov9650_write_sensor(sd, address, test_value, 1);
- ov9650_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -541,6 +577,6 @@ void ov9650_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- ov9650_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h
index 065632f0378e..f4b33b8e8dae 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.h
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -20,7 +20,6 @@
#define M5602_OV9650_H_
#include <linux/dmi.h>
-
#include "m5602_sensor.h"
/*****************************************************************************/
@@ -36,6 +35,7 @@
#define OV9650_PID 0x0a
#define OV9650_VER 0x0b
#define OV9650_COM3 0x0c
+#define OV9650_COM4 0x0d
#define OV9650_COM5 0x0e
#define OV9650_COM6 0x0f
#define OV9650_AECH 0x10
@@ -94,6 +94,8 @@
#define OV9650_REGISTER_RESET (1 << 7)
#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_CIF_SELECT (1 << 5)
+#define OV9650_QVGA_SELECT (1 << 4)
#define OV9650_RGB_SELECT (1 << 2)
#define OV9650_RAW_RGB_SELECT (1 << 0)
@@ -108,6 +110,8 @@
#define OV9650_SYSTEM_CLK_SEL (1 << 7)
#define OV9650_SLAM_MODE (1 << 4)
+#define OV9650_QVGA_VARIOPIXEL (1 << 7)
+
#define OV9650_VFLIP (1 << 4)
#define OV9650_HFLIP (1 << 5)
@@ -124,15 +128,9 @@ extern int dump_sensor;
int ov9650_probe(struct sd *sd);
int ov9650_init(struct sd *sd);
+int ov9650_start(struct sd *sd);
int ov9650_power_down(struct sd *sd);
-int ov9650_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int ov9650_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
-void ov9650_dump_registers(struct sd *sd);
-
int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -155,11 +153,11 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static struct m5602_sensor ov9650 = {
.name = "OV9650",
.i2c_slave_id = 0x60,
+ .i2c_regW = 1,
.probe = ov9650_probe,
.init = ov9650_init,
+ .start = ov9650_start,
.power_down = ov9650_power_down,
- .read_sensor = ov9650_read_sensor,
- .write_sensor = ov9650_write_sensor,
.nctrls = 8,
.ctrls = {
@@ -264,18 +262,38 @@ static struct m5602_sensor ov9650 = {
}
},
- .nmodes = 1,
+ .nmodes = 3,
.modes = {
{
- M5602_DEFAULT_FRAME_WIDTH,
- M5602_DEFAULT_FRAME_HEIGHT,
+ 320,
+ 240,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }, {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }, {
+ 640,
+ 480,
V4L2_PIX_FMT_SBGGR8,
V4L2_FIELD_NONE,
.sizeimage =
- M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
- .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ 640 * 480,
+ .bytesperline = 640,
.colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1
+ .priv = 0
}
}
};
@@ -324,6 +342,7 @@ static const unsigned char init_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
/* Reset chip */
{SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
/* Enable double clock */
@@ -331,8 +350,6 @@ static const unsigned char init_ov9650[][3] =
/* Do something out of spec with the power */
{SENSOR, OV9650_OFON, 0x40},
- /* Set QQVGA */
- {SENSOR, OV9650_COM1, 0x20},
/* Set fast AGC/AEC algorithm with unlimited step size */
{SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
OV9650_AEC_UNLIM_STEP_SIZE |
@@ -343,7 +360,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_ACOM38, 0x81},
/* Turn off color matrix coefficient double option */
{SENSOR, OV9650_COM16, 0x00},
- /* Enable color matrix for RGB/YUV, Delay Y channel,
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
set output Y/UV delay to 1 */
{SENSOR, OV9650_COM13, 0x19},
/* Enable digital BLC, Set output mode to U Y V Y */
@@ -352,7 +369,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_COM24, 0x00},
/* Enable HREF and some out of spec things */
{SENSOR, OV9650_COM12, 0x73},
- /* Set all DBLC offset signs to positive and
+ /* Set all DBLC offset signs to positive and
do some out of spec stuff */
{SENSOR, OV9650_DBLC1, 0xdf},
{SENSOR, OV9650_COM21, 0x06},
@@ -364,7 +381,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_RSVD96, 0x04},
/* Enable full range output */
{SENSOR, OV9650_COM15, 0x0},
- /* Enable HREF at optical black, enable ADBLC bias,
+ /* Enable HREF at optical black, enable ADBLC bias,
enable ADBLC, reset timings at format change */
{SENSOR, OV9650_COM6, 0x4b},
/* Subtract 32 from the B channel bias */
@@ -385,7 +402,7 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_AEB, 0x5c},
/* Set the high and low limit nibbles to 3 */
{SENSOR, OV9650_VPT, 0xc3},
- /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
drop VSYNC at frame drop,
limit exposure timing,
drop frame when the AEC step is larger than the exposure gap */
@@ -394,9 +411,9 @@ static const unsigned char init_ov9650[][3] =
and set PWDN to SLVS (slave mode vertical sync) */
{SENSOR, OV9650_COM10, 0x42},
/* Set horizontal column start high to default value */
- {SENSOR, OV9650_HSTART, 0x1a},
+ {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
/* Set horizontal column end */
- {SENSOR, OV9650_HSTOP, 0xbf},
+ {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
/* Complementing register to the two writes above */
{SENSOR, OV9650_HREF, 0xb2},
/* Set vertical row start high bits */
@@ -405,10 +422,6 @@ static const unsigned char init_ov9650[][3] =
{SENSOR, OV9650_VSTOP, 0x7e},
/* Set complementing vertical frame control */
{SENSOR, OV9650_VREF, 0x10},
- /* Set raw RGB output format with VGA resolution */
- {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
- OV9650_RGB_SELECT |
- OV9650_RAW_RGB_SELECT},
{SENSOR, OV9650_ADC, 0x04},
{SENSOR, OV9650_HV, 0x40},
/* Enable denoise, and white-pixel erase */
@@ -417,30 +430,15 @@ static const unsigned char init_ov9650[][3] =
/* Set the high bits of the exposure value */
{SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+ /* Enable VARIOPIXEL */
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
/* Set the low bits of the exposure value */
{SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
{SENSOR, OV9650_GAIN, GAIN_DEFAULT},
{SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
{SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
-
- {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
- {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL},
-
- {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
- {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
- {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
- {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
- {BRIDGE, M5602_XB_SIG_INI, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
- {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
- {BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
};
static const unsigned char power_down_ov9650[][3] =
@@ -460,43 +458,76 @@ static const unsigned char power_down_ov9650[][3] =
{BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
- {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
};
-/* Vertically and horizontally flips the image if matched, needed for machines
- where the sensor is mounted upside down */
-static
- const
- struct dmi_system_id ov9650_flip_dmi_table[] = {
- {
- .ident = "ASUS A6VC",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
- }
- },
- {
- .ident = "ASUS A6VM",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
- }
- },
- {
- .ident = "ASUS A6JC",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
- }
- },
- {
- .ident = "ASUS A6Kt",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
- }
- },
- { }
+static const unsigned char res_init_ov9650[][2] =
+{
+ {M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {M5602_XB_PIX_OF_LINE_H, 0x82},
+ {M5602_XB_PIX_OF_LINE_L, 0x00},
+ {M5602_XB_SIG_INI, 0x01}
+};
+
+static const unsigned char VGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, /* 640 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xe2},
+
+ {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char CIF_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x20}, /* 288 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 352 + 98 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xc2},
+
+ {SENSOR, OV9650_COM7, OV9650_CIF_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+};
+
+static const unsigned char QVGA_ov9650[][3] =
+{
+ /* Moves the view window in a vertical orientation */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xf0}, /* 240 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x31}, /* 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 320 + 50 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x71},
+
+ {SENSOR, OV9650_COM7, OV9650_QVGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
};
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
index d17ac52566e6..2e7fb91673cf 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -18,6 +18,8 @@
#include "m5602_po1030.h"
+static void po1030_dump_registers(struct sd *sd);
+
int po1030_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0, i;
@@ -38,16 +40,16 @@ int po1030_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
u8 data = preinit_po1030[i][2];
if (preinit_po1030[i][0] == SENSOR)
- po1030_write_sensor(sd,
+ m5602_write_sensor(sd,
preinit_po1030[i][1], &data, 1);
else
m5602_write_bridge(sd, preinit_po1030[i][1], data);
}
- if (po1030_read_sensor(sd, 0x3, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x3, &prod_id, 1))
return -ENODEV;
- if (po1030_read_sensor(sd, 0x4, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x4, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0x02) && (ver_id == 0xef)) {
@@ -64,78 +66,12 @@ sensor_found:
return 0;
}
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
- m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
-
- for (i = 0; i < len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
- return (err < 0) ? err : 0;
-}
-
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* The po1030 only supports one byte writes */
- if (len > 1 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the footer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int po1030_init(struct sd *sd)
{
int i, err = 0;
/* Init the sensor */
- for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+ for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
u8 data[2] = {0x00, 0x00};
switch (init_po1030[i][0]) {
@@ -147,16 +83,10 @@ int po1030_init(struct sd *sd)
case SENSOR:
data[0] = init_po1030[i][2];
- err = po1030_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_po1030[i][1], data, 1);
break;
- case SENSOR_LONG:
- data[0] = init_po1030[i][2];
- data[1] = init_po1030[i][3];
- err = po1030_write_sensor(sd,
- init_po1030[i][1], data, 2);
- break;
default:
info("Invalid stream command, exiting init");
return -EINVAL;
@@ -166,7 +96,7 @@ int po1030_init(struct sd *sd)
if (dump_sensor)
po1030_dump_registers(sd);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
@@ -175,19 +105,19 @@ int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
*val = (i2c_data << 8);
- err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
*val |= i2c_data;
PDEBUG(D_V4L2, "Exposure read as %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -202,7 +132,7 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_H,
&i2c_data, 1);
if (err < 0)
goto out;
@@ -210,11 +140,11 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = (val & 0xff);
PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x",
i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M,
+ err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_M,
&i2c_data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -223,12 +153,12 @@ int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read global gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -237,14 +167,14 @@ int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_CONTROL2,
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2,
&i2c_data, 1);
*val = (i2c_data >> 7) & 0x01 ;
PDEBUG(D_V4L2, "Read hflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -254,13 +184,17 @@ int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set hflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 7;
+ i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -269,14 +203,14 @@ int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
*val = (i2c_data >> 6) & 0x01;
PDEBUG(D_V4L2, "Read vflip %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -286,13 +220,17 @@ int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vflip %d", val);
+ err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1);
+ if (err < 0)
+ goto out;
- i2c_data = (val & 0x01) << 6;
+ i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6);
- err = po1030_write_sensor(sd, PO1030_REG_CONTROL2,
- &i2c_data, 1);
+ err = m5602_write_sensor(sd, PO1030_REG_CONTROL2,
+ &i2c_data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -303,9 +241,9 @@ int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set global gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_GLOBALGAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -314,11 +252,11 @@ int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read red gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -329,9 +267,9 @@ int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set red gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_RED_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
@@ -340,12 +278,12 @@ int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
u8 i2c_data;
int err;
- err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_read_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
*val = i2c_data;
PDEBUG(D_V4L2, "Read blue gain %d", *val);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
@@ -355,10 +293,10 @@ int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
int err;
i2c_data = val & 0xff;
PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data);
- err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN,
+ err = m5602_write_sensor(sd, PO1030_REG_BLUE_GAIN,
&i2c_data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int po1030_power_down(struct sd *sd)
@@ -366,14 +304,14 @@ int po1030_power_down(struct sd *sd)
return 0;
}
-void po1030_dump_registers(struct sd *sd)
+static void po1030_dump_registers(struct sd *sd)
{
int address;
u8 value = 0;
info("Dumping the po1030 sensor core registers");
for (address = 0; address < 0x7f; address++) {
- po1030_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -385,9 +323,9 @@ void po1030_dump_registers(struct sd *sd)
u8 old_value, ctrl_value;
u8 test_value[2] = {0xff, 0xff};
- po1030_read_sensor(sd, address, &old_value, 1);
- po1030_write_sensor(sd, address, test_value, 1);
- po1030_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value[0])
info("register 0x%x is writeable", address);
@@ -395,6 +333,6 @@ void po1030_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- po1030_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h
index a0b75ff61d79..def39d5bcec6 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.h
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -10,7 +10,7 @@
* v4l2 interface modeled after the V4L2 driver
* for SN9C10x PC Camera Controllers
*
- * Register defines taken from Pascal Stangs Proxycon Armlib
+ * Register defines taken from Pascal Stangs Procyon Armlib
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -128,13 +128,6 @@ int po1030_probe(struct sd *sd);
int po1030_init(struct sd *sd);
int po1030_power_down(struct sd *sd);
-void po1030_dump_registers(struct sd *sd);
-
-int po1030_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int po1030_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
@@ -152,6 +145,7 @@ static struct m5602_sensor po1030 = {
.name = "PO1030",
.i2c_slave_id = 0xdc,
+ .i2c_regW = 1,
.probe = po1030_probe,
.init = po1030_init,
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
index 14b1eac5b812..e564a61a72d7 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -18,6 +18,40 @@
#include "m5602_s5k4aa.h"
+static
+ const
+ struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+ {
+ .ident = "Fujitsu-Siemens Amilo Xa 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2550",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+ }
+ }, {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+ }
+ }, {
+ .ident = "MSI GX700/GX705/EX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
+ }
+ },
+ { }
+};
+
+static void s5k4aa_dump_registers(struct sd *sd);
+
int s5k4aa_probe(struct sd *sd)
{
u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@@ -49,7 +83,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR:
data[0] = preinit_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 1);
break;
@@ -57,7 +91,7 @@ int s5k4aa_probe(struct sd *sd)
case SENSOR_LONG:
data[0] = preinit_s5k4aa[i][2];
data[1] = preinit_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
preinit_s5k4aa[i][1],
data, 2);
break;
@@ -68,13 +102,14 @@ int s5k4aa_probe(struct sd *sd)
}
/* Test some registers, but we don't know their exact meaning yet */
- if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
+ if (m5602_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
return -ENODEV;
if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
return -ENODEV;
else
info("Detected a s5k4aa sensor");
+
sensor_found:
sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
@@ -84,90 +119,6 @@ sensor_found:
return 0;
}
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- for (i = 0; (i < len) & !err; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k4aa_init(struct sd *sd)
{
int i, err = 0;
@@ -184,14 +135,14 @@ int s5k4aa_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k4aa[i][2];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k4aa[i][2];
data[1] = init_s5k4aa[i][3];
- err = s5k4aa_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k4aa[i][1], data, 2);
break;
default:
@@ -206,21 +157,21 @@ int s5k4aa_init(struct sd *sd)
if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
u8 data = 0x02;
info("vertical flip quirk active");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
- s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
data |= S5K4AA_RM_V_FLIP;
data &= ~S5K4AA_RM_H_FLIP;
- s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
/* Decrement COLSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
data--;
- s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
/* Increment ROWSTART to preserve color order (BGGR) */
- s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
data++;
- s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
return (err < 0) ? err : 0;
@@ -237,20 +188,20 @@ int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
*val = data << 8;
- err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
*val |= data;
PDEBUG(D_V4L2, "Read exposure %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
@@ -260,17 +211,17 @@ int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set exposure to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = (val >> 8) & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -279,16 +230,16 @@ int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_V_FLIP) >> 7;
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -298,35 +249,35 @@ int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_V_FLIP)
| ((val & 0x01) << 7));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -335,15 +286,15 @@ int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
*val = (data & S5K4AA_RM_H_FLIP) >> 6;
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -354,35 +305,35 @@ int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
PDEBUG(D_V4L2, "Set horizontal flip to %d",
val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
- err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
if (err < 0)
goto out;
if (val) {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data++;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
} else {
- err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
if (err < 0)
goto out;
data--;
- err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
}
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -391,16 +342,16 @@ int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data = S5K4AA_PAGE_MAP_2;
int err;
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
- err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
*val = data;
PDEBUG(D_V4L2, "Read gain %d", *val);
out:
- return (err < 0) ? err : 0;
+ return err;
}
int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -410,28 +361,28 @@ int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
int err;
PDEBUG(D_V4L2, "Set gain to %d", val);
- err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
if (err < 0)
goto out;
data = val & 0xff;
- err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
out:
- return (err < 0) ? err : 0;
+ return err;
}
-void s5k4aa_dump_registers(struct sd *sd)
+static void s5k4aa_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Dumping the s5k4aa register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 value = 0;
- s5k4aa_read_sensor(sd, address, &value, 1);
+ m5602_read_sensor(sd, address, &value, 1);
info("register 0x%x contains 0x%x",
address, value);
}
@@ -439,15 +390,15 @@ void s5k4aa_dump_registers(struct sd *sd)
info("s5k4aa register state dump complete");
for (page = 0; page < 16; page++) {
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
info("Probing for which registers that are "
"read/write for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_value, ctrl_value, test_value = 0xff;
- s5k4aa_read_sensor(sd, address, &old_value, 1);
- s5k4aa_write_sensor(sd, address, &test_value, 1);
- s5k4aa_read_sensor(sd, address, &ctrl_value, 1);
+ m5602_read_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &test_value, 1);
+ m5602_read_sensor(sd, address, &ctrl_value, 1);
if (ctrl_value == test_value)
info("register 0x%x is writeable", address);
@@ -455,9 +406,9 @@ void s5k4aa_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original value */
- s5k4aa_write_sensor(sd, address, &old_value, 1);
+ m5602_write_sensor(sd, address, &old_value, 1);
}
}
info("Read/write register probing complete");
- s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
index eaef67655afa..1f88b0d040c4 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -41,11 +41,10 @@
#define S5K4AA_WINDOW_HEIGHT_LO 0x09
#define S5K4AA_WINDOW_WIDTH_HI 0x0a
#define S5K4AA_WINDOW_WIDTH_LO 0x0b
-#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */
-#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost
- if too low, reduces frame rate
- if too high */
-#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */
+#define S5K4AA_GLOBAL_GAIN__ 0x0f
+/* sync lost, if too low, reduces frame rate if too high */
+#define S5K4AA_H_BLANK_HI__ 0x1d
+#define S5K4AA_H_BLANK_LO__ 0x1e
#define S5K4AA_EXPOSURE_HI 0x17
#define S5K4AA_EXPOSURE_LO 0x18
#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */
@@ -68,13 +67,6 @@ int s5k4aa_probe(struct sd *sd);
int s5k4aa_init(struct sd *sd);
int s5k4aa_power_down(struct sd *sd);
-void s5k4aa_dump_registers(struct sd *sd);
-
-int s5k4aa_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k4aa_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
@@ -89,9 +81,8 @@ static struct m5602_sensor s5k4aa = {
.probe = s5k4aa_probe,
.init = s5k4aa_init,
.power_down = s5k4aa_power_down,
- .read_sensor = s5k4aa_read_sensor,
- .write_sensor = s5k4aa_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 4,
.ctrls = {
{
@@ -338,32 +329,4 @@ static const unsigned char init_s5k4aa[][4] =
{SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00}
};
-static
- const
- struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
- {
- .ident = "Fujitsu-Siemens Amilo Xa 2528",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
- }
- },
- {
- .ident = "Fujitsu-Siemens Amilo Xi 2550",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
- }
- },
- {
- .ident = "MSI GX700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
- DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
- DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
- }
- },
- { }
-};
-
#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 8988a728e0b4..af3f2dc2c702 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -18,6 +18,8 @@
#include "m5602_s5k83a.h"
+static void s5k83a_dump_registers(struct sd *sd);
+
int s5k83a_probe(struct sd *sd)
{
u8 prod_id = 0, ver_id = 0;
@@ -39,7 +41,7 @@ int s5k83a_probe(struct sd *sd)
for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
if (preinit_s5k83a[i][0] == SENSOR)
- err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1],
+ err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
data, 2);
else
err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
@@ -49,10 +51,10 @@ int s5k83a_probe(struct sd *sd)
/* We don't know what register (if any) that contain the product id
* Just pick the first addresses that seem to produce the same results
* on multiple machines */
- if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+ if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
return -ENODEV;
- if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+ if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
return -ENODEV;
if ((prod_id == 0xff) || (ver_id == 0xff))
@@ -68,91 +70,6 @@ sensor_found:
return 0;
}
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
- sd->sensor->i2c_slave_id);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
- if (err < 0)
- goto out;
-
- err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
- if (err < 0)
- goto out;
-
- do {
- err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
- } while ((*i2c_data & I2C_BUSY) && !err);
-
- if (err < 0)
- goto out;
- for (i = 0; i < len && !len; i++) {
- err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
-
- PDEBUG(D_CONF, "Reading sensor register "
- "0x%x containing 0x%x ", address, *i2c_data);
- }
-
-out:
- return (err < 0) ? err : 0;
-}
-
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len)
-{
- int err, i;
- u8 *p;
- struct usb_device *udev = sd->gspca_dev.dev;
- __u8 *buf = sd->gspca_dev.usb_buf;
-
- /* No sensor with a data width larger than 16 bits has yet been seen */
- if (len > 2 || !len)
- return -EINVAL;
-
- memcpy(buf, sensor_urb_skeleton,
- sizeof(sensor_urb_skeleton));
-
- buf[11] = sd->sensor->i2c_slave_id;
- buf[15] = address;
-
- /* Special case larger sensor writes */
- p = buf + 16;
-
- /* Copy a four byte write sequence for each byte to be written to */
- for (i = 0; i < len; i++) {
- memcpy(p, sensor_urb_skeleton + 16, 4);
- p[3] = i2c_data[i];
- p += 4;
- PDEBUG(D_CONF, "Writing sensor register 0x%x with 0x%x",
- address, i2c_data[i]);
- }
-
- /* Copy the tailer */
- memcpy(p, sensor_urb_skeleton + 20, 4);
-
- /* Set the total length */
- p[3] = 0x10 + len;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- 0x04, 0x40, 0x19,
- 0x0000, buf,
- 20 + len * 4, M5602_URB_MSG_TIMEOUT);
-
- return (err < 0) ? err : 0;
-}
-
int s5k83a_init(struct sd *sd)
{
int i, err = 0;
@@ -169,14 +86,14 @@ int s5k83a_init(struct sd *sd)
case SENSOR:
data[0] = init_s5k83a[i][2];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 1);
break;
case SENSOR_LONG:
data[0] = init_s5k83a[i][2];
data[1] = init_s5k83a[i][3];
- err = s5k83a_write_sensor(sd,
+ err = m5602_write_sensor(sd,
init_s5k83a[i][1], data, 2);
break;
default:
@@ -200,14 +117,14 @@ void s5k83a_dump_registers(struct sd *sd)
{
int address;
u8 page, old_page;
- s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Dumping the s5k83a register state for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 val = 0;
- s5k83a_read_sensor(sd, address, &val, 1);
+ m5602_read_sensor(sd, address, &val, 1);
info("register 0x%x contains 0x%x",
address, val);
}
@@ -215,15 +132,15 @@ void s5k83a_dump_registers(struct sd *sd)
info("s5k83a register state dump complete");
for (page = 0; page < 16; page++) {
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
info("Probing for which registers that are read/write "
"for page 0x%x", page);
for (address = 0; address <= 0xff; address++) {
u8 old_val, ctrl_val, test_val = 0xff;
- s5k83a_read_sensor(sd, address, &old_val, 1);
- s5k83a_write_sensor(sd, address, &test_val, 1);
- s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+ m5602_read_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &test_val, 1);
+ m5602_read_sensor(sd, address, &ctrl_val, 1);
if (ctrl_val == test_val)
info("register 0x%x is writeable", address);
@@ -231,11 +148,11 @@ void s5k83a_dump_registers(struct sd *sd)
info("register 0x%x is read only", address);
/* Restore original val */
- s5k83a_write_sensor(sd, address, &old_val, 1);
+ m5602_write_sensor(sd, address, &old_val, 1);
}
}
info("Read/write register probing complete");
- s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+ m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
}
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -244,11 +161,15 @@ int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ if (err < 0)
+ goto out;
+
data[1] = data[1] << 1;
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -259,23 +180,24 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0x00;
data[1] = 0x20;
- err = s5k83a_write_sensor(sd, 0x14, data, 2);
+ err = m5602_write_sensor(sd, 0x14, data, 2);
if (err < 0)
- return err;
+ goto out;
data[0] = 0x01;
data[1] = 0x00;
- err = s5k83a_write_sensor(sd, 0x0d, data, 2);
+ err = m5602_write_sensor(sd, 0x0d, data, 2);
if (err < 0)
- return err;
+ goto out;
/* FIXME: This is not sane, we need to figure out the composition
of these registers */
data[0] = val >> 3; /* brightness, high 5 bits */
data[1] = val >> 1; /* brightness, high 7 bits */
- err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
@@ -284,10 +206,14 @@ int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
u8 data;
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+ if (err < 0)
+ goto out;
*val = data;
- return (err < 0) ? err : 0;
+
+out:
+ return err;
}
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
@@ -297,9 +223,9 @@ int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = val;
- err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1);
- return (err < 0) ? err : 0;
+ return err;
}
int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
@@ -308,7 +234,9 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
u8 data[2];
struct sd *sd = (struct sd *) gspca_dev;
- err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
+ err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2);
+ if (err < 0)
+ goto out;
data[1] = data[1] & 0x3f;
if (data[1] > S5K83A_MAXIMUM_GAIN)
@@ -316,7 +244,8 @@ int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
*val = data[1];
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -327,9 +256,8 @@ int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
data[0] = 0;
data[1] = val;
- err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
+ return err;
}
int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -339,14 +267,15 @@ int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x40) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -356,25 +285,26 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero six bit, seven is hflip */
data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
: (data[0] & 0x80) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0b : 0x0a;
- err = s5k83a_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
@@ -384,14 +314,15 @@ int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
*val = (data[0] | 0x80) ? 1 : 0;
- return (err < 0) ? err : 0;
+out:
+ return err;
}
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -401,23 +332,23 @@ int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
data[0] = 0x05;
- err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
if (err < 0)
- return err;
+ goto out;
- err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
/* set or zero seven bit, six is vflip */
data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
: (data[0] & 0x40) | S5K83A_FLIP_MASK;
- err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
if (err < 0)
- return err;
+ goto out;
data[0] = (val) ? 0x0a : 0x0b;
- err = s5k83a_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
-
- return (err < 0) ? err : 0;
+ err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+out:
+ return err;
}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
index ee3ee9cfca1d..05ccb5b57a88 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -22,15 +22,15 @@
#include "m5602_sensor.h"
#define S5K83A_FLIP 0x01
-#define S5K83A_HFLIP_TUNE 0x03
-#define S5K83A_VFLIP_TUNE 0x05
-#define S5K83A_WHITENESS 0x0a
+#define S5K83A_HFLIP_TUNE 0x03
+#define S5K83A_VFLIP_TUNE 0x05
+#define S5K83A_WHITENESS 0x0a
#define S5K83A_GAIN 0x18
-#define S5K83A_BRIGHTNESS 0x1b
-#define S5K83A_PAGE_MAP 0xec
+#define S5K83A_BRIGHTNESS 0x1b
+#define S5K83A_PAGE_MAP 0xec
-#define S5K83A_DEFAULT_BRIGHTNESS 0x71
-#define S5K83A_DEFAULT_WHITENESS 0x7e
+#define S5K83A_DEFAULT_BRIGHTNESS 0x71
+#define S5K83A_DEFAULT_WHITENESS 0x7e
#define S5K83A_DEFAULT_GAIN 0x00
#define S5K83A_MAXIMUM_GAIN 0x3c
#define S5K83A_FLIP_MASK 0x10
@@ -46,13 +46,6 @@ int s5k83a_probe(struct sd *sd);
int s5k83a_init(struct sd *sd);
int s5k83a_power_down(struct sd *sd);
-void s5k83a_dump_registers(struct sd *sd);
-
-int s5k83a_read_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-int s5k83a_write_sensor(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
@@ -64,15 +57,13 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
-
static struct m5602_sensor s5k83a = {
.name = "S5K83A",
.probe = s5k83a_probe,
.init = s5k83a_init,
.power_down = s5k83a_power_down,
- .read_sensor = s5k83a_read_sensor,
- .write_sensor = s5k83a_write_sensor,
.i2c_slave_id = 0x5a,
+ .i2c_regW = 2,
.nctrls = 5,
.ctrls = {
{
diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h
index 60c9a48e0c02..261623f0da48 100644
--- a/drivers/media/video/gspca/m5602/m5602_sensor.h
+++ b/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -49,23 +49,21 @@ struct m5602_sensor {
/* What i2c address the sensor is connected to */
u8 i2c_slave_id;
+ /* Width of each i2c register (in bytes) */
+ u8 i2c_regW;
+
/* Probes if the sensor is connected */
int (*probe)(struct sd *sd);
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
+ /* Executed when the camera starts to send data */
+ int (*start)(struct sd *sd);
+
/* Performs a power down sequence */
int (*power_down)(struct sd *sd);
- /* Reads a sensor register */
- int (*read_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
- /* Writes to a sensor register */
- int (*write_sensor)(struct sd *sd, const u8 address,
- u8 *i2c_data, const u8 len);
-
int nctrls;
struct ctrl ctrls[M5602_MAX_CTRLS];
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 277ca34a8817..3d2090e67a63 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -39,7 +39,7 @@ struct sd {
static struct ctrl sd_ctrls[] = {
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 589,
@@ -123,7 +123,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->epaddr = 0x01;
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
sd->qindex = 1; /* set the quantization table */
return 0;
}
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index ca671194679e..ee232956c812 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -3,7 +3,18 @@
*
* Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
*
- * (This module is adapted from the ov51x-jpeg package)
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2004 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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
@@ -40,22 +51,18 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
/* Determined by sensor type */
- char sif;
-
- unsigned char primary_i2c_slave; /* I2C write id of sensor */
+ __u8 sif;
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
+ __u8 brightness;
+ __u8 contrast;
+ __u8 colors;
__u8 hflip;
__u8 vflip;
- char compress; /* Should the next frame be compressed? */
- char compress_inited; /* Are compression params uploaded? */
- char stopped; /* Streaming is temporarily paused */
+ __u8 stopped; /* Streaming is temporarily paused */
- char frame_rate; /* current Framerate (OV519 only) */
- char clockdiv; /* clockdiv override for OV519 only */
+ __u8 frame_rate; /* current Framerate (OV519 only) */
+ __u8 clockdiv; /* clockdiv override for OV519 only */
char sensor; /* Type of image sensor chip (SEN_*) */
#define SEN_UNKNOWN 0
@@ -67,7 +74,6 @@ struct sd {
#define SEN_OV7670 6
#define SEN_OV76BE 7
#define SEN_OV8610 8
-
};
/* V4L2 controls supported by the driver */
@@ -158,7 +164,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -170,7 +176,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -184,15 +190,15 @@ static struct v4l2_pix_format sif_mode[] = {
};
/* OV519 Camera interface register numbers */
-#define OV519_CAM_H_SIZE 0x10
-#define OV519_CAM_V_SIZE 0x11
-#define OV519_CAM_X_OFFSETL 0x12
-#define OV519_CAM_X_OFFSETH 0x13
-#define OV519_CAM_Y_OFFSETL 0x14
-#define OV519_CAM_Y_OFFSETH 0x15
-#define OV519_CAM_DIVIDER 0x16
-#define OV519_CAM_DFR 0x20
-#define OV519_CAM_FORMAT 0x25
+#define OV519_R10_H_SIZE 0x10
+#define OV519_R11_V_SIZE 0x11
+#define OV519_R12_X_OFFSETL 0x12
+#define OV519_R13_X_OFFSETH 0x13
+#define OV519_R14_Y_OFFSETL 0x14
+#define OV519_R15_Y_OFFSETH 0x15
+#define OV519_R16_DIVIDER 0x16
+#define OV519_R20_DFR 0x20
+#define OV519_R25_FORMAT 0x25
/* OV519 System Controller register numbers */
#define OV519_SYS_RESET1 0x51
@@ -562,8 +568,8 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_VSTOP, 0x7a },
{ OV7670_REG_VREF, 0x0a },
- { OV7670_REG_COM3, 0 },
- { OV7670_REG_COM14, 0 },
+ { OV7670_REG_COM3, 0x00 },
+ { OV7670_REG_COM14, 0x00 },
/* Mystery scaling numbers */
{ 0x70, 0x3a },
{ 0x71, 0x35 },
@@ -595,8 +601,8 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_COM8, OV7670_COM8_FASTAEC
| OV7670_COM8_AECSTEP
| OV7670_COM8_BFILT },
- { OV7670_REG_GAIN, 0 },
- { OV7670_REG_AECH, 0 },
+ { OV7670_REG_GAIN, 0x00 },
+ { OV7670_REG_AECH, 0x00 },
{ OV7670_REG_COM4, 0x40 }, /* magic reserved bit */
{ OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
{ OV7670_REG_BD50MAX, 0x05 },
@@ -634,16 +640,16 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ OV7670_REG_COM12, 0x78 },
{ 0x4d, 0x40 },
{ 0x4e, 0x20 },
- { OV7670_REG_GFIX, 0 },
+ { OV7670_REG_GFIX, 0x00 },
{ 0x6b, 0x4a },
{ 0x74, 0x10 },
{ 0x8d, 0x4f },
- { 0x8e, 0 },
- { 0x8f, 0 },
- { 0x90, 0 },
- { 0x91, 0 },
- { 0x96, 0 },
- { 0x9a, 0 },
+ { 0x8e, 0x00 },
+ { 0x8f, 0x00 },
+ { 0x90, 0x00 },
+ { 0x91, 0x00 },
+ { 0x96, 0x00 },
+ { 0x9a, 0x00 },
{ 0xb0, 0x84 },
{ 0xb1, 0x0c },
{ 0xb2, 0x0e },
@@ -681,17 +687,17 @@ static const struct ov_i2c_regvals norm_7670[] = {
/* Matrix coefficients */
{ 0x4f, 0x80 },
{ 0x50, 0x80 },
- { 0x51, 0 },
+ { 0x51, 0x00 },
{ 0x52, 0x22 },
{ 0x53, 0x5e },
{ 0x54, 0x80 },
{ 0x58, 0x9e },
{ OV7670_REG_COM16, OV7670_COM16_AWBGAIN },
- { OV7670_REG_EDGE, 0 },
+ { OV7670_REG_EDGE, 0x00 },
{ 0x75, 0x05 },
{ 0x76, 0xe1 },
- { 0x4c, 0 },
+ { 0x4c, 0x00 },
{ 0x77, 0x01 },
{ OV7670_REG_COM13, OV7670_COM13_GAMMA
| OV7670_COM13_UVSAT
@@ -704,7 +710,7 @@ static const struct ov_i2c_regvals norm_7670[] = {
{ 0x34, 0x11 },
{ OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
{ 0xa4, 0x88 },
- { 0x96, 0 },
+ { 0x96, 0x00 },
{ 0x97, 0x30 },
{ 0x98, 0x20 },
{ 0x99, 0x30 },
@@ -942,11 +948,11 @@ static int i2c_w(struct sd *sd,
/* Initiate 3-byte write cycle */
rc = reg_w(sd, R518_I2C_CTL, 0x01);
+ if (rc < 0)
+ return rc;
/* wait for write complete */
msleep(4);
- if (rc < 0)
- return rc;
return reg_r8(sd, R518_I2C_CTL);
}
@@ -1029,7 +1035,7 @@ static inline int ov51x_restart(struct sd *sd)
*/
static int init_ov_sensor(struct sd *sd)
{
- int i, success;
+ int i;
/* Reset the sensor */
if (i2c_w(sd, 0x12, 0x80) < 0)
@@ -1038,11 +1044,11 @@ static int init_ov_sensor(struct sd *sd)
/* Wait for it to initialize */
msleep(150);
- for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+ for (i = 0; i < i2c_detect_tries; i++) {
if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
- success = 1;
- continue;
+ PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+ return 0;
}
/* Reset the sensor */
@@ -1054,10 +1060,7 @@ static int init_ov_sensor(struct sd *sd)
if (i2c_r(sd, 0x00) < 0)
return -EIO;
}
- if (!success)
- return -EIO;
- PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
- return 0;
+ return -EIO;
}
/* Set the read and write slave IDs. The "slave" argument is the write slave,
@@ -1073,7 +1076,6 @@ static int ov51x_set_slave_ids(struct sd *sd,
rc = reg_w(sd, R51x_I2C_W_SID, slave);
if (rc < 0)
return rc;
- sd->primary_i2c_slave = slave;
return reg_w(sd, R51x_I2C_R_SID, slave + 1);
}
@@ -1285,7 +1287,6 @@ static int ov6xx0_configure(struct sd *sd)
/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
static void ov51x_led_control(struct sd *sd, int on)
{
-/* PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); */
reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */
}
@@ -1352,7 +1353,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
}
if (ov8xx0_configure(sd) < 0) {
PDEBUG(D_ERR,
- "Failed to configure OV8xx0 sensor");
+ "Failed to configure OV8xx0 sensor");
goto error;
}
}
@@ -1482,7 +1483,7 @@ static int ov519_mode_init_regs(struct sd *sd)
return -EIO;
if (sd->sensor == SEN_OV7640) {
/* Select 8-bit input mode */
- reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10);
+ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
}
} else {
if (write_regvals(sd, mode_init_519_ov7670,
@@ -1490,14 +1491,14 @@ static int ov519_mode_init_regs(struct sd *sd)
return -EIO;
}
- reg_w(sd, OV519_CAM_H_SIZE, sd->gspca_dev.width >> 4);
- reg_w(sd, OV519_CAM_V_SIZE, sd->gspca_dev.height >> 3);
- reg_w(sd, OV519_CAM_X_OFFSETL, 0x00);
- reg_w(sd, OV519_CAM_X_OFFSETH, 0x00);
- reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00);
- reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00);
- reg_w(sd, OV519_CAM_DIVIDER, 0x00);
- reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */
+ reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4);
+ reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3);
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+ reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
+ reg_w(sd, OV519_R14_Y_OFFSETL, 0x00);
+ reg_w(sd, OV519_R15_Y_OFFSETH, 0x00);
+ reg_w(sd, OV519_R16_DIVIDER, 0x00);
+ reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */
reg_w(sd, 0x26, 0x00); /* Undocumented */
/******** Set the framerate ********/
@@ -1509,8 +1510,8 @@ static int ov519_mode_init_regs(struct sd *sd)
switch (sd->sensor) {
case SEN_OV7640:
switch (sd->frame_rate) {
-/*fixme: default was 30 fps */
- case 30:
+ default:
+/* case 30: */
reg_w(sd, 0xa4, 0x0c);
reg_w(sd, 0x23, 0xff);
break;
@@ -1522,8 +1523,7 @@ static int ov519_mode_init_regs(struct sd *sd)
reg_w(sd, 0xa4, 0x0c);
reg_w(sd, 0x23, 0x1b);
break;
- default:
-/* case 15: */
+ case 15:
reg_w(sd, 0xa4, 0x04);
reg_w(sd, 0x23, 0xff);
sd->clockdiv = 1;
@@ -1576,7 +1576,6 @@ static int ov519_mode_init_regs(struct sd *sd)
}
break;
}
-
return 0;
}
@@ -1667,7 +1666,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
* the gain or the contrast. The "reserved" bits seem
* to have some effect in this case. */
i2c_w(sd, 0x2d, 0x85);
- } else if (sd->clockdiv >= 0) {
+ } else {
i2c_w(sd, 0x11, sd->clockdiv);
}
@@ -1869,7 +1868,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
ret = ov51x_restart(sd);
if (ret < 0)
goto out;
- PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
ov51x_led_control(sd, 1);
return 0;
out:
@@ -1879,8 +1877,10 @@ out:
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- ov51x_stop((struct sd *) gspca_dev);
- ov51x_led_control((struct sd *) gspca_dev, 0);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov51x_stop(sd);
+ ov51x_led_control(sd, 0);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
@@ -1935,9 +1935,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
int val;
val = sd->brightness;
- PDEBUG(D_CONF, "brightness:%d", val);
-/* if (gspca_dev->streaming)
- * ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -1959,8 +1956,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val));
break;
}
-/* if (gspca_dev->streaming)
- * ov51x_restart(sd); */
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1969,9 +1964,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
int val;
val = sd->contrast;
- PDEBUG(D_CONF, "contrast:%d", val);
-/* if (gspca_dev->streaming)
- ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV7610:
case SEN_OV6620:
@@ -2007,8 +1999,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
break;
}
-/* if (gspca_dev->streaming)
- ov51x_restart(sd); */
}
static void setcolors(struct gspca_dev *gspca_dev)
@@ -2017,9 +2007,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
int val;
val = sd->colors;
- PDEBUG(D_CONF, "saturation:%d", val);
-/* if (gspca_dev->streaming)
- ov51x_stop(sd); */
switch (sd->sensor) {
case SEN_OV8610:
case SEN_OV7610:
@@ -2044,8 +2031,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
/* set REG_COM13 values for UV sat auto mode */
break;
}
-/* if (gspca_dev->streaming)
- ov51x_restart(sd); */
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -2053,7 +2038,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
- setbrightness(gspca_dev);
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -2070,7 +2056,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
- setcontrast(gspca_dev);
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -2087,7 +2074,8 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->colors = val;
- setcolors(gspca_dev);
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
return 0;
}
@@ -2104,7 +2092,8 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->hflip = val;
- sethvflip(sd);
+ if (gspca_dev->streaming)
+ sethvflip(sd);
return 0;
}
@@ -2121,7 +2110,8 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->vflip = val;
- sethvflip(sd);
+ if (gspca_dev->streaming)
+ sethvflip(sd);
return 0;
}
@@ -2162,7 +2152,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x05a9, 0x8519)},
{}
};
-#undef DVNAME
+
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
new file mode 100644
index 000000000000..3bf15e401693
--- /dev/null
+++ b/drivers/media/video/gspca/ov534.c
@@ -0,0 +1,601 @@
+/*
+ * ov534/ov772x gspca driver
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
+ * This program is free software; you can redistribute 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "ov534"
+
+#include "gspca.h"
+
+#define OV534_REG_ADDRESS 0xf1 /* ? */
+#define OV534_REG_SUBADDR 0xf2
+#define OV534_REG_WRITE 0xf3
+#define OV534_REG_READ 0xf4
+#define OV534_REG_OPERATION 0xf5
+#define OV534_REG_STATUS 0xf6
+
+#define OV534_OP_WRITE_3 0x37
+#define OV534_OP_WRITE_2 0x33
+#define OV534_OP_READ_2 0xf9
+
+#define CTRL_TIMEOUT 500
+
+MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
+MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ __u32 last_fid;
+ __u32 last_pts;
+ int frame_rate;
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
+ gspca_dev->usb_buf[0] = val;
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ 0x1,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ if (ret < 0)
+ PDEBUG(D_ERR, "write failed");
+}
+
+static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int ret;
+
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ 0x1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+ PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
+ if (ret < 0)
+ PDEBUG(D_ERR, "read failed");
+ return gspca_dev->usb_buf[0];
+}
+
+/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+ * (direction and output)? */
+static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
+{
+ u8 data;
+
+ PDEBUG(D_CONF, "led status: %d", status);
+
+ data = ov534_reg_read(gspca_dev, 0x21);
+ data |= 0x80;
+ ov534_reg_write(gspca_dev, 0x21, data);
+
+ data = ov534_reg_read(gspca_dev, 0x23);
+ if (status)
+ data |= 0x80;
+ else
+ data &= ~(0x80);
+
+ ov534_reg_write(gspca_dev, 0x23, data);
+}
+
+static int sccb_check_status(struct gspca_dev *gspca_dev)
+{
+ u8 data;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
+
+ switch (data) {
+ case 0x00:
+ return 1;
+ case 0x04:
+ return 0;
+ case 0x03:
+ break;
+ default:
+ PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
+ data, i + 1);
+ }
+ }
+ return 0;
+}
+
+static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+{
+ PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val);
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_write failed");
+}
+
+#ifdef GSPCA_DEBUG
+static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 1");
+
+ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(gspca_dev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 2");
+
+ return ov534_reg_read(gspca_dev, OV534_REG_READ);
+}
+#endif
+
+static const __u8 ov534_reg_initdata[][2] = {
+ { 0xe7, 0x3a },
+
+ { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */
+
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+ { 0x89, 0xff },
+ { 0x76, 0x03 },
+ { 0x92, 0x01 },
+ { 0x93, 0x18 },
+ { 0x94, 0x10 },
+ { 0x95, 0x10 },
+ { 0xe2, 0x00 },
+ { 0xe7, 0x3e },
+
+ { 0x96, 0x00 },
+
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x0a },
+ { 0x97, 0x3f },
+ { 0x97, 0x4a },
+ { 0x97, 0x20 },
+ { 0x97, 0x15 },
+ { 0x97, 0x0b },
+
+ { 0x8e, 0x40 },
+ { 0x1f, 0x81 },
+ { 0x34, 0x05 },
+ { 0xe3, 0x04 },
+ { 0x88, 0x00 },
+ { 0x89, 0x00 },
+ { 0x76, 0x00 },
+ { 0xe7, 0x2e },
+ { 0x31, 0xf9 },
+ { 0x25, 0x42 },
+ { 0x21, 0xf0 },
+
+ { 0x1c, 0x00 },
+ { 0x1d, 0x40 },
+ { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+ { 0x1d, 0x00 }, /* payload size */
+ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+ { 0x1d, 0x58 }, /* frame size */
+ { 0x1d, 0x00 }, /* frame size */
+
+ { 0x1c, 0x0a },
+ { 0x1d, 0x08 }, /* turn on UVC header */
+ { 0x1d, 0x0e }, /* .. */
+
+ { 0x8d, 0x1c },
+ { 0x8e, 0x80 },
+ { 0xe5, 0x04 },
+
+ { 0xc0, 0x50 },
+ { 0xc1, 0x3c },
+ { 0xc2, 0x0c },
+};
+
+static const __u8 ov772x_reg_initdata[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
+
+ { 0x3d, 0x03 },
+ { 0x17, 0x26 },
+ { 0x18, 0xa0 },
+ { 0x19, 0x07 },
+ { 0x1a, 0xf0 },
+ { 0x32, 0x00 },
+ { 0x29, 0xa0 },
+ { 0x2c, 0xf0 },
+ { 0x65, 0x20 },
+ { 0x11, 0x01 },
+ { 0x42, 0x7f },
+ { 0x63, 0xe0 },
+ { 0x64, 0xff },
+ { 0x66, 0x00 },
+ { 0x13, 0xf0 },
+ { 0x0d, 0x41 },
+ { 0x0f, 0xc5 },
+ { 0x14, 0x11 },
+
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x24, 0x40 },
+ { 0x25, 0x30 },
+ { 0x26, 0xa1 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x00 },
+ { 0x6b, 0xaa },
+ { 0x13, 0xff },
+
+ { 0x90, 0x05 },
+ { 0x91, 0x01 },
+ { 0x92, 0x03 },
+ { 0x93, 0x00 },
+ { 0x94, 0x60 },
+ { 0x95, 0x3c },
+ { 0x96, 0x24 },
+ { 0x97, 0x1e },
+ { 0x98, 0x62 },
+ { 0x99, 0x80 },
+ { 0x9a, 0x1e },
+ { 0x9b, 0x08 },
+ { 0x9c, 0x20 },
+ { 0x9e, 0x81 },
+
+ { 0xa6, 0x04 },
+ { 0x7e, 0x0c },
+ { 0x7f, 0x16 },
+ { 0x80, 0x2a },
+ { 0x81, 0x4e },
+ { 0x82, 0x61 },
+ { 0x83, 0x6f },
+ { 0x84, 0x7b },
+ { 0x85, 0x86 },
+ { 0x86, 0x8e },
+ { 0x87, 0x97 },
+ { 0x88, 0xa4 },
+ { 0x89, 0xaf },
+ { 0x8a, 0xc5 },
+ { 0x8b, 0xd7 },
+ { 0x8c, 0xe8 },
+ { 0x8d, 0x20 },
+
+ { 0x0c, 0x90 },
+
+ { 0x2b, 0x00 },
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x11, 0x01 },
+ { 0x0c, 0xd0 },
+ { 0x64, 0xff },
+ { 0x0d, 0x41 },
+
+ { 0x14, 0x41 },
+ { 0x0e, 0xcd },
+ { 0xac, 0xbf },
+ { 0x8e, 0x00 },
+ { 0x0c, 0xd0 }
+};
+
+/* set framerate */
+static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int fr = sd->frame_rate;
+
+ switch (fr) {
+ case 50:
+ sccb_reg_write(gspca_dev, 0x11, 0x01);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ break;
+ case 40:
+ sccb_reg_write(gspca_dev, 0x11, 0x02);
+ sccb_reg_write(gspca_dev, 0x0d, 0xc1);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ break;
+/* case 30: */
+ default:
+ fr = 30;
+ sccb_reg_write(gspca_dev, 0x11, 0x04);
+ sccb_reg_write(gspca_dev, 0x0d, 0x81);
+ ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ break;
+ case 15:
+ sccb_reg_write(gspca_dev, 0x11, 0x03);
+ sccb_reg_write(gspca_dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ break;
+ }
+
+ sd->frame_rate = fr;
+ PDEBUG(D_PROBE, "frame_rate: %d", fr);
+}
+
+/* setup method */
+static void ov534_setup(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ /* Initialize bridge chip */
+ for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++)
+ ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0],
+ ov534_reg_initdata[i][1]);
+
+ PDEBUG(D_PROBE, "sensor is ov%02x%02x",
+ sccb_reg_read(gspca_dev, 0x0a),
+ sccb_reg_read(gspca_dev, 0x0b));
+
+ ov534_set_led(gspca_dev, 1);
+
+ /* Initialize sensor */
+ for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++)
+ sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0],
+ ov772x_reg_initdata[i][1]);
+
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ cam->bulk_size = 16384;
+ cam->bulk_nurbs = 2;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ ov534_setup(gspca_dev);
+ ov534_set_frame_rate(gspca_dev);
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ /* start streaming data */
+ ov534_set_led(gspca_dev, 1);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* stop streaming data */
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ __u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ int this_fid;
+ int remaining_len = len;
+ __u8 *next_data = data;
+
+scan_next:
+ if (remaining_len <= 0)
+ return;
+
+ data = next_data;
+ len = min(remaining_len, 2048);
+ remaining_len -= len;
+ next_data += len;
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+ changes. A frame ends when EOF is set, and we've received
+ the correct number of bytes. */
+
+ /* Verify UVC header. Header length is always 12 */
+ if (data[0] != 12 || len < 12) {
+ PDEBUG(D_PACK, "bad header");
+ goto discard;
+ }
+
+ /* Check errors */
+ if (data[1] & UVC_STREAM_ERR) {
+ PDEBUG(D_PACK, "payload error");
+ goto discard;
+ }
+
+ /* Extract PTS and FID */
+ if (!(data[1] & UVC_STREAM_PTS)) {
+ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
+ this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2];
+ this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+
+ /* If PTS or FID has changed, start a new frame. */
+ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+ sd->last_pts = this_pts;
+ sd->last_fid = this_fid;
+ }
+
+ /* Add the data from this payload */
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data + 12, len - 12);
+
+ /* If this packet is marked as EOF, end the frame */
+ if (data[1] & UVC_STREAM_EOF) {
+ sd->last_pts = 0;
+
+ if ((frame->data_end - frame->data) !=
+ (gspca_dev->width * gspca_dev->height * 2)) {
+ PDEBUG(D_PACK, "short frame");
+ goto discard;
+ }
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0);
+ }
+
+ /* Done this payload */
+ goto scan_next;
+
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
+ goto scan_next;
+}
+
+/* get stream parameters (framerate) */
+static int sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
+}
+
+/* set stream parameters (framerate) */
+static int sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+ ov534_set_frame_rate(gspca_dev);
+
+ /* Return the actual framerate */
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */
+ {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */
+ {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 0b0c573d06da..c90ac852bac0 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -1,7 +1,7 @@
/*
* Pixart PAC207BCA library
*
- * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
+ * Copyright (C) 2008 Hans de Goede <hdgoede@redhat.com>
* Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
* Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
*
@@ -27,7 +27,7 @@
#include "gspca.h"
-MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
MODULE_DESCRIPTION("Pixart PAC207");
MODULE_LICENSE("GPL");
@@ -149,7 +149,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = (176 + 2) * 144,
@@ -529,6 +529,7 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x041e, 0x4028)},
{USB_DEVICE(0x093a, 0x2460)},
+ {USB_DEVICE(0x093a, 0x2461)},
{USB_DEVICE(0x093a, 0x2463)},
{USB_DEVICE(0x093a, 0x2464)},
{USB_DEVICE(0x093a, 0x2468)},
@@ -536,6 +537,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2471)},
{USB_DEVICE(0x093a, 0x2472)},
{USB_DEVICE(0x093a, 0x2476)},
+ {USB_DEVICE(0x145f, 0x013a)},
{USB_DEVICE(0x2001, 0xf115)},
{}
};
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index fbd45e235d97..a9c95cba710e 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -226,7 +226,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 8 + 590,
@@ -1064,10 +1064,13 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
+ {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 6c69bc7778fc..b3e4e0677b68 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -132,8 +132,6 @@ struct sensor_data {
ignore atleast the 2 next frames for the new settings to come into effect
before doing any other adjustments */
#define AUTOGAIN_IGNORE_FRAMES 3
-#define AUTOGAIN_DEADZONE 1000
-#define DESIRED_AVG_LUM 7000
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -229,7 +227,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -251,7 +249,7 @@ static struct v4l2_pix_format vga_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -827,18 +825,29 @@ static void setfreq(struct gspca_dev *gspca_dev)
static void do_autogain(struct gspca_dev *gspca_dev)
{
+ int deadzone, desired_avg_lum;
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum = atomic_read(&sd->avg_lum);
if (avg_lum == -1)
return;
+ /* SIF / VGA sensors have a different autoexposure area and thus
+ different avg_lum values for the same picture brightness */
+ if (sensor_data[sd->sensor].flags & F_SIF) {
+ deadzone = 1000;
+ desired_avg_lum = 7000;
+ } else {
+ deadzone = 3000;
+ desired_avg_lum = 23000;
+ }
+
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
- sd->brightness * DESIRED_AVG_LUM / 127,
- AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE)) {
- PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n",
+ sd->brightness * desired_avg_lum / 127,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE)) {
+ PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
(int)sd->gain, (int)sd->exposure);
sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
}
@@ -1226,8 +1235,8 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
- {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
#endif
+ {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)},
{USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)},
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 53cb82d9e7c6..3373b8d9d2a8 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -24,6 +24,8 @@
#include "gspca.h"
#include "jpeg.h"
+#define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0)
+
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
MODULE_LICENSE("GPL");
@@ -35,23 +37,26 @@ struct sd {
atomic_t avg_lum;
unsigned int exposure;
- unsigned short brightness;
- unsigned char contrast;
- unsigned char colors;
- unsigned char autogain;
+ __u16 brightness;
+ __u8 contrast;
+ __u8 colors;
+ __u8 autogain;
+ __u8 blue;
+ __u8 red;
__u8 vflip; /* ov7630 only */
+ __u8 infrared; /* mi0360 only */
- signed char ag_cnt;
+ __s8 ag_cnt;
#define AG_CNT_START 13
- char qindex;
- unsigned char bridge;
+ __u8 qindex;
+ __u8 bridge;
#define BRIDGE_SN9C102P 0
#define BRIDGE_SN9C105 1
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
#define BRIDGE_SN9C325 4
- char sensor; /* Type of image sensor chip */
+ __u8 sensor; /* Type of image sensor chip */
#define SENSOR_HV7131R 0
#define SENSOR_MI0360 1
#define SENSOR_MO4000 2
@@ -59,7 +64,7 @@ struct sd {
#define SENSOR_OV7630 4
#define SENSOR_OV7648 5
#define SENSOR_OV7660 6
- unsigned char i2c_base;
+ __u8 i2c_base;
};
/* V4L2 controls supported by the driver */
@@ -69,10 +74,16 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
@@ -84,7 +95,7 @@ static struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_MAX 0xffff
.maximum = BRIGHTNESS_MAX,
.step = 1,
-#define BRIGHTNESS_DEF 0x7fff
+#define BRIGHTNESS_DEF 0x8000
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
@@ -111,7 +122,7 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 0,
- .maximum = 64,
+ .maximum = 40,
.step = 1,
#define COLOR_DEF 32
.default_value = COLOR_DEF,
@@ -119,7 +130,35 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setcolors,
.get = sd_getcolors,
},
-#define AUTOGAIN_IDX 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define BLUE_BALANCE_DEF 32
+ .default_value = BLUE_BALANCE_DEF,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define RED_BALANCE_DEF 32
+ .default_value = RED_BALANCE_DEF,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
+#define AUTOGAIN_IDX 5
{
{
.id = V4L2_CID_AUTOGAIN,
@@ -135,7 +174,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getautogain,
},
/* ov7630 only */
-#define VFLIP_IDX 4
+#define VFLIP_IDX 6
{
{
.id = V4L2_CID_VFLIP,
@@ -150,9 +189,43 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setvflip,
.get = sd_getvflip,
},
+/* mi0360 only */
+#define INFRARED_IDX 7
+ {
+ {
+ .id = V4L2_CID_INFRARED,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Infrared",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define INFRARED_DEF 0
+ .default_value = INFRARED_DEF,
+ },
+ .set = sd_setinfrared,
+ .get = sd_getinfrared,
+ },
+};
+
+/* table of the disabled controls */
+static __u32 ctrl_dis[] = {
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_HV7131R 0 */
+ (1 << VFLIP_IDX),
+ /* SENSOR_MI0360 1 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_MO4000 2 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OM6802 3 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX),
+ /* SENSOR_OV7630 4 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7648 5 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7660 6 */
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
@@ -231,13 +304,13 @@ static const __u8 sn_ov7630[] = {
static const __u8 sn_ov7648[] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0xa1, 0x6e, 0x18, 0x65, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x82,
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const __u8 sn_ov7660[] = {
@@ -469,6 +542,53 @@ static const __u8 ov7630_sensor_init[][8] = {
/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
{}
};
+
+static const __u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+ {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+ {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
+ {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
+ {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
+ {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
+ {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
+ {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
+ {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
+ {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
+ {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
+
+ {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
+/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+ {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10},
+/*...*/
+/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */
+ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */
+/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */
+/*...*/
+ {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
+/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */
+
+ {}
+};
+
static const __u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
/* (delay 20ms) */
@@ -557,64 +677,6 @@ static const __u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
{}
};
-/* reg 0x04 reg 0x07 reg 0x10 */
-/* expo = (COM1 & 0x02) | ((AECHH & 0x2f) << 10) | (AECh << 2) */
-
-static const __u8 ov7648_sensor_init[][8] = {
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10},
- {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10},
- {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
- {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10},
- {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10},
- {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10},
- {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10},
- {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10},
- {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10},
- {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10},
- {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
- {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10},
- {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10},
- {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10},
- {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10},
- {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10},
- /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
- * This is currently setting a
- * blue tint, and some things more , i leave it here for future test if
- * somene is having problems with color on this sensor
- {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10},
- {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
- {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */
- {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
- {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */
- {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */
- {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/
-/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */
- {}
-};
static const __u8 qtable4[] = {
0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06,
@@ -757,8 +819,6 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
static int probesensor(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
msleep(10);
reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
@@ -770,8 +830,7 @@ static int probesensor(struct gspca_dev *gspca_dev)
&& gspca_dev->usb_buf[3] == 0x00
&& gspca_dev->usb_buf[4] == 0x00) {
PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
- sd->sensor = SENSOR_HV7131R;
- return SENSOR_HV7131R;
+ return 0;
}
PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
@@ -827,17 +886,20 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
reg_w1(gspca_dev, 0x01, 0x40);
break;
case SENSOR_OV7648:
- reg_w1(gspca_dev, 0x01, 0x43);
- reg_w1(gspca_dev, 0x17, 0xae);
+ reg_w1(gspca_dev, 0x01, 0x63);
+ reg_w1(gspca_dev, 0x17, 0x20);
reg_w1(gspca_dev, 0x01, 0x42);
break;
/*jfm: from win trace */
case SENSOR_OV7660:
- reg_w1(gspca_dev, 0x01, 0x61);
- reg_w1(gspca_dev, 0x17, 0x20);
- reg_w1(gspca_dev, 0x01, 0x60);
- reg_w1(gspca_dev, 0x01, 0x40);
- break;
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x60);
+ reg_w1(gspca_dev, 0x01, 0x40);
+ break;
+ }
+ /* fall thru */
default:
reg_w1(gspca_dev, 0x01, 0x43);
reg_w1(gspca_dev, 0x17, 0x61);
@@ -922,6 +984,13 @@ static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
{
int i = 0;
+ i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+ i++;
+/* win: dble reset */
+ i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */
+ i++;
+ msleep(20);
+/* win: i2c reg read 00..7f */
while (ov7648_sensor_init[i][0]) {
i2c_w8(gspca_dev, ov7648_sensor_init[i]);
i++;
@@ -961,19 +1030,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
+ sd->blue = BLUE_BALANCE_DEF;
+ sd->red = RED_BALANCE_DEF;
sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
+ sd->vflip = VFLIP_DEF;
+ sd->infrared = INFRARED_DEF;
- switch (sd->sensor) {
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- case SENSOR_OV7660:
- gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX);
- break;
- }
- if (sd->sensor != SENSOR_OV7630)
- gspca_dev->ctrl_dis |= (1 << VFLIP_IDX);
-
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
return 0;
}
@@ -981,7 +1045,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
-/* const __u8 *sn9c1xx; */
__u8 regGpio[] = { 0x29, 0x74 };
__u8 regF1;
@@ -1100,32 +1163,13 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
return expo;
}
-/* this function is used for sensors o76xx only */
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int val;
- __u8 reg84_full[0x15];
-
- memcpy(reg84_full, reg84, sizeof reg84_full);
- val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */
- reg84_full[0] = (val + 1) / 2; /* red */
- reg84_full[2] = val; /* green */
- reg84_full[4] = (val + 1) / 5; /* blue */
- val = (sd->brightness - BRIGHTNESS_DEF) * 0x10
- / BRIGHTNESS_MAX;
- reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */
- reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full);
-}
-
-/* sensor != ov76xx */
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned int expo;
__u8 k2;
- k2 = sd->brightness >> 10;
+ k2 = ((int) sd->brightness - 0x8000) >> 10;
switch (sd->sensor) {
case SENSOR_HV7131R:
expo = sd->brightness << 4;
@@ -1147,38 +1191,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
break;
}
- reg_w1(gspca_dev, 0x96, k2);
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
}
-/* sensor != ov76xx */
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 k2;
- __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 };
-
- k2 = sd->contrast;
- contrast[2] = k2;
- contrast[0] = (k2 + 1) >> 1;
- contrast[4] = (k2 + 1) / 5;
- reg_w(gspca_dev, 0x84, contrast, 6);
+ __u8 contrast[6];
+
+ k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+ contrast[1] = 0;
+ contrast[2] = k2; /* green */
+ contrast[3] = 0;
+ contrast[4] = (k2 + 1) / 5; /* blue */
+ contrast[5] = 0;
+ reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 blue, red;
-
- if (sd->colors >= 32) {
- red = 32 + (sd->colors - 32) / 2;
- blue = 64 - sd->colors;
- } else {
- red = sd->colors;
- blue = 32 + (32 - sd->colors) / 2;
+ int i, v;
+ __u8 reg8a[12]; /* U & V gains */
+ static __s16 uv[6] = { /* same as reg84 in signed decimal */
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * sd->colors / COLOR_DEF;
+ reg8a[i * 2] = v;
+ reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
}
- reg_w1(gspca_dev, 0x05, red);
+ reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x05, sd->red);
/* reg_w1(gspca_dev, 0x07, 32); */
- reg_w1(gspca_dev, 0x06, blue);
+ reg_w1(gspca_dev, 0x06, sd->blue);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -1195,12 +1250,18 @@ static void setautogain(struct gspca_dev *gspca_dev)
static void setvflip(struct sd *sd)
{
- if (sd->sensor != SENSOR_OV7630)
- return;
i2c_w1(&sd->gspca_dev, 0x75, /* COMN */
sd->vflip ? 0x82 : 0x02);
}
+static void setinfrared(struct sd *sd)
+{
+/*fixme: different sequence for StarCam Clip and StarCam 370i */
+/* Clip */
+ i2c_w1(&sd->gspca_dev, 0x02, /* gpio */
+ sd->infrared ? 0x66 : 0x64);
+}
+
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
@@ -1235,28 +1296,39 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg17 = 0xe2;
break;
case SENSOR_OV7648:
- reg17 = 0xae;
+ reg17 = 0x20;
break;
/*jfm: from win trace */
case SENSOR_OV7660:
- reg17 = 0xa0;
- break;
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg17 = 0xa0;
+ break;
+ }
+ /* fall thru */
default:
reg17 = 0x60;
break;
}
reg_w1(gspca_dev, 0x17, reg17);
- reg_w1(gspca_dev, 0x05, sn9c1xx[5]);
- reg_w1(gspca_dev, 0x07, sn9c1xx[7]);
- reg_w1(gspca_dev, 0x06, sn9c1xx[6]);
+/* set reg1 was here */
+ reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
+ reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
+ reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def);
for (i = 0; i < 8; i++)
reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
switch (sd->sensor) {
- case SENSOR_OV7660:
- reg_w1(gspca_dev, 0x9a, 0x05);
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ reg_w1(gspca_dev, 0x99, 0x60);
break;
+ case SENSOR_OV7660:
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ break;
+ }
+ /* fall thru */
default:
reg_w1(gspca_dev, 0x9a, 0x08);
reg_w1(gspca_dev, 0x99, 0x59);
@@ -1265,10 +1337,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode)
- reg1 = 0x46; /* 320 clk 48Mhz */
+ reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */
else
- reg1 = 0x06; /* 640 clk 24Mz */
- reg17 = 0x61;
+ reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */
+ reg17 = 0x61; /* 0x:20: enable sensor clock */
switch (sd->sensor) {
case SENSOR_HV7131R:
hv7131R_InitSensor(gspca_dev);
@@ -1298,23 +1370,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
case SENSOR_OV7648:
ov7648_InitSensor(gspca_dev);
- reg17 = 0xa2;
- reg1 = 0x44;
-/* if (mode)
- ; * 320x2...
- else
- ; * 640x... */
+ reg17 = 0x21;
+/* reg1 = 0x42; * 42 - 46? */
break;
default:
/* case SENSOR_OV7660: */
ov7660_InitSensor(gspca_dev);
- if (mode) {
-/* reg17 = 0x21; * 320 */
-/* reg1 = 0x44; */
-/* reg1 = 0x46; (done) */
+ if (sd->bridge == BRIDGE_SN9C120) {
+ if (mode) { /* 320x240 - 160x120 */
+ reg17 = 0xa2;
+ reg1 = 0x44; /* 48 Mhz, video trf eneble */
+ }
} else {
- reg17 = 0xa2; /* 640 */
- reg1 = 0x44;
+ reg17 = 0x22;
+ reg1 = 0x06; /* 24 Mhz, video trf eneble
+ * inverse power down */
}
break;
}
@@ -1342,23 +1412,18 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x18, reg18);
reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg1);
switch (sd->sensor) {
- case SENSOR_HV7131R:
case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
+ setinfrared(sd);
break;
case SENSOR_OV7630:
setvflip(sd);
- /* fall thru */
- default: /* OV76xx */
- setbrightcont(gspca_dev);
break;
}
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
setautogain(gspca_dev);
- reg_w1(gspca_dev, 0x01, reg1);
return 0;
}
@@ -1369,6 +1434,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{ 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
static const __u8 stopmi0360[] =
{ 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const __u8 stopov7648[] =
+ { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
__u8 data;
const __u8 *sn9c1xx;
@@ -1382,8 +1449,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
i2c_w8(gspca_dev, stopmi0360);
data = 0x29;
break;
- case SENSOR_OV7630:
case SENSOR_OV7648:
+ i2c_w8(gspca_dev, stopov7648);
+ /* fall thru */
+ case SENSOR_OV7630:
data = 0x29;
break;
default:
@@ -1437,7 +1506,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes = 0;
sd->exposure = setexposure(gspca_dev,
(unsigned int) expotimes);
- setcolors(gspca_dev);
+ setredblue(gspca_dev);
break;
}
}
@@ -1491,19 +1560,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->brightness = val;
- if (gspca_dev->streaming) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -1520,19 +1578,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->contrast = val;
- if (gspca_dev->streaming) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setcontrast(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -1562,6 +1609,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red;
+ return 0;
+}
+
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1598,6 +1681,24 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->infrared = val;
+ if (gspca_dev->streaming)
+ setinfrared(sd);
+ return 0;
+}
+
+static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->infrared;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -1620,12 +1721,15 @@ static const __devinitdata struct usb_device_id device_table[] = {
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
{USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
+#endif
{USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
+#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
- {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
#endif
+ {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
{USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
/* bw600.inf:
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
@@ -1649,7 +1753,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
{USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
/*bw600.inf:*/
- {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/
{USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
{USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)},
/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
@@ -1657,8 +1761,8 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
#endif
{USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
+ {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */
{USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index bca106c153fa..942f04cd44dd 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -111,7 +111,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -124,7 +124,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -633,10 +633,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->subtype = id->driver_info;
if (sd->subtype != LogitechClickSmart310) {
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
} else {
cam->cam_mode = sif_mode;
- cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+ cam->nmodes = ARRAY_SIZE(sif_mode);
}
sd->qindex = 5;
sd->brightness = BRIGHTNESS_DEF;
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index e29954c1c38c..82e3e3e2ada1 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -34,6 +34,8 @@ struct sd {
unsigned short contrast;
__u8 brightness;
__u8 colors;
+ __u8 blue_balance;
+ __u8 red_balance;
char subtype;
#define Arowana300KCMOSCamera 0
@@ -52,6 +54,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
#define MY_BRIGHTNESS 0
@@ -63,7 +69,7 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 127,
.step = 1,
- .default_value = 63,
+ .default_value = 0,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
@@ -75,9 +81,9 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Contrast",
.minimum = 0,
- .maximum = 0xffff,
+ .maximum = 64725,
.step = 1,
- .default_value = 0xaa00,
+ .default_value = 64725,
},
.set = sd_setcontrast,
.get = sd_getcontrast,
@@ -91,14 +97,42 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 63,
.step = 1,
- .default_value = 31,
+ .default_value = 20,
},
.set = sd_setcolors,
.get = sd_getcolors,
},
+#define MY_BLUE_BALANCE 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+#define MY_RED_BALANCE 4
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
@@ -1822,29 +1856,6 @@ static int reg_write(struct usb_device *dev,
return ret;
}
-/* returns: negative is error, pos or zero is data */
-static int reg_read(struct gspca_dev *gspca_dev,
- __u16 req, /* bRequest */
- __u16 index, /* wIndex */
- __u16 length) /* wLength (1 or 2 only) */
-{
- int ret;
-
- gspca_dev->usb_buf[1] = 0;
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index,
- gspca_dev->usb_buf, length,
- 500); /* timeout */
- if (ret < 0) {
- PDEBUG(D_ERR, "reg_read err %d", ret);
- return -1;
- }
- return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
-}
static int write_vector(struct gspca_dev *gspca_dev,
const __u16 data[][3])
@@ -1869,18 +1880,11 @@ static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness);
reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
- reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness);
}
static void getbrightness(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 brightness;
-
- brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2);
- sd->brightness = brightness << 1;
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1895,7 +1899,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
static void getcontrast(struct gspca_dev *gspca_dev)
{
-/* spca50x->contrast = 0xaa01; */
}
static void setcolors(struct gspca_dev *gspca_dev)
@@ -1907,11 +1910,20 @@ static void setcolors(struct gspca_dev *gspca_dev)
static void getcolors(struct gspca_dev *gspca_dev)
{
+}
+
+static void setblue_balance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance);
+}
+
+static void setred_balance(struct gspca_dev *gspca_dev)
+{
struct sd *sd = (struct sd *) gspca_dev;
- sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2);
-/* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */
-/* 2) & 0xFF) << 8; */
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance);
}
/* this function is called at probe time */
@@ -1930,6 +1942,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value;
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
switch (sd->subtype) {
case Arowana300KCMOSCamera:
case SmileIntlCamera:
@@ -1948,15 +1968,17 @@ static int sd_config(struct gspca_dev *gspca_dev,
goto error;
break;
}
+ PDEBUG(D_STREAM, "Initializing SPCA501 finished");
return 0;
error:
return -EINVAL;
}
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int mode;
switch (sd->subtype) {
case ThreeComHomeConnectLite:
@@ -1976,14 +1998,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* Generic 501 open data */
write_vector(gspca_dev, spca501_open_data);
}
- PDEBUG(D_STREAM, "Initializing SPCA501 finished");
- return 0;
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct usb_device *dev = gspca_dev->dev;
- int mode;
/* memorize the wanted pixel format */
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
@@ -2113,6 +2127,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue_balance = val;
+ if (gspca_dev->streaming)
+ setblue_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue_balance;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red_balance = val;
+ if (gspca_dev->streaming)
+ setred_balance(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red_balance;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index 895b9fe4018c..2a33a29010ee 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -59,7 +59,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 645ee9d44d02..96e2512e0621 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -110,7 +110,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 63ec902c895d..be5d740a315d 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -62,7 +62,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{160, 120, V4L2_PIX_FMT_SPCA508, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 3 / 2,
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index c3de4e44123d..3c9288019e96 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -32,22 +32,22 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u16 contrast; /* rev72a only */
-#define CONTRAST_MIN 0x0000
-#define CONTRAST_DEF 0x2000
-#define CONTRAST_MAX 0x3fff
-
__u16 exposure; /* rev12a only */
#define EXPOSURE_MIN 1
#define EXPOSURE_DEF 200
#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */
+ __u8 contrast; /* rev72a only */
+#define CONTRAST_MIN 0x00
+#define CONTRAST_DEF 0x20
+#define CONTRAST_MAX 0x3f
+
__u8 brightness; /* rev72a only */
#define BRIGHTNESS_MIN 0
-#define BRIGHTNESS_DEF 32
-#define BRIGHTNESS_MAX 63
+#define BRIGHTNESS_DEF 0x20
+#define BRIGHTNESS_MAX 0x3f
- __u8 white; /* rev12a only */
+ __u8 white;
#define WHITE_MIN 1
#define WHITE_DEF 0x40
#define WHITE_MAX 0x7f
@@ -73,7 +73,7 @@ struct sd {
#define AG_CNT_START 13
};
-static struct v4l2_pix_format sif_012a_mode[] = {
+static const struct v4l2_pix_format sif_012a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -96,7 +96,7 @@ static struct v4l2_pix_format sif_012a_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_072a_mode[] = {
+static const struct v4l2_pix_format sif_072a_mode[] = {
{160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120,
@@ -146,98 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = {
#define SPCA561_SNAPBIT 0x20
#define SPCA561_SNAPCTRL 0x40
-static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
-{
- int ret;
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 500);
- PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
- if (ret < 0)
- PDEBUG(D_ERR, "reg write: error %d", ret);
-}
-
-static void write_vector(struct gspca_dev *gspca_dev,
- const __u16 data[][2])
-{
- struct usb_device *dev = gspca_dev->dev;
- int i;
-
- i = 0;
- while (data[i][1] != 0) {
- reg_w_val(dev, data[i][1], data[i][0]);
- i++;
- }
-}
-
-/* read 'len' bytes to gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev,
- __u16 index, __u16 length)
-{
- usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, length, 500);
-}
-
-static void reg_w_buf(struct gspca_dev *gspca_dev,
- __u16 index, const __u8 *buffer, __u16 len)
-{
- memcpy(gspca_dev->usb_buf, buffer, len);
- usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, len, 500);
-}
-
-static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg)
-{
- int retry = 60;
- __u8 DataLow;
- __u8 DataHight;
-
- DataLow = valeur;
- DataHight = valeur >> 8;
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8805, DataLow);
- reg_w_val(gspca_dev->dev, 0x8800, DataHight);
- while (retry--) {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- }
-}
-
-static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
-{
- int retry = 60;
- __u8 value;
- __u8 vallsb;
-
- reg_w_val(gspca_dev->dev, 0x8804, 0x92);
- reg_w_val(gspca_dev->dev, 0x8801, reg);
- reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01));
- do {
- reg_r(gspca_dev, 0x8803, 1);
- if (!gspca_dev->usb_buf[0])
- break;
- } while (--retry);
- if (retry == 0)
- return -1;
- reg_r(gspca_dev, 0x8800, 1);
- value = gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8805, 1);
- vallsb = gspca_dev->usb_buf[0];
- return ((int) value << 8) | vallsb;
-}
-
-static const __u16 spca561_init_data[][2] = {
+static const __u16 rev72a_init_data1[][2] = {
{0x0000, 0x8114}, /* Software GPIO output data */
{0x0001, 0x8114}, /* Software GPIO output data */
{0x0000, 0x8112}, /* Some kind of reset */
@@ -247,44 +156,26 @@ static const __u16 spca561_init_data[][2] = {
{0x0001, 0x8118}, /* Conf sensor */
{0x0092, 0x8804}, /* I know nothing about these */
{0x0010, 0x8802}, /* 0x88xx registers, so I won't */
- /***************/
{0x000d, 0x8805}, /* sensor default setting */
- {0x0001, 0x8801}, /* 1 <- 0x0d */
- {0x0000, 0x8800},
- {0x0018, 0x8805},
- {0x0002, 0x8801}, /* 2 <- 0x18 */
- {0x0000, 0x8800},
- {0x0065, 0x8805},
- {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */
- {0x0001, 0x8800},
- {0x0021, 0x8805},
- {0x0005, 0x8801}, /* 5 <- 0x21 */
- {0x0000, 0x8800},
- {0x00aa, 0x8805},
- {0x0007, 0x8801}, /* 7 <- 0xaa */
- {0x0000, 0x8800},
- {0x0004, 0x8805},
- {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */
- {0x0015, 0x8800},
- {0x0002, 0x8805},
- {0x0039, 0x8801}, /* 0x39 <- 0x02 */
- {0x0000, 0x8800},
- {0x0010, 0x8805},
- {0x0035, 0x8801}, /* 0x35 <- 0x10 */
- {0x0000, 0x8800},
- {0x0049, 0x8805},
- {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */
- {0x0010, 0x8800},
- {0x000b, 0x8805},
- {0x0028, 0x8801}, /* 0x28 <- 0x0b */
- {0x0000, 0x8800},
- {0x000f, 0x8805},
- {0x003b, 0x8801}, /* 0x3b <- 0x0f */
- {0x0000, 0x8800},
- {0x0000, 0x8805},
- {0x003c, 0x8801}, /* 0x3c <- 0x00 */
- {0x0000, 0x8800},
- /***************/
+ {}
+};
+static const __u16 rev72a_init_sensor1[][2] = {
+ /* ms-win values */
+ {0x0001, 0x0018}, /* 0x01 <- 0x0d */
+ {0x0002, 0x0065}, /* 0x02 <- 0x18 */
+ {0x0004, 0x0121}, /* 0x04 <- 0x0165 */
+ {0x0005, 0x00aa}, /* 0x05 <- 0x21 */
+ {0x0007, 0x0004}, /* 0x07 <- 0xaa */
+ {0x0020, 0x1502}, /* 0x20 <- 0x1504 */
+ {0x0039, 0x0010}, /* 0x39 <- 0x02 */
+ {0x0035, 0x0049}, /* 0x35 <- 0x10 */
+ {0x0009, 0x100b}, /* 0x09 <- 0x1049 */
+ {0x0028, 0x000f}, /* 0x28 <- 0x0b */
+ {0x003b, 0x003c}, /* 0x3b <- 0x0f */
+ {0x003c, 0x0000}, /* 0x3c <- 0x00 */
+ {}
+};
+static const __u16 rev72a_init_data2[][2] = {
{0x0018, 0x8601}, /* Pixel/line selection for color separation */
{0x0000, 0x8602}, /* Optical black level for user setting */
{0x0060, 0x8604}, /* Optical black horizontal offset */
@@ -309,10 +200,11 @@ static const __u16 spca561_init_data[][2] = {
{0x0004, 0x8612}, /* Gr offset for white balance */
{0x0007, 0x8613}, /* B offset for white balance */
{0x0000, 0x8614}, /* Gb offset for white balance */
- {0x008c, 0x8651}, /* R gain for white balance */
- {0x008c, 0x8652}, /* Gr gain for white balance */
- {0x00b5, 0x8653}, /* B gain for white balance */
- {0x008c, 0x8654}, /* Gb gain for white balance */
+/* from ms-win */
+ {0x0035, 0x8651}, /* R gain for white balance */
+ {0x0040, 0x8652}, /* Gr gain for white balance */
+ {0x005f, 0x8653}, /* B gain for white balance */
+ {0x0040, 0x8654}, /* Gb gain for white balance */
{0x0002, 0x8502}, /* Maximum average bit rate stuff */
{0x0011, 0x8802},
@@ -324,29 +216,22 @@ static const __u16 spca561_init_data[][2] = {
{0x0002, 0x865b}, /* Horizontal offset for valid pixels */
{0x0003, 0x865c}, /* Vertical offset for valid lines */
- /***************//* sensor active */
- {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */
- {0x0021, 0x8805},
- {0x0001, 0x8800},
- {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */
- {0x0065, 0x8805},
- {0x0001, 0x8800},
- {0x0005, 0x8801}, /* 0x05 <- 0x2f */
- {0x002f, 0x8805},
- {0x0000, 0x8800},
- {0x0006, 0x8801}, /* 0x06 <- 0 */
- {0x0000, 0x8805},
- {0x0000, 0x8800},
- {0x000a, 0x8801}, /* 0x0a <- 2 */
- {0x0002, 0x8805},
- {0x0000, 0x8800},
- {0x0009, 0x8801}, /* 0x09 <- 0x1061 */
- {0x0061, 0x8805},
- {0x0010, 0x8800},
- {0x0035, 0x8801}, /* 0x35 <-0x14 */
- {0x0014, 0x8805},
- {0x0000, 0x8800},
+ {}
+};
+static const __u16 rev72a_init_sensor2[][2] = {
+ /* ms-win values */
+ {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */
+ {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */
+ {0x0005, 0x002f}, /* 0x05 <- 0x2f */
+ {0x0006, 0x0000}, /* 0x06 <- 0 */
+ {0x000a, 0x0002}, /* 0x0a <- 2 */
+ {0x0009, 0x1061}, /* 0x09 <- 0x1061 */
+ {0x0035, 0x0014}, /* 0x35 <- 0x14 */
+ {}
+};
+static const __u16 rev72a_init_data3[][2] = {
{0x0030, 0x8112}, /* ISO and drop packet enable */
+/*fixme: should stop here*/
{0x0000, 0x8112}, /* Some kind of reset ???? */
{0x0009, 0x8118}, /* Enable sensor and set standby */
{0x0000, 0x8114}, /* Software GPIO output data */
@@ -434,7 +319,6 @@ static const __u16 spca561_init_data[][2] = {
{}
};
-
/******************** QC Express etch2 stuff ********************/
static const __u16 Pb100_1map8300[][2] = {
/* reg, value */
@@ -515,22 +399,112 @@ static const __u16 spca561_161rev12A_data2[][2] = {
{}
};
-static void sensor_mapwrite(struct gspca_dev *gspca_dev,
- const __u16 sensormap[][2])
+static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
+{
+ int ret;
+
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg write: error %d", ret);
+}
+
+static void write_vector(struct gspca_dev *gspca_dev,
+ const __u16 data[][2])
{
- int i = 0;
- __u8 usbval[2];
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
- while (sensormap[i][0]) {
- usbval[0] = sensormap[i][1];
- usbval[1] = sensormap[i][1] >> 8;
- reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2);
+ i = 0;
+ while (data[i][1] != 0) {
+ reg_w_val(dev, data[i][1], data[i][0]);
i++;
}
}
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length, 500);
+}
+
+/* write 'len' bytes from gspca_dev->usb_buf */
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 len)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len, 500);
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+{
+ int retry = 60;
+
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8805, value);
+ reg_w_val(gspca_dev->dev, 0x8800, value >> 8);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0])
+ return;
+ } while (--retry);
+}
+
+static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+{
+ int retry = 60;
+ __u8 value;
+
+ reg_w_val(gspca_dev->dev, 0x8804, 0x92);
+ reg_w_val(gspca_dev->dev, 0x8801, reg);
+ reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);
+ do {
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0]) {
+ reg_r(gspca_dev, 0x8800, 1);
+ value = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0x8805, 1);
+ return ((int) value << 8) | gspca_dev->usb_buf[0];
+ }
+ } while (--retry);
+ return -1;
+}
+
+static void sensor_mapwrite(struct gspca_dev *gspca_dev,
+ const __u16 (*sensormap)[2])
+{
+ while ((*sensormap)[0]) {
+ gspca_dev->usb_buf[0] = (*sensormap)[1];
+ gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
+ reg_w_buf(gspca_dev, (*sensormap)[0], 2);
+ sensormap++;
+ }
+}
+
+static void write_sensor_72a(struct gspca_dev *gspca_dev,
+ const __u16 (*sensor)[2])
+{
+ while ((*sensor)[0]) {
+ i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
+ sensor++;
+ }
+}
+
static void init_161rev12A(struct gspca_dev *gspca_dev)
{
-/* sensor_reset(gspca_dev); (not in win) */
write_vector(gspca_dev, spca561_161rev12A_data1);
sensor_mapwrite(gspca_dev, Pb100_1map8300);
/*fixme: should be in sd_start*/
@@ -598,49 +572,68 @@ static int sd_init_12a(struct gspca_dev *gspca_dev)
static int sd_init_72a(struct gspca_dev *gspca_dev)
{
PDEBUG(D_STREAM, "Chip revision: 072a");
- write_vector(gspca_dev, spca561_init_data);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+ write_vector(gspca_dev, rev72a_init_data2);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ write_vector(gspca_dev, rev72a_init_data3);
return 0;
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+/* rev 72a only */
+static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
- __u8 lowb;
+ __u8 value;
- switch (sd->chip_revision) {
- case Rev072A:
- lowb = sd->contrast >> 8;
- reg_w_val(dev, 0x8651, lowb);
- reg_w_val(dev, 0x8652, lowb);
- reg_w_val(dev, 0x8653, lowb);
- reg_w_val(dev, 0x8654, lowb);
- break;
- default: {
-/* case Rev012A: { */
- static const __u8 Reg8391[] =
- { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 };
+ value = sd->brightness;
- reg_w_buf(gspca_dev, 0x8391, Reg8391, 8);
- reg_w_buf(gspca_dev, 0x8390, Reg8391, 8);
- break;
- }
- }
+ /* offsets for white balance */
+ reg_w_val(dev, 0x8611, value); /* R */
+ reg_w_val(dev, 0x8612, value); /* Gr */
+ reg_w_val(dev, 0x8613, value); /* B */
+ reg_w_val(dev, 0x8614, value); /* Gb */
}
-/* rev12a only */
static void setwhite(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u16 white;
- __u8 reg8614, reg8616;
+ __u8 blue, red;
+ __u16 reg;
- white = sd->white;
/* try to emulate MS-win as possible */
- reg8616 = 0x90 - white * 5 / 8;
- reg_w_val(gspca_dev->dev, 0x8616, reg8616);
- reg8614 = 0x20 + white * 3 / 8;
- reg_w_val(gspca_dev->dev, 0x8614, reg8614);
+ white = sd->white;
+ red = 0x20 + white * 3 / 8;
+ blue = 0x90 - white * 5 / 8;
+ if (sd->chip_revision == Rev012A) {
+ reg = 0x8614;
+ } else {
+ reg = 0x8651;
+ red += sd->contrast - 0x20;
+ blue += sd->contrast - 0x20;
+ }
+ reg_w_val(gspca_dev->dev, reg, red);
+ reg_w_val(gspca_dev->dev, reg + 2, blue);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ __u8 value;
+
+ if (sd->chip_revision != Rev072A)
+ return;
+ value = sd->contrast + 0x20;
+
+ /* gains for white balance */
+ setwhite(gspca_dev);
+/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
+ reg_w_val(dev, 0x8652, value); /* Gr */
+/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
+ reg_w_val(dev, 0x8654, value); /* Gb */
}
/* rev 12a only */
@@ -649,7 +642,6 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int expo;
int clock_divider;
- __u8 data[2];
/* Register 0x8309 controls exposure for the spca561,
the basic exposure setting goes from 1-2047, where 1 is completely
@@ -673,20 +665,19 @@ static void setexposure(struct gspca_dev *gspca_dev)
clock_divider = 3;
}
expo |= clock_divider << 11;
- data[0] = expo;
- data[1] = expo >> 8;
- reg_w_buf(gspca_dev, 0x8309, data, 2);
+ gspca_dev->usb_buf[0] = expo;
+ gspca_dev->usb_buf[1] = expo >> 8;
+ reg_w_buf(gspca_dev, 0x8309, 2);
}
/* rev 12a only */
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 data[2];
- data[0] = sd->gain;
- data[1] = 0;
- reg_w_buf(gspca_dev, 0x8335, data, 2);
+ gspca_dev->usb_buf[0] = sd->gain;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8335, 2);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -702,9 +693,9 @@ static void setautogain(struct gspca_dev *gspca_dev)
static int sd_start_12a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
- int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */
- __u8 Reg8307[] = { 0xaa, 0x00 };
int mode;
+ static const __u8 Reg8391[8] =
+ {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
if (mode <= 1) {
@@ -716,14 +707,21 @@ static int sd_start_12a(struct gspca_dev *gspca_dev)
* is sufficient to push raw frames at ~20fps */
reg_w_val(dev, 0x8500, mode);
} /* -- qq@kuku.eu.org */
- reg_w_buf(gspca_dev, 0x8307, Reg8307, 2);
- reg_w_val(gspca_dev->dev, 0x8700, Clck);
+
+ gspca_dev->usb_buf[0] = 0xaa;
+ gspca_dev->usb_buf[1] = 0x00;
+ reg_w_buf(gspca_dev, 0x8307, 2);
+ /* clock - lower 0x8X values lead to fps > 30 */
+ reg_w_val(gspca_dev->dev, 0x8700, 0x8a);
/* 0x8f 0x85 0x27 clock */
reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
reg_w_val(gspca_dev->dev, 0x850b, 0x03);
- setcontrast(gspca_dev);
+ memcpy(gspca_dev->usb_buf, Reg8391, 8);
+ reg_w_buf(gspca_dev, 0x8391, 8);
+ reg_w_buf(gspca_dev, 0x8390, 8);
setwhite(gspca_dev);
setautogain(gspca_dev);
+/* setgain(gspca_dev); */
setexposure(gspca_dev);
return 0;
}
@@ -750,6 +748,9 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
reg_w_val(dev, 0x8500, mode); /* mode */
reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
reg_w_val(dev, 0x8112, 0x10 | 0x20);
+ setcontrast(gspca_dev);
+/* setbrightness(gspca_dev); * fixme: bad values */
+ setwhite(gspca_dev);
setautogain(gspca_dev);
return 0;
}
@@ -791,7 +792,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
__u8 luma_mean = 110;
__u8 luma_delta = 20;
__u8 spring = 4;
- __u8 reg8339[2];
if (sd->ag_cnt < 0)
return;
@@ -834,13 +834,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (gainG > 0x3f)
gainG = 0x3f;
- else if (gainG < 4)
+ else if (gainG < 3)
gainG = 3;
i2c_write(gspca_dev, gainG, 0x35);
- if (expotimes >= 0x0256)
+ if (expotimes > 0x0256)
expotimes = 0x0256;
- else if (expotimes < 4)
+ else if (expotimes < 3)
expotimes = 3;
i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
}
@@ -848,13 +848,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
case Rev012A:
reg_r(gspca_dev, 0x8330, 2);
if (gspca_dev->usb_buf[1] > 0x08) {
- reg8339[0] = ++sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = ++sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
} else if (gspca_dev->usb_buf[1] < 0x02) {
- reg8339[0] = --sd->expo12a;
- reg8339[1] = 0;
- reg_w_buf(gspca_dev, 0x8339, reg8339, 2);
+ gspca_dev->usb_buf[0] = --sd->expo12a;
+ gspca_dev->usb_buf[1] = 0;
+ reg_w_buf(gspca_dev, 0x8339, 2);
}
break;
}
@@ -867,8 +867,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
- switch (data[0]) {
- case 0: /* start of frame */
+ switch (data[0]) { /* sequence number */
+ case 0: /* start of frame */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
data, 0);
data += SPCA561_OFFSET_DATA;
@@ -890,8 +890,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
frame, data, len);
}
return;
- case 0xff: /* drop */
-/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ case 0xff: /* drop (empty mpackets) */
return;
}
data++;
@@ -900,55 +899,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
/* rev 72a only */
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 value;
-
- value = sd->brightness;
- reg_w_val(gspca_dev->dev, 0x8611, value);
- reg_w_val(gspca_dev->dev, 0x8612, value);
- reg_w_val(gspca_dev->dev, 0x8613, value);
- reg_w_val(gspca_dev->dev, 0x8614, value);
-}
-
-static void getbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8611, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8612, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8613, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8614, 1);
- tot += gspca_dev->usb_buf[0];
- sd->brightness = tot >> 2;
-}
-
-/* rev72a only */
-static void getcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- __u16 tot;
-
- tot = 0;
- reg_r(gspca_dev, 0x8651, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8652, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8653, 1);
- tot += gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0x8654, 1);
- tot += gspca_dev->usb_buf[0];
- sd->contrast = tot << 6;
- PDEBUG(D_CONF, "get contrast %d", sd->contrast);
-}
-
-/* rev 72a only */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -963,7 +913,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getbrightness(gspca_dev);
*val = sd->brightness;
return 0;
}
@@ -983,7 +932,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- getcontrast(gspca_dev);
*val = sd->contrast;
return 0;
}
@@ -1006,7 +954,6 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-/* rev12a only */
static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1121,6 +1068,19 @@ static struct ctrl sd_ctrls_12a[] = {
static struct ctrl sd_ctrls_72a[] = {
{
+ {
+ .id = V4L2_CID_DO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = WHITE_MIN,
+ .maximum = WHITE_MAX,
+ .step = 1,
+ .default_value = WHITE_DEF,
+ },
+ .set = sd_setwhite,
+ .get = sd_getwhite,
+ },
+ {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index d9d64911f22a..60de9af87fbb 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -109,7 +109,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -424,10 +424,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* beginning of the frame */
#define STKHDRSZ 12
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data + STKHDRSZ, len - STKHDRSZ);
-#undef STKHDRSZ
- return;
+ data += STKHDRSZ;
+ len -= STKHDRSZ;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/video/gspca/stv06xx/Kconfig
new file mode 100644
index 000000000000..634ad38d9fb8
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Kconfig
@@ -0,0 +1,9 @@
+config USB_STV06XX
+ tristate "STV06XX USB Camera Driver"
+ depends on USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on
+ the ST STV06XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv06xx.
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
new file mode 100644
index 000000000000..feeaa94ab588
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+ stv06xx_vv6410.o \
+ stv06xx_hdcs.o \
+ stv06xx_pb0100.o
+
+EXTRA_CFLAGS += -Idrivers/media/video/gspca
+
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
new file mode 100644
index 000000000000..13a021e3cbb7
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_sensor.h"
+
+MODULE_AUTHOR("Erik Andrén");
+MODULE_DESCRIPTION("STV06XX USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int dump_bridge;
+static int dump_sensor;
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+ u8 len = (i2c_data > 0xff) ? 2 : 1;
+
+ buf[0] = i2c_data & 0xff;
+ buf[1] = (i2c_data >> 8) & 0xff;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, address, 0, buf, len,
+ STV06XX_URB_MSG_TIMEOUT);
+
+
+ PDEBUG(D_CONF, "Written 0x%x to address 0x%x, status: %d",
+ i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, address, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+
+ *i2c_data = buf[0];
+
+ PDEBUG(D_CONF, "Read 0x%x from address 0x%x, status %d",
+ *i2c_data, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Wraps the normal write sensor bytes / words functions for writing a
+ single value */
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value)
+{
+ if (sd->sensor->i2c_len == 2) {
+ u16 data[2] = { address, value };
+ return stv06xx_write_sensor_words(sd, data, 1);
+ } else {
+ u8 data[2] = { address, value };
+ return stv06xx_write_sensor_bytes(sd, data, 1);
+ }
+}
+
+static int stv06xx_write_sensor_finish(struct sd *sd)
+{
+ int err = 0;
+
+ if (IS_850(sd)) {
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* Quickam Web needs an extra packet */
+ buf[0] = 0;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1704, 0, buf, 1,
+ STV06XX_URB_MSG_TIMEOUT);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_BYTES && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j] = data[2*i+1];
+ PDEBUG(D_USBO, "I2C: Writing 0x%02x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
+{
+ int err, i, j;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(D_USBO, "I2C: Command buffer contains %d entries", len);
+
+ for (i = 0; i < len;) {
+ /* Build the command buffer */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+ for (j = 0; j < I2C_MAX_WORDS && i < len; j++, i++) {
+ buf[j] = data[2*i];
+ buf[0x10 + j * 2] = data[2*i+1];
+ buf[0x10 + j * 2 + 1] = data[2*i+1] >> 8;
+ PDEBUG(D_USBO, "I2C: Writing 0x%04x to reg 0x%02x",
+ data[2*i+1], data[2*i]);
+ }
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = j - 1; /* Number of commands to send - 1 */
+ buf[0x22] = I2C_WRITE_CMD;
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x0400, 0, buf,
+ I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0)
+ return err;
+ }
+ return stv06xx_write_sensor_finish(sd);
+}
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = stv06xx_write_bridge(sd, STV_I2C_FLUSH, sd->sensor->i2c_flush);
+ if (err < 0)
+ return err;
+
+ /* Clear mem */
+ memset(buf, 0, I2C_BUFFER_LENGTH);
+
+ buf[0] = address;
+ buf[0x20] = sd->sensor->i2c_addr;
+ buf[0x21] = 0;
+
+ /* Read I2C register */
+ buf[0x22] = I2C_READ_CMD;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x1400, 0, buf, I2C_BUFFER_LENGTH,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (err < 0) {
+ PDEBUG(D_ERR, "I2C Read: error writing address: %d", err);
+ return err;
+ }
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x1410, 0, buf, sd->sensor->i2c_len,
+ STV06XX_URB_MSG_TIMEOUT);
+ if (sd->sensor->i2c_len == 2)
+ *value = buf[0] | (buf[1] << 8);
+ else
+ *value = buf[0];
+
+ PDEBUG(D_USBO, "I2C: Read 0x%x from address 0x%x, status: %d",
+ *value, address, err);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Dumps all bridge registers */
+static void stv06xx_dump_bridge(struct sd *sd)
+{
+ int i;
+ u8 data, buf;
+
+ info("Dumping all stv06xx bridge registers");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+
+ info("Read 0x%x from address 0x%x", data, i);
+ }
+
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+ buf = data;
+
+ stv06xx_write_bridge(sd, i, 0xff);
+ stv06xx_read_bridge(sd, i, &data);
+ if (data == 0xff)
+ info("Register 0x%x is read/write", i);
+ else if (data != buf)
+ info("Register 0x%x is read/write,"
+ "but only partially", i);
+ else
+ info("Register 0x%x is read-only", i);
+
+ stv06xx_write_bridge(sd, i, buf);
+ }
+}
+
+/* this function is called at probe and resume time */
+static int stv06xx_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(D_PROBE, "Initializing camera");
+
+ /* Let the usb init settle for a bit
+ before performing the initialization */
+ msleep(250);
+
+ err = sd->sensor->init(sd);
+
+ if (dump_sensor)
+ sd->sensor->dump(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+/* Start the camera */
+static int stv06xx_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ /* Prepare the sensor for start */
+ err = sd->sensor->start(sd);
+ if (err < 0)
+ goto out;
+
+ /* Start isochronous streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 1);
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Starting stream failed");
+ else
+ PDEBUG(D_STREAM, "Started streaming");
+
+ return (err < 0) ? err : 0;
+}
+
+static void stv06xx_stopN(struct gspca_dev *gspca_dev)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* stop ISO-streaming */
+ err = stv06xx_write_bridge(sd, STV_ISO_ENABLE, 0);
+ if (err < 0)
+ goto out;
+
+ err = sd->sensor->stop(sd);
+ if (err < 0)
+ goto out;
+
+out:
+ if (err < 0)
+ PDEBUG(D_STREAM, "Failed to stop stream");
+ else
+ PDEBUG(D_STREAM, "Stopped streaming");
+}
+
+/*
+ * Analyse an USB packet of the data stream and store it appropriately.
+ * Each packet contains an integral number of chunks. Each chunk has
+ * 2-bytes identification, followed by 2-bytes that describe the chunk
+ * length. Known/guessed chunk identifications are:
+ * 8001/8005/C001/C005 - Begin new frame
+ * 8002/8006/C002/C006 - End frame
+ * 0200/4200 - Contains actual image data, bayer or compressed
+ * 0005 - 11 bytes of unknown data
+ * 0100 - 2 bytes of unknown data
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ PDEBUG(D_PACK, "Packet of length %d arrived", len);
+
+ /* A packet may contain several frames
+ loop until the whole packet is reached */
+ while (len) {
+ int id, chunk_len;
+
+ if (len < 4) {
+ PDEBUG(D_PACK, "Packet is smaller than 4 bytes");
+ return;
+ }
+
+ /* Capture the id */
+ id = (data[0] << 8) | data[1];
+
+ /* Capture the chunk length */
+ chunk_len = (data[2] << 8) | data[3];
+ PDEBUG(D_PACK, "Chunk id: %x, length: %d", id, chunk_len);
+
+ data += 4;
+ len -= 4;
+
+ if (len < chunk_len) {
+ PDEBUG(D_ERR, "URB packet length is smaller"
+ " than the specified chunk length");
+ return;
+ }
+
+ switch (id) {
+ case 0x0200:
+ case 0x4200:
+ PDEBUG(D_PACK, "Frame data packet detected");
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, chunk_len);
+ break;
+
+ case 0x8001:
+ case 0x8005:
+ case 0xc001:
+ case 0xc005:
+ PDEBUG(D_PACK, "Starting new frame");
+
+ /* Create a new frame, chunk length should be zero */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a SOF");
+ break;
+
+ case 0x8002:
+ case 0x8006:
+ case 0xc002:
+ PDEBUG(D_PACK, "End of frame detected");
+
+ /* Complete the last frame (if any) */
+ gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+ "non-zero on a EOF");
+ break;
+
+ case 0x0005:
+ PDEBUG(D_PACK, "Chunk 0x005 detected");
+ /* Unknown chunk with 11 bytes of data,
+ occurs just before end of each frame
+ in compressed mode */
+ break;
+
+ case 0x0100:
+ PDEBUG(D_PACK, "Chunk 0x0100 detected");
+ /* Unknown chunk with 2 bytes of data,
+ occurs 2-3 times per USB interrupt */
+ break;
+ default:
+ PDEBUG(D_PACK, "Unknown chunk %d detected", id);
+ /* Unknown chunk */
+ }
+ data += chunk_len;
+ len -= chunk_len;
+ }
+}
+
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = stv06xx_config,
+ .init = stv06xx_init,
+ .start = stv06xx_start,
+ .stopN = stv06xx_stopN,
+ .pkt_scan = stv06xx_pkt_scan
+};
+
+/* This function is called at probe time */
+static int stv06xx_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ PDEBUG(D_PROBE, "Configuring camera");
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = STV_ISOC_ENDPOINT_ADDR;
+ sd->desc = sd_desc;
+ gspca_dev->sd_desc = &sd->desc;
+
+ if (dump_bridge)
+ stv06xx_dump_bridge(sd);
+
+ sd->sensor = &stv06xx_sensor_vv6410;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1x00;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_hdcs1020;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = &stv06xx_sensor_pb0100;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */
+ {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */
+ {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ PDEBUG(D_PROBE, "Probing for a stv06xx device");
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static void sd_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+ PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
+
+ if (sd->sensor->disconnect)
+ sd->sensor->disconnect(sd);
+ gspca_disconnect(intf);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = sd_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all sensor registers at startup");
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
new file mode 100644
index 000000000000..1207e7d17f14
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_H_
+#define STV06XX_H_
+
+#include "gspca.h"
+
+#define MODULE_NAME "STV06xx"
+
+#define STV_ISOC_ENDPOINT_ADDR 0x81
+
+#ifndef V4L2_PIX_FMT_SGRBG8
+#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G')
+#endif
+
+#define STV_REG23 0x0423
+
+/* Control registers of the STV0600 ASIC */
+#define STV_I2C_PARTNER 0x1420
+#define STV_I2C_VAL_REG_VAL_PAIRS_MIN1 0x1421
+#define STV_I2C_READ_WRITE_TOGGLE 0x1422
+#define STV_I2C_FLUSH 0x1423
+#define STV_I2C_SUCC_READ_REG_VALS 0x1424
+
+#define STV_ISO_ENABLE 0x1440
+#define STV_SCAN_RATE 0x1443
+#define STV_LED_CTRL 0x1445
+#define STV_STV0600_EMULATION 0x1446
+#define STV_REG00 0x1500
+#define STV_REG01 0x1501
+#define STV_REG02 0x1502
+#define STV_REG03 0x1503
+#define STV_REG04 0x1504
+
+#define STV_ISO_SIZE_L 0x15c1
+#define STV_ISO_SIZE_H 0x15c2
+
+/* Refers to the CIF 352x288 and QCIF 176x144 */
+/* 1: 288 lines, 2: 144 lines */
+#define STV_Y_CTRL 0x15c3
+
+/* 0xa: 352 columns, 0x6: 176 columns */
+#define STV_X_CTRL 0x1680
+
+#define STV06XX_URB_MSG_TIMEOUT 5000
+
+#define I2C_MAX_BYTES 16
+#define I2C_MAX_WORDS 8
+
+#define I2C_BUFFER_LENGTH 0x23
+#define I2C_READ_CMD 3
+#define I2C_WRITE_CMD 1
+
+#define LED_ON 1
+#define LED_OFF 0
+
+/* STV06xx device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* A pointer to the currently connected sensor */
+ const struct stv06xx_sensor *sensor;
+
+ /* A pointer to the sd_desc struct */
+ struct sd_desc desc;
+
+ /* Sensor private data */
+ void *sensor_priv;
+};
+
+int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data);
+int stv06xx_read_bridge(struct sd *sd, u16 address, u8 *i2c_data);
+
+int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len);
+int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len);
+
+int stv06xx_read_sensor(struct sd *sd, const u8 address, u16 *value);
+int stv06xx_write_sensor(struct sd *sd, u8 address, u16 value);
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
new file mode 100644
index 000000000000..14335a9e4bb5
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_hdcs.h"
+
+enum hdcs_power_state {
+ HDCS_STATE_SLEEP,
+ HDCS_STATE_IDLE,
+ HDCS_STATE_RUN
+};
+
+/* no lock? */
+struct hdcs {
+ enum hdcs_power_state state;
+ int w, h;
+
+ /* visible area of the sensor array */
+ struct {
+ int left, top;
+ int width, height;
+ int border;
+ } array;
+
+ struct {
+ /* Column timing overhead */
+ u8 cto;
+ /* Column processing overhead */
+ u8 cpo;
+ /* Row sample period constant */
+ u16 rs;
+ /* Exposure reset duration */
+ u16 er;
+ } exp;
+
+ int psmp;
+};
+
+static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
+{
+ u8 regs[I2C_MAX_BYTES * 2];
+ int i;
+
+ if (unlikely((len <= 0) || (len >= I2C_MAX_BYTES) ||
+ (reg + len > 0xff)))
+ return -EINVAL;
+
+ for (i = 0; i < len; i++, reg++) {
+ regs[2*i] = reg;
+ regs[2*i+1] = vals[i];
+ }
+
+ return stv06xx_write_sensor_bytes(sd, regs, len);
+}
+
+static int hdcs_set_state(struct sd *sd, enum hdcs_power_state state)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 val;
+ int ret;
+
+ if (hdcs->state == state)
+ return 0;
+
+ /* we need to go idle before running or sleeping */
+ if (hdcs->state != HDCS_STATE_IDLE) {
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (ret)
+ return ret;
+ }
+
+ hdcs->state = HDCS_STATE_IDLE;
+
+ if (state == HDCS_STATE_IDLE)
+ return 0;
+
+ switch (state) {
+ case HDCS_STATE_SLEEP:
+ val = HDCS_SLEEP_MODE;
+ break;
+
+ case HDCS_STATE_RUN:
+ val = HDCS_RUN_ENABLE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), val);
+ if (ret < 0)
+ hdcs->state = state;
+
+ return ret;
+}
+
+static int hdcs_reset(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 1);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONTROL(sd), 0);
+ if (err < 0)
+ hdcs->state = HDCS_STATE_IDLE;
+
+ return err;
+}
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ int cycles;
+ int err;
+ int rowexp;
+ u16 data[2];
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPL, &data[0]);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_read_sensor(sd, HDCS_ROWEXPH, &data[1]);
+ if (err < 0)
+ return err;
+
+ rowexp = (data[1] << 8) | data[0];
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+ rp = hdcs->exp.rs + cp;
+
+ cycles = rp * rowexp;
+ *val = cycles / HDCS_CLK_FREQ_MHZ;
+ PDEBUG(D_V4L2, "Read exposure %d", *val);
+ return 0;
+}
+
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct hdcs *hdcs = sd->sensor_priv;
+ int rowexp, srowexp;
+ int max_srowexp;
+ /* Column time period */
+ int ct;
+ /* Column processing period */
+ int cp;
+ /* Row processing period */
+ int rp;
+ /* Minimum number of column timing periods
+ within the column processing period */
+ int mnct;
+ int cycles, err;
+ u8 exp[4];
+
+ cycles = val * HDCS_CLK_FREQ_MHZ;
+
+ ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
+ cp = hdcs->exp.cto + (hdcs->w * ct / 2);
+
+ /* the cycles one row takes */
+ rp = hdcs->exp.rs + cp;
+
+ rowexp = cycles / rp;
+
+ /* the remaining cycles */
+ cycles -= rowexp * rp;
+
+ /* calculate sub-row exposure */
+ if (IS_1020(sd)) {
+ /* see HDCS-1020 datasheet 3.5.6.4, p. 63 */
+ srowexp = hdcs->w - (cycles + hdcs->exp.er + 13) / ct;
+
+ mnct = (hdcs->exp.er + 12 + ct - 1) / ct;
+ max_srowexp = hdcs->w - mnct;
+ } else {
+ /* see HDCS-1000 datasheet 3.4.5.5, p. 61 */
+ srowexp = cp - hdcs->exp.er - 6 - cycles;
+
+ mnct = (hdcs->exp.er + 5 + ct - 1) / ct;
+ max_srowexp = cp - mnct * ct - 1;
+ }
+
+ if (srowexp < 0)
+ srowexp = 0;
+ else if (srowexp > max_srowexp)
+ srowexp = max_srowexp;
+
+ if (IS_1020(sd)) {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = (srowexp >> 2) & 0xff;
+ /* this clears exposure error flag */
+ exp[3] = 0x1;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ } else {
+ exp[0] = rowexp & 0xff;
+ exp[1] = rowexp >> 8;
+ exp[2] = srowexp & 0xff;
+ exp[3] = srowexp >> 8;
+ err = hdcs_reg_write_seq(sd, HDCS_ROWEXPL, exp, 4);
+ if (err < 0)
+ return err;
+
+ /* clear exposure error flag */
+ err = stv06xx_write_sensor(sd,
+ HDCS_STATUS, BIT(4));
+ }
+ PDEBUG(D_V4L2, "Writing exposure %d, rowexp %d, srowexp %d",
+ val, rowexp, srowexp);
+ return err;
+}
+
+static int hdcs_set_gains(struct sd *sd, u8 r, u8 g, u8 b)
+{
+ u8 gains[4];
+
+ /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
+ if (r > 127)
+ r = 0x80 | (r / 2);
+ if (g > 127)
+ g = 0x80 | (g / 2);
+ if (b > 127)
+ b = 0x80 | (b / 2);
+
+ gains[0] = g;
+ gains[1] = r;
+ gains[2] = b;
+ gains[3] = g;
+
+ return hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
+}
+
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+ u16 data;
+
+ err = stv06xx_read_sensor(sd, HDCS_ERECPGA, &data);
+
+ /* Bit 7 doubles the gain */
+ if (data & 0x80)
+ *val = (data & 0x7f) * 2;
+ else
+ *val = data;
+
+ PDEBUG(D_V4L2, "Read gain %d", *val);
+ return err;
+}
+
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ PDEBUG(D_V4L2, "Writing gain %d", val);
+ return hdcs_set_gains((struct sd *) gspca_dev,
+ val & 0xff, val & 0xff, val & 0xff);
+}
+
+static int hdcs_set_size(struct sd *sd,
+ unsigned int width, unsigned int height)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ u8 win[4];
+ unsigned int x, y;
+ int err;
+
+ /* must be multiple of 4 */
+ width = (width + 3) & ~0x3;
+ height = (height + 3) & ~0x3;
+
+ if (width > hdcs->array.width)
+ width = hdcs->array.width;
+
+ if (IS_1020(sd)) {
+ /* the borders are also invalid */
+ if (height + 2 * hdcs->array.border + HDCS_1020_BOTTOM_Y_SKIP
+ > hdcs->array.height)
+ height = hdcs->array.height - 2 * hdcs->array.border -
+ HDCS_1020_BOTTOM_Y_SKIP;
+
+ y = (hdcs->array.height - HDCS_1020_BOTTOM_Y_SKIP - height) / 2
+ + hdcs->array.top;
+ } else {
+ if (height > hdcs->array.height)
+ height = hdcs->array.height;
+
+ y = hdcs->array.top + (hdcs->array.height - height) / 2;
+ }
+
+ x = hdcs->array.left + (hdcs->array.width - width) / 2;
+
+ win[0] = y / 4;
+ win[1] = x / 4;
+ win[2] = (y + height) / 4 - 1;
+ win[3] = (x + width) / 4 - 1;
+
+ err = hdcs_reg_write_seq(sd, HDCS_FWROW, win, 4);
+ if (err < 0)
+ return err;
+
+ /* Update the current width and height */
+ hdcs->w = width;
+ hdcs->h = height;
+ return err;
+}
+
+static int hdcs_probe_1x00(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x08)
+ return -ENODEV;
+
+ info("HDCS-1000/1100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ hdcs->array.left = 8;
+ hdcs->array.top = 8;
+ hdcs->array.width = HDCS_1X00_DEF_WIDTH;
+ hdcs->array.height = HDCS_1X00_DEF_HEIGHT;
+ hdcs->array.border = 4;
+
+ hdcs->exp.cto = 4;
+ hdcs->exp.cpo = 2;
+ hdcs->exp.rs = 186;
+ hdcs->exp.er = 100;
+
+ /*
+ * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP:
+ * 4 = doesn't work at all
+ * 5 = 7.8 fps,
+ * 6 = 6.9 fps,
+ * 8 = 6.3 fps,
+ * 10 = 5.5 fps,
+ * 15 = 4.4 fps,
+ * 31 = 2.8 fps
+ *
+ * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP:
+ * 15 = doesn't work at all
+ * 18 = doesn't work at all
+ * 19 = 7.3 fps
+ * 20 = 7.4 fps
+ * 21 = 7.4 fps
+ * 22 = 7.4 fps
+ * 24 = 6.3 fps
+ * 30 = 5.4 fps
+ */
+ hdcs->psmp = IS_870(sd) ? 20 : 5;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_probe_1020(struct sd *sd)
+{
+ struct hdcs *hdcs;
+ u16 sensor;
+ int ret;
+
+ ret = stv06xx_read_sensor(sd, HDCS_IDENT, &sensor);
+ if (ret < 0 || sensor != 0x10)
+ return -ENODEV;
+
+ info("HDCS-1020 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls;
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+ return -ENOMEM;
+
+ /*
+ * From Andrey's test image: looks like HDCS-1020 upper-left
+ * visible pixel is at 24,8 (y maybe even smaller?) and lower-right
+ * visible pixel at 375,299 (x maybe even larger?)
+ */
+ hdcs->array.left = 24;
+ hdcs->array.top = 4;
+ hdcs->array.width = HDCS_1020_DEF_WIDTH;
+ hdcs->array.height = 304;
+ hdcs->array.border = 4;
+
+ hdcs->psmp = 6;
+
+ hdcs->exp.cto = 3;
+ hdcs->exp.cpo = 3;
+ hdcs->exp.rs = 155;
+ hdcs->exp.er = 96;
+
+ sd->sensor_priv = hdcs;
+
+ return 0;
+}
+
+static int hdcs_start(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_RUN);
+}
+
+static int hdcs_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return hdcs_set_state(sd, HDCS_STATE_SLEEP);
+}
+
+static void hdcs_disconnect(struct sd *sd)
+{
+ PDEBUG(D_PROBE, "Disconnecting the sensor");
+ kfree(sd->sensor_priv);
+}
+
+static int hdcs_init(struct sd *sd)
+{
+ struct hdcs *hdcs = sd->sensor_priv;
+ int i, err = 0;
+
+ /* Set the STV0602AA in STV0600 emulation mode */
+ if (IS_870(sd))
+ stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1);
+
+ /* Execute the bridge init */
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init) && !err; i++) {
+ err = stv06xx_write_bridge(sd, stv_bridge_init[i][0],
+ stv_bridge_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* sensor soft reset */
+ hdcs_reset(sd);
+
+ /* Execute the sensor init */
+ for (i = 0; i < ARRAY_SIZE(stv_sensor_init) && !err; i++) {
+ err = stv06xx_write_sensor(sd, stv_sensor_init[i][0],
+ stv_sensor_init[i][1]);
+ }
+ if (err < 0)
+ return err;
+
+ /* Enable continous frame capture, bit 2: stop when frame complete */
+ err = stv06xx_write_sensor(sd, HDCS_REG_CONFIG(sd), BIT(3));
+ if (err < 0)
+ return err;
+
+ /* Set PGA sample duration
+ (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */
+ if (IS_1020(sd))
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp);
+ else
+ err = stv06xx_write_sensor(sd, HDCS_TCTRL,
+ (HDCS_ADC_START_SIG_DUR << 5) | hdcs->psmp);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN, HDCS_DEFAULT_GAIN,
+ HDCS_DEFAULT_GAIN);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
+ if (err < 0)
+ return err;
+
+ err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
+ return err;
+}
+
+static int hdcs_dump(struct sd *sd)
+{
+ u16 reg, val;
+
+ info("Dumping sensor registers:");
+
+ for (reg = HDCS_IDENT; reg <= HDCS_ROWEXPH; reg++) {
+ stv06xx_read_sensor(sd, reg, &val);
+ info("reg 0x%02x = 0x%02x", reg, val);
+ }
+ return 0;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
new file mode 100644
index 000000000000..9c7279a4cd88
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ * Copyright (c) 2008 Chia-I Wu
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_HDCS_H_
+#define STV06XX_HDCS_H_
+
+#include "stv06xx_sensor.h"
+
+#define HDCS_REG_CONFIG(sd) (IS_1020(sd) ? HDCS20_CONFIG : HDCS00_CONFIG)
+#define HDCS_REG_CONTROL(sd) (IS_1020(sd) ? HDCS20_CONTROL : HDCS00_CONTROL)
+
+#define HDCS_1X00_DEF_WIDTH 360
+#define HDCS_1X00_DEF_HEIGHT 296
+
+#define HDCS_1020_DEF_WIDTH 352
+#define HDCS_1020_DEF_HEIGHT 292
+
+#define HDCS_1020_BOTTOM_Y_SKIP 4
+
+#define HDCS_CLK_FREQ_MHZ 25
+
+#define HDCS_ADC_START_SIG_DUR 3
+
+/* LSB bit of I2C or register address signifies write (0) or read (1) */
+/* I2C Registers common for both HDCS-1000/1100 and HDCS-1020 */
+/* Identifications Register */
+#define HDCS_IDENT (0x00 << 1)
+/* Status Register */
+#define HDCS_STATUS (0x01 << 1)
+/* Interrupt Mask Register */
+#define HDCS_IMASK (0x02 << 1)
+/* Pad Control Register */
+#define HDCS_PCTRL (0x03 << 1)
+/* Pad Drive Control Register */
+#define HDCS_PDRV (0x04 << 1)
+/* Interface Control Register */
+#define HDCS_ICTRL (0x05 << 1)
+/* Interface Timing Register */
+#define HDCS_ITMG (0x06 << 1)
+/* Baud Fraction Register */
+#define HDCS_BFRAC (0x07 << 1)
+/* Baud Rate Register */
+#define HDCS_BRATE (0x08 << 1)
+/* ADC Control Register */
+#define HDCS_ADCCTRL (0x09 << 1)
+/* First Window Row Register */
+#define HDCS_FWROW (0x0a << 1)
+/* First Window Column Register */
+#define HDCS_FWCOL (0x0b << 1)
+/* Last Window Row Register */
+#define HDCS_LWROW (0x0c << 1)
+/* Last Window Column Register */
+#define HDCS_LWCOL (0x0d << 1)
+/* Timing Control Register */
+#define HDCS_TCTRL (0x0e << 1)
+/* PGA Gain Register: Even Row, Even Column */
+#define HDCS_ERECPGA (0x0f << 1)
+/* PGA Gain Register: Even Row, Odd Column */
+#define HDCS_EROCPGA (0x10 << 1)
+/* PGA Gain Register: Odd Row, Even Column */
+#define HDCS_ORECPGA (0x11 << 1)
+/* PGA Gain Register: Odd Row, Odd Column */
+#define HDCS_OROCPGA (0x12 << 1)
+/* Row Exposure Low Register */
+#define HDCS_ROWEXPL (0x13 << 1)
+/* Row Exposure High Register */
+#define HDCS_ROWEXPH (0x14 << 1)
+
+/* I2C Registers only for HDCS-1000/1100 */
+/* Sub-Row Exposure Low Register */
+#define HDCS00_SROWEXPL (0x15 << 1)
+/* Sub-Row Exposure High Register */
+#define HDCS00_SROWEXPH (0x16 << 1)
+/* Configuration Register */
+#define HDCS00_CONFIG (0x17 << 1)
+/* Control Register */
+#define HDCS00_CONTROL (0x18 << 1)
+
+/* I2C Registers only for HDCS-1020 */
+/* Sub-Row Exposure Register */
+#define HDCS20_SROWEXP (0x15 << 1)
+/* Error Control Register */
+#define HDCS20_ERROR (0x16 << 1)
+/* Interface Timing 2 Register */
+#define HDCS20_ITMG2 (0x17 << 1)
+/* Interface Control 2 Register */
+#define HDCS20_ICTRL2 (0x18 << 1)
+/* Horizontal Blank Register */
+#define HDCS20_HBLANK (0x19 << 1)
+/* Vertical Blank Register */
+#define HDCS20_VBLANK (0x1a << 1)
+/* Configuration Register */
+#define HDCS20_CONFIG (0x1b << 1)
+/* Control Register */
+#define HDCS20_CONTROL (0x1c << 1)
+
+#define HDCS_RUN_ENABLE (1 << 2)
+#define HDCS_SLEEP_MODE (1 << 1)
+
+#define HDCS_DEFAULT_EXPOSURE 5000
+#define HDCS_DEFAULT_GAIN 128
+
+static int hdcs_probe_1x00(struct sd *sd);
+static int hdcs_probe_1020(struct sd *sd);
+static int hdcs_start(struct sd *sd);
+static int hdcs_init(struct sd *sd);
+static int hdcs_stop(struct sd *sd);
+static int hdcs_dump(struct sd *sd);
+static void hdcs_disconnect(struct sd *sd);
+
+static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+ .name = "HP HDCS-1000/1100",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1x00,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .disconnect = hdcs_disconnect,
+ .dump = hdcs_dump,
+
+ .nctrls = 2,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_EXPOSURE,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_exposure,
+ .get = hdcs_get_exposure
+ },
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = HDCS_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = hdcs_set_gain,
+ .get = hdcs_get_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1X00_DEF_WIDTH,
+ HDCS_1X00_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+ .bytesperline = HDCS_1X00_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+ .name = "HDCS-1020",
+ .i2c_flush = 0,
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+ .nctrls = 0,
+ .ctrls = {},
+
+ .init = hdcs_init,
+ .probe = hdcs_probe_1020,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .dump = hdcs_dump,
+
+ .nmodes = 1,
+ .modes = {
+ {
+ HDCS_1020_DEF_WIDTH,
+ HDCS_1020_DEF_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+ .bytesperline = HDCS_1020_DEF_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const u16 stv_bridge_init[][2] = {
+ {STV_ISO_ENABLE, 0},
+ {STV_REG23, 0},
+ {STV_REG00, 0x1d},
+ {STV_REG01, 0xb5},
+ {STV_REG02, 0xa8},
+ {STV_REG03, 0x95},
+ {STV_REG04, 0x07},
+
+ {STV_SCAN_RATE, 0x20},
+ {STV_ISO_SIZE_L, 847},
+ {STV_Y_CTRL, 0x01},
+ {STV_X_CTRL, 0x0a}
+};
+
+static const u8 stv_sensor_init[][2] = {
+ /* Clear status (writing 1 will clear the corresponding status bit) */
+ {HDCS_STATUS, BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ /* Disable all interrupts */
+ {HDCS_IMASK, 0x00},
+ {HDCS_PCTRL, BIT(6) | BIT(5) | BIT(1) | BIT(0)},
+ {HDCS_PDRV, 0x00},
+ {HDCS_ICTRL, BIT(5)},
+ {HDCS_ITMG, BIT(4) | BIT(1)},
+ /* ADC output resolution to 10 bits */
+ {HDCS_ADCCTRL, 10}
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
new file mode 100644
index 000000000000..d0a0f8596454
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+/*
+ * The spec file for the PB-0100 suggests the following for best quality
+ * images after the sensor has been reset :
+ *
+ * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC
+ to produce good black level
+ * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes
+ through R53
+ * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for
+ auto-exposure
+ * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain
+ * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value
+ * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on
+ auto-exposure routine
+ * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate
+ */
+
+#include "stv06xx_pb0100.h"
+
+static int pb0100_probe(struct sd *sd)
+{
+ u16 sensor;
+ int i, err;
+ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if ((sensor >> 8) == 0x64) {
+ sensor_settings = kmalloc(
+ stv06xx_sensor_pb0100.nctrls * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ info("Photobit pb0100 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls;
+ for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++)
+ sensor_settings[i] = stv06xx_sensor_pb0100.
+ ctrls[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int pb0100_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ s32 *sensor_settings = sd->sensor_priv;
+ u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ /* Setup sensor window */
+ if (mode & PB0100_CROP_TO_VGA) {
+ stv06xx_write_sensor(sd, PB_RSTART, 30);
+ stv06xx_write_sensor(sd, PB_CSTART, 20);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1);
+ } else {
+ stv06xx_write_sensor(sd, PB_RSTART, 8);
+ stv06xx_write_sensor(sd, PB_CSTART, 4);
+ stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1);
+ stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1);
+ }
+
+ if (mode & PB0100_SUBSAMPLE) {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+ /* larger -> slower */
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* set_gain also sets red and blue balance */
+ pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
+ pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]);
+ pb0100_set_autogain_target(&sd->gspca_dev,
+ sensor_settings[AUTOGAIN_TARGET_IDX]);
+ pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1));
+ PDEBUG(D_STREAM, "Started stream, status: %d", err);
+
+ return (err < 0) ? err : 0;
+}
+
+static int pb0100_stop(struct sd *sd)
+{
+ int err;
+
+ err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1);
+
+ if (err < 0)
+ goto out;
+
+ /* Set bit 1 to zero */
+ err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ PDEBUG(D_STREAM, "Halting stream");
+out:
+ return (err < 0) ? err : 0;
+}
+
+/* FIXME: Sort the init commands out and put them into tables,
+ this is only for getting the camera to work */
+/* FIXME: No error handling for now,
+ add this once the init has been converted to proper tables */
+static int pb0100_init(struct sd *sd)
+{
+ stv06xx_write_bridge(sd, STV_REG00, 1);
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0);
+
+ /* Reset sensor */
+ stv06xx_write_sensor(sd, PB_RESET, 1);
+ stv06xx_write_sensor(sd, PB_RESET, 0);
+
+ /* Disable chip */
+ stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3));
+
+ /* Gain stuff...*/
+ stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6));
+ stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12);
+
+ /* Set up auto-exposure */
+ /* ADC VREF_HI new setting for a transition
+ from the Expose1 to the Expose2 setting */
+ stv06xx_write_sensor(sd, PB_R28, 12);
+ /* gain max for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180);
+ /* gain min for autoexposure */
+ stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12);
+ /* Maximum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R54, 3);
+ /* Minimum frame integration time (programmed into R8)
+ allowed for auto-exposure routine */
+ stv06xx_write_sensor(sd, PB_R55, 0);
+ stv06xx_write_sensor(sd, PB_UPDATEINT, 1);
+ /* R15 Expose0 (maximum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R15, 800);
+ /* R17 Expose2 (minimum that auto-exposure may use) */
+ stv06xx_write_sensor(sd, PB_R17, 10);
+
+ stv06xx_write_sensor(sd, PB_EXPGAIN, 0);
+
+ /* 0x14 */
+ stv06xx_write_sensor(sd, PB_VOFFSET, 0);
+ /* 0x0D */
+ stv06xx_write_sensor(sd, PB_ADCGAINH, 11);
+ /* Set black level (important!) */
+ stv06xx_write_sensor(sd, PB_ADCGAINL, 0);
+
+ /* ??? */
+ stv06xx_write_bridge(sd, STV_REG00, 0x11);
+ stv06xx_write_bridge(sd, STV_REG03, 0x45);
+ stv06xx_write_bridge(sd, STV_REG04, 0x07);
+
+ /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */
+ stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847);
+
+ /* Scan/timing for the sensor */
+ stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+ stv06xx_write_sensor(sd, PB_CFILLIN, 14);
+ stv06xx_write_sensor(sd, PB_VBL, 0);
+ stv06xx_write_sensor(sd, PB_FINTTIME, 0);
+ stv06xx_write_sensor(sd, PB_RINTTIME, 123);
+
+ stv06xx_write_bridge(sd, STV_REG01, 0xc2);
+ stv06xx_write_bridge(sd, STV_REG02, 0xb0);
+ return 0;
+}
+
+static int pb0100_dump(struct sd *sd)
+{
+ return 0;
+}
+
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[GAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[GAIN_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_G1GAIN, val);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_G2GAIN, val);
+ PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err);
+
+ if (!err)
+ err = pb0100_set_red_balance(gspca_dev,
+ sensor_settings[RED_BALANCE_IDX]);
+ if (!err)
+ err = pb0100_set_blue_balance(gspca_dev,
+ sensor_settings[BLUE_BALANCE_IDX]);
+
+ return err;
+}
+
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[RED_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[RED_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_RGAIN, val);
+ PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[BLUE_BALANCE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[BLUE_BALANCE_IDX] = val;
+ val += sensor_settings[GAIN_IDX];
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+
+ err = stv06xx_write_sensor(sd, PB_BGAIN, val);
+ PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[EXPOSURE_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ if (sensor_settings[AUTOGAIN_IDX])
+ return -EBUSY;
+
+ sensor_settings[EXPOSURE_IDX] = val;
+ err = stv06xx_write_sensor(sd, PB_RINTTIME, val);
+ PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_IDX] = val;
+ if (sensor_settings[AUTOGAIN_IDX]) {
+ if (sensor_settings[NATURAL_IDX])
+ val = BIT(6)|BIT(4)|BIT(0);
+ else
+ val = BIT(4)|BIT(0);
+ } else
+ val = 0;
+
+ err = stv06xx_write_sensor(sd, PB_EXPGAIN, val);
+ PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d",
+ sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX],
+ err);
+
+ return err;
+}
+
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[AUTOGAIN_TARGET_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, totalpixels, brightpixels, darkpixels;
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[AUTOGAIN_TARGET_IDX] = val;
+
+ /* Number of pixels counted by the sensor when subsampling the pixels.
+ * Slightly larger than the real value to avoid oscillation */
+ totalpixels = gspca_dev->width * gspca_dev->height;
+ totalpixels = totalpixels/(8*8) + totalpixels/(64*64);
+
+ brightpixels = (totalpixels * val) >> 8;
+ darkpixels = totalpixels - brightpixels;
+ err = stv06xx_write_sensor(sd, PB_R21, brightpixels);
+ if (!err)
+ err = stv06xx_write_sensor(sd, PB_R22, darkpixels);
+
+ PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err);
+
+ return err;
+}
+
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ *val = sensor_settings[NATURAL_IDX];
+
+ return 0;
+}
+
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 *sensor_settings = sd->sensor_priv;
+
+ sensor_settings[NATURAL_IDX] = val;
+
+ return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]);
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
new file mode 100644
index 000000000000..5ea21a1154c4
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_PB0100_H_
+#define STV06XX_PB0100_H_
+
+#include "stv06xx_sensor.h"
+
+/* mode priv field flags */
+#define PB0100_CROP_TO_VGA 0x01
+#define PB0100_SUBSAMPLE 0x02
+
+/* I2C Registers */
+#define PB_IDENT 0x00 /* Chip Version */
+#define PB_RSTART 0x01 /* Row Window Start */
+#define PB_CSTART 0x02 /* Column Window Start */
+#define PB_RWSIZE 0x03 /* Row Window Size */
+#define PB_CWSIZE 0x04 /* Column Window Size */
+#define PB_CFILLIN 0x05 /* Column Fill-In */
+#define PB_VBL 0x06 /* Vertical Blank Count */
+#define PB_CONTROL 0x07 /* Control Mode */
+#define PB_FINTTIME 0x08 /* Integration Time/Frame Unit Count */
+#define PB_RINTTIME 0x09 /* Integration Time/Row Unit Count */
+#define PB_ROWSPEED 0x0a /* Row Speed Control */
+#define PB_ABORTFRAME 0x0b /* Abort Frame */
+#define PB_R12 0x0c /* Reserved */
+#define PB_RESET 0x0d /* Reset */
+#define PB_EXPGAIN 0x0e /* Exposure Gain Command */
+#define PB_R15 0x0f /* Expose0 */
+#define PB_R16 0x10 /* Expose1 */
+#define PB_R17 0x11 /* Expose2 */
+#define PB_R18 0x12 /* Low0_DAC */
+#define PB_R19 0x13 /* Low1_DAC */
+#define PB_R20 0x14 /* Low2_DAC */
+#define PB_R21 0x15 /* Threshold11 */
+#define PB_R22 0x16 /* Threshold0x */
+#define PB_UPDATEINT 0x17 /* Update Interval */
+#define PB_R24 0x18 /* High_DAC */
+#define PB_R25 0x19 /* Trans0H */
+#define PB_R26 0x1a /* Trans1L */
+#define PB_R27 0x1b /* Trans1H */
+#define PB_R28 0x1c /* Trans2L */
+#define PB_R29 0x1d /* Reserved */
+#define PB_R30 0x1e /* Reserved */
+#define PB_R31 0x1f /* Wait to Read */
+#define PB_PREADCTRL 0x20 /* Pixel Read Control Mode */
+#define PB_R33 0x21 /* IREF_VLN */
+#define PB_R34 0x22 /* IREF_VLP */
+#define PB_R35 0x23 /* IREF_VLN_INTEG */
+#define PB_R36 0x24 /* IREF_MASTER */
+#define PB_R37 0x25 /* IDACP */
+#define PB_R38 0x26 /* IDACN */
+#define PB_R39 0x27 /* DAC_Control_Reg */
+#define PB_R40 0x28 /* VCL */
+#define PB_R41 0x29 /* IREF_VLN_ADCIN */
+#define PB_R42 0x2a /* Reserved */
+#define PB_G1GAIN 0x2b /* Green 1 Gain */
+#define PB_BGAIN 0x2c /* Blue Gain */
+#define PB_RGAIN 0x2d /* Red Gain */
+#define PB_G2GAIN 0x2e /* Green 2 Gain */
+#define PB_R47 0x2f /* Dark Row Address */
+#define PB_R48 0x30 /* Dark Row Options */
+#define PB_R49 0x31 /* Reserved */
+#define PB_R50 0x32 /* Image Test Data */
+#define PB_ADCMAXGAIN 0x33 /* Maximum Gain */
+#define PB_ADCMINGAIN 0x34 /* Minimum Gain */
+#define PB_ADCGLOBALGAIN 0x35 /* Global Gain */
+#define PB_R54 0x36 /* Maximum Frame */
+#define PB_R55 0x37 /* Minimum Frame */
+#define PB_R56 0x38 /* Reserved */
+#define PB_VOFFSET 0x39 /* VOFFSET */
+#define PB_R58 0x3a /* Snap-Shot Sequence Trigger */
+#define PB_ADCGAINH 0x3b /* VREF_HI */
+#define PB_ADCGAINL 0x3c /* VREF_LO */
+#define PB_R61 0x3d /* Reserved */
+#define PB_R62 0x3e /* Reserved */
+#define PB_R63 0x3f /* Reserved */
+#define PB_R64 0x40 /* Red/Blue Gain */
+#define PB_R65 0x41 /* Green 2/Green 1 Gain */
+#define PB_R66 0x42 /* VREF_HI/LO */
+#define PB_R67 0x43 /* Integration Time/Row Unit Count */
+#define PB_R240 0xf0 /* ADC Test */
+#define PB_R241 0xf1 /* Chip Enable */
+#define PB_R242 0xf2 /* Reserved */
+
+static int pb0100_probe(struct sd *sd);
+static int pb0100_start(struct sd *sd);
+static int pb0100_init(struct sd *sd);
+static int pb0100_stop(struct sd *sd);
+static int pb0100_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val);
+static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val);
+static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+ .name = "PB-0100",
+ .i2c_flush = 1,
+ .i2c_addr = 0xba,
+ .i2c_len = 2,
+
+ .nctrls = 7,
+ .ctrls = {
+#define GAIN_IDX 0
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_gain,
+ .get = pb0100_get_gain
+ },
+#define RED_BALANCE_IDX 1
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_red_balance,
+ .get = pb0100_get_red_balance
+ },
+#define BLUE_BALANCE_IDX 2
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -255,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = pb0100_set_blue_balance,
+ .get = pb0100_get_blue_balance
+ },
+#define EXPOSURE_IDX 3
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 511,
+ .step = 1,
+ .default_value = 12
+ },
+ .set = pb0100_set_exposure,
+ .get = pb0100_get_exposure
+ },
+#define AUTOGAIN_IDX 4
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_autogain,
+ .get = pb0100_get_autogain
+ },
+#define AUTOGAIN_TARGET_IDX 5
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain Target",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128
+ },
+ .set = pb0100_set_autogain_target,
+ .get = pb0100_get_autogain_target
+ },
+#define NATURAL_IDX 6
+ {
+ {
+ .id = V4L2_CTRL_CLASS_USER + 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Natural Light Source",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = pb0100_set_natural,
+ .get = pb0100_get_natural
+ },
+ },
+
+ .init = pb0100_init,
+ .probe = pb0100_probe,
+ .start = pb0100_start,
+ .stop = pb0100_stop,
+ .dump = pb0100_dump,
+
+ .nmodes = 2,
+ .modes = {
+/* low res / subsample modes disabled as they are only half res horizontal,
+ halving the vertical resolution does not seem to work */
+ {
+ 320,
+ 240,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .bytesperline = 320,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = PB0100_CROP_TO_VGA
+ },
+ {
+ 352,
+ 288,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage = 352 * 288,
+ .bytesperline = 352,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ },
+ }
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
new file mode 100644
index 000000000000..c726dacefa1f
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_SENSOR_H_
+#define STV06XX_SENSOR_H_
+
+#include "stv06xx.h"
+
+#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850)
+#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870)
+#define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020)
+
+extern const struct stv06xx_sensor stv06xx_sensor_vv6410;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+
+#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+struct stv06xx_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* Sensor i2c address */
+ u8 i2c_addr;
+
+ /* Flush value*/
+ u8 i2c_flush;
+
+ /* length of an i2c word */
+ u8 i2c_len;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Executed at device disconnect */
+ void (*disconnect)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Instructs the sensor to start streaming */
+ int (*start)(struct sd *sd);
+
+ /* Instructs the sensor to stop streaming */
+ int (*stop)(struct sd *sd);
+
+ /* Instructs the sensor to dump all its contents */
+ int (*dump)(struct sd *sd);
+
+ int nctrls;
+ struct ctrl ctrls[STV06XX_MAX_CTRLS];
+
+ char nmodes;
+ struct v4l2_pix_format modes[];
+};
+
+#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
new file mode 100644
index 000000000000..1ca91f2a6dee
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#include "stv06xx_vv6410.h"
+
+static int vv6410_probe(struct sd *sd)
+{
+ u16 data;
+ int err;
+
+ err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+
+ if (err < 0)
+ return -ENODEV;
+
+ if (data == 0x19) {
+ info("vv6410 sensor detected");
+
+ sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes;
+ sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes;
+ sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls;
+ sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int vv6410_init(struct sd *sd)
+{
+ int err = 0, i;
+
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
+ /* if NULL then len contains single value */
+ if (stv_bridge_init[i].data == NULL) {
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start,
+ stv_bridge_init[i].len);
+ } else {
+ int j;
+ for (j = 0; j < stv_bridge_init[i].len; j++)
+ err = stv06xx_write_bridge(sd,
+ stv_bridge_init[i].start + j,
+ stv_bridge_init[i].data[j]);
+ }
+ }
+
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
+ ARRAY_SIZE(vv6410_sensor_init));
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_start(struct sd *sd)
+{
+ int err;
+ struct cam *cam = &sd->gspca_dev.cam;
+ u32 priv = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
+
+ if (priv & VV6410_CROP_TO_QVGA) {
+ PDEBUG(D_CONF, "Cropping to QVGA");
+ stv06xx_write_sensor(sd, VV6410_XENDH, 320 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 240 - 1);
+ } else {
+ stv06xx_write_sensor(sd, VV6410_XENDH, 360 - 1);
+ stv06xx_write_sensor(sd, VV6410_YENDH, 294 - 1);
+ }
+
+ if (priv & VV6410_SUBSAMPLE) {
+ PDEBUG(D_CONF, "Enabling subsampling");
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x06);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10);
+ } else {
+ stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01);
+ stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a);
+
+ stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20);
+ }
+
+ /* Turn on LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_ON);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, 0);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Starting stream");
+
+ return 0;
+}
+
+static int vv6410_stop(struct sd *sd)
+{
+ int err;
+
+ /* Turn off LED */
+ err = stv06xx_write_bridge(sd, STV_LED_CTRL, LED_OFF);
+ if (err < 0)
+ return err;
+
+ err = stv06xx_write_sensor(sd, VV6410_SETUP0, VV6410_LOW_POWER_MODE);
+ if (err < 0)
+ return err;
+
+ PDEBUG(D_STREAM, "Halting stream");
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_dump(struct sd *sd)
+{
+ u8 i;
+ int err = 0;
+
+ info("Dumping all vv6410 sensor registers");
+ for (i = 0; i < 0xff && !err; i++) {
+ u16 data;
+ err = stv06xx_read_sensor(sd, i, &data);
+ info("Register 0x%x contained 0x%x", i, data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_HFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_HFLIP;
+ else
+ i2c_data &= ~VV6410_HFLIP;
+
+ PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+
+ *val = (i2c_data & VV6410_VFLIP) ? 1 : 0;
+
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+
+ if (val)
+ i2c_data |= VV6410_VFLIP;
+ else
+ i2c_data &= ~VV6410_VFLIP;
+
+ PDEBUG(D_V4L2, "Set vertical flip to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_DATAFORMAT, i2c_data);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data);
+
+ *val = i2c_data & 0xf;
+
+ PDEBUG(D_V4L2, "Read analog gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_V4L2, "Set analog gain to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+ return (err < 0) ? err : 0;
+}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
new file mode 100644
index 000000000000..3ff8c4ea3362
--- /dev/null
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher
+ * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland
+ * Copyright (c) 2002, 2003 Tuukka Toivonen
+ * Copyright (c) 2008 Erik Andrén
+ *
+ * This program is free software; you can redistribute 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
+ *
+ * P/N 861037: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600
+ * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express
+ * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam
+ * P/N 861075-0040: Sensor HDCS1000 ASIC
+ * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
+ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web
+ */
+
+#ifndef STV06XX_VV6410_H_
+#define STV06XX_VV6410_H_
+
+#include "stv06xx_sensor.h"
+
+#define VV6410_COLS 416
+#define VV6410_ROWS 320
+
+/* Status registers */
+/* Chip identification number including revision indicator */
+#define VV6410_DEVICEH 0x00
+#define VV6410_DEVICEL 0x01
+
+/* User can determine whether timed I2C data
+ has been consumed by interrogating flag states */
+#define VV6410_STATUS0 0x02
+
+/* Current line counter value */
+#define VV6410_LINECOUNTH 0x03
+#define VV6410_LINECOUNTL 0x04
+
+/* End x coordinate of image size */
+#define VV6410_XENDH 0x05
+#define VV6410_XENDL 0x06
+
+/* End y coordinate of image size */
+#define VV6410_YENDH 0x07
+#define VV6410_YENDL 0x08
+
+/* This is the average pixel value returned from the
+ dark line offset cancellation algorithm */
+#define VV6410_DARKAVGH 0x09
+#define VV6410_DARKAVGL 0x0a
+
+/* This is the average pixel value returned from the
+ black line offset cancellation algorithm */
+#define VV6410_BLACKAVGH 0x0b
+#define VV6410_BLACKAVGL 0x0c
+
+/* Flags to indicate whether the x or y image coordinates have been clipped */
+#define VV6410_STATUS1 0x0d
+
+/* Setup registers */
+
+/* Low-power/sleep modes & video timing */
+#define VV6410_SETUP0 0x10
+
+/* Various parameters */
+#define VV6410_SETUP1 0x11
+
+/* Contains pixel counter reset value used by external sync */
+#define VV6410_SYNCVALUE 0x12
+
+/* Frame grabbing modes (FST, LST and QCK) */
+#define VV6410_FGMODES 0x14
+
+/* FST and QCK mapping modes. */
+#define VV6410_PINMAPPING 0x15
+
+/* Data resolution */
+#define VV6410_DATAFORMAT 0x16
+
+/* Output coding formats */
+#define VV6410_OPFORMAT 0x17
+
+/* Various mode select bits */
+#define VV6410_MODESELECT 0x18
+
+/* Exposure registers */
+/* Fine exposure. */
+#define VV6410_FINEH 0x20
+#define VV6410_FINEL 0x21
+
+/* Coarse exposure */
+#define VV6410_COARSEH 0x22
+#define VV6410_COARSEL 0x23
+
+/* Analog gain setting */
+#define VV6410_ANALOGGAIN 0x24
+
+/* Clock division */
+#define VV6410_CLKDIV 0x25
+
+/* Dark line offset cancellation value */
+#define VV6410_DARKOFFSETH 0x2c
+#define VV6410_DARKOFFSETL 0x2d
+
+/* Dark line offset cancellation enable */
+#define VV6410_DARKOFFSETSETUP 0x2e
+
+/* Video timing registers */
+/* Line Length (Pixel Clocks) */
+#define VV6410_LINELENGTHH 0x52
+#define VV6410_LINELENGTHL 0x53
+
+/* X-co-ordinate of top left corner of region of interest (x-offset) */
+#define VV6410_XOFFSETH 0x57
+#define VV6410_XOFFSETL 0x58
+
+/* Y-coordinate of top left corner of region of interest (y-offset) */
+#define VV6410_YOFFSETH 0x59
+#define VV6410_YOFFSETL 0x5a
+
+/* Field length (Lines) */
+#define VV6410_FIELDLENGTHH 0x61
+#define VV6410_FIELDLENGTHL 0x62
+
+/* System registers */
+/* Black offset cancellation default value */
+#define VV6410_BLACKOFFSETH 0x70
+#define VV6410_BLACKOFFSETL 0x71
+
+/* Black offset cancellation setup */
+#define VV6410_BLACKOFFSETSETUP 0x72
+
+/* Analog Control Register 0 */
+#define VV6410_CR0 0x75
+
+/* Analog Control Register 1 */
+#define VV6410_CR1 0x76
+
+/* ADC Setup Register */
+#define VV6410_AS0 0x77
+
+/* Analog Test Register */
+#define VV6410_AT0 0x78
+
+/* Audio Amplifier Setup Register */
+#define VV6410_AT1 0x79
+
+#define VV6410_HFLIP (1 << 3)
+#define VV6410_VFLIP (1 << 4)
+
+#define VV6410_LOW_POWER_MODE (1 << 0)
+#define VV6410_SOFT_RESET (1 << 2)
+#define VV6410_PAL_25_FPS (0 << 3)
+
+#define VV6410_CLK_DIV_2 (1 << 1)
+
+#define VV6410_FINE_EXPOSURE 320
+#define VV6410_COARSE_EXPOSURE 192
+#define VV6410_DEFAULT_GAIN 5
+
+#define VV6410_SUBSAMPLE 0x01
+#define VV6410_CROP_TO_QVGA 0x02
+
+static int vv6410_probe(struct sd *sd);
+static int vv6410_start(struct sd *sd);
+static int vv6410_init(struct sd *sd);
+static int vv6410_stop(struct sd *sd);
+static int vv6410_dump(struct sd *sd);
+
+/* V4L2 controls supported by the driver */
+static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val);
+static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+ .name = "ST VV6410",
+ .i2c_flush = 5,
+ .i2c_addr = 0x20,
+ .i2c_len = 1,
+ .init = vv6410_init,
+ .probe = vv6410_probe,
+ .start = vv6410_start,
+ .stop = vv6410_stop,
+ .dump = vv6410_dump,
+
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_hflip,
+ .get = vv6410_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_vflip,
+ .get = vv6410_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "analog gain",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = vv6410_set_analog_gain,
+ .get = vv6410_get_analog_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ 356,
+ 292,
+ V4L2_PIX_FMT_SGRBG8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ 356 * 292,
+ .bytesperline = 356,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0
+ }
+ }
+};
+
+/* If NULL, only single value to write, stored in len */
+struct stv_init {
+ const u8 *data;
+ u16 start;
+ u8 len;
+};
+
+static const u8 x1500[] = { /* 0x1500 - 0x150f */
+ 0x0b, 0xa7, 0xb7, 0x00, 0x00
+};
+
+static const u8 x1536[] = { /* 0x1536 - 0x153b */
+ 0x02, 0x00, 0x60, 0x01, 0x20, 0x01
+};
+
+static const u8 x15c1[] = { /* 0x15c1 - 0x15c2 */
+ 0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */
+};
+
+static const struct stv_init stv_bridge_init[] = {
+ /* This reg is written twice. Some kind of reset? */
+ {NULL, 0x1620, 0x80},
+ {NULL, 0x1620, 0x00},
+ {NULL, 0x1423, 0x04},
+ {x1500, 0x1500, ARRAY_SIZE(x1500)},
+ {x1536, 0x1536, ARRAY_SIZE(x1536)},
+ {x15c1, 0x15c1, ARRAY_SIZE(x15c1)}
+};
+
+static const u8 vv6410_sensor_init[][2] = {
+ /* Setup registers */
+ {VV6410_SETUP0, VV6410_SOFT_RESET},
+ {VV6410_SETUP0, VV6410_LOW_POWER_MODE},
+ /* Use shuffled read-out mode */
+ {VV6410_SETUP1, BIT(6)},
+ /* All modes to 1 */
+ {VV6410_FGMODES, BIT(6) | BIT(4) | BIT(2) | BIT(0)},
+ {VV6410_PINMAPPING, 0x00},
+ /* Pre-clock generator divide off */
+ {VV6410_DATAFORMAT, BIT(7) | BIT(0)},
+
+ /* Exposure registers */
+ {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8},
+ {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff},
+ {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8},
+ {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff},
+ {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN},
+ {VV6410_CLKDIV, VV6410_CLK_DIV_2},
+
+ /* System registers */
+ /* Enable voltage doubler */
+ {VV6410_AS0, BIT(6) | BIT(4) | BIT(3) | BIT(2) | BIT(1)},
+ {VV6410_AT0, 0x00},
+ /* Power up audio, differential */
+ {VV6410_AT1, BIT(4)|BIT(0)},
+};
+
+#endif
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index bd9288665a80..6d904d5e4c74 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -123,7 +123,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -136,7 +136,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format custom_mode[] = {
+static const struct v4l2_pix_format custom_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -149,7 +149,7 @@ static struct v4l2_pix_format custom_mode[] = {
.priv = 1},
};
-static struct v4l2_pix_format vga_mode2[] = {
+static const struct v4l2_pix_format vga_mode2[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index eac245d7a756..6ee111a3cbd1 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -233,7 +233,7 @@ static char *effects_control[] = {
"Negative",
};
-static struct v4l2_pix_format vga_mode_t16[] = {
+static const struct v4l2_pix_format vga_mode_t16[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
.sizeimage = 160 * 120 * 4 / 8 + 590,
@@ -499,7 +499,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
msleep(5);
i = 4;
- while (--i < 0) {
+ while (--i > 0) {
byte = reg_r(gspca_dev, 0x0060);
if (!(byte & 0x01))
break;
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index 968a5911704f..94163cceb28a 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -30,15 +30,10 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- int buflen; /* current length of tmpbuf */
- __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */
- __u8 tmpbuf2[352 * 288]; /* no protection... */
+ __u16 brightness;
+ __u16 contrast;
- unsigned short brightness;
- unsigned short contrast;
-
- char packet;
- char synchro;
+ __u8 packet;
};
/* V4L2 controls supported by the driver */
@@ -78,7 +73,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144,
@@ -392,6 +387,8 @@ static void setbrightness(struct gspca_dev *gspca_dev)
/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
+ struct sd *sd = (struct sd *) gspca_dev;
+
reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
tv_8532ReadRegisters(gspca_dev);
@@ -443,6 +440,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
/************************************************/
tv_8532_PollReg(gspca_dev);
reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
+
+ gspca_dev->empty_packet = 0; /* check the empty packets */
+ sd->packet = 0; /* ignore the first packets */
+
return 0;
}
@@ -451,111 +452,36 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
}
-static void tv8532_preprocess(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-/* we should received a whole frame with header and EOL marker
- * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2
- * sequence 2bytes header the Alternate pixels bayer GB 4 bytes
- * Alternate pixels bayer RG 4 bytes EOL */
- int width = gspca_dev->width;
- int height = gspca_dev->height;
- unsigned char *dst = sd->tmpbuf2;
- unsigned char *data = sd->tmpbuf;
- int i;
-
- /* precompute where is the good bayer line */
- if (((data[3] + data[width + 7]) >> 1)
- + (data[4] >> 2)
- + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1)
- + (data[3] >> 2)
- + (data[width + 5] >> 1))
- data += 3;
- else
- data += 2;
- for (i = 0; i < height / 2; i++) {
- memcpy(dst, data, width);
- data += width + 3;
- dst += width;
- memcpy(dst, data, width);
- data += width + 7;
- dst += width;
- }
-}
-
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, /* target */
__u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
-
- if (data[0] != 0x80) {
- sd->packet++;
- if (sd->buflen + len > sizeof sd->tmpbuf) {
- if (gspca_dev->last_packet_type != DISCARD_PACKET) {
- PDEBUG(D_PACK, "buffer overflow");
- gspca_dev->last_packet_type = DISCARD_PACKET;
- }
- return;
- }
- memcpy(&sd->tmpbuf[sd->buflen], data, len);
- sd->buflen += len;
- return;
- }
-
- /* here we detect 0x80 */
- /* counter is limited so we need few header for a frame :) */
-
- /* header 0x80 0x80 0x80 0x80 0x80 */
- /* packet 00 63 127 145 00 */
- /* sof 0 1 1 0 0 */
-
- /* update sequence */
- if (sd->packet == 63 || sd->packet == 127)
- sd->synchro = 1;
-
- /* is there a frame start ? */
- if (sd->packet >= (gspca_dev->height >> 1) - 1) {
- PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro,
- sd->packet);
- if (!sd->synchro) { /* start of frame */
- if (gspca_dev->last_packet_type == FIRST_PACKET) {
- tv8532_preprocess(gspca_dev);
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET,
- frame, sd->tmpbuf2,
- gspca_dev->width *
- gspca_dev->width);
- }
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, 0);
- memcpy(sd->tmpbuf, data, len);
- sd->buflen = len;
- sd->packet = 0;
- return;
- }
- if (gspca_dev->last_packet_type != DISCARD_PACKET) {
- PDEBUG(D_PACK,
- "Warning wrong TV8532 frame detection %d",
- sd->packet);
- gspca_dev->last_packet_type = DISCARD_PACKET;
- }
- return;
- }
-
- if (!sd->synchro) {
- /* Drop packet frame corrupt */
- PDEBUG(D_PACK, "DROP SOF %d packet %d",
- sd->synchro, sd->packet);
- sd->packet = 0;
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- sd->synchro = 1;
- sd->packet++;
- memcpy(&sd->tmpbuf[sd->buflen], data, len);
- sd->buflen += len;
+ int packet_type0, packet_type1;
+
+ packet_type0 = packet_type1 = INTER_PACKET;
+ if (gspca_dev->empty_packet) {
+ gspca_dev->empty_packet = 0;
+ sd->packet = gspca_dev->height / 2;
+ packet_type0 = FIRST_PACKET;
+ } else if (sd->packet == 0)
+ return; /* 2 more lines in 352x288 ! */
+ sd->packet--;
+ if (sd->packet == 0)
+ packet_type1 = LAST_PACKET;
+
+ /* each packet contains:
+ * - header 2 bytes
+ * - RG line
+ * - 4 bytes
+ * - GB line
+ * - 4 bytes
+ */
+ gspca_frame_add(gspca_dev, packet_type0,
+ frame, data + 2, gspca_dev->width);
+ gspca_frame_add(gspca_dev, packet_type1,
+ frame, data + gspca_dev->width + 6, gspca_dev->width);
}
static void setcontrast(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 17af353ddd1c..0525ea51a6de 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -32,44 +32,68 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char autogain;
- unsigned char lightfreq;
+ __u8 hflip;
+ __u8 vflip;
+ __u8 lightfreq;
+ __u8 sharpness;
- char qindex;
char bridge;
#define BRIDGE_VC0321 0
#define BRIDGE_VC0323 1
char sensor;
#define SENSOR_HV7131R 0
-#define SENSOR_MI1320 1
-#define SENSOR_MI1310_SOC 2
-#define SENSOR_OV7660 3
-#define SENSOR_OV7670 4
-#define SENSOR_PO3130NC 5
+#define SENSOR_MI0360 1
+#define SENSOR_MI1320 2
+#define SENSOR_MI1310_SOC 3
+#define SENSOR_OV7660 4
+#define SENSOR_OV7670 5
+#define SENSOR_PO1200 6
+#define SENSOR_PO3130NC 7
};
/* V4L2 controls supported by the driver */
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
+/* next 2 controls work with ov7660 and ov7670 only */
+#define HFLIP_IDX 0
{
{
- .id = V4L2_CID_AUTOGAIN,
+ .id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
+ .name = "Mirror",
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set = sd_sethflip,
+ .get = sd_gethflip,
},
-#define LIGHTFREQ_IDX 1
+#define VFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+#define LIGHTFREQ_IDX 2
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -84,9 +108,25 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setfreq,
.get = sd_getfreq,
},
+/* po1200 only */
+#define SHARPNESS_IDX 3
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+#define SHARPNESS_DEF 1
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
};
-static struct v4l2_pix_format vc0321_mode[] = {
+static const struct v4l2_pix_format vc0321_mode[] = {
{320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 2,
@@ -98,7 +138,7 @@ static struct v4l2_pix_format vc0321_mode[] = {
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0},
};
-static struct v4l2_pix_format vc0323_mode[] = {
+static const struct v4l2_pix_format vc0323_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -111,6 +151,252 @@ static struct v4l2_pix_format vc0323_mode[] = {
.priv = 0},
};
+static const struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 1 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR 0x20
+#define OV7660_MVFP_VFLIP 0x10
+
+static const __u8 mi0360_matrix[9] = {
+ 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
+};
+
+static const __u8 mi0360_initVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0x35, 0x00, 0x60, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const __u8 mi0360_initQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0x35, 0x00, 0xef, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
static const __u8 mi1310_socinitVGA_JPG[][4] = {
{0xb0, 0x03, 0x19, 0xcc},
{0xb0, 0x04, 0x02, 0xcc},
@@ -823,7 +1109,7 @@ static const __u8 ov7660_initVGA_data[][4] = {
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa},
{0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -877,7 +1163,7 @@ static const __u8 ov7660_initQVGA_data[][4] = {
{0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -983,7 +1269,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1048,7 +1335,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1110,7 +1398,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1175,7 +1464,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1204,6 +1494,275 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{},
};
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const __u8 po1200_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const __u8 po1200_matrix[9] = {
+ 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const __u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
+ {0xb0, 0x03, 0x19, 0xcc},
+/* {0x00, 0x00, 0x33, 0xdd}, */
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb0, 0x02, 0x02, 0xcc},
+ {0xb3, 0x5d, 0x00, 0xcc},
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x02, 0xb2, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x02, 0xcc},
+ {0xb3, 0x23, 0x58, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x03, 0xcc},
+ {0xb3, 0x17, 0x1f, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x25, 0x02, 0xaa},
+ {0x00, 0x3c, 0x4f, 0xaa},
+ {0x00, 0x3f, 0xe0, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x45, 0x34, 0xaa},
+ {0x00, 0x55, 0xfe, 0xaa},
+ {0x00, 0x59, 0xd3, 0xaa},
+ {0x00, 0x5e, 0x04, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+ {0x00, 0x62, 0x02, 0xaa},
+ {0x00, 0xa7, 0x31, 0xaa},
+ {0x00, 0xa9, 0x66, 0xaa},
+ {0x00, 0xb0, 0x00, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb3, 0x11, 0xaa},
+ {0x00, 0xb6, 0x26, 0xaa},
+ {0x00, 0xb7, 0x20, 0xaa},
+ {0x00, 0xba, 0x04, 0xaa},
+ {0x00, 0x88, 0x42, 0xaa},
+ {0x00, 0x89, 0x9a, 0xaa},
+ {0x00, 0x8a, 0x88, 0xaa},
+ {0x00, 0x8b, 0x8e, 0xaa},
+ {0x00, 0x8c, 0x3e, 0xaa},
+ {0x00, 0x8d, 0x90, 0xaa},
+ {0x00, 0x8e, 0x87, 0xaa},
+ {0x00, 0x8f, 0x96, 0xaa},
+ {0x00, 0x90, 0x3d, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x10, 0xaa},
+ {0x00, 0x66, 0x20, 0xaa},
+ {0x00, 0x67, 0x2b, 0xaa},
+ {0x00, 0x68, 0x36, 0xaa},
+ {0x00, 0x69, 0x49, 0xaa},
+ {0x00, 0x6a, 0x5a, 0xaa},
+ {0x00, 0x6b, 0x7f, 0xaa},
+ {0x00, 0x6c, 0x9b, 0xaa},
+ {0x00, 0x6d, 0xba, 0xaa},
+ {0x00, 0x6e, 0xd4, 0xaa},
+ {0x00, 0x6f, 0xea, 0xaa},
+ {0x00, 0x70, 0x00, 0xaa},
+ {0x00, 0x71, 0x10, 0xaa},
+ {0x00, 0x72, 0x20, 0xaa},
+ {0x00, 0x73, 0x2b, 0xaa},
+ {0x00, 0x74, 0x36, 0xaa},
+ {0x00, 0x75, 0x49, 0xaa},
+ {0x00, 0x76, 0x5a, 0xaa},
+ {0x00, 0x77, 0x7f, 0xaa},
+ {0x00, 0x78, 0x9b, 0xaa},
+ {0x00, 0x79, 0xba, 0xaa},
+ {0x00, 0x7a, 0xd4, 0xaa},
+ {0x00, 0x7b, 0xea, 0xaa},
+ {0x00, 0x7c, 0x00, 0xaa},
+ {0x00, 0x7d, 0x10, 0xaa},
+ {0x00, 0x7e, 0x20, 0xaa},
+ {0x00, 0x7f, 0x2b, 0xaa},
+ {0x00, 0x80, 0x36, 0xaa},
+ {0x00, 0x81, 0x49, 0xaa},
+ {0x00, 0x82, 0x5a, 0xaa},
+ {0x00, 0x83, 0x7f, 0xaa},
+ {0x00, 0x84, 0x9b, 0xaa},
+ {0x00, 0x85, 0xba, 0xaa},
+ {0x00, 0x86, 0xd4, 0xaa},
+ {0x00, 0x87, 0xea, 0xaa},
+ {0x00, 0x57, 0x2a, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x04, 0x10, 0xaa},
+ {0x00, 0x05, 0x10, 0xaa},
+ {0x00, 0x06, 0x10, 0xaa},
+ {0x00, 0x07, 0x10, 0xaa},
+ {0x00, 0x08, 0x13, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x10, 0xaa},
+ {0x00, 0x0c, 0x20, 0xaa},
+ {0x00, 0x0d, 0x18, 0xaa},
+ {0x00, 0x22, 0x01, 0xaa},
+ {0x00, 0x23, 0x60, 0xaa},
+ {0x00, 0x25, 0x08, 0xaa},
+ {0x00, 0x26, 0x82, 0xaa},
+ {0x00, 0x2e, 0x0f, 0xaa},
+ {0x00, 0x2f, 0x1e, 0xaa},
+ {0x00, 0x30, 0x2d, 0xaa},
+ {0x00, 0x31, 0x3c, 0xaa},
+ {0x00, 0x32, 0x4b, 0xaa},
+ {0x00, 0x33, 0x5a, 0xaa},
+ {0x00, 0x34, 0x69, 0xaa},
+ {0x00, 0x35, 0x78, 0xaa},
+ {0x00, 0x36, 0x87, 0xaa},
+ {0x00, 0x37, 0x96, 0xaa},
+ {0x00, 0x38, 0xa5, 0xaa},
+ {0x00, 0x39, 0xb4, 0xaa},
+ {0x00, 0x3a, 0xc3, 0xaa},
+ {0x00, 0x3b, 0xd2, 0xaa},
+ {0x00, 0x3c, 0xe1, 0xaa},
+ {0x00, 0x3e, 0xff, 0xaa},
+ {0x00, 0x3f, 0xff, 0xaa},
+ {0x00, 0x40, 0xff, 0xaa},
+ {0x00, 0x41, 0xff, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x43, 0xff, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x20, 0xc4, 0xaa},
+ {0x00, 0x13, 0x03, 0xaa},
+ {0x00, 0x3c, 0x50, 0xaa},
+ {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
+ {0x00, 0x51, 0x5b, 0xaa},
+ {0x00, 0x52, 0x91, 0xaa},
+ {0x00, 0x53, 0x4c, 0xaa},
+ {0x00, 0x54, 0x50, 0xaa},
+ {0x00, 0x56, 0x02, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x03, 0xcc},
+ {0xb6, 0x02, 0x20, 0xcc},
+ {0xb6, 0x05, 0x02, 0xcc},
+ {0xb6, 0x04, 0x58, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x21, 0xcc},
+ {0xb6, 0x18, 0x03, 0xcc},
+ {0xb6, 0x17, 0xa9, 0xcc},
+ {0xb6, 0x16, 0x80, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb8, 0x06, 0x20, 0xcc},
+ {0xb8, 0x07, 0x03, 0xcc},
+ {0xb8, 0x08, 0x58, 0xcc},
+ {0xb8, 0x09, 0x02, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xd9, 0x0f, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x10, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x11, 0xaa},
+ {0x00, 0xda, 0x00, 0xaa},
+ {0x00, 0xd9, 0x12, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xd9, 0x13, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xe8, 0x11, 0xaa},
+ {0x00, 0xe9, 0x12, 0xaa},
+ {0x00, 0xea, 0x5c, 0xaa},
+ {0x00, 0xeb, 0xff, 0xaa},
+ {0x00, 0xd8, 0x80, 0xaa},
+ {0x00, 0xe6, 0x02, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe3, 0x05, 0xaa},
+ {0x00, 0xe0, 0x40, 0xaa},
+ {0x00, 0xde, 0x03, 0xaa},
+ {0x00, 0xdf, 0x03, 0xaa},
+ {0x00, 0xdb, 0x02, 0xaa},
+ {0x00, 0xdc, 0x00, 0xaa},
+ {0x00, 0xdd, 0x03, 0xaa},
+ {0x00, 0xe1, 0x08, 0xaa},
+ {0x00, 0xe2, 0x01, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe4, 0x40, 0xaa},
+ {0x00, 0xa8, 0x8f, 0xaa},
+ {0x00, 0xb4, 0x16, 0xaa},
+ {0xb0, 0x02, 0x06, 0xcc},
+ {0xb0, 0x18, 0x06, 0xcc},
+ {0xb0, 0x19, 0x06, 0xcc},
+ {0xb3, 0x5d, 0x18, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0x00, 0xb4, 0x0e, 0xaa},
+ {0x00, 0xb5, 0x49, 0xaa},
+ {0x00, 0xb6, 0x1c, 0xaa},
+ {0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1a, 0x09, 0xaa},
+ {0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+ {0xb8, 0x27, 0x00, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x91, 0x35, 0xaa},
+ {0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x4d, 0x20, 0xaa},
+ {0xb8, 0x22, 0x40, 0xcc},
+ {0xb8, 0x23, 0x40, 0xcc},
+ {0xb8, 0x24, 0x40, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x16, 0x40, 0xaa},
+ {0x00, 0x17, 0x40, 0xaa},
+ {0x00, 0x18, 0x40, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xb4, 0x1c, 0xaa},
+ {0x00, 0xb5, 0x92, 0xaa},
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+};
+
struct sensor_info {
int sensorId;
__u8 I2cAdd;
@@ -1222,6 +1781,9 @@ static const struct sensor_info sensor_info_data[] = {
{SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01},
{SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
{SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* (tested in vc032x_probe_sensor) */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
};
/* read 'len' bytes in gspca_dev->usb_buf */
@@ -1278,18 +1840,18 @@ static void read_sensor_register(struct gspca_dev *gspca_dev,
msleep(1);
}
reg_r(gspca_dev, 0xa1, 0xb33e, 1);
- hdata = gspca_dev->usb_buf[0];
+ ldata = gspca_dev->usb_buf[0];
reg_r(gspca_dev, 0xa1, 0xb33d, 1);
mdata = gspca_dev->usb_buf[0];
reg_r(gspca_dev, 0xa1, 0xb33c, 1);
- ldata = gspca_dev->usb_buf[0];
- PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)",
+ hdata = gspca_dev->usb_buf[0];
+ PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
hdata, mdata, ldata);
reg_r(gspca_dev, 0xa1, 0xb334, 1);
if (gspca_dev->usb_buf[0] == 0x02)
- *value = (ldata << 8) + mdata;
+ *value = (hdata << 8) + mdata;
else
- *value = ldata;
+ *value = hdata;
}
static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
@@ -1300,7 +1862,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
const struct sensor_info *ptsensor_info;
reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
- PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]);
+ PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]);
for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
ptsensor_info = &sensor_info_data[i];
reg_w(dev, 0xa0, 0x02, 0xb334);
@@ -1309,16 +1871,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
reg_w(dev, 0xa0, 0x01, 0xb308);
reg_w(dev, 0xa0, 0x0c, 0xb309);
reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
-/* PDEBUG(D_PROBE,
- "check sensor VC032X -> %d Add -> ox%02X!",
- i, ptsensor_info->I2cAdd); */
reg_w(dev, 0xa0, ptsensor_info->op, 0xb301);
read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value);
- if (value == ptsensor_info->VpId) {
-/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!",
- ptsensor_info->VpId); */
+ if (value == ptsensor_info->VpId)
return ptsensor_info->sensorId;
- }
+
+ /* special case for MI0360 */
+ if (ptsensor_info->sensorId == SENSOR_MI1310_SOC
+ && value == 0x8243)
+ return SENSOR_MI0360;
}
return -1;
}
@@ -1420,13 +1981,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->epaddr = 0x02;
sd->bridge = id->driver_info;
- if (sd->bridge == BRIDGE_VC0321) {
- cam->cam_mode = vc0321_mode;
- cam->nmodes = ARRAY_SIZE(vc0321_mode);
- } else {
- cam->cam_mode = vc0323_mode;
- cam->nmodes = ARRAY_SIZE(vc0323_mode);
- }
vc0321_reset(gspca_dev);
sensor = vc032x_probe_sensor(gspca_dev);
@@ -1436,35 +1990,66 @@ static int sd_config(struct gspca_dev *gspca_dev,
return -EINVAL;
case SENSOR_HV7131R:
PDEBUG(D_PROBE, "Find Sensor HV7131R");
- sd->sensor = SENSOR_HV7131R;
+ break;
+ case SENSOR_MI0360:
+ PDEBUG(D_PROBE, "Find Sensor MI0360");
+ sd->bridge = BRIDGE_VC0323;
break;
case SENSOR_MI1310_SOC:
PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
- sd->sensor = SENSOR_MI1310_SOC;
break;
case SENSOR_MI1320:
PDEBUG(D_PROBE, "Find Sensor MI1320");
- sd->sensor = SENSOR_MI1320;
break;
case SENSOR_OV7660:
PDEBUG(D_PROBE, "Find Sensor OV7660");
- sd->sensor = SENSOR_OV7660;
break;
case SENSOR_OV7670:
PDEBUG(D_PROBE, "Find Sensor OV7670");
- sd->sensor = SENSOR_OV7670;
+ break;
+ case SENSOR_PO1200:
+ PDEBUG(D_PROBE, "Find Sensor PO1200");
break;
case SENSOR_PO3130NC:
PDEBUG(D_PROBE, "Find Sensor PO3130NC");
- sd->sensor = SENSOR_PO3130NC;
break;
}
+ sd->sensor = sensor;
- sd->qindex = 7;
- sd->autogain = AUTOGAIN_DEF;
+ if (sd->bridge == BRIDGE_VC0321) {
+ cam->cam_mode = vc0321_mode;
+ cam->nmodes = ARRAY_SIZE(vc0321_mode);
+ } else {
+ if (sensor != SENSOR_PO1200) {
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode);
+ } else {
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
+ }
+ }
+
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ if (sd->sensor == SENSOR_OV7670) {
+ sd->hflip = 1;
+ sd->vflip = 1;
+ }
sd->lightfreq = FREQ_DEF;
if (sd->sensor != SENSOR_OV7670)
gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_PO1200:
+ break;
+ default:
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
+ | (1 << VFLIP_IDX);
+ break;
+ }
+
+ sd->sharpness = SHARPNESS_DEF;
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
@@ -1482,12 +2067,33 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setquality(struct gspca_dev *gspca_dev)
+/* for OV7660 and OV7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev)
{
-}
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
-static void setautogain(struct gspca_dev *gspca_dev)
-{
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ data = 1;
+ break;
+ case SENSOR_OV7670:
+ data = 7;
+ break;
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0x80 * sd->hflip
+ | 0x40 * sd->vflip
+ | 0x06;
+ i2c_write(gspca_dev, 0x1e, &data, 1);
+ return;
+ default:
+ return;
+ }
+ data |= OV7660_MVFP_MIRROR * sd->hflip
+ | OV7660_MVFP_VFLIP * sd->vflip;
+ i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1);
}
static void setlightfreq(struct gspca_dev *gspca_dev)
@@ -1501,6 +2107,20 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
+/* po1200 only */
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+
+ if (sd->sensor != SENSOR_PO1200)
+ return;
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0xb5 + sd->sharpness * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+}
+
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1551,6 +2171,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7670_initVGA_JPG);
}
break;
+ case SENSOR_MI0360:
+ GammaT = mi1320_gamma;
+ MatrixT = mi0360_matrix;
+ if (mode) {
+ /* 320x240 */
+ usb_exchange(gspca_dev, mi0360_initQVGA_JPG);
+ } else {
+ /* 640x480 */
+ usb_exchange(gspca_dev, mi0360_initVGA_JPG);
+ }
+ break;
case SENSOR_MI1310_SOC:
if (mode) {
/* 320x240 */
@@ -1583,6 +2214,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
usb_exchange(gspca_dev, po3130_rundata);
break;
+ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ usb_exchange(gspca_dev, po1200_initVGA_data);
+ break;
default:
PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
return -EMEDIUMTYPE;
@@ -1615,11 +2251,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS
*/
/* set the led on 0x0892 0x0896 */
- reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
- msleep(100);
- setquality(gspca_dev);
- setautogain(gspca_dev);
- setlightfreq(gspca_dev);
+ if (sd->sensor != SENSOR_PO1200) {
+ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+ msleep(100);
+ sethvflip(gspca_dev);
+ setlightfreq(gspca_dev);
+ } else {
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415);
+ }
}
return 0;
}
@@ -1665,24 +2306,48 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data, len);
return;
}
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+ if (sd->bridge == BRIDGE_VC0321
+ && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
+ len = frame->v4l2_buf.length - (frame->data_end - frame->data);
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- sd->autogain = val;
+ sd->vflip = val;
if (gspca_dev->streaming)
- setautogain(gspca_dev);
+ sethvflip(gspca_dev);
return 0;
}
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->autogain;
+ *val = sd->vflip;
return 0;
}
@@ -1704,6 +2369,24 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming)
+ setsharpness(gspca_dev);
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1743,11 +2426,13 @@ static const struct sd_desc sd_desc = {
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321},
{USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323},
{}
};
diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h
index f52e09c2cc19..bfb559c3b713 100644
--- a/drivers/media/video/gspca/zc3xx-reg.h
+++ b/drivers/media/video/gspca/zc3xx-reg.h
@@ -244,14 +244,6 @@
#define ZC3XX_R1CA_SHARPNESS04 0x01ca
#define ZC3XX_R1CB_SHARPNESS05 0x01cb
-/* Synchronization */
-#define ZC3XX_R190_SYNC00LOW 0x0190
-#define ZC3XX_R191_SYNC00MID 0x0191
-#define ZC3XX_R192_SYNC00HIGH 0x0192
-#define ZC3XX_R195_SYNC01LOW 0x0195
-#define ZC3XX_R196_SYNC01MID 0x0196
-#define ZC3XX_R197_SYNC01HIGH 0x0197
-
/* Dead pixels */
#define ZC3XX_R250_DEADPIXELSMODE 0x0250
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 0befacf49855..ec2a53d53fe2 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -51,16 +51,16 @@ struct sd {
#define SENSOR_CS2102 0
#define SENSOR_CS2102K 1
#define SENSOR_GC0305 2
-#define SENSOR_HDCS2020 3
-#define SENSOR_HDCS2020b 4
-#define SENSOR_HV7131B 5
-#define SENSOR_HV7131C 6
-#define SENSOR_ICM105A 7
-#define SENSOR_MC501CB 8
-#define SENSOR_OV7620 9
-/*#define SENSOR_OV7648 9 - same values */
-#define SENSOR_OV7630C 10
-#define SENSOR_PAS106 11
+#define SENSOR_HDCS2020b 3
+#define SENSOR_HV7131B 4
+#define SENSOR_HV7131C 5
+#define SENSOR_ICM105A 6
+#define SENSOR_MC501CB 7
+#define SENSOR_OV7620 8
+/*#define SENSOR_OV7648 8 - same values */
+#define SENSOR_OV7630C 9
+#define SENSOR_PAS106 10
+#define SENSOR_PAS202B 11
#define SENSOR_PB0330 12
#define SENSOR_PO2030 13
#define SENSOR_TAS5130CK 14
@@ -173,7 +173,7 @@ static struct ctrl sd_ctrls[] = {
},
};
-static struct v4l2_pix_format vga_mode[] = {
+static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
@@ -186,7 +186,7 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static struct v4l2_pix_format sif_mode[] = {
+static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
@@ -1653,295 +1653,6 @@ static const struct usb_action gc0305_NoFliker[] = {
{}
};
-/* play poker with registers at your own risk !! */
-static const struct usb_action hdcs2020xx_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW},
- /* D0 ?? E0 did not start */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
-
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf5, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xf9, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf5, ZC3XX_R10F_RGB12},
- {0xa0, 0xfb, ZC3XX_R110_RGB20},
- {0xa0, 0xed, ZC3XX_R111_RGB21},
- {0xa0, 0x5f, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x003d},
- {0xaa, 0x03, 0x0041},
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x003d},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
- {}
-};
-
-static const struct usb_action hdcs2020xx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xff, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xff, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xff, ZC3XX_R10F_RGB12},
- {0xa0, 0xff, ZC3XX_R110_RGB20},
- {0xa0, 0xff, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0002},
- {0xaa, 0x21, 0x001b},
- {0xaa, 0x03, 0x0044},
- {0xaa, 0x04, 0x0008},
- {0xaa, 0x05, 0x001b},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x44, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xeb, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
-/* {0xa0, 0x18, 0x00fe}, */
- {}
-};
static const struct usb_action hdcs2020xb_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
@@ -2310,67 +2021,6 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xaa, 0x02, 0x0090}, /* 00,02,80,aa */
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x00a1},
- {0xaa, 0x27, 0x0020},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x21, 0x00a0},
- {0xaa, 0x22, 0x0016},
- {0xaa, 0x23, 0x0040},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2F */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x07, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x16, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x40, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
{}
};
@@ -2418,65 +2068,156 @@ static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x00a1},
- {0xaa, 0x27, 0x0020},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x21, 0x0040},
- {0xaa, 0x22, 0x0013},
- {0xaa, 0x23, 0x004c},
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 60 */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x13, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x4c, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
+ {}
+};
+static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
+ {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */
+ {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */
+ {}
+};
+static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
+ {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */
+ {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
+ {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
+ {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+ {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */
+ {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */
+ {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
{}
};
@@ -4389,6 +4130,270 @@ static const struct usb_action pas106b_NoFliker[] = {
{}
};
+/* from usbvm31b.inf */
+static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+ {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+
static const struct usb_action pb03303x_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5725,7 +5730,7 @@ static const struct usb_action tas5130cxx_Initial[] = {
{}
};
static const struct usb_action tas5130cxx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
@@ -6049,7 +6054,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */
{0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */
{0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
- {0xaa, 0x01, 0x0000},
+/*?? {0xaa, 0x01, 0x0000}, */
{0xaa, 0x01, 0x0000},
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
{0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
@@ -6065,8 +6070,8 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */
{0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */
{0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */
- {0xa0, 0x00, 0x0039},
- {0xa1, 0x01, 0x0037},
+/*?? {0xa0, 0x00, 0x0039},
+ {0xa1, 0x01, 0x0037}, */
{0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
{0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
{0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
@@ -6303,7 +6308,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev,
reg_w_i(gspca_dev->dev, valL, 0x93);
reg_w_i(gspca_dev->dev, valH, 0x94);
reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
- msleep(5);
+ msleep(15);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
@@ -6346,30 +6351,35 @@ static void setmatrix(struct gspca_dev *gspca_dev)
{0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
static const __u8 ov7620_matrix[9] =
{0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+ static const __u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
static const __u8 po2030_matrix[9] =
{0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
static const __u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
+ static const __u8 *matrix_tb[SENSOR_MAX] = {
+ NULL, /* SENSOR_CS2102 0 */
+ NULL, /* SENSOR_CS2102K 1 */
+ gc0305_matrix, /* SENSOR_GC0305 2 */
+ NULL, /* SENSOR_HDCS2020b 3 */
+ NULL, /* SENSOR_HV7131B 4 */
+ NULL, /* SENSOR_HV7131C 5 */
+ NULL, /* SENSOR_ICM105A 6 */
+ NULL, /* SENSOR_MC501CB 7 */
+ ov7620_matrix, /* SENSOR_OV7620 8 */
+ NULL, /* SENSOR_OV7630C 9 */
+ NULL, /* SENSOR_PAS106 10 */
+ pas202b_matrix, /* SENSOR_PAS202B 11 */
+ NULL, /* SENSOR_PB0330 12 */
+ po2030_matrix, /* SENSOR_PO2030 13 */
+ NULL, /* SENSOR_TAS5130CK 14 */
+ NULL, /* SENSOR_TAS5130CXX 15 */
+ vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */
+ };
- switch (sd->sensor) {
- case SENSOR_GC0305:
- matrix = gc0305_matrix;
- break;
- case SENSOR_MC501CB:
- return; /* no matrix? */
- case SENSOR_OV7620:
-/* case SENSOR_OV7648: */
- matrix = ov7620_matrix;
- break;
- case SENSOR_PO2030:
- matrix = po2030_matrix;
- break;
- case SENSOR_TAS5130C_VF0250:
- matrix = vf0250_matrix;
- break;
- default: /* matrix already loaded */
- return;
- }
+ matrix = matrix_tb[sd->sensor];
+ if (matrix == NULL)
+ return; /* matrix already loaded */
for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
}
@@ -6585,42 +6595,42 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
{gc0305_NoFliker, gc0305_NoFliker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020 3 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_HDCS2020b 4 */
+/* SENSOR_HDCS2020b 3 */
{hdcs2020b_NoFliker, hdcs2020b_NoFliker,
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+/* SENSOR_HV7131B 4 */
+ {hv7131b_NoFlikerScale, hv7131b_NoFliker,
+ hv7131b_50HZScale, hv7131b_50HZ,
+ hv7131b_60HZScale, hv7131b_60HZ},
+/* SENSOR_HV7131C 5 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_HV7131C 6 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_ICM105A 7 */
+/* SENSOR_ICM105A 6 */
{icm105a_NoFliker, icm105a_NoFlikerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+/* SENSOR_MC501CB 7 */
{MC501CB_NoFliker, MC501CB_NoFlikerScale,
MC501CB_50HZ, MC501CB_50HZScale,
MC501CB_60HZ, MC501CB_60HZScale},
-/* SENSOR_OV7620 9 */
+/* SENSOR_OV7620 8 */
{OV7620_NoFliker, OV7620_NoFliker,
OV7620_50HZ, OV7620_50HZ,
OV7620_60HZ, OV7620_60HZ},
-/* SENSOR_OV7630C 10 */
+/* SENSOR_OV7630C 9 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 11 */
+/* SENSOR_PAS106 10 */
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
+/* SENSOR_PAS202B 11 */
+ {pas202b_NoFlikerScale, pas202b_NoFliker,
+ pas202b_50HZScale, pas202b_50HZ,
+ pas202b_60HZScale, pas202b_60HZ},
/* SENSOR_PB0330 12 */
{pb0330_NoFliker, pb0330_NoFlikerScale,
pb0330_50HZ, pb0330_50HZScale,
@@ -7002,15 +7012,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
5, /* SENSOR_CS2102 0 */
5, /* SENSOR_CS2102K 1 */
4, /* SENSOR_GC0305 2 */
- 4, /* SENSOR_HDCS2020 3 */
- 4, /* SENSOR_HDCS2020b 4 */
- 4, /* SENSOR_HV7131B 5 */
- 4, /* SENSOR_HV7131C 6 */
- 4, /* SENSOR_ICM105A 7 */
- 4, /* SENSOR_MC501CB 8 */
- 3, /* SENSOR_OV7620 9 */
- 4, /* SENSOR_OV7630C 10 */
- 4, /* SENSOR_PAS106 11 */
+ 4, /* SENSOR_HDCS2020b 3 */
+ 4, /* SENSOR_HV7131B 4 */
+ 4, /* SENSOR_HV7131C 5 */
+ 4, /* SENSOR_ICM105A 6 */
+ 4, /* SENSOR_MC501CB 7 */
+ 3, /* SENSOR_OV7620 8 */
+ 4, /* SENSOR_OV7630C 9 */
+ 4, /* SENSOR_PAS106 10 */
+ 4, /* SENSOR_PAS202B 11 */
4, /* SENSOR_PB0330 12 */
4, /* SENSOR_PO2030 13 */
4, /* SENSOR_TAS5130CK 14 */
@@ -7066,8 +7076,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = SENSOR_ICM105A;
break;
case 0x0e:
- PDEBUG(D_PROBE, "Find Sensor HDCS2020");
- sd->sensor = SENSOR_HDCS2020;
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
sd->sharpness = 1;
break;
case 0x0f:
@@ -7153,7 +7163,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->gamma = gamma[(int) sd->sensor];
sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
- sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value;
switch (sd->sensor) {
case SENSOR_GC0305:
@@ -7161,7 +7170,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
case SENSOR_PO2030:
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
break;
- case SENSOR_HDCS2020:
case SENSOR_HV7131B:
case SENSOR_HV7131C:
case SENSOR_OV7630C:
@@ -7191,15 +7199,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
{cs2102_InitialScale, cs2102_Initial}, /* 0 */
{cs2102K_InitialScale, cs2102K_Initial}, /* 1 */
{gc0305_Initial, gc0305_InitialScale}, /* 2 */
- {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 3 */
- {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */
- {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */
- {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
- {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
- {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */
- {OV7620_mode0, OV7620_mode1}, /* 9 */
- {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
- {pas106b_InitialScale, pas106b_Initial}, /* 11 */
+ {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */
+ {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */
+ {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */
+ {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */
+ {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */
+ {OV7620_mode0, OV7620_mode1}, /* 8 */
+ {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */
+ {pas106b_InitialScale, pas106b_Initial}, /* 10 */
+ {pas202b_Initial, pas202b_InitialScale}, /* 11 */
{pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */
/* or {pb03303x_InitialScale, pb03303x_Initial}, */
{PO2030_mode0, PO2030_mode1}, /* 13 */
@@ -7256,6 +7264,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x0008);
reg_w(dev, 0x00, 0x0008);
break;
+ case SENSOR_PAS202B:
case SENSOR_GC0305:
reg_r(gspca_dev, 0x0008);
/* fall thru */
@@ -7269,7 +7278,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_CS2102: /* gamma set in xxx_Initial */
case SENSOR_CS2102K:
- case SENSOR_HDCS2020:
case SENSOR_HDCS2020b:
case SENSOR_PB0330: /* pb with chip_revision - see above */
case SENSOR_OV7630C:
@@ -7282,6 +7290,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
setmatrix(gspca_dev); /* one more time? */
switch (sd->sensor) {
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
reg_r(gspca_dev, 0x0180); /* from win */
reg_w(dev, 0x00, 0x0180);
break;
@@ -7293,37 +7302,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_GC0305:
- case SENSOR_OV7620:
reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
reg_w(dev, 0x15, 0x01ae);
- sd->autogain = 0;
- break;
+ /* fall thru */
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
reg_r(gspca_dev, 0x0180);
break;
- }
-
- setautogain(gspca_dev);
- switch (sd->sensor) {
- case SENSOR_GC0305:
-/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- reg_w(dev, 0x40, 0x0180);
- reg_w(dev, 0x40, 0x0117);
- reg_r(gspca_dev, 0x0180);
- sd->autogain = 1;
- setautogain(gspca_dev);
- break;
case SENSOR_OV7620:
+ reg_w(dev, 0x09, 0x01ad);
+ reg_w(dev, 0x15, 0x01ae);
i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
/*fixme: returned value to send? */
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+ reg_w(dev, 0x40, 0x0117);
reg_r(gspca_dev, 0x0180);
- setautogain(gspca_dev);
- msleep(500);
+ break;
+ }
+
+ setautogain(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x00, 0x0007); /* (from win traces) */
break;
case SENSOR_PO2030:
msleep(500);
@@ -7333,6 +7334,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x02, 0x0008);
break;
}
+ if (sd->sensor == SENSOR_PAS202B)
+ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
return 0;
}
@@ -7530,6 +7533,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0458, 0x700c)},
{USB_DEVICE(0x0458, 0x700f)},
{USB_DEVICE(0x0461, 0x0a00)},
+ {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
{USB_DEVICE(0x046d, 0x08a0)},
{USB_DEVICE(0x046d, 0x08a1)},
{USB_DEVICE(0x046d, 0x08a2)},
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index efe849981ab7..d4658c56eddc 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -385,10 +385,10 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
goto err_out_detach;
}
- /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */
+ /* Phys addr can only be set after attaching (for ir->c.dev) */
snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0",
- ir->c.adapter->dev.bus_id,
- ir->c.dev.bus_id);
+ dev_name(&ir->c.adapter->dev),
+ dev_name(&ir->c.dev));
/* init + register input device */
ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index 4e05f91a9100..2883c8780760 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -877,20 +877,28 @@ static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
static const struct ivtv_card ivtv_card_pg600v2 = {
.type = IVTV_CARD_PG600V2,
.name = "Yuan PG600-2, GotView PCI DVD Lite",
- .comment = "only Composite and S-Video inputs are supported, not the tuner\n",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ /* XC2028 support apparently works for the Yuan, it's still
+ uncertain whether it also works with the GotView. */
.video_inputs = {
- { IVTV_CARD_INPUT_SVIDEO1, 0,
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
- { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
.pci_list = ivtv_pci_pg600v2,
.i2c = &ivtv_i2c_std,
};
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index 48e103be7183..62aa06f5d168 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -73,7 +73,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
@@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
@@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
fmt.fmt.pix.height = itv->params.height;
- itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt);
}
err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
@@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < sizeof(freqs))
- ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]);
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index b69cc1d55e5b..08b762951759 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -60,9 +60,6 @@
#include <media/v4l2-chip-ident.h>
#include "tuner-xc2028.h"
-/* var to keep track of the number of array elements in use */
-int ivtv_cards_active;
-
/* If you have already X v4l cards, then set this to X. This way
the device numbers stay matched. Example: you have a WinTV card
without radio and a PVR-350 with. Normally this would give a
@@ -70,12 +67,6 @@ int ivtv_cards_active;
setting this to 1 you ensure that radio0 is now also radio1. */
int ivtv_first_minor;
-/* Master variable for all ivtv info */
-struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
-
-/* Protects ivtv_cards_active */
-DEFINE_SPINLOCK(ivtv_cards_lock);
-
/* add your revision and whatnot here */
static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
@@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
/* Parameter declarations */
static int cardtype[IVTV_MAX_CARDS];
static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
@@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv)
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
- itv->options.cardtype = cardtype[itv->num];
- itv->options.tuner = tuner[itv->num];
- itv->options.radio = radio[itv->num];
+ itv->options.cardtype = cardtype[itv->instance];
+ itv->options.tuner = tuner[itv->instance];
+ itv->options.radio = radio[itv->instance];
itv->options.newi2c = newi2c;
if (tunertype < -1 || tunertype > 1) {
IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
@@ -688,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
spin_lock_init(&itv->lock);
spin_lock_init(&itv->dma_reg_lock);
- itv->irq_work_queues = create_singlethread_workqueue(itv->name);
+ itv->irq_work_queues = create_singlethread_workqueue(itv->device.name);
if (itv->irq_work_queues == NULL) {
IVTV_ERR("Could not create ivtv workqueue\n");
return -1;
@@ -770,12 +764,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv)
i = 0;
itv->active_input = i;
itv->audio_input = itv->card->video_inputs[i].audio_index;
- if (itv->card->hw_all & IVTV_HW_CX25840)
- itv->video_dec_func = ivtv_cx25840;
- else if (itv->card->hw_all & IVTV_HW_SAA717X)
- itv->video_dec_func = ivtv_saa717x;
- else
- itv->video_dec_func = ivtv_saa7115;
}
static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
@@ -788,21 +776,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
IVTV_DEBUG_INFO("Enabling pci device\n");
if (pci_enable_device(dev)) {
- IVTV_ERR("Can't enable device %d!\n", itv->num);
+ IVTV_ERR("Can't enable device!\n");
return -EIO;
}
if (pci_set_dma_mask(dev, 0xffffffff)) {
- IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+ IVTV_ERR("No suitable DMA available.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
- IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request encoder memory region.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
IVTV_REG_SIZE, "ivtv registers")) {
- IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request register memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
return -EIO;
}
@@ -810,7 +798,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
if (itv->has_cx23415 &&
!request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
IVTV_DECODER_SIZE, "ivtv decoder")) {
- IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request decoder memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
return -EIO;
@@ -853,69 +841,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
return 0;
}
-#ifdef MODULE
-static u32 ivtv_request_module(struct ivtv *itv, u32 hw,
- const char *name, u32 id)
-{
- if ((hw & id) == 0)
- return hw;
- if (request_module(name) != 0) {
- IVTV_ERR("Failed to load module %s\n", name);
- return hw & ~id;
- }
- IVTV_DEBUG_INFO("Loaded module %s\n", name);
- return hw;
-}
-#endif
-
static void ivtv_load_and_init_modules(struct ivtv *itv)
{
u32 hw = itv->card->hw_all;
unsigned i;
-#ifdef MODULE
- /* load modules */
-#ifdef CONFIG_MEDIA_TUNER_MODULE
- hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
-#endif
-#ifdef CONFIG_VIDEO_CX25840_MODULE
- hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840);
-#endif
-#ifdef CONFIG_VIDEO_SAA711X_MODULE
- hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X);
-#endif
-#ifdef CONFIG_VIDEO_SAA7127_MODULE
- hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127);
-#endif
-#ifdef CONFIG_VIDEO_SAA717X_MODULE
- hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X);
-#endif
-#ifdef CONFIG_VIDEO_UPD64031A_MODULE
- hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A);
-#endif
-#ifdef CONFIG_VIDEO_UPD64083_MODULE
- hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X);
-#endif
-#ifdef CONFIG_VIDEO_MSP3400_MODULE
- hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX);
-#endif
-#ifdef CONFIG_VIDEO_VP27SMPX_MODULE
- hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX);
-#endif
-#ifdef CONFIG_VIDEO_WM8775_MODULE
- hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775);
-#endif
-#ifdef CONFIG_VIDEO_WM8739_MODULE
- hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739);
-#endif
-#ifdef CONFIG_VIDEO_CS53L32A_MODULE
- hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A);
-#endif
-#ifdef CONFIG_VIDEO_M52790_MODULE
- hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790);
-#endif
-#endif
-
/* check which i2c devices are actually found */
for (i = 0; i < 32; i++) {
u32 device = 1 << i;
@@ -927,11 +857,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
itv->hw_flags |= device;
continue;
}
- ivtv_i2c_register(itv, i);
- if (ivtv_i2c_hw_addr(itv, device) > 0)
+ if (ivtv_i2c_register(itv, i) == 0)
itv->hw_flags |= device;
}
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+ else
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
hw = itv->hw_flags;
if (itv->card->type == IVTV_CARD_CX23416GYC) {
@@ -949,7 +889,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* The crystal frequency of GVMVPRX is 24.576MHz */
crystal_freq.freq = SAA7115_FREQ_24_576_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_UCGC;
- itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq);
}
if (hw & IVTV_HW_CX25840) {
@@ -967,7 +907,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* determine the exact saa711x model */
itv->hw_flags &= ~IVTV_HW_SAA711X;
- ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
if (v.ident == V4L2_IDENT_SAA7114) {
itv->hw_flags |= IVTV_HW_SAA7114;
/* VBI is not yet supported by the saa7114 driver. */
@@ -1001,28 +941,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
int vbi_buf_size;
struct ivtv *itv;
- spin_lock(&ivtv_cards_lock);
-
- /* Make sure we've got a place for this card */
- if (ivtv_cards_active == IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n",
- ivtv_cards_active);
- spin_unlock(&ivtv_cards_lock);
- return -ENOMEM;
- }
-
itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
- if (itv == NULL) {
- spin_unlock(&ivtv_cards_lock);
+ if (itv == NULL)
return -ENOMEM;
- }
- ivtv_cards[ivtv_cards_active] = itv;
itv->dev = dev;
- itv->num = ivtv_cards_active++;
- snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num);
- IVTV_INFO("Initializing card #%d\n", itv->num);
+ itv->instance = atomic_inc_return(&ivtv_instance) - 1;
- spin_unlock(&ivtv_cards_lock);
+ retval = v4l2_device_register(&dev->dev, &itv->device);
+ if (retval)
+ return retval;
+ /* "ivtv + PCI ID" is a bit of a mouthful, so use
+ "ivtv + instance" instead. */
+ snprintf(itv->device.name, sizeof(itv->device.name),
+ "ivtv%d", itv->instance);
+ IVTV_INFO("Initializing card %d\n", itv->instance);
ivtv_process_options(itv);
if (itv->options.cardtype == -1) {
@@ -1043,8 +975,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
else if (retval == -ENXIO)
goto free_mem;
}
- /* save itv in the pci struct for later use */
- pci_set_drvdata(dev, itv);
/* map io memory */
IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
@@ -1086,7 +1016,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- ivtv_gpio_init(itv);
+ retval = ivtv_gpio_init(itv);
+ if (retval)
+ goto free_io;
/* active i2c */
IVTV_DEBUG_INFO("activating i2c...\n");
@@ -1095,8 +1027,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
-
if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
/* Based on the model number the cardtype may be changed.
The PCI IDs are not always reliable. */
@@ -1191,7 +1121,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
ivtv_reset_tuner_gpio : NULL;
- ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+ ivtv_call_all(itv, tuner, s_type_addr, &setup);
if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE,
@@ -1201,7 +1131,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
.tuner = itv->options.tuner,
.priv = &ctrl,
};
- ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg);
+ ivtv_call_all(itv, tuner, s_config, &cfg);
}
}
@@ -1210,11 +1140,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
itv->tuner_std = itv->std;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std);
+ ivtv_call_all(itv, video, s_std_output, itv->std);
/* Turn off the output signal. The mpeg decoder is not yet
active so without this you would get a green image until the
mpeg decoder becomes active. */
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
}
/* clear interrupt mask, effectively disabling interrupts */
@@ -1222,7 +1152,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
/* Register IRQ */
retval = request_irq(itv->dev->irq, ivtv_irq_handler,
- IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
+ IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv);
if (retval) {
IVTV_ERR("Failed to register irq %d\n", retval);
goto free_i2c;
@@ -1238,7 +1168,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
IVTV_ERR("Error %d registering devices\n", retval);
goto free_streams;
}
- IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name);
+ IVTV_INFO("Initialized card: %s\n", itv->card_name);
return 0;
free_streams:
@@ -1261,10 +1191,8 @@ err:
retval = -ENODEV;
IVTV_ERR("Error %d on initialization\n", retval);
- spin_lock(&ivtv_cards_lock);
- kfree(ivtv_cards[ivtv_cards_active]);
- ivtv_cards[ivtv_cards_active] = NULL;
- spin_unlock(&ivtv_cards_lock);
+ v4l2_device_unregister(&itv->device);
+ kfree(itv);
return retval;
}
@@ -1304,10 +1232,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
if (itv->card->hw_all & IVTV_HW_CX25840) {
struct v4l2_control ctrl;
+ v4l2_subdev_call(itv->sd_video, core, init, 0);
/* CX25840_CID_ENABLE_PVR150_WORKAROUND */
ctrl.id = V4L2_CID_PRIVATE_BASE;
ctrl.value = itv->pvr150_workaround;
- itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+ v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
}
vf.tuner = 0;
@@ -1337,7 +1266,7 @@ int ivtv_init_on_first_open(struct ivtv *itv)
/* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
the mpeg decoder so now the saa7127 receives a proper
signal. */
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
ivtv_init_mpeg_decoder(itv);
}
ivtv_s_std(NULL, &fh, &itv->tuner_std);
@@ -1362,9 +1291,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
static void ivtv_remove(struct pci_dev *pci_dev)
{
- struct ivtv *itv = pci_get_drvdata(pci_dev);
+ struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev);
+ struct ivtv *itv = to_ivtv(dev);
+ int i;
- IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num);
+ IVTV_DEBUG_INFO("Removing card\n");
if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
/* Stop all captures */
@@ -1377,7 +1308,7 @@ static void ivtv_remove(struct pci_dev *pci_dev)
/* Turn off the TV-out */
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
if (atomic_read(&itv->decoding) > 0) {
int type;
@@ -1402,6 +1333,8 @@ static void ivtv_remove(struct pci_dev *pci_dev)
ivtv_streams_cleanup(itv, 1);
ivtv_udma_free(itv);
+ v4l2_device_unregister(&itv->device);
+
exit_ivtv_i2c(itv);
free_irq(itv->dev->irq, (void *)itv);
@@ -1413,8 +1346,11 @@ static void ivtv_remove(struct pci_dev *pci_dev)
release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
pci_disable_device(itv->dev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
- IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+ kfree(itv);
}
/* define a pci_driver for card detection */
@@ -1427,54 +1363,36 @@ static struct pci_driver ivtv_pci_driver = {
static int module_start(void)
{
- printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
-
- memset(ivtv_cards, 0, sizeof(ivtv_cards));
+ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
/* Validate parameters */
if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
IVTV_MAX_CARDS - 1);
return -1;
}
if (ivtv_debug < 0 || ivtv_debug > 2047) {
ivtv_debug = 0;
- printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
}
if (pci_register_driver(&ivtv_pci_driver)) {
- printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
return -ENODEV;
}
- printk(KERN_INFO "ivtv: End initialization\n");
+ printk(KERN_INFO "ivtv: End initialization\n");
return 0;
}
static void module_cleanup(void)
{
- int i, j;
-
pci_unregister_driver(&ivtv_pci_driver);
-
- spin_lock(&ivtv_cards_lock);
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtv_cards[i] == NULL)
- continue;
- for (j = 0; j < IVTV_VBI_FRAMES; j++) {
- kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
- }
- kfree(ivtv_cards[i]);
- }
- spin_unlock(&ivtv_cards_lock);
}
/* Note: These symbols are exported because they are used by the ivtvfb
framebuffer module and an infrared module for the IR-blaster. */
EXPORT_SYMBOL(ivtv_set_irq_mask);
-EXPORT_SYMBOL(ivtv_cards_active);
-EXPORT_SYMBOL(ivtv_cards);
-EXPORT_SYMBOL(ivtv_cards_lock);
EXPORT_SYMBOL(ivtv_api);
EXPORT_SYMBOL(ivtv_vapi);
EXPORT_SYMBOL(ivtv_vapi_result);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 3733b2afec5f..ce8d9b74357e 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -61,6 +61,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/tuner.h>
#include <media/cx2341x.h>
@@ -113,9 +114,6 @@
#define IVTV_REG_VPU (0x9058)
#define IVTV_REG_APU (0xA064)
-/* i2c stuff */
-#define I2C_CLIENTS_MAX 16
-
/* debugging */
extern int ivtv_debug;
@@ -132,12 +130,10 @@ extern int ivtv_debug;
/* Flag to turn on high volume debugging */
#define IVTV_DBGFLG_HIGHVOL (1 << 10)
-/* NOTE: extra space before comma in 'itv->num , ## args' is required for
- gcc-2.95, otherwise it won't compile. */
#define IVTV_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtv_debug) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ v4l2_info(&itv->device, " " type ": " fmt , ##args); \
} while (0)
#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
@@ -152,8 +148,8 @@ extern int ivtv_debug;
#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
do { \
- if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+ v4l2_info(&itv->device, " " type ": " fmt , ##args); \
} while (0)
#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
@@ -167,9 +163,9 @@ extern int ivtv_debug;
#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
/* Standard kernel messages */
-#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args)
+#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args)
+#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args)
/* output modes (cx23415 only) */
#define OUT_NONE 0
@@ -596,8 +592,6 @@ struct ivtv_card;
/* Struct to hold info about ivtv cards */
struct ivtv {
/* General fixed card data */
- int num; /* board number, -1 during init! */
- char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */
struct pci_dev *dev; /* PCI device */
const struct ivtv_card *card; /* card information */
const char *card_name; /* full name of the card */
@@ -609,14 +603,18 @@ struct ivtv {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* hardware description of the board */
v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
- /* controlling video decoder function */
- int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
+ struct v4l2_subdev *sd_audio; /* controlling audio subdev */
+ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
u32 base_addr; /* PCI resource base address */
volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
volatile void __iomem *reg_mem; /* pointer to mapped registers */
struct ivtv_options options; /* user options */
+ struct v4l2_device device;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
/* High-level state info */
unsigned long i_flags; /* global ivtv flags */
@@ -676,7 +674,6 @@ struct ivtv {
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */
int i2c_state; /* i2c bit state */
struct mutex i2c_bus_lock; /* lock i2c bus */
@@ -722,11 +719,13 @@ struct ivtv {
struct osd_info *osd_info; /* ivtvfb private OSD info */
};
+static inline struct ivtv *to_ivtv(struct v4l2_device *dev)
+{
+ return container_of(dev, struct ivtv, device);
+}
+
/* Globals */
-extern struct ivtv *ivtv_cards[];
-extern int ivtv_cards_active;
extern int ivtv_first_minor;
-extern spinlock_t ivtv_cards_lock;
/*==============Prototypes==================*/
@@ -786,4 +785,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
#define write_dec_sync(val, addr) \
do { write_dec(val, addr); read_dec(addr); } while (0)
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). Ignore any errors. */
+#define ivtv_call_hw(itv, hw, o, f, args...) \
+ __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. */
+#define ivtv_call_hw_err(itv, hw, o, f, args...) \
+ __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
#endif
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index 1c404e454a36..5eb587592e9d 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv)
new_stereo_mode = itv->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+ ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
@@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Switch tuner to TV */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
ivtv_audio_set_io(itv);
if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = 0;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
@@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
- ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+ ivtv_call_all(itv, tuner, s_radio);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
- if (itv->hw_flags & IVTV_HW_SAA711X)
- {
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_APLL;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
@@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
int ivtv_v4l2_open(struct inode *inode, struct file *filp)
{
- int res, x, y = 0;
+ int res;
struct ivtv *itv = NULL;
struct ivtv_stream *s = NULL;
- int minor = iminor(inode);
-
- /* Find which card this open was on */
- spin_lock(&ivtv_cards_lock);
- for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
- if (ivtv_cards[x] == NULL)
- continue;
- /* find out which stream this open was on */
- for (y = 0; y < IVTV_MAX_STREAMS; y++) {
- s = &ivtv_cards[x]->streams[y];
- if (s->v4l2dev && s->v4l2dev->minor == minor) {
- itv = ivtv_cards[x];
- break;
- }
- }
- }
- spin_unlock(&ivtv_cards_lock);
+ struct video_device *vdev = video_devdata(filp);
- if (itv == NULL) {
- /* Couldn't find a device registered
- on that minor, shouldn't happen! */
- printk(KERN_WARNING "No ivtv device found on minor %d\n", minor);
- return -ENXIO;
- }
+ s = video_get_drvdata(vdev);
+ itv = s->itv;
mutex_lock(&itv->serialize_lock);
if (ivtv_init_on_first_open(itv)) {
- IVTV_ERR("Failed to initialize on minor %d\n", minor);
+ IVTV_ERR("Failed to initialize on minor %d\n",
+ s->v4l2dev->minor);
mutex_unlock(&itv->serialize_lock);
return -ENXIO;
}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index 74a44844ccaf..dc2850e87a7e 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
return 0;
}
-void ivtv_gpio_init(struct ivtv *itv)
+static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
{
- u16 pin = 0;
-
- if (itv->card->xceive_pin)
- pin = 1 << itv->card->xceive_pin;
-
- if ((itv->card->gpio_init.direction | pin) == 0)
- return;
-
- IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
- read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
-
- /* init output data then direction */
- write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
- write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ return container_of(sd, struct ivtv, sd_gpio);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
@@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = {
.flags = 0,
};
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct v4l2_tuner *tuner = arg;
- struct v4l2_control *ctrl = arg;
- struct v4l2_routing *route = arg;
+ struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- switch (command) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- mask = itv->card->gpio_audio_freq.mask;
- switch (*(u32 *)arg) {
- case 32000:
- data = itv->card->gpio_audio_freq.f32000;
- break;
- case 44100:
- data = itv->card->gpio_audio_freq.f44100;
- break;
- case 48000:
- default:
- data = itv->card->gpio_audio_freq.f48000;
- break;
- }
+ mask = itv->card->gpio_audio_freq.mask;
+ switch (freq) {
+ case 32000:
+ data = itv->card->gpio_audio_freq.f32000;
+ break;
+ case 44100:
+ data = itv->card->gpio_audio_freq.f44100;
+ break;
+ case 48000:
+ default:
+ data = itv->card->gpio_audio_freq.f48000;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- mask = itv->card->gpio_audio_detect.mask;
- if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
- tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
- V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- return 0;
+static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask;
+
+ mask = itv->card->gpio_audio_detect.mask;
+ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+ vt->rxsubchans = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ else
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- mask = itv->card->gpio_audio_mode.mask;
- switch (tuner->audmode) {
- case V4L2_TUNER_MODE_LANG1:
- data = itv->card->gpio_audio_mode.lang1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- data = itv->card->gpio_audio_mode.lang2;
- break;
- case V4L2_TUNER_MODE_MONO:
- data = itv->card->gpio_audio_mode.mono;
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- default:
- data = itv->card->gpio_audio_mode.stereo;
- break;
- }
- break;
+static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case AUDC_SET_RADIO:
- mask = itv->card->gpio_audio_input.mask;
- data = itv->card->gpio_audio_input.radio;
+ mask = itv->card->gpio_audio_mode.mask;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_LANG1:
+ data = itv->card->gpio_audio_mode.lang1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data = itv->card->gpio_audio_mode.lang2;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ data = itv->card->gpio_audio_mode.mono;
break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ default:
+ data = itv->card->gpio_audio_mode.stereo;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_radio(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.radio;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_S_STD:
- mask = itv->card->gpio_audio_input.mask;
+static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.tuner;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ if (route->input > 2)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_input.mask;
+ switch (route->input) {
+ case 0:
data = itv->card->gpio_audio_input.tuner;
break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (route->input > 2)
- return -EINVAL;
- mask = itv->card->gpio_audio_input.mask;
- switch (route->input) {
- case 0:
- data = itv->card->gpio_audio_input.tuner;
- break;
- case 1:
- data = itv->card->gpio_audio_input.linein;
- break;
- case 2:
- default:
- data = itv->card->gpio_audio_input.radio;
- break;
- }
+ case 1:
+ data = itv->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ default:
+ data = itv->card->gpio_audio_input.radio;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
+static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- break;
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = itv->card->gpio_audio_mute.mute;
+ ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
- }
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ if (qc->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ *qc = gpio_ctrl_mute;
+ return 0;
+}
+
+static int subdev_log_status(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
- case VIDIOC_LOG_STATUS:
- IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
- return 0;
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
- return -EINVAL;
- mask = itv->card->gpio_video_input.mask;
- if (route->input == 0)
- data = itv->card->gpio_video_input.tuner;
- else if (route->input == 1)
- data = itv->card->gpio_video_input.composite;
- else
- data = itv->card->gpio_video_input.svideo;
- break;
+static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- default:
+ if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
return -EINVAL;
- }
+ mask = itv->card->gpio_video_input.mask;
+ if (route->input == 0)
+ data = itv->card->gpio_video_input.tuner;
+ else if (route->input == 1)
+ data = itv->card->gpio_video_input.composite;
+ else
+ data = itv->card->gpio_video_input.svideo;
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
return 0;
}
+
+static const struct v4l2_subdev_core_ops subdev_core_ops = {
+ .log_status = subdev_log_status,
+ .g_ctrl = subdev_g_ctrl,
+ .s_ctrl = subdev_s_ctrl,
+ .queryctrl = subdev_queryctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
+ .s_std = subdev_s_std,
+ .s_radio = subdev_s_radio,
+ .g_tuner = subdev_g_tuner,
+ .s_tuner = subdev_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops subdev_audio_ops = {
+ .s_clock_freq = subdev_s_clock_freq,
+ .s_routing = subdev_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops subdev_video_ops = {
+ .s_routing = subdev_s_video_routing,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+ .core = &subdev_core_ops,
+ .tuner = &subdev_tuner_ops,
+ .audio = &subdev_audio_ops,
+ .video = &subdev_video_ops,
+};
+
+int ivtv_gpio_init(struct ivtv *itv)
+{
+ u16 pin = 0;
+
+ if (itv->card->xceive_pin)
+ pin = 1 << itv->card->xceive_pin;
+
+ if ((itv->card->gpio_init.direction | pin) == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+ /* init output data then direction */
+ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
+ snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name);
+ itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio);
+}
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
index 48b6291613a2..0b5d19c8ecb4 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -22,9 +22,8 @@
#define IVTV_GPIO_H
/* GPIO stuff */
-void ivtv_gpio_init(struct ivtv *itv);
+int ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index 41dbbe9621a1..ca1d9557945e 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -90,26 +90,6 @@
#define IVTV_M52790_I2C_ADDR 0x48
/* This array should match the IVTV_HW_ defines */
-static const u8 hw_driverids[] = {
- I2C_DRIVERID_CX25840,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_SAA7127,
- I2C_DRIVERID_MSP3400,
- I2C_DRIVERID_TUNER,
- I2C_DRIVERID_WM8775,
- I2C_DRIVERID_CS53L32A,
- I2C_DRIVERID_TVEEPROM,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_UPD64031A,
- I2C_DRIVERID_UPD64083,
- I2C_DRIVERID_SAA717X,
- I2C_DRIVERID_WM8739,
- I2C_DRIVERID_VP27SMPX,
- I2C_DRIVERID_M52790,
- 0 /* IVTV_HW_GPIO dummy driver ID */
-};
-
-/* This array should match the IVTV_HW_ defines */
static const u8 hw_addrs[] = {
IVTV_CX25840_I2C_ADDR,
IVTV_SAA7115_I2C_ADDR,
@@ -130,6 +110,26 @@ static const u8 hw_addrs[] = {
};
/* This array should match the IVTV_HW_ defines */
+static const char *hw_modules[] = {
+ "cx25840",
+ "saa7115",
+ "saa7127",
+ "msp3400",
+ "tuner",
+ "wm8775",
+ "cs53l32a",
+ NULL,
+ "saa7115",
+ "upd64031a",
+ "upd64083",
+ "saa717x",
+ "wm8739",
+ "vp27smpx",
+ "m52790",
+ NULL
+};
+
+/* This array should match the IVTV_HW_ defines */
static const char * const hw_devicenames[] = {
"cx25840",
"saa7115",
@@ -151,80 +151,58 @@ static const char * const hw_devicenames[] = {
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
- struct i2c_board_info info;
- struct i2c_client *c;
- u8 id;
- int i;
+ struct v4l2_subdev *sd;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ const char *mod = hw_modules[idx];
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
- IVTV_DEBUG_I2C("i2c client register\n");
- if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ if (idx >= ARRAY_SIZE(hw_addrs))
return -1;
- id = hw_driverids[idx];
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
- info.addr = hw_addrs[idx];
- for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
-
- if (i == I2C_CLIENTS_MAX) {
- IVTV_ERR("insufficient room for new I2C client!\n");
- return -ENOMEM;
+ if (hw == IVTV_HW_TUNER) {
+ /* special tuner handling */
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->radio);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->demod);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->tv);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
+ if (!hw_addrs[idx])
+ return -1;
+ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
+ unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END };
- if (id != I2C_DRIVERID_TUNER) {
- if (id == I2C_DRIVERID_UPD64031A ||
- id == I2C_DRIVERID_UPD64083) {
- unsigned short addrs[2] = { info.addr, I2C_CLIENT_END };
-
- c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs);
- } else
- c = i2c_new_device(&itv->i2c_adap, &info);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i] = c;
- return itv->i2c_clients[i] ? 0 : -ENODEV;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs);
+ } else {
+ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
}
-
- /* special tuner handling */
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- return 0;
-}
-
-static int attach_inform(struct i2c_client *client)
-{
- return 0;
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
-static int detach_inform(struct i2c_client *client)
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
{
- int i;
- struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
- IVTV_DEBUG_I2C("i2c client detach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (itv->i2c_clients[i] == client) {
- itv->i2c_clients[i] = NULL;
+ spin_lock(&itv->device.lock);
+ v4l2_device_for_each_subdev(sd, &itv->device) {
+ if (sd->grp_id == hw) {
+ result = sd;
break;
}
}
- IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
- client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
-
- return 0;
+ spin_unlock(&itv->device.lock);
+ return result;
}
/* Set the serial clock line to the desired state */
@@ -494,7 +472,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data,
intervening stop condition */
static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
- struct ivtv *itv = i2c_get_adapdata(i2c_adap);
+ struct v4l2_device *drv = i2c_get_adapdata(i2c_adap);
+ struct ivtv *itv = to_ivtv(drv);
int retval;
int i;
@@ -530,8 +509,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = {
.id = I2C_HW_B_CX2341X,
.algo = &ivtv_algo,
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
};
@@ -583,8 +560,6 @@ static struct i2c_adapter ivtv_i2c_adap_template = {
.id = I2C_HW_B_CX2341X,
.algo = NULL, /* set by i2c-algo-bit */
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
};
@@ -601,160 +576,6 @@ static struct i2c_client ivtv_i2c_client_template = {
.name = "ivtv internal",
};
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
-{
- struct i2c_client *client;
- int retval;
- int i;
-
- IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->i2c_clients[i];
- if (client == NULL || client->driver == NULL ||
- client->driver->command == NULL)
- continue;
- if (addr == client->addr) {
- retval = client->driver->command(client, cmd, arg);
- return retval;
- }
- }
- if (cmd != VIDIOC_G_CHIP_IDENT)
- IVTV_ERR("i2c addr 0x%02x not found for command 0x%x\n", addr, cmd);
- return -ENODEV;
-}
-
-/* Find the i2c device based on the driver ID and return
- its i2c address or -ENODEV if no matching device was found. */
-static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
-{
- struct i2c_client *client;
- int retval = -ENODEV;
- int i;
-
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->i2c_clients[i];
- if (client == NULL || client->driver == NULL)
- continue;
- if (id == client->driver->id) {
- retval = client->addr;
- break;
- }
- }
- return retval;
-}
-
-/* Find the i2c device name matching the DRIVERID */
-static const char *ivtv_i2c_id_name(u32 id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (hw_driverids[i] == id)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device name matching the IVTV_HW_ flag */
-static const char *ivtv_i2c_hw_name(u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device matching the IVTV_HW_ flag and return
- its i2c address or -ENODEV if no matching device was found. */
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return ivtv_i2c_id_addr(itv, hw_driverids[i]);
- return -ENODEV;
-}
-
-/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
- If hw == IVTV_HW_GPIO then call the gpio handler. */
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
-{
- int addr;
-
- if (hw == IVTV_HW_GPIO)
- return ivtv_gpio(itv, cmd, arg);
- if (hw == 0)
- return 0;
-
- addr = ivtv_i2c_hw_addr(itv, hw);
- if (addr < 0) {
- IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n",
- hw, ivtv_i2c_hw_name(hw), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-/* Calls i2c device based on I2C driver ID. */
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
-{
- int addr;
-
- addr = ivtv_i2c_id_addr(itv, id);
- if (addr < 0) {
- if (cmd != VIDIOC_G_CHIP_IDENT)
- IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n",
- id, ivtv_i2c_id_name(id), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
-}
-EXPORT_SYMBOL(ivtv_saa7127);
-
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
-}
-
-/* broadcast cmd for all I2C clients and for the gpio subsystem */
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- if (itv->i2c_adap.algo == NULL) {
- IVTV_ERR("Adapter is not set");
- return;
- }
- i2c_clients_command(&itv->i2c_adap, cmd, arg);
- if (itv->hw_flags & IVTV_HW_GPIO)
- ivtv_gpio(itv, cmd, arg);
-}
-
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv)
{
@@ -763,10 +584,9 @@ int init_ivtv_i2c(struct ivtv *itv)
/* Sanity checks for the I2C hardware arrays. They must be the
* same size and GPIO must be the last entry.
*/
- if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
- IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
- hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
+ ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) ||
+ IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) {
IVTV_ERR("Mismatched I2C hardware arrays\n");
return -ENODEV;
}
@@ -783,8 +603,8 @@ int init_ivtv_i2c(struct ivtv *itv)
itv->i2c_adap.algo_data = &itv->i2c_algo;
sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
- itv->num);
- i2c_set_adapdata(&itv->i2c_adap, itv);
+ itv->instance);
+ i2c_set_adapdata(&itv->i2c_adap, &itv->device);
memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
sizeof(struct i2c_client));
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
index 022978cf533d..396928a06a54 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -21,19 +21,8 @@
#ifndef IVTV_I2C_H
#define IVTV_I2C_H
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
-
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 4bae38d21ef6..cd990a4b81a9 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
return 0;
}
- itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
vbifmt->service_set = ivtv_get_service_set(vbifmt);
return 0;
}
@@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
p->height = h;
if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vid_cap(file, fh, fmt);
}
@@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f
return -EBUSY;
itv->vbi.sliced_in->service_set = 0;
itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
return -EBUSY;
itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
return 0;
}
@@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident
chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
return 0;
}
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
- return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- return -EINVAL;
+ if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ /* TODO: is this correct? */
+ return ivtv_call_all_err(itv, core, g_chip_ident, chip);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
{
struct v4l2_register *regs = arg;
- unsigned long flags;
volatile u8 __iomem *reg_start;
if (!capable(CAP_SYS_ADMIN))
@@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
else
return -EINVAL;
- spin_lock_irqsave(&ivtv_cards_lock, flags);
if (cmd == VIDIOC_DBG_G_REGISTER)
regs->val = readl(regs->reg + reg_start);
else
writel(regs->val, regs->reg + reg_start);
- spin_unlock_irqrestore(&ivtv_cards_lock, flags);
return 0;
}
@@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, g_register, reg);
+ return 0;
}
static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg)
@@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, s_register, reg);
+ return 0;
}
#endif
@@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
streamtype = id->type;
- if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
- printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
- /* Should be replaced */
- /* v4l_printk_ioctl(VIDIOC_S_CROP); */
- }
-
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
@@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
itv->active_output = outp;
route.input = SAA7127_INPUT_TYPE_NORMAL;
route.output = itv->card->video_outputs[outp].video_output;
- ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route);
return 0;
}
@@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *
if (vf->tuner != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, g_frequency, vf);
return 0;
}
@@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
ivtv_mute(itv);
IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, s_frequency, vf);
ivtv_unmute(itv);
return 0;
}
@@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std);
/* Tuner */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
/* set display standard */
itv->std_out = *std;
itv->is_out_60hz = itv->is_60hz;
itv->is_out_50hz = itv->is_50hz;
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+ ivtv_call_all(itv, video, s_std_output, itv->std_out);
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
@@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+ ivtv_call_all(itv, tuner, s_tuner, vt);
return 0;
}
@@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+ ivtv_call_all(itv, tuner, g_tuner, vt);
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
@@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num);
+ IVTV_INFO("================= START STATUS CARD #%d =================\n",
+ itv->instance);
IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
if (itv->hw_flags & IVTV_HW_TVEEPROM) {
struct tveeprom tv;
ivtv_read_eeprom(itv, &tv);
}
- ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+ ivtv_call_all(itv, core, log_status);
ivtv_get_input(itv, itv->active_input, &vidin);
ivtv_get_audio_input(itv, itv->audio_input, &audin);
IVTV_INFO("Video Input: %s\n", vidin.name);
@@ -1518,7 +1512,7 @@ static int ivtv_log_status(struct file *file, void *fh)
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&itv->params, itv->name);
+ cx2341x_log_status(&itv->params, itv->device.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
@@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh)
(s->buffers * s->buf_size) / 1024, s->buffers);
}
- IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted);
- IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num);
+ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+ (long long)itv->mpg_data_received,
+ (long long)itv->vbi_data_inserted);
+ IVTV_INFO("================== END STATUS CARD #%d ==================\n",
+ itv->instance);
return 0;
}
@@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
case VIDIOC_INT_S_AUDIO_ROUTING: {
struct v4l2_routing *route = arg;
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route);
break;
}
@@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
if ((val == 0 && itv->options.newi2c) || (val & 0x01))
ivtv_reset_ir_gpio(itv);
if (val & 0x02)
- itv->video_dec_func(itv, cmd, NULL);
+ v4l2_subdev_call(itv->sd_video, core, reset, 0);
break;
}
diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/video/ivtv/ivtv-routing.c
index 05564919b57f..3fd302294497 100644
--- a/drivers/media/video/ivtv/ivtv-routing.c
+++ b/drivers/media/video/ivtv/ivtv-routing.c
@@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv)
route.output = 0;
if (itv->card->hw_muxer & IVTV_HW_M52790)
route.output = M52790_OUT_STEREO;
- ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route);
route.input = in->audio_input;
route.output = 0;
if (itv->card->hw_audio & IVTV_HW_MSP34XX)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route);
}
/* Selects the video input and output according to the current
@@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input = itv->card->video_inputs[inp].video_input;
route.output = 0;
- itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_video, video, s_routing, &route);
type = itv->card->video_inputs[inp].video_type;
@@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
if (itv->card->hw_video & IVTV_HW_GPIO)
- ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route);
if (itv->card->hw_video & IVTV_HW_UPD64031A) {
if (type == IVTV_CARD_INPUT_VID_TUNER ||
@@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
route.input |= itv->card->gr_config;
- ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route);
}
if (itv->card->hw_video & IVTV_HW_UPD6408X) {
@@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input |= UPD64083_EXT_Y_ADC;
}
}
- ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route);
}
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 9b7aa79eb267..f77d764707b2 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int num_offset = ivtv_stream_info[type].num_offset;
- int num = itv->num + ivtv_first_minor + num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
@@ -205,11 +205,11 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
return -ENOMEM;
}
- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
- itv->num, s->name);
+ snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s",
+ itv->device.name, s->name);
s->v4l2dev->num = num;
- s->v4l2dev->parent = &itv->dev->dev;
+ s->v4l2dev->v4l2_dev = &itv->device;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
s->v4l2dev->tvnorms = V4L2_STD_ALL;
@@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
if (s_mpg->v4l2dev)
num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
}
+ video_set_drvdata(s->v4l2dev, s);
/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, num)) {
@@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv)
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
/* setup VBI registers */
- itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in);
/* determine number of lines and total number of VBI bytes.
A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
@@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
- itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
ivtv_msleep_timeout(300, 1);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
- itv->video_dec_func(itv, VIDIOC_STREAMON, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
}
/* begin_capture */
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
index 4a37a7d2e69d..5c5d1c462fef 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -21,6 +21,7 @@
#include "ivtv-i2c.h"
#include "ivtv-ioctl.h"
#include "ivtv-queue.h"
+#include "ivtv-cards.h"
#include "ivtv-vbi.h"
static void ivtv_set_vps(struct ivtv *itv, int enabled)
@@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled)
data.data[9] = itv->vbi.vps_payload.data[2];
data.data[10] = itv->vbi.vps_payload.data[3];
data.data[11] = itv->vbi.vps_payload.data[4];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
@@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
data.line = (mode & 1) ? 21 : 0;
data.data[0] = cc->odd[0];
data.data[1] = cc->odd[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
data.field = 1;
data.line = (mode & 2) ? 21 : 0;
data.data[0] = cc->even[0];
data.data[1] = cc->even[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
@@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
data.line = enabled ? 23 : 0;
data.data[0] = mode & 0xff;
data.data[1] = (mode >> 8) & 0xff;
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static int odd_parity(u8 c)
@@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8
continue;
}
vbi.p = p + 4;
- itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi);
if (vbi.type && !(lines & (1 << vbi.line))) {
lines |= 1 << vbi.line;
itv->vbi.sliced_data[line].id = vbi.type;
@@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_WSS_625;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
ivtv_set_wss(itv, 1, data.data[0] & 0xf);
vi->wss_missing_cnt = 0;
} else if (vi->wss_missing_cnt == 4) {
@@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_CAPTION_525;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 1;
cc.odd[0] = data.data[0];
cc.odd[1] = data.data[1];
}
data.field = 1;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 2;
cc.even[0] = data.data[0];
cc.even[1] = data.data[1];
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 921e281876f8..36abd2aef6f1 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -48,6 +48,7 @@
#endif
#include "ivtv-driver.h"
+#include "ivtv-cards.h"
#include "ivtv-i2c.h"
#include "ivtv-udma.h"
#include "ivtv-mailbox.h"
@@ -121,15 +122,15 @@ MODULE_LICENSE("GPL");
#define IVTVFB_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtvfb_debug) \
- printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
+ printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \
} while (0)
#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
/* Standard kernel messages */
-#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
+#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)
/* --------------------------------------------------------------------- */
@@ -895,16 +896,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
switch (blank_mode) {
case FB_BLANK_UNBLANK:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_NORMAL:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_VSYNC_SUSPEND:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_POWERDOWN:
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
break;
}
@@ -1188,10 +1189,45 @@ static int ivtvfb_init_card(struct ivtv *itv)
}
+static int __init ivtvfb_callback_init(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (ivtvfb_init_card(itv) == 0) {
+ IVTVFB_INFO("Framebuffer registered on %s\n",
+ itv->device.name);
+ (*(int *)p)++;
+ }
+ }
+ return 0;
+}
+
+static int ivtvfb_callback_cleanup(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+ IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
+ itv->instance);
+ return 0;
+ }
+ IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
+ ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
+ ivtvfb_release_buffers(itv);
+ itv->osd_video_pbase = 0;
+ }
+ return 0;
+}
+
static int __init ivtvfb_init(void)
{
- struct ivtv *itv;
- int i, registered = 0;
+ struct device_driver *drv;
+ int registered = 0;
+ int err;
if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
@@ -1199,20 +1235,11 @@ static int __init ivtvfb_init(void)
return -EINVAL;
}
- /* Locate & initialise all cards supporting an OSD. */
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
- continue;
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (ivtvfb_init_card(itv) == 0) {
- IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
- registered++;
- }
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+ put_driver(drv);
if (!registered) {
- printk(KERN_ERR "ivtvfb: no cards found");
+ printk(KERN_ERR "ivtvfb: no cards found\n");
return -ENODEV;
}
return 0;
@@ -1220,24 +1247,14 @@ static int __init ivtvfb_init(void)
static void ivtvfb_cleanup(void)
{
- struct ivtv *itv;
- int i;
+ struct device_driver *drv;
+ int err;
printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
- for (i = 0; i < ivtv_cards_active; i++) {
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
- if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
- IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
- return;
- }
- IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
- ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
- ivtvfb_release_buffers(itv);
- itv->osd_video_pbase = 0;
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+ put_driver(drv);
}
module_init(ivtvfb_init);
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
index 89a781c6929d..07be14a9fe7b 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/video/m52790.c
@@ -28,7 +28,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/m52790.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -38,89 +38,130 @@ MODULE_LICENSE("GPL");
struct m52790_state {
+ struct v4l2_subdev sd;
u16 input;
u16 output;
};
+static inline struct m52790_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct m52790_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static int m52790_write(struct i2c_client *client)
+static int m52790_write(struct v4l2_subdev *sd)
{
- struct m52790_state *state = i2c_get_clientdata(client);
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
u8 sw1 = (state->input | state->output) & 0xff;
u8 sw2 = (state->input | state->output) >> 8;
return i2c_smbus_write_byte_data(client, sw1, sw2);
}
-static int m52790_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+/* Note: audio and video are linked and cannot be switched separately.
+ So audio and video routing commands are identical for this chip.
+ In theory the video amplifier and audio modes could be handled
+ separately for the output, but that seems to be overkill right now.
+ The same holds for implementing an audio mute control, this is now
+ part of the audio output routing. The normal case is that another
+ chip takes care of the actual muting so making it part of the
+ output routing seems to be the right thing to do for now. */
+static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct m52790_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- /* Note: audio and video are linked and cannot be switched separately.
- So audio and video routing commands are identical for this chip.
- In theory the video amplifier and audio modes could be handled
- separately for the output, but that seems to be overkill right now.
- The same holds for implementing an audio mute control, this is now
- part of the audio output routing. The normal case is that another
- chip takes care of the actual muting so making it part of the
- output routing seems to be the right thing to do for now. */
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input;
- route->output = state->output;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- case VIDIOC_INT_S_VIDEO_ROUTING:
- state->input = route->input;
- state->output = route->output;
- m52790_write(client);
- break;
+ struct m52790_state *state = to_state(sd);
+
+ state->input = route->input;
+ state->output = route->output;
+ m52790_write(sd);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (reg->reg != 0)
- return -EINVAL;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = state->input | state->output;
- else {
- state->input = reg->val & 0x0303;
- state->output = reg->val & ~0x0303;
- m52790_write(client);
- }
- break;
- }
-#endif
+static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_M52790, 0);
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ reg->val = state->input | state->output;
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Switch 1: %02x\n",
- (state->input | state->output) & 0xff);
- v4l_info(client, "Switch 2: %02x\n",
- (state->input | state->output) >> 8);
- break;
+static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- default:
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ state->input = reg->val & 0x0303;
+ state->output = reg->val & ~0x0303;
+ m52790_write(sd);
return 0;
}
+#endif
+
+static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0);
+}
+
+static int m52790_log_status(struct v4l2_subdev *sd)
+{
+ struct m52790_state *state = to_state(sd);
+
+ v4l2_info(sd, "Switch 1: %02x\n",
+ (state->input | state->output) & 0xff);
+ v4l2_info(sd, "Switch 2: %02x\n",
+ (state->input | state->output) >> 8);
+ return 0;
+}
+
+static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops m52790_core_ops = {
+ .log_status = m52790_log_status,
+ .g_chip_ident = m52790_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = m52790_g_register,
+ .s_register = m52790_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops m52790_audio_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_video_ops m52790_video_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_ops m52790_ops = {
+ .core = &m52790_core_ops,
+ .audio = &m52790_audio_ops,
+ .video = &m52790_video_ops,
+};
/* ----------------------------------------------------------------------- */
@@ -130,6 +171,7 @@ static int m52790_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct m52790_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -142,16 +184,20 @@ static int m52790_probe(struct i2c_client *client,
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &m52790_ops);
state->input = M52790_IN_TUNER;
state->output = M52790_OUT_STEREO;
- i2c_set_clientdata(client, state);
- m52790_write(client);
+ m52790_write(sd);
return 0;
}
static int m52790_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index 3da74dcee902..a622dbb72ed8 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -51,14 +51,14 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h>
-#include <media/tvaudio.h>
#include <media/msp3400.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
+#include <media/tvaudio.h>
#include "msp3400-driver.h"
/* ---------------------------------------------------------------------- */
@@ -265,7 +265,7 @@ static char *scart_names[] = {
void msp_set_scart(struct i2c_client *client, int in, int out)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
state->in_scart = in;
@@ -289,7 +289,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
void msp_set_audio(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int bal = 0, bass, treble, loudness;
int val = 0;
int reallymuted = state->muted | state->scan_in_progress;
@@ -336,7 +336,7 @@ void msp_set_audio(struct i2c_client *client)
static void msp_wake_thread(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (NULL == state->kthread)
return;
@@ -390,9 +390,9 @@ static int msp_mode_v4l1_to_v4l2(int mode)
}
#endif
-static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -433,9 +433,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -481,40 +482,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
- struct msp_state *state = i2c_get_clientdata(client);
-
- if (msp_debug >= 2)
- v4l_i2c_print_ioctl(client, cmd);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (cmd) {
- case AUDC_SET_RADIO:
- if (state->radio)
- return 0;
- state->radio = 1;
- v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
- state->watch_stereo = 0;
- switch (state->opmode) {
- case OPMODE_MANUAL:
- /* set msp3400 to FM radio mode */
- msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
- msp3400c_set_carrier(client, MSP_CARRIER(10.7),
- MSP_CARRIER(10.7));
- msp_set_audio(client);
- break;
- case OPMODE_AUTODETECT:
- case OPMODE_AUTOSELECT:
- /* the thread will do for us */
- msp_wake_thread(client);
- break;
- }
- break;
-
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
-#ifdef CONFIG_VIDEO_ALLOW_V4L1
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
@@ -588,105 +565,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
msp_wake_thread(client);
break;
}
-#endif
- case VIDIOC_S_FREQUENCY:
- {
- /* new channel -- kick audio carrier scan */
- msp_wake_thread(client);
- break;
+ default:
+ return -ENOIOCTLCMD;
}
+ return 0;
+}
+#endif
- /* --- v4l2 ioctls --- */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- int update = state->radio || state->v4l2_std != *id;
+/* --- v4l2 ioctls --- */
+static int msp_s_radio(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- state->v4l2_std = *id;
- state->radio = 0;
- if (update)
- msp_wake_thread(client);
+ if (state->radio)
return 0;
+ state->radio = 1;
+ v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
+ state->watch_stereo = 0;
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ /* set msp3400 to FM radio mode */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ msp_set_audio(client);
+ break;
+ case OPMODE_AUTODETECT:
+ case OPMODE_AUTOSELECT:
+ /* the thread will do for us */
+ msp_wake_thread(client);
+ break;
}
+ return 0;
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- *rt = state->routing;
- break;
- }
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
- int tuner = (rt->input >> 3) & 1;
- int sc_in = rt->input & 0x7;
- int sc1_out = rt->output & 0xf;
- int sc2_out = (rt->output >> 4) & 0xf;
- u16 val, reg;
- int i;
- int extern_input = 1;
-
- if (state->routing.input == rt->input &&
- state->routing.output == rt->output)
- break;
- state->routing = *rt;
- /* check if the tuner input is used */
- for (i = 0; i < 5; i++) {
- if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
- extern_input = 0;
- }
- state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
- state->rxsubchans = V4L2_TUNER_SUB_STEREO;
- msp_set_scart(client, sc_in, 0);
- msp_set_scart(client, sc1_out, 1);
- msp_set_scart(client, sc2_out, 2);
- msp_set_audmode(client);
- reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
- val = msp_read_dem(client, reg);
- msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
- /* wake thread when a new input is chosen */
+static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int update = state->radio || state->v4l2_std != id;
+
+ state->v4l2_std = id;
+ state->radio = 0;
+ if (update)
msp_wake_thread(client);
- break;
+ return 0;
+}
+
+static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int tuner = (rt->input >> 3) & 1;
+ int sc_in = rt->input & 0x7;
+ int sc1_out = rt->output & 0xf;
+ int sc2_out = (rt->output >> 4) & 0xf;
+ u16 val, reg;
+ int i;
+ int extern_input = 1;
+
+ if (state->routing.input == rt->input &&
+ state->routing.output == rt->output)
+ return 0;
+ state->routing = *rt;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
}
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ msp_set_scart(client, sc_in, 0);
+ msp_set_scart(client, sc1_out, 1);
+ msp_set_scart(client, sc2_out, 2);
+ msp_set_audmode(client);
+ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+ val = msp_read_dem(client, reg);
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
+static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio)
- break;
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- vt->audmode = state->audmode;
- vt->rxsubchans = state->rxsubchans;
- vt->capability |= V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- break;
- }
+ if (state->radio)
+ return 0;
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ vt->audmode = state->audmode;
+ vt->rxsubchans = state->rxsubchans;
+ vt->capability |= V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio) /* TODO: add mono/stereo support for radio */
- break;
- if (state->audmode == vt->audmode)
- break;
- state->audmode = vt->audmode;
- /* only set audmode */
- msp_set_audmode(client);
- break;
- }
+ if (state->radio) /* TODO: add mono/stereo support for radio */
+ return 0;
+ if (state->audmode == vt->audmode)
+ return 0;
+ state->audmode = vt->audmode;
+ /* only set audmode */
+ msp_set_audmode(client);
+ return 0;
+}
- case VIDIOC_INT_I2S_CLOCK_FREQ:
- {
- u32 *a = (u32 *)arg;
+static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);
+ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
- switch (*a) {
+ switch (freq) {
case 1024000:
state->i2s_mode = 0;
break;
@@ -695,24 +704,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
default:
return -EINVAL;
- }
- break;
}
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct msp_state *state = to_state(sd);
- switch (qc->id) {
+ switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill_std(qc);
default:
break;
- }
- if (!state->has_sound_processing)
- return -EINVAL;
- switch (qc->id) {
+ }
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ switch (qc->id) {
case V4L2_CID_AUDIO_LOUDNESS:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_BASS:
@@ -720,32 +729,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
return v4l2_ctrl_query_fill_std(qc);
default:
return -EINVAL;
- }
}
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return msp_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return msp_set_ctrl(client, arg);
+static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- {
- const char *p;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident,
+ (state->rev1 << 16) | state->rev2);
+}
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
- client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d%s\n",
- state->volume, state->muted ? " (muted)" : "");
- if (state->has_sound_processing) {
- v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass,
- state->treble,
- state->loudness ? "on" : "off");
- }
- switch (state->mode) {
+static int msp_log_status(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const char *p;
+
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
+ client->name, state->rev1, state->rev2);
+ v4l_info(client, "Audio: volume %d%s\n",
+ state->volume, state->muted ? " (muted)" : "");
+ if (state->has_sound_processing) {
+ v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
+ state->balance, state->bass,
+ state->treble,
+ state->loudness ? "on" : "off");
+ }
+ switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break;
@@ -756,36 +771,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
case MSP_MODE_BTSC: p = "BTSC"; break;
case MSP_MODE_EXTERN: p = "External input"; break;
default: p = "unknown"; break;
- }
- if (state->mode == MSP_MODE_EXTERN) {
- v4l_info(client, "Mode: %s\n", p);
- } else if (state->opmode == OPMODE_MANUAL) {
- v4l_info(client, "Mode: %s (%s%s)\n", p,
+ }
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
+ v4l_info(client, "Mode: %s (%s%s)\n", p,
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- } else {
- if (state->opmode == OPMODE_AUTODETECT)
- v4l_info(client, "Mode: %s\n", p);
- v4l_info(client, "Standard: %s (%s%s)\n",
+ } else {
+ if (state->opmode == OPMODE_AUTODETECT)
+ v4l_info(client, "Mode: %s\n", p);
+ v4l_info(client, "Standard: %s (%s%s)\n",
msp_standard_std_name(state->std),
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- }
- v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
- v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
- state->routing.input, state->routing.output);
- v4l_info(client, "ACB: 0x%04x\n", state->acb);
- break;
- }
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident,
- (state->rev1 << 16) | state->rev2);
-
- default:
- /* unknown */
- return -EINVAL;
}
+ v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
+ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
+ state->routing.input, state->routing.output);
+ v4l_info(client, "ACB: 0x%04x\n", state->acb);
return 0;
}
@@ -803,11 +807,49 @@ static int msp_resume(struct i2c_client *client)
return 0;
}
+static int msp_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops msp_core_ops = {
+ .log_status = msp_log_status,
+ .g_chip_ident = msp_g_chip_ident,
+ .g_ctrl = msp_g_ctrl,
+ .s_ctrl = msp_s_ctrl,
+ .queryctrl = msp_queryctrl,
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+ .ioctl = msp_ioctl,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
+ .s_frequency = msp_s_frequency,
+ .g_tuner = msp_g_tuner,
+ .s_tuner = msp_s_tuner,
+ .s_radio = msp_s_radio,
+ .s_std = msp_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops msp_audio_ops = {
+ .s_routing = msp_s_routing,
+ .s_i2s_clock_freq = msp_s_i2s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops msp_ops = {
+ .core = &msp_core_ops,
+ .tuner = &msp_tuner_ops,
+ .audio = &msp_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct msp_state *state;
+ struct v4l2_subdev *sd;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
@@ -827,7 +869,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!state)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &msp_ops);
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
@@ -972,8 +1015,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int msp_remove(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&state->sd);
/* shutdown control thread */
if (state->kthread) {
state->restart = 1;
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h
index ab69a290e5dc..3fe1c1b10f53 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/video/msp3400-driver.h
@@ -5,6 +5,7 @@
#define MSP3400_DRIVER_H
#include <media/msp3400.h>
+#include <media/v4l2-device.h>
/* ---------------------------------------------------------------------- */
@@ -49,6 +50,7 @@ extern int msp_dolby;
extern int msp_stereo_thresh;
struct msp_state {
+ struct v4l2_subdev sd;
int rev1, rev2;
int ident;
u8 has_nicam;
@@ -96,6 +98,11 @@ struct msp_state {
unsigned int watch_stereo:1;
};
+static inline struct msp_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct msp_state, sd);
+}
+
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c
index 846a14a61fd1..a655e9c30146 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/video/msp3400-kthreads.c
@@ -159,7 +159,7 @@ const char *msp_standard_std_name(int std)
static void msp_set_source(struct i2c_client *client, u16 src)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_dolby) {
msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */
@@ -186,7 +186,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2)
void msp3400c_set_mode(struct i2c_client *client, int mode)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode];
int tuner = (state->routing.input >> 3) & 1;
int i;
@@ -227,7 +227,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static char *strmode[] = {
"mono", "stereo", "lang2", "lang1", "lang1+lang2"
};
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
strmode[state->audmode] : "unknown";
int src = 0; /* channel source: FM/AM, nicam or SCART */
@@ -356,7 +356,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static void msp3400c_print_mode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->main == state->second)
v4l_dbg(1, msp_debug, client,
@@ -385,7 +385,7 @@ static void msp3400c_print_mode(struct i2c_client *client)
static int msp3400c_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val;
int rxsubchans = state->rxsubchans;
int newnicam = state->nicam_on;
@@ -463,7 +463,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
/* stereo/multilang monitoring */
static void watch_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_detect_stereo(client))
msp_set_audmode(client);
@@ -475,7 +475,7 @@ static void watch_stereo(struct i2c_client *client)
int msp3400c_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_carrier_detect *cd;
int count, max1, max2, val1, val2, val, i;
@@ -659,7 +659,7 @@ no_second:
int msp3410d_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i, std, count;
v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
@@ -825,7 +825,7 @@ restart:
static int msp34xxg_modus(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->radio) {
v4l_dbg(1, msp_debug, client, "selected radio modus\n");
@@ -852,7 +852,7 @@ static int msp34xxg_modus(struct i2c_client *client)
static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int source, matrix;
switch (state->audmode) {
@@ -895,7 +895,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
static void msp34xxg_set_sources(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
u32 in = state->routing.input;
msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf);
@@ -911,7 +911,7 @@ static void msp34xxg_set_sources(struct i2c_client *client)
/* (re-)initialize the msp34xxg */
static void msp34xxg_reset(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int tuner = (state->routing.input >> 3) & 1;
int modus;
@@ -954,7 +954,7 @@ static void msp34xxg_reset(struct i2c_client *client)
int msp34xxg_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i;
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
@@ -1049,7 +1049,7 @@ unmute:
static int msp34xxg_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int status = msp_read_dem(client, 0x0200);
int is_bilingual = status & 0x100;
int is_stereo = status & 0x40;
@@ -1078,7 +1078,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client)
static void msp34xxg_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->std == 0x20) {
if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
@@ -1095,7 +1095,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
void msp_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
@@ -1110,7 +1110,7 @@ void msp_set_audmode(struct i2c_client *client)
int msp_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 0c524376b67e..1a1a12453672 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -272,21 +272,20 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- unsigned int width_flag = SOCAM_DATAWIDTH_10;
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ /* MT9M001 has all capture_format parameters fixed */
+ unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_MASTER;
if (bus_switch_possible(mt9m001))
- width_flag |= SOCAM_DATAWIDTH_8;
+ flags |= SOCAM_DATAWIDTH_8;
- /* MT9M001 has all capture_format parameters fixed */
- return SOCAM_PCLK_SAMPLE_RISING |
- SOCAM_HSYNC_ACTIVE_HIGH |
- SOCAM_VSYNC_ACTIVE_HIGH |
- SOCAM_MASTER |
- width_flag;
+ return soc_camera_apply_sensor_flags(icl, flags);
}
-static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9m001_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
int ret;
@@ -298,7 +297,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank);
/* The caller provides a supported format, as verified per
- * call to icd->try_fmt_cap() */
+ * call to icd->try_fmt() */
if (!ret)
ret = reg_write(icd, MT9M001_COLUMN_START, rect->left);
if (!ret)
@@ -325,18 +324,20 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd,
return ret;
}
-static int mt9m001_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m001_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height < 32 + icd->y_skip_top)
- f->fmt.pix.height = 32 + icd->y_skip_top;
- if (f->fmt.pix.height > 1024 + icd->y_skip_top)
- f->fmt.pix.height = 1024 + icd->y_skip_top;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 1280)
- f->fmt.pix.width = 1280;
- f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < 32 + icd->y_skip_top)
+ pix->height = 32 + icd->y_skip_top;
+ if (pix->height > 1024 + icd->y_skip_top)
+ pix->height = 1024 + icd->y_skip_top;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 1280)
+ pix->width = 1280;
+ pix->width &= ~0x01; /* has to be even, unsure why was ~3 */
return 0;
}
@@ -447,8 +448,8 @@ static struct soc_camera_ops mt9m001_ops = {
.release = mt9m001_release,
.start_capture = mt9m001_start_capture,
.stop_capture = mt9m001_stop_capture,
- .set_fmt_cap = mt9m001_set_fmt_cap,
- .try_fmt_cap = mt9m001_try_fmt_cap,
+ .set_fmt = mt9m001_set_fmt,
+ .try_fmt = mt9m001_try_fmt,
.set_bus_param = mt9m001_set_bus_param,
.query_bus_param = mt9m001_query_bus_param,
.controls = mt9m001_controls,
@@ -578,6 +579,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
static int mt9m001_video_probe(struct soc_camera_device *icd)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
s32 data;
int ret;
@@ -588,7 +590,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
return -ENODEV;
/* Enable the chip */
- data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1);
+ data = reg_write(icd, MT9M001_CHIP_ENABLE, 1);
dev_dbg(&icd->dev, "write: %d\n", data);
/* Read out the chip version register */
@@ -600,7 +602,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
case 0x8421:
mt9m001->model = V4L2_IDENT_MT9M001C12ST;
icd->formats = mt9m001_colour_formats;
- if (mt9m001->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats);
else
icd->num_formats = 1;
@@ -608,7 +610,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
case 0x8431:
mt9m001->model = V4L2_IDENT_MT9M001C12STM;
icd->formats = mt9m001_monochrome_formats;
- if (mt9m001->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats);
else
icd->num_formats = 1;
@@ -640,8 +642,8 @@ static void mt9m001_video_remove(struct soc_camera_device *icd)
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr,
- mt9m001->icd.dev.parent, mt9m001->icd.vdev);
- soc_camera_video_stop(&mt9m001->icd);
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
}
static int mt9m001_probe(struct i2c_client *client,
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index da0b2d553fd0..c89ea41fe259 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -1,5 +1,5 @@
/*
- * Driver for MT9M111 CMOS Image Sensor from Micron
+ * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
*
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
@@ -19,7 +19,7 @@
#include <media/soc_camera.h>
/*
- * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
* The platform has to define i2c_board_info and call i2c_register_board_info()
*/
@@ -90,7 +90,7 @@
#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
-
+#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
@@ -128,9 +128,14 @@
.colorspace = _colorspace }
#define RGB_FMT(_name, _depth, _fourcc) \
COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB)
+#define JPG_FMT(_name, _depth, _fourcc) \
+ COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_JPEG)
static const struct soc_camera_data_format mt9m111_colour_formats[] = {
- COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG),
+ JPG_FMT("CbYCrY 16 bit", 16, V4L2_PIX_FMT_UYVY),
+ JPG_FMT("CrYCbY 16 bit", 16, V4L2_PIX_FMT_VYUY),
+ JPG_FMT("YCbYCr 16 bit", 16, V4L2_PIX_FMT_YUYV),
+ JPG_FMT("YCrYCb 16 bit", 16, V4L2_PIX_FMT_YVYU),
RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565),
RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555),
RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16),
@@ -145,7 +150,7 @@ enum mt9m111_context {
struct mt9m111 {
struct i2c_client *client;
struct soc_camera_device icd;
- int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */
+ int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
enum mt9m111_context context;
unsigned int left, top, width, height;
u32 pixfmt;
@@ -158,6 +163,7 @@ struct mt9m111 {
unsigned int swap_rgb_red_blue:1;
unsigned int swap_yuv_y_chromas:1;
unsigned int swap_yuv_cb_cr:1;
+ unsigned int autowhitebalance:1;
};
static int reg_page_map_set(struct i2c_client *client, const u16 reg)
@@ -410,9 +416,13 @@ static int mt9m111_stop_capture(struct soc_camera_device *icd)
static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
{
- return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
}
static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
@@ -438,7 +448,24 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
case V4L2_PIX_FMT_RGB565:
ret = mt9m111_setfmt_rgb565(icd);
break;
+ case V4L2_PIX_FMT_UYVY:
+ mt9m111->swap_yuv_y_chromas = 0;
+ mt9m111->swap_yuv_cb_cr = 0;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ mt9m111->swap_yuv_y_chromas = 0;
+ mt9m111->swap_yuv_cb_cr = 1;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
case V4L2_PIX_FMT_YUYV:
+ mt9m111->swap_yuv_y_chromas = 1;
+ mt9m111->swap_yuv_cb_cr = 0;
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ mt9m111->swap_yuv_y_chromas = 1;
+ mt9m111->swap_yuv_cb_cr = 1;
ret = mt9m111_setfmt_yuv(icd);
break;
default:
@@ -452,8 +479,8 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
return ret;
}
-static int mt9m111_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9m111_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
int ret;
@@ -473,13 +500,15 @@ static int mt9m111_set_fmt_cap(struct soc_camera_device *icd,
return ret;
}
-static int mt9m111_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m111_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height > MT9M111_MAX_HEIGHT)
- f->fmt.pix.height = MT9M111_MAX_HEIGHT;
- if (f->fmt.pix.width > MT9M111_MAX_WIDTH)
- f->fmt.pix.width = MT9M111_MAX_WIDTH;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height > MT9M111_MAX_HEIGHT)
+ pix->height = MT9M111_MAX_HEIGHT;
+ if (pix->width > MT9M111_MAX_WIDTH)
+ pix->width = MT9M111_MAX_WIDTH;
return 0;
}
@@ -597,8 +626,8 @@ static struct soc_camera_ops mt9m111_ops = {
.release = mt9m111_release,
.start_capture = mt9m111_start_capture,
.stop_capture = mt9m111_stop_capture,
- .set_fmt_cap = mt9m111_set_fmt_cap,
- .try_fmt_cap = mt9m111_try_fmt_cap,
+ .set_fmt = mt9m111_set_fmt,
+ .try_fmt = mt9m111_try_fmt,
.query_bus_param = mt9m111_query_bus_param,
.set_bus_param = mt9m111_set_bus_param,
.controls = mt9m111_controls,
@@ -634,18 +663,15 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask)
static int mt9m111_get_global_gain(struct soc_camera_device *icd)
{
- unsigned int data, gain;
+ int data;
data = reg_read(GLOBAL_GAIN);
if (data >= 0)
- gain = ((data & (1 << 10)) * 2)
- | ((data & (1 << 9)) * 2)
- | (data & 0x2f);
- else
- gain = data;
-
- return gain;
+ return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
+ (1 << ((data >> 9) & 1));
+ return data;
}
+
static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain)
{
u16 val;
@@ -679,6 +705,23 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on)
return ret;
}
+
+static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ if (on)
+ ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ else
+ ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+
+ if (!ret)
+ mt9m111->autowhitebalance = on;
+
+ return ret;
+}
+
static int mt9m111_get_control(struct soc_camera_device *icd,
struct v4l2_control *ctrl)
{
@@ -715,6 +758,9 @@ static int mt9m111_get_control(struct soc_camera_device *icd,
case V4L2_CID_EXPOSURE_AUTO:
ctrl->value = mt9m111->autoexposure;
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ctrl->value = mt9m111->autowhitebalance;
+ break;
}
return 0;
}
@@ -748,6 +794,9 @@ static int mt9m111_set_control(struct soc_camera_device *icd,
case V4L2_CID_EXPOSURE_AUTO:
ret = mt9m111_set_autoexposure(icd, ctrl->value);
break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = mt9m111_set_autowhitebalance(icd, ctrl->value);
+ break;
default:
ret = -EINVAL;
}
@@ -766,6 +815,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd)
mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
mt9m111_set_global_gain(icd, icd->gain);
mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
+ mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance);
return 0;
}
@@ -798,7 +848,7 @@ static int mt9m111_init(struct soc_camera_device *icd)
if (!ret)
ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
if (ret)
- dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret);
+ dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret);
return ret;
}
@@ -808,7 +858,7 @@ static int mt9m111_release(struct soc_camera_device *icd)
ret = mt9m111_disable(icd);
if (ret < 0)
- dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret);
+ dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret);
return ret;
}
@@ -841,25 +891,30 @@ static int mt9m111_video_probe(struct soc_camera_device *icd)
data = reg_read(CHIP_VERSION);
switch (data) {
- case 0x143a:
+ case 0x143a: /* MT9M111 */
mt9m111->model = V4L2_IDENT_MT9M111;
- icd->formats = mt9m111_colour_formats;
- icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
+ break;
+ case 0x148c: /* MT9M112 */
+ mt9m111->model = V4L2_IDENT_MT9M112;
break;
default:
ret = -ENODEV;
dev_err(&icd->dev,
- "No MT9M111 chip detected, register read %x\n", data);
+ "No MT9M11x chip detected, register read %x\n", data);
goto ei2c;
}
- dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n");
+ icd->formats = mt9m111_colour_formats;
+ icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
+
+ dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data);
ret = soc_camera_video_start(icd);
if (ret)
goto eisis;
mt9m111->autoexposure = 1;
+ mt9m111->autowhitebalance = 1;
mt9m111->swap_rgb_even_odd = 1;
mt9m111->swap_rgb_red_blue = 1;
@@ -889,7 +944,7 @@ static int mt9m111_probe(struct i2c_client *client,
int ret;
if (!icl) {
- dev_err(&client->dev, "MT9M111 driver needs platform data\n");
+ dev_err(&client->dev, "MT9M11x driver needs platform data\n");
return -EINVAL;
}
@@ -968,6 +1023,6 @@ static void __exit mt9m111_mod_exit(void)
module_init(mt9m111_mod_init);
module_exit(mt9m111_mod_exit);
-MODULE_DESCRIPTION("Micron MT9M111 Camera driver");
+MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
new file mode 100644
index 000000000000..1a9d53966d06
--- /dev/null
+++ b/drivers/media/video/mt9t031.c
@@ -0,0 +1,736 @@
+/*
+ * Driver for MT9T031 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+/* mt9t031 i2c address 0x5d
+ * The platform has to define i2c_board_info
+ * and call i2c_register_board_info() */
+
+/* mt9t031 selected register addresses */
+#define MT9T031_CHIP_VERSION 0x00
+#define MT9T031_ROW_START 0x01
+#define MT9T031_COLUMN_START 0x02
+#define MT9T031_WINDOW_HEIGHT 0x03
+#define MT9T031_WINDOW_WIDTH 0x04
+#define MT9T031_HORIZONTAL_BLANKING 0x05
+#define MT9T031_VERTICAL_BLANKING 0x06
+#define MT9T031_OUTPUT_CONTROL 0x07
+#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9T031_SHUTTER_WIDTH 0x09
+#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9T031_FRAME_RESTART 0x0b
+#define MT9T031_SHUTTER_DELAY 0x0c
+#define MT9T031_RESET 0x0d
+#define MT9T031_READ_MODE_1 0x1e
+#define MT9T031_READ_MODE_2 0x20
+#define MT9T031_READ_MODE_3 0x21
+#define MT9T031_ROW_ADDRESS_MODE 0x22
+#define MT9T031_COLUMN_ADDRESS_MODE 0x23
+#define MT9T031_GLOBAL_GAIN 0x35
+#define MT9T031_CHIP_ENABLE 0xF8
+
+#define MT9T031_MAX_HEIGHT 1536
+#define MT9T031_MAX_WIDTH 2048
+#define MT9T031_MIN_HEIGHT 2
+#define MT9T031_MIN_WIDTH 2
+#define MT9T031_HORIZONTAL_BLANK 142
+#define MT9T031_VERTICAL_BLANK 25
+#define MT9T031_COLUMN_SKIP 32
+#define MT9T031_ROW_SKIP 20
+
+#define MT9T031_BUS_PARAM (SOCAM_PCLK_SAMPLE_RISING | \
+ SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | \
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | \
+ SOCAM_MASTER | SOCAM_DATAWIDTH_10)
+
+static const struct soc_camera_data_format mt9t031_colour_formats[] = {
+ {
+ .name = "Bayer (sRGB) 10 bit",
+ .depth = 10,
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }
+};
+
+struct mt9t031 {
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
+ unsigned char autoexposure;
+ u16 xskip;
+ u16 yskip;
+};
+
+static int reg_read(struct soc_camera_device *icd, const u8 reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = mt9t031->client;
+ s32 data = i2c_smbus_read_word_data(client, reg);
+ return data < 0 ? data : swab16(data);
+}
+
+static int reg_write(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ return i2c_smbus_write_word_data(mt9t031->client, reg, swab16(data));
+}
+
+static int reg_set(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(icd, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(icd, reg, ret | data);
+}
+
+static int reg_clear(struct soc_camera_device *icd, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(icd, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(icd, reg, ret & ~data);
+}
+
+static int set_shutter(struct soc_camera_device *icd, const u32 data)
+{
+ int ret;
+
+ ret = reg_write(icd, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
+
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_SHUTTER_WIDTH, data & 0xffff);
+
+ return ret;
+}
+
+static int get_shutter(struct soc_camera_device *icd, u32 *data)
+{
+ int ret;
+
+ ret = reg_read(icd, MT9T031_SHUTTER_WIDTH_UPPER);
+ *data = ret << 16;
+
+ if (ret >= 0)
+ ret = reg_read(icd, MT9T031_SHUTTER_WIDTH);
+ *data |= ret & 0xffff;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_init(struct soc_camera_device *icd)
+{
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ dev_dbg(icd->vdev->parent, "%s\n", __func__);
+
+ ret = reg_write(icd, MT9T031_RESET, 1);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_RESET, 0);
+ if (ret >= 0)
+ ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
+
+ return ret >= 0 ? 0 : -EIO;
+}
+
+static int mt9t031_release(struct soc_camera_device *icd)
+{
+ /* Disable the chip */
+ reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
+ return 0;
+}
+
+static int mt9t031_start_capture(struct soc_camera_device *icd)
+{
+ /* Switch to master "normal" mode */
+ if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9t031_stop_capture(struct soc_camera_device *icd)
+{
+ /* Stop sensor readout */
+ if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9t031_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ /* The caller should have queried our parameters, check anyway */
+ if (flags & ~MT9T031_BUS_PARAM)
+ return -EINVAL;
+
+ if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+ reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+
+ return 0;
+}
+
+static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct soc_camera_link *icl = mt9t031->client->dev.platform_data;
+
+ return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
+}
+
+static int mt9t031_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ int ret;
+ const u16 hblank = MT9T031_HORIZONTAL_BLANK,
+ vblank = MT9T031_VERTICAL_BLANK;
+ u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip,
+ width = rect->width * xskip, height = rect->height * yskip;
+
+ if (pixfmt) {
+ /* S_FMT - use binning and skipping for scaling, recalculate */
+ /* Is this more optimal than just a division? */
+ for (xskip = 8; xskip > 1; xskip--)
+ if (rect->width * xskip <= icd->width_max)
+ break;
+
+ for (yskip = 8; yskip > 1; yskip--)
+ if (rect->height * yskip <= icd->height_max)
+ break;
+
+ width = rect->width * xskip;
+ height = rect->height * yskip;
+
+ dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n",
+ xskip, width, yskip, height);
+ }
+
+ xbin = min(xskip, (u16)3);
+ ybin = min(yskip, (u16)3);
+
+ /* Make sure we don't exceed frame limits */
+ if (rect->left + width > icd->width_max)
+ rect->left = (icd->width_max - width) / 2;
+
+ if (rect->top + height > icd->height_max)
+ rect->top = (icd->height_max - height) / 2;
+
+ /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */
+ switch (xbin) {
+ case 2:
+ rect->left = (rect->left + 1) & ~1;
+ break;
+ case 3:
+ rect->left = roundup(rect->left, 3);
+ }
+
+ switch (ybin) {
+ case 2:
+ rect->top = (rect->top + 1) & ~1;
+ break;
+ case 3:
+ rect->top = roundup(rect->top, 3);
+ }
+
+ /* Blanking and start values - default... */
+ ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank);
+
+ if (pixfmt) {
+ /* Binning, skipping */
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ }
+ dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top);
+
+ /* The caller provides a supported format, as guaranteed by
+ * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_COLUMN_START, rect->left);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_ROW_START, rect->top);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_WINDOW_HEIGHT,
+ height + icd->y_skip_top - 1);
+ if (ret >= 0 && mt9t031->autoexposure) {
+ ret = set_shutter(icd, height + icd->y_skip_top + vblank);
+ if (ret >= 0) {
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
+ const struct v4l2_queryctrl *qctrl =
+ soc_camera_find_qctrl(icd->ops,
+ V4L2_CID_EXPOSURE);
+ icd->exposure = (shutter_max / 2 + (height +
+ icd->y_skip_top + vblank - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
+ shutter_max + qctrl->minimum;
+ }
+ }
+
+ if (!ret && pixfmt) {
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < icd->height_min)
+ pix->height = icd->height_min;
+ if (pix->height > icd->height_max)
+ pix->height = icd->height_max;
+ if (pix->width < icd->width_min)
+ pix->width = icd->width_min;
+ if (pix->width > icd->width_max)
+ pix->width = icd->width_max;
+
+ pix->width &= ~0x01; /* has to be even */
+ pix->height &= ~0x01; /* has to be even */
+
+ return 0;
+}
+
+static int mt9t031_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ id->ident = mt9t031->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t031_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ reg->val = reg_read(icd, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9t031_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match_chip != mt9t031->client->addr)
+ return -ENODEV;
+
+ if (reg_write(icd, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static const struct v4l2_queryctrl mt9t031_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 1,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 255,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }
+};
+
+static int mt9t031_video_probe(struct soc_camera_device *);
+static void mt9t031_video_remove(struct soc_camera_device *);
+static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *);
+static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *);
+
+static struct soc_camera_ops mt9t031_ops = {
+ .owner = THIS_MODULE,
+ .probe = mt9t031_video_probe,
+ .remove = mt9t031_video_remove,
+ .init = mt9t031_init,
+ .release = mt9t031_release,
+ .start_capture = mt9t031_start_capture,
+ .stop_capture = mt9t031_stop_capture,
+ .set_fmt = mt9t031_set_fmt,
+ .try_fmt = mt9t031_try_fmt,
+ .set_bus_param = mt9t031_set_bus_param,
+ .query_bus_param = mt9t031_query_bus_param,
+ .controls = mt9t031_controls,
+ .num_controls = ARRAY_SIZE(mt9t031_controls),
+ .get_control = mt9t031_get_control,
+ .set_control = mt9t031_set_control,
+ .get_chip_id = mt9t031_get_chip_id,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = mt9t031_get_register,
+ .set_register = mt9t031_set_register,
+#endif
+};
+
+static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ data = reg_read(icd, MT9T031_READ_MODE_2);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & 0x8000);
+ break;
+ case V4L2_CID_HFLIP:
+ data = reg_read(icd, MT9T031_READ_MODE_2);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & 0x4000);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ctrl->value = mt9t031->autoexposure;
+ break;
+ }
+ return 0;
+}
+
+static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ const struct v4l2_queryctrl *qctrl;
+ int data;
+
+ qctrl = soc_camera_find_qctrl(&mt9t031_ops, ctrl->id);
+
+ if (!qctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->value)
+ data = reg_set(icd, MT9T031_READ_MODE_2, 0x8000);
+ else
+ data = reg_clear(icd, MT9T031_READ_MODE_2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_HFLIP:
+ if (ctrl->value)
+ data = reg_set(icd, MT9T031_READ_MODE_2, 0x4000);
+ else
+ data = reg_clear(icd, MT9T031_READ_MODE_2, 0x4000);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_GAIN:
+ if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
+ return -EINVAL;
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->value <= qctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = qctrl->default_value - qctrl->minimum;
+ data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&icd->dev, "Setting gain %d\n", data);
+ data = reg_write(icd, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..15 variable step, register values 9..67 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = qctrl->maximum - qctrl->default_value - 1;
+ unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+ 111 + range / 2) / range + 9;
+
+ if (gain <= 32)
+ data = gain;
+ else if (gain <= 64)
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ data = ((gain - 64) * 7 + 28) / 56 + 96;
+
+ dev_dbg(&icd->dev, "Setting gain from %d to %d\n",
+ reg_read(icd, MT9T031_GLOBAL_GAIN), data);
+ data = reg_write(icd, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+
+ /* Success */
+ icd->gain = ctrl->value;
+ break;
+ case V4L2_CID_EXPOSURE:
+ /* mt9t031 has maximum == default */
+ if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
+ return -EINVAL;
+ else {
+ const unsigned long range = qctrl->maximum - qctrl->minimum;
+ const u32 shutter = ((ctrl->value - qctrl->minimum) * 1048 +
+ range / 2) / range + 1;
+ u32 old;
+
+ get_shutter(icd, &old);
+ dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n",
+ old, shutter);
+ if (set_shutter(icd, shutter) < 0)
+ return -EIO;
+ icd->exposure = ctrl->value;
+ mt9t031->autoexposure = 0;
+ }
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->value) {
+ const u16 vblank = MT9T031_VERTICAL_BLANK;
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
+ if (set_shutter(icd, icd->height +
+ icd->y_skip_top + vblank) < 0)
+ return -EIO;
+ qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
+ icd->exposure = (shutter_max / 2 + (icd->height +
+ icd->y_skip_top + vblank - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
+ shutter_max + qctrl->minimum;
+ mt9t031->autoexposure = 1;
+ } else
+ mt9t031->autoexposure = 0;
+ break;
+ }
+ return 0;
+}
+
+/* Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one */
+static int mt9t031_video_probe(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ s32 data;
+ int ret;
+
+ /* We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant. */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /* Enable the chip */
+ data = reg_write(icd, MT9T031_CHIP_ENABLE, 1);
+ dev_dbg(&icd->dev, "write: %d\n", data);
+
+ /* Read out the chip version register */
+ data = reg_read(icd, MT9T031_CHIP_VERSION);
+
+ switch (data) {
+ case 0x1621:
+ mt9t031->model = V4L2_IDENT_MT9T031;
+ icd->formats = mt9t031_colour_formats;
+ icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats);
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err(&icd->dev,
+ "No MT9T031 chip detected, register read %x\n", data);
+ goto ei2c;
+ }
+
+ dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data);
+
+ /* Now that we know the model, we can start video */
+ ret = soc_camera_video_start(icd);
+ if (ret)
+ goto evstart;
+
+ return 0;
+
+evstart:
+ei2c:
+ return ret;
+}
+
+static void mt9t031_video_remove(struct soc_camera_device *icd)
+{
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+
+ dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr,
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
+}
+
+static int mt9t031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t031 *mt9t031;
+ struct soc_camera_device *icd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = client->dev.platform_data;
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
+ if (!mt9t031)
+ return -ENOMEM;
+
+ mt9t031->client = client;
+ i2c_set_clientdata(client, mt9t031);
+
+ /* Second stage probe - when a capture adapter is there */
+ icd = &mt9t031->icd;
+ icd->ops = &mt9t031_ops;
+ icd->control = &client->dev;
+ icd->x_min = MT9T031_COLUMN_SKIP;
+ icd->y_min = MT9T031_ROW_SKIP;
+ icd->x_current = icd->x_min;
+ icd->y_current = icd->y_min;
+ icd->width_min = MT9T031_MIN_WIDTH;
+ icd->width_max = MT9T031_MAX_WIDTH;
+ icd->height_min = MT9T031_MIN_HEIGHT;
+ icd->height_max = MT9T031_MAX_HEIGHT;
+ icd->y_skip_top = 0;
+ icd->iface = icl->bus_id;
+ /* Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width */
+ mt9t031->autoexposure = 1;
+
+ mt9t031->xskip = 1;
+ mt9t031->yskip = 1;
+
+ ret = soc_camera_device_register(icd);
+ if (ret)
+ goto eisdr;
+
+ return 0;
+
+eisdr:
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9t031);
+ return ret;
+}
+
+static int mt9t031_remove(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&mt9t031->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9t031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9t031_id[] = {
+ { "mt9t031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t031_id);
+
+static struct i2c_driver mt9t031_i2c_driver = {
+ .driver = {
+ .name = "mt9t031",
+ },
+ .probe = mt9t031_probe,
+ .remove = mt9t031_remove,
+ .id_table = mt9t031_id,
+};
+
+static int __init mt9t031_mod_init(void)
+{
+ return i2c_add_driver(&mt9t031_i2c_driver);
+}
+
+static void __exit mt9t031_mod_exit(void)
+{
+ i2c_del_driver(&mt9t031_i2c_driver);
+}
+
+module_init(mt9t031_mod_init);
+module_exit(mt9t031_mod_exit);
+
+MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 2584201059d8..14a5f9c21ffa 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -273,6 +273,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
int ret;
u16 pixclk = 0;
@@ -296,6 +297,8 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
}
+ flags = soc_camera_apply_sensor_flags(icl, flags);
+
if (flags & SOCAM_PCLK_SAMPLE_RISING)
pixclk |= 0x10;
@@ -337,14 +340,14 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
width_flag;
}
-static int mt9v022_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int mt9v022_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
int ret;
/* The caller provides a supported format, as verified per call to
- * icd->try_fmt_cap(), datawidth is from our supported format list */
+ * icd->try_fmt(), datawidth is from our supported format list */
switch (pixfmt) {
case V4L2_PIX_FMT_GREY:
case V4L2_PIX_FMT_Y16:
@@ -400,18 +403,20 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd,
return 0;
}
-static int mt9v022_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9v022_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
- if (f->fmt.pix.height < 32 + icd->y_skip_top)
- f->fmt.pix.height = 32 + icd->y_skip_top;
- if (f->fmt.pix.height > 480 + icd->y_skip_top)
- f->fmt.pix.height = 480 + icd->y_skip_top;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 752)
- f->fmt.pix.width = 752;
- f->fmt.pix.width &= ~0x03; /* ? */
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->height < 32 + icd->y_skip_top)
+ pix->height = 32 + icd->y_skip_top;
+ if (pix->height > 480 + icd->y_skip_top)
+ pix->height = 480 + icd->y_skip_top;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 752)
+ pix->width = 752;
+ pix->width &= ~0x03; /* ? */
return 0;
}
@@ -538,8 +543,8 @@ static struct soc_camera_ops mt9v022_ops = {
.release = mt9v022_release,
.start_capture = mt9v022_start_capture,
.stop_capture = mt9v022_stop_capture,
- .set_fmt_cap = mt9v022_set_fmt_cap,
- .try_fmt_cap = mt9v022_try_fmt_cap,
+ .set_fmt = mt9v022_set_fmt,
+ .try_fmt = mt9v022_try_fmt,
.set_bus_param = mt9v022_set_bus_param,
.query_bus_param = mt9v022_query_bus_param,
.controls = mt9v022_controls,
@@ -690,6 +695,7 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
static int mt9v022_video_probe(struct soc_camera_device *icd)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
s32 data;
int ret;
@@ -725,7 +731,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
icd->formats = mt9v022_colour_formats;
- if (mt9v022->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats);
else
icd->num_formats = 1;
@@ -733,7 +739,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11);
mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
icd->formats = mt9v022_monochrome_formats;
- if (mt9v022->client->dev.platform_data)
+ if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats);
else
icd->num_formats = 1;
@@ -760,8 +766,8 @@ static void mt9v022_video_remove(struct soc_camera_device *icd)
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr,
- mt9v022->icd.dev.parent, mt9v022->icd.vdev);
- soc_camera_video_stop(&mt9v022->icd);
+ icd->dev.parent, icd->vdev);
+ soc_camera_video_stop(icd);
}
static int mt9v022_probe(struct i2c_client *client,
diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c
new file mode 100644
index 000000000000..1d54b86c936b
--- /dev/null
+++ b/drivers/media/video/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/video/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(unsigned long base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(unsigned long base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine
+ * might start a new DMA transfer, and we only want to abort
+ * transfers that were started before this routine was called.
+ */
+ free_dmach = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. If the completion
+ * routines attempt to start a new DMA transfer it will fail, so the
+ * DMA controller will be idle after this routine completes.
+ */
+static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels
+ * are idle, so we'll acknowledge the interrupt in the
+ * IRQSTATUS register and exit.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ unsigned long base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or non-zero
+ * if all of the scatter-gather slots are already in use.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) & !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
new file mode 100644
index 000000000000..85c3c7c92af1
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.c
@@ -0,0 +1,1908 @@
+/*
+ * drivers/media/video/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+/*
+ * Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "cam_fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get cam_fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "cam_ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get cam_ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+ CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ
+ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+}
+
+/*
+ * Enable the camera core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/*
+ * Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ while (*size * *cnt > fh->cam->capture_mem)
+ (*cnt)--;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > vb->size) {
+ /*
+ * The image size has been changed to
+ * a larger size since this buffer was
+ * allocated, so we need to free and
+ * reallocate it.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * Oops. We're not supposed to get any errors here.
+ * The only way we could get an error is if we ran out
+ * of scatter-gather DMA slots, but we are supposed to
+ * have at least as many scatter-gather DMA slots as
+ * video buffers so that can't happen.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a camera
+ * interface error, but it doesn't seem to be enough. If we only do
+ * that then subsequent image captures are out of sync by either one
+ * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+ * entire camera subsystem prevents the problem with frame
+ * synchronization.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->version = OMAP24XXCAM_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static unsigned int omap24xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_scheduled_work();
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct file_operations omap24xxcam_fops = {
+ .llseek = no_llseek,
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+ vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY;
+ vfd->fops = &omap24xxcam_fops;
+ vfd->minor = -1;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ vfd->minor = -1;
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device video%d\n", vfd->minor);
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (cam->vfd->minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __init omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * Impose a lower limit on the amount of memory allocated for
+ * capture. We require at least enough memory to double-buffer
+ * QVGA (300KB).
+ */
+ if (capture_mem < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = (mem->end - mem->start) + 1;
+
+ /* map the region */
+ cam->mmio_base = (unsigned long)
+ ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ *
+ * Module initialisation and deinitialisation
+ *
+ */
+
+static int __init omap24xxcam_init(void)
+{
+ return platform_driver_register(&omap24xxcam_driver);
+}
+
+static void __exit omap24xxcam_cleanup(void)
+{
+ platform_driver_unregister(&omap24xxcam_driver);
+}
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+ "buffers (default 4800kiB)");
+
+module_init(omap24xxcam_init);
+module_exit(omap24xxcam_cleanup);
diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h
new file mode 100644
index 000000000000..2ce67f5a48d5
--- /dev/null
+++ b/drivers/media/video/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/video/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
+ * the camera DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+
+/*
+ * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
+ * be queued. (We don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ unsigned long base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ unsigned long mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index 210f1240b331..6ee9b69cc4a9 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -4011,8 +4011,7 @@ ov51x_v4l1_close(struct inode *inode, struct file *file)
/* Do not call this function directly! */
static int
-ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+ov51x_v4l1_ioctl_internal(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_ov511 *ov = video_get_drvdata(vdev);
@@ -4461,7 +4460,7 @@ ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&ov->lock))
return -EINTR;
- rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
+ rc = video_usercopy(file, cmd, arg, ov51x_v4l1_ioctl_internal);
mutex_unlock(&ov->lock);
return rc;
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
new file mode 100644
index 000000000000..54b736fcc07a
--- /dev/null
+++ b/drivers/media/video/ov772x.c
@@ -0,0 +1,1012 @@
+/*
+ * ov772x Camera Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+#include <media/ov772x.h>
+
+/*
+ * register offset
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define BLUE 0x01 /* AWB - Blue channel gain setting */
+#define RED 0x02 /* AWB - Red channel gain setting */
+#define GREEN 0x03 /* AWB - Green channel gain setting */
+#define COM1 0x04 /* Common control 1 */
+#define BAVG 0x05 /* U/B Average Level */
+#define GAVG 0x06 /* Y/Gb Average Level */
+#define RAVG 0x07 /* V/R Average Level */
+#define AECH 0x08 /* Exposure Value - AEC MSBs */
+#define COM2 0x09 /* Common control 2 */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM4 0x0D /* Common control 4 */
+#define COM5 0x0E /* Common control 5 */
+#define COM6 0x0F /* Common control 6 */
+#define AEC 0x10 /* Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define COM7 0x12 /* Common control 7 */
+#define COM8 0x13 /* Common control 8 */
+#define COM9 0x14 /* Common control 9 */
+#define COM10 0x15 /* Common control 10 */
+#define REG16 0x16 /* Register 16 */
+#define HSTART 0x17 /* Horizontal sensor size */
+#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
+#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
+#define VSIZE 0x1A /* Vertical sensor size */
+#define PSHFT 0x1B /* Data format - pixel delay select */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define LAEC 0x1F /* Fine AEC value */
+#define COM11 0x20 /* Common control 11 */
+#define BDBASE 0x22 /* Banding filter Minimum AEC value */
+#define DBSTEP 0x23 /* Banding filter Maximum Setp */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VPT 0x26 /* AGC/AEC Fast mode operating region */
+#define REG28 0x28 /* Register 28 */
+#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
+#define EXHCH 0x2A /* Dummy pixel insert MSB */
+#define EXHCL 0x2B /* Dummy pixel insert LSB */
+#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
+#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
+#define YAVE 0x2F /* Y/G Channel Average value */
+#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
+#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
+#define HREF 0x32 /* Image start and size control */
+#define DM_LNL 0x33 /* Dummy line low 8 bits */
+#define DM_LNH 0x34 /* Dummy line high 8 bits */
+#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
+#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
+#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
+#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
+#define OFF_B 0x39 /* Analog process B channel offset value */
+#define OFF_R 0x3A /* Analog process R channel offset value */
+#define OFF_GB 0x3B /* Analog process Gb channel offset value */
+#define OFF_GR 0x3C /* Analog process Gr channel offset value */
+#define COM12 0x3D /* Common control 12 */
+#define COM13 0x3E /* Common control 13 */
+#define COM14 0x3F /* Common control 14 */
+#define COM15 0x40 /* Common control 15*/
+#define COM16 0x41 /* Common control 16 */
+#define TGT_B 0x42 /* BLC blue channel target value */
+#define TGT_R 0x43 /* BLC red channel target value */
+#define TGT_GB 0x44 /* BLC Gb channel target value */
+#define TGT_GR 0x45 /* BLC Gr channel target value */
+/* for ov7720 */
+#define LCC0 0x46 /* Lens correction control 0 */
+#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
+#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
+#define LCC3 0x49 /* Lens correction option 3 */
+#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
+#define LCC5 0x4B /* Lens correction option 5 */
+#define LCC6 0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR 0x46 /* Lens correction control */
+#define LC_XC 0x47 /* X coordinate of lens correction center relative */
+#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF 0x49 /* Lens correction coefficient */
+#define LC_RADI 0x4A /* Lens correction radius */
+#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
+
+#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define AREF0 0x4E /* Sensor reference control */
+#define AREF1 0x4F /* Sensor reference current control */
+#define AREF2 0x50 /* Analog reference control */
+#define AREF3 0x51 /* ADC reference control */
+#define AREF4 0x52 /* ADC reference control */
+#define AREF5 0x53 /* ADC reference control */
+#define AREF6 0x54 /* Analog reference control */
+#define AREF7 0x55 /* Analog reference control */
+#define UFIX 0x60 /* U channel fixed value output */
+#define VFIX 0x61 /* V channel fixed value output */
+#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0 0x63 /* AWB control byte 0 */
+#define DSP_CTRL1 0x64 /* DSP control byte 1 */
+#define DSP_CTRL2 0x65 /* DSP control byte 2 */
+#define DSP_CTRL3 0x66 /* DSP control byte 3 */
+#define DSP_CTRL4 0x67 /* DSP control byte 4 */
+#define AWB_BIAS 0x68 /* AWB BLC level clip */
+#define AWB_CTRL1 0x69 /* AWB control 1 */
+#define AWB_CTRL2 0x6A /* AWB control 2 */
+#define AWB_CTRL3 0x6B /* AWB control 3 */
+#define AWB_CTRL4 0x6C /* AWB control 4 */
+#define AWB_CTRL5 0x6D /* AWB control 5 */
+#define AWB_CTRL6 0x6E /* AWB control 6 */
+#define AWB_CTRL7 0x6F /* AWB control 7 */
+#define AWB_CTRL8 0x70 /* AWB control 8 */
+#define AWB_CTRL9 0x71 /* AWB control 9 */
+#define AWB_CTRL10 0x72 /* AWB control 10 */
+#define AWB_CTRL11 0x73 /* AWB control 11 */
+#define AWB_CTRL12 0x74 /* AWB control 12 */
+#define AWB_CTRL13 0x75 /* AWB control 13 */
+#define AWB_CTRL14 0x76 /* AWB control 14 */
+#define AWB_CTRL15 0x77 /* AWB control 15 */
+#define AWB_CTRL16 0x78 /* AWB control 16 */
+#define AWB_CTRL17 0x79 /* AWB control 17 */
+#define AWB_CTRL18 0x7A /* AWB control 18 */
+#define AWB_CTRL19 0x7B /* AWB control 19 */
+#define AWB_CTRL20 0x7C /* AWB control 20 */
+#define AWB_CTRL21 0x7D /* AWB control 21 */
+#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
+#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
+#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
+#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
+#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
+#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
+#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
+#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
+#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
+#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
+#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
+#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
+#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
+#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
+#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
+#define SLOP 0x8D /* Gamma curve highest segment slope */
+#define DNSTH 0x8E /* De-noise threshold */
+#define EDGE0 0x8F /* Edge enhancement control 0 */
+#define EDGE1 0x90 /* Edge enhancement control 1 */
+#define DNSOFF 0x91 /* Auto De-noise threshold control */
+#define EDGE2 0x92 /* Edge enhancement strength low point control */
+#define EDGE3 0x93 /* Edge enhancement strength high point control */
+#define MTX1 0x94 /* Matrix coefficient 1 */
+#define MTX2 0x95 /* Matrix coefficient 2 */
+#define MTX3 0x96 /* Matrix coefficient 3 */
+#define MTX4 0x97 /* Matrix coefficient 4 */
+#define MTX5 0x98 /* Matrix coefficient 5 */
+#define MTX6 0x99 /* Matrix coefficient 6 */
+#define MTX_CTRL 0x9A /* Matrix control */
+#define BRIGHT 0x9B /* Brightness control */
+#define CNTRST 0x9C /* Contrast contrast */
+#define CNTRST_CTRL 0x9D /* Contrast contrast center */
+#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
+#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
+#define SCAL0 0xA0 /* Scaling control 0 */
+#define SCAL1 0xA1 /* Scaling control 1 */
+#define SCAL2 0xA2 /* Scaling control 2 */
+#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
+#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
+#define SDE 0xA6 /* Special digital effect control */
+#define USAT 0xA7 /* U component saturation control */
+#define VSAT 0xA8 /* V component saturation control */
+/* for ov7720 */
+#define HUE0 0xA9 /* Hue control 0 */
+#define HUE1 0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS 0xA9 /* Cosine value */
+#define HUESIN 0xAA /* Sine value */
+
+#define SIGN 0xAB /* Sign bit for Hue and contrast */
+#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
+
+/*
+ * register detail
+ */
+
+/* COM2 */
+#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define OCAP_1x 0x00 /* 1x */
+#define OCAP_2x 0x01 /* 2x */
+#define OCAP_3x 0x02 /* 3x */
+#define OCAP_4x 0x03 /* 4x */
+
+/* COM3 */
+#define SWAP_MASK 0x38
+
+#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */
+#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */
+#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+#define SWAP_ML 0x08 /* Swap output MSB/LSB */
+ /* Tri-state option for output clock */
+#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+ /* Tri-state option for output data */
+#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
+
+/* COM4 */
+ /* PLL frequency control */
+#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
+#define PLL_4x 0x40 /* 01: PLL 4x */
+#define PLL_6x 0x80 /* 10: PLL 6x */
+#define PLL_8x 0xc0 /* 11: PLL 8x */
+ /* AEC evaluate window */
+#define AEC_FULL 0x00 /* 00: Full window */
+#define AEC_1p2 0x10 /* 01: 1/2 window */
+#define AEC_1p4 0x20 /* 10: 1/4 window */
+#define AEC_2p3 0x30 /* 11: Low 2/3 window */
+
+/* COM5 */
+#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed slection */
+ /* Auto frame rate max rate control */
+#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
+#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
+#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
+#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
+ /* Auto frame rate active point control */
+#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
+#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
+#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
+#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
+ /* AEC max step control */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+ /* 1 : No limit to AEC increase step */
+
+/* COM7 */
+ /* SCCB Register Reset */
+#define SCCB_RESET 0x80 /* 0 : No change */
+ /* 1 : Resets all registers to default */
+ /* Resolution selection */
+#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
+#define SLCT_VGA 0x00 /* 0 : VGA */
+#define SLCT_QVGA 0x40 /* 1 : QVGA */
+#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+ /* RGB output format control */
+#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+#define FMT_RGB565 0x04 /* 01 : RGB 565 */
+#define FMT_RGB555 0x08 /* 10 : RGB 555 */
+#define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
+#define OFMT_YUV 0x00 /* 00 : YUV */
+#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+#define OFMT_RGB 0x02 /* 10 : RGB */
+#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
+
+/* COM8 */
+#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
+ /* AEC Setp size limit */
+#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
+ /* 1 : Unlimited step size */
+#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
+#define AEC_BND 0x10 /* Enable AEC below banding value */
+#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
+#define AGC_ON 0x04 /* AGC Enable */
+#define AWB_ON 0x02 /* AWB Enable */
+#define AEC_ON 0x01 /* AEC Enable */
+
+/* COM9 */
+#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
+ /* Automatic gain ceiling - maximum AGC value */
+#define GAIN_2x 0x00 /* 000 : 2x */
+#define GAIN_4x 0x10 /* 001 : 4x */
+#define GAIN_8x 0x20 /* 010 : 8x */
+#define GAIN_16x 0x30 /* 011 : 16x */
+#define GAIN_32x 0x40 /* 100 : 32x */
+#define GAIN_64x 0x50 /* 101 : 64x */
+#define GAIN_128x 0x60 /* 110 : 128x */
+#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
+#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
+
+/* COM11 */
+#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
+#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
+
+/* EXHCH */
+#define VSIZE_LSB 0x04 /* Vertical data output size LSB */
+
+/* DSP_CTRL1 */
+#define FIFO_ON 0x80 /* FIFO enable/disable selection */
+#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
+#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
+#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
+#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
+#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
+#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
+#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
+
+/* DSP_CTRL3 */
+#define UV_MASK 0x80 /* UV output sequence option */
+#define UV_ON 0x80 /* ON */
+#define UV_OFF 0x00 /* OFF */
+#define CBAR_MASK 0x20 /* DSP Color bar mask */
+#define CBAR_ON 0x20 /* ON */
+#define CBAR_OFF 0x00 /* OFF */
+
+/* HSTART */
+#define HST_VGA 0x23
+#define HST_QVGA 0x3F
+
+/* HSIZE */
+#define HSZ_VGA 0xA0
+#define HSZ_QVGA 0x50
+
+/* VSTART */
+#define VST_VGA 0x07
+#define VST_QVGA 0x03
+
+/* VSIZE */
+#define VSZ_VGA 0xF0
+#define VSZ_QVGA 0x78
+
+/* HOUTSIZE */
+#define HOSZ_VGA 0xA0
+#define HOSZ_QVGA 0x50
+
+/* VOUTSIZE */
+#define VOSZ_VGA 0xF0
+#define VOSZ_QVGA 0x78
+
+/*
+ * bit configure (32 bit)
+ * this is used in struct ov772x_color_format :: option
+ */
+#define OP_UV 0x00000001
+#define OP_SWAP_RGB 0x00000002
+
+/*
+ * ID
+ */
+#define OV7720 0x7720
+#define OV7725 0x7721
+#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
+
+/*
+ * struct
+ */
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct ov772x_color_format {
+ char *name;
+ __u32 fourcc;
+ const struct regval_list *regs;
+ unsigned int option;
+};
+
+struct ov772x_win_size {
+ char *name;
+ __u32 width;
+ __u32 height;
+ unsigned char com7_bit;
+ const struct regval_list *regs;
+};
+
+struct ov772x_priv {
+ struct ov772x_camera_info *info;
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ const struct ov772x_color_format *fmt;
+ const struct ov772x_win_size *win;
+ int model;
+};
+
+#define ENDMARKER { 0xff, 0xff }
+
+/*
+ * register setting for color format
+ */
+static const struct regval_list ov772x_RGB555_regs[] = {
+ { COM3, 0x00 },
+ { COM7, FMT_RGB555 | OFMT_RGB },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_RGB565_regs[] = {
+ { COM3, 0x00 },
+ { COM7, FMT_RGB565 | OFMT_RGB },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_YYUV_regs[] = {
+ { COM3, SWAP_YUV },
+ { COM7, OFMT_YUV },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_UVYY_regs[] = {
+ { COM3, 0x00 },
+ { COM7, OFMT_YUV },
+ ENDMARKER,
+};
+
+
+/*
+ * register setting for window size
+ */
+static const struct regval_list ov772x_qvga_regs[] = {
+ { HSTART, HST_QVGA },
+ { HSIZE, HSZ_QVGA },
+ { VSTART, VST_QVGA },
+ { VSIZE, VSZ_QVGA },
+ { HOUTSIZE, HOSZ_QVGA },
+ { VOUTSIZE, VOSZ_QVGA },
+ ENDMARKER,
+};
+
+static const struct regval_list ov772x_vga_regs[] = {
+ { HSTART, HST_VGA },
+ { HSIZE, HSZ_VGA },
+ { VSTART, VST_VGA },
+ { VSIZE, VSZ_VGA },
+ { HOUTSIZE, HOSZ_VGA },
+ { VOUTSIZE, VOSZ_VGA },
+ ENDMARKER,
+};
+
+/*
+ * supported format list
+ */
+
+#define SETFOURCC(type) .name = (#type), .fourcc = (V4L2_PIX_FMT_ ## type)
+static const struct soc_camera_data_format ov772x_fmt_lists[] = {
+ {
+ SETFOURCC(YUYV),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(YVYU),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(UYVY),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ SETFOURCC(RGB555),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB555X),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB565),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ SETFOURCC(RGB565X),
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+/*
+ * color format list
+ */
+#define T_YUYV 0
+static const struct ov772x_color_format ov772x_cfmts[] = {
+ [T_YUYV] = {
+ SETFOURCC(YUYV),
+ .regs = ov772x_YYUV_regs,
+ },
+ {
+ SETFOURCC(YVYU),
+ .regs = ov772x_YYUV_regs,
+ .option = OP_UV,
+ },
+ {
+ SETFOURCC(UYVY),
+ .regs = ov772x_UVYY_regs,
+ },
+ {
+ SETFOURCC(RGB555),
+ .regs = ov772x_RGB555_regs,
+ .option = OP_SWAP_RGB,
+ },
+ {
+ SETFOURCC(RGB555X),
+ .regs = ov772x_RGB555_regs,
+ },
+ {
+ SETFOURCC(RGB565),
+ .regs = ov772x_RGB565_regs,
+ .option = OP_SWAP_RGB,
+ },
+ {
+ SETFOURCC(RGB565X),
+ .regs = ov772x_RGB565_regs,
+ },
+};
+
+
+/*
+ * window size list
+ */
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define MAX_WIDTH VGA_WIDTH
+#define MAX_HEIGHT VGA_HEIGHT
+
+static const struct ov772x_win_size ov772x_win_vga = {
+ .name = "VGA",
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .com7_bit = SLCT_VGA,
+ .regs = ov772x_vga_regs,
+};
+
+static const struct ov772x_win_size ov772x_win_qvga = {
+ .name = "QVGA",
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ .com7_bit = SLCT_QVGA,
+ .regs = ov772x_qvga_regs,
+};
+
+
+/*
+ * general function
+ */
+
+static int ov772x_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff) {
+ int ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num,
+ vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int ov772x_mask_set(struct i2c_client *client,
+ u8 command,
+ u8 mask,
+ u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ val &= ~mask;
+ val |= set;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int ov772x_reset(struct i2c_client *client)
+{
+ int ret = i2c_smbus_write_byte_data(client, COM7, SCCB_RESET);
+ msleep(1);
+ return ret;
+}
+
+/*
+ * soc_camera_ops function
+ */
+
+static int ov772x_init(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power) {
+ ret = priv->info->link.power(&priv->client->dev, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->info->link.reset)
+ ret = priv->info->link.reset(&priv->client->dev);
+
+ return ret;
+}
+
+static int ov772x_release(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power)
+ ret = priv->info->link.power(&priv->client->dev, 0);
+
+ return ret;
+}
+
+static int ov772x_start_capture(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret;
+
+ if (!priv->win)
+ priv->win = &ov772x_win_vga;
+ if (!priv->fmt)
+ priv->fmt = &ov772x_cfmts[T_YUYV];
+
+ /*
+ * reset hardware
+ */
+ ov772x_reset(priv->client);
+
+ /*
+ * set color format
+ */
+ ret = ov772x_write_array(priv->client, priv->fmt->regs);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set size format
+ */
+ ret = ov772x_write_array(priv->client, priv->win->regs);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set COM7 bit ( QVGA or VGA )
+ */
+ ret = ov772x_mask_set(priv->client,
+ COM7, SLCT_MASK, priv->win->com7_bit);
+ if (ret < 0)
+ goto start_end;
+
+ /*
+ * set UV setting
+ */
+ if (priv->fmt->option & OP_UV) {
+ ret = ov772x_mask_set(priv->client,
+ DSP_CTRL3, UV_MASK, UV_ON);
+ if (ret < 0)
+ goto start_end;
+ }
+
+ /*
+ * set SWAP setting
+ */
+ if (priv->fmt->option & OP_SWAP_RGB) {
+ ret = ov772x_mask_set(priv->client,
+ COM3, SWAP_MASK, SWAP_RGB);
+ if (ret < 0)
+ goto start_end;
+ }
+
+ dev_dbg(&icd->dev,
+ "format %s, win %s\n", priv->fmt->name, priv->win->name);
+
+start_end:
+ priv->fmt = NULL;
+ priv->win = NULL;
+
+ return ret;
+}
+
+static int ov772x_stop_capture(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ ov772x_reset(priv->client);
+ return 0;
+}
+
+static int ov772x_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct soc_camera_link *icl = priv->client->dev.platform_data;
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ priv->info->buswidth;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int ov772x_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int ov772x_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+}
+#endif
+
+static const struct ov772x_win_size*
+ov772x_select_win(u32 width, u32 height)
+{
+ __u32 diff;
+ const struct ov772x_win_size *win;
+
+ /* default is QVGA */
+ diff = abs(width - ov772x_win_qvga.width) +
+ abs(height - ov772x_win_qvga.height);
+ win = &ov772x_win_qvga;
+
+ /* VGA */
+ if (diff >
+ abs(width - ov772x_win_vga.width) +
+ abs(height - ov772x_win_vga.height))
+ win = &ov772x_win_vga;
+
+ return win;
+}
+
+
+static int ov772x_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt,
+ struct v4l2_rect *rect)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = -EINVAL;
+ int i;
+
+ /*
+ * select format
+ */
+ priv->fmt = NULL;
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (pixfmt == ov772x_cfmts[i].fourcc) {
+ priv->fmt = ov772x_cfmts + i;
+ ret = 0;
+ break;
+ }
+ }
+
+ /*
+ * select win
+ */
+ priv->win = ov772x_select_win(rect->width, rect->height);
+
+ return ret;
+}
+
+static int ov772x_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct ov772x_win_size *win;
+
+ /*
+ * select suitable win
+ */
+ win = ov772x_select_win(pix->width, pix->height);
+
+ pix->width = win->width;
+ pix->height = win->height;
+ pix->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_video_probe(struct soc_camera_device *icd)
+{
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ u8 pid, ver;
+ const char *devname;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /*
+ * ov772x only use 8 or 10 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&icd->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ icd->formats = ov772x_fmt_lists;
+ icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists);
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ pid = i2c_smbus_read_byte_data(priv->client, PID);
+ ver = i2c_smbus_read_byte_data(priv->client, VER);
+
+ switch (VERSION(pid, ver)) {
+ case OV7720:
+ devname = "ov7720";
+ priv->model = V4L2_IDENT_OV7720;
+ break;
+ case OV7725:
+ devname = "ov7725";
+ priv->model = V4L2_IDENT_OV7725;
+ break;
+ default:
+ dev_err(&icd->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ return -ENODEV;
+ }
+
+ dev_info(&icd->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname,
+ pid,
+ ver,
+ i2c_smbus_read_byte_data(priv->client, MIDH),
+ i2c_smbus_read_byte_data(priv->client, MIDL));
+
+
+ return soc_camera_video_start(icd);
+}
+
+static void ov772x_video_remove(struct soc_camera_device *icd)
+{
+ soc_camera_video_stop(icd);
+}
+
+static struct soc_camera_ops ov772x_ops = {
+ .owner = THIS_MODULE,
+ .probe = ov772x_video_probe,
+ .remove = ov772x_video_remove,
+ .init = ov772x_init,
+ .release = ov772x_release,
+ .start_capture = ov772x_start_capture,
+ .stop_capture = ov772x_stop_capture,
+ .set_fmt = ov772x_set_fmt,
+ .try_fmt = ov772x_try_fmt,
+ .set_bus_param = ov772x_set_bus_param,
+ .query_bus_param = ov772x_query_bus_param,
+ .get_chip_id = ov772x_get_chip_id,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = ov772x_get_register,
+ .set_register = ov772x_set_register,
+#endif
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov772x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov772x_priv *priv;
+ struct ov772x_camera_info *info;
+ struct soc_camera_device *icd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ info = client->dev.platform_data;
+ if (!info)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ icd = &priv->icd;
+ icd->ops = &ov772x_ops;
+ icd->control = &client->dev;
+ icd->width_max = MAX_WIDTH;
+ icd->height_max = MAX_HEIGHT;
+ icd->iface = priv->info->link.bus_id;
+
+ ret = soc_camera_device_register(icd);
+
+ if (ret) {
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov772x_remove(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&priv->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov772x_id[] = {
+ { "ov772x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov772x_id);
+
+static struct i2c_driver ov772x_i2c_driver = {
+ .driver = {
+ .name = "ov772x",
+ },
+ .probe = ov772x_probe,
+ .remove = ov772x_remove,
+ .id_table = ov772x_id,
+};
+
+/*
+ * module function
+ */
+
+static int __init ov772x_module_init(void)
+{
+ return i2c_add_driver(&ov772x_i2c_driver);
+}
+
+static void __exit ov772x_module_exit(void)
+{
+ i2c_del_driver(&ov772x_i2c_driver);
+}
+
+module_init(ov772x_module_init);
+module_exit(ov772x_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov772x");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 994807818aa2..45730fac1570 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -10,8 +10,8 @@
* 14478 Potsdam, Germany
*
* Most of this code is directly derived from his userspace driver.
- * His driver works so send any reports to alan@redhat.com unless the
- * userspace driver also doesn't work for you...
+ * His driver works so send any reports to alan@lxorguk.ukuu.org.uk
+ * unless the userspace driver also doesn't work for you...
*
* Changes:
* 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
@@ -680,8 +680,7 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int
* Video4linux interfacing
*/
-static int pms_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
struct pms_device *pd=(struct pms_device *)dev;
@@ -866,7 +865,7 @@ static int pms_do_ioctl(struct inode *inode, struct file *file,
static int pms_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, pms_do_ioctl);
+ return video_usercopy(file, cmd, arg, pms_do_ioctl);
}
static ssize_t pms_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index 19eb274c9cd0..854c2a885358 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -42,7 +42,7 @@ config VIDEO_PVRUSB2_DVB
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
---help---
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 5b81ba469641..4358079f1966 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -2395,7 +2395,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
"usb %s address %d",
- hdw->usb_dev->dev.bus_id,
+ dev_name(&hdw->usb_dev->dev),
hdw->usb_dev->devnum);
ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 733680f21317..e641cd971453 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -628,10 +628,10 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
class_dev->class = &class_ptr->class;
if (pvr2_hdw_get_sn(sfp->channel.hdw)) {
- snprintf(class_dev->bus_id, BUS_ID_SIZE, "sn-%lu",
+ dev_set_name(class_dev, "sn-%lu",
pvr2_hdw_get_sn(sfp->channel.hdw));
} else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) {
- snprintf(class_dev->bus_id, BUS_ID_SIZE, "unit-%c",
+ dev_set_name(class_dev, "unit-%c",
pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a');
} else {
kfree(class_dev);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 97ed95957992..52af1c435965 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -168,8 +168,7 @@ static const char *get_v4l_name(int v4l_type)
* This is part of Video 4 Linux API. The procedure handles ioctl() calls.
*
*/
-static int __pvr2_v4l2_do_ioctl(struct file *file,
- unsigned int cmd, void *arg)
+static int pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_v4l2 *vp = fh->vhead;
@@ -864,7 +863,7 @@ static int __pvr2_v4l2_do_ioctl(struct file *file,
default :
ret = v4l_compat_translate_ioctl(file, cmd,
- arg, __pvr2_v4l2_do_ioctl);
+ arg, pvr2_v4l2_do_ioctl);
}
pvr2_hdw_commit_ctl(hdw);
@@ -890,12 +889,6 @@ static int __pvr2_v4l2_do_ioctl(struct file *file,
return ret;
}
-static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __pvr2_v4l2_do_ioctl(file, cmd, arg);
-}
-
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
{
int num = dip->devbase.num;
@@ -963,7 +956,7 @@ static int pvr2_v4l2_ioctl(struct inode *inode, struct file *file,
#define IVTV_IOC_G_CODEC 0xFFEE7703
#define IVTV_IOC_S_CODEC 0xFFEE7704
if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
- return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl);
+ return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index f3897a3fdb75..1ce9da167b7e 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file,
mutex_lock(&pdev->modlock);
if (!pdev->unplugged)
- r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
+ r = video_usercopy(file, cmd, arg, pwc_video_do_ioctl);
mutex_unlock(&pdev->modlock);
out:
return r;
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 76a1376c9751..d7c147328e35 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -337,8 +337,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
}
-int pwc_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev;
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index 74178754b39b..c046a2535668 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -340,8 +340,7 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power);
extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
/** Functions in pwc-v4l.c */
-extern int pwc_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg);
+extern int pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg);
/** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index eb6be5802928..9d33de22cc48 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -25,7 +25,6 @@
#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <media/v4l2-common.h>
@@ -39,9 +38,106 @@
#include <mach/pxa-regs.h>
#include <mach/camera.h>
+#include "pxa_camera.h"
+
#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
#define PXA_CAM_DRV_NAME "pxa27x-camera"
+/* Camera Interface */
+#define CICR0 0x0000
+#define CICR1 0x0004
+#define CICR2 0x0008
+#define CICR3 0x000C
+#define CICR4 0x0010
+#define CISR 0x0014
+#define CIFR 0x0018
+#define CITOR 0x001C
+#define CIBR0 0x0028
+#define CIBR1 0x0030
+#define CIBR2 0x0038
+
+#define CICR0_DMAEN (1 << 31) /* DMA request enable */
+#define CICR0_PAR_EN (1 << 30) /* Parity enable */
+#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
+#define CICR0_ENB (1 << 28) /* Camera interface enable */
+#define CICR0_DIS (1 << 27) /* Camera interface disable */
+#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
+#define CICR0_TOM (1 << 9) /* Time-out mask */
+#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
+#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
+#define CICR0_EOLM (1 << 6) /* End-of-line mask */
+#define CICR0_PERRM (1 << 5) /* Parity-error mask */
+#define CICR0_QDM (1 << 4) /* Quick-disable mask */
+#define CICR0_CDM (1 << 3) /* Disable-done mask */
+#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
+#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
+#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
+
+#define CICR1_TBIT (1 << 31) /* Transparency bit */
+#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
+#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
+#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
+#define CICR1_RGB_F (1 << 11) /* RGB format */
+#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
+#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
+#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
+#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
+#define CICR1_DW (0x7 << 0) /* Data width mask */
+
+#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
+ wait count mask */
+#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
+ wait count mask */
+#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
+#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR2_FSW (0x7 << 0) /* Frame stabilization
+ wait count mask */
+
+#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
+ wait count mask */
+#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
+ wait count mask */
+#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
+#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
+
+#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
+#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
+#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
+#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
+#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
+#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
+#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
+#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
+
+#define CISR_FTO (1 << 15) /* FIFO time-out */
+#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
+#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
+#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
+#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
+#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
+#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
+#define CISR_EOL (1 << 8) /* End of line */
+#define CISR_PAR_ERR (1 << 7) /* Parity error */
+#define CISR_CQD (1 << 6) /* Camera interface quick disable */
+#define CISR_CDD (1 << 5) /* Camera interface disable done */
+#define CISR_SOF (1 << 4) /* Start of frame */
+#define CISR_EOF (1 << 3) /* End of frame */
+#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
+#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
+#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
+
+#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
+#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
+#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
+#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
+#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
+#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
+#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
+#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
+
#define CICR0_SIM_MP (0 << 24)
#define CICR0_SIM_SP (1 << 24)
#define CICR0_SIM_MS (2 << 24)
@@ -69,8 +165,6 @@
CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \
CICR0_EOFM | CICR0_FOM)
-static DEFINE_MUTEX(camera_lock);
-
/*
* Structures
*/
@@ -120,7 +214,9 @@ struct pxa_camera_dev {
struct pxacamera_platform_data *pdata;
struct resource *res;
unsigned long platform_flags;
- unsigned long platform_mclk_10khz;
+ unsigned long ciclk;
+ unsigned long mclk;
+ u32 mclk_divisor;
struct list_head capture;
@@ -143,8 +239,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
@@ -170,8 +265,7 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int i;
@@ -247,8 +341,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct videobuf_buffer *vb, enum v4l2_field field)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
@@ -367,8 +460,7 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
struct pxa_buffer *active;
@@ -385,7 +477,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
active = pcdev->active;
if (!active) {
- CIFR |= CIFR_RESET_F;
+ unsigned long cifr, cicr0;
+
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
for (i = 0; i < pcdev->channels; i++) {
DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma;
@@ -394,7 +489,9 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
}
pcdev->active = buf;
- CICR0 |= CICR0_ENB;
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
} else {
struct pxa_cam_dma *buf_dma;
struct pxa_cam_dma *act_dma;
@@ -478,6 +575,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
struct videobuf_buffer *vb,
struct pxa_buffer *buf)
{
+ unsigned long cicr0;
+
/* _init is used to debug races, see comment in pxa_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
@@ -490,7 +589,9 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
DCSR(pcdev->dma_chans[0]) = 0;
DCSR(pcdev->dma_chans[1]) = 0;
DCSR(pcdev->dma_chans[2]) = 0;
- CICR0 &= ~CICR0_ENB;
+
+ cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
return;
}
@@ -505,6 +606,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
unsigned long flags;
u32 status, camera_status, overrun;
struct videobuf_buffer *vb;
+ unsigned long cifr, cicr0;
spin_lock_irqsave(&pcdev->lock, flags);
@@ -527,22 +629,26 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
goto out;
}
- camera_status = CISR;
+ camera_status = __raw_readl(pcdev->base + CISR);
overrun = CISR_IFO_0;
if (pcdev->channels == 3)
overrun |= CISR_IFO_1 | CISR_IFO_2;
if (camera_status & overrun) {
dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status);
/* Stop the Capture Interface */
- CICR0 &= ~CICR0_ENB;
+ cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+ __raw_writel(cicr0, pcdev->base + CICR0);
+
/* Stop DMA */
DCSR(channel) = 0;
/* Reset the FIFOs */
- CIFR |= CIFR_RESET_F;
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
/* Enable End-Of-Frame Interrupt */
- CICR0 &= ~CICR0_EOFM;
+ cicr0 &= ~CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
/* Restart the Capture Interface */
- CICR0 |= CICR0_ENB;
+ __raw_writel(cicr0 | CICR0_ENB, pcdev->base + CICR0);
goto out;
}
@@ -598,24 +704,43 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
sizeof(struct pxa_buffer), icd);
}
-static int mclk_get_divisor(struct pxa_camera_dev *pcdev)
+static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
{
- unsigned int mclk_10khz = pcdev->platform_mclk_10khz;
- unsigned long div;
+ unsigned long mclk = pcdev->mclk;
+ u32 div;
unsigned long lcdclk;
- lcdclk = clk_get_rate(pcdev->clk) / 10000;
+ lcdclk = clk_get_rate(pcdev->clk);
+ pcdev->ciclk = lcdclk;
- /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here
- * they get a nice Oops */
- div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1;
+ /* mclk <= ciclk / 4 (27.4.2) */
+ if (mclk > lcdclk / 4) {
+ mclk = lcdclk / 4;
+ dev_warn(pcdev->dev, "Limiting master clock to %lu\n", mclk);
+ }
+
+ /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
+ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, "
- "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div);
+ /* If we're not supplying MCLK, leave it at 0 */
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ pcdev->mclk = lcdclk / (2 * (div + 1));
+
+ dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, "
+ "divisor %u\n", lcdclk, mclk, div);
return div;
}
+static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
+ unsigned long pclk)
+{
+ /* We want a timeout > 1 pixel time, not ">=" */
+ u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1;
+
+ __raw_writel(ciclk_per_pixel, pcdev->base + CITOR);
+}
+
static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
{
struct pxacamera_platform_data *pdata = pcdev->pdata;
@@ -629,7 +754,8 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
pdata->init(pcdev->dev);
}
- CICR0 = 0x3FF; /* disable all interrupts */
+ /* disable all interrupts */
+ __raw_writel(0x3ff, pcdev->base + CICR0);
if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
cicr4 |= CICR4_PCLK_EN;
@@ -642,7 +768,14 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
if (pcdev->platform_flags & PXA_CAMERA_VSP)
cicr4 |= CICR4_VSP;
- CICR4 = mclk_get_divisor(pcdev) | cicr4;
+ __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
+
+ if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+ /* Initialise the timeout under the assumption pclk = mclk */
+ recalculate_fifo_timeout(pcdev, pcdev->mclk);
+ else
+ /* "Safe default" - 13MHz */
+ recalculate_fifo_timeout(pcdev, 13000000);
clk_enable(pcdev->clk);
}
@@ -655,14 +788,15 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
static irqreturn_t pxa_camera_irq(int irq, void *data)
{
struct pxa_camera_dev *pcdev = data;
- unsigned int status = CISR;
+ unsigned long status, cicr0;
- dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status);
+ status = __raw_readl(pcdev->base + CISR);
+ dev_dbg(pcdev->dev, "Camera interrupt status 0x%lx\n", status);
if (!status)
return IRQ_NONE;
- CISR = status;
+ __raw_writel(status, pcdev->base + CISR);
if (status & CISR_EOF) {
int i;
@@ -671,22 +805,24 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
pcdev->active->dmas[i].sg_dma;
DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
}
- CICR0 |= CICR0_EOFM;
+ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
+ __raw_writel(cicr0, pcdev->base + CICR0);
}
return IRQ_HANDLED;
}
-/* The following two functions absolutely depend on the fact, that
- * there can be only one camera on PXA quick capture interface */
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on PXA quick capture interface
+ * Called with .video_lock held
+ */
static int pxa_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int ret;
- mutex_lock(&camera_lock);
-
if (pcdev->icd) {
ret = -EBUSY;
goto ebusy;
@@ -702,11 +838,10 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
pcdev->icd = icd;
ebusy:
- mutex_unlock(&camera_lock);
-
return ret;
}
+/* Called with .video_lock held */
static void pxa_camera_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -718,7 +853,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
icd->devnum);
/* disable capture, disable interrupts */
- CICR0 = 0x3ff;
+ __raw_writel(0x3ff, pcdev->base + CICR0);
/* Stop DMA engine */
DCSR(pcdev->dma_chans[0]) = 0;
@@ -765,6 +900,9 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8))
return -EINVAL;
*flags |= SOCAM_DATAWIDTH_8;
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -772,11 +910,10 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
unsigned long dw, bpp, bus_flags, camera_flags, common_flags;
- u32 cicr0, cicr1, cicr4 = 0;
+ u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0;
int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
if (ret < 0)
@@ -823,12 +960,10 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
* We fix bit-per-pixel equal to data-width... */
switch (common_flags & SOCAM_DATAWIDTH_MASK) {
case SOCAM_DATAWIDTH_10:
- icd->buswidth = 10;
dw = 4;
bpp = 0x40;
break;
case SOCAM_DATAWIDTH_9:
- icd->buswidth = 9;
dw = 3;
bpp = 0x20;
break;
@@ -836,7 +971,6 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
/* Actually it can only be 8 now,
* default is just to silence compiler warnings */
case SOCAM_DATAWIDTH_8:
- icd->buswidth = 8;
dw = 2;
bpp = 0;
}
@@ -852,9 +986,9 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
cicr4 |= CICR4_VSP;
- cicr0 = CICR0;
+ cicr0 = __raw_readl(pcdev->base + CICR0);
if (cicr0 & CICR0_ENB)
- CICR0 = cicr0 & ~CICR0_ENB;
+ __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw;
@@ -862,7 +996,17 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
case V4L2_PIX_FMT_YUV422P:
pcdev->channels = 3;
cicr1 |= CICR1_YCBCR_F;
+ /*
+ * Normally, pxa bus wants as input UYVY format. We allow all
+ * reorderings of the YUV422 format, as no processing is done,
+ * and the YUV stream is just passed through without any
+ * transformation. Note that UYVY is the only format that
+ * should be used if pxa framebuffer Overlay2 is used.
+ */
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
cicr1 |= CICR1_COLOR_SP_VAL(2);
break;
case V4L2_PIX_FMT_RGB555:
@@ -874,27 +1018,32 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
break;
}
- CICR1 = cicr1;
- CICR2 = 0;
- CICR3 = CICR3_LPF_VAL(icd->height - 1) |
+ cicr2 = 0;
+ cicr3 = CICR3_LPF_VAL(icd->height - 1) |
CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
- CICR4 = mclk_get_divisor(pcdev) | cicr4;
+ cicr4 |= pcdev->mclk_divisor;
+
+ __raw_writel(cicr1, pcdev->base + CICR1);
+ __raw_writel(cicr2, pcdev->base + CICR2);
+ __raw_writel(cicr3, pcdev->base + CICR3);
+ __raw_writel(cicr4, pcdev->base + CICR4);
/* CIF interrupts are not used, only DMA */
- CICR0 = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
- CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)) |
- CICR0_DMAEN | CICR0_IRQ_MASK | (cicr0 & CICR0_ENB);
+ cicr0 = (cicr0 & CICR0_ENB) | (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+ CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
+ cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
+ __raw_writel(cicr0, pcdev->base + CICR0);
return 0;
}
-static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
unsigned long bus_flags, camera_flags;
- int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+ int ret = test_platform_param(pcdev, buswidth, &bus_flags);
if (ret < 0)
return ret;
@@ -904,28 +1053,210 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL;
}
-static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static const struct soc_camera_data_format pxa_camera_formats[] = {
+ {
+ .name = "Planar YUV422 16 bit",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+static bool buswidth_supported(struct soc_camera_device *icd, int depth)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+
+ switch (depth) {
+ case 8:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8);
+ case 9:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9);
+ case 10:
+ return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10);
+ }
+ return false;
+}
+
+static int required_buswidth(const struct soc_camera_data_format *fmt)
{
- return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+ switch (fmt->fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ return 8;
+ default:
+ return fmt->depth;
+ }
}
-static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
+ struct soc_camera_format_xlate *xlate)
{
- /* limit to pxa hardware capabilities */
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.height > 2048)
- f->fmt.pix.height = 2048;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > 2048)
- f->fmt.pix.width = 2048;
- f->fmt.pix.width &= ~0x01;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int formats = 0, buswidth, ret;
+
+ buswidth = required_buswidth(icd->formats + idx);
+ if (!buswidth_supported(icd, buswidth))
+ return 0;
+
+ ret = pxa_camera_try_bus_param(icd, buswidth);
+ if (ret < 0)
+ return 0;
+
+ switch (icd->formats[idx].fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &pxa_camera_formats[0];
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = buswidth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s using %s\n",
+ pxa_camera_formats[0].name,
+ icd->formats[idx].name);
+ }
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = buswidth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s packed\n",
+ icd->formats[idx].name);
+ }
+ break;
+ default:
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev,
+ "Providing format %s in pass-through mode\n",
+ icd->formats[idx].name);
+ }
+ }
+
+ return formats;
+}
+
+static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL;
+ const struct soc_camera_format_xlate *xlate;
+ struct soc_camera_sense sense = {
+ .master_clock = pcdev->mclk,
+ .pixel_clock_max = pcdev->ciclk / 4,
+ };
+ int ret, buswidth;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = xlate->buswidth;
+ host_fmt = xlate->host_fmt;
+ cam_fmt = xlate->cam_fmt;
+
+ /* If PCLK is used to latch data from the sensor, check sense */
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ icd->sense = &sense;
+
+ switch (pixfmt) {
+ case 0: /* Only geometry change */
+ ret = icd->ops->set_fmt(icd, pixfmt, rect);
+ break;
+ default:
+ ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect);
+ }
+
+ icd->sense = NULL;
+
+ if (ret < 0) {
+ dev_warn(&ici->dev, "Failed to configure for format %x\n",
+ pixfmt);
+ } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ if (sense.pixel_clock > sense.pixel_clock_max) {
+ dev_err(&ici->dev,
+ "pixel clock %lu set by the camera too high!",
+ sense.pixel_clock);
+ return -EIO;
+ }
+ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+ }
+
+ if (pixfmt && !ret) {
+ icd->buswidth = buswidth;
+ icd->current_fmt = host_fmt;
+ }
+
+ return ret;
+}
+
+static int pxa_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ __u32 pixfmt = pix->pixelformat;
+ enum v4l2_field field;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to pxa hardware capabilities */
+ if (pix->height < 32)
+ pix->height = 32;
+ if (pix->height > 2048)
+ pix->height = 2048;
+ if (pix->width < 48)
+ pix->width = 48;
+ if (pix->width > 2048)
+ pix->width = 2048;
+ pix->width &= ~0x01;
+
+ pix->bytesperline = pix->width *
+ DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ /* camera has to see its format, but the user the original one */
+ pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- return icd->ops->try_fmt_cap(icd, f);
+ ret = icd->ops->try_fmt(icd, f);
+ pix->pixelformat = xlate->host_fmt->fourcc;
+
+ field = pix->field;
+
+ if (field == V4L2_FIELD_ANY) {
+ pix->field = V4L2_FIELD_NONE;
+ } else if (field != V4L2_FIELD_NONE) {
+ dev_err(&icd->dev, "Field type %d unsupported.\n", field);
+ return -EINVAL;
+ }
+
+ return ret;
}
static int pxa_camera_reqbufs(struct soc_camera_file *icf,
@@ -977,16 +1308,15 @@ static int pxa_camera_querycap(struct soc_camera_host *ici,
static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
- pcdev->save_cicr[i++] = CICR0;
- pcdev->save_cicr[i++] = CICR1;
- pcdev->save_cicr[i++] = CICR2;
- pcdev->save_cicr[i++] = CICR3;
- pcdev->save_cicr[i++] = CICR4;
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR1);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR2);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
+ pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
if ((pcdev->icd) && (pcdev->icd->ops->suspend))
ret = pcdev->icd->ops->suspend(pcdev->icd, state);
@@ -996,8 +1326,7 @@ static int pxa_camera_suspend(struct soc_camera_device *icd, pm_message_t state)
static int pxa_camera_resume(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
int i = 0, ret = 0;
@@ -1005,23 +1334,27 @@ static int pxa_camera_resume(struct soc_camera_device *icd)
DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
- CICR0 = pcdev->save_cicr[i++] & ~CICR0_ENB;
- CICR1 = pcdev->save_cicr[i++];
- CICR2 = pcdev->save_cicr[i++];
- CICR3 = pcdev->save_cicr[i++];
- CICR4 = pcdev->save_cicr[i++];
+ __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
+ __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
if ((pcdev->icd) && (pcdev->icd->ops->resume))
ret = pcdev->icd->ops->resume(pcdev->icd);
/* Restart frame capture if active buffer exists */
if (!ret && pcdev->active) {
+ unsigned long cifr, cicr0;
+
/* Reset the FIFOs */
- CIFR |= CIFR_RESET_F;
- /* Enable End-Of-Frame Interrupt */
- CICR0 &= ~CICR0_EOFM;
- /* Restart the Capture Interface */
- CICR0 |= CICR0_ENB;
+ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+ __raw_writel(cifr, pcdev->base + CIFR);
+
+ cicr0 = __raw_readl(pcdev->base + CICR0);
+ cicr0 &= ~CICR0_EOFM; /* Enable End-Of-Frame Interrupt */
+ cicr0 |= CICR0_ENB; /* Restart the Capture Interface */
+ __raw_writel(cicr0, pcdev->base + CICR0);
}
return ret;
@@ -1033,13 +1366,13 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.remove = pxa_camera_remove_device,
.suspend = pxa_camera_suspend,
.resume = pxa_camera_resume,
- .set_fmt_cap = pxa_camera_set_fmt_cap,
- .try_fmt_cap = pxa_camera_try_fmt_cap,
+ .get_formats = pxa_camera_get_formats,
+ .set_fmt = pxa_camera_set_fmt,
+ .try_fmt = pxa_camera_try_fmt,
.init_videobuf = pxa_camera_init_videobuf,
.reqbufs = pxa_camera_reqbufs,
.poll = pxa_camera_poll,
.querycap = pxa_camera_querycap,
- .try_bus_param = pxa_camera_try_bus_param,
.set_bus_param = pxa_camera_set_bus_param,
};
@@ -1071,7 +1404,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
goto exit;
}
- pcdev->clk = clk_get(&pdev->dev, "CAMCLK");
+ pcdev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(pcdev->clk)) {
err = PTR_ERR(pcdev->clk);
goto exit_kfree;
@@ -1090,14 +1423,17 @@ static int pxa_camera_probe(struct platform_device *pdev)
"data widths, using default 10 bit\n");
pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
}
- pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz;
- if (!pcdev->platform_mclk_10khz) {
+ pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+ if (!pcdev->mclk) {
dev_warn(&pdev->dev,
- "mclk_10khz == 0! Please, fix your platform data. "
+ "mclk == 0! Please, fix your platform data. "
"Using default 20MHz\n");
- pcdev->platform_mclk_10khz = 2000;
+ pcdev->mclk = 20000000;
}
+ pcdev->dev = &pdev->dev;
+ pcdev->mclk_divisor = mclk_get_divisor(pcdev);
+
INIT_LIST_HEAD(&pcdev->capture);
spin_lock_init(&pcdev->lock);
@@ -1117,7 +1453,6 @@ static int pxa_camera_probe(struct platform_device *pdev)
}
pcdev->irq = irq;
pcdev->base = base;
- pcdev->dev = &pdev->dev;
/* request dma */
err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
diff --git a/drivers/media/video/pxa_camera.h b/drivers/media/video/pxa_camera.h
new file mode 100644
index 000000000000..89cbfc9a35c5
--- /dev/null
+++ b/drivers/media/video/pxa_camera.h
@@ -0,0 +1,95 @@
+/* Camera Interface */
+#define CICR0 __REG(0x50000000)
+#define CICR1 __REG(0x50000004)
+#define CICR2 __REG(0x50000008)
+#define CICR3 __REG(0x5000000C)
+#define CICR4 __REG(0x50000010)
+#define CISR __REG(0x50000014)
+#define CIFR __REG(0x50000018)
+#define CITOR __REG(0x5000001C)
+#define CIBR0 __REG(0x50000028)
+#define CIBR1 __REG(0x50000030)
+#define CIBR2 __REG(0x50000038)
+
+#define CICR0_DMAEN (1 << 31) /* DMA request enable */
+#define CICR0_PAR_EN (1 << 30) /* Parity enable */
+#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */
+#define CICR0_ENB (1 << 28) /* Camera interface enable */
+#define CICR0_DIS (1 << 27) /* Camera interface disable */
+#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */
+#define CICR0_TOM (1 << 9) /* Time-out mask */
+#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */
+#define CICR0_FEM (1 << 7) /* FIFO-empty mask */
+#define CICR0_EOLM (1 << 6) /* End-of-line mask */
+#define CICR0_PERRM (1 << 5) /* Parity-error mask */
+#define CICR0_QDM (1 << 4) /* Quick-disable mask */
+#define CICR0_CDM (1 << 3) /* Disable-done mask */
+#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */
+#define CICR0_EOFM (1 << 1) /* End-of-frame mask */
+#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */
+
+#define CICR1_TBIT (1 << 31) /* Transparency bit */
+#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */
+#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */
+#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */
+#define CICR1_RGB_F (1 << 11) /* RGB format */
+#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */
+#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */
+#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */
+#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */
+#define CICR1_DW (0x7 << 0) /* Data width mask */
+
+#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock
+ wait count mask */
+#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock
+ wait count mask */
+#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */
+#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR2_FSW (0x7 << 0) /* Frame stabilization
+ wait count mask */
+
+#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock
+ wait count mask */
+#define CICR3_EFW (0xff << 16) /* End-of-frame line clock
+ wait count mask */
+#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */
+#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock
+ wait count mask */
+#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */
+
+#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */
+#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */
+#define CICR4_PCP (1 << 22) /* Pixel clock polarity */
+#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */
+#define CICR4_VSP (1 << 20) /* Vertical sync polarity */
+#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */
+#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */
+#define CICR4_DIV (0xff << 0) /* Clock divisor mask */
+
+#define CISR_FTO (1 << 15) /* FIFO time-out */
+#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */
+#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */
+#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */
+#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */
+#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */
+#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */
+#define CISR_EOL (1 << 8) /* End of line */
+#define CISR_PAR_ERR (1 << 7) /* Parity error */
+#define CISR_CQD (1 << 6) /* Camera interface quick disable */
+#define CISR_CDD (1 << 5) /* Camera interface disable done */
+#define CISR_SOF (1 << 4) /* Start of frame */
+#define CISR_EOF (1 << 3) /* End of frame */
+#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */
+#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */
+#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */
+
+#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */
+#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */
+#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */
+#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */
+#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */
+#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */
+#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */
+#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */
+
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index 4a21b8a6a709..f159441e9375 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -15,7 +15,7 @@
* <richard.guenther@student.uni-tuebingen.de>
*
* with changes by
- * Alan Cox <Alan.Cox@linux.org>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* and
*
@@ -804,8 +804,7 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t,
*
* Returns 0 if successful
*/
-static int do_saa5246a_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct saa5246a_device *t = video_drvdata(file);
@@ -953,7 +952,7 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file,
cmd = vtx_fix_command(cmd);
mutex_lock(&t->lock);
- err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl);
+ err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl);
mutex_unlock(&t->lock);
return err;
}
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index 3bb959c25d9d..6ef3affb97f1 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -8,7 +8,7 @@
* you can add arbitary multiple teletext devices to Linux video4linux
* now (well 32 anyway).
*
- * Alan Cox <Alan.Cox@linux.org>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* The original driver was heavily modified to match the i2c interface
* It was truncated to use the WinTV boards, too.
@@ -190,8 +190,7 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
* Standard character-device-driver functions
*/
-static int do_saa5249_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg)
{
static int virtual_mode = false;
struct saa5249_device *t = video_drvdata(file);
@@ -488,7 +487,7 @@ static int saa5249_ioctl(struct inode *inode, struct file *file,
cmd = vtx_fix_command(cmd);
mutex_lock(&t->lock);
- err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl);
+ err = video_usercopy(file, cmd, arg, do_saa5249_ioctl);
mutex_unlock(&t->lock);
return err;
}
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index c8e9cb3db30a..22708ecdf1bb 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -44,7 +44,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include <media/saa7115.h>
@@ -70,6 +70,7 @@ static unsigned short normal_i2c[] = {
I2C_CLIENT_INSMOD;
struct saa711x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int output;
@@ -89,10 +90,17 @@ struct saa711x_state {
u8 apll;
};
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa711x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_write_byte_data(client, reg, value);
}
@@ -128,9 +136,9 @@ static int saa711x_has_reg(const int id, const u8 reg)
return 1;
}
-static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs)
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
unsigned char reg, data;
while (*regs != 0x00) {
@@ -139,18 +147,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg
/* According with datasheets, reserved regs should be
filled with 0 - seems better not to touch on they */
- if (saa711x_has_reg(state->ident,reg)) {
- if (saa711x_write(client, reg, data) < 0)
+ if (saa711x_has_reg(state->ident, reg)) {
+ if (saa711x_write(sd, reg, data) < 0)
return -1;
} else {
- v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
+ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
}
}
return 0;
}
-static inline int saa711x_read(struct i2c_client *client, u8 reg)
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
@@ -601,7 +611,7 @@ static int saa711x_odd_parity(u8 c)
return c & 1;
}
-static int saa711x_decode_vps(u8 * dst, u8 * p)
+static int saa711x_decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
@@ -648,7 +658,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p)
return err & 0xf0;
}
-static int saa711x_decode_wss(u8 * p)
+static int saa711x_decode_wss(u8 *p)
{
static const int wss_bits[8] = {
0, 0, 0, 1, 0, 1, 1, 1
@@ -675,9 +685,9 @@ static int saa711x_decode_wss(u8 * p)
return wss;
}
-static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
u32 acpf;
u32 acni;
u32 hz;
@@ -685,10 +695,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
u8 acc = 0; /* reg 0x3a, audio clock control */
/* Checks for chips that don't have audio clock (saa7111, saa7113) */
- if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
return 0;
- v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
+ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
/* sanity check */
if (freq < 32000 || freq > 48000)
@@ -715,66 +725,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
if (state->apll)
acc |= 0x08;
- saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
- saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
- saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
(acpf >> 8) & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
(acpf >> 16) & 0x03);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
state->audclk_freq = freq;
return 0;
}
-static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright);
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat);
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue);
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
default:
@@ -784,9 +794,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -808,16 +818,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_set_size(struct i2c_client *client, int width, int height)
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int HPSC, HFSC;
int VSCY;
int res;
int is_50hz = state->std & V4L2_STD_625_50;
int Vsrc = is_50hz ? 576 : 480;
- v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height);
+ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
/* FIXME need better bounds checking here */
if ((width < 1) || (width > 1440))
@@ -825,7 +835,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
if ((height < 1) || (height > Vsrc))
return -EINVAL;
- if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) {
+ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
/* Decoder only supports 720 columns and 480 or 576 lines */
if (width != 720)
return -EINVAL;
@@ -843,22 +853,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
/* Set output width/height */
/* width */
- saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
(u8) (width & 0xff));
- saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((width >> 8) & 0xff));
/* Vertical Scaling uses height/2 */
- res=height/2;
+ res = height / 2;
/* On 60Hz, it is using a higher Vertical Output Size */
if (!is_50hz)
res += (VRES_60HZ - 480) >> 1;
/* height */
- saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
(u8) (res & 0xff));
- saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((res >> 8) & 0xff));
/* Scaling settings */
@@ -869,54 +879,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
HFSC = (int)((1024 * 720) / (HPSC * width));
/* FIXME hardcodes to "Task B"
* write H prescaler integer */
- saa711x_write(client, R_D0_B_HORIZ_PRESCALING,
+ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
(u8) (HPSC & 0x3f));
- v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
/* write H fine-scaling (luminance) */
- saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
(u8) (HFSC & 0xff));
- saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
(u8) ((HFSC >> 8) & 0xff));
/* write H fine-scaling (chrominance)
* must be lum/2, so i'll just bitshift :) */
- saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING,
+ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
(u8) ((HFSC >> 1) & 0xff));
- saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
(u8) ((HFSC >> 9) & 0xff));
VSCY = (int)((1024 * Vsrc) / height);
- v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
/* Correct Contrast and Luminance */
- saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
(u8) (64 * 1024 / VSCY));
- saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL,
+ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
(u8) (64 * 1024 / VSCY));
/* write V fine-scaling (luminance) */
- saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC,
+ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
/* write V fine-scaling (chrominance) */
- saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC,
+ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
/* Activates task "B" */
- saa711x_write(client, R_80_GLOBAL_CNTL_1,
- saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
+ saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
return 0;
}
-static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
/* Prevent unnecessary standard changes. During a standard
change the I-Port is temporarily disabled. Any devices
@@ -932,13 +942,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
if (std & V4L2_STD_525_60) {
- v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
- saa711x_writeregs(client, saa7115_cfg_60hz_video);
- saa711x_set_size(client, 720, 480);
+ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ saa711x_set_size(sd, 720, 480);
} else {
- v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
- saa711x_writeregs(client, saa7115_cfg_50hz_video);
- saa711x_set_size(client, 720, 576);
+ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ saa711x_set_size(sd, 720, 576);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -952,7 +962,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
*/
if (state->ident == V4L2_IDENT_SAA7111 ||
state->ident == V4L2_IDENT_SAA7113) {
- u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f;
+ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
reg |= 0x30;
@@ -965,87 +975,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
} else if (std & V4L2_STD_SECAM) {
reg |= 0x50;
}
- saa711x_write(client, R_0E_CHROMA_CNTL_1, reg);
+ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
} else {
/* restart task B if needed */
- int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10;
+ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
if (taskb && state->ident == V4L2_IDENT_SAA7114) {
- saa711x_writeregs(client, saa7115_cfg_vbi_on);
+ saa711x_writeregs(sd, saa7115_cfg_vbi_on);
}
/* switch audio mode too! */
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- }
-}
-
-static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
-
- return state->std;
-}
-
-static void saa711x_log_status(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
- int reg1e, reg1f;
- int signalOk;
- int vcr;
-
- v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq);
- if (state->ident != V4L2_IDENT_SAA7115) {
- /* status for the saa7114 */
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
- signalOk = (reg1f & 0xc1) == 0x81;
- v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
- return;
- }
-
- /* status for the saa7115 */
- reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC);
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
-
- signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
- vcr = !(reg1f & 0x10);
-
- if (state->input >= 6) {
- v4l_info(client, "Input: S-Video %d\n", state->input - 6);
- } else {
- v4l_info(client, "Input: Composite %d\n", state->input);
+ saa711x_s_clock_freq(sd, state->audclk_freq);
}
- v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-
- switch (reg1e & 0x03) {
- case 1:
- v4l_info(client, "Detected format: NTSC\n");
- break;
- case 2:
- v4l_info(client, "Detected format: PAL\n");
- break;
- case 3:
- v4l_info(client, "Detected format: SECAM\n");
- break;
- default:
- v4l_info(client, "Detected format: BW/No color\n");
- break;
- }
- v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height);
}
/* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int is_50hz = (state->std & V4L2_STD_625_50);
u8 lcr[24];
int i, x;
#if 1
/* saa7113/7114/7118 VBI support are experimental */
- if (!saa711x_has_reg(state->ident,R_41_LCR_BASE))
+ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
return;
#else
@@ -1109,16 +1063,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
/* write the lcr registers */
for (i = 2; i <= 23; i++) {
- saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]);
+ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
}
/* enable/disable raw VBI capturing */
- saa711x_writeregs(client, fmt == NULL ?
+ saa711x_writeregs(sd, fmt == NULL ?
saa7115_cfg_vbi_on :
saa7115_cfg_vbi_off);
}
-static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
static u16 lcr2vbi[] = {
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
@@ -1134,10 +1088,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return -EINVAL;
memset(sliced, 0, sizeof(*sliced));
/* done if using raw VBI */
- if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10)
+ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
return 0;
for (i = 2; i <= 23; i++) {
- u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE);
+ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
sliced->service_lines[0][i] = lcr2vbi[v >> 4];
sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -1147,20 +1101,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return 0;
}
-static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- saa711x_set_lcr(client, &fmt->fmt.sliced);
+ saa711x_set_lcr(sd, &fmt->fmt.sliced);
return 0;
}
if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- saa711x_set_lcr(client, NULL);
+ saa711x_set_lcr(sd, NULL);
return 0;
}
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height);
+ return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height);
}
/* Decode the sliced VBI data stream as created by the saa7115.
@@ -1169,13 +1123,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
The current implementation uses SAV/EAV codes and not the ancillary data
headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
code. */
-static void saa711x_decode_vbi_line(struct i2c_client *client,
- struct v4l2_decode_vbi_line *vbi)
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
{
+ struct saa711x_state *state = to_state(sd);
static const char vbi_no_data_pattern[] = {
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
};
- struct saa711x_state *state = i2c_get_clientdata(client);
u8 *p = vbi->p;
u32 wss;
int id1, id2; /* the ID1 and ID2 bytes from the internal header */
@@ -1202,7 +1155,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
/* If the VBI slicer does not detect any signal it will fill up
the payload buffer with 0xa0 bytes. */
if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
- return;
+ return 0;
/* decode payloads */
switch (id2) {
@@ -1211,275 +1164,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
break;
case 4:
if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
- return;
+ return 0;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
case 5:
wss = saa711x_decode_wss(p);
if (wss == -1)
- return;
+ return 0;
p[0] = wss & 0xff;
p[1] = wss >> 8;
vbi->type = V4L2_SLICED_WSS_625;
break;
case 7:
if (saa711x_decode_vps(p, p) != 0)
- return;
+ return 0;
vbi->type = V4L2_SLICED_VPS;
break;
default:
- return;
+ break;
}
+ return 0;
}
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
+ int status;
- /* ioctls to allow direct access to the saa7115 registers for testing */
- switch (cmd) {
- case VIDIOC_S_FMT:
- return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->radio)
+ return 0;
+ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- case VIDIOC_G_FMT:
- return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg);
+ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ return 0;
+}
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa711x_set_audio_clock_freq(client, *(u32 *)arg);
+static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status;
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
- if (state->radio)
- break;
- status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
+ state->radio = 0;
+ saa711x_set_v4lstd(sd, std);
+ return 0;
+}
- v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
- vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
- break;
- }
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_LOG_STATUS:
- saa711x_log_status(client);
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg);
+static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa711x_state *state = to_state(sd);
+ u32 input = route->input;
+ u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output);
+ /* saa7111/3 does not have these inputs */
+ if ((state->ident == V4L2_IDENT_SAA7113 ||
+ state->ident == V4L2_IDENT_SAA7111) &&
+ (route->input == SAA7115_COMPOSITE4 ||
+ route->input == SAA7115_COMPOSITE5)) {
+ return -EINVAL;
+ }
+ if (route->input > SAA7115_SVIDEO3)
+ return -EINVAL;
+ if (route->output > SAA7115_IPORT_ON)
+ return -EINVAL;
+ if (state->input == route->input && state->output == route->output)
+ return 0;
+ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+ (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+ (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+ state->input = route->input;
+
+ /* saa7111 has slightly different input numbering */
+ if (state->ident == V4L2_IDENT_SAA7111) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(sd, R_10_CHROMA_CNTL_2,
+ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((route->output & 0xc0) ^ 0x40));
+ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((route->output & 2) ? 0x0a : 0));
+ }
- case VIDIOC_S_CTRL:
- return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ /* select mode */
+ saa711x_write(sd, R_02_INPUT_CNTL_1,
+ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+ input);
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+ /* bypass chrominance trap for S-Video modes */
+ saa711x_write(sd, R_09_LUMA_CNTL,
+ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill_std(qc);
- default:
- return -EINVAL;
- }
+ state->output = route->output;
+ if (state->ident == V4L2_IDENT_SAA7114 ||
+ state->ident == V4L2_IDENT_SAA7115) {
+ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
}
+ return 0;
+}
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = saa711x_get_v4lstd(client);
- break;
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_S_STD:
- state->radio = 0;
- saa711x_set_v4lstd(client, *(v4l2_std_id *)arg);
- break;
+ if (state->ident != V4L2_IDENT_SAA7111)
+ return -EINVAL;
+ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+ (val ? 0x80 : 0));
+ return 0;
+}
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
- route->input = state->input;
- route->output = state->output;
- break;
+ if (state->enable != enable) {
+ state->enable = enable;
+ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
+ state->enable);
}
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
- u32 input = route->input;
- u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
-
- v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
- /* saa7111/3 does not have these inputs */
- if ((state->ident == V4L2_IDENT_SAA7113 ||
- state->ident == V4L2_IDENT_SAA7111) &&
- (route->input == SAA7115_COMPOSITE4 ||
- route->input == SAA7115_COMPOSITE5)) {
- return -EINVAL;
- }
- if (route->input > SAA7115_SVIDEO3)
- return -EINVAL;
- if (route->output > SAA7115_IPORT_ON)
- return -EINVAL;
- if (state->input == route->input && state->output == route->output)
- break;
- v4l_dbg(1, debug, client, "now setting %s input %s output\n",
- (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
- state->input = route->input;
-
- /* saa7111 has slightly different input numbering */
- if (state->ident == V4L2_IDENT_SAA7111) {
- if (input >= SAA7115_COMPOSITE4)
- input -= 2;
- /* saa7111 specific */
- saa711x_write(client, R_10_CHROMA_CNTL_2,
- (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) |
- ((route->output & 0xc0) ^ 0x40));
- saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL,
- (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
- ((route->output & 2) ? 0x0a : 0));
- }
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq)
+{
+ struct saa711x_state *state = to_state(sd);
- /* select mode */
- saa711x_write(client, R_02_INPUT_CNTL_1,
- (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) |
- input);
+ if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
+ freq->freq != SAA7115_FREQ_24_576_MHZ)
+ return -EINVAL;
+ state->crystal_freq = freq->freq;
+ state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+ state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+ state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ return 0;
+}
- /* bypass chrominance trap for S-Video modes */
- saa711x_write(client, R_09_LUMA_CNTL,
- (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) |
- (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+ v4l2_dbg(1, debug, sd, "decoder RESET\n");
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+ return 0;
+}
- state->output = route->output;
- if (state->ident == V4L2_IDENT_SAA7114 ||
- state->ident == V4L2_IDENT_SAA7115) {
- saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
- (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
- (state->output & 0x01));
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+ /* Note: the internal field ID is inverted for NTSC,
+ so data->field 0 maps to the saa7115 even field,
+ whereas for PAL it maps to the saa7115 odd field. */
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa711x_read(sd, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x6c);
+ data->data[1] = saa711x_read(sd, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa711x_read(sd, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x69);
+ data->data[1] = saa711x_read(sd, 0x6a);
+ return 0;
}
- break;
+ /* XDS */
+ if (saa711x_read(sd, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x67);
+ data->data[1] = saa711x_read(sd, 0x68);
+ return 0;
+ default:
+ return -EINVAL;
}
+}
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, debug, client, "%s output\n",
- (cmd == VIDIOC_STREAMON) ? "enable" : "disable");
-
- if (state->enable != (cmd == VIDIOC_STREAMON)) {
- state->enable = (cmd == VIDIOC_STREAMON);
- saa711x_write(client,
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
- state->enable);
- }
- break;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_CRYSTAL_FREQ:
- {
- struct v4l2_crystal_freq *freq = arg;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa711x_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
- freq->freq != SAA7115_FREQ_24_576_MHZ)
- return -EINVAL;
- state->crystal_freq = freq->freq;
- state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
- state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
- state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- break;
- }
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_DECODE_VBI_LINE:
- saa711x_decode_vbi_line(client, arg);
- break;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
- case VIDIOC_INT_RESET:
- v4l_dbg(1, debug, client, "decoder RESET\n");
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
- break;
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa711x_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_GPIO:
- if (state->ident != V4L2_IDENT_SAA7111)
- return -EINVAL;
- saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) |
- (*(u32 *)arg ? 0x80 : 0));
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
- case VIDIOC_INT_G_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
- /* Note: the internal field ID is inverted for NTSC,
- so data->field 0 maps to the saa7115 even field,
- whereas for PAL it maps to the saa7115 odd field. */
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- if (saa711x_read(client, 0x6b) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x6c);
- data->data[1] = saa711x_read(client, 0x6d);
- return 0;
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0) {
- /* CC */
- if (saa711x_read(client, 0x66) & 0x30)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x69);
- data->data[1] = saa711x_read(client, 0x6a);
- return 0;
- }
- /* XDS */
- if (saa711x_read(client, 0x66) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x67);
- data->data[1] = saa711x_read(client, 0x68);
- return 0;
- default:
- return -EINVAL;
- }
- break;
+ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+ if (state->ident != V4L2_IDENT_SAA7115) {
+ /* status for the saa7114 */
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+ /* status for the saa7115 */
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa711x_read(client, reg->reg & 0xff);
- else
- saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
+ if (state->input >= 6)
+ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
+ else
+ v4l2_info(sd, "Input: Composite %d\n", state->input);
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ switch (reg1e & 0x03) {
+ case 1:
+ v4l2_info(sd, "Detected format: NTSC\n");
+ break;
+ case 2:
+ v4l2_info(sd, "Detected format: PAL\n");
+ break;
+ case 3:
+ v4l2_info(sd, "Detected format: SECAM\n");
+ break;
default:
- return -EINVAL;
+ v4l2_info(sd, "Detected format: BW/No color\n");
+ break;
}
-
+ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
return 0;
}
+static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa711x_core_ops = {
+ .log_status = saa711x_log_status,
+ .g_chip_ident = saa711x_g_chip_ident,
+ .g_ctrl = saa711x_g_ctrl,
+ .s_ctrl = saa711x_s_ctrl,
+ .queryctrl = saa711x_queryctrl,
+ .reset = saa711x_reset,
+ .s_gpio = saa711x_s_gpio,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa711x_g_register,
+ .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+ .s_std = saa711x_s_std,
+ .s_radio = saa711x_s_radio,
+ .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+ .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_routing = saa711x_s_routing,
+ .s_crystal_freq = saa711x_s_crystal_freq,
+ .g_fmt = saa711x_g_fmt,
+ .s_fmt = saa711x_s_fmt,
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .s_stream = saa711x_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa711x_ops = {
+ .core = &saa711x_core_ops,
+ .tuner = &saa711x_tuner_ops,
+ .audio = &saa711x_audio_ops,
+ .video = &saa711x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
-static int saa7115_probe(struct i2c_client *client,
+static int saa711x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa711x_state *state;
+ struct v4l2_subdev *sd;
int i;
char name[17];
char chip_id;
@@ -1490,8 +1520,8 @@ static int saa7115_probe(struct i2c_client *client,
return -EIO;
for (i = 0; i < 0x0f; i++) {
- saa711x_write(client, 0, i);
- name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+ i2c_smbus_write_byte_data(client, 0, i);
+ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
if (name[i] > '9')
name[i] += 'a' - '9' - 1;
}
@@ -1518,7 +1548,8 @@ static int saa7115_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
@@ -1545,41 +1576,45 @@ static int saa7115_probe(struct i2c_client *client,
break;
default:
state->ident = V4L2_IDENT_SAA7111;
- v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n");
+ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
}
state->audclk_freq = 48000;
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* init to 60hz/48khz */
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
switch (state->ident) {
case V4L2_IDENT_SAA7111:
- saa711x_writeregs(client, saa7111_init);
+ saa711x_writeregs(sd, saa7111_init);
break;
case V4L2_IDENT_SAA7113:
- saa711x_writeregs(client, saa7113_init);
+ saa711x_writeregs(sd, saa7113_init);
break;
default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
- saa711x_writeregs(client, saa7115_init_auto_input);
+ saa711x_writeregs(sd, saa7115_init_auto_input);
}
if (state->ident != V4L2_IDENT_SAA7111)
- saa711x_writeregs(client, saa7115_init_misc);
- saa711x_set_v4lstd(client, V4L2_STD_NTSC);
+ saa711x_writeregs(sd, saa7115_init_misc);
+ saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
- v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
- saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
+ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7115_remove(struct i2c_client *client)
+static int saa711x_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
@@ -1597,9 +1632,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7115",
.driverid = I2C_DRIVERID_SAA711X,
- .command = saa7115_command,
- .probe = saa7115_probe,
- .remove = saa7115_remove,
+ .command = saa711x_command,
+ .probe = saa711x_probe,
+ .remove = saa711x_remove,
.legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
.id_table = saa7115_id,
};
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index cc02fb18efa7..bfc85654795e 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -53,7 +53,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7127.h>
@@ -231,6 +231,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
*/
struct saa7127_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
u32 ident;
enum saa7127_input_type input_type;
@@ -250,6 +251,11 @@ struct saa7127_state {
u8 reg_61;
};
+static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7127_state, sd);
+}
+
static const char * const output_strs[] =
{
"S-Video + Composite",
@@ -281,32 +287,35 @@ static const char * const wss_strs[] = {
/* ----------------------------------------------------------------------- */
-static int saa7127_read(struct i2c_client *client, u8 reg)
+static int saa7127_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
/* ----------------------------------------------------------------------- */
-static int saa7127_write(struct i2c_client *client, u8 reg, u8 val)
+static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
for (i = 0; i < 3; i++) {
if (i2c_smbus_write_byte_data(client, reg, val) == 0)
return 0;
}
- v4l_err(client, "I2C Write Problem\n");
+ v4l2_err(sd, "I2C Write Problem\n");
return -1;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_write_inittab(struct i2c_client *client,
+static int saa7127_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0) {
- saa7127_write(client, regs->reg, regs->value);
+ saa7127_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
@@ -314,16 +323,16 @@ static int saa7127_write_inittab(struct i2c_client *client,
/* ----------------------------------------------------------------------- */
-static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 16))
return -EINVAL;
if (state->vps_enable != enable) {
- v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x54, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x54, enable << 7);
state->vps_enable = enable;
}
if (!enable)
@@ -334,91 +343,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat
state->vps_data[2] = data->data[9];
state->vps_data[3] = data->data[10];
state->vps_data[4] = data->data[11];
- v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n",
+ v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n",
state->vps_data[0], state->vps_data[1],
state->vps_data[2], state->vps_data[3],
state->vps_data[4]);
- saa7127_write(client, 0x55, state->vps_data[0]);
- saa7127_write(client, 0x56, state->vps_data[1]);
- saa7127_write(client, 0x57, state->vps_data[2]);
- saa7127_write(client, 0x58, state->vps_data[3]);
- saa7127_write(client, 0x59, state->vps_data[4]);
+ saa7127_write(sd, 0x55, state->vps_data[0]);
+ saa7127_write(sd, 0x56, state->vps_data[1]);
+ saa7127_write(sd, 0x57, state->vps_data[2]);
+ saa7127_write(sd, 0x58, state->vps_data[3]);
+ saa7127_write(sd, 0x59, state->vps_data[4]);
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 cc = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 21))
return -EINVAL;
if (state->cc_enable != enable) {
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Turn CC %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(state->xds_enable << 7) | (enable << 6) | 0x11);
state->cc_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "CC data: %04x\n", cc);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
state->cc_data = cc;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 xds = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 1 || data->line != 21))
return -EINVAL;
if (state->xds_enable != enable) {
- v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(enable << 7) | (state->cc_enable << 6) | 0x11);
state->xds_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "XDS data: %04x\n", xds);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
state->xds_data = xds;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 23))
return -EINVAL;
if (state->wss_enable != enable) {
- v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x27, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x27, enable << 7);
state->wss_enable = enable;
}
if (!enable)
return 0;
- saa7127_write(client, 0x26, data->data[0]);
- saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f));
- v4l_dbg(1, debug, client,
+ saa7127_write(sd, 0x26, data->data[0]);
+ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
+ v4l2_dbg(1, debug, sd,
"WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
return 0;
@@ -426,18 +435,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat
/* ----------------------------------------------------------------------- */
-static int saa7127_set_video_enable(struct i2c_client *client, int enable)
+static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
if (enable) {
- v4l_dbg(1, debug, client, "Enable Video Output\n");
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x61, state->reg_61);
+ v4l2_dbg(1, debug, sd, "Enable Video Output\n");
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x61, state->reg_61);
} else {
- v4l_dbg(1, debug, client, "Disable Video Output\n");
- saa7127_write(client, 0x2d, (state->reg_2d & 0xf0));
- saa7127_write(client, 0x61, (state->reg_61 | 0xc0));
+ v4l2_dbg(1, debug, sd, "Disable Video Output\n");
+ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
}
state->video_enable = enable;
return 0;
@@ -445,32 +454,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable)
/* ----------------------------------------------------------------------- */
-static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std)
+static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
const struct i2c_reg_value *inittab;
if (std & V4L2_STD_525_60) {
- v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
inittab = saa7127_init_config_60hz;
state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
} else {
- v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n");
inittab = saa7127_init_config_50hz;
state->reg_61 = SAA7127_50HZ_DAC_CONTROL;
}
/* Write Table */
- saa7127_write_inittab(client, inittab);
+ saa7127_write_inittab(sd, inittab);
state->std = std;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_output_type(struct i2c_client *client, int output)
+static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (output) {
case SAA7127_OUTPUT_TYPE_RGB:
@@ -506,165 +515,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output)
default:
return -EINVAL;
}
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Selecting %s output type\n", output_strs[output]);
/* Configure Encoder */
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->output_type = output;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_input_type(struct i2c_client *client, int input)
+static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (input) {
case SAA7127_INPUT_TYPE_NORMAL: /* avia */
- v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n");
+ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
state->reg_3a_cb = 0;
break;
case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
- v4l_dbg(1, debug, client, "Selecting Color Bar generator\n");
+ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
state->reg_3a_cb = 0x80;
break;
default:
return -EINVAL;
}
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->input_type = input;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
- struct v4l2_format *fmt = arg;
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_S_STD_OUTPUT:
- if (state->std == *(v4l2_std_id *)arg)
- break;
- return saa7127_set_std(client, *(v4l2_std_id *)arg);
-
- case VIDIOC_INT_G_STD_OUTPUT:
- *(v4l2_std_id *)arg = state->std;
- break;
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input_type;
- route->output = state->output_type;
- break;
+ if (state->std == std)
+ return 0;
+ return saa7127_set_std(sd, std);
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- int rc = 0;
+static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa7127_state *state = to_state(sd);
+ int rc = 0;
+
+ if (state->input_type != route->input)
+ rc = saa7127_set_input_type(sd, route->input);
+ if (rc == 0 && state->output_type != route->output)
+ rc = saa7127_set_output_type(sd, route->output);
+ return rc;
+}
- if (state->input_type != route->input)
- rc = saa7127_set_input_type(client, route->input);
- if (rc == 0 && state->output_type != route->output)
- rc = saa7127_set_output_type(client, route->output);
- return rc;
- }
+static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- if (state->video_enable == (cmd == VIDIOC_STREAMON))
- break;
- return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON);
-
- case VIDIOC_G_FMT:
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
-
- memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
- if (state->vps_enable)
- fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
- if (state->wss_enable)
- fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
- if (state->cc_enable) {
- fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- }
- fmt->fmt.sliced.service_set =
- (state->vps_enable ? V4L2_SLICED_VPS : 0) |
- (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
- (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
- break;
+ if (state->video_enable == enable)
+ return 0;
+ return saa7127_set_video_enable(sd, enable);
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
- v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal");
- v4l_info(client, "Output: %s\n", state->video_enable ?
- output_strs[state->output_type] : "disabled");
- v4l_info(client, "WSS: %s\n", state->wss_enable ?
- wss_strs[state->wss_mode] : "disabled");
- v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
- v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
- break;
+static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct saa7127_state *state = to_state(sd);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa7127_read(client, reg->reg & 0xff);
- else
- saa7127_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
- case VIDIOC_INT_S_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
-
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- return saa7127_set_wss(client, data);
- case V4L2_SLICED_VPS:
- return saa7127_set_vps(client, data);
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0)
- return saa7127_set_cc(client, data);
- return saa7127_set_xds(client, data);
- default:
- return -EINVAL;
- }
- break;
+ memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
+ if (state->vps_enable)
+ fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
}
+ fmt->fmt.sliced.service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
-
+static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(sd, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(sd, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(sd, data);
+ return saa7127_set_xds(sd, data);
default:
return -EINVAL;
}
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa7127_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa7127_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa7127_log_status(struct v4l2_subdev *sd)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
+ v4l2_info(sd, "Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7127_core_ops = {
+ .log_status = saa7127_log_status,
+ .g_chip_ident = saa7127_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa7127_g_register,
+ .s_register = saa7127_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops saa7127_video_ops = {
+ .s_vbi_data = saa7127_s_vbi_data,
+ .g_fmt = saa7127_g_fmt,
+ .s_std_output = saa7127_s_std_output,
+ .s_routing = saa7127_s_routing,
+ .s_stream = saa7127_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa7127_ops = {
+ .core = &saa7127_core_ops,
+ .video = &saa7127_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int saa7127_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa7127_state *state;
+ struct v4l2_subdev *sd;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
/* Check if the adapter supports the needed features */
@@ -674,40 +713,42 @@ static int saa7127_probe(struct i2c_client *client,
v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
client->addr << 1);
+ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
+
/* First test register 0: Bits 5-7 are a version ID (should be 0),
and bit 2 should also be 0.
This is rather general, so the second test is more specific and
looks at the 'ending point of burst in clock cycles' which is
0x1d after a reset and not expected to ever change. */
- if ((saa7127_read(client, 0) & 0xe4) != 0 ||
- (saa7127_read(client, 0x29) & 0x3f) != 0x1d) {
- v4l_dbg(1, debug, client, "saa7127 not found\n");
+ if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
+ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
+ v4l2_dbg(1, debug, sd, "saa7127 not found\n");
+ kfree(state);
return -ENODEV;
}
- state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
-
- if (state == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(client, state);
/* Configure Encoder */
- v4l_dbg(1, debug, client, "Configuring encoder\n");
- saa7127_write_inittab(client, saa7127_init_config_common);
- saa7127_set_std(client, V4L2_STD_NTSC);
- saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH);
- saa7127_set_vps(client, &vbi);
- saa7127_set_wss(client, &vbi);
- saa7127_set_cc(client, &vbi);
- saa7127_set_xds(client, &vbi);
+ v4l2_dbg(1, debug, sd, "Configuring encoder\n");
+ saa7127_write_inittab(sd, saa7127_init_config_common);
+ saa7127_set_std(sd, V4L2_STD_NTSC);
+ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(sd, &vbi);
+ saa7127_set_wss(sd, &vbi);
+ saa7127_set_cc(sd, &vbi);
+ saa7127_set_xds(sd, &vbi);
if (test_image == 1)
/* The Encoder has an internal Colorbar generator */
/* This can be used for debugging */
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
else
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
- saa7127_set_video_enable(client, 1);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
+ saa7127_set_video_enable(sd, 1);
if (id->driver_data) { /* Chip type is already known */
state->ident = id->driver_data;
@@ -715,10 +756,10 @@ static int saa7127_probe(struct i2c_client *client,
int read_result;
/* Detect if it's an saa7129 */
- read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
- if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2,
+ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
read_result);
state->ident = V4L2_IDENT_SAA7129;
strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
@@ -728,10 +769,10 @@ static int saa7127_probe(struct i2c_client *client,
}
}
- v4l_info(client, "%s found @ 0x%x (%s)\n", client->name,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
if (state->ident == V4L2_IDENT_SAA7129)
- saa7127_write_inittab(client, saa7129_init_config_extra);
+ saa7127_write_inittab(sd, saa7129_init_config_extra);
return 0;
}
@@ -739,9 +780,12 @@ static int saa7127_probe(struct i2c_client *client,
static int saa7127_remove(struct i2c_client *client)
{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
/* Turn off TV output */
- saa7127_set_video_enable(client, 0);
- kfree(i2c_get_clientdata(client));
+ saa7127_set_video_enable(sd, 0);
+ kfree(to_state(sd));
return 0;
}
@@ -760,7 +804,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7127",
.driverid = I2C_DRIVERID_SAA7127,
- .command = saa7127_command,
.probe = saa7127_probe,
.remove = saa7127_remove,
.id_table = saa7127_id,
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 7021bbf5897b..fc2164e28e76 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -34,9 +34,9 @@ config VIDEO_SAA7134_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
---help---
This adds support for DVB cards based on the
Philips saa7134 chip.
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index ddc5402c5fb0..a2e3f6729c5b 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -4606,6 +4606,43 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0200000,
},
},
+ [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = {
+ .name = "Kworld Plus TV Analog Lite PCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x80000700,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x200,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x200,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x100,
+ },
+ .mute = {
+ .name = name_mute,
+ .vmux = 8,
+ .amux = 2,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4736,6 +4773,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_MD7134,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */
+ .subdevice = 0x5000, /* only analog TV and DVB-T for now */
+ .driver_data = SAA7134_BOARD_MD7134,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = 0x1048,
.subdevice = 0x226b,
@@ -5653,6 +5696,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x4878, /* REV:1.02G */
.driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x17de,
+ .subdevice = 0x7128,
+ .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5880,6 +5929,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
case SAA7134_BOARD_REAL_ANGEL_220:
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -6048,7 +6098,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
struct v4l2_priv_tun_config xc2028_cfg;
struct xc2028_ctrl ctl;
- memset(&xc2028_cfg, 0, sizeof(ctl));
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
memset(&ctl, 0, sizeof(ctl));
ctl.fname = XC2028_DEFAULT_FIRMWARE;
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 8c46115d4c79..d9a5652595b5 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -954,20 +954,14 @@ static int dvb_init(struct saa7134_dev *dev)
/* FIXME: add support for multi-frontend */
mutex_init(&dev->frontends.lock);
INIT_LIST_HEAD(&dev->frontends.felist);
- dev->frontends.active_fe_id = 0;
printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
-
- if (videobuf_dvb_alloc_frontend(&dev->frontends, 1) == NULL) {
+ fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+ if (!fe0) {
printk(KERN_ERR "%s() failed to alloc\n", __func__);
return -ENOMEM;
}
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
/* init struct videobuf_dvb */
dev->ts.nr_bufs = 32;
dev->ts.nr_packets = 32*4;
@@ -1376,7 +1370,7 @@ static int dvb_init(struct saa7134_dev *dev)
};
if (!fe0->dvb.frontend)
- return -1;
+ goto dettach_frontend;
fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
if (!fe) {
@@ -1388,7 +1382,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (NULL == fe0->dvb.frontend) {
printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
- return -1;
+ goto dettach_frontend;
}
/* define general-purpose callback pointer */
fe0->dvb.frontend->callback = saa7134_tuner_callback;
@@ -1411,11 +1405,8 @@ static int dvb_init(struct saa7134_dev *dev)
return ret;
dettach_frontend:
- if (fe0->dvb.frontend)
- dvb_frontend_detach(fe0->dvb.frontend);
- fe0->dvb.frontend = NULL;
-
- return -1;
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -EINVAL;
}
static int dvb_fini(struct saa7134_dev *dev)
@@ -1454,8 +1445,7 @@ static int dvb_fini(struct saa7134_dev *dev)
}
}
}
- if (fe0->dvb.frontend)
- videobuf_dvb_unregister_bus(&dev->frontends);
+ videobuf_dvb_unregister_bus(&dev->frontends);
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index c53fd5f9f6b5..d2124f64e4e2 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -97,6 +97,15 @@ static int build_key(struct saa7134_dev *dev)
dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
gpio, ir->mask_keycode, data);
+ switch (dev->board) {
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ if (data == ir->mask_keycode)
+ ir_input_nokey(ir->dev, &ir->ir);
+ else
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+ return 0;
+ }
+
if (ir->polling) {
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
@@ -586,6 +595,11 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keyup = 0x4000;
polling = 50; /* ms */
break;
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ ir_codes = ir_codes_kworld_plus_tv_analog;
+ mask_keycode = 0x7f;
+ polling = 40; /* ms */
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index c5d0b44c179e..76b16407b01e 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -159,7 +159,7 @@ static struct saa7134_tvaudio tvaudio[] = {
.mode = TVAUDIO_FM_MONO,
}
};
-#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio))
+#define TVAUDIO ARRAY_SIZE(tvaudio)
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 24096d6e1ef8..f6c1fcc72070 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -275,8 +275,9 @@ struct saa7134_format {
#define SAA7134_BOARD_REAL_ANGEL_220 150
#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
#define SAA7134_BOARD_ASUSTeK_TIGER 152
+#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
-#define SAA7134_MAXBOARDS 8
+#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index af60ede5310d..9befca65905e 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -37,7 +37,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
*/
struct saa717x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int enable;
@@ -75,6 +76,11 @@ struct saa717x_state {
int audio_input;
};
+static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa717x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* for audio mode */
@@ -88,8 +94,9 @@ struct saa717x_state {
/* ----------------------------------------------------------------------- */
-static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
+static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488;
unsigned char mm1[6];
@@ -109,20 +116,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
}
msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */
msg.buf = mm1;
- v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value);
return i2c_transfer(adap, &msg, 1) == 1;
}
-static void saa717x_write_regs(struct i2c_client *client, u32 *data)
+static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data)
{
while (data[0] || data[1]) {
- saa717x_write(client, data[0], data[1]);
+ saa717x_write(sd, data[0], data[1]);
data += 2;
}
}
-static u32 saa717x_read(struct i2c_client *client, u32 reg)
+static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528;
unsigned char mm1[2];
@@ -146,7 +154,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg)
else
value = mm2[0] & 0xff;
- v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value);
return value;
}
@@ -680,7 +688,7 @@ static u32 reg_set_audio_template[4][2] =
/* Get detected audio flags (from saa7134 driver) */
-static void get_inf_dev_status(struct i2c_client *client,
+static void get_inf_dev_status(struct v4l2_subdev *sd,
int *dual_flag, int *stereo_flag)
{
u32 reg_data3;
@@ -719,13 +727,13 @@ static void get_inf_dev_status(struct i2c_client *client,
/* (demdec status: 0x528) */
/* read current status */
- reg_data3 = saa717x_read(client, 0x0528);
+ reg_data3 = saa717x_read(sd, 0x0528);
- v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n",
+ v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n",
reg_data3, stdres[reg_data3 & 0x1f],
(reg_data3 & 0x000020) ? ",stereo" : "",
(reg_data3 & 0x000040) ? ",dual" : "");
- v4l_dbg(1, debug, client, "detailed status: "
+ v4l2_dbg(1, debug, sd, "detailed status: "
"%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
(reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "",
(reg_data3 & 0x000100) ? " A2/EIAJ dual " : "",
@@ -746,51 +754,51 @@ static void get_inf_dev_status(struct i2c_client *client,
(reg_data3 & 0x100000) ? " init done " : "");
if (reg_data3 & 0x000220) {
- v4l_dbg(1, debug, client, "ST!!!\n");
+ v4l2_dbg(1, debug, sd, "ST!!!\n");
*stereo_flag = 1;
}
if (reg_data3 & 0x000140) {
- v4l_dbg(1, debug, client, "DUAL!!!\n");
+ v4l2_dbg(1, debug, sd, "DUAL!!!\n");
*dual_flag = 1;
}
}
/* regs write to set audio mode */
-static void set_audio_mode(struct i2c_client *client, int audio_mode)
+static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
{
- v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n",
+ v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n",
audio_mode);
- saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]);
- saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]);
+ saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]);
+ saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct i2c_client *client,
+static void set_video_output_level_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
/* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
- saa717x_write(client, 0x10a, decoder->bright);
+ saa717x_write(sd, 0x10a, decoder->bright);
/* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
0h (luminance off) 40: i2c dump
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10b, decoder->contrast);
+ saa717x_write(sd, 0x10b, decoder->contrast);
/* saturation? 7fh(max)-40h(ITU)-0h(color off)
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10c, decoder->sat);
+ saa717x_write(sd, 0x10c, decoder->sat);
/* color hue (phase) control
7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
- saa717x_write(client, 0x10d, decoder->hue);
+ saa717x_write(sd, 0x10d, decoder->hue);
}
/* write regs to set audio volume, bass and treble */
-static int set_audio_regs(struct i2c_client *client,
+static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
u8 mute = 0xac; /* -84 dB */
@@ -798,8 +806,8 @@ static int set_audio_regs(struct i2c_client *client,
unsigned int work_l, work_r;
/* set SIF analog I/O select */
- saa717x_write(client, 0x0594, decoder->audio_input);
- v4l_dbg(1, debug, client, "set audio input %d\n",
+ saa717x_write(sd, 0x0594, decoder->audio_input);
+ v4l2_dbg(1, debug, sd, "set audio input %d\n",
decoder->audio_input);
/* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */
@@ -819,17 +827,17 @@ static int set_audio_regs(struct i2c_client *client,
((u8)decoder->audio_main_vol_r << 8);
}
- saa717x_write(client, 0x480, val);
+ saa717x_write(sd, 0x480, val);
/* bass and treble; go to another function */
/* set bass and treble */
val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
- saa717x_write(client, 0x488, val);
+ saa717x_write(sd, 0x488, val);
return 0;
}
/********** scaling staff ***********/
-static void set_h_prescale(struct i2c_client *client,
+static void set_h_prescale(struct v4l2_subdev *sd,
int task, int prescale)
{
static const struct {
@@ -862,107 +870,101 @@ static void set_h_prescale(struct i2c_client *client,
return;
/* horizonal prescaling */
- saa717x_write(client, 0x60 + task_shift, vals[i].xpsc);
+ saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc);
/* accumulation length */
- saa717x_write(client, 0x61 + task_shift, vals[i].xacl);
+ saa717x_write(sd, 0x61 + task_shift, vals[i].xacl);
/* level control */
- saa717x_write(client, 0x62 + task_shift,
+ saa717x_write(sd, 0x62 + task_shift,
(vals[i].xc2_1 << 3) | vals[i].xdcg);
/*FIR prefilter control */
- saa717x_write(client, 0x63 + task_shift,
+ saa717x_write(sd, 0x63 + task_shift,
(vals[i].vpfy << 2) | vals[i].vpfy);
}
/********** scaling staff ***********/
-static void set_v_scale(struct i2c_client *client, int task, int yscale)
+static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
{
int task_shift;
task_shift = task * 0x40;
/* Vertical scaling ratio (LOW) */
- saa717x_write(client, 0x70 + task_shift, yscale & 0xff);
+ saa717x_write(sd, 0x70 + task_shift, yscale & 0xff);
/* Vertical scaling ratio (HI) */
- saa717x_write(client, 0x71 + task_shift, yscale >> 8);
-}
-
-static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
-{
- /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */
- return 0;
+ saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
-static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- v4l_dbg(1, debug, client, "bright:%d\n", state->bright);
- saa717x_write(client, 0x10a, state->bright);
+ v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
+ saa717x_write(sd, 0x10a, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast);
- saa717x_write(client, 0x10b, state->contrast);
+ v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
+ saa717x_write(sd, 0x10b, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- v4l_dbg(1, debug, client, "sat:%d\n", state->sat);
- saa717x_write(client, 0x10c, state->sat);
+ v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
+ saa717x_write(sd, 0x10c, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- v4l_dbg(1, debug, client, "hue:%d\n", state->hue);
- saa717x_write(client, 0x10d, state->hue);
+ v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
+ saa717x_write(sd, 0x10d, state->hue);
break;
case V4L2_CID_AUDIO_MUTE:
state->audio_main_mute = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_VOLUME:
state->audio_main_volume = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BALANCE:
state->audio_main_balance = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_TREBLE:
state->audio_main_treble = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BASS:
state->audio_main_bass = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
default:
@@ -972,9 +974,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -1103,13 +1105,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = {
},
};
-static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp)
+static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
+ struct saa717x_state *decoder = to_state(sd);
+ int inp = route->input;
int is_tuner = inp & 0x80; /* tuner input flag */
inp &= 0x7f;
- v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp);
+ v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp);
/* inputs from 0-9 are available*/
/* saa717x have mode0-mode9 but mode5 is reserved. */
if (inp < 0 || inp > 9 || inp == 5)
@@ -1119,222 +1123,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta
int input_line = inp;
decoder->input = input_line;
- v4l_dbg(1, debug, client, "now setting %s input %d\n",
+ v4l2_dbg(1, debug, sd, "now setting %s input %d\n",
input_line >= 6 ? "S-Video" : "Composite",
input_line);
/* select mode */
- saa717x_write(client, 0x102,
- (saa717x_read(client, 0x102) & 0xf0) |
+ saa717x_write(sd, 0x102,
+ (saa717x_read(sd, 0x102) & 0xf0) |
input_line);
/* bypass chrominance trap for modes 6..9 */
- saa717x_write(client, 0x109,
- (saa717x_read(client, 0x109) & 0x7f) |
+ saa717x_write(sd, 0x109,
+ (saa717x_read(sd, 0x109) & 0x7f) |
(input_line < 6 ? 0x0 : 0x80));
/* change audio_mode */
if (is_tuner) {
/* tuner */
- set_audio_mode(client, decoder->tuner_audio_mode);
+ set_audio_mode(sd, decoder->tuner_audio_mode);
} else {
/* Force to STEREO mode if Composite or
* S-Video were chosen */
- set_audio_mode(client, TUNER_AUDIO_STEREO);
+ set_audio_mode(sd, TUNER_AUDIO_STEREO);
}
/* change initialize procedure (Composite/S-Video) */
if (is_tuner)
- saa717x_write_regs(client, reg_init_tuner_input);
+ saa717x_write_regs(sd, reg_init_tuner_input);
else if (input_line >= 6)
- saa717x_write_regs(client, reg_init_svideo_input);
+ saa717x_write_regs(sd, reg_init_svideo_input);
else
- saa717x_write_regs(client, reg_init_composite_input);
+ saa717x_write_regs(sd, reg_init_composite_input);
}
return 0;
}
-static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- struct saa717x_state *decoder = i2c_get_clientdata(client);
-
- v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd);
+ int i;
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa717x_set_audio_clock_freq(client, *(u32 *)arg);
+ for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
+ if (qc->id && qc->id == saa717x_qctrl[i].id) {
+ memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_CTRL:
- return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa717x_read(sd, reg->reg);
+ return 0;
+}
- case VIDIOC_QUERYCTRL: {
- struct v4l2_queryctrl *qc = arg;
- int i;
+static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr = reg->reg & 0xffff;
+ u8 val = reg->val & 0xff;
- for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
- if (qc->id && qc->id == saa717x_qctrl[i].id) {
- memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
- return 0;
- }
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
return -EINVAL;
- }
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER: {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = saa717x_read(client, reg->reg);
- break;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa717x_write(sd, addr, val);
+ return 0;
+}
+#endif
- case VIDIOC_DBG_S_REGISTER: {
- struct v4l2_register *reg = arg;
- u16 addr = reg->reg & 0xffff;
- u8 val = reg->val & 0xff;
+static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix;
+ int prescale, h_scale, v_scale;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- saa717x_write(client, addr, val);
- break;
- }
-#endif
+ pix = &fmt->fmt.pix;
+ v4l2_dbg(1, debug, sd, "decoder set size\n");
- case VIDIOC_S_FMT: {
- struct v4l2_format *fmt = (struct v4l2_format *)arg;
- struct v4l2_pix_format *pix;
- int prescale, h_scale, v_scale;
-
- pix = &fmt->fmt.pix;
- v4l_dbg(1, debug, client, "decoder set size\n");
-
- /* FIXME need better bounds checking here */
- if (pix->width < 1 || pix->width > 1440)
- return -EINVAL;
- if (pix->height < 1 || pix->height > 960)
- return -EINVAL;
-
- /* scaling setting */
- /* NTSC and interlace only */
- prescale = SAA717X_NTSC_WIDTH / pix->width;
- if (prescale == 0)
- prescale = 1;
- h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
- /* interlace */
- v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
-
- /* Horizontal prescaling etc */
- set_h_prescale(client, 0, prescale);
- set_h_prescale(client, 1, prescale);
-
- /* Horizontal scaling increment */
- /* TASK A */
- saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF));
-
- /* Vertical prescaling etc */
- set_v_scale(client, 0, v_scale);
- set_v_scale(client, 1, v_scale);
-
- /* set video output size */
- /* video number of pixels at output */
- /* TASK A */
- saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF));
-
- /* video number of lines at output */
- /* TASK A */
- saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF));
- break;
- }
+ /* FIXME need better bounds checking here */
+ if (pix->width < 1 || pix->width > 1440)
+ return -EINVAL;
+ if (pix->height < 1 || pix->height > 960)
+ return -EINVAL;
- case AUDC_SET_RADIO:
- decoder->radio = 1;
- break;
+ /* scaling setting */
+ /* NTSC and interlace only */
+ prescale = SAA717X_NTSC_WIDTH / pix->width;
+ if (prescale == 0)
+ prescale = 1;
+ h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
+ /* interlace */
+ v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
+
+ /* Horizontal prescaling etc */
+ set_h_prescale(sd, 0, prescale);
+ set_h_prescale(sd, 1, prescale);
+
+ /* Horizontal scaling increment */
+ /* TASK A */
+ saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF));
+
+ /* Vertical prescaling etc */
+ set_v_scale(sd, 0, v_scale);
+ set_v_scale(sd, 1, v_scale);
+
+ /* set video output size */
+ /* video number of pixels at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF));
+
+ /* video number of lines at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF));
+ return 0;
+}
- case VIDIOC_S_STD: {
- v4l2_std_id std = *(v4l2_std_id *) arg;
+static int saa717x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *decoder = to_state(sd);
- v4l_dbg(1, debug, client, "decoder set norm ");
- v4l_dbg(1, debug, client, "(not yet implementd)\n");
+ decoder->radio = 1;
+ return 0;
+}
- decoder->radio = 0;
- decoder->std = std;
- break;
- }
+static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_INT_G_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "decoder set norm ");
+ v4l2_dbg(1, debug, sd, "(not yet implementd)\n");
- route->input = decoder->audio_input;
- route->output = 0;
- break;
- }
+ decoder->radio = 0;
+ decoder->std = std;
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa717x_state *decoder = to_state(sd);
- if (route->input < 3) { /* FIXME! --tadachi */
- decoder->audio_input = route->input;
- v4l_dbg(1, debug, client,
+ if (route->input < 3) { /* FIXME! --tadachi */
+ decoder->audio_input = route->input;
+ v4l2_dbg(1, debug, sd,
"set decoder audio input to %d\n",
decoder->audio_input);
- set_audio_regs(client, decoder);
- break;
- }
- return -ERANGE;
- }
-
- case VIDIOC_INT_S_VIDEO_ROUTING: {
- struct v4l2_routing *route = arg;
- int inp = route->input;
-
- return saa717x_set_video_input(client, decoder, inp);
+ set_audio_regs(sd, decoder);
+ return 0;
}
+ return -ERANGE;
+}
- case VIDIOC_STREAMON: {
- v4l_dbg(1, debug, client, "decoder enable output\n");
- decoder->enable = 1;
- saa717x_write(client, 0x193, 0xa6);
- break;
- }
+static int saa717x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_STREAMOFF: {
- v4l_dbg(1, debug, client, "decoder disable output\n");
- decoder->enable = 0;
- saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */
- break;
- }
+ v4l2_dbg(1, debug, sd, "decoder %s output\n",
+ enable ? "enable" : "disable");
+ decoder->enable = enable;
+ saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26);
+ return 0;
+}
- /* change audio mode */
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int audio_mode;
- char *mes[4] = {
- "MONO", "STEREO", "LANG1", "LANG2/SAP"
- };
+/* change audio mode */
+static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int audio_mode;
+ char *mes[4] = {
+ "MONO", "STEREO", "LANG1", "LANG2/SAP"
+ };
- audio_mode = V4L2_TUNER_MODE_STEREO;
+ audio_mode = V4L2_TUNER_MODE_STEREO;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
audio_mode = TUNER_AUDIO_MONO;
break;
@@ -1347,70 +1326,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
case V4L2_TUNER_MODE_LANG1:
audio_mode = TUNER_AUDIO_LANG1;
break;
- }
-
- v4l_dbg(1, debug, client, "change audio mode to %s\n",
- mes[audio_mode]);
- decoder->tuner_audio_mode = audio_mode;
- /* The registers are not changed here. */
- /* See DECODER_ENABLE_OUTPUT section. */
- set_audio_mode(client, decoder->tuner_audio_mode);
- break;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int dual_f, stereo_f;
+ v4l2_dbg(1, debug, sd, "change audio mode to %s\n",
+ mes[audio_mode]);
+ decoder->tuner_audio_mode = audio_mode;
+ /* The registers are not changed here. */
+ /* See DECODER_ENABLE_OUTPUT section. */
+ set_audio_mode(sd, decoder->tuner_audio_mode);
+ return 0;
+}
- if (decoder->radio)
- break;
- get_inf_dev_status(client, &dual_f, &stereo_f);
+static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int dual_f, stereo_f;
- v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n",
- stereo_f, dual_f);
+ if (decoder->radio)
+ return 0;
+ get_inf_dev_status(sd, &dual_f, &stereo_f);
- /* mono */
- if ((dual_f == 0) && (stereo_f == 0)) {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==MONO\n");
- }
+ v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n",
+ stereo_f, dual_f);
- /* stereo */
- if (stereo_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
- vt->audmode == V4L2_TUNER_MODE_LANG1) {
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- v4l_dbg(1, debug, client, "DETECT==ST(ST)\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n");
- }
- }
+ /* mono */
+ if ((dual_f == 0) && (stereo_f == 0)) {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==MONO\n");
+ }
- /* dual */
- if (dual_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL1\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL2\n");
- }
+ /* stereo */
+ if (stereo_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
+ vt->audmode == V4L2_TUNER_MODE_LANG1) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n");
}
- break;
}
- case VIDIOC_LOG_STATUS:
- /* not yet implemented */
- break;
-
- default:
- return -EINVAL;
+ /* dual */
+ if (dual_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n");
+ }
}
-
return 0;
}
+static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa717x_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa717x_g_register,
+ .s_register = saa717x_s_register,
+#endif
+ .queryctrl = saa717x_queryctrl,
+ .g_ctrl = saa717x_g_ctrl,
+ .s_ctrl = saa717x_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
+ .g_tuner = saa717x_g_tuner,
+ .s_tuner = saa717x_s_tuner,
+ .s_std = saa717x_s_std,
+ .s_radio = saa717x_s_radio,
+};
+
+static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+ .s_routing = saa717x_s_video_routing,
+ .s_fmt = saa717x_s_fmt,
+ .s_stream = saa717x_s_stream,
+};
+
+static const struct v4l2_subdev_audio_ops saa717x_audio_ops = {
+ .s_routing = saa717x_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops saa717x_ops = {
+ .core = &saa717x_core_ops,
+ .tuner = &saa717x_tuner_ops,
+ .audio = &saa717x_audio_ops,
+ .video = &saa717x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
@@ -1421,6 +1431,7 @@ static int saa717x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
+ struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
@@ -1428,13 +1439,21 @@ static int saa717x_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- if (saa717x_write(client, 0x5a4, 0xfe) &&
- saa717x_write(client, 0x5a5, 0x0f) &&
- saa717x_write(client, 0x5a6, 0x00) &&
- saa717x_write(client, 0x5a7, 0x01))
- id = saa717x_read(client, 0x5a0);
+ decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa717x_ops);
+
+ if (saa717x_write(sd, 0x5a4, 0xfe) &&
+ saa717x_write(sd, 0x5a5, 0x0f) &&
+ saa717x_write(sd, 0x5a6, 0x00) &&
+ saa717x_write(sd, 0x5a7, 0x01))
+ id = saa717x_read(sd, 0x5a0);
if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) {
- v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id);
+ v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id);
+ kfree(decoder);
return -ENODEV;
}
if (id == 0xc2)
@@ -1445,14 +1464,8 @@ static int saa717x_probe(struct i2c_client *client,
p = "saa7174HL";
else
p = "saa7171";
- v4l_info(client, "%s found @ 0x%x (%s)\n", p,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
-
- decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
- i2c_set_clientdata(client, decoder);
-
- if (decoder == NULL)
- return -ENOMEM;
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
@@ -1481,15 +1494,15 @@ static int saa717x_probe(struct i2c_client *client,
decoder->audio_main_volume =
(decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
- saa717x_write_regs(client, reg_init_initialize);
- set_video_output_level_regs(client, decoder);
+ saa717x_write_regs(sd, reg_init_initialize);
+ set_video_output_level_regs(sd, decoder);
/* set bass,treble to 0db 20041101 K.Ohta */
decoder->audio_main_bass = 0;
decoder->audio_main_treble = 0;
- set_audio_regs(client, decoder);
+ set_audio_regs(sd, decoder);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
@@ -1498,7 +1511,10 @@ static int saa717x_probe(struct i2c_client *client,
static int saa717x_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index 044a2e94c34d..d652f25eef0e 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -975,8 +975,7 @@ static int se401_close(struct inode *inode, struct file *file)
return 0;
}
-static int se401_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int se401_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_se401 *se401 = (struct usb_se401 *)vdev;
@@ -1142,7 +1141,7 @@ static int se401_do_ioctl(struct inode *inode, struct file *file,
static int se401_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, se401_do_ioctl);
+ return video_usercopy(file, cmd, arg, se401_do_ioctl);
}
static ssize_t se401_read(struct file *file, char __user *buf,
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 2407607f2eff..9a2586b07a05 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -29,8 +29,8 @@
#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/videodev2.h>
+#include <linux/clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
@@ -74,8 +74,6 @@
#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
-static DEFINE_MUTEX(camera_lock);
-
/* per video frame buffer */
struct sh_mobile_ceu_buffer {
struct videobuf_buffer vb; /* v4l buffer must be first */
@@ -89,24 +87,27 @@ struct sh_mobile_ceu_dev {
unsigned int irq;
void __iomem *base;
+ struct clk *clk;
unsigned long video_limit;
/* lock used to protect videobuf */
spinlock_t lock;
struct list_head capture;
struct videobuf_buffer *active;
+ int is_interlace;
struct sh_mobile_ceu_info *pdata;
+
+ const struct soc_camera_data_format *camera_fmt;
};
static void ceu_write(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs, unsigned long data)
+ unsigned long reg_offs, u32 data)
{
iowrite32(data, priv->base + reg_offs);
}
-static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs)
+static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
{
return ioread32(priv->base + reg_offs);
}
@@ -154,21 +155,52 @@ static void free_buffer(struct videobuf_queue *vq,
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
+#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
+#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
+#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
+#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+
+
static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1);
- ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313);
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1);
-
- ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000);
+ struct soc_camera_device *icd = pcdev->icd;
+ dma_addr_t phys_addr_top, phys_addr_bottom;
- ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10);
+ /* The hardware is _very_ picky about this sequence. Especially
+ * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
+ * several not-so-well documented interrupt sources in CETCR.
+ */
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE);
+ ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC);
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE);
+ ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
+ ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
+
+ if (!pcdev->active)
+ return;
+
+ phys_addr_top = videobuf_to_dma_contig(pcdev->active);
+ ceu_write(pcdev, CDAYR, phys_addr_top);
+ if (pcdev->is_interlace) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBYR, phys_addr_bottom);
+ }
- if (pcdev->active) {
- pcdev->active->state = VIDEOBUF_ACTIVE;
- ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active));
- ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+ switch (icd->current_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ phys_addr_top += icd->width * icd->height;
+ ceu_write(pcdev, CDACR, phys_addr_top);
+ if (pcdev->is_interlace) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBCR, phys_addr_bottom);
+ }
}
+
+ pcdev->active->state = VIDEOBUF_ACTIVE;
+ ceu_write(pcdev, CAPSR, 0x1); /* start capture */
}
static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
@@ -290,14 +322,13 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
return IRQ_HANDLED;
}
+/* Called with .video_lock held */
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int ret = -EBUSY;
- mutex_lock(&camera_lock);
-
if (pcdev->icd)
goto err;
@@ -309,17 +340,18 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
if (ret)
goto err;
+ clk_enable(pcdev->clk);
+
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
while (ceu_read(pcdev, CSTSR) & 1)
msleep(1);
pcdev->icd = icd;
err:
- mutex_unlock(&camera_lock);
-
return ret;
}
+/* Called with .video_lock held */
static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -342,6 +374,8 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
}
spin_unlock_irqrestore(&pcdev->lock, flags);
+ clk_disable(pcdev->clk);
+
icd->ops->release(icd);
dev_info(&icd->dev,
@@ -356,8 +390,9 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret, buswidth, width, cfszr_width, cdwdr_width;
+ int ret, buswidth, width, height, cfszr_width, cdwdr_width;
unsigned long camera_flags, common_flags, value;
+ int yuv_mode, yuv_lineskip;
camera_flags = icd->ops->query_bus_param(icd);
common_flags = soc_camera_bus_param_compatible(camera_flags,
@@ -383,27 +418,71 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
ceu_write(pcdev, CRCNTR, 0);
ceu_write(pcdev, CRCMPR, 0);
- value = 0x00000010;
- value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0;
- value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0;
- value |= (buswidth == 16) ? (1 << 12) : 0;
+ value = 0x00000010; /* data fetch by default */
+ yuv_mode = yuv_lineskip = 0;
+
+ switch (icd->current_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
+ /* fall-through */
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ yuv_mode = 1;
+ switch (pcdev->camera_fmt->fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (icd->current_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ icd->current_fmt->fourcc == V4L2_PIX_FMT_NV61)
+ value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
+
+ value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+ value |= buswidth == 16 ? 1 << 12 : 0;
ceu_write(pcdev, CAMCR, value);
ceu_write(pcdev, CAPCR, 0x00300000);
- ceu_write(pcdev, CAIFR, 0);
+ ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0);
mdelay(1);
- width = icd->width * (icd->current_fmt->depth / 8);
- width = (buswidth == 16) ? width / 2 : width;
- cfszr_width = (buswidth == 8) ? width / 2 : width;
- cdwdr_width = (buswidth == 16) ? width * 2 : width;
+ if (yuv_mode) {
+ width = icd->width * 2;
+ width = buswidth == 16 ? width / 2 : width;
+ cfszr_width = cdwdr_width = icd->width;
+ } else {
+ width = icd->width * ((icd->current_fmt->depth + 7) >> 3);
+ width = buswidth == 16 ? width / 2 : width;
+ cfszr_width = buswidth == 8 ? width / 2 : width;
+ cdwdr_width = buswidth == 16 ? width * 2 : width;
+ }
+
+ height = icd->height;
+ if (pcdev->is_interlace) {
+ height /= 2;
+ cdwdr_width *= 2;
+ }
ceu_write(pcdev, CAMOR, 0);
- ceu_write(pcdev, CAPWR, (icd->height << 16) | width);
- ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */
- ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width);
- ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */
+ ceu_write(pcdev, CAPWR, (height << 16) | width);
+ ceu_write(pcdev, CFLCR, 0); /* no scaling */
+ ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width);
+ ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */
/* A few words about byte order (observed in Big Endian mode)
*
@@ -417,19 +496,20 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
* using 7 we swap the data bytes to match the incoming order:
* D0, D1, D2, D3, D4, D5, D6, D7
*/
- ceu_write(pcdev, CDOCR, 0x00000017);
+ value = 0x00000017;
+ if (yuv_lineskip)
+ value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
+
+ ceu_write(pcdev, CDOCR, value);
ceu_write(pcdev, CDWDR, cdwdr_width);
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
/* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
- /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */
-
return 0;
}
-static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
- __u32 pixfmt)
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
@@ -444,15 +524,123 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
return 0;
}
-static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static const struct soc_camera_data_format sh_mobile_ceu_formats[] = {
+ {
+ .name = "NV12",
+ .depth = 12,
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV21",
+ .depth = 12,
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV16",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .name = "NV61",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int ret, k, n;
+ int formats = 0;
+
+ ret = sh_mobile_ceu_try_bus_param(icd);
+ if (ret < 0)
+ return 0;
+
+ switch (icd->formats[idx].fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ n = ARRAY_SIZE(sh_mobile_ceu_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+ xlate->host_fmt = &sh_mobile_ceu_formats[k];
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev, "Providing format %s using %s\n",
+ sh_mobile_ceu_formats[k].name,
+ icd->formats[idx].name);
+ }
+ default:
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = icd->formats + idx;
+ xlate->cam_fmt = icd->formats + idx;
+ xlate->buswidth = icd->formats[idx].depth;
+ xlate++;
+ dev_dbg(&ici->dev,
+ "Providing format %s in pass-through mode\n",
+ icd->formats[idx].name);
+ }
+ }
+
+ return formats;
+}
+
+static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
- return icd->ops->set_fmt_cap(icd, pixfmt, rect);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ switch (pixfmt) {
+ case 0: /* Only geometry change */
+ ret = icd->ops->set_fmt(icd, pixfmt, rect);
+ break;
+ default:
+ ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect);
+ }
+
+ if (pixfmt && !ret) {
+ icd->buswidth = xlate->buswidth;
+ icd->current_fmt = xlate->host_fmt;
+ pcdev->camera_fmt = xlate->cam_fmt;
+ }
+
+ return ret;
}
-static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ __u32 pixfmt = f->fmt.pix.pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
/* FIXME: calculate using depth and bus width */
if (f->fmt.pix.height < 4)
@@ -466,8 +654,31 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd,
f->fmt.pix.width &= ~0x01;
f->fmt.pix.height &= ~0x03;
+ f->fmt.pix.bytesperline = f->fmt.pix.width *
+ DIV_ROUND_UP(xlate->host_fmt->depth, 8);
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
/* limit to sensor capabilities */
- return icd->ops->try_fmt_cap(icd, f);
+ ret = icd->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_INTERLACED:
+ pcdev->is_interlace = 1;
+ break;
+ case V4L2_FIELD_ANY:
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+ pcdev->is_interlace = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
}
static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf,
@@ -526,7 +737,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
&sh_mobile_ceu_videobuf_ops,
&ici->dev, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
+ V4L2_FIELD_ANY,
sizeof(struct sh_mobile_ceu_buffer),
icd);
}
@@ -535,12 +746,12 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
.remove = sh_mobile_ceu_remove_device,
- .set_fmt_cap = sh_mobile_ceu_set_fmt_cap,
- .try_fmt_cap = sh_mobile_ceu_try_fmt_cap,
+ .get_formats = sh_mobile_ceu_get_formats,
+ .set_fmt = sh_mobile_ceu_set_fmt,
+ .try_fmt = sh_mobile_ceu_try_fmt,
.reqbufs = sh_mobile_ceu_reqbufs,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
- .try_bus_param = sh_mobile_ceu_try_bus_param,
.set_bus_param = sh_mobile_ceu_set_bus_param,
.init_videobuf = sh_mobile_ceu_init_videobuf,
};
@@ -550,6 +761,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
struct sh_mobile_ceu_dev *pcdev;
struct resource *res;
void __iomem *base;
+ char clk_name[8];
unsigned int irq;
int err = 0;
@@ -609,24 +821,34 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
/* request irq */
err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
- pdev->dev.bus_id, pcdev);
+ dev_name(&pdev->dev), pcdev);
if (err) {
dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
goto exit_release_mem;
}
+ snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id);
+ pcdev->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(pcdev->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ err = PTR_ERR(pcdev->clk);
+ goto exit_free_irq;
+ }
+
pcdev->ici.priv = pcdev;
pcdev->ici.dev.parent = &pdev->dev;
pcdev->ici.nr = pdev->id;
- pcdev->ici.drv_name = pdev->dev.bus_id,
- pcdev->ici.ops = &sh_mobile_ceu_host_ops,
+ pcdev->ici.drv_name = dev_name(&pdev->dev);
+ pcdev->ici.ops = &sh_mobile_ceu_host_ops;
err = soc_camera_host_register(&pcdev->ici);
if (err)
- goto exit_free_irq;
+ goto exit_free_clk;
return 0;
+exit_free_clk:
+ clk_put(pcdev->clk);
exit_free_irq:
free_irq(pcdev->irq, pcdev);
exit_release_mem:
@@ -645,6 +867,7 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev);
soc_camera_host_unregister(&pcdev->ici);
+ clk_put(pcdev->clk);
free_irq(pcdev->irq, pcdev);
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
dma_release_declared_memory(&pdev->dev);
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index fcd2b62f92c4..01a8efb8deb1 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -2162,7 +2162,7 @@ sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index e23734f6d6e2..8cb3457e778d 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -55,7 +55,9 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), },
+#endif
/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */
{ SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), },
@@ -91,10 +93,14 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), },
/* SN9C105 */
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), },
+#endif
{ SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), },
{ SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), },
@@ -113,7 +119,9 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), },
/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */
+#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
+#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
{ SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), },
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 66ebe5956a87..90077cb4fe66 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -33,10 +33,9 @@
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
static DEFINE_MUTEX(list_lock);
-static DEFINE_MUTEX(video_lock);
-const static struct soc_camera_data_format*
-format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
+const struct soc_camera_data_format *soc_camera_format_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
{
unsigned int i;
@@ -45,67 +44,87 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
return icd->formats + i;
return NULL;
}
+EXPORT_SYMBOL(soc_camera_format_by_fourcc);
-static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
{
- struct soc_camera_file *icf = file->private_data;
- struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
- enum v4l2_field field;
- const struct soc_camera_data_format *fmt;
- int ret;
+ unsigned int i;
- WARN_ON(priv != file->private_data);
+ for (i = 0; i < icd->num_user_formats; i++)
+ if (icd->user_formats[i].host_fmt->fourcc == fourcc)
+ return icd->user_formats + i;
+ return NULL;
+}
+EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
- fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat);
- if (!fmt) {
- dev_dbg(&icd->dev, "invalid format 0x%08x\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
+/**
+ * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * @icl: camera platform parameters
+ * @flags: flags to be inverted according to platform configuration
+ * @return: resulting flags
+ */
+unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
+ unsigned long flags)
+{
+ unsigned long f;
- dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc);
+ /* If only one of the two polarities is supported, switch to the opposite */
+ if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
+ f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
+ if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW)
+ flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW;
+ }
- field = f->fmt.pix.field;
+ if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
+ f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
+ if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW)
+ flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW;
+ }
- if (field == V4L2_FIELD_ANY) {
- field = V4L2_FIELD_NONE;
- } else if (V4L2_FIELD_NONE != field) {
- dev_err(&icd->dev, "Field type invalid.\n");
- return -EINVAL;
+ if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
+ f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
+ if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING)
+ flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING;
}
- /* test physical bus parameters */
- ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat);
- if (ret)
- return ret;
+ return flags;
+}
+EXPORT_SYMBOL(soc_camera_apply_sensor_flags);
- /* limit format to hardware capabilities */
- ret = ici->ops->try_fmt_cap(icd, f);
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- /* calculate missing fields */
- f->fmt.pix.field = field;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ WARN_ON(priv != file->private_data);
- return ret;
+ /* limit format to hardware capabilities */
+ return ici->ops->try_fmt(icd, f);
}
static int soc_camera_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ int ret = 0;
+
if (inp->index != 0)
return -EINVAL;
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
- strcpy(inp->name, "Camera");
+ if (icd->ops->enum_input)
+ ret = icd->ops->enum_input(icd, inp);
+ else {
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Camera");
+ }
- return 0;
+ return ret;
}
static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
@@ -125,7 +144,14 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
{
- return 0;
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+ int ret = 0;
+
+ if (icd->ops->set_std)
+ ret = icd->ops->set_std(icd, a);
+
+ return ret;
}
static int soc_camera_reqbufs(struct file *file, void *priv,
@@ -134,8 +160,7 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
int ret;
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -178,6 +203,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK);
}
+static int soc_camera_init_user_formats(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int i, fmts = 0;
+
+ if (!ici->ops->get_formats)
+ /*
+ * Fallback mode - the host will have to serve all
+ * sensor-provided formats one-to-one to the user
+ */
+ fmts = icd->num_formats;
+ else
+ /*
+ * First pass - only count formats this host-sensor
+ * configuration can provide
+ */
+ for (i = 0; i < icd->num_formats; i++)
+ fmts += ici->ops->get_formats(icd, i, NULL);
+
+ if (!fmts)
+ return -ENXIO;
+
+ icd->user_formats =
+ vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
+ if (!icd->user_formats)
+ return -ENOMEM;
+
+ icd->num_user_formats = fmts;
+ fmts = 0;
+
+ dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts);
+
+ /* Second pass - actually fill data formats */
+ for (i = 0; i < icd->num_formats; i++)
+ if (!ici->ops->get_formats) {
+ icd->user_formats[i].host_fmt = icd->formats + i;
+ icd->user_formats[i].cam_fmt = icd->formats + i;
+ icd->user_formats[i].buswidth = icd->formats[i].depth;
+ } else {
+ fmts += ici->ops->get_formats(icd, i,
+ &icd->user_formats[fmts]);
+ }
+
+ icd->current_fmt = icd->user_formats[0].host_fmt;
+
+ return 0;
+}
+
+static void soc_camera_free_user_formats(struct soc_camera_device *icd)
+{
+ vfree(icd->user_formats);
+}
+
static int soc_camera_open(struct inode *inode, struct file *file)
{
struct video_device *vdev;
@@ -190,8 +268,10 @@ static int soc_camera_open(struct inode *inode, struct file *file)
if (!icf)
return -ENOMEM;
- /* Protect against icd->remove() until we module_get() both drivers. */
- mutex_lock(&video_lock);
+ /*
+ * It is safe to dereference these pointers now as long as a user has
+ * the video device open - we are protected by the held cdev reference.
+ */
vdev = video_devdata(file);
icd = container_of(vdev->parent, struct soc_camera_device, dev);
@@ -209,20 +289,25 @@ static int soc_camera_open(struct inode *inode, struct file *file)
goto emgi;
}
+ /* Protect against icd->remove() until we module_get() both drivers. */
+ mutex_lock(&icd->video_lock);
+
icf->icd = icd;
icd->use_count++;
/* Now we really have to activate the camera */
if (icd->use_count == 1) {
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
ret = ici->ops->add(icd);
if (ret < 0) {
dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
- icd->use_count--;
goto eiciadd;
}
}
- mutex_unlock(&video_lock);
+ mutex_unlock(&icd->video_lock);
file->private_data = icf;
dev_dbg(&icd->dev, "camera device open\n");
@@ -231,13 +316,16 @@ static int soc_camera_open(struct inode *inode, struct file *file)
return 0;
- /* All errors are entered with the video_lock held */
+ /* First two errors are entered with the .video_lock held */
eiciadd:
+ soc_camera_free_user_formats(icd);
+eiufmt:
+ icd->use_count--;
+ mutex_unlock(&icd->video_lock);
module_put(ici->ops->owner);
emgi:
module_put(icd->ops->owner);
emgd:
- mutex_unlock(&video_lock);
vfree(icf);
return ret;
}
@@ -249,13 +337,16 @@ static int soc_camera_close(struct inode *inode, struct file *file)
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct video_device *vdev = icd->vdev;
- mutex_lock(&video_lock);
+ mutex_lock(&icd->video_lock);
icd->use_count--;
- if (!icd->use_count)
+ if (!icd->use_count) {
ici->ops->remove(icd);
+ soc_camera_free_user_formats(icd);
+ }
+ mutex_unlock(&icd->video_lock);
+
module_put(icd->ops->owner);
module_put(ici->ops->owner);
- mutex_unlock(&video_lock);
vfree(icf);
@@ -265,7 +356,7 @@ static int soc_camera_close(struct inode *inode, struct file *file)
}
static ssize_t soc_camera_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
@@ -299,8 +390,7 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
if (list_empty(&icf->vb_vidq.stream)) {
dev_err(&icd->dev, "Trying to poll with no queued buffers!\n");
@@ -310,7 +400,6 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
return ici->ops->poll(file, pt);
}
-
static struct file_operations soc_camera_fops = {
.owner = THIS_MODULE,
.open = soc_camera_open,
@@ -322,44 +411,50 @@ static struct file_operations soc_camera_fops = {
.llseek = no_llseek,
};
-
static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ __u32 pixfmt = pix->pixelformat;
int ret;
struct v4l2_rect rect;
- const static struct soc_camera_data_format *data_fmt;
WARN_ON(priv != file->private_data);
- data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat);
- if (!data_fmt)
- return -EINVAL;
-
- /* buswidth may be further adjusted by the ici */
- icd->buswidth = data_fmt->depth;
-
- ret = soc_camera_try_fmt_vid_cap(file, icf, f);
+ ret = soc_camera_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+ if (videobuf_queue_is_busy(&icf->vb_vidq)) {
+ dev_err(&icd->dev, "S_FMT denied: queue busy\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
rect.left = icd->x_current;
rect.top = icd->y_current;
- rect.width = f->fmt.pix.width;
- rect.height = f->fmt.pix.height;
- ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect);
- if (ret < 0)
- return ret;
+ rect.width = pix->width;
+ rect.height = pix->height;
+ ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect);
+ if (ret < 0) {
+ goto unlock;
+ } else if (!icd->current_fmt ||
+ icd->current_fmt->fourcc != pixfmt) {
+ dev_err(&ici->dev,
+ "Host driver hasn't set up current format correctly!\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
- icd->current_fmt = data_fmt;
icd->width = rect.width;
icd->height = rect.height;
- icf->vb_vidq.field = f->fmt.pix.field;
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+ icf->vb_vidq.field = pix->field;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
f->type);
@@ -367,11 +462,16 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
icd->width, icd->height);
/* set physical bus parameters */
- return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat);
+ ret = ici->ops->set_bus_param(icd, pixfmt);
+
+unlock:
+ mutex_unlock(&icf->vb_vidq.vb_lock);
+
+ return ret;
}
static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+ struct v4l2_fmtdesc *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
@@ -379,10 +479,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
- if (f->index >= icd->num_formats)
+ if (f->index >= icd->num_user_formats)
return -EINVAL;
- format = &icd->formats[f->index];
+ format = icd->user_formats[f->index].host_fmt;
strlcpy(f->description, format->name, sizeof(f->description));
f->pixelformat = format->fourcc;
@@ -390,21 +490,21 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
}
static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
WARN_ON(priv != file->private_data);
- f->fmt.pix.width = icd->width;
- f->fmt.pix.height = icd->height;
- f->fmt.pix.field = icf->vb_vidq.field;
- f->fmt.pix.pixelformat = icd->current_fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * icd->current_fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
+ pix->width = icd->width;
+ pix->height = icd->height;
+ pix->field = icf->vb_vidq.field;
+ pix->pixelformat = icd->current_fmt->fourcc;
+ pix->bytesperline = pix->width *
+ DIV_ROUND_UP(icd->current_fmt->depth, 8);
+ pix->sizeimage = pix->height * pix->bytesperline;
dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n",
icd->current_fmt->fourcc);
return 0;
@@ -415,8 +515,7 @@ static int soc_camera_querycap(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
WARN_ON(priv != file->private_data);
@@ -429,6 +528,7 @@ static int soc_camera_streamon(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ int ret;
WARN_ON(priv != file->private_data);
@@ -437,10 +537,16 @@ static int soc_camera_streamon(struct file *file, void *priv,
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ mutex_lock(&icd->video_lock);
+
icd->ops->start_capture(icd);
/* This calls buf_queue from host driver's videobuf_queue_ops */
- return videobuf_streamon(&icf->vb_vidq);
+ ret = videobuf_streamon(&icf->vb_vidq);
+
+ mutex_unlock(&icd->video_lock);
+
+ return ret;
}
static int soc_camera_streamoff(struct file *file, void *priv,
@@ -456,12 +562,16 @@ static int soc_camera_streamoff(struct file *file, void *priv,
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ mutex_lock(&icd->video_lock);
+
/* This calls buf_release from host driver's videobuf_queue_ops for all
* remaining buffers. When the last buffer is freed, stop capture */
videobuf_streamoff(&icf->vb_vidq);
icd->ops->stop_capture(icd);
+ mutex_unlock(&icd->video_lock);
+
return 0;
}
@@ -567,14 +677,16 @@ static int soc_camera_s_crop(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int ret;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- ret = ici->ops->set_fmt_cap(icd, 0, &a->c);
+ /* Cropping is allowed during a running capture, guard consistency */
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+ ret = ici->ops->set_fmt(icd, 0, &a->c);
if (!ret) {
icd->width = a->c.width;
icd->height = a->c.height;
@@ -582,6 +694,8 @@ static int soc_camera_s_crop(struct file *file, void *fh,
icd->y_current = a->c.top;
}
+ mutex_unlock(&icf->vb_vidq.vb_lock);
+
return ret;
}
@@ -692,18 +806,35 @@ static int scan_add_device(struct soc_camera_device *icd)
static int soc_camera_probe(struct device *dev)
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int ret;
- if (!icd->ops->probe)
- return -ENODEV;
+ /*
+ * Possible race scenario:
+ * modprobe <camera-host-driver> triggers __func__
+ * at this moment respective <camera-sensor-driver> gets rmmod'ed
+ * to protect take module references.
+ */
+
+ if (!try_module_get(icd->ops->owner)) {
+ dev_err(&icd->dev, "Couldn't lock sensor driver.\n");
+ ret = -EINVAL;
+ goto emgd;
+ }
+
+ if (!try_module_get(ici->ops->owner)) {
+ dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
+ ret = -EINVAL;
+ goto emgi;
+ }
+
+ mutex_lock(&icd->video_lock);
/* We only call ->add() here to activate and probe the camera.
* We shall ->remove() and deactivate it immediately afterwards. */
ret = ici->ops->add(icd);
if (ret < 0)
- return ret;
+ goto eiadd;
ret = icd->ops->probe(icd);
if (ret >= 0) {
@@ -717,6 +848,12 @@ static int soc_camera_probe(struct device *dev)
}
ici->ops->remove(icd);
+eiadd:
+ mutex_unlock(&icd->video_lock);
+ module_put(ici->ops->owner);
+emgi:
+ module_put(icd->ops->owner);
+emgd:
return ret;
}
@@ -779,11 +916,20 @@ int soc_camera_host_register(struct soc_camera_host *ici)
int ret;
struct soc_camera_host *ix;
- if (!ici->ops->init_videobuf || !ici->ops->add || !ici->ops->remove)
+ if (!ici || !ici->ops ||
+ !ici->ops->try_fmt ||
+ !ici->ops->set_fmt ||
+ !ici->ops->set_bus_param ||
+ !ici->ops->querycap ||
+ !ici->ops->init_videobuf ||
+ !ici->ops->reqbufs ||
+ !ici->ops->add ||
+ !ici->ops->remove ||
+ !ici->ops->poll)
return -EINVAL;
/* Number might be equal to the platform device ID */
- sprintf(ici->dev.bus_id, "camera_host%d", ici->nr);
+ dev_set_name(&ici->dev, "camera_host%d", ici->nr);
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
@@ -847,7 +993,16 @@ int soc_camera_device_register(struct soc_camera_device *icd)
struct soc_camera_device *ix;
int num = -1, i;
- if (!icd)
+ if (!icd || !icd->ops ||
+ !icd->ops->probe ||
+ !icd->ops->init ||
+ !icd->ops->release ||
+ !icd->ops->start_capture ||
+ !icd->ops->stop_capture ||
+ !icd->ops->set_fmt ||
+ !icd->ops->try_fmt ||
+ !icd->ops->query_bus_param ||
+ !icd->ops->set_bus_param)
return -EINVAL;
for (i = 0; i < 256 && num < 0; i++) {
@@ -867,10 +1022,12 @@ int soc_camera_device_register(struct soc_camera_device *icd)
icd->devnum = num;
icd->dev.bus = &soc_camera_bus_type;
- snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id),
- "%u-%u", icd->iface, icd->devnum);
+ dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum);
- icd->dev.release = dummy_release;
+ icd->dev.release = dummy_release;
+ icd->use_count = 0;
+ icd->host_priv = NULL;
+ mutex_init(&icd->video_lock);
return scan_add_device(icd);
}
@@ -917,6 +1074,10 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
#endif
};
+/*
+ * Usually called from the struct soc_camera_ops .probe() method, i.e., from
+ * soc_camera_probe() above with .video_lock held
+ */
int soc_camera_video_start(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
@@ -932,7 +1093,7 @@ int soc_camera_video_start(struct soc_camera_device *icd)
dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev);
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- /* Maybe better &ici->dev */
+
vdev->parent = &icd->dev;
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;
@@ -941,8 +1102,6 @@ int soc_camera_video_start(struct soc_camera_device *icd)
vdev->minor = -1;
vdev->tvnorms = V4L2_STD_UNKNOWN,
- icd->current_fmt = &icd->formats[0];
-
err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor);
if (err < 0) {
dev_err(vdev->parent, "video_register_device failed\n");
@@ -968,10 +1127,10 @@ void soc_camera_video_stop(struct soc_camera_device *icd)
if (!icd->dev.parent || !vdev)
return;
- mutex_lock(&video_lock);
+ mutex_lock(&icd->video_lock);
video_unregister_device(vdev);
icd->vdev = NULL;
- mutex_unlock(&video_lock);
+ mutex_unlock(&icd->video_lock);
}
EXPORT_SYMBOL(soc_camera_video_stop);
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
index bb7a9d480e8f..013ab06e3180 100644
--- a/drivers/media/video/soc_camera_platform.c
+++ b/drivers/media/video/soc_camera_platform.c
@@ -79,19 +79,20 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
return p->bus_param;
}
-static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd,
- __u32 pixfmt, struct v4l2_rect *rect)
+static int soc_camera_platform_set_fmt(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
{
return 0;
}
-static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int soc_camera_platform_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
{
struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
- f->fmt.pix.width = p->format.width;
- f->fmt.pix.height = p->format.height;
+ pix->width = p->format.width;
+ pix->height = p->format.height;
return 0;
}
@@ -124,8 +125,8 @@ static struct soc_camera_ops soc_camera_platform_ops = {
.release = soc_camera_platform_release,
.start_capture = soc_camera_platform_start_capture,
.stop_capture = soc_camera_platform_stop_capture,
- .set_fmt_cap = soc_camera_platform_set_fmt_cap,
- .try_fmt_cap = soc_camera_platform_try_fmt_cap,
+ .set_fmt = soc_camera_platform_set_fmt,
+ .try_fmt = soc_camera_platform_try_fmt,
.set_bus_param = soc_camera_platform_set_bus_param,
.query_bus_param = soc_camera_platform_query_bus_param,
};
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index e9eb6d754d5c..f9516d0f3c11 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -1262,6 +1262,25 @@ static int stk_vidioc_g_parm(struct file *filp,
return 0;
}
+static int stk_vidioc_enum_framesizes(struct file *filp,
+ void *priv, struct v4l2_frmsizeenum *frms)
+{
+ if (frms->index >= ARRAY_SIZE(stk_sizes))
+ return -EINVAL;
+ switch (frms->pixel_format) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_SBGGR8:
+ frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ frms->discrete.width = stk_sizes[frms->index].w;
+ frms->discrete.height = stk_sizes[frms->index].h;
+ return 0;
+ default: return -EINVAL;
+ }
+}
+
static struct file_operations v4l_stk_fops = {
.owner = THIS_MODULE,
.open = v4l_stk_open,
@@ -1296,6 +1315,7 @@ static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = {
.vidioc_g_ctrl = stk_vidioc_g_ctrl,
.vidioc_s_ctrl = stk_vidioc_s_ctrl,
.vidioc_g_parm = stk_vidioc_g_parm,
+ .vidioc_enum_framesizes = stk_vidioc_enum_framesizes,
};
static void stk_v4l_dev_release(struct video_device *vd)
@@ -1376,12 +1396,9 @@ static int stk_camera_probe(struct usb_interface *interface,
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->isoc_ep
- && ((endpoint->bEndpointAddress
- & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
- && ((endpoint->bmAttributes
- & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) {
+ && usb_endpoint_is_isoc_in(endpoint)) {
/* we found an isoc in endpoint */
- dev->isoc_ep = (endpoint->bEndpointAddress & 0xF);
+ dev->isoc_ep = usb_endpoint_num(endpoint);
break;
}
}
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index 328c41b1517d..42acc92c182d 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -1132,8 +1132,7 @@ static int stv_close (struct inode *inode, struct file *file)
return 0;
}
-static int stv680_do_ioctl (struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int stv680_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = file->private_data;
struct usb_stv *stv680 = video_get_drvdata(vdev);
@@ -1303,7 +1302,7 @@ static int stv680_do_ioctl (struct inode *inode, struct file *file,
static int stv680_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl);
+ return video_usercopy(file, cmd, arg, stv680_do_ioctl);
}
static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index 4963d4264880..0c020585fffb 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -47,9 +47,10 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/i2c-addr.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#ifndef VIDEO_AUDIO_BALANCE
# define VIDEO_AUDIO_BALANCE 32
@@ -79,6 +80,7 @@ I2C_CLIENT_INSMOD;
/* Structure of address and subaddresses for the tda7432 */
struct tda7432 {
+ struct v4l2_subdev sd;
int addr;
int input;
int volume;
@@ -86,10 +88,12 @@ struct tda7432 {
int bass, treble;
int lf, lr, rf, rr;
int loud;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+
+static inline struct tda7432 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda7432, sd);
+}
/* The TDA7432 is made by STS-Thompson
* http://www.st.com
@@ -224,32 +228,33 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda7432_write(struct i2c_client *client, int subaddr, int val)
+static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- v4l_dbg(2, debug,client,"In tda7432_write\n");
- v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_write\n");
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- v4l_err(client,"I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
return 0;
}
-/* I don't think we ever actually _read_ the chip... */
-
-static int tda7432_set(struct i2c_client *client)
+static int tda7432_set(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tda7432 *t = to_state(sd);
unsigned char buf[16];
- v4l_dbg(2, debug,client,"In tda7432_set\n");
- v4l_dbg(1, debug,client,
+ v4l2_dbg(1, debug, sd,
"tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n",
- t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud);
+ t->input, t->volume, t->bass, t->treble, t->lf, t->lr,
+ t->rf, t->rr, t->loud);
buf[0] = TDA7432_IN;
buf[1] = t->input;
buf[2] = t->volume;
@@ -260,18 +265,19 @@ static int tda7432_set(struct i2c_client *client)
buf[7] = t->rf;
buf[8] = t->rr;
buf[9] = t->loud;
- if (10 != i2c_master_send(client,buf,10)) {
- v4l_err(client,"I/O error, trying tda7432_set\n");
+ if (10 != i2c_master_send(client, buf, 10)) {
+ v4l2_err(sd, "I/O error, trying tda7432_set\n");
return -1;
}
return 0;
}
-static void do_tda7432_init(struct i2c_client *client)
+static void do_tda7432_init(struct v4l2_subdev *sd)
{
- struct tda7432 *t = i2c_get_clientdata(client);
- v4l_dbg(2, debug,client,"In tda7432_init\n");
+ struct tda7432 *t = to_state(sd);
+
+ v4l2_dbg(2, debug, sd, "In tda7432_init\n");
t->input = TDA7432_STEREO_IN | /* Main (stereo) input */
TDA7432_BASS_SYM | /* Symmetric bass cut */
@@ -288,57 +294,12 @@ static void do_tda7432_init(struct i2c_client *client)
t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */
t->loud = loudness; /* insmod parameter */
- tda7432_set(client);
+ tda7432_set(sd);
}
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind)
+static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t;
- struct i2c_client *client;
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- do_tda7432_init(client);
- i2c_attach_client(client);
-
- v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name);
- return 0;
-}
-
-static int tda7432_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda7432_attach);
- return 0;
-}
-
-static int tda7432_detach(struct i2c_client *client)
-{
- struct tda7432 *t = i2c_get_clientdata(client);
-
- do_tda7432_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
-}
-
-static int tda7432_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
-{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -382,10 +343,9 @@ static int tda7432_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda7432_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda7432 *t = i2c_get_clientdata(client);
+ struct tda7432 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -400,7 +360,7 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (loudness) /* Turn on the loudness bit */
t->volume |= TDA7432_LD_ON;
- tda7432_write(client,TDA7432_VL, t->volume);
+ tda7432_write(sd, TDA7432_VL, t->volume);
return 0;
case V4L2_CID_AUDIO_BALANCE:
if (ctrl->value < 32768) {
@@ -428,14 +388,14 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if(t->bass>= 0x8)
t->bass = (~t->bass & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
case V4L2_CID_AUDIO_TREBLE:
t->treble= ctrl->value >> 12;
if(t->treble>= 0x8)
t->treble = (~t->treble & 0xf) + 0x8 ;
- tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble );
+ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble);
return 0;
default:
return -EINVAL;
@@ -445,92 +405,102 @@ static int tda7432_set_ctrl(struct i2c_client *client,
if (t->muted)
{
/* Mute & update balance*/
- tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE);
- tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE);
+ tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE);
} else {
- tda7432_write(client,TDA7432_LF, t->lf);
- tda7432_write(client,TDA7432_LR, t->lr);
- tda7432_write(client,TDA7432_RF, t->rf);
- tda7432_write(client,TDA7432_RR, t->rr);
+ tda7432_write(sd, TDA7432_LF, t->lf);
+ tda7432_write(sd, TDA7432_LR, t->lr);
+ tda7432_write(sd, TDA7432_RF, t->rf);
+ tda7432_write(sd, TDA7432_RR, t->rr);
}
return 0;
}
-static int tda7432_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- v4l_dbg(2, debug,client,"In tda7432_command\n");
- if (debug>1)
- v4l_i2c_print_ioctl(client,cmd);
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda7432_set_ctrl(client, arg);
-
- case VIDIOC_G_CTRL:
- return tda7432_get_ctrl(client, arg);
-
- } /* end of (cmd) switch */
+ return -EINVAL;
+}
- return 0;
+static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda7432",
- },
- .id = I2C_DRIVERID_TDA7432,
- .attach_adapter = tda7432_probe,
- .detach_client = tda7432_detach,
- .command = tda7432_command,
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda7432_core_ops = {
+ .queryctrl = tda7432_queryctrl,
+ .g_ctrl = tda7432_g_ctrl,
+ .s_ctrl = tda7432_s_ctrl,
};
-static struct i2c_client client_template =
-{
- .name = "tda7432",
- .driver = &driver,
+static const struct v4l2_subdev_ops tda7432_ops = {
+ .core = &tda7432_core_ops,
};
-static int __init tda7432_init(void)
+/* ----------------------------------------------------------------------- */
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda7432_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- if ( (loudness < 0) || (loudness > 15) ) {
- printk(KERN_ERR "loudness parameter must be between 0 and 15\n");
- return -EINVAL;
+ struct tda7432 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda7432_ops);
+ if (loudness < 0 || loudness > 15) {
+ v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");
+ if (loudness < 0)
+ loudness = 0;
+ if (loudness > 15)
+ loudness = 15;
}
- return i2c_add_driver(&driver);
+ do_tda7432_init(sd);
+ return 0;
}
-static void __exit tda7432_fini(void)
+static int tda7432_remove(struct i2c_client *client)
{
- i2c_del_driver(&driver);
-}
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-module_init(tda7432_init);
-module_exit(tda7432_fini);
+ do_tda7432_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+static const struct i2c_device_id tda7432_id[] = {
+ { "tda7432", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda7432_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda7432",
+ .driverid = I2C_DRIVERID_TDA7432,
+ .command = tda7432_command,
+ .probe = tda7432_probe,
+ .remove = tda7432_remove,
+ .id_table = tda7432_id,
+};
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 1c391f0328fd..2644e0dc9251 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -29,7 +29,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tda9840.h"
@@ -62,85 +62,89 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static void tda9840_write(struct i2c_client *client, u8 reg, u8 val)
+static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
if (i2c_smbus_write_byte_data(client, reg, val))
- v4l_dbg(1, debug, client, "error writing %02x to %02x\n",
+ v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n",
val, reg);
}
-static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
{
- int byte = *(int *)arg;
-
- switch (cmd) {
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *t = arg;
- int byte;
+ int byte;
- if (t->index)
- return -EINVAL;
+ if (t->index)
+ return -EINVAL;
- switch (t->audmode) {
- case V4L2_TUNER_MODE_STEREO:
- byte = TDA9840_SET_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- byte = TDA9840_SET_BOTH;
- break;
- case V4L2_TUNER_MODE_LANG1:
- byte = TDA9840_SET_LANG1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- byte = TDA9840_SET_LANG2;
- break;
- default:
- byte = TDA9840_SET_MONO;
- break;
- }
- v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte);
- tda9840_write(client, SWITCH, byte);
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ byte = TDA9840_SET_STEREO;
break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ byte = TDA9840_SET_LANG1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_MONO;
+ break;
+ }
+ v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
+ tda9840_write(sd, SWITCH, byte);
+ return 0;
+}
+
+static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (1 != i2c_master_recv(client, &byte, 1)) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_master_recv() failed\n");
+ return -EIO;
+ }
+
+ if (byte & 0x80) {
+ v4l2_dbg(1, debug, sd,
+ "TDA9840_DETECT: register contents invalid\n");
+ return -EINVAL;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *t = arg;
- u8 byte;
+ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+ switch (byte & 0x60) {
+ case 0x00:
t->rxsubchans = V4L2_TUNER_SUB_MONO;
- if (1 != i2c_master_recv(client, &byte, 1)) {
- v4l_dbg(1, debug, client,
- "i2c_master_recv() failed\n");
- return -EIO;
- }
-
- if (byte & 0x80) {
- v4l_dbg(1, debug, client,
- "TDA9840_DETECT: register contents invalid\n");
- return -EINVAL;
- }
-
- v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte);
-
- switch (byte & 0x60) {
- case 0x00:
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
- case 0x20:
- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- break;
- case 0x40:
- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
- break;
- default: /* Incorrect detect */
- t->rxsubchans = V4L2_TUNER_MODE_MONO;
- break;
- }
+ break;
+ case 0x20:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x40:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ break;
+ default: /* Incorrect detect */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
break;
}
+ return 0;
+}
+static int tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ int byte;
+
+ switch (cmd) {
case TDA9840_LEVEL_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -20)
@@ -152,11 +156,12 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
byte += 0x8;
else
byte = -byte;
- tda9840_write(client, LEVEL_ADJUST, byte);
+ tda9840_write(sd, LEVEL_ADJUST, byte);
break;
case TDA9840_STEREO_ADJUST:
- v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte);
+ byte = *(int *)arg;
+ v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -24)
@@ -169,18 +174,41 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
else
byte = -byte;
- tda9840_write(client, STEREO_ADJUST, byte);
+ tda9840_write(sd, STEREO_ADJUST, byte);
break;
default:
return -ENOIOCTLCMD;
}
-
return 0;
}
+static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tda9840_core_ops = {
+ .ioctl = tda9840_ioctl,
+};
+
+static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
+ .s_tuner = tda9840_s_tuner,
+ .g_tuner = tda9840_g_tuner,
+};
+
+static const struct v4l2_subdev_ops tda9840_ops = {
+ .core = &tda9840_core_ops,
+ .tuner = &tda9840_tuner_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
static int tda9840_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int result;
int byte;
@@ -188,23 +216,38 @@ static int tda9840_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
+ return -EIO;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
+
/* set initial values for level & stereo - adjustment, mode */
byte = 0;
- result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte);
- result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte);
- tda9840_write(client, SWITCH, TDA9840_SET_STEREO);
+ result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte);
+ result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte);
+ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
if (result) {
- v4l_dbg(1, debug, client, "could not initialize tda9840\n");
+ v4l2_dbg(1, debug, sd, "could not initialize tda9840\n");
+ kfree(sd);
return -ENODEV;
}
return 0;
}
+static int tda9840_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
static int tda9840_legacy_probe(struct i2c_adapter *adapter)
{
/* Let's see whether this is a known adapter we can attach to.
@@ -222,6 +265,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TDA9840,
.command = tda9840_command,
.probe = tda9840_probe,
+ .remove = tda9840_remove,
.legacy_probe = tda9840_legacy_probe,
.id_table = tda9840_id,
};
diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
index 792f0b079909..56f0c0eb500f 100644
--- a/drivers/media/video/tda9875.c
+++ b/drivers/media/video/tda9875.c
@@ -25,11 +25,10 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
#include <linux/i2c.h>
-#include <linux/init.h>
-
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <media/i2c-addr.h>
static int debug; /* insmod parameter */
@@ -46,13 +45,15 @@ I2C_CLIENT_INSMOD;
/* This is a superset of the TDA9875 */
struct tda9875 {
+ struct v4l2_subdev sd;
int rvol, lvol;
int bass, treble;
- struct i2c_client c;
};
-static struct i2c_driver driver;
-static struct i2c_client client_template;
+static inline struct tda9875 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tda9875, sd);
+}
#define dprintk if (debug) printk
@@ -105,15 +106,16 @@ static struct i2c_client client_template;
/* Begin code */
-static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char val)
+static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
- dprintk("In tda9875_write\n");
- dprintk("Writing %d 0x%x\n", subaddr, val);
+
+ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(client,buffer,2)) {
- printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n",
+ if (2 != i2c_master_send(client, buffer, 2)) {
+ v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n",
subaddr, val);
return -1;
}
@@ -121,7 +123,7 @@ static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char v
}
-static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
+static int i2c_read_register(struct i2c_client *client, int addr, int reg)
{
unsigned char write[1];
unsigned char read[1];
@@ -129,150 +131,83 @@ static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg)
{ addr, 0, 1, write },
{ addr, I2C_M_RD, 1, read }
};
+
write[0] = reg;
- if (2 != i2c_transfer(adap,msgs,2)) {
- printk(KERN_WARNING "tda9875: I/O error (read2)\n");
+ if (2 != i2c_transfer(client->adapter, msgs, 2)) {
+ v4l_warn(client, "I/O error (read2)\n");
return -1;
}
- dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]);
+ v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]);
return read[0];
}
-static void tda9875_set(struct i2c_client *client)
+static void tda9875_set(struct v4l2_subdev *sd)
{
- struct tda9875 *tda = i2c_get_clientdata(client);
+ struct tda9875 *tda = to_state(sd);
unsigned char a;
- dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",
- tda->lvol,tda->rvol,tda->bass,tda->treble);
-
+ v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n",
+ tda->lvol, tda->rvol, tda->bass, tda->treble);
a = tda->lvol & 0xff;
- tda9875_write(client, TDA9875_MVL, a);
+ tda9875_write(sd, TDA9875_MVL, a);
a =tda->rvol & 0xff;
- tda9875_write(client, TDA9875_MVR, a);
+ tda9875_write(sd, TDA9875_MVR, a);
a =tda->bass & 0xff;
- tda9875_write(client, TDA9875_MBA, a);
+ tda9875_write(sd, TDA9875_MBA, a);
a =tda->treble & 0xff;
- tda9875_write(client, TDA9875_MTR, a);
+ tda9875_write(sd, TDA9875_MTR, a);
}
-static void do_tda9875_init(struct i2c_client *client)
+static void do_tda9875_init(struct v4l2_subdev *sd)
{
- struct tda9875 *t = i2c_get_clientdata(client);
- dprintk("In tda9875_init\n");
- tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/
- tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/
- tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/
- tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/
- tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/
- tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/
- tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/
- tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/
- tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/
- tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/
- tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/
- tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/
- tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/
-
- tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Channel volume 1 mute*/
- tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Channel volume 2 mute */
- tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/
- tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/
- tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/
- tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */
- tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */
- tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */
- tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/
- tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/
- tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/
- tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/
- tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/
- tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/
- tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/
- tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/
-
- tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */
-
- t->lvol=t->rvol =0; /* 0dB */
- t->bass=0; /* 0dB */
- t->treble=0; /* 0dB */
- tda9875_set(client);
-
+ struct tda9875 *t = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "In tda9875_init\n");
+ tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
+ tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
+ tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
+ tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
+ tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
+ tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/
+ tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
+ tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
+ tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
+ tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
+
+ tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
+ tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
+ tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
+ tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
+ tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
+ tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
+ tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
+ tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
+ tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
+ tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
+ tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
+ tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
+ tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
+ tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
+ tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
+
+ tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */
+
+ t->lvol = t->rvol = 0; /* 0dB */
+ t->bass = 0; /* 0dB */
+ t->treble = 0; /* 0dB */
+ tda9875_set(sd);
}
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda9875_checkit(struct i2c_adapter *adap, int addr)
-{
- int dic,rev;
-
- dic=i2c_read_register(adap,addr,254);
- rev=i2c_read_register(adap,addr,255);
-
- if(dic==0 || dic==2) { // tda9875 and tda9875A
- printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n",
- dic==0?"":"A", rev,addr<<1);
- return 1;
- }
- printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev);
- return(0);
-}
-
-static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind)
+static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t;
- struct i2c_client *client;
- dprintk("In tda9875_attach\n");
-
- t = kzalloc(sizeof *t,GFP_KERNEL);
- if (!t)
- return -ENOMEM;
-
- client = &t->c;
- memcpy(client,&client_template,sizeof(struct i2c_client));
- client->adapter = adap;
- client->addr = addr;
- i2c_set_clientdata(client, t);
-
- if(!tda9875_checkit(adap,addr)) {
- kfree(t);
- return 1;
- }
-
- do_tda9875_init(client);
- printk(KERN_INFO "tda9875: init\n");
-
- i2c_attach_client(client);
- return 0;
-}
-
-static int tda9875_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, tda9875_attach);
- return 0;
-}
-
-static int tda9875_detach(struct i2c_client *client)
-{
- struct tda9875 *t = i2c_get_clientdata(client);
-
- do_tda9875_init(client);
- i2c_detach_client(client);
-
- kfree(t);
- return 0;
-}
-
-static int tda9875_get_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
-{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -304,10 +239,9 @@ static int tda9875_get_ctrl(struct i2c_client *client,
return -EINVAL;
}
-static int tda9875_set_ctrl(struct i2c_client *client,
- struct v4l2_control *ctrl)
+static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct tda9875 *t = i2c_get_clientdata(client);
+ struct tda9875 *t = to_state(sd);
int chvol=0, volume, balance, left, right;
switch (ctrl->id) {
@@ -371,85 +305,105 @@ static int tda9875_set_ctrl(struct i2c_client *client,
t->rvol = -84 & 0xff;
}
-//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble);
-
- tda9875_set(client);
-
+ tda9875_set(sd);
return 0;
}
-
-static int tda9875_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- dprintk("In tda9875_command...\n");
-
- switch (cmd) {
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- default:
- return -EINVAL;
- }
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
return v4l2_ctrl_query_fill_std(qc);
}
- case VIDIOC_S_CTRL:
- return tda9875_set_ctrl(client, arg);
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return tda9875_get_ctrl(client, arg);
+static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
- default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */
+/* ----------------------------------------------------------------------- */
- /* nothing */
- dprintk("Default\n");
+static const struct v4l2_subdev_core_ops tda9875_core_ops = {
+ .queryctrl = tda9875_queryctrl,
+ .g_ctrl = tda9875_g_ctrl,
+ .s_ctrl = tda9875_s_ctrl,
+};
- } /* end of (cmd) switch */
+static const struct v4l2_subdev_ops tda9875_ops = {
+ .core = &tda9875_core_ops,
+};
- return 0;
-}
+/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda9875",
- },
- .id = I2C_DRIVERID_TDA9875,
- .attach_adapter = tda9875_probe,
- .detach_client = tda9875_detach,
- .command = tda9875_command,
-};
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
-static struct i2c_client client_template =
+static int tda9875_checkit(struct i2c_client *client, int addr)
{
- .name = "tda9875",
- .driver = &driver,
-};
+ int dic, rev;
-static int __init tda9875_init(void)
-{
- return i2c_add_driver(&driver);
+ dic = i2c_read_register(client, addr, 254);
+ rev = i2c_read_register(client, addr, 255);
+
+ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
+ v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n",
+ dic == 0 ? "" : "A", rev, addr << 1);
+ return 1;
+ }
+ v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n",
+ addr << 1, dic, rev);
+ return 0;
}
-static void __exit tda9875_fini(void)
+static int tda9875_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- i2c_del_driver(&driver);
+ struct tda9875 *t;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ if (!tda9875_checkit(client, client->addr))
+ return -ENODEV;
+
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ sd = &t->sd;
+ v4l2_i2c_subdev_init(sd, client, &tda9875_ops);
+
+ do_tda9875_init(sd);
+ return 0;
}
-module_init(tda9875_init);
-module_exit(tda9875_fini);
+static int tda9875_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+ do_tda9875_init(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+static const struct i2c_device_id tda9875_id[] = {
+ { "tda9875", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda9875_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda9875",
+ .driverid = I2C_DRIVERID_TDA9875,
+ .command = tda9875_command,
+ .probe = tda9875_probe,
+ .remove = tda9875_remove,
+ .id_table = tda9875_id,
+};
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index cde092adbb5a..31dde86f2df4 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6415c.h"
@@ -122,31 +122,57 @@ static int switch_matrix(struct i2c_client *client, int i, int o)
return ret;
}
-static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6415C_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
- switch (cmd) {
- case TEA6415C_SWITCH:
- result = switch_matrix(client, v->in, v->out);
- break;
- default:
- return -ENOIOCTLCMD;
+ return switch_matrix(client, v->in, v->out);
}
- return result;
+ return -ENOIOCTLCMD;
}
+static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+ .ioctl = tea6415c_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6415c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
+
/* let's see whether this adapter can support what we need */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
return 0;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
+ return 0;
+}
+
+static int tea6415c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -168,6 +194,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6415C,
.command = tea6415c_command,
.probe = tea6415c_probe,
+ .remove = tea6415c_remove,
.legacy_probe = tea6415c_legacy_probe,
.id_table = tea6415c_id,
};
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index e50820969e64..38e519f04bde 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -31,7 +31,7 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6420.h"
@@ -90,26 +90,37 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
return 0;
}
-static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
{
- struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- int result = 0;
+ if (cmd == TEA6420_SWITCH) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
- switch (cmd) {
- case TEA6420_SWITCH:
- result = tea6420_switch(client, a->in, a->out, a->gain);
- break;
- default:
- return -ENOIOCTLCMD;
+ return tea6420_switch(client, a->in, a->out, a->gain);
}
+ return -ENOIOCTLCMD;
+}
- return result;
+static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6420_core_ops = {
+ .ioctl = tea6420_ioctl,
+};
+
+static const struct v4l2_subdev_ops tea6420_ops = {
+ .core = &tea6420_core_ops,
+};
+
/* this function is called by i2c_probe */
static int tea6420_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int err, i;
/* let's see whether this adapter can support what we need */
@@ -126,9 +137,22 @@ static int tea6420_probe(struct i2c_client *client,
}
if (err) {
v4l_dbg(1, debug, client, "could not initialize tea6420\n");
- kfree(client);
return -ENODEV;
}
+
+ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
+ return 0;
+}
+
+static int tea6420_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
return 0;
}
@@ -150,6 +174,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.driverid = I2C_DRIVERID_TEA6420,
.command = tea6420_command,
.probe = tea6420_probe,
+ .remove = tea6420_remove,
.legacy_probe = tea6420_legacy_probe,
.id_table = tea6420_id,
};
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index 281065b9dd2d..5c95ecd09dc2 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -30,7 +30,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv-legacy.h>
MODULE_DESCRIPTION("tlv320aic23b driver");
@@ -44,15 +44,22 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
struct tlv320aic23b_state {
+ struct v4l2_subdev sd;
u8 muted;
};
-static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
+static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tlv320aic23b_state, sd);
+}
+
+static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if ((reg < 0 || reg > 9) && (reg != 15)) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -60,61 +67,82 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int tlv320aic23b_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct tlv320aic23b_state *state = i2c_get_clientdata(client);
- struct v4l2_control *ctrl = arg;
- u32 *freq = arg;
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- switch (*freq) {
- case 32000: /* set sample rate to 32 kHz */
- tlv320aic23b_write(client, 8, 0x018);
- break;
- case 44100: /* set sample rate to 44.1 kHz */
- tlv320aic23b_write(client, 8, 0x022);
- break;
- case 48000: /* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
- break;
- default:
- return -EINVAL;
- }
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
+ switch (freq) {
+ case 32000: /* set sample rate to 32 kHz */
+ tlv320aic23b_write(sd, 8, 0x018);
break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- tlv320aic23b_write(client, 0, 0x180); /* mute both channels */
- /* set gain on both channels to +3.0 dB */
- if (!state->muted)
- tlv320aic23b_write(client, 0, 0x119);
+ case 44100: /* set sample rate to 44.1 kHz */
+ tlv320aic23b_write(sd, 8, 0x022);
break;
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %s\n",
- state->muted ? "muted" : "active");
+ case 48000: /* set sample rate to 48 kHz */
+ tlv320aic23b_write(sd, 8, 0x000);
break;
-
default:
return -EINVAL;
}
return 0;
}
+static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */
+ /* set gain on both channels to +3.0 dB */
+ if (!state->muted)
+ tlv320aic23b_write(sd, 0, 0x119);
+ return 0;
+}
+
+static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
+{
+ struct tlv320aic23b_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active");
+ return 0;
+}
+
+static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
+ .log_status = tlv320aic23b_log_status,
+ .g_ctrl = tlv320aic23b_g_ctrl,
+ .s_ctrl = tlv320aic23b_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = {
+ .s_clock_freq = tlv320aic23b_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops tlv320aic23b_ops = {
+ .core = &tlv320aic23b_core_ops,
+ .audio = &tlv320aic23b_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -128,6 +156,7 @@ static int tlv320aic23b_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tlv320aic23b_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -136,32 +165,36 @@ static int tlv320aic23b_probe(struct i2c_client *client,
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops);
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize tlv320aic23b */
/* RESET */
- tlv320aic23b_write(client, 15, 0x000);
+ tlv320aic23b_write(sd, 15, 0x000);
/* turn off DAC & mic input */
- tlv320aic23b_write(client, 6, 0x00A);
+ tlv320aic23b_write(sd, 6, 0x00A);
/* left-justified, 24-bit, master mode */
- tlv320aic23b_write(client, 7, 0x049);
+ tlv320aic23b_write(sd, 7, 0x049);
/* set gain on both channels to +3.0 dB */
- tlv320aic23b_write(client, 0, 0x119);
+ tlv320aic23b_write(sd, 0, 0x119);
/* set sample rate to 48 kHz */
- tlv320aic23b_write(client, 8, 0x000);
+ tlv320aic23b_write(sd, 8, 0x000);
/* activate digital interface */
- tlv320aic23b_write(client, 9, 0x001);
+ tlv320aic23b_write(sd, 9, 0x001);
return 0;
}
static int tlv320aic23b_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 4a7735c6c1a6..97d7509d212f 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -18,7 +18,7 @@
#include <linux/videodev.h>
#include <media/tuner.h>
#include <media/tuner-types.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "mt20xx.h"
@@ -78,6 +78,7 @@ struct tuner {
/* device */
struct dvb_frontend fe;
struct i2c_client *i2c;
+ struct v4l2_subdev sd;
struct list_head list;
unsigned int using_v4l2:1;
@@ -95,6 +96,11 @@ struct tuner {
const char *name;
};
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tuner, sd);
+}
+
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
@@ -213,7 +219,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
static void tuner_status(struct dvb_frontend *fe);
-static struct analog_demod_ops tuner_core_ops = {
+static struct analog_demod_ops tuner_analog_ops = {
.set_params = fe_set_params,
.standby = fe_standby,
.has_signal = fe_has_signal,
@@ -224,7 +230,7 @@ static struct analog_demod_ops tuner_core_ops = {
/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -259,7 +265,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -294,7 +300,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
static void set_freq(struct i2c_client *c, unsigned long freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
switch (t->mode) {
case V4L2_TUNER_RADIO:
@@ -347,7 +353,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
int (*tuner_callback) (void *dev, int component, int cmd, int arg))
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
unsigned char buffer[4];
@@ -470,7 +476,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->name = fe_tuner_ops->info.name;
t->fe.analog_demod_priv = t;
- memcpy(analog_ops, &tuner_core_ops,
+ memcpy(analog_ops, &tuner_analog_ops,
sizeof(struct analog_demod_ops));
} else {
@@ -515,7 +521,7 @@ attach_failed:
static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
(t->mode_mask & tun_setup->mode_mask))) ||
@@ -727,6 +733,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode,
t->mode = mode;
if (check_mode(t, cmd) == -EINVAL) {
+ tuner_dbg("Tuner doesn't support this mode. "
+ "Putting tuner to sleep\n");
t->mode = T_STANDBY;
if (analog_ops->standby)
analog_ops->standby(&t->fe);
@@ -748,43 +756,58 @@ static inline int check_v4l2(struct tuner *t)
return 0;
}
-static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type)
{
- struct tuner *t = i2c_get_clientdata(client);
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+ type->type,
+ type->addr,
+ type->mode_mask,
+ type->config);
+
+ set_addr(client, type);
+ return 0;
+}
+
+static int tuner_s_radio(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
+ == -EINVAL)
+ return 0;
+ if (t->radio_freq)
+ set_freq(client, t->radio_freq);
+ return 0;
+}
+
+static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
+{
+ struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (tuner_debug > 1) {
- v4l_i2c_print_ioctl(client,cmd);
- printk("\n");
- }
+ tuner_dbg("Putting tuner to sleep\n");
+
+ if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
+ return 0;
+ t->mode = T_STANDBY;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return 0;
+}
- switch (cmd) {
- /* --- configuration --- */
- case TUNER_SET_TYPE_ADDR:
- tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
- ((struct tuner_setup *)arg)->type,
- ((struct tuner_setup *)arg)->addr,
- ((struct tuner_setup *)arg)->mode_mask,
- ((struct tuner_setup *)arg)->config);
-
- set_addr(client, (struct tuner_setup *)arg);
- break;
- case AUDC_SET_RADIO:
- if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
- == -EINVAL)
- return 0;
- if (t->radio_freq)
- set_freq(client, t->radio_freq);
- break;
- case TUNER_SET_STANDBY:
- if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
- return 0;
- t->mode = T_STANDBY;
- if (analog_ops->standby)
- analog_ops->standby(&t->fe);
- break;
#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ switch (cmd) {
case VIDIOCSAUDIO:
if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL)
return 0;
@@ -897,149 +920,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
}
return 0;
}
+ }
+ return -ENOIOCTLCMD;
+}
#endif
- case TUNER_SET_CONFIG:
- {
- struct v4l2_priv_tun_config *cfg = arg;
- if (t->type != cfg->tuner)
- break;
+static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (analog_ops->set_config) {
- analog_ops->set_config(&t->fe, cfg->priv);
- break;
- }
+ if (t->type != cfg->tuner)
+ return 0;
- tuner_dbg("Tuner frontend module has no way to set config\n");
- break;
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ return 0;
}
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
- == -EINVAL)
- return 0;
+ tuner_dbg("Tuner frontend module has no way to set config\n");
+ return 0;
+}
- switch_v4l2();
+/* --- v4l ioctls --- */
+/* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- t->std = *id;
- tuner_fixup_std(t);
- if (t->tv_freq)
- set_freq(client, t->tv_freq);
- break;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
+ == -EINVAL)
+ return 0;
- if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
- == -EINVAL)
- return 0;
- switch_v4l2();
- set_freq(client,f->frequency);
+ switch_v4l2();
- break;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ t->std = std;
+ tuner_fixup_std(t);
+ if (t->tv_freq)
+ set_freq(client, t->tv_freq);
+ return 0;
+}
- if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
- return 0;
- switch_v4l2();
- f->type = t->mode;
- if (fe_tuner_ops->get_frequency) {
- u32 abs_freq;
-
- fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- (abs_freq * 2 + 125/2) / 125 :
- (abs_freq + 62500/2) / 62500;
- break;
- }
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- t->radio_freq : t->tv_freq;
- break;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
- return 0;
- switch_v4l2();
-
- tuner->type = t->mode;
- if (analog_ops->get_afc)
- tuner->afc = analog_ops->get_afc(&t->fe);
- if (t->mode == V4L2_TUNER_ANALOG_TV)
- tuner->capability |= V4L2_TUNER_CAP_NORM;
- if (t->mode != V4L2_TUNER_RADIO) {
- tuner->rangelow = tv_range[0] * 16;
- tuner->rangehigh = tv_range[1] * 16;
- break;
- }
+ if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY")
+ == -EINVAL)
+ return 0;
+ switch_v4l2();
+ set_freq(client, f->frequency);
- /* radio mode */
- tuner->rxsubchans =
- V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- if (fe_tuner_ops->get_status) {
- u32 tuner_status;
-
- fe_tuner_ops->get_status(&t->fe, &tuner_status);
- tuner->rxsubchans =
- (tuner_status & TUNER_STATUS_STEREO) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- } else {
- if (analog_ops->is_stereo) {
- tuner->rxsubchans =
- analog_ops->is_stereo(&t->fe) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- }
- }
- if (analog_ops->has_signal)
- tuner->signal = analog_ops->has_signal(&t->fe);
- tuner->capability |=
- V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- tuner->audmode = t->audmode;
- tuner->rangelow = radio_range[0] * 16000;
- tuner->rangehigh = radio_range[1] * 16000;
- break;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+ return 0;
+}
- if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
- return 0;
+static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tuner *t = to_tuner(sd);
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
- switch_v4l2();
+ if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
+ return 0;
+ switch_v4l2();
+ f->type = t->mode;
+ if (fe_tuner_ops->get_frequency) {
+ u32 abs_freq;
+
+ fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ (abs_freq * 2 + 125/2) / 125 :
+ (abs_freq + 62500/2) / 62500;
+ return 0;
+ }
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ t->radio_freq : t->tv_freq;
+ return 0;
+}
- /* do nothing unless we're a radio tuner */
- if (t->mode != V4L2_TUNER_RADIO)
- break;
- t->audmode = tuner->audmode;
- set_radio_freq(client, t->radio_freq);
- break;
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+ if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
+ return 0;
+ switch_v4l2();
+
+ vt->type = t->mode;
+ if (analog_ops->get_afc)
+ vt->afc = analog_ops->get_afc(&t->fe);
+ if (t->mode == V4L2_TUNER_ANALOG_TV)
+ vt->capability |= V4L2_TUNER_CAP_NORM;
+ if (t->mode != V4L2_TUNER_RADIO) {
+ vt->rangelow = tv_range[0] * 16;
+ vt->rangehigh = tv_range[1] * 16;
+ return 0;
+ }
+
+ /* radio mode */
+ vt->rxsubchans =
+ V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (fe_tuner_ops->get_status) {
+ u32 tuner_status;
+
+ fe_tuner_ops->get_status(&t->fe, &tuner_status);
+ vt->rxsubchans =
+ (tuner_status & TUNER_STATUS_STEREO) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
+ } else {
+ if (analog_ops->is_stereo) {
+ vt->rxsubchans =
+ analog_ops->is_stereo(&t->fe) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
}
- case VIDIOC_LOG_STATUS:
- if (analog_ops->tuner_status)
- analog_ops->tuner_status(&t->fe);
- break;
}
+ if (analog_ops->has_signal)
+ vt->signal = analog_ops->has_signal(&t->fe);
+ vt->capability |=
+ V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ vt->audmode = t->audmode;
+ vt->rangelow = radio_range[0] * 16000;
+ vt->rangehigh = radio_range[1] * 16000;
+ return 0;
+}
+
+static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
+ return 0;
+
+ switch_v4l2();
+
+ /* do nothing unless we're a radio tuner */
+ if (t->mode != V4L2_TUNER_RADIO)
+ return 0;
+ t->audmode = vt->audmode;
+ set_radio_freq(client, t->radio_freq);
return 0;
}
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+ if (analog_ops->tuner_status)
+ analog_ops->tuner_status(&t->fe);
+ return 0;
+}
+
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("suspend\n");
/* FIXME: power down ??? */
@@ -1048,7 +1094,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state)
static int tuner_resume(struct i2c_client *c)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("resume\n");
if (V4L2_TUNER_RADIO == t->mode) {
@@ -1061,6 +1107,32 @@ static int tuner_resume(struct i2c_client *c)
return 0;
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+ .log_status = tuner_log_status,
+ .s_standby = tuner_s_standby,
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+ .ioctl = tuner_ioctl,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+ .s_std = tuner_s_std,
+ .s_radio = tuner_s_radio,
+ .g_tuner = tuner_g_tuner,
+ .s_tuner = tuner_s_tuner,
+ .s_frequency = tuner_s_frequency,
+ .g_frequency = tuner_g_frequency,
+ .s_type_addr = tuner_s_type_addr,
+ .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+ .core = &tuner_core_ops,
+ .tuner = &tuner_tuner_ops,
+};
+
/* ---------------------------------------------------------------------- */
static LIST_HEAD(tuner_list);
@@ -1109,9 +1181,9 @@ static int tuner_probe(struct i2c_client *client,
t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
if (NULL == t)
return -ENOMEM;
+ v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
t->i2c = client;
t->name = "(tuner unset)";
- i2c_set_clientdata(client, t);
t->type = UNSET;
t->audmode = V4L2_TUNER_MODE_STEREO;
t->mode_mask = T_UNINITIALIZED;
@@ -1261,8 +1333,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap)
static int tuner_remove(struct i2c_client *client)
{
- struct tuner *t = i2c_get_clientdata(client);
+ struct tuner *t = to_tuner(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&t->sd);
tuner_detach(&t->fe);
t->fe.analog_demod_priv = NULL;
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 3720f0e03a16..d0c794da735b 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -33,8 +33,7 @@
#include <linux/freezer.h>
#include <media/tvaudio.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -110,7 +109,7 @@ struct CHIPDESC {
/* current state of the chip */
struct CHIPSTATE {
- struct i2c_client *c;
+ struct v4l2_subdev sd;
/* chip-specific description - should point to
an entry at CHIPDESC table */
@@ -132,6 +131,11 @@ struct CHIPSTATE {
int audmode;
};
+static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct CHIPSTATE, sd);
+}
+
/* ---------------------------------------------------------------------- */
/* i2c addresses */
@@ -152,34 +156,34 @@ I2C_CLIENT_INSMOD;
static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
if (subaddr < 0) {
- v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n",
- chip->c->name, val);
+ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
chip->shadow.bytes[1] = val;
buffer[0] = val;
- if (1 != i2c_master_send(chip->c,buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n",
- chip->c->name, val);
+ if (1 != i2c_master_send(c, buffer, 1)) {
+ v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
return -1;
}
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n",
- chip->c->name, subaddr, val);
+ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
+ subaddr, val);
chip->shadow.bytes[subaddr+1] = val;
buffer[0] = subaddr;
buffer[1] = val;
- if (2 != i2c_master_send(chip->c,buffer,2)) {
- v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n",
- chip->c->name, subaddr, val);
+ if (2 != i2c_master_send(c, buffer, 2)) {
+ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
+ subaddr, val);
return -1;
}
}
@@ -189,12 +193,14 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
static int chip_write_masked(struct CHIPSTATE *chip,
int subaddr, int val, int mask)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
if (mask != 0) {
if (subaddr < 0) {
val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
} else {
if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register: %d\n",
subaddr);
return -EINVAL;
@@ -208,45 +214,51 @@ static int chip_write_masked(struct CHIPSTATE *chip,
static int chip_read(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer;
- if (1 != i2c_master_recv(chip->c,&buffer,1)) {
- v4l_warn(chip->c, "%s: I/O error (read)\n",
- chip->c->name);
+ if (1 != i2c_master_recv(c, &buffer, 1)) {
+ v4l2_warn(sd, "I/O error (read)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer);
+ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
return buffer;
}
static int chip_read2(struct CHIPSTATE *chip, int subaddr)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char write[1];
unsigned char read[1];
struct i2c_msg msgs[2] = {
- { chip->c->addr, 0, 1, write },
- { chip->c->addr, I2C_M_RD, 1, read }
+ { c->addr, 0, 1, write },
+ { c->addr, I2C_M_RD, 1, read }
};
+
write[0] = subaddr;
- if (2 != i2c_transfer(chip->c->adapter,msgs,2)) {
- v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name);
+ if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ v4l2_warn(sd, "I/O error (read2)\n");
return -1;
}
- v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n",
- chip->c->name, subaddr,read[0]);
+ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
+ subaddr, read[0]);
return read[0];
}
static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
int i;
if (0 == cmd->count)
return 0;
if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l_info(chip->c,
+ v4l2_info(sd,
"Tried to access a non-existent register range: %d to %d\n",
cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
return -EINVAL;
@@ -255,19 +267,19 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
/* FIXME: it seems that the shadow bytes are wrong bellow !*/
/* update our shadow register set; print bytes if (debug > 0) */
- v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:",
- chip->c->name, name,cmd->bytes[0]);
+ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
+ name, cmd->bytes[0]);
for (i = 1; i < cmd->count; i++) {
if (debug)
- printk(" 0x%x",cmd->bytes[i]);
+ printk(KERN_CONT " 0x%x", cmd->bytes[i]);
chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
}
if (debug)
- printk("\n");
+ printk(KERN_CONT "\n");
/* send data to the chip */
- if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) {
- v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name);
+ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ v4l2_warn(sd, "I/O error (%s)\n", name);
return -1;
}
return 0;
@@ -290,9 +302,10 @@ static int chip_thread(void *data)
{
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chip->desc;
+ struct v4l2_subdev *sd = &chip->sd;
int mode;
- v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread started\n");
set_freezable();
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
@@ -302,7 +315,7 @@ static int chip_thread(void *data)
try_to_freeze();
if (kthread_should_stop())
break;
- v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread wakeup\n");
/* don't do anything for radio or if mode != auto */
if (chip->radio || chip->mode != 0)
@@ -314,8 +327,7 @@ static int chip_thread(void *data)
continue;
/* chip detected a new audio mode - set it */
- v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n",
- chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread checkmode\n");
chip->prevmode = mode;
@@ -334,7 +346,7 @@ static int chip_thread(void *data)
mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name);
+ v4l2_dbg(1, debug, sd, "thread exiting\n");
return 0;
}
@@ -363,6 +375,7 @@ static int chip_thread(void *data)
static int tda9840_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val, mode;
val = chip_read(chip);
@@ -372,7 +385,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip)
if (val & TDA9840_ST_STEREO)
mode |= V4L2_TUNER_MODE_STEREO;
- v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
@@ -668,6 +681,7 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode)
static int tda9873_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int val,mode;
val = chip_read(chip);
@@ -676,23 +690,24 @@ static int tda9873_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_STEREO;
if (val & TDA9873_DUAL)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n",
val, mode);
return mode;
}
static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
/* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n");
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n");
return;
}
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data);
switch (mode) {
case V4L2_TUNER_MODE_MONO:
@@ -713,7 +728,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode)
}
chip_write(chip, TDA9873_SW, sw_data);
- v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
+ v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n",
mode, sw_data);
}
@@ -822,6 +837,8 @@ static struct tda9874a_MODES {
static int tda9874a_setup(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
@@ -852,13 +869,14 @@ static int tda9874a_setup(struct CHIPSTATE *chip)
chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
}
- v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
return 1;
}
static int tda9874a_getmode(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dsr,nsr,mode;
int necr; /* just for debugging */
@@ -895,16 +913,18 @@ static int tda9874a_getmode(struct CHIPSTATE *chip)
mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
}
- v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
dsr, nsr, necr, mode);
return mode;
}
static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
+
/* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
/* If auto-muting is disabled, we can hear a signal of degrading quality. */
- if(tda9874a_mode) {
+ if (tda9874a_mode) {
if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
tda9874a_NCONR &= 0xfe; /* enable */
else
@@ -941,7 +961,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_AOSR, aosr);
chip_write(chip, TDA9874A_MDACOSR, mdacosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
mode, aosr, mdacosr);
} else { /* dic == 0x07 */
@@ -976,13 +996,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode)
chip_write(chip, TDA9874A_FMMR, fmmr);
chip_write(chip, TDA9874A_AOSR, aosr);
- v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
mode, fmmr, aosr);
}
}
static int tda9874a_checkit(struct CHIPSTATE *chip)
{
+ struct v4l2_subdev *sd = &chip->sd;
int dic,sic; /* device id. and software id. codes */
if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
@@ -990,10 +1011,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip)
if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
return 0;
- v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
if((dic == 0x11)||(dic == 0x07)) {
- v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h");
+ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
tda9874a_dic = dic; /* remember device id. */
return 1;
}
@@ -1113,12 +1134,12 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
static int tda8425_initialize(struct CHIPSTATE *chip)
{
struct CHIPDESC *desc = chip->desc;
+ struct i2c_client *c = v4l2_get_subdevdata(&chip->sd);
int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1,
/* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF};
- if (chip->c->adapter->id == I2C_HW_B_RIVA) {
- memcpy (desc->inputmap, inputmap, sizeof (inputmap));
- }
+ if (c->adapter->id == I2C_HW_B_RIVA)
+ memcpy(desc->inputmap, inputmap, sizeof(inputmap));
return 0;
}
@@ -1215,9 +1236,11 @@ static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT
static void ta8874z_setmode(struct CHIPSTATE *chip, int mode)
{
+ struct v4l2_subdev *sd = &chip->sd;
int update = 1;
audiocmd *t = NULL;
- v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode);
+
+ v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode);
switch(mode){
case V4L2_TUNER_MODE_MONO:
@@ -1479,142 +1502,11 @@ static struct CHIPDESC chiplist[] = {
/* ---------------------------------------------------------------------- */
-/* i2c registration */
-
-static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct CHIPSTATE *chip;
- struct CHIPDESC *desc;
-
- if (debug) {
- printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
- printk(KERN_INFO "tvaudio: known chips: ");
- for (desc = chiplist; desc->name != NULL; desc++)
- printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
- printk("\n");
- }
-
- chip = kzalloc(sizeof(*chip),GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- chip->c = client;
- i2c_set_clientdata(client, chip);
- /* find description for the chip */
- v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1);
- for (desc = chiplist; desc->name != NULL; desc++) {
- if (0 == *(desc->insmodopt))
- continue;
- if (client->addr < desc->addr_lo ||
- client->addr > desc->addr_hi)
- continue;
- if (desc->checkit && !desc->checkit(chip))
- continue;
- break;
- }
- if (desc->name == NULL) {
- v4l_dbg(1, debug, client, "no matching chip description found\n");
- kfree(chip);
- return -EIO;
- }
- v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
- if (desc->flags) {
- v4l_dbg(1, debug, client, "matches:%s%s%s.\n",
- (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
- (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
- (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
- }
-
- /* fill required data structures */
- if (!id)
- strlcpy(client->name, desc->name, I2C_NAME_SIZE);
- chip->desc = desc;
- chip->shadow.count = desc->registers+1;
- chip->prevmode = -1;
- chip->audmode = V4L2_TUNER_MODE_LANG1;
-
- /* initialization */
- if (desc->initialize != NULL)
- desc->initialize(chip);
- else
- chip_cmd(chip,"init",&desc->init);
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- if (!desc->volfunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without volume controls
- */
- v4l_info(chip->c, "volume callback undefined!\n");
- desc->flags &= ~CHIP_HAS_VOLUME;
- } else {
- chip->left = desc->leftinit ? desc->leftinit : 65535;
- chip->right = desc->rightinit ? desc->rightinit : 65535;
- chip_write(chip, desc->leftreg,
- desc->volfunc(chip->left));
- chip_write(chip, desc->rightreg,
- desc->volfunc(chip->right));
- }
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- if (!desc->bassfunc || !desc->treblefunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without bass/treble controls
- */
- v4l_info(chip->c, "bass/treble callbacks undefined!\n");
- desc->flags &= ~CHIP_HAS_BASSTREBLE;
- } else {
- chip->treble = desc->trebleinit ?
- desc->trebleinit : 32768;
- chip->bass = desc->bassinit ?
- desc->bassinit : 32768;
- chip_write(chip, desc->bassreg,
- desc->bassfunc(chip->bass));
- chip_write(chip, desc->treblereg,
- desc->treblefunc(chip->treble));
- }
- }
-
- chip->thread = NULL;
- if (desc->flags & CHIP_NEED_CHECKMODE) {
- if (!desc->getmode || !desc->setmode) {
- /* This shouldn't be happen. Warn user, but keep working
- without kthread
- */
- v4l_info(chip->c, "set/get mode callbacks undefined!\n");
- return 0;
- }
- /* start async thread */
- init_timer(&chip->wt);
- chip->wt.function = chip_thread_wake;
- chip->wt.data = (unsigned long)chip;
- chip->thread = kthread_run(chip_thread, chip, chip->c->name);
- if (IS_ERR(chip->thread)) {
- v4l_warn(chip->c, "%s: failed to create kthread\n",
- chip->c->name);
- chip->thread = NULL;
- }
- }
- return 0;
-}
-
-static int chip_remove(struct i2c_client *client)
-{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
-
- del_timer_sync(&chip->wt);
- if (chip->thread) {
- /* shutdown async thread */
- kthread_stop(chip->thread);
- chip->thread = NULL;
- }
-
- kfree(chip);
- return 0;
-}
-
-static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1652,9 +1544,10 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip,
return -EINVAL;
}
-static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
+static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
struct v4l2_control *ctrl)
{
+ struct CHIPSTATE *chip = to_state(sd);
struct CHIPDESC *desc = chip->desc;
switch (ctrl->id) {
@@ -1726,161 +1619,327 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip,
/* ---------------------------------------------------------------------- */
/* video4linux interface */
-static int chip_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int tvaudio_s_radio(struct v4l2_subdev *sd)
{
- struct CHIPSTATE *chip = i2c_get_clientdata(client);
- struct CHIPDESC *desc = chip->desc;
+ struct CHIPSTATE *chip = to_state(sd);
- if (debug > 0) {
- v4l_i2c_print_ioctl(chip->c, cmd);
- printk("\n");
+ chip->radio = 1;
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ return 0;
+}
+
+static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ return -EINVAL;
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
}
+ return v4l2_ctrl_query_fill_std(qc);
+}
+
+static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
- switch (cmd) {
- case AUDC_SET_RADIO:
- chip->radio = 1;
+ if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = rt->input;
+ if (chip->muted)
+ return 0;
+ chip_write_masked(chip, desc->inputreg,
+ desc->inputmap[chip->input], desc->inputmask);
+ return 0;
+}
+
+static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = 0;
+
+ if (chip->radio)
+ return 0;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ mode = vt->audmode;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ mode = V4L2_TUNER_MODE_STEREO;
+ break;
+ default:
+ return -EINVAL;
+ }
+ chip->audmode = vt->audmode;
+
+ if (desc->setmode && mode) {
chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
- break;
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- if (!(desc->flags & CHIP_HAS_VOLUME))
- return -EINVAL;
- break;
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- return v4l2_ctrl_query_fill_std(qc);
+ chip->mode = mode;
+ desc->setmode(chip, mode);
}
- case VIDIOC_S_CTRL:
- return tvaudio_set_ctrl(chip, arg);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return tvaudio_get_ctrl(chip, arg);
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+ int mode = V4L2_TUNER_MODE_MONO;
- rt->input = chip->input;
- rt->output = 0;
- break;
+ if (chip->radio)
+ return 0;
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = 0;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+ if (desc->getmode)
+ mode = desc->getmode(chip);
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+ if (mode & V4L2_TUNER_MODE_STEREO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+ /* Note: for SAP it should be mono/lang2 or stereo/lang2.
+ When this module is converted fully to v4l2, then this
+ should change for those chips that can detect SAP. */
+ if (mode & V4L2_TUNER_MODE_LANG1)
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ return 0;
+}
+
+static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 0;
+ return 0;
+}
+
+static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ chip->mode = 0; /* automatic */
+
+ /* For chips that provide getmode and setmode, and doesn't
+ automatically follows the stereo carrier, a kthread is
+ created to set the audio standard. In this case, when then
+ the video channel is changed, tvaudio starts on MONO mode.
+ After waiting for 2 seconds, the kernel thread is called,
+ to follow whatever audio standard is pointed by the
+ audio carrier.
+ */
+ if (chip->thread) {
+ desc->setmode(chip, V4L2_TUNER_MODE_MONO);
+ if (chip->prevmode != V4L2_TUNER_MODE_MONO)
+ chip->prevmode = -1; /* reset previous mode */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+ return 0;
+}
- if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
- return -EINVAL;
- /* There are four inputs: tuner, radio, extern and intern. */
- chip->input = rt->input;
- if (chip->muted)
- break;
- chip_write_masked(chip, desc->inputreg,
- desc->inputmap[chip->input], desc->inputmask);
- break;
+static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0);
+}
+
+static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
+ .g_chip_ident = tvaudio_g_chip_ident,
+ .queryctrl = tvaudio_queryctrl,
+ .g_ctrl = tvaudio_g_ctrl,
+ .s_ctrl = tvaudio_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
+ .s_radio = tvaudio_s_radio,
+ .s_frequency = tvaudio_s_frequency,
+ .s_std = tvaudio_s_std,
+ .s_tuner = tvaudio_s_tuner,
+ .s_tuner = tvaudio_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
+ .s_routing = tvaudio_s_routing,
+};
+
+static const struct v4l2_subdev_ops tvaudio_ops = {
+ .core = &tvaudio_core_ops,
+ .tuner = &tvaudio_tuner_ops,
+ .audio = &tvaudio_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c registration */
+
+static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+ struct v4l2_subdev *sd;
+
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = 0;
- if (chip->radio)
- break;
- switch (vt->audmode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- case V4L2_TUNER_MODE_LANG2:
- mode = vt->audmode;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- mode = V4L2_TUNER_MODE_STEREO;
- break;
- default:
- return -EINVAL;
- }
- chip->audmode = vt->audmode;
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ sd = &chip->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
- if (desc->setmode && mode) {
- chip->watch_stereo = 0;
- /* del_timer(&chip->wt); */
- chip->mode = mode;
- desc->setmode(chip, mode);
- }
+ /* find description for the chip */
+ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
break;
}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int mode = V4L2_TUNER_MODE_MONO;
+ if (desc->name == NULL) {
+ v4l2_dbg(1, debug, sd, "no matching chip description found\n");
+ kfree(chip);
+ return -EIO;
+ }
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
+ if (desc->flags) {
+ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+ }
- if (chip->radio)
- break;
- vt->audmode = chip->audmode;
- vt->rxsubchans = 0;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ /* fill required data structures */
+ if (!id)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+ chip->desc = desc;
+ chip->shadow.count = desc->registers+1;
+ chip->prevmode = -1;
+ chip->audmode = V4L2_TUNER_MODE_LANG1;
- if (desc->getmode)
- mode = desc->getmode(chip);
+ /* initialization */
+ if (desc->initialize != NULL)
+ desc->initialize(chip);
+ else
+ chip_cmd(chip, "init", &desc->init);
- if (mode & V4L2_TUNER_MODE_MONO)
- vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
- if (mode & V4L2_TUNER_MODE_STEREO)
- vt->rxsubchans |= V4L2_TUNER_SUB_STEREO;
- /* Note: for SAP it should be mono/lang2 or stereo/lang2.
- When this module is converted fully to v4l2, then this
- should change for those chips that can detect SAP. */
- if (mode & V4L2_TUNER_MODE_LANG1)
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 |
- V4L2_TUNER_SUB_LANG2;
- break;
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ if (!desc->volfunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without volume controls
+ */
+ v4l2_info(sd, "volume callback undefined!\n");
+ desc->flags &= ~CHIP_HAS_VOLUME;
+ } else {
+ chip->left = desc->leftinit ? desc->leftinit : 65535;
+ chip->right = desc->rightinit ? desc->rightinit : 65535;
+ chip_write(chip, desc->leftreg,
+ desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg,
+ desc->volfunc(chip->right));
+ }
}
- case VIDIOC_S_STD:
- chip->radio = 0;
- break;
- case VIDIOC_S_FREQUENCY:
- chip->mode = 0; /* automatic */
-
- /* For chips that provide getmode and setmode, and doesn't
- automatically follows the stereo carrier, a kthread is
- created to set the audio standard. In this case, when then
- the video channel is changed, tvaudio starts on MONO mode.
- After waiting for 2 seconds, the kernel thread is called,
- to follow whatever audio standard is pointed by the
- audio carrier.
- */
- if (chip->thread) {
- desc->setmode(chip,V4L2_TUNER_MODE_MONO);
- if (chip->prevmode != V4L2_TUNER_MODE_MONO)
- chip->prevmode = -1; /* reset previous mode */
- mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ if (!desc->bassfunc || !desc->treblefunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without bass/treble controls
+ */
+ v4l2_info(sd, "bass/treble callbacks undefined!\n");
+ desc->flags &= ~CHIP_HAS_BASSTREBLE;
+ } else {
+ chip->treble = desc->trebleinit ?
+ desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ?
+ desc->bassinit : 32768;
+ chip_write(chip, desc->bassreg,
+ desc->bassfunc(chip->bass));
+ chip_write(chip, desc->treblereg,
+ desc->treblefunc(chip->treble));
}
- break;
+ }
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0);
+ chip->thread = NULL;
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getmode || !desc->setmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without kthread
+ */
+ v4l2_info(sd, "set/get mode callbacks undefined!\n");
+ return 0;
+ }
+ /* start async thread */
+ init_timer(&chip->wt);
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+ if (IS_ERR(chip->thread)) {
+ v4l2_warn(sd, "failed to create kthread\n");
+ chip->thread = NULL;
+ }
}
return 0;
}
-static int chip_legacy_probe(struct i2c_adapter *adap)
+static int tvaudio_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct CHIPSTATE *chip = to_state(sd);
+
+ del_timer_sync(&chip->wt);
+ if (chip->thread) {
+ /* shutdown async thread */
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
+ }
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(chip);
+ return 0;
+}
+
+static int tvaudio_legacy_probe(struct i2c_adapter *adap)
{
/* don't attach on saa7146 based cards,
because dedicated drivers are used */
@@ -1894,18 +1953,18 @@ static int chip_legacy_probe(struct i2c_adapter *adap)
/* This driver supports many devices and the idea is to let the driver
detect which device is present. So rather than listing all supported
devices here, we pretend to support a single, fake device type. */
-static const struct i2c_device_id chip_id[] = {
+static const struct i2c_device_id tvaudio_id[] = {
{ "tvaudio", 0 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, chip_id);
+MODULE_DEVICE_TABLE(i2c, tvaudio_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tvaudio",
.driverid = I2C_DRIVERID_TVAUDIO,
- .command = chip_command,
- .probe = chip_probe,
- .remove = chip_remove,
- .legacy_probe = chip_legacy_probe,
- .id_table = chip_id,
+ .command = tvaudio_command,
+ .probe = tvaudio_probe,
+ .remove = tvaudio_remove,
+ .legacy_probe = tvaudio_legacy_probe,
+ .id_table = tvaudio_id,
};
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
new file mode 100644
index 000000000000..ac9aa40d09f6
--- /dev/null
+++ b/drivers/media/video/tvp514x.c
@@ -0,0 +1,1569 @@
+/*
+ * drivers/media/video/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-int-device.h>
+#include <media/tvp514x.h>
+
+#include "tvp514x_regs.h"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dump_reg(client, reg, val) \
+ do { \
+ val = tvp514x_read_reg(client, reg); \
+ v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \
+ } while (0)
+
+/**
+ * enum tvp514x_std - enum for supported standards
+ */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * enum tvp514x_state - enum for different decoder states
+ */
+enum tvp514x_state {
+ STATE_NOT_DETECTED,
+ STATE_DETECTED
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+/**
+ * struct tvp514x_decoded - TVP5146/47 decoder object
+ * @v4l2_int_device: Slave handle
+ * @pdata: Board specific
+ * @client: I2C client data
+ * @id: Entry from I2C table
+ * @ver: Chip version
+ * @state: TVP5146/47 decoder state - detected or not-detected
+ * @pix: Current pixel format
+ * @num_fmts: Number of formats
+ * @fmt_list: Format list
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @route: input and output routing at chip level
+ */
+struct tvp514x_decoder {
+ struct v4l2_int_device *v4l2_int_device;
+ const struct tvp514x_platform_data *pdata;
+ struct i2c_client *client;
+
+ struct i2c_device_id *id;
+
+ int ver;
+ enum tvp514x_state state;
+
+ struct v4l2_pix_format pix;
+ int num_fmts;
+ const struct v4l2_fmtdesc *fmt_list;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ struct tvp514x_std_info *std_list;
+
+ struct v4l2_routing route;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list[] = {
+ {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ {TOK_SKIP, 0x0F, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ {TOK_SKIP, 0x13, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ {TOK_SKIP, 0x15, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, 0x26, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x27, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ {TOK_SKIP, 0x29, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ {TOK_SKIP, 0x2B, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ {TOK_SKIP, 0x2F, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x30, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x31, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */
+ {TOK_TERM, 0, 0},
+};
+
+/* List of image formats supported by TVP5146/47 decoder
+ * Currently we are using 8 bit mode only, but can be
+ * extended to 10/20 bit mode.
+ */
+static const struct v4l2_fmtdesc tvp514x_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+/*
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+/*
+ * Control structure for Auto Gain
+ * This is temporary data, will get replaced once
+ * v4l2_ctrl_query_fill supports it.
+ */
+static const struct v4l2_queryctrl tvp514x_autogain_ctrl = {
+ .id = V4L2_CID_AUTOGAIN,
+ .name = "Gain, Automatic",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+};
+
+/*
+ * Read a value from a register in an TVP5146/47 decoder device.
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct i2c_client *client, u8 reg)
+{
+ int err;
+ int retry = 0;
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err == -1) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ int retry = 0;
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * tvp514x_write_regs : Initializes a list of TVP5146/47 registers
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ *
+ * reglist - list of registers to be written
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct i2c_client *client,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(client, next->reg, (u8) next->val);
+ if (err) {
+ v4l_err(client, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * tvp514x_get_current_std:
+ * Returns the current standard detected by TVP5146/47
+ */
+static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder
+ *decoder)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) {
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(decoder->client,
+ REG_VIDEO_STD_STATUS);
+ } else
+ std_status = std; /* use the standard register itself */
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/*
+ * TVP5146/47 register dump function
+ */
+static void tvp514x_reg_dump(struct tvp514x_decoder *decoder)
+{
+ u8 value;
+
+ dump_reg(decoder->client, REG_INPUT_SEL, value);
+ dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value);
+ dump_reg(decoder->client, REG_VIDEO_STD, value);
+ dump_reg(decoder->client, REG_OPERATION_MODE, value);
+ dump_reg(decoder->client, REG_COLOR_KILLER, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL3, value);
+ dump_reg(decoder->client, REG_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_CONTRAST, value);
+ dump_reg(decoder->client, REG_SATURATION, value);
+ dump_reg(decoder->client, REG_HUE, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_COMP_PR_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value);
+ dump_reg(decoder->client, REG_COMP_PB_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_SYNC_CONTROL, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value);
+ dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value);
+}
+
+/*
+ * Configure the TVP5146/47 with the current register settings
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(decoder->client, tvp514x_reg_list);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(decoder);
+
+ return 0;
+}
+
+/*
+ * Detect if an tvp514x is present, and if so which revision.
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+
+ chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION);
+
+ v4l_dbg(1, debug, decoder->client,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l_err(decoder->client,
+ "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+ decoder->state = STATE_DETECTED;
+
+ v4l_info(decoder->client,
+ "%s found at 0x%x (%s)\n", decoder->client->name,
+ decoder->client->addr << 1,
+ decoder->client->adapter->name);
+ return 0;
+}
+
+/*
+ * Following are decoder interface functions implemented by
+ * TVP5146/47 decoder driver.
+ */
+
+/**
+ * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected, returns -EINVAL
+ */
+static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ input_sel = decoder->route.input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return -EINVAL; /* No input detected */
+
+ decoder->current_std = current_std;
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l_dbg(1, debug, decoder->client, "Current STD: %s",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err, i;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (*std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std;
+
+ v4l_dbg(1, debug, decoder->client, "Standard set to: %s",
+ decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @index: number of the input
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int ioctl_s_routing(struct v4l2_int_device *s,
+ struct v4l2_routing *route)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ enum tvp514x_std current_std = STD_INVALID;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((!route) || (route->input >= INPUT_INVALID) ||
+ (route->output >= OUTPUT_INVALID))
+ return -EINVAL; /* Index out of bound */
+
+ input_sel = route->input;
+ output_sel = route->output;
+
+ err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(decoder->client,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ tvp514x_reg_list[REG_INPUT_SEL].val = input_sel;
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ /* get the current standard for future reference */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ continue;
+
+ sync_lock_status = tvp514x_read_reg(decoder->client,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ break; /* Input detected */
+ }
+
+ if ((current_std == STD_INVALID) || (try_count < 0))
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ decoder->route.input = route->input;
+ decoder->route.output = route->output;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Input set to: %d, std : %d",
+ input_sel, current_std);
+
+ return 0;
+}
+
+/**
+ * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qctrl: standard V4L2 v4l2_queryctrl structure
+ *
+ * If the requested control is supported, returns the control information.
+ * Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL;
+
+ if (qctrl == NULL)
+ return err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* Brightness supported is same as standard one (0-255),
+ * so make use of standard API provided.
+ */
+ err = v4l2_ctrl_query_fill_std(qctrl);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ /* Saturation and Contrast supported is -
+ * Contrast: 0 - 255 (Default - 128)
+ * Saturation: 0 - 255 (Default - 128)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue Supported is -
+ * Hue - -180 - +180 (Default - 0, Step - +180)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* Autogain is either 0 or 1*/
+ memcpy(qctrl, &tvp514x_autogain_ctrl,
+ sizeof(struct v4l2_queryctrl));
+ err = 0;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", qctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Query Control: %s : Min - %d, Max - %d, Def - %d",
+ qctrl->name,
+ qctrl->minimum,
+ qctrl->maximum,
+ qctrl->default_value);
+
+ return err;
+}
+
+/**
+ * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the decoder. Otherwise, returns -EINVAL if the control is not
+ * supported.
+ */
+static int
+ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = tvp514x_reg_list[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = tvp514x_reg_list[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = tvp514x_reg_list[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+ ctrl->value = -180;
+ else
+ ctrl->value = 0;
+
+ break;
+ case V4L2_CID_AUTOGAIN:
+ ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val;
+ if ((ctrl->value & 0x3) == 3)
+ ctrl->value = 1;
+ else
+ ctrl->value = 0;
+
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Get Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+ return 0;
+}
+
+/**
+ * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL, value;
+
+ if (ctrl == NULL)
+ return err;
+
+ value = (__s32) ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_CONTRAST,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_SATURATION,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ else if (value == 0)
+ value = 0;
+ else {
+ v4l_err(decoder->client,
+ "invalid hue setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_HUE,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (value == 1)
+ value = 0x0F;
+ else if (value == 0)
+ value = 0x0C;
+ else {
+ v4l_err(decoder->client,
+ "invalid auto gain setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Set Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+
+ return err;
+}
+
+/**
+ * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ *
+ * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
+ */
+static int
+ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int index;
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ index = fmt->index;
+ if ((index >= decoder->num_fmts) || (index < 0))
+ return -EINVAL; /* Index out of bound */
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memcpy(fmt, &decoder->fmt_list[index],
+ sizeof(struct v4l2_fmtdesc));
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: index - %d (%s)",
+ decoder->fmt_list[index].index,
+ decoder->fmt_list[index].description);
+ return 0;
+}
+
+/**
+ * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int
+ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int ifmt;
+ struct v4l2_pix_format *pix;
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix = &f->fmt.pix;
+
+ /* Calculate height and width based on current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ pix->width = decoder->std_list[current_std].width;
+ pix->height = decoder->std_list[current_std].height;
+
+ for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) {
+ if (pix->pixelformat ==
+ decoder->fmt_list[ifmt].pixelformat)
+ break;
+ }
+ if (ifmt == decoder->num_fmts)
+ ifmt = 0; /* None of the format matched, select default */
+ pix->pixelformat = decoder->fmt_list[ifmt].pixelformat;
+
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Try FMT: pixelformat - %s, bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->fmt_list[ifmt].description, pix->bytesperline,
+ pix->width, pix->height);
+ return 0;
+}
+
+/**
+ * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int
+ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_pix_format *pix;
+ int rval;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ pix = &f->fmt.pix;
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ decoder->pix = *pix;
+
+ return rval;
+}
+
+/**
+ * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int
+ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ f->fmt.pix = decoder->pix;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->pix.bytesperline,
+ decoder->pix.width, decoder->pix.height);
+ return 0;
+}
+
+/**
+ * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int rval;
+
+ if (p == NULL)
+ return -EINVAL;
+
+ if (NULL == decoder->pdata->ifparm)
+ return -EINVAL;
+
+ rval = decoder->pdata->ifparm(p);
+ if (rval) {
+ v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval);
+ return rval;
+ }
+
+ p->u.bt656.clock_curr = TVP514X_XCLK_BT656;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: void pointer to hold decoder's private data address
+ *
+ * Returns device's (decoder's) private data area address in p parameter
+ */
+static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (NULL == decoder->pdata->priv_data_set)
+ return -EINVAL;
+
+ return decoder->pdata->priv_data_set(p);
+}
+
+/**
+ * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = 0;
+
+ switch (on) {
+ case V4L2_POWER_OFF:
+ /* Power Down Sequence */
+ err =
+ tvp514x_write_reg(decoder->client, REG_OPERATION_MODE,
+ 0x01);
+ /* Disable mux for TVP5146/47 decoder data path */
+ if (decoder->pdata->power_set)
+ err |= decoder->pdata->power_set(on);
+ decoder->state = STATE_NOT_DETECTED;
+ break;
+
+ case V4L2_POWER_STANDBY:
+ if (decoder->pdata->power_set)
+ err = decoder->pdata->power_set(on);
+ break;
+
+ case V4L2_POWER_ON:
+ /* Enable mux for TVP5146/47 decoder data path */
+ if ((decoder->pdata->power_set) &&
+ (decoder->state == STATE_NOT_DETECTED)) {
+ int i;
+ struct tvp514x_init_seq *int_seq =
+ (struct tvp514x_init_seq *)
+ decoder->id->driver_data;
+
+ err = decoder->pdata->power_set(on);
+
+ /* Power Up Sequence */
+ for (i = 0; i < int_seq->no_regs; i++) {
+ err |= tvp514x_write_reg(decoder->client,
+ int_seq->init_reg_seq[i].reg,
+ int_seq->init_reg_seq[i].val);
+ }
+ /* Detect the sensor is not already detected */
+ err |= tvp514x_detect(decoder);
+ if (err) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+ }
+ err |= tvp514x_configure(decoder);
+ break;
+
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialize the decoder device (calls tvp514x_configure())
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ /* Set default standard to auto */
+ tvp514x_reg_list[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ return tvp514x_configure(decoder);
+}
+
+/**
+ * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+/**
+ * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master. Returns 0 if
+ * TVP5146/47 device could be found, otherwise returns appropriate error.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+
+ err = tvp514x_detect(decoder);
+ if (err < 0) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+
+ v4l_info(decoder->client,
+ "chip version 0x%.2x detected\n", decoder->ver);
+
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power},
+ {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm},
+ {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init},
+ {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap},
+ {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap},
+ {vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+ {vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *) ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+ {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd},
+ {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std},
+ {vidioc_int_s_video_routing_num,
+ (v4l2_int_ioctl_func *) ioctl_s_routing},
+};
+
+static struct v4l2_int_slave tvp514x_slave = {
+ .ioctls = tvp514x_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc),
+};
+
+static struct tvp514x_decoder tvp514x_dev = {
+ .state = STATE_NOT_DETECTED,
+
+ .fmt_list = tvp514x_fmt_list,
+ .num_fmts = ARRAY_SIZE(tvp514x_fmt_list),
+
+ .pix = { /* Default to NTSC 8-bit YUV 422 */
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2,
+ .sizeimage =
+ NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },
+
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+
+};
+
+static struct v4l2_int_device tvp514x_int_device = {
+ .module = THIS_MODULE,
+ .name = TVP514X_MODULE_NAME,
+ .priv = &tvp514x_dev,
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &tvp514x_slave,
+ },
+};
+
+/**
+ * tvp514x_probe - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder = &tvp514x_dev;
+ int err;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ decoder->pdata = client->dev.platform_data;
+ if (!decoder->pdata) {
+ v4l_err(client, "No platform data\n!!");
+ return -ENODEV;
+ }
+ /*
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+ tvp514x_reg_list[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /*
+ * Save the id data, required for power up sequence
+ */
+ decoder->id = (struct i2c_device_id *)id;
+ /* Attach to Master */
+ strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master);
+ decoder->v4l2_int_device = &tvp514x_int_device;
+ decoder->client = client;
+ i2c_set_clientdata(client, decoder);
+
+ /* Register with V4L2 layer as slave device */
+ err = v4l2_int_device_register(decoder->v4l2_int_device);
+ if (err) {
+ i2c_set_clientdata(client, NULL);
+ v4l_err(client,
+ "Unable to register to v4l2. Err[%d]\n", err);
+
+ } else
+ v4l_info(client, "Registered to v4l2 master %s!!\n",
+ decoder->pdata->master);
+
+ return 0;
+}
+
+/**
+ * tvp514x_remove - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int __exit tvp514x_remove(struct i2c_client *client)
+{
+ struct tvp514x_decoder *decoder = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(decoder->v4l2_int_device);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+/*
+ * TVP5146 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5146_init = {
+ .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq),
+ .init_reg_seq = tvp5146_init_reg_seq,
+};
+/*
+ * TVP5147 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5147_init = {
+ .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq),
+ .init_reg_seq = tvp5147_init_reg_seq,
+};
+/*
+ * TVP5146M2/TVP5147M1 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp514xm_init = {
+ .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq),
+ .init_reg_seq = tvp514xm_init_reg_seq,
+};
+/*
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)&tvp5146_init},
+ {"tvp5146m2", (unsigned long)&tvp514xm_init},
+ {"tvp5147", (unsigned long)&tvp5147_init},
+ {"tvp5147m1", (unsigned long)&tvp514xm_init},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_i2c_driver = {
+ .driver = {
+ .name = TVP514X_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tvp514x_probe,
+ .remove = __exit_p(tvp514x_remove),
+ .id_table = tvp514x_id,
+};
+
+/**
+ * tvp514x_init
+ *
+ * Module init function
+ */
+static int __init tvp514x_init(void)
+{
+ return i2c_add_driver(&tvp514x_i2c_driver);
+}
+
+/**
+ * tvp514x_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tvp514x_cleanup(void)
+{
+ i2c_del_driver(&tvp514x_i2c_driver);
+}
+
+module_init(tvp514x_init);
+module_exit(tvp514x_cleanup);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h
new file mode 100644
index 000000000000..351620aeecc2
--- /dev/null
+++ b/drivers/media/video/tvp514x_regs.h
@@ -0,0 +1,297 @@
+/*
+ * drivers/media/video/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+/**
+ * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up
+ * Sequence.
+ * @ no_regs - Number of registers to write for power up sequence.
+ * @ init_reg_seq - Array of registers and respective value to write.
+ */
+struct tvp514x_init_seq {
+ unsigned int no_regs;
+ const struct tvp514x_reg *init_reg_seq;
+};
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 28af5ce5560d..a388a9f0cb18 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -9,8 +9,10 @@
#include <linux/videodev2.h>
#include <linux/delay.h>
#include <linux/video_decoder.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/tvp5150.h>
+#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-chip-ident.h>
#include "tvp5150_reg.h"
@@ -29,21 +31,7 @@ I2C_CLIENT_INSMOD;
static int debug;
module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-#define tvp5150_err(fmt, arg...) do { \
- printk(KERN_ERR "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_info(fmt, arg...) do { \
- printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \
- i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0)
-#define tvp5150_dbg(num, fmt, arg...) \
- do { \
- if (debug >= num) \
- printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\
- c->driver->driver.name, \
- i2c_adapter_id(c->adapter), \
- c->addr , ## arg); } while (0)
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
/* supported controls */
static struct v4l2_queryctrl tvp5150_qctrl[] = {
@@ -87,7 +75,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = {
};
struct tvp5150 {
- struct i2c_client *client;
+ struct v4l2_subdev sd;
v4l2_std_id norm; /* Current set standard */
struct v4l2_routing route;
@@ -98,49 +86,57 @@ struct tvp5150 {
int sat;
};
-static int tvp5150_read(struct i2c_client *c, unsigned char addr)
+static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct tvp5150, sd);
+}
+
+static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[1];
int rc;
buffer[0] = addr;
if (1 != (rc = i2c_master_send(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
msleep(10);
if (1 != (rc = i2c_master_recv(c, buffer, 1)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
return (buffer[0]);
}
-static inline void tvp5150_write(struct i2c_client *c, unsigned char addr,
+static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
unsigned char value)
{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[2];
int rc;
buffer[0] = addr;
buffer[1] = value;
- tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
+ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
if (2 != (rc = i2c_master_send(c, buffer, 2)))
- tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc);
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
}
-static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,int max_line)
+static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
+ const u8 end, int max_line)
{
- int i=0;
+ int i = 0;
- while (init!=(u8)(end+1)) {
- if ((i%max_line) == 0) {
- if (i>0)
+ while (init != (u8)(end + 1)) {
+ if ((i % max_line) == 0) {
+ if (i > 0)
printk("\n");
- printk("tvp5150: %s reg 0x%02x = ",s,init);
+ printk("tvp5150: %s reg 0x%02x = ", s, init);
}
- printk("%02x ",tvp5150_read(c, init));
+ printk("%02x ", tvp5150_read(sd, init));
init++;
i++;
@@ -148,147 +144,148 @@ static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,
printk("\n");
}
-static void dump_reg(struct i2c_client *c)
+static int tvp5150_log_status(struct v4l2_subdev *sd)
{
printk("tvp5150: Video input source selection #1 = 0x%02x\n",
- tvp5150_read(c, TVP5150_VD_IN_SRC_SEL_1));
+ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
printk("tvp5150: Analog channel controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_ANAL_CHL_CTL));
+ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
printk("tvp5150: Operation mode controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_OP_MODE_CTL));
+ tvp5150_read(sd, TVP5150_OP_MODE_CTL));
printk("tvp5150: Miscellaneous controls = 0x%02x\n",
- tvp5150_read(c, TVP5150_MISC_CTL));
+ tvp5150_read(sd, TVP5150_MISC_CTL));
printk("tvp5150: Autoswitch mask= 0x%02x\n",
- tvp5150_read(c, TVP5150_AUTOSW_MSK));
+ tvp5150_read(sd, TVP5150_AUTOSW_MSK));
printk("tvp5150: Color killer threshold control = 0x%02x\n",
- tvp5150_read(c, TVP5150_COLOR_KIL_THSH_CTL));
+ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_2),
- tvp5150_read(c, TVP5150_LUMA_PROC_CTL_3));
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
printk("tvp5150: Brightness control = 0x%02x\n",
- tvp5150_read(c, TVP5150_BRIGHT_CTL));
+ tvp5150_read(sd, TVP5150_BRIGHT_CTL));
printk("tvp5150: Color saturation control = 0x%02x\n",
- tvp5150_read(c, TVP5150_SATURATION_CTL));
+ tvp5150_read(sd, TVP5150_SATURATION_CTL));
printk("tvp5150: Hue control = 0x%02x\n",
- tvp5150_read(c, TVP5150_HUE_CTL));
+ tvp5150_read(sd, TVP5150_HUE_CTL));
printk("tvp5150: Contrast control = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONTRAST_CTL));
+ tvp5150_read(sd, TVP5150_CONTRAST_CTL));
printk("tvp5150: Outputs and data rates select = 0x%02x\n",
- tvp5150_read(c, TVP5150_DATA_RATE_SEL));
+ tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
printk("tvp5150: Configuration shared pins = 0x%02x\n",
- tvp5150_read(c, TVP5150_CONF_SHARED_PIN));
+ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_MSB),
- tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_LSB));
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
printk("tvp5150: Genlock/RTC = 0x%02x\n",
- tvp5150_read(c, TVP5150_GENLOCK));
+ tvp5150_read(sd, TVP5150_GENLOCK));
printk("tvp5150: Horizontal sync start = 0x%02x\n",
- tvp5150_read(c, TVP5150_HORIZ_SYNC_START));
+ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
printk("tvp5150: Vertical blanking start = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_START));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
printk("tvp5150: Vertical blanking stop = 0x%02x\n",
- tvp5150_read(c, TVP5150_VERT_BLANKING_STOP));
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_1),
- tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_2));
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
printk("tvp5150: Interrupt reset register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_RESET_REG_B));
+ tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
printk("tvp5150: Interrupt enable register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INTT_CONFIG_REG_B));
+ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
printk("tvp5150: Video standard = 0x%02x\n",
- tvp5150_read(c, TVP5150_VIDEO_STD));
+ tvp5150_read(sd, TVP5150_VIDEO_STD));
printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
- tvp5150_read(c, TVP5150_CB_GAIN_FACT),
- tvp5150_read(c, TVP5150_CR_GAIN_FACTOR));
+ tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
+ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
printk("tvp5150: Macrovision on counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_ON_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
printk("tvp5150: Macrovision off counter = 0x%02x\n",
- tvp5150_read(c, TVP5150_MACROVISION_OFF_CTR));
+ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
- (tvp5150_read(c, TVP5150_REV_SELECT)&1)?3:4);
+ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
printk("tvp5150: Device ID = %02x%02x\n",
- tvp5150_read(c, TVP5150_MSB_DEV_ID),
- tvp5150_read(c, TVP5150_LSB_DEV_ID));
+ tvp5150_read(sd, TVP5150_MSB_DEV_ID),
+ tvp5150_read(sd, TVP5150_LSB_DEV_ID));
printk("tvp5150: ROM version = (hex) %02x.%02x\n",
- tvp5150_read(c, TVP5150_ROM_MAJOR_VER),
- tvp5150_read(c, TVP5150_ROM_MINOR_VER));
+ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
printk("tvp5150: Vertical line count = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_MSB),
- tvp5150_read(c, TVP5150_VERT_LN_COUNT_LSB));
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
printk("tvp5150: Interrupt status register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_B));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
printk("tvp5150: Interrupt active register B = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ACTIVE_REG_B));
+ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
- tvp5150_read(c, TVP5150_STATUS_REG_1),
- tvp5150_read(c, TVP5150_STATUS_REG_2),
- tvp5150_read(c, TVP5150_STATUS_REG_3),
- tvp5150_read(c, TVP5150_STATUS_REG_4),
- tvp5150_read(c, TVP5150_STATUS_REG_5));
+ tvp5150_read(sd, TVP5150_STATUS_REG_1),
+ tvp5150_read(sd, TVP5150_STATUS_REG_2),
+ tvp5150_read(sd, TVP5150_STATUS_REG_3),
+ tvp5150_read(sd, TVP5150_STATUS_REG_4),
+ tvp5150_read(sd, TVP5150_STATUS_REG_5));
- dump_reg_range(c,"Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
- TVP5150_TELETEXT_FIL1_END,8);
- dump_reg_range(c,"Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
- TVP5150_TELETEXT_FIL2_END,8);
+ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
+ TVP5150_TELETEXT_FIL1_END, 8);
+ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
+ TVP5150_TELETEXT_FIL2_END, 8);
printk("tvp5150: Teletext filter enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_TELETEXT_FIL_ENA));
+ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
printk("tvp5150: Interrupt status register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_STATUS_REG_A));
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
printk("tvp5150: Interrupt enable register A = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_ENABLE_REG_A));
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
printk("tvp5150: Interrupt configuration = 0x%02x\n",
- tvp5150_read(c, TVP5150_INT_CONF));
+ tvp5150_read(sd, TVP5150_INT_CONF));
printk("tvp5150: VDP status register = 0x%02x\n",
- tvp5150_read(c, TVP5150_VDP_STATUS_REG));
+ tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
printk("tvp5150: FIFO word count = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_WORD_COUNT));
+ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_INT_THRESHOLD));
+ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
printk("tvp5150: FIFO reset = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_RESET));
+ tvp5150_read(sd, TVP5150_FIFO_RESET));
printk("tvp5150: Line number interrupt = 0x%02x\n",
- tvp5150_read(c, TVP5150_LINE_NUMBER_INT));
+ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_HIGH),
- tvp5150_read(c, TVP5150_PIX_ALIGN_REG_LOW));
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
printk("tvp5150: FIFO output control = 0x%02x\n",
- tvp5150_read(c, TVP5150_FIFO_OUT_CTRL));
+ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
printk("tvp5150: Full field enable = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_ENA));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
printk("tvp5150: Full field mode register = 0x%02x\n",
- tvp5150_read(c, TVP5150_FULL_FIELD_MODE_REG));
+ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
- dump_reg_range(c,"CC data", TVP5150_CC_DATA_INI,
- TVP5150_CC_DATA_END,8);
+ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
+ TVP5150_CC_DATA_END, 8);
- dump_reg_range(c,"WSS data", TVP5150_WSS_DATA_INI,
- TVP5150_WSS_DATA_END,8);
+ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
+ TVP5150_WSS_DATA_END, 8);
- dump_reg_range(c,"VPS data", TVP5150_VPS_DATA_INI,
- TVP5150_VPS_DATA_END,8);
+ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
+ TVP5150_VPS_DATA_END, 8);
- dump_reg_range(c,"VITC data", TVP5150_VITC_DATA_INI,
- TVP5150_VITC_DATA_END,10);
+ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
+ TVP5150_VITC_DATA_END, 10);
- dump_reg_range(c,"Line mode", TVP5150_LINE_MODE_INI,
- TVP5150_LINE_MODE_END,8);
+ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
+ TVP5150_LINE_MODE_END, 8);
+ return 0;
}
/****************************************************************************
Basic functions
****************************************************************************/
-static inline void tvp5150_selmux(struct i2c_client *c)
+static inline void tvp5150_selmux(struct v4l2_subdev *sd)
{
int opmode=0;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
+ struct tvp5150 *decoder = to_tvp5150(sd);
int input = 0;
unsigned char val;
@@ -309,23 +306,23 @@ static inline void tvp5150_selmux(struct i2c_client *c)
break;
}
- tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i "
+ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
"=> tvp5150 input=%i, opmode=%i\n",
decoder->route.input,decoder->route.output,
input, opmode );
- tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode);
- tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input);
+ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
+ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
/* Svideo should enable YCrCb output and disable GPCL output
* For Composite and TV, it should be the reverse
*/
- val = tvp5150_read(c, TVP5150_MISC_CTL);
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
if (decoder->route.input == TVP5150_SVIDEO)
val = (val & ~0x40) | 0x10;
else
val = (val & ~0x10) | 0x40;
- tvp5150_write(c, TVP5150_MISC_CTL, val);
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
};
struct i2c_reg_value {
@@ -593,35 +590,35 @@ static struct i2c_vbi_ram_value vbi_ram_default[] =
{ (u16)-1 }
};
-static int tvp5150_write_inittab(struct i2c_client *c,
+static int tvp5150_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0xff) {
- tvp5150_write(c, regs->reg, regs->value);
+ tvp5150_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
}
-static int tvp5150_vdp_init(struct i2c_client *c,
+static int tvp5150_vdp_init(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs)
{
unsigned int i;
/* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
/* Before programming, Line mode should be at 0xff */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
/* Load Ram Table */
- while (regs->reg != (u16)-1 ) {
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8);
- tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg);
+ while (regs->reg != (u16)-1) {
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
- for (i=0;i<16;i++)
- tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]);
+ for (i = 0; i < 16; i++)
+ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
regs++;
}
@@ -629,11 +626,13 @@ static int tvp5150_vdp_init(struct i2c_client *c,
}
/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
-static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
+static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
struct v4l2_sliced_vbi_cap *cap)
{
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
int line;
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n");
memset(cap, 0, sizeof *cap);
while (regs->reg != (u16)-1 ) {
@@ -644,6 +643,7 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
regs++;
}
+ return 0;
}
/* Set vbi processing
@@ -659,18 +659,18 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs,
* LSB = field1
* MSB = field2
*/
-static int tvp5150_set_vbi(struct i2c_client *c,
+static int tvp5150_set_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs,
unsigned int type,u8 flags, int line,
const int fields)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
int pos=0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
@@ -698,163 +698,186 @@ static int tvp5150_set_vbi(struct i2c_client *c,
reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
if (fields&1) {
- tvp5150_write(c, reg, type);
+ tvp5150_write(sd, reg, type);
}
if (fields&2) {
- tvp5150_write(c, reg+1, type);
+ tvp5150_write(sd, reg+1, type);
}
return type;
}
-static int tvp5150_get_vbi(struct i2c_client *c,
+static int tvp5150_get_vbi(struct v4l2_subdev *sd,
const struct i2c_vbi_ram_value *regs, int line)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- v4l2_std_id std=decoder->norm;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
u8 reg;
- int pos, type=0;
+ int pos, type = 0;
if (std == V4L2_STD_ALL) {
- tvp5150_err("VBI can't be configured without knowing number of lines\n");
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
return 0;
} else if (std & V4L2_STD_625_50) {
/* Don't follow NTSC Line number convension */
line += 3;
}
- if (line<6||line>27)
+ if (line < 6 || line > 27)
return 0;
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
- pos=tvp5150_read(c, reg)&0x0f;
- if (pos<0x0f)
- type=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg) & 0x0f;
+ if (pos < 0x0f)
+ type = regs[pos].type.vbi_type;
- pos=tvp5150_read(c, reg+1)&0x0f;
- if (pos<0x0f)
- type|=regs[pos].type.vbi_type;
+ pos = tvp5150_read(sd, reg + 1) & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
return type;
}
-static int tvp5150_set_std(struct i2c_client *c, v4l2_std_id std)
+
+static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int fmt=0;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int fmt = 0;
- decoder->norm=std;
+ decoder->norm = std;
/* First tests should be against specific std */
if (std == V4L2_STD_ALL) {
- fmt=0; /* Autodetect mode */
+ fmt = 0; /* Autodetect mode */
} else if (std & V4L2_STD_NTSC_443) {
- fmt=0xa;
+ fmt = 0xa;
} else if (std & V4L2_STD_PAL_M) {
- fmt=0x6;
- } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) {
- fmt=0x8;
+ fmt = 0x6;
+ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ fmt = 0x8;
} else {
/* Then, test against generic ones */
- if (std & V4L2_STD_NTSC) {
- fmt=0x2;
- } else if (std & V4L2_STD_PAL) {
- fmt=0x4;
- } else if (std & V4L2_STD_SECAM) {
- fmt=0xc;
- }
+ if (std & V4L2_STD_NTSC)
+ fmt = 0x2;
+ else if (std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (std & V4L2_STD_SECAM)
+ fmt = 0xc;
}
- tvp5150_dbg(1,"Set video std register to %d.\n",fmt);
- tvp5150_write(c, TVP5150_VIDEO_STD, fmt);
-
+ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
+ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
return 0;
}
-static inline void tvp5150_reset(struct i2c_client *c)
+static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (decoder->norm == std)
+ return 0;
+
+ return tvp5150_set_std(sd, std);
+}
+
+static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
{
+ struct tvp5150 *decoder = to_tvp5150(sd);
u8 msb_id, lsb_id, msb_rom, lsb_rom;
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID);
- lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID);
- msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER);
- lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER);
+ msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID);
+ lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID);
+ msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER);
+ lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- if ((msb_rom==4)&&(lsb_rom==0)) { /* Is TVP5150AM1 */
- tvp5150_info("tvp%02x%02xam1 detected.\n",msb_id, lsb_id);
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
/* ITU-T BT.656.4 timing */
- tvp5150_write(c,TVP5150_REV_SELECT,0);
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
} else {
- if ((msb_rom==3)||(lsb_rom==0x21)) { /* Is TVP5150A */
- tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id);
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
} else {
- tvp5150_info("*** unknown tvp%02x%02x chip detected.\n",msb_id,lsb_id);
- tvp5150_info("*** Rom ver is %d.%d\n",msb_rom,lsb_rom);
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
}
}
/* Initializes TVP5150 to its default values */
- tvp5150_write_inittab(c, tvp5150_init_default);
+ tvp5150_write_inittab(sd, tvp5150_init_default);
/* Initializes VDP registers */
- tvp5150_vdp_init(c, vbi_ram_default);
+ tvp5150_vdp_init(sd, vbi_ram_default);
/* Selects decoder input */
- tvp5150_selmux(c);
+ tvp5150_selmux(sd);
/* Initializes TVP5150 to stream enabled values */
- tvp5150_write_inittab(c, tvp5150_init_enable);
+ tvp5150_write_inittab(sd, tvp5150_init_enable);
/* Initialize image preferences */
- tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright);
- tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast);
- tvp5150_write(c, TVP5150_HUE_CTL, decoder->hue);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast);
+ tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue);
- tvp5150_set_std(c, decoder->norm);
+ tvp5150_set_std(sd, decoder->norm);
+ return 0;
};
-static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n");
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = tvp5150_read(c, TVP5150_BRIGHT_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL);
return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = tvp5150_read(c, TVP5150_CONTRAST_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL);
return 0;
case V4L2_CID_SATURATION:
- ctrl->value = tvp5150_read(c, TVP5150_SATURATION_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL);
return 0;
case V4L2_CID_HUE:
- ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL);
+ ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL);
return 0;
}
return -EINVAL;
}
-static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
+static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
-/* struct tvp5150 *decoder = i2c_get_clientdata(c); */
+ u8 i, n;
+ n = ARRAY_SIZE(tvp5150_qctrl);
+
+ for (i = 0; i < n; i++) {
+ if (ctrl->id != tvp5150_qctrl[i].id)
+ continue;
+ if (ctrl->value < tvp5150_qctrl[i].minimum ||
+ ctrl->value > tvp5150_qctrl[i].maximum)
+ return -ERANGE;
+ v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- tvp5150_write(c, TVP5150_BRIGHT_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value);
return 0;
case V4L2_CID_CONTRAST:
- tvp5150_write(c, TVP5150_CONTRAST_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value);
return 0;
case V4L2_CID_SATURATION:
- tvp5150_write(c, TVP5150_SATURATION_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value);
return 0;
case V4L2_CID_HUE:
- tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value);
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value);
return 0;
}
return -EINVAL;
@@ -863,227 +886,210 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl)
/****************************************************************************
I2C Command
****************************************************************************/
-static int tvp5150_command(struct i2c_client *c,
- unsigned int cmd, void *arg)
-{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- switch (cmd) {
+static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
- case 0:
- case VIDIOC_INT_RESET:
- tvp5150_reset(c);
- break;
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ decoder->route = *route;
+ tvp5150_selmux(sd);
+ return 0;
+}
- *route = decoder->route;
- break;
+static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i;
+
+ /* raw vbi */
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* this is for capturing 36 raw vbi lines
+ if there's a way to cut off the beginning 2 vbi lines
+ with the tvp5150 then the vbi line count could be lowered
+ to 17 lines/field again, although I couldn't find a register
+ which could do that cropping */
+ if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY)
+ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+ if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) {
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+ }
+ return 0;
}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (svbi->service_set != 0) {
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[1][i] = 0;
+ svbi->service_lines[0][i] =
+ tvp5150_set_vbi(sd, vbi_ram_default,
+ svbi->service_lines[0][i], 0xf0, i, 3);
+ }
+ /* Enables FIFO */
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+ } else {
+ /* Disables FIFO*/
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
- decoder->route = *route;
- tvp5150_selmux(c);
- break;
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Disable Line modes */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
}
- case VIDIOC_S_STD:
- if (decoder->norm == *(v4l2_std_id *)arg)
- break;
- return tvp5150_set_std(c, *(v4l2_std_id *)arg);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = decoder->norm;
- break;
+ return 0;
+}
- case VIDIOC_G_SLICED_VBI_CAP:
- {
- struct v4l2_sliced_vbi_cap *cap = arg;
- tvp5150_dbg(1, "VIDIOC_G_SLICED_VBI_CAP\n");
+static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *svbi;
+ int i, mask = 0;
- tvp5150_vbi_get_cap(vbi_ram_default, cap);
- break;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
+
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[0][i] =
+ tvp5150_get_vbi(sd, vbi_ram_default, i);
+ mask |= svbi->service_lines[0][i];
}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
- int i;
-
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- if (svbi->service_set != 0) {
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[1][i] = 0;
-
- svbi->service_lines[0][i]=tvp5150_set_vbi(c,
- vbi_ram_default,
- svbi->service_lines[0][i],0xf0,i,3);
- }
- /* Enables FIFO */
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,1);
- } else {
- /* Disables FIFO*/
- tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,0);
+ svbi->service_set = mask;
+ return 0;
+}
- /* Disable Full Field */
- tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0);
- /* Disable Line modes */
- for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++)
- tvp5150_write(c, i, 0xff);
- }
- break;
- }
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *fmt;
- struct v4l2_sliced_vbi_format *svbi;
+static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- int i, mask=0;
+ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
- fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
- svbi = &fmt->fmt.sliced;
- memset(svbi, 0, sizeof(*svbi));
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
+ rev);
+}
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[0][i]=tvp5150_get_vbi(c,
- vbi_ram_default,i);
- mask|=svbi->service_lines[0][i];
- }
- svbi->service_set=mask;
- break;
- }
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = tvp5150_read(c, reg->reg & 0xff);
- else
- tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- dump_reg(c);
- break;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = tvp5150_read(sd, reg->reg & 0xff);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status = tvp5150_read(c, 0x88);
+static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
- break;
- }
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
- tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n");
+static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int status = tvp5150_read(sd, 0x88);
- for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
- if (qc->id && qc->id == tvp5150_qctrl[i].id) {
- memcpy(qc, &(tvp5150_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
+ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
+ return 0;
+}
- return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- tvp5150_dbg(1, "VIDIOC_G_CTRL called\n");
+static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
- return tvp5150_get_ctrl(c, ctrl);
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- u8 i, n;
- n = ARRAY_SIZE(tvp5150_qctrl);
- for (i = 0; i < n; i++)
- if (ctrl->id == tvp5150_qctrl[i].id) {
- if (ctrl->value <
- tvp5150_qctrl[i].minimum
- || ctrl->value >
- tvp5150_qctrl[i].maximum)
- return -ERANGE;
- tvp5150_dbg(1,
- "VIDIOC_S_CTRL: id=%d, value=%d\n",
- ctrl->id, ctrl->value);
- return tvp5150_set_ctrl(c, ctrl);
- }
- return -EINVAL;
+ v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n");
+
+ for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
+ if (qc->id && qc->id == tvp5150_qctrl[i].id) {
+ memcpy(qc, &(tvp5150_qctrl[i]),
+ sizeof(*qc));
+ return 0;
}
- default:
- return -EINVAL;
- }
+ return -EINVAL;
+}
- return 0;
+static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvp5150_core_ops = {
+ .log_status = tvp5150_log_status,
+ .g_ctrl = tvp5150_g_ctrl,
+ .s_ctrl = tvp5150_s_ctrl,
+ .queryctrl = tvp5150_queryctrl,
+ .reset = tvp5150_reset,
+ .g_chip_ident = tvp5150_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp5150_g_register,
+ .s_register = tvp5150_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
+ .s_std = tvp5150_s_std,
+ .g_tuner = tvp5150_g_tuner,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_routing = tvp5150_s_routing,
+ .g_fmt = tvp5150_g_fmt,
+ .s_fmt = tvp5150_s_fmt,
+ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
+};
+
+static const struct v4l2_subdev_ops tvp5150_ops = {
+ .core = &tvp5150_core_ops,
+ .tuner = &tvp5150_tuner_ops,
+ .video = &tvp5150_video_ops,
+};
+
+
/****************************************************************************
I2C Client & Driver
****************************************************************************/
-static struct i2c_driver driver;
-
-static struct i2c_client client_template = {
- .name = "(unset)",
- .driver = &driver,
-};
-static int tvp5150_detect_client(struct i2c_adapter *adapter,
- int address, int kind)
+static int tvp5150_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
{
- struct i2c_client *c;
struct tvp5150 *core;
- int rv;
-
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: detecting tvp5150 client on address 0x%x\n",
- address << 1);
-
- client_template.adapter = adapter;
- client_template.addr = address;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
- if (!i2c_check_functionality
- (adapter,
+ if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return 0;
-
- c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!c)
- return -ENOMEM;
- memcpy(c, &client_template, sizeof(struct i2c_client));
+ return -EIO;
core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
if (!core) {
- kfree(c);
return -ENOMEM;
}
- i2c_set_clientdata(c, core);
-
- rv = i2c_attach_client(c);
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
core->norm = V4L2_STD_ALL; /* Default is autodetect */
core->route.input = TVP5150_COMPOSITE1;
@@ -1093,69 +1099,38 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter,
core->hue = 0;
core->sat = 128;
- if (rv) {
- kfree(c);
- kfree(core);
- return rv;
- }
-
if (debug > 1)
- dump_reg(c);
+ tvp5150_log_status(sd);
return 0;
}
-static int tvp5150_attach_adapter(struct i2c_adapter *adapter)
+static int tvp5150_remove(struct i2c_client *c)
{
- if (debug)
- printk( KERN_INFO
- "tvp5150.c: starting probe for adapter %s (0x%x)\n",
- adapter->name, adapter->id);
- return i2c_probe(adapter, &addr_data, &tvp5150_detect_client);
-}
-
-static int tvp5150_detach_client(struct i2c_client *c)
-{
- struct tvp5150 *decoder = i2c_get_clientdata(c);
- int err;
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
- tvp5150_dbg(1,
+ v4l2_dbg(1, debug, sd,
"tvp5150.c: removing tvp5150 adapter on address 0x%x\n",
c->addr << 1);
- err = i2c_detach_client(c);
- if (err) {
- return err;
- }
-
- kfree(decoder);
- kfree(c);
-
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_tvp5150(sd));
return 0;
}
/* ----------------------------------------------------------------------- */
-static struct i2c_driver driver = {
- .driver = {
- .name = "tvp5150",
- },
- .id = I2C_DRIVERID_TVP5150,
-
- .attach_adapter = tvp5150_attach_adapter,
- .detach_client = tvp5150_detach_client,
+static const struct i2c_device_id tvp5150_id[] = {
+ { "tvp5150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tvp5150",
+ .driverid = I2C_DRIVERID_TVP5150,
.command = tvp5150_command,
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+ .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
+ .id_table = tvp5150_id,
};
-
-static int __init tvp5150_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit tvp5150_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(tvp5150_init);
-module_exit(tvp5150_exit);
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
new file mode 100644
index 000000000000..d5cdc4be1a35
--- /dev/null
+++ b/drivers/media/video/tw9910.c
@@ -0,0 +1,951 @@
+/*
+ * tw9910 Video Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+#include <media/tw9910.h>
+
+#define GET_ID(val) ((val & 0xF8) >> 3)
+#define GET_ReV(val) (val & 0x07)
+
+/*
+ * register offset
+ */
+#define ID 0x00 /* Product ID Code Register */
+#define STATUS1 0x01 /* Chip Status Register I */
+#define INFORM 0x02 /* Input Format */
+#define OPFORM 0x03 /* Output Format Control Register */
+#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
+#define OUTCTR1 0x05 /* Output Control I */
+#define ACNTL1 0x06 /* Analog Control Register 1 */
+#define CROP_HI 0x07 /* Cropping Register, High */
+#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
+#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
+#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
+#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
+#define CNTRL1 0x0C /* Control Register I */
+#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
+#define SCALE_HI 0x0E /* Scaling Register, High */
+#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
+#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
+#define CONTRAST 0x11 /* CONTRAST Control Register */
+#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
+#define SAT_U 0x13 /* Chroma (U) Gain Register */
+#define SAT_V 0x14 /* Chroma (V) Gain Register */
+#define HUE 0x15 /* Hue Control Register */
+#define CORING1 0x17
+#define CORING2 0x18 /* Coring and IF compensation */
+#define VBICNTL 0x19 /* VBI Control Register */
+#define ACNTL2 0x1A /* Analog Control 2 */
+#define OUTCTR2 0x1B /* Output Control 2 */
+#define SDT 0x1C /* Standard Selection */
+#define SDTR 0x1D /* Standard Recognition */
+#define TEST 0x1F /* Test Control Register */
+#define CLMPG 0x20 /* Clamping Gain */
+#define IAGC 0x21 /* Individual AGC Gain */
+#define AGCGAIN 0x22 /* AGC Gain */
+#define PEAKWT 0x23 /* White Peak Threshold */
+#define CLMPL 0x24 /* Clamp level */
+#define SYNCT 0x25 /* Sync Amplitude */
+#define MISSCNT 0x26 /* Sync Miss Count Register */
+#define PCLAMP 0x27 /* Clamp Position Register */
+#define VCNTL1 0x28 /* Vertical Control I */
+#define VCNTL2 0x29 /* Vertical Control II */
+#define CKILL 0x2A /* Color Killer Level Control */
+#define COMB 0x2B /* Comb Filter Control */
+#define LDLY 0x2C /* Luma Delay and H Filter Control */
+#define MISC1 0x2D /* Miscellaneous Control I */
+#define LOOP 0x2E /* LOOP Control Register */
+#define MISC2 0x2F /* Miscellaneous Control II */
+#define MVSN 0x30 /* Macrovision Detection */
+#define STATUS2 0x31 /* Chip STATUS II */
+#define HFREF 0x32 /* H monitor */
+#define CLMD 0x33 /* CLAMP MODE */
+#define IDCNTL 0x34 /* ID Detection Control */
+#define CLCNTL1 0x35 /* Clamp Control I */
+#define ANAPLLCTL 0x4C
+#define VBIMIN 0x4D
+#define HSLOWCTL 0x4E
+#define WSS3 0x4F
+#define FILLDATA 0x50
+#define SDID 0x51
+#define DID 0x52
+#define WSS1 0x53
+#define WSS2 0x54
+#define VVBI 0x55
+#define LCTL6 0x56
+#define LCTL7 0x57
+#define LCTL8 0x58
+#define LCTL9 0x59
+#define LCTL10 0x5A
+#define LCTL11 0x5B
+#define LCTL12 0x5C
+#define LCTL13 0x5D
+#define LCTL14 0x5E
+#define LCTL15 0x5F
+#define LCTL16 0x60
+#define LCTL17 0x61
+#define LCTL18 0x62
+#define LCTL19 0x63
+#define LCTL20 0x64
+#define LCTL21 0x65
+#define LCTL22 0x66
+#define LCTL23 0x67
+#define LCTL24 0x68
+#define LCTL25 0x69
+#define LCTL26 0x6A
+#define HSGEGIN 0x6B
+#define HSEND 0x6C
+#define OVSDLY 0x6D
+#define OVSEND 0x6E
+#define VBIDELAY 0x6F
+
+/*
+ * register detail
+ */
+
+/* INFORM */
+#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
+#define FC27_FF 0x00 /* 0 : Square pixel mode. */
+ /* Must use 24.54MHz for 60Hz field rate */
+ /* source or 29.5MHz for 50Hz field rate */
+#define IFSEL_S 0x10 /* 01 : S-video decoding */
+#define IFSEL_C 0x00 /* 00 : Composite video decoding */
+ /* Y input video selection */
+#define YSEL_M0 0x00 /* 00 : Mux0 selected */
+#define YSEL_M1 0x04 /* 01 : Mux1 selected */
+#define YSEL_M2 0x08 /* 10 : Mux2 selected */
+#define YSEL_M3 0x10 /* 11 : Mux3 selected */
+
+/* OPFORM */
+#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
+ /* 1 : ITU-R-656 compatible data sequence format */
+#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
+ /* 1 : 16-bit YCrCb 4:2:2 output format.*/
+#define LLCMODE 0x20 /* 1 : LLC output mode. */
+ /* 0 : free-run output mode */
+#define AINC 0x10 /* Serial interface auto-indexing control */
+ /* 0 : auto-increment */
+ /* 1 : non-auto */
+#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
+ /* 0 : Vertical out ctrl by HACTIVE and DVALID */
+#define OEN 0x04 /* Output Enable together with TRI_SEL. */
+
+/* OUTCTR1 */
+#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
+#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
+ /* VS pin output control */
+#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
+#define VSSL_VACT 0x10 /* 1 : VACT */
+#define VSSL_FIELD 0x20 /* 2 : FIELD */
+#define VSSL_VVALID 0x30 /* 3 : VVALID */
+#define VSSL_ZERO 0x70 /* 7 : 0 */
+#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
+#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
+ /* HS pin output control */
+#define HSSL_HACT 0x00 /* 0 : HACT */
+#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
+#define HSSL_DVALID 0x02 /* 2 : DVALID */
+#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
+#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
+#define HSSL_ZERO 0x07 /* 7 : 0 */
+
+/* ACNTL1 */
+#define SRESET 0x80 /* resets the device to its default state
+ * but all register content remain unchanged.
+ * This bit is self-resetting.
+ */
+
+/* VBICNTL */
+/* RTSEL : control the real time signal
+* output from the MPOUT pin
+*/
+#define RTSEL_MASK 0x07
+#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
+#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
+#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
+#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
+#define RTSEL_MONO 0x04 /* 0100 = MONO */
+#define RTSEL_DET50 0x05 /* 0101 = DET50 */
+#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
+#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+
+/*
+ * structure
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct tw9910_scale_ctrl {
+ char *name;
+ unsigned short width;
+ unsigned short height;
+ u16 hscale;
+ u16 vscale;
+};
+
+struct tw9910_cropping_ctrl {
+ u16 vdelay;
+ u16 vactive;
+ u16 hdelay;
+ u16 hactive;
+};
+
+struct tw9910_hsync_ctrl {
+ u16 start;
+ u16 end;
+};
+
+struct tw9910_priv {
+ struct tw9910_video_info *info;
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ const struct tw9910_scale_ctrl *scale;
+};
+
+/*
+ * register settings
+ */
+
+#define ENDMARKER { 0xff, 0xff }
+
+static const struct regval_list tw9910_default_regs[] =
+{
+ { OPFORM, 0x00 },
+ { OUTCTR1, VSP_LO | VSSL_VVALID | HSP_HI | HSSL_HSYNC },
+ ENDMARKER,
+};
+
+static const struct soc_camera_data_format tw9910_color_fmt[] = {
+ {
+ .name = "VYUY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }
+};
+
+static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
+ {
+ .name = "NTSC SQ",
+ .width = 640,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC CCIR601",
+ .width = 720,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC SQ (CIF)",
+ .width = 320,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC CCIR601 (CIF)",
+ .width = 360,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC SQ (QCIF)",
+ .width = 160,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "NTSC CCIR601 (QCIF)",
+ .width = 180,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
+ {
+ .name = "PAL SQ",
+ .width = 768,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL CCIR601",
+ .width = 720,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL SQ (CIF)",
+ .width = 384,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL CCIR601 (CIF)",
+ .width = 360,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL SQ (QCIF)",
+ .width = 192,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "PAL CCIR601 (QCIF)",
+ .width = 180,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_cropping_ctrl tw9910_cropping_ctrl = {
+ .vdelay = 0x0012,
+ .vactive = 0x00F0,
+ .hdelay = 0x0010,
+ .hactive = 0x02D0,
+};
+
+static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
+ .start = 0x0260,
+ .end = 0x0300,
+};
+
+/*
+ * general function
+ */
+static int tw9910_set_scale(struct i2c_client *client,
+ const struct tw9910_scale_ctrl *scale)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SCALE_HI,
+ (scale->vscale & 0x0F00) >> 4 |
+ (scale->hscale & 0x0F00) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
+ scale->hscale & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
+ scale->vscale & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_cropping(struct i2c_client *client,
+ const struct tw9910_cropping_ctrl *cropping)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, CROP_HI,
+ (cropping->vdelay & 0x0300) >> 2 |
+ (cropping->vactive & 0x0300) >> 4 |
+ (cropping->hdelay & 0x0300) >> 6 |
+ (cropping->hactive & 0x0300) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VDELAY_LO,
+ cropping->vdelay & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VACTIVE_LO,
+ cropping->vactive & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HDELAY_LO,
+ cropping->hdelay & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HACTIVE_LO,
+ cropping->hactive & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_hsync(struct i2c_client *client,
+ const struct tw9910_hsync_ctrl *hsync)
+{
+ int ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSGEGIN,
+ (hsync->start & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSEND,
+ (hsync->end & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 2 - 0 */
+ ret = i2c_smbus_read_byte_data(client, HSLOWCTL);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSLOWCTL,
+ (ret & 0x88) |
+ (hsync->start & 0x0007) << 4 |
+ (hsync->end & 0x0007));
+
+ return ret;
+}
+
+static int tw9910_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ while (vals->reg_num != 0xff) {
+ int ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num,
+ vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+
+ val &= ~mask;
+ val |= set;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static void tw9910_reset(struct i2c_client *client)
+{
+ i2c_smbus_write_byte_data(client, ACNTL1, SRESET);
+ msleep(1);
+}
+
+static const struct tw9910_scale_ctrl*
+tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
+{
+ const struct tw9910_scale_ctrl *scale;
+ const struct tw9910_scale_ctrl *ret = NULL;
+ v4l2_std_id norm = icd->vdev->current_norm;
+ __u32 diff = 0xffffffff, tmp;
+ int size, i;
+
+ if (norm & V4L2_STD_NTSC) {
+ scale = tw9910_ntsc_scales;
+ size = ARRAY_SIZE(tw9910_ntsc_scales);
+ } else if (norm & V4L2_STD_PAL) {
+ scale = tw9910_pal_scales;
+ size = ARRAY_SIZE(tw9910_pal_scales);
+ } else {
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ tmp = abs(width - scale[i].width) +
+ abs(height - scale[i].height);
+ if (tmp < diff) {
+ diff = tmp;
+ ret = scale + i;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * soc_camera_ops function
+ */
+static int tw9910_init(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power) {
+ ret = priv->info->link.power(&priv->client->dev, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->info->link.reset)
+ ret = priv->info->link.reset(&priv->client->dev);
+
+ return ret;
+}
+
+static int tw9910_release(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = 0;
+
+ if (priv->info->link.power)
+ ret = priv->info->link.power(&priv->client->dev, 0);
+
+ return ret;
+}
+
+static int tw9910_start_capture(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+
+ if (!priv->scale) {
+ dev_err(&icd->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&icd->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
+
+ return 0;
+}
+
+static int tw9910_stop_capture(struct soc_camera_device *icd)
+{
+ return 0;
+}
+
+static int tw9910_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct soc_camera_link *icl = priv->client->dev.platform_data;
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int tw9910_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ id->ident = V4L2_IDENT_TW9910;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int tw9910_set_std(struct soc_camera_device *icd,
+ v4l2_std_id *a)
+{
+ int ret = -EINVAL;
+
+ if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL))
+ ret = 0;
+
+ return ret;
+}
+
+static int tw9910_enum_input(struct soc_camera_device *icd,
+ struct v4l2_input *inp)
+{
+ inp->type = V4L2_INPUT_TYPE_TUNER;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Video");
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tw9910_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ /* ret = int
+ * reg->val = __u64
+ */
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int tw9910_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+}
+#endif
+
+static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt,
+ struct v4l2_rect *rect)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = -EINVAL;
+ u8 val;
+
+ /*
+ * select suitable norm
+ */
+ priv->scale = tw9910_select_norm(icd, rect->width, rect->height);
+ if (!priv->scale)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * reset hardware
+ */
+ tw9910_reset(priv->client);
+ ret = tw9910_write_array(priv->client, tw9910_default_regs);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set bus width
+ */
+ val = 0x00;
+ if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
+ val = LEN;
+
+ ret = tw9910_mask_set(priv->client, OPFORM, LEN, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * select MPOUT behavior
+ */
+ switch (priv->info->mpout) {
+ case TW9910_MPO_VLOSS:
+ val = RTSEL_VLOSS; break;
+ case TW9910_MPO_HLOCK:
+ val = RTSEL_HLOCK; break;
+ case TW9910_MPO_SLOCK:
+ val = RTSEL_SLOCK; break;
+ case TW9910_MPO_VLOCK:
+ val = RTSEL_VLOCK; break;
+ case TW9910_MPO_MONO:
+ val = RTSEL_MONO; break;
+ case TW9910_MPO_DET50:
+ val = RTSEL_DET50; break;
+ case TW9910_MPO_FIELD:
+ val = RTSEL_FIELD; break;
+ case TW9910_MPO_RTCO:
+ val = RTSEL_RTCO; break;
+ default:
+ val = 0;
+ }
+
+ ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set scale
+ */
+ ret = tw9910_set_scale(priv->client, priv->scale);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set cropping
+ */
+ ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set hsync
+ */
+ ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ return ret;
+
+tw9910_set_fmt_error:
+
+ tw9910_reset(priv->client);
+ priv->scale = NULL;
+
+ return ret;
+}
+
+static int tw9910_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct tw9910_scale_ctrl *scale;
+
+ if (V4L2_FIELD_ANY == pix->field) {
+ pix->field = V4L2_FIELD_INTERLACED;
+ } else if (V4L2_FIELD_INTERLACED != pix->field) {
+ dev_err(&icd->dev, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * select suitable norm
+ */
+ scale = tw9910_select_norm(icd, pix->width, pix->height);
+ if (!scale)
+ return -EINVAL;
+
+ pix->width = scale->width;
+ pix->height = scale->height;
+
+ return 0;
+}
+
+static int tw9910_video_probe(struct soc_camera_device *icd)
+{
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ s32 val;
+ int ret;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /*
+ * tw9910 only use 8 or 16 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&icd->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ icd->formats = tw9910_color_fmt;
+ icd->num_formats = ARRAY_SIZE(tw9910_color_fmt);
+
+ /*
+ * check and show Product ID
+ */
+ val = i2c_smbus_read_byte_data(priv->client, ID);
+ if (0x0B != GET_ID(val) ||
+ 0x00 != GET_ReV(val)) {
+ dev_err(&icd->dev,
+ "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val));
+ return -ENODEV;
+ }
+
+ dev_info(&icd->dev,
+ "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val));
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ return ret;
+
+ icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
+ icd->vdev->current_norm = V4L2_STD_NTSC;
+
+ return ret;
+}
+
+static void tw9910_video_remove(struct soc_camera_device *icd)
+{
+ soc_camera_video_stop(icd);
+}
+
+static struct soc_camera_ops tw9910_ops = {
+ .owner = THIS_MODULE,
+ .probe = tw9910_video_probe,
+ .remove = tw9910_video_remove,
+ .init = tw9910_init,
+ .release = tw9910_release,
+ .start_capture = tw9910_start_capture,
+ .stop_capture = tw9910_stop_capture,
+ .set_fmt = tw9910_set_fmt,
+ .try_fmt = tw9910_try_fmt,
+ .set_bus_param = tw9910_set_bus_param,
+ .query_bus_param = tw9910_query_bus_param,
+ .get_chip_id = tw9910_get_chip_id,
+ .set_std = tw9910_set_std,
+ .enum_input = tw9910_enum_input,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = tw9910_get_register,
+ .set_register = tw9910_set_register,
+#endif
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int tw9910_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+
+{
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct soc_camera_device *icd;
+ const struct tw9910_scale_ctrl *scale;
+ int i, ret;
+
+ info = client->dev.platform_data;
+ if (!info)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent),
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ icd = &priv->icd;
+ icd->ops = &tw9910_ops;
+ icd->control = &client->dev;
+ icd->iface = info->link.bus_id;
+
+ /*
+ * set width and height
+ */
+ icd->width_max = tw9910_ntsc_scales[0].width; /* set default */
+ icd->width_min = tw9910_ntsc_scales[0].width;
+ icd->height_max = tw9910_ntsc_scales[0].height;
+ icd->height_min = tw9910_ntsc_scales[0].height;
+
+ scale = tw9910_ntsc_scales;
+ for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) {
+ icd->width_max = max(scale[i].width, icd->width_max);
+ icd->width_min = min(scale[i].width, icd->width_min);
+ icd->height_max = max(scale[i].height, icd->height_max);
+ icd->height_min = min(scale[i].height, icd->height_min);
+ }
+ scale = tw9910_pal_scales;
+ for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) {
+ icd->width_max = max(scale[i].width, icd->width_max);
+ icd->width_min = min(scale[i].width, icd->width_min);
+ icd->height_max = max(scale[i].height, icd->height_max);
+ icd->height_min = min(scale[i].height, icd->height_min);
+ }
+
+ ret = soc_camera_device_register(icd);
+
+ if (ret) {
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&priv->icd);
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id tw9910_id[] = {
+ { "tw9910", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tw9910_id);
+
+static struct i2c_driver tw9910_i2c_driver = {
+ .driver = {
+ .name = "tw9910",
+ },
+ .probe = tw9910_probe,
+ .remove = tw9910_remove,
+ .id_table = tw9910_id,
+};
+
+/*
+ * module function
+ */
+static int __init tw9910_module_init(void)
+{
+ return i2c_add_driver(&tw9910_i2c_driver);
+}
+
+static void __exit tw9910_module_exit(void)
+{
+ i2c_del_driver(&tw9910_i2c_driver);
+}
+
+module_init(tw9910_module_init);
+module_exit(tw9910_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for tw9910");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index b4628874933b..7a609a3a6dbe 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64031a.h>
@@ -62,6 +62,7 @@ enum {
};
struct upd64031a_state {
+ struct v4l2_subdev sd;
u8 regs[TOT_REGS];
u8 gr_mode;
u8 direct_3dycs_connect;
@@ -69,6 +70,11 @@ struct upd64031a_state {
u8 ext_vert_sync;
};
+static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64031a_state, sd);
+}
+
static u8 upd64031a_init[] = {
0x00, 0xb8, 0x48, 0xd2, 0xe6,
0x03, 0x10, 0x0b, 0xaf, 0x7f,
@@ -78,8 +84,9 @@ static u8 upd64031a_init[] = {
/* ------------------------------------------------------------------------ */
-static u8 upd64031a_read(struct i2c_client *client, u8 reg)
+static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
if (reg >= sizeof(buf))
@@ -90,106 +97,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
/* The input changed due to new input or channel changed */
-static void upd64031a_change(struct i2c_client *client)
+static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
+ struct upd64031a_state *state = to_state(sd);
u8 reg = state->regs[R00];
- v4l_dbg(1, debug, client, "changed input or channel\n");
- upd64031a_write(client, R00, reg | 0x10);
- upd64031a_write(client, R00, reg & ~0x10);
+ v4l2_dbg(1, debug, sd, "changed input or channel\n");
+ upd64031a_write(sd, R00, reg | 0x10);
+ upd64031a_write(sd, R00, reg & ~0x10);
+ return 0;
}
/* ------------------------------------------------------------------------ */
+static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct upd64031a_state *state = to_state(sd);
+ u8 r00, r05, r08;
+
+ state->gr_mode = (route->input & 3) << 6;
+ state->direct_3dycs_connect = (route->input & 0xc) << 4;
+ state->ext_comp_sync =
+ (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+ state->ext_vert_sync =
+ (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+ r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
+ r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
+ state->ext_comp_sync | state->ext_vert_sync;
+ r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
+ state->direct_3dycs_connect;
+ upd64031a_write(sd, R00, r00);
+ upd64031a_write(sd, R05, r05);
+ upd64031a_write(sd, R08, r08);
+ return upd64031a_s_frequency(sd, NULL);
+}
+
+static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0);
+}
+
+static int upd64031a_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
+ upd64031a_read(sd, 0), upd64031a_read(sd, 1));
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64031a_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_S_FREQUENCY:
- upd64031a_change(client);
- break;
-
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->gr_mode >> 6) |
- (state->direct_3dycs_connect >> 4) |
- (state->ext_comp_sync >> 1) |
- (state->ext_vert_sync >> 2);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r05, r08;
-
- state->gr_mode = (route->input & 3) << 6;
- state->direct_3dycs_connect = (route->input & 0xc) << 4;
- state->ext_comp_sync =
- (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
- state->ext_vert_sync =
- (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
- r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
- r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
- state->ext_comp_sync | state->ext_vert_sync;
- r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
- state->direct_3dycs_connect;
- upd64031a_write(client, R00, r00);
- upd64031a_write(client, R05, r05);
- upd64031a_write(client, R08, r08);
- upd64031a_change(client);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n",
- upd64031a_read(client, 0), upd64031a_read(client, 1));
- break;
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
+ .log_status = upd64031a_log_status,
+ .g_chip_ident = upd64031a_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64031a_read(client, reg->reg & 0xff);
- break;
- }
- upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ .g_register = upd64031a_g_register,
+ .s_register = upd64031a_s_register,
#endif
+};
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64031A, 0);
+static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = {
+ .s_frequency = upd64031a_s_frequency,
+};
- default:
- break;
- }
- return 0;
-}
+static const struct v4l2_subdev_video_ops upd64031a_video_ops = {
+ .s_routing = upd64031a_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64031a_ops = {
+ .core = &upd64031a_core_ops,
+ .tuner = &upd64031a_tuner_ops,
+ .video = &upd64031a_video_ops,
+};
/* ------------------------------------------------------------------------ */
@@ -199,6 +227,7 @@ static int upd64031a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64031a_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -210,19 +239,23 @@ static int upd64031a_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64031a_ops);
memcpy(state->regs, upd64031a_init, sizeof(state->regs));
state->gr_mode = UPD64031A_GR_ON << 6;
state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
state->ext_comp_sync = state->ext_vert_sync = 0;
for (i = 0; i < TOT_REGS; i++)
- upd64031a_write(client, i, state->regs[i]);
+ upd64031a_write(sd, i, state->regs[i]);
return 0;
}
static int upd64031a_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index 9521ce004dcc..58412cb9c01a 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64083.h>
@@ -51,11 +51,17 @@ enum {
};
struct upd64083_state {
+ struct v4l2_subdev sd;
u8 mode;
u8 ext_y_adc;
u8 regs[TOT_REGS];
};
+static inline struct upd64083_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64083_state, sd);
+}
+
/* Initial values when used in combination with the
NEC upd64031a ghost reduction chip. */
static u8 upd64083_init[] = {
@@ -68,34 +74,24 @@ static u8 upd64083_init[] = {
/* ------------------------------------------------------------------------ */
-static void upd64083_log_status(struct i2c_client *client)
-{
- u8 buf[7];
-
- i2c_master_recv(client, buf, 7);
- v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
- "SA04=%02x SA05=%02x SA06=%02x\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
-}
-
-/* ------------------------------------------------------------------------ */
-
-static void upd64083_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static u8 upd64083_read(struct i2c_client *client, u8 reg)
+static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[7];
if (reg >= sizeof(buf))
@@ -107,67 +103,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct upd64083_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->mode >> 6) | (state->ext_y_adc >> 3);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r02;
-
- if (route->input > 7 || (route->input & 6) == 6)
- return -EINVAL;
- state->mode = (route->input & 3) << 6;
- state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
- r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
- r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
- upd64083_write(client, R00, r00);
- upd64083_write(client, R02, r02);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- upd64083_log_status(client);
- break;
+ struct upd64083_state *state = to_state(sd);
+ u8 r00, r02;
+
+ if (route->input > 7 || (route->input & 6) == 6)
+ return -EINVAL;
+ state->mode = (route->input & 3) << 6;
+ state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
+ r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
+ r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
+ upd64083_write(sd, R00, r00);
+ upd64083_write(sd, R02, r02);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64083_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (!v4l2_chip_match_i2c_client(client,
+static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64083_read(client, reg->reg & 0xff);
- break;
- }
- upd64083_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64083, 0);
+static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0);
+}
- default:
- break;
- }
+static int upd64083_log_status(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[7];
+ i2c_master_recv(client, buf, 7);
+ v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
+ "SA04=%02x SA05=%02x SA06=%02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
return 0;
}
+static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops upd64083_core_ops = {
+ .log_status = upd64083_log_status,
+ .g_chip_ident = upd64083_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = upd64083_g_register,
+ .s_register = upd64083_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops upd64083_video_ops = {
+ .s_routing = upd64083_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64083_ops = {
+ .core = &upd64083_core_ops,
+ .video = &upd64083_video_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -176,6 +199,7 @@ static int upd64083_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64083_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -187,19 +211,23 @@ static int upd64083_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64083_ops);
/* Initially assume that a ghost reduction chip is present */
state->mode = 0; /* YCS mode */
state->ext_y_adc = (1 << 5);
memcpy(state->regs, upd64083_init, TOT_REGS);
for (i = 0; i < TOT_REGS; i++)
- upd64083_write(client, i, state->regs[i]);
+ upd64083_write(sd, i, state->regs[i]);
return 0;
}
static int upd64083_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c
index c710bcd1df48..f8d85ddb4804 100644
--- a/drivers/media/video/usbvideo/ibmcam.c
+++ b/drivers/media/video/usbvideo/ibmcam.c
@@ -3779,11 +3779,11 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id *
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!", ifnum);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!", ifnum);
return -ENODEV;
}
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
index da27a5287983..90f0ce6a26bc 100644
--- a/drivers/media/video/usbvideo/konicawc.c
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -823,12 +823,12 @@ static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index 4459b8a7f818..fd112f0b9d35 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -447,7 +447,7 @@ static int qcm_sensor_init(struct uvd *uvd)
CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1,
cpu_to_le16(ISOC_PACKET_SIZE)));
CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08));
- CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01));
+ CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143f, 0x01));
CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00));
@@ -955,8 +955,7 @@ static int qcm_probe(struct usb_interface *intf,
for (j=0; j < interface->desc.bNumEndpoints; j++) {
endpoint = &interface->endpoint[j].desc;
- if ((endpoint->bEndpointAddress &
- USB_ENDPOINT_DIR_MASK) != USB_DIR_IN)
+ if (usb_endpoint_dir_out(endpoint))
continue; /* not input then not good */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
@@ -965,9 +964,7 @@ static int qcm_probe(struct usb_interface *intf,
continue; /* 0 pkt size is not what we want */
}
- if ((endpoint->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_ISOC) {
+ if (usb_endpoint_xfer_isoc(endpoint)) {
video_ep = endpoint->bEndpointAddress;
/* break out of the search */
goto good_videoep;
diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c
index 9714baab7833..839a08240c25 100644
--- a/drivers/media/video/usbvideo/ultracam.c
+++ b/drivers/media/video/usbvideo/ultracam.c
@@ -556,12 +556,12 @@ static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
- if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index 7c575bb8184f..148a1f98c70f 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -1123,7 +1123,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file)
if (uvd->debug > 1)
dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
- if (0 < usbvideo_ClientIncModCount(uvd))
+ if (usbvideo_ClientIncModCount(uvd) < 0)
return -ENODEV;
mutex_lock(&uvd->lock);
@@ -1281,8 +1281,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file)
* History:
* 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings.
*/
-static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct uvd *uvd = file->private_data;
@@ -1505,7 +1504,7 @@ static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file,
static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);
+ return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl);
}
/*
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
index 8e2d58bec481..4602597ed8d1 100644
--- a/drivers/media/video/usbvideo/vicam.c
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -844,8 +844,7 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints));
endpoint = &interface->endpoint[0].desc;
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+ if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
bulkEndpoint = endpoint->bEndpointAddress;
} else {
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index d185b57fdcd0..85661b1848fe 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -523,7 +523,7 @@ static int vidioc_querycap (struct file *file, void *priv,
strlcpy(vc->card,
usbvision_device_data[usbvision->DevModel].ModelString,
sizeof(vc->card));
- strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+ strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev),
sizeof(vc->bus_info));
vc->version = USBVISION_DRIVER_VERSION;
vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
@@ -1278,7 +1278,7 @@ static int usbvision_vbi_close(struct inode *inode, struct file *file)
return -ENODEV;
}
-static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
+static int usbvision_do_vbi_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
/* TODO */
@@ -1288,7 +1288,7 @@ static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
static int usbvision_vbi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl);
+ return video_usercopy(file, cmd, arg, usbvision_do_vbi_ioctl);
}
@@ -1679,7 +1679,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
interface = &dev->actconfig->interface[ifnum]->altsetting[0];
}
endpoint = &interface->endpoint[1].desc;
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+ if (usb_endpoint_type(endpoint) !=
USB_ENDPOINT_XFER_ISOC) {
err("%s: interface %d. has non-ISO endpoint!",
__func__, ifnum);
@@ -1687,8 +1687,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
__func__, endpoint->bmAttributes);
return -ENODEV;
}
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
- USB_DIR_OUT) {
+ if (usb_endpoint_dir_out(endpoint)) {
err("%s: interface %d. has ISO OUT endpoint!",
__func__, ifnum);
return -ENODEV;
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index f16aafe9cf14..2208165aa6f0 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -327,6 +327,31 @@ static struct uvc_menu_info exposure_auto_controls[] = {
{ 8, "Aperture Priority Mode" },
};
+static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
+{
+ __s8 zoom = (__s8)data[0];
+
+ switch (query) {
+ case GET_CUR:
+ return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);
+
+ case GET_MIN:
+ case GET_MAX:
+ case GET_RES:
+ case GET_DEF:
+ default:
+ return data[2];
+ }
+}
+
+static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
+{
+ data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
+ data[2] = min(abs(value), 0xff);
+}
+
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
@@ -532,6 +557,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
},
+ {
+ .id = V4L2_CID_ZOOM_ABSOLUTE,
+ .name = "Zoom, Absolute",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_ZOOM_CONTINUOUS,
+ .name = "Zoom, Continuous",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_RELATIVE_CONTROL,
+ .size = 0,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .get = uvc_ctrl_get_zoom,
+ .set = uvc_ctrl_set_zoom,
+ },
+ {
+ .id = V4L2_CID_PRIVACY,
+ .name = "Privacy",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PRIVACY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
};
/* ------------------------------------------------------------------------
@@ -543,18 +600,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
return ctrl->data + id * ctrl->info->size;
}
-static inline int uvc_get_bit(const __u8 *data, int bit)
+static inline int uvc_test_bit(const __u8 *data, int bit)
{
return (data[bit >> 3] >> (bit & 7)) & 1;
}
+static inline void uvc_clear_bit(__u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
/* Extract the bit string specified by mapping->offset and mapping->size
* from the little-endian data stored at 'data' and return the result as
* a signed 32bit integer. Sign extension will be performed if the mapping
* references a signed data type.
*/
-static __s32 uvc_get_le_value(const __u8 *data,
- struct uvc_control_mapping *mapping)
+static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -583,8 +645,8 @@ static __s32 uvc_get_le_value(const __u8 *data,
/* Set the bit string specified by mapping->offset and mapping->size
* in the little-endian data stored at 'data' to the value 'value'.
*/
-static void uvc_set_le_value(__s32 value, __u8 *data,
- struct uvc_control_mapping *mapping)
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -736,7 +798,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->default_value = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data);
}
switch (mapping->v4l2_type) {
@@ -772,21 +834,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->minimum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->maximum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->step = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->step = mapping->get(mapping, GET_RES, data);
}
ret = 0;
@@ -923,8 +985,8 @@ int uvc_ctrl_get(struct uvc_video_device *video,
ctrl->loaded = 1;
}
- xctrl->value = uvc_get_le_value(
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ xctrl->value = mapping->get(mapping, GET_CUR,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
menu = mapping->menu_info;
@@ -980,8 +1042,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
ctrl->info->size);
}
- uvc_set_le_value(value,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
ctrl->dirty = 1;
ctrl->modified = 1;
@@ -1257,6 +1319,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
struct uvc_control_mapping *map;
int ret = -EINVAL;
+ if (mapping->get == NULL)
+ mapping->get = uvc_get_le_value;
+ if (mapping->set == NULL)
+ mapping->set = uvc_set_le_value;
+
if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
"invalid control id 0x%08x\n", mapping->name,
@@ -1306,6 +1373,51 @@ end:
}
/*
+ * Prune an entity of its bogus controls. This currently includes processing
+ * unit auto controls for which no corresponding manual control is available.
+ * Such auto controls make little sense if any, and are known to crash at
+ * least the SiGma Micro webcam.
+ */
+static void
+uvc_ctrl_prune_entity(struct uvc_entity *entity)
+{
+ static const struct {
+ u8 idx_manual;
+ u8 idx_auto;
+ } blacklist[] = {
+ { 2, 11 }, /* Hue */
+ { 6, 12 }, /* White Balance Temperature */
+ { 7, 13 }, /* White Balance Component */
+ };
+
+ u8 *controls;
+ unsigned int size;
+ unsigned int i;
+
+ if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT)
+ return;
+
+ controls = entity->processing.bmControls;
+ size = entity->processing.bControlSize;
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); ++i) {
+ if (blacklist[i].idx_auto >= 8 * size ||
+ blacklist[i].idx_manual >= 8 * size)
+ continue;
+
+ if (!uvc_test_bit(controls, blacklist[i].idx_auto) ||
+ uvc_test_bit(controls, blacklist[i].idx_manual))
+ continue;
+
+ uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no "
+ "matching manual control, removing it.\n", entity->id,
+ blacklist[i].idx_auto);
+
+ uvc_clear_bit(controls, blacklist[i].idx_auto);
+ }
+}
+
+/*
* Initialize device controls.
*/
int uvc_ctrl_init_device(struct uvc_device *dev)
@@ -1331,6 +1443,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
bControlSize = entity->camera.bControlSize;
}
+ if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS)
+ uvc_ctrl_prune_entity(entity);
+
for (i = 0; i < bControlSize; ++i)
ncontrols += hweight8(bmControls[i]);
@@ -1345,7 +1460,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) {
- if (uvc_get_bit(bmControls, i) == 0)
+ if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index d7ad060640bc..89d8bd10a852 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -12,8 +12,8 @@
*/
/*
- * This driver aims to support video input devices compliant with the 'USB
- * Video Class' specification.
+ * This driver aims to support video input and ouput devices compliant with the
+ * 'USB Video Class' specification.
*
* The driver doesn't support the deprecated v4l1 interface. It implements the
* mmap capture method only, and doesn't do any image format conversion in
@@ -32,6 +32,7 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
+#include <asm/unaligned.h>
#include <media/v4l2-common.h>
@@ -43,6 +44,7 @@
#define DRIVER_VERSION "v0.1.0"
#endif
+unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param;
unsigned int uvc_trace_param;
@@ -288,8 +290,10 @@ static int uvc_parse_format(struct uvc_device *dev,
struct uvc_format_desc *fmtdesc;
struct uvc_frame *frame;
const unsigned char *start = buffer;
+ unsigned char *_buffer;
unsigned int interval;
unsigned int i, n;
+ int _buflen;
__u8 ftype;
format->type = buffer[2];
@@ -410,12 +414,20 @@ static int uvc_parse_format(struct uvc_device *dev,
buflen -= buffer[0];
buffer += buffer[0];
+ /* Count the number of frame descriptors to test the bFrameIndex
+ * field when parsing the descriptors. We can't rely on the
+ * bNumFrameDescriptors field as some cameras don't initialize it
+ * properly.
+ */
+ for (_buflen = buflen, _buffer = buffer;
+ _buflen > 2 && _buffer[2] == ftype;
+ _buflen -= _buffer[0], _buffer += _buffer[0])
+ format->nframes++;
+
/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
* based formats have frame descriptors.
*/
while (buflen > 2 && buffer[2] == ftype) {
- frame = &format->frame[format->nframes];
-
if (ftype != VS_FRAME_FRAME_BASED)
n = buflen > 25 ? buffer[25] : 0;
else
@@ -430,22 +442,32 @@ static int uvc_parse_format(struct uvc_device *dev,
return -EINVAL;
}
+ if (buffer[3] - 1 >= format->nframes) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+ "interface %d frame index %u out of range\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[3]);
+ return -EINVAL;
+ }
+
+ frame = &format->frame[buffer[3] - 1];
+
frame->bFrameIndex = buffer[3];
frame->bmCapabilities = buffer[4];
- frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]);
- frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]);
- frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]);
- frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]);
+ frame->wWidth = get_unaligned_le16(&buffer[5]);
+ frame->wHeight = get_unaligned_le16(&buffer[7]);
+ frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
+ frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
if (ftype != VS_FRAME_FRAME_BASED) {
frame->dwMaxVideoFrameBufferSize =
- le32_to_cpup((__le32 *)&buffer[17]);
+ get_unaligned_le32(&buffer[17]);
frame->dwDefaultFrameInterval =
- le32_to_cpup((__le32 *)&buffer[21]);
+ get_unaligned_le32(&buffer[21]);
frame->bFrameIntervalType = buffer[25];
} else {
frame->dwMaxVideoFrameBufferSize = 0;
frame->dwDefaultFrameInterval =
- le32_to_cpup((__le32 *)&buffer[17]);
+ get_unaligned_le32(&buffer[17]);
frame->bFrameIntervalType = buffer[21];
}
frame->dwFrameInterval = *intervals;
@@ -468,7 +490,7 @@ static int uvc_parse_format(struct uvc_device *dev,
* some other divisions by zero which could happen.
*/
for (i = 0; i < n; ++i) {
- interval = le32_to_cpup((__le32 *)&buffer[26+4*i]);
+ interval = get_unaligned_le32(&buffer[26+4*i]);
*(*intervals)++ = interval ? interval : 1;
}
@@ -486,7 +508,6 @@ static int uvc_parse_format(struct uvc_device *dev,
10000000/frame->dwDefaultFrameInterval,
(100000000/frame->dwDefaultFrameInterval)%10);
- format->nframes++;
buflen -= buffer[0];
buffer += buffer[0];
}
@@ -588,46 +609,55 @@ static int uvc_parse_streaming(struct uvc_device *dev,
}
/* Parse the header descriptor. */
- if (buffer[2] == VS_OUTPUT_HEADER) {
+ switch (buffer[2]) {
+ case VS_OUTPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ size = 9;
+ break;
+
+ case VS_INPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ size = 13;
+ break;
+
+ default:
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d OUTPUT HEADER descriptor is not supported.\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber);
+ "%d HEADER descriptor not found.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
goto error;
- } else if (buffer[2] == VS_INPUT_HEADER) {
- p = buflen >= 5 ? buffer[3] : 0;
- n = buflen >= 12 ? buffer[12] : 0;
+ }
- if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d INPUT HEADER descriptor is "
- "invalid.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- goto error;
- }
+ p = buflen >= 4 ? buffer[3] : 0;
+ n = buflen >= size ? buffer[size-1] : 0;
+
+ if (buflen < size + p*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d HEADER descriptor is invalid.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
- streaming->header.bNumFormats = p;
- streaming->header.bEndpointAddress = buffer[6];
+ streaming->header.bNumFormats = p;
+ streaming->header.bEndpointAddress = buffer[6];
+ if (buffer[2] == VS_INPUT_HEADER) {
streaming->header.bmInfo = buffer[7];
streaming->header.bTerminalLink = buffer[8];
streaming->header.bStillCaptureMethod = buffer[9];
streaming->header.bTriggerSupport = buffer[10];
streaming->header.bTriggerUsage = buffer[11];
- streaming->header.bControlSize = n;
-
- streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
- if (streaming->header.bmaControls == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- memcpy(streaming->header.bmaControls, &buffer[13], p*n);
} else {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d HEADER descriptor not found.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
+ streaming->header.bTerminalLink = buffer[7];
+ }
+ streaming->header.bControlSize = n;
+
+ streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
+ if (streaming->header.bmaControls == NULL) {
+ ret = -ENOMEM;
goto error;
}
+ memcpy(streaming->header.bmaControls, &buffer[size], p*n);
+
buflen -= buffer[0];
buffer += buffer[0];
@@ -813,8 +843,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
unit->type = VC_EXTENSION_UNIT;
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins =
- le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
memcpy(unit->extension.baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -858,8 +887,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);
- dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);
+ dev->uvc_version = get_unaligned_le16(&buffer[3]);
+ dev->clock_frequency = get_unaligned_le32(&buffer[7]);
/* Parse all USB Video Streaming interfaces. */
for (i = 0; i < n; ++i) {
@@ -886,7 +915,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
/* Make sure the terminal type MSB is not null, otherwise it
* could be confused with a unit.
*/
- type = le16_to_cpup((__le16 *)&buffer[4]);
+ type = get_unaligned_le16(&buffer[4]);
if ((type & 0xff00) == 0) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d INPUT_TERMINAL %d has invalid "
@@ -928,11 +957,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
term->camera.bControlSize = n;
term->camera.bmControls = (__u8 *)term + sizeof *term;
term->camera.wObjectiveFocalLengthMin =
- le16_to_cpup((__le16 *)&buffer[8]);
+ get_unaligned_le16(&buffer[8]);
term->camera.wObjectiveFocalLengthMax =
- le16_to_cpup((__le16 *)&buffer[10]);
+ get_unaligned_le16(&buffer[10]);
term->camera.wOcularFocalLength =
- le16_to_cpup((__le16 *)&buffer[12]);
+ get_unaligned_le16(&buffer[12]);
memcpy(term->camera.bmControls, &buffer[15], n);
} else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {
term->media.bControlSize = n;
@@ -968,7 +997,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
/* Make sure the terminal type MSB is not null, otherwise it
* could be confused with a unit.
*/
- type = le16_to_cpup((__le16 *)&buffer[4]);
+ type = get_unaligned_le16(&buffer[4]);
if ((type & 0xff00) == 0) {
uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
"interface %d OUTPUT_TERMINAL %d has invalid "
@@ -1042,7 +1071,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->type = buffer[2];
unit->processing.bSourceID = buffer[4];
unit->processing.wMaxMultiplier =
- le16_to_cpup((__le16 *)&buffer[5]);
+ get_unaligned_le16(&buffer[5]);
unit->processing.bControlSize = buffer[7];
unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
memcpy(unit->processing.bmControls, &buffer[8], n);
@@ -1077,8 +1106,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
unit->type = buffer[2];
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins =
- le16_to_cpup((__le16 *)&buffer[21]);
+ unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
memcpy(unit->extension.baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
@@ -1128,8 +1156,13 @@ next_descriptor:
buffer += buffer[0];
}
- /* Check if the optional status endpoint is present. */
- if (alts->desc.bNumEndpoints == 1) {
+ /* Check if the optional status endpoint is present. Built-in iSight
+ * webcams have an interrupt endpoint but spit proprietary data that
+ * don't conform to the UVC status endpoint messages. Don't try to
+ * handle the interrupt endpoint for those cameras.
+ */
+ if (alts->desc.bNumEndpoints == 1 &&
+ !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
struct usb_host_endpoint *ep = &alts->endpoint[0];
struct usb_endpoint_descriptor *desc = &ep->desc;
@@ -1234,6 +1267,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
list_add_tail(&entity->chain, &video->iterms);
break;
+ case TT_STREAMING:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+
+ if (!UVC_ENTITY_IS_ITERM(entity)) {
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+ "terminal %u.\n", entity->id);
+ return -1;
+ }
+
+ if (video->sterm != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
+ "entities in chain.\n");
+ return -1;
+ }
+
+ list_add_tail(&entity->chain, &video->iterms);
+ video->sterm = entity;
+ break;
+
default:
uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
@@ -1344,6 +1397,10 @@ static int uvc_scan_chain(struct uvc_video_device *video)
entity = video->oterm;
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+
+ if (UVC_ENTITY_TYPE(entity) == TT_STREAMING)
+ video->sterm = entity;
+
id = entity->output.bSourceID;
while (id != 0) {
prev = entity;
@@ -1372,8 +1429,11 @@ static int uvc_scan_chain(struct uvc_video_device *video)
return id;
}
- /* Initialize the video buffers queue. */
- uvc_queue_init(&video->queue);
+ if (video->sterm == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
+ "chain.\n");
+ return -1;
+ }
return 0;
}
@@ -1384,7 +1444,8 @@ static int uvc_scan_chain(struct uvc_video_device *video)
* The driver currently supports a single video device per control interface
* only. The terminal and units must match the following structure:
*
- * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
*
* The Extension Units, if present, must have a single input pin. The
* Processing Unit and Extension Units can be in any order. Additional
@@ -1401,7 +1462,7 @@ static int uvc_register_video(struct uvc_device *dev)
list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *streaming;
- if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+ if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
continue;
memset(&dev->video, 0, sizeof dev->video);
@@ -1414,7 +1475,8 @@ static int uvc_register_video(struct uvc_device *dev)
continue;
list_for_each_entry(streaming, &dev->streaming, list) {
- if (streaming->header.bTerminalLink == term->id) {
+ if (streaming->header.bTerminalLink ==
+ dev->video.sterm->id) {
dev->video.streaming = streaming;
found = 1;
break;
@@ -1440,6 +1502,9 @@ static int uvc_register_video(struct uvc_device *dev)
printk(" -> %d).\n", dev->video.oterm->id);
}
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&dev->video.queue, dev->video.streaming->type);
+
/* Initialize the streaming interface with default streaming
* parameters.
*/
@@ -1707,24 +1772,6 @@ static int uvc_reset_resume(struct usb_interface *intf)
* though they are compliant.
*/
static struct usb_device_id uvc_ids[] = {
- /* ALi M5606 (Clevo M540SR) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0402,
- .idProduct = 0x5606,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Creative Live! Optia */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x041e,
- .idProduct = 0x4057,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1810,15 +1857,6 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Silicon Motion SM371 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x090c,
- .idProduct = 0xb371,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* MT6227 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1837,6 +1875,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Samsung Q310) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5931,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Asus F9SG */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1855,6 +1902,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Lenovo Thinkpad SL500 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17ef,
+ .idProduct = 0x480b,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Ecamm Pico iMage */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1884,106 +1940,8 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
- /* Acer OEM Webcam - Unknown vendor */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0100,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Packard Bell OEM Webcam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0101,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer Crystal Eye webcam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0102,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Compaq Presario B1200 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0104,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer Travelmate 7720 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0105,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Medion Akoya Mini E1210 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0141,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Acer OrbiCam - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0200,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Fujitsu Amilo SI2636 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0202,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Advent 4211 - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0203,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0300,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Clevo M570TU - Bison Electronics */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x5986,
- .idProduct = 0x0303,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT
+ | UVC_QUIRK_PRUNE_CONTROLS },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
@@ -2029,6 +1987,8 @@ static void __exit uvc_cleanup(void)
module_init(uvc_init);
module_exit(uvc_cleanup);
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 5646a6a32939..42546342e97d 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -79,12 +79,13 @@
*
*/
-void uvc_queue_init(struct uvc_video_queue *queue)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue);
+ queue->type = type;
}
/*
@@ -132,7 +133,7 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
queue->buffer[i].buf.index = i;
queue->buffer[i].buf.m.offset = i * bufsize;
queue->buffer[i].buf.length = buflength;
- queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ queue->buffer[i].buf.type = queue->type;
queue->buffer[i].buf.sequence = 0;
queue->buffer[i].buf.field = V4L2_FIELD_NONE;
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
@@ -226,7 +227,7 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (v4l2_buf->type != queue->type ||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type,
@@ -249,6 +250,13 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done;
}
+ if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ v4l2_buf->bytesused > buf->buf.length) {
+ uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
spin_lock_irqsave(&queue->irqlock, flags);
if (queue->flags & UVC_QUEUE_DISCONNECTED) {
spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -256,7 +264,11 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done;
}
buf->state = UVC_BUF_STATE_QUEUED;
- buf->buf.bytesused = 0;
+ if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ buf->buf.bytesused = 0;
+ else
+ buf->buf.bytesused = v4l2_buf->bytesused;
+
list_add_tail(&buf->stream, &queue->mainqueue);
list_add_tail(&buf->queue, &queue->irqqueue);
spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -289,7 +301,7 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf;
int ret = 0;
- if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (v4l2_buf->type != queue->type ||
v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type,
@@ -397,6 +409,7 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
}
queue->sequence = 0;
queue->flags |= UVC_QUEUE_STREAMING;
+ queue->buf_used = 0;
} else {
uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue);
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 758dfefaba8d..afcc6934559e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
int ret = 0;
__u8 *fcc;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
@@ -216,7 +216,7 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video,
struct uvc_format *format = video->streaming->cur_format;
struct uvc_frame *frame = video->streaming->cur_frame;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
if (format == NULL || frame == NULL)
@@ -242,7 +242,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
struct uvc_frame *frame;
int ret;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fmt->type != video->streaming->type)
return -EINVAL;
if (uvc_queue_streaming(&video->queue))
@@ -252,9 +252,6 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
if (ret < 0)
return ret;
- if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
- return ret;
-
memcpy(&video->streaming->ctrl, &probe, sizeof probe);
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
@@ -267,7 +264,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
{
uint32_t numerator, denominator;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (parm->type != video->streaming->type)
return -EINVAL;
numerator = video->streaming->ctrl.dwFrameInterval;
@@ -275,13 +272,21 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
uvc_simplify_fraction(&numerator, &denominator, 8, 333);
memset(parm, 0, sizeof *parm);
- parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- parm->parm.capture.capturemode = 0;
- parm->parm.capture.timeperframe.numerator = numerator;
- parm->parm.capture.timeperframe.denominator = denominator;
- parm->parm.capture.extendedmode = 0;
- parm->parm.capture.readbuffers = 0;
+ parm->type = video->streaming->type;
+
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.capturemode = 0;
+ parm->parm.capture.timeperframe.numerator = numerator;
+ parm->parm.capture.timeperframe.denominator = denominator;
+ parm->parm.capture.extendedmode = 0;
+ parm->parm.capture.readbuffers = 0;
+ } else {
+ parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.output.outputmode = 0;
+ parm->parm.output.timeperframe.numerator = numerator;
+ parm->parm.output.timeperframe.denominator = denominator;
+ }
return 0;
}
@@ -291,42 +296,45 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
{
struct uvc_frame *frame = video->streaming->cur_frame;
struct uvc_streaming_control probe;
+ struct v4l2_fract timeperframe;
uint32_t interval;
int ret;
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (parm->type != video->streaming->type)
return -EINVAL;
if (uvc_queue_streaming(&video->queue))
return -EBUSY;
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ timeperframe = parm->parm.capture.timeperframe;
+ else
+ timeperframe = parm->parm.output.timeperframe;
+
memcpy(&probe, &video->streaming->ctrl, sizeof probe);
- interval = uvc_fraction_to_interval(
- parm->parm.capture.timeperframe.numerator,
- parm->parm.capture.timeperframe.denominator);
+ interval = uvc_fraction_to_interval(timeperframe.numerator,
+ timeperframe.denominator);
uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
- parm->parm.capture.timeperframe.numerator,
- parm->parm.capture.timeperframe.denominator,
- interval);
+ timeperframe.numerator, timeperframe.denominator, interval);
probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
/* Probe the device with the new settings. */
if ((ret = uvc_probe_video(video, &probe)) < 0)
return ret;
- /* Commit the new settings. */
- if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
- return ret;
-
memcpy(&video->streaming->ctrl, &probe, sizeof probe);
/* Return the actual frame period. */
- parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;
- parm->parm.capture.timeperframe.denominator = 10000000;
- uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,
- &parm->parm.capture.timeperframe.denominator,
- 8, 333);
+ timeperframe.numerator = probe.dwFrameInterval;
+ timeperframe.denominator = 10000000;
+ uvc_simplify_fraction(&timeperframe.numerator,
+ &timeperframe.denominator, 8, 333);
+
+ if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ parm->parm.capture.timeperframe = timeperframe;
+ else
+ parm->parm.output.timeperframe = timeperframe;
return 0;
}
@@ -464,17 +472,13 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file)
return 0;
}
-static int __uvc_v4l2_do_ioctl(struct file *file,
- unsigned int cmd, void *arg)
+static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct uvc_video_device *video = video_get_drvdata(vdev);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
int ret = 0;
- if (uvc_trace_param & UVC_TRACE_IOCTL)
- v4l_printk_ioctl(cmd);
-
switch (cmd) {
/* Query capabilities */
case VIDIOC_QUERYCAP:
@@ -487,8 +491,12 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
sizeof cap->bus_info);
cap->version = DRIVER_VERSION_NUMBER;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING;
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
break;
}
@@ -666,7 +674,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
struct v4l2_fmtdesc *fmt = arg;
struct uvc_format *format;
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (fmt->type != video->streaming->type ||
fmt->index >= video->streaming->nformats)
return -EINVAL;
@@ -805,7 +813,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
struct v4l2_cropcap *ccap = arg;
struct uvc_frame *frame = video->streaming->cur_frame;
- if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (ccap->type != video->streaming->type)
return -EINVAL;
ccap->bounds.left = 0;
@@ -831,7 +839,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
unsigned int bufsize =
video->streaming->ctrl.dwMaxVideoFrameSize;
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ if (rb->type != video->streaming->type ||
rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
@@ -851,7 +859,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
struct v4l2_buffer *buf = arg;
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (buf->type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -877,7 +885,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
int *type = arg;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (*type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -892,7 +900,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
{
int *type = arg;
- if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (*type != video->streaming->type)
return -EINVAL;
if (!uvc_has_privileges(handle))
@@ -925,7 +933,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- info = kmalloc(sizeof *info, GFP_KERNEL);
+ info = kzalloc(sizeof *info, GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
@@ -952,7 +960,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- map = kmalloc(sizeof *map, GFP_KERNEL);
+ map = kzalloc(sizeof *map, GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
@@ -979,7 +987,7 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
default:
if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
- __uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
+ uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n",
cmd);
return ret;
@@ -988,17 +996,16 @@ static int __uvc_v4l2_do_ioctl(struct file *file,
return ret;
}
-static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- return __uvc_v4l2_do_ioctl(file, cmd, arg);
-}
-
static int uvc_v4l2_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n");
- return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl);
+ if (uvc_trace_param & UVC_TRACE_IOCTL) {
+ uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
+ v4l_printk_ioctl(cmd);
+ printk(")\n");
+ }
+
+ return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index b7bb23820d80..e7c31995527f 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
{
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
- int ret;
pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
: usb_sndctrlpipe(dev->udev, 0);
type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
- ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+ return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
unit << 8 | intfnum, data, size, timeout);
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+ int ret;
+ ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+ UVC_CTRL_CONTROL_TIMEOUT);
if (ret != size) {
uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
"(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
@@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
return 0;
}
-int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size)
-{
- return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
- UVC_CTRL_CONTROL_TIMEOUT);
-}
-
static void uvc_fixup_buffer_size(struct uvc_video_device *video,
struct uvc_streaming_control *ctrl)
{
@@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT);
- if (ret < 0)
+
+ if ((query == GET_MIN || query == GET_MAX) && ret == 2) {
+ /* Some cameras, mostly based on Bison Electronics chipsets,
+ * answer a GET_MIN or GET_MAX request with the wCompQuality
+ * field only.
+ */
+ uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non "
+ "compliance - GET_MIN/MAX(PROBE) incorrectly "
+ "supported. Enabling workaround.\n");
+ memset(ctrl, 0, sizeof ctrl);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
+ ret = 0;
+ goto out;
+ } else if (query == GET_DEF && probe == 1) {
+ /* Many cameras don't support the GET_DEF request on their
+ * video probe control. Warn once and return, the caller will
+ * fall back to GET_CUR.
+ */
+ uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non "
+ "compliance - GET_DEF(PROBE) not supported. "
+ "Enabling workaround.\n");
+ ret = -EIO;
+ goto out;
+ } else if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
+ "%d (exp. %u).\n", query, probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
goto out;
+ }
ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
ctrl->bFormatIndex = data[2];
@@ -114,14 +142,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
- ctrl->dwMaxVideoFrameSize =
- le32_to_cpu(get_unaligned((__le32 *)&data[18]));
- ctrl->dwMaxPayloadTransferSize =
- le32_to_cpu(get_unaligned((__le32 *)&data[22]));
+ ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
+ ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
if (size == 34) {
- ctrl->dwClockFrequency =
- le32_to_cpu(get_unaligned((__le32 *)&data[26]));
+ ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
ctrl->bMinVersion = data[32];
@@ -138,13 +163,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
* Try to get the value from the format and frame descriptor.
*/
uvc_fixup_buffer_size(video, ctrl);
+ ret = 0;
out:
kfree(data);
return ret;
}
-int uvc_set_video_ctrl(struct uvc_video_device *video,
+static int uvc_set_video_ctrl(struct uvc_video_device *video,
struct uvc_streaming_control *ctrl, int probe)
{
__u8 *data;
@@ -168,14 +194,11 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
/* Note: Some of the fields below are not required for IN devices (see
* UVC spec, 4.3.1.1), but we still copy them in case support for OUT
* devices is added in the future. */
- put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize),
- (__le32 *)&data[18]);
- put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize),
- (__le32 *)&data[22]);
+ put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
+ put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
- put_unaligned(cpu_to_le32(ctrl->dwClockFrequency),
- (__le32 *)&data[26]);
+ put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
@@ -186,6 +209,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
+ "%d (exp. %u).\n", probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ }
kfree(data);
return ret;
@@ -252,6 +281,12 @@ done:
return ret;
}
+int uvc_commit_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *probe)
+{
+ return uvc_set_video_ctrl(video, probe, 0);
+}
+
/* ------------------------------------------------------------------------
* Video codecs
*/
@@ -333,7 +368,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
/* Synchronize to the input stream by waiting for the FID bit to be
* toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
- * queue->last_fid is initialized to -1, so the first isochronous
+ * video->last_fid is initialized to -1, so the first isochronous
* frame will always be in sync.
*
* If the device doesn't toggle the FID bit, invert video->last_fid
@@ -360,7 +395,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video,
* last payload can be lost anyway). We thus must check if the FID has
* been toggled.
*
- * queue->last_fid is initialized to -1, so the first isochronous
+ * video->last_fid is initialized to -1, so the first isochronous
* frame will never trigger an end of frame detection.
*
* Empty buffers (bytesused == 0) don't trigger end of frame detection
@@ -418,6 +453,34 @@ static void uvc_video_decode_end(struct uvc_video_device *video,
}
}
+static int uvc_video_encode_header(struct uvc_video_device *video,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ data[0] = 2; /* Header length */
+ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
+ | (video->last_fid & UVC_STREAM_FID);
+ return 2;
+}
+
+static int uvc_video_encode_data(struct uvc_video_device *video,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ struct uvc_video_queue *queue = &video->queue;
+ unsigned int nbytes;
+ void *mem;
+
+ /* Copy video data to the URB buffer. */
+ mem = queue->mem + buf->buf.m.offset + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
+ nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size,
+ nbytes);
+ memcpy(data, mem, nbytes);
+
+ queue->buf_used += nbytes;
+
+ return nbytes;
+}
+
/* ------------------------------------------------------------------------
* URB handling
*/
@@ -477,7 +540,7 @@ static void uvc_video_decode_bulk(struct urb *urb,
/* If the URB is the first of its payload, decode and save the
* header.
*/
- if (video->bulk.header_size == 0) {
+ if (video->bulk.header_size == 0 && !video->bulk.skip_payload) {
do {
ret = uvc_video_decode_start(video, buf, mem, len);
if (ret == -EAGAIN)
@@ -487,14 +550,13 @@ static void uvc_video_decode_bulk(struct urb *urb,
/* If an error occured skip the rest of the payload. */
if (ret < 0 || buf == NULL) {
video->bulk.skip_payload = 1;
- return;
- }
+ } else {
+ memcpy(video->bulk.header, mem, ret);
+ video->bulk.header_size = ret;
- video->bulk.header_size = ret;
- memcpy(video->bulk.header, mem, video->bulk.header_size);
-
- mem += ret;
- len -= ret;
+ mem += ret;
+ len -= ret;
+ }
}
/* The buffer queue might have been cancelled while a bulk transfer
@@ -525,6 +587,48 @@ static void uvc_video_decode_bulk(struct urb *urb,
}
}
+static void uvc_video_encode_bulk(struct urb *urb,
+ struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+ u8 *mem = urb->transfer_buffer;
+ int len = video->urb_size, ret;
+
+ if (buf == NULL) {
+ urb->transfer_buffer_length = 0;
+ return;
+ }
+
+ /* If the URB is the first of its payload, add the header. */
+ if (video->bulk.header_size == 0) {
+ ret = uvc_video_encode_header(video, buf, mem, len);
+ video->bulk.header_size = ret;
+ video->bulk.payload_size += ret;
+ mem += ret;
+ len -= ret;
+ }
+
+ /* Process video data. */
+ ret = uvc_video_encode_data(video, buf, mem, len);
+
+ video->bulk.payload_size += ret;
+ len -= ret;
+
+ if (buf->buf.bytesused == video->queue.buf_used ||
+ video->bulk.payload_size == video->bulk.max_payload_size) {
+ if (buf->buf.bytesused == video->queue.buf_used) {
+ video->queue.buf_used = 0;
+ buf->state = UVC_BUF_STATE_DONE;
+ uvc_queue_next_buffer(&video->queue, buf);
+ video->last_fid ^= UVC_STREAM_FID;
+ }
+
+ video->bulk.header_size = 0;
+ video->bulk.payload_size = 0;
+ }
+
+ urb->transfer_buffer_length = video->urb_size - len;
+}
+
static void uvc_video_complete(struct urb *urb)
{
struct uvc_video_device *video = urb->context;
@@ -722,7 +826,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM;
- pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
+ if (usb_endpoint_dir_in(&ep->desc))
+ pipe = usb_rcvbulkpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+ else
+ pipe = usb_sndbulkpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ size = 0;
for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(0, gfp_flags);
@@ -854,7 +966,7 @@ int uvc_video_resume(struct uvc_video_device *video)
video->frozen = 0;
- if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) {
+ if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) {
uvc_queue_enable(&video->queue, 0);
return ret;
}
@@ -935,23 +1047,30 @@ int uvc_video_init(struct uvc_video_device *video)
break;
}
- /* Commit the default settings. */
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
- if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0)
- return ret;
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
atomic_set(&video->active, 0);
/* Select the video decoding function */
- if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
- video->decode = uvc_video_decode_isight;
- else if (video->streaming->intf->num_altsetting > 1)
- video->decode = uvc_video_decode_isoc;
- else
- video->decode = uvc_video_decode_bulk;
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ video->decode = uvc_video_decode_isight;
+ else if (video->streaming->intf->num_altsetting > 1)
+ video->decode = uvc_video_decode_isoc;
+ else
+ video->decode = uvc_video_decode_bulk;
+ } else {
+ if (video->streaming->intf->num_altsetting == 1)
+ video->decode = uvc_video_encode_bulk;
+ else {
+ uvc_printk(KERN_INFO, "Isochronous endpoints are not "
+ "supported for video output devices.\n");
+ return -EINVAL;
+ }
+ }
return 0;
}
@@ -971,7 +1090,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
return 0;
}
- if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)
+ if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ uvc_no_drop_param)
video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
else
video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
@@ -979,6 +1099,10 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
return ret;
+ /* Commit the streaming parameters. */
+ if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0)
+ return ret;
+
return uvc_init_video(video, GFP_KERNEL);
}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 9a6bc1aafb16..896b791ece15 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -4,7 +4,6 @@
#include <linux/kernel.h>
#include <linux/videodev2.h>
-
/*
* Dynamic controls
*/
@@ -316,6 +315,7 @@ struct uvc_xu_control {
#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
#define UVC_QUIRK_STREAM_NO_FID 0x00000010
#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -383,6 +383,11 @@ struct uvc_control_mapping {
struct uvc_menu_info *menu_info;
__u32 menu_count;
+
+ __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
+ const __u8 *data);
+ void (*set) (struct uvc_control_mapping *mapping, __s32 value,
+ __u8 *data);
};
struct uvc_control {
@@ -523,6 +528,7 @@ struct uvc_streaming {
__u16 maxpsize;
struct uvc_streaming_header header;
+ enum v4l2_buf_type type;
unsigned int nformats;
struct uvc_format *format;
@@ -558,12 +564,15 @@ struct uvc_buffer {
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
struct uvc_video_queue {
+ enum v4l2_buf_type type;
+
void *mem;
unsigned int flags;
__u32 sequence;
unsigned int count;
unsigned int buf_size;
+ unsigned int buf_used;
struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
struct mutex mutex; /* protects buffers and mainqueue */
spinlock_t irqlock; /* protects irqqueue */
@@ -578,8 +587,9 @@ struct uvc_video_device {
atomic_t active;
unsigned int frozen : 1;
- struct list_head iterms;
- struct uvc_entity *oterm;
+ struct list_head iterms; /* Input terminals */
+ struct uvc_entity *oterm; /* Output terminal */
+ struct uvc_entity *sterm; /* USB streaming terminal */
struct uvc_entity *processing;
struct uvc_entity *selector;
struct list_head extensions;
@@ -617,6 +627,7 @@ enum uvc_device_state {
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
+ unsigned long warnings;
__u32 quirks;
int intfnum;
char name[32];
@@ -679,6 +690,10 @@ struct uvc_driver {
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
+#define UVC_WARN_MINMAX 0
+#define UVC_WARN_PROBE_DEF 1
+
+extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_trace_param;
#define uvc_trace(flag, msg...) \
@@ -687,6 +702,12 @@ extern unsigned int uvc_trace_param;
printk(KERN_DEBUG "uvcvideo: " msg); \
} while (0)
+#define uvc_warn_once(dev, warn, msg...) \
+ do { \
+ if (!test_and_set_bit(warn, &dev->warnings)) \
+ printk(KERN_INFO "uvcvideo: " msg); \
+ } while (0)
+
#define uvc_printk(level, msg...) \
printk(level "uvcvideo: " msg)
@@ -709,7 +730,8 @@ extern struct uvc_driver uvc_driver;
extern void uvc_delete(struct kref *kref);
/* Video buffers queue management. */
-extern void uvc_queue_init(struct uvc_video_queue *queue);
+extern void uvc_queue_init(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue);
@@ -740,10 +762,10 @@ extern int uvc_video_resume(struct uvc_video_device *video);
extern int uvc_video_enable(struct uvc_video_device *video, int enable);
extern int uvc_probe_video(struct uvc_video_device *video,
struct uvc_streaming_control *probe);
+extern int uvc_commit_video(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size);
-extern int uvc_set_video_ctrl(struct uvc_video_device *video,
- struct uvc_streaming_control *ctrl, int probe);
/* Status */
extern int uvc_status_init(struct uvc_device *dev);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 846763d7349e..c676b0b0f708 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -28,7 +28,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Author: Alan Cox, <alan@redhat.com>
+ * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk>
*
* Fixes:
*/
@@ -58,6 +58,7 @@
#include <asm/div64.h>
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -320,6 +321,19 @@ const char **v4l2_ctrl_get_menu(u32 id)
"Private packet, IVTV format",
NULL
};
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -352,6 +366,10 @@ const char **v4l2_ctrl_get_menu(u32 id)
return mpeg_stream_type;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
default:
return NULL;
}
@@ -363,17 +381,37 @@ const char *v4l2_ctrl_get_name(u32 id)
{
switch (id) {
/* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
/* MPEG controls */
case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
@@ -410,6 +448,25 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ /* CAMERA controls */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+
default:
return NULL;
}
@@ -428,14 +485,22 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
case V4L2_CID_MPEG_AUDIO_MUTE:
case V4L2_CID_MPEG_VIDEO_MUTE:
case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_PRIVACY:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
min = 0;
max = step = 1;
break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
case V4L2_CID_MPEG_AUDIO_ENCODING:
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
@@ -451,10 +516,12 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_STREAM_TYPE:
case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
qctrl->type = V4L2_CTRL_TYPE_MENU;
step = 1;
break;
case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -801,4 +868,116 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver
return err != -ENOMEM ? 0 : err;
}
EXPORT_SYMBOL(v4l2_i2c_attach);
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = client->driver->driver.owner;
+ /* i2c_client and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, client);
+ i2c_set_clientdata(client, sd);
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ client->driver->driver.name, i2c_adapter_id(client->adapter),
+ client->addr);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
+
+
+
+/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type, u8 addr)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client;
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+ info.addr = addr;
+
+ /* Create the i2c client */
+ client = i2c_new_device(adapter, &info);
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+ return NULL;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type,
+ const unsigned short *addrs)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client = NULL;
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+
+ /* Probe and create the i2c client */
+ client = i2c_new_probed_device(adapter, &info, addrs);
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+ return NULL;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev);
+
#endif
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
new file mode 100644
index 000000000000..d0e1bd3ace6a
--- /dev/null
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -0,0 +1,1082 @@
+/*
+ * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
+ * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
+ *
+ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2003 Pavel Machek (pavel@suse.cz)
+ * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
+ * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * These routines maintain argument size conversion between 32bit and 64bit
+ * ioctls.
+ */
+
+#include <linux/compat.h>
+#define __OLD_VIDIOC_ /* To allow fixing old calls*/
+#include <linux/videodev.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <media/v4l2-ioctl.h>
+
+#ifdef CONFIG_COMPAT
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+struct video_tuner32 {
+ compat_int_t tuner;
+ char name[32];
+ compat_ulong_t rangelow, rangehigh;
+ u32 flags; /* It is really u32 in videodev.h */
+ u16 mode, signal;
+};
+
+static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) ||
+ get_user(kp->tuner, &up->tuner) ||
+ copy_from_user(kp->name, up->name, 32) ||
+ get_user(kp->rangelow, &up->rangelow) ||
+ get_user(kp->rangehigh, &up->rangehigh) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->mode, &up->mode) ||
+ get_user(kp->signal, &up->signal))
+ return -EFAULT;
+ return 0;
+}
+
+static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) ||
+ put_user(kp->tuner, &up->tuner) ||
+ copy_to_user(up->name, kp->name, 32) ||
+ put_user(kp->rangelow, &up->rangelow) ||
+ put_user(kp->rangehigh, &up->rangehigh) ||
+ put_user(kp->flags, &up->flags) ||
+ put_user(kp->mode, &up->mode) ||
+ put_user(kp->signal, &up->signal))
+ return -EFAULT;
+ return 0;
+}
+
+struct video_buffer32 {
+ compat_caddr_t base;
+ compat_int_t height, width, depth, bytesperline;
+};
+
+static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up)
+{
+ u32 tmp;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_buffer32)) ||
+ get_user(tmp, &up->base) ||
+ get_user(kp->height, &up->height) ||
+ get_user(kp->width, &up->width) ||
+ get_user(kp->depth, &up->depth) ||
+ get_user(kp->bytesperline, &up->bytesperline))
+ return -EFAULT;
+
+ /* This is actually a physical address stored
+ * as a void pointer.
+ */
+ kp->base = (void *)(unsigned long) tmp;
+
+ return 0;
+}
+
+static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up)
+{
+ u32 tmp = (u32)((unsigned long)kp->base);
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) ||
+ put_user(tmp, &up->base) ||
+ put_user(kp->height, &up->height) ||
+ put_user(kp->width, &up->width) ||
+ put_user(kp->depth, &up->depth) ||
+ put_user(kp->bytesperline, &up->bytesperline))
+ return -EFAULT;
+ return 0;
+}
+
+struct video_clip32 {
+ s32 x, y, width, height; /* It's really s32 in videodev.h */
+ compat_caddr_t next;
+};
+
+struct video_window32 {
+ u32 x, y, width, height, chromakey, flags;
+ compat_caddr_t clips;
+ compat_int_t clipcount;
+};
+
+static int get_video_window32(struct video_window *kp, struct video_window32 __user *up)
+{
+ struct video_clip __user *uclips;
+ struct video_clip __user *kclips;
+ compat_caddr_t p;
+ int nclips;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)))
+ return -EFAULT;
+
+ if (get_user(nclips, &up->clipcount))
+ return -EFAULT;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)) ||
+ get_user(kp->x, &up->x) ||
+ get_user(kp->y, &up->y) ||
+ get_user(kp->width, &up->width) ||
+ get_user(kp->height, &up->height) ||
+ get_user(kp->chromakey, &up->chromakey) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+
+ nclips = kp->clipcount;
+ kp->clips = NULL;
+
+ if (nclips == 0)
+ return 0;
+ if (get_user(p, &up->clips))
+ return -EFAULT;
+ uclips = compat_ptr(p);
+
+ /* If nclips < 0, then it is a clipping bitmap of size
+ VIDEO_CLIPMAP_SIZE */
+ if (nclips < 0) {
+ if (!access_ok(VERIFY_READ, uclips, VIDEO_CLIPMAP_SIZE))
+ return -EFAULT;
+ kp->clips = compat_alloc_user_space(VIDEO_CLIPMAP_SIZE);
+ if (copy_in_user(kp->clips, uclips, VIDEO_CLIPMAP_SIZE))
+ return -EFAULT;
+ return 0;
+ }
+
+ /* Otherwise it is an array of video_clip structs. */
+ if (!access_ok(VERIFY_READ, uclips, nclips * sizeof(struct video_clip)))
+ return -EFAULT;
+
+ kp->clips = compat_alloc_user_space(nclips * sizeof(struct video_clip));
+ kclips = kp->clips;
+ while (nclips--) {
+ int err;
+
+ err = copy_in_user(&kclips->x, &uclips->x, sizeof(kclips->x));
+ err |= copy_in_user(&kclips->y, &uclips->y, sizeof(kclips->y));
+ err |= copy_in_user(&kclips->width, &uclips->width, sizeof(kclips->width));
+ err |= copy_in_user(&kclips->height, &uclips->height, sizeof(kclips->height));
+ kclips->next = NULL;
+ if (err)
+ return -EFAULT;
+ kclips++;
+ uclips++;
+ }
+ return 0;
+}
+
+/* You get back everything except the clips... */
+static int put_video_window32(struct video_window *kp, struct video_window32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) ||
+ put_user(kp->x, &up->x) ||
+ put_user(kp->y, &up->y) ||
+ put_user(kp->width, &up->width) ||
+ put_user(kp->height, &up->height) ||
+ put_user(kp->chromakey, &up->chromakey) ||
+ put_user(kp->flags, &up->flags) ||
+ put_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ return 0;
+}
+
+struct video_code32 {
+ char loadwhat[16]; /* name or tag of file being passed */
+ compat_int_t datasize;
+ unsigned char *data;
+};
+
+static int get_microcode32(struct video_code *kp, struct video_code32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) ||
+ copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) ||
+ get_user(kp->datasize, &up->datasize) ||
+ copy_from_user(kp->data, up->data, up->datasize))
+ return -EFAULT;
+ return 0;
+}
+
+#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32)
+#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32)
+#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32)
+#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32)
+#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32)
+#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32)
+#define VIDIOCGFREQ32 _IOR('v', 14, u32)
+#define VIDIOCSFREQ32 _IOW('v', 15, u32)
+#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32)
+
+#define VIDIOCCAPTURE32 _IOW('v', 8, s32)
+#define VIDIOCSYNC32 _IOW('v', 18, s32)
+#define VIDIOCSWRITEMODE32 _IOW('v', 25, s32)
+
+#endif
+
+static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOIOCTLCMD;
+
+ if (file->f_op->unlocked_ioctl)
+ ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+ else if (file->f_op->ioctl) {
+ lock_kernel();
+ ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
+ unlock_kernel();
+ }
+
+ return ret;
+}
+
+
+struct v4l2_clip32 {
+ struct v4l2_rect c;
+ compat_caddr_t next;
+};
+
+struct v4l2_window32 {
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ __u32 chromakey;
+ compat_caddr_t clips; /* actually struct v4l2_clip32 * */
+ __u32 clipcount;
+ compat_caddr_t bitmap;
+};
+
+static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
+ copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
+ get_user(kp->field, &up->field) ||
+ get_user(kp->chromakey, &up->chromakey) ||
+ get_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ if (kp->clipcount > 2048)
+ return -EINVAL;
+ if (kp->clipcount) {
+ struct v4l2_clip32 __user *uclips;
+ struct v4l2_clip __user *kclips;
+ int n = kp->clipcount;
+ compat_caddr_t p;
+
+ if (get_user(p, &up->clips))
+ return -EFAULT;
+ uclips = compat_ptr(p);
+ kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
+ kp->clips = kclips;
+ while (--n >= 0) {
+ if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
+ return -EFAULT;
+ if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+ return -EFAULT;
+ uclips += 1;
+ kclips += 1;
+ }
+ } else
+ kp->clips = NULL;
+ return 0;
+}
+
+static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (copy_to_user(&up->w, &kp->w, sizeof(up->w)) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->chromakey, &up->chromakey) ||
+ put_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_format32 {
+ enum v4l2_buf_type type;
+ union {
+ struct v4l2_pix_format pix;
+ struct v4l2_window32 win;
+ struct v4l2_vbi_format vbi;
+ struct v4l2_sliced_vbi_format sliced;
+ __u8 raw_data[200]; /* user-defined */
+ } fmt;
+};
+
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+ get_user(kp->type, &up->type))
+ return -EFAULT;
+ switch (kp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ case 0:
+ return -EINVAL;
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ put_user(kp->type, &up->type))
+ return -EFAULT;
+ switch (kp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ case V4L2_BUF_TYPE_PRIVATE:
+ if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
+ return -EFAULT;
+ return 0;
+ case 0:
+ return -EINVAL;
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+struct v4l2_standard32 {
+ __u32 index;
+ __u32 id[2]; /* __u64 would get the alignment wrong */
+ __u8 name[24];
+ struct v4l2_fract frameperiod; /* Frames, not fields */
+ __u32 framelines;
+ __u32 reserved[4];
+};
+
+static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ /* other fields are not set by the user, nor used by the driver */
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
+ get_user(kp->index, &up->index))
+ return -EFAULT;
+ return 0;
+}
+
+static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+ put_user(kp->index, &up->index) ||
+ copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
+ copy_to_user(up->name, kp->name, 24) ||
+ copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
+ put_user(kp->framelines, &up->framelines) ||
+ copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_buffer32 {
+ __u32 index;
+ enum v4l2_buf_type type;
+ __u32 bytesused;
+ __u32 flags;
+ enum v4l2_field field;
+ struct compat_timeval timestamp;
+ struct v4l2_timecode timecode;
+ __u32 sequence;
+
+ /* memory location */
+ enum v4l2_memory memory;
+ union {
+ __u32 offset;
+ compat_long_t userptr;
+ } m;
+ __u32 length;
+ __u32 input;
+ __u32 reserved;
+};
+
+static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
+ get_user(kp->index, &up->index) ||
+ get_user(kp->type, &up->type) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->memory, &up->memory) ||
+ get_user(kp->input, &up->input))
+ return -EFAULT;
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ break;
+ case V4L2_MEMORY_USERPTR:
+ {
+ compat_long_t tmp;
+
+ if (get_user(kp->length, &up->length) ||
+ get_user(tmp, &up->m.userptr))
+ return -EFAULT;
+
+ kp->m.userptr = (unsigned long)compat_ptr(tmp);
+ }
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (get_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
+ return 0;
+}
+
+static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
+ put_user(kp->index, &up->index) ||
+ put_user(kp->type, &up->type) ||
+ put_user(kp->flags, &up->flags) ||
+ put_user(kp->memory, &up->memory) ||
+ put_user(kp->input, &up->input))
+ return -EFAULT;
+ switch (kp->memory) {
+ case V4L2_MEMORY_MMAP:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_USERPTR:
+ if (put_user(kp->length, &up->length) ||
+ put_user(kp->m.userptr, &up->m.userptr))
+ return -EFAULT;
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ if (put_user(kp->m.offset, &up->m.offset))
+ return -EFAULT;
+ break;
+ }
+ if (put_user(kp->bytesused, &up->bytesused) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
+ copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_user(kp->reserved, &up->reserved))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_framebuffer32 {
+ __u32 capability;
+ __u32 flags;
+ compat_caddr_t base;
+ struct v4l2_pix_format fmt;
+};
+
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
+ get_user(tmp, &up->base) ||
+ get_user(kp->capability, &up->capability) ||
+ get_user(kp->flags, &up->flags))
+ return -EFAULT;
+ kp->base = compat_ptr(tmp);
+ get_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp = (u32)((unsigned long)kp->base);
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+ put_user(tmp, &up->base) ||
+ put_user(kp->capability, &up->capability) ||
+ put_user(kp->flags, &up->flags))
+ return -EFAULT;
+ put_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+struct v4l2_input32 {
+ __u32 index; /* Which input */
+ __u8 name[32]; /* Label */
+ __u32 type; /* Type of input */
+ __u32 audioset; /* Associated audios (bitfield) */
+ __u32 tuner; /* Associated tuner */
+ v4l2_std_id std;
+ __u32 status;
+ __u32 reserved[4];
+} __attribute__ ((packed));
+
+/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
+ Otherwise it is identical to the 32-bit version. */
+static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_ext_controls32 {
+ __u32 ctrl_class;
+ __u32 count;
+ __u32 error_idx;
+ __u32 reserved[2];
+ compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
+};
+
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols;
+ int n;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+ get_user(kp->ctrl_class, &up->ctrl_class) ||
+ get_user(kp->count, &up->count) ||
+ get_user(kp->error_idx, &up->error_idx) ||
+ copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ return -EFAULT;
+ n = kp->count;
+ if (n == 0) {
+ kp->controls = NULL;
+ return 0;
+ }
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control)))
+ return -EFAULT;
+ kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
+ kp->controls = kcontrols;
+ while (--n >= 0) {
+ if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32)))
+ return -EFAULT;
+ if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2)))
+ return -EFAULT;
+ /* Note: if the void * part of the union ever becomes relevant
+ then we need to know the type of the control in order to do
+ the right thing here. Luckily, that is not yet an issue. */
+ if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value)))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols = kp->controls;
+ int n = kp->count;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+ put_user(kp->ctrl_class, &up->ctrl_class) ||
+ put_user(kp->count, &up->count) ||
+ put_user(kp->error_idx, &up->error_idx) ||
+ copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ return -EFAULT;
+ if (!kp->count)
+ return 0;
+
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control)))
+ return -EFAULT;
+
+ while (--n >= 0) {
+ if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32)))
+ return -EFAULT;
+ if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
+ sizeof(ucontrols->reserved2)))
+ return -EFAULT;
+ /* Note: if the void * part of the union ever becomes relevant
+ then we need to know the type of the control in order to do
+ the right thing here. Luckily, that is not yet an issue. */
+ if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value)))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
+#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
+#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
+#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
+#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
+#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32)
+#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
+#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
+#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
+#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
+#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
+#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
+
+#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
+#ifdef __OLD_VIDIOC_
+#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32)
+#endif
+#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
+#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
+#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
+#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32)
+#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
+#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
+
+static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ union {
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ struct video_tuner vt;
+ struct video_buffer vb;
+ struct video_window vw;
+ struct video_code vc;
+ struct video_audio va;
+#endif
+ struct v4l2_format v2f;
+ struct v4l2_buffer v2b;
+ struct v4l2_framebuffer v2fb;
+ struct v4l2_input v2i;
+ struct v4l2_standard v2s;
+ struct v4l2_ext_controls v2ecs;
+ unsigned long vx;
+ int vi;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ int compatible_arg = 1;
+ int err = 0;
+
+ /* First, convert the command. */
+ switch (cmd) {
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break;
+ case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break;
+ case VIDIOCGWIN32: cmd = VIDIOCGWIN; break;
+ case VIDIOCSWIN32: cmd = VIDIOCSWIN; break;
+ case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break;
+ case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break;
+ case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break;
+ case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break;
+ case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break;
+#endif
+ case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
+ case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
+ case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
+ case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
+ case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
+ case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
+ case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
+ case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
+ case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
+ case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+ case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
+ case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
+ case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+ case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+#ifdef __OLD_VIDIOC_
+ case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break;
+#endif
+ case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
+ case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
+ case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
+ case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
+ case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
+ case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ }
+
+ switch (cmd) {
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ case VIDIOCSTUNER:
+ case VIDIOCGTUNER:
+ err = get_video_tuner32(&karg.vt, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCSFBUF:
+ err = get_video_buffer32(&karg.vb, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCSWIN:
+ err = get_video_window32(&karg.vw, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCGWIN:
+ case VIDIOCGFBUF:
+ case VIDIOCGFREQ:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCSMICROCODE:
+ err = get_microcode32(&karg.vc, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCSFREQ:
+ err = get_user(karg.vx, (u32 __user *)up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOCCAPTURE:
+ case VIDIOCSYNC:
+ case VIDIOCSWRITEMODE:
+#endif
+ case VIDIOC_OVERLAY:
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ err = get_user(karg.vi, (s32 __user *)up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = get_v4l2_format32(&karg.v2f, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = get_v4l2_buffer32(&karg.v2b, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_S_FBUF:
+ err = get_v4l2_framebuffer32(&karg.v2fb, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FBUF:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = get_v4l2_standard32(&karg.v2s, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = get_v4l2_input32(&karg.v2i, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+ compatible_arg = 0;
+ break;
+ }
+ if (err)
+ return err;
+
+ if (compatible_arg)
+ err = native_ioctl(file, cmd, (unsigned long)up);
+ else {
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = native_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+ }
+
+ /* Special case: even after an error we need to put the
+ results back for these ioctls since the error_idx will
+ contain information on which control failed. */
+ switch (cmd) {
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+ err = -EFAULT;
+ break;
+ }
+ if (err)
+ return err;
+
+ switch (cmd) {
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ case VIDIOCGTUNER:
+ err = put_video_tuner32(&karg.vt, up);
+ break;
+
+ case VIDIOCGWIN:
+ err = put_video_window32(&karg.vw, up);
+ break;
+
+ case VIDIOCGFBUF:
+ err = put_video_buffer32(&karg.vb, up);
+ break;
+
+ case VIDIOCGFREQ:
+ err = put_user(((u32)karg.vx), (u32 __user *)up);
+ break;
+#endif
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ err = put_user(((s32)karg.vi), (s32 __user *)up);
+ break;
+
+ case VIDIOC_G_FBUF:
+ err = put_v4l2_framebuffer32(&karg.v2fb, up);
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = put_v4l2_format32(&karg.v2f, up);
+ break;
+
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = put_v4l2_buffer32(&karg.v2b, up);
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = put_v4l2_standard32(&karg.v2s, up);
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = put_v4l2_input32(&karg.v2i, up);
+ break;
+ }
+ return err;
+}
+
+long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = -ENOIOCTLCMD;
+
+ if (!file->f_op->ioctl && !file->f_op->unlocked_ioctl)
+ return ret;
+
+ switch (cmd) {
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ case VIDIOCGCAP:
+ case VIDIOCGCHAN:
+ case VIDIOCSCHAN:
+ case VIDIOCGTUNER32:
+ case VIDIOCSTUNER32:
+ case VIDIOCGPICT:
+ case VIDIOCSPICT:
+ case VIDIOCCAPTURE32:
+ case VIDIOCGWIN32:
+ case VIDIOCSWIN32:
+ case VIDIOCGFBUF32:
+ case VIDIOCSFBUF32:
+ case VIDIOCKEY:
+ case VIDIOCGFREQ32:
+ case VIDIOCSFREQ32:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ case VIDIOCSYNC32:
+ case VIDIOCMCAPTURE:
+ case VIDIOCGMBUF:
+ case VIDIOCGUNIT:
+ case VIDIOCGCAPTURE:
+ case VIDIOCSCAPTURE:
+ case VIDIOCSPLAYMODE:
+ case VIDIOCSWRITEMODE32:
+ case VIDIOCGPLAYINFO:
+ case VIDIOCSMICROCODE32:
+ case VIDIOCGVBIFMT:
+ case VIDIOCSVBIFMT:
+#endif
+#ifdef __OLD_VIDIOC_
+ case VIDIOC_OVERLAY32_OLD:
+ case VIDIOC_S_PARM_OLD:
+ case VIDIOC_S_CTRL_OLD:
+ case VIDIOC_G_AUDIO_OLD:
+ case VIDIOC_G_AUDOUT_OLD:
+ case VIDIOC_CROPCAP_OLD:
+#endif
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_RESERVED:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_G_FMT32:
+ case VIDIOC_S_FMT32:
+ case VIDIOC_REQBUFS:
+ case VIDIOC_QUERYBUF32:
+ case VIDIOC_G_FBUF32:
+ case VIDIOC_S_FBUF32:
+ case VIDIOC_OVERLAY32:
+ case VIDIOC_QBUF32:
+ case VIDIOC_DQBUF32:
+ case VIDIOC_STREAMON32:
+ case VIDIOC_STREAMOFF32:
+ case VIDIOC_G_PARM:
+ case VIDIOC_S_PARM:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_ENUMSTD32:
+ case VIDIOC_ENUMINPUT32:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_G_INPUT32:
+ case VIDIOC_S_INPUT32:
+ case VIDIOC_G_OUTPUT32:
+ case VIDIOC_S_OUTPUT32:
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_G_MODULATOR:
+ case VIDIOC_S_MODULATOR:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_JPEGCOMP:
+ case VIDIOC_S_JPEGCOMP:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_TRY_FMT32:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_EXT_CTRLS32:
+ case VIDIOC_S_EXT_CTRLS32:
+ case VIDIOC_TRY_EXT_CTRLS32:
+ case VIDIOC_ENUM_FRAMESIZES:
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_G_CHIP_IDENT:
+ case VIDIOC_S_HW_FREQ_SEEK:
+ ret = do_video_ioctl(file, cmd, arg);
+ break;
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ /* BTTV specific... */
+ case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]):
+ case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]):
+ case _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int):
+ case _IOW('v' , BASE_VIDIOCPRIVATE+3, char [16]): /* struct bttv_pll_info */
+ case _IOR('v' , BASE_VIDIOCPRIVATE+4, int):
+ case _IOR('v' , BASE_VIDIOCPRIVATE+5, int):
+ case _IOR('v' , BASE_VIDIOCPRIVATE+6, int):
+ case _IOR('v' , BASE_VIDIOCPRIVATE+7, int):
+ ret = native_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+ break;
+#endif
+ default:
+ v4l_print_ioctl("compat_ioctl32", cmd);
+ printk(KERN_CONT "\n");
+ break;
+ }
+ return ret;
+}
+#else
+long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+#endif
+EXPORT_SYMBOL_GPL(v4l_compat_ioctl32);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index ccd6566a515e..7ad6711ee327 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -9,7 +9,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Authors: Alan Cox, <alan@redhat.com> (version 1)
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*
* Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
@@ -30,6 +30,7 @@
#include <asm/system.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#define VIDEO_NUM_DEVICES 256
#define VIDEO_NAME "video4linux"
@@ -41,17 +42,17 @@
static ssize_t show_index(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%i\n", vfd->index);
+ return sprintf(buf, "%i\n", vdev->index);
}
static ssize_t show_name(struct device *cd,
struct device_attribute *attr, char *buf)
{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct video_device *vdev = to_video_device(cd);
- return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
}
static struct device_attribute video_device_attrs[] = {
@@ -73,64 +74,64 @@ struct video_device *video_device_alloc(void)
}
EXPORT_SYMBOL(video_device_alloc);
-void video_device_release(struct video_device *vfd)
+void video_device_release(struct video_device *vdev)
{
- kfree(vfd);
+ kfree(vdev);
}
EXPORT_SYMBOL(video_device_release);
-void video_device_release_empty(struct video_device *vfd)
+void video_device_release_empty(struct video_device *vdev)
{
/* Do nothing */
/* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL(video_device_release_empty);
-/* Called when the last user of the character device is gone. */
-static void v4l2_chardev_release(struct kobject *kobj)
+static inline void video_get(struct video_device *vdev)
{
- struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
+ get_device(&vdev->dev);
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+ put_device(&vdev->dev);
+}
+
+/* Called when the last user of the video device exits. */
+static void v4l2_device_release(struct device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
mutex_lock(&videodev_lock);
- if (video_device[vfd->minor] != vfd) {
+ if (video_device[vdev->minor] != vdev) {
mutex_unlock(&videodev_lock);
- BUG();
+ /* should not happen */
+ WARN_ON(1);
return;
}
/* Free up this device for reuse */
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[vfd->vfl_type]);
- mutex_unlock(&videodev_lock);
+ video_device[vdev->minor] = NULL;
- /* Release the character device */
- vfd->cdev_release(kobj);
- /* Release video_device and perform other
- cleanups as needed. */
- if (vfd->release)
- vfd->release(vfd);
-}
+ /* Delete the cdev on this minor as well */
+ cdev_del(vdev->cdev);
+ /* Just in case some driver tries to access this from
+ the release() callback. */
+ vdev->cdev = NULL;
-/* The new kobj_type for the character device */
-static struct kobj_type v4l2_ktype_cdev_default = {
- .release = v4l2_chardev_release,
-};
+ /* Mark minor as free */
+ clear_bit(vdev->num, video_nums[vdev->vfl_type]);
-static void video_release(struct device *cd)
-{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
+ mutex_unlock(&videodev_lock);
- /* It's now safe to delete the char device.
- This will either trigger the v4l2_chardev_release immediately (if
- the refcount goes to 0) or later when the last user of the
- character device closes it. */
- cdev_del(&vfd->cdev);
+ /* Release video_device and perform other
+ cleanups as needed. */
+ vdev->release(vdev);
}
static struct class video_class = {
.name = VIDEO_NAME,
.dev_attrs = video_device_attrs,
- .dev_release = video_release,
};
struct video_device *video_devdata(struct file *file)
@@ -139,13 +140,163 @@ struct video_device *video_devdata(struct file *file)
}
EXPORT_SYMBOL(video_devdata);
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->read)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->read(filp, buf, sz, off);
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->write)
+ return -EINVAL;
+ if (video_is_unregistered(vdev))
+ return -EIO;
+ return vdev->fops->write(filp, buf, sz, off);
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->poll || video_is_unregistered(vdev))
+ return DEFAULT_POLLMASK;
+ return vdev->fops->poll(filp, poll);
+}
+
+static int v4l2_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->ioctl(inode, filp, cmd, arg);
+}
+
+static long v4l2_unlocked_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->unlocked_ioctl)
+ return -ENOTTY;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long v4l2_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->compat_ioctl)
+ return -ENOIOCTLCMD;
+ /* Allow ioctl to continue even if the device was unregistered.
+ Things like dequeueing buffers might still be useful. */
+ return vdev->fops->compat_ioctl(filp, cmd, arg);
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+ struct video_device *vdev = video_devdata(filp);
+
+ if (!vdev->fops->mmap ||
+ video_is_unregistered(vdev))
+ return -ENODEV;
+ return vdev->fops->mmap(filp, vm);
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev;
+ int ret;
+
+ /* Check if the video device is available */
+ mutex_lock(&videodev_lock);
+ vdev = video_devdata(filp);
+ /* return ENODEV if the video device has been removed
+ already or if it is not registered anymore. */
+ if (vdev == NULL || video_is_unregistered(vdev)) {
+ mutex_unlock(&videodev_lock);
+ return -ENODEV;
+ }
+ /* and increase the device refcount */
+ video_get(vdev);
+ mutex_unlock(&videodev_lock);
+ ret = vdev->fops->open(inode, filp);
+ /* decrease the refcount in case of an error */
+ if (ret)
+ video_put(vdev);
+ return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = vdev->fops->release(inode, filp);
+
+ /* decrease the refcount unconditionally since the release()
+ return value is ignored. */
+ video_put(vdev);
+ return ret;
+}
+
+static const struct file_operations v4l2_unlocked_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
+static const struct file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .mmap = v4l2_mmap,
+ .ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
/**
* get_index - assign stream number based on parent device
- * @vdev: video_device to assign index number to, vdev->dev should be assigned
- * @num: -1 if auto assign, requested number otherwise
+ * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ * @num: -1 if auto assign, requested number otherwise
*
+ * Note that when this is called the new device has not yet been registered
+ * in the video_device array.
*
- * returns -ENFILE if num is already in use, a free index number if
+ * Returns -ENFILE if num is already in use, a free index number if
* successful.
*/
static int get_index(struct video_device *vdev, int num)
@@ -162,9 +313,12 @@ static int get_index(struct video_device *vdev, int num)
return -EINVAL;
}
+ /* Some drivers do not set the parent. In that case always return 0. */
+ if (vdev->parent == NULL)
+ return 0;
+
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
if (video_device[i] != NULL &&
- video_device[i] != vdev &&
video_device[i]->parent == vdev->parent) {
used |= 1 << video_device[i]->index;
}
@@ -180,17 +334,15 @@ static int get_index(struct video_device *vdev, int num)
return i > max_index ? -ENFILE : i;
}
-static const struct file_operations video_fops;
-
-int video_register_device(struct video_device *vfd, int type, int nr)
+int video_register_device(struct video_device *vdev, int type, int nr)
{
- return video_register_device_index(vfd, type, nr, -1);
+ return video_register_device_index(vdev, type, nr, -1);
}
EXPORT_SYMBOL(video_register_device);
/**
* video_register_device_index - register video4linux devices
- * @vfd: video device structure we want to register
+ * @vdev: video device structure we want to register
* @type: type of device to register
* @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ...
* -1 == first free)
@@ -214,8 +366,7 @@ EXPORT_SYMBOL(video_register_device);
*
* %VFL_TYPE_RADIO - A radio card
*/
-
-int video_register_device_index(struct video_device *vfd, int type, int nr,
+int video_register_device_index(struct video_device *vdev, int type, int nr,
int index)
{
int i = 0;
@@ -223,14 +374,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
- void *priv = video_get_drvdata(vfd);
+ void *priv = video_get_drvdata(vdev);
- /* the release callback MUST be present */
- BUG_ON(!vfd->release);
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+ if (vdev)
+ vdev->minor = -1;
- if (vfd == NULL)
+ /* the release callback MUST be present */
+ WARN_ON(!vdev || !vdev->release);
+ if (!vdev || !vdev->release)
return -EINVAL;
+ /* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
@@ -250,8 +406,12 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -EINVAL;
}
- vfd->vfl_type = type;
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+ if (vdev->v4l2_dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ /* Part 2: find a free minor, kernel number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
@@ -282,10 +442,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
}
#endif
- /* Initialize the character device */
- cdev_init(&vfd->cdev, vfd->fops);
- vfd->cdev.owner = vfd->fops->owner;
- /* pick a minor number */
+ /* Pick a minor number */
mutex_lock(&videodev_lock);
nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
if (nr == minor_cnt)
@@ -309,72 +466,92 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -ENFILE;
}
#endif
- vfd->minor = i + minor_offset;
- vfd->num = nr;
+ vdev->minor = i + minor_offset;
+ vdev->num = nr;
set_bit(nr, video_nums[type]);
- BUG_ON(video_device[vfd->minor]);
- video_device[vfd->minor] = vfd;
-
- ret = get_index(vfd, index);
- vfd->index = ret;
-
+ /* Should not happen since we thought this minor was free */
+ WARN_ON(video_device[vdev->minor] != NULL);
+ ret = vdev->index = get_index(vdev, index);
mutex_unlock(&videodev_lock);
if (ret < 0) {
printk(KERN_ERR "%s: get_index failed\n", __func__);
- goto fail_minor;
+ goto cleanup;
}
- ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
+ /* Part 3: Initialize the character device */
+ vdev->cdev = cdev_alloc();
+ if (vdev->cdev == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ if (vdev->fops->unlocked_ioctl)
+ vdev->cdev->ops = &v4l2_unlocked_fops;
+ else
+ vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->owner = vdev->fops->owner;
+ ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
- goto fail_minor;
+ kfree(vdev->cdev);
+ vdev->cdev = NULL;
+ goto cleanup;
}
- /* sysfs class */
- memset(&vfd->dev, 0, sizeof(vfd->dev));
+
+ /* Part 4: register the device with sysfs */
+ memset(&vdev->dev, 0, sizeof(vdev->dev));
/* The memset above cleared the device's drvdata, so
put back the copy we made earlier. */
- video_set_drvdata(vfd, priv);
- vfd->dev.class = &video_class;
- vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
- if (vfd->parent)
- vfd->dev.parent = vfd->parent;
- sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
- ret = device_register(&vfd->dev);
+ video_set_drvdata(vdev, priv);
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
+ if (vdev->parent)
+ vdev->dev.parent = vdev->parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, nr);
+ ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto del_cdev;
+ goto cleanup;
}
- /* Remember the cdev's release function */
- vfd->cdev_release = vfd->cdev.kobj.ktype->release;
- /* Install our own */
- vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
- return 0;
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
-del_cdev:
- cdev_del(&vfd->cdev);
+ /* Part 5: Activate this minor. The char device can now be used. */
+ mutex_lock(&videodev_lock);
+ video_device[vdev->minor] = vdev;
+ mutex_unlock(&videodev_lock);
+ return 0;
-fail_minor:
+cleanup:
mutex_lock(&videodev_lock);
- video_device[vfd->minor] = NULL;
- clear_bit(vfd->num, video_nums[type]);
+ if (vdev->cdev)
+ cdev_del(vdev->cdev);
+ clear_bit(vdev->num, video_nums[type]);
mutex_unlock(&videodev_lock);
- vfd->minor = -1;
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
return ret;
}
EXPORT_SYMBOL(video_register_device_index);
/**
* video_unregister_device - unregister a video4linux device
- * @vfd: the device to unregister
+ * @vdev: the device to unregister
*
- * This unregisters the passed device and deassigns the minor
- * number. Future open calls will be met with errors.
+ * This unregisters the passed device. Future open calls will
+ * be met with errors.
*/
-
-void video_unregister_device(struct video_device *vfd)
+void video_unregister_device(struct video_device *vdev)
{
- device_unregister(&vfd->dev);
+ /* Check if vdev was ever registered at all */
+ if (!vdev || vdev->minor < 0)
+ return;
+
+ mutex_lock(&videodev_lock);
+ set_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
+ device_unregister(&vdev->dev);
}
EXPORT_SYMBOL(video_unregister_device);
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
new file mode 100644
index 000000000000..9eefde031597
--- /dev/null
+++ b/drivers/media/video/v4l2-device.c
@@ -0,0 +1,86 @@
+/*
+ V4L2 device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute 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/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+
+int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+{
+ if (dev == NULL || v4l2_dev == NULL)
+ return -EINVAL;
+ /* Warn if we apparently re-register a device */
+ WARN_ON(dev_get_drvdata(dev));
+ INIT_LIST_HEAD(&v4l2_dev->subdevs);
+ spin_lock_init(&v4l2_dev->lock);
+ v4l2_dev->dev = dev;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+ dev->driver->name, dev->bus_id);
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register);
+
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd, *next;
+
+ if (v4l2_dev == NULL || v4l2_dev->dev == NULL)
+ return;
+ dev_set_drvdata(v4l2_dev->dev, NULL);
+ /* unregister subdevs */
+ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list)
+ v4l2_device_unregister_subdev(sd);
+
+ v4l2_dev->dev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister);
+
+int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd)
+{
+ /* Check for valid input */
+ if (dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+ /* Warn if we apparently re-register a subdev */
+ WARN_ON(sd->dev);
+ if (!try_module_get(sd->owner))
+ return -ENODEV;
+ sd->dev = dev;
+ spin_lock(&dev->lock);
+ list_add_tail(&sd->list, &dev->subdevs);
+ spin_unlock(&dev->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+
+void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+{
+ /* return if it isn't registered */
+ if (sd == NULL || sd->dev == NULL)
+ return;
+ spin_lock(&sd->dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&sd->dev->lock);
+ sd->dev = NULL;
+ module_put(sd->owner);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 710e1a40c422..b063381f4b3b 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -8,7 +8,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Authors: Alan Cox, <alan@redhat.com> (version 1)
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*/
@@ -393,10 +393,8 @@ video_fix_command(unsigned int cmd)
* Obsolete usercopy function - Should be removed soon
*/
int
-video_usercopy(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg,
- int (*func)(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg))
+video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+ v4l2_kioctl func)
{
char sbuf[128];
void *mbuf = NULL;
@@ -458,7 +456,7 @@ video_usercopy(struct inode *inode, struct file *file,
}
/* call driver */
- err = func(inode, file, cmd, parg);
+ err = func(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
if (is_ext_ctrl) {
@@ -1481,9 +1479,15 @@ static int __video_do_ioctl(struct file *file,
case VIDIOC_G_CROP:
{
struct v4l2_crop *p = arg;
+ __u32 type;
if (!ops->vidioc_g_crop)
break;
+
+ type = p->type;
+ memset(p, 0, sizeof(*p));
+ p->type = type;
+
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
ret = ops->vidioc_g_crop(file, fh, p);
if (!ret)
@@ -1504,10 +1508,16 @@ static int __video_do_ioctl(struct file *file,
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *p = arg;
+ __u32 type;
/*FIXME: Should also show v4l2_fract pixelaspect */
if (!ops->vidioc_cropcap)
break;
+
+ type = p->type;
+ memset(p, 0, sizeof(*p));
+ p->type = type;
+
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
ret = ops->vidioc_cropcap(file, fh, p);
if (!ret) {
@@ -1522,6 +1532,9 @@ static int __video_do_ioctl(struct file *file,
if (!ops->vidioc_g_jpegcomp)
break;
+
+ memset(p, 0, sizeof(*p));
+
ret = ops->vidioc_g_jpegcomp(file, fh, p);
if (!ret)
dbgarg(cmd, "quality=%d, APPn=%d, "
@@ -1749,6 +1762,77 @@ static int __video_do_ioctl(struct file *file,
ret = ops->vidioc_s_hw_freq_seek(file, fh, p);
break;
}
+ case VIDIOC_ENUM_FRAMESIZES:
+ {
+ struct v4l2_frmsizeenum *p = arg;
+
+ if (!ops->vidioc_enum_framesizes)
+ break;
+
+ memset(p, 0, sizeof(*p));
+
+ ret = ops->vidioc_enum_framesizes(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, type=%d ",
+ p->index, p->pixel_format, p->type);
+ switch (p->type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ dbgarg2("width = %d, height=%d\n",
+ p->discrete.width, p->discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ dbgarg2("min %dx%d, max %dx%d, step %dx%d\n",
+ p->stepwise.min_width, p->stepwise.min_height,
+ p->stepwise.step_width, p->stepwise.step_height,
+ p->stepwise.max_width, p->stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ dbgarg2("continuous\n");
+ break;
+ default:
+ dbgarg2("- Unknown type!\n");
+ }
+
+ break;
+ }
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ {
+ struct v4l2_frmivalenum *p = arg;
+
+ if (!ops->vidioc_enum_frameintervals)
+ break;
+
+ memset(p, 0, sizeof(*p));
+
+ ret = ops->vidioc_enum_frameintervals(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
+ p->index, p->pixel_format,
+ p->width, p->height, p->type);
+ switch (p->type) {
+ case V4L2_FRMIVAL_TYPE_DISCRETE:
+ dbgarg2("fps=%d/%d\n",
+ p->discrete.numerator,
+ p->discrete.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_STEPWISE:
+ dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n",
+ p->stepwise.min.numerator,
+ p->stepwise.min.denominator,
+ p->stepwise.max.numerator,
+ p->stepwise.max.denominator,
+ p->stepwise.step.numerator,
+ p->stepwise.step.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+ dbgarg2("continuous\n");
+ break;
+ default:
+ dbgarg2("- Unknown type!\n");
+ }
+ break;
+ }
+
default:
{
if (!ops->vidioc_default)
@@ -1768,7 +1852,7 @@ static int __video_do_ioctl(struct file *file,
return ret;
}
-int __video_ioctl2(struct file *file,
+long __video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
{
char sbuf[128];
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
new file mode 100644
index 000000000000..e3612f29d0df
--- /dev/null
+++ b/drivers/media/video/v4l2-subdev.c
@@ -0,0 +1,110 @@
+/*
+ V4L2 sub-device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute 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/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
+int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ return v4l2_subdev_call(sd, core, querymenu, arg);
+ case VIDIOC_G_CTRL:
+ return v4l2_subdev_call(sd, core, g_ctrl, arg);
+ case VIDIOC_S_CTRL:
+ return v4l2_subdev_call(sd, core, s_ctrl, arg);
+ case VIDIOC_QUERYMENU:
+ return v4l2_subdev_call(sd, core, queryctrl, arg);
+ case VIDIOC_LOG_STATUS:
+ return v4l2_subdev_call(sd, core, log_status);
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_subdev_call(sd, core, g_chip_ident, arg);
+ case VIDIOC_INT_S_STANDBY:
+ return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_RESET:
+ return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_S_GPIO:
+ return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0);
+ case VIDIOC_INT_INIT:
+ return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ return v4l2_subdev_call(sd, core, g_register, arg);
+ case VIDIOC_DBG_S_REGISTER:
+ return v4l2_subdev_call(sd, core, s_register, arg);
+#endif
+
+ case VIDIOC_INT_S_TUNER_MODE:
+ return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg);
+ case AUDC_SET_RADIO:
+ return v4l2_subdev_call(sd, tuner, s_radio);
+ case VIDIOC_S_TUNER:
+ return v4l2_subdev_call(sd, tuner, s_tuner, arg);
+ case VIDIOC_G_TUNER:
+ return v4l2_subdev_call(sd, tuner, g_tuner, arg);
+ case VIDIOC_S_STD:
+ return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg);
+ case VIDIOC_S_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, s_frequency, arg);
+ case VIDIOC_G_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, g_frequency, arg);
+ case TUNER_SET_TYPE_ADDR:
+ return v4l2_subdev_call(sd, tuner, s_type_addr, arg);
+ case TUNER_SET_CONFIG:
+ return v4l2_subdev_call(sd, tuner, s_config, arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg);
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ return v4l2_subdev_call(sd, audio, s_routing, arg);
+ case VIDIOC_INT_I2S_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg);
+
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ return v4l2_subdev_call(sd, video, s_routing, arg);
+ case VIDIOC_INT_S_CRYSTAL_FREQ:
+ return v4l2_subdev_call(sd, video, s_crystal_freq, arg);
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ return v4l2_subdev_call(sd, video, decode_vbi_line, arg);
+ case VIDIOC_INT_S_VBI_DATA:
+ return v4l2_subdev_call(sd, video, s_vbi_data, arg);
+ case VIDIOC_INT_G_VBI_DATA:
+ return v4l2_subdev_call(sd, video, g_vbi_data, arg);
+ case VIDIOC_G_SLICED_VBI_CAP:
+ return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
+ case VIDIOC_S_FMT:
+ return v4l2_subdev_call(sd, video, s_fmt, arg);
+ case VIDIOC_G_FMT:
+ return v4l2_subdev_call(sd, video, g_fmt, arg);
+ case VIDIOC_INT_S_STD_OUTPUT:
+ return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg);
+ case VIDIOC_STREAMON:
+ return v4l2_subdev_call(sd, video, s_stream, 1);
+ case VIDIOC_STREAMOFF:
+ return v4l2_subdev_call(sd, video, s_stream, 0);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_command);
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 1efc5f3462c6..a72a361daade 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -4237,8 +4237,7 @@ error:
return ret;
}
-static int vino_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int vino_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct vino_channel_settings *vcs = video_drvdata(file);
@@ -4353,7 +4352,7 @@ static int vino_ioctl(struct inode *inode, struct file *file,
if (mutex_lock_interruptible(&vcs->mutex))
return -EINTR;
- ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl);
+ ret = video_usercopy(file, cmd, arg, vino_do_ioctl);
mutex_unlock(&vcs->mutex);
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
index 577956c5410b..f72b859486ad 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/video/vp27smpx.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -40,13 +40,20 @@ MODULE_LICENSE("GPL");
/* ----------------------------------------------------------------------- */
struct vp27smpx_state {
+ struct v4l2_subdev sd;
int radio;
u32 audmode;
};
-static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
+static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
+ return container_of(sd, struct vp27smpx_state, sd);
+}
+
+static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode)
+{
+ struct vp27smpx_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 data[3] = { 0x00, 0x00, 0x04 };
switch (audmode) {
@@ -63,55 +70,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
}
if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
- v4l_err(client, "%s: I/O error setting audmode\n",
- client->name);
+ v4l2_err(sd, "I/O error setting audmode\n");
else
state->audmode = audmode;
}
-static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int vp27smpx_s_radio(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
+ struct vp27smpx_state *state = to_state(sd);
- switch (cmd) {
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_S_STD:
- state->radio = 0;
- break;
+static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_S_TUNER:
- if (!state->radio)
- vp27smpx_set_audmode(client, vt->audmode);
- break;
+ state->radio = 0;
+ return 0;
+}
- case VIDIOC_G_TUNER:
- if (state->radio)
- break;
- vt->audmode = state->audmode;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
+static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_VP27SMPX, 0);
+ if (!state->radio)
+ vp27smpx_set_audmode(sd, vt->audmode);
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Audio Mode: %u%s\n", state->audmode,
- state->radio ? " (Radio)" : "");
- break;
+static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ if (state->radio)
+ return 0;
+ vt->audmode = state->audmode;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- default:
- return -EINVAL;
- }
+static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0);
+}
+
+static int vp27smpx_log_status(struct v4l2_subdev *sd)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode,
+ state->radio ? " (Radio)" : "");
return 0;
}
+static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
+ .log_status = vp27smpx_log_status,
+ .g_chip_ident = vp27smpx_g_chip_ident,
+};
+
+static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
+ .s_radio = vp27smpx_s_radio,
+ .s_std = vp27smpx_s_std,
+ .s_tuner = vp27smpx_s_tuner,
+ .g_tuner = vp27smpx_g_tuner,
+};
+
+static const struct v4l2_subdev_ops vp27smpx_ops = {
+ .core = &vp27smpx_core_ops,
+ .tuner = &vp27smpx_tuner_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -125,6 +166,7 @@ static int vp27smpx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vp27smpx_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -136,17 +178,21 @@ static int vp27smpx_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops);
state->audmode = V4L2_TUNER_MODE_STEREO;
- i2c_set_clientdata(client, state);
/* initialize vp27smpx */
- vp27smpx_set_audmode(client, state->audmode);
+ vp27smpx_set_audmode(sd, state->audmode);
return 0;
}
static int vp27smpx_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index b2dbe48a92bb..56c570c267ea 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -727,8 +727,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data)
* Video4linux interfacing
*/
-static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int w9966_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct w9966_dev *cam = video_drvdata(file);
@@ -881,7 +880,7 @@ static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
static int w9966_v4l_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl);
+ return video_usercopy(file, cmd, arg, w9966_v4l_do_ioctl);
}
// Capture data
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index 54ac3fe26ec2..12a31e7a5f6d 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
@@ -52,6 +52,7 @@ enum {
};
struct wm8739_state {
+ struct v4l2_subdev sd;
u32 clock_freq;
u8 muted;
u16 volume;
@@ -60,43 +61,49 @@ struct wm8739_state {
u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
+static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wm8739_state, sd);
+}
+
/* ------------------------------------------------------------------------ */
-static int wm8739_write(struct i2c_client *client, int reg, u16 val)
+static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
- v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val);
for (i = 0; i < 3; i++)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct i2c_client *client)
+static void wm8739_set_audio(struct v4l2_subdev *sd)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
u16 mute = state->muted ? 0x80 : 0;
/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
* Default setting: 0x17 = 0 dB
*/
- wm8739_write(client, R0, (state->vol_l & 0x1f) | mute);
- wm8739_write(client, R1, (state->vol_r & 0x1f) | mute);
+ wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
}
-static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -117,9 +124,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
switch (ctrl->id) {
@@ -147,7 +154,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
state->vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
@@ -186,77 +193,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
/* ------------------------------------------------------------------------ */
-static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- {
- u32 audiofreq = *(u32 *)arg;
-
- state->clock_freq = audiofreq;
- /* de-activate */
- wm8739_write(client, R9, 0x000);
- switch (audiofreq) {
- case 44100:
- /* 256fps, fs=44.1k */
- wm8739_write(client, R8, 0x020);
- break;
- case 48000:
- /* 256fps, fs=48k */
- wm8739_write(client, R8, 0x000);
- break;
- case 32000:
- /* 256fps, fs=32k */
- wm8739_write(client, R8, 0x018);
- break;
- default:
- break;
- }
- /* activate */
- wm8739_write(client, R9, 0x001);
+ struct wm8739_state *state = to_state(sd);
+
+ state->clock_freq = audiofreq;
+ /* de-activate */
+ wm8739_write(sd, R9, 0x000);
+ switch (audiofreq) {
+ case 44100:
+ /* 256fps, fs=44.1k */
+ wm8739_write(sd, R8, 0x020);
+ break;
+ case 48000:
+ /* 256fps, fs=48k */
+ wm8739_write(sd, R8, 0x000);
+ break;
+ case 32000:
+ /* 256fps, fs=32k */
+ wm8739_write(sd, R8, 0x018);
+ break;
+ default:
break;
}
+ /* activate */
+ wm8739_write(sd, R9, 0x001);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return wm8739_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return wm8739_set_ctrl(client, arg);
+static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
- if (qc->id && qc->id == wm8739_qctrl[i].id) {
- memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
- }
+ for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
+ if (qc->id && qc->id == wm8739_qctrl[i].id) {
+ memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8739, 0);
+static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
- v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f,
- state->muted ? " (muted)" : "");
- v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f,
- state->muted ? " (muted)" : "");
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0);
+}
- default:
- return -EINVAL;
- }
+static int wm8739_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8739_state *state = to_state(sd);
+ v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
+ v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
+ state->muted ? " (muted)" : "");
+ v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
+ state->muted ? " (muted)" : "");
return 0;
}
+static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops wm8739_core_ops = {
+ .log_status = wm8739_log_status,
+ .g_chip_ident = wm8739_g_chip_ident,
+ .queryctrl = wm8739_queryctrl,
+ .g_ctrl = wm8739_g_ctrl,
+ .s_ctrl = wm8739_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
+ .s_clock_freq = wm8739_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops wm8739_ops = {
+ .core = &wm8739_core_ops,
+ .audio = &wm8739_audio_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -265,6 +284,7 @@ static int wm8739_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8739_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -276,6 +296,8 @@ static int wm8739_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
state->vol_l = 0x17; /* 0dB */
state->vol_r = 0x17; /* 0dB */
state->muted = 0;
@@ -283,31 +305,33 @@ static int wm8739_probe(struct i2c_client *client,
/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
state->volume = ((long)state->vol_l + 1) * 65535 / 31;
state->clock_freq = 48000;
- i2c_set_clientdata(client, state);
/* Initialize wm8739 */
/* reset */
- wm8739_write(client, R15, 0x00);
+ wm8739_write(sd, R15, 0x00);
/* filter setting, high path, offet clear */
- wm8739_write(client, R5, 0x000);
+ wm8739_write(sd, R5, 0x000);
/* ADC, OSC, Power Off mode Disable */
- wm8739_write(client, R6, 0x000);
+ wm8739_write(sd, R6, 0x000);
/* Digital Audio interface format:
Enable Master mode, 24 bit, MSB first/left justified */
- wm8739_write(client, R7, 0x049);
+ wm8739_write(sd, R7, 0x049);
/* sampling control: normal, 256fs, 48KHz sampling rate */
- wm8739_write(client, R8, 0x000);
+ wm8739_write(sd, R8, 0x000);
/* activate */
- wm8739_write(client, R9, 0x001);
+ wm8739_write(sd, R9, 0x001);
/* set volume/mute */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
static int wm8739_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 48df661d4fc3..d0220b0ec0bc 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -32,7 +32,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
@@ -54,16 +54,23 @@ enum {
};
struct wm8775_state {
+ struct v4l2_subdev sd;
u8 input; /* Last selected input (0-0xf) */
u8 muted;
};
-static int wm8775_write(struct i2c_client *client, int reg, u16 val)
+static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct wm8775_state, sd);
+}
+
+static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -71,84 +78,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct wm8775_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = state->input;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 4 inputs and one output. Zero or more inputs
- are multiplexed together to the output. Hence there are
- 16 combinations.
- If only one input is active (the normal case) then the
- input values 1, 2, 4 or 8 should be used. */
- if (route->input > 15) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- state->input = route->input;
- if (state->muted)
- break;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- if (!state->muted)
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8775, 0);
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %d%s\n", state->input,
- state->muted ? " (muted)" : "");
- break;
-
- case VIDIOC_S_FREQUENCY:
- /* If I remove this, then it can happen that I have no
- sound the first time I tune from static to a valid channel.
- It's difficult to reproduce and is almost certainly related
- to the zero cross detect circuit. */
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- default:
+ struct wm8775_state *state = to_state(sd);
+
+ /* There are 4 inputs and one output. Zero or more inputs
+ are multiplexed together to the output. Hence there are
+ 16 combinations.
+ If only one input is active (the normal case) then the
+ input values 1, 2, 4 or 8 should be used. */
+ if (route->input > 15) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ state->input = route->input;
+ if (state->muted)
+ return 0;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ if (!state->muted)
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
+}
+
+static int wm8775_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %d%s\n", state->input,
+ state->muted ? " (muted)" : "");
return 0;
}
+static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ /* If I remove this, then it can happen that I have no
+ sound the first time I tune from static to a valid channel.
+ It's difficult to reproduce and is almost certainly related
+ to the zero cross detect circuit. */
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops wm8775_core_ops = {
+ .log_status = wm8775_log_status,
+ .g_chip_ident = wm8775_g_chip_ident,
+ .g_ctrl = wm8775_g_ctrl,
+ .s_ctrl = wm8775_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
+ .s_frequency = wm8775_s_frequency,
+};
+
+static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
+ .s_routing = wm8775_s_routing,
+};
+
+static const struct v4l2_subdev_ops wm8775_ops = {
+ .core = &wm8775_core_ops,
+ .tuner = &wm8775_tuner_ops,
+ .audio = &wm8775_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -162,56 +202,61 @@ static int wm8775_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8775_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n",
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize wm8775 */
/* RESET */
- wm8775_write(client, R23, 0x000);
+ wm8775_write(sd, R23, 0x000);
/* Disable zero cross detect timeout */
- wm8775_write(client, R7, 0x000);
+ wm8775_write(sd, R7, 0x000);
/* Left justified, 24-bit mode */
- wm8775_write(client, R11, 0x021);
+ wm8775_write(sd, R11, 0x021);
/* Master mode, clock ratio 256fs */
- wm8775_write(client, R12, 0x102);
+ wm8775_write(sd, R12, 0x102);
/* Powered up */
- wm8775_write(client, R13, 0x000);
+ wm8775_write(sd, R13, 0x000);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R14, 0x1d4);
+ wm8775_write(sd, R14, 0x1d4);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R15, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
/* ALC Stereo, ALC target level -1dB FS max gain +8dB */
- wm8775_write(client, R16, 0x1bf);
+ wm8775_write(sd, R16, 0x1bf);
/* Enable gain control, use zero cross detection,
ALC hold time 42.6 ms */
- wm8775_write(client, R17, 0x185);
+ wm8775_write(sd, R17, 0x185);
/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
- wm8775_write(client, R18, 0x0a2);
+ wm8775_write(sd, R18, 0x0a2);
/* Enable noise gate, threshold -72dBfs */
- wm8775_write(client, R19, 0x005);
+ wm8775_write(sd, R19, 0x005);
/* Transient window 4ms, lower PGA gain limit -1dB */
- wm8775_write(client, R20, 0x07a);
+ wm8775_write(sd, R20, 0x07a);
/* LRBOTH = 1, use input 2. */
- wm8775_write(client, R21, 0x102);
+ wm8775_write(sd, R21, 0x102);
return 0;
}
static int wm8775_remove(struct i2c_client *client)
{
- kfree(i2c_get_clientdata(client));
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c
index 9fc581707638..9d00e6056491 100644
--- a/drivers/media/video/zc0301/zc0301_core.c
+++ b/drivers/media/video/zc0301/zc0301_core.c
@@ -1020,7 +1020,7 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg)
strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0)
- strlcpy(cap.bus_info, cam->usbdev->dev.bus_id,
+ strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev),
sizeof(cap.bus_info));
if (copy_to_user(arg, &cap, sizeof(cap)))
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
index fa5f2f8f518a..05f39195372e 100644
--- a/drivers/media/video/zoran/zoran_card.c
+++ b/drivers/media/video/zoran/zoran_card.c
@@ -153,12 +153,6 @@ MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
MODULE_AUTHOR("Serguei Miridonov");
MODULE_LICENSE("GPL");
-static struct pci_device_id zr36067_pci_tbl[] = {
- {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0}
-};
-MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
int zoran_num; /* number of Buzs in use */
struct zoran *zoran[BUZ_MAX];
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index db11ab9e60da..00b97d97aeaa 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -1940,11 +1940,7 @@ zoran_set_input (struct zoran *zr,
* ioctl routine
*/
-static int
-zoran_do_ioctl (struct inode *inode,
- struct file *file,
- unsigned int cmd,
- void *arg)
+static int zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct zoran_fh *fh = file->private_data;
struct zoran *zr = fh->zr;
@@ -4201,7 +4197,7 @@ zoran_ioctl (struct inode *inode,
unsigned int cmd,
unsigned long arg)
{
- return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl);
+ return video_usercopy(file, cmd, arg, zoran_do_ioctl);
}
static unsigned int
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index d6a0074b9dc3..c4e8b9aa3827 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -952,7 +952,6 @@ mpt_put_msg_frame_hi_pri(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mpt_free_msg_frame - Place MPT request frame back on FreeQ.
- * @handle: Handle of registered MPT protocol driver
* @ioc: Pointer to MPT adapter structure
* @mf: Pointer to MPT request frame
*
@@ -4563,7 +4562,7 @@ WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag)
failcnt++;
hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF);
/* don't overflow our IOC hs_reply[] buffer! */
- if (u16cnt < sizeof(ioc->hs_reply) / sizeof(ioc->hs_reply[0]))
+ if (u16cnt < ARRAY_SIZE(ioc->hs_reply))
hs_reply[u16cnt] = hword;
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
}
@@ -5422,7 +5421,6 @@ mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhysDiskPage0_t
/**
* mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
* @ioc: Pointer to a Adapter Strucutre
- * @portnum: IOC port number
*
* Return:
* 0 on success
@@ -6939,7 +6937,6 @@ mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info)
/**
* mpt_spi_log_info - Log information returned from SCSI Parallel IOC.
* @ioc: Pointer to MPT_ADAPTER structure
- * @mr: Pointer to MPT reply frame
* @log_info: U32 LogInfo word from the IOC
*
* Refer to lsi/sp_log.h.
@@ -7176,7 +7173,7 @@ union loginfo_type {
sas_loginfo.loginfo = log_info;
if ((sas_loginfo.dw.bus_type != 3 /*SAS*/) &&
- (sas_loginfo.dw.originator < sizeof(originator_str)/sizeof(char*)))
+ (sas_loginfo.dw.originator < ARRAY_SIZE(originator_str)))
return;
originator_desc = originator_str[sas_loginfo.dw.originator];
@@ -7185,21 +7182,21 @@ union loginfo_type {
case 0: /* IOP */
if (sas_loginfo.dw.code <
- sizeof(iop_code_str)/sizeof(char*))
+ ARRAY_SIZE(iop_code_str))
code_desc = iop_code_str[sas_loginfo.dw.code];
break;
case 1: /* PL */
if (sas_loginfo.dw.code <
- sizeof(pl_code_str)/sizeof(char*))
+ ARRAY_SIZE(pl_code_str))
code_desc = pl_code_str[sas_loginfo.dw.code];
break;
case 2: /* IR */
if (sas_loginfo.dw.code >=
- sizeof(ir_code_str)/sizeof(char*))
+ ARRAY_SIZE(ir_code_str))
break;
code_desc = ir_code_str[sas_loginfo.dw.code];
if (sas_loginfo.dw.subcode >=
- sizeof(raid_sub_code_str)/sizeof(char*))
+ ARRAY_SIZE(raid_sub_code_str))
break;
if (sas_loginfo.dw.code == 0)
sub_code_desc =
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index 603ffd008c73..a13f6eecd25b 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -815,7 +815,7 @@ mpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
* @priority: 0 = put it on the timer queue, 1 = put it on the immediate queue
*/
{
- struct mpt_lan_priv *priv = dev->priv;
+ struct mpt_lan_priv *priv = netdev_priv(dev);
if (test_and_set_bit(0, &priv->post_buckets_active) == 0) {
if (priority) {
@@ -834,7 +834,7 @@ mpt_lan_wake_post_buckets_task(struct net_device *dev, int priority)
static int
mpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb)
{
- struct mpt_lan_priv *priv = dev->priv;
+ struct mpt_lan_priv *priv = netdev_priv(dev);
skb->protocol = mpt_lan_type_trans(skb, dev);
@@ -866,7 +866,7 @@ mpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb)
static int
mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg)
{
- struct mpt_lan_priv *priv = dev->priv;
+ struct mpt_lan_priv *priv = netdev_priv(dev);
MPT_ADAPTER *mpt_dev = priv->mpt_dev;
struct sk_buff *skb, *old_skb;
unsigned long flags;
@@ -921,7 +921,7 @@ static int
mpt_lan_receive_post_free(struct net_device *dev,
LANReceivePostReply_t *pRecvRep)
{
- struct mpt_lan_priv *priv = dev->priv;
+ struct mpt_lan_priv *priv = netdev_priv(dev);
MPT_ADAPTER *mpt_dev = priv->mpt_dev;
unsigned long flags;
struct sk_buff *skb;
@@ -976,7 +976,7 @@ static int
mpt_lan_receive_post_reply(struct net_device *dev,
LANReceivePostReply_t *pRecvRep)
{
- struct mpt_lan_priv *priv = dev->priv;
+ struct mpt_lan_priv *priv = netdev_priv(dev);
MPT_ADAPTER *mpt_dev = priv->mpt_dev;
struct sk_buff *skb, *old_skb;
unsigned long flags;
@@ -1427,11 +1427,9 @@ mptlan_probe(struct pci_dev *pdev, const struct pci_device_id *id)
printk(KERN_INFO MYNAM ": %s: Fusion MPT LAN device "
"registered as '%s'\n", ioc->name, dev->name);
printk(KERN_INFO MYNAM ": %s/%s: "
- "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ "LanAddr = %pM\n",
IOC_AND_NETDEV_NAMES_s_s(dev),
- dev->dev_addr[0], dev->dev_addr[1],
- dev->dev_addr[2], dev->dev_addr[3],
- dev->dev_addr[4], dev->dev_addr[5]);
+ dev->dev_addr);
ioc->netdev = dev;
@@ -1516,9 +1514,8 @@ mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
printk (KERN_WARNING MYNAM ": %s: WARNING - Broadcast swap F/W bug detected!\n",
NETDEV_PTR_TO_IOC_NAME_s(dev));
- printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %02x:%02x:%02x:%02x:%02x:%02x\n",
- fch->saddr[0], fch->saddr[1], fch->saddr[2],
- fch->saddr[3], fch->saddr[4], fch->saddr[5]);
+ printk (KERN_WARNING MYNAM ": Please update sender @ MAC_addr = %pM\n",
+ fch->saddr);
}
if (*fch->daddr & 1) {
@@ -1537,7 +1534,6 @@ mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev)
fcllc = (struct fcllc *)skb->data;
-
/* Strip the SNAP header from ARP packets since we don't
* pass them through to the 802.2/SNAP layers.
*/
diff --git a/drivers/message/fusion/mptlan.h b/drivers/message/fusion/mptlan.h
index 33927ee7dc3b..c171afa93239 100644
--- a/drivers/message/fusion/mptlan.h
+++ b/drivers/message/fusion/mptlan.h
@@ -122,7 +122,7 @@ MODULE_DESCRIPTION(LANAME);
#define dlprintk(x)
#endif
-#define NETDEV_TO_LANPRIV_PTR(d) ((struct mpt_lan_priv *)(d)->priv)
+#define NETDEV_TO_LANPRIV_PTR(d) ((struct mpt_lan_priv *)netdev_priv(d))
#define NETDEV_PTR_TO_IOC_NAME_s(d) (NETDEV_TO_LANPRIV_PTR(d)->mpt_dev->name)
#define IOC_AND_NETDEV_NAMES_s_s(d) NETDEV_PTR_TO_IOC_NAME_s(d), (d)->name
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index d62fd4f6b52e..ee090413e598 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -2008,6 +2008,9 @@ mptscsih_host_reset(struct scsi_cmnd *SCpnt)
return FAILED;
}
+ /* make sure we have no outstanding commands at this stage */
+ mptscsih_flush_running_cmds(hd);
+
ioc = hd->ioc;
printk(MYIOC_s_INFO_FMT "attempting host reset! (sc=%p)\n",
ioc->name, SCpnt);
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index e4c0db4dc7b1..9e485459f63b 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -474,9 +474,9 @@ static __init int asic3_gpio_probe(struct platform_device *pdev,
u16 dir_reg[ASIC3_NUM_GPIO_BANKS];
int i;
- memzero(alt_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
- memzero(out_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
- memzero(dir_reg, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
+ memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
/* Enable all GPIOs */
asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff);
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
index b4ed57e02729..6063dc2b52e8 100644
--- a/drivers/mfd/mcp-core.c
+++ b/drivers/mfd/mcp-core.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <asm/system.h>
#include "mcp.h"
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 28380b20bc70..62b32dabf629 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/system.h>
diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c
index 61aeaf79640d..86fed4870f93 100644
--- a/drivers/mfd/ucb1x00-assabet.c
+++ b/drivers/mfd/ucb1x00-assabet.c
@@ -15,7 +15,7 @@
#include <linux/proc_fs.h>
#include <linux/device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include "ucb1x00.h"
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index a316f1b75933..6860c924f364 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -25,7 +25,7 @@
#include <linux/device.h>
#include <linux/mutex.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include "ucb1x00.h"
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 44762ca86a8d..61b7d3eb9a2f 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -31,7 +31,7 @@
#include <linux/slab.h>
#include <linux/kthread.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/collie.h>
#include <asm/mach-types.h>
diff --git a/drivers/misc/sgi-gru/gruprocfs.c b/drivers/misc/sgi-gru/gruprocfs.c
index 533923f83f1a..73b0ca061bb5 100644
--- a/drivers/misc/sgi-gru/gruprocfs.c
+++ b/drivers/misc/sgi-gru/gruprocfs.c
@@ -317,7 +317,6 @@ int gru_proc_init(void)
{
struct proc_entry *p;
- proc_mkdir("sgi_uv", NULL);
proc_gru = proc_mkdir("sgi_uv/gru", NULL);
for (p = proc_files; p->name; p++)
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
index ed1722e50049..7b4cbd5e03e9 100644
--- a/drivers/misc/sgi-xp/xp.h
+++ b/drivers/misc/sgi-xp/xp.h
@@ -194,9 +194,10 @@ enum xp_retval {
xpGruSendMqError, /* 59: gru send message queue related error */
xpBadChannelNumber, /* 60: invalid channel number */
- xpBadMsgType, /* 60: invalid message type */
+ xpBadMsgType, /* 61: invalid message type */
+ xpBiosError, /* 62: BIOS error */
- xpUnknownReason /* 61: unknown reason - must be last in enum */
+ xpUnknownReason /* 63: unknown reason - must be last in enum */
};
/*
@@ -345,6 +346,8 @@ extern unsigned long (*xp_pa) (void *);
extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
size_t);
extern int (*xp_cpu_to_nasid) (int);
+extern enum xp_retval (*xp_expand_memprotect) (unsigned long, unsigned long);
+extern enum xp_retval (*xp_restrict_memprotect) (unsigned long, unsigned long);
extern u64 xp_nofault_PIOR_target;
extern int xp_nofault_PIOR(void *);
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c
index 66a1d19e08ad..9a2e77172d94 100644
--- a/drivers/misc/sgi-xp/xp_main.c
+++ b/drivers/misc/sgi-xp/xp_main.c
@@ -51,6 +51,13 @@ EXPORT_SYMBOL_GPL(xp_remote_memcpy);
int (*xp_cpu_to_nasid) (int cpuid);
EXPORT_SYMBOL_GPL(xp_cpu_to_nasid);
+enum xp_retval (*xp_expand_memprotect) (unsigned long phys_addr,
+ unsigned long size);
+EXPORT_SYMBOL_GPL(xp_expand_memprotect);
+enum xp_retval (*xp_restrict_memprotect) (unsigned long phys_addr,
+ unsigned long size);
+EXPORT_SYMBOL_GPL(xp_restrict_memprotect);
+
/*
* xpc_registrations[] keeps track of xpc_connect()'s done by the kernel-level
* users of XPC.
diff --git a/drivers/misc/sgi-xp/xp_sn2.c b/drivers/misc/sgi-xp/xp_sn2.c
index 1440134caf31..fb3ec9d735a9 100644
--- a/drivers/misc/sgi-xp/xp_sn2.c
+++ b/drivers/misc/sgi-xp/xp_sn2.c
@@ -120,6 +120,38 @@ xp_cpu_to_nasid_sn2(int cpuid)
return cpuid_to_nasid(cpuid);
}
+static enum xp_retval
+xp_expand_memprotect_sn2(unsigned long phys_addr, unsigned long size)
+{
+ u64 nasid_array = 0;
+ int ret;
+
+ ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1,
+ &nasid_array);
+ if (ret != 0) {
+ dev_err(xp, "sn_change_memprotect(,, "
+ "SN_MEMPROT_ACCESS_CLASS_1,) failed ret=%d\n", ret);
+ return xpSalError;
+ }
+ return xpSuccess;
+}
+
+static enum xp_retval
+xp_restrict_memprotect_sn2(unsigned long phys_addr, unsigned long size)
+{
+ u64 nasid_array = 0;
+ int ret;
+
+ ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0,
+ &nasid_array);
+ if (ret != 0) {
+ dev_err(xp, "sn_change_memprotect(,, "
+ "SN_MEMPROT_ACCESS_CLASS_0,) failed ret=%d\n", ret);
+ return xpSalError;
+ }
+ return xpSuccess;
+}
+
enum xp_retval
xp_init_sn2(void)
{
@@ -132,6 +164,8 @@ xp_init_sn2(void)
xp_pa = xp_pa_sn2;
xp_remote_memcpy = xp_remote_memcpy_sn2;
xp_cpu_to_nasid = xp_cpu_to_nasid_sn2;
+ xp_expand_memprotect = xp_expand_memprotect_sn2;
+ xp_restrict_memprotect = xp_restrict_memprotect_sn2;
return xp_register_nofault_code_sn2();
}
diff --git a/drivers/misc/sgi-xp/xp_uv.c b/drivers/misc/sgi-xp/xp_uv.c
index d9f7ce2510bc..d238576b26fa 100644
--- a/drivers/misc/sgi-xp/xp_uv.c
+++ b/drivers/misc/sgi-xp/xp_uv.c
@@ -15,6 +15,11 @@
#include <linux/device.h>
#include <asm/uv/uv_hub.h>
+#if defined CONFIG_X86_64
+#include <asm/uv/bios.h>
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+#include <asm/sn/sn_sal.h>
+#endif
#include "../sgi-gru/grukservices.h"
#include "xp.h"
@@ -49,18 +54,79 @@ xp_cpu_to_nasid_uv(int cpuid)
return UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpuid));
}
+static enum xp_retval
+xp_expand_memprotect_uv(unsigned long phys_addr, unsigned long size)
+{
+ int ret;
+
+#if defined CONFIG_X86_64
+ ret = uv_bios_change_memprotect(phys_addr, size, UV_MEMPROT_ALLOW_RW);
+ if (ret != BIOS_STATUS_SUCCESS) {
+ dev_err(xp, "uv_bios_change_memprotect(,, "
+ "UV_MEMPROT_ALLOW_RW) failed, ret=%d\n", ret);
+ return xpBiosError;
+ }
+
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ u64 nasid_array;
+
+ ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_1,
+ &nasid_array);
+ if (ret != 0) {
+ dev_err(xp, "sn_change_memprotect(,, "
+ "SN_MEMPROT_ACCESS_CLASS_1,) failed ret=%d\n", ret);
+ return xpSalError;
+ }
+#else
+ #error not a supported configuration
+#endif
+ return xpSuccess;
+}
+
+static enum xp_retval
+xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size)
+{
+ int ret;
+
+#if defined CONFIG_X86_64
+ ret = uv_bios_change_memprotect(phys_addr, size,
+ UV_MEMPROT_RESTRICT_ACCESS);
+ if (ret != BIOS_STATUS_SUCCESS) {
+ dev_err(xp, "uv_bios_change_memprotect(,, "
+ "UV_MEMPROT_RESTRICT_ACCESS) failed, ret=%d\n", ret);
+ return xpBiosError;
+ }
+
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ u64 nasid_array;
+
+ ret = sn_change_memprotect(phys_addr, size, SN_MEMPROT_ACCESS_CLASS_0,
+ &nasid_array);
+ if (ret != 0) {
+ dev_err(xp, "sn_change_memprotect(,, "
+ "SN_MEMPROT_ACCESS_CLASS_0,) failed ret=%d\n", ret);
+ return xpSalError;
+ }
+#else
+ #error not a supported configuration
+#endif
+ return xpSuccess;
+}
+
enum xp_retval
xp_init_uv(void)
{
BUG_ON(!is_uv());
xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
- xp_partition_id = 0; /* !!! not correct value */
- xp_region_size = 0; /* !!! not correct value */
+ xp_partition_id = sn_partition_id;
+ xp_region_size = sn_region_size;
xp_pa = xp_pa_uv;
xp_remote_memcpy = xp_remote_memcpy_uv;
xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
+ xp_expand_memprotect = xp_expand_memprotect_uv;
+ xp_restrict_memprotect = xp_restrict_memprotect_uv;
return xpSuccess;
}
diff --git a/drivers/misc/sgi-xp/xpc.h b/drivers/misc/sgi-xp/xpc.h
index 619208d61862..a5bd658c2e83 100644
--- a/drivers/misc/sgi-xp/xpc.h
+++ b/drivers/misc/sgi-xp/xpc.h
@@ -181,6 +181,18 @@ struct xpc_vars_part_sn2 {
xpc_nasid_mask_nlongs))
/*
+ * Info pertinent to a GRU message queue using a watch list for irq generation.
+ */
+struct xpc_gru_mq_uv {
+ void *address; /* address of GRU message queue */
+ unsigned int order; /* size of GRU message queue as a power of 2 */
+ int irq; /* irq raised when message is received in mq */
+ int mmr_blade; /* blade where watchlist was allocated from */
+ unsigned long mmr_offset; /* offset of irq mmr located on mmr_blade */
+ int watchlist_num; /* number of watchlist allocatd by BIOS */
+};
+
+/*
* The activate_mq is used to send/receive GRU messages that affect XPC's
* heartbeat, partition active state, and channel state. This is UV only.
*/
diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c
index b4882ccf6344..73b7fb8de47a 100644
--- a/drivers/misc/sgi-xp/xpc_sn2.c
+++ b/drivers/misc/sgi-xp/xpc_sn2.c
@@ -553,22 +553,17 @@ static u64 xpc_prot_vec_sn2[MAX_NUMNODES];
static enum xp_retval
xpc_allow_amo_ops_sn2(struct amo *amos_page)
{
- u64 nasid_array = 0;
- int ret;
+ enum xp_retval ret = xpSuccess;
/*
* On SHUB 1.1, we cannot call sn_change_memprotect() since the BIST
* collides with memory operations. On those systems we call
* xpc_allow_amo_ops_shub_wars_1_1_sn2() instead.
*/
- if (!enable_shub_wars_1_1()) {
- ret = sn_change_memprotect(ia64_tpa((u64)amos_page), PAGE_SIZE,
- SN_MEMPROT_ACCESS_CLASS_1,
- &nasid_array);
- if (ret != 0)
- return xpSalError;
- }
- return xpSuccess;
+ if (!enable_shub_wars_1_1())
+ ret = xp_expand_memprotect(ia64_tpa((u64)amos_page), PAGE_SIZE);
+
+ return ret;
}
/*
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index 1ac694c01623..91a55b1b1037 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -18,7 +18,15 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/err.h>
#include <asm/uv/uv_hub.h>
+#if defined CONFIG_X86_64
+#include <asm/uv/bios.h>
+#include <asm/uv/uv_irq.h>
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+#include <asm/sn/intr.h>
+#include <asm/sn/sn_sal.h>
+#endif
#include "../sgi-gru/gru.h"
#include "../sgi-gru/grukservices.h"
#include "xpc.h"
@@ -27,15 +35,17 @@ static atomic64_t xpc_heartbeat_uv;
static DECLARE_BITMAP(xpc_heartbeating_to_mask_uv, XP_MAX_NPARTITIONS_UV);
#define XPC_ACTIVATE_MSG_SIZE_UV (1 * GRU_CACHE_LINE_BYTES)
-#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES)
+#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
+ XPC_ACTIVATE_MSG_SIZE_UV)
+#define XPC_ACTIVATE_IRQ_NAME "xpc_activate"
-#define XPC_ACTIVATE_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_ACTIVATE_MSG_SIZE_UV)
-#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
- XPC_NOTIFY_MSG_SIZE_UV)
+#define XPC_NOTIFY_MSG_SIZE_UV (2 * GRU_CACHE_LINE_BYTES)
+#define XPC_NOTIFY_MQ_SIZE_UV (4 * XP_MAX_NPARTITIONS_UV * \
+ XPC_NOTIFY_MSG_SIZE_UV)
+#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
-static void *xpc_activate_mq_uv;
-static void *xpc_notify_mq_uv;
+static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
+static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
static int
xpc_setup_partitions_sn_uv(void)
@@ -52,62 +62,209 @@ xpc_setup_partitions_sn_uv(void)
return 0;
}
-static void *
-xpc_create_gru_mq_uv(unsigned int mq_size, int cpuid, unsigned int irq,
+static int
+xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
+{
+#if defined CONFIG_X86_64
+ mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset);
+ if (mq->irq < 0) {
+ dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
+ mq->irq);
+ }
+
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ int mmr_pnode;
+ unsigned long mmr_value;
+
+ if (strcmp(irq_name, XPC_ACTIVATE_IRQ_NAME) == 0)
+ mq->irq = SGI_XPC_ACTIVATE;
+ else if (strcmp(irq_name, XPC_NOTIFY_IRQ_NAME) == 0)
+ mq->irq = SGI_XPC_NOTIFY;
+ else
+ return -EINVAL;
+
+ mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
+ mmr_value = (unsigned long)cpu_physical_id(cpu) << 32 | mq->irq;
+
+ uv_write_global_mmr64(mmr_pnode, mq->mmr_offset, mmr_value);
+#else
+ #error not a supported configuration
+#endif
+
+ return 0;
+}
+
+static void
+xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
+{
+#if defined CONFIG_X86_64
+ uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset);
+
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ int mmr_pnode;
+ unsigned long mmr_value;
+
+ mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
+ mmr_value = 1UL << 16;
+
+ uv_write_global_mmr64(mmr_pnode, mq->mmr_offset, mmr_value);
+#else
+ #error not a supported configuration
+#endif
+}
+
+static int
+xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
+{
+ int ret;
+
+#if defined CONFIG_X86_64
+ ret = uv_bios_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
+ mq->order, &mq->mmr_offset);
+ if (ret < 0) {
+ dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
+ "ret=%d\n", ret);
+ return ret;
+ }
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ ret = sn_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
+ mq->order, &mq->mmr_offset);
+ if (ret < 0) {
+ dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n",
+ ret);
+ return -EBUSY;
+ }
+#else
+ #error not a supported configuration
+#endif
+
+ mq->watchlist_num = ret;
+ return 0;
+}
+
+static void
+xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
+{
+ int ret;
+
+#if defined CONFIG_X86_64
+ ret = uv_bios_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
+ BUG_ON(ret != BIOS_STATUS_SUCCESS);
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ ret = sn_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
+ BUG_ON(ret != SALRET_OK);
+#else
+ #error not a supported configuration
+#endif
+}
+
+static struct xpc_gru_mq_uv *
+xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
irq_handler_t irq_handler)
{
+ enum xp_retval xp_ret;
int ret;
int nid;
- int mq_order;
+ int pg_order;
struct page *page;
- void *mq;
+ struct xpc_gru_mq_uv *mq;
+
+ mq = kmalloc(sizeof(struct xpc_gru_mq_uv), GFP_KERNEL);
+ if (mq == NULL) {
+ dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to kmalloc() "
+ "a xpc_gru_mq_uv structure\n");
+ ret = -ENOMEM;
+ goto out_1;
+ }
+
+ pg_order = get_order(mq_size);
+ mq->order = pg_order + PAGE_SHIFT;
+ mq_size = 1UL << mq->order;
+
+ mq->mmr_blade = uv_cpu_to_blade_id(cpu);
- nid = cpu_to_node(cpuid);
- mq_order = get_order(mq_size);
+ nid = cpu_to_node(cpu);
page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
- mq_order);
+ pg_order);
if (page == NULL) {
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
"bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
- return NULL;
+ ret = -ENOMEM;
+ goto out_2;
}
+ mq->address = page_address(page);
- mq = page_address(page);
- ret = gru_create_message_queue(mq, mq_size);
+ ret = gru_create_message_queue(mq->address, mq_size);
if (ret != 0) {
dev_err(xpc_part, "gru_create_message_queue() returned "
"error=%d\n", ret);
- free_pages((unsigned long)mq, mq_order);
- return NULL;
+ ret = -EINVAL;
+ goto out_3;
}
- /* !!! Need to do some other things to set up IRQ */
+ /* enable generation of irq when GRU mq operation occurs to this mq */
+ ret = xpc_gru_mq_watchlist_alloc_uv(mq);
+ if (ret != 0)
+ goto out_3;
- ret = request_irq(irq, irq_handler, 0, "xpc", NULL);
+ ret = xpc_get_gru_mq_irq_uv(mq, cpu, irq_name);
+ if (ret != 0)
+ goto out_4;
+
+ ret = request_irq(mq->irq, irq_handler, 0, irq_name, NULL);
if (ret != 0) {
dev_err(xpc_part, "request_irq(irq=%d) returned error=%d\n",
- irq, ret);
- free_pages((unsigned long)mq, mq_order);
- return NULL;
+ mq->irq, ret);
+ goto out_5;
}
- /* !!! enable generation of irq when GRU mq op occurs to this mq */
-
- /* ??? allow other partitions to access GRU mq? */
+ /* allow other partitions to access this GRU mq */
+ xp_ret = xp_expand_memprotect(xp_pa(mq->address), mq_size);
+ if (xp_ret != xpSuccess) {
+ ret = -EACCES;
+ goto out_6;
+ }
return mq;
+
+ /* something went wrong */
+out_6:
+ free_irq(mq->irq, NULL);
+out_5:
+ xpc_release_gru_mq_irq_uv(mq);
+out_4:
+ xpc_gru_mq_watchlist_free_uv(mq);
+out_3:
+ free_pages((unsigned long)mq->address, pg_order);
+out_2:
+ kfree(mq);
+out_1:
+ return ERR_PTR(ret);
}
static void
-xpc_destroy_gru_mq_uv(void *mq, unsigned int mq_size, unsigned int irq)
+xpc_destroy_gru_mq_uv(struct xpc_gru_mq_uv *mq)
{
- /* ??? disallow other partitions to access GRU mq? */
+ unsigned int mq_size;
+ int pg_order;
+ int ret;
+
+ /* disallow other partitions to access GRU mq */
+ mq_size = 1UL << mq->order;
+ ret = xp_restrict_memprotect(xp_pa(mq->address), mq_size);
+ BUG_ON(ret != xpSuccess);
- /* !!! disable generation of irq when GRU mq op occurs to this mq */
+ /* unregister irq handler and release mq irq/vector mapping */
+ free_irq(mq->irq, NULL);
+ xpc_release_gru_mq_irq_uv(mq);
- free_irq(irq, NULL);
+ /* disable generation of irq when GRU mq op occurs to this mq */
+ xpc_gru_mq_watchlist_free_uv(mq);
- free_pages((unsigned long)mq, get_order(mq_size));
+ pg_order = mq->order - PAGE_SHIFT;
+ free_pages((unsigned long)mq->address, pg_order);
+
+ kfree(mq);
}
static enum xp_retval
@@ -402,7 +559,10 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
struct xpc_partition *part;
int wakeup_hb_checker = 0;
- while ((msg_hdr = gru_get_next_message(xpc_activate_mq_uv)) != NULL) {
+ while (1) {
+ msg_hdr = gru_get_next_message(xpc_activate_mq_uv->address);
+ if (msg_hdr == NULL)
+ break;
partid = msg_hdr->partid;
if (partid < 0 || partid >= XP_MAX_NPARTITIONS_UV) {
@@ -418,7 +578,7 @@ xpc_handle_activate_IRQ_uv(int irq, void *dev_id)
}
}
- gru_free_message(xpc_activate_mq_uv, msg_hdr);
+ gru_free_message(xpc_activate_mq_uv->address, msg_hdr);
}
if (wakeup_hb_checker)
@@ -482,7 +642,7 @@ xpc_send_local_activate_IRQ_uv(struct xpc_partition *part, int act_state_req)
struct xpc_partition_uv *part_uv = &part->sn.uv;
/*
- * !!! Make our side think that the remote parition sent an activate
+ * !!! Make our side think that the remote partition sent an activate
* !!! message our way by doing what the activate IRQ handler would
* !!! do had one really been sent.
*/
@@ -500,14 +660,39 @@ static enum xp_retval
xpc_get_partition_rsvd_page_pa_uv(void *buf, u64 *cookie, unsigned long *rp_pa,
size_t *len)
{
- /* !!! call the UV version of sn_partition_reserved_page_pa() */
- return xpUnsupported;
+ s64 status;
+ enum xp_retval ret;
+
+#if defined CONFIG_X86_64
+ status = uv_bios_reserved_page_pa((u64)buf, cookie, (u64 *)rp_pa,
+ (u64 *)len);
+ if (status == BIOS_STATUS_SUCCESS)
+ ret = xpSuccess;
+ else if (status == BIOS_STATUS_MORE_PASSES)
+ ret = xpNeedMoreInfo;
+ else
+ ret = xpBiosError;
+
+#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
+ status = sn_partition_reserved_page_pa((u64)buf, cookie, rp_pa, len);
+ if (status == SALRET_OK)
+ ret = xpSuccess;
+ else if (status == SALRET_MORE_PASSES)
+ ret = xpNeedMoreInfo;
+ else
+ ret = xpSalError;
+
+#else
+ #error not a supported configuration
+#endif
+
+ return ret;
}
static int
xpc_setup_rsvd_page_sn_uv(struct xpc_rsvd_page *rp)
{
- rp->sn.activate_mq_gpa = uv_gpa(xpc_activate_mq_uv);
+ rp->sn.activate_mq_gpa = uv_gpa(xpc_activate_mq_uv->address);
return 0;
}
@@ -1411,22 +1596,18 @@ xpc_init_uv(void)
return -E2BIG;
}
- /* ??? The cpuid argument's value is 0, is that what we want? */
- /* !!! The irq argument's value isn't correct. */
- xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0, 0,
+ xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0,
+ XPC_ACTIVATE_IRQ_NAME,
xpc_handle_activate_IRQ_uv);
- if (xpc_activate_mq_uv == NULL)
- return -ENOMEM;
+ if (IS_ERR(xpc_activate_mq_uv))
+ return PTR_ERR(xpc_activate_mq_uv);
- /* ??? The cpuid argument's value is 0, is that what we want? */
- /* !!! The irq argument's value isn't correct. */
- xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0, 0,
+ xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0,
+ XPC_NOTIFY_IRQ_NAME,
xpc_handle_notify_IRQ_uv);
- if (xpc_notify_mq_uv == NULL) {
- /* !!! The irq argument's value isn't correct. */
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv,
- XPC_ACTIVATE_MQ_SIZE_UV, 0);
- return -ENOMEM;
+ if (IS_ERR(xpc_notify_mq_uv)) {
+ xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
+ return PTR_ERR(xpc_notify_mq_uv);
}
return 0;
@@ -1435,9 +1616,6 @@ xpc_init_uv(void)
void
xpc_exit_uv(void)
{
- /* !!! The irq argument's value isn't correct. */
- xpc_destroy_gru_mq_uv(xpc_notify_mq_uv, XPC_NOTIFY_MQ_SIZE_UV, 0);
-
- /* !!! The irq argument's value isn't correct. */
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv, XPC_ACTIVATE_MQ_SIZE_UV, 0);
+ xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
+ xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
}
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c
index 71513b3af708..8e6aa9508f46 100644
--- a/drivers/misc/sgi-xp/xpnet.c
+++ b/drivers/misc/sgi-xp/xpnet.c
@@ -153,8 +153,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg)
struct sk_buff *skb;
void *dst;
enum xp_retval ret;
- struct xpnet_dev_private *priv =
- (struct xpnet_dev_private *)xpnet_device->priv;
+ struct xpnet_dev_private *priv = netdev_priv(xpnet_device);
if (!XPNET_VALID_MSG(msg)) {
/*
@@ -368,9 +367,7 @@ xpnet_dev_set_config(struct net_device *dev, struct ifmap *new_map)
static struct net_device_stats *
xpnet_dev_get_stats(struct net_device *dev)
{
- struct xpnet_dev_private *priv;
-
- priv = (struct xpnet_dev_private *)dev->priv;
+ struct xpnet_dev_private *priv = netdev_priv(dev);
return &priv->stats;
}
@@ -456,7 +453,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct xpnet_pending_msg *queued_msg;
u64 start_addr, end_addr;
short dest_partid;
- struct xpnet_dev_private *priv = (struct xpnet_dev_private *)dev->priv;
+ struct xpnet_dev_private *priv = netdev_priv(dev);
u16 embedded_bytes = 0;
dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
@@ -541,9 +538,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void
xpnet_dev_tx_timeout(struct net_device *dev)
{
- struct xpnet_dev_private *priv;
-
- priv = (struct xpnet_dev_private *)dev->priv;
+ struct xpnet_dev_private *priv = netdev_priv(dev);
priv->stats.tx_errors++;
return;
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index 2f0fcdb869b7..eb29b1d933ac 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -10,20 +10,6 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * 2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * Changed to conform redesigned i.MX scatter gather DMA interface
- *
- * 2005-11-04 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * Updated for 2.6.14 kernel
- *
- * 2005-12-13 Jay Monkman <jtm@smoothsmoothie.com>
- * Found and corrected problems in the write path
- *
- * 2005-12-30 Pavel Pisa <pisa@cmp.felk.cvut.cz>
- * The event handling rewritten right way in softirq.
- * Added many ugly hacks and delays to overcome SDHC
- * deficiencies
- *
*/
#include <linux/module.h>
@@ -37,9 +23,9 @@
#include <linux/mmc/card.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/io.h>
#include <asm/dma.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <mach/mmc.h>
@@ -50,17 +36,16 @@
#define DRIVER_NAME "imx-mmc"
#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
- INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
- INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
+ INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
+ INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
struct imxmci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
+ void __iomem *base;
int irq;
imx_dmach_t dma;
- unsigned int clkrt;
- unsigned int cmdat;
volatile unsigned int imask;
unsigned int power_mode;
unsigned int present;
@@ -74,7 +59,7 @@ struct imxmci_host {
struct tasklet_struct tasklet;
unsigned int status_reg;
unsigned long pending_events;
- /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */
+ /* Next two fields are there for CPU driven transfers to overcome SDHC deficiencies */
u16 *data_ptr;
unsigned int data_cnt;
atomic_t stuck_timeout;
@@ -114,14 +99,22 @@ struct imxmci_host {
static void imxmci_stop_clock(struct imxmci_host *host)
{
int i = 0;
- MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK;
- while(i < 0x1000) {
- if(!(i & 0x7f))
- MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK;
+ u16 reg;
+
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg & ~STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
+ while (i < 0x1000) {
+ if (!(i & 0x7f)) {
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_STOP_CLK,
+ host->base + MMC_REG_STR_STP_CLK);
+ }
- if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) {
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (!(reg & STATUS_CARD_BUS_CLK_RUN)) {
/* Check twice before cut */
- if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN))
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (!(reg & STATUS_CARD_BUS_CLK_RUN))
return;
}
@@ -135,8 +128,10 @@ static int imxmci_start_clock(struct imxmci_host *host)
unsigned int trials = 0;
unsigned int delay_limit = 128;
unsigned long flags;
+ u16 reg;
- MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg & ~STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
@@ -145,18 +140,21 @@ static int imxmci_start_clock(struct imxmci_host *host)
* then 6 delay loops, but during card detection (low clockrate)
* it takes up to 5000 delay loops and sometimes fails for the first time
*/
- MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
do {
unsigned int delay = delay_limit;
- while(delay--){
- if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ while (delay--) {
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (reg & STATUS_CARD_BUS_CLK_RUN)
/* Check twice before cut */
- if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ reg = readw(host->base + MMC_REG_STATUS);
+ if (reg & STATUS_CARD_BUS_CLK_RUN)
return 0;
- if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
return 0;
}
@@ -167,58 +165,59 @@ static int imxmci_start_clock(struct imxmci_host *host)
* IRQ or schedule delays this function execution and the clocks has
* been already stopped by other means (response processing, SDHC HW)
*/
- if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
- MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+ if (!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) {
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_START_CLK,
+ host->base + MMC_REG_STR_STP_CLK);
+ }
local_irq_restore(flags);
- } while(++trials<256);
+ } while (++trials < 256);
dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
return -1;
}
-static void imxmci_softreset(void)
+static void imxmci_softreset(struct imxmci_host *host)
{
+ int i;
+
/* reset sequence */
- MMC_STR_STP_CLK = 0x8;
- MMC_STR_STP_CLK = 0xD;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
- MMC_STR_STP_CLK = 0x5;
-
- MMC_RES_TO = 0xff;
- MMC_BLK_LEN = 512;
- MMC_NOB = 1;
+ writew(0x08, host->base + MMC_REG_STR_STP_CLK);
+ writew(0x0D, host->base + MMC_REG_STR_STP_CLK);
+
+ for (i = 0; i < 8; i++)
+ writew(0x05, host->base + MMC_REG_STR_STP_CLK);
+
+ writew(0xff, host->base + MMC_REG_RES_TO);
+ writew(512, host->base + MMC_REG_BLK_LEN);
+ writew(1, host->base + MMC_REG_NOB);
}
static int imxmci_busy_wait_for_status(struct imxmci_host *host,
- unsigned int *pstat, unsigned int stat_mask,
- int timeout, const char *where)
+ unsigned int *pstat, unsigned int stat_mask,
+ int timeout, const char *where)
{
- int loops=0;
- while(!(*pstat & stat_mask)) {
- loops+=2;
- if(loops >= timeout) {
+ int loops = 0;
+
+ while (!(*pstat & stat_mask)) {
+ loops += 2;
+ if (loops >= timeout) {
dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
where, *pstat, stat_mask);
return -1;
}
udelay(2);
- *pstat |= MMC_STATUS;
+ *pstat |= readw(host->base + MMC_REG_STATUS);
}
- if(!loops)
+ if (!loops)
return 0;
/* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
- if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000))
+ if (!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock >= 8000000))
dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
- loops, where, *pstat, stat_mask);
+ loops, where, *pstat, stat_mask);
return loops;
}
@@ -235,8 +234,8 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->data = data;
data->bytes_xfered = 0;
- MMC_NOB = nob;
- MMC_BLK_LEN = blksz;
+ writew(nob, host->base + MMC_REG_NOB);
+ writew(blksz, host->base + MMC_REG_BLK_LEN);
/*
* DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
@@ -252,14 +251,14 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->dma_dir = DMA_FROM_DEVICE;
/* Hack to enable read SCR */
- MMC_NOB = 1;
- MMC_BLK_LEN = 512;
+ writew(1, host->base + MMC_REG_NOB);
+ writew(512, host->base + MMC_REG_BLK_LEN);
} else {
host->dma_dir = DMA_TO_DEVICE;
}
/* Convert back to virtual address */
- host->data_ptr = (u16*)sg_virt(data->sg);
+ host->data_ptr = (u16 *)sg_virt(data->sg);
host->data_cnt = 0;
clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
@@ -271,10 +270,11 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ) {
host->dma_dir = DMA_FROM_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
+ data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ);
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_READ);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
@@ -282,10 +282,11 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
host->dma_dir = DMA_TO_DEVICE;
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, host->dma_dir);
+ data->sg_len, host->dma_dir);
imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
- host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE);
+ host->res->start + MMC_REG_BUFFER_ACCESS,
+ DMA_MODE_WRITE);
/*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
@@ -293,12 +294,12 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
#if 1 /* This code is there only for consistency checking and can be disabled in future */
host->dma_size = 0;
- for(i=0; i<host->dma_nents; i++)
- host->dma_size+=data->sg[i].length;
+ for (i = 0; i < host->dma_nents; i++)
+ host->dma_size += data->sg[i].length;
if (datasz > host->dma_size) {
dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
- datasz, host->dma_size);
+ datasz, host->dma_size);
}
#endif
@@ -306,7 +307,7 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
wmb();
- if(host->actual_bus_width == MMC_BUS_WIDTH_4)
+ if (host->actual_bus_width == MMC_BUS_WIDTH_4)
BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */
else
BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */
@@ -317,9 +318,8 @@ static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
/* start DMA engine for read, write is delayed after initial response */
- if (host->dma_dir == DMA_FROM_DEVICE) {
+ if (host->dma_dir == DMA_FROM_DEVICE)
imx_dma_enable(host->dma);
- }
}
static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
@@ -351,16 +351,16 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd,
break;
}
- if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) )
+ if (test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events))
cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
- if ( host->actual_bus_width == MMC_BUS_WIDTH_4 )
+ if (host->actual_bus_width == MMC_BUS_WIDTH_4)
cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
- MMC_CMD = cmd->opcode;
- MMC_ARGH = cmd->arg >> 16;
- MMC_ARGL = cmd->arg & 0xffff;
- MMC_CMD_DAT_CONT = cmdat;
+ writew(cmd->opcode, host->base + MMC_REG_CMD);
+ writew(cmd->arg >> 16, host->base + MMC_REG_ARGH);
+ writew(cmd->arg & 0xffff, host->base + MMC_REG_ARGL);
+ writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
atomic_set(&host->stuck_timeout, 0);
set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
@@ -368,18 +368,18 @@ static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd,
imask = IMXMCI_INT_MASK_DEFAULT;
imask &= ~INT_MASK_END_CMD_RES;
- if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) {
- /*imask &= ~INT_MASK_BUF_READY;*/
+ if (cmdat & CMD_DAT_CONT_DATA_ENABLE) {
+ /* imask &= ~INT_MASK_BUF_READY; */
imask &= ~INT_MASK_DATA_TRAN;
- if ( cmdat & CMD_DAT_CONT_WRITE )
+ if (cmdat & CMD_DAT_CONT_WRITE)
imask &= ~INT_MASK_WRITE_OP_DONE;
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
imask &= ~INT_MASK_BUF_READY;
}
spin_lock_irqsave(&host->lock, flags);
host->imask = imask;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
@@ -395,14 +395,14 @@ static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *
spin_lock_irqsave(&host->lock, flags);
host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
- IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
+ IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
host->imask = IMXMCI_INT_MASK_DEFAULT;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
spin_unlock_irqrestore(&host->lock, flags);
- if(req && req->cmd)
+ if (req && req->cmd)
host->prev_cmd_code = req->cmd->opcode;
host->req = NULL;
@@ -416,17 +416,17 @@ static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
struct mmc_data *data = host->data;
int data_error;
- if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){
+ if (test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
imx_dma_disable(host->dma);
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
host->dma_dir);
}
- if ( stat & STATUS_ERR_MASK ) {
- dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
- if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
+ if (stat & STATUS_ERR_MASK) {
+ dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n", stat);
+ if (stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
data->error = -EILSEQ;
- else if(stat & STATUS_TIME_OUT_READ)
+ else if (stat & STATUS_TIME_OUT_READ)
data->error = -ETIMEDOUT;
else
data->error = -EIO;
@@ -445,7 +445,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
{
struct mmc_command *cmd = host->cmd;
int i;
- u32 a,b,c;
+ u32 a, b, c;
struct mmc_data *data = host->data;
if (!cmd)
@@ -461,18 +461,18 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
cmd->error = -EILSEQ;
}
- if(cmd->flags & MMC_RSP_PRESENT) {
- if(cmd->flags & MMC_RSP_136) {
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
- u32 a = MMC_RES_FIFO & 0xffff;
- u32 b = MMC_RES_FIFO & 0xffff;
- cmd->resp[i] = a<<16 | b;
+ a = readw(host->base + MMC_REG_RES_FIFO);
+ b = readw(host->base + MMC_REG_RES_FIFO);
+ cmd->resp[i] = a << 16 | b;
}
} else {
- a = MMC_RES_FIFO & 0xffff;
- b = MMC_RES_FIFO & 0xffff;
- c = MMC_RES_FIFO & 0xffff;
- cmd->resp[0] = a<<24 | b<<8 | c>>8;
+ a = readw(host->base + MMC_REG_RES_FIFO);
+ b = readw(host->base + MMC_REG_RES_FIFO);
+ c = readw(host->base + MMC_REG_RES_FIFO);
+ cmd->resp[0] = a << 24 | b << 8 | c >> 8;
}
}
@@ -484,36 +484,34 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
/* Wait for FIFO to be empty before starting DMA write */
- stat = MMC_STATUS;
- if(imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 40, "imxmci_cmd_done DMA WR") < 0) {
+ stat = readw(host->base + MMC_REG_STATUS);
+ if (imxmci_busy_wait_for_status(host, &stat,
+ STATUS_APPL_BUFF_FE,
+ 40, "imxmci_cmd_done DMA WR") < 0) {
cmd->error = -EIO;
imxmci_finish_data(host, stat);
- if(host->req)
+ if (host->req)
imxmci_finish_request(host, host->req);
dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
- stat);
+ stat);
return 0;
}
- if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
imx_dma_enable(host->dma);
- }
}
} else {
struct mmc_request *req;
imxmci_stop_clock(host);
req = host->req;
- if(data)
+ if (data)
imxmci_finish_data(host, stat);
- if( req ) {
+ if (req)
imxmci_finish_request(host, req);
- } else {
+ else
dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
- }
}
return 1;
@@ -535,11 +533,10 @@ static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
} else {
struct mmc_request *req;
req = host->req;
- if( req ) {
+ if (req)
imxmci_finish_request(host, req);
- } else {
+ else
dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
- }
}
return 1;
@@ -552,7 +549,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
int trans_done = 0;
unsigned int stat = *pstat;
- if(host->actual_bus_width != MMC_BUS_WIDTH_4)
+ if (host->actual_bus_width != MMC_BUS_WIDTH_4)
burst_len = 16;
else
burst_len = 64;
@@ -563,44 +560,44 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
udelay(20); /* required for clocks < 8MHz*/
- if(host->dma_dir == DMA_FROM_DEVICE) {
+ if (host->dma_dir == DMA_FROM_DEVICE) {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
- STATUS_TIME_OUT_READ,
- 50, "imxmci_cpu_driven_data read");
+ STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
+ STATUS_TIME_OUT_READ,
+ 50, "imxmci_cpu_driven_data read");
- while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
- !(stat & STATUS_TIME_OUT_READ) &&
- (host->data_cnt < 512)) {
+ while ((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
+ !(stat & STATUS_TIME_OUT_READ) &&
+ (host->data_cnt < 512)) {
udelay(20); /* required for clocks < 8MHz*/
- for(i = burst_len; i>=2 ; i-=2) {
+ for (i = burst_len; i >= 2 ; i -= 2) {
u16 data;
- data = MMC_BUFFER_ACCESS;
+ data = readw(host->base + MMC_REG_BUFFER_ACCESS);
udelay(10); /* required for clocks < 8MHz*/
- if(host->data_cnt+2 <= host->dma_size) {
+ if (host->data_cnt+2 <= host->dma_size) {
*(host->data_ptr++) = data;
} else {
- if(host->data_cnt < host->dma_size)
- *(u8*)(host->data_ptr) = data;
+ if (host->data_cnt < host->dma_size)
+ *(u8 *)(host->data_ptr) = data;
}
host->data_cnt += 2;
}
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
host->data_cnt, burst_len, stat);
}
- if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
+ if ((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
trans_done = 1;
- if(host->dma_size & 0x1ff)
+ if (host->dma_size & 0x1ff)
stat &= ~STATUS_CRC_READ_ERR;
- if(stat & STATUS_TIME_OUT_READ) {
+ if (stat & STATUS_TIME_OUT_READ) {
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
stat);
trans_done = -1;
@@ -608,12 +605,12 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
} else {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_APPL_BUFF_FE,
- 20, "imxmci_cpu_driven_data write");
+ STATUS_APPL_BUFF_FE,
+ 20, "imxmci_cpu_driven_data write");
- while((stat & STATUS_APPL_BUFF_FE) &&
- (host->data_cnt < host->dma_size)) {
- if(burst_len >= host->dma_size - host->data_cnt) {
+ while ((stat & STATUS_APPL_BUFF_FE) &&
+ (host->data_cnt < host->dma_size)) {
+ if (burst_len >= host->dma_size - host->data_cnt) {
burst_len = host->dma_size - host->data_cnt;
host->data_cnt = host->dma_size;
trans_done = 1;
@@ -621,10 +618,10 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
host->data_cnt += burst_len;
}
- for(i = burst_len; i>0 ; i-=2)
- MMC_BUFFER_ACCESS = *(host->data_ptr++);
+ for (i = burst_len; i > 0 ; i -= 2)
+ writew(*(host->data_ptr++), host->base + MMC_REG_BUFFER_ACCESS);
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
burst_len, stat);
@@ -639,7 +636,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
static void imxmci_dma_irq(int dma, void *devid)
{
struct imxmci_host *host = devid;
- uint32_t stat = MMC_STATUS;
+ u32 stat = readw(host->base + MMC_REG_STATUS);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
@@ -650,10 +647,11 @@ static void imxmci_dma_irq(int dma, void *devid)
static irqreturn_t imxmci_irq(int irq, void *devid)
{
struct imxmci_host *host = devid;
- uint32_t stat = MMC_STATUS;
+ u32 stat = readw(host->base + MMC_REG_STATUS);
int handled = 1;
- MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;
+ writew(host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT,
+ host->base + MMC_REG_INT_MASK);
atomic_set(&host->stuck_timeout, 0);
host->status_reg = stat;
@@ -671,10 +669,10 @@ static void imxmci_tasklet_fnc(unsigned long data)
unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
int timeout = 0;
- if(atomic_read(&host->stuck_timeout) > 4) {
+ if (atomic_read(&host->stuck_timeout) > 4) {
char *what;
timeout = 1;
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
host->status_reg = stat;
if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
@@ -683,29 +681,37 @@ static void imxmci_tasklet_fnc(unsigned long data)
what = "RESP";
else
if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
- if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
what = "DATA";
else
what = "DMA";
else
what = "???";
- dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
- what, stat, MMC_INT_MASK);
- dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
- MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));
+ dev_err(mmc_dev(host->mmc),
+ "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
+ what, stat,
+ readw(host->base + MMC_REG_INT_MASK));
+ dev_err(mmc_dev(host->mmc),
+ "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
+ readw(host->base + MMC_REG_CMD_DAT_CONT),
+ readw(host->base + MMC_REG_BLK_LEN),
+ readw(host->base + MMC_REG_NOB),
+ CCR(host->dma));
dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
- host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<<host->actual_bus_width, host->dma_size);
+ host->cmd ? host->cmd->opcode : 0,
+ host->prev_cmd_code,
+ 1 << host->actual_bus_width, host->dma_size);
}
- if(!host->present || timeout)
+ if (!host->present || timeout)
host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
- STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
+ STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
- if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
+ if (test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
/*
* This is not required in theory, but there is chance to miss some flag
* which clears automatically by mask write, FreeScale original code keeps
@@ -713,63 +719,62 @@ static void imxmci_tasklet_fnc(unsigned long data)
*/
stat |= host->status_reg;
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
stat &= ~STATUS_CRC_READ_ERR;
- if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
imxmci_busy_wait_for_status(host, &stat,
- STATUS_END_CMD_RESP | STATUS_ERR_MASK,
- 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
+ STATUS_END_CMD_RESP | STATUS_ERR_MASK,
+ 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
}
- if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
- if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ if (stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
+ if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
- if(host->data && (stat & STATUS_ERR_MASK))
+ if (host->data && (stat & STATUS_ERR_MASK))
imxmci_data_done(host, stat);
}
- if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
- stat |= MMC_STATUS;
- if(imxmci_cpu_driven_data(host, &stat)){
- if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
+ stat |= readw(host->base + MMC_REG_STATUS);
+ if (imxmci_cpu_driven_data(host, &stat)) {
+ if (test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
imxmci_cmd_done(host, stat);
atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
- &host->pending_events);
+ &host->pending_events);
imxmci_data_done(host, stat);
}
}
}
- if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
- !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
+ !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
- stat = MMC_STATUS;
+ stat = readw(host->base + MMC_REG_STATUS);
/* Same as above */
stat |= host->status_reg;
- if(host->dma_dir == DMA_TO_DEVICE) {
+ if (host->dma_dir == DMA_TO_DEVICE)
data_dir_mask = STATUS_WRITE_OP_DONE;
- } else {
+ else
data_dir_mask = STATUS_DATA_TRANS_DONE;
- }
- if(stat & data_dir_mask) {
+ if (stat & data_dir_mask) {
clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
imxmci_data_done(host, stat);
}
}
- if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
+ if (test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
- if(host->cmd)
+ if (host->cmd)
imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
- if(host->data)
+ if (host->data)
imxmci_data_done(host, STATUS_TIME_OUT_READ |
STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
- if(host->req)
+ if (host->req)
imxmci_finish_request(host, host->req);
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
@@ -796,9 +801,8 @@ static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
if (req->data->flags & MMC_DATA_WRITE)
cmdat |= CMD_DAT_CONT_WRITE;
- if (req->data->flags & MMC_DATA_STREAM) {
+ if (req->data->flags & MMC_DATA_STREAM)
cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
- }
}
imxmci_start_cmd(host, req->cmd, cmdat);
@@ -811,36 +815,37 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct imxmci_host *host = mmc_priv(mmc);
int prescaler;
- if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
host->actual_bus_width = MMC_BUS_WIDTH_4;
imx_gpio_mode(PB11_PF_SD_DAT3);
- }else{
+ } else {
host->actual_bus_width = MMC_BUS_WIDTH_1;
imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
}
- if ( host->power_mode != ios->power_mode ) {
+ if (host->power_mode != ios->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
- break;
+ break;
case MMC_POWER_UP:
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
- break;
+ break;
case MMC_POWER_ON:
- break;
+ break;
}
host->power_mode = ios->power_mode;
}
- if ( ios->clock ) {
+ if (ios->clock) {
unsigned int clk;
+ u16 reg;
/* The prescaler is 5 for PERCLK2 equal to 96MHz
* then 96MHz / 5 = 19.2 MHz
*/
clk = clk_get_rate(host->clk);
- prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
- switch(prescaler) {
+ prescaler = (clk + (CLK_RATE * 7) / 8) / CLK_RATE;
+ switch (prescaler) {
case 0:
case 1: prescaler = 0;
break;
@@ -858,24 +863,29 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
clk, prescaler);
- for(clk=0; clk<8; clk++) {
+ for (clk = 0; clk < 8; clk++) {
int x;
- x = CLK_RATE / (1<<clk);
- if( x <= ios->clock)
+ x = CLK_RATE / (1 << clk);
+ if (x <= ios->clock)
break;
}
- MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */
+ /* enable controller */
+ reg = readw(host->base + MMC_REG_STR_STP_CLK);
+ writew(reg | STR_STP_CLK_ENABLE,
+ host->base + MMC_REG_STR_STP_CLK);
imxmci_stop_clock(host);
- MMC_CLK_RATE = (prescaler<<3) | clk;
+ writew((prescaler << 3) | clk, host->base + MMC_REG_CLK_RATE);
/*
* Under my understanding, clock should not be started there, because it would
* initiate SDHC sequencer and send last or random command into card
*/
- /*imxmci_start_clock(host);*/
+ /* imxmci_start_clock(host); */
- dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
+ dev_dbg(mmc_dev(host->mmc),
+ "MMC_CLK_RATE: 0x%08x\n",
+ readw(host->base + MMC_REG_CLK_RATE));
} else {
imxmci_stop_clock(host);
}
@@ -915,10 +925,10 @@ static void imxmci_check_status(unsigned long data)
tasklet_schedule(&host->tasklet);
}
- if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
- test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
+ test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
atomic_inc(&host->stuck_timeout);
- if(atomic_read(&host->stuck_timeout) > 4)
+ if (atomic_read(&host->stuck_timeout) > 4)
tasklet_schedule(&host->tasklet);
} else {
atomic_set(&host->stuck_timeout, 0);
@@ -934,6 +944,7 @@ static int imxmci_probe(struct platform_device *pdev)
struct imxmci_host *host = NULL;
struct resource *r;
int ret = 0, irq;
+ u16 rev_no;
printk(KERN_INFO "i.MX mmc driver\n");
@@ -942,7 +953,8 @@ static int imxmci_probe(struct platform_device *pdev)
if (!r || irq < 0)
return -ENXIO;
- if (!request_mem_region(r->start, 0x100, pdev->name))
+ r = request_mem_region(r->start, resource_size(r), pdev->name);
+ if (!r)
return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
@@ -966,6 +978,12 @@ static int imxmci_probe(struct platform_device *pdev)
mmc->max_blk_count = 65535;
host = mmc_priv(mmc);
+ host->base = ioremap(r->start, resource_size(r));
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
host->mmc = mmc;
host->dma_allocated = 0;
host->pdata = pdev->dev.platform_data;
@@ -993,18 +1011,20 @@ static int imxmci_probe(struct platform_device *pdev)
imx_gpio_mode(PB12_PF_SD_CLK);
imx_gpio_mode(PB13_PF_SD_CMD);
- imxmci_softreset();
+ imxmci_softreset(host);
- if ( MMC_REV_NO != 0x390 ) {
+ rev_no = readw(host->base + MMC_REG_REV_NO);
+ if (rev_no != 0x390) {
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
- MMC_REV_NO);
+ readw(host->base + MMC_REG_REV_NO));
goto out;
}
- MMC_READ_TO = 0x2db4; /* recommended in data sheet */
+ /* recommended in data sheet */
+ writew(0x2db4, host->base + MMC_REG_READ_TO);
host->imask = IMXMCI_INT_MASK_DEFAULT;
- MMC_INT_MASK = host->imask;
+ writew(host->imask, host->base + MMC_REG_INT_MASK);
host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
if(host->dma < 0) {
@@ -1012,7 +1032,7 @@ static int imxmci_probe(struct platform_device *pdev)
ret = -EBUSY;
goto out;
}
- host->dma_allocated=1;
+ host->dma_allocated = 1;
imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
@@ -1032,7 +1052,7 @@ static int imxmci_probe(struct platform_device *pdev)
host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status;
add_timer(&host->timer);
- mod_timer(&host->timer, jiffies + (HZ>>1));
+ mod_timer(&host->timer, jiffies + (HZ >> 1));
platform_set_drvdata(pdev, mmc);
@@ -1042,18 +1062,20 @@ static int imxmci_probe(struct platform_device *pdev)
out:
if (host) {
- if(host->dma_allocated){
+ if (host->dma_allocated) {
imx_dma_free(host->dma);
- host->dma_allocated=0;
+ host->dma_allocated = 0;
}
if (host->clk) {
clk_disable(host->clk);
clk_put(host->clk);
}
+ if (host->base)
+ iounmap(host->base);
}
if (mmc)
mmc_free_host(mmc);
- release_mem_region(r->start, 0x100);
+ release_mem_region(r->start, resource_size(r));
return ret;
}
@@ -1072,9 +1094,10 @@ static int imxmci_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
free_irq(host->irq, host);
- if(host->dma_allocated){
+ iounmap(host->base);
+ if (host->dma_allocated) {
imx_dma_free(host->dma);
- host->dma_allocated=0;
+ host->dma_allocated = 0;
}
tasklet_kill(&host->tasklet);
@@ -1082,7 +1105,7 @@ static int imxmci_remove(struct platform_device *pdev)
clk_disable(host->clk);
clk_put(host->clk);
- release_mem_region(host->res->start, 0x100);
+ release_mem_region(host->res->start, resource_size(host->res));
mmc_free_host(mmc);
}
@@ -1109,7 +1132,7 @@ static int imxmci_resume(struct platform_device *dev)
if (mmc) {
host = mmc_priv(mmc);
- if(host)
+ if (host)
set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
ret = mmc_resume_host(mmc);
}
diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h
index e5339e334dbb..09d5d4ee3a77 100644
--- a/drivers/mmc/host/imxmmc.h
+++ b/drivers/mmc/host/imxmmc.h
@@ -1,24 +1,21 @@
+#define MMC_REG_STR_STP_CLK 0x00
+#define MMC_REG_STATUS 0x04
+#define MMC_REG_CLK_RATE 0x08
+#define MMC_REG_CMD_DAT_CONT 0x0C
+#define MMC_REG_RES_TO 0x10
+#define MMC_REG_READ_TO 0x14
+#define MMC_REG_BLK_LEN 0x18
+#define MMC_REG_NOB 0x1C
+#define MMC_REG_REV_NO 0x20
+#define MMC_REG_INT_MASK 0x24
+#define MMC_REG_CMD 0x28
+#define MMC_REG_ARGH 0x2C
+#define MMC_REG_ARGL 0x30
+#define MMC_REG_RES_FIFO 0x34
+#define MMC_REG_BUFFER_ACCESS 0x38
-# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x)))
-
-#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00)
-#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04)
-#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08)
-#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
-#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10)
-#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14)
-#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18)
-#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C)
-#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20)
-#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24)
-#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28)
-#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C)
-#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30)
-#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34)
-#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
-#define MMC_BUFFER_ACCESS_OFS 0x38
-
-
+#define STR_STP_CLK_IPG_CLK_GATE_DIS (1<<15)
+#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14)
#define STR_STP_CLK_ENDIAN (1<<5)
#define STR_STP_CLK_RESET (1<<3)
#define STR_STP_CLK_ENABLE (1<<2)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 2fadf323c696..1bcbdd6763ac 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -500,7 +500,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
}
host = mmc_priv(mmc);
- host->clk = clk_get(&dev->dev, "MCLK");
+ host->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
host->clk = NULL;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 1b9fc3c6b875..67d7b7fef084 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1015,7 +1015,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
}
if (is_read) {
- if (host->id == 1) {
+ if (host->id == 0) {
sync_dev = OMAP_DMA_MMC_RX;
dma_dev_name = "MMC1 read";
} else {
@@ -1023,7 +1023,7 @@ static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data
dma_dev_name = "MMC2 read";
}
} else {
- if (host->id == 1) {
+ if (host->id == 0) {
sync_dev = OMAP_DMA_MMC_TX;
dma_dev_name = "MMC1 write";
} else {
@@ -1317,7 +1317,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
host->slots[id] = slot;
mmc->caps = 0;
- if (host->pdata->conf.wire4)
+ if (host->pdata->slots[id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->ops = &mmc_omap_ops;
@@ -1451,6 +1451,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->irq = irq;
host->use_dma = 1;
+ host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index ebfaa9960939..f88cc7406354 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -26,11 +26,12 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
+#include <linux/io.h>
-#include <asm/dma.h>
-#include <asm/io.h>
#include <asm/sizes.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#include <mach/mmc.h>
@@ -533,7 +534,7 @@ static int pxamci_probe(struct platform_device *pdev)
host->pdata = pdev->dev.platform_data;
host->clkrt = CLKRT_OFF;
- host->clk = clk_get(&pdev->dev, "MMCCLK");
+ host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
host->clk = NULL;
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 3b2085b57769..fcc98a4cce3c 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -25,7 +25,7 @@
#include <mach/regs-sdi.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/mci.h>
+#include <plat/mci.h>
#include "s3cmci.h"
diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c
index 3aa018c092f8..42969fe051b2 100644
--- a/drivers/mtd/maps/dc21285.c
+++ b/drivers/mtd/maps/dc21285.c
@@ -32,16 +32,15 @@ static struct mtd_info *dc21285_mtd;
*/
static void nw_en_write(void)
{
- extern spinlock_t gpio_lock;
unsigned long flags;
/*
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(1, 1);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c
index dcdb1f17577d..3ea1de9be720 100644
--- a/drivers/mtd/maps/ixp2000.c
+++ b/drivers/mtd/maps/ixp2000.c
@@ -170,7 +170,7 @@ static int ixp2000_flash_probe(struct platform_device *dev)
err = -ENOMEM;
goto Error;
}
- memzero(info, sizeof(struct ixp2000_flash_info));
+ memset(info, 0, sizeof(struct ixp2000_flash_info));
platform_set_drvdata(dev, info);
diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c
index 9c7a5fbd4e51..16555cbeaea4 100644
--- a/drivers/mtd/maps/ixp4xx.c
+++ b/drivers/mtd/maps/ixp4xx.c
@@ -201,7 +201,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
err = -ENOMEM;
goto Error;
}
- memzero(info, sizeof(struct ixp4xx_flash_info));
+ memset(info, 0, sizeof(struct ixp4xx_flash_info));
platform_set_drvdata(dev, info);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1c2e9450d663..f8ae0400c49c 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -408,7 +408,7 @@ config MTD_NAND_FSL_UPM
config MTD_NAND_MXC
tristate "MXC NAND support"
- depends on ARCH_MX2
+ depends on ARCH_MX2 || ARCH_MX3
help
This enables the driver for the NAND flash controller on the
MXC processors.
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 15f0a26730ae..fc4144495610 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -20,8 +20,8 @@
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/pxa-regs.h>
#include <mach/pxa3xx_nand.h>
@@ -1080,7 +1080,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
this = &info->nand_chip;
mtd->priv = info;
- info->clk = clk_get(&pdev->dev, "NANDCLK");
+ info->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get nand clock\n");
ret = PTR_ERR(info->clk);
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 556139ed1fdf..8e375d5fe231 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -45,8 +45,8 @@
#include <asm/io.h>
-#include <asm/plat-s3c/regs-nand.h>
-#include <asm/plat-s3c/nand.h>
+#include <plat/regs-nand.h>
+#include <plat/nand.h>
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
@@ -818,7 +818,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
goto exit_error;
}
- memzero(info, sizeof(*info));
+ memset(info, 0, sizeof(*info));
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
@@ -883,7 +883,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
goto exit_error;
}
- memzero(info->mtds, size);
+ memset(info->mtds, 0, size);
/* initialise all possible chips */
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index a7e4d985f5ef..d1e0b8e7224b 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -149,7 +149,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
INIT_COMPLETION(c->irq_done);
if (c->gpio_irq) {
- result = omap_get_gpio_datain(c->gpio_irq);
+ result = gpio_get_value(c->gpio_irq);
if (result == -1) {
ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS);
intr = read_reg(c, ONENAND_REG_INTERRUPT);
@@ -634,9 +634,9 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
"OneNAND\n", c->gpio_irq);
goto err_iounmap;
}
- omap_set_gpio_direction(c->gpio_irq, 1);
+ gpio_direction_input(c->gpio_irq);
- if ((r = request_irq(OMAP_GPIO_IRQ(c->gpio_irq),
+ if ((r = request_irq(gpio_to_irq(c->gpio_irq),
omap2_onenand_interrupt, IRQF_TRIGGER_RISING,
pdev->dev.driver->name, c)) < 0)
goto err_release_gpio;
@@ -723,7 +723,7 @@ err_release_dma:
if (c->dma_channel != -1)
omap_free_dma(c->dma_channel);
if (c->gpio_irq)
- free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+ free_irq(gpio_to_irq(c->gpio_irq), c);
err_release_gpio:
if (c->gpio_irq)
omap_free_gpio(c->gpio_irq);
@@ -760,7 +760,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev)
omap2_onenand_shutdown(pdev);
platform_set_drvdata(pdev, NULL);
if (c->gpio_irq) {
- free_irq(OMAP_GPIO_IRQ(c->gpio_irq), c);
+ free_irq(gpio_to_irq(c->gpio_irq), c);
omap_free_gpio(c->gpio_irq);
}
iounmap(c->onenand.base);
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 7d15e7c6bcad..3d1318a3e688 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -297,8 +297,8 @@ static int __init el1_probe1(struct net_device *dev, int ioaddr)
if (el_debug)
printk(KERN_DEBUG "%s", version);
- memset(dev->priv, 0, sizeof(struct net_local));
lp = netdev_priv(dev);
+ memset(lp, 0, sizeof(struct net_local));
spin_lock_init(&lp->lock);
/*
@@ -725,7 +725,6 @@ static void el_receive(struct net_device *dev)
insb(DATAPORT, skb_put(skb, pkt_len), pkt_len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/3c501.h b/drivers/net/3c501.h
index cfec64efff78..f40b0493337a 100644
--- a/drivers/net/3c501.h
+++ b/drivers/net/3c501.h
@@ -23,7 +23,7 @@ static const struct ethtool_ops netdev_ethtool_ops;
static int el_debug = EL_DEBUG;
/*
- * Board-specific info in dev->priv.
+ * Board-specific info in netdev_priv(dev).
*/
struct net_local
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index 900b0ffdcc68..c092c3929224 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -168,6 +168,21 @@ out:
}
#endif
+static const struct net_device_ops el2_netdev_ops = {
+ .ndo_open = el2_open,
+ .ndo_stop = el2_close,
+
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+
/* Probe for the Etherlink II card at I/O port base IOADDR,
returning non-zero on success. If found, set the station
address and memory parameters in DEVICE. */
@@ -177,7 +192,6 @@ el2_probe1(struct net_device *dev, int ioaddr)
int i, iobase_reg, membase_reg, saved_406, wordlength, retval;
static unsigned version_printed;
unsigned long vendor_id;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, EL2_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -228,7 +242,7 @@ el2_probe1(struct net_device *dev, int ioaddr)
/* Retrieve and print the ethernet address. */
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + i);
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
/* Map the 8390 back into the window. */
outb(ECNTRL_THIN, ioaddr + 0x406);
@@ -336,8 +350,7 @@ el2_probe1(struct net_device *dev, int ioaddr)
ei_status.saved_irq = dev->irq;
- dev->open = &el2_open;
- dev->stop = &el2_close;
+ dev->netdev_ops = &el2_netdev_ops;
dev->ethtool_ops = &netdev_ethtool_ops;
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = eip_poll;
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index a424869707a5..6124605bef05 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -203,10 +203,10 @@ static inline int inb_command(unsigned int base_addr)
static inline void outb_control(unsigned char val, struct net_device *dev)
{
outb(val, dev->base_addr + PORT_CONTROL);
- ((elp_device *)(dev->priv))->hcr_val = val;
+ ((elp_device *)(netdev_priv(dev)))->hcr_val = val;
}
-#define HCR_VAL(x) (((elp_device *)((x)->priv))->hcr_val)
+#define HCR_VAL(x) (((elp_device *)(netdev_priv(x)))->hcr_val)
static inline void outb_command(unsigned char val, unsigned int base_addr)
{
@@ -247,7 +247,7 @@ static inline int get_status(unsigned int base_addr)
static inline void set_hsf(struct net_device *dev, int hsf)
{
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&adapter->lock, flags);
@@ -260,7 +260,7 @@ static bool start_receive(struct net_device *, pcb_struct *);
static inline void adapter_reset(struct net_device *dev)
{
unsigned long timeout;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
unsigned char orig_hcr = adapter->hcr_val;
outb_control(0, dev);
@@ -293,7 +293,7 @@ static inline void adapter_reset(struct net_device *dev)
*/
static inline void check_3c505_dma(struct net_device *dev)
{
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
if (adapter->dmaing && time_after(jiffies, adapter->current_dma.start_time + 10)) {
unsigned long flags, f;
printk(KERN_ERR "%s: DMA %s timed out, %d bytes left\n", dev->name, adapter->current_dma.direction ? "download" : "upload", get_dma_residue(dev->dma));
@@ -340,7 +340,7 @@ static inline bool send_pcb_fast(unsigned int base_addr, unsigned char byte)
/* Check to see if the receiver needs restarting, and kick it if so */
static inline void prime_rx(struct net_device *dev)
{
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
while (adapter->rx_active < ELP_RX_PCBS && netif_running(dev)) {
if (!start_receive(dev, &adapter->itx_pcb))
break;
@@ -375,7 +375,7 @@ static bool send_pcb(struct net_device *dev, pcb_struct * pcb)
{
int i;
unsigned long timeout;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
unsigned long flags;
check_3c505_dma(dev);
@@ -463,7 +463,7 @@ static bool receive_pcb(struct net_device *dev, pcb_struct * pcb)
unsigned long timeout;
unsigned long flags;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
set_hsf(dev, 0);
@@ -543,7 +543,7 @@ static bool receive_pcb(struct net_device *dev, pcb_struct * pcb)
static bool start_receive(struct net_device *dev, pcb_struct * tx_pcb)
{
bool status;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
if (elp_debug >= 3)
printk(KERN_DEBUG "%s: restarting receiver\n", dev->name);
@@ -571,7 +571,7 @@ static bool start_receive(struct net_device *dev, pcb_struct * tx_pcb)
static void receive_packet(struct net_device *dev, int len)
{
int rlen;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
void *target;
struct sk_buff *skb;
unsigned long flags;
@@ -638,13 +638,10 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
int len;
int dlen;
int icount = 0;
- struct net_device *dev;
- elp_device *adapter;
+ struct net_device *dev = dev_id;
+ elp_device *adapter = netdev_priv(dev);
unsigned long timeout;
- dev = dev_id;
- adapter = (elp_device *) dev->priv;
-
spin_lock(&adapter->lock);
do {
@@ -672,7 +669,6 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
skb->protocol = eth_type_trans(skb,dev);
dev->stats.rx_bytes += skb->len;
netif_rx(skb);
- dev->last_rx = jiffies;
}
}
adapter->dmaing = 0;
@@ -838,11 +834,9 @@ static irqreturn_t elp_interrupt(int irq, void *dev_id)
static int elp_open(struct net_device *dev)
{
- elp_device *adapter;
+ elp_device *adapter = netdev_priv(dev);
int retval;
- adapter = dev->priv;
-
if (elp_debug >= 3)
printk(KERN_DEBUG "%s: request to open device\n", dev->name);
@@ -971,7 +965,7 @@ static int elp_open(struct net_device *dev)
static bool send_packet(struct net_device *dev, struct sk_buff *skb)
{
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
unsigned long target;
unsigned long flags;
@@ -1062,7 +1056,7 @@ static void elp_timeout(struct net_device *dev)
static int elp_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
spin_lock_irqsave(&adapter->lock, flags);
check_3c505_dma(dev);
@@ -1104,7 +1098,7 @@ static int elp_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *elp_get_stats(struct net_device *dev)
{
- elp_device *adapter = (elp_device *) dev->priv;
+ elp_device *adapter = netdev_priv(dev);
if (elp_debug >= 3)
printk(KERN_DEBUG "%s: request for stats\n", dev->name);
@@ -1166,9 +1160,7 @@ static const struct ethtool_ops netdev_ethtool_ops = {
static int elp_close(struct net_device *dev)
{
- elp_device *adapter;
-
- adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
if (elp_debug >= 3)
printk(KERN_DEBUG "%s: request to close device\n", dev->name);
@@ -1209,7 +1201,7 @@ static int elp_close(struct net_device *dev)
static void elp_set_mc_list(struct net_device *dev)
{
- elp_device *adapter = (elp_device *) dev->priv;
+ elp_device *adapter = netdev_priv(dev);
struct dev_mc_list *dmi = dev->mc_list;
int i;
unsigned long flags;
@@ -1380,12 +1372,11 @@ static int __init elp_autodetect(struct net_device *dev)
static int __init elplus_setup(struct net_device *dev)
{
- elp_device *adapter = dev->priv;
+ elp_device *adapter = netdev_priv(dev);
int i, tries, tries1, okay;
unsigned long timeout;
unsigned long cookie = 0;
int err = -ENODEV;
- DECLARE_MAC_BUF(mac);
/*
* setup adapter structure
@@ -1522,9 +1513,9 @@ static int __init elplus_setup(struct net_device *dev)
* print remainder of startup message
*/
printk(KERN_INFO "%s: 3c505 at %#lx, irq %d, dma %d, "
- "addr %s, ",
+ "addr %pM, ",
dev->name, dev->base_addr, dev->irq, dev->dma,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
/*
* read more information from the adapter
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index 030c147211ba..423e65d0ba73 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -357,7 +357,6 @@ static int __init el16_probe1(struct net_device *dev, int ioaddr)
static unsigned char init_ID_done, version_printed;
int i, irq, irqval, retval;
struct net_local *lp;
- DECLARE_MAC_BUF(mac);
if (init_ID_done == 0) {
ushort lrs_state = 0xff;
@@ -405,7 +404,7 @@ static int __init el16_probe1(struct net_device *dev, int ioaddr)
outb(0x01, ioaddr + MISC_CTRL);
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + i);
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
if (mem_start)
net_debug = mem_start & 7;
@@ -866,7 +865,6 @@ static void el16_rx(struct net_device *dev)
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -938,14 +936,3 @@ cleanup_module(void)
}
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
- * version-control: t
- * kept-new-versions: 5
- * tab-width: 4
- * c-indent-level: 4
- * End:
- */
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index c7a4f3bcc2bc..535c234286ea 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -541,7 +541,6 @@ static int __devinit el3_common_init(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
int err;
- DECLARE_MAC_BUF(mac);
const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
spin_lock_init(&lp->lock);
@@ -575,9 +574,9 @@ static int __devinit el3_common_init(struct net_device *dev)
}
printk(KERN_INFO "%s: 3c5x9 found at %#3.3lx, %s port, "
- "address %s, IRQ %d.\n",
+ "address %pM, IRQ %d.\n",
dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
- print_mac(mac, dev->dev_addr), dev->irq);
+ dev->dev_addr, dev->irq);
if (el3_debug > 0)
printk(KERN_INFO "%s", version);
@@ -1075,7 +1074,6 @@ el3_rx(struct net_device *dev)
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_len;
dev->stats.rx_packets++;
continue;
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index a0f8b6e2d0af..39ac12233aa7 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -570,7 +570,6 @@ static int corkscrew_setup(struct net_device *dev, int ioaddr,
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
int irq;
- DECLARE_MAC_BUF(mac);
#ifdef __ISAPNP__
if (idev) {
@@ -636,7 +635,7 @@ static int corkscrew_setup(struct net_device *dev, int ioaddr,
checksum = (checksum ^ (checksum >> 8)) & 0xff;
if (checksum != 0x00)
printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
if (eeprom[16] == 0x11c7) { /* Corkscrew */
if (request_dma(dev->dma, "3c515")) {
printk(", DMA %d allocation failed", dev->dma);
@@ -1302,7 +1301,6 @@ static int corkscrew_rx(struct net_device *dev)
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
/* Wait a limited time to go to next packet. */
@@ -1389,7 +1387,6 @@ static int boomerang_rx(struct net_device *dev)
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
entry = (++vp->cur_rx) % RX_RING_SIZE;
@@ -1580,11 +1577,3 @@ void cleanup_module(void)
}
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c515.c"
- * c-indent-level: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c
index e2ce41d3828e..ff41e1ff5603 100644
--- a/drivers/net/3c523.c
+++ b/drivers/net/3c523.c
@@ -308,7 +308,7 @@ static int elmc_open(struct net_device *dev)
static int __init check586(struct net_device *dev, unsigned long where, unsigned size)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
char *iscp_addrs[2];
int i = 0;
@@ -347,9 +347,9 @@ static int __init check586(struct net_device *dev, unsigned long where, unsigned
* set iscp at the right place, called by elmc_probe and open586.
*/
-void alloc586(struct net_device *dev)
+static void alloc586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
elmc_id_reset586();
DELAY(2);
@@ -383,7 +383,6 @@ static int elmc_getinfo(char *buf, int slot, void *d)
{
int len = 0;
struct net_device *dev = d;
- DECLARE_MAC_BUF(mac);
if (dev == NULL)
return len;
@@ -398,8 +397,8 @@ static int elmc_getinfo(char *buf, int slot, void *d)
len += sprintf(buf + len, "Transceiver: %s\n", dev->if_port ?
"External" : "Internal");
len += sprintf(buf + len, "Device: %s\n", dev->name);
- len += sprintf(buf + len, "Hardware Address: %s\n",
- print_mac(mac, dev->dev_addr));
+ len += sprintf(buf + len, "Hardware Address: %pM\n",
+ dev->dev_addr);
return len;
} /* elmc_getinfo() */
@@ -416,8 +415,7 @@ static int __init do_elmc_probe(struct net_device *dev)
int i = 0;
unsigned int size = 0;
int retval;
- struct priv *pr = dev->priv;
- DECLARE_MAC_BUF(mac);
+ struct priv *pr = netdev_priv(dev);
if (MCA_bus == 0) {
return -ENODEV;
@@ -543,8 +541,8 @@ static int __init do_elmc_probe(struct net_device *dev)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(dev->base_addr + i);
- printk(KERN_INFO "%s: hardware address %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: hardware address %pM\n",
+ dev->name, dev->dev_addr);
dev->open = &elmc_open;
dev->stop = &elmc_close;
@@ -578,13 +576,14 @@ err_out:
return retval;
}
+#ifdef MODULE
static void cleanup_card(struct net_device *dev)
{
- mca_set_adapter_procfn(((struct priv *) (dev->priv))->slot, NULL, NULL);
+ mca_set_adapter_procfn(((struct priv *)netdev_priv(dev))->slot,
+ NULL, NULL);
release_region(dev->base_addr, ELMC_IO_EXTENT);
}
-
-#ifndef MODULE
+#else
struct net_device * __init elmc_probe(int unit)
{
struct net_device *dev = alloc_etherdev(sizeof(struct priv));
@@ -616,7 +615,7 @@ static int init586(struct net_device *dev)
void *ptr;
unsigned long s;
int i, result = 0;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
volatile struct configure_cmd_struct *cfg_cmd;
volatile struct iasetup_cmd_struct *ias_cmd;
volatile struct tdr_cmd_struct *tdr_cmd;
@@ -852,7 +851,7 @@ static void *alloc_rfa(struct net_device *dev, void *ptr)
volatile struct rfd_struct *rfd = (struct rfd_struct *) ptr;
volatile struct rbd_struct *rbd;
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
memset((char *) rfd, 0, sizeof(struct rfd_struct) * p->num_recv_buffs);
p->rfd_first = rfd;
@@ -913,7 +912,7 @@ elmc_interrupt(int irq, void *dev_id)
}
/* reading ELMC_CTRL also clears the INT bit. */
- p = (struct priv *) dev->priv;
+ p = netdev_priv(dev);
while ((stat = p->scb->status & STAT_MASK))
{
@@ -969,7 +968,7 @@ static void elmc_rcv_int(struct net_device *dev)
unsigned short totlen;
struct sk_buff *skb;
struct rbd_struct *rbd;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
for (; (status = p->rfd_top->status) & STAT_COMPL;) {
rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
@@ -985,7 +984,6 @@ static void elmc_rcv_int(struct net_device *dev)
skb_copy_to_linear_data(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += totlen;
} else {
@@ -1013,7 +1011,7 @@ static void elmc_rcv_int(struct net_device *dev)
static void elmc_rnr_int(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
dev->stats.rx_errors++;
@@ -1036,7 +1034,7 @@ static void elmc_rnr_int(struct net_device *dev)
static void elmc_xmt_int(struct net_device *dev)
{
int status;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
status = p->xmit_cmds[p->xmit_last]->cmd_status;
if (!(status & STAT_COMPL)) {
@@ -1079,7 +1077,7 @@ static void elmc_xmt_int(struct net_device *dev)
static void startrecv586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
p->scb->rfa_offset = make16(p->rfd_first);
p->scb->cmd = RUC_START;
@@ -1093,7 +1091,7 @@ static void startrecv586(struct net_device *dev)
static void elmc_timeout(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
/* COMMAND-UNIT active? */
if (p->scb->status & CU_ACTIVE) {
#ifdef DEBUG
@@ -1129,7 +1127,7 @@ static int elmc_send_packet(struct sk_buff *skb, struct net_device *dev)
#ifndef NO_NOPCOMMANDS
int next_nop;
#endif
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
netif_stop_queue(dev);
@@ -1200,7 +1198,7 @@ static int elmc_send_packet(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *elmc_get_stats(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
unsigned short crc, aln, rsc, ovrn;
crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index abc84f765973..2df3af3b9b20 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -335,7 +335,6 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
"82586 initialisation failure",
"Adapter list configuration error"
};
- DECLARE_MAC_BUF(mac);
/* Time to play MCA games */
@@ -405,7 +404,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
dev->dev_addr[i] = mca_read_pos(slot,3);
}
- printk("%s: Address %s", dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: Address %pM", dev->name, dev->dev_addr);
mca_write_pos(slot, 6, 0);
mca_write_pos(slot, 7, 0);
@@ -1187,7 +1186,6 @@ static void mc32_rx_ring(struct net_device *dev)
}
skb->protocol=eth_type_trans(skb,dev);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += length;
netif_rx(skb);
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 9ba295d9dd97..665e7fdf27a1 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -803,7 +803,7 @@ static int vortex_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
- if (dev && dev->priv) {
+ if (dev && netdev_priv(dev)) {
if (netif_running(dev)) {
netif_device_detach(dev);
vortex_down(dev, 1);
@@ -1013,7 +1013,6 @@ static int __devinit vortex_probe1(struct device *gendev,
const char *print_name = "3c59x";
struct pci_dev *pdev = NULL;
struct eisa_device *edev = NULL;
- DECLARE_MAC_BUF(mac);
if (!printed_version) {
printk (version);
@@ -1026,7 +1025,7 @@ static int __devinit vortex_probe1(struct device *gendev,
}
if ((edev = DEVICE_EISA(gendev))) {
- print_name = edev->dev.bus_id;
+ print_name = dev_name(&edev->dev);
}
}
@@ -1206,7 +1205,7 @@ static int __devinit vortex_probe1(struct device *gendev,
((__be16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
if (print_info)
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
/* Unfortunately an all zero eeprom passes the checksum and this
gets found in the wild in failure cases. Crypto is hard 8) */
if (!is_valid_ether_addr(dev->dev_addr)) {
@@ -2447,7 +2446,6 @@ static int vortex_rx(struct net_device *dev)
iowrite16(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
/* Wait a limited time to go to next packet. */
for (i = 200; i >= 0; i--)
@@ -2530,7 +2528,6 @@ boomerang_rx(struct net_device *dev)
}
}
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
entry = (++vp->cur_rx) % RX_RING_SIZE;
@@ -2886,7 +2883,7 @@ static void vortex_get_drvinfo(struct net_device *dev,
strcpy(info->bus_info, pci_name(VORTEX_PCI(vp)));
} else {
if (VORTEX_EISA(vp))
- sprintf(info->bus_info, vp->gendev->bus_id);
+ sprintf(info->bus_info, dev_name(vp->gendev));
else
sprintf(info->bus_info, "EISA 0x%lx %d",
dev->base_addr, dev->irq);
@@ -3217,7 +3214,7 @@ static void __exit vortex_eisa_cleanup(void)
#endif
if (compaq_net_device) {
- vp = compaq_net_device->priv;
+ vp = netdev_priv(compaq_net_device);
ioaddr = ioport_map(compaq_net_device->base_addr,
VORTEX_TOTAL_SIZE);
diff --git a/drivers/net/7990.c b/drivers/net/7990.c
index ad6b8a5b6574..7a331acc34ad 100644
--- a/drivers/net/7990.c
+++ b/drivers/net/7990.c
@@ -336,7 +336,6 @@ static int lance_rx (struct net_device *dev)
len);
skb->protocol = eth_type_trans (skb, dev);
netif_rx (skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 9ba1f0b46429..dd7ac8290aec 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -457,7 +457,6 @@ static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
cp->dev->stats.rx_packets++;
cp->dev->stats.rx_bytes += skb->len;
- cp->dev->last_rx = jiffies;
#if CP_VLAN_TAG_USED
if (cp->vlgrp && (desc->opts2 & cpu_to_le32(RxVlanTagged))) {
@@ -605,7 +604,7 @@ rx_next:
spin_lock_irqsave(&cp->lock, flags);
cpw16_f(IntrMask, cp_intr_mask);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
spin_unlock_irqrestore(&cp->lock, flags);
}
@@ -642,9 +641,9 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)
}
if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr))
- if (netif_rx_schedule_prep(dev, &cp->napi)) {
+ if (netif_rx_schedule_prep(&cp->napi)) {
cpw16_f(IntrMask, cp_norx_intr_mask);
- __netif_rx_schedule(dev, &cp->napi);
+ __netif_rx_schedule(&cp->napi);
}
if (status & (TxOK | TxErr | TxEmpty | SWInt))
@@ -1818,6 +1817,26 @@ static void cp_set_d3_state (struct cp_private *cp)
pci_set_power_state (cp->pdev, PCI_D3hot);
}
+static const struct net_device_ops cp_netdev_ops = {
+ .ndo_open = cp_open,
+ .ndo_stop = cp_close,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = cp_set_rx_mode,
+ .ndo_get_stats = cp_get_stats,
+ .ndo_do_ioctl = cp_ioctl,
+ .ndo_start_xmit = cp_start_xmit,
+ .ndo_tx_timeout = cp_tx_timeout,
+#if CP_VLAN_TAG_USED
+ .ndo_vlan_rx_register = cp_vlan_rx_register,
+#endif
+#ifdef BROKEN
+ .ndo_change_mtu = cp_change_mtu,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cp_poll_controller,
+#endif
+};
+
static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *dev;
@@ -1826,7 +1845,6 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
void __iomem *regs;
resource_size_t pciaddr;
unsigned int addr_len, i, pci_using_dac;
- DECLARE_MAC_BUF(mac);
#ifndef MODULE
static int version_printed;
@@ -1931,26 +1949,13 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
cpu_to_le16(read_eeprom (regs, i + 7, addr_len));
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- dev->open = cp_open;
- dev->stop = cp_close;
- dev->set_multicast_list = cp_set_rx_mode;
- dev->hard_start_xmit = cp_start_xmit;
- dev->get_stats = cp_get_stats;
- dev->do_ioctl = cp_ioctl;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = cp_poll_controller;
-#endif
+ dev->netdev_ops = &cp_netdev_ops;
netif_napi_add(dev, &cp->napi, cp_rx_poll, 16);
-#ifdef BROKEN
- dev->change_mtu = cp_change_mtu;
-#endif
dev->ethtool_ops = &cp_ethtool_ops;
- dev->tx_timeout = cp_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#if CP_VLAN_TAG_USED
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = cp_vlan_rx_register;
#endif
if (pci_using_dac)
@@ -1967,10 +1972,10 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_iomap;
printk (KERN_INFO "%s: RTL-8139C+ at 0x%lx, "
- "%s, IRQ %d\n",
+ "%pM, IRQ %d\n",
dev->name,
dev->base_addr,
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
pci_set_drvdata(pdev, dev);
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 63f906b04899..fe370f805793 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -741,8 +741,7 @@ static void rtl8139_chip_reset (void __iomem *ioaddr)
}
-static int __devinit rtl8139_init_board (struct pci_dev *pdev,
- struct net_device **dev_out)
+static __devinit struct net_device * rtl8139_init_board (struct pci_dev *pdev)
{
void __iomem *ioaddr;
struct net_device *dev;
@@ -756,13 +755,11 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev,
assert (pdev != NULL);
- *dev_out = NULL;
-
/* dev and priv zeroed in alloc_etherdev */
dev = alloc_etherdev (sizeof (*tp));
if (dev == NULL) {
dev_err(&pdev->dev, "Unable to alloc new net device\n");
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -906,16 +903,29 @@ match:
rtl8139_chip_reset (ioaddr);
- *dev_out = dev;
- return 0;
+ return dev;
err_out:
__rtl8139_cleanup_dev (dev);
if (disable_dev_on_err)
pci_disable_device (pdev);
- return rc;
+ return ERR_PTR(rc);
}
+static const struct net_device_ops rtl8139_netdev_ops = {
+ .ndo_open = rtl8139_open,
+ .ndo_stop = rtl8139_close,
+ .ndo_get_stats = rtl8139_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_start_xmit = rtl8139_start_xmit,
+ .ndo_set_multicast_list = rtl8139_set_rx_mode,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_tx_timeout = rtl8139_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = rtl8139_poll_controller,
+#endif
+
+};
static int __devinit rtl8139_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -925,7 +935,6 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev,
int i, addr_len, option;
void __iomem *ioaddr;
static int board_idx = -1;
- DECLARE_MAC_BUF(mac);
assert (pdev != NULL);
assert (ent != NULL);
@@ -959,9 +968,9 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev,
use_io = 1;
}
- i = rtl8139_init_board (pdev, &dev);
- if (i < 0)
- return i;
+ dev = rtl8139_init_board (pdev);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
assert (dev != NULL);
tp = netdev_priv(dev);
@@ -977,19 +986,10 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev,
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
/* The Rtl8139-specific entries in the device structure. */
- dev->open = rtl8139_open;
- dev->hard_start_xmit = rtl8139_start_xmit;
- netif_napi_add(dev, &tp->napi, rtl8139_poll, 64);
- dev->stop = rtl8139_close;
- dev->get_stats = rtl8139_get_stats;
- dev->set_multicast_list = rtl8139_set_rx_mode;
- dev->do_ioctl = netdev_ioctl;
+ dev->netdev_ops = &rtl8139_netdev_ops;
dev->ethtool_ops = &rtl8139_ethtool_ops;
- dev->tx_timeout = rtl8139_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rtl8139_poll_controller;
-#endif
+ netif_napi_add(dev, &tp->napi, rtl8139_poll, 64);
/* note: the hardware is not capable of sg/csum/highdma, however
* through the use of skb_copy_and_csum_dev we enable these
@@ -1024,11 +1024,11 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev,
pci_set_drvdata (pdev, dev);
printk (KERN_INFO "%s: %s at 0x%lx, "
- "%s, IRQ %d\n",
+ "%pM, IRQ %d\n",
dev->name,
board_info[ent->driver_data].name,
dev->base_addr,
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
@@ -2026,7 +2026,6 @@ no_early_rx:
skb->protocol = eth_type_trans (skb, dev);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
@@ -2129,7 +2128,7 @@ static int rtl8139_poll(struct napi_struct *napi, int budget)
*/
spin_lock_irqsave(&tp->lock, flags);
RTL_W16_F(IntrMask, rtl8139_intr_mask);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
spin_unlock_irqrestore(&tp->lock, flags);
}
spin_unlock(&tp->rx_lock);
@@ -2179,9 +2178,9 @@ static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance)
/* Receive packets are processed by poll routine.
If not running start it now. */
if (status & RxAckBits){
- if (netif_rx_schedule_prep(dev, &tp->napi)) {
+ if (netif_rx_schedule_prep(&tp->napi)) {
RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
- __netif_rx_schedule(dev, &tp->napi);
+ __netif_rx_schedule(&tp->napi);
}
}
diff --git a/drivers/net/82596.c b/drivers/net/82596.c
index da292e647eb1..b273596368e3 100644
--- a/drivers/net/82596.c
+++ b/drivers/net/82596.c
@@ -457,7 +457,7 @@ static inline int wait_cfg(struct net_device *dev, struct i596_cmd *cmd, int del
static void i596_display_data(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
struct i596_cmd *cmd;
struct i596_rfd *rfd;
struct i596_rbd *rbd;
@@ -527,7 +527,7 @@ static irqreturn_t i596_error(int irq, void *dev_id)
static inline void init_rx_bufs(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
int i;
struct i596_rfd *rfd;
struct i596_rbd *rbd;
@@ -578,7 +578,7 @@ static inline void init_rx_bufs(struct net_device *dev)
static inline void remove_rx_bufs(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
struct i596_rbd *rbd;
int i;
@@ -592,7 +592,7 @@ static inline void remove_rx_bufs(struct net_device *dev)
static void rebuild_rx_bufs(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
int i;
/* Ensure rx frame/buffer descriptors are tidy */
@@ -611,7 +611,7 @@ static void rebuild_rx_bufs(struct net_device *dev)
static int init_i596_mem(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
#if !defined(ENABLE_MVME16x_NET) && !defined(ENABLE_BVME6000_NET) || defined(ENABLE_APRICOT)
short ioaddr = dev->base_addr;
#endif
@@ -764,7 +764,7 @@ failed:
static inline int i596_rx(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
struct i596_rfd *rfd;
struct i596_rbd *rbd;
int frames = 0;
@@ -841,7 +841,6 @@ memory_squeeze:
pkt_len);
#endif
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes+=pkt_len;
}
@@ -959,7 +958,7 @@ static void i596_reset(struct net_device *dev, struct i596_private *lp,
static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
int ioaddr = dev->base_addr;
unsigned long flags;
@@ -1029,7 +1028,7 @@ static int i596_open(struct net_device *dev)
static void i596_tx_timeout (struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
int ioaddr = dev->base_addr;
/* Transmitter timeout, serious problems. */
@@ -1058,7 +1057,7 @@ static void i596_tx_timeout (struct net_device *dev)
static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
struct tx_cmd *tx_cmd;
struct i596_tbd *tbd;
short length = skb->len;
@@ -1116,12 +1115,8 @@ static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void print_eth(unsigned char *add, char *str)
{
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
-
- printk(KERN_DEBUG "i596 0x%p, %s --> %s %02X%02X, %s\n",
- add, print_mac(mac, add + 6), print_mac(mac2, add),
- add[12], add[13], str);
+ printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n",
+ add, add + 6, add, add[12], add[13], str);
}
static int io = 0x300;
@@ -1244,9 +1239,9 @@ found:
dev->tx_timeout = i596_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
- dev->priv = (void *)(dev->mem_start);
+ dev->ml_priv = (void *)(dev->mem_start);
- lp = dev->priv;
+ lp = dev->ml_priv;
DEB(DEB_INIT,printk(KERN_DEBUG "%s: lp at 0x%08lx (%zd bytes), "
"lp->scb at 0x%08lx\n",
dev->name, (unsigned long)lp,
@@ -1307,7 +1302,7 @@ static irqreturn_t i596_interrupt(int irq, void *dev_id)
}
ioaddr = dev->base_addr;
- lp = dev->priv;
+ lp = dev->ml_priv;
spin_lock (&lp->lock);
@@ -1450,7 +1445,7 @@ static irqreturn_t i596_interrupt(int irq, void *dev_id)
static int i596_close(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
unsigned long flags;
netif_stop_queue(dev);
@@ -1500,7 +1495,7 @@ static int i596_close(struct net_device *dev)
static void set_multicast_list(struct net_device *dev)
{
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = dev->ml_priv;
int config = 0, cnt;
DEB(DEB_MULTI,printk(KERN_DEBUG "%s: set multicast list, %d entries, promisc %s, allmulti %s\n",
@@ -1544,7 +1539,6 @@ static void set_multicast_list(struct net_device *dev)
struct dev_mc_list *dmi;
unsigned char *cp;
struct mc_cmd *cmd;
- DECLARE_MAC_BUF(mac);
if (wait_cfg(dev, &lp->mc_cmd.cmd, 1000, "multicast list change request timed out"))
return;
@@ -1555,8 +1549,8 @@ static void set_multicast_list(struct net_device *dev)
for (dmi = dev->mc_list; cnt && dmi != NULL; dmi = dmi->next, cnt--, cp += 6) {
memcpy(cp, dmi->dmi_addr, 6);
if (i596_debug > 1)
- DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %s\n",
- dev->name, print_mac(mac, cp)));
+ DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %pM\n",
+ dev->name, cp));
}
i596_add_cmd(dev, &cmd->cmd);
}
@@ -1604,9 +1598,3 @@ void __exit cleanup_module(void)
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 82596.c"
- * End:
- */
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index f72a2e87d569..fbe609a51e02 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -17,6 +17,30 @@ int ei_close(struct net_device *dev)
}
EXPORT_SYMBOL(ei_close);
+int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(ei_start_xmit);
+
+struct net_device_stats *ei_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(ei_get_stats);
+
+void ei_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(ei_set_multicast_list);
+
+void ei_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(ei_tx_timeout);
+
irqreturn_t ei_interrupt(int irq, void *dev_id)
{
return __ei_interrupt(irq, dev_id);
@@ -31,9 +55,33 @@ void ei_poll(struct net_device *dev)
EXPORT_SYMBOL(ei_poll);
#endif
+const struct net_device_ops ei_netdev_ops = {
+ .ndo_open = ei_open,
+ .ndo_stop = ei_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+EXPORT_SYMBOL(ei_netdev_ops);
+
struct net_device *__alloc_ei_netdev(int size)
{
- return ____alloc_ei_netdev(size);
+ struct net_device *dev = ____alloc_ei_netdev(size);
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
+ if (dev) {
+ dev->hard_start_xmit = ei_start_xmit;
+ dev->get_stats = ei_get_stats;
+ dev->set_multicast_list = ei_set_multicast_list;
+ dev->tx_timeout = ei_tx_timeout;
+ }
+#endif
+ return dev;
}
EXPORT_SYMBOL(__alloc_ei_netdev);
diff --git a/drivers/net/8390.h b/drivers/net/8390.h
index 8e209f5e7c11..3c61d6d2748a 100644
--- a/drivers/net/8390.h
+++ b/drivers/net/8390.h
@@ -33,11 +33,19 @@ extern void ei_poll(struct net_device *dev);
extern void eip_poll(struct net_device *dev);
#endif
+
/* Without I/O delay - non ISA or later chips */
extern void NS8390_init(struct net_device *dev, int startp);
extern int ei_open(struct net_device *dev);
extern int ei_close(struct net_device *dev);
extern irqreturn_t ei_interrupt(int irq, void *dev_id);
+extern void ei_tx_timeout(struct net_device *dev);
+extern int ei_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void ei_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *ei_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops ei_netdev_ops;
+
extern struct net_device *__alloc_ei_netdev(int size);
static inline struct net_device *alloc_ei_netdev(void)
{
@@ -49,6 +57,13 @@ extern void NS8390p_init(struct net_device *dev, int startp);
extern int eip_open(struct net_device *dev);
extern int eip_close(struct net_device *dev);
extern irqreturn_t eip_interrupt(int irq, void *dev_id);
+extern void eip_tx_timeout(struct net_device *dev);
+extern int eip_start_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void eip_set_multicast_list(struct net_device *dev);
+extern struct net_device_stats *eip_get_stats(struct net_device *dev);
+
+extern const struct net_device_ops eip_netdev_ops;
+
extern struct net_device *__alloc_eip_netdev(int size);
static inline struct net_device *alloc_eip_netdev(void)
{
diff --git a/drivers/net/8390p.c b/drivers/net/8390p.c
index 4c6eea4611a2..ee70b358a816 100644
--- a/drivers/net/8390p.c
+++ b/drivers/net/8390p.c
@@ -22,6 +22,30 @@ int eip_close(struct net_device *dev)
}
EXPORT_SYMBOL(eip_close);
+int eip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ return __ei_start_xmit(skb, dev);
+}
+EXPORT_SYMBOL(eip_start_xmit);
+
+struct net_device_stats *eip_get_stats(struct net_device *dev)
+{
+ return __ei_get_stats(dev);
+}
+EXPORT_SYMBOL(eip_get_stats);
+
+void eip_set_multicast_list(struct net_device *dev)
+{
+ __ei_set_multicast_list(dev);
+}
+EXPORT_SYMBOL(eip_set_multicast_list);
+
+void eip_tx_timeout(struct net_device *dev)
+{
+ __ei_tx_timeout(dev);
+}
+EXPORT_SYMBOL(eip_tx_timeout);
+
irqreturn_t eip_interrupt(int irq, void *dev_id)
{
return __ei_interrupt(irq, dev_id);
@@ -36,9 +60,33 @@ void eip_poll(struct net_device *dev)
EXPORT_SYMBOL(eip_poll);
#endif
+const struct net_device_ops eip_netdev_ops = {
+ .ndo_open = eip_open,
+ .ndo_stop = eip_close,
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+EXPORT_SYMBOL(eip_netdev_ops);
+
struct net_device *__alloc_eip_netdev(int size)
{
- return ____alloc_ei_netdev(size);
+ struct net_device *dev = ____alloc_ei_netdev(size);
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
+ if (dev) {
+ dev->hard_start_xmit = eip_start_xmit;
+ dev->get_stats = eip_get_stats;
+ dev->set_multicast_list = eip_set_multicast_list;
+ dev->tx_timeout = eip_tx_timeout;
+ }
+#endif
+ return dev;
}
EXPORT_SYMBOL(__alloc_eip_netdev);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 231eeaf1d552..9a18270c1081 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -61,6 +61,7 @@ config DUMMY
config BONDING
tristate "Bonding driver support"
depends on INET
+ depends on IPV6 || IPV6=n
---help---
Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
Channels together. This is called 'Etherchannel' by Cisco,
@@ -978,6 +979,20 @@ config SMC911X
called smc911x. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>
+config SMSC911X
+ tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+ depends on ARM || SUPERH
+ select CRC32
+ select MII
+ select PHYLIB
+ ---help---
+ Say Y here if you want support for SMSC LAN911x and LAN921x families
+ of ethernet controllers.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called smsc911x.
+
config NET_VENDOR_RACAL
bool "Racal-Interlan (Micom) NI cards"
depends on ISA
@@ -1414,19 +1429,6 @@ config TC35815
depends on NET_PCI && PCI && MIPS
select PHYLIB
-config EEPRO100
- tristate "EtherExpressPro/100 support (eepro100, original Becker driver)"
- depends on NET_PCI && PCI
- select MII
- help
- If you have an Intel EtherExpress PRO/100 PCI network (Ethernet)
- card, say Y 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 eepro100.
-
-
config E100
tristate "Intel(R) PRO/100+ support"
depends on NET_PCI && PCI
@@ -1636,6 +1638,22 @@ config EPIC100
More specific information and updates are available from
<http://www.scyld.com/network/epic100.html>.
+config SMSC9420
+ tristate "SMSC LAN9420 PCI ethernet adapter support"
+ depends on NET_PCI && PCI
+ select CRC32
+ select PHYLIB
+ select SMSC_PHY
+ help
+ This is a driver for SMSC's LAN9420 PCI ethernet adapter.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.linuxdoc.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smsc9420. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>
+
config SUNDANCE
tristate "Sundance Alta support"
depends on NET_PCI && PCI
@@ -1981,10 +1999,10 @@ config IP1000
will be called ipg. This is recommended.
config IGB
- tristate "Intel(R) 82575 PCI-Express Gigabit Ethernet support"
+ tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support"
depends on PCI
---help---
- This driver supports Intel(R) 82575 gigabit ethernet family of
+ This driver supports Intel(R) 82575/82576 gigabit ethernet family of
adapters. For more information on how to identify your adapter, go
to the Adapter & Driver ID Guide at:
@@ -2276,10 +2294,6 @@ config UGETH_MAGIC_PACKET
bool "Magic Packet detection support"
depends on UCC_GETH
-config UGETH_FILTERING
- bool "Mac address filtering support"
- depends on UCC_GETH
-
config UGETH_TX_ON_DEMAND
bool "Transmit on Demand support"
depends on UCC_GETH
@@ -2385,9 +2399,14 @@ config CHELSIO_T1_1G
Enables support for Chelsio's gigabit Ethernet PCI cards. If you
are using only 10G cards say 'N' here.
+config CHELSIO_T3_DEPENDS
+ tristate
+ depends on PCI && INET
+ default y
+
config CHELSIO_T3
tristate "Chelsio Communications T3 10Gb Ethernet support"
- depends on PCI && INET
+ depends on CHELSIO_T3_DEPENDS
select FW_LOADER
select INET_LRO
help
@@ -2450,6 +2469,16 @@ config IXGBE_DCA
driver. DCA is a method for warming the CPU cache before data
is used, with the intent of lessening the impact of cache misses.
+config IXGBE_DCB
+ bool "Data Center Bridging (DCB) Support"
+ default n
+ depends on IXGBE && DCB
+ ---help---
+ Say Y here if you want to use Data Center Bridging (DCB) in the
+ driver.
+
+ If unsure, say N.
+
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
@@ -2626,7 +2655,7 @@ config RIONET_RX_SIZE
default "128"
config FDDI
- bool "FDDI driver support"
+ tristate "FDDI driver support"
depends on (PCI || EISA || TC)
help
Fiber Distributed Data Interface is a high speed local area network
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 017383ad5ec6..e5c34b464211 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -53,10 +53,10 @@ obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCNET32) += pcnet32.o
-obj-$(CONFIG_EEPRO100) += eepro100.o
obj-$(CONFIG_E100) += e100.o
obj-$(CONFIG_TLAN) += tlan.o
obj-$(CONFIG_EPIC100) += epic100.o
+obj-$(CONFIG_SMSC9420) += smsc9420.o
obj-$(CONFIG_SIS190) += sis190.o
obj-$(CONFIG_SIS900) += sis900.o
obj-$(CONFIG_R6040) += r6040.o
@@ -98,7 +98,7 @@ obj-$(CONFIG_HAMACHI) += hamachi.o
obj-$(CONFIG_NET) += Space.o loopback.o
obj-$(CONFIG_SEEQ8005) += seeq8005.o
obj-$(CONFIG_NET_SB1000) += sb1000.o
-obj-$(CONFIG_MAC8390) += mac8390.o
+obj-$(CONFIG_MAC8390) += mac8390.o 8390.o
obj-$(CONFIG_APNE) += apne.o 8390.o
obj-$(CONFIG_PCMCIA_PCNET) += 8390.o
obj-$(CONFIG_HP100) += hp100.o
@@ -125,7 +125,7 @@ obj-$(CONFIG_NE3210) += ne3210.o 8390.o
obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
obj-$(CONFIG_B44) += b44.o
obj-$(CONFIG_FORCEDETH) += forcedeth.o
-obj-$(CONFIG_NE_H8300) += ne-h8300.o
+obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
@@ -191,7 +191,7 @@ obj-$(CONFIG_SC92031) += sc92031.o
obj-$(CONFIG_LP486E) += lp486e.o
obj-$(CONFIG_ETH16I) += eth16i.o
-obj-$(CONFIG_ZORRO8390) += zorro8390.o
+obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
obj-$(CONFIG_EQUALIZER) += eql.o
@@ -203,7 +203,7 @@ obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o
obj-$(CONFIG_DECLANCE) += declance.o
obj-$(CONFIG_ATARILANCE) += atarilance.o
obj-$(CONFIG_A2065) += a2065.o
-obj-$(CONFIG_HYDRA) += hydra.o
+obj-$(CONFIG_HYDRA) += hydra.o 8390.o
obj-$(CONFIG_ARIADNE) += ariadne.o
obj-$(CONFIG_CS89x0) += cs89x0.o
obj-$(CONFIG_MACSONIC) += macsonic.o
@@ -220,6 +220,7 @@ obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c
index 9c0837435b68..7a60bdd9a242 100644
--- a/drivers/net/a2065.c
+++ b/drivers/net/a2065.c
@@ -324,7 +324,6 @@ static int lance_rx (struct net_device *dev)
len);
skb->protocol = eth_type_trans (skb, dev);
netif_rx (skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
@@ -710,7 +709,6 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
unsigned long board, base_addr, mem_start;
struct resource *r1, *r2;
int err;
- DECLARE_MAC_BUF(mac);
board = z->resource.start;
base_addr = board+A2065_LANCE;
@@ -787,8 +785,7 @@ static int __devinit a2065_init_one(struct zorro_dev *z,
zorro_set_drvdata(z, dev);
printk(KERN_INFO "%s: A2065 at 0x%08lx, Ethernet Address "
- "%s\n", dev->name, board,
- print_mac(mac, dev->dev_addr));
+ "%pM\n", dev->name, board, dev->dev_addr);
return 0;
}
diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c
index b1448637107f..071a851a2ea1 100644
--- a/drivers/net/ac3200.c
+++ b/drivers/net/ac3200.c
@@ -146,7 +146,6 @@ out:
static int __init ac_probe1(int ioaddr, struct net_device *dev)
{
int i, retval;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, AC_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -171,8 +170,8 @@ static int __init ac_probe1(int ioaddr, struct net_device *dev)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
- printk(KERN_DEBUG "AC3200 in EISA slot %d, node %s",
- ioaddr/0x1000, print_mac(mac, dev->dev_addr));
+ printk(KERN_DEBUG "AC3200 in EISA slot %d, node %pM",
+ ioaddr/0x1000, dev->dev_addr);
#if 0
/* Check the vendor ID/prefix. Redundant after checking the EISA ID */
if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index 66de80b64b92..517fce48d94a 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -450,6 +450,20 @@ static const struct ethtool_ops ace_ethtool_ops = {
static void ace_watchdog(struct net_device *dev);
+static const struct net_device_ops ace_netdev_ops = {
+ .ndo_open = ace_open,
+ .ndo_stop = ace_close,
+ .ndo_tx_timeout = ace_watchdog,
+ .ndo_get_stats = ace_get_stats,
+ .ndo_start_xmit = ace_start_xmit,
+ .ndo_set_multicast_list = ace_set_multicast_list,
+ .ndo_set_mac_address = ace_set_mac_addr,
+ .ndo_change_mtu = ace_change_mtu,
+#if ACENIC_DO_VLAN
+ .ndo_vlan_rx_register = ace_vlan_rx_register,
+#endif
+};
+
static int __devinit acenic_probe_one(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -466,27 +480,19 @@ static int __devinit acenic_probe_one(struct pci_dev *pdev,
SET_NETDEV_DEV(dev, &pdev->dev);
- ap = dev->priv;
+ ap = netdev_priv(dev);
ap->pdev = pdev;
ap->name = pci_name(pdev);
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
#if ACENIC_DO_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = ace_vlan_rx_register;
#endif
- dev->tx_timeout = &ace_watchdog;
dev->watchdog_timeo = 5*HZ;
- dev->open = &ace_open;
- dev->stop = &ace_close;
- dev->hard_start_xmit = &ace_start_xmit;
- dev->get_stats = &ace_get_stats;
- dev->set_multicast_list = &ace_set_multicast_list;
+ dev->netdev_ops = &ace_netdev_ops;
SET_ETHTOOL_OPS(dev, &ace_ethtool_ops);
- dev->set_mac_address = &ace_set_mac_addr;
- dev->change_mtu = &ace_change_mtu;
/* we only display this string ONCE */
if (!boards_found)
@@ -892,7 +898,6 @@ static int __devinit ace_init(struct net_device *dev)
int board_idx, ecode = 0;
short i;
unsigned char cache_size;
- DECLARE_MAC_BUF(mac);
ap = netdev_priv(dev);
regs = ap->regs;
@@ -1019,7 +1024,7 @@ static int __devinit ace_init(struct net_device *dev)
dev->dev_addr[4] = (mac2 >> 8) & 0xff;
dev->dev_addr[5] = mac2 & 0xff;
- printk("MAC: %s\n", print_mac(mac, dev->dev_addr));
+ printk("MAC: %pM\n", dev->dev_addr);
/*
* Looks like this is necessary to deal with on all architectures,
@@ -2034,7 +2039,6 @@ static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm)
#endif
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += retdesc->size;
@@ -3220,10 +3224,3 @@ static int __devinit read_eeprom_byte(struct net_device *dev,
ap->name, offset);
goto out;
}
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c"
- * End:
- */
diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c
index 07a6697e3635..187ac6eb6e94 100644
--- a/drivers/net/amd8111e.c
+++ b/drivers/net/amd8111e.c
@@ -809,7 +809,6 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
lp->coal_conf.rx_packets++;
lp->coal_conf.rx_bytes += pkt_len;
num_rx_pkt++;
- dev->last_rx = jiffies;
err_next_pkt:
lp->rx_ring[rx_index].buff_phy_addr
@@ -832,7 +831,7 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget)
if (rx_pkt_limit > 0) {
/* Receive descriptor is empty now */
spin_lock_irqsave(&lp->lock, flags);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
writel(VAL0|RINTEN0, mmio + INTEN0);
writel(VAL2 | RDMD0, mmio + CMD0);
spin_unlock_irqrestore(&lp->lock, flags);
@@ -1171,11 +1170,11 @@ static irqreturn_t amd8111e_interrupt(int irq, void *dev_id)
/* Check if Receive Interrupt has occurred. */
if (intr0 & RINT0) {
- if (netif_rx_schedule_prep(dev, &lp->napi)) {
+ if (netif_rx_schedule_prep(&lp->napi)) {
/* Disable receive interupts */
writel(RINTEN0, mmio + INTEN0);
/* Schedule a polling routine */
- __netif_rx_schedule(dev, &lp->napi);
+ __netif_rx_schedule(&lp->napi);
} else if (intren0 & RINTEN0) {
printk("************Driver bug! \
interrupt while in poll\n");
@@ -1821,7 +1820,6 @@ static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
unsigned long reg_addr,reg_len;
struct amd8111e_priv* lp;
struct net_device* dev;
- DECLARE_MAC_BUF(mac);
err = pci_enable_device(pdev);
if(err){
@@ -1963,8 +1961,8 @@ static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
printk(KERN_INFO "%s: AMD-8111e Driver Version: %s\n",
dev->name,MODULE_VERS);
- printk(KERN_INFO "%s: [ Rev %x ] PCI 10/100BaseT Ethernet %s\n",
- dev->name, chip_version, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: [ Rev %x ] PCI 10/100BaseT Ethernet %pM\n",
+ dev->name, chip_version, dev->dev_addr);
if (lp->ext_phy_id)
printk(KERN_INFO "%s: Found MII PHY ID 0x%08x at address 0x%02x\n",
dev->name, lp->ext_phy_id, lp->ext_phy_addr);
diff --git a/drivers/net/apne.c b/drivers/net/apne.c
index 867f6fff543c..1437f5d12121 100644
--- a/drivers/net/apne.c
+++ b/drivers/net/apne.c
@@ -78,9 +78,6 @@
struct net_device * __init apne_probe(int unit);
static int apne_probe1(struct net_device *dev, int ioaddr);
-static int apne_open(struct net_device *dev);
-static int apne_close(struct net_device *dev);
-
static void apne_reset_8390(struct net_device *dev);
static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
@@ -207,7 +204,6 @@ static int __init apne_probe1(struct net_device *dev, int ioaddr)
int neX000, ctron;
#endif
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
if (ei_debug && version_printed++ == 0)
printk(version);
@@ -315,6 +311,7 @@ static int __init apne_probe1(struct net_device *dev, int ioaddr)
dev->base_addr = ioaddr;
dev->irq = IRQ_AMIGA_PORTS;
+ dev->netdev_ops = &ei_netdev_ops;
/* Install the Interrupt handler */
i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev);
@@ -323,7 +320,7 @@ static int __init apne_probe1(struct net_device *dev, int ioaddr)
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = SA_prom[i];
- printk(" %s\n", print_mac(mac, dev->dev_addr));
+ printk(" %pM\n", dev->dev_addr);
printk("%s: %s found.\n", dev->name, name);
@@ -338,11 +335,7 @@ static int __init apne_probe1(struct net_device *dev, int ioaddr)
ei_status.block_input = &apne_block_input;
ei_status.block_output = &apne_block_output;
ei_status.get_8390_hdr = &apne_get_8390_hdr;
- dev->open = &apne_open;
- dev->stop = &apne_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+
NS8390_init(dev, 0);
pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */
@@ -353,22 +346,6 @@ static int __init apne_probe1(struct net_device *dev, int ioaddr)
return 0;
}
-static int
-apne_open(struct net_device *dev)
-{
- ei_open(dev);
- return 0;
-}
-
-static int
-apne_close(struct net_device *dev)
-{
- if (ei_debug > 1)
- printk("%s: Shutting down ethercard.\n", dev->name);
- ei_close(dev);
- return 0;
-}
-
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
static void
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 735fc9476403..54819a34ba0a 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -851,7 +851,6 @@ static void cops_rx(struct net_device *dev)
/* Send packet to a higher place. */
netif_rx(skb);
- dev->last_rx = jiffies;
}
static void cops_timeout(struct net_device *dev)
@@ -1025,11 +1024,3 @@ static void __exit cops_module_exit(void)
module_init(cops_module_init);
module_exit(cops_module_exit);
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c cops.c"
- * c-basic-offset: 4
- * c-file-offsets: ((substatement-open . 0))
- * End:
- */
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c
index 1071144edd66..9a0be9b2eaad 100644
--- a/drivers/net/appletalk/ipddp.c
+++ b/drivers/net/appletalk/ipddp.c
@@ -108,7 +108,7 @@ static struct net_device * __init ipddp_init(void)
*/
static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
{
- return dev->priv;
+ return netdev_priv(dev);
}
/*
@@ -170,8 +170,8 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
- ((struct net_device_stats *) dev->priv)->tx_packets++;
- ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
+ ((struct net_device_stats *) netdev_priv(dev))->tx_packets++;
+ ((struct net_device_stats *) netdev_priv(dev))->tx_bytes += skb->len;
if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0)
dev_kfree_skb(skb);
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index fef5560bc7a2..dc4d49605603 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -726,7 +726,8 @@ static int sendup_buffer (struct net_device *dev)
int dnode, snode, llaptype, len;
int sklen;
struct sk_buff *skb;
- struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
+ struct ltpc_private *ltpc_priv = netdev_priv(dev);
+ struct net_device_stats *stats = &ltpc_priv->stats;
struct lt_rcvlap *ltc = (struct lt_rcvlap *) ltdmacbuf;
if (ltc->command != LT_RCVLAP) {
@@ -783,7 +784,6 @@ static int sendup_buffer (struct net_device *dev)
/* toss it onwards */
netif_rx(skb);
- dev->last_rx = jiffies;
return 0;
}
@@ -823,7 +823,8 @@ static int ltpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr;
/* we'll keep the localtalk node address in dev->pa_addr */
- struct atalk_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr;
+ struct ltpc_private *ltpc_priv = netdev_priv(dev);
+ struct atalk_addr *aa = &ltpc_priv->my_addr;
struct lt_init c;
int ltflags;
@@ -904,7 +905,8 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
* and skb->len is the length of the ddp data + ddp header
*/
- struct net_device_stats *stats = &((struct ltpc_private *)dev->priv)->stats;
+ struct ltpc_private *ltpc_priv = netdev_priv(dev);
+ struct net_device_stats *stats = &ltpc_priv->stats;
int i;
struct lt_sendlap cbuf;
@@ -943,7 +945,8 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *ltpc_get_stats(struct net_device *dev)
{
- struct net_device_stats *stats = &((struct ltpc_private *) dev->priv)->stats;
+ struct ltpc_private *ltpc_priv = netdev_priv(dev);
+ struct net_device_stats *stats = &ltpc_priv->stats;
return stats;
}
diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c
index e0a18e7c73cb..3ff9affb1a91 100644
--- a/drivers/net/arcnet/arc-rawmode.c
+++ b/drivers/net/arcnet/arc-rawmode.c
@@ -87,7 +87,7 @@ MODULE_LICENSE("GPL");
static void rx(struct net_device *dev, int bufnum,
struct archdr *pkthdr, int length)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct sk_buff *skb;
struct archdr *pkt = pkthdr;
int ofs;
@@ -125,7 +125,6 @@ static void rx(struct net_device *dev, int bufnum,
skb->protocol = __constant_htons(ETH_P_ARCNET);
;
netif_rx(skb);
- dev->last_rx = jiffies;
}
@@ -168,7 +167,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct arc_hardware *hard = &pkt->hard;
int ofs;
diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c
index 8c8d6c453c45..e3082a9350fc 100644
--- a/drivers/net/arcnet/arc-rimi.c
+++ b/drivers/net/arcnet/arc-rimi.c
@@ -194,7 +194,7 @@ static int __init arcrimi_found(struct net_device *dev)
/* initialize the rest of the device structure. */
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->card_name = "RIM I";
lp->hw.command = arcrimi_command;
lp->hw.status = arcrimi_status;
@@ -260,7 +260,7 @@ err_free_irq:
*/
static int arcrimi_reset(struct net_device *dev, int really_reset)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->mem_start + 0x800;
BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
@@ -281,7 +281,7 @@ static int arcrimi_reset(struct net_device *dev, int really_reset)
static void arcrimi_setmask(struct net_device *dev, int mask)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->mem_start + 0x800;
AINTMASK(mask);
@@ -289,7 +289,7 @@ static void arcrimi_setmask(struct net_device *dev, int mask)
static int arcrimi_status(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->mem_start + 0x800;
return ASTATUS();
@@ -297,7 +297,7 @@ static int arcrimi_status(struct net_device *dev)
static void arcrimi_command(struct net_device *dev, int cmd)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->mem_start + 0x800;
ACOMMAND(cmd);
@@ -306,7 +306,7 @@ static void arcrimi_command(struct net_device *dev, int cmd)
static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
void *buf, int count)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
}
@@ -315,7 +315,7 @@ static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
void *buf, int count)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
}
@@ -361,7 +361,7 @@ static int __init arc_rimi_init(void)
static void __exit arc_rimi_exit(void)
{
struct net_device *dev = my_dev;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
unregister_netdev(dev);
iounmap(lp->mem_start);
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
index a5b07691e466..6b53e5ed125c 100644
--- a/drivers/net/arcnet/arcnet.c
+++ b/drivers/net/arcnet/arcnet.c
@@ -181,7 +181,7 @@ EXPORT_SYMBOL(arcnet_dump_skb);
static void arcnet_dump_packet(struct net_device *dev, int bufnum,
char *desc, int take_arcnet_lock)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int i, length;
unsigned long flags = 0;
static uint8_t buf[512];
@@ -247,7 +247,7 @@ void arcnet_unregister_proto(struct ArcProto *proto)
*/
static void release_arcbuf(struct net_device *dev, int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int i;
lp->buf_queue[lp->first_free_buf++] = bufnum;
@@ -269,7 +269,7 @@ static void release_arcbuf(struct net_device *dev, int bufnum)
*/
static int get_arcbuf(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int buf = -1, i;
if (!atomic_dec_and_test(&lp->buf_lock)) {
@@ -357,7 +357,7 @@ struct net_device *alloc_arcdev(char *name)
dev = alloc_netdev(sizeof(struct arcnet_local),
name && *name ? name : "arc%d", arcdev_setup);
if(dev) {
- struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
spin_lock_init(&lp->lock);
}
@@ -374,7 +374,7 @@ struct net_device *alloc_arcdev(char *name)
*/
static int arcnet_open(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int count, newmtu, error;
BUGMSG(D_INIT,"opened.");
@@ -474,7 +474,7 @@ static int arcnet_open(struct net_device *dev)
/* The inverse routine to arcnet_open - shuts down the card. */
static int arcnet_close(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
netif_stop_queue(dev);
@@ -556,7 +556,7 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
static int arcnet_rebuild_header(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int status = 0; /* default is failure */
unsigned short type;
uint8_t daddr=0;
@@ -603,7 +603,7 @@ static int arcnet_rebuild_header(struct sk_buff *skb)
/* Called by the kernel in order to transmit a packet. */
static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct archdr *pkt;
struct arc_rfc1201 *soft;
struct ArcProto *proto;
@@ -693,7 +693,7 @@ static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev)
*/
static int go_tx(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
BUGMSG(D_DURING, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
ASTATUS(), lp->intmask, lp->next_tx, lp->cur_tx);
@@ -723,7 +723,7 @@ static int go_tx(struct net_device *dev)
static void arcnet_timeout(struct net_device *dev)
{
unsigned long flags;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int status = ASTATUS();
char *msg;
@@ -771,8 +771,8 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
BUGMSG(D_DURING, "\n");
BUGMSG(D_DURING, "in arcnet_interrupt\n");
-
- lp = dev->priv;
+
+ lp = netdev_priv(dev);
BUG_ON(!lp);
spin_lock(&lp->lock);
@@ -1010,7 +1010,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
*/
static void arcnet_rx(struct net_device *dev, int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct archdr pkt;
struct arc_rfc1201 *soft;
int length, ofs;
@@ -1074,7 +1074,7 @@ static void arcnet_rx(struct net_device *dev, int bufnum)
*/
static struct net_device_stats *arcnet_get_stats(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
return &lp->stats;
}
@@ -1091,7 +1091,7 @@ static void null_rx(struct net_device *dev, int bufnum,
static int null_build_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, uint8_t daddr)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
BUGMSG(D_PROTO,
"tx: can't build header for encap %02Xh; load a protocol driver.\n",
@@ -1106,7 +1106,7 @@ static int null_build_header(struct sk_buff *skb, struct net_device *dev,
static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
int length, int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct arc_hardware newpkt;
BUGMSG(D_PROTO, "tx: no encap for this host; load a protocol driver.\n");
diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c
index 02cb8f1c1148..30580bbe252d 100644
--- a/drivers/net/arcnet/capmode.c
+++ b/drivers/net/arcnet/capmode.c
@@ -61,7 +61,7 @@ static struct ArcProto capmode_proto =
};
-void arcnet_cap_init(void)
+static void arcnet_cap_init(void)
{
int count;
@@ -103,7 +103,7 @@ MODULE_LICENSE("GPL");
static void rx(struct net_device *dev, int bufnum,
struct archdr *pkthdr, int length)
{
- struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct sk_buff *skb;
struct archdr *pkt = pkthdr;
char *pktbuf, *pkthdrbuf;
@@ -151,7 +151,6 @@ static void rx(struct net_device *dev, int bufnum,
skb->protocol = __constant_htons(ETH_P_ARCNET);
;
netif_rx(skb);
- dev->last_rx = jiffies;
}
@@ -198,7 +197,7 @@ static int build_header(struct sk_buff *skb,
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
int bufnum)
{
- struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct arc_hardware *hard = &pkt->hard;
int ofs;
@@ -250,7 +249,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
static int ack_tx(struct net_device *dev, int acked)
{
- struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct sk_buff *ackskb;
struct archdr *ackpkt;
int length=sizeof(struct arc_cap);
diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c
index 9289e6103de5..ea53a940272f 100644
--- a/drivers/net/arcnet/com20020-isa.c
+++ b/drivers/net/arcnet/com20020-isa.c
@@ -52,7 +52,7 @@ static int __init com20020isa_probe(struct net_device *dev)
{
int ioaddr;
unsigned long airqmask;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int err;
BUGLVL(D_NORMAL) printk(VERSION);
@@ -151,7 +151,7 @@ static int __init com20020_init(void)
if (node && node != 0xff)
dev->dev_addr[0] = node;
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->backplane = backplane;
lp->clockp = clockp & 7;
lp->clockm = clockm & 3;
diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c
index b8c0fa6d401d..8b51f632581d 100644
--- a/drivers/net/arcnet/com20020-pci.c
+++ b/drivers/net/arcnet/com20020-pci.c
@@ -72,7 +72,7 @@ static int __devinit com20020pci_probe(struct pci_dev *pdev, const struct pci_de
dev = alloc_arcdev(device);
if (!dev)
return -ENOMEM;
- lp = dev->priv;
+ lp = netdev_priv(dev);
pci_set_drvdata(pdev, dev);
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index 70124a944e7d..103688358fb8 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -89,7 +89,7 @@ static void com20020_copy_to_card(struct net_device *dev, int bufnum,
int com20020_check(struct net_device *dev)
{
int ioaddr = dev->base_addr, status;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
ARCRESET0;
mdelay(RESETtime);
@@ -159,7 +159,7 @@ int com20020_found(struct net_device *dev, int shared)
/* Initialize the rest of the device structure. */
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->hw.owner = THIS_MODULE;
lp->hw.command = com20020_command;
@@ -233,7 +233,7 @@ int com20020_found(struct net_device *dev, int shared)
*/
static int com20020_reset(struct net_device *dev, int really_reset)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
u_int ioaddr = dev->base_addr;
u_char inbyte;
@@ -300,7 +300,7 @@ static int com20020_status(struct net_device *dev)
static void com20020_close(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
/* disable transmitter */
@@ -317,7 +317,7 @@ static void com20020_close(struct net_device *dev)
*/
static void com20020_set_mc_list(struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { /* Enable promiscuous mode */
diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c
index 6599f1046c7b..89de29b3b1dc 100644
--- a/drivers/net/arcnet/com90io.c
+++ b/drivers/net/arcnet/com90io.c
@@ -248,7 +248,7 @@ static int __init com90io_found(struct net_device *dev)
return -EBUSY;
}
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->card_name = "COM90xx I/O";
lp->hw.command = com90io_command;
lp->hw.status = com90io_status;
@@ -290,7 +290,7 @@ static int __init com90io_found(struct net_device *dev)
*/
static int com90io_reset(struct net_device *dev, int really_reset)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
short ioaddr = dev->base_addr;
BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c
index 0d45553ff75c..d762fe46251e 100644
--- a/drivers/net/arcnet/com90xx.c
+++ b/drivers/net/arcnet/com90xx.c
@@ -468,7 +468,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
release_mem_region(shmem, MIRROR_SIZE);
return -ENOMEM;
}
- lp = dev->priv;
+ lp = netdev_priv(dev);
/* find the real shared memory start/end points, including mirrors */
/* guess the actual size of one "memory mirror" - the number of
@@ -583,9 +583,9 @@ static void com90xx_setmask(struct net_device *dev, int mask)
*
* However, it does make sure the card is in a defined state.
*/
-int com90xx_reset(struct net_device *dev, int really_reset)
+static int com90xx_reset(struct net_device *dev, int really_reset)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
short ioaddr = dev->base_addr;
BUGMSG(D_INIT, "Resetting (status=%02Xh)\n", ASTATUS());
@@ -621,7 +621,7 @@ int com90xx_reset(struct net_device *dev, int really_reset)
static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
void *buf, int count)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
}
@@ -630,7 +630,7 @@ static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
void *buf, int count)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
}
@@ -656,7 +656,7 @@ static void __exit com90xx_exit(void)
for (count = 0; count < numcards; count++) {
dev = cards[count];
- lp = dev->priv;
+ lp = netdev_priv(dev);
unregister_netdev(dev);
free_irq(dev->irq, dev);
diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c
index dab185bc51f1..49d39a9cb696 100644
--- a/drivers/net/arcnet/rfc1051.c
+++ b/drivers/net/arcnet/rfc1051.c
@@ -88,7 +88,7 @@ MODULE_LICENSE("GPL");
*/
static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct archdr *pkt = (struct archdr *) skb->data;
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
@@ -125,7 +125,7 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
static void rx(struct net_device *dev, int bufnum,
struct archdr *pkthdr, int length)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct sk_buff *skb;
struct archdr *pkt = pkthdr;
int ofs;
@@ -159,7 +159,6 @@ static void rx(struct net_device *dev, int bufnum,
skb->protocol = type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
}
@@ -169,7 +168,7 @@ static void rx(struct net_device *dev, int bufnum,
static int build_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, uint8_t daddr)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
@@ -220,7 +219,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct arc_hardware *hard = &pkt->hard;
int ofs;
diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c
index 6d6d95cc4404..2303d3a1f4b6 100644
--- a/drivers/net/arcnet/rfc1201.c
+++ b/drivers/net/arcnet/rfc1201.c
@@ -92,7 +92,7 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
{
struct archdr *pkt = (struct archdr *) skb->data;
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
/* Pull off the arcnet header. */
@@ -134,7 +134,7 @@ static __be16 type_trans(struct sk_buff *skb, struct net_device *dev)
static void rx(struct net_device *dev, int bufnum,
struct archdr *pkthdr, int length)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct sk_buff *skb;
struct archdr *pkt = pkthdr;
struct arc_rfc1201 *soft = &pkthdr->soft.rfc1201;
@@ -230,7 +230,6 @@ static void rx(struct net_device *dev, int bufnum,
skb->protocol = type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
} else { /* split packet */
/*
* NOTE: MSDOS ARP packet correction should only need to apply to
@@ -366,7 +365,6 @@ static void rx(struct net_device *dev, int bufnum,
skb->protocol = type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
}
}
}
@@ -376,7 +374,7 @@ static void rx(struct net_device *dev, int bufnum,
static int build_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, uint8_t daddr)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
@@ -443,7 +441,7 @@ static int build_header(struct sk_buff *skb, struct net_device *dev,
static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
struct arc_rfc1201 *soft, int softlen, int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
int ofs;
/* assume length <= XMTU: someone should have handled that by now. */
@@ -476,7 +474,7 @@ static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
const int maxsegsize = XMTU - RFC1201_HDR_SIZE;
struct Outgoing *out;
@@ -511,7 +509,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
static int continue_tx(struct net_device *dev, int bufnum)
{
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
struct Outgoing *out = &lp->outgoing;
struct arc_hardware *hard = &out->pkt->hard;
struct arc_rfc1201 *soft = &out->pkt->soft.rfc1201, *newsoft;
diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c
index 29e53eb71c74..e1d72e06f3e1 100644
--- a/drivers/net/ariadne.c
+++ b/drivers/net/ariadne.c
@@ -165,7 +165,6 @@ static int __devinit ariadne_init_one(struct zorro_dev *z,
struct net_device *dev;
struct ariadne_private *priv;
int err;
- DECLARE_MAC_BUF(mac);
r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960");
if (!r1)
@@ -215,9 +214,8 @@ static int __devinit ariadne_init_one(struct zorro_dev *z,
}
zorro_set_drvdata(z, dev);
- printk(KERN_INFO "%s: Ariadne at 0x%08lx, Ethernet Address "
- "%s\n", dev->name, board,
- print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Ariadne at 0x%08lx, Ethernet Address %pM\n",
+ dev->name, board, dev->dev_addr);
return 0;
}
@@ -613,14 +611,10 @@ static int ariadne_start_xmit(struct sk_buff *skb, struct net_device *dev)
#if 0
{
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
-
- printk(KERN_DEBUG "TX pkt type 0x%04x from %s to %s "
+ printk(KERN_DEBUG "TX pkt type 0x%04x from %pM to %pM "
" data 0x%08x len %d\n",
((u_short *)skb->data)[6],
- print_mac(mac, ((const u8 *)skb->data)+6),
- print_mac(mac, (const u8 *)skb->data),
+ skb->data + 6, skb->data,
(int)skb->data, (int)skb->len);
}
#endif
@@ -743,25 +737,22 @@ static int ariadne_rx(struct net_device *dev)
skb->protocol=eth_type_trans(skb,dev);
#if 0
{
- DECLARE_MAC_BUF(mac);
-
printk(KERN_DEBUG "RX pkt type 0x%04x from ",
((u_short *)skb->data)[6]);
{
u_char *ptr = &((u_char *)skb->data)[6];
- printk("%s", print_mac(mac, ptr));
+ printk("%pM", ptr);
}
printk(" to ");
{
u_char *ptr = (u_char *)skb->data;
- printk("%s", print_mac(mac, ptr));
+ printk("%pM", ptr);
}
printk(" data 0x%08x len %d\n", (int)skb->data, (int)skb->len);
}
#endif
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
index 8eda6eeb43b7..2895db13bfa4 100644
--- a/drivers/net/arm/Kconfig
+++ b/drivers/net/arm/Kconfig
@@ -40,6 +40,14 @@ config ARM_AT91_ETHER
If you wish to compile a kernel for the AT91RM9200 and enable
ethernet support, then you should always answer Y to this.
+config ARM_KS8695_ETHER
+ tristate "KS8695 Ethernet support"
+ depends on ARM && ARCH_KS8695
+ select MII
+ help
+ If you wish to compile a kernel for the KS8695 and want to
+ use the internal ethernet then you should answer Y to this.
+
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
@@ -51,7 +59,7 @@ config EP93XX_ETH
config IXP4XX_ETH
tristate "Intel IXP4xx Ethernet support"
depends on ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
- select MII
+ select PHYLIB
help
Say Y here if you want to use built-in Ethernet ports
on IXP4xx processor.
diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
index 7c812ac2b6a5..c69c0cdba4a2 100644
--- a/drivers/net/arm/Makefile
+++ b/drivers/net/arm/Makefile
@@ -4,9 +4,10 @@
#
obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
-obj-$(CONFIG_ARM_ETHERH) += etherh.o
+obj-$(CONFIG_ARM_ETHERH) += etherh.o ../8390.o
obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
index aa4a5246be53..0c628a9e5339 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/arm/am79c961a.c
@@ -532,7 +532,6 @@ am79c961_rx(struct net_device *dev, struct dev_priv *priv)
am_writeword(dev, hdraddr + 2, RMD_OWN);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
priv->stats.rx_bytes += len;
priv->stats.rx_packets ++;
} else {
@@ -745,10 +744,8 @@ static int __init am79c961_probe(struct platform_device *pdev)
ret = register_netdev(dev);
if (ret == 0) {
- DECLARE_MAC_BUF(mac);
-
- printk(KERN_INFO "%s: ether address %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: ether address %pM\n",
+ dev->name, dev->dev_addr);
return 0;
}
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 6f431a887e7e..442938d50380 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -485,7 +485,6 @@ static void update_mac_address(struct net_device *dev)
static int set_mac_address(struct net_device *dev, void* addr)
{
struct sockaddr *address = addr;
- DECLARE_MAC_BUF(mac);
if (!is_valid_ether_addr(address->sa_data))
return -EADDRNOTAVAIL;
@@ -493,8 +492,8 @@ static int set_mac_address(struct net_device *dev, void* addr)
memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
update_mac_address(dev);
- printk("%s: Setting MAC address to %s\n", dev->name,
- print_mac(mac, dev->dev_addr));
+ printk("%s: Setting MAC address to %pM\n", dev->name,
+ dev->dev_addr);
return 0;
}
@@ -894,7 +893,6 @@ static void at91ether_rx(struct net_device *dev)
memcpy(skb_put(skb, pktlen), p_recv, pktlen);
skb->protocol = eth_type_trans(skb, dev);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pktlen;
netif_rx(skb);
}
@@ -978,7 +976,6 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add
struct at91_private *lp;
unsigned int val;
int res;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(sizeof(struct at91_private));
if (!dev)
@@ -1084,11 +1081,11 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add
gpio_request(lp->board_data.phy_irq_pin, "ethernet_phy");
/* Display ethernet banner */
- printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%s)\n",
+ printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%pM)\n",
dev->name, (uint) dev->base_addr, dev->irq,
at91_emac_read(AT91_EMAC_CFG) & AT91_EMAC_SPD ? "100-" : "10-",
at91_emac_read(AT91_EMAC_CFG) & AT91_EMAC_FD ? "FullDuplex" : "HalfDuplex",
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
if ((phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID))
printk(KERN_INFO "%s: Davicom 9161 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)");
else if (phy_type == MII_LXT971A_ID)
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index 1267444d79da..3ec20cc18b0c 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -259,8 +259,6 @@ static int ep93xx_rx(struct net_device *dev, int processed, int budget)
skb_put(skb, length);
skb->protocol = eth_type_trans(skb, dev);
- dev->last_rx = jiffies;
-
netif_receive_skb(skb);
ep->stats.rx_packets++;
@@ -300,7 +298,7 @@ poll_some_more:
int more = 0;
spin_lock_irq(&ep->rx_lock);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);
if (ep93xx_have_more_rx(ep)) {
wrl(ep, REG_INTEN, REG_INTEN_TX);
@@ -309,7 +307,7 @@ poll_some_more:
}
spin_unlock_irq(&ep->rx_lock);
- if (more && netif_rx_reschedule(dev, napi))
+ if (more && netif_rx_reschedule(napi))
goto poll_some_more;
}
@@ -417,9 +415,9 @@ static irqreturn_t ep93xx_irq(int irq, void *dev_id)
if (status & REG_INTSTS_RX) {
spin_lock(&ep->rx_lock);
- if (likely(netif_rx_schedule_prep(dev, &ep->napi))) {
+ if (likely(netif_rx_schedule_prep(&ep->napi))) {
wrl(ep, REG_INTEN, REG_INTEN_TX);
- __netif_rx_schedule(dev, &ep->napi);
+ __netif_rx_schedule(&ep->napi);
}
spin_unlock(&ep->rx_lock);
}
diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c
index 3bb9e293e2ef..e380de454463 100644
--- a/drivers/net/arm/ether1.c
+++ b/drivers/net/arm/ether1.c
@@ -996,7 +996,6 @@ ether1_probe(struct expansion_card *ec, const struct ecard_id *id)
{
struct net_device *dev;
int i, ret = 0;
- DECLARE_MAC_BUF(mac);
ether1_banner();
@@ -1044,8 +1043,8 @@ ether1_probe(struct expansion_card *ec, const struct ecard_id *id)
if (ret)
goto free;
- printk(KERN_INFO "%s: ether1 in slot %d, %s\n",
- dev->name, ec->slot_no, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: ether1 in slot %d, %pM\n",
+ dev->name, ec->slot_no, dev->dev_addr);
ecard_set_drvdata(ec, dev);
return 0;
diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c
index 67e96ae85035..21a7bef12d3b 100644
--- a/drivers/net/arm/ether3.c
+++ b/drivers/net/arm/ether3.c
@@ -776,7 +776,6 @@ ether3_probe(struct expansion_card *ec, const struct ecard_id *id)
const struct ether3_data *data = id->data;
struct net_device *dev;
int bus_type, ret;
- DECLARE_MAC_BUF(mac);
ether3_banner();
@@ -859,8 +858,8 @@ ether3_probe(struct expansion_card *ec, const struct ecard_id *id)
if (ret)
goto free;
- printk("%s: %s in slot %d, %s\n",
- dev->name, data->name, ec->slot_no, print_mac(mac, dev->dev_addr));
+ printk("%s: %s in slot %d, %pM\n",
+ dev->name, data->name, ec->slot_no, dev->dev_addr);
ecard_set_drvdata(ec, dev);
return 0;
diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c
index 5c5f1e470d3c..6278606d1049 100644
--- a/drivers/net/arm/etherh.c
+++ b/drivers/net/arm/etherh.c
@@ -637,6 +637,21 @@ static const struct ethtool_ops etherh_ethtool_ops = {
.get_drvinfo = etherh_get_drvinfo,
};
+static const struct net_device_ops etherh_netdev_ops = {
+ .ndo_open = etherh_open,
+ .ndo_stop = etherh_close,
+ .ndo_set_config = etherh_set_config,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static u32 etherh_regoffsets[16];
static u32 etherm_regoffsets[16];
@@ -648,7 +663,6 @@ etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
struct net_device *dev;
struct etherh_priv *eh;
int ret;
- DECLARE_MAC_BUF(mac);
etherh_banner();
@@ -664,9 +678,7 @@ etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
SET_NETDEV_DEV(dev, &ec->dev);
- dev->open = etherh_open;
- dev->stop = etherh_close;
- dev->set_config = etherh_set_config;
+ dev->netdev_ops = &etherh_netdev_ops;
dev->irq = ec->irq;
dev->ethtool_ops = &etherh_ethtool_ops;
@@ -746,8 +758,8 @@ etherh_probe(struct expansion_card *ec, const struct ecard_id *id)
if (ret)
goto free;
- printk(KERN_INFO "%s: %s in slot %d, %s\n",
- dev->name, data->name, ec->slot_no, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: %s in slot %d, %pM\n",
+ dev->name, data->name, ec->slot_no, dev->dev_addr);
ecard_set_drvdata(ec, dev);
diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c
index e2d702b8b2e4..5fce1d5c1a1a 100644
--- a/drivers/net/arm/ixp4xx_eth.c
+++ b/drivers/net/arm/ixp4xx_eth.c
@@ -30,12 +30,11 @@
#include <linux/etherdevice.h>
#include <linux/io.h>
#include <linux/kernel.h>
-#include <linux/mii.h>
+#include <linux/phy.h>
#include <linux/platform_device.h>
#include <mach/npe.h>
#include <mach/qmgr.h>
-#define DEBUG_QUEUES 0
#define DEBUG_DESC 0
#define DEBUG_RX 0
#define DEBUG_TX 0
@@ -59,7 +58,6 @@
#define NAPI_WEIGHT 16
#define MDIO_INTERVAL (3 * HZ)
#define MAX_MDIO_RETRIES 100 /* microseconds, typically 30 cycles */
-#define MAX_MII_RESET_RETRIES 100 /* mdio_read() cycles, typically 4 */
#define MAX_CLOSE_WAIT 1000 /* microseconds, typically 2-3 cycles */
#define NPE_ID(port_id) ((port_id) >> 4)
@@ -164,15 +162,14 @@ struct port {
struct npe *npe;
struct net_device *netdev;
struct napi_struct napi;
- struct net_device_stats stat;
- struct mii_if_info mii;
- struct delayed_work mdio_thread;
+ struct phy_device *phydev;
struct eth_plat_info *plat;
buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS];
struct desc *desc_tab; /* coherent */
u32 desc_tab_phys;
int id; /* logical port ID */
- u16 mii_bmcr;
+ int speed, duplex;
+ u8 firmware[4];
};
/* NPE message structure */
@@ -243,19 +240,20 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
static spinlock_t mdio_lock;
static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
+struct mii_bus *mdio_bus;
static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
-static u16 mdio_cmd(struct net_device *dev, int phy_id, int location,
- int write, u16 cmd)
+static int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location,
+ int write, u16 cmd)
{
int cycles = 0;
if (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80) {
- printk(KERN_ERR "%s: MII not ready to transmit\n", dev->name);
- return 0;
+ printk(KERN_ERR "%s: MII not ready to transmit\n", bus->name);
+ return -1;
}
if (write) {
@@ -274,107 +272,119 @@ static u16 mdio_cmd(struct net_device *dev, int phy_id, int location,
}
if (cycles == MAX_MDIO_RETRIES) {
- printk(KERN_ERR "%s: MII write failed\n", dev->name);
- return 0;
+ printk(KERN_ERR "%s #%i: MII write failed\n", bus->name,
+ phy_id);
+ return -1;
}
#if DEBUG_MDIO
- printk(KERN_DEBUG "%s: mdio_cmd() took %i cycles\n", dev->name,
- cycles);
+ printk(KERN_DEBUG "%s #%i: mdio_%s() took %i cycles\n", bus->name,
+ phy_id, write ? "write" : "read", cycles);
#endif
if (write)
return 0;
if (__raw_readl(&mdio_regs->mdio_status[3]) & 0x80) {
- printk(KERN_ERR "%s: MII read failed\n", dev->name);
- return 0;
+#if DEBUG_MDIO
+ printk(KERN_DEBUG "%s #%i: MII read failed\n", bus->name,
+ phy_id);
+#endif
+ return 0xFFFF; /* don't return error */
}
return (__raw_readl(&mdio_regs->mdio_status[0]) & 0xFF) |
- (__raw_readl(&mdio_regs->mdio_status[1]) << 8);
+ ((__raw_readl(&mdio_regs->mdio_status[1]) & 0xFF) << 8);
}
-static int mdio_read(struct net_device *dev, int phy_id, int location)
+static int ixp4xx_mdio_read(struct mii_bus *bus, int phy_id, int location)
{
unsigned long flags;
- u16 val;
+ int ret;
spin_lock_irqsave(&mdio_lock, flags);
- val = mdio_cmd(dev, phy_id, location, 0, 0);
+ ret = ixp4xx_mdio_cmd(bus, phy_id, location, 0, 0);
spin_unlock_irqrestore(&mdio_lock, flags);
- return val;
+#if DEBUG_MDIO
+ printk(KERN_DEBUG "%s #%i: MII read [%i] -> 0x%X\n", bus->name,
+ phy_id, location, ret);
+#endif
+ return ret;
}
-static void mdio_write(struct net_device *dev, int phy_id, int location,
- int val)
+static int ixp4xx_mdio_write(struct mii_bus *bus, int phy_id, int location,
+ u16 val)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&mdio_lock, flags);
- mdio_cmd(dev, phy_id, location, 1, val);
+ ret = ixp4xx_mdio_cmd(bus, phy_id, location, 1, val);
spin_unlock_irqrestore(&mdio_lock, flags);
+#if DEBUG_MDIO
+ printk(KERN_DEBUG "%s #%i: MII read [%i] <- 0x%X, err = %i\n",
+ bus->name, phy_id, location, val, ret);
+#endif
+ return ret;
}
-static void phy_reset(struct net_device *dev, int phy_id)
+static int ixp4xx_mdio_register(void)
{
- struct port *port = netdev_priv(dev);
- int cycles = 0;
+ int err;
- mdio_write(dev, phy_id, MII_BMCR, port->mii_bmcr | BMCR_RESET);
+ if (!(mdio_bus = mdiobus_alloc()))
+ return -ENOMEM;
- while (cycles < MAX_MII_RESET_RETRIES) {
- if (!(mdio_read(dev, phy_id, MII_BMCR) & BMCR_RESET)) {
-#if DEBUG_MDIO
- printk(KERN_DEBUG "%s: phy_reset() took %i cycles\n",
- dev->name, cycles);
-#endif
- return;
- }
- udelay(1);
- cycles++;
- }
+ /* All MII PHY accesses use NPE-B Ethernet registers */
+ spin_lock_init(&mdio_lock);
+ mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT;
+ __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control);
+
+ mdio_bus->name = "IXP4xx MII Bus";
+ mdio_bus->read = &ixp4xx_mdio_read;
+ mdio_bus->write = &ixp4xx_mdio_write;
+ strcpy(mdio_bus->id, "0");
- printk(KERN_ERR "%s: MII reset failed\n", dev->name);
+ if ((err = mdiobus_register(mdio_bus)))
+ mdiobus_free(mdio_bus);
+ return err;
}
-static void eth_set_duplex(struct port *port)
+static void ixp4xx_mdio_remove(void)
{
- if (port->mii.full_duplex)
- __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX,
- &port->regs->tx_control[0]);
- else
- __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX,
- &port->regs->tx_control[0]);
+ mdiobus_unregister(mdio_bus);
+ mdiobus_free(mdio_bus);
}
-static void phy_check_media(struct port *port, int init)
+static void ixp4xx_adjust_link(struct net_device *dev)
{
- if (mii_check_media(&port->mii, 1, init))
- eth_set_duplex(port);
- if (port->mii.force_media) { /* mii_check_media() doesn't work */
- struct net_device *dev = port->netdev;
- int cur_link = mii_link_ok(&port->mii);
- int prev_link = netif_carrier_ok(dev);
-
- if (!prev_link && cur_link) {
- printk(KERN_INFO "%s: link up\n", dev->name);
- netif_carrier_on(dev);
- } else if (prev_link && !cur_link) {
+ struct port *port = netdev_priv(dev);
+ struct phy_device *phydev = port->phydev;
+
+ if (!phydev->link) {
+ if (port->speed) {
+ port->speed = 0;
printk(KERN_INFO "%s: link down\n", dev->name);
- netif_carrier_off(dev);
}
+ return;
}
-}
+ if (port->speed == phydev->speed && port->duplex == phydev->duplex)
+ return;
-static void mdio_thread(struct work_struct *work)
-{
- struct port *port = container_of(work, struct port, mdio_thread.work);
+ port->speed = phydev->speed;
+ port->duplex = phydev->duplex;
+
+ if (port->duplex)
+ __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX,
+ &port->regs->tx_control[0]);
+ else
+ __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX,
+ &port->regs->tx_control[0]);
- phy_check_media(port, 0);
- schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL);
+ printk(KERN_INFO "%s: link up, speed %u Mb/s, %s duplex\n",
+ dev->name, port->speed, port->duplex ? "full" : "half");
}
@@ -412,47 +422,13 @@ static inline void debug_desc(u32 phys, struct desc *desc)
#endif
}
-static inline void debug_queue(unsigned int queue, int is_get, u32 phys)
-{
-#if DEBUG_QUEUES
- static struct {
- int queue;
- char *name;
- } names[] = {
- { TX_QUEUE(0x10), "TX#0 " },
- { TX_QUEUE(0x20), "TX#1 " },
- { TX_QUEUE(0x00), "TX#2 " },
- { RXFREE_QUEUE(0x10), "RX-free#0 " },
- { RXFREE_QUEUE(0x20), "RX-free#1 " },
- { RXFREE_QUEUE(0x00), "RX-free#2 " },
- { TXDONE_QUEUE, "TX-done " },
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(names); i++)
- if (names[i].queue == queue)
- break;
-
- printk(KERN_DEBUG "Queue %i %s%s %X\n", queue,
- i < ARRAY_SIZE(names) ? names[i].name : "",
- is_get ? "->" : "<-", phys);
-#endif
-}
-
-static inline u32 queue_get_entry(unsigned int queue)
-{
- u32 phys = qmgr_get_entry(queue);
- debug_queue(queue, 1, phys);
- return phys;
-}
-
static inline int queue_get_desc(unsigned int queue, struct port *port,
int is_tx)
{
u32 phys, tab_phys, n_desc;
struct desc *tab;
- if (!(phys = queue_get_entry(queue)))
+ if (!(phys = qmgr_get_entry(queue)))
return -1;
phys &= ~0x1F; /* mask out non-address bits */
@@ -468,7 +444,6 @@ static inline int queue_get_desc(unsigned int queue, struct port *port,
static inline void queue_put_desc(unsigned int queue, u32 phys,
struct desc *desc)
{
- debug_queue(queue, 0, phys);
debug_desc(phys, desc);
BUG_ON(phys & 0x1F);
qmgr_put_entry(queue, phys);
@@ -498,7 +473,7 @@ static void eth_rx_irq(void *pdev)
printk(KERN_DEBUG "%s: eth_rx_irq\n", dev->name);
#endif
qmgr_disable_irq(port->plat->rxq);
- netif_rx_schedule(dev, &port->napi);
+ netif_rx_schedule(&port->napi);
}
static int eth_poll(struct napi_struct *napi, int budget)
@@ -526,10 +501,10 @@ static int eth_poll(struct napi_struct *napi, int budget)
printk(KERN_DEBUG "%s: eth_poll netif_rx_complete\n",
dev->name);
#endif
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
qmgr_enable_irq(rxq);
if (!qmgr_stat_empty(rxq) &&
- netif_rx_reschedule(dev, napi)) {
+ netif_rx_reschedule(napi)) {
#if DEBUG_RX
printk(KERN_DEBUG "%s: eth_poll"
" netif_rx_reschedule successed\n",
@@ -562,7 +537,7 @@ static int eth_poll(struct napi_struct *napi, int budget)
#endif
if (!skb) {
- port->stat.rx_dropped++;
+ dev->stats.rx_dropped++;
/* put the desc back on RX-ready queue */
desc->buf_len = MAX_MRU;
desc->pkt_len = 0;
@@ -588,9 +563,8 @@ static int eth_poll(struct napi_struct *napi, int budget)
debug_pkt(dev, "eth_poll", skb->data, skb->len);
skb->protocol = eth_type_trans(skb, dev);
- dev->last_rx = jiffies;
- port->stat.rx_packets++;
- port->stat.rx_bytes += skb->len;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
netif_receive_skb(skb);
/* put the new buffer on RX-free queue */
@@ -618,7 +592,7 @@ static void eth_txdone_irq(void *unused)
#if DEBUG_TX
printk(KERN_DEBUG DRV_NAME ": eth_txdone_irq\n");
#endif
- while ((phys = queue_get_entry(TXDONE_QUEUE)) != 0) {
+ while ((phys = qmgr_get_entry(TXDONE_QUEUE)) != 0) {
u32 npe_id, n_desc;
struct port *port;
struct desc *desc;
@@ -635,8 +609,8 @@ static void eth_txdone_irq(void *unused)
debug_desc(phys, desc);
if (port->tx_buff_tab[n_desc]) { /* not the draining packet */
- port->stat.tx_packets++;
- port->stat.tx_bytes += desc->pkt_len;
+ port->netdev->stats.tx_packets++;
+ port->netdev->stats.tx_bytes += desc->pkt_len;
dma_unmap_tx(port, desc);
#if DEBUG_TX
@@ -674,7 +648,7 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb->len > MAX_MRU)) {
dev_kfree_skb(skb);
- port->stat.tx_errors++;
+ dev->stats.tx_errors++;
return NETDEV_TX_OK;
}
@@ -690,7 +664,7 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
bytes = ALIGN(offset + len, 4);
if (!(mem = kmalloc(bytes, GFP_ATOMIC))) {
dev_kfree_skb(skb);
- port->stat.tx_dropped++;
+ dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4);
@@ -704,7 +678,7 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
#else
kfree(mem);
#endif
- port->stat.tx_dropped++;
+ dev->stats.tx_dropped++;
return NETDEV_TX_OK;
}
@@ -747,12 +721,6 @@ static int eth_xmit(struct sk_buff *skb, struct net_device *dev)
}
-static struct net_device_stats *eth_stats(struct net_device *dev)
-{
- struct port *port = netdev_priv(dev);
- return &port->stat;
-}
-
static void eth_set_mcast_list(struct net_device *dev)
{
struct port *port = netdev_priv(dev);
@@ -786,41 +754,80 @@ static void eth_set_mcast_list(struct net_device *dev)
static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
struct port *port = netdev_priv(dev);
- unsigned int duplex_chg;
- int err;
if (!netif_running(dev))
return -EINVAL;
- err = generic_mii_ioctl(&port->mii, if_mii(req), cmd, &duplex_chg);
- if (duplex_chg)
- eth_set_duplex(port);
- return err;
+ return phy_mii_ioctl(port->phydev, if_mii(req), cmd);
+}
+
+/* ethtool support */
+
+static void ixp4xx_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct port *port = netdev_priv(dev);
+ strcpy(info->driver, DRV_NAME);
+ snprintf(info->fw_version, sizeof(info->fw_version), "%u:%u:%u:%u",
+ port->firmware[0], port->firmware[1],
+ port->firmware[2], port->firmware[3]);
+ strcpy(info->bus_info, "internal");
}
+static int ixp4xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct port *port = netdev_priv(dev);
+ return phy_ethtool_gset(port->phydev, cmd);
+}
+
+static int ixp4xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct port *port = netdev_priv(dev);
+ return phy_ethtool_sset(port->phydev, cmd);
+}
+
+static int ixp4xx_nway_reset(struct net_device *dev)
+{
+ struct port *port = netdev_priv(dev);
+ return phy_start_aneg(port->phydev);
+}
+
+static struct ethtool_ops ixp4xx_ethtool_ops = {
+ .get_drvinfo = ixp4xx_get_drvinfo,
+ .get_settings = ixp4xx_get_settings,
+ .set_settings = ixp4xx_set_settings,
+ .nway_reset = ixp4xx_nway_reset,
+ .get_link = ethtool_op_get_link,
+};
+
static int request_queues(struct port *port)
{
int err;
- err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0);
+ err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0,
+ "%s:RX-free", port->netdev->name);
if (err)
return err;
- err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0);
+ err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0,
+ "%s:RX", port->netdev->name);
if (err)
goto rel_rxfree;
- err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0);
+ err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0,
+ "%s:TX", port->netdev->name);
if (err)
goto rel_rx;
- err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0);
+ err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0,
+ "%s:TX-ready", port->netdev->name);
if (err)
goto rel_tx;
/* TX-done queue handles skbs sent out by the NPEs */
if (!ports_open) {
- err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0);
+ err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0,
+ "%s:TX-done", DRV_NAME);
if (err)
goto rel_txready;
}
@@ -944,10 +951,12 @@ static int eth_open(struct net_device *dev)
npe_name(npe));
return -EIO;
}
+ port->firmware[0] = msg.byte4;
+ port->firmware[1] = msg.byte5;
+ port->firmware[2] = msg.byte6;
+ port->firmware[3] = msg.byte7;
}
- mdio_write(dev, port->plat->phy, MII_BMCR, port->mii_bmcr);
-
memset(&msg, 0, sizeof(msg));
msg.cmd = NPE_VLAN_SETRXQOSENTRY;
msg.eth_id = port->id;
@@ -985,6 +994,9 @@ static int eth_open(struct net_device *dev)
return err;
}
+ port->speed = 0; /* force "link up" message */
+ phy_start(port->phydev);
+
for (i = 0; i < ETH_ALEN; i++)
__raw_writel(dev->dev_addr[i], &port->regs->hw_addr[i]);
__raw_writel(0x08, &port->regs->random_seed);
@@ -1012,10 +1024,8 @@ static int eth_open(struct net_device *dev)
__raw_writel(DEFAULT_RX_CNTRL0, &port->regs->rx_control[0]);
napi_enable(&port->napi);
- phy_check_media(port, 1);
eth_set_mcast_list(dev);
netif_start_queue(dev);
- schedule_delayed_work(&port->mdio_thread, MDIO_INTERVAL);
qmgr_set_irq(port->plat->rxq, QUEUE_IRQ_SRC_NOT_EMPTY,
eth_rx_irq, dev);
@@ -1026,7 +1036,7 @@ static int eth_open(struct net_device *dev)
}
ports_open++;
/* we may already have RX data, enables IRQ */
- netif_rx_schedule(dev, &port->napi);
+ netif_rx_schedule(&port->napi);
return 0;
}
@@ -1106,25 +1116,31 @@ static int eth_close(struct net_device *dev)
printk(KERN_CRIT "%s: unable to disable loopback\n",
dev->name);
- port->mii_bmcr = mdio_read(dev, port->plat->phy, MII_BMCR) &
- ~(BMCR_RESET | BMCR_PDOWN); /* may have been altered */
- mdio_write(dev, port->plat->phy, MII_BMCR,
- port->mii_bmcr | BMCR_PDOWN);
+ phy_stop(port->phydev);
if (!ports_open)
qmgr_disable_irq(TXDONE_QUEUE);
- cancel_rearming_delayed_work(&port->mdio_thread);
destroy_queues(port);
release_queues(port);
return 0;
}
+static const struct net_device_ops ixp4xx_netdev_ops = {
+ .ndo_open = eth_open,
+ .ndo_stop = eth_close,
+ .ndo_start_xmit = eth_xmit,
+ .ndo_set_multicast_list = eth_set_mcast_list,
+ .ndo_do_ioctl = eth_ioctl,
+
+};
+
static int __devinit eth_init_one(struct platform_device *pdev)
{
struct port *port;
struct net_device *dev;
struct eth_plat_info *plat = pdev->dev.platform_data;
u32 regs_phys;
+ char phy_id[BUS_ID_SIZE];
int err;
if (!(dev = alloc_etherdev(sizeof(struct port))))
@@ -1153,12 +1169,8 @@ static int __devinit eth_init_one(struct platform_device *pdev)
goto err_free;
}
- dev->open = eth_open;
- dev->hard_start_xmit = eth_xmit;
- dev->stop = eth_close;
- dev->get_stats = eth_stats;
- dev->do_ioctl = eth_ioctl;
- dev->set_multicast_list = eth_set_mcast_list;
+ dev->netdev_ops = &ixp4xx_netdev_ops;
+ dev->ethtool_ops = &ixp4xx_ethtool_ops;
dev->tx_queue_len = 100;
netif_napi_add(dev, &port->napi, eth_poll, NAPI_WEIGHT);
@@ -1191,22 +1203,19 @@ static int __devinit eth_init_one(struct platform_device *pdev)
__raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control);
udelay(50);
- port->mii.dev = dev;
- port->mii.mdio_read = mdio_read;
- port->mii.mdio_write = mdio_write;
- port->mii.phy_id = plat->phy;
- port->mii.phy_id_mask = 0x1F;
- port->mii.reg_num_mask = 0x1F;
+ snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, "0", plat->phy);
+ port->phydev = phy_connect(dev, phy_id, &ixp4xx_adjust_link, 0,
+ PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(port->phydev)) {
+ printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(port->phydev);
+ }
+
+ port->phydev->irq = PHY_POLL;
printk(KERN_INFO "%s: MII PHY %i on %s\n", dev->name, plat->phy,
npe_name(port->npe));
- phy_reset(dev, plat->phy);
- port->mii_bmcr = mdio_read(dev, plat->phy, MII_BMCR) &
- ~(BMCR_RESET | BMCR_PDOWN);
- mdio_write(dev, plat->phy, MII_BMCR, port->mii_bmcr | BMCR_PDOWN);
-
- INIT_DELAYED_WORK(&port->mdio_thread, mdio_thread);
return 0;
err_unreg:
@@ -1232,7 +1241,7 @@ static int __devexit eth_remove_one(struct platform_device *pdev)
return 0;
}
-static struct platform_driver drv = {
+static struct platform_driver ixp4xx_eth_driver = {
.driver.name = DRV_NAME,
.probe = eth_init_one,
.remove = eth_remove_one,
@@ -1240,20 +1249,19 @@ static struct platform_driver drv = {
static int __init eth_init_module(void)
{
+ int err;
if (!(ixp4xx_read_feature_bits() & IXP4XX_FEATURE_NPEB_ETH0))
return -ENOSYS;
- /* All MII PHY accesses use NPE-B Ethernet registers */
- spin_lock_init(&mdio_lock);
- mdio_regs = (struct eth_regs __iomem *)IXP4XX_EthB_BASE_VIRT;
- __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control);
-
- return platform_driver_register(&drv);
+ if ((err = ixp4xx_mdio_register()))
+ return err;
+ return platform_driver_register(&ixp4xx_eth_driver);
}
static void __exit eth_cleanup_module(void)
{
- platform_driver_unregister(&drv);
+ platform_driver_unregister(&ixp4xx_eth_driver);
+ ixp4xx_mdio_remove();
}
MODULE_AUTHOR("Krzysztof Halasa");
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c
new file mode 100644
index 000000000000..9ad22d1b00fd
--- /dev/null
+++ b/drivers/net/arm/ks8695net.c
@@ -0,0 +1,1675 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME "ks8695_ether"
+#define MODULEVERSION "1.01"
+
+/*
+ * Transmit and device reset timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ * struct rx_ring_desc - Receive descriptor ring element
+ * @status: The status of the descriptor element (E.g. who owns it)
+ * @length: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+ __le32 status;
+ __le32 length;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct tx_ring_desc - Transmit descriptor ring element
+ * @owner: Who owns the descriptor
+ * @status: The number of bytes in the block pointed to by data_ptr
+ * @data_ptr: The physical address of the data block to receive into
+ * @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+ __le32 owner;
+ __le32 status;
+ __le32 data_ptr;
+ __le32 next_desc;
+};
+
+/**
+ * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ * @skb: The buffer in the ring
+ * @dma_ptr: The mapped DMA pointer of the buffer
+ * @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+ struct sk_buff *skb;
+ dma_addr_t dma_ptr;
+ u32 length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ * enum ks8695_dtype - Device type
+ * @KS8695_DTYPE_WAN: This device is a WAN interface
+ * @KS8695_DTYPE_LAN: This device is a LAN interface
+ * @KS8695_DTYPE_HPNA: This device is an HPNA interface
+ */
+enum ks8695_dtype {
+ KS8695_DTYPE_WAN,
+ KS8695_DTYPE_LAN,
+ KS8695_DTYPE_HPNA,
+};
+
+/**
+ * struct ks8695_priv - Private data for the KS8695 Ethernet
+ * @in_suspend: Flag to indicate if we're suspending/resuming
+ * @ndev: The net_device for this interface
+ * @dev: The platform device object for this interface
+ * @dtype: The type of this device
+ * @io_regs: The ioremapped registers for this interface
+ * @rx_irq_name: The textual name of the RX IRQ from the platform data
+ * @tx_irq_name: The textual name of the TX IRQ from the platform data
+ * @link_irq_name: The textual name of the link IRQ from the
+ * platform data if available
+ * @rx_irq: The IRQ number for the RX IRQ
+ * @tx_irq: The IRQ number for the TX IRQ
+ * @link_irq: The IRQ number for the link IRQ if available
+ * @regs_req: The resource request for the registers region
+ * @phyiface_req: The resource request for the phy/switch region
+ * if available
+ * @phyiface_regs: The ioremapped registers for the phy/switch if available
+ * @ring_base: The base pointer of the dma coherent memory for the rings
+ * @ring_base_dma: The DMA mapped equivalent of ring_base
+ * @tx_ring: The pointer in ring_base of the TX ring
+ * @tx_ring_used: The number of slots in the TX ring which are occupied
+ * @tx_ring_next_slot: The next slot to fill in the TX ring
+ * @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ * @tx_buffers: The sk_buff mappings for the TX ring
+ * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ * @rx_ring: The pointer in ring_base of the RX ring
+ * @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ * @rx_buffers: The sk_buff mappings for the RX ring
+ * @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+ int in_suspend;
+ struct net_device *ndev;
+ struct device *dev;
+ enum ks8695_dtype dtype;
+ void __iomem *io_regs;
+
+ const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+ int rx_irq, tx_irq, link_irq;
+
+ struct resource *regs_req, *phyiface_req;
+ void __iomem *phyiface_regs;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct tx_ring_desc *tx_ring;
+ int tx_ring_used;
+ int tx_ring_next_slot;
+ dma_addr_t tx_ring_dma;
+ struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+ spinlock_t txq_lock;
+
+ struct rx_ring_desc *rx_ring;
+ dma_addr_t rx_ring_dma;
+ struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+ int next_rx_desc_read;
+
+ int msg_enable;
+};
+
+/* Register access */
+
+/**
+ * ks8695_readreg - Read from a KS8695 ethernet register
+ * @ksp: The device to read from
+ * @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+ return readl(ksp->io_regs + reg);
+}
+
+/**
+ * ks8695_writereg - Write to a KS8695 ethernet register
+ * @ksp: The device to write to
+ * @reg: The register to write
+ * @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+ writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ * ks8695_port_type - Retrieve port-type as user-friendly string
+ * @ksp: The device to return the type for
+ *
+ * Returns a string indicating which of the WAN, LAN or HPNA
+ * ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_LAN:
+ return "LAN";
+ case KS8695_DTYPE_WAN:
+ return "WAN";
+ case KS8695_DTYPE_HPNA:
+ return "HPNA";
+ }
+
+ return "UNKNOWN";
+}
+
+/**
+ * ks8695_update_mac - Update the MAC registers in the device
+ * @ksp: The device to update
+ *
+ * Updates the MAC registers in the KS8695 device from the address in the
+ * net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+ /* Update the HW with the MAC from the net_device */
+ struct net_device *ndev = ksp->ndev;
+ u32 machigh, maclow;
+
+ maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0));
+ machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0));
+
+ ks8695_writereg(ksp, KS8695_MAL, maclow);
+ ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ * ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ * @ksp: The device to refill
+ *
+ * Iterates the RX ring of the device looking for empty slots.
+ * For each empty slot, we allocate and map a new SKB and give it
+ * to the hardware.
+ * This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+ /* Run around the RX ring, filling in any missing sk_buff's */
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (!ksp->rx_buffers[buff_n].skb) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+ dma_addr_t mapping;
+
+ ksp->rx_buffers[buff_n].skb = skb;
+ if (skb == NULL) {
+ /* Failed to allocate one, perhaps
+ * we'll try again later.
+ */
+ break;
+ }
+
+ mapping = dma_map_single(ksp->dev, skb->data,
+ MAX_RXBUF_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
+ /* Failed to DMA map this SKB, try later */
+ dev_kfree_skb_irq(skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ break;
+ }
+ ksp->rx_buffers[buff_n].dma_ptr = mapping;
+ skb->dev = ksp->ndev;
+ ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+ /* Record this into the DMA ring */
+ ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+ ksp->rx_ring[buff_n].length =
+ cpu_to_le32(MAX_RXBUF_SIZE);
+
+ wmb();
+
+ /* And give ownership over to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+ }
+ }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES 16
+
+/**
+ * ks8695_init_partial_multicast - Init the mcast addr registers
+ * @ksp: The device to initialise
+ * @addr: The multicast address list to use
+ * @nr_addr: The number of addresses in the list
+ *
+ * This routine is a helper for ks8695_set_multicast - it writes
+ * the additional-address registers in the KS8695 ethernet device
+ * and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+ struct dev_mc_list *addr,
+ int nr_addr)
+{
+ u32 low, high;
+ int i;
+
+ for (i = 0; i < nr_addr; i++, addr = addr->next) {
+ /* Ran out of addresses? */
+ if (!addr)
+ break;
+ /* Ran out of space in chip? */
+ BUG_ON(i == KS8695_NR_ADDRESSES);
+
+ low = (addr->dmi_addr[2] << 24) | (addr->dmi_addr[3] << 16) |
+ (addr->dmi_addr[4] << 8) | (addr->dmi_addr[5]);
+ high = (addr->dmi_addr[0] << 8) | (addr->dmi_addr[1]);
+
+ ks8695_writereg(ksp, KS8695_AAL_(i), low);
+ ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+ }
+
+ /* Clear the remaining Additional Station Addresses */
+ for (; i < KS8695_NR_ADDRESSES; i++) {
+ ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+ ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+ }
+}
+
+/* Interrupt handling */
+
+/**
+ * ks8695_tx_irq - Transmit IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the TX ring, clearing out any transmitted slots.
+ * Allows the net_device to pass us new packets once slots are
+ * freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb &&
+ !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+ rmb();
+ /* An SKB which is not owned by HW is present */
+ /* Update the stats for the net_device */
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+ /* Free the packet from the ring */
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Free the sk_buff */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ ksp->tx_ring_used--;
+ }
+ }
+
+ netif_wake_queue(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx_irq - Receive IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * Process the RX ring, passing any received packets up to the
+ * host. If we received anything other than errors, we then
+ * refill the ring.
+ */
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int buff_n;
+ u32 flags;
+ int pktlen;
+ int last_rx_processed = -1;
+
+ buff_n = ksp->next_rx_desc_read;
+ do {
+ if (ksp->rx_buffers[buff_n].skb &&
+ !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ rmb();
+ flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+ /* Found an SKB which we own, this means we
+ * received a packet
+ */
+ if ((flags & (RDES_FS | RDES_LS)) !=
+ (RDES_FS | RDES_LS)) {
+ /* This packet is not the first and
+ * the last segment. Therefore it is
+ * a "spanning" packet and we can't
+ * handle it
+ */
+ goto rx_failure;
+ }
+
+ if (flags & (RDES_ES | RDES_RE)) {
+ /* It's an error packet */
+ ndev->stats.rx_errors++;
+ if (flags & RDES_TL)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_RF)
+ ndev->stats.rx_length_errors++;
+ if (flags & RDES_CE)
+ ndev->stats.rx_crc_errors++;
+ if (flags & RDES_RE)
+ ndev->stats.rx_missed_errors++;
+
+ goto rx_failure;
+ }
+
+ pktlen = flags & RDES_FLEN;
+ pktlen -= 4; /* Drop the CRC */
+
+ /* Retrieve the sk_buff */
+ skb = ksp->rx_buffers[buff_n].skb;
+
+ /* Clear it from the ring */
+ ksp->rx_buffers[buff_n].skb = NULL;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+
+ /* Relinquish the SKB to the network layer */
+ skb_put(skb, pktlen);
+ skb->protocol = eth_type_trans(skb, ndev);
+ netif_rx(skb);
+
+ /* Record stats */
+ ndev->last_rx = jiffies;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += pktlen;
+ goto rx_finished;
+
+rx_failure:
+ /* This ring entry is an error, but we can
+ * re-use the skb
+ */
+ /* Give the ring entry back to the hardware */
+ ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+ /* And note this as processed so we can start
+ * from here next time
+ */
+ last_rx_processed = buff_n;
+ } else {
+ /* Ran out of things to process, stop now */
+ break;
+ }
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ } while (buff_n != ksp->next_rx_desc_read);
+
+ /* And note which RX descriptor we last did anything with */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) & MAX_RX_DESC_MASK;
+
+ /* And refill the buffers */
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Kick the RX DMA engine, in case it became suspended */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_link_irq - Link change IRQ handler
+ * @irq: The IRQ which went off (ignored)
+ * @dev_id: The net_device for the interrupt
+ *
+ * The WAN interface can generate an IRQ when the link changes,
+ * report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if (ctrl & WMC_WLS) {
+ netif_carrier_on(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev,
+ "%s: Link is now up (10%sMbps/%s-duplex)\n",
+ ndev->name,
+ (ctrl & WMC_WSS) ? "0" : "",
+ (ctrl & WMC_WDS) ? "Full" : "Half");
+ } else {
+ netif_carrier_off(ndev);
+ if (netif_msg_link(ksp))
+ dev_info(ksp->dev, "%s: Link is now down.\n",
+ ndev->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ * ks8695_reset - Reset a KS8695 ethernet interface
+ * @ksp: The interface to reset
+ *
+ * Perform an engine reset of the interface and re-program it
+ * with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+ int reset_timeout = watchdog;
+ /* Issue the reset via the TX DMA control register */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+ while (reset_timeout--) {
+ if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+ break;
+ msleep(1);
+ }
+
+ if (reset_timeout == 0) {
+ dev_crit(ksp->dev,
+ "Timeout waiting for DMA engines to reset\n");
+ /* And blithely carry on */
+ }
+
+ /* Definitely wait long enough before attempting to program
+ * the engines
+ */
+ msleep(10);
+
+ /* RX: unicast and broadcast */
+ ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+ /* TX: pad and add CRC */
+ ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ * ks8695_shutdown - Shut down a KS8695 ethernet interface
+ * @ksp: The interface to shut down
+ *
+ * This disables packet RX/TX, cleans up IRQs, drains the rings,
+ * and basically places the interface into a clean shutdown
+ * state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+ int buff_n;
+
+ /* Disable packet transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+ /* Disable packet reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+ /* Release the IRQs */
+ free_irq(ksp->rx_irq, ksp->ndev);
+ free_irq(ksp->tx_irq, ksp->ndev);
+ if (ksp->link_irq != -1)
+ free_irq(ksp->link_irq, ksp->ndev);
+
+ /* Throw away any pending TX packets */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ if (ksp->tx_buffers[buff_n].skb) {
+ /* Remove this SKB from the TX ring */
+ ksp->tx_ring[buff_n].owner = 0;
+ ksp->tx_ring[buff_n].status = 0;
+ ksp->tx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin this SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->tx_buffers[buff_n].dma_ptr,
+ ksp->tx_buffers[buff_n].length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+ ksp->tx_buffers[buff_n].skb = NULL;
+ }
+ }
+
+ /* Purge the RX buffers */
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ if (ksp->rx_buffers[buff_n].skb) {
+ /* Remove the SKB from the RX ring */
+ ksp->rx_ring[buff_n].status = 0;
+ ksp->rx_ring[buff_n].data_ptr = 0;
+
+ /* Unmap and bin the SKB */
+ dma_unmap_single(ksp->dev,
+ ksp->rx_buffers[buff_n].dma_ptr,
+ ksp->rx_buffers[buff_n].length,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+ ksp->rx_buffers[buff_n].skb = NULL;
+ }
+ }
+}
+
+
+/**
+ * ks8695_setup_irq - IRQ setup helper function
+ * @irq: The IRQ number to claim
+ * @irq_name: The name to give the IRQ claimant
+ * @handler: The function to call to handle the IRQ
+ * @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ * Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+ irq_handler_t handler, struct net_device *ndev)
+{
+ int ret;
+
+ ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+ if (ret) {
+ dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_init_net - Initialise a KS8695 ethernet interface
+ * @ksp: The interface to initialise
+ *
+ * This routine fills the RX ring, initialises the DMA engines,
+ * allocates the IRQs and then starts the packet TX and RX
+ * engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+ int ret;
+ u32 ctrl;
+
+ ks8695_refill_rxbuffers(ksp);
+
+ /* Initialise the DMA engines */
+ ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+ ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+ /* Request the IRQs */
+ ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+ ks8695_rx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+ ks8695_tx_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ if (ksp->link_irq != -1) {
+ ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+ ks8695_link_irq, ksp->ndev);
+ if (ret)
+ return ret;
+ }
+
+ /* Set up the ring indices */
+ ksp->next_rx_desc_read = 0;
+ ksp->tx_ring_next_slot = 0;
+ ksp->tx_ring_used = 0;
+
+ /* Bring up transmission */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ /* Enable packet transmission */
+ ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+ /* Bring up the reception */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ /* Enable packet reception */
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+ /* And start the DMA engine */
+ ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+ /* All done */
+ return 0;
+}
+
+/**
+ * ks8695_release_device - HW resource release for KS8695 e-net
+ * @ksp: The device to be freed
+ *
+ * This unallocates io memory regions, dma-coherent regions etc
+ * which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+ /* Unmap the registers */
+ iounmap(ksp->io_regs);
+ if (ksp->phyiface_regs)
+ iounmap(ksp->phyiface_regs);
+
+ /* And release the request */
+ release_resource(ksp->regs_req);
+ kfree(ksp->regs_req);
+ if (ksp->phyiface_req) {
+ release_resource(ksp->phyiface_req);
+ kfree(ksp->phyiface_req);
+ }
+
+ /* Free the ring buffers */
+ dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+ ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ * ks8695_get_msglevel - Get the messages enabled for emission
+ * @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ return ksp->msg_enable;
+}
+
+/**
+ * ks8695_set_msglevel - Set the messages enabled for emission
+ * @ndev: The network device to configure
+ * @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->msg_enable = value;
+}
+
+/**
+ * ks8695_get_settings - Get device-specific settings.
+ * @ndev: The network device to read settings from
+ * @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ /* All ports on the KS8695 support these... */
+ cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+ SUPPORTED_TP | SUPPORTED_MII);
+ cmd->transceiver = XCVR_INTERNAL;
+
+ /* Port specific extras */
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ cmd->phy_address = 0;
+ /* not supported for HPNA */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ /* BUG: Erm, dtype hpna implies no phy regs */
+ /*
+ ctrl = readl(KS8695_MISC_VA + KS8695_HMC);
+ cmd->speed = (ctrl & HMC_HSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & HMC_HDS) ? DUPLEX_FULL : DUPLEX_HALF;
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+ cmd->port = PORT_MII;
+ cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+ cmd->phy_address = 0;
+
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ if ((ctrl & WMC_WAND) == 0) {
+ /* auto-negotiation is enabled */
+ cmd->advertising |= ADVERTISED_Autoneg;
+ if (ctrl & WMC_WANA100F)
+ cmd->advertising |= ADVERTISED_100baseT_Full;
+ if (ctrl & WMC_WANA100H)
+ cmd->advertising |= ADVERTISED_100baseT_Half;
+ if (ctrl & WMC_WANA10F)
+ cmd->advertising |= ADVERTISED_10baseT_Full;
+ if (ctrl & WMC_WANA10H)
+ cmd->advertising |= ADVERTISED_10baseT_Half;
+ if (ctrl & WMC_WANAP)
+ cmd->advertising |= ADVERTISED_Pause;
+ cmd->autoneg = AUTONEG_ENABLE;
+
+ cmd->speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WDS) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ } else {
+ /* auto-negotiation is disabled */
+ cmd->autoneg = AUTONEG_DISABLE;
+
+ cmd->speed = (ctrl & WMC_WANF100) ?
+ SPEED_100 : SPEED_10;
+ cmd->duplex = (ctrl & WMC_WANFF) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ }
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_set_settings - Set device-specific settings.
+ * @ndev: The network device to configure
+ * @cmd: The settings to configure
+ */
+static int
+ks8695_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+ return -EINVAL;
+ if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+ return -EINVAL;
+ if (cmd->port != PORT_MII)
+ return -EINVAL;
+ if (cmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+ if ((cmd->autoneg != AUTONEG_DISABLE) &&
+ (cmd->autoneg != AUTONEG_ENABLE))
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full)) == 0)
+ return -EINVAL;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA does not support auto-negotiation. */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+ if (cmd->advertising & ADVERTISED_100baseT_Full)
+ ctrl |= WMC_WANA100F;
+ if (cmd->advertising & ADVERTISED_100baseT_Half)
+ ctrl |= WMC_WANA100H;
+ if (cmd->advertising & ADVERTISED_10baseT_Full)
+ ctrl |= WMC_WANA10F;
+ if (cmd->advertising & ADVERTISED_10baseT_Half)
+ ctrl |= WMC_WANA10H;
+
+ /* force a re-negotiation */
+ ctrl |= WMC_WANR;
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ } else {
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* BUG: dtype_hpna implies no phy registers */
+ /*
+ ctrl = __raw_readl(KS8695_MISC_VA + KS8695_HMC);
+
+ ctrl &= ~(HMC_HSS | HMC_HDS);
+ if (cmd->speed == SPEED_100)
+ ctrl |= HMC_HSS;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= HMC_HDS;
+
+ __raw_writel(ctrl, KS8695_MISC_VA + KS8695_HMC);
+ */
+ return -EOPNOTSUPP;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* disable auto-negotiation */
+ ctrl |= WMC_WAND;
+ ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+ if (cmd->speed == SPEED_100)
+ ctrl |= WMC_WANF100;
+ if (cmd->duplex == DUPLEX_FULL)
+ ctrl |= WMC_WANFF;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_nwayreset - Restart the autonegotiation on the port.
+ * @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_nwayreset(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy means no autonegotiation on hpna */
+ return -EINVAL;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ if ((ctrl & WMC_WAND) == 0)
+ writel(ctrl | WMC_WANR,
+ ksp->phyiface_regs + KS8695_WMC);
+ else
+ /* auto-negotiation not enabled */
+ return -EINVAL;
+ break;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_get_link - Retrieve link status of network interface
+ * @ndev: The network interface to retrive the link status of.
+ */
+static u32
+ks8695_get_link(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* HPNA always has link */
+ return 1;
+ case KS8695_DTYPE_WAN:
+ /* WAN we can read the PHY for */
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+ return ctrl & WMC_WLS;
+ case KS8695_DTYPE_LAN:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/**
+ * ks8695_get_pause - Retrieve network pause/flow-control advertising
+ * @ndev: The device to retrieve settings from
+ * @param: The structure to fill out with the information
+ */
+static void
+ks8695_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ switch (ksp->dtype) {
+ case KS8695_DTYPE_HPNA:
+ /* No phy link on hpna to configure */
+ return;
+ case KS8695_DTYPE_WAN:
+ ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+ /* advertise Pause */
+ param->autoneg = (ctrl & WMC_WANAP);
+
+ /* current Rx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+ param->rx_pause = (ctrl & DRXC_RFCE);
+
+ /* current Tx Flow-control */
+ ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+ param->tx_pause = (ctrl & DTXC_TFCE);
+ break;
+ case KS8695_DTYPE_LAN:
+ /* The LAN's "phy" is a direct-attached switch */
+ return;
+ }
+}
+
+/**
+ * ks8695_set_pause - Configure pause/flow-control
+ * @ndev: The device to configure
+ * @param: The pause parameters to set
+ *
+ * TODO: Implement this
+ */
+static int
+ks8695_set_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+ return -EOPNOTSUPP;
+}
+
+/**
+ * ks8695_get_drvinfo - Retrieve driver information
+ * @ndev: The network device to retrieve info about
+ * @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+ strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+ strlcpy(info->bus_info, ndev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static struct ethtool_ops ks8695_ethtool_ops = {
+ .get_msglevel = ks8695_get_msglevel,
+ .set_msglevel = ks8695_set_msglevel,
+ .get_settings = ks8695_get_settings,
+ .set_settings = ks8695_set_settings,
+ .nway_reset = ks8695_nwayreset,
+ .get_link = ks8695_get_link,
+ .get_pauseparam = ks8695_get_pause,
+ .set_pauseparam = ks8695_set_pause,
+ .get_drvinfo = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ * ks8695_set_mac - Update MAC in net dev and HW
+ * @ndev: The network device to update
+ * @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+ ks8695_update_mac(ksp);
+
+ dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
+
+ return 0;
+}
+
+/**
+ * ks8695_set_multicast - Set up the multicast behaviour of the interface
+ * @ndev: The net_device to configure
+ *
+ * This routine, called by the net layer, configures promiscuity
+ * and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ u32 ctrl;
+
+ ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+ if (ndev->flags & IFF_PROMISC) {
+ /* enable promiscuous mode */
+ ctrl |= DRXC_RA;
+ } else if (ndev->flags & ~IFF_PROMISC) {
+ /* disable promiscuous mode */
+ ctrl &= ~DRXC_RA;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI) {
+ /* enable all multicast mode */
+ ctrl |= DRXC_RM;
+ } else if (ndev->mc_count > KS8695_NR_ADDRESSES) {
+ /* more specific multicast addresses than can be
+ * handled in hardware
+ */
+ ctrl |= DRXC_RM;
+ } else {
+ /* enable specific multicasts */
+ ctrl &= ~DRXC_RM;
+ ks8695_init_partial_multicast(ksp, ndev->mc_list,
+ ndev->mc_count);
+ }
+
+ ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ * ks8695_timeout - Handle a network tx/rx timeout.
+ * @ndev: The net_device which timed out.
+ *
+ * A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ ks8695_shutdown(ksp);
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ /* We ignore the return from this since it managed to init
+ * before it probably will be okay to init again.
+ */
+ ks8695_init_net(ksp);
+
+ /* Reconfigure promiscuity etc */
+ ks8695_set_multicast(ndev);
+
+ /* And start the TX queue once more */
+ netif_start_queue(ndev);
+}
+
+/**
+ * ks8695_start_xmit - Start a packet transmission
+ * @skb: The packet to transmit
+ * @ndev: The network device to send the packet on
+ *
+ * This routine, called by the net layer, takes ownership of the
+ * sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ * engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int buff_n;
+ dma_addr_t dmap;
+
+ spin_lock_irq(&ksp->txq_lock);
+
+ if (ksp->tx_ring_used == MAX_TX_DESC) {
+ /* Somehow we got entered when we have no room */
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_BUSY;
+ }
+
+ buff_n = ksp->tx_ring_next_slot;
+
+ BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+ dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(ksp->dev, dmap))) {
+ /* Failed to DMA map this SKB, give it back for now */
+ spin_unlock_irq(&ksp->txq_lock);
+ dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+ "transmission, trying later\n", ndev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ ksp->tx_buffers[buff_n].dma_ptr = dmap;
+ /* Mapped okay, store the buffer pointer and length for later */
+ ksp->tx_buffers[buff_n].skb = skb;
+ ksp->tx_buffers[buff_n].length = skb->len;
+
+ /* Fill out the TX descriptor */
+ ksp->tx_ring[buff_n].data_ptr =
+ cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+ ksp->tx_ring[buff_n].status =
+ cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+ (skb->len & TDES_TBS));
+
+ wmb();
+
+ /* Hand it over to the hardware */
+ ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+ if (++ksp->tx_ring_used == MAX_TX_DESC)
+ netif_stop_queue(ndev);
+
+ ndev->trans_start = jiffies;
+
+ /* Kick the TX DMA in case it decided to go IDLE */
+ ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+ /* And update the next ring slot */
+ ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+ spin_unlock_irq(&ksp->txq_lock);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ * @ndev: The net_device to stop
+ *
+ * This disables the TX queue and cleans up a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+
+ ks8695_shutdown(ksp);
+
+ return 0;
+}
+
+/**
+ * ks8695_open - Open (bring up) a KS8695 ethernet interface
+ * @ndev: The net_device to open
+ *
+ * This resets, configures the MAC, initialises the RX ring and
+ * DMA engines and starts the TX queue for a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+ int ret;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ ks8695_reset(ksp);
+
+ ks8695_update_mac(ksp);
+
+ ret = ks8695_init_net(ksp);
+ if (ret) {
+ ks8695_shutdown(ksp);
+ return ret;
+ }
+
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+/* Platform device driver */
+
+/**
+ * ks8695_init_switch - Init LAN switch to known good defaults.
+ * @ksp: The device to initialise
+ *
+ * This initialises the LAN switch in the KS8695 to a known-good
+ * set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Default value for SEC0 according to datasheet */
+ ctrl = 0x40819e00;
+
+ /* LED0 = Speed LED1 = Link/Activity */
+ ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+ ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+ /* Enable Switch */
+ ctrl |= SEC0_ENABLE;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+ /* Defaults for SEC1 */
+ writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ * @ksp: The device to initialise
+ *
+ * This initialises a KS8695's WAN phy to sensible values for
+ * autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+ u32 ctrl;
+
+ /* Support auto-negotiation */
+ ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+ WMC_WANA10F | WMC_WANA10H);
+
+ /* LED0 = Activity , LED1 = Link */
+ ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+ /* Restart Auto-negotiation */
+ ctrl |= WMC_WANR;
+
+ writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+ writel(0, ksp->phyiface_regs + KS8695_WPPM);
+ writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+ .ndo_open = ks8695_open,
+ .ndo_stop = ks8695_stop,
+ .ndo_start_xmit = ks8695_start_xmit,
+ .ndo_tx_timeout = ks8695_timeout,
+ .ndo_set_mac_address = ks8695_set_mac,
+ .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ * ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ * @pdev: The platform device to probe
+ *
+ * Initialise a KS8695 ethernet device from platform data.
+ *
+ * This driver requires at least one IORESOURCE_MEM for the
+ * registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ * respectively. It can optionally take an additional
+ * IORESOURCE_MEM for the switch or phy in the case of the lan or
+ * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ * port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+ struct ks8695_priv *ksp;
+ struct net_device *ndev;
+ struct resource *regs_res, *phyiface_res;
+ struct resource *rxirq_res, *txirq_res, *linkirq_res;
+ int ret = 0;
+ int buff_n;
+ u32 machigh, maclow;
+
+ /* Initialise a net_device */
+ ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+ /* Configure our private structure a little */
+ ksp = netdev_priv(ndev);
+ memset(ksp, 0, sizeof(struct ks8695_priv));
+
+ ksp->dev = &pdev->dev;
+ ksp->ndev = ndev;
+ ksp->msg_enable = NETIF_MSG_LINK;
+
+ /* Retrieve resources */
+ regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+ if (!(regs_res && rxirq_res && txirq_res)) {
+ dev_err(ksp->dev, "insufficient resources\n");
+ ret = -ENOENT;
+ goto failure;
+ }
+
+ ksp->regs_req = request_mem_region(regs_res->start,
+ resource_size(regs_res),
+ pdev->name);
+
+ if (!ksp->regs_req) {
+ dev_err(ksp->dev, "cannot claim register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+ if (!ksp->io_regs) {
+ dev_err(ksp->dev, "failed to ioremap registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ if (phyiface_res) {
+ ksp->phyiface_req =
+ request_mem_region(phyiface_res->start,
+ resource_size(phyiface_res),
+ phyiface_res->name);
+
+ if (!ksp->phyiface_req) {
+ dev_err(ksp->dev,
+ "cannot claim switch register space\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ ksp->phyiface_regs = ioremap(phyiface_res->start,
+ resource_size(phyiface_res));
+
+ if (!ksp->phyiface_regs) {
+ dev_err(ksp->dev,
+ "failed to ioremap switch registers\n");
+ ret = -EINVAL;
+ goto failure;
+ }
+ }
+
+ ksp->rx_irq = rxirq_res->start;
+ ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+ ksp->tx_irq = txirq_res->start;
+ ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+ ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+ ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+ linkirq_res->name : "Ethernet Link";
+
+ /* driver system setup */
+ ndev->netdev_ops = &ks8695_netdev_ops;
+ SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+ ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+
+ /* Retrieve the default MAC addr from the chip. */
+ /* The bootloader should have left it in there for us. */
+
+ machigh = ks8695_readreg(ksp, KS8695_MAH);
+ maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+ ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+ ndev->dev_addr[1] = machigh & 0xFF;
+ ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+ ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+ ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+ ndev->dev_addr[5] = maclow & 0xFF;
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", ndev->name);
+
+ /* In order to be efficient memory-wise, we allocate both
+ * rings in one go.
+ */
+ ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+ &ksp->ring_base_dma, GFP_KERNEL);
+ if (!ksp->ring_base) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* Specify the TX DMA ring buffer */
+ ksp->tx_ring = ksp->ring_base;
+ ksp->tx_ring_dma = ksp->ring_base_dma;
+
+ /* And initialise the queue's lock */
+ spin_lock_init(&ksp->txq_lock);
+
+ /* Specify the RX DMA ring buffer */
+ ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+ ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+ /* Zero the descriptor rings */
+ memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+ memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+ /* Build the rings */
+ for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+ ksp->tx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->tx_ring_dma +
+ (sizeof(struct tx_ring_desc) *
+ ((buff_n + 1) & MAX_TX_DESC_MASK)));
+ }
+
+ for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+ ksp->rx_ring[buff_n].next_desc =
+ cpu_to_le32(ksp->rx_ring_dma +
+ (sizeof(struct rx_ring_desc) *
+ ((buff_n + 1) & MAX_RX_DESC_MASK)));
+ }
+
+ /* Initialise the port (physically) */
+ if (ksp->phyiface_regs && ksp->link_irq == -1) {
+ ks8695_init_switch(ksp);
+ ksp->dtype = KS8695_DTYPE_LAN;
+ } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+ ks8695_init_wan_phy(ksp);
+ ksp->dtype = KS8695_DTYPE_WAN;
+ } else {
+ /* No initialisation since HPNA does not have a PHY */
+ ksp->dtype = KS8695_DTYPE_HPNA;
+ }
+
+ /* And bring up the net_device with the net core */
+ platform_set_drvdata(pdev, ndev);
+ ret = register_netdev(ndev);
+
+ if (ret == 0) {
+ dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+ ks8695_port_type(ksp), ndev->dev_addr);
+ } else {
+ /* Report the failure to register the net_device */
+ dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+ goto failure;
+ }
+
+ /* All is well */
+ return 0;
+
+ /* Error exit path */
+failure:
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ return ret;
+}
+
+/**
+ * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ * @pdev: The device to suspend
+ * @state: The suspend state
+ *
+ * This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ ksp->in_suspend = 1;
+
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ ks8695_shutdown(ksp);
+ }
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ * @pdev: The device to resume
+ *
+ * This routine re-initialises and re-attaches a KS8695 ethernet
+ * device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ ks8695_reset(ksp);
+ ks8695_init_net(ksp);
+ ks8695_set_multicast(ndev);
+ netif_device_attach(ndev);
+ }
+
+ ksp->in_suspend = 0;
+
+ return 0;
+}
+
+/**
+ * ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ * @pdev: The platform device to remove
+ *
+ * This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+ ks8695_release_device(ksp);
+ free_netdev(ndev);
+
+ dev_dbg(&pdev->dev, "released and freed device\n");
+ return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+ .driver = {
+ .name = MODULENAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8695_probe,
+ .remove = __devexit_p(ks8695_drv_remove),
+ .suspend = ks8695_drv_suspend,
+ .resume = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+ printk(KERN_INFO "%s Ethernet driver, V%s\n",
+ MODULENAME, MODULEVERSION);
+
+ return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+ platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics")
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
diff --git a/drivers/net/arm/ks8695net.h b/drivers/net/arm/ks8695net.h
new file mode 100644
index 000000000000..80eff6ea5163
--- /dev/null
+++ b/drivers/net/arm/ks8695net.h
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ * Daniel Silverstone <dsilvers@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN (1 << 31) /* Ownership */
+#define RDES_FS (1 << 30) /* First Descriptor */
+#define RDES_LS (1 << 29) /* Last Descriptor */
+#define RDES_IPE (1 << 28) /* IP Checksum error */
+#define RDES_TCPE (1 << 27) /* TCP Checksum error */
+#define RDES_UDPE (1 << 26) /* UDP Checksum error */
+#define RDES_ES (1 << 25) /* Error summary */
+#define RDES_MF (1 << 24) /* Multicast Frame */
+#define RDES_RE (1 << 19) /* MII Error reported */
+#define RDES_TL (1 << 18) /* Frame too Long */
+#define RDES_RF (1 << 17) /* Runt Frame */
+#define RDES_CE (1 << 16) /* CRC error */
+#define RDES_FT (1 << 15) /* Frame Type */
+#define RDES_FLEN (0x7ff) /* Frame Length */
+
+#define RDES_RER (1 << 25) /* Receive End of Ring */
+#define RDES_RBS (0x7ff) /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN (1 << 31) /* Ownership */
+
+#define TDES_IC (1 << 31) /* Interrupt on Completion */
+#define TDES_FS (1 << 30) /* First Segment */
+#define TDES_LS (1 << 29) /* Last Segment */
+#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
+#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
+#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
+#define TDES_TER (1 << 25) /* Transmit End of Ring */
+#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC (0x00) /* DMA Transmit Control */
+#define KS8695_DRXC (0x04) /* DMA Receive Control */
+#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
+#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
+#define KS8695_TDLB (0x10) /* Transmit Descriptor List
+ * Base Address
+ */
+#define KS8695_RDLB (0x14) /* Receive Descriptor List
+ * Base Address
+ */
+#define KS8695_MAL (0x18) /* MAC Station Address Low */
+#define KS8695_MAH (0x1c) /* MAC Station Address High */
+#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) Low
+ */
+#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
+ * Station Address
+ * (0..15) High
+ */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST (1 << 31) /* Soft Reset */
+#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
+#define DTXC_TUCG (1 << 18) /* Transmit UDP
+ * Checksum Generate
+ */
+#define DTXC_TTCG (1 << 17) /* Transmit TCP
+ * Checksum Generate
+ */
+#define DTXC_TICG (1 << 16) /* Transmit IP
+ * Checksum Generate
+ */
+#define DTXC_TFCE (1 << 9) /* Transmit Flow
+ * Control Enable
+ */
+#define DTXC_TLB (1 << 8) /* Loopback mode */
+#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
+#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
+#define DTXC_TE (1 << 0) /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
+#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
+#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
+#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
+#define DRXC_RFCE (1 << 9) /* Receive Flow Control
+ * Enable
+ */
+#define DRXC_RB (1 << 6) /* Receive Broadcast */
+#define DRXC_RM (1 << 5) /* Receive Multicast */
+#define DRXC_RU (1 << 4) /* Receive Unicast */
+#define DRXC_RERR (1 << 3) /* Receive Error Frame */
+#define DRXC_RA (1 << 2) /* Receive All */
+#define DRXC_RE (1 << 0) /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E (1 << 31) /* Address Enabled */
+
+#endif /* KS8695NET_H */
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index 7e874d485d24..72ea6e378f8d 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -265,7 +265,6 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr)
unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0;
int slot, ret = -ENODEV;
struct net_local *lp = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, AT1700_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -397,7 +396,7 @@ found:
dev->dev_addr[i] = val;
}
}
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
/* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
rather than 150 ohm shielded twisted pair compensation.
@@ -768,7 +767,6 @@ net_rx(struct net_device *dev)
insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
skb->protocol=eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -901,15 +899,3 @@ module_init(at1700_module_init);
module_exit(at1700_module_exit);
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
- * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
- * tab-width: 4
- * c-basic-offset: 4
- * c-indent-level: 4
- * End:
- */
-
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index 0860cc280b01..2d81f6afcb58 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -466,7 +466,6 @@ static unsigned long __init lance_probe1( struct net_device *dev,
int i;
static int did_version;
unsigned short save1, save2;
- DECLARE_MAC_BUF(mac);
PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n",
(long)memaddr, (long)ioaddr ));
@@ -521,7 +520,7 @@ static unsigned long __init lance_probe1( struct net_device *dev,
return( 0 );
probe_ok:
- lp = (struct lance_private *)dev->priv;
+ lp = netdev_priv(dev);
MEM = (struct lance_memory *)memaddr;
IO = lp->iobase = (struct lance_ioreg *)ioaddr;
dev->base_addr = (unsigned long)ioaddr; /* informational only */
@@ -595,7 +594,7 @@ static unsigned long __init lance_probe1( struct net_device *dev,
i = IO->mem;
break;
}
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
if (lp->cardtype == OLD_RIEBL) {
printk( "%s: Warning: This is a default ethernet address!\n",
dev->name );
@@ -640,8 +639,8 @@ static unsigned long __init lance_probe1( struct net_device *dev,
static int lance_open( struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
int i;
@@ -681,8 +680,8 @@ static int lance_open( struct net_device *dev )
/* Initialize the LANCE Rx and Tx rings. */
static void lance_init_ring( struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
int i;
unsigned offset;
@@ -730,7 +729,7 @@ static void lance_init_ring( struct net_device *dev )
static void lance_tx_timeout (struct net_device *dev)
{
- struct lance_private *lp = (struct lance_private *) dev->priv;
+ struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
AREG = CSR0;
@@ -772,14 +771,12 @@ static void lance_tx_timeout (struct net_device *dev)
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
int entry, len;
struct lance_tx_head *head;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n",
dev->name, DREG ));
@@ -802,12 +799,10 @@ static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev )
/* Fill in a Tx ring entry */
if (lance_debug >= 3) {
- printk( "%s: TX pkt type 0x%04x from "
- "%s to %s"
+ printk( "%s: TX pkt type 0x%04x from %pM to %pM"
" data at 0x%08x len %d\n",
dev->name, ((u_short *)skb->data)[6],
- print_mac(mac, &skb->data[6]),
- print_mac(mac2, skb->data),
+ &skb->data[6], skb->data,
(int)skb->data, (int)skb->len );
}
@@ -865,7 +860,7 @@ static irqreturn_t lance_interrupt( int irq, void *dev_id )
return IRQ_NONE;
}
- lp = (struct lance_private *)dev->priv;
+ lp = netdev_priv(dev);
IO = lp->iobase;
spin_lock (&lp->devlock);
@@ -965,8 +960,8 @@ static irqreturn_t lance_interrupt( int irq, void *dev_id )
static int lance_rx( struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
int entry = lp->cur_rx & RX_RING_MOD_MASK;
int i;
@@ -1019,14 +1014,12 @@ static int lance_rx( struct net_device *dev )
if (lance_debug >= 3) {
u_char *data = PKTBUF_ADDR(head);
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- printk(KERN_DEBUG "%s: RX pkt type 0x%04x from %s to %s "
+ printk(KERN_DEBUG "%s: RX pkt type 0x%04x from %pM to %pM "
"data %02x %02x %02x %02x %02x %02x %02x %02x "
"len %d\n",
dev->name, ((u_short *)data)[6],
- print_mac(mac, &data[6]), print_mac(mac2, data),
+ &data[6], data,
data[15], data[16], data[17], data[18],
data[19], data[20], data[21], data[22],
pkt_len);
@@ -1037,7 +1030,6 @@ static int lance_rx( struct net_device *dev )
lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len );
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -1057,8 +1049,8 @@ static int lance_rx( struct net_device *dev )
static int lance_close( struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
netif_stop_queue (dev);
@@ -1084,8 +1076,8 @@ static int lance_close( struct net_device *dev )
*/
static void set_multicast_list( struct net_device *dev )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
struct lance_ioreg *IO = lp->iobase;
if (netif_running(dev))
@@ -1126,8 +1118,8 @@ static void set_multicast_list( struct net_device *dev )
/* This is needed for old RieblCards and possible for new RieblCards */
static int lance_set_mac_address( struct net_device *dev, void *addr )
-
-{ struct lance_private *lp = (struct lance_private *)dev->priv;
+{
+ struct lance_private *lp = netdev_priv(dev);
struct sockaddr *saddr = addr;
int i;
diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c
index 9b603528143d..bb9094d4cbc9 100644
--- a/drivers/net/atl1e/atl1e_main.c
+++ b/drivers/net/atl1e/atl1e_main.c
@@ -1326,9 +1326,9 @@ static irqreturn_t atl1e_intr(int irq, void *data)
AT_WRITE_REG(hw, REG_IMR,
IMR_NORMAL_MASK & ~ISR_RX_EVENT);
AT_WRITE_FLUSH(hw);
- if (likely(netif_rx_schedule_prep(netdev,
+ if (likely(netif_rx_schedule_prep(
&adapter->napi)))
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
} while (--max_ints > 0);
/* re-enable Interrupt*/
@@ -1460,7 +1460,6 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
netif_receive_skb(skb);
}
- netdev->last_rx = jiffies;
skip_pkt:
/* skip current packet whether it's ok or not. */
rx_page->read_offset +=
@@ -1502,7 +1501,6 @@ static int atl1e_clean(struct napi_struct *napi, int budget)
{
struct atl1e_adapter *adapter =
container_of(napi, struct atl1e_adapter, napi);
- struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
u32 imr_data;
int work_done = 0;
@@ -1516,7 +1514,7 @@ static int atl1e_clean(struct napi_struct *napi, int budget)
/* If no Tx and not enough Rx work done, exit the polling mode */
if (work_done < budget) {
quit_polling:
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
imr_data = AT_READ_REG(&adapter->hw, REG_IMR);
AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT);
/* test debug */
@@ -2254,26 +2252,33 @@ static void atl1e_shutdown(struct pci_dev *pdev)
atl1e_suspend(pdev, PMSG_SUSPEND);
}
+static const struct net_device_ops atl1e_netdev_ops = {
+ .ndo_open = atl1e_open,
+ .ndo_stop = atl1e_close,
+ .ndo_start_xmit = atl1e_xmit_frame,
+ .ndo_get_stats = atl1e_get_stats,
+ .ndo_set_multicast_list = atl1e_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl1e_set_mac_addr,
+ .ndo_change_mtu = atl1e_change_mtu,
+ .ndo_do_ioctl = atl1e_ioctl,
+ .ndo_tx_timeout = atl1e_tx_timeout,
+ .ndo_vlan_rx_register = atl1e_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl1e_netpoll,
+#endif
+
+};
+
static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
{
SET_NETDEV_DEV(netdev, &pdev->dev);
pci_set_drvdata(pdev, netdev);
netdev->irq = pdev->irq;
- netdev->open = &atl1e_open;
- netdev->stop = &atl1e_close;
- netdev->hard_start_xmit = &atl1e_xmit_frame;
- netdev->get_stats = &atl1e_get_stats;
- netdev->set_multicast_list = &atl1e_set_multi;
- netdev->set_mac_address = &atl1e_set_mac_addr;
- netdev->change_mtu = &atl1e_change_mtu;
- netdev->do_ioctl = &atl1e_ioctl;
- netdev->tx_timeout = &atl1e_tx_timeout;
+ netdev->netdev_ops = &atl1e_netdev_ops;
+
netdev->watchdog_timeo = AT_TX_WATCHDOG;
- netdev->vlan_rx_register = atl1e_vlan_rx_register;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = atl1e_netpoll;
-#endif
atl1e_set_ethtool_ops(netdev);
netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM |
@@ -2488,7 +2493,7 @@ static pci_ers_result_t
atl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct atl1e_adapter *adapter = netdev->priv;
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
netif_device_detach(netdev);
@@ -2511,7 +2516,7 @@ atl1e_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
static pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct atl1e_adapter *adapter = netdev->priv;
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
if (pci_enable_device(pdev)) {
dev_err(&pdev->dev,
@@ -2539,7 +2544,7 @@ static pci_ers_result_t atl1e_io_slot_reset(struct pci_dev *pdev)
static void atl1e_io_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct atl1e_adapter *adapter = netdev->priv;
+ struct atl1e_adapter *adapter = netdev_priv(netdev);
if (netif_running(netdev)) {
if (atl1e_up(adapter)) {
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index aef403d299ee..c0ceee0d7c80 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -195,7 +195,7 @@ static int __devinit atl1_validate_option(int *value, struct atl1_option *opt,
* value exists, a default value is used. The final value is stored
* in a variable in the adapter structure.
*/
-void __devinit atl1_check_options(struct atl1_adapter *adapter)
+static void __devinit atl1_check_options(struct atl1_adapter *adapter)
{
struct pci_dev *pdev = adapter->pdev;
int bd = adapter->bd_number;
@@ -523,7 +523,7 @@ static int atl1_get_permanent_address(struct atl1_hw *hw)
* Reads the adapter's MAC address from the EEPROM
* hw - Struct containing variables accessed by shared code
*/
-s32 atl1_read_mac_addr(struct atl1_hw *hw)
+static s32 atl1_read_mac_addr(struct atl1_hw *hw)
{
u16 i;
@@ -1390,7 +1390,8 @@ static u32 atl1_check_link(struct atl1_adapter *adapter)
/* auto-neg, insert timer to re-config phy */
if (!adapter->phy_timer_pending) {
adapter->phy_timer_pending = true;
- mod_timer(&adapter->phy_config_timer, jiffies + 3 * HZ);
+ mod_timer(&adapter->phy_config_timer,
+ round_jiffies(jiffies + 3 * HZ));
}
return 0;
@@ -1662,6 +1663,7 @@ static void atl1_via_workaround(struct atl1_adapter *adapter)
static void atl1_inc_smb(struct atl1_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
struct stats_msg_block *smb = adapter->smb.smb;
/* Fill out the OS statistics structure */
@@ -1704,30 +1706,30 @@ static void atl1_inc_smb(struct atl1_adapter *adapter)
adapter->soft_stats.tx_trunc += smb->tx_trunc;
adapter->soft_stats.tx_pause += smb->tx_pause;
- adapter->net_stats.rx_packets = adapter->soft_stats.rx_packets;
- adapter->net_stats.tx_packets = adapter->soft_stats.tx_packets;
- adapter->net_stats.rx_bytes = adapter->soft_stats.rx_bytes;
- adapter->net_stats.tx_bytes = adapter->soft_stats.tx_bytes;
- adapter->net_stats.multicast = adapter->soft_stats.multicast;
- adapter->net_stats.collisions = adapter->soft_stats.collisions;
- adapter->net_stats.rx_errors = adapter->soft_stats.rx_errors;
- adapter->net_stats.rx_over_errors =
+ netdev->stats.rx_packets = adapter->soft_stats.rx_packets;
+ netdev->stats.tx_packets = adapter->soft_stats.tx_packets;
+ netdev->stats.rx_bytes = adapter->soft_stats.rx_bytes;
+ netdev->stats.tx_bytes = adapter->soft_stats.tx_bytes;
+ netdev->stats.multicast = adapter->soft_stats.multicast;
+ netdev->stats.collisions = adapter->soft_stats.collisions;
+ netdev->stats.rx_errors = adapter->soft_stats.rx_errors;
+ netdev->stats.rx_over_errors =
adapter->soft_stats.rx_missed_errors;
- adapter->net_stats.rx_length_errors =
+ netdev->stats.rx_length_errors =
adapter->soft_stats.rx_length_errors;
- adapter->net_stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors;
- adapter->net_stats.rx_frame_errors =
+ netdev->stats.rx_crc_errors = adapter->soft_stats.rx_crc_errors;
+ netdev->stats.rx_frame_errors =
adapter->soft_stats.rx_frame_errors;
- adapter->net_stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors;
- adapter->net_stats.rx_missed_errors =
+ netdev->stats.rx_fifo_errors = adapter->soft_stats.rx_fifo_errors;
+ netdev->stats.rx_missed_errors =
adapter->soft_stats.rx_missed_errors;
- adapter->net_stats.tx_errors = adapter->soft_stats.tx_errors;
- adapter->net_stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors;
- adapter->net_stats.tx_aborted_errors =
+ netdev->stats.tx_errors = adapter->soft_stats.tx_errors;
+ netdev->stats.tx_fifo_errors = adapter->soft_stats.tx_fifo_errors;
+ netdev->stats.tx_aborted_errors =
adapter->soft_stats.tx_aborted_errors;
- adapter->net_stats.tx_window_errors =
+ netdev->stats.tx_window_errors =
adapter->soft_stats.tx_window_errors;
- adapter->net_stats.tx_carrier_errors =
+ netdev->stats.tx_carrier_errors =
adapter->soft_stats.tx_carrier_errors;
}
@@ -1860,7 +1862,7 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
adapter->rx_buffer_len + NET_IP_ALIGN);
if (unlikely(!skb)) {
/* Better luck next round */
- adapter->net_stats.rx_dropped++;
+ adapter->netdev->stats.rx_dropped++;
break;
}
@@ -2026,8 +2028,6 @@ rrd_ok:
buffer_info->skb = NULL;
buffer_info->alloced = 0;
rrd->xsz.valid = 0;
-
- adapter->netdev->last_rx = jiffies;
}
atomic_set(&rrd_ring->next_to_clean, rrd_next_to_clean);
@@ -2524,17 +2524,6 @@ static irqreturn_t atl1_intr(int irq, void *data)
return IRQ_HANDLED;
}
-/*
- * atl1_watchdog - Timer Call-back
- * @data: pointer to netdev cast into an unsigned long
- */
-static void atl1_watchdog(unsigned long data)
-{
- struct atl1_adapter *adapter = (struct atl1_adapter *)data;
-
- /* Reset the timer */
- mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
-}
/*
* atl1_phy_config - Timer Call-back
@@ -2607,7 +2596,6 @@ static s32 atl1_up(struct atl1_adapter *adapter)
if (unlikely(err))
goto err_up;
- mod_timer(&adapter->watchdog_timer, jiffies);
atlx_irq_enable(adapter);
atl1_check_link(adapter);
netif_start_queue(netdev);
@@ -2625,7 +2613,6 @@ static void atl1_down(struct atl1_adapter *adapter)
struct net_device *netdev = adapter->netdev;
netif_stop_queue(netdev);
- del_timer_sync(&adapter->watchdog_timer);
del_timer_sync(&adapter->phy_config_timer);
adapter->phy_timer_pending = false;
@@ -2893,6 +2880,22 @@ static void atl1_poll_controller(struct net_device *netdev)
}
#endif
+static const struct net_device_ops atl1_netdev_ops = {
+ .ndo_open = atl1_open,
+ .ndo_stop = atl1_close,
+ .ndo_start_xmit = atl1_xmit_frame,
+ .ndo_set_multicast_list = atlx_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl1_set_mac,
+ .ndo_change_mtu = atl1_change_mtu,
+ .ndo_do_ioctl = atlx_ioctl,
+ .ndo_tx_timeout = atlx_tx_timeout,
+ .ndo_vlan_rx_register = atlx_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl1_poll_controller,
+#endif
+};
+
/*
* atl1_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -2980,20 +2983,8 @@ static int __devinit atl1_probe(struct pci_dev *pdev,
adapter->mii.phy_id_mask = 0x1f;
adapter->mii.reg_num_mask = 0x1f;
- netdev->open = &atl1_open;
- netdev->stop = &atl1_close;
- netdev->hard_start_xmit = &atl1_xmit_frame;
- netdev->get_stats = &atlx_get_stats;
- netdev->set_multicast_list = &atlx_set_multi;
- netdev->set_mac_address = &atl1_set_mac;
- netdev->change_mtu = &atl1_change_mtu;
- netdev->do_ioctl = &atlx_ioctl;
- netdev->tx_timeout = &atlx_tx_timeout;
+ netdev->netdev_ops = &atl1_netdev_ops;
netdev->watchdog_timeo = 5 * HZ;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = atl1_poll_controller;
-#endif
- netdev->vlan_rx_register = atlx_vlan_rx_register;
netdev->ethtool_ops = &atl1_ethtool_ops;
adapter->bd_number = cards_found;
@@ -3049,13 +3040,8 @@ static int __devinit atl1_probe(struct pci_dev *pdev,
netif_carrier_off(netdev);
netif_stop_queue(netdev);
- init_timer(&adapter->watchdog_timer);
- adapter->watchdog_timer.function = &atl1_watchdog;
- adapter->watchdog_timer.data = (unsigned long)adapter;
-
- init_timer(&adapter->phy_config_timer);
- adapter->phy_config_timer.function = &atl1_phy_config;
- adapter->phy_config_timer.data = (unsigned long)adapter;
+ setup_timer(&adapter->phy_config_timer, &atl1_phy_config,
+ (unsigned long)adapter);
adapter->phy_timer_pending = false;
INIT_WORK(&adapter->tx_timeout_task, atl1_tx_timeout_task);
@@ -3173,8 +3159,6 @@ static struct atl1_stats atl1_gstrings_stats[] = {
{"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
{"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
{"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
- {"rx_dropped", ATL1_STAT(net_stats.rx_dropped)},
- {"tx_dropped", ATL1_STAT(net_stats.tx_dropped)},
{"multicast", ATL1_STAT(soft_stats.multicast)},
{"collisions", ATL1_STAT(soft_stats.collisions)},
{"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
diff --git a/drivers/net/atlx/atl1.h b/drivers/net/atlx/atl1.h
index ffa73fc8d95e..146372fd6683 100644
--- a/drivers/net/atlx/atl1.h
+++ b/drivers/net/atlx/atl1.h
@@ -754,7 +754,7 @@ struct atl1_hw {
struct atl1_adapter {
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
+
struct atl1_sft_stats soft_stats;
struct vlan_group *vlgrp;
u32 rx_buffer_len;
@@ -765,7 +765,7 @@ struct atl1_adapter {
struct work_struct tx_timeout_task;
struct work_struct link_chg_task;
struct work_struct pcie_dma_to_rst_task;
- struct timer_list watchdog_timer;
+
struct timer_list phy_config_timer;
bool phy_timer_pending;
diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c
index 8571e8c0bc67..bc394491b63b 100644
--- a/drivers/net/atlx/atl2.c
+++ b/drivers/net/atlx/atl2.c
@@ -418,7 +418,7 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
* Check that some rx space is free. If not,
* free one and mark stats->rx_dropped++.
*/
- adapter->net_stats.rx_dropped++;
+ netdev->stats.rx_dropped++;
break;
}
skb_reserve(skb, NET_IP_ALIGN);
@@ -435,20 +435,19 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
} else
#endif
netif_rx(skb);
- adapter->net_stats.rx_bytes += rx_size;
- adapter->net_stats.rx_packets++;
- netdev->last_rx = jiffies;
+ netdev->stats.rx_bytes += rx_size;
+ netdev->stats.rx_packets++;
} else {
- adapter->net_stats.rx_errors++;
+ netdev->stats.rx_errors++;
if (rxd->status.ok && rxd->status.pkt_size <= 60)
- adapter->net_stats.rx_length_errors++;
+ netdev->stats.rx_length_errors++;
if (rxd->status.mcast)
- adapter->net_stats.multicast++;
+ netdev->stats.multicast++;
if (rxd->status.crc)
- adapter->net_stats.rx_crc_errors++;
+ netdev->stats.rx_crc_errors++;
if (rxd->status.align)
- adapter->net_stats.rx_frame_errors++;
+ netdev->stats.rx_frame_errors++;
}
/* advance write ptr */
@@ -463,6 +462,7 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
static void atl2_intr_tx(struct atl2_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
u32 txd_read_ptr;
u32 txs_write_ptr;
struct tx_pkt_status *txs;
@@ -522,20 +522,20 @@ static void atl2_intr_tx(struct atl2_adapter *adapter)
/* tx statistics: */
if (txs->ok) {
- adapter->net_stats.tx_bytes += txs->pkt_size;
- adapter->net_stats.tx_packets++;
+ netdev->stats.tx_bytes += txs->pkt_size;
+ netdev->stats.tx_packets++;
}
else
- adapter->net_stats.tx_errors++;
+ netdev->stats.tx_errors++;
if (txs->defer)
- adapter->net_stats.collisions++;
+ netdev->stats.collisions++;
if (txs->abort_col)
- adapter->net_stats.tx_aborted_errors++;
+ netdev->stats.tx_aborted_errors++;
if (txs->late_col)
- adapter->net_stats.tx_window_errors++;
+ netdev->stats.tx_window_errors++;
if (txs->underun)
- adapter->net_stats.tx_fifo_errors++;
+ netdev->stats.tx_fifo_errors++;
} while (1);
if (free_hole) {
@@ -621,7 +621,7 @@ static irqreturn_t atl2_intr(int irq, void *data)
/* link event */
if (status & (ISR_PHY | ISR_MANUAL)) {
- adapter->net_stats.tx_carrier_errors++;
+ adapter->netdev->stats.tx_carrier_errors++;
atl2_check_for_link(adapter);
}
@@ -644,7 +644,6 @@ static int atl2_request_irq(struct atl2_adapter *adapter)
int flags, err = 0;
flags = IRQF_SHARED;
-#ifdef CONFIG_PCI_MSI
adapter->have_msi = true;
err = pci_enable_msi(adapter->pdev);
if (err)
@@ -652,7 +651,6 @@ static int atl2_request_irq(struct atl2_adapter *adapter)
if (adapter->have_msi)
flags &= ~IRQF_SHARED;
-#endif
return request_irq(adapter->pdev->irq, &atl2_intr, flags, netdev->name,
netdev);
@@ -723,7 +721,7 @@ static int atl2_open(struct net_device *netdev)
clear_bit(__ATL2_DOWN, &adapter->flags);
- mod_timer(&adapter->watchdog_timer, jiffies + 4*HZ);
+ mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 4*HZ));
val = ATL2_READ_REG(&adapter->hw, REG_MASTER_CTRL);
ATL2_WRITE_REG(&adapter->hw, REG_MASTER_CTRL,
@@ -900,19 +898,6 @@ static int atl2_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
}
/*
- * atl2_get_stats - Get System Network Statistics
- * @netdev: network interface device structure
- *
- * Returns the address of the device statistics structure.
- * The statistics are actually updated from the timer callback.
- */
-static struct net_device_stats *atl2_get_stats(struct net_device *netdev)
-{
- struct atl2_adapter *adapter = netdev_priv(netdev);
- return &adapter->net_stats;
-}
-
-/*
* atl2_change_mtu - Change the Maximum Transfer Unit
* @netdev: network interface device structure
* @new_mtu: new value for maximum frame size
@@ -1050,18 +1035,21 @@ static void atl2_tx_timeout(struct net_device *netdev)
static void atl2_watchdog(unsigned long data)
{
struct atl2_adapter *adapter = (struct atl2_adapter *) data;
- u32 drop_rxd, drop_rxs;
- unsigned long flags;
if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
+ u32 drop_rxd, drop_rxs;
+ unsigned long flags;
+
spin_lock_irqsave(&adapter->stats_lock, flags);
drop_rxd = ATL2_READ_REG(&adapter->hw, REG_STS_RXD_OV);
drop_rxs = ATL2_READ_REG(&adapter->hw, REG_STS_RXS_OV);
- adapter->net_stats.rx_over_errors += (drop_rxd+drop_rxs);
spin_unlock_irqrestore(&adapter->stats_lock, flags);
+ adapter->netdev->stats.rx_over_errors += drop_rxd + drop_rxs;
+
/* Reset the timer */
- mod_timer(&adapter->watchdog_timer, jiffies + 4 * HZ);
+ mod_timer(&adapter->watchdog_timer,
+ round_jiffies(jiffies + 4 * HZ));
}
}
@@ -1265,7 +1253,8 @@ static int atl2_check_link(struct atl2_adapter *adapter)
* (if interval smaller than 5 seconds, something strange) */
if (!test_bit(__ATL2_DOWN, &adapter->flags)) {
if (!test_and_set_bit(0, &adapter->cfg_phy))
- mod_timer(&adapter->phy_config_timer, jiffies + 5 * HZ);
+ mod_timer(&adapter->phy_config_timer,
+ round_jiffies(jiffies + 5 * HZ));
}
return 0;
@@ -1320,6 +1309,23 @@ static void atl2_poll_controller(struct net_device *netdev)
}
#endif
+
+static const struct net_device_ops atl2_netdev_ops = {
+ .ndo_open = atl2_open,
+ .ndo_stop = atl2_close,
+ .ndo_start_xmit = atl2_xmit_frame,
+ .ndo_set_multicast_list = atl2_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = atl2_set_mac,
+ .ndo_change_mtu = atl2_change_mtu,
+ .ndo_do_ioctl = atl2_ioctl,
+ .ndo_tx_timeout = atl2_tx_timeout,
+ .ndo_vlan_rx_register = atl2_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = atl2_poll_controller,
+#endif
+};
+
/*
* atl2_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -1393,26 +1399,9 @@ static int __devinit atl2_probe(struct pci_dev *pdev,
atl2_setup_pcicmd(pdev);
- netdev->open = &atl2_open;
- netdev->stop = &atl2_close;
- netdev->hard_start_xmit = &atl2_xmit_frame;
- netdev->get_stats = &atl2_get_stats;
- netdev->set_multicast_list = &atl2_set_multi;
- netdev->set_mac_address = &atl2_set_mac;
- netdev->change_mtu = &atl2_change_mtu;
- netdev->do_ioctl = &atl2_ioctl;
+ netdev->netdev_ops = &atl2_netdev_ops;
atl2_set_ethtool_ops(netdev);
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = atl2_poll_controller;
-#endif
-#ifdef HAVE_TX_TIMEOUT
- netdev->tx_timeout = &atl2_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
-#endif
-#ifdef NETIF_F_HW_VLAN_TX
- netdev->vlan_rx_register = atl2_vlan_rx_register;
-#endif
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
netdev->mem_start = mmio_start;
diff --git a/drivers/net/atlx/atl2.h b/drivers/net/atlx/atl2.h
index 09974df76b18..d918bbe621ea 100644
--- a/drivers/net/atlx/atl2.h
+++ b/drivers/net/atlx/atl2.h
@@ -453,7 +453,6 @@ struct atl2_adapter {
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
#ifdef NETIF_F_HW_VLAN_TX
struct vlan_group *vlgrp;
#endif
diff --git a/drivers/net/atlx/atlx.c b/drivers/net/atlx/atlx.c
index 3cc9d1089ca1..3dc014215679 100644
--- a/drivers/net/atlx/atlx.c
+++ b/drivers/net/atlx/atlx.c
@@ -182,19 +182,6 @@ static void atlx_clear_phy_int(struct atlx_adapter *adapter)
}
/*
- * atlx_get_stats - Get System Network Statistics
- * @netdev: network interface device structure
- *
- * Returns the address of the device statistics structure.
- * The statistics are actually updated from the timer callback.
- */
-static struct net_device_stats *atlx_get_stats(struct net_device *netdev)
-{
- struct atlx_adapter *adapter = netdev_priv(netdev);
- return &adapter->net_stats;
-}
-
-/*
* atlx_tx_timeout - Respond to a Tx Hang
* @netdev: network interface device structure
*/
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index c10cd8058e23..ea493ce23982 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -248,7 +248,6 @@ static int __init atp_probe1(long ioaddr)
struct net_local *lp;
int saved_ctrl_reg, status, i;
int res;
- DECLARE_MAC_BUF(mac);
outb(0xff, ioaddr + PAR_DATA);
/* Save the original value of the Control register, in case we guessed
@@ -324,8 +323,8 @@ static int __init atp_probe1(long ioaddr)
#endif
printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, "
- "SAPROM %s.\n",
- dev->name, dev->base_addr, dev->irq, print_mac(mac, dev->dev_addr));
+ "SAPROM %pM.\n",
+ dev->name, dev->base_addr, dev->irq, dev->dev_addr);
/* Reset the ethernet hardware and activate the printer pass-through. */
write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
@@ -421,7 +420,7 @@ static unsigned short __init eeprom_op(long ioaddr, u32 cmd)
registers that "should" only need to be set once at boot, so that
there is non-reboot way to recover if something goes wrong.
- This is an attachable device: if there is no dev->priv entry then it wasn't
+ This is an attachable device: if there is no private entry then it wasn't
probed for at boot-time, and we need to probe for it again.
*/
static int net_open(struct net_device *dev)
@@ -803,21 +802,22 @@ static void net_rx(struct net_device *dev)
static void read_block(long ioaddr, int length, unsigned char *p, int data_mode)
{
-
if (data_mode <= 3) { /* Mode 0 or 1 */
outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR,
ioaddr + PAR_DATA);
if (data_mode <= 1) { /* Mode 0 or 1 */
- do *p++ = read_byte_mode0(ioaddr); while (--length > 0);
- } else /* Mode 2 or 3 */
- do *p++ = read_byte_mode2(ioaddr); while (--length > 0);
- } else if (data_mode <= 5)
- do *p++ = read_byte_mode4(ioaddr); while (--length > 0);
- else
- do *p++ = read_byte_mode6(ioaddr); while (--length > 0);
+ do { *p++ = read_byte_mode0(ioaddr); } while (--length > 0);
+ } else { /* Mode 2 or 3 */
+ do { *p++ = read_byte_mode2(ioaddr); } while (--length > 0);
+ }
+ } else if (data_mode <= 5) {
+ do { *p++ = read_byte_mode4(ioaddr); } while (--length > 0);
+ } else {
+ do { *p++ = read_byte_mode6(ioaddr); } while (--length > 0);
+ }
- outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+ outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
}
@@ -913,7 +913,8 @@ static void __exit atp_cleanup_module(void) {
struct net_device *next_dev;
while (root_atp_dev) {
- next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+ struct net_local *atp_local = netdev_priv(root_atp_dev);
+ next_dev = atp_local->next_module;
unregister_netdev(root_atp_dev);
/* No need to release_region(), since we never snarf it. */
free_netdev(root_atp_dev);
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 019b13c08ae6..9c875bb3f76c 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -193,7 +193,7 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES];
*/
static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
volatile u32 *const mii_control_reg = &aup->mac->mii_control;
volatile u32 *const mii_data_reg = &aup->mac->mii_data;
u32 timedout = 20;
@@ -228,7 +228,7 @@ static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
static void au1000_mdio_write(struct net_device *dev, int phy_addr,
int reg, u16 value)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
volatile u32 *const mii_control_reg = &aup->mac->mii_control;
volatile u32 *const mii_data_reg = &aup->mac->mii_data;
u32 timedout = 20;
@@ -283,7 +283,7 @@ static int au1000_mdiobus_reset(struct mii_bus *bus)
static int mii_probe (struct net_device *dev)
{
- struct au1000_private *const aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *const aup = netdev_priv(dev);
struct phy_device *phydev = NULL;
#if defined(AU1XXX_PHY_STATIC_CONFIG)
@@ -353,7 +353,6 @@ static int mii_probe (struct net_device *dev)
}
/* now we are supposed to have a proper phydev, to attach to... */
- BUG_ON(!phydev);
BUG_ON(phydev->attached_dev);
phydev = phy_connect(dev, phydev->dev.bus_id, &au1000_adjust_link, 0,
@@ -415,7 +414,7 @@ void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB)
static void enable_rx_tx(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (au1000_debug > 4)
printk(KERN_INFO "%s: enable_rx_tx\n", dev->name);
@@ -426,7 +425,7 @@ static void enable_rx_tx(struct net_device *dev)
static void hard_stop(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (au1000_debug > 4)
printk(KERN_INFO "%s: hard stop\n", dev->name);
@@ -438,7 +437,7 @@ static void hard_stop(struct net_device *dev)
static void enable_mac(struct net_device *dev, int force_reset)
{
unsigned long flags;
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
spin_lock_irqsave(&aup->lock, flags);
@@ -457,7 +456,7 @@ static void enable_mac(struct net_device *dev, int force_reset)
static void reset_mac_unlocked(struct net_device *dev)
{
- struct au1000_private *const aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *const aup = netdev_priv(dev);
int i;
hard_stop(dev);
@@ -483,7 +482,7 @@ static void reset_mac_unlocked(struct net_device *dev)
static void reset_mac(struct net_device *dev)
{
- struct au1000_private *const aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *const aup = netdev_priv(dev);
unsigned long flags;
if (au1000_debug > 4)
@@ -572,7 +571,7 @@ static int __init au1000_init_module(void)
static int au1000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct au1000_private *aup = (struct au1000_private *)dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (aup->phy_dev)
return phy_ethtool_gset(aup->phy_dev, cmd);
@@ -582,7 +581,7 @@ static int au1000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int au1000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct au1000_private *aup = (struct au1000_private *)dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -596,7 +595,7 @@ static int au1000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static void
au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct au1000_private *aup = (struct au1000_private *)dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
strcpy(info->driver, DRV_NAME);
strcpy(info->version, DRV_VERSION);
@@ -652,7 +651,7 @@ static struct net_device * au1000_probe(int port_num)
printk("%s: Au1xx0 Ethernet found at 0x%x, irq %d\n",
dev->name, base, irq);
- aup = dev->priv;
+ aup = netdev_priv(dev);
spin_lock_init(&aup->lock);
@@ -817,7 +816,7 @@ err_out:
*/
static int au1000_init(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
unsigned long flags;
int i;
u32 control;
@@ -868,7 +867,7 @@ static int au1000_init(struct net_device *dev)
static void
au1000_adjust_link(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
struct phy_device *phydev = aup->phy_dev;
unsigned long flags;
@@ -947,7 +946,7 @@ au1000_adjust_link(struct net_device *dev)
static int au1000_open(struct net_device *dev)
{
int retval;
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (au1000_debug > 4)
printk("%s: open: dev=%p\n", dev->name, dev);
@@ -982,7 +981,7 @@ static int au1000_open(struct net_device *dev)
static int au1000_close(struct net_device *dev)
{
unsigned long flags;
- struct au1000_private *const aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *const aup = netdev_priv(dev);
if (au1000_debug > 4)
printk("%s: close: dev=%p\n", dev->name, dev);
@@ -1013,7 +1012,7 @@ static void __exit au1000_cleanup_module(void)
for (i = 0; i < num_ifs; i++) {
dev = iflist[i].dev;
if (dev) {
- aup = (struct au1000_private *) dev->priv;
+ aup = netdev_priv(dev);
unregister_netdev(dev);
mdiobus_unregister(aup->mii_bus);
mdiobus_free(aup->mii_bus);
@@ -1035,7 +1034,7 @@ static void __exit au1000_cleanup_module(void)
static void update_tx_stats(struct net_device *dev, u32 status)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
struct net_device_stats *ps = &dev->stats;
if (status & TX_FRAME_ABORTED) {
@@ -1064,7 +1063,7 @@ static void update_tx_stats(struct net_device *dev, u32 status)
*/
static void au1000_tx_ack(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
volatile tx_dma_t *ptxd;
ptxd = aup->tx_dma_ring[aup->tx_tail];
@@ -1091,7 +1090,7 @@ static void au1000_tx_ack(struct net_device *dev)
*/
static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
struct net_device_stats *ps = &dev->stats;
volatile tx_dma_t *ptxd;
u32 buff_stat;
@@ -1145,7 +1144,7 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
static inline void update_rx_stats(struct net_device *dev, u32 status)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
struct net_device_stats *ps = &dev->stats;
ps->rx_packets++;
@@ -1173,7 +1172,7 @@ static inline void update_rx_stats(struct net_device *dev, u32 status)
*/
static int au1000_rx(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
struct sk_buff *skb;
volatile rx_dma_t *prxd;
u32 buff_stat, status;
@@ -1240,7 +1239,6 @@ static int au1000_rx(struct net_device *dev)
/* next descriptor */
prxd = aup->rx_dma_ring[aup->rx_head];
buff_stat = prxd->buff_stat;
- dev->last_rx = jiffies;
}
return 0;
}
@@ -1276,7 +1274,7 @@ static void au1000_tx_timeout(struct net_device *dev)
static void set_rx_mode(struct net_device *dev)
{
- struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (au1000_debug > 4)
printk("%s: set_rx_mode: flags=%x\n", dev->name, dev->flags);
@@ -1308,7 +1306,7 @@ static void set_rx_mode(struct net_device *dev)
static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct au1000_private *aup = (struct au1000_private *)dev->priv;
+ struct au1000_private *aup = netdev_priv(dev);
if (!netif_running(dev)) return -EINVAL;
diff --git a/drivers/net/ax88796.c b/drivers/net/ax88796.c
index 9a314d88e7b6..337488ec707c 100644
--- a/drivers/net/ax88796.c
+++ b/drivers/net/ax88796.c
@@ -758,13 +758,10 @@ static int ax_init_dev(struct net_device *dev, int first_init)
#endif
ax_NS8390_init(dev, 0);
- if (first_init) {
- DECLARE_MAC_BUF(mac);
-
- dev_info(&ax->dev->dev, "%dbit, irq %d, %lx, MAC: %s\n",
+ if (first_init)
+ dev_info(&ax->dev->dev, "%dbit, irq %d, %lx, MAC: %pM\n",
ei_status.word16 ? 16:8, dev->irq, dev->base_addr,
- print_mac(mac, dev->dev_addr));
- }
+ dev->dev_addr);
ret = register_netdev(dev);
if (ret)
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index c3bda5ce67c4..0e7470a201f0 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -829,7 +829,6 @@ static int b44_rx(struct b44 *bp, int budget)
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = eth_type_trans(skb, bp->dev);
netif_receive_skb(skb);
- bp->dev->last_rx = jiffies;
received++;
budget--;
next_pkt:
@@ -847,7 +846,6 @@ static int b44_rx(struct b44 *bp, int budget)
static int b44_poll(struct napi_struct *napi, int budget)
{
struct b44 *bp = container_of(napi, struct b44, napi);
- struct net_device *netdev = bp->dev;
int work_done;
spin_lock_irq(&bp->lock);
@@ -876,7 +874,7 @@ static int b44_poll(struct napi_struct *napi, int budget)
}
if (work_done < budget) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
b44_enable_ints(bp);
}
@@ -908,13 +906,13 @@ static irqreturn_t b44_interrupt(int irq, void *dev_id)
goto irq_ack;
}
- if (netif_rx_schedule_prep(dev, &bp->napi)) {
+ if (netif_rx_schedule_prep(&bp->napi)) {
/* NOTE: These writes are posted by the readback of
* the ISTAT register below.
*/
bp->istat = istat;
__b44_disable_ints(bp);
- __netif_rx_schedule(dev, &bp->napi);
+ __netif_rx_schedule(&bp->napi);
} else {
printk(KERN_ERR PFX "%s: Error, poll already scheduled\n",
dev->name);
@@ -2117,7 +2115,6 @@ static int __devinit b44_init_one(struct ssb_device *sdev,
struct net_device *dev;
struct b44 *bp;
int err;
- DECLARE_MAC_BUF(mac);
instance++;
@@ -2213,8 +2210,8 @@ static int __devinit b44_init_one(struct ssb_device *sdev,
*/
b44_chip_reset(bp, B44_CHIP_RESET_FULL);
- printk(KERN_INFO "%s: Broadcom 44xx/47xx 10/100BaseT Ethernet %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Broadcom 44xx/47xx 10/100BaseT Ethernet %pM\n",
+ dev->name, dev->dev_addr);
return 0;
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index b458d607a9c6..78e31aa861e0 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -741,7 +741,6 @@ static void bfin_mac_rx(struct net_device *dev)
blackfin_dcache_invalidate_range((unsigned long)skb->head,
(unsigned long)skb->tail);
- dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
#if defined(BFIN_MAC_CSUM_OFFLOAD)
skb->csum = current_rx_ptr->status.ip_payload_csum;
diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c
index a42bd19646d3..8a546a33d581 100644
--- a/drivers/net/bmac.c
+++ b/drivers/net/bmac.c
@@ -716,13 +716,11 @@ static irqreturn_t bmac_rxdma_intr(int irq, void *dev_id)
skb_put(skb, nb);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
++dev->stats.rx_packets;
dev->stats.rx_bytes += nb;
} else {
++dev->stats.rx_dropped;
}
- dev->last_rx = jiffies;
if ((skb = bp->rx_bufs[i]) == NULL) {
bp->rx_bufs[i] = skb = dev_alloc_skb(RX_BUFLEN+2);
if (skb != NULL)
@@ -1258,7 +1256,6 @@ static int __devinit bmac_probe(struct macio_dev *mdev, const struct of_device_i
unsigned char addr[6];
struct net_device *dev;
int is_bmac_plus = ((int)match->data) != 0;
- DECLARE_MAC_BUF(mac);
if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) {
printk(KERN_ERR "BMAC: can't use, need 3 addrs and 3 intrs\n");
@@ -1368,8 +1365,8 @@ static int __devinit bmac_probe(struct macio_dev *mdev, const struct of_device_i
goto err_out_irq2;
}
- printk(KERN_INFO "%s: BMAC%s at %s",
- dev->name, (is_bmac_plus ? "+" : ""), print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: BMAC%s at %pM",
+ dev->name, (is_bmac_plus ? "+" : ""), dev->dev_addr);
XXDEBUG((", base_addr=%#0lx", dev->base_addr));
printk("\n");
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index a1a3d0e5d2b4..d4a3dac21dcf 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -57,8 +57,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.8.1"
-#define DRV_MODULE_RELDATE "Oct 7, 2008"
+#define DRV_MODULE_VERSION "1.9.0"
+#define DRV_MODULE_RELDATE "Dec 16, 2008"
#define RUN_AT(x) (jiffies + (x))
@@ -89,6 +89,7 @@ typedef enum {
BCM5709,
BCM5709S,
BCM5716,
+ BCM5716S,
} board_t;
/* indexed by board_t, above */
@@ -105,6 +106,7 @@ static struct {
{ "Broadcom NetXtreme II BCM5709 1000Base-T" },
{ "Broadcom NetXtreme II BCM5709 1000Base-SX" },
{ "Broadcom NetXtreme II BCM5716 1000Base-T" },
+ { "Broadcom NetXtreme II BCM5716 1000Base-SX" },
};
static DEFINE_PCI_DEVICE_TABLE(bnx2_pci_tbl) = {
@@ -128,6 +130,8 @@ static DEFINE_PCI_DEVICE_TABLE(bnx2_pci_tbl) = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S },
{ PCI_VENDOR_ID_BROADCOM, 0x163b,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716 },
+ { PCI_VENDOR_ID_BROADCOM, 0x163c,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716S },
{ 0, }
};
@@ -543,9 +547,9 @@ bnx2_free_rx_mem(struct bnx2 *bp)
for (j = 0; j < bp->rx_max_pg_ring; j++) {
if (rxr->rx_pg_desc_ring[j])
pci_free_consistent(bp->pdev, RXBD_RING_SIZE,
- rxr->rx_pg_desc_ring[i],
- rxr->rx_pg_desc_mapping[i]);
- rxr->rx_pg_desc_ring[i] = NULL;
+ rxr->rx_pg_desc_ring[j],
+ rxr->rx_pg_desc_mapping[j]);
+ rxr->rx_pg_desc_ring[j] = NULL;
}
if (rxr->rx_pg_ring)
vfree(rxr->rx_pg_ring);
@@ -1652,7 +1656,7 @@ bnx2_setup_serdes_phy(struct bnx2 *bp, u8 port)
* exchanging base pages plus 3 next pages and
* normally completes in about 120 msec.
*/
- bp->current_interval = SERDES_AN_TIMEOUT;
+ bp->current_interval = BNX2_SERDES_AN_TIMEOUT;
bp->serdes_an_pending = 1;
mod_timer(&bp->timer, jiffies + bp->current_interval);
} else {
@@ -2274,7 +2278,7 @@ bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent)
return 0;
/* wait for an acknowledgement. */
- for (i = 0; i < (FW_ACK_TIME_OUT_MS / 10); i++) {
+ for (i = 0; i < (BNX2_FW_ACK_TIME_OUT_MS / 10); i++) {
msleep(10);
val = bnx2_shmem_rd(bp, BNX2_FW_MB);
@@ -3000,7 +3004,6 @@ bnx2_rx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
#endif
netif_receive_skb(skb);
- bp->dev->last_rx = jiffies;
rx_pkt++;
next_rx:
@@ -3040,7 +3043,6 @@ bnx2_msi(int irq, void *dev_instance)
{
struct bnx2_napi *bnapi = dev_instance;
struct bnx2 *bp = bnapi->bp;
- struct net_device *dev = bp->dev;
prefetch(bnapi->status_blk.msi);
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
@@ -3051,7 +3053,7 @@ bnx2_msi(int irq, void *dev_instance)
if (unlikely(atomic_read(&bp->intr_sem) != 0))
return IRQ_HANDLED;
- netif_rx_schedule(dev, &bnapi->napi);
+ netif_rx_schedule(&bnapi->napi);
return IRQ_HANDLED;
}
@@ -3061,7 +3063,6 @@ bnx2_msi_1shot(int irq, void *dev_instance)
{
struct bnx2_napi *bnapi = dev_instance;
struct bnx2 *bp = bnapi->bp;
- struct net_device *dev = bp->dev;
prefetch(bnapi->status_blk.msi);
@@ -3069,7 +3070,7 @@ bnx2_msi_1shot(int irq, void *dev_instance)
if (unlikely(atomic_read(&bp->intr_sem) != 0))
return IRQ_HANDLED;
- netif_rx_schedule(dev, &bnapi->napi);
+ netif_rx_schedule(&bnapi->napi);
return IRQ_HANDLED;
}
@@ -3079,7 +3080,6 @@ bnx2_interrupt(int irq, void *dev_instance)
{
struct bnx2_napi *bnapi = dev_instance;
struct bnx2 *bp = bnapi->bp;
- struct net_device *dev = bp->dev;
struct status_block *sblk = bnapi->status_blk.msi;
/* When using INTx, it is possible for the interrupt to arrive
@@ -3106,9 +3106,9 @@ bnx2_interrupt(int irq, void *dev_instance)
if (unlikely(atomic_read(&bp->intr_sem) != 0))
return IRQ_HANDLED;
- if (netif_rx_schedule_prep(dev, &bnapi->napi)) {
+ if (netif_rx_schedule_prep(&bnapi->napi)) {
bnapi->last_status_idx = sblk->status_idx;
- __netif_rx_schedule(dev, &bnapi->napi);
+ __netif_rx_schedule(&bnapi->napi);
}
return IRQ_HANDLED;
@@ -3218,7 +3218,7 @@ static int bnx2_poll_msix(struct napi_struct *napi, int budget)
rmb();
if (likely(!bnx2_has_fast_work(bnapi))) {
- netif_rx_complete(bp->dev, napi);
+ netif_rx_complete(napi);
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num |
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
bnapi->last_status_idx);
@@ -3251,7 +3251,7 @@ static int bnx2_poll(struct napi_struct *napi, int budget)
rmb();
if (likely(!bnx2_has_work(bnapi))) {
- netif_rx_complete(bp->dev, napi);
+ netif_rx_complete(napi);
if (likely(bp->flags & BNX2_FLAG_USING_MSI_OR_MSIX)) {
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
@@ -4493,7 +4493,7 @@ bnx2_reset_chip(struct bnx2 *bp, u32 reset_code)
static int
bnx2_init_chip(struct bnx2 *bp)
{
- u32 val;
+ u32 val, mtu;
int rc, i;
/* Make sure the interrupt is not active. */
@@ -4585,11 +4585,19 @@ bnx2_init_chip(struct bnx2 *bp)
REG_WR(bp, BNX2_EMAC_BACKOFF_SEED, val);
/* Program the MTU. Also include 4 bytes for CRC32. */
- val = bp->dev->mtu + ETH_HLEN + 4;
+ mtu = bp->dev->mtu;
+ val = mtu + ETH_HLEN + ETH_FCS_LEN;
if (val > (MAX_ETHERNET_PACKET_SIZE + 4))
val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA;
REG_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val);
+ if (mtu < 1500)
+ mtu = 1500;
+
+ bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG, BNX2_RBUF_CONFIG_VAL(mtu));
+ bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG2, BNX2_RBUF_CONFIG2_VAL(mtu));
+ bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG3, BNX2_RBUF_CONFIG3_VAL(mtu));
+
for (i = 0; i < BNX2_MAX_MSIX_VEC; i++)
bp->bnx2_napi[i].last_status_idx = 0;
@@ -5719,7 +5727,7 @@ bnx2_5708_serdes_timer(struct bnx2 *bp)
bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
if (bmcr & BMCR_ANENABLE) {
bnx2_enable_forced_2g5(bp);
- bp->current_interval = SERDES_FORCED_TIMEOUT;
+ bp->current_interval = BNX2_SERDES_FORCED_TIMEOUT;
} else {
bnx2_disable_forced_2g5(bp);
bp->serdes_an_pending = 2;
@@ -5816,6 +5824,8 @@ bnx2_enable_msix(struct bnx2 *bp, int msix_vecs)
{
int i, rc;
struct msix_entry msix_ent[BNX2_MAX_MSIX_VEC];
+ struct net_device *dev = bp->dev;
+ const int len = sizeof(bp->irq_tbl[0].name);
bnx2_setup_msix_tbl(bp);
REG_WR(bp, BNX2_PCI_MSIX_CONTROL, BNX2_MAX_MSIX_HW_VEC - 1);
@@ -5826,7 +5836,7 @@ bnx2_enable_msix(struct bnx2 *bp, int msix_vecs)
msix_ent[i].entry = i;
msix_ent[i].vector = 0;
- strcpy(bp->irq_tbl[i].name, bp->dev->name);
+ snprintf(bp->irq_tbl[i].name, len, "%s-%d", dev->name, i);
bp->irq_tbl[i].handler = bnx2_msi_1shot;
}
@@ -6173,7 +6183,7 @@ bnx2_get_stats(struct net_device *dev)
{
struct bnx2 *bp = netdev_priv(dev);
struct statistics_block *stats_blk = bp->stats_blk;
- struct net_device_stats *net_stats = &bp->net_stats;
+ struct net_device_stats *net_stats = &dev->stats;
if (bp->stats_blk == NULL) {
return net_stats;
@@ -6540,7 +6550,7 @@ bnx2_nway_reset(struct net_device *dev)
spin_lock_bh(&bp->phy_lock);
- bp->current_interval = SERDES_AN_TIMEOUT;
+ bp->current_interval = BNX2_SERDES_AN_TIMEOUT;
bp->serdes_an_pending = 1;
mod_timer(&bp->timer, jiffies + bp->current_interval);
}
@@ -7615,7 +7625,8 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
if ((CHIP_ID(bp) == CHIP_ID_5708_A0) ||
(CHIP_ID(bp) == CHIP_ID_5708_B0) ||
- (CHIP_ID(bp) == CHIP_ID_5708_B1)) {
+ (CHIP_ID(bp) == CHIP_ID_5708_B1) ||
+ !(REG_RD(bp, BNX2_PCI_CONFIG_3) & BNX2_PCI_CONFIG_3_VAUX_PRESET)) {
bp->flags |= BNX2_FLAG_NO_WOL;
bp->wol = 0;
}
@@ -7724,6 +7735,25 @@ bnx2_init_napi(struct bnx2 *bp)
}
}
+static const struct net_device_ops bnx2_netdev_ops = {
+ .ndo_open = bnx2_open,
+ .ndo_start_xmit = bnx2_start_xmit,
+ .ndo_stop = bnx2_close,
+ .ndo_get_stats = bnx2_get_stats,
+ .ndo_set_rx_mode = bnx2_set_rx_mode,
+ .ndo_do_ioctl = bnx2_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = bnx2_change_mac_addr,
+ .ndo_change_mtu = bnx2_change_mtu,
+ .ndo_tx_timeout = bnx2_tx_timeout,
+#ifdef BCM_VLAN
+ .ndo_vlan_rx_register = bnx2_vlan_rx_register,
+#endif
+#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
+ .ndo_poll_controller = poll_bnx2,
+#endif
+};
+
static int __devinit
bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -7732,7 +7762,6 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct bnx2 *bp;
int rc;
char str[40];
- DECLARE_MAC_BUF(mac);
if (version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -7749,28 +7778,13 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return rc;
}
- dev->open = bnx2_open;
- dev->hard_start_xmit = bnx2_start_xmit;
- dev->stop = bnx2_close;
- dev->get_stats = bnx2_get_stats;
- dev->set_rx_mode = bnx2_set_rx_mode;
- dev->do_ioctl = bnx2_ioctl;
- dev->set_mac_address = bnx2_change_mac_addr;
- dev->change_mtu = bnx2_change_mtu;
- dev->tx_timeout = bnx2_tx_timeout;
+ dev->netdev_ops = &bnx2_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef BCM_VLAN
- dev->vlan_rx_register = bnx2_vlan_rx_register;
-#endif
dev->ethtool_ops = &bnx2_ethtool_ops;
bp = netdev_priv(dev);
bnx2_init_napi(bp);
-#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
- dev->poll_controller = poll_bnx2;
-#endif
-
pci_set_drvdata(pdev, dev);
memcpy(dev->dev_addr, bp->mac_addr, 6);
@@ -7799,14 +7813,14 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
printk(KERN_INFO "%s: %s (%c%d) %s found at mem %lx, "
- "IRQ %d, node addr %s\n",
+ "IRQ %d, node addr %pM\n",
dev->name,
board_info[ent->driver_data].name,
((CHIP_ID(bp) & 0xf000) >> 12) + 'A',
((CHIP_ID(bp) & 0x0ff0) >> 4),
bnx2_bus_string(bp, str),
dev->base_addr,
- bp->pdev->irq, print_mac(mac, dev->dev_addr));
+ bp->pdev->irq, dev->dev_addr);
return 0;
}
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index 0b032c3c7b61..900641ac63e0 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -4202,7 +4202,14 @@ struct l2_fhdr {
#define BNX2_RBUF_CONFIG 0x0020000c
#define BNX2_RBUF_CONFIG_XOFF_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG_XOFF_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 31 / 1000) + 54)
#define BNX2_RBUF_CONFIG_XON_TRIP (0x3ffL<<16)
+#define BNX2_RBUF_CONFIG_XON_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 39 / 1000) + 66)
+#define BNX2_RBUF_CONFIG_VAL(mtu) \
+ (BNX2_RBUF_CONFIG_XOFF_TRIP_VAL(mtu) | \
+ (BNX2_RBUF_CONFIG_XON_TRIP_VAL(mtu) << 16))
#define BNX2_RBUF_FW_BUF_ALLOC 0x00200010
#define BNX2_RBUF_FW_BUF_ALLOC_VALUE (0x1ffL<<7)
@@ -4224,11 +4231,25 @@ struct l2_fhdr {
#define BNX2_RBUF_CONFIG2 0x0020001c
#define BNX2_RBUF_CONFIG2_MAC_DROP_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG2_MAC_DROP_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 4 / 1000) + 5)
#define BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP (0x3ffL<<16)
+#define BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 2 / 100) + 30)
+#define BNX2_RBUF_CONFIG2_VAL(mtu) \
+ (BNX2_RBUF_CONFIG2_MAC_DROP_TRIP_VAL(mtu) | \
+ (BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP_VAL(mtu) << 16))
#define BNX2_RBUF_CONFIG3 0x00200020
#define BNX2_RBUF_CONFIG3_CU_DROP_TRIP (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG3_CU_DROP_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 12 / 1000) + 18)
#define BNX2_RBUF_CONFIG3_CU_KEEP_TRIP (0x3ffL<<16)
+#define BNX2_RBUF_CONFIG3_CU_KEEP_TRIP_VAL(mtu) \
+ ((((mtu) - 1500) * 2 / 100) + 30)
+#define BNX2_RBUF_CONFIG3_VAL(mtu) \
+ (BNX2_RBUF_CONFIG3_CU_DROP_TRIP_VAL(mtu) | \
+ (BNX2_RBUF_CONFIG3_CU_KEEP_TRIP_VAL(mtu) << 16))
#define BNX2_RBUF_PKT_DATA 0x00208000
#define BNX2_RBUF_CLIST_DATA 0x00210000
@@ -6606,7 +6627,7 @@ struct bnx2_irq {
irq_handler_t handler;
unsigned int vector;
u8 requested;
- char name[16];
+ char name[IFNAMSIZ + 2];
};
struct bnx2_tx_ring_info {
@@ -6661,8 +6682,6 @@ struct bnx2_napi {
struct bnx2_tx_ring_info tx_ring;
};
-#define BNX2_TIMER_INTERVAL HZ
-
struct bnx2 {
/* Fields used in the tx and intr/napi performance paths are grouped */
/* together in the beginning of the structure. */
@@ -6710,7 +6729,11 @@ struct bnx2 {
/* End of fields used in the performance code paths. */
- int current_interval;
+ unsigned int current_interval;
+#define BNX2_TIMER_INTERVAL HZ
+#define BNX2_SERDES_AN_TIMEOUT (HZ / 3)
+#define BNX2_SERDES_FORCED_TIMEOUT (HZ / 10)
+
struct timer_list timer;
struct work_struct reset_task;
@@ -6825,9 +6848,6 @@ struct bnx2 {
u8 flow_ctrl; /* actual flow ctrl settings */
/* may be different from */
/* req_flow_ctrl if autoneg */
-#define FLOW_CTRL_TX 1
-#define FLOW_CTRL_RX 2
-
u32 advertising;
u8 req_flow_ctrl; /* flow ctrl advertisement */
@@ -6842,8 +6862,6 @@ struct bnx2 {
#define PHY_LOOPBACK 2
u8 serdes_an_pending;
-#define SERDES_AN_TIMEOUT (HZ / 3)
-#define SERDES_FORCED_TIMEOUT (HZ / 10)
u8 mac_addr[8];
@@ -6854,8 +6872,6 @@ struct bnx2 {
int pm_cap;
int pcix_cap;
- struct net_device_stats net_stats;
-
struct flash_spec *flash_info;
u32 flash_size;
@@ -6944,14 +6960,14 @@ struct fw_info {
/* This value (in milliseconds) determines the frequency of the driver
* issuing the PULSE message code. The firmware monitors this periodic
* pulse to determine when to switch to an OS-absent mode. */
-#define DRV_PULSE_PERIOD_MS 250
+#define BNX2_DRV_PULSE_PERIOD_MS 250
/* This value (in milliseconds) determines how long the driver should
* wait for an acknowledgement from the firmware before timing out. Once
* the firmware has timed out, the driver will assume there is no firmware
* running and there won't be any firmware-driver synchronization during a
* driver reset. */
-#define FW_ACK_TIME_OUT_MS 1000
+#define BNX2_FW_ACK_TIME_OUT_MS 1000
#define BNX2_DRV_RESET_SIGNATURE 0x00000000
diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c
index 4ce7fe9c5251..67de94f1f30e 100644
--- a/drivers/net/bnx2x_link.c
+++ b/drivers/net/bnx2x_link.c
@@ -289,7 +289,7 @@ static u8 bnx2x_emac_enable(struct link_params *params,
/* pause enable/disable */
bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_RX_MODE,
EMAC_RX_MODE_FLOW_EN);
- if (vars->flow_ctrl & FLOW_CTRL_RX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)
bnx2x_bits_en(bp, emac_base +
EMAC_REG_EMAC_RX_MODE,
EMAC_RX_MODE_FLOW_EN);
@@ -297,7 +297,7 @@ static u8 bnx2x_emac_enable(struct link_params *params,
bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_TX_MODE,
(EMAC_TX_MODE_EXT_PAUSE_EN |
EMAC_TX_MODE_FLOW_EN));
- if (vars->flow_ctrl & FLOW_CTRL_TX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
bnx2x_bits_en(bp, emac_base +
EMAC_REG_EMAC_TX_MODE,
(EMAC_TX_MODE_EXT_PAUSE_EN |
@@ -333,7 +333,7 @@ static u8 bnx2x_emac_enable(struct link_params *params,
/* enable the NIG in/out to the emac */
REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x1);
val = 0;
- if (vars->flow_ctrl & FLOW_CTRL_TX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
val = 1;
REG_WR(bp, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, val);
@@ -396,7 +396,7 @@ static u8 bnx2x_bmac_enable(struct link_params *params, struct link_vars *vars,
/* tx control */
val = 0xc0;
- if (vars->flow_ctrl & FLOW_CTRL_TX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
val |= 0x800000;
wb_data[0] = val;
wb_data[1] = 0;
@@ -423,7 +423,7 @@ static u8 bnx2x_bmac_enable(struct link_params *params, struct link_vars *vars,
/* rx control set to don't strip crc */
val = 0x14;
- if (vars->flow_ctrl & FLOW_CTRL_RX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)
val |= 0x20;
wb_data[0] = val;
wb_data[1] = 0;
@@ -460,7 +460,7 @@ static u8 bnx2x_bmac_enable(struct link_params *params, struct link_vars *vars,
REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 0x0);
REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 0x0);
val = 0;
- if (vars->flow_ctrl & FLOW_CTRL_TX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
val = 1;
REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, val);
REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x0);
@@ -580,14 +580,14 @@ void bnx2x_link_status_update(struct link_params *params,
}
if (vars->link_status & LINK_STATUS_TX_FLOW_CONTROL_ENABLED)
- vars->flow_ctrl |= FLOW_CTRL_TX;
+ vars->flow_ctrl |= BNX2X_FLOW_CTRL_TX;
else
- vars->flow_ctrl &= ~FLOW_CTRL_TX;
+ vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_TX;
if (vars->link_status & LINK_STATUS_RX_FLOW_CONTROL_ENABLED)
- vars->flow_ctrl |= FLOW_CTRL_RX;
+ vars->flow_ctrl |= BNX2X_FLOW_CTRL_RX;
else
- vars->flow_ctrl &= ~FLOW_CTRL_RX;
+ vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_RX;
if (vars->phy_flags & PHY_XGXS_FLAG) {
if (vars->line_speed &&
@@ -618,7 +618,7 @@ void bnx2x_link_status_update(struct link_params *params,
vars->line_speed = 0;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
/* indicate no mac active */
vars->mac_type = MAC_TYPE_NONE;
@@ -691,7 +691,7 @@ static u8 bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl,
return -EINVAL;
}
- if (flow_ctrl & FLOW_CTRL_RX ||
+ if (flow_ctrl & BNX2X_FLOW_CTRL_RX ||
line_speed == SPEED_10 ||
line_speed == SPEED_100 ||
line_speed == SPEED_1000 ||
@@ -1300,8 +1300,8 @@ static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u32 *ieee_fc)
* Please refer to Table 28B-3 of the 802.3ab-1999 spec */
switch (params->req_flow_ctrl) {
- case FLOW_CTRL_AUTO:
- if (params->req_fc_auto_adv == FLOW_CTRL_BOTH) {
+ case BNX2X_FLOW_CTRL_AUTO:
+ if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH) {
*ieee_fc |=
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
} else {
@@ -1309,17 +1309,17 @@ static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u32 *ieee_fc)
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
}
break;
- case FLOW_CTRL_TX:
+ case BNX2X_FLOW_CTRL_TX:
*ieee_fc |=
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
break;
- case FLOW_CTRL_RX:
- case FLOW_CTRL_BOTH:
+ case BNX2X_FLOW_CTRL_RX:
+ case BNX2X_FLOW_CTRL_BOTH:
*ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
break;
- case FLOW_CTRL_NONE:
+ case BNX2X_FLOW_CTRL_NONE:
default:
*ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE;
break;
@@ -1463,18 +1463,18 @@ static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result)
{ /* LD LP */
switch (pause_result) { /* ASYM P ASYM P */
case 0xb: /* 1 0 1 1 */
- vars->flow_ctrl = FLOW_CTRL_TX;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_TX;
break;
case 0xe: /* 1 1 1 0 */
- vars->flow_ctrl = FLOW_CTRL_RX;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_RX;
break;
case 0x5: /* 0 1 0 1 */
case 0x7: /* 0 1 1 1 */
case 0xd: /* 1 1 0 1 */
case 0xf: /* 1 1 1 1 */
- vars->flow_ctrl = FLOW_CTRL_BOTH;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH;
break;
default:
@@ -1531,7 +1531,7 @@ static u8 bnx2x_ext_phy_resove_fc(struct link_params *params,
DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x \n",
pause_result);
bnx2x_pause_resolve(vars, pause_result);
- if (vars->flow_ctrl == FLOW_CTRL_NONE &&
+ if (vars->flow_ctrl == BNX2X_FLOW_CTRL_NONE &&
ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) {
bnx2x_cl45_read(bp, port,
ext_phy_type,
@@ -1567,10 +1567,10 @@ static void bnx2x_flow_ctrl_resolve(struct link_params *params,
u16 lp_pause; /* link partner */
u16 pause_result;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
/* resolve from gp_status in case of AN complete and not sgmii */
- if ((params->req_flow_ctrl == FLOW_CTRL_AUTO) &&
+ if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) &&
(gp_status & MDIO_AN_CL73_OR_37_COMPLETE) &&
(!(vars->phy_flags & PHY_SGMII_FLAG)) &&
(XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
@@ -1591,11 +1591,11 @@ static void bnx2x_flow_ctrl_resolve(struct link_params *params,
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7;
DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result);
bnx2x_pause_resolve(vars, pause_result);
- } else if ((params->req_flow_ctrl == FLOW_CTRL_AUTO) &&
+ } else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) &&
(bnx2x_ext_phy_resove_fc(params, vars))) {
return;
} else {
- if (params->req_flow_ctrl == FLOW_CTRL_AUTO)
+ if (params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO)
vars->flow_ctrl = params->req_fc_auto_adv;
else
vars->flow_ctrl = params->req_flow_ctrl;
@@ -1728,11 +1728,11 @@ static u8 bnx2x_link_settings_status(struct link_params *params,
LINK_STATUS_PARALLEL_DETECTION_USED;
}
- if (vars->flow_ctrl & FLOW_CTRL_TX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
vars->link_status |=
LINK_STATUS_TX_FLOW_CONTROL_ENABLED;
- if (vars->flow_ctrl & FLOW_CTRL_RX)
+ if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)
vars->link_status |=
LINK_STATUS_RX_FLOW_CONTROL_ENABLED;
@@ -1742,7 +1742,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params,
vars->phy_link_up = 0;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->autoneg = AUTO_NEG_DISABLED;
vars->mac_type = MAC_TYPE_NONE;
}
@@ -3924,7 +3924,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 0;
vars->line_speed = 0;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->mac_type = MAC_TYPE_NONE;
if (params->switch_cfg == SWITCH_CFG_1G)
@@ -3946,12 +3946,12 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 1;
vars->line_speed = SPEED_10000;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD);
/* enable on E1.5 FPGA */
if (CHIP_IS_E1H(bp)) {
vars->flow_ctrl |=
- (FLOW_CTRL_TX | FLOW_CTRL_RX);
+ (BNX2X_FLOW_CTRL_TX | BNX2X_FLOW_CTRL_RX);
vars->link_status |=
(LINK_STATUS_TX_FLOW_CONTROL_ENABLED |
LINK_STATUS_RX_FLOW_CONTROL_ENABLED);
@@ -3974,7 +3974,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 1;
vars->line_speed = SPEED_10000;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD);
bnx2x_bmac_enable(params, vars, 0);
@@ -3994,7 +3994,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 1;
vars->line_speed = SPEED_10000;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->mac_type = MAC_TYPE_BMAC;
vars->phy_flags = PHY_XGXS_FLAG;
@@ -4009,7 +4009,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 1;
vars->line_speed = SPEED_1000;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->mac_type = MAC_TYPE_EMAC;
vars->phy_flags = PHY_XGXS_FLAG;
@@ -4026,7 +4026,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
vars->link_up = 1;
vars->line_speed = SPEED_10000;
vars->duplex = DUPLEX_FULL;
- vars->flow_ctrl = FLOW_CTRL_NONE;
+ vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE;
vars->phy_flags = PHY_XGXS_FLAG;
diff --git a/drivers/net/bnx2x_link.h b/drivers/net/bnx2x_link.h
index 86d54a17b411..47cb585f4278 100644
--- a/drivers/net/bnx2x_link.h
+++ b/drivers/net/bnx2x_link.h
@@ -26,11 +26,11 @@
-#define FLOW_CTRL_AUTO PORT_FEATURE_FLOW_CONTROL_AUTO
-#define FLOW_CTRL_TX PORT_FEATURE_FLOW_CONTROL_TX
-#define FLOW_CTRL_RX PORT_FEATURE_FLOW_CONTROL_RX
-#define FLOW_CTRL_BOTH PORT_FEATURE_FLOW_CONTROL_BOTH
-#define FLOW_CTRL_NONE PORT_FEATURE_FLOW_CONTROL_NONE
+#define BNX2X_FLOW_CTRL_AUTO PORT_FEATURE_FLOW_CONTROL_AUTO
+#define BNX2X_FLOW_CTRL_TX PORT_FEATURE_FLOW_CONTROL_TX
+#define BNX2X_FLOW_CTRL_RX PORT_FEATURE_FLOW_CONTROL_RX
+#define BNX2X_FLOW_CTRL_BOTH PORT_FEATURE_FLOW_CONTROL_BOTH
+#define BNX2X_FLOW_CTRL_NONE PORT_FEATURE_FLOW_CONTROL_NONE
#define SPEED_AUTO_NEG 0
#define SPEED_12000 12000
diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c
index 600210d7eff9..ef8103b3523e 100644
--- a/drivers/net/bnx2x_main.c
+++ b/drivers/net/bnx2x_main.c
@@ -1328,7 +1328,6 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp,
dev_kfree_skb(skb);
}
- bp->dev->last_rx = jiffies;
/* put new skb in bin */
fp->tpa_pool[queue].skb = new_skb;
@@ -1557,7 +1556,6 @@ reuse_rx:
#endif
netif_receive_skb(skb);
- bp->dev->last_rx = jiffies;
next_rx:
rx_buf->skb = NULL;
@@ -1594,7 +1592,6 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
{
struct bnx2x_fastpath *fp = fp_cookie;
struct bnx2x *bp = fp->bp;
- struct net_device *dev = bp->dev;
int index = FP_IDX(fp);
/* Return here if interrupt is disabled */
@@ -1617,7 +1614,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
prefetch(&fp->status_blk->c_status_block.status_block_index);
prefetch(&fp->status_blk->u_status_block.status_block_index);
- netif_rx_schedule(dev, &bnx2x_fp(bp, index, napi));
+ netif_rx_schedule(&bnx2x_fp(bp, index, napi));
return IRQ_HANDLED;
}
@@ -1656,7 +1653,7 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
prefetch(&fp->status_blk->c_status_block.status_block_index);
prefetch(&fp->status_blk->u_status_block.status_block_index);
- netif_rx_schedule(dev, &bnx2x_fp(bp, 0, napi));
+ netif_rx_schedule(&bnx2x_fp(bp, 0, napi));
status &= ~mask;
}
@@ -1923,10 +1920,10 @@ static void bnx2x_link_report(struct bnx2x *bp)
else
printk("half duplex");
- if (bp->link_vars.flow_ctrl != FLOW_CTRL_NONE) {
- if (bp->link_vars.flow_ctrl & FLOW_CTRL_RX) {
+ if (bp->link_vars.flow_ctrl != BNX2X_FLOW_CTRL_NONE) {
+ if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) {
printk(", receive ");
- if (bp->link_vars.flow_ctrl & FLOW_CTRL_TX)
+ if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
printk("& transmit ");
} else {
printk(", transmit ");
@@ -1950,11 +1947,11 @@ static u8 bnx2x_initial_phy_init(struct bnx2x *bp)
/* It is recommended to turn off RX FC for jumbo frames
for better performance */
if (IS_E1HMF(bp))
- bp->link_params.req_fc_auto_adv = FLOW_CTRL_BOTH;
+ bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
else if (bp->dev->mtu > 5000)
- bp->link_params.req_fc_auto_adv = FLOW_CTRL_TX;
+ bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_TX;
else
- bp->link_params.req_fc_auto_adv = FLOW_CTRL_BOTH;
+ bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
bnx2x_acquire_phy_lock(bp);
rc = bnx2x_phy_init(&bp->link_params, &bp->link_vars);
@@ -7364,9 +7361,9 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
bp->link_params.req_flow_ctrl = (bp->port.link_config &
PORT_FEATURE_FLOW_CONTROL_MASK);
- if ((bp->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) &&
+ if ((bp->link_params.req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) &&
!(bp->port.supported & SUPPORTED_Autoneg))
- bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE;
+ bp->link_params.req_flow_ctrl = BNX2X_FLOW_CTRL_NONE;
BNX2X_DEV_INFO("req_line_speed %d req_duplex %d req_flow_ctrl 0x%x"
" advertising 0x%x\n",
@@ -8355,13 +8352,13 @@ static void bnx2x_get_pauseparam(struct net_device *dev,
{
struct bnx2x *bp = netdev_priv(dev);
- epause->autoneg = (bp->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) &&
+ epause->autoneg = (bp->link_params.req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) &&
(bp->link_params.req_line_speed == SPEED_AUTO_NEG);
- epause->rx_pause = ((bp->link_vars.flow_ctrl & FLOW_CTRL_RX) ==
- FLOW_CTRL_RX);
- epause->tx_pause = ((bp->link_vars.flow_ctrl & FLOW_CTRL_TX) ==
- FLOW_CTRL_TX);
+ epause->rx_pause = ((bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) ==
+ BNX2X_FLOW_CTRL_RX);
+ epause->tx_pause = ((bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) ==
+ BNX2X_FLOW_CTRL_TX);
DP(NETIF_MSG_LINK, "ethtool_pauseparam: cmd %d\n"
DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n",
@@ -8380,16 +8377,16 @@ static int bnx2x_set_pauseparam(struct net_device *dev,
DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n",
epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause);
- bp->link_params.req_flow_ctrl = FLOW_CTRL_AUTO;
+ bp->link_params.req_flow_ctrl = BNX2X_FLOW_CTRL_AUTO;
if (epause->rx_pause)
- bp->link_params.req_flow_ctrl |= FLOW_CTRL_RX;
+ bp->link_params.req_flow_ctrl |= BNX2X_FLOW_CTRL_RX;
if (epause->tx_pause)
- bp->link_params.req_flow_ctrl |= FLOW_CTRL_TX;
+ bp->link_params.req_flow_ctrl |= BNX2X_FLOW_CTRL_TX;
- if (bp->link_params.req_flow_ctrl == FLOW_CTRL_AUTO)
- bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE;
+ if (bp->link_params.req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO)
+ bp->link_params.req_flow_ctrl = BNX2X_FLOW_CTRL_NONE;
if (epause->autoneg) {
if (!(bp->port.supported & SUPPORTED_Autoneg)) {
@@ -8398,7 +8395,7 @@ static int bnx2x_set_pauseparam(struct net_device *dev,
}
if (bp->link_params.req_line_speed == SPEED_AUTO_NEG)
- bp->link_params.req_flow_ctrl = FLOW_CTRL_AUTO;
+ bp->link_params.req_flow_ctrl = BNX2X_FLOW_CTRL_AUTO;
}
DP(NETIF_MSG_LINK,
@@ -8769,7 +8766,6 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
rc = 0;
test_loopback_rx_exit:
- bp->dev->last_rx = jiffies;
fp->rx_bd_cons = NEXT_RX_IDX(fp->rx_bd_cons);
fp->rx_bd_prod = NEXT_RX_IDX(fp->rx_bd_prod);
@@ -9287,7 +9283,7 @@ static int bnx2x_poll(struct napi_struct *napi, int budget)
#ifdef BNX2X_STOP_ON_ERROR
poll_panic:
#endif
- netif_rx_complete(bp->dev, napi);
+ netif_rx_complete(napi);
bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID,
le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1);
@@ -9853,11 +9849,8 @@ static void bnx2x_set_rx_mode(struct net_device *dev)
mclist && (i < dev->mc_count);
i++, mclist = mclist->next) {
- DP(NETIF_MSG_IFUP, "Adding mcast MAC: "
- "%02x:%02x:%02x:%02x:%02x:%02x\n",
- mclist->dmi_addr[0], mclist->dmi_addr[1],
- mclist->dmi_addr[2], mclist->dmi_addr[3],
- mclist->dmi_addr[4], mclist->dmi_addr[5]);
+ DP(NETIF_MSG_IFUP, "Adding mcast MAC: %pM\n",
+ mclist->dmi_addr);
crc = crc32c_le(0, mclist->dmi_addr, ETH_ALEN);
bit = (crc >> 24) & 0xff;
@@ -10008,6 +10001,25 @@ static void poll_bnx2x(struct net_device *dev)
}
#endif
+static const struct net_device_ops bnx2x_netdev_ops = {
+ .ndo_open = bnx2x_open,
+ .ndo_stop = bnx2x_close,
+ .ndo_start_xmit = bnx2x_start_xmit,
+ .ndo_set_multicast_list = bnx2x_set_rx_mode,
+ .ndo_set_mac_address = bnx2x_change_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = bnx2x_ioctl,
+ .ndo_change_mtu = bnx2x_change_mtu,
+ .ndo_tx_timeout = bnx2x_tx_timeout,
+#ifdef BCM_VLAN
+ .ndo_vlan_rx_register = bnx2x_vlan_rx_register,
+#endif
+#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
+ .ndo_poll_controller = poll_bnx2x,
+#endif
+};
+
+
static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
struct net_device *dev)
{
@@ -10092,8 +10104,7 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
dev->irq = pdev->irq;
- bp->regview = ioremap_nocache(dev->base_addr,
- pci_resource_len(pdev, 0));
+ bp->regview = pci_ioremap_bar(pdev, 0);
if (!bp->regview) {
printk(KERN_ERR PFX "Cannot map register space, aborting\n");
rc = -ENOMEM;
@@ -10119,23 +10130,10 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(bp)*16, 0);
REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(bp)*16, 0);
- dev->hard_start_xmit = bnx2x_start_xmit;
dev->watchdog_timeo = TX_TIMEOUT;
+ dev->netdev_ops = &bnx2x_netdev_ops;
dev->ethtool_ops = &bnx2x_ethtool_ops;
- dev->open = bnx2x_open;
- dev->stop = bnx2x_close;
- dev->set_multicast_list = bnx2x_set_rx_mode;
- dev->set_mac_address = bnx2x_change_mac_addr;
- dev->do_ioctl = bnx2x_ioctl;
- dev->change_mtu = bnx2x_change_mtu;
- dev->tx_timeout = bnx2x_tx_timeout;
-#ifdef BCM_VLAN
- dev->vlan_rx_register = bnx2x_vlan_rx_register;
-#endif
-#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
- dev->poll_controller = poll_bnx2x;
-#endif
dev->features |= NETIF_F_SG;
dev->features |= NETIF_F_HW_CSUM;
if (bp->flags & USING_DAC_FLAG)
@@ -10194,7 +10192,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
struct net_device *dev = NULL;
struct bnx2x *bp;
int rc;
- DECLARE_MAC_BUF(mac);
if (version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -10238,7 +10235,7 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
bnx2x_get_pcie_width(bp),
(bnx2x_get_pcie_speed(bp) == 2) ? "5GHz (Gen2)" : "2.5GHz",
dev->base_addr, bp->pdev->irq);
- printk(KERN_CONT "node addr %s\n", print_mac(mac, dev->dev_addr));
+ printk(KERN_CONT "node addr %pM\n", dev->dev_addr);
return 0;
init_one_exit:
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 5cdae2bc055a..6f9c6faef24c 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -6,3 +6,6 @@ obj-$(CONFIG_BONDING) += bonding.o
bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o
+ipv6-$(subst m,y,$(CONFIG_IPV6)) += bond_ipv6.o
+bonding-objs += $(ipv6-y)
+
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 6106660a4a44..8c2e5ab51f08 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -20,13 +20,12 @@
*
*/
-//#define BONDING_DEBUG 1
-
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
#include <linux/if_bonding.h>
#include <linux/pkt_sched.h>
#include <net/net_namespace.h>
@@ -96,33 +95,7 @@ static struct mac_addr null_mac_addr = {{0, 0, 0, 0, 0, 0}};
static u16 ad_ticks_per_sec;
static const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000;
-// ================= 3AD api to bonding and kernel code ==================
-static u16 __get_link_speed(struct port *port);
-static u8 __get_duplex(struct port *port);
-static inline void __initialize_port_locks(struct port *port);
-//conversions
-static u16 __ad_timer_to_ticks(u16 timer_type, u16 Par);
-
-
-// ================= ad code helper functions ==================
-//needed by ad_rx_machine(...)
-static void __record_pdu(struct lacpdu *lacpdu, struct port *port);
-static void __record_default(struct port *port);
-static void __update_selected(struct lacpdu *lacpdu, struct port *port);
-static void __update_default_selected(struct port *port);
-static void __choose_matched(struct lacpdu *lacpdu, struct port *port);
-static void __update_ntt(struct lacpdu *lacpdu, struct port *port);
-
-//needed for ad_mux_machine(..)
-static void __attach_bond_to_agg(struct port *port);
-static void __detach_bond_from_agg(struct port *port);
-static int __agg_ports_are_ready(struct aggregator *aggregator);
-static void __set_agg_ports_ready(struct aggregator *aggregator, int val);
-
-//needed for ad_agg_selection_logic(...)
-static u32 __get_agg_bandwidth(struct aggregator *aggregator);
-static struct aggregator *__get_active_agg(struct aggregator *aggregator);
-
+static const u8 lacpdu_mcast_addr[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
// ================= main 802.3ad protocol functions ==================
static int ad_lacpdu_send(struct port *port);
@@ -136,7 +109,6 @@ static void ad_agg_selection_logic(struct aggregator *aggregator);
static void ad_clear_agg(struct aggregator *aggregator);
static void ad_initialize_agg(struct aggregator *aggregator);
static void ad_initialize_port(struct port *port, int lacp_fast);
-static void ad_initialize_lacpdu(struct lacpdu *Lacpdu);
static void ad_enable_collecting_distributing(struct port *port);
static void ad_disable_collecting_distributing(struct port *port);
static void ad_marker_info_received(struct bond_marker *marker_info, struct port *port);
@@ -236,6 +208,17 @@ static inline struct aggregator *__get_next_agg(struct aggregator *aggregator)
return &(SLAVE_AD_INFO(slave->next).aggregator);
}
+/*
+ * __agg_has_partner
+ *
+ * Return nonzero if aggregator has a partner (denoted by a non-zero ether
+ * address for the partner). Return 0 if not.
+ */
+static inline int __agg_has_partner(struct aggregator *agg)
+{
+ return !is_zero_ether_addr(agg->partner_system.mac_addr_value);
+}
+
/**
* __disable_port - disable the port's slave
* @port: the port we're looking at
@@ -274,14 +257,14 @@ static inline int __port_is_enabled(struct port *port)
* __get_agg_selection_mode - get the aggregator selection mode
* @port: the port we're looking at
*
- * Get the aggregator selection mode. Can be %BANDWIDTH or %COUNT.
+ * Get the aggregator selection mode. Can be %STABLE, %BANDWIDTH or %COUNT.
*/
static inline u32 __get_agg_selection_mode(struct port *port)
{
struct bonding *bond = __get_bond_by_port(port);
if (bond == NULL) {
- return AD_BANDWIDTH;
+ return BOND_AD_STABLE;
}
return BOND_AD_INFO(bond).agg_select_mode;
@@ -369,7 +352,7 @@ static u16 __get_link_speed(struct port *port)
}
}
- dprintk("Port %d Received link speed %d update from adapter\n", port->actor_port_number, speed);
+ pr_debug("Port %d Received link speed %d update from adapter\n", port->actor_port_number, speed);
return speed;
}
@@ -395,12 +378,12 @@ static u8 __get_duplex(struct port *port)
switch (slave->duplex) {
case DUPLEX_FULL:
retval=0x1;
- dprintk("Port %d Received status full duplex update from adapter\n", port->actor_port_number);
+ pr_debug("Port %d Received status full duplex update from adapter\n", port->actor_port_number);
break;
case DUPLEX_HALF:
default:
retval=0x0;
- dprintk("Port %d Received status NOT full duplex update from adapter\n", port->actor_port_number);
+ pr_debug("Port %d Received status NOT full duplex update from adapter\n", port->actor_port_number);
break;
}
}
@@ -473,33 +456,25 @@ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par)
*/
static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
{
- // validate lacpdu and port
if (lacpdu && port) {
+ struct port_params *partner = &port->partner_oper;
+
// record the new parameter values for the partner operational
- port->partner_oper_port_number = ntohs(lacpdu->actor_port);
- port->partner_oper_port_priority = ntohs(lacpdu->actor_port_priority);
- port->partner_oper_system = lacpdu->actor_system;
- port->partner_oper_system_priority = ntohs(lacpdu->actor_system_priority);
- port->partner_oper_key = ntohs(lacpdu->actor_key);
- // zero partener's lase states
- port->partner_oper_port_state = 0;
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_ACTIVITY);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_TIMEOUT);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_AGGREGATION);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_COLLECTING);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_DISTRIBUTING);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_DEFAULTED);
- port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_EXPIRED);
+ partner->port_number = ntohs(lacpdu->actor_port);
+ partner->port_priority = ntohs(lacpdu->actor_port_priority);
+ partner->system = lacpdu->actor_system;
+ partner->system_priority = ntohs(lacpdu->actor_system_priority);
+ partner->key = ntohs(lacpdu->actor_key);
+ partner->port_state = lacpdu->actor_state;
// set actor_oper_port_state.defaulted to FALSE
port->actor_oper_port_state &= ~AD_STATE_DEFAULTED;
// set the partner sync. to on if the partner is sync. and the port is matched
if ((port->sm_vars & AD_PORT_MATCHED) && (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) {
- port->partner_oper_port_state |= AD_STATE_SYNCHRONIZATION;
+ partner->port_state |= AD_STATE_SYNCHRONIZATION;
} else {
- port->partner_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
+ partner->port_state &= ~AD_STATE_SYNCHRONIZATION;
}
}
}
@@ -514,15 +489,10 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
*/
static void __record_default(struct port *port)
{
- // validate the port
if (port) {
// record the partner admin parameters
- port->partner_oper_port_number = port->partner_admin_port_number;
- port->partner_oper_port_priority = port->partner_admin_port_priority;
- port->partner_oper_system = port->partner_admin_system;
- port->partner_oper_system_priority = port->partner_admin_system_priority;
- port->partner_oper_key = port->partner_admin_key;
- port->partner_oper_port_state = port->partner_admin_port_state;
+ memcpy(&port->partner_oper, &port->partner_admin,
+ sizeof(struct port_params));
// set actor_oper_port_state.defaulted to true
port->actor_oper_port_state |= AD_STATE_DEFAULTED;
@@ -544,16 +514,16 @@ static void __record_default(struct port *port)
*/
static void __update_selected(struct lacpdu *lacpdu, struct port *port)
{
- // validate lacpdu and port
if (lacpdu && port) {
+ const struct port_params *partner = &port->partner_oper;
+
// check if any parameter is different
- if ((ntohs(lacpdu->actor_port) != port->partner_oper_port_number) ||
- (ntohs(lacpdu->actor_port_priority) != port->partner_oper_port_priority) ||
- MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper_system)) ||
- (ntohs(lacpdu->actor_system_priority) != port->partner_oper_system_priority) ||
- (ntohs(lacpdu->actor_key) != port->partner_oper_key) ||
- ((lacpdu->actor_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
- ) {
+ if (ntohs(lacpdu->actor_port) != partner->port_number
+ || ntohs(lacpdu->actor_port_priority) != partner->port_priority
+ || MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system)
+ || ntohs(lacpdu->actor_system_priority) != partner->system_priority
+ || ntohs(lacpdu->actor_key) != partner->key
+ || (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) {
// update the state machine Selected variable
port->sm_vars &= ~AD_PORT_SELECTED;
}
@@ -574,16 +544,18 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port)
*/
static void __update_default_selected(struct port *port)
{
- // validate the port
if (port) {
+ const struct port_params *admin = &port->partner_admin;
+ const struct port_params *oper = &port->partner_oper;
+
// check if any parameter is different
- if ((port->partner_admin_port_number != port->partner_oper_port_number) ||
- (port->partner_admin_port_priority != port->partner_oper_port_priority) ||
- MAC_ADDRESS_COMPARE(&(port->partner_admin_system), &(port->partner_oper_system)) ||
- (port->partner_admin_system_priority != port->partner_oper_system_priority) ||
- (port->partner_admin_key != port->partner_oper_key) ||
- ((port->partner_admin_port_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
- ) {
+ if (admin->port_number != oper->port_number
+ || admin->port_priority != oper->port_priority
+ || MAC_ADDRESS_COMPARE(&admin->system, &oper->system)
+ || admin->system_priority != oper->system_priority
+ || admin->key != oper->key
+ || (admin->port_state & AD_STATE_AGGREGATION)
+ != (oper->port_state & AD_STATE_AGGREGATION)) {
// update the state machine Selected variable
port->sm_vars &= ~AD_PORT_SELECTED;
}
@@ -658,8 +630,8 @@ static void __update_ntt(struct lacpdu *lacpdu, struct port *port)
((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) ||
((lacpdu->partner_state & AD_STATE_AGGREGATION) != (port->actor_oper_port_state & AD_STATE_AGGREGATION))
) {
- // set ntt to be TRUE
- port->ntt = 1;
+
+ port->ntt = true;
}
}
}
@@ -798,6 +770,7 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator)
static inline void __update_lacpdu_from_port(struct port *port)
{
struct lacpdu *lacpdu = &port->lacpdu;
+ const struct port_params *partner = &port->partner_oper;
/* update current actual Actor parameters */
/* lacpdu->subtype initialized
@@ -818,12 +791,12 @@ static inline void __update_lacpdu_from_port(struct port *port)
* lacpdu->partner_information_length initialized
*/
- lacpdu->partner_system_priority = htons(port->partner_oper_system_priority);
- lacpdu->partner_system = port->partner_oper_system;
- lacpdu->partner_key = htons(port->partner_oper_key);
- lacpdu->partner_port_priority = htons(port->partner_oper_port_priority);
- lacpdu->partner_port = htons(port->partner_oper_port_number);
- lacpdu->partner_state = port->partner_oper_port_state;
+ lacpdu->partner_system_priority = htons(partner->system_priority);
+ lacpdu->partner_system = partner->system;
+ lacpdu->partner_key = htons(partner->key);
+ lacpdu->partner_port_priority = htons(partner->port_priority);
+ lacpdu->partner_port = htons(partner->port_number);
+ lacpdu->partner_state = partner->port_state;
/* lacpdu->reserved_3_2 initialized
* lacpdu->tlv_type_collector_info initialized
@@ -853,7 +826,6 @@ static int ad_lacpdu_send(struct port *port)
struct sk_buff *skb;
struct lacpdu_header *lacpdu_header;
int length = sizeof(struct lacpdu_header);
- struct mac_addr lacpdu_multicast_address = AD_MULTICAST_LACPDU_ADDR;
skb = dev_alloc_skb(length);
if (!skb) {
@@ -868,11 +840,11 @@ static int ad_lacpdu_send(struct port *port)
lacpdu_header = (struct lacpdu_header *)skb_put(skb, length);
- lacpdu_header->ad_header.destination_address = lacpdu_multicast_address;
- /* Note: source addres is set to be the member's PERMANENT address, because we use it
- to identify loopback lacpdus in receive. */
- lacpdu_header->ad_header.source_address = *((struct mac_addr *)(slave->perm_hwaddr));
- lacpdu_header->ad_header.length_type = PKT_TYPE_LACPDU;
+ memcpy(lacpdu_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
+ /* Note: source addres is set to be the member's PERMANENT address,
+ because we use it to identify loopback lacpdus in receive. */
+ memcpy(lacpdu_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
+ lacpdu_header->hdr.h_proto = PKT_TYPE_LACPDU;
lacpdu_header->lacpdu = port->lacpdu; // struct copy
@@ -895,7 +867,6 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker)
struct sk_buff *skb;
struct bond_marker_header *marker_header;
int length = sizeof(struct bond_marker_header);
- struct mac_addr lacpdu_multicast_address = AD_MULTICAST_LACPDU_ADDR;
skb = dev_alloc_skb(length + 16);
if (!skb) {
@@ -911,11 +882,11 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker)
marker_header = (struct bond_marker_header *)skb_put(skb, length);
- marker_header->ad_header.destination_address = lacpdu_multicast_address;
- /* Note: source addres is set to be the member's PERMANENT address, because we use it
- to identify loopback MARKERs in receive. */
- marker_header->ad_header.source_address = *((struct mac_addr *)(slave->perm_hwaddr));
- marker_header->ad_header.length_type = PKT_TYPE_LACPDU;
+ memcpy(marker_header->hdr.h_dest, lacpdu_mcast_addr, ETH_ALEN);
+ /* Note: source addres is set to be the member's PERMANENT address,
+ because we use it to identify loopback MARKERs in receive. */
+ memcpy(marker_header->hdr.h_source, slave->perm_hwaddr, ETH_ALEN);
+ marker_header->hdr.h_proto = PKT_TYPE_LACPDU;
marker_header->marker = *marker; // struct copy
@@ -972,7 +943,7 @@ static void ad_mux_machine(struct port *port)
break;
case AD_MUX_ATTACHED:
// check also if agg_select_timer expired(so the edable port will take place only after this timer)
- if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper_port_state & AD_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) {
+ if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) {
port->sm_mux_state = AD_MUX_COLLECTING_DISTRIBUTING;// next state
} else if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) { // if UNSELECTED or STANDBY
port->sm_vars &= ~AD_PORT_READY_N;
@@ -984,7 +955,7 @@ static void ad_mux_machine(struct port *port)
break;
case AD_MUX_COLLECTING_DISTRIBUTING:
if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) ||
- !(port->partner_oper_port_state & AD_STATE_SYNCHRONIZATION)
+ !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION)
) {
port->sm_mux_state = AD_MUX_ATTACHED;// next state
@@ -1007,7 +978,7 @@ static void ad_mux_machine(struct port *port)
// check if the state machine was changed
if (port->sm_mux_state != last_state) {
- dprintk("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_mux_state);
+ pr_debug("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_mux_state);
switch (port->sm_mux_state) {
case AD_MUX_DETACHED:
__detach_bond_from_agg(port);
@@ -1015,7 +986,7 @@ static void ad_mux_machine(struct port *port)
ad_disable_collecting_distributing(port);
port->actor_oper_port_state &= ~AD_STATE_COLLECTING;
port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING;
- port->ntt = 1;
+ port->ntt = true;
break;
case AD_MUX_WAITING:
port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0);
@@ -1026,13 +997,13 @@ static void ad_mux_machine(struct port *port)
port->actor_oper_port_state &= ~AD_STATE_COLLECTING;
port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING;
ad_disable_collecting_distributing(port);
- port->ntt = 1;
+ port->ntt = true;
break;
case AD_MUX_COLLECTING_DISTRIBUTING:
port->actor_oper_port_state |= AD_STATE_COLLECTING;
port->actor_oper_port_state |= AD_STATE_DISTRIBUTING;
ad_enable_collecting_distributing(port);
- port->ntt = 1;
+ port->ntt = true;
break;
default: //to silence the compiler
break;
@@ -1106,7 +1077,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
// check if the State machine was changed or new lacpdu arrived
if ((port->sm_rx_state != last_state) || (lacpdu)) {
- dprintk("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_rx_state);
+ pr_debug("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_rx_state);
switch (port->sm_rx_state) {
case AD_RX_INITIALIZE:
if (!(port->actor_oper_port_key & AD_DUPLEX_KEY_BITS)) {
@@ -1128,7 +1099,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
case AD_RX_LACP_DISABLED:
port->sm_vars &= ~AD_PORT_SELECTED;
__record_default(port);
- port->partner_oper_port_state &= ~AD_STATE_AGGREGATION;
+ port->partner_oper.port_state &= ~AD_STATE_AGGREGATION;
port->sm_vars |= AD_PORT_MATCHED;
port->actor_oper_port_state &= ~AD_STATE_EXPIRED;
break;
@@ -1136,9 +1107,9 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
//Reset of the Synchronization flag. (Standard 43.4.12)
//This reset cause to disable this port in the COLLECTING_DISTRIBUTING state of the
//mux machine in case of EXPIRED even if LINK_DOWN didn't arrive for the port.
- port->partner_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
+ port->partner_oper.port_state &= ~AD_STATE_SYNCHRONIZATION;
port->sm_vars &= ~AD_PORT_MATCHED;
- port->partner_oper_port_state |= AD_SHORT_TIMEOUT;
+ port->partner_oper.port_state |= AD_SHORT_TIMEOUT;
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(AD_SHORT_TIMEOUT));
port->actor_oper_port_state |= AD_STATE_EXPIRED;
break;
@@ -1191,11 +1162,13 @@ static void ad_tx_machine(struct port *port)
// check if there is something to send
if (port->ntt && (port->sm_vars & AD_PORT_LACP_ENABLED)) {
__update_lacpdu_from_port(port);
- // send the lacpdu
+
if (ad_lacpdu_send(port) >= 0) {
- dprintk("Sent LACPDU on port %d\n", port->actor_port_number);
- // mark ntt as false, so it will not be sent again until demanded
- port->ntt = 0;
+ pr_debug("Sent LACPDU on port %d\n", port->actor_port_number);
+
+ /* mark ntt as false, so it will not be sent again until
+ demanded */
+ port->ntt = false;
}
}
// restart tx timer(to verify that we will not exceed AD_MAX_TX_IN_SECOND
@@ -1218,7 +1191,7 @@ static void ad_periodic_machine(struct port *port)
// check if port was reinitialized
if (((port->sm_vars & AD_PORT_BEGIN) || !(port->sm_vars & AD_PORT_LACP_ENABLED) || !port->is_enabled) ||
- (!(port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY) && !(port->partner_oper_port_state & AD_STATE_LACP_ACTIVITY))
+ (!(port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY) && !(port->partner_oper.port_state & AD_STATE_LACP_ACTIVITY))
) {
port->sm_periodic_state = AD_NO_PERIODIC; // next state
}
@@ -1232,12 +1205,12 @@ static void ad_periodic_machine(struct port *port)
// If not expired, check if there is some new timeout parameter from the partner state
switch (port->sm_periodic_state) {
case AD_FAST_PERIODIC:
- if (!(port->partner_oper_port_state & AD_STATE_LACP_TIMEOUT)) {
+ if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) {
port->sm_periodic_state = AD_SLOW_PERIODIC; // next state
}
break;
case AD_SLOW_PERIODIC:
- if ((port->partner_oper_port_state & AD_STATE_LACP_TIMEOUT)) {
+ if ((port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) {
// stop current timer
port->sm_periodic_timer_counter = 0;
port->sm_periodic_state = AD_PERIODIC_TX; // next state
@@ -1253,7 +1226,7 @@ static void ad_periodic_machine(struct port *port)
port->sm_periodic_state = AD_FAST_PERIODIC; // next state
break;
case AD_PERIODIC_TX:
- if (!(port->partner_oper_port_state & AD_STATE_LACP_TIMEOUT)) {
+ if (!(port->partner_oper.port_state & AD_STATE_LACP_TIMEOUT)) {
port->sm_periodic_state = AD_SLOW_PERIODIC; // next state
} else {
port->sm_periodic_state = AD_FAST_PERIODIC; // next state
@@ -1266,7 +1239,7 @@ static void ad_periodic_machine(struct port *port)
// check if the state machine was changed
if (port->sm_periodic_state != last_state) {
- dprintk("Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_periodic_state);
+ pr_debug("Periodic Machine: Port=%d, Last State=%d, Curr State=%d\n", port->actor_port_number, last_state, port->sm_periodic_state);
switch (port->sm_periodic_state) {
case AD_NO_PERIODIC:
port->sm_periodic_timer_counter = 0; // zero timer
@@ -1278,7 +1251,7 @@ static void ad_periodic_machine(struct port *port)
port->sm_periodic_timer_counter = __ad_timer_to_ticks(AD_PERIODIC_TIMER, (u16)(AD_SLOW_PERIODIC_TIME))-1; // decrement 1 tick we lost in the PERIODIC_TX cycle
break;
case AD_PERIODIC_TX:
- port->ntt = 1;
+ port->ntt = true;
break;
default: //to silence the compiler
break;
@@ -1323,7 +1296,7 @@ static void ad_port_selection_logic(struct port *port)
port->next_port_in_aggregator=NULL;
port->actor_port_aggregator_identifier=0;
- dprintk("Port %d left LAG %d\n", port->actor_port_number, temp_aggregator->aggregator_identifier);
+ pr_debug("Port %d left LAG %d\n", port->actor_port_number, temp_aggregator->aggregator_identifier);
// if the aggregator is empty, clear its parameters, and set it ready to be attached
if (!temp_aggregator->lag_ports) {
ad_clear_agg(temp_aggregator);
@@ -1352,11 +1325,11 @@ static void ad_port_selection_logic(struct port *port)
}
// check if current aggregator suits us
if (((aggregator->actor_oper_aggregator_key == port->actor_oper_port_key) && // if all parameters match AND
- !MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(port->partner_oper_system)) &&
- (aggregator->partner_system_priority == port->partner_oper_system_priority) &&
- (aggregator->partner_oper_aggregator_key == port->partner_oper_key)
+ !MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(port->partner_oper.system)) &&
+ (aggregator->partner_system_priority == port->partner_oper.system_priority) &&
+ (aggregator->partner_oper_aggregator_key == port->partner_oper.key)
) &&
- ((MAC_ADDRESS_COMPARE(&(port->partner_oper_system), &(null_mac_addr)) && // partner answers
+ ((MAC_ADDRESS_COMPARE(&(port->partner_oper.system), &(null_mac_addr)) && // partner answers
!aggregator->is_individual) // but is not individual OR
)
) {
@@ -1366,7 +1339,7 @@ static void ad_port_selection_logic(struct port *port)
port->next_port_in_aggregator=aggregator->lag_ports;
port->aggregator->num_of_ports++;
aggregator->lag_ports=port;
- dprintk("Port %d joined LAG %d(existing LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+ pr_debug("Port %d joined LAG %d(existing LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
// mark this port as selected
port->sm_vars |= AD_PORT_SELECTED;
@@ -1385,16 +1358,16 @@ static void ad_port_selection_logic(struct port *port)
// update the new aggregator's parameters
// if port was responsed from the end-user
if (port->actor_oper_port_key & AD_DUPLEX_KEY_BITS) {// if port is full duplex
- port->aggregator->is_individual = 0;
+ port->aggregator->is_individual = false;
} else {
- port->aggregator->is_individual = 1;
+ port->aggregator->is_individual = true;
}
port->aggregator->actor_admin_aggregator_key = port->actor_admin_port_key;
port->aggregator->actor_oper_aggregator_key = port->actor_oper_port_key;
- port->aggregator->partner_system=port->partner_oper_system;
- port->aggregator->partner_system_priority = port->partner_oper_system_priority;
- port->aggregator->partner_oper_aggregator_key = port->partner_oper_key;
+ port->aggregator->partner_system=port->partner_oper.system;
+ port->aggregator->partner_system_priority = port->partner_oper.system_priority;
+ port->aggregator->partner_oper_aggregator_key = port->partner_oper.key;
port->aggregator->receive_state = 1;
port->aggregator->transmit_state = 1;
port->aggregator->lag_ports = port;
@@ -1403,7 +1376,7 @@ static void ad_port_selection_logic(struct port *port)
// mark this port as selected
port->sm_vars |= AD_PORT_SELECTED;
- dprintk("Port %d joined LAG %d(new LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+ pr_debug("Port %d joined LAG %d(new LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
} else {
printk(KERN_ERR DRV_NAME ": %s: Port %d (on %s) did not find a suitable aggregator\n",
port->slave->dev->master->name,
@@ -1414,9 +1387,82 @@ static void ad_port_selection_logic(struct port *port)
// else set ready=FALSE in all aggregator's ports
__set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator));
- if (!__check_agg_selection_timer(port) && (aggregator = __get_first_agg(port))) {
- ad_agg_selection_logic(aggregator);
+ aggregator = __get_first_agg(port);
+ ad_agg_selection_logic(aggregator);
+}
+
+/*
+ * Decide if "agg" is a better choice for the new active aggregator that
+ * the current best, according to the ad_select policy.
+ */
+static struct aggregator *ad_agg_selection_test(struct aggregator *best,
+ struct aggregator *curr)
+{
+ /*
+ * 0. If no best, select current.
+ *
+ * 1. If the current agg is not individual, and the best is
+ * individual, select current.
+ *
+ * 2. If current agg is individual and the best is not, keep best.
+ *
+ * 3. Therefore, current and best are both individual or both not
+ * individual, so:
+ *
+ * 3a. If current agg partner replied, and best agg partner did not,
+ * select current.
+ *
+ * 3b. If current agg partner did not reply and best agg partner
+ * did reply, keep best.
+ *
+ * 4. Therefore, current and best both have partner replies or
+ * both do not, so perform selection policy:
+ *
+ * BOND_AD_COUNT: Select by count of ports. If count is equal,
+ * select by bandwidth.
+ *
+ * BOND_AD_STABLE, BOND_AD_BANDWIDTH: Select by bandwidth.
+ */
+ if (!best)
+ return curr;
+
+ if (!curr->is_individual && best->is_individual)
+ return curr;
+
+ if (curr->is_individual && !best->is_individual)
+ return best;
+
+ if (__agg_has_partner(curr) && !__agg_has_partner(best))
+ return curr;
+
+ if (!__agg_has_partner(curr) && __agg_has_partner(best))
+ return best;
+
+ switch (__get_agg_selection_mode(curr->lag_ports)) {
+ case BOND_AD_COUNT:
+ if (curr->num_of_ports > best->num_of_ports)
+ return curr;
+
+ if (curr->num_of_ports < best->num_of_ports)
+ return best;
+
+ /*FALLTHROUGH*/
+ case BOND_AD_STABLE:
+ case BOND_AD_BANDWIDTH:
+ if (__get_agg_bandwidth(curr) > __get_agg_bandwidth(best))
+ return curr;
+
+ break;
+
+ default:
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Impossible agg select mode %d\n",
+ curr->slave->dev->master->name,
+ __get_agg_selection_mode(curr->lag_ports));
+ break;
}
+
+ return best;
}
/**
@@ -1424,156 +1470,138 @@ static void ad_port_selection_logic(struct port *port)
* @aggregator: the aggregator we're looking at
*
* It is assumed that only one aggregator may be selected for a team.
- * The logic of this function is to select (at first time) the aggregator with
- * the most ports attached to it, and to reselect the active aggregator only if
- * the previous aggregator has no more ports related to it.
+ *
+ * The logic of this function is to select the aggregator according to
+ * the ad_select policy:
+ *
+ * BOND_AD_STABLE: select the aggregator with the most ports attached to
+ * it, and to reselect the active aggregator only if the previous
+ * aggregator has no more ports related to it.
+ *
+ * BOND_AD_BANDWIDTH: select the aggregator with the highest total
+ * bandwidth, and reselect whenever a link state change takes place or the
+ * set of slaves in the bond changes.
+ *
+ * BOND_AD_COUNT: select the aggregator with largest number of ports
+ * (slaves), and reselect whenever a link state change takes place or the
+ * set of slaves in the bond changes.
*
* FIXME: this function MUST be called with the first agg in the bond, or
* __get_active_agg() won't work correctly. This function should be better
* called with the bond itself, and retrieve the first agg from it.
*/
-static void ad_agg_selection_logic(struct aggregator *aggregator)
+static void ad_agg_selection_logic(struct aggregator *agg)
{
- struct aggregator *best_aggregator = NULL, *active_aggregator = NULL;
- struct aggregator *last_active_aggregator = NULL, *origin_aggregator;
+ struct aggregator *best, *active, *origin;
struct port *port;
- u16 num_of_aggs=0;
- origin_aggregator = aggregator;
+ origin = agg;
- //get current active aggregator
- last_active_aggregator = __get_active_agg(aggregator);
+ active = __get_active_agg(agg);
+ best = active;
- // search for the aggregator with the most ports attached to it.
do {
- // count how many candidate lag's we have
- if (aggregator->lag_ports) {
- num_of_aggs++;
- }
- if (aggregator->is_active && !aggregator->is_individual && // if current aggregator is the active aggregator
- MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr))) { // and partner answers to 802.3ad PDUs
- if (aggregator->num_of_ports) { // if any ports attached to the current aggregator
- best_aggregator=NULL; // disregard the best aggregator that was chosen by now
- break; // stop the selection of other aggregator if there are any ports attached to this active aggregator
- } else { // no ports attached to this active aggregator
- aggregator->is_active = 0; // mark this aggregator as not active anymore
- }
- }
- if (aggregator->num_of_ports) { // if any ports attached
- if (best_aggregator) { // if there is a candidte aggregator
- //The reasons for choosing new best aggregator:
- // 1. if current agg is NOT individual and the best agg chosen so far is individual OR
- // current and best aggs are both individual or both not individual, AND
- // 2a. current agg partner reply but best agg partner do not reply OR
- // 2b. current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply AND
- // current has more ports/bandwidth, or same amount of ports but current has faster ports, THEN
- // current agg become best agg so far
-
- //if current agg is NOT individual and the best agg chosen so far is individual change best_aggregator
- if (!aggregator->is_individual && best_aggregator->is_individual) {
- best_aggregator=aggregator;
- }
- // current and best aggs are both individual or both not individual
- else if ((aggregator->is_individual && best_aggregator->is_individual) ||
- (!aggregator->is_individual && !best_aggregator->is_individual)) {
- // current and best aggs are both individual or both not individual AND
- // current agg partner reply but best agg partner do not reply
- if ((MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
- !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
- best_aggregator=aggregator;
- }
- // current agg partner reply OR current agg partner do not reply AND best agg partner also do not reply
- else if (! (!MAC_ADDRESS_COMPARE(&(aggregator->partner_system), &(null_mac_addr)) &&
- MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) {
- if ((__get_agg_selection_mode(aggregator->lag_ports) == AD_BANDWIDTH)&&
- (__get_agg_bandwidth(aggregator) > __get_agg_bandwidth(best_aggregator))) {
- best_aggregator=aggregator;
- } else if (__get_agg_selection_mode(aggregator->lag_ports) == AD_COUNT) {
- if (((aggregator->num_of_ports > best_aggregator->num_of_ports) &&
- (aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS))||
- ((aggregator->num_of_ports == best_aggregator->num_of_ports) &&
- ((u16)(aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS) >
- (u16)(best_aggregator->actor_oper_aggregator_key & AD_SPEED_KEY_BITS)))) {
- best_aggregator=aggregator;
- }
- }
- }
- }
- } else {
- best_aggregator=aggregator;
+ agg->is_active = 0;
+
+ if (agg->num_of_ports)
+ best = ad_agg_selection_test(best, agg);
+
+ } while ((agg = __get_next_agg(agg)));
+
+ if (best &&
+ __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) {
+ /*
+ * For the STABLE policy, don't replace the old active
+ * aggregator if it's still active (it has an answering
+ * partner) or if both the best and active don't have an
+ * answering partner.
+ */
+ if (active && active->lag_ports &&
+ active->lag_ports->is_enabled &&
+ (__agg_has_partner(active) ||
+ (!__agg_has_partner(active) && !__agg_has_partner(best)))) {
+ if (!(!active->actor_oper_aggregator_key &&
+ best->actor_oper_aggregator_key)) {
+ best = NULL;
+ active->is_active = 1;
}
}
- aggregator->is_active = 0; // mark all aggregators as not active anymore
- } while ((aggregator = __get_next_agg(aggregator)));
-
- // if we have new aggregator selected, don't replace the old aggregator if it has an answering partner,
- // or if both old aggregator and new aggregator don't have answering partner
- if (best_aggregator) {
- if (last_active_aggregator && last_active_aggregator->lag_ports && last_active_aggregator->lag_ports->is_enabled &&
- (MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) || // partner answers OR
- (!MAC_ADDRESS_COMPARE(&(last_active_aggregator->partner_system), &(null_mac_addr)) && // both old and new
- !MAC_ADDRESS_COMPARE(&(best_aggregator->partner_system), &(null_mac_addr)))) // partner do not answer
- ) {
- // if new aggregator has link, and old aggregator does not, replace old aggregator.(do nothing)
- // -> don't replace otherwise.
- if (!(!last_active_aggregator->actor_oper_aggregator_key && best_aggregator->actor_oper_aggregator_key)) {
- best_aggregator=NULL;
- last_active_aggregator->is_active = 1; // don't replace good old aggregator
+ }
- }
- }
+ if (best && (best == active)) {
+ best = NULL;
+ active->is_active = 1;
}
// if there is new best aggregator, activate it
- if (best_aggregator) {
- for (aggregator = __get_first_agg(best_aggregator->lag_ports);
- aggregator;
- aggregator = __get_next_agg(aggregator)) {
-
- dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
- aggregator->aggregator_identifier, aggregator->num_of_ports,
- aggregator->actor_oper_aggregator_key, aggregator->partner_oper_aggregator_key,
- aggregator->is_individual, aggregator->is_active);
+ if (best) {
+ pr_debug("best Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
+ best->aggregator_identifier, best->num_of_ports,
+ best->actor_oper_aggregator_key,
+ best->partner_oper_aggregator_key,
+ best->is_individual, best->is_active);
+ pr_debug("best ports %p slave %p %s\n",
+ best->lag_ports, best->slave,
+ best->slave ? best->slave->dev->name : "NULL");
+
+ for (agg = __get_first_agg(best->lag_ports); agg;
+ agg = __get_next_agg(agg)) {
+
+ pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
+ agg->aggregator_identifier, agg->num_of_ports,
+ agg->actor_oper_aggregator_key,
+ agg->partner_oper_aggregator_key,
+ agg->is_individual, agg->is_active);
}
// check if any partner replys
- if (best_aggregator->is_individual) {
- printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad response from "
- "the link partner for any adapters in the bond\n",
- best_aggregator->slave->dev->master->name);
+ if (best->is_individual) {
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad"
+ " response from the link partner for any"
+ " adapters in the bond\n",
+ best->slave->dev->master->name);
}
- // check if there are more than one aggregator
- if (num_of_aggs > 1) {
- dprintk("Warning: More than one Link Aggregation Group was "
- "found in the bond. Only one group will function in the bond\n");
- }
-
- best_aggregator->is_active = 1;
- dprintk("LAG %d choosed as the active LAG\n", best_aggregator->aggregator_identifier);
- dprintk("Agg=%d; Ports=%d; a key=%d; p key=%d; Indiv=%d; Active=%d\n",
- best_aggregator->aggregator_identifier, best_aggregator->num_of_ports,
- best_aggregator->actor_oper_aggregator_key, best_aggregator->partner_oper_aggregator_key,
- best_aggregator->is_individual, best_aggregator->is_active);
+ best->is_active = 1;
+ pr_debug("LAG %d chosen as the active LAG\n",
+ best->aggregator_identifier);
+ pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
+ best->aggregator_identifier, best->num_of_ports,
+ best->actor_oper_aggregator_key,
+ best->partner_oper_aggregator_key,
+ best->is_individual, best->is_active);
// disable the ports that were related to the former active_aggregator
- if (last_active_aggregator) {
- for (port=last_active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+ if (active) {
+ for (port = active->lag_ports; port;
+ port = port->next_port_in_aggregator) {
__disable_port(port);
}
}
}
- // if the selected aggregator is of join individuals(partner_system is NULL), enable their ports
- active_aggregator = __get_active_agg(origin_aggregator);
+ /*
+ * if the selected aggregator is of join individuals
+ * (partner_system is NULL), enable their ports
+ */
+ active = __get_active_agg(origin);
- if (active_aggregator) {
- if (!MAC_ADDRESS_COMPARE(&(active_aggregator->partner_system), &(null_mac_addr))) {
- for (port=active_aggregator->lag_ports; port; port=port->next_port_in_aggregator) {
+ if (active) {
+ if (!__agg_has_partner(active)) {
+ for (port = active->lag_ports; port;
+ port = port->next_port_in_aggregator) {
__enable_port(port);
}
}
}
+
+ if (origin->slave) {
+ struct bonding *bond;
+
+ bond = bond_get_bond_by_slave(origin->slave);
+ if (bond)
+ bond_3ad_set_carrier(bond);
+ }
}
/**
@@ -1584,7 +1612,7 @@ static void ad_agg_selection_logic(struct aggregator *aggregator)
static void ad_clear_agg(struct aggregator *aggregator)
{
if (aggregator) {
- aggregator->is_individual = 0;
+ aggregator->is_individual = false;
aggregator->actor_admin_aggregator_key = 0;
aggregator->actor_oper_aggregator_key = 0;
aggregator->partner_system = null_mac_addr;
@@ -1595,7 +1623,7 @@ static void ad_clear_agg(struct aggregator *aggregator)
aggregator->lag_ports = NULL;
aggregator->is_active = 0;
aggregator->num_of_ports = 0;
- dprintk("LAG %d was cleared\n", aggregator->aggregator_identifier);
+ pr_debug("LAG %d was cleared\n", aggregator->aggregator_identifier);
}
}
@@ -1623,13 +1651,32 @@ static void ad_initialize_agg(struct aggregator *aggregator)
*/
static void ad_initialize_port(struct port *port, int lacp_fast)
{
+ static const struct port_params tmpl = {
+ .system_priority = 0xffff,
+ .key = 1,
+ .port_number = 1,
+ .port_priority = 0xff,
+ .port_state = 1,
+ };
+ static const struct lacpdu lacpdu = {
+ .subtype = 0x01,
+ .version_number = 0x01,
+ .tlv_type_actor_info = 0x01,
+ .actor_information_length = 0x14,
+ .tlv_type_partner_info = 0x02,
+ .partner_information_length = 0x14,
+ .tlv_type_collector_info = 0x03,
+ .collector_information_length = 0x10,
+ .collector_max_delay = htons(AD_COLLECTOR_MAX_DELAY),
+ };
+
if (port) {
port->actor_port_number = 1;
port->actor_port_priority = 0xff;
port->actor_system = null_mac_addr;
port->actor_system_priority = 0xffff;
port->actor_port_aggregator_identifier = 0;
- port->ntt = 0;
+ port->ntt = false;
port->actor_admin_port_key = 1;
port->actor_oper_port_key = 1;
port->actor_admin_port_state = AD_STATE_AGGREGATION | AD_STATE_LACP_ACTIVITY;
@@ -1639,19 +1686,10 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
}
- port->partner_admin_system = null_mac_addr;
- port->partner_oper_system = null_mac_addr;
- port->partner_admin_system_priority = 0xffff;
- port->partner_oper_system_priority = 0xffff;
- port->partner_admin_key = 1;
- port->partner_oper_key = 1;
- port->partner_admin_port_number = 1;
- port->partner_oper_port_number = 1;
- port->partner_admin_port_priority = 0xff;
- port->partner_oper_port_priority = 0xff;
- port->partner_admin_port_state = 1;
- port->partner_oper_port_state = 1;
- port->is_enabled = 1;
+ memcpy(&port->partner_admin, &tmpl, sizeof(tmpl));
+ memcpy(&port->partner_oper, &tmpl, sizeof(tmpl));
+
+ port->is_enabled = true;
// ****** private parameters ******
port->sm_vars = 0x3;
port->sm_rx_state = 0;
@@ -1667,7 +1705,7 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
port->next_port_in_aggregator = NULL;
port->transaction_id = 0;
- ad_initialize_lacpdu(&(port->lacpdu));
+ memcpy(&port->lacpdu, &lacpdu, sizeof(lacpdu));
}
}
@@ -1680,7 +1718,7 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
static void ad_enable_collecting_distributing(struct port *port)
{
if (port->aggregator->is_active) {
- dprintk("Enabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+ pr_debug("Enabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
__enable_port(port);
}
}
@@ -1693,7 +1731,7 @@ static void ad_enable_collecting_distributing(struct port *port)
static void ad_disable_collecting_distributing(struct port *port)
{
if (port->aggregator && MAC_ADDRESS_COMPARE(&(port->aggregator->partner_system), &(null_mac_addr))) {
- dprintk("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
+ pr_debug("Disabling port %d(LAG %d)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
__disable_port(port);
}
}
@@ -1731,7 +1769,7 @@ static void ad_marker_info_send(struct port *port)
// send the marker information
if (ad_marker_send(port, &marker) >= 0) {
- dprintk("Sent Marker Information on port %d\n", port->actor_port_number);
+ pr_debug("Sent Marker Information on port %d\n", port->actor_port_number);
}
}
#endif
@@ -1755,7 +1793,7 @@ static void ad_marker_info_received(struct bond_marker *marker_info,
// send the marker response
if (ad_marker_send(port, &marker) >= 0) {
- dprintk("Sent Marker Response on port %d\n", port->actor_port_number);
+ pr_debug("Sent Marker Response on port %d\n", port->actor_port_number);
}
}
@@ -1776,53 +1814,6 @@ static void ad_marker_response_received(struct bond_marker *marker,
// DO NOTHING, SINCE WE DECIDED NOT TO IMPLEMENT THIS FEATURE FOR NOW
}
-/**
- * ad_initialize_lacpdu - initialize a given lacpdu structure
- * @lacpdu: lacpdu structure to initialize
- *
- */
-static void ad_initialize_lacpdu(struct lacpdu *lacpdu)
-{
- u16 index;
-
- // initialize lacpdu data
- lacpdu->subtype = 0x01;
- lacpdu->version_number = 0x01;
- lacpdu->tlv_type_actor_info = 0x01;
- lacpdu->actor_information_length = 0x14;
- // lacpdu->actor_system_priority updated on send
- // lacpdu->actor_system updated on send
- // lacpdu->actor_key updated on send
- // lacpdu->actor_port_priority updated on send
- // lacpdu->actor_port updated on send
- // lacpdu->actor_state updated on send
- lacpdu->tlv_type_partner_info = 0x02;
- lacpdu->partner_information_length = 0x14;
- for (index=0; index<=2; index++) {
- lacpdu->reserved_3_1[index]=0;
- }
- // lacpdu->partner_system_priority updated on send
- // lacpdu->partner_system updated on send
- // lacpdu->partner_key updated on send
- // lacpdu->partner_port_priority updated on send
- // lacpdu->partner_port updated on send
- // lacpdu->partner_state updated on send
- for (index=0; index<=2; index++) {
- lacpdu->reserved_3_2[index]=0;
- }
- lacpdu->tlv_type_collector_info = 0x03;
- lacpdu->collector_information_length= 0x10;
- lacpdu->collector_max_delay = htons(AD_COLLECTOR_MAX_DELAY);
- for (index=0; index<=11; index++) {
- lacpdu->reserved_12[index]=0;
- }
- lacpdu->tlv_type_terminator = 0x00;
- lacpdu->terminator_length = 0;
- for (index=0; index<=49; index++) {
- lacpdu->reserved_50[index]=0;
- }
-}
-
//////////////////////////////////////////////////////////////////////////////////////
// ================= AD exported functions to the main bonding code ==================
//////////////////////////////////////////////////////////////////////////////////////
@@ -1830,6 +1821,19 @@ static void ad_initialize_lacpdu(struct lacpdu *lacpdu)
// Check aggregators status in team every T seconds
#define AD_AGGREGATOR_SELECTION_TIMER 8
+/*
+ * bond_3ad_initiate_agg_selection(struct bonding *bond)
+ *
+ * Set the aggregation selection timer, to initiate an agg selection in
+ * the very near future. Called during first initialization, and during
+ * any down to up transitions of the bond.
+ */
+void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout)
+{
+ BOND_AD_INFO(bond).agg_select_timer = timeout;
+ BOND_AD_INFO(bond).agg_select_mode = bond->params.ad_select;
+}
+
static u16 aggregator_identifier;
/**
@@ -1854,9 +1858,9 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
// initialize how many times this module is called in one second(should be about every 100ms)
ad_ticks_per_sec = tick_resolution;
- // initialize the aggregator selection timer(to activate an aggregation selection after initialize)
- BOND_AD_INFO(bond).agg_select_timer = (AD_AGGREGATOR_SELECTION_TIMER * ad_ticks_per_sec);
- BOND_AD_INFO(bond).agg_select_mode = AD_BANDWIDTH;
+ bond_3ad_initiate_agg_selection(bond,
+ AD_AGGREGATOR_SELECTION_TIMER *
+ ad_ticks_per_sec);
}
}
@@ -1956,7 +1960,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
return;
}
- dprintk("Unbinding Link Aggregation Group %d\n", aggregator->aggregator_identifier);
+ pr_debug("Unbinding Link Aggregation Group %d\n", aggregator->aggregator_identifier);
/* Tell the partner that this port is not suitable for aggregation */
port->actor_oper_port_state &= ~AD_STATE_AGGREGATION;
@@ -1980,7 +1984,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
// if new aggregator found, copy the aggregator's parameters
// and connect the related lag_ports to the new aggregator
if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) {
- dprintk("Some port(s) related to LAG %d - replaceing with LAG %d\n", aggregator->aggregator_identifier, new_aggregator->aggregator_identifier);
+ pr_debug("Some port(s) related to LAG %d - replaceing with LAG %d\n", aggregator->aggregator_identifier, new_aggregator->aggregator_identifier);
if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) {
printk(KERN_INFO DRV_NAME ": %s: Removing an active aggregator\n",
@@ -2031,7 +2035,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
}
}
- dprintk("Unbinding port %d\n", port->actor_port_number);
+ pr_debug("Unbinding port %d\n", port->actor_port_number);
// find the aggregator that this port is connected to
temp_aggregator = __get_first_agg(port);
for (; temp_aggregator; temp_aggregator = __get_next_agg(temp_aggregator)) {
@@ -2162,7 +2166,7 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
switch (lacpdu->subtype) {
case AD_TYPE_LACPDU:
- dprintk("Received LACPDU on port %d\n", port->actor_port_number);
+ pr_debug("Received LACPDU on port %d\n", port->actor_port_number);
ad_rx_machine(lacpdu, port);
break;
@@ -2171,17 +2175,17 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
switch (((struct bond_marker *)lacpdu)->tlv_type) {
case AD_MARKER_INFORMATION_SUBTYPE:
- dprintk("Received Marker Information on port %d\n", port->actor_port_number);
+ pr_debug("Received Marker Information on port %d\n", port->actor_port_number);
ad_marker_info_received((struct bond_marker *)lacpdu, port);
break;
case AD_MARKER_RESPONSE_SUBTYPE:
- dprintk("Received Marker Response on port %d\n", port->actor_port_number);
+ pr_debug("Received Marker Response on port %d\n", port->actor_port_number);
ad_marker_response_received((struct bond_marker *)lacpdu, port);
break;
default:
- dprintk("Received an unknown Marker subtype on slot %d\n", port->actor_port_number);
+ pr_debug("Received an unknown Marker subtype on slot %d\n", port->actor_port_number);
}
}
}
@@ -2209,7 +2213,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS;
port->actor_oper_port_key=port->actor_admin_port_key |= (__get_link_speed(port) << 1);
- dprintk("Port %d changed speed\n", port->actor_port_number);
+ pr_debug("Port %d changed speed\n", port->actor_port_number);
// there is no need to reselect a new aggregator, just signal the
// state machines to reinitialize
port->sm_vars |= AD_PORT_BEGIN;
@@ -2237,7 +2241,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS;
port->actor_oper_port_key=port->actor_admin_port_key |= __get_duplex(port);
- dprintk("Port %d changed duplex\n", port->actor_port_number);
+ pr_debug("Port %d changed duplex\n", port->actor_port_number);
// there is no need to reselect a new aggregator, just signal the
// state machines to reinitialize
port->sm_vars |= AD_PORT_BEGIN;
@@ -2267,14 +2271,14 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
// on link down we are zeroing duplex and speed since some of the adaptors(ce1000.lan) report full duplex/speed instead of N/A(duplex) / 0(speed)
// on link up we are forcing recheck on the duplex and speed since some of he adaptors(ce1000.lan) report
if (link == BOND_LINK_UP) {
- port->is_enabled = 1;
+ port->is_enabled = true;
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS;
port->actor_oper_port_key=port->actor_admin_port_key |= __get_duplex(port);
port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS;
port->actor_oper_port_key=port->actor_admin_port_key |= (__get_link_speed(port) << 1);
} else {
/* link has failed */
- port->is_enabled = 0;
+ port->is_enabled = false;
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS;
port->actor_oper_port_key= (port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS);
}
@@ -2346,7 +2350,7 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
{
struct slave *slave, *start_at;
- struct bonding *bond = dev->priv;
+ struct bonding *bond = netdev_priv(dev);
int slave_agg_no;
int slaves_in_agg;
int agg_id;
@@ -2426,7 +2430,7 @@ out:
int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev)
{
- struct bonding *bond = dev->priv;
+ struct bonding *bond = netdev_priv(dev);
struct slave *slave = NULL;
int ret = NET_RX_DROP;
@@ -2437,7 +2441,8 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac
goto out;
read_lock(&bond->lock);
- slave = bond_get_slave_by_dev((struct bonding *)dev->priv, orig_dev);
+ slave = bond_get_slave_by_dev((struct bonding *)netdev_priv(dev),
+ orig_dev);
if (!slave)
goto out_unlock;
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index b5ee45f6d55a..8a83eb283c21 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -33,7 +33,6 @@
#define AD_TIMER_INTERVAL 100 /*msec*/
#define MULTICAST_LACPDU_ADDR {0x01, 0x80, 0xC2, 0x00, 0x00, 0x02}
-#define AD_MULTICAST_LACPDU_ADDR {MULTICAST_LACPDU_ADDR}
#define AD_LACP_SLOW 0
#define AD_LACP_FAST 1
@@ -42,10 +41,11 @@ typedef struct mac_addr {
u8 mac_addr_value[ETH_ALEN];
} mac_addr_t;
-typedef enum {
- AD_BANDWIDTH = 0,
- AD_COUNT
-} agg_selection_t;
+enum {
+ BOND_AD_STABLE = 0,
+ BOND_AD_BANDWIDTH = 1,
+ BOND_AD_COUNT = 2,
+};
// rx machine states(43.4.11 in the 802.3ad standard)
typedef enum {
@@ -105,12 +105,6 @@ typedef enum {
#pragma pack(1)
-typedef struct ad_header {
- struct mac_addr destination_address;
- struct mac_addr source_address;
- __be16 length_type;
-} ad_header_t;
-
// Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard)
typedef struct lacpdu {
u8 subtype; // = LACP(= 0x01)
@@ -143,7 +137,7 @@ typedef struct lacpdu {
} lacpdu_t;
typedef struct lacpdu_header {
- struct ad_header ad_header;
+ struct ethhdr hdr;
struct lacpdu lacpdu;
} lacpdu_header_t;
@@ -164,7 +158,7 @@ typedef struct bond_marker {
} bond_marker_t;
typedef struct bond_marker_header {
- struct ad_header ad_header;
+ struct ethhdr hdr;
struct bond_marker marker;
} bond_marker_header_t;
@@ -183,7 +177,7 @@ struct port;
typedef struct aggregator {
struct mac_addr aggregator_mac_address;
u16 aggregator_identifier;
- u16 is_individual; // BOOLEAN
+ bool is_individual;
u16 actor_admin_aggregator_key;
u16 actor_oper_aggregator_key;
struct mac_addr partner_system;
@@ -198,6 +192,15 @@ typedef struct aggregator {
u16 num_of_ports;
} aggregator_t;
+struct port_params {
+ struct mac_addr system;
+ u16 system_priority;
+ u16 key;
+ u16 port_number;
+ u16 port_priority;
+ u16 port_state;
+};
+
// port structure(43.4.6 in the 802.3ad standard)
typedef struct port {
u16 actor_port_number;
@@ -205,24 +208,17 @@ typedef struct port {
struct mac_addr actor_system; // This parameter is added here although it is not specified in the standard, just for simplification
u16 actor_system_priority; // This parameter is added here although it is not specified in the standard, just for simplification
u16 actor_port_aggregator_identifier;
- u16 ntt; // BOOLEAN
+ bool ntt;
u16 actor_admin_port_key;
u16 actor_oper_port_key;
u8 actor_admin_port_state;
u8 actor_oper_port_state;
- struct mac_addr partner_admin_system;
- struct mac_addr partner_oper_system;
- u16 partner_admin_system_priority;
- u16 partner_oper_system_priority;
- u16 partner_admin_key;
- u16 partner_oper_key;
- u16 partner_admin_port_number;
- u16 partner_oper_port_number;
- u16 partner_admin_port_priority;
- u16 partner_oper_port_priority;
- u8 partner_admin_port_state;
- u8 partner_oper_port_state;
- u16 is_enabled; // BOOLEAN
+
+ struct port_params partner_admin;
+ struct port_params partner_oper;
+
+ bool is_enabled;
+
// ****** PRIVATE PARAMETERS ******
u16 sm_vars; // all state machines variables for this port
rx_states_t sm_rx_state; // state machine rx state
@@ -241,10 +237,10 @@ typedef struct port {
} port_t;
// system structure
-typedef struct ad_system {
+struct ad_system {
u16 sys_priority;
struct mac_addr sys_mac_addr;
-} ad_system_t;
+};
#ifdef __ia64__
#pragma pack()
@@ -255,7 +251,7 @@ typedef struct ad_system {
#define SLAVE_AD_INFO(slave) ((slave)->ad_info)
struct ad_bond_info {
- ad_system_t system; // 802.3ad system structure
+ struct ad_system system; /* 802.3ad system structure */
u32 agg_select_timer; // Timer to select aggregator after all adapter's hand shakes
u32 agg_select_mode; // Mode of selection of active aggregator(bandwidth/count)
int lacp_fast; /* whether fast periodic tx should be
@@ -277,6 +273,7 @@ void bond_3ad_initialize(struct bonding *bond, u16 tick_resolution, int lacp_fas
int bond_3ad_bind_slave(struct slave *slave);
void bond_3ad_unbind_slave(struct slave *slave);
void bond_3ad_state_machine_handler(struct work_struct *);
+void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
void bond_3ad_adapter_speed_changed(struct slave *slave);
void bond_3ad_adapter_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 87437c788476..27fb7f5c21cf 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -20,8 +20,6 @@
*
*/
-//#define BONDING_DEBUG 1
-
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -346,30 +344,37 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct packet_type *ptype, struct net_device *orig_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond;
struct arp_pkt *arp = (struct arp_pkt *)skb->data;
int res = NET_RX_DROP;
if (dev_net(bond_dev) != &init_net)
goto out;
- if (!(bond_dev->flags & IFF_MASTER))
+ while (bond_dev->priv_flags & IFF_802_1Q_VLAN)
+ bond_dev = vlan_dev_real_dev(bond_dev);
+
+ if (!(bond_dev->priv_flags & IFF_BONDING) ||
+ !(bond_dev->flags & IFF_MASTER))
goto out;
if (!arp) {
- dprintk("Packet has no ARP data\n");
+ pr_debug("Packet has no ARP data\n");
goto out;
}
if (skb->len < sizeof(struct arp_pkt)) {
- dprintk("Packet is too small to be an ARP\n");
+ pr_debug("Packet is too small to be an ARP\n");
goto out;
}
if (arp->op_code == htons(ARPOP_REPLY)) {
/* update rx hash table for this ARP */
+ printk("rar: update orig %s bond_dev %s\n", orig_dev->name,
+ bond_dev->name);
+ bond = netdev_priv(bond_dev);
rlb_update_entry_from_arp(bond, arp);
- dprintk("Server received an ARP Reply from client\n");
+ pr_debug("Server received an ARP Reply from client\n");
}
res = NET_RX_SUCCESS;
@@ -723,7 +728,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
if (tx_slave) {
memcpy(arp->mac_src,tx_slave->dev->dev_addr, ETH_ALEN);
}
- dprintk("Server sent ARP Reply packet\n");
+ pr_debug("Server sent ARP Reply packet\n");
} else if (arp->op_code == htons(ARPOP_REQUEST)) {
/* Create an entry in the rx_hashtbl for this client as a
* place holder.
@@ -743,7 +748,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
* updated with their assigned mac.
*/
rlb_req_update_subnet_clients(bond, arp->ip_src);
- dprintk("Server sent ARP Request packet\n");
+ pr_debug("Server sent ARP Request packet\n");
}
return tx_slave;
@@ -818,7 +823,7 @@ static int rlb_initialize(struct bonding *bond)
/*initialize packet type*/
pk_type->type = __constant_htons(ETH_P_ARP);
- pk_type->dev = bond->dev;
+ pk_type->dev = NULL;
pk_type->func = rlb_arp_recv;
/* register to receive ARPs */
@@ -1211,11 +1216,6 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
}
bond_for_each_slave(bond, slave, i) {
- if (slave->dev->set_mac_address == NULL) {
- res = -EOPNOTSUPP;
- goto unwind;
- }
-
/* save net_device's current hw address */
memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN);
@@ -1224,9 +1224,8 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
/* restore net_device's hw address */
memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN);
- if (res) {
+ if (res)
goto unwind;
- }
}
return 0;
@@ -1285,7 +1284,7 @@ void bond_alb_deinitialize(struct bonding *bond)
int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct ethhdr *eth_data;
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
struct slave *tx_slave = NULL;
@@ -1706,7 +1705,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
*/
int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct sockaddr *sa = addr;
struct slave *slave, *swap_slave;
int res;
diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c
new file mode 100644
index 000000000000..0d73bf5ac5a5
--- /dev/null
+++ b/drivers/net/bonding/bond_ipv6.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright(c) 2008 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/if_vlan.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include "bonding.h"
+
+/*
+ * Assign bond->master_ipv6 to the next IPv6 address in the list, or
+ * zero it out if there are none.
+ */
+static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr)
+{
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
+
+ if (!dev)
+ return;
+
+ idev = in6_dev_get(dev);
+ if (!idev)
+ return;
+
+ read_lock_bh(&idev->lock);
+ ifa = idev->addr_list;
+ if (ifa)
+ ipv6_addr_copy(addr, &ifa->addr);
+ else
+ ipv6_addr_set(addr, 0, 0, 0, 0);
+
+ read_unlock_bh(&idev->lock);
+
+ in6_dev_put(idev);
+}
+
+static void bond_na_send(struct net_device *slave_dev,
+ struct in6_addr *daddr,
+ int router,
+ unsigned short vlan_id)
+{
+ struct in6_addr mcaddr;
+ struct icmp6hdr icmp6h = {
+ .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
+ };
+ struct sk_buff *skb;
+
+ icmp6h.icmp6_router = router;
+ icmp6h.icmp6_solicited = 0;
+ icmp6h.icmp6_override = 1;
+
+ addrconf_addr_solict_mult(daddr, &mcaddr);
+
+ pr_debug("ipv6 na on slave %s: dest %pI6, src %pI6\n",
+ slave_dev->name, &mcaddr, daddr);
+
+ skb = ndisc_build_skb(slave_dev, &mcaddr, daddr, &icmp6h, daddr,
+ ND_OPT_TARGET_LL_ADDR);
+
+ if (!skb) {
+ printk(KERN_ERR DRV_NAME ": NA packet allocation failed\n");
+ return;
+ }
+
+ if (vlan_id) {
+ skb = vlan_put_tag(skb, vlan_id);
+ if (!skb) {
+ printk(KERN_ERR DRV_NAME ": failed to insert VLAN tag\n");
+ return;
+ }
+ }
+
+ ndisc_send_skb(skb, slave_dev, NULL, &mcaddr, daddr, &icmp6h);
+}
+
+/*
+ * Kick out an unsolicited Neighbor Advertisement for an IPv6 address on
+ * the bonding master. This will help the switch learn our address
+ * if in active-backup mode.
+ *
+ * Caller must hold curr_slave_lock for read or better
+ */
+void bond_send_unsolicited_na(struct bonding *bond)
+{
+ struct slave *slave = bond->curr_active_slave;
+ struct vlan_entry *vlan;
+ struct inet6_dev *idev;
+ int is_router;
+
+ pr_debug("bond_send_unsol_na: bond %s slave %s\n", bond->dev->name,
+ slave ? slave->dev->name : "NULL");
+
+ if (!slave || !bond->send_unsol_na ||
+ test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
+ return;
+
+ bond->send_unsol_na--;
+
+ idev = in6_dev_get(bond->dev);
+ if (!idev)
+ return;
+
+ is_router = !!idev->cnf.forwarding;
+
+ in6_dev_put(idev);
+
+ if (!ipv6_addr_any(&bond->master_ipv6))
+ bond_na_send(slave->dev, &bond->master_ipv6, is_router, 0);
+
+ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
+ if (!ipv6_addr_any(&vlan->vlan_ipv6)) {
+ bond_na_send(slave->dev, &vlan->vlan_ipv6, is_router,
+ vlan->vlan_id);
+ }
+ }
+}
+
+/*
+ * bond_inet6addr_event: handle inet6addr notifier chain events.
+ *
+ * We keep track of device IPv6 addresses primarily to use as source
+ * addresses in NS probes.
+ *
+ * We track one IPv6 for the main device (if it has one).
+ */
+static int bond_inet6addr_event(struct notifier_block *this,
+ unsigned long event,
+ void *ptr)
+{
+ struct inet6_ifaddr *ifa = ptr;
+ struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
+ struct bonding *bond;
+ struct vlan_entry *vlan;
+
+ if (dev_net(event_dev) != &init_net)
+ return NOTIFY_DONE;
+
+ list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ if (bond->dev == event_dev) {
+ switch (event) {
+ case NETDEV_UP:
+ if (ipv6_addr_any(&bond->master_ipv6))
+ ipv6_addr_copy(&bond->master_ipv6,
+ &ifa->addr);
+ return NOTIFY_OK;
+ case NETDEV_DOWN:
+ if (ipv6_addr_equal(&bond->master_ipv6,
+ &ifa->addr))
+ bond_glean_dev_ipv6(bond->dev,
+ &bond->master_ipv6);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+
+ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
+ vlan_dev = vlan_group_get_device(bond->vlgrp,
+ vlan->vlan_id);
+ if (vlan_dev == event_dev) {
+ switch (event) {
+ case NETDEV_UP:
+ if (ipv6_addr_any(&vlan->vlan_ipv6))
+ ipv6_addr_copy(&vlan->vlan_ipv6,
+ &ifa->addr);
+ return NOTIFY_OK;
+ case NETDEV_DOWN:
+ if (ipv6_addr_equal(&vlan->vlan_ipv6,
+ &ifa->addr))
+ bond_glean_dev_ipv6(vlan_dev,
+ &vlan->vlan_ipv6);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block bond_inet6addr_notifier = {
+ .notifier_call = bond_inet6addr_event,
+};
+
+void bond_register_ipv6_notifier(void)
+{
+ register_inet6addr_notifier(&bond_inet6addr_notifier);
+}
+
+void bond_unregister_ipv6_notifier(void)
+{
+ unregister_inet6addr_notifier(&bond_inet6addr_notifier);
+}
+
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index a3efba59eee9..460c2cad2755 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -31,8 +31,6 @@
*
*/
-//#define BONDING_DEBUG 1
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -89,6 +87,7 @@
static int max_bonds = BOND_DEFAULT_MAX_BONDS;
static int num_grat_arp = 1;
+static int num_unsol_na = 1;
static int miimon = BOND_LINK_MON_INTERV;
static int updelay = 0;
static int downdelay = 0;
@@ -96,6 +95,7 @@ static int use_carrier = 1;
static char *mode = NULL;
static char *primary = NULL;
static char *lacp_rate = NULL;
+static char *ad_select = NULL;
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
@@ -107,6 +107,8 @@ module_param(max_bonds, int, 0);
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
module_param(num_grat_arp, int, 0644);
MODULE_PARM_DESC(num_grat_arp, "Number of gratuitous ARP packets to send on failover event");
+module_param(num_unsol_na, int, 0644);
+MODULE_PARM_DESC(num_unsol_na, "Number of unsolicited IPv6 Neighbor Advertisements packets to send on failover event");
module_param(miimon, int, 0);
MODULE_PARM_DESC(miimon, "Link check interval in milliseconds");
module_param(updelay, int, 0);
@@ -127,6 +129,8 @@ MODULE_PARM_DESC(primary, "Primary network device to use");
module_param(lacp_rate, charp, 0);
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner "
"(slow/fast)");
+module_param(ad_select, charp, 0);
+MODULE_PARM_DESC(ad_select, "803.ad aggregation selection logic: stable (0, default), bandwidth (1), count (2)");
module_param(xmit_hash_policy, charp, 0);
MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method: 0 for layer 2 (default)"
", 1 for layer 3+4");
@@ -150,7 +154,6 @@ LIST_HEAD(bond_dev_list);
static struct proc_dir_entry *bond_proc_dir = NULL;
#endif
-extern struct rw_semaphore bonding_rwsem;
static __be32 arp_target[BOND_MAX_ARP_TARGETS] = { 0, } ;
static int arp_ip_count = 0;
static int bond_mode = BOND_MODE_ROUNDROBIN;
@@ -158,13 +161,13 @@ static int xmit_hashtype= BOND_XMIT_POLICY_LAYER2;
static int lacp_fast = 0;
-struct bond_parm_tbl bond_lacp_tbl[] = {
+const struct bond_parm_tbl bond_lacp_tbl[] = {
{ "slow", AD_LACP_SLOW},
{ "fast", AD_LACP_FAST},
{ NULL, -1},
};
-struct bond_parm_tbl bond_mode_tbl[] = {
+const struct bond_parm_tbl bond_mode_tbl[] = {
{ "balance-rr", BOND_MODE_ROUNDROBIN},
{ "active-backup", BOND_MODE_ACTIVEBACKUP},
{ "balance-xor", BOND_MODE_XOR},
@@ -175,14 +178,14 @@ struct bond_parm_tbl bond_mode_tbl[] = {
{ NULL, -1},
};
-struct bond_parm_tbl xmit_hashtype_tbl[] = {
+const struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ "layer2", BOND_XMIT_POLICY_LAYER2},
{ "layer3+4", BOND_XMIT_POLICY_LAYER34},
{ "layer2+3", BOND_XMIT_POLICY_LAYER23},
{ NULL, -1},
};
-struct bond_parm_tbl arp_validate_tbl[] = {
+const struct bond_parm_tbl arp_validate_tbl[] = {
{ "none", BOND_ARP_VALIDATE_NONE},
{ "active", BOND_ARP_VALIDATE_ACTIVE},
{ "backup", BOND_ARP_VALIDATE_BACKUP},
@@ -190,13 +193,20 @@ struct bond_parm_tbl arp_validate_tbl[] = {
{ NULL, -1},
};
-struct bond_parm_tbl fail_over_mac_tbl[] = {
+const struct bond_parm_tbl fail_over_mac_tbl[] = {
{ "none", BOND_FOM_NONE},
{ "active", BOND_FOM_ACTIVE},
{ "follow", BOND_FOM_FOLLOW},
{ NULL, -1},
};
+struct bond_parm_tbl ad_select_tbl[] = {
+{ "stable", BOND_AD_STABLE},
+{ "bandwidth", BOND_AD_BANDWIDTH},
+{ "count", BOND_AD_COUNT},
+{ NULL, -1},
+};
+
/*-------------------------- Forward declarations ---------------------------*/
static void bond_send_gratuitous_arp(struct bonding *bond);
@@ -206,24 +216,20 @@ static void bond_deinit(struct net_device *bond_dev);
static const char *bond_mode_name(int mode)
{
- switch (mode) {
- case BOND_MODE_ROUNDROBIN :
- return "load balancing (round-robin)";
- case BOND_MODE_ACTIVEBACKUP :
- return "fault-tolerance (active-backup)";
- case BOND_MODE_XOR :
- return "load balancing (xor)";
- case BOND_MODE_BROADCAST :
- return "fault-tolerance (broadcast)";
- case BOND_MODE_8023AD:
- return "IEEE 802.3ad Dynamic link aggregation";
- case BOND_MODE_TLB:
- return "transmit load balancing";
- case BOND_MODE_ALB:
- return "adaptive load balancing";
- default:
+ static const char *names[] = {
+ [BOND_MODE_ROUNDROBIN] = "load balancing (round-robin)",
+ [BOND_MODE_ACTIVEBACKUP] = "fault-tolerance (active-backup)",
+ [BOND_MODE_XOR] = "load balancing (xor)",
+ [BOND_MODE_BROADCAST] = "fault-tolerance (broadcast)",
+ [BOND_MODE_8023AD]= "IEEE 802.3ad Dynamic link aggregation",
+ [BOND_MODE_TLB] = "transmit load balancing",
+ [BOND_MODE_ALB] = "adaptive load balancing",
+ };
+
+ if (mode < 0 || mode > BOND_MODE_ALB)
return "unknown";
- }
+
+ return names[mode];
}
/*---------------------------------- VLAN -----------------------------------*/
@@ -239,17 +245,16 @@ static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
{
struct vlan_entry *vlan;
- dprintk("bond: %s, vlan id %d\n",
+ pr_debug("bond: %s, vlan id %d\n",
(bond ? bond->dev->name: "None"), vlan_id);
- vlan = kmalloc(sizeof(struct vlan_entry), GFP_KERNEL);
+ vlan = kzalloc(sizeof(struct vlan_entry), GFP_KERNEL);
if (!vlan) {
return -ENOMEM;
}
INIT_LIST_HEAD(&vlan->vlan_list);
vlan->vlan_id = vlan_id;
- vlan->vlan_ip = 0;
write_lock_bh(&bond->lock);
@@ -257,7 +262,7 @@ static int bond_add_vlan(struct bonding *bond, unsigned short vlan_id)
write_unlock_bh(&bond->lock);
- dprintk("added VLAN ID %d on bond %s\n", vlan_id, bond->dev->name);
+ pr_debug("added VLAN ID %d on bond %s\n", vlan_id, bond->dev->name);
return 0;
}
@@ -274,7 +279,7 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
struct vlan_entry *vlan;
int res = -ENODEV;
- dprintk("bond: %s, vlan id %d\n", bond->dev->name, vlan_id);
+ pr_debug("bond: %s, vlan id %d\n", bond->dev->name, vlan_id);
write_lock_bh(&bond->lock);
@@ -282,12 +287,10 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
if (vlan->vlan_id == vlan_id) {
list_del(&vlan->vlan_list);
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond))
bond_alb_clear_vlan(bond, vlan_id);
- }
- dprintk("removed VLAN ID %d from bond %s\n", vlan_id,
+ pr_debug("removed VLAN ID %d from bond %s\n", vlan_id,
bond->dev->name);
kfree(vlan);
@@ -307,7 +310,7 @@ static int bond_del_vlan(struct bonding *bond, unsigned short vlan_id)
}
}
- dprintk("couldn't find VLAN ID %d in bond %s\n", vlan_id,
+ pr_debug("couldn't find VLAN ID %d in bond %s\n", vlan_id,
bond->dev->name);
out:
@@ -331,13 +334,13 @@ static int bond_has_challenged_slaves(struct bonding *bond)
bond_for_each_slave(bond, slave, i) {
if (slave->dev->features & NETIF_F_VLAN_CHALLENGED) {
- dprintk("found VLAN challenged slave - %s\n",
+ pr_debug("found VLAN challenged slave - %s\n",
slave->dev->name);
return 1;
}
}
- dprintk("no VLAN challenged slaves found\n");
+ pr_debug("no VLAN challenged slaves found\n");
return 0;
}
@@ -442,7 +445,7 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_de
*/
static void bond_vlan_rx_register(struct net_device *bond_dev, struct vlan_group *grp)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
int i;
@@ -450,10 +453,11 @@ static void bond_vlan_rx_register(struct net_device *bond_dev, struct vlan_group
bond_for_each_slave(bond, slave, i) {
struct net_device *slave_dev = slave->dev;
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_dev->vlan_rx_register) {
- slave_dev->vlan_rx_register(slave_dev, grp);
+ slave_ops->ndo_vlan_rx_register) {
+ slave_ops->ndo_vlan_rx_register(slave_dev, grp);
}
}
}
@@ -465,16 +469,17 @@ static void bond_vlan_rx_register(struct net_device *bond_dev, struct vlan_group
*/
static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
int i, res;
bond_for_each_slave(bond, slave, i) {
struct net_device *slave_dev = slave->dev;
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
- slave_dev->vlan_rx_add_vid) {
- slave_dev->vlan_rx_add_vid(slave_dev, vid);
+ slave_ops->ndo_vlan_rx_add_vid) {
+ slave_ops->ndo_vlan_rx_add_vid(slave_dev, vid);
}
}
@@ -493,21 +498,22 @@ static void bond_vlan_rx_add_vid(struct net_device *bond_dev, uint16_t vid)
*/
static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
struct net_device *vlan_dev;
int i, res;
bond_for_each_slave(bond, slave, i) {
struct net_device *slave_dev = slave->dev;
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
if ((slave_dev->features & NETIF_F_HW_VLAN_FILTER) &&
- slave_dev->vlan_rx_kill_vid) {
+ slave_ops->ndo_vlan_rx_kill_vid) {
/* Save and then restore vlan_dev in the grp array,
* since the slave's driver might clear it.
*/
vlan_dev = vlan_group_get_device(bond->vlgrp, vid);
- slave_dev->vlan_rx_kill_vid(slave_dev, vid);
+ slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vid);
vlan_group_set_device(bond->vlgrp, vid, vlan_dev);
}
}
@@ -523,26 +529,23 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid)
static void bond_add_vlans_on_slave(struct bonding *bond, struct net_device *slave_dev)
{
struct vlan_entry *vlan;
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
write_lock_bh(&bond->lock);
- if (list_empty(&bond->vlan_list)) {
+ if (list_empty(&bond->vlan_list))
goto out;
- }
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_dev->vlan_rx_register) {
- slave_dev->vlan_rx_register(slave_dev, bond->vlgrp);
- }
+ slave_ops->ndo_vlan_rx_register)
+ slave_ops->ndo_vlan_rx_register(slave_dev, bond->vlgrp);
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
- !(slave_dev->vlan_rx_add_vid)) {
+ !(slave_ops->ndo_vlan_rx_add_vid))
goto out;
- }
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- slave_dev->vlan_rx_add_vid(slave_dev, vlan->vlan_id);
- }
+ list_for_each_entry(vlan, &bond->vlan_list, vlan_list)
+ slave_ops->ndo_vlan_rx_add_vid(slave_dev, vlan->vlan_id);
out:
write_unlock_bh(&bond->lock);
@@ -550,34 +553,32 @@ out:
static void bond_del_vlans_from_slave(struct bonding *bond, struct net_device *slave_dev)
{
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
struct vlan_entry *vlan;
struct net_device *vlan_dev;
write_lock_bh(&bond->lock);
- if (list_empty(&bond->vlan_list)) {
+ if (list_empty(&bond->vlan_list))
goto out;
- }
if (!(slave_dev->features & NETIF_F_HW_VLAN_FILTER) ||
- !(slave_dev->vlan_rx_kill_vid)) {
+ !(slave_ops->ndo_vlan_rx_kill_vid))
goto unreg;
- }
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
/* Save and then restore vlan_dev in the grp array,
* since the slave's driver might clear it.
*/
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
- slave_dev->vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
+ slave_ops->ndo_vlan_rx_kill_vid(slave_dev, vlan->vlan_id);
vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev);
}
unreg:
if ((slave_dev->features & NETIF_F_HW_VLAN_RX) &&
- slave_dev->vlan_rx_register) {
- slave_dev->vlan_rx_register(slave_dev, NULL);
- }
+ slave_ops->ndo_vlan_rx_register)
+ slave_ops->ndo_vlan_rx_register(slave_dev, NULL);
out:
write_unlock_bh(&bond->lock);
@@ -686,15 +687,15 @@ static int bond_update_speed_duplex(struct slave *slave)
*/
static int bond_check_dev_link(struct bonding *bond, struct net_device *slave_dev, int reporting)
{
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
static int (* ioctl)(struct net_device *, struct ifreq *, int);
struct ifreq ifr;
struct mii_ioctl_data *mii;
- if (bond->params.use_carrier) {
+ if (bond->params.use_carrier)
return netif_carrier_ok(slave_dev) ? BMSR_LSTATUS : 0;
- }
- ioctl = slave_dev->do_ioctl;
+ ioctl = slave_ops->ndo_do_ioctl;
if (ioctl) {
/* TODO: set pointer to correct ioctl on a per team member */
/* bases to make this more efficient. that is, once */
@@ -927,7 +928,7 @@ static int bond_mc_list_copy(struct dev_mc_list *mc_list, struct bonding *bond,
*/
static void bond_mc_list_flush(struct net_device *bond_dev, struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct dev_mc_list *dmi;
for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) {
@@ -1164,10 +1165,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
}
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond))
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
- }
} else {
if (USES_PRIMARY(bond->params.mode)) {
printk(KERN_INFO DRV_NAME
@@ -1182,8 +1181,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond_mc_swap(bond, new_active, old_active);
}
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
bond_alb_handle_active_change(bond, new_active);
if (old_active)
bond_set_slave_inactive_flags(old_active);
@@ -1208,6 +1206,9 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
bond->send_grat_arp = bond->params.num_grat_arp;
bond_send_gratuitous_arp(bond);
+ bond->send_unsol_na = bond->params.num_unsol_na;
+ bond_send_unsolicited_na(bond);
+
write_unlock_bh(&bond->curr_slave_lock);
read_unlock(&bond->lock);
@@ -1315,9 +1316,9 @@ static void bond_detach_slave(struct bonding *bond, struct slave *slave)
static int bond_sethwaddr(struct net_device *bond_dev,
struct net_device *slave_dev)
{
- dprintk("bond_dev=%p\n", bond_dev);
- dprintk("slave_dev=%p\n", slave_dev);
- dprintk("slave_dev->addr_len=%d\n", slave_dev->addr_len);
+ pr_debug("bond_dev=%p\n", bond_dev);
+ pr_debug("slave_dev=%p\n", slave_dev);
+ pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len);
memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len);
return 0;
}
@@ -1364,14 +1365,12 @@ done:
return 0;
}
-
static void bond_setup_by_slave(struct net_device *bond_dev,
struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
- bond_dev->neigh_setup = slave_dev->neigh_setup;
- bond_dev->header_ops = slave_dev->header_ops;
+ bond_dev->header_ops = slave_dev->header_ops;
bond_dev->type = slave_dev->type;
bond_dev->hard_header_len = slave_dev->hard_header_len;
@@ -1385,7 +1384,8 @@ static void bond_setup_by_slave(struct net_device *bond_dev,
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
+ const struct net_device_ops *slave_ops = slave_dev->netdev_ops;
struct slave *new_slave = NULL;
struct dev_mc_list *dmi;
struct sockaddr addr;
@@ -1394,7 +1394,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
int res = 0;
if (!bond->params.use_carrier && slave_dev->ethtool_ops == NULL &&
- slave_dev->do_ioctl == NULL) {
+ slave_ops->ndo_do_ioctl == NULL) {
printk(KERN_WARNING DRV_NAME
": %s: Warning: no link monitoring support for %s\n",
bond_dev->name, slave_dev->name);
@@ -1409,14 +1409,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* already enslaved */
if (slave_dev->flags & IFF_SLAVE) {
- dprintk("Error, Device was already enslaved\n");
+ pr_debug("Error, Device was already enslaved\n");
return -EBUSY;
}
/* vlan challenged mutual exclusion */
/* no need to lock since we're protected by rtnl_lock */
if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) {
- dprintk("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
+ pr_debug("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
if (!list_empty(&bond->vlan_list)) {
printk(KERN_ERR DRV_NAME
": %s: Error: cannot enslave VLAN "
@@ -1434,7 +1434,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
}
} else {
- dprintk("%s: ! NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
+ pr_debug("%s: ! NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
if (bond->slave_cnt == 0) {
/* First slave, and it is not VLAN challenged,
* so remove the block of adding VLANs over the bond.
@@ -1476,7 +1476,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_undo_flags;
}
- if (slave_dev->set_mac_address == NULL) {
+ if (slave_ops->ndo_set_mac_address == NULL) {
if (bond->slave_cnt == 0) {
printk(KERN_WARNING DRV_NAME
": %s: Warning: The first slave device "
@@ -1522,28 +1522,27 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
addr.sa_family = slave_dev->type;
res = dev_set_mac_address(slave_dev, &addr);
if (res) {
- dprintk("Error %d calling set_mac_address\n", res);
+ pr_debug("Error %d calling set_mac_address\n", res);
goto err_free;
}
}
res = netdev_set_master(slave_dev, bond_dev);
if (res) {
- dprintk("Error %d calling netdev_set_master\n", res);
+ pr_debug("Error %d calling netdev_set_master\n", res);
goto err_restore_mac;
}
/* open the slave since the application closed it */
res = dev_open(slave_dev);
if (res) {
- dprintk("Openning slave %s failed\n", slave_dev->name);
+ pr_debug("Openning slave %s failed\n", slave_dev->name);
goto err_unset_master;
}
new_slave->dev = slave_dev;
slave_dev->priv_flags |= IFF_BONDING;
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
/* bond_alb_init_slave() must be called before all other stages since
* it might fail and we do not want to have to undo everything
*/
@@ -1641,18 +1640,18 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (!bond->params.miimon ||
(bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS)) {
if (bond->params.updelay) {
- dprintk("Initial state of slave_dev is "
+ pr_debug("Initial state of slave_dev is "
"BOND_LINK_BACK\n");
new_slave->link = BOND_LINK_BACK;
new_slave->delay = bond->params.updelay;
} else {
- dprintk("Initial state of slave_dev is "
+ pr_debug("Initial state of slave_dev is "
"BOND_LINK_UP\n");
new_slave->link = BOND_LINK_UP;
}
new_slave->jiffies = jiffies;
} else {
- dprintk("Initial state of slave_dev is "
+ pr_debug("Initial state of slave_dev is "
"BOND_LINK_DOWN\n");
new_slave->link = BOND_LINK_DOWN;
}
@@ -1713,7 +1712,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_set_slave_inactive_flags(new_slave);
break;
default:
- dprintk("This slave is always active in trunk mode\n");
+ pr_debug("This slave is always active in trunk mode\n");
/* always active in trunk mode */
new_slave->state = BOND_STATE_ACTIVE;
@@ -1787,11 +1786,10 @@ err_undo_flags:
*/
int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *oldcurrent;
struct sockaddr addr;
int mac_addr_differ;
- DECLARE_MAC_BUF(mac);
/* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_SLAVE) ||
@@ -1820,11 +1818,11 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
if (!mac_addr_differ && (bond->slave_cnt > 1))
printk(KERN_WARNING DRV_NAME
": %s: Warning: the permanent HWaddr of %s - "
- "%s - is still in use by %s. "
+ "%pM - is still in use by %s. "
"Set the HWaddr of %s to a different address "
"to avoid conflicts.\n",
bond_dev->name, slave_dev->name,
- print_mac(mac, slave->perm_hwaddr),
+ slave->perm_hwaddr,
bond_dev->name, slave_dev->name);
}
@@ -1860,8 +1858,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
bond_change_active_slave(bond, NULL);
}
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
/* Must be called only after the slave has been
* detached from the list and the curr_active_slave
* has been cleared (if our_slave == old_current),
@@ -1981,7 +1978,7 @@ void bond_destroy(struct bonding *bond)
static void bond_destructor(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
if (bond->wq)
destroy_workqueue(bond->wq);
@@ -1999,7 +1996,7 @@ static void bond_destructor(struct net_device *bond_dev)
*/
int bond_release_and_destroy(struct net_device *bond_dev, struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
int ret;
ret = bond_release(bond_dev, slave_dev);
@@ -2016,7 +2013,7 @@ int bond_release_and_destroy(struct net_device *bond_dev, struct net_device *sl
*/
static int bond_release_all(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
struct net_device *slave_dev;
struct sockaddr addr;
@@ -2050,8 +2047,7 @@ static int bond_release_all(struct net_device *bond_dev)
*/
write_unlock_bh(&bond->lock);
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
/* must be called only after the slave
* has been detached from the list
*/
@@ -2147,7 +2143,7 @@ out:
*/
static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *old_active = NULL;
struct slave *new_active = NULL;
int res = 0;
@@ -2196,7 +2192,7 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
info->bond_mode = bond->params.mode;
info->miimon = bond->params.miimon;
@@ -2210,7 +2206,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave;
int i, found = 0;
@@ -2378,8 +2374,7 @@ static void bond_miimon_commit(struct bonding *bond)
if (bond->params.mode == BOND_MODE_8023AD)
bond_3ad_handle_link_change(slave, BOND_LINK_UP);
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB))
+ if (bond_is_lb(bond))
bond_alb_handle_link_change(bond, slave,
BOND_LINK_UP);
@@ -2464,6 +2459,12 @@ void bond_mii_monitor(struct work_struct *work)
read_unlock(&bond->curr_slave_lock);
}
+ if (bond->send_unsol_na) {
+ read_lock(&bond->curr_slave_lock);
+ bond_send_unsolicited_na(bond);
+ read_unlock(&bond->curr_slave_lock);
+ }
+
if (bond_miimon_inspect(bond)) {
read_unlock(&bond->lock);
rtnl_lock();
@@ -2532,7 +2533,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_
{
struct sk_buff *skb;
- dprintk("arp %d on slave %s: dst %x src %x vid %d\n", arp_op,
+ pr_debug("arp %d on slave %s: dst %x src %x vid %d\n", arp_op,
slave_dev->name, dest_ip, src_ip, vlan_id);
skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip,
@@ -2565,9 +2566,9 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
if (!targets[i])
continue;
- dprintk("basa: target %x\n", targets[i]);
+ pr_debug("basa: target %x\n", targets[i]);
if (list_empty(&bond->vlan_list)) {
- dprintk("basa: empty vlan: arp_send\n");
+ pr_debug("basa: empty vlan: arp_send\n");
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
bond->master_ip, 0);
continue;
@@ -2586,8 +2587,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
if (rv) {
if (net_ratelimit()) {
printk(KERN_WARNING DRV_NAME
- ": %s: no route to arp_ip_target %u.%u.%u.%u\n",
- bond->dev->name, NIPQUAD(fl.fl4_dst));
+ ": %s: no route to arp_ip_target %pI4\n",
+ bond->dev->name, &fl.fl4_dst);
}
continue;
}
@@ -2597,7 +2598,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
*/
if (rt->u.dst.dev == bond->dev) {
ip_rt_put(rt);
- dprintk("basa: rtdev == bond->dev: arp_send\n");
+ pr_debug("basa: rtdev == bond->dev: arp_send\n");
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
bond->master_ip, 0);
continue;
@@ -2608,7 +2609,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id);
if (vlan_dev == rt->u.dst.dev) {
vlan_id = vlan->vlan_id;
- dprintk("basa: vlan match on %s %d\n",
+ pr_debug("basa: vlan match on %s %d\n",
vlan_dev->name, vlan_id);
break;
}
@@ -2623,8 +2624,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
if (net_ratelimit()) {
printk(KERN_WARNING DRV_NAME
- ": %s: no path to arp_ip_target %u.%u.%u.%u via rt.dev %s\n",
- bond->dev->name, NIPQUAD(fl.fl4_dst),
+ ": %s: no path to arp_ip_target %pI4 via rt.dev %s\n",
+ bond->dev->name, &fl.fl4_dst,
rt->u.dst.dev ? rt->u.dst.dev->name : "NULL");
}
ip_rt_put(rt);
@@ -2643,7 +2644,7 @@ static void bond_send_gratuitous_arp(struct bonding *bond)
struct vlan_entry *vlan;
struct net_device *vlan_dev;
- dprintk("bond_send_grat_arp: bond %s slave %s\n", bond->dev->name,
+ pr_debug("bond_send_grat_arp: bond %s slave %s\n", bond->dev->name,
slave ? slave->dev->name : "NULL");
if (!slave || !bond->send_grat_arp ||
@@ -2673,10 +2674,8 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
targets = bond->params.arp_targets;
for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
- dprintk("bva: sip %u.%u.%u.%u tip %u.%u.%u.%u t[%d] "
- "%u.%u.%u.%u bhti(tip) %d\n",
- NIPQUAD(sip), NIPQUAD(tip), i, NIPQUAD(targets[i]),
- bond_has_this_ip(bond, tip));
+ pr_debug("bva: sip %pI4 tip %pI4 t[%d] %pI4 bhti(tip) %d\n",
+ &sip, &tip, i, &targets[i], bond_has_this_ip(bond, tip));
if (sip == targets[i]) {
if (bond_has_this_ip(bond, tip))
slave->last_arp_rx = jiffies;
@@ -2699,10 +2698,10 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
goto out;
- bond = dev->priv;
+ bond = netdev_priv(dev);
read_lock(&bond->lock);
- dprintk("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
+ pr_debug("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
bond->dev->name, skb->dev ? skb->dev->name : "NULL",
orig_dev ? orig_dev->name : "NULL");
@@ -2728,10 +2727,10 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
arp_ptr += 4 + dev->addr_len;
memcpy(&tip, arp_ptr, 4);
- dprintk("bond_arp_rcv: %s %s/%d av %d sv %d sip %u.%u.%u.%u"
- " tip %u.%u.%u.%u\n", bond->dev->name, slave->dev->name,
- slave->state, bond->params.arp_validate,
- slave_do_arp_validate(bond, slave), NIPQUAD(sip), NIPQUAD(tip));
+ pr_debug("bond_arp_rcv: %s %s/%d av %d sv %d sip %pI4 tip %pI4\n",
+ bond->dev->name, slave->dev->name, slave->state,
+ bond->params.arp_validate, slave_do_arp_validate(bond, slave),
+ &sip, &tip);
/*
* Backup slaves won't see the ARP reply, but do come through
@@ -3161,6 +3160,12 @@ void bond_activebackup_arp_mon(struct work_struct *work)
read_unlock(&bond->curr_slave_lock);
}
+ if (bond->send_unsol_na) {
+ read_lock(&bond->curr_slave_lock);
+ bond_send_unsolicited_na(bond);
+ read_unlock(&bond->curr_slave_lock);
+ }
+
if (bond_ab_arp_inspect(bond, delta_in_ticks)) {
read_unlock(&bond->lock);
rtnl_lock();
@@ -3239,7 +3244,6 @@ static void bond_info_show_master(struct seq_file *seq)
struct bonding *bond = seq->private;
struct slave *curr;
int i;
- u32 target;
read_lock(&bond->curr_slave_lock);
curr = bond->curr_active_slave;
@@ -3293,8 +3297,7 @@ static void bond_info_show_master(struct seq_file *seq)
continue;
if (printed)
seq_printf(seq, ",");
- target = ntohl(bond->params.arp_targets[i]);
- seq_printf(seq, " %d.%d.%d.%d", HIPQUAD(target));
+ seq_printf(seq, " %pI4", &bond->params.arp_targets[i]);
printed = 1;
}
seq_printf(seq, "\n");
@@ -3302,11 +3305,12 @@ static void bond_info_show_master(struct seq_file *seq)
if (bond->params.mode == BOND_MODE_8023AD) {
struct ad_info ad_info;
- DECLARE_MAC_BUF(mac);
seq_puts(seq, "\n802.3ad info\n");
seq_printf(seq, "LACP rate: %s\n",
(bond->params.lacp_fast) ? "fast" : "slow");
+ seq_printf(seq, "Aggregator selection policy (ad_select): %s\n",
+ ad_select_tbl[bond->params.ad_select].modename);
if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
seq_printf(seq, "bond %s has no active aggregator\n",
@@ -3322,8 +3326,8 @@ static void bond_info_show_master(struct seq_file *seq)
ad_info.actor_key);
seq_printf(seq, "\tPartner Key: %d\n",
ad_info.partner_key);
- seq_printf(seq, "\tPartner Mac Address: %s\n",
- print_mac(mac, ad_info.partner_system));
+ seq_printf(seq, "\tPartner Mac Address: %pM\n",
+ ad_info.partner_system);
}
}
}
@@ -3331,7 +3335,6 @@ static void bond_info_show_master(struct seq_file *seq)
static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave)
{
struct bonding *bond = seq->private;
- DECLARE_MAC_BUF(mac);
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
@@ -3339,9 +3342,7 @@ static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave
seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
- seq_printf(seq,
- "Permanent HW addr: %s\n",
- print_mac(mac, slave->perm_hwaddr));
+ seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr);
if (bond->params.mode == BOND_MODE_8023AD) {
const struct aggregator *agg
@@ -3506,7 +3507,7 @@ static int bond_event_changename(struct bonding *bond)
static int bond_master_netdev_event(unsigned long event, struct net_device *bond_dev)
{
- struct bonding *event_bond = bond_dev->priv;
+ struct bonding *event_bond = netdev_priv(bond_dev);
switch (event) {
case NETDEV_CHANGENAME:
@@ -3524,7 +3525,7 @@ static int bond_master_netdev_event(unsigned long event, struct net_device *bond
static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev)
{
struct net_device *bond_dev = slave_dev->master;
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
switch (event) {
case NETDEV_UNREGISTER:
@@ -3591,7 +3592,7 @@ static int bond_netdev_event(struct notifier_block *this, unsigned long event, v
if (dev_net(event_dev) != &init_net)
return NOTIFY_DONE;
- dprintk("event_dev: %s, event: %lx\n",
+ pr_debug("event_dev: %s, event: %lx\n",
(event_dev ? event_dev->name : "None"),
event);
@@ -3599,12 +3600,12 @@ static int bond_netdev_event(struct notifier_block *this, unsigned long event, v
return NOTIFY_DONE;
if (event_dev->flags & IFF_MASTER) {
- dprintk("IFF_MASTER\n");
+ pr_debug("IFF_MASTER\n");
return bond_master_netdev_event(event, event_dev);
}
if (event_dev->flags & IFF_SLAVE) {
- dprintk("IFF_SLAVE\n");
+ pr_debug("IFF_SLAVE\n");
return bond_slave_netdev_event(event, event_dev);
}
@@ -3775,12 +3776,11 @@ static int bond_xmit_hash_policy_l2(struct sk_buff *skb,
static int bond_open(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
bond->kill_timers = 0;
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
/* bond_alb_initialize must be called before the timer
* is started.
*/
@@ -3816,6 +3816,7 @@ static int bond_open(struct net_device *bond_dev)
queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
bond_register_lacpdu(bond);
+ bond_3ad_initiate_agg_selection(bond, 1);
}
return 0;
@@ -3823,7 +3824,7 @@ static int bond_open(struct net_device *bond_dev)
static int bond_close(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
if (bond->params.mode == BOND_MODE_8023AD) {
/* Unregister the receive of LACPDUs */
@@ -3836,6 +3837,7 @@ static int bond_close(struct net_device *bond_dev)
write_lock_bh(&bond->lock);
bond->send_grat_arp = 0;
+ bond->send_unsol_na = 0;
/* signal timers not to re-arm */
bond->kill_timers = 1;
@@ -3863,8 +3865,7 @@ static int bond_close(struct net_device *bond_dev)
}
- if ((bond->params.mode == BOND_MODE_TLB) ||
- (bond->params.mode == BOND_MODE_ALB)) {
+ if (bond_is_lb(bond)) {
/* Must be called only after all
* slaves have been released
*/
@@ -3876,8 +3877,8 @@ static int bond_close(struct net_device *bond_dev)
static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
- struct net_device_stats *stats = &(bond->stats), *sstats;
+ struct bonding *bond = netdev_priv(bond_dev);
+ struct net_device_stats *stats = &bond->stats;
struct net_device_stats local_stats;
struct slave *slave;
int i;
@@ -3887,7 +3888,8 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
read_lock_bh(&bond->lock);
bond_for_each_slave(bond, slave, i) {
- sstats = slave->dev->get_stats(slave->dev);
+ const struct net_device_stats *sstats = dev_get_stats(slave->dev);
+
local_stats.rx_packets += sstats->rx_packets;
local_stats.rx_bytes += sstats->rx_bytes;
local_stats.rx_errors += sstats->rx_errors;
@@ -3932,7 +3934,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
struct mii_ioctl_data *mii = NULL;
int res = 0;
- dprintk("bond_ioctl: master=%s, cmd=%d\n",
+ pr_debug("bond_ioctl: master=%s, cmd=%d\n",
bond_dev->name, cmd);
switch (cmd) {
@@ -3954,7 +3956,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
}
if (mii->reg_num == 1) {
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
mii->val_out = 0;
read_lock(&bond->lock);
read_lock(&bond->curr_slave_lock);
@@ -4010,12 +4012,12 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
down_write(&(bonding_rwsem));
slave_dev = dev_get_by_name(&init_net, ifr->ifr_slave);
- dprintk("slave_dev=%p: \n", slave_dev);
+ pr_debug("slave_dev=%p: \n", slave_dev);
if (!slave_dev) {
res = -ENODEV;
} else {
- dprintk("slave_dev->name=%s: \n", slave_dev->name);
+ pr_debug("slave_dev->name=%s: \n", slave_dev->name);
switch (cmd) {
case BOND_ENSLAVE_OLD:
case SIOCBONDENSLAVE:
@@ -4046,7 +4048,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
static void bond_set_multicast_list(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct dev_mc_list *dmi;
/*
@@ -4102,17 +4104,31 @@ static void bond_set_multicast_list(struct net_device *bond_dev)
read_unlock(&bond->lock);
}
+static int bond_neigh_setup(struct net_device *dev, struct neigh_parms *parms)
+{
+ struct bonding *bond = netdev_priv(dev);
+ struct slave *slave = bond->first_slave;
+
+ if (slave) {
+ const struct net_device_ops *slave_ops
+ = slave->dev->netdev_ops;
+ if (slave_ops->ndo_neigh_setup)
+ return slave_ops->ndo_neigh_setup(dev, parms);
+ }
+ return 0;
+}
+
/*
* Change the MTU of all of a master's slaves to match the master
*/
static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *stop_at;
int res = 0;
int i;
- dprintk("bond=%p, name=%s, new_mtu=%d\n", bond,
+ pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond,
(bond_dev ? bond_dev->name : "None"), new_mtu);
/* Can't hold bond->lock with bh disabled here since
@@ -4131,7 +4147,7 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
*/
bond_for_each_slave(bond, slave, i) {
- dprintk("s %p s->p %p c_m %p\n", slave,
+ pr_debug("s %p s->p %p c_m %p\n", slave,
slave->prev, slave->dev->change_mtu);
res = dev_set_mtu(slave->dev, new_mtu);
@@ -4145,7 +4161,7 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu)
* means changing their mtu from timer context, which
* is probably not a good idea.
*/
- dprintk("err %d %s\n", res, slave->dev->name);
+ pr_debug("err %d %s\n", res, slave->dev->name);
goto unwind;
}
}
@@ -4162,7 +4178,7 @@ unwind:
tmp_res = dev_set_mtu(slave->dev, bond_dev->mtu);
if (tmp_res) {
- dprintk("unwind err %d dev %s\n", tmp_res,
+ pr_debug("unwind err %d dev %s\n", tmp_res,
slave->dev->name);
}
}
@@ -4179,13 +4195,17 @@ unwind:
*/
static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct sockaddr *sa = addr, tmp_sa;
struct slave *slave, *stop_at;
int res = 0;
int i;
- dprintk("bond=%p, name=%s\n", bond, (bond_dev ? bond_dev->name : "None"));
+ if (bond->params.mode == BOND_MODE_ALB)
+ return bond_alb_set_mac_address(bond_dev, addr);
+
+
+ pr_debug("bond=%p, name=%s\n", bond, (bond_dev ? bond_dev->name : "None"));
/*
* If fail_over_mac is set to active, do nothing and return
@@ -4214,11 +4234,12 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
*/
bond_for_each_slave(bond, slave, i) {
- dprintk("slave %p %s\n", slave, slave->dev->name);
+ const struct net_device_ops *slave_ops = slave->dev->netdev_ops;
+ pr_debug("slave %p %s\n", slave, slave->dev->name);
- if (slave->dev->set_mac_address == NULL) {
+ if (slave_ops->ndo_set_mac_address == NULL) {
res = -EOPNOTSUPP;
- dprintk("EOPNOTSUPP %s\n", slave->dev->name);
+ pr_debug("EOPNOTSUPP %s\n", slave->dev->name);
goto unwind;
}
@@ -4230,7 +4251,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
* breakage anyway until ARP finish
* updating, so...
*/
- dprintk("err %d %s\n", res, slave->dev->name);
+ pr_debug("err %d %s\n", res, slave->dev->name);
goto unwind;
}
}
@@ -4250,7 +4271,7 @@ unwind:
tmp_res = dev_set_mac_address(slave->dev, &tmp_sa);
if (tmp_res) {
- dprintk("unwind err %d dev %s\n", tmp_res,
+ pr_debug("unwind err %d dev %s\n", tmp_res,
slave->dev->name);
}
}
@@ -4260,7 +4281,7 @@ unwind:
static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *start_at;
int i, slave_no, res = 1;
@@ -4309,7 +4330,7 @@ out:
*/
static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
int res = 1;
read_lock(&bond->lock);
@@ -4341,7 +4362,7 @@ out:
*/
static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *start_at;
int slave_no;
int i;
@@ -4387,7 +4408,7 @@ out:
*/
static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave, *start_at;
struct net_device *tx_dev = NULL;
int i;
@@ -4463,6 +4484,35 @@ static void bond_set_xmit_hash_policy(struct bonding *bond)
}
}
+static int bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ const struct bonding *bond = netdev_priv(dev);
+
+ switch (bond->params.mode) {
+ case BOND_MODE_ROUNDROBIN:
+ return bond_xmit_roundrobin(skb, dev);
+ case BOND_MODE_ACTIVEBACKUP:
+ return bond_xmit_activebackup(skb, dev);
+ case BOND_MODE_XOR:
+ return bond_xmit_xor(skb, dev);
+ case BOND_MODE_BROADCAST:
+ return bond_xmit_broadcast(skb, dev);
+ case BOND_MODE_8023AD:
+ return bond_3ad_xmit_xor(skb, dev);
+ case BOND_MODE_ALB:
+ case BOND_MODE_TLB:
+ return bond_alb_xmit(skb, dev);
+ default:
+ /* Should never happen, mode already checked */
+ printk(KERN_ERR DRV_NAME ": %s: Error: Unknown bonding mode %d\n",
+ dev->name, bond->params.mode);
+ WARN_ON_ONCE(1);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+}
+
+
/*
* set bond mode specific net device operations
*/
@@ -4472,29 +4522,22 @@ void bond_set_mode_ops(struct bonding *bond, int mode)
switch (mode) {
case BOND_MODE_ROUNDROBIN:
- bond_dev->hard_start_xmit = bond_xmit_roundrobin;
break;
case BOND_MODE_ACTIVEBACKUP:
- bond_dev->hard_start_xmit = bond_xmit_activebackup;
break;
case BOND_MODE_XOR:
- bond_dev->hard_start_xmit = bond_xmit_xor;
bond_set_xmit_hash_policy(bond);
break;
case BOND_MODE_BROADCAST:
- bond_dev->hard_start_xmit = bond_xmit_broadcast;
break;
case BOND_MODE_8023AD:
bond_set_master_3ad_flags(bond);
- bond_dev->hard_start_xmit = bond_3ad_xmit_xor;
bond_set_xmit_hash_policy(bond);
break;
case BOND_MODE_ALB:
bond_set_master_alb_flags(bond);
/* FALLTHRU */
case BOND_MODE_TLB:
- bond_dev->hard_start_xmit = bond_alb_xmit;
- bond_dev->set_mac_address = bond_alb_set_mac_address;
break;
default:
/* Should never happen, mode already checked */
@@ -4524,15 +4567,30 @@ static const struct ethtool_ops bond_ethtool_ops = {
.get_flags = ethtool_op_get_flags,
};
+static const struct net_device_ops bond_netdev_ops = {
+ .ndo_open = bond_open,
+ .ndo_stop = bond_close,
+ .ndo_start_xmit = bond_start_xmit,
+ .ndo_get_stats = bond_get_stats,
+ .ndo_do_ioctl = bond_do_ioctl,
+ .ndo_set_multicast_list = bond_set_multicast_list,
+ .ndo_change_mtu = bond_change_mtu,
+ .ndo_set_mac_address = bond_set_mac_address,
+ .ndo_neigh_setup = bond_neigh_setup,
+ .ndo_vlan_rx_register = bond_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = bond_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = bond_vlan_rx_kill_vid,
+};
+
/*
* Does not allocate but creates a /proc entry.
* Allowed to fail.
*/
static int bond_init(struct net_device *bond_dev, struct bond_params *params)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
- dprintk("Begin bond_init for %s\n", bond_dev->name);
+ pr_debug("Begin bond_init for %s\n", bond_dev->name);
/* initialize rwlocks */
rwlock_init(&bond->lock);
@@ -4551,20 +4609,13 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
bond->primary_slave = NULL;
bond->dev = bond_dev;
bond->send_grat_arp = 0;
+ bond->send_unsol_na = 0;
bond->setup_by_slave = 0;
INIT_LIST_HEAD(&bond->vlan_list);
/* Initialize the device entry points */
- bond_dev->open = bond_open;
- bond_dev->stop = bond_close;
- bond_dev->get_stats = bond_get_stats;
- bond_dev->do_ioctl = bond_do_ioctl;
+ bond_dev->netdev_ops = &bond_netdev_ops;
bond_dev->ethtool_ops = &bond_ethtool_ops;
- bond_dev->set_multicast_list = bond_set_multicast_list;
- bond_dev->change_mtu = bond_change_mtu;
- bond_dev->set_mac_address = bond_set_mac_address;
- bond_dev->validate_addr = NULL;
-
bond_set_mode_ops(bond, bond->params.mode);
bond_dev->destructor = bond_destructor;
@@ -4573,6 +4624,8 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
bond_dev->priv_flags |= IFF_BONDING;
+ if (bond->params.arp_interval)
+ bond_dev->priv_flags |= IFF_MASTER_ARPMON;
/* At first, we block adding VLANs. That's the only way to
* prevent problems that occur when adding VLANs over an
@@ -4591,9 +4644,6 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
* when there are slaves that are not hw accel
* capable
*/
- bond_dev->vlan_rx_register = bond_vlan_rx_register;
- bond_dev->vlan_rx_add_vid = bond_vlan_rx_add_vid;
- bond_dev->vlan_rx_kill_vid = bond_vlan_rx_kill_vid;
bond_dev->features |= (NETIF_F_HW_VLAN_TX |
NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER);
@@ -4632,7 +4682,7 @@ static void bond_work_cancel_all(struct bonding *bond)
*/
static void bond_deinit(struct net_device *bond_dev)
{
- struct bonding *bond = bond_dev->priv;
+ struct bonding *bond = netdev_priv(bond_dev);
list_del(&bond->bond_list);
@@ -4672,7 +4722,7 @@ static void bond_free_all(void)
* some mode names are substrings of other names, and calls from sysfs
* may have whitespace in the name (trailing newlines, for example).
*/
-int bond_parse_parm(const char *buf, struct bond_parm_tbl *tbl)
+int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
{
int mode = -1, i, rv;
char *p, modestr[BOND_MAX_MODENAME_LEN + 1] = { 0, };
@@ -4751,6 +4801,23 @@ static int bond_check_params(struct bond_params *params)
}
}
+ if (ad_select) {
+ params->ad_select = bond_parse_parm(ad_select, ad_select_tbl);
+ if (params->ad_select == -1) {
+ printk(KERN_ERR DRV_NAME
+ ": Error: Invalid ad_select \"%s\"\n",
+ ad_select == NULL ? "NULL" : ad_select);
+ return -EINVAL;
+ }
+
+ if (bond_mode != BOND_MODE_8023AD) {
+ printk(KERN_WARNING DRV_NAME
+ ": ad_select param only affects 802.3ad mode\n");
+ }
+ } else {
+ params->ad_select = BOND_AD_STABLE;
+ }
+
if (max_bonds < 0 || max_bonds > INT_MAX) {
printk(KERN_WARNING DRV_NAME
": Warning: max_bonds (%d) not in range %d-%d, so it "
@@ -4798,6 +4865,13 @@ static int bond_check_params(struct bond_params *params)
num_grat_arp = 1;
}
+ if (num_unsol_na < 0 || num_unsol_na > 255) {
+ printk(KERN_WARNING DRV_NAME
+ ": Warning: num_unsol_na (%d) not in range 0-255 so it "
+ "was reset to 1 \n", num_unsol_na);
+ num_unsol_na = 1;
+ }
+
/* reset values for 802.3ad */
if (bond_mode == BOND_MODE_8023AD) {
if (!miimon) {
@@ -4999,6 +5073,7 @@ static int bond_check_params(struct bond_params *params)
params->xmit_policy = xmit_hashtype;
params->miimon = miimon;
params->num_grat_arp = num_grat_arp;
+ params->num_unsol_na = num_unsol_na;
params->arp_interval = arp_interval;
params->arp_validate = arp_validate_value;
params->updelay = updelay;
@@ -5099,7 +5174,7 @@ int bond_create(char *name, struct bond_params *params)
up_write(&bonding_rwsem);
rtnl_unlock(); /* allows sysfs registration of net device */
- res = bond_create_sysfs_entry(bond_dev->priv);
+ res = bond_create_sysfs_entry(netdev_priv(bond_dev));
if (res < 0) {
rtnl_lock();
down_write(&bonding_rwsem);
@@ -5151,6 +5226,7 @@ static int __init bonding_init(void)
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
+ bond_register_ipv6_notifier();
goto out;
err:
@@ -5173,6 +5249,7 @@ static void __exit bonding_exit(void)
{
unregister_netdevice_notifier(&bond_netdev_notifier);
unregister_inetaddr_notifier(&bond_inetaddr_notifier);
+ bond_unregister_ipv6_notifier();
bond_destroy_sysfs();
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 3bdb47382521..18cf4787874c 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -36,22 +36,13 @@
#include <linux/rtnetlink.h>
#include <net/net_namespace.h>
-/* #define BONDING_DEBUG 1 */
#include "bonding.h"
+
#define to_dev(obj) container_of(obj,struct device,kobj)
-#define to_bond(cd) ((struct bonding *)(to_net_dev(cd)->priv))
+#define to_bond(cd) ((struct bonding *)(netdev_priv(to_net_dev(cd))))
/*---------------------------- Declarations -------------------------------*/
-
-extern struct list_head bond_dev_list;
-extern struct bond_params bonding_defaults;
-extern struct bond_parm_tbl bond_mode_tbl[];
-extern struct bond_parm_tbl bond_lacp_tbl[];
-extern struct bond_parm_tbl xmit_hashtype_tbl[];
-extern struct bond_parm_tbl arp_validate_tbl[];
-extern struct bond_parm_tbl fail_over_mac_tbl[];
-
static int expected_refcount = -1;
/*--------------------------- Data Structures -----------------------------*/
@@ -316,18 +307,12 @@ static ssize_t bonding_store_slaves(struct device *d,
/* Set the slave's MTU to match the bond */
original_mtu = dev->mtu;
- if (dev->mtu != bond->dev->mtu) {
- if (dev->change_mtu) {
- res = dev->change_mtu(dev,
- bond->dev->mtu);
- if (res) {
- ret = res;
- goto out;
- }
- } else {
- dev->mtu = bond->dev->mtu;
- }
+ res = dev_set_mtu(dev, bond->dev->mtu);
+ if (res) {
+ ret = res;
+ goto out;
}
+
res = bond_enslave(bond->dev, dev);
bond_for_each_slave(bond, slave, i)
if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0)
@@ -356,11 +341,7 @@ static ssize_t bonding_store_slaves(struct device *d,
goto out;
}
/* set the slave MTU to the default */
- if (dev->change_mtu) {
- dev->change_mtu(dev, original_mtu);
- } else {
- dev->mtu = original_mtu;
- }
+ dev_set_mtu(dev, original_mtu);
}
else {
printk(KERN_ERR DRV_NAME ": unable to remove non-existent slave %s for bond %s.\n",
@@ -620,6 +601,8 @@ static ssize_t bonding_store_arp_interval(struct device *d,
": %s: Setting ARP monitoring interval to %d.\n",
bond->dev->name, new_value);
bond->params.arp_interval = new_value;
+ if (bond->params.arp_interval)
+ bond->dev->priv_flags |= IFF_MASTER_ARPMON;
if (bond->params.miimon) {
printk(KERN_INFO DRV_NAME
": %s: ARP monitoring cannot be used with MII monitoring. "
@@ -672,8 +655,8 @@ static ssize_t bonding_show_arp_targets(struct device *d,
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
if (bond->params.arp_targets[i])
- res += sprintf(buf + res, "%u.%u.%u.%u ",
- NIPQUAD(bond->params.arp_targets[i]));
+ res += sprintf(buf + res, "%pI4 ",
+ &bond->params.arp_targets[i]);
}
if (res)
buf[res-1] = '\n'; /* eat the leftover space */
@@ -695,8 +678,8 @@ static ssize_t bonding_store_arp_targets(struct device *d,
if (buf[0] == '+') {
if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
printk(KERN_ERR DRV_NAME
- ": %s: invalid ARP target %u.%u.%u.%u specified for addition\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: invalid ARP target %pI4 specified for addition\n",
+ bond->dev->name, &newtarget);
ret = -EINVAL;
goto out;
}
@@ -704,8 +687,8 @@ static ssize_t bonding_store_arp_targets(struct device *d,
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
if (targets[i] == newtarget) { /* duplicate */
printk(KERN_ERR DRV_NAME
- ": %s: ARP target %u.%u.%u.%u is already present\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: ARP target %pI4 is already present\n",
+ bond->dev->name, &newtarget);
if (done)
targets[i] = 0;
ret = -EINVAL;
@@ -713,8 +696,8 @@ static ssize_t bonding_store_arp_targets(struct device *d,
}
if (targets[i] == 0 && !done) {
printk(KERN_INFO DRV_NAME
- ": %s: adding ARP target %d.%d.%d.%d.\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: adding ARP target %pI4.\n",
+ bond->dev->name, &newtarget);
done = 1;
targets[i] = newtarget;
}
@@ -731,8 +714,8 @@ static ssize_t bonding_store_arp_targets(struct device *d,
else if (buf[0] == '-') {
if ((newtarget == 0) || (newtarget == htonl(INADDR_BROADCAST))) {
printk(KERN_ERR DRV_NAME
- ": %s: invalid ARP target %d.%d.%d.%d specified for removal\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: invalid ARP target %pI4 specified for removal\n",
+ bond->dev->name, &newtarget);
ret = -EINVAL;
goto out;
}
@@ -740,16 +723,16 @@ static ssize_t bonding_store_arp_targets(struct device *d,
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
if (targets[i] == newtarget) {
printk(KERN_INFO DRV_NAME
- ": %s: removing ARP target %d.%d.%d.%d.\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: removing ARP target %pI4.\n",
+ bond->dev->name, &newtarget);
targets[i] = 0;
done = 1;
}
}
if (!done) {
printk(KERN_INFO DRV_NAME
- ": %s: unable to remove nonexistent ARP target %d.%d.%d.%d.\n",
- bond->dev->name, NIPQUAD(newtarget));
+ ": %s: unable to remove nonexistent ARP target %pI4.\n",
+ bond->dev->name, &newtarget);
ret = -EINVAL;
goto out;
}
@@ -942,6 +925,53 @@ out:
}
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR, bonding_show_lacp, bonding_store_lacp);
+static ssize_t bonding_show_ad_select(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%s %d\n",
+ ad_select_tbl[bond->params.ad_select].modename,
+ bond->params.ad_select);
+}
+
+
+static ssize_t bonding_store_ad_select(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(d);
+
+ if (bond->dev->flags & IFF_UP) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to update ad_select because interface "
+ "is up.\n", bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ new_value = bond_parse_parm(buf, ad_select_tbl);
+
+ if (new_value != -1) {
+ bond->params.ad_select = new_value;
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting ad_select to %s (%d).\n",
+ bond->dev->name, ad_select_tbl[new_value].modename,
+ new_value);
+ } else {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid ad_select value %.*s.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
+ ret = -EINVAL;
+ }
+out:
+ return ret;
+}
+
+static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR, bonding_show_ad_select, bonding_store_ad_select);
+
/*
* Show and set the number of grat ARP to send after a failover event.
*/
@@ -981,6 +1011,47 @@ out:
return ret;
}
static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR, bonding_show_n_grat_arp, bonding_store_n_grat_arp);
+
+/*
+ * Show and set the number of unsolicted NA's to send after a failover event.
+ */
+static ssize_t bonding_show_n_unsol_na(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%d\n", bond->params.num_unsol_na);
+}
+
+static ssize_t bonding_store_n_unsol_na(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(d);
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no num_unsol_na value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (new_value < 0 || new_value > 255) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Invalid num_unsol_na value %d not in range 0-255; rejected.\n",
+ bond->dev->name, new_value);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ bond->params.num_unsol_na = new_value;
+ }
+out:
+ return ret;
+}
+static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR, bonding_show_n_unsol_na, bonding_store_n_unsol_na);
+
/*
* Show and set the MII monitor interval. There are two tricky bits
* here. First, if MII monitoring is activated, then we must disable
@@ -1039,6 +1110,7 @@ static ssize_t bonding_store_miimon(struct device *d,
"ARP monitoring. Disabling ARP monitoring...\n",
bond->dev->name);
bond->params.arp_interval = 0;
+ bond->dev->priv_flags &= ~IFF_MASTER_ARPMON;
if (bond->params.arp_validate) {
bond_unregister_arp(bond);
bond->params.arp_validate =
@@ -1391,13 +1463,11 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
{
int count = 0;
struct bonding *bond = to_bond(d);
- DECLARE_MAC_BUF(mac);
if (bond->params.mode == BOND_MODE_8023AD) {
struct ad_info ad_info;
if (!bond_3ad_get_active_agg_info(bond, &ad_info)) {
- count = sprintf(buf,"%s\n",
- print_mac(mac, ad_info.partner_system));
+ count = sprintf(buf, "%pM\n", ad_info.partner_system);
}
}
@@ -1417,8 +1487,10 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_downdelay.attr,
&dev_attr_updelay.attr,
&dev_attr_lacp_rate.attr,
+ &dev_attr_ad_select.attr,
&dev_attr_xmit_hash_policy.attr,
&dev_attr_num_grat_arp.attr,
+ &dev_attr_num_unsol_na.attr,
&dev_attr_miimon.attr,
&dev_attr_primary.attr,
&dev_attr_use_carrier.attr,
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ffb668dd6d3b..ca849d2adf98 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -19,23 +19,18 @@
#include <linux/proc_fs.h>
#include <linux/if_bonding.h>
#include <linux/kobject.h>
+#include <linux/in6.h>
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.3.0"
-#define DRV_RELDATE "June 10, 2008"
+#define DRV_VERSION "3.5.0"
+#define DRV_RELDATE "November 4, 2008"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
#define BOND_MAX_ARP_TARGETS 16
-#ifdef BONDING_DEBUG
-#define dprintk(fmt, args...) \
- printk(KERN_DEBUG \
- DRV_NAME ": %s() %d: " fmt, __func__, __LINE__ , ## args )
-#else
-#define dprintk(fmt, args...)
-#endif /* BONDING_DEBUG */
+extern struct list_head bond_dev_list;
#define IS_UP(dev) \
((((dev)->flags & IFF_UP) == IFF_UP) && \
@@ -126,6 +121,7 @@ struct bond_params {
int xmit_policy;
int miimon;
int num_grat_arp;
+ int num_unsol_na;
int arp_interval;
int arp_validate;
int use_carrier;
@@ -133,6 +129,7 @@ struct bond_params {
int updelay;
int downdelay;
int lacp_fast;
+ int ad_select;
char primary[IFNAMSIZ];
__be32 arp_targets[BOND_MAX_ARP_TARGETS];
};
@@ -148,6 +145,9 @@ struct vlan_entry {
struct list_head vlan_list;
__be32 vlan_ip;
unsigned short vlan_id;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr vlan_ipv6;
+#endif
};
struct slave {
@@ -195,6 +195,7 @@ struct bonding {
rwlock_t curr_slave_lock;
s8 kill_timers;
s8 send_grat_arp;
+ s8 send_unsol_na;
s8 setup_by_slave;
struct net_device_stats stats;
#ifdef CONFIG_PROC_FS
@@ -218,6 +219,9 @@ struct bonding {
struct delayed_work arp_work;
struct delayed_work alb_work;
struct delayed_work ad_work;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr master_ipv6;
+#endif
};
/**
@@ -245,7 +249,13 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
return NULL;
}
- return (struct bonding *)slave->dev->master->priv;
+ return (struct bonding *)netdev_priv(slave->dev->master);
+}
+
+static inline bool bond_is_lb(const struct bonding *bond)
+{
+ return bond->params.mode == BOND_MODE_TLB
+ || bond->params.mode == BOND_MODE_ALB;
}
#define BOND_FOM_NONE 0
@@ -275,7 +285,7 @@ static inline unsigned long slave_last_rx(struct bonding *bond,
static inline void bond_set_slave_inactive_flags(struct slave *slave)
{
- struct bonding *bond = slave->dev->master->priv;
+ struct bonding *bond = netdev_priv(slave->dev->master);
if (bond->params.mode != BOND_MODE_TLB &&
bond->params.mode != BOND_MODE_ALB)
slave->state = BOND_STATE_BACKUP;
@@ -327,7 +337,7 @@ void bond_mii_monitor(struct work_struct *);
void bond_loadbalance_arp_mon(struct work_struct *);
void bond_activebackup_arp_mon(struct work_struct *);
void bond_set_mode_ops(struct bonding *bond, int mode);
-int bond_parse_parm(const char *mode_arg, struct bond_parm_tbl *tbl);
+int bond_parse_parm(const char *mode_arg, const struct bond_parm_tbl *tbl);
void bond_select_active_slave(struct bonding *bond);
void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
void bond_register_arp(struct bonding *);
@@ -335,11 +345,35 @@ void bond_unregister_arp(struct bonding *);
/* exported from bond_main.c */
extern struct list_head bond_dev_list;
-extern struct bond_parm_tbl bond_lacp_tbl[];
-extern struct bond_parm_tbl bond_mode_tbl[];
-extern struct bond_parm_tbl xmit_hashtype_tbl[];
-extern struct bond_parm_tbl arp_validate_tbl[];
-extern struct bond_parm_tbl fail_over_mac_tbl[];
+extern const struct bond_parm_tbl bond_lacp_tbl[];
+extern const struct bond_parm_tbl bond_mode_tbl[];
+extern const struct bond_parm_tbl xmit_hashtype_tbl[];
+extern const struct bond_parm_tbl arp_validate_tbl[];
+extern const struct bond_parm_tbl fail_over_mac_tbl[];
+extern struct bond_params bonding_defaults;
+extern struct bond_parm_tbl ad_select_tbl[];
+
+/* exported from bond_sysfs.c */
+extern struct rw_semaphore bonding_rwsem;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+void bond_send_unsolicited_na(struct bonding *bond);
+void bond_register_ipv6_notifier(void);
+void bond_unregister_ipv6_notifier(void);
+#else
+static inline void bond_send_unsolicited_na(struct bonding *bond)
+{
+ return;
+}
+static inline void bond_register_ipv6_notifier(void)
+{
+ return;
+}
+static inline void bond_unregister_ipv6_notifier(void)
+{
+ return;
+}
+#endif
#endif /* _LINUX_BONDING_H */
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index 103f0f1df280..a10c1d7b3b0a 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -128,26 +128,30 @@ static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+static const struct net_device_ops vcan_netdev_ops = {
+ .ndo_start_xmit = vcan_tx,
+};
+
static void vcan_setup(struct net_device *dev)
{
- dev->type = ARPHRD_CAN;
- dev->mtu = sizeof(struct can_frame);
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->tx_queue_len = 0;
- dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_NOARP;
/* set flags according to driver capabilities */
if (echo)
dev->flags |= IFF_ECHO;
- dev->hard_start_xmit = vcan_tx;
- dev->destructor = free_netdev;
+ dev->netdev_ops = &vcan_netdev_ops;
+ dev->destructor = free_netdev;
}
static struct rtnl_link_ops vcan_link_ops __read_mostly = {
- .kind = "vcan",
- .setup = vcan_setup,
+ .kind = "vcan",
+ .setup = vcan_setup,
};
static __init int vcan_init_module(void)
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c
index 86909cfb14de..321f43d9f0e2 100644
--- a/drivers/net/cassini.c
+++ b/drivers/net/cassini.c
@@ -2347,7 +2347,7 @@ static int cas_rx_ringN(struct cas *cp, int ring, int budget)
drops = 0;
while (1) {
struct cas_rx_comp *rxc = rxcs + entry;
- struct sk_buff *skb;
+ struct sk_buff *uninitialized_var(skb);
int type, len;
u64 words[4];
int i, dring;
@@ -2405,7 +2405,6 @@ static int cas_rx_ringN(struct cas *cp, int ring, int budget)
cp->net_stats[ring].rx_packets++;
cp->net_stats[ring].rx_bytes += len;
spin_unlock(&cp->stat_lock[ring]);
- cp->dev->last_rx = jiffies;
next:
npackets++;
@@ -2507,7 +2506,7 @@ static irqreturn_t cas_interruptN(int irq, void *dev_id)
if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
#ifdef USE_NAPI
cas_mask_intr(cp);
- netif_rx_schedule(dev, &cp->napi);
+ netif_rx_schedule(&cp->napi);
#else
cas_rx_ringN(cp, ring, 0);
#endif
@@ -2558,7 +2557,7 @@ static irqreturn_t cas_interrupt1(int irq, void *dev_id)
if (status & INTR_RX_DONE_ALT) { /* handle rx separately */
#ifdef USE_NAPI
cas_mask_intr(cp);
- netif_rx_schedule(dev, &cp->napi);
+ netif_rx_schedule(&cp->napi);
#else
cas_rx_ringN(cp, 1, 0);
#endif
@@ -2614,7 +2613,7 @@ static irqreturn_t cas_interrupt(int irq, void *dev_id)
if (status & INTR_RX_DONE) {
#ifdef USE_NAPI
cas_mask_intr(cp);
- netif_rx_schedule(dev, &cp->napi);
+ netif_rx_schedule(&cp->napi);
#else
cas_rx_ringN(cp, 0, 0);
#endif
@@ -2692,7 +2691,7 @@ rx_comp:
#endif
spin_unlock_irqrestore(&cp->lock, flags);
if (enable_intr) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
cas_unmask_intr(cp);
}
return credits;
@@ -4988,7 +4987,6 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
int i, err, pci_using_dac;
u16 pci_cmd;
u8 orig_cacheline_size = 0, cas_cacheline_size = 0;
- DECLARE_MAC_BUF(mac);
if (cas_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -5201,12 +5199,12 @@ static int __devinit cas_init_one(struct pci_dev *pdev,
i = readl(cp->regs + REG_BIM_CFG);
printk(KERN_INFO "%s: Sun Cassini%s (%sbit/%sMHz PCI/%s) "
- "Ethernet[%d] %s\n", dev->name,
+ "Ethernet[%d] %pM\n", dev->name,
(cp->cas_flags & CAS_FLAG_REG_PLUS) ? "+" : "",
(i & BIM_CFG_32BIT) ? "32" : "64",
(i & BIM_CFG_66MHZ) ? "66" : "33",
(cp->phy_type == CAS_PHY_SERDES) ? "Fi" : "Cu", pdev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
pci_set_drvdata(pdev, dev);
cp->hw_running = 1;
diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c
index 638c9a27a7a6..9b6011e7678e 100644
--- a/drivers/net/chelsio/cxgb2.c
+++ b/drivers/net/chelsio/cxgb2.c
@@ -120,7 +120,7 @@ static const char pci_speed[][4] = {
*/
static void t1_set_rxmode(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
struct t1_rx_mode rm;
@@ -252,7 +252,7 @@ static void cxgb_down(struct adapter *adapter)
static int cxgb_open(struct net_device *dev)
{
int err;
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
int other_ports = adapter->open_device_map & PORT_MASK;
napi_enable(&adapter->napi);
@@ -272,7 +272,7 @@ static int cxgb_open(struct net_device *dev)
static int cxgb_close(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
struct cmac *mac = p->mac;
@@ -298,7 +298,7 @@ static int cxgb_close(struct net_device *dev)
static struct net_device_stats *t1_get_stats(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
struct net_device_stats *ns = &p->netstats;
const struct cmac_statistics *pstats;
@@ -346,14 +346,14 @@ static struct net_device_stats *t1_get_stats(struct net_device *dev)
static u32 get_msglevel(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
return adapter->msg_enable;
}
static void set_msglevel(struct net_device *dev, u32 val)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
adapter->msg_enable = val;
}
@@ -434,7 +434,7 @@ static int get_regs_len(struct net_device *dev)
static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
strcpy(info->driver, DRV_NAME);
strcpy(info->version, DRV_VERSION);
@@ -461,7 +461,7 @@ static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
u64 *data)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
const struct cmac_statistics *s;
const struct sge_intr_counts *t;
@@ -552,7 +552,7 @@ static inline void reg_block_dump(struct adapter *ap, void *buf,
static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *buf)
{
- struct adapter *ap = dev->priv;
+ struct adapter *ap = dev->ml_priv;
/*
* Version scheme: bits 0..9: chip version, bits 10..15: chip revision
@@ -574,7 +574,7 @@ static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
cmd->supported = p->link_config.supported;
@@ -634,7 +634,7 @@ static int speed_duplex_to_caps(int speed, int duplex)
static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
struct link_config *lc = &p->link_config;
@@ -669,7 +669,7 @@ static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static void get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0;
@@ -680,7 +680,7 @@ static void get_pauseparam(struct net_device *dev,
static int set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct port_info *p = &adapter->port[dev->if_port];
struct link_config *lc = &p->link_config;
@@ -709,14 +709,14 @@ static int set_pauseparam(struct net_device *dev,
static u32 get_rx_csum(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
return (adapter->flags & RX_CSUM_ENABLED) != 0;
}
static int set_rx_csum(struct net_device *dev, u32 data)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
if (data)
adapter->flags |= RX_CSUM_ENABLED;
@@ -727,7 +727,7 @@ static int set_rx_csum(struct net_device *dev, u32 data)
static int set_tso(struct net_device *dev, u32 value)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
if (!(adapter->flags & TSO_CAPABLE))
return value ? -EOPNOTSUPP : 0;
@@ -736,7 +736,7 @@ static int set_tso(struct net_device *dev, u32 value)
static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
e->rx_max_pending = MAX_RX_BUFFERS;
@@ -752,7 +752,7 @@ static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
if (e->rx_pending > MAX_RX_BUFFERS || e->rx_mini_pending ||
@@ -776,7 +776,7 @@ static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce;
@@ -787,7 +787,7 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
c->rx_coalesce_usecs = adapter->params.sge.rx_coalesce_usecs;
c->rate_sample_interval = adapter->params.sge.sample_interval_usecs;
@@ -797,7 +797,7 @@ static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
static int get_eeprom_len(struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
return t1_is_asic(adapter) ? EEPROM_SIZE : 0;
}
@@ -810,7 +810,7 @@ static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
{
int i;
u8 buf[EEPROM_SIZE] __attribute__((aligned(4)));
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
e->magic = EEPROM_MAGIC(adapter);
for (i = e->offset & ~3; i < e->offset + e->len; i += sizeof(u32))
@@ -848,7 +848,7 @@ static const struct ethtool_ops t1_ethtool_ops = {
static int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct mii_ioctl_data *data = if_mii(req);
switch (cmd) {
@@ -887,7 +887,7 @@ static int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
static int t1_change_mtu(struct net_device *dev, int new_mtu)
{
int ret;
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
if (!mac->ops->set_mtu)
@@ -902,7 +902,7 @@ static int t1_change_mtu(struct net_device *dev, int new_mtu)
static int t1_set_mac_addr(struct net_device *dev, void *p)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
struct sockaddr *addr = p;
@@ -915,10 +915,10 @@ static int t1_set_mac_addr(struct net_device *dev, void *p)
}
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-static void vlan_rx_register(struct net_device *dev,
+static void t1_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
spin_lock_irq(&adapter->async_lock);
adapter->vlan_grp = grp;
@@ -931,7 +931,7 @@ static void vlan_rx_register(struct net_device *dev,
static void t1_netpoll(struct net_device *dev)
{
unsigned long flags;
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
local_irq_save(flags);
t1_interrupt(adapter->pdev->irq, adapter);
@@ -1010,6 +1010,24 @@ void t1_fatal_err(struct adapter *adapter)
adapter->name);
}
+static const struct net_device_ops cxgb_netdev_ops = {
+ .ndo_open = cxgb_open,
+ .ndo_stop = cxgb_close,
+ .ndo_start_xmit = t1_start_xmit,
+ .ndo_get_stats = t1_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = t1_set_rxmode,
+ .ndo_do_ioctl = t1_ioctl,
+ .ndo_change_mtu = t1_change_mtu,
+ .ndo_set_mac_address = t1_set_mac_addr,
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ .ndo_vlan_rx_register = t1_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = t1_netpoll,
+#endif
+};
+
static int __devinit init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -1077,7 +1095,7 @@ static int __devinit init_one(struct pci_dev *pdev,
SET_NETDEV_DEV(netdev, &pdev->dev);
if (!adapter) {
- adapter = netdev->priv;
+ adapter = netdev_priv(netdev);
adapter->pdev = pdev;
adapter->port[0].dev = netdev; /* so we don't leak it */
@@ -1118,7 +1136,7 @@ static int __devinit init_one(struct pci_dev *pdev,
netdev->if_port = i;
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len - 1;
- netdev->priv = adapter;
+ netdev->ml_priv = adapter;
netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
netdev->features |= NETIF_F_LLTX;
@@ -1130,7 +1148,6 @@ static int __devinit init_one(struct pci_dev *pdev,
adapter->flags |= VLAN_ACCEL_CAPABLE;
netdev->features |=
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- netdev->vlan_rx_register = vlan_rx_register;
#endif
/* T204: disable TSO */
@@ -1140,19 +1157,10 @@ static int __devinit init_one(struct pci_dev *pdev,
}
}
- netdev->open = cxgb_open;
- netdev->stop = cxgb_close;
- netdev->hard_start_xmit = t1_start_xmit;
+ netdev->netdev_ops = &cxgb_netdev_ops;
netdev->hard_header_len += (adapter->flags & TSO_CAPABLE) ?
sizeof(struct cpl_tx_pkt_lso) : sizeof(struct cpl_tx_pkt);
- netdev->get_stats = t1_get_stats;
- netdev->set_multicast_list = t1_set_rxmode;
- netdev->do_ioctl = t1_ioctl;
- netdev->change_mtu = t1_change_mtu;
- netdev->set_mac_address = t1_set_mac_addr;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = t1_netpoll;
-#endif
+
netif_napi_add(netdev, &adapter->napi, t1_poll, 64);
SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
@@ -1382,7 +1390,7 @@ static inline void t1_sw_reset(struct pci_dev *pdev)
static void __devexit remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
int i;
for_each_port(adapter, i) {
diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c
index 7092df50ff78..d984b7995763 100644
--- a/drivers/net/chelsio/sge.c
+++ b/drivers/net/chelsio/sge.c
@@ -1381,7 +1381,6 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len)
st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id());
skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev);
- skb->dev->last_rx = jiffies;
if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
skb->protocol == htons(ETH_P_IP) &&
(skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) {
@@ -1610,11 +1609,10 @@ static int process_pure_responses(struct adapter *adapter)
int t1_poll(struct napi_struct *napi, int budget)
{
struct adapter *adapter = container_of(napi, struct adapter, napi);
- struct net_device *dev = adapter->port[0].dev;
int work_done = process_responses(adapter, budget);
if (likely(work_done < budget)) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
writel(adapter->sge->respQ.cidx,
adapter->regs + A_SG_SLEEPING);
}
@@ -1628,13 +1626,11 @@ irqreturn_t t1_interrupt(int irq, void *data)
int handled;
if (likely(responses_pending(adapter))) {
- struct net_device *dev = sge->netdev;
-
writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
if (napi_schedule_prep(&adapter->napi)) {
if (process_pure_responses(adapter))
- __netif_rx_schedule(dev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
else {
/* no data, no NAPI needed */
writel(sge->respQ.cidx, adapter->regs + A_SG_SLEEPING);
@@ -1782,7 +1778,7 @@ static inline int eth_hdr_len(const void *data)
*/
int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct adapter *adapter = dev->priv;
+ struct adapter *adapter = dev->ml_priv;
struct sge *sge = adapter->sge;
struct sge_port_stats *st = per_cpu_ptr(sge->port_stats[dev->if_port],
smp_processor_id());
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index 017a5361b980..f66548751c38 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -428,7 +428,7 @@ static int cpmac_poll(struct napi_struct *napi, int budget)
printk(KERN_WARNING "%s: rx: polling, but no queue\n",
priv->dev->name);
spin_unlock(&priv->rx_lock);
- netif_rx_complete(priv->dev, napi);
+ netif_rx_complete(napi);
return 0;
}
@@ -514,7 +514,7 @@ static int cpmac_poll(struct napi_struct *napi, int budget)
if (processed == 0) {
/* we ran out of packets to read,
* revert to interrupt-driven mode */
- netif_rx_complete(priv->dev, napi);
+ netif_rx_complete(napi);
cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1);
return 0;
}
@@ -536,7 +536,7 @@ fatal_error:
}
spin_unlock(&priv->rx_lock);
- netif_rx_complete(priv->dev, napi);
+ netif_rx_complete(napi);
netif_tx_stop_all_queues(priv->dev);
napi_disable(&priv->napi);
@@ -802,9 +802,9 @@ static irqreturn_t cpmac_irq(int irq, void *dev_id)
if (status & MAC_INT_RX) {
queue = (status >> 8) & 7;
- if (netif_rx_schedule_prep(dev, &priv->napi)) {
+ if (netif_rx_schedule_prep(&priv->napi)) {
cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 1 << queue);
- __netif_rx_schedule(dev, &priv->napi);
+ __netif_rx_schedule(&priv->napi);
}
}
@@ -1103,7 +1103,6 @@ static int __devinit cpmac_probe(struct platform_device *pdev)
struct cpmac_priv *priv;
struct net_device *dev;
struct plat_cpmac_data *pdata;
- DECLARE_MAC_BUF(mac);
pdata = pdev->dev.platform_data;
@@ -1180,8 +1179,8 @@ static int __devinit cpmac_probe(struct platform_device *pdev)
if (netif_msg_probe(priv)) {
printk(KERN_INFO
"cpmac: device %s (regs: %p, irq: %d, phy: %s, "
- "mac: %s)\n", dev->name, (void *)mem->start, dev->irq,
- priv->phy_name, print_mac(mac, dev->dev_addr));
+ "mac: %pM)\n", dev->name, (void *)mem->start, dev->irq,
+ priv->phy_name, dev->dev_addr);
}
return 0;
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index 7e8a63106bdf..c9806c58b2fd 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -419,7 +419,6 @@ e100_set_mac_address(struct net_device *dev, void *p)
{
struct net_local *np = netdev_priv(dev);
struct sockaddr *addr = p;
- DECLARE_MAC_BUF(mac);
spin_lock(&np->lock); /* preemption protection */
@@ -440,8 +439,7 @@ e100_set_mac_address(struct net_device *dev, void *p)
/* show it in the log as well */
- printk(KERN_INFO "%s: changed MAC to %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: changed MAC to %pM\n", dev->name, dev->dev_addr);
spin_unlock(&np->lock);
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 7107620f615d..ff6497658a45 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -170,11 +170,7 @@ static char version[] __initdata =
/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps
them to system IRQ numbers. This mapping is card specific and is set to
the configuration of the Cirrus Eval board for this chip. */
-#ifdef CONFIG_ARCH_CLPS7500
-static unsigned int netcard_portlist[] __used __initdata =
- { 0x80090303, 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
-static unsigned int cs8900_irq_map[] = {12,0,0,0};
-#elif defined(CONFIG_SH_HICOSH4)
+#if defined(CONFIG_SH_HICOSH4)
static unsigned int netcard_portlist[] __used __initdata =
{ 0x0300, 0};
static unsigned int cs8900_irq_map[] = {1,0,0,0};
@@ -521,7 +517,6 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
unsigned rev_type = 0;
int eeprom_buff[CHKSUM_LEN];
int retval;
- DECLARE_MAC_BUF(mac);
/* Initialize the device structure. */
if (!modular) {
@@ -846,7 +841,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
}
/* print the ethernet address. */
- printk(", MAC %s", print_mac(mac, dev->dev_addr));
+ printk(", MAC %pM", dev->dev_addr);
dev->open = net_open;
dev->stop = net_close;
@@ -1025,14 +1020,13 @@ skip_this_frame:
}
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += length;
}
#endif /* ALLOW_DMA */
-void __init reset_chip(struct net_device *dev)
+static void __init reset_chip(struct net_device *dev)
{
#if !defined(CONFIG_MACH_MX31ADS)
#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
@@ -1719,7 +1713,6 @@ net_rx(struct net_device *dev)
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += length;
}
@@ -1817,11 +1810,10 @@ static int set_mac_address(struct net_device *dev, void *p)
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- if (net_debug) {
- DECLARE_MAC_BUF(mac);
- printk("%s: Setting MAC address to %s.\n",
- dev->name, print_mac(mac, dev->dev_addr));
- }
+ if (net_debug)
+ printk("%s: Setting MAC address to %pM.\n",
+ dev->name, dev->dev_addr);
+
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index bc8e2413abd2..5b346f9eaa8b 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -63,6 +63,7 @@ struct port_info {
struct link_config link_config;
struct net_device_stats netstats;
int activity;
+ __be32 iscsi_ipv4addr;
};
enum { /* adapter flags */
@@ -196,6 +197,7 @@ struct sge_qset { /* an SGE queue set */
int lro_frag_len;
void *lro_va;
struct net_device *netdev;
+ struct netdev_queue *tx_q; /* associated netdev TX queue */
unsigned long txq_stopped; /* which Tx queues are stopped */
struct timer_list tx_reclaim_timer; /* reclaims TX buffers */
unsigned long port_stats[SGE_PSTAT_MAX];
@@ -294,7 +296,8 @@ int t3_mgmt_tx(struct adapter *adap, struct sk_buff *skb);
void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p);
int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
int irq_vec_idx, const struct qset_params *p,
- int ntxq, struct net_device *dev);
+ int ntxq, struct net_device *dev,
+ struct netdev_queue *netdevq);
int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx,
unsigned char *data);
irqreturn_t t3_sge_intr_msix(int irq, void *cookie);
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h
index e312d315a42d..db4f4f575b6a 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -714,7 +714,7 @@ int t3_seeprom_read(struct adapter *adapter, u32 addr, __le32 *data);
int t3_seeprom_write(struct adapter *adapter, u32 addr, __le32 data);
int t3_seeprom_wp(struct adapter *adapter, int enable);
int t3_get_tp_version(struct adapter *adapter, u32 *vers);
-int t3_check_tpsram_version(struct adapter *adapter, int *must_load);
+int t3_check_tpsram_version(struct adapter *adapter);
int t3_check_tpsram(struct adapter *adapter, const u8 *tp_ram,
unsigned int size);
int t3_set_proto_sram(struct adapter *adap, const u8 *data);
@@ -722,7 +722,7 @@ int t3_read_flash(struct adapter *adapter, unsigned int addr,
unsigned int nwords, u32 *data, int byte_oriented);
int t3_load_fw(struct adapter *adapter, const u8 * fw_data, unsigned int size);
int t3_get_fw_version(struct adapter *adapter, u32 *vers);
-int t3_check_fw_version(struct adapter *adapter, int *must_load);
+int t3_check_fw_version(struct adapter *adapter);
int t3_init_hw(struct adapter *adapter, u32 fw_params);
void mac_prep(struct cmac *mac, struct adapter *adapter, int index);
void early_hw_init(struct adapter *adapter, const struct adapter_info *ai);
diff --git a/drivers/net/cxgb3/cxgb3_ctl_defs.h b/drivers/net/cxgb3/cxgb3_ctl_defs.h
index 1d8d46eb3c96..369fe711fd7f 100644
--- a/drivers/net/cxgb3/cxgb3_ctl_defs.h
+++ b/drivers/net/cxgb3/cxgb3_ctl_defs.h
@@ -57,6 +57,9 @@ enum {
RDMA_GET_MIB = 19,
GET_RX_PAGE_INFO = 50,
+ GET_ISCSI_IPV4ADDR = 51,
+
+ GET_EMBEDDED_INFO = 70,
};
/*
@@ -86,6 +89,12 @@ struct iff_mac {
u16 vlan_tag;
};
+/* Structure used to request a port's iSCSI IPv4 address */
+struct iscsi_ipv4addr {
+ struct net_device *dev; /* the net_device */
+ __be32 ipv4addr; /* the return iSCSI IPv4 address */
+};
+
struct pci_dev;
/*
@@ -169,4 +178,12 @@ struct ofld_page_info {
unsigned int page_size; /* Page size, should be a power of 2 */
unsigned int num; /* Number of pages */
};
+
+/*
+ * Structure used to get firmware and protocol engine versions.
+ */
+struct ch_embedded_info {
+ u32 fw_vers;
+ u32 tp_vers;
+};
#endif /* _CXGB3_OFFLOAD_CTL_DEFS_H */
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index 2c341f83d327..2847f947499d 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -494,6 +494,36 @@ static void enable_all_napi(struct adapter *adap)
}
/**
+ * set_qset_lro - Turn a queue set's LRO capability on and off
+ * @dev: the device the qset is attached to
+ * @qset_idx: the queue set index
+ * @val: the LRO switch
+ *
+ * Sets LRO on or off for a particular queue set.
+ * the device's features flag is updated to reflect the LRO
+ * capability when all queues belonging to the device are
+ * in the same state.
+ */
+static void set_qset_lro(struct net_device *dev, int qset_idx, int val)
+{
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adapter = pi->adapter;
+ int i, lro_on = 1;
+
+ adapter->params.sge.qset[qset_idx].lro = !!val;
+ adapter->sge.qs[qset_idx].lro_enabled = !!val;
+
+ /* let ethtool report LRO on only if all queues are LRO enabled */
+ for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; ++i)
+ lro_on &= adapter->params.sge.qset[i].lro;
+
+ if (lro_on)
+ dev->features |= NETIF_F_LRO;
+ else
+ dev->features &= ~NETIF_F_LRO;
+}
+
+/**
* setup_sge_qsets - configure SGE Tx/Rx/response queues
* @adap: the adapter
*
@@ -516,12 +546,12 @@ static int setup_sge_qsets(struct adapter *adap)
pi->qs = &adap->sge.qs[pi->first_qset];
for (j = pi->first_qset; j < pi->first_qset + pi->nqsets;
++j, ++qset_idx) {
- if (!pi->rx_csum_offload)
- adap->params.sge.qset[qset_idx].lro = 0;
+ set_qset_lro(dev, qset_idx, pi->rx_csum_offload);
err = t3_sge_alloc_qset(adap, qset_idx, 1,
(adap->flags & USING_MSIX) ? qset_idx + 1 :
irq_idx,
- &adap->params.sge.qset[qset_idx], ntxq, dev);
+ &adap->params.sge.qset[qset_idx], ntxq, dev,
+ netdev_get_tx_queue(dev, j));
if (err) {
t3_stop_sge_timers(adap);
t3_free_sge_resources(adap);
@@ -824,8 +854,8 @@ static int bind_qsets(struct adapter *adap)
return err;
}
-#define FW_FNAME "t3fw-%d.%d.%d.bin"
-#define TPSRAM_NAME "t3%c_protocol_sram-%d.%d.%d.bin"
+#define FW_FNAME "cxgb3/t3fw-%d.%d.%d.bin"
+#define TPSRAM_NAME "cxgb3/t3%c_psram-%d.%d.%d.bin"
static int upgrade_fw(struct adapter *adap)
{
@@ -928,21 +958,22 @@ release_tpsram:
static int cxgb_up(struct adapter *adap)
{
int err;
- int must_load;
if (!(adap->flags & FULL_INIT_DONE)) {
- err = t3_check_fw_version(adap, &must_load);
+ err = t3_check_fw_version(adap);
if (err == -EINVAL) {
err = upgrade_fw(adap);
- if (err && must_load)
- goto out;
+ CH_WARN(adap, "FW upgrade to %d.%d.%d %s\n",
+ FW_VERSION_MAJOR, FW_VERSION_MINOR,
+ FW_VERSION_MICRO, err ? "failed" : "succeeded");
}
- err = t3_check_tpsram_version(adap, &must_load);
+ err = t3_check_tpsram_version(adap);
if (err == -EINVAL) {
err = update_tpsram(adap);
- if (err && must_load)
- goto out;
+ CH_WARN(adap, "TP upgrade to %d.%d.%d %s\n",
+ TP_VERSION_MAJOR, TP_VERSION_MINOR,
+ TP_VERSION_MICRO, err ? "failed" : "succeeded");
}
/*
@@ -1136,9 +1167,10 @@ static int cxgb_open(struct net_device *dev)
"Could not initialize offload capabilities\n");
}
+ dev->real_num_tx_queues = pi->nqsets;
link_start(dev);
t3_port_intr_enable(adapter, pi->port_id);
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
if (!other_ports)
schedule_chk_task(adapter);
@@ -1151,7 +1183,7 @@ static int cxgb_close(struct net_device *dev)
struct adapter *adapter = pi->adapter;
t3_port_intr_disable(adapter, pi->port_id);
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
pi->phy.ops->power_down(&pi->phy, 1);
netif_carrier_off(dev);
t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX);
@@ -1634,13 +1666,10 @@ static int set_rx_csum(struct net_device *dev, u32 data)
p->rx_csum_offload = data;
if (!data) {
- struct adapter *adap = p->adapter;
int i;
- for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) {
- adap->params.sge.qset[i].lro = 0;
- adap->sge.qs[i].lro_enabled = 0;
- }
+ for (i = p->first_qset; i < p->first_qset + p->nqsets; i++)
+ set_qset_lro(dev, i, 0);
}
return 0;
}
@@ -1795,6 +1824,25 @@ static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
memset(&wol->sopass, 0, sizeof(wol->sopass));
}
+static int cxgb3_set_flags(struct net_device *dev, u32 data)
+{
+ struct port_info *pi = netdev_priv(dev);
+ int i;
+
+ if (data & ETH_FLAG_LRO) {
+ if (!pi->rx_csum_offload)
+ return -EINVAL;
+
+ for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++)
+ set_qset_lro(dev, i, 1);
+
+ } else
+ for (i = pi->first_qset; i < pi->first_qset + pi->nqsets; i++)
+ set_qset_lro(dev, i, 0);
+
+ return 0;
+}
+
static const struct ethtool_ops cxgb_ethtool_ops = {
.get_settings = get_settings,
.set_settings = set_settings,
@@ -1824,6 +1872,8 @@ static const struct ethtool_ops cxgb_ethtool_ops = {
.get_regs = get_regs,
.get_wol = get_wol,
.set_tso = ethtool_op_set_tso,
+ .get_flags = ethtool_op_get_flags,
+ .set_flags = cxgb3_set_flags,
};
static int in_range(int val, int lo, int hi)
@@ -1940,11 +1990,9 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr)
}
}
}
- if (t.lro >= 0) {
- struct sge_qset *qs = &adapter->sge.qs[t.qset_idx];
- q->lro = t.lro;
- qs->lro_enabled = t.lro;
- }
+ if (t.lro >= 0)
+ set_qset_lro(dev, t.qset_idx, t.lro);
+
break;
}
case CHELSIO_GET_QSET_PARAMS:{
@@ -2783,6 +2831,22 @@ static void __devinit print_port_info(struct adapter *adap,
}
}
+static const struct net_device_ops cxgb_netdev_ops = {
+ .ndo_open = cxgb_open,
+ .ndo_stop = cxgb_close,
+ .ndo_start_xmit = t3_eth_xmit,
+ .ndo_get_stats = cxgb_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = cxgb_set_rxmode,
+ .ndo_do_ioctl = cxgb_ioctl,
+ .ndo_change_mtu = cxgb_change_mtu,
+ .ndo_set_mac_address = cxgb_set_mac_addr,
+ .ndo_vlan_rx_register = vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cxgb_netpoll,
+#endif
+};
+
static int __devinit init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -2871,7 +2935,7 @@ static int __devinit init_one(struct pci_dev *pdev,
for (i = 0; i < ai->nports; ++i) {
struct net_device *netdev;
- netdev = alloc_etherdev(sizeof(struct port_info));
+ netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS);
if (!netdev) {
err = -ENOMEM;
goto out_free_dev;
@@ -2885,6 +2949,7 @@ static int __devinit init_one(struct pci_dev *pdev,
pi->rx_csum_offload = 1;
pi->port_id = i;
netif_carrier_off(netdev);
+ netif_tx_stop_all_queues(netdev);
netdev->irq = pdev->irq;
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len - 1;
@@ -2894,20 +2959,7 @@ static int __devinit init_one(struct pci_dev *pdev,
netdev->features |= NETIF_F_HIGHDMA;
netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- netdev->vlan_rx_register = vlan_rx_register;
-
- netdev->open = cxgb_open;
- netdev->stop = cxgb_close;
- netdev->hard_start_xmit = t3_eth_xmit;
- netdev->get_stats = cxgb_get_stats;
- netdev->set_multicast_list = cxgb_set_rxmode;
- netdev->do_ioctl = cxgb_ioctl;
- netdev->change_mtu = cxgb_change_mtu;
- netdev->set_mac_address = cxgb_set_mac_addr;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = cxgb_netpoll;
-#endif
-
+ netdev->netdev_ops = &cxgb_netdev_ops;
SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops);
}
diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c
index 265aa8a15afa..2d7f69aff1d9 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -182,7 +182,9 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
static int cxgb_ulp_iscsi_ctl(struct adapter *adapter, unsigned int req,
void *data)
{
+ int i;
int ret = 0;
+ unsigned int val = 0;
struct ulp_iscsi_info *uiip = data;
switch (req) {
@@ -191,32 +193,55 @@ static int cxgb_ulp_iscsi_ctl(struct adapter *adapter, unsigned int req,
uiip->llimit = t3_read_reg(adapter, A_ULPRX_ISCSI_LLIMIT);
uiip->ulimit = t3_read_reg(adapter, A_ULPRX_ISCSI_ULIMIT);
uiip->tagmask = t3_read_reg(adapter, A_ULPRX_ISCSI_TAGMASK);
+
+ val = t3_read_reg(adapter, A_ULPRX_ISCSI_PSZ);
+ for (i = 0; i < 4; i++, val >>= 8)
+ uiip->pgsz_factor[i] = val & 0xFF;
+
+ val = t3_read_reg(adapter, A_TP_PARA_REG7);
+ uiip->max_txsz =
+ uiip->max_rxsz = min((val >> S_PMMAXXFERLEN0)&M_PMMAXXFERLEN0,
+ (val >> S_PMMAXXFERLEN1)&M_PMMAXXFERLEN1);
/*
* On tx, the iscsi pdu has to be <= tx page size and has to
* fit into the Tx PM FIFO.
*/
- uiip->max_txsz = min(adapter->params.tp.tx_pg_size,
- t3_read_reg(adapter, A_PM1_TX_CFG) >> 17);
- /* on rx, the iscsi pdu has to be < rx page size and the
- whole pdu + cpl headers has to fit into one sge buffer */
- uiip->max_rxsz = min_t(unsigned int,
- adapter->params.tp.rx_pg_size,
- (adapter->sge.qs[0].fl[1].buf_size -
- sizeof(struct cpl_rx_data) * 2 -
- sizeof(struct cpl_rx_data_ddp)));
+ val = min(adapter->params.tp.tx_pg_size,
+ t3_read_reg(adapter, A_PM1_TX_CFG) >> 17);
+ uiip->max_txsz = min(val, uiip->max_txsz);
+
+ /* set MaxRxData to 16224 */
+ val = t3_read_reg(adapter, A_TP_PARA_REG2);
+ if ((val >> S_MAXRXDATA) != 0x3f60) {
+ val &= (M_RXCOALESCESIZE << S_RXCOALESCESIZE);
+ val |= V_MAXRXDATA(0x3f60);
+ printk(KERN_INFO
+ "%s, iscsi set MaxRxData to 16224 (0x%x).\n",
+ adapter->name, val);
+ t3_write_reg(adapter, A_TP_PARA_REG2, val);
+ }
+
+ /*
+ * on rx, the iscsi pdu has to be < rx page size and the
+ * the max rx data length programmed in TP
+ */
+ val = min(adapter->params.tp.rx_pg_size,
+ ((t3_read_reg(adapter, A_TP_PARA_REG2)) >>
+ S_MAXRXDATA) & M_MAXRXDATA);
+ uiip->max_rxsz = min(val, uiip->max_rxsz);
break;
case ULP_ISCSI_SET_PARAMS:
t3_write_reg(adapter, A_ULPRX_ISCSI_TAGMASK, uiip->tagmask);
- /* set MaxRxData and MaxCoalesceSize to 16224 */
- t3_write_reg(adapter, A_TP_PARA_REG2, 0x3f603f60);
/* program the ddp page sizes */
- {
- int i;
- unsigned int val = 0;
- for (i = 0; i < 4; i++)
- val |= (uiip->pgsz_factor[i] & 0xF) << (8 * i);
- if (val)
- t3_write_reg(adapter, A_ULPRX_ISCSI_PSZ, val);
+ for (i = 0; i < 4; i++)
+ val |= (uiip->pgsz_factor[i] & 0xF) << (8 * i);
+ if (val && (val != t3_read_reg(adapter, A_ULPRX_ISCSI_PSZ))) {
+ printk(KERN_INFO
+ "%s, setting iscsi pgsz 0x%x, %u,%u,%u,%u.\n",
+ adapter->name, val, uiip->pgsz_factor[0],
+ uiip->pgsz_factor[1], uiip->pgsz_factor[2],
+ uiip->pgsz_factor[3]);
+ t3_write_reg(adapter, A_ULPRX_ISCSI_PSZ, val);
}
break;
default:
@@ -407,6 +432,21 @@ static int cxgb_offload_ctl(struct t3cdev *tdev, unsigned int req, void *data)
rx_page_info->page_size = tp->rx_pg_size;
rx_page_info->num = tp->rx_num_pgs;
break;
+ case GET_ISCSI_IPV4ADDR: {
+ struct iscsi_ipv4addr *p = data;
+ struct port_info *pi = netdev_priv(p->dev);
+ p->ipv4addr = pi->iscsi_ipv4addr;
+ break;
+ }
+ case GET_EMBEDDED_INFO: {
+ struct ch_embedded_info *e = data;
+
+ spin_lock(&adapter->stats_lock);
+ t3_get_fw_version(adapter, &e->fw_vers);
+ t3_get_tp_version(adapter, &e->tp_vers);
+ spin_unlock(&adapter->stats_lock);
+ break;
+ }
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index c6480be0bc1f..6c641a889471 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -36,6 +36,7 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/dma-mapping.h>
+#include <net/arp.h>
#include "common.h"
#include "regs.h"
#include "sge_defs.h"
@@ -549,16 +550,15 @@ static void *alloc_ring(struct pci_dev *pdev, size_t nelem, size_t elem_size,
if (!p)
return NULL;
- if (sw_size) {
+ if (sw_size && metadata) {
s = kcalloc(nelem, sw_size, GFP_KERNEL);
if (!s) {
dma_free_coherent(&pdev->dev, len, p, *phys);
return NULL;
}
- }
- if (metadata)
*(void **)metadata = s;
+ }
memset(p, 0, len);
return p;
}
@@ -1121,10 +1121,10 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb,
htonl(V_WR_TID(q->token)));
}
-static inline void t3_stop_queue(struct net_device *dev, struct sge_qset *qs,
- struct sge_txq *q)
+static inline void t3_stop_tx_queue(struct netdev_queue *txq,
+ struct sge_qset *qs, struct sge_txq *q)
{
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
set_bit(TXQ_ETH, &qs->txq_stopped);
q->stops++;
}
@@ -1138,11 +1138,13 @@ static inline void t3_stop_queue(struct net_device *dev, struct sge_qset *qs,
*/
int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ int qidx;
unsigned int ndesc, pidx, credits, gen, compl;
const struct port_info *pi = netdev_priv(dev);
struct adapter *adap = pi->adapter;
- struct sge_qset *qs = pi->qs;
- struct sge_txq *q = &qs->txq[TXQ_ETH];
+ struct netdev_queue *txq;
+ struct sge_qset *qs;
+ struct sge_txq *q;
/*
* The chip min packet length is 9 octets but play safe and reject
@@ -1153,6 +1155,11 @@ int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+ qidx = skb_get_queue_mapping(skb);
+ qs = &pi->qs[qidx];
+ q = &qs->txq[TXQ_ETH];
+ txq = netdev_get_tx_queue(dev, qidx);
+
spin_lock(&q->lock);
reclaim_completed_tx(adap, q);
@@ -1160,7 +1167,7 @@ int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
ndesc = calc_tx_descs(skb);
if (unlikely(credits < ndesc)) {
- t3_stop_queue(dev, qs, q);
+ t3_stop_tx_queue(txq, qs, q);
dev_err(&adap->pdev->dev,
"%s: Tx ring %u full while queue awake!\n",
dev->name, q->cntxt_id & 7);
@@ -1170,12 +1177,12 @@ int t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
q->in_use += ndesc;
if (unlikely(credits - ndesc < q->stop_thres)) {
- t3_stop_queue(dev, qs, q);
+ t3_stop_tx_queue(txq, qs, q);
if (should_restart_tx(q) &&
test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) {
q->restarts++;
- netif_wake_queue(dev);
+ netif_tx_wake_queue(txq);
}
}
@@ -1839,7 +1846,7 @@ static void restart_tx(struct sge_qset *qs)
test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) {
qs->txq[TXQ_ETH].restarts++;
if (netif_running(qs->netdev))
- netif_wake_queue(qs->netdev);
+ netif_tx_wake_queue(qs->tx_q);
}
if (test_bit(TXQ_OFLD, &qs->txq_stopped) &&
@@ -1857,6 +1864,54 @@ static void restart_tx(struct sge_qset *qs)
}
/**
+ * cxgb3_arp_process - process an ARP request probing a private IP address
+ * @adapter: the adapter
+ * @skb: the skbuff containing the ARP request
+ *
+ * Check if the ARP request is probing the private IP address
+ * dedicated to iSCSI, generate an ARP reply if so.
+ */
+static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct port_info *pi;
+ struct arphdr *arp;
+ unsigned char *arp_ptr;
+ unsigned char *sha;
+ __be32 sip, tip;
+
+ if (!dev)
+ return;
+
+ skb_reset_network_header(skb);
+ arp = arp_hdr(skb);
+
+ if (arp->ar_op != htons(ARPOP_REQUEST))
+ return;
+
+ arp_ptr = (unsigned char *)(arp + 1);
+ sha = arp_ptr;
+ arp_ptr += dev->addr_len;
+ memcpy(&sip, arp_ptr, sizeof(sip));
+ arp_ptr += sizeof(sip);
+ arp_ptr += dev->addr_len;
+ memcpy(&tip, arp_ptr, sizeof(tip));
+
+ pi = netdev_priv(dev);
+ if (tip != pi->iscsi_ipv4addr)
+ return;
+
+ arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
+ dev->dev_addr, sha);
+
+}
+
+static inline int is_arp(struct sk_buff *skb)
+{
+ return skb->protocol == htons(ETH_P_ARP);
+}
+
+/**
* rx_eth - process an ingress ethernet packet
* @adap: the adapter
* @rq: the response queue that received the packet
@@ -1876,11 +1931,10 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
skb_pull(skb, sizeof(*p) + pad);
skb->protocol = eth_type_trans(skb, adap->port[p->iff]);
- skb->dev->last_rx = jiffies;
pi = netdev_priv(skb->dev);
if (pi->rx_csum_offload && p->csum_valid && p->csum == htons(0xffff) &&
!p->fragment) {
- rspq_to_qset(rq)->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++;
+ qs->port_stats[SGE_PSTAT_RX_CSUM_GOOD]++;
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else
skb->ip_summed = CHECKSUM_NONE;
@@ -1895,16 +1949,28 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
grp,
ntohs(p->vlan),
p);
- else
+ else {
+ if (unlikely(pi->iscsi_ipv4addr &&
+ is_arp(skb))) {
+ unsigned short vtag = ntohs(p->vlan) &
+ VLAN_VID_MASK;
+ skb->dev = vlan_group_get_device(grp,
+ vtag);
+ cxgb3_arp_process(adap, skb);
+ }
__vlan_hwaccel_rx(skb, grp, ntohs(p->vlan),
rq->polling);
+ }
else
dev_kfree_skb_any(skb);
} else if (rq->polling) {
if (lro)
lro_receive_skb(&qs->lro_mgr, skb, p);
- else
+ else {
+ if (unlikely(pi->iscsi_ipv4addr && is_arp(skb)))
+ cxgb3_arp_process(adap, skb);
netif_receive_skb(skb);
+ }
} else
netif_rx(skb);
}
@@ -2308,7 +2374,7 @@ next_fl:
static inline int is_pure_response(const struct rsp_desc *r)
{
- u32 n = ntohl(r->flags) & (F_RSPD_ASYNC_NOTIF | F_RSPD_IMM_DATA_VALID);
+ __be32 n = r->flags & htonl(F_RSPD_ASYNC_NOTIF | F_RSPD_IMM_DATA_VALID);
return (n | r->len_cq) == 0;
}
@@ -2826,6 +2892,7 @@ void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p)
* @p: configuration parameters for this queue set
* @ntxq: number of Tx queues for the queue set
* @netdev: net device associated with this queue set
+ * @netdevq: net device TX queue associated with this queue set
*
* Allocate resources and initialize an SGE queue set. A queue set
* comprises a response queue, two Rx free-buffer queues, and up to 3
@@ -2834,7 +2901,8 @@ void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p)
*/
int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
int irq_vec_idx, const struct qset_params *p,
- int ntxq, struct net_device *dev)
+ int ntxq, struct net_device *dev,
+ struct netdev_queue *netdevq)
{
int i, avail, ret = -ENOMEM;
struct sge_qset *q = &adapter->sge.qs[id];
@@ -2970,6 +3038,7 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports,
q->adap = adapter;
q->netdev = dev;
+ q->tx_q = netdevq;
t3_update_qset_coalesce(q, p);
init_lro_mgr(q, lro_mgr);
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index 9a0898b0dbce..2d1433077a8e 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -925,11 +925,10 @@ int t3_get_tp_version(struct adapter *adapter, u32 *vers)
/**
* t3_check_tpsram_version - read the tp sram version
* @adapter: the adapter
- * @must_load: set to 1 if loading a new microcode image is required
*
* Reads the protocol sram version from flash.
*/
-int t3_check_tpsram_version(struct adapter *adapter, int *must_load)
+int t3_check_tpsram_version(struct adapter *adapter)
{
int ret;
u32 vers;
@@ -938,7 +937,6 @@ int t3_check_tpsram_version(struct adapter *adapter, int *must_load)
if (adapter->params.rev == T3_REV_A)
return 0;
- *must_load = 1;
ret = t3_get_tp_version(adapter, &vers);
if (ret)
@@ -949,13 +947,7 @@ int t3_check_tpsram_version(struct adapter *adapter, int *must_load)
if (major == TP_VERSION_MAJOR && minor == TP_VERSION_MINOR)
return 0;
-
- if (major != TP_VERSION_MAJOR)
- CH_ERR(adapter, "found wrong TP version (%u.%u), "
- "driver needs version %d.%d\n", major, minor,
- TP_VERSION_MAJOR, TP_VERSION_MINOR);
else {
- *must_load = 0;
CH_ERR(adapter, "found wrong TP version (%u.%u), "
"driver compiled for version %d.%d\n", major, minor,
TP_VERSION_MAJOR, TP_VERSION_MINOR);
@@ -1012,18 +1004,16 @@ int t3_get_fw_version(struct adapter *adapter, u32 *vers)
/**
* t3_check_fw_version - check if the FW is compatible with this driver
* @adapter: the adapter
- * @must_load: set to 1 if loading a new FW image is required
-
+ *
* Checks if an adapter's FW is compatible with the driver. Returns 0
* if the versions are compatible, a negative error otherwise.
*/
-int t3_check_fw_version(struct adapter *adapter, int *must_load)
+int t3_check_fw_version(struct adapter *adapter)
{
int ret;
u32 vers;
unsigned int type, major, minor;
- *must_load = 1;
ret = t3_get_fw_version(adapter, &vers);
if (ret)
return ret;
@@ -1035,17 +1025,11 @@ int t3_check_fw_version(struct adapter *adapter, int *must_load)
if (type == FW_VERSION_T3 && major == FW_VERSION_MAJOR &&
minor == FW_VERSION_MINOR)
return 0;
-
- if (major != FW_VERSION_MAJOR)
- CH_ERR(adapter, "found wrong FW version(%u.%u), "
- "driver needs version %u.%u\n", major, minor,
- FW_VERSION_MAJOR, FW_VERSION_MINOR);
- else if (minor < FW_VERSION_MINOR) {
- *must_load = 0;
+ else if (major != FW_VERSION_MAJOR || minor < FW_VERSION_MINOR)
CH_WARN(adapter, "found old FW minor version(%u.%u), "
"driver compiled for version %u.%u\n", major, minor,
FW_VERSION_MAJOR, FW_VERSION_MINOR);
- } else {
+ else {
CH_WARN(adapter, "found newer FW version(%u.%u), "
"driver compiled for version %u.%u\n", major, minor,
FW_VERSION_MAJOR, FW_VERSION_MINOR);
diff --git a/drivers/net/cxgb3/version.h b/drivers/net/cxgb3/version.h
index bb8698a86754..b1b25c37aa10 100644
--- a/drivers/net/cxgb3/version.h
+++ b/drivers/net/cxgb3/version.h
@@ -35,7 +35,7 @@
#define DRV_DESC "Chelsio T3 Network Driver"
#define DRV_NAME "cxgb3"
/* Driver version */
-#define DRV_VERSION "1.1.0-ko"
+#define DRV_VERSION "1.1.1-ko"
/* Firmware version */
#define FW_VERSION_MAJOR 7
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c
index 33f956bd6b59..d07130971b8f 100644
--- a/drivers/net/cxgb3/vsc8211.c
+++ b/drivers/net/cxgb3/vsc8211.c
@@ -262,6 +262,7 @@ static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
return 0;
}
+#ifdef UNUSED
/*
* Enable/disable auto MDI/MDI-X in forced link speed mode.
*/
@@ -301,6 +302,7 @@ int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
err = vsc8211_set_automdi(phy, 1);
return err;
}
+#endif /* UNUSED */
static int vsc8211_power_down(struct cphy *cphy, int enable)
{
diff --git a/drivers/net/de600.c b/drivers/net/de600.c
index cb849b091f98..970f820ba814 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/de600.c
@@ -369,7 +369,6 @@ static void de600_rx_intr(struct net_device *dev)
netif_rx(skb);
/* update stats */
- dev->last_rx = jiffies;
dev->stats.rx_packets++; /* count all receives */
dev->stats.rx_bytes += size; /* count all received bytes */
@@ -384,7 +383,6 @@ static struct net_device * __init de600_probe(void)
int i;
struct net_device *dev;
int err;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(0);
if (!dev)
@@ -439,7 +437,7 @@ static struct net_device * __init de600_probe(void)
goto out1;
}
- printk(", Ethernet Address: %s\n", print_mac(mac, dev->dev_addr));
+ printk(", Ethernet Address: %pM\n", dev->dev_addr);
dev->open = de600_open;
dev->stop = de600_close;
diff --git a/drivers/net/de620.c b/drivers/net/de620.c
index d454e143483e..bdfa89403389 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/de620.c
@@ -686,7 +686,6 @@ static int de620_rx_intr(struct net_device *dev)
PRINTK(("Read %d bytes\n", size));
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb); /* deliver it "upstairs" */
- dev->last_rx = jiffies;
/* count all receives */
dev->stats.rx_packets++;
dev->stats.rx_bytes += size;
@@ -800,7 +799,6 @@ struct net_device * __init de620_probe(int unit)
struct net_device *dev;
int err = -ENOMEM;
int i;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(0);
if (!dev)
@@ -853,7 +851,7 @@ struct net_device * __init de620_probe(int unit)
dev->broadcast[i] = 0xff;
}
- printk(", Ethernet Address: %s", print_mac(mac, dev->dev_addr));
+ printk(", Ethernet Address: %pM", dev->dev_addr);
printk(" (%dk RAM,",
(nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64);
@@ -876,10 +874,7 @@ struct net_device * __init de620_probe(int unit)
if (de620_debug) {
printk("\nEEPROM contents:\n");
printk("RAM_Size = 0x%02X\n", nic_data.RAM_Size);
- printk("NodeID = %02X:%02X:%02X:%02X:%02X:%02X\n",
- nic_data.NodeID[0], nic_data.NodeID[1],
- nic_data.NodeID[2], nic_data.NodeID[3],
- nic_data.NodeID[4], nic_data.NodeID[5]);
+ printk("NodeID = %pM\n", nic_data.NodeID);
printk("Model = %d\n", nic_data.Model);
printk("Media = %d\n", nic_data.Media);
printk("SCR = 0x%02x\n", nic_data.SCR);
@@ -1008,20 +1003,3 @@ void cleanup_module(void)
}
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-
-/*
- * (add '-DMODULE' when compiling as loadable module)
- *
- * compile-command:
- * gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O2 \
- * -fomit-frame-pointer -m486 \
- * -I/usr/src/linux/include -I../../net/inet -c de620.c
-*/
-/*
- * Local variables:
- * kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
- * module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
- * compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
- * End:
- */
diff --git a/drivers/net/declance.c b/drivers/net/declance.c
index 3e3506411ac0..7ce3053530f9 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/declance.c
@@ -622,7 +622,6 @@ static int lance_rx(struct net_device *dev)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
@@ -1023,7 +1022,6 @@ static int __init dec_lance_probe(struct device *bdev, const int type)
int i, ret;
unsigned long esar_base;
unsigned char *esar;
- DECLARE_MAC_BUF(mac);
if (dec_lance_debug && version_printed++ == 0)
printk(version);
@@ -1035,7 +1033,7 @@ static int __init dec_lance_probe(struct device *bdev, const int type)
dev = root_lance_dev;
while (dev) {
i++;
- lp = (struct lance_private *)dev->priv;
+ lp = netdev_priv(dev);
dev = lp->next;
}
snprintf(name, sizeof(name), fmt, i);
@@ -1223,8 +1221,7 @@ static int __init dec_lance_probe(struct device *bdev, const int type)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = esar[i * 4];
- printk(", addr = %s, irq = %d\n",
- print_mac(mac, dev->dev_addr), dev->irq);
+ printk(", addr = %pM, irq = %d\n", dev->dev_addr, dev->irq);
dev->open = &lance_open;
dev->stop = &lance_close;
diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c
index c062aacf229c..6445cedd5868 100644
--- a/drivers/net/defxx.c
+++ b/drivers/net/defxx.c
@@ -477,6 +477,15 @@ static void dfx_get_bars(struct device *bdev,
}
}
+static const struct net_device_ops dfx_netdev_ops = {
+ .ndo_open = dfx_open,
+ .ndo_stop = dfx_close,
+ .ndo_start_xmit = dfx_xmt_queue_pkt,
+ .ndo_get_stats = dfx_ctl_get_stats,
+ .ndo_set_multicast_list = dfx_ctl_set_multicast_list,
+ .ndo_set_mac_address = dfx_ctl_set_mac_address,
+};
+
/*
* ================
* = dfx_register =
@@ -511,7 +520,7 @@ static int __devinit dfx_register(struct device *bdev)
int dfx_bus_pci = DFX_BUS_PCI(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
- char *print_name = bdev->bus_id;
+ const char *print_name = dev_name(bdev);
struct net_device *dev;
DFX_board_t *bp; /* board pointer */
resource_size_t bar_start = 0; /* pointer to port */
@@ -573,13 +582,7 @@ static int __devinit dfx_register(struct device *bdev)
}
/* Initialize new device structure */
-
- dev->get_stats = dfx_ctl_get_stats;
- dev->open = dfx_open;
- dev->stop = dfx_close;
- dev->hard_start_xmit = dfx_xmt_queue_pkt;
- dev->set_multicast_list = dfx_ctl_set_multicast_list;
- dev->set_mac_address = dfx_ctl_set_mac_address;
+ dev->netdev_ops = &dfx_netdev_ops;
if (dfx_bus_pci)
pci_set_master(to_pci_dev(bdev));
@@ -3103,7 +3106,6 @@ static void dfx_rcv_queue_process(
netif_rx(skb);
/* Update the rcv counters */
- bp->dev->last_rx = jiffies;
bp->rcv_total_frames++;
if (*(p_buff + RCV_BUFF_K_DA) & 0x01)
bp->rcv_multicast_frames++;
@@ -3741,10 +3743,3 @@ MODULE_AUTHOR("Lawrence V. Stefani");
MODULE_DESCRIPTION("DEC FDDIcontroller TC/EISA/PCI (DEFTA/DEFEA/DEFPA) driver "
DRV_VERSION " " DRV_RELDATE);
MODULE_LICENSE("GPL");
-
-
-/*
- * Local variables:
- * kernel-compile-command: "gcc -D__KERNEL__ -I/root/linux/include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -c defxx.c"
- * End:
- */
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index ace39ec0a367..e4cef491dc73 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -573,7 +573,6 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
s16 nicsr;
u_long ioaddr;
u_long mem_start;
- DECLARE_MAC_BUF(mac);
/*
* We are now supposed to enter this function with the
@@ -601,7 +600,7 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
return -ENXIO;
}
- lp = (struct depca_private *) dev->priv;
+ lp = netdev_priv(dev);
mem_start = lp->mem_start;
if (!mem_start || lp->adapter < DEPCA || lp->adapter >=unknown)
@@ -633,7 +632,7 @@ static int __init depca_hw_init (struct net_device *dev, struct device *device)
printk(", h/w address ");
status = get_hw_addr(dev);
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
if (status != 0) {
printk(" which has an Ethernet PROM CRC error.\n");
return -ENXIO;
@@ -821,7 +820,7 @@ out_priv:
static int depca_open(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
s16 nicsr;
int status = 0;
@@ -866,7 +865,7 @@ static int depca_open(struct net_device *dev)
/* Initialize the lance Rx and Tx descriptor rings. */
static void depca_init_ring(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_int i;
u_long offset;
@@ -924,7 +923,7 @@ static void depca_tx_timeout(struct net_device *dev)
*/
static int depca_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
int status = 0;
@@ -972,7 +971,7 @@ static irqreturn_t depca_interrupt(int irq, void *dev_id)
return IRQ_NONE;
}
- lp = (struct depca_private *) dev->priv;
+ lp = netdev_priv(dev);
ioaddr = dev->base_addr;
spin_lock(&lp->lock);
@@ -1010,7 +1009,7 @@ static irqreturn_t depca_interrupt(int irq, void *dev_id)
/* Called with lp->lock held */
static int depca_rx(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
int i, entry;
s32 status;
@@ -1057,7 +1056,6 @@ static int depca_rx(struct net_device *dev)
/*
** Update stats
*/
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
for (i = 1; i < DEPCA_PKT_STAT_SZ - 1; i++) {
@@ -1108,7 +1106,7 @@ static int depca_rx(struct net_device *dev)
*/
static int depca_tx(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
int entry;
s32 status;
u_long ioaddr = dev->base_addr;
@@ -1149,7 +1147,7 @@ static int depca_tx(struct net_device *dev)
static int depca_close(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
s16 nicsr;
u_long ioaddr = dev->base_addr;
@@ -1185,7 +1183,7 @@ static int depca_close(struct net_device *dev)
static void LoadCSRs(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
@@ -1202,7 +1200,7 @@ static void LoadCSRs(struct net_device *dev)
static int InitRestartDepca(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
int i, status = 0;
@@ -1234,7 +1232,7 @@ static int InitRestartDepca(struct net_device *dev)
*/
static void set_multicast_list(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
netif_stop_queue(dev);
@@ -1263,7 +1261,7 @@ static void set_multicast_list(struct net_device *dev)
*/
static void SetMulticastFilter(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
struct dev_mc_list *dmi = dev->mc_list;
char *addrs;
int i, j, bit, byte;
@@ -1431,7 +1429,7 @@ static int __init depca_mca_probe(struct device *device)
dev->irq = irq;
dev->base_addr = iobase;
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->depca_bus = DEPCA_BUS_MCA;
lp->adapter = depca_mca_adapter_type[mdev->index];
lp->mem_start = mem_start;
@@ -1534,7 +1532,7 @@ static int __init depca_isa_probe (struct platform_device *device)
dev->base_addr = ioaddr;
dev->irq = irq; /* Use whatever value the user gave
* us, and 0 if he didn't. */
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->depca_bus = DEPCA_BUS_ISA;
lp->adapter = adapter;
lp->mem_start = mem_start;
@@ -1558,6 +1556,7 @@ static int __init depca_isa_probe (struct platform_device *device)
#ifdef CONFIG_EISA
static int __init depca_eisa_probe (struct device *device)
{
+ enum depca_type adapter = unknown;
struct eisa_device *edev;
struct net_device *dev;
struct depca_private *lp;
@@ -1576,11 +1575,15 @@ static int __init depca_eisa_probe (struct device *device)
* the EISA configuration structures (yet... :-), just rely on
* the ISA probing to sort it out... */
- depca_shmem_probe (&mem_start);
+ adapter = depca_shmem_probe (&mem_start);
+ if (adapter == unknown) {
+ status = -ENODEV;
+ goto out_free;
+ }
dev->base_addr = ioaddr;
dev->irq = irq;
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->depca_bus = DEPCA_BUS_EISA;
lp->adapter = edev->id.driver_data;
lp->mem_start = mem_start;
@@ -1605,7 +1608,7 @@ static int __devexit depca_device_remove (struct device *device)
int bus;
dev = device->driver_data;
- lp = dev->priv;
+ lp = netdev_priv(dev);
unregister_netdev (dev);
iounmap (lp->sh_mem);
@@ -1747,7 +1750,7 @@ static int __init DevicePresent(u_long ioaddr)
static int __init get_hw_addr(struct net_device *dev)
{
u_long ioaddr = dev->base_addr;
- struct depca_private *lp = dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
int i, k, tmp, status = 0;
u_short j, x, chksum;
@@ -1782,7 +1785,7 @@ static int __init get_hw_addr(struct net_device *dev)
*/
static int load_packet(struct net_device *dev, struct sk_buff *skb)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
int i, entry, end, len, status = 0;
entry = lp->tx_new; /* Ring around buffer number. */
@@ -1837,11 +1840,10 @@ static int load_packet(struct net_device *dev, struct sk_buff *skb)
static void depca_dbg_open(struct net_device *dev)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
struct depca_init *p = &lp->init_block;
int i;
- DECLARE_MAC_BUF(mac);
if (depca_debug > 1) {
/* Do not copy the shadow init block into shared memory */
@@ -1880,7 +1882,7 @@ static void depca_dbg_open(struct net_device *dev)
printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base));
printk("Initialisation block at 0x%8.8lx(Phys)\n", lp->mem_start);
printk(" mode: 0x%4.4x\n", p->mode);
- printk(" physical address: %s\n", print_mac(mac, p->phys_addr));
+ printk(" physical address: %pM\n", p->phys_addr);
printk(" multicast hash table: ");
for (i = 0; i < (HASH_TABLE_LEN >> 3) - 1; i++) {
printk("%2.2x:", p->mcast_table[i]);
@@ -1909,7 +1911,7 @@ static void depca_dbg_open(struct net_device *dev)
*/
static int depca_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct depca_private *lp = (struct depca_private *) dev->priv;
+ struct depca_private *lp = netdev_priv(dev);
struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_ifru;
int i, status = 0;
u_long ioaddr = dev->base_addr;
diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c
index f8037110a522..c749e9fb47ef 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/dl2k.c
@@ -85,6 +85,19 @@ static int mii_write (struct net_device *dev, int phy_addr, int reg_num,
static const struct ethtool_ops ethtool_ops;
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = rio_open,
+ .ndo_start_xmit = start_xmit,
+ .ndo_stop = rio_close,
+ .ndo_get_stats = get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_multicast_list = set_multicast,
+ .ndo_do_ioctl = rio_ioctl,
+ .ndo_tx_timeout = rio_tx_timeout,
+ .ndo_change_mtu = change_mtu,
+};
+
static int __devinit
rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -97,7 +110,6 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
static int version_printed;
void *ring_space;
dma_addr_t ring_dma;
- DECLARE_MAC_BUF(mac);
if (!version_printed++)
printk ("%s", version);
@@ -198,15 +210,8 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
else if (tx_coalesce > TX_RING_SIZE-1)
tx_coalesce = TX_RING_SIZE - 1;
}
- dev->open = &rio_open;
- dev->hard_start_xmit = &start_xmit;
- dev->stop = &rio_close;
- dev->get_stats = &get_stats;
- dev->set_multicast_list = &set_multicast;
- dev->do_ioctl = &rio_ioctl;
- dev->tx_timeout = &rio_tx_timeout;
+ dev->netdev_ops = &netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
- dev->change_mtu = &change_mtu;
SET_ETHTOOL_OPS(dev, &ethtool_ops);
#if 0
dev->features = NETIF_F_IP_CSUM;
@@ -257,8 +262,8 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
card_idx++;
- printk (KERN_INFO "%s: %s, %s, IRQ %d\n",
- dev->name, np->name, print_mac(mac, dev->dev_addr), irq);
+ printk (KERN_INFO "%s: %s, %pM, IRQ %d\n",
+ dev->name, np->name, dev->dev_addr, irq);
if (tx_coalesce > 1)
printk(KERN_INFO "tx_coalesce:\t%d packets\n",
tx_coalesce);
@@ -892,7 +897,6 @@ receive_packet (struct net_device *dev)
}
#endif
netif_rx (skb);
- dev->last_rx = jiffies;
}
entry = (entry + 1) % RX_RING_SIZE;
}
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 5a9083e3f443..bcf92917bbf3 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -137,7 +137,7 @@ typedef struct board_info {
static inline board_info_t *to_dm9000_board(struct net_device *dev)
{
- return dev->priv;
+ return netdev_priv(dev);
}
/* DM9000 network board routine ---------------------------- */
@@ -626,7 +626,7 @@ static unsigned char dm9000_type_to_char(enum dm9000_type type)
static void
dm9000_hash_table(struct net_device *dev)
{
- board_info_t *db = (board_info_t *) dev->priv;
+ board_info_t *db = netdev_priv(dev);
struct dev_mc_list *mcptr = dev->mc_list;
int mc_cnt = dev->mc_count;
int i, oft;
@@ -677,7 +677,7 @@ dm9000_hash_table(struct net_device *dev)
static void
dm9000_init_dm9000(struct net_device *dev)
{
- board_info_t *db = dev->priv;
+ board_info_t *db = netdev_priv(dev);
unsigned int imr;
dm9000_dbg(db, 1, "entering %s\n", __func__);
@@ -723,7 +723,7 @@ dm9000_init_dm9000(struct net_device *dev)
/* Our watchdog timed out. Called by the networking layer */
static void dm9000_timeout(struct net_device *dev)
{
- board_info_t *db = (board_info_t *) dev->priv;
+ board_info_t *db = netdev_priv(dev);
u8 reg_save;
unsigned long flags;
@@ -751,7 +751,7 @@ static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
- board_info_t *db = dev->priv;
+ board_info_t *db = netdev_priv(dev);
dm9000_dbg(db, 3, "%s:\n", __func__);
@@ -831,7 +831,7 @@ struct dm9000_rxhdr {
static void
dm9000_rx(struct net_device *dev)
{
- board_info_t *db = (board_info_t *) dev->priv;
+ board_info_t *db = netdev_priv(dev);
struct dm9000_rxhdr rxhdr;
struct sk_buff *skb;
u8 rxbyte, *rdptr;
@@ -928,7 +928,7 @@ dm9000_rx(struct net_device *dev)
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
- board_info_t *db = dev->priv;
+ board_info_t *db = netdev_priv(dev);
int int_status;
u8 reg_save;
@@ -996,7 +996,7 @@ static void dm9000_poll_controller(struct net_device *dev)
static int
dm9000_open(struct net_device *dev)
{
- board_info_t *db = dev->priv;
+ board_info_t *db = netdev_priv(dev);
unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
if (netif_msg_ifup(db))
@@ -1046,7 +1046,7 @@ static void dm9000_msleep(board_info_t *db, unsigned int ms)
static int
dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg)
{
- board_info_t *db = (board_info_t *) dev->priv;
+ board_info_t *db = netdev_priv(dev);
unsigned long flags;
unsigned int reg_save;
int ret;
@@ -1093,7 +1093,7 @@ static void
dm9000_phy_write(struct net_device *dev,
int phyaddr_unused, int reg, int value)
{
- board_info_t *db = (board_info_t *) dev->priv;
+ board_info_t *db = netdev_priv(dev);
unsigned long flags;
unsigned long reg_save;
@@ -1134,7 +1134,7 @@ dm9000_phy_write(struct net_device *dev,
static void
dm9000_shutdown(struct net_device *dev)
{
- board_info_t *db = dev->priv;
+ board_info_t *db = netdev_priv(dev);
/* RESET device */
dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
@@ -1150,7 +1150,7 @@ dm9000_shutdown(struct net_device *dev)
static int
dm9000_stop(struct net_device *ndev)
{
- board_info_t *db = ndev->priv;
+ board_info_t *db = netdev_priv(ndev);
if (netif_msg_ifdown(db))
dev_dbg(db->dev, "shutting down %s\n", ndev->name);
@@ -1197,7 +1197,7 @@ dm9000_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "dm9000_probe()\n");
/* setup board info structure */
- db = ndev->priv;
+ db = netdev_priv(ndev);
memset(db, 0, sizeof(*db));
db->dev = &pdev->dev;
@@ -1385,13 +1385,11 @@ dm9000_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev);
ret = register_netdev(ndev);
- if (ret == 0) {
- DECLARE_MAC_BUF(mac);
- printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %s (%s)\n",
+ if (ret == 0)
+ printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
ndev->name, dm9000_type_to_char(db->type),
db->io_addr, db->io_data, ndev->irq,
- print_mac(mac, ndev->dev_addr), mac_src);
- }
+ ndev->dev_addr, mac_src);
return 0;
out:
@@ -1410,7 +1408,7 @@ dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)
board_info_t *db;
if (ndev) {
- db = (board_info_t *) ndev->priv;
+ db = netdev_priv(ndev);
db->in_suspend = 1;
if (netif_running(ndev)) {
@@ -1425,7 +1423,7 @@ static int
dm9000_drv_resume(struct platform_device *dev)
{
struct net_device *ndev = platform_get_drvdata(dev);
- board_info_t *db = (board_info_t *) ndev->priv;
+ board_info_t *db = netdev_priv(ndev);
if (ndev) {
@@ -1449,7 +1447,7 @@ dm9000_drv_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
unregister_netdev(ndev);
- dm9000_release_board(pdev, (board_info_t *) ndev->priv);
+ dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));
free_netdev(ndev); /* free device structure */
dev_dbg(&pdev->dev, "released and freed device\n");
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 84e14f397d9a..8ebd7d789405 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -57,18 +57,23 @@ static void set_multicast_list(struct net_device *dev)
{
}
+static const struct net_device_ops dummy_netdev_ops = {
+ .ndo_start_xmit = dummy_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = set_multicast_list,
+ .ndo_set_mac_address = dummy_set_address,
+};
+
static void dummy_setup(struct net_device *dev)
{
+ ether_setup(dev);
+
/* Initialize the device structure. */
- dev->hard_start_xmit = dummy_xmit;
- dev->set_multicast_list = set_multicast_list;
- dev->set_mac_address = dummy_set_address;
+ dev->netdev_ops = &dummy_netdev_ops;
dev->destructor = free_netdev;
/* Fill in device structure with ethernet-generic values. */
- ether_setup(dev);
dev->tx_queue_len = 0;
- dev->change_mtu = NULL;
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
random_ether_addr(dev->dev_addr);
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index e8bfcce6b319..9f38b16ccbbd 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1580,11 +1580,13 @@ static void e100_watchdog(unsigned long data)
mii_ethtool_gset(&nic->mii, &cmd);
if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->netdev)) {
- DPRINTK(LINK, INFO, "link up, %sMbps, %s-duplex\n",
- cmd.speed == SPEED_100 ? "100" : "10",
- cmd.duplex == DUPLEX_FULL ? "full" : "half");
+ printk(KERN_INFO "e100: %s NIC Link is Up %s Mbps %s Duplex\n",
+ nic->netdev->name,
+ cmd.speed == SPEED_100 ? "100" : "10",
+ cmd.duplex == DUPLEX_FULL ? "Full" : "Half");
} else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->netdev)) {
- DPRINTK(LINK, INFO, "link down\n");
+ printk(KERN_INFO "e100: %s NIC Link is Down\n",
+ nic->netdev->name);
}
mii_check_link(&nic->mii);
@@ -1880,7 +1882,6 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
} else {
dev->stats.rx_packets++;
dev->stats.rx_bytes += actual_size;
- nic->netdev->last_rx = jiffies;
netif_receive_skb(skb);
if(work_done)
(*work_done)++;
@@ -2048,9 +2049,9 @@ static irqreturn_t e100_intr(int irq, void *dev_id)
if(stat_ack & stat_ack_rnr)
nic->ru_running = RU_SUSPENDED;
- if(likely(netif_rx_schedule_prep(netdev, &nic->napi))) {
+ if(likely(netif_rx_schedule_prep(&nic->napi))) {
e100_disable_irq(nic);
- __netif_rx_schedule(netdev, &nic->napi);
+ __netif_rx_schedule(&nic->napi);
}
return IRQ_HANDLED;
@@ -2059,7 +2060,6 @@ static irqreturn_t e100_intr(int irq, void *dev_id)
static int e100_poll(struct napi_struct *napi, int budget)
{
struct nic *nic = container_of(napi, struct nic, napi);
- struct net_device *netdev = nic->netdev;
unsigned int work_done = 0;
e100_rx_clean(nic, &work_done, budget);
@@ -2067,7 +2067,7 @@ static int e100_poll(struct napi_struct *napi, int budget)
/* If budget not fully consumed, exit the polling mode */
if (work_done < budget) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
e100_enable_irq(nic);
}
@@ -2322,7 +2322,8 @@ static int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct nic *nic = netdev_priv(netdev);
- if(wol->wolopts != WAKE_MAGIC && wol->wolopts != 0)
+ if ((wol->wolopts && wol->wolopts != WAKE_MAGIC) ||
+ !device_can_wakeup(&nic->pdev->dev))
return -EOPNOTSUPP;
if(wol->wolopts)
@@ -2330,6 +2331,8 @@ static int e100_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
else
nic->flags &= ~wol_magic;
+ device_set_wakeup_enable(&nic->pdev->dev, wol->wolopts);
+
e100_exec_cb(nic, NULL, e100_configure);
return 0;
@@ -2610,13 +2613,27 @@ static int e100_close(struct net_device *netdev)
return 0;
}
+static const struct net_device_ops e100_netdev_ops = {
+ .ndo_open = e100_open,
+ .ndo_stop = e100_close,
+ .ndo_start_xmit = e100_xmit_frame,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = e100_set_multicast_list,
+ .ndo_set_mac_address = e100_set_mac_address,
+ .ndo_change_mtu = e100_change_mtu,
+ .ndo_do_ioctl = e100_do_ioctl,
+ .ndo_tx_timeout = e100_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = e100_netpoll,
+#endif
+};
+
static int __devinit e100_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *netdev;
struct nic *nic;
int err;
- DECLARE_MAC_BUF(mac);
if(!(netdev = alloc_etherdev(sizeof(struct nic)))) {
if(((1 << debug) - 1) & NETIF_MSG_PROBE)
@@ -2624,19 +2641,9 @@ static int __devinit e100_probe(struct pci_dev *pdev,
return -ENOMEM;
}
- netdev->open = e100_open;
- netdev->stop = e100_close;
- netdev->hard_start_xmit = e100_xmit_frame;
- netdev->set_multicast_list = e100_set_multicast_list;
- netdev->set_mac_address = e100_set_mac_address;
- netdev->change_mtu = e100_change_mtu;
- netdev->do_ioctl = e100_do_ioctl;
+ netdev->netdev_ops = &e100_netdev_ops;
SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);
- netdev->tx_timeout = e100_tx_timeout;
netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = e100_netpoll;
-#endif
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
nic = netdev_priv(netdev);
@@ -2734,8 +2741,10 @@ static int __devinit e100_probe(struct pci_dev *pdev,
/* Wol magic packet can be enabled from eeprom */
if((nic->mac >= mac_82558_D101_A4) &&
- (nic->eeprom[eeprom_id] & eeprom_id_wol))
+ (nic->eeprom[eeprom_id] & eeprom_id_wol)) {
nic->flags |= wol_magic;
+ device_set_wakeup_enable(&pdev->dev, true);
+ }
/* ack any pending wake events, disable PME */
pci_pme_active(pdev, false);
@@ -2746,9 +2755,9 @@ static int __devinit e100_probe(struct pci_dev *pdev,
goto err_out_free;
}
- DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, MAC addr %s\n",
+ DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, MAC addr %pM\n",
(unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0),
- pdev->irq, print_mac(mac, netdev->dev_addr));
+ pdev->irq, netdev->dev_addr);
return 0;
@@ -2794,11 +2803,10 @@ static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
pci_save_state(pdev);
if ((nic->flags & wol_magic) | e100_asf(nic)) {
- pci_enable_wake(pdev, PCI_D3hot, 1);
- pci_enable_wake(pdev, PCI_D3cold, 1);
+ if (pci_enable_wake(pdev, PCI_D3cold, true))
+ pci_enable_wake(pdev, PCI_D3hot, true);
} else {
- pci_enable_wake(pdev, PCI_D3hot, 0);
- pci_enable_wake(pdev, PCI_D3cold, 0);
+ pci_enable_wake(pdev, PCI_D3hot, false);
}
pci_disable_device(pdev);
@@ -2843,7 +2851,7 @@ static pci_ers_result_t e100_io_error_detected(struct pci_dev *pdev, pci_channel
struct nic *nic = netdev_priv(netdev);
/* Similar to calling e100_down(), but avoids adapter I/O. */
- netdev->stop(netdev);
+ e100_close(netdev);
/* Detach; put netif into a state similar to hotplug unplug. */
napi_enable(&nic->napi);
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index 62f62970f978..f5581de04757 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -284,7 +284,6 @@ struct e1000_adapter {
int cleaned_count);
struct e1000_rx_ring *rx_ring; /* One per active queue */
struct napi_struct napi;
- struct net_device *polling_netdev; /* One per active queue */
int num_tx_queues;
int num_rx_queues;
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 872799b746f5..26474c92193f 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -888,6 +888,26 @@ static int e1000_is_need_ioport(struct pci_dev *pdev)
}
}
+static const struct net_device_ops e1000_netdev_ops = {
+ .ndo_open = e1000_open,
+ .ndo_stop = e1000_close,
+ .ndo_start_xmit = e1000_xmit_frame,
+ .ndo_get_stats = e1000_get_stats,
+ .ndo_set_rx_mode = e1000_set_rx_mode,
+ .ndo_set_mac_address = e1000_set_mac,
+ .ndo_tx_timeout = e1000_tx_timeout,
+ .ndo_change_mtu = e1000_change_mtu,
+ .ndo_do_ioctl = e1000_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+
+ .ndo_vlan_rx_register = e1000_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = e1000_netpoll,
+#endif
+};
+
/**
* e1000_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -912,7 +932,6 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
u16 eeprom_data = 0;
u16 eeprom_apme_mask = E1000_EEPROM_APME;
int bars, need_ioport;
- DECLARE_MAC_BUF(mac);
/* do not allocate ioport bars when not needed */
need_ioport = e1000_is_need_ioport(pdev);
@@ -967,8 +986,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
hw->back = adapter;
err = -EIO;
- hw->hw_addr = ioremap(pci_resource_start(pdev, BAR_0),
- pci_resource_len(pdev, BAR_0));
+ hw->hw_addr = pci_ioremap_bar(pdev, BAR_0);
if (!hw->hw_addr)
goto err_ioremap;
@@ -983,24 +1001,11 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
}
}
- netdev->open = &e1000_open;
- netdev->stop = &e1000_close;
- netdev->hard_start_xmit = &e1000_xmit_frame;
- netdev->get_stats = &e1000_get_stats;
- netdev->set_rx_mode = &e1000_set_rx_mode;
- netdev->set_mac_address = &e1000_set_mac;
- netdev->change_mtu = &e1000_change_mtu;
- netdev->do_ioctl = &e1000_ioctl;
+ netdev->netdev_ops = &e1000_netdev_ops;
e1000_set_ethtool_ops(netdev);
- netdev->tx_timeout = &e1000_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
netif_napi_add(netdev, &adapter->napi, e1000_clean, 64);
- netdev->vlan_rx_register = e1000_vlan_rx_register;
- netdev->vlan_rx_add_vid = e1000_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = e1000_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = e1000_netpoll;
-#endif
+
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
adapter->bd_number = cards_found;
@@ -1016,9 +1021,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
* because it depends on mac_type */
if ((hw->mac_type == e1000_ich8lan) &&
(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
- hw->flash_address =
- ioremap(pci_resource_start(pdev, 1),
- pci_resource_len(pdev, 1));
+ hw->flash_address = pci_ioremap_bar(pdev, 1);
if (!hw->flash_address)
goto err_flashmap;
}
@@ -1195,7 +1198,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
(hw->bus_width == e1000_bus_width_pciex_1) ? "Width x1" :
"32-bit"));
- printk("%s\n", print_mac(mac, netdev->dev_addr));
+ printk("%pM\n", netdev->dev_addr);
if (hw->bus_type == e1000_bus_type_pci_express) {
DPRINTK(PROBE, WARNING, "This device (id %04x:%04x) will no "
@@ -1239,12 +1242,8 @@ err_eeprom:
if (hw->flash_address)
iounmap(hw->flash_address);
err_flashmap:
- for (i = 0; i < adapter->num_rx_queues; i++)
- dev_put(&adapter->polling_netdev[i]);
-
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
- kfree(adapter->polling_netdev);
err_sw_init:
iounmap(hw->hw_addr);
err_ioremap:
@@ -1272,7 +1271,6 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
struct net_device *netdev = pci_get_drvdata(pdev);
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- int i;
cancel_work_sync(&adapter->reset_task);
@@ -1282,9 +1280,6 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
* would have already happened in close and is redundant. */
e1000_release_hw_control(adapter);
- for (i = 0; i < adapter->num_rx_queues; i++)
- dev_put(&adapter->polling_netdev[i]);
-
unregister_netdev(netdev);
if (!e1000_check_phy_reset_block(hw))
@@ -1292,7 +1287,6 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
- kfree(adapter->polling_netdev);
iounmap(hw->hw_addr);
if (hw->flash_address)
@@ -1318,7 +1312,6 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
- int i;
/* PCI config space info */
@@ -1375,11 +1368,6 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter)
return -ENOMEM;
}
- for (i = 0; i < adapter->num_rx_queues; i++) {
- adapter->polling_netdev[i].priv = adapter;
- dev_hold(&adapter->polling_netdev[i]);
- set_bit(__LINK_STATE_START, &adapter->polling_netdev[i].state);
- }
spin_lock_init(&adapter->tx_queue_lock);
/* Explicitly disable IRQ since the NIC can be in any state. */
@@ -1397,8 +1385,7 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter)
* @adapter: board private structure to initialize
*
* We allocate one ring per queue at run-time since we don't know the
- * number of queues at compile-time. The polling_netdev array is
- * intended for Multiqueue, but should work fine with a single queue.
+ * number of queues at compile-time.
**/
static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter)
@@ -1415,15 +1402,6 @@ static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter)
return -ENOMEM;
}
- adapter->polling_netdev = kcalloc(adapter->num_rx_queues,
- sizeof(struct net_device),
- GFP_KERNEL);
- if (!adapter->polling_netdev) {
- kfree(adapter->tx_ring);
- kfree(adapter->rx_ring);
- return -ENOMEM;
- }
-
return E1000_SUCCESS;
}
@@ -2521,10 +2499,11 @@ static void e1000_watchdog(unsigned long data)
&adapter->link_duplex);
ctrl = er32(CTRL);
- DPRINTK(LINK, INFO, "NIC Link is Up %d Mbps %s, "
- "Flow Control: %s\n",
- adapter->link_speed,
- adapter->link_duplex == FULL_DUPLEX ?
+ printk(KERN_INFO "e1000: %s NIC Link is Up %d Mbps %s, "
+ "Flow Control: %s\n",
+ netdev->name,
+ adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
"Full Duplex" : "Half Duplex",
((ctrl & E1000_CTRL_TFCE) && (ctrl &
E1000_CTRL_RFCE)) ? "RX/TX" : ((ctrl &
@@ -2600,7 +2579,8 @@ static void e1000_watchdog(unsigned long data)
if (netif_carrier_ok(netdev)) {
adapter->link_speed = 0;
adapter->link_duplex = 0;
- DPRINTK(LINK, INFO, "NIC Link is Down\n");
+ printk(KERN_INFO "e1000: %s NIC Link is Down\n",
+ netdev->name);
netif_carrier_off(netdev);
netif_stop_queue(netdev);
mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ));
@@ -3707,12 +3687,12 @@ static irqreturn_t e1000_intr_msi(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- if (likely(netif_rx_schedule_prep(netdev, &adapter->napi))) {
+ if (likely(netif_rx_schedule_prep(&adapter->napi))) {
adapter->total_tx_bytes = 0;
adapter->total_tx_packets = 0;
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
} else
e1000_irq_enable(adapter);
@@ -3767,12 +3747,12 @@ static irqreturn_t e1000_intr(int irq, void *data)
ew32(IMC, ~0);
E1000_WRITE_FLUSH();
}
- if (likely(netif_rx_schedule_prep(netdev, &adapter->napi))) {
+ if (likely(netif_rx_schedule_prep(&adapter->napi))) {
adapter->total_tx_bytes = 0;
adapter->total_tx_packets = 0;
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
} else
/* this really should not happen! if it does it is basically a
* bug, but not a hard error, so enable ints and continue */
@@ -3791,8 +3771,7 @@ static int e1000_clean(struct napi_struct *napi, int budget)
struct net_device *poll_dev = adapter->netdev;
int tx_cleaned = 0, work_done = 0;
- /* Must NOT use netdev_priv macro here. */
- adapter = poll_dev->priv;
+ adapter = netdev_priv(poll_dev);
/* e1000_clean is called per-cpu. This lock protects
* tx_ring[0] from being cleaned by multiple cpus
@@ -3814,7 +3793,7 @@ static int e1000_clean(struct napi_struct *napi, int budget)
if (work_done < budget) {
if (likely(adapter->itr_setting & 3))
e1000_set_itr(adapter);
- netif_rx_complete(poll_dev, napi);
+ netif_rx_complete(napi);
e1000_irq_enable(adapter);
}
@@ -4104,8 +4083,6 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
netif_receive_skb(skb);
}
- netdev->last_rx = jiffies;
-
next_desc:
rx_desc->status = 0;
@@ -4789,7 +4766,7 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct e1000_adapter *adapter = netdev->priv;
+ struct e1000_adapter *adapter = netdev_priv(netdev);
netif_device_detach(netdev);
@@ -4811,7 +4788,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)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct e1000_adapter *adapter = netdev->priv;
+ struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
int err;
@@ -4845,7 +4822,7 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
static void e1000_io_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct e1000_adapter *adapter = netdev->priv;
+ struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
e1000_init_manageability(adapter);
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index b2c910c52df9..cf43ee743b3c 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -28,6 +28,7 @@
/*
* 82571EB Gigabit Ethernet Controller
+ * 82571EB Gigabit Ethernet Controller (Copper)
* 82571EB Gigabit Ethernet Controller (Fiber)
* 82571EB Dual Port Gigabit Mezzanine Adapter
* 82571EB Quad Port Gigabit Mezzanine Adapter
@@ -331,8 +332,9 @@ static s32 e1000_get_variants_82571(struct e1000_adapter *adapter)
case e1000_82573:
if (pdev->device == E1000_DEV_ID_82573L) {
- e1000_read_nvm(&adapter->hw, NVM_INIT_3GIO_3, 1,
- &eeprom_data);
+ if (e1000_read_nvm(&adapter->hw, NVM_INIT_3GIO_3, 1,
+ &eeprom_data) < 0)
+ break;
if (eeprom_data & NVM_WORD1A_ASPM_MASK)
adapter->flags &= ~FLAG_HAS_JUMBO_FRAMES;
}
@@ -973,6 +975,12 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
ew32(CTRL_EXT, reg);
}
+ if (hw->mac.type == e1000_82571) {
+ reg = er32(PBA_ECC);
+ reg |= E1000_PBA_ECC_CORR_EN;
+ ew32(PBA_ECC, reg);
+ }
+
/* PCI-Ex Control Register */
if (hw->mac.type == e1000_82574) {
reg = er32(GCR);
@@ -1111,8 +1119,8 @@ static s32 e1000_setup_link_82571(struct e1000_hw *hw)
* set it to full.
*/
if ((hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) &&
- hw->fc.type == e1000_fc_default)
- hw->fc.type = e1000_fc_full;
+ hw->fc.requested_mode == e1000_fc_default)
+ hw->fc.requested_mode = e1000_fc_full;
return e1000e_setup_link(hw);
}
@@ -1387,6 +1395,7 @@ static struct e1000_phy_operations e82_phy_ops_igp = {
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
.write_phy_reg = e1000e_write_phy_reg_igp,
+ .cfg_on_link_up = NULL,
};
static struct e1000_phy_operations e82_phy_ops_m88 = {
@@ -1403,6 +1412,7 @@ static struct e1000_phy_operations e82_phy_ops_m88 = {
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
.write_phy_reg = e1000e_write_phy_reg_m88,
+ .cfg_on_link_up = NULL,
};
static struct e1000_phy_operations e82_phy_ops_bm = {
@@ -1419,6 +1429,7 @@ static struct e1000_phy_operations e82_phy_ops_bm = {
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
.write_phy_reg = e1000e_write_phy_reg_bm2,
+ .cfg_on_link_up = NULL,
};
static struct e1000_nvm_operations e82571_nvm_ops = {
diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h
index 48f79ecb82a0..e6caf29d4252 100644
--- a/drivers/net/e1000e/defines.h
+++ b/drivers/net/e1000e/defines.h
@@ -372,6 +372,13 @@
#define E1000_ICR_TXQ1 0x00800000 /* Tx Queue 1 Interrupt */
#define E1000_ICR_OTHER 0x01000000 /* Other Interrupts */
+/* PBA ECC Register */
+#define E1000_PBA_ECC_COUNTER_MASK 0xFFF00000 /* ECC counter mask */
+#define E1000_PBA_ECC_COUNTER_SHIFT 20 /* ECC counter shift value */
+#define E1000_PBA_ECC_CORR_EN 0x00000001 /* ECC correction enable */
+#define E1000_PBA_ECC_STAT_CLR 0x00000002 /* Clear ECC error counter */
+#define E1000_PBA_ECC_INT_EN 0x00000004 /* Enable ICR bit 5 for ECC */
+
/*
* This defines the bits that are set in the Interrupt Mask
* Set/Read Register. Each bit is documented below:
@@ -565,6 +572,7 @@
#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */
#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */
#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */
+#define E1000_EECD_SEC1VAL_VALID_MASK (E1000_EECD_AUTO_RD | E1000_EECD_PRES)
#define E1000_NVM_RW_REG_DATA 16 /* Offset to data in NVM read/write registers */
#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index c55fd6fdb91c..37bcb190eef8 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -193,6 +193,7 @@ struct e1000_adapter {
u16 mng_vlan_id;
u16 link_speed;
u16 link_duplex;
+ u16 eeprom_vers;
spinlock_t tx_queue_lock; /* prevent concurrent tail updates */
@@ -388,6 +389,7 @@ extern int e1000e_setup_tx_resources(struct e1000_adapter *adapter);
extern void e1000e_free_rx_resources(struct e1000_adapter *adapter);
extern void e1000e_free_tx_resources(struct e1000_adapter *adapter);
extern void e1000e_update_stats(struct e1000_adapter *adapter);
+extern bool e1000_has_link(struct e1000_adapter *adapter);
extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter);
extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter);
diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c
index da9c09c248ed..8964838c686b 100644
--- a/drivers/net/e1000e/es2lan.c
+++ b/drivers/net/e1000e/es2lan.c
@@ -112,6 +112,11 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw);
static void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw);
static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw);
static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex);
+static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw);
+static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+ u16 *data);
+static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+ u16 data);
/**
* e1000_init_phy_params_80003es2lan - Init ESB2 PHY func ptrs.
@@ -275,8 +280,6 @@ static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw)
u16 mask;
mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
- mask |= E1000_SWFW_CSR_SM;
-
return e1000_acquire_swfw_sync_80003es2lan(hw, mask);
}
@@ -292,7 +295,36 @@ static void e1000_release_phy_80003es2lan(struct e1000_hw *hw)
u16 mask;
mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
- mask |= E1000_SWFW_CSR_SM;
+ e1000_release_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ * e1000_acquire_mac_csr_80003es2lan - Acquire rights to access Kumeran register
+ * @hw: pointer to the HW structure
+ *
+ * Acquire the semaphore to access the Kumeran interface.
+ *
+ **/
+static s32 e1000_acquire_mac_csr_80003es2lan(struct e1000_hw *hw)
+{
+ u16 mask;
+
+ mask = E1000_SWFW_CSR_SM;
+
+ return e1000_acquire_swfw_sync_80003es2lan(hw, mask);
+}
+
+/**
+ * e1000_release_mac_csr_80003es2lan - Release rights to access Kumeran Register
+ * @hw: pointer to the HW structure
+ *
+ * Release the semaphore used to access the Kumeran interface
+ **/
+static void e1000_release_mac_csr_80003es2lan(struct e1000_hw *hw)
+{
+ u16 mask;
+
+ mask = E1000_SWFW_CSR_SM;
e1000_release_swfw_sync_80003es2lan(hw, mask);
}
@@ -347,7 +379,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask)
u32 swmask = mask;
u32 fwmask = mask << 16;
s32 i = 0;
- s32 timeout = 200;
+ s32 timeout = 50;
while (i < timeout) {
if (e1000e_get_hw_semaphore(hw))
@@ -715,13 +747,7 @@ static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
ret_val = e1000e_get_speed_and_duplex_copper(hw,
speed,
duplex);
- if (ret_val)
- return ret_val;
- if (*speed == SPEED_1000)
- ret_val = e1000_cfg_kmrn_1000_80003es2lan(hw);
- else
- ret_val = e1000_cfg_kmrn_10_100_80003es2lan(hw,
- *duplex);
+ hw->phy.ops.cfg_on_link_up(hw);
} else {
ret_val = e1000e_get_speed_and_duplex_fiber_serdes(hw,
speed,
@@ -763,8 +789,10 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
ctrl = er32(CTRL);
+ ret_val = e1000_acquire_phy_80003es2lan(hw);
hw_dbg(hw, "Issuing a global reset to MAC\n");
ew32(CTRL, ctrl | E1000_CTRL_RST);
+ e1000_release_phy_80003es2lan(hw);
ret_val = e1000e_get_auto_rd_done(hw);
if (ret_val)
@@ -907,8 +935,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u32 ctrl_ext;
- u32 i = 0;
- u16 data, data2;
+ u16 data;
ret_val = e1e_rphy(hw, GG82563_PHY_MAC_SPEC_CTRL, &data);
if (ret_val)
@@ -972,19 +999,20 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
}
/* Bypass Rx and Tx FIFO's */
- ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL,
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+ E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL,
E1000_KMRNCTRLSTA_FIFO_CTRL_RX_BYPASS |
E1000_KMRNCTRLSTA_FIFO_CTRL_TX_BYPASS);
if (ret_val)
return ret_val;
- ret_val = e1000e_read_kmrn_reg(hw,
+ ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE,
&data);
if (ret_val)
return ret_val;
data |= E1000_KMRNCTRLSTA_OPMODE_E_IDLE;
- ret_val = e1000e_write_kmrn_reg(hw,
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
E1000_KMRNCTRLSTA_OFFSET_MAC2PHY_OPMODE,
data);
if (ret_val)
@@ -1019,18 +1047,9 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- do {
- ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL,
- &data);
- if (ret_val)
- return ret_val;
-
- ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL,
- &data2);
- if (ret_val)
- return ret_val;
- i++;
- } while ((data != data2) && (i < GG82563_MAX_KMRN_RETRY));
+ ret_val = e1e_rphy(hw, GG82563_PHY_KMRN_MODE_CTRL, &data);
+ if (ret_val)
+ return ret_val;
data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
ret_val = e1e_wphy(hw, GG82563_PHY_KMRN_MODE_CTRL, data);
@@ -1077,23 +1096,27 @@ static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw)
* iteration and increase the max iterations when
* polling the phy; this fixes erroneous timeouts at 10Mbps.
*/
- ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF);
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 4),
+ 0xFFFF);
if (ret_val)
return ret_val;
- ret_val = e1000e_read_kmrn_reg(hw, GG82563_REG(0x34, 9), &reg_data);
+ ret_val = e1000_read_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
+ &reg_data);
if (ret_val)
return ret_val;
reg_data |= 0x3F;
- ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data);
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw, GG82563_REG(0x34, 9),
+ reg_data);
if (ret_val)
return ret_val;
- ret_val = e1000e_read_kmrn_reg(hw,
+ ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
&reg_data);
if (ret_val)
return ret_val;
reg_data |= E1000_KMRNCTRLSTA_INB_CTRL_DIS_PADDING;
- ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+ E1000_KMRNCTRLSTA_OFFSET_INB_CTRL,
reg_data);
if (ret_val)
return ret_val;
@@ -1108,6 +1131,35 @@ static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw)
}
/**
+ * e1000_cfg_on_link_up_80003es2lan - es2 link configuration after link-up
+ * @hw: pointer to the HW structure
+ * @duplex: current duplex setting
+ *
+ * Configure the KMRN interface by applying last minute quirks for
+ * 10/100 operation.
+ **/
+static s32 e1000_cfg_on_link_up_80003es2lan(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ u16 speed;
+ u16 duplex;
+
+ if (hw->phy.media_type == e1000_media_type_copper) {
+ ret_val = e1000e_get_speed_and_duplex_copper(hw, &speed,
+ &duplex);
+ if (ret_val)
+ return ret_val;
+
+ if (speed == SPEED_1000)
+ ret_val = e1000_cfg_kmrn_1000_80003es2lan(hw);
+ else
+ ret_val = e1000_cfg_kmrn_10_100_80003es2lan(hw, duplex);
+ }
+
+ return ret_val;
+}
+
+/**
* e1000_cfg_kmrn_10_100_80003es2lan - Apply "quirks" for 10/100 operation
* @hw: pointer to the HW structure
* @duplex: current duplex setting
@@ -1123,8 +1175,9 @@ static s32 e1000_cfg_kmrn_10_100_80003es2lan(struct e1000_hw *hw, u16 duplex)
u16 reg_data, reg_data2;
reg_data = E1000_KMRNCTRLSTA_HD_CTRL_10_100_DEFAULT;
- ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
- reg_data);
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+ E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
+ reg_data);
if (ret_val)
return ret_val;
@@ -1170,8 +1223,9 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw)
u32 i = 0;
reg_data = E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT;
- ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
- reg_data);
+ ret_val = e1000_write_kmrn_reg_80003es2lan(hw,
+ E1000_KMRNCTRLSTA_OFFSET_HD_CTRL,
+ reg_data);
if (ret_val)
return ret_val;
@@ -1199,6 +1253,71 @@ static s32 e1000_cfg_kmrn_1000_80003es2lan(struct e1000_hw *hw)
}
/**
+ * e1000_read_kmrn_reg_80003es2lan - Read kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquire semaphore, then read the PHY register at offset
+ * using the kumeran interface. The information retrieved is stored in data.
+ * Release the semaphore before exiting.
+ **/
+static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+ u16 *data)
+{
+ u32 kmrnctrlsta;
+ s32 ret_val = 0;
+
+ ret_val = e1000_acquire_mac_csr_80003es2lan(hw);
+ if (ret_val)
+ return ret_val;
+
+ kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
+ E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
+ ew32(KMRNCTRLSTA, kmrnctrlsta);
+
+ udelay(2);
+
+ kmrnctrlsta = er32(KMRNCTRLSTA);
+ *data = (u16)kmrnctrlsta;
+
+ e1000_release_mac_csr_80003es2lan(hw);
+
+ return ret_val;
+}
+
+/**
+ * e1000_write_kmrn_reg_80003es2lan - Write kumeran register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquire semaphore, then write the data to PHY register
+ * at the offset using the kumeran interface. Release semaphore
+ * before exiting.
+ **/
+static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
+ u16 data)
+{
+ u32 kmrnctrlsta;
+ s32 ret_val = 0;
+
+ ret_val = e1000_acquire_mac_csr_80003es2lan(hw);
+ if (ret_val)
+ return ret_val;
+
+ kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
+ E1000_KMRNCTRLSTA_OFFSET) | data;
+ ew32(KMRNCTRLSTA, kmrnctrlsta);
+
+ udelay(2);
+
+ e1000_release_mac_csr_80003es2lan(hw);
+
+ return ret_val;
+}
+
+/**
* e1000_clear_hw_cntrs_80003es2lan - Clear device specific hardware counters
* @hw: pointer to the HW structure
*
@@ -1276,6 +1395,7 @@ static struct e1000_phy_operations es2_phy_ops = {
.set_d0_lplu_state = NULL,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
.write_phy_reg = e1000_write_phy_reg_gg82563_80003es2lan,
+ .cfg_on_link_up = e1000_cfg_on_link_up_80003es2lan,
};
static struct e1000_nvm_operations es2_nvm_ops = {
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c
index 62421ce96311..e48956d924b0 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/e1000e/ethtool.c
@@ -173,11 +173,8 @@ static int e1000_get_settings(struct net_device *netdev,
static u32 e1000_get_link(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
- u32 status;
-
- status = er32(STATUS);
- return (status & E1000_STATUS_LU) ? 1 : 0;
+
+ return e1000_has_link(adapter);
}
static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx)
@@ -249,7 +246,7 @@ static int e1000_set_settings(struct net_device *netdev,
ADVERTISED_Autoneg;
ecmd->advertising = hw->phy.autoneg_advertised;
if (adapter->fc_autoneg)
- hw->fc.original_type = e1000_fc_default;
+ hw->fc.requested_mode = e1000_fc_default;
} else {
if (e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) {
clear_bit(__E1000_RESETTING, &adapter->state);
@@ -279,11 +276,11 @@ static void e1000_get_pauseparam(struct net_device *netdev,
pause->autoneg =
(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
- if (hw->fc.type == e1000_fc_rx_pause) {
+ if (hw->fc.current_mode == e1000_fc_rx_pause) {
pause->rx_pause = 1;
- } else if (hw->fc.type == e1000_fc_tx_pause) {
+ } else if (hw->fc.current_mode == e1000_fc_tx_pause) {
pause->tx_pause = 1;
- } else if (hw->fc.type == e1000_fc_full) {
+ } else if (hw->fc.current_mode == e1000_fc_full) {
pause->rx_pause = 1;
pause->tx_pause = 1;
}
@@ -301,19 +298,8 @@ static int e1000_set_pauseparam(struct net_device *netdev,
while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
msleep(1);
- if (pause->rx_pause && pause->tx_pause)
- hw->fc.type = e1000_fc_full;
- else if (pause->rx_pause && !pause->tx_pause)
- hw->fc.type = e1000_fc_rx_pause;
- else if (!pause->rx_pause && pause->tx_pause)
- hw->fc.type = e1000_fc_tx_pause;
- else if (!pause->rx_pause && !pause->tx_pause)
- hw->fc.type = e1000_fc_none;
-
- hw->fc.original_type = hw->fc.type;
-
if (adapter->fc_autoneg == AUTONEG_ENABLE) {
- hw->fc.type = e1000_fc_default;
+ hw->fc.requested_mode = e1000_fc_default;
if (netif_running(adapter->netdev)) {
e1000e_down(adapter);
e1000e_up(adapter);
@@ -321,6 +307,17 @@ static int e1000_set_pauseparam(struct net_device *netdev,
e1000e_reset(adapter);
}
} else {
+ if (pause->rx_pause && pause->tx_pause)
+ hw->fc.requested_mode = e1000_fc_full;
+ else if (pause->rx_pause && !pause->tx_pause)
+ hw->fc.requested_mode = e1000_fc_rx_pause;
+ else if (!pause->rx_pause && pause->tx_pause)
+ hw->fc.requested_mode = e1000_fc_tx_pause;
+ else if (!pause->rx_pause && !pause->tx_pause)
+ hw->fc.requested_mode = e1000_fc_none;
+
+ hw->fc.current_mode = hw->fc.requested_mode;
+
retval = ((hw->phy.media_type == e1000_media_type_fiber) ?
hw->mac.ops.setup_link(hw) : e1000e_force_mac_fc(hw));
}
@@ -495,18 +492,19 @@ static int e1000_get_eeprom(struct net_device *netdev,
for (i = 0; i < last_word - first_word + 1; i++) {
ret_val = e1000_read_nvm(hw, first_word + i, 1,
&eeprom_buff[i]);
- if (ret_val) {
- /* a read error occurred, throw away the
- * result */
- memset(eeprom_buff, 0xff, sizeof(eeprom_buff));
+ if (ret_val)
break;
- }
}
}
- /* Device's eeprom is always little-endian, word addressable */
- for (i = 0; i < last_word - first_word + 1; i++)
- le16_to_cpus(&eeprom_buff[i]);
+ if (ret_val) {
+ /* a read error occurred, throw away the result */
+ memset(eeprom_buff, 0xff, sizeof(eeprom_buff));
+ } else {
+ /* Device's eeprom is always little-endian, word addressable */
+ for (i = 0; i < last_word - first_word + 1; i++)
+ le16_to_cpus(&eeprom_buff[i]);
+ }
memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
kfree(eeprom_buff);
@@ -558,6 +556,9 @@ static int e1000_set_eeprom(struct net_device *netdev,
ret_val = e1000_read_nvm(hw, last_word, 1,
&eeprom_buff[last_word - first_word]);
+ if (ret_val)
+ goto out;
+
/* Device's eeprom is always little-endian, word addressable */
for (i = 0; i < last_word - first_word + 1; i++)
le16_to_cpus(&eeprom_buff[i]);
@@ -570,15 +571,18 @@ static int e1000_set_eeprom(struct net_device *netdev,
ret_val = e1000_write_nvm(hw, first_word,
last_word - first_word + 1, eeprom_buff);
+ if (ret_val)
+ goto out;
+
/*
* Update the checksum over the first part of the EEPROM if needed
- * and flush shadow RAM for 82573 controllers
+ * and flush shadow RAM for applicable controllers
*/
- if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG) ||
- (hw->mac.type == e1000_82574) ||
- (hw->mac.type == e1000_82573)))
- e1000e_update_nvm_checksum(hw);
+ if ((first_word <= NVM_CHECKSUM_REG) ||
+ (hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82573))
+ ret_val = e1000e_update_nvm_checksum(hw);
+out:
kfree(eeprom_buff);
return ret_val;
}
@@ -588,7 +592,6 @@ static void e1000_get_drvinfo(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
char firmware_version[32];
- u16 eeprom_data;
strncpy(drvinfo->driver, e1000e_driver_name, 32);
strncpy(drvinfo->version, e1000e_driver_version, 32);
@@ -597,11 +600,10 @@ static void e1000_get_drvinfo(struct net_device *netdev,
* EEPROM image version # is reported as firmware version # for
* PCI-E controllers
*/
- e1000_read_nvm(&adapter->hw, 5, 1, &eeprom_data);
sprintf(firmware_version, "%d.%d-%d",
- (eeprom_data & 0xF000) >> 12,
- (eeprom_data & 0x0FF0) >> 4,
- eeprom_data & 0x000F);
+ (adapter->eeprom_vers & 0xF000) >> 12,
+ (adapter->eeprom_vers & 0x0FF0) >> 4,
+ (adapter->eeprom_vers & 0x000F));
strncpy(drvinfo->fw_version, firmware_version, 32);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
@@ -865,7 +867,7 @@ static int e1000_eeprom_test(struct e1000_adapter *adapter, u64 *data)
for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
if ((e1000_read_nvm(&adapter->hw, i, 1, &temp)) < 0) {
*data = 1;
- break;
+ return *data;
}
checksum += temp;
}
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h
index f66ed37a7f76..f25e961c6b3b 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/e1000e/hw.h
@@ -87,6 +87,7 @@ enum e1e_registers {
E1000_EEMNGCTL = 0x01010, /* MNG EEprom Control */
E1000_EEWR = 0x0102C, /* EEPROM Write Register - RW */
E1000_FLOP = 0x0103C, /* FLASH Opcode Register */
+ E1000_PBA_ECC = 0x01100, /* PBA ECC Register */
E1000_ERT = 0x02008, /* Early Rx Threshold - RW */
E1000_FCRTL = 0x02160, /* Flow Control Receive Threshold Low - RW */
E1000_FCRTH = 0x02168, /* Flow Control Receive Threshold High - RW */
@@ -436,7 +437,7 @@ enum e1000_rev_polarity{
e1000_rev_polarity_undefined = 0xFF
};
-enum e1000_fc_type {
+enum e1000_fc_mode {
e1000_fc_none = 0,
e1000_fc_rx_pause,
e1000_fc_tx_pause,
@@ -738,6 +739,7 @@ struct e1000_phy_operations {
s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
s32 (*write_phy_reg)(struct e1000_hw *, u32, u16);
+ s32 (*cfg_on_link_up)(struct e1000_hw *);
};
/* Function pointers for the NVM. */
@@ -848,8 +850,8 @@ struct e1000_fc_info {
u16 pause_time; /* Flow control pause timer */
bool send_xon; /* Flow control send XON */
bool strict_ieee; /* Strict IEEE mode */
- enum e1000_fc_type type; /* Type of flow control */
- enum e1000_fc_type original_type;
+ enum e1000_fc_mode current_mode; /* FC mode in effect */
+ enum e1000_fc_mode requested_mode; /* FC mode requested by caller */
};
struct e1000_dev_spec_82571 {
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index d115a6d30f29..f2a5963b5a95 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -27,6 +27,7 @@
*******************************************************************************/
/*
+ * 82562G 10/100 Network Connection
* 82562G-2 10/100 Network Connection
* 82562GT 10/100 Network Connection
* 82562GT-2 10/100 Network Connection
@@ -40,6 +41,7 @@
* 82566MM Gigabit Network Connection
* 82567LM Gigabit Network Connection
* 82567LF Gigabit Network Connection
+ * 82567V Gigabit Network Connection
* 82567LM-2 Gigabit Network Connection
* 82567LF-2 Gigabit Network Connection
* 82567V-2 Gigabit Network Connection
@@ -92,6 +94,8 @@
#define E1000_ICH_NVM_SIG_WORD 0x13
#define E1000_ICH_NVM_SIG_MASK 0xC000
+#define E1000_ICH_NVM_VALID_SIG_MASK 0xC0
+#define E1000_ICH_NVM_SIG_VALUE 0x80
#define E1000_ICH8_LAN_INIT_TIMEOUT 1500
@@ -956,45 +960,62 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
* @bank: pointer to the variable that returns the active bank
*
* Reads signature byte from the NVM using the flash access registers.
+ * Word 0x13 bits 15:14 = 10b indicate a valid signature for that bank.
**/
static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
{
+ u32 eecd;
struct e1000_nvm_info *nvm = &hw->nvm;
- /* flash bank size is in words */
u32 bank1_offset = nvm->flash_bank_size * sizeof(u16);
u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
- u8 bank_high_byte = 0;
+ u8 sig_byte = 0;
+ s32 ret_val = 0;
- if (hw->mac.type != e1000_ich10lan) {
- if (er32(EECD) & E1000_EECD_SEC1VAL)
- *bank = 1;
- else
- *bank = 0;
- } else {
- /*
- * Make sure the signature for bank 0 is valid,
- * if not check for bank1
- */
- e1000_read_flash_byte_ich8lan(hw, act_offset, &bank_high_byte);
- if ((bank_high_byte & 0xC0) == 0x80) {
+ switch (hw->mac.type) {
+ case e1000_ich8lan:
+ case e1000_ich9lan:
+ eecd = er32(EECD);
+ if ((eecd & E1000_EECD_SEC1VAL_VALID_MASK) ==
+ E1000_EECD_SEC1VAL_VALID_MASK) {
+ if (eecd & E1000_EECD_SEC1VAL)
+ *bank = 1;
+ else
+ *bank = 0;
+
+ return 0;
+ }
+ hw_dbg(hw, "Unable to determine valid NVM bank via EEC - "
+ "reading flash signature\n");
+ /* fall-thru */
+ default:
+ /* set bank to 0 in case flash read fails */
+ *bank = 0;
+
+ /* Check bank 0 */
+ ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset,
+ &sig_byte);
+ if (ret_val)
+ return ret_val;
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
*bank = 0;
- } else {
- /*
- * find if segment 1 is valid by verifying
- * bit 15:14 = 10b in word 0x13
- */
- e1000_read_flash_byte_ich8lan(hw,
- act_offset + bank1_offset,
- &bank_high_byte);
+ return 0;
+ }
- /* bank1 has a valid signature equivalent to SEC1V */
- if ((bank_high_byte & 0xC0) == 0x80) {
- *bank = 1;
- } else {
- hw_dbg(hw, "ERROR: EEPROM not present\n");
- return -E1000_ERR_NVM;
- }
+ /* Check bank 1 */
+ ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset +
+ bank1_offset,
+ &sig_byte);
+ if (ret_val)
+ return ret_val;
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
+ *bank = 1;
+ return 0;
}
+
+ hw_dbg(hw, "ERROR: No valid NVM bank present\n");
+ return -E1000_ERR_NVM;
}
return 0;
@@ -1027,11 +1048,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
ret_val = e1000_acquire_swflag_ich8lan(hw);
if (ret_val)
- return ret_val;
+ goto out;
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val)
- return ret_val;
+ goto release;
act_offset = (bank) ? nvm->flash_bank_size : 0;
act_offset += offset;
@@ -1050,8 +1071,13 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
}
}
+release:
e1000_release_swflag_ich8lan(hw);
+out:
+ if (ret_val)
+ hw_dbg(hw, "NVM read error: %d\n", ret_val);
+
return ret_val;
}
@@ -1340,14 +1366,14 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
ret_val = e1000e_update_nvm_checksum_generic(hw);
if (ret_val)
- return ret_val;
+ goto out;
if (nvm->type != e1000_nvm_flash_sw)
- return ret_val;
+ goto out;
ret_val = e1000_acquire_swflag_ich8lan(hw);
if (ret_val)
- return ret_val;
+ goto out;
/*
* We're writing to the opposite bank so if we're on bank 1,
@@ -1355,17 +1381,27 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
* is going to be written
*/
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
- if (ret_val)
- return ret_val;
+ if (ret_val) {
+ e1000_release_swflag_ich8lan(hw);
+ goto out;
+ }
if (bank == 0) {
new_bank_offset = nvm->flash_bank_size;
old_bank_offset = 0;
- e1000_erase_flash_bank_ich8lan(hw, 1);
+ ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
+ if (ret_val) {
+ e1000_release_swflag_ich8lan(hw);
+ goto out;
+ }
} else {
old_bank_offset = nvm->flash_bank_size;
new_bank_offset = 0;
- e1000_erase_flash_bank_ich8lan(hw, 0);
+ ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
+ if (ret_val) {
+ e1000_release_swflag_ich8lan(hw);
+ goto out;
+ }
}
for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) {
@@ -1377,9 +1413,11 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
if (dev_spec->shadow_ram[i].modified) {
data = dev_spec->shadow_ram[i].value;
} else {
- e1000_read_flash_word_ich8lan(hw,
- i + old_bank_offset,
- &data);
+ ret_val = e1000_read_flash_word_ich8lan(hw, i +
+ old_bank_offset,
+ &data);
+ if (ret_val)
+ break;
}
/*
@@ -1420,7 +1458,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
/* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */
hw_dbg(hw, "Flash commit failed.\n");
e1000_release_swflag_ich8lan(hw);
- return ret_val;
+ goto out;
}
/*
@@ -1430,14 +1468,18 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
* and we need to change bit 14 to 0b
*/
act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
- e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+ ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+ if (ret_val) {
+ e1000_release_swflag_ich8lan(hw);
+ goto out;
+ }
data &= 0xBFFF;
ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
act_offset * 2 + 1,
(u8)(data >> 8));
if (ret_val) {
e1000_release_swflag_ich8lan(hw);
- return ret_val;
+ goto out;
}
/*
@@ -1450,7 +1492,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0);
if (ret_val) {
e1000_release_swflag_ich8lan(hw);
- return ret_val;
+ goto out;
}
/* Great! Everything worked, we can now clear the cached entries. */
@@ -1468,6 +1510,10 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
e1000e_reload_nvm(hw);
msleep(10);
+out:
+ if (ret_val)
+ hw_dbg(hw, "NVM update error: %d\n", ret_val);
+
return ret_val;
}
@@ -1894,7 +1940,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
}
ret_val = e1000_acquire_swflag_ich8lan(hw);
/* Whether or not the swflag was acquired, we need to reset the part */
- hw_dbg(hw, "Issuing a global reset to ich8lan");
+ hw_dbg(hw, "Issuing a global reset to ich8lan\n");
ew32(CTRL, (ctrl | E1000_CTRL_RST));
msleep(20);
@@ -2074,12 +2120,17 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
* the default flow control setting, so we explicitly
* set it to full.
*/
- if (hw->fc.type == e1000_fc_default)
- hw->fc.type = e1000_fc_full;
+ if (hw->fc.requested_mode == e1000_fc_default)
+ hw->fc.requested_mode = e1000_fc_full;
- hw->fc.original_type = hw->fc.type;
+ /*
+ * Save off the requested flow control mode for use later. Depending
+ * on the link partner's capabilities, we may or may not use this mode.
+ */
+ hw->fc.current_mode = hw->fc.requested_mode;
- hw_dbg(hw, "After fix-ups FlowControl is now = %x\n", hw->fc.type);
+ hw_dbg(hw, "After fix-ups FlowControl is now = %x\n",
+ hw->fc.current_mode);
/* Continue to configure the copper link. */
ret_val = e1000_setup_copper_link_ich8lan(hw);
diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c
index 089578f6855a..66741104ffd1 100644
--- a/drivers/net/e1000e/lib.c
+++ b/drivers/net/e1000e/lib.c
@@ -575,20 +575,42 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
*/
/* SYNCH bit and IV bit are sticky. */
udelay(10);
- if (E1000_RXCW_SYNCH & er32(RXCW)) {
+ rxcw = er32(RXCW);
+ if (rxcw & E1000_RXCW_SYNCH) {
if (!(rxcw & E1000_RXCW_IV)) {
- mac->serdes_has_link = 1;
- hw_dbg(hw, "SERDES: Link is up.\n");
+ mac->serdes_has_link = true;
+ hw_dbg(hw, "SERDES: Link up - forced.\n");
}
} else {
- mac->serdes_has_link = 0;
- hw_dbg(hw, "SERDES: Link is down.\n");
+ mac->serdes_has_link = false;
+ hw_dbg(hw, "SERDES: Link down - force failed.\n");
}
}
if (E1000_TXCW_ANE & er32(TXCW)) {
status = er32(STATUS);
- mac->serdes_has_link = (status & E1000_STATUS_LU);
+ if (status & E1000_STATUS_LU) {
+ /* SYNCH bit and IV bit are sticky, so reread rxcw. */
+ udelay(10);
+ rxcw = er32(RXCW);
+ if (rxcw & E1000_RXCW_SYNCH) {
+ if (!(rxcw & E1000_RXCW_IV)) {
+ mac->serdes_has_link = true;
+ hw_dbg(hw, "SERDES: Link up - autoneg "
+ "completed sucessfully.\n");
+ } else {
+ mac->serdes_has_link = false;
+ hw_dbg(hw, "SERDES: Link down - invalid"
+ "codewords detected in autoneg.\n");
+ }
+ } else {
+ mac->serdes_has_link = false;
+ hw_dbg(hw, "SERDES: Link down - no sync.\n");
+ }
+ } else {
+ mac->serdes_has_link = false;
+ hw_dbg(hw, "SERDES: Link down - autoneg failed\n");
+ }
}
return 0;
@@ -623,12 +645,12 @@ static s32 e1000_set_default_fc_generic(struct e1000_hw *hw)
}
if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == 0)
- hw->fc.type = e1000_fc_none;
+ hw->fc.requested_mode = e1000_fc_none;
else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) ==
NVM_WORD0F_ASM_DIR)
- hw->fc.type = e1000_fc_tx_pause;
+ hw->fc.requested_mode = e1000_fc_tx_pause;
else
- hw->fc.type = e1000_fc_full;
+ hw->fc.requested_mode = e1000_fc_full;
return 0;
}
@@ -656,23 +678,23 @@ s32 e1000e_setup_link(struct e1000_hw *hw)
return 0;
/*
- * If flow control is set to default, set flow control based on
- * the EEPROM flow control settings.
+ * If requested flow control is set to default, set flow control
+ * based on the EEPROM flow control settings.
*/
- if (hw->fc.type == e1000_fc_default) {
+ if (hw->fc.requested_mode == e1000_fc_default) {
ret_val = e1000_set_default_fc_generic(hw);
if (ret_val)
return ret_val;
}
/*
- * We want to save off the original Flow Control configuration just
- * in case we get disconnected and then reconnected into a different
- * hub or switch with different Flow Control capabilities.
+ * Save off the requested flow control mode for use later. Depending
+ * on the link partner's capabilities, we may or may not use this mode.
*/
- hw->fc.original_type = hw->fc.type;
+ hw->fc.current_mode = hw->fc.requested_mode;
- hw_dbg(hw, "After fix-ups FlowControl is now = %x\n", hw->fc.type);
+ hw_dbg(hw, "After fix-ups FlowControl is now = %x\n",
+ hw->fc.current_mode);
/* Call the necessary media_type subroutine to configure the link. */
ret_val = mac->ops.setup_physical_interface(hw);
@@ -724,7 +746,7 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw)
* do not support receiving pause frames).
* 3: Both Rx and Tx flow control (symmetric) are enabled.
*/
- switch (hw->fc.type) {
+ switch (hw->fc.current_mode) {
case e1000_fc_none:
/* Flow control completely disabled by a software over-ride. */
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD);
@@ -906,7 +928,7 @@ s32 e1000e_set_fc_watermarks(struct e1000_hw *hw)
* ability to transmit pause frames is not enabled, then these
* registers will be set to 0.
*/
- if (hw->fc.type & e1000_fc_tx_pause) {
+ if (hw->fc.current_mode & e1000_fc_tx_pause) {
/*
* We need to set up the Receive Threshold high and low water
* marks as well as (optionally) enabling the transmission of
@@ -945,7 +967,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw)
* receive flow control.
*
* The "Case" statement below enables/disable flow control
- * according to the "hw->fc.type" parameter.
+ * according to the "hw->fc.current_mode" parameter.
*
* The possible values of the "fc" parameter are:
* 0: Flow control is completely disabled
@@ -956,9 +978,9 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw)
* 3: Both Rx and Tx flow control (symmetric) is enabled.
* other: No other values should be possible at this point.
*/
- hw_dbg(hw, "hw->fc.type = %u\n", hw->fc.type);
+ hw_dbg(hw, "hw->fc.current_mode = %u\n", hw->fc.current_mode);
- switch (hw->fc.type) {
+ switch (hw->fc.current_mode) {
case e1000_fc_none:
ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));
break;
@@ -1102,11 +1124,11 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
* ONLY. Hence, we must now check to see if we need to
* turn OFF the TRANSMISSION of PAUSE frames.
*/
- if (hw->fc.original_type == e1000_fc_full) {
- hw->fc.type = e1000_fc_full;
+ if (hw->fc.requested_mode == e1000_fc_full) {
+ hw->fc.current_mode = e1000_fc_full;
hw_dbg(hw, "Flow Control = FULL.\r\n");
} else {
- hw->fc.type = e1000_fc_rx_pause;
+ hw->fc.current_mode = e1000_fc_rx_pause;
hw_dbg(hw, "Flow Control = "
"RX PAUSE frames only.\r\n");
}
@@ -1124,7 +1146,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
- hw->fc.type = e1000_fc_tx_pause;
+ hw->fc.current_mode = e1000_fc_tx_pause;
hw_dbg(hw, "Flow Control = Tx PAUSE frames only.\r\n");
}
/*
@@ -1140,14 +1162,14 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
- hw->fc.type = e1000_fc_rx_pause;
+ hw->fc.current_mode = e1000_fc_rx_pause;
hw_dbg(hw, "Flow Control = Rx PAUSE frames only.\r\n");
} else {
/*
* Per the IEEE spec, at this point flow control
* should be disabled.
*/
- hw->fc.type = e1000_fc_none;
+ hw->fc.current_mode = e1000_fc_none;
hw_dbg(hw, "Flow Control = NONE.\r\n");
}
@@ -1163,7 +1185,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
}
if (duplex == HALF_DUPLEX)
- hw->fc.type = e1000_fc_none;
+ hw->fc.current_mode = e1000_fc_none;
/*
* Now we call a subroutine to actually force the MAC
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 122539a0e1fe..d4639facd1bd 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -102,9 +102,7 @@ static void e1000_receive_skb(struct e1000_adapter *adapter,
vlan_hwaccel_receive_skb(skb, adapter->vlgrp,
le16_to_cpu(vlan));
else
- netif_receive_skb(skb);
-
- netdev->last_rx = jiffies;
+ napi_gro_receive(&adapter->napi, skb);
}
/**
@@ -1181,12 +1179,12 @@ static irqreturn_t e1000_intr_msi(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
+ if (netif_rx_schedule_prep(&adapter->napi)) {
adapter->total_tx_bytes = 0;
adapter->total_tx_packets = 0;
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
return IRQ_HANDLED;
@@ -1248,12 +1246,12 @@ static irqreturn_t e1000_intr(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
+ if (netif_rx_schedule_prep(&adapter->napi)) {
adapter->total_tx_bytes = 0;
adapter->total_tx_packets = 0;
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
return IRQ_HANDLED;
@@ -1322,10 +1320,10 @@ static irqreturn_t e1000_intr_msix_rx(int irq, void *data)
adapter->rx_ring->set_itr = 0;
}
- if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
+ if (netif_rx_schedule_prep(&adapter->napi)) {
adapter->total_rx_bytes = 0;
adapter->total_rx_packets = 0;
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
return IRQ_HANDLED;
}
@@ -1480,7 +1478,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter)
int err = 0, vector = 0;
if (strlen(netdev->name) < (IFNAMSIZ - 5))
- sprintf(adapter->rx_ring->name, "%s-rx0", netdev->name);
+ sprintf(adapter->rx_ring->name, "%s-rx-0", netdev->name);
else
memcpy(adapter->rx_ring->name, netdev->name, IFNAMSIZ);
err = request_irq(adapter->msix_entries[vector].vector,
@@ -1493,7 +1491,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter)
vector++;
if (strlen(netdev->name) < (IFNAMSIZ - 5))
- sprintf(adapter->tx_ring->name, "%s-tx0", netdev->name);
+ sprintf(adapter->tx_ring->name, "%s-tx-0", netdev->name);
else
memcpy(adapter->tx_ring->name, netdev->name, IFNAMSIZ);
err = request_irq(adapter->msix_entries[vector].vector,
@@ -2003,8 +2001,7 @@ static int e1000_clean(struct napi_struct *napi, int budget)
struct net_device *poll_dev = adapter->netdev;
int tx_cleaned = 0, work_done = 0;
- /* Must NOT use netdev_priv macro here. */
- adapter = poll_dev->priv;
+ adapter = netdev_priv(poll_dev);
if (adapter->msix_entries &&
!(adapter->rx_ring->ims_val & adapter->tx_ring->ims_val))
@@ -2031,7 +2028,7 @@ clean_rx:
if (work_done < budget) {
if (adapter->itr_setting & 3)
e1000_set_itr(adapter);
- netif_rx_complete(poll_dev, napi);
+ netif_rx_complete(napi);
if (adapter->msix_entries)
ew32(IMS, adapter->rx_ring->ims_val);
else
@@ -2787,7 +2784,7 @@ void e1000e_reset(struct e1000_adapter *adapter)
else
fc->pause_time = E1000_FC_PAUSE_TIME;
fc->send_xon = 1;
- fc->type = fc->original_type;
+ fc->current_mode = fc->requested_mode;
/* Allow time for pending master requests to run */
mac->ops.reset_hw(hw);
@@ -3410,7 +3407,10 @@ static void e1000_print_link_info(struct e1000_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 ctrl = er32(CTRL);
- e_info("Link is Up %d Mbps %s, Flow Control: %s\n",
+ /* Link status message must follow this format for user tools */
+ printk(KERN_INFO "e1000e: %s NIC Link is Up %d Mbps %s, "
+ "Flow Control: %s\n",
+ adapter->netdev->name,
adapter->link_speed,
(adapter->link_duplex == FULL_DUPLEX) ?
"Full Duplex" : "Half Duplex",
@@ -3420,7 +3420,7 @@ static void e1000_print_link_info(struct e1000_adapter *adapter)
((ctrl & E1000_CTRL_TFCE) ? "TX" : "None" )));
}
-static bool e1000_has_link(struct e1000_adapter *adapter)
+bool e1000_has_link(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
bool link_active = 0;
@@ -3495,6 +3495,7 @@ static void e1000_watchdog_task(struct work_struct *work)
struct e1000_adapter, watchdog_task);
struct net_device *netdev = adapter->netdev;
struct e1000_mac_info *mac = &adapter->hw.mac;
+ struct e1000_phy_info *phy = &adapter->hw.phy;
struct e1000_ring *tx_ring = adapter->tx_ring;
struct e1000_hw *hw = &adapter->hw;
u32 link, tctl;
@@ -3601,6 +3602,13 @@ static void e1000_watchdog_task(struct work_struct *work)
tctl |= E1000_TCTL_EN;
ew32(TCTL, tctl);
+ /*
+ * Perform any post-link-up configuration before
+ * reporting link up.
+ */
+ if (phy->ops.cfg_on_link_up)
+ phy->ops.cfg_on_link_up(hw);
+
netif_carrier_on(netdev);
netif_tx_wake_all_queues(netdev);
@@ -3612,7 +3620,9 @@ static void e1000_watchdog_task(struct work_struct *work)
if (netif_carrier_ok(netdev)) {
adapter->link_speed = 0;
adapter->link_duplex = 0;
- e_info("Link is Down\n");
+ /* Link status message must follow this format */
+ printk(KERN_INFO "e1000e: %s NIC Link is Down\n",
+ adapter->netdev->name);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
if (!test_bit(__E1000_DOWN, &adapter->state))
@@ -4464,7 +4474,27 @@ static int e1000_suspend(struct pci_dev *pdev, pm_message_t state)
pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ /*
+ * The pci-e switch on some quad port adapters will report a
+ * correctable error when the MAC transitions from D0 to D3. To
+ * prevent this we need to mask off the correctable errors on the
+ * downstream port of the pci-e switch.
+ */
+ if (adapter->flags & FLAG_IS_QUAD_PORT) {
+ struct pci_dev *us_dev = pdev->bus->self;
+ int pos = pci_find_capability(us_dev, PCI_CAP_ID_EXP);
+ 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));
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ pci_write_config_word(us_dev, pos + PCI_EXP_DEVCTL, devctl);
+ } else {
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ }
return 0;
}
@@ -4669,14 +4699,12 @@ static void e1000_print_device_info(struct e1000_adapter *adapter)
u32 pba_num;
/* print bus type/speed/width info */
- e_info("(PCI Express:2.5GB/s:%s) %02x:%02x:%02x:%02x:%02x:%02x\n",
+ e_info("(PCI Express:2.5GB/s:%s) %pM\n",
/* bus width */
((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" :
"Width x1"),
/* MAC address */
- netdev->dev_addr[0], netdev->dev_addr[1],
- netdev->dev_addr[2], netdev->dev_addr[3],
- netdev->dev_addr[4], netdev->dev_addr[5]);
+ netdev->dev_addr);
e_info("Intel(R) PRO/%s Network Connection\n",
(hw->phy.type == e1000_phy_ife) ? "10/100" : "1000");
e1000e_read_pba_num(hw, &pba_num);
@@ -4694,20 +4722,40 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter)
return;
ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &buf);
- if (!(le16_to_cpu(buf) & (1 << 0))) {
+ if (!ret_val && (!(le16_to_cpu(buf) & (1 << 0)))) {
/* Deep Smart Power Down (DSPD) */
dev_warn(&adapter->pdev->dev,
"Warning: detected DSPD enabled in EEPROM\n");
}
ret_val = e1000_read_nvm(hw, NVM_INIT_3GIO_3, 1, &buf);
- if (le16_to_cpu(buf) & (3 << 2)) {
+ if (!ret_val && (le16_to_cpu(buf) & (3 << 2))) {
/* ASPM enable */
dev_warn(&adapter->pdev->dev,
"Warning: detected ASPM enabled in EEPROM\n");
}
}
+static const struct net_device_ops e1000e_netdev_ops = {
+ .ndo_open = e1000_open,
+ .ndo_stop = e1000_close,
+ .ndo_start_xmit = e1000_xmit_frame,
+ .ndo_get_stats = e1000_get_stats,
+ .ndo_set_multicast_list = e1000_set_multi,
+ .ndo_set_mac_address = e1000_set_mac,
+ .ndo_change_mtu = e1000_change_mtu,
+ .ndo_do_ioctl = e1000_ioctl,
+ .ndo_tx_timeout = e1000_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+
+ .ndo_vlan_rx_register = e1000_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = e1000_netpoll,
+#endif
+};
+
/**
* e1000_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -4766,7 +4814,10 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
goto err_pci_reg;
pci_set_master(pdev);
- pci_save_state(pdev);
+ /* PCI config space info */
+ err = pci_save_state(pdev);
+ if (err)
+ goto err_alloc_etherdev;
err = -ENOMEM;
netdev = alloc_etherdev(sizeof(struct e1000_adapter));
@@ -4806,24 +4857,10 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
}
/* construct the net_device struct */
- netdev->open = &e1000_open;
- netdev->stop = &e1000_close;
- netdev->hard_start_xmit = &e1000_xmit_frame;
- netdev->get_stats = &e1000_get_stats;
- netdev->set_multicast_list = &e1000_set_multi;
- netdev->set_mac_address = &e1000_set_mac;
- netdev->change_mtu = &e1000_change_mtu;
- netdev->do_ioctl = &e1000_ioctl;
+ netdev->netdev_ops = &e1000e_netdev_ops;
e1000e_set_ethtool_ops(netdev);
- netdev->tx_timeout = &e1000_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
netif_napi_add(netdev, &adapter->napi, e1000_clean, 64);
- netdev->vlan_rx_register = e1000_vlan_rx_register;
- netdev->vlan_rx_add_vid = e1000_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = e1000_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = e1000_netpoll;
-#endif
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
netdev->mem_start = mmio_start;
@@ -4924,10 +4961,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len);
if (!is_valid_ether_addr(netdev->perm_addr)) {
- e_err("Invalid MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
- netdev->perm_addr[0], netdev->perm_addr[1],
- netdev->perm_addr[2], netdev->perm_addr[3],
- netdev->perm_addr[4], netdev->perm_addr[5]);
+ e_err("Invalid MAC Address: %pM\n", netdev->perm_addr);
err = -EIO;
goto err_eeprom;
}
@@ -4948,8 +4982,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
/* Initialize link parameters. User can change them with ethtool */
adapter->hw.mac.autoneg = 1;
adapter->fc_autoneg = 1;
- adapter->hw.fc.original_type = e1000_fc_default;
- adapter->hw.fc.type = e1000_fc_default;
+ adapter->hw.fc.requested_mode = e1000_fc_default;
+ adapter->hw.fc.current_mode = e1000_fc_default;
adapter->hw.phy.autoneg_advertised = 0x2f;
/* ring size defaults */
@@ -4990,6 +5024,9 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
adapter->wol = adapter->eeprom_wol;
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+ /* save off EEPROM version number */
+ e1000_read_nvm(&adapter->hw, 5, 1, &adapter->eeprom_vers);
+
/* reset the hardware with the new settings */
e1000e_reset(adapter);
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c
index 6cd333ae61d0..dc4a9cba6a73 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/e1000e/phy.c
@@ -744,7 +744,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
* other: No software override. The flow control configuration
* in the EEPROM is used.
*/
- switch (hw->fc.type) {
+ switch (hw->fc.current_mode) {
case e1000_fc_none:
/*
* Flow control (Rx & Tx) is completely disabled by a
@@ -1030,14 +1030,14 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
e1000e_phy_force_speed_duplex_setup(hw, &phy_data);
- /* Reset the phy to commit changes. */
- phy_data |= MII_CR_RESET;
-
ret_val = e1e_wphy(hw, PHY_CONTROL, phy_data);
if (ret_val)
return ret_val;
- udelay(1);
+ /* Reset the phy to commit changes. */
+ ret_val = e1000e_commit_phy(hw);
+ if (ret_val)
+ return ret_val;
if (phy->autoneg_wait_to_complete) {
hw_dbg(hw, "Waiting for forced speed/duplex link on M88 phy.\n");
@@ -1114,7 +1114,7 @@ void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl)
u32 ctrl;
/* Turn off flow control when forcing speed/duplex */
- hw->fc.type = e1000_fc_none;
+ hw->fc.current_mode = e1000_fc_none;
/* Force speed/duplex on the mac */
ctrl = er32(CTRL);
diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c
index 6390f51ea6fb..20eb05cddb83 100644
--- a/drivers/net/e2100.c
+++ b/drivers/net/e2100.c
@@ -107,7 +107,7 @@ static void e21_block_output(struct net_device *dev, int count,
const unsigned char *buf, int start_page);
static void e21_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
-
+static int e21_open(struct net_device *dev);
static int e21_close(struct net_device *dev);
@@ -160,6 +160,21 @@ out:
}
#endif
+static const struct net_device_ops e21_netdev_ops = {
+ .ndo_open = e21_open,
+ .ndo_stop = e21_close,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init e21_probe1(struct net_device *dev, int ioaddr)
{
int i, status, retval;
@@ -265,11 +280,8 @@ static int __init e21_probe1(struct net_device *dev, int ioaddr)
ei_status.block_input = &e21_block_input;
ei_status.block_output = &e21_block_output;
ei_status.get_8390_hdr = &e21_get_8390_hdr;
- dev->open = &e21_open;
- dev->stop = &e21_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+
+ dev->netdev_ops = &e21_netdev_ops;
NS8390_init(dev, 0);
retval = register_netdev(dev);
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index 1f11350e16cf..e187c88ae145 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -605,7 +605,7 @@ out:
static void __init printEEPROMInfo(struct net_device *dev)
{
- struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ struct eepro_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
unsigned short Word;
int i,j;
@@ -690,7 +690,6 @@ static void __init eepro_print_info (struct net_device *dev)
struct eepro_local * lp = netdev_priv(dev);
int i;
const char * ifmap[] = {"AUI", "10Base2", "10BaseT"};
- DECLARE_MAC_BUF(mac);
i = inb(dev->base_addr + ID_REG);
printk(KERN_DEBUG " id: %#x ",i);
@@ -715,7 +714,7 @@ static void __init eepro_print_info (struct net_device *dev)
break;
}
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
if (net_debug > 3)
printk(KERN_DEBUG ", %dK RCV buffer",
@@ -1396,7 +1395,7 @@ set_multicast_list(struct net_device *dev)
#define eeprom_delay() { udelay(40); }
#define EE_READ_CMD (6 << 6)
-int
+static int
read_eeprom(int ioaddr, int location, struct net_device *dev)
{
int i;
@@ -1581,7 +1580,6 @@ eepro_rx(struct net_device *dev)
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
@@ -1676,7 +1674,7 @@ eepro_transmit_interrupt(struct net_device *dev)
static int eepro_ethtool_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
- struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ struct eepro_local *lp = netdev_priv(dev);
cmd->supported = SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
deleted file mode 100644
index e3e26c595fa3..000000000000
--- a/drivers/net/eepro100.c
+++ /dev/null
@@ -1,2401 +0,0 @@
-/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */
-/*
- Written 1996-1999 by Donald Becker.
-
- The driver also contains updates by different kernel developers
- (see incomplete list below).
- Current maintainer is Andrey V. Savochkin <saw@saw.sw.com.sg>.
- Please use this email address and linux-kernel mailing list for bug reports.
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
- It should work with all i82557/558/559 boards.
-
- Version history:
- 1998 Apr - 2000 Feb Andrey V. Savochkin <saw@saw.sw.com.sg>
- Serious fixes for multicast filter list setting, TX timeout routine;
- RX ring refilling logic; other stuff
- 2000 Feb Jeff Garzik <jgarzik@pobox.com>
- Convert to new PCI driver interface
- 2000 Mar 24 Dragan Stancevic <visitor@valinux.com>
- Disabled FC and ER, to avoid lockups when when we get FCP interrupts.
- 2000 Jul 17 Goutham Rao <goutham.rao@intel.com>
- PCI DMA API fixes, adding pci_dma_sync_single calls where neccesary
- 2000 Aug 31 David Mosberger <davidm@hpl.hp.com>
- rx_align support: enables rx DMA without causing unaligned accesses.
-*/
-
-static const char * const version =
-"eepro100.c:v1.09j-t 9/29/99 Donald Becker\n"
-"eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
-
-/* A few user-configurable values that apply to all boards.
- First set is undocumented and spelled per Intel recommendations. */
-
-static int congenb /* = 0 */; /* Enable congestion control in the DP83840. */
-static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
-/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
-static int txdmacount = 128;
-static int rxdmacount /* = 0 */;
-
-#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__) || defined(__mips__) || \
- defined(__arm__)
- /* align rx buffers to 2 bytes so that IP header is aligned */
-# define rx_align(skb) skb_reserve((skb), 2)
-# define RxFD_ALIGNMENT __attribute__ ((aligned (2), packed))
-#else
-# define rx_align(skb)
-# define RxFD_ALIGNMENT
-#endif
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
- Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
-static int multicast_filter_limit = 64;
-
-/* 'options' is used to pass a transceiver override or full-duplex flag
- e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE 64
-#define RX_RING_SIZE 64
-/* How much slots multicast filter setup may take.
- Do not descrease without changing set_rx_mode() implementaion. */
-#define TX_MULTICAST_SIZE 2
-#define TX_MULTICAST_RESERV (TX_MULTICAST_SIZE*2)
-/* Actual number of TX packets queued, must be
- <= TX_RING_SIZE-TX_MULTICAST_RESERV. */
-#define TX_QUEUE_LIMIT (TX_RING_SIZE-TX_MULTICAST_RESERV)
-/* Hysteresis marking queue as no longer full. */
-#define TX_QUEUE_UNFULL (TX_QUEUE_LIMIT-4)
-
-/* Operational parameters that usually are not changed. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (2*HZ)
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ 1536
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/pci.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/mii.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/skbuff.h>
-#include <linux/ethtool.h>
-
-static int use_io;
-static int debug = -1;
-#define DEBUG_DEFAULT (NETIF_MSG_DRV | \
- NETIF_MSG_HW | \
- NETIF_MSG_RX_ERR | \
- NETIF_MSG_TX_ERR)
-#define DEBUG ((debug >= 0) ? (1<<debug)-1 : DEBUG_DEFAULT)
-
-
-MODULE_AUTHOR("Maintainer: Andrey V. Savochkin <saw@saw.sw.com.sg>");
-MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver");
-MODULE_LICENSE("GPL");
-module_param(use_io, int, 0);
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param_array(full_duplex, int, NULL, 0);
-module_param(congenb, int, 0);
-module_param(txfifo, int, 0);
-module_param(rxfifo, int, 0);
-module_param(txdmacount, int, 0);
-module_param(rxdmacount, int, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-module_param(multicast_filter_limit, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(options, "Bits 0-3: transceiver type, bit 4: full duplex, bit 5: 100Mbps");
-MODULE_PARM_DESC(full_duplex, "full duplex setting(s) (1)");
-MODULE_PARM_DESC(congenb, "Enable congestion control (1)");
-MODULE_PARM_DESC(txfifo, "Tx FIFO threshold in 4 byte units, (0-15)");
-MODULE_PARM_DESC(rxfifo, "Rx FIFO threshold in 4 byte units, (0-15)");
-MODULE_PARM_DESC(txdmacount, "Tx DMA burst length; 128 - disable (0-128)");
-MODULE_PARM_DESC(rxdmacount, "Rx DMA burst length; 128 - disable (0-128)");
-MODULE_PARM_DESC(rx_copybreak, "copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-MODULE_PARM_DESC(multicast_filter_limit, "maximum number of filtered multicast addresses");
-
-#define RUN_AT(x) (jiffies + (x))
-
-#define netdevice_start(dev)
-#define netdevice_stop(dev)
-#define netif_set_tx_timeout(dev, tf, tm) \
- do { \
- (dev)->tx_timeout = (tf); \
- (dev)->watchdog_timeo = (tm); \
- } while(0)
-
-
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
-single-chip fast Ethernet controller for PCI, as used on the Intel
-EtherExpress Pro 100 adapter.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line. While it's
-possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it.
-
-III. Driver operation
-
-IIIA. General
-The Speedo3 is very similar to other Intel network chips, that is to say
-"apparently designed on a different planet". This chips retains the complex
-Rx and Tx descriptors and multiple buffers pointers as previous chips, but
-also has simplified Tx and Rx buffer modes. This driver uses the "flexible"
-Tx mode, but in a simplified lower-overhead manner: it associates only a
-single buffer descriptor with each frame descriptor.
-
-Despite the extra space overhead in each receive skbuff, the driver must use
-the simplified Rx buffer mode to assure that only a single data buffer is
-associated with each RxFD. The driver implements this by reserving space
-for the Rx descriptor at the head of each Rx skbuff.
-
-The Speedo-3 has receive and command unit base addresses that are added to
-almost all descriptor pointers. The driver sets these to zero, so that all
-pointer fields are absolute addresses.
-
-The System Control Block (SCB) of some previous Intel chips exists on the
-chip in both PCI I/O and memory space. This driver uses the I/O space
-registers, but might switch to memory mapped mode to better support non-x86
-processors.
-
-IIIB. Transmit structure
-
-The driver must use the complex Tx command+descriptor mode in order to
-have a indirect pointer to the skbuff data section. Each Tx command block
-(TxCB) is associated with two immediately appended Tx Buffer Descriptor
-(TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the
-speedo_private data structure for each adapter instance.
-
-The newer i82558 explicitly supports this structure, and can read the two
-TxBDs in the same PCI burst as the TxCB.
-
-This ring structure is used for all normal transmit packets, but the
-transmit packet descriptors aren't long enough for most non-Tx commands such
-as CmdConfigure. This is complicated by the possibility that the chip has
-already loaded the link address in the previous descriptor. So for these
-commands we convert the next free descriptor on the ring to a NoOp, and point
-that descriptor's link to the complex command.
-
-An additional complexity of these non-transmit commands are that they may be
-added asynchronous to the normal transmit queue, so we disable interrupts
-whenever the Tx descriptor ring is manipulated.
-
-A notable aspect of these special configure commands is that they do
-work with the normal Tx ring entry scavenge method. The Tx ring scavenge
-is done at interrupt time using the 'dirty_tx' index, and checking for the
-command-complete bit. While the setup frames may have the NoOp command on the
-Tx ring marked as complete, but not have completed the setup command, this
-is not a problem. The tx_ring entry can be still safely reused, as the
-tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
-
-Commands may have bits set e.g. CmdSuspend in the command word to either
-suspend or stop the transmit/command unit. This driver always flags the last
-command with CmdSuspend, erases the CmdSuspend in the previous command, and
-then issues a CU_RESUME.
-Note: Watch out for the potential race condition here: imagine
- erasing the previous suspend
- the chip processes the previous command
- the chip processes the final command, and suspends
- doing the CU_RESUME
- the chip processes the next-yet-valid post-final-command.
-So blindly sending a CU_RESUME is only safe if we do it immediately after
-after erasing the previous CmdSuspend, without the possibility of an
-intervening delay. Thus the resume command is always within the
-interrupts-disabled region. This is a timing dependence, but handling this
-condition in a timing-independent way would considerably complicate the code.
-
-Note: In previous generation Intel chips, restarting the command unit was a
-notoriously slow process. This is presumably no longer true.
-
-IIIC. Receive structure
-
-Because of the bus-master support on the Speedo3 this driver uses the new
-SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
-This scheme allocates full-sized skbuffs as receive buffers. The value
-SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
-trade-off the memory wasted by passing the full-sized skbuff to the queue
-layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-For small frames the copying cost is negligible (esp. considering that we
-are pre-loading the cache with immediately useful header information), so we
-allocate a new, minimally-sized skbuff. For large frames the copying cost
-is non-trivial, and the larger copy might flush the cache of useful data, so
-we pass up the skbuff the packet was received into.
-
-IV. Notes
-
-Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
-that stated that I could disclose the information. But I still resent
-having to sign an Intel NDA when I'm helping Intel sell their own product!
-
-*/
-
-static int speedo_found1(struct pci_dev *pdev, void __iomem *ioaddr, int fnd_cnt, int acpi_idle_state);
-
-/* Offsets to the various registers.
- All accesses need not be longword aligned. */
-enum speedo_offsets {
- SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
- SCBIntmask = 3,
- SCBPointer = 4, /* General purpose pointer. */
- SCBPort = 8, /* Misc. commands and operands. */
- SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
- SCBCtrlMDI = 16, /* MDI interface control. */
- SCBEarlyRx = 20, /* Early receive byte count. */
-};
-/* Commands that can be put in a command list entry. */
-enum commands {
- CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000,
- CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000,
- CmdDump = 0x60000, CmdDiagnose = 0x70000,
- CmdSuspend = 0x40000000, /* Suspend after completion. */
- CmdIntr = 0x20000000, /* Interrupt after completion. */
- CmdTxFlex = 0x00080000, /* Use "Flexible mode" for CmdTx command. */
-};
-/* Clear CmdSuspend (1<<30) avoiding interference with the card access to the
- status bits. Previous driver versions used separate 16 bit fields for
- commands and statuses. --SAW
- */
-#if defined(__alpha__)
-# define clear_suspend(cmd) clear_bit(30, &(cmd)->cmd_status);
-#else
-# define clear_suspend(cmd) ((__le16 *)&(cmd)->cmd_status)[1] &= ~cpu_to_le16(1<<14)
-#endif
-
-enum SCBCmdBits {
- SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
- SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
- SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
- /* The rest are Rx and Tx commands. */
- CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
- CUCmdBase=0x0060, /* CU Base address (set to zero) . */
- CUDumpStats=0x0070, /* Dump then reset stats counters. */
- RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
- RxResumeNoResources=0x0007,
-};
-
-enum SCBPort_cmds {
- PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3,
-};
-
-/* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor { /* A generic descriptor. */
- volatile __le32 cmd_status; /* All command and status fields. */
- __le32 link; /* struct descriptor * */
- unsigned char params[0];
-};
-
-/* The Speedo3 Rx and Tx buffer descriptors. */
-struct RxFD { /* Receive frame descriptor. */
- volatile __le32 status;
- __le32 link; /* struct RxFD * */
- __le32 rx_buf_addr; /* void * */
- __le32 count;
-} RxFD_ALIGNMENT;
-
-/* Selected elements of the Tx/RxFD.status word. */
-enum RxFD_bits {
- RxComplete=0x8000, RxOK=0x2000,
- RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010,
- RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002,
- TxUnderrun=0x1000, StatusComplete=0x8000,
-};
-
-#define CONFIG_DATA_SIZE 22
-struct TxFD { /* Transmit frame descriptor set. */
- __le32 status;
- __le32 link; /* void * */
- __le32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
- __le32 count; /* # of TBD (=1), Tx start thresh., etc. */
- /* This constitutes two "TBD" entries -- we only use one. */
-#define TX_DESCR_BUF_OFFSET 16
- __le32 tx_buf_addr0; /* void *, frame to be transmitted. */
- __le32 tx_buf_size0; /* Length of Tx frame. */
- __le32 tx_buf_addr1; /* void *, frame to be transmitted. */
- __le32 tx_buf_size1; /* Length of Tx frame. */
- /* the structure must have space for at least CONFIG_DATA_SIZE starting
- * from tx_desc_addr field */
-};
-
-/* Multicast filter setting block. --SAW */
-struct speedo_mc_block {
- struct speedo_mc_block *next;
- unsigned int tx;
- dma_addr_t frame_dma;
- unsigned int len;
- struct descriptor frame __attribute__ ((__aligned__(16)));
-};
-
-/* Elements of the dump_statistics block. This block must be lword aligned. */
-struct speedo_stats {
- __le32 tx_good_frames;
- __le32 tx_coll16_errs;
- __le32 tx_late_colls;
- __le32 tx_underruns;
- __le32 tx_lost_carrier;
- __le32 tx_deferred;
- __le32 tx_one_colls;
- __le32 tx_multi_colls;
- __le32 tx_total_colls;
- __le32 rx_good_frames;
- __le32 rx_crc_errs;
- __le32 rx_align_errs;
- __le32 rx_resource_errs;
- __le32 rx_overrun_errs;
- __le32 rx_colls_errs;
- __le32 rx_runt_errs;
- __le32 done_marker;
-};
-
-enum Rx_ring_state_bits {
- RrNoMem=1, RrPostponed=2, RrNoResources=4, RrOOMReported=8,
-};
-
-/* Do not change the position (alignment) of the first few elements!
- The later elements are grouped for cache locality.
-
- Unfortunately, all the positions have been shifted since there.
- A new re-alignment is required. 2000/03/06 SAW */
-struct speedo_private {
- void __iomem *regs;
- struct TxFD *tx_ring; /* Commands (usually CmdTxPacket). */
- struct RxFD *rx_ringp[RX_RING_SIZE]; /* Rx descriptor, used as ring. */
- /* The addresses of a Tx/Rx-in-place packets/buffers. */
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- /* Mapped addresses of the rings. */
- dma_addr_t tx_ring_dma;
-#define TX_RING_ELEM_DMA(sp, n) ((sp)->tx_ring_dma + (n)*sizeof(struct TxFD))
- dma_addr_t rx_ring_dma[RX_RING_SIZE];
- struct descriptor *last_cmd; /* Last command sent. */
- unsigned int cur_tx, dirty_tx; /* The ring entries to be free()ed. */
- spinlock_t lock; /* Group with Tx control cache line. */
- u32 tx_threshold; /* The value for txdesc.count. */
- struct RxFD *last_rxf; /* Last filled RX buffer. */
- dma_addr_t last_rxf_dma;
- unsigned int cur_rx, dirty_rx; /* The next free ring entry */
- long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
- struct net_device_stats stats;
- struct speedo_stats *lstats;
- dma_addr_t lstats_dma;
- int chip_id;
- struct pci_dev *pdev;
- struct timer_list timer; /* Media selection timer. */
- struct speedo_mc_block *mc_setup_head; /* Multicast setup frame list head. */
- struct speedo_mc_block *mc_setup_tail; /* Multicast setup frame list tail. */
- long in_interrupt; /* Word-aligned dev->interrupt */
- unsigned char acpi_pwr;
- signed char rx_mode; /* Current PROMISC/ALLMULTI setting. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int flow_ctrl:1; /* Use 802.3x flow control. */
- unsigned int rx_bug:1; /* Work around receiver hang errata. */
- unsigned char default_port:8; /* Last dev->if_port value. */
- unsigned char rx_ring_state; /* RX ring status flags. */
- unsigned short phy[2]; /* PHY media interfaces available. */
- unsigned short partner; /* Link partner caps. */
- struct mii_if_info mii_if; /* MII API hooks, info */
- u32 msg_enable; /* debug message level */
-};
-
-/* The parameters for a CmdConfigure operation.
- There are so many options that it would be difficult to document each bit.
- We mostly use the default or recommended settings. */
-static const char i82557_config_cmd[CONFIG_DATA_SIZE] = {
- 22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
- 0, 0x2E, 0, 0x60, 0,
- 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
- 0x3f, 0x05, };
-static const char i82558_config_cmd[CONFIG_DATA_SIZE] = {
- 22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */
- 0, 0x2E, 0, 0x60, 0x08, 0x88,
- 0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */
- 0x31, 0x05, };
-
-/* PHY media interface chips. */
-static const char * const phys[] = {
- "None", "i82553-A/B", "i82553-C", "i82503",
- "DP83840", "80c240", "80c24", "i82555",
- "unknown-8", "unknown-9", "DP83840A", "unknown-11",
- "unknown-12", "unknown-13", "unknown-14", "unknown-15", };
-enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
- S80C24, I82555, DP83840A=10, };
-static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
-#define EE_READ_CMD (6)
-
-static int eepro100_init_one(struct pci_dev *pdev,
- const struct pci_device_id *ent);
-
-static int do_eeprom_cmd(void __iomem *ioaddr, int cmd, int cmd_len);
-static int mdio_read(struct net_device *dev, int phy_id, int location);
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
-static int speedo_open(struct net_device *dev);
-static void speedo_resume(struct net_device *dev);
-static void speedo_timer(unsigned long data);
-static void speedo_init_rx_ring(struct net_device *dev);
-static void speedo_tx_timeout(struct net_device *dev);
-static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void speedo_refill_rx_buffers(struct net_device *dev, int force);
-static int speedo_rx(struct net_device *dev);
-static void speedo_tx_buffer_gc(struct net_device *dev);
-static irqreturn_t speedo_interrupt(int irq, void *dev_instance);
-static int speedo_close(struct net_device *dev);
-static struct net_device_stats *speedo_get_stats(struct net_device *dev);
-static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct net_device *dev);
-static void speedo_show_state(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-
-
-
-#ifdef honor_default_port
-/* Optional driver feature to allow forcing the transceiver setting.
- Not recommended. */
-static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100,
- 0x2000, 0x2100, 0x0400, 0x3100};
-#endif
-
-/* How to wait for the command unit to accept a command.
- Typically this takes 0 ticks. */
-static inline unsigned char wait_for_cmd_done(struct net_device *dev,
- struct speedo_private *sp)
-{
- int wait = 1000;
- void __iomem *cmd_ioaddr = sp->regs + SCBCmd;
- unsigned char r;
-
- do {
- udelay(1);
- r = ioread8(cmd_ioaddr);
- } while(r && --wait >= 0);
-
- if (wait < 0)
- printk(KERN_ALERT "%s: wait_for_cmd_done timeout!\n", dev->name);
- return r;
-}
-
-static int __devinit eepro100_init_one (struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- void __iomem *ioaddr;
- int irq, pci_bar;
- int acpi_idle_state = 0, pm;
- static int cards_found /* = 0 */;
- unsigned long pci_base;
-
-#ifndef MODULE
- /* when built-in, we only print version if device is found */
- static int did_version;
- if (did_version++ == 0)
- printk(version);
-#endif
-
- /* save power state before pci_enable_device overwrites it */
- pm = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pm) {
- u16 pwr_command;
- pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command);
- acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
- }
-
- if (pci_enable_device(pdev))
- goto err_out_free_mmio_region;
-
- pci_set_master(pdev);
-
- if (!request_region(pci_resource_start(pdev, 1),
- pci_resource_len(pdev, 1), "eepro100")) {
- dev_err(&pdev->dev, "eepro100: cannot reserve I/O ports\n");
- goto err_out_none;
- }
- if (!request_mem_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0), "eepro100")) {
- dev_err(&pdev->dev, "eepro100: cannot reserve MMIO region\n");
- goto err_out_free_pio_region;
- }
-
- irq = pdev->irq;
- pci_bar = use_io ? 1 : 0;
- pci_base = pci_resource_start(pdev, pci_bar);
- if (DEBUG & NETIF_MSG_PROBE)
- printk("Found Intel i82557 PCI Speedo at %#lx, IRQ %d.\n",
- pci_base, irq);
-
- ioaddr = pci_iomap(pdev, pci_bar, 0);
- if (!ioaddr) {
- dev_err(&pdev->dev, "eepro100: cannot remap IO\n");
- goto err_out_free_mmio_region;
- }
-
- if (speedo_found1(pdev, ioaddr, cards_found, acpi_idle_state) == 0)
- cards_found++;
- else
- goto err_out_iounmap;
-
- return 0;
-
-err_out_iounmap: ;
- pci_iounmap(pdev, ioaddr);
-err_out_free_mmio_region:
- release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-err_out_free_pio_region:
- release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
-err_out_none:
- return -ENODEV;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling 'interrupt' - used by things like netconsole to send skbs
- * without having to re-enable interrupts. It's not called while
- * the interrupt routine is executing.
- */
-
-static void poll_speedo (struct net_device *dev)
-{
- /* disable_irq is not very nice, but with the funny lockless design
- we have no other choice. */
- disable_irq(dev->irq);
- speedo_interrupt (dev->irq, dev);
- enable_irq(dev->irq);
-}
-#endif
-
-static int __devinit speedo_found1(struct pci_dev *pdev,
- void __iomem *ioaddr, int card_idx, int acpi_idle_state)
-{
- struct net_device *dev;
- struct speedo_private *sp;
- const char *product;
- int i, option;
- u16 eeprom[0x100];
- int size;
- void *tx_ring_space;
- dma_addr_t tx_ring_dma;
- DECLARE_MAC_BUF(mac);
-
- size = TX_RING_SIZE * sizeof(struct TxFD) + sizeof(struct speedo_stats);
- tx_ring_space = pci_alloc_consistent(pdev, size, &tx_ring_dma);
- if (tx_ring_space == NULL)
- return -1;
-
- dev = alloc_etherdev(sizeof(struct speedo_private));
- if (dev == NULL) {
- printk(KERN_ERR "eepro100: Could not allocate ethernet device.\n");
- pci_free_consistent(pdev, size, tx_ring_space, tx_ring_dma);
- return -1;
- }
-
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- if (dev->mem_start > 0)
- option = dev->mem_start;
- else if (card_idx >= 0 && options[card_idx] >= 0)
- option = options[card_idx];
- else
- option = 0;
-
- rtnl_lock();
- if (dev_alloc_name(dev, dev->name) < 0)
- goto err_free_unlock;
-
- /* Read the station address EEPROM before doing the reset.
- Nominally his should even be done before accepting the device, but
- then we wouldn't have a device name with which to report the error.
- The size test is for 6 bit vs. 8 bit address serial EEPROMs.
- */
- {
- void __iomem *iobase;
- int read_cmd, ee_size;
- u16 sum;
- int j;
-
- /* Use IO only to avoid postponed writes and satisfy EEPROM timing
- requirements. */
- iobase = pci_iomap(pdev, 1, pci_resource_len(pdev, 1));
- if (!iobase)
- goto err_free_unlock;
- if ((do_eeprom_cmd(iobase, EE_READ_CMD << 24, 27) & 0xffe0000)
- == 0xffe0000) {
- ee_size = 0x100;
- read_cmd = EE_READ_CMD << 24;
- } else {
- ee_size = 0x40;
- read_cmd = EE_READ_CMD << 22;
- }
-
- for (j = 0, i = 0, sum = 0; i < ee_size; i++) {
- u16 value = do_eeprom_cmd(iobase, read_cmd | (i << 16), 27);
- eeprom[i] = value;
- sum += value;
- if (i < 3) {
- dev->dev_addr[j++] = value;
- dev->dev_addr[j++] = value >> 8;
- }
- }
- if (sum != 0xBABA)
- printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
- "check settings before activating this device!\n",
- dev->name, sum);
- /* Don't unregister_netdev(dev); as the EEPro may actually be
- usable, especially if the MAC address is set later.
- On the other hand, it may be unusable if MDI data is corrupted. */
-
- pci_iounmap(pdev, iobase);
- }
-
- /* Reset the chip: stop Tx and Rx processes and clear counters.
- This takes less than 10usec and will easily finish before the next
- action. */
- iowrite32(PortReset, ioaddr + SCBPort);
- ioread32(ioaddr + SCBPort);
- udelay(10);
-
- if (eeprom[3] & 0x0100)
- product = "OEM i82557/i82558 10/100 Ethernet";
- else
- product = pci_name(pdev);
-
- printk(KERN_INFO "%s: %s, %s, IRQ %d.\n", dev->name, product,
- print_mac(mac, dev->dev_addr), pdev->irq);
-
- sp = netdev_priv(dev);
-
- /* we must initialize this early, for mdio_{read,write} */
- sp->regs = ioaddr;
-
-#if 1 || defined(kernel_bloat)
- /* OK, this is pure kernel bloat. I don't like it when other drivers
- waste non-pageable kernel space to emit similar messages, but I need
- them for bug reports. */
- {
- const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
- /* The self-test results must be paragraph aligned. */
- volatile s32 *self_test_results;
- int boguscnt = 16000; /* Timeout for set-test. */
- if ((eeprom[3] & 0x03) != 0x03)
- printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
- " work-around.\n");
- printk(KERN_INFO " Board assembly %4.4x%2.2x-%3.3d, Physical"
- " connectors present:",
- eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
- for (i = 0; i < 4; i++)
- if (eeprom[5] & (1<<i))
- printk(connectors[i]);
- printk("\n"KERN_INFO" Primary interface chip %s PHY #%d.\n",
- phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
- if (eeprom[7] & 0x0700)
- printk(KERN_INFO " Secondary interface chip %s.\n",
- phys[(eeprom[7]>>8)&7]);
- if (((eeprom[6]>>8) & 0x3f) == DP83840
- || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
- int mdi_reg23 = mdio_read(dev, eeprom[6] & 0x1f, 23) | 0x0422;
- if (congenb)
- mdi_reg23 |= 0x0100;
- printk(KERN_INFO" DP83840 specific setup, setting register 23 to %4.4x.\n",
- mdi_reg23);
- mdio_write(dev, eeprom[6] & 0x1f, 23, mdi_reg23);
- }
- if ((option >= 0) && (option & 0x70)) {
- printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n",
- (option & 0x20 ? 100 : 10),
- (option & 0x10 ? "full" : "half"));
- mdio_write(dev, eeprom[6] & 0x1f, MII_BMCR,
- ((option & 0x20) ? 0x2000 : 0) | /* 100mbps? */
- ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
- }
-
- /* Perform a system self-test. */
- self_test_results = (s32*) ((((long) tx_ring_space) + 15) & ~0xf);
- self_test_results[0] = 0;
- self_test_results[1] = -1;
- iowrite32(tx_ring_dma | PortSelfTest, ioaddr + SCBPort);
- do {
- udelay(10);
- } while (self_test_results[1] == -1 && --boguscnt >= 0);
-
- if (boguscnt < 0) { /* Test optimized out. */
- printk(KERN_ERR "Self test failed, status %8.8x:\n"
- KERN_ERR " Failure to initialize the i82557.\n"
- KERN_ERR " Verify that the card is a bus-master"
- " capable slot.\n",
- self_test_results[1]);
- } else
- printk(KERN_INFO " General self-test: %s.\n"
- KERN_INFO " Serial sub-system self-test: %s.\n"
- KERN_INFO " Internal registers self-test: %s.\n"
- KERN_INFO " ROM checksum self-test: %s (%#8.8x).\n",
- self_test_results[1] & 0x1000 ? "failed" : "passed",
- self_test_results[1] & 0x0020 ? "failed" : "passed",
- self_test_results[1] & 0x0008 ? "failed" : "passed",
- self_test_results[1] & 0x0004 ? "failed" : "passed",
- self_test_results[0]);
- }
-#endif /* kernel_bloat */
-
- iowrite32(PortReset, ioaddr + SCBPort);
- ioread32(ioaddr + SCBPort);
- udelay(10);
-
- /* Return the chip to its original power state. */
- pci_set_power_state(pdev, acpi_idle_state);
-
- pci_set_drvdata (pdev, dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- dev->irq = pdev->irq;
-
- sp->pdev = pdev;
- sp->msg_enable = DEBUG;
- sp->acpi_pwr = acpi_idle_state;
- sp->tx_ring = tx_ring_space;
- sp->tx_ring_dma = tx_ring_dma;
- sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE);
- sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE);
- init_timer(&sp->timer); /* used in ioctl() */
- spin_lock_init(&sp->lock);
-
- sp->mii_if.full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
- if (card_idx >= 0) {
- if (full_duplex[card_idx] >= 0)
- sp->mii_if.full_duplex = full_duplex[card_idx];
- }
- sp->default_port = option >= 0 ? (option & 0x0f) : 0;
-
- sp->phy[0] = eeprom[6];
- sp->phy[1] = eeprom[7];
-
- sp->mii_if.phy_id = eeprom[6] & 0x1f;
- sp->mii_if.phy_id_mask = 0x1f;
- sp->mii_if.reg_num_mask = 0x1f;
- sp->mii_if.dev = dev;
- sp->mii_if.mdio_read = mdio_read;
- sp->mii_if.mdio_write = mdio_write;
-
- sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
- if (((pdev->device > 0x1030 && (pdev->device < 0x103F)))
- || (pdev->device == 0x2449) || (pdev->device == 0x2459)
- || (pdev->device == 0x245D)) {
- sp->chip_id = 1;
- }
-
- if (sp->rx_bug)
- printk(KERN_INFO " Receiver lock-up workaround activated.\n");
-
- /* The Speedo-specific entries in the device structure. */
- dev->open = &speedo_open;
- dev->hard_start_xmit = &speedo_start_xmit;
- netif_set_tx_timeout(dev, &speedo_tx_timeout, TX_TIMEOUT);
- dev->stop = &speedo_close;
- dev->get_stats = &speedo_get_stats;
- dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &speedo_ioctl;
- SET_ETHTOOL_OPS(dev, &ethtool_ops);
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = &poll_speedo;
-#endif
-
- if (register_netdevice(dev))
- goto err_free_unlock;
- rtnl_unlock();
-
- return 0;
-
- err_free_unlock:
- rtnl_unlock();
- free_netdev(dev);
- return -1;
-}
-
-static void do_slow_command(struct net_device *dev, struct speedo_private *sp, int cmd)
-{
- void __iomem *cmd_ioaddr = sp->regs + SCBCmd;
- int wait = 0;
- do
- if (ioread8(cmd_ioaddr) == 0) break;
- while(++wait <= 200);
- if (wait > 100)
- printk(KERN_ERR "Command %4.4x never accepted (%d polls)!\n",
- ioread8(cmd_ioaddr), wait);
-
- iowrite8(cmd, cmd_ioaddr);
-
- for (wait = 0; wait <= 100; wait++)
- if (ioread8(cmd_ioaddr) == 0) return;
- for (; wait <= 20000; wait++)
- if (ioread8(cmd_ioaddr) == 0) return;
- else udelay(1);
- printk(KERN_ERR "Command %4.4x was not accepted after %d polls!"
- " Current status %8.8x.\n",
- cmd, wait, ioread32(sp->regs + SCBStatus));
-}
-
-/* Serial EEPROM section.
- A "bit" grungy, but we work our way through bit-by-bit :->. */
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
-#define EE_CS 0x02 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
-#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
-#define EE_ENB (0x4800 | EE_CS)
-#define EE_WRITE_0 0x4802
-#define EE_WRITE_1 0x4806
-#define EE_OFFSET SCBeeprom
-
-/* The fixes for the code were kindly provided by Dragan Stancevic
- <visitor@valinux.com> to strictly follow Intel specifications of EEPROM
- access timing.
- The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
- interval for serial EEPROM. However, it looks like that there is an
- additional requirement dictating larger udelay's in the code below.
- 2000/05/24 SAW */
-static int __devinit do_eeprom_cmd(void __iomem *ioaddr, int cmd, int cmd_len)
-{
- unsigned retval = 0;
- void __iomem *ee_addr = ioaddr + SCBeeprom;
-
- iowrite16(EE_ENB, ee_addr); udelay(2);
- iowrite16(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
-
- /* Shift the command bits out. */
- do {
- short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
- iowrite16(dataval, ee_addr); udelay(2);
- iowrite16(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
- retval = (retval << 1) | ((ioread16(ee_addr) & EE_DATA_READ) ? 1 : 0);
- } while (--cmd_len >= 0);
- iowrite16(EE_ENB, ee_addr); udelay(2);
-
- /* Terminate the EEPROM access. */
- iowrite16(EE_ENB & ~EE_CS, ee_addr);
- return retval;
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int location)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
- iowrite32(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
- do {
- val = ioread32(ioaddr + SCBCtrlMDI);
- if (--boguscnt < 0) {
- printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
- break;
- }
- } while (! (val & 0x10000000));
- return val & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
- iowrite32(0x04000000 | (location<<16) | (phy_id<<21) | value,
- ioaddr + SCBCtrlMDI);
- do {
- val = ioread32(ioaddr + SCBCtrlMDI);
- if (--boguscnt < 0) {
- printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
- break;
- }
- } while (! (val & 0x10000000));
-}
-
-static int
-speedo_open(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int retval;
-
- if (netif_msg_ifup(sp))
- printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
-
- pci_set_power_state(sp->pdev, PCI_D0);
-
- /* Set up the Tx queue early.. */
- sp->cur_tx = 0;
- sp->dirty_tx = 0;
- sp->last_cmd = NULL;
- sp->tx_full = 0;
- sp->in_interrupt = 0;
-
- /* .. we can safely take handler calls during init. */
- retval = request_irq(dev->irq, &speedo_interrupt, IRQF_SHARED, dev->name, dev);
- if (retval) {
- return retval;
- }
-
- dev->if_port = sp->default_port;
-
-#ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us
- /* Retrigger negotiation to reset previous errors. */
- if ((sp->phy[0] & 0x8000) == 0) {
- int phy_addr = sp->phy[0] & 0x1f ;
- /* Use 0x3300 for restarting NWay, other values to force xcvr:
- 0x0000 10-HD
- 0x0100 10-FD
- 0x2000 100-HD
- 0x2100 100-FD
- */
-#ifdef honor_default_port
- mdio_write(dev, phy_addr, MII_BMCR, mii_ctrl[dev->default_port & 7]);
-#else
- mdio_write(dev, phy_addr, MII_BMCR, 0x3300);
-#endif
- }
-#endif
-
- speedo_init_rx_ring(dev);
-
- /* Fire up the hardware. */
- iowrite16(SCBMaskAll, ioaddr + SCBCmd);
- speedo_resume(dev);
-
- netdevice_start(dev);
- netif_start_queue(dev);
-
- /* Setup the chip and configure the multicast list. */
- sp->mc_setup_head = NULL;
- sp->mc_setup_tail = NULL;
- sp->flow_ctrl = sp->partner = 0;
- sp->rx_mode = -1; /* Invalid -> always reset the mode. */
- set_rx_mode(dev);
- if ((sp->phy[0] & 0x8000) == 0)
- sp->mii_if.advertising = mdio_read(dev, sp->phy[0] & 0x1f, MII_ADVERTISE);
-
- mii_check_link(&sp->mii_if);
-
- if (netif_msg_ifup(sp)) {
- printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
- dev->name, ioread16(ioaddr + SCBStatus));
- }
-
- /* Set the timer. The timer serves a dual purpose:
- 1) to monitor the media interface (e.g. link beat) and perhaps switch
- to an alternate media type
- 2) to monitor Rx activity, and restart the Rx process if the receiver
- hangs. */
- sp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
- sp->timer.data = (unsigned long)dev;
- sp->timer.function = &speedo_timer; /* timer handler */
- add_timer(&sp->timer);
-
- /* No need to wait for the command unit to accept here. */
- if ((sp->phy[0] & 0x8000) == 0)
- mdio_read(dev, sp->phy[0] & 0x1f, MII_BMCR);
-
- return 0;
-}
-
-/* Start the chip hardware after a full reset. */
-static void speedo_resume(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
-
- /* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */
- sp->tx_threshold = 0x01208000;
-
- /* Set the segment registers to '0'. */
- if (wait_for_cmd_done(dev, sp) != 0) {
- iowrite32(PortPartialReset, ioaddr + SCBPort);
- udelay(10);
- }
-
- iowrite32(0, ioaddr + SCBPointer);
- ioread32(ioaddr + SCBPointer); /* Flush to PCI. */
- udelay(10); /* Bogus, but it avoids the bug. */
-
- /* Note: these next two operations can take a while. */
- do_slow_command(dev, sp, RxAddrLoad);
- do_slow_command(dev, sp, CUCmdBase);
-
- /* Load the statistics block and rx ring addresses. */
- iowrite32(sp->lstats_dma, ioaddr + SCBPointer);
- ioread32(ioaddr + SCBPointer); /* Flush to PCI */
-
- iowrite8(CUStatsAddr, ioaddr + SCBCmd);
- sp->lstats->done_marker = 0;
- wait_for_cmd_done(dev, sp);
-
- if (sp->rx_ringp[sp->cur_rx % RX_RING_SIZE] == NULL) {
- if (netif_msg_rx_err(sp))
- printk(KERN_DEBUG "%s: NULL cur_rx in speedo_resume().\n",
- dev->name);
- } else {
- iowrite32(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
- ioaddr + SCBPointer);
- ioread32(ioaddr + SCBPointer); /* Flush to PCI */
- }
-
- /* Note: RxStart should complete instantly. */
- do_slow_command(dev, sp, RxStart);
- do_slow_command(dev, sp, CUDumpStats);
-
- /* Fill the first command with our physical address. */
- {
- struct descriptor *ias_cmd;
-
- ias_cmd =
- (struct descriptor *)&sp->tx_ring[sp->cur_tx++ % TX_RING_SIZE];
- /* Avoid a bug(?!) here by marking the command already completed. */
- ias_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000);
- ias_cmd->link =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
- memcpy(ias_cmd->params, dev->dev_addr, 6);
- if (sp->last_cmd)
- clear_suspend(sp->last_cmd);
- sp->last_cmd = ias_cmd;
- }
-
- /* Start the chip's Tx process and unmask interrupts. */
- iowrite32(TX_RING_ELEM_DMA(sp, sp->dirty_tx % TX_RING_SIZE),
- ioaddr + SCBPointer);
- /* We are not ACK-ing FCP and ER in the interrupt handler yet so they should
- remain masked --Dragan */
- iowrite16(CUStart | SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd);
-}
-
-/*
- * Sometimes the receiver stops making progress. This routine knows how to
- * get it going again, without losing packets or being otherwise nasty like
- * a chip reset would be. Previously the driver had a whole sequence
- * of if RxSuspended, if it's no buffers do one thing, if it's no resources,
- * do another, etc. But those things don't really matter. Separate logic
- * in the ISR provides for allocating buffers--the other half of operation
- * is just making sure the receiver is active. speedo_rx_soft_reset does that.
- * This problem with the old, more involved algorithm is shown up under
- * ping floods on the order of 60K packets/second on a 100Mbps fdx network.
- */
-static void
-speedo_rx_soft_reset(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- struct RxFD *rfd;
- void __iomem *ioaddr;
-
- ioaddr = sp->regs;
- if (wait_for_cmd_done(dev, sp) != 0) {
- printk("%s: previous command stalled\n", dev->name);
- return;
- }
- /*
- * Put the hardware into a known state.
- */
- iowrite8(RxAbort, ioaddr + SCBCmd);
-
- rfd = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
-
- rfd->rx_buf_addr = cpu_to_le32(0xffffffff);
-
- if (wait_for_cmd_done(dev, sp) != 0) {
- printk("%s: RxAbort command stalled\n", dev->name);
- return;
- }
- iowrite32(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
- ioaddr + SCBPointer);
- iowrite8(RxStart, ioaddr + SCBCmd);
-}
-
-
-/* Media monitoring and control. */
-static void speedo_timer(unsigned long data)
-{
- struct net_device *dev = (struct net_device *)data;
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int phy_num = sp->phy[0] & 0x1f;
-
- /* We have MII and lost link beat. */
- if ((sp->phy[0] & 0x8000) == 0) {
- int partner = mdio_read(dev, phy_num, MII_LPA);
- if (partner != sp->partner) {
- int flow_ctrl = sp->mii_if.advertising & partner & 0x0400 ? 1 : 0;
- if (netif_msg_link(sp)) {
- printk(KERN_DEBUG "%s: Link status change.\n", dev->name);
- printk(KERN_DEBUG "%s: Old partner %x, new %x, adv %x.\n",
- dev->name, sp->partner, partner, sp->mii_if.advertising);
- }
- sp->partner = partner;
- if (flow_ctrl != sp->flow_ctrl) {
- sp->flow_ctrl = flow_ctrl;
- sp->rx_mode = -1; /* Trigger a reload. */
- }
- }
- }
- mii_check_link(&sp->mii_if);
- if (netif_msg_timer(sp)) {
- printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n",
- dev->name, ioread16(ioaddr + SCBStatus));
- }
- if (sp->rx_mode < 0 ||
- (sp->rx_bug && jiffies - sp->last_rx_time > 2*HZ)) {
- /* We haven't received a packet in a Long Time. We might have been
- bitten by the receiver hang bug. This can be cleared by sending
- a set multicast list command. */
- if (netif_msg_timer(sp))
- printk(KERN_DEBUG "%s: Sending a multicast list set command"
- " from a timer routine,"
- " m=%d, j=%ld, l=%ld.\n",
- dev->name, sp->rx_mode, jiffies, sp->last_rx_time);
- set_rx_mode(dev);
- }
- /* We must continue to monitor the media. */
- sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */
- add_timer(&sp->timer);
-}
-
-static void speedo_show_state(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- int i;
-
- if (netif_msg_pktdata(sp)) {
- printk(KERN_DEBUG "%s: Tx ring dump, Tx queue %u / %u:\n",
- dev->name, sp->cur_tx, sp->dirty_tx);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk(KERN_DEBUG "%s: %c%c%2d %8.8x.\n", dev->name,
- i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ',
- i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ',
- i, sp->tx_ring[i].status);
-
- printk(KERN_DEBUG "%s: Printing Rx ring"
- " (next to receive into %u, dirty index %u).\n",
- dev->name, sp->cur_rx, sp->dirty_rx);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(KERN_DEBUG "%s: %c%c%c%2d %8.8x.\n", dev->name,
- sp->rx_ringp[i] == sp->last_rxf ? 'l' : ' ',
- i == sp->dirty_rx % RX_RING_SIZE ? '*' : ' ',
- i == sp->cur_rx % RX_RING_SIZE ? '=' : ' ',
- i, (sp->rx_ringp[i] != NULL) ?
- (unsigned)sp->rx_ringp[i]->status : 0);
- }
-
-#if 0
- {
- void __iomem *ioaddr = sp->regs;
- int phy_num = sp->phy[0] & 0x1f;
- for (i = 0; i < 16; i++) {
- /* FIXME: what does it mean? --SAW */
- if (i == 6) i = 21;
- printk(KERN_DEBUG "%s: PHY index %d register %d is %4.4x.\n",
- dev->name, phy_num, i, mdio_read(dev, phy_num, i));
- }
- }
-#endif
-
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-speedo_init_rx_ring(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- struct RxFD *rxf, *last_rxf = NULL;
- dma_addr_t last_rxf_dma = 0 /* to shut up the compiler */;
- int i;
-
- sp->cur_rx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
- if (skb)
- rx_align(skb); /* Align IP on 16 byte boundary */
- sp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break; /* OK. Just initially short of Rx bufs. */
- skb->dev = dev; /* Mark as being used by this device. */
- rxf = (struct RxFD *)skb->data;
- sp->rx_ringp[i] = rxf;
- sp->rx_ring_dma[i] =
- pci_map_single(sp->pdev, rxf,
- PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_BIDIRECTIONAL);
- skb_reserve(skb, sizeof(struct RxFD));
- if (last_rxf) {
- last_rxf->link = cpu_to_le32(sp->rx_ring_dma[i]);
- pci_dma_sync_single_for_device(sp->pdev, last_rxf_dma,
- sizeof(struct RxFD), PCI_DMA_TODEVICE);
- }
- last_rxf = rxf;
- last_rxf_dma = sp->rx_ring_dma[i];
- rxf->status = cpu_to_le32(0x00000001); /* '1' is flag value only. */
- rxf->link = 0; /* None yet. */
- /* This field unused by i82557. */
- rxf->rx_buf_addr = cpu_to_le32(0xffffffff);
- rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
- pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[i],
- sizeof(struct RxFD), PCI_DMA_TODEVICE);
- }
- sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
- /* Mark the last entry as end-of-list. */
- last_rxf->status = cpu_to_le32(0xC0000002); /* '2' is flag value only. */
- pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[RX_RING_SIZE-1],
- sizeof(struct RxFD), PCI_DMA_TODEVICE);
- sp->last_rxf = last_rxf;
- sp->last_rxf_dma = last_rxf_dma;
-}
-
-static void speedo_purge_tx(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- int entry;
-
- while ((int)(sp->cur_tx - sp->dirty_tx) > 0) {
- entry = sp->dirty_tx % TX_RING_SIZE;
- if (sp->tx_skbuff[entry]) {
- sp->stats.tx_errors++;
- pci_unmap_single(sp->pdev,
- le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
- sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(sp->tx_skbuff[entry]);
- sp->tx_skbuff[entry] = NULL;
- }
- sp->dirty_tx++;
- }
- while (sp->mc_setup_head != NULL) {
- struct speedo_mc_block *t;
- if (netif_msg_tx_err(sp))
- printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
- pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
- sp->mc_setup_head->len, PCI_DMA_TODEVICE);
- t = sp->mc_setup_head->next;
- kfree(sp->mc_setup_head);
- sp->mc_setup_head = t;
- }
- sp->mc_setup_tail = NULL;
- sp->tx_full = 0;
- netif_wake_queue(dev);
-}
-
-static void reset_mii(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
-
- /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
- if ((sp->phy[0] & 0x8000) == 0) {
- int phy_addr = sp->phy[0] & 0x1f;
- int advertising = mdio_read(dev, phy_addr, MII_ADVERTISE);
- int mii_bmcr = mdio_read(dev, phy_addr, MII_BMCR);
- mdio_write(dev, phy_addr, MII_BMCR, 0x0400);
- mdio_write(dev, phy_addr, MII_BMSR, 0x0000);
- mdio_write(dev, phy_addr, MII_ADVERTISE, 0x0000);
- mdio_write(dev, phy_addr, MII_BMCR, 0x8000);
-#ifdef honor_default_port
- mdio_write(dev, phy_addr, MII_BMCR, mii_ctrl[dev->default_port & 7]);
-#else
- mdio_read(dev, phy_addr, MII_BMCR);
- mdio_write(dev, phy_addr, MII_BMCR, mii_bmcr);
- mdio_write(dev, phy_addr, MII_ADVERTISE, advertising);
-#endif
- }
-}
-
-static void speedo_tx_timeout(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int status = ioread16(ioaddr + SCBStatus);
- unsigned long flags;
-
- if (netif_msg_tx_err(sp)) {
- printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
- " %4.4x at %d/%d command %8.8x.\n",
- dev->name, status, ioread16(ioaddr + SCBCmd),
- sp->dirty_tx, sp->cur_tx,
- sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status);
-
- }
- speedo_show_state(dev);
-#if 0
- if ((status & 0x00C0) != 0x0080
- && (status & 0x003C) == 0x0010) {
- /* Only the command unit has stopped. */
- printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
- dev->name);
- iowrite32(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE]),
- ioaddr + SCBPointer);
- iowrite16(CUStart, ioaddr + SCBCmd);
- reset_mii(dev);
- } else {
-#else
- {
-#endif
- del_timer_sync(&sp->timer);
- /* Reset the Tx and Rx units. */
- iowrite32(PortReset, ioaddr + SCBPort);
- /* We may get spurious interrupts here. But I don't think that they
- may do much harm. 1999/12/09 SAW */
- udelay(10);
- /* Disable interrupts. */
- iowrite16(SCBMaskAll, ioaddr + SCBCmd);
- synchronize_irq(dev->irq);
- speedo_tx_buffer_gc(dev);
- /* Free as much as possible.
- It helps to recover from a hang because of out-of-memory.
- It also simplifies speedo_resume() in case TX ring is full or
- close-to-be full. */
- speedo_purge_tx(dev);
- speedo_refill_rx_buffers(dev, 1);
- spin_lock_irqsave(&sp->lock, flags);
- speedo_resume(dev);
- sp->rx_mode = -1;
- dev->trans_start = jiffies;
- spin_unlock_irqrestore(&sp->lock, flags);
- set_rx_mode(dev); /* it takes the spinlock itself --SAW */
- /* Reset MII transceiver. Do it before starting the timer to serialize
- mdio_xxx operations. Yes, it's a paranoya :-) 2000/05/09 SAW */
- reset_mii(dev);
- sp->timer.expires = RUN_AT(2*HZ);
- add_timer(&sp->timer);
- }
- return;
-}
-
-static int
-speedo_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int entry;
-
- /* Prevent interrupts from changing the Tx ring from underneath us. */
- unsigned long flags;
-
- spin_lock_irqsave(&sp->lock, flags);
-
- /* Check if there are enough space. */
- if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
- printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", dev->name);
- netif_stop_queue(dev);
- sp->tx_full = 1;
- spin_unlock_irqrestore(&sp->lock, flags);
- return 1;
- }
-
- /* Calculate the Tx descriptor entry. */
- entry = sp->cur_tx++ % TX_RING_SIZE;
-
- sp->tx_skbuff[entry] = skb;
- sp->tx_ring[entry].status =
- cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex);
- if (!(entry & ((TX_RING_SIZE>>2)-1)))
- sp->tx_ring[entry].status |= cpu_to_le32(CmdIntr);
- sp->tx_ring[entry].link =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
- sp->tx_ring[entry].tx_desc_addr =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, entry) + TX_DESCR_BUF_OFFSET);
- /* The data region is always in one buffer descriptor. */
- sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold);
- sp->tx_ring[entry].tx_buf_addr0 =
- cpu_to_le32(pci_map_single(sp->pdev, skb->data,
- skb->len, PCI_DMA_TODEVICE));
- sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len);
-
- /* workaround for hardware bug on 10 mbit half duplex */
-
- if ((sp->partner == 0) && (sp->chip_id == 1)) {
- wait_for_cmd_done(dev, sp);
- iowrite8(0 , ioaddr + SCBCmd);
- udelay(1);
- }
-
- /* Trigger the command unit resume. */
- wait_for_cmd_done(dev, sp);
- clear_suspend(sp->last_cmd);
- /* We want the time window between clearing suspend flag on the previous
- command and resuming CU to be as small as possible.
- Interrupts in between are very undesired. --SAW */
- iowrite8(CUResume, ioaddr + SCBCmd);
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
- /* Leave room for set_rx_mode(). If there is no more space than reserved
- for multicast filter mark the ring as full. */
- if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
- netif_stop_queue(dev);
- sp->tx_full = 1;
- }
-
- spin_unlock_irqrestore(&sp->lock, flags);
-
- dev->trans_start = jiffies;
-
- return 0;
-}
-
-static void speedo_tx_buffer_gc(struct net_device *dev)
-{
- unsigned int dirty_tx;
- struct speedo_private *sp = netdev_priv(dev);
-
- dirty_tx = sp->dirty_tx;
- while ((int)(sp->cur_tx - dirty_tx) > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = le32_to_cpu(sp->tx_ring[entry].status);
-
- if (netif_msg_tx_done(sp))
- printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
- entry, status);
- if ((status & StatusComplete) == 0)
- break; /* It still hasn't been processed. */
- if (status & TxUnderrun)
- if (sp->tx_threshold < 0x01e08000) {
- if (netif_msg_tx_err(sp))
- printk(KERN_DEBUG "%s: TX underrun, threshold adjusted.\n",
- dev->name);
- sp->tx_threshold += 0x00040000;
- }
- /* Free the original skb. */
- if (sp->tx_skbuff[entry]) {
- sp->stats.tx_packets++; /* Count only user packets. */
- sp->stats.tx_bytes += sp->tx_skbuff[entry]->len;
- pci_unmap_single(sp->pdev,
- le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
- sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(sp->tx_skbuff[entry]);
- sp->tx_skbuff[entry] = NULL;
- }
- dirty_tx++;
- }
-
- if (netif_msg_tx_err(sp) && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) {
- printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
- " full=%d.\n",
- dirty_tx, sp->cur_tx, sp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-
- while (sp->mc_setup_head != NULL
- && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) {
- struct speedo_mc_block *t;
- if (netif_msg_tx_err(sp))
- printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
- pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
- sp->mc_setup_head->len, PCI_DMA_TODEVICE);
- t = sp->mc_setup_head->next;
- kfree(sp->mc_setup_head);
- sp->mc_setup_head = t;
- }
- if (sp->mc_setup_head == NULL)
- sp->mc_setup_tail = NULL;
-
- sp->dirty_tx = dirty_tx;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static irqreturn_t speedo_interrupt(int irq, void *dev_instance)
-{
- struct net_device *dev = (struct net_device *)dev_instance;
- struct speedo_private *sp;
- void __iomem *ioaddr;
- long boguscnt = max_interrupt_work;
- unsigned short status;
- unsigned int handled = 0;
-
- sp = netdev_priv(dev);
- ioaddr = sp->regs;
-
-#ifndef final_version
- /* A lock to prevent simultaneous entry on SMP machines. */
- if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
- printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
- dev->name);
- sp->in_interrupt = 0; /* Avoid halting machine. */
- return IRQ_NONE;
- }
-#endif
-
- do {
- status = ioread16(ioaddr + SCBStatus);
- /* Acknowledge all of the current interrupt sources ASAP. */
- /* Will change from 0xfc00 to 0xff00 when we start handling
- FCP and ER interrupts --Dragan */
- iowrite16(status & 0xfc00, ioaddr + SCBStatus);
-
- if (netif_msg_intr(sp))
- printk(KERN_DEBUG "%s: interrupt status=%#4.4x.\n",
- dev->name, status);
-
- if ((status & 0xfc00) == 0)
- break;
- handled = 1;
-
-
- if ((status & 0x5000) || /* Packet received, or Rx error. */
- (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)
- /* Need to gather the postponed packet. */
- speedo_rx(dev);
-
- /* Always check if all rx buffers are allocated. --SAW */
- speedo_refill_rx_buffers(dev, 0);
-
- spin_lock(&sp->lock);
- /*
- * The chip may have suspended reception for various reasons.
- * Check for that, and re-prime it should this be the case.
- */
- switch ((status >> 2) & 0xf) {
- case 0: /* Idle */
- break;
- case 1: /* Suspended */
- case 2: /* No resources (RxFDs) */
- case 9: /* Suspended with no more RBDs */
- case 10: /* No resources due to no RBDs */
- case 12: /* Ready with no RBDs */
- speedo_rx_soft_reset(dev);
- break;
- case 3: case 5: case 6: case 7: case 8:
- case 11: case 13: case 14: case 15:
- /* these are all reserved values */
- break;
- }
-
-
- /* User interrupt, Command/Tx unit interrupt or CU not active. */
- if (status & 0xA400) {
- speedo_tx_buffer_gc(dev);
- if (sp->tx_full
- && (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) {
- /* The ring is no longer full. */
- sp->tx_full = 0;
- netif_wake_queue(dev); /* Attention: under a spinlock. --SAW */
- }
- }
-
- spin_unlock(&sp->lock);
-
- if (--boguscnt < 0) {
- printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
- dev->name, status);
- /* Clear all interrupt sources. */
- /* Will change from 0xfc00 to 0xff00 when we start handling
- FCP and ER interrupts --Dragan */
- iowrite16(0xfc00, ioaddr + SCBStatus);
- break;
- }
- } while (1);
-
- if (netif_msg_intr(sp))
- printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
- dev->name, ioread16(ioaddr + SCBStatus));
-
- clear_bit(0, (void*)&sp->in_interrupt);
- return IRQ_RETVAL(handled);
-}
-
-static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
-{
- struct speedo_private *sp = netdev_priv(dev);
- struct RxFD *rxf;
- struct sk_buff *skb;
- /* Get a fresh skbuff to replace the consumed one. */
- skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
- if (skb)
- rx_align(skb); /* Align IP on 16 byte boundary */
- sp->rx_skbuff[entry] = skb;
- if (skb == NULL) {
- sp->rx_ringp[entry] = NULL;
- return NULL;
- }
- rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->data;
- sp->rx_ring_dma[entry] =
- pci_map_single(sp->pdev, rxf,
- PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
- skb->dev = dev;
- skb_reserve(skb, sizeof(struct RxFD));
- rxf->rx_buf_addr = cpu_to_le32(0xffffffff);
- pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
- sizeof(struct RxFD), PCI_DMA_TODEVICE);
- return rxf;
-}
-
-static inline void speedo_rx_link(struct net_device *dev, int entry,
- struct RxFD *rxf, dma_addr_t rxf_dma)
-{
- struct speedo_private *sp = netdev_priv(dev);
- rxf->status = cpu_to_le32(0xC0000001); /* '1' for driver use only. */
- rxf->link = 0; /* None yet. */
- rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
- sp->last_rxf->link = cpu_to_le32(rxf_dma);
- sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
- pci_dma_sync_single_for_device(sp->pdev, sp->last_rxf_dma,
- sizeof(struct RxFD), PCI_DMA_TODEVICE);
- sp->last_rxf = rxf;
- sp->last_rxf_dma = rxf_dma;
-}
-
-static int speedo_refill_rx_buf(struct net_device *dev, int force)
-{
- struct speedo_private *sp = netdev_priv(dev);
- int entry;
- struct RxFD *rxf;
-
- entry = sp->dirty_rx % RX_RING_SIZE;
- if (sp->rx_skbuff[entry] == NULL) {
- rxf = speedo_rx_alloc(dev, entry);
- if (rxf == NULL) {
- unsigned int forw;
- int forw_entry;
- if (netif_msg_rx_err(sp) || !(sp->rx_ring_state & RrOOMReported)) {
- printk(KERN_WARNING "%s: can't fill rx buffer (force %d)!\n",
- dev->name, force);
- sp->rx_ring_state |= RrOOMReported;
- }
- speedo_show_state(dev);
- if (!force)
- return -1; /* Better luck next time! */
- /* Borrow an skb from one of next entries. */
- for (forw = sp->dirty_rx + 1; forw != sp->cur_rx; forw++)
- if (sp->rx_skbuff[forw % RX_RING_SIZE] != NULL)
- break;
- if (forw == sp->cur_rx)
- return -1;
- forw_entry = forw % RX_RING_SIZE;
- sp->rx_skbuff[entry] = sp->rx_skbuff[forw_entry];
- sp->rx_skbuff[forw_entry] = NULL;
- rxf = sp->rx_ringp[forw_entry];
- sp->rx_ringp[forw_entry] = NULL;
- sp->rx_ringp[entry] = rxf;
- }
- } else {
- rxf = sp->rx_ringp[entry];
- }
- speedo_rx_link(dev, entry, rxf, sp->rx_ring_dma[entry]);
- sp->dirty_rx++;
- sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); /* Mark the progress. */
- return 0;
-}
-
-static void speedo_refill_rx_buffers(struct net_device *dev, int force)
-{
- struct speedo_private *sp = netdev_priv(dev);
-
- /* Refill the RX ring. */
- while ((int)(sp->cur_rx - sp->dirty_rx) > 0 &&
- speedo_refill_rx_buf(dev, force) != -1);
-}
-
-static int
-speedo_rx(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- int entry = sp->cur_rx % RX_RING_SIZE;
- int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
- int alloc_ok = 1;
- int npkts = 0;
-
- if (netif_msg_intr(sp))
- printk(KERN_DEBUG " In speedo_rx().\n");
- /* If we own the next entry, it's a new packet. Send it up. */
- while (sp->rx_ringp[entry] != NULL) {
- int status;
- int pkt_len;
-
- pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
- sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
- status = le32_to_cpu(sp->rx_ringp[entry]->status);
- pkt_len = le32_to_cpu(sp->rx_ringp[entry]->count) & 0x3fff;
-
- if (!(status & RxComplete))
- break;
-
- if (--rx_work_limit < 0)
- break;
-
- /* Check for a rare out-of-memory case: the current buffer is
- the last buffer allocated in the RX ring. --SAW */
- if (sp->last_rxf == sp->rx_ringp[entry]) {
- /* Postpone the packet. It'll be reaped at an interrupt when this
- packet is no longer the last packet in the ring. */
- if (netif_msg_rx_err(sp))
- printk(KERN_DEBUG "%s: RX packet postponed!\n",
- dev->name);
- sp->rx_ring_state |= RrPostponed;
- break;
- }
-
- if (netif_msg_rx_status(sp))
- printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status,
- pkt_len);
- if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) {
- if (status & RxErrTooBig)
- printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
- "status %8.8x!\n", dev->name, status);
- else if (! (status & RxOK)) {
- /* There was a fatal error. This *should* be impossible. */
- sp->stats.rx_errors++;
- printk(KERN_ERR "%s: Anomalous event in speedo_rx(), "
- "status %8.8x.\n",
- dev->name, status);
- }
- } else {
- struct sk_buff *skb;
-
- /* Check if the packet is long enough to just accept without
- copying to a properly sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- /* 'skb_put()' points to the start of sk_buff data area. */
- pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry],
- sizeof(struct RxFD) + pkt_len,
- PCI_DMA_FROMDEVICE);
-
-#if 1 || USE_IP_CSUM
- /* Packet is in one chunk -- we can copy + cksum. */
- skb_copy_to_linear_data(skb, sp->rx_skbuff[entry]->data, pkt_len);
- skb_put(skb, pkt_len);
-#else
- skb_copy_from_linear_data(sp->rx_skbuff[entry],
- skb_put(skb, pkt_len),
- pkt_len);
-#endif
- pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry],
- sizeof(struct RxFD) + pkt_len,
- PCI_DMA_FROMDEVICE);
- npkts++;
- } else {
- /* Pass up the already-filled skbuff. */
- skb = sp->rx_skbuff[entry];
- if (skb == NULL) {
- printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n",
- dev->name);
- break;
- }
- sp->rx_skbuff[entry] = NULL;
- skb_put(skb, pkt_len);
- npkts++;
- sp->rx_ringp[entry] = NULL;
- pci_unmap_single(sp->pdev, sp->rx_ring_dma[entry],
- PKT_BUF_SZ + sizeof(struct RxFD),
- PCI_DMA_FROMDEVICE);
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->last_rx = jiffies;
- sp->stats.rx_packets++;
- sp->stats.rx_bytes += pkt_len;
- }
- entry = (++sp->cur_rx) % RX_RING_SIZE;
- sp->rx_ring_state &= ~RrPostponed;
- /* Refill the recently taken buffers.
- Do it one-by-one to handle traffic bursts better. */
- if (alloc_ok && speedo_refill_rx_buf(dev, 0) == -1)
- alloc_ok = 0;
- }
-
- /* Try hard to refill the recently taken buffers. */
- speedo_refill_rx_buffers(dev, 1);
-
- if (npkts)
- sp->last_rx_time = jiffies;
-
- return 0;
-}
-
-static int
-speedo_close(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int i;
-
- netdevice_stop(dev);
- netif_stop_queue(dev);
-
- if (netif_msg_ifdown(sp))
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
- dev->name, ioread16(ioaddr + SCBStatus));
-
- /* Shut off the media monitoring timer. */
- del_timer_sync(&sp->timer);
-
- iowrite16(SCBMaskAll, ioaddr + SCBCmd);
-
- /* Shutting down the chip nicely fails to disable flow control. So.. */
- iowrite32(PortPartialReset, ioaddr + SCBPort);
- ioread32(ioaddr + SCBPort); /* flush posted write */
- /*
- * The chip requires a 10 microsecond quiet period. Wait here!
- */
- udelay(10);
-
- free_irq(dev->irq, dev);
- speedo_show_state(dev);
-
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = sp->rx_skbuff[i];
- sp->rx_skbuff[i] = NULL;
- /* Clear the Rx descriptors. */
- if (skb) {
- pci_unmap_single(sp->pdev,
- sp->rx_ring_dma[i],
- PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
- dev_kfree_skb(skb);
- }
- }
-
- for (i = 0; i < TX_RING_SIZE; i++) {
- struct sk_buff *skb = sp->tx_skbuff[i];
- sp->tx_skbuff[i] = NULL;
- /* Clear the Tx descriptors. */
- if (skb) {
- pci_unmap_single(sp->pdev,
- le32_to_cpu(sp->tx_ring[i].tx_buf_addr0),
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb(skb);
- }
- }
-
- /* Free multicast setting blocks. */
- for (i = 0; sp->mc_setup_head != NULL; i++) {
- struct speedo_mc_block *t;
- t = sp->mc_setup_head->next;
- kfree(sp->mc_setup_head);
- sp->mc_setup_head = t;
- }
- sp->mc_setup_tail = NULL;
- if (netif_msg_ifdown(sp))
- printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i);
-
- pci_set_power_state(sp->pdev, PCI_D2);
-
- return 0;
-}
-
-/* The Speedo-3 has an especially awkward and unusable method of getting
- statistics out of the chip. It takes an unpredictable length of time
- for the dump-stats command to complete. To avoid a busy-wait loop we
- update the stats with the previous dump results, and then trigger a
- new dump.
-
- Oh, and incoming frames are dropped while executing dump-stats!
- */
-static struct net_device_stats *
-speedo_get_stats(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
-
- /* Update only if the previous dump finished. */
- if (sp->lstats->done_marker == cpu_to_le32(0xA007)) {
- sp->stats.tx_aborted_errors += le32_to_cpu(sp->lstats->tx_coll16_errs);
- sp->stats.tx_window_errors += le32_to_cpu(sp->lstats->tx_late_colls);
- sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats->tx_underruns);
- sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats->tx_lost_carrier);
- /*sp->stats.tx_deferred += le32_to_cpu(sp->lstats->tx_deferred);*/
- sp->stats.collisions += le32_to_cpu(sp->lstats->tx_total_colls);
- sp->stats.rx_crc_errors += le32_to_cpu(sp->lstats->rx_crc_errs);
- sp->stats.rx_frame_errors += le32_to_cpu(sp->lstats->rx_align_errs);
- sp->stats.rx_over_errors += le32_to_cpu(sp->lstats->rx_resource_errs);
- sp->stats.rx_fifo_errors += le32_to_cpu(sp->lstats->rx_overrun_errs);
- sp->stats.rx_length_errors += le32_to_cpu(sp->lstats->rx_runt_errs);
- sp->lstats->done_marker = 0x0000;
- if (netif_running(dev)) {
- unsigned long flags;
- /* Take a spinlock to make wait_for_cmd_done and sending the
- command atomic. --SAW */
- spin_lock_irqsave(&sp->lock, flags);
- wait_for_cmd_done(dev, sp);
- iowrite8(CUDumpStats, ioaddr + SCBCmd);
- spin_unlock_irqrestore(&sp->lock, flags);
- }
- }
- return &sp->stats;
-}
-
-static void speedo_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct speedo_private *sp = netdev_priv(dev);
- strncpy(info->driver, "eepro100", sizeof(info->driver)-1);
- strncpy(info->version, version, sizeof(info->version)-1);
- if (sp->pdev)
- strcpy(info->bus_info, pci_name(sp->pdev));
-}
-
-static int speedo_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
- struct speedo_private *sp = netdev_priv(dev);
- spin_lock_irq(&sp->lock);
- mii_ethtool_gset(&sp->mii_if, ecmd);
- spin_unlock_irq(&sp->lock);
- return 0;
-}
-
-static int speedo_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
-{
- struct speedo_private *sp = netdev_priv(dev);
- int res;
- spin_lock_irq(&sp->lock);
- res = mii_ethtool_sset(&sp->mii_if, ecmd);
- spin_unlock_irq(&sp->lock);
- return res;
-}
-
-static int speedo_nway_reset(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- return mii_nway_restart(&sp->mii_if);
-}
-
-static u32 speedo_get_link(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- return mii_link_ok(&sp->mii_if);
-}
-
-static u32 speedo_get_msglevel(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- return sp->msg_enable;
-}
-
-static void speedo_set_msglevel(struct net_device *dev, u32 v)
-{
- struct speedo_private *sp = netdev_priv(dev);
- sp->msg_enable = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .get_drvinfo = speedo_get_drvinfo,
- .get_settings = speedo_get_settings,
- .set_settings = speedo_set_settings,
- .nway_reset = speedo_nway_reset,
- .get_link = speedo_get_link,
- .get_msglevel = speedo_get_msglevel,
- .set_msglevel = speedo_set_msglevel,
-};
-
-static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct speedo_private *sp = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(rq);
- int phy = sp->phy[0] & 0x1f;
- int saved_acpi;
- int t;
-
- switch(cmd) {
- case SIOCGMIIPHY: /* Get address of MII PHY in use. */
- data->phy_id = phy;
-
- case SIOCGMIIREG: /* Read MII PHY register. */
- /* FIXME: these operations need to be serialized with MDIO
- access from the timeout handler.
- They are currently serialized only with MDIO access from the
- timer routine. 2000/05/09 SAW */
- saved_acpi = pci_set_power_state(sp->pdev, PCI_D0);
- t = del_timer_sync(&sp->timer);
- data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
- if (t)
- add_timer(&sp->timer); /* may be set to the past --SAW */
- pci_set_power_state(sp->pdev, saved_acpi);
- return 0;
-
- case SIOCSMIIREG: /* Write MII PHY register. */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- saved_acpi = pci_set_power_state(sp->pdev, PCI_D0);
- t = del_timer_sync(&sp->timer);
- mdio_write(dev, data->phy_id, data->reg_num, data->val_in);
- if (t)
- add_timer(&sp->timer); /* may be set to the past --SAW */
- pci_set_power_state(sp->pdev, saved_acpi);
- return 0;
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/* Set or clear the multicast filter for this adaptor.
- This is very ugly with Intel chips -- we usually have to execute an
- entire configuration command, plus process a multicast command.
- This is complicated. We must put a large configuration command and
- an arbitrarily-sized multicast command in the transmit list.
- To minimize the disruption -- the previous command might have already
- loaded the link -- we convert the current command block, normally a Tx
- command, into a no-op and link it to the new command.
-*/
-static void set_rx_mode(struct net_device *dev)
-{
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- struct descriptor *last_cmd;
- char new_rx_mode;
- unsigned long flags;
- int entry, i;
-
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- new_rx_mode = 3;
- } else if ((dev->flags & IFF_ALLMULTI) ||
- dev->mc_count > multicast_filter_limit) {
- new_rx_mode = 1;
- } else
- new_rx_mode = 0;
-
- if (netif_msg_rx_status(sp))
- printk(KERN_DEBUG "%s: set_rx_mode %d -> %d\n", dev->name,
- sp->rx_mode, new_rx_mode);
-
- if ((int)(sp->cur_tx - sp->dirty_tx) > TX_RING_SIZE - TX_MULTICAST_SIZE) {
- /* The Tx ring is full -- don't add anything! Hope the mode will be
- * set again later. */
- sp->rx_mode = -1;
- return;
- }
-
- if (new_rx_mode != sp->rx_mode) {
- u8 *config_cmd_data;
-
- spin_lock_irqsave(&sp->lock, flags);
- entry = sp->cur_tx++ % TX_RING_SIZE;
- last_cmd = sp->last_cmd;
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
- sp->tx_skbuff[entry] = NULL; /* Redundant. */
- sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure);
- sp->tx_ring[entry].link =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
- config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
- /* Construct a full CmdConfig frame. */
- memcpy(config_cmd_data, i82558_config_cmd, CONFIG_DATA_SIZE);
- config_cmd_data[1] = (txfifo << 4) | rxfifo;
- config_cmd_data[4] = rxdmacount;
- config_cmd_data[5] = txdmacount + 0x80;
- config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
- /* 0x80 doesn't disable FC 0x84 does.
- Disable Flow control since we are not ACK-ing any FC interrupts
- for now. --Dragan */
- config_cmd_data[19] = 0x84;
- config_cmd_data[19] |= sp->mii_if.full_duplex ? 0x40 : 0;
- config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
- if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */
- config_cmd_data[15] |= 0x80;
- config_cmd_data[8] = 0;
- }
- /* Trigger the command unit resume. */
- wait_for_cmd_done(dev, sp);
- clear_suspend(last_cmd);
- iowrite8(CUResume, ioaddr + SCBCmd);
- if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
- netif_stop_queue(dev);
- sp->tx_full = 1;
- }
- spin_unlock_irqrestore(&sp->lock, flags);
- }
-
- if (new_rx_mode == 0 && dev->mc_count < 4) {
- /* The simple case of 0-3 multicast list entries occurs often, and
- fits within one tx_ring[] entry. */
- struct dev_mc_list *mclist;
- __le16 *setup_params, *eaddrs;
-
- spin_lock_irqsave(&sp->lock, flags);
- entry = sp->cur_tx++ % TX_RING_SIZE;
- last_cmd = sp->last_cmd;
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
-
- sp->tx_skbuff[entry] = NULL;
- sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList);
- sp->tx_ring[entry].link =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
- sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
- setup_params = (__le16 *)&sp->tx_ring[entry].tx_desc_addr;
- *setup_params++ = cpu_to_le16(dev->mc_count*6);
- /* Fill in the multicast addresses. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (__le16 *)mclist->dmi_addr;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- }
-
- wait_for_cmd_done(dev, sp);
- clear_suspend(last_cmd);
- /* Immediately trigger the command unit resume. */
- iowrite8(CUResume, ioaddr + SCBCmd);
-
- if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
- netif_stop_queue(dev);
- sp->tx_full = 1;
- }
- spin_unlock_irqrestore(&sp->lock, flags);
- } else if (new_rx_mode == 0) {
- struct dev_mc_list *mclist;
- __le16 *setup_params, *eaddrs;
- struct speedo_mc_block *mc_blk;
- struct descriptor *mc_setup_frm;
- int i;
-
- mc_blk = kmalloc(sizeof(*mc_blk) + 2 + multicast_filter_limit*6,
- GFP_ATOMIC);
- if (mc_blk == NULL) {
- printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
- dev->name);
- sp->rx_mode = -1; /* We failed, try again. */
- return;
- }
- mc_blk->next = NULL;
- mc_blk->len = 2 + multicast_filter_limit*6;
- mc_blk->frame_dma =
- pci_map_single(sp->pdev, &mc_blk->frame, mc_blk->len,
- PCI_DMA_TODEVICE);
- mc_setup_frm = &mc_blk->frame;
-
- /* Fill the setup frame. */
- if (netif_msg_ifup(sp))
- printk(KERN_DEBUG "%s: Constructing a setup frame at %p.\n",
- dev->name, mc_setup_frm);
- mc_setup_frm->cmd_status =
- cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList);
- /* Link set below. */
- setup_params = (__le16 *)&mc_setup_frm->params;
- *setup_params++ = cpu_to_le16(dev->mc_count*6);
- /* Fill in the multicast addresses. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (__le16 *)mclist->dmi_addr;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- }
-
- /* Disable interrupts while playing with the Tx Cmd list. */
- spin_lock_irqsave(&sp->lock, flags);
-
- if (sp->mc_setup_tail)
- sp->mc_setup_tail->next = mc_blk;
- else
- sp->mc_setup_head = mc_blk;
- sp->mc_setup_tail = mc_blk;
- mc_blk->tx = sp->cur_tx;
-
- entry = sp->cur_tx++ % TX_RING_SIZE;
- last_cmd = sp->last_cmd;
- sp->last_cmd = mc_setup_frm;
-
- /* Change the command to a NoOp, pointing to the CmdMulti command. */
- sp->tx_skbuff[entry] = NULL;
- sp->tx_ring[entry].status = cpu_to_le32(CmdNOp);
- sp->tx_ring[entry].link = cpu_to_le32(mc_blk->frame_dma);
-
- /* Set the link in the setup frame. */
- mc_setup_frm->link =
- cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
-
- pci_dma_sync_single_for_device(sp->pdev, mc_blk->frame_dma,
- mc_blk->len, PCI_DMA_TODEVICE);
-
- wait_for_cmd_done(dev, sp);
- clear_suspend(last_cmd);
- /* Immediately trigger the command unit resume. */
- iowrite8(CUResume, ioaddr + SCBCmd);
-
- if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
- netif_stop_queue(dev);
- sp->tx_full = 1;
- }
- spin_unlock_irqrestore(&sp->lock, flags);
-
- if (netif_msg_rx_status(sp))
- printk(" CmdMCSetup frame length %d in entry %d.\n",
- dev->mc_count, entry);
- }
-
- sp->rx_mode = new_rx_mode;
-}
-
-#ifdef CONFIG_PM
-static int eepro100_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
-
- pci_save_state(pdev);
-
- if (!netif_running(dev))
- return 0;
-
- del_timer_sync(&sp->timer);
-
- netif_device_detach(dev);
- iowrite32(PortPartialReset, ioaddr + SCBPort);
-
- /* XXX call pci_set_power_state ()? */
- pci_disable_device(pdev);
- pci_set_power_state (pdev, PCI_D3hot);
- return 0;
-}
-
-static int eepro100_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
- struct speedo_private *sp = netdev_priv(dev);
- void __iomem *ioaddr = sp->regs;
- int rc;
-
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
-
- rc = pci_enable_device(pdev);
- if (rc)
- return rc;
-
- pci_set_master(pdev);
-
- if (!netif_running(dev))
- return 0;
-
- /* I'm absolutely uncertain if this part of code may work.
- The problems are:
- - correct hardware reinitialization;
- - correct driver behavior between different steps of the
- reinitialization;
- - serialization with other driver calls.
- 2000/03/08 SAW */
- iowrite16(SCBMaskAll, ioaddr + SCBCmd);
- speedo_resume(dev);
- netif_device_attach(dev);
- sp->rx_mode = -1;
- sp->flow_ctrl = sp->partner = 0;
- set_rx_mode(dev);
- sp->timer.expires = RUN_AT(2*HZ);
- add_timer(&sp->timer);
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static void __devexit eepro100_remove_one (struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata (pdev);
- struct speedo_private *sp = netdev_priv(dev);
-
- unregister_netdev(dev);
-
- release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
- release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-
- pci_iounmap(pdev, sp->regs);
- pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
- + sizeof(struct speedo_stats),
- sp->tx_ring, sp->tx_ring_dma);
- pci_disable_device(pdev);
- free_netdev(dev);
-}
-
-static struct pci_device_id eepro100_pci_tbl[] = {
- { PCI_VENDOR_ID_INTEL, 0x1229, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1209, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1029, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1030, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1031, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1032, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1033, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1034, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1035, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1036, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1037, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1038, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1039, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x103A, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x103B, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x103C, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x103D, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x103E, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1050, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1059, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x1227, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x2449, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x2459, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x245D, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x5200, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, 0x5201, PCI_ANY_ID, PCI_ANY_ID, },
- { 0,}
-};
-MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
-
-static struct pci_driver eepro100_driver = {
- .name = "eepro100",
- .id_table = eepro100_pci_tbl,
- .probe = eepro100_init_one,
- .remove = __devexit_p(eepro100_remove_one),
-#ifdef CONFIG_PM
- .suspend = eepro100_suspend,
- .resume = eepro100_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init eepro100_init_module(void)
-{
-#ifdef MODULE
- printk(version);
-#endif
- return pci_register_driver(&eepro100_driver);
-}
-
-static void __exit eepro100_cleanup_module(void)
-{
- pci_unregister_driver(&eepro100_driver);
-}
-
-module_init(eepro100_init_module);
-module_exit(eepro100_cleanup_module);
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index b751c1b96cfa..9ff3f2f5e382 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -967,7 +967,6 @@ static void eexp_hw_rx_pio(struct net_device *dev)
insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1);
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -1047,7 +1046,7 @@ static void eexp_hw_tx_pio(struct net_device *dev, unsigned short *buf,
/*
* Sanity check the suspected EtherExpress card
* Read hardware address, reset card, size memory and initialize buffer
- * memory pointers. These are held in dev->priv, in case someone has more
+ * memory pointers. These are held in netdev_priv(), in case someone has more
* than one card in a machine.
*/
diff --git a/drivers/net/eexpress.h b/drivers/net/eexpress.h
index 707df3fcfe40..dc9c6ea289e9 100644
--- a/drivers/net/eexpress.h
+++ b/drivers/net/eexpress.h
@@ -68,17 +68,17 @@
*/
/* these functions take the SCB status word and test the relevant status bit */
-#define SCB_complete(s) ((s&0x8000)!=0)
-#define SCB_rxdframe(s) ((s&0x4000)!=0)
-#define SCB_CUdead(s) ((s&0x2000)!=0)
-#define SCB_RUdead(s) ((s&0x1000)!=0)
-#define SCB_ack(s) (s & 0xf000)
+#define SCB_complete(s) (((s) & 0x8000) != 0)
+#define SCB_rxdframe(s) (((s) & 0x4000) != 0)
+#define SCB_CUdead(s) (((s) & 0x2000) != 0)
+#define SCB_RUdead(s) (((s) & 0x1000) != 0)
+#define SCB_ack(s) ((s) & 0xf000)
/* Command unit status: 0=idle, 1=suspended, 2=active */
-#define SCB_CUstat(s) ((s&0x0300)>>8)
+#define SCB_CUstat(s) (((s)&0x0300)>>8)
/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */
-#define SCB_RUstat(s) ((s&0x0070)>>4)
+#define SCB_RUstat(s) (((s)&0x0070)>>4)
/* SCB commands */
#define SCB_CUnop 0x0000
@@ -98,18 +98,18 @@
* Command block defines
*/
-#define Stat_Done(s) ((s&0x8000)!=0)
-#define Stat_Busy(s) ((s&0x4000)!=0)
-#define Stat_OK(s) ((s&0x2000)!=0)
-#define Stat_Abort(s) ((s&0x1000)!=0)
-#define Stat_STFail ((s&0x0800)!=0)
-#define Stat_TNoCar(s) ((s&0x0400)!=0)
-#define Stat_TNoCTS(s) ((s&0x0200)!=0)
-#define Stat_TNoDMA(s) ((s&0x0100)!=0)
-#define Stat_TDefer(s) ((s&0x0080)!=0)
-#define Stat_TColl(s) ((s&0x0040)!=0)
-#define Stat_TXColl(s) ((s&0x0020)!=0)
-#define Stat_NoColl(s) (s&0x000f)
+#define Stat_Done(s) (((s) & 0x8000) != 0)
+#define Stat_Busy(s) (((s) & 0x4000) != 0)
+#define Stat_OK(s) (((s) & 0x2000) != 0)
+#define Stat_Abort(s) (((s) & 0x1000) != 0)
+#define Stat_STFail (((s) & 0x0800) != 0)
+#define Stat_TNoCar(s) (((s) & 0x0400) != 0)
+#define Stat_TNoCTS(s) (((s) & 0x0200) != 0)
+#define Stat_TNoDMA(s) (((s) & 0x0100) != 0)
+#define Stat_TDefer(s) (((s) & 0x0080) != 0)
+#define Stat_TColl(s) (((s) & 0x0040) != 0)
+#define Stat_TXColl(s) (((s) & 0x0020) != 0)
+#define Stat_NoColl(s) ((s) & 0x000f)
/* Cmd_END will end AFTER the command if this is the first
* command block after an SCB_CUstart, but BEFORE the command
@@ -136,16 +136,16 @@
* Frame Descriptor (Receive block) defines
*/
-#define FD_Done(s) ((s&0x8000)!=0)
-#define FD_Busy(s) ((s&0x4000)!=0)
-#define FD_OK(s) ((s&0x2000)!=0)
+#define FD_Done(s) (((s) & 0x8000) != 0)
+#define FD_Busy(s) (((s) & 0x4000) != 0)
+#define FD_OK(s) (((s) & 0x2000) != 0)
-#define FD_CRC(s) ((s&0x0800)!=0)
-#define FD_Align(s) ((s&0x0400)!=0)
-#define FD_Resrc(s) ((s&0x0200)!=0)
-#define FD_DMA(s) ((s&0x0100)!=0)
-#define FD_Short(s) ((s&0x0080)!=0)
-#define FD_NoEOF(s) ((s&0x0040)!=0)
+#define FD_CRC(s) (((s) & 0x0800) != 0)
+#define FD_Align(s) (((s) & 0x0400) != 0)
+#define FD_Resrc(s) (((s) & 0x0200) != 0)
+#define FD_DMA(s) (((s) & 0x0100) != 0)
+#define FD_Short(s) (((s) & 0x0080) != 0)
+#define FD_NoEOF(s) (((s) & 0x0040) != 0)
struct rfd_header {
volatile unsigned long flags;
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index 002d918fb4c7..9930d5f8b9e1 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -40,7 +40,7 @@
#include <asm/io.h>
#define DRV_NAME "ehea"
-#define DRV_VERSION "EHEA_0095"
+#define DRV_VERSION "EHEA_0096"
/* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 422fcb93e2c3..a2f1905a23df 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -728,7 +728,6 @@ static int ehea_proc_rwqes(struct net_device *dev,
}
ehea_proc_skb(pr, cqe, skb);
- dev->last_rx = jiffies;
} else {
pr->p_stats.poll_receive_errors++;
port_reset = ehea_treat_poll_error(pr, rq, cqe,
@@ -831,7 +830,7 @@ static int ehea_poll(struct napi_struct *napi, int budget)
while ((rx != budget) || force_irq) {
pr->poll_counter = 0;
force_irq = 0;
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
ehea_reset_cq_ep(pr->recv_cq);
ehea_reset_cq_ep(pr->send_cq);
ehea_reset_cq_n1(pr->recv_cq);
@@ -842,7 +841,7 @@ static int ehea_poll(struct napi_struct *napi, int budget)
if (!cqe && !cqe_skb)
return rx;
- if (!netif_rx_reschedule(dev, napi))
+ if (!netif_rx_reschedule(napi))
return rx;
cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
@@ -860,7 +859,7 @@ static void ehea_netpoll(struct net_device *dev)
int i;
for (i = 0; i < port->num_def_qps; i++)
- netif_rx_schedule(dev, &port->port_res[i].napi);
+ netif_rx_schedule(&port->port_res[i].napi);
}
#endif
@@ -868,7 +867,7 @@ static irqreturn_t ehea_recv_irq_handler(int irq, void *param)
{
struct ehea_port_res *pr = param;
- netif_rx_schedule(pr->port->netdev, &pr->napi);
+ netif_rx_schedule(&pr->napi);
return IRQ_HANDLED;
}
diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c
index 9d006878f045..225c692b5d99 100644
--- a/drivers/net/ehea/ehea_qmr.c
+++ b/drivers/net/ehea/ehea_qmr.c
@@ -182,7 +182,7 @@ struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter,
goto out_kill_hwq;
}
} else {
- if ((hret != H_PAGE_REGISTERED) || (!vpage)) {
+ if (hret != H_PAGE_REGISTERED) {
ehea_error("CQ: registration of page failed "
"hret=%lx\n", hret);
goto out_kill_hwq;
@@ -303,7 +303,7 @@ struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
goto out_kill_hwq;
} else {
- if ((hret != H_PAGE_REGISTERED) || (!vpage))
+ if (hret != H_PAGE_REGISTERED)
goto out_kill_hwq;
}
@@ -653,7 +653,7 @@ static int ehea_update_busmap(unsigned long pfn, unsigned long nr_pages, int add
int top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
int dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
int idx = i & EHEA_INDEX_MASK;
-
+
if (add) {
int ret = ehea_init_bmap(ehea_bmap, top, dir);
if (ret)
@@ -780,7 +780,7 @@ void ehea_destroy_busmap(void)
kfree(ehea_bmap);
ehea_bmap = NULL;
-out_destroy:
+out_destroy:
mutex_unlock(&ehea_busmap_mutex);
}
@@ -858,10 +858,10 @@ static u64 ehea_reg_mr_sections(int top, int dir, u64 *pt,
for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) {
if (!ehea_bmap->top[top]->dir[dir]->ent[idx])
continue;
-
+
hret = ehea_reg_mr_section(top, dir, idx, pt, adapter, mr);
if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
- return hret;
+ return hret;
}
return hret;
}
@@ -879,7 +879,7 @@ static u64 ehea_reg_mr_dir_sections(int top, u64 *pt,
hret = ehea_reg_mr_sections(top, dir, pt, adapter, mr);
if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
- return hret;
+ return hret;
}
return hret;
}
@@ -893,7 +893,7 @@ int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
unsigned long top;
- pt = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ pt = (void *)get_zeroed_page(GFP_KERNEL);
if (!pt) {
ehea_error("no mem");
ret = -ENOMEM;
@@ -937,7 +937,7 @@ int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
mr->adapter = adapter;
ret = 0;
out:
- kfree(pt);
+ free_page((unsigned long)pt);
return ret;
}
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index c414554ac321..b0ef46c51a9d 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -196,16 +196,32 @@ static void enc28j60_soft_reset(struct enc28j60_net *priv)
*/
static void enc28j60_set_bank(struct enc28j60_net *priv, u8 addr)
{
- if ((addr & BANK_MASK) != priv->bank) {
- u8 b = (addr & BANK_MASK) >> 5;
+ u8 b = (addr & BANK_MASK) >> 5;
- if (b != (ECON1_BSEL1 | ECON1_BSEL0))
+ /* These registers (EIE, EIR, ESTAT, ECON2, ECON1)
+ * are present in all banks, no need to switch bank
+ */
+ if (addr >= EIE && addr <= ECON1)
+ return;
+
+ /* Clear or set each bank selection bit as needed */
+ if ((b & ECON1_BSEL0) != (priv->bank & ECON1_BSEL0)) {
+ if (b & ECON1_BSEL0)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL0);
+ else
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
- ECON1_BSEL1 | ECON1_BSEL0);
- if (b != 0)
- spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, b);
- priv->bank = (addr & BANK_MASK);
+ ECON1_BSEL0);
}
+ if ((b & ECON1_BSEL1) != (priv->bank & ECON1_BSEL1)) {
+ if (b & ECON1_BSEL1)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL1);
+ else
+ spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
+ ECON1_BSEL1);
+ }
+ priv->bank = b;
}
/*
@@ -477,12 +493,10 @@ static int enc28j60_set_hw_macaddr(struct net_device *ndev)
mutex_lock(&priv->lock);
if (!priv->hw_enable) {
- if (netif_msg_drv(priv)) {
- DECLARE_MAC_BUF(mac);
+ if (netif_msg_drv(priv))
printk(KERN_INFO DRV_NAME
- ": %s: Setting MAC address to %s\n",
- ndev->name, print_mac(mac, ndev->dev_addr));
- }
+ ": %s: Setting MAC address to %pM\n",
+ ndev->name, ndev->dev_addr);
/* NOTE: MAC address in ENC28J60 is byte-backward */
nolock_regb_write(priv, MAADR5, ndev->dev_addr[0]);
nolock_regb_write(priv, MAADR4, ndev->dev_addr[1]);
@@ -958,8 +972,7 @@ static void enc28j60_hw_rx(struct net_device *ndev)
/* update statistics */
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
- ndev->last_rx = jiffies;
- netif_rx(skb);
+ netif_rx_ni(skb);
}
}
/*
@@ -1340,11 +1353,9 @@ static int enc28j60_net_open(struct net_device *dev)
printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
if (!is_valid_ether_addr(dev->dev_addr)) {
- if (netif_msg_ifup(priv)) {
- DECLARE_MAC_BUF(mac);
- dev_err(&dev->dev, "invalid MAC address %s\n",
- print_mac(mac, dev->dev_addr));
- }
+ if (netif_msg_ifup(priv))
+ dev_err(&dev->dev, "invalid MAC address %pM\n",
+ dev->dev_addr);
return -EADDRNOTAVAIL;
}
/* Reset the hardware here (and take it out of low power mode) */
@@ -1465,7 +1476,7 @@ enc28j60_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info,
- dev->dev.parent->bus_id, sizeof(info->bus_info));
+ dev_name(dev->dev.parent), sizeof(info->bus_info));
}
static int
diff --git a/drivers/net/enic/cq_desc.h b/drivers/net/enic/cq_desc.h
index c036a8bfd043..1eb289f773bf 100644
--- a/drivers/net/enic/cq_desc.h
+++ b/drivers/net/enic/cq_desc.h
@@ -44,9 +44,10 @@ struct cq_desc {
u8 type_color;
};
-#define CQ_DESC_TYPE_BITS 7
+#define CQ_DESC_TYPE_BITS 4
#define CQ_DESC_TYPE_MASK ((1 << CQ_DESC_TYPE_BITS) - 1)
#define CQ_DESC_COLOR_MASK 1
+#define CQ_DESC_COLOR_SHIFT 7
#define CQ_DESC_Q_NUM_BITS 10
#define CQ_DESC_Q_NUM_MASK ((1 << CQ_DESC_Q_NUM_BITS) - 1)
#define CQ_DESC_COMP_NDX_BITS 12
@@ -58,7 +59,7 @@ static inline void cq_desc_dec(const struct cq_desc *desc_arg,
const struct cq_desc *desc = desc_arg;
const u8 type_color = desc->type_color;
- *color = (type_color >> CQ_DESC_TYPE_BITS) & CQ_DESC_COLOR_MASK;
+ *color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
/*
* Make sure color bit is read from desc *before* other fields
diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h
index 7f677e89a788..a832cc5d6a1e 100644
--- a/drivers/net/enic/enic.h
+++ b/drivers/net/enic/enic.h
@@ -33,7 +33,7 @@
#define DRV_NAME "enic"
#define DRV_DESCRIPTION "Cisco 10G Ethernet Driver"
-#define DRV_VERSION "0.0.1-18163.472-k1"
+#define DRV_VERSION "1.0.0.648"
#define DRV_COPYRIGHT "Copyright 2008 Cisco Systems, Inc"
#define PFX DRV_NAME ": "
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index 180e968dc54d..d039e16f2763 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -273,6 +273,8 @@ static struct ethtool_ops enic_ethtool_ops = {
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = enic_set_tso,
+ .get_flags = ethtool_op_get_flags,
+ .set_flags = ethtool_op_set_flags,
};
static void enic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
@@ -409,8 +411,8 @@ static irqreturn_t enic_isr_legacy(int irq, void *data)
}
if (ENIC_TEST_INTR(pba, ENIC_INTX_WQ_RQ)) {
- if (netif_rx_schedule_prep(netdev, &enic->napi))
- __netif_rx_schedule(netdev, &enic->napi);
+ if (netif_rx_schedule_prep(&enic->napi))
+ __netif_rx_schedule(&enic->napi);
} else {
vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]);
}
@@ -438,7 +440,7 @@ static irqreturn_t enic_isr_msi(int irq, void *data)
* writes).
*/
- netif_rx_schedule(enic->netdev, &enic->napi);
+ netif_rx_schedule(&enic->napi);
return IRQ_HANDLED;
}
@@ -448,7 +450,7 @@ static irqreturn_t enic_isr_msix_rq(int irq, void *data)
struct enic *enic = data;
/* schedule NAPI polling for RQ cleanup */
- netif_rx_schedule(enic->netdev, &enic->napi);
+ netif_rx_schedule(&enic->napi);
return IRQ_HANDLED;
}
@@ -895,6 +897,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
int skipped, void *opaque)
{
struct enic *enic = vnic_dev_priv(rq->vdev);
+ struct net_device *netdev = enic->netdev;
struct sk_buff *skb;
u8 type, color, eop, sop, ingress_port, vlan_stripped;
@@ -929,7 +932,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
if (net_ratelimit())
printk(KERN_ERR PFX
"%s: packet error: bad FCS\n",
- enic->netdev->name);
+ netdev->name);
}
dev_kfree_skb_any(skb);
@@ -943,19 +946,18 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
*/
skb_put(skb, bytes_written);
- skb->protocol = eth_type_trans(skb, enic->netdev);
+ skb->protocol = eth_type_trans(skb, netdev);
if (enic->csum_rx_enabled && !csum_not_calc) {
skb->csum = htons(checksum);
skb->ip_summed = CHECKSUM_COMPLETE;
}
- skb->dev = enic->netdev;
- enic->netdev->last_rx = jiffies;
+ skb->dev = netdev;
if (enic->vlan_group && vlan_stripped) {
- if (ENIC_SETTING(enic, LRO) && ipv4)
+ if ((netdev->features & NETIF_F_LRO) && ipv4)
lro_vlan_hwaccel_receive_skb(&enic->lro_mgr,
skb, enic->vlan_group,
vlan, cq_desc);
@@ -965,7 +967,7 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
} else {
- if (ENIC_SETTING(enic, LRO) && ipv4)
+ if ((netdev->features & NETIF_F_LRO) && ipv4)
lro_receive_skb(&enic->lro_mgr, skb, cq_desc);
else
netif_receive_skb(skb);
@@ -1063,10 +1065,10 @@ static int enic_poll(struct napi_struct *napi, int budget)
/* If no work done, flush all LROs and exit polling
*/
- if (ENIC_SETTING(enic, LRO))
+ if (netdev->features & NETIF_F_LRO)
lro_flush_all(&enic->lro_mgr);
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]);
}
@@ -1107,10 +1109,10 @@ static int enic_poll_msix(struct napi_struct *napi, int budget)
/* If no work done, flush all LROs and exit polling
*/
- if (ENIC_SETTING(enic, LRO))
+ if (netdev->features & NETIF_F_LRO)
lro_flush_all(&enic->lro_mgr);
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]);
}
@@ -1591,6 +1593,23 @@ static void enic_iounmap(struct enic *enic)
iounmap(enic->bar0.vaddr);
}
+static const struct net_device_ops enic_netdev_ops = {
+ .ndo_open = enic_open,
+ .ndo_stop = enic_stop,
+ .ndo_start_xmit = enic_hard_start_xmit,
+ .ndo_get_stats = enic_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = enic_set_multicast_list,
+ .ndo_change_mtu = enic_change_mtu,
+ .ndo_vlan_rx_register = enic_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = enic_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = enic_vlan_rx_kill_vid,
+ .ndo_tx_timeout = enic_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = enic_poll_controller,
+#endif
+};
+
static int __devinit enic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -1746,13 +1765,13 @@ static int __devinit enic_probe(struct pci_dev *pdev,
}
/* Get available resource counts
- */
+ */
enic_get_res_counts(enic);
/* Set interrupt mode based on resource counts and system
* capabilities
- */
+ */
err = enic_set_intr_mode(enic);
if (err) {
@@ -1814,21 +1833,9 @@ static int __devinit enic_probe(struct pci_dev *pdev,
goto err_out_free_vnic_resources;
}
- netdev->open = enic_open;
- netdev->stop = enic_stop;
- netdev->hard_start_xmit = enic_hard_start_xmit;
- netdev->get_stats = enic_get_stats;
- netdev->set_multicast_list = enic_set_multicast_list;
- netdev->change_mtu = enic_change_mtu;
- netdev->vlan_rx_register = enic_vlan_rx_register;
- netdev->vlan_rx_add_vid = enic_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = enic_vlan_rx_kill_vid;
- netdev->tx_timeout = enic_tx_timeout;
+ netdev->netdev_ops = &enic_netdev_ops;
netdev->watchdog_timeo = 2 * HZ;
netdev->ethtool_ops = &enic_ethtool_ops;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = enic_poll_controller;
-#endif
switch (vnic_dev_get_intr_mode(enic->vdev)) {
default:
@@ -1845,22 +1852,23 @@ static int __devinit enic_probe(struct pci_dev *pdev,
if (ENIC_SETTING(enic, TSO))
netdev->features |= NETIF_F_TSO |
NETIF_F_TSO6 | NETIF_F_TSO_ECN;
+ if (ENIC_SETTING(enic, LRO))
+ netdev->features |= NETIF_F_LRO;
if (using_dac)
netdev->features |= NETIF_F_HIGHDMA;
enic->csum_rx_enabled = ENIC_SETTING(enic, RXCSUM);
- if (ENIC_SETTING(enic, LRO)) {
- enic->lro_mgr.max_aggr = ENIC_LRO_MAX_AGGR;
- enic->lro_mgr.max_desc = ENIC_LRO_MAX_DESC;
- enic->lro_mgr.lro_arr = enic->lro_desc;
- enic->lro_mgr.get_skb_header = enic_get_skb_header;
- enic->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
- enic->lro_mgr.dev = netdev;
- enic->lro_mgr.ip_summed = CHECKSUM_COMPLETE;
- enic->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
- }
+ enic->lro_mgr.max_aggr = ENIC_LRO_MAX_AGGR;
+ enic->lro_mgr.max_desc = ENIC_LRO_MAX_DESC;
+ enic->lro_mgr.lro_arr = enic->lro_desc;
+ enic->lro_mgr.get_skb_header = enic_get_skb_header;
+ enic->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
+ enic->lro_mgr.dev = netdev;
+ enic->lro_mgr.ip_summed = CHECKSUM_COMPLETE;
+ enic->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
+
err = register_netdev(netdev);
if (err) {
diff --git a/drivers/net/enic/enic_res.c b/drivers/net/enic/enic_res.c
index 95184b9108ef..e5fc9384f8f5 100644
--- a/drivers/net/enic/enic_res.c
+++ b/drivers/net/enic/enic_res.c
@@ -90,11 +90,8 @@ int enic_get_vnic_config(struct enic *enic)
c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer);
- printk(KERN_INFO PFX "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
- "wq/rq %d/%d\n",
- enic->mac_addr[0], enic->mac_addr[1], enic->mac_addr[2],
- enic->mac_addr[3], enic->mac_addr[4], enic->mac_addr[5],
- c->wq_desc_count, c->rq_desc_count);
+ printk(KERN_INFO PFX "vNIC MAC addr %pM wq/rq %d/%d\n",
+ enic->mac_addr, c->wq_desc_count, c->rq_desc_count);
printk(KERN_INFO PFX "vNIC mtu %d csum tx/rx %d/%d tso/lro %d/%d "
"intr timer %d\n",
c->mtu, ENIC_SETTING(enic, TXCSUM),
diff --git a/drivers/net/enic/enic_res.h b/drivers/net/enic/enic_res.h
index 68534a29b7ac..7bf272fa859b 100644
--- a/drivers/net/enic/enic_res.h
+++ b/drivers/net/enic/enic_res.h
@@ -58,8 +58,6 @@ static inline void enic_queue_wq_desc_ex(struct vnic_wq *wq,
(u16)vlan_tag,
0 /* loopback */);
- wmb();
-
vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop);
}
@@ -127,8 +125,6 @@ static inline void enic_queue_rq_desc(struct vnic_rq *rq,
(u64)dma_addr | VNIC_PADDR_TARGET,
type, (u16)len);
- wmb();
-
vnic_rq_post(rq, os_buf, os_buf_index, dma_addr, len);
}
diff --git a/drivers/net/enic/vnic_dev.c b/drivers/net/enic/vnic_dev.c
index 4d104f5c30f9..11708579b6ce 100644
--- a/drivers/net/enic/vnic_dev.c
+++ b/drivers/net/enic/vnic_dev.c
@@ -43,6 +43,7 @@ struct vnic_dev {
struct vnic_devcmd_notify *notify;
struct vnic_devcmd_notify notify_copy;
dma_addr_t notify_pa;
+ u32 notify_sz;
u32 *linkstatus;
dma_addr_t linkstatus_pa;
struct vnic_stats *stats;
@@ -235,14 +236,6 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
struct vnic_devcmd __iomem *devcmd = vdev->devcmd;
int delay;
u32 status;
- int dev_cmd_err[] = {
- /* convert from fw's version of error.h to host's version */
- 0, /* ERR_SUCCESS */
- EINVAL, /* ERR_EINVAL */
- EFAULT, /* ERR_EFAULT */
- EPERM, /* ERR_EPERM */
- EBUSY, /* ERR_EBUSY */
- };
int err;
status = ioread32(&devcmd->status);
@@ -270,10 +263,12 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
if (!(status & STAT_BUSY)) {
if (status & STAT_ERROR) {
- err = dev_cmd_err[(int)readq(&devcmd->args[0])];
- printk(KERN_ERR "Error %d devcmd %d\n",
- err, _CMD_N(cmd));
- return -err;
+ err = (int)readq(&devcmd->args[0]);
+ if (err != ERR_ECMDUNKNOWN ||
+ cmd != CMD_CAPABILITY)
+ printk(KERN_ERR "Error %d devcmd %d\n",
+ err, _CMD_N(cmd));
+ return err;
}
if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
@@ -290,6 +285,17 @@ int vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
return -ETIMEDOUT;
}
+static int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
+{
+ u64 a0 = (u32)cmd, a1 = 0;
+ int wait = 1000;
+ int err;
+
+ err = vnic_dev_cmd(vdev, CMD_CAPABILITY, &a0, &a1, wait);
+
+ return !(err || a0);
+}
+
int vnic_dev_fw_info(struct vnic_dev *vdev,
struct vnic_devcmd_fw_info **fw_info)
{
@@ -489,10 +495,7 @@ void vnic_dev_add_addr(struct vnic_dev *vdev, u8 *addr)
err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
if (err)
- printk(KERN_ERR
- "Can't add addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
- err);
+ printk(KERN_ERR "Can't add addr [%pM], %d\n", addr, err);
}
void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
@@ -507,16 +510,14 @@ void vnic_dev_del_addr(struct vnic_dev *vdev, u8 *addr)
err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait);
if (err)
- printk(KERN_ERR
- "Can't del addr [%02x:%02x:%02x:%02x:%02x:%02x], %d\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
- err);
+ printk(KERN_ERR "Can't del addr [%pM], %d\n", addr, err);
}
int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
{
u64 a0, a1;
int wait = 1000;
+ int r;
if (!vdev->notify) {
vdev->notify = pci_alloc_consistent(vdev->pdev,
@@ -524,13 +525,16 @@ int vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
&vdev->notify_pa);
if (!vdev->notify)
return -ENOMEM;
+ memset(vdev->notify, 0, sizeof(struct vnic_devcmd_notify));
}
a0 = vdev->notify_pa;
a1 = ((u64)intr << 32) & 0x0000ffff00000000ULL;
a1 += sizeof(struct vnic_devcmd_notify);
- return vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+ r = vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+ vdev->notify_sz = (r == 0) ? (u32)a1 : 0;
+ return r;
}
void vnic_dev_notify_unset(struct vnic_dev *vdev)
@@ -543,22 +547,22 @@ void vnic_dev_notify_unset(struct vnic_dev *vdev)
a1 += sizeof(struct vnic_devcmd_notify);
vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+ vdev->notify_sz = 0;
}
static int vnic_dev_notify_ready(struct vnic_dev *vdev)
{
u32 *words;
- unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4;
+ unsigned int nwords = vdev->notify_sz / 4;
unsigned int i;
u32 csum;
- if (!vdev->notify)
+ if (!vdev->notify || !vdev->notify_sz)
return 0;
do {
csum = 0;
- memcpy(&vdev->notify_copy, vdev->notify,
- sizeof(struct vnic_devcmd_notify));
+ memcpy(&vdev->notify_copy, vdev->notify, vdev->notify_sz);
words = (u32 *)&vdev->notify_copy;
for (i = 1; i < nwords; i++)
csum += words[i];
@@ -571,7 +575,20 @@ int vnic_dev_init(struct vnic_dev *vdev, int arg)
{
u64 a0 = (u32)arg, a1 = 0;
int wait = 1000;
- return vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
+ int r = 0;
+
+ if (vnic_dev_capable(vdev, CMD_INIT))
+ r = vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
+ else {
+ vnic_dev_cmd(vdev, CMD_INIT_v1, &a0, &a1, wait);
+ if (a0 & CMD_INITF_DEFAULT_MAC) {
+ // Emulate these for old CMD_INIT_v1 which
+ // didn't pass a0 so no CMD_INITF_*.
+ vnic_dev_cmd(vdev, CMD_MAC_ADDR, &a0, &a1, wait);
+ vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
+ }
+ }
+ return r;
}
int vnic_dev_link_status(struct vnic_dev *vdev)
@@ -672,3 +689,4 @@ err_out:
return NULL;
}
+
diff --git a/drivers/net/enic/vnic_devcmd.h b/drivers/net/enic/vnic_devcmd.h
index d8617a3373b1..8062c75154e6 100644
--- a/drivers/net/enic/vnic_devcmd.h
+++ b/drivers/net/enic/vnic_devcmd.h
@@ -168,7 +168,8 @@ enum vnic_devcmd_cmd {
CMD_CLOSE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25),
/* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */
- CMD_INIT = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26),
+/***** Replaced by CMD_INIT *****/
+ CMD_INIT_v1 = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26),
/* variant of CMD_INIT, with provisioning info
* (u64)a0=paddr of vnic_devcmd_provinfo
@@ -198,6 +199,14 @@ enum vnic_devcmd_cmd {
/* undo initialize of virtual link */
CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34),
+
+ /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */
+ CMD_INIT = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 35),
+
+ /* check fw capability of a cmd:
+ * in: (u32)a0=cmd
+ * out: (u32)a0=errno, 0:valid cmd, a1=supported VNIC_STF_* bits */
+ CMD_CAPABILITY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 36),
};
/* flags for CMD_OPEN */
@@ -249,8 +258,16 @@ struct vnic_devcmd_notify {
u32 uif; /* uplink interface */
u32 status; /* status bits (see VNIC_STF_*) */
u32 error; /* error code (see ERR_*) for first ERR */
+ u32 link_down_cnt; /* running count of link down transitions */
};
#define VNIC_STF_FATAL_ERR 0x0001 /* fatal fw error */
+#define VNIC_STF_STD_PAUSE 0x0002 /* standard link-level pause on */
+#define VNIC_STF_PFC_PAUSE 0x0004 /* priority flow control pause on */
+/* all supported status flags */
+#define VNIC_STF_ALL (VNIC_STF_FATAL_ERR |\
+ VNIC_STF_STD_PAUSE |\
+ VNIC_STF_PFC_PAUSE |\
+ 0)
struct vnic_devcmd_provinfo {
u8 oui[3];
diff --git a/drivers/net/enic/vnic_intr.h b/drivers/net/enic/vnic_intr.h
index ccc408116af8..ce633a5a7e3c 100644
--- a/drivers/net/enic/vnic_intr.h
+++ b/drivers/net/enic/vnic_intr.h
@@ -78,7 +78,7 @@ static inline void vnic_intr_return_credits(struct vnic_intr *intr,
static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
{
- /* get and ack interrupt in one read (clear-and-ack-on-read) */
+ /* read PBA without clearing */
return ioread32(legacy_pba);
}
diff --git a/drivers/net/enic/vnic_resource.h b/drivers/net/enic/vnic_resource.h
index 144d2812f081..b61c22aec41a 100644
--- a/drivers/net/enic/vnic_resource.h
+++ b/drivers/net/enic/vnic_resource.h
@@ -38,7 +38,7 @@ enum vnic_res_type {
RES_TYPE_INTR_CTRL, /* Interrupt ctrl table */
RES_TYPE_INTR_TABLE, /* MSI/MSI-X Interrupt table */
RES_TYPE_INTR_PBA, /* MSI/MSI-X PBA table */
- RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status, r2c */
+ RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status */
RES_TYPE_RSVD6,
RES_TYPE_RSVD7,
RES_TYPE_DEVCMD, /* Device command region */
diff --git a/drivers/net/enic/vnic_rq.h b/drivers/net/enic/vnic_rq.h
index 82bfca67cc4d..fd0ef66d2e9f 100644
--- a/drivers/net/enic/vnic_rq.h
+++ b/drivers/net/enic/vnic_rq.h
@@ -132,8 +132,15 @@ static inline void vnic_rq_post(struct vnic_rq *rq,
#define VNIC_RQ_RETURN_RATE 0xf /* keep 2^n - 1 */
#endif
- if ((buf->index & VNIC_RQ_RETURN_RATE) == 0)
+ if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
iowrite32(buf->index, &rq->ctrl->posted_index);
+ }
}
static inline void vnic_rq_return_descs(struct vnic_rq *rq, unsigned int count)
diff --git a/drivers/net/enic/vnic_rss.h b/drivers/net/enic/vnic_rss.h
index e325d65d7c34..5fbb3c923bcd 100644
--- a/drivers/net/enic/vnic_rss.h
+++ b/drivers/net/enic/vnic_rss.h
@@ -1,6 +1,19 @@
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
*/
#ifndef _VNIC_RSS_H_
diff --git a/drivers/net/enic/vnic_wq.h b/drivers/net/enic/vnic_wq.h
index 7081828d8a42..c826137dc651 100644
--- a/drivers/net/enic/vnic_wq.h
+++ b/drivers/net/enic/vnic_wq.h
@@ -108,8 +108,15 @@ static inline void vnic_wq_post(struct vnic_wq *wq,
buf->len = len;
buf = buf->next;
- if (eop)
+ if (eop) {
+ /* Adding write memory barrier prevents compiler and/or CPU
+ * reordering, thus avoiding descriptor posting before
+ * descriptor is initialized. Otherwise, hardware can read
+ * stale descriptor fields.
+ */
+ wmb();
iowrite32(buf->index, &wq->ctrl->posted_index);
+ }
wq->to_use = buf;
wq->ring.desc_avail--;
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 76118ddd1042..f9b37c80dda6 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -322,7 +322,6 @@ static int __devinit epic_init_one (struct pci_dev *pdev,
int i, ret, option = 0, duplex = 0;
void *ring_space;
dma_addr_t ring_dma;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -364,7 +363,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev,
ioaddr = pci_resource_start (pdev, 0);
#else
ioaddr = pci_resource_start (pdev, 1);
- ioaddr = (long) ioremap (ioaddr, pci_resource_len (pdev, 1));
+ ioaddr = (long) pci_ioremap_bar(pdev, 1);
if (!ioaddr) {
dev_err(&pdev->dev, "ioremap failed\n");
goto err_out_free_netdev;
@@ -372,7 +371,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev,
#endif
pci_set_drvdata(pdev, dev);
- ep = dev->priv;
+ ep = netdev_priv(dev);
ep->mii.dev = dev;
ep->mii.mdio_read = mdio_read;
ep->mii.mdio_write = mdio_write;
@@ -499,9 +498,9 @@ static int __devinit epic_init_one (struct pci_dev *pdev,
if (ret < 0)
goto err_out_unmap_rx;
- printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %s\n",
+ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %pM\n",
dev->name, pci_id_tbl[chip_idx].name, ioaddr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
out:
return ret;
@@ -655,7 +654,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
static int epic_open(struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
int i;
int retval;
@@ -767,7 +766,7 @@ static int epic_open(struct net_device *dev)
static void epic_pause(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
netif_stop_queue (dev);
@@ -790,7 +789,7 @@ static void epic_pause(struct net_device *dev)
static void epic_restart(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
int i;
/* Soft reset the chip. */
@@ -842,7 +841,7 @@ static void epic_restart(struct net_device *dev)
static void check_media(struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
int mii_lpa = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], MII_LPA) : 0;
int negotiated = mii_lpa & ep->mii.advertising;
@@ -864,7 +863,7 @@ static void check_media(struct net_device *dev)
static void epic_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
int next_tick = 5*HZ;
@@ -885,7 +884,7 @@ static void epic_timer(unsigned long data)
static void epic_tx_timeout(struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
if (debug > 0) {
@@ -914,7 +913,7 @@ static void epic_tx_timeout(struct net_device *dev)
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void epic_init_ring(struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
int i;
ep->tx_full = 0;
@@ -960,7 +959,7 @@ static void epic_init_ring(struct net_device *dev)
static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
int entry, free_count;
u32 ctrl_word;
unsigned long flags;
@@ -1088,7 +1087,7 @@ static void epic_tx(struct net_device *dev, struct epic_private *ep)
static irqreturn_t epic_interrupt(int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
unsigned int handled = 0;
int status;
@@ -1110,9 +1109,9 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance)
if ((status & EpicNapiEvent) && !ep->reschedule_in_poll) {
spin_lock(&ep->napi_lock);
- if (netif_rx_schedule_prep(dev, &ep->napi)) {
+ if (netif_rx_schedule_prep(&ep->napi)) {
epic_napi_irq_off(dev, ep);
- __netif_rx_schedule(dev, &ep->napi);
+ __netif_rx_schedule(&ep->napi);
} else
ep->reschedule_in_poll++;
spin_unlock(&ep->napi_lock);
@@ -1156,7 +1155,7 @@ out:
static int epic_rx(struct net_device *dev, int budget)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
int entry = ep->cur_rx % RX_RING_SIZE;
int rx_work_limit = ep->dirty_rx + RX_RING_SIZE - ep->cur_rx;
int work_done = 0;
@@ -1223,7 +1222,6 @@ static int epic_rx(struct net_device *dev, int budget)
}
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
ep->stats.rx_packets++;
ep->stats.rx_bytes += pkt_len;
}
@@ -1290,7 +1288,7 @@ rx_action:
more = ep->reschedule_in_poll;
if (!more) {
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
outl(EpicNapiEvent, ioaddr + INTSTAT);
epic_napi_irq_on(dev, ep);
} else
@@ -1308,7 +1306,7 @@ rx_action:
static int epic_close(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
struct sk_buff *skb;
int i;
@@ -1358,7 +1356,7 @@ static int epic_close(struct net_device *dev)
static struct net_device_stats *epic_get_stats(struct net_device *dev)
{
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
long ioaddr = dev->base_addr;
if (netif_running(dev)) {
@@ -1379,7 +1377,7 @@ static struct net_device_stats *epic_get_stats(struct net_device *dev)
static void set_rx_mode(struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
unsigned char mc_filter[8]; /* Multicast hash filter */
int i;
@@ -1418,7 +1416,7 @@ static void set_rx_mode(struct net_device *dev)
static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
strcpy (info->driver, DRV_NAME);
strcpy (info->version, DRV_VERSION);
@@ -1427,7 +1425,7 @@ static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *
static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
int rc;
spin_lock_irq(&np->lock);
@@ -1439,7 +1437,7 @@ static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
int rc;
spin_lock_irq(&np->lock);
@@ -1451,13 +1449,13 @@ static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int netdev_nway_reset(struct net_device *dev)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
return mii_nway_restart(&np->mii);
}
static u32 netdev_get_link(struct net_device *dev)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
return mii_link_ok(&np->mii);
}
@@ -1506,7 +1504,7 @@ static const struct ethtool_ops netdev_ethtool_ops = {
static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct epic_private *np = dev->priv;
+ struct epic_private *np = netdev_priv(dev);
long ioaddr = dev->base_addr;
struct mii_ioctl_data *data = if_mii(rq);
int rc;
@@ -1534,7 +1532,7 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static void __devexit epic_remove_one (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct epic_private *ep = dev->priv;
+ struct epic_private *ep = netdev_priv(dev);
pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma);
pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma);
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
index 18f1364d3d5b..40125694bd9f 100644
--- a/drivers/net/eql.c
+++ b/drivers/net/eql.c
@@ -162,6 +162,13 @@ static void eql_timer(unsigned long param)
static char version[] __initdata =
"Equalizer2002: Simon Janes (simon@ncm.com) and David S. Miller (davem@redhat.com)\n";
+static const struct net_device_ops eql_netdev_ops = {
+ .ndo_open = eql_open,
+ .ndo_stop = eql_close,
+ .ndo_do_ioctl = eql_ioctl,
+ .ndo_start_xmit = eql_slave_xmit,
+};
+
static void __init eql_setup(struct net_device *dev)
{
equalizer_t *eql = netdev_priv(dev);
@@ -175,10 +182,7 @@ static void __init eql_setup(struct net_device *dev)
INIT_LIST_HEAD(&eql->queue.all_slaves);
eql->queue.master_dev = dev;
- dev->open = eql_open;
- dev->stop = eql_close;
- dev->do_ioctl = eql_ioctl;
- dev->hard_start_xmit = eql_slave_xmit;
+ dev->netdev_ops = &eql_netdev_ops;
/*
* Now we undo some of the things that eth_setup does
diff --git a/drivers/net/es3210.c b/drivers/net/es3210.c
index deefa51b8c31..5569f2ffb62c 100644
--- a/drivers/net/es3210.c
+++ b/drivers/net/es3210.c
@@ -64,9 +64,6 @@ static const char version[] =
static int es_probe1(struct net_device *dev, int ioaddr);
-static int es_open(struct net_device *dev);
-static int es_close(struct net_device *dev);
-
static void es_reset_8390(struct net_device *dev);
static void es_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
@@ -179,7 +176,6 @@ static int __init es_probe1(struct net_device *dev, int ioaddr)
{
int i, retval;
unsigned long eisa_id;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT, "es3210"))
return -ENODEV;
@@ -205,14 +201,14 @@ static int __init es_probe1(struct net_device *dev, int ioaddr)
if (dev->dev_addr[0] != ES_ADDR0 ||
dev->dev_addr[1] != ES_ADDR1 ||
dev->dev_addr[2] != ES_ADDR2) {
- printk("es3210.c: card not found %s (invalid_prefix).\n",
- print_mac(mac, dev->dev_addr));
+ printk("es3210.c: card not found %pM (invalid_prefix).\n",
+ dev->dev_addr);
retval = -ENODEV;
goto out;
}
- printk("es3210.c: ES3210 rev. %ld at %#x, node %s",
- eisa_id>>24, ioaddr, print_mac(mac, dev->dev_addr));
+ printk("es3210.c: ES3210 rev. %ld at %#x, node %pM",
+ eisa_id>>24, ioaddr, dev->dev_addr);
/* Snarf the interrupt now. */
if (dev->irq == 0) {
@@ -290,11 +286,7 @@ static int __init es_probe1(struct net_device *dev, int ioaddr)
ei_status.block_output = &es_block_output;
ei_status.get_8390_hdr = &es_get_8390_hdr;
- dev->open = &es_open;
- dev->stop = &es_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ei_netdev_ops;
NS8390_init(dev, 0);
retval = register_netdev(dev);
@@ -386,22 +378,6 @@ static void es_block_output(struct net_device *dev, int count,
memcpy_toio(shmem, buf, count);
}
-static int es_open(struct net_device *dev)
-{
- ei_open(dev);
- return 0;
-}
-
-static int es_close(struct net_device *dev)
-{
-
- if (ei_debug > 1)
- printk("%s: Shutting down ethercard.\n", dev->name);
-
- ei_close(dev);
- return 0;
-}
-
#ifdef MODULE
#define MAX_ES_CARDS 4 /* Max number of ES3210 cards per module */
#define NAMELEN 8 /* # of chars for storing dev->name */
diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c
index bee8b3fbc565..5c048f2fd74f 100644
--- a/drivers/net/eth16i.c
+++ b/drivers/net/eth16i.c
@@ -1205,7 +1205,6 @@ static void eth16i_rx(struct net_device *dev)
printk(KERN_DEBUG ".\n");
}
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
@@ -1466,7 +1465,7 @@ void __exit cleanup_module(void)
for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) {
struct net_device *dev = dev_eth16i[this_dev];
- if(dev->priv) {
+ if (netdev_priv(dev)) {
unregister_netdev(dev);
free_irq(dev->irq, dev);
release_region(dev->base_addr, ETH16I_IO_EXTENT);
@@ -1475,15 +1474,3 @@ void __exit cleanup_module(void)
}
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eth16i.c"
- * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict -prototypes -O6 -c eth16i.c"
- * tab-width: 8
- * c-basic-offset: 8
- * c-indent-level: 8
- * End:
- */
-
-/* End of file eth16i.c */
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 593a120e31b2..b852303c9362 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -396,7 +396,6 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase)
u_long mem_start, shmem_length;
u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0;
u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0;
- DECLARE_MAC_BUF(mac);
/*
** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot.
@@ -461,7 +460,7 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase)
if (lemac != LeMAC2)
DevicePresent(iobase); /* need after EWRK3_INIT */
status = get_hw_addr(dev, eeprom_image, lemac);
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
if (status) {
printk(" which has an EEPROM CRC error.\n");
@@ -646,10 +645,8 @@ static int ewrk3_open(struct net_device *dev)
ewrk3_init(dev);
if (ewrk3_debug > 1) {
- DECLARE_MAC_BUF(mac);
printk("%s: ewrk3 open with irq %d\n", dev->name, dev->irq);
- printk(" physical address: %s\n",
- print_mac(mac, dev->dev_addr));
+ printk(" physical address: %pM\n", dev->dev_addr);
if (lp->shmem_length == 0) {
printk(" no shared memory, I/O only mode\n");
} else {
@@ -1029,7 +1026,6 @@ static int ewrk3_rx(struct net_device *dev)
/*
** Update stats
*/
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
@@ -1971,13 +1967,3 @@ module_exit(ewrk3_exit_module);
module_init(ewrk3_init_module);
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c"
- *
- * compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c"
- * End:
- */
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index b455ae931f7a..31ab1ff623fc 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -486,7 +486,6 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
#else
int bar = 1;
#endif
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -665,9 +664,9 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev,
if (err)
goto err_out_free_tx;
- printk(KERN_INFO "%s: %s at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr,
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
return 0;
@@ -1727,7 +1726,6 @@ static int netdev_rx(struct net_device *dev)
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index ecd5c71a7a8a..7e33c129d51c 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -1155,7 +1155,7 @@ static phy_info_t const phy_info_ks8721bl = {
static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
{
- struct fec_enet_private *fep = dev->priv;
+ struct fec_enet_private *fep = netdev_priv(dev);
volatile uint *s = &(fep->phy_status);
*s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
@@ -2562,7 +2562,6 @@ static int __init fec_enet_module_init(void)
{
struct net_device *dev;
int i, err;
- DECLARE_MAC_BUF(mac);
printk("FEC ENET Version 0.2\n");
@@ -2581,8 +2580,7 @@ static int __init fec_enet_module_init(void)
return -EIO;
}
- printk("%s: ethernet %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: ethernet %pM\n", dev->name, dev->dev_addr);
}
return 0;
}
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index aec3b97e794d..cd8e98b45ec5 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -216,7 +216,7 @@ static int mpc52xx_fec_init_phy(struct net_device *dev)
struct phy_device *phydev;
char phy_id[BUS_ID_SIZE];
- snprintf(phy_id, BUS_ID_SIZE, "%x:%02x",
+ snprintf(phy_id, sizeof(phy_id), "%x:%02x",
(unsigned int)dev->base_addr, priv->phy_addr);
priv->link = PHY_DOWN;
@@ -487,7 +487,6 @@ static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
rskb->protocol = eth_type_trans(rskb, dev);
netif_rx(rskb);
- dev->last_rx = jiffies;
} else {
/* Can't get a new one : reuse the same & drop pkt */
dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
diff --git a/drivers/net/fec_mpc52xx_phy.c b/drivers/net/fec_mpc52xx_phy.c
index 45dd9bdc5d62..dd9bfa42ac34 100644
--- a/drivers/net/fec_mpc52xx_phy.c
+++ b/drivers/net/fec_mpc52xx_phy.c
@@ -122,9 +122,6 @@ static int mpc52xx_fec_mdio_probe(struct of_device *of,
out_be32(&priv->regs->mii_speed,
((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1);
- /* enable MII interrupt */
- out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII);
-
err = mdiobus_register(bus);
if (err)
goto out_unmap;
@@ -156,7 +153,7 @@ static int mpc52xx_fec_mdio_remove(struct of_device *of)
iounmap(priv->regs);
for (i=0; i<PHY_MAX_ADDR; i++)
- if (bus->irq[i])
+ if (bus->irq[i] != PHY_POLL)
irq_dispose_mapping(bus->irq[i]);
kfree(priv);
kfree(bus->irq);
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index cc7328b15521..5b68dc20168d 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -712,12 +712,12 @@ struct nv_skb_map {
/*
* SMP locking:
- * All hardware access under dev->priv->lock, except the performance
+ * All hardware access under netdev_priv(dev)->lock, except the performance
* critical parts:
* - rx is (pseudo-) lockless: it relies on the single-threading provided
* by the arch code for interrupts.
* - tx setup is lockless: it relies on netif_tx_lock. Actual submission
- * needs dev->priv->lock :-(
+ * needs netdev_priv(dev)->lock :-(
* - set_multicast_list: preparation lockless, relies on netif_tx_lock.
*/
@@ -818,7 +818,7 @@ struct fe_priv {
* Maximum number of loops until we assume that a bit in the irq mask
* is stuck. Overridable with module param.
*/
-static int max_interrupt_work = 5;
+static int max_interrupt_work = 15;
/*
* Optimization can be either throuput mode or cpu mode
@@ -1446,9 +1446,9 @@ static int phy_init(struct net_device *dev)
/* some phys clear out pause advertisment on reset, set it back */
mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
- /* restart auto negotiation */
+ /* restart auto negotiation, power down phy */
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
- mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
+ mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE | BMCR_PDOWN);
if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
return PHY_ERROR;
}
@@ -1760,7 +1760,7 @@ static void nv_do_rx_refill(unsigned long data)
struct fe_priv *np = netdev_priv(dev);
/* Just reschedule NAPI rx processing */
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
}
#else
static void nv_do_rx_refill(unsigned long data)
@@ -2735,7 +2735,6 @@ static int nv_rx_process(struct net_device *dev, int limit)
#else
netif_rx(skb);
#endif
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
next_pkt:
@@ -2848,7 +2847,6 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit)
}
}
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
} else {
@@ -3405,7 +3403,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
#ifdef CONFIG_FORCEDETH_NAPI
if (events & NVREG_IRQ_RX_ALL) {
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
/* Disable furthur receive irq's */
spin_lock(&np->lock);
@@ -3522,7 +3520,7 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
#ifdef CONFIG_FORCEDETH_NAPI
if (events & NVREG_IRQ_RX_ALL) {
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
/* Disable furthur receive irq's */
spin_lock(&np->lock);
@@ -3680,7 +3678,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget)
/* re-enable receive interrupts */
spin_lock_irqsave(&np->lock, flags);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
np->irqmask |= NVREG_IRQ_RX_ALL;
if (np->msi_flags & NV_MSI_X_ENABLED)
@@ -3706,7 +3704,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data)
writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus);
if (events) {
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
/* disable receive interrupts on the nic */
writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
pci_push(base);
@@ -5210,6 +5208,10 @@ static int nv_open(struct net_device *dev)
dprintk(KERN_DEBUG "nv_open: begin\n");
+ /* power up phy */
+ mii_rw(dev, np->phyaddr, MII_BMCR,
+ mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ) & ~BMCR_PDOWN);
+
/* erase previous misconfiguration */
if (np->driver_data & DEV_HAS_POWER_CNTRL)
nv_mac_reset(dev);
@@ -5403,6 +5405,10 @@ static int nv_close(struct net_device *dev)
if (np->wolenabled) {
writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
nv_start_rx(dev);
+ } else {
+ /* power down phy */
+ mii_rw(dev, np->phyaddr, MII_BMCR,
+ mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ)|BMCR_PDOWN);
}
/* FIXME: power down nic */
@@ -5410,6 +5416,38 @@ static int nv_close(struct net_device *dev)
return 0;
}
+static const struct net_device_ops nv_netdev_ops = {
+ .ndo_open = nv_open,
+ .ndo_stop = nv_close,
+ .ndo_get_stats = nv_get_stats,
+ .ndo_start_xmit = nv_start_xmit,
+ .ndo_tx_timeout = nv_tx_timeout,
+ .ndo_change_mtu = nv_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = nv_set_mac_address,
+ .ndo_set_multicast_list = nv_set_multicast,
+ .ndo_vlan_rx_register = nv_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = nv_poll_controller,
+#endif
+};
+
+static const struct net_device_ops nv_netdev_ops_optimized = {
+ .ndo_open = nv_open,
+ .ndo_stop = nv_close,
+ .ndo_get_stats = nv_get_stats,
+ .ndo_start_xmit = nv_start_xmit_optimized,
+ .ndo_tx_timeout = nv_tx_timeout,
+ .ndo_change_mtu = nv_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = nv_set_mac_address,
+ .ndo_set_multicast_list = nv_set_multicast,
+ .ndo_vlan_rx_register = nv_vlan_rx_register,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = nv_poll_controller,
+#endif
+};
+
static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
{
struct net_device *dev;
@@ -5420,7 +5458,6 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
u32 powerstate, txreg;
u32 phystate_orig = 0, phystate;
int phyinitialized = 0;
- DECLARE_MAC_BUF(mac);
static int printed_version;
if (!printed_version++)
@@ -5530,7 +5567,6 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
if (id->driver_data & DEV_HAS_VLAN) {
np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
- dev->vlan_rx_register = nv_vlan_rx_register;
}
np->msi_flags = 0;
@@ -5580,25 +5616,15 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
if (!np->rx_skb || !np->tx_skb)
goto out_freering;
- dev->open = nv_open;
- dev->stop = nv_close;
-
if (!nv_optimized(np))
- dev->hard_start_xmit = nv_start_xmit;
+ dev->netdev_ops = &nv_netdev_ops;
else
- dev->hard_start_xmit = nv_start_xmit_optimized;
- dev->get_stats = nv_get_stats;
- dev->change_mtu = nv_change_mtu;
- dev->set_mac_address = nv_set_mac_address;
- dev->set_multicast_list = nv_set_multicast;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = nv_poll_controller;
-#endif
+ dev->netdev_ops = &nv_netdev_ops_optimized;
+
#ifdef CONFIG_FORCEDETH_NAPI
netif_napi_add(dev, &np->napi, nv_napi_poll, RX_WORK_PER_LOOP);
#endif
SET_ETHTOOL_OPS(dev, &ops);
- dev->tx_timeout = nv_tx_timeout;
dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
pci_set_drvdata(pci_dev, dev);
@@ -5653,8 +5679,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
* to 01:23:45:67:89:ab
*/
dev_printk(KERN_ERR, &pci_dev->dev,
- "Invalid Mac address detected: %s\n",
- print_mac(mac, dev->dev_addr));
+ "Invalid Mac address detected: %pM\n",
+ dev->dev_addr);
dev_printk(KERN_ERR, &pci_dev->dev,
"Please complain to your hardware vendor. Switching to a random MAC.\n");
dev->dev_addr[0] = 0x00;
@@ -5663,8 +5689,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
get_random_bytes(&dev->dev_addr[3], 3);
}
- dprintk(KERN_DEBUG "%s: MAC Address %s\n",
- pci_name(pci_dev), print_mac(mac, dev->dev_addr));
+ dprintk(KERN_DEBUG "%s: MAC Address %pM\n",
+ pci_name(pci_dev), dev->dev_addr);
/* set mac address */
nv_copy_mac_to_hw(dev);
@@ -6141,7 +6167,7 @@ static struct pci_device_id pci_tbl[] = {
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_36),
- .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
+ .driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V3|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE,
},
{ /* MCP79 Ethernet Controller */
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_37),
diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c
index a6f49d025787..4e6a9195fe5f 100644
--- a/drivers/net/fs_enet/fs_enet-main.c
+++ b/drivers/net/fs_enet/fs_enet-main.c
@@ -209,7 +209,7 @@ static int fs_enet_rx_napi(struct napi_struct *napi, int budget)
if (received < budget) {
/* done */
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
(*fep->ops->napi_enable_rx)(dev);
}
return received;
@@ -478,7 +478,7 @@ fs_enet_interrupt(int irq, void *dev_id)
/* NOTE: it is possible for FCCs in NAPI mode */
/* to submit a spurious interrupt while in poll */
if (napi_ok)
- __netif_rx_schedule(dev, &fep->napi);
+ __netif_rx_schedule(&fep->napi);
}
}
@@ -1117,10 +1117,7 @@ static int __devinit fs_enet_probe(struct of_device *ofdev,
if (ret)
goto out_free_bd;
- printk(KERN_INFO "%s: fs_enet: %02x:%02x:%02x:%02x:%02x:%02x\n",
- ndev->name,
- ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
- ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
+ printk(KERN_INFO "%s: fs_enet: %pM\n", ndev->name, ndev->dev_addr);
return 0;
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index c4af949bf860..c672ecfc9595 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -25,11 +25,8 @@
*
* Theory of operation
*
- * The driver is initialized through platform_device. Structures which
- * define the configuration needed by the board are defined in a
- * board structure in arch/ppc/platforms (though I do not
- * discount the possibility that other architectures could one
- * day be supported.
+ * The driver is initialized through of_device. Configuration information
+ * is therefore conveyed through an OF-style device tree.
*
* The Gianfar Ethernet Controller uses a ring of buffer
* descriptors. The beginning is indicated by a register
@@ -78,7 +75,7 @@
#include <linux/if_vlan.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
-#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -92,6 +89,8 @@
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
+#include <linux/of.h>
#include "gianfar.h"
#include "gianfar_mii.h"
@@ -119,8 +118,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id);
static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev);
-static int gfar_probe(struct platform_device *pdev);
-static int gfar_remove(struct platform_device *pdev);
+static int gfar_probe(struct of_device *ofdev,
+ const struct of_device_id *match);
+static int gfar_remove(struct of_device *ofdev);
static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
@@ -131,7 +131,8 @@ static void gfar_netpoll(struct net_device *dev);
#endif
int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
static int gfar_clean_tx_ring(struct net_device *dev);
-static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
+ int amount_pull);
static void gfar_vlan_rx_register(struct net_device *netdev,
struct vlan_group *grp);
void gfar_halt(struct net_device *dev);
@@ -149,29 +150,163 @@ MODULE_LICENSE("GPL");
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
- return (priv->vlan_enable || priv->rx_csum_enable);
+ return priv->vlgrp || priv->rx_csum_enable;
+}
+
+static int gfar_of_init(struct net_device *dev)
+{
+ struct device_node *phy, *mdio;
+ const unsigned int *id;
+ const char *model;
+ const char *ctype;
+ const void *mac_addr;
+ const phandle *ph;
+ u64 addr, size;
+ int err = 0;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct device_node *np = priv->node;
+ char bus_name[MII_BUS_ID_SIZE];
+
+ if (!np || !of_device_is_available(np))
+ return -ENODEV;
+
+ /* get a pointer to the register memory */
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ priv->regs = ioremap(addr, size);
+
+ if (priv->regs == NULL)
+ return -ENOMEM;
+
+ priv->interruptTransmit = irq_of_parse_and_map(np, 0);
+
+ model = of_get_property(np, "model", NULL);
+
+ /* If we aren't the FEC we have multiple interrupts */
+ if (model && strcasecmp(model, "FEC")) {
+ priv->interruptReceive = irq_of_parse_and_map(np, 1);
+
+ priv->interruptError = irq_of_parse_and_map(np, 2);
+
+ if (priv->interruptTransmit < 0 ||
+ priv->interruptReceive < 0 ||
+ priv->interruptError < 0) {
+ err = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ mac_addr = of_get_mac_address(np);
+ if (mac_addr)
+ memcpy(dev->dev_addr, mac_addr, MAC_ADDR_LEN);
+
+ if (model && !strcasecmp(model, "TSEC"))
+ priv->device_flags =
+ FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE |
+ FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR;
+ if (model && !strcasecmp(model, "eTSEC"))
+ priv->device_flags =
+ FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE |
+ FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR |
+ FSL_GIANFAR_DEV_HAS_PADDING |
+ FSL_GIANFAR_DEV_HAS_CSUM |
+ FSL_GIANFAR_DEV_HAS_VLAN |
+ FSL_GIANFAR_DEV_HAS_MAGIC_PACKET |
+ FSL_GIANFAR_DEV_HAS_EXTENDED_HASH;
+
+ ctype = of_get_property(np, "phy-connection-type", NULL);
+
+ /* We only care about rgmii-id. The rest are autodetected */
+ if (ctype && !strcmp(ctype, "rgmii-id"))
+ priv->interface = PHY_INTERFACE_MODE_RGMII_ID;
+ else
+ priv->interface = PHY_INTERFACE_MODE_MII;
+
+ if (of_get_property(np, "fsl,magic-packet", NULL))
+ priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
+
+ ph = of_get_property(np, "phy-handle", NULL);
+ if (ph == NULL) {
+ u32 *fixed_link;
+
+ fixed_link = (u32 *)of_get_property(np, "fixed-link", NULL);
+ if (!fixed_link) {
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ snprintf(priv->phy_bus_id, BUS_ID_SIZE, PHY_ID_FMT, "0",
+ fixed_link[0]);
+ } else {
+ phy = of_find_node_by_phandle(*ph);
+
+ if (phy == NULL) {
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ mdio = of_get_parent(phy);
+
+ id = of_get_property(phy, "reg", NULL);
+
+ of_node_put(phy);
+ of_node_put(mdio);
+
+ gfar_mdio_bus_name(bus_name, mdio);
+ snprintf(priv->phy_bus_id, BUS_ID_SIZE, "%s:%02x",
+ bus_name, *id);
+ }
+
+ /* Find the TBI PHY. If it's not there, we don't support SGMII */
+ ph = of_get_property(np, "tbi-handle", NULL);
+ if (ph) {
+ struct device_node *tbi = of_find_node_by_phandle(*ph);
+ struct of_device *ofdev;
+ struct mii_bus *bus;
+
+ if (!tbi)
+ return 0;
+
+ mdio = of_get_parent(tbi);
+ if (!mdio)
+ return 0;
+
+ ofdev = of_find_device_by_node(mdio);
+
+ of_node_put(mdio);
+
+ id = of_get_property(tbi, "reg", NULL);
+ if (!id)
+ return 0;
+
+ of_node_put(tbi);
+
+ bus = dev_get_drvdata(&ofdev->dev);
+
+ priv->tbiphy = bus->phy_map[*id];
+ }
+
+ return 0;
+
+err_out:
+ iounmap(priv->regs);
+ return err;
}
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
-static int gfar_probe(struct platform_device *pdev)
+static int gfar_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
{
u32 tempval;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
- struct gianfar_platform_data *einfo;
- struct resource *r;
- int err = 0, irq;
DECLARE_MAC_BUF(mac);
-
- einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
-
- if (NULL == einfo) {
- printk(KERN_ERR "gfar %d: Missing additional data!\n",
- pdev->id);
-
- return -ENODEV;
- }
+ int err = 0;
+ int len_devname;
/* Create an ethernet device instance */
dev = alloc_etherdev(sizeof (*priv));
@@ -181,64 +316,23 @@ static int gfar_probe(struct platform_device *pdev)
priv = netdev_priv(dev);
priv->dev = dev;
+ priv->node = ofdev->node;
- /* Set the info in the priv to the current info */
- priv->einfo = einfo;
-
- /* fill out IRQ fields */
- if (einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- irq = platform_get_irq_byname(pdev, "tx");
- if (irq < 0)
- goto regs_fail;
- priv->interruptTransmit = irq;
-
- irq = platform_get_irq_byname(pdev, "rx");
- if (irq < 0)
- goto regs_fail;
- priv->interruptReceive = irq;
-
- irq = platform_get_irq_byname(pdev, "error");
- if (irq < 0)
- goto regs_fail;
- priv->interruptError = irq;
- } else {
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- goto regs_fail;
- priv->interruptTransmit = irq;
- }
-
- /* get a pointer to the register memory */
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->regs = ioremap(r->start, sizeof (struct gfar));
+ err = gfar_of_init(dev);
- if (NULL == priv->regs) {
- err = -ENOMEM;
+ if (err)
goto regs_fail;
- }
spin_lock_init(&priv->txlock);
spin_lock_init(&priv->rxlock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
- platform_set_drvdata(pdev, dev);
+ dev_set_drvdata(&ofdev->dev, priv);
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
- /* To do this, we write Graceful Receive Stop and Graceful */
- /* Transmit Stop, and then wait until the corresponding bits */
- /* in IEVENT indicate the stops have completed. */
- tempval = gfar_read(&priv->regs->dmactrl);
- tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
-
- tempval = gfar_read(&priv->regs->dmactrl);
- tempval |= (DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
-
- while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
- cpu_relax();
+ gfar_halt(dev);
/* Reset MAC layer */
gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
@@ -252,13 +346,10 @@ static int gfar_probe(struct platform_device *pdev)
/* Initialize ECNTRL */
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
- /* Copy the station address into the dev structure, */
- memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
-
/* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs);
- SET_NETDEV_DEV(dev, &pdev->dev);
+ SET_NETDEV_DEV(dev, &ofdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -276,23 +367,21 @@ static int gfar_probe(struct platform_device *pdev)
dev->ethtool_ops = &gfar_ethtool_ops;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
- dev->features |= NETIF_F_IP_CSUM;
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA;
} else
priv->rx_csum_enable = 0;
priv->vlgrp = NULL;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
dev->vlan_rx_register = gfar_vlan_rx_register;
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-
- priv->vlan_enable = 1;
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
priv->extended_hash = 1;
priv->hash_width = 9;
@@ -327,7 +416,7 @@ static int gfar_probe(struct platform_device *pdev)
priv->hash_regs[7] = &priv->regs->gaddr7;
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
priv->padding = DEFAULT_PADDING;
else
priv->padding = 0;
@@ -338,13 +427,12 @@ static int gfar_probe(struct platform_device *pdev)
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
+ priv->num_txbdfree = DEFAULT_TX_RING_SIZE;
priv->txcoalescing = DEFAULT_TX_COALESCE;
- priv->txcount = DEFAULT_TXCOUNT;
- priv->txtime = DEFAULT_TXTIME;
+ priv->txic = DEFAULT_TXIC;
priv->rxcoalescing = DEFAULT_RX_COALESCE;
- priv->rxcount = DEFAULT_RXCOUNT;
- priv->rxtime = DEFAULT_RXTIME;
+ priv->rxic = DEFAULT_RXIC;
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -360,12 +448,28 @@ static int gfar_probe(struct platform_device *pdev)
goto register_fail;
}
+ /* fill out IRQ number and name fields */
+ len_devname = strlen(dev->name);
+ strncpy(&priv->int_name_tx[0], dev->name, len_devname);
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ strncpy(&priv->int_name_tx[len_devname],
+ "_tx", sizeof("_tx") + 1);
+
+ strncpy(&priv->int_name_rx[0], dev->name, len_devname);
+ strncpy(&priv->int_name_rx[len_devname],
+ "_rx", sizeof("_rx") + 1);
+
+ strncpy(&priv->int_name_er[0], dev->name, len_devname);
+ strncpy(&priv->int_name_er[len_devname],
+ "_er", sizeof("_er") + 1);
+ } else
+ priv->int_name_tx[len_devname] = '\0';
+
/* Create all the sysfs files */
gfar_init_sysfs(dev);
/* Print out the device info */
- printk(KERN_INFO DEVICE_NAME "%s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO DEVICE_NAME "%pM\n", dev->name, dev->dev_addr);
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
@@ -382,29 +486,28 @@ regs_fail:
return err;
}
-static int gfar_remove(struct platform_device *pdev)
+static int gfar_remove(struct of_device *ofdev)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
- platform_set_drvdata(pdev, NULL);
+ dev_set_drvdata(&ofdev->dev, NULL);
iounmap(priv->regs);
- free_netdev(dev);
+ free_netdev(priv->dev);
return 0;
}
#ifdef CONFIG_PM
-static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
+static int gfar_suspend(struct of_device *ofdev, pm_message_t state)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
+ struct net_device *dev = priv->dev;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
- (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
netif_device_detach(dev);
@@ -445,14 +548,14 @@ static int gfar_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int gfar_resume(struct platform_device *pdev)
+static int gfar_resume(struct of_device *ofdev)
{
- struct net_device *dev = platform_get_drvdata(pdev);
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
+ struct net_device *dev = priv->dev;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
- (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
if (!netif_running(dev)) {
netif_device_attach(dev);
@@ -511,7 +614,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
if (ecntrl & ECNTRL_REDUCED_MII_MODE)
return PHY_INTERFACE_MODE_RMII;
else {
- phy_interface_t interface = priv->einfo->interface;
+ phy_interface_t interface = priv->interface;
/*
* This isn't autodetected right now, so it must
@@ -524,7 +627,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev)
}
}
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
return PHY_INTERFACE_MODE_GMII;
return PHY_INTERFACE_MODE_MII;
@@ -538,21 +641,18 @@ static int init_phy(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
- priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
+ priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
SUPPORTED_1000baseT_Full : 0;
struct phy_device *phydev;
- char phy_id[BUS_ID_SIZE];
phy_interface_t interface;
priv->oldlink = 0;
priv->oldspeed = 0;
priv->oldduplex = -1;
- snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id, priv->einfo->phy_id);
-
interface = gfar_get_interface(dev);
- phydev = phy_connect(dev, phy_id, &adjust_link, 0, interface);
+ phydev = phy_connect(dev, priv->phy_bus_id, &adjust_link, 0, interface);
if (interface == PHY_INTERFACE_MODE_SGMII)
gfar_configure_serdes(dev);
@@ -583,35 +683,31 @@ static int init_phy(struct net_device *dev)
static void gfar_configure_serdes(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar_mii __iomem *regs =
- (void __iomem *)&priv->regs->gfar_mii_regs;
- int tbipa = gfar_read(&priv->regs->tbipa);
- struct mii_bus *bus = gfar_get_miibus(priv);
- if (bus)
- mutex_lock(&bus->mdio_lock);
+ if (!priv->tbiphy) {
+ printk(KERN_WARNING "SGMII mode requires that the device "
+ "tree specify a tbi-handle\n");
+ return;
+ }
- /* If the link is already up, we must already be ok, and don't need to
+ /*
+ * If the link is already up, we must already be ok, and don't need to
* configure and reset the TBI<->SerDes link. Maybe U-Boot configured
* everything for us? Resetting it takes the link down and requires
* several seconds for it to come back.
*/
- if (gfar_local_mdio_read(regs, tbipa, MII_BMSR) & BMSR_LSTATUS)
- goto done;
+ if (phy_read(priv->tbiphy, MII_BMSR) & BMSR_LSTATUS)
+ return;
/* Single clk mode, mii mode off(for serdes communication) */
- gfar_local_mdio_write(regs, tbipa, MII_TBICON, TBICON_CLK_SELECT);
+ phy_write(priv->tbiphy, MII_TBICON, TBICON_CLK_SELECT);
- gfar_local_mdio_write(regs, tbipa, MII_ADVERTISE,
+ phy_write(priv->tbiphy, MII_ADVERTISE,
ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
ADVERTISE_1000XPSE_ASYM);
- gfar_local_mdio_write(regs, tbipa, MII_BMCR, BMCR_ANENABLE |
+ phy_write(priv->tbiphy, MII_BMCR, BMCR_ANENABLE |
BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000);
-
- done:
- if (bus)
- mutex_unlock(&bus->mdio_lock);
}
static void init_registers(struct net_device *dev)
@@ -644,7 +740,7 @@ static void init_registers(struct net_device *dev)
gfar_write(&priv->regs->gaddr7, 0);
/* Zero out the rmon mib registers if it has them */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib));
/* Mask off the CAM interrupts */
@@ -719,7 +815,7 @@ void stop_gfar(struct net_device *dev)
spin_unlock_irqrestore(&priv->txlock, flags);
/* Free the IRQs */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
free_irq(priv->interruptError, dev);
free_irq(priv->interruptTransmit, dev);
free_irq(priv->interruptReceive, dev);
@@ -742,22 +838,26 @@ static void free_skb_resources(struct gfar_private *priv)
{
struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
- int i;
+ int i, j;
/* Go through all the buffer descriptors and free their data buffers */
txbdp = priv->tx_bd_base;
for (i = 0; i < priv->tx_ring_size; i++) {
-
- if (priv->tx_skbuff[i]) {
- dma_unmap_single(&priv->dev->dev, txbdp->bufPtr,
- txbdp->length,
- DMA_TO_DEVICE);
- dev_kfree_skb_any(priv->tx_skbuff[i]);
- priv->tx_skbuff[i] = NULL;
+ if (!priv->tx_skbuff[i])
+ continue;
+
+ dma_unmap_single(&priv->dev->dev, txbdp->bufPtr,
+ txbdp->length, DMA_TO_DEVICE);
+ txbdp->lstatus = 0;
+ for (j = 0; j < skb_shinfo(priv->tx_skbuff[i])->nr_frags; j++) {
+ txbdp++;
+ dma_unmap_page(&priv->dev->dev, txbdp->bufPtr,
+ txbdp->length, DMA_TO_DEVICE);
}
-
txbdp++;
+ dev_kfree_skb_any(priv->tx_skbuff[i]);
+ priv->tx_skbuff[i] = NULL;
}
kfree(priv->tx_skbuff);
@@ -777,8 +877,7 @@ static void free_skb_resources(struct gfar_private *priv)
priv->rx_skbuff[i] = NULL;
}
- rxbdp->status = 0;
- rxbdp->length = 0;
+ rxbdp->lstatus = 0;
rxbdp->bufPtr = 0;
rxbdp++;
@@ -815,6 +914,8 @@ void gfar_start(struct net_device *dev)
/* Unmask the interrupts we look for */
gfar_write(&regs->imask, IMASK_DEFAULT);
+
+ dev->trans_start = jiffies;
}
/* Bring the controller up and running */
@@ -889,6 +990,7 @@ int startup_gfar(struct net_device *dev)
priv->rx_skbuff[i] = NULL;
/* Initialize some variables in our dev structure */
+ priv->num_txbdfree = priv->tx_ring_size;
priv->dirty_tx = priv->cur_tx = priv->tx_bd_base;
priv->cur_rx = priv->rx_bd_base;
priv->skb_curtx = priv->skb_dirtytx = 0;
@@ -897,8 +999,7 @@ int startup_gfar(struct net_device *dev)
/* Initialize Transmit Descriptor Ring */
txbdp = priv->tx_bd_base;
for (i = 0; i < priv->tx_ring_size; i++) {
- txbdp->status = 0;
- txbdp->length = 0;
+ txbdp->lstatus = 0;
txbdp->bufPtr = 0;
txbdp++;
}
@@ -933,11 +1034,11 @@ int startup_gfar(struct net_device *dev)
/* If the device has multiple interrupts, register for
* them. Otherwise, only register for the one */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
if (request_irq(priv->interruptError, gfar_error,
- 0, "enet_error", dev) < 0) {
+ 0, priv->int_name_er, dev) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
dev->name, priv->interruptError);
@@ -947,7 +1048,7 @@ int startup_gfar(struct net_device *dev)
}
if (request_irq(priv->interruptTransmit, gfar_transmit,
- 0, "enet_tx", dev) < 0) {
+ 0, priv->int_name_tx, dev) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
dev->name, priv->interruptTransmit);
@@ -958,7 +1059,7 @@ int startup_gfar(struct net_device *dev)
}
if (request_irq(priv->interruptReceive, gfar_receive,
- 0, "enet_rx", dev) < 0) {
+ 0, priv->int_name_rx, dev) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
dev->name, priv->interruptReceive);
@@ -968,10 +1069,10 @@ int startup_gfar(struct net_device *dev)
}
} else {
if (request_irq(priv->interruptTransmit, gfar_interrupt,
- 0, "gfar_interrupt", dev) < 0) {
+ 0, priv->int_name_tx, dev) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, priv->interruptError);
+ dev->name, priv->interruptTransmit);
err = -1;
goto err_irq_fail;
@@ -981,17 +1082,13 @@ int startup_gfar(struct net_device *dev)
phy_start(priv->phydev);
/* Configure the coalescing support */
+ gfar_write(&regs->txic, 0);
if (priv->txcoalescing)
- gfar_write(&regs->txic,
- mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(&regs->txic, 0);
+ gfar_write(&regs->txic, priv->txic);
+ gfar_write(&regs->rxic, 0);
if (priv->rxcoalescing)
- gfar_write(&regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&regs->rxic, 0);
+ gfar_write(&regs->rxic, priv->rxic);
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
@@ -1003,9 +1100,6 @@ int startup_gfar(struct net_device *dev)
rctrl |= RCTRL_EMEN;
}
- if (priv->vlan_enable)
- rctrl |= RCTRL_VLAN;
-
if (priv->padding) {
rctrl &= ~RCTRL_PAL_MASK;
rctrl |= RCTRL_PADDING(priv->padding);
@@ -1094,11 +1188,11 @@ static int gfar_enet_open(struct net_device *dev)
return err;
}
-static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp)
+static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb)
{
struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN);
- memset(fcb, 0, GMAC_FCB_LEN);
+ cacheable_memzero(fcb, GMAC_FCB_LEN);
return fcb;
}
@@ -1137,96 +1231,140 @@ void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
fcb->vlctl = vlan_tx_tag_get(skb);
}
+static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride,
+ struct txbd8 *base, int ring_size)
+{
+ struct txbd8 *new_bd = bdp + stride;
+
+ return (new_bd >= (base + ring_size)) ? (new_bd - ring_size) : new_bd;
+}
+
+static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base,
+ int ring_size)
+{
+ return skip_txbd(bdp, 1, base, ring_size);
+}
+
/* This is called by the kernel when a frame is ready for transmission. */
/* It is pointed to by the dev->hard_start_xmit function pointer */
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
struct txfcb *fcb = NULL;
- struct txbd8 *txbdp;
- u16 status;
+ struct txbd8 *txbdp, *txbdp_start, *base;
+ u32 lstatus;
+ int i;
+ u32 bufaddr;
unsigned long flags;
+ unsigned int nr_frags, length;
+
+ base = priv->tx_bd_base;
+
+ /* total number of fragments in the SKB */
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ spin_lock_irqsave(&priv->txlock, flags);
+
+ /* check if there is space to queue this packet */
+ if (nr_frags > priv->num_txbdfree) {
+ /* no space, stop the queue */
+ netif_stop_queue(dev);
+ dev->stats.tx_fifo_errors++;
+ spin_unlock_irqrestore(&priv->txlock, flags);
+ return NETDEV_TX_BUSY;
+ }
/* Update transmit stats */
dev->stats.tx_bytes += skb->len;
- /* Lock priv now */
- spin_lock_irqsave(&priv->txlock, flags);
+ txbdp = txbdp_start = priv->cur_tx;
- /* Point at the first free tx descriptor */
- txbdp = priv->cur_tx;
+ if (nr_frags == 0) {
+ lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
+ } else {
+ /* Place the fragment addresses and lengths into the TxBDs */
+ for (i = 0; i < nr_frags; i++) {
+ /* Point at the next BD, wrapping as needed */
+ txbdp = next_txbd(txbdp, base, priv->tx_ring_size);
+
+ length = skb_shinfo(skb)->frags[i].size;
- /* Clear all but the WRAP status flags */
- status = txbdp->status & TXBD_WRAP;
+ lstatus = txbdp->lstatus | length |
+ BD_LFLAG(TXBD_READY);
+
+ /* Handle the last BD specially */
+ if (i == nr_frags - 1)
+ lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
+
+ bufaddr = dma_map_page(&dev->dev,
+ skb_shinfo(skb)->frags[i].page,
+ skb_shinfo(skb)->frags[i].page_offset,
+ length,
+ DMA_TO_DEVICE);
+
+ /* set the TxBD length and buffer pointer */
+ txbdp->bufPtr = bufaddr;
+ txbdp->lstatus = lstatus;
+ }
+
+ lstatus = txbdp_start->lstatus;
+ }
/* Set up checksumming */
- if (likely((dev->features & NETIF_F_IP_CSUM)
- && (CHECKSUM_PARTIAL == skb->ip_summed))) {
- fcb = gfar_add_fcb(skb, txbdp);
- status |= TXBD_TOE;
+ if (CHECKSUM_PARTIAL == skb->ip_summed) {
+ fcb = gfar_add_fcb(skb);
+ lstatus |= BD_LFLAG(TXBD_TOE);
gfar_tx_checksum(skb, fcb);
}
- if (priv->vlan_enable &&
- unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) {
+ if (priv->vlgrp && vlan_tx_tag_present(skb)) {
if (unlikely(NULL == fcb)) {
- fcb = gfar_add_fcb(skb, txbdp);
- status |= TXBD_TOE;
+ fcb = gfar_add_fcb(skb);
+ lstatus |= BD_LFLAG(TXBD_TOE);
}
gfar_tx_vlan(skb, fcb);
}
- /* Set buffer length and pointer */
- txbdp->length = skb->len;
- txbdp->bufPtr = dma_map_single(&dev->dev, skb->data,
- skb->len, DMA_TO_DEVICE);
-
- /* Save the skb pointer so we can free it later */
+ /* setup the TxBD length and buffer pointer for the first BD */
priv->tx_skbuff[priv->skb_curtx] = skb;
+ txbdp_start->bufPtr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
- /* Update the current skb pointer (wrapping if this was the last) */
- priv->skb_curtx =
- (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
-
- /* Flag the BD as interrupt-causing */
- status |= TXBD_INTERRUPT;
+ lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
- /* Flag the BD as ready to go, last in frame, and */
- /* in need of CRC */
- status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
-
- dev->trans_start = jiffies;
-
- /* The powerpc-specific eieio() is used, as wmb() has too strong
+ /*
+ * The powerpc-specific eieio() is used, as wmb() has too strong
* semantics (it requires synchronization between cacheable and
* uncacheable mappings, which eieio doesn't provide and which we
* don't need), thus requiring a more expensive sync instruction. At
* some point, the set of architecture-independent barrier functions
* should be expanded to include weaker barriers.
*/
-
eieio();
- txbdp->status = status;
- /* If this was the last BD in the ring, the next one */
- /* is at the beginning of the ring */
- if (txbdp->status & TXBD_WRAP)
- txbdp = priv->tx_bd_base;
- else
- txbdp++;
+ txbdp_start->lstatus = lstatus;
+
+ /* Update the current skb pointer to the next entry we will use
+ * (wrapping if necessary) */
+ priv->skb_curtx = (priv->skb_curtx + 1) &
+ TX_RING_MOD_MASK(priv->tx_ring_size);
+
+ priv->cur_tx = next_txbd(txbdp, base, priv->tx_ring_size);
+
+ /* reduce TxBD free count */
+ priv->num_txbdfree -= (nr_frags + 1);
+
+ dev->trans_start = jiffies;
/* If the next BD still needs to be cleaned up, then the bds
are full. We need to tell the kernel to stop sending us stuff. */
- if (txbdp == priv->dirty_tx) {
+ if (!priv->num_txbdfree) {
netif_stop_queue(dev);
dev->stats.tx_fifo_errors++;
}
- /* Update the current txbd to the next one */
- priv->cur_tx = txbdp;
-
/* Tell the DMA to go go go */
gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
@@ -1270,11 +1408,15 @@ static void gfar_vlan_rx_register(struct net_device *dev,
{
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
+ struct vlan_group *old_grp;
u32 tempval;
spin_lock_irqsave(&priv->rxlock, flags);
- priv->vlgrp = grp;
+ old_grp = priv->vlgrp;
+
+ if (old_grp == grp)
+ return;
if (grp) {
/* Enable VLAN tag insertion */
@@ -1286,6 +1428,7 @@ static void gfar_vlan_rx_register(struct net_device *dev,
/* Enable VLAN tag extraction */
tempval = gfar_read(&priv->regs->rctrl);
tempval |= RCTRL_VLEX;
+ tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
gfar_write(&priv->regs->rctrl, tempval);
} else {
/* Disable VLAN tag insertion */
@@ -1296,9 +1439,16 @@ static void gfar_vlan_rx_register(struct net_device *dev,
/* Disable VLAN tag extraction */
tempval = gfar_read(&priv->regs->rctrl);
tempval &= ~RCTRL_VLEX;
+ /* If parse is no longer required, then disable parser */
+ if (tempval & RCTRL_REQ_PARSER)
+ tempval |= RCTRL_PRSDEP_INIT;
+ else
+ tempval &= ~RCTRL_PRSDEP_INIT;
gfar_write(&priv->regs->rctrl, tempval);
}
+ gfar_change_mtu(dev, dev->mtu);
+
spin_unlock_irqrestore(&priv->rxlock, flags);
}
@@ -1309,14 +1459,9 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
- if (priv->vlan_enable)
+ if (priv->vlgrp)
frame_size += VLAN_HLEN;
- if (gfar_uses_fcb(priv))
- frame_size += GMAC_FCB_LEN;
-
- frame_size += priv->padding;
-
if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
if (netif_msg_drv(priv))
printk(KERN_ERR "%s: Invalid MTU setting\n",
@@ -1324,6 +1469,11 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
return -EINVAL;
}
+ if (gfar_uses_fcb(priv))
+ frame_size += GMAC_FCB_LEN;
+
+ frame_size += priv->padding;
+
tempsize =
(frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
INCREMENTAL_BUFFER_SIZE;
@@ -1388,83 +1538,85 @@ static void gfar_timeout(struct net_device *dev)
/* Interrupt Handler for Transmit complete */
static int gfar_clean_tx_ring(struct net_device *dev)
{
- struct txbd8 *bdp;
struct gfar_private *priv = netdev_priv(dev);
+ struct txbd8 *bdp;
+ struct txbd8 *lbdp = NULL;
+ struct txbd8 *base = priv->tx_bd_base;
+ struct sk_buff *skb;
+ int skb_dirtytx;
+ int tx_ring_size = priv->tx_ring_size;
+ int frags = 0;
+ int i;
int howmany = 0;
+ u32 lstatus;
bdp = priv->dirty_tx;
- while ((bdp->status & TXBD_READY) == 0) {
- /* If dirty_tx and cur_tx are the same, then either the */
- /* ring is empty or full now (it could only be full in the beginning, */
- /* obviously). If it is empty, we are done. */
- if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0))
- break;
+ skb_dirtytx = priv->skb_dirtytx;
- howmany++;
+ while ((skb = priv->tx_skbuff[skb_dirtytx])) {
+ frags = skb_shinfo(skb)->nr_frags;
+ lbdp = skip_txbd(bdp, frags, base, tx_ring_size);
- /* Deferred means some collisions occurred during transmit, */
- /* but we eventually sent the packet. */
- if (bdp->status & TXBD_DEF)
- dev->stats.collisions++;
+ lstatus = lbdp->lstatus;
- /* Unmap the DMA memory */
- dma_unmap_single(&priv->dev->dev, bdp->bufPtr,
- bdp->length, DMA_TO_DEVICE);
+ /* Only clean completed frames */
+ if ((lstatus & BD_LFLAG(TXBD_READY)) &&
+ (lstatus & BD_LENGTH_MASK))
+ break;
+
+ dma_unmap_single(&dev->dev,
+ bdp->bufPtr,
+ bdp->length,
+ DMA_TO_DEVICE);
- /* Free the sk buffer associated with this TxBD */
- dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]);
+ bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
+ bdp = next_txbd(bdp, base, tx_ring_size);
- priv->tx_skbuff[priv->skb_dirtytx] = NULL;
- priv->skb_dirtytx =
- (priv->skb_dirtytx +
- 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+ for (i = 0; i < frags; i++) {
+ dma_unmap_page(&dev->dev,
+ bdp->bufPtr,
+ bdp->length,
+ DMA_TO_DEVICE);
+ bdp->lstatus &= BD_LFLAG(TXBD_WRAP);
+ bdp = next_txbd(bdp, base, tx_ring_size);
+ }
- /* Clean BD length for empty detection */
- bdp->length = 0;
+ dev_kfree_skb_any(skb);
+ priv->tx_skbuff[skb_dirtytx] = NULL;
- /* update bdp to point at next bd in the ring (wrapping if necessary) */
- if (bdp->status & TXBD_WRAP)
- bdp = priv->tx_bd_base;
- else
- bdp++;
+ skb_dirtytx = (skb_dirtytx + 1) &
+ TX_RING_MOD_MASK(tx_ring_size);
- /* Move dirty_tx to be the next bd */
- priv->dirty_tx = bdp;
+ howmany++;
+ priv->num_txbdfree += frags + 1;
+ }
+
+ /* If we freed a buffer, we can restart transmission, if necessary */
+ if (netif_queue_stopped(dev) && priv->num_txbdfree)
+ netif_wake_queue(dev);
- /* We freed a buffer, so now we can restart transmission */
- if (netif_queue_stopped(dev))
- netif_wake_queue(dev);
- } /* while ((bdp->status & TXBD_READY) == 0) */
+ /* Update dirty indicators */
+ priv->skb_dirtytx = skb_dirtytx;
+ priv->dirty_tx = bdp;
dev->stats.tx_packets += howmany;
return howmany;
}
-/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *dev_id)
+static void gfar_schedule_cleanup(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
-
- /* Clear IEVENT */
- gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
-
- /* Lock priv */
- spin_lock(&priv->txlock);
-
- gfar_clean_tx_ring(dev);
-
- /* If we are coalescing the interrupts, reset the timer */
- /* Otherwise, clear it */
- if (likely(priv->txcoalescing)) {
- gfar_write(&priv->regs->txic, 0);
- gfar_write(&priv->regs->txic,
- mk_ic_value(priv->txcount, priv->txtime));
+ if (netif_rx_schedule_prep(&priv->napi)) {
+ gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED);
+ __netif_rx_schedule(&priv->napi);
}
+}
- spin_unlock(&priv->txlock);
-
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *dev_id)
+{
+ gfar_schedule_cleanup((struct net_device *)dev_id);
return IRQ_HANDLED;
}
@@ -1472,20 +1624,19 @@ static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
struct sk_buff *skb)
{
struct gfar_private *priv = netdev_priv(dev);
- u32 * status_len = (u32 *)bdp;
- u16 flags;
+ u32 lstatus;
bdp->bufPtr = dma_map_single(&dev->dev, skb->data,
priv->rx_buffer_size, DMA_FROM_DEVICE);
- flags = RXBD_EMPTY | RXBD_INTERRUPT;
+ lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT);
if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1)
- flags |= RXBD_WRAP;
+ lstatus |= BD_LFLAG(RXBD_WRAP);
eieio();
- *status_len = (u32)flags << 16;
+ bdp->lstatus = lstatus;
}
@@ -1552,28 +1703,7 @@ static inline void count_errors(unsigned short status, struct net_device *dev)
irqreturn_t gfar_receive(int irq, void *dev_id)
{
- struct net_device *dev = (struct net_device *) dev_id;
- struct gfar_private *priv = netdev_priv(dev);
- u32 tempval;
-
- /* support NAPI */
- /* Clear IEVENT, so interrupts aren't called again
- * because of the packets that have already arrived */
- gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
-
- if (netif_rx_schedule_prep(dev, &priv->napi)) {
- tempval = gfar_read(&priv->regs->imask);
- tempval &= IMASK_RTX_DISABLED;
- gfar_write(&priv->regs->imask, tempval);
-
- __netif_rx_schedule(dev, &priv->napi);
- } else {
- if (netif_msg_rx_err(priv))
- printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n",
- dev->name, gfar_read(&priv->regs->ievent),
- gfar_read(&priv->regs->imask));
- }
-
+ gfar_schedule_cleanup((struct net_device *)dev_id);
return IRQ_HANDLED;
}
@@ -1589,59 +1719,38 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb)
}
-static inline struct rxfcb *gfar_get_fcb(struct sk_buff *skb)
-{
- struct rxfcb *fcb = (struct rxfcb *)skb->data;
-
- /* Remove the FCB from the skb */
- skb_pull(skb, GMAC_FCB_LEN);
-
- return fcb;
-}
-
/* gfar_process_frame() -- handle one incoming packet if skb
* isn't NULL. */
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
- int length)
+ int amount_pull)
{
struct gfar_private *priv = netdev_priv(dev);
struct rxfcb *fcb = NULL;
- if (NULL == skb) {
- if (netif_msg_rx_err(priv))
- printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name);
- dev->stats.rx_dropped++;
- priv->extra_stats.rx_skbmissing++;
- } else {
- int ret;
-
- /* Prep the skb for the packet */
- skb_put(skb, length);
+ int ret;
- /* Grab the FCB if there is one */
- if (gfar_uses_fcb(priv))
- fcb = gfar_get_fcb(skb);
+ /* fcb is at the beginning if exists */
+ fcb = (struct rxfcb *)skb->data;
- /* Remove the padded bytes, if there are any */
- if (priv->padding)
- skb_pull(skb, priv->padding);
+ /* Remove the FCB from the skb */
+ /* Remove the padded bytes, if there are any */
+ if (amount_pull)
+ skb_pull(skb, amount_pull);
- if (priv->rx_csum_enable)
- gfar_rx_checksum(skb, fcb);
+ if (priv->rx_csum_enable)
+ gfar_rx_checksum(skb, fcb);
- /* Tell the skb what kind of packet this is */
- skb->protocol = eth_type_trans(skb, dev);
+ /* Tell the skb what kind of packet this is */
+ skb->protocol = eth_type_trans(skb, dev);
- /* Send the packet up the stack */
- if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN))) {
- ret = vlan_hwaccel_receive_skb(skb, priv->vlgrp,
- fcb->vlctl);
- } else
- ret = netif_receive_skb(skb);
+ /* Send the packet up the stack */
+ if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN)))
+ ret = vlan_hwaccel_receive_skb(skb, priv->vlgrp, fcb->vlctl);
+ else
+ ret = netif_receive_skb(skb);
- if (NET_RX_DROP == ret)
- priv->extra_stats.kernel_dropped++;
- }
+ if (NET_RX_DROP == ret)
+ priv->extra_stats.kernel_dropped++;
return 0;
}
@@ -1652,14 +1761,19 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
*/
int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
{
- struct rxbd8 *bdp;
+ struct rxbd8 *bdp, *base;
struct sk_buff *skb;
- u16 pkt_len;
+ int pkt_len;
+ int amount_pull;
int howmany = 0;
struct gfar_private *priv = netdev_priv(dev);
/* Get the first full descriptor */
bdp = priv->cur_rx;
+ base = priv->rx_bd_base;
+
+ amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) +
+ priv->padding;
while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
struct sk_buff *newskb;
@@ -1680,23 +1794,30 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
if (unlikely(!newskb))
newskb = skb;
-
- if (skb)
+ else if (skb)
dev_kfree_skb_any(skb);
} else {
/* Increment the number of packets */
dev->stats.rx_packets++;
howmany++;
- /* Remove the FCS from the packet length */
- pkt_len = bdp->length - 4;
+ if (likely(skb)) {
+ pkt_len = bdp->length - ETH_FCS_LEN;
+ /* Remove the FCS from the packet length */
+ skb_put(skb, pkt_len);
+ dev->stats.rx_bytes += pkt_len;
- gfar_process_frame(dev, skb, pkt_len);
+ gfar_process_frame(dev, skb, amount_pull);
- dev->stats.rx_bytes += pkt_len;
- }
+ } else {
+ if (netif_msg_rx_err(priv))
+ printk(KERN_WARNING
+ "%s: Missing skb!\n", dev->name);
+ dev->stats.rx_dropped++;
+ priv->extra_stats.rx_skbmissing++;
+ }
- dev->last_rx = jiffies;
+ }
priv->rx_skbuff[priv->skb_currx] = newskb;
@@ -1704,10 +1825,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
gfar_new_rxbdp(dev, bdp, newskb);
/* Update to the next pointer */
- if (bdp->status & RXBD_WRAP)
- bdp = priv->rx_bd_base;
- else
- bdp++;
+ bdp = next_bd(bdp, base, priv->rx_ring_size);
/* update to point at the next skb */
priv->skb_currx =
@@ -1725,19 +1843,27 @@ static int gfar_poll(struct napi_struct *napi, int budget)
{
struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
struct net_device *dev = priv->dev;
- int howmany;
+ int tx_cleaned = 0;
+ int rx_cleaned = 0;
unsigned long flags;
+ /* Clear IEVENT, so interrupts aren't called again
+ * because of the packets that have already arrived */
+ gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+
/* If we fail to get the lock, don't bother with the TX BDs */
if (spin_trylock_irqsave(&priv->txlock, flags)) {
- gfar_clean_tx_ring(dev);
+ tx_cleaned = gfar_clean_tx_ring(dev);
spin_unlock_irqrestore(&priv->txlock, flags);
}
- howmany = gfar_clean_rx_ring(dev, budget);
+ rx_cleaned = gfar_clean_rx_ring(dev, budget);
+
+ if (tx_cleaned)
+ return budget;
- if (howmany < budget) {
- netif_rx_complete(dev, napi);
+ if (rx_cleaned < budget) {
+ netif_rx_complete(napi);
/* Clear the halt bit in RSTAT */
gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
@@ -1748,12 +1874,15 @@ static int gfar_poll(struct napi_struct *napi, int budget)
/* Otherwise, clear it */
if (likely(priv->rxcoalescing)) {
gfar_write(&priv->regs->rxic, 0);
- gfar_write(&priv->regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
+ gfar_write(&priv->regs->rxic, priv->rxic);
+ }
+ if (likely(priv->txcoalescing)) {
+ gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, priv->txic);
}
}
- return howmany;
+ return rx_cleaned;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1767,7 +1896,7 @@ static void gfar_netpoll(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
/* If the device has multiple interrupts, run tx/rx */
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
disable_irq(priv->interruptTransmit);
disable_irq(priv->interruptReceive);
disable_irq(priv->interruptError);
@@ -2061,7 +2190,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);
/* Magic Packet is not an error. */
- if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+ if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
(events & IEVENT_MAG))
events &= ~IEVENT_MAG;
@@ -2127,16 +2256,24 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:fsl-gianfar");
+static struct of_device_id gfar_match[] =
+{
+ {
+ .type = "network",
+ .compatible = "gianfar",
+ },
+ {},
+};
+
/* Structure for a device driver */
-static struct platform_driver gfar_driver = {
+static struct of_platform_driver gfar_driver = {
+ .name = "fsl-gianfar",
+ .match_table = gfar_match,
+
.probe = gfar_probe,
.remove = gfar_remove,
.suspend = gfar_suspend,
.resume = gfar_resume,
- .driver = {
- .name = "fsl-gianfar",
- .owner = THIS_MODULE,
- },
};
static int __init gfar_init(void)
@@ -2146,7 +2283,7 @@ static int __init gfar_init(void)
if (err)
return err;
- err = platform_driver_register(&gfar_driver);
+ err = of_register_platform_driver(&gfar_driver);
if (err)
gfar_mdio_exit();
@@ -2156,7 +2293,7 @@ static int __init gfar_init(void)
static void __exit gfar_exit(void)
{
- platform_driver_unregister(&gfar_driver);
+ of_unregister_platform_driver(&gfar_driver);
gfar_mdio_exit();
}
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index f46e9b63af13..b1a83344acc7 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -189,6 +189,18 @@ extern const char gfar_driver_version[];
#define mk_ic_value(count, time) (IC_ICEN | \
mk_ic_icft(count) | \
mk_ic_ictt(time))
+#define get_icft_value(ic) (((unsigned long)ic & IC_ICFT_MASK) >> \
+ IC_ICFT_SHIFT)
+#define get_ictt_value(ic) ((unsigned long)ic & IC_ICTT_MASK)
+
+#define DEFAULT_TXIC mk_ic_value(DEFAULT_TXCOUNT, DEFAULT_TXTIME)
+#define DEFAULT_RXIC mk_ic_value(DEFAULT_RXCOUNT, DEFAULT_RXTIME)
+
+#define skip_bd(bdp, stride, base, ring_size) ({ \
+ typeof(bdp) new_bd = (bdp) + (stride); \
+ (new_bd >= (base) + (ring_size)) ? (new_bd - (ring_size)) : new_bd; })
+
+#define next_bd(bdp, base, ring_size) skip_bd(bdp, 1, base, ring_size)
#define RCTRL_PAL_MASK 0x001f0000
#define RCTRL_VLEX 0x00002000
@@ -200,8 +212,10 @@ extern const char gfar_driver_version[];
#define RCTRL_PRSDEP_INIT 0x000000c0
#define RCTRL_PROM 0x00000008
#define RCTRL_EMEN 0x00000002
-#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN \
- | RCTRL_TUCSEN | RCTRL_PRSDEP_INIT)
+#define RCTRL_REQ_PARSER (RCTRL_VLEX | RCTRL_IPCSEN | \
+ RCTRL_TUCSEN)
+#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN | RCTRL_TUCSEN | \
+ RCTRL_PRSDEP_INIT)
#define RCTRL_EXTHASH (RCTRL_GHTX)
#define RCTRL_VLAN (RCTRL_PRSDEP_INIT)
#define RCTRL_PADDING(x) ((x << 16) & RCTRL_PAL_MASK)
@@ -237,7 +251,7 @@ extern const char gfar_driver_version[];
#define IEVENT_FIQ 0x00000004
#define IEVENT_DPE 0x00000002
#define IEVENT_PERR 0x00000001
-#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0)
+#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0 | IEVENT_BSY)
#define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF)
#define IEVENT_RTX_MASK (IEVENT_RX_MASK | IEVENT_TX_MASK)
#define IEVENT_ERR_MASK \
@@ -297,6 +311,8 @@ extern const char gfar_driver_version[];
#define ATTRELI_EI_MASK 0x00003fff
#define ATTRELI_EI(x) (x)
+#define BD_LFLAG(flags) ((flags) << 16)
+#define BD_LENGTH_MASK 0x00ff
/* TxBD status field bits */
#define TXBD_READY 0x8000
@@ -358,10 +374,17 @@ extern const char gfar_driver_version[];
#define RXFCB_PERR_MASK 0x000c
#define RXFCB_PERR_BADL3 0x0008
+#define GFAR_INT_NAME_MAX IFNAMSIZ + 4
+
struct txbd8
{
- u16 status; /* Status Fields */
- u16 length; /* Buffer length */
+ union {
+ struct {
+ u16 status; /* Status Fields */
+ u16 length; /* Buffer length */
+ };
+ u32 lstatus;
+ };
u32 bufPtr; /* Buffer Pointer */
};
@@ -376,8 +399,13 @@ struct txfcb {
struct rxbd8
{
- u16 status; /* Status Fields */
- u16 length; /* Buffer Length */
+ union {
+ struct {
+ u16 status; /* Status Fields */
+ u16 length; /* Buffer Length */
+ };
+ u32 lstatus;
+ };
u32 bufPtr; /* Buffer Pointer */
};
@@ -657,6 +685,19 @@ struct gfar {
};
+/* Flags related to gianfar device features */
+#define FSL_GIANFAR_DEV_HAS_GIGABIT 0x00000001
+#define FSL_GIANFAR_DEV_HAS_COALESCE 0x00000002
+#define FSL_GIANFAR_DEV_HAS_RMON 0x00000004
+#define FSL_GIANFAR_DEV_HAS_MULTI_INTR 0x00000008
+#define FSL_GIANFAR_DEV_HAS_CSUM 0x00000010
+#define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020
+#define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040
+#define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080
+#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100
+#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
+#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblence)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -681,8 +722,7 @@ struct gfar_private {
/* Configuration info for the coalescing features */
unsigned char txcoalescing;
- unsigned short txcount;
- unsigned short txtime;
+ unsigned long txic;
/* Buffer descriptor pointers */
struct txbd8 *tx_bd_base; /* First tx buffer descriptor */
@@ -690,10 +730,12 @@ struct gfar_private {
struct txbd8 *dirty_tx; /* First buffer in line
to be transmitted */
unsigned int tx_ring_size;
+ unsigned int num_txbdfree; /* number of TxBDs free */
/* RX Locked fields */
spinlock_t rxlock;
+ struct device_node *node;
struct net_device *dev;
struct napi_struct napi;
@@ -703,8 +745,7 @@ struct gfar_private {
/* RX Coalescing values */
unsigned char rxcoalescing;
- unsigned short rxcount;
- unsigned short rxtime;
+ unsigned long rxic;
struct rxbd8 *rx_bd_base; /* First Rx buffers */
struct rxbd8 *cur_rx; /* Next free rx ring entry */
@@ -733,8 +774,10 @@ struct gfar_private {
/* Bitfield update lock */
spinlock_t bflock;
- unsigned char vlan_enable:1,
- rx_csum_enable:1,
+ phy_interface_t interface;
+ char phy_bus_id[BUS_ID_SIZE];
+ u32 device_flags;
+ unsigned char rx_csum_enable:1,
extended_hash:1,
bd_stash_en:1,
wol_en:1; /* Wake-on-LAN enabled */
@@ -744,11 +787,9 @@ struct gfar_private {
unsigned int interruptReceive;
unsigned int interruptError;
- /* info structure initialized by platform code */
- struct gianfar_platform_data *einfo;
-
/* PHY stuff */
struct phy_device *phydev;
+ struct phy_device *tbiphy;
struct mii_bus *mii_bus;
int oldspeed;
int oldduplex;
@@ -757,6 +798,11 @@ struct gfar_private {
uint32_t msg_enable;
struct work_struct reset_task;
+
+ char int_name_tx[GFAR_INT_NAME_MAX];
+ char int_name_rx[GFAR_INT_NAME_MAX];
+ char int_name_er[GFAR_INT_NAME_MAX];
+
/* Network Statistics */
struct gfar_extra_stats extra_stats;
};
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index fb7d3ccc0fdc..59b3b5d98efe 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -121,7 +121,7 @@ static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
{
struct gfar_private *priv = netdev_priv(dev);
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
else
memcpy(buf, stat_gstrings,
@@ -138,7 +138,7 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -158,7 +158,7 @@ static int gfar_sset_count(struct net_device *dev, int sset)
switch (sset) {
case ETH_SS_STATS:
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
return GFAR_STATS_LEN;
else
return GFAR_EXTRA_STATS_LEN;
@@ -201,8 +201,8 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
if (NULL == phydev)
return -ENODEV;
- cmd->maxtxpkt = priv->txcount;
- cmd->maxrxpkt = priv->rxcount;
+ cmd->maxtxpkt = get_icft_value(priv->txic);
+ cmd->maxrxpkt = get_icft_value(priv->rxic);
return phy_ethtool_gset(phydev, cmd);
}
@@ -279,18 +279,26 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ unsigned long rxtime;
+ unsigned long rxcount;
+ unsigned long txtime;
+ unsigned long txcount;
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
if (NULL == priv->phydev)
return -ENODEV;
- cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
- cvals->rx_max_coalesced_frames = priv->rxcount;
+ rxtime = get_ictt_value(priv->rxic);
+ rxcount = get_icft_value(priv->rxic);
+ txtime = get_ictt_value(priv->txic);
+ txcount = get_icft_value(priv->txic);;
+ cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime);
+ cvals->rx_max_coalesced_frames = rxcount;
- cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, priv->txtime);
- cvals->tx_max_coalesced_frames = priv->txcount;
+ cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, txtime);
+ cvals->tx_max_coalesced_frames = txcount;
cvals->use_adaptive_rx_coalesce = 0;
cvals->use_adaptive_tx_coalesce = 0;
@@ -332,7 +340,7 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
/* Set up rx coalescing */
@@ -358,8 +366,9 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
- priv->rxcount = cvals->rx_max_coalesced_frames;
+ priv->rxic = mk_ic_value(
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs),
+ cvals->rx_max_coalesced_frames);
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
@@ -381,20 +390,17 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
- priv->txcount = cvals->tx_max_coalesced_frames;
+ priv->txic = mk_ic_value(
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs),
+ cvals->tx_max_coalesced_frames);
+ gfar_write(&priv->regs->rxic, 0);
if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic,
- mk_ic_value(priv->rxcount, priv->rxtime));
- else
- gfar_write(&priv->regs->rxic, 0);
+ gfar_write(&priv->regs->rxic, priv->rxic);
+ gfar_write(&priv->regs->txic, 0);
if (priv->txcoalescing)
- gfar_write(&priv->regs->txic,
- mk_ic_value(priv->txcount, priv->txtime));
- else
- gfar_write(&priv->regs->txic, 0);
+ gfar_write(&priv->regs->txic, priv->txic);
return 0;
}
@@ -456,11 +462,12 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
spin_lock(&priv->rxlock);
gfar_halt(dev);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
spin_unlock(&priv->rxlock);
spin_unlock_irqrestore(&priv->txlock, flags);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
+
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
@@ -468,11 +475,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
/* Change the size */
priv->rx_ring_size = rvals->rx_pending;
priv->tx_ring_size = rvals->tx_pending;
+ priv->num_txbdfree = priv->tx_ring_size;
/* Rebuild the rings with the new size */
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
-
+ netif_wake_queue(dev);
+ }
return err;
}
@@ -482,7 +491,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
unsigned long flags;
int err = 0;
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
if (dev->flags & IFF_UP) {
@@ -492,11 +501,12 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
spin_lock(&priv->rxlock);
gfar_halt(dev);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
spin_unlock(&priv->rxlock);
spin_unlock_irqrestore(&priv->txlock, flags);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
+
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
@@ -505,9 +515,10 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
priv->rx_csum_enable = data;
spin_unlock_irqrestore(&priv->bflock, flags);
- if (dev->flags & IFF_UP)
+ if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
-
+ netif_wake_queue(dev);
+ }
return err;
}
@@ -515,7 +526,7 @@ static uint32_t gfar_get_rx_csum(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return 0;
return priv->rx_csum_enable;
@@ -523,22 +534,19 @@ static uint32_t gfar_get_rx_csum(struct net_device *dev)
static int gfar_set_tx_csum(struct net_device *dev, uint32_t data)
{
- unsigned long flags;
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
- spin_lock_irqsave(&priv->txlock, flags);
- gfar_halt(dev);
+ netif_tx_lock_bh(dev);
if (data)
dev->features |= NETIF_F_IP_CSUM;
else
dev->features &= ~NETIF_F_IP_CSUM;
- gfar_start(dev);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ netif_tx_unlock_bh(dev);
return 0;
}
@@ -547,7 +555,7 @@ static uint32_t gfar_get_tx_csum(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return 0;
return (dev->features & NETIF_F_IP_CSUM) != 0;
@@ -570,7 +578,7 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct gfar_private *priv = netdev_priv(dev);
- if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) {
wol->supported = WAKE_MAGIC;
wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0;
} else {
@@ -583,7 +591,7 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
- if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
+ if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
wol->wolopts != 0)
return -EINVAL;
@@ -616,6 +624,7 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_tx_csum = gfar_get_tx_csum,
.set_rx_csum = gfar_set_rx_csum,
.set_tx_csum = gfar_set_tx_csum,
+ .set_sg = ethtool_op_set_sg,
.get_msglevel = gfar_get_msglevel,
.set_msglevel = gfar_set_msglevel,
#ifdef CONFIG_PM
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c
index 0e2595d24933..f3706e415b45 100644
--- a/drivers/net/gianfar_mii.c
+++ b/drivers/net/gianfar_mii.c
@@ -34,6 +34,8 @@
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -150,19 +152,83 @@ static int gfar_mdio_reset(struct mii_bus *bus)
return 0;
}
+/* Allocate an array which provides irq #s for each PHY on the given bus */
+static int *create_irq_map(struct device_node *np)
+{
+ int *irqs;
+ int i;
+ struct device_node *child = NULL;
+
+ irqs = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
+
+ if (!irqs)
+ return NULL;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ irqs[i] = PHY_POLL;
+
+ while ((child = of_get_next_child(np, child)) != NULL) {
+ int irq = irq_of_parse_and_map(child, 0);
+ const u32 *id;
+
+ if (irq == NO_IRQ)
+ continue;
+
+ id = of_get_property(child, "reg", NULL);
+
+ if (!id)
+ continue;
+
+ if (*id < PHY_MAX_ADDR && *id >= 0)
+ irqs[*id] = irq;
+ else
+ printk(KERN_WARNING "%s: "
+ "%d is not a valid PHY address\n",
+ np->full_name, *id);
+ }
+
+ return irqs;
+}
+
+
+void gfar_mdio_bus_name(char *name, struct device_node *np)
+{
+ const u32 *reg;
+
+ reg = of_get_property(np, "reg", NULL);
-static int gfar_mdio_probe(struct device *dev)
+ snprintf(name, MII_BUS_ID_SIZE, "%s@%x", np->name, reg ? *reg : 0);
+}
+
+/* Scan the bus in reverse, looking for an empty spot */
+static int gfar_mdio_find_free(struct mii_bus *new_bus)
+{
+ int i;
+
+ for (i = PHY_MAX_ADDR; i > 0; i--) {
+ u32 phy_id;
+
+ if (get_phy_id(new_bus, i, &phy_id))
+ return -1;
+
+ if (phy_id == 0xffffffff)
+ break;
+ }
+
+ return i;
+}
+
+static int gfar_mdio_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gianfar_mdio_data *pdata;
struct gfar_mii __iomem *regs;
struct gfar __iomem *enet_regs;
struct mii_bus *new_bus;
- struct resource *r;
- int i, err = 0;
-
- if (NULL == dev)
- return -EINVAL;
+ int err = 0;
+ u64 addr, size;
+ struct device_node *np = ofdev->node;
+ struct device_node *tbi;
+ int tbiaddr = -1;
new_bus = mdiobus_alloc();
if (NULL == new_bus)
@@ -172,31 +238,28 @@ static int gfar_mdio_probe(struct device *dev)
new_bus->read = &gfar_mdio_read,
new_bus->write = &gfar_mdio_write,
new_bus->reset = &gfar_mdio_reset,
- snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
-
- pdata = (struct gianfar_mdio_data *)pdev->dev.platform_data;
-
- if (NULL == pdata) {
- printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
- return -ENODEV;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gfar_mdio_bus_name(new_bus->id, np);
/* Set the PHY base address */
- regs = ioremap(r->start, sizeof (struct gfar_mii));
+ addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
+ regs = ioremap(addr, size);
if (NULL == regs) {
err = -ENOMEM;
- goto reg_map_fail;
+ goto err_free_bus;
}
new_bus->priv = (void __force *)regs;
- new_bus->irq = pdata->irq;
+ new_bus->irq = create_irq_map(np);
+
+ if (new_bus->irq == NULL) {
+ err = -ENOMEM;
+ goto err_unmap_regs;
+ }
- new_bus->parent = dev;
- dev_set_drvdata(dev, new_bus);
+ new_bus->parent = &ofdev->dev;
+ dev_set_drvdata(&ofdev->dev, new_bus);
/*
* This is mildly evil, but so is our hardware for doing this.
@@ -206,96 +269,109 @@ static int gfar_mdio_probe(struct device *dev)
enet_regs = (struct gfar __iomem *)
((char *)regs - offsetof(struct gfar, gfar_mii_regs));
- /* Scan the bus, looking for an empty spot for TBIPA */
- gfar_write(&enet_regs->tbipa, 0);
- for (i = PHY_MAX_ADDR; i > 0; i--) {
- u32 phy_id;
+ for_each_child_of_node(np, tbi) {
+ if (!strncmp(tbi->type, "tbi-phy", 8))
+ break;
+ }
- err = get_phy_id(new_bus, i, &phy_id);
- if (err)
- goto bus_register_fail;
+ if (tbi) {
+ const u32 *prop = of_get_property(tbi, "reg", NULL);
- if (phy_id == 0xffffffff)
- break;
+ if (prop)
+ tbiaddr = *prop;
}
- /* The bus is full. We don't support using 31 PHYs, sorry */
- if (i == 0) {
+ if (tbiaddr == -1) {
+ gfar_write(&enet_regs->tbipa, 0);
+
+ tbiaddr = gfar_mdio_find_free(new_bus);
+ }
+
+ /*
+ * We define TBIPA at 0 to be illegal, opting to fail for boards that
+ * have PHYs at 1-31, rather than change tbipa and rescan.
+ */
+ if (tbiaddr == 0) {
err = -EBUSY;
- goto bus_register_fail;
+ goto err_free_irqs;
}
- gfar_write(&enet_regs->tbipa, i);
+ gfar_write(&enet_regs->tbipa, tbiaddr);
+
+ /*
+ * The TBIPHY-only buses will find PHYs at every address,
+ * so we mask them all but the TBI
+ */
+ if (!of_device_is_compatible(np, "fsl,gianfar-mdio"))
+ new_bus->phy_mask = ~(1 << tbiaddr);
err = mdiobus_register(new_bus);
- if (0 != err) {
+ if (err != 0) {
printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
new_bus->name);
- goto bus_register_fail;
+ goto err_free_irqs;
}
return 0;
-bus_register_fail:
+err_free_irqs:
+ kfree(new_bus->irq);
+err_unmap_regs:
iounmap(regs);
-reg_map_fail:
+err_free_bus:
mdiobus_free(new_bus);
return err;
}
-static int gfar_mdio_remove(struct device *dev)
+static int gfar_mdio_remove(struct of_device *ofdev)
{
- struct mii_bus *bus = dev_get_drvdata(dev);
+ struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
mdiobus_unregister(bus);
- dev_set_drvdata(dev, NULL);
+ dev_set_drvdata(&ofdev->dev, NULL);
iounmap((void __iomem *)bus->priv);
bus->priv = NULL;
+ kfree(bus->irq);
mdiobus_free(bus);
return 0;
}
-static struct device_driver gianfar_mdio_driver = {
+static struct of_device_id gfar_mdio_match[] =
+{
+ {
+ .compatible = "fsl,gianfar-mdio",
+ },
+ {
+ .compatible = "fsl,gianfar-tbi",
+ },
+ {
+ .type = "mdio",
+ .compatible = "gianfar",
+ },
+ {},
+};
+
+static struct of_platform_driver gianfar_mdio_driver = {
.name = "fsl-gianfar_mdio",
- .bus = &platform_bus_type,
+ .match_table = gfar_mdio_match,
+
.probe = gfar_mdio_probe,
.remove = gfar_mdio_remove,
};
-static int match_mdio_bus(struct device *dev, void *data)
-{
- const struct gfar_private *priv = data;
- const struct platform_device *pdev = to_platform_device(dev);
-
- return !strcmp(pdev->name, gianfar_mdio_driver.name) &&
- pdev->id == priv->einfo->mdio_bus;
-}
-
-/* Given a gfar_priv structure, find the mii_bus controlled by this device (not
- * necessarily the same as the bus the gfar's PHY is on), if one exists.
- * Normally only the first gianfar controls a mii_bus. */
-struct mii_bus *gfar_get_miibus(const struct gfar_private *priv)
-{
- /*const*/ struct device *d;
-
- d = bus_find_device(gianfar_mdio_driver.bus, NULL, (void *)priv,
- match_mdio_bus);
- return d ? dev_get_drvdata(d) : NULL;
-}
-
int __init gfar_mdio_init(void)
{
- return driver_register(&gianfar_mdio_driver);
+ return of_register_platform_driver(&gianfar_mdio_driver);
}
void gfar_mdio_exit(void)
{
- driver_unregister(&gianfar_mdio_driver);
+ of_unregister_platform_driver(&gianfar_mdio_driver);
}
diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h
index 02dc970ca1ff..65c242cd468a 100644
--- a/drivers/net/gianfar_mii.h
+++ b/drivers/net/gianfar_mii.h
@@ -49,4 +49,6 @@ int gfar_local_mdio_read(struct gfar_mii __iomem *regs, int mii_id, int regnum);
struct mii_bus *gfar_get_miibus(const struct gfar_private *priv);
int __init gfar_mdio_init(void);
void gfar_mdio_exit(void);
+
+void gfar_mdio_bus_name(char *name, struct device_node *np);
#endif /* GIANFAR_PHY_H */
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
index 3199526bcecb..32200227c923 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/hamachi.c
@@ -568,6 +568,19 @@ static void set_rx_mode(struct net_device *dev);
static const struct ethtool_ops ethtool_ops;
static const struct ethtool_ops ethtool_ops_no_mii;
+static const struct net_device_ops hamachi_netdev_ops = {
+ .ndo_open = hamachi_open,
+ .ndo_stop = hamachi_close,
+ .ndo_start_xmit = hamachi_start_xmit,
+ .ndo_get_stats = hamachi_get_stats,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = hamachi_tx_timeout,
+ .ndo_do_ioctl = netdev_ioctl,
+};
+
+
static int __devinit hamachi_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -582,7 +595,6 @@ static int __devinit hamachi_init_one (struct pci_dev *pdev,
void *ring_space;
dma_addr_t ring_dma;
int ret = -ENOMEM;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -723,17 +735,11 @@ static int __devinit hamachi_init_one (struct pci_dev *pdev,
/* The Hamachi-specific entries in the device structure. */
- dev->open = &hamachi_open;
- dev->hard_start_xmit = &hamachi_start_xmit;
- dev->stop = &hamachi_close;
- dev->get_stats = &hamachi_get_stats;
- dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &netdev_ioctl;
+ dev->netdev_ops = &hamachi_netdev_ops;
if (chip_tbl[hmp->chip_id].flags & CanHaveMII)
SET_ETHTOOL_OPS(dev, &ethtool_ops);
else
SET_ETHTOOL_OPS(dev, &ethtool_ops_no_mii);
- dev->tx_timeout = &hamachi_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
if (mtu)
dev->mtu = mtu;
@@ -744,9 +750,9 @@ static int __devinit hamachi_init_one (struct pci_dev *pdev,
goto err_out_unmap_rx;
}
- printk(KERN_INFO "%s: %s type %x at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s type %x at %p, %pM, IRQ %d.\n",
dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev),
- ioaddr, print_mac(mac, dev->dev_addr), irq);
+ ioaddr, dev->dev_addr, irq);
i = readb(ioaddr + PCIClkMeas);
printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
"%2.2x, LPA %4.4x.\n",
@@ -1646,7 +1652,6 @@ static int hamachi_rx(struct net_device *dev)
#endif /* RX_CHECKSUM */
netif_rx(skb);
- dev->last_rx = jiffies;
hmp->stats.rx_packets++;
}
entry = (++hmp->cur_rx) % RX_RING_SIZE;
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 0f501d2ca935..50f1e172ee8f 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -373,7 +373,6 @@ static void sp_bump(struct sixpack *sp, char cmd)
memcpy(ptr, sp->cooked_buf + 1, count);
skb->protocol = ax25_type_trans(skb, sp->dev);
netif_rx(skb);
- sp->dev->last_rx = jiffies;
sp->dev->stats.rx_packets++;
return;
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 00bc7fbb6b37..81a65e3a1c05 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -555,7 +555,6 @@ static void do_rxpacket(struct net_device *dev)
memcpy(cp, bc->hdlcrx.buf, pktlen - 1);
skb->protocol = ax25_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
bc->stats.rx_packets++;
}
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 58f4b1d7bf1f..46f8f3390e7d 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -230,7 +230,6 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty
skb->protocol = ax25_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
unlock:
rcu_read_unlock();
@@ -441,16 +440,15 @@ static int bpq_seq_show(struct seq_file *seq, void *v)
"dev ether destination accept from\n");
else {
const struct bpqdev *bpqdev = v;
- DECLARE_MAC_BUF(mac);
- seq_printf(seq, "%-5s %-10s %s ",
+ seq_printf(seq, "%-5s %-10s %pM ",
bpqdev->axdev->name, bpqdev->ethdev->name,
- print_mac(mac, bpqdev->dest_addr));
+ bpqdev->dest_addr);
if (is_multicast_ether_addr(bpqdev->acpt_addr))
seq_printf(seq, "*\n");
else
- seq_printf(seq, "%s\n", print_mac(mac, bpqdev->acpt_addr));
+ seq_printf(seq, "%pM\n", bpqdev->acpt_addr);
}
return 0;
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index e8cfadefa4b6..e67103396ed7 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -572,7 +572,7 @@ static int __init setup_adapter(int card_base, int type, int n)
priv->param.persist = 256;
priv->param.dma = -1;
INIT_WORK(&priv->rx_work, rx_bh);
- dev->priv = priv;
+ dev->ml_priv = priv;
sprintf(dev->name, "dmascc%i", 2 * n + i);
dev->base_addr = card_base;
dev->irq = irq;
@@ -720,7 +720,7 @@ static int read_scc_data(struct scc_priv *priv)
static int scc_open(struct net_device *dev)
{
- struct scc_priv *priv = dev->priv;
+ struct scc_priv *priv = dev->ml_priv;
struct scc_info *info = priv->info;
int card_base = priv->card_base;
@@ -862,7 +862,7 @@ static int scc_open(struct net_device *dev)
static int scc_close(struct net_device *dev)
{
- struct scc_priv *priv = dev->priv;
+ struct scc_priv *priv = dev->ml_priv;
struct scc_info *info = priv->info;
int card_base = priv->card_base;
@@ -891,7 +891,7 @@ static int scc_close(struct net_device *dev)
static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct scc_priv *priv = dev->priv;
+ struct scc_priv *priv = dev->ml_priv;
switch (cmd) {
case SIOCGSCCPARAM:
@@ -918,7 +918,7 @@ static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int scc_send_packet(struct sk_buff *skb, struct net_device *dev)
{
- struct scc_priv *priv = dev->priv;
+ struct scc_priv *priv = dev->ml_priv;
unsigned long flags;
int i;
@@ -963,7 +963,7 @@ static int scc_send_packet(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *scc_get_stats(struct net_device *dev)
{
- struct scc_priv *priv = dev->priv;
+ struct scc_priv *priv = dev->ml_priv;
return &priv->stats;
}
@@ -1283,7 +1283,6 @@ static void rx_bh(struct work_struct *ugli_api)
memcpy(&data[1], priv->rx_buf[i], cb);
skb->protocol = ax25_type_trans(skb, priv->dev);
netif_rx(skb);
- priv->dev->last_rx = jiffies;
priv->stats.rx_packets++;
priv->stats.rx_bytes += cb;
}
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index c258a0586e61..8eba61a1d4ab 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -162,7 +162,6 @@ static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s)
memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
skb->protocol = ax25_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
s->stats.rx_packets++;
}
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index b8e25c4624d2..bbdb311b8420 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -303,7 +303,6 @@ static void ax_bump(struct mkiss *ax)
memcpy(skb_put(skb,count), ax->rbuff, count);
skb->protocol = ax25_type_trans(skb, ax->dev);
netif_rx(skb);
- ax->dev->last_rx = jiffies;
ax->stats.rx_packets++;
ax->stats.rx_bytes += count;
spin_unlock_bh(&ax->buflock);
@@ -847,12 +846,13 @@ static int mkiss_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct mkiss *ax = mkiss_get(tty);
- struct net_device *dev = ax->dev;
+ struct net_device *dev;
unsigned int tmp, err;
/* First make sure we're connected. */
if (ax == NULL)
return -ENXIO;
+ dev = ax->dev;
switch (cmd) {
case SIOCGIFNAME:
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index c17e39bc5460..c011af7088ea 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1518,7 +1518,7 @@ static int scc_net_alloc(const char *name, struct scc_channel *scc)
if (!dev)
return -ENOMEM;
- dev->priv = scc;
+ dev->ml_priv = scc;
scc->dev = dev;
spin_lock_init(&scc->lock);
init_timer(&scc->tx_t);
@@ -1575,7 +1575,7 @@ static void scc_net_setup(struct net_device *dev)
static int scc_net_open(struct net_device *dev)
{
- struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
if (!scc->init)
return -EINVAL;
@@ -1593,7 +1593,7 @@ static int scc_net_open(struct net_device *dev)
static int scc_net_close(struct net_device *dev)
{
- struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
unsigned long flags;
netif_stop_queue(dev);
@@ -1627,7 +1627,6 @@ static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
skb->protocol = ax25_type_trans(skb, scc->dev);
netif_rx(skb);
- scc->dev->last_rx = jiffies;
return;
}
@@ -1635,7 +1634,7 @@ static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
static int scc_net_tx(struct sk_buff *skb, struct net_device *dev)
{
- struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
unsigned long flags;
char kisscmd;
@@ -1705,7 +1704,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct scc_mem_config memcfg;
struct scc_hw_config hwcfg;
struct scc_calibrate cal;
- struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
int chan;
unsigned char device_name[IFNAMSIZ];
void __user *arg = ifr->ifr_data;
@@ -1952,7 +1951,7 @@ static int scc_net_set_mac_address(struct net_device *dev, void *addr)
static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
{
- struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct scc_channel *scc = (struct scc_channel *) dev->ml_priv;
scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 1c942862a3f4..5407f7486c9c 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -515,7 +515,6 @@ static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp)
memcpy(cp, yp->rx_buf, pkt_len - 1);
skb->protocol = ax25_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
++yp->stats.rx_packets;
}
}
diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
index c01e290d09d2..b507dbc16e62 100644
--- a/drivers/net/hp-plus.c
+++ b/drivers/net/hp-plus.c
@@ -158,6 +158,21 @@ out:
}
#endif
+static const struct net_device_ops hpp_netdev_ops = {
+ .ndo_open = hpp_open,
+ .ndo_stop = hpp_close,
+ .ndo_start_xmit = eip_start_xmit,
+ .ndo_tx_timeout = eip_tx_timeout,
+ .ndo_get_stats = eip_get_stats,
+ .ndo_set_multicast_list = eip_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = eip_poll,
+#endif
+};
+
+
/* Do the interesting part of the probe at a single address. */
static int __init hpp_probe1(struct net_device *dev, int ioaddr)
{
@@ -166,7 +181,6 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr)
const char name[] = "HP-PC-LAN+";
int mem_start;
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -193,7 +207,7 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr)
}
checksum += inb(ioaddr + 14);
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
if (checksum != 0xff) {
printk(" bad checksum %2.2x.\n", checksum);
@@ -227,11 +241,7 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr)
/* Set the base address to point to the NIC, not the "real" base! */
dev->base_addr = ioaddr + NIC_OFFSET;
- dev->open = &hpp_open;
- dev->stop = &hpp_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = eip_poll;
-#endif
+ dev->netdev_ops = &hpp_netdev_ops;
ei_status.name = name;
ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
@@ -302,8 +312,7 @@ hpp_open(struct net_device *dev)
/* Select the operational page. */
outw(Perf_Page, ioaddr + HP_PAGING);
- eip_open(dev);
- return 0;
+ return eip_open(dev);
}
static int
diff --git a/drivers/net/hp.c b/drivers/net/hp.c
index 0a8c64930ad3..5c4d78c1ff42 100644
--- a/drivers/net/hp.c
+++ b/drivers/net/hp.c
@@ -59,8 +59,6 @@ static unsigned int hppclan_portlist[] __initdata =
static int hp_probe1(struct net_device *dev, int ioaddr);
-static int hp_open(struct net_device *dev);
-static int hp_close(struct net_device *dev);
static void hp_reset_8390(struct net_device *dev);
static void hp_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
@@ -127,7 +125,6 @@ static int __init hp_probe1(struct net_device *dev, int ioaddr)
int i, retval, board_id, wordmode;
const char *name;
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -161,7 +158,7 @@ static int __init hp_probe1(struct net_device *dev, int ioaddr)
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = inb(ioaddr + i);
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
/* Snarf the interrupt now. Someday this could be moved to open(). */
if (dev->irq < 2) {
@@ -199,11 +196,7 @@ static int __init hp_probe1(struct net_device *dev, int ioaddr)
/* Set the base address to point to the NIC, not the "real" base! */
dev->base_addr = ioaddr + NIC_OFFSET;
- dev->open = &hp_open;
- dev->stop = &hp_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = eip_poll;
-#endif
+ dev->netdev_ops = &eip_netdev_ops;
ei_status.name = name;
ei_status.word16 = wordmode;
@@ -228,20 +221,6 @@ out:
return retval;
}
-static int
-hp_open(struct net_device *dev)
-{
- eip_open(dev);
- return 0;
-}
-
-static int
-hp_close(struct net_device *dev)
-{
- eip_close(dev);
- return 0;
-}
-
static void
hp_reset_8390(struct net_device *dev)
{
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index 571dd80fb850..ebe7651fcb86 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -1212,7 +1212,7 @@ static int hp100_init_rxpdl(struct net_device *dev,
*(pdlptr + 2) = (u_int) virt_to_whatever(dev, pdlptr); /* Address Frag 1 */
*(pdlptr + 3) = 4; /* Length Frag 1 */
- return ((((MAX_RX_FRAG * 2 + 2) + 3) / 4) * 4);
+ return roundup(MAX_RX_FRAG * 2 + 2, 4);
}
@@ -1227,7 +1227,7 @@ static int hp100_init_txpdl(struct net_device *dev,
ringptr->pdl_paddr = virt_to_whatever(dev, pdlptr); /* +1 */
ringptr->skb = (void *) NULL;
- return ((((MAX_TX_FRAG * 2 + 2) + 3) / 4) * 4);
+ return roundup(MAX_TX_FRAG * 2 + 2, 4);
}
/*
@@ -1256,7 +1256,7 @@ static int hp100_build_rx_pdl(hp100_ring_t * ringptr,
/* Note: This depends on the alloc_skb functions allocating more
* space than requested, i.e. aligning to 16bytes */
- ringptr->skb = dev_alloc_skb(((MAX_ETHER_SIZE + 2 + 3) / 4) * 4);
+ ringptr->skb = dev_alloc_skb(roundup(MAX_ETHER_SIZE + 2, 4));
if (NULL != ringptr->skb) {
/*
@@ -1279,7 +1279,7 @@ static int hp100_build_rx_pdl(hp100_ring_t * ringptr,
#ifdef HP100_DEBUG_BM
printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
dev->name, (u_int) ringptr->pdl,
- ((MAX_ETHER_SIZE + 2 + 3) / 4) * 4,
+ roundup(MAX_ETHER_SIZE + 2, 4),
(unsigned int) ringptr->skb->data);
#endif
@@ -1834,7 +1834,6 @@ static void hp100_rx(struct net_device *dev)
ptr[9], ptr[10], ptr[11]);
#endif
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
}
@@ -1925,7 +1924,6 @@ static void hp100_rx_bm(struct net_device *dev)
netif_rx(ptr->skb); /* Up and away... */
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
}
@@ -2093,9 +2091,8 @@ static void hp100_set_multicast_list(struct net_device *dev)
addrs = dmi->dmi_addr;
if ((*addrs & 0x01) == 0x01) { /* multicast address? */
#ifdef HP100_DEBUG
- DECLARE_MAC_BUF(mac);
- printk("hp100: %s: multicast = %s, ",
- dev->name, print_mac(mac, addrs));
+ printk("hp100: %s: multicast = %pM, ",
+ dev->name, addrs);
#endif
for (j = idx = 0; j < 6; j++) {
idx ^= *addrs++ & 0x3f;
@@ -3057,12 +3054,3 @@ static void __exit hp100_module_exit(void)
module_init(hp100_module_init)
module_exit(hp100_module_exit)
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c"
- * c-indent-level: 2
- * tab-width: 8
- * End:
- */
diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c
index b96cf2dcb109..9cb38a8d4387 100644
--- a/drivers/net/hydra.c
+++ b/drivers/net/hydra.c
@@ -94,6 +94,21 @@ static int __devinit hydra_init_one(struct zorro_dev *z,
return 0;
}
+static const struct net_device_ops hydra_netdev_ops = {
+ .ndo_open = hydra_open,
+ .ndo_stop = hydra_close,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __devinit hydra_init(struct zorro_dev *z)
{
struct net_device *dev;
@@ -103,14 +118,13 @@ static int __devinit hydra_init(struct zorro_dev *z)
int start_page, stop_page;
int j;
int err;
- DECLARE_MAC_BUF(mac);
static u32 hydra_offsets[16] = {
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
};
- dev = ____alloc_ei_netdev(0);
+ dev = alloc_ei_netdev();
if (!dev)
return -ENOMEM;
@@ -145,12 +159,8 @@ static int __devinit hydra_init(struct zorro_dev *z)
ei_status.block_output = &hydra_block_output;
ei_status.get_8390_hdr = &hydra_get_8390_hdr;
ei_status.reg_offset = hydra_offsets;
- dev->open = &hydra_open;
- dev->stop = &hydra_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = __ei_poll;
-#endif
+ dev->netdev_ops = &hydra_netdev_ops;
__NS8390_init(dev, 0);
err = register_netdev(dev);
@@ -163,8 +173,8 @@ static int __devinit hydra_init(struct zorro_dev *z)
zorro_set_drvdata(z, dev);
printk(KERN_INFO "%s: Hydra at 0x%08lx, address "
- "%s (hydra.c " HYDRA_VERSION ")\n",
- dev->name, z->resource.start, print_mac(mac, dev->dev_addr));
+ "%pM (hydra.c " HYDRA_VERSION ")\n",
+ dev->name, z->resource.start, dev->dev_addr);
return 0;
}
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 901212aa37cb..87a706694fb3 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -396,9 +396,7 @@ static void emac_hash_mc(struct emac_instance *dev)
for (dmi = dev->ndev->mc_list; dmi; dmi = dmi->next) {
int slot, reg, mask;
- DBG2(dev, "mc %02x:%02x:%02x:%02x:%02x:%02x" NL,
- dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
- dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5]);
+ DBG2(dev, "mc %pM" NL, dmi->dmi_addr);
slot = EMAC_XAHT_CRC_TO_SLOT(dev, ether_crc(ETH_ALEN, dmi->dmi_addr));
reg = EMAC_XAHT_SLOT_TO_REG(dev, slot);
@@ -2865,11 +2863,8 @@ static int __devinit emac_probe(struct of_device *ofdev,
wake_up_all(&emac_probe_wait);
- printk(KERN_INFO
- "%s: EMAC-%d %s, MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
- ndev->name, dev->cell_index, np->full_name,
- ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
- ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
+ printk(KERN_INFO "%s: EMAC-%d %s, MAC %pM\n",
+ ndev->name, dev->cell_index, np->full_name, ndev->dev_addr);
if (dev->phy_mode == PHY_MODE_SGMII)
printk(KERN_NOTICE "%s: in SGMII mode\n", ndev->name);
diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c
index f02764725a22..5b5bf9f9861a 100644
--- a/drivers/net/ibmlana.c
+++ b/drivers/net/ibmlana.c
@@ -605,7 +605,6 @@ static void irqrx_handler(struct net_device *dev)
skb->ip_summed = CHECKSUM_NONE;
/* bookkeeping */
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += rda.length;
@@ -914,7 +913,6 @@ static int __devinit ibmlana_init_one(struct device *kdev)
int base = 0, irq = 0, iobase = 0, memlen = 0;
ibmlana_priv *priv;
ibmlana_medium medium;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(sizeof(ibmlana_priv));
if (!dev)
@@ -990,10 +988,10 @@ static int __devinit ibmlana_init_one(struct device *kdev)
/* print config */
printk(KERN_INFO "%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, "
- "MAC address %s.\n",
+ "MAC address %pM.\n",
dev->name, priv->realirq, dev->base_addr,
dev->mem_start, dev->mem_end - 1,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
printk(KERN_INFO "%s: %s medium\n", dev->name, MediaNames[priv->medium]);
/* reset board */
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index c2d57f836088..9bc0f178f24b 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -527,7 +527,7 @@ retry:
static int ibmveth_open(struct net_device *netdev)
{
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
u64 mac_address = 0;
int rxq_entries = 1;
unsigned long lpar_rc;
@@ -666,7 +666,7 @@ static int ibmveth_open(struct net_device *netdev)
static int ibmveth_close(struct net_device *netdev)
{
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
long lpar_rc;
ibmveth_debug_printk("close starting\n");
@@ -722,7 +722,7 @@ static u32 netdev_get_link(struct net_device *dev) {
static void ibmveth_set_rx_csum_flags(struct net_device *dev, u32 data)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
if (data)
adapter->rx_csum = 1;
@@ -741,7 +741,7 @@ static void ibmveth_set_rx_csum_flags(struct net_device *dev, u32 data)
static void ibmveth_set_tx_csum_flags(struct net_device *dev, u32 data)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
if (data) {
dev->features |= NETIF_F_IP_CSUM;
@@ -753,7 +753,7 @@ static void ibmveth_set_tx_csum_flags(struct net_device *dev, u32 data)
static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
void (*done) (struct net_device *, u32))
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
u64 set_attr, clr_attr, ret_attr;
long ret;
int rc1 = 0, rc2 = 0;
@@ -805,7 +805,7 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data,
static int ibmveth_set_rx_csum(struct net_device *dev, u32 data)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
if ((data && adapter->rx_csum) || (!data && !adapter->rx_csum))
return 0;
@@ -815,7 +815,7 @@ static int ibmveth_set_rx_csum(struct net_device *dev, u32 data)
static int ibmveth_set_tx_csum(struct net_device *dev, u32 data)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
int rc = 0;
if (data && (dev->features & NETIF_F_IP_CSUM))
@@ -833,7 +833,7 @@ static int ibmveth_set_tx_csum(struct net_device *dev, u32 data)
static u32 ibmveth_get_rx_csum(struct net_device *dev)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
return adapter->rx_csum;
}
@@ -862,7 +862,7 @@ static void ibmveth_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
int i;
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
for (i = 0; i < ARRAY_SIZE(ibmveth_stats); i++)
data[i] = IBMVETH_GET_STAT(adapter, ibmveth_stats[i].offset);
@@ -889,7 +889,7 @@ static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
union ibmveth_buf_desc desc;
unsigned long lpar_rc;
unsigned long correlator;
@@ -1014,7 +1014,6 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
netdev->stats.rx_packets++;
netdev->stats.rx_bytes += length;
frames_processed++;
- netdev->last_rx = jiffies;
}
} while (frames_processed < budget);
@@ -1029,10 +1028,10 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
ibmveth_assert(lpar_rc == H_SUCCESS);
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
if (ibmveth_rxq_pending_buffer(adapter) &&
- netif_rx_reschedule(netdev, napi)) {
+ netif_rx_reschedule(napi)) {
lpar_rc = h_vio_signal(adapter->vdev->unit_address,
VIO_IRQ_DISABLE);
goto restart_poll;
@@ -1045,21 +1044,21 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance)
{
struct net_device *netdev = dev_instance;
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
unsigned long lpar_rc;
- if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
+ if (netif_rx_schedule_prep(&adapter->napi)) {
lpar_rc = h_vio_signal(adapter->vdev->unit_address,
VIO_IRQ_DISABLE);
ibmveth_assert(lpar_rc == H_SUCCESS);
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
return IRQ_HANDLED;
}
static void ibmveth_set_multicast_list(struct net_device *netdev)
{
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
unsigned long lpar_rc;
if((netdev->flags & IFF_PROMISC) || (netdev->mc_count > adapter->mcastFilterSize)) {
@@ -1107,7 +1106,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
{
- struct ibmveth_adapter *adapter = dev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
struct vio_dev *viodev = adapter->vdev;
int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH;
int i;
@@ -1159,7 +1158,7 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
#ifdef CONFIG_NET_POLL_CONTROLLER
static void ibmveth_poll_controller(struct net_device *dev)
{
- ibmveth_replenish_task(dev->priv);
+ ibmveth_replenish_task(netdev_priv(dev));
ibmveth_interrupt(dev->irq, dev);
}
#endif
@@ -1241,7 +1240,7 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_
if(!netdev)
return -ENOMEM;
- adapter = netdev->priv;
+ adapter = netdev_priv(netdev);
dev->dev.driver_data = netdev;
adapter->vdev = dev;
@@ -1337,7 +1336,7 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_
static int __devexit ibmveth_remove(struct vio_dev *dev)
{
struct net_device *netdev = dev->dev.driver_data;
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
int i;
for(i = 0; i<IbmVethNumBufferPools; i++)
@@ -1371,13 +1370,12 @@ static int ibmveth_show(struct seq_file *seq, void *v)
struct ibmveth_adapter *adapter = seq->private;
char *current_mac = ((char*) &adapter->netdev->dev_addr);
char *firmware_mac = ((char*) &adapter->mac_addr) ;
- DECLARE_MAC_BUF(mac);
seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version);
seq_printf(seq, "Unit Address: 0x%x\n", adapter->vdev->unit_address);
- seq_printf(seq, "Current MAC: %s\n", print_mac(mac, current_mac));
- seq_printf(seq, "Firmware MAC: %s\n", print_mac(mac, firmware_mac));
+ seq_printf(seq, "Current MAC: %pM\n", current_mac);
+ seq_printf(seq, "Firmware MAC: %pM\n", firmware_mac);
seq_printf(seq, "\nAdapter Statistics:\n");
seq_printf(seq, " TX: vio_map_single failres: %ld\n", adapter->tx_map_failed);
@@ -1472,7 +1470,7 @@ const char * buf, size_t count)
kobj);
struct net_device *netdev =
container_of(kobj->parent, struct device, kobj)->driver_data;
- struct ibmveth_adapter *adapter = netdev->priv;
+ struct ibmveth_adapter *adapter = netdev_priv(netdev);
long value = simple_strtol(buf, NULL, 10);
long rc;
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index e4fbefc8c82f..60a263001933 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -137,18 +137,23 @@ resched:
}
+static const struct net_device_ops ifb_netdev_ops = {
+ .ndo_open = ifb_open,
+ .ndo_stop = ifb_close,
+ .ndo_start_xmit = ifb_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
static void ifb_setup(struct net_device *dev)
{
/* Initialize the device structure. */
- dev->hard_start_xmit = ifb_xmit;
- dev->open = &ifb_open;
- dev->stop = &ifb_close;
dev->destructor = free_netdev;
+ dev->netdev_ops = &ifb_netdev_ops;
/* Fill in device structure with ethernet-generic values. */
ether_setup(dev);
dev->tx_queue_len = TX_Q_LIMIT;
- dev->change_mtu = NULL;
+
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
random_ether_addr(dev->dev_addr);
diff --git a/drivers/net/igb/e1000_defines.h b/drivers/net/igb/e1000_defines.h
index ce700689fb57..40d03426c122 100644
--- a/drivers/net/igb/e1000_defines.h
+++ b/drivers/net/igb/e1000_defines.h
@@ -168,18 +168,12 @@
#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
-/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
-/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
-#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
-#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
-#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
-#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
/*
@@ -329,6 +323,7 @@
#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */
#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */
+#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
/* Extended desc bits for Linksec and timesync */
/* Transmit Control */
diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c
index e18747c70bec..97f0049a5d6b 100644
--- a/drivers/net/igb/e1000_mac.c
+++ b/drivers/net/igb/e1000_mac.c
@@ -50,13 +50,6 @@ void igb_remove_device(struct e1000_hw *hw)
kfree(hw->dev_spec);
}
-static void igb_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value)
-{
- struct igb_adapter *adapter = hw->back;
-
- pci_read_config_word(adapter->pdev, reg, value);
-}
-
static s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
{
struct igb_adapter *adapter = hw->back;
@@ -83,8 +76,8 @@ s32 igb_get_bus_info_pcie(struct e1000_hw *hw)
{
struct e1000_bus_info *bus = &hw->bus;
s32 ret_val;
- u32 status;
- u16 pcie_link_status, pci_header_type;
+ u32 reg;
+ u16 pcie_link_status;
bus->type = e1000_bus_type_pci_express;
bus->speed = e1000_bus_speed_2500;
@@ -99,14 +92,8 @@ s32 igb_get_bus_info_pcie(struct e1000_hw *hw)
PCIE_LINK_WIDTH_MASK) >>
PCIE_LINK_WIDTH_SHIFT);
- igb_read_pci_cfg(hw, PCI_HEADER_TYPE_REGISTER, &pci_header_type);
- if (pci_header_type & PCI_HEADER_TYPE_MULTIFUNC) {
- status = rd32(E1000_STATUS);
- bus->func = (status & E1000_STATUS_FUNC_MASK)
- >> E1000_STATUS_FUNC_SHIFT;
- } else {
- bus->func = 0;
- }
+ reg = rd32(E1000_STATUS);
+ bus->func = (reg & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT;
return 0;
}
@@ -229,8 +216,8 @@ void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
if (!hw->mac.disable_av)
rar_high |= E1000_RAH_AV;
- array_wr32(E1000_RA, (index << 1), rar_low);
- array_wr32(E1000_RA, ((index << 1) + 1), rar_high);
+ wr32(E1000_RAL(index), rar_low);
+ wr32(E1000_RAH(index), rar_high);
}
/**
diff --git a/drivers/net/igb/e1000_regs.h b/drivers/net/igb/e1000_regs.h
index 95523af26056..bdf5d839c4bf 100644
--- a/drivers/net/igb/e1000_regs.h
+++ b/drivers/net/igb/e1000_regs.h
@@ -221,6 +221,10 @@
#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */
#define E1000_RA 0x05400 /* Receive Address - RW Array */
#define E1000_RA2 0x054E0 /* 2nd half of receive address array - RW Array */
+#define E1000_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
+ (0x054E0 + ((_i - 16) * 8)))
+#define E1000_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
+ (0x054E4 + ((_i - 16) * 8)))
#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
#define E1000_VMD_CTL 0x0581C /* VMDq Control - RW */
#define E1000_WUC 0x05800 /* Wakeup Control - RW */
diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h
index 4ff6f0567f3f..5a27825cc48a 100644
--- a/drivers/net/igb/igb.h
+++ b/drivers/net/igb/igb.h
@@ -43,8 +43,6 @@ struct igb_adapter;
#endif
/* Interrupt defines */
-#define IGB_MAX_TX_CLEAN 72
-
#define IGB_MIN_DYN_ITR 3000
#define IGB_MAX_DYN_ITR 96000
@@ -127,7 +125,8 @@ struct igb_buffer {
/* TX */
struct {
unsigned long time_stamp;
- u32 length;
+ u16 length;
+ u16 next_to_watch;
};
/* RX */
struct {
@@ -160,7 +159,8 @@ struct igb_ring {
u16 itr_register;
u16 cpu;
- int queue_index;
+ u16 queue_index;
+ u16 reg_idx;
unsigned int total_bytes;
unsigned int total_packets;
@@ -294,6 +294,8 @@ struct igb_adapter {
unsigned int lro_flushed;
unsigned int lro_no_desc;
#endif
+ unsigned int tx_ring_count;
+ unsigned int rx_ring_count;
};
#define IGB_FLAG_HAS_MSI (1 << 0)
@@ -325,7 +327,41 @@ extern void igb_reset(struct igb_adapter *);
extern int igb_set_spd_dplx(struct igb_adapter *, u16);
extern int igb_setup_tx_resources(struct igb_adapter *, struct igb_ring *);
extern int igb_setup_rx_resources(struct igb_adapter *, struct igb_ring *);
+extern void igb_free_tx_resources(struct igb_ring *);
+extern void igb_free_rx_resources(struct igb_ring *);
extern void igb_update_stats(struct igb_adapter *);
extern void igb_set_ethtool_ops(struct net_device *);
+static inline s32 igb_reset_phy(struct e1000_hw *hw)
+{
+ if (hw->phy.ops.reset_phy)
+ return hw->phy.ops.reset_phy(hw);
+
+ return 0;
+}
+
+static inline s32 igb_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ if (hw->phy.ops.read_phy_reg)
+ return hw->phy.ops.read_phy_reg(hw, offset, data);
+
+ return 0;
+}
+
+static inline s32 igb_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ if (hw->phy.ops.write_phy_reg)
+ return hw->phy.ops.write_phy_reg(hw, offset, data);
+
+ return 0;
+}
+
+static inline s32 igb_get_phy_info(struct e1000_hw *hw)
+{
+ if (hw->phy.ops.get_phy_info)
+ return hw->phy.ops.get_phy_info(hw);
+
+ return 0;
+}
+
#endif /* _IGB_H_ */
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c
index 89964fa739a0..3c831f1472ad 100644
--- a/drivers/net/igb/igb_ethtool.c
+++ b/drivers/net/igb/igb_ethtool.c
@@ -101,8 +101,8 @@ static const struct igb_stats igb_gstrings_stats[] = {
};
#define IGB_QUEUE_STATS_LEN \
- ((((struct igb_adapter *)netdev->priv)->num_rx_queues + \
- ((struct igb_adapter *)netdev->priv)->num_tx_queues) * \
+ ((((struct igb_adapter *)netdev_priv(netdev))->num_rx_queues + \
+ ((struct igb_adapter *)netdev_priv(netdev))->num_tx_queues) * \
(sizeof(struct igb_queue_stats) / sizeof(u64)))
#define IGB_GLOBAL_STATS_LEN \
sizeof(igb_gstrings_stats) / sizeof(struct igb_stats)
@@ -494,8 +494,6 @@ static void igb_get_regs(struct net_device *netdev,
/* These should probably be added to e1000_regs.h instead */
#define E1000_PSRTYPE_REG(_i) (0x05480 + ((_i) * 4))
- #define E1000_RAL(_i) (0x05400 + ((_i) * 8))
- #define E1000_RAH(_i) (0x05404 + ((_i) * 8))
#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8))
#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4))
#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4))
@@ -714,15 +712,13 @@ static void igb_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct igb_adapter *adapter = netdev_priv(netdev);
- struct igb_ring *tx_ring = adapter->tx_ring;
- struct igb_ring *rx_ring = adapter->rx_ring;
ring->rx_max_pending = IGB_MAX_RXD;
ring->tx_max_pending = IGB_MAX_TXD;
ring->rx_mini_max_pending = 0;
ring->rx_jumbo_max_pending = 0;
- ring->rx_pending = rx_ring->count;
- ring->tx_pending = tx_ring->count;
+ ring->rx_pending = adapter->rx_ring_count;
+ ring->tx_pending = adapter->tx_ring_count;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
}
@@ -731,12 +727,9 @@ static int igb_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct igb_adapter *adapter = netdev_priv(netdev);
- struct igb_buffer *old_buf;
- struct igb_buffer *old_rx_buf;
- void *old_desc;
+ struct igb_ring *temp_ring;
int i, err;
- u32 new_rx_count, new_tx_count, old_size;
- dma_addr_t old_dma;
+ u32 new_rx_count, new_tx_count;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
@@ -749,12 +742,19 @@ static int igb_set_ringparam(struct net_device *netdev,
new_tx_count = min(new_tx_count, (u32)IGB_MAX_TXD);
new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
- if ((new_tx_count == adapter->tx_ring->count) &&
- (new_rx_count == adapter->rx_ring->count)) {
+ if ((new_tx_count == adapter->tx_ring_count) &&
+ (new_rx_count == adapter->rx_ring_count)) {
/* nothing to do */
return 0;
}
+ if (adapter->num_tx_queues > adapter->num_rx_queues)
+ temp_ring = vmalloc(adapter->num_tx_queues * sizeof(struct igb_ring));
+ else
+ temp_ring = vmalloc(adapter->num_rx_queues * sizeof(struct igb_ring));
+ if (!temp_ring)
+ return -ENOMEM;
+
while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
msleep(1);
@@ -766,62 +766,55 @@ static int igb_set_ringparam(struct net_device *netdev,
* because the ISRs in MSI-X mode get passed pointers
* to the tx and rx ring structs.
*/
- if (new_tx_count != adapter->tx_ring->count) {
+ if (new_tx_count != adapter->tx_ring_count) {
+ memcpy(temp_ring, adapter->tx_ring,
+ adapter->num_tx_queues * sizeof(struct igb_ring));
+
for (i = 0; i < adapter->num_tx_queues; i++) {
- /* Save existing descriptor ring */
- old_buf = adapter->tx_ring[i].buffer_info;
- old_desc = adapter->tx_ring[i].desc;
- old_size = adapter->tx_ring[i].size;
- old_dma = adapter->tx_ring[i].dma;
- /* Try to allocate a new one */
- adapter->tx_ring[i].buffer_info = NULL;
- adapter->tx_ring[i].desc = NULL;
- adapter->tx_ring[i].count = new_tx_count;
- err = igb_setup_tx_resources(adapter,
- &adapter->tx_ring[i]);
+ temp_ring[i].count = new_tx_count;
+ err = igb_setup_tx_resources(adapter, &temp_ring[i]);
if (err) {
- /* Restore the old one so at least
- the adapter still works, even if
- we failed the request */
- adapter->tx_ring[i].buffer_info = old_buf;
- adapter->tx_ring[i].desc = old_desc;
- adapter->tx_ring[i].size = old_size;
- adapter->tx_ring[i].dma = old_dma;
+ while (i) {
+ i--;
+ igb_free_tx_resources(&temp_ring[i]);
+ }
goto err_setup;
}
- /* Free the old buffer manually */
- vfree(old_buf);
- pci_free_consistent(adapter->pdev, old_size,
- old_desc, old_dma);
}
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ igb_free_tx_resources(&adapter->tx_ring[i]);
+
+ memcpy(adapter->tx_ring, temp_ring,
+ adapter->num_tx_queues * sizeof(struct igb_ring));
+
+ adapter->tx_ring_count = new_tx_count;
}
if (new_rx_count != adapter->rx_ring->count) {
- for (i = 0; i < adapter->num_rx_queues; i++) {
+ memcpy(temp_ring, adapter->rx_ring,
+ adapter->num_rx_queues * sizeof(struct igb_ring));
- old_rx_buf = adapter->rx_ring[i].buffer_info;
- old_desc = adapter->rx_ring[i].desc;
- old_size = adapter->rx_ring[i].size;
- old_dma = adapter->rx_ring[i].dma;
-
- adapter->rx_ring[i].buffer_info = NULL;
- adapter->rx_ring[i].desc = NULL;
- adapter->rx_ring[i].dma = 0;
- adapter->rx_ring[i].count = new_rx_count;
- err = igb_setup_rx_resources(adapter,
- &adapter->rx_ring[i]);
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ temp_ring[i].count = new_rx_count;
+ err = igb_setup_rx_resources(adapter, &temp_ring[i]);
if (err) {
- adapter->rx_ring[i].buffer_info = old_rx_buf;
- adapter->rx_ring[i].desc = old_desc;
- adapter->rx_ring[i].size = old_size;
- adapter->rx_ring[i].dma = old_dma;
+ while (i) {
+ i--;
+ igb_free_rx_resources(&temp_ring[i]);
+ }
goto err_setup;
}
- vfree(old_rx_buf);
- pci_free_consistent(adapter->pdev, old_size, old_desc,
- old_dma);
}
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ igb_free_rx_resources(&adapter->rx_ring[i]);
+
+ memcpy(adapter->rx_ring, temp_ring,
+ adapter->num_rx_queues * sizeof(struct igb_ring));
+
+ adapter->rx_ring_count = new_rx_count;
}
err = 0;
@@ -830,6 +823,7 @@ err_setup:
igb_up(adapter);
clear_bit(__IGB_RESETTING, &adapter->state);
+ vfree(temp_ring);
return err;
}
@@ -1343,8 +1337,9 @@ static int igb_setup_desc_rings(struct igb_adapter *adapter)
wr32(E1000_RDLEN(0), rx_ring->size);
wr32(E1000_RDH(0), 0);
wr32(E1000_RDT(0), 0);
+ rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 |
- E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+ E1000_RCTL_RDMTS_HALF |
(adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
wr32(E1000_RCTL, rctl);
wr32(E1000_SRRCTL(0), 0);
@@ -1380,10 +1375,10 @@ static void igb_phy_disable_receiver(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
/* Write out to PHY registers 29 and 30 to disable the Receiver. */
- hw->phy.ops.write_phy_reg(hw, 29, 0x001F);
- hw->phy.ops.write_phy_reg(hw, 30, 0x8FFC);
- hw->phy.ops.write_phy_reg(hw, 29, 0x001A);
- hw->phy.ops.write_phy_reg(hw, 30, 0x8FF0);
+ igb_write_phy_reg(hw, 29, 0x001F);
+ igb_write_phy_reg(hw, 30, 0x8FFC);
+ igb_write_phy_reg(hw, 29, 0x001A);
+ igb_write_phy_reg(hw, 30, 0x8FF0);
}
static int igb_integrated_phy_loopback(struct igb_adapter *adapter)
@@ -1396,17 +1391,17 @@ static int igb_integrated_phy_loopback(struct igb_adapter *adapter)
if (hw->phy.type == e1000_phy_m88) {
/* Auto-MDI/MDIX Off */
- hw->phy.ops.write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, 0x0808);
+ igb_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, 0x0808);
/* reset to update Auto-MDI/MDIX */
- hw->phy.ops.write_phy_reg(hw, PHY_CONTROL, 0x9140);
+ igb_write_phy_reg(hw, PHY_CONTROL, 0x9140);
/* autoneg off */
- hw->phy.ops.write_phy_reg(hw, PHY_CONTROL, 0x8140);
+ igb_write_phy_reg(hw, PHY_CONTROL, 0x8140);
}
ctrl_reg = rd32(E1000_CTRL);
/* force 1000, set loopback */
- hw->phy.ops.write_phy_reg(hw, PHY_CONTROL, 0x4140);
+ igb_write_phy_reg(hw, PHY_CONTROL, 0x4140);
/* Now set up the MAC to the same speed/duplex as the PHY. */
ctrl_reg = rd32(E1000_CTRL);
@@ -1500,10 +1495,10 @@ static void igb_loopback_cleanup(struct igb_adapter *adapter)
wr32(E1000_RCTL, rctl);
hw->mac.autoneg = true;
- hw->phy.ops.read_phy_reg(hw, PHY_CONTROL, &phy_reg);
+ igb_read_phy_reg(hw, PHY_CONTROL, &phy_reg);
if (phy_reg & MII_CR_LOOPBACK) {
phy_reg &= ~MII_CR_LOOPBACK;
- hw->phy.ops.write_phy_reg(hw, PHY_CONTROL, phy_reg);
+ igb_write_phy_reg(hw, PHY_CONTROL, phy_reg);
igb_phy_sw_reset(hw);
}
}
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c
index 20d27e622ec1..022794e579c7 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/igb/igb_main.c
@@ -42,6 +42,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
+#include <linux/aer.h>
#ifdef CONFIG_IGB_DCA
#include <linux/dca.h>
#endif
@@ -76,8 +77,6 @@ static int igb_setup_all_tx_resources(struct igb_adapter *);
static int igb_setup_all_rx_resources(struct igb_adapter *);
static void igb_free_all_tx_resources(struct igb_adapter *);
static void igb_free_all_rx_resources(struct igb_adapter *);
-static void igb_free_tx_resources(struct igb_ring *);
-static void igb_free_rx_resources(struct igb_ring *);
void igb_update_stats(struct igb_adapter *);
static int igb_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit igb_remove(struct pci_dev *pdev);
@@ -232,6 +231,40 @@ static void __exit igb_exit_module(void)
module_exit(igb_exit_module);
+#define Q_IDX_82576(i) (((i & 0x1) << 3) + (i >> 1))
+/**
+ * igb_cache_ring_register - Descriptor ring to register mapping
+ * @adapter: board private structure to initialize
+ *
+ * Once we know the feature-set enabled for the device, we'll cache
+ * the register offset the descriptor ring is assigned to.
+ **/
+static void igb_cache_ring_register(struct igb_adapter *adapter)
+{
+ int i;
+
+ switch (adapter->hw.mac.type) {
+ case e1000_82576:
+ /* The queues are allocated for virtualization such that VF 0
+ * is allocated queues 0 and 8, VF 1 queues 1 and 9, etc.
+ * In order to avoid collision we start at the first free queue
+ * and continue consuming queues in the same sequence
+ */
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].reg_idx = Q_IDX_82576(i);
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->tx_ring[i].reg_idx = Q_IDX_82576(i);
+ break;
+ case e1000_82575:
+ default:
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].reg_idx = i;
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->tx_ring[i].reg_idx = i;
+ break;
+ }
+}
+
/**
* igb_alloc_queues - Allocate memory for all rings
* @adapter: board private structure to initialize
@@ -259,11 +292,13 @@ static int igb_alloc_queues(struct igb_adapter *adapter)
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igb_ring *ring = &(adapter->tx_ring[i]);
+ ring->count = adapter->tx_ring_count;
ring->adapter = adapter;
ring->queue_index = i;
}
for (i = 0; i < adapter->num_rx_queues; i++) {
struct igb_ring *ring = &(adapter->rx_ring[i]);
+ ring->count = adapter->rx_ring_count;
ring->adapter = adapter;
ring->queue_index = i;
ring->itr_register = E1000_ITR;
@@ -271,6 +306,8 @@ static int igb_alloc_queues(struct igb_adapter *adapter)
/* set a default napi handler for each rx_ring */
netif_napi_add(adapter->netdev, &ring->napi, igb_poll, 64);
}
+
+ igb_cache_ring_register(adapter);
return 0;
}
@@ -311,36 +348,36 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
array_wr32(E1000_MSIXBM(0), msix_vector, msixbm);
break;
case e1000_82576:
- /* The 82576 uses a table-based method for assigning vectors.
+ /* 82576 uses a table-based method for assigning vectors.
Each queue has a single entry in the table to which we write
a vector number along with a "valid" bit. Sadly, the layout
of the table is somewhat counterintuitive. */
if (rx_queue > IGB_N0_QUEUE) {
- index = (rx_queue & 0x7);
+ index = (rx_queue >> 1);
ivar = array_rd32(E1000_IVAR0, index);
- if (rx_queue < 8) {
- /* vector goes into low byte of register */
- ivar = ivar & 0xFFFFFF00;
- ivar |= msix_vector | E1000_IVAR_VALID;
- } else {
+ if (rx_queue & 0x1) {
/* vector goes into third byte of register */
ivar = ivar & 0xFF00FFFF;
ivar |= (msix_vector | E1000_IVAR_VALID) << 16;
+ } else {
+ /* vector goes into low byte of register */
+ ivar = ivar & 0xFFFFFF00;
+ ivar |= msix_vector | E1000_IVAR_VALID;
}
adapter->rx_ring[rx_queue].eims_value= 1 << msix_vector;
array_wr32(E1000_IVAR0, index, ivar);
}
if (tx_queue > IGB_N0_QUEUE) {
- index = (tx_queue & 0x7);
+ index = (tx_queue >> 1);
ivar = array_rd32(E1000_IVAR0, index);
- if (tx_queue < 8) {
- /* vector goes into second byte of register */
- ivar = ivar & 0xFFFF00FF;
- ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
- } else {
+ if (tx_queue & 0x1) {
/* vector goes into high byte of register */
ivar = ivar & 0x00FFFFFF;
ivar |= (msix_vector | E1000_IVAR_VALID) << 24;
+ } else {
+ /* vector goes into second byte of register */
+ ivar = ivar & 0xFFFF00FF;
+ ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
}
adapter->tx_ring[tx_queue].eims_value= 1 << msix_vector;
array_wr32(E1000_IVAR0, index, ivar);
@@ -445,7 +482,7 @@ static int igb_request_msix(struct igb_adapter *adapter)
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igb_ring *ring = &(adapter->tx_ring[i]);
- sprintf(ring->name, "%s-tx%d", netdev->name, i);
+ sprintf(ring->name, "%s-tx-%d", netdev->name, i);
err = request_irq(adapter->msix_entries[vector].vector,
&igb_msix_tx, 0, ring->name,
&(adapter->tx_ring[i]));
@@ -458,7 +495,7 @@ static int igb_request_msix(struct igb_adapter *adapter)
for (i = 0; i < adapter->num_rx_queues; i++) {
struct igb_ring *ring = &(adapter->rx_ring[i]);
if (strlen(netdev->name) < (IFNAMSIZ - 5))
- sprintf(ring->name, "%s-rx%d", netdev->name, i);
+ sprintf(ring->name, "%s-rx-%d", netdev->name, i);
else
memcpy(ring->name, netdev->name, IFNAMSIZ);
err = request_irq(adapter->msix_entries[vector].vector,
@@ -931,8 +968,7 @@ void igb_reset(struct igb_adapter *adapter)
wr32(E1000_VET, ETHERNET_IEEE_VLAN_TYPE);
igb_reset_adaptive(&adapter->hw);
- if (adapter->hw.phy.ops.get_phy_info)
- adapter->hw.phy.ops.get_phy_info(&adapter->hw);
+ igb_get_phy_info(&adapter->hw);
}
/**
@@ -950,6 +986,25 @@ static int igb_is_need_ioport(struct pci_dev *pdev)
}
}
+static const struct net_device_ops igb_netdev_ops = {
+ .ndo_open = igb_open,
+ .ndo_stop = igb_close,
+ .ndo_start_xmit = igb_xmit_frame_adv,
+ .ndo_get_stats = igb_get_stats,
+ .ndo_set_multicast_list = igb_set_multi,
+ .ndo_set_mac_address = igb_set_mac,
+ .ndo_change_mtu = igb_change_mtu,
+ .ndo_do_ioctl = igb_ioctl,
+ .ndo_tx_timeout = igb_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_vlan_rx_register = igb_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = igb_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = igb_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = igb_netpoll,
+#endif
+};
+
/**
* igb_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -1031,6 +1086,13 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (err)
goto err_pci_reg;
+ err = pci_enable_pcie_error_reporting(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed "
+ "0x%x\n", err);
+ /* non-fatal, continue */
+ }
+
pci_set_master(pdev);
pci_save_state(pdev);
@@ -1059,23 +1121,9 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (!adapter->hw.hw_addr)
goto err_ioremap;
- netdev->open = &igb_open;
- netdev->stop = &igb_close;
- netdev->get_stats = &igb_get_stats;
- netdev->set_multicast_list = &igb_set_multi;
- netdev->set_mac_address = &igb_set_mac;
- netdev->change_mtu = &igb_change_mtu;
- netdev->do_ioctl = &igb_ioctl;
+ netdev->netdev_ops = &igb_netdev_ops;
igb_set_ethtool_ops(netdev);
- netdev->tx_timeout = &igb_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
- netdev->vlan_rx_register = igb_vlan_rx_register;
- netdev->vlan_rx_add_vid = igb_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = igb_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = igb_netpoll;
-#endif
- netdev->hard_start_xmit = &igb_xmit_frame_adv;
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
@@ -1275,16 +1323,14 @@ static int __devinit igb_probe(struct pci_dev *pdev,
dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
/* print bus type/speed/width info */
- dev_info(&pdev->dev,
- "%s: (PCIe:%s:%s) %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n",
netdev->name,
((hw->bus.speed == e1000_bus_speed_2500)
? "2.5Gb/s" : "unknown"),
((hw->bus.width == e1000_bus_width_pcie_x4)
? "Width x4" : (hw->bus.width == e1000_bus_width_pcie_x1)
? "Width x1" : "unknown"),
- netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
- netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+ netdev->dev_addr);
igb_read_part_num(hw, &part_num);
dev_info(&pdev->dev, "%s: PBA No: %06x-%03x\n", netdev->name,
@@ -1302,7 +1348,7 @@ err_register:
igb_release_hw_control(adapter);
err_eeprom:
if (!igb_check_reset_block(hw))
- hw->phy.ops.reset_phy(hw);
+ igb_reset_phy(hw);
if (hw->flash_address)
iounmap(hw->flash_address);
@@ -1338,6 +1384,7 @@ static void __devexit igb_remove(struct pci_dev *pdev)
#ifdef CONFIG_IGB_DCA
struct e1000_hw *hw = &adapter->hw;
#endif
+ int err;
/* flush_scheduled work may reschedule our watchdog task, so
* explicitly disable watchdog tasks from being rescheduled */
@@ -1362,9 +1409,8 @@ static void __devexit igb_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
- if (adapter->hw.phy.ops.reset_phy &&
- !igb_check_reset_block(&adapter->hw))
- adapter->hw.phy.ops.reset_phy(&adapter->hw);
+ if (!igb_check_reset_block(&adapter->hw))
+ igb_reset_phy(&adapter->hw);
igb_remove_device(&adapter->hw);
igb_reset_interrupt_capability(adapter);
@@ -1378,6 +1424,11 @@ static void __devexit igb_remove(struct pci_dev *pdev)
free_netdev(netdev);
+ err = pci_disable_pcie_error_reporting(pdev);
+ if (err)
+ dev_err(&pdev->dev,
+ "pci_disable_pcie_error_reporting failed 0x%x\n", err);
+
pci_disable_device(pdev);
}
@@ -1397,6 +1448,8 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter)
pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word);
+ adapter->tx_ring_count = IGB_DEFAULT_TXD;
+ adapter->rx_ring_count = IGB_DEFAULT_RXD;
adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
adapter->rx_ps_hdr_size = 0; /* disable packet split */
adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
@@ -1558,8 +1611,7 @@ int igb_setup_tx_resources(struct igb_adapter *adapter,
memset(tx_ring->buffer_info, 0, size);
/* round up to nearest 4K */
- tx_ring->size = tx_ring->count * sizeof(struct e1000_tx_desc)
- + sizeof(u32);
+ tx_ring->size = tx_ring->count * sizeof(struct e1000_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size,
@@ -1618,43 +1670,37 @@ static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
**/
static void igb_configure_tx(struct igb_adapter *adapter)
{
- u64 tdba, tdwba;
+ u64 tdba;
struct e1000_hw *hw = &adapter->hw;
u32 tctl;
u32 txdctl, txctrl;
- int i;
+ int i, j;
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igb_ring *ring = &(adapter->tx_ring[i]);
-
- wr32(E1000_TDLEN(i),
+ j = ring->reg_idx;
+ wr32(E1000_TDLEN(j),
ring->count * sizeof(struct e1000_tx_desc));
tdba = ring->dma;
- wr32(E1000_TDBAL(i),
+ wr32(E1000_TDBAL(j),
tdba & 0x00000000ffffffffULL);
- wr32(E1000_TDBAH(i), tdba >> 32);
-
- tdwba = ring->dma + ring->count * sizeof(struct e1000_tx_desc);
- tdwba |= 1; /* enable head wb */
- wr32(E1000_TDWBAL(i),
- tdwba & 0x00000000ffffffffULL);
- wr32(E1000_TDWBAH(i), tdwba >> 32);
+ wr32(E1000_TDBAH(j), tdba >> 32);
- ring->head = E1000_TDH(i);
- ring->tail = E1000_TDT(i);
+ ring->head = E1000_TDH(j);
+ ring->tail = E1000_TDT(j);
writel(0, hw->hw_addr + ring->tail);
writel(0, hw->hw_addr + ring->head);
- txdctl = rd32(E1000_TXDCTL(i));
+ txdctl = rd32(E1000_TXDCTL(j));
txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
- wr32(E1000_TXDCTL(i), txdctl);
+ wr32(E1000_TXDCTL(j), txdctl);
/* Turn off Relaxed Ordering on head write-backs. The
* writebacks MUST be delivered in order or it will
* completely screw up our bookeeping.
*/
- txctrl = rd32(E1000_DCA_TXCTRL(i));
+ txctrl = rd32(E1000_DCA_TXCTRL(j));
txctrl &= ~E1000_DCA_TXCTRL_TX_WB_RO_EN;
- wr32(E1000_DCA_TXCTRL(i), txctrl);
+ wr32(E1000_DCA_TXCTRL(j), txctrl);
}
@@ -1771,14 +1817,14 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 rctl;
u32 srrctl = 0;
- int i;
+ int i, j;
rctl = rd32(E1000_RCTL);
rctl &= ~(3 << E1000_RCTL_MO_SHIFT);
+ rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
- rctl |= E1000_RCTL_EN | E1000_RCTL_BAM |
- E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF |
+ rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF |
(adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
/*
@@ -1788,38 +1834,26 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
*/
rctl |= E1000_RCTL_SECRC;
- rctl &= ~E1000_RCTL_SBP;
+ /*
+ * disable store bad packets, long packet enable, and clear size bits.
+ */
+ rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_LPE | E1000_RCTL_SZ_256);
- if (adapter->netdev->mtu <= ETH_DATA_LEN)
- rctl &= ~E1000_RCTL_LPE;
- else
+ if (adapter->netdev->mtu > ETH_DATA_LEN)
rctl |= E1000_RCTL_LPE;
- if (adapter->rx_buffer_len <= IGB_RXBUFFER_2048) {
- /* Setup buffer sizes */
- rctl &= ~E1000_RCTL_SZ_4096;
- rctl |= E1000_RCTL_BSEX;
- switch (adapter->rx_buffer_len) {
- case IGB_RXBUFFER_256:
- rctl |= E1000_RCTL_SZ_256;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- case IGB_RXBUFFER_512:
- rctl |= E1000_RCTL_SZ_512;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- case IGB_RXBUFFER_1024:
- rctl |= E1000_RCTL_SZ_1024;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- case IGB_RXBUFFER_2048:
- default:
- rctl |= E1000_RCTL_SZ_2048;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- }
- } else {
- rctl &= ~E1000_RCTL_BSEX;
- srrctl = adapter->rx_buffer_len >> E1000_SRRCTL_BSIZEPKT_SHIFT;
+
+ /* Setup buffer sizes */
+ switch (adapter->rx_buffer_len) {
+ case IGB_RXBUFFER_256:
+ rctl |= E1000_RCTL_SZ_256;
+ break;
+ case IGB_RXBUFFER_512:
+ rctl |= E1000_RCTL_SZ_512;
+ break;
+ default:
+ srrctl = ALIGN(adapter->rx_buffer_len, 1024)
+ >> E1000_SRRCTL_BSIZEPKT_SHIFT;
+ break;
}
/* 82575 and greater support packet-split where the protocol
@@ -1841,8 +1875,10 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF;
}
- for (i = 0; i < adapter->num_rx_queues; i++)
- wr32(E1000_SRRCTL(i), srrctl);
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ j = adapter->rx_ring[i].reg_idx;
+ wr32(E1000_SRRCTL(j), srrctl);
+ }
wr32(E1000_RCTL, rctl);
}
@@ -1859,7 +1895,7 @@ static void igb_configure_rx(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 rctl, rxcsum;
u32 rxdctl;
- int i;
+ int i, j;
/* disable receives while setting up the descriptors */
rctl = rd32(E1000_RCTL);
@@ -1874,25 +1910,26 @@ static void igb_configure_rx(struct igb_adapter *adapter)
* the Base and Length of the Rx Descriptor Ring */
for (i = 0; i < adapter->num_rx_queues; i++) {
struct igb_ring *ring = &(adapter->rx_ring[i]);
+ j = ring->reg_idx;
rdba = ring->dma;
- wr32(E1000_RDBAL(i),
+ wr32(E1000_RDBAL(j),
rdba & 0x00000000ffffffffULL);
- wr32(E1000_RDBAH(i), rdba >> 32);
- wr32(E1000_RDLEN(i),
+ wr32(E1000_RDBAH(j), rdba >> 32);
+ wr32(E1000_RDLEN(j),
ring->count * sizeof(union e1000_adv_rx_desc));
- ring->head = E1000_RDH(i);
- ring->tail = E1000_RDT(i);
+ ring->head = E1000_RDH(j);
+ ring->tail = E1000_RDT(j);
writel(0, hw->hw_addr + ring->tail);
writel(0, hw->hw_addr + ring->head);
- rxdctl = rd32(E1000_RXDCTL(i));
+ rxdctl = rd32(E1000_RXDCTL(j));
rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
rxdctl &= 0xFFF00000;
rxdctl |= IGB_RX_PTHRESH;
rxdctl |= IGB_RX_HTHRESH << 8;
rxdctl |= IGB_RX_WTHRESH << 16;
- wr32(E1000_RXDCTL(i), rxdctl);
+ wr32(E1000_RXDCTL(j), rxdctl);
#ifdef CONFIG_IGB_LRO
/* Intitial LRO Settings */
ring->lro_mgr.max_aggr = MAX_LRO_AGGR;
@@ -1922,7 +1959,7 @@ static void igb_configure_rx(struct igb_adapter *adapter)
shift = 6;
for (j = 0; j < (32 * 4); j++) {
reta.bytes[j & 3] =
- (j % adapter->num_rx_queues) << shift;
+ adapter->rx_ring[(j % adapter->num_rx_queues)].reg_idx << shift;
if ((j & 3) == 3)
writel(reta.dword,
hw->hw_addr + E1000_RETA(0) + (j & ~3));
@@ -1984,7 +2021,7 @@ static void igb_configure_rx(struct igb_adapter *adapter)
*
* Free all transmit software resources
**/
-static void igb_free_tx_resources(struct igb_ring *tx_ring)
+void igb_free_tx_resources(struct igb_ring *tx_ring)
{
struct pci_dev *pdev = tx_ring->adapter->pdev;
@@ -2082,7 +2119,7 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter)
*
* Free all receive software resources
**/
-static void igb_free_rx_resources(struct igb_ring *rx_ring)
+void igb_free_rx_resources(struct igb_ring *rx_ring)
{
struct pci_dev *pdev = rx_ring->adapter->pdev;
@@ -2274,8 +2311,7 @@ static void igb_set_multi(struct net_device *netdev)
static void igb_update_phy_info(unsigned long data)
{
struct igb_adapter *adapter = (struct igb_adapter *) data;
- if (adapter->hw.phy.ops.get_phy_info)
- adapter->hw.phy.ops.get_phy_info(&adapter->hw);
+ igb_get_phy_info(&adapter->hw);
}
/**
@@ -2330,9 +2366,10 @@ static void igb_watchdog_task(struct work_struct *work)
&adapter->link_duplex);
ctrl = rd32(E1000_CTRL);
- dev_info(&adapter->pdev->dev,
- "NIC Link is Up %d Mbps %s, "
+ /* Links status message must follow this format */
+ printk(KERN_INFO "igb: %s NIC Link is Up %d Mbps %s, "
"Flow Control: %s\n",
+ netdev->name,
adapter->link_speed,
adapter->link_duplex == FULL_DUPLEX ?
"Full Duplex" : "Half Duplex",
@@ -2367,7 +2404,9 @@ static void igb_watchdog_task(struct work_struct *work)
if (netif_carrier_ok(netdev)) {
adapter->link_speed = 0;
adapter->link_duplex = 0;
- dev_info(&adapter->pdev->dev, "NIC Link is Down\n");
+ /* Links status message must follow this format */
+ printk(KERN_INFO "igb: %s NIC Link is Down\n",
+ netdev->name);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
if (!test_bit(__IGB_DOWN, &adapter->state))
@@ -2703,6 +2742,7 @@ static inline int igb_tso_adv(struct igb_adapter *adapter,
context_desc->seqnum_seed = 0;
buffer_info->time_stamp = jiffies;
+ buffer_info->next_to_watch = i;
buffer_info->dma = 0;
i++;
if (i == tx_ring->count)
@@ -2766,6 +2806,7 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
cpu_to_le32(tx_ring->queue_index << 4);
buffer_info->time_stamp = jiffies;
+ buffer_info->next_to_watch = i;
buffer_info->dma = 0;
i++;
@@ -2784,8 +2825,8 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
#define IGB_MAX_DATA_PER_TXD (1<<IGB_MAX_TXD_PWR)
static inline int igb_tx_map_adv(struct igb_adapter *adapter,
- struct igb_ring *tx_ring,
- struct sk_buff *skb)
+ struct igb_ring *tx_ring, struct sk_buff *skb,
+ unsigned int first)
{
struct igb_buffer *buffer_info;
unsigned int len = skb_headlen(skb);
@@ -2799,6 +2840,7 @@ static inline int igb_tx_map_adv(struct igb_adapter *adapter,
buffer_info->length = len;
/* set time_stamp *before* dma to help avoid a possible race */
buffer_info->time_stamp = jiffies;
+ buffer_info->next_to_watch = i;
buffer_info->dma = pci_map_single(adapter->pdev, skb->data, len,
PCI_DMA_TODEVICE);
count++;
@@ -2816,6 +2858,7 @@ static inline int igb_tx_map_adv(struct igb_adapter *adapter,
BUG_ON(len >= IGB_MAX_DATA_PER_TXD);
buffer_info->length = len;
buffer_info->time_stamp = jiffies;
+ buffer_info->next_to_watch = i;
buffer_info->dma = pci_map_page(adapter->pdev,
frag->page,
frag->page_offset,
@@ -2828,8 +2871,9 @@ static inline int igb_tx_map_adv(struct igb_adapter *adapter,
i = 0;
}
- i = (i == 0) ? tx_ring->count - 1 : i - 1;
+ i = ((i == 0) ? tx_ring->count - 1 : i - 1);
tx_ring->buffer_info[i].skb = skb;
+ tx_ring->buffer_info[first].next_to_watch = i;
return count;
}
@@ -2936,6 +2980,7 @@ static int igb_xmit_frame_ring_adv(struct sk_buff *skb,
struct igb_ring *tx_ring)
{
struct igb_adapter *adapter = netdev_priv(netdev);
+ unsigned int first;
unsigned int tx_flags = 0;
unsigned int len;
u8 hdr_len = 0;
@@ -2972,6 +3017,8 @@ static int igb_xmit_frame_ring_adv(struct sk_buff *skb,
if (skb->protocol == htons(ETH_P_IP))
tx_flags |= IGB_TX_FLAGS_IPV4;
+ first = tx_ring->next_to_use;
+
tso = skb_is_gso(skb) ? igb_tso_adv(adapter, tx_ring, skb, tx_flags,
&hdr_len) : 0;
@@ -2987,7 +3034,7 @@ static int igb_xmit_frame_ring_adv(struct sk_buff *skb,
tx_flags |= IGB_TX_FLAGS_CSUM;
igb_tx_queue_adv(adapter, tx_ring, tx_flags,
- igb_tx_map_adv(adapter, tx_ring, skb),
+ igb_tx_map_adv(adapter, tx_ring, skb, first),
skb->len, hdr_len);
netdev->trans_start = jiffies;
@@ -3249,7 +3296,7 @@ void igb_update_stats(struct igb_adapter *adapter)
/* Phy Stats */
if (hw->phy.media_type == e1000_media_type_copper) {
if ((adapter->link_speed == SPEED_1000) &&
- (!hw->phy.ops.read_phy_reg(hw, PHY_1000T_STATUS,
+ (!igb_read_phy_reg(hw, PHY_1000T_STATUS,
&phy_tmp))) {
phy_tmp &= PHY_IDLE_ERROR_COUNT_MASK;
adapter->phy_stats.idle_errors += phy_tmp;
@@ -3332,7 +3379,6 @@ static void igb_write_itr(struct igb_ring *ring)
static irqreturn_t igb_msix_rx(int irq, void *data)
{
struct igb_ring *rx_ring = data;
- struct igb_adapter *adapter = rx_ring->adapter;
/* Write the ITR value calculated at the end of the
* previous interrupt.
@@ -3340,11 +3386,11 @@ static irqreturn_t igb_msix_rx(int irq, void *data)
igb_write_itr(rx_ring);
- if (netif_rx_schedule_prep(adapter->netdev, &rx_ring->napi))
- __netif_rx_schedule(adapter->netdev, &rx_ring->napi);
+ if (netif_rx_schedule_prep(&rx_ring->napi))
+ __netif_rx_schedule(&rx_ring->napi);
#ifdef CONFIG_IGB_DCA
- if (adapter->flags & IGB_FLAG_DCA_ENABLED)
+ if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED)
igb_update_rx_dca(rx_ring);
#endif
return IRQ_HANDLED;
@@ -3357,7 +3403,7 @@ static void igb_update_rx_dca(struct igb_ring *rx_ring)
struct igb_adapter *adapter = rx_ring->adapter;
struct e1000_hw *hw = &adapter->hw;
int cpu = get_cpu();
- int q = rx_ring - adapter->rx_ring;
+ int q = rx_ring->reg_idx;
if (rx_ring->cpu != cpu) {
dca_rxctrl = rd32(E1000_DCA_RXCTRL(q));
@@ -3384,7 +3430,7 @@ static void igb_update_tx_dca(struct igb_ring *tx_ring)
struct igb_adapter *adapter = tx_ring->adapter;
struct e1000_hw *hw = &adapter->hw;
int cpu = get_cpu();
- int q = tx_ring - adapter->tx_ring;
+ int q = tx_ring->reg_idx;
if (tx_ring->cpu != cpu) {
dca_txctrl = rd32(E1000_DCA_TXCTRL(q));
@@ -3493,7 +3539,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- netif_rx_schedule(netdev, &adapter->rx_ring[0].napi);
+ netif_rx_schedule(&adapter->rx_ring[0].napi);
return IRQ_HANDLED;
}
@@ -3531,7 +3577,7 @@ static irqreturn_t igb_intr(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- netif_rx_schedule(netdev, &adapter->rx_ring[0].napi);
+ netif_rx_schedule(&adapter->rx_ring[0].napi);
return IRQ_HANDLED;
}
@@ -3566,7 +3612,7 @@ static int igb_poll(struct napi_struct *napi, int budget)
!netif_running(netdev)) {
if (adapter->itr_setting & 3)
igb_set_itr(adapter);
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
if (!test_bit(__IGB_DOWN, &adapter->state))
igb_irq_enable(adapter);
return 0;
@@ -3592,7 +3638,7 @@ static int igb_clean_rx_ring_msix(struct napi_struct *napi, int budget)
/* If not enough Rx work done, exit the polling mode */
if ((work_done == 0) || !netif_running(netdev)) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
if (adapter->itr_setting & 3) {
if (adapter->num_rx_queues == 1)
@@ -3610,12 +3656,6 @@ static int igb_clean_rx_ring_msix(struct napi_struct *napi, int budget)
return 1;
}
-static inline u32 get_head(struct igb_ring *tx_ring)
-{
- void *end = (struct e1000_tx_desc *)tx_ring->desc + tx_ring->count;
- return le32_to_cpu(*(volatile __le32 *)end);
-}
-
/**
* igb_clean_tx_irq - Reclaim resources after transmit completes
* @adapter: board private structure
@@ -3624,24 +3664,25 @@ static inline u32 get_head(struct igb_ring *tx_ring)
static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
{
struct igb_adapter *adapter = tx_ring->adapter;
- struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
- struct e1000_tx_desc *tx_desc;
+ struct e1000_hw *hw = &adapter->hw;
struct igb_buffer *buffer_info;
struct sk_buff *skb;
- unsigned int i;
- u32 head, oldhead;
- unsigned int count = 0;
+ union e1000_adv_tx_desc *tx_desc, *eop_desc;
unsigned int total_bytes = 0, total_packets = 0;
- bool retval = true;
+ unsigned int i, eop, count = 0;
+ bool cleaned = false;
- rmb();
- head = get_head(tx_ring);
i = tx_ring->next_to_clean;
- while (1) {
- while (i != head) {
- tx_desc = E1000_TX_DESC(*tx_ring, i);
+ eop = tx_ring->buffer_info[i].next_to_watch;
+ eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop);
+
+ while ((eop_desc->wb.status & cpu_to_le32(E1000_TXD_STAT_DD)) &&
+ (count < tx_ring->count)) {
+ for (cleaned = false; !cleaned; count++) {
+ tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
buffer_info = &tx_ring->buffer_info[i];
+ cleaned = (i == eop);
skb = buffer_info->skb;
if (skb) {
@@ -3656,25 +3697,17 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
}
igb_unmap_and_free_tx_resource(adapter, buffer_info);
+ tx_desc->wb.status = 0;
i++;
if (i == tx_ring->count)
i = 0;
-
- count++;
- if (count == IGB_MAX_TX_CLEAN) {
- retval = false;
- goto done_cleaning;
- }
}
- oldhead = head;
- rmb();
- head = get_head(tx_ring);
- if (head == oldhead)
- goto done_cleaning;
- } /* while (1) */
-
-done_cleaning:
+
+ eop = tx_ring->buffer_info[i].next_to_watch;
+ eop_desc = E1000_TX_DESC_ADV(*tx_ring, eop);
+ }
+
tx_ring->next_to_clean = i;
if (unlikely(count &&
@@ -3701,7 +3734,6 @@ done_cleaning:
&& !(rd32(E1000_STATUS) &
E1000_STATUS_TXOFF)) {
- tx_desc = E1000_TX_DESC(*tx_ring, i);
/* detected Tx unit hang */
dev_err(&adapter->pdev->dev,
"Detected Tx Unit Hang\n"
@@ -3710,9 +3742,9 @@ done_cleaning:
" TDT <%x>\n"
" next_to_use <%x>\n"
" next_to_clean <%x>\n"
- " head (WB) <%x>\n"
"buffer_info[next_to_clean]\n"
" time_stamp <%lx>\n"
+ " next_to_watch <%x>\n"
" jiffies <%lx>\n"
" desc.status <%x>\n",
tx_ring->queue_index,
@@ -3720,10 +3752,10 @@ done_cleaning:
readl(adapter->hw.hw_addr + tx_ring->tail),
tx_ring->next_to_use,
tx_ring->next_to_clean,
- head,
tx_ring->buffer_info[i].time_stamp,
+ eop,
jiffies,
- tx_desc->upper.fields.status);
+ eop_desc->wb.status);
netif_stop_subqueue(netdev, tx_ring->queue_index);
}
}
@@ -3733,7 +3765,7 @@ done_cleaning:
tx_ring->tx_stats.packets += total_packets;
adapter->net_stats.tx_bytes += total_bytes;
adapter->net_stats.tx_packets += total_packets;
- return retval;
+ return (count < tx_ring->count);
}
#ifdef CONFIG_IGB_LRO
@@ -3919,8 +3951,10 @@ send_up:
next_buffer = &rx_ring->buffer_info[i];
if (!(staterr & E1000_RXD_STAT_EOP)) {
- buffer_info->skb = xchg(&next_buffer->skb, skb);
- buffer_info->dma = xchg(&next_buffer->dma, 0);
+ buffer_info->skb = next_buffer->skb;
+ buffer_info->dma = next_buffer->dma;
+ next_buffer->skb = skb;
+ next_buffer->dma = 0;
goto next_desc;
}
@@ -3938,8 +3972,6 @@ send_up:
igb_receive_skb(rx_ring, staterr, rx_desc, skb);
- netdev->last_rx = jiffies;
-
next_desc:
rx_desc->wb.upper.status_error = 0;
@@ -4102,9 +4134,8 @@ static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
case SIOCGMIIREG:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (adapter->hw.phy.ops.read_phy_reg(&adapter->hw,
- data->reg_num
- & 0x1F, &data->val_out))
+ if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F,
+ &data->val_out))
return -EIO;
break;
case SIOCSMIIREG:
@@ -4474,27 +4505,38 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
struct net_device *netdev = pci_get_drvdata(pdev);
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ pci_ers_result_t result;
int err;
if (adapter->need_ioport)
err = pci_enable_device(pdev);
else
err = pci_enable_device_mem(pdev);
+
if (err) {
dev_err(&pdev->dev,
"Cannot re-enable PCI device after reset.\n");
- return PCI_ERS_RESULT_DISCONNECT;
- }
- pci_set_master(pdev);
- pci_restore_state(pdev);
+ result = PCI_ERS_RESULT_DISCONNECT;
+ } else {
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D3hot, 0);
- pci_enable_wake(pdev, PCI_D3cold, 0);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
- igb_reset(adapter);
- wr32(E1000_WUS, ~0);
+ igb_reset(adapter);
+ wr32(E1000_WUS, ~0);
+ result = PCI_ERS_RESULT_RECOVERED;
+ }
+
+ err = pci_cleanup_aer_uncorrect_error_status(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_cleanup_aer_uncorrect_error_status "
+ "failed 0x%0x\n", err);
+ /* non-fatal, continue */
+ }
- return PCI_ERS_RESULT_RECOVERED;
+ return result;
}
/**
@@ -4522,7 +4564,6 @@ static void igb_io_resume(struct pci_dev *pdev)
/* let the f/w know that the h/w is now under the control of the
* driver. */
igb_get_hw_control(adapter);
-
}
/* igb_main.c */
diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c
index 1f25263dc7eb..170b12d1d70e 100644
--- a/drivers/net/ioc3-eth.c
+++ b/drivers/net/ioc3-eth.c
@@ -390,11 +390,8 @@ static int nic_init(struct ioc3 *ioc3)
}
printk("Found %s NIC", type);
- if (type != unknown) {
- printk (" registration number %02x:%02x:%02x:%02x:%02x:%02x,"
- " CRC %02x", serial[0], serial[1], serial[2],
- serial[3], serial[4], serial[5], crc);
- }
+ if (type != unknown)
+ printk (" registration number %pM, CRC %02x", serial, crc);
printk(".\n");
return 0;
@@ -443,12 +440,9 @@ static void ioc3_get_eaddr_nic(struct ioc3_private *ip)
*/
static void ioc3_get_eaddr(struct ioc3_private *ip)
{
- DECLARE_MAC_BUF(mac);
-
ioc3_get_eaddr_nic(ip);
- printk("Ethernet address is %s.\n",
- print_mac(mac, priv_netdev(ip)->dev_addr));
+ printk("Ethernet address is %pM.\n", priv_netdev(ip)->dev_addr);
}
static void __ioc3_set_mac_address(struct net_device *dev)
@@ -627,7 +621,6 @@ static inline void ioc3_rx(struct ioc3_private *ip)
rxb = (struct ioc3_erxbuf *) new_skb->data;
skb_reserve(new_skb, RX_OFFSET);
- priv_netdev(ip)->last_rx = jiffies;
ip->stats.rx_packets++; /* Statistics */
ip->stats.rx_bytes += len;
} else {
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 059369885be1..7b6d435a8468 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -1222,7 +1222,6 @@ static void ipg_nic_rx_with_start_and_end(struct net_device *dev,
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
- dev->last_rx = jiffies;
sp->rx_buff[entry] = NULL;
}
@@ -1256,7 +1255,6 @@ static void ipg_nic_rx_with_start(struct net_device *dev,
jumbo->skb = skb;
sp->rx_buff[entry] = NULL;
- dev->last_rx = jiffies;
}
static void ipg_nic_rx_with_end(struct net_device *dev,
@@ -1292,7 +1290,6 @@ static void ipg_nic_rx_with_end(struct net_device *dev,
}
}
- dev->last_rx = jiffies;
jumbo->found_start = 0;
jumbo->current_size = 0;
jumbo->skb = NULL;
@@ -1325,7 +1322,6 @@ static void ipg_nic_rx_no_start_no_end(struct net_device *dev,
skb->data, sp->rxfrag_size);
}
}
- dev->last_rx = jiffies;
ipg_nic_rx_free_skb(dev);
}
} else {
@@ -1494,11 +1490,6 @@ static int ipg_nic_rx(struct net_device *dev)
* when processing completes.
*/
netif_rx(skb);
-
- /* Record frame receive time (jiffies = Linux
- * kernel current time stamp).
- */
- dev->last_rx = jiffies;
}
/* Assure RX buffer is not reused by IPG. */
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index 2ff181861d2d..3c58e67ef1e4 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -292,7 +292,7 @@ static int ali_ircc_open(int i, chipio_t *info)
return -ENOMEM;
}
- self = dev->priv;
+ self = netdev_priv(dev);
self->netdev = dev;
spin_lock_init(&self->lock);
@@ -665,7 +665,7 @@ static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id)
IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __func__);
- self = dev->priv;
+ self = netdev_priv(dev);
spin_lock(&self->lock);
@@ -1333,7 +1333,7 @@ static int ali_ircc_net_open(struct net_device *dev)
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct ali_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
@@ -1396,7 +1396,7 @@ static int ali_ircc_net_close(struct net_device *dev)
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct ali_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
/* Stop device */
@@ -1436,7 +1436,7 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __func__ );
- self = (struct ali_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
iobase = self->io.fir_base;
netif_stop_queue(dev);
@@ -1931,7 +1931,6 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self)
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
- self->netdev->last_rx = jiffies;
}
}
@@ -1960,7 +1959,7 @@ static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
IRDA_ASSERT(dev != NULL, return 0;);
- self = (struct ali_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
iobase = self->io.sir_base;
@@ -2028,7 +2027,7 @@ static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
IRDA_ASSERT(dev != NULL, return -1;);
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return -1;);
@@ -2114,7 +2113,7 @@ static int ali_ircc_is_receiving(struct ali_ircc_cb *self)
static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev)
{
- struct ali_ircc_cb *self = (struct ali_ircc_cb *) dev->priv;
+ struct ali_ircc_cb *self = netdev_priv(dev);
IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __func__ );
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index a1e4508717c8..6c4b53ffbcac 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -620,7 +620,6 @@ static int au1k_irda_rx(struct net_device *dev)
/* next descriptor */
prxd = aup->rx_ring[aup->rx_head];
flags = prxd->flags;
- dev->last_rx = jiffies;
}
return 0;
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 69d16b30323b..687c2d53d4d2 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -979,7 +979,7 @@ toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev)
unsigned long flags;
struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
- self = (struct toshoboe_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT (self != NULL, return 0; );
@@ -1384,7 +1384,7 @@ toshoboe_net_close (struct net_device *dev)
IRDA_DEBUG (4, "%s()\n", __func__);
IRDA_ASSERT (dev != NULL, return -1; );
- self = (struct toshoboe_cb *) dev->priv;
+ self = netdev_priv(dev);
/* Stop device */
netif_stop_queue(dev);
@@ -1422,7 +1422,7 @@ toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
IRDA_ASSERT (dev != NULL, return -1; );
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT (self != NULL, return -1; );
@@ -1546,7 +1546,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
return -ENOMEM;
}
- self = dev->priv;
+ self = netdev_priv(dev);
self->netdev = dev;
self->pdev = pci_dev;
self->base = pci_resource_start(pci_dev,0);
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index b5d6b9ac162a..205e4e825a97 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -384,7 +384,7 @@ static void speed_bulk_callback(struct urb *urb)
*/
static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct irda_usb_cb *self = netdev->priv;
+ struct irda_usb_cb *self = netdev_priv(netdev);
struct urb *urb = self->tx_urb;
unsigned long flags;
s32 speed;
@@ -628,7 +628,7 @@ static void write_bulk_callback(struct urb *urb)
static void irda_usb_net_timeout(struct net_device *netdev)
{
unsigned long flags;
- struct irda_usb_cb *self = netdev->priv;
+ struct irda_usb_cb *self = netdev_priv(netdev);
struct urb *urb;
int done = 0; /* If we have made any progress */
@@ -929,7 +929,6 @@ static void irda_usb_receive(struct urb *urb)
/* Keep stats up to date */
self->stats.rx_bytes += len;
self->stats.rx_packets++;
- self->netdev->last_rx = jiffies;
done:
/* Note : at this point, the URB we've just received (urb)
@@ -1175,7 +1174,7 @@ static int irda_usb_net_open(struct net_device *netdev)
IRDA_DEBUG(1, "%s()\n", __func__);
IRDA_ASSERT(netdev != NULL, return -1;);
- self = (struct irda_usb_cb *) netdev->priv;
+ self = netdev_priv(netdev);
IRDA_ASSERT(self != NULL, return -1;);
spin_lock_irqsave(&self->lock, flags);
@@ -1257,7 +1256,7 @@ static int irda_usb_net_close(struct net_device *netdev)
IRDA_DEBUG(1, "%s()\n", __func__);
IRDA_ASSERT(netdev != NULL, return -1;);
- self = (struct irda_usb_cb *) netdev->priv;
+ self = netdev_priv(netdev);
IRDA_ASSERT(self != NULL, return -1;);
/* Clear this flag *before* unlinking the urbs and *before*
@@ -1306,7 +1305,7 @@ static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
int ret = 0;
IRDA_ASSERT(dev != NULL, return -1;);
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __func__, dev->name, cmd);
@@ -1348,7 +1347,7 @@ static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
*/
static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev)
{
- struct irda_usb_cb *self = dev->priv;
+ struct irda_usb_cb *self = netdev_priv(dev);
return &self->stats;
}
@@ -1641,7 +1640,7 @@ static int irda_usb_probe(struct usb_interface *intf,
goto err_out;
SET_NETDEV_DEV(net, &intf->dev);
- self = net->priv;
+ self = netdev_priv(net);
self->netdev = net;
spin_lock_init(&self->lock);
init_timer(&self->rx_defer_timer);
diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c
index 6bcee01c684c..d53aa9582137 100644
--- a/drivers/net/irda/irtty-sir.c
+++ b/drivers/net/irda/irtty-sir.c
@@ -191,7 +191,7 @@ static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t
tty = priv->tty;
if (!tty->ops->write)
return 0;
- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
writelen = tty_write_room(tty);
if (writelen > len)
writelen = len;
@@ -263,8 +263,7 @@ static void irtty_write_wakeup(struct tty_struct *tty)
IRDA_ASSERT(priv != NULL, return;);
IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;);
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (priv->dev)
sirdev_write_complete(priv->dev);
}
@@ -522,7 +521,7 @@ static void irtty_close(struct tty_struct *tty)
/* Stop tty */
irtty_stop_receiver(tty, TRUE);
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (tty->ops->stop)
tty->ops->stop(tty);
diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c
index e1429fc6d050..c747c874d44d 100644
--- a/drivers/net/irda/kingsun-sir.c
+++ b/drivers/net/irda/kingsun-sir.c
@@ -235,7 +235,6 @@ static void kingsun_rcv_irq(struct urb *urb)
&kingsun->stats,
&kingsun->rx_buff, bytes[i]);
}
- kingsun->netdev->last_rx = jiffies;
do_gettimeofday(&kingsun->rx_time);
kingsun->receiving =
(kingsun->rx_buff.state != OUTSIDE_FRAME)
diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c
index 2e67ae015d91..600d96f9cdb7 100644
--- a/drivers/net/irda/ks959-sir.c
+++ b/drivers/net/irda/ks959-sir.c
@@ -474,7 +474,6 @@ static void ks959_rcv_irq(struct urb *urb)
bytes[i]);
}
}
- kingsun->netdev->last_rx = jiffies;
do_gettimeofday(&kingsun->rx_time);
kingsun->receiving =
(kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c
index 3843b5faba8b..0e7f89337b25 100644
--- a/drivers/net/irda/ksdazzle-sir.c
+++ b/drivers/net/irda/ksdazzle-sir.c
@@ -371,7 +371,6 @@ static void ksdazzle_rcv_irq(struct urb *urb)
async_unwrap_char(kingsun->netdev, &kingsun->stats,
&kingsun->rx_unwrap_buff, bytes[i]);
}
- kingsun->netdev->last_rx = jiffies;
kingsun->receiving =
(kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0;
}
diff --git a/drivers/net/irda/ma600-sir.c b/drivers/net/irda/ma600-sir.c
index 1ceed9cfb7c4..e91216452379 100644
--- a/drivers/net/irda/ma600-sir.c
+++ b/drivers/net/irda/ma600-sir.c
@@ -236,7 +236,7 @@ static int ma600_change_speed(struct sir_dev *dev, unsigned speed)
* avoid the state machine complexity before we get things working
*/
-int ma600_reset(struct sir_dev *dev)
+static int ma600_reset(struct sir_dev *dev)
{
IRDA_DEBUG(2, "%s()\n", __func__);
diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c
index ad92d3ff1c40..904c9610c0dd 100644
--- a/drivers/net/irda/mcs7780.c
+++ b/drivers/net/irda/mcs7780.c
@@ -806,7 +806,6 @@ static void mcs_receive_irq(struct urb *urb)
mcs_unwrap_fir(mcs, urb->transfer_buffer,
urb->actual_length);
}
- mcs->netdev->last_rx = jiffies;
do_gettimeofday(&mcs->rx_time);
}
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 8583d951a6ad..2c6bf2d11bb1 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -373,7 +373,7 @@ static int __init nsc_ircc_open(chipio_t *info)
return -ENOMEM;
}
- self = dev->priv;
+ self = netdev_priv(dev);
self->netdev = dev;
spin_lock_init(&self->lock);
@@ -1354,7 +1354,7 @@ static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
__s32 speed;
__u8 bank;
- self = (struct nsc_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
@@ -1427,7 +1427,7 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
__u8 bank;
int mtt, diff;
- self = (struct nsc_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
iobase = self->io.fir_base;
netif_stop_queue(dev);
@@ -1896,7 +1896,6 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase)
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
- self->netdev->last_rx = jiffies;
}
}
/* Restore bank register */
@@ -2085,7 +2084,7 @@ static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id)
__u8 bsr, eir;
int iobase;
- self = dev->priv;
+ self = netdev_priv(dev);
spin_lock(&self->lock);
@@ -2166,7 +2165,7 @@ static int nsc_ircc_net_open(struct net_device *dev)
IRDA_DEBUG(4, "%s()\n", __func__);
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct nsc_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
@@ -2229,7 +2228,7 @@ static int nsc_ircc_net_close(struct net_device *dev)
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct nsc_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
/* Stop device */
@@ -2275,7 +2274,7 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
IRDA_ASSERT(dev != NULL, return -1;);
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return -1;);
@@ -2310,7 +2309,7 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev)
{
- struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv;
+ struct nsc_ircc_cb *self = netdev_priv(dev);
return &self->stats;
}
diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c
index c5b02b66f756..004a9aab3a50 100644
--- a/drivers/net/irda/pxaficp_ir.c
+++ b/drivers/net/irda/pxaficp_ir.c
@@ -22,9 +22,53 @@
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/irda.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-uart.h>
+
+#define FICP __REG(0x40800000) /* Start of FICP area */
+#define ICCR0 __REG(0x40800000) /* ICP Control Register 0 */
+#define ICCR1 __REG(0x40800004) /* ICP Control Register 1 */
+#define ICCR2 __REG(0x40800008) /* ICP Control Register 2 */
+#define ICDR __REG(0x4080000c) /* ICP Data Register */
+#define ICSR0 __REG(0x40800014) /* ICP Status Register 0 */
+#define ICSR1 __REG(0x40800018) /* ICP Status Register 1 */
+
+#define ICCR0_AME (1 << 7) /* Address match enable */
+#define ICCR0_TIE (1 << 6) /* Transmit FIFO interrupt enable */
+#define ICCR0_RIE (1 << 5) /* Recieve FIFO interrupt enable */
+#define ICCR0_RXE (1 << 4) /* Receive enable */
+#define ICCR0_TXE (1 << 3) /* Transmit enable */
+#define ICCR0_TUS (1 << 2) /* Transmit FIFO underrun select */
+#define ICCR0_LBM (1 << 1) /* Loopback mode */
+#define ICCR0_ITR (1 << 0) /* IrDA transmission */
+
+#define ICCR2_RXP (1 << 3) /* Receive Pin Polarity select */
+#define ICCR2_TXP (1 << 2) /* Transmit Pin Polarity select */
+#define ICCR2_TRIG (3 << 0) /* Receive FIFO Trigger threshold */
+#define ICCR2_TRIG_8 (0 << 0) /* >= 8 bytes */
+#define ICCR2_TRIG_16 (1 << 0) /* >= 16 bytes */
+#define ICCR2_TRIG_32 (2 << 0) /* >= 32 bytes */
+
+#ifdef CONFIG_PXA27x
+#define ICSR0_EOC (1 << 6) /* DMA End of Descriptor Chain */
+#endif
+#define ICSR0_FRE (1 << 5) /* Framing error */
+#define ICSR0_RFS (1 << 4) /* Receive FIFO service request */
+#define ICSR0_TFS (1 << 3) /* Transnit FIFO service request */
+#define ICSR0_RAB (1 << 2) /* Receiver abort */
+#define ICSR0_TUR (1 << 1) /* Trunsmit FIFO underun */
+#define ICSR0_EIF (1 << 0) /* End/Error in FIFO */
+
+#define ICSR1_ROR (1 << 6) /* Receiver FIFO underrun */
+#define ICSR1_CRE (1 << 5) /* CRC error */
+#define ICSR1_EOF (1 << 4) /* End of frame */
+#define ICSR1_TNF (1 << 3) /* Transmit FIFO not full */
+#define ICSR1_RNE (1 << 2) /* Receive FIFO not empty */
+#define ICSR1_TBY (1 << 1) /* Tramsmiter busy flag */
+#define ICSR1_RSY (1 << 0) /* Recevier synchronized flag */
#define IrSR_RXPL_NEG_IS_ZERO (1<<4)
#define IrSR_RXPL_POS_IS_ZERO 0x0
@@ -225,7 +269,6 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
}
lsr = STLSR;
}
- dev->last_rx = jiffies;
si->last_oscr = OSCR;
break;
@@ -237,7 +280,6 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id)
si->stats.rx_bytes++;
async_unwrap_char(dev, &si->stats, &si->rx_buff, STRBR);
} while (STLSR & LSR_DR);
- dev->last_rx = jiffies;
si->last_oscr = OSCR;
break;
@@ -397,8 +439,6 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in
si->stats.rx_packets++;
si->stats.rx_bytes += len;
-
- dev->last_rx = jiffies;
}
}
diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c
index a95188948de7..d302bcf4c148 100644
--- a/drivers/net/irda/sa1100_ir.c
+++ b/drivers/net/irda/sa1100_ir.c
@@ -36,7 +36,7 @@
#include <net/irda/irda_device.h>
#include <asm/irq.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <asm/mach/irda.h>
@@ -298,7 +298,7 @@ static int sa1100_irda_suspend(struct platform_device *pdev, pm_message_t state)
if (!dev)
return 0;
- si = dev->priv;
+ si = netdev_priv(dev);
if (si->open) {
/*
* Stop the transmit queue
@@ -323,7 +323,7 @@ static int sa1100_irda_resume(struct platform_device *pdev)
if (!dev)
return 0;
- si = dev->priv;
+ si = netdev_priv(dev);
if (si->open) {
/*
* If we missed a speed change, initialise at the new speed
@@ -359,7 +359,7 @@ static int sa1100_irda_resume(struct platform_device *pdev)
*/
static void sa1100_irda_hpsir_irq(struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
int status;
status = Ser2UTSR0;
@@ -410,7 +410,6 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev)
Ser2UTDR);
} while (Ser2UTSR1 & UTSR1_RNE);
- dev->last_rx = jiffies;
}
if (status & UTSR0_TFS && si->tx_buff.len) {
@@ -515,7 +514,6 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
sa1100_irda_rx_alloc(si);
netif_rx(skb);
- dev->last_rx = jiffies;
} else {
/*
* Remap the buffer.
@@ -534,7 +532,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev
*/
static void sa1100_irda_fir_irq(struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
/*
* Stop RX DMA
@@ -582,7 +580,7 @@ static void sa1100_irda_fir_irq(struct net_device *dev)
static irqreturn_t sa1100_irda_irq(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
- if (IS_FIR(((struct sa1100_irda *)dev->priv)))
+ if (IS_FIR(((struct sa1100_irda *)netdev_priv(dev))))
sa1100_irda_fir_irq(dev);
else
sa1100_irda_hpsir_irq(dev);
@@ -595,7 +593,7 @@ static irqreturn_t sa1100_irda_irq(int irq, void *dev_id)
static void sa1100_irda_txdma_irq(void *id)
{
struct net_device *dev = id;
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
struct sk_buff *skb = si->txskb;
si->txskb = NULL;
@@ -649,7 +647,7 @@ static void sa1100_irda_txdma_irq(void *id)
static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
int speed = irda_get_next_speed(skb);
/*
@@ -724,7 +722,7 @@ static int
sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
{
struct if_irda_req *rq = (struct if_irda_req *)ifreq;
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
int ret = -EOPNOTSUPP;
switch (cmd) {
@@ -766,13 +764,13 @@ sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
static struct net_device_stats *sa1100_irda_stats(struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
return &si->stats;
}
static int sa1100_irda_start(struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
int err;
si->speed = 9600;
@@ -835,7 +833,7 @@ err_irq:
static int sa1100_irda_stop(struct net_device *dev)
{
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
disable_irq(dev->irq);
sa1100_irda_shutdown(si);
@@ -908,7 +906,7 @@ static int sa1100_irda_probe(struct platform_device *pdev)
if (!dev)
goto err_mem_4;
- si = dev->priv;
+ si = netdev_priv(dev);
si->dev = &pdev->dev;
si->pdata = pdev->dev.platform_data;
@@ -987,7 +985,7 @@ static int sa1100_irda_remove(struct platform_device *pdev)
struct net_device *dev = platform_get_drvdata(pdev);
if (dev) {
- struct sa1100_irda *si = dev->priv;
+ struct sa1100_irda *si = netdev_priv(dev);
unregister_netdev(dev);
kfree(si->tx_buff.head);
kfree(si->rx_buff.head);
diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c
index 3f32909c24c8..ceef040aa76d 100644
--- a/drivers/net/irda/sir_dev.c
+++ b/drivers/net/irda/sir_dev.c
@@ -584,14 +584,14 @@ EXPORT_SYMBOL(sirdev_receive);
static struct net_device_stats *sirdev_get_stats(struct net_device *ndev)
{
- struct sir_dev *dev = ndev->priv;
+ struct sir_dev *dev = netdev_priv(ndev);
return (dev) ? &dev->stats : NULL;
}
static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
{
- struct sir_dev *dev = ndev->priv;
+ struct sir_dev *dev = netdev_priv(ndev);
unsigned long flags;
int actual = 0;
int err;
@@ -683,7 +683,7 @@ static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev)
static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
struct if_irda_req *irq = (struct if_irda_req *) rq;
- struct sir_dev *dev = ndev->priv;
+ struct sir_dev *dev = netdev_priv(ndev);
int ret = 0;
IRDA_ASSERT(dev != NULL, return -1;);
@@ -795,7 +795,7 @@ static void sirdev_free_buffers(struct sir_dev *dev)
static int sirdev_open(struct net_device *ndev)
{
- struct sir_dev *dev = ndev->priv;
+ struct sir_dev *dev = netdev_priv(ndev);
const struct sir_driver *drv = dev->drv;
if (!drv)
@@ -840,7 +840,7 @@ errout_dec:
static int sirdev_close(struct net_device *ndev)
{
- struct sir_dev *dev = ndev->priv;
+ struct sir_dev *dev = netdev_priv(ndev);
const struct sir_driver *drv;
// IRDA_DEBUG(0, "%s\n", __func__);
@@ -896,7 +896,7 @@ struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *n
IRDA_ERROR("%s - Can't allocate memory for IrDA control block!\n", __func__);
goto out;
}
- dev = ndev->priv;
+ dev = netdev_priv(ndev);
irda_init_max_qos_capabilies(&dev->qos);
dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index b5360fe99d3a..5d09e157e15b 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -872,7 +872,7 @@ static void smsc_ircc_timeout(struct net_device *dev)
* waits until the next transmit interrupt, and continues until the
* frame is transmitted.
*/
-int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
+static int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
{
struct smsc_ircc_cb *self;
unsigned long flags;
@@ -1128,7 +1128,7 @@ static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed)
* Set speed of IrDA port to specified baudrate
*
*/
-void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed)
+static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, __u32 speed)
{
int iobase;
int fcr; /* FIFO control reg */
@@ -1894,7 +1894,7 @@ static void __exit smsc_ircc_cleanup(void)
* This function *must* be called with spinlock held, because it may
* be called from the irq handler (via smsc_ircc_change_speed()). - Jean II
*/
-void smsc_ircc_sir_start(struct smsc_ircc_cb *self)
+static void smsc_ircc_sir_start(struct smsc_ircc_cb *self)
{
struct net_device *dev;
int fir_base, sir_base;
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 3575804fd7c6..ca4cd9266e55 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -824,7 +824,6 @@ static void stir_rcv_irq(struct urb *urb)
unwrap_chars(stir, urb->transfer_buffer,
urb->actual_length);
- stir->netdev->last_rx = jiffies;
do_gettimeofday(&stir->rx_time);
}
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index 84e609ea5fbb..74c78cf7a333 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -334,7 +334,7 @@ static __devinit int via_ircc_open(int i, chipio_t * info, unsigned int id)
if (dev == NULL)
return -ENOMEM;
- self = dev->priv;
+ self = netdev_priv(dev);
self->netdev = dev;
spin_lock_init(&self->lock);
@@ -824,7 +824,7 @@ static int via_ircc_hard_xmit_sir(struct sk_buff *skb,
u16 iobase;
__u32 speed;
- self = (struct via_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
iobase = self->io.fir_base;
@@ -896,7 +896,7 @@ static int via_ircc_hard_xmit_fir(struct sk_buff *skb,
__u32 speed;
unsigned long flags;
- self = (struct via_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
iobase = self->io.fir_base;
if (self->st_fifo.len)
@@ -1349,7 +1349,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
static irqreturn_t via_ircc_interrupt(int dummy, void *dev_id)
{
struct net_device *dev = dev_id;
- struct via_ircc_cb *self = dev->priv;
+ struct via_ircc_cb *self = netdev_priv(dev);
int iobase;
u8 iHostIntType, iRxIntType, iTxIntType;
@@ -1522,7 +1522,7 @@ static int via_ircc_net_open(struct net_device *dev)
IRDA_DEBUG(3, "%s()\n", __func__);
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct via_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
self->stats.rx_packets = 0;
IRDA_ASSERT(self != NULL, return 0;);
iobase = self->io.fir_base;
@@ -1589,7 +1589,7 @@ static int via_ircc_net_close(struct net_device *dev)
IRDA_DEBUG(3, "%s()\n", __func__);
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct via_ircc_cb *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
/* Stop device */
@@ -1628,7 +1628,7 @@ static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
int ret = 0;
IRDA_ASSERT(dev != NULL, return -1;);
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_DEBUG(1, "%s(), %s, (cmd=0x%X)\n", __func__, dev->name,
cmd);
@@ -1663,7 +1663,7 @@ static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq,
static struct net_device_stats *via_ircc_net_get_stats(struct net_device
*dev)
{
- struct via_ircc_cb *self = (struct via_ircc_cb *) dev->priv;
+ struct via_ircc_cb *self = netdev_priv(dev);
return &self->stats;
}
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
index 9c926d205de9..0d30f8d659a1 100644
--- a/drivers/net/irda/vlsi_ir.c
+++ b/drivers/net/irda/vlsi_ir.c
@@ -178,7 +178,7 @@ static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev)
static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
u8 byte;
u16 word;
unsigned delta1, delta2;
@@ -346,7 +346,7 @@ static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r)
static int vlsi_seq_show(struct seq_file *seq, void *v)
{
struct net_device *ndev = seq->private;
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
unsigned long flags;
seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION);
@@ -543,7 +543,7 @@ static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd)
struct sk_buff *skb;
int ret = 0;
struct net_device *ndev = (struct net_device *)pci_get_drvdata(r->pdev);
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir);
/* dma buffer now owned by the CPU */
@@ -600,7 +600,6 @@ static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd)
netif_rx(skb);
else
netif_rx_ni(skb);
- ndev->last_rx = jiffies;
done:
rd_set_status(rd, 0);
@@ -638,7 +637,7 @@ static void vlsi_fill_rx(struct vlsi_ring *r)
static void vlsi_rx_interrupt(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
struct vlsi_ring *r = idev->rx_ring;
struct ring_descr *rd;
int ret;
@@ -856,7 +855,7 @@ static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase)
static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
struct vlsi_ring *r = idev->tx_ring;
struct ring_descr *rd;
unsigned long flags;
@@ -1063,7 +1062,7 @@ drop:
static void vlsi_tx_interrupt(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
struct vlsi_ring *r = idev->tx_ring;
struct ring_descr *rd;
unsigned iobase;
@@ -1262,7 +1261,7 @@ static inline void vlsi_clear_regs(unsigned iobase)
static int vlsi_init_chip(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
unsigned iobase;
u16 ptr;
@@ -1376,14 +1375,14 @@ static int vlsi_stop_hw(vlsi_irda_dev_t *idev)
static struct net_device_stats * vlsi_get_stats(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
return &idev->stats;
}
static void vlsi_tx_timeout(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
vlsi_reg_debug(ndev->base_addr, __func__);
@@ -1408,7 +1407,7 @@ static void vlsi_tx_timeout(struct net_device *ndev)
static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
struct if_irda_req *irq = (struct if_irda_req *) rq;
unsigned long flags;
u16 fifocnt;
@@ -1458,7 +1457,7 @@ static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
static irqreturn_t vlsi_interrupt(int irq, void *dev_instance)
{
struct net_device *ndev = dev_instance;
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
unsigned iobase;
u8 irintr;
int boguscount = 5;
@@ -1499,7 +1498,7 @@ static irqreturn_t vlsi_interrupt(int irq, void *dev_instance)
static int vlsi_open(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
int err = -EAGAIN;
char hwname[32];
@@ -1558,7 +1557,7 @@ errout:
static int vlsi_close(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
netif_stop_queue(ndev);
@@ -1581,7 +1580,7 @@ static int vlsi_close(struct net_device *ndev)
static int vlsi_irda_init(struct net_device *ndev)
{
- vlsi_irda_dev_t *idev = ndev->priv;
+ vlsi_irda_dev_t *idev = netdev_priv(ndev);
struct pci_dev *pdev = idev->pdev;
ndev->irq = pdev->irq;
@@ -1656,7 +1655,7 @@ vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_disable;
}
- idev = ndev->priv;
+ idev = netdev_priv(ndev);
spin_lock_init(&idev->lock);
mutex_init(&idev->mtx);
@@ -1713,7 +1712,7 @@ static void __devexit vlsi_irda_remove(struct pci_dev *pdev)
unregister_netdev(ndev);
- idev = ndev->priv;
+ idev = netdev_priv(ndev);
mutex_lock(&idev->mtx);
if (idev->proc_entry) {
remove_proc_entry(ndev->name, vlsi_proc_root);
@@ -1748,7 +1747,7 @@ static int vlsi_irda_suspend(struct pci_dev *pdev, pm_message_t state)
__func__, pci_name(pdev));
return 0;
}
- idev = ndev->priv;
+ idev = netdev_priv(ndev);
mutex_lock(&idev->mtx);
if (pdev->current_state != 0) { /* already suspended */
if (state.event > pdev->current_state) { /* simply go deeper */
@@ -1787,7 +1786,7 @@ static int vlsi_irda_resume(struct pci_dev *pdev)
__func__, pci_name(pdev));
return 0;
}
- idev = ndev->priv;
+ idev = netdev_priv(ndev);
mutex_lock(&idev->mtx);
if (pdev->current_state == 0) {
mutex_unlock(&idev->mtx);
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 002a6d769f21..30ec9131c5ce 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -147,8 +147,8 @@ static void __exit w83977af_cleanup(void)
* Open driver instance
*
*/
-int w83977af_open(int i, unsigned int iobase, unsigned int irq,
- unsigned int dma)
+static int w83977af_open(int i, unsigned int iobase, unsigned int irq,
+ unsigned int dma)
{
struct net_device *dev;
struct w83977af_ir *self;
@@ -178,7 +178,7 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq,
goto err_out;
}
- self = dev->priv;
+ self = netdev_priv(dev);
spin_lock_init(&self->lock);
@@ -310,7 +310,7 @@ static int w83977af_close(struct w83977af_ir *self)
return 0;
}
-int w83977af_probe( int iobase, int irq, int dma)
+static int w83977af_probe(int iobase, int irq, int dma)
{
int version;
int i;
@@ -409,7 +409,7 @@ int w83977af_probe( int iobase, int irq, int dma)
return -1;
}
-void w83977af_change_speed(struct w83977af_ir *self, __u32 speed)
+static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed)
{
int ir_mode = HCR_SIR;
int iobase;
@@ -489,7 +489,7 @@ void w83977af_change_speed(struct w83977af_ir *self, __u32 speed)
* Sets up a DMA transfer to send the current frame.
*
*/
-int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+static int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct w83977af_ir *self;
__s32 speed;
@@ -497,7 +497,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
__u8 set;
int mtt;
- self = (struct w83977af_ir *) dev->priv;
+ self = netdev_priv(dev);
iobase = self->io.fir_base;
@@ -731,7 +731,7 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self)
* if it starts to receive a frame.
*
*/
-int w83977af_dma_receive(struct w83977af_ir *self)
+static int w83977af_dma_receive(struct w83977af_ir *self)
{
int iobase;
__u8 set;
@@ -803,7 +803,7 @@ int w83977af_dma_receive(struct w83977af_ir *self)
* Finished with receiving a frame
*
*/
-int w83977af_dma_receive_complete(struct w83977af_ir *self)
+static int w83977af_dma_receive_complete(struct w83977af_ir *self)
{
struct sk_buff *skb;
struct st_fifo *st_fifo;
@@ -923,7 +923,6 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self)
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IRDA);
netif_rx(skb);
- self->netdev->last_rx = jiffies;
}
}
/* Restore set register */
@@ -1119,7 +1118,7 @@ static irqreturn_t w83977af_interrupt(int irq, void *dev_id)
__u8 set, icr, isr;
int iobase;
- self = dev->priv;
+ self = netdev_priv(dev);
iobase = self->io.fir_base;
@@ -1192,7 +1191,7 @@ static int w83977af_net_open(struct net_device *dev)
IRDA_DEBUG(0, "%s()\n", __func__ );
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct w83977af_ir *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
@@ -1256,7 +1255,7 @@ static int w83977af_net_close(struct net_device *dev)
IRDA_ASSERT(dev != NULL, return -1;);
- self = (struct w83977af_ir *) dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return 0;);
@@ -1303,7 +1302,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
IRDA_ASSERT(dev != NULL, return -1;);
- self = dev->priv;
+ self = netdev_priv(dev);
IRDA_ASSERT(self != NULL, return -1;);
@@ -1339,7 +1338,7 @@ out:
static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev)
{
- struct w83977af_ir *self = (struct w83977af_ir *) dev->priv;
+ struct w83977af_ir *self = netdev_priv(dev);
return &self->stats;
}
diff --git a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c
index d6ff26af37b3..3126678bdd3c 100644
--- a/drivers/net/isa-skeleton.c
+++ b/drivers/net/isa-skeleton.c
@@ -192,7 +192,6 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr)
static unsigned version_printed;
int i;
int err = -ENODEV;
- DECLARE_MAC_BUF(mac);
/* Grab the region so that no one else tries to probe our ioports. */
if (!request_region(ioaddr, NETCARD_IO_EXTENT, cardname))
@@ -220,7 +219,7 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + i);
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
err = -EAGAIN;
#ifdef jumpered_interrupts
@@ -584,7 +583,6 @@ net_rx(struct net_device *dev)
insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
}
@@ -711,15 +709,3 @@ cleanup_module(void)
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command:
- * gcc -D__KERNEL__ -Wall -Wstrict-prototypes -Wwrite-strings
- * -Wredundant-decls -O2 -m486 -c skeleton.c
- * version-control: t
- * kept-new-versions: 5
- * tab-width: 4
- * c-indent-level: 4
- * End:
- */
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index c46864d626b2..c7457f97259d 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -952,7 +952,7 @@ static int veth_change_mtu(struct net_device *dev, int new_mtu)
static void veth_set_multicast_list(struct net_device *dev)
{
- struct veth_port *port = (struct veth_port *) dev->priv;
+ struct veth_port *port = netdev_priv(dev);
unsigned long flags;
write_lock_irqsave(&port->mcast_gate, flags);
@@ -1044,7 +1044,7 @@ static struct net_device *veth_probe_one(int vlan,
return NULL;
}
- port = (struct veth_port *) dev->priv;
+ port = netdev_priv(dev);
spin_lock_init(&port->queue_lock);
rwlock_init(&port->mcast_gate);
@@ -1102,7 +1102,7 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
struct net_device *dev)
{
struct veth_lpar_connection *cnx = veth_cnx[rlp];
- struct veth_port *port = (struct veth_port *) dev->priv;
+ struct veth_port *port = netdev_priv(dev);
HvLpEvent_Rc rc;
struct veth_msg *msg = NULL;
unsigned long flags;
@@ -1191,7 +1191,7 @@ static void veth_transmit_to_many(struct sk_buff *skb,
static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned char *frame = skb->data;
- struct veth_port *port = (struct veth_port *) dev->priv;
+ struct veth_port *port = netdev_priv(dev);
HvLpIndexMap lpmask;
if (! (frame[0] & 0x01)) {
@@ -1255,7 +1255,7 @@ static void veth_wake_queues(struct veth_lpar_connection *cnx)
if (! dev)
continue;
- port = (struct veth_port *)dev->priv;
+ port = netdev_priv(dev);
if (! (port->lpar_map & (1<<cnx->remote_lp)))
continue;
@@ -1284,7 +1284,7 @@ static void veth_stop_queues(struct veth_lpar_connection *cnx)
if (! dev)
continue;
- port = (struct veth_port *)dev->priv;
+ port = netdev_priv(dev);
/* If this cnx is not on the vlan for this port, continue */
if (! (port->lpar_map & (1 << cnx->remote_lp)))
@@ -1506,7 +1506,7 @@ static void veth_receive(struct veth_lpar_connection *cnx,
continue;
}
- port = (struct veth_port *)dev->priv;
+ port = netdev_priv(dev);
dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000;
if ((vlan > HVMAXARCHITECTEDVIRTUALLANS) || !port) {
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
index be3c7dc96f63..eee28d395682 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -321,6 +321,24 @@ ixgb_reset(struct ixgb_adapter *adapter)
}
}
+static const struct net_device_ops ixgb_netdev_ops = {
+ .ndo_open = ixgb_open,
+ .ndo_stop = ixgb_close,
+ .ndo_start_xmit = ixgb_xmit_frame,
+ .ndo_get_stats = ixgb_get_stats,
+ .ndo_set_multicast_list = ixgb_set_multi,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = ixgb_set_mac,
+ .ndo_change_mtu = ixgb_change_mtu,
+ .ndo_tx_timeout = ixgb_tx_timeout,
+ .ndo_vlan_rx_register = ixgb_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = ixgb_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ixgb_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ixgb_netpoll,
+#endif
+};
+
/**
* ixgb_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -381,8 +399,7 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->hw.back = adapter;
adapter->msg_enable = netif_msg_init(debug, DEFAULT_DEBUG_LEVEL_SHIFT);
- adapter->hw.hw_addr = ioremap(pci_resource_start(pdev, BAR_0),
- pci_resource_len(pdev, BAR_0));
+ adapter->hw.hw_addr = pci_ioremap_bar(pdev, BAR_0);
if (!adapter->hw.hw_addr) {
err = -EIO;
goto err_ioremap;
@@ -397,23 +414,10 @@ ixgb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
}
- netdev->open = &ixgb_open;
- netdev->stop = &ixgb_close;
- netdev->hard_start_xmit = &ixgb_xmit_frame;
- netdev->get_stats = &ixgb_get_stats;
- netdev->set_multicast_list = &ixgb_set_multi;
- netdev->set_mac_address = &ixgb_set_mac;
- netdev->change_mtu = &ixgb_change_mtu;
+ netdev->netdev_ops = &ixgb_netdev_ops;
ixgb_set_ethtool_ops(netdev);
- netdev->tx_timeout = &ixgb_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
netif_napi_add(netdev, &adapter->napi, ixgb_clean, 64);
- netdev->vlan_rx_register = ixgb_vlan_rx_register;
- netdev->vlan_rx_add_vid = ixgb_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = ixgb_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = ixgb_netpoll;
-#endif
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
@@ -1106,8 +1110,15 @@ ixgb_watchdog(unsigned long data)
if (adapter->hw.link_up) {
if (!netif_carrier_ok(netdev)) {
- DPRINTK(LINK, INFO,
- "NIC Link is Up 10000 Mbps Full Duplex\n");
+ printk(KERN_INFO "ixgb: %s NIC Link is Up 10 Gbps "
+ "Full Duplex, Flow Control: %s\n",
+ netdev->name,
+ (adapter->hw.fc.type == ixgb_fc_full) ?
+ "RX/TX" :
+ ((adapter->hw.fc.type == ixgb_fc_rx_pause) ?
+ "RX" :
+ ((adapter->hw.fc.type == ixgb_fc_tx_pause) ?
+ "TX" : "None")));
adapter->link_speed = 10000;
adapter->link_duplex = FULL_DUPLEX;
netif_carrier_on(netdev);
@@ -1117,7 +1128,8 @@ ixgb_watchdog(unsigned long data)
if (netif_carrier_ok(netdev)) {
adapter->link_speed = 0;
adapter->link_duplex = 0;
- DPRINTK(LINK, INFO, "NIC Link is Down\n");
+ printk(KERN_INFO "ixgb: %s NIC Link is Down\n",
+ netdev->name);
netif_carrier_off(netdev);
netif_stop_queue(netdev);
@@ -1709,14 +1721,14 @@ ixgb_intr(int irq, void *data)
if (!test_bit(__IXGB_DOWN, &adapter->flags))
mod_timer(&adapter->watchdog_timer, jiffies);
- if (netif_rx_schedule_prep(netdev, &adapter->napi)) {
+ if (netif_rx_schedule_prep(&adapter->napi)) {
/* Disable interrupts and register for poll. The flush
of the posted write is intentionally left out.
*/
IXGB_WRITE_REG(&adapter->hw, IMC, ~0);
- __netif_rx_schedule(netdev, &adapter->napi);
+ __netif_rx_schedule(&adapter->napi);
}
return IRQ_HANDLED;
}
@@ -1730,7 +1742,6 @@ static int
ixgb_clean(struct napi_struct *napi, int budget)
{
struct ixgb_adapter *adapter = container_of(napi, struct ixgb_adapter, napi);
- struct net_device *netdev = adapter->netdev;
int work_done = 0;
ixgb_clean_tx_irq(adapter);
@@ -1738,7 +1749,7 @@ ixgb_clean(struct napi_struct *napi, int budget)
/* If budget not fully consumed, exit the polling mode */
if (work_done < budget) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
if (!test_bit(__IXGB_DOWN, &adapter->flags))
ixgb_irq_enable(adapter);
}
@@ -1981,7 +1992,6 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter, int *work_done, int work_to_do)
} else {
netif_receive_skb(skb);
}
- netdev->last_rx = jiffies;
rxdesc_done:
/* clean up descriptor, might be written over by hw */
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
index ccd83d9f579e..6e7ef765bcd8 100644
--- a/drivers/net/ixgbe/Makefile
+++ b/drivers/net/ixgbe/Makefile
@@ -34,3 +34,5 @@ obj-$(CONFIG_IXGBE) += ixgbe.o
ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o \
ixgbe_82598.o ixgbe_phy.o
+
+ixgbe-$(CONFIG_IXGBE_DCB) += ixgbe_dcb.o ixgbe_dcb_82598.o ixgbe_dcb_nl.o
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
index e116d340dcc6..e112008f39c1 100644
--- a/drivers/net/ixgbe/ixgbe.h
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -32,10 +32,11 @@
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/inet_lro.h>
+#include <linux/aer.h>
#include "ixgbe_type.h"
#include "ixgbe_common.h"
-
+#include "ixgbe_dcb.h"
#ifdef CONFIG_IXGBE_DCA
#include <linux/dca.h>
#endif
@@ -84,6 +85,7 @@
#define IXGBE_TX_FLAGS_TSO (u32)(1 << 2)
#define IXGBE_TX_FLAGS_IPV4 (u32)(1 << 3)
#define IXGBE_TX_FLAGS_VLAN_MASK 0xffff0000
+#define IXGBE_TX_FLAGS_VLAN_PRIO_MASK 0x0000e000
#define IXGBE_TX_FLAGS_VLAN_SHIFT 16
#define IXGBE_MAX_LRO_DESCRIPTORS 8
@@ -134,7 +136,7 @@ struct ixgbe_ring {
u16 reg_idx; /* holds the special value that gets the hardware register
* offset associated with this ring, which is different
- * for DCE and RSS modes */
+ * for DCB and RSS modes */
#ifdef CONFIG_IXGBE_DCA
/* cpu for tx queue */
@@ -152,8 +154,10 @@ struct ixgbe_ring {
u16 rx_buf_len;
};
+#define RING_F_DCB 0
#define RING_F_VMDQ 1
#define RING_F_RSS 2
+#define IXGBE_MAX_DCB_INDICES 8
#define IXGBE_MAX_RSS_INDICES 16
#define IXGBE_MAX_VMDQ_INDICES 16
struct ixgbe_ring_feature {
@@ -164,6 +168,10 @@ struct ixgbe_ring_feature {
#define MAX_RX_QUEUES 64
#define MAX_TX_QUEUES 32
+#define MAX_RX_PACKET_BUFFERS ((adapter->flags & IXGBE_FLAG_DCB_ENABLED) \
+ ? 8 : 1)
+#define MAX_TX_PACKET_BUFFERS MAX_RX_PACKET_BUFFERS
+
/* MAX_MSIX_Q_VECTORS of these are allocated,
* but we only use one per queue-specific vector.
*/
@@ -215,6 +223,9 @@ struct ixgbe_adapter {
struct work_struct reset_task;
struct ixgbe_q_vector q_vector[MAX_MSIX_Q_VECTORS];
char name[MAX_MSIX_COUNT][IFNAMSIZ + 5];
+ struct ixgbe_dcb_config dcb_cfg;
+ struct ixgbe_dcb_config temp_dcb_cfg;
+ u8 dcb_set_bitmap;
/* Interrupt Throttle Rate */
u32 itr_setting;
@@ -267,8 +278,10 @@ struct ixgbe_adapter {
#define IXGBE_FLAG_RSS_CAPABLE (u32)(1 << 17)
#define IXGBE_FLAG_VMDQ_CAPABLE (u32)(1 << 18)
#define IXGBE_FLAG_VMDQ_ENABLED (u32)(1 << 19)
+#define IXGBE_FLAG_FAN_FAIL_CAPABLE (u32)(1 << 20)
#define IXGBE_FLAG_NEED_LINK_UPDATE (u32)(1 << 22)
#define IXGBE_FLAG_IN_WATCHDOG_TASK (u32)(1 << 23)
+#define IXGBE_FLAG_DCB_ENABLED (u32)(1 << 24)
/* default to trying for four seconds */
#define IXGBE_TRY_LINK_TIMEOUT (4 * HZ)
@@ -299,12 +312,15 @@ struct ixgbe_adapter {
unsigned long link_check_timeout;
struct work_struct watchdog_task;
+ struct work_struct sfp_task;
+ struct timer_list sfp_timer;
};
enum ixbge_state_t {
__IXGBE_TESTING,
__IXGBE_RESETTING,
- __IXGBE_DOWN
+ __IXGBE_DOWN,
+ __IXGBE_SFP_MODULE_NOT_FOUND
};
enum ixgbe_boards {
@@ -312,6 +328,12 @@ enum ixgbe_boards {
};
extern struct ixgbe_info ixgbe_82598_info;
+#ifdef CONFIG_IXGBE_DCB
+extern struct dcbnl_rtnl_ops dcbnl_ops;
+extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
+ struct ixgbe_dcb_config *dst_dcb_cfg,
+ int tc_max);
+#endif
extern char ixgbe_driver_name[];
extern const char ixgbe_driver_version[];
@@ -326,5 +348,9 @@ extern int ixgbe_setup_tx_resources(struct ixgbe_adapter *, struct ixgbe_ring *)
extern void ixgbe_free_rx_resources(struct ixgbe_adapter *, struct ixgbe_ring *);
extern void ixgbe_free_tx_resources(struct ixgbe_adapter *, struct ixgbe_ring *);
extern void ixgbe_update_stats(struct ixgbe_adapter *adapter);
+extern void ixgbe_reset_interrupt_capability(struct ixgbe_adapter *adapter);
+extern int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter);
+void ixgbe_napi_add_all(struct ixgbe_adapter *adapter);
+void ixgbe_napi_del_all(struct ixgbe_adapter *adapter);
#endif /* _IXGBE_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
index 7cddcfba809e..ad5699d9ab0d 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -46,6 +46,8 @@ static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg,
bool autoneg_wait_to_complete);
+static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
+ u8 *eeprom_data);
/**
*/
@@ -53,12 +55,40 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
{
struct ixgbe_mac_info *mac = &hw->mac;
struct ixgbe_phy_info *phy = &hw->phy;
+ s32 ret_val = 0;
+ u16 list_offset, data_offset;
/* Call PHY identify routine to get the phy type */
ixgbe_identify_phy_generic(hw);
/* PHY Init */
switch (phy->type) {
+ case ixgbe_phy_tn:
+ phy->ops.check_link = &ixgbe_check_phy_link_tnx;
+ phy->ops.get_firmware_version =
+ &ixgbe_get_phy_firmware_version_tnx;
+ break;
+ case ixgbe_phy_nl:
+ phy->ops.reset = &ixgbe_reset_phy_nl;
+
+ /* Call SFP+ identify routine to get the SFP+ module type */
+ ret_val = phy->ops.identify_sfp(hw);
+ if (ret_val != 0)
+ goto out;
+ else if (hw->phy.sfp_type == ixgbe_sfp_type_unknown) {
+ ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
+ goto out;
+ }
+
+ /* Check to see if SFP+ module is supported */
+ ret_val = ixgbe_get_sfp_init_sequence_offsets(hw,
+ &list_offset,
+ &data_offset);
+ if (ret_val != 0) {
+ ret_val = IXGBE_ERR_SFP_NOT_SUPPORTED;
+ goto out;
+ }
+ break;
default:
break;
}
@@ -77,7 +107,8 @@ static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
mac->max_rx_queues = IXGBE_82598_MAX_RX_QUEUES;
mac->max_tx_queues = IXGBE_82598_MAX_TX_QUEUES;
- return 0;
+out:
+ return ret_val;
}
/**
@@ -146,9 +177,9 @@ static s32 ixgbe_get_link_capabilities_82598(struct ixgbe_hw *hw,
*
* Determines the link capabilities by reading the AUTOC register.
**/
-s32 ixgbe_get_copper_link_capabilities_82598(struct ixgbe_hw *hw,
- ixgbe_link_speed *speed,
- bool *autoneg)
+static s32 ixgbe_get_copper_link_capabilities_82598(struct ixgbe_hw *hw,
+ ixgbe_link_speed *speed,
+ bool *autoneg)
{
s32 status = IXGBE_ERR_LINK_SETUP;
u16 speed_ability;
@@ -186,9 +217,15 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
case IXGBE_DEV_ID_82598EB_CX4:
case IXGBE_DEV_ID_82598_CX4_DUAL_PORT:
+ case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
+ case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
case IXGBE_DEV_ID_82598EB_XF_LR:
+ case IXGBE_DEV_ID_82598EB_SFP_LOM:
media_type = ixgbe_media_type_fiber;
break;
+ case IXGBE_DEV_ID_82598AT:
+ media_type = ixgbe_media_type_copper;
+ break;
default:
media_type = ixgbe_media_type_unknown;
break;
@@ -205,7 +242,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
* Configures the flow control settings based on SW configuration. This
* function is used for 802.3x flow control configuration only.
**/
-s32 ixgbe_setup_fc_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
+static s32 ixgbe_setup_fc_82598(struct ixgbe_hw *hw, s32 packetbuf_num)
{
u32 frctl_reg;
u32 rmcs_reg;
@@ -391,6 +428,46 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
{
u32 links_reg;
u32 i;
+ u16 link_reg, adapt_comp_reg;
+
+ /*
+ * SERDES PHY requires us to read link status from register 0xC79F.
+ * Bit 0 set indicates link is up/ready; clear indicates link down.
+ * 0xC00C is read to check that the XAUI lanes are active. Bit 0
+ * clear indicates active; set indicates inactive.
+ */
+ if (hw->phy.type == ixgbe_phy_nl) {
+ hw->phy.ops.read_reg(hw, 0xC79F, IXGBE_TWINAX_DEV, &link_reg);
+ hw->phy.ops.read_reg(hw, 0xC79F, IXGBE_TWINAX_DEV, &link_reg);
+ hw->phy.ops.read_reg(hw, 0xC00C, IXGBE_TWINAX_DEV,
+ &adapt_comp_reg);
+ if (link_up_wait_to_complete) {
+ for (i = 0; i < IXGBE_LINK_UP_TIME; i++) {
+ if ((link_reg & 1) &&
+ ((adapt_comp_reg & 1) == 0)) {
+ *link_up = true;
+ break;
+ } else {
+ *link_up = false;
+ }
+ msleep(100);
+ hw->phy.ops.read_reg(hw, 0xC79F,
+ IXGBE_TWINAX_DEV,
+ &link_reg);
+ hw->phy.ops.read_reg(hw, 0xC00C,
+ IXGBE_TWINAX_DEV,
+ &adapt_comp_reg);
+ }
+ } else {
+ if ((link_reg & 1) && ((adapt_comp_reg & 1) == 0))
+ *link_up = true;
+ else
+ *link_up = false;
+ }
+
+ if (*link_up == false)
+ goto out;
+ }
links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
if (link_up_wait_to_complete) {
@@ -416,6 +493,7 @@ static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw,
else
*speed = IXGBE_LINK_SPEED_1GB_FULL;
+out:
return 0;
}
@@ -648,7 +726,7 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
* @rar: receive address register index to associate with a VMDq index
* @vmdq: VMDq set index
**/
-s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
+static s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
{
u32 rar_high;
@@ -692,8 +770,8 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
*
* Turn on/off specified VLAN in the VLAN filter table.
**/
-s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind,
- bool vlan_on)
+static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind,
+ bool vlan_on)
{
u32 regindex;
u32 bitindex;
@@ -816,7 +894,7 @@ static s32 ixgbe_blink_led_stop_82598(struct ixgbe_hw *hw, u32 index)
*
* Performs read operation to Atlas analog register specified.
**/
-s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val)
+static s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val)
{
u32 atlas_ctl;
@@ -838,7 +916,7 @@ s32 ixgbe_read_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 *val)
*
* Performs write operation to Atlas analog register specified.
**/
-s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val)
+static s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val)
{
u32 atlas_ctl;
@@ -851,12 +929,75 @@ s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val)
}
/**
+ * ixgbe_read_i2c_eeprom_82598 - Read 8 bit EEPROM word of an SFP+ module
+ * over I2C interface through an intermediate phy.
+ * @hw: pointer to hardware structure
+ * @byte_offset: EEPROM byte offset to read
+ * @eeprom_data: value read
+ *
+ * Performs byte read operation to SFP module's EEPROM over I2C interface.
+ **/
+static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset,
+ u8 *eeprom_data)
+{
+ s32 status = 0;
+ u16 sfp_addr = 0;
+ u16 sfp_data = 0;
+ u16 sfp_stat = 0;
+ u32 i;
+
+ if (hw->phy.type == ixgbe_phy_nl) {
+ /*
+ * phy SDA/SCL registers are at addresses 0xC30A to
+ * 0xC30D. These registers are used to talk to the SFP+
+ * module's EEPROM through the SDA/SCL (I2C) interface.
+ */
+ sfp_addr = (IXGBE_I2C_EEPROM_DEV_ADDR << 8) + byte_offset;
+ sfp_addr = (sfp_addr | IXGBE_I2C_EEPROM_READ_MASK);
+ hw->phy.ops.write_reg(hw,
+ IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ sfp_addr);
+
+ /* Poll status */
+ for (i = 0; i < 100; i++) {
+ hw->phy.ops.read_reg(hw,
+ IXGBE_MDIO_PMA_PMD_SDA_SCL_STAT,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ &sfp_stat);
+ sfp_stat = sfp_stat & IXGBE_I2C_EEPROM_STATUS_MASK;
+ if (sfp_stat != IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS)
+ break;
+ msleep(10);
+ }
+
+ if (sfp_stat != IXGBE_I2C_EEPROM_STATUS_PASS) {
+ hw_dbg(hw, "EEPROM read did not pass.\n");
+ status = IXGBE_ERR_SFP_NOT_PRESENT;
+ goto out;
+ }
+
+ /* Read data */
+ hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE, &sfp_data);
+
+ *eeprom_data = (u8)(sfp_data >> 8);
+ } else {
+ status = IXGBE_ERR_PHY;
+ goto out;
+ }
+
+out:
+ return status;
+}
+
+/**
* ixgbe_get_supported_physical_layer_82598 - Returns physical layer type
* @hw: pointer to hardware structure
*
* Determines physical layer capabilities of the current configuration.
**/
-s32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
+static s32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
{
s32 physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
@@ -865,13 +1006,39 @@ s32 ixgbe_get_supported_physical_layer_82598(struct ixgbe_hw *hw)
case IXGBE_DEV_ID_82598_CX4_DUAL_PORT:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_CX4;
break;
+ case IXGBE_DEV_ID_82598_DA_DUAL_PORT:
+ physical_layer = IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU;
+ break;
case IXGBE_DEV_ID_82598AF_DUAL_PORT:
case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+ case IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
break;
case IXGBE_DEV_ID_82598EB_XF_LR:
physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
break;
+ case IXGBE_DEV_ID_82598AT:
+ physical_layer = (IXGBE_PHYSICAL_LAYER_10GBASE_T |
+ IXGBE_PHYSICAL_LAYER_1000BASE_T);
+ break;
+ case IXGBE_DEV_ID_82598EB_SFP_LOM:
+ hw->phy.ops.identify_sfp(hw);
+
+ switch (hw->phy.sfp_type) {
+ case ixgbe_sfp_type_da_cu:
+ physical_layer = IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU;
+ break;
+ case ixgbe_sfp_type_sr:
+ physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_SR;
+ break;
+ case ixgbe_sfp_type_lr:
+ physical_layer = IXGBE_PHYSICAL_LAYER_10GBASE_LR;
+ break;
+ default:
+ physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
+ break;
+ }
+ break;
default:
physical_layer = IXGBE_PHYSICAL_LAYER_UNKNOWN;
@@ -923,12 +1090,13 @@ static struct ixgbe_eeprom_operations eeprom_ops_82598 = {
static struct ixgbe_phy_operations phy_ops_82598 = {
.identify = &ixgbe_identify_phy_generic,
- /* .identify_sfp = &ixgbe_identify_sfp_module_generic, */
+ .identify_sfp = &ixgbe_identify_sfp_module_generic,
.reset = &ixgbe_reset_phy_generic,
.read_reg = &ixgbe_read_phy_reg_generic,
.write_reg = &ixgbe_write_phy_reg_generic,
.setup_link = &ixgbe_setup_phy_link_generic,
.setup_link_speed = &ixgbe_setup_phy_link_speed_generic,
+ .read_i2c_eeprom = &ixgbe_read_i2c_eeprom_82598,
};
struct ixgbe_info ixgbe_82598_info = {
diff --git a/drivers/net/ixgbe/ixgbe_dcb.c b/drivers/net/ixgbe/ixgbe_dcb.c
new file mode 100644
index 000000000000..e2e28ac63dec
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_dcb.c
@@ -0,0 +1,332 @@
+/*******************************************************************************
+
+ Intel 10 Gigabit PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+
+#include "ixgbe.h"
+#include "ixgbe_type.h"
+#include "ixgbe_dcb.h"
+#include "ixgbe_dcb_82598.h"
+
+/**
+ * ixgbe_dcb_config - Struct containing DCB settings.
+ * @dcb_config: Pointer to DCB config structure
+ *
+ * This function checks DCB rules for DCB settings.
+ * The following rules are checked:
+ * 1. The sum of bandwidth percentages of all Bandwidth Groups must total 100%.
+ * 2. The sum of bandwidth percentages of all Traffic Classes within a Bandwidth
+ * Group must total 100.
+ * 3. A Traffic Class should not be set to both Link Strict Priority
+ * and Group Strict Priority.
+ * 4. Link strict Bandwidth Groups can only have link strict traffic classes
+ * with zero bandwidth.
+ */
+s32 ixgbe_dcb_check_config(struct ixgbe_dcb_config *dcb_config)
+{
+ struct tc_bw_alloc *p;
+ s32 ret_val = 0;
+ u8 i, j, bw = 0, bw_id;
+ u8 bw_sum[2][MAX_BW_GROUP];
+ bool link_strict[2][MAX_BW_GROUP];
+
+ memset(bw_sum, 0, sizeof(bw_sum));
+ memset(link_strict, 0, sizeof(link_strict));
+
+ /* First Tx, then Rx */
+ for (i = 0; i < 2; i++) {
+ /* Check each traffic class for rule violation */
+ for (j = 0; j < MAX_TRAFFIC_CLASS; j++) {
+ p = &dcb_config->tc_config[j].path[i];
+
+ bw = p->bwg_percent;
+ bw_id = p->bwg_id;
+
+ if (bw_id >= MAX_BW_GROUP) {
+ ret_val = DCB_ERR_CONFIG;
+ goto err_config;
+ }
+ if (p->prio_type == prio_link) {
+ link_strict[i][bw_id] = true;
+ /* Link strict should have zero bandwidth */
+ if (bw) {
+ ret_val = DCB_ERR_LS_BW_NONZERO;
+ goto err_config;
+ }
+ } else if (!bw) {
+ /*
+ * Traffic classes without link strict
+ * should have non-zero bandwidth.
+ */
+ ret_val = DCB_ERR_TC_BW_ZERO;
+ goto err_config;
+ }
+ bw_sum[i][bw_id] += bw;
+ }
+
+ bw = 0;
+
+ /* Check each bandwidth group for rule violation */
+ for (j = 0; j < MAX_BW_GROUP; j++) {
+ bw += dcb_config->bw_percentage[i][j];
+ /*
+ * Sum of bandwidth percentages of all traffic classes
+ * within a Bandwidth Group must total 100 except for
+ * link strict group (zero bandwidth).
+ */
+ if (link_strict[i][j]) {
+ if (bw_sum[i][j]) {
+ /*
+ * Link strict group should have zero
+ * bandwidth.
+ */
+ ret_val = DCB_ERR_LS_BWG_NONZERO;
+ goto err_config;
+ }
+ } else if (bw_sum[i][j] != BW_PERCENT &&
+ bw_sum[i][j] != 0) {
+ ret_val = DCB_ERR_TC_BW;
+ goto err_config;
+ }
+ }
+
+ if (bw != BW_PERCENT) {
+ ret_val = DCB_ERR_BW_GROUP;
+ goto err_config;
+ }
+ }
+
+err_config:
+ return ret_val;
+}
+
+/**
+ * ixgbe_dcb_calculate_tc_credits - Calculates traffic class credits
+ * @ixgbe_dcb_config: Struct containing DCB settings.
+ * @direction: Configuring either Tx or Rx.
+ *
+ * This function calculates the credits allocated to each traffic class.
+ * It should be called only after the rules are checked by
+ * ixgbe_dcb_check_config().
+ */
+s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_dcb_config *dcb_config,
+ u8 direction)
+{
+ struct tc_bw_alloc *p;
+ s32 ret_val = 0;
+ /* Initialization values default for Tx settings */
+ u32 credit_refill = 0;
+ u32 credit_max = 0;
+ u16 link_percentage = 0;
+ u8 bw_percent = 0;
+ u8 i;
+
+ if (dcb_config == NULL) {
+ ret_val = DCB_ERR_CONFIG;
+ goto out;
+ }
+
+ /* Find out the link percentage for each TC first */
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ p = &dcb_config->tc_config[i].path[direction];
+ bw_percent = dcb_config->bw_percentage[direction][p->bwg_id];
+
+ link_percentage = p->bwg_percent;
+ /* Must be careful of integer division for very small nums */
+ link_percentage = (link_percentage * bw_percent) / 100;
+ if (p->bwg_percent > 0 && link_percentage == 0)
+ link_percentage = 1;
+
+ /* Save link_percentage for reference */
+ p->link_percent = (u8)link_percentage;
+
+ /* Calculate credit refill and save it */
+ credit_refill = link_percentage * MINIMUM_CREDIT_REFILL;
+ p->data_credits_refill = (u16)credit_refill;
+
+ /* Calculate maximum credit for the TC */
+ credit_max = (link_percentage * MAX_CREDIT) / 100;
+
+ /*
+ * Adjustment based on rule checking, if the percentage
+ * of a TC is too small, the maximum credit may not be
+ * enough to send out a jumbo frame in data plane arbitration.
+ */
+ if (credit_max && (credit_max < MINIMUM_CREDIT_FOR_JUMBO))
+ credit_max = MINIMUM_CREDIT_FOR_JUMBO;
+
+ if (direction == DCB_TX_CONFIG) {
+ /*
+ * Adjustment based on rule checking, if the
+ * percentage of a TC is too small, the maximum
+ * credit may not be enough to send out a TSO
+ * packet in descriptor plane arbitration.
+ */
+ if (credit_max &&
+ (credit_max < MINIMUM_CREDIT_FOR_TSO))
+ credit_max = MINIMUM_CREDIT_FOR_TSO;
+
+ dcb_config->tc_config[i].desc_credits_max =
+ (u16)credit_max;
+ }
+
+ p->data_credits_max = (u16)credit_max;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * ixgbe_dcb_get_tc_stats - Returns status of each traffic class
+ * @hw: pointer to hardware structure
+ * @stats: pointer to statistics structure
+ * @tc_count: Number of elements in bwg_array.
+ *
+ * This function returns the status data for each of the Traffic Classes in use.
+ */
+s32 ixgbe_dcb_get_tc_stats(struct ixgbe_hw *hw, struct ixgbe_hw_stats *stats,
+ u8 tc_count)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_get_tc_stats_82598(hw, stats, tc_count);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_get_pfc_stats - Returns CBFC status of each traffic class
+ * hw - pointer to hardware structure
+ * stats - pointer to statistics structure
+ * tc_count - Number of elements in bwg_array.
+ *
+ * This function returns the CBFC status data for each of the Traffic Classes.
+ */
+s32 ixgbe_dcb_get_pfc_stats(struct ixgbe_hw *hw, struct ixgbe_hw_stats *stats,
+ u8 tc_count)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_get_pfc_stats_82598(hw, stats, tc_count);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_config_rx_arbiter - Config Rx arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Rx Data Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_rx_arbiter(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_config_rx_arbiter_82598(hw, dcb_config);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_config_tx_desc_arbiter - Config Tx Desc arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Tx Descriptor Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_tx_desc_arbiter(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_config_tx_desc_arbiter_82598(hw, dcb_config);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_config_tx_data_arbiter - Config Tx data arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Tx Data Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_tx_data_arbiter(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_config_tx_data_arbiter_82598(hw, dcb_config);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_config_pfc - Config priority flow control
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Priority Flow Control for each traffic class.
+ */
+s32 ixgbe_dcb_config_pfc(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_config_pfc_82598(hw, dcb_config);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_config_tc_stats - Config traffic class statistics
+ * @hw: pointer to hardware structure
+ *
+ * Configure queue statistics registers, all queues belonging to same traffic
+ * class uses a single set of queue statistics counters.
+ */
+s32 ixgbe_dcb_config_tc_stats(struct ixgbe_hw *hw)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_config_tc_stats_82598(hw);
+ return ret;
+}
+
+/**
+ * ixgbe_dcb_hw_config - Config and enable DCB
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure dcb settings and enable dcb mode.
+ */
+s32 ixgbe_dcb_hw_config(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret = 0;
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ ret = ixgbe_dcb_hw_config_82598(hw, dcb_config);
+ return ret;
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_dcb.h b/drivers/net/ixgbe/ixgbe_dcb.h
new file mode 100644
index 000000000000..75f6efe1e369
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_dcb.h
@@ -0,0 +1,184 @@
+/*******************************************************************************
+
+ Intel 10 Gigabit PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _DCB_CONFIG_H_
+#define _DCB_CONFIG_H_
+
+#include "ixgbe_type.h"
+
+/* DCB data structures */
+
+#define IXGBE_MAX_PACKET_BUFFERS 8
+#define MAX_USER_PRIORITY 8
+#define MAX_TRAFFIC_CLASS 8
+#define MAX_BW_GROUP 8
+#define BW_PERCENT 100
+
+#define DCB_TX_CONFIG 0
+#define DCB_RX_CONFIG 1
+
+/* DCB error Codes */
+#define DCB_SUCCESS 0
+#define DCB_ERR_CONFIG -1
+#define DCB_ERR_PARAM -2
+
+/* Transmit and receive Errors */
+/* Error in bandwidth group allocation */
+#define DCB_ERR_BW_GROUP -3
+/* Error in traffic class bandwidth allocation */
+#define DCB_ERR_TC_BW -4
+/* Traffic class has both link strict and group strict enabled */
+#define DCB_ERR_LS_GS -5
+/* Link strict traffic class has non zero bandwidth */
+#define DCB_ERR_LS_BW_NONZERO -6
+/* Link strict bandwidth group has non zero bandwidth */
+#define DCB_ERR_LS_BWG_NONZERO -7
+/* Traffic class has zero bandwidth */
+#define DCB_ERR_TC_BW_ZERO -8
+
+#define DCB_NOT_IMPLEMENTED 0x7FFFFFFF
+
+struct dcb_pfc_tc_debug {
+ u8 tc;
+ u8 pause_status;
+ u64 pause_quanta;
+};
+
+enum strict_prio_type {
+ prio_none = 0,
+ prio_group,
+ prio_link
+};
+
+/* Traffic class bandwidth allocation per direction */
+struct tc_bw_alloc {
+ u8 bwg_id; /* Bandwidth Group (BWG) ID */
+ u8 bwg_percent; /* % of BWG's bandwidth */
+ u8 link_percent; /* % of link bandwidth */
+ u8 up_to_tc_bitmap; /* User Priority to Traffic Class mapping */
+ u16 data_credits_refill; /* Credit refill amount in 64B granularity */
+ u16 data_credits_max; /* Max credits for a configured packet buffer
+ * in 64B granularity.*/
+ enum strict_prio_type prio_type; /* Link or Group Strict Priority */
+};
+
+enum dcb_pfc_type {
+ pfc_disabled = 0,
+ pfc_enabled_full,
+ pfc_enabled_tx,
+ pfc_enabled_rx
+};
+
+/* Traffic class configuration */
+struct tc_configuration {
+ struct tc_bw_alloc path[2]; /* One each for Tx/Rx */
+ enum dcb_pfc_type dcb_pfc; /* Class based flow control setting */
+
+ u16 desc_credits_max; /* For Tx Descriptor arbitration */
+ u8 tc; /* Traffic class (TC) */
+};
+
+enum dcb_rx_pba_cfg {
+ pba_equal, /* PBA[0-7] each use 64KB FIFO */
+ pba_80_48 /* PBA[0-3] each use 80KB, PBA[4-7] each use 48KB */
+};
+
+/*
+ * This structure contains many values encoded as fixed-point
+ * numbers, meaning that some of bits are dedicated to the
+ * magnitude and others to the fraction part. In the comments
+ * this is shown as f=n, where n is the number of fraction bits.
+ * These fraction bits are always the low-order bits. The size
+ * of the magnitude is not specified.
+ */
+struct bcn_config {
+ u32 rp_admin_mode[MAX_TRAFFIC_CLASS]; /* BCN enabled, per TC */
+ u32 bcna_option[2]; /* BCNA Port + MAC Addr */
+ u32 rp_w; /* Derivative Weight, f=3 */
+ u32 rp_gi; /* Increase Gain, f=12 */
+ u32 rp_gd; /* Decrease Gain, f=12 */
+ u32 rp_ru; /* Rate Unit */
+ u32 rp_alpha; /* Max Decrease Factor, f=12 */
+ u32 rp_beta; /* Max Increase Factor, f=12 */
+ u32 rp_ri; /* Initial Rate */
+ u32 rp_td; /* Drift Interval Timer */
+ u32 rp_rd; /* Drift Increase */
+ u32 rp_tmax; /* Severe Congestion Backoff Timer Range */
+ u32 rp_rmin; /* Severe Congestion Restart Rate */
+ u32 rp_wrtt; /* RTT Moving Average Weight */
+};
+
+struct ixgbe_dcb_config {
+ struct bcn_config bcn;
+
+ struct tc_configuration tc_config[MAX_TRAFFIC_CLASS];
+ u8 bw_percentage[2][MAX_BW_GROUP]; /* One each for Tx/Rx */
+
+ bool round_robin_enable;
+
+ enum dcb_rx_pba_cfg rx_pba_cfg;
+
+ u32 dcb_cfg_version; /* Not used...OS-specific? */
+ u32 link_speed; /* For bandwidth allocation validation purpose */
+};
+
+/* DCB driver APIs */
+
+/* DCB rule checking function.*/
+s32 ixgbe_dcb_check_config(struct ixgbe_dcb_config *config);
+
+/* DCB credits calculation */
+s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_dcb_config *, u8);
+
+/* DCB PFC functions */
+s32 ixgbe_dcb_config_pfc(struct ixgbe_hw *, struct ixgbe_dcb_config *g);
+s32 ixgbe_dcb_get_pfc_stats(struct ixgbe_hw *, struct ixgbe_hw_stats *, u8);
+
+/* DCB traffic class stats */
+s32 ixgbe_dcb_config_tc_stats(struct ixgbe_hw *);
+s32 ixgbe_dcb_get_tc_stats(struct ixgbe_hw *, struct ixgbe_hw_stats *, u8);
+
+/* DCB config arbiters */
+s32 ixgbe_dcb_config_tx_desc_arbiter(struct ixgbe_hw *,
+ struct ixgbe_dcb_config *);
+s32 ixgbe_dcb_config_tx_data_arbiter(struct ixgbe_hw *,
+ struct ixgbe_dcb_config *);
+s32 ixgbe_dcb_config_rx_arbiter(struct ixgbe_hw *, struct ixgbe_dcb_config *);
+
+/* DCB hw initialization */
+s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *);
+
+/* DCB definitions for credit calculation */
+#define MAX_CREDIT_REFILL 511 /* 0x1FF * 64B = 32704B */
+#define MINIMUM_CREDIT_REFILL 5 /* 5*64B = 320B */
+#define MINIMUM_CREDIT_FOR_JUMBO 145 /* 145= UpperBound((9*1024+54)/64B) for 9KB jumbo frame */
+#define DCB_MAX_TSO_SIZE (32*1024) /* MAX TSO packet size supported in DCB mode */
+#define MINIMUM_CREDIT_FOR_TSO (DCB_MAX_TSO_SIZE/64 + 1) /* 513 for 32KB TSO packet */
+#define MAX_CREDIT 4095 /* Maximum credit supported: 256KB * 1204 / 64B */
+
+#endif /* _DCB_CONFIG_H */
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ixgbe/ixgbe_dcb_82598.c
new file mode 100644
index 000000000000..2c046b0b5d28
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_dcb_82598.c
@@ -0,0 +1,398 @@
+/*******************************************************************************
+
+ Intel 10 Gigabit PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe.h"
+#include "ixgbe_type.h"
+#include "ixgbe_dcb.h"
+#include "ixgbe_dcb_82598.h"
+
+/**
+ * ixgbe_dcb_get_tc_stats_82598 - Return status data for each traffic class
+ * @hw: pointer to hardware structure
+ * @stats: pointer to statistics structure
+ * @tc_count: Number of elements in bwg_array.
+ *
+ * This function returns the status data for each of the Traffic Classes in use.
+ */
+s32 ixgbe_dcb_get_tc_stats_82598(struct ixgbe_hw *hw,
+ struct ixgbe_hw_stats *stats,
+ u8 tc_count)
+{
+ int tc;
+
+ if (tc_count > MAX_TRAFFIC_CLASS)
+ return DCB_ERR_PARAM;
+
+ /* Statistics pertaining to each traffic class */
+ for (tc = 0; tc < tc_count; tc++) {
+ /* Transmitted Packets */
+ stats->qptc[tc] += IXGBE_READ_REG(hw, IXGBE_QPTC(tc));
+ /* Transmitted Bytes */
+ stats->qbtc[tc] += IXGBE_READ_REG(hw, IXGBE_QBTC(tc));
+ /* Received Packets */
+ stats->qprc[tc] += IXGBE_READ_REG(hw, IXGBE_QPRC(tc));
+ /* Received Bytes */
+ stats->qbrc[tc] += IXGBE_READ_REG(hw, IXGBE_QBRC(tc));
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_get_pfc_stats_82598 - Returns CBFC status data
+ * @hw: pointer to hardware structure
+ * @stats: pointer to statistics structure
+ * @tc_count: Number of elements in bwg_array.
+ *
+ * This function returns the CBFC status data for each of the Traffic Classes.
+ */
+s32 ixgbe_dcb_get_pfc_stats_82598(struct ixgbe_hw *hw,
+ struct ixgbe_hw_stats *stats,
+ u8 tc_count)
+{
+ int tc;
+
+ if (tc_count > MAX_TRAFFIC_CLASS)
+ return DCB_ERR_PARAM;
+
+ for (tc = 0; tc < tc_count; tc++) {
+ /* Priority XOFF Transmitted */
+ stats->pxofftxc[tc] += IXGBE_READ_REG(hw, IXGBE_PXOFFTXC(tc));
+ /* Priority XOFF Received */
+ stats->pxoffrxc[tc] += IXGBE_READ_REG(hw, IXGBE_PXOFFRXC(tc));
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_config_packet_buffers_82598 - Configure packet buffers
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure packet buffers for DCB mode.
+ */
+static s32 ixgbe_dcb_config_packet_buffers_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ s32 ret_val = 0;
+ u32 value = IXGBE_RXPBSIZE_64KB;
+ u8 i = 0;
+
+ /* Setup Rx packet buffer sizes */
+ switch (dcb_config->rx_pba_cfg) {
+ case pba_80_48:
+ /* Setup the first four at 80KB */
+ value = IXGBE_RXPBSIZE_80KB;
+ for (; i < 4; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), value);
+ /* Setup the last four at 48KB...don't re-init i */
+ value = IXGBE_RXPBSIZE_48KB;
+ /* Fall Through */
+ case pba_equal:
+ default:
+ for (; i < IXGBE_MAX_PACKET_BUFFERS; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), value);
+
+ /* Setup Tx packet buffer sizes */
+ for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_TXPBSIZE(i),
+ IXGBE_TXPBSIZE_40KB);
+ }
+ break;
+ }
+
+ return ret_val;
+}
+
+/**
+ * ixgbe_dcb_config_rx_arbiter_82598 - Config Rx data arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Rx Data Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ struct tc_bw_alloc *p;
+ u32 reg = 0;
+ u32 credit_refill = 0;
+ u32 credit_max = 0;
+ u8 i = 0;
+
+ reg = IXGBE_READ_REG(hw, IXGBE_RUPPBMR) | IXGBE_RUPPBMR_MQA;
+ IXGBE_WRITE_REG(hw, IXGBE_RUPPBMR, reg);
+
+ reg = IXGBE_READ_REG(hw, IXGBE_RMCS);
+ /* Enable Arbiter */
+ reg &= ~IXGBE_RMCS_ARBDIS;
+ /* Enable Receive Recycle within the BWG */
+ reg |= IXGBE_RMCS_RRM;
+ /* Enable Deficit Fixed Priority arbitration*/
+ reg |= IXGBE_RMCS_DFP;
+
+ IXGBE_WRITE_REG(hw, IXGBE_RMCS, reg);
+
+ /* Configure traffic class credits and priority */
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ p = &dcb_config->tc_config[i].path[DCB_RX_CONFIG];
+ credit_refill = p->data_credits_refill;
+ credit_max = p->data_credits_max;
+
+ reg = credit_refill | (credit_max << IXGBE_RT2CR_MCL_SHIFT);
+
+ if (p->prio_type == prio_link)
+ reg |= IXGBE_RT2CR_LSP;
+
+ IXGBE_WRITE_REG(hw, IXGBE_RT2CR(i), reg);
+ }
+
+ reg = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
+ reg |= IXGBE_RDRXCTL_RDMTS_1_2;
+ reg |= IXGBE_RDRXCTL_MPBEN;
+ reg |= IXGBE_RDRXCTL_MCEN;
+ IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, reg);
+
+ reg = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+ /* Make sure there is enough descriptors before arbitration */
+ reg &= ~IXGBE_RXCTRL_DMBYPS;
+ IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg);
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_config_tx_desc_arbiter_82598 - Config Tx Desc. arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Tx Descriptor Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ struct tc_bw_alloc *p;
+ u32 reg, max_credits;
+ u8 i;
+
+ reg = IXGBE_READ_REG(hw, IXGBE_DPMCS);
+
+ /* Enable arbiter */
+ reg &= ~IXGBE_DPMCS_ARBDIS;
+ if (!(dcb_config->round_robin_enable)) {
+ /* Enable DFP and Recycle mode */
+ reg |= (IXGBE_DPMCS_TDPAC | IXGBE_DPMCS_TRM);
+ }
+ reg |= IXGBE_DPMCS_TSOEF;
+ /* Configure Max TSO packet size 34KB including payload and headers */
+ reg |= (0x4 << IXGBE_DPMCS_MTSOS_SHIFT);
+
+ IXGBE_WRITE_REG(hw, IXGBE_DPMCS, reg);
+
+ /* Configure traffic class credits and priority */
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ p = &dcb_config->tc_config[i].path[DCB_TX_CONFIG];
+ max_credits = dcb_config->tc_config[i].desc_credits_max;
+ reg = max_credits << IXGBE_TDTQ2TCCR_MCL_SHIFT;
+ reg |= p->data_credits_refill;
+ reg |= (u32)(p->bwg_id) << IXGBE_TDTQ2TCCR_BWG_SHIFT;
+
+ if (p->prio_type == prio_group)
+ reg |= IXGBE_TDTQ2TCCR_GSP;
+
+ if (p->prio_type == prio_link)
+ reg |= IXGBE_TDTQ2TCCR_LSP;
+
+ IXGBE_WRITE_REG(hw, IXGBE_TDTQ2TCCR(i), reg);
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_config_tx_data_arbiter_82598 - Config Tx data arbiter
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Tx Data Arbiter and credits for each traffic class.
+ */
+s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ struct tc_bw_alloc *p;
+ u32 reg;
+ u8 i;
+
+ reg = IXGBE_READ_REG(hw, IXGBE_PDPMCS);
+ /* Enable Data Plane Arbiter */
+ reg &= ~IXGBE_PDPMCS_ARBDIS;
+ /* Enable DFP and Transmit Recycle Mode */
+ reg |= (IXGBE_PDPMCS_TPPAC | IXGBE_PDPMCS_TRM);
+
+ IXGBE_WRITE_REG(hw, IXGBE_PDPMCS, reg);
+
+ /* Configure traffic class credits and priority */
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ p = &dcb_config->tc_config[i].path[DCB_TX_CONFIG];
+ reg = p->data_credits_refill;
+ reg |= (u32)(p->data_credits_max) << IXGBE_TDPT2TCCR_MCL_SHIFT;
+ reg |= (u32)(p->bwg_id) << IXGBE_TDPT2TCCR_BWG_SHIFT;
+
+ if (p->prio_type == prio_group)
+ reg |= IXGBE_TDPT2TCCR_GSP;
+
+ if (p->prio_type == prio_link)
+ reg |= IXGBE_TDPT2TCCR_LSP;
+
+ IXGBE_WRITE_REG(hw, IXGBE_TDPT2TCCR(i), reg);
+ }
+
+ /* Enable Tx packet buffer division */
+ reg = IXGBE_READ_REG(hw, IXGBE_DTXCTL);
+ reg |= IXGBE_DTXCTL_ENDBUBD;
+ IXGBE_WRITE_REG(hw, IXGBE_DTXCTL, reg);
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_config_pfc_82598 - Config priority flow control
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure Priority Flow Control for each traffic class.
+ */
+s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ u32 reg, rx_pba_size;
+ u8 i;
+
+ /* Enable Transmit Priority Flow Control */
+ reg = IXGBE_READ_REG(hw, IXGBE_RMCS);
+ reg &= ~IXGBE_RMCS_TFCE_802_3X;
+ /* correct the reporting of our flow control status */
+ hw->fc.type = ixgbe_fc_none;
+ reg |= IXGBE_RMCS_TFCE_PRIORITY;
+ IXGBE_WRITE_REG(hw, IXGBE_RMCS, reg);
+
+ /* Enable Receive Priority Flow Control */
+ reg = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+ reg &= ~IXGBE_FCTRL_RFCE;
+ reg |= IXGBE_FCTRL_RPFCE;
+ IXGBE_WRITE_REG(hw, IXGBE_FCTRL, reg);
+
+ /*
+ * Configure flow control thresholds and enable priority flow control
+ * for each traffic class.
+ */
+ for (i = 0; i < MAX_TRAFFIC_CLASS; i++) {
+ if (dcb_config->rx_pba_cfg == pba_equal) {
+ rx_pba_size = IXGBE_RXPBSIZE_64KB;
+ } else {
+ rx_pba_size = (i < 4) ? IXGBE_RXPBSIZE_80KB
+ : IXGBE_RXPBSIZE_48KB;
+ }
+
+ reg = ((rx_pba_size >> 5) & 0xFFF0);
+ if (dcb_config->tc_config[i].dcb_pfc == pfc_enabled_tx ||
+ dcb_config->tc_config[i].dcb_pfc == pfc_enabled_full)
+ reg |= IXGBE_FCRTL_XONE;
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTL(i), reg);
+
+ reg = ((rx_pba_size >> 2) & 0xFFF0);
+ if (dcb_config->tc_config[i].dcb_pfc == pfc_enabled_tx ||
+ dcb_config->tc_config[i].dcb_pfc == pfc_enabled_full)
+ reg |= IXGBE_FCRTH_FCEN;
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTH(i), reg);
+ }
+
+ /* Configure pause time */
+ for (i = 0; i < (MAX_TRAFFIC_CLASS >> 1); i++)
+ IXGBE_WRITE_REG(hw, IXGBE_FCTTV(i), 0x68006800);
+
+ /* Configure flow control refresh threshold value */
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTV, 0x3400);
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_config_tc_stats_82598 - Configure traffic class statistics
+ * @hw: pointer to hardware structure
+ *
+ * Configure queue statistics registers, all queues belonging to same traffic
+ * class uses a single set of queue statistics counters.
+ */
+s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *hw)
+{
+ u32 reg = 0;
+ u8 i = 0;
+ u8 j = 0;
+
+ /* Receive Queues stats setting - 8 queues per statistics reg */
+ for (i = 0, j = 0; i < 15 && j < 8; i = i + 2, j++) {
+ reg = IXGBE_READ_REG(hw, IXGBE_RQSMR(i));
+ reg |= ((0x1010101) * j);
+ IXGBE_WRITE_REG(hw, IXGBE_RQSMR(i), reg);
+ reg = IXGBE_READ_REG(hw, IXGBE_RQSMR(i + 1));
+ reg |= ((0x1010101) * j);
+ IXGBE_WRITE_REG(hw, IXGBE_RQSMR(i + 1), reg);
+ }
+ /* Transmit Queues stats setting - 4 queues per statistics reg */
+ for (i = 0; i < 8; i++) {
+ reg = IXGBE_READ_REG(hw, IXGBE_TQSMR(i));
+ reg |= ((0x1010101) * i);
+ IXGBE_WRITE_REG(hw, IXGBE_TQSMR(i), reg);
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_dcb_hw_config_82598 - Config and enable DCB
+ * @hw: pointer to hardware structure
+ * @dcb_config: pointer to ixgbe_dcb_config structure
+ *
+ * Configure dcb settings and enable dcb mode.
+ */
+s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *hw,
+ struct ixgbe_dcb_config *dcb_config)
+{
+ ixgbe_dcb_config_packet_buffers_82598(hw, dcb_config);
+ ixgbe_dcb_config_rx_arbiter_82598(hw, dcb_config);
+ ixgbe_dcb_config_tx_desc_arbiter_82598(hw, dcb_config);
+ ixgbe_dcb_config_tx_data_arbiter_82598(hw, dcb_config);
+ ixgbe_dcb_config_pfc_82598(hw, dcb_config);
+ ixgbe_dcb_config_tc_stats_82598(hw);
+
+ return 0;
+}
diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ixgbe/ixgbe_dcb_82598.h
new file mode 100644
index 000000000000..1e6a313719d7
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_dcb_82598.h
@@ -0,0 +1,94 @@
+/*******************************************************************************
+
+ Intel 10 Gigabit PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _DCB_82598_CONFIG_H_
+#define _DCB_82598_CONFIG_H_
+
+/* DCB register definitions */
+
+#define IXGBE_DPMCS_MTSOS_SHIFT 16
+#define IXGBE_DPMCS_TDPAC 0x00000001 /* 0 Round Robin, 1 DFP - Deficit Fixed Priority */
+#define IXGBE_DPMCS_TRM 0x00000010 /* Transmit Recycle Mode */
+#define IXGBE_DPMCS_ARBDIS 0x00000040 /* DCB arbiter disable */
+#define IXGBE_DPMCS_TSOEF 0x00080000 /* TSO Expand Factor: 0=x4, 1=x2 */
+
+#define IXGBE_RUPPBMR_MQA 0x80000000 /* Enable UP to queue mapping */
+
+#define IXGBE_RT2CR_MCL_SHIFT 12 /* Offset to Max Credit Limit setting */
+#define IXGBE_RT2CR_LSP 0x80000000 /* LSP enable bit */
+
+#define IXGBE_RDRXCTL_MPBEN 0x00000010 /* DMA config for multiple packet buffers enable */
+#define IXGBE_RDRXCTL_MCEN 0x00000040 /* DMA config for multiple cores (RSS) enable */
+
+#define IXGBE_TDTQ2TCCR_MCL_SHIFT 12
+#define IXGBE_TDTQ2TCCR_BWG_SHIFT 9
+#define IXGBE_TDTQ2TCCR_GSP 0x40000000
+#define IXGBE_TDTQ2TCCR_LSP 0x80000000
+
+#define IXGBE_TDPT2TCCR_MCL_SHIFT 12
+#define IXGBE_TDPT2TCCR_BWG_SHIFT 9
+#define IXGBE_TDPT2TCCR_GSP 0x40000000
+#define IXGBE_TDPT2TCCR_LSP 0x80000000
+
+#define IXGBE_PDPMCS_TPPAC 0x00000020 /* 0 Round Robin, 1 for DFP - Deficit Fixed Priority */
+#define IXGBE_PDPMCS_ARBDIS 0x00000040 /* Arbiter disable */
+#define IXGBE_PDPMCS_TRM 0x00000100 /* Transmit Recycle Mode enable */
+
+#define IXGBE_DTXCTL_ENDBUBD 0x00000004 /* Enable DBU buffer division */
+
+#define IXGBE_TXPBSIZE_40KB 0x0000A000 /* 40KB Packet Buffer */
+#define IXGBE_RXPBSIZE_48KB 0x0000C000 /* 48KB Packet Buffer */
+#define IXGBE_RXPBSIZE_64KB 0x00010000 /* 64KB Packet Buffer */
+#define IXGBE_RXPBSIZE_80KB 0x00014000 /* 80KB Packet Buffer */
+
+#define IXGBE_RDRXCTL_RDMTS_1_2 0x00000000
+
+/* DCB hardware-specific driver APIs */
+
+/* DCB PFC functions */
+s32 ixgbe_dcb_config_pfc_82598(struct ixgbe_hw *, struct ixgbe_dcb_config *);
+s32 ixgbe_dcb_get_pfc_stats_82598(struct ixgbe_hw *, struct ixgbe_hw_stats *,
+ u8);
+
+/* DCB traffic class stats */
+s32 ixgbe_dcb_config_tc_stats_82598(struct ixgbe_hw *);
+s32 ixgbe_dcb_get_tc_stats_82598(struct ixgbe_hw *, struct ixgbe_hw_stats *,
+ u8);
+
+/* DCB config arbiters */
+s32 ixgbe_dcb_config_tx_desc_arbiter_82598(struct ixgbe_hw *,
+ struct ixgbe_dcb_config *);
+s32 ixgbe_dcb_config_tx_data_arbiter_82598(struct ixgbe_hw *,
+ struct ixgbe_dcb_config *);
+s32 ixgbe_dcb_config_rx_arbiter_82598(struct ixgbe_hw *,
+ struct ixgbe_dcb_config *);
+
+/* DCB hw initialization */
+s32 ixgbe_dcb_hw_config_82598(struct ixgbe_hw *, struct ixgbe_dcb_config *);
+
+#endif /* _DCB_82598_CONFIG_H */
diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c
new file mode 100644
index 000000000000..4129976953f5
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c
@@ -0,0 +1,641 @@
+/*******************************************************************************
+
+ Intel 10 Gigabit PCI Express Linux driver
+ Copyright(c) 1999 - 2008 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe.h"
+#include <linux/dcbnl.h>
+
+/* Callbacks for DCB netlink in the kernel */
+#define BIT_DCB_MODE 0x01
+#define BIT_PFC 0x02
+#define BIT_PG_RX 0x04
+#define BIT_PG_TX 0x08
+#define BIT_BCN 0x10
+
+int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
+ struct ixgbe_dcb_config *dst_dcb_cfg, int tc_max)
+{
+ struct tc_configuration *src_tc_cfg = NULL;
+ struct tc_configuration *dst_tc_cfg = NULL;
+ int i;
+
+ if (!src_dcb_cfg || !dst_dcb_cfg)
+ return -EINVAL;
+
+ for (i = DCB_PG_ATTR_TC_0; i < tc_max + DCB_PG_ATTR_TC_0; i++) {
+ src_tc_cfg = &src_dcb_cfg->tc_config[i - DCB_PG_ATTR_TC_0];
+ dst_tc_cfg = &dst_dcb_cfg->tc_config[i - DCB_PG_ATTR_TC_0];
+
+ dst_tc_cfg->path[DCB_TX_CONFIG].prio_type =
+ src_tc_cfg->path[DCB_TX_CONFIG].prio_type;
+
+ dst_tc_cfg->path[DCB_TX_CONFIG].bwg_id =
+ src_tc_cfg->path[DCB_TX_CONFIG].bwg_id;
+
+ dst_tc_cfg->path[DCB_TX_CONFIG].bwg_percent =
+ src_tc_cfg->path[DCB_TX_CONFIG].bwg_percent;
+
+ dst_tc_cfg->path[DCB_TX_CONFIG].up_to_tc_bitmap =
+ src_tc_cfg->path[DCB_TX_CONFIG].up_to_tc_bitmap;
+
+ dst_tc_cfg->path[DCB_RX_CONFIG].prio_type =
+ src_tc_cfg->path[DCB_RX_CONFIG].prio_type;
+
+ dst_tc_cfg->path[DCB_RX_CONFIG].bwg_id =
+ src_tc_cfg->path[DCB_RX_CONFIG].bwg_id;
+
+ dst_tc_cfg->path[DCB_RX_CONFIG].bwg_percent =
+ src_tc_cfg->path[DCB_RX_CONFIG].bwg_percent;
+
+ dst_tc_cfg->path[DCB_RX_CONFIG].up_to_tc_bitmap =
+ src_tc_cfg->path[DCB_RX_CONFIG].up_to_tc_bitmap;
+ }
+
+ for (i = DCB_PG_ATTR_BW_ID_0; i < DCB_PG_ATTR_BW_ID_MAX; i++) {
+ dst_dcb_cfg->bw_percentage[DCB_TX_CONFIG]
+ [i-DCB_PG_ATTR_BW_ID_0] = src_dcb_cfg->bw_percentage
+ [DCB_TX_CONFIG][i-DCB_PG_ATTR_BW_ID_0];
+ dst_dcb_cfg->bw_percentage[DCB_RX_CONFIG]
+ [i-DCB_PG_ATTR_BW_ID_0] = src_dcb_cfg->bw_percentage
+ [DCB_RX_CONFIG][i-DCB_PG_ATTR_BW_ID_0];
+ }
+
+ for (i = DCB_PFC_UP_ATTR_0; i < DCB_PFC_UP_ATTR_MAX; i++) {
+ dst_dcb_cfg->tc_config[i - DCB_PFC_UP_ATTR_0].dcb_pfc =
+ src_dcb_cfg->tc_config[i - DCB_PFC_UP_ATTR_0].dcb_pfc;
+ }
+
+ for (i = DCB_BCN_ATTR_RP_0; i < DCB_BCN_ATTR_RP_ALL; i++) {
+ dst_dcb_cfg->bcn.rp_admin_mode[i - DCB_BCN_ATTR_RP_0] =
+ src_dcb_cfg->bcn.rp_admin_mode[i - DCB_BCN_ATTR_RP_0];
+ }
+ dst_dcb_cfg->bcn.bcna_option[0] = src_dcb_cfg->bcn.bcna_option[0];
+ dst_dcb_cfg->bcn.bcna_option[1] = src_dcb_cfg->bcn.bcna_option[1];
+ dst_dcb_cfg->bcn.rp_alpha = src_dcb_cfg->bcn.rp_alpha;
+ dst_dcb_cfg->bcn.rp_beta = src_dcb_cfg->bcn.rp_beta;
+ dst_dcb_cfg->bcn.rp_gd = src_dcb_cfg->bcn.rp_gd;
+ dst_dcb_cfg->bcn.rp_gi = src_dcb_cfg->bcn.rp_gi;
+ dst_dcb_cfg->bcn.rp_tmax = src_dcb_cfg->bcn.rp_tmax;
+ dst_dcb_cfg->bcn.rp_td = src_dcb_cfg->bcn.rp_td;
+ dst_dcb_cfg->bcn.rp_rmin = src_dcb_cfg->bcn.rp_rmin;
+ dst_dcb_cfg->bcn.rp_w = src_dcb_cfg->bcn.rp_w;
+ dst_dcb_cfg->bcn.rp_rd = src_dcb_cfg->bcn.rp_rd;
+ dst_dcb_cfg->bcn.rp_ru = src_dcb_cfg->bcn.rp_ru;
+ dst_dcb_cfg->bcn.rp_wrtt = src_dcb_cfg->bcn.rp_wrtt;
+ dst_dcb_cfg->bcn.rp_ri = src_dcb_cfg->bcn.rp_ri;
+
+ return 0;
+}
+
+static u8 ixgbe_dcbnl_get_state(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ DPRINTK(DRV, INFO, "Get DCB Admin Mode.\n");
+
+ return !!(adapter->flags & IXGBE_FLAG_DCB_ENABLED);
+}
+
+static u16 ixgbe_dcb_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+ /* All traffic should default to class 0 */
+ return 0;
+}
+
+static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state)
+{
+ u8 err = 0;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ DPRINTK(DRV, INFO, "Set DCB Admin Mode.\n");
+
+ if (state > 0) {
+ /* Turn on DCB */
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
+ goto out;
+
+ if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) {
+ DPRINTK(DRV, ERR, "Enable failed, needs MSI-X\n");
+ err = 1;
+ goto out;
+ }
+
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_stop(netdev);
+ ixgbe_reset_interrupt_capability(adapter);
+ ixgbe_napi_del_all(adapter);
+ INIT_LIST_HEAD(&netdev->napi_list);
+ kfree(adapter->tx_ring);
+ kfree(adapter->rx_ring);
+ adapter->tx_ring = NULL;
+ adapter->rx_ring = NULL;
+ netdev->select_queue = &ixgbe_dcb_select_queue;
+
+ adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED;
+ adapter->flags |= IXGBE_FLAG_DCB_ENABLED;
+ ixgbe_init_interrupt_scheme(adapter);
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_open(netdev);
+ } else {
+ /* Turn off DCB */
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_stop(netdev);
+ ixgbe_reset_interrupt_capability(adapter);
+ ixgbe_napi_del_all(adapter);
+ INIT_LIST_HEAD(&netdev->napi_list);
+ kfree(adapter->tx_ring);
+ kfree(adapter->rx_ring);
+ adapter->tx_ring = NULL;
+ adapter->rx_ring = NULL;
+ netdev->select_queue = NULL;
+
+ adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
+ adapter->flags |= IXGBE_FLAG_RSS_ENABLED;
+ ixgbe_init_interrupt_scheme(adapter);
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_open(netdev);
+ }
+ }
+out:
+ return err;
+}
+
+static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev,
+ u8 *perm_addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int i;
+
+ for (i = 0; i < netdev->addr_len; i++)
+ perm_addr[i] = adapter->hw.mac.perm_addr[i];
+}
+
+static void ixgbe_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
+ u8 prio, u8 bwg_id, u8 bw_pct,
+ u8 up_map)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (prio != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[0].prio_type = prio;
+ if (bwg_id != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_id = bwg_id;
+ if (bw_pct != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_percent =
+ bw_pct;
+ if (up_map != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap =
+ up_map;
+
+ if ((adapter->temp_dcb_cfg.tc_config[tc].path[0].prio_type !=
+ adapter->dcb_cfg.tc_config[tc].path[0].prio_type) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_id !=
+ adapter->dcb_cfg.tc_config[tc].path[0].bwg_id) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[0].bwg_percent !=
+ adapter->dcb_cfg.tc_config[tc].path[0].bwg_percent) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap !=
+ adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap))
+ adapter->dcb_set_bitmap |= BIT_PG_TX;
+}
+
+static void ixgbe_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
+ u8 bw_pct)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ adapter->temp_dcb_cfg.bw_percentage[0][bwg_id] = bw_pct;
+
+ if (adapter->temp_dcb_cfg.bw_percentage[0][bwg_id] !=
+ adapter->dcb_cfg.bw_percentage[0][bwg_id])
+ adapter->dcb_set_bitmap |= BIT_PG_RX;
+}
+
+static void ixgbe_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
+ u8 prio, u8 bwg_id, u8 bw_pct,
+ u8 up_map)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (prio != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[1].prio_type = prio;
+ if (bwg_id != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_id = bwg_id;
+ if (bw_pct != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_percent =
+ bw_pct;
+ if (up_map != DCB_ATTR_VALUE_UNDEFINED)
+ adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap =
+ up_map;
+
+ if ((adapter->temp_dcb_cfg.tc_config[tc].path[1].prio_type !=
+ adapter->dcb_cfg.tc_config[tc].path[1].prio_type) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_id !=
+ adapter->dcb_cfg.tc_config[tc].path[1].bwg_id) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[1].bwg_percent !=
+ adapter->dcb_cfg.tc_config[tc].path[1].bwg_percent) ||
+ (adapter->temp_dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap !=
+ adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap))
+ adapter->dcb_set_bitmap |= BIT_PG_RX;
+}
+
+static void ixgbe_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
+ u8 bw_pct)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ adapter->temp_dcb_cfg.bw_percentage[1][bwg_id] = bw_pct;
+
+ if (adapter->temp_dcb_cfg.bw_percentage[1][bwg_id] !=
+ adapter->dcb_cfg.bw_percentage[1][bwg_id])
+ adapter->dcb_set_bitmap |= BIT_PG_RX;
+}
+
+static void ixgbe_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
+ u8 *prio, u8 *bwg_id, u8 *bw_pct,
+ u8 *up_map)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *prio = adapter->dcb_cfg.tc_config[tc].path[0].prio_type;
+ *bwg_id = adapter->dcb_cfg.tc_config[tc].path[0].bwg_id;
+ *bw_pct = adapter->dcb_cfg.tc_config[tc].path[0].bwg_percent;
+ *up_map = adapter->dcb_cfg.tc_config[tc].path[0].up_to_tc_bitmap;
+}
+
+static void ixgbe_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
+ u8 *bw_pct)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *bw_pct = adapter->dcb_cfg.bw_percentage[0][bwg_id];
+}
+
+static void ixgbe_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
+ u8 *prio, u8 *bwg_id, u8 *bw_pct,
+ u8 *up_map)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *prio = adapter->dcb_cfg.tc_config[tc].path[1].prio_type;
+ *bwg_id = adapter->dcb_cfg.tc_config[tc].path[1].bwg_id;
+ *bw_pct = adapter->dcb_cfg.tc_config[tc].path[1].bwg_percent;
+ *up_map = adapter->dcb_cfg.tc_config[tc].path[1].up_to_tc_bitmap;
+}
+
+static void ixgbe_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
+ u8 *bw_pct)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *bw_pct = adapter->dcb_cfg.bw_percentage[1][bwg_id];
+}
+
+static void ixgbe_dcbnl_set_pfc_cfg(struct net_device *netdev, int priority,
+ u8 setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ adapter->temp_dcb_cfg.tc_config[priority].dcb_pfc = setting;
+ if (adapter->temp_dcb_cfg.tc_config[priority].dcb_pfc !=
+ adapter->dcb_cfg.tc_config[priority].dcb_pfc)
+ adapter->dcb_set_bitmap |= BIT_PFC;
+}
+
+static void ixgbe_dcbnl_get_pfc_cfg(struct net_device *netdev, int priority,
+ u8 *setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *setting = adapter->dcb_cfg.tc_config[priority].dcb_pfc;
+}
+
+static u8 ixgbe_dcbnl_set_all(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int ret;
+
+ adapter->dcb_set_bitmap &= ~BIT_BCN; /* no set for BCN */
+ if (!adapter->dcb_set_bitmap)
+ return 1;
+
+ while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
+ msleep(1);
+
+ if (netif_running(netdev))
+ ixgbe_down(adapter);
+
+ ret = ixgbe_copy_dcb_cfg(&adapter->temp_dcb_cfg, &adapter->dcb_cfg,
+ adapter->ring_feature[RING_F_DCB].indices);
+ if (ret) {
+ clear_bit(__IXGBE_RESETTING, &adapter->state);
+ return ret;
+ }
+
+ if (netif_running(netdev))
+ ixgbe_up(adapter);
+
+ adapter->dcb_set_bitmap = 0x00;
+ clear_bit(__IXGBE_RESETTING, &adapter->state);
+ return ret;
+}
+
+static u8 ixgbe_dcbnl_getcap(struct net_device *netdev, int capid, u8 *cap)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u8 rval = 0;
+
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ switch (capid) {
+ case DCB_CAP_ATTR_PG:
+ *cap = true;
+ break;
+ case DCB_CAP_ATTR_PFC:
+ *cap = true;
+ break;
+ case DCB_CAP_ATTR_UP2TC:
+ *cap = false;
+ break;
+ case DCB_CAP_ATTR_PG_TCS:
+ *cap = 0x80;
+ break;
+ case DCB_CAP_ATTR_PFC_TCS:
+ *cap = 0x80;
+ break;
+ case DCB_CAP_ATTR_GSP:
+ *cap = true;
+ break;
+ case DCB_CAP_ATTR_BCN:
+ *cap = false;
+ break;
+ default:
+ rval = -EINVAL;
+ break;
+ }
+ } else {
+ rval = -EINVAL;
+ }
+
+ return rval;
+}
+
+static u8 ixgbe_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u8 rval = 0;
+
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ switch (tcid) {
+ case DCB_NUMTCS_ATTR_PG:
+ *num = MAX_TRAFFIC_CLASS;
+ break;
+ case DCB_NUMTCS_ATTR_PFC:
+ *num = MAX_TRAFFIC_CLASS;
+ break;
+ default:
+ rval = -EINVAL;
+ break;
+ }
+ } else {
+ rval = -EINVAL;
+ }
+
+ return rval;
+}
+
+static u8 ixgbe_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num)
+{
+ return -EINVAL;
+}
+
+static u8 ixgbe_dcbnl_getpfcstate(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ return !!(adapter->flags & IXGBE_FLAG_DCB_ENABLED);
+}
+
+static void ixgbe_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
+{
+ return;
+}
+
+static void ixgbe_dcbnl_getbcnrp(struct net_device *netdev, int priority,
+ u8 *setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ *setting = adapter->dcb_cfg.bcn.rp_admin_mode[priority];
+}
+
+
+static void ixgbe_dcbnl_getbcncfg(struct net_device *netdev, int enum_index,
+ u32 *setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ switch (enum_index) {
+ case DCB_BCN_ATTR_BCNA_0:
+ *setting = adapter->dcb_cfg.bcn.bcna_option[0];
+ break;
+ case DCB_BCN_ATTR_BCNA_1:
+ *setting = adapter->dcb_cfg.bcn.bcna_option[1];
+ break;
+ case DCB_BCN_ATTR_ALPHA:
+ *setting = adapter->dcb_cfg.bcn.rp_alpha;
+ break;
+ case DCB_BCN_ATTR_BETA:
+ *setting = adapter->dcb_cfg.bcn.rp_beta;
+ break;
+ case DCB_BCN_ATTR_GD:
+ *setting = adapter->dcb_cfg.bcn.rp_gd;
+ break;
+ case DCB_BCN_ATTR_GI:
+ *setting = adapter->dcb_cfg.bcn.rp_gi;
+ break;
+ case DCB_BCN_ATTR_TMAX:
+ *setting = adapter->dcb_cfg.bcn.rp_tmax;
+ break;
+ case DCB_BCN_ATTR_TD:
+ *setting = adapter->dcb_cfg.bcn.rp_td;
+ break;
+ case DCB_BCN_ATTR_RMIN:
+ *setting = adapter->dcb_cfg.bcn.rp_rmin;
+ break;
+ case DCB_BCN_ATTR_W:
+ *setting = adapter->dcb_cfg.bcn.rp_w;
+ break;
+ case DCB_BCN_ATTR_RD:
+ *setting = adapter->dcb_cfg.bcn.rp_rd;
+ break;
+ case DCB_BCN_ATTR_RU:
+ *setting = adapter->dcb_cfg.bcn.rp_ru;
+ break;
+ case DCB_BCN_ATTR_WRTT:
+ *setting = adapter->dcb_cfg.bcn.rp_wrtt;
+ break;
+ case DCB_BCN_ATTR_RI:
+ *setting = adapter->dcb_cfg.bcn.rp_ri;
+ break;
+ default:
+ *setting = -1;
+ }
+}
+
+static void ixgbe_dcbnl_setbcnrp(struct net_device *netdev, int priority,
+ u8 setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ adapter->temp_dcb_cfg.bcn.rp_admin_mode[priority] = setting;
+
+ if (adapter->temp_dcb_cfg.bcn.rp_admin_mode[priority] !=
+ adapter->dcb_cfg.bcn.rp_admin_mode[priority])
+ adapter->dcb_set_bitmap |= BIT_BCN;
+}
+
+static void ixgbe_dcbnl_setbcncfg(struct net_device *netdev, int enum_index,
+ u32 setting)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ switch (enum_index) {
+ case DCB_BCN_ATTR_BCNA_0:
+ adapter->temp_dcb_cfg.bcn.bcna_option[0] = setting;
+ if (adapter->temp_dcb_cfg.bcn.bcna_option[0] !=
+ adapter->dcb_cfg.bcn.bcna_option[0])
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_BCNA_1:
+ adapter->temp_dcb_cfg.bcn.bcna_option[1] = setting;
+ if (adapter->temp_dcb_cfg.bcn.bcna_option[1] !=
+ adapter->dcb_cfg.bcn.bcna_option[1])
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_ALPHA:
+ adapter->temp_dcb_cfg.bcn.rp_alpha = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_alpha !=
+ adapter->dcb_cfg.bcn.rp_alpha)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_BETA:
+ adapter->temp_dcb_cfg.bcn.rp_beta = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_beta !=
+ adapter->dcb_cfg.bcn.rp_beta)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_GD:
+ adapter->temp_dcb_cfg.bcn.rp_gd = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_gd !=
+ adapter->dcb_cfg.bcn.rp_gd)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_GI:
+ adapter->temp_dcb_cfg.bcn.rp_gi = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_gi !=
+ adapter->dcb_cfg.bcn.rp_gi)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_TMAX:
+ adapter->temp_dcb_cfg.bcn.rp_tmax = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_tmax !=
+ adapter->dcb_cfg.bcn.rp_tmax)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_TD:
+ adapter->temp_dcb_cfg.bcn.rp_td = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_td !=
+ adapter->dcb_cfg.bcn.rp_td)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_RMIN:
+ adapter->temp_dcb_cfg.bcn.rp_rmin = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_rmin !=
+ adapter->dcb_cfg.bcn.rp_rmin)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_W:
+ adapter->temp_dcb_cfg.bcn.rp_w = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_w !=
+ adapter->dcb_cfg.bcn.rp_w)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_RD:
+ adapter->temp_dcb_cfg.bcn.rp_rd = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_rd !=
+ adapter->dcb_cfg.bcn.rp_rd)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_RU:
+ adapter->temp_dcb_cfg.bcn.rp_ru = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_ru !=
+ adapter->dcb_cfg.bcn.rp_ru)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_WRTT:
+ adapter->temp_dcb_cfg.bcn.rp_wrtt = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_wrtt !=
+ adapter->dcb_cfg.bcn.rp_wrtt)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ case DCB_BCN_ATTR_RI:
+ adapter->temp_dcb_cfg.bcn.rp_ri = setting;
+ if (adapter->temp_dcb_cfg.bcn.rp_ri !=
+ adapter->dcb_cfg.bcn.rp_ri)
+ adapter->dcb_set_bitmap |= BIT_BCN;
+ break;
+ default:
+ break;
+ }
+}
+
+struct dcbnl_rtnl_ops dcbnl_ops = {
+ .getstate = ixgbe_dcbnl_get_state,
+ .setstate = ixgbe_dcbnl_set_state,
+ .getpermhwaddr = ixgbe_dcbnl_get_perm_hw_addr,
+ .setpgtccfgtx = ixgbe_dcbnl_set_pg_tc_cfg_tx,
+ .setpgbwgcfgtx = ixgbe_dcbnl_set_pg_bwg_cfg_tx,
+ .setpgtccfgrx = ixgbe_dcbnl_set_pg_tc_cfg_rx,
+ .setpgbwgcfgrx = ixgbe_dcbnl_set_pg_bwg_cfg_rx,
+ .getpgtccfgtx = ixgbe_dcbnl_get_pg_tc_cfg_tx,
+ .getpgbwgcfgtx = ixgbe_dcbnl_get_pg_bwg_cfg_tx,
+ .getpgtccfgrx = ixgbe_dcbnl_get_pg_tc_cfg_rx,
+ .getpgbwgcfgrx = ixgbe_dcbnl_get_pg_bwg_cfg_rx,
+ .setpfccfg = ixgbe_dcbnl_set_pfc_cfg,
+ .getpfccfg = ixgbe_dcbnl_get_pfc_cfg,
+ .setall = ixgbe_dcbnl_set_all,
+ .getcap = ixgbe_dcbnl_getcap,
+ .getnumtcs = ixgbe_dcbnl_getnumtcs,
+ .setnumtcs = ixgbe_dcbnl_setnumtcs,
+ .getpfcstate = ixgbe_dcbnl_getpfcstate,
+ .setpfcstate = ixgbe_dcbnl_setpfcstate,
+ .getbcncfg = ixgbe_dcbnl_getbcncfg,
+ .getbcnrp = ixgbe_dcbnl_getbcnrp,
+ .setbcncfg = ixgbe_dcbnl_setbcncfg,
+ .setbcnrp = ixgbe_dcbnl_setbcnrp
+};
+
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
index 81a9c4b86726..67f87a79154d 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -94,12 +94,21 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = {
};
#define IXGBE_QUEUE_STATS_LEN \
- ((((struct ixgbe_adapter *)netdev->priv)->num_tx_queues + \
- ((struct ixgbe_adapter *)netdev->priv)->num_rx_queues) * \
- (sizeof(struct ixgbe_queue_stats) / sizeof(u64)))
-#define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + IXGBE_QUEUE_STATS_LEN)
+ ((((struct ixgbe_adapter *)netdev_priv(netdev))->num_tx_queues + \
+ ((struct ixgbe_adapter *)netdev_priv(netdev))->num_rx_queues) * \
+ (sizeof(struct ixgbe_queue_stats) / sizeof(u64)))
#define IXGBE_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbe_gstrings_stats)
-#define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + IXGBE_QUEUE_STATS_LEN)
+#define IXGBE_PB_STATS_LEN ( \
+ (((struct ixgbe_adapter *)netdev_priv(netdev))->flags & \
+ IXGBE_FLAG_DCB_ENABLED) ? \
+ (sizeof(((struct ixgbe_adapter *)0)->stats.pxonrxc) + \
+ sizeof(((struct ixgbe_adapter *)0)->stats.pxontxc) + \
+ sizeof(((struct ixgbe_adapter *)0)->stats.pxoffrxc) + \
+ sizeof(((struct ixgbe_adapter *)0)->stats.pxofftxc)) \
+ / sizeof(u64) : 0)
+#define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + \
+ IXGBE_PB_STATS_LEN + \
+ IXGBE_QUEUE_STATS_LEN)
static int ixgbe_get_settings(struct net_device *netdev,
struct ethtool_cmd *ecmd)
@@ -149,6 +158,8 @@ static int ixgbe_set_settings(struct net_device *netdev,
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
+ u32 advertised, old;
+ s32 err;
switch (hw->phy.media_type) {
case ixgbe_media_type_fiber:
@@ -157,6 +168,31 @@ static int ixgbe_set_settings(struct net_device *netdev,
return -EINVAL;
/* in this case we currently only support 10Gb/FULL */
break;
+ case ixgbe_media_type_copper:
+ /* 10000/copper and 1000/copper must autoneg
+ * this function does not support any duplex forcing, but can
+ * limit the advertising of the adapter to only 10000 or 1000 */
+ if (ecmd->autoneg == AUTONEG_DISABLE)
+ return -EINVAL;
+
+ old = hw->phy.autoneg_advertised;
+ advertised = 0;
+ if (ecmd->advertising & ADVERTISED_10000baseT_Full)
+ advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+
+ if (ecmd->advertising & ADVERTISED_1000baseT_Full)
+ advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+
+ if (old == advertised)
+ break;
+ /* this sets the link speed and restarts auto-neg */
+ err = hw->mac.ops.setup_link_speed(hw, advertised, true, true);
+ if (err) {
+ DPRINTK(PROBE, INFO,
+ "setup link failed with code %d\n", err);
+ hw->mac.ops.setup_link_speed(hw, old, true, true);
+ }
+ break;
default:
break;
}
@@ -676,30 +712,15 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
return 0;
}
- if (adapter->num_tx_queues > adapter->num_rx_queues)
- temp_ring = vmalloc(adapter->num_tx_queues *
- sizeof(struct ixgbe_ring));
- else
- temp_ring = vmalloc(adapter->num_rx_queues *
- sizeof(struct ixgbe_ring));
+ temp_ring = kcalloc(adapter->num_tx_queues,
+ sizeof(struct ixgbe_ring), GFP_KERNEL);
if (!temp_ring)
return -ENOMEM;
while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state))
msleep(1);
- if (netif_running(netdev))
- ixgbe_down(adapter);
-
- /*
- * We can't just free everything and then setup again,
- * because the ISRs in MSI-X mode get passed pointers
- * to the tx and rx ring structs.
- */
if (new_tx_count != adapter->tx_ring->count) {
- memcpy(temp_ring, adapter->tx_ring,
- adapter->num_tx_queues * sizeof(struct ixgbe_ring));
-
for (i = 0; i < adapter->num_tx_queues; i++) {
temp_ring[i].count = new_tx_count;
err = ixgbe_setup_tx_resources(adapter, &temp_ring[i]);
@@ -711,21 +732,28 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
}
goto err_setup;
}
+ temp_ring[i].v_idx = adapter->tx_ring[i].v_idx;
}
-
- for (i = 0; i < adapter->num_tx_queues; i++)
- ixgbe_free_tx_resources(adapter, &adapter->tx_ring[i]);
-
- memcpy(adapter->tx_ring, temp_ring,
- adapter->num_tx_queues * sizeof(struct ixgbe_ring));
-
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_stop(netdev);
+ ixgbe_reset_interrupt_capability(adapter);
+ ixgbe_napi_del_all(adapter);
+ INIT_LIST_HEAD(&netdev->napi_list);
+ kfree(adapter->tx_ring);
+ adapter->tx_ring = temp_ring;
+ temp_ring = NULL;
adapter->tx_ring_count = new_tx_count;
}
- if (new_rx_count != adapter->rx_ring->count) {
- memcpy(temp_ring, adapter->rx_ring,
- adapter->num_rx_queues * sizeof(struct ixgbe_ring));
+ temp_ring = kcalloc(adapter->num_rx_queues,
+ sizeof(struct ixgbe_ring), GFP_KERNEL);
+ if (!temp_ring) {
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_open(netdev);
+ return -ENOMEM;
+ }
+ if (new_rx_count != adapter->rx_ring->count) {
for (i = 0; i < adapter->num_rx_queues; i++) {
temp_ring[i].count = new_rx_count;
err = ixgbe_setup_rx_resources(adapter, &temp_ring[i]);
@@ -737,13 +765,16 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
}
goto err_setup;
}
+ temp_ring[i].v_idx = adapter->rx_ring[i].v_idx;
}
-
- for (i = 0; i < adapter->num_rx_queues; i++)
- ixgbe_free_rx_resources(adapter, &adapter->rx_ring[i]);
-
- memcpy(adapter->rx_ring, temp_ring,
- adapter->num_rx_queues * sizeof(struct ixgbe_ring));
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_stop(netdev);
+ ixgbe_reset_interrupt_capability(adapter);
+ ixgbe_napi_del_all(adapter);
+ INIT_LIST_HEAD(&netdev->napi_list);
+ kfree(adapter->rx_ring);
+ adapter->rx_ring = temp_ring;
+ temp_ring = NULL;
adapter->rx_ring_count = new_rx_count;
}
@@ -751,8 +782,9 @@ static int ixgbe_set_ringparam(struct net_device *netdev,
/* success! */
err = 0;
err_setup:
+ ixgbe_init_interrupt_scheme(adapter);
if (netif_running(netdev))
- ixgbe_up(adapter);
+ netdev->netdev_ops->ndo_open(netdev);
clear_bit(__IXGBE_RESETTING, &adapter->state);
return err;
@@ -804,6 +836,16 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
data[i + k] = queue_stat[k];
i += k;
}
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ for (j = 0; j < MAX_TX_PACKET_BUFFERS; j++) {
+ data[i++] = adapter->stats.pxontxc[j];
+ data[i++] = adapter->stats.pxofftxc[j];
+ }
+ for (j = 0; j < MAX_RX_PACKET_BUFFERS; j++) {
+ data[i++] = adapter->stats.pxonrxc[j];
+ data[i++] = adapter->stats.pxoffrxc[j];
+ }
+ }
}
static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
@@ -832,6 +874,20 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
sprintf(p, "rx_queue_%u_bytes", i);
p += ETH_GSTRING_LEN;
}
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ for (i = 0; i < MAX_TX_PACKET_BUFFERS; i++) {
+ sprintf(p, "tx_pb_%u_pxon", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "tx_pb_%u_pxoff", i);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < MAX_RX_PACKET_BUFFERS; i++) {
+ sprintf(p, "rx_pb_%u_pxon", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rx_pb_%u_pxoff", i);
+ p += ETH_GSTRING_LEN;
+ }
+ }
/* BUG_ON(p - data != IXGBE_STATS_LEN * ETH_GSTRING_LEN); */
break;
}
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 5236f633ee36..acef3c65cd2c 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -68,12 +68,20 @@ static struct pci_device_id ixgbe_pci_tbl[] = {
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT),
board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598AT),
+ board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_CX4),
board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_CX4_DUAL_PORT),
board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_DA_DUAL_PORT),
+ board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM),
+ board_82598 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_XF_LR),
board_82598 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82598EB_SFP_LOM),
+ board_82598 },
/* required last entry */
{0, }
@@ -402,7 +410,7 @@ static void ixgbe_receive_skb(struct ixgbe_adapter *adapter,
if (adapter->netdev->features & NETIF_F_LRO &&
skb->ip_summed == CHECKSUM_UNNECESSARY) {
- if (adapter->vlgrp && is_vlan)
+ if (adapter->vlgrp && is_vlan && (tag != 0))
lro_vlan_hwaccel_receive_skb(&ring->lro_mgr, skb,
adapter->vlgrp, tag,
rx_desc);
@@ -411,12 +419,12 @@ static void ixgbe_receive_skb(struct ixgbe_adapter *adapter,
ring->lro_used = true;
} else {
if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
- if (adapter->vlgrp && is_vlan)
+ if (adapter->vlgrp && is_vlan && (tag != 0))
vlan_hwaccel_receive_skb(skb, adapter->vlgrp, tag);
else
netif_receive_skb(skb);
} else {
- if (adapter->vlgrp && is_vlan)
+ if (adapter->vlgrp && is_vlan && (tag != 0))
vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
else
netif_rx(skb);
@@ -471,7 +479,6 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
union ixgbe_adv_rx_desc *rx_desc;
struct ixgbe_rx_buffer *bi;
unsigned int i;
- unsigned int bufsz = rx_ring->rx_buf_len + NET_IP_ALIGN;
i = rx_ring->next_to_use;
bi = &rx_ring->rx_buffer_info[i];
@@ -500,8 +507,10 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
}
if (!bi->skb) {
- struct sk_buff *skb = netdev_alloc_skb(adapter->netdev,
- bufsz);
+ struct sk_buff *skb;
+ skb = netdev_alloc_skb(adapter->netdev,
+ (rx_ring->rx_buf_len +
+ NET_IP_ALIGN));
if (!skb) {
adapter->alloc_rx_buff_failed++;
@@ -516,7 +525,8 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
skb_reserve(skb, NET_IP_ALIGN);
bi->skb = skb;
- bi->dma = pci_map_single(pdev, skb->data, bufsz,
+ bi->dma = pci_map_single(pdev, skb->data,
+ rx_ring->rx_buf_len,
PCI_DMA_FROMDEVICE);
}
/* Refresh the desc even if buffer_addrs didn't change because
@@ -607,7 +617,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
if (len && !skb_shinfo(skb)->nr_frags) {
pci_unmap_single(pdev, rx_buffer_info->dma,
- rx_ring->rx_buf_len + NET_IP_ALIGN,
+ rx_ring->rx_buf_len,
PCI_DMA_FROMDEVICE);
skb_put(skb, len);
}
@@ -666,7 +676,6 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
skb->protocol = eth_type_trans(skb, adapter->netdev);
ixgbe_receive_skb(adapter, skb, staterr, rx_ring, rx_desc);
- adapter->netdev->last_rx = jiffies;
next_desc:
rx_desc->wb.upper.status_error = 0;
@@ -904,6 +913,17 @@ static void ixgbe_set_itr_msix(struct ixgbe_q_vector *q_vector)
return;
}
+static void ixgbe_check_fan_failure(struct ixgbe_adapter *adapter, u32 eicr)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ if ((adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE) &&
+ (eicr & IXGBE_EICR_GPI_SDP1)) {
+ DPRINTK(PROBE, CRIT, "Fan has stopped, replace the adapter\n");
+ /* write to clear the interrupt */
+ IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1);
+ }
+}
static void ixgbe_check_lsc(struct ixgbe_adapter *adapter)
{
@@ -928,6 +948,8 @@ static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
if (eicr & IXGBE_EICR_LSC)
ixgbe_check_lsc(adapter);
+ ixgbe_check_fan_failure(adapter, eicr);
+
if (!test_bit(__IXGBE_DOWN, &adapter->state))
IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER);
@@ -990,7 +1012,7 @@ static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data)
rx_ring = &(adapter->rx_ring[r_idx]);
/* disable interrupts on this vector only */
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, rx_ring->v_idx);
- netif_rx_schedule(adapter->netdev, &q_vector->napi);
+ netif_rx_schedule(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -1031,7 +1053,7 @@ static int ixgbe_clean_rxonly(struct napi_struct *napi, int budget)
/* If all Rx work done, exit the polling mode */
if (work_done < budget) {
- netif_rx_complete(adapter->netdev, napi);
+ netif_rx_complete(napi);
if (adapter->itr_setting & 3)
ixgbe_set_itr_msix(q_vector);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -1080,7 +1102,7 @@ static int ixgbe_clean_rxonly_many(struct napi_struct *napi, int budget)
rx_ring = &(adapter->rx_ring[r_idx]);
/* If all Rx work done, exit the polling mode */
if (work_done < budget) {
- netif_rx_complete(adapter->netdev, napi);
+ netif_rx_complete(napi);
if (adapter->itr_setting & 3)
ixgbe_set_itr_msix(q_vector);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -1187,6 +1209,7 @@ static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
struct net_device *netdev = adapter->netdev;
irqreturn_t (*handler)(int, void *);
int i, vector, q_vectors, err;
+ int ri=0, ti=0;
/* Decrement for Other and TCP Timer vectors */
q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
@@ -1201,10 +1224,19 @@ static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
&ixgbe_msix_clean_many)
for (vector = 0; vector < q_vectors; vector++) {
handler = SET_HANDLER(&adapter->q_vector[vector]);
- sprintf(adapter->name[vector], "%s:v%d-%s",
- netdev->name, vector,
- (handler == &ixgbe_msix_clean_rx) ? "Rx" :
- ((handler == &ixgbe_msix_clean_tx) ? "Tx" : "TxRx"));
+
+ if(handler == &ixgbe_msix_clean_rx) {
+ sprintf(adapter->name[vector], "%s-%s-%d",
+ netdev->name, "rx", ri++);
+ }
+ else if(handler == &ixgbe_msix_clean_tx) {
+ sprintf(adapter->name[vector], "%s-%s-%d",
+ netdev->name, "tx", ti++);
+ }
+ else
+ sprintf(adapter->name[vector], "%s-%s-%d",
+ netdev->name, "TxRx", vector);
+
err = request_irq(adapter->msix_entries[vector].vector,
handler, 0, adapter->name[vector],
&(adapter->q_vector[vector]));
@@ -1312,6 +1344,8 @@ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter)
{
u32 mask;
mask = IXGBE_EIMS_ENABLE_MASK;
+ if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE)
+ mask |= IXGBE_EIMS_GPI_SDP1;
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
IXGBE_WRITE_FLUSH(&adapter->hw);
}
@@ -1342,13 +1376,15 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
if (eicr & IXGBE_EICR_LSC)
ixgbe_check_lsc(adapter);
- if (netif_rx_schedule_prep(netdev, &adapter->q_vector[0].napi)) {
+ ixgbe_check_fan_failure(adapter, eicr);
+
+ if (netif_rx_schedule_prep(&adapter->q_vector[0].napi)) {
adapter->tx_ring[0].total_packets = 0;
adapter->tx_ring[0].total_bytes = 0;
adapter->rx_ring[0].total_packets = 0;
adapter->rx_ring[0].total_bytes = 0;
/* would disable interrupts here but EIAM disabled it */
- __netif_rx_schedule(netdev, &adapter->q_vector[0].napi);
+ __netif_rx_schedule(&adapter->q_vector[0].napi);
}
return IRQ_HANDLED;
@@ -1651,10 +1687,12 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
* effects of setting this bit are only that SRRCTL must be
* fully programmed [0..15]
*/
- rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
- rdrxctl |= IXGBE_RDRXCTL_MVMEN;
- IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl);
-
+ if (adapter->flags &
+ (IXGBE_FLAG_RSS_ENABLED | IXGBE_FLAG_VMDQ_ENABLED)) {
+ rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
+ rdrxctl |= IXGBE_RDRXCTL_MVMEN;
+ IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rdrxctl);
+ }
if (adapter->flags & IXGBE_FLAG_RSS_ENABLED) {
/* Fill out redirection table */
@@ -1713,6 +1751,16 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev,
ixgbe_irq_disable(adapter);
adapter->vlgrp = grp;
+ /*
+ * For a DCB driver, always enable VLAN tag stripping so we can
+ * still receive traffic from a DCB-enabled host even if we're
+ * not in DCB mode.
+ */
+ ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
+ ctrl |= IXGBE_VLNCTRL_VME;
+ ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
+
if (grp) {
/* enable VLAN tag insert/strip */
ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
@@ -1877,6 +1925,44 @@ static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter)
}
}
+#ifdef CONFIG_IXGBE_DCB
+/*
+ * ixgbe_configure_dcb - Configure DCB hardware
+ * @adapter: ixgbe adapter struct
+ *
+ * This is called by the driver on open to configure the DCB hardware.
+ * This is also called by the gennetlink interface when reconfiguring
+ * the DCB state.
+ */
+static void ixgbe_configure_dcb(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 txdctl, vlnctrl;
+ int i, j;
+
+ ixgbe_dcb_check_config(&adapter->dcb_cfg);
+ ixgbe_dcb_calculate_tc_credits(&adapter->dcb_cfg, DCB_TX_CONFIG);
+ ixgbe_dcb_calculate_tc_credits(&adapter->dcb_cfg, DCB_RX_CONFIG);
+
+ /* reconfigure the hardware */
+ ixgbe_dcb_hw_config(&adapter->hw, &adapter->dcb_cfg);
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ j = adapter->tx_ring[i].reg_idx;
+ txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(j));
+ /* PThresh workaround for Tx hang with DFP enabled. */
+ txdctl |= 32;
+ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(j), txdctl);
+ }
+ /* Enable VLAN tag insert/strip */
+ vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ vlnctrl |= IXGBE_VLNCTRL_VME | IXGBE_VLNCTRL_VFE;
+ vlnctrl &= ~IXGBE_VLNCTRL_CFIEN;
+ IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
+ hw->mac.ops.set_vfta(&adapter->hw, 0, 0, true);
+}
+
+#endif
static void ixgbe_configure(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
@@ -1885,6 +1971,16 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter)
ixgbe_set_rx_mode(netdev);
ixgbe_restore_vlan(adapter);
+#ifdef CONFIG_IXGBE_DCB
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ netif_set_gso_max_size(netdev, 32768);
+ ixgbe_configure_dcb(adapter);
+ } else {
+ netif_set_gso_max_size(netdev, 65536);
+ }
+#else
+ netif_set_gso_max_size(netdev, 65536);
+#endif
ixgbe_configure_tx(adapter);
ixgbe_configure_rx(adapter);
@@ -1924,6 +2020,13 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE);
}
+ /* Enable fan failure interrupt if media type is copper */
+ if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE) {
+ gpie = IXGBE_READ_REG(hw, IXGBE_GPIE);
+ gpie |= IXGBE_SDP1_GPIEN;
+ IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
+ }
+
mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD);
if (max_frame != (mhadd >> IXGBE_MHADD_MFS_SHIFT)) {
mhadd &= ~IXGBE_MHADD_MFS_MASK;
@@ -1961,6 +2064,8 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
else
ixgbe_configure_msi_and_legacy(adapter);
+ ixgbe_napi_add_all(adapter);
+
clear_bit(__IXGBE_DOWN, &adapter->state);
ixgbe_napi_enable_all(adapter);
@@ -2205,7 +2310,7 @@ static int ixgbe_poll(struct napi_struct *napi, int budget)
/* If budget not fully consumed, exit the polling mode */
if (work_done < budget) {
- netif_rx_complete(adapter->netdev, napi);
+ netif_rx_complete(napi);
if (adapter->itr_setting & 3)
ixgbe_set_itr(adapter);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -2231,6 +2336,11 @@ static void ixgbe_reset_task(struct work_struct *work)
struct ixgbe_adapter *adapter;
adapter = container_of(work, struct ixgbe_adapter, reset_task);
+ /* If we're already down or resetting, just bail */
+ if (test_bit(__IXGBE_DOWN, &adapter->state) ||
+ test_bit(__IXGBE_RESETTING, &adapter->state))
+ return;
+
adapter->tx_timeout_count++;
ixgbe_reinit_locked(adapter);
@@ -2240,15 +2350,31 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter)
{
int nrq = 1, ntq = 1;
int feature_mask = 0, rss_i, rss_m;
+ int dcb_i, dcb_m;
/* Number of supported queues */
switch (adapter->hw.mac.type) {
case ixgbe_mac_82598EB:
+ dcb_i = adapter->ring_feature[RING_F_DCB].indices;
+ dcb_m = 0;
rss_i = adapter->ring_feature[RING_F_RSS].indices;
rss_m = 0;
feature_mask |= IXGBE_FLAG_RSS_ENABLED;
+ feature_mask |= IXGBE_FLAG_DCB_ENABLED;
switch (adapter->flags & feature_mask) {
+ case (IXGBE_FLAG_RSS_ENABLED | IXGBE_FLAG_DCB_ENABLED):
+ dcb_m = 0x7 << 3;
+ rss_i = min(8, rss_i);
+ rss_m = 0x7;
+ nrq = dcb_i * rss_i;
+ ntq = min(MAX_TX_QUEUES, dcb_i * rss_i);
+ break;
+ case (IXGBE_FLAG_DCB_ENABLED):
+ dcb_m = 0x7 << 3;
+ nrq = dcb_i;
+ ntq = dcb_i;
+ break;
case (IXGBE_FLAG_RSS_ENABLED):
rss_m = 0xF;
nrq = rss_i;
@@ -2256,6 +2382,8 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter)
break;
case 0:
default:
+ dcb_i = 0;
+ dcb_m = 0;
rss_i = 0;
rss_m = 0;
nrq = 1;
@@ -2263,6 +2391,12 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter)
break;
}
+ /* Sanity check, we should never have zero queues */
+ nrq = (nrq ?:1);
+ ntq = (ntq ?:1);
+
+ adapter->ring_feature[RING_F_DCB].indices = dcb_i;
+ adapter->ring_feature[RING_F_DCB].mask = dcb_m;
adapter->ring_feature[RING_F_RSS].indices = rss_i;
adapter->ring_feature[RING_F_RSS].mask = rss_m;
break;
@@ -2314,6 +2448,7 @@ static void ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter,
adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
+ adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED;
ixgbe_set_num_queues(adapter);
} else {
@@ -2333,15 +2468,42 @@ static void ixgbe_cache_ring_register(struct ixgbe_adapter *adapter)
{
int feature_mask = 0, rss_i;
int i, txr_idx, rxr_idx;
+ int dcb_i;
/* Number of supported queues */
switch (adapter->hw.mac.type) {
case ixgbe_mac_82598EB:
+ dcb_i = adapter->ring_feature[RING_F_DCB].indices;
rss_i = adapter->ring_feature[RING_F_RSS].indices;
txr_idx = 0;
rxr_idx = 0;
+ feature_mask |= IXGBE_FLAG_DCB_ENABLED;
feature_mask |= IXGBE_FLAG_RSS_ENABLED;
switch (adapter->flags & feature_mask) {
+ case (IXGBE_FLAG_RSS_ENABLED | IXGBE_FLAG_DCB_ENABLED):
+ for (i = 0; i < dcb_i; i++) {
+ int j;
+ /* Rx first */
+ for (j = 0; j < adapter->num_rx_queues; j++) {
+ adapter->rx_ring[rxr_idx].reg_idx =
+ i << 3 | j;
+ rxr_idx++;
+ }
+ /* Tx now */
+ for (j = 0; j < adapter->num_tx_queues; j++) {
+ adapter->tx_ring[txr_idx].reg_idx =
+ i << 2 | (j >> 1);
+ if (j & 1)
+ txr_idx++;
+ }
+ }
+ case (IXGBE_FLAG_DCB_ENABLED):
+ /* the number of queues is assumed to be symmetric */
+ for (i = 0; i < dcb_i; i++) {
+ adapter->rx_ring[i].reg_idx = i << 3;
+ adapter->tx_ring[i].reg_idx = i << 2;
+ }
+ break;
case (IXGBE_FLAG_RSS_ENABLED):
for (i = 0; i < adapter->num_rx_queues; i++)
adapter->rx_ring[i].reg_idx = i;
@@ -2363,8 +2525,7 @@ static void ixgbe_cache_ring_register(struct ixgbe_adapter *adapter)
* @adapter: board private structure to initialize
*
* We allocate one ring per queue at run-time since we don't know the
- * number of queues at compile-time. The polling_netdev array is
- * intended for Multiqueue, but should work fine with a single queue.
+ * number of queues at compile-time.
**/
static int ixgbe_alloc_queues(struct ixgbe_adapter *adapter)
{
@@ -2435,6 +2596,7 @@ static int ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter)
adapter->msix_entries = kcalloc(v_budget,
sizeof(struct msix_entry), GFP_KERNEL);
if (!adapter->msix_entries) {
+ adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED;
adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED;
ixgbe_set_num_queues(adapter);
kfree(adapter->tx_ring);
@@ -2475,7 +2637,7 @@ out:
return err;
}
-static void ixgbe_reset_interrupt_capability(struct ixgbe_adapter *adapter)
+void ixgbe_reset_interrupt_capability(struct ixgbe_adapter *adapter)
{
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
@@ -2499,7 +2661,7 @@ static void ixgbe_reset_interrupt_capability(struct ixgbe_adapter *adapter)
* - Hardware queue count (num_*_queues)
* - defined by miscellaneous hardware support/features (RSS, etc.)
**/
-static int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter)
+int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter)
{
int err;
@@ -2535,6 +2697,57 @@ err_alloc_queues:
}
/**
+ * ixgbe_sfp_timer - worker thread to find a missing module
+ * @data: pointer to our adapter struct
+ **/
+static void ixgbe_sfp_timer(unsigned long data)
+{
+ struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
+
+ /* Do the sfp_timer outside of interrupt context due to the
+ * delays that sfp+ detection requires
+ */
+ schedule_work(&adapter->sfp_task);
+}
+
+/**
+ * ixgbe_sfp_task - worker thread to find a missing module
+ * @work: pointer to work_struct containing our data
+ **/
+static void ixgbe_sfp_task(struct work_struct *work)
+{
+ struct ixgbe_adapter *adapter = container_of(work,
+ struct ixgbe_adapter,
+ sfp_task);
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ if ((hw->phy.type == ixgbe_phy_nl) &&
+ (hw->phy.sfp_type == ixgbe_sfp_type_not_present)) {
+ s32 ret = hw->phy.ops.identify_sfp(hw);
+ if (ret)
+ goto reschedule;
+ ret = hw->phy.ops.reset(hw);
+ if (ret == IXGBE_ERR_SFP_NOT_SUPPORTED) {
+ DPRINTK(PROBE, ERR, "failed to initialize because an "
+ "unsupported SFP+ module type was detected.\n"
+ "Reload the driver after installing a "
+ "supported module.\n");
+ unregister_netdev(adapter->netdev);
+ } else {
+ DPRINTK(PROBE, INFO, "detected SFP+: %d\n",
+ hw->phy.sfp_type);
+ }
+ /* don't need this routine any more */
+ clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
+ }
+ return;
+reschedule:
+ if (test_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state))
+ mod_timer(&adapter->sfp_timer,
+ round_jiffies(jiffies + (2 * HZ)));
+}
+
+/**
* ixgbe_sw_init - Initialize general software structures (struct ixgbe_adapter)
* @adapter: board private structure to initialize
*
@@ -2547,6 +2760,10 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
unsigned int rss;
+#ifdef CONFIG_IXGBE_DCB
+ int j;
+ struct tc_configuration *tc;
+#endif
/* PCI config space info */
@@ -2560,6 +2777,30 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
rss = min(IXGBE_MAX_RSS_INDICES, (int)num_online_cpus());
adapter->ring_feature[RING_F_RSS].indices = rss;
adapter->flags |= IXGBE_FLAG_RSS_ENABLED;
+ adapter->ring_feature[RING_F_DCB].indices = IXGBE_MAX_DCB_INDICES;
+
+#ifdef CONFIG_IXGBE_DCB
+ /* Configure DCB traffic classes */
+ for (j = 0; j < MAX_TRAFFIC_CLASS; j++) {
+ tc = &adapter->dcb_cfg.tc_config[j];
+ tc->path[DCB_TX_CONFIG].bwg_id = 0;
+ tc->path[DCB_TX_CONFIG].bwg_percent = 12 + (j & 1);
+ tc->path[DCB_RX_CONFIG].bwg_id = 0;
+ tc->path[DCB_RX_CONFIG].bwg_percent = 12 + (j & 1);
+ tc->dcb_pfc = pfc_disabled;
+ }
+ adapter->dcb_cfg.bw_percentage[DCB_TX_CONFIG][0] = 100;
+ adapter->dcb_cfg.bw_percentage[DCB_RX_CONFIG][0] = 100;
+ adapter->dcb_cfg.rx_pba_cfg = pba_equal;
+ adapter->dcb_cfg.round_robin_enable = false;
+ adapter->dcb_set_bitmap = 0x00;
+ ixgbe_copy_dcb_cfg(&adapter->dcb_cfg, &adapter->temp_dcb_cfg,
+ adapter->ring_feature[RING_F_DCB].indices);
+
+#endif
+ if (hw->mac.ops.get_media_type &&
+ (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_copper))
+ adapter->flags |= IXGBE_FLAG_FAN_FAIL_CAPABLE;
/* default flow control settings */
hw->fc.original_type = ixgbe_fc_none;
@@ -2934,11 +3175,16 @@ static int ixgbe_close(struct net_device *netdev)
* @adapter: private struct
* helper function to napi_add each possible q_vector->napi
*/
-static void ixgbe_napi_add_all(struct ixgbe_adapter *adapter)
+void ixgbe_napi_add_all(struct ixgbe_adapter *adapter)
{
int q_idx, q_vectors;
+ struct net_device *netdev = adapter->netdev;
int (*poll)(struct napi_struct *, int);
+ /* check if we already have our netdev->napi_list populated */
+ if (&netdev->napi_list != netdev->napi_list.next)
+ return;
+
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
poll = &ixgbe_clean_rxonly;
/* Only enable as many vectors as we have rx queues. */
@@ -2955,7 +3201,7 @@ static void ixgbe_napi_add_all(struct ixgbe_adapter *adapter)
}
}
-static void ixgbe_napi_del_all(struct ixgbe_adapter *adapter)
+void ixgbe_napi_del_all(struct ixgbe_adapter *adapter)
{
int q_idx;
int q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
@@ -3032,6 +3278,7 @@ static int ixgbe_suspend(struct pci_dev *pdev, pm_message_t state)
}
ixgbe_reset_interrupt_capability(adapter);
ixgbe_napi_del_all(adapter);
+ INIT_LIST_HEAD(&netdev->napi_list);
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
@@ -3076,6 +3323,18 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
adapter->stats.mpc[i] += mpc;
total_mpc += adapter->stats.mpc[i];
adapter->stats.rnbc[i] += IXGBE_READ_REG(hw, IXGBE_RNBC(i));
+ adapter->stats.qptc[i] += IXGBE_READ_REG(hw, IXGBE_QPTC(i));
+ adapter->stats.qbtc[i] += IXGBE_READ_REG(hw, IXGBE_QBTC(i));
+ adapter->stats.qprc[i] += IXGBE_READ_REG(hw, IXGBE_QPRC(i));
+ adapter->stats.qbrc[i] += IXGBE_READ_REG(hw, IXGBE_QBRC(i));
+ adapter->stats.pxonrxc[i] += IXGBE_READ_REG(hw,
+ IXGBE_PXONRXC(i));
+ adapter->stats.pxontxc[i] += IXGBE_READ_REG(hw,
+ IXGBE_PXONTXC(i));
+ adapter->stats.pxoffrxc[i] += IXGBE_READ_REG(hw,
+ IXGBE_PXOFFRXC(i));
+ adapter->stats.pxofftxc[i] += IXGBE_READ_REG(hw,
+ IXGBE_PXOFFTXC(i));
}
adapter->stats.gprc += IXGBE_READ_REG(hw, IXGBE_GPRC);
/* work around hardware counting issue */
@@ -3204,15 +3463,16 @@ static void ixgbe_watchdog_task(struct work_struct *work)
u32 rmcs = IXGBE_READ_REG(hw, IXGBE_RMCS);
#define FLOW_RX (frctl & IXGBE_FCTRL_RFCE)
#define FLOW_TX (rmcs & IXGBE_RMCS_TFCE_802_3X)
- DPRINTK(LINK, INFO, "NIC Link is Up %s, "
- "Flow Control: %s\n",
- (link_speed == IXGBE_LINK_SPEED_10GB_FULL ?
- "10 Gbps" :
- (link_speed == IXGBE_LINK_SPEED_1GB_FULL ?
- "1 Gbps" : "unknown speed")),
- ((FLOW_RX && FLOW_TX) ? "RX/TX" :
- (FLOW_RX ? "RX" :
- (FLOW_TX ? "TX" : "None"))));
+ printk(KERN_INFO "ixgbe: %s NIC Link is Up %s, "
+ "Flow Control: %s\n",
+ netdev->name,
+ (link_speed == IXGBE_LINK_SPEED_10GB_FULL ?
+ "10 Gbps" :
+ (link_speed == IXGBE_LINK_SPEED_1GB_FULL ?
+ "1 Gbps" : "unknown speed")),
+ ((FLOW_RX && FLOW_TX) ? "RX/TX" :
+ (FLOW_RX ? "RX" :
+ (FLOW_TX ? "TX" : "None"))));
netif_carrier_on(netdev);
netif_tx_wake_all_queues(netdev);
@@ -3224,7 +3484,8 @@ static void ixgbe_watchdog_task(struct work_struct *work)
adapter->link_up = false;
adapter->link_speed = 0;
if (netif_carrier_ok(netdev)) {
- DPRINTK(LINK, INFO, "NIC Link is Down\n");
+ printk(KERN_INFO "ixgbe: %s NIC Link is Down\n",
+ netdev->name);
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
}
@@ -3573,6 +3834,14 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
tx_flags |= vlan_tx_tag_get(skb);
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ tx_flags &= ~IXGBE_TX_FLAGS_VLAN_PRIO_MASK;
+ tx_flags |= (skb->queue_mapping << 13);
+ }
+ tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
+ tx_flags |= IXGBE_TX_FLAGS_VLAN;
+ } else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
+ tx_flags |= (skb->queue_mapping << 13);
tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
tx_flags |= IXGBE_TX_FLAGS_VLAN;
}
@@ -3687,9 +3956,31 @@ static int ixgbe_link_config(struct ixgbe_hw *hw)
/* must always autoneg for both 1G and 10G link */
hw->mac.autoneg = true;
+ if ((hw->mac.type == ixgbe_mac_82598EB) &&
+ (hw->phy.media_type == ixgbe_media_type_copper))
+ autoneg = IXGBE_LINK_SPEED_82598_AUTONEG;
+
return hw->mac.ops.setup_link_speed(hw, autoneg, true, true);
}
+static const struct net_device_ops ixgbe_netdev_ops = {
+ .ndo_open = ixgbe_open,
+ .ndo_stop = ixgbe_close,
+ .ndo_start_xmit = ixgbe_xmit_frame,
+ .ndo_get_stats = ixgbe_get_stats,
+ .ndo_set_multicast_list = ixgbe_set_rx_mode,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = ixgbe_set_mac,
+ .ndo_change_mtu = ixgbe_change_mtu,
+ .ndo_tx_timeout = ixgbe_tx_timeout,
+ .ndo_vlan_rx_register = ixgbe_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = ixgbe_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ixgbe_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ixgbe_netpoll,
+#endif
+};
+
/**
* ixgbe_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -3739,6 +4030,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
goto err_pci_reg;
}
+ err = pci_enable_pcie_error_reporting(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed "
+ "0x%x\n", err);
+ /* non-fatal, continue */
+ }
+
pci_set_master(pdev);
pci_save_state(pdev);
@@ -3771,23 +4069,9 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
continue;
}
- netdev->open = &ixgbe_open;
- netdev->stop = &ixgbe_close;
- netdev->hard_start_xmit = &ixgbe_xmit_frame;
- netdev->get_stats = &ixgbe_get_stats;
- netdev->set_rx_mode = &ixgbe_set_rx_mode;
- netdev->set_multicast_list = &ixgbe_set_rx_mode;
- netdev->set_mac_address = &ixgbe_set_mac;
- netdev->change_mtu = &ixgbe_change_mtu;
+ netdev->netdev_ops = &ixgbe_netdev_ops;
ixgbe_set_ethtool_ops(netdev);
- netdev->tx_timeout = &ixgbe_tx_timeout;
netdev->watchdog_timeo = 5 * HZ;
- netdev->vlan_rx_register = ixgbe_vlan_rx_register;
- netdev->vlan_rx_add_vid = ixgbe_vlan_rx_add_vid;
- netdev->vlan_rx_kill_vid = ixgbe_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = ixgbe_netpoll;
-#endif
strcpy(netdev->name, pci_name(pdev));
adapter->bd_number = cards_found;
@@ -3805,11 +4089,31 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
/* PHY */
memcpy(&hw->phy.ops, ii->phy_ops, sizeof(hw->phy.ops));
- /* phy->sfp_type = ixgbe_sfp_type_unknown; */
+ hw->phy.sfp_type = ixgbe_sfp_type_unknown;
+
+ /* set up this timer and work struct before calling get_invariants
+ * which might start the timer
+ */
+ init_timer(&adapter->sfp_timer);
+ adapter->sfp_timer.function = &ixgbe_sfp_timer;
+ adapter->sfp_timer.data = (unsigned long) adapter;
+
+ INIT_WORK(&adapter->sfp_task, ixgbe_sfp_task);
err = ii->get_invariants(hw);
- if (err)
+ if (err == IXGBE_ERR_SFP_NOT_PRESENT) {
+ /* start a kernel thread to watch for a module to arrive */
+ set_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
+ mod_timer(&adapter->sfp_timer,
+ round_jiffies(jiffies + (2 * HZ)));
+ err = 0;
+ } else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) {
+ DPRINTK(PROBE, ERR, "failed to load because an "
+ "unsupported SFP+ module type was detected.\n");
goto err_hw_init;
+ } else if (err) {
+ goto err_hw_init;
+ }
/* setup the private structure */
err = ixgbe_sw_init(adapter);
@@ -3839,6 +4143,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
netdev->vlan_features |= NETIF_F_IP_CSUM;
netdev->vlan_features |= NETIF_F_SG;
+ if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
+ adapter->flags &= ~IXGBE_FLAG_RSS_ENABLED;
+
+#ifdef CONFIG_IXGBE_DCB
+ netdev->dcbnl_ops = &dcbnl_ops;
+#endif
+
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
@@ -3873,8 +4184,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
pci_read_config_word(pdev, IXGBE_PCI_LINK_STATUS, &link_status);
link_speed = link_status & IXGBE_PCI_LINK_SPEED;
link_width = link_status & IXGBE_PCI_LINK_WIDTH;
- dev_info(&pdev->dev, "(PCI Express:%s:%s) "
- "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev_info(&pdev->dev, "(PCI Express:%s:%s) %pM\n",
((link_speed == IXGBE_PCI_LINK_SPEED_5000) ? "5.0Gb/s" :
(link_speed == IXGBE_PCI_LINK_SPEED_2500) ? "2.5Gb/s" :
"Unknown"),
@@ -3883,8 +4193,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
(link_width == IXGBE_PCI_LINK_WIDTH_2) ? "Width x2" :
(link_width == IXGBE_PCI_LINK_WIDTH_1) ? "Width x1" :
"Unknown"),
- netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
- netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
+ netdev->dev_addr);
ixgbe_read_pba_num_generic(hw, &part_num);
dev_info(&pdev->dev, "MAC: %d, PHY: %d, PBA No: %06x-%03x\n",
hw->mac.type, hw->phy.type,
@@ -3911,8 +4220,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
netif_carrier_off(netdev);
netif_tx_stop_all_queues(netdev);
- ixgbe_napi_add_all(adapter);
-
strcpy(netdev->name, "eth%d");
err = register_netdev(netdev);
if (err)
@@ -3938,6 +4245,9 @@ err_hw_init:
err_sw_init:
ixgbe_reset_interrupt_capability(adapter);
err_eeprom:
+ clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
+ del_timer_sync(&adapter->sfp_timer);
+ cancel_work_sync(&adapter->sfp_task);
iounmap(hw->hw_addr);
err_ioremap:
free_netdev(netdev);
@@ -3962,10 +4272,18 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int err;
set_bit(__IXGBE_DOWN, &adapter->state);
+ /* clear the module not found bit to make sure the worker won't
+ * reschedule
+ */
+ clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state);
del_timer_sync(&adapter->watchdog_timer);
+ del_timer_sync(&adapter->sfp_timer);
+ cancel_work_sync(&adapter->watchdog_task);
+ cancel_work_sync(&adapter->sfp_task);
flush_scheduled_work();
#ifdef CONFIG_IXGBE_DCA
@@ -3976,7 +4294,8 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
}
#endif
- unregister_netdev(netdev);
+ if (netdev->reg_state == NETREG_REGISTERED)
+ unregister_netdev(netdev);
ixgbe_reset_interrupt_capability(adapter);
@@ -3986,12 +4305,16 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
DPRINTK(PROBE, INFO, "complete\n");
- ixgbe_napi_del_all(adapter);
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
free_netdev(netdev);
+ err = pci_disable_pcie_error_reporting(pdev);
+ if (err)
+ dev_err(&pdev->dev,
+ "pci_disable_pcie_error_reporting failed 0x%x\n", err);
+
pci_disable_device(pdev);
}
@@ -4007,7 +4330,7 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgbe_adapter *adapter = netdev->priv;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
netif_device_detach(netdev);
@@ -4028,22 +4351,34 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgbe_adapter *adapter = netdev->priv;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ pci_ers_result_t result;
+ int err;
if (pci_enable_device(pdev)) {
DPRINTK(PROBE, ERR,
"Cannot re-enable PCI device after reset.\n");
- return PCI_ERS_RESULT_DISCONNECT;
- }
- pci_set_master(pdev);
- pci_restore_state(pdev);
+ result = PCI_ERS_RESULT_DISCONNECT;
+ } else {
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D3hot, 0);
- pci_enable_wake(pdev, PCI_D3cold, 0);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
- ixgbe_reset(adapter);
+ ixgbe_reset(adapter);
+
+ result = PCI_ERS_RESULT_RECOVERED;
+ }
- return PCI_ERS_RESULT_RECOVERED;
+ err = pci_cleanup_aer_uncorrect_error_status(pdev);
+ if (err) {
+ dev_err(&pdev->dev,
+ "pci_cleanup_aer_uncorrect_error_status failed 0x%0x\n", err);
+ /* non-fatal, continue */
+ }
+
+ return result;
}
/**
@@ -4056,7 +4391,7 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
static void ixgbe_io_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgbe_adapter *adapter = netdev->priv;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
if (netif_running(netdev)) {
if (ixgbe_up(adapter)) {
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c
index 764035a8c9a1..5a8669aedf64 100644
--- a/drivers/net/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ixgbe/ixgbe_phy.c
@@ -121,9 +121,15 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
enum ixgbe_phy_type phy_type;
switch (phy_id) {
+ case TN1010_PHY_ID:
+ phy_type = ixgbe_phy_tn;
+ break;
case QT2022_PHY_ID:
phy_type = ixgbe_phy_qt;
break;
+ case ATH_PHY_ID:
+ phy_type = ixgbe_phy_nl;
+ break;
default:
phy_type = ixgbe_phy_unknown;
break;
@@ -426,3 +432,323 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
return 0;
}
+/**
+ * ixgbe_reset_phy_nl - Performs a PHY reset
+ * @hw: pointer to hardware structure
+ **/
+s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw)
+{
+ u16 phy_offset, control, eword, edata, block_crc;
+ bool end_data = false;
+ u16 list_offset, data_offset;
+ u16 phy_data = 0;
+ s32 ret_val = 0;
+ u32 i;
+
+ hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data);
+
+ /* reset the PHY and poll for completion */
+ hw->phy.ops.write_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE,
+ (phy_data | IXGBE_MDIO_PHY_XS_RESET));
+
+ for (i = 0; i < 100; i++) {
+ hw->phy.ops.read_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE, &phy_data);
+ if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) == 0)
+ break;
+ msleep(10);
+ }
+
+ if ((phy_data & IXGBE_MDIO_PHY_XS_RESET) != 0) {
+ hw_dbg(hw, "PHY reset did not complete.\n");
+ ret_val = IXGBE_ERR_PHY;
+ goto out;
+ }
+
+ /* Get init offsets */
+ ret_val = ixgbe_get_sfp_init_sequence_offsets(hw, &list_offset,
+ &data_offset);
+ if (ret_val != 0)
+ goto out;
+
+ ret_val = hw->eeprom.ops.read(hw, data_offset, &block_crc);
+ data_offset++;
+ while (!end_data) {
+ /*
+ * Read control word from PHY init contents offset
+ */
+ ret_val = hw->eeprom.ops.read(hw, data_offset, &eword);
+ control = (eword & IXGBE_CONTROL_MASK_NL) >>
+ IXGBE_CONTROL_SHIFT_NL;
+ edata = eword & IXGBE_DATA_MASK_NL;
+ switch (control) {
+ case IXGBE_DELAY_NL:
+ data_offset++;
+ hw_dbg(hw, "DELAY: %d MS\n", edata);
+ msleep(edata);
+ break;
+ case IXGBE_DATA_NL:
+ hw_dbg(hw, "DATA: \n");
+ data_offset++;
+ hw->eeprom.ops.read(hw, data_offset++,
+ &phy_offset);
+ for (i = 0; i < edata; i++) {
+ hw->eeprom.ops.read(hw, data_offset, &eword);
+ hw->phy.ops.write_reg(hw, phy_offset,
+ IXGBE_TWINAX_DEV, eword);
+ hw_dbg(hw, "Wrote %4.4x to %4.4x\n", eword,
+ phy_offset);
+ data_offset++;
+ phy_offset++;
+ }
+ break;
+ case IXGBE_CONTROL_NL:
+ data_offset++;
+ hw_dbg(hw, "CONTROL: \n");
+ if (edata == IXGBE_CONTROL_EOL_NL) {
+ hw_dbg(hw, "EOL\n");
+ end_data = true;
+ } else if (edata == IXGBE_CONTROL_SOL_NL) {
+ hw_dbg(hw, "SOL\n");
+ } else {
+ hw_dbg(hw, "Bad control value\n");
+ ret_val = IXGBE_ERR_PHY;
+ goto out;
+ }
+ break;
+ default:
+ hw_dbg(hw, "Bad control type\n");
+ ret_val = IXGBE_ERR_PHY;
+ goto out;
+ }
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * ixgbe_identify_sfp_module_generic - Identifies SFP module and assigns
+ * the PHY type.
+ * @hw: pointer to hardware structure
+ *
+ * Searches for and indentifies the SFP module. Assings appropriate PHY type.
+ **/
+s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
+ u32 vendor_oui = 0;
+ u8 identifier = 0;
+ u8 comp_codes_1g = 0;
+ u8 comp_codes_10g = 0;
+ u8 oui_bytes[4] = {0, 0, 0, 0};
+ u8 transmission_media = 0;
+
+ status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER,
+ &identifier);
+
+ if (status == IXGBE_ERR_SFP_NOT_PRESENT) {
+ hw->phy.sfp_type = ixgbe_sfp_type_not_present;
+ goto out;
+ }
+
+ if (identifier == IXGBE_SFF_IDENTIFIER_SFP) {
+ hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_1GBE_COMP_CODES,
+ &comp_codes_1g);
+ hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_10GBE_COMP_CODES,
+ &comp_codes_10g);
+ hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_TRANSMISSION_MEDIA,
+ &transmission_media);
+
+ /* ID Module
+ * =========
+ * 0 SFP_DA_CU
+ * 1 SFP_SR
+ * 2 SFP_LR
+ */
+ if (transmission_media & IXGBE_SFF_TWIN_AX_CAPABLE)
+ hw->phy.sfp_type = ixgbe_sfp_type_da_cu;
+ else if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE)
+ hw->phy.sfp_type = ixgbe_sfp_type_sr;
+ else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE)
+ hw->phy.sfp_type = ixgbe_sfp_type_lr;
+ else
+ hw->phy.sfp_type = ixgbe_sfp_type_unknown;
+
+ /* Determine PHY vendor */
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ hw->phy.id = identifier;
+ hw->phy.ops.read_i2c_eeprom(hw,
+ IXGBE_SFF_VENDOR_OUI_BYTE0,
+ &oui_bytes[0]);
+ hw->phy.ops.read_i2c_eeprom(hw,
+ IXGBE_SFF_VENDOR_OUI_BYTE1,
+ &oui_bytes[1]);
+ hw->phy.ops.read_i2c_eeprom(hw,
+ IXGBE_SFF_VENDOR_OUI_BYTE2,
+ &oui_bytes[2]);
+
+ vendor_oui =
+ ((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) |
+ (oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) |
+ (oui_bytes[2] << IXGBE_SFF_VENDOR_OUI_BYTE2_SHIFT));
+
+ switch (vendor_oui) {
+ case IXGBE_SFF_VENDOR_OUI_TYCO:
+ if (transmission_media &
+ IXGBE_SFF_TWIN_AX_CAPABLE)
+ hw->phy.type = ixgbe_phy_tw_tyco;
+ break;
+ case IXGBE_SFF_VENDOR_OUI_FTL:
+ hw->phy.type = ixgbe_phy_sfp_ftl;
+ break;
+ case IXGBE_SFF_VENDOR_OUI_AVAGO:
+ hw->phy.type = ixgbe_phy_sfp_avago;
+ break;
+ default:
+ if (transmission_media &
+ IXGBE_SFF_TWIN_AX_CAPABLE)
+ hw->phy.type = ixgbe_phy_tw_unknown;
+ else
+ hw->phy.type = ixgbe_phy_sfp_unknown;
+ break;
+ }
+ }
+ status = 0;
+ }
+
+out:
+ return status;
+}
+
+/**
+ * ixgbe_get_sfp_init_sequence_offsets - Checks the MAC's EEPROM to see
+ * if it supports a given SFP+ module type, if so it returns the offsets to the
+ * phy init sequence block.
+ * @hw: pointer to hardware structure
+ * @list_offset: offset to the SFP ID list
+ * @data_offset: offset to the SFP data block
+ **/
+s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
+ u16 *list_offset,
+ u16 *data_offset)
+{
+ u16 sfp_id;
+
+ if (hw->phy.sfp_type == ixgbe_sfp_type_unknown)
+ return IXGBE_ERR_SFP_NOT_SUPPORTED;
+
+ if (hw->phy.sfp_type == ixgbe_sfp_type_not_present)
+ return IXGBE_ERR_SFP_NOT_PRESENT;
+
+ if ((hw->device_id == IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM) &&
+ (hw->phy.sfp_type == ixgbe_sfp_type_da_cu))
+ return IXGBE_ERR_SFP_NOT_SUPPORTED;
+
+ /* Read offset to PHY init contents */
+ hw->eeprom.ops.read(hw, IXGBE_PHY_INIT_OFFSET_NL, list_offset);
+
+ if ((!*list_offset) || (*list_offset == 0xFFFF))
+ return IXGBE_ERR_PHY;
+
+ /* Shift offset to first ID word */
+ (*list_offset)++;
+
+ /*
+ * Find the matching SFP ID in the EEPROM
+ * and program the init sequence
+ */
+ hw->eeprom.ops.read(hw, *list_offset, &sfp_id);
+
+ while (sfp_id != IXGBE_PHY_INIT_END_NL) {
+ if (sfp_id == hw->phy.sfp_type) {
+ (*list_offset)++;
+ hw->eeprom.ops.read(hw, *list_offset, data_offset);
+ if ((!*data_offset) || (*data_offset == 0xFFFF)) {
+ hw_dbg(hw, "SFP+ module not supported\n");
+ return IXGBE_ERR_SFP_NOT_SUPPORTED;
+ } else {
+ break;
+ }
+ } else {
+ (*list_offset) += 2;
+ if (hw->eeprom.ops.read(hw, *list_offset, &sfp_id))
+ return IXGBE_ERR_PHY;
+ }
+ }
+
+ if (sfp_id == IXGBE_PHY_INIT_END_NL) {
+ hw_dbg(hw, "No matching SFP+ module found\n");
+ return IXGBE_ERR_SFP_NOT_SUPPORTED;
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_check_phy_link_tnx - Determine link and speed status
+ * @hw: pointer to hardware structure
+ *
+ * Reads the VS1 register to determine if link is up and the current speed for
+ * the PHY.
+ **/
+s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
+ bool *link_up)
+{
+ s32 status = 0;
+ u32 time_out;
+ u32 max_time_out = 10;
+ u16 phy_link = 0;
+ u16 phy_speed = 0;
+ u16 phy_data = 0;
+
+ /* Initialize speed and link to default case */
+ *link_up = false;
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+
+ /*
+ * Check current speed and link status of the PHY register.
+ * This is a vendor specific register and may have to
+ * be changed for other copper PHYs.
+ */
+ for (time_out = 0; time_out < max_time_out; time_out++) {
+ udelay(10);
+ status = hw->phy.ops.read_reg(hw,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ &phy_data);
+ phy_link = phy_data &
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS;
+ phy_speed = phy_data &
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS;
+ if (phy_link == IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS) {
+ *link_up = true;
+ if (phy_speed ==
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS)
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+ break;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_get_phy_firmware_version_tnx - Gets the PHY Firmware Version
+ * @hw: pointer to hardware structure
+ * @firmware_version: pointer to the PHY Firmware Version
+ **/
+s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
+ u16 *firmware_version)
+{
+ s32 status = 0;
+
+ status = hw->phy.ops.read_reg(hw, TNX_FW_REV,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ firmware_version);
+
+ return status;
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_phy.h b/drivers/net/ixgbe/ixgbe_phy.h
index 9bfe3f2b1d8f..43a97bc420f5 100644
--- a/drivers/net/ixgbe/ixgbe_phy.h
+++ b/drivers/net/ixgbe/ixgbe_phy.h
@@ -63,6 +63,18 @@
#define IXGBE_SFF_VENDOR_OUI_FTL 0x00906500
#define IXGBE_SFF_VENDOR_OUI_AVAGO 0x00176A00
+/* I2C SDA and SCL timing parameters for standard mode */
+#define IXGBE_I2C_T_HD_STA 4
+#define IXGBE_I2C_T_LOW 5
+#define IXGBE_I2C_T_HIGH 4
+#define IXGBE_I2C_T_SU_STA 5
+#define IXGBE_I2C_T_HD_DATA 5
+#define IXGBE_I2C_T_SU_DATA 1
+#define IXGBE_I2C_T_RISE 1
+#define IXGBE_I2C_T_FALL 1
+#define IXGBE_I2C_T_SU_STO 4
+#define IXGBE_I2C_T_BUF 5
+
s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
@@ -77,4 +89,17 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw,
bool autoneg,
bool autoneg_wait_to_complete);
+/* PHY specific */
+s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw,
+ ixgbe_link_speed *speed,
+ bool *link_up);
+s32 ixgbe_get_phy_firmware_version_tnx(struct ixgbe_hw *hw,
+ u16 *firmware_version);
+
+s32 ixgbe_reset_phy_nl(struct ixgbe_hw *hw);
+s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw);
+s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw,
+ u16 *list_offset,
+ u16 *data_offset);
+
#endif /* _IXGBE_PHY_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h
index c6f8fa1c4e59..83a11ff9ffd1 100644
--- a/drivers/net/ixgbe/ixgbe_type.h
+++ b/drivers/net/ixgbe/ixgbe_type.h
@@ -36,8 +36,12 @@
/* Device IDs */
#define IXGBE_DEV_ID_82598AF_DUAL_PORT 0x10C6
#define IXGBE_DEV_ID_82598AF_SINGLE_PORT 0x10C7
+#define IXGBE_DEV_ID_82598EB_SFP_LOM 0x10DB
+#define IXGBE_DEV_ID_82598AT 0x10C8
#define IXGBE_DEV_ID_82598EB_CX4 0x10DD
#define IXGBE_DEV_ID_82598_CX4_DUAL_PORT 0x10EC
+#define IXGBE_DEV_ID_82598_DA_DUAL_PORT 0x10F1
+#define IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM 0x10E1
#define IXGBE_DEV_ID_82598EB_XF_LR 0x10F4
/* General Registers */
@@ -452,6 +456,7 @@
#define IXGBE_MDIO_PHY_XS_DEV_TYPE 0x4
#define IXGBE_MDIO_AUTO_NEG_DEV_TYPE 0x7
#define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE 0x1E /* Device 30 */
+#define IXGBE_TWINAX_DEV 1
#define IXGBE_MDIO_COMMAND_TIMEOUT 100 /* PHY Timeout for 1 GB mode */
@@ -487,12 +492,27 @@
#define IXGBE_PHY_REVISION_MASK 0xFFFFFFF0
#define IXGBE_MAX_PHY_ADDR 32
-/* PHY IDs*/
+/* PHY IDs */
+#define TN1010_PHY_ID 0x00A19410
+#define TNX_FW_REV 0xB
#define QT2022_PHY_ID 0x0043A400
+#define ATH_PHY_ID 0x03429050
/* PHY Types */
#define IXGBE_M88E1145_E_PHY_ID 0x01410CD0
+/* Special PHY Init Routine */
+#define IXGBE_PHY_INIT_OFFSET_NL 0x002B
+#define IXGBE_PHY_INIT_END_NL 0xFFFF
+#define IXGBE_CONTROL_MASK_NL 0xF000
+#define IXGBE_DATA_MASK_NL 0x0FFF
+#define IXGBE_CONTROL_SHIFT_NL 12
+#define IXGBE_DELAY_NL 0
+#define IXGBE_DATA_NL 1
+#define IXGBE_CONTROL_NL 0x000F
+#define IXGBE_CONTROL_EOL_NL 0x0FFF
+#define IXGBE_CONTROL_SOL_NL 0x0000
+
/* General purpose Interrupt Enable */
#define IXGBE_SDP0_GPIEN 0x00000001 /* SDP0 */
#define IXGBE_SDP1_GPIEN 0x00000002 /* SDP1 */
@@ -1202,8 +1222,10 @@ enum ixgbe_mac_type {
enum ixgbe_phy_type {
ixgbe_phy_unknown = 0,
+ ixgbe_phy_tn,
ixgbe_phy_qt,
ixgbe_phy_xaui,
+ ixgbe_phy_nl,
ixgbe_phy_tw_tyco,
ixgbe_phy_tw_unknown,
ixgbe_phy_sfp_avago,
@@ -1225,6 +1247,7 @@ enum ixgbe_sfp_type {
ixgbe_sfp_type_da_cu = 0,
ixgbe_sfp_type_sr = 1,
ixgbe_sfp_type_lr = 2,
+ ixgbe_sfp_type_not_present = 0xFFFE,
ixgbe_sfp_type_unknown = 0xFFFF
};
@@ -1396,6 +1419,8 @@ struct ixgbe_phy_operations {
s32 (*setup_link)(struct ixgbe_hw *);
s32 (*setup_link_speed)(struct ixgbe_hw *, ixgbe_link_speed, bool,
bool);
+ s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *);
+ s32 (*get_firmware_version)(struct ixgbe_hw *, u16 *);
s32 (*read_i2c_byte)(struct ixgbe_hw *, u8, u8, u8 *);
s32 (*write_i2c_byte)(struct ixgbe_hw *, u8, u8, u8);
s32 (*read_i2c_eeprom)(struct ixgbe_hw *, u8 , u8 *);
@@ -1486,6 +1511,7 @@ struct ixgbe_info {
#define IXGBE_ERR_PHY_ADDR_INVALID -17
#define IXGBE_ERR_I2C -18
#define IXGBE_ERR_SFP_NOT_SUPPORTED -19
+#define IXGBE_ERR_SFP_NOT_PRESENT -20
#define IXGBE_NOT_IMPLEMENTED 0x7FFFFFFF
#endif /* _IXGBE_TYPE_H_ */
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index 7b70c66504a0..014745720560 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -114,8 +114,6 @@ static int ixpdev_rx(struct net_device *dev, int processed, int budget)
skb_put(skb, desc->pkt_length);
skb->protocol = eth_type_trans(skb, nds[desc->channel]);
- dev->last_rx = jiffies;
-
netif_receive_skb(skb);
}
@@ -143,7 +141,7 @@ static int ixpdev_poll(struct napi_struct *napi, int budget)
break;
} while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff);
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff);
return rx;
@@ -206,7 +204,7 @@ static irqreturn_t ixpdev_interrupt(int irq, void *dev_id)
ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff);
if (likely(napi_schedule_prep(&ip->napi))) {
- __netif_rx_schedule(dev, &ip->napi);
+ __netif_rx_schedule(&ip->napi);
} else {
printk(KERN_CRIT "ixp2000: irq while polling!!\n");
}
diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c
index 07944820f745..334ff9e12cdd 100644
--- a/drivers/net/jazzsonic.c
+++ b/drivers/net/jazzsonic.c
@@ -208,7 +208,6 @@ static int __init jazz_sonic_probe(struct platform_device *pdev)
struct sonic_local *lp;
struct resource *res;
int err = 0;
- DECLARE_MAC_BUF(mac);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -233,8 +232,7 @@ static int __init jazz_sonic_probe(struct platform_device *pdev)
if (err)
goto out1;
- printk("%s: MAC %s IRQ %d\n",
- dev->name, print_mac(mac, dev->dev_addr), dev->irq);
+ printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
return 0;
diff --git a/drivers/net/jme.c b/drivers/net/jme.c
index 665e70d620fc..08b34051c646 100644
--- a/drivers/net/jme.c
+++ b/drivers/net/jme.c
@@ -435,15 +435,18 @@ jme_check_link(struct net_device *netdev, int testonly)
GHC_DPX);
switch (phylink & PHY_LINK_SPEED_MASK) {
case PHY_LINK_SPEED_10M:
- ghc |= GHC_SPEED_10M;
+ ghc |= GHC_SPEED_10M |
+ GHC_TO_CLK_PCIE | GHC_TXMAC_CLK_PCIE;
strcat(linkmsg, "10 Mbps, ");
break;
case PHY_LINK_SPEED_100M:
- ghc |= GHC_SPEED_100M;
+ ghc |= GHC_SPEED_100M |
+ GHC_TO_CLK_PCIE | GHC_TXMAC_CLK_PCIE;
strcat(linkmsg, "100 Mbps, ");
break;
case PHY_LINK_SPEED_1000M:
- ghc |= GHC_SPEED_1000M;
+ ghc |= GHC_SPEED_1000M |
+ GHC_TO_CLK_GPHY | GHC_TXMAC_CLK_GPHY;
strcat(linkmsg, "1000 Mbps, ");
break;
default:
@@ -463,14 +466,6 @@ jme_check_link(struct net_device *netdev, int testonly)
TXTRHD_TXREN |
((8 << TXTRHD_TXRL_SHIFT) & TXTRHD_TXRL));
}
- strcat(linkmsg, (phylink & PHY_LINK_DUPLEX) ?
- "Full-Duplex, " :
- "Half-Duplex, ");
-
- if (phylink & PHY_LINK_MDI_STAT)
- strcat(linkmsg, "MDI-X");
- else
- strcat(linkmsg, "MDI");
gpreg1 = GPREG1_DEFAULT;
if (is_buggy250(jme->pdev->device, jme->chiprev)) {
@@ -492,11 +487,17 @@ jme_check_link(struct net_device *netdev, int testonly)
break;
}
}
- jwrite32(jme, JME_GPREG1, gpreg1);
- jme->reg_ghc = ghc;
+ jwrite32(jme, JME_GPREG1, gpreg1);
jwrite32(jme, JME_GHC, ghc);
+ jme->reg_ghc = ghc;
+ strcat(linkmsg, (phylink & PHY_LINK_DUPLEX) ?
+ "Full-Duplex, " :
+ "Half-Duplex, ");
+ strcat(linkmsg, (phylink & PHY_LINK_MDI_STAT) ?
+ "MDI-X" :
+ "MDI");
msg_link(jme, "Link is up at %s.\n", linkmsg);
netif_carrier_on(netdev);
} else {
@@ -931,7 +932,6 @@ jme_alloc_and_feed_skb(struct jme_adapter *jme, int idx)
cpu_to_le16(RXWBFLAG_DEST_MUL))
++(NET_STAT(jme).multicast);
- jme->dev->last_rx = jiffies;
NET_STAT(jme).rx_bytes += framesize;
++(NET_STAT(jme).rx_packets);
}
@@ -1250,7 +1250,6 @@ static int
jme_poll(JME_NAPI_HOLDER(holder), JME_NAPI_WEIGHT(budget))
{
struct jme_adapter *jme = jme_napi_priv(holder);
- struct net_device *netdev = jme->dev;
int rest;
rest = jme_process_receive(jme, JME_NAPI_WEIGHT_VAL(budget));
@@ -2591,14 +2590,6 @@ static const struct ethtool_ops jme_ethtool_ops = {
static int
jme_pci_dma64(struct pci_dev *pdev)
{
- if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK))
- if (!pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))
- return 1;
-
- if (!pci_set_dma_mask(pdev, DMA_40BIT_MASK))
- if (!pci_set_consistent_dma_mask(pdev, DMA_40BIT_MASK))
- return 1;
-
if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK))
if (!pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))
return 0;
@@ -2626,6 +2617,18 @@ jme_check_hw_ver(struct jme_adapter *jme)
jme->chiprev = (chipmode & CM_CHIPREV_MASK) >> CM_CHIPREV_SHIFT;
}
+static const struct net_device_ops jme_netdev_ops = {
+ .ndo_open = jme_open,
+ .ndo_stop = jme_close,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_start_xmit = jme_start_xmit,
+ .ndo_set_mac_address = jme_set_macaddr,
+ .ndo_set_multicast_list = jme_set_multi,
+ .ndo_change_mtu = jme_change_mtu,
+ .ndo_tx_timeout = jme_tx_timeout,
+ .ndo_vlan_rx_register = jme_vlan_rx_register,
+};
+
static int __devinit
jme_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -2675,17 +2678,9 @@ jme_init_one(struct pci_dev *pdev,
rc = -ENOMEM;
goto err_out_release_regions;
}
- netdev->open = jme_open;
- netdev->stop = jme_close;
- netdev->hard_start_xmit = jme_start_xmit;
- netdev->set_mac_address = jme_set_macaddr;
- netdev->set_multicast_list = jme_set_multi;
- netdev->change_mtu = jme_change_mtu;
+ netdev->netdev_ops = &jme_netdev_ops;
netdev->ethtool_ops = &jme_ethtool_ops;
- netdev->tx_timeout = jme_tx_timeout;
netdev->watchdog_timeo = TX_TIMEOUT;
- netdev->vlan_rx_register = jme_vlan_rx_register;
- NETDEV_GET_STATS(netdev, &jme_get_stats);
netdev->features = NETIF_F_HW_CSUM |
NETIF_F_SG |
NETIF_F_TSO |
@@ -2861,18 +2856,10 @@ jme_init_one(struct pci_dev *pdev,
goto err_out_free_shadow;
}
- msg_probe(jme,
- "JMC250 gigabit%s ver:%x rev:%x "
- "macaddr:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ msg_probe(jme, "JMC250 gigabit%s ver:%x rev:%x macaddr:%pM\n",
(jme->fpgaver != 0) ? " (FPGA)" : "",
(jme->fpgaver != 0) ? jme->fpgaver : jme->chiprev,
- jme->rev,
- netdev->dev_addr[0],
- netdev->dev_addr[1],
- netdev->dev_addr[2],
- netdev->dev_addr[3],
- netdev->dev_addr[4],
- netdev->dev_addr[5]);
+ jme->rev, netdev->dev_addr);
return 0;
diff --git a/drivers/net/jme.h b/drivers/net/jme.h
index f863aee6648b..5154411b5e6b 100644
--- a/drivers/net/jme.h
+++ b/drivers/net/jme.h
@@ -22,7 +22,7 @@
*/
#ifndef __JME_H_INCLUDED__
-#define __JME_H_INCLUDEE__
+#define __JME_H_INCLUDED__
#define DRV_NAME "jme"
#define DRV_VERSION "1.0.3"
@@ -398,15 +398,15 @@ struct jme_ring {
#define JME_NAPI_WEIGHT(w) int w
#define JME_NAPI_WEIGHT_VAL(w) w
#define JME_NAPI_WEIGHT_SET(w, r)
-#define JME_RX_COMPLETE(dev, napis) netif_rx_complete(dev, napis)
+#define JME_RX_COMPLETE(dev, napis) netif_rx_complete(napis)
#define JME_NAPI_ENABLE(priv) napi_enable(&priv->napi);
#define JME_NAPI_DISABLE(priv) \
if (!napi_disable_pending(&priv->napi)) \
napi_disable(&priv->napi);
#define JME_RX_SCHEDULE_PREP(priv) \
- netif_rx_schedule_prep(priv->dev, &priv->napi)
+ netif_rx_schedule_prep(&priv->napi)
#define JME_RX_SCHEDULE(priv) \
- __netif_rx_schedule(priv->dev, &priv->napi);
+ __netif_rx_schedule(&priv->napi);
/*
* Jmac Adapter Private data
@@ -815,16 +815,30 @@ static inline u32 smi_phy_addr(int x)
* Global Host Control
*/
enum jme_ghc_bit_mask {
- GHC_SWRST = 0x40000000,
- GHC_DPX = 0x00000040,
- GHC_SPEED = 0x00000030,
- GHC_LINK_POLL = 0x00000001,
+ GHC_SWRST = 0x40000000,
+ GHC_DPX = 0x00000040,
+ GHC_SPEED = 0x00000030,
+ GHC_LINK_POLL = 0x00000001,
};
enum jme_ghc_speed_val {
- GHC_SPEED_10M = 0x00000010,
- GHC_SPEED_100M = 0x00000020,
- GHC_SPEED_1000M = 0x00000030,
+ GHC_SPEED_10M = 0x00000010,
+ GHC_SPEED_100M = 0x00000020,
+ GHC_SPEED_1000M = 0x00000030,
+};
+
+enum jme_ghc_to_clk {
+ GHC_TO_CLK_OFF = 0x00000000,
+ GHC_TO_CLK_GPHY = 0x00400000,
+ GHC_TO_CLK_PCIE = 0x00800000,
+ GHC_TO_CLK_INVALID = 0x00C00000,
+};
+
+enum jme_ghc_txmac_clk {
+ GHC_TXMAC_CLK_OFF = 0x00000000,
+ GHC_TXMAC_CLK_GPHY = 0x00100000,
+ GHC_TXMAC_CLK_PCIE = 0x00200000,
+ GHC_TXMAC_CLK_INVALID = 0x00300000,
};
/*
diff --git a/drivers/net/korina.c b/drivers/net/korina.c
index e18576316bda..4a5580c1126a 100644
--- a/drivers/net/korina.c
+++ b/drivers/net/korina.c
@@ -327,7 +327,7 @@ static irqreturn_t korina_rx_dma_interrupt(int irq, void *dev_id)
dmas = readl(&lp->rx_dma_regs->dmas);
if (dmas & (DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR)) {
- netif_rx_schedule_prep(dev, &lp->napi);
+ netif_rx_schedule_prep(&lp->napi);
dmasm = readl(&lp->rx_dma_regs->dmasm);
writel(dmasm | (DMA_STAT_DONE |
@@ -409,7 +409,6 @@ static int korina_rx(struct net_device *dev, int limit)
/* Pass the packet to upper layers */
netif_receive_skb(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
@@ -467,7 +466,7 @@ static int korina_poll(struct napi_struct *napi, int budget)
work_done = korina_rx(dev, budget);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
writel(readl(&lp->rx_dma_regs->dmasm) &
~(DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR),
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index 977ed3401bb3..d7afb938ea62 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -359,7 +359,7 @@ int __init init_module(void)
static void cleanup_card(struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
if (dev->dma != 4)
free_dma(dev->dma);
release_region(dev->base_addr, LANCE_TOTAL_SIZE);
@@ -418,7 +418,7 @@ static int __init do_lance_probe(struct net_device *dev)
if (card < NUM_CARDS) { /*Signature OK*/
result = lance_probe1(dev, ioaddr, 0, 0);
if (!result) {
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int ver = lp->chip_version;
r->name = chip_table[ver].name;
@@ -466,7 +466,6 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
unsigned long flags;
int err = -ENOMEM;
void __iomem *bios;
- DECLARE_MAC_BUF(mac);
/* First we look for special cases.
Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
@@ -520,7 +519,7 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
}
}
- /* We can't allocate dev->priv from alloc_etherdev() because it must
+ /* We can't allocate private data from alloc_etherdev() because it must
a ISA DMA-able region. */
chipname = chip_table[lance_version].name;
printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr);
@@ -529,7 +528,7 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
The first six bytes are the station address. */
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + i);
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
dev->base_addr = ioaddr;
/* Make certain the data structures used by the LANCE are aligned and DMAble. */
@@ -538,7 +537,7 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
if(lp==NULL)
return -ENODEV;
if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
- dev->priv = lp;
+ dev->ml_priv = lp;
lp->name = chipname;
lp->rx_buffs = (unsigned long)kmalloc(PKT_BUF_SZ*RX_RING_SIZE,
GFP_DMA | GFP_KERNEL);
@@ -742,7 +741,7 @@ out_lp:
static int
lance_open(struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int ioaddr = dev->base_addr;
int i;
@@ -830,7 +829,7 @@ lance_open(struct net_device *dev)
static void
lance_purge_ring(struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int i;
/* Free all the skbuffs in the Rx and Tx queues. */
@@ -854,7 +853,7 @@ lance_purge_ring(struct net_device *dev)
static void
lance_init_ring(struct net_device *dev, gfp_t gfp)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int i;
lp->cur_rx = lp->cur_tx = 0;
@@ -896,7 +895,7 @@ lance_init_ring(struct net_device *dev, gfp_t gfp)
static void
lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
if (must_reinit ||
(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
@@ -910,7 +909,7 @@ lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
static void lance_tx_timeout (struct net_device *dev)
{
- struct lance_private *lp = (struct lance_private *) dev->priv;
+ struct lance_private *lp = (struct lance_private *) dev->ml_priv;
int ioaddr = dev->base_addr;
outw (0, ioaddr + LANCE_ADDR);
@@ -944,7 +943,7 @@ static void lance_tx_timeout (struct net_device *dev)
static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int ioaddr = dev->base_addr;
int entry;
unsigned long flags;
@@ -1021,7 +1020,7 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id)
int must_restart;
ioaddr = dev->base_addr;
- lp = dev->priv;
+ lp = dev->ml_priv;
spin_lock (&lp->devlock);
@@ -1134,7 +1133,7 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id)
static int
lance_rx(struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
int entry = lp->cur_rx & RX_RING_MOD_MASK;
int i;
@@ -1191,7 +1190,6 @@ lance_rx(struct net_device *dev)
pkt_len);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes+=pkt_len;
}
@@ -1213,7 +1211,7 @@ static int
lance_close(struct net_device *dev)
{
int ioaddr = dev->base_addr;
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
netif_stop_queue (dev);
@@ -1246,7 +1244,7 @@ lance_close(struct net_device *dev)
static struct net_device_stats *lance_get_stats(struct net_device *dev)
{
- struct lance_private *lp = dev->priv;
+ struct lance_private *lp = dev->ml_priv;
if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
short ioaddr = dev->base_addr;
diff --git a/drivers/net/lib82596.c b/drivers/net/lib82596.c
index b59f442bbf36..7415f517491d 100644
--- a/drivers/net/lib82596.c
+++ b/drivers/net/lib82596.c
@@ -739,7 +739,6 @@ memory_squeeze:
skb->len = pkt_len;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -1034,12 +1033,8 @@ static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void print_eth(unsigned char *add, char *str)
{
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
-
- printk(KERN_DEBUG "i596 0x%p, %s --> %s %02X%02X, %s\n",
- add, print_mac(mac, add + 6), print_mac(mac2, add),
- add[12], add[13], str);
+ printk(KERN_DEBUG "i596 0x%p, %pM --> %pM %02X%02X, %s\n",
+ add, add + 6, add, add[12], add[13], str);
}
static int __devinit i82596_probe(struct net_device *dev)
@@ -1343,7 +1338,6 @@ static void set_multicast_list(struct net_device *dev)
struct i596_private *lp = netdev_priv(dev);
struct i596_dma *dma = lp->dma;
int config = 0, cnt;
- DECLARE_MAC_BUF(mac);
DEB(DEB_MULTI,
printk(KERN_DEBUG
@@ -1407,8 +1401,8 @@ static void set_multicast_list(struct net_device *dev)
if (i596_debug > 1)
DEB(DEB_MULTI,
printk(KERN_DEBUG
- "%s: Adding address %s\n",
- dev->name, print_mac(mac, cp)));
+ "%s: Adding address %pM\n",
+ dev->name, cp));
}
DMA_WBACK_INV(dev, &dma->mc_cmd, sizeof(struct mc_cmd));
i596_add_cmd(dev, &cmd->cmd);
diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c
index f80dcc11fe26..789b6cb744b2 100644
--- a/drivers/net/lib8390.c
+++ b/drivers/net/lib8390.c
@@ -108,14 +108,13 @@ int ei_debug = 1;
/* Index to functions. */
static void ei_tx_intr(struct net_device *dev);
static void ei_tx_err(struct net_device *dev);
-static void ei_tx_timeout(struct net_device *dev);
+void ei_tx_timeout(struct net_device *dev);
static void ei_receive(struct net_device *dev);
static void ei_rx_overrun(struct net_device *dev);
/* Routines generic to NS8390-based boards. */
static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
int start_page);
-static void set_multicast_list(struct net_device *dev);
static void do_set_multicast_list(struct net_device *dev);
static void __NS8390_init(struct net_device *dev, int startp);
@@ -206,10 +205,6 @@ static int __ei_open(struct net_device *dev)
unsigned long flags;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
- /* The card I/O part of the driver (e.g. 3c503) can hook a Tx timeout
- wrapper that does e.g. media check & then calls ei_tx_timeout. */
- if (dev->tx_timeout == NULL)
- dev->tx_timeout = ei_tx_timeout;
if (dev->watchdog_timeo <= 0)
dev->watchdog_timeo = TX_TIMEOUT;
@@ -258,7 +253,7 @@ static int __ei_close(struct net_device *dev)
* completed (or failed) - i.e. never posted a Tx related interrupt.
*/
-static void ei_tx_timeout(struct net_device *dev)
+static void __ei_tx_timeout(struct net_device *dev)
{
unsigned long e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
@@ -304,7 +299,7 @@ static void ei_tx_timeout(struct net_device *dev)
* Sends a packet to an 8390 network device.
*/
-static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int __ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
@@ -764,7 +759,6 @@ static void ei_receive(struct net_device *dev)
ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
if (pkt_stat & ENRSR_PHY)
@@ -883,7 +877,7 @@ static void ei_rx_overrun(struct net_device *dev)
* Collect the stats. This is called unlocked and from several contexts.
*/
-static struct net_device_stats *get_stats(struct net_device *dev)
+static struct net_device_stats *__ei_get_stats(struct net_device *dev)
{
unsigned long ioaddr = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
@@ -992,7 +986,7 @@ static void do_set_multicast_list(struct net_device *dev)
* not called too often. Must protect against both bh and irq users
*/
-static void set_multicast_list(struct net_device *dev)
+static void __ei_set_multicast_list(struct net_device *dev)
{
unsigned long flags;
struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev);
@@ -1016,10 +1010,6 @@ static void ethdev_setup(struct net_device *dev)
if (ei_debug > 1)
printk(version);
- dev->hard_start_xmit = &ei_start_xmit;
- dev->get_stats = get_stats;
- dev->set_multicast_list = &set_multicast_list;
-
ether_setup(dev);
spin_lock_init(&ei_local->page_lock);
diff --git a/drivers/net/lne390.c b/drivers/net/lne390.c
index b36989097883..41cbaaef0654 100644
--- a/drivers/net/lne390.c
+++ b/drivers/net/lne390.c
@@ -53,9 +53,6 @@ static const char *version =
static int lne390_probe1(struct net_device *dev, int ioaddr);
-static int lne390_open(struct net_device *dev);
-static int lne390_close(struct net_device *dev);
-
static void lne390_reset_8390(struct net_device *dev);
static void lne390_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
@@ -169,7 +166,6 @@ static int __init lne390_probe1(struct net_device *dev, int ioaddr)
{
int i, revision, ret;
unsigned long eisa_id;
- DECLARE_MAC_BUF(mac);
if (inb_p(ioaddr + LNE390_ID_PORT) == 0xff) return -ENODEV;
@@ -203,8 +199,8 @@ static int __init lne390_probe1(struct net_device *dev, int ioaddr)
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = inb(ioaddr + LNE390_SA_PROM + i);
- printk("lne390.c: LNE390%X in EISA slot %d, address %s.\n",
- 0xa+revision, ioaddr/0x1000, print_mac(mac, dev->dev_addr));
+ printk("lne390.c: LNE390%X in EISA slot %d, address %pM.\n",
+ 0xa+revision, ioaddr/0x1000, dev->dev_addr);
printk("lne390.c: ");
@@ -279,11 +275,7 @@ static int __init lne390_probe1(struct net_device *dev, int ioaddr)
ei_status.block_output = &lne390_block_output;
ei_status.get_8390_hdr = &lne390_get_8390_hdr;
- dev->open = &lne390_open;
- dev->stop = &lne390_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ei_netdev_ops;
NS8390_init(dev, 0);
ret = register_netdev(dev);
@@ -375,21 +367,6 @@ static void lne390_block_output(struct net_device *dev, int count,
memcpy_toio(shmem, buf, count);
}
-static int lne390_open(struct net_device *dev)
-{
- ei_open(dev);
- return 0;
-}
-
-static int lne390_close(struct net_device *dev)
-{
-
- if (ei_debug > 1)
- printk("%s: Shutting down ethercard.\n", dev->name);
-
- ei_close(dev);
- return 0;
-}
#ifdef MODULE
#define MAX_LNE_CARDS 4 /* Max number of LNE390 cards per module */
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index b1ac63ab8c16..b7d438a367f3 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -76,8 +76,6 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
skb->protocol = eth_type_trans(skb,dev);
- dev->last_rx = jiffies;
-
/* it's OK to use per_cpu_ptr() because BHs are off */
pcpu_lstats = dev->ml_priv;
lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id());
@@ -89,7 +87,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static struct net_device_stats *get_stats(struct net_device *dev)
+static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
const struct pcpu_lstats *pcpu_lstats;
struct net_device_stats *stats = &dev->stats;
@@ -145,15 +143,19 @@ static void loopback_dev_free(struct net_device *dev)
free_netdev(dev);
}
+static const struct net_device_ops loopback_ops = {
+ .ndo_init = loopback_dev_init,
+ .ndo_start_xmit= loopback_xmit,
+ .ndo_get_stats = loopback_get_stats,
+};
+
/*
* The loopback device is special. There is only one instance
* per network namespace.
*/
static void loopback_setup(struct net_device *dev)
{
- dev->get_stats = &get_stats;
dev->mtu = (16 * 1024) + 20 + 20 + 12;
- dev->hard_start_xmit = loopback_xmit;
dev->hard_header_len = ETH_HLEN; /* 14 */
dev->addr_len = ETH_ALEN; /* 6 */
dev->tx_queue_len = 0;
@@ -167,8 +169,8 @@ static void loopback_setup(struct net_device *dev)
| NETIF_F_NETNS_LOCAL;
dev->ethtool_ops = &loopback_ethtool_ops;
dev->header_ops = &eth_header_ops;
- dev->init = loopback_dev_init;
- dev->destructor = loopback_dev_free;
+ dev->netdev_ops = &loopback_ops;
+ dev->destructor = loopback_dev_free;
}
/* Setup and register the loopback device. */
@@ -206,17 +208,8 @@ static __net_exit void loopback_net_exit(struct net *net)
unregister_netdev(dev);
}
-static struct pernet_operations __net_initdata loopback_net_ops = {
+/* Registered in net/core/dev.c */
+struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
.exit = loopback_net_exit,
};
-
-static int __init loopback_init(void)
-{
- return register_pernet_device(&loopback_net_ops);
-}
-
-/* Loopback is special. It should be initialized before any other network
- * device and network subsystem.
- */
-fs_initcall(loopback_init);
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
index 83fa9d82a004..4d1a059921c6 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/lp486e.c
@@ -390,7 +390,7 @@ i596_timeout(struct net_device *dev, char *msg, int ct) {
struct i596_private *lp;
int boguscnt = ct;
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
while (lp->scb.command) {
if (--boguscnt == 0) {
printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n",
@@ -411,7 +411,7 @@ init_rx_bufs(struct net_device *dev, int num) {
int i;
// struct i596_rbd *rbd;
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
lp->scb.pa_rfd = I596_NULL;
for (i = 0; i < num; i++) {
@@ -468,7 +468,7 @@ remove_rx_bufs(struct net_device *dev) {
struct i596_private *lp;
struct i596_rfd *rfd;
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
lp->rx_tail->pa_next = I596_NULL;
do {
@@ -517,7 +517,7 @@ CLEAR_INT(void) {
/* selftest or dump */
static void
i596_port_do(struct net_device *dev, int portcmd, char *cmdname) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
u16 *outp;
int i, m;
@@ -541,7 +541,7 @@ i596_port_do(struct net_device *dev, int portcmd, char *cmdname) {
static int
i596_scp_setup(struct net_device *dev) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
int boguscnt;
/* Setup SCP, ISCP, SCB */
@@ -622,7 +622,7 @@ init_i596(struct net_device *dev) {
if (i596_scp_setup(dev))
return 1;
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
lp->scb.command = 0;
memcpy ((void *)lp->i596_config, init_setup, 14);
@@ -676,7 +676,6 @@ i596_rx_one(struct net_device *dev, struct i596_private *lp,
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
} else {
#if 0
@@ -705,7 +704,7 @@ i596_rx_one(struct net_device *dev, struct i596_private *lp,
static int
i596_rx(struct net_device *dev) {
- struct i596_private *lp = (struct i596_private *) dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
struct i596_rfd *rfd;
int frames = 0;
@@ -738,7 +737,7 @@ i596_cleanup_cmd(struct net_device *dev) {
struct i596_private *lp;
struct i596_cmd *cmd;
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
while (lp->cmd_head) {
cmd = (struct i596_cmd *)lp->cmd_head;
@@ -806,7 +805,7 @@ static void i596_reset(struct net_device *dev, struct i596_private *lp, int ioad
}
static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
unsigned long flags;
@@ -912,7 +911,7 @@ static int i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
static void
i596_tx_timeout (struct net_device *dev) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
/* Transmitter timeout, serious problems. */
@@ -970,7 +969,7 @@ static int __init lp486e_probe(struct net_device *dev) {
return -EBUSY;
}
- lp = (struct i596_private *) dev->priv;
+ lp = netdev_priv(dev);
spin_lock_init(&lp->cmd_lock);
/*
@@ -1147,7 +1146,7 @@ static irqreturn_t
i596_interrupt(int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
unsigned short status, ack_cmd = 0;
int frames_in = 0;
@@ -1215,7 +1214,7 @@ i596_interrupt(int irq, void *dev_instance)
}
static int i596_close(struct net_device *dev) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
netif_stop_queue(dev);
@@ -1242,7 +1241,7 @@ static int i596_close(struct net_device *dev) {
*/
static void set_multicast_list(struct net_device *dev) {
- struct i596_private *lp = dev->priv;
+ struct i596_private *lp = netdev_priv(dev);
struct i596_cmd *cmd;
if (i596_debug > 1)
diff --git a/drivers/net/mac8390.c b/drivers/net/mac8390.c
index 98e3eb2697c9..57716e22660c 100644
--- a/drivers/net/mac8390.c
+++ b/drivers/net/mac8390.c
@@ -304,7 +304,7 @@ struct net_device * __init mac8390_probe(int unit)
if (!MACH_IS_MAC)
return ERR_PTR(-ENODEV);
- dev = ____alloc_ei_netdev(0);
+ dev = alloc_ei_netdev();
if (!dev)
return ERR_PTR(-ENOMEM);
@@ -478,6 +478,20 @@ void cleanup_module(void)
#endif /* MODULE */
+static const struct net_device_ops mac8390_netdev_ops = {
+ .ndo_open = mac8390_open,
+ .ndo_stop = mac8390_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init mac8390_initdev(struct net_device * dev, struct nubus_dev * ndev,
enum mac8390_type type)
{
@@ -503,11 +517,7 @@ static int __init mac8390_initdev(struct net_device * dev, struct nubus_dev * nd
int access_bitmode = 0;
/* Now fill in our stuff */
- dev->open = &mac8390_open;
- dev->stop = &mac8390_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = __ei_poll;
-#endif
+ dev->netdev_ops = &mac8390_netdev_ops;
/* GAR, ei_status is actually a macro even though it looks global */
ei_status.name = cardname[type];
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c
index 4ce8afd481c3..380a1a54d530 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/mac89x0.c
@@ -181,7 +181,6 @@ struct net_device * __init mac89x0_probe(int unit)
unsigned long ioaddr;
unsigned short sig;
int err = -ENODEV;
- DECLARE_MAC_BUF(mac);
if (!MACH_IS_MAC)
return ERR_PTR(-ENODEV);
@@ -279,8 +278,7 @@ struct net_device * __init mac89x0_probe(int unit)
/* print the IRQ and ethernet address. */
- printk(" IRQ %d ADDR %s\n",
- dev->irq, print_mac(mac, dev->dev_addr));
+ printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr);
dev->open = net_open;
dev->stop = net_close;
@@ -518,7 +516,6 @@ net_rx(struct net_device *dev)
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += length;
}
@@ -628,14 +625,3 @@ cleanup_module(void)
free_netdev(dev_cs89x0);
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o mac89x0.o mac89x0.c"
- * version-control: t
- * kept-new-versions: 5
- * c-indent-level: 8
- * tab-width: 8
- * End:
- *
- */
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 01f7a31bac76..a04da4ecaa88 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -435,7 +435,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag,
bp->stats.rx_packets++;
bp->stats.rx_bytes += len;
- bp->dev->last_rx = jiffies;
dev_dbg(&bp->pdev->dev, "received skb of length %u, csum: %08x\n",
skb->len, skb->csum);
netif_receive_skb(skb);
@@ -520,7 +519,7 @@ static int macb_poll(struct napi_struct *napi, int budget)
* this function was called last time, and no packets
* have been received since.
*/
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
goto out;
}
@@ -531,13 +530,13 @@ static int macb_poll(struct napi_struct *napi, int budget)
dev_warn(&bp->pdev->dev,
"No RX buffers complete, status = %02lx\n",
(unsigned long)status);
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
goto out;
}
work_done = macb_rx(bp, budget);
if (work_done < budget)
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/*
* We've done what we can to clean the buffers. Make sure we
@@ -572,7 +571,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
}
if (status & MACB_RX_INT_FLAGS) {
- if (netif_rx_schedule_prep(dev, &bp->napi)) {
+ if (netif_rx_schedule_prep(&bp->napi)) {
/*
* There's no point taking any more interrupts
* until we have processed the buffers
@@ -580,7 +579,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id)
macb_writel(bp, IDR, MACB_RX_INT_FLAGS);
dev_dbg(&bp->pdev->dev,
"scheduling RX softirq\n");
- __netif_rx_schedule(dev, &bp->napi);
+ __netif_rx_schedule(&bp->napi);
}
}
@@ -1104,7 +1103,6 @@ static int __init macb_probe(struct platform_device *pdev)
unsigned long pclk_hz;
u32 config;
int err = -ENXIO;
- DECLARE_MAC_BUF(mac);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
@@ -1223,10 +1221,8 @@ static int __init macb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
- printk(KERN_INFO "%s: Atmel MACB at 0x%08lx irq %d "
- "(%s)\n",
- dev->name, dev->base_addr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Atmel MACB at 0x%08lx irq %d (%pM)\n",
+ dev->name, dev->base_addr, dev->irq, dev->dev_addr);
phydev = bp->phy_dev;
printk(KERN_INFO "%s: attached PHY driver [%s] "
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index 451acdca2a21..feebbd92aff2 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -101,7 +101,6 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i
struct mace_data *mp;
const unsigned char *addr;
int j, rev, rc = -EBUSY;
- DECLARE_MAC_BUF(mac);
if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) {
printk(KERN_ERR "can't use MACE %s: need 3 addrs and 3 irqs\n",
@@ -144,7 +143,7 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i
}
SET_NETDEV_DEV(dev, &mdev->ofdev.dev);
- mp = dev->priv;
+ mp = netdev_priv(dev);
mp->mdev = mdev;
macio_set_drvdata(mdev, dev);
@@ -165,7 +164,7 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i
in_8(&mp->mace->chipid_lo);
- mp = (struct mace_data *) dev->priv;
+ mp = netdev_priv(dev);
mp->maccc = ENXMT | ENRCV;
mp->tx_dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
@@ -241,8 +240,8 @@ static int __devinit mace_probe(struct macio_dev *mdev, const struct of_device_i
goto err_free_rx_irq;
}
- printk(KERN_INFO "%s: MACE at %s, chip revision %d.%d\n",
- dev->name, print_mac(mac, dev->dev_addr),
+ printk(KERN_INFO "%s: MACE at %pM, chip revision %d.%d\n",
+ dev->name, dev->dev_addr,
mp->chipid >> 8, mp->chipid & 0xff);
return 0;
@@ -276,7 +275,7 @@ static int __devexit mace_remove(struct macio_dev *mdev)
macio_set_drvdata(mdev, NULL);
- mp = dev->priv;
+ mp = netdev_priv(dev);
unregister_netdev(dev);
@@ -312,7 +311,7 @@ static void dbdma_reset(volatile struct dbdma_regs __iomem *dma)
static void mace_reset(struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
int i;
@@ -367,7 +366,7 @@ static void mace_reset(struct net_device *dev)
static void __mace_set_address(struct net_device *dev, void *addr)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
unsigned char *p = addr;
int i;
@@ -388,7 +387,7 @@ static void __mace_set_address(struct net_device *dev, void *addr)
static int mace_set_address(struct net_device *dev, void *addr)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
unsigned long flags;
@@ -423,7 +422,7 @@ static inline void mace_clean_rings(struct mace_data *mp)
static int mace_open(struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
volatile struct dbdma_regs __iomem *td = mp->tx_dma;
@@ -493,7 +492,7 @@ static int mace_open(struct net_device *dev)
static int mace_close(struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
volatile struct dbdma_regs __iomem *td = mp->tx_dma;
@@ -513,7 +512,7 @@ static int mace_close(struct net_device *dev)
static inline void mace_set_timeout(struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
if (mp->timeout_active)
del_timer(&mp->tx_timeout);
@@ -526,7 +525,7 @@ static inline void mace_set_timeout(struct net_device *dev)
static int mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct dbdma_regs __iomem *td = mp->tx_dma;
volatile struct dbdma_cmd *cp, *np;
unsigned long flags;
@@ -581,7 +580,7 @@ static int mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
static void mace_set_multicast(struct net_device *dev)
{
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
int i, j;
u32 crc;
@@ -656,7 +655,7 @@ static void mace_handle_misc_intrs(struct mace_data *mp, int intr, struct net_de
static irqreturn_t mace_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
volatile struct dbdma_regs __iomem *td = mp->tx_dma;
volatile struct dbdma_cmd *cp;
@@ -802,7 +801,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
static void mace_tx_timeout(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct mace __iomem *mb = mp->mace;
volatile struct dbdma_regs __iomem *td = mp->tx_dma;
volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
@@ -873,7 +872,7 @@ static irqreturn_t mace_txdma_intr(int irq, void *dev_id)
static irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct mace_data *mp = (struct mace_data *) dev->priv;
+ struct mace_data *mp = netdev_priv(dev);
volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
volatile struct dbdma_cmd *cp, *np;
int i, nb, stat, next;
@@ -929,7 +928,6 @@ static irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_bytes += skb->len;
netif_rx(skb);
- dev->last_rx = jiffies;
mp->rx_bufs[i] = NULL;
++dev->stats.rx_packets;
}
diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c
index 85587a6667b9..274e99bb63ac 100644
--- a/drivers/net/macmace.c
+++ b/drivers/net/macmace.c
@@ -194,7 +194,6 @@ static int __devinit mace_probe(struct platform_device *pdev)
unsigned char checksum = 0;
static int found = 0;
int err;
- DECLARE_MAC_BUF(mac);
if (found || macintosh_config->ether_type != MAC_ETHER_MACE)
return -ENODEV;
@@ -249,8 +248,8 @@ static int __devinit mace_probe(struct platform_device *pdev)
dev->set_multicast_list = mace_set_multicast;
dev->set_mac_address = mace_set_address;
- printk(KERN_INFO "%s: 68K MACE, hardware address %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: 68K MACE, hardware address %pM\n",
+ dev->name, dev->dev_addr);
err = register_netdev(dev);
if (!err)
@@ -674,7 +673,6 @@ static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += frame_length;
}
diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c
index e64c2086d33c..205bb05c25d6 100644
--- a/drivers/net/macsonic.c
+++ b/drivers/net/macsonic.c
@@ -220,7 +220,6 @@ static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev)
struct sonic_local *lp = netdev_priv(dev);
const int prom_addr = ONBOARD_SONIC_PROM_BASE;
int i;
- DECLARE_MAC_BUF(mac);
/* On NuBus boards we can sometimes look in the ROM resources.
No such luck for comm-slot/onboard. */
@@ -264,8 +263,8 @@ static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev)
dev->dev_addr[1] = val >> 8;
dev->dev_addr[0] = val & 0xff;
- printk(KERN_INFO "HW Address from CAM 15: %s\n",
- print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "HW Address from CAM 15: %pM\n",
+ dev->dev_addr);
} else return 0;
if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) &&
@@ -560,7 +559,6 @@ static int __init mac_sonic_probe(struct platform_device *pdev)
struct net_device *dev;
struct sonic_local *lp;
int err;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(sizeof(struct sonic_local));
if (!dev)
@@ -584,8 +582,7 @@ found:
if (err)
goto out;
- printk("%s: MAC %s IRQ %d\n",
- dev->name, print_mac(mac, dev->dev_addr), dev->irq);
+ printk("%s: MAC %pM IRQ %d\n", dev->name, dev->dev_addr, dev->irq);
return 0;
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 590039cbb146..7e24b5048686 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -87,7 +87,6 @@ static void macvlan_broadcast(struct sk_buff *skb,
dev->stats.rx_bytes += skb->len + ETH_HLEN;
dev->stats.rx_packets++;
dev->stats.multicast++;
- dev->last_rx = jiffies;
nskb->dev = dev;
if (!compare_ether_addr(eth->h_dest, dev->broadcast))
@@ -136,7 +135,6 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
dev->stats.rx_bytes += skb->len + ETH_HLEN;
dev->stats.rx_packets++;
- dev->last_rx = jiffies;
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
@@ -145,7 +143,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
return NULL;
}
-static int macvlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int macvlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
const struct macvlan_dev *vlan = netdev_priv(dev);
unsigned int len = skb->len;
@@ -336,24 +334,53 @@ static u32 macvlan_ethtool_get_rx_csum(struct net_device *dev)
return lowerdev->ethtool_ops->get_rx_csum(lowerdev);
}
+static int macvlan_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ const struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+
+ if (!lowerdev->ethtool_ops->get_settings)
+ return -EOPNOTSUPP;
+
+ return lowerdev->ethtool_ops->get_settings(lowerdev, cmd);
+}
+
+static u32 macvlan_ethtool_get_flags(struct net_device *dev)
+{
+ const struct macvlan_dev *vlan = netdev_priv(dev);
+ struct net_device *lowerdev = vlan->lowerdev;
+
+ if (!lowerdev->ethtool_ops->get_flags)
+ return 0;
+ return lowerdev->ethtool_ops->get_flags(lowerdev);
+}
+
static const struct ethtool_ops macvlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
+ .get_settings = macvlan_ethtool_get_settings,
.get_rx_csum = macvlan_ethtool_get_rx_csum,
.get_drvinfo = macvlan_ethtool_get_drvinfo,
+ .get_flags = macvlan_ethtool_get_flags,
+};
+
+static const struct net_device_ops macvlan_netdev_ops = {
+ .ndo_init = macvlan_init,
+ .ndo_open = macvlan_open,
+ .ndo_stop = macvlan_stop,
+ .ndo_start_xmit = macvlan_start_xmit,
+ .ndo_change_mtu = macvlan_change_mtu,
+ .ndo_change_rx_flags = macvlan_change_rx_flags,
+ .ndo_set_mac_address = macvlan_set_mac_address,
+ .ndo_set_multicast_list = macvlan_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
};
static void macvlan_setup(struct net_device *dev)
{
ether_setup(dev);
- dev->init = macvlan_init;
- dev->open = macvlan_open;
- dev->stop = macvlan_stop;
- dev->change_mtu = macvlan_change_mtu;
- dev->change_rx_flags = macvlan_change_rx_flags;
- dev->set_mac_address = macvlan_set_mac_address;
- dev->set_multicast_list = macvlan_set_multicast_list;
- dev->hard_start_xmit = macvlan_hard_start_xmit;
+ dev->netdev_ops = &macvlan_netdev_ops;
dev->destructor = free_netdev;
dev->header_ops = &macvlan_hard_header_ops,
dev->ethtool_ops = &macvlan_ethtool_ops;
diff --git a/drivers/net/meth.c b/drivers/net/meth.c
index a1e22ed1f6ee..c336a1f42510 100644
--- a/drivers/net/meth.c
+++ b/drivers/net/meth.c
@@ -94,10 +94,9 @@ char o2meth_eaddr[8]={0,0,0,0,0,0,0,0};
static inline void load_eaddr(struct net_device *dev)
{
int i;
- DECLARE_MAC_BUF(mac);
u64 macaddr;
- DPRINTK("Loading MAC Address: %s\n", print_mac(mac, dev->dev_addr));
+ DPRINTK("Loading MAC Address: %pM\n", dev->dev_addr);
macaddr = 0;
for (i = 0; i < 6; i++)
macaddr |= (u64)dev->dev_addr[i] << ((5 - i) * 8);
@@ -421,7 +420,6 @@ static void meth_rx(struct net_device* dev, unsigned long int_status)
skb_put(skb_c, len);
priv->rx_skbs[priv->rx_write] = skb;
skb_c->protocol = eth_type_trans(skb_c, dev);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
netif_rx(skb_c);
diff --git a/drivers/net/mlx4/cq.c b/drivers/net/mlx4/cq.c
index b7ad2829d67e..ac57b6a42c6e 100644
--- a/drivers/net/mlx4/cq.c
+++ b/drivers/net/mlx4/cq.c
@@ -189,7 +189,7 @@ EXPORT_SYMBOL_GPL(mlx4_cq_resize);
int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq,
- int collapsed)
+ unsigned vector, int collapsed)
{
struct mlx4_priv *priv = mlx4_priv(dev);
struct mlx4_cq_table *cq_table = &priv->cq_table;
@@ -198,6 +198,11 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
u64 mtt_addr;
int err;
+ if (vector >= dev->caps.num_comp_vectors)
+ return -EINVAL;
+
+ cq->vector = vector;
+
cq->cqn = mlx4_bitmap_alloc(&cq_table->bitmap);
if (cq->cqn == -1)
return -ENOMEM;
@@ -227,7 +232,7 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
cq_context->flags = cpu_to_be32(!!collapsed << 18);
cq_context->logsize_usrpage = cpu_to_be32((ilog2(nent) << 24) | uar->index);
- cq_context->comp_eqn = priv->eq_table.eq[MLX4_EQ_COMP].eqn;
+ cq_context->comp_eqn = priv->eq_table.eq[vector].eqn;
cq_context->log_page_size = mtt->page_shift - MLX4_ICM_PAGE_SHIFT;
mtt_addr = mlx4_mtt_addr(dev, mtt);
@@ -276,7 +281,7 @@ void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq)
if (err)
mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn);
- synchronize_irq(priv->eq_table.eq[MLX4_EQ_COMP].irq);
+ synchronize_irq(priv->eq_table.eq[cq->vector].irq);
spin_lock_irq(&cq_table->lock);
radix_tree_delete(&cq_table->tree, cq->cqn);
diff --git a/drivers/net/mlx4/en_cq.c b/drivers/net/mlx4/en_cq.c
index 1368a8010af4..91f50de84be9 100644
--- a/drivers/net/mlx4/en_cq.c
+++ b/drivers/net/mlx4/en_cq.c
@@ -51,10 +51,13 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
int err;
cq->size = entries;
- if (mode == RX)
+ if (mode == RX) {
cq->buf_size = cq->size * sizeof(struct mlx4_cqe);
- else
+ cq->vector = ring % mdev->dev->caps.num_comp_vectors;
+ } else {
cq->buf_size = sizeof(struct mlx4_cqe);
+ cq->vector = 0;
+ }
cq->ring = ring;
cq->is_tx = mode;
@@ -68,6 +71,8 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv,
err = mlx4_en_map_buffer(&cq->wqres.buf);
if (err)
mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size);
+ else
+ cq->buf = (struct mlx4_cqe *) cq->wqres.buf.direct.buf;
return err;
}
@@ -82,11 +87,10 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
cq->mcq.arm_db = cq->wqres.db.db + 1;
*cq->mcq.set_ci_db = 0;
*cq->mcq.arm_db = 0;
- cq->buf = (struct mlx4_cqe *) cq->wqres.buf.direct.buf;
memset(cq->buf, 0, cq->buf_size);
err = mlx4_cq_alloc(mdev->dev, cq->size, &cq->wqres.mtt, &mdev->priv_uar,
- cq->wqres.db.dma, &cq->mcq, cq->is_tx);
+ cq->wqres.db.dma, &cq->mcq, cq->vector, cq->is_tx);
if (err)
return err;
@@ -136,7 +140,6 @@ int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq)
{
- cq->armed = 1;
mlx4_cq_arm(&cq->mcq, MLX4_CQ_DB_REQ_NOT, priv->mdev->uar_map,
&priv->mdev->uar_lock);
diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/mlx4/en_main.c
index 4b9794e97a79..eda72dd2120f 100644
--- a/drivers/net/mlx4/en_main.c
+++ b/drivers/net/mlx4/en_main.c
@@ -169,13 +169,10 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) {
mlx4_info(mdev, "Using %d tx rings for port:%d\n",
mdev->profile.prof[i].tx_ring_num, i);
- if (!mdev->profile.prof[i].rx_ring_num) {
- mdev->profile.prof[i].rx_ring_num = 1;
- mlx4_info(mdev, "Defaulting to %d rx rings for port:%d\n",
- 1, i);
- } else
- mlx4_info(mdev, "Using %d rx rings for port:%d\n",
- mdev->profile.prof[i].rx_ring_num, i);
+ mdev->profile.prof[i].rx_ring_num =
+ min_t(int, dev->caps.num_comp_vectors, MAX_RX_RINGS);
+ mlx4_info(mdev, "Defaulting to %d rx rings for port:%d\n",
+ mdev->profile.prof[i].rx_ring_num, i);
}
/* Create our own workqueue for reset/multicast tasks
diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c
index 96e709d6440a..15bb38d99304 100644
--- a/drivers/net/mlx4/en_netdev.c
+++ b/drivers/net/mlx4/en_netdev.c
@@ -369,7 +369,6 @@ static struct net_device_stats *mlx4_en_get_stats(struct net_device *dev)
static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
{
- struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_cq *cq;
int i;
@@ -379,15 +378,8 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
* satisfy our coelsing target.
* - moder_time is set to a fixed value.
*/
- priv->rx_frames = (mdev->profile.rx_moder_cnt ==
- MLX4_EN_AUTO_CONF) ?
- MLX4_EN_RX_COAL_TARGET /
- priv->dev->mtu + 1 :
- mdev->profile.rx_moder_cnt;
- priv->rx_usecs = (mdev->profile.rx_moder_time ==
- MLX4_EN_AUTO_CONF) ?
- MLX4_EN_RX_COAL_TIME :
- mdev->profile.rx_moder_time;
+ priv->rx_frames = MLX4_EN_RX_COAL_TARGET / priv->dev->mtu + 1;
+ priv->rx_usecs = MLX4_EN_RX_COAL_TIME;
mlx4_dbg(INTR, priv, "Default coalesing params for mtu:%d - "
"rx_frames:%d rx_usecs:%d\n",
priv->dev->mtu, priv->rx_frames, priv->rx_usecs);
@@ -411,7 +403,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv)
priv->pkt_rate_high = MLX4_EN_RX_RATE_HIGH;
priv->rx_usecs_high = MLX4_EN_RX_COAL_TIME_HIGH;
priv->sample_interval = MLX4_EN_SAMPLE_INTERVAL;
- priv->adaptive_rx_coal = mdev->profile.auto_moder;
+ priv->adaptive_rx_coal = 1;
priv->last_moder_time = MLX4_EN_AUTO_CONF;
priv->last_moder_jiffies = 0;
priv->last_moder_packets = 0;
@@ -560,7 +552,7 @@ static void mlx4_en_linkstate(struct work_struct *work)
}
-static int mlx4_en_start_port(struct net_device *dev)
+int mlx4_en_start_port(struct net_device *dev)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
@@ -715,7 +707,7 @@ cq_err:
}
-static void mlx4_en_stop_port(struct net_device *dev)
+void mlx4_en_stop_port(struct net_device *dev)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
@@ -834,7 +826,7 @@ static int mlx4_en_close(struct net_device *dev)
return 0;
}
-static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
+void mlx4_en_free_resources(struct mlx4_en_priv *priv)
{
int i;
@@ -853,7 +845,7 @@ static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
}
}
-static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
+int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
{
struct mlx4_en_dev *mdev = priv->mdev;
struct mlx4_en_port_profile *prof = priv->prof;
@@ -953,6 +945,23 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static const struct net_device_ops mlx4_netdev_ops = {
+ .ndo_open = mlx4_en_open,
+ .ndo_stop = mlx4_en_close,
+ .ndo_start_xmit = mlx4_en_xmit,
+ .ndo_get_stats = mlx4_en_get_stats,
+ .ndo_set_multicast_list = mlx4_en_set_multicast,
+ .ndo_set_mac_address = mlx4_en_set_mac,
+ .ndo_change_mtu = mlx4_en_change_mtu,
+ .ndo_tx_timeout = mlx4_en_tx_timeout,
+ .ndo_vlan_rx_register = mlx4_en_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = mlx4_en_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mlx4_en_netpoll,
+#endif
+};
+
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
struct mlx4_en_port_profile *prof)
{
@@ -1029,22 +1038,9 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
/*
* Initialize netdev entry points
*/
-
- dev->open = &mlx4_en_open;
- dev->stop = &mlx4_en_close;
- dev->hard_start_xmit = &mlx4_en_xmit;
- dev->get_stats = &mlx4_en_get_stats;
- dev->set_multicast_list = &mlx4_en_set_multicast;
- dev->set_mac_address = &mlx4_en_set_mac;
- dev->change_mtu = &mlx4_en_change_mtu;
- dev->tx_timeout = &mlx4_en_tx_timeout;
+ dev->netdev_ops = &mlx4_netdev_ops;
dev->watchdog_timeo = MLX4_EN_WATCHDOG_TIMEOUT;
- dev->vlan_rx_register = mlx4_en_vlan_rx_register;
- dev->vlan_rx_add_vid = mlx4_en_vlan_rx_add_vid;
- dev->vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = mlx4_en_netpoll;
-#endif
+
SET_ETHTOOL_OPS(dev, &mlx4_en_ethtool_ops);
/* Set defualt MAC */
diff --git a/drivers/net/mlx4/en_params.c b/drivers/net/mlx4/en_params.c
index 95706ee1c019..cfeef0f1bacc 100644
--- a/drivers/net/mlx4/en_params.c
+++ b/drivers/net/mlx4/en_params.c
@@ -60,49 +60,26 @@ MLX4_EN_PARM_INT(num_lro, MLX4_EN_MAX_LRO_DESCRIPTORS,
"Number of LRO sessions per ring or disabled (0)");
/* Priority pausing */
-MLX4_EN_PARM_INT(pptx, MLX4_EN_DEF_TX_PAUSE,
- "Pause policy on TX: 0 never generate pause frames "
- "1 generate pause frames according to RX buffer threshold");
-MLX4_EN_PARM_INT(pprx, MLX4_EN_DEF_RX_PAUSE,
- "Pause policy on RX: 0 ignore received pause frames "
- "1 respect received pause frames");
MLX4_EN_PARM_INT(pfctx, 0, "Priority based Flow Control policy on TX[7:0]."
" Per priority bit mask");
MLX4_EN_PARM_INT(pfcrx, 0, "Priority based Flow Control policy on RX[7:0]."
" Per priority bit mask");
-/* Interrupt moderation tunning */
-MLX4_EN_PARM_INT(rx_moder_cnt, MLX4_EN_AUTO_CONF,
- "Max coalesced descriptors for Rx interrupt moderation");
-MLX4_EN_PARM_INT(rx_moder_time, MLX4_EN_AUTO_CONF,
- "Timeout following last packet for Rx interrupt moderation");
-MLX4_EN_PARM_INT(auto_moder, 1, "Enable dynamic interrupt moderation");
-
-MLX4_EN_PARM_INT(rx_ring_num1, 0, "Number or Rx rings for port 1 (0 = #cores)");
-MLX4_EN_PARM_INT(rx_ring_num2, 0, "Number or Rx rings for port 2 (0 = #cores)");
-
-MLX4_EN_PARM_INT(tx_ring_size1, MLX4_EN_AUTO_CONF, "Tx ring size for port 1");
-MLX4_EN_PARM_INT(tx_ring_size2, MLX4_EN_AUTO_CONF, "Tx ring size for port 2");
-MLX4_EN_PARM_INT(rx_ring_size1, MLX4_EN_AUTO_CONF, "Rx ring size for port 1");
-MLX4_EN_PARM_INT(rx_ring_size2, MLX4_EN_AUTO_CONF, "Rx ring size for port 2");
-
-
int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
{
struct mlx4_en_profile *params = &mdev->profile;
int i;
- params->rx_moder_cnt = min_t(int, rx_moder_cnt, MLX4_EN_AUTO_CONF);
- params->rx_moder_time = min_t(int, rx_moder_time, MLX4_EN_AUTO_CONF);
- params->auto_moder = auto_moder;
params->rss_xor = (rss_xor != 0);
params->rss_mask = rss_mask & 0x1f;
params->num_lro = min_t(int, num_lro , MLX4_EN_MAX_LRO_DESCRIPTORS);
for (i = 1; i <= MLX4_MAX_PORTS; i++) {
- params->prof[i].rx_pause = pprx;
+ params->prof[i].rx_pause = 1;
params->prof[i].rx_ppp = pfcrx;
- params->prof[i].tx_pause = pptx;
+ params->prof[i].tx_pause = 1;
params->prof[i].tx_ppp = pfctx;
+ params->prof[i].tx_ring_size = MLX4_EN_DEF_TX_RING_SIZE;
+ params->prof[i].rx_ring_size = MLX4_EN_DEF_RX_RING_SIZE;
}
if (pfcrx || pfctx) {
params->prof[1].tx_ring_num = MLX4_EN_TX_RING_NUM;
@@ -111,32 +88,7 @@ int mlx4_en_get_profile(struct mlx4_en_dev *mdev)
params->prof[1].tx_ring_num = 1;
params->prof[2].tx_ring_num = 1;
}
- params->prof[1].rx_ring_num = min_t(int, rx_ring_num1, MAX_RX_RINGS);
- params->prof[2].rx_ring_num = min_t(int, rx_ring_num2, MAX_RX_RINGS);
-
- if (tx_ring_size1 == MLX4_EN_AUTO_CONF)
- tx_ring_size1 = MLX4_EN_DEF_TX_RING_SIZE;
- params->prof[1].tx_ring_size =
- (tx_ring_size1 < MLX4_EN_MIN_TX_SIZE) ?
- MLX4_EN_MIN_TX_SIZE : roundup_pow_of_two(tx_ring_size1);
-
- if (tx_ring_size2 == MLX4_EN_AUTO_CONF)
- tx_ring_size2 = MLX4_EN_DEF_TX_RING_SIZE;
- params->prof[2].tx_ring_size =
- (tx_ring_size2 < MLX4_EN_MIN_TX_SIZE) ?
- MLX4_EN_MIN_TX_SIZE : roundup_pow_of_two(tx_ring_size2);
-
- if (rx_ring_size1 == MLX4_EN_AUTO_CONF)
- rx_ring_size1 = MLX4_EN_DEF_RX_RING_SIZE;
- params->prof[1].rx_ring_size =
- (rx_ring_size1 < MLX4_EN_MIN_RX_SIZE) ?
- MLX4_EN_MIN_RX_SIZE : roundup_pow_of_two(rx_ring_size1);
-
- if (rx_ring_size2 == MLX4_EN_AUTO_CONF)
- rx_ring_size2 = MLX4_EN_DEF_RX_RING_SIZE;
- params->prof[2].rx_ring_size =
- (rx_ring_size2 < MLX4_EN_MIN_RX_SIZE) ?
- MLX4_EN_MIN_RX_SIZE : roundup_pow_of_two(rx_ring_size2);
+
return 0;
}
@@ -433,6 +385,54 @@ static void mlx4_en_get_pauseparam(struct net_device *dev,
pause->rx_pause = priv->prof->rx_pause;
}
+static int mlx4_en_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *param)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = priv->mdev;
+ u32 rx_size, tx_size;
+ int port_up = 0;
+ int err = 0;
+
+ if (param->rx_jumbo_pending || param->rx_mini_pending)
+ return -EINVAL;
+
+ rx_size = roundup_pow_of_two(param->rx_pending);
+ rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE);
+ tx_size = roundup_pow_of_two(param->tx_pending);
+ tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE);
+
+ if (rx_size == priv->prof->rx_ring_size &&
+ tx_size == priv->prof->tx_ring_size)
+ return 0;
+
+ mutex_lock(&mdev->state_lock);
+ if (priv->port_up) {
+ port_up = 1;
+ mlx4_en_stop_port(dev);
+ }
+
+ mlx4_en_free_resources(priv);
+
+ priv->prof->tx_ring_size = tx_size;
+ priv->prof->rx_ring_size = rx_size;
+
+ err = mlx4_en_alloc_resources(priv);
+ if (err) {
+ mlx4_err(mdev, "Failed reallocating port resources\n");
+ goto out;
+ }
+ if (port_up) {
+ err = mlx4_en_start_port(dev);
+ if (err)
+ mlx4_err(mdev, "Failed starting port\n");
+ }
+
+out:
+ mutex_unlock(&mdev->state_lock);
+ return err;
+}
+
static void mlx4_en_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *param)
{
@@ -472,6 +472,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
.get_pauseparam = mlx4_en_get_pauseparam,
.set_pauseparam = mlx4_en_set_pauseparam,
.get_ringparam = mlx4_en_get_ringparam,
+ .set_ringparam = mlx4_en_set_ringparam,
.get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags,
};
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c
index 6232227f56c3..c61b0bdca1a4 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/mlx4/en_rx.c
@@ -443,7 +443,8 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv)
/* Fill Rx buffers */
ring->full = 0;
}
- if (mlx4_en_fill_rx_buffers(priv))
+ err = mlx4_en_fill_rx_buffers(priv);
+ if (err)
goto err_buffers;
for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) {
@@ -776,8 +777,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
} else
netif_receive_skb(skb);
- dev->last_rx = jiffies;
-
next:
++cq->mcq.cons_index;
index = (cq->mcq.cons_index) & ring->size_mask;
@@ -815,7 +814,7 @@ void mlx4_en_rx_irq(struct mlx4_cq *mcq)
struct mlx4_en_priv *priv = netdev_priv(cq->dev);
if (priv->port_up)
- netif_rx_schedule(cq->dev, &cq->napi);
+ netif_rx_schedule(&cq->napi);
else
mlx4_en_arm_cq(priv, cq);
}
@@ -835,7 +834,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
INC_PERF_COUNTER(priv->pstats.napi_quota);
else {
/* Done for now */
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
mlx4_en_arm_cq(priv, cq);
}
return done;
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c
index 8592f8fb8475..ff4d75205c25 100644
--- a/drivers/net/mlx4/en_tx.c
+++ b/drivers/net/mlx4/en_tx.c
@@ -379,8 +379,8 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
/* Wakeup Tx queue if this ring stopped it */
if (unlikely(ring->blocked)) {
- if (((u32) (ring->prod - ring->cons) <=
- ring->size - HEADROOM - MAX_DESC_TXBBS) && !cq->armed) {
+ if ((u32) (ring->prod - ring->cons) <=
+ ring->size - HEADROOM - MAX_DESC_TXBBS) {
/* TODO: support multiqueue netdevs. Currently, we block
* when *any* ring is full. Note that:
@@ -404,14 +404,11 @@ void mlx4_en_tx_irq(struct mlx4_cq *mcq)
struct mlx4_en_priv *priv = netdev_priv(cq->dev);
struct mlx4_en_tx_ring *ring = &priv->tx_ring[cq->ring];
- spin_lock_irq(&ring->comp_lock);
- cq->armed = 0;
+ if (!spin_trylock(&ring->comp_lock))
+ return;
mlx4_en_process_tx_cq(cq->dev, cq);
- if (ring->blocked)
- mlx4_en_arm_cq(priv, cq);
- else
- mod_timer(&cq->timer, jiffies + 1);
- spin_unlock_irq(&ring->comp_lock);
+ mod_timer(&cq->timer, jiffies + 1);
+ spin_unlock(&ring->comp_lock);
}
@@ -424,8 +421,10 @@ void mlx4_en_poll_tx_cq(unsigned long data)
INC_PERF_COUNTER(priv->pstats.tx_poll);
- netif_tx_lock(priv->dev);
- spin_lock_irq(&ring->comp_lock);
+ if (!spin_trylock(&ring->comp_lock)) {
+ mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT);
+ return;
+ }
mlx4_en_process_tx_cq(cq->dev, cq);
inflight = (u32) (ring->prod - ring->cons - ring->last_nr_txbb);
@@ -435,8 +434,7 @@ void mlx4_en_poll_tx_cq(unsigned long data)
if (inflight && priv->port_up)
mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT);
- spin_unlock_irq(&ring->comp_lock);
- netif_tx_unlock(priv->dev);
+ spin_unlock(&ring->comp_lock);
}
static struct mlx4_en_tx_desc *mlx4_en_bounce_to_desc(struct mlx4_en_priv *priv,
@@ -479,7 +477,10 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind)
/* Poll the CQ every mlx4_en_TX_MODER_POLL packets */
if ((++ring->poll_cnt & (MLX4_EN_TX_POLL_MODER - 1)) == 0)
- mlx4_en_process_tx_cq(priv->dev, cq);
+ if (spin_trylock(&ring->comp_lock)) {
+ mlx4_en_process_tx_cq(priv->dev, cq);
+ spin_unlock(&ring->comp_lock);
+ }
}
static void *get_frag_ptr(struct sk_buff *skb)
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c
index de169338cd90..2c19bff7cbab 100644
--- a/drivers/net/mlx4/eq.c
+++ b/drivers/net/mlx4/eq.c
@@ -243,10 +243,6 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
* least that often.
*/
if (unlikely(set_ci >= MLX4_NUM_SPARE_EQE)) {
- /*
- * Conditional on hca_type is OK here because
- * this is a rare case, not the fast path.
- */
eq_set_ci(eq, 0);
set_ci = 0;
}
@@ -266,7 +262,7 @@ static irqreturn_t mlx4_interrupt(int irq, void *dev_ptr)
writel(priv->eq_table.clr_mask, priv->eq_table.clr_int);
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i)
work |= mlx4_eq_int(dev, &priv->eq_table.eq[i]);
return IRQ_RETVAL(work);
@@ -304,6 +300,17 @@ static int mlx4_HW2SW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
MLX4_CMD_TIME_CLASS_A);
}
+static int mlx4_num_eq_uar(struct mlx4_dev *dev)
+{
+ /*
+ * Each UAR holds 4 EQ doorbells. To figure out how many UARs
+ * we need to map, take the difference of highest index and
+ * the lowest index we'll use and add 1.
+ */
+ return (dev->caps.num_comp_vectors + 1 + dev->caps.reserved_eqs) / 4 -
+ dev->caps.reserved_eqs / 4 + 1;
+}
+
static void __iomem *mlx4_get_eq_uar(struct mlx4_dev *dev, struct mlx4_eq *eq)
{
struct mlx4_priv *priv = mlx4_priv(dev);
@@ -483,9 +490,11 @@ static void mlx4_free_irqs(struct mlx4_dev *dev)
if (eq_table->have_irq)
free_irq(dev->pdev->irq, dev);
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i)
if (eq_table->eq[i].have_irq)
free_irq(eq_table->eq[i].irq, eq_table->eq + i);
+
+ kfree(eq_table->irq_names);
}
static int mlx4_map_clr_int(struct mlx4_dev *dev)
@@ -551,57 +560,93 @@ void mlx4_unmap_eq_icm(struct mlx4_dev *dev)
__free_page(priv->eq_table.icm_page);
}
+int mlx4_alloc_eq_table(struct mlx4_dev *dev)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ priv->eq_table.eq = kcalloc(dev->caps.num_eqs - dev->caps.reserved_eqs,
+ sizeof *priv->eq_table.eq, GFP_KERNEL);
+ if (!priv->eq_table.eq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void mlx4_free_eq_table(struct mlx4_dev *dev)
+{
+ kfree(mlx4_priv(dev)->eq_table.eq);
+}
+
int mlx4_init_eq_table(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
int err;
int i;
+ priv->eq_table.uar_map = kcalloc(sizeof *priv->eq_table.uar_map,
+ mlx4_num_eq_uar(dev), GFP_KERNEL);
+ if (!priv->eq_table.uar_map) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+
err = mlx4_bitmap_init(&priv->eq_table.bitmap, dev->caps.num_eqs,
dev->caps.num_eqs - 1, dev->caps.reserved_eqs, 0);
if (err)
- return err;
+ goto err_out_free;
- for (i = 0; i < ARRAY_SIZE(priv->eq_table.uar_map); ++i)
+ for (i = 0; i < mlx4_num_eq_uar(dev); ++i)
priv->eq_table.uar_map[i] = NULL;
err = mlx4_map_clr_int(dev);
if (err)
- goto err_out_free;
+ goto err_out_bitmap;
priv->eq_table.clr_mask =
swab32(1 << (priv->eq_table.inta_pin & 31));
priv->eq_table.clr_int = priv->clr_base +
(priv->eq_table.inta_pin < 32 ? 4 : 0);
- err = mlx4_create_eq(dev, dev->caps.num_cqs + MLX4_NUM_SPARE_EQE,
- (dev->flags & MLX4_FLAG_MSI_X) ? MLX4_EQ_COMP : 0,
- &priv->eq_table.eq[MLX4_EQ_COMP]);
- if (err)
- goto err_out_unmap;
+ priv->eq_table.irq_names = kmalloc(16 * dev->caps.num_comp_vectors, GFP_KERNEL);
+ if (!priv->eq_table.irq_names) {
+ err = -ENOMEM;
+ goto err_out_bitmap;
+ }
+
+ for (i = 0; i < dev->caps.num_comp_vectors; ++i) {
+ err = mlx4_create_eq(dev, dev->caps.num_cqs + MLX4_NUM_SPARE_EQE,
+ (dev->flags & MLX4_FLAG_MSI_X) ? i : 0,
+ &priv->eq_table.eq[i]);
+ if (err)
+ goto err_out_unmap;
+ }
err = mlx4_create_eq(dev, MLX4_NUM_ASYNC_EQE + MLX4_NUM_SPARE_EQE,
- (dev->flags & MLX4_FLAG_MSI_X) ? MLX4_EQ_ASYNC : 0,
- &priv->eq_table.eq[MLX4_EQ_ASYNC]);
+ (dev->flags & MLX4_FLAG_MSI_X) ? dev->caps.num_comp_vectors : 0,
+ &priv->eq_table.eq[dev->caps.num_comp_vectors]);
if (err)
goto err_out_comp;
if (dev->flags & MLX4_FLAG_MSI_X) {
- static const char *eq_name[] = {
- [MLX4_EQ_COMP] = DRV_NAME " (comp)",
- [MLX4_EQ_ASYNC] = DRV_NAME " (async)"
- };
+ static const char async_eq_name[] = "mlx4-async";
+ const char *eq_name;
+
+ for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) {
+ if (i < dev->caps.num_comp_vectors) {
+ snprintf(priv->eq_table.irq_names + i * 16, 16,
+ "mlx4-comp-%d", i);
+ eq_name = priv->eq_table.irq_names + i * 16;
+ } else
+ eq_name = async_eq_name;
- for (i = 0; i < MLX4_NUM_EQ; ++i) {
err = request_irq(priv->eq_table.eq[i].irq,
- mlx4_msi_x_interrupt,
- 0, eq_name[i], priv->eq_table.eq + i);
+ mlx4_msi_x_interrupt, 0, eq_name,
+ priv->eq_table.eq + i);
if (err)
goto err_out_async;
priv->eq_table.eq[i].have_irq = 1;
}
-
} else {
err = request_irq(dev->pdev->irq, mlx4_interrupt,
IRQF_SHARED, DRV_NAME, dev);
@@ -612,28 +657,36 @@ int mlx4_init_eq_table(struct mlx4_dev *dev)
}
err = mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 0,
- priv->eq_table.eq[MLX4_EQ_ASYNC].eqn);
+ priv->eq_table.eq[dev->caps.num_comp_vectors].eqn);
if (err)
mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n",
- priv->eq_table.eq[MLX4_EQ_ASYNC].eqn, err);
+ priv->eq_table.eq[dev->caps.num_comp_vectors].eqn, err);
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i)
eq_set_ci(&priv->eq_table.eq[i], 1);
return 0;
err_out_async:
- mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_ASYNC]);
+ mlx4_free_eq(dev, &priv->eq_table.eq[dev->caps.num_comp_vectors]);
err_out_comp:
- mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_COMP]);
+ i = dev->caps.num_comp_vectors - 1;
err_out_unmap:
+ while (i >= 0) {
+ mlx4_free_eq(dev, &priv->eq_table.eq[i]);
+ --i;
+ }
mlx4_unmap_clr_int(dev);
mlx4_free_irqs(dev);
-err_out_free:
+err_out_bitmap:
mlx4_bitmap_cleanup(&priv->eq_table.bitmap);
+
+err_out_free:
+ kfree(priv->eq_table.uar_map);
+
return err;
}
@@ -643,18 +696,20 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev)
int i;
mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1,
- priv->eq_table.eq[MLX4_EQ_ASYNC].eqn);
+ priv->eq_table.eq[dev->caps.num_comp_vectors].eqn);
mlx4_free_irqs(dev);
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i)
mlx4_free_eq(dev, &priv->eq_table.eq[i]);
mlx4_unmap_clr_int(dev);
- for (i = 0; i < ARRAY_SIZE(priv->eq_table.uar_map); ++i)
+ for (i = 0; i < mlx4_num_eq_uar(dev); ++i)
if (priv->eq_table.uar_map[i])
iounmap(priv->eq_table.uar_map[i]);
mlx4_bitmap_cleanup(&priv->eq_table.bitmap);
+
+ kfree(priv->eq_table.uar_map);
}
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index 90a0281d15ea..710c79e7a2db 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -421,9 +421,7 @@ static int mlx4_init_cmpt_table(struct mlx4_dev *dev, u64 cmpt_base,
((u64) (MLX4_CMPT_TYPE_EQ *
cmpt_entry_sz) << MLX4_CMPT_SHIFT),
cmpt_entry_sz,
- roundup_pow_of_two(MLX4_NUM_EQ +
- dev->caps.reserved_eqs),
- MLX4_NUM_EQ + dev->caps.reserved_eqs, 0, 0);
+ dev->caps.num_eqs, dev->caps.num_eqs, 0, 0);
if (err)
goto err_cq;
@@ -810,12 +808,12 @@ static int mlx4_setup_hca(struct mlx4_dev *dev)
if (dev->flags & MLX4_FLAG_MSI_X) {
mlx4_warn(dev, "NOP command failed to generate MSI-X "
"interrupt IRQ %d).\n",
- priv->eq_table.eq[MLX4_EQ_ASYNC].irq);
+ priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
mlx4_warn(dev, "Trying again without MSI-X.\n");
} else {
mlx4_err(dev, "NOP command failed to generate interrupt "
"(IRQ %d), aborting.\n",
- priv->eq_table.eq[MLX4_EQ_ASYNC].irq);
+ priv->eq_table.eq[dev->caps.num_comp_vectors].irq);
mlx4_err(dev, "BIOS or ACPI interrupt routing problem?\n");
}
@@ -908,31 +906,50 @@ err_uar_table_free:
static void mlx4_enable_msi_x(struct mlx4_dev *dev)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- struct msix_entry entries[MLX4_NUM_EQ];
+ struct msix_entry *entries;
+ int nreq;
int err;
int i;
if (msi_x) {
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ nreq = min(dev->caps.num_eqs - dev->caps.reserved_eqs,
+ num_possible_cpus() + 1);
+ entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL);
+ if (!entries)
+ goto no_msi;
+
+ for (i = 0; i < nreq; ++i)
entries[i].entry = i;
- err = pci_enable_msix(dev->pdev, entries, ARRAY_SIZE(entries));
+ retry:
+ err = pci_enable_msix(dev->pdev, entries, nreq);
if (err) {
- if (err > 0)
- mlx4_info(dev, "Only %d MSI-X vectors available, "
- "not using MSI-X\n", err);
+ /* Try again if at least 2 vectors are available */
+ if (err > 1) {
+ mlx4_info(dev, "Requested %d vectors, "
+ "but only %d MSI-X vectors available, "
+ "trying again\n", nreq, err);
+ nreq = err;
+ goto retry;
+ }
+
goto no_msi;
}
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ dev->caps.num_comp_vectors = nreq - 1;
+ for (i = 0; i < nreq; ++i)
priv->eq_table.eq[i].irq = entries[i].vector;
dev->flags |= MLX4_FLAG_MSI_X;
+
+ kfree(entries);
return;
}
no_msi:
- for (i = 0; i < MLX4_NUM_EQ; ++i)
+ dev->caps.num_comp_vectors = 1;
+
+ for (i = 0; i < 2; ++i)
priv->eq_table.eq[i].irq = dev->pdev->irq;
}
@@ -1074,6 +1091,10 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
goto err_cmd;
+ err = mlx4_alloc_eq_table(dev);
+ if (err)
+ goto err_close;
+
mlx4_enable_msi_x(dev);
err = mlx4_setup_hca(dev);
@@ -1084,7 +1105,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
if (err)
- goto err_close;
+ goto err_free_eq;
for (port = 1; port <= dev->caps.num_ports; port++) {
err = mlx4_init_port_info(dev, port);
@@ -1114,6 +1135,9 @@ err_port:
mlx4_cleanup_pd_table(dev);
mlx4_cleanup_uar_table(dev);
+err_free_eq:
+ mlx4_free_eq_table(dev);
+
err_close:
if (dev->flags & MLX4_FLAG_MSI_X)
pci_disable_msix(pdev);
@@ -1177,6 +1201,7 @@ static void mlx4_remove_one(struct pci_dev *pdev)
iounmap(priv->kar);
mlx4_uar_free(dev, &priv->driver_uar);
mlx4_cleanup_uar_table(dev);
+ mlx4_free_eq_table(dev);
mlx4_close_hca(dev);
mlx4_cmd_cleanup(dev);
diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c
index 592c01ae2c5d..6053c357a470 100644
--- a/drivers/net/mlx4/mcg.c
+++ b/drivers/net/mlx4/mcg.c
@@ -118,17 +118,7 @@ static int find_mgm(struct mlx4_dev *dev,
return err;
if (0)
- mlx4_dbg(dev, "Hash for %04x:%04x:%04x:%04x:"
- "%04x:%04x:%04x:%04x is %04x\n",
- be16_to_cpu(((__be16 *) gid)[0]),
- be16_to_cpu(((__be16 *) gid)[1]),
- be16_to_cpu(((__be16 *) gid)[2]),
- be16_to_cpu(((__be16 *) gid)[3]),
- be16_to_cpu(((__be16 *) gid)[4]),
- be16_to_cpu(((__be16 *) gid)[5]),
- be16_to_cpu(((__be16 *) gid)[6]),
- be16_to_cpu(((__be16 *) gid)[7]),
- *hash);
+ mlx4_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
*index = *hash;
*prev = -1;
@@ -215,7 +205,7 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
if (block_mcast_loopback)
mgm->qp[members_count++] = cpu_to_be32((qp->qpn & MGM_QPN_MASK) |
- (1 << MGM_BLCK_LB_BIT));
+ (1U << MGM_BLCK_LB_BIT));
else
mgm->qp[members_count++] = cpu_to_be32(qp->qpn & MGM_QPN_MASK);
@@ -277,16 +267,7 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16])
goto out;
if (index == -1) {
- mlx4_err(dev, "MGID %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
- "not found\n",
- be16_to_cpu(((__be16 *) gid)[0]),
- be16_to_cpu(((__be16 *) gid)[1]),
- be16_to_cpu(((__be16 *) gid)[2]),
- be16_to_cpu(((__be16 *) gid)[3]),
- be16_to_cpu(((__be16 *) gid)[4]),
- be16_to_cpu(((__be16 *) gid)[5]),
- be16_to_cpu(((__be16 *) gid)[6]),
- be16_to_cpu(((__be16 *) gid)[7]));
+ mlx4_err(dev, "MGID %pI6 not found\n", gid);
err = -EINVAL;
goto out;
}
diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h
index 34c909deaff3..e0213bad61c7 100644
--- a/drivers/net/mlx4/mlx4.h
+++ b/drivers/net/mlx4/mlx4.h
@@ -63,12 +63,6 @@ enum {
};
enum {
- MLX4_EQ_ASYNC,
- MLX4_EQ_COMP,
- MLX4_NUM_EQ
-};
-
-enum {
MLX4_NUM_PDS = 1 << 15
};
@@ -205,10 +199,11 @@ struct mlx4_cq_table {
struct mlx4_eq_table {
struct mlx4_bitmap bitmap;
+ char *irq_names;
void __iomem *clr_int;
- void __iomem *uar_map[(MLX4_NUM_EQ + 6) / 4];
+ void __iomem **uar_map;
u32 clr_mask;
- struct mlx4_eq eq[MLX4_NUM_EQ];
+ struct mlx4_eq *eq;
u64 icm_virt;
struct page *icm_page;
dma_addr_t icm_dma;
@@ -328,6 +323,9 @@ void mlx4_bitmap_cleanup(struct mlx4_bitmap *bitmap);
int mlx4_reset(struct mlx4_dev *dev);
+int mlx4_alloc_eq_table(struct mlx4_dev *dev);
+void mlx4_free_eq_table(struct mlx4_dev *dev);
+
int mlx4_init_pd_table(struct mlx4_dev *dev);
int mlx4_init_uar_table(struct mlx4_dev *dev);
int mlx4_init_mr_table(struct mlx4_dev *dev);
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h
index 98ddc0811f93..2e96c7b2180a 100644
--- a/drivers/net/mlx4/mlx4_en.h
+++ b/drivers/net/mlx4/mlx4_en.h
@@ -58,17 +58,17 @@
#define mlx4_dbg(mlevel, priv, format, arg...) \
if (NETIF_MSG_##mlevel & priv->msg_enable) \
printk(KERN_DEBUG "%s %s: " format , DRV_NAME ,\
- (&priv->mdev->pdev->dev)->bus_id , ## arg)
+ (dev_name(&priv->mdev->pdev->dev)) , ## arg)
#define mlx4_err(mdev, format, arg...) \
printk(KERN_ERR "%s %s: " format , DRV_NAME ,\
- (&mdev->pdev->dev)->bus_id , ## arg)
+ (dev_name(&mdev->pdev->dev)) , ## arg)
#define mlx4_info(mdev, format, arg...) \
printk(KERN_INFO "%s %s: " format , DRV_NAME ,\
- (&mdev->pdev->dev)->bus_id , ## arg)
+ (dev_name(&mdev->pdev->dev)) , ## arg)
#define mlx4_warn(mdev, format, arg...) \
printk(KERN_WARNING "%s %s: " format , DRV_NAME ,\
- (&mdev->pdev->dev)->bus_id , ## arg)
+ (dev_name(&mdev->pdev->dev)) , ## arg)
/*
* Device constants
@@ -311,7 +311,6 @@ struct mlx4_en_cq {
enum cq_type is_tx;
u16 moder_time;
u16 moder_cnt;
- int armed;
struct mlx4_cqe *buf;
#define MLX4_EN_OPCODE_ERROR 0x1e
};
@@ -334,9 +333,6 @@ struct mlx4_en_profile {
u8 rss_mask;
u32 active_ports;
u32 small_pkt_int;
- int rx_moder_cnt;
- int rx_moder_time;
- int auto_moder;
u8 no_reset;
struct mlx4_en_port_profile prof[MLX4_MAX_PORTS + 1];
};
@@ -493,6 +489,12 @@ void mlx4_en_destroy_netdev(struct net_device *dev);
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
struct mlx4_en_port_profile *prof);
+int mlx4_en_start_port(struct net_device *dev);
+void mlx4_en_stop_port(struct net_device *dev);
+
+void mlx4_en_free_resources(struct mlx4_en_priv *priv);
+int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
+
int mlx4_en_get_profile(struct mlx4_en_dev *mdev);
int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
diff --git a/drivers/net/mlx4/profile.c b/drivers/net/mlx4/profile.c
index 9ca42b213d54..919fb9eb1b62 100644
--- a/drivers/net/mlx4/profile.c
+++ b/drivers/net/mlx4/profile.c
@@ -107,7 +107,9 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
profile[MLX4_RES_AUXC].num = request->num_qp;
profile[MLX4_RES_SRQ].num = request->num_srq;
profile[MLX4_RES_CQ].num = request->num_cq;
- profile[MLX4_RES_EQ].num = MLX4_NUM_EQ + dev_cap->reserved_eqs;
+ profile[MLX4_RES_EQ].num = min(dev_cap->max_eqs,
+ dev_cap->reserved_eqs +
+ num_possible_cpus() + 1);
profile[MLX4_RES_DMPT].num = request->num_mpt;
profile[MLX4_RES_CMPT].num = MLX4_NUM_CMPTS;
profile[MLX4_RES_MTT].num = request->num_mtt;
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index e513f76f2a9f..7253a499d9c8 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -51,8 +51,8 @@
#include <linux/workqueue.h>
#include <linux/phy.h>
#include <linux/mv643xx_eth.h>
-#include <asm/io.h>
-#include <asm/types.h>
+#include <linux/io.h>
+#include <linux/types.h>
#include <asm/system.h>
static char mv643xx_eth_driver_name[] = "mv643xx_eth";
@@ -78,16 +78,17 @@ static char mv643xx_eth_driver_version[] = "1.4";
#define WINDOW_PROTECT(w) (0x0294 + ((w) << 4))
/*
- * Per-port registers.
+ * Main per-port registers. These live at offset 0x0400 for
+ * port #0, 0x0800 for port #1, and 0x0c00 for port #2.
*/
-#define PORT_CONFIG(p) (0x0400 + ((p) << 10))
+#define PORT_CONFIG 0x0000
#define UNICAST_PROMISCUOUS_MODE 0x00000001
-#define PORT_CONFIG_EXT(p) (0x0404 + ((p) << 10))
-#define MAC_ADDR_LOW(p) (0x0414 + ((p) << 10))
-#define MAC_ADDR_HIGH(p) (0x0418 + ((p) << 10))
-#define SDMA_CONFIG(p) (0x041c + ((p) << 10))
-#define PORT_SERIAL_CONTROL(p) (0x043c + ((p) << 10))
-#define PORT_STATUS(p) (0x0444 + ((p) << 10))
+#define PORT_CONFIG_EXT 0x0004
+#define MAC_ADDR_LOW 0x0014
+#define MAC_ADDR_HIGH 0x0018
+#define SDMA_CONFIG 0x001c
+#define PORT_SERIAL_CONTROL 0x003c
+#define PORT_STATUS 0x0044
#define TX_FIFO_EMPTY 0x00000400
#define TX_IN_PROGRESS 0x00000080
#define PORT_SPEED_MASK 0x00000030
@@ -97,31 +98,35 @@ static char mv643xx_eth_driver_version[] = "1.4";
#define FLOW_CONTROL_ENABLED 0x00000008
#define FULL_DUPLEX 0x00000004
#define LINK_UP 0x00000002
-#define TXQ_COMMAND(p) (0x0448 + ((p) << 10))
-#define TXQ_FIX_PRIO_CONF(p) (0x044c + ((p) << 10))
-#define TX_BW_RATE(p) (0x0450 + ((p) << 10))
-#define TX_BW_MTU(p) (0x0458 + ((p) << 10))
-#define TX_BW_BURST(p) (0x045c + ((p) << 10))
-#define INT_CAUSE(p) (0x0460 + ((p) << 10))
+#define TXQ_COMMAND 0x0048
+#define TXQ_FIX_PRIO_CONF 0x004c
+#define TX_BW_RATE 0x0050
+#define TX_BW_MTU 0x0058
+#define TX_BW_BURST 0x005c
+#define INT_CAUSE 0x0060
#define INT_TX_END 0x07f80000
#define INT_RX 0x000003fc
#define INT_EXT 0x00000002
-#define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10))
+#define INT_CAUSE_EXT 0x0064
#define INT_EXT_LINK_PHY 0x00110000
#define INT_EXT_TX 0x000000ff
-#define INT_MASK(p) (0x0468 + ((p) << 10))
-#define INT_MASK_EXT(p) (0x046c + ((p) << 10))
-#define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10))
-#define TXQ_FIX_PRIO_CONF_MOVED(p) (0x04dc + ((p) << 10))
-#define TX_BW_RATE_MOVED(p) (0x04e0 + ((p) << 10))
-#define TX_BW_MTU_MOVED(p) (0x04e8 + ((p) << 10))
-#define TX_BW_BURST_MOVED(p) (0x04ec + ((p) << 10))
-#define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4))
-#define RXQ_COMMAND(p) (0x0680 + ((p) << 10))
-#define TXQ_CURRENT_DESC_PTR(p, q) (0x06c0 + ((p) << 10) + ((q) << 2))
-#define TXQ_BW_TOKENS(p, q) (0x0700 + ((p) << 10) + ((q) << 4))
-#define TXQ_BW_CONF(p, q) (0x0704 + ((p) << 10) + ((q) << 4))
-#define TXQ_BW_WRR_CONF(p, q) (0x0708 + ((p) << 10) + ((q) << 4))
+#define INT_MASK 0x0068
+#define INT_MASK_EXT 0x006c
+#define TX_FIFO_URGENT_THRESHOLD 0x0074
+#define TXQ_FIX_PRIO_CONF_MOVED 0x00dc
+#define TX_BW_RATE_MOVED 0x00e0
+#define TX_BW_MTU_MOVED 0x00e8
+#define TX_BW_BURST_MOVED 0x00ec
+#define RXQ_CURRENT_DESC_PTR(q) (0x020c + ((q) << 4))
+#define RXQ_COMMAND 0x0280
+#define TXQ_CURRENT_DESC_PTR(q) (0x02c0 + ((q) << 2))
+#define TXQ_BW_TOKENS(q) (0x0300 + ((q) << 4))
+#define TXQ_BW_CONF(q) (0x0304 + ((q) << 4))
+#define TXQ_BW_WRR_CONF(q) (0x0308 + ((q) << 4))
+
+/*
+ * Misc per-port registers.
+ */
#define MIB_COUNTERS(p) (0x1000 + ((p) << 7))
#define SPECIAL_MCAST_TABLE(p) (0x1400 + ((p) << 10))
#define OTHER_MCAST_TABLE(p) (0x1500 + ((p) << 10))
@@ -138,14 +143,14 @@ static char mv643xx_eth_driver_version[] = "1.4";
#if defined(__BIG_ENDIAN)
#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
- RX_BURST_SIZE_16_64BIT | \
- TX_BURST_SIZE_16_64BIT
+ (RX_BURST_SIZE_16_64BIT | \
+ TX_BURST_SIZE_16_64BIT)
#elif defined(__LITTLE_ENDIAN)
#define PORT_SDMA_CONFIG_DEFAULT_VALUE \
- RX_BURST_SIZE_16_64BIT | \
+ (RX_BURST_SIZE_16_64BIT | \
BLM_RX_NO_SWAP | \
BLM_TX_NO_SWAP | \
- TX_BURST_SIZE_16_64BIT
+ TX_BURST_SIZE_16_64BIT)
#else
#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined
#endif
@@ -351,6 +356,7 @@ struct tx_queue {
struct mv643xx_eth_private {
struct mv643xx_eth_shared_private *shared;
+ void __iomem *base;
int port_num;
struct net_device *dev;
@@ -401,11 +407,21 @@ static inline u32 rdl(struct mv643xx_eth_private *mp, int offset)
return readl(mp->shared->base + offset);
}
+static inline u32 rdlp(struct mv643xx_eth_private *mp, int offset)
+{
+ return readl(mp->base + offset);
+}
+
static inline void wrl(struct mv643xx_eth_private *mp, int offset, u32 data)
{
writel(data, mp->shared->base + offset);
}
+static inline void wrlp(struct mv643xx_eth_private *mp, int offset, u32 data)
+{
+ writel(data, mp->base + offset);
+}
+
/* rxq/txq helper functions *************************************************/
static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq)
@@ -421,7 +437,7 @@ static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq)
static void rxq_enable(struct rx_queue *rxq)
{
struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
- wrl(mp, RXQ_COMMAND(mp->port_num), 1 << rxq->index);
+ wrlp(mp, RXQ_COMMAND, 1 << rxq->index);
}
static void rxq_disable(struct rx_queue *rxq)
@@ -429,26 +445,25 @@ static void rxq_disable(struct rx_queue *rxq)
struct mv643xx_eth_private *mp = rxq_to_mp(rxq);
u8 mask = 1 << rxq->index;
- wrl(mp, RXQ_COMMAND(mp->port_num), mask << 8);
- while (rdl(mp, RXQ_COMMAND(mp->port_num)) & mask)
+ wrlp(mp, RXQ_COMMAND, mask << 8);
+ while (rdlp(mp, RXQ_COMMAND) & mask)
udelay(10);
}
static void txq_reset_hw_ptr(struct tx_queue *txq)
{
struct mv643xx_eth_private *mp = txq_to_mp(txq);
- int off = TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index);
u32 addr;
addr = (u32)txq->tx_desc_dma;
addr += txq->tx_curr_desc * sizeof(struct tx_desc);
- wrl(mp, off, addr);
+ wrlp(mp, TXQ_CURRENT_DESC_PTR(txq->index), addr);
}
static void txq_enable(struct tx_queue *txq)
{
struct mv643xx_eth_private *mp = txq_to_mp(txq);
- wrl(mp, TXQ_COMMAND(mp->port_num), 1 << txq->index);
+ wrlp(mp, TXQ_COMMAND, 1 << txq->index);
}
static void txq_disable(struct tx_queue *txq)
@@ -456,8 +471,8 @@ static void txq_disable(struct tx_queue *txq)
struct mv643xx_eth_private *mp = txq_to_mp(txq);
u8 mask = 1 << txq->index;
- wrl(mp, TXQ_COMMAND(mp->port_num), mask << 8);
- while (rdl(mp, TXQ_COMMAND(mp->port_num)) & mask)
+ wrlp(mp, TXQ_COMMAND, mask << 8);
+ while (rdlp(mp, TXQ_COMMAND) & mask)
udelay(10);
}
@@ -528,37 +543,38 @@ static int rxq_process(struct rx_queue *rxq, int budget)
* on, or the error summary bit is set, the packet needs
* to be dropped.
*/
- if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
- (RX_FIRST_DESC | RX_LAST_DESC))
- || (cmd_sts & ERROR_SUMMARY)) {
- stats->rx_dropped++;
-
- if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
- (RX_FIRST_DESC | RX_LAST_DESC)) {
- if (net_ratelimit())
- dev_printk(KERN_ERR, &mp->dev->dev,
- "received packet spanning "
- "multiple descriptors\n");
- }
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC | ERROR_SUMMARY))
+ != (RX_FIRST_DESC | RX_LAST_DESC))
+ goto err;
- if (cmd_sts & ERROR_SUMMARY)
- stats->rx_errors++;
+ /*
+ * The -4 is for the CRC in the trailer of the
+ * received packet
+ */
+ skb_put(skb, byte_cnt - 2 - 4);
- dev_kfree_skb(skb);
- } else {
- /*
- * The -4 is for the CRC in the trailer of the
- * received packet
- */
- skb_put(skb, byte_cnt - 2 - 4);
-
- if (cmd_sts & LAYER_4_CHECKSUM_OK)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- skb->protocol = eth_type_trans(skb, mp->dev);
- netif_receive_skb(skb);
+ if (cmd_sts & LAYER_4_CHECKSUM_OK)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->protocol = eth_type_trans(skb, mp->dev);
+ netif_receive_skb(skb);
+
+ continue;
+
+err:
+ stats->rx_dropped++;
+
+ if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
+ (RX_FIRST_DESC | RX_LAST_DESC)) {
+ if (net_ratelimit())
+ dev_printk(KERN_ERR, &mp->dev->dev,
+ "received packet spanning "
+ "multiple descriptors\n");
}
- mp->dev->last_rx = jiffies;
+ if (cmd_sts & ERROR_SUMMARY)
+ stats->rx_errors++;
+
+ dev_kfree_skb(skb);
}
if (rx < budget)
@@ -577,6 +593,7 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
struct sk_buff *skb;
int unaligned;
int rx;
+ struct rx_desc *rx_desc;
skb = __skb_dequeue(&mp->rx_recycle);
if (skb == NULL)
@@ -599,13 +616,14 @@ static int rxq_refill(struct rx_queue *rxq, int budget)
if (rxq->rx_used_desc == rxq->rx_ring_size)
rxq->rx_used_desc = 0;
- rxq->rx_desc_area[rx].buf_ptr = dma_map_single(NULL, skb->data,
- mp->skb_size, DMA_FROM_DEVICE);
- rxq->rx_desc_area[rx].buf_size = mp->skb_size;
+ rx_desc = rxq->rx_desc_area + rx;
+
+ rx_desc->buf_ptr = dma_map_single(NULL, skb->data,
+ mp->skb_size, DMA_FROM_DEVICE);
+ rx_desc->buf_size = mp->skb_size;
rxq->rx_skb[rx] = skb;
wmb();
- rxq->rx_desc_area[rx].cmd_sts = BUFFER_OWNED_BY_DMA |
- RX_ENABLE_INTERRUPT;
+ rx_desc->cmd_sts = BUFFER_OWNED_BY_DMA | RX_ENABLE_INTERRUPT;
wmb();
/*
@@ -638,21 +656,6 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
return 0;
}
-static int txq_alloc_desc_index(struct tx_queue *txq)
-{
- int tx_desc_curr;
-
- BUG_ON(txq->tx_desc_count >= txq->tx_ring_size);
-
- tx_desc_curr = txq->tx_curr_desc++;
- if (txq->tx_curr_desc == txq->tx_ring_size)
- txq->tx_curr_desc = 0;
-
- BUG_ON(txq->tx_curr_desc == txq->tx_used_desc);
-
- return tx_desc_curr;
-}
-
static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
{
int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -664,7 +667,9 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb)
struct tx_desc *desc;
this_frag = &skb_shinfo(skb)->frags[frag];
- tx_index = txq_alloc_desc_index(txq);
+ tx_index = txq->tx_curr_desc++;
+ if (txq->tx_curr_desc == txq->tx_ring_size)
+ txq->tx_curr_desc = 0;
desc = &txq->tx_desc_area[tx_index];
/*
@@ -746,7 +751,9 @@ no_csum:
cmd_sts |= 5 << TX_IHL_SHIFT;
}
- tx_index = txq_alloc_desc_index(txq);
+ tx_index = txq->tx_curr_desc++;
+ if (txq->tx_curr_desc == txq->tx_ring_size)
+ txq->tx_curr_desc = 0;
desc = &txq->tx_desc_area[tx_index];
if (nr_frags) {
@@ -831,10 +838,10 @@ static void txq_kick(struct tx_queue *txq)
__netif_tx_lock(nq, smp_processor_id());
- if (rdl(mp, TXQ_COMMAND(mp->port_num)) & (1 << txq->index))
+ if (rdlp(mp, TXQ_COMMAND) & (1 << txq->index))
goto out;
- hw_desc_ptr = rdl(mp, TXQ_CURRENT_DESC_PTR(mp->port_num, txq->index));
+ hw_desc_ptr = rdlp(mp, TXQ_CURRENT_DESC_PTR(txq->index));
expected_ptr = (u32)txq->tx_desc_dma +
txq->tx_curr_desc * sizeof(struct tx_desc);
@@ -941,14 +948,14 @@ static void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst)
switch (mp->shared->tx_bw_control) {
case TX_BW_CONTROL_OLD_LAYOUT:
- wrl(mp, TX_BW_RATE(mp->port_num), token_rate);
- wrl(mp, TX_BW_MTU(mp->port_num), mtu);
- wrl(mp, TX_BW_BURST(mp->port_num), bucket_size);
+ wrlp(mp, TX_BW_RATE, token_rate);
+ wrlp(mp, TX_BW_MTU, mtu);
+ wrlp(mp, TX_BW_BURST, bucket_size);
break;
case TX_BW_CONTROL_NEW_LAYOUT:
- wrl(mp, TX_BW_RATE_MOVED(mp->port_num), token_rate);
- wrl(mp, TX_BW_MTU_MOVED(mp->port_num), mtu);
- wrl(mp, TX_BW_BURST_MOVED(mp->port_num), bucket_size);
+ wrlp(mp, TX_BW_RATE_MOVED, token_rate);
+ wrlp(mp, TX_BW_MTU_MOVED, mtu);
+ wrlp(mp, TX_BW_BURST_MOVED, bucket_size);
break;
}
}
@@ -967,9 +974,8 @@ static void txq_set_rate(struct tx_queue *txq, int rate, int burst)
if (bucket_size > 65535)
bucket_size = 65535;
- wrl(mp, TXQ_BW_TOKENS(mp->port_num, txq->index), token_rate << 14);
- wrl(mp, TXQ_BW_CONF(mp->port_num, txq->index),
- (bucket_size << 10) | token_rate);
+ wrlp(mp, TXQ_BW_TOKENS(txq->index), token_rate << 14);
+ wrlp(mp, TXQ_BW_CONF(txq->index), (bucket_size << 10) | token_rate);
}
static void txq_set_fixed_prio_mode(struct tx_queue *txq)
@@ -984,17 +990,17 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq)
off = 0;
switch (mp->shared->tx_bw_control) {
case TX_BW_CONTROL_OLD_LAYOUT:
- off = TXQ_FIX_PRIO_CONF(mp->port_num);
+ off = TXQ_FIX_PRIO_CONF;
break;
case TX_BW_CONTROL_NEW_LAYOUT:
- off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
+ off = TXQ_FIX_PRIO_CONF_MOVED;
break;
}
if (off) {
- val = rdl(mp, off);
+ val = rdlp(mp, off);
val |= 1 << txq->index;
- wrl(mp, off, val);
+ wrlp(mp, off, val);
}
}
@@ -1010,26 +1016,25 @@ static void txq_set_wrr(struct tx_queue *txq, int weight)
off = 0;
switch (mp->shared->tx_bw_control) {
case TX_BW_CONTROL_OLD_LAYOUT:
- off = TXQ_FIX_PRIO_CONF(mp->port_num);
+ off = TXQ_FIX_PRIO_CONF;
break;
case TX_BW_CONTROL_NEW_LAYOUT:
- off = TXQ_FIX_PRIO_CONF_MOVED(mp->port_num);
+ off = TXQ_FIX_PRIO_CONF_MOVED;
break;
}
if (off) {
- val = rdl(mp, off);
+ val = rdlp(mp, off);
val &= ~(1 << txq->index);
- wrl(mp, off, val);
+ wrlp(mp, off, val);
/*
* Configure WRR weight for this queue.
*/
- off = TXQ_BW_WRR_CONF(mp->port_num, txq->index);
- val = rdl(mp, off);
+ val = rdlp(mp, off);
val = (val & ~0xff) | (weight & 0xff);
- wrl(mp, off, val);
+ wrlp(mp, TXQ_BW_WRR_CONF(txq->index), val);
}
}
@@ -1084,20 +1089,20 @@ static int smi_bus_read(struct mii_bus *bus, int addr, int reg)
int ret;
if (smi_wait_ready(msp)) {
- printk("mv643xx_eth: SMI bus busy timeout\n");
+ printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg);
if (smi_wait_ready(msp)) {
- printk("mv643xx_eth: SMI bus busy timeout\n");
+ printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
ret = readl(smi_reg);
if (!(ret & SMI_READ_VALID)) {
- printk("mv643xx_eth: SMI bus read not valid\n");
+ printk(KERN_WARNING "mv643xx_eth: SMI bus read not valid\n");
return -ENODEV;
}
@@ -1110,7 +1115,7 @@ static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val)
void __iomem *smi_reg = msp->base + SMI_REG;
if (smi_wait_ready(msp)) {
- printk("mv643xx_eth: SMI bus busy timeout\n");
+ printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
@@ -1118,7 +1123,7 @@ static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val)
(addr << 16) | (val & 0xffff), smi_reg);
if (smi_wait_ready(msp)) {
- printk("mv643xx_eth: SMI bus busy timeout\n");
+ printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n");
return -ETIMEDOUT;
}
@@ -1271,7 +1276,8 @@ static const struct mv643xx_eth_stats mv643xx_eth_stats[] = {
MIBSTAT(late_collision),
};
-static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+static int
+mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
int err;
@@ -1289,12 +1295,14 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd *
return err;
}
-static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd)
+static int
+mv643xx_eth_get_settings_phyless(struct net_device *dev,
+ struct ethtool_cmd *cmd)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
u32 port_status;
- port_status = rdl(mp, PORT_STATUS(mp->port_num));
+ port_status = rdlp(mp, PORT_STATUS);
cmd->supported = SUPPORTED_MII;
cmd->advertising = ADVERTISED_MII;
@@ -1323,7 +1331,8 @@ static int mv643xx_eth_get_settings_phyless(struct net_device *dev, struct ethto
return 0;
}
-static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+static int
+mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
@@ -1335,7 +1344,9 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd *
return phy_ethtool_sset(mp->phy, cmd);
}
-static int mv643xx_eth_set_settings_phyless(struct net_device *dev, struct ethtool_cmd *cmd)
+static int
+mv643xx_eth_set_settings_phyless(struct net_device *dev,
+ struct ethtool_cmd *cmd)
{
return -EINVAL;
}
@@ -1443,11 +1454,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = {
/* address handling *********************************************************/
static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr)
{
- unsigned int mac_h;
- unsigned int mac_l;
-
- mac_h = rdl(mp, MAC_ADDR_HIGH(mp->port_num));
- mac_l = rdl(mp, MAC_ADDR_LOW(mp->port_num));
+ unsigned int mac_h = rdlp(mp, MAC_ADDR_HIGH);
+ unsigned int mac_l = rdlp(mp, MAC_ADDR_LOW);
addr[0] = (mac_h >> 24) & 0xff;
addr[1] = (mac_h >> 16) & 0xff;
@@ -1457,57 +1465,71 @@ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr)
addr[5] = mac_l & 0xff;
}
-static void init_mac_tables(struct mv643xx_eth_private *mp)
+static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr)
{
- int i;
-
- for (i = 0; i < 0x100; i += 4) {
- wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0);
- wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0);
- }
-
- for (i = 0; i < 0x10; i += 4)
- wrl(mp, UNICAST_TABLE(mp->port_num) + i, 0);
+ wrlp(mp, MAC_ADDR_HIGH,
+ (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]);
+ wrlp(mp, MAC_ADDR_LOW, (addr[4] << 8) | addr[5]);
}
-static void set_filter_table_entry(struct mv643xx_eth_private *mp,
- int table, unsigned char entry)
+static u32 uc_addr_filter_mask(struct net_device *dev)
{
- unsigned int table_reg;
-
- /* Set "accepts frame bit" at specified table entry */
- table_reg = rdl(mp, table + (entry & 0xfc));
- table_reg |= 0x01 << (8 * (entry & 3));
- wrl(mp, table + (entry & 0xfc), table_reg);
-}
+ struct dev_addr_list *uc_ptr;
+ u32 nibbles;
-static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr)
-{
- unsigned int mac_h;
- unsigned int mac_l;
- int table;
+ if (dev->flags & IFF_PROMISC)
+ return 0;
- mac_l = (addr[4] << 8) | addr[5];
- mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
+ nibbles = 1 << (dev->dev_addr[5] & 0x0f);
+ for (uc_ptr = dev->uc_list; uc_ptr != NULL; uc_ptr = uc_ptr->next) {
+ if (memcmp(dev->dev_addr, uc_ptr->da_addr, 5))
+ return 0;
+ if ((dev->dev_addr[5] ^ uc_ptr->da_addr[5]) & 0xf0)
+ return 0;
- wrl(mp, MAC_ADDR_LOW(mp->port_num), mac_l);
- wrl(mp, MAC_ADDR_HIGH(mp->port_num), mac_h);
+ nibbles |= 1 << (uc_ptr->da_addr[5] & 0x0f);
+ }
- table = UNICAST_TABLE(mp->port_num);
- set_filter_table_entry(mp, table, addr[5] & 0x0f);
+ return nibbles;
}
-static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr)
+static void mv643xx_eth_program_unicast_filter(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
+ u32 port_config;
+ u32 nibbles;
+ int i;
- /* +2 is for the offset of the HW addr type */
- memcpy(dev->dev_addr, addr + 2, 6);
-
- init_mac_tables(mp);
uc_addr_set(mp, dev->dev_addr);
- return 0;
+ port_config = rdlp(mp, PORT_CONFIG);
+ nibbles = uc_addr_filter_mask(dev);
+ if (!nibbles) {
+ port_config |= UNICAST_PROMISCUOUS_MODE;
+ wrlp(mp, PORT_CONFIG, port_config);
+ return;
+ }
+
+ for (i = 0; i < 16; i += 4) {
+ int off = UNICAST_TABLE(mp->port_num) + i;
+ u32 v;
+
+ v = 0;
+ if (nibbles & 1)
+ v |= 0x00000001;
+ if (nibbles & 2)
+ v |= 0x00000100;
+ if (nibbles & 4)
+ v |= 0x00010000;
+ if (nibbles & 8)
+ v |= 0x01000000;
+ nibbles >>= 4;
+
+ wrl(mp, off, v);
+ }
+
+ port_config &= ~UNICAST_PROMISCUOUS_MODE;
+ wrlp(mp, PORT_CONFIG, port_config);
}
static int addr_crc(unsigned char *addr)
@@ -1528,24 +1550,22 @@ static int addr_crc(unsigned char *addr)
return crc;
}
-static void mv643xx_eth_set_rx_mode(struct net_device *dev)
+static void mv643xx_eth_program_multicast_filter(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
- u32 port_config;
+ u32 *mc_spec;
+ u32 *mc_other;
struct dev_addr_list *addr;
int i;
- port_config = rdl(mp, PORT_CONFIG(mp->port_num));
- if (dev->flags & IFF_PROMISC)
- port_config |= UNICAST_PROMISCUOUS_MODE;
- else
- port_config &= ~UNICAST_PROMISCUOUS_MODE;
- wrl(mp, PORT_CONFIG(mp->port_num), port_config);
-
if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
- int port_num = mp->port_num;
- u32 accept = 0x01010101;
+ int port_num;
+ u32 accept;
+ int i;
+oom:
+ port_num = mp->port_num;
+ accept = 0x01010101;
for (i = 0; i < 0x100; i += 4) {
wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept);
wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept);
@@ -1553,28 +1573,55 @@ static void mv643xx_eth_set_rx_mode(struct net_device *dev)
return;
}
- for (i = 0; i < 0x100; i += 4) {
- wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0);
- wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0);
- }
+ mc_spec = kmalloc(0x200, GFP_KERNEL);
+ if (mc_spec == NULL)
+ goto oom;
+ mc_other = mc_spec + (0x100 >> 2);
+
+ memset(mc_spec, 0, 0x100);
+ memset(mc_other, 0, 0x100);
for (addr = dev->mc_list; addr != NULL; addr = addr->next) {
u8 *a = addr->da_addr;
- int table;
-
- if (addr->da_addrlen != 6)
- continue;
+ u32 *table;
+ int entry;
if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) {
- table = SPECIAL_MCAST_TABLE(mp->port_num);
- set_filter_table_entry(mp, table, a[5]);
+ table = mc_spec;
+ entry = a[5];
} else {
- int crc = addr_crc(a);
-
- table = OTHER_MCAST_TABLE(mp->port_num);
- set_filter_table_entry(mp, table, crc);
+ table = mc_other;
+ entry = addr_crc(a);
}
+
+ table[entry >> 2] |= 1 << (entry & 3);
}
+
+ for (i = 0; i < 0x100; i += 4) {
+ wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, mc_spec[i >> 2]);
+ wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, mc_other[i >> 2]);
+ }
+
+ kfree(mc_spec);
+}
+
+static void mv643xx_eth_set_rx_mode(struct net_device *dev)
+{
+ mv643xx_eth_program_unicast_filter(dev);
+ mv643xx_eth_program_multicast_filter(dev);
+}
+
+static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+
+ netif_addr_lock_bh(dev);
+ mv643xx_eth_program_unicast_filter(dev);
+ netif_addr_unlock_bh(dev);
+
+ return 0;
}
@@ -1758,26 +1805,25 @@ static int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp)
u32 int_cause;
u32 int_cause_ext;
- int_cause = rdl(mp, INT_CAUSE(mp->port_num)) &
- (INT_TX_END | INT_RX | INT_EXT);
+ int_cause = rdlp(mp, INT_CAUSE) & (INT_TX_END | INT_RX | INT_EXT);
if (int_cause == 0)
return 0;
int_cause_ext = 0;
if (int_cause & INT_EXT)
- int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num));
+ int_cause_ext = rdlp(mp, INT_CAUSE_EXT);
int_cause &= INT_TX_END | INT_RX;
if (int_cause) {
- wrl(mp, INT_CAUSE(mp->port_num), ~int_cause);
+ wrlp(mp, INT_CAUSE, ~int_cause);
mp->work_tx_end |= ((int_cause & INT_TX_END) >> 19) &
- ~(rdl(mp, TXQ_COMMAND(mp->port_num)) & 0xff);
+ ~(rdlp(mp, TXQ_COMMAND) & 0xff);
mp->work_rx |= (int_cause & INT_RX) >> 2;
}
int_cause_ext &= INT_EXT_LINK_PHY | INT_EXT_TX;
if (int_cause_ext) {
- wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext);
+ wrlp(mp, INT_CAUSE_EXT, ~int_cause_ext);
if (int_cause_ext & INT_EXT_LINK_PHY)
mp->work_link = 1;
mp->work_tx |= int_cause_ext & INT_EXT_TX;
@@ -1794,7 +1840,7 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id)
if (unlikely(!mv643xx_eth_collect_events(mp)))
return IRQ_NONE;
- wrl(mp, INT_MASK(mp->port_num), 0);
+ wrlp(mp, INT_MASK, 0);
napi_schedule(&mp->napi);
return IRQ_HANDLED;
@@ -1808,7 +1854,7 @@ static void handle_link_event(struct mv643xx_eth_private *mp)
int duplex;
int fc;
- port_status = rdl(mp, PORT_STATUS(mp->port_num));
+ port_status = rdlp(mp, PORT_STATUS);
if (!(port_status & LINK_UP)) {
if (netif_carrier_ok(dev)) {
int i;
@@ -1908,7 +1954,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget)
if (mp->work_rx_oom)
mod_timer(&mp->rx_oom, jiffies + (HZ / 10));
napi_complete(napi);
- wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
+ wrlp(mp, INT_MASK, INT_TX_END | INT_RX | INT_EXT);
}
return work_done;
@@ -1957,17 +2003,17 @@ static void port_start(struct mv643xx_eth_private *mp)
/*
* Configure basic link parameters.
*/
- pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num));
+ pscr = rdlp(mp, PORT_SERIAL_CONTROL);
pscr |= SERIAL_PORT_ENABLE;
- wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
+ wrlp(mp, PORT_SERIAL_CONTROL, pscr);
pscr |= DO_NOT_FORCE_LINK_FAIL;
if (mp->phy == NULL)
pscr |= FORCE_LINK_PASS;
- wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
+ wrlp(mp, PORT_SERIAL_CONTROL, pscr);
- wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE);
+ wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE);
/*
* Configure TX path and queues.
@@ -1984,31 +2030,30 @@ static void port_start(struct mv643xx_eth_private *mp)
/*
* Add configured unicast address to address filter table.
*/
- uc_addr_set(mp, mp->dev->dev_addr);
+ mv643xx_eth_program_unicast_filter(mp->dev);
/*
* Receive all unmatched unicast, TCP, UDP, BPDU and broadcast
* frames to RX queue #0, and include the pseudo-header when
* calculating receive checksums.
*/
- wrl(mp, PORT_CONFIG(mp->port_num), 0x02000000);
+ wrlp(mp, PORT_CONFIG, 0x02000000);
/*
* Treat BPDUs as normal multicasts, and disable partition mode.
*/
- wrl(mp, PORT_CONFIG_EXT(mp->port_num), 0x00000000);
+ wrlp(mp, PORT_CONFIG_EXT, 0x00000000);
/*
* Enable the receive queues.
*/
for (i = 0; i < mp->rxq_count; i++) {
struct rx_queue *rxq = mp->rxq + i;
- int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i);
u32 addr;
addr = (u32)rxq->rx_desc_dma;
addr += rxq->rx_curr_desc * sizeof(struct rx_desc);
- wrl(mp, off, addr);
+ wrlp(mp, RXQ_CURRENT_DESC_PTR(i), addr);
rxq_enable(rxq);
}
@@ -2019,7 +2064,7 @@ static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64;
u32 val;
- val = rdl(mp, SDMA_CONFIG(mp->port_num));
+ val = rdlp(mp, SDMA_CONFIG);
if (mp->shared->extended_rx_coal_limit) {
if (coal > 0xffff)
coal = 0xffff;
@@ -2032,7 +2077,7 @@ static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
val &= ~0x003fff00;
val |= (coal & 0x3fff) << 8;
}
- wrl(mp, SDMA_CONFIG(mp->port_num), val);
+ wrlp(mp, SDMA_CONFIG, val);
}
static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
@@ -2041,7 +2086,7 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay)
if (coal > 0x3fff)
coal = 0x3fff;
- wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4);
+ wrlp(mp, TX_FIFO_URGENT_THRESHOLD, (coal & 0x3fff) << 4);
}
static void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp)
@@ -2070,9 +2115,9 @@ static int mv643xx_eth_open(struct net_device *dev)
int err;
int i;
- wrl(mp, INT_CAUSE(mp->port_num), 0);
- wrl(mp, INT_CAUSE_EXT(mp->port_num), 0);
- rdl(mp, INT_CAUSE_EXT(mp->port_num));
+ wrlp(mp, INT_CAUSE, 0);
+ wrlp(mp, INT_CAUSE_EXT, 0);
+ rdlp(mp, INT_CAUSE_EXT);
err = request_irq(dev->irq, mv643xx_eth_irq,
IRQF_SHARED, dev->name, dev);
@@ -2081,8 +2126,6 @@ static int mv643xx_eth_open(struct net_device *dev)
return -EAGAIN;
}
- init_mac_tables(mp);
-
mv643xx_eth_recalc_skb_size(mp);
napi_enable(&mp->napi);
@@ -2121,8 +2164,8 @@ static int mv643xx_eth_open(struct net_device *dev)
set_rx_coal(mp, 0);
set_tx_coal(mp, 0);
- wrl(mp, INT_MASK_EXT(mp->port_num), INT_EXT_LINK_PHY | INT_EXT_TX);
- wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
+ wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX);
+ wrlp(mp, INT_MASK, INT_TX_END | INT_RX | INT_EXT);
return 0;
@@ -2147,7 +2190,7 @@ static void port_reset(struct mv643xx_eth_private *mp)
txq_disable(mp->txq + i);
while (1) {
- u32 ps = rdl(mp, PORT_STATUS(mp->port_num));
+ u32 ps = rdlp(mp, PORT_STATUS);
if ((ps & (TX_IN_PROGRESS | TX_FIFO_EMPTY)) == TX_FIFO_EMPTY)
break;
@@ -2155,11 +2198,11 @@ static void port_reset(struct mv643xx_eth_private *mp)
}
/* Reset the Enable bit in the Configuration Register */
- data = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num));
+ data = rdlp(mp, PORT_SERIAL_CONTROL);
data &= ~(SERIAL_PORT_ENABLE |
DO_NOT_FORCE_LINK_FAIL |
FORCE_LINK_PASS);
- wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), data);
+ wrlp(mp, PORT_SERIAL_CONTROL, data);
}
static int mv643xx_eth_stop(struct net_device *dev)
@@ -2167,8 +2210,8 @@ static int mv643xx_eth_stop(struct net_device *dev)
struct mv643xx_eth_private *mp = netdev_priv(dev);
int i;
- wrl(mp, INT_MASK(mp->port_num), 0x00000000);
- rdl(mp, INT_MASK(mp->port_num));
+ wrlp(mp, INT_MASK, 0x00000000);
+ rdlp(mp, INT_MASK);
del_timer_sync(&mp->mib_counters_timer);
@@ -2261,12 +2304,12 @@ static void mv643xx_eth_netpoll(struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
- wrl(mp, INT_MASK(mp->port_num), 0x00000000);
- rdl(mp, INT_MASK(mp->port_num));
+ wrlp(mp, INT_MASK, 0x00000000);
+ rdlp(mp, INT_MASK);
mv643xx_eth_irq(dev->irq, dev);
- wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT);
+ wrlp(mp, INT_MASK, INT_TX_END | INT_RX | INT_EXT);
}
#endif
@@ -2314,8 +2357,8 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
* [21:8], or a 16-bit coal limit in bits [25,21:7] of the
* SDMA config register.
*/
- writel(0x02000000, msp->base + SDMA_CONFIG(0));
- if (readl(msp->base + SDMA_CONFIG(0)) & 0x02000000)
+ writel(0x02000000, msp->base + 0x0400 + SDMA_CONFIG);
+ if (readl(msp->base + 0x0400 + SDMA_CONFIG) & 0x02000000)
msp->extended_rx_coal_limit = 1;
else
msp->extended_rx_coal_limit = 0;
@@ -2325,12 +2368,12 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
* yes, whether its associated registers are in the old or
* the new place.
*/
- writel(1, msp->base + TX_BW_MTU_MOVED(0));
- if (readl(msp->base + TX_BW_MTU_MOVED(0)) & 1) {
+ writel(1, msp->base + 0x0400 + TX_BW_MTU_MOVED);
+ if (readl(msp->base + 0x0400 + TX_BW_MTU_MOVED) & 1) {
msp->tx_bw_control = TX_BW_CONTROL_NEW_LAYOUT;
} else {
- writel(7, msp->base + TX_BW_RATE(0));
- if (readl(msp->base + TX_BW_RATE(0)) & 7)
+ writel(7, msp->base + 0x0400 + TX_BW_RATE);
+ if (readl(msp->base + 0x0400 + TX_BW_RATE) & 7)
msp->tx_bw_control = TX_BW_CONTROL_OLD_LAYOUT;
else
msp->tx_bw_control = TX_BW_CONTROL_ABSENT;
@@ -2339,7 +2382,7 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
static int mv643xx_eth_shared_probe(struct platform_device *pdev)
{
- static int mv643xx_eth_version_printed = 0;
+ static int mv643xx_eth_version_printed;
struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
struct mv643xx_eth_shared_private *msp;
struct resource *res;
@@ -2563,10 +2606,10 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
{
u32 pscr;
- pscr = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num));
+ pscr = rdlp(mp, PORT_SERIAL_CONTROL);
if (pscr & SERIAL_PORT_ENABLE) {
pscr &= ~SERIAL_PORT_ENABLE;
- wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
+ wrlp(mp, PORT_SERIAL_CONTROL, pscr);
}
pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED;
@@ -2584,7 +2627,7 @@ static void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex)
pscr |= SET_FULL_DUPLEX_MODE;
}
- wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr);
+ wrlp(mp, PORT_SERIAL_CONTROL, pscr);
}
static int mv643xx_eth_probe(struct platform_device *pdev)
@@ -2593,7 +2636,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
struct mv643xx_eth_private *mp;
struct net_device *dev;
struct resource *res;
- DECLARE_MAC_BUF(mac);
int err;
pd = pdev->dev.platform_data;
@@ -2617,6 +2659,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mp);
mp->shared = platform_get_drvdata(pd->shared);
+ mp->base = mp->shared->base + 0x0400 + (pd->port_number << 10);
mp->port_num = pd->port_number;
mp->dev = dev;
@@ -2664,7 +2707,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
dev->hard_start_xmit = mv643xx_eth_xmit;
dev->open = mv643xx_eth_open;
dev->stop = mv643xx_eth_stop;
- dev->set_multicast_list = mv643xx_eth_set_rx_mode;
+ dev->set_rx_mode = mv643xx_eth_set_rx_mode;
dev->set_mac_address = mv643xx_eth_set_mac_address;
dev->do_ioctl = mv643xx_eth_ioctl;
dev->change_mtu = mv643xx_eth_change_mtu;
@@ -2687,8 +2730,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
if (err)
goto out;
- dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n",
- mp->port_num, print_mac(mac, dev->dev_addr));
+ dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %pM\n",
+ mp->port_num, dev->dev_addr);
if (mp->tx_desc_sram_size > 0)
dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n");
@@ -2721,8 +2764,8 @@ static void mv643xx_eth_shutdown(struct platform_device *pdev)
struct mv643xx_eth_private *mp = platform_get_drvdata(pdev);
/* Mask all interrupts on ethernet port */
- wrl(mp, INT_MASK(mp->port_num), 0);
- rdl(mp, INT_MASK(mp->port_num));
+ wrlp(mp, INT_MASK, 0);
+ rdlp(mp, INT_MASK);
if (netif_running(mp->dev))
port_reset(mp);
diff --git a/drivers/net/mvme147.c b/drivers/net/mvme147.c
index 06ca4252155f..435e5a847c43 100644
--- a/drivers/net/mvme147.c
+++ b/drivers/net/mvme147.c
@@ -67,7 +67,6 @@ struct net_device * __init mvme147lance_probe(int unit)
u_long *addr;
u_long address;
int err;
- DECLARE_MAC_BUF(mac);
if (!MACH_IS_MVME147 || called)
return ERR_PTR(-ENODEV);
@@ -102,11 +101,11 @@ struct net_device * __init mvme147lance_probe(int unit)
dev->dev_addr[3]=address&0xff;
printk("%s: MVME147 at 0x%08lx, irq %d, "
- "Hardware Address %s\n",
+ "Hardware Address %pM\n",
dev->name, dev->base_addr, MVME147_LANCE_IRQ,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
- lp = (struct m147lance_private *)dev->priv;
+ lp = netdev_priv(dev);
lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 16K */
if (!lp->ram)
{
@@ -190,7 +189,7 @@ int __init init_module(void)
void __exit cleanup_module(void)
{
- struct m147lance_private *lp = dev_mvme147_lance->priv;
+ struct m147lance_private *lp = netdev_priv(dev_mvme147_lance);
unregister_netdev(dev_mvme147_lance);
free_pages(lp->ram, 3);
free_netdev(dev_mvme147_lance);
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index b37867097308..5e70180bf569 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -75,7 +75,7 @@
#include "myri10ge_mcp.h"
#include "myri10ge_mcp_gen_header.h"
-#define MYRI10GE_VERSION_STR "1.4.3-1.378"
+#define MYRI10GE_VERSION_STR "1.4.4-1.395"
MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
MODULE_AUTHOR("Maintainer: help@myri.com");
@@ -1024,7 +1024,7 @@ static int myri10ge_reset(struct myri10ge_priv *mgp)
ss->dca_tag = NULL;
}
}
-#endif /* CONFIG_DCA */
+#endif /* CONFIG_MYRI10GE_DCA */
/* reset mcp/driver shared state back to 0 */
@@ -1121,7 +1121,7 @@ static int myri10ge_notify_dca_device(struct device *dev, void *data)
myri10ge_teardown_dca(mgp);
return 0;
}
-#endif /* CONFIG_DCA */
+#endif /* CONFIG_MYRI10GE_DCA */
static inline void
myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
@@ -1309,7 +1309,7 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, struct myri10ge_rx_buf *rx,
skb = netdev_alloc_skb(dev, MYRI10GE_HLEN + 16);
if (unlikely(skb == NULL)) {
- mgp->stats.rx_dropped++;
+ ss->stats.rx_dropped++;
do {
i--;
put_page(rx_frags[i].page);
@@ -1334,7 +1334,6 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, struct myri10ge_rx_buf *rx,
myri10ge_vlan_ip_csum(skb, csum);
}
netif_receive_skb(skb);
- dev->last_rx = jiffies;
return 1;
}
@@ -1504,7 +1503,6 @@ static int myri10ge_poll(struct napi_struct *napi, int budget)
{
struct myri10ge_slice_state *ss =
container_of(napi, struct myri10ge_slice_state, napi);
- struct net_device *netdev = ss->mgp->dev;
int work_done;
#ifdef CONFIG_MYRI10GE_DCA
@@ -1516,7 +1514,7 @@ static int myri10ge_poll(struct napi_struct *napi, int budget)
work_done = myri10ge_clean_rx_done(ss, budget);
if (work_done < budget) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
put_be32(htonl(3), ss->irq_claim);
}
return work_done;
@@ -1534,7 +1532,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg)
/* an interrupt on a non-zero receive-only slice is implicitly
* valid since MSI-X irqs are not shared */
if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) {
- netif_rx_schedule(ss->dev, &ss->napi);
+ netif_rx_schedule(&ss->napi);
return (IRQ_HANDLED);
}
@@ -1545,7 +1543,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg)
/* low bit indicates receives are present, so schedule
* napi poll handler */
if (stats->valid & 1)
- netif_rx_schedule(ss->dev, &ss->napi);
+ netif_rx_schedule(&ss->napi);
if (!mgp->msi_enabled && !mgp->msix_enabled) {
put_be32(0, mgp->irq_deassert);
@@ -2230,6 +2228,8 @@ myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr,
*ip_hdr = iph;
if (iph->protocol != IPPROTO_TCP)
return -1;
+ if (iph->frag_off & htons(IP_MF | IP_OFFSET))
+ return -1;
*hdr_flags |= LRO_TCP;
*tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2);
@@ -2927,6 +2927,7 @@ static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *segs, *curr;
struct myri10ge_priv *mgp = netdev_priv(dev);
+ struct myri10ge_slice_state *ss;
int status;
segs = skb_gso_segment(skb, dev->features & ~NETIF_F_TSO6);
@@ -2953,8 +2954,9 @@ static int myri10ge_sw_tso(struct sk_buff *skb, struct net_device *dev)
return 0;
drop:
+ ss = &mgp->ss[skb_get_queue_mapping(skb)];
dev_kfree_skb_any(skb);
- mgp->stats.tx_dropped += 1;
+ ss->stats.tx_dropped += 1;
return 0;
}
@@ -2985,7 +2987,6 @@ static void myri10ge_set_multicast_list(struct net_device *dev)
struct dev_mc_list *mc_list;
__be32 data[2] = { 0, 0 };
int err;
- DECLARE_MAC_BUF(mac);
/* can be called from atomic contexts,
* pass 1 to force atomicity in myri10ge_send_cmd() */
@@ -3032,8 +3033,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev)
printk(KERN_ERR "myri10ge: %s: Failed "
"MXGEFW_JOIN_MULTICAST_GROUP, error status:"
"%d\t", dev->name, err);
- printk(KERN_ERR "MAC %s\n",
- print_mac(mac, mc_list->dmi_addr));
+ printk(KERN_ERR "MAC %pM\n", mc_list->dmi_addr);
goto abort;
}
}
@@ -3732,6 +3732,17 @@ abort_with_fw:
myri10ge_load_firmware(mgp, 0);
}
+static const struct net_device_ops myri10ge_netdev_ops = {
+ .ndo_open = myri10ge_open,
+ .ndo_stop = myri10ge_close,
+ .ndo_start_xmit = myri10ge_xmit,
+ .ndo_get_stats = myri10ge_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = myri10ge_change_mtu,
+ .ndo_set_multicast_list = myri10ge_set_multicast_list,
+ .ndo_set_mac_address = myri10ge_set_mac_address,
+};
+
static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
@@ -3740,6 +3751,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
int i;
int status = -ENXIO;
int dac_enabled;
+ unsigned hdr_offset, ss_offset;
netdev = alloc_etherdev_mq(sizeof(*mgp), MYRI10GE_MAX_SLICES);
if (netdev == NULL) {
@@ -3807,14 +3819,6 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (mgp->mtrr >= 0)
mgp->wc_enabled = 1;
#endif
- /* Hack. need to get rid of these magic numbers */
- mgp->sram_size =
- 2 * 1024 * 1024 - (2 * (48 * 1024) + (32 * 1024)) - 0x100;
- if (mgp->sram_size > mgp->board_span) {
- dev_err(&pdev->dev, "board span %ld bytes too small\n",
- mgp->board_span);
- goto abort_with_mtrr;
- }
mgp->sram = ioremap_wc(mgp->iomem_base, mgp->board_span);
if (mgp->sram == NULL) {
dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n",
@@ -3822,9 +3826,19 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
status = -ENXIO;
goto abort_with_mtrr;
}
+ hdr_offset =
+ ntohl(__raw_readl(mgp->sram + MCP_HEADER_PTR_OFFSET)) & 0xffffc;
+ ss_offset = hdr_offset + offsetof(struct mcp_gen_header, string_specs);
+ mgp->sram_size = ntohl(__raw_readl(mgp->sram + ss_offset));
+ if (mgp->sram_size > mgp->board_span ||
+ mgp->sram_size <= MYRI10GE_FW_OFFSET) {
+ dev_err(&pdev->dev,
+ "invalid sram_size %dB or board span %ldB\n",
+ mgp->sram_size, mgp->board_span);
+ goto abort_with_ioremap;
+ }
memcpy_fromio(mgp->eeprom_strings,
- mgp->sram + mgp->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE,
- MYRI10GE_EEPROM_STRINGS_SIZE);
+ mgp->sram + mgp->sram_size, MYRI10GE_EEPROM_STRINGS_SIZE);
memset(mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2);
status = myri10ge_read_mac_addr(mgp);
if (status)
@@ -3860,15 +3874,10 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
if ((myri10ge_initial_mtu + ETH_HLEN) < 68)
myri10ge_initial_mtu = 68;
+
+ netdev->netdev_ops = &myri10ge_netdev_ops;
netdev->mtu = myri10ge_initial_mtu;
- netdev->open = myri10ge_open;
- netdev->stop = myri10ge_close;
- netdev->hard_start_xmit = myri10ge_xmit;
- netdev->get_stats = myri10ge_get_stats;
netdev->base_addr = mgp->iomem_base;
- netdev->change_mtu = myri10ge_change_mtu;
- netdev->set_multicast_list = myri10ge_set_multicast_list;
- netdev->set_mac_address = myri10ge_set_mac_address;
netdev->features = mgp->features;
if (dac_enabled)
@@ -4019,7 +4028,7 @@ static struct notifier_block myri10ge_dca_notifier = {
.next = NULL,
.priority = 0,
};
-#endif /* CONFIG_DCA */
+#endif /* CONFIG_MYRI10GE_DCA */
static __init int myri10ge_init_module(void)
{
diff --git a/drivers/net/myri10ge/myri10ge_mcp.h b/drivers/net/myri10ge/myri10ge_mcp.h
index 993721090777..11be150e4d67 100644
--- a/drivers/net/myri10ge/myri10ge_mcp.h
+++ b/drivers/net/myri10ge/myri10ge_mcp.h
@@ -111,61 +111,61 @@ enum myri10ge_mcp_cmd_type {
MXGEFW_CMD_NONE = 0,
/* Reset the mcp, it is left in a safe state, waiting
* for the driver to set all its parameters */
- MXGEFW_CMD_RESET,
+ MXGEFW_CMD_RESET = 1,
/* get the version number of the current firmware..
* (may be available in the eeprom strings..? */
- MXGEFW_GET_MCP_VERSION,
+ MXGEFW_GET_MCP_VERSION = 2,
/* Parameters which must be set by the driver before it can
* issue MXGEFW_CMD_ETHERNET_UP. They persist until the next
* MXGEFW_CMD_RESET is issued */
- MXGEFW_CMD_SET_INTRQ_DMA,
+ MXGEFW_CMD_SET_INTRQ_DMA = 3,
/* data0 = LSW of the host address
* data1 = MSW of the host address
* data2 = slice number if multiple slices are used
*/
- MXGEFW_CMD_SET_BIG_BUFFER_SIZE, /* in bytes, power of 2 */
- MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, /* in bytes */
+ MXGEFW_CMD_SET_BIG_BUFFER_SIZE = 4, /* in bytes, power of 2 */
+ MXGEFW_CMD_SET_SMALL_BUFFER_SIZE = 5, /* in bytes */
/* Parameters which refer to lanai SRAM addresses where the
* driver must issue PIO writes for various things */
- MXGEFW_CMD_GET_SEND_OFFSET,
- MXGEFW_CMD_GET_SMALL_RX_OFFSET,
- MXGEFW_CMD_GET_BIG_RX_OFFSET,
+ MXGEFW_CMD_GET_SEND_OFFSET = 6,
+ MXGEFW_CMD_GET_SMALL_RX_OFFSET = 7,
+ MXGEFW_CMD_GET_BIG_RX_OFFSET = 8,
/* data0 = slice number if multiple slices are used */
- MXGEFW_CMD_GET_IRQ_ACK_OFFSET,
- MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
+ MXGEFW_CMD_GET_IRQ_ACK_OFFSET = 9,
+ MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET = 10,
/* Parameters which refer to rings stored on the MCP,
* and whose size is controlled by the mcp */
- MXGEFW_CMD_GET_SEND_RING_SIZE, /* in bytes */
- MXGEFW_CMD_GET_RX_RING_SIZE, /* in bytes */
+ MXGEFW_CMD_GET_SEND_RING_SIZE = 11, /* in bytes */
+ MXGEFW_CMD_GET_RX_RING_SIZE = 12, /* in bytes */
/* Parameters which refer to rings stored in the host,
* and whose size is controlled by the host. Note that
* all must be physically contiguous and must contain
* a power of 2 number of entries. */
- MXGEFW_CMD_SET_INTRQ_SIZE, /* in bytes */
+ MXGEFW_CMD_SET_INTRQ_SIZE = 13, /* in bytes */
#define MXGEFW_CMD_SET_INTRQ_SIZE_FLAG_NO_STRICT_SIZE_CHECK (1 << 31)
/* command to bring ethernet interface up. Above parameters
* (plus mtu & mac address) must have been exchanged prior
* to issuing this command */
- MXGEFW_CMD_ETHERNET_UP,
+ MXGEFW_CMD_ETHERNET_UP = 14,
/* command to bring ethernet interface down. No further sends
* or receives may be processed until an MXGEFW_CMD_ETHERNET_UP
* is issued, and all interrupt queues must be flushed prior
* to ack'ing this command */
- MXGEFW_CMD_ETHERNET_DOWN,
+ MXGEFW_CMD_ETHERNET_DOWN = 15,
/* commands the driver may issue live, without resetting
* the nic. Note that increasing the mtu "live" should
@@ -173,40 +173,40 @@ enum myri10ge_mcp_cmd_type {
* sufficiently large to handle the new mtu. Decreasing
* the mtu live is safe */
- MXGEFW_CMD_SET_MTU,
- MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, /* in microseconds */
- MXGEFW_CMD_SET_STATS_INTERVAL, /* in microseconds */
- MXGEFW_CMD_SET_STATS_DMA_OBSOLETE, /* replaced by SET_STATS_DMA_V2 */
+ MXGEFW_CMD_SET_MTU = 16,
+ MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET = 17, /* in microseconds */
+ MXGEFW_CMD_SET_STATS_INTERVAL = 18, /* in microseconds */
+ MXGEFW_CMD_SET_STATS_DMA_OBSOLETE = 19, /* replaced by SET_STATS_DMA_V2 */
- MXGEFW_ENABLE_PROMISC,
- MXGEFW_DISABLE_PROMISC,
- MXGEFW_SET_MAC_ADDRESS,
+ MXGEFW_ENABLE_PROMISC = 20,
+ MXGEFW_DISABLE_PROMISC = 21,
+ MXGEFW_SET_MAC_ADDRESS = 22,
- MXGEFW_ENABLE_FLOW_CONTROL,
- MXGEFW_DISABLE_FLOW_CONTROL,
+ MXGEFW_ENABLE_FLOW_CONTROL = 23,
+ MXGEFW_DISABLE_FLOW_CONTROL = 24,
/* do a DMA test
* data0,data1 = DMA address
* data2 = RDMA length (MSH), WDMA length (LSH)
* command return data = repetitions (MSH), 0.5-ms ticks (LSH)
*/
- MXGEFW_DMA_TEST,
+ MXGEFW_DMA_TEST = 25,
- MXGEFW_ENABLE_ALLMULTI,
- MXGEFW_DISABLE_ALLMULTI,
+ MXGEFW_ENABLE_ALLMULTI = 26,
+ MXGEFW_DISABLE_ALLMULTI = 27,
/* returns MXGEFW_CMD_ERROR_MULTICAST
* if there is no room in the cache
* data0,MSH(data1) = multicast group address */
- MXGEFW_JOIN_MULTICAST_GROUP,
+ MXGEFW_JOIN_MULTICAST_GROUP = 28,
/* returns MXGEFW_CMD_ERROR_MULTICAST
* if the address is not in the cache,
* or is equal to FF-FF-FF-FF-FF-FF
* data0,MSH(data1) = multicast group address */
- MXGEFW_LEAVE_MULTICAST_GROUP,
- MXGEFW_LEAVE_ALL_MULTICAST_GROUPS,
+ MXGEFW_LEAVE_MULTICAST_GROUP = 29,
+ MXGEFW_LEAVE_ALL_MULTICAST_GROUPS = 30,
- MXGEFW_CMD_SET_STATS_DMA_V2,
+ MXGEFW_CMD_SET_STATS_DMA_V2 = 31,
/* data0, data1 = bus addr,
* data2 = sizeof(struct mcp_irq_data) from driver point of view, allows
* adding new stuff to mcp_irq_data without changing the ABI
@@ -216,14 +216,14 @@ enum myri10ge_mcp_cmd_type {
* (in the upper 16 bits).
*/
- MXGEFW_CMD_UNALIGNED_TEST,
+ MXGEFW_CMD_UNALIGNED_TEST = 32,
/* same than DMA_TEST (same args) but abort with UNALIGNED on unaligned
* chipset */
- MXGEFW_CMD_UNALIGNED_STATUS,
+ MXGEFW_CMD_UNALIGNED_STATUS = 33,
/* return data = boolean, true if the chipset is known to be unaligned */
- MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS,
+ MXGEFW_CMD_ALWAYS_USE_N_BIG_BUFFERS = 34,
/* data0 = number of big buffers to use. It must be 0 or a power of 2.
* 0 indicates that the NIC consumes as many buffers as they are required
* for packet. This is the default behavior.
@@ -233,8 +233,8 @@ enum myri10ge_mcp_cmd_type {
* the NIC to be able to receive maximum-sized packets.
*/
- MXGEFW_CMD_GET_MAX_RSS_QUEUES,
- MXGEFW_CMD_ENABLE_RSS_QUEUES,
+ MXGEFW_CMD_GET_MAX_RSS_QUEUES = 35,
+ MXGEFW_CMD_ENABLE_RSS_QUEUES = 36,
/* data0 = number of slices n (0, 1, ..., n-1) to enable
* data1 = interrupt mode | use of multiple transmit queues.
* 0=share one INTx/MSI.
@@ -249,18 +249,18 @@ enum myri10ge_mcp_cmd_type {
#define MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE 0x1
#define MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES 0x2
- MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET,
- MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA,
+ MXGEFW_CMD_GET_RSS_SHARED_INTERRUPT_MASK_OFFSET = 37,
+ MXGEFW_CMD_SET_RSS_SHARED_INTERRUPT_DMA = 38,
/* data0, data1 = bus address lsw, msw */
- MXGEFW_CMD_GET_RSS_TABLE_OFFSET,
+ MXGEFW_CMD_GET_RSS_TABLE_OFFSET = 39,
/* get the offset of the indirection table */
- MXGEFW_CMD_SET_RSS_TABLE_SIZE,
+ MXGEFW_CMD_SET_RSS_TABLE_SIZE = 40,
/* set the size of the indirection table */
- MXGEFW_CMD_GET_RSS_KEY_OFFSET,
+ MXGEFW_CMD_GET_RSS_KEY_OFFSET = 41,
/* get the offset of the secret key */
- MXGEFW_CMD_RSS_KEY_UPDATED,
+ MXGEFW_CMD_RSS_KEY_UPDATED = 42,
/* tell nic that the secret key's been updated */
- MXGEFW_CMD_SET_RSS_ENABLE,
+ MXGEFW_CMD_SET_RSS_ENABLE = 43,
/* data0 = enable/disable rss
* 0: disable rss. nic does not distribute receive packets.
* 1: enable rss. nic distributes receive packets among queues.
@@ -277,7 +277,7 @@ enum myri10ge_mcp_cmd_type {
#define MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT 0x5
#define MXGEFW_RSS_HASH_TYPE_MAX 0x5
- MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
+ MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE = 44,
/* Return data = the max. size of the entire headers of a IPv6 TSO packet.
* If the header size of a IPv6 TSO packet is larger than the specified
* value, then the driver must not use TSO.
@@ -286,7 +286,7 @@ enum myri10ge_mcp_cmd_type {
* always has enough header buffer to store maximum-sized headers.
*/
- MXGEFW_CMD_SET_TSO_MODE,
+ MXGEFW_CMD_SET_TSO_MODE = 45,
/* data0 = TSO mode.
* 0: Linux/FreeBSD style (NIC default)
* 1: NDIS/NetBSD style
@@ -294,33 +294,37 @@ enum myri10ge_mcp_cmd_type {
#define MXGEFW_TSO_MODE_LINUX 0
#define MXGEFW_TSO_MODE_NDIS 1
- MXGEFW_CMD_MDIO_READ,
+ MXGEFW_CMD_MDIO_READ = 46,
/* data0 = dev_addr (PMA/PMD or PCS ...), data1 = register/addr */
- MXGEFW_CMD_MDIO_WRITE,
+ MXGEFW_CMD_MDIO_WRITE = 47,
/* data0 = dev_addr, data1 = register/addr, data2 = value */
- MXGEFW_CMD_XFP_I2C_READ,
- /* Starts to get a fresh copy of one byte or of the whole xfp i2c table, the
+ MXGEFW_CMD_I2C_READ = 48,
+ /* Starts to get a fresh copy of one byte or of the module i2c table, the
* obtained data is cached inside the xaui-xfi chip :
- * data0 : "all" flag : 0 => get one byte, 1=> get 256 bytes,
- * data1 : if (data0 == 0): index of byte to refresh [ not used otherwise ]
+ * data0 : 0 => get one byte, 1=> get 256 bytes
+ * data1 : If data0 == 0: location to refresh
+ * bit 7:0 register location
+ * bit 8:15 is the i2c slave addr (0 is interpreted as 0xA1)
+ * bit 23:16 is the i2c bus number (for multi-port NICs)
+ * If data0 == 1: unused
* The operation might take ~1ms for a single byte or ~65ms when refreshing all 256 bytes
- * During the i2c operation, MXGEFW_CMD_XFP_I2C_READ or MXGEFW_CMD_XFP_BYTE attempts
+ * During the i2c operation, MXGEFW_CMD_I2C_READ or MXGEFW_CMD_I2C_BYTE attempts
* will return MXGEFW_CMD_ERROR_BUSY
*/
- MXGEFW_CMD_XFP_BYTE,
+ MXGEFW_CMD_I2C_BYTE = 49,
/* Return the last obtained copy of a given byte in the xfp i2c table
- * (copy cached during the last relevant MXGEFW_CMD_XFP_I2C_READ)
+ * (copy cached during the last relevant MXGEFW_CMD_I2C_READ)
* data0 : index of the desired table entry
* Return data = the byte stored at the requested index in the table
*/
- MXGEFW_CMD_GET_VPUMP_OFFSET,
+ MXGEFW_CMD_GET_VPUMP_OFFSET = 50,
/* Return data = NIC memory offset of mcp_vpump_public_global */
- MXGEFW_CMD_RESET_VPUMP,
+ MXGEFW_CMD_RESET_VPUMP = 51,
/* Resets the VPUMP state */
- MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE,
+ MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE = 52,
/* data0 = mcp_slot type to use.
* 0 = the default 4B mcp_slot
* 1 = 8B mcp_slot_8
@@ -328,7 +332,7 @@ enum myri10ge_mcp_cmd_type {
#define MXGEFW_RSS_MCP_SLOT_TYPE_MIN 0
#define MXGEFW_RSS_MCP_SLOT_TYPE_WITH_HASH 1
- MXGEFW_CMD_SET_THROTTLE_FACTOR,
+ MXGEFW_CMD_SET_THROTTLE_FACTOR = 53,
/* set the throttle factor for ethp_z8e
* data0 = throttle_factor
* throttle_factor = 256 * pcie-raw-speed / tx_speed
@@ -344,45 +348,50 @@ enum myri10ge_mcp_cmd_type {
* with tx_boundary == 4096, max-throttle-factor == 4095 => min-speed == 1Gb/s
*/
- MXGEFW_CMD_VPUMP_UP,
+ MXGEFW_CMD_VPUMP_UP = 54,
/* Allocates VPump Connection, Send Request and Zero copy buffer address tables */
- MXGEFW_CMD_GET_VPUMP_CLK,
+ MXGEFW_CMD_GET_VPUMP_CLK = 55,
/* Get the lanai clock */
- MXGEFW_CMD_GET_DCA_OFFSET,
+ MXGEFW_CMD_GET_DCA_OFFSET = 56,
/* offset of dca control for WDMAs */
/* VMWare NetQueue commands */
- MXGEFW_CMD_NETQ_GET_FILTERS_PER_QUEUE,
- MXGEFW_CMD_NETQ_ADD_FILTER,
+ MXGEFW_CMD_NETQ_GET_FILTERS_PER_QUEUE = 57,
+ MXGEFW_CMD_NETQ_ADD_FILTER = 58,
/* data0 = filter_id << 16 | queue << 8 | type */
/* data1 = MS4 of MAC Addr */
/* data2 = LS2_MAC << 16 | VLAN_tag */
- MXGEFW_CMD_NETQ_DEL_FILTER,
+ MXGEFW_CMD_NETQ_DEL_FILTER = 59,
/* data0 = filter_id */
- MXGEFW_CMD_NETQ_QUERY1,
- MXGEFW_CMD_NETQ_QUERY2,
- MXGEFW_CMD_NETQ_QUERY3,
- MXGEFW_CMD_NETQ_QUERY4,
-
+ MXGEFW_CMD_NETQ_QUERY1 = 60,
+ MXGEFW_CMD_NETQ_QUERY2 = 61,
+ MXGEFW_CMD_NETQ_QUERY3 = 62,
+ MXGEFW_CMD_NETQ_QUERY4 = 63,
+
+ MXGEFW_CMD_RELAX_RXBUFFER_ALIGNMENT = 64,
+ /* When set, small receive buffers can cross page boundaries.
+ * Both small and big receive buffers may start at any address.
+ * This option has performance implications, so use with caution.
+ */
};
enum myri10ge_mcp_cmd_status {
MXGEFW_CMD_OK = 0,
- MXGEFW_CMD_UNKNOWN,
- MXGEFW_CMD_ERROR_RANGE,
- MXGEFW_CMD_ERROR_BUSY,
- MXGEFW_CMD_ERROR_EMPTY,
- MXGEFW_CMD_ERROR_CLOSED,
- MXGEFW_CMD_ERROR_HASH_ERROR,
- MXGEFW_CMD_ERROR_BAD_PORT,
- MXGEFW_CMD_ERROR_RESOURCES,
- MXGEFW_CMD_ERROR_MULTICAST,
- MXGEFW_CMD_ERROR_UNALIGNED,
- MXGEFW_CMD_ERROR_NO_MDIO,
- MXGEFW_CMD_ERROR_XFP_FAILURE,
- MXGEFW_CMD_ERROR_XFP_ABSENT,
- MXGEFW_CMD_ERROR_BAD_PCIE_LINK
+ MXGEFW_CMD_UNKNOWN = 1,
+ MXGEFW_CMD_ERROR_RANGE = 2,
+ MXGEFW_CMD_ERROR_BUSY = 3,
+ MXGEFW_CMD_ERROR_EMPTY = 4,
+ MXGEFW_CMD_ERROR_CLOSED = 5,
+ MXGEFW_CMD_ERROR_HASH_ERROR = 6,
+ MXGEFW_CMD_ERROR_BAD_PORT = 7,
+ MXGEFW_CMD_ERROR_RESOURCES = 8,
+ MXGEFW_CMD_ERROR_MULTICAST = 9,
+ MXGEFW_CMD_ERROR_UNALIGNED = 10,
+ MXGEFW_CMD_ERROR_NO_MDIO = 11,
+ MXGEFW_CMD_ERROR_I2C_FAILURE = 12,
+ MXGEFW_CMD_ERROR_I2C_ABSENT = 13,
+ MXGEFW_CMD_ERROR_BAD_PCIE_LINK = 14
};
#define MXGEFW_OLD_IRQ_DATA_LEN 40
diff --git a/drivers/net/myri10ge/myri10ge_mcp_gen_header.h b/drivers/net/myri10ge/myri10ge_mcp_gen_header.h
index a8662ea8079a..caa6cbbb631e 100644
--- a/drivers/net/myri10ge/myri10ge_mcp_gen_header.h
+++ b/drivers/net/myri10ge/myri10ge_mcp_gen_header.h
@@ -41,6 +41,8 @@ struct mcp_gen_header {
unsigned short handoff_id_major; /* must be equal */
unsigned short handoff_id_caps; /* bitfield: new mcp must have superset */
unsigned msix_table_addr; /* start address of msix table in firmware */
+ unsigned bss_addr; /* start of bss */
+ unsigned features;
/* 8 */
};
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 3ad7589d6a1c..899ed065a147 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -318,13 +318,10 @@ static void myri_is_not_so_happy(struct myri_eth *mp)
#ifdef DEBUG_HEADER
static void dump_ehdr(struct ethhdr *ehdr)
{
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- printk("ehdr[h_dst(%s)"
- "h_source(%s)"
+ printk("ehdr[h_dst(%pM)"
+ "h_source(%pM)"
"h_proto(%04x)]\n",
- print_mac(mac, ehdr->h_dest), print_mac(mac2, ehdr->h_source),
- ehdr->h_proto);
+ ehdr->h_dest, ehdr->h_source, ehdr->h_proto);
}
static void dump_ehdr_and_myripad(unsigned char *stuff)
@@ -528,7 +525,6 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev)
DRX(("prot[%04x] netif_rx ", skb->protocol));
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
next:
@@ -540,7 +536,7 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev)
static irqreturn_t myri_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct myri_eth *mp = (struct myri_eth *) dev->priv;
+ struct myri_eth *mp = netdev_priv(dev);
void __iomem *lregs = mp->lregs;
struct myri_channel __iomem *chan = &mp->shmem->channel;
unsigned long flags;
@@ -579,14 +575,14 @@ static irqreturn_t myri_interrupt(int irq, void *dev_id)
static int myri_open(struct net_device *dev)
{
- struct myri_eth *mp = (struct myri_eth *) dev->priv;
+ struct myri_eth *mp = netdev_priv(dev);
return myri_init(mp, in_interrupt());
}
static int myri_close(struct net_device *dev)
{
- struct myri_eth *mp = (struct myri_eth *) dev->priv;
+ struct myri_eth *mp = netdev_priv(dev);
myri_clean_rings(mp);
return 0;
@@ -594,7 +590,7 @@ static int myri_close(struct net_device *dev)
static void myri_tx_timeout(struct net_device *dev)
{
- struct myri_eth *mp = (struct myri_eth *) dev->priv;
+ struct myri_eth *mp = netdev_priv(dev);
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
@@ -605,7 +601,7 @@ static void myri_tx_timeout(struct net_device *dev)
static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct myri_eth *mp = (struct myri_eth *) dev->priv;
+ struct myri_eth *mp = netdev_priv(dev);
struct sendq __iomem *sq = mp->sq;
struct myri_txd __iomem *txd;
unsigned long flags;
@@ -905,7 +901,6 @@ static int __devinit myri_sbus_probe(struct of_device *op, const struct of_devic
struct device_node *dp = op->node;
static unsigned version_printed;
struct net_device *dev;
- DECLARE_MAC_BUF(mac);
struct myri_eth *mp;
const void *prop;
static int num;
@@ -1088,15 +1083,15 @@ static int __devinit myri_sbus_probe(struct of_device *op, const struct of_devic
num++;
- printk("%s: MyriCOM MyriNET Ethernet %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: MyriCOM MyriNET Ethernet %pM\n",
+ dev->name, dev->dev_addr);
return 0;
err_free_irq:
free_irq(dev->irq, dev);
err:
- /* This will also free the co-allocated 'dev->priv' */
+ /* This will also free the co-allocated private data*/
free_netdev(dev);
return -ENODEV;
}
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index f7fa3944659b..478edb92bca3 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -792,7 +792,6 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
const int pcibar = 1; /* PCI base address register */
int prev_eedata;
u32 tmp;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -948,10 +947,10 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
if (netif_msg_drv(np)) {
printk(KERN_INFO "natsemi %s: %s at %#08llx "
- "(%s), %s, IRQ %d",
+ "(%s), %pM, IRQ %d",
dev->name, natsemi_pci_info[chip_idx].name,
(unsigned long long)iostart, pci_name(np->pci_dev),
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
if (dev->if_port == PORT_TP)
printk(", port TP.\n");
else if (np->ignore_phy)
@@ -2194,10 +2193,10 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
- if (netif_rx_schedule_prep(dev, &np->napi)) {
+ if (netif_rx_schedule_prep(&np->napi)) {
/* Disable interrupts and register for poll */
natsemi_irq_disable(dev);
- __netif_rx_schedule(dev, &np->napi);
+ __netif_rx_schedule(&np->napi);
} else
printk(KERN_WARNING
"%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
@@ -2249,7 +2248,7 @@ static int natsemi_poll(struct napi_struct *napi, int budget)
np->intr_status = readl(ioaddr + IntrStatus);
} while (np->intr_status);
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/* Reenable interrupts providing nothing is trying to shut
* the chip down. */
@@ -2362,7 +2361,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
}
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/ne-h8300.c b/drivers/net/ne-h8300.c
index fbc7531d3c7d..b57239171046 100644
--- a/drivers/net/ne-h8300.c
+++ b/drivers/net/ne-h8300.c
@@ -167,7 +167,7 @@ static void cleanup_card(struct net_device *dev)
#ifndef MODULE
struct net_device * __init ne_probe(int unit)
{
- struct net_device *dev = ____alloc_ei_netdev(0);
+ struct net_device *dev = alloc_ei_netdev();
int err;
if (!dev)
@@ -193,6 +193,21 @@ out:
}
#endif
+static const struct net_device_ops ne_netdev_ops = {
+ .ndo_open = ne_open,
+ .ndo_stop = ne_close,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init ne_probe1(struct net_device *dev, int ioaddr)
{
int i;
@@ -204,7 +219,6 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr)
static unsigned version_printed;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
unsigned char bus_width;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -299,7 +313,7 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr)
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = SA_prom[i];
- printk(" %s\n", print_mac(mac, dev->dev_addr));
+ printk(" %pM\n", dev->dev_addr);
printk("%s: %s found at %#x, using IRQ %d.\n",
dev->name, name, ioaddr, dev->irq);
@@ -320,11 +334,9 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr)
ei_status.block_output = &ne_block_output;
ei_status.get_8390_hdr = &ne_get_8390_hdr;
ei_status.priv = 0;
- dev->open = &ne_open;
- dev->stop = &ne_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = __ei_poll;
-#endif
+
+ dev->netdev_ops = &ne_netdev_ops;
+
__NS8390_init(dev, 0);
ret = register_netdev(dev);
@@ -625,7 +637,7 @@ int init_module(void)
int err;
for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
- struct net_device *dev = ____alloc_ei_netdev(0);
+ struct net_device *dev = alloc_ei_netdev();
if (!dev)
break;
if (io[this_dev]) {
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index eb681c0d51ba..5c3e242428f1 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -174,9 +174,6 @@ bad_clone_list[] __initdata = {
static int ne_probe1(struct net_device *dev, unsigned long ioaddr);
static int ne_probe_isapnp(struct net_device *dev);
-static int ne_open(struct net_device *dev);
-static int ne_close(struct net_device *dev);
-
static void ne_reset_8390(struct net_device *dev);
static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
@@ -297,7 +294,6 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
int neX000, ctron, copam, bad_card;
int reg0, ret;
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -517,7 +513,7 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
}
#endif
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
ei_status.name = name;
ei_status.tx_start_page = start_page;
@@ -537,11 +533,8 @@ static int __init ne_probe1(struct net_device *dev, unsigned long ioaddr)
ei_status.block_output = &ne_block_output;
ei_status.get_8390_hdr = &ne_get_8390_hdr;
ei_status.priv = 0;
- dev->open = &ne_open;
- dev->stop = &ne_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = eip_poll;
-#endif
+
+ dev->netdev_ops = &eip_netdev_ops;
NS8390p_init(dev, 0);
ret = register_netdev(dev);
@@ -558,20 +551,6 @@ err_out:
return ret;
}
-static int ne_open(struct net_device *dev)
-{
- eip_open(dev);
- return 0;
-}
-
-static int ne_close(struct net_device *dev)
-{
- if (ei_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
- eip_close(dev);
- return 0;
-}
-
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
@@ -950,7 +929,7 @@ static void __init ne_add_devices(void)
}
#ifdef MODULE
-int __init init_module()
+int __init init_module(void)
{
int retval;
ne_add_devices();
diff --git a/drivers/net/ne2.c b/drivers/net/ne2.c
index 332df75a9ab6..a53bb201d3c7 100644
--- a/drivers/net/ne2.c
+++ b/drivers/net/ne2.c
@@ -137,9 +137,6 @@ extern int netcard_probe(struct net_device *dev);
static int ne2_probe1(struct net_device *dev, int slot);
-static int ne_open(struct net_device *dev);
-static int ne_close(struct net_device *dev);
-
static void ne_reset_8390(struct net_device *dev);
static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
@@ -302,7 +299,6 @@ out:
static int ne2_procinfo(char *buf, int slot, struct net_device *dev)
{
int len=0;
- DECLARE_MAC_BUF(mac);
len += sprintf(buf+len, "The NE/2 Ethernet Adapter\n" );
len += sprintf(buf+len, "Driver written by Wim Dumon ");
@@ -313,7 +309,7 @@ static int ne2_procinfo(char *buf, int slot, struct net_device *dev)
len += sprintf(buf+len, "Based on the original NE2000 drivers\n" );
len += sprintf(buf+len, "Base IO: %#x\n", (unsigned int)dev->base_addr);
len += sprintf(buf+len, "IRQ : %d\n", dev->irq);
- len += sprintf(buf+len, "HW addr : %s\n", print_mac(mac, dev->dev_addr));
+ len += sprintf(buf+len, "HW addr : %pM\n", dev->dev_addr);
return len;
}
@@ -326,7 +322,6 @@ static int __init ne2_probe1(struct net_device *dev, int slot)
const char *name = "NE/2";
int start_page, stop_page;
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
if (ei_debug && version_printed++ == 0)
printk(version);
@@ -469,7 +464,7 @@ static int __init ne2_probe1(struct net_device *dev, int slot)
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = SA_prom[i];
- printk(" %s\n", print_mac(mac, dev->dev_addr));
+ printk(" %pM\n", dev->dev_addr);
printk("%s: %s found at %#x, using IRQ %d.\n",
dev->name, name, base_addr, dev->irq);
@@ -494,11 +489,7 @@ static int __init ne2_probe1(struct net_device *dev, int slot)
ei_status.priv = slot;
- dev->open = &ne_open;
- dev->stop = &ne_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = eip_poll;
-#endif
+ dev->netdev_ops = &eip_netdev_ops;
NS8390p_init(dev, 0);
retval = register_netdev(dev);
@@ -513,20 +504,6 @@ out:
return retval;
}
-static int ne_open(struct net_device *dev)
-{
- eip_open(dev);
- return 0;
-}
-
-static int ne_close(struct net_device *dev)
-{
- if (ei_debug > 1)
- printk("%s: Shutting down ethercard.\n", dev->name);
- eip_close(dev);
- return 0;
-}
-
/* Hard reset the card. This used to pause for the same period that a
8390 reset command required, but that shouldn't be necessary. */
static void ne_reset_8390(struct net_device *dev)
diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c
index de0de744a8fa..62f20ba211cb 100644
--- a/drivers/net/ne2k-pci.c
+++ b/drivers/net/ne2k-pci.c
@@ -200,6 +200,19 @@ struct ne2k_pci_card {
in the 'dev' and 'ei_status' structures.
*/
+static const struct net_device_ops ne2k_netdev_ops = {
+ .ndo_open = ne2k_pci_open,
+ .ndo_stop = ne2k_pci_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -212,7 +225,6 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
static unsigned int fnd_cnt;
long ioaddr;
int flags = pci_clone_list[chip_idx].flags;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -266,6 +278,8 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
dev_err(&pdev->dev, "cannot allocate ethernet device\n");
goto err_out_free_res;
}
+ dev->netdev_ops = &ne2k_netdev_ops;
+
SET_NETDEV_DEV(dev, &pdev->dev);
/* Reset card. Who knows what dain-bramaged state it was left in. */
@@ -354,12 +368,8 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
ei_status.block_output = &ne2k_pci_block_output;
ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
ei_status.priv = (unsigned long) pdev;
- dev->open = &ne2k_pci_open;
- dev->stop = &ne2k_pci_close;
+
dev->ethtool_ops = &ne2k_pci_ethtool_ops;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
NS8390_init(dev, 0);
i = register_netdev(dev);
@@ -368,9 +378,9 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev,
for(i = 0; i < 6; i++)
dev->dev_addr[i] = SA_prom[i];
- printk("%s: %s found at %#lx, IRQ %d, %s.\n",
+ printk("%s: %s found at %#lx, IRQ %d, %pM.\n",
dev->name, pci_clone_list[chip_idx].name, ioaddr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
@@ -626,7 +636,7 @@ static void ne2k_pci_block_output(struct net_device *dev, int count,
static void ne2k_pci_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
- struct ei_device *ei = dev->priv;
+ struct ei_device *ei = netdev_priv(dev);
struct pci_dev *pci_dev = (struct pci_dev *) ei->priv;
strcpy(info->driver, DRV_NAME);
diff --git a/drivers/net/ne3210.c b/drivers/net/ne3210.c
index 425043a88db9..fac43fd6fc87 100644
--- a/drivers/net/ne3210.c
+++ b/drivers/net/ne3210.c
@@ -45,9 +45,6 @@
#define DRV_NAME "ne3210"
-static int ne3210_open(struct net_device *dev);
-static int ne3210_close(struct net_device *dev);
-
static void ne3210_reset_8390(struct net_device *dev);
static void ne3210_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
@@ -99,7 +96,6 @@ static int __init ne3210_eisa_probe (struct device *device)
int i, retval, port_index;
struct eisa_device *edev = to_eisa_device (device);
struct net_device *dev;
- DECLARE_MAC_BUF(mac);
/* Allocate dev->priv and fill in 8390 specific dev fields. */
if (!(dev = alloc_ei_netdev ())) {
@@ -131,8 +127,8 @@ static int __init ne3210_eisa_probe (struct device *device)
port_index = inb(ioaddr + NE3210_CFG2) >> 6;
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = inb(ioaddr + NE3210_SA_PROM + i);
- printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr: %s.\n",
- edev->slot, ifmap[port_index], print_mac(mac, dev->dev_addr));
+ printk("ne3210.c: NE3210 in EISA slot %d, media: %s, addr: %pM.\n",
+ edev->slot, ifmap[port_index], dev->dev_addr);
/* Snarf the interrupt now. CFG file has them all listed as `edge' with share=NO */
dev->irq = irq_map[(inb(ioaddr + NE3210_CFG2) >> 3) & 0x07];
@@ -200,11 +196,8 @@ static int __init ne3210_eisa_probe (struct device *device)
ei_status.block_output = &ne3210_block_output;
ei_status.get_8390_hdr = &ne3210_get_8390_hdr;
- dev->open = &ne3210_open;
- dev->stop = &ne3210_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ei_netdev_ops;
+
dev->if_port = ifmap_val[port_index];
if ((retval = register_netdev (dev)))
@@ -321,22 +314,6 @@ static void ne3210_block_output(struct net_device *dev, int count,
memcpy_toio(shmem, buf, count);
}
-static int ne3210_open(struct net_device *dev)
-{
- ei_open(dev);
- return 0;
-}
-
-static int ne3210_close(struct net_device *dev)
-{
-
- if (ei_debug > 1)
- printk("%s: Shutting down ethercard.\n", dev->name);
-
- ei_close(dev);
- return 0;
-}
-
static struct eisa_device_id ne3210_ids[] = {
{ "EGL0101" },
{ "NVL1801" },
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 9681618c3232..d304d38cd5d1 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -307,17 +307,14 @@ static ssize_t show_remote_ip(struct netconsole_target *nt, char *buf)
static ssize_t show_local_mac(struct netconsole_target *nt, char *buf)
{
struct net_device *dev = nt->np.dev;
+ static const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- DECLARE_MAC_BUF(mac);
- return snprintf(buf, PAGE_SIZE, "%s\n", dev ?
- print_mac(mac, dev->dev_addr) : "ff:ff:ff:ff:ff:ff");
+ return snprintf(buf, PAGE_SIZE, "%pM\n", dev ? dev->dev_addr : bcast);
}
static ssize_t show_remote_mac(struct netconsole_target *nt, char *buf)
{
- DECLARE_MAC_BUF(mac);
- return snprintf(buf, PAGE_SIZE, "%s\n",
- print_mac(mac, nt->np.remote_mac));
+ return snprintf(buf, PAGE_SIZE, "%pM\n", nt->np.remote_mac);
}
/*
diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c
index b289a0a2b945..1861d5bbd96b 100644
--- a/drivers/net/netx-eth.c
+++ b/drivers/net/netx-eth.c
@@ -165,7 +165,6 @@ static void netx_eth_receive(struct net_device *ndev)
pfifo_push(EMPTY_PTR_FIFO(priv->id),
FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno));
- ndev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, ndev);
netif_rx(skb);
ndev->stats.rx_packets++;
diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c
index b974ca0fc530..e45ce2951729 100644
--- a/drivers/net/netxen/netxen_nic_ethtool.c
+++ b/drivers/net/netxen/netxen_nic_ethtool.c
@@ -275,11 +275,11 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
} else
return -EOPNOTSUPP;
- if (netif_running(dev)) {
- dev->stop(dev);
- dev->open(dev);
- }
- return 0;
+ if (!netif_running(dev))
+ return 0;
+
+ dev->netdev_ops->ndo_stop(dev);
+ return dev->netdev_ops->ndo_open(dev);
}
static int netxen_nic_get_regs_len(struct net_device *dev)
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 84978f80f396..aa6e603bfcbf 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -537,7 +537,7 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter,
static int nx_p3_sre_macaddr_change(struct net_device *dev,
u8 *addr, unsigned op)
{
- struct netxen_adapter *adapter = (struct netxen_adapter *)dev->priv;
+ struct netxen_adapter *adapter = netdev_priv(dev);
nx_nic_req_t req;
nx_mac_req_t mac_req;
int rv;
@@ -1459,7 +1459,7 @@ static int netxen_nic_pci_mem_read_direct(struct netxen_adapter *adapter,
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
- if (mem_ptr == 0UL) {
+ if (mem_ptr == NULL) {
*(uint8_t *)data = 0;
return -1;
}
@@ -1533,7 +1533,7 @@ netxen_nic_pci_mem_write_direct(struct netxen_adapter *adapter, u64 off,
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
- if (mem_ptr == 0UL)
+ if (mem_ptr == NULL)
return -1;
addr = mem_ptr;
addr += start & (PAGE_SIZE - 1);
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 5bba675d0504..d924468e506e 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -1285,9 +1285,7 @@ static void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid,
}
adapter->stats.rxdropped++;
} else {
-
netif_receive_skb(skb);
- netdev->last_rx = jiffies;
adapter->stats.no_rcv++;
adapter->stats.rxbytes += length;
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 6ef3f0d84bcf..ba01524b5531 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -439,7 +439,6 @@ netxen_read_mac_addr(struct netxen_adapter *adapter)
int i;
unsigned char *p;
__le64 mac_addr;
- DECLARE_MAC_BUF(mac);
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
@@ -462,15 +461,39 @@ netxen_read_mac_addr(struct netxen_adapter *adapter)
/* set station address */
- if (!is_valid_ether_addr(netdev->perm_addr)) {
- dev_warn(&pdev->dev, "Bad MAC address %s.\n",
- print_mac(mac, netdev->dev_addr));
- } else
+ if (!is_valid_ether_addr(netdev->perm_addr))
+ dev_warn(&pdev->dev, "Bad MAC address %pM.\n", netdev->dev_addr);
+ else
adapter->macaddr_set(adapter, netdev->dev_addr);
return 0;
}
+static void netxen_set_multicast_list(struct net_device *dev)
+{
+ struct netxen_adapter *adapter = netdev_priv(dev);
+
+ if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
+ netxen_p3_nic_set_multi(dev);
+ else
+ netxen_p2_nic_set_multi(dev);
+}
+
+static const struct net_device_ops netxen_netdev_ops = {
+ .ndo_open = netxen_nic_open,
+ .ndo_stop = netxen_nic_close,
+ .ndo_start_xmit = netxen_nic_xmit_frame,
+ .ndo_get_stats = netxen_nic_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = netxen_set_multicast_list,
+ .ndo_set_mac_address = netxen_nic_set_mac,
+ .ndo_change_mtu = netxen_nic_change_mtu,
+ .ndo_tx_timeout = netxen_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = netxen_nic_poll_controller,
+#endif
+};
+
/*
* netxen_nic_probe()
*
@@ -543,7 +566,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
SET_NETDEV_DEV(netdev, &pdev->dev);
- adapter = netdev->priv;
+ adapter = netdev_priv(netdev);
adapter->netdev = netdev;
adapter->pdev = pdev;
adapter->ahw.pci_func = pci_func_id;
@@ -682,25 +705,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
else
adapter->max_mc_count = 16;
- netdev->open = netxen_nic_open;
- netdev->stop = netxen_nic_close;
- netdev->hard_start_xmit = netxen_nic_xmit_frame;
- netdev->get_stats = netxen_nic_get_stats;
- if (NX_IS_REVISION_P3(revision_id))
- netdev->set_multicast_list = netxen_p3_nic_set_multi;
- else
- netdev->set_multicast_list = netxen_p2_nic_set_multi;
- netdev->set_mac_address = netxen_nic_set_mac;
- netdev->change_mtu = netxen_nic_change_mtu;
- netdev->tx_timeout = netxen_tx_timeout;
+ netdev->netdev_ops = &netxen_netdev_ops;
netdev->watchdog_timeo = 2*HZ;
netxen_nic_change_mtu(netdev, netdev->mtu);
SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
-#ifdef CONFIG_NET_POLL_CONTROLLER
- netdev->poll_controller = netxen_nic_poll_controller;
-#endif
+
/* ScatterGather support */
netdev->features = NETIF_F_SG;
netdev->features |= NETIF_F_IP_CSUM;
@@ -988,7 +999,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
*/
static int netxen_nic_open(struct net_device *netdev)
{
- struct netxen_adapter *adapter = (struct netxen_adapter *)netdev->priv;
+ struct netxen_adapter *adapter = netdev_priv(netdev);
int err = 0;
int ctx, ring;
irq_handler_t handler;
@@ -1077,7 +1088,7 @@ static int netxen_nic_open(struct net_device *netdev)
netxen_nic_set_link_parameters(adapter);
- netdev->set_multicast_list(netdev);
+ netxen_set_multicast_list(netdev);
if (adapter->set_mtu)
adapter->set_mtu(adapter, netdev->mtu);
@@ -1572,7 +1583,7 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget)
}
if ((work_done < budget) && tx_complete) {
- netif_rx_complete(adapter->netdev, &adapter->napi);
+ netif_rx_complete(&adapter->napi);
netxen_nic_enable_int(adapter);
}
diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c
index 27f07f6a45b1..c3b9c83b32fe 100644
--- a/drivers/net/netxen/netxen_nic_niu.c
+++ b/drivers/net/netxen/netxen_nic_niu.c
@@ -608,7 +608,6 @@ int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
int phy = adapter->physical_port;
unsigned char mac_addr[6];
int i;
- DECLARE_MAC_BUF(mac);
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
return 0;
@@ -636,10 +635,8 @@ int netxen_niu_macaddr_set(struct netxen_adapter *adapter,
if (i == 10) {
printk(KERN_ERR "%s: cannot set Mac addr for %s\n",
netxen_nic_driver_name, adapter->netdev->name);
- printk(KERN_ERR "MAC address set: %s.\n",
- print_mac(mac, addr));
- printk(KERN_ERR "MAC address get: %s.\n",
- print_mac(mac, mac_addr));
+ printk(KERN_ERR "MAC address set: %pM.\n", addr);
+ printk(KERN_ERR "MAC address get: %pM.\n", mac_addr);
}
return 0;
}
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
index 8e0ca9f4e404..539e18ab485c 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ni5010.c
@@ -203,7 +203,6 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr)
unsigned int data = 0;
int boguscount = 40;
int err = -ENODEV;
- DECLARE_MAC_BUF(mac);
dev->base_addr = ioaddr;
dev->irq = irq;
@@ -271,7 +270,7 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr)
outw(i, IE_GP);
dev->dev_addr[i] = inb(IE_SAPROM);
}
- printk("%s ", print_mac(mac, dev->dev_addr));
+ printk("%pM ", dev->dev_addr);
PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name));
@@ -329,7 +328,7 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr)
outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */
}
printk("-> bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE);
- memset(dev->priv, 0, sizeof(struct ni5010_local));
+ memset(netdev_priv(dev), 0, sizeof(struct ni5010_local));
dev->open = ni5010_open;
dev->stop = ni5010_close;
@@ -570,7 +569,6 @@ static void ni5010_rx(struct net_device *dev)
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += i_pkt_size;
@@ -768,12 +766,3 @@ module_init(ni5010_init_module);
module_exit(ni5010_cleanup_module);
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ni5010.c"
- * version-control: t
- * kept-new-versions: 5
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index b9a882d362da..a8bcc00c3302 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -9,8 +9,6 @@
* [feel free to mail ....]
*
* when using as module: (no autoprobing!)
- * compile with:
- * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ -DMODULE -c ni52.c
* run with e.g:
* insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000
*
@@ -214,7 +212,7 @@ struct priv {
/* wait for command with timeout: */
static void wait_for_scb_cmd(struct net_device *dev)
{
- struct priv *p = dev->priv;
+ struct priv *p = netdev_priv(dev);
int i;
for (i = 0; i < 16384; i++) {
if (readb(&p->scb->cmd_cuc) == 0)
@@ -233,7 +231,7 @@ static void wait_for_scb_cmd(struct net_device *dev)
static void wait_for_scb_cmd_ruc(struct net_device *dev)
{
- struct priv *p = dev->priv;
+ struct priv *p = netdev_priv(dev);
int i;
for (i = 0; i < 16384; i++) {
if (readb(&p->scb->cmd_ruc) == 0)
@@ -298,7 +296,7 @@ static int ni52_open(struct net_device *dev)
static int check_iscp(struct net_device *dev, void __iomem *addr)
{
struct iscp_struct __iomem *iscp = addr;
- struct priv *p = dev->priv;
+ struct priv *p = netdev_priv(dev);
memset_io(iscp, 0, sizeof(struct iscp_struct));
writel(make24(iscp), &p->scp->iscp);
@@ -318,7 +316,7 @@ static int check_iscp(struct net_device *dev, void __iomem *addr)
*/
static int check586(struct net_device *dev, unsigned size)
{
- struct priv *p = dev->priv;
+ struct priv *p = netdev_priv(dev);
int i;
p->mapped = ioremap(dev->mem_start, size);
@@ -354,7 +352,7 @@ Enodev:
*/
static void alloc586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
ni_reset586();
mdelay(32);
@@ -400,7 +398,7 @@ struct net_device * __init ni52_probe(int unit)
if (!dev)
return ERR_PTR(-ENOMEM);
- p = dev->priv;
+ p = netdev_priv(dev);
if (unit >= 0) {
sprintf(dev->name, "eth%d", unit);
@@ -446,7 +444,7 @@ out:
static int __init ni52_probe1(struct net_device *dev, int ioaddr)
{
int i, size, retval;
- struct priv *priv = dev->priv;
+ struct priv *priv = netdev_priv(dev);
dev->base_addr = ioaddr;
dev->irq = irq;
@@ -588,7 +586,7 @@ static int init586(struct net_device *dev)
{
void __iomem *ptr;
int i, result = 0;
- struct priv *p = (struct priv *)dev->priv;
+ struct priv *p = netdev_priv(dev);
struct configure_cmd_struct __iomem *cfg_cmd;
struct iasetup_cmd_struct __iomem *ias_cmd;
struct tdr_cmd_struct __iomem *tdr_cmd;
@@ -829,7 +827,7 @@ static void __iomem *alloc_rfa(struct net_device *dev, void __iomem *ptr)
struct rfd_struct __iomem *rfd = ptr;
struct rbd_struct __iomem *rbd;
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
memset_io(rfd, 0,
sizeof(struct rfd_struct) * (p->num_recv_buffs + rfdadd));
@@ -878,7 +876,7 @@ static irqreturn_t ni52_interrupt(int irq, void *dev_id)
int cnt = 0;
struct priv *p;
- p = (struct priv *) dev->priv;
+ p = netdev_priv(dev);
if (debuglevel > 1)
printk("I");
@@ -950,7 +948,7 @@ static void ni52_rcv_int(struct net_device *dev)
unsigned short totlen;
struct sk_buff *skb;
struct rbd_struct __iomem *rbd;
- struct priv *p = (struct priv *)dev->priv;
+ struct priv *p = netdev_priv(dev);
if (debuglevel > 0)
printk("R");
@@ -970,7 +968,6 @@ static void ni52_rcv_int(struct net_device *dev)
memcpy_fromio(skb->data, p->base + readl(&rbd->buffer), totlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
p->stats.rx_packets++;
p->stats.rx_bytes += totlen;
} else
@@ -1040,7 +1037,7 @@ static void ni52_rcv_int(struct net_device *dev)
static void ni52_rnr_int(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
p->stats.rx_errors++;
@@ -1065,7 +1062,7 @@ static void ni52_rnr_int(struct net_device *dev)
static void ni52_xmt_int(struct net_device *dev)
{
int status;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
if (debuglevel > 0)
printk("X");
@@ -1113,7 +1110,7 @@ static void ni52_xmt_int(struct net_device *dev)
static void startrecv586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
wait_for_scb_cmd(dev);
wait_for_scb_cmd_ruc(dev);
@@ -1126,7 +1123,7 @@ static void startrecv586(struct net_device *dev)
static void ni52_timeout(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
#ifndef NO_NOPCOMMANDS
if (readb(&p->scb->cus) & CU_ACTIVE) { /* COMMAND-UNIT active? */
netif_wake_queue(dev);
@@ -1177,7 +1174,7 @@ static int ni52_send_packet(struct sk_buff *skb, struct net_device *dev)
#ifndef NO_NOPCOMMANDS
int next_nop;
#endif
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
if (skb->len > XMIT_BUFF_SIZE) {
printk(KERN_ERR "%s: Sorry, max. framelength is %d bytes. The length of your frame is %d bytes.\n", dev->name, XMIT_BUFF_SIZE, skb->len);
@@ -1274,7 +1271,7 @@ static int ni52_send_packet(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *ni52_get_stats(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
unsigned short crc, aln, rsc, ovrn;
/* Get error-statistics from the ni82586 */
@@ -1337,7 +1334,7 @@ int __init init_module(void)
void __exit cleanup_module(void)
{
- struct priv *p = dev_ni52->priv;
+ struct priv *p = netdev_priv(dev_ni52);
unregister_netdev(dev_ni52);
iounmap(p->mapped);
release_region(dev_ni52->base_addr, NI52_TOTAL_SIZE);
@@ -1346,7 +1343,3 @@ void __exit cleanup_module(void)
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-/*
- * END: linux/drivers/net/ni52.c
- */
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index 3edc971d0eca..254057275e0e 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -7,8 +7,6 @@
* EtherBlaster. (probably it also works with every full NE2100
* compatible card)
*
- * To compile as module, type:
- * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ -DMODULE -c ni65.c
* driver probes: io: 0x360,0x300,0x320,0x340 / dma: 3,5,6,7
*
* This is an extension to the Linux operating system, and is covered by the
@@ -295,7 +293,7 @@ static void ni65_set_performance(struct priv *p)
*/
static int ni65_open(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
int irqval = request_irq(dev->irq, &ni65_interrupt,0,
cards[p->cardno].cardname,dev);
if (irqval) {
@@ -321,7 +319,7 @@ static int ni65_open(struct net_device *dev)
*/
static int ni65_close(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
netif_stop_queue(dev);
@@ -345,7 +343,7 @@ static int ni65_close(struct net_device *dev)
static void cleanup_card(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
disable_dma(dev->dma);
free_dma(dev->dma);
release_region(dev->base_addr, cards[p->cardno].total_size);
@@ -444,7 +442,7 @@ static int __init ni65_probe1(struct net_device *dev,int ioaddr)
release_region(ioaddr, cards[i].total_size);
return j;
}
- p = (struct priv *) dev->priv;
+ p = dev->ml_priv;
p->cmdr_addr = ioaddr + cards[i].cmd_offset;
p->cardno = i;
spin_lock_init(&p->ring_lock);
@@ -647,8 +645,8 @@ static int ni65_alloc_buffer(struct net_device *dev)
if(!ptr)
return -ENOMEM;
- p = dev->priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7);
- memset((char *) dev->priv,0,sizeof(struct priv));
+ p = dev->ml_priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7);
+ memset((char *)p, 0, sizeof(struct priv));
p->self = ptr;
for(i=0;i<TMDNUM;i++)
@@ -790,7 +788,7 @@ static void ni65_stop_start(struct net_device *dev,struct priv *p)
static int ni65_lance_reinit(struct net_device *dev)
{
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
unsigned long flags;
p->lock = 0;
@@ -876,7 +874,7 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
struct priv *p;
int bcnt = 32;
- p = (struct priv *) dev->priv;
+ p = dev->ml_priv;
spin_lock(&p->ring_lock);
@@ -899,7 +897,7 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
if(csr0 & CSR0_ERR)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
if(debuglevel > 1)
printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0);
if(csr0 & CSR0_BABL)
@@ -924,7 +922,7 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
int j;
for(j=0;j<RMDNUM;j++)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
int i,k,num1,num2;
for(i=RMDNUM-1;i>0;i--) {
num2 = (p->rmdnum + i) & (RMDNUM-1);
@@ -982,7 +980,7 @@ static irqreturn_t ni65_interrupt(int irq, void * dev_id)
*/
static void ni65_xmit_intr(struct net_device *dev,int csr0)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
while(p->xmit_queued)
{
@@ -1049,7 +1047,7 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
struct rmd *rmdp;
int rmdstat,len;
int cnt=0;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
rmdp = p->rmdhead + p->rmdnum;
while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
@@ -1113,7 +1111,6 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
p->stats.rx_bytes += len;
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
}
else
{
@@ -1140,7 +1137,7 @@ static void ni65_recv_intr(struct net_device *dev,int csr0)
static void ni65_timeout(struct net_device *dev)
{
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name);
for(i=0;i<TMDNUM;i++)
@@ -1157,7 +1154,7 @@ static void ni65_timeout(struct net_device *dev)
static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
netif_stop_queue(dev);
@@ -1222,7 +1219,7 @@ static struct net_device_stats *ni65_get_stats(struct net_device *dev)
#if 0
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = dev->ml_priv;
for(i=0;i<RMDNUM;i++)
{
struct rmd *rmdp = p->rmdhead + ((p->rmdnum + i) & (RMDNUM-1));
@@ -1231,7 +1228,7 @@ static struct net_device_stats *ni65_get_stats(struct net_device *dev)
printk("\n");
#endif
- return &((struct priv *) dev->priv)->stats;
+ return &((struct priv *)dev->ml_priv)->stats;
}
static void set_multicast_list(struct net_device *dev)
@@ -1266,7 +1263,3 @@ void __exit cleanup_module(void)
#endif /* MODULE */
MODULE_LICENSE("GPL");
-
-/*
- * END of ni65.c
- */
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 1b6f548c4411..0c0b752315ca 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -448,7 +448,7 @@ static int serdes_init_niu_1g_serdes(struct niu *np)
struct niu_link_config *lp = &np->link_config;
u16 pll_cfg, pll_sts;
int max_retry = 100;
- u64 sig, mask, val;
+ u64 uninitialized_var(sig), mask, val;
u32 tx_cfg, rx_cfg;
unsigned long i;
int err;
@@ -547,7 +547,7 @@ static int serdes_init_niu_10g_serdes(struct niu *np)
struct niu_link_config *lp = &np->link_config;
u32 tx_cfg, rx_cfg, pll_cfg, pll_sts;
int max_retry = 100;
- u64 sig, mask, val;
+ u64 uninitialized_var(sig), mask, val;
unsigned long i;
int err;
@@ -738,7 +738,7 @@ static int esr_write_glue0(struct niu *np, unsigned long chan, u32 val)
static int esr_reset(struct niu *np)
{
- u32 reset;
+ u32 uninitialized_var(reset);
int err;
err = mdio_write(np, np->port, NIU_ESR_DEV_ADDR,
@@ -3392,8 +3392,6 @@ static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp)
skb->protocol = eth_type_trans(skb, np->dev);
netif_receive_skb(skb);
- np->dev->last_rx = jiffies;
-
return num_rcr;
}
@@ -3529,6 +3527,57 @@ out:
}
}
+static inline void niu_sync_rx_discard_stats(struct niu *np,
+ struct rx_ring_info *rp,
+ const int limit)
+{
+ /* This elaborate scheme is needed for reading the RX discard
+ * counters, as they are only 16-bit and can overflow quickly,
+ * and because the overflow indication bit is not usable as
+ * the counter value does not wrap, but remains at max value
+ * 0xFFFF.
+ *
+ * In theory and in practice counters can be lost in between
+ * reading nr64() and clearing the counter nw64(). For this
+ * reason, the number of counter clearings nw64() is
+ * limited/reduced though the limit parameter.
+ */
+ int rx_channel = rp->rx_channel;
+ u32 misc, wred;
+
+ /* RXMISC (Receive Miscellaneous Discard Count), covers the
+ * following discard events: IPP (Input Port Process),
+ * FFLP/TCAM, Full RCR (Receive Completion Ring) RBR (Receive
+ * Block Ring) prefetch buffer is empty.
+ */
+ misc = nr64(RXMISC(rx_channel));
+ if (unlikely((misc & RXMISC_COUNT) > limit)) {
+ nw64(RXMISC(rx_channel), 0);
+ rp->rx_errors += misc & RXMISC_COUNT;
+
+ if (unlikely(misc & RXMISC_OFLOW))
+ dev_err(np->device, "rx-%d: Counter overflow "
+ "RXMISC discard\n", rx_channel);
+
+ niudbg(RX_ERR, "%s-rx-%d: MISC drop=%u over=%u\n",
+ np->dev->name, rx_channel, misc, misc-limit);
+ }
+
+ /* WRED (Weighted Random Early Discard) by hardware */
+ wred = nr64(RED_DIS_CNT(rx_channel));
+ if (unlikely((wred & RED_DIS_CNT_COUNT) > limit)) {
+ nw64(RED_DIS_CNT(rx_channel), 0);
+ rp->rx_dropped += wred & RED_DIS_CNT_COUNT;
+
+ if (unlikely(wred & RED_DIS_CNT_OFLOW))
+ dev_err(np->device, "rx-%d: Counter overflow "
+ "WRED discard\n", rx_channel);
+
+ niudbg(RX_ERR, "%s-rx-%d: WRED drop=%u over=%u\n",
+ np->dev->name, rx_channel, wred, wred-limit);
+ }
+}
+
static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
{
int qlen, rcr_done = 0, work_done = 0;
@@ -3569,6 +3618,10 @@ static int niu_rx_work(struct niu *np, struct rx_ring_info *rp, int budget)
nw64(RX_DMA_CTL_STAT(rp->rx_channel), stat);
+ /* Only sync discards stats when qlen indicate potential for drops */
+ if (qlen > 10)
+ niu_sync_rx_discard_stats(np, rp, 0x7FFF);
+
return work_done;
}
@@ -3616,7 +3669,7 @@ static int niu_poll(struct napi_struct *napi, int budget)
work_done = niu_poll_core(np, lp, budget);
if (work_done < budget) {
- netif_rx_complete(np->dev, napi);
+ netif_rx_complete(napi);
niu_ldg_rearm(np, lp, 1);
}
return work_done;
@@ -4035,12 +4088,12 @@ static void __niu_fastpath_interrupt(struct niu *np, int ldg, u64 v0)
static void niu_schedule_napi(struct niu *np, struct niu_ldg *lp,
u64 v0, u64 v1, u64 v2)
{
- if (likely(netif_rx_schedule_prep(np->dev, &lp->napi))) {
+ if (likely(netif_rx_schedule_prep(&lp->napi))) {
lp->v0 = v0;
lp->v1 = v1;
lp->v2 = v2;
__niu_fastpath_interrupt(np, lp->ldg_num, v0);
- __netif_rx_schedule(np->dev, &lp->napi);
+ __netif_rx_schedule(&lp->napi);
}
}
@@ -5849,17 +5902,42 @@ static void niu_stop_hw(struct niu *np)
niu_reset_rx_channels(np);
}
+static void niu_set_irq_name(struct niu *np)
+{
+ int port = np->port;
+ int i, j = 1;
+
+ sprintf(np->irq_name[0], "%s:MAC", np->dev->name);
+
+ if (port == 0) {
+ sprintf(np->irq_name[1], "%s:MIF", np->dev->name);
+ sprintf(np->irq_name[2], "%s:SYSERR", np->dev->name);
+ j = 3;
+ }
+
+ for (i = 0; i < np->num_ldg - j; i++) {
+ if (i < np->num_rx_rings)
+ sprintf(np->irq_name[i+j], "%s-rx-%d",
+ np->dev->name, i);
+ else if (i < np->num_tx_rings + np->num_rx_rings)
+ sprintf(np->irq_name[i+j], "%s-tx-%d", np->dev->name,
+ i - np->num_rx_rings);
+ }
+}
+
static int niu_request_irq(struct niu *np)
{
int i, j, err;
+ niu_set_irq_name(np);
+
err = 0;
for (i = 0; i < np->num_ldg; i++) {
struct niu_ldg *lp = &np->ldg[i];
err = request_irq(lp->irq, niu_interrupt,
IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- np->dev->name, lp);
+ np->irq_name[i], lp);
if (err)
goto out_free_irqs;
@@ -6050,15 +6128,17 @@ static void niu_get_rx_stats(struct niu *np)
for (i = 0; i < np->num_rx_rings; i++) {
struct rx_ring_info *rp = &np->rx_rings[i];
+ niu_sync_rx_discard_stats(np, rp, 0);
+
pkts += rp->rx_packets;
bytes += rp->rx_bytes;
dropped += rp->rx_dropped;
errors += rp->rx_errors;
}
- np->net_stats.rx_packets = pkts;
- np->net_stats.rx_bytes = bytes;
- np->net_stats.rx_dropped = dropped;
- np->net_stats.rx_errors = errors;
+ np->dev->stats.rx_packets = pkts;
+ np->dev->stats.rx_bytes = bytes;
+ np->dev->stats.rx_dropped = dropped;
+ np->dev->stats.rx_errors = errors;
}
static void niu_get_tx_stats(struct niu *np)
@@ -6074,9 +6154,9 @@ static void niu_get_tx_stats(struct niu *np)
bytes += rp->tx_bytes;
errors += rp->tx_errors;
}
- np->net_stats.tx_packets = pkts;
- np->net_stats.tx_bytes = bytes;
- np->net_stats.tx_errors = errors;
+ np->dev->stats.tx_packets = pkts;
+ np->dev->stats.tx_bytes = bytes;
+ np->dev->stats.tx_errors = errors;
}
static struct net_device_stats *niu_get_stats(struct net_device *dev)
@@ -6086,7 +6166,7 @@ static struct net_device_stats *niu_get_stats(struct net_device *dev)
niu_get_rx_stats(np);
niu_get_tx_stats(np);
- return &np->net_stats;
+ return &dev->stats;
}
static void niu_load_hash_xmac(struct niu *np, u16 *hash)
@@ -6991,6 +7071,8 @@ static void niu_get_ethtool_stats(struct net_device *dev,
for (i = 0; i < np->num_rx_rings; i++) {
struct rx_ring_info *rp = &np->rx_rings[i];
+ niu_sync_rx_discard_stats(np, rp, 0);
+
data[0] = rp->rx_channel;
data[1] = rp->rx_packets;
data[2] = rp->rx_bytes;
@@ -8824,7 +8906,7 @@ static u64 niu_pci_map_page(struct device *dev, struct page *page,
static void niu_pci_unmap_page(struct device *dev, u64 dma_address,
size_t size, enum dma_data_direction direction)
{
- return dma_unmap_page(dev, dma_address, size, direction);
+ dma_unmap_page(dev, dma_address, size, direction);
}
static u64 niu_pci_map_single(struct device *dev, void *cpu_addr,
@@ -8891,28 +8973,31 @@ static struct net_device * __devinit niu_alloc_and_init(
return dev;
}
+static const struct net_device_ops niu_netdev_ops = {
+ .ndo_open = niu_open,
+ .ndo_stop = niu_close,
+ .ndo_start_xmit = niu_start_xmit,
+ .ndo_get_stats = niu_get_stats,
+ .ndo_set_multicast_list = niu_set_rx_mode,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = niu_set_mac_addr,
+ .ndo_do_ioctl = niu_ioctl,
+ .ndo_tx_timeout = niu_tx_timeout,
+ .ndo_change_mtu = niu_change_mtu,
+};
+
static void __devinit niu_assign_netdev_ops(struct net_device *dev)
{
- dev->open = niu_open;
- dev->stop = niu_close;
- dev->get_stats = niu_get_stats;
- dev->set_multicast_list = niu_set_rx_mode;
- dev->set_mac_address = niu_set_mac_addr;
- dev->do_ioctl = niu_ioctl;
- dev->tx_timeout = niu_tx_timeout;
- dev->hard_start_xmit = niu_start_xmit;
+ dev->netdev_ops = &niu_netdev_ops;
dev->ethtool_ops = &niu_ethtool_ops;
dev->watchdog_timeo = NIU_TX_TIMEOUT;
- dev->change_mtu = niu_change_mtu;
}
static void __devinit niu_device_announce(struct niu *np)
{
struct net_device *dev = np->dev;
- DECLARE_MAC_BUF(mac);
- pr_info("%s: NIU Ethernet %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ pr_info("%s: NIU Ethernet %pM\n", dev->name, dev->dev_addr);
if (np->parent->plat_type == PLAT_TYPE_ATCA_CP3220) {
pr_info("%s: Port type[%s] mode[%s:%s] XCVR[%s] phy[%s]\n",
diff --git a/drivers/net/niu.h b/drivers/net/niu.h
index 180ca8ae93de..e1a7392e8d70 100644
--- a/drivers/net/niu.h
+++ b/drivers/net/niu.h
@@ -3243,12 +3243,12 @@ struct niu {
#define NIU_FLAGS_XMAC 0x00010000 /* 0=BMAC 1=XMAC */
u32 msg_enable;
+ char irq_name[NIU_NUM_RXCHAN+NIU_NUM_TXCHAN+3][IFNAMSIZ + 6];
/* Protects hw programming, and ring state. */
spinlock_t lock;
const struct niu_ops *ops;
- struct net_device_stats net_stats;
union niu_mac_stats mac_stats;
struct rx_ring_info *rx_rings;
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index ff449619f047..46b0772489e4 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -1948,14 +1948,25 @@ static void ns83820_probe_phy(struct net_device *ndev)
}
#endif
-static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_device_id *id)
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = ns83820_open,
+ .ndo_stop = ns83820_stop,
+ .ndo_start_xmit = ns83820_hard_start_xmit,
+ .ndo_get_stats = ns83820_get_stats,
+ .ndo_change_mtu = ns83820_change_mtu,
+ .ndo_set_multicast_list = ns83820_set_multicast,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = ns83820_tx_timeout,
+};
+
+static int __devinit ns83820_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *id)
{
struct net_device *ndev;
struct ns83820 *dev;
long addr;
int err;
int using_dac = 0;
- DECLARE_MAC_BUF(mac);
/* See if we can set the dma mask early on; failure is fatal. */
if (sizeof(dma_addr_t) == 8 &&
@@ -2041,14 +2052,8 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_
ndev->name, le32_to_cpu(readl(dev->base + 0x22c)),
pci_dev->subsystem_vendor, pci_dev->subsystem_device);
- ndev->open = ns83820_open;
- ndev->stop = ns83820_stop;
- ndev->hard_start_xmit = ns83820_hard_start_xmit;
- ndev->get_stats = ns83820_get_stats;
- ndev->change_mtu = ns83820_change_mtu;
- ndev->set_multicast_list = ns83820_set_multicast;
+ ndev->netdev_ops = &netdev_ops;
SET_ETHTOOL_OPS(ndev, &ops);
- ndev->tx_timeout = ns83820_tx_timeout;
ndev->watchdog_timeo = 5 * HZ;
pci_set_drvdata(pci_dev, ndev);
@@ -2220,12 +2225,11 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_
ndev->features |= NETIF_F_HIGHDMA;
}
- printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %s io=0x%08lx irq=%d f=%s\n",
+ printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %pM io=0x%08lx irq=%d f=%s\n",
ndev->name,
(unsigned)readl(dev->base + SRR) >> 8,
(unsigned)readl(dev->base + SRR) & 0xff,
- print_mac(mac, ndev->dev_addr),
- addr, pci_dev->irq,
+ ndev->dev_addr, addr, pci_dev->irq,
(ndev->features & NETIF_F_HIGHDMA) ? "h,sg" : "sg"
);
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c
index edc0fd588985..5b7a574ce571 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/pasemi_mac.c
@@ -954,7 +954,6 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
{
const struct pasemi_mac_rxring *rxring = data;
struct pasemi_mac *mac = rxring->mac;
- struct net_device *dev = mac->netdev;
const struct pasemi_dmachan *chan = &rxring->chan;
unsigned int reg;
@@ -971,7 +970,7 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
if (*chan->status & PAS_STATUS_ERROR)
reg |= PAS_IOB_DMA_RXCH_RESET_DINTC;
- netif_rx_schedule(dev, &mac->napi);
+ netif_rx_schedule(&mac->napi);
write_iob_reg(PAS_IOB_DMA_RXCH_RESET(chan->chno), reg);
@@ -1011,7 +1010,7 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data)
mod_timer(&txring->clean_timer, jiffies + (TX_CLEAN_INTERVAL)*2);
- netif_rx_schedule(mac->netdev, &mac->napi);
+ netif_rx_schedule(&mac->napi);
if (reg)
write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg);
@@ -1105,7 +1104,8 @@ static int pasemi_mac_phy_init(struct net_device *dev)
goto err;
phy_id = *prop;
- snprintf(mac->phy_id, BUS_ID_SIZE, "%x:%02x", (int)r.start, phy_id);
+ snprintf(mac->phy_id, sizeof(mac->phy_id), "%x:%02x",
+ (int)r.start, phy_id);
of_node_put(phy_dn);
@@ -1633,14 +1633,13 @@ static void pasemi_mac_set_rx_mode(struct net_device *dev)
static int pasemi_mac_poll(struct napi_struct *napi, int budget)
{
struct pasemi_mac *mac = container_of(napi, struct pasemi_mac, napi);
- struct net_device *dev = mac->netdev;
int pkts;
pasemi_mac_clean_tx(tx_ring(mac));
pkts = pasemi_mac_clean_rx(rx_ring(mac), budget);
if (pkts < budget) {
/* all done, no more packets present */
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
pasemi_mac_restart_rx_intr(mac);
pasemi_mac_restart_tx_intr(mac);
@@ -1742,7 +1741,6 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct net_device *dev;
struct pasemi_mac *mac;
int err;
- DECLARE_MAC_BUF(mac_buf);
err = pci_enable_device(pdev);
if (err)
@@ -1849,9 +1847,9 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err);
goto out;
} else if netif_msg_probe(mac)
- printk(KERN_INFO "%s: PA Semi %s: intf %d, hw addr %s\n",
+ printk(KERN_INFO "%s: PA Semi %s: intf %d, hw addr %pM\n",
dev->name, mac->type == MAC_TYPE_GMAC ? "GMAC" : "XAUI",
- mac->dma_if, print_mac(mac_buf, dev->dev_addr));
+ mac->dma_if, dev->dev_addr);
return err;
diff --git a/drivers/net/pasemi_mac_ethtool.c b/drivers/net/pasemi_mac_ethtool.c
index 5e8df3afea64..064a4fe1dd90 100644
--- a/drivers/net/pasemi_mac_ethtool.c
+++ b/drivers/net/pasemi_mac_ethtool.c
@@ -109,7 +109,7 @@ static void
pasemi_mac_ethtool_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ering)
{
- struct pasemi_mac *mac = netdev->priv;
+ struct pasemi_mac *mac = netdev_priv(netdev);
ering->tx_max_pending = TX_RING_SIZE/2;
ering->tx_pending = RING_USED(mac->tx)/2;
@@ -130,7 +130,7 @@ static int pasemi_mac_get_sset_count(struct net_device *netdev, int sset)
static void pasemi_mac_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
- struct pasemi_mac *mac = netdev->priv;
+ struct pasemi_mac *mac = netdev_priv(netdev);
int i;
data[0] = pasemi_read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if))
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index 0a575fef29e6..c95fd72c3bb9 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -737,7 +737,6 @@ static int __devinit netdrv_init_one (struct pci_dev *pdev,
int i, addr_len, option;
void *ioaddr = NULL;
static int board_idx = -1;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -782,7 +781,7 @@ static int __devinit netdrv_init_one (struct pci_dev *pdev,
dev->irq = pdev->irq;
dev->base_addr = (unsigned long) ioaddr;
- /* dev->priv/tp zeroed and aligned in alloc_etherdev */
+ /* netdev_priv()/tp zeroed and aligned in alloc_etherdev */
tp = netdev_priv(dev);
/* note: tp->chipset set in netdrv_init_board */
@@ -797,11 +796,11 @@ static int __devinit netdrv_init_one (struct pci_dev *pdev,
tp->phys[0] = 32;
- printk (KERN_INFO "%s: %s at 0x%lx, %sIRQ %d\n",
+ printk (KERN_INFO "%s: %s at 0x%lx, %pM IRQ %d\n",
dev->name,
board_info[ent->driver_data].name,
dev->base_addr,
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
printk (KERN_DEBUG "%s: Identified 8139 chip type '%s'\n",
@@ -1566,7 +1565,6 @@ static void netdrv_rx_interrupt (struct net_device *dev,
skb->protocol = eth_type_trans (skb, dev);
netif_rx (skb);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
} else {
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 08c4dd896077..e5cb6b1f0ebd 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -345,7 +345,6 @@ static int tc574_config(struct pcmcia_device *link)
__be16 *phys_addr;
char *cardname;
__u32 config;
- DECLARE_MAC_BUF(mac);
phys_addr = (__be16 *)dev->dev_addr;
@@ -463,9 +462,9 @@ static int tc574_config(struct pcmcia_device *link)
strcpy(lp->node.dev_name, dev->name);
printk(KERN_INFO "%s: %s at io %#3lx, irq %d, "
- "hw_addr %s.\n",
+ "hw_addr %pM.\n",
dev->name, cardname, dev->base_addr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n",
8 << config & Ram_size,
ram_split[(config & Ram_split) >> Ram_split_shift],
@@ -1062,7 +1061,6 @@ static int el3_rx(struct net_device *dev, int worklimit)
((pkt_len+3)>>2));
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index c235cdba69c6..73ecc657999d 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -255,7 +255,6 @@ static int tc589_config(struct pcmcia_device *link)
int last_fn, last_ret, i, j, multi = 0, fifo;
unsigned int ioaddr;
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
- DECLARE_MAC_BUF(mac);
DEBUG(0, "3c589_config(0x%p)\n", link);
@@ -333,9 +332,9 @@ static int tc589_config(struct pcmcia_device *link)
strcpy(lp->node.dev_name, dev->name);
printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, "
- "hw_addr %s\n",
+ "hw_addr %pM\n",
dev->name, (multi ? "562" : "589"), dev->base_addr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
printk(KERN_INFO " %dK FIFO split %s Rx:Tx, %s xcvr\n",
(fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
if_names[dev->if_port]);
@@ -884,7 +883,6 @@ static int el3_rx(struct net_device *dev)
(pkt_len+3)>>2);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 0418045166c3..0afa72095810 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -321,7 +321,6 @@ static int axnet_config(struct pcmcia_device *link)
struct net_device *dev = link->priv;
axnet_dev_t *info = PRIV(dev);
int i, j, last_ret, last_fn;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "axnet_config(0x%p)\n", link);
@@ -397,10 +396,10 @@ static int axnet_config(struct pcmcia_device *link)
strcpy(info->node.dev_name, dev->name);
printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, "
- "hw_addr %s\n",
+ "hw_addr %pM\n",
dev->name, ((info->flags & IS_AX88790) ? 7 : 1),
dev->base_addr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
if (info->phy_id != -1) {
DEBUG(0, " MII transceiver at index %d, status %x.\n", info->phy_id, j);
} else {
@@ -906,7 +905,7 @@ int ei_debug = 1;
/* Index to functions. */
static void ei_tx_intr(struct net_device *dev);
static void ei_tx_err(struct net_device *dev);
-static void ei_tx_timeout(struct net_device *dev);
+static void axnet_tx_timeout(struct net_device *dev);
static void ei_receive(struct net_device *dev);
static void ei_rx_overrun(struct net_device *dev);
@@ -957,9 +956,9 @@ static int ax_open(struct net_device *dev)
#ifdef HAVE_TX_TIMEOUT
/* The card I/O part of the driver (e.g. 3c503) can hook a Tx timeout
- wrapper that does e.g. media check & then calls ei_tx_timeout. */
+ wrapper that does e.g. media check & then calls axnet_tx_timeout. */
if (dev->tx_timeout == NULL)
- dev->tx_timeout = ei_tx_timeout;
+ dev->tx_timeout = axnet_tx_timeout;
if (dev->watchdog_timeo <= 0)
dev->watchdog_timeo = TX_TIMEOUT;
#endif
@@ -1003,14 +1002,14 @@ static int ax_close(struct net_device *dev)
}
/**
- * ei_tx_timeout - handle transmit time out condition
+ * axnet_tx_timeout - handle transmit time out condition
* @dev: network device which has apparently fallen asleep
*
* Called by kernel when device never acknowledges a transmit has
* completed (or failed) - i.e. never posted a Tx related interrupt.
*/
-static void ei_tx_timeout(struct net_device *dev)
+static void axnet_tx_timeout(struct net_device *dev)
{
long e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
@@ -1047,14 +1046,14 @@ static void ei_tx_timeout(struct net_device *dev)
}
/**
- * ei_start_xmit - begin packet transmission
+ * axnet_start_xmit - begin packet transmission
* @skb: packet to be sent
* @dev: network device to which packet is sent
*
* Sends a packet to an 8390 network device.
*/
-static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static int axnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
long e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
@@ -1493,7 +1492,6 @@ static void ei_receive(struct net_device *dev)
ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
if (pkt_stat & ENRSR_PHY)
@@ -1720,7 +1718,7 @@ static void axdev_setup(struct net_device *dev)
ei_local = (struct ei_device *)netdev_priv(dev);
spin_lock_init(&ei_local->page_lock);
- dev->hard_start_xmit = &ei_start_xmit;
+ dev->hard_start_xmit = &axnet_start_xmit;
dev->get_stats = get_stats;
dev->set_multicast_list = &set_multicast_list;
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
index 831090c75622..7b5c77b7bd27 100644
--- a/drivers/net/pcmcia/com20020_cs.c
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -155,7 +155,7 @@ static int com20020_probe(struct pcmcia_device *p_dev)
if (!dev)
goto fail_alloc_dev;
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->timeout = timeout;
lp->backplane = backplane;
lp->clockp = clockp;
@@ -303,7 +303,7 @@ static int com20020_config(struct pcmcia_device *link)
goto failed;
}
- lp = dev->priv;
+ lp = netdev_priv(dev);
lp->card_name = "PCMCIA COM20020";
lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
@@ -364,7 +364,7 @@ static int com20020_resume(struct pcmcia_device *link)
if (link->open) {
int ioaddr = dev->base_addr;
- struct arcnet_local *lp = dev->priv;
+ struct arcnet_local *lp = netdev_priv(dev);
ARCRESET;
}
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 69d916daa7bb..69dcfbbabe82 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -125,6 +125,7 @@ typedef struct local_info_t {
u_short tx_queue_len;
cardtype_t cardtype;
u_short sent;
+ u_char __iomem *base;
} local_info_t;
#define MC_FILTERBREAK 64
@@ -242,6 +243,7 @@ static int fmvj18x_probe(struct pcmcia_device *link)
lp = netdev_priv(dev);
link->priv = dev;
lp->p_dev = link;
+ lp->base = NULL;
/* The io structure describes IO port mapping */
link->io.NumPorts1 = 32;
@@ -348,7 +350,6 @@ static int fmvj18x_config(struct pcmcia_device *link)
cardtype_t cardtype;
char *card_name = "unknown";
u_char *node_id;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "fmvj18x_config(0x%p)\n", link);
@@ -443,8 +444,10 @@ static int fmvj18x_config(struct pcmcia_device *link)
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
- if (link->io.BasePort2 != 0)
- fmvj18x_setup_mfc(link);
+ if (link->io.BasePort2 != 0) {
+ ret = fmvj18x_setup_mfc(link);
+ if (ret != 0) goto failed;
+ }
ioaddr = dev->base_addr;
@@ -539,9 +542,9 @@ static int fmvj18x_config(struct pcmcia_device *link)
/* print current configuration */
printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, "
- "hw_addr %s\n",
+ "hw_addr %pM\n",
dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2",
- dev->base_addr, dev->irq, print_mac(mac, dev->dev_addr));
+ dev->base_addr, dev->irq, dev->dev_addr);
return 0;
@@ -611,10 +614,10 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
{
win_req_t req;
memreq_t mem;
- u_char __iomem *base;
- int i, j;
+ int i;
struct net_device *dev = link->priv;
unsigned int ioaddr;
+ local_info_t *lp = netdev_priv(dev);
/* Allocate a small memory window */
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
@@ -626,25 +629,32 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
return -1;
}
- base = ioremap(req.Base, req.Size);
+ lp->base = ioremap(req.Base, req.Size);
+ if (lp->base == NULL) {
+ printk(KERN_NOTICE "fmvj18x_cs: ioremap failed\n");
+ return -1;
+ }
+
mem.Page = 0;
mem.CardOffset = 0;
- pcmcia_map_mem_page(link->win, &mem);
-
+ i = pcmcia_map_mem_page(link->win, &mem);
+ if (i != 0) {
+ iounmap(lp->base);
+ lp->base = NULL;
+ cs_error(link, MapMemPage, i);
+ return -1;
+ }
+
ioaddr = dev->base_addr;
- writeb(0x47, base+0x800); /* Config Option Register of LAN */
- writeb(0x0, base+0x802); /* Config and Status Register */
+ writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */
+ writeb(0x0, lp->base+0x802); /* Config and Status Register */
- writeb(ioaddr & 0xff, base+0x80a); /* I/O Base(Low) of LAN */
- writeb((ioaddr >> 8) & 0xff, base+0x80c); /* I/O Base(High) of LAN */
+ writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */
+ writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */
- writeb(0x45, base+0x820); /* Config Option Register of Modem */
- writeb(0x8, base+0x822); /* Config and Status Register */
+ writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */
+ writeb(0x8, lp->base+0x822); /* Config and Status Register */
- iounmap(base);
- j = pcmcia_release_window(link->win);
- if (j != 0)
- cs_error(link, ReleaseWindow, j);
return 0;
}
@@ -652,8 +662,25 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
static void fmvj18x_release(struct pcmcia_device *link)
{
- DEBUG(0, "fmvj18x_release(0x%p)\n", link);
- pcmcia_disable_device(link);
+
+ struct net_device *dev = link->priv;
+ local_info_t *lp = netdev_priv(dev);
+ u_char __iomem *tmp;
+ int j;
+
+ DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+
+ if (lp->base != NULL) {
+ tmp = lp->base;
+ lp->base = NULL; /* set NULL before iounmap */
+ iounmap(tmp);
+ j = pcmcia_release_window(link->win);
+ if (j != 0)
+ cs_error(link, ReleaseWindow, j);
+ }
+
+ pcmcia_disable_device(link);
+
}
static int fmvj18x_suspend(struct pcmcia_device *link)
@@ -784,6 +811,13 @@ static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
outb(D_TX_INTR, ioaddr + TX_INTR);
outb(D_RX_INTR, ioaddr + RX_INTR);
+
+ if (lp->base != NULL) {
+ /* Ack interrupt for multifunction card */
+ writeb(0x01, lp->base+0x802);
+ writeb(0x09, lp->base+0x822);
+ }
+
return IRQ_HANDLED;
} /* fjn_interrupt */
@@ -1036,7 +1070,6 @@ static void fjn_rx(struct net_device *dev)
#endif
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 448cd40aeba5..ec7c588c9ae5 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -659,7 +659,6 @@ static int nmclan_config(struct pcmcia_device *link)
u_char buf[64];
int i, last_ret, last_fn;
unsigned int ioaddr;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "nmclan_config(0x%p)\n", link);
@@ -719,9 +718,9 @@ static int nmclan_config(struct pcmcia_device *link)
strcpy(lp->node.dev_name, dev->name);
printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port,"
- " hw_addr %s\n",
+ " hw_addr %pM\n",
dev->name, dev->base_addr, dev->irq, if_names[dev->if_port],
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
return 0;
cs_failed:
@@ -1193,7 +1192,6 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
- dev->last_rx = jiffies;
lp->linux_stats.rx_packets++;
lp->linux_stats.rx_bytes += pkt_len;
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index ce486f094492..c38ed777f0a8 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -554,7 +554,6 @@ static int pcnet_config(struct pcmcia_device *link)
int last_ret, last_fn, start_pg, stop_pg, cm_offset;
int has_shmem = 0;
hw_info_t *local_hw_info;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "pcnet_config(0x%p)\n", link);
@@ -675,7 +674,7 @@ static int pcnet_config(struct pcmcia_device *link)
printk (" mem %#5lx,", dev->mem_start);
if (info->flags & HAS_MISC_REG)
printk(" %s xcvr,", if_names[dev->if_port]);
- printk(" hw_addr %s\n", print_mac(mac, dev->dev_addr));
+ printk(" hw_addr %pM\n", dev->dev_addr);
return 0;
cs_failed:
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index c74d6656d266..fccd53ef3c64 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -949,7 +949,6 @@ static int smc91c92_config(struct pcmcia_device *link)
int i, j, rev;
unsigned int ioaddr;
u_long mir;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "smc91c92_config(0x%p)\n", link);
@@ -1062,9 +1061,9 @@ static int smc91c92_config(struct pcmcia_device *link)
strcpy(smc->node.dev_name, dev->name);
printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, "
- "hw_addr %s\n",
+ "hw_addr %pM\n",
dev->name, name, (rev & 0x0f), dev->base_addr, dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
if (rev > 0) {
if (mir & 0x3ff)
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index e1fd585e7131..fef7e1861d6a 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -772,7 +772,6 @@ xirc2ps_config(struct pcmcia_device * link)
int err, i;
u_char buf[64];
cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
- DECLARE_MAC_BUF(mac);
local->dingo_ccr = NULL;
@@ -1051,9 +1050,9 @@ xirc2ps_config(struct pcmcia_device * link)
strcpy(local->node.dev_name, dev->name);
/* give some infos about the hardware */
- printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr %s\n",
+ printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr %pM\n",
dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
return 0;
@@ -1243,7 +1242,6 @@ xirc2ps_interrupt(int irq, void *dev_id)
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pktlen;
if (!(rsr & PhyPkt))
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index ca8c0e037400..044b7b07f5f4 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1246,7 +1246,6 @@ static void pcnet32_rx_entry(struct net_device *dev,
dev->stats.rx_bytes += skb->len;
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
return;
}
@@ -1398,7 +1397,7 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
if (work_done < budget) {
spin_lock_irqsave(&lp->lock, flags);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
/* clear interrupt masks */
val = lp->a.read_csr(ioaddr, CSR3);
@@ -1747,8 +1746,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
memset(dev->dev_addr, 0, sizeof(dev->dev_addr));
if (pcnet32_debug & NETIF_MSG_PROBE) {
- DECLARE_MAC_BUF(mac);
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
/* Version 0x2623 and 0x2624 */
if (((chip_version + 1) & 0xfffe) == 0x2624) {
@@ -2588,14 +2586,14 @@ pcnet32_interrupt(int irq, void *dev_id)
dev->name, csr0);
/* unlike for the lance, there is no restart needed */
}
- if (netif_rx_schedule_prep(dev, &lp->napi)) {
+ if (netif_rx_schedule_prep(&lp->napi)) {
u16 val;
/* set interrupt masks */
val = lp->a.read_csr(ioaddr, CSR3);
val |= 0x5f00;
lp->a.write_csr(ioaddr, CSR3, val);
mmiowb();
- __netif_rx_schedule(dev, &lp->napi);
+ __netif_rx_schedule(&lp->napi);
break;
}
csr0 = lp->a.read_csr(ioaddr, CSR0);
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index d55932acd887..de9cf5136fdc 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -66,6 +66,22 @@ config REALTEK_PHY
---help---
Supports the Realtek 821x PHY.
+config NATIONAL_PHY
+ tristate "Drivers for National Semiconductor PHYs"
+ ---help---
+ Currently supports the DP83865 PHY.
+
+config STE10XP
+ depends on PHYLIB
+ tristate "Driver for STMicroelectronics STe10Xp PHYs"
+ ---help---
+ This is the driver for the STe100p and STe101p PHYs.
+
+config LSI_ET1011C_PHY
+ tristate "Driver for LSI ET1011C PHY"
+ ---help---
+ Supports the LSI ET1011C PHY.
+
config FIXED_PHY
bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
depends on PHYLIB=y
@@ -84,10 +100,13 @@ config MDIO_BITBANG
If in doubt, say N.
-config MDIO_OF_GPIO
+config MDIO_GPIO
tristate "Support for GPIO lib-based bitbanged MDIO buses"
- depends on MDIO_BITBANG && OF_GPIO
+ depends on MDIO_BITBANG && GENERIC_GPIO
---help---
Supports GPIO lib-based MDIO busses.
+ To compile this driver as a module, choose M here: the module
+ will be called mdio-gpio.
+
endif # PHYLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index eee329fa6f53..3a1bfefefbc3 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -13,6 +13,9 @@ obj-$(CONFIG_VITESSE_PHY) += vitesse.o
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
obj-$(CONFIG_ICPLUS_PHY) += icplus.o
obj-$(CONFIG_REALTEK_PHY) += realtek.o
+obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
obj-$(CONFIG_FIXED_PHY) += fixed.o
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
-obj-$(CONFIG_MDIO_OF_GPIO) += mdio-ofgpio.o
+obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
+obj-$(CONFIG_NATIONAL_PHY) += national.o
+obj-$(CONFIG_STE10XP) += ste10Xp.o
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 4b4dc98ad165..190efc3301c6 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -17,6 +17,8 @@
#include <linux/module.h>
#include <linux/phy.h>
+#define PHY_ID_BCM50610 0x0143bd60
+
#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
@@ -54,6 +56,21 @@
#define MII_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
/*
+ * AUXILIARY CONTROL SHADOW ACCESS REGISTERS. (PHY REG 0x18)
+ */
+#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
+#define MII_BCM54XX_AUXCTL_ACTL_TX_6DB 0x0400
+#define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800
+
+#define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
+#define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
+#define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
+
+#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
+
+
+/*
* Broadcom LED source encodings. These are used in BCM5461, BCM5481,
* BCM5482, and possibly some others.
*/
@@ -88,6 +105,24 @@
#define BCM5482_SHD_MODE_1000BX 0x0001 /* Enable 1000BASE-X registers */
/*
+ * EXPANSION SHADOW ACCESS REGISTERS. (PHY REG 0x15, 0x16, and 0x17)
+ */
+#define MII_BCM54XX_EXP_AADJ1CH0 0x001f
+#define MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN 0x0200
+#define MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF 0x0100
+#define MII_BCM54XX_EXP_AADJ1CH3 0x601f
+#define MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ 0x0002
+#define MII_BCM54XX_EXP_EXP08 0x0F08
+#define MII_BCM54XX_EXP_EXP08_RJCT_2MHZ 0x0001
+#define MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE 0x0200
+#define MII_BCM54XX_EXP_EXP75 0x0f75
+#define MII_BCM54XX_EXP_EXP75_VDACCTRL 0x003c
+#define MII_BCM54XX_EXP_EXP96 0x0f96
+#define MII_BCM54XX_EXP_EXP96_MYST 0x0010
+#define MII_BCM54XX_EXP_EXP97 0x0f97
+#define MII_BCM54XX_EXP_EXP97_MYST 0x0c0c
+
+/*
* BCM5482: Secondary SerDes registers
*/
#define BCM5482_SSD_1000BX_CTL 0x00 /* 1000BASE-X Control */
@@ -128,40 +163,93 @@ static int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow, u16 val)
MII_BCM54XX_SHD_DATA(val));
}
-/*
- * Indirect register access functions for the Expansion Registers
- * and Secondary SerDes registers (when sec_serdes=1).
- */
-static int bcm54xx_exp_read(struct phy_device *phydev,
- int sec_serdes, u8 regnum)
+/* Indirect register access functions for the Expansion Registers */
+static int bcm54xx_exp_read(struct phy_device *phydev, u8 regnum)
{
int val;
- phy_write(phydev, MII_BCM54XX_EXP_SEL,
- (sec_serdes ? MII_BCM54XX_EXP_SEL_SSD :
- MII_BCM54XX_EXP_SEL_ER) |
- regnum);
+ val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
+ if (val < 0)
+ return val;
+
val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
- phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
+
+ /* Restore default value. It's O.K. if this write fails. */
+ phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
return val;
}
-static int bcm54xx_exp_write(struct phy_device *phydev,
- int sec_serdes, u8 regnum, u16 val)
+static int bcm54xx_exp_write(struct phy_device *phydev, u16 regnum, u16 val)
{
int ret;
- phy_write(phydev, MII_BCM54XX_EXP_SEL,
- (sec_serdes ? MII_BCM54XX_EXP_SEL_SSD :
- MII_BCM54XX_EXP_SEL_ER) |
- regnum);
+ ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
+ if (ret < 0)
+ return ret;
+
ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
- phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
+
+ /* Restore default value. It's O.K. if this write fails. */
+ phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
return ret;
}
+static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
+{
+ return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
+}
+
+static int bcm50610_a0_workaround(struct phy_device *phydev)
+{
+ int err;
+
+ err = bcm54xx_auxctl_write(phydev,
+ MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
+ MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
+ MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
+ if (err < 0)
+ return err;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
+ MII_BCM54XX_EXP_EXP08_RJCT_2MHZ |
+ MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE);
+ if (err < 0)
+ goto error;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0,
+ MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
+ MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
+ if (err < 0)
+ goto error;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3,
+ MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
+ if (err < 0)
+ goto error;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75,
+ MII_BCM54XX_EXP_EXP75_VDACCTRL);
+ if (err < 0)
+ goto error;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96,
+ MII_BCM54XX_EXP_EXP96_MYST);
+ if (err < 0)
+ goto error;
+
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97,
+ MII_BCM54XX_EXP_EXP97_MYST);
+
+error:
+ bcm54xx_auxctl_write(phydev,
+ MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
+ MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
+
+ return err;
+}
+
static int bcm54xx_config_init(struct phy_device *phydev)
{
int reg, err;
@@ -183,6 +271,13 @@ static int bcm54xx_config_init(struct phy_device *phydev)
err = phy_write(phydev, MII_BCM54XX_IMR, reg);
if (err < 0)
return err;
+
+ if (phydev->drv->phy_id == PHY_ID_BCM50610) {
+ err = bcm50610_a0_workaround(phydev);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -205,18 +300,27 @@ static int bcm5482_config_init(struct phy_device *phydev)
/*
* Enable SGMII slave mode and auto-detection
*/
- reg = bcm54xx_exp_read(phydev, 1, BCM5482_SSD_SGMII_SLAVE);
- bcm54xx_exp_write(phydev, 1, BCM5482_SSD_SGMII_SLAVE,
- reg |
- BCM5482_SSD_SGMII_SLAVE_EN |
- BCM5482_SSD_SGMII_SLAVE_AD);
+ reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
+ err = bcm54xx_exp_read(phydev, reg);
+ if (err < 0)
+ return err;
+ err = bcm54xx_exp_write(phydev, reg, err |
+ BCM5482_SSD_SGMII_SLAVE_EN |
+ BCM5482_SSD_SGMII_SLAVE_AD);
+ if (err < 0)
+ return err;
/*
* Disable secondary SerDes powerdown
*/
- reg = bcm54xx_exp_read(phydev, 1, BCM5482_SSD_1000BX_CTL);
- bcm54xx_exp_write(phydev, 1, BCM5482_SSD_1000BX_CTL,
- reg & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
+ reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
+ err = bcm54xx_exp_read(phydev, reg);
+ if (err < 0)
+ return err;
+ err = bcm54xx_exp_write(phydev, reg,
+ err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
+ if (err < 0)
+ return err;
/*
* Select 1000BASE-X register set (primary SerDes)
@@ -335,7 +439,8 @@ static struct phy_driver bcm5411_driver = {
.phy_id = 0x00206070,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5411",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
@@ -349,7 +454,8 @@ static struct phy_driver bcm5421_driver = {
.phy_id = 0x002060e0,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5421",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
@@ -363,7 +469,8 @@ static struct phy_driver bcm5461_driver = {
.phy_id = 0x002060c0,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5461",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
@@ -377,7 +484,8 @@ static struct phy_driver bcm5464_driver = {
.phy_id = 0x002060b0,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5464",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = genphy_config_aneg,
@@ -391,7 +499,8 @@ static struct phy_driver bcm5481_driver = {
.phy_id = 0x0143bca0,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5481",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54xx_config_init,
.config_aneg = bcm5481_config_aneg,
@@ -405,7 +514,8 @@ static struct phy_driver bcm5482_driver = {
.phy_id = 0x0143bcb0,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5482",
- .features = PHY_GBIT_FEATURES,
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm5482_config_init,
.config_aneg = genphy_config_aneg,
@@ -415,6 +525,36 @@ static struct phy_driver bcm5482_driver = {
.driver = { .owner = THIS_MODULE },
};
+static struct phy_driver bcm50610_driver = {
+ .phy_id = PHY_ID_BCM50610,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM50610",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = bcm54xx_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = bcm54xx_ack_interrupt,
+ .config_intr = bcm54xx_config_intr,
+ .driver = { .owner = THIS_MODULE },
+};
+
+static struct phy_driver bcm57780_driver = {
+ .phy_id = 0x03625d90,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM57780",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = bcm54xx_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = bcm54xx_ack_interrupt,
+ .config_intr = bcm54xx_config_intr,
+ .driver = { .owner = THIS_MODULE },
+};
+
static int __init broadcom_init(void)
{
int ret;
@@ -437,8 +577,18 @@ static int __init broadcom_init(void)
ret = phy_driver_register(&bcm5482_driver);
if (ret)
goto out_5482;
+ ret = phy_driver_register(&bcm50610_driver);
+ if (ret)
+ goto out_50610;
+ ret = phy_driver_register(&bcm57780_driver);
+ if (ret)
+ goto out_57780;
return ret;
+out_57780:
+ phy_driver_unregister(&bcm50610_driver);
+out_50610:
+ phy_driver_unregister(&bcm5482_driver);
out_5482:
phy_driver_unregister(&bcm5481_driver);
out_5481:
@@ -455,6 +605,8 @@ out_5411:
static void __exit broadcom_exit(void)
{
+ phy_driver_unregister(&bcm57780_driver);
+ phy_driver_unregister(&bcm50610_driver);
phy_driver_unregister(&bcm5482_driver);
phy_driver_unregister(&bcm5481_driver);
phy_driver_unregister(&bcm5464_driver);
diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c
new file mode 100644
index 000000000000..b031fa21f1aa
--- /dev/null
+++ b/drivers/net/phy/et1011c.c
@@ -0,0 +1,113 @@
+/*
+ * drivers/net/phy/et1011c.c
+ *
+ * Driver for LSI ET1011C PHYs
+ *
+ * Author: Chaithrika U S
+ *
+ * Copyright (c) 2008 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+
+#define ET1011C_STATUS_REG (0x1A)
+#define ET1011C_CONFIG_REG (0x16)
+#define ET1011C_SPEED_MASK (0x0300)
+#define ET1011C_GIGABIT_SPEED (0x0200)
+#define ET1011C_TX_FIFO_MASK (0x3000)
+#define ET1011C_TX_FIFO_DEPTH_8 (0x0000)
+#define ET1011C_TX_FIFO_DEPTH_16 (0x1000)
+#define ET1011C_INTERFACE_MASK (0x0007)
+#define ET1011C_GMII_INTERFACE (0x0002)
+#define ET1011C_SYS_CLK_EN (0x01 << 4)
+
+
+MODULE_DESCRIPTION("LSI ET1011C PHY driver");
+MODULE_AUTHOR("Chaithrika U S");
+MODULE_LICENSE("GPL");
+
+static int et1011c_config_aneg(struct phy_device *phydev)
+{
+ int ctl = 0;
+ ctl = phy_read(phydev, MII_BMCR);
+ if (ctl < 0)
+ return ctl;
+ ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 |
+ BMCR_ANENABLE);
+ /* First clear the PHY */
+ phy_write(phydev, MII_BMCR, ctl | BMCR_RESET);
+
+ return genphy_config_aneg(phydev);
+}
+
+static int et1011c_read_status(struct phy_device *phydev)
+{
+ int ret;
+ u32 val;
+ static int speed;
+ ret = genphy_read_status(phydev);
+
+ if (speed != phydev->speed) {
+ speed = phydev->speed;
+ val = phy_read(phydev, ET1011C_STATUS_REG);
+ if ((val & ET1011C_SPEED_MASK) ==
+ ET1011C_GIGABIT_SPEED) {
+ val = phy_read(phydev, ET1011C_CONFIG_REG);
+ val &= ~ET1011C_TX_FIFO_MASK;
+ phy_write(phydev, ET1011C_CONFIG_REG, val\
+ | ET1011C_GMII_INTERFACE\
+ | ET1011C_SYS_CLK_EN\
+ | ET1011C_TX_FIFO_DEPTH_16);
+
+ }
+ }
+ return ret;
+}
+
+static struct phy_driver et1011c_driver = {
+ .phy_id = 0x0282f014,
+ .name = "ET1011C",
+ .phy_id_mask = 0xfffffff0,
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_1000baseT_Full),
+ .flags = PHY_POLL,
+ .config_aneg = et1011c_config_aneg,
+ .read_status = et1011c_read_status,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init et1011c_init(void)
+{
+ return phy_driver_register(&et1011c_driver);
+}
+
+static void __exit et1011c_exit(void)
+{
+ phy_driver_unregister(&et1011c_driver);
+}
+
+module_init(et1011c_init);
+module_exit(et1011c_exit);
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
new file mode 100644
index 000000000000..a439ebeb4319
--- /dev/null
+++ b/drivers/net/phy/mdio-gpio.c
@@ -0,0 +1,296 @@
+/*
+ * GPIO based MDIO bitbang driver.
+ * Supports OpenFirmware.
+ *
+ * Copyright (c) 2008 CSE Semaphore Belgium.
+ * by Laurent Pinchart <laurentp@cse-semaphore.com>
+ *
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * Based on earlier work by
+ *
+ * Copyright (c) 2003 Intracom S.A.
+ * by Pantelis Antoniou <panto@intracom.gr>
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mdio-gpio.h>
+
+#ifdef CONFIG_OF_GPIO
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#endif
+
+struct mdio_gpio_info {
+ struct mdiobb_ctrl ctrl;
+ int mdc, mdio;
+};
+
+static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
+{
+ struct mdio_gpio_info *bitbang =
+ container_of(ctrl, struct mdio_gpio_info, ctrl);
+
+ if (dir)
+ gpio_direction_output(bitbang->mdio, 1);
+ else
+ gpio_direction_input(bitbang->mdio);
+}
+
+static int mdio_get(struct mdiobb_ctrl *ctrl)
+{
+ struct mdio_gpio_info *bitbang =
+ container_of(ctrl, struct mdio_gpio_info, ctrl);
+
+ return gpio_get_value(bitbang->mdio);
+}
+
+static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
+{
+ struct mdio_gpio_info *bitbang =
+ container_of(ctrl, struct mdio_gpio_info, ctrl);
+
+ gpio_set_value(bitbang->mdio, what);
+}
+
+static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
+{
+ struct mdio_gpio_info *bitbang =
+ container_of(ctrl, struct mdio_gpio_info, ctrl);
+
+ gpio_set_value(bitbang->mdc, what);
+}
+
+static struct mdiobb_ops mdio_gpio_ops = {
+ .owner = THIS_MODULE,
+ .set_mdc = mdc_set,
+ .set_mdio_dir = mdio_dir,
+ .set_mdio_data = mdio_set,
+ .get_mdio_data = mdio_get,
+};
+
+static int __devinit mdio_gpio_bus_init(struct device *dev,
+ struct mdio_gpio_platform_data *pdata,
+ int bus_id)
+{
+ struct mii_bus *new_bus;
+ struct mdio_gpio_info *bitbang;
+ int ret = -ENOMEM;
+ int i;
+
+ bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL);
+ if (!bitbang)
+ goto out;
+
+ bitbang->ctrl.ops = &mdio_gpio_ops;
+ bitbang->mdc = pdata->mdc;
+ bitbang->mdio = pdata->mdio;
+
+ new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
+ if (!new_bus)
+ goto out_free_bitbang;
+
+ new_bus->name = "GPIO Bitbanged MDIO",
+
+ ret = -ENODEV;
+
+ new_bus->phy_mask = pdata->phy_mask;
+ new_bus->irq = pdata->irqs;
+ new_bus->parent = dev;
+
+ if (new_bus->phy_mask == ~0)
+ goto out_free_bus;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ if (!new_bus->irq[i])
+ new_bus->irq[i] = PHY_POLL;
+
+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", bus_id);
+
+ if (gpio_request(bitbang->mdc, "mdc"))
+ goto out_free_bus;
+
+ if (gpio_request(bitbang->mdio, "mdio"))
+ goto out_free_mdc;
+
+ dev_set_drvdata(dev, new_bus);
+
+ ret = mdiobus_register(new_bus);
+ if (ret)
+ goto out_free_all;
+
+ return 0;
+
+out_free_all:
+ dev_set_drvdata(dev, NULL);
+ gpio_free(bitbang->mdio);
+out_free_mdc:
+ gpio_free(bitbang->mdc);
+out_free_bus:
+ free_mdio_bitbang(new_bus);
+out_free_bitbang:
+ kfree(bitbang);
+out:
+ return ret;
+}
+
+static void __devexit mdio_gpio_bus_destroy(struct device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(dev);
+ struct mdio_gpio_info *bitbang = bus->priv;
+
+ mdiobus_unregister(bus);
+ free_mdio_bitbang(bus);
+ dev_set_drvdata(dev, NULL);
+ gpio_free(bitbang->mdc);
+ gpio_free(bitbang->mdio);
+ kfree(bitbang);
+}
+
+static int __devinit mdio_gpio_probe(struct platform_device *pdev)
+{
+ struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata)
+ return -ENODEV;
+
+ return mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id);
+}
+
+static int __devexit mdio_gpio_remove(struct platform_device *pdev)
+{
+ mdio_gpio_bus_destroy(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF_GPIO
+static void __devinit add_phy(struct mdio_gpio_platform_data *pdata,
+ struct device_node *np)
+{
+ const u32 *data;
+ int len, id, irq;
+
+ data = of_get_property(np, "reg", &len);
+ if (!data || len != 4)
+ return;
+
+ id = *data;
+ pdata->phy_mask &= ~(1 << id);
+
+ irq = of_irq_to_resource(np, 0, NULL);
+ if (irq)
+ pdata->irqs[id] = irq;
+}
+
+static int __devinit mdio_ofgpio_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct device_node *np = NULL;
+ struct mdio_gpio_platform_data *pdata;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->mdc = of_get_gpio(ofdev->node, 0);
+ pdata->mdio = of_get_gpio(ofdev->node, 1);
+
+ if (pdata->mdc < 0 || pdata->mdio < 0)
+ goto out_free;
+
+ while ((np = of_get_next_child(ofdev->node, np)))
+ if (!strcmp(np->type, "ethernet-phy"))
+ add_phy(pdata, np);
+
+ return mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc);
+
+out_free:
+ kfree(pdata);
+ return -ENODEV;
+}
+
+static int __devexit mdio_ofgpio_remove(struct of_device *ofdev)
+{
+ mdio_gpio_bus_destroy(&ofdev->dev);
+ kfree(ofdev->dev.platform_data);
+
+ return 0;
+}
+
+static struct of_device_id mdio_ofgpio_match[] = {
+ {
+ .compatible = "virtual,mdio-gpio",
+ },
+ {},
+};
+
+static struct of_platform_driver mdio_ofgpio_driver = {
+ .name = "mdio-gpio",
+ .match_table = mdio_ofgpio_match,
+ .probe = mdio_ofgpio_probe,
+ .remove = __devexit_p(mdio_ofgpio_remove),
+};
+
+static inline int __init mdio_ofgpio_init(void)
+{
+ return of_register_platform_driver(&mdio_ofgpio_driver);
+}
+
+static inline void __exit mdio_ofgpio_exit(void)
+{
+ of_unregister_platform_driver(&mdio_ofgpio_driver);
+}
+#else
+static inline int __init mdio_ofgpio_init(void) { return 0; }
+static inline void __exit mdio_ofgpio_exit(void) { }
+#endif /* CONFIG_OF_GPIO */
+
+static struct platform_driver mdio_gpio_driver = {
+ .probe = mdio_gpio_probe,
+ .remove = __devexit_p(mdio_gpio_remove),
+ .driver = {
+ .name = "mdio-gpio",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mdio_gpio_init(void)
+{
+ int ret;
+
+ ret = mdio_ofgpio_init();
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&mdio_gpio_driver);
+ if (ret)
+ mdio_ofgpio_exit();
+
+ return ret;
+}
+module_init(mdio_gpio_init);
+
+static void __exit mdio_gpio_exit(void)
+{
+ platform_driver_unregister(&mdio_gpio_driver);
+ mdio_ofgpio_exit();
+}
+module_exit(mdio_gpio_exit);
+
+MODULE_ALIAS("platform:mdio-gpio");
+MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");
diff --git a/drivers/net/phy/mdio-ofgpio.c b/drivers/net/phy/mdio-ofgpio.c
deleted file mode 100644
index 2ff97754e574..000000000000
--- a/drivers/net/phy/mdio-ofgpio.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * OpenFirmware GPIO based MDIO bitbang driver.
- *
- * Copyright (c) 2008 CSE Semaphore Belgium.
- * by Laurent Pinchart <laurentp@cse-semaphore.com>
- *
- * Based on earlier work by
- *
- * Copyright (c) 2003 Intracom S.A.
- * by Pantelis Antoniou <panto@intracom.gr>
- *
- * 2005 (c) MontaVista Software, Inc.
- * Vitaly Bordug <vbordug@ru.mvista.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/mdio-bitbang.h>
-#include <linux/of_gpio.h>
-#include <linux/of_platform.h>
-
-struct mdio_gpio_info {
- struct mdiobb_ctrl ctrl;
- int mdc, mdio;
-};
-
-static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
-{
- struct mdio_gpio_info *bitbang =
- container_of(ctrl, struct mdio_gpio_info, ctrl);
-
- if (dir)
- gpio_direction_output(bitbang->mdio, 1);
- else
- gpio_direction_input(bitbang->mdio);
-}
-
-static int mdio_read(struct mdiobb_ctrl *ctrl)
-{
- struct mdio_gpio_info *bitbang =
- container_of(ctrl, struct mdio_gpio_info, ctrl);
-
- return gpio_get_value(bitbang->mdio);
-}
-
-static void mdio(struct mdiobb_ctrl *ctrl, int what)
-{
- struct mdio_gpio_info *bitbang =
- container_of(ctrl, struct mdio_gpio_info, ctrl);
-
- gpio_set_value(bitbang->mdio, what);
-}
-
-static void mdc(struct mdiobb_ctrl *ctrl, int what)
-{
- struct mdio_gpio_info *bitbang =
- container_of(ctrl, struct mdio_gpio_info, ctrl);
-
- gpio_set_value(bitbang->mdc, what);
-}
-
-static struct mdiobb_ops mdio_gpio_ops = {
- .owner = THIS_MODULE,
- .set_mdc = mdc,
- .set_mdio_dir = mdio_dir,
- .set_mdio_data = mdio,
- .get_mdio_data = mdio_read,
-};
-
-static int __devinit mdio_ofgpio_bitbang_init(struct mii_bus *bus,
- struct device_node *np)
-{
- struct mdio_gpio_info *bitbang = bus->priv;
-
- bitbang->mdc = of_get_gpio(np, 0);
- bitbang->mdio = of_get_gpio(np, 1);
-
- if (bitbang->mdc < 0 || bitbang->mdio < 0)
- return -ENODEV;
-
- snprintf(bus->id, MII_BUS_ID_SIZE, "%x", bitbang->mdc);
- return 0;
-}
-
-static void __devinit add_phy(struct mii_bus *bus, struct device_node *np)
-{
- const u32 *data;
- int len, id, irq;
-
- data = of_get_property(np, "reg", &len);
- if (!data || len != 4)
- return;
-
- id = *data;
- bus->phy_mask &= ~(1 << id);
-
- irq = of_irq_to_resource(np, 0, NULL);
- if (irq != NO_IRQ)
- bus->irq[id] = irq;
-}
-
-static int __devinit mdio_ofgpio_probe(struct of_device *ofdev,
- const struct of_device_id *match)
-{
- struct device_node *np = NULL;
- struct mii_bus *new_bus;
- struct mdio_gpio_info *bitbang;
- int ret = -ENOMEM;
- int i;
-
- bitbang = kzalloc(sizeof(struct mdio_gpio_info), GFP_KERNEL);
- if (!bitbang)
- goto out;
-
- bitbang->ctrl.ops = &mdio_gpio_ops;
-
- new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
- if (!new_bus)
- goto out_free_bitbang;
-
- new_bus->name = "GPIO Bitbanged MII",
-
- ret = mdio_ofgpio_bitbang_init(new_bus, ofdev->node);
- if (ret)
- goto out_free_bus;
-
- new_bus->phy_mask = ~0;
- new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq)
- goto out_free_bus;
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- new_bus->irq[i] = -1;
-
- while ((np = of_get_next_child(ofdev->node, np)))
- if (!strcmp(np->type, "ethernet-phy"))
- add_phy(new_bus, np);
-
- new_bus->parent = &ofdev->dev;
- dev_set_drvdata(&ofdev->dev, new_bus);
-
- ret = mdiobus_register(new_bus);
- if (ret)
- goto out_free_irqs;
-
- return 0;
-
-out_free_irqs:
- dev_set_drvdata(&ofdev->dev, NULL);
- kfree(new_bus->irq);
-out_free_bus:
- free_mdio_bitbang(new_bus);
-out_free_bitbang:
- kfree(bitbang);
-out:
- return ret;
-}
-
-static int mdio_ofgpio_remove(struct of_device *ofdev)
-{
- struct mii_bus *bus = dev_get_drvdata(&ofdev->dev);
- struct mdio_gpio_info *bitbang = bus->priv;
-
- mdiobus_unregister(bus);
- kfree(bus->irq);
- free_mdio_bitbang(bus);
- dev_set_drvdata(&ofdev->dev, NULL);
- kfree(bitbang);
-
- return 0;
-}
-
-static struct of_device_id mdio_ofgpio_match[] = {
- {
- .compatible = "virtual,mdio-gpio",
- },
- {},
-};
-
-static struct of_platform_driver mdio_ofgpio_driver = {
- .name = "mdio-gpio",
- .match_table = mdio_ofgpio_match,
- .probe = mdio_ofgpio_probe,
- .remove = mdio_ofgpio_remove,
-};
-
-static int mdio_ofgpio_init(void)
-{
- return of_register_platform_driver(&mdio_ofgpio_driver);
-}
-
-static void mdio_ofgpio_exit(void)
-{
- of_unregister_platform_driver(&mdio_ofgpio_driver);
-}
-
-module_init(mdio_ofgpio_init);
-module_exit(mdio_ofgpio_exit);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 536bda1f428b..11adf6ed4628 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -63,7 +63,9 @@ EXPORT_SYMBOL(mdiobus_alloc);
static void mdiobus_release(struct device *d)
{
struct mii_bus *bus = to_mii_bus(d);
- BUG_ON(bus->state != MDIOBUS_RELEASED);
+ BUG_ON(bus->state != MDIOBUS_RELEASED &&
+ /* for compatibility with error handling in drivers */
+ bus->state != MDIOBUS_ALLOCATED);
kfree(bus);
}
@@ -83,8 +85,7 @@ static struct class mdio_bus_class = {
*/
int mdiobus_register(struct mii_bus *bus)
{
- int i;
- int err = 0;
+ int i, err;
if (NULL == bus || NULL == bus->name ||
NULL == bus->read ||
@@ -97,7 +98,7 @@ int mdiobus_register(struct mii_bus *bus)
bus->dev.parent = bus->parent;
bus->dev.class = &mdio_bus_class;
bus->dev.groups = NULL;
- memcpy(bus->dev.bus_id, bus->id, MII_BUS_ID_SIZE);
+ dev_set_name(&bus->dev, bus->id);
err = device_register(&bus->dev);
if (err) {
@@ -105,8 +106,6 @@ int mdiobus_register(struct mii_bus *bus)
return -EINVAL;
}
- bus->state = MDIOBUS_REGISTERED;
-
mutex_init(&bus->mdio_lock);
if (bus->reset)
@@ -118,13 +117,23 @@ int mdiobus_register(struct mii_bus *bus)
struct phy_device *phydev;
phydev = mdiobus_scan(bus, i);
- if (IS_ERR(phydev))
+ if (IS_ERR(phydev)) {
err = PTR_ERR(phydev);
+ goto error;
+ }
}
}
+ bus->state = MDIOBUS_REGISTERED;
pr_info("%s: probed\n", bus->name);
+ return 0;
+error:
+ while (--i >= 0) {
+ if (bus->phy_map[i])
+ device_unregister(&bus->phy_map[i]->dev);
+ }
+ device_del(&bus->dev);
return err;
}
EXPORT_SYMBOL(mdiobus_register);
@@ -191,7 +200,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
phydev->dev.parent = bus->parent;
phydev->dev.bus = &mdio_bus_type;
- snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, addr);
+ dev_set_name(&phydev->dev, PHY_ID_FMT, bus->id, addr);
phydev->bus = bus;
@@ -284,9 +293,12 @@ static int mdio_bus_suspend(struct device * dev, pm_message_t state)
{
int ret = 0;
struct device_driver *drv = dev->driver;
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ struct phy_device *phydev = to_phy_device(dev);
- if (drv && drv->suspend)
- ret = drv->suspend(dev, state);
+ if ((!device_may_wakeup(phydev->dev.parent)) &&
+ (phydrv && phydrv->suspend))
+ ret = phydrv->suspend(phydev);
return ret;
}
@@ -295,9 +307,12 @@ static int mdio_bus_resume(struct device * dev)
{
int ret = 0;
struct device_driver *drv = dev->driver;
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ struct phy_device *phydev = to_phy_device(dev);
- if (drv && drv->resume)
- ret = drv->resume(dev);
+ if ((!device_may_wakeup(phydev->dev.parent)) &&
+ (phydrv && phydrv->resume))
+ ret = phydrv->resume(phydev);
return ret;
}
diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c
new file mode 100644
index 000000000000..6c636eb72089
--- /dev/null
+++ b/drivers/net/phy/national.c
@@ -0,0 +1,155 @@
+/*
+ * drivers/net/phy/national.c
+ *
+ * Driver for National Semiconductor PHYs
+ *
+ * Author: Stuart Menefy <stuart.menefy@st.com>
+ * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ *
+ * Copyright (c) 2008 STMicroelectronics Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+
+/* DP83865 phy identifier values */
+#define DP83865_PHY_ID 0x20005c7a
+
+#define DP83865_INT_MASK_REG 0x15
+#define DP83865_INT_MASK_STATUS 0x14
+
+#define DP83865_INT_REMOTE_FAULT 0x0008
+#define DP83865_INT_ANE_COMPLETED 0x0010
+#define DP83865_INT_LINK_CHANGE 0xe000
+#define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \
+ DP83865_INT_ANE_COMPLETED | \
+ DP83865_INT_LINK_CHANGE)
+
+/* Advanced proprietary configuration */
+#define NS_EXP_MEM_CTL 0x16
+#define NS_EXP_MEM_DATA 0x1d
+#define NS_EXP_MEM_ADD 0x1e
+
+#define LED_CTRL_REG 0x13
+#define AN_FALLBACK_AN 0x0001
+#define AN_FALLBACK_CRC 0x0002
+#define AN_FALLBACK_IE 0x0004
+#define ALL_FALLBACK_ON (AN_FALLBACK_AN | AN_FALLBACK_CRC | AN_FALLBACK_IE)
+
+enum hdx_loopback {
+ hdx_loopback_on = 0,
+ hdx_loopback_off = 1,
+};
+
+static u8 ns_exp_read(struct phy_device *phydev, u16 reg)
+{
+ phy_write(phydev, NS_EXP_MEM_ADD, reg);
+ return phy_read(phydev, NS_EXP_MEM_DATA);
+}
+
+static void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data)
+{
+ phy_write(phydev, NS_EXP_MEM_ADD, reg);
+ phy_write(phydev, NS_EXP_MEM_DATA, data);
+}
+
+static int ns_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, DP83865_INT_MASK_REG,
+ DP83865_INT_MASK_DEFAULT);
+ else
+ err = phy_write(phydev, DP83865_INT_MASK_REG, 0);
+
+ return err;
+}
+
+static int ns_ack_interrupt(struct phy_device *phydev)
+{
+ int ret = phy_read(phydev, DP83865_INT_MASK_STATUS);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)
+{
+ int bmcr = phy_read(phydev, MII_BMCR);
+
+ phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN));
+
+ /* Enable 8 bit expended memory read/write (no auto increment) */
+ phy_write(phydev, NS_EXP_MEM_CTL, 0);
+ phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0);
+ phy_write(phydev, NS_EXP_MEM_DATA, 0x0008);
+ phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN));
+ phy_write(phydev, LED_CTRL_REG, mode);
+ return;
+}
+
+static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable)
+{
+ if (disable)
+ ns_exp_write(phydev, 0x1c0, ns_exp_read(phydev, 0x1c0) | 1);
+ else
+ ns_exp_write(phydev, 0x1c0,
+ ns_exp_read(phydev, 0x1c0) & 0xfffe);
+
+ printk(KERN_DEBUG "DP83865 PHY: 10BASE-T HDX loopback %s\n",
+ (ns_exp_read(phydev, 0x1c0) & 0x0001) ? "off" : "on");
+
+ return;
+}
+
+static int ns_config_init(struct phy_device *phydev)
+{
+ ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON);
+ /* In the latest MAC or switches design, the 10 Mbps loopback
+ is desired to be turned off. */
+ ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off);
+ return ns_ack_interrupt(phydev);
+}
+
+static struct phy_driver dp83865_driver = {
+ .phy_id = DP83865_PHY_ID,
+ .phy_id_mask = 0xfffffff0,
+ .name = "NatSemi DP83865",
+ .features = PHY_GBIT_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = ns_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = ns_ack_interrupt,
+ .config_intr = ns_config_intr,
+ .driver = {.owner = THIS_MODULE,}
+};
+
+static int __init ns_init(void)
+{
+ return phy_driver_register(&dp83865_driver);
+}
+
+static void __exit ns_exit(void)
+{
+ phy_driver_unregister(&dp83865_driver);
+}
+
+MODULE_DESCRIPTION("NatSemi PHY driver");
+MODULE_AUTHOR("Stuart Menefy");
+MODULE_LICENSE("GPL");
+
+module_init(ns_init);
+module_exit(ns_exit);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index df4e6257d4a7..e4ede6080c9d 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -45,7 +45,7 @@
*/
void phy_print_status(struct phy_device *phydev)
{
- pr_info("PHY: %s - Link is %s", phydev->dev.bus_id,
+ pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev),
phydev->link ? "Up" : "Down");
if (phydev->link)
printk(" - %d/%s", phydev->speed,
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 25acbbde4a60..e35460165bf7 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -74,7 +74,7 @@ int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
if (!fixup)
return -ENOMEM;
- strncpy(fixup->bus_id, bus_id, BUS_ID_SIZE);
+ strlcpy(fixup->bus_id, bus_id, sizeof(fixup->bus_id));
fixup->phy_uid = phy_uid;
fixup->phy_uid_mask = phy_uid_mask;
fixup->run = run;
@@ -109,7 +109,7 @@ EXPORT_SYMBOL(phy_register_fixup_for_id);
*/
static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
{
- if (strcmp(fixup->bus_id, phydev->dev.bus_id) != 0)
+ if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0)
if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
return 0;
@@ -232,7 +232,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr)
return NULL;
/*
- * Broken hardware is sometimes missing the pull down resistor on the
+ * Broken hardware is sometimes missing the pull-up resistor on the
* MDIO line, which results in reads to non-existent devices returning
* 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
* device as well.
@@ -517,23 +517,6 @@ int genphy_setup_forced(struct phy_device *phydev)
err = phy_write(phydev, MII_BMCR, ctl);
- if (err < 0)
- return err;
-
- /*
- * Run the fixups on this PHY, just in case the
- * board code needs to change something after a reset
- */
- err = phy_scan_fixups(phydev);
-
- if (err < 0)
- return err;
-
- /* We just reset the device, so we'd better configure any
- * settings the PHY requires to operate */
- if (phydev->drv->config_init)
- err = phydev->drv->config_init(phydev);
-
return err;
}
@@ -779,7 +762,35 @@ static int genphy_config_init(struct phy_device *phydev)
return 0;
}
+int genphy_suspend(struct phy_device *phydev)
+{
+ int value;
+ mutex_lock(&phydev->lock);
+
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, (value | BMCR_PDOWN));
+
+ mutex_unlock(&phydev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(genphy_suspend);
+
+int genphy_resume(struct phy_device *phydev)
+{
+ int value;
+
+ mutex_lock(&phydev->lock);
+
+ value = phy_read(phydev, MII_BMCR);
+ phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN));
+
+ mutex_unlock(&phydev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(genphy_resume);
/**
* phy_probe - probe and init a PHY device
@@ -855,7 +866,6 @@ int phy_driver_register(struct phy_driver *new_driver)
{
int retval;
- memset(&new_driver->driver, 0, sizeof(new_driver->driver));
new_driver->driver.name = new_driver->name;
new_driver->driver.bus = &mdio_bus_type;
new_driver->driver.probe = phy_probe;
@@ -890,6 +900,8 @@ static struct phy_driver genphy_driver = {
.features = 0,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
.driver = {.owner= THIS_MODULE, },
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 73baa7a3bb0e..c05d38d46350 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -126,6 +126,27 @@ static struct phy_driver lan8700_driver = {
.driver = { .owner = THIS_MODULE, }
};
+static struct phy_driver lan911x_int_driver = {
+ .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
+ .phy_id_mask = 0xfffffff0,
+ .name = "SMSC LAN911x Internal PHY",
+
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
+ | SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+
+ /* basic functions */
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .config_init = smsc_phy_config_init,
+
+ /* IRQ related */
+ .ack_interrupt = smsc_phy_ack_interrupt,
+ .config_intr = smsc_phy_config_intr,
+
+ .driver = { .owner = THIS_MODULE, }
+};
+
static int __init smsc_init(void)
{
int ret;
@@ -142,8 +163,14 @@ static int __init smsc_init(void)
if (ret)
goto err3;
+ ret = phy_driver_register (&lan911x_int_driver);
+ if (ret)
+ goto err4;
+
return 0;
+err4:
+ phy_driver_unregister (&lan8700_driver);
err3:
phy_driver_unregister (&lan8187_driver);
err2:
@@ -154,6 +181,7 @@ err1:
static void __exit smsc_exit(void)
{
+ phy_driver_unregister (&lan911x_int_driver);
phy_driver_unregister (&lan8700_driver);
phy_driver_unregister (&lan8187_driver);
phy_driver_unregister (&lan83c185_driver);
diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c
new file mode 100644
index 000000000000..6bdb0d53aaf9
--- /dev/null
+++ b/drivers/net/phy/ste10Xp.c
@@ -0,0 +1,137 @@
+/*
+ * drivers/net/phy/ste10Xp.c
+ *
+ * Driver for STMicroelectronics STe10Xp PHYs
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ *
+ * Copyright (c) 2008 STMicroelectronics Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+
+#define MII_XCIIS 0x11 /* Configuration Info IRQ & Status Reg */
+#define MII_XIE 0x12 /* Interrupt Enable Register */
+#define MII_XIE_DEFAULT_MASK 0x0070 /* ANE complete, Remote Fault, Link Down */
+
+#define STE101P_PHY_ID 0x00061c50
+#define STE100P_PHY_ID 0x1c040011
+
+static int ste10Xp_config_init(struct phy_device *phydev)
+{
+ int value, err;
+
+ /* Software Reset PHY */
+ value = phy_read(phydev, MII_BMCR);
+ if (value < 0)
+ return value;
+
+ value |= BMCR_RESET;
+ err = phy_write(phydev, MII_BMCR, value);
+ if (err < 0)
+ return err;
+
+ do {
+ value = phy_read(phydev, MII_BMCR);
+ } while (value & BMCR_RESET);
+
+ return 0;
+}
+
+static int ste10Xp_config_intr(struct phy_device *phydev)
+{
+ int err, value;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ /* Enable all STe101P interrupts (PR12) */
+ err = phy_write(phydev, MII_XIE, MII_XIE_DEFAULT_MASK);
+ /* clear any pending interrupts */
+ if (err == 0) {
+ value = phy_read(phydev, MII_XCIIS);
+ if (value < 0)
+ err = value;
+ }
+ } else
+ err = phy_write(phydev, MII_XIE, 0);
+
+ return err;
+}
+
+static int ste10Xp_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_XCIIS);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static struct phy_driver ste101p_pdriver = {
+ .phy_id = STE101P_PHY_ID,
+ .phy_id_mask = 0xfffffff0,
+ .name = "STe101p",
+ .features = PHY_BASIC_FEATURES | SUPPORTED_Pause,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = ste10Xp_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = ste10Xp_ack_interrupt,
+ .config_intr = ste10Xp_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .driver = {.owner = THIS_MODULE,}
+};
+
+static struct phy_driver ste100p_pdriver = {
+ .phy_id = STE100P_PHY_ID,
+ .phy_id_mask = 0xffffffff,
+ .name = "STe100p",
+ .features = PHY_BASIC_FEATURES | SUPPORTED_Pause,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = ste10Xp_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = ste10Xp_ack_interrupt,
+ .config_intr = ste10Xp_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .driver = {.owner = THIS_MODULE,}
+};
+
+static int __init ste10Xp_init(void)
+{
+ int retval;
+
+ retval = phy_driver_register(&ste100p_pdriver);
+ if (retval < 0)
+ return retval;
+ return phy_driver_register(&ste101p_pdriver);
+}
+
+static void __exit ste10Xp_exit(void)
+{
+ phy_driver_unregister(&ste100p_pdriver);
+ phy_driver_unregister(&ste101p_pdriver);
+}
+
+module_init(ste10Xp_init);
+module_exit(ste10Xp_exit);
+
+MODULE_DESCRIPTION("STMicroelectronics STe10Xp PHY driver");
+MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 1e965427b0e9..0c46d603b8fe 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -229,7 +229,7 @@ static inline void enable_parport_interrupts (struct net_device *dev)
if (dev->irq != -1)
{
struct parport *port =
- ((struct net_local *)dev->priv)->pardev->port;
+ ((struct net_local *)netdev_priv(dev))->pardev->port;
port->ops->enable_irq (port);
}
}
@@ -239,7 +239,7 @@ static inline void disable_parport_interrupts (struct net_device *dev)
if (dev->irq != -1)
{
struct parport *port =
- ((struct net_local *)dev->priv)->pardev->port;
+ ((struct net_local *)netdev_priv(dev))->pardev->port;
port->ops->disable_irq (port);
}
}
@@ -247,7 +247,7 @@ static inline void disable_parport_interrupts (struct net_device *dev)
static inline void write_data (struct net_device *dev, unsigned char data)
{
struct parport *port =
- ((struct net_local *)dev->priv)->pardev->port;
+ ((struct net_local *)netdev_priv(dev))->pardev->port;
port->ops->write_data (port, data);
}
@@ -255,7 +255,7 @@ static inline void write_data (struct net_device *dev, unsigned char data)
static inline unsigned char read_status (struct net_device *dev)
{
struct parport *port =
- ((struct net_local *)dev->priv)->pardev->port;
+ ((struct net_local *)netdev_priv(dev))->pardev->port;
return port->ops->read_status (port);
}
@@ -638,14 +638,14 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
case PLIP_PK_DATA:
lbuf = rcv->skb->data;
- do
+ do {
if (plip_receive(nibble_timeout, dev,
&rcv->nibble, &lbuf[rcv->byte]))
return TIMEOUT;
- while (++rcv->byte < rcv->length.h);
- do
+ } while (++rcv->byte < rcv->length.h);
+ do {
rcv->checksum += lbuf[--rcv->byte];
- while (rcv->byte);
+ } while (rcv->byte);
rcv->state = PLIP_PK_CHECKSUM;
case PLIP_PK_CHECKSUM:
@@ -664,7 +664,6 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
/* Inform the upper layer for the arrival of a packet. */
rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
netif_rx_ni(rcv->skb);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += rcv->length.h;
dev->stats.rx_packets++;
rcv->skb = NULL;
@@ -817,14 +816,14 @@ plip_send_packet(struct net_device *dev, struct net_local *nl,
snd->checksum = 0;
case PLIP_PK_DATA:
- do
+ do {
if (plip_send(nibble_timeout, dev,
&snd->nibble, lbuf[snd->byte]))
return TIMEOUT;
- while (++snd->byte < snd->length.h);
- do
+ } while (++snd->byte < snd->length.h);
+ do {
snd->checksum += lbuf[--snd->byte];
- while (snd->byte);
+ } while (snd->byte);
snd->state = PLIP_PK_CHECKSUM;
case PLIP_PK_CHECKSUM:
@@ -1018,8 +1017,8 @@ plip_hard_header(struct sk_buff *skb, struct net_device *dev,
return ret;
}
-int plip_hard_header_cache(const struct neighbour *neigh,
- struct hh_cache *hh)
+static int plip_hard_header_cache(const struct neighbour *neigh,
+ struct hh_cache *hh)
{
int ret;
@@ -1397,9 +1396,3 @@ static int __init plip_init (void)
module_init(plip_init);
module_exit(plip_cleanup_module);
MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -c plip.c"
- * End:
- */
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 451bdb57d6fc..6567fabd2e13 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -293,9 +293,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
err = -EFAULT;
switch (cmd) {
case PPPIOCGCHAN:
- err = -ENXIO;
- if (!ap)
- break;
err = -EFAULT;
if (put_user(ppp_channel_index(&ap->chan), p))
break;
@@ -303,9 +300,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
break;
case PPPIOCGUNIT:
- err = -ENXIO;
- if (!ap)
- break;
err = -EFAULT;
if (put_user(ppp_unit_number(&ap->chan), p))
break;
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 7e857e938adb..06b448285eb5 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -27,6 +27,7 @@
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/list.h>
+#include <linux/idr.h>
#include <linux/netdevice.h>
#include <linux/poll.h>
#include <linux/ppp_defs.h>
@@ -116,6 +117,7 @@ struct ppp {
unsigned long last_xmit; /* jiffies when last pkt sent 9c */
unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
struct net_device *dev; /* network interface device a4 */
+ int closing; /* is device closing down? a8 */
#ifdef CONFIG_PPP_MULTILINK
int nxchan; /* next channel to send something on */
u32 nxseq; /* next sequence number to send */
@@ -172,35 +174,13 @@ struct channel {
*/
/*
- * A cardmap represents a mapping from unsigned integers to pointers,
- * and provides a fast "find lowest unused number" operation.
- * It uses a broad (32-way) tree with a bitmap at each level.
- * It is designed to be space-efficient for small numbers of entries
- * and time-efficient for large numbers of entries.
- */
-#define CARDMAP_ORDER 5
-#define CARDMAP_WIDTH (1U << CARDMAP_ORDER)
-#define CARDMAP_MASK (CARDMAP_WIDTH - 1)
-
-struct cardmap {
- int shift;
- unsigned long inuse;
- struct cardmap *parent;
- void *ptr[CARDMAP_WIDTH];
-};
-static void *cardmap_get(struct cardmap *map, unsigned int nr);
-static int cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);
-static unsigned int cardmap_find_first_free(struct cardmap *map);
-static void cardmap_destroy(struct cardmap **map);
-
-/*
* all_ppp_mutex protects the all_ppp_units mapping.
* It also ensures that finding a ppp unit in the all_ppp_units map
* and updating its file.refcnt field is atomic.
*/
static DEFINE_MUTEX(all_ppp_mutex);
-static struct cardmap *all_ppp_units;
static atomic_t ppp_unit_count = ATOMIC_INIT(0);
+static DEFINE_IDR(ppp_units_idr);
/*
* all_channels_lock protects all_channels and last_channel_index,
@@ -269,6 +249,9 @@ static struct channel *ppp_find_channel(int unit);
static int ppp_connect_channel(struct channel *pch, int unit);
static int ppp_disconnect_channel(struct channel *pch);
static void ppp_destroy_channel(struct channel *pch);
+static int unit_get(struct idr *p, void *ptr);
+static void unit_put(struct idr *p, int n);
+static void *unit_find(struct idr *p, int n);
static struct class *ppp_class;
@@ -886,7 +869,7 @@ out_chrdev:
static int
ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ppp *ppp = (struct ppp *) dev->priv;
+ struct ppp *ppp = netdev_priv(dev);
int npi, proto;
unsigned char *pp;
@@ -931,7 +914,7 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
static int
ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct ppp *ppp = dev->priv;
+ struct ppp *ppp = netdev_priv(dev);
int err = -EFAULT;
void __user *addr = (void __user *) ifr->ifr_ifru.ifru_data;
struct ppp_stats stats;
@@ -971,8 +954,14 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return err;
}
+static const struct net_device_ops ppp_netdev_ops = {
+ .ndo_start_xmit = ppp_start_xmit,
+ .ndo_do_ioctl = ppp_net_ioctl,
+};
+
static void ppp_setup(struct net_device *dev)
{
+ dev->netdev_ops = &ppp_netdev_ops;
dev->hard_header_len = PPP_HDRLEN;
dev->mtu = PPP_MTU;
dev->addr_len = 0;
@@ -995,7 +984,7 @@ ppp_xmit_process(struct ppp *ppp)
struct sk_buff *skb;
ppp_xmit_lock(ppp);
- if (ppp->dev) {
+ if (!ppp->closing) {
ppp_push(ppp);
while (!ppp->xmit_pending
&& (skb = skb_dequeue(&ppp->file.xq)))
@@ -1463,8 +1452,7 @@ static inline void
ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{
ppp_recv_lock(ppp);
- /* ppp->dev == 0 means interface is closing down */
- if (ppp->dev)
+ if (!ppp->closing)
ppp_receive_frame(ppp, skb, pch);
else
kfree_skb(skb);
@@ -1684,7 +1672,6 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
skb->protocol = htons(npindex_to_ethertype[npi]);
skb_reset_mac_header(skb);
netif_rx(skb);
- ppp->dev->last_rx = jiffies;
}
}
return;
@@ -2414,13 +2401,12 @@ ppp_create_interface(int unit, int *retp)
int ret = -ENOMEM;
int i;
- ppp = kzalloc(sizeof(struct ppp), GFP_KERNEL);
- if (!ppp)
- goto out;
- dev = alloc_netdev(0, "", ppp_setup);
+ dev = alloc_netdev(sizeof(struct ppp), "", ppp_setup);
if (!dev)
goto out1;
+ ppp = netdev_priv(dev);
+ ppp->dev = dev;
ppp->mru = PPP_MRU;
init_ppp_file(&ppp->file, INTERFACE);
ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */
@@ -2433,18 +2419,25 @@ ppp_create_interface(int unit, int *retp)
ppp->minseq = -1;
skb_queue_head_init(&ppp->mrq);
#endif /* CONFIG_PPP_MULTILINK */
- ppp->dev = dev;
- dev->priv = ppp;
-
- dev->hard_start_xmit = ppp_start_xmit;
- dev->do_ioctl = ppp_net_ioctl;
ret = -EEXIST;
mutex_lock(&all_ppp_mutex);
- if (unit < 0)
- unit = cardmap_find_first_free(all_ppp_units);
- else if (cardmap_get(all_ppp_units, unit) != NULL)
- goto out2; /* unit already exists */
+
+ if (unit < 0) {
+ unit = unit_get(&ppp_units_idr, ppp);
+ if (unit < 0) {
+ *retp = unit;
+ goto out2;
+ }
+ } else {
+ if (unit_find(&ppp_units_idr, unit))
+ goto out2; /* unit already exists */
+ else {
+ /* darn, someone is cheating us? */
+ *retp = -EINVAL;
+ goto out2;
+ }
+ }
/* Initialize the new ppp unit */
ppp->file.index = unit;
@@ -2452,29 +2445,22 @@ ppp_create_interface(int unit, int *retp)
ret = register_netdev(dev);
if (ret != 0) {
+ unit_put(&ppp_units_idr, unit);
printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
dev->name, ret);
goto out2;
}
atomic_inc(&ppp_unit_count);
- ret = cardmap_set(&all_ppp_units, unit, ppp);
- if (ret != 0)
- goto out3;
-
mutex_unlock(&all_ppp_mutex);
+
*retp = 0;
return ppp;
-out3:
- atomic_dec(&ppp_unit_count);
- unregister_netdev(dev);
out2:
mutex_unlock(&all_ppp_mutex);
free_netdev(dev);
out1:
- kfree(ppp);
-out:
*retp = ret;
return NULL;
}
@@ -2498,19 +2484,17 @@ init_ppp_file(struct ppp_file *pf, int kind)
*/
static void ppp_shutdown_interface(struct ppp *ppp)
{
- struct net_device *dev;
-
mutex_lock(&all_ppp_mutex);
- ppp_lock(ppp);
- dev = ppp->dev;
- ppp->dev = NULL;
- ppp_unlock(ppp);
/* This will call dev_close() for us. */
- if (dev) {
- unregister_netdev(dev);
- free_netdev(dev);
- }
- cardmap_set(&all_ppp_units, ppp->file.index, NULL);
+ ppp_lock(ppp);
+ if (!ppp->closing) {
+ ppp->closing = 1;
+ ppp_unlock(ppp);
+ unregister_netdev(ppp->dev);
+ } else
+ ppp_unlock(ppp);
+
+ unit_put(&ppp_units_idr, ppp->file.index);
ppp->file.dead = 1;
ppp->owner = NULL;
wake_up_interruptible(&ppp->file.rwait);
@@ -2554,7 +2538,7 @@ static void ppp_destroy_interface(struct ppp *ppp)
if (ppp->xmit_pending)
kfree_skb(ppp->xmit_pending);
- kfree(ppp);
+ free_netdev(ppp->dev);
}
/*
@@ -2564,7 +2548,7 @@ static void ppp_destroy_interface(struct ppp *ppp)
static struct ppp *
ppp_find_unit(int unit)
{
- return cardmap_get(all_ppp_units, unit);
+ return unit_find(&ppp_units_idr, unit);
}
/*
@@ -2616,7 +2600,7 @@ ppp_connect_channel(struct channel *pch, int unit)
if (pch->file.hdrlen > ppp->file.hdrlen)
ppp->file.hdrlen = pch->file.hdrlen;
hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
- if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+ if (hdrlen > ppp->dev->hard_header_len)
ppp->dev->hard_header_len = hdrlen;
list_add_tail(&pch->clist, &ppp->channels);
++ppp->n_channels;
@@ -2682,123 +2666,45 @@ static void __exit ppp_cleanup(void)
/* should never happen */
if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
printk(KERN_ERR "PPP: removing module but units remain!\n");
- cardmap_destroy(&all_ppp_units);
unregister_chrdev(PPP_MAJOR, "ppp");
device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
class_destroy(ppp_class);
+ idr_destroy(&ppp_units_idr);
}
/*
- * Cardmap implementation.
+ * Units handling. Caller must protect concurrent access
+ * by holding all_ppp_mutex
*/
-static void *cardmap_get(struct cardmap *map, unsigned int nr)
+
+/* get new free unit number and associate pointer with it */
+static int unit_get(struct idr *p, void *ptr)
{
- struct cardmap *p;
- int i;
+ int unit, err;
- for (p = map; p != NULL; ) {
- if ((i = nr >> p->shift) >= CARDMAP_WIDTH)
- return NULL;
- if (p->shift == 0)
- return p->ptr[i];
- nr &= ~(CARDMAP_MASK << p->shift);
- p = p->ptr[i];
+again:
+ if (idr_pre_get(p, GFP_KERNEL) == 0) {
+ printk(KERN_ERR "Out of memory expanding drawable idr\n");
+ return -ENOMEM;
}
- return NULL;
-}
-static int cardmap_set(struct cardmap **pmap, unsigned int nr, void *ptr)
-{
- struct cardmap *p;
- int i;
+ err = idr_get_new_above(p, ptr, 0, &unit);
+ if (err == -EAGAIN)
+ goto again;
- p = *pmap;
- if (p == NULL || (nr >> p->shift) >= CARDMAP_WIDTH) {
- do {
- /* need a new top level */
- struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
- if (!np)
- goto enomem;
- np->ptr[0] = p;
- if (p != NULL) {
- np->shift = p->shift + CARDMAP_ORDER;
- p->parent = np;
- } else
- np->shift = 0;
- p = np;
- } while ((nr >> p->shift) >= CARDMAP_WIDTH);
- *pmap = p;
- }
- while (p->shift > 0) {
- i = (nr >> p->shift) & CARDMAP_MASK;
- if (p->ptr[i] == NULL) {
- struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
- if (!np)
- goto enomem;
- np->shift = p->shift - CARDMAP_ORDER;
- np->parent = p;
- p->ptr[i] = np;
- }
- if (ptr == NULL)
- clear_bit(i, &p->inuse);
- p = p->ptr[i];
- }
- i = nr & CARDMAP_MASK;
- p->ptr[i] = ptr;
- if (ptr != NULL)
- set_bit(i, &p->inuse);
- else
- clear_bit(i, &p->inuse);
- return 0;
- enomem:
- return -ENOMEM;
+ return unit;
}
-static unsigned int cardmap_find_first_free(struct cardmap *map)
+/* put unit number back to a pool */
+static void unit_put(struct idr *p, int n)
{
- struct cardmap *p;
- unsigned int nr = 0;
- int i;
-
- if ((p = map) == NULL)
- return 0;
- for (;;) {
- i = find_first_zero_bit(&p->inuse, CARDMAP_WIDTH);
- if (i >= CARDMAP_WIDTH) {
- if (p->parent == NULL)
- return CARDMAP_WIDTH << p->shift;
- p = p->parent;
- i = (nr >> p->shift) & CARDMAP_MASK;
- set_bit(i, &p->inuse);
- continue;
- }
- nr = (nr & (~CARDMAP_MASK << p->shift)) | (i << p->shift);
- if (p->shift == 0 || p->ptr[i] == NULL)
- return nr;
- p = p->ptr[i];
- }
+ idr_remove(p, n);
}
-static void cardmap_destroy(struct cardmap **pmap)
+/* get pointer associated with the number */
+static void *unit_find(struct idr *p, int n)
{
- struct cardmap *p, *np;
- int i;
-
- for (p = *pmap; p != NULL; p = np) {
- if (p->shift != 0) {
- for (i = 0; i < CARDMAP_WIDTH; ++i)
- if (p->ptr[i] != NULL)
- break;
- if (i < CARDMAP_WIDTH) {
- np = p->ptr[i];
- p->ptr[i] = NULL;
- continue;
- }
- }
- np = p->parent;
- kfree(p);
- }
- *pmap = NULL;
+ return idr_find(p, n);
}
/* Module/initialization stuff */
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index 801d8f99d471..1e892b7b1f8c 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -333,9 +333,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
err = -EFAULT;
switch (cmd) {
case PPPIOCGCHAN:
- err = -ENXIO;
- if (!ap)
- break;
err = -EFAULT;
if (put_user(ppp_channel_index(&ap->chan), p))
break;
@@ -343,9 +340,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
break;
case PPPIOCGUNIT:
- err = -ENXIO;
- if (!ap)
- break;
err = -EFAULT;
if (put_user(ppp_unit_number(&ap->chan), p))
break;
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index b646e92134dc..c22b30533a14 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -958,7 +958,6 @@ static int pppoe_seq_show(struct seq_file *seq, void *v)
{
struct pppox_sock *po;
char *dev_name;
- DECLARE_MAC_BUF(mac);
if (v == SEQ_START_TOKEN) {
seq_puts(seq, "Id Address Device\n");
@@ -968,8 +967,8 @@ static int pppoe_seq_show(struct seq_file *seq, void *v)
po = v;
dev_name = po->pppoe_pa.dev;
- seq_printf(seq, "%08X %s %8s\n",
- po->pppoe_pa.sid, print_mac(mac, po->pppoe_pa.remote), dev_name);
+ seq_printf(seq, "%08X %pM %8s\n",
+ po->pppoe_pa.sid, po->pppoe_pa.remote, dev_name);
out:
return 0;
}
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c
index e98d9773158d..f1a946785c6a 100644
--- a/drivers/net/pppol2tp.c
+++ b/drivers/net/pppol2tp.c
@@ -489,6 +489,30 @@ out:
spin_unlock_bh(&session->reorder_q.lock);
}
+static inline int pppol2tp_verify_udp_checksum(struct sock *sk,
+ struct sk_buff *skb)
+{
+ struct udphdr *uh = udp_hdr(skb);
+ u16 ulen = ntohs(uh->len);
+ struct inet_sock *inet;
+ __wsum psum;
+
+ if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check)
+ return 0;
+
+ inet = inet_sk(sk);
+ psum = csum_tcpudp_nofold(inet->saddr, inet->daddr, ulen,
+ IPPROTO_UDP, 0);
+
+ if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
+ !csum_fold(csum_add(psum, skb->csum)))
+ return 0;
+
+ skb->csum = psum;
+
+ return __skb_checksum_complete(skb);
+}
+
/* Internal receive frame. Do the real work of receiving an L2TP data frame
* here. The skb is not on a list when we get here.
* Returns 0 if the packet was a data packet and was successfully passed on.
@@ -509,6 +533,9 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
if (tunnel == NULL)
goto no_tunnel;
+ if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb))
+ goto discard_bad_csum;
+
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
@@ -725,6 +752,14 @@ discard:
return 0;
+discard_bad_csum:
+ LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
+ UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0);
+ tunnel->stats.rx_errors++;
+ kfree_skb(skb);
+
+ return 0;
+
error:
/* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr));
@@ -851,7 +886,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
static const unsigned char ppph[2] = { 0xff, 0x03 };
struct sock *sk = sock->sk;
struct inet_sock *inet;
- __wsum csum = 0;
+ __wsum csum;
struct sk_buff *skb;
int error;
int hdr_len;
@@ -859,6 +894,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
struct pppol2tp_tunnel *tunnel;
struct udphdr *uh;
unsigned int len;
+ struct sock *sk_tun;
+ u16 udp_len;
error = -ENOTCONN;
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
@@ -870,7 +907,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
if (session == NULL)
goto error;
- tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
+ sk_tun = session->tunnel_sock;
+ tunnel = pppol2tp_sock_to_tunnel(sk_tun);
if (tunnel == NULL)
goto error_put_sess;
@@ -893,11 +931,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb_reset_transport_header(skb);
/* Build UDP header */
- inet = inet_sk(session->tunnel_sock);
+ inet = inet_sk(sk_tun);
+ udp_len = hdr_len + sizeof(ppph) + total_len;
uh = (struct udphdr *) skb->data;
uh->source = inet->sport;
uh->dest = inet->dport;
- uh->len = htons(hdr_len + sizeof(ppph) + total_len);
+ uh->len = htons(udp_len);
uh->check = 0;
skb_put(skb, sizeof(struct udphdr));
@@ -919,8 +958,22 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb_put(skb, total_len);
/* Calculate UDP checksum if configured to do so */
- if (session->tunnel_sock->sk_no_check != UDP_CSUM_NOXMIT)
- csum = udp_csum_outgoing(sk, skb);
+ if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
+ skb->ip_summed = CHECKSUM_NONE;
+ else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ csum = skb_checksum(skb, 0, udp_len, 0);
+ uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ udp_len, IPPROTO_UDP, csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ udp_len, IPPROTO_UDP, 0);
+ }
/* Debug */
if (session->send_seq)
@@ -1008,13 +1061,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
struct sock *sk = (struct sock *) chan->private;
struct sock *sk_tun;
int hdr_len;
+ u16 udp_len;
struct pppol2tp_session *session;
struct pppol2tp_tunnel *tunnel;
int rc;
int headroom;
int data_len = skb->len;
struct inet_sock *inet;
- __wsum csum = 0;
+ __wsum csum;
struct udphdr *uh;
unsigned int len;
int old_headroom;
@@ -1060,6 +1114,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
/* Setup L2TP header */
pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len));
+ udp_len = sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len;
+
/* Setup UDP header */
inet = inet_sk(sk_tun);
__skb_push(skb, sizeof(*uh));
@@ -1067,13 +1123,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
uh = udp_hdr(skb);
uh->source = inet->sport;
uh->dest = inet->dport;
- uh->len = htons(sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len);
+ uh->len = htons(udp_len);
uh->check = 0;
- /* *BROKEN* Calculate UDP checksum if configured to do so */
- if (sk_tun->sk_no_check != UDP_CSUM_NOXMIT)
- csum = udp_csum_outgoing(sk_tun, skb);
-
/* Debug */
if (session->send_seq)
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
@@ -1108,6 +1160,24 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->dst = dst_clone(__sk_dst_get(sk_tun));
pppol2tp_skb_set_owner_w(skb, sk_tun);
+ /* Calculate UDP checksum if configured to do so */
+ if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
+ skb->ip_summed = CHECKSUM_NONE;
+ else if (!(skb->dst->dev->features & NETIF_F_V4_CSUM)) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ csum = skb_checksum(skb, 0, udp_len, 0);
+ uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ udp_len, IPPROTO_UDP, csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ udp_len, IPPROTO_UDP, 0);
+ }
+
/* Queue the packet to IP for output */
len = skb->len;
rc = ip_queue_xmit(skb, 1);
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index 2eb54fd7bed5..4b564eda5bd9 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -1443,7 +1443,6 @@ int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card)
{
int status;
u64 v1, v2;
- DECLARE_MAC_BUF(mac);
netdev->features = NETIF_F_IP_CSUM;
@@ -1474,9 +1473,8 @@ int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card)
__func__, netdev->name, status);
return status;
}
- dev_info(ctodev(card), "%s: MAC addr %s\n",
- netdev->name,
- print_mac(mac, netdev->dev_addr));
+ dev_info(ctodev(card), "%s: MAC addr %pM\n",
+ netdev->name, netdev->dev_addr);
return 0;
}
diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c
index a834b52a6a2c..ec2314246682 100644
--- a/drivers/net/ps3_gelic_wireless.c
+++ b/drivers/net/ps3_gelic_wireless.c
@@ -30,10 +30,11 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <linux/if_arp.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <net/iw_handler.h>
-#include <net/ieee80211.h>
#include <linux/dma-mapping.h>
#include <net/checksum.h>
@@ -449,9 +450,9 @@ static size_t gelic_wl_synthesize_ie(u8 *buf,
/* element id */
if (rsn)
- *buf++ = MFIE_TYPE_RSN;
+ *buf++ = WLAN_EID_RSN;
else
- *buf++ = MFIE_TYPE_GENERIC;
+ *buf++ = WLAN_EID_GENERIC;
/* length filed; set later */
buf++;
@@ -539,7 +540,7 @@ static void gelic_wl_parse_ie(u8 *data, size_t len,
break;
switch (item_id) {
- case MFIE_TYPE_GENERIC:
+ case WLAN_EID_GENERIC:
if ((OUI_LEN + 1 <= item_len) &&
!memcmp(pos, wpa_oui, OUI_LEN) &&
pos[OUI_LEN] == 0x01) {
@@ -547,7 +548,7 @@ static void gelic_wl_parse_ie(u8 *data, size_t len,
ie_info->wpa.len = item_len + 2;
}
break;
- case MFIE_TYPE_RSN:
+ case WLAN_EID_RSN:
ie_info->rsn.data = pos - 2;
/* length includes the header */
ie_info->rsn.len = item_len + 2;
@@ -581,7 +582,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev,
char *tmp;
u8 rate;
unsigned int i, j, len;
- u8 buf[MAX_WPA_IE_LEN];
+ u8 buf[64]; /* arbitrary size large enough */
pr_debug("%s: <-\n", __func__);
@@ -763,7 +764,6 @@ static void scan_list_dump(struct gelic_wl_info *wl)
{
struct gelic_wl_scan_info *scan_info;
int i;
- DECLARE_MAC_BUF(mac);
i = 0;
list_for_each_entry(scan_info, &wl->network_list, list) {
@@ -775,8 +775,7 @@ static void scan_list_dump(struct gelic_wl_info *wl)
scan_info->rate_len, scan_info->rate_ext_len,
scan_info->essid_len);
/* -- */
- pr_debug("bssid=%s\n",
- print_mac(mac, &scan_info->hwinfo->bssid[2]));
+ pr_debug("bssid=%pM\n", &scan_info->hwinfo->bssid[2]);
pr_debug("essid=%s\n", scan_info->hwinfo->essid);
}
}
@@ -1167,11 +1166,7 @@ static int gelic_wl_set_ap(struct net_device *netdev,
ETH_ALEN);
set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
- pr_debug("%s: bss=%02x:%02x:%02x:%02x:%02x:%02x\n",
- __func__,
- wl->bssid[0], wl->bssid[1],
- wl->bssid[2], wl->bssid[3],
- wl->bssid[4], wl->bssid[5]);
+ pr_debug("%s: bss=%pM\n", __func__, wl->bssid);
} else {
pr_debug("%s: clear bssid\n", __func__);
clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
@@ -1632,7 +1627,6 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
unsigned long this_time = jiffies;
unsigned int data_len, i, found, r;
void *buf;
- DECLARE_MAC_BUF(mac);
pr_debug("%s:start\n", __func__);
mutex_lock(&wl->scan_lock);
@@ -1684,9 +1678,9 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
scan_info_size < data_len;
i++, scan_info_size += be16_to_cpu(scan_info->size),
scan_info = (void *)scan_info + be16_to_cpu(scan_info->size)) {
- pr_debug("%s:size=%d bssid=%s scan_info=%p\n", __func__,
+ pr_debug("%s:size=%d bssid=%pM scan_info=%p\n", __func__,
be16_to_cpu(scan_info->size),
- print_mac(mac, &scan_info->bssid[2]), scan_info);
+ &scan_info->bssid[2], scan_info);
/*
* The wireless firmware may return invalid channel 0 and/or
@@ -1741,14 +1735,14 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
target->essid_len = strnlen(scan_info->essid,
sizeof(scan_info->essid));
target->rate_len = 0;
- for (r = 0; r < MAX_RATES_LENGTH; r++)
+ for (r = 0; r < 12; r++)
if (scan_info->rate[r])
target->rate_len++;
if (8 < target->rate_len)
pr_info("%s: AP returns %d rates\n", __func__,
target->rate_len);
target->rate_ext_len = 0;
- for (r = 0; r < MAX_RATES_EX_LENGTH; r++)
+ for (r = 0; r < 16; r++)
if (scan_info->ext_rate[r])
target->rate_ext_len++;
list_move_tail(&target->list, &wl->network_list);
@@ -1787,7 +1781,6 @@ struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
struct gelic_wl_scan_info *best_bss;
int weight, best_weight;
u16 security;
- DECLARE_MAC_BUF(mac);
pr_debug("%s: <-\n", __func__);
@@ -1857,8 +1850,8 @@ struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
#ifdef DEBUG
pr_debug("%s: -> bss=%p\n", __func__, best_bss);
if (best_bss) {
- pr_debug("%s:addr=%s\n", __func__,
- print_mac(mac, &best_bss->hwinfo->bssid[2]));
+ pr_debug("%s:addr=%pM\n", __func__,
+ &best_bss->hwinfo->bssid[2]);
}
#endif
return best_bss;
diff --git a/drivers/net/ps3_gelic_wireless.h b/drivers/net/ps3_gelic_wireless.h
index 5339e0078d18..5b631c6c9775 100644
--- a/drivers/net/ps3_gelic_wireless.h
+++ b/drivers/net/ps3_gelic_wireless.h
@@ -164,8 +164,8 @@ struct gelic_eurus_scan_info {
__be16 security;
u8 bssid[8]; /* last ETH_ALEN are valid. bssid[0],[1] are unused */
u8 essid[32]; /* IW_ESSID_MAX_SIZE */
- u8 rate[16]; /* first MAX_RATES_LENGTH(12) are valid */
- u8 ext_rate[16]; /* first MAX_RATES_EX_LENGTH(16) are valid */
+ u8 rate[16]; /* first 12 are valid */
+ u8 ext_rate[16]; /* first 16 are valid */
__be32 reserved1;
__be32 reserved2;
__be32 reserved3;
diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index 508452c02151..189ec29ac7a4 100644
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -2127,7 +2127,6 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev,
skb->protocol = eth_type_trans(skb, qdev->ndev);
netif_receive_skb(skb);
- qdev->ndev->last_rx = jiffies;
lrg_buf_cb2->skb = NULL;
if (qdev->device_id == QL3022_DEVICE_ID)
@@ -2201,7 +2200,6 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
netif_receive_skb(skb2);
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += length;
- ndev->last_rx = jiffies;
lrg_buf_cb2->skb = NULL;
if (qdev->device_id == QL3022_DEVICE_ID)
@@ -2286,7 +2284,6 @@ static int ql_tx_rx_clean(struct ql3_adapter *qdev,
static int ql_poll(struct napi_struct *napi, int budget)
{
struct ql3_adapter *qdev = container_of(napi, struct ql3_adapter, napi);
- struct net_device *ndev = qdev->ndev;
int rx_cleaned = 0, tx_cleaned = 0;
unsigned long hw_flags;
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
@@ -2295,7 +2292,7 @@ static int ql_poll(struct napi_struct *napi, int budget)
if (tx_cleaned + rx_cleaned != budget) {
spin_lock_irqsave(&qdev->hw_lock, hw_flags);
- __netif_rx_complete(ndev, napi);
+ __netif_rx_complete(napi);
ql_update_small_bufq_prod_index(qdev);
ql_update_lrg_bufq_prod_index(qdev);
writel(qdev->rsp_consumer_index,
@@ -2354,8 +2351,8 @@ static irqreturn_t ql3xxx_isr(int irq, void *dev_id)
spin_unlock(&qdev->adapter_lock);
} else if (value & ISP_IMR_DISABLE_CMPL_INT) {
ql_disable_interrupts(qdev);
- if (likely(netif_rx_schedule_prep(ndev, &qdev->napi))) {
- __netif_rx_schedule(ndev, &qdev->napi);
+ if (likely(netif_rx_schedule_prep(&qdev->napi))) {
+ __netif_rx_schedule(&qdev->napi);
}
} else {
return IRQ_NONE;
@@ -3520,7 +3517,6 @@ static void ql_display_dev_info(struct net_device *ndev)
{
struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev);
struct pci_dev *pdev = qdev->pdev;
- DECLARE_MAC_BUF(mac);
printk(KERN_INFO PFX
"\n%s Adapter %d RevisionID %d found %s on PCI slot %d.\n",
@@ -3546,8 +3542,8 @@ static void ql_display_dev_info(struct net_device *ndev)
if (netif_msg_probe(qdev))
printk(KERN_INFO PFX
- "%s: MAC address %s\n",
- ndev->name, print_mac(mac, ndev->dev_addr));
+ "%s: MAC address %pM\n",
+ ndev->name, ndev->dev_addr);
}
static int ql_adapter_down(struct ql3_adapter *qdev, int do_reset)
@@ -3903,13 +3899,24 @@ static void ql3xxx_timer(unsigned long ptr)
queue_delayed_work(qdev->workqueue, &qdev->link_state_work, 0);
}
+static const struct net_device_ops ql3xxx_netdev_ops = {
+ .ndo_open = ql3xxx_open,
+ .ndo_start_xmit = ql3xxx_send,
+ .ndo_stop = ql3xxx_close,
+ .ndo_set_multicast_list = NULL, /* not allowed on NIC side */
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = ql3xxx_set_mac_address,
+ .ndo_tx_timeout = ql3xxx_tx_timeout,
+};
+
static int __devinit ql3xxx_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_entry)
{
struct net_device *ndev = NULL;
struct ql3_adapter *qdev = NULL;
static int cards_found = 0;
- int pci_using_dac, err;
+ int uninitialized_var(pci_using_dac), err;
err = pci_enable_device(pdev);
if (err) {
@@ -3969,9 +3976,7 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev,
if (qdev->device_id == QL3032_DEVICE_ID)
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
- qdev->mem_map_registers =
- ioremap_nocache(pci_resource_start(pdev, 1),
- pci_resource_len(qdev->pdev, 1));
+ qdev->mem_map_registers = pci_ioremap_bar(pdev, 1);
if (!qdev->mem_map_registers) {
printk(KERN_ERR PFX "%s: cannot map device registers\n",
pci_name(pdev));
@@ -3983,17 +3988,8 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev,
spin_lock_init(&qdev->hw_lock);
/* Set driver entry points */
- ndev->open = ql3xxx_open;
- ndev->hard_start_xmit = ql3xxx_send;
- ndev->stop = ql3xxx_close;
- /* ndev->set_multicast_list
- * This device is one side of a two-function adapter
- * (NIC and iSCSI). Promiscuous mode setting/clearing is
- * not allowed from the NIC side.
- */
+ ndev->netdev_ops = &ql3xxx_netdev_ops;
SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops);
- ndev->set_mac_address = ql3xxx_set_mac_address;
- ndev->tx_timeout = ql3xxx_tx_timeout;
ndev->watchdog_timeo = 5 * HZ;
netif_napi_add(ndev, &qdev->napi, ql_poll, 64);
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c
index b62fbd4bf00f..eefb81b13758 100644
--- a/drivers/net/qlge/qlge_ethtool.c
+++ b/drivers/net/qlge/qlge_ethtool.c
@@ -97,7 +97,7 @@ exit:
return status;
}
-void ql_update_stats(struct ql_adapter *qdev)
+static void ql_update_stats(struct ql_adapter *qdev)
{
u32 i;
u64 data;
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index b83a9c9b6a97..718a7bd0cd1a 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -336,12 +336,11 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type,
(addr[5]);
QPRINTK(qdev, IFUP, INFO,
- "Adding %s address %02x:%02x:%02x:%02x:%02x:%02x"
+ "Adding %s address %pM"
" at index %d in the CAM.\n",
((type ==
MAC_ADDR_TYPE_MULTI_MAC) ? "MULTICAST" :
- "UNICAST"), addr[0], addr[1], addr[2], addr[3],
- addr[4], addr[5], index);
+ "UNICAST"), addr, index);
status =
ql_wait_reg_rdy(qdev,
@@ -643,7 +642,7 @@ static void ql_enable_all_completion_interrupts(struct ql_adapter *qdev)
}
-int ql_read_flash_word(struct ql_adapter *qdev, int offset, u32 *data)
+static int ql_read_flash_word(struct ql_adapter *qdev, int offset, u32 *data)
{
int status = 0;
/* wait for reg to come ready */
@@ -833,7 +832,7 @@ end:
}
/* Get the next large buffer. */
-struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
+static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
{
struct bq_desc *lbq_desc = &rx_ring->lbq[rx_ring->lbq_curr_idx];
rx_ring->lbq_curr_idx++;
@@ -844,7 +843,7 @@ struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
}
/* Get the next small buffer. */
-struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring)
+static struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring)
{
struct bq_desc *sbq_desc = &rx_ring->sbq[rx_ring->sbq_curr_idx];
rx_ring->sbq_curr_idx++;
@@ -1167,7 +1166,7 @@ map_error:
return NETDEV_TX_BUSY;
}
-void ql_realign_skb(struct sk_buff *skb, int len)
+static void ql_realign_skb(struct sk_buff *skb, int len)
{
void *temp_addr = skb->data;
@@ -1452,7 +1451,6 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
"Passing a normal packet upstream.\n");
netif_rx(skb);
}
- ndev->last_rx = jiffies;
}
/* Process an outbound completion from an rx ring. */
@@ -1649,7 +1647,7 @@ static int ql_napi_poll_msix(struct napi_struct *napi, int budget)
rx_ring->cq_id);
if (work_done < budget) {
- __netif_rx_complete(qdev->ndev, napi);
+ __netif_rx_complete(napi);
ql_enable_completion_interrupt(qdev, rx_ring->irq);
}
return work_done;
@@ -1734,8 +1732,7 @@ static irqreturn_t qlge_msix_tx_isr(int irq, void *dev_id)
static irqreturn_t qlge_msix_rx_isr(int irq, void *dev_id)
{
struct rx_ring *rx_ring = dev_id;
- struct ql_adapter *qdev = rx_ring->qdev;
- netif_rx_schedule(qdev->ndev, &rx_ring->napi);
+ netif_rx_schedule(&rx_ring->napi);
return IRQ_HANDLED;
}
@@ -1821,8 +1818,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
&rx_ring->rx_work,
0);
else
- netif_rx_schedule(qdev->ndev,
- &rx_ring->napi);
+ netif_rx_schedule(&rx_ring->napi);
work_done++;
}
}
@@ -2071,7 +2067,7 @@ err:
return -ENOMEM;
}
-void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
+static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
int i;
struct bq_desc *lbq_desc;
@@ -2134,7 +2130,7 @@ mem_error:
return -ENOMEM;
}
-void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
+static void ql_free_sbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
int i;
struct bq_desc *sbq_desc;
@@ -2469,7 +2465,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
rx_ring->sbq_base_indirect_dma = shadow_reg_dma;
/* PCI doorbell mem area + 0x00 for consumer index register */
- rx_ring->cnsmr_idx_db_reg = (u32 *) doorbell_area;
+ rx_ring->cnsmr_idx_db_reg = (u32 __iomem *) doorbell_area;
rx_ring->cnsmr_idx = 0;
rx_ring->curr_entry = rx_ring->cq_base;
@@ -2477,10 +2473,10 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
rx_ring->valid_db_reg = doorbell_area + 0x04;
/* PCI doorbell mem area + 0x18 for large buffer consumer */
- rx_ring->lbq_prod_idx_db_reg = (u32 *) (doorbell_area + 0x18);
+ rx_ring->lbq_prod_idx_db_reg = (u32 __iomem *) (doorbell_area + 0x18);
/* PCI doorbell mem area + 0x1c */
- rx_ring->sbq_prod_idx_db_reg = (u32 *) (doorbell_area + 0x1c);
+ rx_ring->sbq_prod_idx_db_reg = (u32 __iomem *) (doorbell_area + 0x1c);
memset((void *)cqicb, 0, sizeof(struct cqicb));
cqicb->msix_vect = rx_ring->irq;
@@ -2611,7 +2607,7 @@ static int ql_start_tx_ring(struct ql_adapter *qdev, struct tx_ring *tx_ring)
* Assign doorbell registers for this tx_ring.
*/
/* TX PCI doorbell mem area for tx producer index */
- tx_ring->prod_idx_db_reg = (u32 *) doorbell_area;
+ tx_ring->prod_idx_db_reg = (u32 __iomem *) doorbell_area;
tx_ring->prod_idx = 0;
/* TX PCI doorbell mem area + 0x04 */
tx_ring->valid_db_reg = doorbell_area + 0x04;
@@ -3127,11 +3123,7 @@ static void ql_display_dev_info(struct net_device *ndev)
qdev->chip_rev_id >> 4 & 0x0000000f,
qdev->chip_rev_id >> 8 & 0x0000000f,
qdev->chip_rev_id >> 12 & 0x0000000f);
- QPRINTK(qdev, PROBE, INFO,
- "MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
- ndev->dev_addr[0], ndev->dev_addr[1],
- ndev->dev_addr[2], ndev->dev_addr[3], ndev->dev_addr[4],
- ndev->dev_addr[5]);
+ QPRINTK(qdev, PROBE, INFO, "MAC address %pM\n", ndev->dev_addr);
}
static int ql_adapter_down(struct ql_adapter *qdev)
@@ -3156,7 +3148,7 @@ static int ql_adapter_down(struct ql_adapter *qdev)
* a workqueue only if it's a single interrupt
* environment (MSI/Legacy).
*/
- for (i = 1; i > qdev->rx_ring_count; i++) {
+ for (i = 1; i < qdev->rx_ring_count; i++) {
rx_ring = &qdev->rx_ring[i];
/* Only the RSS rings use NAPI on multi irq
* environment. Outbound completion processing
@@ -3526,6 +3518,7 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p)
{
struct ql_adapter *qdev = (struct ql_adapter *)netdev_priv(ndev);
struct sockaddr *addr = p;
+ int ret = 0;
if (netif_running(ndev))
return -EBUSY;
@@ -3538,11 +3531,11 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p)
if (ql_set_mac_addr_reg(qdev, (u8 *) ndev->dev_addr,
MAC_ADDR_TYPE_CAM_MAC, qdev->func)) {/* Unicast */
QPRINTK(qdev, HW, ERR, "Failed to load MAC address.\n");
- return -1;
+ ret = -1;
}
spin_unlock(&qdev->hw_lock);
- return 0;
+ return ret;
}
static void qlge_tx_timeout(struct net_device *ndev)
@@ -3592,7 +3585,7 @@ static void ql_release_all(struct pci_dev *pdev)
qdev->q_workqueue = NULL;
}
if (qdev->reg_base)
- iounmap((void *)qdev->reg_base);
+ iounmap(qdev->reg_base);
if (qdev->doorbell_area)
iounmap(qdev->doorbell_area);
pci_release_regions(pdev);
@@ -3721,6 +3714,22 @@ err_out:
return err;
}
+
+static const struct net_device_ops qlge_netdev_ops = {
+ .ndo_open = qlge_open,
+ .ndo_stop = qlge_close,
+ .ndo_start_xmit = qlge_send,
+ .ndo_change_mtu = qlge_change_mtu,
+ .ndo_get_stats = qlge_get_stats,
+ .ndo_set_multicast_list = qlge_set_multicast_list,
+ .ndo_set_mac_address = qlge_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = qlge_tx_timeout,
+ .ndo_vlan_rx_register = ql_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = ql_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = ql_vlan_rx_kill_vid,
+};
+
static int __devinit qlge_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_entry)
{
@@ -3758,19 +3767,11 @@ static int __devinit qlge_probe(struct pci_dev *pdev,
*/
ndev->tx_queue_len = qdev->tx_ring_size;
ndev->irq = pdev->irq;
- ndev->open = qlge_open;
- ndev->stop = qlge_close;
- ndev->hard_start_xmit = qlge_send;
+
+ ndev->netdev_ops = &qlge_netdev_ops;
SET_ETHTOOL_OPS(ndev, &qlge_ethtool_ops);
- ndev->change_mtu = qlge_change_mtu;
- ndev->get_stats = qlge_get_stats;
- ndev->set_multicast_list = qlge_set_multicast_list;
- ndev->set_mac_address = qlge_set_mac_address;
- ndev->tx_timeout = qlge_tx_timeout;
ndev->watchdog_timeo = 10 * HZ;
- ndev->vlan_rx_register = ql_vlan_rx_register;
- ndev->vlan_rx_add_vid = ql_vlan_rx_add_vid;
- ndev->vlan_rx_kill_vid = ql_vlan_rx_kill_vid;
+
err = register_netdev(ndev);
if (err) {
dev_err(&pdev->dev, "net device registration failed.\n");
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c
index 24fe344bcf1f..fa31891b6e62 100644
--- a/drivers/net/qlge/qlge_mpi.c
+++ b/drivers/net/qlge/qlge_mpi.c
@@ -19,7 +19,7 @@ exit:
return status;
}
-int ql_get_mb_sts(struct ql_adapter *qdev, struct mbox_params *mbcp)
+static int ql_get_mb_sts(struct ql_adapter *qdev, struct mbox_params *mbcp)
{
int i, status;
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
index 34fe7ef8e5ed..53bbddfc8c95 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/r6040.c
@@ -49,8 +49,8 @@
#include <asm/processor.h>
#define DRV_NAME "r6040"
-#define DRV_VERSION "0.18"
-#define DRV_RELDATE "13Jul2008"
+#define DRV_VERSION "0.19"
+#define DRV_RELDATE "18Dec2008"
/* PHY CHIP Address */
#define PHY1_ADDR 1 /* For MAC1 */
@@ -214,7 +214,7 @@ static int r6040_phy_read(void __iomem *ioaddr, int phy_addr, int reg)
/* Wait for the read bit to be cleared */
while (limit--) {
cmd = ioread16(ioaddr + MMDIO);
- if (cmd & MDIO_READ)
+ if (!(cmd & MDIO_READ))
break;
}
@@ -233,7 +233,7 @@ static void r6040_phy_write(void __iomem *ioaddr, int phy_addr, int reg, u16 val
/* Wait for the write bit to be cleared */
while (limit--) {
cmd = ioread16(ioaddr + MMDIO);
- if (cmd & MDIO_WRITE)
+ if (!(cmd & MDIO_WRITE))
break;
}
}
@@ -598,7 +598,6 @@ static int r6040_rx(struct net_device *dev, int limit)
/* Send to upper layer */
netif_receive_skb(skb_ptr);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += descptr->len - 4;
@@ -668,7 +667,7 @@ static int r6040_poll(struct napi_struct *napi, int budget)
work_done = r6040_rx(dev, budget);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/* Enable RX interrupt */
iowrite16(ioread16(ioaddr + MIER) | RX_INTS, ioaddr + MIER);
}
@@ -681,8 +680,10 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id)
struct net_device *dev = dev_id;
struct r6040_private *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->base;
- u16 status;
+ u16 misr, status;
+ /* Save MIER */
+ misr = ioread16(ioaddr + MIER);
/* Mask off RDC MAC interrupt */
iowrite16(MSK_INT, ioaddr + MIER);
/* Read MISR status and clear */
@@ -702,14 +703,17 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id)
dev->stats.rx_fifo_errors++;
/* Mask off RX interrupt */
- iowrite16(ioread16(ioaddr + MIER) & ~RX_INTS, ioaddr + MIER);
- netif_rx_schedule(dev, &lp->napi);
+ misr &= ~RX_INTS;
+ netif_rx_schedule(&lp->napi);
}
/* TX interrupt request */
if (status & TX_INTS)
r6040_tx(dev);
+ /* Restore RDC MAC interrupt */
+ iowrite16(misr, ioaddr + MIER);
+
return IRQ_HANDLED;
}
@@ -1030,13 +1034,28 @@ static u32 netdev_get_link(struct net_device *dev)
return mii_link_ok(&rp->mii_if);
}
-static struct ethtool_ops netdev_ethtool_ops = {
+static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
.get_settings = netdev_get_settings,
.set_settings = netdev_set_settings,
.get_link = netdev_get_link,
};
+static const struct net_device_ops r6040_netdev_ops = {
+ .ndo_open = r6040_open,
+ .ndo_stop = r6040_close,
+ .ndo_start_xmit = r6040_start_xmit,
+ .ndo_get_stats = r6040_get_stats,
+ .ndo_set_multicast_list = r6040_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = r6040_ioctl,
+ .ndo_tx_timeout = r6040_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = r6040_poll_controller,
+#endif
+};
+
static int __devinit r6040_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -1128,18 +1147,10 @@ static int __devinit r6040_init_one(struct pci_dev *pdev,
lp->switch_sig = 0;
/* The RDC-specific entries in the device structure. */
- dev->open = &r6040_open;
- dev->hard_start_xmit = &r6040_start_xmit;
- dev->stop = &r6040_close;
- dev->get_stats = r6040_get_stats;
- dev->set_multicast_list = &r6040_multicast_list;
- dev->do_ioctl = &r6040_ioctl;
+ dev->netdev_ops = &r6040_netdev_ops;
dev->ethtool_ops = &netdev_ethtool_ops;
- dev->tx_timeout = &r6040_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = r6040_poll_controller;
-#endif
+
netif_napi_add(dev, &lp->napi, r6040_poll, 64);
lp->mii_if.dev = dev;
lp->mii_if.mdio_read = r6040_mdio_read;
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 4b7cb389dc49..2c73ca606b35 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -474,6 +474,7 @@ struct rtl8169_private {
void (*hw_start)(struct net_device *);
unsigned int (*phy_reset_pending)(void __iomem *);
unsigned int (*link_ok)(void __iomem *);
+ int (*do_ioctl)(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd);
int pcie_cap;
struct delayed_work task;
unsigned features;
@@ -1829,9 +1830,11 @@ static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct rtl8169_private *tp = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(ifr);
- if (!netif_running(dev))
- return -ENODEV;
+ return netif_running(dev) ? tp->do_ioctl(tp, data, cmd) : -ENODEV;
+}
+static int rtl_xmii_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd)
+{
switch (cmd) {
case SIOCGMIIPHY:
data->phy_id = 32; /* Internal PHY */
@@ -1850,6 +1853,11 @@ static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EOPNOTSUPP;
}
+static int rtl_tbi_ioctl(struct rtl8169_private *tp, struct mii_ioctl_data *data, int cmd)
+{
+ return -EOPNOTSUPP;
+}
+
static const struct rtl_cfg_info {
void (*hw_start)(struct net_device *);
unsigned int region;
@@ -1915,6 +1923,26 @@ static void rtl_disable_msi(struct pci_dev *pdev, struct rtl8169_private *tp)
}
}
+static const struct net_device_ops rtl8169_netdev_ops = {
+ .ndo_open = rtl8169_open,
+ .ndo_stop = rtl8169_close,
+ .ndo_get_stats = rtl8169_get_stats,
+ .ndo_start_xmit = rtl8169_start_xmit,
+ .ndo_tx_timeout = rtl8169_tx_timeout,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = rtl8169_change_mtu,
+ .ndo_set_mac_address = rtl_set_mac_address,
+ .ndo_do_ioctl = rtl8169_ioctl,
+ .ndo_set_multicast_list = rtl_set_rx_mode,
+#ifdef CONFIG_R8169_VLAN
+ .ndo_vlan_rx_register = rtl8169_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = rtl8169_netpoll,
+#endif
+
+};
+
static int __devinit
rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -1941,6 +1969,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
SET_NETDEV_DEV(dev, &pdev->dev);
+ dev->netdev_ops = &rtl8169_netdev_ops;
tp = netdev_priv(dev);
tp->dev = dev;
tp->pci_dev = pdev;
@@ -2076,6 +2105,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->phy_reset_enable = rtl8169_tbi_reset_enable;
tp->phy_reset_pending = rtl8169_tbi_reset_pending;
tp->link_ok = rtl8169_tbi_link_ok;
+ tp->do_ioctl = rtl_tbi_ioctl;
tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */
} else {
@@ -2084,8 +2114,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->phy_reset_enable = rtl8169_xmii_reset_enable;
tp->phy_reset_pending = rtl8169_xmii_reset_pending;
tp->link_ok = rtl8169_xmii_link_ok;
-
- dev->do_ioctl = rtl8169_ioctl;
+ tp->do_ioctl = rtl_xmii_ioctl;
}
spin_lock_init(&tp->lock);
@@ -2097,28 +2126,15 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->dev_addr[i] = RTL_R8(MAC0 + i);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- dev->open = rtl8169_open;
- dev->hard_start_xmit = rtl8169_start_xmit;
- dev->get_stats = rtl8169_get_stats;
SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
- dev->stop = rtl8169_close;
- dev->tx_timeout = rtl8169_tx_timeout;
- dev->set_multicast_list = rtl_set_rx_mode;
dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
dev->irq = pdev->irq;
dev->base_addr = (unsigned long) ioaddr;
- dev->change_mtu = rtl8169_change_mtu;
- dev->set_mac_address = rtl_set_mac_address;
netif_napi_add(dev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT);
#ifdef CONFIG_R8169_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = rtl8169_vlan_rx_register;
-#endif
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rtl8169_netpoll;
#endif
tp->intr_mask = 0xffff;
@@ -3484,7 +3500,6 @@ static int rtl8169_rx_interrupt(struct net_device *dev,
if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)
netif_receive_skb(skb);
- dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
}
@@ -3566,8 +3581,8 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event);
tp->intr_mask = ~tp->napi_event;
- if (likely(netif_rx_schedule_prep(dev, &tp->napi)))
- __netif_rx_schedule(dev, &tp->napi);
+ if (likely(netif_rx_schedule_prep(&tp->napi)))
+ __netif_rx_schedule(&tp->napi);
else if (netif_msg_intr(tp)) {
printk(KERN_INFO "%s: interrupt %04x in poll\n",
dev->name, status);
@@ -3588,7 +3603,7 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
rtl8169_tx_interrupt(dev, tp, ioaddr);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
tp->intr_mask = 0xffff;
/*
* 20040426: the barrier is not strictly required but the
diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c
index 2b8fd68bc516..a6fd27a2cc3d 100644
--- a/drivers/net/rionet.c
+++ b/drivers/net/rionet.c
@@ -94,7 +94,7 @@ static int rionet_rx_clean(struct net_device *ndev)
{
int i;
int error = 0;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
void *data;
i = rnet->rx_slot;
@@ -132,7 +132,7 @@ static int rionet_rx_clean(struct net_device *ndev)
static void rionet_rx_fill(struct net_device *ndev, int end)
{
int i;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
i = rnet->rx_slot;
do {
@@ -151,7 +151,7 @@ static void rionet_rx_fill(struct net_device *ndev, int end)
static int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,
struct rio_dev *rdev)
{
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
rio_add_outb_message(rnet->mport, rdev, 0, skb->data, skb->len);
rnet->tx_skb[rnet->tx_slot] = skb;
@@ -175,7 +175,7 @@ static int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,
static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
int i;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
struct ethhdr *eth = (struct ethhdr *)skb->data;
u16 destid;
unsigned long flags;
@@ -215,7 +215,7 @@ static void rionet_dbell_event(struct rio_mport *mport, void *dev_id, u16 sid, u
u16 info)
{
struct net_device *ndev = dev_id;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
struct rionet_peer *peer;
if (netif_msg_intr(rnet))
@@ -243,7 +243,7 @@ static void rionet_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox
{
int n;
struct net_device *ndev = dev_id;
- struct rionet_private *rnet = (struct rionet_private *)ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
if (netif_msg_intr(rnet))
printk(KERN_INFO "%s: inbound message event, mbox %d slot %d\n",
@@ -258,7 +258,7 @@ static void rionet_inb_msg_event(struct rio_mport *mport, void *dev_id, int mbox
static void rionet_outb_msg_event(struct rio_mport *mport, void *dev_id, int mbox, int slot)
{
struct net_device *ndev = dev_id;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
spin_lock(&rnet->lock);
@@ -287,7 +287,7 @@ static int rionet_open(struct net_device *ndev)
int i, rc = 0;
struct rionet_peer *peer, *tmp;
u32 pwdcsr;
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
if (netif_msg_ifup(rnet))
printk(KERN_INFO "%s: open\n", DRV_NAME);
@@ -351,7 +351,7 @@ static int rionet_open(struct net_device *ndev)
static int rionet_close(struct net_device *ndev)
{
- struct rionet_private *rnet = (struct rionet_private *)ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
struct rionet_peer *peer, *tmp;
int i;
@@ -400,7 +400,7 @@ static void rionet_remove(struct rio_dev *rdev)
static void rionet_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
strcpy(info->driver, DRV_NAME);
strcpy(info->version, DRV_VERSION);
@@ -410,14 +410,14 @@ static void rionet_get_drvinfo(struct net_device *ndev,
static u32 rionet_get_msglevel(struct net_device *ndev)
{
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
return rnet->msg_enable;
}
static void rionet_set_msglevel(struct net_device *ndev, u32 value)
{
- struct rionet_private *rnet = ndev->priv;
+ struct rionet_private *rnet = netdev_priv(ndev);
rnet->msg_enable = value;
}
@@ -435,7 +435,6 @@ static int rionet_setup_netdev(struct rio_mport *mport)
struct net_device *ndev = NULL;
struct rionet_private *rnet;
u16 device_id;
- DECLARE_MAC_BUF(mac);
/* Allocate our net_device structure */
ndev = alloc_etherdev(sizeof(struct rionet_private));
@@ -456,7 +455,7 @@ static int rionet_setup_netdev(struct rio_mport *mport)
RIO_MAX_ROUTE_ENTRIES(mport->sys_size));
/* Set up private area */
- rnet = (struct rionet_private *)ndev->priv;
+ rnet = netdev_priv(ndev);
rnet->mport = mport;
/* Set the default MAC address */
@@ -485,12 +484,12 @@ static int rionet_setup_netdev(struct rio_mport *mport)
if (rc != 0)
goto out;
- printk("%s: %s %s Version %s, MAC %s\n",
+ printk("%s: %s %s Version %s, MAC %pM\n",
ndev->name,
DRV_NAME,
DRV_DESC,
DRV_VERSION,
- print_mac(mac, ndev->dev_addr));
+ ndev->dev_addr);
out:
return rc;
diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c
index 3dd8f1342f70..d890829a9acc 100644
--- a/drivers/net/rrunner.c
+++ b/drivers/net/rrunner.c
@@ -63,6 +63,16 @@ MODULE_LICENSE("GPL");
static char version[] __devinitdata = "rrunner.c: v0.50 11/11/2002 Jes Sorensen (jes@wildopensource.com)\n";
+
+static const struct net_device_ops rr_netdev_ops = {
+ .ndo_open = rr_open,
+ .ndo_stop = rr_close,
+ .ndo_do_ioctl = rr_ioctl,
+ .ndo_start_xmit = rr_start_xmit,
+ .ndo_change_mtu = hippi_change_mtu,
+ .ndo_set_mac_address = hippi_mac_addr,
+};
+
/*
* Implementation notes:
*
@@ -115,10 +125,7 @@ static int __devinit rr_init_one(struct pci_dev *pdev,
spin_lock_init(&rrpriv->lock);
dev->irq = pdev->irq;
- dev->open = &rr_open;
- dev->hard_start_xmit = &rr_start_xmit;
- dev->stop = &rr_close;
- dev->do_ioctl = &rr_ioctl;
+ dev->netdev_ops = &rr_netdev_ops;
dev->base_addr = pci_resource_start(pdev, 0);
@@ -511,7 +518,6 @@ static int __devinit rr_init(struct net_device *dev)
struct rr_private *rrpriv;
struct rr_regs __iomem *regs;
u32 sram_size, rev;
- DECLARE_MAC_BUF(mac);
rrpriv = netdev_priv(dev);
regs = rrpriv->regs;
@@ -549,7 +555,7 @@ static int __devinit rr_init(struct net_device *dev)
*(__be32 *)(dev->dev_addr+2) =
htonl(rr_read_eeprom_word(rrpriv, offsetof(struct eeprom, manf.BoardULA[4])));
- printk(" MAC: %s\n", print_mac(mac, dev->dev_addr));
+ printk(" MAC: %pM\n", dev->dev_addr);
sram_size = rr_read_eeprom_word(rrpriv, 8);
printk(" SRAM size 0x%06x\n", sram_size);
@@ -1006,7 +1012,6 @@ static void rx_int(struct net_device *dev, u32 rxlimit, u32 index)
netif_rx(skb); /* send it up */
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -1708,9 +1713,3 @@ static void __exit rr_cleanup_module(void)
module_init(rr_init_module);
module_exit(rr_cleanup_module);
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -pipe -fomit-frame-pointer -fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c rrunner.c"
- * End:
- */
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index 6a1375f9cbb8..f5c57c059bca 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -352,12 +352,13 @@ static void do_s2io_copy_mac_addr(struct s2io_nic *sp, int offset, u64 mac_addr)
sp->def_mac_addr[offset].mac_addr[1] = (u8) (mac_addr >> 32);
sp->def_mac_addr[offset].mac_addr[0] = (u8) (mac_addr >> 40);
}
+
/* Add the vlan */
static void s2io_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
+ struct vlan_group *grp)
{
int i;
- struct s2io_nic *nic = dev->priv;
+ struct s2io_nic *nic = netdev_priv(dev);
unsigned long flags[MAX_TX_FIFOS];
struct mac_info *mac_control = &nic->mac_control;
struct config_param *config = &nic->config;
@@ -372,10 +373,10 @@ static void s2io_vlan_rx_register(struct net_device *dev,
}
/* Unregister the vlan */
-static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid)
+static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
int i;
- struct s2io_nic *nic = dev->priv;
+ struct s2io_nic *nic = netdev_priv(dev);
unsigned long flags[MAX_TX_FIFOS];
struct mac_info *mac_control = &nic->mac_control;
struct config_param *config = &nic->config;
@@ -2837,7 +2838,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget)
int pkts_processed = 0;
u8 __iomem *addr = NULL;
u8 val8 = 0;
- struct s2io_nic *nic = dev->priv;
+ struct s2io_nic *nic = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = nic->bar0;
int budget_org = budget;
@@ -2851,7 +2852,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget)
s2io_chk_rx_buffers(nic, ring);
if (pkts_processed < budget_org) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/*Re Enable MSI-Rx Vector*/
addr = (u8 __iomem *)&bar0->xmsi_mask_reg;
addr += 7 - ring->ring_no;
@@ -2865,7 +2866,6 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget)
{
struct s2io_nic *nic = container_of(napi, struct s2io_nic, napi);
struct ring_info *ring;
- struct net_device *dev = nic->dev;
struct config_param *config;
struct mac_info *mac_control;
int pkts_processed = 0;
@@ -2889,7 +2889,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget)
break;
}
if (pkts_processed < budget_org) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/* Re enable the Rx interrupts for the ring */
writeq(0, &bar0->rx_traffic_mask);
readl(&bar0->rx_traffic_mask);
@@ -2909,7 +2909,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget)
*/
static void s2io_netpoll(struct net_device *dev)
{
- struct s2io_nic *nic = dev->priv;
+ struct s2io_nic *nic = netdev_priv(dev);
struct mac_info *mac_control;
struct config_param *config;
struct XENA_dev_config __iomem *bar0 = nic->bar0;
@@ -3171,7 +3171,7 @@ static void tx_intr_handler(struct fifo_info *fifo_data)
static void s2io_mdio_write(u32 mmd_type, u64 addr, u16 value, struct net_device *dev)
{
u64 val64 = 0x0;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
//address transaction
@@ -3220,7 +3220,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev)
{
u64 val64 = 0x0;
u64 rval64 = 0x0;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
/* address transaction */
@@ -3324,7 +3324,7 @@ static void s2io_updt_xpak_counter(struct net_device *dev)
u64 val64 = 0x0;
u64 addr = 0x0;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct stat_block *stat_info = sp->mac_control.stats_info;
/* Check the communication with the MDIO slave */
@@ -3990,7 +3990,7 @@ static void remove_inta_isr(struct s2io_nic *sp)
static int s2io_open(struct net_device *dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
int err = 0;
/*
@@ -4048,7 +4048,7 @@ hw_init_failed:
static int s2io_close(struct net_device *dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct config_param *config = &sp->config;
u64 tmp64;
int offset;
@@ -4087,7 +4087,7 @@ static int s2io_close(struct net_device *dev)
static int s2io_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
u16 frg_cnt, frg_len, i, queue, queue_len, put_off, get_off;
register u64 val64;
struct TxD *txdp;
@@ -4329,7 +4329,6 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id)
struct ring_info *ring = (struct ring_info *)dev_id;
struct s2io_nic *sp = ring->nic;
struct XENA_dev_config __iomem *bar0 = sp->bar0;
- struct net_device *dev = sp->dev;
if (unlikely(!is_s2io_card_up(sp)))
return IRQ_HANDLED;
@@ -4343,7 +4342,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id)
val8 = (ring->ring_no == 0) ? 0x7f : 0xff;
writeb(val8, addr);
val8 = readb(addr);
- netif_rx_schedule(dev, &ring->napi);
+ netif_rx_schedule(&ring->napi);
} else {
rx_intr_handler(ring, 0);
s2io_chk_rx_buffers(sp, ring);
@@ -4485,7 +4484,7 @@ static int do_s2io_chk_alarm_bit(u64 value, void __iomem * addr,
static void s2io_handle_errors(void * dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
u64 temp64 = 0,val64=0;
int i = 0;
@@ -4752,7 +4751,7 @@ reset:
static irqreturn_t s2io_isr(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *) dev_id;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
int i;
u64 reason = 0;
@@ -4790,7 +4789,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id)
if (config->napi) {
if (reason & GEN_INTR_RXTRAFFIC) {
- netif_rx_schedule(dev, &sp->napi);
+ netif_rx_schedule(&sp->napi);
writeq(S2IO_MINUS_ONE, &bar0->rx_traffic_mask);
writeq(S2IO_MINUS_ONE, &bar0->rx_traffic_int);
readl(&bar0->rx_traffic_int);
@@ -4881,7 +4880,7 @@ static void s2io_updt_stats(struct s2io_nic *sp)
static struct net_device_stats *s2io_get_stats(struct net_device *dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct mac_info *mac_control;
struct config_param *config;
int i;
@@ -4948,7 +4947,7 @@ static void s2io_set_multicast(struct net_device *dev)
{
int i, j, prev_cnt;
struct dev_mc_list *mclist;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
u64 val64 = 0, multi_mac = 0x010203040506ULL, mask =
0xfeffffffffffULL;
@@ -5112,7 +5111,7 @@ static void s2io_set_multicast(struct net_device *dev)
/* read from CAM unicast & multicast addresses and store it in
* def_mac_addr structure
*/
-void do_s2io_store_unicast_mc(struct s2io_nic *sp)
+static void do_s2io_store_unicast_mc(struct s2io_nic *sp)
{
int offset;
u64 mac_addr = 0x0;
@@ -5277,7 +5276,7 @@ static int s2io_set_mac_addr(struct net_device *dev, void *p)
static int do_s2io_prog_unicast(struct net_device *dev, u8 *addr)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
register u64 mac_addr = 0, perm_addr = 0;
int i;
u64 tmp64;
@@ -5336,7 +5335,7 @@ static int do_s2io_prog_unicast(struct net_device *dev, u8 *addr)
static int s2io_ethtool_sset(struct net_device *dev,
struct ethtool_cmd *info)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
if ((info->autoneg == AUTONEG_ENABLE) ||
(info->speed != SPEED_10000) || (info->duplex != DUPLEX_FULL))
return -EINVAL;
@@ -5362,7 +5361,7 @@ static int s2io_ethtool_sset(struct net_device *dev,
static int s2io_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
info->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
info->advertising = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
info->port = PORT_FIBRE;
@@ -5397,7 +5396,7 @@ static int s2io_ethtool_gset(struct net_device *dev, struct ethtool_cmd *info)
static void s2io_ethtool_gdrvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
strncpy(info->driver, s2io_driver_name, sizeof(info->driver));
strncpy(info->version, s2io_driver_version, sizeof(info->version));
@@ -5427,7 +5426,7 @@ static void s2io_ethtool_gregs(struct net_device *dev,
int i;
u64 reg;
u8 *reg_space = (u8 *) space;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
regs->len = XENA_REG_SPACE;
regs->version = sp->pdev->subsystem_device;
@@ -5487,7 +5486,7 @@ static void s2io_phy_id(unsigned long data)
static int s2io_ethtool_idnic(struct net_device *dev, u32 data)
{
u64 val64 = 0, last_gpio_ctrl_val;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
u16 subid;
@@ -5525,7 +5524,7 @@ static int s2io_ethtool_idnic(struct net_device *dev, u32 data)
static void s2io_ethtool_gringparam(struct net_device *dev,
struct ethtool_ringparam *ering)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
int i,tx_desc_count=0,rx_desc_count=0;
if (sp->rxd_mode == RXD_MODE_1)
@@ -5568,7 +5567,7 @@ static void s2io_ethtool_getpause_data(struct net_device *dev,
struct ethtool_pauseparam *ep)
{
u64 val64;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
val64 = readq(&bar0->rmac_pause_cfg);
@@ -5595,7 +5594,7 @@ static int s2io_ethtool_setpause_data(struct net_device *dev,
struct ethtool_pauseparam *ep)
{
u64 val64;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct XENA_dev_config __iomem *bar0 = sp->bar0;
val64 = readq(&bar0->rmac_pause_cfg);
@@ -5825,7 +5824,7 @@ static int s2io_ethtool_geeprom(struct net_device *dev,
{
u32 i, valid;
u64 data;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
eeprom->magic = sp->pdev->vendor | (sp->pdev->device << 16);
@@ -5863,7 +5862,7 @@ static int s2io_ethtool_seeprom(struct net_device *dev,
{
int len = eeprom->len, cnt = 0;
u64 valid = 0, data;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
if (eeprom->magic != (sp->pdev->vendor | (sp->pdev->device << 16))) {
DBG_PRINT(ERR_DBG,
@@ -6243,7 +6242,7 @@ static void s2io_ethtool_test(struct net_device *dev,
struct ethtool_test *ethtest,
uint64_t * data)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
int orig_state = netif_running(sp->dev);
if (ethtest->flags == ETH_TEST_FL_OFFLINE) {
@@ -6299,7 +6298,7 @@ static void s2io_get_ethtool_stats(struct net_device *dev,
u64 * tmp_stats)
{
int i = 0, k;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
struct stat_block *stat_info = sp->mac_control.stats_info;
s2io_updt_stats(sp);
@@ -6578,14 +6577,14 @@ static int s2io_ethtool_get_regs_len(struct net_device *dev)
static u32 s2io_ethtool_get_rx_csum(struct net_device * dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
return (sp->rx_csum);
}
static int s2io_ethtool_set_rx_csum(struct net_device *dev, u32 data)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
if (data)
sp->rx_csum = 1;
@@ -6602,7 +6601,7 @@ static int s2io_get_eeprom_len(struct net_device *dev)
static int s2io_get_sset_count(struct net_device *dev, int sset)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
switch (sset) {
case ETH_SS_TEST:
@@ -6625,7 +6624,7 @@ static void s2io_ethtool_get_strings(struct net_device *dev,
u32 stringset, u8 * data)
{
int stat_size = 0;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
switch (stringset) {
case ETH_SS_TEST:
@@ -6727,7 +6726,7 @@ static int s2io_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static int s2io_change_mtu(struct net_device *dev, int new_mtu)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
int ret = 0;
if ((new_mtu < MIN_MTU) || (new_mtu > S2IO_JUMBO_SIZE)) {
@@ -7331,7 +7330,7 @@ out_unlock:
static void s2io_tx_watchdog(struct net_device *dev)
{
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
if (netif_carrier_ok(dev)) {
sp->mac_control.stats_info->sw_stat.watchdog_timer_cnt++;
@@ -7366,7 +7365,7 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
int ring_no = ring_data->ring_no;
u16 l3_csum, l4_csum;
unsigned long long err = rxdp->Control_1 & RXD_T_CODE;
- struct lro *lro;
+ struct lro *uninitialized_var(lro);
u8 err_mask;
skb->dev = dev;
@@ -7544,7 +7543,6 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp)
sp->mac_control.stats_info->sw_stat.mem_freed += skb->truesize;
send_up:
queue_rx_frame(skb, RXD_GET_VLAN_TAG(rxdp->Control_2));
- dev->last_rx = jiffies;
aggregate:
sp->mac_control.rings[ring_no].rx_bufs_left -= 1;
return SUCCESS;
@@ -7718,6 +7716,24 @@ static int rts_ds_steer(struct s2io_nic *nic, u8 ds_codepoint, u8 ring)
S2IO_BIT_RESET);
}
+static const struct net_device_ops s2io_netdev_ops = {
+ .ndo_open = s2io_open,
+ .ndo_stop = s2io_close,
+ .ndo_get_stats = s2io_get_stats,
+ .ndo_start_xmit = s2io_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = s2io_set_multicast,
+ .ndo_do_ioctl = s2io_ioctl,
+ .ndo_set_mac_address = s2io_set_mac_addr,
+ .ndo_change_mtu = s2io_change_mtu,
+ .ndo_vlan_rx_register = s2io_vlan_rx_register,
+ .ndo_vlan_rx_kill_vid = s2io_vlan_rx_kill_vid,
+ .ndo_tx_timeout = s2io_tx_watchdog,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = s2io_netpoll,
+#endif
+};
+
/**
* s2io_init_nic - Initialization of the adapter .
* @pdev : structure containing the PCI related information of the device.
@@ -7748,7 +7764,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
int mode;
u8 dev_intr_type = intr_type;
u8 dev_multiq = 0;
- DECLARE_MAC_BUF(mac);
ret = s2io_verify_parm(pdev, &dev_intr_type, &dev_multiq);
if (ret)
@@ -7798,7 +7813,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
SET_NETDEV_DEV(dev, &pdev->dev);
/* Private member variable initialized to s2io NIC structure */
- sp = dev->priv;
+ sp = netdev_priv(dev);
memset(sp, 0, sizeof(struct s2io_nic));
sp->dev = dev;
sp->pdev = pdev;
@@ -7918,8 +7933,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
goto mem_alloc_failed;
}
- sp->bar0 = ioremap(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ sp->bar0 = pci_ioremap_bar(pdev, 0);
if (!sp->bar0) {
DBG_PRINT(ERR_DBG, "%s: Neterion: cannot remap io mem1\n",
dev->name);
@@ -7927,8 +7941,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
goto bar0_remap_failed;
}
- sp->bar1 = ioremap(pci_resource_start(pdev, 2),
- pci_resource_len(pdev, 2));
+ sp->bar1 = pci_ioremap_bar(pdev, 2);
if (!sp->bar1) {
DBG_PRINT(ERR_DBG, "%s: Neterion: cannot remap io mem2\n",
dev->name);
@@ -7946,26 +7959,9 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
}
/* Driver entry points */
- dev->open = &s2io_open;
- dev->stop = &s2io_close;
- dev->hard_start_xmit = &s2io_xmit;
- dev->get_stats = &s2io_get_stats;
- dev->set_multicast_list = &s2io_set_multicast;
- dev->do_ioctl = &s2io_ioctl;
- dev->set_mac_address = &s2io_set_mac_addr;
- dev->change_mtu = &s2io_change_mtu;
+ dev->netdev_ops = &s2io_netdev_ops;
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = s2io_vlan_rx_register;
- dev->vlan_rx_kill_vid = (void *)s2io_vlan_rx_kill_vid;
-
- /*
- * will use eth_mac_addr() for dev->set_mac_address
- * mac address will be set every time dev->open() is called
- */
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = s2io_netpoll;
-#endif
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
if (sp->high_dma_flag == TRUE)
@@ -7976,7 +7972,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
dev->features |= NETIF_F_UFO;
dev->features |= NETIF_F_HW_CSUM;
}
- dev->tx_timeout = &s2io_tx_watchdog;
dev->watchdog_timeo = WATCH_DOG_TIMEOUT;
INIT_WORK(&sp->rst_timer_task, s2io_restart_nic);
INIT_WORK(&sp->set_link_task, s2io_set_link);
@@ -8125,8 +8120,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre)
sp->product_name, pdev->revision);
DBG_PRINT(ERR_DBG, "%s: Driver version %s\n", dev->name,
s2io_driver_version);
- DBG_PRINT(ERR_DBG, "%s: MAC ADDR: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ DBG_PRINT(ERR_DBG, "%s: MAC ADDR: %pM\n", dev->name, dev->dev_addr);
DBG_PRINT(ERR_DBG, "SERIAL NUMBER: %s\n", sp->serial_num);
if (sp->device_type & XFRAME_II_DEVICE) {
mode = s2io_print_pci_mode(sp);
@@ -8255,7 +8249,7 @@ static void __devexit s2io_rem_nic(struct pci_dev *pdev)
flush_scheduled_work();
- sp = dev->priv;
+ sp = netdev_priv(dev);
unregister_netdev(dev);
free_shared_mem(sp);
@@ -8590,7 +8584,7 @@ static void clear_lro_session(struct lro *lro)
static void queue_rx_frame(struct sk_buff *skb, u16 vlan_tag)
{
struct net_device *dev = skb->dev;
- struct s2io_nic *sp = dev->priv;
+ struct s2io_nic *sp = netdev_priv(dev);
skb->protocol = eth_type_trans(skb, dev);
if (sp->vlgrp && vlan_tag
@@ -8639,7 +8633,7 @@ static pci_ers_result_t s2io_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct s2io_nic *sp = netdev->priv;
+ struct s2io_nic *sp = netdev_priv(netdev);
netif_device_detach(netdev);
@@ -8664,7 +8658,7 @@ static pci_ers_result_t s2io_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t s2io_io_slot_reset(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct s2io_nic *sp = netdev->priv;
+ struct s2io_nic *sp = netdev_priv(netdev);
if (pci_enable_device(pdev)) {
printk(KERN_ERR "s2io: "
@@ -8688,7 +8682,7 @@ static pci_ers_result_t s2io_io_slot_reset(struct pci_dev *pdev)
static void s2io_io_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct s2io_nic *sp = netdev->priv;
+ struct s2io_nic *sp = netdev_priv(netdev);
if (netif_running(netdev)) {
if (s2io_card_up(sp)) {
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index 5986cec17f19..be3025310e90 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -869,7 +869,6 @@ printk("cm0: IP identification: %02x%02x fragment offset: %02x%02x\n", buffer[3
/* datagram completed: send to upper level */
skb_trim(skb, dlen);
netif_rx(skb);
- dev->last_rx = jiffies;
stats->rx_bytes+=dlen;
stats->rx_packets++;
lp->rx_skb[ns] = NULL;
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c
index 2615d46e6e50..31e38fae017f 100644
--- a/drivers/net/sb1250-mac.c
+++ b/drivers/net/sb1250-mac.c
@@ -2039,9 +2039,9 @@ static irqreturn_t sbmac_intr(int irq,void *dev_instance)
sbdma_tx_process(sc,&(sc->sbm_txdma), 0);
if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) {
- if (netif_rx_schedule_prep(dev, &sc->napi)) {
+ if (netif_rx_schedule_prep(&sc->napi)) {
__raw_writeq(0, sc->sbm_imr);
- __netif_rx_schedule(dev, &sc->napi);
+ __netif_rx_schedule(&sc->napi);
/* Depend on the exit from poll to reenable intr */
}
else {
@@ -2292,7 +2292,6 @@ static int sbmac_init(struct platform_device *pldev, long long base)
uint64_t ea_reg;
int i;
int err;
- DECLARE_MAC_BUF(mac);
sc->sbm_dev = dev;
sc->sbe_idx = idx;
@@ -2373,8 +2372,8 @@ static int sbmac_init(struct platform_device *pldev, long long base)
* process so we need to finish off the config message that
* was being displayed)
*/
- pr_info("%s: SiByte Ethernet at 0x%08Lx, address: %s\n",
- dev->name, base, print_mac(mac, eaddr));
+ pr_info("%s: SiByte Ethernet at 0x%08Lx, address: %pM\n",
+ dev->name, base, eaddr);
sc->mii_bus->name = sbmac_mdio_string;
snprintf(sc->mii_bus->id, MII_BUS_ID_SIZE, "%x", idx);
@@ -2668,7 +2667,7 @@ static int sbmac_poll(struct napi_struct *napi, int budget)
sbdma_tx_process(sc, &(sc->sbm_txdma), 1);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
#ifdef CONFIG_SBMAC_COALESCE
__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) |
diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c
index 61955f8d8011..42fd31276602 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/sc92031.c
@@ -816,7 +816,6 @@ static void _sc92031_rx_tasklet(struct net_device *dev)
}
skb->protocol = eth_type_trans(skb, dev);
- dev->last_rx = jiffies;
netif_rx(skb);
dev->stats.rx_bytes += pkt_size;
@@ -1387,7 +1386,7 @@ static void sc92031_ethtool_get_ethtool_stats(struct net_device *dev,
spin_unlock_bh(&priv->lock);
}
-static struct ethtool_ops sc92031_ethtool_ops = {
+static const struct ethtool_ops sc92031_ethtool_ops = {
.get_settings = sc92031_ethtool_get_settings,
.set_settings = sc92031_ethtool_set_settings,
.get_drvinfo = sc92031_ethtool_get_drvinfo,
@@ -1400,6 +1399,21 @@ static struct ethtool_ops sc92031_ethtool_ops = {
.get_ethtool_stats = sc92031_ethtool_get_ethtool_stats,
};
+
+static const struct net_device_ops sc92031_netdev_ops = {
+ .ndo_get_stats = sc92031_get_stats,
+ .ndo_start_xmit = sc92031_start_xmit,
+ .ndo_open = sc92031_open,
+ .ndo_stop = sc92031_stop,
+ .ndo_set_multicast_list = sc92031_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = sc92031_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = sc92031_poll_controller,
+#endif
+};
+
static int __devinit sc92031_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -1453,17 +1467,9 @@ static int __devinit sc92031_probe(struct pci_dev *pdev,
/* faked with skb_copy_and_csum_dev */
dev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;
- dev->get_stats = sc92031_get_stats;
- dev->ethtool_ops = &sc92031_ethtool_ops;
- dev->hard_start_xmit = sc92031_start_xmit;
+ dev->netdev_ops = &sc92031_netdev_ops;
dev->watchdog_timeo = TX_TIMEOUT;
- dev->open = sc92031_open;
- dev->stop = sc92031_stop;
- dev->set_multicast_list = sc92031_set_multicast_list;
- dev->tx_timeout = sc92031_tx_timeout;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = sc92031_poll_controller;
-#endif
+ dev->ethtool_ops = &sc92031_ethtool_ops;
priv = netdev_priv(dev);
spin_lock_init(&priv->lock);
diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c
index 48c64fb20eec..12a8ffffeb03 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/seeq8005.c
@@ -158,7 +158,6 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
int old_dmaar;
int old_rear;
int retval;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, SEEQ8005_IO_EXTENT, "seeq8005"))
return -ENODEV;
@@ -303,7 +302,7 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
/* Retrieve and print the ethernet address. */
for (i = 0; i < 6; i++)
dev->dev_addr[i] = SA_prom[i+6];
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
if (dev->irq == 0xff)
; /* Do nothing: a user-level program will set it. */
@@ -564,7 +563,6 @@ static void seeq8005_rx(struct net_device *dev)
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -746,12 +744,3 @@ void __exit cleanup_module(void)
}
#endif /* MODULE */
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
- * version-control: t
- * kept-new-versions: 5
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig
index 3be13b592b4d..c535408ad6be 100644
--- a/drivers/net/sfc/Kconfig
+++ b/drivers/net/sfc/Kconfig
@@ -12,3 +12,11 @@ config SFC
To compile this driver as a module, choose M here. The module
will be called sfc.
+config SFC_MTD
+ bool "Solarflare Solarstorm SFC4000 flash MTD support"
+ depends on SFC && MTD && !(SFC=y && MTD=m)
+ default y
+ help
+ This exposes the on-board flash memory as an MTD device (e.g.
+ /dev/mtd1). This makes it possible to upload new boot code
+ to the NIC.
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index c8f5704c8fb1..b89f9be3cb13 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,5 +1,6 @@
-sfc-y += efx.o falcon.o tx.o rx.o falcon_xmac.o \
- selftest.o ethtool.o xfp_phy.o \
+sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \
+ falcon_xmac.o selftest.o ethtool.o xfp_phy.o \
mdio_10g.o tenxpress.o boards.o sfe4001.o
+sfc-$(CONFIG_SFC_MTD) += mtd.o
obj-$(CONFIG_SFC) += sfc.o
diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c
index 99e602373269..64903496aa9a 100644
--- a/drivers/net/sfc/boards.c
+++ b/drivers/net/sfc/boards.c
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -11,6 +11,7 @@
#include "phy.h"
#include "boards.h"
#include "efx.h"
+#include "workarounds.h"
/* Macros for unpacking the board revision */
/* The revision info is in host byte order. */
@@ -52,9 +53,128 @@ static void board_blink(struct efx_nic *efx, bool blink)
}
/*****************************************************************************
+ * Support for LM87 sensor chip used on several boards
+ */
+#define LM87_REG_ALARMS1 0x41
+#define LM87_REG_ALARMS2 0x42
+#define LM87_IN_LIMITS(nr, _min, _max) \
+ 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
+#define LM87_AIN_LIMITS(nr, _min, _max) \
+ 0x3B + (nr), _max, 0x1A + (nr), _min
+#define LM87_TEMP_INT_LIMITS(_min, _max) \
+ 0x39, _max, 0x3A, _min
+#define LM87_TEMP_EXT1_LIMITS(_min, _max) \
+ 0x37, _max, 0x38, _min
+
+#define LM87_ALARM_TEMP_INT 0x10
+#define LM87_ALARM_TEMP_EXT1 0x20
+
+#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
+
+static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+ const u8 *reg_values)
+{
+ struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
+ int rc;
+
+ if (!client)
+ return -EIO;
+
+ while (*reg_values) {
+ u8 reg = *reg_values++;
+ u8 value = *reg_values++;
+ rc = i2c_smbus_write_byte_data(client, reg, value);
+ if (rc)
+ goto err;
+ }
+
+ efx->board_info.hwmon_client = client;
+ return 0;
+
+err:
+ i2c_unregister_device(client);
+ return rc;
+}
+
+static void efx_fini_lm87(struct efx_nic *efx)
+{
+ i2c_unregister_device(efx->board_info.hwmon_client);
+}
+
+static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+ struct i2c_client *client = efx->board_info.hwmon_client;
+ s32 alarms1, alarms2;
+
+ /* If link is up then do not monitor temperature */
+ if (EFX_WORKAROUND_7884(efx) && efx->link_up)
+ return 0;
+
+ alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
+ alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
+ if (alarms1 < 0)
+ return alarms1;
+ if (alarms2 < 0)
+ return alarms2;
+ alarms1 &= mask;
+ alarms2 &= mask >> 8;
+ if (alarms1 || alarms2) {
+ EFX_ERR(efx,
+ "LM87 detected a hardware failure (status %02x:%02x)"
+ "%s%s\n",
+ alarms1, alarms2,
+ (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
+ (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_SENSORS_LM87 */
+
+static inline int
+efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+ const u8 *reg_values)
+{
+ return 0;
+}
+static inline void efx_fini_lm87(struct efx_nic *efx)
+{
+}
+static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SENSORS_LM87 */
+
+/*****************************************************************************
* Support for the SFE4002
*
*/
+static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
+
+static const u8 sfe4002_lm87_regs[] = {
+ LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
+ LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
+ LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
+ LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */
+ LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
+ LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
+ LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */
+ LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
+ LM87_TEMP_INT_LIMITS(10, 60), /* board */
+ LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
+ 0
+};
+
+static struct i2c_board_info sfe4002_hwmon_info = {
+ I2C_BOARD_INFO("lm87", 0x2e),
+ .platform_data = &sfe4002_lm87_channel,
+ .irq = -1,
+};
+
/****************************************************************************/
/* LED allocations. Note that on rev A0 boards the schematic and the reality
* differ: red and green are swapped. Below is the fixed (A1) layout (there
@@ -84,81 +204,67 @@ static void sfe4002_fault_led(struct efx_nic *efx, bool state)
QUAKE_LED_OFF);
}
+static int sfe4002_check_hw(struct efx_nic *efx)
+{
+ /* A0 board rev. 4002s report a temperature fault the whole time
+ * (bad sensor) so we mask it out. */
+ unsigned alarm_mask =
+ (efx->board_info.major == 0 && efx->board_info.minor == 0) ?
+ ~LM87_ALARM_TEMP_EXT1 : ~0;
+
+ return efx_check_lm87(efx, alarm_mask);
+}
+
static int sfe4002_init(struct efx_nic *efx)
{
+ int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
+ if (rc)
+ return rc;
+ efx->board_info.monitor = sfe4002_check_hw;
efx->board_info.init_leds = sfe4002_init_leds;
efx->board_info.set_fault_led = sfe4002_fault_led;
efx->board_info.blink = board_blink;
+ efx->board_info.fini = efx_fini_lm87;
return 0;
}
/* This will get expanded as board-specific details get moved out of the
* PHY drivers. */
struct efx_board_data {
+ enum efx_board_type type;
const char *ref_model;
const char *gen_type;
int (*init) (struct efx_nic *nic);
};
-static int dummy_init(struct efx_nic *nic)
-{
- return 0;
-}
static struct efx_board_data board_data[] = {
- [EFX_BOARD_INVALID] =
- {NULL, NULL, dummy_init},
- [EFX_BOARD_SFE4001] =
- {"SFE4001", "10GBASE-T adapter", sfe4001_init},
- [EFX_BOARD_SFE4002] =
- {"SFE4002", "XFP adapter", sfe4002_init},
+ { EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init },
+ { EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init },
+ { EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter",
+ sfn4111t_init },
};
-int efx_set_board_info(struct efx_nic *efx, u16 revision_info)
+void efx_set_board_info(struct efx_nic *efx, u16 revision_info)
{
- int rc = 0;
- struct efx_board_data *data;
-
- if (BOARD_TYPE(revision_info) >= EFX_BOARD_MAX) {
- EFX_ERR(efx, "squashing unknown board type %d\n",
- BOARD_TYPE(revision_info));
- revision_info = 0;
- }
+ struct efx_board_data *data = NULL;
+ int i;
- if (BOARD_TYPE(revision_info) == 0) {
- efx->board_info.major = 0;
- efx->board_info.minor = 0;
- /* For early boards that don't have revision info. there is
- * only 1 board for each PHY type, so we can work it out, with
- * the exception of the PHY-less boards. */
- switch (efx->phy_type) {
- case PHY_TYPE_10XPRESS:
- efx->board_info.type = EFX_BOARD_SFE4001;
- break;
- case PHY_TYPE_XFP:
- efx->board_info.type = EFX_BOARD_SFE4002;
- break;
- default:
- efx->board_info.type = 0;
- break;
- }
- } else {
- efx->board_info.type = BOARD_TYPE(revision_info);
- efx->board_info.major = BOARD_MAJOR(revision_info);
- efx->board_info.minor = BOARD_MINOR(revision_info);
- }
+ efx->board_info.type = BOARD_TYPE(revision_info);
+ efx->board_info.major = BOARD_MAJOR(revision_info);
+ efx->board_info.minor = BOARD_MINOR(revision_info);
- data = &board_data[efx->board_info.type];
+ for (i = 0; i < ARRAY_SIZE(board_data); i++)
+ if (board_data[i].type == efx->board_info.type)
+ data = &board_data[i];
- /* Report the board model number or generic type for recognisable
- * boards. */
- if (efx->board_info.type != 0)
+ if (data) {
EFX_INFO(efx, "board is %s rev %c%d\n",
(efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
? data->ref_model : data->gen_type,
'A' + efx->board_info.major, efx->board_info.minor);
-
- efx->board_info.init = data->init;
-
- return rc;
+ efx->board_info.init = data->init;
+ } else {
+ EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type);
+ }
}
diff --git a/drivers/net/sfc/boards.h b/drivers/net/sfc/boards.h
index c6e01b64bfb4..d93c6c6a7548 100644
--- a/drivers/net/sfc/boards.h
+++ b/drivers/net/sfc/boards.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -12,14 +12,16 @@
/* Board IDs (must fit in 8 bits) */
enum efx_board_type {
- EFX_BOARD_INVALID = 0,
- EFX_BOARD_SFE4001 = 1, /* SFE4001 (10GBASE-T) */
+ EFX_BOARD_SFE4001 = 1,
EFX_BOARD_SFE4002 = 2,
- /* Insert new types before here */
- EFX_BOARD_MAX
+ EFX_BOARD_SFN4111T = 0x51,
};
-extern int efx_set_board_info(struct efx_nic *efx, u16 revision_info);
+extern void efx_set_board_info(struct efx_nic *efx, u16 revision_info);
+
+/* SFE4001 (10GBASE-T) */
extern int sfe4001_init(struct efx_nic *efx);
+/* SFN4111T (100/1000/10GBASE-T) */
+extern int sfn4111t_init(struct efx_nic *efx);
#endif
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 06ea71c7e34e..7673fd92eaf5 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -21,14 +21,12 @@
#include <linux/ethtool.h>
#include <linux/topology.h>
#include "net_driver.h"
-#include "gmii.h"
#include "ethtool.h"
#include "tx.h"
#include "rx.h"
#include "efx.h"
#include "mdio_10g.h"
#include "falcon.h"
-#include "mac.h"
#define EFX_MAX_MTU (9 * 1024)
@@ -39,6 +37,12 @@
*/
static struct workqueue_struct *refill_workqueue;
+/* Reset workqueue. If any NIC has a hardware failure then a reset will be
+ * queued onto this work queue. This is not a per-nic work queue, because
+ * efx_reset_work() acquires the rtnl lock, so resets are naturally serialised.
+ */
+static struct workqueue_struct *reset_workqueue;
+
/**************************************************************************
*
* Configurable values
@@ -58,13 +62,15 @@ MODULE_PARM_DESC(lro, "Large receive offload acceleration");
/*
* Use separate channels for TX and RX events
*
- * Set this to 1 to use separate channels for TX and RX. It allows us to
- * apply a higher level of interrupt moderation to TX events.
+ * Set this to 1 to use separate channels for TX and RX. It allows us
+ * to control interrupt affinity separately for TX and RX.
*
- * This is forced to 0 for MSI interrupt mode as the interrupt vector
- * is not written
+ * This is only used in MSI-X interrupt mode
*/
-static unsigned int separate_tx_and_rx_channels = true;
+static unsigned int separate_tx_channels;
+module_param(separate_tx_channels, uint, 0644);
+MODULE_PARM_DESC(separate_tx_channels,
+ "Use separate channels for TX and RX");
/* This is the weight assigned to each of the (per-channel) virtual
* NAPI devices.
@@ -77,11 +83,6 @@ static int napi_weight = 64;
*/
unsigned int efx_monitor_interval = 1 * HZ;
-/* This controls whether or not the hardware monitor will trigger a
- * reset when it detects an error condition.
- */
-static unsigned int monitor_reset = true;
-
/* This controls whether or not the driver will initialise devices
* with invalid MAC addresses stored in the EEPROM or flash. If true,
* such devices will be initialised with a random locally-generated
@@ -128,6 +129,10 @@ static unsigned int rss_cpus;
module_param(rss_cpus, uint, 0444);
MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling");
+static int phy_flash_cfg;
+module_param(phy_flash_cfg, int, 0644);
+MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially");
+
/**************************************************************************
*
* Utility functions and prototypes
@@ -211,7 +216,6 @@ static int efx_poll(struct napi_struct *napi, int budget)
{
struct efx_channel *channel =
container_of(napi, struct efx_channel, napi_str);
- struct net_device *napi_dev = channel->napi_dev;
int rx_packets;
EFX_TRACE(channel->efx, "channel %d NAPI poll executing on CPU %d\n",
@@ -225,7 +229,7 @@ static int efx_poll(struct napi_struct *napi, int budget)
* since efx_channel_processed() will have no effect if
* interrupts have already been disabled.
*/
- netif_rx_complete(napi_dev, napi);
+ netif_rx_complete(napi);
efx_channel_processed(channel);
}
@@ -349,6 +353,27 @@ static int efx_probe_channel(struct efx_channel *channel)
}
+static void efx_set_channel_names(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+ const char *type = "";
+ int number;
+
+ efx_for_each_channel(channel, efx) {
+ number = channel->channel;
+ if (efx->n_channels > efx->n_rx_queues) {
+ if (channel->channel < efx->n_rx_queues) {
+ type = "-rx";
+ } else {
+ type = "-tx";
+ number -= efx->n_rx_queues;
+ }
+ }
+ snprintf(channel->name, sizeof(channel->name),
+ "%s%s-%d", efx->name, type, number);
+ }
+}
+
/* Channels are shutdown and reinitialised whilst the NIC is running
* to propagate configuration changes (mtu, checksum offload), or
* to clear hardware error conditions
@@ -523,26 +548,8 @@ static void efx_link_status_changed(struct efx_nic *efx)
/* Status message for kernel log */
if (efx->link_up) {
- struct mii_if_info *gmii = &efx->mii;
- unsigned adv, lpa;
- /* NONE here means direct XAUI from the controller, with no
- * MDIO-attached device we can query. */
- if (efx->phy_type != PHY_TYPE_NONE) {
- adv = gmii_advertised(gmii);
- lpa = gmii_lpa(gmii);
- } else {
- lpa = GM_LPA_10000 | LPA_DUPLEX;
- adv = lpa;
- }
- EFX_INFO(efx, "link up at %dMbps %s-duplex "
- "(adv %04x lpa %04x) (MTU %d)%s\n",
- (efx->link_options & GM_LPA_10000 ? 10000 :
- (efx->link_options & GM_LPA_1000 ? 1000 :
- (efx->link_options & GM_LPA_100 ? 100 :
- 10))),
- (efx->link_options & GM_LPA_DUPLEX ?
- "full" : "half"),
- adv, lpa,
+ EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n",
+ efx->link_speed, efx->link_fd ? "full" : "half",
efx->net_dev->mtu,
(efx->promiscuous ? " [PROMISC]" : ""));
} else {
@@ -566,10 +573,28 @@ void __efx_reconfigure_port(struct efx_nic *efx)
netif_addr_unlock_bh(efx->net_dev);
}
- falcon_reconfigure_xmac(efx);
+ falcon_deconfigure_mac_wrapper(efx);
+
+ /* Reconfigure the PHY, disabling transmit in mac level loopback. */
+ if (LOOPBACK_INTERNAL(efx))
+ efx->phy_mode |= PHY_MODE_TX_DISABLED;
+ else
+ efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
+ efx->phy_op->reconfigure(efx);
+
+ if (falcon_switch_mac(efx))
+ goto fail;
+
+ efx->mac_op->reconfigure(efx);
/* Inform kernel of loss/gain of carrier */
efx_link_status_changed(efx);
+ return;
+
+fail:
+ EFX_ERR(efx, "failed to reconfigure MAC\n");
+ efx->phy_op->fini(efx);
+ efx->port_initialized = false;
}
/* Reinitialise the MAC to pick up new PHY settings, even if the port is
@@ -586,10 +611,9 @@ void efx_reconfigure_port(struct efx_nic *efx)
/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
* we don't efx_reconfigure_port() if the port is disabled. Care is taken
* in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
-static void efx_reconfigure_work(struct work_struct *data)
+static void efx_phy_work(struct work_struct *data)
{
- struct efx_nic *efx = container_of(data, struct efx_nic,
- reconfigure_work);
+ struct efx_nic *efx = container_of(data, struct efx_nic, phy_work);
mutex_lock(&efx->mac_lock);
if (efx->port_enabled)
@@ -597,6 +621,16 @@ static void efx_reconfigure_work(struct work_struct *data)
mutex_unlock(&efx->mac_lock);
}
+static void efx_mac_work(struct work_struct *data)
+{
+ struct efx_nic *efx = container_of(data, struct efx_nic, mac_work);
+
+ mutex_lock(&efx->mac_lock);
+ if (efx->port_enabled)
+ efx->mac_op->irq(efx);
+ mutex_unlock(&efx->mac_lock);
+}
+
static int efx_probe_port(struct efx_nic *efx)
{
int rc;
@@ -608,21 +642,22 @@ static int efx_probe_port(struct efx_nic *efx)
if (rc)
goto err;
+ if (phy_flash_cfg)
+ efx->phy_mode = PHY_MODE_SPECIAL;
+
/* Sanity check MAC address */
if (is_valid_ether_addr(efx->mac_address)) {
memcpy(efx->net_dev->dev_addr, efx->mac_address, ETH_ALEN);
} else {
- DECLARE_MAC_BUF(mac);
-
- EFX_ERR(efx, "invalid MAC address %s\n",
- print_mac(mac, efx->mac_address));
+ EFX_ERR(efx, "invalid MAC address %pM\n",
+ efx->mac_address);
if (!allow_bad_hwaddr) {
rc = -EINVAL;
goto err;
}
random_ether_addr(efx->net_dev->dev_addr);
- EFX_INFO(efx, "using locally-generated MAC %s\n",
- print_mac(mac, efx->net_dev->dev_addr));
+ EFX_INFO(efx, "using locally-generated MAC %pM\n",
+ efx->net_dev->dev_addr);
}
return 0;
@@ -638,23 +673,30 @@ static int efx_init_port(struct efx_nic *efx)
EFX_LOG(efx, "init port\n");
- /* Initialise the MAC and PHY */
- rc = falcon_init_xmac(efx);
+ rc = efx->phy_op->init(efx);
if (rc)
return rc;
+ efx->phy_op->reconfigure(efx);
+
+ mutex_lock(&efx->mac_lock);
+ rc = falcon_switch_mac(efx);
+ mutex_unlock(&efx->mac_lock);
+ if (rc)
+ goto fail;
+ efx->mac_op->reconfigure(efx);
efx->port_initialized = true;
efx->stats_enabled = true;
-
- /* Reconfigure port to program MAC registers */
- falcon_reconfigure_xmac(efx);
-
return 0;
+
+fail:
+ efx->phy_op->fini(efx);
+ return rc;
}
/* Allow efx_reconfigure_port() to be scheduled, and close the window
* between efx_stop_port and efx_flush_all whereby a previously scheduled
- * efx_reconfigure_port() may have been cancelled */
+ * efx_phy_work()/efx_mac_work() may have been cancelled */
static void efx_start_port(struct efx_nic *efx)
{
EFX_LOG(efx, "start port\n");
@@ -663,13 +705,14 @@ static void efx_start_port(struct efx_nic *efx)
mutex_lock(&efx->mac_lock);
efx->port_enabled = true;
__efx_reconfigure_port(efx);
+ efx->mac_op->irq(efx);
mutex_unlock(&efx->mac_lock);
}
-/* Prevent efx_reconfigure_work and efx_monitor() from executing, and
- * efx_set_multicast_list() from scheduling efx_reconfigure_work.
- * efx_reconfigure_work can still be scheduled via NAPI processing
- * until efx_flush_all() is called */
+/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing,
+ * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work
+ * and efx_mac_work may still be scheduled via NAPI processing until
+ * efx_flush_all() is called */
static void efx_stop_port(struct efx_nic *efx)
{
EFX_LOG(efx, "stop port\n");
@@ -692,7 +735,7 @@ static void efx_fini_port(struct efx_nic *efx)
if (!efx->port_initialized)
return;
- falcon_fini_xmac(efx);
+ efx->phy_op->fini(efx);
efx->port_initialized = false;
efx->link_up = false;
@@ -840,26 +883,33 @@ static void efx_probe_interrupts(struct efx_nic *efx)
if (efx->interrupt_mode == EFX_INT_MODE_MSIX) {
struct msix_entry xentries[EFX_MAX_CHANNELS];
int wanted_ints;
+ int rx_queues;
/* We want one RX queue and interrupt per CPU package
* (or as specified by the rss_cpus module parameter).
* We will need one channel per interrupt.
*/
- wanted_ints = rss_cpus ? rss_cpus : efx_wanted_rx_queues();
- efx->n_rx_queues = min(wanted_ints, max_channels);
+ rx_queues = rss_cpus ? rss_cpus : efx_wanted_rx_queues();
+ wanted_ints = rx_queues + (separate_tx_channels ? 1 : 0);
+ wanted_ints = min(wanted_ints, max_channels);
- for (i = 0; i < efx->n_rx_queues; i++)
+ for (i = 0; i < wanted_ints; i++)
xentries[i].entry = i;
- rc = pci_enable_msix(efx->pci_dev, xentries, efx->n_rx_queues);
+ rc = pci_enable_msix(efx->pci_dev, xentries, wanted_ints);
if (rc > 0) {
- EFX_BUG_ON_PARANOID(rc >= efx->n_rx_queues);
- efx->n_rx_queues = rc;
+ EFX_ERR(efx, "WARNING: Insufficient MSI-X vectors"
+ " available (%d < %d).\n", rc, wanted_ints);
+ EFX_ERR(efx, "WARNING: Performance may be reduced.\n");
+ EFX_BUG_ON_PARANOID(rc >= wanted_ints);
+ wanted_ints = rc;
rc = pci_enable_msix(efx->pci_dev, xentries,
- efx->n_rx_queues);
+ wanted_ints);
}
if (rc == 0) {
- for (i = 0; i < efx->n_rx_queues; i++)
+ efx->n_rx_queues = min(rx_queues, wanted_ints);
+ efx->n_channels = wanted_ints;
+ for (i = 0; i < wanted_ints; i++)
efx->channel[i].irq = xentries[i].vector;
} else {
/* Fall back to single channel MSI */
@@ -871,6 +921,7 @@ static void efx_probe_interrupts(struct efx_nic *efx)
/* Try single interrupt MSI */
if (efx->interrupt_mode == EFX_INT_MODE_MSI) {
efx->n_rx_queues = 1;
+ efx->n_channels = 1;
rc = pci_enable_msi(efx->pci_dev);
if (rc == 0) {
efx->channel[0].irq = efx->pci_dev->irq;
@@ -883,6 +934,7 @@ static void efx_probe_interrupts(struct efx_nic *efx)
/* Assume legacy interrupts */
if (efx->interrupt_mode == EFX_INT_MODE_LEGACY) {
efx->n_rx_queues = 1;
+ efx->n_channels = 1 + (separate_tx_channels ? 1 : 0);
efx->legacy_irq = efx->pci_dev->irq;
}
}
@@ -907,8 +959,8 @@ static void efx_set_channels(struct efx_nic *efx)
struct efx_rx_queue *rx_queue;
efx_for_each_tx_queue(tx_queue, efx) {
- if (!EFX_INT_MODE_USE_MSI(efx) && separate_tx_and_rx_channels)
- tx_queue->channel = &efx->channel[1];
+ if (separate_tx_channels)
+ tx_queue->channel = &efx->channel[efx->n_channels-1];
else
tx_queue->channel = &efx->channel[0];
tx_queue->channel->used_flags |= EFX_USED_BY_TX;
@@ -985,6 +1037,7 @@ static int efx_probe_all(struct efx_nic *efx)
goto fail3;
}
}
+ efx_set_channel_names(efx);
return 0;
@@ -1050,7 +1103,8 @@ static void efx_flush_all(struct efx_nic *efx)
cancel_delayed_work_sync(&rx_queue->work);
/* Stop scheduled port reconfigurations */
- cancel_work_sync(&efx->reconfigure_work);
+ cancel_work_sync(&efx->mac_work);
+ cancel_work_sync(&efx->phy_work);
}
@@ -1087,7 +1141,7 @@ static void efx_stop_all(struct efx_nic *efx)
* window to loose phy events */
efx_stop_port(efx);
- /* Flush reconfigure_work, refill_workqueue, monitor_work */
+ /* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */
efx_flush_all(efx);
/* Isolate the MAC from the TX and RX engines, so that queue
@@ -1159,36 +1213,31 @@ static void efx_monitor(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic,
monitor_work.work);
- int rc = 0;
+ int rc;
EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
raw_smp_processor_id());
-
/* If the mac_lock is already held then it is likely a port
* reconfiguration is already in place, which will likely do
* most of the work of check_hw() anyway. */
- if (!mutex_trylock(&efx->mac_lock)) {
- queue_delayed_work(efx->workqueue, &efx->monitor_work,
- efx_monitor_interval);
- return;
- }
-
- if (efx->port_enabled)
- rc = falcon_check_xmac(efx);
- mutex_unlock(&efx->mac_lock);
-
+ if (!mutex_trylock(&efx->mac_lock))
+ goto out_requeue;
+ if (!efx->port_enabled)
+ goto out_unlock;
+ rc = efx->board_info.monitor(efx);
if (rc) {
- if (monitor_reset) {
- EFX_ERR(efx, "hardware monitor detected a fault: "
- "triggering reset\n");
- efx_schedule_reset(efx, RESET_TYPE_MONITOR);
- } else {
- EFX_ERR(efx, "hardware monitor detected a fault, "
- "skipping reset\n");
- }
+ EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
+ (rc == -ERANGE) ? "reported fault" : "failed");
+ efx->phy_mode |= PHY_MODE_LOW_POWER;
+ falcon_sim_phy_event(efx);
}
+ efx->phy_op->poll(efx);
+ efx->mac_op->poll(efx);
+out_unlock:
+ mutex_unlock(&efx->mac_lock);
+out_requeue:
queue_delayed_work(efx->workqueue, &efx->monitor_work,
efx_monitor_interval);
}
@@ -1282,6 +1331,8 @@ static int efx_net_open(struct net_device *net_dev)
EFX_LOG(efx, "opening device %s on CPU %d\n", net_dev->name,
raw_smp_processor_id());
+ if (efx->state == STATE_DISABLED)
+ return -EIO;
if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY;
@@ -1300,10 +1351,12 @@ static int efx_net_stop(struct net_device *net_dev)
EFX_LOG(efx, "closing %s on CPU %d\n", net_dev->name,
raw_smp_processor_id());
- /* Stop the device and flush all the channels */
- efx_stop_all(efx);
- efx_fini_channels(efx);
- efx_init_channels(efx);
+ if (efx->state != STATE_DISABLED) {
+ /* Stop the device and flush all the channels */
+ efx_stop_all(efx);
+ efx_fini_channels(efx);
+ efx_init_channels(efx);
+ }
return 0;
}
@@ -1322,7 +1375,7 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
if (!spin_trylock(&efx->stats_lock))
return stats;
if (efx->stats_enabled) {
- falcon_update_stats_xmac(efx);
+ efx->mac_op->update_stats(efx);
falcon_update_nic_stats(efx);
}
spin_unlock(&efx->stats_lock);
@@ -1360,12 +1413,11 @@ static void efx_watchdog(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
- EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n",
- atomic_read(&efx->netif_stop_count), efx->port_enabled,
- monitor_reset ? "resetting channels" : "skipping reset");
+ EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d:"
+ " resetting channels\n",
+ atomic_read(&efx->netif_stop_count), efx->port_enabled);
- if (monitor_reset)
- efx_schedule_reset(efx, RESET_TYPE_MONITOR);
+ efx_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG);
}
@@ -1401,9 +1453,8 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
EFX_ASSERT_RESET_SERIALISED(efx);
if (!is_valid_ether_addr(new_addr)) {
- DECLARE_MAC_BUF(mac);
- EFX_ERR(efx, "invalid ethernet MAC address requested: %s\n",
- print_mac(mac, new_addr));
+ EFX_ERR(efx, "invalid ethernet MAC address requested: %pM\n",
+ new_addr);
return -EINVAL;
}
@@ -1447,22 +1498,43 @@ static void efx_set_multicast_list(struct net_device *net_dev)
return;
if (changed)
- queue_work(efx->workqueue, &efx->reconfigure_work);
+ queue_work(efx->workqueue, &efx->phy_work);
/* Create and activate new global multicast hash table */
falcon_set_multicast_hash(efx);
}
+static const struct net_device_ops efx_netdev_ops = {
+ .ndo_open = efx_net_open,
+ .ndo_stop = efx_net_stop,
+ .ndo_get_stats = efx_net_stats,
+ .ndo_tx_timeout = efx_watchdog,
+ .ndo_start_xmit = efx_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = efx_ioctl,
+ .ndo_change_mtu = efx_change_mtu,
+ .ndo_set_mac_address = efx_set_mac_address,
+ .ndo_set_multicast_list = efx_set_multicast_list,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = efx_netpoll,
+#endif
+};
+
+static void efx_update_name(struct efx_nic *efx)
+{
+ strcpy(efx->name, efx->net_dev->name);
+ efx_mtd_rename(efx);
+ efx_set_channel_names(efx);
+}
+
static int efx_netdev_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct net_device *net_dev = ptr;
- if (net_dev->open == efx_net_open && event == NETDEV_CHANGENAME) {
- struct efx_nic *efx = netdev_priv(net_dev);
-
- strcpy(efx->name, net_dev->name);
- }
+ if (net_dev->netdev_ops == &efx_netdev_ops &&
+ event == NETDEV_CHANGENAME)
+ efx_update_name(netdev_priv(net_dev));
return NOTIFY_DONE;
}
@@ -1471,6 +1543,14 @@ static struct notifier_block efx_netdev_notifier = {
.notifier_call = efx_netdev_event,
};
+static ssize_t
+show_phy_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+ return sprintf(buf, "%d\n", efx->phy_type);
+}
+static DEVICE_ATTR(phy_type, 0644, show_phy_type, NULL);
+
static int efx_register_netdev(struct efx_nic *efx)
{
struct net_device *net_dev = efx->net_dev;
@@ -1478,18 +1558,7 @@ static int efx_register_netdev(struct efx_nic *efx)
net_dev->watchdog_timeo = 5 * HZ;
net_dev->irq = efx->pci_dev->irq;
- net_dev->open = efx_net_open;
- net_dev->stop = efx_net_stop;
- net_dev->get_stats = efx_net_stats;
- net_dev->tx_timeout = &efx_watchdog;
- net_dev->hard_start_xmit = efx_hard_start_xmit;
- net_dev->do_ioctl = efx_ioctl;
- net_dev->change_mtu = efx_change_mtu;
- net_dev->set_mac_address = efx_set_mac_address;
- net_dev->set_multicast_list = efx_set_multicast_list;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- net_dev->poll_controller = efx_netpoll;
-#endif
+ net_dev->netdev_ops = &efx_netdev_ops;
SET_NETDEV_DEV(net_dev, &efx->pci_dev->dev);
SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
@@ -1497,7 +1566,7 @@ static int efx_register_netdev(struct efx_nic *efx)
netif_carrier_off(efx->net_dev);
/* Clear MAC statistics */
- falcon_update_stats_xmac(efx);
+ efx->mac_op->update_stats(efx);
memset(&efx->mac_stats, 0, sizeof(efx->mac_stats));
rc = register_netdev(net_dev);
@@ -1505,9 +1574,22 @@ static int efx_register_netdev(struct efx_nic *efx)
EFX_ERR(efx, "could not register net dev\n");
return rc;
}
- strcpy(efx->name, net_dev->name);
+
+ rtnl_lock();
+ efx_update_name(efx);
+ rtnl_unlock();
+
+ rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_type);
+ if (rc) {
+ EFX_ERR(efx, "failed to init net dev attributes\n");
+ goto fail_registered;
+ }
return 0;
+
+fail_registered:
+ unregister_netdev(net_dev);
+ return rc;
}
static void efx_unregister_netdev(struct efx_nic *efx)
@@ -1527,6 +1609,7 @@ static void efx_unregister_netdev(struct efx_nic *efx)
if (efx_dev_registered(efx)) {
strlcpy(efx->name, pci_name(efx->pci_dev), sizeof(efx->name));
+ device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_type);
unregister_netdev(efx->net_dev);
}
}
@@ -1541,8 +1624,6 @@ static void efx_unregister_netdev(struct efx_nic *efx)
* before reset. */
void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
- int rc;
-
EFX_ASSERT_RESET_SERIALISED(efx);
/* The net_dev->get_stats handler is quite slow, and will fail
@@ -1553,10 +1634,9 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
efx_stop_all(efx);
mutex_lock(&efx->mac_lock);
+ mutex_lock(&efx->spi_lock);
- rc = falcon_xmac_get_settings(efx, ecmd);
- if (rc)
- EFX_ERR(efx, "could not back up PHY settings\n");
+ efx->phy_op->get_settings(efx, ecmd);
efx_fini_channels(efx);
}
@@ -1581,10 +1661,11 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok)
if (ok) {
efx_init_channels(efx);
- if (falcon_xmac_set_settings(efx, ecmd))
+ if (efx->phy_op->set_settings(efx, ecmd))
EFX_ERR(efx, "could not restore PHY settings\n");
}
+ mutex_unlock(&efx->spi_lock);
mutex_unlock(&efx->mac_lock);
if (ok) {
@@ -1607,7 +1688,7 @@ static int efx_reset(struct efx_nic *efx)
{
struct ethtool_cmd ecmd;
enum reset_type method = efx->reset_pending;
- int rc;
+ int rc = 0;
/* Serialise with kernel interfaces */
rtnl_lock();
@@ -1616,7 +1697,7 @@ static int efx_reset(struct efx_nic *efx)
* flag set so that efx_pci_probe_main will be retried */
if (efx->state != STATE_RUNNING) {
EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n");
- goto unlock_rtnl;
+ goto out_unlock;
}
EFX_INFO(efx, "resetting (%d)\n", method);
@@ -1626,7 +1707,7 @@ static int efx_reset(struct efx_nic *efx)
rc = falcon_reset_hw(efx, method);
if (rc) {
EFX_ERR(efx, "failed to reset hardware\n");
- goto fail;
+ goto out_disable;
}
/* Allow resets to be rescheduled. */
@@ -1640,28 +1721,23 @@ static int efx_reset(struct efx_nic *efx)
/* Leave device stopped if necessary */
if (method == RESET_TYPE_DISABLE) {
+ efx_reset_up(efx, &ecmd, false);
rc = -EIO;
- goto fail;
+ } else {
+ rc = efx_reset_up(efx, &ecmd, true);
}
- rc = efx_reset_up(efx, &ecmd, true);
- if (rc)
- goto disable;
-
- EFX_LOG(efx, "reset complete\n");
- unlock_rtnl:
- rtnl_unlock();
- return 0;
-
- fail:
- efx_reset_up(efx, &ecmd, false);
- disable:
- EFX_ERR(efx, "has been disabled\n");
- efx->state = STATE_DISABLED;
+out_disable:
+ if (rc) {
+ EFX_ERR(efx, "has been disabled\n");
+ efx->state = STATE_DISABLED;
+ dev_close(efx->net_dev);
+ } else {
+ EFX_LOG(efx, "reset complete\n");
+ }
+out_unlock:
rtnl_unlock();
- efx_unregister_netdev(efx);
- efx_fini_port(efx);
return rc;
}
@@ -1709,7 +1785,7 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
efx->reset_pending = method;
- queue_work(efx->reset_workqueue, &efx->reset_work);
+ queue_work(reset_workqueue, &efx->reset_work);
}
/**************************************************************************
@@ -1743,10 +1819,16 @@ int efx_port_dummy_op_int(struct efx_nic *efx)
void efx_port_dummy_op_void(struct efx_nic *efx) {}
void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {}
+static struct efx_mac_operations efx_dummy_mac_operations = {
+ .reconfigure = efx_port_dummy_op_void,
+ .poll = efx_port_dummy_op_void,
+ .irq = efx_port_dummy_op_void,
+};
+
static struct efx_phy_operations efx_dummy_phy_operations = {
.init = efx_port_dummy_op_int,
.reconfigure = efx_port_dummy_op_void,
- .check_hw = efx_port_dummy_op_int,
+ .poll = efx_port_dummy_op_void,
.fini = efx_port_dummy_op_void,
.clear_interrupt = efx_port_dummy_op_void,
};
@@ -1755,6 +1837,7 @@ static struct efx_board efx_dummy_board_info = {
.init = efx_port_dummy_op_int,
.init_leds = efx_port_dummy_op_int,
.set_fault_led = efx_port_dummy_op_blink,
+ .monitor = efx_port_dummy_op_int,
.blink = efx_port_dummy_op_blink,
.fini = efx_port_dummy_op_void,
};
@@ -1774,12 +1857,13 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
struct efx_channel *channel;
struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue;
- int i, rc;
+ int i;
/* Initialise common structures */
memset(efx, 0, sizeof(*efx));
spin_lock_init(&efx->biu_lock);
spin_lock_init(&efx->phy_lock);
+ mutex_init(&efx->spi_lock);
INIT_WORK(&efx->reset_work, efx_reset_work);
INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor);
efx->pci_dev = pci_dev;
@@ -1793,9 +1877,11 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
spin_lock_init(&efx->netif_stop_lock);
spin_lock_init(&efx->stats_lock);
mutex_init(&efx->mac_lock);
+ efx->mac_op = &efx_dummy_mac_operations;
efx->phy_op = &efx_dummy_phy_operations;
efx->mii.dev = net_dev;
- INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work);
+ INIT_WORK(&efx->phy_work, efx_phy_work);
+ INIT_WORK(&efx->mac_work, efx_mac_work);
atomic_set(&efx->netif_stop_count, 1);
for (i = 0; i < EFX_MAX_CHANNELS; i++) {
@@ -1841,34 +1927,18 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
efx->interrupt_mode = max(efx->type->max_interrupt_mode,
interrupt_mode);
- efx->workqueue = create_singlethread_workqueue("sfc_work");
- if (!efx->workqueue) {
- rc = -ENOMEM;
- goto fail1;
- }
-
- efx->reset_workqueue = create_singlethread_workqueue("sfc_reset");
- if (!efx->reset_workqueue) {
- rc = -ENOMEM;
- goto fail2;
- }
+ /* Would be good to use the net_dev name, but we're too early */
+ snprintf(efx->workqueue_name, sizeof(efx->workqueue_name), "sfc%s",
+ pci_name(pci_dev));
+ efx->workqueue = create_singlethread_workqueue(efx->workqueue_name);
+ if (!efx->workqueue)
+ return -ENOMEM;
return 0;
-
- fail2:
- destroy_workqueue(efx->workqueue);
- efx->workqueue = NULL;
-
- fail1:
- return rc;
}
static void efx_fini_struct(struct efx_nic *efx)
{
- if (efx->reset_workqueue) {
- destroy_workqueue(efx->reset_workqueue);
- efx->reset_workqueue = NULL;
- }
if (efx->workqueue) {
destroy_workqueue(efx->workqueue);
efx->workqueue = NULL;
@@ -1927,11 +1997,13 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
efx_unregister_netdev(efx);
+ efx_mtd_remove(efx);
+
/* Wait for any scheduled resets to complete. No more will be
* scheduled from this point because efx_stop_all() has been
* called, we are no longer registered with driverlink, and
* the net_device's have been removed. */
- flush_workqueue(efx->reset_workqueue);
+ cancel_work_sync(&efx->reset_work);
efx_pci_remove_main(efx);
@@ -1992,6 +2064,7 @@ static int efx_pci_probe_main(struct efx_nic *efx)
efx_fini_port(efx);
fail5:
fail4:
+ efx->board_info.fini(efx);
fail3:
efx_fini_napi(efx);
fail2:
@@ -2045,14 +2118,23 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
* we're in STATE_INIT. */
for (i = 0; i < 5; i++) {
rc = efx_pci_probe_main(efx);
- if (rc == 0)
- break;
/* Serialise against efx_reset(). No more resets will be
* scheduled since efx_stop_all() has been called, and we
* have not and never have been registered with either
* the rtnetlink or driverlink layers. */
- flush_workqueue(efx->reset_workqueue);
+ cancel_work_sync(&efx->reset_work);
+
+ if (rc == 0) {
+ if (efx->reset_pending != RESET_TYPE_NONE) {
+ /* If there was a scheduled reset during
+ * probe, the NIC is probably hosed anyway */
+ efx_pci_remove_main(efx);
+ rc = -EIO;
+ } else {
+ break;
+ }
+ }
/* Retry if a recoverably reset event has been scheduled */
if ((efx->reset_pending != RESET_TYPE_INVISIBLE) &&
@@ -2070,16 +2152,15 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
/* Switch to the running state before we expose the device to
* the OS. This is to ensure that the initial gathering of
* MAC stats succeeds. */
- rtnl_lock();
efx->state = STATE_RUNNING;
- rtnl_unlock();
+
+ efx_mtd_probe(efx); /* allowed to fail */
rc = efx_register_netdev(efx);
if (rc)
goto fail5;
EFX_LOG(efx, "initialisation successful\n");
-
return 0;
fail5:
@@ -2127,6 +2208,11 @@ static int __init efx_init_module(void)
rc = -ENOMEM;
goto err_refill;
}
+ reset_workqueue = create_singlethread_workqueue("sfc_reset");
+ if (!reset_workqueue) {
+ rc = -ENOMEM;
+ goto err_reset;
+ }
rc = pci_register_driver(&efx_pci_driver);
if (rc < 0)
@@ -2135,6 +2221,8 @@ static int __init efx_init_module(void)
return 0;
err_pci:
+ destroy_workqueue(reset_workqueue);
+ err_reset:
destroy_workqueue(refill_workqueue);
err_refill:
unregister_netdevice_notifier(&efx_netdev_notifier);
@@ -2147,6 +2235,7 @@ static void __exit efx_exit_module(void)
printk(KERN_INFO "Solarflare NET driver unloading\n");
pci_unregister_driver(&efx_pci_driver);
+ destroy_workqueue(reset_workqueue);
destroy_workqueue(refill_workqueue);
unregister_netdevice_notifier(&efx_netdev_notifier);
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h
index d02937b70eee..0dd7a532c78a 100644
--- a/drivers/net/sfc/efx.h
+++ b/drivers/net/sfc/efx.h
@@ -58,6 +58,16 @@ extern int efx_port_dummy_op_int(struct efx_nic *efx);
extern void efx_port_dummy_op_void(struct efx_nic *efx);
extern void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink);
+/* MTD */
+#ifdef CONFIG_SFC_MTD
+extern int efx_mtd_probe(struct efx_nic *efx);
+extern void efx_mtd_rename(struct efx_nic *efx);
+extern void efx_mtd_remove(struct efx_nic *efx);
+#else
+static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; }
+static inline void efx_mtd_rename(struct efx_nic *efx) {}
+static inline void efx_mtd_remove(struct efx_nic *efx) {}
+#endif
extern unsigned int efx_monitor_interval;
@@ -67,7 +77,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel)
channel->channel, raw_smp_processor_id());
channel->work_pending = true;
- netif_rx_schedule(channel->napi_dev, &channel->napi_str);
+ netif_rx_schedule(&channel->napi_str);
}
#endif /* EFX_EFX_H */
diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h
index cec15dbb88e4..60cbc6e1e66b 100644
--- a/drivers/net/sfc/enum.h
+++ b/drivers/net/sfc/enum.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -13,22 +13,24 @@
/**
* enum efx_loopback_mode - loopback modes
* @LOOPBACK_NONE: no loopback
- * @LOOPBACK_XGMII: loopback within MAC at XGMII level
- * @LOOPBACK_XGXS: loopback within MAC at XGXS level
- * @LOOPBACK_XAUI: loopback within MAC at XAUI level
- * @LOOPBACK_PHYXS: loopback within PHY at PHYXS level
- * @LOOPBACK_PCS: loopback within PHY at PCS level
- * @LOOPBACK_PMAPMD: loopback within PHY at PMAPMD level
+ * @LOOPBACK_GMAC: loopback within GMAC at unspecified level
+ * @LOOPBACK_XGMII: loopback within XMAC at XGMII level
+ * @LOOPBACK_XGXS: loopback within XMAC at XGXS level
+ * @LOOPBACK_XAUI: loopback within XMAC at XAUI level
+ * @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level
+ * @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level
+ * @LOOPBACK_PCS: loopback within 10G PHY at PCS level
+ * @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level
* @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!)
*/
/* Please keep in order and up-to-date w.r.t the following two #defines */
enum efx_loopback_mode {
LOOPBACK_NONE = 0,
- LOOPBACK_MAC = 1,
+ LOOPBACK_GMAC = 1,
LOOPBACK_XGMII = 2,
LOOPBACK_XGXS = 3,
LOOPBACK_XAUI = 4,
- LOOPBACK_PHY = 5,
+ LOOPBACK_GPHY = 5,
LOOPBACK_PHYXS = 6,
LOOPBACK_PCS = 7,
LOOPBACK_PMAPMD = 8,
@@ -45,15 +47,19 @@ extern const char *efx_loopback_mode_names[];
LOOPBACK_MODE_NAME(efx->loopback_mode)
/* These loopbacks occur within the controller */
-#define LOOPBACKS_10G_INTERNAL ((1 << LOOPBACK_XGMII)| \
- (1 << LOOPBACK_XGXS) | \
- (1 << LOOPBACK_XAUI))
+#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \
+ (1 << LOOPBACK_XGMII)| \
+ (1 << LOOPBACK_XGXS) | \
+ (1 << LOOPBACK_XAUI))
#define LOOPBACK_MASK(_efx) \
(1 << (_efx)->loopback_mode)
#define LOOPBACK_INTERNAL(_efx) \
- (!!(LOOPBACKS_10G_INTERNAL & LOOPBACK_MASK(_efx)))
+ (!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx)))
+
+#define LOOPBACK_CHANGED(_from, _to, _mask) \
+ (!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask)))
#define LOOPBACK_OUT_OF(_from, _to, _mask) \
((LOOPBACK_MASK(_from) & (_mask)) && !(LOOPBACK_MASK(_to) & (_mask)))
@@ -72,7 +78,7 @@ extern const char *efx_loopback_mode_names[];
* @RESET_TYPE_ALL: reset everything but PCI core blocks
* @RESET_TYPE_WORLD: reset everything, save & restore PCI config
* @RESET_TYPE_DISABLE: disable NIC
- * @RESET_TYPE_MONITOR: reset due to hardware monitor
+ * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog
* @RESET_TYPE_INT_ERROR: reset due to internal error
* @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
@@ -86,7 +92,7 @@ enum reset_type {
RESET_TYPE_WORLD = 2,
RESET_TYPE_DISABLE = 3,
RESET_TYPE_MAX_METHOD,
- RESET_TYPE_MONITOR,
+ RESET_TYPE_TX_WATCHDOG,
RESET_TYPE_INT_ERROR,
RESET_TYPE_RX_RECOVERY,
RESET_TYPE_RX_DESC_FETCH,
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index cd0d0873d978..53d259e90187 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -12,24 +12,24 @@
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
+#include "workarounds.h"
#include "selftest.h"
#include "efx.h"
#include "ethtool.h"
#include "falcon.h"
-#include "gmii.h"
#include "spi.h"
-#include "mac.h"
+#include "mdio_10g.h"
const char *efx_loopback_mode_names[] = {
[LOOPBACK_NONE] = "NONE",
- [LOOPBACK_MAC] = "MAC",
+ [LOOPBACK_GMAC] = "GMAC",
[LOOPBACK_XGMII] = "XGMII",
[LOOPBACK_XGXS] = "XGXS",
[LOOPBACK_XAUI] = "XAUI",
- [LOOPBACK_PHY] = "PHY",
- [LOOPBACK_PHYXS] = "PHY(XS)",
- [LOOPBACK_PCS] = "PHY(PCS)",
- [LOOPBACK_PMAPMD] = "PHY(PMAPMD)",
+ [LOOPBACK_GPHY] = "GPHY",
+ [LOOPBACK_PHYXS] = "PHYXS",
+ [LOOPBACK_PCS] = "PCS",
+ [LOOPBACK_PMAPMD] = "PMA/PMD",
[LOOPBACK_NETWORK] = "NETWORK",
};
@@ -172,10 +172,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
/* Number of ethtool statistics */
#define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
-/* EEPROM range with gPXE configuration */
#define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
-#define EFX_ETHTOOL_EEPROM_MIN 0x800U
-#define EFX_ETHTOOL_EEPROM_MAX 0x1800U
/**************************************************************************
*
@@ -185,12 +182,16 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
*/
/* Identify device by flashing LEDs */
-static int efx_ethtool_phys_id(struct net_device *net_dev, u32 seconds)
+static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count)
{
struct efx_nic *efx = netdev_priv(net_dev);
efx->board_info.blink(efx, 1);
- schedule_timeout_interruptible(seconds * HZ);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (count)
+ schedule_timeout(count * HZ);
+ else
+ schedule();
efx->board_info.blink(efx, 0);
return 0;
}
@@ -200,13 +201,15 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
struct ethtool_cmd *ecmd)
{
struct efx_nic *efx = netdev_priv(net_dev);
- int rc;
mutex_lock(&efx->mac_lock);
- rc = falcon_xmac_get_settings(efx, ecmd);
+ efx->phy_op->get_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock);
- return rc;
+ /* Falcon GMAC does not support 1000Mbps HD */
+ ecmd->supported &= ~SUPPORTED_1000baseT_Half;
+
+ return 0;
}
/* This must be called with rtnl_lock held. */
@@ -216,8 +219,18 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
struct efx_nic *efx = netdev_priv(net_dev);
int rc;
+ if (EFX_WORKAROUND_13963(efx) && !ecmd->autoneg)
+ return -EINVAL;
+
+ /* Falcon GMAC does not support 1000Mbps HD */
+ if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) {
+ EFX_LOG(efx, "rejecting unsupported 1000Mbps HD"
+ " setting\n");
+ return -EINVAL;
+ }
+
mutex_lock(&efx->mac_lock);
- rc = falcon_xmac_set_settings(efx, ecmd);
+ rc = efx->phy_op->set_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock);
if (!rc)
efx_reconfigure_port(efx);
@@ -241,10 +254,10 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
* @strings: Ethtool strings, or %NULL
* @data: Ethtool test results, or %NULL
* @test: Pointer to test result (used only if data != %NULL)
- * @unit_format: Unit name format (e.g. "channel\%d")
- * @unit_id: Unit id (e.g. 0 for "channel0")
+ * @unit_format: Unit name format (e.g. "chan\%d")
+ * @unit_id: Unit id (e.g. 0 for "chan0")
* @test_format: Test name format (e.g. "loopback.\%s.tx.sent")
- * @test_id: Test id (e.g. "PHY" for "loopback.PHY.tx_sent")
+ * @test_id: Test id (e.g. "PHYXS" for "loopback.PHYXS.tx_sent")
*
* Fill in an individual self-test entry.
*/
@@ -261,18 +274,20 @@ static void efx_fill_test(unsigned int test_index,
/* Fill string, if applicable */
if (strings) {
- snprintf(unit_str.name, sizeof(unit_str.name),
- unit_format, unit_id);
+ if (strchr(unit_format, '%'))
+ snprintf(unit_str.name, sizeof(unit_str.name),
+ unit_format, unit_id);
+ else
+ strcpy(unit_str.name, unit_format);
snprintf(test_str.name, sizeof(test_str.name),
test_format, test_id);
snprintf(strings[test_index].name,
sizeof(strings[test_index].name),
- "%-9s%-17s", unit_str.name, test_str.name);
+ "%-6s %-24s", unit_str.name, test_str.name);
}
}
-#define EFX_PORT_NAME "port%d", 0
-#define EFX_CHANNEL_NAME(_channel) "channel%d", _channel->channel
+#define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel
#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
#define EFX_LOOPBACK_NAME(_mode, _counter) \
@@ -307,11 +322,11 @@ static int efx_fill_loopback_test(struct efx_nic *efx,
}
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_good,
- EFX_PORT_NAME,
+ "rx", 0,
EFX_LOOPBACK_NAME(mode, "rx_good"));
efx_fill_test(test_index++, strings, data,
&lb_tests->rx_bad,
- EFX_PORT_NAME,
+ "rx", 0,
EFX_LOOPBACK_NAME(mode, "rx_bad"));
return test_index;
@@ -330,7 +345,7 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
u64 *data)
{
struct efx_channel *channel;
- unsigned int n = 0;
+ unsigned int n = 0, i;
enum efx_loopback_mode mode;
efx_fill_test(n++, strings, data, &tests->mii,
@@ -358,14 +373,12 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
efx_fill_test(n++, strings, data, &tests->registers,
"core", 0, "registers", NULL);
- efx_fill_test(n++, strings, data, &tests->phy,
- EFX_PORT_NAME, "phy", NULL);
+
+ for (i = 0; i < efx->phy_op->num_tests; i++)
+ efx_fill_test(n++, strings, data, &tests->phy[i],
+ "phy", 0, efx->phy_op->test_names[i], NULL);
/* Loopback tests */
- efx_fill_test(n++, strings, data, &tests->loopback_speed,
- EFX_PORT_NAME, "loopback.speed", NULL);
- efx_fill_test(n++, strings, data, &tests->loopback_full_duplex,
- EFX_PORT_NAME, "loopback.full_duplex", NULL);
for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
if (!(efx->loopback_modes & (1 << mode)))
continue;
@@ -429,7 +442,7 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS);
/* Update MAC and NIC statistics */
- net_dev->get_stats(net_dev);
+ dev_get_stats(net_dev);
/* Fill detailed statistics buffer */
for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) {
@@ -476,7 +489,7 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
{
struct efx_nic *efx = netdev_priv(net_dev);
struct efx_self_tests efx_tests;
- int offline, already_up;
+ int already_up;
int rc;
ASSERT_RTNL();
@@ -496,24 +509,15 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
}
memset(&efx_tests, 0, sizeof(efx_tests));
- offline = (test->flags & ETH_TEST_FL_OFFLINE);
-
- /* Perform online self tests first */
- rc = efx_online_test(efx, &efx_tests);
- if (rc)
- goto out;
- /* Perform offline tests only if online tests passed */
- if (offline)
- rc = efx_offline_test(efx, &efx_tests,
- efx->loopback_modes);
+ rc = efx_selftest(efx, &efx_tests, test->flags);
- out:
if (!already_up)
dev_close(efx->net_dev);
- EFX_LOG(efx, "%s all %sline self-tests\n",
- rc == 0 ? "passed" : "failed", offline ? "off" : "on");
+ EFX_LOG(efx, "%s %sline self-tests\n",
+ rc == 0 ? "passed" : "failed",
+ (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
fail2:
fail1:
@@ -545,8 +549,8 @@ static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
if (!spi)
return 0;
- return min(spi->size, EFX_ETHTOOL_EEPROM_MAX) -
- min(spi->size, EFX_ETHTOOL_EEPROM_MIN);
+ return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) -
+ min(spi->size, EFX_EEPROM_BOOTCONFIG_START);
}
static int efx_ethtool_get_eeprom(struct net_device *net_dev,
@@ -557,8 +561,13 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
size_t len;
int rc;
- rc = falcon_spi_read(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
+ rc = mutex_lock_interruptible(&efx->spi_lock);
+ if (rc)
+ return rc;
+ rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
+ mutex_unlock(&efx->spi_lock);
+
eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
eeprom->len = len;
return rc;
@@ -575,8 +584,13 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
return -EINVAL;
- rc = falcon_spi_write(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
+ rc = mutex_lock_interruptible(&efx->spi_lock);
+ if (rc)
+ return rc;
+ rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
+ mutex_unlock(&efx->spi_lock);
+
eeprom->len = len;
return rc;
}
@@ -666,23 +680,52 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
struct ethtool_pauseparam *pause)
{
struct efx_nic *efx = netdev_priv(net_dev);
- enum efx_fc_type flow_control = efx->flow_control;
- int rc;
+ enum efx_fc_type wanted_fc;
+ bool reset;
- flow_control &= ~(EFX_FC_RX | EFX_FC_TX | EFX_FC_AUTO);
- flow_control |= pause->rx_pause ? EFX_FC_RX : 0;
- flow_control |= pause->tx_pause ? EFX_FC_TX : 0;
- flow_control |= pause->autoneg ? EFX_FC_AUTO : 0;
+ wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
+ (pause->tx_pause ? EFX_FC_TX : 0) |
+ (pause->autoneg ? EFX_FC_AUTO : 0));
+
+ if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
+ EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
+ return -EINVAL;
+ }
+
+ if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) &&
+ (wanted_fc & EFX_FC_AUTO)) {
+ EFX_LOG(efx, "PHY does not support flow control "
+ "autonegotiation\n");
+ return -EINVAL;
+ }
+
+ /* TX flow control may automatically turn itself off if the
+ * link partner (intermittently) stops responding to pause
+ * frames. There isn't any indication that this has happened,
+ * so the best we do is leave it up to the user to spot this
+ * and fix it be cycling transmit flow control on this end. */
+ reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
+ if (EFX_WORKAROUND_11482(efx) && reset) {
+ if (falcon_rev(efx) >= FALCON_REV_B0) {
+ /* Recover by resetting the EM block */
+ if (efx->link_up)
+ falcon_drain_tx_fifo(efx);
+ } else {
+ /* Schedule a reset to recover */
+ efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
+ }
+ }
/* Try to push the pause parameters */
mutex_lock(&efx->mac_lock);
- rc = falcon_xmac_set_pause(efx, flow_control);
- mutex_unlock(&efx->mac_lock);
- if (!rc)
- efx_reconfigure_port(efx);
+ efx->wanted_fc = wanted_fc;
+ mdio_clause45_set_pause(efx);
+ __efx_reconfigure_port(efx);
- return rc;
+ mutex_unlock(&efx->mac_lock);
+
+ return 0;
}
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
@@ -690,9 +733,9 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
{
struct efx_nic *efx = netdev_priv(net_dev);
- pause->rx_pause = !!(efx->flow_control & EFX_FC_RX);
- pause->tx_pause = !!(efx->flow_control & EFX_FC_TX);
- pause->autoneg = !!(efx->flow_control & EFX_FC_AUTO);
+ pause->rx_pause = !!(efx->wanted_fc & EFX_FC_RX);
+ pause->tx_pause = !!(efx->wanted_fc & EFX_FC_TX);
+ pause->autoneg = !!(efx->wanted_fc & EFX_FC_AUTO);
}
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 31ed1f49de00..6884dc8c1f82 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -15,11 +15,11 @@
#include <linux/seq_file.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
+#include <linux/mii.h>
#include "net_driver.h"
#include "bitfield.h"
#include "efx.h"
#include "mac.h"
-#include "gmii.h"
#include "spi.h"
#include "falcon.h"
#include "falcon_hwdefs.h"
@@ -70,6 +70,20 @@ static int disable_dma_stats;
#define RX_DC_ENTRIES_ORDER 2
#define RX_DC_BASE 0x100000
+static const unsigned int
+/* "Large" EEPROM device: Atmel AT25640 or similar
+ * 8 KB, 16-bit address, 32 B write block */
+large_eeprom_type = ((13 << SPI_DEV_TYPE_SIZE_LBN)
+ | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN)
+ | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN)),
+/* Default flash device: Atmel AT25F1024
+ * 128 KB, 24-bit address, 32 KB erase block, 256 B write block */
+default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN)
+ | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN)
+ | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN)
+ | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN)
+ | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN));
+
/* RX FIFO XOFF watermark
*
* When the amount of the RX FIFO increases used increases past this
@@ -770,15 +784,18 @@ static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue,
rx_ev_buf_owner_id_err | rx_ev_eth_crc_err |
rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err);
- /* Count errors that are not in MAC stats. */
+ /* Count errors that are not in MAC stats. Ignore expected
+ * checksum errors during self-test. */
if (rx_ev_frm_trunc)
++rx_queue->channel->n_rx_frm_trunc;
else if (rx_ev_tobe_disc)
++rx_queue->channel->n_rx_tobe_disc;
- else if (rx_ev_ip_hdr_chksum_err)
- ++rx_queue->channel->n_rx_ip_hdr_chksum_err;
- else if (rx_ev_tcp_udp_chksum_err)
- ++rx_queue->channel->n_rx_tcp_udp_chksum_err;
+ else if (!efx->loopback_selftest) {
+ if (rx_ev_ip_hdr_chksum_err)
+ ++rx_queue->channel->n_rx_ip_hdr_chksum_err;
+ else if (rx_ev_tcp_udp_chksum_err)
+ ++rx_queue->channel->n_rx_tcp_udp_chksum_err;
+ }
if (rx_ev_ip_frag_err)
++rx_queue->channel->n_rx_ip_frag_err;
@@ -809,7 +826,7 @@ static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue,
#endif
if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) &&
- efx->phy_type == PHY_TYPE_10XPRESS))
+ efx->phy_type == PHY_TYPE_SFX7101))
tenxpress_crc_err(efx);
}
@@ -893,22 +910,20 @@ static void falcon_handle_global_event(struct efx_channel *channel,
efx_qword_t *event)
{
struct efx_nic *efx = channel->efx;
- bool is_phy_event = false, handled = false;
+ bool handled = false;
- /* Check for interrupt on either port. Some boards have a
- * single PHY wired to the interrupt line for port 1. */
if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) ||
EFX_QWORD_FIELD(*event, G_PHY1_INTR) ||
- EFX_QWORD_FIELD(*event, XG_PHY_INTR))
- is_phy_event = true;
+ EFX_QWORD_FIELD(*event, XG_PHY_INTR) ||
+ EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) {
+ efx->phy_op->clear_interrupt(efx);
+ queue_work(efx->workqueue, &efx->phy_work);
+ handled = true;
+ }
if ((falcon_rev(efx) >= FALCON_REV_B0) &&
- EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0))
- is_phy_event = true;
-
- if (is_phy_event) {
- efx->phy_op->clear_interrupt(efx);
- queue_work(efx->workqueue, &efx->reconfigure_work);
+ EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) {
+ queue_work(efx->workqueue, &efx->mac_work);
handled = true;
}
@@ -1151,6 +1166,19 @@ void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic)
falcon_generate_event(channel, &test_event);
}
+void falcon_sim_phy_event(struct efx_nic *efx)
+{
+ efx_qword_t phy_event;
+
+ EFX_POPULATE_QWORD_1(phy_event, EV_CODE, GLOBAL_EV_DECODE);
+ if (EFX_IS10G(efx))
+ EFX_SET_OWORD_FIELD(phy_event, XG_PHY_INTR, 1);
+ else
+ EFX_SET_OWORD_FIELD(phy_event, G_PHY0_INTR, 1);
+
+ falcon_generate_event(&efx->channel[0], &phy_event);
+}
+
/**************************************************************************
*
* Flush handling
@@ -1560,7 +1588,7 @@ int falcon_init_interrupt(struct efx_nic *efx)
efx_for_each_channel(channel, efx) {
rc = request_irq(channel->irq, falcon_msi_interrupt,
IRQF_PROBE_SHARED, /* Not shared */
- efx->name, channel);
+ channel->name, channel);
if (rc) {
EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq);
goto fail2;
@@ -1605,32 +1633,45 @@ void falcon_fini_interrupt(struct efx_nic *efx)
**************************************************************************
*/
-#define FALCON_SPI_MAX_LEN ((unsigned) sizeof(efx_oword_t))
+#define FALCON_SPI_MAX_LEN sizeof(efx_oword_t)
+
+static int falcon_spi_poll(struct efx_nic *efx)
+{
+ efx_oword_t reg;
+ falcon_read(efx, &reg, EE_SPI_HCMD_REG_KER);
+ return EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0;
+}
/* Wait for SPI command completion */
static int falcon_spi_wait(struct efx_nic *efx)
{
- unsigned long timeout = jiffies + DIV_ROUND_UP(HZ, 10);
- efx_oword_t reg;
- bool cmd_en, timer_active;
+ /* Most commands will finish quickly, so we start polling at
+ * very short intervals. Sometimes the command may have to
+ * wait for VPD or expansion ROM access outside of our
+ * control, so we allow up to 100 ms. */
+ unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 10);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ if (!falcon_spi_poll(efx))
+ return 0;
+ udelay(10);
+ }
for (;;) {
- falcon_read(efx, &reg, EE_SPI_HCMD_REG_KER);
- cmd_en = EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN);
- timer_active = EFX_OWORD_FIELD(reg, EE_WR_TIMER_ACTIVE);
- if (!cmd_en && !timer_active)
+ if (!falcon_spi_poll(efx))
return 0;
if (time_after_eq(jiffies, timeout)) {
EFX_ERR(efx, "timed out waiting for SPI\n");
return -ETIMEDOUT;
}
- cpu_relax();
+ schedule_timeout_uninterruptible(1);
}
}
-static int falcon_spi_cmd(const struct efx_spi_device *spi,
- unsigned int command, int address,
- const void *in, void *out, unsigned int len)
+int falcon_spi_cmd(const struct efx_spi_device *spi,
+ unsigned int command, int address,
+ const void *in, void *out, size_t len)
{
struct efx_nic *efx = spi->efx;
bool addressed = (address >= 0);
@@ -1641,9 +1682,10 @@ static int falcon_spi_cmd(const struct efx_spi_device *spi,
/* Input validation */
if (len > FALCON_SPI_MAX_LEN)
return -EINVAL;
+ BUG_ON(!mutex_is_locked(&efx->spi_lock));
- /* Check SPI not currently being accessed */
- rc = falcon_spi_wait(efx);
+ /* Check that previous command is not still running */
+ rc = falcon_spi_poll(efx);
if (rc)
return rc;
@@ -1685,8 +1727,8 @@ static int falcon_spi_cmd(const struct efx_spi_device *spi,
return 0;
}
-static unsigned int
-falcon_spi_write_limit(const struct efx_spi_device *spi, unsigned int start)
+static size_t
+falcon_spi_write_limit(const struct efx_spi_device *spi, size_t start)
{
return min(FALCON_SPI_MAX_LEN,
(spi->block_size - (start & (spi->block_size - 1))));
@@ -1699,38 +1741,40 @@ efx_spi_munge_command(const struct efx_spi_device *spi,
return command | (((address >> 8) & spi->munge_address) << 3);
}
-
-static int falcon_spi_fast_wait(const struct efx_spi_device *spi)
+/* Wait up to 10 ms for buffered write completion */
+int falcon_spi_wait_write(const struct efx_spi_device *spi)
{
+ struct efx_nic *efx = spi->efx;
+ unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100);
u8 status;
- int i, rc;
-
- /* Wait up to 1000us for flash/EEPROM to finish a fast operation. */
- for (i = 0; i < 50; i++) {
- udelay(20);
+ int rc;
+ for (;;) {
rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
&status, sizeof(status));
if (rc)
return rc;
if (!(status & SPI_STATUS_NRDY))
return 0;
+ if (time_after_eq(jiffies, timeout)) {
+ EFX_ERR(efx, "SPI write timeout on device %d"
+ " last status=0x%02x\n",
+ spi->device_id, status);
+ return -ETIMEDOUT;
+ }
+ schedule_timeout_uninterruptible(1);
}
- EFX_ERR(spi->efx,
- "timed out waiting for device %d last status=0x%02x\n",
- spi->device_id, status);
- return -ETIMEDOUT;
}
int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer)
{
- unsigned int command, block_len, pos = 0;
+ size_t block_len, pos = 0;
+ unsigned int command;
int rc = 0;
while (pos < len) {
- block_len = min((unsigned int)len - pos,
- FALCON_SPI_MAX_LEN);
+ block_len = min(len - pos, FALCON_SPI_MAX_LEN);
command = efx_spi_munge_command(spi, SPI_READ, start + pos);
rc = falcon_spi_cmd(spi, command, start + pos, NULL,
@@ -1756,7 +1800,8 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
u8 verify_buffer[FALCON_SPI_MAX_LEN];
- unsigned int command, block_len, pos = 0;
+ size_t block_len, pos = 0;
+ unsigned int command;
int rc = 0;
while (pos < len) {
@@ -1764,7 +1809,7 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
if (rc)
break;
- block_len = min((unsigned int)len - pos,
+ block_len = min(len - pos,
falcon_spi_write_limit(spi, start + pos));
command = efx_spi_munge_command(spi, SPI_WRITE, start + pos);
rc = falcon_spi_cmd(spi, command, start + pos,
@@ -1772,7 +1817,7 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
if (rc)
break;
- rc = falcon_spi_fast_wait(spi);
+ rc = falcon_spi_wait_write(spi);
if (rc)
break;
@@ -1805,40 +1850,61 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
*
**************************************************************************
*/
-void falcon_drain_tx_fifo(struct efx_nic *efx)
+
+static int falcon_reset_macs(struct efx_nic *efx)
{
- efx_oword_t temp;
+ efx_oword_t reg;
int count;
- if ((falcon_rev(efx) < FALCON_REV_B0) ||
- (efx->loopback_mode != LOOPBACK_NONE))
- return;
+ if (falcon_rev(efx) < FALCON_REV_B0) {
+ /* It's not safe to use GLB_CTL_REG to reset the
+ * macs, so instead use the internal MAC resets
+ */
+ if (!EFX_IS10G(efx)) {
+ EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 1);
+ falcon_write(efx, &reg, GM_CFG1_REG);
+ udelay(1000);
+
+ EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0);
+ falcon_write(efx, &reg, GM_CFG1_REG);
+ udelay(1000);
+ return 0;
+ } else {
+ EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
+ falcon_write(efx, &reg, XM_GLB_CFG_REG);
+
+ for (count = 0; count < 10000; count++) {
+ falcon_read(efx, &reg, XM_GLB_CFG_REG);
+ if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
+ return 0;
+ udelay(10);
+ }
- falcon_read(efx, &temp, MAC0_CTRL_REG_KER);
- /* There is no point in draining more than once */
- if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0))
- return;
+ EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
+ return -ETIMEDOUT;
+ }
+ }
/* MAC stats will fail whilst the TX fifo is draining. Serialise
* the drain sequence with the statistics fetch */
spin_lock(&efx->stats_lock);
- EFX_SET_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0, 1);
- falcon_write(efx, &temp, MAC0_CTRL_REG_KER);
+ falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+ EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1);
+ falcon_write(efx, &reg, MAC0_CTRL_REG_KER);
- /* Reset the MAC and EM block. */
- falcon_read(efx, &temp, GLB_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(temp, RST_XGTX, 1);
- EFX_SET_OWORD_FIELD(temp, RST_XGRX, 1);
- EFX_SET_OWORD_FIELD(temp, RST_EM, 1);
- falcon_write(efx, &temp, GLB_CTL_REG_KER);
+ falcon_read(efx, &reg, GLB_CTL_REG_KER);
+ EFX_SET_OWORD_FIELD(reg, RST_XGTX, 1);
+ EFX_SET_OWORD_FIELD(reg, RST_XGRX, 1);
+ EFX_SET_OWORD_FIELD(reg, RST_EM, 1);
+ falcon_write(efx, &reg, GLB_CTL_REG_KER);
count = 0;
while (1) {
- falcon_read(efx, &temp, GLB_CTL_REG_KER);
- if (!EFX_OWORD_FIELD(temp, RST_XGTX) &&
- !EFX_OWORD_FIELD(temp, RST_XGRX) &&
- !EFX_OWORD_FIELD(temp, RST_EM)) {
+ falcon_read(efx, &reg, GLB_CTL_REG_KER);
+ if (!EFX_OWORD_FIELD(reg, RST_XGTX) &&
+ !EFX_OWORD_FIELD(reg, RST_XGRX) &&
+ !EFX_OWORD_FIELD(reg, RST_EM)) {
EFX_LOG(efx, "Completed MAC reset after %d loops\n",
count);
break;
@@ -1855,21 +1921,39 @@ void falcon_drain_tx_fifo(struct efx_nic *efx)
/* If we've reset the EM block and the link is up, then
* we'll have to kick the XAUI link so the PHY can recover */
- if (efx->link_up && EFX_WORKAROUND_5147(efx))
+ if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx))
falcon_reset_xaui(efx);
+
+ return 0;
+}
+
+void falcon_drain_tx_fifo(struct efx_nic *efx)
+{
+ efx_oword_t reg;
+
+ if ((falcon_rev(efx) < FALCON_REV_B0) ||
+ (efx->loopback_mode != LOOPBACK_NONE))
+ return;
+
+ falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+ /* There is no point in draining more than once */
+ if (EFX_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0))
+ return;
+
+ falcon_reset_macs(efx);
}
void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
{
- efx_oword_t temp;
+ efx_oword_t reg;
if (falcon_rev(efx) < FALCON_REV_B0)
return;
/* Isolate the MAC -> RX */
- falcon_read(efx, &temp, RX_CFG_REG_KER);
- EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 0);
- falcon_write(efx, &temp, RX_CFG_REG_KER);
+ falcon_read(efx, &reg, RX_CFG_REG_KER);
+ EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0);
+ falcon_write(efx, &reg, RX_CFG_REG_KER);
if (!efx->link_up)
falcon_drain_tx_fifo(efx);
@@ -1881,14 +1965,12 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
int link_speed;
bool tx_fc;
- if (efx->link_options & GM_LPA_10000)
- link_speed = 0x3;
- else if (efx->link_options & GM_LPA_1000)
- link_speed = 0x2;
- else if (efx->link_options & GM_LPA_100)
- link_speed = 0x1;
- else
- link_speed = 0x0;
+ switch (efx->link_speed) {
+ case 10000: link_speed = 3; break;
+ case 1000: link_speed = 2; break;
+ case 100: link_speed = 1; break;
+ default: link_speed = 0; break;
+ }
/* MAC_LINK_STATUS controls MAC backpressure but doesn't work
* as advertised. Disable to ensure packets are not
* indefinitely held and TX queue can be flushed at any point
@@ -1914,7 +1996,7 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
/* Transmission of pause frames when RX crosses the threshold is
* covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL.
* Action on receipt of pause frames is controller by XM_DIS_FCNTL */
- tx_fc = !!(efx->flow_control & EFX_FC_TX);
+ tx_fc = !!(efx->link_fc & EFX_FC_TX);
falcon_read(efx, &reg, RX_CFG_REG_KER);
EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc);
@@ -1998,7 +2080,8 @@ static int falcon_gmii_wait(struct efx_nic *efx)
efx_dword_t md_stat;
int count;
- for (count = 0; count < 1000; count++) { /* wait upto 10ms */
+ /* wait upto 50ms - taken max from datasheet */
+ for (count = 0; count < 5000; count++) {
falcon_readl(efx, &md_stat, MD_STAT_REG_KER);
if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) {
if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 ||
@@ -2162,10 +2245,14 @@ static void falcon_init_mdio(struct mii_if_info *gmii)
static int falcon_probe_phy(struct efx_nic *efx)
{
switch (efx->phy_type) {
- case PHY_TYPE_10XPRESS:
- efx->phy_op = &falcon_tenxpress_phy_ops;
+ case PHY_TYPE_SFX7101:
+ efx->phy_op = &falcon_sfx7101_phy_ops;
+ break;
+ case PHY_TYPE_SFT9001A:
+ case PHY_TYPE_SFT9001B:
+ efx->phy_op = &falcon_sft9001_phy_ops;
break;
- case PHY_TYPE_XFP:
+ case PHY_TYPE_QT2022C2:
efx->phy_op = &falcon_xfp_phy_ops;
break;
default:
@@ -2174,10 +2261,59 @@ static int falcon_probe_phy(struct efx_nic *efx)
return -1;
}
- efx->loopback_modes = LOOPBACKS_10G_INTERNAL | efx->phy_op->loopbacks;
+ if (efx->phy_op->macs & EFX_XMAC)
+ efx->loopback_modes |= ((1 << LOOPBACK_XGMII) |
+ (1 << LOOPBACK_XGXS) |
+ (1 << LOOPBACK_XAUI));
+ if (efx->phy_op->macs & EFX_GMAC)
+ efx->loopback_modes |= (1 << LOOPBACK_GMAC);
+ efx->loopback_modes |= efx->phy_op->loopbacks;
+
return 0;
}
+int falcon_switch_mac(struct efx_nic *efx)
+{
+ struct efx_mac_operations *old_mac_op = efx->mac_op;
+ efx_oword_t nic_stat;
+ unsigned strap_val;
+
+ /* Internal loopbacks override the phy speed setting */
+ if (efx->loopback_mode == LOOPBACK_GMAC) {
+ efx->link_speed = 1000;
+ efx->link_fd = true;
+ } else if (LOOPBACK_INTERNAL(efx)) {
+ efx->link_speed = 10000;
+ efx->link_fd = true;
+ }
+
+ efx->mac_op = (EFX_IS10G(efx) ?
+ &falcon_xmac_operations : &falcon_gmac_operations);
+ if (old_mac_op == efx->mac_op)
+ return 0;
+
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+ /* Not all macs support a mac-level link state */
+ efx->mac_up = true;
+
+ falcon_read(efx, &nic_stat, NIC_STAT_REG);
+ strap_val = EFX_IS10G(efx) ? 5 : 3;
+ if (falcon_rev(efx) >= FALCON_REV_B0) {
+ EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_EN, 1);
+ EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_OVR, strap_val);
+ falcon_write(efx, &nic_stat, NIC_STAT_REG);
+ } else {
+ /* Falcon A1 does not support 1G/10G speed switching
+ * and must not be used with a PHY that does. */
+ BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val);
+ }
+
+
+ EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G');
+ return falcon_reset_macs(efx);
+}
+
/* This call is responsible for hooking in the MAC and PHY operations */
int falcon_probe_port(struct efx_nic *efx)
{
@@ -2194,9 +2330,9 @@ int falcon_probe_port(struct efx_nic *efx)
/* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */
if (falcon_rev(efx) >= FALCON_REV_B0)
- efx->flow_control = EFX_FC_RX | EFX_FC_TX;
+ efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
else
- efx->flow_control = EFX_FC_RX;
+ efx->wanted_fc = EFX_FC_RX;
/* Allocate buffer for stats */
rc = falcon_alloc_buffer(efx, &efx->stats_buffer,
@@ -2253,13 +2389,18 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
__le16 *word, *limit;
u32 csum;
- region = kmalloc(NVCONFIG_END, GFP_KERNEL);
+ spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
+ if (!spi)
+ return -EINVAL;
+
+ region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL);
if (!region)
return -ENOMEM;
nvconfig = region + NVCONFIG_OFFSET;
- spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
- rc = falcon_spi_read(spi, 0, NVCONFIG_END, NULL, region);
+ mutex_lock(&efx->spi_lock);
+ rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
+ mutex_unlock(&efx->spi_lock);
if (rc) {
EFX_ERR(efx, "Failed to read %s\n",
efx->spi_flash ? "flash" : "EEPROM");
@@ -2283,7 +2424,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
limit = (__le16 *) (nvconfig + 1);
} else {
word = region;
- limit = region + NVCONFIG_END;
+ limit = region + FALCON_NVCONFIG_END;
}
for (csum = 0; word < limit; ++word)
csum += le16_to_cpu(*word);
@@ -2325,6 +2466,10 @@ static struct {
EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) },
{ DP_CTRL_REG,
EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) },
+ { GM_CFG2_REG,
+ EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) },
+ { GMF_CFG0_REG,
+ EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) },
{ XM_GLB_CFG_REG,
EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) },
{ XM_TX_CFG_REG,
@@ -2545,7 +2690,7 @@ static int falcon_spi_device_init(struct efx_nic *efx,
struct efx_spi_device *spi_device;
if (device_type != 0) {
- spi_device = kmalloc(sizeof(*spi_device), GFP_KERNEL);
+ spi_device = kzalloc(sizeof(*spi_device), GFP_KERNEL);
if (!spi_device)
return -ENOMEM;
spi_device->device_id = device_id;
@@ -2555,6 +2700,11 @@ static int falcon_spi_device_init(struct efx_nic *efx,
SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN);
spi_device->munge_address = (spi_device->size == 1 << 9 &&
spi_device->addr_len == 1);
+ spi_device->erase_command =
+ SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD);
+ spi_device->erase_size =
+ 1 << SPI_DEV_TYPE_FIELD(device_type,
+ SPI_DEV_TYPE_ERASE_SIZE);
spi_device->block_size =
1 << SPI_DEV_TYPE_FIELD(device_type,
SPI_DEV_TYPE_BLOCK_SIZE);
@@ -2645,6 +2795,7 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
static int falcon_probe_nic_variant(struct efx_nic *efx)
{
efx_oword_t altera_build;
+ efx_oword_t nic_stat;
falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER);
if (EFX_OWORD_FIELD(altera_build, VER_ALL)) {
@@ -2652,27 +2803,20 @@ static int falcon_probe_nic_variant(struct efx_nic *efx)
return -ENODEV;
}
+ falcon_read(efx, &nic_stat, NIC_STAT_REG);
+
switch (falcon_rev(efx)) {
case FALCON_REV_A0:
case 0xff:
EFX_ERR(efx, "Falcon rev A0 not supported\n");
return -ENODEV;
- case FALCON_REV_A1:{
- efx_oword_t nic_stat;
-
- falcon_read(efx, &nic_stat, NIC_STAT_REG);
-
+ case FALCON_REV_A1:
if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) {
EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
return -ENODEV;
}
- if (!EFX_OWORD_FIELD(nic_stat, STRAP_10G)) {
- EFX_ERR(efx, "1G mode not supported\n");
- return -ENODEV;
- }
break;
- }
case FALCON_REV_B0:
break;
@@ -2682,6 +2826,9 @@ static int falcon_probe_nic_variant(struct efx_nic *efx)
return -ENODEV;
}
+ /* Initial assumed speed */
+ efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000;
+
return 0;
}
@@ -2689,80 +2836,37 @@ static int falcon_probe_nic_variant(struct efx_nic *efx)
static void falcon_probe_spi_devices(struct efx_nic *efx)
{
efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg;
- bool has_flash, has_eeprom, boot_is_external;
+ int boot_dev;
falcon_read(efx, &gpio_ctl, GPIO_CTL_REG_KER);
falcon_read(efx, &nic_stat, NIC_STAT_REG);
falcon_read(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
- has_flash = EFX_OWORD_FIELD(nic_stat, SF_PRST);
- has_eeprom = EFX_OWORD_FIELD(nic_stat, EE_PRST);
- boot_is_external = EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE);
-
- if (has_flash) {
- /* Default flash SPI device: Atmel AT25F1024
- * 128 KB, 24-bit address, 32 KB erase block,
- * 256 B write block
- */
- u32 flash_device_type =
- (17 << SPI_DEV_TYPE_SIZE_LBN)
- | (3 << SPI_DEV_TYPE_ADDR_LEN_LBN)
- | (0x52 << SPI_DEV_TYPE_ERASE_CMD_LBN)
- | (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN)
- | (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
-
- falcon_spi_device_init(efx, &efx->spi_flash,
- EE_SPI_FLASH, flash_device_type);
-
- if (!boot_is_external) {
- /* Disable VPD and set clock dividers to safe
- * values for initial programming.
- */
- EFX_LOG(efx, "Booted from internal ASIC settings;"
- " setting SPI config\n");
- EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0,
- /* 125 MHz / 7 ~= 20 MHz */
- EE_SF_CLOCK_DIV, 7,
- /* 125 MHz / 63 ~= 2 MHz */
- EE_EE_CLOCK_DIV, 63);
- falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
- }
- }
-
- if (has_eeprom) {
- u32 eeprom_device_type;
-
- /* If it has no flash, it must have a large EEPROM
- * for chip config; otherwise check whether 9-bit
- * addressing is used for VPD configuration
- */
- if (has_flash &&
- (!boot_is_external ||
- EFX_OWORD_FIELD(ee_vpd_cfg, EE_VPD_EN_AD9_MODE))) {
- /* Default SPI device: Atmel AT25040 or similar
- * 512 B, 9-bit address, 8 B write block
- */
- eeprom_device_type =
- (9 << SPI_DEV_TYPE_SIZE_LBN)
- | (1 << SPI_DEV_TYPE_ADDR_LEN_LBN)
- | (3 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
- } else {
- /* "Large" SPI device: Atmel AT25640 or similar
- * 8 KB, 16-bit address, 32 B write block
- */
- eeprom_device_type =
- (13 << SPI_DEV_TYPE_SIZE_LBN)
- | (2 << SPI_DEV_TYPE_ADDR_LEN_LBN)
- | (5 << SPI_DEV_TYPE_BLOCK_SIZE_LBN);
- }
-
- falcon_spi_device_init(efx, &efx->spi_eeprom,
- EE_SPI_EEPROM, eeprom_device_type);
- }
-
- EFX_LOG(efx, "flash is %s, EEPROM is %s\n",
- (has_flash ? "present" : "absent"),
- (has_eeprom ? "present" : "absent"));
+ if (EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE)) {
+ boot_dev = (EFX_OWORD_FIELD(nic_stat, SF_PRST) ?
+ EE_SPI_FLASH : EE_SPI_EEPROM);
+ EFX_LOG(efx, "Booted from %s\n",
+ boot_dev == EE_SPI_FLASH ? "flash" : "EEPROM");
+ } else {
+ /* Disable VPD and set clock dividers to safe
+ * values for initial programming. */
+ boot_dev = -1;
+ EFX_LOG(efx, "Booted from internal ASIC settings;"
+ " setting SPI config\n");
+ EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0,
+ /* 125 MHz / 7 ~= 20 MHz */
+ EE_SF_CLOCK_DIV, 7,
+ /* 125 MHz / 63 ~= 2 MHz */
+ EE_EE_CLOCK_DIV, 63);
+ falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
+ }
+
+ if (boot_dev == EE_SPI_FLASH)
+ falcon_spi_device_init(efx, &efx->spi_flash, EE_SPI_FLASH,
+ default_flash_type);
+ if (boot_dev == EE_SPI_EEPROM)
+ falcon_spi_device_init(efx, &efx->spi_eeprom, EE_SPI_EEPROM,
+ large_eeprom_type);
}
int falcon_probe_nic(struct efx_nic *efx)
@@ -2825,10 +2929,10 @@ int falcon_probe_nic(struct efx_nic *efx)
goto fail5;
/* Initialise I2C adapter */
- efx->i2c_adap.owner = THIS_MODULE;
+ efx->i2c_adap.owner = THIS_MODULE;
nic_data->i2c_data = falcon_i2c_bit_operations;
nic_data->i2c_data.data = efx;
- efx->i2c_adap.algo_data = &nic_data->i2c_data;
+ efx->i2c_adap.algo_data = &nic_data->i2c_data;
efx->i2c_adap.dev.parent = &efx->pci_dev->dev;
strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name));
rc = i2c_bit_add_bus(&efx->i2c_adap);
@@ -2862,20 +2966,18 @@ int falcon_init_nic(struct efx_nic *efx)
unsigned thresh;
int rc;
- /* Set up the address region register. This is only needed
- * for the B0 FPGA, but since we are just pushing in the
- * reset defaults this may as well be unconditional. */
- EFX_POPULATE_OWORD_4(temp, ADR_REGION0, 0,
- ADR_REGION1, (1 << 16),
- ADR_REGION2, (2 << 16),
- ADR_REGION3, (3 << 16));
- falcon_write(efx, &temp, ADR_REGION_REG_KER);
-
/* Use on-chip SRAM */
falcon_read(efx, &temp, NIC_STAT_REG);
EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1);
falcon_write(efx, &temp, NIC_STAT_REG);
+ /* Set the source of the GMAC clock */
+ if (falcon_rev(efx) == FALCON_REV_B0) {
+ falcon_read(efx, &temp, GPIO_CTL_REG_KER);
+ EFX_SET_OWORD_FIELD(temp, GPIO_USE_NIC_CLK, true);
+ falcon_write(efx, &temp, GPIO_CTL_REG_KER);
+ }
+
/* Set buffer table mode */
EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL);
falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER);
diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h
index be025ba7a6c6..7869c3d74383 100644
--- a/drivers/net/sfc/falcon.h
+++ b/drivers/net/sfc/falcon.h
@@ -12,6 +12,7 @@
#define EFX_FALCON_H
#include "net_driver.h"
+#include "efx.h"
/*
* Falcon hardware control
@@ -65,6 +66,7 @@ extern int falcon_probe_port(struct efx_nic *efx);
extern void falcon_remove_port(struct efx_nic *efx);
/* MAC/PHY */
+extern int falcon_switch_mac(struct efx_nic *efx);
extern bool falcon_xaui_link_ok(struct efx_nic *efx);
extern int falcon_dma_stats(struct efx_nic *efx,
unsigned int done_offset);
@@ -77,6 +79,7 @@ extern int falcon_init_interrupt(struct efx_nic *efx);
extern void falcon_enable_interrupts(struct efx_nic *efx);
extern void falcon_generate_test_event(struct efx_channel *channel,
unsigned int magic);
+extern void falcon_sim_phy_event(struct efx_nic *efx);
extern void falcon_generate_interrupt(struct efx_nic *efx);
extern void falcon_set_int_moderation(struct efx_channel *channel);
extern void falcon_disable_interrupts(struct efx_nic *efx);
diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c
new file mode 100644
index 000000000000..8865eae20ac5
--- /dev/null
+++ b/drivers/net/sfc/falcon_gmac.c
@@ -0,0 +1,229 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications 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, incorporated herein by reference.
+ */
+
+#include <linux/delay.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "falcon.h"
+#include "mac.h"
+#include "falcon_hwdefs.h"
+#include "falcon_io.h"
+#include "gmii.h"
+
+/**************************************************************************
+ *
+ * MAC operations
+ *
+ *************************************************************************/
+
+static void falcon_reconfigure_gmac(struct efx_nic *efx)
+{
+ bool loopback, tx_fc, rx_fc, bytemode;
+ int if_mode;
+ unsigned int max_frame_len;
+ efx_oword_t reg;
+
+ /* Configuration register 1 */
+ tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd;
+ rx_fc = !!(efx->link_fc & EFX_FC_RX);
+ loopback = (efx->loopback_mode == LOOPBACK_GMAC);
+ bytemode = (efx->link_speed == 1000);
+
+ EFX_POPULATE_OWORD_5(reg,
+ GM_LOOP, loopback,
+ GM_TX_EN, 1,
+ GM_TX_FC_EN, tx_fc,
+ GM_RX_EN, 1,
+ GM_RX_FC_EN, rx_fc);
+ falcon_write(efx, &reg, GM_CFG1_REG);
+ udelay(10);
+
+ /* Configuration register 2 */
+ if_mode = (bytemode) ? 2 : 1;
+ EFX_POPULATE_OWORD_5(reg,
+ GM_IF_MODE, if_mode,
+ GM_PAD_CRC_EN, 1,
+ GM_LEN_CHK, 1,
+ GM_FD, efx->link_fd,
+ GM_PAMBL_LEN, 0x7/*datasheet recommended */);
+
+ falcon_write(efx, &reg, GM_CFG2_REG);
+ udelay(10);
+
+ /* Max frame len register */
+ max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
+ EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len);
+ falcon_write(efx, &reg, GM_MAX_FLEN_REG);
+ udelay(10);
+
+ /* FIFO configuration register 0 */
+ EFX_POPULATE_OWORD_5(reg,
+ GMF_FTFENREQ, 1,
+ GMF_STFENREQ, 1,
+ GMF_FRFENREQ, 1,
+ GMF_SRFENREQ, 1,
+ GMF_WTMENREQ, 1);
+ falcon_write(efx, &reg, GMF_CFG0_REG);
+ udelay(10);
+
+ /* FIFO configuration register 1 */
+ EFX_POPULATE_OWORD_2(reg,
+ GMF_CFGFRTH, 0x12,
+ GMF_CFGXOFFRTX, 0xffff);
+ falcon_write(efx, &reg, GMF_CFG1_REG);
+ udelay(10);
+
+ /* FIFO configuration register 2 */
+ EFX_POPULATE_OWORD_2(reg,
+ GMF_CFGHWM, 0x3f,
+ GMF_CFGLWM, 0xa);
+ falcon_write(efx, &reg, GMF_CFG2_REG);
+ udelay(10);
+
+ /* FIFO configuration register 3 */
+ EFX_POPULATE_OWORD_2(reg,
+ GMF_CFGHWMFT, 0x1c,
+ GMF_CFGFTTH, 0x08);
+ falcon_write(efx, &reg, GMF_CFG3_REG);
+ udelay(10);
+
+ /* FIFO configuration register 4 */
+ EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1);
+ falcon_write(efx, &reg, GMF_CFG4_REG);
+ udelay(10);
+
+ /* FIFO configuration register 5 */
+ falcon_read(efx, &reg, GMF_CFG5_REG);
+ EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode);
+ EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd);
+ EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd);
+ EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0);
+ falcon_write(efx, &reg, GMF_CFG5_REG);
+ udelay(10);
+
+ /* MAC address */
+ EFX_POPULATE_OWORD_4(reg,
+ GM_HWADDR_5, efx->net_dev->dev_addr[5],
+ GM_HWADDR_4, efx->net_dev->dev_addr[4],
+ GM_HWADDR_3, efx->net_dev->dev_addr[3],
+ GM_HWADDR_2, efx->net_dev->dev_addr[2]);
+ falcon_write(efx, &reg, GM_ADR1_REG);
+ udelay(10);
+ EFX_POPULATE_OWORD_2(reg,
+ GM_HWADDR_1, efx->net_dev->dev_addr[1],
+ GM_HWADDR_0, efx->net_dev->dev_addr[0]);
+ falcon_write(efx, &reg, GM_ADR2_REG);
+ udelay(10);
+
+ falcon_reconfigure_mac_wrapper(efx);
+}
+
+static void falcon_update_stats_gmac(struct efx_nic *efx)
+{
+ struct efx_mac_stats *mac_stats = &efx->mac_stats;
+ unsigned long old_rx_pause, old_tx_pause;
+ unsigned long new_rx_pause, new_tx_pause;
+ int rc;
+
+ rc = falcon_dma_stats(efx, GDmaDone_offset);
+ if (rc)
+ return;
+
+ /* Pause frames are erroneously counted as errors (SFC bug 3269) */
+ old_rx_pause = mac_stats->rx_pause;
+ old_tx_pause = mac_stats->tx_pause;
+
+ /* Update MAC stats from DMAed values */
+ FALCON_STAT(efx, GRxGoodOct, rx_good_bytes);
+ FALCON_STAT(efx, GRxBadOct, rx_bad_bytes);
+ FALCON_STAT(efx, GRxMissPkt, rx_missed);
+ FALCON_STAT(efx, GRxFalseCRS, rx_false_carrier);
+ FALCON_STAT(efx, GRxPausePkt, rx_pause);
+ FALCON_STAT(efx, GRxBadPkt, rx_bad);
+ FALCON_STAT(efx, GRxUcastPkt, rx_unicast);
+ FALCON_STAT(efx, GRxMcastPkt, rx_multicast);
+ FALCON_STAT(efx, GRxBcastPkt, rx_broadcast);
+ FALCON_STAT(efx, GRxGoodLt64Pkt, rx_good_lt64);
+ FALCON_STAT(efx, GRxBadLt64Pkt, rx_bad_lt64);
+ FALCON_STAT(efx, GRx64Pkt, rx_64);
+ FALCON_STAT(efx, GRx65to127Pkt, rx_65_to_127);
+ FALCON_STAT(efx, GRx128to255Pkt, rx_128_to_255);
+ FALCON_STAT(efx, GRx256to511Pkt, rx_256_to_511);
+ FALCON_STAT(efx, GRx512to1023Pkt, rx_512_to_1023);
+ FALCON_STAT(efx, GRx1024to15xxPkt, rx_1024_to_15xx);
+ FALCON_STAT(efx, GRx15xxtoJumboPkt, rx_15xx_to_jumbo);
+ FALCON_STAT(efx, GRxGtJumboPkt, rx_gtjumbo);
+ FALCON_STAT(efx, GRxFcsErr64to15xxPkt, rx_bad_64_to_15xx);
+ FALCON_STAT(efx, GRxFcsErr15xxtoJumboPkt, rx_bad_15xx_to_jumbo);
+ FALCON_STAT(efx, GRxFcsErrGtJumboPkt, rx_bad_gtjumbo);
+ FALCON_STAT(efx, GTxGoodBadOct, tx_bytes);
+ FALCON_STAT(efx, GTxGoodOct, tx_good_bytes);
+ FALCON_STAT(efx, GTxSglColPkt, tx_single_collision);
+ FALCON_STAT(efx, GTxMultColPkt, tx_multiple_collision);
+ FALCON_STAT(efx, GTxExColPkt, tx_excessive_collision);
+ FALCON_STAT(efx, GTxDefPkt, tx_deferred);
+ FALCON_STAT(efx, GTxLateCol, tx_late_collision);
+ FALCON_STAT(efx, GTxExDefPkt, tx_excessive_deferred);
+ FALCON_STAT(efx, GTxPausePkt, tx_pause);
+ FALCON_STAT(efx, GTxBadPkt, tx_bad);
+ FALCON_STAT(efx, GTxUcastPkt, tx_unicast);
+ FALCON_STAT(efx, GTxMcastPkt, tx_multicast);
+ FALCON_STAT(efx, GTxBcastPkt, tx_broadcast);
+ FALCON_STAT(efx, GTxLt64Pkt, tx_lt64);
+ FALCON_STAT(efx, GTx64Pkt, tx_64);
+ FALCON_STAT(efx, GTx65to127Pkt, tx_65_to_127);
+ FALCON_STAT(efx, GTx128to255Pkt, tx_128_to_255);
+ FALCON_STAT(efx, GTx256to511Pkt, tx_256_to_511);
+ FALCON_STAT(efx, GTx512to1023Pkt, tx_512_to_1023);
+ FALCON_STAT(efx, GTx1024to15xxPkt, tx_1024_to_15xx);
+ FALCON_STAT(efx, GTx15xxtoJumboPkt, tx_15xx_to_jumbo);
+ FALCON_STAT(efx, GTxGtJumboPkt, tx_gtjumbo);
+ FALCON_STAT(efx, GTxNonTcpUdpPkt, tx_non_tcpudp);
+ FALCON_STAT(efx, GTxMacSrcErrPkt, tx_mac_src_error);
+ FALCON_STAT(efx, GTxIpSrcErrPkt, tx_ip_src_error);
+
+ /* Pause frames are erroneously counted as errors (SFC bug 3269) */
+ new_rx_pause = mac_stats->rx_pause;
+ new_tx_pause = mac_stats->tx_pause;
+ mac_stats->rx_bad -= (new_rx_pause - old_rx_pause);
+ mac_stats->tx_bad -= (new_tx_pause - old_tx_pause);
+
+ /* Derive stats that the MAC doesn't provide directly */
+ mac_stats->tx_bad_bytes =
+ mac_stats->tx_bytes - mac_stats->tx_good_bytes;
+ mac_stats->tx_packets =
+ mac_stats->tx_lt64 + mac_stats->tx_64 +
+ mac_stats->tx_65_to_127 + mac_stats->tx_128_to_255 +
+ mac_stats->tx_256_to_511 + mac_stats->tx_512_to_1023 +
+ mac_stats->tx_1024_to_15xx + mac_stats->tx_15xx_to_jumbo +
+ mac_stats->tx_gtjumbo;
+ mac_stats->tx_collision =
+ mac_stats->tx_single_collision +
+ mac_stats->tx_multiple_collision +
+ mac_stats->tx_excessive_collision +
+ mac_stats->tx_late_collision;
+ mac_stats->rx_bytes =
+ mac_stats->rx_good_bytes + mac_stats->rx_bad_bytes;
+ mac_stats->rx_packets =
+ mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64 +
+ mac_stats->rx_64 + mac_stats->rx_65_to_127 +
+ mac_stats->rx_128_to_255 + mac_stats->rx_256_to_511 +
+ mac_stats->rx_512_to_1023 + mac_stats->rx_1024_to_15xx +
+ mac_stats->rx_15xx_to_jumbo + mac_stats->rx_gtjumbo;
+ mac_stats->rx_good = mac_stats->rx_packets - mac_stats->rx_bad;
+ mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
+}
+
+struct efx_mac_operations falcon_gmac_operations = {
+ .reconfigure = falcon_reconfigure_gmac,
+ .update_stats = falcon_update_stats_gmac,
+ .irq = efx_port_dummy_op_void,
+ .poll = efx_port_dummy_op_void,
+};
diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h
index 5d584b0dbb51..bda8d5bb72e4 100644
--- a/drivers/net/sfc/falcon_hwdefs.h
+++ b/drivers/net/sfc/falcon_hwdefs.h
@@ -111,12 +111,18 @@
/* NIC status register */
#define NIC_STAT_REG 0x0200
+#define EE_STRAP_EN_LBN 31
+#define EE_STRAP_EN_WIDTH 1
+#define EE_STRAP_OVR_LBN 24
+#define EE_STRAP_OVR_WIDTH 4
#define ONCHIP_SRAM_LBN 16
#define ONCHIP_SRAM_WIDTH 1
#define SF_PRST_LBN 9
#define SF_PRST_WIDTH 1
#define EE_PRST_LBN 8
#define EE_PRST_WIDTH 1
+#define STRAP_PINS_LBN 0
+#define STRAP_PINS_WIDTH 3
/* These bit definitions are extrapolated from the list of numerical
* values for STRAP_PINS.
*/
@@ -130,6 +136,8 @@
/* GPIO control register */
#define GPIO_CTL_REG_KER 0x0210
+#define GPIO_USE_NIC_CLK_LBN (30)
+#define GPIO_USE_NIC_CLK_WIDTH (1)
#define GPIO_OUTPUTS_LBN (16)
#define GPIO_OUTPUTS_WIDTH (4)
#define GPIO_INPUTS_LBN (8)
@@ -492,6 +500,107 @@
#define MAC_MCAST_HASH_REG0_KER 0xca0
#define MAC_MCAST_HASH_REG1_KER 0xcb0
+/* GMAC configuration register 1 */
+#define GM_CFG1_REG 0xe00
+#define GM_SW_RST_LBN 31
+#define GM_SW_RST_WIDTH 1
+#define GM_LOOP_LBN 8
+#define GM_LOOP_WIDTH 1
+#define GM_RX_FC_EN_LBN 5
+#define GM_RX_FC_EN_WIDTH 1
+#define GM_TX_FC_EN_LBN 4
+#define GM_TX_FC_EN_WIDTH 1
+#define GM_RX_EN_LBN 2
+#define GM_RX_EN_WIDTH 1
+#define GM_TX_EN_LBN 0
+#define GM_TX_EN_WIDTH 1
+
+/* GMAC configuration register 2 */
+#define GM_CFG2_REG 0xe10
+#define GM_PAMBL_LEN_LBN 12
+#define GM_PAMBL_LEN_WIDTH 4
+#define GM_IF_MODE_LBN 8
+#define GM_IF_MODE_WIDTH 2
+#define GM_LEN_CHK_LBN 4
+#define GM_LEN_CHK_WIDTH 1
+#define GM_PAD_CRC_EN_LBN 2
+#define GM_PAD_CRC_EN_WIDTH 1
+#define GM_FD_LBN 0
+#define GM_FD_WIDTH 1
+
+/* GMAC maximum frame length register */
+#define GM_MAX_FLEN_REG 0xe40
+#define GM_MAX_FLEN_LBN 0
+#define GM_MAX_FLEN_WIDTH 16
+
+/* GMAC station address register 1 */
+#define GM_ADR1_REG 0xf00
+#define GM_HWADDR_5_LBN 24
+#define GM_HWADDR_5_WIDTH 8
+#define GM_HWADDR_4_LBN 16
+#define GM_HWADDR_4_WIDTH 8
+#define GM_HWADDR_3_LBN 8
+#define GM_HWADDR_3_WIDTH 8
+#define GM_HWADDR_2_LBN 0
+#define GM_HWADDR_2_WIDTH 8
+
+/* GMAC station address register 2 */
+#define GM_ADR2_REG 0xf10
+#define GM_HWADDR_1_LBN 24
+#define GM_HWADDR_1_WIDTH 8
+#define GM_HWADDR_0_LBN 16
+#define GM_HWADDR_0_WIDTH 8
+
+/* GMAC FIFO configuration register 0 */
+#define GMF_CFG0_REG 0xf20
+#define GMF_FTFENREQ_LBN 12
+#define GMF_FTFENREQ_WIDTH 1
+#define GMF_STFENREQ_LBN 11
+#define GMF_STFENREQ_WIDTH 1
+#define GMF_FRFENREQ_LBN 10
+#define GMF_FRFENREQ_WIDTH 1
+#define GMF_SRFENREQ_LBN 9
+#define GMF_SRFENREQ_WIDTH 1
+#define GMF_WTMENREQ_LBN 8
+#define GMF_WTMENREQ_WIDTH 1
+
+/* GMAC FIFO configuration register 1 */
+#define GMF_CFG1_REG 0xf30
+#define GMF_CFGFRTH_LBN 16
+#define GMF_CFGFRTH_WIDTH 5
+#define GMF_CFGXOFFRTX_LBN 0
+#define GMF_CFGXOFFRTX_WIDTH 16
+
+/* GMAC FIFO configuration register 2 */
+#define GMF_CFG2_REG 0xf40
+#define GMF_CFGHWM_LBN 16
+#define GMF_CFGHWM_WIDTH 6
+#define GMF_CFGLWM_LBN 0
+#define GMF_CFGLWM_WIDTH 6
+
+/* GMAC FIFO configuration register 3 */
+#define GMF_CFG3_REG 0xf50
+#define GMF_CFGHWMFT_LBN 16
+#define GMF_CFGHWMFT_WIDTH 6
+#define GMF_CFGFTTH_LBN 0
+#define GMF_CFGFTTH_WIDTH 6
+
+/* GMAC FIFO configuration register 4 */
+#define GMF_CFG4_REG 0xf60
+#define GMF_HSTFLTRFRM_PAUSE_LBN 12
+#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12
+
+/* GMAC FIFO configuration register 5 */
+#define GMF_CFG5_REG 0xf70
+#define GMF_CFGHDPLX_LBN 22
+#define GMF_CFGHDPLX_WIDTH 1
+#define GMF_CFGBYTMODE_LBN 19
+#define GMF_CFGBYTMODE_WIDTH 1
+#define GMF_HSTDRPLT64_LBN 18
+#define GMF_HSTDRPLT64_WIDTH 1
+#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12
+#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1
+
/* XGMAC address register low */
#define XM_ADR_LO_REG 0x1200
#define XM_ADR_3_LBN 24
@@ -944,6 +1053,8 @@
#define XG_MNT_INTR_B0_WIDTH 1
#define RX_RECOVERY_A1_LBN 11
#define RX_RECOVERY_A1_WIDTH 1
+#define XFP_PHY_INTR_LBN 10
+#define XFP_PHY_INTR_WIDTH 1
#define XG_PHY_INTR_LBN 9
#define XG_PHY_INTR_WIDTH 1
#define G_PHY1_INTR_LBN 8
@@ -962,54 +1073,103 @@
**************************************************************************
*
*/
+
#define GRxGoodOct_offset 0x0
+#define GRxGoodOct_WIDTH 48
#define GRxBadOct_offset 0x8
+#define GRxBadOct_WIDTH 48
#define GRxMissPkt_offset 0x10
+#define GRxMissPkt_WIDTH 32
#define GRxFalseCRS_offset 0x14
+#define GRxFalseCRS_WIDTH 32
#define GRxPausePkt_offset 0x18
+#define GRxPausePkt_WIDTH 32
#define GRxBadPkt_offset 0x1C
+#define GRxBadPkt_WIDTH 32
#define GRxUcastPkt_offset 0x20
+#define GRxUcastPkt_WIDTH 32
#define GRxMcastPkt_offset 0x24
+#define GRxMcastPkt_WIDTH 32
#define GRxBcastPkt_offset 0x28
+#define GRxBcastPkt_WIDTH 32
#define GRxGoodLt64Pkt_offset 0x2C
+#define GRxGoodLt64Pkt_WIDTH 32
#define GRxBadLt64Pkt_offset 0x30
+#define GRxBadLt64Pkt_WIDTH 32
#define GRx64Pkt_offset 0x34
+#define GRx64Pkt_WIDTH 32
#define GRx65to127Pkt_offset 0x38
+#define GRx65to127Pkt_WIDTH 32
#define GRx128to255Pkt_offset 0x3C
+#define GRx128to255Pkt_WIDTH 32
#define GRx256to511Pkt_offset 0x40
+#define GRx256to511Pkt_WIDTH 32
#define GRx512to1023Pkt_offset 0x44
+#define GRx512to1023Pkt_WIDTH 32
#define GRx1024to15xxPkt_offset 0x48
+#define GRx1024to15xxPkt_WIDTH 32
#define GRx15xxtoJumboPkt_offset 0x4C
+#define GRx15xxtoJumboPkt_WIDTH 32
#define GRxGtJumboPkt_offset 0x50
+#define GRxGtJumboPkt_WIDTH 32
#define GRxFcsErr64to15xxPkt_offset 0x54
+#define GRxFcsErr64to15xxPkt_WIDTH 32
#define GRxFcsErr15xxtoJumboPkt_offset 0x58
+#define GRxFcsErr15xxtoJumboPkt_WIDTH 32
#define GRxFcsErrGtJumboPkt_offset 0x5C
+#define GRxFcsErrGtJumboPkt_WIDTH 32
#define GTxGoodBadOct_offset 0x80
+#define GTxGoodBadOct_WIDTH 48
#define GTxGoodOct_offset 0x88
+#define GTxGoodOct_WIDTH 48
#define GTxSglColPkt_offset 0x90
+#define GTxSglColPkt_WIDTH 32
#define GTxMultColPkt_offset 0x94
+#define GTxMultColPkt_WIDTH 32
#define GTxExColPkt_offset 0x98
+#define GTxExColPkt_WIDTH 32
#define GTxDefPkt_offset 0x9C
+#define GTxDefPkt_WIDTH 32
#define GTxLateCol_offset 0xA0
+#define GTxLateCol_WIDTH 32
#define GTxExDefPkt_offset 0xA4
+#define GTxExDefPkt_WIDTH 32
#define GTxPausePkt_offset 0xA8
+#define GTxPausePkt_WIDTH 32
#define GTxBadPkt_offset 0xAC
+#define GTxBadPkt_WIDTH 32
#define GTxUcastPkt_offset 0xB0
+#define GTxUcastPkt_WIDTH 32
#define GTxMcastPkt_offset 0xB4
+#define GTxMcastPkt_WIDTH 32
#define GTxBcastPkt_offset 0xB8
+#define GTxBcastPkt_WIDTH 32
#define GTxLt64Pkt_offset 0xBC
+#define GTxLt64Pkt_WIDTH 32
#define GTx64Pkt_offset 0xC0
+#define GTx64Pkt_WIDTH 32
#define GTx65to127Pkt_offset 0xC4
+#define GTx65to127Pkt_WIDTH 32
#define GTx128to255Pkt_offset 0xC8
+#define GTx128to255Pkt_WIDTH 32
#define GTx256to511Pkt_offset 0xCC
+#define GTx256to511Pkt_WIDTH 32
#define GTx512to1023Pkt_offset 0xD0
+#define GTx512to1023Pkt_WIDTH 32
#define GTx1024to15xxPkt_offset 0xD4
+#define GTx1024to15xxPkt_WIDTH 32
#define GTx15xxtoJumboPkt_offset 0xD8
+#define GTx15xxtoJumboPkt_WIDTH 32
#define GTxGtJumboPkt_offset 0xDC
+#define GTxGtJumboPkt_WIDTH 32
#define GTxNonTcpUdpPkt_offset 0xE0
+#define GTxNonTcpUdpPkt_WIDTH 16
#define GTxMacSrcErrPkt_offset 0xE4
+#define GTxMacSrcErrPkt_WIDTH 16
#define GTxIpSrcErrPkt_offset 0xE8
+#define GTxIpSrcErrPkt_WIDTH 16
#define GDmaDone_offset 0xEC
+#define GDmaDone_WIDTH 32
#define XgRxOctets_offset 0x0
#define XgRxOctets_WIDTH 48
@@ -1150,7 +1310,6 @@ struct falcon_nvconfig_board_v3 {
(((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field)))
#define NVCONFIG_OFFSET 0x300
-#define NVCONFIG_END 0x400
#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C
struct falcon_nvconfig {
diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c
index d4012314dd01..5a03713685ac 100644
--- a/drivers/net/sfc/falcon_xmac.c
+++ b/drivers/net/sfc/falcon_xmac.c
@@ -15,7 +15,6 @@
#include "falcon_hwdefs.h"
#include "falcon_io.h"
#include "mac.h"
-#include "gmii.h"
#include "mdio_10g.h"
#include "phy.h"
#include "boards.h"
@@ -26,24 +25,6 @@
* MAC operations
*
*************************************************************************/
-static int falcon_reset_xmac(struct efx_nic *efx)
-{
- efx_oword_t reg;
- int count;
-
- EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
- falcon_write(efx, &reg, XM_GLB_CFG_REG);
-
- for (count = 0; count < 10000; count++) { /* wait upto 100ms */
- falcon_read(efx, &reg, XM_GLB_CFG_REG);
- if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
- return 0;
- udelay(10);
- }
-
- EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
- return -ETIMEDOUT;
-}
/* Configure the XAUI driver that is an output from Falcon */
static void falcon_setup_xaui(struct efx_nic *efx)
@@ -99,31 +80,20 @@ int falcon_reset_xaui(struct efx_nic *efx)
return -ETIMEDOUT;
}
-static bool falcon_xgmii_status(struct efx_nic *efx)
+static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
{
efx_oword_t reg;
- if (falcon_rev(efx) < FALCON_REV_B0)
- return true;
-
- /* The ISR latches, so clear it and re-read */
- falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
- falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
-
- if (EFX_OWORD_FIELD(reg, XM_LCLFLT) ||
- EFX_OWORD_FIELD(reg, XM_RMTFLT)) {
- EFX_INFO(efx, "MGT_INT: "EFX_DWORD_FMT"\n", EFX_DWORD_VAL(reg));
- return false;
- }
-
- return true;
-}
+ if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+ return;
-static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
-{
- efx_oword_t reg;
+ /* We expect xgmii faults if the wireside link is up */
+ if (!EFX_WORKAROUND_5147(efx) || !efx->link_up)
+ return;
- if ((falcon_rev(efx) < FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+ /* We can only use this interrupt to signal the negative edge of
+ * xaui_align [we have to poll the positive edge]. */
+ if (!efx->mac_up)
return;
/* Flush the ISR */
@@ -136,35 +106,7 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
falcon_write(efx, &reg, XM_MGT_INT_MSK_REG_B0);
}
-int falcon_init_xmac(struct efx_nic *efx)
-{
- int rc;
-
- /* Initialize the PHY first so the clock is around */
- rc = efx->phy_op->init(efx);
- if (rc)
- goto fail1;
-
- rc = falcon_reset_xaui(efx);
- if (rc)
- goto fail2;
-
- /* Wait again. Give the PHY and MAC time to come back */
- schedule_timeout_uninterruptible(HZ / 10);
-
- rc = falcon_reset_xmac(efx);
- if (rc)
- goto fail2;
-
- falcon_mask_status_intr(efx, true);
- return 0;
-
- fail2:
- efx->phy_op->fini(efx);
- fail1:
- return rc;
-}
-
+/* Get status of XAUI link */
bool falcon_xaui_link_ok(struct efx_nic *efx)
{
efx_oword_t reg;
@@ -188,18 +130,10 @@ bool falcon_xaui_link_ok(struct efx_nic *efx)
EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
falcon_write(efx, &reg, XX_CORE_STAT_REG);
- /* If the link is up, then check the phy side of the xaui link
- * (error conditions from the wire side propoagate back through
- * the phy to the xaui side). */
- if (efx->link_up && link_ok) {
+ /* If the link is up, then check the phy side of the xaui link */
+ if (efx->link_up && link_ok)
if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS))
link_ok = mdio_clause45_phyxgxs_lane_sync(efx);
- }
-
- /* If the PHY and XAUI links are up, then check the mac's xgmii
- * fault state */
- if (efx->link_up && link_ok)
- link_ok = falcon_xgmii_status(efx);
return link_ok;
}
@@ -208,7 +142,7 @@ static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
{
unsigned int max_frame_len;
efx_oword_t reg;
- bool rx_fc = !!(efx->flow_control & EFX_FC_RX);
+ bool rx_fc = !!(efx->link_fc & EFX_FC_RX);
/* Configure MAC - cut-thru mode is hard wired on */
EFX_POPULATE_DWORD_3(reg,
@@ -311,70 +245,39 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
* to come back up. Bash it until it comes back up */
-static bool falcon_check_xaui_link_up(struct efx_nic *efx)
+static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
{
- int max_tries, tries;
- tries = EFX_WORKAROUND_5147(efx) ? 5 : 1;
- max_tries = tries;
+ efx->mac_up = falcon_xaui_link_ok(efx);
if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
- (efx->phy_type == PHY_TYPE_NONE) ||
efx_phy_mode_disabled(efx->phy_mode))
- return false;
-
- while (tries) {
- if (falcon_xaui_link_ok(efx))
- return true;
+ /* XAUI link is expected to be down */
+ return;
- EFX_LOG(efx, "%s Clobbering XAUI (%d tries left).\n",
- __func__, tries);
+ while (!efx->mac_up && tries) {
+ EFX_LOG(efx, "bashing xaui\n");
falcon_reset_xaui(efx);
udelay(200);
- tries--;
- }
- EFX_LOG(efx, "Failed to bring XAUI link back up in %d tries!\n",
- max_tries);
- return false;
+ efx->mac_up = falcon_xaui_link_ok(efx);
+ --tries;
+ }
}
-void falcon_reconfigure_xmac(struct efx_nic *efx)
+static void falcon_reconfigure_xmac(struct efx_nic *efx)
{
- bool xaui_link_ok;
-
falcon_mask_status_intr(efx, false);
- falcon_deconfigure_mac_wrapper(efx);
-
- /* Reconfigure the PHY, disabling transmit in mac level loopback. */
- if (LOOPBACK_INTERNAL(efx))
- efx->phy_mode |= PHY_MODE_TX_DISABLED;
- else
- efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
- efx->phy_op->reconfigure(efx);
-
falcon_reconfigure_xgxs_core(efx);
falcon_reconfigure_xmac_core(efx);
falcon_reconfigure_mac_wrapper(efx);
- /* Ensure XAUI link is up */
- xaui_link_ok = falcon_check_xaui_link_up(efx);
-
- if (xaui_link_ok && efx->link_up)
- falcon_mask_status_intr(efx, true);
-}
-
-void falcon_fini_xmac(struct efx_nic *efx)
-{
- /* Isolate the MAC - PHY */
- falcon_deconfigure_mac_wrapper(efx);
-
- /* Potentially power down the PHY */
- efx->phy_op->fini(efx);
+ falcon_check_xaui_link_up(efx, 5);
+ falcon_mask_status_intr(efx, true);
}
-void falcon_update_stats_xmac(struct efx_nic *efx)
+static void falcon_update_stats_xmac(struct efx_nic *efx)
{
struct efx_mac_stats *mac_stats = &efx->mac_stats;
int rc;
@@ -439,97 +342,35 @@ void falcon_update_stats_xmac(struct efx_nic *efx)
mac_stats->rx_control * 64);
}
-int falcon_check_xmac(struct efx_nic *efx)
+static void falcon_xmac_irq(struct efx_nic *efx)
{
- bool xaui_link_ok;
- int rc;
-
- if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
- efx_phy_mode_disabled(efx->phy_mode))
- return 0;
-
- falcon_mask_status_intr(efx, false);
- xaui_link_ok = falcon_xaui_link_ok(efx);
-
- if (EFX_WORKAROUND_5147(efx) && !xaui_link_ok)
- falcon_reset_xaui(efx);
-
- /* Call the PHY check_hw routine */
- rc = efx->phy_op->check_hw(efx);
-
- /* Unmask interrupt if everything was (and still is) ok */
- if (xaui_link_ok && efx->link_up)
- falcon_mask_status_intr(efx, true);
-
- return rc;
-}
-
-/* Simulate a PHY event */
-void falcon_xmac_sim_phy_event(struct efx_nic *efx)
-{
- efx_qword_t phy_event;
-
- EFX_POPULATE_QWORD_2(phy_event,
- EV_CODE, GLOBAL_EV_DECODE,
- XG_PHY_INTR, 1);
- falcon_generate_event(&efx->channel[0], &phy_event);
+ /* The XGMII link has a transient fault, which indicates either:
+ * - there's a transient xgmii fault
+ * - falcon's end of the xaui link may need a kick
+ * - the wire-side link may have gone down, but the lasi/poll()
+ * hasn't noticed yet.
+ *
+ * We only want to even bother polling XAUI if we're confident it's
+ * not (1) or (3). In both cases, the only reliable way to spot this
+ * is to wait a bit. We do this here by forcing the mac link state
+ * to down, and waiting for the mac poll to come round and check
+ */
+ efx->mac_up = false;
}
-int falcon_xmac_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+static void falcon_poll_xmac(struct efx_nic *efx)
{
- mdio_clause45_get_settings(efx, ecmd);
- ecmd->transceiver = XCVR_INTERNAL;
- ecmd->phy_address = efx->mii.phy_id;
- ecmd->autoneg = AUTONEG_DISABLE;
- ecmd->duplex = DUPLEX_FULL;
- return 0;
-}
+ if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up)
+ return;
-int falcon_xmac_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
-{
- if (ecmd->transceiver != XCVR_INTERNAL)
- return -EINVAL;
- if (ecmd->autoneg != AUTONEG_DISABLE)
- return -EINVAL;
- if (ecmd->duplex != DUPLEX_FULL)
- return -EINVAL;
-
- return mdio_clause45_set_settings(efx, ecmd);
+ falcon_mask_status_intr(efx, false);
+ falcon_check_xaui_link_up(efx, 1);
+ falcon_mask_status_intr(efx, true);
}
-
-int falcon_xmac_set_pause(struct efx_nic *efx, enum efx_fc_type flow_control)
-{
- bool reset;
-
- if (flow_control & EFX_FC_AUTO) {
- EFX_LOG(efx, "10G does not support flow control "
- "autonegotiation\n");
- return -EINVAL;
- }
-
- if ((flow_control & EFX_FC_TX) && !(flow_control & EFX_FC_RX))
- return -EINVAL;
-
- /* TX flow control may automatically turn itself off if the
- * link partner (intermittently) stops responding to pause
- * frames. There isn't any indication that this has happened,
- * so the best we do is leave it up to the user to spot this
- * and fix it be cycling transmit flow control on this end. */
- reset = ((flow_control & EFX_FC_TX) &&
- !(efx->flow_control & EFX_FC_TX));
- if (EFX_WORKAROUND_11482(efx) && reset) {
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- /* Recover by resetting the EM block */
- if (efx->link_up)
- falcon_drain_tx_fifo(efx);
- } else {
- /* Schedule a reset to recover */
- efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
- }
- }
-
- efx->flow_control = flow_control;
-
- return 0;
-}
+struct efx_mac_operations falcon_xmac_operations = {
+ .reconfigure = falcon_reconfigure_xmac,
+ .update_stats = falcon_update_stats_xmac,
+ .irq = falcon_xmac_irq,
+ .poll = falcon_poll_xmac,
+};
diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h
index d25bbd1297f4..dfccaa7b573e 100644
--- a/drivers/net/sfc/gmii.h
+++ b/drivers/net/sfc/gmii.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006 Solarflare Communications Inc.
+ * Copyright 2006-2008 Solarflare Communications 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
@@ -57,139 +57,4 @@
#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
#define ISR_JABBER 0x0001 /* Bit 0 - jabber */
-/* Logically extended advertisement register */
-#define GM_ADVERTISE_SLCT ADVERTISE_SLCT
-#define GM_ADVERTISE_CSMA ADVERTISE_CSMA
-#define GM_ADVERTISE_10HALF ADVERTISE_10HALF
-#define GM_ADVERTISE_1000XFULL ADVERTISE_1000XFULL
-#define GM_ADVERTISE_10FULL ADVERTISE_10FULL
-#define GM_ADVERTISE_1000XHALF ADVERTISE_1000XHALF
-#define GM_ADVERTISE_100HALF ADVERTISE_100HALF
-#define GM_ADVERTISE_1000XPAUSE ADVERTISE_1000XPAUSE
-#define GM_ADVERTISE_100FULL ADVERTISE_100FULL
-#define GM_ADVERTISE_1000XPSE_ASYM ADVERTISE_1000XPSE_ASYM
-#define GM_ADVERTISE_100BASE4 ADVERTISE_100BASE4
-#define GM_ADVERTISE_PAUSE_CAP ADVERTISE_PAUSE_CAP
-#define GM_ADVERTISE_PAUSE_ASYM ADVERTISE_PAUSE_ASYM
-#define GM_ADVERTISE_RESV ADVERTISE_RESV
-#define GM_ADVERTISE_RFAULT ADVERTISE_RFAULT
-#define GM_ADVERTISE_LPACK ADVERTISE_LPACK
-#define GM_ADVERTISE_NPAGE ADVERTISE_NPAGE
-#define GM_ADVERTISE_1000FULL (ADVERTISE_1000FULL << 8)
-#define GM_ADVERTISE_1000HALF (ADVERTISE_1000HALF << 8)
-#define GM_ADVERTISE_1000 (GM_ADVERTISE_1000FULL | \
- GM_ADVERTISE_1000HALF)
-#define GM_ADVERTISE_FULL (GM_ADVERTISE_1000FULL | \
- ADVERTISE_FULL)
-#define GM_ADVERTISE_ALL (GM_ADVERTISE_1000FULL | \
- GM_ADVERTISE_1000HALF | \
- ADVERTISE_ALL)
-
-/* Logically extended link partner ability register */
-#define GM_LPA_SLCT LPA_SLCT
-#define GM_LPA_10HALF LPA_10HALF
-#define GM_LPA_1000XFULL LPA_1000XFULL
-#define GM_LPA_10FULL LPA_10FULL
-#define GM_LPA_1000XHALF LPA_1000XHALF
-#define GM_LPA_100HALF LPA_100HALF
-#define GM_LPA_1000XPAUSE LPA_1000XPAUSE
-#define GM_LPA_100FULL LPA_100FULL
-#define GM_LPA_1000XPAUSE_ASYM LPA_1000XPAUSE_ASYM
-#define GM_LPA_100BASE4 LPA_100BASE4
-#define GM_LPA_PAUSE_CAP LPA_PAUSE_CAP
-#define GM_LPA_PAUSE_ASYM LPA_PAUSE_ASYM
-#define GM_LPA_RESV LPA_RESV
-#define GM_LPA_RFAULT LPA_RFAULT
-#define GM_LPA_LPACK LPA_LPACK
-#define GM_LPA_NPAGE LPA_NPAGE
-#define GM_LPA_1000FULL (LPA_1000FULL << 6)
-#define GM_LPA_1000HALF (LPA_1000HALF << 6)
-#define GM_LPA_10000FULL 0x00040000
-#define GM_LPA_10000HALF 0x00080000
-#define GM_LPA_DUPLEX (GM_LPA_1000FULL | GM_LPA_10000FULL \
- | LPA_DUPLEX)
-#define GM_LPA_10 (LPA_10FULL | LPA_10HALF)
-#define GM_LPA_100 LPA_100
-#define GM_LPA_1000 (GM_LPA_1000FULL | GM_LPA_1000HALF)
-#define GM_LPA_10000 (GM_LPA_10000FULL | GM_LPA_10000HALF)
-
-/* Retrieve GMII autonegotiation advertised abilities
- *
- * The MII advertisment register (MII_ADVERTISE) is logically extended
- * to include advertisement bits ADVERTISE_1000FULL and
- * ADVERTISE_1000HALF from MII_CTRL1000. The result can be tested
- * against the GM_ADVERTISE_xxx constants.
- */
-static inline unsigned int gmii_advertised(struct mii_if_info *gmii)
-{
- unsigned int advertise;
- unsigned int ctrl1000;
-
- advertise = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_ADVERTISE);
- ctrl1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_CTRL1000);
- return (((ctrl1000 << 8) & GM_ADVERTISE_1000) | advertise);
-}
-
-/* Retrieve GMII autonegotiation link partner abilities
- *
- * The MII link partner ability register (MII_LPA) is logically
- * extended by adding bits LPA_1000HALF and LPA_1000FULL from
- * MII_STAT1000. The result can be tested against the GM_LPA_xxx
- * constants.
- */
-static inline unsigned int gmii_lpa(struct mii_if_info *gmii)
-{
- unsigned int lpa;
- unsigned int stat1000;
-
- lpa = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_LPA);
- stat1000 = gmii->mdio_read(gmii->dev, gmii->phy_id, MII_STAT1000);
- return (((stat1000 << 6) & GM_LPA_1000) | lpa);
-}
-
-/* Calculate GMII autonegotiated link technology
- *
- * "negotiated" should be the result of gmii_advertised() logically
- * ANDed with the result of gmii_lpa().
- *
- * "tech" will be negotiated with the unused bits masked out. For
- * example, if both ends of the link are capable of both
- * GM_LPA_1000FULL and GM_LPA_100FULL, GM_LPA_100FULL will be masked
- * out.
- */
-static inline unsigned int gmii_nway_result(unsigned int negotiated)
-{
- unsigned int other_bits;
-
- /* Mask out the speed and duplexity bits */
- other_bits = negotiated & ~(GM_LPA_10 | GM_LPA_100 | GM_LPA_1000);
-
- if (negotiated & GM_LPA_1000FULL)
- return (other_bits | GM_LPA_1000FULL);
- else if (negotiated & GM_LPA_1000HALF)
- return (other_bits | GM_LPA_1000HALF);
- else
- return (other_bits | mii_nway_result(negotiated));
-}
-
-/* Calculate GMII non-autonegotiated link technology
- *
- * This provides an equivalent to gmii_nway_result for the case when
- * autonegotiation is disabled.
- */
-static inline unsigned int gmii_forced_result(unsigned int bmcr)
-{
- unsigned int result;
- int full_duplex;
-
- full_duplex = bmcr & BMCR_FULLDPLX;
- if (bmcr & BMCR_SPEED1000)
- result = full_duplex ? GM_LPA_1000FULL : GM_LPA_1000HALF;
- else if (bmcr & BMCR_SPEED100)
- result = full_duplex ? GM_LPA_100FULL : GM_LPA_100HALF;
- else
- result = full_duplex ? GM_LPA_10FULL : GM_LPA_10HALF;
- return result;
-}
-
#endif /* EFX_GMII_H */
diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h
index a31571c69137..4e7074278fe1 100644
--- a/drivers/net/sfc/mac.h
+++ b/drivers/net/sfc/mac.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2007 Solarflare Communications Inc.
+ * Copyright 2006-2008 Solarflare Communications 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
@@ -13,17 +13,7 @@
#include "net_driver.h"
-extern int falcon_init_xmac(struct efx_nic *efx);
-extern void falcon_reconfigure_xmac(struct efx_nic *efx);
-extern void falcon_update_stats_xmac(struct efx_nic *efx);
-extern void falcon_fini_xmac(struct efx_nic *efx);
-extern int falcon_check_xmac(struct efx_nic *efx);
-extern void falcon_xmac_sim_phy_event(struct efx_nic *efx);
-extern int falcon_xmac_get_settings(struct efx_nic *efx,
- struct ethtool_cmd *ecmd);
-extern int falcon_xmac_set_settings(struct efx_nic *efx,
- struct ethtool_cmd *ecmd);
-extern int falcon_xmac_set_pause(struct efx_nic *efx,
- enum efx_fc_type pause_params);
+extern struct efx_mac_operations falcon_gmac_operations;
+extern struct efx_mac_operations falcon_xmac_operations;
#endif
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c
index 003e48dcb2f3..f6a16428113d 100644
--- a/drivers/net/sfc/mdio_10g.c
+++ b/drivers/net/sfc/mdio_10g.c
@@ -47,13 +47,16 @@ static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd,
if (LOOPBACK_INTERNAL(efx))
return 0;
- /* Read MMD STATUS2 to check it is responding. */
- status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT2);
- if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
- ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
- MDIO_MMDREG_STAT2_PRESENT_VAL) {
- EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
- return -EIO;
+ if (mmd != MDIO_MMD_AN) {
+ /* Read MMD STATUS2 to check it is responding. */
+ status = mdio_clause45_read(efx, phy_id, mmd,
+ MDIO_MMDREG_STAT2);
+ if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) &
+ ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) !=
+ MDIO_MMDREG_STAT2_PRESENT_VAL) {
+ EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd);
+ return -EIO;
+ }
}
/* Read MMD STATUS 1 to check for fault. */
@@ -121,16 +124,18 @@ int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
int mdio_clause45_check_mmds(struct efx_nic *efx,
unsigned int mmd_mask, unsigned int fatal_mask)
{
- int devices, mmd = 0;
- int probe_mmd;
+ u32 devices;
+ int mmd = 0, probe_mmd;
/* Historically we have probed the PHYXS to find out what devices are
* present,but that doesn't work so well if the PHYXS isn't expected
* to exist, if so just find the first item in the list supplied. */
- probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS0_PHYXS) ? MDIO_MMD_PHYXS :
+ probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS_PHYXS) ? MDIO_MMD_PHYXS :
__ffs(mmd_mask);
- devices = mdio_clause45_read(efx, efx->mii.phy_id,
- probe_mmd, MDIO_MMDREG_DEVS0);
+ devices = (mdio_clause45_read(efx, efx->mii.phy_id,
+ probe_mmd, MDIO_MMDREG_DEVS0) |
+ mdio_clause45_read(efx, efx->mii.phy_id,
+ probe_mmd, MDIO_MMDREG_DEVS1) << 16);
/* Check all the expected MMDs are present */
if (devices < 0) {
@@ -162,7 +167,7 @@ int mdio_clause45_check_mmds(struct efx_nic *efx,
bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
{
int phy_id = efx->mii.phy_id;
- int status;
+ u32 reg;
bool ok = true;
int mmd = 0;
@@ -174,26 +179,33 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
return false;
else if (efx_phy_mode_disabled(efx->phy_mode))
return false;
- else if (efx->loopback_mode == LOOPBACK_PHYXS)
- mmd_mask &= ~(MDIO_MMDREG_DEVS0_PHYXS |
- MDIO_MMDREG_DEVS0_PCS |
- MDIO_MMDREG_DEVS0_PMAPMD);
- else if (efx->loopback_mode == LOOPBACK_PCS)
- mmd_mask &= ~(MDIO_MMDREG_DEVS0_PCS |
- MDIO_MMDREG_DEVS0_PMAPMD);
+ else if (efx->loopback_mode == LOOPBACK_PHYXS) {
+ mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS |
+ MDIO_MMDREG_DEVS_PCS |
+ MDIO_MMDREG_DEVS_PMAPMD |
+ MDIO_MMDREG_DEVS_AN);
+ if (!mmd_mask) {
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
+ MDIO_PHYXS_STATUS2);
+ return !(reg & (1 << MDIO_PHYXS_STATUS2_RX_FAULT_LBN));
+ }
+ } else if (efx->loopback_mode == LOOPBACK_PCS)
+ mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS |
+ MDIO_MMDREG_DEVS_PMAPMD |
+ MDIO_MMDREG_DEVS_AN);
else if (efx->loopback_mode == LOOPBACK_PMAPMD)
- mmd_mask &= ~MDIO_MMDREG_DEVS0_PMAPMD;
+ mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD |
+ MDIO_MMDREG_DEVS_AN);
while (mmd_mask) {
if (mmd_mask & 1) {
/* Double reads because link state is latched, and a
* read moves the current state into the register */
- status = mdio_clause45_read(efx, phy_id,
- mmd, MDIO_MMDREG_STAT1);
- status = mdio_clause45_read(efx, phy_id,
- mmd, MDIO_MMDREG_STAT1);
-
- ok = ok && (status & (1 << MDIO_MMDREG_STAT1_LINK_LBN));
+ reg = mdio_clause45_read(efx, phy_id,
+ mmd, MDIO_MMDREG_STAT1);
+ reg = mdio_clause45_read(efx, phy_id,
+ mmd, MDIO_MMDREG_STAT1);
+ ok = ok && (reg & (1 << MDIO_MMDREG_STAT1_LINK_LBN));
}
mmd_mask = (mmd_mask >> 1);
mmd++;
@@ -203,61 +215,73 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
void mdio_clause45_transmit_disable(struct efx_nic *efx)
{
- int phy_id = efx->mii.phy_id;
- int ctrl1, ctrl2;
-
- ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
- MDIO_MMDREG_TXDIS);
- if (efx->phy_mode & PHY_MODE_TX_DISABLED)
- ctrl2 |= (1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
- else
- ctrl1 &= ~(1 << MDIO_MMDREG_TXDIS_GLOBAL_LBN);
- if (ctrl1 != ctrl2)
- mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
- MDIO_MMDREG_TXDIS, ctrl2);
+ mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_TXDIS, MDIO_MMDREG_TXDIS_GLOBAL_LBN,
+ efx->phy_mode & PHY_MODE_TX_DISABLED);
}
void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
{
int phy_id = efx->mii.phy_id;
- int ctrl1, ctrl2;
- /* Handle (with debouncing) PMA/PMD loopback */
- ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
- MDIO_MMDREG_CTRL1);
-
- if (efx->loopback_mode == LOOPBACK_PMAPMD)
- ctrl2 |= (1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
- else
- ctrl2 &= ~(1 << MDIO_PMAPMD_CTRL1_LBACK_LBN);
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_CTRL1, MDIO_PMAPMD_CTRL1_LBACK_LBN,
+ efx->loopback_mode == LOOPBACK_PMAPMD);
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PCS,
+ MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN,
+ efx->loopback_mode == LOOPBACK_PCS);
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PHYXS,
+ MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN,
+ efx->loopback_mode == LOOPBACK_NETWORK);
+}
- if (ctrl1 != ctrl2)
- mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
- MDIO_MMDREG_CTRL1, ctrl2);
+static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx,
+ int lpower, int mmd)
+{
+ int phy = efx->mii.phy_id;
+ int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1);
- /* Handle (with debouncing) PCS loopback */
- ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
- MDIO_MMDREG_CTRL1);
- if (efx->loopback_mode == LOOPBACK_PCS)
- ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
- else
- ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
+ EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n",
+ mmd, lpower);
- if (ctrl1 != ctrl2)
- mdio_clause45_write(efx, phy_id, MDIO_MMD_PCS,
- MDIO_MMDREG_CTRL1, ctrl2);
+ if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) {
+ mdio_clause45_set_flag(efx, phy, mmd, MDIO_MMDREG_CTRL1,
+ MDIO_MMDREG_CTRL1_LPOWER_LBN, lpower);
+ }
+}
- /* Handle (with debouncing) PHYXS network loopback */
- ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
- MDIO_MMDREG_CTRL1);
- if (efx->loopback_mode == LOOPBACK_NETWORK)
- ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
- else
- ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LBACK_LBN);
+void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
+ int low_power, unsigned int mmd_mask)
+{
+ int mmd = 0;
+ mmd_mask &= ~MDIO_MMDREG_DEVS_AN;
+ while (mmd_mask) {
+ if (mmd_mask & 1)
+ mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
+ mmd_mask = (mmd_mask >> 1);
+ mmd++;
+ }
+}
- if (ctrl1 != ctrl2)
- mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
- MDIO_MMDREG_CTRL1, ctrl2);
+static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp)
+{
+ int phy_id = efx->mii.phy_id;
+ u32 result = 0;
+ int reg;
+
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, addr);
+ if (reg & ADVERTISE_10HALF)
+ result |= ADVERTISED_10baseT_Half;
+ if (reg & ADVERTISE_10FULL)
+ result |= ADVERTISED_10baseT_Full;
+ if (reg & ADVERTISE_100HALF)
+ result |= ADVERTISED_100baseT_Half;
+ if (reg & ADVERTISE_100FULL)
+ result |= ADVERTISED_100baseT_Full;
+ if (reg & LPA_RESV)
+ result |= xnp;
+
+ return result;
}
/**
@@ -266,95 +290,290 @@ void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
* @ecmd: Buffer for settings
*
* On return the 'port', 'speed', 'supported' and 'advertising' fields of
- * ecmd have been filled out based on the PMA type.
+ * ecmd have been filled out.
*/
void mdio_clause45_get_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd)
{
- int pma_type;
+ mdio_clause45_get_settings_ext(efx, ecmd, 0, 0);
+}
- /* If no PMA is present we are presumably talking something XAUI-ish
- * like CX4. Which we report as FIBRE (see below) */
- if ((efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)) == 0) {
- ecmd->speed = SPEED_10000;
- ecmd->port = PORT_FIBRE;
- ecmd->supported = SUPPORTED_FIBRE;
- ecmd->advertising = ADVERTISED_FIBRE;
- return;
- }
+/**
+ * mdio_clause45_get_settings_ext - Read (some of) the PHY settings over MDIO.
+ * @efx: Efx NIC
+ * @ecmd: Buffer for settings
+ * @xnp: Advertised Extended Next Page state
+ * @xnp_lpa: Link Partner's advertised XNP state
+ *
+ * On return the 'port', 'speed', 'supported' and 'advertising' fields of
+ * ecmd have been filled out.
+ */
+void mdio_clause45_get_settings_ext(struct efx_nic *efx,
+ struct ethtool_cmd *ecmd,
+ u32 xnp, u32 xnp_lpa)
+{
+ int phy_id = efx->mii.phy_id;
+ int reg;
- pma_type = mdio_clause45_read(efx, efx->mii.phy_id,
- MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL2);
- pma_type &= MDIO_PMAPMD_CTRL2_TYPE_MASK;
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->phy_address = phy_id;
- switch (pma_type) {
- /* We represent CX4 as fibre in the absence of anything
- better. */
- case MDIO_PMAPMD_CTRL2_10G_CX4:
- ecmd->speed = SPEED_10000;
- ecmd->port = PORT_FIBRE;
- ecmd->supported = SUPPORTED_FIBRE;
- ecmd->advertising = ADVERTISED_FIBRE;
- break;
- /* 10G Base-T */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_CTRL2);
+ switch (reg & MDIO_PMAPMD_CTRL2_TYPE_MASK) {
case MDIO_PMAPMD_CTRL2_10G_BT:
- ecmd->speed = SPEED_10000;
- ecmd->port = PORT_TP;
- ecmd->supported = SUPPORTED_TP | SUPPORTED_10000baseT_Full;
- ecmd->advertising = (ADVERTISED_FIBRE
- | ADVERTISED_10000baseT_Full);
- break;
case MDIO_PMAPMD_CTRL2_1G_BT:
- ecmd->speed = SPEED_1000;
- ecmd->port = PORT_TP;
- ecmd->supported = SUPPORTED_TP | SUPPORTED_1000baseT_Full;
- ecmd->advertising = (ADVERTISED_FIBRE
- | ADVERTISED_1000baseT_Full);
- break;
case MDIO_PMAPMD_CTRL2_100_BT:
- ecmd->speed = SPEED_100;
- ecmd->port = PORT_TP;
- ecmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
- ecmd->advertising = (ADVERTISED_FIBRE
- | ADVERTISED_100baseT_Full);
- break;
case MDIO_PMAPMD_CTRL2_10_BT:
- ecmd->speed = SPEED_10;
ecmd->port = PORT_TP;
- ecmd->supported = SUPPORTED_TP | SUPPORTED_10baseT_Full;
- ecmd->advertising = ADVERTISED_FIBRE | ADVERTISED_10baseT_Full;
+ ecmd->supported = SUPPORTED_TP;
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_SPEED);
+ if (reg & (1 << MDIO_MMDREG_SPEED_10G_LBN))
+ ecmd->supported |= SUPPORTED_10000baseT_Full;
+ if (reg & (1 << MDIO_MMDREG_SPEED_1000M_LBN))
+ ecmd->supported |= (SUPPORTED_1000baseT_Full |
+ SUPPORTED_1000baseT_Half);
+ if (reg & (1 << MDIO_MMDREG_SPEED_100M_LBN))
+ ecmd->supported |= (SUPPORTED_100baseT_Full |
+ SUPPORTED_100baseT_Half);
+ if (reg & (1 << MDIO_MMDREG_SPEED_10M_LBN))
+ ecmd->supported |= (SUPPORTED_10baseT_Full |
+ SUPPORTED_10baseT_Half);
+ ecmd->advertising = ADVERTISED_TP;
break;
- /* All the other defined modes are flavours of
- * 10G optical */
+
+ /* We represent CX4 as fibre in the absence of anything better */
+ case MDIO_PMAPMD_CTRL2_10G_CX4:
+ /* All the other defined modes are flavours of optical */
default:
- ecmd->speed = SPEED_10000;
ecmd->port = PORT_FIBRE;
ecmd->supported = SUPPORTED_FIBRE;
ecmd->advertising = ADVERTISED_FIBRE;
break;
}
+
+ if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
+ ecmd->supported |= SUPPORTED_Autoneg;
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_CTRL1);
+ if (reg & BMCR_ANENABLE) {
+ ecmd->autoneg = AUTONEG_ENABLE;
+ ecmd->advertising |=
+ ADVERTISED_Autoneg |
+ mdio_clause45_get_an(efx,
+ MDIO_AN_ADVERTISE, xnp);
+ } else
+ ecmd->autoneg = AUTONEG_DISABLE;
+ } else
+ ecmd->autoneg = AUTONEG_DISABLE;
+
+ if (ecmd->autoneg) {
+ /* If AN is complete, report best common mode,
+ * otherwise report best advertised mode. */
+ u32 common = ecmd->advertising;
+ if (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_STAT1) &
+ (1 << MDIO_AN_STATUS_AN_DONE_LBN)) {
+ common &= mdio_clause45_get_an(efx, MDIO_AN_LPA,
+ xnp_lpa);
+ }
+ if (common & ADVERTISED_10000baseT_Full) {
+ ecmd->speed = SPEED_10000;
+ ecmd->duplex = DUPLEX_FULL;
+ } else if (common & (ADVERTISED_1000baseT_Full |
+ ADVERTISED_1000baseT_Half)) {
+ ecmd->speed = SPEED_1000;
+ ecmd->duplex = !!(common & ADVERTISED_1000baseT_Full);
+ } else if (common & (ADVERTISED_100baseT_Full |
+ ADVERTISED_100baseT_Half)) {
+ ecmd->speed = SPEED_100;
+ ecmd->duplex = !!(common & ADVERTISED_100baseT_Full);
+ } else {
+ ecmd->speed = SPEED_10;
+ ecmd->duplex = !!(common & ADVERTISED_10baseT_Full);
+ }
+ } else {
+ /* Report forced settings */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_CTRL1);
+ ecmd->speed = (((reg & BMCR_SPEED1000) ? 100 : 1) *
+ ((reg & BMCR_SPEED100) ? 100 : 10));
+ ecmd->duplex = (reg & BMCR_FULLDPLX ||
+ ecmd->speed == SPEED_10000);
+ }
}
/**
* mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO.
* @efx: Efx NIC
* @ecmd: New settings
- *
- * Currently this just enforces that we are _not_ changing the
- * 'port', 'speed', 'supported' or 'advertising' settings as these
- * cannot be changed on any currently supported PHY.
*/
int mdio_clause45_set_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd)
{
- struct ethtool_cmd tmpcmd;
- mdio_clause45_get_settings(efx, &tmpcmd);
- /* None of the current PHYs support more than one mode
- * of operation (and only 10GBT ever will), so keep things
- * simple for now */
- if ((ecmd->speed == tmpcmd.speed) && (ecmd->port == tmpcmd.port) &&
- (ecmd->supported == tmpcmd.supported) &&
- (ecmd->advertising == tmpcmd.advertising))
+ int phy_id = efx->mii.phy_id;
+ struct ethtool_cmd prev;
+ u32 required;
+ int ctrl1_bits, reg;
+
+ efx->phy_op->get_settings(efx, &prev);
+
+ if (ecmd->advertising == prev.advertising &&
+ ecmd->speed == prev.speed &&
+ ecmd->duplex == prev.duplex &&
+ ecmd->port == prev.port &&
+ ecmd->autoneg == prev.autoneg)
return 0;
- return -EOPNOTSUPP;
+
+ /* We can only change these settings for -T PHYs */
+ if (prev.port != PORT_TP || ecmd->port != PORT_TP)
+ return -EINVAL;
+
+ /* Check that PHY supports these settings and work out the
+ * basic control bits */
+ if (ecmd->duplex) {
+ switch (ecmd->speed) {
+ case SPEED_10:
+ ctrl1_bits = BMCR_FULLDPLX;
+ required = SUPPORTED_10baseT_Full;
+ break;
+ case SPEED_100:
+ ctrl1_bits = BMCR_SPEED100 | BMCR_FULLDPLX;
+ required = SUPPORTED_100baseT_Full;
+ break;
+ case SPEED_1000:
+ ctrl1_bits = BMCR_SPEED1000 | BMCR_FULLDPLX;
+ required = SUPPORTED_1000baseT_Full;
+ break;
+ case SPEED_10000:
+ ctrl1_bits = (BMCR_SPEED1000 | BMCR_SPEED100 |
+ BMCR_FULLDPLX);
+ required = SUPPORTED_10000baseT_Full;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (ecmd->speed) {
+ case SPEED_10:
+ ctrl1_bits = 0;
+ required = SUPPORTED_10baseT_Half;
+ break;
+ case SPEED_100:
+ ctrl1_bits = BMCR_SPEED100;
+ required = SUPPORTED_100baseT_Half;
+ break;
+ case SPEED_1000:
+ ctrl1_bits = BMCR_SPEED1000;
+ required = SUPPORTED_1000baseT_Half;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (ecmd->autoneg)
+ required |= SUPPORTED_Autoneg;
+ required |= ecmd->advertising;
+ if (required & ~prev.supported)
+ return -EINVAL;
+
+ /* Set the basic control bits */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ MDIO_MMDREG_CTRL1);
+ reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | 0x003c);
+ reg |= ctrl1_bits;
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL1,
+ reg);
+
+ /* Set the AN registers */
+ if (ecmd->autoneg != prev.autoneg ||
+ ecmd->advertising != prev.advertising) {
+ bool xnp = false;
+
+ if (efx->phy_op->set_xnp_advertise)
+ xnp = efx->phy_op->set_xnp_advertise(efx,
+ ecmd->advertising);
+
+ if (ecmd->autoneg) {
+ reg = 0;
+ if (ecmd->advertising & ADVERTISED_10baseT_Half)
+ reg |= ADVERTISE_10HALF;
+ if (ecmd->advertising & ADVERTISED_10baseT_Full)
+ reg |= ADVERTISE_10FULL;
+ if (ecmd->advertising & ADVERTISED_100baseT_Half)
+ reg |= ADVERTISE_100HALF;
+ if (ecmd->advertising & ADVERTISED_100baseT_Full)
+ reg |= ADVERTISE_100FULL;
+ if (xnp)
+ reg |= ADVERTISE_RESV;
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+ MDIO_AN_ADVERTISE, reg);
+ }
+
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_CTRL1);
+ if (ecmd->autoneg)
+ reg |= BMCR_ANENABLE | BMCR_ANRESTART;
+ else
+ reg &= ~BMCR_ANENABLE;
+ if (xnp)
+ reg |= 1 << MDIO_AN_CTRL_XNP_LBN;
+ else
+ reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN);
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_CTRL1, reg);
+ }
+
+ return 0;
+}
+
+void mdio_clause45_set_pause(struct efx_nic *efx)
+{
+ int phy_id = efx->mii.phy_id;
+ int reg;
+
+ if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) {
+ /* Set pause capability advertising */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_AN_ADVERTISE);
+ reg &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ reg |= efx_fc_advertise(efx->wanted_fc);
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+ MDIO_AN_ADVERTISE, reg);
+
+ /* Restart auto-negotiation */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_CTRL1);
+ if (reg & BMCR_ANENABLE) {
+ reg |= BMCR_ANRESTART;
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_AN,
+ MDIO_MMDREG_CTRL1, reg);
+ }
+ }
+}
+
+enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx)
+{
+ int phy_id = efx->mii.phy_id;
+ int lpa;
+
+ if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)))
+ return efx->wanted_fc;
+ lpa = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_AN_LPA);
+ return efx_fc_resolve(efx->wanted_fc, lpa);
+}
+
+void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
+ u16 addr, int bit, bool sense)
+{
+ int old_val = mdio_clause45_read(efx, prt, dev, addr);
+ int new_val;
+
+ if (sense)
+ new_val = old_val | (1 << bit);
+ else
+ new_val = old_val & ~(1 << bit);
+ if (old_val != new_val)
+ mdio_clause45_write(efx, prt, dev, addr, new_val);
}
diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h
index 19c42eaf7fb4..09bf801d0569 100644
--- a/drivers/net/sfc/mdio_10g.h
+++ b/drivers/net/sfc/mdio_10g.h
@@ -33,6 +33,8 @@
#define MDIO_MMD_TC (6)
/* Auto negotiation */
#define MDIO_MMD_AN (7)
+/* Clause 22 extension */
+#define MDIO_MMD_C22EXT 29
/* Generic register locations */
#define MDIO_MMDREG_CTRL1 (0)
@@ -54,6 +56,9 @@
/* Loopback bit for WIS, PCS, PHYSX and DTEXS */
#define MDIO_MMDREG_CTRL1_LBACK_LBN (14)
#define MDIO_MMDREG_CTRL1_LBACK_WIDTH (1)
+/* Low power */
+#define MDIO_MMDREG_CTRL1_LPOWER_LBN (11)
+#define MDIO_MMDREG_CTRL1_LPOWER_WIDTH (1)
/* Bits in MMDREG_STAT1 */
#define MDIO_MMDREG_STAT1_FAULT_LBN (7)
@@ -70,14 +75,26 @@
#define MDIO_ID_MODEL(_id32) ((_id32 >> 4) & 0x3f)
#define MDIO_ID_OUI(_id32) (_id32 >> 10)
-/* Bits in MMDREG_DEVS0. Someone thoughtfully layed things out
+/* Bits in MMDREG_DEVS0/1. Someone thoughtfully layed things out
* so the 'bit present' bit number of an MMD is the number of
* that MMD */
#define DEV_PRESENT_BIT(_b) (1 << _b)
-#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
-#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS)
-#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
+#define MDIO_MMDREG_DEVS_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS)
+#define MDIO_MMDREG_DEVS_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS)
+#define MDIO_MMDREG_DEVS_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD)
+#define MDIO_MMDREG_DEVS_AN DEV_PRESENT_BIT(MDIO_MMD_AN)
+#define MDIO_MMDREG_DEVS_C22EXT DEV_PRESENT_BIT(MDIO_MMD_C22EXT)
+
+/* Bits in MMDREG_SPEED */
+#define MDIO_MMDREG_SPEED_10G_LBN 0
+#define MDIO_MMDREG_SPEED_10G_WIDTH 1
+#define MDIO_MMDREG_SPEED_1000M_LBN 4
+#define MDIO_MMDREG_SPEED_1000M_WIDTH 1
+#define MDIO_MMDREG_SPEED_100M_LBN 5
+#define MDIO_MMDREG_SPEED_100M_WIDTH 1
+#define MDIO_MMDREG_SPEED_10M_LBN 6
+#define MDIO_MMDREG_SPEED_10M_WIDTH 1
/* Bits in MMDREG_STAT2 */
#define MDIO_MMDREG_STAT2_PRESENT_VAL (2)
@@ -111,17 +128,34 @@
#define MDIO_PMAPMD_CTRL2_10_BT (0xf)
#define MDIO_PMAPMD_CTRL2_TYPE_MASK (0xf)
+/* PMA 10GBT registers */
+#define MDIO_PMAPMD_10GBT_TXPWR (131)
+#define MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN (0)
+#define MDIO_PMAPMD_10GBT_TXPWR_SHORT_WIDTH (1)
+
+/* PHY XGXS Status 2 */
+#define MDIO_PHYXS_STATUS2 (8)
+#define MDIO_PHYXS_STATUS2_RX_FAULT_LBN 10
+
/* PHY XGXS lane state */
#define MDIO_PHYXS_LANE_STATE (0x18)
#define MDIO_PHYXS_LANE_ALIGNED_LBN (12)
/* AN registers */
+#define MDIO_AN_CTRL_XNP_LBN 13
#define MDIO_AN_STATUS (1)
#define MDIO_AN_STATUS_XNP_LBN (7)
#define MDIO_AN_STATUS_PAGE_LBN (6)
#define MDIO_AN_STATUS_AN_DONE_LBN (5)
#define MDIO_AN_STATUS_LP_AN_CAP_LBN (0)
+#define MDIO_AN_ADVERTISE 16
+#define MDIO_AN_ADVERTISE_XNP_LBN 12
+#define MDIO_AN_LPA 19
+#define MDIO_AN_XNP 22
+#define MDIO_AN_LPA_XNP 25
+
+#define MDIO_AN_10GBT_ADVERTISE 32
#define MDIO_AN_10GBT_STATUS (33)
#define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */
#define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */
@@ -240,16 +274,37 @@ extern void mdio_clause45_transmit_disable(struct efx_nic *efx);
/* Generic part of reconfigure: set/clear loopback bits */
extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx);
+/* Set the power state of the specified MMDs */
+extern void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
+ int low_power, unsigned int mmd_mask);
+
/* Read (some of) the PHY settings over MDIO */
extern void mdio_clause45_get_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd);
+/* Read (some of) the PHY settings over MDIO */
+extern void
+mdio_clause45_get_settings_ext(struct efx_nic *efx, struct ethtool_cmd *ecmd,
+ u32 xnp, u32 xnp_lpa);
+
/* Set (some of) the PHY settings over MDIO */
extern int mdio_clause45_set_settings(struct efx_nic *efx,
struct ethtool_cmd *ecmd);
+/* Set pause parameters to be advertised through AN (if available) */
+extern void mdio_clause45_set_pause(struct efx_nic *efx);
+
+/* Get pause parameters from AN if available (otherwise return
+ * requested pause parameters)
+ */
+enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx);
+
/* Wait for specified MMDs to exit reset within a timeout */
extern int mdio_clause45_wait_reset_mmds(struct efx_nic *efx,
unsigned int mmd_mask);
+/* Set or clear flag, debouncing */
+extern void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev,
+ u16 addr, int bit, bool sense);
+
#endif /* EFX_MDIO_10G_H */
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c
new file mode 100644
index 000000000000..665cafb88d6a
--- /dev/null
+++ b/drivers/net/sfc/mtd.c
@@ -0,0 +1,268 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications 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, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/delay.h>
+
+#define EFX_DRIVER_NAME "sfc_mtd"
+#include "net_driver.h"
+#include "spi.h"
+
+#define EFX_SPI_VERIFY_BUF_LEN 16
+
+struct efx_mtd {
+ const struct efx_spi_device *spi;
+ struct mtd_info mtd;
+ char name[IFNAMSIZ + 20];
+};
+
+/* SPI utilities */
+
+static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
+{
+ const struct efx_spi_device *spi = efx_mtd->spi;
+ struct efx_nic *efx = spi->efx;
+ u8 status;
+ int rc, i;
+
+ /* Wait up to 4s for flash/EEPROM to finish a slow operation. */
+ for (i = 0; i < 40; i++) {
+ __set_current_state(uninterruptible ?
+ TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
+ &status, sizeof(status));
+ if (rc)
+ return rc;
+ if (!(status & SPI_STATUS_NRDY))
+ return 0;
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name);
+ return -ETIMEDOUT;
+}
+
+static int efx_spi_unlock(const struct efx_spi_device *spi)
+{
+ const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
+ SPI_STATUS_BP0);
+ u8 status;
+ int rc;
+
+ rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status));
+ if (rc)
+ return rc;
+
+ if (!(status & unlock_mask))
+ return 0; /* already unlocked */
+
+ rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+ if (rc)
+ return rc;
+ rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
+ if (rc)
+ return rc;
+
+ status &= ~unlock_mask;
+ rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
+ if (rc)
+ return rc;
+ rc = falcon_spi_wait_write(spi);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
+{
+ const struct efx_spi_device *spi = efx_mtd->spi;
+ unsigned pos, block_len;
+ u8 empty[EFX_SPI_VERIFY_BUF_LEN];
+ u8 buffer[EFX_SPI_VERIFY_BUF_LEN];
+ int rc;
+
+ if (len != spi->erase_size)
+ return -EINVAL;
+
+ if (spi->erase_command == 0)
+ return -EOPNOTSUPP;
+
+ rc = efx_spi_unlock(spi);
+ if (rc)
+ return rc;
+ rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+ if (rc)
+ return rc;
+ rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0);
+ if (rc)
+ return rc;
+ rc = efx_spi_slow_wait(efx_mtd, false);
+
+ /* Verify the entire region has been wiped */
+ memset(empty, 0xff, sizeof(empty));
+ for (pos = 0; pos < len; pos += block_len) {
+ block_len = min(len - pos, sizeof(buffer));
+ rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer);
+ if (rc)
+ return rc;
+ if (memcmp(empty, buffer, block_len))
+ return -EIO;
+
+ /* Avoid locking up the system */
+ cond_resched();
+ if (signal_pending(current))
+ return -EINTR;
+ }
+
+ return rc;
+}
+
+/* MTD interface */
+
+static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
+ size_t *retlen, u8 *buffer)
+{
+ struct efx_mtd *efx_mtd = mtd->priv;
+ const struct efx_spi_device *spi = efx_mtd->spi;
+ struct efx_nic *efx = spi->efx;
+ int rc;
+
+ rc = mutex_lock_interruptible(&efx->spi_lock);
+ if (rc)
+ return rc;
+ rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start,
+ len, retlen, buffer);
+ mutex_unlock(&efx->spi_lock);
+ return rc;
+}
+
+static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
+{
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->spi->efx;
+ int rc;
+
+ rc = mutex_lock_interruptible(&efx->spi_lock);
+ if (rc)
+ return rc;
+ rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr,
+ erase->len);
+ mutex_unlock(&efx->spi_lock);
+
+ if (rc == 0) {
+ erase->state = MTD_ERASE_DONE;
+ } else {
+ erase->state = MTD_ERASE_FAILED;
+ erase->fail_addr = 0xffffffff;
+ }
+ mtd_erase_callback(erase);
+ return rc;
+}
+
+static int efx_mtd_write(struct mtd_info *mtd, loff_t start,
+ size_t len, size_t *retlen, const u8 *buffer)
+{
+ struct efx_mtd *efx_mtd = mtd->priv;
+ const struct efx_spi_device *spi = efx_mtd->spi;
+ struct efx_nic *efx = spi->efx;
+ int rc;
+
+ rc = mutex_lock_interruptible(&efx->spi_lock);
+ if (rc)
+ return rc;
+ rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start,
+ len, retlen, buffer);
+ mutex_unlock(&efx->spi_lock);
+ return rc;
+}
+
+static void efx_mtd_sync(struct mtd_info *mtd)
+{
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->spi->efx;
+ int rc;
+
+ mutex_lock(&efx->spi_lock);
+ rc = efx_spi_slow_wait(efx_mtd, true);
+ mutex_unlock(&efx->spi_lock);
+
+ if (rc)
+ EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
+ return;
+}
+
+void efx_mtd_remove(struct efx_nic *efx)
+{
+ if (efx->spi_flash && efx->spi_flash->mtd) {
+ struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
+ int rc;
+
+ for (;;) {
+ rc = del_mtd_device(&efx_mtd->mtd);
+ if (rc != -EBUSY)
+ break;
+ ssleep(1);
+ }
+ WARN_ON(rc);
+ kfree(efx_mtd);
+ }
+}
+
+void efx_mtd_rename(struct efx_nic *efx)
+{
+ if (efx->spi_flash && efx->spi_flash->mtd) {
+ struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
+ snprintf(efx_mtd->name, sizeof(efx_mtd->name),
+ "%s sfc_flash_bootrom", efx->name);
+ }
+}
+
+int efx_mtd_probe(struct efx_nic *efx)
+{
+ struct efx_spi_device *spi = efx->spi_flash;
+ struct efx_mtd *efx_mtd;
+
+ if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
+ return -ENODEV;
+
+ efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL);
+ if (!efx_mtd)
+ return -ENOMEM;
+
+ efx_mtd->spi = spi;
+ spi->mtd = efx_mtd;
+
+ efx_mtd->mtd.type = MTD_NORFLASH;
+ efx_mtd->mtd.flags = MTD_CAP_NORFLASH;
+ efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
+ efx_mtd->mtd.erasesize = spi->erase_size;
+ efx_mtd->mtd.writesize = 1;
+ efx_mtd_rename(efx);
+
+ efx_mtd->mtd.owner = THIS_MODULE;
+ efx_mtd->mtd.priv = efx_mtd;
+ efx_mtd->mtd.name = efx_mtd->name;
+ efx_mtd->mtd.erase = efx_mtd_erase;
+ efx_mtd->mtd.read = efx_mtd_read;
+ efx_mtd->mtd.write = efx_mtd_write;
+ efx_mtd->mtd.sync = efx_mtd_sync;
+
+ if (add_mtd_device(&efx_mtd->mtd)) {
+ kfree(efx_mtd);
+ spi->mtd = NULL;
+ /* add_mtd_device() returns 1 if the MTD table is full */
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index cdb11fad6050..5f255f75754e 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -42,7 +42,7 @@
#ifndef EFX_DRIVER_NAME
#define EFX_DRIVER_NAME "sfc"
#endif
-#define EFX_DRIVER_VERSION "2.2"
+#define EFX_DRIVER_VERSION "2.3"
#ifdef EFX_ENABLE_DEBUG
#define EFX_BUG_ON_PARANOID(x) BUG_ON(x)
@@ -327,6 +327,7 @@ enum efx_rx_alloc_method {
*
* @efx: Associated Efx NIC
* @channel: Channel instance number
+ * @name: Name for channel and IRQ
* @used_flags: Channel is used by net driver
* @enabled: Channel enabled indicator
* @irq: IRQ number (MSI and MSI-X only)
@@ -357,6 +358,7 @@ enum efx_rx_alloc_method {
struct efx_channel {
struct efx_nic *efx;
int channel;
+ char name[IFNAMSIZ + 6];
int used_flags;
bool enabled;
int irq;
@@ -414,6 +416,7 @@ struct efx_blinker {
* @init_leds: Sets up board LEDs
* @set_fault_led: Turns the fault LED on or off
* @blink: Starts/stops blinking
+ * @monitor: Board-specific health check function
* @fini: Cleanup function
* @blinker: used to blink LEDs in software
* @hwmon_client: I2C client for hardware monitor
@@ -428,6 +431,7 @@ struct efx_board {
* have a separate init callback that happens later than
* board init. */
int (*init_leds)(struct efx_nic *efx);
+ int (*monitor) (struct efx_nic *nic);
void (*set_fault_led) (struct efx_nic *efx, bool state);
void (*blink) (struct efx_nic *efx, bool start);
void (*fini) (struct efx_nic *nic);
@@ -449,16 +453,20 @@ enum efx_int_mode {
enum phy_type {
PHY_TYPE_NONE = 0,
- PHY_TYPE_CX4_RTMR = 1,
- PHY_TYPE_1G_ALASKA = 2,
- PHY_TYPE_10XPRESS = 3,
- PHY_TYPE_XFP = 4,
+ PHY_TYPE_TXC43128 = 1,
+ PHY_TYPE_88E1111 = 2,
+ PHY_TYPE_SFX7101 = 3,
+ PHY_TYPE_QT2022C2 = 4,
PHY_TYPE_PM8358 = 6,
+ PHY_TYPE_SFT9001A = 8,
+ PHY_TYPE_SFT9001B = 10,
PHY_TYPE_MAX /* Insert any new items before this */
};
#define PHY_ADDR_INVALID 0xff
+#define EFX_IS10G(efx) ((efx)->link_speed == 10000)
+
enum nic_state {
STATE_INIT = 0,
STATE_RUNNING = 1,
@@ -499,6 +507,55 @@ enum efx_fc_type {
EFX_FC_AUTO = 4,
};
+/* Supported MAC bit-mask */
+enum efx_mac_type {
+ EFX_GMAC = 1,
+ EFX_XMAC = 2,
+};
+
+static inline unsigned int efx_fc_advertise(enum efx_fc_type wanted_fc)
+{
+ unsigned int adv = 0;
+ if (wanted_fc & EFX_FC_RX)
+ adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+ if (wanted_fc & EFX_FC_TX)
+ adv ^= ADVERTISE_PAUSE_ASYM;
+ return adv;
+}
+
+static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc,
+ unsigned int lpa)
+{
+ unsigned int adv = efx_fc_advertise(wanted_fc);
+
+ if (!(wanted_fc & EFX_FC_AUTO))
+ return wanted_fc;
+
+ if (adv & lpa & ADVERTISE_PAUSE_CAP)
+ return EFX_FC_RX | EFX_FC_TX;
+ if (adv & lpa & ADVERTISE_PAUSE_ASYM) {
+ if (adv & ADVERTISE_PAUSE_CAP)
+ return EFX_FC_RX;
+ if (lpa & ADVERTISE_PAUSE_CAP)
+ return EFX_FC_TX;
+ }
+ return 0;
+}
+
+/**
+ * struct efx_mac_operations - Efx MAC operations table
+ * @reconfigure: Reconfigure MAC. Serialised by the mac_lock
+ * @update_stats: Update statistics
+ * @irq: Hardware MAC event callback. Serialised by the mac_lock
+ * @poll: Poll for hardware state. Serialised by the mac_lock
+ */
+struct efx_mac_operations {
+ void (*reconfigure) (struct efx_nic *efx);
+ void (*update_stats) (struct efx_nic *efx);
+ void (*irq) (struct efx_nic *efx);
+ void (*poll) (struct efx_nic *efx);
+};
+
/**
* struct efx_phy_operations - Efx PHY operations table
* @init: Initialise PHY
@@ -506,17 +563,33 @@ enum efx_fc_type {
* @reconfigure: Reconfigure PHY (e.g. for new link parameters)
* @clear_interrupt: Clear down interrupt
* @blink: Blink LEDs
- * @check_hw: Check hardware
+ * @poll: Poll for hardware state. Serialised by the mac_lock.
+ * @get_settings: Get ethtool settings. Serialised by the mac_lock.
+ * @set_settings: Set ethtool settings. Serialised by the mac_lock.
+ * @set_xnp_advertise: Set abilities advertised in Extended Next Page
+ * (only needed where AN bit is set in mmds)
+ * @num_tests: Number of PHY-specific tests/results
+ * @test_names: Names of the tests/results
+ * @run_tests: Run tests and record results as appropriate.
+ * Flags are the ethtool tests flags.
* @mmds: MMD presence mask
* @loopbacks: Supported loopback modes mask
*/
struct efx_phy_operations {
+ enum efx_mac_type macs;
int (*init) (struct efx_nic *efx);
void (*fini) (struct efx_nic *efx);
void (*reconfigure) (struct efx_nic *efx);
void (*clear_interrupt) (struct efx_nic *efx);
- int (*check_hw) (struct efx_nic *efx);
- int (*test) (struct efx_nic *efx);
+ void (*poll) (struct efx_nic *efx);
+ void (*get_settings) (struct efx_nic *efx,
+ struct ethtool_cmd *ecmd);
+ int (*set_settings) (struct efx_nic *efx,
+ struct ethtool_cmd *ecmd);
+ bool (*set_xnp_advertise) (struct efx_nic *efx, u32);
+ u32 num_tests;
+ const char *const *test_names;
+ int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags);
int mmds;
unsigned loopbacks;
};
@@ -525,11 +598,15 @@ struct efx_phy_operations {
* @enum efx_phy_mode - PHY operating mode flags
* @PHY_MODE_NORMAL: on and should pass traffic
* @PHY_MODE_TX_DISABLED: on with TX disabled
+ * @PHY_MODE_LOW_POWER: set to low power through MDIO
+ * @PHY_MODE_OFF: switched off through external control
* @PHY_MODE_SPECIAL: on but will not pass traffic
*/
enum efx_phy_mode {
PHY_MODE_NORMAL = 0,
PHY_MODE_TX_DISABLED = 1,
+ PHY_MODE_LOW_POWER = 2,
+ PHY_MODE_OFF = 4,
PHY_MODE_SPECIAL = 8,
};
@@ -629,7 +706,7 @@ union efx_multicast_hash {
* @legacy_irq: IRQ number
* @workqueue: Workqueue for port reconfigures and the HW monitor.
* Work items do not hold and must not acquire RTNL.
- * @reset_workqueue: Workqueue for resets. Work item will acquire RTNL.
+ * @workqueue_name: Name of workqueue
* @reset_work: Scheduled reset workitem
* @monitor_work: Hardware monitor workitem
* @membase_phys: Memory BAR value as physical address
@@ -644,6 +721,7 @@ union efx_multicast_hash {
* @rx_queue: RX DMA queues
* @channel: Channels
* @n_rx_queues: Number of RX queues
+ * @n_channels: Number of channels in use
* @rx_buffer_len: RX buffer length
* @rx_buffer_order: Order (log2) of number of pages for each RX buffer
* @irq_status: Interrupt status buffer
@@ -655,15 +733,16 @@ union efx_multicast_hash {
* This field will be %NULL if no flash device is present.
* @spi_eeprom: SPI EEPROM device
* This field will be %NULL if no EEPROM device is present.
+ * @spi_lock: SPI bus lock
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
* @nic_data: Hardware dependant state
* @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
* @port_inhibited, efx_monitor() and efx_reconfigure_port()
* @port_enabled: Port enabled indicator.
- * Serialises efx_stop_all(), efx_start_all() and efx_monitor() and
- * efx_reconfigure_work with kernel interfaces. Safe to read under any
- * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must
- * be held to modify it.
+ * Serialises efx_stop_all(), efx_start_all(), efx_monitor(),
+ * efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read
+ * under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all
+ * three must be held to modify it.
* @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock
* @port_initialized: Port initialized?
* @net_dev: Operating system network device. Consider holding the rtnl lock
@@ -677,6 +756,7 @@ union efx_multicast_hash {
* @stats_lock: Statistics update lock. Serialises statistics fetches
* @stats_enabled: Temporarily disable statistics fetches.
* Serialised by @stats_lock
+ * @mac_op: MAC interface
* @mac_address: Permanent MAC address
* @phy_type: PHY type
* @phy_lock: PHY access lock
@@ -684,13 +764,17 @@ union efx_multicast_hash {
* @phy_data: PHY private data (including PHY-specific stats)
* @mii: PHY interface
* @phy_mode: PHY operating mode. Serialised by @mac_lock.
+ * @mac_up: MAC link state
* @link_up: Link status
- * @link_options: Link options (MII/GMII format)
+ * @link_fd: Link is full duplex
+ * @link_fc: Actualy flow control flags
+ * @link_speed: Link speed (Mbps)
* @n_link_state_changes: Number of times the link has changed state
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
* @multicast_hash: Multicast hash table
- * @flow_control: Flow control flags - separate RX/TX so can't use link_options
- * @reconfigure_work: work item for dealing with PHY events
+ * @wanted_fc: Wanted flow control flags
+ * @phy_work: work item for dealing with PHY events
+ * @mac_work: work item for dealing with MAC events
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
* @loopback_selftest: Offline self-test private state
@@ -704,7 +788,7 @@ struct efx_nic {
const struct efx_nic_type *type;
int legacy_irq;
struct workqueue_struct *workqueue;
- struct workqueue_struct *reset_workqueue;
+ char workqueue_name[16];
struct work_struct reset_work;
struct delayed_work monitor_work;
resource_size_t membase_phys;
@@ -723,6 +807,7 @@ struct efx_nic {
struct efx_channel channel[EFX_MAX_CHANNELS];
int n_rx_queues;
+ int n_channels;
unsigned int rx_buffer_len;
unsigned int rx_buffer_order;
@@ -731,12 +816,14 @@ struct efx_nic {
struct efx_spi_device *spi_flash;
struct efx_spi_device *spi_eeprom;
+ struct mutex spi_lock;
unsigned n_rx_nodesc_drop_cnt;
struct falcon_nic_data *nic_data;
struct mutex mac_lock;
+ struct work_struct mac_work;
bool port_enabled;
bool port_inhibited;
@@ -752,23 +839,27 @@ struct efx_nic {
spinlock_t stats_lock;
bool stats_enabled;
+ struct efx_mac_operations *mac_op;
unsigned char mac_address[ETH_ALEN];
enum phy_type phy_type;
spinlock_t phy_lock;
+ struct work_struct phy_work;
struct efx_phy_operations *phy_op;
void *phy_data;
struct mii_if_info mii;
enum efx_phy_mode phy_mode;
+ bool mac_up;
bool link_up;
- unsigned int link_options;
+ bool link_fd;
+ enum efx_fc_type link_fc;
+ unsigned int link_speed;
unsigned int n_link_state_changes;
bool promiscuous;
union efx_multicast_hash multicast_hash;
- enum efx_fc_type flow_control;
- struct work_struct reconfigure_work;
+ enum efx_fc_type wanted_fc;
atomic_t rx_reset;
enum efx_loopback_mode loopback_mode;
diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h
index f746536f4ffa..58c493ef81bb 100644
--- a/drivers/net/sfc/phy.h
+++ b/drivers/net/sfc/phy.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -11,9 +11,10 @@
#define EFX_PHY_H
/****************************************************************************
- * 10Xpress (SFX7101) PHY
+ * 10Xpress (SFX7101 and SFT9001) PHYs
*/
-extern struct efx_phy_operations falcon_tenxpress_phy_ops;
+extern struct efx_phy_operations falcon_sfx7101_phy_ops;
+extern struct efx_phy_operations falcon_sft9001_phy_ops;
extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
extern void tenxpress_crc_err(struct efx_nic *efx);
diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c
index 0f805da4ce55..b8ba4bbad889 100644
--- a/drivers/net/sfc/rx.c
+++ b/drivers/net/sfc/rx.c
@@ -752,7 +752,7 @@ void __efx_rx_packet(struct efx_channel *channel,
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
done:
- efx->net_dev->last_rx = jiffies;
+ ;
}
void efx_rx_strategy(struct efx_channel *channel)
diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c
index 362956e3fe17..dba0d64d50cd 100644
--- a/drivers/net/sfc/selftest.c
+++ b/drivers/net/sfc/selftest.c
@@ -26,7 +26,6 @@
#include "selftest.h"
#include "boards.h"
#include "workarounds.h"
-#include "mac.h"
#include "spi.h"
#include "falcon_io.h"
#include "mdio_10g.h"
@@ -105,9 +104,11 @@ static int efx_test_mii(struct efx_nic *efx, struct efx_self_tests *tests)
goto out;
}
- rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0);
- if (rc)
- goto out;
+ if (EFX_IS10G(efx)) {
+ rc = mdio_clause45_check_mmds(efx, efx->phy_op->mmds, 0);
+ if (rc)
+ goto out;
+ }
out:
mutex_unlock(&efx->mac_lock);
@@ -246,17 +247,20 @@ static int efx_test_eventq_irq(struct efx_channel *channel,
return 0;
}
-static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests)
+static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests,
+ unsigned flags)
{
int rc;
- if (!efx->phy_op->test)
+ if (!efx->phy_op->run_tests)
return 0;
+ EFX_BUG_ON_PARANOID(efx->phy_op->num_tests == 0 ||
+ efx->phy_op->num_tests > EFX_MAX_PHY_TESTS);
+
mutex_lock(&efx->mac_lock);
- rc = efx->phy_op->test(efx);
+ rc = efx->phy_op->run_tests(efx, tests->phy, flags);
mutex_unlock(&efx->mac_lock);
- tests->phy = rc ? -1 : 1;
return rc;
}
@@ -563,8 +567,7 @@ efx_test_loopback(struct efx_tx_queue *tx_queue,
return 0;
}
-static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd,
- struct efx_self_tests *tests,
+static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned int loopback_modes)
{
enum efx_loopback_mode mode;
@@ -593,12 +596,14 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd,
efx->loopback_mode = mode;
efx_reconfigure_port(efx);
- /* Wait for the PHY to signal the link is up */
+ /* Wait for the PHY to signal the link is up. Interrupts
+ * are enabled for PHY's using LASI, otherwise we poll()
+ * quickly */
count = 0;
do {
struct efx_channel *channel = &efx->channel[0];
- falcon_check_xmac(efx);
+ efx->phy_op->poll(efx);
schedule_timeout_uninterruptible(HZ / 10);
if (channel->work_pending)
efx_process_channel_now(channel);
@@ -606,13 +611,12 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd,
flush_workqueue(efx->workqueue);
rmb();
- /* efx->link_up can be 1 even if the XAUI link is down,
- * (bug5762). Usually, it's not worth bothering with the
- * difference, but for selftests, we need that extra
- * guarantee that the link is really, really, up.
- */
+ /* We need both the phy and xaui links to be ok.
+ * rather than relying on the falcon_xmac irq/poll
+ * regime, just poll xaui directly */
link_up = efx->link_up;
- if (!falcon_xaui_link_ok(efx))
+ if (link_up && EFX_IS10G(efx) &&
+ !falcon_xaui_link_ok(efx))
link_up = false;
} while ((++count < 20) && !link_up);
@@ -652,47 +656,48 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct ethtool_cmd ecmd,
/**************************************************************************
*
- * Entry points
+ * Entry point
*
*************************************************************************/
-/* Online (i.e. non-disruptive) testing
- * This checks interrupt generation, event delivery and PHY presence. */
-int efx_online_test(struct efx_nic *efx, struct efx_self_tests *tests)
+int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
+ unsigned flags)
{
+ enum efx_loopback_mode loopback_mode = efx->loopback_mode;
+ int phy_mode = efx->phy_mode;
+ struct ethtool_cmd ecmd;
struct efx_channel *channel;
- int rc, rc2 = 0;
+ int rc_test = 0, rc_reset = 0, rc;
+
+ /* Online (i.e. non-disruptive) testing
+ * This checks interrupt generation, event delivery and PHY presence. */
rc = efx_test_mii(efx, tests);
- if (rc && !rc2)
- rc2 = rc;
+ if (rc && !rc_test)
+ rc_test = rc;
rc = efx_test_nvram(efx, tests);
- if (rc && !rc2)
- rc2 = rc;
+ if (rc && !rc_test)
+ rc_test = rc;
rc = efx_test_interrupts(efx, tests);
- if (rc && !rc2)
- rc2 = rc;
+ if (rc && !rc_test)
+ rc_test = rc;
efx_for_each_channel(channel, efx) {
rc = efx_test_eventq_irq(channel, tests);
- if (rc && !rc2)
- rc2 = rc;
+ if (rc && !rc_test)
+ rc_test = rc;
}
- return rc2;
-}
+ if (rc_test)
+ return rc_test;
-/* Offline (i.e. disruptive) testing
- * This checks MAC and PHY loopback on the specified port. */
-int efx_offline_test(struct efx_nic *efx,
- struct efx_self_tests *tests, unsigned int loopback_modes)
-{
- enum efx_loopback_mode loopback_mode = efx->loopback_mode;
- int phy_mode = efx->phy_mode;
- struct ethtool_cmd ecmd, ecmd_test;
- int rc, rc2 = 0;
+ if (!(flags & ETH_TEST_FL_OFFLINE))
+ return efx_test_phy(efx, tests, flags);
+
+ /* Offline (i.e. disruptive) testing
+ * This checks MAC and PHY loopback on the specified port. */
/* force the carrier state off so the kernel doesn't transmit during
* the loopback test, and the watchdog timeout doesn't fire. Also put
@@ -700,8 +705,15 @@ int efx_offline_test(struct efx_nic *efx,
*/
mutex_lock(&efx->mac_lock);
efx->port_inhibited = true;
- if (efx->loopback_modes)
- efx->loopback_mode = __ffs(efx->loopback_modes);
+ if (efx->loopback_modes) {
+ /* We need the 312 clock from the PHY to test the XMAC
+ * registers, so move into XGMII loopback if available */
+ if (efx->loopback_modes & (1 << LOOPBACK_XGMII))
+ efx->loopback_mode = LOOPBACK_XGMII;
+ else
+ efx->loopback_mode = __ffs(efx->loopback_modes);
+ }
+
__efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
@@ -709,39 +721,34 @@ int efx_offline_test(struct efx_nic *efx,
efx_reset_down(efx, &ecmd);
rc = efx_test_chip(efx, tests);
- if (rc && !rc2)
- rc2 = rc;
+ if (rc && !rc_test)
+ rc_test = rc;
/* reset the chip to recover from the register test */
- rc = falcon_reset_hw(efx, RESET_TYPE_ALL);
-
- /* Modify the saved ecmd so that when efx_reset_up() restores the phy
- * state, AN is disabled, and the phy is powered, and out of loopback */
- memcpy(&ecmd_test, &ecmd, sizeof(ecmd_test));
- if (ecmd_test.autoneg == AUTONEG_ENABLE) {
- ecmd_test.autoneg = AUTONEG_DISABLE;
- ecmd_test.duplex = DUPLEX_FULL;
- ecmd_test.speed = SPEED_10000;
- }
+ rc_reset = falcon_reset_hw(efx, RESET_TYPE_ALL);
+
+ /* Ensure that the phy is powered and out of loopback
+ * for the bist and loopback tests */
+ efx->phy_mode &= ~PHY_MODE_LOW_POWER;
efx->loopback_mode = LOOPBACK_NONE;
- rc = efx_reset_up(efx, &ecmd_test, rc == 0);
- if (rc) {
+ rc = efx_reset_up(efx, &ecmd, rc_reset == 0);
+ if (rc && !rc_reset)
+ rc_reset = rc;
+
+ if (rc_reset) {
EFX_ERR(efx, "Unable to recover from chip test\n");
efx_schedule_reset(efx, RESET_TYPE_DISABLE);
- return rc;
+ return rc_reset;
}
- tests->loopback_speed = ecmd_test.speed;
- tests->loopback_full_duplex = ecmd_test.duplex;
-
- rc = efx_test_phy(efx, tests);
- if (rc && !rc2)
- rc2 = rc;
+ rc = efx_test_phy(efx, tests, flags);
+ if (rc && !rc_test)
+ rc_test = rc;
- rc = efx_test_loopbacks(efx, ecmd_test, tests, loopback_modes);
- if (rc && !rc2)
- rc2 = rc;
+ rc = efx_test_loopbacks(efx, tests, efx->loopback_modes);
+ if (rc && !rc_test)
+ rc_test = rc;
/* restore the PHY to the previous state */
efx->loopback_mode = loopback_mode;
@@ -749,6 +756,6 @@ int efx_offline_test(struct efx_nic *efx,
efx->port_inhibited = false;
efx_ethtool_set_settings(efx->net_dev, &ecmd);
- return rc2;
+ return rc_test;
}
diff --git a/drivers/net/sfc/selftest.h b/drivers/net/sfc/selftest.h
index fc15df15d766..39451cf938cf 100644
--- a/drivers/net/sfc/selftest.h
+++ b/drivers/net/sfc/selftest.h
@@ -24,6 +24,8 @@ struct efx_loopback_self_tests {
int rx_bad;
};
+#define EFX_MAX_PHY_TESTS 20
+
/* Efx self test results
* For fields which are not counters, 1 indicates success and -1
* indicates failure.
@@ -38,18 +40,14 @@ struct efx_self_tests {
int eventq_poll[EFX_MAX_CHANNELS];
/* offline tests */
int registers;
- int phy;
- int loopback_speed;
- int loopback_full_duplex;
+ int phy[EFX_MAX_PHY_TESTS];
struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1];
};
extern void efx_loopback_rx_packet(struct efx_nic *efx,
const char *buf_ptr, int pkt_len);
-extern int efx_online_test(struct efx_nic *efx,
- struct efx_self_tests *tests);
-extern int efx_offline_test(struct efx_nic *efx,
- struct efx_self_tests *tests,
- unsigned int loopback_modes);
+extern int efx_selftest(struct efx_nic *efx,
+ struct efx_self_tests *tests,
+ unsigned flags);
#endif /* EFX_SELFTEST_H */
diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c
index fe4e3fd22330..16b80acb9992 100644
--- a/drivers/net/sfc/sfe4001.c
+++ b/drivers/net/sfc/sfe4001.c
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007 Solarflare Communications Inc.
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -8,10 +8,21 @@
*/
/*****************************************************************************
- * Support for the SFE4001 NIC: driver code for the PCA9539 I/O expander that
- * controls the PHY power rails, and for the MAX6647 temp. sensor used to check
- * the PHY
+ * Support for the SFE4001 and SFN4111T NICs.
+ *
+ * The SFE4001 does not power-up fully at reset due to its high power
+ * consumption. We control its power via a PCA9539 I/O expander.
+ * Both boards have a MAX6647 temperature monitor which we expose to
+ * the lm90 driver.
+ *
+ * This also provides minimal support for reflashing the PHY, which is
+ * initiated by resetting it with the FLASH_CFG_1 pin pulled down.
+ * On SFE4001 rev A2 and later this is connected to the 3V3X output of
+ * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3.
+ * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually
+ * exclusive with the network device being open.
*/
+
#include <linux/delay.h>
#include "net_driver.h"
#include "efx.h"
@@ -21,6 +32,7 @@
#include "falcon_hwdefs.h"
#include "falcon_io.h"
#include "mac.h"
+#include "workarounds.h"
/**************************************************************************
*
@@ -65,48 +77,9 @@
#define P1_SPARE_LBN 4
#define P1_SPARE_WIDTH 4
-
-/**************************************************************************
- *
- * Temperature Sensor
- *
- **************************************************************************/
-#define MAX6647 0x4e
-
-#define RLTS 0x00
-#define RLTE 0x01
-#define RSL 0x02
-#define RCL 0x03
-#define RCRA 0x04
-#define RLHN 0x05
-#define RLLI 0x06
-#define RRHI 0x07
-#define RRLS 0x08
-#define WCRW 0x0a
-#define WLHO 0x0b
-#define WRHA 0x0c
-#define WRLN 0x0e
-#define OSHT 0x0f
-#define REET 0x10
-#define RIET 0x11
-#define RWOE 0x19
-#define RWOI 0x20
-#define HYS 0x21
-#define QUEUE 0x22
-#define MFID 0xfe
-#define REVID 0xff
-
-/* Status bits */
-#define MAX6647_BUSY (1 << 7) /* ADC is converting */
-#define MAX6647_LHIGH (1 << 6) /* Local high temp. alarm */
-#define MAX6647_LLOW (1 << 5) /* Local low temp. alarm */
-#define MAX6647_RHIGH (1 << 4) /* Remote high temp. alarm */
-#define MAX6647_RLOW (1 << 3) /* Remote low temp. alarm */
-#define MAX6647_FAULT (1 << 2) /* DXN/DXP short/open circuit */
-#define MAX6647_EOT (1 << 1) /* Remote junction overtemp. */
-#define MAX6647_IOT (1 << 0) /* Local junction overtemp. */
-
-static const u8 xgphy_max_temperature = 90;
+/* Temperature Sensor */
+#define MAX664X_REG_RSL 0x02
+#define MAX664X_REG_WLHO 0x0B
static void sfe4001_poweroff(struct efx_nic *efx)
{
@@ -119,7 +92,7 @@ static void sfe4001_poweroff(struct efx_nic *efx)
i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
/* Clear any over-temperature alert */
- i2c_smbus_read_byte_data(hwmon_client, RSL);
+ i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
}
static int sfe4001_poweron(struct efx_nic *efx)
@@ -131,7 +104,7 @@ static int sfe4001_poweron(struct efx_nic *efx)
u8 out;
/* Clear any previous over-temperature alert */
- rc = i2c_smbus_read_byte_data(hwmon_client, RSL);
+ rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
if (rc < 0)
return rc;
@@ -209,10 +182,29 @@ fail_on:
return rc;
}
-/* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin
- * using the 3V3X output of the IO-expander. Allow the user to set
- * this when the device is stopped, and keep it stopped then.
- */
+static int sfn4111t_reset(struct efx_nic *efx)
+{
+ efx_oword_t reg;
+
+ /* GPIO pins are also used for I2C, so block that temporarily */
+ mutex_lock(&efx->i2c_adap.bus_lock);
+
+ falcon_read(efx, &reg, GPIO_CTL_REG_KER);
+ EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true);
+ EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, false);
+ falcon_write(efx, &reg, GPIO_CTL_REG_KER);
+ msleep(1000);
+ EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, true);
+ EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, true);
+ EFX_SET_OWORD_FIELD(reg, GPIO3_OUT,
+ !(efx->phy_mode & PHY_MODE_SPECIAL));
+ falcon_write(efx, &reg, GPIO_CTL_REG_KER);
+
+ mutex_unlock(&efx->i2c_adap.bus_lock);
+
+ ssleep(1);
+ return 0;
+}
static ssize_t show_phy_flash_cfg(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -241,7 +233,10 @@ static ssize_t set_phy_flash_cfg(struct device *dev,
err = -EBUSY;
} else {
efx->phy_mode = new_mode;
- err = sfe4001_poweron(efx);
+ if (efx->board_info.type == EFX_BOARD_SFE4001)
+ err = sfe4001_poweron(efx);
+ else
+ err = sfn4111t_reset(efx);
efx_reconfigure_port(efx);
}
rtnl_unlock();
@@ -261,35 +256,62 @@ static void sfe4001_fini(struct efx_nic *efx)
i2c_unregister_device(efx->board_info.hwmon_client);
}
+static int sfe4001_check_hw(struct efx_nic *efx)
+{
+ s32 status;
+
+ /* If XAUI link is up then do not monitor */
+ if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
+ return 0;
+
+ /* Check the powered status of the PHY. Lack of power implies that
+ * the MAX6647 has shut down power to it, probably due to a temp.
+ * alarm. Reading the power status rather than the MAX6647 status
+ * directly because the later is read-to-clear and would thus
+ * start to power up the PHY again when polled, causing us to blip
+ * the power undesirably.
+ * We know we can read from the IO expander because we did
+ * it during power-on. Assume failure now is bad news. */
+ status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
+ if (status >= 0 &&
+ (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
+ return 0;
+
+ /* Use board power control, not PHY power control */
+ sfe4001_poweroff(efx);
+ efx->phy_mode = PHY_MODE_OFF;
+
+ return (status < 0) ? -EIO : -ERANGE;
+}
+
+static struct i2c_board_info sfe4001_hwmon_info = {
+ I2C_BOARD_INFO("max6647", 0x4e),
+ .irq = -1,
+};
+
/* This board uses an I2C expander to provider power to the PHY, which needs to
* be turned on before the PHY can be used.
* Context: Process context, rtnl lock held
*/
int sfe4001_init(struct efx_nic *efx)
{
- struct i2c_client *hwmon_client;
int rc;
- hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647);
- if (!hwmon_client)
+#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
+ efx->board_info.hwmon_client =
+ i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
+#else
+ efx->board_info.hwmon_client =
+ i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr);
+#endif
+ if (!efx->board_info.hwmon_client)
return -EIO;
- efx->board_info.hwmon_client = hwmon_client;
- /* Set DSP over-temperature alert threshold */
- EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
- rc = i2c_smbus_write_byte_data(hwmon_client, WLHO,
- xgphy_max_temperature);
+ /* Raise board/PHY high limit from 85 to 90 degrees Celsius */
+ rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client,
+ MAX664X_REG_WLHO, 90);
if (rc)
- goto fail_ioexp;
-
- /* Read it back and verify */
- rc = i2c_smbus_read_byte_data(hwmon_client, RLHN);
- if (rc < 0)
- goto fail_ioexp;
- if (rc != xgphy_max_temperature) {
- rc = -EFAULT;
- goto fail_ioexp;
- }
+ goto fail_hwmon;
efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
if (!efx->board_info.ioexp_client) {
@@ -301,6 +323,7 @@ int sfe4001_init(struct efx_nic *efx)
* blink code. */
efx->board_info.blink = tenxpress_phy_blink;
+ efx->board_info.monitor = sfe4001_check_hw;
efx->board_info.fini = sfe4001_fini;
rc = sfe4001_poweron(efx);
@@ -319,6 +342,64 @@ fail_on:
fail_ioexp:
i2c_unregister_device(efx->board_info.ioexp_client);
fail_hwmon:
- i2c_unregister_device(hwmon_client);
+ i2c_unregister_device(efx->board_info.hwmon_client);
+ return rc;
+}
+
+static int sfn4111t_check_hw(struct efx_nic *efx)
+{
+ s32 status;
+
+ /* If XAUI link is up then do not monitor */
+ if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
+ return 0;
+
+ /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */
+ status = i2c_smbus_read_byte_data(efx->board_info.hwmon_client,
+ MAX664X_REG_RSL);
+ if (status < 0)
+ return -EIO;
+ if (status & 0x57)
+ return -ERANGE;
+ return 0;
+}
+
+static void sfn4111t_fini(struct efx_nic *efx)
+{
+ EFX_INFO(efx, "%s\n", __func__);
+
+ device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ i2c_unregister_device(efx->board_info.hwmon_client);
+}
+
+static struct i2c_board_info sfn4111t_hwmon_info = {
+ I2C_BOARD_INFO("max6647", 0x4e),
+ .irq = -1,
+};
+
+int sfn4111t_init(struct efx_nic *efx)
+{
+ int rc;
+
+ efx->board_info.hwmon_client =
+ i2c_new_device(&efx->i2c_adap, &sfn4111t_hwmon_info);
+ if (!efx->board_info.hwmon_client)
+ return -EIO;
+
+ efx->board_info.blink = tenxpress_phy_blink;
+ efx->board_info.monitor = sfn4111t_check_hw;
+ efx->board_info.fini = sfn4111t_fini;
+
+ rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ if (rc)
+ goto fail_hwmon;
+
+ if (efx->phy_mode & PHY_MODE_SPECIAL)
+ sfn4111t_reset(efx);
+
+ return 0;
+
+fail_hwmon:
+ i2c_unregister_device(efx->board_info.hwmon_client);
return rc;
}
diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h
index feef61942377..1b1ceb411671 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/sfc/spi.h
@@ -25,6 +25,7 @@
#define SPI_WRDI 0x04 /* Reset write enable latch */
#define SPI_RDSR 0x05 /* Read status register */
#define SPI_WREN 0x06 /* Set write enable latch */
+#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */
#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */
#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */
@@ -36,6 +37,7 @@
/**
* struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device
* @efx: The Efx controller that owns this device
+ * @mtd: MTD state
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
@@ -44,23 +46,51 @@
* use bit 3 of the command byte as address bit A8, rather
* than having a two-byte address. If this flag is set, then
* commands should be munged in this way.
+ * @erase_command: Erase command (or 0 if sector erase not needed).
+ * @erase_size: Erase sector size (in bytes)
+ * Erase commands affect sectors with this size and alignment.
+ * This must be a power of two.
* @block_size: Write block size (in bytes).
* Write commands are limited to blocks with this size and alignment.
- * @read: Read function for the device
- * @write: Write function for the device
*/
struct efx_spi_device {
struct efx_nic *efx;
+#ifdef CONFIG_SFC_MTD
+ void *mtd;
+#endif
int device_id;
unsigned int size;
unsigned int addr_len;
unsigned int munge_address:1;
+ u8 erase_command;
+ unsigned int erase_size;
unsigned int block_size;
};
+int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
+ int address, const void* in, void *out, size_t len);
+int falcon_spi_wait_write(const struct efx_spi_device *spi);
int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
+/*
+ * SFC4000 flash is partitioned into:
+ * 0-0x400 chip and board config (see falcon_hwdefs.h)
+ * 0x400-0x8000 unused (or may contain VPD if EEPROM not present)
+ * 0x8000-end boot code (mapped to PCI expansion ROM)
+ * SFC4000 small EEPROM (size < 0x400) is used for VPD only.
+ * SFC4000 large EEPROM (size >= 0x400) is partitioned into:
+ * 0-0x400 chip and board config
+ * configurable VPD
+ * 0x800-0x1800 boot config
+ * Aside from the chip and board config, all of these are optional and may
+ * be absent or truncated depending on the devices used.
+ */
+#define FALCON_NVCONFIG_END 0x400U
+#define FALCON_FLASH_BOOTCODE_START 0x8000U
+#define EFX_EEPROM_BOOTCONFIG_START 0x800U
+#define EFX_EEPROM_BOOTCONFIG_END 0x1800U
+
#endif /* EFX_SPI_H */
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c
index d507c93d666e..b9768760fae7 100644
--- a/drivers/net/sfc/tenxpress.c
+++ b/drivers/net/sfc/tenxpress.c
@@ -1,6 +1,6 @@
/****************************************************************************
- * Driver for Solarflare 802.3an compliant PHY
- * Copyright 2007 Solarflare Communications Inc.
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2007-2008 Solarflare Communications 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
@@ -10,45 +10,76 @@
#include <linux/delay.h>
#include <linux/seq_file.h>
#include "efx.h"
-#include "gmii.h"
#include "mdio_10g.h"
#include "falcon.h"
#include "phy.h"
#include "falcon_hwdefs.h"
#include "boards.h"
-#include "mac.h"
+#include "workarounds.h"
+#include "selftest.h"
-/* We expect these MMDs to be in the package */
-/* AN not here as mdio_check_mmds() requires STAT2 support */
-#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PMAPMD | \
- MDIO_MMDREG_DEVS0_PCS | \
- MDIO_MMDREG_DEVS0_PHYXS)
-
-#define TENXPRESS_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
- (1 << LOOPBACK_PCS) | \
- (1 << LOOPBACK_PMAPMD) | \
- (1 << LOOPBACK_NETWORK))
+/* We expect these MMDs to be in the package. SFT9001 also has a
+ * clause 22 extension MMD, but since it doesn't have all the generic
+ * MMD registers it is pointless to include it here.
+ */
+#define TENXPRESS_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PMAPMD | \
+ MDIO_MMDREG_DEVS_PCS | \
+ MDIO_MMDREG_DEVS_PHYXS | \
+ MDIO_MMDREG_DEVS_AN)
+
+#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
+ (1 << LOOPBACK_PCS) | \
+ (1 << LOOPBACK_PMAPMD) | \
+ (1 << LOOPBACK_NETWORK))
+
+#define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \
+ (1 << LOOPBACK_PHYXS) | \
+ (1 << LOOPBACK_PCS) | \
+ (1 << LOOPBACK_PMAPMD) | \
+ (1 << LOOPBACK_NETWORK))
/* We complain if we fail to see the link partner as 10G capable this many
* times in a row (must be > 1 as sampling the autoneg. registers is racy)
*/
#define MAX_BAD_LP_TRIES (5)
+/* LASI Control */
+#define PMA_PMD_LASI_CTRL 36866
+#define PMA_PMD_LASI_STATUS 36869
+#define PMA_PMD_LS_ALARM_LBN 0
+#define PMA_PMD_LS_ALARM_WIDTH 1
+#define PMA_PMD_TX_ALARM_LBN 1
+#define PMA_PMD_TX_ALARM_WIDTH 1
+#define PMA_PMD_RX_ALARM_LBN 2
+#define PMA_PMD_RX_ALARM_WIDTH 1
+#define PMA_PMD_AN_ALARM_LBN 3
+#define PMA_PMD_AN_ALARM_WIDTH 1
+
/* Extended control register */
-#define PMA_PMD_XCONTROL_REG 0xc000
-#define PMA_PMD_LNPGA_POWERDOWN_LBN 8
-#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
+#define PMA_PMD_XCONTROL_REG 49152
+#define PMA_PMD_EXT_GMII_EN_LBN 1
+#define PMA_PMD_EXT_GMII_EN_WIDTH 1
+#define PMA_PMD_EXT_CLK_OUT_LBN 2
+#define PMA_PMD_EXT_CLK_OUT_WIDTH 1
+#define PMA_PMD_LNPGA_POWERDOWN_LBN 8 /* SFX7101 only */
+#define PMA_PMD_LNPGA_POWERDOWN_WIDTH 1
+#define PMA_PMD_EXT_CLK312_LBN 8 /* SFT9001 only */
+#define PMA_PMD_EXT_CLK312_WIDTH 1
+#define PMA_PMD_EXT_LPOWER_LBN 12
+#define PMA_PMD_EXT_LPOWER_WIDTH 1
+#define PMA_PMD_EXT_SSR_LBN 15
+#define PMA_PMD_EXT_SSR_WIDTH 1
/* extended status register */
-#define PMA_PMD_XSTATUS_REG 0xc001
+#define PMA_PMD_XSTATUS_REG 49153
#define PMA_PMD_XSTAT_FLP_LBN (12)
/* LED control register */
-#define PMA_PMD_LED_CTRL_REG (0xc007)
+#define PMA_PMD_LED_CTRL_REG 49159
#define PMA_PMA_LED_ACTIVITY_LBN (3)
/* LED function override register */
-#define PMA_PMD_LED_OVERR_REG (0xc009)
+#define PMA_PMD_LED_OVERR_REG 49161
/* Bit positions for different LEDs (there are more but not wired on SFE4001)*/
#define PMA_PMD_LED_LINK_LBN (0)
#define PMA_PMD_LED_SPEED_LBN (2)
@@ -59,41 +90,99 @@
#define PMA_PMD_LED_ON (1)
#define PMA_PMD_LED_OFF (2)
#define PMA_PMD_LED_FLASH (3)
+#define PMA_PMD_LED_MASK 3
/* All LEDs under hardware control */
#define PMA_PMD_LED_FULL_AUTO (0)
/* Green and Amber under hardware control, Red off */
#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
-
-/* Special Software reset register */
-#define PMA_PMD_EXT_CTRL_REG 49152
-#define PMA_PMD_EXT_SSR_LBN 15
-
-/* Misc register defines */
-#define PCS_CLOCK_CTRL_REG 0xd801
+#define PMA_PMD_SPEED_ENABLE_REG 49192
+#define PMA_PMD_100TX_ADV_LBN 1
+#define PMA_PMD_100TX_ADV_WIDTH 1
+#define PMA_PMD_1000T_ADV_LBN 2
+#define PMA_PMD_1000T_ADV_WIDTH 1
+#define PMA_PMD_10000T_ADV_LBN 3
+#define PMA_PMD_10000T_ADV_WIDTH 1
+#define PMA_PMD_SPEED_LBN 4
+#define PMA_PMD_SPEED_WIDTH 4
+
+/* Cable diagnostics - SFT9001 only */
+#define PMA_PMD_CDIAG_CTRL_REG 49213
+#define CDIAG_CTRL_IMMED_LBN 15
+#define CDIAG_CTRL_BRK_LINK_LBN 12
+#define CDIAG_CTRL_IN_PROG_LBN 11
+#define CDIAG_CTRL_LEN_UNIT_LBN 10
+#define CDIAG_CTRL_LEN_METRES 1
+#define PMA_PMD_CDIAG_RES_REG 49174
+#define CDIAG_RES_A_LBN 12
+#define CDIAG_RES_B_LBN 8
+#define CDIAG_RES_C_LBN 4
+#define CDIAG_RES_D_LBN 0
+#define CDIAG_RES_WIDTH 4
+#define CDIAG_RES_OPEN 2
+#define CDIAG_RES_OK 1
+#define CDIAG_RES_INVALID 0
+/* Set of 4 registers for pairs A-D */
+#define PMA_PMD_CDIAG_LEN_REG 49175
+
+/* Serdes control registers - SFT9001 only */
+#define PMA_PMD_CSERDES_CTRL_REG 64258
+/* Set the 156.25 MHz output to 312.5 MHz to drive Falcon's XMAC */
+#define PMA_PMD_CSERDES_DEFAULT 0x000f
+
+/* Misc register defines - SFX7101 only */
+#define PCS_CLOCK_CTRL_REG 55297
#define PLL312_RST_N_LBN 2
-#define PCS_SOFT_RST2_REG 0xd806
+#define PCS_SOFT_RST2_REG 55302
#define SERDES_RST_N_LBN 13
#define XGXS_RST_N_LBN 12
-#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */
+#define PCS_TEST_SELECT_REG 55303 /* PRM 10.5.8 */
#define CLK312_EN_LBN 3
/* PHYXS registers */
+#define PHYXS_XCONTROL_REG 49152
+#define PHYXS_RESET_LBN 15
+#define PHYXS_RESET_WIDTH 1
+
#define PHYXS_TEST1 (49162)
#define LOOPBACK_NEAR_LBN (8)
#define LOOPBACK_NEAR_WIDTH (1)
+#define PCS_10GBASET_STAT1 32
+#define PCS_10GBASET_BLKLK_LBN 0
+#define PCS_10GBASET_BLKLK_WIDTH 1
+
/* Boot status register */
-#define PCS_BOOT_STATUS_REG (0xd000)
+#define PCS_BOOT_STATUS_REG 53248
#define PCS_BOOT_FATAL_ERR_LBN (0)
#define PCS_BOOT_PROGRESS_LBN (1)
#define PCS_BOOT_PROGRESS_WIDTH (2)
#define PCS_BOOT_COMPLETE_LBN (3)
+
#define PCS_BOOT_MAX_DELAY (100)
#define PCS_BOOT_POLL_DELAY (10)
+/* 100M/1G PHY registers */
+#define GPHY_XCONTROL_REG 49152
+#define GPHY_ISOLATE_LBN 10
+#define GPHY_ISOLATE_WIDTH 1
+#define GPHY_DUPLEX_LBN 8
+#define GPHY_DUPLEX_WIDTH 1
+#define GPHY_LOOPBACK_NEAR_LBN 14
+#define GPHY_LOOPBACK_NEAR_WIDTH 1
+
+#define C22EXT_STATUS_REG 49153
+#define C22EXT_STATUS_LINK_LBN 2
+#define C22EXT_STATUS_LINK_WIDTH 1
+
+#define C22EXT_MSTSLV_REG 49162
+#define C22EXT_MSTSLV_1000_HD_LBN 10
+#define C22EXT_MSTSLV_1000_HD_WIDTH 1
+#define C22EXT_MSTSLV_1000_FD_LBN 11
+#define C22EXT_MSTSLV_1000_FD_WIDTH 1
+
/* Time to wait between powering down the LNPGA and turning off the power
* rails */
#define LNPGA_PDOWN_WAIT (HZ / 5)
@@ -117,6 +206,38 @@ void tenxpress_crc_err(struct efx_nic *efx)
atomic_inc(&phy_data->bad_crc_count);
}
+static ssize_t show_phy_short_reach(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+ int reg;
+
+ reg = mdio_clause45_read(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+ MDIO_PMAPMD_10GBT_TXPWR);
+ return sprintf(buf, "%d\n",
+ !!(reg & (1 << MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN)));
+}
+
+static ssize_t set_phy_short_reach(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+ rtnl_lock();
+ mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+ MDIO_PMAPMD_10GBT_TXPWR,
+ MDIO_PMAPMD_10GBT_TXPWR_SHORT_LBN,
+ count != 0 && *buf != '0');
+ efx_reconfigure_port(efx);
+ rtnl_unlock();
+
+ return count;
+}
+
+static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach,
+ set_phy_short_reach);
+
/* Check that the C166 has booted successfully */
static int tenxpress_phy_check(struct efx_nic *efx)
{
@@ -148,27 +269,42 @@ static int tenxpress_phy_check(struct efx_nic *efx)
static int tenxpress_init(struct efx_nic *efx)
{
- int rc, reg;
+ int phy_id = efx->mii.phy_id;
+ int reg;
+ int rc;
- /* Turn on the clock */
- reg = (1 << CLK312_EN_LBN);
- mdio_clause45_write(efx, efx->mii.phy_id,
- MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg);
+ if (efx->phy_type == PHY_TYPE_SFX7101) {
+ /* Enable 312.5 MHz clock */
+ mdio_clause45_write(efx, phy_id,
+ MDIO_MMD_PCS, PCS_TEST_SELECT_REG,
+ 1 << CLK312_EN_LBN);
+ } else {
+ /* Enable 312.5 MHz clock and GMII */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG);
+ reg |= ((1 << PMA_PMD_EXT_GMII_EN_LBN) |
+ (1 << PMA_PMD_EXT_CLK_OUT_LBN) |
+ (1 << PMA_PMD_EXT_CLK312_LBN));
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG, reg);
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+ GPHY_XCONTROL_REG, GPHY_ISOLATE_LBN,
+ false);
+ }
rc = tenxpress_phy_check(efx);
if (rc < 0)
return rc;
/* Set the LEDs up as: Green = Link, Amber = Link/Act, Red = Off */
- reg = mdio_clause45_read(efx, efx->mii.phy_id,
- MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG);
- reg |= (1 << PMA_PMA_LED_ACTIVITY_LBN);
- mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
- PMA_PMD_LED_CTRL_REG, reg);
-
- reg = PMA_PMD_LED_DEFAULT;
- mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
- PMA_PMD_LED_OVERR_REG, reg);
+ if (efx->phy_type == PHY_TYPE_SFX7101) {
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_LED_CTRL_REG,
+ PMA_PMA_LED_ACTIVITY_LBN,
+ true);
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_LED_OVERR_REG, PMA_PMD_LED_DEFAULT);
+ }
return rc;
}
@@ -184,22 +320,43 @@ static int tenxpress_phy_init(struct efx_nic *efx)
efx->phy_data = phy_data;
phy_data->phy_mode = efx->phy_mode;
- rc = mdio_clause45_wait_reset_mmds(efx,
- TENXPRESS_REQUIRED_DEVS);
- if (rc < 0)
- goto fail;
+ if (!(efx->phy_mode & PHY_MODE_SPECIAL)) {
+ if (efx->phy_type == PHY_TYPE_SFT9001A) {
+ int reg;
+ reg = mdio_clause45_read(efx, efx->mii.phy_id,
+ MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG);
+ reg |= (1 << PMA_PMD_EXT_SSR_LBN);
+ mdio_clause45_write(efx, efx->mii.phy_id,
+ MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG, reg);
+ mdelay(200);
+ }
- rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
- if (rc < 0)
- goto fail;
+ rc = mdio_clause45_wait_reset_mmds(efx,
+ TENXPRESS_REQUIRED_DEVS);
+ if (rc < 0)
+ goto fail;
+
+ rc = mdio_clause45_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0);
+ if (rc < 0)
+ goto fail;
+ }
rc = tenxpress_init(efx);
if (rc < 0)
goto fail;
+ if (efx->phy_type == PHY_TYPE_SFT9001B) {
+ rc = device_create_file(&efx->pci_dev->dev,
+ &dev_attr_phy_short_reach);
+ if (rc)
+ goto fail;
+ }
+
schedule_timeout_uninterruptible(HZ / 5); /* 200ms */
- /* Let XGXS and SerDes out of reset and resets 10XPress */
+ /* Let XGXS and SerDes out of reset */
falcon_reset_xaui(efx);
return 0;
@@ -210,21 +367,24 @@ static int tenxpress_phy_init(struct efx_nic *efx)
return rc;
}
+/* Perform a "special software reset" on the PHY. The caller is
+ * responsible for saving and restoring the PHY hardware registers
+ * properly, and masking/unmasking LASI */
static int tenxpress_special_reset(struct efx_nic *efx)
{
int rc, reg;
/* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so
* a special software reset can glitch the XGMAC sufficiently for stats
- * requests to fail. Since we don't ofen special_reset, just lock. */
+ * requests to fail. Since we don't often special_reset, just lock. */
spin_lock(&efx->stats_lock);
/* Initiate reset */
reg = mdio_clause45_read(efx, efx->mii.phy_id,
- MDIO_MMD_PMAPMD, PMA_PMD_EXT_CTRL_REG);
+ MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG);
reg |= (1 << PMA_PMD_EXT_SSR_LBN);
mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
- PMA_PMD_EXT_CTRL_REG, reg);
+ PMA_PMD_XCONTROL_REG, reg);
mdelay(200);
@@ -239,174 +399,257 @@ static int tenxpress_special_reset(struct efx_nic *efx)
if (rc < 0)
goto unlock;
+ /* Wait for the XGXS state machine to churn */
+ mdelay(10);
unlock:
spin_unlock(&efx->stats_lock);
return rc;
}
-static void tenxpress_set_bad_lp(struct efx_nic *efx, bool bad_lp)
+static void sfx7101_check_bad_lp(struct efx_nic *efx, bool link_ok)
{
struct tenxpress_phy_data *pd = efx->phy_data;
+ int phy_id = efx->mii.phy_id;
+ bool bad_lp;
int reg;
+ if (link_ok) {
+ bad_lp = false;
+ } else {
+ /* Check that AN has started but not completed. */
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
+ MDIO_AN_STATUS);
+ if (!(reg & (1 << MDIO_AN_STATUS_LP_AN_CAP_LBN)))
+ return; /* LP status is unknown */
+ bad_lp = !(reg & (1 << MDIO_AN_STATUS_AN_DONE_LBN));
+ if (bad_lp)
+ pd->bad_lp_tries++;
+ }
+
/* Nothing to do if all is well and was previously so. */
- if (!(bad_lp || pd->bad_lp_tries))
+ if (!pd->bad_lp_tries)
return;
- reg = mdio_clause45_read(efx, efx->mii.phy_id,
- MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG);
+ /* Use the RX (red) LED as an error indicator once we've seen AN
+ * failure several times in a row, and also log a message. */
+ if (!bad_lp || pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_LED_OVERR_REG);
+ reg &= ~(PMA_PMD_LED_MASK << PMA_PMD_LED_RX_LBN);
+ if (!bad_lp) {
+ reg |= PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN;
+ } else {
+ reg |= PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN;
+ EFX_ERR(efx, "appears to be plugged into a port"
+ " that is not 10GBASE-T capable. The PHY"
+ " supports 10GBASE-T ONLY, so no link can"
+ " be established\n");
+ }
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_LED_OVERR_REG, reg);
+ pd->bad_lp_tries = bad_lp;
+ }
+}
- if (bad_lp)
- pd->bad_lp_tries++;
- else
- pd->bad_lp_tries = 0;
-
- if (pd->bad_lp_tries == MAX_BAD_LP_TRIES) {
- pd->bad_lp_tries = 0; /* Restart count */
- reg &= ~(PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
- reg |= (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN);
- EFX_ERR(efx, "This NIC appears to be plugged into"
- " a port that is not 10GBASE-T capable.\n"
- " This PHY is 10GBASE-T ONLY, so no link can"
- " be established.\n");
+static bool sfx7101_link_ok(struct efx_nic *efx)
+{
+ return mdio_clause45_links_ok(efx,
+ MDIO_MMDREG_DEVS_PMAPMD |
+ MDIO_MMDREG_DEVS_PCS |
+ MDIO_MMDREG_DEVS_PHYXS);
+}
+
+static bool sft9001_link_ok(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ int phy_id = efx->mii.phy_id;
+ u32 reg;
+
+ if (efx_phy_mode_disabled(efx->phy_mode))
+ return false;
+ else if (efx->loopback_mode == LOOPBACK_GPHY)
+ return true;
+ else if (efx->loopback_mode)
+ return mdio_clause45_links_ok(efx,
+ MDIO_MMDREG_DEVS_PMAPMD |
+ MDIO_MMDREG_DEVS_PHYXS);
+
+ /* We must use the same definition of link state as LASI,
+ * otherwise we can miss a link state transition
+ */
+ if (ecmd->speed == 10000) {
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PCS,
+ PCS_10GBASET_STAT1);
+ return reg & (1 << PCS_10GBASET_BLKLK_LBN);
} else {
- reg |= (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN);
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT,
+ C22EXT_STATUS_REG);
+ return reg & (1 << C22EXT_STATUS_LINK_LBN);
}
- mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
- PMA_PMD_LED_OVERR_REG, reg);
}
-/* Check link status and return a boolean OK value. If the link is NOT
- * OK we have a quick rummage round to see if we appear to be plugged
- * into a non-10GBT port and if so warn the user that they won't get
- * link any time soon as we are 10GBT only, unless caller specified
- * not to do this check (it isn't useful in loopback) */
-static bool tenxpress_link_ok(struct efx_nic *efx, bool check_lp)
+static void tenxpress_ext_loopback(struct efx_nic *efx)
{
- bool ok = mdio_clause45_links_ok(efx, TENXPRESS_REQUIRED_DEVS);
-
- if (ok) {
- tenxpress_set_bad_lp(efx, false);
- } else if (check_lp) {
- /* Are we plugged into the wrong sort of link? */
- bool bad_lp = false;
- int phy_id = efx->mii.phy_id;
- int an_stat = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN,
- MDIO_AN_STATUS);
- int xphy_stat = mdio_clause45_read(efx, phy_id,
- MDIO_MMD_PMAPMD,
- PMA_PMD_XSTATUS_REG);
- /* Are we plugged into anything that sends FLPs? If
- * not we can't distinguish between not being plugged
- * in and being plugged into a non-AN antique. The FLP
- * bit has the advantage of not clearing when autoneg
- * restarts. */
- if (!(xphy_stat & (1 << PMA_PMD_XSTAT_FLP_LBN))) {
- tenxpress_set_bad_lp(efx, false);
- return ok;
- }
+ int phy_id = efx->mii.phy_id;
- /* If it can do 10GBT it must be XNP capable */
- bad_lp = !(an_stat & (1 << MDIO_AN_STATUS_XNP_LBN));
- if (!bad_lp && (an_stat & (1 << MDIO_AN_STATUS_PAGE_LBN))) {
- bad_lp = !(mdio_clause45_read(efx, phy_id,
- MDIO_MMD_AN, MDIO_AN_10GBT_STATUS) &
- (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN));
- }
- tenxpress_set_bad_lp(efx, bad_lp);
- }
- return ok;
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PHYXS,
+ PHYXS_TEST1, LOOPBACK_NEAR_LBN,
+ efx->loopback_mode == LOOPBACK_PHYXS);
+ if (efx->phy_type != PHY_TYPE_SFX7101)
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+ GPHY_XCONTROL_REG,
+ GPHY_LOOPBACK_NEAR_LBN,
+ efx->loopback_mode == LOOPBACK_GPHY);
}
-static void tenxpress_phyxs_loopback(struct efx_nic *efx)
+static void tenxpress_low_power(struct efx_nic *efx)
{
int phy_id = efx->mii.phy_id;
- int ctrl1, ctrl2;
- ctrl1 = ctrl2 = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS,
- PHYXS_TEST1);
- if (efx->loopback_mode == LOOPBACK_PHYXS)
- ctrl2 |= (1 << LOOPBACK_NEAR_LBN);
+ if (efx->phy_type == PHY_TYPE_SFX7101)
+ mdio_clause45_set_mmds_lpower(
+ efx, !!(efx->phy_mode & PHY_MODE_LOW_POWER),
+ TENXPRESS_REQUIRED_DEVS);
else
- ctrl2 &= ~(1 << LOOPBACK_NEAR_LBN);
- if (ctrl1 != ctrl2)
- mdio_clause45_write(efx, phy_id, MDIO_MMD_PHYXS,
- PHYXS_TEST1, ctrl2);
+ mdio_clause45_set_flag(
+ efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG, PMA_PMD_EXT_LPOWER_LBN,
+ !!(efx->phy_mode & PHY_MODE_LOW_POWER));
}
static void tenxpress_phy_reconfigure(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
- bool loop_change = LOOPBACK_OUT_OF(phy_data, efx,
- TENXPRESS_LOOPBACKS);
+ struct ethtool_cmd ecmd;
+ bool phy_mode_change, loop_reset, loop_toggle, loopback;
- if (efx->phy_mode & PHY_MODE_SPECIAL) {
+ if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) {
phy_data->phy_mode = efx->phy_mode;
return;
}
- /* When coming out of transmit disable, coming out of low power
- * mode, or moving out of any PHY internal loopback mode,
- * perform a special software reset */
- if ((efx->phy_mode == PHY_MODE_NORMAL &&
- phy_data->phy_mode != PHY_MODE_NORMAL) ||
- loop_change) {
- tenxpress_special_reset(efx);
- falcon_reset_xaui(efx);
+ tenxpress_low_power(efx);
+
+ phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL &&
+ phy_data->phy_mode != PHY_MODE_NORMAL);
+ loopback = LOOPBACK_MASK(efx) & efx->phy_op->loopbacks;
+ loop_toggle = LOOPBACK_CHANGED(phy_data, efx, efx->phy_op->loopbacks);
+ loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) ||
+ LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY));
+
+ if (loop_reset || loop_toggle || loopback || phy_mode_change) {
+ int rc;
+
+ efx->phy_op->get_settings(efx, &ecmd);
+
+ if (loop_reset || phy_mode_change) {
+ tenxpress_special_reset(efx);
+
+ /* Reset XAUI if we were in 10G, and are staying
+ * in 10G. If we're moving into and out of 10G
+ * then xaui will be reset anyway */
+ if (EFX_IS10G(efx))
+ falcon_reset_xaui(efx);
+ }
+
+ if (efx->phy_type != PHY_TYPE_SFX7101) {
+ /* Only change autoneg once, on coming out or
+ * going into loopback */
+ if (loop_toggle)
+ ecmd.autoneg = !loopback;
+ if (loopback) {
+ ecmd.duplex = DUPLEX_FULL;
+ if (efx->loopback_mode == LOOPBACK_GPHY)
+ ecmd.speed = SPEED_1000;
+ else
+ ecmd.speed = SPEED_10000;
+ }
+ }
+
+ rc = efx->phy_op->set_settings(efx, &ecmd);
+ WARN_ON(rc);
}
mdio_clause45_transmit_disable(efx);
mdio_clause45_phy_reconfigure(efx);
- tenxpress_phyxs_loopback(efx);
+ tenxpress_ext_loopback(efx);
phy_data->loopback_mode = efx->loopback_mode;
phy_data->phy_mode = efx->phy_mode;
- efx->link_up = tenxpress_link_ok(efx, false);
- efx->link_options = GM_LPA_10000FULL;
-}
-static void tenxpress_phy_clear_interrupt(struct efx_nic *efx)
-{
- /* Nothing done here - LASI interrupts aren't reliable so poll */
+ if (efx->phy_type == PHY_TYPE_SFX7101) {
+ efx->link_speed = 10000;
+ efx->link_fd = true;
+ efx->link_up = sfx7101_link_ok(efx);
+ } else {
+ efx->phy_op->get_settings(efx, &ecmd);
+ efx->link_speed = ecmd.speed;
+ efx->link_fd = ecmd.duplex == DUPLEX_FULL;
+ efx->link_up = sft9001_link_ok(efx, &ecmd);
+ }
+ efx->link_fc = mdio_clause45_get_pause(efx);
}
-
/* Poll PHY for interrupt */
-static int tenxpress_phy_check_hw(struct efx_nic *efx)
+static void tenxpress_phy_poll(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
- bool link_ok;
-
- link_ok = tenxpress_link_ok(efx, true);
+ bool change = false, link_ok;
+ unsigned link_fc;
+
+ if (efx->phy_type == PHY_TYPE_SFX7101) {
+ link_ok = sfx7101_link_ok(efx);
+ if (link_ok != efx->link_up) {
+ change = true;
+ } else {
+ link_fc = mdio_clause45_get_pause(efx);
+ if (link_fc != efx->link_fc)
+ change = true;
+ }
+ sfx7101_check_bad_lp(efx, link_ok);
+ } else if (efx->loopback_mode) {
+ bool link_ok = sft9001_link_ok(efx, NULL);
+ if (link_ok != efx->link_up)
+ change = true;
+ } else {
+ u32 status = mdio_clause45_read(efx, efx->mii.phy_id,
+ MDIO_MMD_PMAPMD,
+ PMA_PMD_LASI_STATUS);
+ if (status & (1 << PMA_PMD_LS_ALARM_LBN))
+ change = true;
+ }
- if (link_ok != efx->link_up)
- falcon_xmac_sim_phy_event(efx);
+ if (change)
+ falcon_sim_phy_event(efx);
if (phy_data->phy_mode != PHY_MODE_NORMAL)
- return 0;
+ return;
- if (atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
+ if (EFX_WORKAROUND_10750(efx) &&
+ atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) {
EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n");
falcon_reset_xaui(efx);
atomic_set(&phy_data->bad_crc_count, 0);
}
-
- return 0;
}
static void tenxpress_phy_fini(struct efx_nic *efx)
{
int reg;
- /* Power down the LNPGA */
- reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
- mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
- PMA_PMD_XCONTROL_REG, reg);
-
- /* Waiting here ensures that the board fini, which can turn off the
- * power to the PHY, won't get run until the LNPGA powerdown has been
- * given long enough to complete. */
- schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
+ if (efx->phy_type == PHY_TYPE_SFT9001B) {
+ device_remove_file(&efx->pci_dev->dev,
+ &dev_attr_phy_short_reach);
+ } else {
+ /* Power down the LNPGA */
+ reg = (1 << PMA_PMD_LNPGA_POWERDOWN_LBN);
+ mdio_clause45_write(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_XCONTROL_REG, reg);
+
+ /* Waiting here ensures that the board fini, which can turn
+ * off the power to the PHY, won't get run until the LNPGA
+ * powerdown has been given long enough to complete. */
+ schedule_timeout_uninterruptible(LNPGA_PDOWN_WAIT); /* 200 ms */
+ }
kfree(efx->phy_data);
efx->phy_data = NULL;
@@ -430,19 +673,236 @@ void tenxpress_phy_blink(struct efx_nic *efx, bool blink)
PMA_PMD_LED_OVERR_REG, reg);
}
-static int tenxpress_phy_test(struct efx_nic *efx)
+static const char *const sfx7101_test_names[] = {
+ "bist"
+};
+
+static int
+sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
+ int rc;
+
+ if (!(flags & ETH_TEST_FL_OFFLINE))
+ return 0;
+
/* BIST is automatically run after a special software reset */
- return tenxpress_special_reset(efx);
+ rc = tenxpress_special_reset(efx);
+ results[0] = rc ? -1 : 1;
+ return rc;
}
-struct efx_phy_operations falcon_tenxpress_phy_ops = {
+static const char *const sft9001_test_names[] = {
+ "bist",
+ "cable.pairA.status",
+ "cable.pairB.status",
+ "cable.pairC.status",
+ "cable.pairD.status",
+ "cable.pairA.length",
+ "cable.pairB.length",
+ "cable.pairC.length",
+ "cable.pairD.length",
+};
+
+static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags)
+{
+ struct ethtool_cmd ecmd;
+ int phy_id = efx->mii.phy_id;
+ int rc = 0, rc2, i, res_reg;
+
+ if (!(flags & ETH_TEST_FL_OFFLINE))
+ return 0;
+
+ efx->phy_op->get_settings(efx, &ecmd);
+
+ /* Initialise cable diagnostic results to unknown failure */
+ for (i = 1; i < 9; ++i)
+ results[i] = -1;
+
+ /* Run cable diagnostics; wait up to 5 seconds for them to complete.
+ * A cable fault is not a self-test failure, but a timeout is. */
+ mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_CDIAG_CTRL_REG,
+ (1 << CDIAG_CTRL_IMMED_LBN) |
+ (1 << CDIAG_CTRL_BRK_LINK_LBN) |
+ (CDIAG_CTRL_LEN_METRES << CDIAG_CTRL_LEN_UNIT_LBN));
+ i = 0;
+ while (mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_CDIAG_CTRL_REG) &
+ (1 << CDIAG_CTRL_IN_PROG_LBN)) {
+ if (++i == 50) {
+ rc = -ETIMEDOUT;
+ goto reset;
+ }
+ msleep(100);
+ }
+ res_reg = mdio_clause45_read(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_CDIAG_RES_REG);
+ for (i = 0; i < 4; i++) {
+ int pair_res =
+ (res_reg >> (CDIAG_RES_A_LBN - i * CDIAG_RES_WIDTH))
+ & ((1 << CDIAG_RES_WIDTH) - 1);
+ int len_reg = mdio_clause45_read(efx, efx->mii.phy_id,
+ MDIO_MMD_PMAPMD,
+ PMA_PMD_CDIAG_LEN_REG + i);
+ if (pair_res == CDIAG_RES_OK)
+ results[1 + i] = 1;
+ else if (pair_res == CDIAG_RES_INVALID)
+ results[1 + i] = -1;
+ else
+ results[1 + i] = -pair_res;
+ if (pair_res != CDIAG_RES_INVALID &&
+ pair_res != CDIAG_RES_OPEN &&
+ len_reg != 0xffff)
+ results[5 + i] = len_reg;
+ }
+
+ /* We must reset to exit cable diagnostic mode. The BIST will
+ * also run when we do this. */
+reset:
+ rc2 = tenxpress_special_reset(efx);
+ results[0] = rc2 ? -1 : 1;
+ if (!rc)
+ rc = rc2;
+
+ rc2 = efx->phy_op->set_settings(efx, &ecmd);
+ if (!rc)
+ rc = rc2;
+
+ return rc;
+}
+
+static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx)
+{
+ int phy = efx->mii.phy_id;
+ u32 lpa = 0;
+ int reg;
+
+ if (efx->phy_type != PHY_TYPE_SFX7101) {
+ reg = mdio_clause45_read(efx, phy, MDIO_MMD_C22EXT,
+ C22EXT_MSTSLV_REG);
+ if (reg & (1 << C22EXT_MSTSLV_1000_HD_LBN))
+ lpa |= ADVERTISED_1000baseT_Half;
+ if (reg & (1 << C22EXT_MSTSLV_1000_FD_LBN))
+ lpa |= ADVERTISED_1000baseT_Full;
+ }
+ reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS);
+ if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN))
+ lpa |= ADVERTISED_10000baseT_Full;
+ return lpa;
+}
+
+static void sfx7101_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full,
+ tenxpress_get_xnp_lpa(efx));
+ ecmd->supported |= SUPPORTED_10000baseT_Full;
+ ecmd->advertising |= ADVERTISED_10000baseT_Full;
+}
+
+static void sft9001_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ int phy_id = efx->mii.phy_id;
+ u32 xnp_adv = 0;
+ int reg;
+
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD,
+ PMA_PMD_SPEED_ENABLE_REG);
+ if (EFX_WORKAROUND_13204(efx) && (reg & (1 << PMA_PMD_100TX_ADV_LBN)))
+ xnp_adv |= ADVERTISED_100baseT_Full;
+ if (reg & (1 << PMA_PMD_1000T_ADV_LBN))
+ xnp_adv |= ADVERTISED_1000baseT_Full;
+ if (reg & (1 << PMA_PMD_10000T_ADV_LBN))
+ xnp_adv |= ADVERTISED_10000baseT_Full;
+
+ mdio_clause45_get_settings_ext(efx, ecmd, xnp_adv,
+ tenxpress_get_xnp_lpa(efx));
+
+ ecmd->supported |= (SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full);
+
+ /* Use the vendor defined C22ext register for duplex settings */
+ if (ecmd->speed != SPEED_10000 && !ecmd->autoneg) {
+ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT,
+ GPHY_XCONTROL_REG);
+ ecmd->duplex = (reg & (1 << GPHY_DUPLEX_LBN) ?
+ DUPLEX_FULL : DUPLEX_HALF);
+ }
+}
+
+static int sft9001_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ int phy_id = efx->mii.phy_id;
+ int rc;
+
+ rc = mdio_clause45_set_settings(efx, ecmd);
+ if (rc)
+ return rc;
+
+ if (ecmd->speed != SPEED_10000 && !ecmd->autoneg)
+ mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT,
+ GPHY_XCONTROL_REG, GPHY_DUPLEX_LBN,
+ ecmd->duplex == DUPLEX_FULL);
+
+ return rc;
+}
+
+static bool sft9001_set_xnp_advertise(struct efx_nic *efx, u32 advertising)
+{
+ int phy = efx->mii.phy_id;
+ int reg = mdio_clause45_read(efx, phy, MDIO_MMD_PMAPMD,
+ PMA_PMD_SPEED_ENABLE_REG);
+ bool enabled;
+
+ reg &= ~((1 << 2) | (1 << 3));
+ if (EFX_WORKAROUND_13204(efx) &&
+ (advertising & ADVERTISED_100baseT_Full))
+ reg |= 1 << PMA_PMD_100TX_ADV_LBN;
+ if (advertising & ADVERTISED_1000baseT_Full)
+ reg |= 1 << PMA_PMD_1000T_ADV_LBN;
+ if (advertising & ADVERTISED_10000baseT_Full)
+ reg |= 1 << PMA_PMD_10000T_ADV_LBN;
+ mdio_clause45_write(efx, phy, MDIO_MMD_PMAPMD,
+ PMA_PMD_SPEED_ENABLE_REG, reg);
+
+ enabled = (advertising &
+ (ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_10000baseT_Full));
+ if (EFX_WORKAROUND_13204(efx))
+ enabled |= (advertising & ADVERTISED_100baseT_Full);
+ return enabled;
+}
+
+struct efx_phy_operations falcon_sfx7101_phy_ops = {
+ .macs = EFX_XMAC,
+ .init = tenxpress_phy_init,
+ .reconfigure = tenxpress_phy_reconfigure,
+ .poll = tenxpress_phy_poll,
+ .fini = tenxpress_phy_fini,
+ .clear_interrupt = efx_port_dummy_op_void,
+ .get_settings = sfx7101_get_settings,
+ .set_settings = mdio_clause45_set_settings,
+ .num_tests = ARRAY_SIZE(sfx7101_test_names),
+ .test_names = sfx7101_test_names,
+ .run_tests = sfx7101_run_tests,
+ .mmds = TENXPRESS_REQUIRED_DEVS,
+ .loopbacks = SFX7101_LOOPBACKS,
+};
+
+struct efx_phy_operations falcon_sft9001_phy_ops = {
+ .macs = EFX_GMAC | EFX_XMAC,
.init = tenxpress_phy_init,
.reconfigure = tenxpress_phy_reconfigure,
- .check_hw = tenxpress_phy_check_hw,
+ .poll = tenxpress_phy_poll,
.fini = tenxpress_phy_fini,
- .clear_interrupt = tenxpress_phy_clear_interrupt,
- .test = tenxpress_phy_test,
+ .clear_interrupt = efx_port_dummy_op_void,
+ .get_settings = sft9001_get_settings,
+ .set_settings = sft9001_set_settings,
+ .set_xnp_advertise = sft9001_set_xnp_advertise,
+ .num_tests = ARRAY_SIZE(sft9001_test_names),
+ .test_names = sft9001_test_names,
+ .run_tests = sft9001_run_tests,
.mmds = TENXPRESS_REQUIRED_DEVS,
- .loopbacks = TENXPRESS_LOOPBACKS,
+ .loopbacks = SFT9001_LOOPBACKS,
};
diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h
index fa7b49d69288..82e03e1d7371 100644
--- a/drivers/net/sfc/workarounds.h
+++ b/drivers/net/sfc/workarounds.h
@@ -17,15 +17,20 @@
#define EFX_WORKAROUND_ALWAYS(efx) 1
#define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1)
+#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx)
+#define EFX_WORKAROUND_SFX7101(efx) ((efx)->phy_type == PHY_TYPE_SFX7101)
+#define EFX_WORKAROUND_SFT9001A(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A)
/* XAUI resets if link not detected */
#define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
/* RX PCIe double split performance issue */
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
+/* Bit-bashed I2C reads cause performance drop */
+#define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G
/* TX pkt parser problem with <= 16 byte TXes */
#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
/* Low rate CRC errors require XAUI reset */
-#define EFX_WORKAROUND_10750 EFX_WORKAROUND_ALWAYS
+#define EFX_WORKAROUND_10750 EFX_WORKAROUND_SFX7101
/* TX_EV_PKT_ERR can be caused by a dangling TX descriptor
* or a PCIe error (bug 11028) */
#define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS
@@ -49,4 +54,9 @@
/* Leak overlength packets rather than free */
#define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A
+/* Need to send XNP pages for 100BaseT */
+#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001A
+/* Need to keep AN enabled */
+#define EFX_WORKAROUND_13963 EFX_WORKAROUND_SFT9001A
+
#endif /* EFX_WORKAROUNDS_H */
diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c
index 276151df3a70..2d50b6ecf5f9 100644
--- a/drivers/net/sfc/xfp_phy.c
+++ b/drivers/net/sfc/xfp_phy.c
@@ -7,22 +7,21 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
/*
- * Driver for XFP optical PHYs (plus some support specific to the Quake 2032)
+ * Driver for XFP optical PHYs (plus some support specific to the Quake 2022/32)
* See www.amcc.com for details (search for qt2032)
*/
#include <linux/timer.h>
#include <linux/delay.h>
#include "efx.h"
-#include "gmii.h"
#include "mdio_10g.h"
#include "xenpack.h"
#include "phy.h"
-#include "mac.h"
+#include "falcon.h"
-#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_PCS | \
- MDIO_MMDREG_DEVS0_PMAPMD | \
- MDIO_MMDREG_DEVS0_PHYXS)
+#define XFP_REQUIRED_DEVS (MDIO_MMDREG_DEVS_PCS | \
+ MDIO_MMDREG_DEVS_PMAPMD | \
+ MDIO_MMDREG_DEVS_PHYXS)
#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
@@ -65,7 +64,7 @@ static int xfp_reset_phy(struct efx_nic *efx)
/* Check that all the MMDs we expect are present and responding. We
* expect faults on some if the link is down, but not on the PHY XS */
rc = mdio_clause45_check_mmds(efx, XFP_REQUIRED_DEVS,
- MDIO_MMDREG_DEVS0_PHYXS);
+ MDIO_MMDREG_DEVS_PHYXS);
if (rc < 0)
goto fail;
@@ -120,15 +119,12 @@ static int xfp_link_ok(struct efx_nic *efx)
return mdio_clause45_links_ok(efx, XFP_REQUIRED_DEVS);
}
-static int xfp_phy_check_hw(struct efx_nic *efx)
+static void xfp_phy_poll(struct efx_nic *efx)
{
- int rc = 0;
int link_up = xfp_link_ok(efx);
/* Simulate a PHY event if link state has changed */
if (link_up != efx->link_up)
- falcon_xmac_sim_phy_event(efx);
-
- return rc;
+ falcon_sim_phy_event(efx);
}
static void xfp_phy_reconfigure(struct efx_nic *efx)
@@ -145,7 +141,9 @@ static void xfp_phy_reconfigure(struct efx_nic *efx)
phy_data->phy_mode = efx->phy_mode;
efx->link_up = xfp_link_ok(efx);
- efx->link_options = GM_LPA_10000FULL;
+ efx->link_speed = 10000;
+ efx->link_fd = true;
+ efx->link_fc = efx->wanted_fc;
}
@@ -160,11 +158,14 @@ static void xfp_phy_fini(struct efx_nic *efx)
}
struct efx_phy_operations falcon_xfp_phy_ops = {
+ .macs = EFX_XMAC,
.init = xfp_phy_init,
.reconfigure = xfp_phy_reconfigure,
- .check_hw = xfp_phy_check_hw,
+ .poll = xfp_phy_poll,
.fini = xfp_phy_fini,
.clear_interrupt = xfp_phy_clear_interrupt,
+ .get_settings = mdio_clause45_get_settings,
+ .set_settings = mdio_clause45_set_settings,
.mmds = XFP_REQUIRED_DEVS,
.loopbacks = XFP_LOOPBACKS,
};
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c
index 6261201403cd..97d68560067d 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/sgiseeq.c
@@ -377,7 +377,6 @@ memory_squeeze:
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
} else {
@@ -657,7 +656,7 @@ static void timeout(struct net_device *dev)
static void sgiseeq_set_multicast(struct net_device *dev)
{
- struct sgiseeq_private *sp = (struct sgiseeq_private *) dev->priv;
+ struct sgiseeq_private *sp = netdev_priv(dev);
unsigned char oldmode = sp->mode;
if(dev->flags & IFF_PROMISC)
@@ -719,7 +718,6 @@ static int __init sgiseeq_probe(struct platform_device *pdev)
struct sgiseeq_private *sp;
struct net_device *dev;
int err;
- DECLARE_MAC_BUF(mac);
dev = alloc_etherdev(sizeof (struct sgiseeq_private));
if (!dev) {
@@ -793,8 +791,7 @@ static int __init sgiseeq_probe(struct platform_device *pdev)
goto err_out_free_page;
}
- printk(KERN_INFO "%s: %s %s\n",
- dev->name, sgiseeqstr, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: %s %pM\n", dev->name, sgiseeqstr, dev->dev_addr);
return 0;
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index 59f242a67714..7f8e514eb5e9 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -540,7 +540,6 @@ static int sh_eth_rx(struct net_device *ndev)
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
netif_rx(skb);
- ndev->last_rx = jiffies;
mdp->stats.rx_packets++;
mdp->stats.rx_bytes += pkt_len;
}
@@ -800,7 +799,7 @@ static int sh_eth_phy_init(struct net_device *ndev)
char phy_id[BUS_ID_SIZE];
struct phy_device *phydev = NULL;
- snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT,
+ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
mdp->mii_bus->id , mdp->phy_id);
mdp->link = PHY_DOWN;
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index e6e3bf58a569..83cc3c5f7946 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -627,7 +627,6 @@ static int sis190_rx_interrupt(struct net_device *dev,
sis190_rx_skb(skb);
- dev->last_rx = jiffies;
stats->rx_packets++;
stats->rx_bytes += pkt_size;
if ((status & BCAST) == MCAST)
@@ -1791,7 +1790,6 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
struct net_device *dev;
void __iomem *ioaddr;
int rc;
- DECLARE_MAC_BUF(mac);
if (!printed_version) {
net_drv(&debug, KERN_INFO SIS190_DRIVER_NAME " loaded.\n");
@@ -1841,10 +1839,9 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
if (rc < 0)
goto err_remove_mii;
- net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), "
- "%s\n",
+ net_probe(tp, KERN_INFO "%s: %s at %p (IRQ: %d), %pM\n",
pci_name(pdev), sis_chip_info[ent->driver_data].name,
- ioaddr, dev->irq, print_mac(mac, dev->dev_addr));
+ ioaddr, dev->irq, dev->dev_addr);
net_probe(tp, KERN_INFO "%s: %s mode.\n", dev->name,
(tp->features & F_HAS_RGMII) ? "RGMII" : "GMII");
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index 8e8337e8b072..4acd41a093ad 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -381,6 +381,21 @@ static int __devinit sis96x_get_mac_addr(struct pci_dev * pci_dev,
return 0;
}
+static const struct net_device_ops sis900_netdev_ops = {
+ .ndo_open = sis900_open,
+ .ndo_stop = sis900_close,
+ .ndo_start_xmit = sis900_start_xmit,
+ .ndo_set_config = sis900_set_config,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mii_ioctl,
+ .ndo_tx_timeout = sis900_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = sis900_poll,
+#endif
+};
+
/**
* sis900_probe - Probe for sis900 device
* @pci_dev: the sis900 pci device
@@ -404,7 +419,6 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
int i, ret;
const char *card_name = card_names[pci_id->driver_data];
const char *dev_name = pci_name(pci_dev);
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -437,7 +451,7 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
if (ret)
goto err_out;
- sis_priv = net_dev->priv;
+ sis_priv = netdev_priv(net_dev);
net_dev->base_addr = ioaddr;
net_dev->irq = pci_dev->irq;
sis_priv->pci_dev = pci_dev;
@@ -462,20 +476,10 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
sis_priv->rx_ring_dma = ring_dma;
/* The SiS900-specific entries in the device structure. */
- net_dev->open = &sis900_open;
- net_dev->hard_start_xmit = &sis900_start_xmit;
- net_dev->stop = &sis900_close;
- net_dev->set_config = &sis900_set_config;
- net_dev->set_multicast_list = &set_rx_mode;
- net_dev->do_ioctl = &mii_ioctl;
- net_dev->tx_timeout = sis900_tx_timeout;
+ net_dev->netdev_ops = &sis900_netdev_ops;
net_dev->watchdog_timeo = TX_TIMEOUT;
net_dev->ethtool_ops = &sis900_ethtool_ops;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- net_dev->poll_controller = &sis900_poll;
-#endif
-
if (sis900_debug > 0)
sis_priv->msg_enable = sis900_debug;
else
@@ -534,9 +538,9 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
goto err_unmap_rx;
/* print some information about our NIC */
- printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %s\n",
+ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %pM\n",
net_dev->name, card_name, ioaddr, net_dev->irq,
- print_mac(mac, net_dev->dev_addr));
+ net_dev->dev_addr);
/* Detect Wake on Lan support */
ret = (inl(net_dev->base_addr + CFGPMC) & PMESP) >> 27;
@@ -570,7 +574,7 @@ static int __devinit sis900_probe(struct pci_dev *pci_dev,
static int __devinit sis900_mii_probe(struct net_device * net_dev)
{
- struct sis900_private * sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
const char *dev_name = pci_name(sis_priv->pci_dev);
u16 poll_bit = MII_STAT_LINK, status = 0;
unsigned long timeout = jiffies + 5 * HZ;
@@ -698,7 +702,7 @@ static int __devinit sis900_mii_probe(struct net_device * net_dev)
static u16 sis900_default_phy(struct net_device * net_dev)
{
- struct sis900_private * sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct mii_phy *phy = NULL, *phy_home = NULL,
*default_phy = NULL, *phy_lan = NULL;
u16 status;
@@ -999,7 +1003,7 @@ static void sis900_poll(struct net_device *dev)
static int
sis900_open(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
int ret;
@@ -1055,7 +1059,7 @@ sis900_open(struct net_device *net_dev)
static void
sis900_init_rxfilter (struct net_device * net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
u32 rfcrSave;
u32 i;
@@ -1093,7 +1097,7 @@ sis900_init_rxfilter (struct net_device * net_dev)
static void
sis900_init_tx_ring(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
int i;
@@ -1127,7 +1131,7 @@ sis900_init_tx_ring(struct net_device *net_dev)
static void
sis900_init_rx_ring(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
int i;
@@ -1198,7 +1202,7 @@ sis900_init_rx_ring(struct net_device *net_dev)
static void sis630_set_eq(struct net_device *net_dev, u8 revision)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
u16 reg14h, eq_value=0, max_value=0, min_value=0;
int i, maxcount=10;
@@ -1271,13 +1275,13 @@ static void sis630_set_eq(struct net_device *net_dev, u8 revision)
static void sis900_timer(unsigned long data)
{
struct net_device *net_dev = (struct net_device *)data;
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct mii_phy *mii_phy = sis_priv->mii;
static const int next_tick = 5*HZ;
u16 status;
if (!sis_priv->autong_complete){
- int speed, duplex = 0;
+ int uninitialized_var(speed), duplex = 0;
sis900_read_mode(net_dev, &speed, &duplex);
if (duplex){
@@ -1341,7 +1345,7 @@ static void sis900_timer(unsigned long data)
static void sis900_check_mode(struct net_device *net_dev, struct mii_phy *mii_phy)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
int speed, duplex;
@@ -1420,7 +1424,7 @@ static void sis900_set_mode (long ioaddr, int speed, int duplex)
static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
int i = 0;
u32 status;
@@ -1455,7 +1459,7 @@ static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr)
static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct mii_phy *phy = sis_priv->mii;
int phy_addr = sis_priv->cur_phy;
u32 status;
@@ -1510,7 +1514,7 @@ static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex
static void sis900_tx_timeout(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
unsigned long flags;
int i;
@@ -1569,7 +1573,7 @@ static void sis900_tx_timeout(struct net_device *net_dev)
static int
sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
unsigned int entry;
unsigned long flags;
@@ -1638,7 +1642,7 @@ sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
static irqreturn_t sis900_interrupt(int irq, void *dev_instance)
{
struct net_device *net_dev = dev_instance;
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
int boguscnt = max_interrupt_work;
long ioaddr = net_dev->base_addr;
u32 status;
@@ -1700,7 +1704,7 @@ static irqreturn_t sis900_interrupt(int irq, void *dev_instance)
static int sis900_rx(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
@@ -1789,7 +1793,6 @@ static int sis900_rx(struct net_device *net_dev)
/* some network statistics */
if ((rx_status & BCAST) == MCAST)
net_dev->stats.multicast++;
- net_dev->last_rx = jiffies;
net_dev->stats.rx_bytes += rx_size;
net_dev->stats.rx_packets++;
sis_priv->dirty_rx++;
@@ -1850,7 +1853,7 @@ refill_rx_ring:
static void sis900_finish_xmit (struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
for (; sis_priv->dirty_tx != sis_priv->cur_tx; sis_priv->dirty_tx++) {
struct sk_buff *skb;
@@ -1919,7 +1922,7 @@ static void sis900_finish_xmit (struct net_device *net_dev)
static int sis900_close(struct net_device *net_dev)
{
long ioaddr = net_dev->base_addr;
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct sk_buff *skb;
int i;
@@ -1974,7 +1977,7 @@ static int sis900_close(struct net_device *net_dev)
static void sis900_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
strcpy (info->driver, SIS900_MODULE_NAME);
strcpy (info->version, SIS900_DRV_VERSION);
@@ -1983,26 +1986,26 @@ static void sis900_get_drvinfo(struct net_device *net_dev,
static u32 sis900_get_msglevel(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
return sis_priv->msg_enable;
}
static void sis900_set_msglevel(struct net_device *net_dev, u32 value)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
sis_priv->msg_enable = value;
}
static u32 sis900_get_link(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
return mii_link_ok(&sis_priv->mii_info);
}
static int sis900_get_settings(struct net_device *net_dev,
struct ethtool_cmd *cmd)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
spin_lock_irq(&sis_priv->lock);
mii_ethtool_gset(&sis_priv->mii_info, cmd);
spin_unlock_irq(&sis_priv->lock);
@@ -2012,7 +2015,7 @@ static int sis900_get_settings(struct net_device *net_dev,
static int sis900_set_settings(struct net_device *net_dev,
struct ethtool_cmd *cmd)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
int rt;
spin_lock_irq(&sis_priv->lock);
rt = mii_ethtool_sset(&sis_priv->mii_info, cmd);
@@ -2022,7 +2025,7 @@ static int sis900_set_settings(struct net_device *net_dev,
static int sis900_nway_reset(struct net_device *net_dev)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
return mii_nway_restart(&sis_priv->mii_info);
}
@@ -2039,7 +2042,7 @@ static int sis900_nway_reset(struct net_device *net_dev)
static int sis900_set_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long pmctrl_addr = net_dev->base_addr + pmctrl;
u32 cfgpmcsr = 0, pmctrl_bits = 0;
@@ -2110,7 +2113,7 @@ static const struct ethtool_ops sis900_ethtool_ops = {
static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
{
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct mii_ioctl_data *data = if_mii(rq);
switch(cmd) {
@@ -2144,7 +2147,7 @@ static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd)
static int sis900_set_config(struct net_device *dev, struct ifmap *map)
{
- struct sis900_private *sis_priv = dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(dev);
struct mii_phy *mii_phy = sis_priv->mii;
u16 status;
@@ -2267,7 +2270,7 @@ static inline u16 sis900_mcast_bitnr(u8 *addr, u8 revision)
static void set_rx_mode(struct net_device *net_dev)
{
long ioaddr = net_dev->base_addr;
- struct sis900_private * sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
u16 mc_filter[16] = {0}; /* 256/128 bits multicast hash table */
int i, table_entries;
u32 rx_mode;
@@ -2342,7 +2345,7 @@ static void set_rx_mode(struct net_device *net_dev)
static void sis900_reset(struct net_device *net_dev)
{
- struct sis900_private * sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
int i = 0;
u32 status = TxRCMP | RxRCMP;
@@ -2375,7 +2378,7 @@ static void sis900_reset(struct net_device *net_dev)
static void __devexit sis900_remove(struct pci_dev *pci_dev)
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
- struct sis900_private * sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
struct mii_phy *phy = NULL;
while (sis_priv->first_mii) {
@@ -2419,7 +2422,7 @@ static int sis900_suspend(struct pci_dev *pci_dev, pm_message_t state)
static int sis900_resume(struct pci_dev *pci_dev)
{
struct net_device *net_dev = pci_get_drvdata(pci_dev);
- struct sis900_private *sis_priv = net_dev->priv;
+ struct sis900_private *sis_priv = netdev_priv(net_dev);
long ioaddr = net_dev->base_addr;
if(!netif_running(net_dev))
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index a2b092bb3626..607efeaf0bc5 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -168,6 +168,17 @@ static int num_boards; /* total number of adapters configured */
#define PRINTK(s, args...)
#endif // DRIVERDEBUG
+static const struct net_device_ops skfp_netdev_ops = {
+ .ndo_open = skfp_open,
+ .ndo_stop = skfp_close,
+ .ndo_start_xmit = skfp_send_pkt,
+ .ndo_get_stats = skfp_ctl_get_stats,
+ .ndo_change_mtu = fddi_change_mtu,
+ .ndo_set_multicast_list = skfp_ctl_set_multicast_list,
+ .ndo_set_mac_address = skfp_ctl_set_mac_address,
+ .ndo_do_ioctl = skfp_ioctl,
+};
+
/*
* =================
* = skfp_init_one =
@@ -253,13 +264,7 @@ static int skfp_init_one(struct pci_dev *pdev,
}
dev->irq = pdev->irq;
- dev->get_stats = &skfp_ctl_get_stats;
- dev->open = &skfp_open;
- dev->stop = &skfp_close;
- dev->hard_start_xmit = &skfp_send_pkt;
- dev->set_multicast_list = &skfp_ctl_set_multicast_list;
- dev->set_mac_address = &skfp_ctl_set_mac_address;
- dev->do_ioctl = &skfp_ioctl;
+ dev->netdev_ops = &skfp_netdev_ops;
SET_NETDEV_DEV(dev, &pdev->dev);
@@ -612,7 +617,7 @@ static int skfp_close(struct net_device *dev)
* Interrupts are disabled, then reenabled at the adapter.
*/
-irqreturn_t skfp_interrupt(int irq, void *dev_id)
+static irqreturn_t skfp_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct s_smc *smc; /* private board structure pointer */
@@ -679,7 +684,7 @@ irqreturn_t skfp_interrupt(int irq, void *dev_id)
* independent.
*
*/
-struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev)
+static struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev)
{
struct s_smc *bp = netdev_priv(dev);
@@ -1224,7 +1229,7 @@ static void send_queued_packets(struct s_smc *smc)
* Verify if the source address is set. Insert it if necessary.
*
************************/
-void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr)
+static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr)
{
unsigned char SRBit;
@@ -1680,7 +1685,6 @@ void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd,
skb->protocol = fddi_type_trans(skb, bp->dev);
netif_rx(skb);
- bp->dev->last_rx = jiffies;
HWM_RX_CHECK(smc, RX_LOW_WATERMARK);
return;
@@ -1939,7 +1943,6 @@ int mac_drv_rx_init(struct s_smc *smc, int len, int fc,
// deliver frame to system
skb->protocol = fddi_type_trans(skb, smc->os.dev);
- skb->dev->last_rx = jiffies;
netif_rx(skb);
return (0);
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 43f4c730be42..c9dbb06f8c94 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -104,6 +104,7 @@ static void yukon_get_stats(struct skge_port *skge, u64 *data);
static void yukon_init(struct skge_hw *hw, int port);
static void genesis_mac_init(struct skge_hw *hw, int port);
static void genesis_link_up(struct skge_port *skge);
+static void skge_set_multicast(struct net_device *dev);
/* Avoid conditionals by using array */
static const int txqaddr[] = { Q_XA1, Q_XA2 };
@@ -149,24 +150,6 @@ static u32 wol_supported(const struct skge_hw *hw)
return WAKE_MAGIC | WAKE_PHY;
}
-static u32 pci_wake_enabled(struct pci_dev *dev)
-{
- int pm = pci_find_capability(dev, PCI_CAP_ID_PM);
- u16 value;
-
- /* If device doesn't support PM Capabilities, but request is to disable
- * wake events, it's a nop; otherwise fail */
- if (!pm)
- return 0;
-
- pci_read_config_word(dev, pm + PCI_PM_PMC, &value);
-
- value &= PCI_PM_CAP_PME_MASK;
- value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */
-
- return value != 0;
-}
-
static void skge_wol_init(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
@@ -254,10 +237,14 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
- if (wol->wolopts & ~wol_supported(hw))
+ if ((wol->wolopts & ~wol_supported(hw))
+ || !device_can_wakeup(&hw->pdev->dev))
return -EOPNOTSUPP;
skge->wol = wol->wolopts;
+
+ device_set_wakeup_enable(&hw->pdev->dev, skge->wol);
+
return 0;
}
@@ -2477,7 +2464,7 @@ static void skge_phy_reset(struct skge_port *skge)
}
spin_unlock_bh(&hw->phy_lock);
- dev->set_multicast_list(dev);
+ skge_set_multicast(dev);
}
/* Basic MII support */
@@ -3045,6 +3032,18 @@ static inline int bad_phy_status(const struct skge_hw *hw, u32 status)
(status & GMR_FS_RX_OK) == 0;
}
+static void skge_set_multicast(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+
+ if (hw->chip_id == CHIP_ID_GENESIS)
+ genesis_set_multicast(dev);
+ else
+ yukon_set_multicast(dev);
+
+}
+
/* Get receive buffer from descriptor.
* Handles copy of small buffers and reallocation failures
@@ -3200,7 +3199,6 @@ static int skge_poll(struct napi_struct *napi, int to_do)
skb = skge_rx_get(dev, e, control, rd->status, rd->csum2);
if (likely(skb)) {
- dev->last_rx = jiffies;
netif_receive_skb(skb);
++work_done;
@@ -3216,7 +3214,7 @@ static int skge_poll(struct napi_struct *napi, int to_do)
unsigned long flags;
spin_lock_irqsave(&hw->hw_lock, flags);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
hw->intr_mask |= napimask[skge->port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
skge_read32(hw, B0_IMSK);
@@ -3379,7 +3377,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id)
if (status & (IS_XA1_F|IS_R1_F)) {
struct skge_port *skge = netdev_priv(hw->dev[0]);
hw->intr_mask &= ~(IS_XA1_F|IS_R1_F);
- netif_rx_schedule(hw->dev[0], &skge->napi);
+ netif_rx_schedule(&skge->napi);
}
if (status & IS_PA_TO_TX1)
@@ -3399,7 +3397,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id)
if (status & (IS_XA2_F|IS_R2_F)) {
hw->intr_mask &= ~(IS_XA2_F|IS_R2_F);
- netif_rx_schedule(hw->dev[1], &skge->napi);
+ netif_rx_schedule(&skge->napi);
}
if (status & IS_PA_TO_RX2) {
@@ -3730,7 +3728,7 @@ static int skge_device_event(struct notifier_block *unused,
struct skge_port *skge;
struct dentry *d;
- if (dev->open != &skge_up || !skge_debug)
+ if (dev->netdev_ops->ndo_open != &skge_up || !skge_debug)
goto done;
skge = netdev_priv(dev);
@@ -3804,6 +3802,23 @@ static __exit void skge_debug_cleanup(void)
#define skge_debug_cleanup()
#endif
+static const struct net_device_ops skge_netdev_ops = {
+ .ndo_open = skge_up,
+ .ndo_stop = skge_down,
+ .ndo_start_xmit = skge_xmit_frame,
+ .ndo_do_ioctl = skge_ioctl,
+ .ndo_get_stats = skge_get_stats,
+ .ndo_tx_timeout = skge_tx_timeout,
+ .ndo_change_mtu = skge_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = skge_set_multicast,
+ .ndo_set_mac_address = skge_set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = skge_netpoll,
+#endif
+};
+
+
/* Initialize network device */
static struct net_device *skge_devinit(struct skge_hw *hw, int port,
int highmem)
@@ -3817,24 +3832,9 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
}
SET_NETDEV_DEV(dev, &hw->pdev->dev);
- dev->open = skge_up;
- dev->stop = skge_down;
- dev->do_ioctl = skge_ioctl;
- dev->hard_start_xmit = skge_xmit_frame;
- dev->get_stats = skge_get_stats;
- if (hw->chip_id == CHIP_ID_GENESIS)
- dev->set_multicast_list = genesis_set_multicast;
- else
- dev->set_multicast_list = yukon_set_multicast;
-
- dev->set_mac_address = skge_set_mac_address;
- dev->change_mtu = skge_change_mtu;
- SET_ETHTOOL_OPS(dev, &skge_ethtool_ops);
- dev->tx_timeout = skge_tx_timeout;
+ dev->netdev_ops = &skge_netdev_ops;
+ dev->ethtool_ops = &skge_ethtool_ops;
dev->watchdog_timeo = TX_WATCHDOG;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = skge_netpoll;
-#endif
dev->irq = hw->pdev->irq;
if (highmem)
@@ -3856,7 +3856,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
skge->speed = -1;
skge->advertising = skge_supported_modes(hw);
- if (pci_wake_enabled(hw->pdev))
+ if (device_may_wakeup(&hw->pdev->dev))
skge->wol = wol_supported(hw) & WAKE_MAGIC;
hw->dev[port] = dev;
@@ -3885,11 +3885,10 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
static void __devinit skge_show_addr(struct net_device *dev)
{
const struct skge_port *skge = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
if (netif_msg_probe(skge))
- printk(KERN_INFO PFX "%s: addr %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO PFX "%s: addr %pM\n",
+ dev->name, dev->dev_addr);
}
static int __devinit skge_probe(struct pci_dev *pdev,
@@ -4082,8 +4081,8 @@ static int skge_suspend(struct pci_dev *pdev, pm_message_t state)
}
skge_write32(hw, B0_IMSK, 0);
- pci_enable_wake(pdev, pci_choose_state(pdev, state), wol);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ pci_prepare_to_sleep(pdev);
return 0;
}
@@ -4096,7 +4095,7 @@ static int skge_resume(struct pci_dev *pdev)
if (!hw)
return 0;
- err = pci_set_power_state(pdev, PCI_D0);
+ err = pci_back_from_sleep(pdev);
if (err)
goto out;
@@ -4104,8 +4103,6 @@ static int skge_resume(struct pci_dev *pdev)
if (err)
goto out;
- pci_enable_wake(pdev, PCI_D0, 0);
-
err = skge_reset(hw);
if (err)
goto out;
@@ -4146,8 +4143,8 @@ static void skge_shutdown(struct pci_dev *pdev)
wol |= skge->wol;
}
- pci_enable_wake(pdev, PCI_D3hot, wol);
- pci_enable_wake(pdev, PCI_D3cold, wol);
+ if (pci_enable_wake(pdev, PCI_D3cold, wol))
+ pci_enable_wake(pdev, PCI_D3hot, wol);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 3813d15e2df7..3668e81e474d 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -3979,7 +3979,7 @@ static int sky2_device_event(struct notifier_block *unused,
struct net_device *dev = ptr;
struct sky2_port *sky2 = netdev_priv(dev);
- if (dev->open != sky2_up || !sky2_debug)
+ if (dev->netdev_ops->ndo_open != sky2_up || !sky2_debug)
return NOTIFY_DONE;
switch(event) {
@@ -4041,6 +4041,41 @@ static __exit void sky2_debug_cleanup(void)
#define sky2_debug_cleanup()
#endif
+/* Two copies of network device operations to handle special case of
+ not allowing netpoll on second port */
+static const struct net_device_ops sky2_netdev_ops[2] = {
+ {
+ .ndo_open = sky2_up,
+ .ndo_stop = sky2_down,
+ .ndo_start_xmit = sky2_xmit_frame,
+ .ndo_do_ioctl = sky2_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = sky2_set_mac_address,
+ .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_change_mtu = sky2_change_mtu,
+ .ndo_tx_timeout = sky2_tx_timeout,
+#ifdef SKY2_VLAN_TAG_USED
+ .ndo_vlan_rx_register = sky2_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = sky2_netpoll,
+#endif
+ },
+ {
+ .ndo_open = sky2_up,
+ .ndo_stop = sky2_down,
+ .ndo_start_xmit = sky2_xmit_frame,
+ .ndo_do_ioctl = sky2_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = sky2_set_mac_address,
+ .ndo_set_multicast_list = sky2_set_multicast,
+ .ndo_change_mtu = sky2_change_mtu,
+ .ndo_tx_timeout = sky2_tx_timeout,
+#ifdef SKY2_VLAN_TAG_USED
+ .ndo_vlan_rx_register = sky2_vlan_rx_register,
+#endif
+ },
+};
/* Initialize network device */
static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
@@ -4057,20 +4092,9 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
SET_NETDEV_DEV(dev, &hw->pdev->dev);
dev->irq = hw->pdev->irq;
- dev->open = sky2_up;
- dev->stop = sky2_down;
- dev->do_ioctl = sky2_ioctl;
- dev->hard_start_xmit = sky2_xmit_frame;
- dev->set_multicast_list = sky2_set_multicast;
- dev->set_mac_address = sky2_set_mac_address;
- dev->change_mtu = sky2_change_mtu;
SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops);
- dev->tx_timeout = sky2_tx_timeout;
dev->watchdog_timeo = TX_WATCHDOG;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- if (port == 0)
- dev->poll_controller = sky2_netpoll;
-#endif
+ dev->netdev_ops = &sky2_netdev_ops[port];
sky2 = netdev_priv(dev);
sky2->netdev = dev;
@@ -4104,7 +4128,6 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
if (!(sky2->hw->chip_id == CHIP_ID_YUKON_FE_P &&
sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0)) {
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = sky2_vlan_rx_register;
}
#endif
@@ -4118,11 +4141,10 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
static void __devinit sky2_show_addr(struct net_device *dev)
{
const struct sky2_port *sky2 = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
if (netif_msg_probe(sky2))
- printk(KERN_INFO PFX "%s: addr %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO PFX "%s: addr %pM\n",
+ dev->name, dev->dev_addr);
}
/* Handle software interrupt used during MSI test */
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 1d58991d395b..8e1c0baf6958 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -365,7 +365,6 @@ static void sl_bump(struct slip *sl)
skb_reset_mac_header(skb);
skb->protocol = htons(ETH_P_IP);
netif_rx(skb);
- sl->dev->last_rx = jiffies;
sl->rx_packets++;
}
@@ -402,7 +401,7 @@ static void sl_encaps(struct slip *sl, unsigned char *icp, int len)
* if we did not request it before write operation.
* 14 Oct 1994 Dmitry Gorodchanin.
*/
- sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
actual = sl->tty->ops->write(sl->tty, sl->xbuff, count);
#ifdef SL_CHECK_TRANSMIT
sl->dev->trans_start = jiffies;
@@ -432,7 +431,7 @@ static void slip_write_wakeup(struct tty_struct *tty)
/* Now serial buffer is almost free & we can start
* transmission of another packet */
sl->tx_packets++;
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
sl_unlock(sl);
return;
}
@@ -465,7 +464,7 @@ static void sl_tx_timeout(struct net_device *dev)
(tty_chars_in_buffer(sl->tty) || sl->xleft) ?
"bad line quality" : "driver error");
sl->xleft = 0;
- sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
sl_unlock(sl);
#endif
}
@@ -515,10 +514,9 @@ sl_close(struct net_device *dev)
struct slip *sl = netdev_priv(dev);
spin_lock_bh(&sl->lock);
- if (sl->tty) {
+ if (sl->tty)
/* TTY discipline is running. */
- sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- }
+ clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
netif_stop_queue(dev);
sl->rcount = 0;
sl->xleft = 0;
diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c
index d6abb68e6e2f..404b80e5ba11 100644
--- a/drivers/net/smc-mca.c
+++ b/drivers/net/smc-mca.c
@@ -182,6 +182,22 @@ static char *smc_mca_adapter_names[] __initdata = {
static int ultra_found = 0;
+
+static const struct net_device_ops ultramca_netdev_ops = {
+ .ndo_open = ultramca_open,
+ .ndo_stop = ultramca_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init ultramca_probe(struct device *gen_dev)
{
unsigned short ioaddr;
@@ -196,7 +212,6 @@ static int __init ultramca_probe(struct device *gen_dev)
int tirq = 0;
int base_addr = ultra_io[ultra_found];
int irq = ultra_irq[ultra_found];
- DECLARE_MAC_BUF(mac);
if (base_addr || irq) {
printk(KERN_INFO "Probing for SMC MCA adapter");
@@ -334,8 +349,8 @@ static int __init ultramca_probe(struct device *gen_dev)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
- printk(KERN_INFO "smc_mca[%d]: Parameters: %#3x, %s",
- slot + 1, ioaddr, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "smc_mca[%d]: Parameters: %#3x, %pM",
+ slot + 1, ioaddr, dev->dev_addr);
/* Switch from the station address to the alternate register set
* and read the useful registers there.
@@ -385,11 +400,7 @@ static int __init ultramca_probe(struct device *gen_dev)
ei_status.priv = slot;
- dev->open = &ultramca_open;
- dev->stop = &ultramca_close_card;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ultramca_netdev_ops;
NS8390_init(dev, 0);
diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
index 00d6cf1af484..b3866089a206 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/smc-ultra.c
@@ -187,6 +187,21 @@ out:
}
#endif
+static const struct net_device_ops ultra_netdev_ops = {
+ .ndo_open = ultra_open,
+ .ndo_stop = ultra_close_card,
+
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init ultra_probe1(struct net_device *dev, int ioaddr)
{
int i, retval;
@@ -198,7 +213,6 @@ static int __init ultra_probe1(struct net_device *dev, int ioaddr)
unsigned char num_pages, irqreg, addr, piomode;
unsigned char idreg = inb(ioaddr + 7);
unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -228,8 +242,8 @@ static int __init ultra_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
- printk("%s: %s at %#3x, %s", dev->name, model_name,
- ioaddr, print_mac(mac, dev->dev_addr));
+ printk("%s: %s at %#3x, %pM", dev->name, model_name,
+ ioaddr, dev->dev_addr);
/* Switch from the station address to the alternate register set and
read the useful registers there. */
@@ -301,11 +315,8 @@ static int __init ultra_probe1(struct net_device *dev, int ioaddr)
ei_status.get_8390_hdr = &ultra_get_8390_hdr;
}
ei_status.reset_8390 = &ultra_reset_8390;
- dev->open = &ultra_open;
- dev->stop = &ultra_close_card;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+
+ dev->netdev_ops = &ultra_netdev_ops;
NS8390_init(dev, 0);
retval = register_netdev(dev);
diff --git a/drivers/net/smc-ultra32.c b/drivers/net/smc-ultra32.c
index a5a91ace28cc..cb6c097a2e0a 100644
--- a/drivers/net/smc-ultra32.c
+++ b/drivers/net/smc-ultra32.c
@@ -163,7 +163,6 @@ static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
unsigned char idreg;
unsigned char reg4;
const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"};
- DECLARE_MAC_BUF(mac);
if (!request_region(ioaddr, ULTRA32_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -207,8 +206,8 @@ static int __init ultra32_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
- printk("%s: %s at 0x%X, %s",
- dev->name, model_name, ioaddr, print_mac(mac, dev->dev_addr));
+ printk("%s: %s at 0x%X, %pM",
+ dev->name, model_name, ioaddr, dev->dev_addr);
/* Switch from the station address to the alternate register set and
read the useful registers there. */
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index 9a16a79b67d0..bf3aa2a1effe 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -439,7 +439,6 @@ static inline void smc911x_rcv(struct net_device *dev)
DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);
PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
- dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
@@ -1231,7 +1230,6 @@ smc911x_rx_dma_irq(int dma, void *data)
BUG_ON(skb == NULL);
lp->current_rx_skb = NULL;
PRINT_PKT(skb->data, skb->len);
- dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
@@ -2050,9 +2048,6 @@ err_out:
*/
static int __devinit smc911x_drv_probe(struct platform_device *pdev)
{
-#ifdef SMC_DYNAMIC_BUS_CONFIG
- struct smc911x_platdata *pd = pdev->dev.platform_data;
-#endif
struct net_device *ndev;
struct resource *res;
struct smc911x_local *lp;
@@ -2087,11 +2082,14 @@ static int __devinit smc911x_drv_probe(struct platform_device *pdev)
lp = netdev_priv(ndev);
lp->netdev = ndev;
#ifdef SMC_DYNAMIC_BUS_CONFIG
- if (!pd) {
- ret = -EINVAL;
- goto release_both;
+ {
+ struct smc911x_platdata *pd = pdev->dev.platform_data;
+ if (!pd) {
+ ret = -EINVAL;
+ goto release_both;
+ }
+ memcpy(&lp->cfg, pd, sizeof(lp->cfg));
}
- memcpy(&lp->cfg, pd, sizeof(lp->cfg));
#endif
addr = ioremap(res->start, SMC911X_IO_EXTENT);
diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h
index cc7d85bdfb3e..870b4c33f108 100644
--- a/drivers/net/smc911x.h
+++ b/drivers/net/smc911x.h
@@ -200,6 +200,9 @@ static inline void SMC_outsl(struct smc911x_local *lp, int reg,
#ifdef SMC_USE_PXA_DMA
+
+#include <mach/dma.h>
+
/*
* Define the request and free functions
* These are unfortunately architecture specific as no generic allocation
diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c
index de67744c4a2a..18d653bbd4e0 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/smc9194.c
@@ -764,7 +764,7 @@ out:
. interrupt, so an auto-detect routine can detect it, and find the IRQ,
------------------------------------------------------------------------
*/
-int __init smc_findirq( int ioaddr )
+static int __init smc_findirq(int ioaddr)
{
#ifndef NO_AUTOPROBE
int timeout = 20;
@@ -876,8 +876,6 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
word memory_info_register;
word memory_cfg_register;
- DECLARE_MAC_BUF(mac);
-
/* Grab the region so that no one else tries to probe our ioports. */
if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
return -EBUSY;
@@ -1033,10 +1031,10 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
/*
. Print the Ethernet address
*/
- printk("ADDR: %s\n", print_mac(mac, dev->dev_addr));
+ printk("ADDR: %pM\n", dev->dev_addr);
/* set the private data to zero by default */
- memset(dev->priv, 0, sizeof(struct smc_local));
+ memset(netdev_priv(dev), 0, sizeof(struct smc_local));
/* Grab the IRQ */
retval = request_irq(dev->irq, &smc_interrupt, 0, DRV_NAME, dev);
@@ -1110,7 +1108,7 @@ static int smc_open(struct net_device *dev)
int i; /* used to set hw ethernet address */
/* clear out all the junk that was put here before... */
- memset(dev->priv, 0, sizeof(struct smc_local));
+ memset(netdev_priv(dev), 0, sizeof(struct smc_local));
/* reset the hardware */
@@ -1166,7 +1164,7 @@ static void smc_timeout(struct net_device *dev)
smc_enable( dev->base_addr );
dev->trans_start = jiffies;
/* clear anything saved */
- ((struct smc_local *)dev->priv)->saved_skb = NULL;
+ ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
netif_wake_queue(dev);
}
@@ -1272,7 +1270,6 @@ static void smc_rcv(struct net_device *dev)
skb->protocol = eth_type_trans(skb, dev );
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += packet_length;
} else {
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index 35c56abf4113..b215a8d85e62 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -90,33 +90,6 @@ static const char version[] =
#include "smc91x.h"
-#ifdef CONFIG_ISA
-/*
- * the LAN91C111 can be at any of the following port addresses. To change,
- * for a slightly different card, you can add it to the array. Keep in
- * mind that the array must end in zero.
- */
-static unsigned int smc_portlist[] __initdata = {
- 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
- 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0
-};
-
-#ifndef SMC_IOADDR
-# define SMC_IOADDR -1
-#endif
-static unsigned long io = SMC_IOADDR;
-module_param(io, ulong, 0400);
-MODULE_PARM_DESC(io, "I/O base address");
-
-#ifndef SMC_IRQ
-# define SMC_IRQ -1
-#endif
-static int irq = SMC_IRQ;
-module_param(irq, int, 0400);
-MODULE_PARM_DESC(irq, "IRQ number");
-
-#endif /* CONFIG_ISA */
-
#ifndef SMC_NOWAIT
# define SMC_NOWAIT 0
#endif
@@ -518,7 +491,6 @@ static inline void smc_rcv(struct net_device *dev)
PRINT_PKT(data, packet_len - 4);
- dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->stats.rx_packets++;
@@ -1778,7 +1750,6 @@ static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr,
int retval;
unsigned int val, revision_register;
const char *version_string;
- DECLARE_MAC_BUF(mac);
DBG(2, "%s: %s\n", CARDNAME, __func__);
@@ -1972,8 +1943,8 @@ static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr,
"set using ifconfig\n", dev->name);
} else {
/* Print the Ethernet address */
- printk("%s: Ethernet addr: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: Ethernet addr: %pM\n",
+ dev->name, dev->dev_addr);
}
if (lp->phy_type == 0) {
@@ -2316,15 +2287,6 @@ static struct platform_driver smc_driver = {
static int __init smc_init(void)
{
-#ifdef MODULE
-#ifdef CONFIG_ISA
- if (io == -1)
- printk(KERN_WARNING
- "%s: You shouldn't use auto-probing with insmod!\n",
- CARDNAME);
-#endif
-#endif
-
return platform_driver_register(&smc_driver);
}
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index a07cc9351c6b..c4ccd121bc9c 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -87,49 +87,28 @@ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define RPC_LSA_DEFAULT RPC_LED_100_10
#define RPC_LSB_DEFAULT RPC_LED_TX_RX
-# if defined (CONFIG_BFIN561_EZKIT)
#define SMC_CAN_USE_8BIT 0
#define SMC_CAN_USE_16BIT 1
+# if defined(CONFIG_BF561)
#define SMC_CAN_USE_32BIT 1
-#define SMC_IO_SHIFT 0
-#define SMC_NOWAIT 1
-#define SMC_USE_BFIN_DMA 0
-
-
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_outsl(a, r, p, l) outsl((unsigned long *)((a) + (r)), p, l)
-#define SMC_insl(a, r, p, l) insl ((unsigned long *)((a) + (r)), p, l)
# else
-#define SMC_CAN_USE_8BIT 0
-#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 0
+# endif
#define SMC_IO_SHIFT 0
#define SMC_NOWAIT 1
#define SMC_USE_BFIN_DMA 0
-
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outsw(a, r, p, l) outsw((unsigned long *)((a) + (r)), p, l)
-#define SMC_insw(a, r, p, l) insw ((unsigned long *)((a) + (r)), p, l)
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+# if SMC_CAN_USE_32BIT
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
# endif
-/* check if the mac in reg is valid */
-#define SMC_GET_MAC_ADDR(lp, addr) \
- do { \
- unsigned int __v; \
- __v = SMC_inw(ioaddr, ADDR0_REG(lp)); \
- addr[0] = __v; addr[1] = __v >> 8; \
- __v = SMC_inw(ioaddr, ADDR1_REG(lp)); \
- addr[2] = __v; addr[3] = __v >> 8; \
- __v = SMC_inw(ioaddr, ADDR2_REG(lp)); \
- addr[4] = __v; addr[5] = __v >> 8; \
- if (*(u32 *)(&addr[0]) == 0xFFFFFFFF) { \
- random_ether_addr(addr); \
- } \
- } while (0)
+
#elif defined(CONFIG_REDWOOD_5) || defined(CONFIG_REDWOOD_6)
/* We can only do 16-bit reads and writes in the static memory space. */
@@ -286,19 +265,6 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define SMC_IRQ_FLAGS (0)
-#elif defined(CONFIG_ISA)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 0
-
-#define SMC_inb(a, r) inb((a) + (r))
-#define SMC_inw(a, r) inw((a) + (r))
-#define SMC_outb(v, a, r) outb(v, (a) + (r))
-#define SMC_outw(v, a, r) outw(v, (a) + (r))
-#define SMC_insw(a, r, p, l) insw((a) + (r), p, l)
-#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
-
#elif defined(CONFIG_M32R)
#define SMC_CAN_USE_8BIT 0
@@ -527,7 +493,8 @@ struct smc_local {
* as RX which can overrun memory and lose packets.
*/
#include <linux/dma-mapping.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#ifdef SMC_insl
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
new file mode 100644
index 000000000000..dc3f1108884d
--- /dev/null
+++ b/drivers/net/smsc911x.c
@@ -0,0 +1,2071 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ * LAN9210, LAN9211
+ * LAN9220, LAN9221
+ *
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/phy.h>
+#include <linux/smsc911x.h>
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_MDIONAME "smsc911x-mdio"
+#define SMSC_DRV_VERSION "2008-10-21"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SMSC_DRV_VERSION);
+
+#if USE_DEBUG > 0
+static int debug = 16;
+#else
+static int debug = 3;
+#endif
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+
+ /* used to decide which workarounds apply */
+ unsigned int generation;
+
+ /* device configuration (copied from platform_data during probe) */
+ struct smsc911x_platform_config config;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ */
+ spinlock_t mac_lock;
+
+ /* spinlock to ensure 16-bit accesses are serialised.
+ * unused with a 32-bit bus */
+ spinlock_t dev_lock;
+
+ struct phy_device *phy_dev;
+ struct mii_bus *mii_bus;
+ int phy_irq[PHY_MAX_ADDR];
+ unsigned int using_extphy;
+ int last_duplex;
+ int last_carrier;
+
+ u32 msg_enable;
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+ struct net_device *dev;
+ struct napi_struct napi;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+};
+
+/* The 16-bit access functions are significantly slower, due to the locking
+ * necessary. If your bus hardware can be configured to do this for you
+ * (in response to a single 32-bit operation from software), you should use
+ * the 32-bit access functions instead. */
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT)
+ return readl(pdata->ioaddr + reg);
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ u32 data;
+ unsigned long flags;
+
+ /* these two 16-bit reads must be performed consecutively, so
+ * must not be interrupted by our own ISR (which would start
+ * another read operation) */
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+ data = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+
+ return data;
+ }
+
+ BUG();
+}
+
+static inline void smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
+ u32 val)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writel(val, pdata->ioaddr + reg);
+ return;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ unsigned long flags;
+
+ /* these two 16-bit writes must be performed consecutively, so
+ * must not be interrupted by our own ISR (which would start
+ * another read operation) */
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+ return;
+ }
+
+ BUG();
+}
+
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writesl(pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+ return;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, *buf++);
+ return;
+ }
+
+ BUG();
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ readsl(pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+ return;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ return;
+ }
+
+ BUG();
+}
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes mac_lock is held */
+static int smsc911x_mac_complete(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 0;
+ }
+ SMSC_WARNING(HW, "Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return -EIO;
+}
+
+/* Fetches a MAC register value. Assumes mac_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING(HW, "MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+ MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to complete */
+ if (likely(smsc911x_mac_complete(pdata) == 0))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARNING(HW, "MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, mac_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING(HW,
+ "smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(pdata, MAC_CSR_DATA, val);
+
+ /* Write the actual data */
+ smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+ MAC_CSR_CMD_CSR_BUSY_));
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_complete(pdata) == 0))
+ return;
+
+ SMSC_WARNING(HW,
+ "smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Get a phy register */
+static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+ struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+ unsigned long flags;
+ unsigned int addr;
+ int i, reg;
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING(HW,
+ "MII is busy in smsc911x_mii_read???");
+ reg = -EIO;
+ goto out;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++)
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ reg = smsc911x_mac_read(pdata, MII_DATA);
+ goto out;
+ }
+
+ SMSC_WARNING(HW, "Timed out waiting for MII write to finish");
+ reg = -EIO;
+
+out:
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ return reg;
+}
+
+/* Set a phy register */
+static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+ u16 val)
+{
+ struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+ unsigned long flags;
+ unsigned int addr;
+ int i, reg;
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING(HW,
+ "MII is busy in smsc911x_mii_write???");
+ reg = -EIO;
+ goto out;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++)
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ reg = 0;
+ goto out;
+ }
+
+ SMSC_WARNING(HW, "Timed out waiting for MII write to finish");
+ reg = -EIO;
+
+out:
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ return reg;
+}
+
+/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
+ * If something goes wrong, returns -ENODEV to revert back to internal phy.
+ * Performed at initialisation only, so interrupts are enabled */
+static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* External phy is requested, supported, and detected */
+ if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+
+ /* Switch to external phy. Assuming tx and rx are stopped
+ * because smsc911x_phy_initialise is called before
+ * smsc911x_rx_initialise and tx_initialise. */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+
+ SMSC_TRACE(HW, "Successfully switched to external PHY");
+ pdata->using_extphy = 1;
+ } else {
+ SMSC_WARNING(HW, "No external PHY detected, "
+ "Using internal PHY instead.");
+ /* Use internal phy */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ ulong bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (u32)((ulong)pdata->loopback_tx_pkt & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_a);
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_b);
+
+ bufp = (ulong)pdata->loopback_tx_pkt & (~0x3);
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (u32)((ulong)pdata->loopback_tx_pkt & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING(HW, "Failed to transmit "
+ "during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARNING(HW, "Transmit encountered "
+ "errors during loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING(HW,
+ "Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARNING(HW, "Receive encountered "
+ "errors during loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (ulong)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += (u32)((ulong)pdata->loopback_rx_pkt & 0x3);
+ rdsz >>= 2;
+
+ smsc911x_rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARNING(HW, "Unexpected packet size "
+ "during loop back test, size=%d, will retry",
+ pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE(HW, "Successfully verified "
+ "loopback packet");
+ return 0;
+ } else {
+ SMSC_WARNING(HW, "Data mismatch "
+ "during loop back test, will retry");
+ }
+ }
+ }
+
+ return -EIO;
+}
+
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned int temp;
+ unsigned int i = 100000;
+
+ BUG_ON(!phy_dev);
+ BUG_ON(!phy_dev->bus);
+
+ SMSC_TRACE(HW, "Performing PHY BCR Reset");
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, BMCR_RESET);
+ do {
+ msleep(1);
+ temp = smsc911x_mii_read(phy_dev->bus, phy_dev->addr,
+ MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARNING(HW, "PHY reset failed to complete.");
+ return -EIO;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe */
+ msleep(1);
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ int result = -EIO;
+ unsigned int i, val;
+ unsigned long flags;
+
+ /* Initialise tx packet using broadcast destination address */
+ memset(pdata->loopback_tx_pkt, 0xff, ETH_ALEN);
+
+ /* Use incrementing source address */
+ for (i = 6; i < 12; i++)
+ pdata->loopback_tx_pkt[i] = (char)i;
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+
+ for (i = 14; i < MIN_PACKET_SIZE; i++)
+ pdata->loopback_tx_pkt[i] = (char)i;
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(pdata, HW_CFG, val);
+
+ smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+ smsc911x_reg_write(pdata, RX_CFG,
+ (u32)((ulong)pdata->loopback_rx_pkt & 0x03) << 8);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR,
+ BMCR_LOOPBACK | BMCR_FULLDPLX);
+
+ /* Enable MAC tx/rx, FD */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata) == 0) {
+ result = 0;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0);
+
+ smsc911x_reg_write(pdata, TX_CFG, 0);
+ smsc911x_reg_write(pdata, RX_CFG, 0);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ struct phy_device *phy_dev = pdata->phy_dev;
+ u32 afc = smsc911x_reg_read(pdata, AFC_CFG);
+ u32 flow;
+ unsigned long flags;
+
+ if (phy_dev->duplex == DUPLEX_FULL) {
+ u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+ u16 rmtadv = phy_read(phy_dev, MII_LPA);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+ if (cap & FLOW_CTRL_RX)
+ flow = 0xFFFF0002;
+ else
+ flow = 0;
+
+ if (cap & FLOW_CTRL_TX)
+ afc |= 0xF;
+ else
+ afc &= ~0xF;
+
+ SMSC_TRACE(HW, "rx pause %s, tx pause %s",
+ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+ (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+ } else {
+ SMSC_TRACE(HW, "half duplex");
+ flow = 0;
+ afc |= 0xF;
+ }
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, FLOW, flow);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_reg_write(pdata, AFC_CFG, afc);
+}
+
+/* Update link mode if anything has changed. Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc911x_phy_adjust_link(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned long flags;
+ int carrier;
+
+ if (phy_dev->duplex != pdata->last_duplex) {
+ unsigned int mac_cr;
+ SMSC_TRACE(HW, "duplex state has changed");
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (phy_dev->duplex) {
+ SMSC_TRACE(HW,
+ "configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE(HW,
+ "configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_phy_update_flowcontrol(pdata);
+ pdata->last_duplex = phy_dev->duplex;
+ }
+
+ carrier = netif_carrier_ok(dev);
+ if (carrier != pdata->last_carrier) {
+ SMSC_TRACE(HW, "carrier state has changed");
+ if (carrier) {
+ SMSC_TRACE(HW, "configuring for carrier OK");
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore orginal GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata, GPIO_CFG,
+ pdata->gpio_setting);
+ }
+ } else {
+ SMSC_TRACE(HW, "configuring for no carrier");
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata,
+ GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
+ && (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * orginal GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata, GPIO_CFG,
+ pdata->gpio_setting);
+ }
+ }
+ pdata->last_carrier = carrier;
+ }
+}
+
+static int smsc911x_mii_probe(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int phy_addr;
+
+ /* find the first phy */
+ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+ if (pdata->mii_bus->phy_map[phy_addr]) {
+ phydev = pdata->mii_bus->phy_map[phy_addr];
+ SMSC_TRACE(PROBE, "PHY %d: addr %d, phy_id 0x%08X",
+ phy_addr, phydev->addr, phydev->phy_id);
+ break;
+ }
+ }
+
+ if (!phydev) {
+ pr_err("%s: no PHY found\n", dev->name);
+ return -ENODEV;
+ }
+
+ phydev = phy_connect(dev, phydev->dev.bus_id,
+ &smsc911x_phy_adjust_link, 0, pdata->config.phy_interface);
+
+ if (IS_ERR(phydev)) {
+ pr_err("%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);
+
+ /* mask with MAC supported features */
+ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phydev->advertising = phydev->supported;
+
+ pdata->phy_dev = phydev;
+ pdata->last_duplex = -1;
+ pdata->last_carrier = -1;
+
+#ifdef USE_PHY_WORK_AROUND
+ if (smsc911x_phy_loopbacktest(dev) < 0) {
+ SMSC_WARNING(HW, "Failed Loop Back Test");
+ return -ENODEV;
+ }
+ SMSC_TRACE(HW, "Passed Loop Back Test");
+#endif /* USE_PHY_WORK_AROUND */
+
+ SMSC_TRACE(HW, "phy initialised succesfully");
+ return 0;
+}
+
+static int __devinit smsc911x_mii_init(struct platform_device *pdev,
+ struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ int err = -ENXIO, i;
+
+ pdata->mii_bus = mdiobus_alloc();
+ if (!pdata->mii_bus) {
+ err = -ENOMEM;
+ goto err_out_1;
+ }
+
+ pdata->mii_bus->name = SMSC_MDIONAME;
+ snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+ pdata->mii_bus->priv = pdata;
+ pdata->mii_bus->read = smsc911x_mii_read;
+ pdata->mii_bus->write = smsc911x_mii_write;
+ pdata->mii_bus->irq = pdata->phy_irq;
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ pdata->mii_bus->irq[i] = PHY_POLL;
+
+ pdata->mii_bus->parent = &pdev->dev;
+
+ pdata->using_extphy = 0;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ case 0x117A0000:
+ case 0x115A0000:
+ /* External PHY supported, try to autodetect */
+ if (smsc911x_phy_initialise_external(pdata) < 0) {
+ SMSC_TRACE(HW, "No external PHY detected, "
+ "using internal PHY");
+ }
+ break;
+ default:
+ SMSC_TRACE(HW, "External PHY is not supported, "
+ "using internal PHY");
+ break;
+ }
+
+ if (!pdata->using_extphy) {
+ /* Mask all PHYs except ID 1 (internal) */
+ pdata->mii_bus->phy_mask = ~(1 << 1);
+ }
+
+ if (mdiobus_register(pdata->mii_bus)) {
+ SMSC_WARNING(PROBE, "Error registering mii bus");
+ goto err_out_free_bus_2;
+ }
+
+ if (smsc911x_mii_probe(dev) < 0) {
+ SMSC_WARNING(PROBE, "Error registering mii bus");
+ goto err_out_unregister_bus_3;
+ }
+
+ return 0;
+
+err_out_unregister_bus_3:
+ mdiobus_unregister(pdata->mii_bus);
+err_out_free_bus_2:
+ mdiobus_free(pdata->mii_bus);
+err_out_1:
+ return err;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ return (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARNING(HW,
+ "Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & 0x00008000)) {
+ dev->stats.tx_errors++;
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & 0x00000100)) {
+ dev->stats.collisions += 16;
+ dev->stats.tx_aborted_errors += 1;
+ } else {
+ dev->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & 0x00000800))
+ dev->stats.tx_carrier_errors += 1;
+ if (unlikely(tx_stat & 0x00000200)) {
+ dev->stats.collisions++;
+ dev->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct net_device *dev, unsigned int rxstat)
+{
+ int crc_err = 0;
+
+ if (unlikely(rxstat & 0x00008000)) {
+ dev->stats.rx_errors++;
+ if (unlikely(rxstat & 0x00000002)) {
+ dev->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
+ /* Frame type indicates length,
+ * and length error is set */
+ dev->stats.rx_length_errors++;
+ }
+ if (rxstat & RX_STS_MCAST_)
+ dev->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(pdata, RX_DP_CTRL, RX_DP_CTRL_RX_FFWD_);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING(HW, "Timed out waiting for "
+ "RX FFWD to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata =
+ container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->dev;
+ int npackets = 0;
+
+ while (likely(netif_running(dev)) && (npackets < budget)) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ struct sk_buff *skb;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ if (!rxstat) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_);
+ netif_rx_complete(napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ break;
+ }
+
+ /* Count packet for NAPI scheduling, even if it has an error.
+ * Error packets still require cycles to discard */
+ npackets++;
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ smsc911x_rx_counterrors(dev, rxstat);
+
+ if (unlikely(rxstat & RX_STS_ES_)) {
+ SMSC_WARNING(RX_ERR,
+ "Discarding packet with error bit set");
+ /* Packet has an error, discard it and continue with
+ * the next */
+ smsc911x_rx_fastforward(pdata, pktwords);
+ dev->stats.rx_dropped++;
+ continue;
+ }
+
+ skb = netdev_alloc_skb(dev, pktlength + NET_IP_ALIGN);
+ if (unlikely(!skb)) {
+ SMSC_WARNING(RX_ERR,
+ "Unable to allocate skb for rx packet");
+ /* Drop the packet and stop this polling iteration */
+ smsc911x_rx_fastforward(pdata, pktwords);
+ dev->stats.rx_dropped++;
+ break;
+ }
+
+ skb->data = skb->head;
+ skb_reset_tail_pointer(skb);
+
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ smsc911x_rx_readfifo(pdata, (unsigned int *)skb->head,
+ pktwords);
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += (pktlength - 4);
+ dev->last_rx = jiffies;
+ }
+
+ /* Return total received packets */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the mac_lock held */
+ unsigned int mac_cr;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+ SMSC_TRACE(HW, "maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X",
+ mac_cr, pdata->hashhi, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->mac_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARNING(DRV, "Rx not stopped");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->mac_lock);
+}
+
+static int smsc911x_soft_reset(struct smsc911x_data *pdata)
+{
+ unsigned int timeout;
+ unsigned int temp;
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(pdata, HW_CFG, HW_CFG_SRST_);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ SMSC_WARNING(DRV, "Failed to complete reset");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* Sets the device MAC address to dev_addr, called with mac_lock held */
+static void
+smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg;
+
+ /* if the phy is not yet registered, retry later*/
+ if (!pdata->phy_dev) {
+ SMSC_WARNING(HW, "phy_dev is NULL");
+ return -EAGAIN;
+ }
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ SMSC_WARNING(HW, "dev_addr is not a valid MAC address");
+ return -EADDRNOTAVAIL;
+ }
+
+ /* Reset the LAN911x */
+ if (smsc911x_soft_reset(pdata)) {
+ SMSC_WARNING(HW, "soft reset failed");
+ return -EIO;
+ }
+
+ smsc911x_reg_write(pdata, HW_CFG, 0x00050000);
+ smsc911x_reg_write(pdata, AFC_CFG, 0x006E3740);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((timeout--) &&
+ (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING(IFUP,
+ "Timed out waiting for EEPROM busy bit to clear");
+
+ smsc911x_reg_write(pdata, GPIO_CFG, 0x70070000);
+
+ /* The soft reset above cleared the device's MAC address,
+ * restore it from local copy (set in probe) */
+ spin_lock_irq(&pdata->mac_lock);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+ smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+ /* Set interrupt deassertion to 100uS */
+ intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->config.irq_polarity) {
+ SMSC_TRACE(IFUP, "irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE(IFUP, "irq polarity: active low");
+ }
+
+ if (pdata->config.irq_type) {
+ SMSC_TRACE(IFUP, "irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE(IFUP, "irq type: open drain");
+ }
+
+ smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+ SMSC_TRACE(IFUP, "Testing irq handler using IRQ %d", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(pdata, INT_EN, temp);
+
+ timeout = 1000;
+ while (timeout--) {
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ dev_warn(&dev->dev, "ISR failed signaling test (IRQ %d)\n",
+ dev->irq);
+ return -ENODEV;
+ }
+ SMSC_TRACE(IFUP, "IRQ handler passed test using IRQ %d", dev->irq);
+
+ dev_info(&dev->dev, "SMSC911x/921x identified at %#08lx, IRQ: %d\n",
+ (unsigned long)pdata->ioaddr, dev->irq);
+
+ /* Bring the PHY up */
+ phy_start(pdata->phy_dev);
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ /* Preserve TX FIFO size and external PHY configuration */
+ temp &= (HW_CFG_TX_FIF_SZ_|0x00000FFF);
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(pdata, HW_CFG, temp);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write(pdata, RX_CFG, (2 << 8));
+
+ /* enable NAPI polling before enabling RX interrupts */
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+
+ spin_lock_irq(&pdata->mac_lock);
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int temp;
+
+ /* Disable all device interrupts */
+ temp = smsc911x_reg_read(pdata, INT_CFG);
+ temp &= ~INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(pdata, INT_CFG, temp);
+
+ /* Stop Tx and Rx polling */
+ netif_stop_queue(dev);
+ napi_disable(&pdata->napi);
+
+ /* At this point all Rx and Tx activity is stopped */
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(dev);
+
+ /* Bring the PHY down */
+ if (pdata->phy_dev)
+ phy_stop(pdata->phy_dev);
+
+ SMSC_TRACE(IFDOWN, "Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ ulong bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARNING(TX_ERR,
+ "Tx data fifo low, space available: %d", freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = (u32)((ulong)skb->data & 0x03) << 16;
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_a);
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_b);
+
+ bufp = (ulong)skb->data & (~0x3);
+ wrsz = (u32)skb->len + 3;
+ wrsz += (u32)((ulong)skb->data & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ dev_kfree_skb(skb);
+ dev->trans_start = jiffies;
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(dev);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(dev);
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &dev->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->mc_count > 0) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ unsigned int count = 0;
+ struct dev_mc_list *mc_list = dev->mc_list;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while (mc_list) {
+ count++;
+ if ((mc_list->dmi_addrlen) == ETH_ALEN) {
+ unsigned int bitnum =
+ smsc911x_hash(mc_list->dmi_addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ } else {
+ SMSC_WARNING(DRV, "dmi_addrlen != 6");
+ }
+ mc_list = mc_list->next;
+ }
+ if (count != (unsigned int)dev->mc_count)
+ SMSC_WARNING(DRV, "mc_count != dev->mc_count");
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE(HW, "scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RXSTOP_INT_EN_;
+ smsc911x_reg_write(pdata, INT_EN, temp);
+
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u32 intsts = smsc911x_reg_read(pdata, INT_STS);
+ u32 inten = smsc911x_reg_read(pdata, INT_EN);
+ int serviced = IRQ_NONE;
+ u32 temp;
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_SW_INT_);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE(INTR, "RX Stop interrupt");
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_TDFA_);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ SMSC_TRACE(INTR, "RX Error interrupt");
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RXE_);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ if (likely(netif_rx_schedule_prep(&pdata->napi))) {
+ /* Disable Rx interrupts */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ /* Schedule a NAPI poll */
+ __netif_rx_schedule(&pdata->napi);
+ } else {
+ SMSC_WARNING(RX_ERR,
+ "netif_rx_schedule_prep failed");
+ }
+ serviced = IRQ_HANDLED;
+ }
+
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ if (!netif_running(dev) || !pdata->phy_dev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(pdata->phy_dev, if_mii(ifr), cmd);
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return phy_ethtool_gset(pdata->phy_dev, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return phy_ethtool_sset(pdata->phy_dev, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, dev->dev.parent->bus_id,
+ sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return phy_start_aneg(pdata->phy_dev);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_DATA - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_DATA; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ for (i = MAC_CR; i <= WUCSR; i++) {
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ data[j++] = smsc911x_mac_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ }
+
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(pdata, GPIO_CFG, temp);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE(DRV, "op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING(DRV, "Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(pdata, E2P_CMD, e2cmd);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ SMSC_TRACE(DRV, "TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE(DRV, "Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE(DRV, "address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ SMSC_TRACE(DRV, "address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write(pdata, E2P_DATA, (u32)data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static const struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+};
+
+static const struct net_device_ops smsc911x_netdev_ops = {
+ .ndo_open = smsc911x_open,
+ .ndo_stop = smsc911x_stop,
+ .ndo_start_xmit = smsc911x_hard_start_xmit,
+ .ndo_get_stats = smsc911x_get_stats,
+ .ndo_set_multicast_list = smsc911x_set_multicast_list,
+ .ndo_do_ioctl = smsc911x_do_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smsc911x_poll_controller,
+#endif
+};
+
+/* Initializing private device structures, only called from probe */
+static int __devinit smsc911x_init(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int byte_test;
+
+ SMSC_TRACE(PROBE, "Driver Parameters:");
+ SMSC_TRACE(PROBE, "LAN base: 0x%08lX",
+ (unsigned long)pdata->ioaddr);
+ SMSC_TRACE(PROBE, "IRQ: %d", dev->irq);
+ SMSC_TRACE(PROBE, "PHY will be autodetected.");
+
+ spin_lock_init(&pdata->dev_lock);
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARNING(PROBE, "pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Check byte ordering */
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+ SMSC_TRACE(PROBE, "BYTE_TEST: 0x%08X", byte_test);
+ if (byte_test == 0x43218765) {
+ SMSC_TRACE(PROBE, "BYTE_TEST looks swapped, "
+ "applying WORD_SWAP");
+ smsc911x_reg_write(pdata, WORD_SWAP, 0xffffffff);
+
+ /* 1 dummy read of BYTE_TEST is needed after a write to
+ * WORD_SWAP before its contents are valid */
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+ }
+
+ if (byte_test != 0x87654321) {
+ SMSC_WARNING(DRV, "BYTE_TEST: 0x%08X", byte_test);
+ if (((byte_test >> 16) & 0xFFFF) == (byte_test & 0xFFFF)) {
+ SMSC_WARNING(PROBE,
+ "top 16 bits equal to bottom 16 bits");
+ SMSC_TRACE(PROBE, "This may mean the chip is set "
+ "for 32 bit while the bus is reading 16 bit");
+ }
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ case 0x01170000:
+ case 0x01160000:
+ case 0x01150000:
+ /* LAN911[5678] family */
+ pdata->generation = pdata->idrev & 0x0000FFFF;
+ break;
+
+ case 0x118A0000:
+ case 0x117A0000:
+ case 0x116A0000:
+ case 0x115A0000:
+ /* LAN921[5678] family */
+ pdata->generation = 3;
+ break;
+
+ case 0x92100000:
+ case 0x92110000:
+ case 0x92200000:
+ case 0x92210000:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARNING(PROBE, "LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ SMSC_TRACE(PROBE, "LAN911x identified, idrev: 0x%08X, generation: %d",
+ pdata->idrev, pdata->generation);
+
+ if (pdata->generation == 0)
+ SMSC_WARNING(PROBE,
+ "This driver is not intended for this chip revision");
+
+ /* Reset the LAN911x */
+ if (smsc911x_soft_reset(pdata))
+ return -ENODEV;
+
+ /* Disable all interrupt sources until we bring the device up */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+
+ ether_setup(dev);
+ dev->flags |= IFF_MULTICAST;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, SMSC_NAPI_WEIGHT);
+ dev->netdev_ops = &smsc911x_netdev_ops;
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+ return 0;
+}
+
+static int __devexit smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+ BUG_ON(!pdata->phy_dev);
+
+ SMSC_TRACE(IFDOWN, "Stopping driver.");
+
+ phy_disconnect(pdata->phy_dev);
+ pdata->phy_dev = NULL;
+ mdiobus_unregister(pdata->mii_bus);
+ mdiobus_free(pdata->mii_bus);
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, res->end - res->start);
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct smsc911x_platform_config *config = pdev->dev.platform_data;
+ struct resource *res;
+ unsigned int intcfg = 0;
+ int res_size;
+ int retval;
+ DECLARE_MAC_BUF(mac);
+
+ pr_info("%s: Driver version %s.\n", SMSC_CHIPNAME, SMSC_DRV_VERSION);
+
+ /* platform data specifies irq & dynamic bus configuration */
+ if (!pdev->dev.platform_data) {
+ pr_warning("%s: platform_data not provided\n", SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_warning("%s: Could not allocate resource.\n",
+ SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = res->end - res->start;
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ pr_warning("%s: Could not allocate device.\n", SMSC_CHIPNAME);
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+
+ dev->irq = platform_get_irq(pdev, 0);
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ /* copy config parameters across to pdata */
+ memcpy(&pdata->config, config, sizeof(pdata->config));
+
+ pdata->dev = dev;
+ pdata->msg_enable = ((1 << debug) - 1);
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARNING(PROBE,
+ "Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ retval = smsc911x_init(dev);
+ if (retval < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->config.irq_polarity == SMSC911X_IRQ_POLARITY_ACTIVE_HIGH)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ if (pdata->config.irq_type == SMSC911X_IRQ_TYPE_PUSH_PULL)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+ /* Ensure interrupts are globally disabled before connecting ISR */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+ smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler, IRQF_DISABLED,
+ SMSC_CHIPNAME, dev);
+ if (retval) {
+ SMSC_WARNING(PROBE,
+ "Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARNING(PROBE,
+ "Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE(PROBE, "Network interface: \"%s\"", dev->name);
+ }
+
+ spin_lock_init(&pdata->mac_lock);
+
+ retval = smsc911x_mii_init(pdev, dev);
+ if (retval) {
+ SMSC_WARNING(PROBE,
+ "Error %i initialising mii", retval);
+ goto out_unregister_netdev_5;
+ }
+
+ spin_lock_irq(&pdata->mac_lock);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE(PROBE, "MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE(PROBE,
+ "Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE(PROBE,
+ "MAC Address is set to random_ether_addr");
+ }
+ }
+
+ spin_unlock_irq(&pdata->mac_lock);
+
+ dev_info(&dev->dev, "MAC Address: %s\n",
+ print_mac(mac, dev->dev_addr));
+
+ return 0;
+
+out_unregister_netdev_5:
+ unregister_netdev(dev);
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, res->end - res->start);
+out_0:
+ return retval;
+}
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = smsc911x_drv_remove,
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
new file mode 100644
index 000000000000..2b76654bb958
--- /dev/null
+++ b/drivers/net/smsc911x.h
@@ -0,0 +1,390 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * This program is free software; you can redistribute 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 __SMSC911X_H__
+#define __SMSC911X_H__
+
+#define TX_FIFO_LOW_THRESHOLD ((u32)1600)
+#define SMSC911X_EEPROM_SIZE ((u32)7)
+#define USE_DEBUG 0
+
+/* This is the maximum number of packets to be received every
+ * NAPI poll */
+#define SMSC_NAPI_WEIGHT 16
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be succesfully looped back */
+#define USE_PHY_WORK_AROUND
+
+#define DPRINTK(nlevel, klevel, fmt, args...) \
+ ((void)((NETIF_MSG_##nlevel & pdata->msg_enable) && \
+ printk(KERN_##klevel "%s: %s: " fmt "\n", \
+ pdata->dev->name, __func__, ## args)))
+
+#if USE_DEBUG >= 1
+#define SMSC_WARNING(nlevel, fmt, args...) \
+ DPRINTK(nlevel, WARNING, fmt, ## args)
+#else
+#define SMSC_WARNING(nlevel, fmt, args...) \
+ ({ do {} while (0); 0; })
+#endif
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(nlevel, fmt, args...) \
+ DPRINTK(nlevel, INFO, fmt, ## args)
+#else
+#define SMSC_TRACE(nlevel, fmt, args...) \
+ ({ do {} while (0); 0; })
+#endif
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+#define SMSC_ASSERT_MAC_LOCK(pdata) \
+ WARN_ON(!spin_is_locked(&pdata->mac_lock))
+#else
+#define SMSC_ASSERT_MAC_LOCK(pdata) do {} while (0)
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_MCAST_ 0x00000400
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define WORD_SWAP 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c
new file mode 100644
index 000000000000..27e017d96966
--- /dev/null
+++ b/drivers/net/smsc9420.c
@@ -0,0 +1,1744 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008 SMSC
+ *
+ * This program is free software; you can redistribute 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/netdevice.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <linux/if_vlan.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+#include <asm/unaligned.h>
+#include "smsc9420.h"
+
+#define DRV_NAME "smsc9420"
+#define PFX DRV_NAME ": "
+#define DRV_MDIONAME "smsc9420-mdio"
+#define DRV_DESCRIPTION "SMSC LAN9420 driver"
+#define DRV_VERSION "1.01"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+struct smsc9420_dma_desc {
+ u32 status;
+ u32 length;
+ u32 buffer1;
+ u32 buffer2;
+};
+
+struct smsc9420_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
+
+struct smsc9420_pdata {
+ void __iomem *base_addr;
+ struct pci_dev *pdev;
+ struct net_device *dev;
+
+ struct smsc9420_dma_desc *rx_ring;
+ struct smsc9420_dma_desc *tx_ring;
+ struct smsc9420_ring_info *tx_buffers;
+ struct smsc9420_ring_info *rx_buffers;
+ dma_addr_t rx_dma_addr;
+ dma_addr_t tx_dma_addr;
+ int tx_ring_head, tx_ring_tail;
+ int rx_ring_head, rx_ring_tail;
+
+ spinlock_t int_lock;
+ spinlock_t phy_lock;
+
+ struct napi_struct napi;
+
+ bool software_irq_signal;
+ bool rx_csum;
+ u32 msg_enable;
+
+ struct phy_device *phy_dev;
+ struct mii_bus *mii_bus;
+ int phy_irq[PHY_MAX_ADDR];
+ int last_duplex;
+ int last_carrier;
+};
+
+static const struct pci_device_id smsc9420_id_table[] = {
+ { PCI_VENDOR_ID_9420, PCI_DEVICE_ID_9420, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, smsc9420_id_table);
+
+#define SMSC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+static uint smsc_debug;
+static uint debug = -1;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, "debug level");
+
+#define smsc_dbg(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_DEBUG PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_info(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_INFO PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_warn(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_WARNING PFX f "\n", ## a); \
+} while (0)
+
+static inline u32 smsc9420_reg_read(struct smsc9420_pdata *pd, u32 offset)
+{
+ return ioread32(pd->base_addr + offset);
+}
+
+static inline void
+smsc9420_reg_write(struct smsc9420_pdata *pd, u32 offset, u32 value)
+{
+ iowrite32(value, pd->base_addr + offset);
+}
+
+static inline void smsc9420_pci_flush_write(struct smsc9420_pdata *pd)
+{
+ /* to ensure PCI write completion, we must perform a PCI read */
+ smsc9420_reg_read(pd, ID_REV);
+}
+
+static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+ struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+ unsigned long flags;
+ u32 addr;
+ int i, reg = -EIO;
+
+ spin_lock_irqsave(&pd->phy_lock, flags);
+
+ /* confirm MII not busy */
+ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+ smsc_warn(DRV, "MII is busy???");
+ goto out;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACCESS_MII_READ_;
+ smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+ /* wait for read to complete with 50us timeout */
+ for (i = 0; i < 5; i++) {
+ if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+ MII_ACCESS_MII_BUSY_)) {
+ reg = (u16)smsc9420_reg_read(pd, MII_DATA);
+ goto out;
+ }
+ udelay(10);
+ }
+
+ smsc_warn(DRV, "MII busy timeout!");
+
+out:
+ spin_unlock_irqrestore(&pd->phy_lock, flags);
+ return reg;
+}
+
+static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+ u16 val)
+{
+ struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+ unsigned long flags;
+ u32 addr;
+ int i, reg = -EIO;
+
+ spin_lock_irqsave(&pd->phy_lock, flags);
+
+ /* confirm MII not busy */
+ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+ smsc_warn(DRV, "MII is busy???");
+ goto out;
+ }
+
+ /* put the data to write in the MAC */
+ smsc9420_reg_write(pd, MII_DATA, (u32)val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACCESS_MII_WRITE_;
+ smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+ /* wait for write to complete with 50us timeout */
+ for (i = 0; i < 5; i++) {
+ if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+ MII_ACCESS_MII_BUSY_)) {
+ reg = 0;
+ goto out;
+ }
+ udelay(10);
+ }
+
+ smsc_warn(DRV, "MII busy timeout!");
+
+out:
+ spin_unlock_irqrestore(&pd->phy_lock, flags);
+ return reg;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static u32 smsc9420_hash(u8 addr[ETH_ALEN])
+{
+ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd)
+{
+ int timeout = 100000;
+
+ BUG_ON(!pd);
+
+ if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ smsc_dbg(DRV, "smsc9420_eeprom_reload: Eeprom busy");
+ return -EIO;
+ }
+
+ smsc9420_reg_write(pd, E2P_CMD,
+ (E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_RELOAD_));
+
+ do {
+ udelay(10);
+ if (!(smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_))
+ return 0;
+ } while (timeout--);
+
+ smsc_warn(DRV, "smsc9420_eeprom_reload: Eeprom timed out");
+ return -EIO;
+}
+
+/* Standard ioctls for mii-tool */
+static int smsc9420_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ if (!netif_running(dev) || !pd->phy_dev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(pd->phy_dev, if_mii(ifr), cmd);
+}
+
+static int smsc9420_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return phy_ethtool_gset(pd->phy_dev, cmd);
+}
+
+static int smsc9420_ethtool_set_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ return phy_ethtool_sset(pd->phy_dev, cmd);
+}
+
+static void smsc9420_ethtool_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+ strcpy(drvinfo->driver, DRV_NAME);
+ strcpy(drvinfo->bus_info, pci_name(pd->pdev));
+ strcpy(drvinfo->version, DRV_VERSION);
+}
+
+static u32 smsc9420_ethtool_get_msglevel(struct net_device *netdev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+ return pd->msg_enable;
+}
+
+static void smsc9420_ethtool_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+ pd->msg_enable = data;
+}
+
+static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+ return phy_start_aneg(pd->phy_dev);
+}
+
+static int smsc9420_ethtool_getregslen(struct net_device *dev)
+{
+ /* all smsc9420 registers plus all phy registers */
+ return 0x100 + (32 * sizeof(u32));
+}
+
+static void
+smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phy_dev = pd->phy_dev;
+ unsigned int i, j = 0;
+ u32 *data = buf;
+
+ regs->version = smsc9420_reg_read(pd, ID_REV);
+ for (i = 0; i < 0x100; i += (sizeof(u32)))
+ data[j++] = smsc9420_reg_read(pd, i);
+
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
+{
+ unsigned int temp = smsc9420_reg_read(pd, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc9420_reg_write(pd, GPIO_CFG, temp);
+ msleep(1);
+}
+
+static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ smsc_dbg(HW, "op 0x%08x", op);
+ if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ smsc_warn(HW, "Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc9420_reg_write(pd, E2P_CMD, e2cmd);
+
+ do {
+ msleep(1);
+ e2cmd = smsc9420_reg_read(pd, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ smsc_info(HW, "TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ smsc_info(HW, "Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x", address);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret)
+ data[address] = smsc9420_reg_read(pd, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x, data 0x%x", address, data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc9420_reg_write(pd, E2P_DATA, (u32)data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+ }
+
+ return ret;
+}
+
+static int smsc9420_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC9420_EEPROM_SIZE;
+}
+
+static int smsc9420_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u8 eeprom_data[SMSC9420_EEPROM_SIZE];
+ int len, i;
+
+ smsc9420_eeprom_enable_access(pd);
+
+ len = min(eeprom->len, SMSC9420_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc9420_eeprom_read_location(pd, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc9420_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int ret;
+
+ smsc9420_eeprom_enable_access(pd);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static const struct ethtool_ops smsc9420_ethtool_ops = {
+ .get_settings = smsc9420_ethtool_get_settings,
+ .set_settings = smsc9420_ethtool_set_settings,
+ .get_drvinfo = smsc9420_ethtool_get_drvinfo,
+ .get_msglevel = smsc9420_ethtool_get_msglevel,
+ .set_msglevel = smsc9420_ethtool_set_msglevel,
+ .nway_reset = smsc9420_ethtool_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = smsc9420_ethtool_get_eeprom_len,
+ .get_eeprom = smsc9420_ethtool_get_eeprom,
+ .set_eeprom = smsc9420_ethtool_set_eeprom,
+ .get_regs_len = smsc9420_ethtool_getregslen,
+ .get_regs = smsc9420_ethtool_getregs,
+};
+
+/* Sets the device MAC address to dev_addr */
+static void smsc9420_set_mac_address(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u8 *dev_addr = dev->dev_addr;
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc9420_reg_write(pd, ADDRH, mac_high16);
+ smsc9420_reg_write(pd, ADDRL, mac_low32);
+}
+
+static void smsc9420_check_mac_address(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc9420_set_mac_address(dev);
+ smsc_dbg(PROBE, "MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc9420_reg_read(pd, ADDRH);
+ u32 mac_low32 = smsc9420_reg_read(pd, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ smsc_dbg(PROBE, "Mac Address is read from EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc9420_set_mac_address(dev);
+ smsc_dbg(PROBE,
+ "MAC Address is set to random_ether_addr");
+ }
+ }
+}
+
+static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
+{
+ u32 dmac_control, mac_cr, dma_intr_ena;
+ int timeOut = 1000;
+
+ /* disable TX DMAC */
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control &= (~DMAC_CONTROL_ST_);
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+
+ /* Wait max 10ms for transmit process to stop */
+ while (timeOut--) {
+ if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_TS_)
+ break;
+ udelay(10);
+ }
+
+ if (!timeOut)
+ smsc_warn(IFDOWN, "TX DMAC failed to stop");
+
+ /* ACK Tx DMAC stop bit */
+ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_TXPS_);
+
+ /* mask TX DMAC interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= ~(DMAC_INTR_ENA_TX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop MAC TX */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_TXEN_);
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_free_tx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->tx_ring);
+
+ if (!pd->tx_buffers)
+ return;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ struct sk_buff *skb = pd->tx_buffers[i].skb;
+
+ if (skb) {
+ BUG_ON(!pd->tx_buffers[i].mapping);
+ pci_unmap_single(pd->pdev, pd->tx_buffers[i].mapping,
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ }
+
+ pd->tx_ring[i].status = 0;
+ pd->tx_ring[i].length = 0;
+ pd->tx_ring[i].buffer1 = 0;
+ pd->tx_ring[i].buffer2 = 0;
+ }
+ wmb();
+
+ kfree(pd->tx_buffers);
+ pd->tx_buffers = NULL;
+
+ pd->tx_ring_head = 0;
+ pd->tx_ring_tail = 0;
+}
+
+static void smsc9420_free_rx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->rx_ring);
+
+ if (!pd->rx_buffers)
+ return;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (pd->rx_buffers[i].skb)
+ dev_kfree_skb_any(pd->rx_buffers[i].skb);
+
+ if (pd->rx_buffers[i].mapping)
+ pci_unmap_single(pd->pdev, pd->rx_buffers[i].mapping,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+
+ pd->rx_ring[i].status = 0;
+ pd->rx_ring[i].length = 0;
+ pd->rx_ring[i].buffer1 = 0;
+ pd->rx_ring[i].buffer2 = 0;
+ }
+ wmb();
+
+ kfree(pd->rx_buffers);
+ pd->rx_buffers = NULL;
+
+ pd->rx_ring_head = 0;
+ pd->rx_ring_tail = 0;
+}
+
+static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
+{
+ int timeOut = 1000;
+ u32 mac_cr, dmac_control, dma_intr_ena;
+
+ /* mask RX DMAC interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop RX MAC prior to stoping DMA */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_RXEN_);
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop RX DMAC */
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control &= (~DMAC_CONTROL_SR_);
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+ smsc9420_pci_flush_write(pd);
+
+ /* wait up to 10ms for receive to stop */
+ while (timeOut--) {
+ if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_RS_)
+ break;
+ udelay(10);
+ }
+
+ if (!timeOut)
+ smsc_warn(IFDOWN, "RX DMAC did not stop! timeout.");
+
+ /* ACK the Rx DMAC stop bit */
+ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_RXPS_);
+}
+
+static irqreturn_t smsc9420_isr(int irq, void *dev_id)
+{
+ struct smsc9420_pdata *pd = dev_id;
+ u32 int_cfg, int_sts, int_ctl;
+ irqreturn_t ret = IRQ_NONE;
+ ulong flags;
+
+ BUG_ON(!pd);
+ BUG_ON(!pd->base_addr);
+
+ int_cfg = smsc9420_reg_read(pd, INT_CFG);
+
+ /* check if it's our interrupt */
+ if ((int_cfg & (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_)) !=
+ (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_))
+ return IRQ_NONE;
+
+ int_sts = smsc9420_reg_read(pd, INT_STAT);
+
+ if (likely(INT_STAT_DMAC_INT_ & int_sts)) {
+ u32 status = smsc9420_reg_read(pd, DMAC_STATUS);
+ u32 ints_to_clear = 0;
+
+ if (status & DMAC_STS_TX_) {
+ ints_to_clear |= (DMAC_STS_TX_ | DMAC_STS_NIS_);
+ netif_wake_queue(pd->dev);
+ }
+
+ if (status & DMAC_STS_RX_) {
+ /* mask RX DMAC interrupts */
+ u32 dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_);
+ netif_rx_schedule(&pd->napi);
+ }
+
+ if (ints_to_clear)
+ smsc9420_reg_write(pd, DMAC_STATUS, ints_to_clear);
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(INT_STAT_SW_INT_ & int_sts)) {
+ /* mask software interrupt */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_ctl = smsc9420_reg_read(pd, INT_CTL);
+ int_ctl &= (~INT_CTL_SW_INT_EN_);
+ smsc9420_reg_write(pd, INT_CTL, int_ctl);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ smsc9420_reg_write(pd, INT_STAT, INT_STAT_SW_INT_);
+ pd->software_irq_signal = true;
+ smp_wmb();
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* to ensure PCI write completion, we must perform a PCI read */
+ smsc9420_pci_flush_write(pd);
+
+ return ret;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc9420_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc9420_isr(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd)
+{
+ smsc9420_reg_write(pd, BUS_MODE, BUS_MODE_SWR_);
+ smsc9420_reg_read(pd, BUS_MODE);
+ udelay(2);
+ if (smsc9420_reg_read(pd, BUS_MODE) & BUS_MODE_SWR_)
+ smsc_warn(DRV, "Software reset not cleared");
+}
+
+static int smsc9420_stop(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 int_cfg;
+ ulong flags;
+
+ BUG_ON(!pd);
+ BUG_ON(!pd->phy_dev);
+
+ /* disable master interrupt */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ netif_tx_disable(dev);
+ napi_disable(&pd->napi);
+
+ smsc9420_stop_tx(pd);
+ smsc9420_free_tx_ring(pd);
+
+ smsc9420_stop_rx(pd);
+ smsc9420_free_rx_ring(pd);
+
+ free_irq(dev->irq, pd);
+
+ smsc9420_dmac_soft_reset(pd);
+
+ phy_stop(pd->phy_dev);
+
+ phy_disconnect(pd->phy_dev);
+ pd->phy_dev = NULL;
+ mdiobus_unregister(pd->mii_bus);
+ mdiobus_free(pd->mii_bus);
+
+ return 0;
+}
+
+static void smsc9420_rx_count_stats(struct net_device *dev, u32 desc_status)
+{
+ if (unlikely(desc_status & RDES0_ERROR_SUMMARY_)) {
+ dev->stats.rx_errors++;
+ if (desc_status & RDES0_DESCRIPTOR_ERROR_)
+ dev->stats.rx_over_errors++;
+ else if (desc_status & (RDES0_FRAME_TOO_LONG_ |
+ RDES0_RUNT_FRAME_ | RDES0_COLLISION_SEEN_))
+ dev->stats.rx_frame_errors++;
+ else if (desc_status & RDES0_CRC_ERROR_)
+ dev->stats.rx_crc_errors++;
+ }
+
+ if (unlikely(desc_status & RDES0_LENGTH_ERROR_))
+ dev->stats.rx_length_errors++;
+
+ if (unlikely(!((desc_status & RDES0_LAST_DESCRIPTOR_) &&
+ (desc_status & RDES0_FIRST_DESCRIPTOR_))))
+ dev->stats.rx_length_errors++;
+
+ if (desc_status & RDES0_MULTICAST_FRAME_)
+ dev->stats.multicast++;
+}
+
+static void smsc9420_rx_handoff(struct smsc9420_pdata *pd, const int index,
+ const u32 status)
+{
+ struct net_device *dev = pd->dev;
+ struct sk_buff *skb;
+ u16 packet_length = (status & RDES0_FRAME_LENGTH_MASK_)
+ >> RDES0_FRAME_LENGTH_SHFT_;
+
+ /* remove crc from packet lendth */
+ packet_length -= 4;
+
+ if (pd->rx_csum)
+ packet_length -= 2;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += packet_length;
+
+ pci_unmap_single(pd->pdev, pd->rx_buffers[index].mapping,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ pd->rx_buffers[index].mapping = 0;
+
+ skb = pd->rx_buffers[index].skb;
+ pd->rx_buffers[index].skb = NULL;
+
+ if (pd->rx_csum) {
+ u16 hw_csum = get_unaligned_le16(skb_tail_pointer(skb) +
+ NET_IP_ALIGN + packet_length + 4);
+ put_unaligned_le16(cpu_to_le16(hw_csum), &skb->csum);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, packet_length);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_receive_skb(skb);
+ dev->last_rx = jiffies;
+}
+
+static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
+{
+ struct sk_buff *skb = netdev_alloc_skb(pd->dev, PKT_BUF_SZ);
+ dma_addr_t mapping;
+
+ BUG_ON(pd->rx_buffers[index].skb);
+ BUG_ON(pd->rx_buffers[index].mapping);
+
+ if (unlikely(!skb)) {
+ smsc_warn(RX_ERR, "Failed to allocate new skb!");
+ return -ENOMEM;
+ }
+
+ skb->dev = pd->dev;
+
+ mapping = pci_map_single(pd->pdev, skb_tail_pointer(skb),
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(pd->pdev, mapping)) {
+ dev_kfree_skb_any(skb);
+ smsc_warn(RX_ERR, "pci_map_single failed!");
+ return -ENOMEM;
+ }
+
+ pd->rx_buffers[index].skb = skb;
+ pd->rx_buffers[index].mapping = mapping;
+ pd->rx_ring[index].buffer1 = mapping + NET_IP_ALIGN;
+ pd->rx_ring[index].status = RDES0_OWN_;
+ wmb();
+
+ return 0;
+}
+
+static void smsc9420_alloc_new_rx_buffers(struct smsc9420_pdata *pd)
+{
+ while (pd->rx_ring_tail != pd->rx_ring_head) {
+ if (smsc9420_alloc_rx_buffer(pd, pd->rx_ring_tail))
+ break;
+
+ pd->rx_ring_tail = (pd->rx_ring_tail + 1) % RX_RING_SIZE;
+ }
+}
+
+static int smsc9420_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc9420_pdata *pd =
+ container_of(napi, struct smsc9420_pdata, napi);
+ struct net_device *dev = pd->dev;
+ u32 drop_frame_cnt, dma_intr_ena, status;
+ int work_done;
+
+ for (work_done = 0; work_done < budget; work_done++) {
+ rmb();
+ status = pd->rx_ring[pd->rx_ring_head].status;
+
+ /* stop if DMAC owns this dma descriptor */
+ if (status & RDES0_OWN_)
+ break;
+
+ smsc9420_rx_count_stats(dev, status);
+ smsc9420_rx_handoff(pd, pd->rx_ring_head, status);
+ pd->rx_ring_head = (pd->rx_ring_head + 1) % RX_RING_SIZE;
+ smsc9420_alloc_new_rx_buffers(pd);
+ }
+
+ drop_frame_cnt = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+ dev->stats.rx_dropped +=
+ (drop_frame_cnt & 0xFFFF) + ((drop_frame_cnt >> 17) & 0x3FF);
+
+ /* Kick RXDMA */
+ smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+ smsc9420_pci_flush_write(pd);
+
+ if (work_done < budget) {
+ netif_rx_complete(&pd->napi);
+
+ /* re-enable RX DMA interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena |= (DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+ }
+ return work_done;
+}
+
+static void
+smsc9420_tx_update_stats(struct net_device *dev, u32 status, u32 length)
+{
+ if (unlikely(status & TDES0_ERROR_SUMMARY_)) {
+ dev->stats.tx_errors++;
+ if (status & (TDES0_EXCESSIVE_DEFERRAL_ |
+ TDES0_EXCESSIVE_COLLISIONS_))
+ dev->stats.tx_aborted_errors++;
+
+ if (status & (TDES0_LOSS_OF_CARRIER_ | TDES0_NO_CARRIER_))
+ dev->stats.tx_carrier_errors++;
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += (length & 0x7FF);
+ }
+
+ if (unlikely(status & TDES0_EXCESSIVE_COLLISIONS_)) {
+ dev->stats.collisions += 16;
+ } else {
+ dev->stats.collisions +=
+ (status & TDES0_COLLISION_COUNT_MASK_) >>
+ TDES0_COLLISION_COUNT_SHFT_;
+ }
+
+ if (unlikely(status & TDES0_HEARTBEAT_FAIL_))
+ dev->stats.tx_heartbeat_errors++;
+}
+
+/* Check for completed dma transfers, update stats and free skbs */
+static void smsc9420_complete_tx(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ while (pd->tx_ring_tail != pd->tx_ring_head) {
+ int index = pd->tx_ring_tail;
+ u32 status, length;
+
+ rmb();
+ status = pd->tx_ring[index].status;
+ length = pd->tx_ring[index].length;
+
+ /* Check if DMA still owns this descriptor */
+ if (unlikely(TDES0_OWN_ & status))
+ break;
+
+ smsc9420_tx_update_stats(dev, status, length);
+
+ BUG_ON(!pd->tx_buffers[index].skb);
+ BUG_ON(!pd->tx_buffers[index].mapping);
+
+ pci_unmap_single(pd->pdev, pd->tx_buffers[index].mapping,
+ pd->tx_buffers[index].skb->len, PCI_DMA_TODEVICE);
+ pd->tx_buffers[index].mapping = 0;
+
+ dev_kfree_skb_any(pd->tx_buffers[index].skb);
+ pd->tx_buffers[index].skb = NULL;
+
+ pd->tx_ring[index].buffer1 = 0;
+ wmb();
+
+ pd->tx_ring_tail = (pd->tx_ring_tail + 1) % TX_RING_SIZE;
+ }
+}
+
+static int smsc9420_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ dma_addr_t mapping;
+ int index = pd->tx_ring_head;
+ u32 tmp_desc1;
+ bool about_to_take_last_desc =
+ (((pd->tx_ring_head + 2) % TX_RING_SIZE) == pd->tx_ring_tail);
+
+ smsc9420_complete_tx(dev);
+
+ rmb();
+ BUG_ON(pd->tx_ring[index].status & TDES0_OWN_);
+ BUG_ON(pd->tx_buffers[index].skb);
+ BUG_ON(pd->tx_buffers[index].mapping);
+
+ mapping = pci_map_single(pd->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pd->pdev, mapping)) {
+ smsc_warn(TX_ERR, "pci_map_single failed, dropping packet");
+ return NETDEV_TX_BUSY;
+ }
+
+ pd->tx_buffers[index].skb = skb;
+ pd->tx_buffers[index].mapping = mapping;
+
+ tmp_desc1 = (TDES1_LS_ | ((u32)skb->len & 0x7FF));
+ if (unlikely(about_to_take_last_desc)) {
+ tmp_desc1 |= TDES1_IC_;
+ netif_stop_queue(pd->dev);
+ }
+
+ /* check if we are at the last descriptor and need to set EOR */
+ if (unlikely(index == (TX_RING_SIZE - 1)))
+ tmp_desc1 |= TDES1_TER_;
+
+ pd->tx_ring[index].buffer1 = mapping;
+ pd->tx_ring[index].length = tmp_desc1;
+ wmb();
+
+ /* increment head */
+ pd->tx_ring_head = (pd->tx_ring_head + 1) % TX_RING_SIZE;
+
+ /* assign ownership to DMAC */
+ pd->tx_ring[index].status = TDES0_OWN_;
+ wmb();
+
+ /* kick the DMA */
+ smsc9420_reg_write(pd, TX_POLL_DEMAND, 1);
+ smsc9420_pci_flush_write(pd);
+
+ dev->trans_start = jiffies;
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *smsc9420_get_stats(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 counter = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+ dev->stats.rx_dropped +=
+ (counter & 0x0000FFFF) + ((counter >> 17) & 0x000003FF);
+ return &dev->stats;
+}
+
+static void smsc9420_set_multicast_list(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+
+ if (dev->flags & IFF_PROMISC) {
+ smsc_dbg(HW, "Promiscuous Mode Enabled");
+ mac_cr |= MAC_CR_PRMS_;
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr &= (~MAC_CR_HPFILT_);
+ } else if (dev->flags & IFF_ALLMULTI) {
+ smsc_dbg(HW, "Receive all Multicast Enabled");
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr |= MAC_CR_MCPAS_;
+ mac_cr &= (~MAC_CR_HPFILT_);
+ } else if (dev->mc_count > 0) {
+ struct dev_mc_list *mc_list = dev->mc_list;
+ u32 hash_lo = 0, hash_hi = 0;
+
+ smsc_dbg(HW, "Multicast filter enabled");
+ while (mc_list) {
+ u32 bit_num = smsc9420_hash(mc_list->dmi_addr);
+ u32 mask = 1 << (bit_num & 0x1F);
+
+ if (bit_num & 0x20)
+ hash_hi |= mask;
+ else
+ hash_lo |= mask;
+
+ mc_list = mc_list->next;
+ }
+ smsc9420_reg_write(pd, HASHH, hash_hi);
+ smsc9420_reg_write(pd, HASHL, hash_lo);
+
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr |= MAC_CR_HPFILT_;
+ } else {
+ smsc_dbg(HW, "Receive own packets only.");
+ smsc9420_reg_write(pd, HASHH, 0);
+ smsc9420_reg_write(pd, HASHL, 0);
+
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr &= (~MAC_CR_HPFILT_);
+ }
+
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
+{
+ struct phy_device *phy_dev = pd->phy_dev;
+ u32 flow;
+
+ if (phy_dev->duplex == DUPLEX_FULL) {
+ u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+ u16 rmtadv = phy_read(phy_dev, MII_LPA);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+ if (cap & FLOW_CTRL_RX)
+ flow = 0xFFFF0002;
+ else
+ flow = 0;
+
+ smsc_info(LINK, "rx pause %s, tx pause %s",
+ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+ (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+ } else {
+ smsc_info(LINK, "half duplex");
+ flow = 0;
+ }
+
+ smsc9420_reg_write(pd, FLOW, flow);
+}
+
+/* Update link mode if anything has changed. Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc9420_phy_adjust_link(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phy_dev = pd->phy_dev;
+ int carrier;
+
+ if (phy_dev->duplex != pd->last_duplex) {
+ u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+ if (phy_dev->duplex) {
+ smsc_dbg(LINK, "full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ smsc_dbg(LINK, "half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+ smsc9420_phy_update_flowcontrol(pd);
+ pd->last_duplex = phy_dev->duplex;
+ }
+
+ carrier = netif_carrier_ok(dev);
+ if (carrier != pd->last_carrier) {
+ if (carrier)
+ smsc_dbg(LINK, "carrier OK");
+ else
+ smsc_dbg(LINK, "no carrier");
+ pd->last_carrier = carrier;
+ }
+}
+
+static int smsc9420_mii_probe(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+
+ BUG_ON(pd->phy_dev);
+
+ /* Device only supports internal PHY at address 1 */
+ if (!pd->mii_bus->phy_map[1]) {
+ pr_err("%s: no PHY found at address 1\n", dev->name);
+ return -ENODEV;
+ }
+
+ phydev = pd->mii_bus->phy_map[1];
+ smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr,
+ phydev->phy_id);
+
+ phydev = phy_connect(dev, phydev->dev.bus_id,
+ &smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ pr_err("%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq);
+
+ /* mask with MAC supported features */
+ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phydev->advertising = phydev->supported;
+
+ pd->phy_dev = phydev;
+ pd->last_duplex = -1;
+ pd->last_carrier = -1;
+
+ return 0;
+}
+
+static int smsc9420_mii_init(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int err = -ENXIO, i;
+
+ pd->mii_bus = mdiobus_alloc();
+ if (!pd->mii_bus) {
+ err = -ENOMEM;
+ goto err_out_1;
+ }
+ pd->mii_bus->name = DRV_MDIONAME;
+ snprintf(pd->mii_bus->id, MII_BUS_ID_SIZE, "%x",
+ (pd->pdev->bus->number << 8) | pd->pdev->devfn);
+ pd->mii_bus->priv = pd;
+ pd->mii_bus->read = smsc9420_mii_read;
+ pd->mii_bus->write = smsc9420_mii_write;
+ pd->mii_bus->irq = pd->phy_irq;
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ pd->mii_bus->irq[i] = PHY_POLL;
+
+ /* Mask all PHYs except ID 1 (internal) */
+ pd->mii_bus->phy_mask = ~(1 << 1);
+
+ if (mdiobus_register(pd->mii_bus)) {
+ smsc_warn(PROBE, "Error registering mii bus");
+ goto err_out_free_bus_2;
+ }
+
+ if (smsc9420_mii_probe(dev) < 0) {
+ smsc_warn(PROBE, "Error probing mii bus");
+ goto err_out_unregister_bus_3;
+ }
+
+ return 0;
+
+err_out_unregister_bus_3:
+ mdiobus_unregister(pd->mii_bus);
+err_out_free_bus_2:
+ mdiobus_free(pd->mii_bus);
+err_out_1:
+ return err;
+}
+
+static int smsc9420_alloc_tx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->tx_ring);
+
+ pd->tx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+ TX_RING_SIZE), GFP_KERNEL);
+ if (!pd->tx_buffers) {
+ smsc_warn(IFUP, "Failed to allocated tx_buffers");
+ return -ENOMEM;
+ }
+
+ /* Initialize the TX Ring */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ pd->tx_buffers[i].skb = NULL;
+ pd->tx_buffers[i].mapping = 0;
+ pd->tx_ring[i].status = 0;
+ pd->tx_ring[i].length = 0;
+ pd->tx_ring[i].buffer1 = 0;
+ pd->tx_ring[i].buffer2 = 0;
+ }
+ pd->tx_ring[TX_RING_SIZE - 1].length = TDES1_TER_;
+ wmb();
+
+ pd->tx_ring_head = 0;
+ pd->tx_ring_tail = 0;
+
+ smsc9420_reg_write(pd, TX_BASE_ADDR, pd->tx_dma_addr);
+ smsc9420_pci_flush_write(pd);
+
+ return 0;
+}
+
+static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->rx_ring);
+
+ pd->rx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+ RX_RING_SIZE), GFP_KERNEL);
+ if (pd->rx_buffers == NULL) {
+ smsc_warn(IFUP, "Failed to allocated rx_buffers");
+ goto out;
+ }
+
+ /* initialize the rx ring */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ pd->rx_ring[i].status = 0;
+ pd->rx_ring[i].length = PKT_BUF_SZ;
+ pd->rx_ring[i].buffer2 = 0;
+ pd->rx_buffers[i].skb = NULL;
+ pd->rx_buffers[i].mapping = 0;
+ }
+ pd->rx_ring[RX_RING_SIZE - 1].length = (PKT_BUF_SZ | RDES1_RER_);
+
+ /* now allocate the entire ring of skbs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (smsc9420_alloc_rx_buffer(pd, i)) {
+ smsc_warn(IFUP, "failed to allocate rx skb %d", i);
+ goto out_free_rx_skbs;
+ }
+ }
+
+ pd->rx_ring_head = 0;
+ pd->rx_ring_tail = 0;
+
+ smsc9420_reg_write(pd, VLAN1, ETH_P_8021Q);
+ smsc_dbg(IFUP, "VLAN1 = 0x%08x", smsc9420_reg_read(pd, VLAN1));
+
+ if (pd->rx_csum) {
+ /* Enable RX COE */
+ u32 coe = smsc9420_reg_read(pd, COE_CR) | RX_COE_EN;
+ smsc9420_reg_write(pd, COE_CR, coe);
+ smsc_dbg(IFUP, "COE_CR = 0x%08x", coe);
+ }
+
+ smsc9420_reg_write(pd, RX_BASE_ADDR, pd->rx_dma_addr);
+ smsc9420_pci_flush_write(pd);
+
+ return 0;
+
+out_free_rx_skbs:
+ smsc9420_free_rx_ring(pd);
+out:
+ return -ENOMEM;
+}
+
+static int smsc9420_open(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd;
+ u32 bus_mode, mac_cr, dmac_control, int_cfg, dma_intr_ena, int_ctl;
+ unsigned long flags;
+ int result = 0, timeout;
+
+ BUG_ON(!dev);
+ pd = netdev_priv(dev);
+ BUG_ON(!pd);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ smsc_warn(IFUP, "dev_addr is not a valid MAC address");
+ result = -EADDRNOTAVAIL;
+ goto out_0;
+ }
+
+ netif_carrier_off(dev);
+
+ /* disable, mask and acknowlege all interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ smsc9420_reg_write(pd, INT_CTL, 0);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, 0);
+ smsc9420_reg_write(pd, INT_STAT, 0xFFFFFFFF);
+ smsc9420_pci_flush_write(pd);
+
+ if (request_irq(dev->irq, smsc9420_isr, IRQF_SHARED | IRQF_DISABLED,
+ DRV_NAME, pd)) {
+ smsc_warn(IFUP, "Unable to use IRQ = %d", dev->irq);
+ result = -ENODEV;
+ goto out_0;
+ }
+
+ smsc9420_dmac_soft_reset(pd);
+
+ /* make sure MAC_CR is sane */
+ smsc9420_reg_write(pd, MAC_CR, 0);
+
+ smsc9420_set_mac_address(dev);
+
+ /* Configure GPIO pins to drive LEDs */
+ smsc9420_reg_write(pd, GPIO_CFG,
+ (GPIO_CFG_LED_3_ | GPIO_CFG_LED_2_ | GPIO_CFG_LED_1_));
+
+ bus_mode = BUS_MODE_DMA_BURST_LENGTH_16;
+
+#ifdef __BIG_ENDIAN
+ bus_mode |= BUS_MODE_DBO_;
+#endif
+
+ smsc9420_reg_write(pd, BUS_MODE, bus_mode);
+
+ smsc9420_pci_flush_write(pd);
+
+ /* set bus master bridge arbitration priority for Rx and TX DMA */
+ smsc9420_reg_write(pd, BUS_CFG, BUS_CFG_RXTXWEIGHT_4_1);
+
+ smsc9420_reg_write(pd, DMAC_CONTROL,
+ (DMAC_CONTROL_SF_ | DMAC_CONTROL_OSF_));
+
+ smsc9420_pci_flush_write(pd);
+
+ /* test the IRQ connection to the ISR */
+ smsc_dbg(IFUP, "Testing ISR using IRQ %d", dev->irq);
+
+ spin_lock_irqsave(&pd->int_lock, flags);
+ /* configure interrupt deassertion timer and enable interrupts */
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+ int_cfg &= ~(INT_CFG_INT_DEAS_MASK);
+ int_cfg |= (INT_DEAS_TIME & INT_CFG_INT_DEAS_MASK);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+
+ /* unmask software interrupt */
+ int_ctl = smsc9420_reg_read(pd, INT_CTL) | INT_CTL_SW_INT_EN_;
+ smsc9420_reg_write(pd, INT_CTL, int_ctl);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+ smsc9420_pci_flush_write(pd);
+
+ timeout = 1000;
+ pd->software_irq_signal = false;
+ smp_wmb();
+ while (timeout--) {
+ if (pd->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ /* disable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ if (!pd->software_irq_signal) {
+ smsc_warn(IFUP, "ISR failed signaling test");
+ result = -ENODEV;
+ goto out_free_irq_1;
+ }
+
+ smsc_dbg(IFUP, "ISR passed test using IRQ %d", dev->irq);
+
+ result = smsc9420_alloc_tx_ring(pd);
+ if (result) {
+ smsc_warn(IFUP, "Failed to Initialize tx dma ring");
+ result = -ENOMEM;
+ goto out_free_irq_1;
+ }
+
+ result = smsc9420_alloc_rx_ring(pd);
+ if (result) {
+ smsc_warn(IFUP, "Failed to Initialize rx dma ring");
+ result = -ENOMEM;
+ goto out_free_tx_ring_2;
+ }
+
+ result = smsc9420_mii_init(dev);
+ if (result) {
+ smsc_warn(IFUP, "Failed to initialize Phy");
+ result = -ENODEV;
+ goto out_free_rx_ring_3;
+ }
+
+ /* Bring the PHY up */
+ phy_start(pd->phy_dev);
+
+ napi_enable(&pd->napi);
+
+ /* start tx and rx */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) | MAC_CR_TXEN_ | MAC_CR_RXEN_;
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control |= DMAC_CONTROL_ST_ | DMAC_CONTROL_SR_;
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+ smsc9420_pci_flush_write(pd);
+
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena |=
+ (DMAC_INTR_ENA_TX_ | DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ netif_wake_queue(dev);
+
+ smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+
+ /* enable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ return 0;
+
+out_free_rx_ring_3:
+ smsc9420_free_rx_ring(pd);
+out_free_tx_ring_2:
+ smsc9420_free_tx_ring(pd);
+out_free_irq_1:
+ free_irq(dev->irq, pd);
+out_0:
+ return result;
+}
+
+#ifdef CONFIG_PM
+
+static int smsc9420_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 int_cfg;
+ ulong flags;
+
+ /* disable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ if (netif_running(dev)) {
+ netif_tx_disable(dev);
+ smsc9420_stop_tx(pd);
+ smsc9420_free_tx_ring(pd);
+
+ napi_disable(&pd->napi);
+ smsc9420_stop_rx(pd);
+ smsc9420_free_rx_ring(pd);
+
+ free_irq(dev->irq, pd);
+
+ netif_device_detach(dev);
+ }
+
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int smsc9420_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ err = pci_enable_wake(pdev, 0, 0);
+ if (err)
+ smsc_warn(IFUP, "pci_enable_wake failed: %d", err);
+
+ if (netif_running(dev)) {
+ err = smsc9420_open(dev);
+ netif_device_attach(dev);
+ }
+ return err;
+}
+
+#endif /* CONFIG_PM */
+
+static const struct net_device_ops smsc9420_netdev_ops = {
+ .ndo_open = smsc9420_open,
+ .ndo_stop = smsc9420_stop,
+ .ndo_start_xmit = smsc9420_hard_start_xmit,
+ .ndo_get_stats = smsc9420_get_stats,
+ .ndo_set_multicast_list = smsc9420_set_multicast_list,
+ .ndo_do_ioctl = smsc9420_do_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smsc9420_poll_controller,
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+};
+
+static int __devinit
+smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net_device *dev;
+ struct smsc9420_pdata *pd;
+ void __iomem *virt_addr;
+ int result = 0;
+ u32 id_rev;
+
+ printk(KERN_INFO DRV_DESCRIPTION " version " DRV_VERSION "\n");
+
+ /* First do the PCI initialisation */
+ result = pci_enable_device(pdev);
+ if (unlikely(result)) {
+ printk(KERN_ERR "Cannot enable smsc9420\n");
+ goto out_0;
+ }
+
+ pci_set_master(pdev);
+
+ dev = alloc_etherdev(sizeof(*pd));
+ if (!dev) {
+ printk(KERN_ERR "ether device alloc failed\n");
+ goto out_disable_pci_device_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ if (!(pci_resource_flags(pdev, SMSC_BAR) & IORESOURCE_MEM)) {
+ printk(KERN_ERR "Cannot find PCI device base address\n");
+ goto out_free_netdev_2;
+ }
+
+ if ((pci_request_regions(pdev, DRV_NAME))) {
+ printk(KERN_ERR "Cannot obtain PCI resources, aborting.\n");
+ goto out_free_netdev_2;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_ERR "No usable DMA configuration, aborting.\n");
+ goto out_free_regions_3;
+ }
+
+ virt_addr = ioremap(pci_resource_start(pdev, SMSC_BAR),
+ pci_resource_len(pdev, SMSC_BAR));
+ if (!virt_addr) {
+ printk(KERN_ERR "Cannot map device registers, aborting.\n");
+ goto out_free_regions_3;
+ }
+
+ /* registers are double mapped with 0 offset for LE and 0x200 for BE */
+ virt_addr += LAN9420_CPSR_ENDIAN_OFFSET;
+
+ dev->base_addr = (ulong)virt_addr;
+
+ pd = netdev_priv(dev);
+
+ /* pci descriptors are created in the PCI consistent area */
+ pd->rx_ring = pci_alloc_consistent(pdev,
+ sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE +
+ sizeof(struct smsc9420_dma_desc) * TX_RING_SIZE,
+ &pd->rx_dma_addr);
+
+ if (!pd->rx_ring)
+ goto out_free_io_4;
+
+ /* descriptors are aligned due to the nature of pci_alloc_consistent */
+ pd->tx_ring = (struct smsc9420_dma_desc *)
+ (pd->rx_ring + RX_RING_SIZE);
+ pd->tx_dma_addr = pd->rx_dma_addr +
+ sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE;
+
+ pd->pdev = pdev;
+ pd->dev = dev;
+ pd->base_addr = virt_addr;
+ pd->msg_enable = smsc_debug;
+ pd->rx_csum = true;
+
+ smsc_dbg(PROBE, "lan_base=0x%08lx", (ulong)virt_addr);
+
+ id_rev = smsc9420_reg_read(pd, ID_REV);
+ switch (id_rev & 0xFFFF0000) {
+ case 0x94200000:
+ smsc_info(PROBE, "LAN9420 identified, ID_REV=0x%08X", id_rev);
+ break;
+ default:
+ smsc_warn(PROBE, "LAN9420 NOT identified");
+ smsc_warn(PROBE, "ID_REV=0x%08X", id_rev);
+ goto out_free_dmadesc_5;
+ }
+
+ smsc9420_dmac_soft_reset(pd);
+ smsc9420_eeprom_reload(pd);
+ smsc9420_check_mac_address(dev);
+
+ dev->netdev_ops = &smsc9420_netdev_ops;
+ dev->ethtool_ops = &smsc9420_ethtool_ops;
+ dev->irq = pdev->irq;
+
+ netif_napi_add(dev, &pd->napi, smsc9420_rx_poll, NAPI_WEIGHT);
+
+ result = register_netdev(dev);
+ if (result) {
+ smsc_warn(PROBE, "error %i registering device", result);
+ goto out_free_dmadesc_5;
+ }
+
+ pci_set_drvdata(pdev, dev);
+
+ spin_lock_init(&pd->int_lock);
+ spin_lock_init(&pd->phy_lock);
+
+ dev_info(&dev->dev, "MAC Address: %pM\n", dev->dev_addr);
+
+ return 0;
+
+out_free_dmadesc_5:
+ pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+ (RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+out_free_io_4:
+ iounmap(virt_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+out_free_regions_3:
+ pci_release_regions(pdev);
+out_free_netdev_2:
+ free_netdev(dev);
+out_disable_pci_device_1:
+ pci_disable_device(pdev);
+out_0:
+ return -ENODEV;
+}
+
+static void __devexit smsc9420_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev;
+ struct smsc9420_pdata *pd;
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return;
+
+ pci_set_drvdata(pdev, NULL);
+
+ pd = netdev_priv(dev);
+ unregister_netdev(dev);
+
+ /* tx_buffers and rx_buffers are freed in stop */
+ BUG_ON(pd->tx_buffers);
+ BUG_ON(pd->rx_buffers);
+
+ BUG_ON(!pd->tx_ring);
+ BUG_ON(!pd->rx_ring);
+
+ pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+ (RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+
+ iounmap(pd->base_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+ pci_release_regions(pdev);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver smsc9420_driver = {
+ .name = DRV_NAME,
+ .id_table = smsc9420_id_table,
+ .probe = smsc9420_probe,
+ .remove = __devexit_p(smsc9420_remove),
+#ifdef CONFIG_PM
+ .suspend = smsc9420_suspend,
+ .resume = smsc9420_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init smsc9420_init_module(void)
+{
+ smsc_debug = netif_msg_init(debug, SMSC_MSG_DEFAULT);
+
+ return pci_register_driver(&smsc9420_driver);
+}
+
+static void __exit smsc9420_exit_module(void)
+{
+ pci_unregister_driver(&smsc9420_driver);
+}
+
+module_init(smsc9420_init_module);
+module_exit(smsc9420_exit_module);
diff --git a/drivers/net/smsc9420.h b/drivers/net/smsc9420.h
new file mode 100644
index 000000000000..69c351f93f86
--- /dev/null
+++ b/drivers/net/smsc9420.h
@@ -0,0 +1,275 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008 SMSC
+ *
+ * This program is free software; you can redistribute 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 _SMSC9420_H
+#define _SMSC9420_H
+
+#define TX_RING_SIZE (32)
+#define RX_RING_SIZE (128)
+
+/* interrupt deassertion in multiples of 10us */
+#define INT_DEAS_TIME (50)
+
+#define NAPI_WEIGHT (64)
+#define SMSC_BAR (3)
+
+#ifdef __BIG_ENDIAN
+/* Register set is duplicated for BE at an offset of 0x200 */
+#define LAN9420_CPSR_ENDIAN_OFFSET (0x200)
+#else
+#define LAN9420_CPSR_ENDIAN_OFFSET (0)
+#endif
+
+#define PCI_VENDOR_ID_9420 (0x1055)
+#define PCI_DEVICE_ID_9420 (0xE420)
+
+#define LAN_REGISTER_EXTENT (0x400)
+
+#define SMSC9420_EEPROM_SIZE ((u32)11)
+
+#define PKT_BUF_SZ (VLAN_ETH_FRAME_LEN + NET_IP_ALIGN + 4)
+
+/***********************************************/
+/* DMA Controller Control and Status Registers */
+/***********************************************/
+#define BUS_MODE (0x00)
+#define BUS_MODE_SWR_ (BIT(0))
+#define BUS_MODE_DMA_BURST_LENGTH_1 (BIT(8))
+#define BUS_MODE_DMA_BURST_LENGTH_2 (BIT(9))
+#define BUS_MODE_DMA_BURST_LENGTH_4 (BIT(10))
+#define BUS_MODE_DMA_BURST_LENGTH_8 (BIT(11))
+#define BUS_MODE_DMA_BURST_LENGTH_16 (BIT(12))
+#define BUS_MODE_DMA_BURST_LENGTH_32 (BIT(13))
+#define BUS_MODE_DBO_ (BIT(20))
+
+#define TX_POLL_DEMAND (0x04)
+
+#define RX_POLL_DEMAND (0x08)
+
+#define RX_BASE_ADDR (0x0C)
+
+#define TX_BASE_ADDR (0x10)
+
+#define DMAC_STATUS (0x14)
+#define DMAC_STS_TS_ (7 << 20)
+#define DMAC_STS_RS_ (7 << 17)
+#define DMAC_STS_NIS_ (BIT(16))
+#define DMAC_STS_AIS_ (BIT(15))
+#define DMAC_STS_RWT_ (BIT(9))
+#define DMAC_STS_RXPS_ (BIT(8))
+#define DMAC_STS_RXBU_ (BIT(7))
+#define DMAC_STS_RX_ (BIT(6))
+#define DMAC_STS_TXUNF_ (BIT(5))
+#define DMAC_STS_TXBU_ (BIT(2))
+#define DMAC_STS_TXPS_ (BIT(1))
+#define DMAC_STS_TX_ (BIT(0))
+
+#define DMAC_CONTROL (0x18)
+#define DMAC_CONTROL_TTM_ (BIT(22))
+#define DMAC_CONTROL_SF_ (BIT(21))
+#define DMAC_CONTROL_ST_ (BIT(13))
+#define DMAC_CONTROL_OSF_ (BIT(2))
+#define DMAC_CONTROL_SR_ (BIT(1))
+
+#define DMAC_INTR_ENA (0x1C)
+#define DMAC_INTR_ENA_NIS_ (BIT(16))
+#define DMAC_INTR_ENA_AIS_ (BIT(15))
+#define DMAC_INTR_ENA_RWT_ (BIT(9))
+#define DMAC_INTR_ENA_RXPS_ (BIT(8))
+#define DMAC_INTR_ENA_RXBU_ (BIT(7))
+#define DMAC_INTR_ENA_RX_ (BIT(6))
+#define DMAC_INTR_ENA_TXBU_ (BIT(2))
+#define DMAC_INTR_ENA_TXPS_ (BIT(1))
+#define DMAC_INTR_ENA_TX_ (BIT(0))
+
+#define MISS_FRAME_CNTR (0x20)
+
+#define TX_BUFF_ADDR (0x50)
+
+#define RX_BUFF_ADDR (0x54)
+
+/* Transmit Descriptor Bit Defs */
+#define TDES0_OWN_ (0x80000000)
+#define TDES0_ERROR_SUMMARY_ (0x00008000)
+#define TDES0_LOSS_OF_CARRIER_ (0x00000800)
+#define TDES0_NO_CARRIER_ (0x00000400)
+#define TDES0_LATE_COLLISION_ (0x00000200)
+#define TDES0_EXCESSIVE_COLLISIONS_ (0x00000100)
+#define TDES0_HEARTBEAT_FAIL_ (0x00000080)
+#define TDES0_COLLISION_COUNT_MASK_ (0x00000078)
+#define TDES0_COLLISION_COUNT_SHFT_ (3)
+#define TDES0_EXCESSIVE_DEFERRAL_ (0x00000004)
+#define TDES0_DEFERRED_ (0x00000001)
+
+#define TDES1_IC_ 0x80000000
+#define TDES1_LS_ 0x40000000
+#define TDES1_FS_ 0x20000000
+#define TDES1_TXCSEN_ 0x08000000
+#define TDES1_TER_ (BIT(25))
+#define TDES1_TCH_ 0x01000000
+
+/* Receive Descriptor 0 Bit Defs */
+#define RDES0_OWN_ (0x80000000)
+#define RDES0_FRAME_LENGTH_MASK_ (0x07FF0000)
+#define RDES0_FRAME_LENGTH_SHFT_ (16)
+#define RDES0_ERROR_SUMMARY_ (0x00008000)
+#define RDES0_DESCRIPTOR_ERROR_ (0x00004000)
+#define RDES0_LENGTH_ERROR_ (0x00001000)
+#define RDES0_RUNT_FRAME_ (0x00000800)
+#define RDES0_MULTICAST_FRAME_ (0x00000400)
+#define RDES0_FIRST_DESCRIPTOR_ (0x00000200)
+#define RDES0_LAST_DESCRIPTOR_ (0x00000100)
+#define RDES0_FRAME_TOO_LONG_ (0x00000080)
+#define RDES0_COLLISION_SEEN_ (0x00000040)
+#define RDES0_FRAME_TYPE_ (0x00000020)
+#define RDES0_WATCHDOG_TIMEOUT_ (0x00000010)
+#define RDES0_MII_ERROR_ (0x00000008)
+#define RDES0_DRIBBLING_BIT_ (0x00000004)
+#define RDES0_CRC_ERROR_ (0x00000002)
+
+/* Receive Descriptor 1 Bit Defs */
+#define RDES1_RER_ (0x02000000)
+
+/***********************************************/
+/* MAC Control and Status Registers */
+/***********************************************/
+#define MAC_CR (0x80)
+#define MAC_CR_RXALL_ (0x80000000)
+#define MAC_CR_DIS_RXOWN_ (0x00800000)
+#define MAC_CR_LOOPBK_ (0x00200000)
+#define MAC_CR_FDPX_ (0x00100000)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_INVFILT_ (0x00020000)
+#define MAC_CR_PASSBAD_ (0x00010000)
+#define MAC_CR_HFILT_ (0x00008000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_LCOLL_ (0x00001000)
+#define MAC_CR_DIS_BCAST_ (0x00000800)
+#define MAC_CR_DIS_RTRY_ (0x00000400)
+#define MAC_CR_PADSTR_ (0x00000100)
+#define MAC_CR_BOLMT_MSK (0x000000C0)
+#define MAC_CR_MFCHK_ (0x00000020)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x84)
+
+#define ADDRL (0x88)
+
+#define HASHH (0x8C)
+
+#define HASHL (0x90)
+
+#define MII_ACCESS (0x94)
+#define MII_ACCESS_MII_BUSY_ (0x00000001)
+#define MII_ACCESS_MII_WRITE_ (0x00000002)
+#define MII_ACCESS_MII_READ_ (0x00000000)
+#define MII_ACCESS_INDX_MSK_ (0x000007C0)
+#define MII_ACCESS_PHYADDR_MSK_ (0x0000F8C0)
+#define MII_ACCESS_INDX_SHFT_CNT (6)
+#define MII_ACCESS_PHYADDR_SHFT_CNT (11)
+
+#define MII_DATA (0x98)
+
+#define FLOW (0x9C)
+
+#define VLAN1 (0xA0)
+
+#define VLAN2 (0xA4)
+
+#define WUFF (0xA8)
+
+#define WUCSR (0xAC)
+
+#define COE_CR (0xB0)
+#define TX_COE_EN (0x00010000)
+#define RX_COE_MODE (0x00000002)
+#define RX_COE_EN (0x00000001)
+
+/***********************************************/
+/* System Control and Status Registers */
+/***********************************************/
+#define ID_REV (0xC0)
+
+#define INT_CTL (0xC4)
+#define INT_CTL_SW_INT_EN_ (0x00008000)
+#define INT_CTL_SBERR_INT_EN_ (1 << 12)
+#define INT_CTL_MBERR_INT_EN_ (1 << 13)
+#define INT_CTL_GPT_INT_EN_ (0x00000008)
+#define INT_CTL_PHY_INT_EN_ (0x00000004)
+#define INT_CTL_WAKE_INT_EN_ (0x00000002)
+
+#define INT_STAT (0xC8)
+#define INT_STAT_SW_INT_ (1 << 15)
+#define INT_STAT_MBERR_INT_ (1 << 13)
+#define INT_STAT_SBERR_INT_ (1 << 12)
+#define INT_STAT_GPT_INT_ (1 << 3)
+#define INT_STAT_PHY_INT_ (0x00000004)
+#define INT_STAT_WAKE_INT_ (0x00000002)
+#define INT_STAT_DMAC_INT_ (0x00000001)
+
+#define INT_CFG (0xCC)
+#define INT_CFG_IRQ_INT_ (0x00080000)
+#define INT_CFG_IRQ_EN_ (0x00040000)
+#define INT_CFG_INT_DEAS_CLR_ (0x00000200)
+#define INT_CFG_INT_DEAS_MASK (0x000000FF)
+
+#define GPIO_CFG (0xD0)
+#define GPIO_CFG_LED_3_ (0x40000000)
+#define GPIO_CFG_LED_2_ (0x20000000)
+#define GPIO_CFG_LED_1_ (0x10000000)
+#define GPIO_CFG_EEPR_EN_ (0x00700000)
+
+#define GPT_CFG (0xD4)
+#define GPT_CFG_TIMER_EN_ (0x20000000)
+
+#define GPT_CNT (0xD8)
+
+#define BUS_CFG (0xDC)
+#define BUS_CFG_RXTXWEIGHT_1_1 (0 << 25)
+#define BUS_CFG_RXTXWEIGHT_2_1 (1 << 25)
+#define BUS_CFG_RXTXWEIGHT_3_1 (2 << 25)
+#define BUS_CFG_RXTXWEIGHT_4_1 (3 << 25)
+
+#define PMT_CTRL (0xE0)
+
+#define FREE_RUN (0xF4)
+
+#define E2P_CMD (0xF8)
+#define E2P_CMD_EPC_BUSY_ (0x80000000)
+#define E2P_CMD_EPC_CMD_ (0x70000000)
+#define E2P_CMD_EPC_CMD_READ_ (0x00000000)
+#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000)
+#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000)
+#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000)
+#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000)
+#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000)
+#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000)
+#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000)
+#define E2P_CMD_EPC_TIMEOUT_ (0x00000200)
+#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100)
+#define E2P_CMD_EPC_ADDR_ (0x000000FF)
+
+#define E2P_DATA (0xFC)
+#define E2P_DATA_EEPROM_DATA_ (0x000000FF)
+
+#endif /* _SMSC9420_H */
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
index 8069f3e32d83..211e805c1223 100644
--- a/drivers/net/sonic.c
+++ b/drivers/net/sonic.c
@@ -450,7 +450,6 @@ static void sonic_rx(struct net_device *dev)
skb_trim(used_skb, pkt_len);
used_skb->protocol = eth_type_trans(used_skb, dev);
netif_rx(used_skb);
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
diff --git a/drivers/net/sonic.h b/drivers/net/sonic.h
index 7db13e4a7ea5..07091dd27e5d 100644
--- a/drivers/net/sonic.h
+++ b/drivers/net/sonic.h
@@ -371,7 +371,7 @@ static inline __u16 sonic_buf_get(void* base, int bitmode,
static inline void sonic_cda_put(struct net_device* dev, int entry,
int offset, __u16 val)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
sonic_buf_put(lp->cda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_CD) + offset, val);
}
@@ -379,27 +379,27 @@ static inline void sonic_cda_put(struct net_device* dev, int entry,
static inline __u16 sonic_cda_get(struct net_device* dev, int entry,
int offset)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
return sonic_buf_get(lp->cda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_CD) + offset);
}
static inline void sonic_set_cam_enable(struct net_device* dev, __u16 val)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
sonic_buf_put(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE, val);
}
static inline __u16 sonic_get_cam_enable(struct net_device* dev)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
return sonic_buf_get(lp->cda, lp->dma_bitmode, SONIC_CDA_CAM_ENABLE);
}
static inline void sonic_tda_put(struct net_device* dev, int entry,
int offset, __u16 val)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
sonic_buf_put(lp->tda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_TD) + offset, val);
}
@@ -407,7 +407,7 @@ static inline void sonic_tda_put(struct net_device* dev, int entry,
static inline __u16 sonic_tda_get(struct net_device* dev, int entry,
int offset)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
return sonic_buf_get(lp->tda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_TD) + offset);
}
@@ -415,7 +415,7 @@ static inline __u16 sonic_tda_get(struct net_device* dev, int entry,
static inline void sonic_rda_put(struct net_device* dev, int entry,
int offset, __u16 val)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
sonic_buf_put(lp->rda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_RD) + offset, val);
}
@@ -423,7 +423,7 @@ static inline void sonic_rda_put(struct net_device* dev, int entry,
static inline __u16 sonic_rda_get(struct net_device* dev, int entry,
int offset)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
return sonic_buf_get(lp->rda, lp->dma_bitmode,
(entry * SIZEOF_SONIC_RD) + offset);
}
@@ -431,7 +431,7 @@ static inline __u16 sonic_rda_get(struct net_device* dev, int entry,
static inline void sonic_rra_put(struct net_device* dev, int entry,
int offset, __u16 val)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
sonic_buf_put(lp->rra, lp->dma_bitmode,
(entry * SIZEOF_SONIC_RR) + offset, val);
}
@@ -439,7 +439,7 @@ static inline void sonic_rra_put(struct net_device* dev, int entry,
static inline __u16 sonic_rra_get(struct net_device* dev, int entry,
int offset)
{
- struct sonic_local* lp = (struct sonic_local *) dev->priv;
+ struct sonic_local *lp = netdev_priv(dev);
return sonic_buf_get(lp->rra, lp->dma_bitmode,
(entry * SIZEOF_SONIC_RR) + offset);
}
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index 07599b492359..88d2c67788df 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -789,7 +789,7 @@ spider_net_set_low_watermark(struct spider_net_card *card)
* spider_net_release_tx_chain releases the tx descriptors that spider has
* finished with (if non-brutal) or simply release tx descriptors (if brutal).
* If some other context is calling this function, we return 1 so that we're
- * scheduled again (if we were scheduled) and will not loose initiative.
+ * scheduled again (if we were scheduled) and will not lose initiative.
*/
static int
spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
@@ -1277,7 +1277,6 @@ bad_desc:
static int spider_net_poll(struct napi_struct *napi, int budget)
{
struct spider_net_card *card = container_of(napi, struct spider_net_card, napi);
- struct net_device *netdev = card->netdev;
int packets_done = 0;
while (packets_done < budget) {
@@ -1302,7 +1301,7 @@ static int spider_net_poll(struct napi_struct *napi, int budget)
/* if all packets are in the stack, enable interrupts and return 0 */
/* if not, return 1 */
if (packets_done < budget) {
- netif_rx_complete(netdev, napi);
+ netif_rx_complete(napi);
spider_net_rx_irq_on(card);
card->ignore_rx_ramfull = 0;
}
@@ -1529,8 +1528,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg,
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
- netif_rx_schedule(card->netdev,
- &card->napi);
+ netif_rx_schedule(&card->napi);
}
show_error = 0;
break;
@@ -1550,8 +1548,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg,
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
- netif_rx_schedule(card->netdev,
- &card->napi);
+ netif_rx_schedule(&card->napi);
show_error = 0;
break;
@@ -1565,8 +1562,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg,
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
- netif_rx_schedule(card->netdev,
- &card->napi);
+ netif_rx_schedule(&card->napi);
show_error = 0;
break;
@@ -1660,11 +1656,11 @@ spider_net_interrupt(int irq, void *ptr)
if (status_reg & SPIDER_NET_RXINT ) {
spider_net_rx_irq_off(card);
- netif_rx_schedule(netdev, &card->napi);
+ netif_rx_schedule(&card->napi);
card->num_rx_ints ++;
}
if (status_reg & SPIDER_NET_TXINT)
- netif_rx_schedule(netdev, &card->napi);
+ netif_rx_schedule(&card->napi);
if (status_reg & SPIDER_NET_LINKINT)
spider_net_link_reset(netdev);
diff --git a/drivers/net/spider_net_ethtool.c b/drivers/net/spider_net_ethtool.c
index 85691d2a0be2..5bae728c3820 100644
--- a/drivers/net/spider_net_ethtool.c
+++ b/drivers/net/spider_net_ethtool.c
@@ -118,7 +118,7 @@ spider_net_ethtool_nway_reset(struct net_device *netdev)
static u32
spider_net_ethtool_get_rx_csum(struct net_device *netdev)
{
- struct spider_net_card *card = netdev->priv;
+ struct spider_net_card *card = netdev_priv(netdev);
return card->options.rx_csum;
}
@@ -126,7 +126,7 @@ spider_net_ethtool_get_rx_csum(struct net_device *netdev)
static int
spider_net_ethtool_set_rx_csum(struct net_device *netdev, u32 n)
{
- struct spider_net_card *card = netdev->priv;
+ struct spider_net_card *card = netdev_priv(netdev);
card->options.rx_csum = n;
return 0;
@@ -137,7 +137,7 @@ static void
spider_net_ethtool_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ering)
{
- struct spider_net_card *card = netdev->priv;
+ struct spider_net_card *card = netdev_priv(netdev);
ering->tx_max_pending = SPIDER_NET_TX_DESCRIPTORS_MAX;
ering->tx_pending = card->tx_chain.num_desc;
@@ -158,7 +158,7 @@ static int spider_net_get_sset_count(struct net_device *netdev, int sset)
static void spider_net_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
- struct spider_net_card *card = netdev->priv;
+ struct spider_net_card *card = netdev_priv(netdev);
data[0] = netdev->stats.tx_packets;
data[1] = netdev->stats.tx_bytes;
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index 1d2ef8f47780..f54ac2389da2 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -653,7 +653,6 @@ static int __devinit starfire_init_one(struct pci_dev *pdev,
void __iomem *base;
int drv_flags, io_size;
int boguscnt;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -823,9 +822,9 @@ static int __devinit starfire_init_one(struct pci_dev *pdev,
if (register_netdev(dev))
goto err_out_cleardev;
- printk(KERN_INFO "%s: %s at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
dev->name, netdrv_tbl[chip_idx].name, base,
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
if (drv_flags & CanHaveMII) {
int phy, phy_idx = 0;
@@ -881,9 +880,9 @@ static int mdio_read(struct net_device *dev, int phy_id, int location)
void __iomem *mdio_addr = np->base + MIICtrl + (phy_id<<7) + (location<<2);
int result, boguscnt=1000;
/* ??? Should we add a busy-wait here? */
- do
+ do {
result = readl(mdio_addr);
- while ((result & 0xC0000000) != 0x80000000 && --boguscnt > 0);
+ } while ((result & 0xC0000000) != 0x80000000 && --boguscnt > 0);
if (boguscnt == 0)
return 0;
if ((result & 0xffff) == 0xffff)
@@ -1291,8 +1290,8 @@ static irqreturn_t intr_handler(int irq, void *dev_instance)
if (intr_status & (IntrRxDone | IntrRxEmpty)) {
u32 enable;
- if (likely(netif_rx_schedule_prep(dev, &np->napi))) {
- __netif_rx_schedule(dev, &np->napi);
+ if (likely(netif_rx_schedule_prep(&np->napi))) {
+ __netif_rx_schedule(&np->napi);
enable = readl(ioaddr + IntrEnable);
enable &= ~(IntrRxDone | IntrRxEmpty);
writel(enable, ioaddr + IntrEnable);
@@ -1452,12 +1451,8 @@ static int __netdev_rx(struct net_device *dev, int *quota)
#ifndef final_version /* Remove after testing. */
/* You will want this info for the initial debug. */
if (debug > 5) {
- printk(KERN_DEBUG " Rx data " MAC_FMT " " MAC_FMT
- " %2.2x%2.2x.\n",
- skb->data[0], skb->data[1], skb->data[2],
- skb->data[3], skb->data[4], skb->data[5],
- skb->data[6], skb->data[7], skb->data[8],
- skb->data[9], skb->data[10], skb->data[11],
+ printk(KERN_DEBUG " Rx data %pM %pM %2.2x%2.2x.\n",
+ skb->data, skb->data + 6,
skb->data[12], skb->data[13]);
}
#endif
@@ -1501,7 +1496,6 @@ static int __netdev_rx(struct net_device *dev, int *quota)
} else
#endif /* VLAN_SUPPORT */
netif_receive_skb(skb);
- dev->last_rx = jiffies;
np->stats.rx_packets++;
next_rx:
@@ -1509,6 +1503,11 @@ static int __netdev_rx(struct net_device *dev, int *quota)
desc->status = 0;
np->rx_done = (np->rx_done + 1) % DONE_Q_SIZE;
}
+
+ if (*quota == 0) { /* out of rx quota */
+ retcode = 1;
+ goto out;
+ }
writew(np->rx_done, np->base + CompletionQConsumerIdx);
out:
@@ -1536,7 +1535,7 @@ static int netdev_poll(struct napi_struct *napi, int budget)
intr_status = readl(ioaddr + IntrStatus);
} while (intr_status & (IntrRxDone | IntrRxEmpty));
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
intr_status = readl(ioaddr + IntrEnable);
intr_status |= IntrRxDone | IntrRxEmpty;
writel(intr_status, ioaddr + IntrEnable);
diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c
index 2ed0bd596815..87a6b8eabc67 100644
--- a/drivers/net/stnic.c
+++ b/drivers/net/stnic.c
@@ -60,8 +60,6 @@ static byte stnic_eadr[6] =
static struct net_device *stnic_dev;
-static int stnic_open (struct net_device *dev);
-static int stnic_close (struct net_device *dev);
static void stnic_reset (struct net_device *dev);
static void stnic_get_hdr (struct net_device *dev, struct e8390_pkt_hdr *hdr,
int ring_page);
@@ -122,11 +120,7 @@ static int __init stnic_probe(void)
/* Set the base address to point to the NIC, not the "real" base! */
dev->base_addr = 0x1000;
dev->irq = IRQ_STNIC;
- dev->open = &stnic_open;
- dev->stop = &stnic_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+ dev->netdev_ops = &ei_netdev_ops;
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
@@ -168,23 +162,6 @@ static int __init stnic_probe(void)
return 0;
}
-static int
-stnic_open (struct net_device *dev)
-{
-#if 0
- printk (KERN_DEBUG "stnic open\n");
-#endif
- ei_open (dev);
- return 0;
-}
-
-static int
-stnic_close (struct net_device *dev)
-{
- ei_close (dev);
- return 0;
-}
-
static void
stnic_reset (struct net_device *dev)
{
diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c
index e531302d95f5..e0d84772771c 100644
--- a/drivers/net/sun3_82586.c
+++ b/drivers/net/sun3_82586.c
@@ -209,7 +209,7 @@ static int sun3_82586_open(struct net_device *dev)
static int check586(struct net_device *dev,char *where,unsigned size)
{
struct priv pb;
- struct priv *p = /* (struct priv *) dev->priv*/ &pb;
+ struct priv *p = &pb;
char *iscp_addr;
int i;
@@ -247,7 +247,7 @@ static int check586(struct net_device *dev,char *where,unsigned size)
*/
static void alloc586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
sun3_reset586();
DELAY(1);
@@ -363,17 +363,21 @@ static int __init sun3_82586_probe1(struct net_device *dev,int ioaddr)
goto out;
}
- ((struct priv *) (dev->priv))->memtop = (char *)dvma_btov(dev->mem_start);
- ((struct priv *) (dev->priv))->base = (unsigned long) dvma_btov(0);
+ ((struct priv *)netdev_priv(dev))->memtop =
+ (char *)dvma_btov(dev->mem_start);
+ ((struct priv *)netdev_priv(dev))->base = (unsigned long) dvma_btov(0);
alloc586(dev);
/* set number of receive-buffs according to memsize */
if(size == 0x2000)
- ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8;
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_8;
else if(size == 0x4000)
- ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_16;
else
- ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_32;
+ ((struct priv *)netdev_priv(dev))->num_recv_buffs =
+ NUM_RECV_BUFFS_32;
printk("Memaddr: 0x%lx, Memsize: %d, IRQ %d\n",dev->mem_start,size, dev->irq);
@@ -397,7 +401,7 @@ static int init586(struct net_device *dev)
{
void *ptr;
int i,result=0;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
volatile struct configure_cmd_struct *cfg_cmd;
volatile struct iasetup_cmd_struct *ias_cmd;
volatile struct tdr_cmd_struct *tdr_cmd;
@@ -631,7 +635,7 @@ static void *alloc_rfa(struct net_device *dev,void *ptr)
volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr;
volatile struct rbd_struct *rbd;
int i;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd));
p->rfd_first = rfd;
@@ -683,7 +687,7 @@ static irqreturn_t sun3_82586_interrupt(int irq,void *dev_id)
printk ("sun3_82586-interrupt: irq %d for unknown device.\n",irq);
return IRQ_NONE;
}
- p = (struct priv *) dev->priv;
+ p = netdev_priv(dev);
if(debuglevel > 1)
printk("I");
@@ -753,7 +757,7 @@ static void sun3_82586_rcv_int(struct net_device *dev)
unsigned short totlen;
struct sk_buff *skb;
struct rbd_struct *rbd;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
if(debuglevel > 0)
printk("R");
@@ -871,7 +875,7 @@ static void sun3_82586_rcv_int(struct net_device *dev)
static void sun3_82586_rnr_int(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
p->stats.rx_errors++;
@@ -895,7 +899,7 @@ static void sun3_82586_rnr_int(struct net_device *dev)
static void sun3_82586_xmt_int(struct net_device *dev)
{
int status;
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
if(debuglevel > 0)
printk("X");
@@ -945,7 +949,7 @@ static void sun3_82586_xmt_int(struct net_device *dev)
static void startrecv586(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
WAIT_4_SCB_CMD();
WAIT_4_SCB_CMD_RUC();
@@ -957,7 +961,7 @@ static void startrecv586(struct net_device *dev)
static void sun3_82586_timeout(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
#ifndef NO_NOPCOMMANDS
if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */
{
@@ -999,7 +1003,7 @@ static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev)
#ifndef NO_NOPCOMMANDS
int next_nop;
#endif
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
if(skb->len > XMIT_BUFF_SIZE)
{
@@ -1108,7 +1112,7 @@ static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *sun3_82586_get_stats(struct net_device *dev)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
unsigned short crc,aln,rsc,ovrn;
crc = swab16(p->scb->crc_errs); /* get error-statistic from the ni82586 */
@@ -1171,7 +1175,7 @@ void cleanup_module(void)
*/
void sun3_82586_dump(struct net_device *dev,void *ptr)
{
- struct priv *p = (struct priv *) dev->priv;
+ struct priv *p = netdev_priv(dev);
struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr;
int i;
diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c
index 359452a06c67..4bb8f72c65cc 100644
--- a/drivers/net/sun3lance.c
+++ b/drivers/net/sun3lance.c
@@ -303,7 +303,6 @@ static int __init lance_probe( struct net_device *dev)
static int did_version;
volatile unsigned short *ioaddr_probe;
unsigned short tmp1, tmp2;
- DECLARE_MAC_BUF(mac);
#ifdef CONFIG_SUN3
ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE);
@@ -379,7 +378,7 @@ static int __init lance_probe( struct net_device *dev)
MEM->init.hwaddr[4] = dev->dev_addr[5];
MEM->init.hwaddr[5] = dev->dev_addr[4];
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
MEM->init.mode = 0x0000;
MEM->init.filter[0] = 0x00000000;
@@ -824,12 +823,10 @@ static int lance_rx( struct net_device *dev )
#if 0
if (lance_debug >= 3) {
u_char *data = PKTBUF_ADDR(head);
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2)
printk("%s: RX pkt %d type 0x%04x"
- " from %s to %s",
+ " from %pM to %pM",
dev->name, lp->new_tx, ((u_short *)data)[6],
- print_mac(mac, &data[6]), print_mac(mac2, data));
+ &data[6], data);
printk(" data %02x %02x %02x %02x %02x %02x %02x %02x "
"len %d at %08x\n",
@@ -852,7 +849,6 @@ static int lance_rx( struct net_device *dev )
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 018d0fca9422..7f69c7f176c4 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -878,7 +878,6 @@ static void bigmac_rx(struct bigmac *bp)
/* No checksums done by the BigMAC ;-( */
skb->protocol = eth_type_trans(skb, bp->dev);
netif_rx(skb);
- bp->dev->last_rx = jiffies;
bp->enet_stats.rx_packets++;
bp->enet_stats.rx_bytes += len;
next:
@@ -917,7 +916,7 @@ static irqreturn_t bigmac_interrupt(int irq, void *dev_id)
static int bigmac_open(struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
int ret;
ret = request_irq(dev->irq, &bigmac_interrupt, IRQF_SHARED, dev->name, bp);
@@ -934,7 +933,7 @@ static int bigmac_open(struct net_device *dev)
static int bigmac_close(struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
del_timer(&bp->bigmac_timer);
bp->timer_state = asleep;
@@ -948,7 +947,7 @@ static int bigmac_close(struct net_device *dev)
static void bigmac_tx_timeout(struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
bigmac_init_hw(bp, 0);
netif_wake_queue(dev);
@@ -957,7 +956,7 @@ static void bigmac_tx_timeout(struct net_device *dev)
/* Put a packet on the wire. */
static int bigmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
int len, entry;
u32 mapping;
@@ -990,7 +989,7 @@ static int bigmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *bigmac_get_stats(struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
bigmac_get_counters(bp, bp->bregs);
return &bp->enet_stats;
@@ -998,7 +997,7 @@ static struct net_device_stats *bigmac_get_stats(struct net_device *dev)
static void bigmac_set_multicast(struct net_device *dev)
{
- struct bigmac *bp = (struct bigmac *) dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
void __iomem *bregs = bp->bregs;
struct dev_mc_list *dmi = dev->mc_list;
char *addrs;
@@ -1061,7 +1060,7 @@ static void bigmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i
static u32 bigmac_get_link(struct net_device *dev)
{
- struct bigmac *bp = dev->priv;
+ struct bigmac *bp = netdev_priv(dev);
spin_lock_irq(&bp->lock);
bp->sw_bmsr = bigmac_tcvr_read(bp, bp->tregs, BIGMAC_BMSR);
@@ -1081,7 +1080,6 @@ static int __devinit bigmac_ether_init(struct of_device *op,
static int version_printed;
struct net_device *dev;
u8 bsizes, bsizes_more;
- DECLARE_MAC_BUF(mac);
struct bigmac *bp;
int i;
@@ -1212,8 +1210,8 @@ static int __devinit bigmac_ether_init(struct of_device *op,
dev_set_drvdata(&bp->bigmac_op->dev, bp);
- printk(KERN_INFO "%s: BigMAC 100baseT Ethernet %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: BigMAC 100baseT Ethernet %pM\n",
+ dev->name, dev->dev_addr);
return 0;
@@ -1235,7 +1233,7 @@ fail_and_cleanup:
bp->bmac_block,
bp->bblock_dvma);
- /* This also frees the co-located 'dev->priv' */
+ /* This also frees the co-located private data */
free_netdev(dev);
return -ENODEV;
}
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index f860ea150395..698893b92003 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -468,7 +468,6 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
int bar = 1;
#endif
int phy, phy_end, phy_idx = 0;
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -547,9 +546,9 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
if (i)
goto err_out_unmap_rx;
- printk(KERN_INFO "%s: %s at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
dev->name, pci_id_tbl[chip_idx].name, ioaddr,
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
np->phys[0] = 1; /* Default setting */
np->mii_preamble_required++;
@@ -1351,7 +1350,6 @@ static void rx_poll(unsigned long data)
skb->protocol = eth_type_trans(skb, dev);
/* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */
netif_rx(skb);
- dev->last_rx = jiffies;
}
entry = (entry + 1) % RX_RING_SIZE;
received++;
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index fed7eba65ead..8a7460412482 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -164,7 +164,7 @@ static u16 __phy_read(struct gem *gp, int phy_addr, int reg)
static inline int _phy_read(struct net_device *dev, int mii_id, int reg)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
return __phy_read(gp, mii_id, reg);
}
@@ -197,7 +197,7 @@ static void __phy_write(struct gem *gp, int phy_addr, int reg, u16 val)
static inline void _phy_write(struct net_device *dev, int mii_id, int reg, int val)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
__phy_write(gp, mii_id, reg, val & 0xffff);
}
@@ -863,7 +863,6 @@ static int gem_rx(struct gem *gp, int work_to_do)
gp->net_stats.rx_packets++;
gp->net_stats.rx_bytes += len;
- gp->dev->last_rx = jiffies;
next:
entry = NEXT_RX(entry);
@@ -922,7 +921,7 @@ static int gem_poll(struct napi_struct *napi, int budget)
gp->status = readl(gp->regs + GREG_STAT);
} while (gp->status & GREG_STAT_NAPI);
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
gem_enable_ints(gp);
spin_unlock_irqrestore(&gp->lock, flags);
@@ -933,7 +932,7 @@ static int gem_poll(struct napi_struct *napi, int budget)
static irqreturn_t gem_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned long flags;
/* Swallow interrupts when shutting the chip down, though
@@ -945,7 +944,7 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
spin_lock_irqsave(&gp->lock, flags);
- if (netif_rx_schedule_prep(dev, &gp->napi)) {
+ if (netif_rx_schedule_prep(&gp->napi)) {
u32 gem_status = readl(gp->regs + GREG_STAT);
if (gem_status == 0) {
@@ -955,7 +954,7 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id)
}
gp->status = gem_status;
gem_disable_ints(gp);
- __netif_rx_schedule(dev, &gp->napi);
+ __netif_rx_schedule(&gp->napi);
}
spin_unlock_irqrestore(&gp->lock, flags);
@@ -979,7 +978,7 @@ static void gem_poll_controller(struct net_device *dev)
static void gem_tx_timeout(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
if (!gp->running) {
@@ -1018,7 +1017,7 @@ static __inline__ int gem_intme(int entry)
static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
int entry;
u64 ctrl;
unsigned long flags;
@@ -2208,7 +2207,7 @@ static void gem_stop_phy(struct gem *gp, int wol)
static int gem_do_start(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&gp->lock, flags);
@@ -2255,7 +2254,7 @@ static int gem_do_start(struct net_device *dev)
static void gem_do_stop(struct net_device *dev, int wol)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned long flags;
spin_lock_irqsave(&gp->lock, flags);
@@ -2330,7 +2329,7 @@ static void gem_reset_task(struct work_struct *work)
static int gem_open(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
int rc = 0;
mutex_lock(&gp->pm_mutex);
@@ -2349,7 +2348,7 @@ static int gem_open(struct net_device *dev)
static int gem_close(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
mutex_lock(&gp->pm_mutex);
@@ -2368,7 +2367,7 @@ static int gem_close(struct net_device *dev)
static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned long flags;
mutex_lock(&gp->pm_mutex);
@@ -2432,7 +2431,7 @@ static int gem_suspend(struct pci_dev *pdev, pm_message_t state)
static int gem_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned long flags;
printk(KERN_INFO "%s: resuming\n", dev->name);
@@ -2506,7 +2505,7 @@ static int gem_resume(struct pci_dev *pdev)
static struct net_device_stats *gem_get_stats(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
struct net_device_stats *stats = &gp->net_stats;
spin_lock_irq(&gp->lock);
@@ -2542,7 +2541,7 @@ static struct net_device_stats *gem_get_stats(struct net_device *dev)
static int gem_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *macaddr = (struct sockaddr *) addr;
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unsigned char *e = &dev->dev_addr[0];
if (!is_valid_ether_addr(macaddr->sa_data))
@@ -2570,7 +2569,7 @@ static int gem_set_mac_address(struct net_device *dev, void *addr)
static void gem_set_multicast(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
u32 rxcfg, rxcfg_new;
int limit = 10000;
@@ -2619,7 +2618,7 @@ static void gem_set_multicast(struct net_device *dev)
static int gem_change_mtu(struct net_device *dev, int new_mtu)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
if (new_mtu < GEM_MIN_MTU || new_mtu > GEM_MAX_MTU)
return -EINVAL;
@@ -2650,7 +2649,7 @@ static int gem_change_mtu(struct net_device *dev, int new_mtu)
static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
strcpy(info->driver, DRV_NAME);
strcpy(info->version, DRV_VERSION);
@@ -2659,7 +2658,7 @@ static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info
static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1) {
@@ -2720,7 +2719,7 @@ static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
/* Verify the settings we care about. */
if (cmd->autoneg != AUTONEG_ENABLE &&
@@ -2751,7 +2750,7 @@ static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int gem_nway_reset(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
if (!gp->want_autoneg)
return -EINVAL;
@@ -2768,13 +2767,13 @@ static int gem_nway_reset(struct net_device *dev)
static u32 gem_get_msglevel(struct net_device *dev)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
return gp->msg_enable;
}
static void gem_set_msglevel(struct net_device *dev, u32 value)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
gp->msg_enable = value;
}
@@ -2786,7 +2785,7 @@ static void gem_set_msglevel(struct net_device *dev, u32 value)
static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
/* Add more when I understand how to program the chip */
if (gp->has_wol) {
@@ -2800,7 +2799,7 @@ static void gem_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
if (!gp->has_wol)
return -EOPNOTSUPP;
@@ -2822,7 +2821,7 @@ static const struct ethtool_ops gem_ethtool_ops = {
static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(ifr);
int rc = -EOPNOTSUPP;
unsigned long flags;
@@ -2954,7 +2953,7 @@ static void gem_remove_one(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
if (dev) {
- struct gem *gp = dev->priv;
+ struct gem *gp = netdev_priv(dev);
unregister_netdev(dev);
@@ -2998,7 +2997,6 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
struct net_device *dev;
struct gem *gp;
int err, pci_using_dac;
- DECLARE_MAC_BUF(mac);
if (gem_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -3058,7 +3056,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
}
SET_NETDEV_DEV(dev, &pdev->dev);
- gp = dev->priv;
+ gp = netdev_priv(dev);
err = pci_request_regions(pdev, DRV_NAME);
if (err) {
@@ -3182,9 +3180,8 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
goto err_out_free_consistent;
}
- printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet "
- "%s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Sun GEM (PCI) 10/100/1000BaseT Ethernet %pM\n",
+ dev->name, dev->dev_addr);
if (gp->phy_type == phy_mii_mdio0 ||
gp->phy_type == phy_mii_mdio1)
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index f1ebeb5f65b2..b22d3355fb45 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -2072,7 +2072,6 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
hp->net_stats.rx_packets++;
hp->net_stats.rx_bytes += len;
next:
@@ -2131,7 +2130,7 @@ static irqreturn_t quattro_sbus_interrupt(int irq, void *cookie)
for (i = 0; i < 4; i++) {
struct net_device *dev = qp->happy_meals[i];
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT);
HMD(("quattro_interrupt: status=%08x ", happy_status));
@@ -2176,7 +2175,7 @@ static irqreturn_t quattro_sbus_interrupt(int irq, void *cookie)
static int happy_meal_open(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
int res;
HMD(("happy_meal_open: "));
@@ -2208,7 +2207,7 @@ static int happy_meal_open(struct net_device *dev)
static int happy_meal_close(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
spin_lock_irq(&hp->happy_lock);
happy_meal_stop(hp, hp->gregs);
@@ -2237,7 +2236,7 @@ static int happy_meal_close(struct net_device *dev)
static void happy_meal_tx_timeout(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
tx_dump_log();
@@ -2255,7 +2254,7 @@ static void happy_meal_tx_timeout(struct net_device *dev)
static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
int entry;
u32 tx_flags;
@@ -2344,7 +2343,7 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
spin_lock_irq(&hp->happy_lock);
happy_meal_get_counters(hp, hp->bigmacregs);
@@ -2355,7 +2354,7 @@ static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
static void happy_meal_set_multicast(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
void __iomem *bregs = hp->bigmacregs;
struct dev_mc_list *dmi = dev->mc_list;
char *addrs;
@@ -2401,7 +2400,7 @@ static void happy_meal_set_multicast(struct net_device *dev)
/* Ethtool support... */
static int hme_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
cmd->supported =
(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
@@ -2446,7 +2445,7 @@ static int hme_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static int hme_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
/* Verify the settings we care about. */
if (cmd->autoneg != AUTONEG_ENABLE &&
@@ -2470,7 +2469,7 @@ static int hme_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
strcpy(info->driver, "sunhme");
strcpy(info->version, "2.02");
@@ -2492,7 +2491,7 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info
static u32 hme_get_link(struct net_device *dev)
{
- struct happy_meal *hp = dev->priv;
+ struct happy_meal *hp = netdev_priv(dev);
spin_lock_irq(&hp->happy_lock);
hp->sw_bmcr = happy_meal_tcvr_read(hp, hp->tcvregs, MII_BMCR);
@@ -2617,7 +2616,6 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
struct net_device *dev;
int i, qfe_slot = -1;
int err = -ENODEV;
- DECLARE_MAC_BUF(mac);
if (is_qfe) {
qp = quattro_sbus_find(op);
@@ -2797,7 +2795,7 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
printk(KERN_INFO "%s: HAPPY MEAL (SBUS) 10/100baseT Ethernet ",
dev->name);
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
return 0;
@@ -2932,7 +2930,6 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
int i, qfe_slot = -1;
char prom_name[64];
int err;
- DECLARE_MAC_BUF(mac);
/* Now make sure pci_dev cookie is there. */
#ifdef CONFIG_SPARC
@@ -2973,7 +2970,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
dev->base_addr = (long) pdev;
- hp = (struct happy_meal *)dev->priv;
+ hp = netdev_priv(dev);
memset(hp, 0, sizeof(*hp));
hp->happy_dev = pdev;
@@ -3141,7 +3138,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
printk(KERN_INFO "%s: HAPPY MEAL (PCI/CheerIO) 10/100BaseT Ethernet ",
dev->name);
- printk("%s\n", print_mac(mac, dev->dev_addr));
+ printk("%pM\n", dev->dev_addr);
return 0;
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index 704301a5a7ff..281373281756 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -555,7 +555,6 @@ static void lance_rx_dvma(struct net_device *dev)
len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
@@ -726,7 +725,6 @@ static void lance_rx_pio(struct net_device *dev)
lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
}
@@ -1321,7 +1319,6 @@ static int __devinit sparc_lance_probe_one(struct of_device *op,
static unsigned version_printed;
struct lance_private *lp;
struct net_device *dev;
- DECLARE_MAC_BUF(mac);
int i;
dev = alloc_etherdev(sizeof(struct lance_private) + 8);
@@ -1491,8 +1488,8 @@ no_link_test:
dev_set_drvdata(&op->dev, lp);
- printk(KERN_INFO "%s: LANCE %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: LANCE %pM\n",
+ dev->name, dev->dev_addr);
return 0;
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index f63644744ff9..6e8f377355fe 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -446,7 +446,6 @@ static void qe_rx(struct sunqe *qep)
len);
skb->protocol = eth_type_trans(skb, qep->dev);
netif_rx(skb);
- qep->dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
}
@@ -513,7 +512,7 @@ static irqreturn_t qec_interrupt(int irq, void *dev_id)
static int qe_open(struct net_device *dev)
{
- struct sunqe *qep = (struct sunqe *) dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
qep->mconfig = (MREGS_MCONFIG_TXENAB |
MREGS_MCONFIG_RXENAB |
@@ -523,7 +522,7 @@ static int qe_open(struct net_device *dev)
static int qe_close(struct net_device *dev)
{
- struct sunqe *qep = (struct sunqe *) dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
qe_stop(qep);
return 0;
@@ -549,7 +548,7 @@ static void qe_tx_reclaim(struct sunqe *qep)
static void qe_tx_timeout(struct net_device *dev)
{
- struct sunqe *qep = (struct sunqe *) dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
int tx_full;
spin_lock_irq(&qep->lock);
@@ -575,7 +574,7 @@ out:
/* Get a packet queued to go onto the wire. */
static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct sunqe *qep = (struct sunqe *) dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
struct sunqe_buffers *qbufs = qep->buffers;
__u32 txbuf_dvma, qbufs_dvma = qep->buffers_dvma;
unsigned char *txbuf;
@@ -627,7 +626,7 @@ static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void qe_set_multicast(struct net_device *dev)
{
- struct sunqe *qep = (struct sunqe *) dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
struct dev_mc_list *dmi = dev->mc_list;
u8 new_mconfig = qep->mconfig;
char *addrs;
@@ -693,7 +692,7 @@ static void qe_set_multicast(struct net_device *dev)
static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
const struct linux_prom_registers *regs;
- struct sunqe *qep = dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
struct of_device *op;
strcpy(info->driver, "sunqe");
@@ -708,7 +707,7 @@ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
static u32 qe_get_link(struct net_device *dev)
{
- struct sunqe *qep = dev->priv;
+ struct sunqe *qep = netdev_priv(dev);
void __iomem *mregs = qep->mregs;
u8 phyconfig;
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c
index a720065553df..233f1cda36e5 100644
--- a/drivers/net/sunvnet.c
+++ b/drivers/net/sunvnet.c
@@ -1149,7 +1149,6 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev,
struct vnet *vp;
const u64 *rmac;
int len, i, err, switch_port;
- DECLARE_MAC_BUF(mac);
print_version();
@@ -1214,8 +1213,8 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev,
dev_set_drvdata(&vdev->dev, port);
- printk(KERN_INFO "%s: PORT ( remote-mac %s%s )\n",
- vp->dev->name, print_mac(mac, port->raddr),
+ printk(KERN_INFO "%s: PORT ( remote-mac %pM%s )\n",
+ vp->dev->name, port->raddr,
switch_port ? " switch-port" : "");
vio_port_up(&port->vio);
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index df20cafff7dd..bcd0e60cbda9 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -37,6 +37,7 @@ static const char *version = "tc35815.c:v" DRV_VERSION "\n";
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
+#include <linux/if_vlan.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/spinlock.h>
@@ -236,7 +237,7 @@ struct tc35815_regs {
#define Rx_Halted 0x00008000 /* Rx Halted */
#define Rx_Good 0x00004000 /* Rx Good */
#define Rx_RxPar 0x00002000 /* Rx Parity Error */
- /* 0x00001000 not use */
+#define Rx_TypePkt 0x00001000 /* Rx Type Packet */
#define Rx_LongErr 0x00000800 /* Rx Long Error */
#define Rx_Over 0x00000400 /* Rx Overflow */
#define Rx_CRCErr 0x00000200 /* Rx CRC Error */
@@ -244,8 +245,9 @@ struct tc35815_regs {
#define Rx_10Stat 0x00000080 /* Rx 10Mbps Status */
#define Rx_IntRx 0x00000040 /* Rx Interrupt */
#define Rx_CtlRecd 0x00000020 /* Rx Control Receive */
+#define Rx_InLenErr 0x00000010 /* Rx In Range Frame Length Error */
-#define Rx_Stat_Mask 0x0000EFC0 /* Rx All Status Mask */
+#define Rx_Stat_Mask 0x0000FFF0 /* Rx All Status Mask */
/* Int_En bit asign -------------------------------------------------------- */
#define Int_NRAbtEn 0x00000800 /* 1:Non-recoverable Abort Enable */
@@ -340,7 +342,7 @@ struct BDesc {
Tx_En) /* maybe 0x7b01 */
#endif
#define RX_CTL_CMD (Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \
- | Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */
+ | Rx_EnCRCErr | Rx_EnAlign | Rx_StripCRC | Rx_RxEn) /* maybe 0x6f11 */
#define INT_EN_CMD (Int_NRAbtEn | \
Int_DmParErrEn | Int_DParDEn | Int_DParErrEn | \
Int_SSysErrEn | Int_RMasAbtEn | Int_RTargAbtEn | \
@@ -372,9 +374,11 @@ struct BDesc {
#if RX_CTL_CMD & Rx_LongEn
#define RX_BUF_SIZE PAGE_SIZE
#elif RX_CTL_CMD & Rx_StripCRC
-#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 4 + 2, 32) /* +2: reserve */
+#define RX_BUF_SIZE \
+ L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + NET_IP_ALIGN)
#else
-#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 2, 32) /* +2: reserve */
+#define RX_BUF_SIZE \
+ L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN + NET_IP_ALIGN)
#endif
#endif /* TC35815_USE_PACKEDBUFFER */
#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */
@@ -865,7 +869,6 @@ static int __devinit tc35815_init_one(struct pci_dev *pdev,
struct net_device *dev;
struct tc35815_local *lp;
int rc;
- DECLARE_MAC_BUF(mac);
static int printed_version;
if (!printed_version++) {
@@ -942,11 +945,11 @@ static int __devinit tc35815_init_one(struct pci_dev *pdev,
goto err_out;
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- printk(KERN_INFO "%s: %s at 0x%lx, %s, IRQ %d\n",
+ printk(KERN_INFO "%s: %s at 0x%lx, %pM, IRQ %d\n",
dev->name,
chip_info[ent->driver_data].name,
dev->base_addr,
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
rc = tc_mii_init(dev);
@@ -1288,12 +1291,9 @@ panic_queues(struct net_device *dev)
static void print_eth(const u8 *add)
{
- DECLARE_MAC_BUF(mac);
-
printk(KERN_DEBUG "print_eth(%p)\n", add);
- printk(KERN_DEBUG " %s =>", print_mac(mac, add + 6));
- printk(KERN_CONT " %s : %02x%02x\n",
- print_mac(mac, add), add[12], add[13]);
+ printk(KERN_DEBUG " %pM => %pM : %02x%02x\n",
+ add + 6, add, add[12], add[13]);
}
static int tc35815_tx_full(struct net_device *dev)
@@ -1609,8 +1609,8 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id)
if (!(dmactl & DMA_IntMask)) {
/* disable interrupts */
tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl);
- if (netif_rx_schedule_prep(dev, &lp->napi))
- __netif_rx_schedule(dev, &lp->napi);
+ if (netif_rx_schedule_prep(&lp->napi))
+ __netif_rx_schedule(&lp->napi);
else {
printk(KERN_ERR "%s: interrupt taken in poll\n",
dev->name);
@@ -1669,7 +1669,7 @@ tc35815_rx(struct net_device *dev)
struct RxFD *next_rfd;
#endif
#if (RX_CTL_CMD & Rx_StripCRC) == 0
- pkt_len -= 4;
+ pkt_len -= ETH_FCS_LEN;
#endif
if (netif_msg_rx_status(lp))
@@ -1688,14 +1688,14 @@ tc35815_rx(struct net_device *dev)
#endif
#ifdef TC35815_USE_PACKEDBUFFER
BUG_ON(bd_count > 2);
- skb = dev_alloc_skb(pkt_len + 2); /* +2: for reserve */
+ skb = dev_alloc_skb(pkt_len + NET_IP_ALIGN);
if (skb == NULL) {
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
dev->name);
dev->stats.rx_dropped++;
break;
}
- skb_reserve(skb, 2); /* 16 bit alignment */
+ skb_reserve(skb, NET_IP_ALIGN);
data = skb_put(skb, pkt_len);
@@ -1747,8 +1747,9 @@ tc35815_rx(struct net_device *dev)
pci_unmap_single(lp->pci_dev,
lp->rx_skbs[cur_bd].skb_dma,
RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
- if (!HAVE_DMA_RXALIGN(lp))
- memmove(skb->data, skb->data - 2, pkt_len);
+ if (!HAVE_DMA_RXALIGN(lp) && NET_IP_ALIGN)
+ memmove(skb->data, skb->data - NET_IP_ALIGN,
+ pkt_len);
data = skb_put(skb, pkt_len);
#endif /* TC35815_USE_PACKEDBUFFER */
if (netif_msg_pktdata(lp))
@@ -1760,7 +1761,6 @@ tc35815_rx(struct net_device *dev)
#else
netif_rx(skb);
#endif
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
@@ -1919,7 +1919,7 @@ static int tc35815_poll(struct napi_struct *napi, int budget)
spin_unlock(&lp->lock);
if (received < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
/* enable interrupts */
tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl);
}
@@ -2153,13 +2153,12 @@ static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned ch
int cam_index = index * 6;
u32 cam_data;
u32 saved_addr;
- DECLARE_MAC_BUF(mac);
saved_addr = tc_readl(&tr->CAM_Adr);
if (netif_msg_hw(lp))
- printk(KERN_DEBUG "%s: CAM %d: %s\n",
- dev->name, index, print_mac(mac, addr));
+ printk(KERN_DEBUG "%s: CAM %d: %pM\n",
+ dev->name, index, addr);
if (index & 1) {
/* read modify write */
tc_writel(cam_index - 2, &tr->CAM_Adr);
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index 91f9054a1d95..a10a83a11d9f 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -251,7 +251,7 @@ static void bdx_isr_extra(struct bdx_priv *priv, u32 isr)
static irqreturn_t bdx_isr_napi(int irq, void *dev)
{
struct net_device *ndev = dev;
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
u32 isr;
ENTER;
@@ -265,8 +265,8 @@ static irqreturn_t bdx_isr_napi(int irq, void *dev)
bdx_isr_extra(priv, isr);
if (isr & (IR_RX_DESC_0 | IR_TX_FREE_0)) {
- if (likely(netif_rx_schedule_prep(ndev, &priv->napi))) {
- __netif_rx_schedule(ndev, &priv->napi);
+ if (likely(netif_rx_schedule_prep(&priv->napi))) {
+ __netif_rx_schedule(&priv->napi);
RET(IRQ_HANDLED);
} else {
/* NOTE: we get here if intr has slipped into window
@@ -289,7 +289,6 @@ static irqreturn_t bdx_isr_napi(int irq, void *dev)
static int bdx_poll(struct napi_struct *napi, int budget)
{
struct bdx_priv *priv = container_of(napi, struct bdx_priv, napi);
- struct net_device *dev = priv->ndev;
int work_done;
ENTER;
@@ -303,7 +302,7 @@ static int bdx_poll(struct napi_struct *napi, int budget)
* device lock and allow waiting tasks (eg rmmod) to advance) */
priv->napi_stop = 0;
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
bdx_enable_interrupts(priv);
}
return work_done;
@@ -559,7 +558,7 @@ static int bdx_close(struct net_device *ndev)
struct bdx_priv *priv = NULL;
ENTER;
- priv = ndev->priv;
+ priv = netdev_priv(ndev);
napi_disable(&priv->napi);
@@ -588,7 +587,7 @@ static int bdx_open(struct net_device *ndev)
int rc;
ENTER;
- priv = ndev->priv;
+ priv = netdev_priv(ndev);
bdx_reset(priv);
if (netif_running(ndev))
netif_stop_queue(priv->ndev);
@@ -633,7 +632,7 @@ static int bdx_range_check(struct bdx_priv *priv, u32 offset)
static int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
u32 data[3];
int error;
@@ -698,7 +697,7 @@ static int bdx_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
*/
static void __bdx_vlan_rx_vid(struct net_device *ndev, uint16_t vid, int enable)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
u32 reg, bit, val;
ENTER;
@@ -748,7 +747,7 @@ static void bdx_vlan_rx_kill_vid(struct net_device *ndev, unsigned short vid)
static void
bdx_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
ENTER;
DBG("device='%s', group='%p'\n", ndev->name, grp);
@@ -787,7 +786,7 @@ static int bdx_change_mtu(struct net_device *ndev, int new_mtu)
static void bdx_setmulti(struct net_device *ndev)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
u32 rxf_val =
GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB | GMAC_RX_FILTER_OSEN;
@@ -847,7 +846,7 @@ static void bdx_setmulti(struct net_device *ndev)
static int bdx_set_mac(struct net_device *ndev, void *p)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
struct sockaddr *addr = p;
ENTER;
@@ -929,7 +928,7 @@ static void bdx_update_stats(struct bdx_priv *priv)
static struct net_device_stats *bdx_get_stats(struct net_device *ndev)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
struct net_device_stats *net_stat = &priv->net_stats;
return net_stat;
}
@@ -1237,7 +1236,6 @@ static int bdx_rx_receive(struct bdx_priv *priv, struct rxd_fifo *f, int budget)
ENTER;
max_done = budget;
- priv->ndev->last_rx = jiffies;
f->m.wptr = READ_REG(priv, f->m.reg_WPTR) & TXF_WPTR_WR_PTR;
size = f->m.wptr - f->m.rptr;
@@ -1624,7 +1622,7 @@ static inline int bdx_tx_space(struct bdx_priv *priv)
*/
static int bdx_tx_transmit(struct sk_buff *skb, struct net_device *ndev)
{
- struct bdx_priv *priv = ndev->priv;
+ struct bdx_priv *priv = netdev_priv(ndev);
struct txd_fifo *f = &priv->txd_fifo0;
int txd_checksum = 7; /* full checksum */
int txd_lgsnd = 0;
@@ -1886,6 +1884,21 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
RET();
}
+static const struct net_device_ops bdx_netdev_ops = {
+ .ndo_open = bdx_open,
+ .ndo_stop = bdx_close,
+ .ndo_start_xmit = bdx_tx_transmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = bdx_ioctl,
+ .ndo_set_multicast_list = bdx_setmulti,
+ .ndo_get_stats = bdx_get_stats,
+ .ndo_change_mtu = bdx_change_mtu,
+ .ndo_set_mac_address = bdx_set_mac,
+ .ndo_vlan_rx_register = bdx_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = bdx_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = bdx_vlan_rx_kill_vid,
+};
+
/**
* bdx_probe - Device Initialization Routine
* @pdev: PCI device information struct
@@ -1995,18 +2008,8 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out_iomap;
}
- ndev->open = bdx_open;
- ndev->stop = bdx_close;
- ndev->hard_start_xmit = bdx_tx_transmit;
- ndev->do_ioctl = bdx_ioctl;
- ndev->set_multicast_list = bdx_setmulti;
- ndev->get_stats = bdx_get_stats;
- ndev->change_mtu = bdx_change_mtu;
- ndev->set_mac_address = bdx_set_mac;
+ ndev->netdev_ops = &bdx_netdev_ops;
ndev->tx_queue_len = BDX_NDEV_TXQ_LEN;
- ndev->vlan_rx_register = bdx_vlan_rx_register;
- ndev->vlan_rx_add_vid = bdx_vlan_rx_add_vid;
- ndev->vlan_rx_kill_vid = bdx_vlan_rx_kill_vid;
bdx_ethtool_ops(ndev); /* ethtool interface */
@@ -2027,7 +2030,7 @@ bdx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ndev->features |= NETIF_F_HIGHDMA;
/************** priv ****************/
- priv = nic->priv[port] = ndev->priv;
+ priv = nic->priv[port] = netdev_priv(ndev);
memset(priv, 0, sizeof(struct bdx_priv));
priv->pBdxRegs = nic->regs + port * 0x8000;
@@ -2150,7 +2153,7 @@ static int bdx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{
u32 rdintcm;
u32 tdintcm;
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
rdintcm = priv->rdintcm;
tdintcm = priv->tdintcm;
@@ -2181,7 +2184,7 @@ static int bdx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
static void
bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
strlcat(drvinfo->driver, BDX_DRV_NAME, sizeof(drvinfo->driver));
strlcat(drvinfo->version, BDX_DRV_VERSION, sizeof(drvinfo->version));
@@ -2223,7 +2226,7 @@ bdx_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal)
{
u32 rdintcm;
u32 tdintcm;
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
rdintcm = priv->rdintcm;
tdintcm = priv->tdintcm;
@@ -2252,7 +2255,7 @@ bdx_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal)
{
u32 rdintcm;
u32 tdintcm;
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
int rx_coal;
int tx_coal;
int rx_max_coal;
@@ -2310,7 +2313,7 @@ static inline int bdx_tx_fifo_size_to_packets(int tx_size)
static void
bdx_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
{
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
/*max_pending - the maximum-sized FIFO we allow */
ring->rx_max_pending = bdx_rx_fifo_size_to_packets(3);
@@ -2327,7 +2330,7 @@ bdx_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
static int
bdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
{
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
int rx_size = 0;
int tx_size = 0;
@@ -2388,7 +2391,7 @@ static void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
*/
static int bdx_get_stats_count(struct net_device *netdev)
{
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
BDX_ASSERT(ARRAY_SIZE(bdx_stat_names)
!= sizeof(struct bdx_stats) / sizeof(u64));
return ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0);
@@ -2403,7 +2406,7 @@ static int bdx_get_stats_count(struct net_device *netdev)
static void bdx_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
- struct bdx_priv *priv = netdev->priv;
+ struct bdx_priv *priv = netdev_priv(netdev);
if (priv->stats_flag) {
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index eb9f8f3638e1..04ae1e86aeaa 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -54,20 +54,21 @@
#include <asm/prom.h>
#endif
+#define BAR_0 0
+#define BAR_2 2
+
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define TG3_VLAN_TAG_USED 1
#else
#define TG3_VLAN_TAG_USED 0
#endif
-#define TG3_TSO_SUPPORT 1
-
#include "tg3.h"
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.94"
-#define DRV_MODULE_RELDATE "August 14, 2008"
+#define DRV_MODULE_VERSION "3.97"
+#define DRV_MODULE_RELDATE "December 10, 2008"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -129,6 +130,8 @@
/* minimum number of free TX descriptors required to wake up TX process */
#define TG3_TX_WAKEUP_THRESH(tp) ((tp)->tx_pending / 4)
+#define TG3_RAW_IP_ALIGN 2
+
/* number of ETHTOOL_GSTATS u64's */
#define TG3_NUM_STATS (sizeof(struct tg3_ethtool_stats)/sizeof(u64))
@@ -205,7 +208,13 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5723)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761E)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5761S)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5761SE)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5785)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57780)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57760)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57790)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57720)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -872,13 +881,48 @@ static int tg3_mdio_reset(struct mii_bus *bp)
return 0;
}
-static void tg3_mdio_config(struct tg3 *tp)
+static void tg3_mdio_config_5785(struct tg3 *tp)
{
u32 val;
+ struct phy_device *phydev;
- if (tp->mdio_bus->phy_map[PHY_ADDR]->interface !=
- PHY_INTERFACE_MODE_RGMII)
+ phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
+ case TG3_PHY_ID_BCM50610:
+ val = MAC_PHYCFG2_50610_LED_MODES;
+ break;
+ case TG3_PHY_ID_BCMAC131:
+ val = MAC_PHYCFG2_AC131_LED_MODES;
+ break;
+ case TG3_PHY_ID_RTL8211C:
+ val = MAC_PHYCFG2_RTL8211C_LED_MODES;
+ break;
+ case TG3_PHY_ID_RTL8201E:
+ val = MAC_PHYCFG2_RTL8201E_LED_MODES;
+ break;
+ default:
return;
+ }
+
+ if (phydev->interface != PHY_INTERFACE_MODE_RGMII) {
+ tw32(MAC_PHYCFG2, val);
+
+ val = tr32(MAC_PHYCFG1);
+ val &= ~MAC_PHYCFG1_RGMII_INT;
+ tw32(MAC_PHYCFG1, val);
+
+ return;
+ }
+
+ if (!(tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE))
+ val |= MAC_PHYCFG2_EMODE_MASK_MASK |
+ MAC_PHYCFG2_FMODE_MASK_MASK |
+ MAC_PHYCFG2_GMODE_MASK_MASK |
+ MAC_PHYCFG2_ACT_MASK_MASK |
+ MAC_PHYCFG2_QUAL_MASK_MASK |
+ MAC_PHYCFG2_INBAND_ENABLE;
+
+ tw32(MAC_PHYCFG2, val);
val = tr32(MAC_PHYCFG1) & ~(MAC_PHYCFG1_RGMII_EXT_RX_DEC |
MAC_PHYCFG1_RGMII_SND_STAT_EN);
@@ -890,11 +934,6 @@ static void tg3_mdio_config(struct tg3 *tp)
}
tw32(MAC_PHYCFG1, val | MAC_PHYCFG1_RGMII_INT | MAC_PHYCFG1_TXC_DRV);
- val = tr32(MAC_PHYCFG2) & ~(MAC_PHYCFG2_INBAND_ENABLE);
- if (!(tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE))
- val |= MAC_PHYCFG2_INBAND_ENABLE;
- tw32(MAC_PHYCFG2, val);
-
val = tr32(MAC_EXT_RGMII_MODE);
val &= ~(MAC_RGMII_MODE_RX_INT_B |
MAC_RGMII_MODE_RX_QUALITY |
@@ -903,7 +942,7 @@ static void tg3_mdio_config(struct tg3 *tp)
MAC_RGMII_MODE_TX_ENABLE |
MAC_RGMII_MODE_TX_LOWPWR |
MAC_RGMII_MODE_TX_RESET);
- if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) {
+ if (!(tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE)) {
if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN)
val |= MAC_RGMII_MODE_RX_INT_B |
MAC_RGMII_MODE_RX_QUALITY |
@@ -929,8 +968,9 @@ static void tg3_mdio_start(struct tg3 *tp)
tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
- if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED)
- tg3_mdio_config(tp);
+ if ((tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ tg3_mdio_config_5785(tp);
}
static void tg3_mdio_stop(struct tg3 *tp)
@@ -984,29 +1024,44 @@ static int tg3_mdio_init(struct tg3 *tp)
if (i) {
printk(KERN_WARNING "%s: mdiobus_reg failed (0x%x)\n",
tp->dev->name, i);
+ mdiobus_free(tp->mdio_bus);
return i;
}
- tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED;
-
phydev = tp->mdio_bus->phy_map[PHY_ADDR];
- switch (phydev->phy_id) {
+ if (!phydev || !phydev->drv) {
+ printk(KERN_WARNING "%s: No PHY devices\n", tp->dev->name);
+ mdiobus_unregister(tp->mdio_bus);
+ mdiobus_free(tp->mdio_bus);
+ return -ENODEV;
+ }
+
+ switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
+ case TG3_PHY_ID_BCM57780:
+ phydev->interface = PHY_INTERFACE_MODE_GMII;
+ break;
case TG3_PHY_ID_BCM50610:
- phydev->interface = PHY_INTERFACE_MODE_RGMII;
if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE)
phydev->dev_flags |= PHY_BRCM_STD_IBND_DISABLE;
if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN)
phydev->dev_flags |= PHY_BRCM_EXT_IBND_RX_ENABLE;
if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN)
phydev->dev_flags |= PHY_BRCM_EXT_IBND_TX_ENABLE;
+ /* fallthru */
+ case TG3_PHY_ID_RTL8211C:
+ phydev->interface = PHY_INTERFACE_MODE_RGMII;
break;
+ case TG3_PHY_ID_RTL8201E:
case TG3_PHY_ID_BCMAC131:
phydev->interface = PHY_INTERFACE_MODE_MII;
break;
}
- tg3_mdio_config(tp);
+ tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ tg3_mdio_config_5785(tp);
return 0;
}
@@ -1130,9 +1185,9 @@ static void tg3_link_report(struct tg3 *tp)
printk(KERN_INFO PFX
"%s: Flow control is %s for TX and %s for RX.\n",
tp->dev->name,
- (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_TX) ?
+ (tp->link_config.active_flowctrl & FLOW_CTRL_TX) ?
"on" : "off",
- (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_RX) ?
+ (tp->link_config.active_flowctrl & FLOW_CTRL_RX) ?
"on" : "off");
tg3_ump_link_report(tp);
}
@@ -1142,11 +1197,11 @@ static u16 tg3_advert_flowctrl_1000T(u8 flow_ctrl)
{
u16 miireg;
- if ((flow_ctrl & TG3_FLOW_CTRL_TX) && (flow_ctrl & TG3_FLOW_CTRL_RX))
+ if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX))
miireg = ADVERTISE_PAUSE_CAP;
- else if (flow_ctrl & TG3_FLOW_CTRL_TX)
+ else if (flow_ctrl & FLOW_CTRL_TX)
miireg = ADVERTISE_PAUSE_ASYM;
- else if (flow_ctrl & TG3_FLOW_CTRL_RX)
+ else if (flow_ctrl & FLOW_CTRL_RX)
miireg = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
else
miireg = 0;
@@ -1158,11 +1213,11 @@ static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
{
u16 miireg;
- if ((flow_ctrl & TG3_FLOW_CTRL_TX) && (flow_ctrl & TG3_FLOW_CTRL_RX))
+ if ((flow_ctrl & FLOW_CTRL_TX) && (flow_ctrl & FLOW_CTRL_RX))
miireg = ADVERTISE_1000XPAUSE;
- else if (flow_ctrl & TG3_FLOW_CTRL_TX)
+ else if (flow_ctrl & FLOW_CTRL_TX)
miireg = ADVERTISE_1000XPSE_ASYM;
- else if (flow_ctrl & TG3_FLOW_CTRL_RX)
+ else if (flow_ctrl & FLOW_CTRL_RX)
miireg = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
else
miireg = 0;
@@ -1170,28 +1225,6 @@ static u16 tg3_advert_flowctrl_1000X(u8 flow_ctrl)
return miireg;
}
-static u8 tg3_resolve_flowctrl_1000T(u16 lcladv, u16 rmtadv)
-{
- u8 cap = 0;
-
- if (lcladv & ADVERTISE_PAUSE_CAP) {
- if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = TG3_FLOW_CTRL_TX | TG3_FLOW_CTRL_RX;
- else if (rmtadv & LPA_PAUSE_ASYM)
- cap = TG3_FLOW_CTRL_RX;
- } else {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = TG3_FLOW_CTRL_TX | TG3_FLOW_CTRL_RX;
- }
- } else if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM))
- cap = TG3_FLOW_CTRL_TX;
- }
-
- return cap;
-}
-
static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
{
u8 cap = 0;
@@ -1199,16 +1232,16 @@ static u8 tg3_resolve_flowctrl_1000X(u16 lcladv, u16 rmtadv)
if (lcladv & ADVERTISE_1000XPAUSE) {
if (lcladv & ADVERTISE_1000XPSE_ASYM) {
if (rmtadv & LPA_1000XPAUSE)
- cap = TG3_FLOW_CTRL_TX | TG3_FLOW_CTRL_RX;
+ cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
else if (rmtadv & LPA_1000XPAUSE_ASYM)
- cap = TG3_FLOW_CTRL_RX;
+ cap = FLOW_CTRL_RX;
} else {
if (rmtadv & LPA_1000XPAUSE)
- cap = TG3_FLOW_CTRL_TX | TG3_FLOW_CTRL_RX;
+ cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
}
} else if (lcladv & ADVERTISE_1000XPSE_ASYM) {
if ((rmtadv & LPA_1000XPAUSE) && (rmtadv & LPA_1000XPAUSE_ASYM))
- cap = TG3_FLOW_CTRL_TX;
+ cap = FLOW_CTRL_TX;
}
return cap;
@@ -1231,13 +1264,13 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES)
flowctrl = tg3_resolve_flowctrl_1000X(lcladv, rmtadv);
else
- flowctrl = tg3_resolve_flowctrl_1000T(lcladv, rmtadv);
+ flowctrl = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
} else
flowctrl = tp->link_config.flowctrl;
tp->link_config.active_flowctrl = flowctrl;
- if (flowctrl & TG3_FLOW_CTRL_RX)
+ if (flowctrl & FLOW_CTRL_RX)
tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE;
else
tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE;
@@ -1245,7 +1278,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
if (old_rx_mode != tp->rx_mode)
tw32_f(MAC_RX_MODE, tp->rx_mode);
- if (flowctrl & TG3_FLOW_CTRL_TX)
+ if (flowctrl & FLOW_CTRL_TX)
tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE;
else
tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE;
@@ -1299,6 +1332,15 @@ static void tg3_adjust_link(struct net_device *dev)
udelay(40);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ if (phydev->speed == SPEED_10)
+ tw32(MAC_MI_STAT,
+ MAC_MI_STAT_10MBPS_MODE |
+ MAC_MI_STAT_LNKSTAT_ATTN_ENAB);
+ else
+ tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB);
+ }
+
if (phydev->speed == SPEED_1000 && phydev->duplex == DUPLEX_HALF)
tw32(MAC_TX_LENGTHS,
((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
@@ -1339,25 +1381,37 @@ static int tg3_phy_init(struct tg3 *tp)
phydev = tp->mdio_bus->phy_map[PHY_ADDR];
/* Attach the MAC to the PHY. */
- phydev = phy_connect(tp->dev, phydev->dev.bus_id, tg3_adjust_link,
+ phydev = phy_connect(tp->dev, dev_name(&phydev->dev), tg3_adjust_link,
phydev->dev_flags, phydev->interface);
if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", tp->dev->name);
return PTR_ERR(phydev);
}
- tp->tg3_flags3 |= TG3_FLG3_PHY_CONNECTED;
-
/* Mask with MAC supported features. */
- phydev->supported &= (PHY_GBIT_FEATURES |
- SUPPORTED_Pause |
- SUPPORTED_Asym_Pause);
+ switch (phydev->interface) {
+ case PHY_INTERFACE_MODE_GMII:
+ case PHY_INTERFACE_MODE_RGMII:
+ if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) {
+ phydev->supported &= (PHY_GBIT_FEATURES |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ break;
+ }
+ /* fallthru */
+ case PHY_INTERFACE_MODE_MII:
+ phydev->supported &= (PHY_BASIC_FEATURES |
+ SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ break;
+ default:
+ phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]);
+ return -EINVAL;
+ }
- phydev->advertising = phydev->supported;
+ tp->tg3_flags3 |= TG3_FLG3_PHY_CONNECTED;
- printk(KERN_INFO
- "%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- tp->dev->name, phydev->drv->name, phydev->dev.bus_id);
+ phydev->advertising = phydev->supported;
return 0;
}
@@ -1406,6 +1460,34 @@ static void tg3_phydsp_write(struct tg3 *tp, u32 reg, u32 val)
tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val);
}
+static void tg3_phy_toggle_apd(struct tg3 *tp, bool enable)
+{
+ u32 reg;
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
+ return;
+
+ reg = MII_TG3_MISC_SHDW_WREN |
+ MII_TG3_MISC_SHDW_SCR5_SEL |
+ MII_TG3_MISC_SHDW_SCR5_LPED |
+ MII_TG3_MISC_SHDW_SCR5_DLPTLM |
+ MII_TG3_MISC_SHDW_SCR5_SDTL |
+ MII_TG3_MISC_SHDW_SCR5_C125OE;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 || !enable)
+ reg |= MII_TG3_MISC_SHDW_SCR5_DLLAPD;
+
+ tg3_writephy(tp, MII_TG3_MISC_SHDW, reg);
+
+
+ reg = MII_TG3_MISC_SHDW_WREN |
+ MII_TG3_MISC_SHDW_APD_SEL |
+ MII_TG3_MISC_SHDW_APD_WKTM_84MS;
+ if (enable)
+ reg |= MII_TG3_MISC_SHDW_APD_ENABLE;
+
+ tg3_writephy(tp, MII_TG3_MISC_SHDW, reg);
+}
+
static void tg3_phy_toggle_automdix(struct tg3 *tp, int enable)
{
u32 phy;
@@ -1737,7 +1819,8 @@ static int tg3_phy_reset(struct tg3 *tp)
tw32(TG3_CPMU_CTRL, cpmuctrl);
}
- if (tp->tg3_flags3 & TG3_FLG3_5761_5784_AX_FIXES) {
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX ||
+ GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5761_AX) {
u32 val;
val = tr32(TG3_CPMU_LSPD_1000MB_CLK);
@@ -1747,16 +1830,15 @@ static int tg3_phy_reset(struct tg3 *tp)
udelay(40);
tw32_f(TG3_CPMU_LSPD_1000MB_CLK, val);
}
-
- /* Disable GPHY autopowerdown. */
- tg3_writephy(tp, MII_TG3_MISC_SHDW,
- MII_TG3_MISC_SHDW_WREN |
- MII_TG3_MISC_SHDW_APD_SEL |
- MII_TG3_MISC_SHDW_APD_WKTM_84MS);
}
tg3_phy_apply_otp(tp);
+ if (tp->tg3_flags3 & TG3_FLG3_PHY_ENABLE_APD)
+ tg3_phy_toggle_apd(tp, true);
+ else
+ tg3_phy_toggle_apd(tp, false);
+
out:
if (tp->tg3_flags2 & TG3_FLG2_PHY_ADC_BUG) {
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
@@ -1961,7 +2043,7 @@ static int tg3_halt_cpu(struct tg3 *, u32);
static int tg3_nvram_lock(struct tg3 *);
static void tg3_nvram_unlock(struct tg3 *);
-static void tg3_power_down_phy(struct tg3 *tp)
+static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power)
{
u32 val;
@@ -1984,10 +2066,15 @@ static void tg3_power_down_phy(struct tg3 *tp)
tw32_f(GRC_MISC_CFG, val | GRC_MISC_CFG_EPHY_IDDQ);
udelay(40);
return;
- } else if (!(tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)) {
+ } else if (do_low_power) {
tg3_writephy(tp, MII_TG3_EXT_CTRL,
MII_TG3_EXT_CTRL_FORCE_LED_OFF);
- tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL,
+ MII_TG3_AUXCTL_SHDWSEL_PWRCTL |
+ MII_TG3_AUXCTL_PCTL_100TX_LPWR |
+ MII_TG3_AUXCTL_PCTL_SPR_ISOLATE |
+ MII_TG3_AUXCTL_PCTL_VREG_11V);
}
/* The PHY should not be powered down on some chips because
@@ -1999,7 +2086,8 @@ static void tg3_power_down_phy(struct tg3 *tp)
(tp->tg3_flags2 & TG3_FLG2_MII_SERDES)))
return;
- if (tp->tg3_flags3 & TG3_FLG3_5761_5784_AX_FIXES) {
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX ||
+ GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5761_AX) {
val = tr32(TG3_CPMU_LSPD_1000MB_CLK);
val &= ~CPMU_LSPD_1000MB_MACCLK_MASK;
val |= CPMU_LSPD_1000MB_MACCLK_12_5;
@@ -2009,9 +2097,47 @@ static void tg3_power_down_phy(struct tg3 *tp)
tg3_writephy(tp, MII_BMCR, BMCR_PDOWN);
}
+/* tp->lock is held. */
+static void __tg3_set_mac_addr(struct tg3 *tp, int skip_mac_1)
+{
+ u32 addr_high, addr_low;
+ int i;
+
+ addr_high = ((tp->dev->dev_addr[0] << 8) |
+ tp->dev->dev_addr[1]);
+ addr_low = ((tp->dev->dev_addr[2] << 24) |
+ (tp->dev->dev_addr[3] << 16) |
+ (tp->dev->dev_addr[4] << 8) |
+ (tp->dev->dev_addr[5] << 0));
+ for (i = 0; i < 4; i++) {
+ if (i == 1 && skip_mac_1)
+ continue;
+ tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_ADDR_0_LOW + (i * 8), addr_low);
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ for (i = 0; i < 12; i++) {
+ tw32(MAC_EXTADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_EXTADDR_0_LOW + (i * 8), addr_low);
+ }
+ }
+
+ addr_high = (tp->dev->dev_addr[0] +
+ tp->dev->dev_addr[1] +
+ tp->dev->dev_addr[2] +
+ tp->dev->dev_addr[3] +
+ tp->dev->dev_addr[4] +
+ tp->dev->dev_addr[5]) &
+ TX_BACKOFF_SEED_MASK;
+ tw32(MAC_TX_BACKOFF_SEED, addr_high);
+}
+
static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
{
u32 misc_host_ctrl;
+ bool device_should_wake, do_low_power;
/* Make sure register accesses (indirect or otherwise)
* will function correctly.
@@ -2041,15 +2167,34 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tp->dev->name, state);
return -EINVAL;
}
+
+ /* Restore the CLKREQ setting. */
+ if (tp->tg3_flags3 & TG3_FLG3_CLKREQ_BUG) {
+ u16 lnkctl;
+
+ pci_read_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_LNKCTL,
+ &lnkctl);
+ lnkctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
+ pci_write_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_LNKCTL,
+ lnkctl);
+ }
+
misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
tw32(TG3PCI_MISC_HOST_CTRL,
misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT);
+ device_should_wake = pci_pme_capable(tp->pdev, state) &&
+ device_may_wakeup(&tp->pdev->dev) &&
+ (tp->tg3_flags & TG3_FLAG_WOL_ENABLE);
+
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
+ do_low_power = false;
if ((tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) &&
!tp->link_config.phy_is_low_power) {
struct phy_device *phydev;
- u32 advertising;
+ u32 phyid, advertising;
phydev = tp->mdio_bus->phy_map[PHY_ADDR];
@@ -2066,7 +2211,7 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
ADVERTISED_10baseT_Half;
if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) ||
- (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)) {
+ device_should_wake) {
if (tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB)
advertising |=
ADVERTISED_100baseT_Half |
@@ -2079,8 +2224,19 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
phydev->advertising = advertising;
phy_start_aneg(phydev);
+
+ phyid = phydev->drv->phy_id & phydev->drv->phy_id_mask;
+ if (phyid != TG3_PHY_ID_BCMAC131) {
+ phyid &= TG3_PHY_OUI_MASK;
+ if (phyid == TG3_PHY_OUI_1 &&
+ phyid == TG3_PHY_OUI_2 &&
+ phyid == TG3_PHY_OUI_3)
+ do_low_power = true;
+ }
}
} else {
+ do_low_power = true;
+
if (tp->link_config.phy_is_low_power == 0) {
tp->link_config.phy_is_low_power = 1;
tp->link_config.orig_speed = tp->link_config.speed;
@@ -2096,6 +2252,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
}
}
+ __tg3_set_mac_addr(tp, 0);
+
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
u32 val;
@@ -2118,11 +2276,11 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
WOL_DRV_WOL |
WOL_SET_MAGIC_PKT);
- if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) {
+ if (device_should_wake) {
u32 mac_mode;
if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
- if (!(tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)) {
+ if (do_low_power) {
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
udelay(40);
}
@@ -2150,9 +2308,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
if (!(tp->tg3_flags2 & TG3_FLG2_5750_PLUS))
tw32(MAC_LED_CTRL, tp->led_ctrl);
- if (pci_pme_capable(tp->pdev, state) &&
- (tp->tg3_flags & TG3_FLAG_WOL_ENABLE))
- mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE;
+ mac_mode |= MAC_MODE_MAGIC_PKT_ENABLE;
+ if (((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) &&
+ !(tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) &&
+ ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) ||
+ (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE)))
+ mac_mode |= MAC_MODE_KEEP_FRAME_IN_WOL;
if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
mac_mode |= tp->mac_mode &
@@ -2224,10 +2385,9 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
}
}
- if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
- !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) &&
- !(tp->tg3_flags3 & TG3_FLG3_ENABLE_APE))
- tg3_power_down_phy(tp);
+ if (!(device_should_wake) &&
+ !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
+ tg3_power_down_phy(tp, do_low_power);
tg3_frob_aux_power(tp);
@@ -2250,7 +2410,7 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
- if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)
+ if (device_should_wake)
pci_enable_wake(tp->pdev, state, true);
/* Finally, set the new power state. */
@@ -2789,6 +2949,24 @@ relink:
NIC_SRAM_FIRMWARE_MBOX_MAGIC2);
}
+ /* Prevent send BD corruption. */
+ if (tp->tg3_flags3 & TG3_FLG3_CLKREQ_BUG) {
+ u16 oldlnkctl, newlnkctl;
+
+ pci_read_config_word(tp->pdev,
+ tp->pcie_cap + 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;
+ else
+ newlnkctl = oldlnkctl | PCI_EXP_LNKCTL_CLKREQ_EN;
+ if (newlnkctl != oldlnkctl)
+ pci_write_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_LNKCTL,
+ newlnkctl);
+ }
+
if (current_link_up != netif_carrier_ok(tp->dev)) {
if (current_link_up)
netif_carrier_on(tp->dev);
@@ -3765,8 +3943,7 @@ static int tg3_setup_phy(struct tg3 *tp, int force_reset)
err = tg3_setup_copper_phy(tp, force_reset);
}
- if (tp->pci_chip_rev_id == CHIPREV_ID_5784_A0 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5784_A1) {
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX) {
u32 val, scale;
val = tr32(TG3_CPMU_CLCK_STAT) & CPMU_CLCK_STAT_MAC_CLCK_MASK;
@@ -4100,12 +4277,15 @@ static int tg3_rx(struct tg3 *tp, int budget)
goto next_pkt;
}
- len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */
+ len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) -
+ ETH_FCS_LEN;
if (len > RX_COPY_THRESHOLD
- && tp->rx_offset == 2
- /* rx_offset != 2 iff this is a 5701 card running
- * in PCI-X mode [see tg3_get_invariants()] */
+ && tp->rx_offset == NET_IP_ALIGN
+ /* rx_offset will likely not equal NET_IP_ALIGN
+ * if this is a 5701 card running in PCI-X mode
+ * [see tg3_get_invariants()]
+ */
) {
int skb_size;
@@ -4125,11 +4305,12 @@ static int tg3_rx(struct tg3 *tp, int budget)
tg3_recycle_rx(tp, opaque_key,
desc_idx, *post_ptr);
- copy_skb = netdev_alloc_skb(tp->dev, len + 2);
+ copy_skb = netdev_alloc_skb(tp->dev,
+ len + TG3_RAW_IP_ALIGN);
if (copy_skb == NULL)
goto drop_it_no_recycle;
- skb_reserve(copy_skb, 2);
+ skb_reserve(copy_skb, TG3_RAW_IP_ALIGN);
skb_put(copy_skb, len);
pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE);
skb_copy_from_linear_data(skb, copy_skb->data, len);
@@ -4157,7 +4338,6 @@ static int tg3_rx(struct tg3 *tp, int budget)
#endif
netif_receive_skb(skb);
- tp->dev->last_rx = jiffies;
received++;
budget--;
@@ -4271,7 +4451,7 @@ static int tg3_poll(struct napi_struct *napi, int budget)
sblk->status &= ~SD_STATUS_UPDATED;
if (likely(!tg3_has_work(tp))) {
- netif_rx_complete(tp->dev, napi);
+ netif_rx_complete(napi);
tg3_restart_ints(tp);
break;
}
@@ -4281,7 +4461,7 @@ static int tg3_poll(struct napi_struct *napi, int budget)
tx_recovery:
/* work_done is guaranteed to be less than budget. */
- netif_rx_complete(tp->dev, napi);
+ netif_rx_complete(napi);
schedule_work(&tp->reset_task);
return work_done;
}
@@ -4330,7 +4510,7 @@ static irqreturn_t tg3_msi_1shot(int irq, void *dev_id)
prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]);
if (likely(!tg3_irq_sync(tp)))
- netif_rx_schedule(dev, &tp->napi);
+ netif_rx_schedule(&tp->napi);
return IRQ_HANDLED;
}
@@ -4355,7 +4535,7 @@ static irqreturn_t tg3_msi(int irq, void *dev_id)
*/
tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
if (likely(!tg3_irq_sync(tp)))
- netif_rx_schedule(dev, &tp->napi);
+ netif_rx_schedule(&tp->napi);
return IRQ_RETVAL(1);
}
@@ -4397,7 +4577,7 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id)
sblk->status &= ~SD_STATUS_UPDATED;
if (likely(tg3_has_work(tp))) {
prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]);
- netif_rx_schedule(dev, &tp->napi);
+ netif_rx_schedule(&tp->napi);
} else {
/* No work, shared interrupt perhaps? re-enable
* interrupts, and flush that PCI write
@@ -4443,7 +4623,7 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id)
tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
if (tg3_irq_sync(tp))
goto out;
- if (netif_rx_schedule_prep(dev, &tp->napi)) {
+ if (netif_rx_schedule_prep(&tp->napi)) {
prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]);
/* Update last_tag to mark that this status has been
* seen. Because interrupt may be shared, we may be
@@ -4451,7 +4631,7 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id)
* if tg3_poll() is not scheduled.
*/
tp->last_tag = sblk->status_tag;
- __netif_rx_schedule(dev, &tp->napi);
+ __netif_rx_schedule(&tp->napi);
}
out:
return IRQ_RETVAL(handled);
@@ -5557,6 +5737,13 @@ static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
event = APE_EVENT_STATUS_STATE_START;
break;
case RESET_KIND_SHUTDOWN:
+ /* With the interface we are currently using,
+ * APE does not track driver state. Wiping
+ * out the HOST SEGMENT SIGNATURE forces
+ * the APE to assume OS absent status.
+ */
+ tg3_ape_write32(tp, TG3_APE_HOST_SEG_SIG, 0x0);
+
event = APE_EVENT_STATUS_STATE_UNLOAD;
break;
case RESET_KIND_SUSPEND:
@@ -5721,17 +5908,19 @@ static void tg3_restore_pci_state(struct tg3 *tp)
pci_write_config_word(tp->pdev, PCI_COMMAND, tp->pci_cmd);
- if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)
- pcie_set_readrq(tp->pdev, 4096);
- else {
- pci_write_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE,
- tp->pci_cacheline_sz);
- pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER,
- tp->pci_lat_timer);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) {
+ if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)
+ pcie_set_readrq(tp->pdev, 4096);
+ else {
+ pci_write_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE,
+ tp->pci_cacheline_sz);
+ pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER,
+ tp->pci_lat_timer);
+ }
}
/* Make sure PCI-X relaxed ordering bit is clear. */
- if (tp->pcix_cap) {
+ if (tp->tg3_flags & TG3_FLAG_PCIX_MODE) {
u16 pcix_cmd;
pci_read_config_word(tp->pdev, tp->pcix_cap + PCI_X_CMD,
@@ -5788,11 +5977,7 @@ static int tg3_chip_reset(struct tg3 *tp)
tg3_save_pci_state(tp);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ (tp->tg3_flags3 & TG3_FLG3_5755_PLUS))
tw32(GRC_FASTBOOT_PC, 0);
/*
@@ -5871,7 +6056,7 @@ static int tg3_chip_reset(struct tg3 *tp)
udelay(120);
- if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
+ if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && tp->pcie_cap) {
if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) {
int i;
u32 cfg_val;
@@ -5884,8 +6069,23 @@ static int tg3_chip_reset(struct tg3 *tp)
pci_write_config_dword(tp->pdev, 0xc4,
cfg_val | (1 << 15));
}
- /* Set PCIE max payload size and clear error status. */
- pci_write_config_dword(tp->pdev, 0xd8, 0xf5000);
+
+ /* Set PCIE max payload size to 128 bytes and
+ * clear the "no snoop" and "relaxed ordering" bits.
+ */
+ pci_write_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_DEVCTL,
+ 0);
+
+ pcie_set_readrq(tp->pdev, 4096);
+
+ /* Clear error status */
+ pci_write_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_CED |
+ PCI_EXP_DEVSTA_NFED |
+ PCI_EXP_DEVSTA_FED |
+ PCI_EXP_DEVSTA_URD);
}
tg3_restore_pci_state(tp);
@@ -6883,43 +7083,6 @@ static int tg3_load_tso_firmware(struct tg3 *tp)
}
-/* tp->lock is held. */
-static void __tg3_set_mac_addr(struct tg3 *tp, int skip_mac_1)
-{
- u32 addr_high, addr_low;
- int i;
-
- addr_high = ((tp->dev->dev_addr[0] << 8) |
- tp->dev->dev_addr[1]);
- addr_low = ((tp->dev->dev_addr[2] << 24) |
- (tp->dev->dev_addr[3] << 16) |
- (tp->dev->dev_addr[4] << 8) |
- (tp->dev->dev_addr[5] << 0));
- for (i = 0; i < 4; i++) {
- if (i == 1 && skip_mac_1)
- continue;
- tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high);
- tw32(MAC_ADDR_0_LOW + (i * 8), addr_low);
- }
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
- for (i = 0; i < 12; i++) {
- tw32(MAC_EXTADDR_0_HIGH + (i * 8), addr_high);
- tw32(MAC_EXTADDR_0_LOW + (i * 8), addr_low);
- }
- }
-
- addr_high = (tp->dev->dev_addr[0] +
- tp->dev->dev_addr[1] +
- tp->dev->dev_addr[2] +
- tp->dev->dev_addr[3] +
- tp->dev->dev_addr[4] +
- tp->dev->dev_addr[5]) &
- TX_BACKOFF_SEED_MASK;
- tw32(MAC_TX_BACKOFF_SEED, addr_high);
-}
-
static int tg3_set_mac_addr(struct net_device *dev, void *p)
{
struct tg3 *tp = netdev_priv(dev);
@@ -7024,8 +7187,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tg3_write_sig_legacy(tp, RESET_KIND_INIT);
- if (tp->pci_chip_rev_id == CHIPREV_ID_5784_A0 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5784_A1) {
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5784_AX) {
val = tr32(TG3_CPMU_CTRL);
val &= ~(CPMU_CTRL_LINK_AWARE_MODE | CPMU_CTRL_LINK_IDLE_MODE);
tw32(TG3_CPMU_CTRL, val);
@@ -7091,8 +7253,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
return err;
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) {
/* This value is determined during the probe time DMA
* engine test, tg3_test_dma.
*/
@@ -7332,7 +7493,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
RDMAC_MODE_LNGREAD_ENAB);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
rdmac_mode |= RDMAC_MODE_BD_SBD_CRPT_ENAB |
RDMAC_MODE_MBUF_RBD_CRPT_ENAB |
RDMAC_MODE_MBUF_SBD_CRPT_ENAB;
@@ -7354,7 +7516,11 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST;
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
- rdmac_mode |= (1 << 27);
+ rdmac_mode |= RDMAC_MODE_IPV4_LSO_EN;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
+ rdmac_mode |= RDMAC_MODE_IPV6_LSO_EN;
/* Receive/send statistics. */
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
@@ -7501,11 +7667,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
}
/* Enable host coalescing bug fix */
- if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) ||
- (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) ||
- (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) ||
- (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) ||
- (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785))
+ if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
val |= WDMAC_MODE_STATUS_TAG_FIX;
tw32_f(WDMAC_MODE, val);
@@ -7566,10 +7728,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
udelay(100);
tp->rx_mode = RX_MODE_ENABLE;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
tp->rx_mode |= RX_MODE_IPV6_CSUM_ENABLE;
tw32_f(MAC_RX_MODE, tp->rx_mode);
@@ -9066,7 +9225,8 @@ static void tg3_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
else
wol->supported = 0;
wol->wolopts = 0;
- if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE)
+ if ((tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
+ device_can_wakeup(&tp->pdev->dev))
wol->wolopts = WAKE_MAGIC;
memset(&wol->sopass, 0, sizeof(wol->sopass));
}
@@ -9116,14 +9276,15 @@ static int tg3_set_tso(struct net_device *dev, u32 value)
return -EINVAL;
return 0;
}
- if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) {
+ if ((dev->features & NETIF_F_IPV6_CSUM) &&
+ (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)) {
if (value) {
dev->features |= NETIF_F_TSO6;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
dev->features |= NETIF_F_TSO_ECN;
} else
dev->features &= ~(NETIF_F_TSO6 | NETIF_F_TSO_ECN);
@@ -9238,12 +9399,12 @@ static void tg3_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam
epause->autoneg = (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) != 0;
- if (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_RX)
+ if (tp->link_config.active_flowctrl & FLOW_CTRL_RX)
epause->rx_pause = 1;
else
epause->rx_pause = 0;
- if (tp->link_config.active_flowctrl & TG3_FLOW_CTRL_TX)
+ if (tp->link_config.active_flowctrl & FLOW_CTRL_TX)
epause->tx_pause = 1;
else
epause->tx_pause = 0;
@@ -9294,14 +9455,14 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
}
} else {
if (epause->rx_pause)
- tp->link_config.flowctrl |= TG3_FLOW_CTRL_RX;
+ tp->link_config.flowctrl |= FLOW_CTRL_RX;
else
- tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_RX;
+ tp->link_config.flowctrl &= ~FLOW_CTRL_RX;
if (epause->tx_pause)
- tp->link_config.flowctrl |= TG3_FLOW_CTRL_TX;
+ tp->link_config.flowctrl |= FLOW_CTRL_TX;
else
- tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_TX;
+ tp->link_config.flowctrl &= ~FLOW_CTRL_TX;
if (netif_running(dev))
tg3_setup_flow_control(tp, 0, 0);
@@ -9321,13 +9482,13 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
else
tp->tg3_flags &= ~TG3_FLAG_PAUSE_AUTONEG;
if (epause->rx_pause)
- tp->link_config.flowctrl |= TG3_FLOW_CTRL_RX;
+ tp->link_config.flowctrl |= FLOW_CTRL_RX;
else
- tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_RX;
+ tp->link_config.flowctrl &= ~FLOW_CTRL_RX;
if (epause->tx_pause)
- tp->link_config.flowctrl |= TG3_FLOW_CTRL_TX;
+ tp->link_config.flowctrl |= FLOW_CTRL_TX;
else
- tp->link_config.flowctrl &= ~TG3_FLOW_CTRL_TX;
+ tp->link_config.flowctrl &= ~FLOW_CTRL_TX;
if (netif_running(dev)) {
tg3_halt(tp, RESET_KIND_SHUTDOWN, 1);
@@ -9378,11 +9539,7 @@ static int tg3_set_tx_csum(struct net_device *dev, u32 data)
return 0;
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
ethtool_op_set_tx_ipv6_csum(dev, data);
else
ethtool_op_set_tx_csum(dev, data);
@@ -9899,18 +10056,13 @@ static int tg3_test_memory(struct tg3 *tp)
int err = 0;
int i;
- if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
- mem_tbl = mem_tbl_5755;
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
- mem_tbl = mem_tbl_5906;
- else
- mem_tbl = mem_tbl_5705;
- } else
+ if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
+ mem_tbl = mem_tbl_5755;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ mem_tbl = mem_tbl_5906;
+ else if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)
+ mem_tbl = mem_tbl_5705;
+ else
mem_tbl = mem_tbl_570x;
for (i = 0; mem_tbl[i].offset != 0xffffffff; i++) {
@@ -10110,9 +10262,11 @@ static int tg3_test_loopback(struct tg3 *tp)
if (err)
return TG3_LOOPBACK_FAILED;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ /* Turn off gphy autopowerdown. */
+ if (tp->tg3_flags3 & TG3_FLG3_PHY_ENABLE_APD)
+ tg3_phy_toggle_apd(tp, false);
+
+ if (tp->tg3_flags & TG3_FLAG_CPMU_PRESENT) {
int i;
u32 status;
@@ -10139,9 +10293,7 @@ static int tg3_test_loopback(struct tg3 *tp)
if (tg3_run_loopback(tp, TG3_MAC_LOOPBACK))
err |= TG3_MAC_LOOPBACK_FAILED;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ if (tp->tg3_flags & TG3_FLAG_CPMU_PRESENT) {
tw32(TG3_CPMU_CTRL, cpmuctrl);
/* Release the mutex */
@@ -10154,6 +10306,10 @@ static int tg3_test_loopback(struct tg3 *tp)
err |= TG3_PHY_LOOPBACK_FAILED;
}
+ /* Re-enable gphy autopowerdown. */
+ if (tp->tg3_flags3 & TG3_FLG3_PHY_ENABLE_APD)
+ tg3_phy_toggle_apd(tp, true);
+
return err;
}
@@ -10756,6 +10912,102 @@ static void __devinit tg3_get_5906_nvram_info(struct tg3 *tp)
tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;
}
+static void __devinit tg3_get_57780_nvram_info(struct tg3 *tp)
+{
+ u32 nvcfg1;
+
+ nvcfg1 = tr32(NVRAM_CFG1);
+
+ switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
+ case FLASH_5787VENDOR_ATMEL_EEPROM_376KHZ:
+ case FLASH_5787VENDOR_MICRO_EEPROM_376KHZ:
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;
+
+ nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS;
+ tw32(NVRAM_CFG1, nvcfg1);
+ return;
+ case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED:
+ case FLASH_57780VENDOR_ATMEL_AT45DB011D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB011B:
+ case FLASH_57780VENDOR_ATMEL_AT45DB021D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB021B:
+ case FLASH_57780VENDOR_ATMEL_AT45DB041D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB041B:
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->tg3_flags2 |= TG3_FLG2_FLASH;
+
+ switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
+ case FLASH_5752VENDOR_ATMEL_FLASH_BUFFERED:
+ case FLASH_57780VENDOR_ATMEL_AT45DB011D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB011B:
+ tp->nvram_size = TG3_NVRAM_SIZE_128KB;
+ break;
+ case FLASH_57780VENDOR_ATMEL_AT45DB021D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB021B:
+ tp->nvram_size = TG3_NVRAM_SIZE_256KB;
+ break;
+ case FLASH_57780VENDOR_ATMEL_AT45DB041D:
+ case FLASH_57780VENDOR_ATMEL_AT45DB041B:
+ tp->nvram_size = TG3_NVRAM_SIZE_512KB;
+ break;
+ }
+ break;
+ case FLASH_5752VENDOR_ST_M45PE10:
+ case FLASH_5752VENDOR_ST_M45PE20:
+ case FLASH_5752VENDOR_ST_M45PE40:
+ tp->nvram_jedecnum = JEDEC_ST;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->tg3_flags2 |= TG3_FLG2_FLASH;
+
+ switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
+ case FLASH_5752VENDOR_ST_M45PE10:
+ tp->nvram_size = TG3_NVRAM_SIZE_128KB;
+ break;
+ case FLASH_5752VENDOR_ST_M45PE20:
+ tp->nvram_size = TG3_NVRAM_SIZE_256KB;
+ break;
+ case FLASH_5752VENDOR_ST_M45PE40:
+ tp->nvram_size = TG3_NVRAM_SIZE_512KB;
+ break;
+ }
+ break;
+ default:
+ return;
+ }
+
+ switch (nvcfg1 & NVRAM_CFG1_5752PAGE_SIZE_MASK) {
+ case FLASH_5752PAGE_SIZE_256:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+ tp->nvram_pagesize = 256;
+ break;
+ case FLASH_5752PAGE_SIZE_512:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+ tp->nvram_pagesize = 512;
+ break;
+ case FLASH_5752PAGE_SIZE_1K:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+ tp->nvram_pagesize = 1024;
+ break;
+ case FLASH_5752PAGE_SIZE_2K:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+ tp->nvram_pagesize = 2048;
+ break;
+ case FLASH_5752PAGE_SIZE_4K:
+ tp->tg3_flags3 |= TG3_FLG3_NO_NVRAM_ADDR_TRANS;
+ tp->nvram_pagesize = 4096;
+ break;
+ case FLASH_5752PAGE_SIZE_264:
+ tp->nvram_pagesize = 264;
+ break;
+ case FLASH_5752PAGE_SIZE_528:
+ tp->nvram_pagesize = 528;
+ break;
+ }
+}
+
/* Chips other than 5700/5701 use the NVRAM for fetching info. */
static void __devinit tg3_nvram_init(struct tg3 *tp)
{
@@ -10796,6 +11048,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp)
tg3_get_5761_nvram_info(tp);
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tg3_get_5906_nvram_info(tp);
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
+ tg3_get_57780_nvram_info(tp);
else
tg3_get_nvram_info(tp);
@@ -11116,12 +11370,8 @@ static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len,
if (i == (len - 4))
nvram_cmd |= NVRAM_CMD_LAST;
- if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) &&
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 &&
+ !(tp->tg3_flags3 & TG3_FLG3_5755_PLUS) &&
(tp->nvram_jedecnum == JEDEC_ST) &&
(nvram_cmd & NVRAM_CMD_FIRST)) {
@@ -11296,10 +11546,9 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
if (val & VCPU_CFGSHDW_ASPM_DBNC)
tp->tg3_flags |= TG3_FLAG_ASPM_WORKAROUND;
if ((val & VCPU_CFGSHDW_WOL_ENABLE) &&
- (val & VCPU_CFGSHDW_WOL_MAGPKT) &&
- device_may_wakeup(&tp->pdev->dev))
+ (val & VCPU_CFGSHDW_WOL_MAGPKT))
tp->tg3_flags |= TG3_FLAG_WOL_ENABLE;
- return;
+ goto done;
}
tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val);
@@ -11421,15 +11670,17 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)
tp->tg3_flags2 |= TG3_FLG2_ASF_NEW_HANDSHAKE;
}
- if (nic_cfg & NIC_SRAM_DATA_CFG_APE_ENABLE)
+
+ if ((nic_cfg & NIC_SRAM_DATA_CFG_APE_ENABLE) &&
+ (tp->tg3_flags2 & TG3_FLG2_5750_PLUS))
tp->tg3_flags3 |= TG3_FLG3_ENABLE_APE;
+
if (tp->tg3_flags2 & TG3_FLG2_ANY_SERDES &&
!(nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL))
tp->tg3_flags &= ~TG3_FLAG_WOL_CAP;
if ((tp->tg3_flags & TG3_FLAG_WOL_CAP) &&
- (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE) &&
- device_may_wakeup(&tp->pdev->dev))
+ (nic_cfg & NIC_SRAM_DATA_CFG_WOL_ENABLE))
tp->tg3_flags |= TG3_FLAG_WOL_ENABLE;
if (cfg2 & (1 << 17))
@@ -11440,6 +11691,11 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
if (cfg2 & (1 << 18))
tp->tg3_flags2 |= TG3_FLG2_SERDES_PREEMPHASIS;
+ if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
+ GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX)) &&
+ (cfg2 & NIC_SRAM_DATA_CFG_2_APD_EN))
+ tp->tg3_flags3 |= TG3_FLG3_PHY_ENABLE_APD;
+
if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
u32 cfg3;
@@ -11455,6 +11711,10 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
if (cfg4 & NIC_SRAM_RGMII_EXT_IBND_TX_EN)
tp->tg3_flags3 |= TG3_FLG3_RGMII_EXT_IBND_TX_EN;
}
+done:
+ device_init_wakeup(&tp->pdev->dev, tp->tg3_flags & TG3_FLAG_WOL_CAP);
+ device_set_wakeup_enable(&tp->pdev->dev,
+ tp->tg3_flags & TG3_FLAG_WOL_ENABLE);
}
static int __devinit tg3_issue_otp_command(struct tg3 *tp, u32 cmd)
@@ -11751,6 +12011,51 @@ static int __devinit tg3_fw_img_is_valid(struct tg3 *tp, u32 offset)
return 1;
}
+static void __devinit tg3_read_sb_ver(struct tg3 *tp, u32 val)
+{
+ u32 offset, major, minor, build;
+
+ tp->fw_ver[0] = 's';
+ tp->fw_ver[1] = 'b';
+ tp->fw_ver[2] = '\0';
+
+ if ((val & TG3_EEPROM_SB_FORMAT_MASK) != TG3_EEPROM_SB_FORMAT_1)
+ return;
+
+ switch (val & TG3_EEPROM_SB_REVISION_MASK) {
+ case TG3_EEPROM_SB_REVISION_0:
+ offset = TG3_EEPROM_SB_F1R0_EDH_OFF;
+ break;
+ case TG3_EEPROM_SB_REVISION_2:
+ offset = TG3_EEPROM_SB_F1R2_EDH_OFF;
+ break;
+ case TG3_EEPROM_SB_REVISION_3:
+ offset = TG3_EEPROM_SB_F1R3_EDH_OFF;
+ break;
+ default:
+ return;
+ }
+
+ if (tg3_nvram_read_swab(tp, offset, &val))
+ return;
+
+ build = (val & TG3_EEPROM_SB_EDH_BLD_MASK) >>
+ TG3_EEPROM_SB_EDH_BLD_SHFT;
+ major = (val & TG3_EEPROM_SB_EDH_MAJ_MASK) >>
+ TG3_EEPROM_SB_EDH_MAJ_SHFT;
+ minor = val & TG3_EEPROM_SB_EDH_MIN_MASK;
+
+ if (minor > 99 || build > 26)
+ return;
+
+ snprintf(&tp->fw_ver[2], 30, " v%d.%02d", major, minor);
+
+ if (build > 0) {
+ tp->fw_ver[8] = 'a' + build - 1;
+ tp->fw_ver[9] = '\0';
+ }
+}
+
static void __devinit tg3_read_fw_ver(struct tg3 *tp)
{
u32 val, offset, start;
@@ -11760,8 +12065,12 @@ static void __devinit tg3_read_fw_ver(struct tg3 *tp)
if (tg3_nvram_read_swab(tp, 0, &val))
return;
- if (val != TG3_EEPROM_MAGIC)
+ if (val != TG3_EEPROM_MAGIC) {
+ if ((val & TG3_EEPROM_MAGIC_FW_MSK) == TG3_EEPROM_MAGIC_FW)
+ tg3_read_sb_ver(tp, val);
+
return;
+ }
if (tg3_nvram_read_swab(tp, 0xc, &offset) ||
tg3_nvram_read_swab(tp, 0x4, &start))
@@ -11849,11 +12158,10 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
{ },
};
u32 misc_ctrl_reg;
- u32 cacheline_sz_reg;
u32 pci_state_reg, grc_misc_cfg;
u32 val;
u16 pci_cmd;
- int err, pcie_cap;
+ int err;
/* Force memory write invalidate off. If we leave it on,
* then on 5700_BX chips we have to enable a workaround.
@@ -11882,7 +12190,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_read_config_dword(tp->pdev, TG3PCI_PRODID_ASICREV,
&prod_id_asic_rev);
- tp->pci_chip_rev_id = prod_id_asic_rev & PROD_ID_ASIC_REV_MASK;
+ tp->pci_chip_rev_id = prod_id_asic_rev;
}
/* Wrong chip ID in 5752 A0. This code can be removed later
@@ -12019,26 +12327,23 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
tp->misc_host_ctrl);
- pci_read_config_dword(tp->pdev, TG3PCI_CACHELINESZ,
- &cacheline_sz_reg);
-
- tp->pci_cacheline_sz = (cacheline_sz_reg >> 0) & 0xff;
- tp->pci_lat_timer = (cacheline_sz_reg >> 8) & 0xff;
- tp->pci_hdr_type = (cacheline_sz_reg >> 16) & 0xff;
- tp->pci_bist = (cacheline_sz_reg >> 24) & 0xff;
-
if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5714))
tp->pdev_peer = tg3_find_peer(tp);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
+ /* Intentionally exclude ASIC_REV_5906 */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
+ tp->tg3_flags3 |= TG3_FLG3_5755_PLUS;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
+ (tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS))
tp->tg3_flags2 |= TG3_FLG2_5750_PLUS;
@@ -12046,6 +12351,18 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
(tp->tg3_flags2 & TG3_FLG2_5750_PLUS))
tp->tg3_flags2 |= TG3_FLG2_5705_PLUS;
+ /* 5700 B0 chips do not support checksumming correctly due
+ * to hardware bugs.
+ */
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0)
+ tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS;
+ else {
+ tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS;
+ tp->dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
+ if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
+ tp->dev->features |= NETIF_F_IPV6_CSUM;
+ }
+
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
tp->tg3_flags |= TG3_FLAG_SUPPORT_MSI;
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX ||
@@ -12055,11 +12372,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->pdev_peer == tp->pdev))
tp->tg3_flags &= ~TG3_FLAG_SUPPORT_MSI;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
@@ -12076,21 +12389,41 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS))
tp->tg3_flags2 |= TG3_FLG2_JUMBO_CAPABLE;
- pcie_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_EXP);
- if (pcie_cap != 0) {
+ pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE,
+ &pci_state_reg);
+
+ tp->pcie_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_EXP);
+ if (tp->pcie_cap != 0) {
+ u16 lnkctl;
+
tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS;
pcie_set_readrq(tp->pdev, 4096);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
- u16 lnkctl;
-
- pci_read_config_word(tp->pdev,
- pcie_cap + PCI_EXP_LNKCTL,
- &lnkctl);
- if (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN)
+ pci_read_config_word(tp->pdev,
+ tp->pcie_cap + PCI_EXP_LNKCTL,
+ &lnkctl);
+ if (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->tg3_flags2 &= ~TG3_FLG2_HW_TSO_2;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
+ tp->tg3_flags3 |= TG3_FLG3_CLKREQ_BUG;
}
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS;
+ } else if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ||
+ (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) {
+ tp->pcix_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_PCIX);
+ if (!tp->pcix_cap) {
+ printk(KERN_ERR PFX "Cannot find PCI-X "
+ "capability, aborting.\n");
+ return -EIO;
+ }
+
+ if (!(pci_state_reg & PCISTATE_CONV_PCI_MODE))
+ tp->tg3_flags |= TG3_FLAG_PCIX_MODE;
}
/* If we have an AMD 762 or VIA K8T800 chipset, write
@@ -12103,42 +12436,29 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS))
tp->tg3_flags |= TG3_FLAG_MBOX_WRITE_REORDER;
+ pci_read_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE,
+ &tp->pci_cacheline_sz);
+ pci_read_config_byte(tp->pdev, PCI_LATENCY_TIMER,
+ &tp->pci_lat_timer);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 &&
tp->pci_lat_timer < 64) {
tp->pci_lat_timer = 64;
-
- cacheline_sz_reg = ((tp->pci_cacheline_sz & 0xff) << 0);
- cacheline_sz_reg |= ((tp->pci_lat_timer & 0xff) << 8);
- cacheline_sz_reg |= ((tp->pci_hdr_type & 0xff) << 16);
- cacheline_sz_reg |= ((tp->pci_bist & 0xff) << 24);
-
- pci_write_config_dword(tp->pdev, TG3PCI_CACHELINESZ,
- cacheline_sz_reg);
- }
-
- if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ||
- (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) {
- tp->pcix_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_PCIX);
- if (!tp->pcix_cap) {
- printk(KERN_ERR PFX "Cannot find PCI-X "
- "capability, aborting.\n");
- return -EIO;
- }
+ pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER,
+ tp->pci_lat_timer);
}
- pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE,
- &pci_state_reg);
-
- if (tp->pcix_cap && (pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) {
- tp->tg3_flags |= TG3_FLAG_PCIX_MODE;
+ if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) {
+ /* 5700 BX chips need to have their TX producer index
+ * mailboxes written twice to workaround a bug.
+ */
+ tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG;
- /* If this is a 5700 BX chipset, and we are in PCI-X
- * mode, enable register write workaround.
+ /* If we are in PCI-X mode, enable register write workaround.
*
* The workaround is to use indirect register accesses
* for all chip writes not to mailbox registers.
*/
- if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX) {
+ if (tp->tg3_flags & TG3_FLAG_PCIX_MODE) {
u32 pm_reg;
tp->tg3_flags |= TG3_FLAG_PCIX_TARGET_HWBUG;
@@ -12163,12 +12483,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
}
}
- /* 5700 BX chips need to have their TX producer index mailboxes
- * written twice to workaround a bug.
- */
- if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5700_BX)
- tp->tg3_flags |= TG3_FLAG_TXD_MBOX_HWBUG;
-
if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0)
tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED;
if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0)
@@ -12263,16 +12577,10 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT;
- if (tp->pci_chip_rev_id == CHIPREV_ID_5784_A0 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5784_A1 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5761_A0 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5761_A1)
- tp->tg3_flags3 |= TG3_FLG3_5761_5784_AX_FIXES;
- }
-
/* Set up tp->grc_local_ctrl before calling tg3_set_power_state().
* GPIO1 driven high will bring 5700's external PHY out of reset.
* It is also used as eeprom write protect on LOMs.
@@ -12288,7 +12596,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752)
tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE3;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755)
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_UART_SEL;
if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761) {
@@ -12308,12 +12617,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
return err;
}
- /* 5700 B0 chips do not support checksumming correctly due
- * to hardware bugs.
- */
- if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0)
- tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS;
-
/* Derive initial jumbo mode from MTU assigned in
* ether_setup() via the alloc_etherdev() call
*/
@@ -12346,7 +12649,10 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0)
tp->tg3_flags2 |= TG3_FLG2_PHY_5704_A0_BUG;
- if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
+ if ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57780) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
@@ -12356,8 +12662,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG;
if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5755M)
tp->tg3_flags2 |= TG3_FLG2_PHY_ADJUST_TRIM;
- } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785)
+ } else
tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG;
}
@@ -12378,7 +12683,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX)
tp->coalesce_mode |= HOSTCC_MODE_32BYTE;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB;
err = tg3_mdio_init(tp);
@@ -12463,6 +12769,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
(tp->pdev->device == PCI_DEVICE_ID_TIGON3_5751F ||
tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F ||
tp->pdev->device == PCI_DEVICE_ID_TIGON3_5787F)) ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57790 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->tg3_flags |= TG3_FLAG_10_100_ONLY;
@@ -12512,20 +12819,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
else
tp->tg3_flags &= ~TG3_FLAG_POLL_SERDES;
- /* All chips before 5787 can get confused if TX buffers
- * straddle the 4GB address boundary in some cases.
- */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
- tp->dev->hard_start_xmit = tg3_start_xmit;
- else
- tp->dev->hard_start_xmit = tg3_start_xmit_dma_bug;
-
- tp->rx_offset = 2;
+ tp->rx_offset = NET_IP_ALIGN;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 &&
(tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0)
tp->rx_offset = 0;
@@ -13241,18 +13535,53 @@ static void __devinit tg3_init_coal(struct tg3 *tp)
}
}
+static const struct net_device_ops tg3_netdev_ops = {
+ .ndo_open = tg3_open,
+ .ndo_stop = tg3_close,
+ .ndo_start_xmit = tg3_start_xmit,
+ .ndo_get_stats = tg3_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = tg3_set_rx_mode,
+ .ndo_set_mac_address = tg3_set_mac_addr,
+ .ndo_do_ioctl = tg3_ioctl,
+ .ndo_tx_timeout = tg3_tx_timeout,
+ .ndo_change_mtu = tg3_change_mtu,
+#if TG3_VLAN_TAG_USED
+ .ndo_vlan_rx_register = tg3_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = tg3_poll_controller,
+#endif
+};
+
+static const struct net_device_ops tg3_netdev_ops_dma_bug = {
+ .ndo_open = tg3_open,
+ .ndo_stop = tg3_close,
+ .ndo_start_xmit = tg3_start_xmit_dma_bug,
+ .ndo_get_stats = tg3_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = tg3_set_rx_mode,
+ .ndo_set_mac_address = tg3_set_mac_addr,
+ .ndo_do_ioctl = tg3_ioctl,
+ .ndo_tx_timeout = tg3_tx_timeout,
+ .ndo_change_mtu = tg3_change_mtu,
+#if TG3_VLAN_TAG_USED
+ .ndo_vlan_rx_register = tg3_vlan_rx_register,
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = tg3_poll_controller,
+#endif
+};
+
static int __devinit tg3_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static int tg3_version_printed = 0;
- resource_size_t tg3reg_base;
- unsigned long tg3reg_len;
struct net_device *dev;
struct tg3 *tp;
int err, pm_cap;
char str[40];
u64 dma_mask, persist_dma_mask;
- DECLARE_MAC_BUF(mac);
if (tg3_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -13264,13 +13593,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
return err;
}
- if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
- printk(KERN_ERR PFX "Cannot find proper PCI device "
- "base address, aborting.\n");
- err = -ENODEV;
- goto err_out_disable_pdev;
- }
-
err = pci_request_regions(pdev, DRV_MODULE_NAME);
if (err) {
printk(KERN_ERR PFX "Cannot obtain PCI resources, "
@@ -13289,9 +13611,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_free_res;
}
- tg3reg_base = pci_resource_start(pdev, 0);
- tg3reg_len = pci_resource_len(pdev, 0);
-
dev = alloc_etherdev(sizeof(*tp));
if (!dev) {
printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n");
@@ -13303,7 +13622,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
#if TG3_VLAN_TAG_USED
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_rx_register = tg3_vlan_rx_register;
#endif
tp = netdev_priv(dev);
@@ -13343,7 +13661,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
spin_lock_init(&tp->indirect_lock);
INIT_WORK(&tp->reset_task, tg3_reset_task);
- tp->regs = ioremap_nocache(tg3reg_base, tg3reg_len);
+ tp->regs = pci_ioremap_bar(pdev, BAR_0);
if (!tp->regs) {
printk(KERN_ERR PFX "Cannot map device registers, "
"aborting.\n");
@@ -13357,21 +13675,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING;
tp->tx_pending = TG3_DEF_TX_RING_PENDING;
- dev->open = tg3_open;
- dev->stop = tg3_close;
- dev->get_stats = tg3_get_stats;
- dev->set_multicast_list = tg3_set_rx_mode;
- dev->set_mac_address = tg3_set_mac_addr;
- dev->do_ioctl = tg3_ioctl;
- dev->tx_timeout = tg3_tx_timeout;
netif_napi_add(dev, &tp->napi, tg3_poll, 64);
dev->ethtool_ops = &tg3_ethtool_ops;
dev->watchdog_timeo = TG3_TX_TIMEOUT;
- dev->change_mtu = tg3_change_mtu;
dev->irq = pdev->irq;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = tg3_poll_controller;
-#endif
err = tg3_get_invariants(tp);
if (err) {
@@ -13380,6 +13687,13 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_iounmap;
}
+ if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ dev->netdev_ops = &tg3_netdev_ops;
+ else
+ dev->netdev_ops = &tg3_netdev_ops_dma_bug;
+
+
/* The EPB bridge inside 5714, 5715, and 5780 and any
* device behind the EPB cannot support DMA addresses > 40-bit.
* On 64-bit systems with IOMMU, use 40-bit dma_mask.
@@ -13439,14 +13753,16 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
* is off by default, but can be enabled using ethtool.
*/
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
- dev->features |= NETIF_F_TSO;
- if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
- (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906))
+ if (dev->features & NETIF_F_IP_CSUM)
+ dev->features |= NETIF_F_TSO;
+ if ((dev->features & NETIF_F_IPV6_CSUM) &&
+ (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2))
dev->features |= NETIF_F_TSO6;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
dev->features |= NETIF_F_TSO_ECN;
}
@@ -13466,17 +13782,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
}
if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
- if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
- printk(KERN_ERR PFX "Cannot find proper PCI device "
- "base address for APE, aborting.\n");
- err = -ENODEV;
- goto err_out_iounmap;
- }
-
- tg3reg_base = pci_resource_start(pdev, 2);
- tg3reg_len = pci_resource_len(pdev, 2);
-
- tp->aperegs = ioremap_nocache(tg3reg_base, tg3reg_len);
+ tp->aperegs = pci_ioremap_bar(pdev, BAR_2);
if (!tp->aperegs) {
printk(KERN_ERR PFX "Cannot map APE registers, "
"aborting.\n");
@@ -13504,25 +13810,9 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_apeunmap;
}
- /* Tigon3 can do ipv4 only... and some chips have buggy
- * checksumming.
- */
- if ((tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) == 0) {
- dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
- dev->features |= NETIF_F_IPV6_CSUM;
-
- tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS;
- } else
- tp->tg3_flags &= ~TG3_FLAG_RX_CHECKSUMS;
-
/* flow control autonegotiation is default behavior */
tp->tg3_flags |= TG3_FLAG_PAUSE_AUTONEG;
- tp->link_config.flowctrl = TG3_FLOW_CTRL_TX | TG3_FLOW_CTRL_RX;
+ tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
tg3_init_coal(tp);
@@ -13535,26 +13825,34 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_apeunmap;
}
- printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x PHY(%s)] "
- "(%s) %s Ethernet %s\n",
+ printk(KERN_INFO "%s: Tigon3 [partno(%s) rev %04x] (%s) MAC address %pM\n",
dev->name,
tp->board_part_number,
tp->pci_chip_rev_id,
- tg3_phy_string(tp),
tg3_bus_string(tp, str),
- ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100Base-TX" :
- ((tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) ? "1000Base-SX" :
- "10/100/1000Base-T")),
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
- printk(KERN_INFO "%s: RXcsums[%d] LinkChgREG[%d] "
- "MIirq[%d] ASF[%d] WireSpeed[%d] TSOcap[%d]\n",
+ if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)
+ printk(KERN_INFO
+ "%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
+ tp->dev->name,
+ tp->mdio_bus->phy_map[PHY_ADDR]->drv->name,
+ dev_name(&tp->mdio_bus->phy_map[PHY_ADDR]->dev));
+ else
+ printk(KERN_INFO
+ "%s: attached PHY is %s (%s Ethernet) (WireSpeed[%d])\n",
+ tp->dev->name, tg3_phy_string(tp),
+ ((tp->tg3_flags & TG3_FLAG_10_100_ONLY) ? "10/100Base-TX" :
+ ((tp->tg3_flags2 & TG3_FLG2_ANY_SERDES) ? "1000Base-SX" :
+ "10/100/1000Base-T")),
+ (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) == 0);
+
+ printk(KERN_INFO "%s: RXcsums[%d] LinkChgREG[%d] MIirq[%d] ASF[%d] TSOcap[%d]\n",
dev->name,
(tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) != 0,
(tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) != 0,
(tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) != 0,
(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0,
- (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) == 0,
(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) != 0);
printk(KERN_INFO "%s: dma_rwctrl[%08x] dma_mask[%d-bit]\n",
dev->name, tp->dma_rwctrl,
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index be252abe8985..8936edfb0438 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -38,26 +38,13 @@
#define TG3PCI_DEVICE_TIGON3_2 0x1645 /* BCM5701 */
#define TG3PCI_DEVICE_TIGON3_3 0x1646 /* BCM5702 */
#define TG3PCI_DEVICE_TIGON3_4 0x1647 /* BCM5703 */
-#define TG3PCI_COMMAND 0x00000004
-#define TG3PCI_STATUS 0x00000006
-#define TG3PCI_CCREVID 0x00000008
-#define TG3PCI_CACHELINESZ 0x0000000c
-#define TG3PCI_LATTIMER 0x0000000d
-#define TG3PCI_HEADERTYPE 0x0000000e
-#define TG3PCI_BIST 0x0000000f
-#define TG3PCI_BASE0_LOW 0x00000010
-#define TG3PCI_BASE0_HIGH 0x00000014
-/* 0x18 --> 0x2c unused */
-#define TG3PCI_SUBSYSVENID 0x0000002c
-#define TG3PCI_SUBSYSID 0x0000002e
-#define TG3PCI_ROMADDR 0x00000030
-#define TG3PCI_CAPLIST 0x00000034
-/* 0x35 --> 0x3c unused */
-#define TG3PCI_IRQ_LINE 0x0000003c
-#define TG3PCI_IRQ_PIN 0x0000003d
-#define TG3PCI_MIN_GNT 0x0000003e
-#define TG3PCI_MAX_LAT 0x0000003f
-/* 0x40 --> 0x64 unused */
+#define TG3PCI_DEVICE_TIGON3_5761S 0x1688
+#define TG3PCI_DEVICE_TIGON3_5761SE 0x1689
+#define TG3PCI_DEVICE_TIGON3_57780 0x1692
+#define TG3PCI_DEVICE_TIGON3_57760 0x1690
+#define TG3PCI_DEVICE_TIGON3_57790 0x1694
+#define TG3PCI_DEVICE_TIGON3_57720 0x168c
+/* 0x04 --> 0x64 unused */
#define TG3PCI_MSI_DATA 0x00000064
/* 0x66 --> 0x68 unused */
#define TG3PCI_MISC_HOST_CTRL 0x00000068
@@ -108,10 +95,6 @@
#define CHIPREV_ID_5752_A1 0x6001
#define CHIPREV_ID_5714_A2 0x9002
#define CHIPREV_ID_5906_A1 0xc001
-#define CHIPREV_ID_5784_A0 0x5784000
-#define CHIPREV_ID_5784_A1 0x5784001
-#define CHIPREV_ID_5761_A0 0x5761000
-#define CHIPREV_ID_5761_A1 0x5761001
#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
#define ASIC_REV_5700 0x07
#define ASIC_REV_5701 0x00
@@ -129,6 +112,7 @@
#define ASIC_REV_5784 0x5784
#define ASIC_REV_5761 0x5761
#define ASIC_REV_5785 0x5785
+#define ASIC_REV_57780 0x57780
#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
#define CHIPREV_5700_AX 0x70
#define CHIPREV_5700_BX 0x71
@@ -325,6 +309,7 @@
#define MAC_MODE_TDE_ENABLE 0x00200000
#define MAC_MODE_RDE_ENABLE 0x00400000
#define MAC_MODE_FHDE_ENABLE 0x00800000
+#define MAC_MODE_KEEP_FRAME_IN_WOL 0x01000000
#define MAC_MODE_APE_RX_EN 0x08000000
#define MAC_MODE_APE_TX_EN 0x10000000
#define MAC_STATUS 0x00000404
@@ -414,6 +399,7 @@
#define MI_COM_DATA_MASK 0x0000ffff
#define MAC_MI_STAT 0x00000450
#define MAC_MI_STAT_LNKSTAT_ATTN_ENAB 0x00000001
+#define MAC_MI_STAT_10MBPS_MODE 0x00000002
#define MAC_MI_MODE 0x00000454
#define MAC_MI_MODE_CLK_10MHZ 0x00000001
#define MAC_MI_MODE_SHORT_PREAMBLE 0x00000002
@@ -539,6 +525,100 @@
#define MAC_PHYCFG1_TXC_DRV 0x20000000
#define MAC_PHYCFG2 0x000005a4
#define MAC_PHYCFG2_INBAND_ENABLE 0x00000001
+#define MAC_PHYCFG2_EMODE_MASK_MASK 0x000001c0
+#define MAC_PHYCFG2_EMODE_MASK_AC131 0x000000c0
+#define MAC_PHYCFG2_EMODE_MASK_50610 0x00000100
+#define MAC_PHYCFG2_EMODE_MASK_RT8211 0x00000000
+#define MAC_PHYCFG2_EMODE_MASK_RT8201 0x000001c0
+#define MAC_PHYCFG2_EMODE_COMP_MASK 0x00000e00
+#define MAC_PHYCFG2_EMODE_COMP_AC131 0x00000600
+#define MAC_PHYCFG2_EMODE_COMP_50610 0x00000400
+#define MAC_PHYCFG2_EMODE_COMP_RT8211 0x00000800
+#define MAC_PHYCFG2_EMODE_COMP_RT8201 0x00000000
+#define MAC_PHYCFG2_FMODE_MASK_MASK 0x00007000
+#define MAC_PHYCFG2_FMODE_MASK_AC131 0x00006000
+#define MAC_PHYCFG2_FMODE_MASK_50610 0x00004000
+#define MAC_PHYCFG2_FMODE_MASK_RT8211 0x00000000
+#define MAC_PHYCFG2_FMODE_MASK_RT8201 0x00007000
+#define MAC_PHYCFG2_FMODE_COMP_MASK 0x00038000
+#define MAC_PHYCFG2_FMODE_COMP_AC131 0x00030000
+#define MAC_PHYCFG2_FMODE_COMP_50610 0x00008000
+#define MAC_PHYCFG2_FMODE_COMP_RT8211 0x00038000
+#define MAC_PHYCFG2_FMODE_COMP_RT8201 0x00000000
+#define MAC_PHYCFG2_GMODE_MASK_MASK 0x001c0000
+#define MAC_PHYCFG2_GMODE_MASK_AC131 0x001c0000
+#define MAC_PHYCFG2_GMODE_MASK_50610 0x00100000
+#define MAC_PHYCFG2_GMODE_MASK_RT8211 0x00000000
+#define MAC_PHYCFG2_GMODE_MASK_RT8201 0x001c0000
+#define MAC_PHYCFG2_GMODE_COMP_MASK 0x00e00000
+#define MAC_PHYCFG2_GMODE_COMP_AC131 0x00e00000
+#define MAC_PHYCFG2_GMODE_COMP_50610 0x00000000
+#define MAC_PHYCFG2_GMODE_COMP_RT8211 0x00200000
+#define MAC_PHYCFG2_GMODE_COMP_RT8201 0x00000000
+#define MAC_PHYCFG2_ACT_MASK_MASK 0x03000000
+#define MAC_PHYCFG2_ACT_MASK_AC131 0x03000000
+#define MAC_PHYCFG2_ACT_MASK_50610 0x01000000
+#define MAC_PHYCFG2_ACT_MASK_RT8211 0x03000000
+#define MAC_PHYCFG2_ACT_MASK_RT8201 0x01000000
+#define MAC_PHYCFG2_ACT_COMP_MASK 0x0c000000
+#define MAC_PHYCFG2_ACT_COMP_AC131 0x00000000
+#define MAC_PHYCFG2_ACT_COMP_50610 0x00000000
+#define MAC_PHYCFG2_ACT_COMP_RT8211 0x00000000
+#define MAC_PHYCFG2_ACT_COMP_RT8201 0x08000000
+#define MAC_PHYCFG2_QUAL_MASK_MASK 0x30000000
+#define MAC_PHYCFG2_QUAL_MASK_AC131 0x30000000
+#define MAC_PHYCFG2_QUAL_MASK_50610 0x30000000
+#define MAC_PHYCFG2_QUAL_MASK_RT8211 0x30000000
+#define MAC_PHYCFG2_QUAL_MASK_RT8201 0x30000000
+#define MAC_PHYCFG2_QUAL_COMP_MASK 0xc0000000
+#define MAC_PHYCFG2_QUAL_COMP_AC131 0x00000000
+#define MAC_PHYCFG2_QUAL_COMP_50610 0x00000000
+#define MAC_PHYCFG2_QUAL_COMP_RT8211 0x00000000
+#define MAC_PHYCFG2_QUAL_COMP_RT8201 0x00000000
+#define MAC_PHYCFG2_50610_LED_MODES \
+ (MAC_PHYCFG2_EMODE_MASK_50610 | \
+ MAC_PHYCFG2_EMODE_COMP_50610 | \
+ MAC_PHYCFG2_FMODE_MASK_50610 | \
+ MAC_PHYCFG2_FMODE_COMP_50610 | \
+ MAC_PHYCFG2_GMODE_MASK_50610 | \
+ MAC_PHYCFG2_GMODE_COMP_50610 | \
+ MAC_PHYCFG2_ACT_MASK_50610 | \
+ MAC_PHYCFG2_ACT_COMP_50610 | \
+ MAC_PHYCFG2_QUAL_MASK_50610 | \
+ MAC_PHYCFG2_QUAL_COMP_50610)
+#define MAC_PHYCFG2_AC131_LED_MODES \
+ (MAC_PHYCFG2_EMODE_MASK_AC131 | \
+ MAC_PHYCFG2_EMODE_COMP_AC131 | \
+ MAC_PHYCFG2_FMODE_MASK_AC131 | \
+ MAC_PHYCFG2_FMODE_COMP_AC131 | \
+ MAC_PHYCFG2_GMODE_MASK_AC131 | \
+ MAC_PHYCFG2_GMODE_COMP_AC131 | \
+ MAC_PHYCFG2_ACT_MASK_AC131 | \
+ MAC_PHYCFG2_ACT_COMP_AC131 | \
+ MAC_PHYCFG2_QUAL_MASK_AC131 | \
+ MAC_PHYCFG2_QUAL_COMP_AC131)
+#define MAC_PHYCFG2_RTL8211C_LED_MODES \
+ (MAC_PHYCFG2_EMODE_MASK_RT8211 | \
+ MAC_PHYCFG2_EMODE_COMP_RT8211 | \
+ MAC_PHYCFG2_FMODE_MASK_RT8211 | \
+ MAC_PHYCFG2_FMODE_COMP_RT8211 | \
+ MAC_PHYCFG2_GMODE_MASK_RT8211 | \
+ MAC_PHYCFG2_GMODE_COMP_RT8211 | \
+ MAC_PHYCFG2_ACT_MASK_RT8211 | \
+ MAC_PHYCFG2_ACT_COMP_RT8211 | \
+ MAC_PHYCFG2_QUAL_MASK_RT8211 | \
+ MAC_PHYCFG2_QUAL_COMP_RT8211)
+#define MAC_PHYCFG2_RTL8201E_LED_MODES \
+ (MAC_PHYCFG2_EMODE_MASK_RT8201 | \
+ MAC_PHYCFG2_EMODE_COMP_RT8201 | \
+ MAC_PHYCFG2_FMODE_MASK_RT8201 | \
+ MAC_PHYCFG2_FMODE_COMP_RT8201 | \
+ MAC_PHYCFG2_GMODE_MASK_RT8201 | \
+ MAC_PHYCFG2_GMODE_COMP_RT8201 | \
+ MAC_PHYCFG2_ACT_MASK_RT8201 | \
+ MAC_PHYCFG2_ACT_COMP_RT8201 | \
+ MAC_PHYCFG2_QUAL_MASK_RT8201 | \
+ MAC_PHYCFG2_QUAL_COMP_RT8201)
#define MAC_EXT_RGMII_MODE 0x000005a8
#define MAC_RGMII_MODE_TX_ENABLE 0x00000001
#define MAC_RGMII_MODE_TX_LOWPWR 0x00000002
@@ -1104,6 +1184,8 @@
#define RDMAC_MODE_MBUF_SBD_CRPT_ENAB 0x00002000
#define RDMAC_MODE_FIFO_SIZE_128 0x00020000
#define RDMAC_MODE_FIFO_LONG_BURST 0x00030000
+#define RDMAC_MODE_IPV4_LSO_EN 0x08000000
+#define RDMAC_MODE_IPV6_LSO_EN 0x10000000
#define RDMAC_STATUS 0x00004804
#define RDMAC_STATUS_TGTABORT 0x00000004
#define RDMAC_STATUS_MSTABORT 0x00000008
@@ -1550,6 +1632,12 @@
#define FLASH_5761VENDOR_ST_A_M45PE40 0x02000000
#define FLASH_5761VENDOR_ST_A_M45PE80 0x02000002
#define FLASH_5761VENDOR_ST_A_M45PE16 0x02000003
+#define FLASH_57780VENDOR_ATMEL_AT45DB011D 0x00400000
+#define FLASH_57780VENDOR_ATMEL_AT45DB011B 0x03400000
+#define FLASH_57780VENDOR_ATMEL_AT45DB021D 0x00400002
+#define FLASH_57780VENDOR_ATMEL_AT45DB021B 0x03400002
+#define FLASH_57780VENDOR_ATMEL_AT45DB041D 0x00400001
+#define FLASH_57780VENDOR_ATMEL_AT45DB041B 0x03400001
#define NVRAM_CFG1_5752PAGE_SIZE_MASK 0x70000000
#define FLASH_5752PAGE_SIZE_256 0x00000000
#define FLASH_5752PAGE_SIZE_512 0x10000000
@@ -1557,6 +1645,7 @@
#define FLASH_5752PAGE_SIZE_2K 0x30000000
#define FLASH_5752PAGE_SIZE_4K 0x40000000
#define FLASH_5752PAGE_SIZE_264 0x50000000
+#define FLASH_5752PAGE_SIZE_528 0x60000000
#define NVRAM_CFG2 0x00007018
#define NVRAM_CFG3 0x0000701c
#define NVRAM_SWARB 0x00007020
@@ -1649,6 +1738,17 @@
#define TG3_NVM_DIRTYPE_SHIFT 24
#define TG3_NVM_DIRTYPE_ASFINI 1
+#define TG3_EEPROM_SB_F1R0_EDH_OFF 0x10
+#define TG3_EEPROM_SB_F1R2_EDH_OFF 0x14
+#define TG3_EEPROM_SB_F1R2_MBA_OFF 0x10
+#define TG3_EEPROM_SB_F1R3_EDH_OFF 0x18
+#define TG3_EEPROM_SB_EDH_MAJ_MASK 0x00000700
+#define TG3_EEPROM_SB_EDH_MAJ_SHFT 8
+#define TG3_EEPROM_SB_EDH_MIN_MASK 0x000000ff
+#define TG3_EEPROM_SB_EDH_BLD_MASK 0x0000f800
+#define TG3_EEPROM_SB_EDH_BLD_SHFT 11
+
+
/* 32K Window into NIC internal memory */
#define NIC_SRAM_WIN_BASE 0x00008000
@@ -1724,6 +1824,7 @@
#define NIC_SRAM_DATA_CFG_2 0x00000d38
+#define NIC_SRAM_DATA_CFG_2_APD_EN 0x00000400
#define SHASTA_EXT_LED_MODE_MASK 0x00018000
#define SHASTA_EXT_LED_LEGACY 0x00000000
#define SHASTA_EXT_LED_SHARED 0x00008000
@@ -1792,6 +1893,11 @@
#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */
+#define MII_TG3_AUXCTL_PCTL_100TX_LPWR 0x0010
+#define MII_TG3_AUXCTL_PCTL_SPR_ISOLATE 0x0020
+#define MII_TG3_AUXCTL_PCTL_VREG_11V 0x0180
+#define MII_TG3_AUXCTL_SHDWSEL_PWRCTL 0x0002
+
#define MII_TG3_AUXCTL_MISC_WREN 0x8000
#define MII_TG3_AUXCTL_MISC_FORCE_AMDIX 0x0200
#define MII_TG3_AUXCTL_MISC_RDSEL_MISC 0x7000
@@ -1817,12 +1923,6 @@
#define MII_TG3_ISTAT 0x1a /* IRQ status register */
#define MII_TG3_IMASK 0x1b /* IRQ mask register */
-#define MII_TG3_MISC_SHDW 0x1c
-#define MII_TG3_MISC_SHDW_WREN 0x8000
-#define MII_TG3_MISC_SHDW_APD_SEL 0x2800
-
-#define MII_TG3_MISC_SHDW_APD_WKTM_84MS 0x0001
-
/* ISTAT/IMASK event bits */
#define MII_TG3_INT_LINKCHG 0x0002
#define MII_TG3_INT_SPEEDCHG 0x0004
@@ -1831,7 +1931,9 @@
#define MII_TG3_MISC_SHDW 0x1c
#define MII_TG3_MISC_SHDW_WREN 0x8000
-#define MII_TG3_MISC_SHDW_SCR5_SEL 0x1400
+
+#define MII_TG3_MISC_SHDW_APD_WKTM_84MS 0x0001
+#define MII_TG3_MISC_SHDW_APD_ENABLE 0x0020
#define MII_TG3_MISC_SHDW_APD_SEL 0x2800
#define MII_TG3_MISC_SHDW_SCR5_C125OE 0x0001
@@ -1839,9 +1941,8 @@
#define MII_TG3_MISC_SHDW_SCR5_SDTL 0x0004
#define MII_TG3_MISC_SHDW_SCR5_DLPTLM 0x0008
#define MII_TG3_MISC_SHDW_SCR5_LPED 0x0010
+#define MII_TG3_MISC_SHDW_SCR5_SEL 0x1400
-#define MII_TG3_MISC_SHDW_APD_WKTM_84MS 0x0001
-#define MII_TG3_MISC_SHDW_APD_ENABLE 0x0020
#define MII_TG3_EPHY_TEST 0x1f /* 5906 PHY register */
#define MII_TG3_EPHY_SHADOW_EN 0x80
@@ -2211,8 +2312,6 @@ struct tg3_link_config {
u8 duplex;
u8 autoneg;
u8 flowctrl;
-#define TG3_FLOW_CTRL_TX 0x01
-#define TG3_FLOW_CTRL_RX 0x02
/* Describes what we actually have. */
u8 active_flowctrl;
@@ -2507,7 +2606,6 @@ struct tg3 {
u32 tg3_flags3;
#define TG3_FLG3_NO_NVRAM_ADDR_TRANS 0x00000001
#define TG3_FLG3_ENABLE_APE 0x00000002
-#define TG3_FLG3_5761_5784_AX_FIXES 0x00000004
#define TG3_FLG3_5701_DMA_BUG 0x00000008
#define TG3_FLG3_USE_PHYLIB 0x00000010
#define TG3_FLG3_MDIOBUS_INITED 0x00000020
@@ -2516,6 +2614,9 @@ struct tg3 {
#define TG3_FLG3_RGMII_STD_IBND_DISABLE 0x00000100
#define TG3_FLG3_RGMII_EXT_IBND_RX_EN 0x00000200
#define TG3_FLG3_RGMII_EXT_IBND_TX_EN 0x00000400
+#define TG3_FLG3_CLKREQ_BUG 0x00000800
+#define TG3_FLG3_PHY_ENABLE_APD 0x00001000
+#define TG3_FLG3_5755_PLUS 0x00002000
struct timer_list timer;
u16 timer_counter;
@@ -2547,14 +2648,16 @@ struct tg3 {
/* PCI block */
u32 pci_chip_rev_id;
+ u16 pci_cmd;
u8 pci_cacheline_sz;
u8 pci_lat_timer;
- u8 pci_hdr_type;
- u8 pci_bist;
int pm_cap;
int msi_cap;
+ union {
int pcix_cap;
+ int pcie_cap;
+ };
struct mii_bus *mdio_bus;
int mdio_irq[PHY_MAX_ADDR];
@@ -2588,11 +2691,16 @@ struct tg3 {
#define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */
#define TG3_PHY_ID_BCM50610 0x143bd60
#define TG3_PHY_ID_BCMAC131 0x143bc70
-
+#define TG3_PHY_ID_RTL8211C 0x001cc910
+#define TG3_PHY_ID_RTL8201E 0x00008200
+#define TG3_PHY_ID_BCM57780 0x03625d90
+#define TG3_PHY_OUI_MASK 0xfffffc00
+#define TG3_PHY_OUI_1 0x00206000
+#define TG3_PHY_OUI_2 0x0143bc00
+#define TG3_PHY_OUI_3 0x03625c00
u32 led_ctrl;
u32 phy_otp;
- u16 pci_cmd;
char board_part_number[24];
#define TG3_VER_SIZE 32
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index c41d68761364..85ef8b744557 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -163,6 +163,11 @@
* v1.15 Apr 4, 2002 - Correct operation when aui=1 to be
* 10T half duplex no loopback
* Thanks to Gunnar Eikman
+ *
+ * Sakari Ailus <sakari.ailus@iki.fi>:
+ *
+ * v1.15a Dec 15 2008 - Remove bbuf support, it doesn't work anyway.
+ *
*******************************************************************************/
#include <linux/module.h>
@@ -213,12 +218,8 @@ static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "ThunderLAN debug mask");
-static int bbuf;
-module_param(bbuf, int, 0);
-MODULE_PARM_DESC(bbuf, "ThunderLAN use big buffer (0-1)");
-
static const char TLanSignature[] = "TLAN";
-static const char tlan_banner[] = "ThunderLAN driver v1.15\n";
+static const char tlan_banner[] = "ThunderLAN driver v1.15a\n";
static int tlan_have_pci;
static int tlan_have_eisa;
@@ -859,13 +860,8 @@ static int TLan_Init( struct net_device *dev )
priv = netdev_priv(dev);
- if ( bbuf ) {
- dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
- * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
- } else {
- dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
- * ( sizeof(TLanList) );
- }
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) );
priv->dmaStorage = pci_alloc_consistent(priv->pciDev,
dma_size, &priv->dmaStorageDMA);
priv->dmaSize = dma_size;
@@ -881,16 +877,6 @@ static int TLan_Init( struct net_device *dev )
priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
priv->txListDMA = priv->rxListDMA + sizeof(TLanList) * TLAN_NUM_RX_LISTS;
- if ( bbuf ) {
- priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
- priv->rxBufferDMA =priv->txListDMA
- + sizeof(TLanList) * TLAN_NUM_TX_LISTS;
- priv->txBuffer = priv->rxBuffer
- + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
- priv->txBufferDMA = priv->rxBufferDMA
- + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
- }
-
err = 0;
for ( i = 0; i < 6 ; i++ )
err |= TLan_EeReadByte( dev,
@@ -1094,10 +1080,10 @@ static void TLan_tx_timeout_work(struct work_struct *work)
static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
{
TLanPrivateInfo *priv = netdev_priv(dev);
- TLanList *tail_list;
dma_addr_t tail_list_phys;
- u8 *tail_buffer;
+ TLanList *tail_list;
unsigned long flags;
+ unsigned int txlen;
if ( ! priv->phyOnline ) {
TLAN_DBG( TLAN_DEBUG_TX, "TRANSMIT: %s PHY is not ready\n",
@@ -1108,6 +1094,7 @@ static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
if (skb_padto(skb, TLAN_MIN_FRAME_SIZE))
return 0;
+ txlen = max(skb->len, (unsigned int)TLAN_MIN_FRAME_SIZE);
tail_list = priv->txList + priv->txTail;
tail_list_phys = priv->txListDMA + sizeof(TLanList) * priv->txTail;
@@ -1123,18 +1110,13 @@ static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
tail_list->forward = 0;
- if ( bbuf ) {
- tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
- skb_copy_from_linear_data(skb, tail_buffer, skb->len);
- } else {
- tail_list->buffer[0].address = pci_map_single(priv->pciDev,
- skb->data, skb->len,
- PCI_DMA_TODEVICE);
- TLan_StoreSKB(tail_list, skb);
- }
+ tail_list->buffer[0].address = pci_map_single(priv->pciDev,
+ skb->data, txlen,
+ PCI_DMA_TODEVICE);
+ TLan_StoreSKB(tail_list, skb);
- tail_list->frameSize = (u16) skb->len;
- tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+ tail_list->frameSize = (u16) txlen;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) txlen;
tail_list->buffer[1].count = 0;
tail_list->buffer[1].address = 0;
@@ -1161,9 +1143,6 @@ static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
- if ( bbuf )
- dev_kfree_skb_any(skb);
-
dev->trans_start = jiffies;
return 0;
@@ -1427,15 +1406,16 @@ static u32 TLan_HandleTxEOF( struct net_device *dev, u16 host_int )
head_list = priv->txList + priv->txHead;
while (((tmpCStat = head_list->cStat ) & TLAN_CSTAT_FRM_CMP) && (ack < 255)) {
+ struct sk_buff *skb = TLan_GetSKB(head_list);
+
ack++;
- if ( ! bbuf ) {
- struct sk_buff *skb = TLan_GetSKB(head_list);
- pci_unmap_single(priv->pciDev, head_list->buffer[0].address,
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_any(skb);
- head_list->buffer[8].address = 0;
- head_list->buffer[9].address = 0;
- }
+ pci_unmap_single(priv->pciDev, head_list->buffer[0].address,
+ max(skb->len,
+ (unsigned int)TLAN_MIN_FRAME_SIZE),
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ head_list->buffer[8].address = 0;
+ head_list->buffer[9].address = 0;
if ( tmpCStat & TLAN_CSTAT_EOC )
eoc = 1;
@@ -1545,7 +1525,6 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
TLanPrivateInfo *priv = netdev_priv(dev);
u32 ack = 0;
int eoc = 0;
- u8 *head_buffer;
TLanList *head_list;
struct sk_buff *skb;
TLanList *tail_list;
@@ -1560,53 +1539,33 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
while (((tmpCStat = head_list->cStat) & TLAN_CSTAT_FRM_CMP) && (ack < 255)) {
dma_addr_t frameDma = head_list->buffer[0].address;
u32 frameSize = head_list->frameSize;
+ struct sk_buff *new_skb;
+
ack++;
if (tmpCStat & TLAN_CSTAT_EOC)
eoc = 1;
- if (bbuf) {
- skb = netdev_alloc_skb(dev, frameSize + 7);
- if ( !skb )
- goto drop_and_reuse;
-
- head_buffer = priv->rxBuffer
- + (priv->rxHead * TLAN_MAX_FRAME_SIZE);
- skb_reserve(skb, 2);
- pci_dma_sync_single_for_cpu(priv->pciDev,
- frameDma, frameSize,
- PCI_DMA_FROMDEVICE);
- skb_copy_from_linear_data(skb, head_buffer, frameSize);
- skb_put(skb, frameSize);
- dev->stats.rx_bytes += frameSize;
-
- skb->protocol = eth_type_trans( skb, dev );
- netif_rx( skb );
- } else {
- struct sk_buff *new_skb;
-
- new_skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
- if ( !new_skb )
- goto drop_and_reuse;
-
- skb = TLan_GetSKB(head_list);
- pci_unmap_single(priv->pciDev, frameDma,
- TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE);
- skb_put( skb, frameSize );
+ new_skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
+ if ( !new_skb )
+ goto drop_and_reuse;
- dev->stats.rx_bytes += frameSize;
+ skb = TLan_GetSKB(head_list);
+ pci_unmap_single(priv->pciDev, frameDma,
+ TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE);
+ skb_put( skb, frameSize );
- skb->protocol = eth_type_trans( skb, dev );
- netif_rx( skb );
+ dev->stats.rx_bytes += frameSize;
- skb_reserve( new_skb, NET_IP_ALIGN );
- head_list->buffer[0].address = pci_map_single(priv->pciDev,
- new_skb->data,
- TLAN_MAX_FRAME_SIZE,
- PCI_DMA_FROMDEVICE);
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
- TLan_StoreSKB(head_list, new_skb);
+ skb_reserve( new_skb, NET_IP_ALIGN );
+ head_list->buffer[0].address = pci_map_single(priv->pciDev,
+ new_skb->data,
+ TLAN_MAX_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
- }
+ TLan_StoreSKB(head_list, new_skb);
drop_and_reuse:
head_list->forward = 0;
head_list->cStat = 0;
@@ -1649,8 +1608,6 @@ drop_and_reuse:
}
}
- dev->last_rx = jiffies;
-
return ack;
} /* TLan_HandleRxEOF */
@@ -1991,12 +1948,7 @@ static void TLan_ResetLists( struct net_device *dev )
for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
list = priv->txList + i;
list->cStat = TLAN_CSTAT_UNUSED;
- if ( bbuf ) {
- list->buffer[0].address = priv->txBufferDMA
- + ( i * TLAN_MAX_FRAME_SIZE );
- } else {
- list->buffer[0].address = 0;
- }
+ list->buffer[0].address = 0;
list->buffer[2].count = 0;
list->buffer[2].address = 0;
list->buffer[8].address = 0;
@@ -2011,23 +1963,18 @@ static void TLan_ResetLists( struct net_device *dev )
list->cStat = TLAN_CSTAT_READY;
list->frameSize = TLAN_MAX_FRAME_SIZE;
list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
- if ( bbuf ) {
- list->buffer[0].address = priv->rxBufferDMA
- + ( i * TLAN_MAX_FRAME_SIZE );
- } else {
- skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
- if ( !skb ) {
- pr_err("TLAN: out of memory for received data.\n" );
- break;
- }
-
- skb_reserve( skb, NET_IP_ALIGN );
- list->buffer[0].address = pci_map_single(priv->pciDev,
- skb->data,
- TLAN_MAX_FRAME_SIZE,
- PCI_DMA_FROMDEVICE);
- TLan_StoreSKB(list, skb);
+ skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
+ if ( !skb ) {
+ pr_err("TLAN: out of memory for received data.\n" );
+ break;
}
+
+ skb_reserve( skb, NET_IP_ALIGN );
+ list->buffer[0].address = pci_map_single(priv->pciDev,
+ skb->data,
+ TLAN_MAX_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ TLan_StoreSKB(list, skb);
list->buffer[1].count = 0;
list->buffer[1].address = 0;
list->forward = list_phys + sizeof(TLanList);
@@ -2050,32 +1997,33 @@ static void TLan_FreeLists( struct net_device *dev )
TLanList *list;
struct sk_buff *skb;
- if ( ! bbuf ) {
- for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
- list = priv->txList + i;
- skb = TLan_GetSKB(list);
- if ( skb ) {
- pci_unmap_single(priv->pciDev,
- list->buffer[0].address, skb->len,
- PCI_DMA_TODEVICE);
- dev_kfree_skb_any( skb );
- list->buffer[8].address = 0;
- list->buffer[9].address = 0;
- }
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ skb = TLan_GetSKB(list);
+ if ( skb ) {
+ pci_unmap_single(
+ priv->pciDev,
+ list->buffer[0].address,
+ max(skb->len,
+ (unsigned int)TLAN_MIN_FRAME_SIZE),
+ PCI_DMA_TODEVICE);
+ dev_kfree_skb_any( skb );
+ list->buffer[8].address = 0;
+ list->buffer[9].address = 0;
}
+ }
- for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
- list = priv->rxList + i;
- skb = TLan_GetSKB(list);
- if ( skb ) {
- pci_unmap_single(priv->pciDev,
- list->buffer[0].address,
- TLAN_MAX_FRAME_SIZE,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any( skb );
- list->buffer[8].address = 0;
- list->buffer[9].address = 0;
- }
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ skb = TLan_GetSKB(list);
+ if ( skb ) {
+ pci_unmap_single(priv->pciDev,
+ list->buffer[0].address,
+ TLAN_MAX_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any( skb );
+ list->buffer[8].address = 0;
+ list->buffer[9].address = 0;
}
}
} /* TLan_FreeLists */
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index bf621328b601..43853e3b210e 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -296,8 +296,9 @@ static int __devinit xl_probe(struct pci_dev *pdev,
} ;
/*
- * Allowing init_trdev to allocate the dev->priv structure will align xl_private
- * on a 32 bytes boundary which we need for the rx/tx descriptors
+ * Allowing init_trdev to allocate the private data will align
+ * xl_private on a 32 bytes boundary which we need for the rx/tx
+ * descriptors
*/
dev = alloc_trdev(sizeof(struct xl_private)) ;
@@ -638,13 +639,13 @@ static int xl_open(struct net_device *dev)
/* These MUST be on 8 byte boundaries */
xl_priv->xl_tx_ring = kzalloc((sizeof(struct xl_tx_desc) * XL_TX_RING_SIZE) + 7, GFP_DMA | GFP_KERNEL);
if (xl_priv->xl_tx_ring == NULL) {
- printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers.\n",
+ printk(KERN_WARNING "%s: Not enough memory to allocate tx buffers.\n",
dev->name);
free_irq(dev->irq,dev);
return -ENOMEM;
}
xl_priv->xl_rx_ring = kzalloc((sizeof(struct xl_rx_desc) * XL_RX_RING_SIZE) +7, GFP_DMA | GFP_KERNEL);
- if (xl_priv->xl_tx_ring == NULL) {
+ if (xl_priv->xl_rx_ring == NULL) {
printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers.\n",
dev->name);
free_irq(dev->irq,dev);
@@ -669,6 +670,8 @@ static int xl_open(struct net_device *dev)
if (i==0) {
printk(KERN_WARNING "%s: Not enough memory to allocate rx buffers. Adapter disabled \n",dev->name) ;
free_irq(dev->irq,dev) ;
+ kfree(xl_priv->xl_tx_ring);
+ kfree(xl_priv->xl_rx_ring);
return -EIO ;
}
@@ -974,7 +977,6 @@ static void xl_rx(struct net_device *dev)
netif_rx(skb2) ;
} /* if multiple buffers */
- dev->last_rx = jiffies ;
} /* while packet to do */
/* Clear the updComplete interrupt */
@@ -1571,7 +1573,6 @@ static void xl_arb_cmd(struct net_device *dev)
* anyway.
*/
- dev->last_rx = jiffies ;
/* Acknowledge interrupt, this tells nic we are done with the arb */
writel(ACK_INTERRUPT | ARBCACK | LATCH_ACK, xl_mmio + MMIO_COMMAND) ;
diff --git a/drivers/net/tokenring/Kconfig b/drivers/net/tokenring/Kconfig
index e6b2e06493e7..c4137b0f808e 100644
--- a/drivers/net/tokenring/Kconfig
+++ b/drivers/net/tokenring/Kconfig
@@ -4,7 +4,7 @@
# So far, we only have PCI, ISA, and MCA token ring devices
menuconfig TR
- bool "Token Ring driver support"
+ tristate "Token Ring driver support"
depends on NETDEVICES && !UML
depends on (PCI || ISA || MCA || CCW)
select LLC
diff --git a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c
index 7a7de0469eae..b566d6d79ecd 100644
--- a/drivers/net/tokenring/abyss.c
+++ b/drivers/net/tokenring/abyss.c
@@ -99,7 +99,6 @@ static int __devinit abyss_attach(struct pci_dev *pdev, const struct pci_device_
struct net_local *tp;
int ret, pci_irq_line;
unsigned long pci_ioaddr;
- DECLARE_MAC_BUF(mac);
if (versionprinted++ == 0)
printk("%s", version);
@@ -147,8 +146,7 @@ static int __devinit abyss_attach(struct pci_dev *pdev, const struct pci_device_
abyss_read_eeprom(dev);
- printk("%s: Ring Station Address: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: Ring Station Address: %pM\n", dev->name, dev->dev_addr);
tp = netdev_priv(dev);
tp->setnselout = abyss_setnselout_pins;
diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c
index e494c63bfbd9..fa7bce6e0c6d 100644
--- a/drivers/net/tokenring/ibmtr.c
+++ b/drivers/net/tokenring/ibmtr.c
@@ -389,7 +389,6 @@ static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr)
unsigned long timeout;
static int version_printed;
#endif
- DECLARE_MAC_BUF(mac);
/* Query the adapter PIO base port which will return
* indication of where MMIO was placed. We also have a
@@ -703,8 +702,7 @@ static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr)
channel_def[cardpresent - 1], adapter_def(ti->adapter_type));
DPRINTK("using irq %d, PIOaddr %hx, %dK shared RAM.\n",
irq, PIOaddr, ti->mapped_ram_size / 2);
- DPRINTK("Hardware address : %s\n",
- print_mac(mac, dev->dev_addr));
+ DPRINTK("Hardware address : %pM\n", dev->dev_addr);
if (ti->page_mask)
DPRINTK("Shared RAM paging enabled. "
"Page size: %uK Shared Ram size %dK\n",
@@ -1741,8 +1739,6 @@ static void tr_rx(struct net_device *dev)
void __iomem *trhhdr = rbuf + offsetof(struct rec_buf, data);
u8 saddr[6];
u8 daddr[6];
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
int i;
for (i = 0 ; i < 6 ; i++)
saddr[i] = readb(trhhdr + SADDR_OFST + i);
@@ -1750,9 +1746,9 @@ static void tr_rx(struct net_device *dev)
daddr[i] = readb(trhhdr + DADDR_OFST + i);
DPRINTK("Probably non-IP frame received.\n");
DPRINTK("ssap: %02X dsap: %02X "
- "saddr: %s daddr: %$s\n",
+ "saddr: %pM daddr: %pM\n",
readb(llc + SSAP_OFST), readb(llc + DSAP_OFST),
- print_mac(mac, saddr), print_mac(mac2, daddr));
+ saddr, daddr);
}
#endif
@@ -1826,7 +1822,6 @@ static void tr_rx(struct net_device *dev)
skb->ip_summed = CHECKSUM_COMPLETE;
}
netif_rx(skb);
- dev->last_rx = jiffies;
} /*tr_rx */
/*****************************************************************************/
@@ -1842,8 +1837,8 @@ static void ibmtr_reset_timer(struct timer_list *tmr, struct net_device *dev)
/*****************************************************************************/
-void tok_rerun(unsigned long dev_addr){
-
+static void tok_rerun(unsigned long dev_addr)
+{
struct net_device *dev = (struct net_device *)dev_addr;
struct tok_info *ti = netdev_priv(dev);
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 59d1673f9387..239c75217b12 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -446,9 +446,6 @@ static int streamer_reset(struct net_device *dev)
unsigned int uaa_addr;
struct sk_buff *skb = NULL;
__u16 misr;
-#if STREAMER_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
streamer_priv = netdev_priv(dev);
streamer_mmio = streamer_priv->streamer_mmio;
@@ -577,8 +574,7 @@ static int streamer_reset(struct net_device *dev)
dev->dev_addr[i+1]= addr & 0xff;
}
#if STREAMER_DEBUG
- printk("Adapter address: %s\n",
- print_mac(mac, dev->dev_addr));
+ printk("Adapter address: %pM\n", dev->dev_addr);
#endif
}
return 0;
@@ -1013,7 +1009,6 @@ static void streamer_rx(struct net_device *dev)
/* send up to the protocol */
netif_rx(skb);
}
- dev->last_rx = jiffies;
streamer_priv->streamer_stats.rx_packets++;
streamer_priv->streamer_stats.rx_bytes += length;
} /* if skb == null */
@@ -1538,7 +1533,6 @@ static void streamer_arb_cmd(struct net_device *dev)
#if STREAMER_NETWORK_MONITOR
struct trh_hdr *mac_hdr;
- DECLARE_MAC_BUF(mac);
#endif
writew(streamer_priv->arb, streamer_mmio + LAPA);
@@ -1611,11 +1605,11 @@ static void streamer_arb_cmd(struct net_device *dev)
dev->name);
mac_hdr = tr_hdr(mac_frame);
printk(KERN_WARNING
- "%s: MAC Frame Dest. Addr: %s\n",
- dev->name, print_mac(mac, mac_hdr->daddr));
+ "%s: MAC Frame Dest. Addr: %pM\n",
+ dev->name, mac_hdr->daddr);
printk(KERN_WARNING
- "%s: MAC Frame Srce. Addr: %s\n",
- dev->name, DEV->ADDR6(mac_hdr->saddr));
+ "%s: MAC Frame Srce. Addr: %pM\n",
+ dev->name, mac_hdr->saddr);
#endif
netif_rx(mac_frame);
@@ -1850,8 +1844,6 @@ static int sprintf_info(char *buffer, struct net_device *dev)
struct streamer_parameters_table spt;
int size = 0;
int i;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
writew(streamer_priv->streamer_addr_table_addr, streamer_mmio + LAPA);
for (i = 0; i < 14; i += 2) {
@@ -1873,9 +1865,8 @@ static int sprintf_info(char *buffer, struct net_device *dev)
size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
size += sprintf(buffer + size,
- "%6s: %s : %s : %02x:%02x:%02x:%02x\n",
- dev->name, print_mac(mac, dev->dev_addr),
- print_mac(mac2, sat.node_addr),
+ "%6s: %pM : %pM : %02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr, sat.node_addr,
sat.func_addr[0], sat.func_addr[1],
sat.func_addr[2], sat.func_addr[3]);
@@ -1884,19 +1875,18 @@ static int sprintf_info(char *buffer, struct net_device *dev)
size += sprintf(buffer + size, "%6s: Physical Addr : Up Node Address : Poll Address : AccPri : Auth Src : Att Code :\n", dev->name);
size += sprintf(buffer + size,
- "%6s: %02x:%02x:%02x:%02x : %s : %s : %04x : %04x : %04x :\n",
+ "%6s: %02x:%02x:%02x:%02x : %pM : %pM : %04x : %04x : %04x :\n",
dev->name, spt.phys_addr[0], spt.phys_addr[1],
spt.phys_addr[2], spt.phys_addr[3],
- print_mac(mac, spt.up_node_addr),
- print_mac(mac2, spt.poll_addr),
+ spt.up_node_addr, spt.poll_addr,
ntohs(spt.acc_priority), ntohs(spt.auth_source_class),
ntohs(spt.att_code));
size += sprintf(buffer + size, "%6s: Source Address : Bcn T : Maj. V : Lan St : Lcl Rg : Mon Err : Frame Correl : \n", dev->name);
size += sprintf(buffer + size,
- "%6s: %s : %04x : %04x : %04x : %04x : %04x : %04x : \n",
- dev->name, print_mac(mac, spt.source_addr),
+ "%6s: %pM : %04x : %04x : %04x : %04x : %04x : %04x : \n",
+ dev->name, spt.source_addr,
ntohs(spt.beacon_type), ntohs(spt.major_vector),
ntohs(spt.lan_status), ntohs(spt.local_ring),
ntohs(spt.mon_error), ntohs(spt.frame_correl));
@@ -1905,10 +1895,10 @@ static int sprintf_info(char *buffer, struct net_device *dev)
dev->name);
size += sprintf(buffer + size,
- "%6s: : %02x : %02x : %s : %02x:%02x:%02x:%02x : \n",
+ "%6s: : %02x : %02x : %pM : %02x:%02x:%02x:%02x : \n",
dev->name, ntohs(spt.beacon_transmit),
ntohs(spt.beacon_receive),
- print_mac(mac, spt.beacon_naun),
+ spt.beacon_naun,
spt.beacon_phys[0], spt.beacon_phys[1],
spt.beacon_phys[2], spt.beacon_phys[3]);
return size;
diff --git a/drivers/net/tokenring/madgemc.c b/drivers/net/tokenring/madgemc.c
index c9c5a2b1ed9e..917b4d201e09 100644
--- a/drivers/net/tokenring/madgemc.c
+++ b/drivers/net/tokenring/madgemc.c
@@ -152,7 +152,6 @@ static int __devinit madgemc_probe(struct device *device)
struct card_info *card;
struct mca_device *mdev = to_mca_device(device);
int ret = 0;
- DECLARE_MAC_BUF(mac);
if (versionprinted++ == 0)
printk("%s", version);
@@ -323,8 +322,8 @@ static int __devinit madgemc_probe(struct device *device)
mca_device_set_name(mdev, (card->cardtype == 0x08)?MADGEMC16_CARDNAME:MADGEMC32_CARDNAME);
mca_set_adapter_procfn(mdev->slot, madgemc_mcaproc, dev);
- printk("%s: Ring Station Address: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: Ring Station Address: %pM\n",
+ dev->name, dev->dev_addr);
if (tmsdev_init(dev, device)) {
printk("%s: unable to get memory for dev->priv.\n",
@@ -467,7 +466,7 @@ static irqreturn_t madgemc_interrupt(int irq, void *dev_id)
* zero to leave the TMS NSELOUT bits unaffected.
*
*/
-unsigned short madgemc_setnselout_pins(struct net_device *dev)
+static unsigned short madgemc_setnselout_pins(struct net_device *dev)
{
unsigned char reg1;
struct net_local *tp = netdev_priv(dev);
@@ -690,7 +689,6 @@ static int madgemc_mcaproc(char *buf, int slot, void *d)
struct net_local *tp = netdev_priv(dev);
struct card_info *curcard = tp->tmspriv;
int len = 0;
- DECLARE_MAC_BUF(mac);
len += sprintf(buf+len, "-------\n");
if (curcard) {
@@ -714,8 +712,8 @@ static int madgemc_mcaproc(char *buf, int slot, void *d)
}
len += sprintf(buf+len, " (%s)\n", (curcard->fairness)?"Unfair":"Fair");
- len += sprintf(buf+len, "Ring Station Address: %s\n",
- print_mac(mac, dev->dev_addr));
+ len += sprintf(buf+len, "Ring Station Address: %pM\n",
+ dev->dev_addr);
} else
len += sprintf(buf+len, "Card not configured\n");
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index 0ab51a0f35fc..ecb5c7c96910 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -421,10 +421,7 @@ static int olympic_init(struct net_device *dev)
memcpy_fromio(&dev->dev_addr[0], adapter_addr,6);
#if OLYMPIC_DEBUG
- {
- DECLARE_MAC_BUF(mac);
- printk("adapter address: %s\n", print_mac(mac, dev->dev_addr));
- }
+ printk("adapter address: %pM\n", dev->dev_addr);
#endif
olympic_priv->olympic_addr_table_addr = swab16(readw(init_srb + 12));
@@ -441,7 +438,6 @@ static int olympic_open(struct net_device *dev)
unsigned long flags, t;
int i, open_finished = 1 ;
u8 resp, err;
- DECLARE_MAC_BUF(mac);
DECLARE_WAITQUEUE(wait,current) ;
@@ -569,8 +565,8 @@ static int olympic_open(struct net_device *dev)
goto out;
case 0x32:
- printk(KERN_WARNING "%s: Invalid LAA: %s\n",
- dev->name, print_mac(mac, olympic_priv->olympic_laa));
+ printk(KERN_WARNING "%s: Invalid LAA: %pM\n",
+ dev->name, olympic_priv->olympic_laa);
goto out;
default:
@@ -704,13 +700,12 @@ static int olympic_open(struct net_device *dev)
u8 __iomem *opt;
int i;
u8 addr[6];
- DECLARE_MAC_BUF(mac);
oat = (olympic_priv->olympic_lap + olympic_priv->olympic_addr_table_addr);
opt = (olympic_priv->olympic_lap + olympic_priv->olympic_parms_addr);
for (i = 0; i < 6; i++)
addr[i] = readb(oat+offsetof(struct olympic_adapter_addr_table,node_addr)+i);
- printk("%s: Node Address: %s\n",dev->name, print_mac(mac, addr));
+ printk("%s: Node Address: %pM\n", dev->name, addr);
printk("%s: Functional Address: %02x:%02x:%02x:%02x\n",dev->name,
readb(oat+offsetof(struct olympic_adapter_addr_table,func_addr)),
readb(oat+offsetof(struct olympic_adapter_addr_table,func_addr)+1),
@@ -719,7 +714,7 @@ static int olympic_open(struct net_device *dev)
for (i = 0; i < 6; i++)
addr[i] = readb(opt+offsetof(struct olympic_parameters_table, up_node_addr)+i);
- printk("%s: NAUN Address: %s\n",dev->name, print_mac(mac, addr));
+ printk("%s: NAUN Address: %pM\n", dev->name, addr);
}
netif_start_queue(dev);
@@ -867,7 +862,6 @@ static void olympic_rx(struct net_device *dev)
skb->protocol = tr_type_trans(skb,dev);
netif_rx(skb) ;
}
- dev->last_rx = jiffies ;
olympic_priv->olympic_stats.rx_packets++ ;
olympic_priv->olympic_stats.rx_bytes += length ;
} /* if skb == null */
@@ -1440,19 +1434,12 @@ static void olympic_arb_cmd(struct net_device *dev)
struct trh_hdr *mac_hdr;
printk(KERN_WARNING "%s: Received MAC Frame, details: \n",dev->name);
mac_hdr = tr_hdr(mac_frame);
- printk(KERN_WARNING "%s: MAC Frame Dest. Addr: "
- MAC_FMT " \n", dev->name,
- mac_hdr->daddr[0], mac_hdr->daddr[1],
- mac_hdr->daddr[2], mac_hdr->daddr[3],
- mac_hdr->daddr[4], mac_hdr->daddr[5]);
- printk(KERN_WARNING "%s: MAC Frame Srce. Addr: "
- MAC_FMT " \n", dev->name,
- mac_hdr->saddr[0], mac_hdr->saddr[1],
- mac_hdr->saddr[2], mac_hdr->saddr[3],
- mac_hdr->saddr[4], mac_hdr->saddr[5]);
+ printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %pM\n",
+ dev->name, mac_hdr->daddr);
+ printk(KERN_WARNING "%s: MAC Frame Srce. Addr: %pM\n",
+ dev->name, mac_hdr->saddr);
}
netif_rx(mac_frame);
- dev->last_rx = jiffies;
drop_frame:
/* Now tell the card we have dealt with the received frame */
@@ -1647,8 +1634,6 @@ static int olympic_proc_info(char *buffer, char **start, off_t offset, int lengt
u8 addr[6];
u8 addr2[6];
int i;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
size = sprintf(buffer,
"IBM Pit/Pit-Phy/Olympic Chipset Token Ring Adapter %s\n",dev->name);
@@ -1658,10 +1643,9 @@ static int olympic_proc_info(char *buffer, char **start, off_t offset, int lengt
for (i = 0 ; i < 6 ; i++)
addr[i] = readb(oat+offsetof(struct olympic_adapter_addr_table,node_addr) + i);
- size += sprintf(buffer+size, "%6s: %s : %s : %02x:%02x:%02x:%02x\n",
+ size += sprintf(buffer+size, "%6s: %pM : %pM : %02x:%02x:%02x:%02x\n",
dev->name,
- print_mac(mac, dev->dev_addr),
- print_mac(mac2, addr),
+ dev->dev_addr, addr,
readb(oat+offsetof(struct olympic_adapter_addr_table,func_addr)),
readb(oat+offsetof(struct olympic_adapter_addr_table,func_addr)+1),
readb(oat+offsetof(struct olympic_adapter_addr_table,func_addr)+2),
@@ -1677,14 +1661,13 @@ static int olympic_proc_info(char *buffer, char **start, off_t offset, int lengt
for (i = 0 ; i < 6 ; i++)
addr2[i] = readb(opt+offsetof(struct olympic_parameters_table, poll_addr) + i);
- size += sprintf(buffer+size, "%6s: %02x:%02x:%02x:%02x : %s : %s : %04x : %04x : %04x :\n",
+ size += sprintf(buffer+size, "%6s: %02x:%02x:%02x:%02x : %pM : %pM : %04x : %04x : %04x :\n",
dev->name,
readb(opt+offsetof(struct olympic_parameters_table, phys_addr)),
readb(opt+offsetof(struct olympic_parameters_table, phys_addr)+1),
readb(opt+offsetof(struct olympic_parameters_table, phys_addr)+2),
readb(opt+offsetof(struct olympic_parameters_table, phys_addr)+3),
- print_mac(mac, addr),
- print_mac(mac2, addr2),
+ addr, addr2,
swab16(readw(opt+offsetof(struct olympic_parameters_table, acc_priority))),
swab16(readw(opt+offsetof(struct olympic_parameters_table, auth_source_class))),
swab16(readw(opt+offsetof(struct olympic_parameters_table, att_code))));
@@ -1694,9 +1677,8 @@ static int olympic_proc_info(char *buffer, char **start, off_t offset, int lengt
for (i = 0 ; i < 6 ; i++)
addr[i] = readb(opt+offsetof(struct olympic_parameters_table, source_addr) + i);
- size += sprintf(buffer+size, "%6s: %s : %04x : %04x : %04x : %04x : %04x : %04x : \n",
- dev->name,
- print_mac(mac, addr),
+ size += sprintf(buffer+size, "%6s: %pM : %04x : %04x : %04x : %04x : %04x : %04x : \n",
+ dev->name, addr,
swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_type))),
swab16(readw(opt+offsetof(struct olympic_parameters_table, major_vector))),
swab16(readw(opt+offsetof(struct olympic_parameters_table, lan_status))),
@@ -1709,11 +1691,11 @@ static int olympic_proc_info(char *buffer, char **start, off_t offset, int lengt
for (i = 0 ; i < 6 ; i++)
addr[i] = readb(opt+offsetof(struct olympic_parameters_table, beacon_naun) + i);
- size += sprintf(buffer+size, "%6s: : %02x : %02x : %s : %02x:%02x:%02x:%02x : \n",
+ size += sprintf(buffer+size, "%6s: : %02x : %02x : %pM : %02x:%02x:%02x:%02x : \n",
dev->name,
swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_transmit))),
swab16(readw(opt+offsetof(struct olympic_parameters_table, beacon_receive))),
- print_mac(mac, addr),
+ addr,
readb(opt+offsetof(struct olympic_parameters_table, beacon_phys)),
readb(opt+offsetof(struct olympic_parameters_table, beacon_phys)+1),
readb(opt+offsetof(struct olympic_parameters_table, beacon_phys)+2),
diff --git a/drivers/net/tokenring/proteon.c b/drivers/net/tokenring/proteon.c
index 00ea94513460..b8c955f6d31a 100644
--- a/drivers/net/tokenring/proteon.c
+++ b/drivers/net/tokenring/proteon.c
@@ -122,7 +122,6 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
static int versionprinted;
const unsigned *port;
int j,err = 0;
- DECLARE_MAC_BUF(mac);
if (!dev)
return -ENOMEM;
@@ -153,8 +152,8 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
proteon_read_eeprom(dev);
- printk(KERN_DEBUG "proteon.c: Ring Station Address: %s\n",
- print_mac(mac, dev->dev_addr));
+ printk(KERN_DEBUG "proteon.c: Ring Station Address: %pM\n",
+ dev->dev_addr);
tp = netdev_priv(dev);
tp->setnselout = proteon_setnselout_pins;
@@ -284,7 +283,7 @@ static void proteon_read_eeprom(struct net_device *dev)
dev->dev_addr[i] = proteon_sifreadw(dev, SIFINC) >> 8;
}
-unsigned short proteon_setnselout_pins(struct net_device *dev)
+static unsigned short proteon_setnselout_pins(struct net_device *dev)
{
return 0;
}
diff --git a/drivers/net/tokenring/skisa.c b/drivers/net/tokenring/skisa.c
index 41b6999a0f33..c0f58f08782c 100644
--- a/drivers/net/tokenring/skisa.c
+++ b/drivers/net/tokenring/skisa.c
@@ -139,7 +139,6 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
static int versionprinted;
const unsigned *port;
int j, err = 0;
- DECLARE_MAC_BUF(mac);
if (!dev)
return -ENOMEM;
@@ -170,8 +169,8 @@ static int __init setup_card(struct net_device *dev, struct device *pdev)
sk_isa_read_eeprom(dev);
- printk(KERN_DEBUG "skisa.c: Ring Station Address: %s\n",
- print_mac(mac, dev->dev_addr));
+ printk(KERN_DEBUG "skisa.c: Ring Station Address: %pM\n",
+ dev->dev_addr);
tp = netdev_priv(dev);
tp->setnselout = sk_isa_setnselout_pins;
@@ -301,7 +300,7 @@ static void sk_isa_read_eeprom(struct net_device *dev)
dev->dev_addr[i] = sk_isa_sifreadw(dev, SIFINC) >> 8;
}
-unsigned short sk_isa_setnselout_pins(struct net_device *dev)
+static unsigned short sk_isa_setnselout_pins(struct net_device *dev)
{
return 0;
}
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index ed50d288e494..a011666342ff 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -3910,7 +3910,6 @@ static int smctr_process_rx_packet(MAC_HEADER *rmf, __u16 size,
/* Kick the packet on up. */
skb->protocol = tr_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
err = 0;
}
@@ -4496,7 +4495,6 @@ static int smctr_rx_frame(struct net_device *dev)
/* Kick the packet on up. */
skb->protocol = tr_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
} else {
}
}
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index d07c4523c847..5be34c2fd483 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -180,10 +180,14 @@ void tms380tr_wait(unsigned long time);
static void tms380tr_write_rpl_status(RPL *rpl, unsigned int Status);
static void tms380tr_write_tpl_status(TPL *tpl, unsigned int Status);
-#define SIFREADB(reg) (((struct net_local *)dev->priv)->sifreadb(dev, reg))
-#define SIFWRITEB(val, reg) (((struct net_local *)dev->priv)->sifwriteb(dev, val, reg))
-#define SIFREADW(reg) (((struct net_local *)dev->priv)->sifreadw(dev, reg))
-#define SIFWRITEW(val, reg) (((struct net_local *)dev->priv)->sifwritew(dev, val, reg))
+#define SIFREADB(reg) \
+ (((struct net_local *)netdev_priv(dev))->sifreadb(dev, reg))
+#define SIFWRITEB(val, reg) \
+ (((struct net_local *)netdev_priv(dev))->sifwriteb(dev, val, reg))
+#define SIFREADW(reg) \
+ (((struct net_local *)netdev_priv(dev))->sifreadw(dev, reg))
+#define SIFWRITEW(val, reg) \
+ (((struct net_local *)netdev_priv(dev))->sifwritew(dev, val, reg))
@@ -2186,7 +2190,6 @@ static void tms380tr_rcv_status_irq(struct net_device *dev)
skb_trim(skb,Length);
skb->protocol = tr_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
}
}
else /* Invalid frame */
@@ -2331,7 +2334,7 @@ int tmsdev_init(struct net_device *dev, struct device *pdev)
{
struct net_local *tms_local;
- memset(dev->priv, 0, sizeof(struct net_local));
+ memset(netdev_priv(dev), 0, sizeof(struct net_local));
tms_local = netdev_priv(dev);
init_waitqueue_head(&tms_local->wait_for_tok_int);
if (pdev->dma_mask)
diff --git a/drivers/net/tokenring/tmspci.c b/drivers/net/tokenring/tmspci.c
index 5f0ee880cfff..5f601773c260 100644
--- a/drivers/net/tokenring/tmspci.c
+++ b/drivers/net/tokenring/tmspci.c
@@ -100,7 +100,6 @@ static int __devinit tms_pci_attach(struct pci_dev *pdev, const struct pci_devic
unsigned int pci_irq_line;
unsigned long pci_ioaddr;
struct card_info *cardinfo = &card_info_table[ent->driver_data];
- DECLARE_MAC_BUF(mac);
if (versionprinted++ == 0)
printk("%s", version);
@@ -137,8 +136,8 @@ static int __devinit tms_pci_attach(struct pci_dev *pdev, const struct pci_devic
tms_pci_read_eeprom(dev);
- printk("%s: Ring Station Address: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk("%s: Ring Station Address: %pM\n",
+ dev->name, dev->dev_addr);
ret = tmsdev_init(dev, &pdev->dev);
if (ret) {
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
index eb1da6f0b086..75461dbd4876 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/tsi108_eth.c
@@ -788,7 +788,6 @@ static int tsi108_complete_rx(struct net_device *dev, int budget)
skb_put(skb, data->rxring[rx].len);
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
}
return done;
@@ -889,7 +888,7 @@ static int tsi108_poll(struct napi_struct *napi, int budget)
if (num_received < budget) {
data->rxpending = 0;
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
TSI_WRITE(TSI108_EC_INTMASK,
TSI_READ(TSI108_EC_INTMASK)
@@ -920,7 +919,7 @@ static void tsi108_rx_int(struct net_device *dev)
* from tsi108_check_rxring().
*/
- if (netif_rx_schedule_prep(dev, &data->napi)) {
+ if (netif_rx_schedule_prep(&data->napi)) {
/* Mask, rather than ack, the receive interrupts. The ack
* will happen in tsi108_poll().
*/
@@ -931,7 +930,7 @@ static void tsi108_rx_int(struct net_device *dev)
| TSI108_INT_RXTHRESH |
TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR |
TSI108_INT_RXWAIT);
- __netif_rx_schedule(dev, &data->napi);
+ __netif_rx_schedule(&data->napi);
} else {
if (!netif_running(dev)) {
/* This can happen if an interrupt occurs while the
@@ -1569,7 +1568,6 @@ tsi108_init_one(struct platform_device *pdev)
struct tsi108_prv_data *data = NULL;
hw_info *einfo;
int err = 0;
- DECLARE_MAC_BUF(mac);
einfo = pdev->dev.platform_data;
@@ -1659,8 +1657,8 @@ tsi108_init_one(struct platform_device *pdev)
}
platform_set_drvdata(pdev, dev);
- printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Tsi108 Gigabit Ethernet, MAC: %pM\n",
+ dev->name, dev->dev_addr);
#ifdef DEBUG
data->msg_enable = DEBUG;
dump_eth_one(dev);
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index 124d5d690dde..5166be930a52 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -459,7 +459,6 @@ static void de_rx (struct de_private *de)
de->net_stats.rx_packets++;
de->net_stats.rx_bytes += skb->len;
- de->dev->last_rx = jiffies;
rc = netif_rx (skb);
if (rc == NET_RX_DROP)
drop = 1;
@@ -484,7 +483,7 @@ rx_next:
static irqreturn_t de_interrupt (int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
u32 status;
status = dr32(MacStatus);
@@ -590,7 +589,7 @@ next:
static int de_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
unsigned int entry, tx_free;
u32 mapping, len, flags = FirstFrag | LastFrag;
struct de_desc *txd;
@@ -653,7 +652,7 @@ static int de_start_xmit (struct sk_buff *skb, struct net_device *dev)
static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
u16 hash_table[32];
struct dev_mc_list *mclist;
int i;
@@ -684,7 +683,7 @@ static void build_setup_frame_hash(u16 *setup_frm, struct net_device *dev)
static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
struct dev_mc_list *mclist;
int i;
u16 *eaddrs;
@@ -712,7 +711,7 @@ static void build_setup_frame_perfect(u16 *setup_frm, struct net_device *dev)
static void __de_set_rx_mode (struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
u32 macmode;
unsigned int entry;
u32 mapping;
@@ -797,7 +796,7 @@ out:
static void de_set_rx_mode (struct net_device *dev)
{
unsigned long flags;
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
spin_lock_irqsave (&de->lock, flags);
__de_set_rx_mode(dev);
@@ -821,7 +820,7 @@ static void __de_get_stats(struct de_private *de)
static struct net_device_stats *de_get_stats(struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
/* The chip only need report frame silently dropped. */
spin_lock_irq(&de->lock);
@@ -1355,7 +1354,7 @@ static void de_free_rings (struct de_private *de)
static int de_open (struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
int rc;
if (netif_msg_ifup(de))
@@ -1400,7 +1399,7 @@ err_out_free:
static int de_close (struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
unsigned long flags;
if (netif_msg_ifdown(de))
@@ -1423,7 +1422,7 @@ static int de_close (struct net_device *dev)
static void de_tx_timeout (struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
printk(KERN_DEBUG "%s: NIC status %08x mode %08x sia %08x desc %u/%u/%u\n",
dev->name, dr32(MacStatus), dr32(MacMode), dr32(SIAStatus),
@@ -1574,7 +1573,7 @@ static int __de_set_settings(struct de_private *de, struct ethtool_cmd *ecmd)
static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
strcpy (info->driver, DRV_NAME);
strcpy (info->version, DRV_VERSION);
@@ -1589,7 +1588,7 @@ static int de_get_regs_len(struct net_device *dev)
static int de_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
int rc;
spin_lock_irq(&de->lock);
@@ -1601,7 +1600,7 @@ static int de_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
static int de_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
int rc;
spin_lock_irq(&de->lock);
@@ -1613,14 +1612,14 @@ static int de_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
static u32 de_get_msglevel(struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
return de->msg_enable;
}
static void de_set_msglevel(struct net_device *dev, u32 msglvl)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
de->msg_enable = msglvl;
}
@@ -1628,7 +1627,7 @@ static void de_set_msglevel(struct net_device *dev, u32 msglvl)
static int de_get_eeprom(struct net_device *dev,
struct ethtool_eeprom *eeprom, u8 *data)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
if (!de->ee_data)
return -EOPNOTSUPP;
@@ -1642,7 +1641,7 @@ static int de_get_eeprom(struct net_device *dev,
static int de_nway_reset(struct net_device *dev)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
u32 status;
if (de->media_type != DE_MEDIA_TP_AUTO)
@@ -1661,7 +1660,7 @@ static int de_nway_reset(struct net_device *dev)
static void de_get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *data)
{
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
regs->version = (DE_REGS_VER << 2) | de->de21040;
@@ -1692,9 +1691,9 @@ static void __devinit de21040_get_mac_address (struct de_private *de)
for (i = 0; i < 6; i++) {
int value, boguscnt = 100000;
- do
+ do {
value = dr32(ROMCmd);
- while (value < 0 && --boguscnt > 0);
+ } while (value < 0 && --boguscnt > 0);
de->dev->dev_addr[i] = value;
udelay(1);
if (boguscnt <= 0)
@@ -1932,7 +1931,6 @@ static int __devinit de_init_one (struct pci_dev *pdev,
void __iomem *regs;
unsigned long pciaddr;
static int board_idx = -1;
- DECLARE_MAC_BUF(mac);
board_idx++;
@@ -1956,7 +1954,7 @@ static int __devinit de_init_one (struct pci_dev *pdev,
dev->tx_timeout = de_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
- de = dev->priv;
+ de = netdev_priv(dev);
de->de21040 = ent->driver_data == 0 ? 1 : 0;
de->pdev = pdev;
de->dev = dev;
@@ -2046,11 +2044,11 @@ static int __devinit de_init_one (struct pci_dev *pdev,
goto err_out_iomap;
/* print info about board and interface just registered */
- printk (KERN_INFO "%s: %s at 0x%lx, %s, IRQ %d\n",
+ printk (KERN_INFO "%s: %s at 0x%lx, %pM, IRQ %d\n",
dev->name,
de->de21040 ? "21040" : "21041",
dev->base_addr,
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
pci_set_drvdata(pdev, dev);
@@ -2078,7 +2076,7 @@ err_out_free:
static void __devexit de_remove_one (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
BUG_ON(!dev);
unregister_netdev(dev);
@@ -2095,7 +2093,7 @@ static void __devexit de_remove_one (struct pci_dev *pdev)
static int de_suspend (struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata (pdev);
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
rtnl_lock();
if (netif_running (dev)) {
@@ -2130,7 +2128,7 @@ static int de_suspend (struct pci_dev *pdev, pm_message_t state)
static int de_resume (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata (pdev);
- struct de_private *de = dev->priv;
+ struct de_private *de = netdev_priv(dev);
int retval = 0;
rtnl_lock();
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index 6444cbec0bdc..67bfd6f43366 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -1085,7 +1085,6 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
struct de4x5_private *lp = netdev_priv(dev);
struct pci_dev *pdev = NULL;
int i, status=0;
- DECLARE_MAC_BUF(mac);
gendev->driver_data = dev;
@@ -1119,10 +1118,10 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
}
dev->base_addr = iobase;
- printk ("%s: %s at 0x%04lx", gendev->bus_id, name, iobase);
+ printk ("%s: %s at 0x%04lx", dev_name(gendev), name, iobase);
status = get_hw_addr(dev);
- printk(", h/w address %s\n", print_mac(mac, dev->dev_addr));
+ printk(", h/w address %pM\n", dev->dev_addr);
if (status != 0) {
printk(" which has an Ethernet PROM CRC error.\n");
@@ -1154,7 +1153,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev)
}
}
lp->fdx = lp->params.fdx;
- sprintf(lp->adapter_name,"%s (%s)", name, gendev->bus_id);
+ sprintf(lp->adapter_name,"%s (%s)", name, dev_name(gendev));
lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc);
#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY)
@@ -1647,7 +1646,6 @@ de4x5_rx(struct net_device *dev)
netif_rx(skb);
/* Update stats */
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += pkt_len;
}
@@ -5401,7 +5399,6 @@ static void
de4x5_dbg_srom(struct de4x5_srom *p)
{
int i;
- DECLARE_MAC_BUF(mac);
if (de4x5_debug & DEBUG_SROM) {
printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
@@ -5410,7 +5407,7 @@ de4x5_dbg_srom(struct de4x5_srom *p)
printk("SROM version: %02x\n", (u_char)(p->version));
printk("# controllers: %02x\n", (u_char)(p->num_controllers));
- printk("Hardware Address: %s\n", print_mac(mac, p->ieee_addr));
+ printk("Hardware Address: %pM\n", p->ieee_addr);
printk("CRC checksum: %04x\n", (u_short)(p->chksum));
for (i=0; i<64; i++) {
printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i));
@@ -5424,12 +5421,10 @@ static void
de4x5_dbg_rx(struct sk_buff *skb, int len)
{
int i, j;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (de4x5_debug & DEBUG_RX) {
- printk("R: %s <- %s len/SAP:%02x%02x [%d]\n",
- print_mac(mac, skb->data), print_mac(mac2, &skb->data[6]),
+ printk("R: %pM <- %pM len/SAP:%02x%02x [%d]\n",
+ skb->data, &skb->data[6],
(u_char)skb->data[12],
(u_char)skb->data[13],
len);
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index c91852f49a48..28a5c51b43a0 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -362,7 +362,6 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev,
struct net_device *dev;
u32 pci_pmr;
int i, err;
- DECLARE_MAC_BUF(mac);
DMFE_DBUG(0, "dmfe_init_one()", 0);
@@ -475,12 +474,11 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev,
if (err)
goto err_out_free_buf;
- printk(KERN_INFO "%s: Davicom DM%04lx at pci%s, "
- "%s, irq %d.\n",
+ printk(KERN_INFO "%s: Davicom DM%04lx at pci%s, %pM, irq %d.\n",
dev->name,
ent->driver_data >> 16,
pci_name(pdev),
- print_mac(mac, dev->dev_addr),
+ dev->dev_addr,
dev->irq);
pci_set_master(pdev);
@@ -1010,7 +1008,6 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db)
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
db->stats.rx_packets++;
db->stats.rx_bytes += rxlen;
}
diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c
index 0dcced1263b9..391acd32a6a5 100644
--- a/drivers/net/tulip/eeprom.c
+++ b/drivers/net/tulip/eeprom.c
@@ -337,7 +337,7 @@ int __devinit tulip_read_eeprom(struct net_device *dev, int location, int addr_l
{
int i;
unsigned retval = 0;
- struct tulip_private *tp = dev->priv;
+ struct tulip_private *tp = netdev_priv(dev);
void __iomem *ee_addr = tp->base_addr + CSR9;
int read_cmd = location | (EE_READ_CMD << addr_len);
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c
index c6bad987d63e..6c3428a37c0b 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/tulip/interrupt.c
@@ -103,7 +103,7 @@ void oom_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct tulip_private *tp = netdev_priv(dev);
- netif_rx_schedule(dev, &tp->napi);
+ netif_rx_schedule(&tp->napi);
}
int tulip_poll(struct napi_struct *napi, int budget)
@@ -231,7 +231,6 @@ int tulip_poll(struct napi_struct *napi, int budget)
netif_receive_skb(skb);
- dev->last_rx = jiffies;
tp->stats.rx_packets++;
tp->stats.rx_bytes += pkt_len;
}
@@ -301,7 +300,7 @@ int tulip_poll(struct napi_struct *napi, int budget)
/* Remove us from polling list and enable RX intr. */
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7);
/* The last op happens after poll completion. Which means the following:
@@ -337,7 +336,7 @@ int tulip_poll(struct napi_struct *napi, int budget)
* before we did netif_rx_complete(). See? We would lose it. */
/* remove ourselves from the polling list */
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
return work_done;
}
@@ -444,7 +443,6 @@ static int tulip_rx(struct net_device *dev)
netif_rx(skb);
- dev->last_rx = jiffies;
tp->stats.rx_packets++;
tp->stats.rx_bytes += pkt_len;
}
@@ -521,7 +519,7 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance)
rxd++;
/* Mask RX intrs and add the device to poll list. */
iowrite32(tulip_tbl[tp->chip_id].valid_intrs&~RxPollInt, ioaddr + CSR7);
- netif_rx_schedule(dev, &tp->napi);
+ netif_rx_schedule(&tp->napi);
if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass)))
break;
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index cafa89e60167..ff84babb3ff3 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -1050,13 +1050,11 @@ static void set_rx_mode(struct net_device *dev)
filterbit = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
filterbit &= 0x3f;
mc_filter[filterbit >> 5] |= 1 << (filterbit & 31);
- if (tulip_debug > 2) {
- DECLARE_MAC_BUF(mac);
- printk(KERN_INFO "%s: Added filter for %s"
+ if (tulip_debug > 2)
+ printk(KERN_INFO "%s: Added filter for %pM"
" %8.8x bit %d.\n",
- dev->name, print_mac(mac, mclist->dmi_addr),
+ dev->name, mclist->dmi_addr,
ether_crc(ETH_ALEN, mclist->dmi_addr), filterbit);
- }
}
if (mc_filter[0] == tp->mc_filter[0] &&
mc_filter[1] == tp->mc_filter[1])
@@ -1250,7 +1248,6 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
const char *chip_name = tulip_tbl[chip_idx].chip_name;
unsigned int eeprom_missing = 0;
unsigned int force_csr0 = 0;
- DECLARE_MAC_BUF(mac);
#ifndef MODULE
static int did_version; /* Already printed version info. */
@@ -1432,9 +1429,9 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
for (i = 0; i < 3; i++) {
int value, boguscnt = 100000;
iowrite32(0x600 | i, ioaddr + 0x98);
- do
+ do {
value = ioread32(ioaddr + CSR9);
- while (value < 0 && --boguscnt > 0);
+ } while (value < 0 && --boguscnt > 0);
put_unaligned_le16(value, ((__le16 *)dev->dev_addr) + i);
sum += value & 0xffff;
}
@@ -1635,7 +1632,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
if (eeprom_missing)
printk(" EEPROM not present,");
- printk(" %s", print_mac(mac, dev->dev_addr));
+ printk(" %pM", dev->dev_addr);
printk(", IRQ %d.\n", irq);
if (tp->chip_id == PNIC2)
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c
index e9e628621639..00cbc5251dcc 100644
--- a/drivers/net/tulip/uli526x.c
+++ b/drivers/net/tulip/uli526x.c
@@ -261,7 +261,6 @@ static int __devinit uli526x_init_one (struct pci_dev *pdev,
struct uli526x_board_info *db; /* board information structure */
struct net_device *dev;
int i, err;
- DECLARE_MAC_BUF(mac);
ULI526X_DBUG(0, "uli526x_init_one()", 0);
@@ -379,9 +378,9 @@ static int __devinit uli526x_init_one (struct pci_dev *pdev,
if (err)
goto err_out_res;
- printk(KERN_INFO "%s: ULi M%04lx at pci%s, %s, irq %d.\n",
+ printk(KERN_INFO "%s: ULi M%04lx at pci%s, %pM, irq %d.\n",
dev->name,ent->driver_data >> 16,pci_name(pdev),
- print_mac(mac, dev->dev_addr), dev->irq);
+ dev->dev_addr, dev->irq);
pci_set_master(pdev);
@@ -855,7 +854,6 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
db->stats.rx_packets++;
db->stats.rx_bytes += rxlen;
@@ -892,7 +890,7 @@ static struct net_device_stats * uli526x_get_stats(struct net_device *dev)
static void uli526x_set_filter_mode(struct net_device * dev)
{
- struct uli526x_board_info *db = dev->priv;
+ struct uli526x_board_info *db = netdev_priv(dev);
unsigned long flags;
ULI526X_DBUG(0, "uli526x_set_filter_mode()", 0);
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index 50068194c163..022d99af8646 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -355,7 +355,6 @@ static int __devinit w840_probe1 (struct pci_dev *pdev,
int irq;
int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
void __iomem *ioaddr;
- DECLARE_MAC_BUF(mac);
i = pci_enable_device(pdev);
if (i) return i;
@@ -435,9 +434,9 @@ static int __devinit w840_probe1 (struct pci_dev *pdev,
if (i)
goto err_out_cleardev;
- printk(KERN_INFO "%s: %s at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
dev->name, pci_id_tbl[chip_idx].name, ioaddr,
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
if (np->drv_flags & CanHaveMII) {
int phy, phy_idx = 0;
@@ -1245,20 +1244,15 @@ static int netdev_rx(struct net_device *dev)
}
#ifndef final_version /* Remove after testing. */
/* You will want this info for the initial debug. */
- if (debug > 5) {
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
-
- printk(KERN_DEBUG " Rx data %s %s"
+ if (debug > 5)
+ printk(KERN_DEBUG " Rx data %pM %pM"
" %2.2x%2.2x %d.%d.%d.%d.\n",
- print_mac(mac, &skb->data[0]), print_mac(mac2, &skb->data[6]),
+ &skb->data[0], &skb->data[6],
skb->data[12], skb->data[13],
skb->data[14], skb->data[15], skb->data[16], skb->data[17]);
- }
#endif
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
np->stats.rx_packets++;
np->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c
index 6b93d0169116..13c8703ecb9f 100644
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/tulip/xircom_cb.c
@@ -1072,7 +1072,6 @@ static void read_mac_address(struct xircom_private *card)
unsigned char j, tuple, link, data_id, data_count;
unsigned long flags;
int i;
- DECLARE_MAC_BUF(mac);
enter("read_mac_address");
@@ -1102,7 +1101,7 @@ static void read_mac_address(struct xircom_private *card)
}
}
spin_unlock_irqrestore(&card->lock, flags);
- pr_debug(" %s\n", print_mac(mac, card->dev->dev_addr));
+ pr_debug(" %pM\n", card->dev->dev_addr);
leave("read_mac_address");
}
@@ -1202,7 +1201,6 @@ static void investigate_read_descriptor(struct net_device *dev,struct xircom_pri
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
card->stats.rx_packets++;
card->stats.rx_bytes += pkt_len;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 33b6d1b122fb..69f9a0ec764d 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -305,6 +305,23 @@ tun_net_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static const struct net_device_ops tun_netdev_ops = {
+ .ndo_open = tun_net_open,
+ .ndo_stop = tun_net_close,
+ .ndo_start_xmit = tun_net_xmit,
+ .ndo_change_mtu = tun_net_change_mtu,
+};
+
+static const struct net_device_ops tap_netdev_ops = {
+ .ndo_open = tun_net_open,
+ .ndo_stop = tun_net_close,
+ .ndo_start_xmit = tun_net_xmit,
+ .ndo_change_mtu = tun_net_change_mtu,
+ .ndo_set_multicast_list = tun_net_mclist,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
/* Initialize net device. */
static void tun_net_init(struct net_device *dev)
{
@@ -312,11 +329,12 @@ static void tun_net_init(struct net_device *dev)
switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TUN_DEV:
+ dev->netdev_ops = &tun_netdev_ops;
+
/* Point-to-Point TUN Device */
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->mtu = 1500;
- dev->change_mtu = tun_net_change_mtu;
/* Zero header length */
dev->type = ARPHRD_NONE;
@@ -325,10 +343,9 @@ static void tun_net_init(struct net_device *dev)
break;
case TUN_TAP_DEV:
+ dev->netdev_ops = &tap_netdev_ops;
/* Ethernet TAP Device */
ether_setup(dev);
- dev->change_mtu = tun_net_change_mtu;
- dev->set_multicast_list = tun_net_mclist;
random_ether_addr(dev->dev_addr);
@@ -529,7 +546,6 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
}
netif_rx_ni(skb);
- tun->dev->last_rx = jiffies;
tun->dev->stats.rx_packets++;
tun->dev->stats.rx_bytes += len;
@@ -676,9 +692,6 @@ static void tun_setup(struct net_device *dev)
tun->owner = -1;
tun->group = -1;
- dev->open = tun_net_open;
- dev->hard_start_xmit = tun_net_xmit;
- dev->stop = tun_net_close;
dev->ethtool_ops = &tun_ethtool_ops;
dev->destructor = free_netdev;
dev->features |= NETIF_F_NETNS_LOCAL;
@@ -702,6 +715,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
struct tun_net *tn;
struct tun_struct *tun;
struct net_device *dev;
+ const struct cred *cred = current_cred();
int err;
tn = net_generic(net, tun_net_id);
@@ -712,11 +726,12 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
/* Check permissions */
if (((tun->owner != -1 &&
- current->euid != tun->owner) ||
+ cred->euid != tun->owner) ||
(tun->group != -1 &&
- current->egid != tun->group)) &&
- !capable(CAP_NET_ADMIN))
+ cred->egid != tun->group)) &&
+ !capable(CAP_NET_ADMIN)) {
return -EPERM;
+ }
}
else if (__dev_get_by_name(net, ifr->ifr_name))
return -EINVAL;
@@ -750,6 +765,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
return -ENOMEM;
dev_net_set(dev, net);
+
tun = netdev_priv(dev);
tun->dev = dev;
tun->flags = flags;
@@ -883,7 +899,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
void __user* argp = (void __user*)arg;
struct ifreq ifr;
int ret;
- DECLARE_MAC_BUF(mac);
if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
if (copy_from_user(&ifr, argp, sizeof ifr))
@@ -1011,8 +1026,8 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file,
case SIOCSIFHWADDR:
/* Set hw address */
- DBG(KERN_DEBUG "%s: set hw address: %s\n",
- tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data));
+ DBG(KERN_DEBUG "%s: set hw address: %pM\n",
+ tun->dev->name, ifr.ifr_hwaddr.sa_data);
rtnl_lock();
ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 734ce0977f02..0009f4e34433 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -1729,7 +1729,6 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * read
netif_receive_skb(new_skb);
spin_unlock(&tp->state_lock);
- tp->dev->last_rx = jiffies;
received++;
budget--;
}
@@ -1756,7 +1755,6 @@ static int
typhoon_poll(struct napi_struct *napi, int budget)
{
struct typhoon *tp = container_of(napi, struct typhoon, napi);
- struct net_device *dev = tp->dev;
struct typhoon_indexes *indexes = tp->indexes;
int work_done;
@@ -1785,7 +1783,7 @@ typhoon_poll(struct napi_struct *napi, int budget)
}
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
iowrite32(TYPHOON_INTR_NONE,
tp->ioaddr + TYPHOON_REG_INTR_MASK);
typhoon_post_pci_writes(tp->ioaddr);
@@ -1798,7 +1796,7 @@ static irqreturn_t
typhoon_interrupt(int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
- struct typhoon *tp = dev->priv;
+ struct typhoon *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->ioaddr;
u32 intr_status;
@@ -1808,10 +1806,10 @@ typhoon_interrupt(int irq, void *dev_instance)
iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS);
- if (netif_rx_schedule_prep(dev, &tp->napi)) {
+ if (netif_rx_schedule_prep(&tp->napi)) {
iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
typhoon_post_pci_writes(ioaddr);
- __netif_rx_schedule(dev, &tp->napi);
+ __netif_rx_schedule(&tp->napi);
} else {
printk(KERN_ERR "%s: Error, poll already scheduled\n",
dev->name);
@@ -2311,7 +2309,6 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
struct cmd_desc xp_cmd;
struct resp_desc xp_resp[3];
int err = 0;
- DECLARE_MAC_BUF(mac);
if(!did_version++)
printk(KERN_INFO "%s", version);
@@ -2526,11 +2523,11 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev);
- printk(KERN_INFO "%s: %s at %s 0x%llx, %s\n",
+ printk(KERN_INFO "%s: %s at %s 0x%llx, %pM\n",
dev->name, typhoon_card_info[card_id].name,
use_mmio ? "MMIO" : "IO",
(unsigned long long)pci_resource_start(pdev, use_mmio),
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
/* xp_resp still contains the response to the READ_VERSIONS command.
* For debugging, let the user know what version he has.
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index c87747bb24c5..7d5a1303e30d 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -188,17 +188,6 @@ static void mem_disp(u8 *addr, int size)
}
#endif /* DEBUG */
-#ifdef CONFIG_UGETH_FILTERING
-static void enqueue(struct list_head *node, struct list_head *lh)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ugeth_lock, flags);
- list_add_tail(node, lh);
- spin_unlock_irqrestore(&ugeth_lock, flags);
-}
-#endif /* CONFIG_UGETH_FILTERING */
-
static struct list_head *dequeue(struct list_head *lh)
{
unsigned long flags;
@@ -391,23 +380,6 @@ static int dump_init_enet_entries(struct ucc_geth_private *ugeth,
}
#endif
-#ifdef CONFIG_UGETH_FILTERING
-static struct enet_addr_container *get_enet_addr_container(void)
-{
- struct enet_addr_container *enet_addr_cont;
-
- /* allocate memory */
- enet_addr_cont = kmalloc(sizeof(struct enet_addr_container), GFP_KERNEL);
- if (!enet_addr_cont) {
- ugeth_err("%s: No memory for enet_addr_container object.",
- __func__);
- return NULL;
- }
-
- return enet_addr_cont;
-}
-#endif /* CONFIG_UGETH_FILTERING */
-
static void put_enet_addr_container(struct enet_addr_container *enet_addr_cont)
{
kfree(enet_addr_cont);
@@ -420,28 +392,6 @@ static void set_mac_addr(__be16 __iomem *reg, u8 *mac)
out_be16(&reg[2], ((u16)mac[1] << 8) | mac[0]);
}
-#ifdef CONFIG_UGETH_FILTERING
-static int hw_add_addr_in_paddr(struct ucc_geth_private *ugeth,
- u8 *p_enet_addr, u8 paddr_num)
-{
- struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt;
-
- if (!(paddr_num < NUM_OF_PADDRS)) {
- ugeth_warn("%s: Illegal paddr_num.", __func__);
- return -EINVAL;
- }
-
- p_82xx_addr_filt =
- (struct ucc_geth_82xx_address_filtering_pram *) ugeth->p_rx_glbl_pram->
- addressfiltering;
-
- /* Ethernet frames are defined in Little Endian mode, */
- /* therefore to insert the address we reverse the bytes. */
- set_mac_addr(&p_82xx_addr_filt->paddr[paddr_num].h, p_enet_addr);
- return 0;
-}
-#endif /* CONFIG_UGETH_FILTERING */
-
static int hw_clear_addr_in_paddr(struct ucc_geth_private *ugeth, u8 paddr_num)
{
struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt;
@@ -1615,8 +1565,8 @@ static int init_phy(struct net_device *dev)
priv->oldspeed = 0;
priv->oldduplex = -1;
- snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->ug_info->mdio_bus,
- priv->ug_info->phy_address);
+ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, priv->ug_info->mdio_bus,
+ priv->ug_info->phy_address);
phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface);
@@ -1647,6 +1597,7 @@ static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
struct ucc_fast_private *uccf;
u32 cecr_subblock;
u32 temp;
+ int i = 10;
uccf = ugeth->uccf;
@@ -1664,8 +1615,9 @@ static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth)
/* Wait for command to complete */
do {
+ msleep(10);
temp = in_be32(uccf->p_ucce);
- } while (!(temp & UCCE_GRA));
+ } while (!(temp & UCCE_GRA) && --i);
uccf->stopped_tx = 1;
@@ -1677,6 +1629,7 @@ static int ugeth_graceful_stop_rx(struct ucc_geth_private * ugeth)
struct ucc_fast_private *uccf;
u32 cecr_subblock;
u8 temp;
+ int i = 10;
uccf = ugeth->uccf;
@@ -1694,9 +1647,9 @@ static int ugeth_graceful_stop_rx(struct ucc_geth_private * ugeth)
ucc_num);
qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock,
QE_CR_PROTOCOL_ETHERNET, 0);
-
+ msleep(10);
temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack);
- } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX));
+ } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) && --i);
uccf->stopped_rx = 1;
@@ -1799,196 +1752,6 @@ static void ugeth_dump_regs(struct ucc_geth_private *ugeth)
#endif
}
-#ifdef CONFIG_UGETH_FILTERING
-static int ugeth_ext_filtering_serialize_tad(struct ucc_geth_tad_params *
- p_UccGethTadParams,
- struct qe_fltr_tad *qe_fltr_tad)
-{
- u16 temp;
-
- /* Zero serialized TAD */
- memset(qe_fltr_tad, 0, QE_FLTR_TAD_SIZE);
-
- qe_fltr_tad->serialized[0] |= UCC_GETH_TAD_V; /* Must have this */
- if (p_UccGethTadParams->rx_non_dynamic_extended_features_mode ||
- (p_UccGethTadParams->vtag_op != UCC_GETH_VLAN_OPERATION_TAGGED_NOP)
- || (p_UccGethTadParams->vnontag_op !=
- UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP)
- )
- qe_fltr_tad->serialized[0] |= UCC_GETH_TAD_EF;
- if (p_UccGethTadParams->reject_frame)
- qe_fltr_tad->serialized[0] |= UCC_GETH_TAD_REJ;
- temp =
- (u16) (((u16) p_UccGethTadParams->
- vtag_op) << UCC_GETH_TAD_VTAG_OP_SHIFT);
- qe_fltr_tad->serialized[0] |= (u8) (temp >> 8); /* upper bits */
-
- qe_fltr_tad->serialized[1] |= (u8) (temp & 0x00ff); /* lower bits */
- if (p_UccGethTadParams->vnontag_op ==
- UCC_GETH_VLAN_OPERATION_NON_TAGGED_Q_TAG_INSERT)
- qe_fltr_tad->serialized[1] |= UCC_GETH_TAD_V_NON_VTAG_OP;
- qe_fltr_tad->serialized[1] |=
- p_UccGethTadParams->rqos << UCC_GETH_TAD_RQOS_SHIFT;
-
- qe_fltr_tad->serialized[2] |=
- p_UccGethTadParams->vpri << UCC_GETH_TAD_V_PRIORITY_SHIFT;
- /* upper bits */
- qe_fltr_tad->serialized[2] |= (u8) (p_UccGethTadParams->vid >> 8);
- /* lower bits */
- qe_fltr_tad->serialized[3] |= (u8) (p_UccGethTadParams->vid & 0x00ff);
-
- return 0;
-}
-
-static struct enet_addr_container_t
- *ugeth_82xx_filtering_get_match_addr_in_hash(struct ucc_geth_private *ugeth,
- struct enet_addr *p_enet_addr)
-{
- struct enet_addr_container *enet_addr_cont;
- struct list_head *p_lh;
- u16 i, num;
- int32_t j;
- u8 *p_counter;
-
- if ((*p_enet_addr)[0] & ENET_GROUP_ADDR) {
- p_lh = &ugeth->group_hash_q;
- p_counter = &(ugeth->numGroupAddrInHash);
- } else {
- p_lh = &ugeth->ind_hash_q;
- p_counter = &(ugeth->numIndAddrInHash);
- }
-
- if (!p_lh)
- return NULL;
-
- num = *p_counter;
-
- for (i = 0; i < num; i++) {
- enet_addr_cont =
- (struct enet_addr_container *)
- ENET_ADDR_CONT_ENTRY(dequeue(p_lh));
- for (j = ENET_NUM_OCTETS_PER_ADDRESS - 1; j >= 0; j--) {
- if ((*p_enet_addr)[j] != (enet_addr_cont->address)[j])
- break;
- if (j == 0)
- return enet_addr_cont; /* Found */
- }
- enqueue(p_lh, &enet_addr_cont->node); /* Put it back */
- }
- return NULL;
-}
-
-static int ugeth_82xx_filtering_add_addr_in_hash(struct ucc_geth_private *ugeth,
- struct enet_addr *p_enet_addr)
-{
- enum ucc_geth_enet_address_recognition_location location;
- struct enet_addr_container *enet_addr_cont;
- struct list_head *p_lh;
- u8 i;
- u32 limit;
- u8 *p_counter;
-
- if ((*p_enet_addr)[0] & ENET_GROUP_ADDR) {
- p_lh = &ugeth->group_hash_q;
- limit = ugeth->ug_info->maxGroupAddrInHash;
- location =
- UCC_GETH_ENET_ADDRESS_RECOGNITION_LOCATION_GROUP_HASH;
- p_counter = &(ugeth->numGroupAddrInHash);
- } else {
- p_lh = &ugeth->ind_hash_q;
- limit = ugeth->ug_info->maxIndAddrInHash;
- location =
- UCC_GETH_ENET_ADDRESS_RECOGNITION_LOCATION_INDIVIDUAL_HASH;
- p_counter = &(ugeth->numIndAddrInHash);
- }
-
- if ((enet_addr_cont =
- ugeth_82xx_filtering_get_match_addr_in_hash(ugeth, p_enet_addr))) {
- list_add(p_lh, &enet_addr_cont->node); /* Put it back */
- return 0;
- }
- if ((!p_lh) || (!(*p_counter < limit)))
- return -EBUSY;
- if (!(enet_addr_cont = get_enet_addr_container()))
- return -ENOMEM;
- for (i = 0; i < ENET_NUM_OCTETS_PER_ADDRESS; i++)
- (enet_addr_cont->address)[i] = (*p_enet_addr)[i];
- enet_addr_cont->location = location;
- enqueue(p_lh, &enet_addr_cont->node); /* Put it back */
- ++(*p_counter);
-
- hw_add_addr_in_hash(ugeth, enet_addr_cont->address);
- return 0;
-}
-
-static int ugeth_82xx_filtering_clear_addr_in_hash(struct ucc_geth_private *ugeth,
- struct enet_addr *p_enet_addr)
-{
- struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt;
- struct enet_addr_container *enet_addr_cont;
- struct ucc_fast_private *uccf;
- enum comm_dir comm_dir;
- u16 i, num;
- struct list_head *p_lh;
- u32 *addr_h, *addr_l;
- u8 *p_counter;
-
- uccf = ugeth->uccf;
-
- p_82xx_addr_filt =
- (struct ucc_geth_82xx_address_filtering_pram *) ugeth->p_rx_glbl_pram->
- addressfiltering;
-
- if (!
- (enet_addr_cont =
- ugeth_82xx_filtering_get_match_addr_in_hash(ugeth, p_enet_addr)))
- return -ENOENT;
-
- /* It's been found and removed from the CQ. */
- /* Now destroy its container */
- put_enet_addr_container(enet_addr_cont);
-
- if ((*p_enet_addr)[0] & ENET_GROUP_ADDR) {
- addr_h = &(p_82xx_addr_filt->gaddr_h);
- addr_l = &(p_82xx_addr_filt->gaddr_l);
- p_lh = &ugeth->group_hash_q;
- p_counter = &(ugeth->numGroupAddrInHash);
- } else {
- addr_h = &(p_82xx_addr_filt->iaddr_h);
- addr_l = &(p_82xx_addr_filt->iaddr_l);
- p_lh = &ugeth->ind_hash_q;
- p_counter = &(ugeth->numIndAddrInHash);
- }
-
- comm_dir = 0;
- if (uccf->enabled_tx)
- comm_dir |= COMM_DIR_TX;
- if (uccf->enabled_rx)
- comm_dir |= COMM_DIR_RX;
- if (comm_dir)
- ugeth_disable(ugeth, comm_dir);
-
- /* Clear the hash table. */
- out_be32(addr_h, 0x00000000);
- out_be32(addr_l, 0x00000000);
-
- /* Add all remaining CQ elements back into hash */
- num = --(*p_counter);
- for (i = 0; i < num; i++) {
- enet_addr_cont =
- (struct enet_addr_container *)
- ENET_ADDR_CONT_ENTRY(dequeue(p_lh));
- hw_add_addr_in_hash(ugeth, enet_addr_cont->address);
- enqueue(p_lh, &enet_addr_cont->node); /* Put it back */
- }
-
- if (comm_dir)
- ugeth_enable(ugeth, comm_dir);
-
- return 0;
-}
-#endif /* CONFIG_UGETH_FILTERING */
-
static int ugeth_82xx_filtering_clear_all_addr_in_hash(struct ucc_geth_private *
ugeth,
enum enet_addr_type
@@ -2051,28 +1814,6 @@ static int ugeth_82xx_filtering_clear_all_addr_in_hash(struct ucc_geth_private *
return 0;
}
-#ifdef CONFIG_UGETH_FILTERING
-static int ugeth_82xx_filtering_add_addr_in_paddr(struct ucc_geth_private *ugeth,
- struct enet_addr *p_enet_addr,
- u8 paddr_num)
-{
- int i;
-
- if ((*p_enet_addr)[0] & ENET_GROUP_ADDR)
- ugeth_warn
- ("%s: multicast address added to paddr will have no "
- "effect - is this what you wanted?",
- __func__);
-
- ugeth->indAddrRegUsed[paddr_num] = 1; /* mark this paddr as used */
- /* store address in our database */
- for (i = 0; i < ENET_NUM_OCTETS_PER_ADDRESS; i++)
- ugeth->paddr[paddr_num][i] = (*p_enet_addr)[i];
- /* put in hardware */
- return hw_add_addr_in_paddr(ugeth, p_enet_addr, paddr_num);
-}
-#endif /* CONFIG_UGETH_FILTERING */
-
static int ugeth_82xx_filtering_clear_addr_in_paddr(struct ucc_geth_private *ugeth,
u8 paddr_num)
{
@@ -2215,7 +1956,10 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth)
while (!list_empty(&ugeth->ind_hash_q))
put_enet_addr_container(ENET_ADDR_CONT_ENTRY
(dequeue(&ugeth->ind_hash_q)));
-
+ if (ugeth->ug_regs) {
+ iounmap(ugeth->ug_regs);
+ ugeth->ug_regs = NULL;
+ }
}
static void ucc_geth_set_multi(struct net_device *dev)
@@ -2297,8 +2041,6 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth)
tempval &= ~(MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX);
out_be32(&ug_regs->maccfg1, tempval);
- free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
-
ucc_geth_memclean(ugeth);
}
@@ -2419,11 +2161,15 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
if (ucc_fast_init(uf_info, &ugeth->uccf)) {
if (netif_msg_probe(ugeth))
ugeth_err("%s: Failed to init uccf.", __func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
- ugeth->ug_regs = (struct ucc_geth __iomem *) ioremap(uf_info->regs, sizeof(struct ucc_geth));
+ ugeth->ug_regs = ioremap(uf_info->regs, sizeof(*ugeth->ug_regs));
+ if (!ugeth->ug_regs) {
+ if (netif_msg_probe(ugeth))
+ ugeth_err("%s: Failed to ioremap regs.", __func__);
+ return -ENOMEM;
+ }
return 0;
}
@@ -2475,7 +2221,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Bad number of Rx threads value.",
__func__);
- ucc_geth_memclean(ugeth);
return -EINVAL;
break;
}
@@ -2500,7 +2245,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Bad number of Tx threads value.",
__func__);
- ucc_geth_memclean(ugeth);
return -EINVAL;
break;
}
@@ -2554,7 +2298,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: IPGIFG initialization parameter too large.",
__func__);
- ucc_geth_memclean(ugeth);
return ret_val;
}
@@ -2572,7 +2315,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Half Duplex initialization parameter too large.",
__func__);
- ucc_geth_memclean(ugeth);
return ret_val;
}
@@ -2627,7 +2369,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate memory for Tx bd rings.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
/* Zero unused end of bd ring, according to spec */
@@ -2663,7 +2404,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate memory for Rx bd rings.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
}
@@ -2679,7 +2419,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Could not allocate tx_skbuff",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2711,7 +2450,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Could not allocate rx_skbuff",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2745,7 +2483,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_tx_glbl_pram.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
ugeth->p_tx_glbl_pram =
@@ -2768,7 +2505,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_thread_data_tx.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2798,7 +2534,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_send_q_mem_reg.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2842,7 +2577,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_scheduler.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2893,7 +2627,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
("%s: Can not allocate DPRAM memory for"
" p_tx_fw_statistics_pram.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
ugeth->p_tx_fw_statistics_pram =
@@ -2933,7 +2666,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_rx_glbl_pram.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
ugeth->p_rx_glbl_pram =
@@ -2955,7 +2687,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_thread_data_rx.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -2979,7 +2710,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for"
" p_rx_fw_statistics_pram.", __func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
ugeth->p_rx_fw_statistics_pram =
@@ -3002,7 +2732,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for"
" p_rx_irq_coalescing_tbl.", __func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -3071,7 +2800,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_rx_bd_qs_tbl.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -3148,7 +2876,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Null Extended Filtering Chain Pointer.",
__func__);
- ucc_geth_memclean(ugeth);
return -EINVAL;
}
@@ -3162,7 +2889,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for"
" p_exf_glbl_param.", __func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
@@ -3210,7 +2936,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate memory for"
" p_UccInitEnetParamShadows.", __func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
/* Zero out *p_init_enet_param_shadow */
@@ -3245,7 +2970,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Invalid largest External Lookup Key Size.",
__func__);
- ucc_geth_memclean(ugeth);
return -EINVAL;
}
ugeth->p_init_enet_param_shadow->largestexternallookupkeysize =
@@ -3272,7 +2996,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Can not fill p_init_enet_param_shadow.",
__func__);
- ucc_geth_memclean(ugeth);
return ret_val;
}
@@ -3288,7 +3011,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Can not fill p_init_enet_param_shadow.",
__func__);
- ucc_geth_memclean(ugeth);
return ret_val;
}
@@ -3298,7 +3020,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Can not fill Rx bds with buffers.",
__func__);
- ucc_geth_memclean(ugeth);
return ret_val;
}
}
@@ -3310,7 +3031,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth_err
("%s: Can not allocate DPRAM memory for p_init_enet_pram.",
__func__);
- ucc_geth_memclean(ugeth);
return -ENOMEM;
}
p_init_enet_pram =
@@ -3352,28 +3072,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
return 0;
}
-/* ucc_geth_timeout gets called when a packet has not been
- * transmitted after a set amount of time.
- * For now, assume that clearing out all the structures, and
- * starting over will fix the problem. */
-static void ucc_geth_timeout(struct net_device *dev)
-{
- struct ucc_geth_private *ugeth = netdev_priv(dev);
-
- ugeth_vdbg("%s: IN", __func__);
-
- dev->stats.tx_errors++;
-
- ugeth_dump_regs(ugeth);
-
- if (dev->flags & IFF_UP) {
- ucc_geth_stop(ugeth);
- ucc_geth_startup(ugeth);
- }
-
- netif_tx_schedule_all(dev);
-}
-
/* This is called by the kernel when a frame is ready for transmission. */
/* It is pointed to by the dev->hard_start_xmit function pointer */
static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -3502,8 +3200,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit
netif_receive_skb(skb);
}
- ugeth->dev->last_rx = jiffies;
-
skb = get_new_skb(ugeth, bd);
if (!skb) {
if (netif_msg_rx_err(ugeth))
@@ -3592,7 +3288,7 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget)
struct ucc_fast_private *uccf;
u32 uccm;
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
uccf = ugeth->uccf;
uccm = in_be32(uccf->p_uccm);
uccm |= UCCE_RX_EVENTS;
@@ -3626,10 +3322,10 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info)
/* check for receive events that require processing */
if (ucce & UCCE_RX_EVENTS) {
- if (netif_rx_schedule_prep(dev, &ugeth->napi)) {
+ if (netif_rx_schedule_prep(&ugeth->napi)) {
uccm &= ~UCCE_RX_EVENTS;
out_be32(uccf->p_uccm, uccm);
- __netif_rx_schedule(dev, &ugeth->napi);
+ __netif_rx_schedule(&ugeth->napi);
}
}
@@ -3697,7 +3393,7 @@ static int ucc_geth_open(struct net_device *dev)
if (err) {
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Cannot configure internal struct, aborting.", dev->name);
- return err;
+ goto out_err_stop;
}
napi_enable(&ugeth->napi);
@@ -3738,22 +3434,19 @@ static int ucc_geth_open(struct net_device *dev)
phy_start(ugeth->phydev);
- err =
- request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0,
- "UCC Geth", dev);
+ err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
if (err) {
if (netif_msg_ifup(ugeth))
- ugeth_err("%s: Cannot get IRQ for net device, aborting.",
- dev->name);
- ucc_geth_stop(ugeth);
+ ugeth_err("%s: Cannot enable net device, aborting.", dev->name);
goto out_err;
}
- err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX);
+ err = request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler,
+ 0, "UCC Geth", dev);
if (err) {
if (netif_msg_ifup(ugeth))
- ugeth_err("%s: Cannot enable net device, aborting.", dev->name);
- ucc_geth_stop(ugeth);
+ ugeth_err("%s: Cannot get IRQ for net device, aborting.",
+ dev->name);
goto out_err;
}
@@ -3763,7 +3456,8 @@ static int ucc_geth_open(struct net_device *dev)
out_err:
napi_disable(&ugeth->napi);
-
+out_err_stop:
+ ucc_geth_stop(ugeth);
return err;
}
@@ -3778,6 +3472,8 @@ static int ucc_geth_close(struct net_device *dev)
ucc_geth_stop(ugeth);
+ free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev);
+
phy_disconnect(ugeth->phydev);
ugeth->phydev = NULL;
@@ -3786,6 +3482,45 @@ static int ucc_geth_close(struct net_device *dev)
return 0;
}
+/* Reopen device. This will reset the MAC and PHY. */
+static void ucc_geth_timeout_work(struct work_struct *work)
+{
+ struct ucc_geth_private *ugeth;
+ struct net_device *dev;
+
+ ugeth = container_of(work, struct ucc_geth_private, timeout_work);
+ dev = ugeth->dev;
+
+ ugeth_vdbg("%s: IN", __func__);
+
+ dev->stats.tx_errors++;
+
+ ugeth_dump_regs(ugeth);
+
+ if (dev->flags & IFF_UP) {
+ /*
+ * Must reset MAC *and* PHY. This is done by reopening
+ * the device.
+ */
+ ucc_geth_close(dev);
+ ucc_geth_open(dev);
+ }
+
+ netif_tx_schedule_all(dev);
+}
+
+/*
+ * ucc_geth_timeout gets called when a packet has not been
+ * transmitted after a set amount of time.
+ */
+static void ucc_geth_timeout(struct net_device *dev)
+{
+ struct ucc_geth_private *ugeth = netdev_priv(dev);
+
+ netif_carrier_off(dev);
+ schedule_work(&ugeth->timeout_work);
+}
+
static phy_interface_t to_phy_interface(const char *phy_connection_type)
{
if (strcasecmp(phy_connection_type, "mii") == 0)
@@ -4026,6 +3761,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
dev->hard_start_xmit = ucc_geth_start_xmit;
dev->tx_timeout = ucc_geth_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
+ INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work);
netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, UCC_GETH_DEV_WEIGHT);
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = ucc_netpoll;
diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h
index abc0e2242634..d74d2f7cb739 100644
--- a/drivers/net/ucc_geth.h
+++ b/drivers/net/ucc_geth.h
@@ -1186,6 +1186,7 @@ struct ucc_geth_private {
struct ucc_fast_private *uccf;
struct net_device *dev;
struct napi_struct napi;
+ struct work_struct timeout_work;
struct ucc_geth __iomem *ug_regs;
struct ucc_geth_init_pram *p_init_enet_param_shadow;
struct ucc_geth_exf_global_pram __iomem *p_exf_glbl_param;
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index de57490103fc..e009481c606c 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -246,10 +246,11 @@ out:
static void asix_async_cmd_callback(struct urb *urb)
{
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+ int status = urb->status;
- if (urb->status < 0)
+ if (status < 0)
printk(KERN_DEBUG "asix_async_cmd_callback() failed with %d",
- urb->status);
+ status);
kfree(req);
usb_free_urb(urb);
diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c
index 466a89e24444..cb7acbbb2798 100644
--- a/drivers/net/usb/catc.c
+++ b/drivers/net/usb/catc.c
@@ -229,14 +229,15 @@ static void catc_rx_done(struct urb *urb)
u8 *pkt_start = urb->transfer_buffer;
struct sk_buff *skb;
int pkt_len, pkt_offset = 0;
+ int status = urb->status;
if (!catc->is_f5u011) {
clear_bit(RX_RUNNING, &catc->flags);
pkt_offset = 2;
}
- if (urb->status) {
- dbg("rx_done, status %d, length %d", urb->status, urb->actual_length);
+ if (status) {
+ dbg("rx_done, status %d, length %d", status, urb->actual_length);
return;
}
@@ -271,16 +272,14 @@ static void catc_rx_done(struct urb *urb)
} while (pkt_start - (u8 *) urb->transfer_buffer < urb->actual_length);
- catc->netdev->last_rx = jiffies;
-
if (catc->is_f5u011) {
if (atomic_read(&catc->recq_sz)) {
- int status;
+ int state;
atomic_dec(&catc->recq_sz);
dbg("getting extra packet");
urb->dev = catc->usbdev;
- if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- dbg("submit(rx_urb) status %d", status);
+ if ((state = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ dbg("submit(rx_urb) status %d", state);
}
} else {
clear_bit(RX_RUNNING, &catc->flags);
@@ -292,8 +291,9 @@ static void catc_irq_done(struct urb *urb)
{
struct catc *catc = urb->context;
u8 *data = urb->transfer_buffer;
- int status;
+ int status = urb->status;
unsigned int hasdata = 0, linksts = LinkNoChange;
+ int res;
if (!catc->is_f5u011) {
hasdata = data[1] & 0x80;
@@ -309,7 +309,7 @@ static void catc_irq_done(struct urb *urb)
linksts = LinkBad;
}
- switch (urb->status) {
+ switch (status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
@@ -318,7 +318,7 @@ static void catc_irq_done(struct urb *urb)
return;
/* -EPIPE: should clear the halt */
default: /* error */
- dbg("irq_done, status %d, data %02x %02x.", urb->status, data[0], data[1]);
+ dbg("irq_done, status %d, data %02x %02x.", status, data[0], data[1]);
goto resubmit;
}
@@ -338,17 +338,17 @@ static void catc_irq_done(struct urb *urb)
atomic_inc(&catc->recq_sz);
} else {
catc->rx_urb->dev = catc->usbdev;
- if ((status = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) {
- err("submit(rx_urb) status %d", status);
+ if ((res = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) {
+ err("submit(rx_urb) status %d", res);
}
}
}
resubmit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
- if (status)
+ res = usb_submit_urb (urb, GFP_ATOMIC);
+ if (res)
err ("can't resubmit intr, %s-%s, status %d",
catc->usbdev->bus->bus_name,
- catc->usbdev->devpath, status);
+ catc->usbdev->devpath, res);
}
/*
@@ -380,9 +380,9 @@ static void catc_tx_done(struct urb *urb)
{
struct catc *catc = urb->context;
unsigned long flags;
- int r;
+ int r, status = urb->status;
- if (urb->status == -ECONNRESET) {
+ if (status == -ECONNRESET) {
dbg("Tx Reset.");
urb->status = 0;
catc->netdev->trans_start = jiffies;
@@ -392,8 +392,8 @@ static void catc_tx_done(struct urb *urb)
return;
}
- if (urb->status) {
- dbg("tx_done, status %d, length %d", urb->status, urb->actual_length);
+ if (status) {
+ dbg("tx_done, status %d, length %d", status, urb->actual_length);
return;
}
@@ -504,9 +504,10 @@ static void catc_ctrl_done(struct urb *urb)
struct catc *catc = urb->context;
struct ctrl_queue *q;
unsigned long flags;
+ int status = urb->status;
- if (urb->status)
- dbg("ctrl_done, status %d, len %d.", urb->status, urb->actual_length);
+ if (status)
+ dbg("ctrl_done, status %d, len %d.", status, urb->actual_length);
spin_lock_irqsave(&catc->ctrl_lock, flags);
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index db3377dae9d5..edd244f3acb5 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -123,10 +123,11 @@ static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
static void dm_write_async_callback(struct urb *urb)
{
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+ int status = urb->status;
- if (urb->status < 0)
+ if (status < 0)
printk(KERN_DEBUG "dm_write_async_callback() failed with %d\n",
- urb->status);
+ status);
kfree(req);
usb_free_urb(urb);
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 8e90891f0e42..9f7896a25f1b 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -417,6 +417,11 @@ static const struct usb_device_id hso_ids[] = {
{USB_DEVICE(0x0af0, 0x7401)}, /* GI 0401 */
{USB_DEVICE(0x0af0, 0x7501)}, /* GTM 382 */
{USB_DEVICE(0x0af0, 0x7601)}, /* GE40x */
+ {USB_DEVICE(0x0af0, 0x7701)},
+ {USB_DEVICE(0x0af0, 0x7801)},
+ {USB_DEVICE(0x0af0, 0x7901)},
+ {USB_DEVICE(0x0af0, 0x7361)},
+ {icon321_port_device(0x0af0, 0xd051)},
{}
};
MODULE_DEVICE_TABLE(usb, hso_ids);
@@ -658,10 +663,9 @@ static int hso_net_open(struct net_device *net)
odev->rx_buf_missing = sizeof(struct iphdr);
spin_unlock_irqrestore(&odev->net_lock, flags);
- hso_start_net_device(odev->parent);
-
/* We are up and running. */
set_bit(HSO_NET_RUNNING, &odev->flags);
+ hso_start_net_device(odev->parent);
/* Tell the kernel we are ready to start receiving from it */
netif_start_queue(net);
@@ -2750,18 +2754,21 @@ static int hso_resume(struct usb_interface *iface)
if (network_table[i] &&
(network_table[i]->interface == iface)) {
hso_net = dev2net(network_table[i]);
- /* First transmit any lingering data, then restart the
- * device. */
- if (hso_net->skb_tx_buf) {
- dev_dbg(&iface->dev,
- "Transmitting lingering data\n");
- hso_net_start_xmit(hso_net->skb_tx_buf,
- hso_net->net);
- hso_net->skb_tx_buf = NULL;
+ if (hso_net->flags & IFF_UP) {
+ /* First transmit any lingering data,
+ then restart the device. */
+ if (hso_net->skb_tx_buf) {
+ dev_dbg(&iface->dev,
+ "Transmitting"
+ " lingering data\n");
+ hso_net_start_xmit(hso_net->skb_tx_buf,
+ hso_net->net);
+ hso_net->skb_tx_buf = NULL;
+ }
+ result = hso_start_net_device(network_table[i]);
+ if (result)
+ goto out;
}
- result = hso_start_net_device(network_table[i]);
- if (result)
- goto out;
}
}
@@ -2824,7 +2831,7 @@ static struct usb_endpoint_descriptor *hso_get_ep(struct usb_interface *intf,
for (i = 0; i < iface->desc.bNumEndpoints; i++) {
endp = &iface->endpoint[i].desc;
if (((endp->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir) &&
- ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type))
+ (usb_endpoint_type(endp) == type))
return endp;
}
@@ -2894,6 +2901,7 @@ static struct usb_driver hso_driver = {
.id_table = hso_ids,
.suspend = hso_suspend,
.resume = hso_resume,
+ .reset_resume = hso_resume,
.supports_autosuspend = 1,
};
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index fdbf3be24fda..2ee034f70d1c 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -516,8 +516,9 @@ static void int_callback(struct urb *u)
{
struct kaweth_device *kaweth = u->context;
int act_state;
+ int status = u->status;
- switch (u->status) {
+ switch (status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
@@ -598,6 +599,7 @@ static void kaweth_usb_receive(struct urb *urb)
{
struct kaweth_device *kaweth = urb->context;
struct net_device *net = kaweth->net;
+ int status = urb->status;
int count = urb->actual_length;
int count2 = urb->transfer_buffer_length;
@@ -606,7 +608,7 @@ static void kaweth_usb_receive(struct urb *urb)
struct sk_buff *skb;
- if(unlikely(urb->status == -ECONNRESET || urb->status == -ESHUTDOWN))
+ if(unlikely(status == -ECONNRESET || status == -ESHUTDOWN))
/* we are killed - set a flag and wake the disconnect handler */
{
kaweth->end = 1;
@@ -621,10 +623,10 @@ static void kaweth_usb_receive(struct urb *urb)
}
spin_unlock(&kaweth->device_lock);
- if(urb->status && urb->status != -EREMOTEIO && count != 1) {
+ if(status && status != -EREMOTEIO && count != 1) {
err("%s RX status: %d count: %d packet_len: %d",
net->name,
- urb->status,
+ status,
count,
(int)pkt_len);
kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
@@ -775,10 +777,11 @@ static void kaweth_usb_transmit_complete(struct urb *urb)
{
struct kaweth_device *kaweth = urb->context;
struct sk_buff *skb = kaweth->tx_skb;
+ int status = urb->status;
- if (unlikely(urb->status != 0))
- if (urb->status != -ENOENT)
- dbg("%s: TX status %d.", kaweth->net->name, urb->status);
+ if (unlikely(status != 0))
+ if (status != -ENOENT)
+ dbg("%s: TX status %d.", kaweth->net->name, status);
netif_wake_queue(kaweth->net);
dev_kfree_skb_irq(skb);
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index b5143509e8be..5385d66b306e 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -115,10 +115,11 @@ static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data)
static void mcs7830_async_cmd_callback(struct urb *urb)
{
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+ int status = urb->status;
- if (urb->status < 0)
+ if (status < 0)
printk(KERN_DEBUG "%s() failed with %d\n",
- __func__, urb->status);
+ __func__, status);
kfree(req);
usb_free_urb(urb);
@@ -344,14 +345,14 @@ out:
static int mcs7830_mdio_read(struct net_device *netdev, int phy_id,
int location)
{
- struct usbnet *dev = netdev->priv;
+ struct usbnet *dev = netdev_priv(netdev);
return mcs7830_read_phy(dev, location);
}
static void mcs7830_mdio_write(struct net_device *netdev, int phy_id,
int location, int val)
{
- struct usbnet *dev = netdev->priv;
+ struct usbnet *dev = netdev_priv(netdev);
mcs7830_write_phy(dev, location, val);
}
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index 7914867110ed..166880c113d6 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -99,11 +99,12 @@ static int update_eth_regs_async(pegasus_t *);
static void ctrl_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
+ int status = urb->status;
if (!pegasus)
return;
- switch (urb->status) {
+ switch (status) {
case 0:
if (pegasus->flags & ETH_REGS_CHANGE) {
pegasus->flags &= ~ETH_REGS_CHANGE;
@@ -119,7 +120,7 @@ static void ctrl_callback(struct urb *urb)
default:
if (netif_msg_drv(pegasus) && printk_ratelimit())
dev_dbg(&pegasus->intf->dev, "%s, status %d\n",
- __func__, urb->status);
+ __func__, status);
}
pegasus->flags &= ~ETH_REGS_CHANGED;
wake_up(&pegasus->ctrl_wait);
@@ -611,6 +612,7 @@ static void read_bulk_callback(struct urb *urb)
pegasus_t *pegasus = urb->context;
struct net_device *net;
int rx_status, count = urb->actual_length;
+ int status = urb->status;
u8 *buf = urb->transfer_buffer;
__u16 pkt_len;
@@ -621,7 +623,7 @@ static void read_bulk_callback(struct urb *urb)
if (!netif_device_present(net) || !netif_running(net))
return;
- switch (urb->status) {
+ switch (status) {
case 0:
break;
case -ETIME:
@@ -639,11 +641,11 @@ static void read_bulk_callback(struct urb *urb)
case -ECONNRESET:
case -ESHUTDOWN:
if (netif_msg_ifdown(pegasus))
- pr_debug("%s: rx unlink, %d\n", net->name, urb->status);
+ pr_debug("%s: rx unlink, %d\n", net->name, status);
return;
default:
if (netif_msg_rx_err(pegasus))
- pr_debug("%s: RX status %d\n", net->name, urb->status);
+ pr_debug("%s: RX status %d\n", net->name, status);
goto goon;
}
@@ -769,6 +771,7 @@ static void write_bulk_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
struct net_device *net;
+ int status = urb->status;
if (!pegasus)
return;
@@ -778,7 +781,7 @@ static void write_bulk_callback(struct urb *urb)
if (!netif_device_present(net) || !netif_running(net))
return;
- switch (urb->status) {
+ switch (status) {
case -EPIPE:
/* FIXME schedule_work() to clear the tx halt */
netif_stop_queue(net);
@@ -790,11 +793,11 @@ static void write_bulk_callback(struct urb *urb)
case -ECONNRESET:
case -ESHUTDOWN:
if (netif_msg_ifdown(pegasus))
- pr_debug("%s: tx unlink, %d\n", net->name, urb->status);
+ pr_debug("%s: tx unlink, %d\n", net->name, status);
return;
default:
if (netif_msg_tx_err(pegasus))
- pr_info("%s: TX status %d\n", net->name, urb->status);
+ pr_info("%s: TX status %d\n", net->name, status);
/* FALL THROUGH */
case 0:
break;
@@ -808,13 +811,13 @@ static void intr_callback(struct urb *urb)
{
pegasus_t *pegasus = urb->context;
struct net_device *net;
- int status;
+ int res, status = urb->status;
if (!pegasus)
return;
net = pegasus->net;
- switch (urb->status) {
+ switch (status) {
case 0:
break;
case -ECONNRESET: /* unlink */
@@ -827,7 +830,7 @@ static void intr_callback(struct urb *urb)
*/
if (netif_msg_timer(pegasus))
pr_debug("%s: intr status %d\n", net->name,
- urb->status);
+ status);
}
if (urb->actual_length >= 6) {
@@ -854,12 +857,12 @@ static void intr_callback(struct urb *urb)
pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
}
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status == -ENODEV)
+ res = usb_submit_urb(urb, GFP_ATOMIC);
+ if (res == -ENODEV)
netif_device_detach(pegasus->net);
- if (status && netif_msg_timer(pegasus))
+ if (res && netif_msg_timer(pegasus))
printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n",
- net->name, status);
+ net->name, res);
}
static void pegasus_tx_timeout(struct net_device *net)
@@ -1213,7 +1216,7 @@ static void pegasus_set_multicast(struct net_device *net)
pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
if (netif_msg_link(pegasus))
- pr_info("%s: set allmulti\n", net->name);
+ pr_debug("%s: set allmulti\n", net->name);
} else {
pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
@@ -1273,6 +1276,7 @@ static inline void setup_pegasus_II(pegasus_t * pegasus)
}
+static int pegasus_count;
static struct workqueue_struct *pegasus_workqueue = NULL;
#define CARRIER_CHECK_DELAY (2 * HZ)
@@ -1301,6 +1305,18 @@ static int pegasus_blacklisted(struct usb_device *udev)
return 0;
}
+/* we rely on probe() and remove() being serialized so we
+ * don't need extra locking on pegasus_count.
+ */
+static void pegasus_dec_workqueue(void)
+{
+ pegasus_count--;
+ if (pegasus_count == 0) {
+ destroy_workqueue(pegasus_workqueue);
+ pegasus_workqueue = NULL;
+ }
+}
+
static int pegasus_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1309,14 +1325,18 @@ static int pegasus_probe(struct usb_interface *intf,
pegasus_t *pegasus;
int dev_index = id - pegasus_ids;
int res = -ENOMEM;
- DECLARE_MAC_BUF(mac);
- usb_get_dev(dev);
+ if (pegasus_blacklisted(dev))
+ return -ENODEV;
- if (pegasus_blacklisted(dev)) {
- res = -ENODEV;
- goto out;
+ if (pegasus_count == 0) {
+ pegasus_workqueue = create_singlethread_workqueue("pegasus");
+ if (!pegasus_workqueue)
+ return -ENOMEM;
}
+ pegasus_count++;
+
+ usb_get_dev(dev);
net = alloc_etherdev(sizeof(struct pegasus));
if (!net) {
@@ -1386,10 +1406,10 @@ static int pegasus_probe(struct usb_interface *intf,
queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
CARRIER_CHECK_DELAY);
- dev_info(&intf->dev, "%s, %s, %s\n",
+ dev_info(&intf->dev, "%s, %s, %pM\n",
net->name,
usb_dev_id[dev_index].name,
- print_mac(mac, net->dev_addr));
+ net->dev_addr);
return 0;
out3:
@@ -1401,6 +1421,7 @@ out1:
free_netdev(net);
out:
usb_put_dev(dev);
+ pegasus_dec_workqueue();
return res;
}
@@ -1426,6 +1447,7 @@ static void pegasus_disconnect(struct usb_interface *intf)
pegasus->rx_skb = NULL;
}
free_netdev(pegasus->net);
+ pegasus_dec_workqueue();
}
static int pegasus_suspend (struct usb_interface *intf, pm_message_t message)
@@ -1469,7 +1491,7 @@ static struct usb_driver pegasus_driver = {
.resume = pegasus_resume,
};
-static void parse_id(char *id)
+static void __init parse_id(char *id)
{
unsigned int vendor_id=0, device_id=0, flags=0, i=0;
char *token, *name=NULL;
@@ -1505,15 +1527,11 @@ static int __init pegasus_init(void)
pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
if (devid)
parse_id(devid);
- pegasus_workqueue = create_singlethread_workqueue("pegasus");
- if (!pegasus_workqueue)
- return -ENOMEM;
return usb_register(&pegasus_driver);
}
static void __exit pegasus_exit(void)
{
- destroy_workqueue(pegasus_workqueue);
usb_deregister(&pegasus_driver);
}
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 6133401ebc67..d8664bf18c00 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -212,8 +212,9 @@ static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
static void ctrl_callback(struct urb *urb)
{
rtl8150_t *dev;
+ int status = urb->status;
- switch (urb->status) {
+ switch (status) {
case 0:
break;
case -EINPROGRESS:
@@ -221,7 +222,7 @@ static void ctrl_callback(struct urb *urb)
case -ENOENT:
break;
default:
- dev_warn(&urb->dev->dev, "ctrl urb status %d\n", urb->status);
+ dev_warn(&urb->dev->dev, "ctrl urb status %d\n", status);
}
dev = urb->context;
clear_bit(RX_REG_SET, &dev->flags);
@@ -424,7 +425,8 @@ static void read_bulk_callback(struct urb *urb)
struct sk_buff *skb;
struct net_device *netdev;
u16 rx_stat;
- int status;
+ int status = urb->status;
+ int result;
dev = urb->context;
if (!dev)
@@ -435,7 +437,7 @@ static void read_bulk_callback(struct urb *urb)
if (!netif_device_present(netdev))
return;
- switch (urb->status) {
+ switch (status) {
case 0:
break;
case -ENOENT:
@@ -444,7 +446,7 @@ static void read_bulk_callback(struct urb *urb)
dev_warn(&urb->dev->dev, "may be reset is needed?..\n");
goto goon;
default:
- dev_warn(&urb->dev->dev, "Rx status %d\n", urb->status);
+ dev_warn(&urb->dev->dev, "Rx status %d\n", status);
goto goon;
}
@@ -474,10 +476,10 @@ static void read_bulk_callback(struct urb *urb)
goon:
usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
- status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
- if (status == -ENODEV)
+ result = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
+ if (result == -ENODEV)
netif_device_detach(dev->netdev);
- else if (status) {
+ else if (result) {
set_bit(RX_URB_FAIL, &dev->flags);
goto resched;
} else {
@@ -530,6 +532,7 @@ tlsched:
static void write_bulk_callback(struct urb *urb)
{
rtl8150_t *dev;
+ int status = urb->status;
dev = urb->context;
if (!dev)
@@ -537,9 +540,9 @@ static void write_bulk_callback(struct urb *urb)
dev_kfree_skb_irq(dev->tx_skb);
if (!netif_device_present(dev->netdev))
return;
- if (urb->status)
+ if (status)
dev_info(&urb->dev->dev, "%s: Tx status %d\n",
- dev->netdev->name, urb->status);
+ dev->netdev->name, status);
dev->netdev->trans_start = jiffies;
netif_wake_queue(dev->netdev);
}
@@ -548,12 +551,13 @@ static void intr_callback(struct urb *urb)
{
rtl8150_t *dev;
__u8 *d;
- int status;
+ int status = urb->status;
+ int res;
dev = urb->context;
if (!dev)
return;
- switch (urb->status) {
+ switch (status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
@@ -563,7 +567,7 @@ static void intr_callback(struct urb *urb)
/* -EPIPE: should clear the halt */
default:
dev_info(&urb->dev->dev, "%s: intr status %d\n",
- dev->netdev->name, urb->status);
+ dev->netdev->name, status);
goto resubmit;
}
@@ -591,13 +595,13 @@ static void intr_callback(struct urb *urb)
}
resubmit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
- if (status == -ENODEV)
+ res = usb_submit_urb (urb, GFP_ATOMIC);
+ if (res == -ENODEV)
netif_device_detach(dev->netdev);
- else if (status)
+ else if (res)
err ("can't resubmit intr, %s-%s/input0, status %d",
dev->udev->bus->bus_name,
- dev->udev->devpath, status);
+ dev->udev->devpath, res);
}
static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 51e2f5d7d14e..5574abe29c73 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -31,7 +31,7 @@
#include "smsc95xx.h"
#define SMSC_CHIPNAME "smsc95xx"
-#define SMSC_DRIVER_VERSION "1.0.3"
+#define SMSC_DRIVER_VERSION "1.0.4"
#define HS_USB_PKT_SIZE (512)
#define FS_USB_PKT_SIZE (64)
#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
@@ -40,15 +40,16 @@
#define MAX_SINGLE_PACKET_SIZE (2048)
#define LAN95XX_EEPROM_MAGIC (0x9500)
#define EEPROM_MAC_OFFSET (0x01)
+#define DEFAULT_TX_CSUM_ENABLE (true)
#define DEFAULT_RX_CSUM_ENABLE (true)
#define SMSC95XX_INTERNAL_PHY_ID (1)
#define SMSC95XX_TX_OVERHEAD (8)
-#define FLOW_CTRL_TX (1)
-#define FLOW_CTRL_RX (2)
+#define SMSC95XX_TX_OVERHEAD_CSUM (12)
struct smsc95xx_priv {
u32 mac_cr;
spinlock_t mac_cr_lock;
+ bool use_tx_csum;
bool use_rx_csum;
};
@@ -310,9 +311,10 @@ static void smsc95xx_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_context *usb_context = urb->context;
struct usbnet *dev = usb_context->dev;
+ int status = urb->status;
- if (urb->status < 0)
- devwarn(dev, "async callback failed with %d", urb->status);
+ if (status < 0)
+ devwarn(dev, "async callback failed with %d", status);
complete(&usb_context->notify);
@@ -434,28 +436,6 @@ static void smsc95xx_set_multicast(struct net_device *netdev)
smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr);
}
-static u8 smsc95xx_resolve_flowctrl_fulldplx(u16 lcladv, u16 rmtadv)
-{
- u8 cap = 0;
-
- if (lcladv & ADVERTISE_PAUSE_CAP) {
- if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
- else if (rmtadv & LPA_PAUSE_ASYM)
- cap = FLOW_CTRL_RX;
- } else {
- if (rmtadv & LPA_PAUSE_CAP)
- cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
- }
- } else if (lcladv & ADVERTISE_PAUSE_ASYM) {
- if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM))
- cap = FLOW_CTRL_TX;
- }
-
- return cap;
-}
-
static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
u16 lcladv, u16 rmtadv)
{
@@ -468,7 +448,7 @@ static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
}
if (duplex == DUPLEX_FULL) {
- u8 cap = smsc95xx_resolve_flowctrl_fulldplx(lcladv, rmtadv);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
if (cap & FLOW_CTRL_RX)
flow = 0xFFFF0002;
@@ -556,9 +536,10 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata);
}
-/* Enable or disable Rx checksum offload engine */
-static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable)
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct usbnet *dev)
{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
u32 read_buf;
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
if (ret < 0) {
@@ -566,7 +547,12 @@ static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable)
return ret;
}
- if (enable)
+ if (pdata->use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (pdata->use_rx_csum)
read_buf |= Rx_COE_EN_;
else
read_buf &= ~Rx_COE_EN_;
@@ -626,7 +612,26 @@ static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
pdata->use_rx_csum = !!val;
- return smsc95xx_set_rx_csum(dev, pdata->use_rx_csum);
+ return smsc95xx_set_csums(dev);
+}
+
+static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+
+ return pdata->use_tx_csum;
+}
+
+static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val)
+{
+ struct usbnet *dev = netdev_priv(netdev);
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+
+ pdata->use_tx_csum = !!val;
+
+ ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
+ return smsc95xx_set_csums(dev);
}
static struct ethtool_ops smsc95xx_ethtool_ops = {
@@ -640,6 +645,8 @@ static struct ethtool_ops smsc95xx_ethtool_ops = {
.get_eeprom_len = smsc95xx_ethtool_get_eeprom_len,
.get_eeprom = smsc95xx_ethtool_get_eeprom,
.set_eeprom = smsc95xx_ethtool_set_eeprom,
+ .get_tx_csum = smsc95xx_ethtool_get_tx_csum,
+ .set_tx_csum = smsc95xx_ethtool_set_tx_csum,
.get_rx_csum = smsc95xx_ethtool_get_rx_csum,
.set_rx_csum = smsc95xx_ethtool_set_rx_csum,
};
@@ -757,9 +764,9 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
static int smsc95xx_reset(struct usbnet *dev)
{
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ struct net_device *netdev = dev->net;
u32 read_buf, write_buf, burst_cap;
int ret = 0, timeout;
- DECLARE_MAC_BUF(mac);
if (netif_msg_ifup(dev))
devdbg(dev, "entering smsc95xx_reset");
@@ -818,8 +825,7 @@ static int smsc95xx_reset(struct usbnet *dev)
return ret;
if (netif_msg_ifup(dev))
- devdbg(dev, "MAC Address: %s",
- print_mac(mac, dev->net->dev_addr));
+ devdbg(dev, "MAC Address: %pM", dev->net->dev_addr);
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0) {
@@ -970,10 +976,11 @@ static int smsc95xx_reset(struct usbnet *dev)
return ret;
}
- /* Enable or disable Rx checksum offload engine */
- ret = smsc95xx_set_rx_csum(dev, pdata->use_rx_csum);
+ /* Enable or disable checksum offload engines */
+ ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum);
+ ret = smsc95xx_set_csums(dev);
if (ret < 0) {
- devwarn(dev, "Failed to set Rx csum offload: %d", ret);
+ devwarn(dev, "Failed to set csum offload: %d", ret);
return ret;
}
@@ -1029,6 +1036,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
spin_lock_init(&pdata->mac_cr_lock);
+ pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE;
pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
/* Init all registers */
@@ -1148,22 +1156,44 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
return 1;
}
+static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb)
+{
+ int len = skb->data - skb->head;
+ u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len);
+ u16 low_16 = (u16)(skb->csum_start - len);
+ return (high_16 << 16) | low_16;
+}
+
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
+ struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+ bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL);
+ int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD;
u32 tx_cmd_a, tx_cmd_b;
- if (skb_headroom(skb) < SMSC95XX_TX_OVERHEAD) {
+ /* We do not advertise SG, so skbs should be already linearized */
+ BUG_ON(skb_shinfo(skb)->nr_frags);
+
+ if (skb_headroom(skb) < overhead) {
struct sk_buff *skb2 = skb_copy_expand(skb,
- SMSC95XX_TX_OVERHEAD, 0, flags);
+ overhead, 0, flags);
dev_kfree_skb_any(skb);
skb = skb2;
if (!skb)
return NULL;
}
+ if (csum) {
+ u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
+ skb_push(skb, 4);
+ memcpy(skb->data, &csum_preamble, 4);
+ }
+
skb_push(skb, 4);
tx_cmd_b = (u32)(skb->len - 4);
+ if (csum)
+ tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
cpu_to_le32s(&tx_cmd_b);
memcpy(skb->data, &tx_cmd_b, 4);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 02d25c743994..aa3149078888 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1125,7 +1125,6 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
struct usb_device *xdev;
int status;
const char *name;
- DECLARE_MAC_BUF(mac);
name = udev->dev.driver->name;
info = (struct driver_info *) prod->driver_info;
@@ -1236,11 +1235,11 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
if (status)
goto out3;
if (netif_msg_probe (dev))
- devinfo (dev, "register '%s' at usb-%s-%s, %s, %s",
+ devinfo (dev, "register '%s' at usb-%s-%s, %s, %pM",
udev->dev.driver->name,
xdev->bus->bus_name, xdev->devpath,
dev->driver_info->description,
- print_mac(mac, net->dev_addr));
+ net->dev_addr);
// ok, it's ready to go.
usb_set_intfdata (udev, dev);
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 31cd817f33f9..852d0e7c4e62 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -8,7 +8,6 @@
*
*/
-#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
@@ -30,14 +29,10 @@ struct veth_net_stats {
struct veth_priv {
struct net_device *peer;
- struct net_device *dev;
- struct list_head list;
struct veth_net_stats *stats;
unsigned ip_summed;
};
-static LIST_HEAD(veth_list);
-
/*
* ethtool interface
*/
@@ -267,16 +262,20 @@ static void veth_dev_free(struct net_device *dev)
free_netdev(dev);
}
+static const struct net_device_ops veth_netdev_ops = {
+ .ndo_init = veth_dev_init,
+ .ndo_open = veth_open,
+ .ndo_start_xmit = veth_xmit,
+ .ndo_get_stats = veth_get_stats,
+};
+
static void veth_setup(struct net_device *dev)
{
ether_setup(dev);
- dev->hard_start_xmit = veth_xmit;
- dev->get_stats = veth_get_stats;
- dev->open = veth_open;
+ dev->netdev_ops = &veth_netdev_ops;
dev->ethtool_ops = &veth_ethtool_ops;
dev->features |= NETIF_F_LLTX;
- dev->init = veth_dev_init;
dev->destructor = veth_dev_free;
}
@@ -302,7 +301,7 @@ static int veth_device_event(struct notifier_block *unused,
{
struct net_device *dev = ptr;
- if (dev->open != veth_open)
+ if (dev->netdev_ops->ndo_open != veth_open)
goto out;
switch (event) {
@@ -420,14 +419,10 @@ static int veth_newlink(struct net_device *dev,
*/
priv = netdev_priv(dev);
- priv->dev = dev;
priv->peer = peer;
- list_add(&priv->list, &veth_list);
priv = netdev_priv(peer);
- priv->dev = peer;
priv->peer = dev;
- INIT_LIST_HEAD(&priv->list);
return 0;
err_register_dev:
@@ -449,13 +444,6 @@ static void veth_dellink(struct net_device *dev)
priv = netdev_priv(dev);
peer = priv->peer;
- if (!list_empty(&priv->list))
- list_del(&priv->list);
-
- priv = netdev_priv(peer);
- if (!list_empty(&priv->list))
- list_del(&priv->list);
-
unregister_netdevice(dev);
unregister_netdevice(peer);
}
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 5b7870080c56..ac07cc6e3cb2 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -191,12 +191,13 @@ IIId. Synchronization
The driver runs as two independent, single-threaded flows of control. One
is the send-packet routine, which enforces single-threaded use by the
-dev->priv->lock spinlock. The other thread is the interrupt handler, which
-is single threaded by the hardware and interrupt handling software.
+netdev_priv(dev)->lock spinlock. The other thread is the interrupt handler,
+which is single threaded by the hardware and interrupt handling software.
The send packet thread has partial control over the Tx ring. It locks the
-dev->priv->lock whenever it's queuing a Tx packet. If the next slot in the ring
-is not available it stops the transmit queue by calling netif_stop_queue.
+netdev_priv(dev)->lock whenever it's queuing a Tx packet. If the next slot in
+the ring is not available it stops the transmit queue by
+calling netif_stop_queue.
The interrupt handler has exclusive control over the Rx ring and records stats
from the Tx ring. After reaping the stats, it marks the Tx queue entry as
@@ -588,7 +589,7 @@ static int rhine_napipoll(struct napi_struct *napi, int budget)
work_done = rhine_rx(dev, budget);
if (work_done < budget) {
- netif_rx_complete(dev, napi);
+ netif_rx_complete(napi);
iowrite16(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow |
IntrRxDropped | IntrRxNoBuf | IntrTxAborted |
@@ -614,6 +615,20 @@ static void __devinit rhine_hw_init(struct net_device *dev, long pioaddr)
rhine_reload_eeprom(pioaddr, dev);
}
+static const struct net_device_ops rhine_netdev_ops = {
+ .ndo_open = rhine_open,
+ .ndo_stop = rhine_close,
+ .ndo_start_xmit = rhine_start_tx,
+ .ndo_get_stats = rhine_get_stats,
+ .ndo_set_multicast_list = rhine_set_rx_mode,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_tx_timeout = rhine_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = rhine_poll,
+#endif
+};
+
static int __devinit rhine_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -631,7 +646,6 @@ static int __devinit rhine_init_one(struct pci_dev *pdev,
#else
int bar = 0;
#endif
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -765,18 +779,10 @@ static int __devinit rhine_init_one(struct pci_dev *pdev,
rp->mii_if.reg_num_mask = 0x1f;
/* The chip-specific entries in the device structure. */
- dev->open = rhine_open;
- dev->hard_start_xmit = rhine_start_tx;
- dev->stop = rhine_close;
- dev->get_stats = rhine_get_stats;
- dev->set_multicast_list = rhine_set_rx_mode;
- dev->do_ioctl = netdev_ioctl;
- dev->ethtool_ops = &netdev_ethtool_ops;
- dev->tx_timeout = rhine_tx_timeout;
+ dev->netdev_ops = &rhine_netdev_ops;
+ dev->ethtool_ops = &netdev_ethtool_ops,
dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = rhine_poll;
-#endif
+
netif_napi_add(dev, &rp->napi, rhine_napipoll, 64);
if (rp->quirks & rqRhineI)
@@ -787,14 +793,14 @@ static int __devinit rhine_init_one(struct pci_dev *pdev,
if (rc)
goto err_out_unmap;
- printk(KERN_INFO "%s: VIA %s at 0x%lx, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: VIA %s at 0x%lx, %pM, IRQ %d.\n",
dev->name, name,
#ifdef USE_MMIO
memaddr,
#else
(long)ioaddr,
#endif
- print_mac(mac, dev->dev_addr), pdev->irq);
+ dev->dev_addr, pdev->irq);
pci_set_drvdata(pdev, dev);
@@ -1312,7 +1318,7 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
IntrPCIErr | IntrStatsMax | IntrLinkChange,
ioaddr + IntrEnable);
- netif_rx_schedule(dev, &rp->napi);
+ netif_rx_schedule(&rp->napi);
}
if (intr_status & (IntrTxErrSummary | IntrTxDone)) {
@@ -1505,7 +1511,6 @@ static int rhine_rx(struct net_device *dev, int limit)
}
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
- dev->last_rx = jiffies;
rp->stats.rx_bytes += pkt_len;
rp->stats.rx_packets++;
}
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index 11cb3e504e1c..58e25d090ae0 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -849,6 +849,20 @@ static int velocity_soft_reset(struct velocity_info *vptr)
return 0;
}
+static const struct net_device_ops velocity_netdev_ops = {
+ .ndo_open = velocity_open,
+ .ndo_stop = velocity_close,
+ .ndo_start_xmit = velocity_xmit,
+ .ndo_get_stats = velocity_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_multicast_list = velocity_set_multi,
+ .ndo_change_mtu = velocity_change_mtu,
+ .ndo_do_ioctl = velocity_ioctl,
+ .ndo_vlan_rx_add_vid = velocity_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = velocity_vlan_rx_kill_vid,
+ .ndo_vlan_rx_register = velocity_vlan_rx_register,
+};
+
/**
* velocity_found1 - set up discovered velocity card
* @pdev: PCI device
@@ -958,18 +972,8 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi
vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs);
dev->irq = pdev->irq;
- dev->open = velocity_open;
- dev->hard_start_xmit = velocity_xmit;
- dev->stop = velocity_close;
- dev->get_stats = velocity_get_stats;
- dev->set_multicast_list = velocity_set_multi;
- dev->do_ioctl = velocity_ioctl;
+ dev->netdev_ops = &velocity_netdev_ops;
dev->ethtool_ops = &velocity_ethtool_ops;
- dev->change_mtu = velocity_change_mtu;
-
- dev->vlan_rx_add_vid = velocity_vlan_rx_add_vid;
- dev->vlan_rx_kill_vid = velocity_vlan_rx_kill_vid;
- dev->vlan_rx_register = velocity_vlan_rx_register;
#ifdef VELOCITY_ZERO_COPY_SUPPORT
dev->features |= NETIF_F_SG;
@@ -1412,8 +1416,6 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status)
rd->size |= RX_INTEN;
- vptr->dev->last_rx = jiffies;
-
rd_curr++;
if (rd_curr >= vptr->options.numrx)
rd_curr = 0;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 0196a0df9021..b7004ff36451 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -34,6 +34,7 @@ module_param(gso, bool, 0444);
/* FIXME: MTU in config. */
#define MAX_PACKET_LEN (ETH_HLEN+ETH_DATA_LEN)
+#define GOOD_COPY_LEN 128
struct virtnet_info
{
@@ -58,6 +59,9 @@ struct virtnet_info
/* I like... big packets and I cannot lie! */
bool big_packets;
+ /* Host will merge rx buffers for big packets (shake it! shake it!) */
+ bool mergeable_rx_bufs;
+
/* Receive & send queues. */
struct sk_buff_head recv;
struct sk_buff_head send;
@@ -66,22 +70,27 @@ struct virtnet_info
struct page *pages;
};
-static inline struct virtio_net_hdr *skb_vnet_hdr(struct sk_buff *skb)
+static inline void *skb_vnet_hdr(struct sk_buff *skb)
{
return (struct virtio_net_hdr *)skb->cb;
}
-static inline void vnet_hdr_to_sg(struct scatterlist *sg, struct sk_buff *skb)
-{
- sg_init_one(sg, skb_vnet_hdr(skb), sizeof(struct virtio_net_hdr));
-}
-
static void give_a_page(struct virtnet_info *vi, struct page *page)
{
page->private = (unsigned long)vi->pages;
vi->pages = page;
}
+static void trim_pages(struct virtnet_info *vi, struct sk_buff *skb)
+{
+ unsigned int i;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+ give_a_page(vi, skb_shinfo(skb)->frags[i].page);
+ skb_shinfo(skb)->nr_frags = 0;
+ skb->data_len = 0;
+}
+
static struct page *get_a_page(struct virtnet_info *vi, gfp_t gfp_mask)
{
struct page *p = vi->pages;
@@ -111,31 +120,97 @@ static void skb_xmit_done(struct virtqueue *svq)
static void receive_skb(struct net_device *dev, struct sk_buff *skb,
unsigned len)
{
+ struct virtnet_info *vi = netdev_priv(dev);
struct virtio_net_hdr *hdr = skb_vnet_hdr(skb);
int err;
+ int i;
if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) {
pr_debug("%s: short packet %i\n", dev->name, len);
dev->stats.rx_length_errors++;
goto drop;
}
- len -= sizeof(struct virtio_net_hdr);
- if (len <= MAX_PACKET_LEN) {
- unsigned int i;
+ if (vi->mergeable_rx_bufs) {
+ struct virtio_net_hdr_mrg_rxbuf *mhdr = skb_vnet_hdr(skb);
+ unsigned int copy;
+ char *p = page_address(skb_shinfo(skb)->frags[0].page);
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
- give_a_page(dev->priv, skb_shinfo(skb)->frags[i].page);
- skb->data_len = 0;
- skb_shinfo(skb)->nr_frags = 0;
- }
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ len -= sizeof(struct virtio_net_hdr_mrg_rxbuf);
- err = pskb_trim(skb, len);
- if (err) {
- pr_debug("%s: pskb_trim failed %i %d\n", dev->name, len, err);
- dev->stats.rx_dropped++;
- goto drop;
+ memcpy(hdr, p, sizeof(*mhdr));
+ p += sizeof(*mhdr);
+
+ copy = len;
+ if (copy > skb_tailroom(skb))
+ copy = skb_tailroom(skb);
+
+ memcpy(skb_put(skb, copy), p, copy);
+
+ len -= copy;
+
+ if (!len) {
+ give_a_page(vi, skb_shinfo(skb)->frags[0].page);
+ skb_shinfo(skb)->nr_frags--;
+ } else {
+ skb_shinfo(skb)->frags[0].page_offset +=
+ sizeof(*mhdr) + copy;
+ skb_shinfo(skb)->frags[0].size = len;
+ skb->data_len += len;
+ skb->len += len;
+ }
+
+ while (--mhdr->num_buffers) {
+ struct sk_buff *nskb;
+
+ i = skb_shinfo(skb)->nr_frags;
+ if (i >= MAX_SKB_FRAGS) {
+ pr_debug("%s: packet too long %d\n", dev->name,
+ len);
+ dev->stats.rx_length_errors++;
+ goto drop;
+ }
+
+ nskb = vi->rvq->vq_ops->get_buf(vi->rvq, &len);
+ if (!nskb) {
+ pr_debug("%s: rx error: %d buffers missing\n",
+ dev->name, mhdr->num_buffers);
+ dev->stats.rx_length_errors++;
+ goto drop;
+ }
+
+ __skb_unlink(nskb, &vi->recv);
+ vi->num--;
+
+ skb_shinfo(skb)->frags[i] = skb_shinfo(nskb)->frags[0];
+ skb_shinfo(nskb)->nr_frags = 0;
+ kfree_skb(nskb);
+
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+
+ skb_shinfo(skb)->frags[i].size = len;
+ skb_shinfo(skb)->nr_frags++;
+ skb->data_len += len;
+ skb->len += len;
+ }
+ } else {
+ len -= sizeof(struct virtio_net_hdr);
+
+ if (len <= MAX_PACKET_LEN)
+ trim_pages(vi, skb);
+
+ err = pskb_trim(skb, len);
+ if (err) {
+ pr_debug("%s: pskb_trim failed %i %d\n", dev->name,
+ len, err);
+ dev->stats.rx_dropped++;
+ goto drop;
+ }
}
+
skb->truesize += skb->data_len;
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
@@ -194,7 +269,7 @@ drop:
dev_kfree_skb(skb);
}
-static void try_fill_recv(struct virtnet_info *vi)
+static void try_fill_recv_maxbufs(struct virtnet_info *vi)
{
struct sk_buff *skb;
struct scatterlist sg[2+MAX_SKB_FRAGS];
@@ -202,12 +277,16 @@ static void try_fill_recv(struct virtnet_info *vi)
sg_init_table(sg, 2+MAX_SKB_FRAGS);
for (;;) {
+ struct virtio_net_hdr *hdr;
+
skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN);
if (unlikely(!skb))
break;
skb_put(skb, MAX_PACKET_LEN);
- vnet_hdr_to_sg(sg, skb);
+
+ hdr = skb_vnet_hdr(skb);
+ sg_init_one(sg, hdr, sizeof(*hdr));
if (vi->big_packets) {
for (i = 0; i < MAX_SKB_FRAGS; i++) {
@@ -232,6 +311,55 @@ static void try_fill_recv(struct virtnet_info *vi)
err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, num, skb);
if (err) {
skb_unlink(skb, &vi->recv);
+ trim_pages(vi, skb);
+ kfree_skb(skb);
+ break;
+ }
+ vi->num++;
+ }
+ if (unlikely(vi->num > vi->max))
+ vi->max = vi->num;
+ vi->rvq->vq_ops->kick(vi->rvq);
+}
+
+static void try_fill_recv(struct virtnet_info *vi)
+{
+ struct sk_buff *skb;
+ struct scatterlist sg[1];
+ int err;
+
+ if (!vi->mergeable_rx_bufs) {
+ try_fill_recv_maxbufs(vi);
+ return;
+ }
+
+ for (;;) {
+ skb_frag_t *f;
+
+ skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN);
+ if (unlikely(!skb))
+ break;
+
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ f = &skb_shinfo(skb)->frags[0];
+ f->page = get_a_page(vi, GFP_ATOMIC);
+ if (!f->page) {
+ kfree_skb(skb);
+ break;
+ }
+
+ f->page_offset = 0;
+ f->size = PAGE_SIZE;
+
+ skb_shinfo(skb)->nr_frags++;
+
+ sg_init_one(sg, page_address(f->page), PAGE_SIZE);
+ skb_queue_head(&vi->recv, skb);
+
+ err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, 1, skb);
+ if (err) {
+ skb_unlink(skb, &vi->recv);
kfree_skb(skb);
break;
}
@@ -246,9 +374,9 @@ static void skb_recv_done(struct virtqueue *rvq)
{
struct virtnet_info *vi = rvq->vdev->priv;
/* Schedule NAPI, Suppress further interrupts if successful. */
- if (netif_rx_schedule_prep(vi->dev, &vi->napi)) {
+ if (netif_rx_schedule_prep(&vi->napi)) {
rvq->vq_ops->disable_cb(rvq);
- __netif_rx_schedule(vi->dev, &vi->napi);
+ __netif_rx_schedule(&vi->napi);
}
}
@@ -274,11 +402,11 @@ again:
/* Out of packets? */
if (received < budget) {
- netif_rx_complete(vi->dev, napi);
+ netif_rx_complete(napi);
if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq))
&& napi_schedule_prep(napi)) {
vi->rvq->vq_ops->disable_cb(vi->rvq);
- __netif_rx_schedule(vi->dev, napi);
+ __netif_rx_schedule(napi);
goto again;
}
}
@@ -320,17 +448,14 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
{
int num, err;
struct scatterlist sg[2+MAX_SKB_FRAGS];
- struct virtio_net_hdr *hdr;
+ struct virtio_net_hdr_mrg_rxbuf *mhdr = skb_vnet_hdr(skb);
+ struct virtio_net_hdr *hdr = skb_vnet_hdr(skb);
const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
sg_init_table(sg, 2+MAX_SKB_FRAGS);
- pr_debug("%s: xmit %p " MAC_FMT "\n", vi->dev->name, skb,
- dest[0], dest[1], dest[2],
- dest[3], dest[4], dest[5]);
+ pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
- /* Encode metadata header at front. */
- hdr = skb_vnet_hdr(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL) {
hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
hdr->csum_start = skb->csum_start - skb_headroom(skb);
@@ -358,7 +483,14 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
hdr->gso_size = hdr->hdr_len = 0;
}
- vnet_hdr_to_sg(sg, skb);
+ mhdr->num_buffers = 0;
+
+ /* Encode metadata header at front. */
+ if (vi->mergeable_rx_bufs)
+ sg_init_one(sg, mhdr, sizeof(*mhdr));
+ else
+ sg_init_one(sg, hdr, sizeof(*hdr));
+
num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
@@ -448,9 +580,9 @@ static int virtnet_open(struct net_device *dev)
* won't get another interrupt, so process any outstanding packets
* now. virtnet_poll wants re-enable the queue, so we disable here.
* We synchronize against interrupts via NAPI_STATE_SCHED */
- if (netif_rx_schedule_prep(dev, &vi->napi)) {
+ if (netif_rx_schedule_prep(&vi->napi)) {
vi->rvq->vq_ops->disable_cb(vi->rvq);
- __netif_rx_schedule(dev, &vi->napi);
+ __netif_rx_schedule(&vi->napi);
}
return 0;
}
@@ -478,8 +610,20 @@ static int virtnet_set_tx_csum(struct net_device *dev, u32 data)
static struct ethtool_ops virtnet_ethtool_ops = {
.set_tx_csum = virtnet_set_tx_csum,
.set_sg = ethtool_op_set_sg,
+ .set_tso = ethtool_op_set_tso,
};
+#define MIN_MTU 68
+#define MAX_MTU 65535
+
+static int virtnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (new_mtu < MIN_MTU || new_mtu > MAX_MTU)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
static int virtnet_probe(struct virtio_device *vdev)
{
int err;
@@ -495,6 +639,7 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->open = virtnet_open;
dev->stop = virtnet_close;
dev->hard_start_xmit = start_xmit;
+ dev->change_mtu = virtnet_change_mtu;
dev->features = NETIF_F_HIGHDMA;
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = virtnet_netpoll;
@@ -547,6 +692,9 @@ static int virtnet_probe(struct virtio_device *vdev)
|| virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
vi->big_packets = true;
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
+ vi->mergeable_rx_bufs = true;
+
/* We expect two virtqueues, receive then send. */
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) {
@@ -639,6 +787,7 @@ static unsigned int features[] = {
VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */
+ VIRTIO_NET_F_MRG_RXBUF,
VIRTIO_F_NOTIFY_ON_EMPTY,
};
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index 21efd99b9294..d08ce6a264cb 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -207,6 +207,8 @@ config PC300
tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)"
depends on HDLC && PCI && BROKEN
---help---
+ This driver is broken because of struct tty_driver change.
+
Driver for the Cyclades-PC300 synchronous communication boards.
These boards provide synchronous serial interfaces to your
@@ -333,6 +335,13 @@ config DSCC4_PCI_RST
Say Y if your card supports this feature.
+config IXP4XX_HSS
+ tristate "Intel IXP4xx HSS (synchronous serial port) support"
+ depends on HDLC && ARM && ARCH_IXP4XX && IXP4XX_NPE && IXP4XX_QMGR
+ help
+ Say Y here if you want to use built-in HSS ports
+ on IXP4xx processor.
+
config DLCI
tristate "Frame Relay DLCI support"
---help---
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 102549605d09..19d14bc28356 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
obj-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
obj-$(CONFIG_HDLC_FR) += hdlc_fr.o
-obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o syncppp.o
+obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
obj-$(CONFIG_HDLC_X25) += hdlc_x25.o
pc300-y := pc300_drv.o
@@ -41,6 +41,7 @@ obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
obj-$(CONFIG_PCI200SYN) += pci200syn.o
obj-$(CONFIG_PC300TOO) += pc300too.o
+obj-$(CONFIG_IXP4XX_HSS) += ixp4xx_hss.o
clean-files := wanxlfw.inc
$(obj)/wanxl.o: $(obj)/wanxlfw.inc
diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c
index c8e563106a4a..b46897996f7e 100644
--- a/drivers/net/wan/c101.c
+++ b/drivers/net/wan/c101.c
@@ -88,7 +88,7 @@ static card_t **new_card = &first_card;
/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
#define sca_outw(value, reg, card) do { \
writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \
- writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\
+ writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg + 1));\
} while(0)
#define port_to_card(port) (port)
@@ -113,7 +113,7 @@ static inline void openwin(card_t *card, u8 page)
}
-#include "hd6457x.c"
+#include "hd64570.c"
static inline void set_carrier(port_t *port)
@@ -381,7 +381,7 @@ static int __init c101_run(unsigned long irq, unsigned long winbase)
return result;
}
- sca_init_sync_port(card); /* Set up C101 memory */
+ sca_init_port(card); /* Set up C101 memory */
set_carrier(card);
printk(KERN_INFO "%s: Moxa C101 on IRQ%u,"
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 7f97f8d08c39..d80b72e22dea 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -754,7 +754,6 @@ static int cosa_net_rx_done(struct channel_data *chan)
chan->netdev->stats.rx_bytes += chan->cosa->rxsize;
netif_rx(chan->rx_skb);
chan->rx_skb = NULL;
- chan->netdev->last_rx = jiffies;
return 0;
}
diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c
index 5a7303dc0965..5fa52923efa8 100644
--- a/drivers/net/wan/cycx_x25.c
+++ b/drivers/net/wan/cycx_x25.c
@@ -199,6 +199,8 @@ static struct net_device *cycx_x25_get_dev_by_lcn(struct wan_device *wandev,
static struct net_device *
cycx_x25_get_dev_by_dte_addr(struct wan_device *wandev, char *dte);
+static void cycx_x25_chan_setup(struct net_device *dev);
+
#ifdef CYCLOMX_X25_DEBUG
static void hex_dump(char *msg, unsigned char *p, int len);
static void cycx_x25_dump_config(struct cycx_x25_config *conf);
@@ -353,6 +355,12 @@ static int cycx_wan_update(struct wan_device *wandev)
return 0;
}
+/* callback to initialize device */
+static void cycx_x25_chan_setup(struct net_device *dev)
+{
+ dev->init = cycx_netdevice_init;
+}
+
/* Create new logical channel.
* This routine is called by the router when ROUTER_IFNEW IOCTL is being
* handled.
@@ -376,11 +384,12 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
return -EINVAL;
}
- /* allocate and initialize private data */
- chan = kzalloc(sizeof(struct cycx_x25_channel), GFP_KERNEL);
- if (!chan)
+ dev = alloc_netdev(sizeof(struct cycx_x25_channel), conf->name,
+ cycx_x25_chan_setup);
+ if (!dev)
return -ENOMEM;
+ chan = netdev_priv(dev);
strcpy(chan->name, conf->name);
chan->card = card;
chan->link = conf->port;
@@ -396,14 +405,14 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
if (len > WAN_ADDRESS_SZ) {
printk(KERN_ERR "%s: %s local addr too long!\n",
wandev->name, chan->name);
- kfree(chan);
- return -EINVAL;
+ err = -EINVAL;
+ goto error;
} else {
chan->local_addr = kmalloc(len + 1, GFP_KERNEL);
if (!chan->local_addr) {
- kfree(chan);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto error;
}
}
@@ -429,41 +438,31 @@ static int cycx_wan_new_if(struct wan_device *wandev, struct net_device *dev,
"%s: PVC %u is out of range on interface %s!\n",
wandev->name, lcn, chan->name);
err = -EINVAL;
+ goto error;
}
} else {
printk(KERN_ERR "%s: invalid media address on interface %s!\n",
wandev->name, chan->name);
err = -EINVAL;
+ goto error;
}
- if (err) {
- kfree(chan->local_addr);
- kfree(chan);
- return err;
- }
-
- /* prepare network device data space for registration */
- strcpy(dev->name, chan->name);
- dev->init = cycx_netdevice_init;
- dev->priv = chan;
-
return 0;
+
+error:
+ free_netdev(dev);
+ return err;
}
/* Delete logical channel. */
static int cycx_wan_del_if(struct wan_device *wandev, struct net_device *dev)
{
- if (dev->priv) {
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
- if (chan->svc) {
- kfree(chan->local_addr);
- if (chan->state == WAN_CONNECTED)
- del_timer(&chan->timer);
- }
-
- kfree(chan);
- dev->priv = NULL;
+ if (chan->svc) {
+ kfree(chan->local_addr);
+ if (chan->state == WAN_CONNECTED)
+ del_timer(&chan->timer);
}
return 0;
@@ -484,7 +483,7 @@ static const struct header_ops cycx_header_ops = {
* registration. */
static int cycx_netdevice_init(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
struct cycx_device *card = chan->card;
struct wan_device *wandev = &card->wandev;
@@ -542,7 +541,7 @@ static int cycx_netdevice_open(struct net_device *dev)
* o if there's no more open channels then disconnect physical link. */
static int cycx_netdevice_stop(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
netif_stop_queue(dev);
@@ -596,7 +595,7 @@ static int cycx_netdevice_rebuild_header(struct sk_buff *skb)
static int cycx_netdevice_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
struct cycx_device *card = chan->card;
if (!chan->svc)
@@ -670,7 +669,7 @@ free_packet:
* Return a pointer to struct net_device_stats */
static struct net_device_stats *cycx_netdevice_get_stats(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
return chan ? &chan->ifstats : NULL;
}
@@ -783,7 +782,7 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
return;
}
- chan = dev->priv;
+ chan = netdev_priv(dev);
reset_timer(dev);
if (chan->drop_sequence) {
@@ -843,7 +842,6 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd)
skb_reset_mac_header(skb);
netif_rx(skb);
- dev->last_rx = jiffies; /* timestamp */
}
/* Connect interrupt handler. */
@@ -884,7 +882,7 @@ static void cycx_x25_irq_connect(struct cycx_device *card,
return;
}
- chan = dev->priv;
+ chan = netdev_priv(dev);
chan->lcn = lcn;
cycx_x25_connect_response(card, chan);
cycx_x25_set_chan_state(dev, WAN_CONNECTED);
@@ -914,7 +912,7 @@ static void cycx_x25_irq_connect_confirm(struct cycx_device *card,
}
clear_bit(--key, (void*)&card->u.x.connection_keys);
- chan = dev->priv;
+ chan = netdev_priv(dev);
chan->lcn = lcn;
cycx_x25_set_chan_state(dev, WAN_CONNECTED);
}
@@ -954,7 +952,7 @@ static void cycx_x25_irq_disconnect(struct cycx_device *card,
dev = cycx_x25_get_dev_by_lcn(wandev, lcn);
if (dev) {
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
cycx_x25_disconnect_response(card, chan->link, lcn);
cycx_x25_set_chan_state(dev, WAN_DISCONNECTED);
@@ -1302,7 +1300,7 @@ static struct net_device *cycx_x25_get_dev_by_lcn(struct wan_device *wandev,
struct cycx_x25_channel *chan;
while (dev) {
- chan = (struct cycx_x25_channel*)dev->priv;
+ chan = netdev_priv(dev);
if (chan->lcn == lcn)
break;
@@ -1319,7 +1317,7 @@ static struct net_device *
struct cycx_x25_channel *chan;
while (dev) {
- chan = (struct cycx_x25_channel*)dev->priv;
+ chan = netdev_priv(dev);
if (!strcmp(chan->addr, dte))
break;
@@ -1337,7 +1335,7 @@ static struct net_device *
* <0 failure */
static int cycx_x25_chan_connect(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
struct cycx_device *card = chan->card;
if (chan->svc) {
@@ -1362,7 +1360,7 @@ static int cycx_x25_chan_connect(struct net_device *dev)
* o if SVC then clear X.25 call */
static void cycx_x25_chan_disconnect(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
if (chan->svc) {
x25_clear_call(chan->card, chan->link, chan->lcn, 0, 0);
@@ -1375,7 +1373,7 @@ static void cycx_x25_chan_disconnect(struct net_device *dev)
static void cycx_x25_chan_timer(unsigned long d)
{
struct net_device *dev = (struct net_device *)d;
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
if (chan->state == WAN_CONNECTED)
cycx_x25_chan_disconnect(dev);
@@ -1387,7 +1385,7 @@ static void cycx_x25_chan_timer(unsigned long d)
/* Set logical channel state. */
static void cycx_x25_set_chan_state(struct net_device *dev, u8 state)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
struct cycx_device *card = chan->card;
unsigned long flags;
char *string_state = NULL;
@@ -1453,7 +1451,7 @@ static void cycx_x25_set_chan_state(struct net_device *dev, u8 state)
* to the router. */
static int cycx_x25_chan_send(struct net_device *dev, struct sk_buff *skb)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
struct cycx_device *card = chan->card;
int bitm = 0; /* final packet */
unsigned len = skb->len;
@@ -1494,7 +1492,6 @@ static void cycx_x25_chan_send_event(struct net_device *dev, u8 event)
skb->protocol = x25_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies; /* timestamp */
}
/* Convert line speed in bps to a number used by cyclom 2x code. */
@@ -1547,7 +1544,7 @@ static unsigned dec_to_uint(u8 *str, int len)
static void reset_timer(struct net_device *dev)
{
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
if (chan->svc)
mod_timer(&chan->timer, jiffies+chan->idle_tmout*HZ);
@@ -1600,7 +1597,7 @@ static void cycx_x25_dump_devs(struct wan_device *wandev)
printk(KERN_INFO "---------------------------------------\n");
while(dev) {
- struct cycx_x25_channel *chan = dev->priv;
+ struct cycx_x25_channel *chan = netdev_priv(dev);
printk(KERN_INFO "%-5.5s %-15.15s %d ETH_P_%s\n",
chan->name, chan->addr, netif_queue_stopped(dev),
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index b14242768fad..a297e3efa05d 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -74,7 +74,7 @@ static int dlci_header(struct sk_buff *skb, struct net_device *dev,
unsigned int hlen;
char *dest;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
hdr.control = FRAD_I_UI;
switch(type)
@@ -110,7 +110,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
struct frhdr *hdr;
int process, header;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
if (!pskb_may_pull(skb, sizeof(*hdr))) {
printk(KERN_NOTICE "%s: invalid data no header\n",
dev->name);
@@ -181,7 +181,6 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
dlp->stats.rx_bytes += skb->len;
netif_rx(skb);
dlp->stats.rx_packets++;
- dev->last_rx = jiffies;
}
else
dev_kfree_skb(skb);
@@ -197,7 +196,7 @@ static int dlci_transmit(struct sk_buff *skb, struct net_device *dev)
if (!skb || !dev)
return(0);
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
netif_stop_queue(dev);
@@ -235,9 +234,9 @@ static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, in
struct frad_local *flp;
int err;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
- flp = dlp->slave->priv;
+ flp = netdev_priv(dlp->slave);
if (!get)
{
@@ -269,7 +268,7 @@ static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (!capable(CAP_NET_ADMIN))
return(-EPERM);
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
switch(cmd)
{
@@ -298,7 +297,7 @@ static int dlci_change_mtu(struct net_device *dev, int new_mtu)
{
struct dlci_local *dlp;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
return((*dlp->slave->change_mtu)(dlp->slave, new_mtu));
}
@@ -309,7 +308,7 @@ static int dlci_open(struct net_device *dev)
struct frad_local *flp;
int err;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
if (!*(short *)(dev->dev_addr))
return(-EINVAL);
@@ -317,7 +316,7 @@ static int dlci_open(struct net_device *dev)
if (!netif_running(dlp->slave))
return(-ENOTCONN);
- flp = dlp->slave->priv;
+ flp = netdev_priv(dlp->slave);
err = (*flp->activate)(dlp->slave, dev);
if (err)
return(err);
@@ -335,9 +334,9 @@ static int dlci_close(struct net_device *dev)
netif_stop_queue(dev);
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
- flp = dlp->slave->priv;
+ flp = netdev_priv(dlp->slave);
err = (*flp->deactivate)(dlp->slave, dev);
return 0;
@@ -347,7 +346,7 @@ static struct net_device_stats *dlci_get_stats(struct net_device *dev)
{
struct dlci_local *dlp;
- dlp = dev->priv;
+ dlp = netdev_priv(dev);
return(&dlp->stats);
}
@@ -365,7 +364,7 @@ static int dlci_add(struct dlci_add *dlci)
if (!slave)
return -ENODEV;
- if (slave->type != ARPHRD_FRAD || slave->priv == NULL)
+ if (slave->type != ARPHRD_FRAD || netdev_priv(slave) == NULL)
goto err1;
/* create device name */
@@ -391,11 +390,11 @@ static int dlci_add(struct dlci_add *dlci)
*(short *)(master->dev_addr) = dlci->dlci;
- dlp = (struct dlci_local *) master->priv;
+ dlp = netdev_priv(master);
dlp->slave = slave;
dlp->master = master;
- flp = slave->priv;
+ flp = netdev_priv(slave);
err = (*flp->assoc)(slave, master);
if (err < 0)
goto err2;
@@ -435,9 +434,9 @@ static int dlci_del(struct dlci_add *dlci)
return(-EBUSY);
}
- dlp = master->priv;
+ dlp = netdev_priv(master);
slave = dlp->slave;
- flp = slave->priv;
+ flp = netdev_priv(slave);
rtnl_lock();
err = (*flp->deassoc)(slave, master);
@@ -491,7 +490,7 @@ static const struct header_ops dlci_header_ops = {
static void dlci_setup(struct net_device *dev)
{
- struct dlci_local *dlp = dev->priv;
+ struct dlci_local *dlp = netdev_priv(dev);
dev->flags = 0;
dev->open = dlci_open;
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 5f1ccb2b08b1..888025db2f02 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -659,7 +659,6 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv,
skb_put(skb, pkt_len);
if (netif_running(dev))
skb->protocol = hdlc_type_trans(skb, dev);
- skb->dev->last_rx = jiffies;
netif_rx(skb);
} else {
if (skb->data[pkt_len] & FrameRdo)
@@ -730,8 +729,7 @@ static int __devinit dscc4_init_one(struct pci_dev *pdev,
goto err_free_mmio_region_1;
}
- ioaddr = ioremap(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ ioaddr = pci_ioremap_bar(pdev, 0);
if (!ioaddr) {
printk(KERN_ERR "%s: cannot remap MMIO region %llx @ %llx\n",
DRV_NAME, (unsigned long long)pci_resource_len(pdev, 0),
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 9557ad078ab8..48a2c9d28950 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -896,7 +896,6 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port,
fst_process_rx_status(rx_status, port_to_dev(port)->name);
if (rx_status == NET_RX_DROP)
dev->stats.rx_dropped++;
- dev->last_rx = jiffies;
}
/*
@@ -1322,7 +1321,6 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port)
fst_process_rx_status(rx_status, port_to_dev(port)->name);
if (rx_status == NET_RX_DROP)
dev->stats.rx_dropped++;
- dev->last_rx = jiffies;
} else {
card->dma_skb_rx = skb;
card->dma_port_rx = port;
diff --git a/drivers/net/wan/hd64570.c b/drivers/net/wan/hd64570.c
new file mode 100644
index 000000000000..223238de475c
--- /dev/null
+++ b/drivers/net/wan/hd64570.c
@@ -0,0 +1,723 @@
+/*
+ * Hitachi SCA HD64570 driver for Linux
+ *
+ * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ *
+ * Source of information: Hitachi HD64570 SCA User's Manual
+ *
+ * We use the following SCA memory map:
+ *
+ * Packet buffer descriptor rings - starting from winbase or win0base:
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
+ *
+ * Packet data buffers - starting from winbase + buff_offset:
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used)
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used)
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/hdlc.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include "hd64570.h"
+
+#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET)
+#define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
+#define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
+
+#define SCA_INTR_MSCI(node) (node ? 0x10 : 0x01)
+#define SCA_INTR_DMAC_RX(node) (node ? 0x20 : 0x02)
+#define SCA_INTR_DMAC_TX(node) (node ? 0x40 : 0x04)
+
+
+static inline struct net_device *port_to_dev(port_t *port)
+{
+ return port->dev;
+}
+
+static inline int sca_intr_status(card_t *card)
+{
+ u8 result = 0;
+ u8 isr0 = sca_in(ISR0, card);
+ u8 isr1 = sca_in(ISR1, card);
+
+ if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
+ if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
+ if (isr1 & 0x30) result |= SCA_INTR_DMAC_RX(1);
+ if (isr1 & 0xC0) result |= SCA_INTR_DMAC_TX(1);
+ if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
+ if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
+
+ if (!(result & SCA_INTR_DMAC_TX(0)))
+ if (sca_in(DSR_TX(0), card) & DSR_EOM)
+ result |= SCA_INTR_DMAC_TX(0);
+ if (!(result & SCA_INTR_DMAC_TX(1)))
+ if (sca_in(DSR_TX(1), card) & DSR_EOM)
+ result |= SCA_INTR_DMAC_TX(1);
+
+ return result;
+}
+
+static inline port_t* dev_to_port(struct net_device *dev)
+{
+ return dev_to_hdlc(dev)->priv;
+}
+
+static inline u16 next_desc(port_t *port, u16 desc, int transmit)
+{
+ return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
+ : port_to_card(port)->rx_ring_buffers);
+}
+
+
+static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
+{
+ u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
+ u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
+
+ desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
+ return log_node(port) * (rx_buffs + tx_buffs) +
+ transmit * rx_buffs + desc;
+}
+
+
+static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
+{
+ /* Descriptor offset always fits in 16 bits */
+ return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
+}
+
+
+static inline pkt_desc __iomem *desc_address(port_t *port, u16 desc,
+ int transmit)
+{
+#ifdef PAGE0_ALWAYS_MAPPED
+ return (pkt_desc __iomem *)(win0base(port_to_card(port))
+ + desc_offset(port, desc, transmit));
+#else
+ return (pkt_desc __iomem *)(winbase(port_to_card(port))
+ + desc_offset(port, desc, transmit));
+#endif
+}
+
+
+static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
+{
+ return port_to_card(port)->buff_offset +
+ desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
+}
+
+
+static inline void sca_set_carrier(port_t *port)
+{
+ if (!(sca_in(get_msci(port) + ST3, port_to_card(port)) & ST3_DCD)) {
+#ifdef DEBUG_LINK
+ printk(KERN_DEBUG "%s: sca_set_carrier on\n",
+ port_to_dev(port)->name);
+#endif
+ netif_carrier_on(port_to_dev(port));
+ } else {
+#ifdef DEBUG_LINK
+ printk(KERN_DEBUG "%s: sca_set_carrier off\n",
+ port_to_dev(port)->name);
+#endif
+ netif_carrier_off(port_to_dev(port));
+ }
+}
+
+
+static void sca_init_port(port_t *port)
+{
+ card_t *card = port_to_card(port);
+ int transmit, i;
+
+ port->rxin = 0;
+ port->txin = 0;
+ port->txlast = 0;
+
+#ifndef PAGE0_ALWAYS_MAPPED
+ openwin(card, 0);
+#endif
+
+ for (transmit = 0; transmit < 2; transmit++) {
+ u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
+ u16 buffs = transmit ? card->tx_ring_buffers
+ : card->rx_ring_buffers;
+
+ for (i = 0; i < buffs; i++) {
+ pkt_desc __iomem *desc = desc_address(port, i, transmit);
+ u16 chain_off = desc_offset(port, i + 1, transmit);
+ u32 buff_off = buffer_offset(port, i, transmit);
+
+ writew(chain_off, &desc->cp);
+ writel(buff_off, &desc->bp);
+ writew(0, &desc->len);
+ writeb(0, &desc->stat);
+ }
+
+ /* DMA disable - to halt state */
+ sca_out(0, transmit ? DSR_TX(phy_node(port)) :
+ DSR_RX(phy_node(port)), card);
+ /* software ABORT - to initial state */
+ sca_out(DCR_ABORT, transmit ? DCR_TX(phy_node(port)) :
+ DCR_RX(phy_node(port)), card);
+
+ /* current desc addr */
+ sca_out(0, dmac + CPB, card); /* pointer base */
+ sca_outw(desc_offset(port, 0, transmit), dmac + CDAL, card);
+ if (!transmit)
+ sca_outw(desc_offset(port, buffs - 1, transmit),
+ dmac + EDAL, card);
+ else
+ sca_outw(desc_offset(port, 0, transmit), dmac + EDAL,
+ card);
+
+ /* clear frame end interrupt counter */
+ sca_out(DCR_CLEAR_EOF, transmit ? DCR_TX(phy_node(port)) :
+ DCR_RX(phy_node(port)), card);
+
+ if (!transmit) { /* Receive */
+ /* set buffer length */
+ sca_outw(HDLC_MAX_MRU, dmac + BFLL, card);
+ /* Chain mode, Multi-frame */
+ sca_out(0x14, DMR_RX(phy_node(port)), card);
+ sca_out(DIR_EOME | DIR_BOFE, DIR_RX(phy_node(port)),
+ card);
+ /* DMA enable */
+ sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
+ } else { /* Transmit */
+ /* Chain mode, Multi-frame */
+ sca_out(0x14, DMR_TX(phy_node(port)), card);
+ /* enable underflow interrupts */
+ sca_out(DIR_BOFE, DIR_TX(phy_node(port)), card);
+ }
+ }
+ sca_set_carrier(port);
+}
+
+
+#ifdef NEED_SCA_MSCI_INTR
+/* MSCI interrupt service */
+static inline void sca_msci_intr(port_t *port)
+{
+ u16 msci = get_msci(port);
+ card_t* card = port_to_card(port);
+ u8 stat = sca_in(msci + ST1, card); /* read MSCI ST1 status */
+
+ /* Reset MSCI TX underrun and CDCD status bit */
+ sca_out(stat & (ST1_UDRN | ST1_CDCD), msci + ST1, card);
+
+ if (stat & ST1_UDRN) {
+ /* TX Underrun error detected */
+ port_to_dev(port)->stats.tx_errors++;
+ port_to_dev(port)->stats.tx_fifo_errors++;
+ }
+
+ if (stat & ST1_CDCD)
+ sca_set_carrier(port);
+}
+#endif
+
+
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc,
+ u16 rxin)
+{
+ struct net_device *dev = port_to_dev(port);
+ struct sk_buff *skb;
+ u16 len;
+ u32 buff;
+ u32 maxlen;
+ u8 page;
+
+ len = readw(&desc->len);
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ dev->stats.rx_dropped++;
+ return;
+ }
+
+ buff = buffer_offset(port, rxin, 0);
+ page = buff / winsize(card);
+ buff = buff % winsize(card);
+ maxlen = winsize(card) - buff;
+
+ openwin(card, page);
+
+ if (len > maxlen) {
+ memcpy_fromio(skb->data, winbase(card) + buff, maxlen);
+ openwin(card, page + 1);
+ memcpy_fromio(skb->data + maxlen, winbase(card), len - maxlen);
+ } else
+ memcpy_fromio(skb->data, winbase(card) + buff, len);
+
+#ifndef PAGE0_ALWAYS_MAPPED
+ openwin(card, 0); /* select pkt_desc table page back */
+#endif
+ skb_put(skb, len);
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len);
+ debug_frame(skb);
+#endif
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ skb->protocol = hdlc_type_trans(skb, dev);
+ netif_rx(skb);
+}
+
+
+/* Receive DMA interrupt service */
+static inline void sca_rx_intr(port_t *port)
+{
+ struct net_device *dev = port_to_dev(port);
+ u16 dmac = get_dmac_rx(port);
+ card_t *card = port_to_card(port);
+ u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */
+
+ /* Reset DSR status bits */
+ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
+ DSR_RX(phy_node(port)), card);
+
+ if (stat & DSR_BOF)
+ /* Dropped one or more frames */
+ dev->stats.rx_over_errors++;
+
+ while (1) {
+ u32 desc_off = desc_offset(port, port->rxin, 0);
+ pkt_desc __iomem *desc;
+ u32 cda = sca_inw(dmac + CDAL, card);
+
+ if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
+ break; /* No frame received */
+
+ desc = desc_address(port, port->rxin, 0);
+ stat = readb(&desc->stat);
+ if (!(stat & ST_RX_EOM))
+ port->rxpart = 1; /* partial frame received */
+ else if ((stat & ST_ERROR_MASK) || port->rxpart) {
+ dev->stats.rx_errors++;
+ if (stat & ST_RX_OVERRUN)
+ dev->stats.rx_fifo_errors++;
+ else if ((stat & (ST_RX_SHORT | ST_RX_ABORT |
+ ST_RX_RESBIT)) || port->rxpart)
+ dev->stats.rx_frame_errors++;
+ else if (stat & ST_RX_CRC)
+ dev->stats.rx_crc_errors++;
+ if (stat & ST_RX_EOM)
+ port->rxpart = 0; /* received last fragment */
+ } else
+ sca_rx(card, port, desc, port->rxin);
+
+ /* Set new error descriptor address */
+ sca_outw(desc_off, dmac + EDAL, card);
+ port->rxin = next_desc(port, port->rxin, 0);
+ }
+
+ /* make sure RX DMA is enabled */
+ sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
+}
+
+
+/* Transmit DMA interrupt service */
+static inline void sca_tx_intr(port_t *port)
+{
+ struct net_device *dev = port_to_dev(port);
+ u16 dmac = get_dmac_tx(port);
+ card_t* card = port_to_card(port);
+ u8 stat;
+
+ spin_lock(&port->lock);
+
+ stat = sca_in(DSR_TX(phy_node(port)), card); /* read DMA Status */
+
+ /* Reset DSR status bits */
+ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
+ DSR_TX(phy_node(port)), card);
+
+ while (1) {
+ pkt_desc __iomem *desc;
+
+ u32 desc_off = desc_offset(port, port->txlast, 1);
+ u32 cda = sca_inw(dmac + CDAL, card);
+ if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
+ break; /* Transmitter is/will_be sending this frame */
+
+ desc = desc_address(port, port->txlast, 1);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += readw(&desc->len);
+ writeb(0, &desc->stat); /* Free descriptor */
+ port->txlast = next_desc(port, port->txlast, 1);
+ }
+
+ netif_wake_queue(dev);
+ spin_unlock(&port->lock);
+}
+
+
+static irqreturn_t sca_intr(int irq, void* dev_id)
+{
+ card_t *card = dev_id;
+ int i;
+ u8 stat;
+ int handled = 0;
+ u8 page = sca_get_page(card);
+
+ while((stat = sca_intr_status(card)) != 0) {
+ handled = 1;
+ for (i = 0; i < 2; i++) {
+ port_t *port = get_port(card, i);
+ if (port) {
+ if (stat & SCA_INTR_MSCI(i))
+ sca_msci_intr(port);
+
+ if (stat & SCA_INTR_DMAC_RX(i))
+ sca_rx_intr(port);
+
+ if (stat & SCA_INTR_DMAC_TX(i))
+ sca_tx_intr(port);
+ }
+ }
+ }
+
+ openwin(card, page); /* Restore original page */
+ return IRQ_RETVAL(handled);
+}
+
+
+static void sca_set_port(port_t *port)
+{
+ card_t* card = port_to_card(port);
+ u16 msci = get_msci(port);
+ u8 md2 = sca_in(msci + MD2, card);
+ unsigned int tmc, br = 10, brv = 1024;
+
+
+ if (port->settings.clock_rate > 0) {
+ /* Try lower br for better accuracy*/
+ do {
+ br--;
+ brv >>= 1; /* brv = 2^9 = 512 max in specs */
+
+ /* Baud Rate = CLOCK_BASE / TMC / 2^BR */
+ tmc = CLOCK_BASE / brv / port->settings.clock_rate;
+ }while (br > 1 && tmc <= 128);
+
+ if (tmc < 1) {
+ tmc = 1;
+ br = 0; /* For baud=CLOCK_BASE we use tmc=1 br=0 */
+ brv = 1;
+ } else if (tmc > 255)
+ tmc = 256; /* tmc=0 means 256 - low baud rates */
+
+ port->settings.clock_rate = CLOCK_BASE / brv / tmc;
+ } else {
+ br = 9; /* Minimum clock rate */
+ tmc = 256; /* 8bit = 0 */
+ port->settings.clock_rate = CLOCK_BASE / (256 * 512);
+ }
+
+ port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
+ port->txs = (port->txs & ~CLK_BRG_MASK) | br;
+ port->tmc = tmc;
+
+ /* baud divisor - time constant*/
+ sca_out(port->tmc, msci + TMC, card);
+
+ /* Set BRG bits */
+ sca_out(port->rxs, msci + RXS, card);
+ sca_out(port->txs, msci + TXS, card);
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+ else
+ md2 &= ~MD2_LOOPBACK;
+
+ sca_out(md2, msci + MD2, card);
+
+}
+
+
+static void sca_open(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t* card = port_to_card(port);
+ u16 msci = get_msci(port);
+ u8 md0, md2;
+
+ switch(port->encoding) {
+ case ENCODING_NRZ: md2 = MD2_NRZ; break;
+ case ENCODING_NRZI: md2 = MD2_NRZI; break;
+ case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break;
+ case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break;
+ default: md2 = MD2_MANCHESTER;
+ }
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+
+ switch(port->parity) {
+ case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break;
+ case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break;
+ case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
+ case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
+ default: md0 = MD0_HDLC | MD0_CRC_NONE;
+ }
+
+ sca_out(CMD_RESET, msci + CMD, card);
+ sca_out(md0, msci + MD0, card);
+ sca_out(0x00, msci + MD1, card); /* no address field check */
+ sca_out(md2, msci + MD2, card);
+ sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
+ sca_out(CTL_IDLE, msci + CTL, card);
+
+ /* Allow at least 8 bytes before requesting RX DMA operation */
+ /* TX with higher priority and possibly with shorter transfers */
+ sca_out(0x07, msci + RRC, card); /* +1=RXRDY/DMA activation condition*/
+ sca_out(0x10, msci + TRC0, card); /* = TXRDY/DMA activation condition*/
+ sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
+
+/* We're using the following interrupts:
+ - TXINT (DMAC completed all transmisions, underrun or DCD change)
+ - all DMA interrupts
+*/
+ sca_set_carrier(port);
+
+ /* MSCI TX INT and RX INT A IRQ enable */
+ sca_out(IE0_TXINT | IE0_RXINTA, msci + IE0, card);
+ sca_out(IE1_UDRN | IE1_CDCD, msci + IE1, card);
+ sca_out(sca_in(IER0, card) | (phy_node(port) ? 0xC0 : 0x0C),
+ IER0, card); /* TXINT and RXINT */
+ /* enable DMA IRQ */
+ sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
+ IER1, card);
+
+ sca_out(port->tmc, msci + TMC, card); /* Restore registers */
+ sca_out(port->rxs, msci + RXS, card);
+ sca_out(port->txs, msci + TXS, card);
+ sca_out(CMD_TX_ENABLE, msci + CMD, card);
+ sca_out(CMD_RX_ENABLE, msci + CMD, card);
+
+ netif_start_queue(dev);
+}
+
+
+static void sca_close(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t* card = port_to_card(port);
+
+ /* reset channel */
+ sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
+ /* disable MSCI interrupts */
+ sca_out(sca_in(IER0, card) & (phy_node(port) ? 0x0F : 0xF0),
+ IER0, card);
+ /* disable DMA interrupts */
+ sca_out(sca_in(IER1, card) & (phy_node(port) ? 0x0F : 0xF0),
+ IER1, card);
+
+ netif_stop_queue(dev);
+}
+
+
+static int sca_attach(struct net_device *dev, unsigned short encoding,
+ unsigned short parity)
+{
+ if (encoding != ENCODING_NRZ &&
+ encoding != ENCODING_NRZI &&
+ encoding != ENCODING_FM_MARK &&
+ encoding != ENCODING_FM_SPACE &&
+ encoding != ENCODING_MANCHESTER)
+ return -EINVAL;
+
+ if (parity != PARITY_NONE &&
+ parity != PARITY_CRC16_PR0 &&
+ parity != PARITY_CRC16_PR1 &&
+ parity != PARITY_CRC16_PR0_CCITT &&
+ parity != PARITY_CRC16_PR1_CCITT)
+ return -EINVAL;
+
+ dev_to_port(dev)->encoding = encoding;
+ dev_to_port(dev)->parity = parity;
+ return 0;
+}
+
+
+#ifdef DEBUG_RINGS
+static void sca_dump_rings(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t *card = port_to_card(port);
+ u16 cnt;
+#ifndef PAGE0_ALWAYS_MAPPED
+ u8 page = sca_get_page(card);
+
+ openwin(card, 0);
+#endif
+
+ printk(KERN_DEBUG "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
+ sca_inw(get_dmac_rx(port) + CDAL, card),
+ sca_inw(get_dmac_rx(port) + EDAL, card),
+ sca_in(DSR_RX(phy_node(port)), card), port->rxin,
+ sca_in(DSR_RX(phy_node(port)), card) & DSR_DE ? "" : "in");
+ for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
+ printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
+
+ printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
+ "last=%u %sactive",
+ sca_inw(get_dmac_tx(port) + CDAL, card),
+ sca_inw(get_dmac_tx(port) + EDAL, card),
+ sca_in(DSR_TX(phy_node(port)), card), port->txin, port->txlast,
+ sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
+
+ for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
+ printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
+ printk("\n");
+
+ printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x, ST: %02x %02x %02x %02x,"
+ " FST: %02x CST: %02x %02x\n",
+ sca_in(get_msci(port) + MD0, card),
+ sca_in(get_msci(port) + MD1, card),
+ sca_in(get_msci(port) + MD2, card),
+ sca_in(get_msci(port) + ST0, card),
+ sca_in(get_msci(port) + ST1, card),
+ sca_in(get_msci(port) + ST2, card),
+ sca_in(get_msci(port) + ST3, card),
+ sca_in(get_msci(port) + FST, card),
+ sca_in(get_msci(port) + CST0, card),
+ sca_in(get_msci(port) + CST1, card));
+
+ printk(KERN_DEBUG "ISR: %02x %02x %02x\n", sca_in(ISR0, card),
+ sca_in(ISR1, card), sca_in(ISR2, card));
+
+#ifndef PAGE0_ALWAYS_MAPPED
+ openwin(card, page); /* Restore original page */
+#endif
+}
+#endif /* DEBUG_RINGS */
+
+
+static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t *card = port_to_card(port);
+ pkt_desc __iomem *desc;
+ u32 buff, len;
+ u8 page;
+ u32 maxlen;
+
+ spin_lock_irq(&port->lock);
+
+ desc = desc_address(port, port->txin + 1, 1);
+ BUG_ON(readb(&desc->stat)); /* previous xmit should stop queue */
+
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len);
+ debug_frame(skb);
+#endif
+
+ desc = desc_address(port, port->txin, 1);
+ buff = buffer_offset(port, port->txin, 1);
+ len = skb->len;
+ page = buff / winsize(card);
+ buff = buff % winsize(card);
+ maxlen = winsize(card) - buff;
+
+ openwin(card, page);
+ if (len > maxlen) {
+ memcpy_toio(winbase(card) + buff, skb->data, maxlen);
+ openwin(card, page + 1);
+ memcpy_toio(winbase(card), skb->data + maxlen, len - maxlen);
+ } else
+ memcpy_toio(winbase(card) + buff, skb->data, len);
+
+#ifndef PAGE0_ALWAYS_MAPPED
+ openwin(card, 0); /* select pkt_desc table page back */
+#endif
+ writew(len, &desc->len);
+ writeb(ST_TX_EOM, &desc->stat);
+ dev->trans_start = jiffies;
+
+ port->txin = next_desc(port, port->txin, 1);
+ sca_outw(desc_offset(port, port->txin, 1),
+ get_dmac_tx(port) + EDAL, card);
+
+ sca_out(DSR_DE, DSR_TX(phy_node(port)), card); /* Enable TX DMA */
+
+ desc = desc_address(port, port->txin + 1, 1);
+ if (readb(&desc->stat)) /* allow 1 packet gap */
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&port->lock);
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+
+#ifdef NEED_DETECT_RAM
+static u32 __devinit sca_detect_ram(card_t *card, u8 __iomem *rambase,
+ u32 ramsize)
+{
+ /* Round RAM size to 32 bits, fill from end to start */
+ u32 i = ramsize &= ~3;
+ u32 size = winsize(card);
+
+ openwin(card, (i - 4) / size); /* select last window */
+
+ do {
+ i -= 4;
+ if ((i + 4) % size == 0)
+ openwin(card, i / size);
+ writel(i ^ 0x12345678, rambase + i % size);
+ } while (i > 0);
+
+ for (i = 0; i < ramsize ; i += 4) {
+ if (i % size == 0)
+ openwin(card, i / size);
+
+ if (readl(rambase + i % size) != (i ^ 0x12345678))
+ break;
+ }
+
+ return i;
+}
+#endif /* NEED_DETECT_RAM */
+
+
+static void __devinit sca_init(card_t *card, int wait_states)
+{
+ sca_out(wait_states, WCRL, card); /* Wait Control */
+ sca_out(wait_states, WCRM, card);
+ sca_out(wait_states, WCRH, card);
+
+ sca_out(0, DMER, card); /* DMA Master disable */
+ sca_out(0x03, PCR, card); /* DMA priority */
+ sca_out(0, DSR_RX(0), card); /* DMA disable - to halt state */
+ sca_out(0, DSR_TX(0), card);
+ sca_out(0, DSR_RX(1), card);
+ sca_out(0, DSR_TX(1), card);
+ sca_out(DMER_DME, DMER, card); /* DMA Master enable */
+}
diff --git a/drivers/net/wan/hd64572.c b/drivers/net/wan/hd64572.c
new file mode 100644
index 000000000000..08b3536944fe
--- /dev/null
+++ b/drivers/net/wan/hd64572.c
@@ -0,0 +1,640 @@
+/*
+ * Hitachi (now Renesas) SCA-II HD64572 driver for Linux
+ *
+ * Copyright (C) 1998-2008 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ *
+ * Source of information: HD64572 SCA-II User's Manual
+ *
+ * We use the following SCA memory map:
+ *
+ * Packet buffer descriptor rings - starting from card->rambase:
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
+ *
+ * Packet data buffers - starting from card->rambase + buff_offset:
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used)
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used)
+ */
+
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/hdlc.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include "hd64572.h"
+
+#define NAPI_WEIGHT 16
+
+#define get_msci(port) (port->chan ? MSCI1_OFFSET : MSCI0_OFFSET)
+#define get_dmac_rx(port) (port->chan ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
+#define get_dmac_tx(port) (port->chan ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
+
+#define sca_in(reg, card) readb(card->scabase + (reg))
+#define sca_out(value, reg, card) writeb(value, card->scabase + (reg))
+#define sca_inw(reg, card) readw(card->scabase + (reg))
+#define sca_outw(value, reg, card) writew(value, card->scabase + (reg))
+#define sca_inl(reg, card) readl(card->scabase + (reg))
+#define sca_outl(value, reg, card) writel(value, card->scabase + (reg))
+
+static int sca_poll(struct napi_struct *napi, int budget);
+
+static inline port_t* dev_to_port(struct net_device *dev)
+{
+ return dev_to_hdlc(dev)->priv;
+}
+
+static inline void enable_intr(port_t *port)
+{
+ /* enable DMIB and MSCI RXINTA interrupts */
+ sca_outl(sca_inl(IER0, port->card) |
+ (port->chan ? 0x08002200 : 0x00080022), IER0, port->card);
+}
+
+static inline void disable_intr(port_t *port)
+{
+ sca_outl(sca_inl(IER0, port->card) &
+ (port->chan ? 0x00FF00FF : 0xFF00FF00), IER0, port->card);
+}
+
+static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
+{
+ u16 rx_buffs = port->card->rx_ring_buffers;
+ u16 tx_buffs = port->card->tx_ring_buffers;
+
+ desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
+ return port->chan * (rx_buffs + tx_buffs) + transmit * rx_buffs + desc;
+}
+
+
+static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
+{
+ /* Descriptor offset always fits in 16 bits */
+ return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
+}
+
+
+static inline pkt_desc __iomem *desc_address(port_t *port, u16 desc,
+ int transmit)
+{
+ return (pkt_desc __iomem *)(port->card->rambase +
+ desc_offset(port, desc, transmit));
+}
+
+
+static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
+{
+ return port->card->buff_offset +
+ desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
+}
+
+
+static inline void sca_set_carrier(port_t *port)
+{
+ if (!(sca_in(get_msci(port) + ST3, port->card) & ST3_DCD)) {
+#ifdef DEBUG_LINK
+ printk(KERN_DEBUG "%s: sca_set_carrier on\n",
+ port->netdev.name);
+#endif
+ netif_carrier_on(port->netdev);
+ } else {
+#ifdef DEBUG_LINK
+ printk(KERN_DEBUG "%s: sca_set_carrier off\n",
+ port->netdev.name);
+#endif
+ netif_carrier_off(port->netdev);
+ }
+}
+
+
+static void sca_init_port(port_t *port)
+{
+ card_t *card = port->card;
+ u16 dmac_rx = get_dmac_rx(port), dmac_tx = get_dmac_tx(port);
+ int transmit, i;
+
+ port->rxin = 0;
+ port->txin = 0;
+ port->txlast = 0;
+
+ for (transmit = 0; transmit < 2; transmit++) {
+ u16 buffs = transmit ? card->tx_ring_buffers
+ : card->rx_ring_buffers;
+
+ for (i = 0; i < buffs; i++) {
+ pkt_desc __iomem *desc = desc_address(port, i, transmit);
+ u16 chain_off = desc_offset(port, i + 1, transmit);
+ u32 buff_off = buffer_offset(port, i, transmit);
+
+ writel(chain_off, &desc->cp);
+ writel(buff_off, &desc->bp);
+ writew(0, &desc->len);
+ writeb(0, &desc->stat);
+ }
+ }
+
+ /* DMA disable - to halt state */
+ sca_out(0, DSR_RX(port->chan), card);
+ sca_out(0, DSR_TX(port->chan), card);
+
+ /* software ABORT - to initial state */
+ sca_out(DCR_ABORT, DCR_RX(port->chan), card);
+ sca_out(DCR_ABORT, DCR_TX(port->chan), card);
+
+ /* current desc addr */
+ sca_outl(desc_offset(port, 0, 0), dmac_rx + CDAL, card);
+ sca_outl(desc_offset(port, card->tx_ring_buffers - 1, 0),
+ dmac_rx + EDAL, card);
+ sca_outl(desc_offset(port, 0, 1), dmac_tx + CDAL, card);
+ sca_outl(desc_offset(port, 0, 1), dmac_tx + EDAL, card);
+
+ /* clear frame end interrupt counter */
+ sca_out(DCR_CLEAR_EOF, DCR_RX(port->chan), card);
+ sca_out(DCR_CLEAR_EOF, DCR_TX(port->chan), card);
+
+ /* Receive */
+ sca_outw(HDLC_MAX_MRU, dmac_rx + BFLL, card); /* set buffer length */
+ sca_out(0x14, DMR_RX(port->chan), card); /* Chain mode, Multi-frame */
+ sca_out(DIR_EOME, DIR_RX(port->chan), card); /* enable interrupts */
+ sca_out(DSR_DE, DSR_RX(port->chan), card); /* DMA enable */
+
+ /* Transmit */
+ sca_out(0x14, DMR_TX(port->chan), card); /* Chain mode, Multi-frame */
+ sca_out(DIR_EOME, DIR_TX(port->chan), card); /* enable interrupts */
+
+ sca_set_carrier(port);
+ netif_napi_add(port->netdev, &port->napi, sca_poll, NAPI_WEIGHT);
+}
+
+
+/* MSCI interrupt service */
+static inline void sca_msci_intr(port_t *port)
+{
+ u16 msci = get_msci(port);
+ card_t* card = port->card;
+
+ if (sca_in(msci + ST1, card) & ST1_CDCD) {
+ /* Reset MSCI CDCD status bit */
+ sca_out(ST1_CDCD, msci + ST1, card);
+ sca_set_carrier(port);
+ }
+}
+
+
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc,
+ u16 rxin)
+{
+ struct net_device *dev = port->netdev;
+ struct sk_buff *skb;
+ u16 len;
+ u32 buff;
+
+ len = readw(&desc->len);
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ dev->stats.rx_dropped++;
+ return;
+ }
+
+ buff = buffer_offset(port, rxin, 0);
+ memcpy_fromio(skb->data, card->rambase + buff, len);
+
+ skb_put(skb, len);
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len);
+ debug_frame(skb);
+#endif
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ skb->protocol = hdlc_type_trans(skb, dev);
+ netif_receive_skb(skb);
+}
+
+
+/* Receive DMA service */
+static inline int sca_rx_done(port_t *port, int budget)
+{
+ struct net_device *dev = port->netdev;
+ u16 dmac = get_dmac_rx(port);
+ card_t *card = port->card;
+ u8 stat = sca_in(DSR_RX(port->chan), card); /* read DMA Status */
+ int received = 0;
+
+ /* Reset DSR status bits */
+ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
+ DSR_RX(port->chan), card);
+
+ if (stat & DSR_BOF)
+ /* Dropped one or more frames */
+ dev->stats.rx_over_errors++;
+
+ while (received < budget) {
+ u32 desc_off = desc_offset(port, port->rxin, 0);
+ pkt_desc __iomem *desc;
+ u32 cda = sca_inl(dmac + CDAL, card);
+
+ if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
+ break; /* No frame received */
+
+ desc = desc_address(port, port->rxin, 0);
+ stat = readb(&desc->stat);
+ if (!(stat & ST_RX_EOM))
+ port->rxpart = 1; /* partial frame received */
+ else if ((stat & ST_ERROR_MASK) || port->rxpart) {
+ dev->stats.rx_errors++;
+ if (stat & ST_RX_OVERRUN)
+ dev->stats.rx_fifo_errors++;
+ else if ((stat & (ST_RX_SHORT | ST_RX_ABORT |
+ ST_RX_RESBIT)) || port->rxpart)
+ dev->stats.rx_frame_errors++;
+ else if (stat & ST_RX_CRC)
+ dev->stats.rx_crc_errors++;
+ if (stat & ST_RX_EOM)
+ port->rxpart = 0; /* received last fragment */
+ } else {
+ sca_rx(card, port, desc, port->rxin);
+ received++;
+ }
+
+ /* Set new error descriptor address */
+ sca_outl(desc_off, dmac + EDAL, card);
+ port->rxin = (port->rxin + 1) % card->rx_ring_buffers;
+ }
+
+ /* make sure RX DMA is enabled */
+ sca_out(DSR_DE, DSR_RX(port->chan), card);
+ return received;
+}
+
+
+/* Transmit DMA service */
+static inline void sca_tx_done(port_t *port)
+{
+ struct net_device *dev = port->netdev;
+ card_t* card = port->card;
+ u8 stat;
+
+ spin_lock(&port->lock);
+
+ stat = sca_in(DSR_TX(port->chan), card); /* read DMA Status */
+
+ /* Reset DSR status bits */
+ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
+ DSR_TX(port->chan), card);
+
+ while (1) {
+ pkt_desc __iomem *desc = desc_address(port, port->txlast, 1);
+ u8 stat = readb(&desc->stat);
+
+ if (!(stat & ST_TX_OWNRSHP))
+ break; /* not yet transmitted */
+ if (stat & ST_TX_UNDRRUN) {
+ dev->stats.tx_errors++;
+ dev->stats.tx_fifo_errors++;
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += readw(&desc->len);
+ }
+ writeb(0, &desc->stat); /* Free descriptor */
+ port->txlast = (port->txlast + 1) % card->tx_ring_buffers;
+ }
+
+ netif_wake_queue(dev);
+ spin_unlock(&port->lock);
+}
+
+
+static int sca_poll(struct napi_struct *napi, int budget)
+{
+ port_t *port = container_of(napi, port_t, napi);
+ u32 isr0 = sca_inl(ISR0, port->card);
+ int received = 0;
+
+ if (isr0 & (port->chan ? 0x08000000 : 0x00080000))
+ sca_msci_intr(port);
+
+ if (isr0 & (port->chan ? 0x00002000 : 0x00000020))
+ sca_tx_done(port);
+
+ if (isr0 & (port->chan ? 0x00000200 : 0x00000002))
+ received = sca_rx_done(port, budget);
+
+ if (received < budget) {
+ netif_rx_complete(napi);
+ enable_intr(port);
+ }
+
+ return received;
+}
+
+static irqreturn_t sca_intr(int irq, void *dev_id)
+{
+ card_t *card = dev_id;
+ u32 isr0 = sca_inl(ISR0, card);
+ int i, handled = 0;
+
+ for (i = 0; i < 2; i++) {
+ port_t *port = get_port(card, i);
+ if (port && (isr0 & (i ? 0x08002200 : 0x00080022))) {
+ handled = 1;
+ disable_intr(port);
+ netif_rx_schedule(&port->napi);
+ }
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+
+static void sca_set_port(port_t *port)
+{
+ card_t* card = port->card;
+ u16 msci = get_msci(port);
+ u8 md2 = sca_in(msci + MD2, card);
+ unsigned int tmc, br = 10, brv = 1024;
+
+
+ if (port->settings.clock_rate > 0) {
+ /* Try lower br for better accuracy*/
+ do {
+ br--;
+ brv >>= 1; /* brv = 2^9 = 512 max in specs */
+
+ /* Baud Rate = CLOCK_BASE / TMC / 2^BR */
+ tmc = CLOCK_BASE / brv / port->settings.clock_rate;
+ }while (br > 1 && tmc <= 128);
+
+ if (tmc < 1) {
+ tmc = 1;
+ br = 0; /* For baud=CLOCK_BASE we use tmc=1 br=0 */
+ brv = 1;
+ } else if (tmc > 255)
+ tmc = 256; /* tmc=0 means 256 - low baud rates */
+
+ port->settings.clock_rate = CLOCK_BASE / brv / tmc;
+ } else {
+ br = 9; /* Minimum clock rate */
+ tmc = 256; /* 8bit = 0 */
+ port->settings.clock_rate = CLOCK_BASE / (256 * 512);
+ }
+
+ port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
+ port->txs = (port->txs & ~CLK_BRG_MASK) | br;
+ port->tmc = tmc;
+
+ /* baud divisor - time constant*/
+ sca_out(port->tmc, msci + TMCR, card);
+ sca_out(port->tmc, msci + TMCT, card);
+
+ /* Set BRG bits */
+ sca_out(port->rxs, msci + RXS, card);
+ sca_out(port->txs, msci + TXS, card);
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+ else
+ md2 &= ~MD2_LOOPBACK;
+
+ sca_out(md2, msci + MD2, card);
+
+}
+
+
+static void sca_open(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t* card = port->card;
+ u16 msci = get_msci(port);
+ u8 md0, md2;
+
+ switch(port->encoding) {
+ case ENCODING_NRZ: md2 = MD2_NRZ; break;
+ case ENCODING_NRZI: md2 = MD2_NRZI; break;
+ case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break;
+ case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break;
+ default: md2 = MD2_MANCHESTER;
+ }
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+
+ switch(port->parity) {
+ case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break;
+ case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break;
+ case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
+ case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
+ default: md0 = MD0_HDLC | MD0_CRC_NONE;
+ }
+
+ sca_out(CMD_RESET, msci + CMD, card);
+ sca_out(md0, msci + MD0, card);
+ sca_out(0x00, msci + MD1, card); /* no address field check */
+ sca_out(md2, msci + MD2, card);
+ sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
+ /* Skip the rest of underrun frame */
+ sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
+ sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
+ sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
+ sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
+ sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
+ sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
+
+/* We're using the following interrupts:
+ - RXINTA (DCD changes only)
+ - DMIB (EOM - single frame transfer complete)
+*/
+ sca_outl(IE0_RXINTA | IE0_CDCD, msci + IE0, card);
+
+ sca_out(port->tmc, msci + TMCR, card);
+ sca_out(port->tmc, msci + TMCT, card);
+ sca_out(port->rxs, msci + RXS, card);
+ sca_out(port->txs, msci + TXS, card);
+ sca_out(CMD_TX_ENABLE, msci + CMD, card);
+ sca_out(CMD_RX_ENABLE, msci + CMD, card);
+
+ sca_set_carrier(port);
+ enable_intr(port);
+ napi_enable(&port->napi);
+ netif_start_queue(dev);
+}
+
+
+static void sca_close(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+
+ /* reset channel */
+ sca_out(CMD_RESET, get_msci(port) + CMD, port->card);
+ disable_intr(port);
+ napi_disable(&port->napi);
+ netif_stop_queue(dev);
+}
+
+
+static int sca_attach(struct net_device *dev, unsigned short encoding,
+ unsigned short parity)
+{
+ if (encoding != ENCODING_NRZ &&
+ encoding != ENCODING_NRZI &&
+ encoding != ENCODING_FM_MARK &&
+ encoding != ENCODING_FM_SPACE &&
+ encoding != ENCODING_MANCHESTER)
+ return -EINVAL;
+
+ if (parity != PARITY_NONE &&
+ parity != PARITY_CRC16_PR0 &&
+ parity != PARITY_CRC16_PR1 &&
+ parity != PARITY_CRC32_PR1_CCITT &&
+ parity != PARITY_CRC16_PR1_CCITT)
+ return -EINVAL;
+
+ dev_to_port(dev)->encoding = encoding;
+ dev_to_port(dev)->parity = parity;
+ return 0;
+}
+
+
+#ifdef DEBUG_RINGS
+static void sca_dump_rings(struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t *card = port->card;
+ u16 cnt;
+
+ printk(KERN_DEBUG "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
+ sca_inl(get_dmac_rx(port) + CDAL, card),
+ sca_inl(get_dmac_rx(port) + EDAL, card),
+ sca_in(DSR_RX(port->chan), card), port->rxin,
+ sca_in(DSR_RX(port->chan), card) & DSR_DE ? "" : "in");
+ for (cnt = 0; cnt < port->card->rx_ring_buffers; cnt++)
+ printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
+
+ printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
+ "last=%u %sactive",
+ sca_inl(get_dmac_tx(port) + CDAL, card),
+ sca_inl(get_dmac_tx(port) + EDAL, card),
+ sca_in(DSR_TX(port->chan), card), port->txin, port->txlast,
+ sca_in(DSR_TX(port->chan), card) & DSR_DE ? "" : "in");
+
+ for (cnt = 0; cnt < port->card->tx_ring_buffers; cnt++)
+ printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
+ printk("\n");
+
+ printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x,"
+ " ST: %02x %02x %02x %02x %02x, FST: %02x CST: %02x %02x\n",
+ sca_in(get_msci(port) + MD0, card),
+ sca_in(get_msci(port) + MD1, card),
+ sca_in(get_msci(port) + MD2, card),
+ sca_in(get_msci(port) + ST0, card),
+ sca_in(get_msci(port) + ST1, card),
+ sca_in(get_msci(port) + ST2, card),
+ sca_in(get_msci(port) + ST3, card),
+ sca_in(get_msci(port) + ST4, card),
+ sca_in(get_msci(port) + FST, card),
+ sca_in(get_msci(port) + CST0, card),
+ sca_in(get_msci(port) + CST1, card));
+
+ printk(KERN_DEBUG "ILAR: %02x ISR: %08x %08x\n", sca_in(ILAR, card),
+ sca_inl(ISR0, card), sca_inl(ISR1, card));
+}
+#endif /* DEBUG_RINGS */
+
+
+static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ port_t *port = dev_to_port(dev);
+ card_t *card = port->card;
+ pkt_desc __iomem *desc;
+ u32 buff, len;
+
+ spin_lock_irq(&port->lock);
+
+ desc = desc_address(port, port->txin + 1, 1);
+ BUG_ON(readb(&desc->stat)); /* previous xmit should stop queue */
+
+#ifdef DEBUG_PKT
+ printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len);
+ debug_frame(skb);
+#endif
+
+ desc = desc_address(port, port->txin, 1);
+ buff = buffer_offset(port, port->txin, 1);
+ len = skb->len;
+ memcpy_toio(card->rambase + buff, skb->data, len);
+
+ writew(len, &desc->len);
+ writeb(ST_TX_EOM, &desc->stat);
+ dev->trans_start = jiffies;
+
+ port->txin = (port->txin + 1) % card->tx_ring_buffers;
+ sca_outl(desc_offset(port, port->txin, 1),
+ get_dmac_tx(port) + EDAL, card);
+
+ sca_out(DSR_DE, DSR_TX(port->chan), card); /* Enable TX DMA */
+
+ desc = desc_address(port, port->txin + 1, 1);
+ if (readb(&desc->stat)) /* allow 1 packet gap */
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&port->lock);
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+
+static u32 __devinit sca_detect_ram(card_t *card, u8 __iomem *rambase,
+ u32 ramsize)
+{
+ /* Round RAM size to 32 bits, fill from end to start */
+ u32 i = ramsize &= ~3;
+
+ do {
+ i -= 4;
+ writel(i ^ 0x12345678, rambase + i);
+ } while (i > 0);
+
+ for (i = 0; i < ramsize ; i += 4) {
+ if (readl(rambase + i) != (i ^ 0x12345678))
+ break;
+ }
+
+ return i;
+}
+
+
+static void __devinit sca_init(card_t *card, int wait_states)
+{
+ sca_out(wait_states, WCRL, card); /* Wait Control */
+ sca_out(wait_states, WCRM, card);
+ sca_out(wait_states, WCRH, card);
+
+ sca_out(0, DMER, card); /* DMA Master disable */
+ sca_out(0x03, PCR, card); /* DMA priority */
+ sca_out(0, DSR_RX(0), card); /* DMA disable - to halt state */
+ sca_out(0, DSR_TX(0), card);
+ sca_out(0, DSR_RX(1), card);
+ sca_out(0, DSR_TX(1), card);
+ sca_out(DMER_DME, DMER, card); /* DMA Master enable */
+}
diff --git a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c
deleted file mode 100644
index 591fb45a7c68..000000000000
--- a/drivers/net/wan/hd6457x.c
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Hitachi SCA HD64570 and HD64572 common driver for Linux
- *
- * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
- *
- * 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.
- *
- * Sources of information:
- * Hitachi HD64570 SCA User's Manual
- * Hitachi HD64572 SCA-II User's Manual
- *
- * We use the following SCA memory map:
- *
- * Packet buffer descriptor rings - starting from winbase or win0base:
- * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
- * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
- * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
- * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
- *
- * Packet data buffers - starting from winbase + buff_offset:
- * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers
- * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers
- * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used)
- * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used)
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-
-#include <linux/hdlc.h>
-
-#if (!defined (__HD64570_H) && !defined (__HD64572_H)) || \
- (defined (__HD64570_H) && defined (__HD64572_H))
-#error Either hd64570.h or hd64572.h must be included
-#endif
-
-#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET)
-#define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
-#define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
-
-#define SCA_INTR_MSCI(node) (node ? 0x10 : 0x01)
-#define SCA_INTR_DMAC_RX(node) (node ? 0x20 : 0x02)
-#define SCA_INTR_DMAC_TX(node) (node ? 0x40 : 0x04)
-
-#ifdef __HD64570_H /* HD64570 */
-#define sca_outa(value, reg, card) sca_outw(value, reg, card)
-#define sca_ina(reg, card) sca_inw(reg, card)
-#define writea(value, ptr) writew(value, ptr)
-
-#else /* HD64572 */
-#define sca_outa(value, reg, card) sca_outl(value, reg, card)
-#define sca_ina(reg, card) sca_inl(reg, card)
-#define writea(value, ptr) writel(value, ptr)
-#endif
-
-static inline struct net_device *port_to_dev(port_t *port)
-{
- return port->dev;
-}
-
-static inline int sca_intr_status(card_t *card)
-{
- u8 result = 0;
-
-#ifdef __HD64570_H /* HD64570 */
- u8 isr0 = sca_in(ISR0, card);
- u8 isr1 = sca_in(ISR1, card);
-
- if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
- if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
- if (isr1 & 0x30) result |= SCA_INTR_DMAC_RX(1);
- if (isr1 & 0xC0) result |= SCA_INTR_DMAC_TX(1);
- if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
- if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
-
-#else /* HD64572 */
- u32 isr0 = sca_inl(ISR0, card);
-
- if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0);
- if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0);
- if (isr0 & 0x00000F00) result |= SCA_INTR_DMAC_RX(1);
- if (isr0 & 0x0000F000) result |= SCA_INTR_DMAC_TX(1);
- if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0);
- if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1);
-
-#endif /* HD64570 vs HD64572 */
-
- if (!(result & SCA_INTR_DMAC_TX(0)))
- if (sca_in(DSR_TX(0), card) & DSR_EOM)
- result |= SCA_INTR_DMAC_TX(0);
- if (!(result & SCA_INTR_DMAC_TX(1)))
- if (sca_in(DSR_TX(1), card) & DSR_EOM)
- result |= SCA_INTR_DMAC_TX(1);
-
- return result;
-}
-
-static inline port_t* dev_to_port(struct net_device *dev)
-{
- return dev_to_hdlc(dev)->priv;
-}
-
-static inline u16 next_desc(port_t *port, u16 desc, int transmit)
-{
- return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
- : port_to_card(port)->rx_ring_buffers);
-}
-
-
-
-static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
-{
- u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
- u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
-
- desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
- return log_node(port) * (rx_buffs + tx_buffs) +
- transmit * rx_buffs + desc;
-}
-
-
-
-static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
-{
- /* Descriptor offset always fits in 16 bytes */
- return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
-}
-
-
-
-static inline pkt_desc __iomem *desc_address(port_t *port, u16 desc, int transmit)
-{
-#ifdef PAGE0_ALWAYS_MAPPED
- return (pkt_desc __iomem *)(win0base(port_to_card(port))
- + desc_offset(port, desc, transmit));
-#else
- return (pkt_desc __iomem *)(winbase(port_to_card(port))
- + desc_offset(port, desc, transmit));
-#endif
-}
-
-
-
-static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
-{
- return port_to_card(port)->buff_offset +
- desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
-}
-
-
-static inline void sca_set_carrier(port_t *port)
-{
- if (!(sca_in(get_msci(port) + ST3, port_to_card(port)) & ST3_DCD)) {
-#ifdef DEBUG_LINK
- printk(KERN_DEBUG "%s: sca_set_carrier on\n",
- port_to_dev(port)->name);
-#endif
- netif_carrier_on(port_to_dev(port));
- } else {
-#ifdef DEBUG_LINK
- printk(KERN_DEBUG "%s: sca_set_carrier off\n",
- port_to_dev(port)->name);
-#endif
- netif_carrier_off(port_to_dev(port));
- }
-}
-
-
-static void sca_init_sync_port(port_t *port)
-{
- card_t *card = port_to_card(port);
- int transmit, i;
-
- port->rxin = 0;
- port->txin = 0;
- port->txlast = 0;
-
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- openwin(card, 0);
-#endif
-
- for (transmit = 0; transmit < 2; transmit++) {
- u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
- u16 buffs = transmit ? card->tx_ring_buffers
- : card->rx_ring_buffers;
-
- for (i = 0; i < buffs; i++) {
- pkt_desc __iomem *desc = desc_address(port, i, transmit);
- u16 chain_off = desc_offset(port, i + 1, transmit);
- u32 buff_off = buffer_offset(port, i, transmit);
-
- writea(chain_off, &desc->cp);
- writel(buff_off, &desc->bp);
- writew(0, &desc->len);
- writeb(0, &desc->stat);
- }
-
- /* DMA disable - to halt state */
- sca_out(0, transmit ? DSR_TX(phy_node(port)) :
- DSR_RX(phy_node(port)), card);
- /* software ABORT - to initial state */
- sca_out(DCR_ABORT, transmit ? DCR_TX(phy_node(port)) :
- DCR_RX(phy_node(port)), card);
-
-#ifdef __HD64570_H
- sca_out(0, dmac + CPB, card); /* pointer base */
-#endif
- /* current desc addr */
- sca_outa(desc_offset(port, 0, transmit), dmac + CDAL, card);
- if (!transmit)
- sca_outa(desc_offset(port, buffs - 1, transmit),
- dmac + EDAL, card);
- else
- sca_outa(desc_offset(port, 0, transmit), dmac + EDAL,
- card);
-
- /* clear frame end interrupt counter */
- sca_out(DCR_CLEAR_EOF, transmit ? DCR_TX(phy_node(port)) :
- DCR_RX(phy_node(port)), card);
-
- if (!transmit) { /* Receive */
- /* set buffer length */
- sca_outw(HDLC_MAX_MRU, dmac + BFLL, card);
- /* Chain mode, Multi-frame */
- sca_out(0x14, DMR_RX(phy_node(port)), card);
- sca_out(DIR_EOME | DIR_BOFE, DIR_RX(phy_node(port)),
- card);
- /* DMA enable */
- sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
- } else { /* Transmit */
- /* Chain mode, Multi-frame */
- sca_out(0x14, DMR_TX(phy_node(port)), card);
- /* enable underflow interrupts */
- sca_out(DIR_BOFE, DIR_TX(phy_node(port)), card);
- }
- }
- sca_set_carrier(port);
-}
-
-
-
-#ifdef NEED_SCA_MSCI_INTR
-/* MSCI interrupt service */
-static inline void sca_msci_intr(port_t *port)
-{
- u16 msci = get_msci(port);
- card_t* card = port_to_card(port);
- u8 stat = sca_in(msci + ST1, card); /* read MSCI ST1 status */
-
- /* Reset MSCI TX underrun and CDCD status bit */
- sca_out(stat & (ST1_UDRN | ST1_CDCD), msci + ST1, card);
-
- if (stat & ST1_UDRN) {
- /* TX Underrun error detected */
- port_to_dev(port)->stats.tx_errors++;
- port_to_dev(port)->stats.tx_fifo_errors++;
- }
-
- if (stat & ST1_CDCD)
- sca_set_carrier(port);
-}
-#endif
-
-
-
-static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u16 rxin)
-{
- struct net_device *dev = port_to_dev(port);
- struct sk_buff *skb;
- u16 len;
- u32 buff;
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- u32 maxlen;
- u8 page;
-#endif
-
- len = readw(&desc->len);
- skb = dev_alloc_skb(len);
- if (!skb) {
- dev->stats.rx_dropped++;
- return;
- }
-
- buff = buffer_offset(port, rxin, 0);
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- page = buff / winsize(card);
- buff = buff % winsize(card);
- maxlen = winsize(card) - buff;
-
- openwin(card, page);
-
- if (len > maxlen) {
- memcpy_fromio(skb->data, winbase(card) + buff, maxlen);
- openwin(card, page + 1);
- memcpy_fromio(skb->data + maxlen, winbase(card), len - maxlen);
- } else
-#endif
- memcpy_fromio(skb->data, winbase(card) + buff, len);
-
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- /* select pkt_desc table page back */
- openwin(card, 0);
-#endif
- skb_put(skb, len);
-#ifdef DEBUG_PKT
- printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len);
- debug_frame(skb);
-#endif
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += skb->len;
- dev->last_rx = jiffies;
- skb->protocol = hdlc_type_trans(skb, dev);
- netif_rx(skb);
-}
-
-
-
-/* Receive DMA interrupt service */
-static inline void sca_rx_intr(port_t *port)
-{
- struct net_device *dev = port_to_dev(port);
- u16 dmac = get_dmac_rx(port);
- card_t *card = port_to_card(port);
- u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */
-
- /* Reset DSR status bits */
- sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
- DSR_RX(phy_node(port)), card);
-
- if (stat & DSR_BOF)
- /* Dropped one or more frames */
- dev->stats.rx_over_errors++;
-
- while (1) {
- u32 desc_off = desc_offset(port, port->rxin, 0);
- pkt_desc __iomem *desc;
- u32 cda = sca_ina(dmac + CDAL, card);
-
- if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
- break; /* No frame received */
-
- desc = desc_address(port, port->rxin, 0);
- stat = readb(&desc->stat);
- if (!(stat & ST_RX_EOM))
- port->rxpart = 1; /* partial frame received */
- else if ((stat & ST_ERROR_MASK) || port->rxpart) {
- dev->stats.rx_errors++;
- if (stat & ST_RX_OVERRUN)
- dev->stats.rx_fifo_errors++;
- else if ((stat & (ST_RX_SHORT | ST_RX_ABORT |
- ST_RX_RESBIT)) || port->rxpart)
- dev->stats.rx_frame_errors++;
- else if (stat & ST_RX_CRC)
- dev->stats.rx_crc_errors++;
- if (stat & ST_RX_EOM)
- port->rxpart = 0; /* received last fragment */
- } else
- sca_rx(card, port, desc, port->rxin);
-
- /* Set new error descriptor address */
- sca_outa(desc_off, dmac + EDAL, card);
- port->rxin = next_desc(port, port->rxin, 0);
- }
-
- /* make sure RX DMA is enabled */
- sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
-}
-
-
-
-/* Transmit DMA interrupt service */
-static inline void sca_tx_intr(port_t *port)
-{
- struct net_device *dev = port_to_dev(port);
- u16 dmac = get_dmac_tx(port);
- card_t* card = port_to_card(port);
- u8 stat;
-
- spin_lock(&port->lock);
-
- stat = sca_in(DSR_TX(phy_node(port)), card); /* read DMA Status */
-
- /* Reset DSR status bits */
- sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
- DSR_TX(phy_node(port)), card);
-
- while (1) {
- pkt_desc __iomem *desc;
-
- u32 desc_off = desc_offset(port, port->txlast, 1);
- u32 cda = sca_ina(dmac + CDAL, card);
- if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
- break; /* Transmitter is/will_be sending this frame */
-
- desc = desc_address(port, port->txlast, 1);
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += readw(&desc->len);
- writeb(0, &desc->stat); /* Free descriptor */
- port->txlast = next_desc(port, port->txlast, 1);
- }
-
- netif_wake_queue(dev);
- spin_unlock(&port->lock);
-}
-
-
-
-static irqreturn_t sca_intr(int irq, void* dev_id)
-{
- card_t *card = dev_id;
- int i;
- u8 stat;
- int handled = 0;
-
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- u8 page = sca_get_page(card);
-#endif
-
- while((stat = sca_intr_status(card)) != 0) {
- handled = 1;
- for (i = 0; i < 2; i++) {
- port_t *port = get_port(card, i);
- if (port) {
- if (stat & SCA_INTR_MSCI(i))
- sca_msci_intr(port);
-
- if (stat & SCA_INTR_DMAC_RX(i))
- sca_rx_intr(port);
-
- if (stat & SCA_INTR_DMAC_TX(i))
- sca_tx_intr(port);
- }
- }
- }
-
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- openwin(card, page); /* Restore original page */
-#endif
- return IRQ_RETVAL(handled);
-}
-
-
-
-static void sca_set_port(port_t *port)
-{
- card_t* card = port_to_card(port);
- u16 msci = get_msci(port);
- u8 md2 = sca_in(msci + MD2, card);
- unsigned int tmc, br = 10, brv = 1024;
-
-
- if (port->settings.clock_rate > 0) {
- /* Try lower br for better accuracy*/
- do {
- br--;
- brv >>= 1; /* brv = 2^9 = 512 max in specs */
-
- /* Baud Rate = CLOCK_BASE / TMC / 2^BR */
- tmc = CLOCK_BASE / brv / port->settings.clock_rate;
- }while (br > 1 && tmc <= 128);
-
- if (tmc < 1) {
- tmc = 1;
- br = 0; /* For baud=CLOCK_BASE we use tmc=1 br=0 */
- brv = 1;
- } else if (tmc > 255)
- tmc = 256; /* tmc=0 means 256 - low baud rates */
-
- port->settings.clock_rate = CLOCK_BASE / brv / tmc;
- } else {
- br = 9; /* Minimum clock rate */
- tmc = 256; /* 8bit = 0 */
- port->settings.clock_rate = CLOCK_BASE / (256 * 512);
- }
-
- port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
- port->txs = (port->txs & ~CLK_BRG_MASK) | br;
- port->tmc = tmc;
-
- /* baud divisor - time constant*/
-#ifdef __HD64570_H
- sca_out(port->tmc, msci + TMC, card);
-#else
- sca_out(port->tmc, msci + TMCR, card);
- sca_out(port->tmc, msci + TMCT, card);
-#endif
-
- /* Set BRG bits */
- sca_out(port->rxs, msci + RXS, card);
- sca_out(port->txs, msci + TXS, card);
-
- if (port->settings.loopback)
- md2 |= MD2_LOOPBACK;
- else
- md2 &= ~MD2_LOOPBACK;
-
- sca_out(md2, msci + MD2, card);
-
-}
-
-
-
-static void sca_open(struct net_device *dev)
-{
- port_t *port = dev_to_port(dev);
- card_t* card = port_to_card(port);
- u16 msci = get_msci(port);
- u8 md0, md2;
-
- switch(port->encoding) {
- case ENCODING_NRZ: md2 = MD2_NRZ; break;
- case ENCODING_NRZI: md2 = MD2_NRZI; break;
- case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break;
- case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break;
- default: md2 = MD2_MANCHESTER;
- }
-
- if (port->settings.loopback)
- md2 |= MD2_LOOPBACK;
-
- switch(port->parity) {
- case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break;
- case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break;
-#ifdef __HD64570_H
- case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
-#else
- case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
-#endif
- case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
- default: md0 = MD0_HDLC | MD0_CRC_NONE;
- }
-
- sca_out(CMD_RESET, msci + CMD, card);
- sca_out(md0, msci + MD0, card);
- sca_out(0x00, msci + MD1, card); /* no address field check */
- sca_out(md2, msci + MD2, card);
- sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
-#ifdef __HD64570_H
- sca_out(CTL_IDLE, msci + CTL, card);
-#else
- /* Skip the rest of underrun frame */
- sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
-#endif
-
-#ifdef __HD64570_H
- /* Allow at least 8 bytes before requesting RX DMA operation */
- /* TX with higher priority and possibly with shorter transfers */
- sca_out(0x07, msci + RRC, card); /* +1=RXRDY/DMA activation condition*/
- sca_out(0x10, msci + TRC0, card); /* = TXRDY/DMA activation condition*/
- sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
-#else
- sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
- sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
- sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
- sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
- sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
-#endif
-
-/* We're using the following interrupts:
- - TXINT (DMAC completed all transmisions, underrun or DCD change)
- - all DMA interrupts
-*/
-
- sca_set_carrier(port);
-
-#ifdef __HD64570_H
- /* MSCI TX INT and RX INT A IRQ enable */
- sca_out(IE0_TXINT | IE0_RXINTA, msci + IE0, card);
- sca_out(IE1_UDRN | IE1_CDCD, msci + IE1, card);
- sca_out(sca_in(IER0, card) | (phy_node(port) ? 0xC0 : 0x0C),
- IER0, card); /* TXINT and RXINT */
- /* enable DMA IRQ */
- sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
- IER1, card);
-#else
- /* MSCI TXINT and RXINTA interrupt enable */
- sca_outl(IE0_TXINT | IE0_RXINTA | IE0_UDRN | IE0_CDCD, msci + IE0,
- card);
- /* DMA & MSCI IRQ enable */
- sca_outl(sca_inl(IER0, card) |
- (phy_node(port) ? 0x0A006600 : 0x000A0066), IER0, card);
-#endif
-
-#ifdef __HD64570_H
- sca_out(port->tmc, msci + TMC, card); /* Restore registers */
-#else
- sca_out(port->tmc, msci + TMCR, card);
- sca_out(port->tmc, msci + TMCT, card);
-#endif
- sca_out(port->rxs, msci + RXS, card);
- sca_out(port->txs, msci + TXS, card);
- sca_out(CMD_TX_ENABLE, msci + CMD, card);
- sca_out(CMD_RX_ENABLE, msci + CMD, card);
-
- netif_start_queue(dev);
-}
-
-
-
-static void sca_close(struct net_device *dev)
-{
- port_t *port = dev_to_port(dev);
- card_t* card = port_to_card(port);
-
- /* reset channel */
- sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
-#ifdef __HD64570_H
- /* disable MSCI interrupts */
- sca_out(sca_in(IER0, card) & (phy_node(port) ? 0x0F : 0xF0),
- IER0, card);
- /* disable DMA interrupts */
- sca_out(sca_in(IER1, card) & (phy_node(port) ? 0x0F : 0xF0),
- IER1, card);
-#else
- /* disable DMA & MSCI IRQ */
- sca_outl(sca_inl(IER0, card) &
- (phy_node(port) ? 0x00FF00FF : 0xFF00FF00), IER0, card);
-#endif
- netif_stop_queue(dev);
-}
-
-
-
-static int sca_attach(struct net_device *dev, unsigned short encoding,
- unsigned short parity)
-{
- if (encoding != ENCODING_NRZ &&
- encoding != ENCODING_NRZI &&
- encoding != ENCODING_FM_MARK &&
- encoding != ENCODING_FM_SPACE &&
- encoding != ENCODING_MANCHESTER)
- return -EINVAL;
-
- if (parity != PARITY_NONE &&
- parity != PARITY_CRC16_PR0 &&
- parity != PARITY_CRC16_PR1 &&
-#ifdef __HD64570_H
- parity != PARITY_CRC16_PR0_CCITT &&
-#else
- parity != PARITY_CRC32_PR1_CCITT &&
-#endif
- parity != PARITY_CRC16_PR1_CCITT)
- return -EINVAL;
-
- dev_to_port(dev)->encoding = encoding;
- dev_to_port(dev)->parity = parity;
- return 0;
-}
-
-
-
-#ifdef DEBUG_RINGS
-static void sca_dump_rings(struct net_device *dev)
-{
- port_t *port = dev_to_port(dev);
- card_t *card = port_to_card(port);
- u16 cnt;
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- u8 page;
-#endif
-
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- page = sca_get_page(card);
- openwin(card, 0);
-#endif
-
- printk(KERN_DEBUG "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
- sca_ina(get_dmac_rx(port) + CDAL, card),
- sca_ina(get_dmac_rx(port) + EDAL, card),
- sca_in(DSR_RX(phy_node(port)), card), port->rxin,
- sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in");
- for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 0)->stat)));
-
- printk("\n" KERN_DEBUG "TX ring: CDA=%u EDA=%u DSR=%02X in=%u "
- "last=%u %sactive",
- sca_ina(get_dmac_tx(port) + CDAL, card),
- sca_ina(get_dmac_tx(port) + EDAL, card),
- sca_in(DSR_TX(phy_node(port)), card), port->txin, port->txlast,
- sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
-
- for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
- printk(" %02X", readb(&(desc_address(port, cnt, 1)->stat)));
- printk("\n");
-
- printk(KERN_DEBUG "MSCI: MD: %02x %02x %02x, "
- "ST: %02x %02x %02x %02x"
-#ifdef __HD64572_H
- " %02x"
-#endif
- ", FST: %02x CST: %02x %02x\n",
- sca_in(get_msci(port) + MD0, card),
- sca_in(get_msci(port) + MD1, card),
- sca_in(get_msci(port) + MD2, card),
- sca_in(get_msci(port) + ST0, card),
- sca_in(get_msci(port) + ST1, card),
- sca_in(get_msci(port) + ST2, card),
- sca_in(get_msci(port) + ST3, card),
-#ifdef __HD64572_H
- sca_in(get_msci(port) + ST4, card),
-#endif
- sca_in(get_msci(port) + FST, card),
- sca_in(get_msci(port) + CST0, card),
- sca_in(get_msci(port) + CST1, card));
-
-#ifdef __HD64572_H
- printk(KERN_DEBUG "ILAR: %02x ISR: %08x %08x\n", sca_in(ILAR, card),
- sca_inl(ISR0, card), sca_inl(ISR1, card));
-#else
- printk(KERN_DEBUG "ISR: %02x %02x %02x\n", sca_in(ISR0, card),
- sca_in(ISR1, card), sca_in(ISR2, card));
-#endif
-
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- openwin(card, page); /* Restore original page */
-#endif
-}
-#endif /* DEBUG_RINGS */
-
-
-
-static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- port_t *port = dev_to_port(dev);
- card_t *card = port_to_card(port);
- pkt_desc __iomem *desc;
- u32 buff, len;
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- u8 page;
- u32 maxlen;
-#endif
-
- spin_lock_irq(&port->lock);
-
- desc = desc_address(port, port->txin + 1, 1);
- if (readb(&desc->stat)) { /* allow 1 packet gap */
- /* should never happen - previous xmit should stop queue */
-#ifdef DEBUG_PKT
- printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
-#endif
- netif_stop_queue(dev);
- spin_unlock_irq(&port->lock);
- return 1; /* request packet to be queued */
- }
-
-#ifdef DEBUG_PKT
- printk(KERN_DEBUG "%s TX(%i):", dev->name, skb->len);
- debug_frame(skb);
-#endif
-
- desc = desc_address(port, port->txin, 1);
- buff = buffer_offset(port, port->txin, 1);
- len = skb->len;
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- page = buff / winsize(card);
- buff = buff % winsize(card);
- maxlen = winsize(card) - buff;
-
- openwin(card, page);
- if (len > maxlen) {
- memcpy_toio(winbase(card) + buff, skb->data, maxlen);
- openwin(card, page + 1);
- memcpy_toio(winbase(card), skb->data + maxlen, len - maxlen);
- }
- else
-#endif
- memcpy_toio(winbase(card) + buff, skb->data, len);
-
-#if !defined(PAGE0_ALWAYS_MAPPED) && !defined(ALL_PAGES_ALWAYS_MAPPED)
- openwin(card, 0); /* select pkt_desc table page back */
-#endif
- writew(len, &desc->len);
- writeb(ST_TX_EOM, &desc->stat);
- dev->trans_start = jiffies;
-
- port->txin = next_desc(port, port->txin, 1);
- sca_outa(desc_offset(port, port->txin, 1),
- get_dmac_tx(port) + EDAL, card);
-
- sca_out(DSR_DE, DSR_TX(phy_node(port)), card); /* Enable TX DMA */
-
- desc = desc_address(port, port->txin + 1, 1);
- if (readb(&desc->stat)) /* allow 1 packet gap */
- netif_stop_queue(dev);
-
- spin_unlock_irq(&port->lock);
-
- dev_kfree_skb(skb);
- return 0;
-}
-
-
-
-#ifdef NEED_DETECT_RAM
-static u32 __devinit sca_detect_ram(card_t *card, u8 __iomem *rambase, u32 ramsize)
-{
- /* Round RAM size to 32 bits, fill from end to start */
- u32 i = ramsize &= ~3;
-
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- u32 size = winsize(card);
-
- openwin(card, (i - 4) / size); /* select last window */
-#endif
- do {
- i -= 4;
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- if ((i + 4) % size == 0)
- openwin(card, i / size);
- writel(i ^ 0x12345678, rambase + i % size);
-#else
- writel(i ^ 0x12345678, rambase + i);
-#endif
- }while (i > 0);
-
- for (i = 0; i < ramsize ; i += 4) {
-#ifndef ALL_PAGES_ALWAYS_MAPPED
- if (i % size == 0)
- openwin(card, i / size);
-
- if (readl(rambase + i % size) != (i ^ 0x12345678))
- break;
-#else
- if (readl(rambase + i) != (i ^ 0x12345678))
- break;
-#endif
- }
-
- return i;
-}
-#endif /* NEED_DETECT_RAM */
-
-
-
-static void __devinit sca_init(card_t *card, int wait_states)
-{
- sca_out(wait_states, WCRL, card); /* Wait Control */
- sca_out(wait_states, WCRM, card);
- sca_out(wait_states, WCRH, card);
-
- sca_out(0, DMER, card); /* DMA Master disable */
- sca_out(0x03, PCR, card); /* DMA priority */
- sca_out(0, DSR_RX(0), card); /* DMA disable - to halt state */
- sca_out(0, DSR_TX(0), card);
- sca_out(0, DSR_RX(1), card);
- sca_out(0, DSR_TX(1), card);
- sca_out(DMER_DME, DMER, card); /* DMA Master enable */
-}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index d3d5055741ad..f1ddd7c3459c 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -342,7 +342,7 @@ static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
static int pvc_open(struct net_device *dev)
{
- pvc_device *pvc = dev->priv;
+ pvc_device *pvc = dev->ml_priv;
if ((pvc->frad->flags & IFF_UP) == 0)
return -EIO; /* Frad must be UP in order to activate PVC */
@@ -362,7 +362,7 @@ static int pvc_open(struct net_device *dev)
static int pvc_close(struct net_device *dev)
{
- pvc_device *pvc = dev->priv;
+ pvc_device *pvc = dev->ml_priv;
if (--pvc->open_count == 0) {
hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
@@ -381,7 +381,7 @@ static int pvc_close(struct net_device *dev)
static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- pvc_device *pvc = dev->priv;
+ pvc_device *pvc = dev->ml_priv;
fr_proto_pvc_info info;
if (ifr->ifr_settings.type == IF_GET_PROTO) {
@@ -409,7 +409,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
{
- pvc_device *pvc = dev->priv;
+ pvc_device *pvc = dev->ml_priv;
if (pvc->state.active) {
if (dev->type == ARPHRD_ETHER) {
@@ -1111,7 +1111,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
dev->change_mtu = pvc_change_mtu;
dev->mtu = HDLC_MAX_MTU;
dev->tx_queue_len = 0;
- dev->priv = pvc;
+ dev->ml_priv = pvc;
result = dev_alloc_name(dev, dev->name);
if (result < 0) {
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 4efe9e6d32d5..57fe714c1c7f 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
- * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
@@ -18,87 +18,633 @@
#include <linux/module.h>
#include <linux/pkt_sched.h>
#include <linux/poll.h>
-#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
-#include <net/syncppp.h>
+#include <linux/spinlock.h>
+
+#define DEBUG_CP 0 /* also bytes# to dump */
+#define DEBUG_STATE 0
+#define DEBUG_HARD_HEADER 0
+
+#define HDLC_ADDR_ALLSTATIONS 0xFF
+#define HDLC_CTRL_UI 0x03
+
+#define PID_LCP 0xC021
+#define PID_IP 0x0021
+#define PID_IPCP 0x8021
+#define PID_IPV6 0x0057
+#define PID_IPV6CP 0x8057
+
+enum {IDX_LCP = 0, IDX_IPCP, IDX_IPV6CP, IDX_COUNT};
+enum {CP_CONF_REQ = 1, CP_CONF_ACK, CP_CONF_NAK, CP_CONF_REJ, CP_TERM_REQ,
+ CP_TERM_ACK, CP_CODE_REJ, LCP_PROTO_REJ, LCP_ECHO_REQ, LCP_ECHO_REPLY,
+ LCP_DISC_REQ, CP_CODES};
+#if DEBUG_CP
+static const char *const code_names[CP_CODES] = {
+ "0", "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq",
+ "TermAck", "CodeRej", "ProtoRej", "EchoReq", "EchoReply", "Discard"
+};
+static char debug_buffer[64 + 3 * DEBUG_CP];
+#endif
+
+enum {LCP_OPTION_MRU = 1, LCP_OPTION_ACCM, LCP_OPTION_MAGIC = 5};
+
+struct hdlc_header {
+ u8 address;
+ u8 control;
+ __be16 protocol;
+};
+
+struct cp_header {
+ u8 code;
+ u8 id;
+ __be16 len;
+};
+
-struct ppp_state {
- struct ppp_device pppdev;
- struct ppp_device *syncppp_ptr;
- int (*old_change_mtu)(struct net_device *dev, int new_mtu);
+struct proto {
+ struct net_device *dev;
+ struct timer_list timer;
+ unsigned long timeout;
+ u16 pid; /* protocol ID */
+ u8 state;
+ u8 cr_id; /* ID of last Configuration-Request */
+ u8 restart_counter;
};
+struct ppp {
+ struct proto protos[IDX_COUNT];
+ spinlock_t lock;
+ unsigned long last_pong;
+ unsigned int req_timeout, cr_retries, term_retries;
+ unsigned int keepalive_interval, keepalive_timeout;
+ u8 seq; /* local sequence number for requests */
+ u8 echo_id; /* ID of last Echo-Request (LCP) */
+};
+
+enum {CLOSED = 0, STOPPED, STOPPING, REQ_SENT, ACK_RECV, ACK_SENT, OPENED,
+ STATES, STATE_MASK = 0xF};
+enum {START = 0, STOP, TO_GOOD, TO_BAD, RCR_GOOD, RCR_BAD, RCA, RCN, RTR, RTA,
+ RUC, RXJ_GOOD, RXJ_BAD, EVENTS};
+enum {INV = 0x10, IRC = 0x20, ZRC = 0x40, SCR = 0x80, SCA = 0x100,
+ SCN = 0x200, STR = 0x400, STA = 0x800, SCJ = 0x1000};
+
+#if DEBUG_STATE
+static const char *const state_names[STATES] = {
+ "Closed", "Stopped", "Stopping", "ReqSent", "AckRecv", "AckSent",
+ "Opened"
+};
+static const char *const event_names[EVENTS] = {
+ "Start", "Stop", "TO+", "TO-", "RCR+", "RCR-", "RCA", "RCN",
+ "RTR", "RTA", "RUC", "RXJ+", "RXJ-"
+};
+#endif
+
+static struct sk_buff_head tx_queue; /* used when holding the spin lock */
+
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
+static inline struct ppp* get_ppp(struct net_device *dev)
+{
+ return (struct ppp *)dev_to_hdlc(dev)->state;
+}
-static inline struct ppp_state* state(hdlc_device *hdlc)
+static inline struct proto* get_proto(struct net_device *dev, u16 pid)
{
- return(struct ppp_state *)(hdlc->state);
+ struct ppp *ppp = get_ppp(dev);
+
+ switch (pid) {
+ case PID_LCP:
+ return &ppp->protos[IDX_LCP];
+ case PID_IPCP:
+ return &ppp->protos[IDX_IPCP];
+ case PID_IPV6CP:
+ return &ppp->protos[IDX_IPV6CP];
+ default:
+ return NULL;
+ }
}
+static inline const char* proto_name(u16 pid)
+{
+ switch (pid) {
+ case PID_LCP:
+ return "LCP";
+ case PID_IPCP:
+ return "IPCP";
+ case PID_IPV6CP:
+ return "IPV6CP";
+ default:
+ return NULL;
+ }
+}
-static int ppp_open(struct net_device *dev)
+static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- int (*old_ioctl)(struct net_device *, struct ifreq *, int);
- int result;
+ struct hdlc_header *data = (struct hdlc_header*)skb->data;
+
+ if (skb->len < sizeof(struct hdlc_header))
+ return htons(ETH_P_HDLC);
+ if (data->address != HDLC_ADDR_ALLSTATIONS ||
+ data->control != HDLC_CTRL_UI)
+ return htons(ETH_P_HDLC);
+
+ switch (data->protocol) {
+ case __constant_htons(PID_IP):
+ skb_pull(skb, sizeof(struct hdlc_header));
+ return htons(ETH_P_IP);
- dev->ml_priv = &state(hdlc)->syncppp_ptr;
- state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
- state(hdlc)->pppdev.dev = dev;
+ case __constant_htons(PID_IPV6):
+ skb_pull(skb, sizeof(struct hdlc_header));
+ return htons(ETH_P_IPV6);
- old_ioctl = dev->do_ioctl;
- state(hdlc)->old_change_mtu = dev->change_mtu;
- sppp_attach(&state(hdlc)->pppdev);
- /* sppp_attach nukes them. We don't need syncppp's ioctl */
- dev->do_ioctl = old_ioctl;
- state(hdlc)->pppdev.sppp.pp_flags &= ~PP_CISCO;
- dev->type = ARPHRD_PPP;
- result = sppp_open(dev);
- if (result) {
- sppp_detach(dev);
- return result;
+ default:
+ return htons(ETH_P_HDLC);
}
+}
- return 0;
+
+static int ppp_hard_header(struct sk_buff *skb, struct net_device *dev,
+ u16 type, const void *daddr, const void *saddr,
+ unsigned int len)
+{
+ struct hdlc_header *data;
+#if DEBUG_HARD_HEADER
+ printk(KERN_DEBUG "%s: ppp_hard_header() called\n", dev->name);
+#endif
+
+ skb_push(skb, sizeof(struct hdlc_header));
+ data = (struct hdlc_header*)skb->data;
+
+ data->address = HDLC_ADDR_ALLSTATIONS;
+ data->control = HDLC_CTRL_UI;
+ switch (type) {
+ case ETH_P_IP:
+ data->protocol = htons(PID_IP);
+ break;
+ case ETH_P_IPV6:
+ data->protocol = htons(PID_IPV6);
+ break;
+ case PID_LCP:
+ case PID_IPCP:
+ case PID_IPV6CP:
+ data->protocol = htons(type);
+ break;
+ default: /* unknown protocol */
+ data->protocol = 0;
+ }
+ return sizeof(struct hdlc_header);
}
+static void ppp_tx_flush(void)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&tx_queue)) != NULL)
+ dev_queue_xmit(skb);
+}
-static void ppp_close(struct net_device *dev)
+static void ppp_tx_cp(struct net_device *dev, u16 pid, u8 code,
+ u8 id, unsigned int len, const void *data)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
+ struct sk_buff *skb;
+ struct cp_header *cp;
+ unsigned int magic_len = 0;
+ static u32 magic;
+
+#if DEBUG_CP
+ int i;
+ char *ptr;
+#endif
+
+ if (pid == PID_LCP && (code == LCP_ECHO_REQ || code == LCP_ECHO_REPLY))
+ magic_len = sizeof(magic);
+
+ skb = dev_alloc_skb(sizeof(struct hdlc_header) +
+ sizeof(struct cp_header) + magic_len + len);
+ if (!skb) {
+ printk(KERN_WARNING "%s: out of memory in ppp_tx_cp()\n",
+ dev->name);
+ return;
+ }
+ skb_reserve(skb, sizeof(struct hdlc_header));
+
+ cp = (struct cp_header *)skb_put(skb, sizeof(struct cp_header));
+ cp->code = code;
+ cp->id = id;
+ cp->len = htons(sizeof(struct cp_header) + magic_len + len);
+
+ if (magic_len)
+ memcpy(skb_put(skb, magic_len), &magic, magic_len);
+ if (len)
+ memcpy(skb_put(skb, len), data, len);
+
+#if DEBUG_CP
+ BUG_ON(code >= CP_CODES);
+ ptr = debug_buffer;
+ *ptr = '\x0';
+ for (i = 0; i < min_t(unsigned int, magic_len + len, DEBUG_CP); i++) {
+ sprintf(ptr, " %02X", skb->data[sizeof(struct cp_header) + i]);
+ ptr += strlen(ptr);
+ }
+ printk(KERN_DEBUG "%s: TX %s [%s id 0x%X]%s\n", dev->name,
+ proto_name(pid), code_names[code], id, debug_buffer);
+#endif
- sppp_close(dev);
- sppp_detach(dev);
+ ppp_hard_header(skb, dev, pid, NULL, NULL, 0);
- dev->change_mtu = state(hdlc)->old_change_mtu;
- dev->mtu = HDLC_MAX_MTU;
- dev->hard_header_len = 16;
+ skb->priority = TC_PRIO_CONTROL;
+ skb->dev = dev;
+ skb_reset_network_header(skb);
+ skb_queue_tail(&tx_queue, skb);
}
+/* State transition table (compare STD-51)
+ Events Actions
+ TO+ = Timeout with counter > 0 irc = Initialize-Restart-Count
+ TO- = Timeout with counter expired zrc = Zero-Restart-Count
+
+ RCR+ = Receive-Configure-Request (Good) scr = Send-Configure-Request
+ RCR- = Receive-Configure-Request (Bad)
+ RCA = Receive-Configure-Ack sca = Send-Configure-Ack
+ RCN = Receive-Configure-Nak/Rej scn = Send-Configure-Nak/Rej
+
+ RTR = Receive-Terminate-Request str = Send-Terminate-Request
+ RTA = Receive-Terminate-Ack sta = Send-Terminate-Ack
+
+ RUC = Receive-Unknown-Code scj = Send-Code-Reject
+ RXJ+ = Receive-Code-Reject (permitted)
+ or Receive-Protocol-Reject
+ RXJ- = Receive-Code-Reject (catastrophic)
+ or Receive-Protocol-Reject
+*/
+static int cp_table[EVENTS][STATES] = {
+ /* CLOSED STOPPED STOPPING REQ_SENT ACK_RECV ACK_SENT OPENED
+ 0 1 2 3 4 5 6 */
+ {IRC|SCR|3, INV , INV , INV , INV , INV , INV }, /* START */
+ { INV , 0 , 0 , 0 , 0 , 0 , 0 }, /* STOP */
+ { INV , INV ,STR|2, SCR|3 ,SCR|3, SCR|5 , INV }, /* TO+ */
+ { INV , INV , 1 , 1 , 1 , 1 , INV }, /* TO- */
+ { STA|0 ,IRC|SCR|SCA|5, 2 , SCA|5 ,SCA|6, SCA|5 ,SCR|SCA|5}, /* RCR+ */
+ { STA|0 ,IRC|SCR|SCN|3, 2 , SCN|3 ,SCN|4, SCN|3 ,SCR|SCN|3}, /* RCR- */
+ { STA|0 , STA|1 , 2 , IRC|4 ,SCR|3, 6 , SCR|3 }, /* RCA */
+ { STA|0 , STA|1 , 2 ,IRC|SCR|3,SCR|3,IRC|SCR|5, SCR|3 }, /* RCN */
+ { STA|0 , STA|1 ,STA|2, STA|3 ,STA|3, STA|3 ,ZRC|STA|2}, /* RTR */
+ { 0 , 1 , 1 , 3 , 3 , 5 , SCR|3 }, /* RTA */
+ { SCJ|0 , SCJ|1 ,SCJ|2, SCJ|3 ,SCJ|4, SCJ|5 , SCJ|6 }, /* RUC */
+ { 0 , 1 , 2 , 3 , 3 , 5 , 6 }, /* RXJ+ */
+ { 0 , 1 , 1 , 1 , 1 , 1 ,IRC|STR|2}, /* RXJ- */
+};
+
-static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
+/* SCA: RCR+ must supply id, len and data
+ SCN: RCR- must supply code, id, len and data
+ STA: RTR must supply id
+ SCJ: RUC must supply CP packet len and data */
+static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code,
+ u8 id, unsigned int len, const void *data)
{
- return __constant_htons(ETH_P_WAN_PPP);
+ int old_state, action;
+ struct ppp *ppp = get_ppp(dev);
+ struct proto *proto = get_proto(dev, pid);
+
+ old_state = proto->state;
+ BUG_ON(old_state >= STATES);
+ BUG_ON(event >= EVENTS);
+
+#if DEBUG_STATE
+ printk(KERN_DEBUG "%s: %s ppp_cp_event(%s) %s ...\n", dev->name,
+ proto_name(pid), event_names[event], state_names[proto->state]);
+#endif
+
+ action = cp_table[event][old_state];
+
+ proto->state = action & STATE_MASK;
+ if (action & (SCR | STR)) /* set Configure-Req/Terminate-Req timer */
+ mod_timer(&proto->timer, proto->timeout =
+ jiffies + ppp->req_timeout * HZ);
+ if (action & ZRC)
+ proto->restart_counter = 0;
+ if (action & IRC)
+ proto->restart_counter = (proto->state == STOPPING) ?
+ ppp->term_retries : ppp->cr_retries;
+
+ if (action & SCR) /* send Configure-Request */
+ ppp_tx_cp(dev, pid, CP_CONF_REQ, proto->cr_id = ++ppp->seq,
+ 0, NULL);
+ if (action & SCA) /* send Configure-Ack */
+ ppp_tx_cp(dev, pid, CP_CONF_ACK, id, len, data);
+ if (action & SCN) /* send Configure-Nak/Reject */
+ ppp_tx_cp(dev, pid, code, id, len, data);
+ if (action & STR) /* send Terminate-Request */
+ ppp_tx_cp(dev, pid, CP_TERM_REQ, ++ppp->seq, 0, NULL);
+ if (action & STA) /* send Terminate-Ack */
+ ppp_tx_cp(dev, pid, CP_TERM_ACK, id, 0, NULL);
+ if (action & SCJ) /* send Code-Reject */
+ ppp_tx_cp(dev, pid, CP_CODE_REJ, ++ppp->seq, len, data);
+
+ if (old_state != OPENED && proto->state == OPENED) {
+ printk(KERN_INFO "%s: %s up\n", dev->name, proto_name(pid));
+ if (pid == PID_LCP) {
+ netif_dormant_off(dev);
+ ppp_cp_event(dev, PID_IPCP, START, 0, 0, 0, NULL);
+ ppp_cp_event(dev, PID_IPV6CP, START, 0, 0, 0, NULL);
+ ppp->last_pong = jiffies;
+ mod_timer(&proto->timer, proto->timeout =
+ jiffies + ppp->keepalive_interval * HZ);
+ }
+ }
+ if (old_state == OPENED && proto->state != OPENED) {
+ printk(KERN_INFO "%s: %s down\n", dev->name, proto_name(pid));
+ if (pid == PID_LCP) {
+ netif_dormant_on(dev);
+ ppp_cp_event(dev, PID_IPCP, STOP, 0, 0, 0, NULL);
+ ppp_cp_event(dev, PID_IPV6CP, STOP, 0, 0, 0, NULL);
+ }
+ }
+ if (old_state != CLOSED && proto->state == CLOSED)
+ del_timer(&proto->timer);
+
+#if DEBUG_STATE
+ printk(KERN_DEBUG "%s: %s ppp_cp_event(%s) ... %s\n", dev->name,
+ proto_name(pid), event_names[event], state_names[proto->state]);
+#endif
+}
+
+
+static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id,
+ unsigned int req_len, const u8 *data)
+{
+ static u8 const valid_accm[6] = { LCP_OPTION_ACCM, 6, 0, 0, 0, 0 };
+ const u8 *opt;
+ u8 *out;
+ unsigned int len = req_len, nak_len = 0, rej_len = 0;
+
+ if (!(out = kmalloc(len, GFP_ATOMIC))) {
+ dev->stats.rx_dropped++;
+ return; /* out of memory, ignore CR packet */
+ }
+
+ for (opt = data; len; len -= opt[1], opt += opt[1]) {
+ if (len < 2 || len < opt[1]) {
+ dev->stats.rx_errors++;
+ return; /* bad packet, drop silently */
+ }
+
+ if (pid == PID_LCP)
+ switch (opt[0]) {
+ case LCP_OPTION_MRU:
+ continue; /* MRU always OK and > 1500 bytes? */
+
+ case LCP_OPTION_ACCM: /* async control character map */
+ if (!memcmp(opt, valid_accm,
+ sizeof(valid_accm)))
+ continue;
+ if (!rej_len) { /* NAK it */
+ memcpy(out + nak_len, valid_accm,
+ sizeof(valid_accm));
+ nak_len += sizeof(valid_accm);
+ continue;
+ }
+ break;
+ case LCP_OPTION_MAGIC:
+ if (opt[1] != 6 || (!opt[2] && !opt[3] &&
+ !opt[4] && !opt[5]))
+ break; /* reject invalid magic number */
+ continue;
+ }
+ /* reject this option */
+ memcpy(out + rej_len, opt, opt[1]);
+ rej_len += opt[1];
+ }
+
+ if (rej_len)
+ ppp_cp_event(dev, pid, RCR_BAD, CP_CONF_REJ, id, rej_len, out);
+ else if (nak_len)
+ ppp_cp_event(dev, pid, RCR_BAD, CP_CONF_NAK, id, nak_len, out);
+ else
+ ppp_cp_event(dev, pid, RCR_GOOD, CP_CONF_ACK, id, req_len, data);
+
+ kfree(out);
+}
+
+static int ppp_rx(struct sk_buff *skb)
+{
+ struct hdlc_header *hdr = (struct hdlc_header*)skb->data;
+ struct net_device *dev = skb->dev;
+ struct ppp *ppp = get_ppp(dev);
+ struct proto *proto;
+ struct cp_header *cp;
+ unsigned long flags;
+ unsigned int len;
+ u16 pid;
+#if DEBUG_CP
+ int i;
+ char *ptr;
+#endif
+
+ spin_lock_irqsave(&ppp->lock, flags);
+ /* Check HDLC header */
+ if (skb->len < sizeof(struct hdlc_header))
+ goto rx_error;
+ cp = (struct cp_header*)skb_pull(skb, sizeof(struct hdlc_header));
+ if (hdr->address != HDLC_ADDR_ALLSTATIONS ||
+ hdr->control != HDLC_CTRL_UI)
+ goto rx_error;
+
+ pid = ntohs(hdr->protocol);
+ proto = get_proto(dev, pid);
+ if (!proto) {
+ if (ppp->protos[IDX_LCP].state == OPENED)
+ ppp_tx_cp(dev, PID_LCP, LCP_PROTO_REJ,
+ ++ppp->seq, skb->len + 2, &hdr->protocol);
+ goto rx_error;
+ }
+
+ len = ntohs(cp->len);
+ if (len < sizeof(struct cp_header) /* no complete CP header? */ ||
+ skb->len < len /* truncated packet? */)
+ goto rx_error;
+ skb_pull(skb, sizeof(struct cp_header));
+ len -= sizeof(struct cp_header);
+
+ /* HDLC and CP headers stripped from skb */
+#if DEBUG_CP
+ if (cp->code < CP_CODES)
+ sprintf(debug_buffer, "[%s id 0x%X]", code_names[cp->code],
+ cp->id);
+ else
+ sprintf(debug_buffer, "[code %u id 0x%X]", cp->code, cp->id);
+ ptr = debug_buffer + strlen(debug_buffer);
+ for (i = 0; i < min_t(unsigned int, len, DEBUG_CP); i++) {
+ sprintf(ptr, " %02X", skb->data[i]);
+ ptr += strlen(ptr);
+ }
+ printk(KERN_DEBUG "%s: RX %s %s\n", dev->name, proto_name(pid),
+ debug_buffer);
+#endif
+
+ /* LCP only */
+ if (pid == PID_LCP)
+ switch (cp->code) {
+ case LCP_PROTO_REJ:
+ pid = ntohs(*(__be16*)skb->data);
+ if (pid == PID_LCP || pid == PID_IPCP ||
+ pid == PID_IPV6CP)
+ ppp_cp_event(dev, pid, RXJ_BAD, 0, 0,
+ 0, NULL);
+ goto out;
+
+ case LCP_ECHO_REQ: /* send Echo-Reply */
+ if (len >= 4 && proto->state == OPENED)
+ ppp_tx_cp(dev, PID_LCP, LCP_ECHO_REPLY,
+ cp->id, len - 4, skb->data + 4);
+ goto out;
+
+ case LCP_ECHO_REPLY:
+ if (cp->id == ppp->echo_id)
+ ppp->last_pong = jiffies;
+ goto out;
+
+ case LCP_DISC_REQ: /* discard */
+ goto out;
+ }
+
+ /* LCP, IPCP and IPV6CP */
+ switch (cp->code) {
+ case CP_CONF_REQ:
+ ppp_cp_parse_cr(dev, pid, cp->id, len, skb->data);
+ goto out;
+
+ case CP_CONF_ACK:
+ if (cp->id == proto->cr_id)
+ ppp_cp_event(dev, pid, RCA, 0, 0, 0, NULL);
+ goto out;
+
+ case CP_CONF_REJ:
+ case CP_CONF_NAK:
+ if (cp->id == proto->cr_id)
+ ppp_cp_event(dev, pid, RCN, 0, 0, 0, NULL);
+ goto out;
+
+ case CP_TERM_REQ:
+ ppp_cp_event(dev, pid, RTR, 0, cp->id, 0, NULL);
+ goto out;
+
+ case CP_TERM_ACK:
+ ppp_cp_event(dev, pid, RTA, 0, 0, 0, NULL);
+ goto out;
+
+ case CP_CODE_REJ:
+ ppp_cp_event(dev, pid, RXJ_BAD, 0, 0, 0, NULL);
+ goto out;
+
+ default:
+ len += sizeof(struct cp_header);
+ if (len > dev->mtu)
+ len = dev->mtu;
+ ppp_cp_event(dev, pid, RUC, 0, 0, len, cp);
+ goto out;
+ }
+ goto out;
+
+rx_error:
+ dev->stats.rx_errors++;
+out:
+ spin_unlock_irqrestore(&ppp->lock, flags);
+ dev_kfree_skb_any(skb);
+ ppp_tx_flush();
+ return NET_RX_DROP;
}
+static void ppp_timer(unsigned long arg)
+{
+ struct proto *proto = (struct proto *)arg;
+ struct ppp *ppp = get_ppp(proto->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ppp->lock, flags);
+ switch (proto->state) {
+ case STOPPING:
+ case REQ_SENT:
+ case ACK_RECV:
+ case ACK_SENT:
+ if (proto->restart_counter) {
+ ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0,
+ 0, NULL);
+ proto->restart_counter--;
+ } else
+ ppp_cp_event(proto->dev, proto->pid, TO_BAD, 0, 0,
+ 0, NULL);
+ break;
+
+ case OPENED:
+ if (proto->pid != PID_LCP)
+ break;
+ if (time_after(jiffies, ppp->last_pong +
+ ppp->keepalive_timeout * HZ)) {
+ printk(KERN_INFO "%s: Link down\n", proto->dev->name);
+ ppp_cp_event(proto->dev, PID_LCP, STOP, 0, 0, 0, NULL);
+ ppp_cp_event(proto->dev, PID_LCP, START, 0, 0, 0, NULL);
+ } else { /* send keep-alive packet */
+ ppp->echo_id = ++ppp->seq;
+ ppp_tx_cp(proto->dev, PID_LCP, LCP_ECHO_REQ,
+ ppp->echo_id, 0, NULL);
+ proto->timer.expires = jiffies +
+ ppp->keepalive_interval * HZ;
+ add_timer(&proto->timer);
+ }
+ break;
+ }
+ spin_unlock_irqrestore(&ppp->lock, flags);
+ ppp_tx_flush();
+}
+
+
+static void ppp_start(struct net_device *dev)
+{
+ struct ppp *ppp = get_ppp(dev);
+ int i;
+
+ for (i = 0; i < IDX_COUNT; i++) {
+ struct proto *proto = &ppp->protos[i];
+ proto->dev = dev;
+ init_timer(&proto->timer);
+ proto->timer.function = ppp_timer;
+ proto->timer.data = (unsigned long)proto;
+ proto->state = CLOSED;
+ }
+ ppp->protos[IDX_LCP].pid = PID_LCP;
+ ppp->protos[IDX_IPCP].pid = PID_IPCP;
+ ppp->protos[IDX_IPV6CP].pid = PID_IPV6CP;
+
+ ppp_cp_event(dev, PID_LCP, START, 0, 0, 0, NULL);
+}
+
+static void ppp_stop(struct net_device *dev)
+{
+ ppp_cp_event(dev, PID_LCP, STOP, 0, 0, 0, NULL);
+}
static struct hdlc_proto proto = {
- .open = ppp_open,
- .close = ppp_close,
+ .start = ppp_start,
+ .stop = ppp_stop,
.type_trans = ppp_type_trans,
.ioctl = ppp_ioctl,
+ .netif_rx = ppp_rx,
.module = THIS_MODULE,
};
+static const struct header_ops ppp_header_ops = {
+ .create = ppp_hard_header,
+};
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
+ struct ppp *ppp;
int result;
switch (ifr->ifr_settings.type) {
@@ -109,25 +655,35 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
return 0; /* return protocol only, no settable parameters */
case IF_PROTO_PPP:
- if(!capable(CAP_NET_ADMIN))
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if(dev->flags & IFF_UP)
+ if (dev->flags & IFF_UP)
return -EBUSY;
/* no settable parameters */
- result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+ result = hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
if (result)
return result;
- result = attach_hdlc_protocol(dev, &proto,
- sizeof(struct ppp_state));
+ result = attach_hdlc_protocol(dev, &proto, sizeof(struct ppp));
if (result)
return result;
+
+ ppp = get_ppp(dev);
+ spin_lock_init(&ppp->lock);
+ ppp->req_timeout = 2;
+ ppp->cr_retries = 10;
+ ppp->term_retries = 2;
+ ppp->keepalive_interval = 10;
+ ppp->keepalive_timeout = 60;
+
dev->hard_start_xmit = hdlc->xmit;
+ dev->hard_header_len = sizeof(struct hdlc_header);
+ dev->header_ops = &ppp_header_ops;
dev->type = ARPHRD_PPP;
- netif_dormant_off(dev);
+ netif_dormant_on(dev);
return 0;
}
@@ -137,12 +693,11 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
static int __init mod_init(void)
{
+ skb_queue_head_init(&tx_queue);
register_hdlc_protocol(&proto);
return 0;
}
-
-
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index e299313f828a..af54f0cf1b35 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -66,7 +66,6 @@ static void hostess_input(struct z8530_channel *c, struct sk_buff *skb)
* it right now.
*/
netif_rx(skb);
- c->netdevice->last_rx = jiffies;
}
/*
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
new file mode 100644
index 000000000000..2dc241689d37
--- /dev/null
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -0,0 +1,1325 @@
+/*
+ * Intel IXP4xx HSS (synchronous serial port) driver for Linux
+ *
+ * Copyright (C) 2007-2008 Krzysztof Hałasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/cdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/fs.h>
+#include <linux/hdlc.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <mach/npe.h>
+#include <mach/qmgr.h>
+
+#define DEBUG_DESC 0
+#define DEBUG_RX 0
+#define DEBUG_TX 0
+#define DEBUG_PKT_BYTES 0
+#define DEBUG_CLOSE 0
+
+#define DRV_NAME "ixp4xx_hss"
+
+#define PKT_EXTRA_FLAGS 0 /* orig 1 */
+#define PKT_NUM_PIPES 1 /* 1, 2 or 4 */
+#define PKT_PIPE_FIFO_SIZEW 4 /* total 4 dwords per HSS */
+
+#define RX_DESCS 16 /* also length of all RX queues */
+#define TX_DESCS 16 /* also length of all TX queues */
+
+#define POOL_ALLOC_SIZE (sizeof(struct desc) * (RX_DESCS + TX_DESCS))
+#define RX_SIZE (HDLC_MAX_MRU + 4) /* NPE needs more space */
+#define MAX_CLOSE_WAIT 1000 /* microseconds */
+#define HSS_COUNT 2
+#define FRAME_SIZE 256 /* doesn't matter at this point */
+#define FRAME_OFFSET 0
+#define MAX_CHANNELS (FRAME_SIZE / 8)
+
+#define NAPI_WEIGHT 16
+
+/* Queue IDs */
+#define HSS0_CHL_RXTRIG_QUEUE 12 /* orig size = 32 dwords */
+#define HSS0_PKT_RX_QUEUE 13 /* orig size = 32 dwords */
+#define HSS0_PKT_TX0_QUEUE 14 /* orig size = 16 dwords */
+#define HSS0_PKT_TX1_QUEUE 15
+#define HSS0_PKT_TX2_QUEUE 16
+#define HSS0_PKT_TX3_QUEUE 17
+#define HSS0_PKT_RXFREE0_QUEUE 18 /* orig size = 16 dwords */
+#define HSS0_PKT_RXFREE1_QUEUE 19
+#define HSS0_PKT_RXFREE2_QUEUE 20
+#define HSS0_PKT_RXFREE3_QUEUE 21
+#define HSS0_PKT_TXDONE_QUEUE 22 /* orig size = 64 dwords */
+
+#define HSS1_CHL_RXTRIG_QUEUE 10
+#define HSS1_PKT_RX_QUEUE 0
+#define HSS1_PKT_TX0_QUEUE 5
+#define HSS1_PKT_TX1_QUEUE 6
+#define HSS1_PKT_TX2_QUEUE 7
+#define HSS1_PKT_TX3_QUEUE 8
+#define HSS1_PKT_RXFREE0_QUEUE 1
+#define HSS1_PKT_RXFREE1_QUEUE 2
+#define HSS1_PKT_RXFREE2_QUEUE 3
+#define HSS1_PKT_RXFREE3_QUEUE 4
+#define HSS1_PKT_TXDONE_QUEUE 9
+
+#define NPE_PKT_MODE_HDLC 0
+#define NPE_PKT_MODE_RAW 1
+#define NPE_PKT_MODE_56KMODE 2
+#define NPE_PKT_MODE_56KENDIAN_MSB 4
+
+/* PKT_PIPE_HDLC_CFG_WRITE flags */
+#define PKT_HDLC_IDLE_ONES 0x1 /* default = flags */
+#define PKT_HDLC_CRC_32 0x2 /* default = CRC-16 */
+#define PKT_HDLC_MSB_ENDIAN 0x4 /* default = LE */
+
+
+/* hss_config, PCRs */
+/* Frame sync sampling, default = active low */
+#define PCR_FRM_SYNC_ACTIVE_HIGH 0x40000000
+#define PCR_FRM_SYNC_FALLINGEDGE 0x80000000
+#define PCR_FRM_SYNC_RISINGEDGE 0xC0000000
+
+/* Frame sync pin: input (default) or output generated off a given clk edge */
+#define PCR_FRM_SYNC_OUTPUT_FALLING 0x20000000
+#define PCR_FRM_SYNC_OUTPUT_RISING 0x30000000
+
+/* Frame and data clock sampling on edge, default = falling */
+#define PCR_FCLK_EDGE_RISING 0x08000000
+#define PCR_DCLK_EDGE_RISING 0x04000000
+
+/* Clock direction, default = input */
+#define PCR_SYNC_CLK_DIR_OUTPUT 0x02000000
+
+/* Generate/Receive frame pulses, default = enabled */
+#define PCR_FRM_PULSE_DISABLED 0x01000000
+
+ /* Data rate is full (default) or half the configured clk speed */
+#define PCR_HALF_CLK_RATE 0x00200000
+
+/* Invert data between NPE and HSS FIFOs? (default = no) */
+#define PCR_DATA_POLARITY_INVERT 0x00100000
+
+/* TX/RX endianness, default = LSB */
+#define PCR_MSB_ENDIAN 0x00080000
+
+/* Normal (default) / open drain mode (TX only) */
+#define PCR_TX_PINS_OPEN_DRAIN 0x00040000
+
+/* No framing bit transmitted and expected on RX? (default = framing bit) */
+#define PCR_SOF_NO_FBIT 0x00020000
+
+/* Drive data pins? */
+#define PCR_TX_DATA_ENABLE 0x00010000
+
+/* Voice 56k type: drive the data pins low (default), high, high Z */
+#define PCR_TX_V56K_HIGH 0x00002000
+#define PCR_TX_V56K_HIGH_IMP 0x00004000
+
+/* Unassigned type: drive the data pins low (default), high, high Z */
+#define PCR_TX_UNASS_HIGH 0x00000800
+#define PCR_TX_UNASS_HIGH_IMP 0x00001000
+
+/* T1 @ 1.544MHz only: Fbit dictated in FIFO (default) or high Z */
+#define PCR_TX_FB_HIGH_IMP 0x00000400
+
+/* 56k data endiannes - which bit unused: high (default) or low */
+#define PCR_TX_56KE_BIT_0_UNUSED 0x00000200
+
+/* 56k data transmission type: 32/8 bit data (default) or 56K data */
+#define PCR_TX_56KS_56K_DATA 0x00000100
+
+/* hss_config, cCR */
+/* Number of packetized clients, default = 1 */
+#define CCR_NPE_HFIFO_2_HDLC 0x04000000
+#define CCR_NPE_HFIFO_3_OR_4HDLC 0x08000000
+
+/* default = no loopback */
+#define CCR_LOOPBACK 0x02000000
+
+/* HSS number, default = 0 (first) */
+#define CCR_SECOND_HSS 0x01000000
+
+
+/* hss_config, clkCR: main:10, num:10, denom:12 */
+#define CLK42X_SPEED_EXP ((0x3FF << 22) | ( 2 << 12) | 15) /*65 KHz*/
+
+#define CLK42X_SPEED_512KHZ (( 130 << 22) | ( 2 << 12) | 15)
+#define CLK42X_SPEED_1536KHZ (( 43 << 22) | ( 18 << 12) | 47)
+#define CLK42X_SPEED_1544KHZ (( 43 << 22) | ( 33 << 12) | 192)
+#define CLK42X_SPEED_2048KHZ (( 32 << 22) | ( 34 << 12) | 63)
+#define CLK42X_SPEED_4096KHZ (( 16 << 22) | ( 34 << 12) | 127)
+#define CLK42X_SPEED_8192KHZ (( 8 << 22) | ( 34 << 12) | 255)
+
+#define CLK46X_SPEED_512KHZ (( 130 << 22) | ( 24 << 12) | 127)
+#define CLK46X_SPEED_1536KHZ (( 43 << 22) | (152 << 12) | 383)
+#define CLK46X_SPEED_1544KHZ (( 43 << 22) | ( 66 << 12) | 385)
+#define CLK46X_SPEED_2048KHZ (( 32 << 22) | (280 << 12) | 511)
+#define CLK46X_SPEED_4096KHZ (( 16 << 22) | (280 << 12) | 1023)
+#define CLK46X_SPEED_8192KHZ (( 8 << 22) | (280 << 12) | 2047)
+
+
+/* hss_config, LUT entries */
+#define TDMMAP_UNASSIGNED 0
+#define TDMMAP_HDLC 1 /* HDLC - packetized */
+#define TDMMAP_VOICE56K 2 /* Voice56K - 7-bit channelized */
+#define TDMMAP_VOICE64K 3 /* Voice64K - 8-bit channelized */
+
+/* offsets into HSS config */
+#define HSS_CONFIG_TX_PCR 0x00 /* port configuration registers */
+#define HSS_CONFIG_RX_PCR 0x04
+#define HSS_CONFIG_CORE_CR 0x08 /* loopback control, HSS# */
+#define HSS_CONFIG_CLOCK_CR 0x0C /* clock generator control */
+#define HSS_CONFIG_TX_FCR 0x10 /* frame configuration registers */
+#define HSS_CONFIG_RX_FCR 0x14
+#define HSS_CONFIG_TX_LUT 0x18 /* channel look-up tables */
+#define HSS_CONFIG_RX_LUT 0x38
+
+
+/* NPE command codes */
+/* writes the ConfigWord value to the location specified by offset */
+#define PORT_CONFIG_WRITE 0x40
+
+/* triggers the NPE to load the contents of the configuration table */
+#define PORT_CONFIG_LOAD 0x41
+
+/* triggers the NPE to return an HssErrorReadResponse message */
+#define PORT_ERROR_READ 0x42
+
+/* triggers the NPE to reset internal status and enable the HssPacketized
+ operation for the flow specified by pPipe */
+#define PKT_PIPE_FLOW_ENABLE 0x50
+#define PKT_PIPE_FLOW_DISABLE 0x51
+#define PKT_NUM_PIPES_WRITE 0x52
+#define PKT_PIPE_FIFO_SIZEW_WRITE 0x53
+#define PKT_PIPE_HDLC_CFG_WRITE 0x54
+#define PKT_PIPE_IDLE_PATTERN_WRITE 0x55
+#define PKT_PIPE_RX_SIZE_WRITE 0x56
+#define PKT_PIPE_MODE_WRITE 0x57
+
+/* HDLC packet status values - desc->status */
+#define ERR_SHUTDOWN 1 /* stop or shutdown occurrance */
+#define ERR_HDLC_ALIGN 2 /* HDLC alignment error */
+#define ERR_HDLC_FCS 3 /* HDLC Frame Check Sum error */
+#define ERR_RXFREE_Q_EMPTY 4 /* RX-free queue became empty while receiving
+ this packet (if buf_len < pkt_len) */
+#define ERR_HDLC_TOO_LONG 5 /* HDLC frame size too long */
+#define ERR_HDLC_ABORT 6 /* abort sequence received */
+#define ERR_DISCONNECTING 7 /* disconnect is in progress */
+
+
+#ifdef __ARMEB__
+typedef struct sk_buff buffer_t;
+#define free_buffer dev_kfree_skb
+#define free_buffer_irq dev_kfree_skb_irq
+#else
+typedef void buffer_t;
+#define free_buffer kfree
+#define free_buffer_irq kfree
+#endif
+
+struct port {
+ struct device *dev;
+ struct npe *npe;
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct hss_plat_info *plat;
+ buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS];
+ struct desc *desc_tab; /* coherent */
+ u32 desc_tab_phys;
+ unsigned int id;
+ unsigned int clock_type, clock_rate, loopback;
+ unsigned int initialized, carrier;
+ u8 hdlc_cfg;
+};
+
+/* NPE message structure */
+struct msg {
+#ifdef __ARMEB__
+ u8 cmd, unused, hss_port, index;
+ union {
+ struct { u8 data8a, data8b, data8c, data8d; };
+ struct { u16 data16a, data16b; };
+ struct { u32 data32; };
+ };
+#else
+ u8 index, hss_port, unused, cmd;
+ union {
+ struct { u8 data8d, data8c, data8b, data8a; };
+ struct { u16 data16b, data16a; };
+ struct { u32 data32; };
+ };
+#endif
+};
+
+/* HDLC packet descriptor */
+struct desc {
+ u32 next; /* pointer to next buffer, unused */
+
+#ifdef __ARMEB__
+ u16 buf_len; /* buffer length */
+ u16 pkt_len; /* packet length */
+ u32 data; /* pointer to data buffer in RAM */
+ u8 status;
+ u8 error_count;
+ u16 __reserved;
+#else
+ u16 pkt_len; /* packet length */
+ u16 buf_len; /* buffer length */
+ u32 data; /* pointer to data buffer in RAM */
+ u16 __reserved;
+ u8 error_count;
+ u8 status;
+#endif
+ u32 __reserved1[4];
+};
+
+
+#define rx_desc_phys(port, n) ((port)->desc_tab_phys + \
+ (n) * sizeof(struct desc))
+#define rx_desc_ptr(port, n) (&(port)->desc_tab[n])
+
+#define tx_desc_phys(port, n) ((port)->desc_tab_phys + \
+ ((n) + RX_DESCS) * sizeof(struct desc))
+#define tx_desc_ptr(port, n) (&(port)->desc_tab[(n) + RX_DESCS])
+
+/*****************************************************************************
+ * global variables
+ ****************************************************************************/
+
+static int ports_open;
+static struct dma_pool *dma_pool;
+static spinlock_t npe_lock;
+
+static const struct {
+ int tx, txdone, rx, rxfree;
+}queue_ids[2] = {{HSS0_PKT_TX0_QUEUE, HSS0_PKT_TXDONE_QUEUE, HSS0_PKT_RX_QUEUE,
+ HSS0_PKT_RXFREE0_QUEUE},
+ {HSS1_PKT_TX0_QUEUE, HSS1_PKT_TXDONE_QUEUE, HSS1_PKT_RX_QUEUE,
+ HSS1_PKT_RXFREE0_QUEUE},
+};
+
+/*****************************************************************************
+ * utility functions
+ ****************************************************************************/
+
+static inline struct port* dev_to_port(struct net_device *dev)
+{
+ return dev_to_hdlc(dev)->priv;
+}
+
+#ifndef __ARMEB__
+static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
+{
+ int i;
+ for (i = 0; i < cnt; i++)
+ dest[i] = swab32(src[i]);
+}
+#endif
+
+/*****************************************************************************
+ * HSS access
+ ****************************************************************************/
+
+static void hss_npe_send(struct port *port, struct msg *msg, const char* what)
+{
+ u32 *val = (u32*)msg;
+ if (npe_send_message(port->npe, msg, what)) {
+ printk(KERN_CRIT "HSS-%i: unable to send command [%08X:%08X]"
+ " to %s\n", port->id, val[0], val[1],
+ npe_name(port->npe));
+ BUG();
+ }
+}
+
+static void hss_config_set_lut(struct port *port)
+{
+ struct msg msg;
+ int ch;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+
+ for (ch = 0; ch < MAX_CHANNELS; ch++) {
+ msg.data32 >>= 2;
+ msg.data32 |= TDMMAP_HDLC << 30;
+
+ if (ch % 16 == 15) {
+ msg.index = HSS_CONFIG_TX_LUT + ((ch / 4) & ~3);
+ hss_npe_send(port, &msg, "HSS_SET_TX_LUT");
+
+ msg.index += HSS_CONFIG_RX_LUT - HSS_CONFIG_TX_LUT;
+ hss_npe_send(port, &msg, "HSS_SET_RX_LUT");
+ }
+ }
+}
+
+static void hss_config(struct port *port)
+{
+ struct msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+ msg.index = HSS_CONFIG_TX_PCR;
+ msg.data32 = PCR_FRM_SYNC_OUTPUT_RISING | PCR_MSB_ENDIAN |
+ PCR_TX_DATA_ENABLE | PCR_SOF_NO_FBIT;
+ if (port->clock_type == CLOCK_INT)
+ msg.data32 |= PCR_SYNC_CLK_DIR_OUTPUT;
+ hss_npe_send(port, &msg, "HSS_SET_TX_PCR");
+
+ msg.index = HSS_CONFIG_RX_PCR;
+ msg.data32 ^= PCR_TX_DATA_ENABLE | PCR_DCLK_EDGE_RISING;
+ hss_npe_send(port, &msg, "HSS_SET_RX_PCR");
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+ msg.index = HSS_CONFIG_CORE_CR;
+ msg.data32 = (port->loopback ? CCR_LOOPBACK : 0) |
+ (port->id ? CCR_SECOND_HSS : 0);
+ hss_npe_send(port, &msg, "HSS_SET_CORE_CR");
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+ msg.index = HSS_CONFIG_CLOCK_CR;
+ msg.data32 = CLK42X_SPEED_2048KHZ /* FIXME */;
+ hss_npe_send(port, &msg, "HSS_SET_CLOCK_CR");
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+ msg.index = HSS_CONFIG_TX_FCR;
+ msg.data16a = FRAME_OFFSET;
+ msg.data16b = FRAME_SIZE - 1;
+ hss_npe_send(port, &msg, "HSS_SET_TX_FCR");
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_WRITE;
+ msg.hss_port = port->id;
+ msg.index = HSS_CONFIG_RX_FCR;
+ msg.data16a = FRAME_OFFSET;
+ msg.data16b = FRAME_SIZE - 1;
+ hss_npe_send(port, &msg, "HSS_SET_RX_FCR");
+
+ hss_config_set_lut(port);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_CONFIG_LOAD;
+ msg.hss_port = port->id;
+ hss_npe_send(port, &msg, "HSS_LOAD_CONFIG");
+
+ if (npe_recv_message(port->npe, &msg, "HSS_LOAD_CONFIG") ||
+ /* HSS_LOAD_CONFIG for port #1 returns port_id = #4 */
+ msg.cmd != PORT_CONFIG_LOAD || msg.data32) {
+ printk(KERN_CRIT "HSS-%i: HSS_LOAD_CONFIG failed\n",
+ port->id);
+ BUG();
+ }
+
+ /* HDLC may stop working without this - check FIXME */
+ npe_recv_message(port->npe, &msg, "FLUSH_IT");
+}
+
+static void hss_set_hdlc_cfg(struct port *port)
+{
+ struct msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PKT_PIPE_HDLC_CFG_WRITE;
+ msg.hss_port = port->id;
+ msg.data8a = port->hdlc_cfg; /* rx_cfg */
+ msg.data8b = port->hdlc_cfg | (PKT_EXTRA_FLAGS << 3); /* tx_cfg */
+ hss_npe_send(port, &msg, "HSS_SET_HDLC_CFG");
+}
+
+static u32 hss_get_status(struct port *port)
+{
+ struct msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PORT_ERROR_READ;
+ msg.hss_port = port->id;
+ hss_npe_send(port, &msg, "PORT_ERROR_READ");
+ if (npe_recv_message(port->npe, &msg, "PORT_ERROR_READ")) {
+ printk(KERN_CRIT "HSS-%i: unable to read HSS status\n",
+ port->id);
+ BUG();
+ }
+
+ return msg.data32;
+}
+
+static void hss_start_hdlc(struct port *port)
+{
+ struct msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PKT_PIPE_FLOW_ENABLE;
+ msg.hss_port = port->id;
+ msg.data32 = 0;
+ hss_npe_send(port, &msg, "HSS_ENABLE_PKT_PIPE");
+}
+
+static void hss_stop_hdlc(struct port *port)
+{
+ struct msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PKT_PIPE_FLOW_DISABLE;
+ msg.hss_port = port->id;
+ hss_npe_send(port, &msg, "HSS_DISABLE_PKT_PIPE");
+ hss_get_status(port); /* make sure it's halted */
+}
+
+static int hss_load_firmware(struct port *port)
+{
+ struct msg msg;
+ int err;
+
+ if (port->initialized)
+ return 0;
+
+ if (!npe_running(port->npe) &&
+ (err = npe_load_firmware(port->npe, npe_name(port->npe),
+ port->dev)))
+ return err;
+
+ /* HDLC mode configuration */
+ memset(&msg, 0, sizeof(msg));
+ msg.cmd = PKT_NUM_PIPES_WRITE;
+ msg.hss_port = port->id;
+ msg.data8a = PKT_NUM_PIPES;
+ hss_npe_send(port, &msg, "HSS_SET_PKT_PIPES");
+
+ msg.cmd = PKT_PIPE_FIFO_SIZEW_WRITE;
+ msg.data8a = PKT_PIPE_FIFO_SIZEW;
+ hss_npe_send(port, &msg, "HSS_SET_PKT_FIFO");
+
+ msg.cmd = PKT_PIPE_MODE_WRITE;
+ msg.data8a = NPE_PKT_MODE_HDLC;
+ /* msg.data8b = inv_mask */
+ /* msg.data8c = or_mask */
+ hss_npe_send(port, &msg, "HSS_SET_PKT_MODE");
+
+ msg.cmd = PKT_PIPE_RX_SIZE_WRITE;
+ msg.data16a = HDLC_MAX_MRU; /* including CRC */
+ hss_npe_send(port, &msg, "HSS_SET_PKT_RX_SIZE");
+
+ msg.cmd = PKT_PIPE_IDLE_PATTERN_WRITE;
+ msg.data32 = 0x7F7F7F7F; /* ??? FIXME */
+ hss_npe_send(port, &msg, "HSS_SET_PKT_IDLE");
+
+ port->initialized = 1;
+ return 0;
+}
+
+/*****************************************************************************
+ * packetized (HDLC) operation
+ ****************************************************************************/
+
+static inline void debug_pkt(struct net_device *dev, const char *func,
+ u8 *data, int len)
+{
+#if DEBUG_PKT_BYTES
+ int i;
+
+ printk(KERN_DEBUG "%s: %s(%i)", dev->name, func, len);
+ for (i = 0; i < len; i++) {
+ if (i >= DEBUG_PKT_BYTES)
+ break;
+ printk("%s%02X", !(i % 4) ? " " : "", data[i]);
+ }
+ printk("\n");
+#endif
+}
+
+
+static inline void debug_desc(u32 phys, struct desc *desc)
+{
+#if DEBUG_DESC
+ printk(KERN_DEBUG "%X: %X %3X %3X %08X %X %X\n",
+ phys, desc->next, desc->buf_len, desc->pkt_len,
+ desc->data, desc->status, desc->error_count);
+#endif
+}
+
+static inline int queue_get_desc(unsigned int queue, struct port *port,
+ int is_tx)
+{
+ u32 phys, tab_phys, n_desc;
+ struct desc *tab;
+
+ if (!(phys = qmgr_get_entry(queue)))
+ return -1;
+
+ BUG_ON(phys & 0x1F);
+ tab_phys = is_tx ? tx_desc_phys(port, 0) : rx_desc_phys(port, 0);
+ tab = is_tx ? tx_desc_ptr(port, 0) : rx_desc_ptr(port, 0);
+ n_desc = (phys - tab_phys) / sizeof(struct desc);
+ BUG_ON(n_desc >= (is_tx ? TX_DESCS : RX_DESCS));
+ debug_desc(phys, &tab[n_desc]);
+ BUG_ON(tab[n_desc].next);
+ return n_desc;
+}
+
+static inline void queue_put_desc(unsigned int queue, u32 phys,
+ struct desc *desc)
+{
+ debug_desc(phys, desc);
+ BUG_ON(phys & 0x1F);
+ qmgr_put_entry(queue, phys);
+ BUG_ON(qmgr_stat_overflow(queue));
+}
+
+
+static inline void dma_unmap_tx(struct port *port, struct desc *desc)
+{
+#ifdef __ARMEB__
+ dma_unmap_single(&port->netdev->dev, desc->data,
+ desc->buf_len, DMA_TO_DEVICE);
+#else
+ dma_unmap_single(&port->netdev->dev, desc->data & ~3,
+ ALIGN((desc->data & 3) + desc->buf_len, 4),
+ DMA_TO_DEVICE);
+#endif
+}
+
+
+static void hss_hdlc_set_carrier(void *pdev, int carrier)
+{
+ struct net_device *netdev = pdev;
+ struct port *port = dev_to_port(netdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&npe_lock, flags);
+ port->carrier = carrier;
+ if (!port->loopback) {
+ if (carrier)
+ netif_carrier_on(netdev);
+ else
+ netif_carrier_off(netdev);
+ }
+ spin_unlock_irqrestore(&npe_lock, flags);
+}
+
+static void hss_hdlc_rx_irq(void *pdev)
+{
+ struct net_device *dev = pdev;
+ struct port *port = dev_to_port(dev);
+
+#if DEBUG_RX
+ printk(KERN_DEBUG "%s: hss_hdlc_rx_irq\n", dev->name);
+#endif
+ qmgr_disable_irq(queue_ids[port->id].rx);
+ netif_rx_schedule(dev, &port->napi);
+}
+
+static int hss_hdlc_poll(struct napi_struct *napi, int budget)
+{
+ struct port *port = container_of(napi, struct port, napi);
+ struct net_device *dev = port->netdev;
+ unsigned int rxq = queue_ids[port->id].rx;
+ unsigned int rxfreeq = queue_ids[port->id].rxfree;
+ int received = 0;
+
+#if DEBUG_RX
+ printk(KERN_DEBUG "%s: hss_hdlc_poll\n", dev->name);
+#endif
+
+ while (received < budget) {
+ struct sk_buff *skb;
+ struct desc *desc;
+ int n;
+#ifdef __ARMEB__
+ struct sk_buff *temp;
+ u32 phys;
+#endif
+
+ if ((n = queue_get_desc(rxq, port, 0)) < 0) {
+#if DEBUG_RX
+ printk(KERN_DEBUG "%s: hss_hdlc_poll"
+ " netif_rx_complete\n", dev->name);
+#endif
+ netif_rx_complete(dev, napi);
+ qmgr_enable_irq(rxq);
+ if (!qmgr_stat_empty(rxq) &&
+ netif_rx_reschedule(napi)) {
+#if DEBUG_RX
+ printk(KERN_DEBUG "%s: hss_hdlc_poll"
+ " netif_rx_reschedule succeeded\n",
+ dev->name);
+#endif
+ qmgr_disable_irq(rxq);
+ continue;
+ }
+#if DEBUG_RX
+ printk(KERN_DEBUG "%s: hss_hdlc_poll all done\n",
+ dev->name);
+#endif
+ return received; /* all work done */
+ }
+
+ desc = rx_desc_ptr(port, n);
+#if 0 /* FIXME - error_count counts modulo 256, perhaps we should use it */
+ if (desc->error_count)
+ printk(KERN_DEBUG "%s: hss_hdlc_poll status 0x%02X"
+ " errors %u\n", dev->name, desc->status,
+ desc->error_count);
+#endif
+ skb = NULL;
+ switch (desc->status) {
+ case 0:
+#ifdef __ARMEB__
+ if ((skb = netdev_alloc_skb(dev, RX_SIZE)) != NULL) {
+ phys = dma_map_single(&dev->dev, skb->data,
+ RX_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->dev, phys)) {
+ dev_kfree_skb(skb);
+ skb = NULL;
+ }
+ }
+#else
+ skb = netdev_alloc_skb(dev, desc->pkt_len);
+#endif
+ if (!skb)
+ dev->stats.rx_dropped++;
+ break;
+ case ERR_HDLC_ALIGN:
+ case ERR_HDLC_ABORT:
+ dev->stats.rx_frame_errors++;
+ dev->stats.rx_errors++;
+ break;
+ case ERR_HDLC_FCS:
+ dev->stats.rx_crc_errors++;
+ dev->stats.rx_errors++;
+ break;
+ case ERR_HDLC_TOO_LONG:
+ dev->stats.rx_length_errors++;
+ dev->stats.rx_errors++;
+ break;
+ default: /* FIXME - remove printk */
+ printk(KERN_ERR "%s: hss_hdlc_poll: status 0x%02X"
+ " errors %u\n", dev->name, desc->status,
+ desc->error_count);
+ dev->stats.rx_errors++;
+ }
+
+ if (!skb) {
+ /* put the desc back on RX-ready queue */
+ desc->buf_len = RX_SIZE;
+ desc->pkt_len = desc->status = 0;
+ queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc);
+ continue;
+ }
+
+ /* process received frame */
+#ifdef __ARMEB__
+ temp = skb;
+ skb = port->rx_buff_tab[n];
+ dma_unmap_single(&dev->dev, desc->data,
+ RX_SIZE, DMA_FROM_DEVICE);
+#else
+ dma_sync_single(&dev->dev, desc->data,
+ RX_SIZE, DMA_FROM_DEVICE);
+ memcpy_swab32((u32 *)skb->data, (u32 *)port->rx_buff_tab[n],
+ ALIGN(desc->pkt_len, 4) / 4);
+#endif
+ skb_put(skb, desc->pkt_len);
+
+ debug_pkt(dev, "hss_hdlc_poll", skb->data, skb->len);
+
+ skb->protocol = hdlc_type_trans(skb, dev);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ netif_receive_skb(skb);
+
+ /* put the new buffer on RX-free queue */
+#ifdef __ARMEB__
+ port->rx_buff_tab[n] = temp;
+ desc->data = phys;
+#endif
+ desc->buf_len = RX_SIZE;
+ desc->pkt_len = 0;
+ queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc);
+ received++;
+ }
+#if DEBUG_RX
+ printk(KERN_DEBUG "hss_hdlc_poll: end, not all work done\n");
+#endif
+ return received; /* not all work done */
+}
+
+
+static void hss_hdlc_txdone_irq(void *pdev)
+{
+ struct net_device *dev = pdev;
+ struct port *port = dev_to_port(dev);
+ int n_desc;
+
+#if DEBUG_TX
+ printk(KERN_DEBUG DRV_NAME ": hss_hdlc_txdone_irq\n");
+#endif
+ while ((n_desc = queue_get_desc(queue_ids[port->id].txdone,
+ port, 1)) >= 0) {
+ struct desc *desc;
+ int start;
+
+ desc = tx_desc_ptr(port, n_desc);
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += desc->pkt_len;
+
+ dma_unmap_tx(port, desc);
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_txdone_irq free %p\n",
+ dev->name, port->tx_buff_tab[n_desc]);
+#endif
+ free_buffer_irq(port->tx_buff_tab[n_desc]);
+ port->tx_buff_tab[n_desc] = NULL;
+
+ start = qmgr_stat_empty(port->plat->txreadyq);
+ queue_put_desc(port->plat->txreadyq,
+ tx_desc_phys(port, n_desc), desc);
+ if (start) {
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_txdone_irq xmit"
+ " ready\n", dev->name);
+#endif
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+static int hss_hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct port *port = dev_to_port(dev);
+ unsigned int txreadyq = port->plat->txreadyq;
+ int len, offset, bytes, n;
+ void *mem;
+ u32 phys;
+ struct desc *desc;
+
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_xmit\n", dev->name);
+#endif
+
+ if (unlikely(skb->len > HDLC_MAX_MRU)) {
+ dev_kfree_skb(skb);
+ dev->stats.tx_errors++;
+ return NETDEV_TX_OK;
+ }
+
+ debug_pkt(dev, "hss_hdlc_xmit", skb->data, skb->len);
+
+ len = skb->len;
+#ifdef __ARMEB__
+ offset = 0; /* no need to keep alignment */
+ bytes = len;
+ mem = skb->data;
+#else
+ offset = (int)skb->data & 3; /* keep 32-bit alignment */
+ bytes = ALIGN(offset + len, 4);
+ if (!(mem = kmalloc(bytes, GFP_ATOMIC))) {
+ dev_kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+ memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4);
+ dev_kfree_skb(skb);
+#endif
+
+ phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE);
+ if (dma_mapping_error(&dev->dev, phys)) {
+#ifdef __ARMEB__
+ dev_kfree_skb(skb);
+#else
+ kfree(mem);
+#endif
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ n = queue_get_desc(txreadyq, port, 1);
+ BUG_ON(n < 0);
+ desc = tx_desc_ptr(port, n);
+
+#ifdef __ARMEB__
+ port->tx_buff_tab[n] = skb;
+#else
+ port->tx_buff_tab[n] = mem;
+#endif
+ desc->data = phys + offset;
+ desc->buf_len = desc->pkt_len = len;
+
+ wmb();
+ queue_put_desc(queue_ids[port->id].tx, tx_desc_phys(port, n), desc);
+ dev->trans_start = jiffies;
+
+ if (qmgr_stat_empty(txreadyq)) {
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_xmit queue full\n", dev->name);
+#endif
+ netif_stop_queue(dev);
+ /* we could miss TX ready interrupt */
+ if (!qmgr_stat_empty(txreadyq)) {
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_xmit ready again\n",
+ dev->name);
+#endif
+ netif_wake_queue(dev);
+ }
+ }
+
+#if DEBUG_TX
+ printk(KERN_DEBUG "%s: hss_hdlc_xmit end\n", dev->name);
+#endif
+ return NETDEV_TX_OK;
+}
+
+
+static int request_hdlc_queues(struct port *port)
+{
+ int err;
+
+ err = qmgr_request_queue(queue_ids[port->id].rxfree, RX_DESCS, 0, 0,
+ "%s:RX-free", port->netdev->name);
+ if (err)
+ return err;
+
+ err = qmgr_request_queue(queue_ids[port->id].rx, RX_DESCS, 0, 0,
+ "%s:RX", port->netdev->name);
+ if (err)
+ goto rel_rxfree;
+
+ err = qmgr_request_queue(queue_ids[port->id].tx, TX_DESCS, 0, 0,
+ "%s:TX", port->netdev->name);
+ if (err)
+ goto rel_rx;
+
+ err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0,
+ "%s:TX-ready", port->netdev->name);
+ if (err)
+ goto rel_tx;
+
+ err = qmgr_request_queue(queue_ids[port->id].txdone, TX_DESCS, 0, 0,
+ "%s:TX-done", port->netdev->name);
+ if (err)
+ goto rel_txready;
+ return 0;
+
+rel_txready:
+ qmgr_release_queue(port->plat->txreadyq);
+rel_tx:
+ qmgr_release_queue(queue_ids[port->id].tx);
+rel_rx:
+ qmgr_release_queue(queue_ids[port->id].rx);
+rel_rxfree:
+ qmgr_release_queue(queue_ids[port->id].rxfree);
+ printk(KERN_DEBUG "%s: unable to request hardware queues\n",
+ port->netdev->name);
+ return err;
+}
+
+static void release_hdlc_queues(struct port *port)
+{
+ qmgr_release_queue(queue_ids[port->id].rxfree);
+ qmgr_release_queue(queue_ids[port->id].rx);
+ qmgr_release_queue(queue_ids[port->id].txdone);
+ qmgr_release_queue(queue_ids[port->id].tx);
+ qmgr_release_queue(port->plat->txreadyq);
+}
+
+static int init_hdlc_queues(struct port *port)
+{
+ int i;
+
+ if (!ports_open)
+ if (!(dma_pool = dma_pool_create(DRV_NAME, NULL,
+ POOL_ALLOC_SIZE, 32, 0)))
+ return -ENOMEM;
+
+ if (!(port->desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL,
+ &port->desc_tab_phys)))
+ return -ENOMEM;
+ memset(port->desc_tab, 0, POOL_ALLOC_SIZE);
+ memset(port->rx_buff_tab, 0, sizeof(port->rx_buff_tab)); /* tables */
+ memset(port->tx_buff_tab, 0, sizeof(port->tx_buff_tab));
+
+ /* Setup RX buffers */
+ for (i = 0; i < RX_DESCS; i++) {
+ struct desc *desc = rx_desc_ptr(port, i);
+ buffer_t *buff;
+ void *data;
+#ifdef __ARMEB__
+ if (!(buff = netdev_alloc_skb(port->netdev, RX_SIZE)))
+ return -ENOMEM;
+ data = buff->data;
+#else
+ if (!(buff = kmalloc(RX_SIZE, GFP_KERNEL)))
+ return -ENOMEM;
+ data = buff;
+#endif
+ desc->buf_len = RX_SIZE;
+ desc->data = dma_map_single(&port->netdev->dev, data,
+ RX_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&port->netdev->dev, desc->data)) {
+ free_buffer(buff);
+ return -EIO;
+ }
+ port->rx_buff_tab[i] = buff;
+ }
+
+ return 0;
+}
+
+static void destroy_hdlc_queues(struct port *port)
+{
+ int i;
+
+ if (port->desc_tab) {
+ for (i = 0; i < RX_DESCS; i++) {
+ struct desc *desc = rx_desc_ptr(port, i);
+ buffer_t *buff = port->rx_buff_tab[i];
+ if (buff) {
+ dma_unmap_single(&port->netdev->dev,
+ desc->data, RX_SIZE,
+ DMA_FROM_DEVICE);
+ free_buffer(buff);
+ }
+ }
+ for (i = 0; i < TX_DESCS; i++) {
+ struct desc *desc = tx_desc_ptr(port, i);
+ buffer_t *buff = port->tx_buff_tab[i];
+ if (buff) {
+ dma_unmap_tx(port, desc);
+ free_buffer(buff);
+ }
+ }
+ dma_pool_free(dma_pool, port->desc_tab, port->desc_tab_phys);
+ port->desc_tab = NULL;
+ }
+
+ if (!ports_open && dma_pool) {
+ dma_pool_destroy(dma_pool);
+ dma_pool = NULL;
+ }
+}
+
+static int hss_hdlc_open(struct net_device *dev)
+{
+ struct port *port = dev_to_port(dev);
+ unsigned long flags;
+ int i, err = 0;
+
+ if ((err = hdlc_open(dev)))
+ return err;
+
+ if ((err = hss_load_firmware(port)))
+ goto err_hdlc_close;
+
+ if ((err = request_hdlc_queues(port)))
+ goto err_hdlc_close;
+
+ if ((err = init_hdlc_queues(port)))
+ goto err_destroy_queues;
+
+ spin_lock_irqsave(&npe_lock, flags);
+ if (port->plat->open)
+ if ((err = port->plat->open(port->id, dev,
+ hss_hdlc_set_carrier)))
+ goto err_unlock;
+ spin_unlock_irqrestore(&npe_lock, flags);
+
+ /* Populate queues with buffers, no failure after this point */
+ for (i = 0; i < TX_DESCS; i++)
+ queue_put_desc(port->plat->txreadyq,
+ tx_desc_phys(port, i), tx_desc_ptr(port, i));
+
+ for (i = 0; i < RX_DESCS; i++)
+ queue_put_desc(queue_ids[port->id].rxfree,
+ rx_desc_phys(port, i), rx_desc_ptr(port, i));
+
+ napi_enable(&port->napi);
+ netif_start_queue(dev);
+
+ qmgr_set_irq(queue_ids[port->id].rx, QUEUE_IRQ_SRC_NOT_EMPTY,
+ hss_hdlc_rx_irq, dev);
+
+ qmgr_set_irq(queue_ids[port->id].txdone, QUEUE_IRQ_SRC_NOT_EMPTY,
+ hss_hdlc_txdone_irq, dev);
+ qmgr_enable_irq(queue_ids[port->id].txdone);
+
+ ports_open++;
+
+ hss_set_hdlc_cfg(port);
+ hss_config(port);
+
+ hss_start_hdlc(port);
+
+ /* we may already have RX data, enables IRQ */
+ netif_rx_schedule(dev, &port->napi);
+ return 0;
+
+err_unlock:
+ spin_unlock_irqrestore(&npe_lock, flags);
+err_destroy_queues:
+ destroy_hdlc_queues(port);
+ release_hdlc_queues(port);
+err_hdlc_close:
+ hdlc_close(dev);
+ return err;
+}
+
+static int hss_hdlc_close(struct net_device *dev)
+{
+ struct port *port = dev_to_port(dev);
+ unsigned long flags;
+ int i, buffs = RX_DESCS; /* allocated RX buffers */
+
+ spin_lock_irqsave(&npe_lock, flags);
+ ports_open--;
+ qmgr_disable_irq(queue_ids[port->id].rx);
+ netif_stop_queue(dev);
+ napi_disable(&port->napi);
+
+ hss_stop_hdlc(port);
+
+ while (queue_get_desc(queue_ids[port->id].rxfree, port, 0) >= 0)
+ buffs--;
+ while (queue_get_desc(queue_ids[port->id].rx, port, 0) >= 0)
+ buffs--;
+
+ if (buffs)
+ printk(KERN_CRIT "%s: unable to drain RX queue, %i buffer(s)"
+ " left in NPE\n", dev->name, buffs);
+
+ buffs = TX_DESCS;
+ while (queue_get_desc(queue_ids[port->id].tx, port, 1) >= 0)
+ buffs--; /* cancel TX */
+
+ i = 0;
+ do {
+ while (queue_get_desc(port->plat->txreadyq, port, 1) >= 0)
+ buffs--;
+ if (!buffs)
+ break;
+ } while (++i < MAX_CLOSE_WAIT);
+
+ if (buffs)
+ printk(KERN_CRIT "%s: unable to drain TX queue, %i buffer(s) "
+ "left in NPE\n", dev->name, buffs);
+#if DEBUG_CLOSE
+ if (!buffs)
+ printk(KERN_DEBUG "Draining TX queues took %i cycles\n", i);
+#endif
+ qmgr_disable_irq(queue_ids[port->id].txdone);
+
+ if (port->plat->close)
+ port->plat->close(port->id, dev);
+ spin_unlock_irqrestore(&npe_lock, flags);
+
+ destroy_hdlc_queues(port);
+ release_hdlc_queues(port);
+ hdlc_close(dev);
+ return 0;
+}
+
+
+static int hss_hdlc_attach(struct net_device *dev, unsigned short encoding,
+ unsigned short parity)
+{
+ struct port *port = dev_to_port(dev);
+
+ if (encoding != ENCODING_NRZ)
+ return -EINVAL;
+
+ switch(parity) {
+ case PARITY_CRC16_PR1_CCITT:
+ port->hdlc_cfg = 0;
+ return 0;
+
+ case PARITY_CRC32_PR1_CCITT:
+ port->hdlc_cfg = PKT_HDLC_CRC_32;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ const size_t size = sizeof(sync_serial_settings);
+ sync_serial_settings new_line;
+ sync_serial_settings __user *line = ifr->ifr_settings.ifs_ifsu.sync;
+ struct port *port = dev_to_port(dev);
+ unsigned long flags;
+ int clk;
+
+ if (cmd != SIOCWANDEV)
+ return hdlc_ioctl(dev, ifr, cmd);
+
+ switch(ifr->ifr_settings.type) {
+ case IF_GET_IFACE:
+ ifr->ifr_settings.type = IF_IFACE_V35;
+ if (ifr->ifr_settings.size < size) {
+ ifr->ifr_settings.size = size; /* data size wanted */
+ return -ENOBUFS;
+ }
+ memset(&new_line, 0, sizeof(new_line));
+ new_line.clock_type = port->clock_type;
+ new_line.clock_rate = 2048000; /* FIXME */
+ new_line.loopback = port->loopback;
+ if (copy_to_user(line, &new_line, size))
+ return -EFAULT;
+ return 0;
+
+ case IF_IFACE_SYNC_SERIAL:
+ case IF_IFACE_V35:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&new_line, line, size))
+ return -EFAULT;
+
+ clk = new_line.clock_type;
+ if (port->plat->set_clock)
+ clk = port->plat->set_clock(port->id, clk);
+
+ if (clk != CLOCK_EXT && clk != CLOCK_INT)
+ return -EINVAL; /* No such clock setting */
+
+ if (new_line.loopback != 0 && new_line.loopback != 1)
+ return -EINVAL;
+
+ port->clock_type = clk; /* Update settings */
+ /* FIXME port->clock_rate = new_line.clock_rate */;
+ port->loopback = new_line.loopback;
+
+ spin_lock_irqsave(&npe_lock, flags);
+
+ if (dev->flags & IFF_UP)
+ hss_config(port);
+
+ if (port->loopback || port->carrier)
+ netif_carrier_on(port->netdev);
+ else
+ netif_carrier_off(port->netdev);
+ spin_unlock_irqrestore(&npe_lock, flags);
+
+ return 0;
+
+ default:
+ return hdlc_ioctl(dev, ifr, cmd);
+ }
+}
+
+/*****************************************************************************
+ * initialization
+ ****************************************************************************/
+
+static int __devinit hss_init_one(struct platform_device *pdev)
+{
+ struct port *port;
+ struct net_device *dev;
+ hdlc_device *hdlc;
+ int err;
+
+ if ((port = kzalloc(sizeof(*port), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ if ((port->npe = npe_request(0)) == NULL) {
+ err = -ENOSYS;
+ goto err_free;
+ }
+
+ if ((port->netdev = dev = alloc_hdlcdev(port)) == NULL) {
+ err = -ENOMEM;
+ goto err_plat;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ hdlc = dev_to_hdlc(dev);
+ hdlc->attach = hss_hdlc_attach;
+ hdlc->xmit = hss_hdlc_xmit;
+ dev->open = hss_hdlc_open;
+ dev->stop = hss_hdlc_close;
+ dev->do_ioctl = hss_hdlc_ioctl;
+ dev->tx_queue_len = 100;
+ port->clock_type = CLOCK_EXT;
+ port->clock_rate = 2048000;
+ port->id = pdev->id;
+ port->dev = &pdev->dev;
+ port->plat = pdev->dev.platform_data;
+ netif_napi_add(dev, &port->napi, hss_hdlc_poll, NAPI_WEIGHT);
+
+ if ((err = register_hdlc_device(dev)))
+ goto err_free_netdev;
+
+ platform_set_drvdata(pdev, port);
+
+ printk(KERN_INFO "%s: HSS-%i\n", dev->name, port->id);
+ return 0;
+
+err_free_netdev:
+ free_netdev(dev);
+err_plat:
+ npe_release(port->npe);
+err_free:
+ kfree(port);
+ return err;
+}
+
+static int __devexit hss_remove_one(struct platform_device *pdev)
+{
+ struct port *port = platform_get_drvdata(pdev);
+
+ unregister_hdlc_device(port->netdev);
+ free_netdev(port->netdev);
+ npe_release(port->npe);
+ platform_set_drvdata(pdev, NULL);
+ kfree(port);
+ return 0;
+}
+
+static struct platform_driver ixp4xx_hss_driver = {
+ .driver.name = DRV_NAME,
+ .probe = hss_init_one,
+ .remove = hss_remove_one,
+};
+
+static int __init hss_init_module(void)
+{
+ if ((ixp4xx_read_feature_bits() &
+ (IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS)) !=
+ (IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS))
+ return -ENOSYS;
+
+ spin_lock_init(&npe_lock);
+
+ return platform_driver_register(&ixp4xx_hss_driver);
+}
+
+static void __exit hss_cleanup_module(void)
+{
+ platform_driver_unregister(&ixp4xx_hss_driver);
+}
+
+MODULE_AUTHOR("Krzysztof Halasa");
+MODULE_DESCRIPTION("Intel IXP4xx HSS driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ixp4xx_hss");
+module_init(hss_init_module);
+module_exit(hss_cleanup_module);
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
index 24fd613466b7..5b61b3eef45f 100644
--- a/drivers/net/wan/lapbether.c
+++ b/drivers/net/wan/lapbether.c
@@ -143,7 +143,6 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
*ptr = 0x00;
skb->protocol = x25_type_trans(skb, dev);
- skb->dev->last_rx = jiffies;
return netif_rx(skb);
}
@@ -235,7 +234,6 @@ static void lapbeth_connected(struct net_device *dev, int reason)
*ptr = 0x01;
skb->protocol = x25_type_trans(skb, dev);
- skb->dev->last_rx = jiffies;
netif_rx(skb);
}
@@ -253,7 +251,6 @@ static void lapbeth_disconnected(struct net_device *dev, int reason)
*ptr = 0x02;
skb->protocol = x25_type_trans(skb, dev);
- skb->dev->last_rx = jiffies;
netif_rx(skb);
}
diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c
index d7bb63e616b5..feac3b99f8fe 100644
--- a/drivers/net/wan/lmc/lmc_main.c
+++ b/drivers/net/wan/lmc/lmc_main.c
@@ -1594,7 +1594,6 @@ static int lmc_rx(struct net_device *dev)
goto skip_packet;
}
- dev->last_rx = jiffies;
sc->lmc_device->stats.rx_packets++;
sc->lmc_device->stats.rx_bytes += len;
diff --git a/drivers/net/wan/lmc/lmc_proto.c b/drivers/net/wan/lmc/lmc_proto.c
index be9877ff551e..94b4c208b013 100644
--- a/drivers/net/wan/lmc/lmc_proto.c
+++ b/drivers/net/wan/lmc/lmc_proto.c
@@ -142,7 +142,6 @@ void lmc_proto_netif(lmc_softc_t *sc, struct sk_buff *skb) /*FOLD00*/
case LMC_PPP:
case LMC_NET:
default:
- skb->dev->last_rx = jiffies;
netif_rx(skb);
break;
case LMC_RAW:
diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c
index 0a566b0daacb..697715ae80f4 100644
--- a/drivers/net/wan/n2.c
+++ b/drivers/net/wan/n2.c
@@ -53,7 +53,7 @@ static const char* devname = "RISCom/N2";
#define NEED_SCA_MSCI_INTR
#define MAX_TX_BUFFERS 10
-static char *hw = NULL; /* pointer to hw=xxx command line string */
+static char *hw; /* pointer to hw=xxx command line string */
/* RISCom/N2 Board Registers */
@@ -145,7 +145,6 @@ static card_t **new_card = &first_card;
&(card)->ports[port] : NULL)
-
static __inline__ u8 sca_get_page(card_t *card)
{
return inb(card->io + N2_PSR) & PSR_PAGEBITS;
@@ -159,9 +158,7 @@ static __inline__ void openwin(card_t *card, u8 page)
}
-
-#include "hd6457x.c"
-
+#include "hd64570.c"
static void n2_set_iface(port_t *port)
@@ -478,7 +475,7 @@ static int __init n2_run(unsigned long io, unsigned long irq,
n2_destroy_card(card);
return -ENOBUFS;
}
- sca_init_sync_port(port); /* Set up SCA memory */
+ sca_init_port(port); /* Set up SCA memory */
printk(KERN_INFO "%s: RISCom/N2 node %d\n",
dev->name, port->phy_node);
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index d0a8d1e352ac..c23fde0c0344 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -1769,7 +1769,7 @@ cpc_trace(struct net_device *dev, struct sk_buff *skb_main, char rx_tx)
static void cpc_tx_timeout(struct net_device *dev)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
int ch = chan->channel;
@@ -1796,7 +1796,7 @@ static void cpc_tx_timeout(struct net_device *dev)
static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
int ch = chan->channel;
@@ -1874,7 +1874,7 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev)
static void cpc_net_rx(struct net_device *dev)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
int ch = chan->channel;
@@ -2522,7 +2522,7 @@ static int cpc_change_mtu(struct net_device *dev, int new_mtu)
static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
pc300conf_t conf_aux;
@@ -2718,9 +2718,8 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
pc300patrntst.num_errors =
falc_pattern_test_error(card, ch);
- if (!arg
- || copy_to_user(arg, &pc300patrntst,
- sizeof (pc300patterntst_t)))
+ if (copy_to_user(arg, &pc300patrntst,
+ sizeof(pc300patterntst_t)))
return -EINVAL;
} else {
falc_pattern_test(card, ch, pc300patrntst.patrntst_on);
@@ -3058,7 +3057,7 @@ static int tx_config(pc300dev_t * d)
static int cpc_attach(struct net_device *dev, unsigned short encoding,
unsigned short parity)
{
- pc300dev_t *d = (pc300dev_t *)dev->priv;
+ pc300dev_t *d = (pc300dev_t *)dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *)d->chan;
pc300_t *card = (pc300_t *)chan->card;
pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
@@ -3138,7 +3137,7 @@ static void cpc_closech(pc300dev_t * d)
int cpc_open(struct net_device *dev)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
struct ifreq ifr;
int result;
@@ -3166,7 +3165,7 @@ err_out:
static int cpc_close(struct net_device *dev)
{
- pc300dev_t *d = (pc300dev_t *) dev->priv;
+ pc300dev_t *d = (pc300dev_t *) dev_to_hdlc(dev)->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
unsigned long flags;
@@ -3347,7 +3346,7 @@ static void cpc_init_card(pc300_t * card)
d->line_on = 0;
d->line_off = 0;
- dev = alloc_hdlcdev(NULL);
+ dev = alloc_hdlcdev(d);
if (dev == NULL)
continue;
@@ -3372,7 +3371,6 @@ static void cpc_init_card(pc300_t * card)
dev->do_ioctl = cpc_ioctl;
if (register_hdlc_device(dev) == 0) {
- dev->priv = d; /* We need 'priv', hdlc doesn't */
printk("%s: Cyclades-PC300/", dev->name);
switch (card->hw.type) {
case PC300_TE:
diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c
index bf1b01590429..f247e5d9002a 100644
--- a/drivers/net/wan/pc300too.c
+++ b/drivers/net/wan/pc300too.c
@@ -1,7 +1,7 @@
/*
* Cyclades PC300 synchronous serial card driver for Linux
*
- * Copyright (C) 2000-2007 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2008 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
@@ -11,7 +11,7 @@
*
* Sources of information:
* Hitachi HD64572 SCA-II User's Manual
- * Cyclades PC300 Linux driver
+ * Original Cyclades PC300 Linux driver
*
* This driver currently supports only PC300/RSV (V.24/V.35) and
* PC300/X21 cards.
@@ -37,17 +37,11 @@
#include "hd64572.h"
-static const char* version = "Cyclades PC300 driver version: 1.17";
-static const char* devname = "PC300";
-
#undef DEBUG_PKT
#define DEBUG_RINGS
#define PC300_PLX_SIZE 0x80 /* PLX control window size (128 B) */
#define PC300_SCA_SIZE 0x400 /* SCA window size (1 KB) */
-#define ALL_PAGES_ALWAYS_MAPPED
-#define NEED_DETECT_RAM
-#define NEED_SCA_MSCI_INTR
#define MAX_TX_BUFFERS 10
static int pci_clock_freq = 33000000;
@@ -81,7 +75,8 @@ typedef struct {
typedef struct port_s {
- struct net_device *dev;
+ struct napi_struct napi;
+ struct net_device *netdev;
struct card_s *card;
spinlock_t lock; /* TX lock */
sync_serial_settings settings;
@@ -93,7 +88,7 @@ typedef struct port_s {
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
u16 txlast;
u8 rxs, txs, tmc; /* SCA registers */
- u8 phy_node; /* physical port # - 0 or 1 */
+ u8 chan; /* physical port # - 0 or 1 */
}port_t;
@@ -114,21 +109,10 @@ typedef struct card_s {
}card_t;
-#define sca_in(reg, card) readb(card->scabase + (reg))
-#define sca_out(value, reg, card) writeb(value, card->scabase + (reg))
-#define sca_inw(reg, card) readw(card->scabase + (reg))
-#define sca_outw(value, reg, card) writew(value, card->scabase + (reg))
-#define sca_inl(reg, card) readl(card->scabase + (reg))
-#define sca_outl(value, reg, card) writel(value, card->scabase + (reg))
-
-#define port_to_card(port) (port->card)
-#define log_node(port) (port->phy_node)
-#define phy_node(port) (port->phy_node)
-#define winbase(card) (card->rambase)
#define get_port(card, port) ((port) < (card)->n_ports ? \
(&(card)->ports[port]) : (NULL))
-#include "hd6457x.c"
+#include "hd64572.c"
static void pc300_set_iface(port_t *port)
@@ -139,8 +123,8 @@ static void pc300_set_iface(port_t *port)
u8 rxs = port->rxs & CLK_BRG_MASK;
u8 txs = port->txs & CLK_BRG_MASK;
- sca_out(EXS_TES1, (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
- port_to_card(port));
+ sca_out(EXS_TES1, (port->chan ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
+ port->card);
switch(port->settings.clock_type) {
case CLOCK_INT:
rxs |= CLK_BRG; /* BRG output */
@@ -172,10 +156,10 @@ static void pc300_set_iface(port_t *port)
if (port->card->type == PC300_RSV) {
if (port->iface == IF_IFACE_V35)
writel(card->init_ctrl_value |
- PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
+ PC300_CHMEDIA_MASK(port->chan), init_ctrl);
else
writel(card->init_ctrl_value &
- ~PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
+ ~PC300_CHMEDIA_MASK(port->chan), init_ctrl);
}
}
@@ -280,10 +264,8 @@ static void pc300_pci_remove_one(struct pci_dev *pdev)
card_t *card = pci_get_drvdata(pdev);
for (i = 0; i < 2; i++)
- if (card->ports[i].card) {
- struct net_device *dev = port_to_dev(&card->ports[i]);
- unregister_hdlc_device(dev);
- }
+ if (card->ports[i].card)
+ unregister_hdlc_device(card->ports[i].netdev);
if (card->irq)
free_irq(card->irq, card);
@@ -298,10 +280,10 @@ static void pc300_pci_remove_one(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- if (card->ports[0].dev)
- free_netdev(card->ports[0].dev);
- if (card->ports[1].dev)
- free_netdev(card->ports[1].dev);
+ if (card->ports[0].netdev)
+ free_netdev(card->ports[0].netdev);
+ if (card->ports[1].netdev)
+ free_netdev(card->ports[1].netdev);
kfree(card);
}
@@ -318,12 +300,6 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
u32 scaphys; /* SCA memory base */
u32 plxphys; /* PLX registers memory base */
-#ifndef MODULE
- static int printed_version;
- if (!printed_version++)
- printk(KERN_INFO "%s\n", version);
-#endif
-
i = pci_enable_device(pdev);
if (i)
return i;
@@ -343,27 +319,6 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
}
pci_set_drvdata(pdev, card);
- if (pdev->device == PCI_DEVICE_ID_PC300_TE_1 ||
- pdev->device == PCI_DEVICE_ID_PC300_TE_2)
- card->type = PC300_TE; /* not fully supported */
- else if (card->init_ctrl_value & PC300_CTYPE_MASK)
- card->type = PC300_X21;
- else
- card->type = PC300_RSV;
-
- if (pdev->device == PCI_DEVICE_ID_PC300_RX_1 ||
- pdev->device == PCI_DEVICE_ID_PC300_TE_1)
- card->n_ports = 1;
- else
- card->n_ports = 2;
-
- for (i = 0; i < card->n_ports; i++)
- if (!(card->ports[i].dev = alloc_hdlcdev(&card->ports[i]))) {
- printk(KERN_ERR "pc300: unable to allocate memory\n");
- pc300_pci_remove_one(pdev);
- return -ENOMEM;
- }
-
if (pci_resource_len(pdev, 0) != PC300_PLX_SIZE ||
pci_resource_len(pdev, 2) != PC300_SCA_SIZE ||
pci_resource_len(pdev, 3) < 16384) {
@@ -372,14 +327,14 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
return -EFAULT;
}
- plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
+ plxphys = pci_resource_start(pdev, 0) & PCI_BASE_ADDRESS_MEM_MASK;
card->plxbase = ioremap(plxphys, PC300_PLX_SIZE);
- scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
+ scaphys = pci_resource_start(pdev, 2) & PCI_BASE_ADDRESS_MEM_MASK;
card->scabase = ioremap(scaphys, PC300_SCA_SIZE);
- ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
- card->rambase = ioremap(ramphys, pci_resource_len(pdev,3));
+ ramphys = pci_resource_start(pdev, 3) & PCI_BASE_ADDRESS_MEM_MASK;
+ card->rambase = pci_ioremap_bar(pdev, 3);
if (card->plxbase == NULL ||
card->scabase == NULL ||
@@ -393,6 +348,27 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
card->init_ctrl_value = readl(&((plx9050 __iomem *)card->scabase)->init_ctrl);
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, plxphys);
+ if (pdev->device == PCI_DEVICE_ID_PC300_TE_1 ||
+ pdev->device == PCI_DEVICE_ID_PC300_TE_2)
+ card->type = PC300_TE; /* not fully supported */
+ else if (card->init_ctrl_value & PC300_CTYPE_MASK)
+ card->type = PC300_X21;
+ else
+ card->type = PC300_RSV;
+
+ if (pdev->device == PCI_DEVICE_ID_PC300_RX_1 ||
+ pdev->device == PCI_DEVICE_ID_PC300_TE_1)
+ card->n_ports = 1;
+ else
+ card->n_ports = 2;
+
+ for (i = 0; i < card->n_ports; i++)
+ if (!(card->ports[i].netdev = alloc_hdlcdev(&card->ports[i]))) {
+ printk(KERN_ERR "pc300: unable to allocate memory\n");
+ pc300_pci_remove_one(pdev);
+ return -ENOMEM;
+ }
+
/* Reset PLX */
p = &card->plxbase->init_ctrl;
writel(card->init_ctrl_value | 0x40000000, p);
@@ -446,7 +422,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
writew(0x0041, &card->plxbase->intr_ctrl_stat);
/* Allocate IRQ */
- if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, devname, card)) {
+ if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, "pc300", card)) {
printk(KERN_WARNING "pc300: could not allocate IRQ%d.\n",
pdev->irq);
pc300_pci_remove_one(pdev);
@@ -463,9 +439,9 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
for (i = 0; i < card->n_ports; i++) {
port_t *port = &card->ports[i];
- struct net_device *dev = port_to_dev(port);
+ struct net_device *dev = port->netdev;
hdlc_device *hdlc = dev_to_hdlc(dev);
- port->phy_node = i;
+ port->chan = i;
spin_lock_init(&port->lock);
dev->irq = card->irq;
@@ -484,6 +460,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
else
port->iface = IF_IFACE_V35;
+ sca_init_port(port);
if (register_hdlc_device(dev)) {
printk(KERN_ERR "pc300: unable to register hdlc "
"device\n");
@@ -491,10 +468,9 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
pc300_pci_remove_one(pdev);
return -ENOBUFS;
}
- sca_init_sync_port(port); /* Set up SCA memory */
- printk(KERN_INFO "%s: PC300 node %d\n",
- dev->name, port->phy_node);
+ printk(KERN_INFO "%s: PC300 channel %d\n",
+ dev->name, port->chan);
}
return 0;
}
@@ -524,9 +500,6 @@ static struct pci_driver pc300_pci_driver = {
static int __init pc300_init_module(void)
{
-#ifdef MODULE
- printk(KERN_INFO "%s\n", version);
-#endif
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
printk(KERN_ERR "pc300: Invalid PCI clock frequency\n");
return -EINVAL;
diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c
index b595b64e7538..1104d3a692f7 100644
--- a/drivers/net/wan/pci200syn.c
+++ b/drivers/net/wan/pci200syn.c
@@ -1,7 +1,7 @@
/*
* Goramo PCI200SYN synchronous serial card driver for Linux
*
- * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2002-2008 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
@@ -33,17 +33,11 @@
#include "hd64572.h"
-static const char* version = "Goramo PCI200SYN driver version: 1.16";
-static const char* devname = "PCI200SYN";
-
#undef DEBUG_PKT
#define DEBUG_RINGS
#define PCI200SYN_PLX_SIZE 0x80 /* PLX control window size (128b) */
#define PCI200SYN_SCA_SIZE 0x400 /* SCA window size (1Kb) */
-#define ALL_PAGES_ALWAYS_MAPPED
-#define NEED_DETECT_RAM
-#define NEED_SCA_MSCI_INTR
#define MAX_TX_BUFFERS 10
static int pci_clock_freq = 33000000;
@@ -68,7 +62,8 @@ typedef struct {
typedef struct port_s {
- struct net_device *dev;
+ struct napi_struct napi;
+ struct net_device *netdev;
struct card_s *card;
spinlock_t lock; /* TX lock */
sync_serial_settings settings;
@@ -79,7 +74,7 @@ typedef struct port_s {
u16 txin; /* tx ring buffer 'in' and 'last' pointers */
u16 txlast;
u8 rxs, txs, tmc; /* SCA registers */
- u8 phy_node; /* physical port # - 0 or 1 */
+ u8 chan; /* physical port # - 0 or 1 */
}port_t;
@@ -97,17 +92,6 @@ typedef struct card_s {
}card_t;
-#define sca_in(reg, card) readb(card->scabase + (reg))
-#define sca_out(value, reg, card) writeb(value, card->scabase + (reg))
-#define sca_inw(reg, card) readw(card->scabase + (reg))
-#define sca_outw(value, reg, card) writew(value, card->scabase + (reg))
-#define sca_inl(reg, card) readl(card->scabase + (reg))
-#define sca_outl(value, reg, card) writel(value, card->scabase + (reg))
-
-#define port_to_card(port) (port->card)
-#define log_node(port) (port->phy_node)
-#define phy_node(port) (port->phy_node)
-#define winbase(card) (card->rambase)
#define get_port(card, port) (&card->ports[port])
#define sca_flush(card) (sca_in(IER0, card));
@@ -127,7 +111,7 @@ static inline void new_memcpy_toio(char __iomem *dest, char *src, int length)
#undef memcpy_toio
#define memcpy_toio new_memcpy_toio
-#include "hd6457x.c"
+#include "hd64572.c"
static void pci200_set_iface(port_t *port)
@@ -137,8 +121,8 @@ static void pci200_set_iface(port_t *port)
u8 rxs = port->rxs & CLK_BRG_MASK;
u8 txs = port->txs & CLK_BRG_MASK;
- sca_out(EXS_TES1, (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
- port_to_card(port));
+ sca_out(EXS_TES1, (port->chan ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
+ port->card);
switch(port->settings.clock_type) {
case CLOCK_INT:
rxs |= CLK_BRG; /* BRG output */
@@ -180,7 +164,7 @@ static int pci200_open(struct net_device *dev)
sca_open(dev);
pci200_set_iface(port);
- sca_flush(port_to_card(port));
+ sca_flush(port->card);
return 0;
}
@@ -189,7 +173,7 @@ static int pci200_open(struct net_device *dev)
static int pci200_close(struct net_device *dev)
{
sca_close(dev);
- sca_flush(port_to_card(dev_to_port(dev)));
+ sca_flush(dev_to_port(dev)->card);
hdlc_close(dev);
return 0;
}
@@ -242,7 +226,7 @@ static int pci200_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
memcpy(&port->settings, &new_line, size); /* Update settings */
pci200_set_iface(port);
- sca_flush(port_to_card(port));
+ sca_flush(port->card);
return 0;
default:
@@ -258,10 +242,8 @@ static void pci200_pci_remove_one(struct pci_dev *pdev)
card_t *card = pci_get_drvdata(pdev);
for (i = 0; i < 2; i++)
- if (card->ports[i].card) {
- struct net_device *dev = port_to_dev(&card->ports[i]);
- unregister_hdlc_device(dev);
- }
+ if (card->ports[i].card)
+ unregister_hdlc_device(card->ports[i].netdev);
if (card->irq)
free_irq(card->irq, card);
@@ -276,10 +258,10 @@ static void pci200_pci_remove_one(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- if (card->ports[0].dev)
- free_netdev(card->ports[0].dev);
- if (card->ports[1].dev)
- free_netdev(card->ports[1].dev);
+ if (card->ports[0].netdev)
+ free_netdev(card->ports[0].netdev);
+ if (card->ports[1].netdev)
+ free_netdev(card->ports[1].netdev);
kfree(card);
}
@@ -296,12 +278,6 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
u32 scaphys; /* SCA memory base */
u32 plxphys; /* PLX registers memory base */
-#ifndef MODULE
- static int printed_version;
- if (!printed_version++)
- printk(KERN_INFO "%s\n", version);
-#endif
-
i = pci_enable_device(pdev);
if (i)
return i;
@@ -320,9 +296,9 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
return -ENOBUFS;
}
pci_set_drvdata(pdev, card);
- card->ports[0].dev = alloc_hdlcdev(&card->ports[0]);
- card->ports[1].dev = alloc_hdlcdev(&card->ports[1]);
- if (!card->ports[0].dev || !card->ports[1].dev) {
+ card->ports[0].netdev = alloc_hdlcdev(&card->ports[0]);
+ card->ports[1].netdev = alloc_hdlcdev(&card->ports[1]);
+ if (!card->ports[0].netdev || !card->ports[1].netdev) {
printk(KERN_ERR "pci200syn: unable to allocate memory\n");
pci200_pci_remove_one(pdev);
return -ENOMEM;
@@ -343,7 +319,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
card->scabase = ioremap(scaphys, PCI200SYN_SCA_SIZE);
ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
- card->rambase = ioremap(ramphys, pci_resource_len(pdev,3));
+ card->rambase = pci_ioremap_bar(pdev, 3);
if (card->plxbase == NULL ||
card->scabase == NULL ||
@@ -398,7 +374,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
writew(readw(p) | 0x0040, p);
/* Allocate IRQ */
- if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, devname, card)) {
+ if (request_irq(pdev->irq, sca_intr, IRQF_SHARED, "pci200syn", card)) {
printk(KERN_WARNING "pci200syn: could not allocate IRQ%d.\n",
pdev->irq);
pci200_pci_remove_one(pdev);
@@ -410,9 +386,9 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
for (i = 0; i < 2; i++) {
port_t *port = &card->ports[i];
- struct net_device *dev = port_to_dev(port);
+ struct net_device *dev = port->netdev;
hdlc_device *hdlc = dev_to_hdlc(dev);
- port->phy_node = i;
+ port->chan = i;
spin_lock_init(&port->lock);
dev->irq = card->irq;
@@ -426,6 +402,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
hdlc->xmit = sca_xmit;
port->settings.clock_type = CLOCK_EXT;
port->card = card;
+ sca_init_port(port);
if (register_hdlc_device(dev)) {
printk(KERN_ERR "pci200syn: unable to register hdlc "
"device\n");
@@ -433,10 +410,9 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev,
pci200_pci_remove_one(pdev);
return -ENOBUFS;
}
- sca_init_sync_port(port); /* Set up SCA memory */
- printk(KERN_INFO "%s: PCI200SYN node %d\n",
- dev->name, port->phy_node);
+ printk(KERN_INFO "%s: PCI200SYN channel %d\n",
+ dev->name, port->chan);
}
sca_flush(card);
@@ -464,9 +440,6 @@ static struct pci_driver pci200_pci_driver = {
static int __init pci200_init_module(void)
{
-#ifdef MODULE
- printk(KERN_INFO "%s\n", version);
-#endif
if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
printk(KERN_ERR "pci200syn: Invalid PCI clock frequency\n");
return -EINVAL;
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index ee51b6a5e605..0aa28e1d4366 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -186,6 +186,7 @@ static unsigned int netcard_portlist[ ] __initdata = {
0x2b0, 0x2b4, 0x2c0, 0x2c4, 0x2d0, 0x2d4, 0x2e0, 0x2e4, 0x2f0, 0x2f4,
0 };
+#define NET_LOCAL_LOCK(dev) (((struct net_local *)netdev_priv(dev))->lock)
/*
* Look for SBNI card which addr stored in dev->base_addr, if nonzero.
@@ -287,7 +288,7 @@ static int __init sbni_init(struct net_device *dev)
}
-int __init
+static int __init
sbni_pci_probe( struct net_device *dev )
{
struct pci_dev *pdev = NULL;
@@ -378,22 +379,23 @@ sbni_probe1( struct net_device *dev, unsigned long ioaddr, int irq )
dev->irq = irq;
dev->base_addr = ioaddr;
- /* Allocate dev->priv and fill in sbni-specific dev fields. */
- nl = dev->priv;
+ /* Fill in sbni-specific dev fields. */
+ nl = netdev_priv(dev);
if( !nl ) {
printk( KERN_ERR "%s: unable to get memory!\n", dev->name );
release_region( ioaddr, SBNI_IO_EXTENT );
return NULL;
}
- dev->priv = nl;
memset( nl, 0, sizeof(struct net_local) );
spin_lock_init( &nl->lock );
/* store MAC address (generate if that isn't known) */
*(__be16 *)dev->dev_addr = htons( 0x00ff );
*(__be32 *)(dev->dev_addr + 2) = htonl( 0x01000000 |
- ( (mac[num] ? mac[num] : (u32)((long)dev->priv)) & 0x00ffffff) );
+ ((mac[num] ?
+ mac[num] :
+ (u32)((long)netdev_priv(dev))) & 0x00ffffff));
/* store link settings (speed, receive level ) */
nl->maxframe = DEFAULT_FRAME_LEN;
@@ -447,7 +449,7 @@ sbni_start_xmit( struct sk_buff *skb, struct net_device *dev )
/* Looking for idle device in the list */
for( p = dev; p; ) {
- struct net_local *nl = (struct net_local *) p->priv;
+ struct net_local *nl = netdev_priv(p);
spin_lock( &nl->lock );
if( nl->tx_buf_p || (nl->state & FL_LINE_DOWN) ) {
p = nl->link;
@@ -469,7 +471,7 @@ sbni_start_xmit( struct sk_buff *skb, struct net_device *dev )
static int
sbni_start_xmit( struct sk_buff *skb, struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
netif_stop_queue( dev );
spin_lock( &nl->lock );
@@ -503,12 +505,12 @@ static irqreturn_t
sbni_interrupt( int irq, void *dev_id )
{
struct net_device *dev = dev_id;
- struct net_local *nl = dev->priv;
+ struct net_local *nl = netdev_priv(dev);
int repeat;
spin_lock( &nl->lock );
if( nl->second )
- spin_lock( &((struct net_local *) nl->second->priv)->lock );
+ spin_lock(&NET_LOCAL_LOCK(nl->second));
do {
repeat = 0;
@@ -522,7 +524,7 @@ sbni_interrupt( int irq, void *dev_id )
} while( repeat );
if( nl->second )
- spin_unlock( &((struct net_local *)nl->second->priv)->lock );
+ spin_unlock(&NET_LOCAL_LOCK(nl->second));
spin_unlock( &nl->lock );
return IRQ_HANDLED;
}
@@ -531,7 +533,7 @@ sbni_interrupt( int irq, void *dev_id )
static void
handle_channel( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
int req_ans;
@@ -540,7 +542,7 @@ handle_channel( struct net_device *dev )
#ifdef CONFIG_SBNI_MULTILINE
/* Lock the master device because we going to change its local data */
if( nl->state & FL_SLAVE )
- spin_lock( &((struct net_local *) nl->master->priv)->lock );
+ spin_lock(&NET_LOCAL_LOCK(nl->master));
#endif
outb( (inb( ioaddr + CSR0 ) & ~EN_INT) | TR_REQ, ioaddr + CSR0 );
@@ -576,7 +578,7 @@ handle_channel( struct net_device *dev )
#ifdef CONFIG_SBNI_MULTILINE
if( nl->state & FL_SLAVE )
- spin_unlock( &((struct net_local *) nl->master->priv)->lock );
+ spin_unlock(&NET_LOCAL_LOCK(nl->master));
#endif
}
@@ -589,7 +591,7 @@ handle_channel( struct net_device *dev )
static int
recv_frame( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u32 crc = CRC32_INITIAL;
@@ -623,7 +625,7 @@ recv_frame( struct net_device *dev )
static void
send_frame( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
u32 crc = CRC32_INITIAL;
@@ -680,7 +682,7 @@ do_send:
static void
download_data( struct net_device *dev, u32 *crc_p )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
struct sk_buff *skb = nl->tx_buf_p;
unsigned len = min_t(unsigned int, skb->len - nl->outpos, nl->framelen);
@@ -699,7 +701,7 @@ static int
upload_data( struct net_device *dev, unsigned framelen, unsigned frameno,
unsigned is_first, u32 crc )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
int frame_ok;
@@ -721,9 +723,9 @@ upload_data( struct net_device *dev, unsigned framelen, unsigned frameno,
nl->wait_frameno = 0,
nl->inppos = 0,
#ifdef CONFIG_SBNI_MULTILINE
- ((struct net_local *) nl->master->priv)
+ ((struct net_local *)netdev_priv(nl->master))
->stats.rx_errors++,
- ((struct net_local *) nl->master->priv)
+ ((struct net_local *)netdev_priv(nl->master))
->stats.rx_missed_errors++;
#else
nl->stats.rx_errors++,
@@ -740,8 +742,10 @@ upload_data( struct net_device *dev, unsigned framelen, unsigned frameno,
*/
nl->wait_frameno = 0,
#ifdef CONFIG_SBNI_MULTILINE
- ((struct net_local *) nl->master->priv)->stats.rx_errors++,
- ((struct net_local *) nl->master->priv)->stats.rx_crc_errors++;
+ ((struct net_local *)netdev_priv(nl->master))
+ ->stats.rx_errors++,
+ ((struct net_local *)netdev_priv(nl->master))
+ ->stats.rx_crc_errors++;
#else
nl->stats.rx_errors++,
nl->stats.rx_crc_errors++;
@@ -755,8 +759,8 @@ static inline void
send_complete( struct net_local *nl )
{
#ifdef CONFIG_SBNI_MULTILINE
- ((struct net_local *) nl->master->priv)->stats.tx_packets++;
- ((struct net_local *) nl->master->priv)->stats.tx_bytes
+ ((struct net_local *)netdev_priv(nl->master))->stats.tx_packets++;
+ ((struct net_local *)netdev_priv(nl->master))->stats.tx_bytes
+= nl->tx_buf_p->len;
#else
nl->stats.tx_packets++;
@@ -775,7 +779,7 @@ send_complete( struct net_local *nl )
static void
interpret_ack( struct net_device *dev, unsigned ack )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
if( ack == FRAME_SENT_OK ) {
nl->state &= ~FL_NEED_RESEND;
@@ -809,7 +813,7 @@ interpret_ack( struct net_device *dev, unsigned ack )
static int
append_frame_to_pkt( struct net_device *dev, unsigned framelen, u32 crc )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
u8 *p;
@@ -840,7 +844,7 @@ append_frame_to_pkt( struct net_device *dev, unsigned framelen, u32 crc )
static void
prepare_to_send( struct sk_buff *skb, struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
unsigned int len;
@@ -871,15 +875,15 @@ prepare_to_send( struct sk_buff *skb, struct net_device *dev )
static void
drop_xmit_queue( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
if( nl->tx_buf_p )
dev_kfree_skb_any( nl->tx_buf_p ),
nl->tx_buf_p = NULL,
#ifdef CONFIG_SBNI_MULTILINE
- ((struct net_local *) nl->master->priv)
+ ((struct net_local *)netdev_priv(nl->master))
->stats.tx_errors++,
- ((struct net_local *) nl->master->priv)
+ ((struct net_local *)netdev_priv(nl->master))
->stats.tx_carrier_errors++;
#else
nl->stats.tx_errors++,
@@ -903,7 +907,7 @@ drop_xmit_queue( struct net_device *dev )
static void
send_frame_header( struct net_device *dev, u32 *crc_p )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
u32 crc = *crc_p;
u32 len_field = nl->framelen + 6; /* CRC + frameno + reserved */
@@ -1005,7 +1009,7 @@ get_rx_buf( struct net_device *dev )
static void
indicate_pkt( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
struct sk_buff *skb = nl->rx_buf_p;
skb_put( skb, nl->inppos );
@@ -1013,13 +1017,12 @@ indicate_pkt( struct net_device *dev )
#ifdef CONFIG_SBNI_MULTILINE
skb->protocol = eth_type_trans( skb, nl->master );
netif_rx( skb );
- dev->last_rx = jiffies;
- ++((struct net_local *) nl->master->priv)->stats.rx_packets;
- ((struct net_local *) nl->master->priv)->stats.rx_bytes += nl->inppos;
+ ++((struct net_local *)netdev_priv(nl->master))->stats.rx_packets;
+ ((struct net_local *)netdev_priv(nl->master))->stats.rx_bytes +=
+ nl->inppos;
#else
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
- dev->last_rx = jiffies;
++nl->stats.rx_packets;
nl->stats.rx_bytes += nl->inppos;
#endif
@@ -1038,7 +1041,7 @@ static void
sbni_watchdog( unsigned long arg )
{
struct net_device *dev = (struct net_device *) arg;
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
struct timer_list *w = &nl->watchdog;
unsigned long flags;
unsigned char csr0;
@@ -1091,7 +1094,7 @@ static unsigned char timeout_rxl_tab[] = {
static void
card_start( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
nl->timer_ticks = CHANGE_LEVEL_START_TICKS;
nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
@@ -1113,7 +1116,7 @@ card_start( struct net_device *dev )
static void
change_level( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
if( nl->delta_rxl == 0 ) /* do not auto-negotiate RxL */
return;
@@ -1137,7 +1140,7 @@ change_level( struct net_device *dev )
static void
timeout_change_level( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
nl->cur_rxl_index = timeout_rxl_tab[ nl->timeout_rxl ];
if( ++nl->timeout_rxl >= 4 )
@@ -1160,7 +1163,7 @@ timeout_change_level( struct net_device *dev )
static int
sbni_open( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
struct timer_list *w = &nl->watchdog;
/*
@@ -1176,7 +1179,7 @@ sbni_open( struct net_device *dev )
|| (*p)->base_addr == dev->base_addr - 4)
&& (*p)->flags & IFF_UP ) {
- ((struct net_local *) ((*p)->priv))
+ ((struct net_local *) (netdev_priv(*p)))
->second = dev;
printk( KERN_NOTICE "%s: using shared irq "
"with %s\n", dev->name, (*p)->name );
@@ -1216,7 +1219,7 @@ handler_attached:
static int
sbni_close( struct net_device *dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
if( nl->second && nl->second->flags & IFF_UP ) {
printk( KERN_NOTICE "Secondary channel (%s) is active!\n",
@@ -1300,7 +1303,7 @@ sbni_card_probe( unsigned long ioaddr )
static int
sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd )
{
- struct net_local *nl = (struct net_local *) dev->priv;
+ struct net_local *nl = netdev_priv(dev);
struct sbni_flags flags;
int error = 0;
@@ -1390,8 +1393,8 @@ sbni_ioctl( struct net_device *dev, struct ifreq *ifr, int cmd )
static int
enslave( struct net_device *dev, struct net_device *slave_dev )
{
- struct net_local *nl = (struct net_local *) dev->priv;
- struct net_local *snl = (struct net_local *) slave_dev->priv;
+ struct net_local *nl = netdev_priv(dev);
+ struct net_local *snl = netdev_priv(slave_dev);
if( nl->state & FL_SLAVE ) /* This isn't master or free device */
return -EBUSY;
@@ -1425,9 +1428,9 @@ enslave( struct net_device *dev, struct net_device *slave_dev )
static int
emancipate( struct net_device *dev )
{
- struct net_local *snl = (struct net_local *) dev->priv;
+ struct net_local *snl = netdev_priv(dev);
struct net_device *p = snl->master;
- struct net_local *nl = (struct net_local *) p->priv;
+ struct net_local *nl = netdev_priv(p);
if( !(snl->state & FL_SLAVE) )
return -EINVAL;
@@ -1438,7 +1441,7 @@ emancipate( struct net_device *dev )
/* exclude from list */
for(;;) { /* must be in list */
- struct net_local *t = (struct net_local *) p->priv;
+ struct net_local *t = netdev_priv(p);
if( t->link == dev ) {
t->link = snl->link;
break;
@@ -1465,7 +1468,7 @@ emancipate( struct net_device *dev )
static struct net_device_stats *
sbni_get_stats( struct net_device *dev )
{
- return &((struct net_local *) dev->priv)->stats;
+ return &((struct net_local *)netdev_priv(dev))->stats;
}
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index 73e2f2780932..6a07ba9371db 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -185,7 +185,7 @@ static void sdla_stop(struct net_device *dev)
{
struct frad_local *flp;
- flp = dev->priv;
+ flp = netdev_priv(dev);
switch(flp->type)
{
case SDLA_S502A:
@@ -212,7 +212,7 @@ static void sdla_start(struct net_device *dev)
{
struct frad_local *flp;
- flp = dev->priv;
+ flp = netdev_priv(dev);
switch(flp->type)
{
case SDLA_S502A:
@@ -432,7 +432,7 @@ static int sdla_cmd(struct net_device *dev, int cmd, short dlci, short flags,
int ret, waiting, len;
long window;
- flp = dev->priv;
+ flp = netdev_priv(dev);
window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF;
cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK));
ret = 0;
@@ -509,7 +509,7 @@ static int sdla_activate(struct net_device *slave, struct net_device *master)
struct frad_local *flp;
int i;
- flp = slave->priv;
+ flp = netdev_priv(slave);
for(i=0;i<CONFIG_DLCI_MAX;i++)
if (flp->master[i] == master)
@@ -531,7 +531,7 @@ static int sdla_deactivate(struct net_device *slave, struct net_device *master)
struct frad_local *flp;
int i;
- flp = slave->priv;
+ flp = netdev_priv(slave);
for(i=0;i<CONFIG_DLCI_MAX;i++)
if (flp->master[i] == master)
@@ -556,7 +556,7 @@ static int sdla_assoc(struct net_device *slave, struct net_device *master)
if (master->type != ARPHRD_DLCI)
return(-EINVAL);
- flp = slave->priv;
+ flp = netdev_priv(slave);
for(i=0;i<CONFIG_DLCI_MAX;i++)
{
@@ -589,7 +589,7 @@ static int sdla_deassoc(struct net_device *slave, struct net_device *master)
struct frad_local *flp;
int i;
- flp = slave->priv;
+ flp = netdev_priv(slave);
for(i=0;i<CONFIG_DLCI_MAX;i++)
if (flp->master[i] == master)
@@ -619,7 +619,7 @@ static int sdla_dlci_conf(struct net_device *slave, struct net_device *master, i
int i;
short len, ret;
- flp = slave->priv;
+ flp = netdev_priv(slave);
for(i=0;i<CONFIG_DLCI_MAX;i++)
if (flp->master[i] == master)
@@ -628,7 +628,7 @@ static int sdla_dlci_conf(struct net_device *slave, struct net_device *master, i
if (i == CONFIG_DLCI_MAX)
return(-ENODEV);
- dlp = master->priv;
+ dlp = netdev_priv(master);
ret = SDLA_RET_OK;
len = sizeof(struct dlci_conf);
@@ -659,7 +659,7 @@ static int sdla_transmit(struct sk_buff *skb, struct net_device *dev)
unsigned long flags;
struct buf_entry *pbuf;
- flp = dev->priv;
+ flp = netdev_priv(dev);
ret = 0;
accept = 1;
@@ -755,7 +755,7 @@ static void sdla_receive(struct net_device *dev)
int i=0, received, success, addr, buf_base, buf_top;
short dlci, len, len2, split;
- flp = dev->priv;
+ flp = netdev_priv(dev);
success = 1;
received = addr = buf_top = buf_base = 0;
len = dlci = 0;
@@ -860,7 +860,7 @@ static void sdla_receive(struct net_device *dev)
if (success)
{
flp->stats.rx_packets++;
- dlp = master->priv;
+ dlp = netdev_priv(master);
(*dlp->receive)(skb, master);
}
@@ -925,7 +925,7 @@ static void sdla_poll(unsigned long device)
struct frad_local *flp;
dev = (struct net_device *) device;
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (sdla_byte(dev, SDLA_502_RCV_BUF))
sdla_receive(dev);
@@ -941,7 +941,7 @@ static int sdla_close(struct net_device *dev)
int len, i;
short dlcis[CONFIG_DLCI_MAX];
- flp = dev->priv;
+ flp = netdev_priv(dev);
len = 0;
for(i=0;i<CONFIG_DLCI_MAX;i++)
@@ -1002,7 +1002,7 @@ static int sdla_open(struct net_device *dev)
int len, i;
char byte;
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (!flp->initialized)
return(-EPERM);
@@ -1079,7 +1079,7 @@ static int sdla_open(struct net_device *dev)
for(i=0;i<CONFIG_DLCI_MAX;i++)
if (flp->dlci[i])
{
- dlp = flp->master[i]->priv;
+ dlp = netdev_priv(flp->master[i]);
if (dlp->configured)
sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, &dlp->config, sizeof(struct dlci_conf), NULL, NULL);
}
@@ -1099,7 +1099,7 @@ static int sdla_config(struct net_device *dev, struct frad_conf __user *conf, in
if (dev->type == 0xFFFF)
return(-EUNATCH);
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (!get)
{
@@ -1230,7 +1230,7 @@ static int sdla_reconfig(struct net_device *dev)
struct conf_data data;
int i, len;
- flp = dev->priv;
+ flp = netdev_priv(dev);
len = 0;
for(i=0;i<CONFIG_DLCI_MAX;i++)
@@ -1255,7 +1255,7 @@ static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if(!capable(CAP_NET_ADMIN))
return -EPERM;
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (!flp->initialized)
return(-EINVAL);
@@ -1321,7 +1321,7 @@ static int sdla_change_mtu(struct net_device *dev, int new_mtu)
{
struct frad_local *flp;
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (netif_running(dev))
return(-EBUSY);
@@ -1338,7 +1338,7 @@ static int sdla_set_config(struct net_device *dev, struct ifmap *map)
unsigned base;
int err = -EINVAL;
- flp = dev->priv;
+ flp = netdev_priv(dev);
if (flp->initialized)
return(-EINVAL);
@@ -1593,14 +1593,14 @@ fail:
static struct net_device_stats *sdla_stats(struct net_device *dev)
{
struct frad_local *flp;
- flp = dev->priv;
+ flp = netdev_priv(dev);
return(&flp->stats);
}
static void setup_sdla(struct net_device *dev)
{
- struct frad_local *flp = dev->priv;
+ struct frad_local *flp = netdev_priv(dev);
netdev_boot_setup_check(dev);
@@ -1651,7 +1651,7 @@ static int __init init_sdla(void)
static void __exit exit_sdla(void)
{
- struct frad_local *flp = sdla->priv;
+ struct frad_local *flp = netdev_priv(sdla);
unregister_netdev(sdla);
if (flp->initialized) {
diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c
index c0235844a4d5..0941a26f6e3f 100644
--- a/drivers/net/wan/sealevel.c
+++ b/drivers/net/wan/sealevel.c
@@ -68,7 +68,6 @@ static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb)
skb_reset_mac_header(skb);
skb->dev = c->netdevice;
netif_rx(skb);
- c->netdevice->last_rx = jiffies;
}
/*
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
deleted file mode 100644
index 6e92f7b44b1a..000000000000
--- a/drivers/net/wan/syncppp.c
+++ /dev/null
@@ -1,1480 +0,0 @@
-/*
- * NET3: A (fairly minimal) implementation of synchronous PPP for Linux
- * as well as a CISCO HDLC implementation. See the copyright
- * message below for the original source.
- *
- * This program is free software; you can redistribute 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 however. This code is also used in a different form by FreeBSD.
- * Therefore when making any non OS specific change please consider
- * contributing it back to the original author under the terms
- * below in addition.
- * -- Alan
- *
- * Port for Linux-2.1 by Jan "Yenya" Kasprzak <kas@fi.muni.cz>
- */
-
-/*
- * Synchronous PPP/Cisco link level subroutines.
- * Keepalive protocol implemented in both Cisco and PPP modes.
- *
- * Copyright (C) 1994 Cronyx Ltd.
- * Author: Serge Vakulenko, <vak@zebub.msk.su>
- *
- * This software is distributed with NO WARRANTIES, not even the implied
- * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Authors grant any other persons or organisations permission to use
- * or modify this software as long as this message is kept with the software,
- * all derivative works or modified versions.
- *
- * Version 1.9, Wed Oct 4 18:58:15 MSK 1995
- *
- * $Id: syncppp.c,v 1.18 2000/04/11 05:25:31 asj Exp $
- */
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/route.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/random.h>
-#include <linux/pkt_sched.h>
-#include <linux/spinlock.h>
-#include <linux/rcupdate.h>
-
-#include <net/net_namespace.h>
-#include <net/syncppp.h>
-
-#include <asm/byteorder.h>
-#include <asm/uaccess.h>
-
-#define MAXALIVECNT 6 /* max. alive packets */
-
-#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
-#define PPP_UI 0x03 /* Unnumbered Information */
-#define PPP_IP 0x0021 /* Internet Protocol */
-#define PPP_ISO 0x0023 /* ISO OSI Protocol */
-#define PPP_XNS 0x0025 /* Xerox NS Protocol */
-#define PPP_IPX 0x002b /* Novell IPX Protocol */
-#define PPP_LCP 0xc021 /* Link Control Protocol */
-#define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */
-
-#define LCP_CONF_REQ 1 /* PPP LCP configure request */
-#define LCP_CONF_ACK 2 /* PPP LCP configure acknowledge */
-#define LCP_CONF_NAK 3 /* PPP LCP configure negative ack */
-#define LCP_CONF_REJ 4 /* PPP LCP configure reject */
-#define LCP_TERM_REQ 5 /* PPP LCP terminate request */
-#define LCP_TERM_ACK 6 /* PPP LCP terminate acknowledge */
-#define LCP_CODE_REJ 7 /* PPP LCP code reject */
-#define LCP_PROTO_REJ 8 /* PPP LCP protocol reject */
-#define LCP_ECHO_REQ 9 /* PPP LCP echo request */
-#define LCP_ECHO_REPLY 10 /* PPP LCP echo reply */
-#define LCP_DISC_REQ 11 /* PPP LCP discard request */
-
-#define LCP_OPT_MRU 1 /* maximum receive unit */
-#define LCP_OPT_ASYNC_MAP 2 /* async control character map */
-#define LCP_OPT_AUTH_PROTO 3 /* authentication protocol */
-#define LCP_OPT_QUAL_PROTO 4 /* quality protocol */
-#define LCP_OPT_MAGIC 5 /* magic number */
-#define LCP_OPT_RESERVED 6 /* reserved */
-#define LCP_OPT_PROTO_COMP 7 /* protocol field compression */
-#define LCP_OPT_ADDR_COMP 8 /* address/control field compression */
-
-#define IPCP_CONF_REQ LCP_CONF_REQ /* PPP IPCP configure request */
-#define IPCP_CONF_ACK LCP_CONF_ACK /* PPP IPCP configure acknowledge */
-#define IPCP_CONF_NAK LCP_CONF_NAK /* PPP IPCP configure negative ack */
-#define IPCP_CONF_REJ LCP_CONF_REJ /* PPP IPCP configure reject */
-#define IPCP_TERM_REQ LCP_TERM_REQ /* PPP IPCP terminate request */
-#define IPCP_TERM_ACK LCP_TERM_ACK /* PPP IPCP terminate acknowledge */
-#define IPCP_CODE_REJ LCP_CODE_REJ /* PPP IPCP code reject */
-
-#define CISCO_MULTICAST 0x8f /* Cisco multicast address */
-#define CISCO_UNICAST 0x0f /* Cisco unicast address */
-#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
-#define CISCO_ADDR_REQ 0 /* Cisco address request */
-#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
-#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
-
-struct ppp_header {
- u8 address;
- u8 control;
- __be16 protocol;
-};
-#define PPP_HEADER_LEN sizeof (struct ppp_header)
-
-struct lcp_header {
- u8 type;
- u8 ident;
- __be16 len;
-};
-#define LCP_HEADER_LEN sizeof (struct lcp_header)
-
-struct cisco_packet {
- __be32 type;
- __be32 par1;
- __be32 par2;
- __be16 rel;
- __be16 time0;
- __be16 time1;
-};
-#define CISCO_PACKET_LEN 18
-#define CISCO_BIG_PACKET_LEN 20
-
-static struct sppp *spppq;
-static struct timer_list sppp_keepalive_timer;
-static DEFINE_SPINLOCK(spppq_lock);
-
-/* global xmit queue for sending packets while spinlock is held */
-static struct sk_buff_head tx_queue;
-
-static void sppp_keepalive (unsigned long dummy);
-static void sppp_cp_send (struct sppp *sp, u16 proto, u8 type,
- u8 ident, u16 len, void *data);
-static void sppp_cisco_send (struct sppp *sp, int type, u32 par1, u32 par2);
-static void sppp_lcp_input (struct sppp *sp, struct sk_buff *m);
-static void sppp_cisco_input (struct sppp *sp, struct sk_buff *m);
-static void sppp_ipcp_input (struct sppp *sp, struct sk_buff *m);
-static void sppp_lcp_open (struct sppp *sp);
-static void sppp_ipcp_open (struct sppp *sp);
-static int sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h,
- int len, u32 *magic);
-static void sppp_cp_timeout (unsigned long arg);
-static char *sppp_lcp_type_name (u8 type);
-static char *sppp_ipcp_type_name (u8 type);
-static void sppp_print_bytes (u8 *p, u16 len);
-
-static int debug;
-
-/* Flush global outgoing packet queue to dev_queue_xmit().
- *
- * dev_queue_xmit() must be called with interrupts enabled
- * which means it can't be called with spinlocks held.
- * If a packet needs to be sent while a spinlock is held,
- * then put the packet into tx_queue, and call sppp_flush_xmit()
- * after spinlock is released.
- */
-static void sppp_flush_xmit(void)
-{
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&tx_queue)) != NULL)
- dev_queue_xmit(skb);
-}
-
-/*
- * Interface down stub
- */
-
-static void if_down(struct net_device *dev)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
-
- sp->pp_link_state=SPPP_LINK_DOWN;
-}
-
-/*
- * Timeout routine activations.
- */
-
-static void sppp_set_timeout(struct sppp *p,int s)
-{
- if (! (p->pp_flags & PP_TIMO))
- {
- init_timer(&p->pp_timer);
- p->pp_timer.function=sppp_cp_timeout;
- p->pp_timer.expires=jiffies+s*HZ;
- p->pp_timer.data=(unsigned long)p;
- p->pp_flags |= PP_TIMO;
- add_timer(&p->pp_timer);
- }
-}
-
-static void sppp_clear_timeout(struct sppp *p)
-{
- if (p->pp_flags & PP_TIMO)
- {
- del_timer(&p->pp_timer);
- p->pp_flags &= ~PP_TIMO;
- }
-}
-
-/**
- * sppp_input - receive and process a WAN PPP frame
- * @skb: The buffer to process
- * @dev: The device it arrived on
- *
- * This can be called directly by cards that do not have
- * timing constraints but is normally called from the network layer
- * after interrupt servicing to process frames queued via netif_rx().
- *
- * We process the options in the card. If the frame is destined for
- * the protocol stacks then it requeues the frame for the upper level
- * protocol. If it is a control from it is processed and discarded
- * here.
- */
-
-static void sppp_input (struct net_device *dev, struct sk_buff *skb)
-{
- struct ppp_header *h;
- struct sppp *sp = (struct sppp *)sppp_of(dev);
- unsigned long flags;
-
- skb->dev=dev;
- skb_reset_mac_header(skb);
-
- if (!pskb_may_pull(skb, PPP_HEADER_LEN)) {
- /* Too small packet, drop it. */
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_DEBUG "%s: input packet is too small, %d bytes\n",
- dev->name, skb->len);
- kfree_skb(skb);
- return;
- }
-
- /* Get PPP header. */
- h = (struct ppp_header *)skb->data;
- skb_pull(skb,sizeof(struct ppp_header));
-
- spin_lock_irqsave(&sp->lock, flags);
-
- switch (h->address) {
- default: /* Invalid PPP packet. */
- goto invalid;
- case PPP_ALLSTATIONS:
- if (h->control != PPP_UI)
- goto invalid;
- if (sp->pp_flags & PP_CISCO) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: PPP packet in Cisco mode <0x%x 0x%x 0x%x>\n",
- dev->name,
- h->address, h->control, ntohs (h->protocol));
- goto drop;
- }
- switch (ntohs (h->protocol)) {
- default:
- if (sp->lcp.state == LCP_STATE_OPENED)
- sppp_cp_send (sp, PPP_LCP, LCP_PROTO_REJ,
- ++sp->pp_seq, skb->len + 2,
- &h->protocol);
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid input protocol <0x%x 0x%x 0x%x>\n",
- dev->name,
- h->address, h->control, ntohs (h->protocol));
- goto drop;
- case PPP_LCP:
- sppp_lcp_input (sp, skb);
- goto drop;
- case PPP_IPCP:
- if (sp->lcp.state == LCP_STATE_OPENED)
- sppp_ipcp_input (sp, skb);
- else
- printk(KERN_DEBUG "IPCP when still waiting LCP finish.\n");
- goto drop;
- case PPP_IP:
- if (sp->ipcp.state == IPCP_STATE_OPENED) {
- if(sp->pp_flags&PP_DEBUG)
- printk(KERN_DEBUG "Yow an IP frame.\n");
- skb->protocol=htons(ETH_P_IP);
- netif_rx(skb);
- dev->last_rx = jiffies;
- goto done;
- }
- break;
-#ifdef IPX
- case PPP_IPX:
- /* IPX IPXCP not implemented yet */
- if (sp->lcp.state == LCP_STATE_OPENED) {
- skb->protocol=htons(ETH_P_IPX);
- netif_rx(skb);
- dev->last_rx = jiffies;
- goto done;
- }
- break;
-#endif
- }
- break;
- case CISCO_MULTICAST:
- case CISCO_UNICAST:
- /* Don't check the control field here (RFC 1547). */
- if (! (sp->pp_flags & PP_CISCO)) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: Cisco packet in PPP mode <0x%x 0x%x 0x%x>\n",
- dev->name,
- h->address, h->control, ntohs (h->protocol));
- goto drop;
- }
- switch (ntohs (h->protocol)) {
- default:
- goto invalid;
- case CISCO_KEEPALIVE:
- sppp_cisco_input (sp, skb);
- goto drop;
-#ifdef CONFIG_INET
- case ETH_P_IP:
- skb->protocol=htons(ETH_P_IP);
- netif_rx(skb);
- dev->last_rx = jiffies;
- goto done;
-#endif
-#ifdef CONFIG_IPX
- case ETH_P_IPX:
- skb->protocol=htons(ETH_P_IPX);
- netif_rx(skb);
- dev->last_rx = jiffies;
- goto done;
-#endif
- }
- break;
- }
- goto drop;
-
-invalid:
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid input packet <0x%x 0x%x 0x%x>\n",
- dev->name, h->address, h->control, ntohs (h->protocol));
-drop:
- kfree_skb(skb);
-done:
- spin_unlock_irqrestore(&sp->lock, flags);
- sppp_flush_xmit();
- return;
-}
-
-/*
- * Handle transmit packets.
- */
-
-static int sppp_hard_header(struct sk_buff *skb,
- struct net_device *dev, __u16 type,
- const void *daddr, const void *saddr,
- unsigned int len)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
- struct ppp_header *h;
- skb_push(skb,sizeof(struct ppp_header));
- h=(struct ppp_header *)skb->data;
- if(sp->pp_flags&PP_CISCO)
- {
- h->address = CISCO_UNICAST;
- h->control = 0;
- }
- else
- {
- h->address = PPP_ALLSTATIONS;
- h->control = PPP_UI;
- }
- if(sp->pp_flags & PP_CISCO)
- {
- h->protocol = htons(type);
- }
- else switch(type)
- {
- case ETH_P_IP:
- h->protocol = htons(PPP_IP);
- break;
- case ETH_P_IPX:
- h->protocol = htons(PPP_IPX);
- break;
- }
- return sizeof(struct ppp_header);
-}
-
-static const struct header_ops sppp_header_ops = {
- .create = sppp_hard_header,
-};
-
-/*
- * Send keepalive packets, every 10 seconds.
- */
-
-static void sppp_keepalive (unsigned long dummy)
-{
- struct sppp *sp;
- unsigned long flags;
-
- spin_lock_irqsave(&spppq_lock, flags);
-
- for (sp=spppq; sp; sp=sp->pp_next)
- {
- struct net_device *dev = sp->pp_if;
-
- /* Keepalive mode disabled or channel down? */
- if (! (sp->pp_flags & PP_KEEPALIVE) ||
- ! (dev->flags & IFF_UP))
- continue;
-
- spin_lock(&sp->lock);
-
- /* No keepalive in PPP mode if LCP not opened yet. */
- if (! (sp->pp_flags & PP_CISCO) &&
- sp->lcp.state != LCP_STATE_OPENED) {
- spin_unlock(&sp->lock);
- continue;
- }
-
- if (sp->pp_alivecnt == MAXALIVECNT) {
- /* No keepalive packets got. Stop the interface. */
- printk (KERN_WARNING "%s: protocol down\n", dev->name);
- if_down (dev);
- if (! (sp->pp_flags & PP_CISCO)) {
- /* Shut down the PPP link. */
- sp->lcp.magic = jiffies;
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- sppp_clear_timeout (sp);
- /* Initiate negotiation. */
- sppp_lcp_open (sp);
- }
- }
- if (sp->pp_alivecnt <= MAXALIVECNT)
- ++sp->pp_alivecnt;
- if (sp->pp_flags & PP_CISCO)
- sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq,
- sp->pp_rseq);
- else if (sp->lcp.state == LCP_STATE_OPENED) {
- __be32 nmagic = htonl (sp->lcp.magic);
- sp->lcp.echoid = ++sp->pp_seq;
- sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REQ,
- sp->lcp.echoid, 4, &nmagic);
- }
-
- spin_unlock(&sp->lock);
- }
- spin_unlock_irqrestore(&spppq_lock, flags);
- sppp_flush_xmit();
- sppp_keepalive_timer.expires=jiffies+10*HZ;
- add_timer(&sppp_keepalive_timer);
-}
-
-/*
- * Handle incoming PPP Link Control Protocol packets.
- */
-
-static void sppp_lcp_input (struct sppp *sp, struct sk_buff *skb)
-{
- struct lcp_header *h;
- struct net_device *dev = sp->pp_if;
- int len = skb->len;
- u8 *p, opt[6];
- u32 rmagic = 0;
-
- if (!pskb_may_pull(skb, sizeof(struct lcp_header))) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid lcp packet length: %d bytes\n",
- dev->name, len);
- return;
- }
- h = (struct lcp_header *)skb->data;
- skb_pull(skb,sizeof(struct lcp_header *));
-
- if (sp->pp_flags & PP_DEBUG)
- {
- char state = '?';
- switch (sp->lcp.state) {
- case LCP_STATE_CLOSED: state = 'C'; break;
- case LCP_STATE_ACK_RCVD: state = 'R'; break;
- case LCP_STATE_ACK_SENT: state = 'S'; break;
- case LCP_STATE_OPENED: state = 'O'; break;
- }
- printk (KERN_WARNING "%s: lcp input(%c): %d bytes <%s id=%xh len=%xh",
- dev->name, state, len,
- sppp_lcp_type_name (h->type), h->ident, ntohs (h->len));
- if (len > 4)
- sppp_print_bytes ((u8*) (h+1), len-4);
- printk (">\n");
- }
- if (len > ntohs (h->len))
- len = ntohs (h->len);
- switch (h->type) {
- default:
- /* Unknown packet type -- send Code-Reject packet. */
- sppp_cp_send (sp, PPP_LCP, LCP_CODE_REJ, ++sp->pp_seq,
- skb->len, h);
- break;
- case LCP_CONF_REQ:
- if (len < 4) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_DEBUG"%s: invalid lcp configure request packet length: %d bytes\n",
- dev->name, len);
- break;
- }
- if (len>4 && !sppp_lcp_conf_parse_options (sp, h, len, &rmagic))
- goto badreq;
- if (rmagic == sp->lcp.magic) {
- /* Local and remote magics equal -- loopback? */
- if (sp->pp_loopcnt >= MAXALIVECNT*5) {
- printk (KERN_WARNING "%s: loopback\n",
- dev->name);
- sp->pp_loopcnt = 0;
- if (dev->flags & IFF_UP) {
- if_down (dev);
- }
- } else if (sp->pp_flags & PP_DEBUG)
- printk (KERN_DEBUG "%s: conf req: magic glitch\n",
- dev->name);
- ++sp->pp_loopcnt;
-
- /* MUST send Conf-Nack packet. */
- rmagic = ~sp->lcp.magic;
- opt[0] = LCP_OPT_MAGIC;
- opt[1] = sizeof (opt);
- opt[2] = rmagic >> 24;
- opt[3] = rmagic >> 16;
- opt[4] = rmagic >> 8;
- opt[5] = rmagic;
- sppp_cp_send (sp, PPP_LCP, LCP_CONF_NAK,
- h->ident, sizeof (opt), &opt);
-badreq:
- switch (sp->lcp.state) {
- case LCP_STATE_OPENED:
- /* Initiate renegotiation. */
- sppp_lcp_open (sp);
- /* fall through... */
- case LCP_STATE_ACK_SENT:
- /* Go to closed state. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- }
- break;
- }
- /* Send Configure-Ack packet. */
- sp->pp_loopcnt = 0;
- if (sp->lcp.state != LCP_STATE_OPENED) {
- sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK,
- h->ident, len-4, h+1);
- }
- /* Change the state. */
- switch (sp->lcp.state) {
- case LCP_STATE_CLOSED:
- sp->lcp.state = LCP_STATE_ACK_SENT;
- break;
- case LCP_STATE_ACK_RCVD:
- sp->lcp.state = LCP_STATE_OPENED;
- sppp_ipcp_open (sp);
- break;
- case LCP_STATE_OPENED:
- /* Remote magic changed -- close session. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- /* Initiate renegotiation. */
- sppp_lcp_open (sp);
- /* Send ACK after our REQ in attempt to break loop */
- sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK,
- h->ident, len-4, h+1);
- sp->lcp.state = LCP_STATE_ACK_SENT;
- break;
- }
- break;
- case LCP_CONF_ACK:
- if (h->ident != sp->lcp.confid)
- break;
- sppp_clear_timeout (sp);
- if ((sp->pp_link_state != SPPP_LINK_UP) &&
- (dev->flags & IFF_UP)) {
- /* Coming out of loopback mode. */
- sp->pp_link_state=SPPP_LINK_UP;
- printk (KERN_INFO "%s: protocol up\n", dev->name);
- }
- switch (sp->lcp.state) {
- case LCP_STATE_CLOSED:
- sp->lcp.state = LCP_STATE_ACK_RCVD;
- sppp_set_timeout (sp, 5);
- break;
- case LCP_STATE_ACK_SENT:
- sp->lcp.state = LCP_STATE_OPENED;
- sppp_ipcp_open (sp);
- break;
- }
- break;
- case LCP_CONF_NAK:
- if (h->ident != sp->lcp.confid)
- break;
- p = (u8*) (h+1);
- if (len>=10 && p[0] == LCP_OPT_MAGIC && p[1] >= 4) {
- rmagic = (u32)p[2] << 24 |
- (u32)p[3] << 16 | p[4] << 8 | p[5];
- if (rmagic == ~sp->lcp.magic) {
- int newmagic;
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_DEBUG "%s: conf nak: magic glitch\n",
- dev->name);
- get_random_bytes(&newmagic, sizeof(newmagic));
- sp->lcp.magic += newmagic;
- } else
- sp->lcp.magic = rmagic;
- }
- if (sp->lcp.state != LCP_STATE_ACK_SENT) {
- /* Go to closed state. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- }
- /* The link will be renegotiated after timeout,
- * to avoid endless req-nack loop. */
- sppp_clear_timeout (sp);
- sppp_set_timeout (sp, 2);
- break;
- case LCP_CONF_REJ:
- if (h->ident != sp->lcp.confid)
- break;
- sppp_clear_timeout (sp);
- /* Initiate renegotiation. */
- sppp_lcp_open (sp);
- if (sp->lcp.state != LCP_STATE_ACK_SENT) {
- /* Go to closed state. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- }
- break;
- case LCP_TERM_REQ:
- sppp_clear_timeout (sp);
- /* Send Terminate-Ack packet. */
- sppp_cp_send (sp, PPP_LCP, LCP_TERM_ACK, h->ident, 0, NULL);
- /* Go to closed state. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- /* Initiate renegotiation. */
- sppp_lcp_open (sp);
- break;
- case LCP_TERM_ACK:
- case LCP_CODE_REJ:
- case LCP_PROTO_REJ:
- /* Ignore for now. */
- break;
- case LCP_DISC_REQ:
- /* Discard the packet. */
- break;
- case LCP_ECHO_REQ:
- if (sp->lcp.state != LCP_STATE_OPENED)
- break;
- if (len < 8) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid lcp echo request packet length: %d bytes\n",
- dev->name, len);
- break;
- }
- if (ntohl (*(__be32*)(h+1)) == sp->lcp.magic) {
- /* Line loopback mode detected. */
- printk (KERN_WARNING "%s: loopback\n", dev->name);
- if_down (dev);
-
- /* Shut down the PPP link. */
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- sppp_clear_timeout (sp);
- /* Initiate negotiation. */
- sppp_lcp_open (sp);
- break;
- }
- *(__be32 *)(h+1) = htonl (sp->lcp.magic);
- sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REPLY, h->ident, len-4, h+1);
- break;
- case LCP_ECHO_REPLY:
- if (h->ident != sp->lcp.echoid)
- break;
- if (len < 8) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid lcp echo reply packet length: %d bytes\n",
- dev->name, len);
- break;
- }
- if (ntohl(*(__be32 *)(h+1)) != sp->lcp.magic)
- sp->pp_alivecnt = 0;
- break;
- }
-}
-
-/*
- * Handle incoming Cisco keepalive protocol packets.
- */
-
-static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
-{
- struct cisco_packet *h;
- struct net_device *dev = sp->pp_if;
-
- if (!pskb_may_pull(skb, sizeof(struct cisco_packet))
- || (skb->len != CISCO_PACKET_LEN
- && skb->len != CISCO_BIG_PACKET_LEN)) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid cisco packet length: %d bytes\n",
- dev->name, skb->len);
- return;
- }
- h = (struct cisco_packet *)skb->data;
- skb_pull(skb, sizeof(struct cisco_packet*));
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: cisco input: %d bytes <%xh %xh %xh %xh %xh-%xh>\n",
- dev->name, skb->len,
- ntohl (h->type), h->par1, h->par2, h->rel,
- h->time0, h->time1);
- switch (ntohl (h->type)) {
- default:
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: unknown cisco packet type: 0x%x\n",
- dev->name, ntohl (h->type));
- break;
- case CISCO_ADDR_REPLY:
- /* Reply on address request, ignore */
- break;
- case CISCO_KEEPALIVE_REQ:
- sp->pp_alivecnt = 0;
- sp->pp_rseq = ntohl (h->par1);
- if (sp->pp_seq == sp->pp_rseq) {
- /* Local and remote sequence numbers are equal.
- * Probably, the line is in loopback mode. */
- int newseq;
- if (sp->pp_loopcnt >= MAXALIVECNT) {
- printk (KERN_WARNING "%s: loopback\n",
- dev->name);
- sp->pp_loopcnt = 0;
- if (dev->flags & IFF_UP) {
- if_down (dev);
- }
- }
- ++sp->pp_loopcnt;
-
- /* Generate new local sequence number */
- get_random_bytes(&newseq, sizeof(newseq));
- sp->pp_seq ^= newseq;
- break;
- }
- sp->pp_loopcnt = 0;
- if (sp->pp_link_state==SPPP_LINK_DOWN &&
- (dev->flags & IFF_UP)) {
- sp->pp_link_state=SPPP_LINK_UP;
- printk (KERN_INFO "%s: protocol up\n", dev->name);
- }
- break;
- case CISCO_ADDR_REQ:
- /* Stolen from net/ipv4/devinet.c -- SIOCGIFADDR ioctl */
- {
- __be32 addr = 0, mask = htonl(~0U); /* FIXME: is the mask correct? */
-#ifdef CONFIG_INET
- struct in_device *in_dev;
- struct in_ifaddr *ifa;
-
- rcu_read_lock();
- if ((in_dev = __in_dev_get_rcu(dev)) != NULL)
- {
- for (ifa=in_dev->ifa_list; ifa != NULL;
- ifa=ifa->ifa_next) {
- if (strcmp(dev->name, ifa->ifa_label) == 0)
- {
- addr = ifa->ifa_local;
- mask = ifa->ifa_mask;
- break;
- }
- }
- }
- rcu_read_unlock();
-#endif
- sppp_cisco_send (sp, CISCO_ADDR_REPLY, ntohl(addr), ntohl(mask));
- break;
- }
- }
-}
-
-
-/*
- * Send PPP LCP packet.
- */
-
-static void sppp_cp_send (struct sppp *sp, u16 proto, u8 type,
- u8 ident, u16 len, void *data)
-{
- struct ppp_header *h;
- struct lcp_header *lh;
- struct sk_buff *skb;
- struct net_device *dev = sp->pp_if;
-
- skb=alloc_skb(dev->hard_header_len+PPP_HEADER_LEN+LCP_HEADER_LEN+len,
- GFP_ATOMIC);
- if (skb==NULL)
- return;
-
- skb_reserve(skb,dev->hard_header_len);
-
- h = (struct ppp_header *)skb_put(skb, sizeof(struct ppp_header));
- h->address = PPP_ALLSTATIONS; /* broadcast address */
- h->control = PPP_UI; /* Unnumbered Info */
- h->protocol = htons (proto); /* Link Control Protocol */
-
- lh = (struct lcp_header *)skb_put(skb, sizeof(struct lcp_header));
- lh->type = type;
- lh->ident = ident;
- lh->len = htons (LCP_HEADER_LEN + len);
-
- if (len)
- memcpy(skb_put(skb,len),data, len);
-
- if (sp->pp_flags & PP_DEBUG) {
- printk (KERN_WARNING "%s: %s output <%s id=%xh len=%xh",
- dev->name,
- proto==PPP_LCP ? "lcp" : "ipcp",
- proto==PPP_LCP ? sppp_lcp_type_name (lh->type) :
- sppp_ipcp_type_name (lh->type), lh->ident,
- ntohs (lh->len));
- if (len)
- sppp_print_bytes ((u8*) (lh+1), len);
- printk (">\n");
- }
- /* Control is high priority so it doesn't get queued behind data */
- skb->priority=TC_PRIO_CONTROL;
- skb->dev = dev;
- skb_queue_tail(&tx_queue, skb);
-}
-
-/*
- * Send Cisco keepalive packet.
- */
-
-static void sppp_cisco_send (struct sppp *sp, int type, u32 par1, u32 par2)
-{
- struct ppp_header *h;
- struct cisco_packet *ch;
- struct sk_buff *skb;
- struct net_device *dev = sp->pp_if;
- u32 t = jiffies * 1000/HZ;
-
- skb=alloc_skb(dev->hard_header_len+PPP_HEADER_LEN+CISCO_PACKET_LEN,
- GFP_ATOMIC);
-
- if(skb==NULL)
- return;
-
- skb_reserve(skb, dev->hard_header_len);
- h = (struct ppp_header *)skb_put (skb, sizeof(struct ppp_header));
- h->address = CISCO_MULTICAST;
- h->control = 0;
- h->protocol = htons (CISCO_KEEPALIVE);
-
- ch = (struct cisco_packet*)skb_put(skb, CISCO_PACKET_LEN);
- ch->type = htonl (type);
- ch->par1 = htonl (par1);
- ch->par2 = htonl (par2);
- ch->rel = htons(0xffff);
- ch->time0 = htons ((u16) (t >> 16));
- ch->time1 = htons ((u16) t);
-
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: cisco output: <%xh %xh %xh %xh %xh-%xh>\n",
- dev->name, ntohl (ch->type), ch->par1,
- ch->par2, ch->rel, ch->time0, ch->time1);
- skb->priority=TC_PRIO_CONTROL;
- skb->dev = dev;
- skb_queue_tail(&tx_queue, skb);
-}
-
-/**
- * sppp_close - close down a synchronous PPP or Cisco HDLC link
- * @dev: The network device to drop the link of
- *
- * This drops the logical interface to the channel. It is not
- * done politely as we assume we will also be dropping DTR. Any
- * timeouts are killed.
- */
-
-int sppp_close (struct net_device *dev)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&sp->lock, flags);
- sp->pp_link_state = SPPP_LINK_DOWN;
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- sppp_clear_timeout (sp);
- spin_unlock_irqrestore(&sp->lock, flags);
-
- return 0;
-}
-
-EXPORT_SYMBOL(sppp_close);
-
-/**
- * sppp_open - open a synchronous PPP or Cisco HDLC link
- * @dev: Network device to activate
- *
- * Close down any existing synchronous session and commence
- * from scratch. In the PPP case this means negotiating LCP/IPCP
- * and friends, while for Cisco HDLC we simply need to start sending
- * keepalives
- */
-
-int sppp_open (struct net_device *dev)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
- unsigned long flags;
-
- sppp_close(dev);
-
- spin_lock_irqsave(&sp->lock, flags);
- if (!(sp->pp_flags & PP_CISCO)) {
- sppp_lcp_open (sp);
- }
- sp->pp_link_state = SPPP_LINK_DOWN;
- spin_unlock_irqrestore(&sp->lock, flags);
- sppp_flush_xmit();
-
- return 0;
-}
-
-EXPORT_SYMBOL(sppp_open);
-
-/**
- * sppp_reopen - notify of physical link loss
- * @dev: Device that lost the link
- *
- * This function informs the synchronous protocol code that
- * the underlying link died (for example a carrier drop on X.21)
- *
- * We increment the magic numbers to ensure that if the other end
- * failed to notice we will correctly start a new session. It happens
- * do to the nature of telco circuits is that you can lose carrier on
- * one endonly.
- *
- * Having done this we go back to negotiating. This function may
- * be called from an interrupt context.
- */
-
-int sppp_reopen (struct net_device *dev)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
- unsigned long flags;
-
- sppp_close(dev);
-
- spin_lock_irqsave(&sp->lock, flags);
- if (!(sp->pp_flags & PP_CISCO))
- {
- sp->lcp.magic = jiffies;
- ++sp->pp_seq;
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- /* Give it a moment for the line to settle then go */
- sppp_set_timeout (sp, 1);
- }
- sp->pp_link_state=SPPP_LINK_DOWN;
- spin_unlock_irqrestore(&sp->lock, flags);
-
- return 0;
-}
-
-EXPORT_SYMBOL(sppp_reopen);
-
-/**
- * sppp_change_mtu - Change the link MTU
- * @dev: Device to change MTU on
- * @new_mtu: New MTU
- *
- * Change the MTU on the link. This can only be called with
- * the link down. It returns an error if the link is up or
- * the mtu is out of range.
- */
-
-static int sppp_change_mtu(struct net_device *dev, int new_mtu)
-{
- if(new_mtu<128||new_mtu>PPP_MTU||(dev->flags&IFF_UP))
- return -EINVAL;
- dev->mtu=new_mtu;
- return 0;
-}
-
-/**
- * sppp_do_ioctl - Ioctl handler for ppp/hdlc
- * @dev: Device subject to ioctl
- * @ifr: Interface request block from the user
- * @cmd: Command that is being issued
- *
- * This function handles the ioctls that may be issued by the user
- * to control the settings of a PPP/HDLC link. It does both busy
- * and security checks. This function is intended to be wrapped by
- * callers who wish to add additional ioctl calls of their own.
- */
-
-int sppp_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct sppp *sp = (struct sppp *)sppp_of(dev);
-
- if(dev->flags&IFF_UP)
- return -EBUSY;
-
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- switch(cmd)
- {
- case SPPPIOCCISCO:
- sp->pp_flags|=PP_CISCO;
- dev->type = ARPHRD_HDLC;
- break;
- case SPPPIOCPPP:
- sp->pp_flags&=~PP_CISCO;
- dev->type = ARPHRD_PPP;
- break;
- case SPPPIOCDEBUG:
- sp->pp_flags&=~PP_DEBUG;
- if(ifr->ifr_flags)
- sp->pp_flags|=PP_DEBUG;
- break;
- case SPPPIOCGFLAGS:
- if(copy_to_user(ifr->ifr_data, &sp->pp_flags, sizeof(sp->pp_flags)))
- return -EFAULT;
- break;
- case SPPPIOCSFLAGS:
- if(copy_from_user(&sp->pp_flags, ifr->ifr_data, sizeof(sp->pp_flags)))
- return -EFAULT;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-EXPORT_SYMBOL(sppp_do_ioctl);
-
-/**
- * sppp_attach - attach synchronous PPP/HDLC to a device
- * @pd: PPP device to initialise
- *
- * This initialises the PPP/HDLC support on an interface. At the
- * time of calling the dev element must point to the network device
- * that this interface is attached to. The interface should not yet
- * be registered.
- */
-
-void sppp_attach(struct ppp_device *pd)
-{
- struct net_device *dev = pd->dev;
- struct sppp *sp = &pd->sppp;
- unsigned long flags;
-
- /* Make sure embedding is safe for sppp_of */
- BUG_ON(sppp_of(dev) != sp);
-
- spin_lock_irqsave(&spppq_lock, flags);
- /* Initialize keepalive handler. */
- if (! spppq)
- {
- init_timer(&sppp_keepalive_timer);
- sppp_keepalive_timer.expires=jiffies+10*HZ;
- sppp_keepalive_timer.function=sppp_keepalive;
- add_timer(&sppp_keepalive_timer);
- }
- /* Insert new entry into the keepalive list. */
- sp->pp_next = spppq;
- spppq = sp;
- spin_unlock_irqrestore(&spppq_lock, flags);
-
- sp->pp_loopcnt = 0;
- sp->pp_alivecnt = 0;
- sp->pp_seq = 0;
- sp->pp_rseq = 0;
- sp->pp_flags = PP_KEEPALIVE|PP_CISCO|debug;/*PP_DEBUG;*/
- sp->lcp.magic = 0;
- sp->lcp.state = LCP_STATE_CLOSED;
- sp->ipcp.state = IPCP_STATE_CLOSED;
- sp->pp_if = dev;
- spin_lock_init(&sp->lock);
-
- /*
- * Device specific setup. All but interrupt handler and
- * hard_start_xmit.
- */
-
- dev->header_ops = &sppp_header_ops;
-
- dev->tx_queue_len = 10;
- dev->type = ARPHRD_HDLC;
- dev->addr_len = 0;
- dev->hard_header_len = sizeof(struct ppp_header);
- dev->mtu = PPP_MTU;
- /*
- * These 4 are callers but MUST also call sppp_ functions
- */
- dev->do_ioctl = sppp_do_ioctl;
-#if 0
- dev->get_stats = NULL; /* Let the driver override these */
- dev->open = sppp_open;
- dev->stop = sppp_close;
-#endif
- dev->change_mtu = sppp_change_mtu;
- dev->flags = IFF_MULTICAST|IFF_POINTOPOINT|IFF_NOARP;
-}
-
-EXPORT_SYMBOL(sppp_attach);
-
-/**
- * sppp_detach - release PPP resources from a device
- * @dev: Network device to release
- *
- * Stop and free up any PPP/HDLC resources used by this
- * interface. This must be called before the device is
- * freed.
- */
-
-void sppp_detach (struct net_device *dev)
-{
- struct sppp **q, *p, *sp = (struct sppp *)sppp_of(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&spppq_lock, flags);
- /* Remove the entry from the keepalive list. */
- for (q = &spppq; (p = *q); q = &p->pp_next)
- if (p == sp) {
- *q = p->pp_next;
- break;
- }
-
- /* Stop keepalive handler. */
- if (! spppq)
- del_timer(&sppp_keepalive_timer);
- sppp_clear_timeout (sp);
- spin_unlock_irqrestore(&spppq_lock, flags);
-}
-
-EXPORT_SYMBOL(sppp_detach);
-
-/*
- * Analyze the LCP Configure-Request options list
- * for the presence of unknown options.
- * If the request contains unknown options, build and
- * send Configure-reject packet, containing only unknown options.
- */
-static int
-sppp_lcp_conf_parse_options (struct sppp *sp, struct lcp_header *h,
- int len, u32 *magic)
-{
- u8 *buf, *r, *p;
- int rlen;
-
- len -= 4;
- buf = r = kmalloc (len, GFP_ATOMIC);
- if (! buf)
- return (0);
-
- p = (void*) (h+1);
- for (rlen=0; len>1 && p[1]; len-=p[1], p+=p[1]) {
- switch (*p) {
- case LCP_OPT_MAGIC:
- /* Magic number -- extract. */
- if (len >= 6 && p[1] == 6) {
- *magic = (u32)p[2] << 24 |
- (u32)p[3] << 16 | p[4] << 8 | p[5];
- continue;
- }
- break;
- case LCP_OPT_ASYNC_MAP:
- /* Async control character map -- check to be zero. */
- if (len >= 6 && p[1] == 6 && ! p[2] && ! p[3] &&
- ! p[4] && ! p[5])
- continue;
- break;
- case LCP_OPT_MRU:
- /* Maximum receive unit -- always OK. */
- continue;
- default:
- /* Others not supported. */
- break;
- }
- /* Add the option to rejected list. */
- memcpy(r, p, p[1]);
- r += p[1];
- rlen += p[1];
- }
- if (rlen)
- sppp_cp_send (sp, PPP_LCP, LCP_CONF_REJ, h->ident, rlen, buf);
- kfree(buf);
- return (rlen == 0);
-}
-
-static void sppp_ipcp_input (struct sppp *sp, struct sk_buff *skb)
-{
- struct lcp_header *h;
- struct net_device *dev = sp->pp_if;
- int len = skb->len;
-
- if (!pskb_may_pull(skb, sizeof(struct lcp_header))) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid ipcp packet length: %d bytes\n",
- dev->name, len);
- return;
- }
- h = (struct lcp_header *)skb->data;
- skb_pull(skb,sizeof(struct lcp_header));
- if (sp->pp_flags & PP_DEBUG) {
- printk (KERN_WARNING "%s: ipcp input: %d bytes <%s id=%xh len=%xh",
- dev->name, len,
- sppp_ipcp_type_name (h->type), h->ident, ntohs (h->len));
- if (len > 4)
- sppp_print_bytes ((u8*) (h+1), len-4);
- printk (">\n");
- }
- if (len > ntohs (h->len))
- len = ntohs (h->len);
- switch (h->type) {
- default:
- /* Unknown packet type -- send Code-Reject packet. */
- sppp_cp_send (sp, PPP_IPCP, IPCP_CODE_REJ, ++sp->pp_seq, len, h);
- break;
- case IPCP_CONF_REQ:
- if (len < 4) {
- if (sp->pp_flags & PP_DEBUG)
- printk (KERN_WARNING "%s: invalid ipcp configure request packet length: %d bytes\n",
- dev->name, len);
- return;
- }
- if (len > 4) {
- sppp_cp_send (sp, PPP_IPCP, LCP_CONF_REJ, h->ident,
- len-4, h+1);
-
- switch (sp->ipcp.state) {
- case IPCP_STATE_OPENED:
- /* Initiate renegotiation. */
- sppp_ipcp_open (sp);
- /* fall through... */
- case IPCP_STATE_ACK_SENT:
- /* Go to closed state. */
- sp->ipcp.state = IPCP_STATE_CLOSED;
- }
- } else {
- /* Send Configure-Ack packet. */
- sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_ACK, h->ident,
- 0, NULL);
- /* Change the state. */
- if (sp->ipcp.state == IPCP_STATE_ACK_RCVD)
- sp->ipcp.state = IPCP_STATE_OPENED;
- else
- sp->ipcp.state = IPCP_STATE_ACK_SENT;
- }
- break;
- case IPCP_CONF_ACK:
- if (h->ident != sp->ipcp.confid)
- break;
- sppp_clear_timeout (sp);
- switch (sp->ipcp.state) {
- case IPCP_STATE_CLOSED:
- sp->ipcp.state = IPCP_STATE_ACK_RCVD;
- sppp_set_timeout (sp, 5);
- break;
- case IPCP_STATE_ACK_SENT:
- sp->ipcp.state = IPCP_STATE_OPENED;
- break;
- }
- break;
- case IPCP_CONF_NAK:
- case IPCP_CONF_REJ:
- if (h->ident != sp->ipcp.confid)
- break;
- sppp_clear_timeout (sp);
- /* Initiate renegotiation. */
- sppp_ipcp_open (sp);
- if (sp->ipcp.state != IPCP_STATE_ACK_SENT)
- /* Go to closed state. */
- sp->ipcp.state = IPCP_STATE_CLOSED;
- break;
- case IPCP_TERM_REQ:
- /* Send Terminate-Ack packet. */
- sppp_cp_send (sp, PPP_IPCP, IPCP_TERM_ACK, h->ident, 0, NULL);
- /* Go to closed state. */
- sp->ipcp.state = IPCP_STATE_CLOSED;
- /* Initiate renegotiation. */
- sppp_ipcp_open (sp);
- break;
- case IPCP_TERM_ACK:
- /* Ignore for now. */
- case IPCP_CODE_REJ:
- /* Ignore for now. */
- break;
- }
-}
-
-static void sppp_lcp_open (struct sppp *sp)
-{
- char opt[6];
-
- if (! sp->lcp.magic)
- sp->lcp.magic = jiffies;
- opt[0] = LCP_OPT_MAGIC;
- opt[1] = sizeof (opt);
- opt[2] = sp->lcp.magic >> 24;
- opt[3] = sp->lcp.magic >> 16;
- opt[4] = sp->lcp.magic >> 8;
- opt[5] = sp->lcp.magic;
- sp->lcp.confid = ++sp->pp_seq;
- sppp_cp_send (sp, PPP_LCP, LCP_CONF_REQ, sp->lcp.confid,
- sizeof (opt), &opt);
- sppp_set_timeout (sp, 2);
-}
-
-static void sppp_ipcp_open (struct sppp *sp)
-{
- sp->ipcp.confid = ++sp->pp_seq;
- sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_REQ, sp->ipcp.confid, 0, NULL);
- sppp_set_timeout (sp, 2);
-}
-
-/*
- * Process PPP control protocol timeouts.
- */
-
-static void sppp_cp_timeout (unsigned long arg)
-{
- struct sppp *sp = (struct sppp*) arg;
- unsigned long flags;
-
- spin_lock_irqsave(&sp->lock, flags);
-
- sp->pp_flags &= ~PP_TIMO;
- if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
- spin_unlock_irqrestore(&sp->lock, flags);
- return;
- }
- switch (sp->lcp.state) {
- case LCP_STATE_CLOSED:
- /* No ACK for Configure-Request, retry. */
- sppp_lcp_open (sp);
- break;
- case LCP_STATE_ACK_RCVD:
- /* ACK got, but no Configure-Request for peer, retry. */
- sppp_lcp_open (sp);
- sp->lcp.state = LCP_STATE_CLOSED;
- break;
- case LCP_STATE_ACK_SENT:
- /* ACK sent but no ACK for Configure-Request, retry. */
- sppp_lcp_open (sp);
- break;
- case LCP_STATE_OPENED:
- /* LCP is already OK, try IPCP. */
- switch (sp->ipcp.state) {
- case IPCP_STATE_CLOSED:
- /* No ACK for Configure-Request, retry. */
- sppp_ipcp_open (sp);
- break;
- case IPCP_STATE_ACK_RCVD:
- /* ACK got, but no Configure-Request for peer, retry. */
- sppp_ipcp_open (sp);
- sp->ipcp.state = IPCP_STATE_CLOSED;
- break;
- case IPCP_STATE_ACK_SENT:
- /* ACK sent but no ACK for Configure-Request, retry. */
- sppp_ipcp_open (sp);
- break;
- case IPCP_STATE_OPENED:
- /* IPCP is OK. */
- break;
- }
- break;
- }
- spin_unlock_irqrestore(&sp->lock, flags);
- sppp_flush_xmit();
-}
-
-static char *sppp_lcp_type_name (u8 type)
-{
- static char buf [8];
- switch (type) {
- case LCP_CONF_REQ: return ("conf-req");
- case LCP_CONF_ACK: return ("conf-ack");
- case LCP_CONF_NAK: return ("conf-nack");
- case LCP_CONF_REJ: return ("conf-rej");
- case LCP_TERM_REQ: return ("term-req");
- case LCP_TERM_ACK: return ("term-ack");
- case LCP_CODE_REJ: return ("code-rej");
- case LCP_PROTO_REJ: return ("proto-rej");
- case LCP_ECHO_REQ: return ("echo-req");
- case LCP_ECHO_REPLY: return ("echo-reply");
- case LCP_DISC_REQ: return ("discard-req");
- }
- sprintf (buf, "%xh", type);
- return (buf);
-}
-
-static char *sppp_ipcp_type_name (u8 type)
-{
- static char buf [8];
- switch (type) {
- case IPCP_CONF_REQ: return ("conf-req");
- case IPCP_CONF_ACK: return ("conf-ack");
- case IPCP_CONF_NAK: return ("conf-nack");
- case IPCP_CONF_REJ: return ("conf-rej");
- case IPCP_TERM_REQ: return ("term-req");
- case IPCP_TERM_ACK: return ("term-ack");
- case IPCP_CODE_REJ: return ("code-rej");
- }
- sprintf (buf, "%xh", type);
- return (buf);
-}
-
-static void sppp_print_bytes (u_char *p, u16 len)
-{
- printk (" %x", *p++);
- while (--len > 0)
- printk ("-%x", *p++);
-}
-
-/**
- * sppp_rcv - receive and process a WAN PPP frame
- * @skb: The buffer to process
- * @dev: The device it arrived on
- * @p: Unused
- * @orig_dev: Unused
- *
- * Protocol glue. This drives the deferred processing mode the poorer
- * cards use. This can be called directly by cards that do not have
- * timing constraints but is normally called from the network layer
- * after interrupt servicing to process frames queued via netif_rx.
- */
-
-static int sppp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *p, struct net_device *orig_dev)
-{
- if (dev_net(dev) != &init_net) {
- kfree_skb(skb);
- return 0;
- }
-
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
- return NET_RX_DROP;
- sppp_input(dev,skb);
- return 0;
-}
-
-static struct packet_type sppp_packet_type = {
- .type = __constant_htons(ETH_P_WAN_PPP),
- .func = sppp_rcv,
-};
-
-static char banner[] __initdata =
- KERN_INFO "Cronyx Ltd, Synchronous PPP and CISCO HDLC (c) 1994\n"
- KERN_INFO "Linux port (c) 1998 Building Number Three Ltd & "
- "Jan \"Yenya\" Kasprzak.\n";
-
-static int __init sync_ppp_init(void)
-{
- if(debug)
- debug=PP_DEBUG;
- printk(banner);
- skb_queue_head_init(&tx_queue);
- dev_add_pack(&sppp_packet_type);
- return 0;
-}
-
-
-static void __exit sync_ppp_cleanup(void)
-{
- dev_remove_pack(&sppp_packet_type);
-}
-
-module_init(sync_ppp_init);
-module_exit(sync_ppp_cleanup);
-module_param(debug, int, 0);
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index a8a5ca0ee6c2..4bffb67ebcae 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -220,7 +220,6 @@ static inline void wanxl_rx_intr(card_t *card)
#endif
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
- dev->last_rx = jiffies;
skb->protocol = hdlc_type_trans(skb, dev);
netif_rx(skb);
skb = NULL;
@@ -411,12 +410,12 @@ static int wanxl_open(struct net_device *dev)
writel(1 << (DOORBELL_TO_CARD_OPEN_0 + port->node), dbr);
timeout = jiffies + HZ;
- do
+ do {
if (get_status(port)->open) {
netif_start_queue(dev);
return 0;
}
- while (time_after(timeout, jiffies));
+ } while (time_after(timeout, jiffies));
printk(KERN_ERR "%s: unable to open port\n", dev->name);
/* ask the card to close the port, should it be still alive */
@@ -438,10 +437,10 @@ static int wanxl_close(struct net_device *dev)
port->card->plx + PLX_DOORBELL_TO_CARD);
timeout = jiffies + HZ;
- do
+ do {
if (!get_status(port)->open)
break;
- while (time_after(timeout, jiffies));
+ } while (time_after(timeout, jiffies));
if (get_status(port)->open)
printk(KERN_ERR "%s: unable to close port\n", dev->name);
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 2a6c7a60756f..e6e2ce3e7bcf 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -64,7 +64,7 @@ static struct x25_asy *x25_asy_alloc(void)
if (dev == NULL)
break;
- sl = dev->priv;
+ sl = netdev_priv(dev);
/* Not in use ? */
if (!test_and_set_bit(SLF_INUSE, &sl->flags))
return sl;
@@ -86,7 +86,7 @@ static struct x25_asy *x25_asy_alloc(void)
return NULL;
/* Initialize channel control data */
- sl = dev->priv;
+ sl = netdev_priv(dev);
dev->base_addr = i;
/* register device so that it can be ifconfig'ed */
@@ -120,7 +120,7 @@ static void x25_asy_free(struct x25_asy *sl)
static int x25_asy_change_mtu(struct net_device *dev, int newmtu)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
unsigned char *xbuff, *rbuff;
int len = 2 * newmtu;
@@ -211,7 +211,6 @@ static void x25_asy_bump(struct x25_asy *sl)
printk(KERN_DEBUG "x25_asy: data received err - %d\n", err);
} else {
netif_rx(skb);
- sl->dev->last_rx = jiffies;
sl->stats.rx_packets++;
}
}
@@ -243,7 +242,7 @@ static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len)
* if we did not request it before write operation.
* 14 Oct 1994 Dmitry Gorodchanin.
*/
- sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
actual = sl->tty->ops->write(sl->tty, sl->xbuff, count);
sl->xleft = count - actual;
sl->xhead = sl->xbuff + actual;
@@ -258,7 +257,7 @@ static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len)
static void x25_asy_write_wakeup(struct tty_struct *tty)
{
int actual;
- struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
+ struct x25_asy *sl = tty->disc_data;
/* First make sure we're connected. */
if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev))
@@ -268,7 +267,7 @@ static void x25_asy_write_wakeup(struct tty_struct *tty)
/* Now serial buffer is almost free & we can start
* transmission of another packet */
sl->stats.tx_packets++;
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
x25_asy_unlock(sl);
return;
}
@@ -280,7 +279,7 @@ static void x25_asy_write_wakeup(struct tty_struct *tty)
static void x25_asy_timeout(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
spin_lock(&sl->lock);
if (netif_queue_stopped(dev)) {
@@ -291,7 +290,7 @@ static void x25_asy_timeout(struct net_device *dev)
(tty_chars_in_buffer(sl->tty) || sl->xleft) ?
"bad line quality" : "driver error");
sl->xleft = 0;
- sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
x25_asy_unlock(sl);
}
spin_unlock(&sl->lock);
@@ -301,7 +300,7 @@ static void x25_asy_timeout(struct net_device *dev)
static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
int err;
if (!netif_running(sl->dev)) {
@@ -361,7 +360,6 @@ static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev)
static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb)
{
- skb->dev->last_rx = jiffies;
return netif_rx(skb);
}
@@ -373,7 +371,7 @@ static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb)
static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
spin_lock(&sl->lock);
if (netif_queue_stopped(sl->dev) || sl->tty == NULL) {
@@ -398,7 +396,7 @@ static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb)
static void x25_asy_connected(struct net_device *dev, int reason)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
struct sk_buff *skb;
unsigned char *ptr;
@@ -413,12 +411,11 @@ static void x25_asy_connected(struct net_device *dev, int reason)
skb->protocol = x25_type_trans(skb, sl->dev);
netif_rx(skb);
- sl->dev->last_rx = jiffies;
}
static void x25_asy_disconnected(struct net_device *dev, int reason)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
struct sk_buff *skb;
unsigned char *ptr;
@@ -433,7 +430,6 @@ static void x25_asy_disconnected(struct net_device *dev, int reason)
skb->protocol = x25_type_trans(skb, sl->dev);
netif_rx(skb);
- sl->dev->last_rx = jiffies;
}
static struct lapb_register_struct x25_asy_callbacks = {
@@ -450,7 +446,7 @@ static struct lapb_register_struct x25_asy_callbacks = {
/* Open the low-level part of the X.25 channel. Easy! */
static int x25_asy_open(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
unsigned long len;
int err;
@@ -499,12 +495,12 @@ norbuff:
/* Close the low-level part of the X.25 channel. Easy! */
static int x25_asy_close(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
int err;
spin_lock(&sl->lock);
if (sl->tty)
- sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
netif_stop_queue(dev);
sl->rcount = 0;
@@ -527,7 +523,7 @@ static int x25_asy_close(struct net_device *dev)
static void x25_asy_receive_buf(struct tty_struct *tty,
const unsigned char *cp, char *fp, int count)
{
- struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
+ struct x25_asy *sl = tty->disc_data;
if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev))
return;
@@ -555,7 +551,7 @@ static void x25_asy_receive_buf(struct tty_struct *tty,
static int x25_asy_open_tty(struct tty_struct *tty)
{
- struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
+ struct x25_asy *sl = tty->disc_data;
int err;
if (tty->ops->write == NULL)
@@ -596,7 +592,7 @@ static int x25_asy_open_tty(struct tty_struct *tty)
*/
static void x25_asy_close_tty(struct tty_struct *tty)
{
- struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
+ struct x25_asy *sl = tty->disc_data;
/* First make sure we're connected. */
if (!sl || sl->magic != X25_ASY_MAGIC)
@@ -615,7 +611,7 @@ static void x25_asy_close_tty(struct tty_struct *tty)
static struct net_device_stats *x25_asy_get_stats(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
return &sl->stats;
}
@@ -624,7 +620,7 @@ static struct net_device_stats *x25_asy_get_stats(struct net_device *dev)
* STANDARD X.25 ENCAPSULATION *
************************************************************************/
-int x25_asy_esc(unsigned char *s, unsigned char *d, int len)
+static int x25_asy_esc(unsigned char *s, unsigned char *d, int len)
{
unsigned char *ptr = d;
unsigned char c;
@@ -696,7 +692,7 @@ static void x25_asy_unesc(struct x25_asy *sl, unsigned char s)
static int x25_asy_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct x25_asy *sl = (struct x25_asy *) tty->disc_data;
+ struct x25_asy *sl = tty->disc_data;
/* First make sure we're connected. */
if (!sl || sl->magic != X25_ASY_MAGIC)
@@ -717,7 +713,7 @@ static int x25_asy_ioctl(struct tty_struct *tty, struct file *file,
static int x25_asy_open_dev(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
if (sl->tty == NULL)
return -ENODEV;
return 0;
@@ -726,7 +722,7 @@ static int x25_asy_open_dev(struct net_device *dev)
/* Initialise the X.25 driver. Called by the device init code */
static void x25_asy_setup(struct net_device *dev)
{
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
sl->magic = X25_ASY_MAGIC;
sl->dev = dev;
@@ -793,7 +789,7 @@ static void __exit exit_x25_asy(void)
for (i = 0; i < x25_asy_maxdev; i++) {
dev = x25_asy_devs[i];
if (dev) {
- struct x25_asy *sl = dev->priv;
+ struct x25_asy *sl = netdev_priv(dev);
spin_lock_bh(&sl->lock);
if (sl->tty)
diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c
index 5bf7e01ef0e9..3d00971fe5ee 100644
--- a/drivers/net/wan/z85230.c
+++ b/drivers/net/wan/z85230.c
@@ -601,24 +601,18 @@ static void z8530_dma_status(struct z8530_channel *chan)
write_zsctrl(chan, RES_H_IUS);
}
-struct z8530_irqhandler z8530_dma_sync=
-{
+static struct z8530_irqhandler z8530_dma_sync = {
z8530_dma_rx,
z8530_dma_tx,
z8530_dma_status
};
-EXPORT_SYMBOL(z8530_dma_sync);
-
-struct z8530_irqhandler z8530_txdma_sync=
-{
+static struct z8530_irqhandler z8530_txdma_sync = {
z8530_rx,
z8530_dma_tx,
z8530_dma_status
};
-EXPORT_SYMBOL(z8530_txdma_sync);
-
/**
* z8530_rx_clear - Handle RX events from a stopped chip
* @c: Z8530 channel to shut up
@@ -710,7 +704,7 @@ EXPORT_SYMBOL(z8530_nop);
irqreturn_t z8530_interrupt(int irq, void *dev_id)
{
struct z8530_dev *dev=dev_id;
- u8 intr;
+ u8 uninitialized_var(intr);
static volatile int locker=0;
int work=0;
struct z8530_irqhandler *irqs;
diff --git a/drivers/net/wd.c b/drivers/net/wd.c
index fa14255282af..3c1edda08d3d 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/wd.c
@@ -147,6 +147,20 @@ out:
}
#endif
+static const struct net_device_ops wd_netdev_ops = {
+ .ndo_open = wd_open,
+ .ndo_stop = wd_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __init wd_probe1(struct net_device *dev, int ioaddr)
{
int i;
@@ -156,7 +170,6 @@ static int __init wd_probe1(struct net_device *dev, int ioaddr)
int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
const char *model_name;
static unsigned version_printed;
- DECLARE_MAC_BUF(mac);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
@@ -178,8 +191,8 @@ static int __init wd_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = inb(ioaddr + 8 + i);
- printk("%s: WD80x3 at %#3x, %s",
- dev->name, ioaddr, print_mac(mac, dev->dev_addr));
+ printk("%s: WD80x3 at %#3x, %pM",
+ dev->name, ioaddr, dev->dev_addr);
/* The following PureData probe code was contributed by
Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
@@ -332,11 +345,8 @@ static int __init wd_probe1(struct net_device *dev, int ioaddr)
ei_status.block_input = &wd_block_input;
ei_status.block_output = &wd_block_output;
ei_status.get_8390_hdr = &wd_get_8390_hdr;
- dev->open = &wd_open;
- dev->stop = &wd_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = ei_poll;
-#endif
+
+ dev->netdev_ops = &wd_netdev_ops;
NS8390_init(dev, 0);
#if 1
@@ -366,8 +376,7 @@ wd_open(struct net_device *dev)
outb(ei_status.reg5, ioaddr+WD_CMDREG5);
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
- ei_open(dev);
- return 0;
+ return ei_open(dev);
}
static void
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 45bdf0b339bb..ea543fcf2687 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -123,154 +123,11 @@ config PCMCIA_RAYCS
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-config IPW2100
- tristate "Intel PRO/Wireless 2100 Network Connection"
- depends on PCI && WLAN_80211
- select WIRELESS_EXT
- select FW_LOADER
- select IEEE80211
- ---help---
- A driver for the Intel PRO/Wireless 2100 Network
- Connection 802.11b wireless network adapter.
-
- See <file:Documentation/networking/README.ipw2100> for information on
- the capabilities currently enabled in this driver and for tips
- for debugging issues and problems.
-
- In order to use this driver, you will need a firmware image for it.
- You can obtain the firmware from
- <http://ipw2100.sf.net/>. Once you have the firmware image, you
- will need to place it in /lib/firmware.
-
- You will also very likely need the Wireless Tools in order to
- configure your card:
-
- <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
-
- It is recommended that you compile this driver as a module (M)
- rather than built-in (Y). This driver requires firmware at device
- initialization time, and when built-in this typically happens
- before the filesystem is accessible (hence firmware will be
- unavailable and initialization will fail). If you do choose to build
- this driver into your kernel image, you can avoid this problem by
- including the firmware and a firmware loader in an initramfs.
-
-config IPW2100_MONITOR
- bool "Enable promiscuous mode"
- depends on IPW2100
- ---help---
- Enables promiscuous/monitor mode support for the ipw2100 driver.
- With this feature compiled into the driver, you can switch to
- promiscuous mode via the Wireless Tool's Monitor mode. While in this
- mode, no packets can be sent.
-
-config IPW2100_DEBUG
- bool "Enable full debugging output in IPW2100 module."
- depends on IPW2100
- ---help---
- This option will enable debug tracing output for the IPW2100.
-
- This will result in the kernel module being ~60k larger. You can
- control which debug output is sent to the kernel log by setting the
- value in
-
- /sys/bus/pci/drivers/ipw2100/debug_level
-
- This entry will only exist if this option is enabled.
-
- If you are not trying to debug or develop the IPW2100 driver, you
- most likely want to say N here.
-
-config IPW2200
- tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
- depends on PCI && WLAN_80211
- select WIRELESS_EXT
- select FW_LOADER
- select IEEE80211
- ---help---
- A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
- Connection adapters.
-
- See <file:Documentation/networking/README.ipw2200> for
- information on the capabilities currently enabled in this
- driver and for tips for debugging issues and problems.
-
- In order to use this driver, you will need a firmware image for it.
- You can obtain the firmware from
- <http://ipw2200.sf.net/>. See the above referenced README.ipw2200
- for information on where to install the firmware images.
-
- You will also very likely need the Wireless Tools in order to
- configure your card:
-
- <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
-
- It is recommended that you compile this driver as a module (M)
- rather than built-in (Y). This driver requires firmware at device
- initialization time, and when built-in this typically happens
- before the filesystem is accessible (hence firmware will be
- unavailable and initialization will fail). If you do choose to build
- this driver into your kernel image, you can avoid this problem by
- including the firmware and a firmware loader in an initramfs.
-
-config IPW2200_MONITOR
- bool "Enable promiscuous mode"
- depends on IPW2200
- ---help---
- Enables promiscuous/monitor mode support for the ipw2200 driver.
- With this feature compiled into the driver, you can switch to
- promiscuous mode via the Wireless Tool's Monitor mode. While in this
- mode, no packets can be sent.
-
-config IPW2200_RADIOTAP
- bool "Enable radiotap format 802.11 raw packet support"
- depends on IPW2200_MONITOR
-
-config IPW2200_PROMISCUOUS
- bool "Enable creation of a RF radiotap promiscuous interface"
- depends on IPW2200_MONITOR
- select IPW2200_RADIOTAP
- ---help---
- Enables the creation of a second interface prefixed 'rtap'.
- This second interface will provide every received in radiotap
- format.
-
- This is useful for performing wireless network analysis while
- maintaining an active association.
-
- Example usage:
-
- % modprobe ipw2200 rtap_iface=1
- % ifconfig rtap0 up
- % tethereal -i rtap0
-
- If you do not specify 'rtap_iface=1' as a module parameter then
- the rtap interface will not be created and you will need to turn
- it on via sysfs:
-
- % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
-
-config IPW2200_QOS
- bool "Enable QoS support"
- depends on IPW2200 && EXPERIMENTAL
-
-config IPW2200_DEBUG
- bool "Enable full debugging output in IPW2200 module."
- depends on IPW2200
- ---help---
- This option will enable low level debug tracing output for IPW2200.
-
- Note, normal debug code is already compiled in. This low level
- debug option enables debug on hot paths (e.g Tx, Rx, ISR) and
- will result in the kernel module being ~70 larger. Most users
- will typically not need this high verbosity debug information.
-
- If you are not sure, say N here.
-
config LIBERTAS
tristate "Marvell 8xxx Libertas WLAN driver support"
depends on WLAN_80211
select WIRELESS_EXT
+ select LIB80211
select FW_LOADER
---help---
A library for Marvell Libertas 8xxx devices.
@@ -357,6 +214,21 @@ config HERMES
configure your card and that /etc/pcmcia/wireless.opts works :
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>
+config HERMES_CACHE_FW_ON_INIT
+ bool "Cache Hermes firmware on driver initialisation"
+ depends on HERMES
+ default y
+ ---help---
+ Say Y to cache any firmware required by the Hermes drivers
+ on startup. The firmware will remain cached until the
+ driver is unloaded. The cache uses 64K of RAM.
+
+ Otherwise load the firmware from userspace as required. In
+ this case the driver should be unloaded and restarted
+ whenever the firmware is changed.
+
+ If you are not sure, say Y.
+
config APPLE_AIRPORT
tristate "Apple Airport support (built-in)"
depends on PPC_PMAC && HERMES
@@ -651,7 +523,7 @@ config RTL8180
config RTL8187
tristate "Realtek 8187 and 8187B USB support"
- depends on MAC80211 && USB && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && USB && WLAN_80211
select EEPROM_93CX6
---help---
This is a driver for RTL8187 and RTL8187B based cards.
@@ -711,6 +583,7 @@ config MAC80211_HWSIM
source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/ath5k/Kconfig"
source "drivers/net/wireless/ath9k/Kconfig"
+source "drivers/net/wireless/ipw2x00/Kconfig"
source "drivers/net/wireless/iwlwifi/Kconfig"
source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 59d2d805f60b..ac590e1ca8be 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -2,9 +2,8 @@
# Makefile for the Linux Wireless network device drivers.
#
-obj-$(CONFIG_IPW2100) += ipw2100.o
-
-obj-$(CONFIG_IPW2200) += ipw2200.o
+obj-$(CONFIG_IPW2100) += ipw2x00/
+obj-$(CONFIG_IPW2200) += ipw2x00/
obj-$(CONFIG_STRIP) += strip.o
obj-$(CONFIG_ARLAN) += arlan.o
@@ -16,14 +15,7 @@ obj-$(CONFIG_WAVELAN) += wavelan.o
obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o
-obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o
-obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
-obj-$(CONFIG_APPLE_AIRPORT) += airport.o
-obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
-obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
-obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
-obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
-obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
+obj-$(CONFIG_HERMES) += orinoco/
obj-$(CONFIG_AIRO) += airo.o
obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
@@ -38,6 +30,8 @@ obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_B43LEGACY) += b43legacy/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
+obj-$(CONFIG_RTL8180) += rtl818x/
+obj-$(CONFIG_RTL8187) += rtl818x/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
@@ -50,12 +44,6 @@ obj-$(CONFIG_LIBERTAS) += libertas/
obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
-rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o
-rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o
-
-obj-$(CONFIG_RTL8180) += rtl8180.o
-obj-$(CONFIG_RTL8187) += rtl8187.o
-
obj-$(CONFIG_ADM8211) += adm8211.o
obj-$(CONFIG_IWLWIFI) += iwlwifi/
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index b2c050b68890..fc0897fb2239 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -341,15 +341,14 @@ static void adm8211_interrupt_tci(struct ieee80211_hw *dev)
pci_unmap_single(priv->pdev, info->mapping,
info->skb->len, PCI_DMA_TODEVICE);
- memset(&txi->status, 0, sizeof(txi->status));
+ ieee80211_tx_info_clear_status(txi);
+
skb_pull(skb, sizeof(struct adm8211_tx_hdr));
memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen);
- if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) {
- if (status & TDES0_STATUS_ES)
- txi->status.excessive_retries = 1;
- else
- txi->flags |= IEEE80211_TX_STAT_ACK;
- }
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ !(status & TDES0_STATUS_ES))
+ txi->flags |= IEEE80211_TX_STAT_ACK;
+
ieee80211_tx_status_irqsafe(dev, skb);
info->skb = NULL;
@@ -1298,25 +1297,10 @@ static void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid)
ADM8211_CSR_WRITE(ABDA1, reg);
}
-static int adm8211_set_ssid(struct ieee80211_hw *dev, u8 *ssid, size_t ssid_len)
-{
- struct adm8211_priv *priv = dev->priv;
- u8 buf[36];
-
- if (ssid_len > 32)
- return -EINVAL;
-
- memset(buf, 0, sizeof(buf));
- buf[0] = ssid_len;
- memcpy(buf + 1, ssid, ssid_len);
- adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33);
- /* TODO: configure beacon for adhoc? */
- return 0;
-}
-
-static int adm8211_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
+static int adm8211_config(struct ieee80211_hw *dev, u32 changed)
{
struct adm8211_priv *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
int channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
if (channel != priv->channel) {
@@ -1338,13 +1322,6 @@ static int adm8211_config_interface(struct ieee80211_hw *dev,
memcpy(priv->bssid, conf->bssid, ETH_ALEN);
}
- if (conf->ssid_len != priv->ssid_len ||
- memcmp(conf->ssid, priv->ssid, conf->ssid_len)) {
- adm8211_set_ssid(dev, conf->ssid, conf->ssid_len);
- priv->ssid_len = conf->ssid_len;
- memcpy(priv->ssid, conf->ssid, conf->ssid_len);
- }
-
return 0;
}
@@ -1690,8 +1667,10 @@ static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info);
+ u8 rc_flags;
- short_preamble = !!(txrate->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE);
+ rc_flags = info->control.rates[0].flags;
+ short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
plcp_signal = txrate->bitrate;
hdr = (struct ieee80211_hdr *)skb->data;
@@ -1723,10 +1702,10 @@ static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
if (short_preamble)
txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE);
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS);
- txhdr->retry_limit = info->control.retry_limit;
+ txhdr->retry_limit = info->control.rates[0].count;
adm8211_tx_raw(dev, skb, plcp_signal, hdrlen);
@@ -1791,7 +1770,6 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
int err;
u32 reg;
u8 perm_addr[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
err = pci_enable_device(pdev);
if (err) {
@@ -1925,8 +1903,8 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
goto err_free_desc;
}
- printk(KERN_INFO "%s: hwaddr %s, Rev 0x%02x\n",
- wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr),
+ printk(KERN_INFO "%s: hwaddr %pM, Rev 0x%02x\n",
+ wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
pdev->revision);
return 0;
diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/adm8211.h
index 9b190ee26e90..4f6ab1322189 100644
--- a/drivers/net/wireless/adm8211.h
+++ b/drivers/net/wireless/adm8211.h
@@ -553,8 +553,6 @@ struct adm8211_priv {
int channel;
u8 bssid[ETH_ALEN];
- u8 ssid[32];
- size_t ssid_len;
u8 soft_rx_crc;
u8 retry_limit;
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 370133e492d2..fc4322ca669f 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -47,10 +47,11 @@
#include <linux/ioport.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
-#include <net/ieee80211.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/ieee80211.h>
+
#include "airo.h"
#define DRV_NAME "airo"
@@ -1270,6 +1271,7 @@ static int flashrestart(struct airo_info *ai,struct net_device *dev);
#define airo_print_err(name, fmt, args...) \
airo_print(KERN_ERR, name, fmt, ##args)
+#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash)
/***********************************************************************
* MIC ROUTINES *
@@ -1865,7 +1867,7 @@ static void try_auto_wep(struct airo_info *ai)
}
static int airo_open(struct net_device *dev) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int rc = 0;
if (test_bit(FLAG_FLASHING, &ai->flags))
@@ -1912,7 +1914,7 @@ static int airo_open(struct net_device *dev) {
static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) {
int npacks, pending;
unsigned long flags;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
if (!skb) {
airo_print_err(dev->name, "%s: skb == NULL!",__func__);
@@ -1956,7 +1958,7 @@ static int mpi_send_packet (struct net_device *dev)
unsigned char *buffer;
s16 len;
__le16 *payloadLen;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
u8 *sendbuf;
/* get a packet to send */
@@ -2085,7 +2087,7 @@ static void get_tx_error(struct airo_info *ai, s32 fid)
static void airo_end_xmit(struct net_device *dev) {
u16 status;
int i;
- struct airo_info *priv = dev->priv;
+ struct airo_info *priv = dev->ml_priv;
struct sk_buff *skb = priv->xmit.skb;
int fid = priv->xmit.fid;
u32 *fids = priv->fids;
@@ -2111,7 +2113,7 @@ static void airo_end_xmit(struct net_device *dev) {
static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
s16 len;
int i, j;
- struct airo_info *priv = dev->priv;
+ struct airo_info *priv = dev->ml_priv;
u32 *fids = priv->fids;
if ( skb == NULL ) {
@@ -2150,7 +2152,7 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
static void airo_end_xmit11(struct net_device *dev) {
u16 status;
int i;
- struct airo_info *priv = dev->priv;
+ struct airo_info *priv = dev->ml_priv;
struct sk_buff *skb = priv->xmit11.skb;
int fid = priv->xmit11.fid;
u32 *fids = priv->fids;
@@ -2176,7 +2178,7 @@ static void airo_end_xmit11(struct net_device *dev) {
static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
s16 len;
int i, j;
- struct airo_info *priv = dev->priv;
+ struct airo_info *priv = dev->ml_priv;
u32 *fids = priv->fids;
if (test_bit(FLAG_MPI, &priv->flags)) {
@@ -2220,7 +2222,7 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
static void airo_read_stats(struct net_device *dev)
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
StatsRid stats_rid;
__le32 *vals = stats_rid.vals;
@@ -2254,7 +2256,7 @@ static void airo_read_stats(struct net_device *dev)
static struct net_device_stats *airo_get_stats(struct net_device *dev)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
if (!test_bit(JOB_STATS, &local->jobs)) {
/* Get stats out of the card if available */
@@ -2281,7 +2283,7 @@ static void airo_set_promisc(struct airo_info *ai) {
}
static void airo_set_multicast_list(struct net_device *dev) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
if ((dev->flags ^ ai->flags) & IFF_PROMISC) {
change_bit(FLAG_PROMISC, &ai->flags);
@@ -2299,7 +2301,7 @@ static void airo_set_multicast_list(struct net_device *dev) {
static int airo_set_mac_address(struct net_device *dev, void *p)
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
struct sockaddr *addr = p;
readConfigRid(ai, 1);
@@ -2339,7 +2341,7 @@ static void del_airo_dev(struct airo_info *ai)
}
static int airo_close(struct net_device *dev) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
netif_stop_queue(dev);
@@ -2365,7 +2367,7 @@ static int airo_close(struct net_device *dev) {
void stop_airo_card( struct net_device *dev, int freeres )
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
set_bit(FLAG_RADIO_DOWN, &ai->flags);
disable_MAC(ai, 1);
@@ -2665,7 +2667,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
struct net_device *dev = alloc_netdev(0, "wifi%d", wifi_setup);
if (!dev)
return NULL;
- dev->priv = ethdev->priv;
+ dev->ml_priv = ethdev->ml_priv;
dev->irq = ethdev->irq;
dev->base_addr = ethdev->base_addr;
dev->wireless_data = ethdev->wireless_data;
@@ -2680,7 +2682,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
}
static int reset_card( struct net_device *dev , int lock) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
if (lock && down_interruptible(&ai->sem))
return -1;
@@ -2757,7 +2759,6 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
struct net_device *dev;
struct airo_info *ai;
int i, rc;
- DECLARE_MAC_BUF(mac);
/* Create the network device object. */
dev = alloc_netdev(sizeof(*ai), "", ether_setup);
@@ -2766,7 +2767,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
return NULL;
}
- ai = dev->priv;
+ ai = dev->ml_priv = netdev_priv(dev);
ai->wifidev = NULL;
ai->flags = 1 << FLAG_RADIO_DOWN;
ai->jobs = 0;
@@ -2860,15 +2861,14 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
goto err_out_reg;
set_bit(FLAG_REGISTERED,&ai->flags);
- airo_print_info(dev->name, "MAC enabled %s",
- print_mac(mac, dev->dev_addr));
+ airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
/* Allocate the transmit buffers */
if (probe && !test_bit(FLAG_MPI,&ai->flags))
for( i = 0; i < MAX_FIDS; i++ )
ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2);
- if (setup_proc_entry(dev, dev->priv) < 0)
+ if (setup_proc_entry(dev, dev->ml_priv) < 0)
goto err_out_wifi;
return dev;
@@ -2917,8 +2917,7 @@ static int waitbusy (struct airo_info *ai) {
int reset_airo_card( struct net_device *dev )
{
int i;
- struct airo_info *ai = dev->priv;
- DECLARE_MAC_BUF(mac);
+ struct airo_info *ai = dev->ml_priv;
if (reset_card (dev, 1))
return -1;
@@ -2927,8 +2926,7 @@ int reset_airo_card( struct net_device *dev )
airo_print_err(dev->name, "MAC could not be enabled");
return -1;
}
- airo_print_info(dev->name, "MAC enabled %s",
- print_mac(mac, dev->dev_addr));
+ airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr);
/* Allocate the transmit buffers if needed */
if (!test_bit(FLAG_MPI,&ai->flags))
for( i = 0; i < MAX_FIDS; i++ )
@@ -2942,7 +2940,7 @@ int reset_airo_card( struct net_device *dev )
EXPORT_SYMBOL(reset_airo_card);
static void airo_send_event(struct net_device *dev) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
union iwreq_data wrqu;
StatusRid status_rid;
@@ -3019,7 +3017,7 @@ out:
static int airo_thread(void *data) {
struct net_device *dev = data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int locked;
set_freezable();
@@ -3134,7 +3132,7 @@ static irqreturn_t airo_interrupt(int irq, void *dev_id)
struct net_device *dev = dev_id;
u16 status;
u16 fid;
- struct airo_info *apriv = dev->priv;
+ struct airo_info *apriv = dev->ml_priv;
u16 savedInterrupts = 0;
int handled = 0;
@@ -3369,7 +3367,6 @@ badrx:
skb->protocol = htons(ETH_P_802_2);
} else
skb->protocol = eth_type_trans(skb,dev);
- skb->dev->last_rx = jiffies;
skb->ip_summed = CHECKSUM_NONE;
netif_rx( skb );
@@ -3599,7 +3596,6 @@ badmic:
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = eth_type_trans(skb, ai->dev);
- skb->dev->last_rx = jiffies;
netif_rx(skb);
}
badrx:
@@ -3611,7 +3607,7 @@ badrx:
}
}
-void mpi_receive_802_11 (struct airo_info *ai)
+static void mpi_receive_802_11(struct airo_info *ai)
{
RxFid rxd;
struct sk_buff *skb = NULL;
@@ -3693,7 +3689,6 @@ void mpi_receive_802_11 (struct airo_info *ai)
skb->pkt_type = PACKET_OTHERHOST;
skb->dev = ai->wifidev;
skb->protocol = htons(ETH_P_802_2);
- skb->dev->last_rx = jiffies;
skb->ip_summed = CHECKSUM_NONE;
netif_rx( skb );
badrx:
@@ -4604,7 +4599,7 @@ static int proc_status_open(struct inode *inode, struct file *file)
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *apriv = dev->priv;
+ struct airo_info *apriv = dev->ml_priv;
CapabilityRid cap_rid;
StatusRid status_rid;
u16 mode;
@@ -4687,7 +4682,7 @@ static int proc_stats_rid_open( struct inode *inode,
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *apriv = dev->priv;
+ struct airo_info *apriv = dev->ml_priv;
StatsRid stats;
int i, j;
__le32 *vals = stats.vals;
@@ -4750,7 +4745,7 @@ static void proc_config_on_close(struct inode *inode, struct file *file)
struct proc_data *data = file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
char *line;
if ( !data->writelen ) return;
@@ -4962,7 +4957,7 @@ static int proc_config_open(struct inode *inode, struct file *file)
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int i;
__le16 mode;
@@ -5053,7 +5048,7 @@ static void proc_SSID_on_close(struct inode *inode, struct file *file)
struct proc_data *data = (struct proc_data *)file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
SsidRid SSID_rid;
int i;
char *p = data->wbuffer;
@@ -5096,7 +5091,7 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data = (struct proc_data *)file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
APListRid APList_rid;
int i;
@@ -5191,7 +5186,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int i;
char key[16];
u16 index = 0;
@@ -5233,7 +5228,7 @@ static int proc_wepkey_open( struct inode *inode, struct file *file )
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
char *ptr;
WepKeyRid wkr;
__le16 lastindex;
@@ -5282,7 +5277,7 @@ static int proc_SSID_open(struct inode *inode, struct file *file)
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int i;
char *ptr;
SsidRid SSID_rid;
@@ -5326,11 +5321,10 @@ static int proc_APList_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int i;
char *ptr;
APListRid APList_rid;
- DECLARE_MAC_BUF(mac);
if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
@@ -5354,8 +5348,7 @@ static int proc_APList_open( struct inode *inode, struct file *file ) {
// We end when we find a zero MAC
if ( !*(int*)APList_rid.ap[i] &&
!*(int*)&APList_rid.ap[i][2]) break;
- ptr += sprintf(ptr, "%s\n",
- print_mac(mac, APList_rid.ap[i]));
+ ptr += sprintf(ptr, "%pM\n", APList_rid.ap[i]);
}
if (i==0) ptr += sprintf(ptr, "Not using specific APs\n");
@@ -5368,13 +5361,12 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
char *ptr;
BSSListRid BSSList_rid;
int rc;
/* If doLoseSync is not 1, we won't do a Lose Sync */
int doLoseSync = -1;
- DECLARE_MAC_BUF(mac);
if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
@@ -5411,8 +5403,8 @@ static int proc_BSSList_open( struct inode *inode, struct file *file ) {
we have to add a spin lock... */
rc = readBSSListRid(ai, doLoseSync, &BSSList_rid);
while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) {
- ptr += sprintf(ptr, "%s %*s rssi = %d",
- print_mac(mac, BSSList_rid.bssid),
+ ptr += sprintf(ptr, "%pM %*s rssi = %d",
+ BSSList_rid.bssid,
(int)BSSList_rid.ssidLen,
BSSList_rid.ssid,
le16_to_cpu(BSSList_rid.dBm));
@@ -5447,7 +5439,7 @@ static int proc_close( struct inode *inode, struct file *file )
associated we will check every minute to see if anything has
changed. */
static void timer_func( struct net_device *dev ) {
- struct airo_info *apriv = dev->priv;
+ struct airo_info *apriv = dev->ml_priv;
/* We don't have a link so try changing the authtype */
readConfigRid(apriv, 0);
@@ -5518,7 +5510,7 @@ static void __devexit airo_pci_remove(struct pci_dev *pdev)
static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
Cmd cmd;
Resp rsp;
@@ -5550,7 +5542,7 @@ static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
static int airo_pci_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
pci_power_t prev_state = pdev->current_state;
pci_set_power_state(pdev, PCI_D0);
@@ -5729,7 +5721,7 @@ static int airo_set_freq(struct net_device *dev,
struct iw_freq *fwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int rc = -EINPROGRESS; /* Call commit handler */
/* If setting by frequency, convert to a channel */
@@ -5774,7 +5766,7 @@ static int airo_get_freq(struct net_device *dev,
struct iw_freq *fwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
StatusRid status_rid; /* Card status info */
int ch;
@@ -5805,7 +5797,7 @@ static int airo_set_essid(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
SsidRid SSID_rid; /* SSIDs */
/* Reload the list of current SSID */
@@ -5851,7 +5843,7 @@ static int airo_get_essid(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
StatusRid status_rid; /* Card status info */
readStatusRid(local, &status_rid, 1);
@@ -5879,7 +5871,7 @@ static int airo_set_wap(struct net_device *dev,
struct sockaddr *awrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
Cmd cmd;
Resp rsp;
APListRid APList_rid;
@@ -5916,7 +5908,7 @@ static int airo_get_wap(struct net_device *dev,
struct sockaddr *awrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
StatusRid status_rid; /* Card status info */
readStatusRid(local, &status_rid, 1);
@@ -5937,7 +5929,7 @@ static int airo_set_nick(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
/* Check the size of the string */
if(dwrq->length > 16) {
@@ -5960,7 +5952,7 @@ static int airo_get_nick(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
strncpy(extra, local->config.nodeName, 16);
@@ -5979,7 +5971,7 @@ static int airo_set_rate(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
CapabilityRid cap_rid; /* Card capability info */
u8 brate = 0;
int i;
@@ -6049,7 +6041,7 @@ static int airo_get_rate(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
StatusRid status_rid; /* Card status info */
readStatusRid(local, &status_rid, 1);
@@ -6071,7 +6063,7 @@ static int airo_set_rts(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int rthr = vwrq->value;
if(vwrq->disabled)
@@ -6095,7 +6087,7 @@ static int airo_get_rts(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
vwrq->value = le16_to_cpu(local->config.rtsThres);
@@ -6114,7 +6106,7 @@ static int airo_set_frag(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int fthr = vwrq->value;
if(vwrq->disabled)
@@ -6139,7 +6131,7 @@ static int airo_get_frag(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
vwrq->value = le16_to_cpu(local->config.fragThresh);
@@ -6158,7 +6150,7 @@ static int airo_set_mode(struct net_device *dev,
__u32 *uwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int reset = 0;
readConfigRid(local, 1);
@@ -6221,7 +6213,7 @@ static int airo_get_mode(struct net_device *dev,
__u32 *uwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
/* If not managed, assume it's ad-hoc */
@@ -6258,7 +6250,7 @@ static int airo_set_encode(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
CapabilityRid cap_rid; /* Card capability info */
int perm = ( dwrq->flags & IW_ENCODE_TEMP ? 0 : 1 );
__le16 currentAuthType = local->config.authType;
@@ -6345,7 +6337,7 @@ static int airo_get_encode(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
CapabilityRid cap_rid; /* Card capability info */
@@ -6393,7 +6385,7 @@ static int airo_set_encodeext(struct net_device *dev,
union iwreq_data *wrqu,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
CapabilityRid cap_rid; /* Card capability info */
@@ -6479,7 +6471,7 @@ static int airo_get_encodeext(struct net_device *dev,
union iwreq_data *wrqu,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct iw_point *encoding = &wrqu->encoding;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
CapabilityRid cap_rid; /* Card capability info */
@@ -6542,7 +6534,7 @@ static int airo_set_auth(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct iw_param *param = &wrqu->param;
__le16 currentAuthType = local->config.authType;
@@ -6610,7 +6602,7 @@ static int airo_get_auth(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct iw_param *param = &wrqu->param;
__le16 currentAuthType = local->config.authType;
@@ -6659,7 +6651,7 @@ static int airo_set_txpow(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
CapabilityRid cap_rid; /* Card capability info */
int i;
int rc = -EINVAL;
@@ -6696,7 +6688,7 @@ static int airo_get_txpow(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
vwrq->value = le16_to_cpu(local->config.txPower);
@@ -6716,7 +6708,7 @@ static int airo_set_retry(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
int rc = -EINVAL;
if(vwrq->disabled) {
@@ -6754,7 +6746,7 @@ static int airo_get_retry(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
vwrq->disabled = 0; /* Can't be disabled */
@@ -6785,7 +6777,7 @@ static int airo_get_range(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct iw_range *range = (struct iw_range *) extra;
CapabilityRid cap_rid; /* Card capability info */
int i;
@@ -6910,7 +6902,7 @@ static int airo_set_power(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
if (vwrq->disabled) {
@@ -6967,7 +6959,7 @@ static int airo_get_power(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
__le16 mode;
readConfigRid(local, 1);
@@ -6998,7 +6990,7 @@ static int airo_set_sens(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
local->config.rssiThreshold =
@@ -7017,7 +7009,7 @@ static int airo_get_sens(struct net_device *dev,
struct iw_param *vwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
readConfigRid(local, 1);
vwrq->value = le16_to_cpu(local->config.rssiThreshold);
@@ -7037,7 +7029,7 @@ static int airo_get_aplist(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
struct sockaddr *address = (struct sockaddr *) extra;
struct iw_quality qual[IW_MAX_AP];
BSSListRid BSSList;
@@ -7110,7 +7102,7 @@ static int airo_set_scan(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
Cmd cmd;
Resp rsp;
int wake = 0;
@@ -7156,7 +7148,7 @@ static inline char *airo_translate_scan(struct net_device *dev,
char *end_buf,
BSSListRid *bss)
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
struct iw_event iwe; /* Temporary buffer */
__le16 capabilities;
char * current_val; /* For rates */
@@ -7274,56 +7266,53 @@ static inline char *airo_translate_scan(struct net_device *dev,
if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) {
unsigned int num_null_ies = 0;
u16 length = sizeof (bss->extra.iep);
- struct ieee80211_info_element *info_element =
- (struct ieee80211_info_element *) &bss->extra.iep;
+ u8 *ie = (void *)&bss->extra.iep;
- while ((length >= sizeof(*info_element)) && (num_null_ies < 2)) {
- if (sizeof(*info_element) + info_element->len > length) {
+ while ((length >= 2) && (num_null_ies < 2)) {
+ if (2 + ie[1] > length) {
/* Invalid element, don't continue parsing IE */
break;
}
- switch (info_element->id) {
- case MFIE_TYPE_SSID:
+ switch (ie[0]) {
+ case WLAN_EID_SSID:
/* Two zero-length SSID elements
* mean we're done parsing elements */
- if (!info_element->len)
+ if (!ie[1])
num_null_ies++;
break;
- case MFIE_TYPE_GENERIC:
- if (info_element->len >= 4 &&
- info_element->data[0] == 0x00 &&
- info_element->data[1] == 0x50 &&
- info_element->data[2] == 0xf2 &&
- info_element->data[3] == 0x01) {
+ case WLAN_EID_GENERIC:
+ if (ie[1] >= 4 &&
+ ie[2] == 0x00 &&
+ ie[3] == 0x50 &&
+ ie[4] == 0xf2 &&
+ ie[5] == 0x01) {
iwe.cmd = IWEVGENIE;
- iwe.u.data.length = min(info_element->len + 2,
- MAX_WPA_IE_LEN);
+ /* 64 is an arbitrary cut-off */
+ iwe.u.data.length = min(ie[1] + 2,
+ 64);
current_ev = iwe_stream_add_point(
info, current_ev,
- end_buf, &iwe,
- (char *) info_element);
+ end_buf, &iwe, ie);
}
break;
- case MFIE_TYPE_RSN:
+ case WLAN_EID_RSN:
iwe.cmd = IWEVGENIE;
- iwe.u.data.length = min(info_element->len + 2,
- MAX_WPA_IE_LEN);
+ /* 64 is an arbitrary cut-off */
+ iwe.u.data.length = min(ie[1] + 2, 64);
current_ev = iwe_stream_add_point(
info, current_ev, end_buf,
- &iwe, (char *) info_element);
+ &iwe, ie);
break;
default:
break;
}
- length -= sizeof(*info_element) + info_element->len;
- info_element =
- (struct ieee80211_info_element *)&info_element->
- data[info_element->len];
+ length -= 2 + ie[1];
+ ie += 2 + ie[1];
}
}
return current_ev;
@@ -7338,7 +7327,7 @@ static int airo_get_scan(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
BSSListElement *net;
int err = 0;
char *current_ev = extra;
@@ -7382,7 +7371,7 @@ static int airo_config_commit(struct net_device *dev,
void *zwrq, /* NULL */
char *extra) /* NULL */
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
if (!test_bit (FLAG_COMMIT, &local->flags))
return 0;
@@ -7527,7 +7516,7 @@ static const struct iw_handler_def airo_handler_def =
static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
int rc = 0;
- struct airo_info *ai = (struct airo_info *)dev->priv;
+ struct airo_info *ai = dev->ml_priv;
if (ai->power.event)
return 0;
@@ -7655,7 +7644,7 @@ static void airo_read_wireless_stats(struct airo_info *local)
static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
{
- struct airo_info *local = dev->priv;
+ struct airo_info *local = dev->ml_priv;
if (!test_bit(JOB_WSTATS, &local->jobs)) {
/* Get stats out of the card if available */
@@ -7680,7 +7669,7 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) {
unsigned short ridcode;
unsigned char *iobuf;
int len;
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
if (test_bit(FLAG_FLASHING, &ai->flags))
return -EIO;
@@ -7746,7 +7735,7 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) {
*/
static int writerids(struct net_device *dev, aironet_ioctl *comp) {
- struct airo_info *ai = dev->priv;
+ struct airo_info *ai = dev->ml_priv;
int ridcode;
int enabled;
static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
@@ -7869,41 +7858,41 @@ static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
switch(comp->command)
{
case AIROFLSHRST:
- return cmdreset((struct airo_info *)dev->priv);
+ return cmdreset((struct airo_info *)dev->ml_priv);
case AIROFLSHSTFL:
- if (!((struct airo_info *)dev->priv)->flash &&
- (((struct airo_info *)dev->priv)->flash = kmalloc (FLASHSIZE, GFP_KERNEL)) == NULL)
+ if (!AIRO_FLASH(dev) &&
+ (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL)
return -ENOMEM;
- return setflashmode((struct airo_info *)dev->priv);
+ return setflashmode((struct airo_info *)dev->ml_priv);
case AIROFLSHGCHR: /* Get char from aux */
if(comp->len != sizeof(int))
return -EINVAL;
if (copy_from_user(&z,comp->data,comp->len))
return -EFAULT;
- return flashgchar((struct airo_info *)dev->priv,z,8000);
+ return flashgchar((struct airo_info *)dev->ml_priv, z, 8000);
case AIROFLSHPCHR: /* Send char to card. */
if(comp->len != sizeof(int))
return -EINVAL;
if (copy_from_user(&z,comp->data,comp->len))
return -EFAULT;
- return flashpchar((struct airo_info *)dev->priv,z,8000);
+ return flashpchar((struct airo_info *)dev->ml_priv, z, 8000);
case AIROFLPUTBUF: /* Send 32k to card */
- if (!((struct airo_info *)dev->priv)->flash)
+ if (!AIRO_FLASH(dev))
return -ENOMEM;
if(comp->len > FLASHSIZE)
return -EINVAL;
- if(copy_from_user(((struct airo_info *)dev->priv)->flash,comp->data,comp->len))
+ if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len))
return -EFAULT;
- flashputbuf((struct airo_info *)dev->priv);
+ flashputbuf((struct airo_info *)dev->ml_priv);
return 0;
case AIRORESTART:
- if(flashrestart((struct airo_info *)dev->priv,dev))
+ if (flashrestart((struct airo_info *)dev->ml_priv, dev))
return -EIO;
return 0;
}
diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c
deleted file mode 100644
index ce03a2e865fa..000000000000
--- a/drivers/net/wireless/airport.c
+++ /dev/null
@@ -1,286 +0,0 @@
-/* airport.c
- *
- * A driver for "Hermes" chipset based Apple Airport wireless
- * card.
- *
- * Copyright notice & release notes in file orinoco.c
- *
- * Note specific to airport stub:
- *
- * 0.05 : first version of the new split driver
- * 0.06 : fix possible hang on powerup, add sleep support
- */
-
-#define DRIVER_NAME "airport"
-#define PFX DRIVER_NAME ": "
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <asm/pmac_feature.h>
-
-#include "orinoco.h"
-
-#define AIRPORT_IO_LEN (0x1000) /* one page */
-
-struct airport {
- struct macio_dev *mdev;
- void __iomem *vaddr;
- int irq_requested;
- int ndev_registered;
-};
-
-static int
-airport_suspend(struct macio_dev *mdev, pm_message_t state)
-{
- struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err;
-
- printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name);
-
- err = orinoco_lock(priv, &flags);
- if (err) {
- printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n",
- dev->name);
- return 0;
- }
-
- err = __orinoco_down(dev);
- if (err)
- printk(KERN_WARNING "%s: PBOOK_SLEEP_NOW: Error %d downing interface\n",
- dev->name, err);
-
- netif_device_detach(dev);
-
- priv->hw_unavailable++;
-
- orinoco_unlock(priv, &flags);
-
- disable_irq(dev->irq);
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0);
-
- return 0;
-}
-
-static int
-airport_resume(struct macio_dev *mdev)
-{
- struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err;
-
- printk(KERN_DEBUG "%s: Airport waking up\n", dev->name);
-
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1);
- msleep(200);
-
- enable_irq(dev->irq);
-
- err = orinoco_reinit_firmware(dev);
- if (err) {
- printk(KERN_ERR "%s: Error %d re-initializing firmware on PBOOK_WAKE\n",
- dev->name, err);
- return 0;
- }
-
- spin_lock_irqsave(&priv->lock, flags);
-
- netif_device_attach(dev);
-
- priv->hw_unavailable--;
-
- if (priv->open && (! priv->hw_unavailable)) {
- err = __orinoco_up(dev);
- if (err)
- printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n",
- dev->name, err);
- }
-
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
-}
-
-static int
-airport_detach(struct macio_dev *mdev)
-{
- struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
- struct orinoco_private *priv = netdev_priv(dev);
- struct airport *card = priv->card;
-
- if (card->ndev_registered)
- unregister_netdev(dev);
- card->ndev_registered = 0;
-
- if (card->irq_requested)
- free_irq(dev->irq, dev);
- card->irq_requested = 0;
-
- if (card->vaddr)
- iounmap(card->vaddr);
- card->vaddr = NULL;
-
- macio_release_resource(mdev, 0);
-
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0);
- ssleep(1);
-
- macio_set_drvdata(mdev, NULL);
- free_orinocodev(dev);
-
- return 0;
-}
-
-static int airport_hard_reset(struct orinoco_private *priv)
-{
- /* It would be nice to power cycle the Airport for a real hard
- * reset, but for some reason although it appears to
- * re-initialize properly, it falls in a screaming heap
- * shortly afterwards. */
-#if 0
- struct net_device *dev = priv->ndev;
- struct airport *card = priv->card;
-
- /* Vitally important. If we don't do this it seems we get an
- * interrupt somewhere during the power cycle, since
- * hw_unavailable is already set it doesn't get ACKed, we get
- * into an interrupt loop and the PMU decides to turn us
- * off. */
- disable_irq(dev->irq);
-
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0);
- ssleep(1);
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 1);
- ssleep(1);
-
- enable_irq(dev->irq);
- ssleep(1);
-#endif
-
- return 0;
-}
-
-static int
-airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
-{
- struct orinoco_private *priv;
- struct net_device *dev;
- struct airport *card;
- unsigned long phys_addr;
- hermes_t *hw;
-
- if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) {
- printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n");
- return -ENODEV;
- }
-
- /* Allocate space for private device-specific data */
- dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
- airport_hard_reset, NULL);
- if (! dev) {
- printk(KERN_ERR PFX "Cannot allocate network device\n");
- return -ENODEV;
- }
- priv = netdev_priv(dev);
- card = priv->card;
-
- hw = &priv->hw;
- card->mdev = mdev;
-
- if (macio_request_resource(mdev, 0, "airport")) {
- printk(KERN_ERR PFX "can't request IO resource !\n");
- free_orinocodev(dev);
- return -EBUSY;
- }
-
- SET_NETDEV_DEV(dev, &mdev->ofdev.dev);
-
- macio_set_drvdata(mdev, dev);
-
- /* Setup interrupts & base address */
- dev->irq = macio_irq(mdev, 0);
- phys_addr = macio_resource_start(mdev, 0); /* Physical address */
- printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr);
- dev->base_addr = phys_addr;
- card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN);
- if (!card->vaddr) {
- printk(KERN_ERR PFX "ioremap() failed\n");
- goto failed;
- }
-
- hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING);
-
- /* Power up card */
- pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1);
- ssleep(1);
-
- /* Reset it before we get the interrupt */
- hermes_init(hw);
-
- if (request_irq(dev->irq, orinoco_interrupt, 0, dev->name, dev)) {
- printk(KERN_ERR PFX "Couldn't get IRQ %d\n", dev->irq);
- goto failed;
- }
- card->irq_requested = 1;
-
- /* Tell the stack we exist */
- if (register_netdev(dev) != 0) {
- printk(KERN_ERR PFX "register_netdev() failed\n");
- goto failed;
- }
- printk(KERN_DEBUG PFX "Card registered for interface %s\n", dev->name);
- card->ndev_registered = 1;
- return 0;
- failed:
- airport_detach(mdev);
- return -ENODEV;
-} /* airport_attach */
-
-
-static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
- " (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
-MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
-MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
-MODULE_LICENSE("Dual MPL/GPL");
-
-static struct of_device_id airport_match[] =
-{
- {
- .name = "radio",
- },
- {},
-};
-
-MODULE_DEVICE_TABLE (of, airport_match);
-
-static struct macio_driver airport_driver =
-{
- .name = DRIVER_NAME,
- .match_table = airport_match,
- .probe = airport_attach,
- .remove = airport_detach,
- .suspend = airport_suspend,
- .resume = airport_resume,
-};
-
-static int __init
-init_airport(void)
-{
- printk(KERN_DEBUG "%s\n", version);
-
- return macio_register_driver(&airport_driver);
-}
-
-static void __exit
-exit_airport(void)
-{
- return macio_unregister_driver(&airport_driver);
-}
-
-module_init(init_airport);
-module_exit(exit_airport);
diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c
index dec5e874a54d..bfca15da6f0f 100644
--- a/drivers/net/wireless/arlan-main.c
+++ b/drivers/net/wireless/arlan-main.c
@@ -1467,19 +1467,17 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short
else if (hw_dst_addr[1] == 0x40)
printk(KERN_ERR "%s m/bcast 0x0140 \n", dev->name);
while (dmi)
- { if (dmi->dmi_addrlen == 6)
- {
- DECLARE_MAC_BUF(mac);
+ {
+ if (dmi->dmi_addrlen == 6) {
if (arlan_debug & ARLAN_DEBUG_HEADER_DUMP)
- printk(KERN_ERR "%s mcl %s\n",
- dev->name, print_mac(mac, dmi->dmi_addr));
+ printk(KERN_ERR "%s mcl %pM\n",
+ dev->name, dmi->dmi_addr);
for (i = 0; i < 6; i++)
if (dmi->dmi_addr[i] != hw_dst_addr[i])
break;
if (i == 6)
break;
- }
- else
+ } else
printk(KERN_ERR "%s: invalid multicast address length given.\n", dev->name);
dmi = dmi->next;
}
@@ -1512,18 +1510,14 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short
{
char immedDestAddress[6];
char immedSrcAddress[6];
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
memcpy_fromio(immedDestAddress, arlan->immedDestAddress, 6);
memcpy_fromio(immedSrcAddress, arlan->immedSrcAddress, 6);
- printk(KERN_WARNING "%s t %s f %s imd %s ims %s\n",
- dev->name, print_mac(mac, skbtmp),
- print_mac(mac2, &skbtmp[6]),
- print_mac(mac3, immedDestAddress),
- print_mac(mac4, immedSrcAddress));
+ printk(KERN_WARNING "%s t %pM f %pM imd %pM ims %pM\n",
+ dev->name, skbtmp,
+ &skbtmp[6],
+ immedDestAddress,
+ immedSrcAddress);
}
skb->protocol = eth_type_trans(skb, dev);
IFDEBUG(ARLAN_DEBUG_HEADER_DUMP)
@@ -1535,7 +1529,6 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short
printk(KERN_WARNING "arlan kernel pkt type trans %x \n", skb->protocol);
}
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h
index 53ea439aff48..183ffc8e62ca 100644
--- a/drivers/net/wireless/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath5k/ath5k.h
@@ -507,11 +507,15 @@ enum ath5k_tx_queue_id {
#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0004 /* Enable TXEOL interrupt -not used- */
#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0008 /* Enable TXDESC interrupt -not used- */
#define AR5K_TXQ_FLAG_TXURNINT_ENABLE 0x0010 /* Enable TXURN interrupt */
-#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0020 /* Disable random post-backoff */
-#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0040 /* Enable ready time expiry policy (?)*/
-#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0080 /* Enable backoff while bursting */
-#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x0100 /* Disable backoff while bursting */
-#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x0200 /* Enable hw compression -not implemented-*/
+#define AR5K_TXQ_FLAG_CBRORNINT_ENABLE 0x0020 /* Enable CBRORN interrupt */
+#define AR5K_TXQ_FLAG_CBRURNINT_ENABLE 0x0040 /* Enable CBRURN interrupt */
+#define AR5K_TXQ_FLAG_QTRIGINT_ENABLE 0x0080 /* Enable QTRIG interrupt */
+#define AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE 0x0100 /* Enable TXNOFRM interrupt */
+#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0200 /* Disable random post-backoff */
+#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0300 /* Enable ready time expiry policy (?)*/
+#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0800 /* Enable backoff while bursting */
+#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x1000 /* Disable backoff while bursting */
+#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x2000 /* Enable hw compression -not implemented-*/
/*
* A struct to hold tx queue's parameters
@@ -817,13 +821,6 @@ struct ath5k_athchan_2ghz {
return (false); \
} while (0)
-enum ath5k_ant_setting {
- AR5K_ANT_VARIABLE = 0, /* variable by programming */
- AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */
- AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */
- AR5K_ANT_MAX = 3,
-};
-
/*
* Hardware interrupt abstraction
*/
@@ -853,7 +850,7 @@ enum ath5k_ant_setting {
* checked. We should do this with ath5k_hw_update_mib_counters() but
* it seems we should also then do some noise immunity work.
* @AR5K_INT_RXPHY: RX PHY Error
- * @AR5K_INT_RXKCM: ??
+ * @AR5K_INT_RXKCM: RX Key cache miss
* @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a
* beacon that must be handled in software. The alternative is if you
* have VEOL support, in that case you let the hardware deal with things.
@@ -869,7 +866,7 @@ enum ath5k_ant_setting {
* @AR5K_INT_FATAL: Fatal errors were encountered, typically caused by DMA
* errors. These types of errors we can enable seem to be of type
* AR5K_SIMR2_MCABT, AR5K_SIMR2_SSERR and AR5K_SIMR2_DPERR.
- * @AR5K_INT_GLOBAL: Seems to be used to clear and set the IER
+ * @AR5K_INT_GLOBAL: Used to clear and set the IER
* @AR5K_INT_NOCARD: signals the card has been removed
* @AR5K_INT_COMMON: common interrupts shared amogst MACs with the same
* bit value
@@ -881,36 +878,61 @@ enum ath5k_ant_setting {
* MACs.
*/
enum ath5k_int {
- AR5K_INT_RX = 0x00000001, /* Not common */
+ AR5K_INT_RXOK = 0x00000001,
AR5K_INT_RXDESC = 0x00000002,
+ AR5K_INT_RXERR = 0x00000004,
AR5K_INT_RXNOFRM = 0x00000008,
AR5K_INT_RXEOL = 0x00000010,
AR5K_INT_RXORN = 0x00000020,
- AR5K_INT_TX = 0x00000040, /* Not common */
+ AR5K_INT_TXOK = 0x00000040,
AR5K_INT_TXDESC = 0x00000080,
+ AR5K_INT_TXERR = 0x00000100,
+ AR5K_INT_TXNOFRM = 0x00000200,
+ AR5K_INT_TXEOL = 0x00000400,
AR5K_INT_TXURN = 0x00000800,
AR5K_INT_MIB = 0x00001000,
+ AR5K_INT_SWI = 0x00002000,
AR5K_INT_RXPHY = 0x00004000,
AR5K_INT_RXKCM = 0x00008000,
AR5K_INT_SWBA = 0x00010000,
+ AR5K_INT_BRSSI = 0x00020000,
AR5K_INT_BMISS = 0x00040000,
- AR5K_INT_BNR = 0x00100000, /* Not common */
- AR5K_INT_GPIO = 0x01000000,
- AR5K_INT_FATAL = 0x40000000, /* Not common */
- AR5K_INT_GLOBAL = 0x80000000,
-
- AR5K_INT_COMMON = AR5K_INT_RXNOFRM
- | AR5K_INT_RXDESC
- | AR5K_INT_RXEOL
- | AR5K_INT_RXORN
- | AR5K_INT_TXURN
- | AR5K_INT_TXDESC
- | AR5K_INT_MIB
- | AR5K_INT_RXPHY
- | AR5K_INT_RXKCM
- | AR5K_INT_SWBA
- | AR5K_INT_BMISS
- | AR5K_INT_GPIO,
+ AR5K_INT_FATAL = 0x00080000, /* Non common */
+ AR5K_INT_BNR = 0x00100000, /* Non common */
+ AR5K_INT_TIM = 0x00200000, /* Non common */
+ AR5K_INT_DTIM = 0x00400000, /* Non common */
+ AR5K_INT_DTIM_SYNC = 0x00800000, /* Non common */
+ AR5K_INT_GPIO = 0x01000000,
+ AR5K_INT_BCN_TIMEOUT = 0x02000000, /* Non common */
+ AR5K_INT_CAB_TIMEOUT = 0x04000000, /* Non common */
+ AR5K_INT_RX_DOPPLER = 0x08000000, /* Non common */
+ AR5K_INT_QCBRORN = 0x10000000, /* Non common */
+ AR5K_INT_QCBRURN = 0x20000000, /* Non common */
+ AR5K_INT_QTRIG = 0x40000000, /* Non common */
+ AR5K_INT_GLOBAL = 0x80000000,
+
+ AR5K_INT_COMMON = AR5K_INT_RXOK
+ | AR5K_INT_RXDESC
+ | AR5K_INT_RXERR
+ | AR5K_INT_RXNOFRM
+ | AR5K_INT_RXEOL
+ | AR5K_INT_RXORN
+ | AR5K_INT_TXOK
+ | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR
+ | AR5K_INT_TXNOFRM
+ | AR5K_INT_TXEOL
+ | AR5K_INT_TXURN
+ | AR5K_INT_MIB
+ | AR5K_INT_SWI
+ | AR5K_INT_RXPHY
+ | AR5K_INT_RXKCM
+ | AR5K_INT_SWBA
+ | AR5K_INT_BRSSI
+ | AR5K_INT_BMISS
+ | AR5K_INT_GPIO
+ | AR5K_INT_GLOBAL,
+
AR5K_INT_NOCARD = 0xffffffff
};
@@ -1030,6 +1052,7 @@ struct ath5k_hw {
bool ah_calibration;
bool ah_running;
bool ah_single_chip;
+ bool ah_combined_mic;
enum ath5k_rfgain ah_rf_gain;
u32 ah_mac_srev;
@@ -1064,10 +1087,11 @@ struct ath5k_hw {
u8 ah_sta_id[ETH_ALEN];
- /* Current BSSID we are trying to assoc to / creating.
+ /* Current BSSID we are trying to assoc to / create.
* This is passed by mac80211 on config_interface() and cached here for
* use in resets */
u8 ah_bssid[ETH_ALEN];
+ u8 ah_bssid_mask[ETH_ALEN];
u32 ah_gpio[AR5K_MAX_GPIO];
int ah_gpio_npins;
@@ -1081,6 +1105,11 @@ struct ath5k_hw {
u32 ah_txq_imr_txurn;
u32 ah_txq_imr_txdesc;
u32 ah_txq_imr_txeol;
+ u32 ah_txq_imr_cbrorn;
+ u32 ah_txq_imr_cbrurn;
+ u32 ah_txq_imr_qtrig;
+ u32 ah_txq_imr_nofrm;
+ u32 ah_txq_isr;
u32 *ah_rf_banks;
size_t ah_rf_banks_size;
struct ath5k_gain ah_gain;
@@ -1321,4 +1350,9 @@ static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits)
return retval;
}
+static inline int ath5k_pad_size(int hdrlen)
+{
+ return (hdrlen < 24) ? 0 : hdrlen & 3;
+}
+
#endif
diff --git a/drivers/net/wireless/ath5k/attach.c b/drivers/net/wireless/ath5k/attach.c
index 51d569883cdd..dea378f76731 100644
--- a/drivers/net/wireless/ath5k/attach.c
+++ b/drivers/net/wireless/ath5k/attach.c
@@ -106,7 +106,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
{
struct ath5k_hw *ah;
struct pci_dev *pdev = sc->pdev;
- u8 mac[ETH_ALEN];
+ u8 mac[ETH_ALEN] = {};
int ret;
u32 srev;
@@ -317,15 +317,15 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
goto err_free;
}
- /* Set MAC address */
- ret = ath5k_eeprom_read_mac(ah, mac);
- if (ret) {
- ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
- sc->pdev->device);
- goto err_free;
+ if (srev >= AR5K_SREV_AR2414) {
+ ah->ah_combined_mic = true;
+ AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
+ AR5K_MISC_MODE_COMBINED_MIC);
}
+ /* MAC address is cleared until add_interface */
ath5k_hw_set_lladdr(ah, mac);
+
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
memset(ah->ah_bssid, 0xff, ETH_ALEN);
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index 2d14255eb103..4af2607deec0 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -60,6 +60,9 @@
#include "debug.h"
static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/******************\
@@ -197,7 +200,7 @@ static int ath5k_pci_resume(struct pci_dev *pdev);
#endif /* CONFIG_PM */
static struct pci_driver ath5k_pci_driver = {
- .name = "ath5k_pci",
+ .name = KBUILD_MODNAME,
.id_table = ath5k_pci_id_table,
.probe = ath5k_pci_probe,
.remove = __devexit_p(ath5k_pci_remove),
@@ -219,8 +222,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static void ath5k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
-static int ath5k_config(struct ieee80211_hw *hw,
- struct ieee80211_conf *conf);
+static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
static int ath5k_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf);
@@ -238,7 +240,7 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ieee80211_hw *hw,
+static int ath5k_beacon_update(struct ath5k_softc *sc,
struct sk_buff *skb);
static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -548,8 +550,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
/* set up multi-rate retry capabilities */
if (sc->ah->ah_version == AR5K_AR5212) {
- hw->max_altrates = 3;
- hw->max_altrate_tries = 11;
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
}
/* Finish private driver data initialization */
@@ -711,7 +713,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
- u8 mac[ETH_ALEN];
+ u8 mac[ETH_ALEN] = {};
int ret;
ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
@@ -781,7 +783,13 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
- ath5k_hw_get_lladdr(ah, mac);
+ ret = ath5k_eeprom_read_mac(ah, mac);
+ if (ret) {
+ ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
+ sc->pdev->device);
+ goto err_queues;
+ }
+
SET_IEEE80211_PERM_ADDR(hw, mac);
/* All MAC address bits matter for ACKs */
memset(sc->bssidmask, 0xff, ETH_ALEN);
@@ -1188,7 +1196,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
(sc->power_level * 2),
ieee80211_get_tx_rate(sc->hw, info)->hw_value,
- info->control.retry_limit, keyidx, 0, flags, 0, 0);
+ info->control.rates[0].count, keyidx, 0, flags, 0, 0);
if (ret)
goto err_unmap;
@@ -1200,7 +1208,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
break;
mrr_rate[i] = rate->hw_value;
- mrr_tries[i] = info->control.retries[i].limit;
+ mrr_tries[i] = info->control.rates[i + 1].count;
}
ah->ah_setup_mrr_tx_desc(ah, ds,
@@ -1660,7 +1668,7 @@ ath5k_tasklet_rx(unsigned long data)
struct ath5k_desc *ds;
int ret;
int hdrlen;
- int pad;
+ int padsize;
spin_lock(&sc->rxbuflock);
if (list_empty(&sc->rxbuf)) {
@@ -1745,16 +1753,19 @@ accept:
skb_put(skb, rs.rs_datalen);
- /*
- * the hardware adds a padding to 4 byte boundaries between
- * the header and the payload data if the header length is
- * not multiples of 4 - remove it
- */
+ /* The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = (4 - hdrlen % 4) % 4; However, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit. In addition, we must
+ * not try to remove padding from short control frames that do
+ * not have payload. */
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- if (hdrlen & 3) {
- pad = hdrlen % 4;
- memmove(skb->data + pad, skb->data, hdrlen);
- skb_pull(skb, pad);
+ padsize = ath5k_pad_size(hdrlen);
+ if (padsize) {
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
}
/*
@@ -1785,7 +1796,17 @@ accept:
rxs.noise = sc->ah->ah_noise_floor;
rxs.signal = rxs.noise + rs.rs_rssi;
- rxs.qual = rs.rs_rssi * 100 / 64;
+
+ /* An rssi of 35 indicates you should be able use
+ * 54 Mbps reliably. A more elaborate scheme can be used
+ * here but it requires a map of SNR/throughput for each
+ * possible mode used */
+ rxs.qual = rs.rs_rssi * 100 / 35;
+
+ /* rssi can be more than 35 though, anything above that
+ * should be considered at 100% */
+ if (rxs.qual > 100)
+ rxs.qual = 100;
rxs.antenna = rs.rs_antenna;
rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
@@ -1846,30 +1867,26 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
PCI_DMA_TODEVICE);
- memset(&info->status, 0, sizeof(info->status));
- info->tx_rate_idx = ath5k_hw_to_driver_rix(sc,
- ts.ts_rate[ts.ts_final_idx]);
- info->status.retry_count = ts.ts_longretry;
-
+ ieee80211_tx_info_clear_status(info);
for (i = 0; i < 4; i++) {
- struct ieee80211_tx_altrate *r =
- &info->status.retries[i];
+ struct ieee80211_tx_rate *r =
+ &info->status.rates[i];
if (ts.ts_rate[i]) {
- r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
- r->limit = ts.ts_retry[i];
+ r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
+ r->count = ts.ts_retry[i];
} else {
- r->rate_idx = -1;
- r->limit = 0;
+ r->idx = -1;
+ r->count = 0;
}
}
- info->status.excessive_retries = 0;
+ /* count the successful attempt as well */
+ info->status.rates[ts.ts_final_idx].count++;
+
if (unlikely(ts.ts_status)) {
sc->ll_stats.dot11ACKFailureCount++;
- if (ts.ts_status & AR5K_TXERR_XRETRY)
- info->status.excessive_retries = 1;
- else if (ts.ts_status & AR5K_TXERR_FILT)
+ if (ts.ts_status & AR5K_TXERR_FILT)
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
} else {
info->flags |= IEEE80211_TX_STAT_ACK;
@@ -2143,8 +2160,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
*
* In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
* interrupts to detect TSF updates only.
- *
- * AP mode is missing.
*/
static void
ath5k_beacon_config(struct ath5k_softc *sc)
@@ -2157,7 +2172,9 @@ ath5k_beacon_config(struct ath5k_softc *sc)
if (sc->opmode == NL80211_IFTYPE_STATION) {
sc->imask |= AR5K_INT_BMISS;
- } else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+ } else if (sc->opmode == NL80211_IFTYPE_ADHOC ||
+ sc->opmode == NL80211_IFTYPE_MESH_POINT ||
+ sc->opmode == NL80211_IFTYPE_AP) {
/*
* In IBSS mode we use a self-linked tx descriptor and let the
* hardware send the beacons automatically. We have to load it
@@ -2169,13 +2186,15 @@ ath5k_beacon_config(struct ath5k_softc *sc)
sc->imask |= AR5K_INT_SWBA;
- if (ath5k_hw_hasveol(ah)) {
- spin_lock(&sc->block);
- ath5k_beacon_send(sc);
- spin_unlock(&sc->block);
- }
+ if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+ if (ath5k_hw_hasveol(ah)) {
+ spin_lock(&sc->block);
+ ath5k_beacon_send(sc);
+ spin_unlock(&sc->block);
+ }
+ } else
+ ath5k_beacon_update_timers(sc, -1);
}
- /* TODO else AP */
ath5k_hw_set_imr(ah, sc->imask);
}
@@ -2215,9 +2234,9 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume)
*/
sc->curchan = sc->hw->conf.channel;
sc->curband = &sc->sbands[sc->curchan->band];
- sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL |
- AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL |
- AR5K_INT_MIB;
+ sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+ AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+ AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
ret = ath5k_reset(sc, false, false);
if (ret)
goto done;
@@ -2409,9 +2428,10 @@ ath5k_intr(int irq, void *dev_id)
/* bump tx trigger level */
ath5k_hw_update_tx_triglevel(ah, true);
}
- if (status & AR5K_INT_RX)
+ if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
tasklet_schedule(&sc->rxtq);
- if (status & AR5K_INT_TX)
+ if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+ | AR5K_INT_TXERR | AR5K_INT_TXEOL))
tasklet_schedule(&sc->txtq);
if (status & AR5K_INT_BMISS) {
}
@@ -2527,8 +2547,7 @@ ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
led->led_dev.brightness_set = ath5k_led_brightness_set;
err = led_classdev_register(&sc->pdev->dev, &led->led_dev);
- if (err)
- {
+ if (err) {
ATH5K_WARN(sc, "could not register LED %s\n", name);
led->sc = NULL;
}
@@ -2607,7 +2626,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ath5k_buf *bf;
unsigned long flags;
int hdrlen;
- int pad;
+ int padsize;
ath5k_debug_dump_skb(sc, skb, "TX ", 1);
@@ -2619,15 +2638,16 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* if this is not the case we add the padding after the header
*/
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- if (hdrlen & 3) {
- pad = hdrlen % 4;
- if (skb_headroom(skb) < pad) {
+ padsize = ath5k_pad_size(hdrlen);
+ if (padsize) {
+
+ if (skb_headroom(skb) < padsize) {
ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
- " headroom to pad %d\n", hdrlen, pad);
+ " headroom to pad %d\n", hdrlen, padsize);
return -1;
}
- skb_push(skb, pad);
- memmove(skb->data, skb->data+pad, hdrlen);
+ skb_push(skb, padsize);
+ memmove(skb->data, skb->data+padsize, hdrlen);
}
spin_lock_irqsave(&sc->txbuflock, flags);
@@ -2746,8 +2766,10 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
sc->vif = conf->vif;
switch (conf->type) {
+ case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_MONITOR:
sc->opmode = conf->type;
break;
@@ -2759,6 +2781,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
/* Set to a reasonable value. Note that this will
* be set to mac80211's value at ath5k_config(). */
sc->bintval = 1000;
+ ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
ret = 0;
end:
@@ -2771,11 +2794,13 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ath5k_softc *sc = hw->priv;
+ u8 mac[ETH_ALEN] = {};
mutex_lock(&sc->lock);
if (sc->vif != conf->vif)
goto end;
+ ath5k_hw_set_lladdr(sc->ah, mac);
sc->vif = NULL;
end:
mutex_unlock(&sc->lock);
@@ -2785,10 +2810,10 @@ end:
* TODO: Phy disable/diversity etc
*/
static int
-ath5k_config(struct ieee80211_hw *hw,
- struct ieee80211_conf *conf)
+ath5k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath5k_softc *sc = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
sc->bintval = conf->beacon_int;
sc->power_level = conf->power_level;
@@ -2809,7 +2834,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
ret = -EIO;
goto unlock;
}
- if (conf->bssid) {
+ if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) {
/* Cache for later use during resets */
memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN);
/* XXX: assoc id is set to 0 for now, mac80211 doesn't have
@@ -2817,18 +2842,17 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
mmiowb();
}
-
if (conf->changed & IEEE80211_IFCC_BEACON &&
- vif->type == NL80211_IFTYPE_ADHOC) {
+ (vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_MESH_POINT ||
+ vif->type == NL80211_IFTYPE_AP)) {
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
if (!beacon) {
ret = -ENOMEM;
goto unlock;
}
- /* call old handler for now */
- ath5k_beacon_update(hw, beacon);
+ ath5k_beacon_update(sc, beacon);
}
-
mutex_unlock(&sc->lock);
return ath5k_reset_wake(sc);
@@ -2888,9 +2912,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
if (*new_flags & FIF_PROMISC_IN_BSS) {
rfilt |= AR5K_RX_FILTER_PROM;
__set_bit(ATH_STAT_PROMISC, sc->status);
- }
- else
+ } else {
__clear_bit(ATH_STAT_PROMISC, sc->status);
+ }
}
/* Note, AR5K_RX_FILTER_MCAST is already enabled */
@@ -2948,12 +2972,15 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
test_bit(ATH_STAT_PROMISC, sc->status))
rfilt |= AR5K_RX_FILTER_PROM;
if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) ||
- sc->opmode == NL80211_IFTYPE_ADHOC) {
+ sc->opmode == NL80211_IFTYPE_ADHOC ||
+ sc->opmode == NL80211_IFTYPE_AP)
rfilt |= AR5K_RX_FILTER_BEACON;
- }
+ if (sc->opmode == NL80211_IFTYPE_MESH_POINT)
+ rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON |
+ AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM;
/* Set filters */
- ath5k_hw_set_rx_filter(ah,rfilt);
+ ath5k_hw_set_rx_filter(ah, rfilt);
/* Set multicast bits */
ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
@@ -2970,12 +2997,13 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ath5k_softc *sc = hw->priv;
int ret = 0;
- switch(key->alg) {
+ if (modparam_nohwcrypt)
+ return -EOPNOTSUPP;
+
+ switch (key->alg) {
case ALG_WEP:
- /* XXX: fix hardware encryption, its not working. For now
- * allow software encryption */
- /* break; */
case ALG_TKIP:
+ break;
case ALG_CCMP:
return -EOPNOTSUPP;
default:
@@ -2994,6 +3022,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
__set_bit(key->keyidx, sc->keymap);
key->hw_key_idx = key->keyidx;
+ key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
+ IEEE80211_KEY_FLAG_GENERATE_MMIC);
break;
case DISABLE_KEY:
ath5k_hw_reset_key(sc->ah, key->keyidx);
@@ -3060,19 +3090,13 @@ ath5k_reset_tsf(struct ieee80211_hw *hw)
}
static int
-ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
+ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
{
- struct ath5k_softc *sc = hw->priv;
unsigned long flags;
int ret;
ath5k_debug_dump_skb(sc, skb, "BC ", 1);
- if (sc->opmode != NL80211_IFTYPE_ADHOC) {
- ret = -EIO;
- goto end;
- }
-
spin_lock_irqsave(&sc->block, flags);
ath5k_txbuf_free(sc, sc->bbuf);
sc->bbuf->skb = skb;
@@ -3085,7 +3109,6 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
mmiowb();
}
-end:
return ret;
}
static void
diff --git a/drivers/net/wireless/ath5k/desc.c b/drivers/net/wireless/ath5k/desc.c
index 5e362a7a3620..b40a9287a39a 100644
--- a/drivers/net/wireless/ath5k/desc.c
+++ b/drivers/net/wireless/ath5k/desc.c
@@ -71,7 +71,7 @@ ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
/* Verify and set frame length */
/* remove padding we might have added before */
- frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
+ frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
@@ -202,7 +202,7 @@ static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
/* Verify and set frame length */
/* remove padding we might have added before */
- frame_len = pkt_len - (hdr_len & 3) + FCS_LEN;
+ frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
diff --git a/drivers/net/wireless/ath5k/dma.c b/drivers/net/wireless/ath5k/dma.c
index 7adceb2c7fab..7e2b1a67e5da 100644
--- a/drivers/net/wireless/ath5k/dma.c
+++ b/drivers/net/wireless/ath5k/dma.c
@@ -472,9 +472,6 @@ bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
*
* NOTE: We use read-and-clear register, so after this function is called ISR
* is zeroed.
- *
- * XXX: Why filter interrupts in sw with interrupt_mask ? No benefit at all
- * plus it can be misleading (one might thing that we save interrupts this way)
*/
int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
{
@@ -494,11 +491,16 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
}
} else {
/*
- * Read interrupt status from the Read-And-Clear
- * shadow register.
+ * Read interrupt status from Interrupt
+ * Status Register shadow copy (Read And Clear)
+ *
* Note: PISR/SISR Not available on 5210
*/
data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
+ if (unlikely(data == AR5K_INT_NOCARD)) {
+ *interrupt_mask = data;
+ return -ENODEV;
+ }
}
/*
@@ -506,17 +508,9 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
*/
*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
- if (unlikely(data == AR5K_INT_NOCARD))
- return -ENODEV;
-
- if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR))
- *interrupt_mask |= AR5K_INT_RX;
-
- if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR
- | AR5K_ISR_TXDESC | AR5K_ISR_TXEOL))
- *interrupt_mask |= AR5K_INT_TX;
-
if (ah->ah_version != AR5K_AR5210) {
+ u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2);
+
/*HIU = Host Interface Unit (PCI etc)*/
if (unlikely(data & (AR5K_ISR_HIUERR)))
*interrupt_mask |= AR5K_INT_FATAL;
@@ -524,24 +518,93 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
/*Beacon Not Ready*/
if (unlikely(data & (AR5K_ISR_BNR)))
*interrupt_mask |= AR5K_INT_BNR;
- }
- /*
- * XXX: BMISS interrupts may occur after association.
- * I found this on 5210 code but it needs testing. If this is
- * true we should disable them before assoc and re-enable them
- * after a successfull assoc + some jiffies.
- */
-#if 0
- interrupt_mask &= ~AR5K_INT_BMISS;
-#endif
+ if (unlikely(sisr2 & (AR5K_SISR2_SSERR |
+ AR5K_SISR2_DPERR |
+ AR5K_SISR2_MCABT)))
+ *interrupt_mask |= AR5K_INT_FATAL;
+
+ if (data & AR5K_ISR_TIM)
+ *interrupt_mask |= AR5K_INT_TIM;
+
+ if (data & AR5K_ISR_BCNMISC) {
+ if (sisr2 & AR5K_SISR2_TIM)
+ *interrupt_mask |= AR5K_INT_TIM;
+ if (sisr2 & AR5K_SISR2_DTIM)
+ *interrupt_mask |= AR5K_INT_DTIM;
+ if (sisr2 & AR5K_SISR2_DTIM_SYNC)
+ *interrupt_mask |= AR5K_INT_DTIM_SYNC;
+ if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
+ *interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
+ if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
+ *interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
+ }
+
+ if (data & AR5K_ISR_RXDOPPLER)
+ *interrupt_mask |= AR5K_INT_RX_DOPPLER;
+ if (data & AR5K_ISR_QCBRORN) {
+ *interrupt_mask |= AR5K_INT_QCBRORN;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+ AR5K_SISR3_QCBRORN);
+ }
+ if (data & AR5K_ISR_QCBRURN) {
+ *interrupt_mask |= AR5K_INT_QCBRURN;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
+ AR5K_SISR3_QCBRURN);
+ }
+ if (data & AR5K_ISR_QTRIG) {
+ *interrupt_mask |= AR5K_INT_QTRIG;
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR4),
+ AR5K_SISR4_QTRIG);
+ }
+
+ if (data & AR5K_ISR_TXOK)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+ AR5K_SISR0_QCU_TXOK);
+
+ if (data & AR5K_ISR_TXDESC)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
+ AR5K_SISR0_QCU_TXDESC);
+
+ if (data & AR5K_ISR_TXERR)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+ AR5K_SISR1_QCU_TXERR);
+
+ if (data & AR5K_ISR_TXEOL)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
+ AR5K_SISR1_QCU_TXEOL);
+
+ if (data & AR5K_ISR_TXURN)
+ ah->ah_txq_isr |= AR5K_REG_MS(
+ ath5k_hw_reg_read(ah, AR5K_RAC_SISR2),
+ AR5K_SISR2_QCU_TXURN);
+ } else {
+ if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
+ | AR5K_ISR_HIUERR | AR5K_ISR_DPERR)))
+ *interrupt_mask |= AR5K_INT_FATAL;
+
+ /*
+ * XXX: BMISS interrupts may occur after association.
+ * I found this on 5210 code but it needs testing. If this is
+ * true we should disable them before assoc and re-enable them
+ * after a successfull assoc + some jiffies.
+ interrupt_mask &= ~AR5K_INT_BMISS;
+ */
+ }
/*
* In case we didn't handle anything,
* print the register value.
*/
if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
- ATH5K_PRINTF("0x%08x\n", data);
+ ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr);
return 0;
}
@@ -560,14 +623,17 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
{
enum ath5k_int old_mask, int_mask;
+ old_mask = ah->ah_imr;
+
/*
* Disable card interrupts to prevent any race conditions
- * (they will be re-enabled afterwards).
+ * (they will be re-enabled afterwards if AR5K_INT GLOBAL
+ * is set again on the new mask).
*/
- ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
- ath5k_hw_reg_read(ah, AR5K_IER);
-
- old_mask = ah->ah_imr;
+ if (old_mask & AR5K_INT_GLOBAL) {
+ ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
+ ath5k_hw_reg_read(ah, AR5K_IER);
+ }
/*
* Add additional, chipset-dependent interrupt mask flags
@@ -575,30 +641,64 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
*/
int_mask = new_mask & AR5K_INT_COMMON;
- if (new_mask & AR5K_INT_RX)
- int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN |
- AR5K_IMR_RXDESC;
-
- if (new_mask & AR5K_INT_TX)
- int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC |
- AR5K_IMR_TXURN;
-
if (ah->ah_version != AR5K_AR5210) {
+ /* Preserve per queue TXURN interrupt mask */
+ u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
+ & AR5K_SIMR2_QCU_TXURN;
+
if (new_mask & AR5K_INT_FATAL) {
int_mask |= AR5K_IMR_HIUERR;
- AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_MCABT |
- AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR);
+ simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
+ | AR5K_SIMR2_DPERR);
}
+
+ /*Beacon Not Ready*/
+ if (new_mask & AR5K_INT_BNR)
+ int_mask |= AR5K_INT_BNR;
+
+ if (new_mask & AR5K_INT_TIM)
+ int_mask |= AR5K_IMR_TIM;
+
+ if (new_mask & AR5K_INT_TIM)
+ simr2 |= AR5K_SISR2_TIM;
+ if (new_mask & AR5K_INT_DTIM)
+ simr2 |= AR5K_SISR2_DTIM;
+ if (new_mask & AR5K_INT_DTIM_SYNC)
+ simr2 |= AR5K_SISR2_DTIM_SYNC;
+ if (new_mask & AR5K_INT_BCN_TIMEOUT)
+ simr2 |= AR5K_SISR2_BCN_TIMEOUT;
+ if (new_mask & AR5K_INT_CAB_TIMEOUT)
+ simr2 |= AR5K_SISR2_CAB_TIMEOUT;
+
+ if (new_mask & AR5K_INT_RX_DOPPLER)
+ int_mask |= AR5K_IMR_RXDOPPLER;
+
+ /* Note: Per queue interrupt masks
+ * are set via reset_tx_queue (qcu.c) */
+ ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
+ ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
+
+ } else {
+ if (new_mask & AR5K_INT_FATAL)
+ int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
+ | AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
+
+ ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
}
- ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
+ /* If RXNOFRM interrupt is masked disable it
+ * by setting AR5K_RXNOFRM to zero */
+ if (!(new_mask & AR5K_INT_RXNOFRM))
+ ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
/* Store new interrupt mask */
ah->ah_imr = new_mask;
- /* ..re-enable interrupts */
- ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
- ath5k_hw_reg_read(ah, AR5K_IER);
+ /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
+ if (new_mask & AR5K_INT_GLOBAL) {
+ ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
+ ath5k_hw_reg_read(ah, AR5K_IER);
+ }
return old_mask;
}
diff --git a/drivers/net/wireless/ath5k/eeprom.c b/drivers/net/wireless/ath5k/eeprom.c
index a883839b6a9f..1cb7edfae625 100644
--- a/drivers/net/wireless/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath5k/eeprom.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -63,8 +64,8 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data)
/*
* Translate binary channel representation in EEPROM to frequency
*/
-static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin,
- unsigned int mode)
+static u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin,
+ unsigned int mode)
{
u16 val;
@@ -72,13 +73,13 @@ static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin,
return bin;
if (mode == AR5K_EEPROM_MODE_11A) {
- if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
+ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
val = (5 * bin) + 4800;
else
val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 :
(bin * 10) + 5100;
} else {
- if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2)
+ if (ee->ee_version > AR5K_EEPROM_VERSION_3_2)
val = bin + 2300;
else
val = bin + 2400;
@@ -88,6 +89,71 @@ static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin,
}
/*
+ * Initialize eeprom & capabilities structs
+ */
+static int
+ath5k_eeprom_init_header(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ int ret;
+ u16 val;
+
+ /* Initial TX thermal adjustment values */
+ ee->ee_tx_clip = 4;
+ ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
+ ee->ee_gain_select = 1;
+
+ /*
+ * Read values from EEPROM and store them in the capability structure
+ */
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
+
+ /* Return if we have an old EEPROM */
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
+ return 0;
+
+#ifdef notyet
+ /*
+ * Validate the checksum of the EEPROM date. There are some
+ * devices with invalid EEPROMs.
+ */
+ for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) {
+ AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
+ cksum ^= val;
+ }
+ if (cksum != AR5K_EEPROM_INFO_CKSUM) {
+ ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum);
+ return -EIO;
+ }
+#endif
+
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
+ ee_ant_gain);
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
+ AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
+ }
+
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
+ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
+ ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
+ ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
+
+ AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
+ ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
+ ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
+ }
+
+ return 0;
+}
+
+
+/*
* Read antenna infos from eeprom
*/
static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
@@ -100,7 +166,7 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
AR5K_EEPROM_READ(o++, val);
ee->ee_switch_settling[mode] = (val >> 8) & 0x7f;
- ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f;
+ ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f;
ee->ee_ant_control[mode][i] = (val << 4) & 0x3f;
AR5K_EEPROM_READ(o++, val);
@@ -157,6 +223,30 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
u16 val;
int ret;
+ ee->ee_n_piers[mode] = 0;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
+ switch(mode) {
+ case AR5K_EEPROM_MODE_11A:
+ ee->ee_ob[mode][3] = (val >> 5) & 0x7;
+ ee->ee_db[mode][3] = (val >> 2) & 0x7;
+ ee->ee_ob[mode][2] = (val << 1) & 0x7;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
+ ee->ee_db[mode][2] = (val >> 12) & 0x7;
+ ee->ee_ob[mode][1] = (val >> 9) & 0x7;
+ ee->ee_db[mode][1] = (val >> 6) & 0x7;
+ ee->ee_ob[mode][0] = (val >> 3) & 0x7;
+ ee->ee_db[mode][0] = val & 0x7;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ case AR5K_EEPROM_MODE_11B:
+ ee->ee_ob[mode][1] = (val >> 4) & 0x7;
+ ee->ee_db[mode][1] = val & 0x7;
+ break;
+ }
+
AR5K_EEPROM_READ(o++, val);
ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff;
ee->ee_thr_62[mode] = val & 0xff;
@@ -209,8 +299,11 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
AR5K_EEPROM_READ(o++, val);
ee->ee_i_gain[mode] |= (val << 3) & 0x38;
- if (mode == AR5K_EEPROM_MODE_11G)
+ if (mode == AR5K_EEPROM_MODE_11G) {
ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff;
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6)
+ ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
+ }
}
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 &&
@@ -219,10 +312,77 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
}
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 &&
- mode == AR5K_EEPROM_MODE_11G)
- ee->ee_scaled_cck_delta = (val >> 11) & 0x1f;
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0)
+ goto done;
+
+ switch(mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1)
+ break;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_margin_tx_rx[mode] = val & 0x3f;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ AR5K_EEPROM_READ(o++, val);
+
+ ee->ee_pwr_cal_b[0].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ ee->ee_pwr_cal_b[1].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+ if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_pwr_cal_b[2].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ AR5K_EEPROM_READ(o++, val);
+
+ ee->ee_pwr_cal_g[0].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ ee->ee_pwr_cal_g[1].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+ if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_turbo_max_power[mode] = val & 0x7f;
+ ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_pwr_cal_g[2].freq =
+ ath5k_eeprom_bin2freq(ee, val & 0xff, mode);
+ if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS)
+ ee->ee_n_piers[mode]++;
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
+ ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
+ ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_cck_ofdm_gain_delta = val & 0xff;
+ }
+ break;
+ }
+
+done:
/* return new offset */
*offset = o;
@@ -230,204 +390,944 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
}
/*
- * Initialize eeprom & capabilities structs
+ * Read turbo mode information on newer EEPROM versions
*/
-int ath5k_eeprom_init(struct ath5k_hw *ah)
+static int
+ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah,
+ u32 *offset, unsigned int mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
- unsigned int mode, i;
- int ret;
- u32 offset;
+ u32 o = *offset;
u16 val;
+ int ret;
- /* Initial TX thermal adjustment values */
- ee->ee_tx_clip = 4;
- ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
- ee->ee_gain_select = 1;
+ if (ee->ee_version < AR5K_EEPROM_VERSION_5_0)
+ return 0;
- /*
- * Read values from EEPROM and store them in the capability structure
- */
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic);
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect);
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain);
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version);
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header);
+ switch (mode){
+ case AR5K_EEPROM_MODE_11A:
+ ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f;
- /* Return if we have an old EEPROM */
- if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
- return 0;
+ ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3;
+ ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f;
+
+ ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7;
+ ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff;
+
+ if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >=2)
+ ee->ee_pd_gain_overlap = (val >> 9) & 0xf;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f;
+
+ ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1;
+ ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f;
+
+ ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f;
+ AR5K_EEPROM_READ(o++, val);
+ ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5;
+ ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff;
+ break;
+ }
+
+ /* return new offset */
+ *offset = o;
+
+ return 0;
+}
+
+
+static int
+ath5k_eeprom_init_modes(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 mode_offset[3];
+ unsigned int mode;
+ u32 offset;
+ int ret;
-#ifdef notyet
/*
- * Validate the checksum of the EEPROM date. There are some
- * devices with invalid EEPROMs.
+ * Get values for all modes
*/
- for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) {
- AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val);
- cksum ^= val;
+ mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version);
+ mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version);
+ mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version);
+
+ ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] =
+ AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+ offset = mode_offset[mode];
+
+ ret = ath5k_eeprom_read_ants(ah, &offset, mode);
+ if (ret)
+ return ret;
+
+ ret = ath5k_eeprom_read_modes(ah, &offset, mode);
+ if (ret)
+ return ret;
+
+ ret = ath5k_eeprom_read_turbo_modes(ah, &offset, mode);
+ if (ret)
+ return ret;
}
- if (cksum != AR5K_EEPROM_INFO_CKSUM) {
- ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum);
- return -EIO;
+
+ /* override for older eeprom versions for better performance */
+ if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) {
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15;
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28;
+ ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28;
}
-#endif
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
- ee_ant_gain);
+ return 0;
+}
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
- AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
- }
+static inline void
+ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
+{
+ const static u16 intercepts3[] =
+ { 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
+ const static u16 intercepts3_2[] =
+ { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
+ const u16 *ip;
+ int i;
+
+ if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
+ ip = intercepts3_2;
+ else
+ ip = intercepts3;
- if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
- AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val);
- ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7;
- ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7;
+ for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
+ *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100;
+}
- AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val);
- ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7;
- ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7;
+static inline int
+ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
+ struct ath5k_chan_pcal_info *pc, u8 *count)
+{
+ int o = *offset;
+ int i = 0;
+ u8 f1, f2;
+ int ret;
+ u16 val;
+
+ while(i < max) {
+ AR5K_EEPROM_READ(o++, val);
+
+ f1 = (val >> 8) & 0xff;
+ f2 = val & 0xff;
+
+ if (f1)
+ pc[i++].freq = f1;
+
+ if (f2)
+ pc[i++].freq = f2;
+
+ if (!f1 || !f2)
+ break;
}
+ *offset = o;
+ *count = i;
- /*
- * Get conformance test limit values
- */
- offset = AR5K_EEPROM_CTL(ah->ah_ee_version);
- ee->ee_ctls = AR5K_EEPROM_N_CTLS(ah->ah_ee_version);
+ return 0;
+}
+
+static int
+ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a;
+ int i, ret;
+ u16 val;
+ u8 mask;
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+ ath5k_eeprom_read_freq_list(ah, &offset,
+ AR5K_EEPROM_N_5GHZ_CHAN, pcal,
+ &ee->ee_n_piers[AR5K_EEPROM_MODE_11A]);
+ } else {
+ mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version);
- for (i = 0; i < ee->ee_ctls; i++) {
AR5K_EEPROM_READ(offset++, val);
- ee->ee_ctl[i] = (val >> 8) & 0xff;
- ee->ee_ctl[i + 1] = val & 0xff;
+ pcal[0].freq = (val >> 9) & mask;
+ pcal[1].freq = (val >> 2) & mask;
+ pcal[2].freq = (val << 5) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[2].freq |= (val >> 11) & 0x1f;
+ pcal[3].freq = (val >> 4) & mask;
+ pcal[4].freq = (val << 3) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[4].freq |= (val >> 13) & 0x7;
+ pcal[5].freq = (val >> 6) & mask;
+ pcal[6].freq = (val << 1) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[6].freq |= (val >> 15) & 0x1;
+ pcal[7].freq = (val >> 8) & mask;
+ pcal[8].freq = (val >> 1) & mask;
+ pcal[9].freq = (val << 6) & mask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ pcal[9].freq |= (val >> 10) & 0x3f;
+ ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10;
}
- /*
- * Get values for 802.11a (5GHz)
- */
- mode = AR5K_EEPROM_MODE_11A;
+ for(i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i += 1) {
+ pcal[i].freq = ath5k_eeprom_bin2freq(ee,
+ pcal[i].freq, AR5K_EEPROM_MODE_11A);
+ }
- ee->ee_turbo_max_power[mode] =
- AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header);
+ return 0;
+}
- offset = AR5K_EEPROM_MODES_11A(ah->ah_ee_version);
+static inline int
+ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal;
+ int i;
+
+ switch(mode) {
+ case AR5K_EEPROM_MODE_11B:
+ pcal = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ pcal = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
- ret = ath5k_eeprom_read_ants(ah, &offset, mode);
- if (ret)
- return ret;
+ ath5k_eeprom_read_freq_list(ah, &offset,
+ AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal,
+ &ee->ee_n_piers[mode]);
+ for(i = 0; i < AR5K_EEPROM_N_2GHZ_CHAN_2413; i += 1) {
+ pcal[i].freq = ath5k_eeprom_bin2freq(ee,
+ pcal[i].freq, mode);
+ }
- AR5K_EEPROM_READ(offset++, val);
- ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
- ee->ee_ob[mode][3] = (val >> 5) & 0x7;
- ee->ee_db[mode][3] = (val >> 2) & 0x7;
- ee->ee_ob[mode][2] = (val << 1) & 0x7;
-
- AR5K_EEPROM_READ(offset++, val);
- ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
- ee->ee_db[mode][2] = (val >> 12) & 0x7;
- ee->ee_ob[mode][1] = (val >> 9) & 0x7;
- ee->ee_db[mode][1] = (val >> 6) & 0x7;
- ee->ee_ob[mode][0] = (val >> 3) & 0x7;
- ee->ee_db[mode][0] = val & 0x7;
-
- ret = ath5k_eeprom_read_modes(ah, &offset, mode);
- if (ret)
- return ret;
+ return 0;
+}
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) {
- AR5K_EEPROM_READ(offset++, val);
- ee->ee_margin_tx_rx[mode] = val & 0x3f;
+
+static int
+ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *pcal;
+ int offset, ret;
+ int i, j;
+ u16 val;
+
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ switch(mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+
+ ret = ath5k_eeprom_init_11a_pcal_freq(ah,
+ offset + AR5K_EEPROM_GROUP1_OFFSET);
+ if (ret < 0)
+ return ret;
+
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+ pcal = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header) &&
+ !AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ pcal = ee->ee_pwr_cal_b;
+ offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+ /* fixed piers */
+ pcal[0].freq = 2412;
+ pcal[1].freq = 2447;
+ pcal[2].freq = 2484;
+ ee->ee_n_piers[mode] = 3;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ pcal = ee->ee_pwr_cal_g;
+ offset += AR5K_EEPROM_GROUP4_OFFSET;
+
+ /* fixed piers */
+ pcal[0].freq = 2312;
+ pcal[1].freq = 2412;
+ pcal[2].freq = 2484;
+ ee->ee_n_piers[mode] = 3;
+ break;
+ default:
+ return -EINVAL;
}
- /*
- * Get values for 802.11b (2.4GHz)
- */
- mode = AR5K_EEPROM_MODE_11B;
- offset = AR5K_EEPROM_MODES_11B(ah->ah_ee_version);
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ struct ath5k_chan_pcal_info_rf5111 *cdata =
+ &pcal[i].rf5111_info;
- ret = ath5k_eeprom_read_ants(ah, &offset, mode);
- if (ret)
- return ret;
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M);
+ cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M);
+ cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M);
- AR5K_EEPROM_READ(offset++, val);
- ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
- ee->ee_ob[mode][1] = (val >> 4) & 0x7;
- ee->ee_db[mode][1] = val & 0x7;
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[0] |= ((val >> 14) & 0x3);
+ cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M);
- ret = ath5k_eeprom_read_modes(ah, &offset, mode);
- if (ret)
- return ret;
+ AR5K_EEPROM_READ(offset++, val);
+ cdata->pwr[3] |= ((val >> 12) & 0xf);
+ cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M);
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
AR5K_EEPROM_READ(offset++, val);
- ee->ee_cal_pier[mode][0] =
- ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
- ee->ee_cal_pier[mode][1] =
- ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode);
+ cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M);
AR5K_EEPROM_READ(offset++, val);
- ee->ee_cal_pier[mode][2] =
- ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
+ cdata->pwr[8] |= ((val >> 14) & 0x3);
+ cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M);
+ cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M);
+
+ ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min,
+ cdata->pcdac_max, cdata->pcdac);
+
+ for (j = 0; j < AR5K_EEPROM_N_PCDAC; j++) {
+ cdata->pwr[j] = (u16)
+ (AR5K_EEPROM_POWER_STEP * cdata->pwr[j]);
+ }
}
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
- ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+ return 0;
+}
- /*
- * Get values for 802.11g (2.4GHz)
- */
- mode = AR5K_EEPROM_MODE_11G;
- offset = AR5K_EEPROM_MODES_11G(ah->ah_ee_version);
+static int
+ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
+ struct ath5k_chan_pcal_info *gen_chan_info;
+ u32 offset;
+ unsigned int i, c;
+ u16 val;
+ int ret;
- ret = ath5k_eeprom_read_ants(ah, &offset, mode);
- if (ret)
- return ret;
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ /*
+ * Read 5GHz EEPROM channels
+ */
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+ gen_chan_info = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP3_OFFSET;
+
+ /* NB: frequency piers parsed during mode init */
+ gen_chan_info = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ offset = AR5K_EEPROM_GROUPS_START(ee->ee_version);
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP4_OFFSET;
+ else if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+ offset += AR5K_EEPROM_GROUP2_OFFSET;
+
+ /* NB: frequency piers parsed during mode init */
+ gen_chan_info = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
- AR5K_EEPROM_READ(offset++, val);
- ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
- ee->ee_ob[mode][1] = (val >> 4) & 0x7;
- ee->ee_db[mode][1] = val & 0x7;
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ chan_pcal_info = &gen_chan_info[i].rf5112_info;
- ret = ath5k_eeprom_read_modes(ah, &offset, mode);
- if (ret)
- return ret;
+ /* Power values in dBm * 4
+ * for the lower xpd gain curve
+ * (0 dBm -> higher output power) */
+ for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr_x0[c] = (val & 0xff);
+ chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff);
+ }
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
+ /* PCDAC steps
+ * corresponding to the above power
+ * measurements */
AR5K_EEPROM_READ(offset++, val);
- ee->ee_cal_pier[mode][0] =
- ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
- ee->ee_cal_pier[mode][1] =
- ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode);
+ chan_pcal_info->pcdac_x0[1] = (val & 0x1f);
+ chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
+ chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
+ /* Power values in dBm * 4
+ * for the higher xpd gain curve
+ * (18 dBm -> lower output power) */
AR5K_EEPROM_READ(offset++, val);
- ee->ee_turbo_max_power[mode] = val & 0x7f;
- ee->ee_xr_power[mode] = (val >> 7) & 0x3f;
+ chan_pcal_info->pwr_x3[0] = (val & 0xff);
+ chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff);
AR5K_EEPROM_READ(offset++, val);
- ee->ee_cal_pier[mode][2] =
- ath5k_eeprom_bin2freq(ah, val & 0xff, mode);
+ chan_pcal_info->pwr_x3[2] = (val & 0xff);
+
+ /* PCDAC steps
+ * corresponding to the above power
+ * measurements (static) */
+ chan_pcal_info->pcdac_x3[0] = 20;
+ chan_pcal_info->pcdac_x3[1] = 35;
+ chan_pcal_info->pcdac_x3[2] = 63;
+
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
+ chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff);
+
+ /* Last xpd0 power level is also channel maximum */
+ gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
+ } else {
+ chan_pcal_info->pcdac_x0[0] = 1;
+ gen_chan_info[i].max_pwr = ((val >> 8) & 0xff);
+ }
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1)
- ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f;
+ /* Recreate pcdac_x0 table for this channel using pcdac steps */
+ chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0];
+ chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1];
+ chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2];
+ }
+
+ return 0;
+}
+
+static inline unsigned int
+ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode)
+{
+ static const unsigned int pdgains_size[] = { 4, 6, 9, 12 };
+ unsigned int sz;
+
+ sz = pdgains_size[ee->ee_pd_gains[mode] - 1];
+ sz *= ee->ee_n_piers[mode];
+
+ return sz;
+}
+
+static unsigned int
+ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
+{
+ u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4);
+
+ switch(mode) {
+ case AR5K_EEPROM_MODE_11G:
+ if (AR5K_EEPROM_HDR_11B(ee->ee_header))
+ offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + 2;
+ /* fall through */
+ case AR5K_EEPROM_MODE_11B:
+ if (AR5K_EEPROM_HDR_11A(ee->ee_header))
+ offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + 5;
+ /* fall through */
+ case AR5K_EEPROM_MODE_11A:
+ break;
+ default:
+ break;
+ }
+
+ return offset;
+}
+
+static int
+ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info;
+ struct ath5k_chan_pcal_info *gen_chan_info;
+ unsigned int i, c;
+ u32 offset;
+ int ret;
+ u16 val;
+ u8 pd_gains = 0;
+
+ if (ee->ee_x_gain[mode] & 0x1) pd_gains++;
+ if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++;
+ if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++;
+ if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++;
+ ee->ee_pd_gains[mode] = pd_gains;
+
+ offset = ath5k_cal_data_offset_2413(ee, mode);
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+ offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
+ gen_chan_info = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+ return 0;
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ gen_chan_info = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ gen_chan_info = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (pd_gains == 0)
+ return 0;
+
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ chan_pcal_info = &gen_chan_info[i].rf2413_info;
+
+ /*
+ * Read pwr_i, pddac_i and the first
+ * 2 pd points (pwr, pddac)
+ */
AR5K_EEPROM_READ(offset++, val);
- ee->ee_i_cal[mode] = (val >> 8) & 0x3f;
- ee->ee_q_cal[mode] = (val >> 3) & 0x1f;
+ chan_pcal_info->pwr_i[0] = val & 0x1f;
+ chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f;
+ chan_pcal_info->pwr[0][0] =
+ (val >> 12) & 0xf;
- if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) {
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[0][0] = val & 0x3f;
+ chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf;
+ chan_pcal_info->pddac[0][1] =
+ (val >> 10) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr[0][2] = val & 0xf;
+ chan_pcal_info->pddac[0][2] =
+ (val >> 4) & 0x3f;
+
+ chan_pcal_info->pwr[0][3] = 0;
+ chan_pcal_info->pddac[0][3] = 0;
+
+ if (pd_gains > 1) {
+ /*
+ * Pd gain 0 is not the last pd gain
+ * so it only has 2 pd points.
+ * Continue wih pd gain 1.
+ */
+ chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f;
+
+ chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1;
AR5K_EEPROM_READ(offset++, val);
- ee->ee_cck_ofdm_gain_delta = val & 0xff;
+ chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1;
+
+ chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf;
+ chan_pcal_info->pddac[1][0] =
+ (val >> 10) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr[1][1] = val & 0xf;
+ chan_pcal_info->pddac[1][1] =
+ (val >> 4) & 0x3f;
+ chan_pcal_info->pwr[1][2] =
+ (val >> 10) & 0xf;
+
+ chan_pcal_info->pddac[1][2] =
+ (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[1][2] |=
+ (val & 0xF) << 2;
+
+ chan_pcal_info->pwr[1][3] = 0;
+ chan_pcal_info->pddac[1][3] = 0;
+ } else if (pd_gains == 1) {
+ /*
+ * Pd gain 0 is the last one so
+ * read the extra point.
+ */
+ chan_pcal_info->pwr[0][3] =
+ (val >> 10) & 0xf;
+
+ chan_pcal_info->pddac[0][3] =
+ (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[0][3] |=
+ (val & 0xF) << 2;
+ }
+
+ /*
+ * Proceed with the other pd_gains
+ * as above.
+ */
+ if (pd_gains > 2) {
+ chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f;
+ chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f;
+
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr[2][0] =
+ (val >> 0) & 0xf;
+ chan_pcal_info->pddac[2][0] =
+ (val >> 4) & 0x3f;
+ chan_pcal_info->pwr[2][1] =
+ (val >> 10) & 0xf;
+
+ chan_pcal_info->pddac[2][1] =
+ (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[2][1] |=
+ (val & 0xF) << 2;
+
+ chan_pcal_info->pwr[2][2] =
+ (val >> 4) & 0xf;
+ chan_pcal_info->pddac[2][2] =
+ (val >> 8) & 0x3f;
+
+ chan_pcal_info->pwr[2][3] = 0;
+ chan_pcal_info->pddac[2][3] = 0;
+ } else if (pd_gains == 2) {
+ chan_pcal_info->pwr[1][3] =
+ (val >> 4) & 0xf;
+ chan_pcal_info->pddac[1][3] =
+ (val >> 8) & 0x3f;
+ }
+
+ if (pd_gains > 3) {
+ chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+
+ chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f;
+ chan_pcal_info->pwr[3][0] =
+ (val >> 10) & 0xf;
+ chan_pcal_info->pddac[3][0] =
+ (val >> 14) & 0x3;
+
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[3][0] |=
+ (val & 0xF) << 2;
+ chan_pcal_info->pwr[3][1] =
+ (val >> 4) & 0xf;
+ chan_pcal_info->pddac[3][1] =
+ (val >> 8) & 0x3f;
+
+ chan_pcal_info->pwr[3][2] =
+ (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr[3][2] |=
+ ((val >> 0) & 0x3) << 2;
+
+ chan_pcal_info->pddac[3][2] =
+ (val >> 2) & 0x3f;
+ chan_pcal_info->pwr[3][3] =
+ (val >> 8) & 0xf;
+
+ chan_pcal_info->pddac[3][3] =
+ (val >> 12) & 0xF;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pddac[3][3] |=
+ ((val >> 0) & 0x3) << 4;
+ } else if (pd_gains == 3) {
+ chan_pcal_info->pwr[2][3] =
+ (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ chan_pcal_info->pwr[2][3] |=
+ ((val >> 0) & 0x3) << 2;
+
+ chan_pcal_info->pddac[2][3] =
+ (val >> 2) & 0x3f;
+ }
+
+ for (c = 0; c < pd_gains; c++) {
+ /* Recreate pwr table for this channel using pwr steps */
+ chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2;
+ chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0];
+ chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1];
+ chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2];
+ if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2])
+ chan_pcal_info->pwr[c][3] = 0;
+
+ /* Recreate pddac table for this channel using pddac steps */
+ chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c];
+ chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0];
+ chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1];
+ chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2];
+ if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2])
+ chan_pcal_info->pddac[c][3] = 0;
}
}
- /*
- * Read 5GHz EEPROM channels
- */
+ return 0;
+}
+
+/*
+ * Read per rate target power (this is the maximum tx power
+ * supported by the card). This info is used when setting
+ * tx power, no matter the channel.
+ *
+ * This also works for v5 EEPROMs.
+ */
+static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_rate_pcal_info *rate_pcal_info;
+ u16 *rate_target_pwr_num;
+ u32 offset;
+ u16 val;
+ int ret, i;
+
+ offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1);
+ rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode];
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_a;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_b;
+ ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version);
+ rate_pcal_info = ee->ee_rate_tpwr_g;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Different freq mask for older eeproms (<= v3.2) */
+ if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) {
+ for (i = 0; i < (*rate_target_pwr_num); i++) {
+ AR5K_EEPROM_READ(offset++, val);
+ rate_pcal_info[i].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode);
+
+ rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f);
+ rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+
+ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+ val == 0) {
+ (*rate_target_pwr_num) = i;
+ break;
+ }
+
+ rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7);
+ rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f);
+ rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f);
+ }
+ } else {
+ for (i = 0; i < (*rate_target_pwr_num); i++) {
+ AR5K_EEPROM_READ(offset++, val);
+ rate_pcal_info[i].freq =
+ ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode);
+
+ rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f);
+ rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f;
+
+ AR5K_EEPROM_READ(offset++, val);
+
+ if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS ||
+ val == 0) {
+ (*rate_target_pwr_num) = i;
+ break;
+ }
+
+ rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf;
+ rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f);
+ rate_pcal_info[i].target_power_54 = (val & 0x3f);
+ }
+ }
+
+ return 0;
+}
+
+static int
+ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ int (*read_pcal)(struct ath5k_hw *hw, int mode);
+ int mode;
+ int err;
+
+ if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) &&
+ (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1))
+ read_pcal = ath5k_eeprom_read_pcal_info_5112;
+ else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) &&
+ (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2))
+ read_pcal = ath5k_eeprom_read_pcal_info_2413;
+ else
+ read_pcal = ath5k_eeprom_read_pcal_info_5111;
+
+ for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
+ err = read_pcal(ah, mode);
+ if (err)
+ return err;
+
+ err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Read conformance test limits */
+static int
+ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_edge_power *rep;
+ unsigned int fmask, pmask;
+ unsigned int ctl_mode;
+ int ret, i, j;
+ u32 offset;
+ u16 val;
+
+ pmask = AR5K_EEPROM_POWER_M;
+ fmask = AR5K_EEPROM_FREQ_M(ee->ee_version);
+ offset = AR5K_EEPROM_CTL(ee->ee_version);
+ ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version);
+ for (i = 0; i < ee->ee_ctls; i += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ ee->ee_ctl[i] = (val >> 8) & 0xff;
+ ee->ee_ctl[i + 1] = val & 0xff;
+ }
+
+ offset = AR5K_EEPROM_GROUP8_OFFSET;
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0)
+ offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) -
+ AR5K_EEPROM_GROUP5_OFFSET;
+ else
+ offset += AR5K_EEPROM_GROUPS_START(ee->ee_version);
+
+ rep = ee->ee_ctl_pwr;
+ for(i = 0; i < ee->ee_ctls; i++) {
+ switch(ee->ee_ctl[i] & AR5K_CTL_MODE_M) {
+ case AR5K_CTL_11A:
+ case AR5K_CTL_TURBO:
+ ctl_mode = AR5K_EEPROM_MODE_11A;
+ break;
+ default:
+ ctl_mode = AR5K_EEPROM_MODE_11G;
+ break;
+ }
+ if (ee->ee_ctl[i] == 0) {
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3)
+ offset += 8;
+ else
+ offset += 7;
+ rep += AR5K_EEPROM_N_EDGES;
+ continue;
+ }
+ if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[j].freq = (val >> 8) & fmask;
+ rep[j + 1].freq = val & fmask;
+ }
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[j].edge = (val >> 8) & pmask;
+ rep[j].flag = (val >> 14) & 1;
+ rep[j + 1].edge = val & pmask;
+ rep[j + 1].flag = (val >> 6) & 1;
+ }
+ } else {
+ AR5K_EEPROM_READ(offset++, val);
+ rep[0].freq = (val >> 9) & fmask;
+ rep[1].freq = (val >> 2) & fmask;
+ rep[2].freq = (val << 5) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[2].freq |= (val >> 11) & 0x1f;
+ rep[3].freq = (val >> 4) & fmask;
+ rep[4].freq = (val << 3) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[4].freq |= (val >> 13) & 0x7;
+ rep[5].freq = (val >> 6) & fmask;
+ rep[6].freq = (val << 1) & fmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[6].freq |= (val >> 15) & 0x1;
+ rep[7].freq = (val >> 8) & fmask;
+
+ rep[0].edge = (val >> 2) & pmask;
+ rep[1].edge = (val << 4) & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[1].edge |= (val >> 12) & 0xf;
+ rep[2].edge = (val >> 6) & pmask;
+ rep[3].edge = val & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[4].edge = (val >> 10) & pmask;
+ rep[5].edge = (val >> 4) & pmask;
+ rep[6].edge = (val << 2) & pmask;
+
+ AR5K_EEPROM_READ(offset++, val);
+ rep[6].edge |= (val >> 14) & 0x3;
+ rep[7].edge = (val >> 8) & pmask;
+ }
+ for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) {
+ rep[j].freq = ath5k_eeprom_bin2freq(ee,
+ rep[j].freq, ctl_mode);
+ }
+ rep += AR5K_EEPROM_N_EDGES;
+ }
return 0;
}
+
+/*
+ * Initialize eeprom power tables
+ */
+int
+ath5k_eeprom_init(struct ath5k_hw *ah)
+{
+ int err;
+
+ err = ath5k_eeprom_init_header(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_init_modes(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_read_pcal_info(ah);
+ if (err < 0)
+ return err;
+
+ err = ath5k_eeprom_read_ctl_info(ah);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
/*
* Read the MAC address from eeprom
*/
diff --git a/drivers/net/wireless/ath5k/eeprom.h b/drivers/net/wireless/ath5k/eeprom.h
index a468ecfbb18a..09eb7d0176a4 100644
--- a/drivers/net/wireless/ath5k/eeprom.h
+++ b/drivers/net/wireless/ath5k/eeprom.h
@@ -25,24 +25,8 @@
#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
-#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
-#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
-#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
-#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
-#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
-#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
-#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
-#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
-#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
-#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
-#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
-#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
-#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
-#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
-#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
-#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
-#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */
+#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */
#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */
#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
#define AR5K_EEPROM_INFO_CKSUM 0xffff
@@ -53,15 +37,19 @@
#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */
#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
-#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain ee_cck_ofdm_power_delta (eeprom_read_modes) */
-#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc*, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
+#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
+#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */
-#define AR5K_EEPROM_VERSION_4_3 0x4003
+#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */
#define AR5K_EEPROM_VERSION_4_4 0x4004
#define AR5K_EEPROM_VERSION_4_5 0x4005
#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */
-#define AR5K_EEPROM_VERSION_4_7 0x4007
+#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */
+#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */
+#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */
+#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */
+#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */
#define AR5K_EEPROM_MODE_11A 0
#define AR5K_EEPROM_MODE_11B 1
@@ -74,8 +62,8 @@
#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
-#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
+#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz */
#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c
#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2
@@ -87,27 +75,95 @@
(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
-#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((int8_t)(((_v) >> 8) & 0xff))
-#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((int8_t)((_v) & 0xff))
+#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff))
+#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff))
+
+/* Misc values available since EEPROM 4.0 */
+#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4)
+#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
+#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1)
+#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1)
+#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
+
+#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5)
+#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
+#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1)
+
+#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6)
+#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff)
+#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7)
+#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f)
+#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff)
+
+#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8)
+#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff)
+#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3)
+#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3)
+
+#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9)
+#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1)
+#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1)
+#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1)
+#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1)
+#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf)
+#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1)
+#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf)
+
+#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
+#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x8)
+#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x8)
+#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1)
+#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1)
+#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1)
+#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 9) & 0x1)
+#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 10) & 0x1)
/* calibration settings */
#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */
+#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */
+#define AR5K_EEPROM_GROUP1_OFFSET 0x0
+#define AR5K_EEPROM_GROUP2_OFFSET 0x5
+#define AR5K_EEPROM_GROUP3_OFFSET 0x37
+#define AR5K_EEPROM_GROUP4_OFFSET 0x46
+#define AR5K_EEPROM_GROUP5_OFFSET 0x55
+#define AR5K_EEPROM_GROUP6_OFFSET 0x65
+#define AR5K_EEPROM_GROUP7_OFFSET 0x69
+#define AR5K_EEPROM_GROUP8_OFFSET 0x6f
+
+#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP5_OFFSET, 0x0000)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP6_OFFSET, 0x0010)
+#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
+ AR5K_EEPROM_GROUP7_OFFSET, 0x0014)
/* [3.1 - 3.3] */
#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec
#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed
-/* Misc values available since EEPROM 4.0 */
-#define AR5K_EEPROM_MISC0 0x00c4
-#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
-#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
-#define AR5K_EEPROM_MISC1 0x00c5
-#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
-#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
-
+#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
+#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
+#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
+#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
+#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
+#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
+#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
+#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
+#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
+#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
+#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
+#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
+#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
+#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
+#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
+#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
/* Some EEPROM defines */
#define AR5K_EEPROM_EEP_SCALE 100
@@ -115,8 +171,11 @@
#define AR5K_EEPROM_N_MODES 3
#define AR5K_EEPROM_N_5GHZ_CHAN 10
#define AR5K_EEPROM_N_2GHZ_CHAN 3
+#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_MAX_CHAN 10
+#define AR5K_EEPROM_N_PWR_POINTS_5111 11
#define AR5K_EEPROM_N_PCDAC 11
+#define AR5K_EEPROM_N_PHASE_CAL 5
#define AR5K_EEPROM_N_TEST_FREQ 8
#define AR5K_EEPROM_N_EDGES 8
#define AR5K_EEPROM_N_INTERCEPTS 11
@@ -136,6 +195,8 @@
#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4
#define AR5K_EEPROM_N_XPD0_POINTS 4
#define AR5K_EEPROM_N_XPD3_POINTS 3
+#define AR5K_EEPROM_N_PD_GAINS 4
+#define AR5K_EEPROM_N_PD_POINTS 5
#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35
#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55
#define AR5K_EEPROM_POWER_M 0x3f
@@ -158,8 +219,99 @@
#define AR5K_EEPROM_READ_HDR(_o, _v) \
AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \
-/* Struct to hold EEPROM calibration data */
+enum ath5k_ant_setting {
+ AR5K_ANT_VARIABLE = 0, /* variable by programming */
+ AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */
+ AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */
+ AR5K_ANT_MAX = 3,
+};
+
+enum ath5k_ctl_mode {
+ AR5K_CTL_11A = 0,
+ AR5K_CTL_11B = 1,
+ AR5K_CTL_11G = 2,
+ AR5K_CTL_TURBO = 3,
+ AR5K_CTL_108G = 4,
+ AR5K_CTL_2GHT20 = 5,
+ AR5K_CTL_5GHT20 = 6,
+ AR5K_CTL_2GHT40 = 7,
+ AR5K_CTL_5GHT40 = 8,
+ AR5K_CTL_MODE_M = 15,
+};
+
+/* Per channel calibration data, used for power table setup */
+struct ath5k_chan_pcal_info_rf5111 {
+ /* Power levels in half dbm units
+ * for one power curve. */
+ u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+ /* PCDAC table steps
+ * for the above values */
+ u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+ /* Starting PCDAC step */
+ u8 pcdac_min;
+ /* Final PCDAC step */
+ u8 pcdac_max;
+};
+
+struct ath5k_chan_pcal_info_rf5112 {
+ /* Power levels in quarter dBm units
+ * for lower (0) and higher (3)
+ * level curves */
+ s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+ /* PCDAC table steps
+ * for the above values */
+ u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+ u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+};
+
+struct ath5k_chan_pcal_info_rf2413 {
+ /* Starting pwr/pddac values */
+ s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+ u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+ /* (pwr,pddac) points */
+ s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+ u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+};
+
+struct ath5k_chan_pcal_info {
+ /* Frequency */
+ u16 freq;
+ /* Max available power */
+ s8 max_pwr;
+ union {
+ struct ath5k_chan_pcal_info_rf5111 rf5111_info;
+ struct ath5k_chan_pcal_info_rf5112 rf5112_info;
+ struct ath5k_chan_pcal_info_rf2413 rf2413_info;
+ };
+};
+
+/* Per rate calibration data for each mode, used for power table setup */
+struct ath5k_rate_pcal_info {
+ u16 freq; /* Frequency */
+ /* Power level for 6-24Mbit/s rates */
+ u16 target_power_6to24;
+ /* Power level for 36Mbit rate */
+ u16 target_power_36;
+ /* Power level for 48Mbit rate */
+ u16 target_power_48;
+ /* Power level for 54Mbit rate */
+ u16 target_power_54;
+};
+
+/* Power edges for conformance test limits */
+struct ath5k_edge_power {
+ u16 freq;
+ u16 edge; /* in half dBm */
+ bool flag;
+};
+
+/* EEPROM calibration data */
struct ath5k_eeprom_info {
+
+ /* Header information */
u16 ee_magic;
u16 ee_protect;
u16 ee_regdomain;
@@ -168,6 +320,11 @@ struct ath5k_eeprom_info {
u16 ee_ant_gain;
u16 ee_misc0;
u16 ee_misc1;
+ u16 ee_misc2;
+ u16 ee_misc3;
+ u16 ee_misc4;
+ u16 ee_misc5;
+ u16 ee_misc6;
u16 ee_cck_ofdm_gain_delta;
u16 ee_cck_ofdm_power_delta;
u16 ee_scaled_cck_delta;
@@ -185,7 +342,7 @@ struct ath5k_eeprom_info {
u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES];
u16 ee_xr_power[AR5K_EEPROM_N_MODES];
u16 ee_switch_settling[AR5K_EEPROM_N_MODES];
- u16 ee_ant_tx_rx[AR5K_EEPROM_N_MODES];
+ u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES];
u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
@@ -198,18 +355,40 @@ struct ath5k_eeprom_info {
u16 ee_x_gain[AR5K_EEPROM_N_MODES];
u16 ee_i_gain[AR5K_EEPROM_N_MODES];
u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
+ u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES];
+ u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES];
+ u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES];
- /* Unused */
+ /* Power calibration data */
u16 ee_false_detect[AR5K_EEPROM_N_MODES];
- u16 ee_cal_pier[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_2GHZ_CHAN];
- u16 ee_channel[AR5K_EEPROM_N_MODES][AR5K_EEPROM_MAX_CHAN]; /*empty*/
+
+ /* Number of pd gain curves per mode (RF2413) */
+ u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+
+ u8 ee_n_piers[AR5K_EEPROM_N_MODES];
+ struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
+ struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN];
+ struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN];
+
+ /* Per rate target power levels */
+ u16 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN];
+ struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN];
/* Conformance test limits (Unused) */
u16 ee_ctls;
u16 ee_ctl[AR5K_EEPROM_MAX_CTLS];
+ struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
/* Noise Floor Calibration settings */
s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES];
s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES];
+ s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES];
+ s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES];
+ s8 ee_pd_gain_overlap;
+
+ u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
};
+
diff --git a/drivers/net/wireless/ath5k/initvals.c b/drivers/net/wireless/ath5k/initvals.c
index ceaa6c475c06..450bd6e945ff 100644
--- a/drivers/net/wireless/ath5k/initvals.c
+++ b/drivers/net/wireless/ath5k/initvals.c
@@ -1681,7 +1681,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
*/
/* For AR5212 and combatible */
- if (ah->ah_version == AR5K_AR5212){
+ if (ah->ah_version == AR5K_AR5212) {
/* First set of mode-specific settings */
ath5k_hw_ini_mode_registers(ah,
@@ -1695,7 +1695,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
ar5212_ini, change_channel);
/* Second set of mode-specific settings */
- if (ah->ah_radio == AR5K_RF5111){
+ if (ah->ah_radio == AR5K_RF5111) {
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(ar5212_rf5111_ini_mode_end),
@@ -1706,7 +1706,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
ARRAY_SIZE(rf5111_ini_bbgain),
rf5111_ini_bbgain, change_channel);
- } else if (ah->ah_radio == AR5K_RF5112){
+ } else if (ah->ah_radio == AR5K_RF5112) {
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(ar5212_rf5112_ini_mode_end),
@@ -1716,7 +1716,7 @@ int ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool change_channel)
ARRAY_SIZE(rf5112_ini_bbgain),
rf5112_ini_bbgain, change_channel);
- } else if (ah->ah_radio == AR5K_RF5413){
+ } else if (ah->ah_radio == AR5K_RF5413) {
ath5k_hw_ini_mode_registers(ah,
ARRAY_SIZE(rf5413_ini_mode_end),
diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c
index a47df9a24aa1..0cac05c6a9ce 100644
--- a/drivers/net/wireless/ath5k/pcu.c
+++ b/drivers/net/wireless/ath5k/pcu.c
@@ -46,34 +46,45 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah)
{
u32 pcu_reg, beacon_reg, low_id, high_id;
- pcu_reg = 0;
+
+ /* Preserve rest settings */
+ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+ pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP
+ | AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
+ (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0));
+
beacon_reg = 0;
ATH5K_TRACE(ah->ah_sc);
switch (ah->ah_op_mode) {
case NL80211_IFTYPE_ADHOC:
- pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA |
- (ah->ah_version == AR5K_AR5210 ?
- AR5K_STA_ID1_NO_PSPOLL : 0);
+ pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE;
beacon_reg |= AR5K_BCR_ADHOC;
+ if (ah->ah_version == AR5K_AR5210)
+ pcu_reg |= AR5K_STA_ID1_NO_PSPOLL;
+ else
+ AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC);
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
- pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA |
- (ah->ah_version == AR5K_AR5210 ?
- AR5K_STA_ID1_NO_PSPOLL : 0);
+ pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE;
beacon_reg |= AR5K_BCR_AP;
+ if (ah->ah_version == AR5K_AR5210)
+ pcu_reg |= AR5K_STA_ID1_NO_PSPOLL;
+ else
+ AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC);
break;
case NL80211_IFTYPE_STATION:
- pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
- (ah->ah_version == AR5K_AR5210 ?
+ pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
AR5K_STA_ID1_PWR_SV : 0);
case NL80211_IFTYPE_MONITOR:
- pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA |
- (ah->ah_version == AR5K_AR5210 ?
+ pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE
+ | (ah->ah_version == AR5K_AR5210 ?
AR5K_STA_ID1_NO_PSPOLL : 0);
break;
@@ -130,6 +141,8 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah,
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR);
ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE);
}
+
+ /* TODO: Handle ANI stats */
}
/**
@@ -258,16 +271,19 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac)
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
{
u32 low_id, high_id;
+ u32 pcu_reg;
ATH5K_TRACE(ah->ah_sc);
/* Set new station ID */
memcpy(ah->ah_sta_id, mac, ETH_ALEN);
+ pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
+
low_id = AR5K_LOW_ID(mac);
high_id = AR5K_HIGH_ID(mac);
ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
- ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1);
+ ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
return 0;
}
@@ -290,8 +306,10 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
* Set simple BSSID mask on 5212
*/
if (ah->ah_version == AR5K_AR5212) {
- ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0);
- ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1);
+ ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask),
+ AR5K_BSS_IDM0);
+ ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask),
+ AR5K_BSS_IDM1);
}
/*
@@ -415,6 +433,9 @@ int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
u32 low_id, high_id;
ATH5K_TRACE(ah->ah_sc);
+ /* Cache bssid mask so that we can restore it
+ * on reset */
+ memcpy(ah->ah_bssid_mask, mask, ETH_ALEN);
if (ah->ah_version == AR5K_AR5212) {
low_id = AR5K_LOW_ID(mask);
high_id = AR5K_HIGH_ID(mask);
@@ -576,7 +597,7 @@ void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter)
filter |= AR5K_RX_FILTER_PROM;
}
- /*Zero length DMA*/
+ /*Zero length DMA (phy error reporting) */
if (data)
AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA);
else
@@ -661,7 +682,12 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
* Set the additional timers by mode
*/
switch (ah->ah_op_mode) {
+ case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_STATION:
+ /* In STA mode timer1 is used as next wakeup
+ * timer and timer2 as next CFP duration start
+ * timer. Both in 1/8TUs. */
+ /* TODO: PCF handling */
if (ah->ah_version == AR5K_AR5210) {
timer1 = 0xffffffff;
timer2 = 0xffffffff;
@@ -669,27 +695,60 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
timer1 = 0x0000ffff;
timer2 = 0x0007ffff;
}
+ /* Mark associated AP as PCF incapable for now */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF);
break;
-
+ case NL80211_IFTYPE_ADHOC:
+ AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM);
default:
+ /* On non-STA modes timer1 is used as next DMA
+ * beacon alert (DBA) timer and timer2 as next
+ * software beacon alert. Both in 1/8TUs. */
timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3;
timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3;
+ break;
}
+ /* Timer3 marks the end of our ATIM window
+ * a zero length window is not allowed because
+ * we 'll get no beacons */
timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1);
/*
* Set the beacon register and enable all timers.
- * (next beacon, DMA beacon, software beacon, ATIM window time)
*/
- ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
+ /* When in AP mode zero timer0 to start TSF */
+ if (ah->ah_op_mode == NL80211_IFTYPE_AP)
+ ath5k_hw_reg_write(ah, 0, AR5K_TIMER0);
+ else
+ ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0);
ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1);
ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2);
ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3);
+ /* Force a TSF reset if requested and enable beacons */
+ if (interval & AR5K_BEACON_RESET_TSF)
+ ath5k_hw_reset_tsf(ah);
+
ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD |
- AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE),
- AR5K_BEACON);
+ AR5K_BEACON_ENABLE),
+ AR5K_BEACON);
+
+ /* Flush any pending BMISS interrupts on ISR by
+ * performing a clear-on-write operation on PISR
+ * register for the BMISS bit (writing a bit on
+ * ISR togles a reset for that bit and leaves
+ * the rest bits intact) */
+ if (ah->ah_version == AR5K_AR5210)
+ ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR);
+ else
+ ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR);
+
+ /* TODO: Set enchanced sleep registers on AR5212
+ * based on vif->bss_conf params, until then
+ * disable power save reporting.*/
+ AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV);
+
}
#if 0
@@ -899,14 +958,26 @@ int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr)
*/
int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
{
- unsigned int i;
+ unsigned int i, type;
+ u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
+ type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry));
+
for (i = 0; i < AR5K_KEYCACHE_SIZE; i++)
ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i));
+ /* Reset associated MIC entry if TKIP
+ * is enabled located at offset (entry + 64) */
+ if (type == AR5K_KEYTABLE_TYPE_TKIP) {
+ AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE);
+ for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++)
+ ath5k_hw_reg_write(ah, 0,
+ AR5K_KEYTABLE_OFF(micentry, i));
+ }
+
/*
* Set NULL encryption on AR5212+
*
@@ -916,10 +987,16 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry)
* Note2: Windows driver (ndiswrapper) sets this to
* 0x00000714 instead of 0x00000007
*/
- if (ah->ah_version > AR5K_AR5211)
+ if (ah->ah_version > AR5K_AR5211) {
ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
AR5K_KEYTABLE_TYPE(entry));
+ if (type == AR5K_KEYTABLE_TYPE_TKIP) {
+ ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
+ AR5K_KEYTABLE_TYPE(micentry));
+ }
+ }
+
return 0;
}
@@ -936,6 +1013,23 @@ int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry)
AR5K_KEYTABLE_VALID;
}
+static
+int ath5k_keycache_type(const struct ieee80211_key_conf *key)
+{
+ switch (key->alg) {
+ case ALG_TKIP:
+ return AR5K_KEYTABLE_TYPE_TKIP;
+ case ALG_CCMP:
+ return AR5K_KEYTABLE_TYPE_CCM;
+ case ALG_WEP:
+ if (key->keylen == LEN_WEP40)
+ return AR5K_KEYTABLE_TYPE_40;
+ else if (key->keylen == LEN_WEP104)
+ return AR5K_KEYTABLE_TYPE_104;
+ }
+ return -EINVAL;
+}
+
/*
* Set a key entry on the table
*/
@@ -943,40 +1037,53 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
const struct ieee80211_key_conf *key, const u8 *mac)
{
unsigned int i;
+ int keylen;
__le32 key_v[5] = {};
+ __le32 key0 = 0, key1 = 0;
+ __le32 *rxmic, *txmic;
u32 keytype;
+ u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET;
+ bool is_tkip;
+ const u8 *key_ptr;
ATH5K_TRACE(ah->ah_sc);
- /* key->keylen comes in from mac80211 in bytes */
+ is_tkip = (key->alg == ALG_TKIP);
- if (key->keylen > AR5K_KEYTABLE_SIZE / 8)
+ /*
+ * key->keylen comes in from mac80211 in bytes.
+ * TKIP is 128 bit + 128 bit mic
+ */
+ keylen = (is_tkip) ? (128 / 8) : key->keylen;
+
+ if (entry > AR5K_KEYTABLE_SIZE ||
+ (is_tkip && micentry > AR5K_KEYTABLE_SIZE))
return -EOPNOTSUPP;
- switch (key->keylen) {
- /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */
- case 40 / 8:
- memcpy(&key_v[0], key->key, 5);
- keytype = AR5K_KEYTABLE_TYPE_40;
- break;
+ if (unlikely(keylen > 16))
+ return -EOPNOTSUPP;
- /* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */
- case 104 / 8:
- memcpy(&key_v[0], &key->key[0], 6);
- memcpy(&key_v[2], &key->key[6], 6);
- memcpy(&key_v[4], &key->key[12], 1);
- keytype = AR5K_KEYTABLE_TYPE_104;
- break;
- /* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */
- case 128 / 8:
- memcpy(&key_v[0], &key->key[0], 6);
- memcpy(&key_v[2], &key->key[6], 6);
- memcpy(&key_v[4], &key->key[12], 4);
- keytype = AR5K_KEYTABLE_TYPE_128;
- break;
+ keytype = ath5k_keycache_type(key);
+ if (keytype < 0)
+ return keytype;
- default:
- return -EINVAL; /* shouldn't happen */
+ /*
+ * each key block is 6 bytes wide, written as pairs of
+ * alternating 32 and 16 bit le values.
+ */
+ key_ptr = key->key;
+ for (i = 0; keylen >= 6; keylen -= 6) {
+ memcpy(&key_v[i], key_ptr, 6);
+ i += 2;
+ key_ptr += 6;
+ }
+ if (keylen)
+ memcpy(&key_v[i], key_ptr, keylen);
+
+ /* intentionally corrupt key until mic is installed */
+ if (is_tkip) {
+ key0 = key_v[0] = ~key_v[0];
+ key1 = key_v[1] = ~key_v[1];
}
for (i = 0; i < ARRAY_SIZE(key_v); i++)
@@ -985,6 +1092,40 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry,
ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry));
+ if (is_tkip) {
+ /* Install rx/tx MIC */
+ rxmic = (__le32 *) &key->key[16];
+ txmic = (__le32 *) &key->key[24];
+
+ if (ah->ah_combined_mic) {
+ key_v[0] = rxmic[0];
+ key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16);
+ key_v[2] = rxmic[1];
+ key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff);
+ key_v[4] = txmic[1];
+ } else {
+ key_v[0] = rxmic[0];
+ key_v[1] = 0;
+ key_v[2] = rxmic[1];
+ key_v[3] = 0;
+ key_v[4] = 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(key_v); i++)
+ ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]),
+ AR5K_KEYTABLE_OFF(micentry, i));
+
+ ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL,
+ AR5K_KEYTABLE_TYPE(micentry));
+ ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry));
+ ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry));
+
+ /* restore first 2 words of key */
+ ath5k_hw_reg_write(ah, le32_to_cpu(~key0),
+ AR5K_KEYTABLE_OFF(entry, 0));
+ ath5k_hw_reg_write(ah, le32_to_cpu(~key1),
+ AR5K_KEYTABLE_OFF(entry, 1));
+ }
+
return ath5k_hw_set_key_lladdr(ah, entry, mac);
}
diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c
index e43f6563e61a..7ba18e09463b 100644
--- a/drivers/net/wireless/ath5k/phy.c
+++ b/drivers/net/wireless/ath5k/phy.c
@@ -1412,7 +1412,8 @@ static int ath5k_hw_rf5112_rfregs(struct ath5k_hw *ah,
rf_ini = rfregs_2112a;
rf_size = ARRAY_SIZE(rfregs_5112a);
if (mode < 2) {
- ATH5K_ERR(ah->ah_sc,"invalid channel mode: %i\n",mode);
+ ATH5K_ERR(ah->ah_sc, "invalid channel mode: %i\n",
+ mode);
return -EINVAL;
}
mode = mode - 2; /*no a/turboa modes for 2112*/
@@ -1708,7 +1709,7 @@ enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath5k_hw *ah)
if (ah->ah_radio >= AR5K_RF5112) {
ath5k_hw_rfregs_gainf_corr(ah);
ah->ah_gain.g_current =
- ah->ah_gain.g_current>=ah->ah_gain.g_f_corr ?
+ ah->ah_gain.g_current >= ah->ah_gain.g_f_corr ?
(ah->ah_gain.g_current-ah->ah_gain.g_f_corr) :
0;
}
@@ -2195,9 +2196,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
return ret;
}
- ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
- if (ret)
- return ret;
+ ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
/*
* Re-enable RX/TX and beacons
diff --git a/drivers/net/wireless/ath5k/qcu.c b/drivers/net/wireless/ath5k/qcu.c
index 01bf09176d23..1b7bc50ea8eb 100644
--- a/drivers/net/wireless/ath5k/qcu.c
+++ b/drivers/net/wireless/ath5k/qcu.c
@@ -432,13 +432,30 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
+
+ if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
+ AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
/* Update secondary interrupt mask registers */
+
+ /* Filter out inactive queues */
ah->ah_txq_imr_txok &= ah->ah_txq_status;
ah->ah_txq_imr_txerr &= ah->ah_txq_status;
ah->ah_txq_imr_txurn &= ah->ah_txq_status;
ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
ah->ah_txq_imr_txeol &= ah->ah_txq_status;
+ ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
+ ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
+ ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
+ ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
AR5K_SIMR0_QCU_TXOK) |
@@ -448,8 +465,24 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
AR5K_SIMR1_QCU_TXERR) |
AR5K_REG_SM(ah->ah_txq_imr_txeol,
AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
- ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txurn,
- AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2);
+ /* Update simr2 but don't overwrite rest simr2 settings */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
+ AR5K_REG_SM(ah->ah_txq_imr_txurn,
+ AR5K_SIMR2_QCU_TXURN));
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
+ AR5K_SIMR3_QCBRORN) |
+ AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
+ AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
+ AR5K_SIMR4_QTRIG), AR5K_SIMR4);
+ /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
+ ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
+ AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
+ /* No queue has TXNOFRM enabled, disable the interrupt
+ * by setting AR5K_TXNOFRM to zero */
+ if (ah->ah_txq_imr_nofrm == 0)
+ ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
}
return 0;
diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h
index e557fe178bbf..91aaeaf88199 100644
--- a/drivers/net/wireless/ath5k/reg.h
+++ b/drivers/net/wireless/ath5k/reg.h
@@ -234,6 +234,7 @@
#define AR5K_TXNOFRM 0x004c
#define AR5K_TXNOFRM_M 0x000003ff
#define AR5K_TXNOFRM_QCU 0x000ffc00
+#define AR5K_TXNOFRM_QCU_S 10
/*
* Receive frame gap timeout register
@@ -350,7 +351,7 @@
#define AR5K_SISR3 0x0090 /* Register Address [5211+] */
#define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */
-#define AR5K_SISR3_QCBORN_S 0
+#define AR5K_SISR3_QCBRORN_S 0
#define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */
#define AR5K_SISR3_QCBRURN_S 16
@@ -1113,14 +1114,16 @@
#define AR5K_PCU_MAX 0x8fff
/*
- * First station id register (MAC address in lower 32 bits)
+ * First station id register (Lower 32 bits of MAC address)
*/
-#define AR5K_STA_ID0 0x8000
+#define AR5K_STA_ID0 0x8000
+#define AR5K_STA_ID0_ARRD_L32 0xffffffff
/*
- * Second station id register (MAC address in upper 16 bits)
+ * Second station id register (Upper 16 bits of MAC address + PCU settings)
*/
#define AR5K_STA_ID1 0x8004 /* Register Address */
+#define AR5K_STA_ID1_ADDR_U16 0x0000ffff /* Upper 16 bits of MAC addres */
#define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */
#define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */
#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */
@@ -1726,6 +1729,7 @@
#define AR5K_MISC_MODE 0x8120 /* Register Address */
#define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */
#define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */
+#define AR5K_MISC_MODE_COMBINED_MIC 0x00000004 /* use rx/tx MIC key */
/* more bits */
/*
@@ -1810,6 +1814,10 @@
#define AR5K_KEYTABLE_MAC1(_n) AR5K_KEYTABLE_OFF(_n, 7)
#define AR5K_KEYTABLE_VALID 0x00008000
+/* If key type is TKIP and MIC is enabled
+ * MIC key goes in offset entry + 64 */
+#define AR5K_KEYTABLE_MIC_OFFSET 64
+
/* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit
* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit
* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit
diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c
index 1b6d45b6772d..dc2d7d8bdb7a 100644
--- a/drivers/net/wireless/ath5k/reset.c
+++ b/drivers/net/wireless/ath5k/reset.c
@@ -674,7 +674,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
(ee->ee_switch_settling[ee_mode] << 7) & 0x3f80,
0xffffc07f);
AR5K_REG_MASKED_BITS(ah, AR5K_PHY_GAIN,
- (ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000,
+ (ee->ee_atn_tx_rx[ee_mode] << 12) & 0x3f000,
0xfffc0fff);
AR5K_REG_MASKED_BITS(ah, AR5K_PHY_DESIRED_SIZE,
(ee->ee_adc_desired_size[ee_mode] & 0x00ff) |
@@ -842,9 +842,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
*
* XXX: Find an interval that's OK for all cards...
*/
- ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
- if (ret)
- return ret;
+ ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
/*
* Reset queues and start beacon timers at the end of the reset routine
@@ -864,8 +862,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
/* Pre-enable interrupts on 5211/5212*/
if (ah->ah_version != AR5K_AR5210)
- ath5k_hw_set_imr(ah, AR5K_INT_RX | AR5K_INT_TX |
- AR5K_INT_FATAL);
+ ath5k_hw_set_imr(ah, ah->ah_imr);
/*
* Set RF kill flags if supported by the device (read from the EEPROM)
diff --git a/drivers/net/wireless/ath9k/Kconfig b/drivers/net/wireless/ath9k/Kconfig
index 80a692430413..c43bd321f97f 100644
--- a/drivers/net/wireless/ath9k/Kconfig
+++ b/drivers/net/wireless/ath9k/Kconfig
@@ -9,3 +9,14 @@ config ATH9K
Atheros IEEE 802.11n AR5008 and AR9001 family of chipsets.
If you choose to build a module, it'll be called ath9k.
+
+config ATH9K_DEBUG
+ bool "Atheros ath9k debugging"
+ depends on ATH9K
+ ---help---
+ Say Y, if you need ath9k to display debug messages.
+ Pass the debug mask as a module parameter:
+
+ modprobe ath9k debug=0x00002000
+
+ Look in ath9k/core.h for possible debug masks
diff --git a/drivers/net/wireless/ath9k/Makefile b/drivers/net/wireless/ath9k/Makefile
index a6411517e5f8..1209d14613ac 100644
--- a/drivers/net/wireless/ath9k/Makefile
+++ b/drivers/net/wireless/ath9k/Makefile
@@ -1,11 +1,16 @@
ath9k-y += hw.o \
+ eeprom.o \
+ mac.o \
+ calib.o \
+ ani.o \
phy.o \
regd.o \
beacon.o \
main.o \
recv.o \
xmit.o \
- rc.o \
- core.o
+ rc.o
+
+ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath9k/ani.c b/drivers/net/wireless/ath9k/ani.c
new file mode 100644
index 000000000000..251e2d9a7a4a
--- /dev/null
+++ b/drivers/net/wireless/ath9k/ani.c
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hw.h"
+#include "reg.h"
+#include "phy.h"
+
+static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
+ if (ahp->ah_ani[i].c.channel == chan->channel)
+ return i;
+ if (ahp->ah_ani[i].c.channel == 0) {
+ ahp->ah_ani[i].c.channel = chan->channel;
+ ahp->ah_ani[i].c.channelFlags = chan->channelFlags;
+ return i;
+ }
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "No more channel states left. Using channel 0\n");
+
+ return 0;
+}
+
+static bool ath9k_hw_ani_control(struct ath_hal *ah,
+ enum ath9k_ani_cmd cmd, int param)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState = ahp->ah_curani;
+
+ switch (cmd & ahp->ah_ani_function) {
+ case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
+ u32 level = param;
+
+ if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned)ARRAY_SIZE(ahp->ah_totalSizeDesired));
+ return false;
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
+ AR_PHY_DESIRED_SZ_TOT_DES,
+ ahp->ah_totalSizeDesired[level]);
+ REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
+ AR_PHY_AGC_CTL1_COARSE_LOW,
+ ahp->ah_coarseLow[level]);
+ REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
+ AR_PHY_AGC_CTL1_COARSE_HIGH,
+ ahp->ah_coarseHigh[level]);
+ REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
+ AR_PHY_FIND_SIG_FIRPWR,
+ ahp->ah_firpwr[level]);
+
+ if (level > aniState->noiseImmunityLevel)
+ ahp->ah_stats.ast_ani_niup++;
+ else if (level < aniState->noiseImmunityLevel)
+ ahp->ah_stats.ast_ani_nidown++;
+ aniState->noiseImmunityLevel = level;
+ break;
+ }
+ case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
+ const int m1ThreshLow[] = { 127, 50 };
+ const int m2ThreshLow[] = { 127, 40 };
+ const int m1Thresh[] = { 127, 0x4d };
+ const int m2Thresh[] = { 127, 0x40 };
+ const int m2CountThr[] = { 31, 16 };
+ const int m2CountThrLow[] = { 63, 48 };
+ u32 on = param ? 1 : 0;
+
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+ m1ThreshLow[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+ m2ThreshLow[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+ AR_PHY_SFCORR_M1_THRESH,
+ m1Thresh[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+ AR_PHY_SFCORR_M2_THRESH,
+ m2Thresh[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+ AR_PHY_SFCORR_M2COUNT_THR,
+ m2CountThr[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+ m2CountThrLow[on]);
+
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+ AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+ m1ThreshLow[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+ AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+ m2ThreshLow[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+ AR_PHY_SFCORR_EXT_M1_THRESH,
+ m1Thresh[on]);
+ REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+ AR_PHY_SFCORR_EXT_M2_THRESH,
+ m2Thresh[on]);
+
+ if (on)
+ REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+ else
+ REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
+ AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+
+ if (!on != aniState->ofdmWeakSigDetectOff) {
+ if (on)
+ ahp->ah_stats.ast_ani_ofdmon++;
+ else
+ ahp->ah_stats.ast_ani_ofdmoff++;
+ aniState->ofdmWeakSigDetectOff = !on;
+ }
+ break;
+ }
+ case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{
+ const int weakSigThrCck[] = { 8, 6 };
+ u32 high = param ? 1 : 0;
+
+ REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
+ AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK,
+ weakSigThrCck[high]);
+ if (high != aniState->cckWeakSigThreshold) {
+ if (high)
+ ahp->ah_stats.ast_ani_cckhigh++;
+ else
+ ahp->ah_stats.ast_ani_ccklow++;
+ aniState->cckWeakSigThreshold = high;
+ }
+ break;
+ }
+ case ATH9K_ANI_FIRSTEP_LEVEL:{
+ const int firstep[] = { 0, 4, 8 };
+ u32 level = param;
+
+ if (level >= ARRAY_SIZE(firstep)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned) ARRAY_SIZE(firstep));
+ return false;
+ }
+ REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
+ AR_PHY_FIND_SIG_FIRSTEP,
+ firstep[level]);
+ if (level > aniState->firstepLevel)
+ ahp->ah_stats.ast_ani_stepup++;
+ else if (level < aniState->firstepLevel)
+ ahp->ah_stats.ast_ani_stepdown++;
+ aniState->firstepLevel = level;
+ break;
+ }
+ case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
+ const int cycpwrThr1[] =
+ { 2, 4, 6, 8, 10, 12, 14, 16 };
+ u32 level = param;
+
+ if (level >= ARRAY_SIZE(cycpwrThr1)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned)
+ ARRAY_SIZE(cycpwrThr1));
+ return false;
+ }
+ REG_RMW_FIELD(ah, AR_PHY_TIMING5,
+ AR_PHY_TIMING5_CYCPWR_THR1,
+ cycpwrThr1[level]);
+ if (level > aniState->spurImmunityLevel)
+ ahp->ah_stats.ast_ani_spurup++;
+ else if (level < aniState->spurImmunityLevel)
+ ahp->ah_stats.ast_ani_spurdown++;
+ aniState->spurImmunityLevel = level;
+ break;
+ }
+ case ATH9K_ANI_PRESENT:
+ break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "invalid cmd %u\n", cmd);
+ return false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n");
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
+ "ofdmWeakSigDetectOff=%d\n",
+ aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
+ !aniState->ofdmWeakSigDetectOff);
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "cckWeakSigThreshold=%d, "
+ "firstepLevel=%d, listenTime=%d\n",
+ aniState->cckWeakSigThreshold, aniState->firstepLevel,
+ aniState->listenTime);
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
+ aniState->cycleCount, aniState->ofdmPhyErrCount,
+ aniState->cckPhyErrCount);
+
+ return true;
+}
+
+static void ath9k_hw_update_mibstats(struct ath_hal *ah,
+ struct ath9k_mib_stats *stats)
+{
+ stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
+ stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
+ stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
+ stats->rts_good += REG_READ(ah, AR_RTS_OK);
+ stats->beacons += REG_READ(ah, AR_BEACON_CNT);
+}
+
+static void ath9k_ani_restart(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState;
+
+ if (!DO_ANI(ah))
+ return;
+
+ aniState = ahp->ah_curani;
+
+ aniState->listenTime = 0;
+ if (ahp->ah_hasHwPhyCounters) {
+ if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
+ aniState->ofdmPhyErrBase = 0;
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "OFDM Trigger is too high for hw counters\n");
+ } else {
+ aniState->ofdmPhyErrBase =
+ AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
+ }
+ if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
+ aniState->cckPhyErrBase = 0;
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "CCK Trigger is too high for hw counters\n");
+ } else {
+ aniState->cckPhyErrBase =
+ AR_PHY_COUNTMAX - aniState->cckTrigHigh;
+ }
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "Writing ofdmbase=%u cckbase=%u\n",
+ aniState->ofdmPhyErrBase,
+ aniState->cckPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+
+ ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+ }
+ aniState->ofdmPhyErrCount = 0;
+ aniState->cckPhyErrCount = 0;
+}
+
+static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_channel *chan = ah->ah_curchan;
+ struct ar5416AniState *aniState;
+ enum wireless_mode mode;
+ int32_t rssi;
+
+ if (!DO_ANI(ah))
+ return;
+
+ aniState = ahp->ah_curani;
+
+ if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
+ if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+ aniState->noiseImmunityLevel + 1)) {
+ return;
+ }
+ }
+
+ if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
+ if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+ aniState->spurImmunityLevel + 1)) {
+ return;
+ }
+ }
+
+ if (ah->ah_opmode == NL80211_IFTYPE_AP) {
+ if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel + 1);
+ }
+ return;
+ }
+ rssi = BEACON_RSSI(ahp);
+ if (rssi > aniState->rssiThrHigh) {
+ if (!aniState->ofdmWeakSigDetectOff) {
+ if (ath9k_hw_ani_control(ah,
+ ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ false)) {
+ ath9k_hw_ani_control(ah,
+ ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
+ return;
+ }
+ }
+ if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel + 1);
+ return;
+ }
+ } else if (rssi > aniState->rssiThrLow) {
+ if (aniState->ofdmWeakSigDetectOff)
+ ath9k_hw_ani_control(ah,
+ ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ true);
+ if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel + 1);
+ return;
+ } else {
+ mode = ath9k_hw_chan2wmode(ah, chan);
+ if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) {
+ if (!aniState->ofdmWeakSigDetectOff)
+ ath9k_hw_ani_control(ah,
+ ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ false);
+ if (aniState->firstepLevel > 0)
+ ath9k_hw_ani_control(ah,
+ ATH9K_ANI_FIRSTEP_LEVEL, 0);
+ return;
+ }
+ }
+}
+
+static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_channel *chan = ah->ah_curchan;
+ struct ar5416AniState *aniState;
+ enum wireless_mode mode;
+ int32_t rssi;
+
+ if (!DO_ANI(ah))
+ return;
+
+ aniState = ahp->ah_curani;
+ if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
+ if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+ aniState->noiseImmunityLevel + 1)) {
+ return;
+ }
+ }
+ if (ah->ah_opmode == NL80211_IFTYPE_AP) {
+ if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel + 1);
+ }
+ return;
+ }
+ rssi = BEACON_RSSI(ahp);
+ if (rssi > aniState->rssiThrLow) {
+ if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel + 1);
+ } else {
+ mode = ath9k_hw_chan2wmode(ah, chan);
+ if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) {
+ if (aniState->firstepLevel > 0)
+ ath9k_hw_ani_control(ah,
+ ATH9K_ANI_FIRSTEP_LEVEL, 0);
+ }
+ }
+}
+
+static void ath9k_hw_ani_lower_immunity(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState;
+ int32_t rssi;
+
+ aniState = ahp->ah_curani;
+
+ if (ah->ah_opmode == NL80211_IFTYPE_AP) {
+ if (aniState->firstepLevel > 0) {
+ if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel - 1))
+ return;
+ }
+ } else {
+ rssi = BEACON_RSSI(ahp);
+ if (rssi > aniState->rssiThrHigh) {
+ /* XXX: Handle me */
+ } else if (rssi > aniState->rssiThrLow) {
+ if (aniState->ofdmWeakSigDetectOff) {
+ if (ath9k_hw_ani_control(ah,
+ ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ true) == true)
+ return;
+ }
+ if (aniState->firstepLevel > 0) {
+ if (ath9k_hw_ani_control(ah,
+ ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel - 1) == true)
+ return;
+ }
+ } else {
+ if (aniState->firstepLevel > 0) {
+ if (ath9k_hw_ani_control(ah,
+ ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel - 1) == true)
+ return;
+ }
+ }
+ }
+
+ if (aniState->spurImmunityLevel > 0) {
+ if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+ aniState->spurImmunityLevel - 1))
+ return;
+ }
+
+ if (aniState->noiseImmunityLevel > 0) {
+ ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+ aniState->noiseImmunityLevel - 1);
+ return;
+ }
+}
+
+static int32_t ath9k_hw_ani_get_listen_time(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState;
+ u32 txFrameCount, rxFrameCount, cycleCount;
+ int32_t listenTime;
+
+ txFrameCount = REG_READ(ah, AR_TFCNT);
+ rxFrameCount = REG_READ(ah, AR_RFCNT);
+ cycleCount = REG_READ(ah, AR_CCCNT);
+
+ aniState = ahp->ah_curani;
+ if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
+
+ listenTime = 0;
+ ahp->ah_stats.ast_ani_lzero++;
+ } else {
+ int32_t ccdelta = cycleCount - aniState->cycleCount;
+ int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
+ int32_t tfdelta = txFrameCount - aniState->txFrameCount;
+ listenTime = (ccdelta - rfdelta - tfdelta) / 44000;
+ }
+ aniState->cycleCount = cycleCount;
+ aniState->txFrameCount = txFrameCount;
+ aniState->rxFrameCount = rxFrameCount;
+
+ return listenTime;
+}
+
+void ath9k_ani_reset(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState;
+ struct ath9k_channel *chan = ah->ah_curchan;
+ int index;
+
+ if (!DO_ANI(ah))
+ return;
+
+ index = ath9k_hw_get_ani_channel_idx(ah, chan);
+ aniState = &ahp->ah_ani[index];
+ ahp->ah_curani = aniState;
+
+ if (DO_ANI(ah) && ah->ah_opmode != NL80211_IFTYPE_STATION
+ && ah->ah_opmode != NL80211_IFTYPE_ADHOC) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "Reset ANI state opmode %u\n", ah->ah_opmode);
+ ahp->ah_stats.ast_ani_reset++;
+
+ ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
+ ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
+ ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ !ATH9K_ANI_USE_OFDM_WEAK_SIG);
+ ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
+ ATH9K_ANI_CCK_WEAK_SIG_THR);
+
+ ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
+ ATH9K_RX_FILTER_PHYERR);
+
+ if (ah->ah_opmode == NL80211_IFTYPE_AP) {
+ ahp->ah_curani->ofdmTrigHigh =
+ ah->ah_config.ofdm_trig_high;
+ ahp->ah_curani->ofdmTrigLow =
+ ah->ah_config.ofdm_trig_low;
+ ahp->ah_curani->cckTrigHigh =
+ ah->ah_config.cck_trig_high;
+ ahp->ah_curani->cckTrigLow =
+ ah->ah_config.cck_trig_low;
+ }
+ ath9k_ani_restart(ah);
+ return;
+ }
+
+ if (aniState->noiseImmunityLevel != 0)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+ aniState->noiseImmunityLevel);
+ if (aniState->spurImmunityLevel != 0)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+ aniState->spurImmunityLevel);
+ if (aniState->ofdmWeakSigDetectOff)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+ !aniState->ofdmWeakSigDetectOff);
+ if (aniState->cckWeakSigThreshold)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
+ aniState->cckWeakSigThreshold);
+ if (aniState->firstepLevel != 0)
+ ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+ aniState->firstepLevel);
+ if (ahp->ah_hasHwPhyCounters) {
+ ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
+ ~ATH9K_RX_FILTER_PHYERR);
+ ath9k_ani_restart(ah);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+
+ } else {
+ ath9k_ani_restart(ah);
+ ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
+ ATH9K_RX_FILTER_PHYERR);
+ }
+}
+
+void ath9k_hw_ani_monitor(struct ath_hal *ah,
+ const struct ath9k_node_stats *stats,
+ struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416AniState *aniState;
+ int32_t listenTime;
+
+ aniState = ahp->ah_curani;
+ ahp->ah_stats.ast_nodestats = *stats;
+
+ listenTime = ath9k_hw_ani_get_listen_time(ah);
+ if (listenTime < 0) {
+ ahp->ah_stats.ast_ani_lneg++;
+ ath9k_ani_restart(ah);
+ return;
+ }
+
+ aniState->listenTime += listenTime;
+
+ if (ahp->ah_hasHwPhyCounters) {
+ u32 phyCnt1, phyCnt2;
+ u32 ofdmPhyErrCnt, cckPhyErrCnt;
+
+ ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+
+ phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
+ phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
+
+ if (phyCnt1 < aniState->ofdmPhyErrBase ||
+ phyCnt2 < aniState->cckPhyErrBase) {
+ if (phyCnt1 < aniState->ofdmPhyErrBase) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "phyCnt1 0x%x, resetting "
+ "counter value to 0x%x\n",
+ phyCnt1,
+ aniState->ofdmPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_1,
+ aniState->ofdmPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_1,
+ AR_PHY_ERR_OFDM_TIMING);
+ }
+ if (phyCnt2 < aniState->cckPhyErrBase) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "phyCnt2 0x%x, resetting "
+ "counter value to 0x%x\n",
+ phyCnt2,
+ aniState->cckPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_2,
+ aniState->cckPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_2,
+ AR_PHY_ERR_CCK_TIMING);
+ }
+ return;
+ }
+
+ ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
+ ahp->ah_stats.ast_ani_ofdmerrs +=
+ ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
+ aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
+
+ cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
+ ahp->ah_stats.ast_ani_cckerrs +=
+ cckPhyErrCnt - aniState->cckPhyErrCount;
+ aniState->cckPhyErrCount = cckPhyErrCnt;
+ }
+
+ if (!DO_ANI(ah))
+ return;
+
+ if (aniState->listenTime > 5 * ahp->ah_aniPeriod) {
+ if (aniState->ofdmPhyErrCount <= aniState->listenTime *
+ aniState->ofdmTrigLow / 1000 &&
+ aniState->cckPhyErrCount <= aniState->listenTime *
+ aniState->cckTrigLow / 1000)
+ ath9k_hw_ani_lower_immunity(ah);
+ ath9k_ani_restart(ah);
+ } else if (aniState->listenTime > ahp->ah_aniPeriod) {
+ if (aniState->ofdmPhyErrCount > aniState->listenTime *
+ aniState->ofdmTrigHigh / 1000) {
+ ath9k_hw_ani_ofdm_err_trigger(ah);
+ ath9k_ani_restart(ah);
+ } else if (aniState->cckPhyErrCount >
+ aniState->listenTime * aniState->cckTrigHigh /
+ 1000) {
+ ath9k_hw_ani_cck_err_trigger(ah);
+ ath9k_ani_restart(ah);
+ }
+ }
+}
+
+bool ath9k_hw_phycounters(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ahp->ah_hasHwPhyCounters ? true : false;
+}
+
+void ath9k_enable_mib_counters(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n");
+
+ ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+
+ REG_WRITE(ah, AR_FILT_OFDM, 0);
+ REG_WRITE(ah, AR_FILT_CCK, 0);
+ REG_WRITE(ah, AR_MIBC,
+ ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
+ & 0x0f);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+ REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+}
+
+void ath9k_hw_disable_mib_counters(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n");
+
+ REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC);
+
+ ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+
+ REG_WRITE(ah, AR_FILT_OFDM, 0);
+ REG_WRITE(ah, AR_FILT_CCK, 0);
+}
+
+u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
+ u32 *rxc_pcnt,
+ u32 *rxf_pcnt,
+ u32 *txf_pcnt)
+{
+ static u32 cycles, rx_clear, rx_frame, tx_frame;
+ u32 good = 1;
+
+ u32 rc = REG_READ(ah, AR_RCCNT);
+ u32 rf = REG_READ(ah, AR_RFCNT);
+ u32 tf = REG_READ(ah, AR_TFCNT);
+ u32 cc = REG_READ(ah, AR_CCCNT);
+
+ if (cycles == 0 || cycles > cc) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "cycle counter wrap. ExtBusy = 0\n");
+ good = 0;
+ } else {
+ u32 cc_d = cc - cycles;
+ u32 rc_d = rc - rx_clear;
+ u32 rf_d = rf - rx_frame;
+ u32 tf_d = tf - tx_frame;
+
+ if (cc_d != 0) {
+ *rxc_pcnt = rc_d * 100 / cc_d;
+ *rxf_pcnt = rf_d * 100 / cc_d;
+ *txf_pcnt = tf_d * 100 / cc_d;
+ } else {
+ good = 0;
+ }
+ }
+
+ cycles = cc;
+ rx_frame = rf;
+ rx_clear = rc;
+ tx_frame = tf;
+
+ return good;
+}
+
+/*
+ * Process a MIB interrupt. We may potentially be invoked because
+ * any of the MIB counters overflow/trigger so don't assume we're
+ * here because a PHY error counter triggered.
+ */
+void ath9k_hw_procmibevent(struct ath_hal *ah,
+ const struct ath9k_node_stats *stats)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 phyCnt1, phyCnt2;
+
+ /* Reset these counters regardless */
+ REG_WRITE(ah, AR_FILT_OFDM, 0);
+ REG_WRITE(ah, AR_FILT_CCK, 0);
+ if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
+ REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
+
+ /* Clear the mib counters and save them in the stats */
+ ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+ ahp->ah_stats.ast_nodestats = *stats;
+
+ if (!DO_ANI(ah))
+ return;
+
+ /* NB: these are not reset-on-read */
+ phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
+ phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
+ if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
+ ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
+ struct ar5416AniState *aniState = ahp->ah_curani;
+ u32 ofdmPhyErrCnt, cckPhyErrCnt;
+
+ /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
+ ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
+ ahp->ah_stats.ast_ani_ofdmerrs +=
+ ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
+ aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
+
+ cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
+ ahp->ah_stats.ast_ani_cckerrs +=
+ cckPhyErrCnt - aniState->cckPhyErrCount;
+ aniState->cckPhyErrCount = cckPhyErrCnt;
+
+ /*
+ * NB: figure out which counter triggered. If both
+ * trigger we'll only deal with one as the processing
+ * clobbers the error counter so the trigger threshold
+ * check will never be true.
+ */
+ if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
+ ath9k_hw_ani_ofdm_err_trigger(ah);
+ if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
+ ath9k_hw_ani_cck_err_trigger(ah);
+ /* NB: always restart to insure the h/w counters are reset */
+ ath9k_ani_restart(ah);
+ }
+}
+
+void ath9k_hw_ani_setup(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
+ const int coarseHigh[] = { -14, -14, -14, -14, -12 };
+ const int coarseLow[] = { -64, -64, -64, -64, -70 };
+ const int firpwr[] = { -78, -78, -78, -78, -80 };
+
+ for (i = 0; i < 5; i++) {
+ ahp->ah_totalSizeDesired[i] = totalSizeDesired[i];
+ ahp->ah_coarseHigh[i] = coarseHigh[i];
+ ahp->ah_coarseLow[i] = coarseLow[i];
+ ahp->ah_firpwr[i] = firpwr[i];
+ }
+}
+
+void ath9k_hw_ani_attach(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Attach ANI\n");
+
+ ahp->ah_hasHwPhyCounters = 1;
+
+ memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani));
+ for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
+ ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
+ ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
+ ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
+ ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
+ ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
+ ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
+ ahp->ah_ani[i].ofdmWeakSigDetectOff =
+ !ATH9K_ANI_USE_OFDM_WEAK_SIG;
+ ahp->ah_ani[i].cckWeakSigThreshold =
+ ATH9K_ANI_CCK_WEAK_SIG_THR;
+ ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
+ ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
+ if (ahp->ah_hasHwPhyCounters) {
+ ahp->ah_ani[i].ofdmPhyErrBase =
+ AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
+ ahp->ah_ani[i].cckPhyErrBase =
+ AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
+ }
+ }
+ if (ahp->ah_hasHwPhyCounters) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "Setting OfdmErrBase = 0x%08x\n",
+ ahp->ah_ani[0].ofdmPhyErrBase);
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
+ ahp->ah_ani[0].cckPhyErrBase);
+
+ REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase);
+ REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase);
+ ath9k_enable_mib_counters(ah);
+ }
+ ahp->ah_aniPeriod = ATH9K_ANI_PERIOD;
+ if (ah->ah_config.enable_ani)
+ ahp->ah_procPhyErr |= HAL_PROCESS_ANI;
+}
+
+void ath9k_hw_ani_detach(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detach ANI\n");
+
+ if (ahp->ah_hasHwPhyCounters) {
+ ath9k_hw_disable_mib_counters(ah);
+ REG_WRITE(ah, AR_PHY_ERR_1, 0);
+ REG_WRITE(ah, AR_PHY_ERR_2, 0);
+ }
+}
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h
index accace5f7efb..d27813502953 100644
--- a/drivers/net/wireless/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath9k/ath9k.h
@@ -26,6 +26,7 @@
#define AR9160_DEVID_PCI 0x0027
#define AR9280_DEVID_PCI 0x0029
#define AR9280_DEVID_PCIE 0x002a
+#define AR9285_DEVID_PCIE 0x002b
#define AR5416_AR9100_DEVID 0x000b
@@ -138,6 +139,19 @@ struct ath_desc {
#define ATH9K_TXDESC_NOACK 0x0002
#define ATH9K_TXDESC_RTSENA 0x0004
#define ATH9K_TXDESC_CTSENA 0x0008
+/* ATH9K_TXDESC_INTREQ forces a tx interrupt to be generated for
+ * the descriptor its marked on. We take a tx interrupt to reap
+ * descriptors when the h/w hits an EOL condition or
+ * when the descriptor is specifically marked to generate
+ * an interrupt with this flag. Descriptors should be
+ * marked periodically to insure timely replenishing of the
+ * supply needed for sending frames. Defering interrupts
+ * reduces system load and potentially allows more concurrent
+ * work to be done but if done to aggressively can cause
+ * senders to backup. When the hardware queue is left too
+ * large rate control information may also be too out of
+ * date. An Alternative for this is TX interrupt mitigation
+ * but this needs more testing. */
#define ATH9K_TXDESC_INTREQ 0x0010
#define ATH9K_TXDESC_VEOL 0x0020
#define ATH9K_TXDESC_EXT_ONLY 0x0040
@@ -388,22 +402,6 @@ enum ath9k_int {
ATH9K_INT_NOCARD = 0xffffffff
};
-struct ath9k_rate_table {
- int rateCount;
- u8 rateCodeToIndex[256];
- struct {
- u8 valid;
- u8 phy;
- u32 rateKbps;
- u8 rateCode;
- u8 shortPreamble;
- u8 dot11Rate;
- u8 controlRate;
- u16 lpAckDuration;
- u16 spAckDuration;
- } info[32];
-};
-
#define ATH9K_RATESERIES_RTS_CTS 0x0001
#define ATH9K_RATESERIES_2040 0x0002
#define ATH9K_RATESERIES_HALFGI 0x0004
@@ -479,12 +477,10 @@ struct ath9k_channel {
(((_c)->channelFlags & CHANNEL_A_HT20) == CHANNEL_A_HT20) || \
(((_c)->channelFlags & CHANNEL_A_HT40PLUS) == CHANNEL_A_HT40PLUS) || \
(((_c)->channelFlags & CHANNEL_A_HT40MINUS) == CHANNEL_A_HT40MINUS))
-#define IS_CHAN_B(_c) (((_c)->channelFlags & CHANNEL_B) == CHANNEL_B)
#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
(((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
(((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
(((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
-#define IS_CHAN_CCK(_c) (((_c)->channelFlags & CHANNEL_CCK) != 0)
#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
@@ -493,6 +489,7 @@ struct ath9k_channel {
#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
/* These macros check chanmode and not channelFlags */
+#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \
((_c)->chanmode == CHANNEL_G_HT20))
#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \
@@ -651,13 +648,6 @@ enum ath9k_ant_setting {
ATH9K_ANT_FIXED_B
};
-enum ath9k_opmode {
- ATH9K_M_STA = 1,
- ATH9K_M_IBSS = 0,
- ATH9K_M_HOSTAP = 6,
- ATH9K_M_MONITOR = 8
-};
-
#define ATH9K_SLOT_TIME_6 6
#define ATH9K_SLOT_TIME_9 9
#define ATH9K_SLOT_TIME_20 20
@@ -689,13 +679,19 @@ enum ath9k_ani_cmd {
ATH9K_ANI_ALL = 0xff
};
-enum phytype {
- PHY_DS,
- PHY_FH,
- PHY_OFDM,
- PHY_HT,
+enum {
+ WLAN_RC_PHY_OFDM,
+ WLAN_RC_PHY_CCK,
+ WLAN_RC_PHY_HT_20_SS,
+ WLAN_RC_PHY_HT_20_DS,
+ WLAN_RC_PHY_HT_40_SS,
+ WLAN_RC_PHY_HT_40_DS,
+ WLAN_RC_PHY_HT_20_SS_HGI,
+ WLAN_RC_PHY_HT_20_DS_HGI,
+ WLAN_RC_PHY_HT_40_SS_HGI,
+ WLAN_RC_PHY_HT_40_DS_HGI,
+ WLAN_RC_PHY_MAX
};
-#define PHY_CCK PHY_DS
enum ath9k_tp_scale {
ATH9K_TP_SCALE_MAX = 0,
@@ -778,7 +774,8 @@ struct ath_hal {
void __iomem *ah_sh;
struct ath_softc *ah_sc;
- enum ath9k_opmode ah_opmode;
+
+ enum nl80211_iftype ah_opmode;
struct ath9k_ops_config ah_config;
struct ath9k_hw_capabilities ah_caps;
@@ -815,195 +812,246 @@ struct chan_centers {
u16 ext_center;
};
-int ath_hal_getcapability(struct ath_hal *ah,
- enum ath9k_capability_type type,
- u32 capability,
- u32 *result);
-const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah,
- u32 mode);
-void ath9k_hw_detach(struct ath_hal *ah);
-struct ath_hal *ath9k_hw_attach(u16 devid,
- struct ath_softc *sc,
- void __iomem *mem,
- int *error);
-bool ath9k_regd_init_channels(struct ath_hal *ah,
- u32 maxchans, u32 *nchans,
- u8 *regclassids,
- u32 maxregids, u32 *nregids,
- u16 cc,
- bool enableOutdoor,
- bool enableExtendedChannels);
+struct ath_rate_table;
+
+/* Helpers */
+
+enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah,
+ const struct ath9k_channel *chan);
+bool ath9k_hw_wait(struct ath_hal *ah, u32 reg, u32 mask, u32 val);
+u32 ath9k_hw_reverse_bits(u32 val, u32 n);
+bool ath9k_get_channel_edges(struct ath_hal *ah,
+ u16 flags, u16 *low,
+ u16 *high);
+u16 ath9k_hw_computetxtime(struct ath_hal *ah,
+ struct ath_rate_table *rates,
+ u32 frameLen, u16 rateix,
+ bool shortPreamble);
u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags);
-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah,
- enum ath9k_int ints);
-bool ath9k_hw_reset(struct ath_hal *ah,
- struct ath9k_channel *chan,
+void ath9k_hw_get_channel_centers(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct chan_centers *centers);
+
+/* Attach, Detach */
+
+const char *ath9k_hw_probe(u16 vendorid, u16 devid);
+void ath9k_hw_detach(struct ath_hal *ah);
+struct ath_hal *ath9k_hw_attach(u16 devid, struct ath_softc *sc,
+ void __iomem *mem, int *error);
+void ath9k_hw_rfdetach(struct ath_hal *ah);
+
+
+/* HW Reset */
+
+bool ath9k_hw_reset(struct ath_hal *ah, struct ath9k_channel *chan,
enum ath9k_ht_macmode macmode,
u8 txchainmask, u8 rxchainmask,
enum ath9k_ht_extprotspacing extprotspacing,
- bool bChannelChange,
- int *status);
-bool ath9k_hw_phy_disable(struct ath_hal *ah);
-void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
- bool *isCalDone);
-void ath9k_hw_ani_monitor(struct ath_hal *ah,
- const struct ath9k_node_stats *stats,
- struct ath9k_channel *chan);
-bool ath9k_hw_calibrate(struct ath_hal *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal,
- bool *isCalDone);
-s16 ath9k_hw_getchan_noise(struct ath_hal *ah,
- struct ath9k_channel *chan);
-void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
- u16 assocId);
-void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits);
-void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
- u16 assocId);
-bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q);
-void ath9k_hw_reset_tsf(struct ath_hal *ah);
-bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry);
-bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry,
- const u8 *mac);
-bool ath9k_hw_set_keycache_entry(struct ath_hal *ah,
- u16 entry,
- const struct ath9k_keyval *k,
- const u8 *mac,
- int xorKey);
-bool ath9k_hw_set_tsfadjust(struct ath_hal *ah,
- u32 setting);
-void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore);
-bool ath9k_hw_intrpend(struct ath_hal *ah);
-bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked);
-bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah,
- bool bIncTrigLevel);
-void ath9k_hw_procmibevent(struct ath_hal *ah,
- const struct ath9k_node_stats *stats);
-bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set);
-void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode);
-bool ath9k_hw_phycounters(struct ath_hal *ah);
+ bool bChannelChange, int *status);
+
+/* Key Cache Management */
+
bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry);
-bool ath9k_hw_getcapability(struct ath_hal *ah,
- enum ath9k_capability_type type,
- u32 capability,
- u32 *result);
-bool ath9k_hw_setcapability(struct ath_hal *ah,
- enum ath9k_capability_type type,
- u32 capability,
- u32 setting,
- int *status);
-u32 ath9k_hw_getdefantenna(struct ath_hal *ah);
-void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac);
-void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask);
-bool ath9k_hw_setbssidmask(struct ath_hal *ah,
- const u8 *mask);
+bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, const u8 *mac);
+bool ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry,
+ const struct ath9k_keyval *k,
+ const u8 *mac, int xorKey);
+bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry);
+
+/* Power Management */
+
bool ath9k_hw_setpower(struct ath_hal *ah,
enum ath9k_power_mode mode);
-enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah);
-u64 ath9k_hw_gettsf64(struct ath_hal *ah);
+void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore);
+
+/* Beacon timers */
+
+void ath9k_hw_beaconinit(struct ath_hal *ah, u32 next_beacon, u32 beacon_period);
+void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
+ const struct ath9k_beacon_state *bs);
+/* HW Capabilities */
+
+bool ath9k_hw_fill_cap_info(struct ath_hal *ah);
+bool ath9k_hw_getcapability(struct ath_hal *ah, enum ath9k_capability_type type,
+ u32 capability, u32 *result);
+bool ath9k_hw_setcapability(struct ath_hal *ah, enum ath9k_capability_type type,
+ u32 capability, u32 setting, int *status);
+
+/* GPIO / RFKILL / Antennae */
+
+void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio);
+u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio);
+void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio,
+ u32 ah_signal_type);
+void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 val);
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+void ath9k_enable_rfkill(struct ath_hal *ah);
+#endif
+int ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg);
u32 ath9k_hw_getdefantenna(struct ath_hal *ah);
-bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us);
+void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna);
bool ath9k_hw_setantennaswitch(struct ath_hal *ah,
enum ath9k_ant_setting settings,
struct ath9k_channel *chan,
u8 *tx_chainmask,
u8 *rx_chainmask,
u8 *antenna_cfgd);
-void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna);
-int ath9k_hw_select_antconfig(struct ath_hal *ah,
- u32 cfg);
-bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q,
- u32 txdp);
+
+/* General Operation */
+
+u32 ath9k_hw_getrxfilter(struct ath_hal *ah);
+void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits);
+bool ath9k_hw_phy_disable(struct ath_hal *ah);
+bool ath9k_hw_disable(struct ath_hal *ah);
+bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit);
+void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac);
+bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac);
+void ath9k_hw_setopmode(struct ath_hal *ah);
+void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, u32 filter1);
+void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask);
+bool ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask);
+void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, u16 assocId);
+u64 ath9k_hw_gettsf64(struct ath_hal *ah);
+void ath9k_hw_reset_tsf(struct ath_hal *ah);
+bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, u32 setting);
+bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us);
+void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode);
+
+/* Regulatory */
+
+bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah);
+struct ath9k_channel* ath9k_regd_check_channel(struct ath_hal *ah,
+ const struct ath9k_channel *c);
+u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan);
+u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
+ struct ath9k_channel *chan);
+bool ath9k_regd_init_channels(struct ath_hal *ah,
+ u32 maxchans, u32 *nchans, u8 *regclassids,
+ u32 maxregids, u32 *nregids, u16 cc,
+ bool enableOutdoor, bool enableExtendedChannels);
+
+/* ANI */
+
+void ath9k_ani_reset(struct ath_hal *ah);
+void ath9k_hw_ani_monitor(struct ath_hal *ah,
+ const struct ath9k_node_stats *stats,
+ struct ath9k_channel *chan);
+bool ath9k_hw_phycounters(struct ath_hal *ah);
+void ath9k_enable_mib_counters(struct ath_hal *ah);
+void ath9k_hw_disable_mib_counters(struct ath_hal *ah);
+u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
+ u32 *rxc_pcnt,
+ u32 *rxf_pcnt,
+ u32 *txf_pcnt);
+void ath9k_hw_procmibevent(struct ath_hal *ah,
+ const struct ath9k_node_stats *stats);
+void ath9k_hw_ani_setup(struct ath_hal *ah);
+void ath9k_hw_ani_attach(struct ath_hal *ah);
+void ath9k_hw_ani_detach(struct ath_hal *ah);
+
+/* Calibration */
+
+void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
+ bool *isCalDone);
+void ath9k_hw_start_nfcal(struct ath_hal *ah);
+void ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan);
+int16_t ath9k_hw_getnf(struct ath_hal *ah,
+ struct ath9k_channel *chan);
+void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah);
+s16 ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan);
+bool ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal,
+ bool *isCalDone);
+bool ath9k_hw_init_cal(struct ath_hal *ah,
+ struct ath9k_channel *chan);
+
+
+/* EEPROM */
+
+int ath9k_hw_set_txpower(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u16 cfgCtl,
+ u8 twiceAntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u8 powerLimit);
+void ath9k_hw_set_addac(struct ath_hal *ah, struct ath9k_channel *chan);
+bool ath9k_hw_set_power_per_rate_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *ratesArray,
+ u16 cfgCtl,
+ u8 AntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u8 powerLimit);
+bool ath9k_hw_set_power_cal_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *pTxPowerIndexOffset);
+bool ath9k_hw_eeprom_set_board_values(struct ath_hal *ah,
+ struct ath9k_channel *chan);
+int ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u8 index, u16 *config);
+u8 ath9k_hw_get_num_ant_config(struct ath_hal *ah,
+ enum ieee80211_band freq_band);
+u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah, u16 i, bool is2GHz);
+int ath9k_hw_eeprom_attach(struct ath_hal *ah);
+
+/* Interrupt Handling */
+
+bool ath9k_hw_intrpend(struct ath_hal *ah);
+bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked);
+enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah);
+enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints);
+
+/* MAC (PCU/QCU) */
+
+u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q);
+bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, u32 txdp);
bool ath9k_hw_txstart(struct ath_hal *ah, u32 q);
-u16 ath9k_hw_computetxtime(struct ath_hal *ah,
- const struct ath9k_rate_table *rates,
- u32 frameLen, u16 rateix,
- bool shortPreamble);
+u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q);
+bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel);
+bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q);
+bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 segLen, bool firstSeg,
+ bool lastSeg, const struct ath_desc *ds0);
+void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds);
+int ath9k_hw_txprocdesc(struct ath_hal *ah, struct ath_desc *ds);
+void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
+ u32 keyIx, enum ath9k_key_type keyType, u32 flags);
void ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds,
struct ath_desc *lastds,
u32 durUpdateEn, u32 rtsctsRate,
u32 rtsctsDuration,
struct ath9k_11n_rate_series series[],
u32 nseries, u32 flags);
-void ath9k_hw_set11n_burstduration(struct ath_hal *ah,
- struct ath_desc *ds,
+void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds,
+ u32 aggrLen);
+void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds,
+ u32 numDelims);
+void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds);
+void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds);
+void ath9k_hw_set11n_burstduration(struct ath_hal *ah, struct ath_desc *ds,
u32 burstDuration);
-void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds);
-u32 ath9k_hw_reverse_bits(u32 val, u32 n);
-bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q);
-u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan);
-u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
- struct ath9k_channel *chan);
-u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags);
-bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q,
- struct ath9k_tx_queue_info *qinfo);
+void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, struct ath_desc *ds,
+ u32 vmf);
+void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs);
bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q,
const struct ath9k_tx_queue_info *qinfo);
-struct ath9k_channel *ath9k_regd_check_channel(struct ath_hal *ah,
- const struct ath9k_channel *c);
-void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 pktLen, enum ath9k_pkt_type type,
- u32 txPower, u32 keyIx,
- enum ath9k_key_type keyType, u32 flags);
-bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 segLen, bool firstSeg,
- bool lastSeg,
- const struct ath_desc *ds0);
-u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
- u32 *rxc_pcnt,
- u32 *rxf_pcnt,
- u32 *txf_pcnt);
-void ath9k_hw_dmaRegDump(struct ath_hal *ah);
-void ath9k_hw_beaconinit(struct ath_hal *ah,
- u32 next_beacon, u32 beacon_period);
-void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
- const struct ath9k_beacon_state *bs);
+bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q,
+ struct ath9k_tx_queue_info *qinfo);
+int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type,
+ const struct ath9k_tx_queue_info *qinfo);
+bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q);
+bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q);
+int ath9k_hw_rxprocdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 pa, struct ath_desc *nds, u64 tsf);
bool ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds,
u32 size, u32 flags);
+bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set);
void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp);
void ath9k_hw_rxena(struct ath_hal *ah);
-void ath9k_hw_setopmode(struct ath_hal *ah);
-bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac);
-void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0,
- u32 filter1);
-u32 ath9k_hw_getrxfilter(struct ath_hal *ah);
void ath9k_hw_startpcureceive(struct ath_hal *ah);
void ath9k_hw_stoppcurecv(struct ath_hal *ah);
bool ath9k_hw_stopdmarecv(struct ath_hal *ah);
-int ath9k_hw_rxprocdesc(struct ath_hal *ah,
- struct ath_desc *ds, u32 pa,
- struct ath_desc *nds, u64 tsf);
-u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q);
-int ath9k_hw_txprocdesc(struct ath_hal *ah,
- struct ath_desc *ds);
-void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds,
- u32 numDelims);
-void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds,
- u32 aggrLen);
-void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds);
-bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q);
-void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs);
-void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds);
-void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah,
- struct ath_desc *ds, u32 vmf);
-bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit);
-bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah);
-int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type,
- const struct ath9k_tx_queue_info *qinfo);
-u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q);
-const char *ath9k_hw_probe(u16 vendorid, u16 devid);
-bool ath9k_hw_disable(struct ath_hal *ah);
-void ath9k_hw_rfdetach(struct ath_hal *ah);
-void ath9k_hw_get_channel_centers(struct ath_hal *ah,
- struct ath9k_channel *chan,
- struct chan_centers *centers);
-bool ath9k_get_channel_edges(struct ath_hal *ah,
- u16 flags, u16 *low,
- u16 *high);
-void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio,
- u32 ah_signal_type);
-void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 value);
-u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio);
-void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio);
+
#endif
diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c
index 4dd1c1bda0fb..3ab0b43aaf93 100644
--- a/drivers/net/wireless/ath9k/beacon.c
+++ b/drivers/net/wireless/ath9k/beacon.c
@@ -14,13 +14,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
- /* Implementation of beacon processing. */
-
#include "core.h"
/*
- * Configure parameters for the beacon queue
- *
* This function will modify certain transmit queue properties depending on
* the operating mode of the station (AP or AdHoc). Parameters are AIFS
* settings and channel width min/max
@@ -30,33 +26,38 @@ static int ath_beaconq_config(struct ath_softc *sc)
struct ath_hal *ah = sc->sc_ah;
struct ath9k_tx_queue_info qi;
- ath9k_hw_get_txq_props(ah, sc->sc_bhalq, &qi);
- if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
+ ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) {
/* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0;
} else {
/* Adhoc mode; important thing is to use 2x cwmin. */
- qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
- qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
- qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
+ qi.tqi_aifs = sc->beacon.beacon_qi.tqi_aifs;
+ qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin;
+ qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax;
}
- if (!ath9k_hw_set_txq_props(ah, sc->sc_bhalq, &qi)) {
+ if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to update h/w beacon queue parameters\n",
- __func__);
+ "unable to update h/w beacon queue parameters\n");
return 0;
} else {
- ath9k_hw_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
+ ath9k_hw_resettxqueue(ah, sc->beacon.beaconq); /* push to h/w */
return 1;
}
}
+static void ath_bstuck_process(struct ath_softc *sc)
+{
+ DPRINTF(sc, ATH_DBG_BEACON,
+ "stuck beacon; resetting (bmiss count %u)\n",
+ sc->beacon.bmisscnt);
+ ath_reset(sc, false);
+}
+
/*
- * Setup the beacon frame for transmit.
- *
* Associates the beacon frame buffer with a transmit descriptor. Will set
* up all required antenna switch parameters, rate codes, and channel flags.
* Beacons are always sent out at the lowest rate, and are not retried.
@@ -68,21 +69,20 @@ static void ath_beacon_setup(struct ath_softc *sc,
struct ath_hal *ah = sc->sc_ah;
struct ath_desc *ds;
struct ath9k_11n_rate_series series[4];
- const struct ath9k_rate_table *rt;
+ struct ath_rate_table *rt;
int flags, antenna;
u8 rix, rate;
int ctsrate = 0;
int ctsduration = 0;
- DPRINTF(sc, ATH_DBG_BEACON, "%s: m %p len %u\n",
- __func__, skb, skb->len);
+ DPRINTF(sc, ATH_DBG_BEACON, "m %p len %u\n", skb, skb->len);
/* setup descriptors */
ds = bf->bf_desc;
flags = ATH9K_TXDESC_NOACK;
- if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC &&
(ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
ds->ds_link = bf->bf_daddr; /* self-linked */
flags |= ATH9K_TXDESC_VEOL;
@@ -96,7 +96,7 @@ static void ath_beacon_setup(struct ath_softc *sc,
* SWBA's
* XXX assumes two antenna
*/
- antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
+ antenna = ((sc->beacon.ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
}
ds->ds_data = bf->bf_buf_addr;
@@ -106,15 +106,15 @@ static void ath_beacon_setup(struct ath_softc *sc,
* XXX everything at min xmit rate
*/
rix = 0;
- rt = sc->sc_currates;
- rate = rt->info[rix].rateCode;
+ rt = sc->cur_rate_table;
+ rate = rt->info[rix].ratecode;
if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
- rate |= rt->info[rix].shortPreamble;
+ rate |= rt->info[rix].short_preamble;
ath9k_hw_set11n_txdesc(ah, ds,
skb->len + FCS_LEN, /* frame length */
ATH9K_PKT_TYPE_BEACON, /* Atheros packet type */
- avp->av_btxctl.txpower, /* txpower XXX */
+ MAX_RATE_POWER, /* FIXME */
ATH9K_TXKEYIX_INVALID, /* no encryption */
ATH9K_KEY_TYPE_CLEAR, /* no encryption */
flags /* no ack,
@@ -138,31 +138,26 @@ static void ath_beacon_setup(struct ath_softc *sc,
ctsrate, ctsduration, series, 4, 0);
}
-/*
- * Generate beacon frame and queue cab data for a vap.
- *
- * Updates the contents of the beacon frame. It is assumed that the buffer for
- * the beacon frame has been allocated in the ATH object, and simply needs to
- * be filled for this cycle. Also, any CAB (crap after beacon?) traffic will
- * be added to the beacon frame at this point.
-*/
+/* Generate beacon frame and queue cab data for a vap */
static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
{
struct ath_buf *bf;
struct ath_vap *avp;
struct sk_buff *skb;
struct ath_txq *cabq;
+ struct ieee80211_vif *vif;
struct ieee80211_tx_info *info;
int cabq_depth;
- avp = sc->sc_vaps[if_id];
- ASSERT(avp);
+ vif = sc->sc_vaps[if_id];
+ ASSERT(vif);
- cabq = sc->sc_cabq;
+ avp = (void *)vif->drv_priv;
+ cabq = sc->beacon.cabq;
if (avp->av_bcbuf == NULL) {
- DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n",
- __func__, avp, avp->av_bcbuf);
+ DPRINTF(sc, ATH_DBG_BEACON, "avp=%p av_bcbuf=%p\n",
+ avp, avp->av_bcbuf);
return NULL;
}
@@ -172,9 +167,10 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
pci_unmap_single(sc->pdev, bf->bf_dmacontext,
skb->len,
PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
}
- skb = ieee80211_beacon_get(sc->hw, avp->av_if_data);
+ skb = ieee80211_beacon_get(sc->hw, vif);
bf->bf_mpdu = skb;
if (skb == NULL)
return NULL;
@@ -186,17 +182,24 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
* TX frames)
*/
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- sc->seq_no += 0x10;
+ sc->tx.seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
+ hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
bf->bf_buf_addr = bf->bf_dmacontext =
pci_map_single(sc->pdev, skb->data,
skb->len,
PCI_DMA_TODEVICE);
+ if (unlikely(pci_dma_mapping_error(sc->pdev, bf->bf_buf_addr))) {
+ dev_kfree_skb_any(skb);
+ bf->bf_mpdu = NULL;
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "pci_dma_mapping_error() on beaconing\n");
+ return NULL;
+ }
- skb = ieee80211_get_buffered_bc(sc->hw, avp->av_if_data);
+ skb = ieee80211_get_buffered_bc(sc->hw, vif);
/*
* if the CABQ traffic from previous DTIM is pending and the current
@@ -219,7 +222,7 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
if (sc->sc_nvaps > 1) {
ath_tx_draintxq(sc, cabq, false);
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: flush previous cabq traffic\n", __func__);
+ "flush previous cabq traffic\n");
}
}
@@ -232,7 +235,7 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
*/
while (skb) {
ath_tx_cabq(sc, skb);
- skb = ieee80211_get_buffered_bc(sc->hw, avp->av_if_data);
+ skb = ieee80211_get_buffered_bc(sc->hw, vif);
}
return bf;
@@ -244,17 +247,20 @@ static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
*/
static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id)
{
+ struct ieee80211_vif *vif;
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
struct ath_vap *avp;
struct sk_buff *skb;
- avp = sc->sc_vaps[if_id];
- ASSERT(avp);
+ vif = sc->sc_vaps[if_id];
+ ASSERT(vif);
+
+ avp = (void *)vif->drv_priv;
if (avp->av_bcbuf == NULL) {
- DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n",
- __func__, avp, avp != NULL ? avp->av_bcbuf : NULL);
+ DPRINTF(sc, ATH_DBG_BEACON, "avp=%p av_bcbuf=%p\n",
+ avp, avp != NULL ? avp->av_bcbuf : NULL);
return;
}
bf = avp->av_bcbuf;
@@ -264,20 +270,12 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id)
ath_beacon_setup(sc, avp, bf);
/* NB: caller is known to have already stopped tx dma */
- ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
- ath9k_hw_txstart(ah, sc->sc_bhalq);
- DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__,
- sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc);
+ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
+ ath9k_hw_txstart(ah, sc->beacon.beaconq);
+ DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n",
+ sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc);
}
-/*
- * Setup a h/w transmit queue for beacons.
- *
- * This function allocates an information structure (struct ath9k_txq_info)
- * on the stack, sets some specific parameters (zero out channel width
- * min/max, and enable aifs). The info structure does not need to be
- * persistant.
-*/
int ath_beaconq_setup(struct ath_hal *ah)
{
struct ath9k_tx_queue_info qi;
@@ -290,35 +288,29 @@ int ath_beaconq_setup(struct ath_hal *ah)
return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
}
-
-/*
- * Allocate and setup an initial beacon frame.
- *
- * Allocate a beacon state variable for a specific VAP instance created on
- * the ATH interface. This routine also calculates the beacon "slot" for
- * staggared beacons in the mBSSID case.
-*/
int ath_beacon_alloc(struct ath_softc *sc, int if_id)
{
+ struct ieee80211_vif *vif;
struct ath_vap *avp;
struct ieee80211_hdr *hdr;
struct ath_buf *bf;
struct sk_buff *skb;
__le64 tstamp;
- avp = sc->sc_vaps[if_id];
- ASSERT(avp);
+ vif = sc->sc_vaps[if_id];
+ ASSERT(vif);
+
+ avp = (void *)vif->drv_priv;
/* Allocate a beacon descriptor if we haven't done so. */
if (!avp->av_bcbuf) {
/* Allocate beacon state for hostap/ibss. We know
* a buffer is available. */
-
- avp->av_bcbuf = list_first_entry(&sc->sc_bbuf,
+ avp->av_bcbuf = list_first_entry(&sc->beacon.bbuf,
struct ath_buf, list);
list_del(&avp->av_bcbuf->list);
- if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP ||
!(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
int slot;
/*
@@ -327,13 +319,13 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id)
*/
avp->av_bslot = 0;
for (slot = 0; slot < ATH_BCBUF; slot++)
- if (sc->sc_bslot[slot] == ATH_IF_ID_ANY) {
+ if (sc->beacon.bslot[slot] == ATH_IF_ID_ANY) {
/*
* XXX hack, space out slots to better
* deal with misses
*/
if (slot+1 < ATH_BCBUF &&
- sc->sc_bslot[slot+1] ==
+ sc->beacon.bslot[slot+1] ==
ATH_IF_ID_ANY) {
avp->av_bslot = slot+1;
break;
@@ -341,8 +333,8 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id)
avp->av_bslot = slot;
/* NB: keep looking for a double slot */
}
- BUG_ON(sc->sc_bslot[avp->av_bslot] != ATH_IF_ID_ANY);
- sc->sc_bslot[avp->av_bslot] = if_id;
+ BUG_ON(sc->beacon.bslot[avp->av_bslot] != ATH_IF_ID_ANY);
+ sc->beacon.bslot[avp->av_bslot] = if_id;
sc->sc_nbcnvaps++;
}
}
@@ -363,15 +355,14 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id)
* FIXME: Fill avp->av_btxctl.txpower and
* avp->av_btxctl.shortPreamble
*/
- skb = ieee80211_beacon_get(sc->hw, avp->av_if_data);
+ skb = ieee80211_beacon_get(sc->hw, vif);
if (skb == NULL) {
- DPRINTF(sc, ATH_DBG_BEACON, "%s: cannot get skb\n",
- __func__);
+ DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n");
return -ENOMEM;
}
tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
- sc->bc_tstamp = le64_to_cpu(tstamp);
+ sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
/*
* Calculate a TSF adjustment factor required for
@@ -402,36 +393,36 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id)
val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: %s beacons, bslot %d intval %u tsfadjust %llu\n",
- __func__, "stagger",
+ "stagger beacons, bslot %d intval %u tsfadjust %llu\n",
avp->av_bslot, intval, (unsigned long long)tsfadjust);
hdr = (struct ieee80211_hdr *)skb->data;
memcpy(&hdr[1], &val, sizeof(val));
}
+ bf->bf_mpdu = skb;
bf->bf_buf_addr = bf->bf_dmacontext =
pci_map_single(sc->pdev, skb->data,
skb->len,
PCI_DMA_TODEVICE);
- bf->bf_mpdu = skb;
+ if (unlikely(pci_dma_mapping_error(sc->pdev, bf->bf_buf_addr))) {
+ dev_kfree_skb_any(skb);
+ bf->bf_mpdu = NULL;
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "pci_dma_mapping_error() on beacon alloc\n");
+ return -ENOMEM;
+ }
return 0;
}
-/*
- * Reclaim beacon resources and return buffer to the pool.
- *
- * Checks the VAP to put the beacon frame buffer back to the ATH object
- * queue, and de-allocates any skbs that were sent as CAB traffic.
-*/
void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp)
{
if (avp->av_bcbuf != NULL) {
struct ath_buf *bf;
if (avp->av_bslot != -1) {
- sc->sc_bslot[avp->av_bslot] = ATH_IF_ID_ANY;
+ sc->beacon.bslot[avp->av_bslot] = ATH_IF_ID_ANY;
sc->sc_nbcnvaps--;
}
@@ -444,19 +435,12 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp)
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
}
- list_add_tail(&bf->list, &sc->sc_bbuf);
+ list_add_tail(&bf->list, &sc->beacon.bbuf);
avp->av_bcbuf = NULL;
}
}
-/*
- * Tasklet for Sending Beacons
- *
- * Transmit one or more beacon frames at SWBA. Dynamic updates to the frame
- * contents are done as needed and the slot time is also adjusted based on
- * current state.
-*/
void ath9k_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
@@ -473,9 +457,7 @@ void ath9k_beacon_tasklet(unsigned long data)
if (sc->sc_flags & SC_OP_NO_RESET) {
show_cycles = ath9k_hw_GetMibCycleCountsPct(ah,
- &rx_clear,
- &rx_frame,
- &tx_frame);
+ &rx_clear, &rx_frame, &tx_frame);
}
/*
@@ -487,67 +469,65 @@ void ath9k_beacon_tasklet(unsigned long data)
*
* FIXME: Clean up this mess !!
*/
- if (ath9k_hw_numtxpending(ah, sc->sc_bhalq) != 0) {
- sc->sc_bmisscount++;
+ if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) {
+ sc->beacon.bmisscnt++;
/* XXX: doth needs the chanchange IE countdown decremented.
* We should consider adding a mac80211 call to indicate
* a beacon miss so appropriate action could be taken
* (in that layer).
*/
- if (sc->sc_bmisscount < BSTUCK_THRESH) {
+ if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
if (sc->sc_flags & SC_OP_NO_RESET) {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: missed %u consecutive beacons\n",
- __func__, sc->sc_bmisscount);
+ "missed %u consecutive beacons\n",
+ sc->beacon.bmisscnt);
if (show_cycles) {
/*
* Display cycle counter stats from HW
* to aide in debug of stickiness.
*/
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: busy times: rx_clear=%d, "
+ "busy times: rx_clear=%d, "
"rx_frame=%d, tx_frame=%d\n",
- __func__, rx_clear, rx_frame,
+ rx_clear, rx_frame,
tx_frame);
} else {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: unable to obtain "
- "busy times\n", __func__);
+ "unable to obtain "
+ "busy times\n");
}
} else {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: missed %u consecutive beacons\n",
- __func__, sc->sc_bmisscount);
+ "missed %u consecutive beacons\n",
+ sc->beacon.bmisscnt);
}
- } else if (sc->sc_bmisscount >= BSTUCK_THRESH) {
+ } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
if (sc->sc_flags & SC_OP_NO_RESET) {
- if (sc->sc_bmisscount == BSTUCK_THRESH) {
+ if (sc->beacon.bmisscnt == BSTUCK_THRESH) {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: beacon is officially "
- "stuck\n", __func__);
- ath9k_hw_dmaRegDump(ah);
+ "beacon is officially "
+ "stuck\n");
}
} else {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: beacon is officially stuck\n",
- __func__);
+ "beacon is officially stuck\n");
ath_bstuck_process(sc);
}
}
return;
}
- if (sc->sc_bmisscount != 0) {
+ if (sc->beacon.bmisscnt != 0) {
if (sc->sc_flags & SC_OP_NO_RESET) {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: resume beacon xmit after %u misses\n",
- __func__, sc->sc_bmisscount);
+ "resume beacon xmit after %u misses\n",
+ sc->beacon.bmisscnt);
} else {
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: resume beacon xmit after %u misses\n",
- __func__, sc->sc_bmisscount);
+ "resume beacon xmit after %u misses\n",
+ sc->beacon.bmisscnt);
}
- sc->sc_bmisscount = 0;
+ sc->beacon.bmisscnt = 0;
}
/*
@@ -562,11 +542,11 @@ void ath9k_beacon_tasklet(unsigned long data)
tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU(tsf>>32, tsf);
slot = ((tsftu % intval) * ATH_BCBUF) / intval;
- if_id = sc->sc_bslot[(slot + 1) % ATH_BCBUF];
+ if_id = sc->beacon.bslot[(slot + 1) % ATH_BCBUF];
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: slot %d [tsf %llu tsftu %u intval %u] if_id %d\n",
- __func__, slot, (unsigned long long)tsf, tsftu,
+ "slot %d [tsf %llu tsftu %u intval %u] if_id %d\n",
+ slot, (unsigned long long)tsf, tsftu,
intval, if_id);
bfaddr = 0;
@@ -594,48 +574,34 @@ void ath9k_beacon_tasklet(unsigned long data)
* set to ATH_BCBUF so this check is a noop.
*/
/* XXX locking */
- if (sc->sc_updateslot == UPDATE) {
- sc->sc_updateslot = COMMIT; /* commit next beacon */
- sc->sc_slotupdate = slot;
- } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
- ath_setslottime(sc); /* commit change to hardware */
-
+ if (sc->beacon.updateslot == UPDATE) {
+ sc->beacon.updateslot = COMMIT; /* commit next beacon */
+ sc->beacon.slotupdate = slot;
+ } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) {
+ ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime);
+ sc->beacon.updateslot = OK;
+ }
if (bfaddr != 0) {
/*
* Stop any current dma and put the new frame(s) on the queue.
* This should never fail since we check above that no frames
* are still pending on the queue.
*/
- if (!ath9k_hw_stoptxdma(ah, sc->sc_bhalq)) {
+ if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: beacon queue %u did not stop?\n",
- __func__, sc->sc_bhalq);
+ "beacon queue %u did not stop?\n", sc->beacon.beaconq);
/* NB: the HAL still stops DMA, so proceed */
}
/* NB: cabq traffic should already be queued and primed */
- ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bfaddr);
- ath9k_hw_txstart(ah, sc->sc_bhalq);
+ ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
+ ath9k_hw_txstart(ah, sc->beacon.beaconq);
- sc->ast_be_xmit += bc; /* XXX per-vap? */
+ sc->beacon.ast_be_xmit += bc; /* XXX per-vap? */
}
}
/*
- * Tasklet for Beacon Stuck processing
- *
- * Processing for Beacon Stuck.
- * Basically resets the chip.
-*/
-void ath_bstuck_process(struct ath_softc *sc)
-{
- DPRINTF(sc, ATH_DBG_BEACON,
- "%s: stuck beacon; resetting (bmiss count %u)\n",
- __func__, sc->sc_bmisscount);
- ath_reset(sc, false);
-}
-
-/*
* Configure the beacon and sleep timers.
*
* When operating as an AP this resets the TSF and sets
@@ -652,15 +618,21 @@ void ath_bstuck_process(struct ath_softc *sc)
*/
void ath_beacon_config(struct ath_softc *sc, int if_id)
{
+ struct ieee80211_vif *vif;
struct ath_hal *ah = sc->sc_ah;
struct ath_beacon_config conf;
- enum ath9k_opmode av_opmode;
+ struct ath_vap *avp;
+ enum nl80211_iftype opmode;
u32 nexttbtt, intval;
- if (if_id != ATH_IF_ID_ANY)
- av_opmode = sc->sc_vaps[if_id]->av_opmode;
- else
- av_opmode = sc->sc_ah->ah_opmode;
+ if (if_id != ATH_IF_ID_ANY) {
+ vif = sc->sc_vaps[if_id];
+ ASSERT(vif);
+ avp = (void *)vif->drv_priv;
+ opmode = avp->av_opmode;
+ } else {
+ opmode = sc->sc_ah->ah_opmode;
+ }
memset(&conf, 0, sizeof(struct ath_beacon_config));
@@ -672,10 +644,10 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
/* extract tstamp from last beacon and convert to TU */
- nexttbtt = TSF_TO_TU(sc->bc_tstamp >> 32, sc->bc_tstamp);
+ nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
/* XXX conditionalize multi-bss support? */
- if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) {
/*
* For multi-bss ap support beacons are either staggered
* evenly over N slots or burst together. For the former
@@ -694,11 +666,11 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
else if (intval) /* NB: can be 0 for monitor mode */
nexttbtt = roundup(nexttbtt, intval);
- DPRINTF(sc, ATH_DBG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
- __func__, nexttbtt, intval, conf.beacon_interval);
+ DPRINTF(sc, ATH_DBG_BEACON, "nexttbtt %u intval %u (%u)\n",
+ nexttbtt, intval, conf.beacon_interval);
- /* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */
- if (sc->sc_ah->ah_opmode == ATH9K_M_STA) {
+ /* Check for NL80211_IFTYPE_AP and sc_nostabeacons for WDS client */
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) {
struct ath9k_beacon_state bs;
u64 tsf;
u32 tsftu;
@@ -782,7 +754,7 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
bs.bs_sleepduration = bs.bs_dtimperiod;
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: tsf %llu "
+ "tsf %llu "
"tsf:tu %u "
"intval %u "
"nexttbtt %u "
@@ -794,7 +766,6 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
"maxdur %u "
"next %u "
"timoffset %u\n",
- __func__,
(unsigned long long)tsf, tsftu,
bs.bs_intval,
bs.bs_nexttbtt,
@@ -818,7 +789,7 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
ath9k_hw_set_interrupts(ah, 0);
if (nexttbtt == intval)
intval |= ATH9K_BEACON_RESET_TSF;
- if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC) {
/*
* Pull nexttbtt forward to reflect the current
* TSF
@@ -834,8 +805,8 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
}
#undef FUDGE
DPRINTF(sc, ATH_DBG_BEACON,
- "%s: IBSS nexttbtt %u intval %u (%u)\n",
- __func__, nexttbtt,
+ "IBSS nexttbtt %u intval %u (%u)\n",
+ nexttbtt,
intval & ~ATH9K_BEACON_RESET_TSF,
conf.beacon_interval);
@@ -850,7 +821,7 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
if (!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL))
sc->sc_imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
- } else if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP) {
+ } else if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) {
/*
* In AP mode we enable the beacon timers and
* SWBA interrupts to prepare beacon frames.
@@ -860,20 +831,18 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
ath_beaconq_config(sc);
}
ath9k_hw_beaconinit(ah, nexttbtt, intval);
- sc->sc_bmisscount = 0;
+ sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(ah, sc->sc_imask);
/*
* When using a self-linked beacon descriptor in
* ibss mode load it once here.
*/
- if (sc->sc_ah->ah_opmode == ATH9K_M_IBSS &&
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC &&
(ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL))
ath_beacon_start_adhoc(sc, 0);
}
}
-/* Function to collect beacon rssi data and resync beacon if necessary */
-
void ath_beacon_sync(struct ath_softc *sc, int if_id)
{
/*
diff --git a/drivers/net/wireless/ath9k/calib.c b/drivers/net/wireless/ath9k/calib.c
new file mode 100644
index 000000000000..3c7454fc51bd
--- /dev/null
+++ b/drivers/net/wireless/ath9k/calib.c
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hw.h"
+#include "reg.h"
+#include "phy.h"
+
+static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 };
+
+/* We can tune this as we go by monitoring really low values */
+#define ATH9K_NF_TOO_LOW -60
+
+/* AR5416 may return very high value (like -31 dBm), in those cases the nf
+ * is incorrect and we should use the static NF value. Later we can try to
+ * find out why they are reporting these values */
+
+static bool ath9k_hw_nf_in_range(struct ath_hal *ah, s16 nf)
+{
+ if (nf > ATH9K_NF_TOO_LOW) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "noise floor value detected (%d) is "
+ "lower than what we think is a "
+ "reasonable value (%d)\n",
+ nf, ATH9K_NF_TOO_LOW);
+ return false;
+ }
+ return true;
+}
+
+static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
+{
+ int16_t nfval;
+ int16_t sort[ATH9K_NF_CAL_HIST_MAX];
+ int i, j;
+
+ for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
+ sort[i] = nfCalBuffer[i];
+
+ for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
+ for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
+ if (sort[j] > sort[j - 1]) {
+ nfval = sort[j];
+ sort[j] = sort[j - 1];
+ sort[j - 1] = nfval;
+ }
+ }
+ }
+ nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
+
+ return nfval;
+}
+
+static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
+ int16_t *nfarray)
+{
+ int i;
+
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
+
+ if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
+ h[i].currIndex = 0;
+
+ if (h[i].invalidNFcount > 0) {
+ if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE ||
+ nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
+ h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
+ } else {
+ h[i].invalidNFcount--;
+ h[i].privNF = nfarray[i];
+ }
+ } else {
+ h[i].privNF =
+ ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
+ }
+ }
+ return;
+}
+
+static void ath9k_hw_do_getnf(struct ath_hal *ah,
+ int16_t nfarray[NUM_NF_READINGS])
+{
+ int16_t nf;
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
+ else
+ nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
+
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 0] is %d\n", nf);
+ nfarray[0] = nf;
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
+ AR9280_PHY_CH1_MINCCA_PWR);
+ else
+ nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
+ AR_PHY_CH1_MINCCA_PWR);
+
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 1] is %d\n", nf);
+ nfarray[1] = nf;
+
+ if (!AR_SREV_9280(ah)) {
+ nf = MS(REG_READ(ah, AR_PHY_CH2_CCA),
+ AR_PHY_CH2_MINCCA_PWR);
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 2] is %d\n", nf);
+ nfarray[2] = nf;
+ }
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
+ AR9280_PHY_EXT_MINCCA_PWR);
+ else
+ nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
+ AR_PHY_EXT_MINCCA_PWR);
+
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 0] is %d\n", nf);
+ nfarray[3] = nf;
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
+ AR9280_PHY_CH1_EXT_MINCCA_PWR);
+ else
+ nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
+ AR_PHY_CH1_EXT_MINCCA_PWR);
+
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 1] is %d\n", nf);
+ nfarray[4] = nf;
+
+ if (!AR_SREV_9280(ah)) {
+ nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA),
+ AR_PHY_CH2_EXT_MINCCA_PWR);
+ if (nf & 0x100)
+ nf = 0 - ((nf ^ 0x1ff) + 1);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 2] is %d\n", nf);
+ nfarray[5] = nf;
+ }
+}
+
+static bool getNoiseFloorThresh(struct ath_hal *ah,
+ const struct ath9k_channel *chan,
+ int16_t *nft)
+{
+ switch (chan->chanmode) {
+ case CHANNEL_A:
+ case CHANNEL_A_HT20:
+ case CHANNEL_A_HT40PLUS:
+ case CHANNEL_A_HT40MINUS:
+ *nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_5);
+ break;
+ case CHANNEL_B:
+ case CHANNEL_G:
+ case CHANNEL_G_HT20:
+ case CHANNEL_G_HT40PLUS:
+ case CHANNEL_G_HT40MINUS:
+ *nft = (int8_t)ath9k_hw_get_eeprom(ah, EEP_NFTHRESH_2);
+ break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "invalid channel flags 0x%x\n", chan->channelFlags);
+ return false;
+ }
+
+ return true;
+}
+
+static void ath9k_hw_setup_calibration(struct ath_hal *ah,
+ struct hal_cal_list *currCal)
+{
+ REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0),
+ AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
+ currCal->calData->calCountMax);
+
+ switch (currCal->calData->calType) {
+ case IQ_MISMATCH_CAL:
+ REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "starting IQ Mismatch Calibration\n");
+ break;
+ case ADC_GAIN_CAL:
+ REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "starting ADC Gain Calibration\n");
+ break;
+ case ADC_DC_CAL:
+ REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "starting ADC DC Calibration\n");
+ break;
+ case ADC_DC_INIT_CAL:
+ REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "starting Init ADC DC Calibration\n");
+ break;
+ }
+
+ REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
+ AR_PHY_TIMING_CTRL4_DO_CAL);
+}
+
+static void ath9k_hw_reset_calibration(struct ath_hal *ah,
+ struct hal_cal_list *currCal)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ ath9k_hw_setup_calibration(ah, currCal);
+
+ currCal->calState = CAL_RUNNING;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ ahp->ah_Meas0.sign[i] = 0;
+ ahp->ah_Meas1.sign[i] = 0;
+ ahp->ah_Meas2.sign[i] = 0;
+ ahp->ah_Meas3.sign[i] = 0;
+ }
+
+ ahp->ah_CalSamples = 0;
+}
+
+static void ath9k_hw_per_calibration(struct ath_hal *ah,
+ struct ath9k_channel *ichan,
+ u8 rxchainmask,
+ struct hal_cal_list *currCal,
+ bool *isCalDone)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ *isCalDone = false;
+
+ if (currCal->calState == CAL_RUNNING) {
+ if (!(REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
+ AR_PHY_TIMING_CTRL4_DO_CAL)) {
+
+ currCal->calData->calCollect(ah);
+ ahp->ah_CalSamples++;
+
+ if (ahp->ah_CalSamples >= currCal->calData->calNumSamples) {
+ int i, numChains = 0;
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ if (rxchainmask & (1 << i))
+ numChains++;
+ }
+
+ currCal->calData->calPostProc(ah, numChains);
+ ichan->CalValid |= currCal->calData->calType;
+ currCal->calState = CAL_DONE;
+ *isCalDone = true;
+ } else {
+ ath9k_hw_setup_calibration(ah, currCal);
+ }
+ }
+ } else if (!(ichan->CalValid & currCal->calData->calType)) {
+ ath9k_hw_reset_calibration(ah, currCal);
+ }
+}
+
+static bool ath9k_hw_iscal_supported(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ enum hal_cal_types calType)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ bool retval = false;
+
+ switch (calType & ahp->ah_suppCals) {
+ case IQ_MISMATCH_CAL:
+ if (!IS_CHAN_B(chan))
+ retval = true;
+ break;
+ case ADC_GAIN_CAL:
+ case ADC_DC_CAL:
+ if (!IS_CHAN_B(chan)
+ && !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan)))
+ retval = true;
+ break;
+ }
+
+ return retval;
+}
+
+static void ath9k_hw_iqcal_collect(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ ahp->ah_totalPowerMeasI[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
+ ahp->ah_totalPowerMeasQ[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
+ ahp->ah_totalIqCorrMeas[i] +=
+ (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
+ ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i],
+ ahp->ah_totalPowerMeasQ[i],
+ ahp->ah_totalIqCorrMeas[i]);
+ }
+}
+
+static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ ahp->ah_totalAdcIOddPhase[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
+ ahp->ah_totalAdcIEvenPhase[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
+ ahp->ah_totalAdcQOddPhase[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
+ ahp->ah_totalAdcQEvenPhase[i] +=
+ REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
+ "oddq=0x%08x; evenq=0x%08x;\n",
+ ahp->ah_CalSamples, i,
+ ahp->ah_totalAdcIOddPhase[i],
+ ahp->ah_totalAdcIEvenPhase[i],
+ ahp->ah_totalAdcQOddPhase[i],
+ ahp->ah_totalAdcQEvenPhase[i]);
+ }
+}
+
+static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ int i;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ ahp->ah_totalAdcDcOffsetIOddPhase[i] +=
+ (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
+ ahp->ah_totalAdcDcOffsetIEvenPhase[i] +=
+ (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
+ ahp->ah_totalAdcDcOffsetQOddPhase[i] +=
+ (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
+ ahp->ah_totalAdcDcOffsetQEvenPhase[i] +=
+ (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
+ "oddq=0x%08x; evenq=0x%08x;\n",
+ ahp->ah_CalSamples, i,
+ ahp->ah_totalAdcDcOffsetIOddPhase[i],
+ ahp->ah_totalAdcDcOffsetIEvenPhase[i],
+ ahp->ah_totalAdcDcOffsetQOddPhase[i],
+ ahp->ah_totalAdcDcOffsetQEvenPhase[i]);
+ }
+}
+
+static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 powerMeasQ, powerMeasI, iqCorrMeas;
+ u32 qCoffDenom, iCoffDenom;
+ int32_t qCoff, iCoff;
+ int iqCorrNeg, i;
+
+ for (i = 0; i < numChains; i++) {
+ powerMeasI = ahp->ah_totalPowerMeasI[i];
+ powerMeasQ = ahp->ah_totalPowerMeasQ[i];
+ iqCorrMeas = ahp->ah_totalIqCorrMeas[i];
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Starting IQ Cal and Correction for Chain %d\n",
+ i);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Orignal: Chn %diq_corr_meas = 0x%08x\n",
+ i, ahp->ah_totalIqCorrMeas[i]);
+
+ iqCorrNeg = 0;
+
+ if (iqCorrMeas > 0x80000000) {
+ iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
+ iqCorrNeg = 1;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
+ iqCorrNeg);
+
+ iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
+ qCoffDenom = powerMeasQ / 64;
+
+ if (powerMeasQ != 0) {
+ iCoff = iqCorrMeas / iCoffDenom;
+ qCoff = powerMeasI / qCoffDenom - 64;
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d iCoff = 0x%08x\n", i, iCoff);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d qCoff = 0x%08x\n", i, qCoff);
+
+ iCoff = iCoff & 0x3f;
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "New: Chn %d iCoff = 0x%08x\n", i, iCoff);
+ if (iqCorrNeg == 0x0)
+ iCoff = 0x40 - iCoff;
+
+ if (qCoff > 15)
+ qCoff = 15;
+ else if (qCoff <= -16)
+ qCoff = 16;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
+ i, iCoff, qCoff);
+
+ REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
+ iCoff);
+ REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
+ qCoff);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "IQ Cal and Correction done for Chain %d\n",
+ i);
+ }
+ }
+
+ REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
+ AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
+}
+
+static void ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, u8 numChains)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, qEvenMeasOffset;
+ u32 qGainMismatch, iGainMismatch, val, i;
+
+ for (i = 0; i < numChains; i++) {
+ iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i];
+ iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i];
+ qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i];
+ qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i];
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Starting ADC Gain Cal for Chain %d\n", i);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
+ iOddMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_i = 0x%08x\n", i,
+ iEvenMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
+ qOddMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_q = 0x%08x\n", i,
+ qEvenMeasOffset);
+
+ if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) {
+ iGainMismatch =
+ ((iEvenMeasOffset * 32) /
+ iOddMeasOffset) & 0x3f;
+ qGainMismatch =
+ ((qOddMeasOffset * 32) /
+ qEvenMeasOffset) & 0x3f;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d gain_mismatch_i = 0x%08x\n", i,
+ iGainMismatch);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d gain_mismatch_q = 0x%08x\n", i,
+ qGainMismatch);
+
+ val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
+ val &= 0xfffff000;
+ val |= (qGainMismatch) | (iGainMismatch << 6);
+ REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "ADC Gain Cal done for Chain %d\n", i);
+ }
+ }
+
+ REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
+ REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
+ AR_PHY_NEW_ADC_GAIN_CORR_ENABLE);
+}
+
+static void ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, u8 numChains)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 iOddMeasOffset, iEvenMeasOffset, val, i;
+ int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch;
+ const struct hal_percal_data *calData =
+ ahp->ah_cal_list_curr->calData;
+ u32 numSamples =
+ (1 << (calData->calCountMax + 5)) * calData->calNumSamples;
+
+ for (i = 0; i < numChains; i++) {
+ iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i];
+ iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i];
+ qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i];
+ qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i];
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Starting ADC DC Offset Cal for Chain %d\n", i);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_i = %d\n", i,
+ iOddMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_i = %d\n", i,
+ iEvenMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_q = %d\n", i,
+ qOddMeasOffset);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_q = %d\n", i,
+ qEvenMeasOffset);
+
+ iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) /
+ numSamples) & 0x1ff;
+ qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) /
+ numSamples) & 0x1ff;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
+ iDcMismatch);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
+ qDcMismatch);
+
+ val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
+ val &= 0xc0000fff;
+ val |= (qDcMismatch << 12) | (iDcMismatch << 21);
+ REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "ADC DC Offset Cal done for Chain %d\n", i);
+ }
+
+ REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
+ REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
+ AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE);
+}
+
+void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
+ bool *isCalDone)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_channel *ichan =
+ ath9k_regd_check_channel(ah, chan);
+ struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
+
+ *isCalDone = true;
+
+ if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
+ return;
+
+ if (currCal == NULL)
+ return;
+
+ if (ichan == NULL) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "invalid channel %u/0x%x; no mapping\n",
+ chan->channel, chan->channelFlags);
+ return;
+ }
+
+
+ if (currCal->calState != CAL_DONE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Calibration state incorrect, %d\n",
+ currCal->calState);
+ return;
+ }
+
+
+ if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType))
+ return;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "Resetting Cal %d state for channel %u/0x%x\n",
+ currCal->calData->calType, chan->channel,
+ chan->channelFlags);
+
+ ichan->CalValid &= ~currCal->calData->calType;
+ currCal->calState = CAL_WAITING;
+
+ *isCalDone = false;
+}
+
+void ath9k_hw_start_nfcal(struct ath_hal *ah)
+{
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_ENABLE_NF);
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+}
+
+void ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
+{
+ struct ath9k_nfcal_hist *h;
+ int i, j;
+ int32_t val;
+ const u32 ar5416_cca_regs[6] = {
+ AR_PHY_CCA,
+ AR_PHY_CH1_CCA,
+ AR_PHY_CH2_CCA,
+ AR_PHY_EXT_CCA,
+ AR_PHY_CH1_EXT_CCA,
+ AR_PHY_CH2_EXT_CCA
+ };
+ u8 chainmask;
+
+ if (AR_SREV_9280(ah))
+ chainmask = 0x1B;
+ else
+ chainmask = 0x3F;
+
+#ifdef ATH_NF_PER_CHAN
+ h = chan->nfCalHist;
+#else
+ h = ah->nfCalHist;
+#endif
+
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ if (chainmask & (1 << i)) {
+ val = REG_READ(ah, ar5416_cca_regs[i]);
+ val &= 0xFFFFFE00;
+ val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+ REG_WRITE(ah, ar5416_cca_regs[i], val);
+ }
+ }
+
+ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_ENABLE_NF);
+ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+
+ for (j = 0; j < 1000; j++) {
+ if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
+ AR_PHY_AGC_CONTROL_NF) == 0)
+ break;
+ udelay(10);
+ }
+
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ if (chainmask & (1 << i)) {
+ val = REG_READ(ah, ar5416_cca_regs[i]);
+ val &= 0xFFFFFE00;
+ val |= (((u32) (-50) << 1) & 0x1ff);
+ REG_WRITE(ah, ar5416_cca_regs[i], val);
+ }
+ }
+}
+
+int16_t ath9k_hw_getnf(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ int16_t nf, nfThresh;
+ int16_t nfarray[NUM_NF_READINGS] = { 0 };
+ struct ath9k_nfcal_hist *h;
+ u8 chainmask;
+
+ if (AR_SREV_9280(ah))
+ chainmask = 0x1B;
+ else
+ chainmask = 0x3F;
+
+ chan->channelFlags &= (~CHANNEL_CW_INT);
+ if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "NF did not complete in calibration window\n");
+ nf = 0;
+ chan->rawNoiseFloor = nf;
+ return chan->rawNoiseFloor;
+ } else {
+ ath9k_hw_do_getnf(ah, nfarray);
+ nf = nfarray[0];
+ if (getNoiseFloorThresh(ah, chan, &nfThresh)
+ && nf > nfThresh) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "noise floor failed detected; "
+ "detected %d, threshold %d\n",
+ nf, nfThresh);
+ chan->channelFlags |= CHANNEL_CW_INT;
+ }
+ }
+
+#ifdef ATH_NF_PER_CHAN
+ h = chan->nfCalHist;
+#else
+ h = ah->nfCalHist;
+#endif
+
+ ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
+ chan->rawNoiseFloor = h[0].privNF;
+
+ return chan->rawNoiseFloor;
+}
+
+void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah)
+{
+ int i, j;
+
+ for (i = 0; i < NUM_NF_READINGS; i++) {
+ ah->nfCalHist[i].currIndex = 0;
+ ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE;
+ ah->nfCalHist[i].invalidNFcount =
+ AR_PHY_CCA_FILTERWINDOW_LENGTH;
+ for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
+ ah->nfCalHist[i].nfCalBuffer[j] =
+ AR_PHY_CCA_MAX_GOOD_VALUE;
+ }
+ }
+ return;
+}
+
+s16 ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan)
+{
+ struct ath9k_channel *ichan;
+ s16 nf;
+
+ ichan = ath9k_regd_check_channel(ah, chan);
+ if (ichan == NULL) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "invalid channel %u/0x%x; no mapping\n",
+ chan->channel, chan->channelFlags);
+ return ATH_DEFAULT_NOISE_FLOOR;
+ }
+ if (ichan->rawNoiseFloor == 0) {
+ enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan);
+ nf = NOISE_FLOOR[mode];
+ } else
+ nf = ichan->rawNoiseFloor;
+
+ if (!ath9k_hw_nf_in_range(ah, nf))
+ nf = ATH_DEFAULT_NOISE_FLOOR;
+
+ return nf;
+}
+
+bool ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal,
+ bool *isCalDone)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
+ struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
+
+ *isCalDone = true;
+
+ if (ichan == NULL) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "invalid channel %u/0x%x; no mapping\n",
+ chan->channel, chan->channelFlags);
+ return false;
+ }
+
+ if (currCal &&
+ (currCal->calState == CAL_RUNNING ||
+ currCal->calState == CAL_WAITING)) {
+ ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal,
+ isCalDone);
+ if (*isCalDone) {
+ ahp->ah_cal_list_curr = currCal = currCal->calNext;
+
+ if (currCal->calState == CAL_WAITING) {
+ *isCalDone = false;
+ ath9k_hw_reset_calibration(ah, currCal);
+ }
+ }
+ }
+
+ if (longcal) {
+ ath9k_hw_getnf(ah, ichan);
+ ath9k_hw_loadnf(ah, ah->ah_curchan);
+ ath9k_hw_start_nfcal(ah);
+
+ if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
+ chan->channelFlags |= CHANNEL_CW_INT;
+ ichan->channelFlags &= ~CHANNEL_CW_INT;
+ }
+ }
+
+ return true;
+}
+
+static inline void ath9k_hw_9285_pa_cal(struct ath_hal *ah)
+{
+
+ u32 regVal;
+ int i, offset, offs_6_1, offs_0;
+ u32 ccomp_org, reg_field;
+ u32 regList[][2] = {
+ { 0x786c, 0 },
+ { 0x7854, 0 },
+ { 0x7820, 0 },
+ { 0x7824, 0 },
+ { 0x7868, 0 },
+ { 0x783c, 0 },
+ { 0x7838, 0 },
+ };
+
+ if (AR_SREV_9285_11(ah)) {
+ REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
+ udelay(10);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(regList); i++)
+ regList[i][1] = REG_READ(ah, regList[i][0]);
+
+ regVal = REG_READ(ah, 0x7834);
+ regVal &= (~(0x1));
+ REG_WRITE(ah, 0x7834, regVal);
+ regVal = REG_READ(ah, 0x9808);
+ regVal |= (0x1 << 27);
+ REG_WRITE(ah, 0x9808, regVal);
+
+ REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
+ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
+ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
+ REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 1);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
+ ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 7);
+
+ REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0);
+ udelay(30);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0);
+
+ for (i = 6; i > 0; i--) {
+ regVal = REG_READ(ah, 0x7834);
+ regVal |= (1 << (19 + i));
+ REG_WRITE(ah, 0x7834, regVal);
+ udelay(1);
+ regVal = REG_READ(ah, 0x7834);
+ regVal &= (~(0x1 << (19 + i)));
+ reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9);
+ regVal |= (reg_field << (19 + i));
+ REG_WRITE(ah, 0x7834, regVal);
+ }
+
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1);
+ udelay(1);
+ reg_field = MS(REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field);
+ offs_6_1 = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS);
+ offs_0 = MS(REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP);
+
+ offset = (offs_6_1<<1) | offs_0;
+ offset = offset - 0;
+ offs_6_1 = offset>>1;
+ offs_0 = offset & 1;
+
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1);
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0);
+
+ regVal = REG_READ(ah, 0x7834);
+ regVal |= 0x1;
+ REG_WRITE(ah, 0x7834, regVal);
+ regVal = REG_READ(ah, 0x9808);
+ regVal &= (~(0x1 << 27));
+ REG_WRITE(ah, 0x9808, regVal);
+
+ for (i = 0; i < ARRAY_SIZE(regList); i++)
+ REG_WRITE(ah, regList[i][0], regList[i][1]);
+
+ REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org);
+
+ if (AR_SREV_9285_11(ah))
+ REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT);
+
+}
+
+bool ath9k_hw_init_cal(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_channel *ichan = ath9k_regd_check_channel(ah, chan);
+
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+ REG_READ(ah, AR_PHY_AGC_CONTROL) |
+ AR_PHY_AGC_CONTROL_CAL);
+
+ if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "offset calibration failed to complete in 1ms; "
+ "noisy environment?\n");
+ return false;
+ }
+
+ if (AR_SREV_9285(ah) && AR_SREV_9285_11_OR_LATER(ah))
+ ath9k_hw_9285_pa_cal(ah);
+
+ REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+ REG_READ(ah, AR_PHY_AGC_CONTROL) |
+ AR_PHY_AGC_CONTROL_NF);
+
+ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL;
+
+ if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) {
+ if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) {
+ INIT_CAL(&ahp->ah_adcGainCalData);
+ INSERT_CAL(ahp, &ahp->ah_adcGainCalData);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "enabling ADC Gain Calibration.\n");
+ }
+ if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) {
+ INIT_CAL(&ahp->ah_adcDcCalData);
+ INSERT_CAL(ahp, &ahp->ah_adcDcCalData);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "enabling ADC DC Calibration.\n");
+ }
+ if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) {
+ INIT_CAL(&ahp->ah_iqCalData);
+ INSERT_CAL(ahp, &ahp->ah_iqCalData);
+ DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+ "enabling IQ Calibration.\n");
+ }
+
+ ahp->ah_cal_list_curr = ahp->ah_cal_list;
+
+ if (ahp->ah_cal_list_curr)
+ ath9k_hw_reset_calibration(ah, ahp->ah_cal_list_curr);
+ }
+
+ ichan->CalValid = 0;
+
+ return true;
+}
+
+const struct hal_percal_data iq_cal_multi_sample = {
+ IQ_MISMATCH_CAL,
+ MAX_CAL_SAMPLES,
+ PER_MIN_LOG_COUNT,
+ ath9k_hw_iqcal_collect,
+ ath9k_hw_iqcalibrate
+};
+const struct hal_percal_data iq_cal_single_sample = {
+ IQ_MISMATCH_CAL,
+ MIN_CAL_SAMPLES,
+ PER_MAX_LOG_COUNT,
+ ath9k_hw_iqcal_collect,
+ ath9k_hw_iqcalibrate
+};
+const struct hal_percal_data adc_gain_cal_multi_sample = {
+ ADC_GAIN_CAL,
+ MAX_CAL_SAMPLES,
+ PER_MIN_LOG_COUNT,
+ ath9k_hw_adc_gaincal_collect,
+ ath9k_hw_adc_gaincal_calibrate
+};
+const struct hal_percal_data adc_gain_cal_single_sample = {
+ ADC_GAIN_CAL,
+ MIN_CAL_SAMPLES,
+ PER_MAX_LOG_COUNT,
+ ath9k_hw_adc_gaincal_collect,
+ ath9k_hw_adc_gaincal_calibrate
+};
+const struct hal_percal_data adc_dc_cal_multi_sample = {
+ ADC_DC_CAL,
+ MAX_CAL_SAMPLES,
+ PER_MIN_LOG_COUNT,
+ ath9k_hw_adc_dccal_collect,
+ ath9k_hw_adc_dccal_calibrate
+};
+const struct hal_percal_data adc_dc_cal_single_sample = {
+ ADC_DC_CAL,
+ MIN_CAL_SAMPLES,
+ PER_MAX_LOG_COUNT,
+ ath9k_hw_adc_dccal_collect,
+ ath9k_hw_adc_dccal_calibrate
+};
+const struct hal_percal_data adc_init_dc_cal = {
+ ADC_DC_INIT_CAL,
+ MIN_CAL_SAMPLES,
+ INIT_LOG_COUNT,
+ ath9k_hw_adc_dccal_collect,
+ ath9k_hw_adc_dccal_calibrate
+};
diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c
deleted file mode 100644
index c5033f6f42ac..000000000000
--- a/drivers/net/wireless/ath9k/core.c
+++ /dev/null
@@ -1,1886 +0,0 @@
-/*
- * Copyright (c) 2008, Atheros Communications Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
- /* Implementation of the main "ATH" layer. */
-
-#include "core.h"
-#include "regd.h"
-
-static int ath_outdoor; /* enable outdoor use */
-
-static u32 ath_chainmask_sel_up_rssi_thres =
- ATH_CHAINMASK_SEL_UP_RSSI_THRES;
-static u32 ath_chainmask_sel_down_rssi_thres =
- ATH_CHAINMASK_SEL_DOWN_RSSI_THRES;
-static u32 ath_chainmask_sel_period =
- ATH_CHAINMASK_SEL_TIMEOUT;
-
-/* return bus cachesize in 4B word units */
-
-static void bus_read_cachesize(struct ath_softc *sc, int *csz)
-{
- u8 u8tmp;
-
- pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp);
- *csz = (int)u8tmp;
-
- /*
- * This check was put in to avoid "unplesant" consequences if
- * the bootrom has not fully initialized all PCI devices.
- * Sometimes the cache line size register is not set
- */
-
- if (*csz == 0)
- *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */
-}
-
-/*
- * Set current operating mode
- *
- * This function initializes and fills the rate table in the ATH object based
- * on the operating mode.
-*/
-static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
-{
- const struct ath9k_rate_table *rt;
- int i;
-
- memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
- rt = ath9k_hw_getratetable(sc->sc_ah, mode);
- BUG_ON(!rt);
-
- for (i = 0; i < rt->rateCount; i++)
- sc->sc_rixmap[rt->info[i].rateCode] = (u8) i;
-
- memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
- for (i = 0; i < 256; i++) {
- u8 ix = rt->rateCodeToIndex[i];
-
- if (ix == 0xff)
- continue;
-
- sc->sc_hwmap[i].ieeerate =
- rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
- sc->sc_hwmap[i].rateKbps = rt->info[ix].rateKbps;
-
- if (rt->info[ix].shortPreamble ||
- rt->info[ix].phy == PHY_OFDM) {
- /* XXX: Handle this */
- }
-
- /* NB: this uses the last entry if the rate isn't found */
- /* XXX beware of overlow */
- }
- sc->sc_currates = rt;
- sc->sc_curmode = mode;
- /*
- * All protection frames are transmited at 2Mb/s for
- * 11g, otherwise at 1Mb/s.
- * XXX select protection rate index from rate table.
- */
- sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
-}
-
-/*
- * Set up rate table (legacy rates)
- */
-static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
-{
- struct ath_hal *ah = sc->sc_ah;
- const struct ath9k_rate_table *rt = NULL;
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *rate;
- int i, maxrates;
-
- switch (band) {
- case IEEE80211_BAND_2GHZ:
- rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11G);
- break;
- case IEEE80211_BAND_5GHZ:
- rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11A);
- break;
- default:
- break;
- }
-
- if (rt == NULL)
- return;
-
- sband = &sc->sbands[band];
- rate = sc->rates[band];
-
- if (rt->rateCount > ATH_RATE_MAX)
- maxrates = ATH_RATE_MAX;
- else
- maxrates = rt->rateCount;
-
- for (i = 0; i < maxrates; i++) {
- rate[i].bitrate = rt->info[i].rateKbps / 100;
- rate[i].hw_value = rt->info[i].rateCode;
- sband->n_bitrates++;
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: Rate: %2dMbps, ratecode: %2d\n",
- __func__,
- rate[i].bitrate / 10,
- rate[i].hw_value);
- }
-}
-
-/*
- * Set up channel list
- */
-static int ath_setup_channels(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
- int nchan, i, a = 0, b = 0;
- u8 regclassids[ATH_REGCLASSIDS_MAX];
- u32 nregclass = 0;
- struct ieee80211_supported_band *band_2ghz;
- struct ieee80211_supported_band *band_5ghz;
- struct ieee80211_channel *chan_2ghz;
- struct ieee80211_channel *chan_5ghz;
- struct ath9k_channel *c;
-
- /* Fill in ah->ah_channels */
- if (!ath9k_regd_init_channels(ah,
- ATH_CHAN_MAX,
- (u32 *)&nchan,
- regclassids,
- ATH_REGCLASSIDS_MAX,
- &nregclass,
- CTRY_DEFAULT,
- false,
- 1)) {
- u32 rd = ah->ah_currentRD;
-
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to collect channel list; "
- "regdomain likely %u country code %u\n",
- __func__, rd, CTRY_DEFAULT);
- return -EINVAL;
- }
-
- band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ];
- band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ];
- chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ];
- chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ];
-
- for (i = 0; i < nchan; i++) {
- c = &ah->ah_channels[i];
- if (IS_CHAN_2GHZ(c)) {
- chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
- chan_2ghz[a].center_freq = c->channel;
- chan_2ghz[a].max_power = c->maxTxPower;
-
- if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
- chan_2ghz[a].flags |=
- IEEE80211_CHAN_NO_IBSS;
- if (c->channelFlags & CHANNEL_PASSIVE)
- chan_2ghz[a].flags |=
- IEEE80211_CHAN_PASSIVE_SCAN;
-
- band_2ghz->n_channels = ++a;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: 2MHz channel: %d, "
- "channelFlags: 0x%x\n",
- __func__,
- c->channel,
- c->channelFlags);
- } else if (IS_CHAN_5GHZ(c)) {
- chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
- chan_5ghz[b].center_freq = c->channel;
- chan_5ghz[b].max_power = c->maxTxPower;
-
- if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
- chan_5ghz[b].flags |=
- IEEE80211_CHAN_NO_IBSS;
- if (c->channelFlags & CHANNEL_PASSIVE)
- chan_5ghz[b].flags |=
- IEEE80211_CHAN_PASSIVE_SCAN;
-
- band_5ghz->n_channels = ++b;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: 5MHz channel: %d, "
- "channelFlags: 0x%x\n",
- __func__,
- c->channel,
- c->channelFlags);
- }
- }
-
- return 0;
-}
-
-/*
- * Determine mode from channel flags
- *
- * This routine will provide the enumerated WIRELESSS_MODE value based
- * on the settings of the channel flags. If no valid set of flags
- * exist, the lowest mode (11b) is selected.
-*/
-
-static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan)
-{
- if (chan->chanmode == CHANNEL_A)
- return ATH9K_MODE_11A;
- else if (chan->chanmode == CHANNEL_G)
- return ATH9K_MODE_11G;
- else if (chan->chanmode == CHANNEL_B)
- return ATH9K_MODE_11B;
- else if (chan->chanmode == CHANNEL_A_HT20)
- return ATH9K_MODE_11NA_HT20;
- else if (chan->chanmode == CHANNEL_G_HT20)
- return ATH9K_MODE_11NG_HT20;
- else if (chan->chanmode == CHANNEL_A_HT40PLUS)
- return ATH9K_MODE_11NA_HT40PLUS;
- else if (chan->chanmode == CHANNEL_A_HT40MINUS)
- return ATH9K_MODE_11NA_HT40MINUS;
- else if (chan->chanmode == CHANNEL_G_HT40PLUS)
- return ATH9K_MODE_11NG_HT40PLUS;
- else if (chan->chanmode == CHANNEL_G_HT40MINUS)
- return ATH9K_MODE_11NG_HT40MINUS;
-
- WARN_ON(1); /* should not get here */
-
- return ATH9K_MODE_11B;
-}
-
-/*
- * Stop the device, grabbing the top-level lock to protect
- * against concurrent entry through ath_init (which can happen
- * if another thread does a system call and the thread doing the
- * stop is preempted).
- */
-
-static int ath_stop(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: invalid %ld\n",
- __func__, sc->sc_flags & SC_OP_INVALID);
-
- /*
- * Shutdown the hardware and driver:
- * stop output from above
- * turn off timers
- * disable interrupts
- * clear transmit machinery
- * clear receive machinery
- * turn off the radio
- * reclaim beacon resources
- *
- * Note that some of this work is not possible if the
- * hardware is gone (invalid).
- */
-
- ath_draintxq(sc, false);
- if (!(sc->sc_flags & SC_OP_INVALID)) {
- ath_stoprecv(sc);
- ath9k_hw_phy_disable(ah);
- } else
- sc->sc_rxlink = NULL;
-
- return 0;
-}
-
-/*
- * Set the current channel
- *
- * Set/change channels. If the channel is really being changed, it's done
- * by reseting the chip. To accomplish this we must first cleanup any pending
- * DMA, then restart stuff after a la ath_init.
-*/
-int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
-{
- struct ath_hal *ah = sc->sc_ah;
- bool fastcc = true, stopped;
-
- if (sc->sc_flags & SC_OP_INVALID) /* the device is invalid or removed */
- return -EIO;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: %u (%u MHz) -> %u (%u MHz), cflags:%x\n",
- __func__,
- ath9k_hw_mhz2ieee(ah, sc->sc_ah->ah_curchan->channel,
- sc->sc_ah->ah_curchan->channelFlags),
- sc->sc_ah->ah_curchan->channel,
- ath9k_hw_mhz2ieee(ah, hchan->channel, hchan->channelFlags),
- hchan->channel, hchan->channelFlags);
-
- if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
- hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
- (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
- (sc->sc_flags & SC_OP_FULL_RESET)) {
- int status;
- /*
- * This is only performed if the channel settings have
- * actually changed.
- *
- * To switch channels clear any pending DMA operations;
- * wait long enough for the RX fifo to drain, reset the
- * hardware at the new frequency, and then re-enable
- * the relevant bits of the h/w.
- */
- ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
- ath_draintxq(sc, false); /* clear pending tx frames */
- stopped = ath_stoprecv(sc); /* turn off frame recv */
-
- /* XXX: do not flush receive queue here. We don't want
- * to flush data frames already in queue because of
- * changing channel. */
-
- if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
- fastcc = false;
-
- spin_lock_bh(&sc->sc_resetlock);
- if (!ath9k_hw_reset(ah, hchan,
- sc->sc_ht_info.tx_chan_width,
- sc->sc_tx_chainmask,
- sc->sc_rx_chainmask,
- sc->sc_ht_extprotspacing,
- fastcc, &status)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset channel %u (%uMhz) "
- "flags 0x%x hal status %u\n", __func__,
- ath9k_hw_mhz2ieee(ah, hchan->channel,
- hchan->channelFlags),
- hchan->channel, hchan->channelFlags, status);
- spin_unlock_bh(&sc->sc_resetlock);
- return -EIO;
- }
- spin_unlock_bh(&sc->sc_resetlock);
-
- sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
- sc->sc_flags &= ~SC_OP_FULL_RESET;
-
- /* Re-enable rx framework */
- if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to restart recv logic\n", __func__);
- return -EIO;
- }
- /*
- * Change channels and update the h/w rate map
- * if we're switching; e.g. 11a to 11b/g.
- */
- ath_setcurmode(sc, ath_chan2mode(hchan));
-
- ath_update_txpow(sc); /* update tx power state */
- /*
- * Re-enable interrupts.
- */
- ath9k_hw_set_interrupts(ah, sc->sc_imask);
- }
- return 0;
-}
-
-/**********************/
-/* Chainmask Handling */
-/**********************/
-
-static void ath_chainmask_sel_timertimeout(unsigned long data)
-{
- struct ath_chainmask_sel *cm = (struct ath_chainmask_sel *)data;
- cm->switch_allowed = 1;
-}
-
-/* Start chainmask select timer */
-static void ath_chainmask_sel_timerstart(struct ath_chainmask_sel *cm)
-{
- cm->switch_allowed = 0;
- mod_timer(&cm->timer, ath_chainmask_sel_period);
-}
-
-/* Stop chainmask select timer */
-static void ath_chainmask_sel_timerstop(struct ath_chainmask_sel *cm)
-{
- cm->switch_allowed = 0;
- del_timer_sync(&cm->timer);
-}
-
-static void ath_chainmask_sel_init(struct ath_softc *sc, struct ath_node *an)
-{
- struct ath_chainmask_sel *cm = &an->an_chainmask_sel;
-
- memset(cm, 0, sizeof(struct ath_chainmask_sel));
-
- cm->cur_tx_mask = sc->sc_tx_chainmask;
- cm->cur_rx_mask = sc->sc_rx_chainmask;
- cm->tx_avgrssi = ATH_RSSI_DUMMY_MARKER;
- setup_timer(&cm->timer,
- ath_chainmask_sel_timertimeout, (unsigned long) cm);
-}
-
-int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an)
-{
- struct ath_chainmask_sel *cm = &an->an_chainmask_sel;
-
- /*
- * Disable auto-swtiching in one of the following if conditions.
- * sc_chainmask_auto_sel is used for internal global auto-switching
- * enabled/disabled setting
- */
- if (sc->sc_ah->ah_caps.tx_chainmask != ATH_CHAINMASK_SEL_3X3) {
- cm->cur_tx_mask = sc->sc_tx_chainmask;
- return cm->cur_tx_mask;
- }
-
- if (cm->tx_avgrssi == ATH_RSSI_DUMMY_MARKER)
- return cm->cur_tx_mask;
-
- if (cm->switch_allowed) {
- /* Switch down from tx 3 to tx 2. */
- if (cm->cur_tx_mask == ATH_CHAINMASK_SEL_3X3 &&
- ATH_RSSI_OUT(cm->tx_avgrssi) >=
- ath_chainmask_sel_down_rssi_thres) {
- cm->cur_tx_mask = sc->sc_tx_chainmask;
-
- /* Don't let another switch happen until
- * this timer expires */
- ath_chainmask_sel_timerstart(cm);
- }
- /* Switch up from tx 2 to 3. */
- else if (cm->cur_tx_mask == sc->sc_tx_chainmask &&
- ATH_RSSI_OUT(cm->tx_avgrssi) <=
- ath_chainmask_sel_up_rssi_thres) {
- cm->cur_tx_mask = ATH_CHAINMASK_SEL_3X3;
-
- /* Don't let another switch happen
- * until this timer expires */
- ath_chainmask_sel_timerstart(cm);
- }
- }
-
- return cm->cur_tx_mask;
-}
-
-/*
- * Update tx/rx chainmask. For legacy association,
- * hard code chainmask to 1x1, for 11n association, use
- * the chainmask configuration.
- */
-
-void ath_update_chainmask(struct ath_softc *sc, int is_ht)
-{
- sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
- if (is_ht) {
- sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
- sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
- } else {
- sc->sc_tx_chainmask = 1;
- sc->sc_rx_chainmask = 1;
- }
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: tx chmask: %d, rx chmask: %d\n",
- __func__, sc->sc_tx_chainmask, sc->sc_rx_chainmask);
-}
-
-/*******/
-/* ANI */
-/*******/
-
-/*
- * This routine performs the periodic noise floor calibration function
- * that is used to adjust and optimize the chip performance. This
- * takes environmental changes (location, temperature) into account.
- * When the task is complete, it reschedules itself depending on the
- * appropriate interval that was calculated.
- */
-
-static void ath_ani_calibrate(unsigned long data)
-{
- struct ath_softc *sc;
- struct ath_hal *ah;
- bool longcal = false;
- bool shortcal = false;
- bool aniflag = false;
- unsigned int timestamp = jiffies_to_msecs(jiffies);
- u32 cal_interval;
-
- sc = (struct ath_softc *)data;
- ah = sc->sc_ah;
-
- /*
- * don't calibrate when we're scanning.
- * we are most likely not on our home channel.
- */
- if (sc->rx_filter & FIF_BCN_PRBRESP_PROMISC)
- return;
-
- /* Long calibration runs independently of short calibration. */
- if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
- longcal = true;
- DPRINTF(sc, ATH_DBG_ANI, "%s: longcal @%lu\n",
- __func__, jiffies);
- sc->sc_ani.sc_longcal_timer = timestamp;
- }
-
- /* Short calibration applies only while sc_caldone is false */
- if (!sc->sc_ani.sc_caldone) {
- if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
- ATH_SHORT_CALINTERVAL) {
- shortcal = true;
- DPRINTF(sc, ATH_DBG_ANI, "%s: shortcal @%lu\n",
- __func__, jiffies);
- sc->sc_ani.sc_shortcal_timer = timestamp;
- sc->sc_ani.sc_resetcal_timer = timestamp;
- }
- } else {
- if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
- ATH_RESTART_CALINTERVAL) {
- ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
- &sc->sc_ani.sc_caldone);
- if (sc->sc_ani.sc_caldone)
- sc->sc_ani.sc_resetcal_timer = timestamp;
- }
- }
-
- /* Verify whether we must check ANI */
- if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
- ATH_ANI_POLLINTERVAL) {
- aniflag = true;
- sc->sc_ani.sc_checkani_timer = timestamp;
- }
-
- /* Skip all processing if there's nothing to do. */
- if (longcal || shortcal || aniflag) {
- /* Call ANI routine if necessary */
- if (aniflag)
- ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
- ah->ah_curchan);
-
- /* Perform calibration if necessary */
- if (longcal || shortcal) {
- bool iscaldone = false;
-
- if (ath9k_hw_calibrate(ah, ah->ah_curchan,
- sc->sc_rx_chainmask, longcal,
- &iscaldone)) {
- if (longcal)
- sc->sc_ani.sc_noise_floor =
- ath9k_hw_getchan_noise(ah,
- ah->ah_curchan);
-
- DPRINTF(sc, ATH_DBG_ANI,
- "%s: calibrate chan %u/%x nf: %d\n",
- __func__,
- ah->ah_curchan->channel,
- ah->ah_curchan->channelFlags,
- sc->sc_ani.sc_noise_floor);
- } else {
- DPRINTF(sc, ATH_DBG_ANY,
- "%s: calibrate chan %u/%x failed\n",
- __func__,
- ah->ah_curchan->channel,
- ah->ah_curchan->channelFlags);
- }
- sc->sc_ani.sc_caldone = iscaldone;
- }
- }
-
- /*
- * Set timer interval based on previous results.
- * The interval must be the shortest necessary to satisfy ANI,
- * short calibration and long calibration.
- */
-
- cal_interval = ATH_ANI_POLLINTERVAL;
- if (!sc->sc_ani.sc_caldone)
- cal_interval = min(cal_interval, (u32)ATH_SHORT_CALINTERVAL);
-
- mod_timer(&sc->sc_ani.timer, jiffies + msecs_to_jiffies(cal_interval));
-}
-
-/******************/
-/* VAP management */
-/******************/
-
-int ath_vap_attach(struct ath_softc *sc,
- int if_id,
- struct ieee80211_vif *if_data,
- enum ath9k_opmode opmode)
-{
- struct ath_vap *avp;
-
- if (if_id >= ATH_BCBUF || sc->sc_vaps[if_id] != NULL) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Invalid interface id = %u\n", __func__, if_id);
- return -EINVAL;
- }
-
- switch (opmode) {
- case ATH9K_M_STA:
- case ATH9K_M_IBSS:
- case ATH9K_M_MONITOR:
- break;
- case ATH9K_M_HOSTAP:
- /* XXX not right, beacon buffer is allocated on RUN trans */
- if (list_empty(&sc->sc_bbuf))
- return -ENOMEM;
- break;
- default:
- return -EINVAL;
- }
-
- /* create ath_vap */
- avp = kmalloc(sizeof(struct ath_vap), GFP_KERNEL);
- if (avp == NULL)
- return -ENOMEM;
-
- memset(avp, 0, sizeof(struct ath_vap));
- avp->av_if_data = if_data;
- /* Set the VAP opmode */
- avp->av_opmode = opmode;
- avp->av_bslot = -1;
-
- if (opmode == ATH9K_M_HOSTAP)
- ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
-
- sc->sc_vaps[if_id] = avp;
- sc->sc_nvaps++;
- /* Set the device opmode */
- sc->sc_ah->ah_opmode = opmode;
-
- /* default VAP configuration */
- avp->av_config.av_fixed_rateset = IEEE80211_FIXED_RATE_NONE;
- avp->av_config.av_fixed_retryset = 0x03030303;
-
- return 0;
-}
-
-int ath_vap_detach(struct ath_softc *sc, int if_id)
-{
- struct ath_hal *ah = sc->sc_ah;
- struct ath_vap *avp;
-
- avp = sc->sc_vaps[if_id];
- if (avp == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n",
- __func__, if_id);
- return -EINVAL;
- }
-
- /*
- * Quiesce the hardware while we remove the vap. In
- * particular we need to reclaim all references to the
- * vap state by any frames pending on the tx queues.
- *
- * XXX can we do this w/o affecting other vap's?
- */
- ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
- ath_draintxq(sc, false); /* stop xmit side */
- ath_stoprecv(sc); /* stop recv side */
- ath_flushrecv(sc); /* flush recv queue */
-
- kfree(avp);
- sc->sc_vaps[if_id] = NULL;
- sc->sc_nvaps--;
-
- return 0;
-}
-
-int ath_vap_config(struct ath_softc *sc,
- int if_id, struct ath_vap_config *if_config)
-{
- struct ath_vap *avp;
-
- if (if_id >= ATH_BCBUF) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Invalid interface id = %u\n", __func__, if_id);
- return -EINVAL;
- }
-
- avp = sc->sc_vaps[if_id];
- ASSERT(avp != NULL);
-
- if (avp)
- memcpy(&avp->av_config, if_config, sizeof(avp->av_config));
-
- return 0;
-}
-
-/********/
-/* Core */
-/********/
-
-int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan)
-{
- struct ath_hal *ah = sc->sc_ah;
- int status;
- int error = 0;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: mode %d\n",
- __func__, sc->sc_ah->ah_opmode);
-
- /*
- * Stop anything previously setup. This is safe
- * whether this is the first time through or not.
- */
- ath_stop(sc);
-
- /* Initialize chanmask selection */
- sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask;
- sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
-
- /* Reset SERDES registers */
- ath9k_hw_configpcipowersave(ah, 0);
-
- /*
- * The basic interface to setting the hardware in a good
- * state is ``reset''. On return the hardware is known to
- * be powered up and with interrupts disabled. This must
- * be followed by initialization of the appropriate bits
- * and then setup of the interrupt mask.
- */
-
- spin_lock_bh(&sc->sc_resetlock);
- if (!ath9k_hw_reset(ah, initial_chan,
- sc->sc_ht_info.tx_chan_width,
- sc->sc_tx_chainmask, sc->sc_rx_chainmask,
- sc->sc_ht_extprotspacing, false, &status)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset hardware; hal status %u "
- "(freq %u flags 0x%x)\n", __func__, status,
- initial_chan->channel, initial_chan->channelFlags);
- error = -EIO;
- spin_unlock_bh(&sc->sc_resetlock);
- goto done;
- }
- spin_unlock_bh(&sc->sc_resetlock);
- /*
- * This is needed only to setup initial state
- * but it's best done after a reset.
- */
- ath_update_txpow(sc);
-
- /*
- * Setup the hardware after reset:
- * The receive engine is set going.
- * Frame transmit is handled entirely
- * in the frame output path; there's nothing to do
- * here except setup the interrupt mask.
- */
- if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to start recv logic\n", __func__);
- error = -EIO;
- goto done;
- }
- /* Setup our intr mask. */
- sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX
- | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
- | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
-
- if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT)
- sc->sc_imask |= ATH9K_INT_GTT;
-
- if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
- sc->sc_imask |= ATH9K_INT_CST;
-
- /*
- * Enable MIB interrupts when there are hardware phy counters.
- * Note we only do this (at the moment) for station mode.
- */
- if (ath9k_hw_phycounters(ah) &&
- ((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
- (sc->sc_ah->ah_opmode == ATH9K_M_IBSS)))
- sc->sc_imask |= ATH9K_INT_MIB;
- /*
- * Some hardware processes the TIM IE and fires an
- * interrupt when the TIM bit is set. For hardware
- * that does, if not overridden by configuration,
- * enable the TIM interrupt when operating as station.
- */
- if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
- (sc->sc_ah->ah_opmode == ATH9K_M_STA) &&
- !sc->sc_config.swBeaconProcess)
- sc->sc_imask |= ATH9K_INT_TIM;
- /*
- * Don't enable interrupts here as we've not yet built our
- * vap and node data structures, which will be needed as soon
- * as we start receiving.
- */
- ath_setcurmode(sc, ath_chan2mode(initial_chan));
-
- /* XXX: we must make sure h/w is ready and clear invalid flag
- * before turning on interrupt. */
- sc->sc_flags &= ~SC_OP_INVALID;
-done:
- return error;
-}
-
-int ath_reset(struct ath_softc *sc, bool retry_tx)
-{
- struct ath_hal *ah = sc->sc_ah;
- int status;
- int error = 0;
-
- ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
- ath_draintxq(sc, retry_tx); /* stop xmit */
- ath_stoprecv(sc); /* stop recv */
- ath_flushrecv(sc); /* flush recv queue */
-
- /* Reset chip */
- spin_lock_bh(&sc->sc_resetlock);
- if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan,
- sc->sc_ht_info.tx_chan_width,
- sc->sc_tx_chainmask, sc->sc_rx_chainmask,
- sc->sc_ht_extprotspacing, false, &status)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset hardware; hal status %u\n",
- __func__, status);
- error = -EIO;
- }
- spin_unlock_bh(&sc->sc_resetlock);
-
- if (ath_startrecv(sc) != 0) /* restart recv */
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to start recv logic\n", __func__);
-
- /*
- * We may be doing a reset in response to a request
- * that changes the channel so update any state that
- * might change as a result.
- */
- ath_setcurmode(sc, ath_chan2mode(sc->sc_ah->ah_curchan));
-
- ath_update_txpow(sc);
-
- if (sc->sc_flags & SC_OP_BEACONS)
- ath_beacon_config(sc, ATH_IF_ID_ANY); /* restart beacons */
-
- ath9k_hw_set_interrupts(ah, sc->sc_imask);
-
- /* Restart the txq */
- if (retry_tx) {
- int i;
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
- if (ATH_TXQ_SETUP(sc, i)) {
- spin_lock_bh(&sc->sc_txq[i].axq_lock);
- ath_txq_schedule(sc, &sc->sc_txq[i]);
- spin_unlock_bh(&sc->sc_txq[i].axq_lock);
- }
- }
- }
-
- return error;
-}
-
-int ath_suspend(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
-
- /* No I/O if device has been surprise removed */
- if (sc->sc_flags & SC_OP_INVALID)
- return -EIO;
-
- /* Shut off the interrupt before setting sc->sc_invalid to '1' */
- ath9k_hw_set_interrupts(ah, 0);
-
- /* XXX: we must make sure h/w will not generate any interrupt
- * before setting the invalid flag. */
- sc->sc_flags |= SC_OP_INVALID;
-
- /* disable HAL and put h/w to sleep */
- ath9k_hw_disable(sc->sc_ah);
-
- ath9k_hw_configpcipowersave(sc->sc_ah, 1);
-
- return 0;
-}
-
-/* Interrupt handler. Most of the actual processing is deferred.
- * It's the caller's responsibility to ensure the chip is awake. */
-
-irqreturn_t ath_isr(int irq, void *dev)
-{
- struct ath_softc *sc = dev;
- struct ath_hal *ah = sc->sc_ah;
- enum ath9k_int status;
- bool sched = false;
-
- do {
- if (sc->sc_flags & SC_OP_INVALID) {
- /*
- * The hardware is not ready/present, don't
- * touch anything. Note this can happen early
- * on if the IRQ is shared.
- */
- return IRQ_NONE;
- }
- if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */
- return IRQ_NONE;
- }
-
- /*
- * Figure out the reason(s) for the interrupt. Note
- * that the hal returns a pseudo-ISR that may include
- * bits we haven't explicitly enabled so we mask the
- * value to insure we only process bits we requested.
- */
- ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
-
- status &= sc->sc_imask; /* discard unasked-for bits */
-
- /*
- * If there are no status bits set, then this interrupt was not
- * for me (should have been caught above).
- */
-
- if (!status)
- return IRQ_NONE;
-
- sc->sc_intrstatus = status;
-
- if (status & ATH9K_INT_FATAL) {
- /* need a chip reset */
- sched = true;
- } else if (status & ATH9K_INT_RXORN) {
- /* need a chip reset */
- sched = true;
- } else {
- if (status & ATH9K_INT_SWBA) {
- /* schedule a tasklet for beacon handling */
- tasklet_schedule(&sc->bcon_tasklet);
- }
- if (status & ATH9K_INT_RXEOL) {
- /*
- * NB: the hardware should re-read the link when
- * RXE bit is written, but it doesn't work
- * at least on older hardware revs.
- */
- sched = true;
- }
-
- if (status & ATH9K_INT_TXURN)
- /* bump tx trigger level */
- ath9k_hw_updatetxtriglevel(ah, true);
- /* XXX: optimize this */
- if (status & ATH9K_INT_RX)
- sched = true;
- if (status & ATH9K_INT_TX)
- sched = true;
- if (status & ATH9K_INT_BMISS)
- sched = true;
- /* carrier sense timeout */
- if (status & ATH9K_INT_CST)
- sched = true;
- if (status & ATH9K_INT_MIB) {
- /*
- * Disable interrupts until we service the MIB
- * interrupt; otherwise it will continue to
- * fire.
- */
- ath9k_hw_set_interrupts(ah, 0);
- /*
- * Let the hal handle the event. We assume
- * it will clear whatever condition caused
- * the interrupt.
- */
- ath9k_hw_procmibevent(ah, &sc->sc_halstats);
- ath9k_hw_set_interrupts(ah, sc->sc_imask);
- }
- if (status & ATH9K_INT_TIM_TIMER) {
- if (!(ah->ah_caps.hw_caps &
- ATH9K_HW_CAP_AUTOSLEEP)) {
- /* Clear RxAbort bit so that we can
- * receive frames */
- ath9k_hw_setrxabort(ah, 0);
- sched = true;
- }
- }
- }
- } while (0);
-
- if (sched) {
- /* turn off every interrupt except SWBA */
- ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA));
- tasklet_schedule(&sc->intr_tq);
- }
-
- return IRQ_HANDLED;
-}
-
-/* Deferred interrupt processing */
-
-static void ath9k_tasklet(unsigned long data)
-{
- struct ath_softc *sc = (struct ath_softc *)data;
- u32 status = sc->sc_intrstatus;
-
- if (status & ATH9K_INT_FATAL) {
- /* need a chip reset */
- ath_reset(sc, false);
- return;
- } else {
-
- if (status &
- (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) {
- /* XXX: fill me in */
- /*
- if (status & ATH9K_INT_RXORN) {
- }
- if (status & ATH9K_INT_RXEOL) {
- }
- */
- spin_lock_bh(&sc->sc_rxflushlock);
- ath_rx_tasklet(sc, 0);
- spin_unlock_bh(&sc->sc_rxflushlock);
- }
- /* XXX: optimize this */
- if (status & ATH9K_INT_TX)
- ath_tx_tasklet(sc);
- /* XXX: fill me in */
- /*
- if (status & ATH9K_INT_BMISS) {
- }
- if (status & (ATH9K_INT_TIM | ATH9K_INT_DTIMSYNC)) {
- if (status & ATH9K_INT_TIM) {
- }
- if (status & ATH9K_INT_DTIMSYNC) {
- }
- }
- */
- }
-
- /* re-enable hardware interrupt */
- ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
-}
-
-int ath_init(u16 devid, struct ath_softc *sc)
-{
- struct ath_hal *ah = NULL;
- int status;
- int error = 0, i;
- int csz = 0;
-
- /* XXX: hardware will not be ready until ath_open() being called */
- sc->sc_flags |= SC_OP_INVALID;
-
- sc->sc_debug = DBG_DEFAULT;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: devid 0x%x\n", __func__, devid);
-
- /* Initialize tasklet */
- tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
- tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
- (unsigned long)sc);
-
- /*
- * Cache line size is used to size and align various
- * structures used to communicate with the hardware.
- */
- bus_read_cachesize(sc, &csz);
- /* XXX assert csz is non-zero */
- sc->sc_cachelsz = csz << 2; /* convert to bytes */
-
- spin_lock_init(&sc->sc_resetlock);
-
- ah = ath9k_hw_attach(devid, sc, sc->mem, &status);
- if (ah == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to attach hardware; HAL status %u\n",
- __func__, status);
- error = -ENXIO;
- goto bad;
- }
- sc->sc_ah = ah;
-
- /* Initializes the noise floor to a reasonable default value.
- * Later on this will be updated during ANI processing. */
- sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
-
- /* Get the hardware key cache size. */
- sc->sc_keymax = ah->ah_caps.keycache_size;
- if (sc->sc_keymax > ATH_KEYMAX) {
- DPRINTF(sc, ATH_DBG_KEYCACHE,
- "%s: Warning, using only %u entries in %u key cache\n",
- __func__, ATH_KEYMAX, sc->sc_keymax);
- sc->sc_keymax = ATH_KEYMAX;
- }
-
- /*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up.
- */
- for (i = 0; i < sc->sc_keymax; i++)
- ath9k_hw_keyreset(ah, (u16) i);
- /*
- * Mark key cache slots associated with global keys
- * as in use. If we knew TKIP was not to be used we
- * could leave the +32, +64, and +32+64 slots free.
- * XXX only for splitmic.
- */
- for (i = 0; i < IEEE80211_WEP_NKID; i++) {
- set_bit(i, sc->sc_keymap);
- set_bit(i + 32, sc->sc_keymap);
- set_bit(i + 64, sc->sc_keymap);
- set_bit(i + 32 + 64, sc->sc_keymap);
- }
- /*
- * Collect the channel list using the default country
- * code and including outdoor channels. The 802.11 layer
- * is resposible for filtering this list based on settings
- * like the phy mode.
- */
- error = ath_setup_channels(sc);
- if (error)
- goto bad;
-
- /* default to STA mode */
- sc->sc_ah->ah_opmode = ATH9K_M_MONITOR;
-
- /* Setup rate tables */
-
- ath_setup_rates(sc, IEEE80211_BAND_2GHZ);
- ath_setup_rates(sc, IEEE80211_BAND_5GHZ);
-
- /* NB: setup here so ath_rate_update is happy */
- ath_setcurmode(sc, ATH9K_MODE_11A);
-
- /*
- * Allocate hardware transmit queues: one queue for
- * beacon frames and one data queue for each QoS
- * priority. Note that the hal handles reseting
- * these queues at the needed time.
- */
- sc->sc_bhalq = ath_beaconq_setup(ah);
- if (sc->sc_bhalq == -1) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup a beacon xmit queue\n", __func__);
- error = -EIO;
- goto bad2;
- }
- sc->sc_cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
- if (sc->sc_cabq == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup CAB xmit queue\n", __func__);
- error = -EIO;
- goto bad2;
- }
-
- sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
- ath_cabq_update(sc);
-
- for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++)
- sc->sc_haltype2q[i] = -1;
-
- /* Setup data queues */
- /* NB: ensure BK queue is the lowest priority h/w queue */
- if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup xmit queue for BK traffic\n",
- __func__);
- error = -EIO;
- goto bad2;
- }
-
- if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup xmit queue for BE traffic\n",
- __func__);
- error = -EIO;
- goto bad2;
- }
- if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup xmit queue for VI traffic\n",
- __func__);
- error = -EIO;
- goto bad2;
- }
- if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to setup xmit queue for VO traffic\n",
- __func__);
- error = -EIO;
- goto bad2;
- }
-
- setup_timer(&sc->sc_ani.timer, ath_ani_calibrate, (unsigned long)sc);
-
- sc->sc_rc = ath_rate_attach(ah);
- if (sc->sc_rc == NULL) {
- error = -EIO;
- goto bad2;
- }
-
- if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
- ATH9K_CIPHER_TKIP, NULL)) {
- /*
- * Whether we should enable h/w TKIP MIC.
- * XXX: if we don't support WME TKIP MIC, then we wouldn't
- * report WMM capable, so it's always safe to turn on
- * TKIP MIC in this case.
- */
- ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
- 0, 1, NULL);
- }
-
- /*
- * Check whether the separate key cache entries
- * are required to handle both tx+rx MIC keys.
- * With split mic keys the number of stations is limited
- * to 27 otherwise 59.
- */
- if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
- ATH9K_CIPHER_TKIP, NULL)
- && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
- ATH9K_CIPHER_MIC, NULL)
- && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
- 0, NULL))
- sc->sc_splitmic = 1;
-
- /* turn on mcast key search if possible */
- if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
- (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
- 1, NULL);
-
- sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
- sc->sc_config.txpowlimit_override = 0;
-
- /* 11n Capabilities */
- if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
- sc->sc_flags |= SC_OP_TXAGGR;
- sc->sc_flags |= SC_OP_RXAGGR;
- }
-
- sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask;
- sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
-
- ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
- sc->sc_defant = ath9k_hw_getdefantenna(ah);
-
- ath9k_hw_getmac(ah, sc->sc_myaddr);
- if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
- ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
- ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
- ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
- }
- sc->sc_slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
-
- /* initialize beacon slots */
- for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++)
- sc->sc_bslot[i] = ATH_IF_ID_ANY;
-
- /* save MISC configurations */
- sc->sc_config.swBeaconProcess = 1;
-
-#ifdef CONFIG_SLOW_ANT_DIV
- /* range is 40 - 255, we use something in the middle */
- ath_slow_ant_div_init(&sc->sc_antdiv, sc, 0x127);
-#endif
-
- return 0;
-bad2:
- /* cleanup tx queues */
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
- if (ATH_TXQ_SETUP(sc, i))
- ath_tx_cleanupq(sc, &sc->sc_txq[i]);
-bad:
- if (ah)
- ath9k_hw_detach(ah);
- return error;
-}
-
-void ath_deinit(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
- int i;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s\n", __func__);
-
- tasklet_kill(&sc->intr_tq);
- tasklet_kill(&sc->bcon_tasklet);
- ath_stop(sc);
- if (!(sc->sc_flags & SC_OP_INVALID))
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
- ath_rate_detach(sc->sc_rc);
- /* cleanup tx queues */
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
- if (ATH_TXQ_SETUP(sc, i))
- ath_tx_cleanupq(sc, &sc->sc_txq[i]);
- ath9k_hw_detach(ah);
-}
-
-/*******************/
-/* Node Management */
-/*******************/
-
-struct ath_node *ath_node_attach(struct ath_softc *sc, u8 *addr, int if_id)
-{
- struct ath_vap *avp;
- struct ath_node *an;
- DECLARE_MAC_BUF(mac);
-
- avp = sc->sc_vaps[if_id];
- ASSERT(avp != NULL);
-
- /* mac80211 sta_notify callback is from an IRQ context, so no sleep */
- an = kmalloc(sizeof(struct ath_node), GFP_ATOMIC);
- if (an == NULL)
- return NULL;
- memset(an, 0, sizeof(*an));
-
- an->an_sc = sc;
- memcpy(an->an_addr, addr, ETH_ALEN);
- atomic_set(&an->an_refcnt, 1);
-
- /* set up per-node tx/rx state */
- ath_tx_node_init(sc, an);
- ath_rx_node_init(sc, an);
-
- ath_chainmask_sel_init(sc, an);
- ath_chainmask_sel_timerstart(&an->an_chainmask_sel);
- list_add(&an->list, &sc->node_list);
-
- return an;
-}
-
-void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag)
-{
- unsigned long flags;
-
- DECLARE_MAC_BUF(mac);
-
- ath_chainmask_sel_timerstop(&an->an_chainmask_sel);
- an->an_flags |= ATH_NODE_CLEAN;
- ath_tx_node_cleanup(sc, an, bh_flag);
- ath_rx_node_cleanup(sc, an);
-
- ath_tx_node_free(sc, an);
- ath_rx_node_free(sc, an);
-
- spin_lock_irqsave(&sc->node_lock, flags);
-
- list_del(&an->list);
-
- spin_unlock_irqrestore(&sc->node_lock, flags);
-
- kfree(an);
-}
-
-/* Finds a node and increases the refcnt if found */
-
-struct ath_node *ath_node_get(struct ath_softc *sc, u8 *addr)
-{
- struct ath_node *an = NULL, *an_found = NULL;
-
- if (list_empty(&sc->node_list)) /* FIXME */
- goto out;
- list_for_each_entry(an, &sc->node_list, list) {
- if (!compare_ether_addr(an->an_addr, addr)) {
- atomic_inc(&an->an_refcnt);
- an_found = an;
- break;
- }
- }
-out:
- return an_found;
-}
-
-/* Decrements the refcnt and if it drops to zero, detach the node */
-
-void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag)
-{
- if (atomic_dec_and_test(&an->an_refcnt))
- ath_node_detach(sc, an, bh_flag);
-}
-
-/* Finds a node, doesn't increment refcnt. Caller must hold sc->node_lock */
-struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr)
-{
- struct ath_node *an = NULL, *an_found = NULL;
-
- if (list_empty(&sc->node_list))
- return NULL;
-
- list_for_each_entry(an, &sc->node_list, list)
- if (!compare_ether_addr(an->an_addr, addr)) {
- an_found = an;
- break;
- }
-
- return an_found;
-}
-
-/*
- * Set up New Node
- *
- * Setup driver-specific state for a newly associated node. This routine
- * really only applies if compression or XR are enabled, there is no code
- * covering any other cases.
-*/
-
-void ath_newassoc(struct ath_softc *sc,
- struct ath_node *an, int isnew, int isuapsd)
-{
- int tidno;
-
- /* if station reassociates, tear down the aggregation state. */
- if (!isnew) {
- for (tidno = 0; tidno < WME_NUM_TID; tidno++) {
- if (sc->sc_flags & SC_OP_TXAGGR)
- ath_tx_aggr_teardown(sc, an, tidno);
- if (sc->sc_flags & SC_OP_RXAGGR)
- ath_rx_aggr_teardown(sc, an, tidno);
- }
- }
- an->an_flags = 0;
-}
-
-/**************/
-/* Encryption */
-/**************/
-
-void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot)
-{
- ath9k_hw_keyreset(sc->sc_ah, keyix);
- if (freeslot)
- clear_bit(keyix, sc->sc_keymap);
-}
-
-int ath_keyset(struct ath_softc *sc,
- u16 keyix,
- struct ath9k_keyval *hk,
- const u8 mac[ETH_ALEN])
-{
- bool status;
-
- status = ath9k_hw_set_keycache_entry(sc->sc_ah,
- keyix, hk, mac, false);
-
- return status != false;
-}
-
-/***********************/
-/* TX Power/Regulatory */
-/***********************/
-
-/*
- * Set Transmit power in HAL
- *
- * This routine makes the actual HAL calls to set the new transmit power
- * limit.
-*/
-
-void ath_update_txpow(struct ath_softc *sc)
-{
- struct ath_hal *ah = sc->sc_ah;
- u32 txpow;
-
- if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
- ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
- /* read back in case value is clamped */
- ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
- sc->sc_curtxpow = txpow;
- }
-}
-
-/* Return the current country and domain information */
-void ath_get_currentCountry(struct ath_softc *sc,
- struct ath9k_country_entry *ctry)
-{
- ath9k_regd_get_current_country(sc->sc_ah, ctry);
-
- /* If HAL not specific yet, since it is band dependent,
- * use the one we passed in. */
- if (ctry->countryCode == CTRY_DEFAULT) {
- ctry->iso[0] = 0;
- ctry->iso[1] = 0;
- } else if (ctry->iso[0] && ctry->iso[1]) {
- if (!ctry->iso[2]) {
- if (ath_outdoor)
- ctry->iso[2] = 'O';
- else
- ctry->iso[2] = 'I';
- }
- }
-}
-
-/**************************/
-/* Slow Antenna Diversity */
-/**************************/
-
-void ath_slow_ant_div_init(struct ath_antdiv *antdiv,
- struct ath_softc *sc,
- int32_t rssitrig)
-{
- int trig;
-
- /* antdivf_rssitrig can range from 40 - 0xff */
- trig = (rssitrig > 0xff) ? 0xff : rssitrig;
- trig = (rssitrig < 40) ? 40 : rssitrig;
-
- antdiv->antdiv_sc = sc;
- antdiv->antdivf_rssitrig = trig;
-}
-
-void ath_slow_ant_div_start(struct ath_antdiv *antdiv,
- u8 num_antcfg,
- const u8 *bssid)
-{
- antdiv->antdiv_num_antcfg =
- num_antcfg < ATH_ANT_DIV_MAX_CFG ?
- num_antcfg : ATH_ANT_DIV_MAX_CFG;
- antdiv->antdiv_state = ATH_ANT_DIV_IDLE;
- antdiv->antdiv_curcfg = 0;
- antdiv->antdiv_bestcfg = 0;
- antdiv->antdiv_laststatetsf = 0;
-
- memcpy(antdiv->antdiv_bssid, bssid, sizeof(antdiv->antdiv_bssid));
-
- antdiv->antdiv_start = 1;
-}
-
-void ath_slow_ant_div_stop(struct ath_antdiv *antdiv)
-{
- antdiv->antdiv_start = 0;
-}
-
-static int32_t ath_find_max_val(int32_t *val,
- u8 num_val, u8 *max_index)
-{
- u32 MaxVal = *val++;
- u32 cur_index = 0;
-
- *max_index = 0;
- while (++cur_index < num_val) {
- if (*val > MaxVal) {
- MaxVal = *val;
- *max_index = cur_index;
- }
-
- val++;
- }
-
- return MaxVal;
-}
-
-void ath_slow_ant_div(struct ath_antdiv *antdiv,
- struct ieee80211_hdr *hdr,
- struct ath_rx_status *rx_stats)
-{
- struct ath_softc *sc = antdiv->antdiv_sc;
- struct ath_hal *ah = sc->sc_ah;
- u64 curtsf = 0;
- u8 bestcfg, curcfg = antdiv->antdiv_curcfg;
- __le16 fc = hdr->frame_control;
-
- if (antdiv->antdiv_start && ieee80211_is_beacon(fc)
- && !compare_ether_addr(hdr->addr3, antdiv->antdiv_bssid)) {
- antdiv->antdiv_lastbrssi[curcfg] = rx_stats->rs_rssi;
- antdiv->antdiv_lastbtsf[curcfg] = ath9k_hw_gettsf64(sc->sc_ah);
- curtsf = antdiv->antdiv_lastbtsf[curcfg];
- } else {
- return;
- }
-
- switch (antdiv->antdiv_state) {
- case ATH_ANT_DIV_IDLE:
- if ((antdiv->antdiv_lastbrssi[curcfg] <
- antdiv->antdivf_rssitrig)
- && ((curtsf - antdiv->antdiv_laststatetsf) >
- ATH_ANT_DIV_MIN_IDLE_US)) {
-
- curcfg++;
- if (curcfg == antdiv->antdiv_num_antcfg)
- curcfg = 0;
-
- if (!ath9k_hw_select_antconfig(ah, curcfg)) {
- antdiv->antdiv_bestcfg = antdiv->antdiv_curcfg;
- antdiv->antdiv_curcfg = curcfg;
- antdiv->antdiv_laststatetsf = curtsf;
- antdiv->antdiv_state = ATH_ANT_DIV_SCAN;
- }
- }
- break;
-
- case ATH_ANT_DIV_SCAN:
- if ((curtsf - antdiv->antdiv_laststatetsf) <
- ATH_ANT_DIV_MIN_SCAN_US)
- break;
-
- curcfg++;
- if (curcfg == antdiv->antdiv_num_antcfg)
- curcfg = 0;
-
- if (curcfg == antdiv->antdiv_bestcfg) {
- ath_find_max_val(antdiv->antdiv_lastbrssi,
- antdiv->antdiv_num_antcfg, &bestcfg);
- if (!ath9k_hw_select_antconfig(ah, bestcfg)) {
- antdiv->antdiv_bestcfg = bestcfg;
- antdiv->antdiv_curcfg = bestcfg;
- antdiv->antdiv_laststatetsf = curtsf;
- antdiv->antdiv_state = ATH_ANT_DIV_IDLE;
- }
- } else {
- if (!ath9k_hw_select_antconfig(ah, curcfg)) {
- antdiv->antdiv_curcfg = curcfg;
- antdiv->antdiv_laststatetsf = curtsf;
- antdiv->antdiv_state = ATH_ANT_DIV_SCAN;
- }
- }
-
- break;
- }
-}
-
-/***********************/
-/* Descriptor Handling */
-/***********************/
-
-/*
- * Set up DMA descriptors
- *
- * This function will allocate both the DMA descriptor structure, and the
- * buffers it contains. These are used to contain the descriptors used
- * by the system.
-*/
-
-int ath_descdma_setup(struct ath_softc *sc,
- struct ath_descdma *dd,
- struct list_head *head,
- const char *name,
- int nbuf,
- int ndesc)
-{
-#define DS2PHYS(_dd, _ds) \
- ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
-#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
-#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
-
- struct ath_desc *ds;
- struct ath_buf *bf;
- int i, bsize, error;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA: %u buffers %u desc/buf\n",
- __func__, name, nbuf, ndesc);
-
- /* ath_desc must be a multiple of DWORDs */
- if ((sizeof(struct ath_desc) % 4) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: ath_desc not DWORD aligned\n",
- __func__);
- ASSERT((sizeof(struct ath_desc) % 4) == 0);
- error = -ENOMEM;
- goto fail;
- }
-
- dd->dd_name = name;
- dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
-
- /*
- * Need additional DMA memory because we can't use
- * descriptors that cross the 4K page boundary. Assume
- * one skipped descriptor per 4K page.
- */
- if (!(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
- u32 ndesc_skipped =
- ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
- u32 dma_len;
-
- while (ndesc_skipped) {
- dma_len = ndesc_skipped * sizeof(struct ath_desc);
- dd->dd_desc_len += dma_len;
-
- ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
- };
- }
-
- /* allocate descriptors */
- dd->dd_desc = pci_alloc_consistent(sc->pdev,
- dd->dd_desc_len,
- &dd->dd_desc_paddr);
- if (dd->dd_desc == NULL) {
- error = -ENOMEM;
- goto fail;
- }
- ds = dd->dd_desc;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA map: %p (%u) -> %llx (%u)\n",
- __func__, dd->dd_name, ds, (u32) dd->dd_desc_len,
- ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
-
- /* allocate buffers */
- bsize = sizeof(struct ath_buf) * nbuf;
- bf = kmalloc(bsize, GFP_KERNEL);
- if (bf == NULL) {
- error = -ENOMEM;
- goto fail2;
- }
- memset(bf, 0, bsize);
- dd->dd_bufptr = bf;
-
- INIT_LIST_HEAD(head);
- for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
- bf->bf_desc = ds;
- bf->bf_daddr = DS2PHYS(dd, ds);
-
- if (!(sc->sc_ah->ah_caps.hw_caps &
- ATH9K_HW_CAP_4KB_SPLITTRANS)) {
- /*
- * Skip descriptor addresses which can cause 4KB
- * boundary crossing (addr + length) with a 32 dword
- * descriptor fetch.
- */
- while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
- ASSERT((caddr_t) bf->bf_desc <
- ((caddr_t) dd->dd_desc +
- dd->dd_desc_len));
-
- ds += ndesc;
- bf->bf_desc = ds;
- bf->bf_daddr = DS2PHYS(dd, ds);
- }
- }
- list_add_tail(&bf->list, head);
- }
- return 0;
-fail2:
- pci_free_consistent(sc->pdev,
- dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
-fail:
- memset(dd, 0, sizeof(*dd));
- return error;
-#undef ATH_DESC_4KB_BOUND_CHECK
-#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
-#undef DS2PHYS
-}
-
-/*
- * Cleanup DMA descriptors
- *
- * This function will free the DMA block that was allocated for the descriptor
- * pool. Since this was allocated as one "chunk", it is freed in the same
- * manner.
-*/
-
-void ath_descdma_cleanup(struct ath_softc *sc,
- struct ath_descdma *dd,
- struct list_head *head)
-{
- /* Free memory associated with descriptors */
- pci_free_consistent(sc->pdev,
- dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
-
- INIT_LIST_HEAD(head);
- kfree(dd->dd_bufptr);
- memset(dd, 0, sizeof(*dd));
-}
-
-/*************/
-/* Utilities */
-/*************/
-
-int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
-{
- int qnum;
-
- switch (queue) {
- case 0:
- qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO];
- break;
- case 1:
- qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI];
- break;
- case 2:
- qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
- break;
- case 3:
- qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK];
- break;
- default:
- qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
- break;
- }
-
- return qnum;
-}
-
-int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
-{
- int qnum;
-
- switch (queue) {
- case ATH9K_WME_AC_VO:
- qnum = 0;
- break;
- case ATH9K_WME_AC_VI:
- qnum = 1;
- break;
- case ATH9K_WME_AC_BE:
- qnum = 2;
- break;
- case ATH9K_WME_AC_BK:
- qnum = 3;
- break;
- default:
- qnum = -1;
- break;
- }
-
- return qnum;
-}
-
-
-/*
- * Expand time stamp to TSF
- *
- * Extend 15-bit time stamp from rx descriptor to
- * a full 64-bit TSF using the current h/w TSF.
-*/
-
-u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp)
-{
- u64 tsf;
-
- tsf = ath9k_hw_gettsf64(sc->sc_ah);
- if ((tsf & 0x7fff) < rstamp)
- tsf -= 0x8000;
- return (tsf & ~0x7fff) | rstamp;
-}
-
-/*
- * Set Default Antenna
- *
- * Call into the HAL to set the default antenna to use. Not really valid for
- * MIMO technology.
-*/
-
-void ath_setdefantenna(void *context, u32 antenna)
-{
- struct ath_softc *sc = (struct ath_softc *)context;
- struct ath_hal *ah = sc->sc_ah;
-
- /* XXX block beacon interrupts */
- ath9k_hw_setantenna(ah, antenna);
- sc->sc_defant = antenna;
- sc->sc_rxotherant = 0;
-}
-
-/*
- * Set Slot Time
- *
- * This will wake up the chip if required, and set the slot time for the
- * frame (maximum transmit time). Slot time is assumed to be already set
- * in the ATH object member sc_slottime
-*/
-
-void ath_setslottime(struct ath_softc *sc)
-{
- ath9k_hw_setslottime(sc->sc_ah, sc->sc_slottime);
- sc->sc_updateslot = OK;
-}
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h
index cb3e61e57c4d..4ca2aed236e0 100644
--- a/drivers/net/wireless/ath9k/core.h
+++ b/drivers/net/wireless/ath9k/core.h
@@ -17,27 +17,8 @@
#ifndef CORE_H
#define CORE_H
-#include <linux/version.h>
-#include <linux/autoconf.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/wait.h>
#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <asm/byteorder.h>
-#include <linux/scatterlist.h>
-#include <asm/page.h>
#include <net/mac80211.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
@@ -47,10 +28,6 @@
struct ath_node;
-/******************/
-/* Utility macros */
-/******************/
-
/* Macro to expand scalars to 64-bit objects */
#define ito64(x) (sizeof(x) == 8) ? \
@@ -84,94 +61,125 @@ struct ath_node;
#define TSF_TO_TU(_h,_l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
-#define ATH9K_BH_STATUS_INTACT 0
-#define ATH9K_BH_STATUS_CHANGE 1
-
-#define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<<i))
-
-static inline unsigned long get_timestamp(void)
-{
- return ((jiffies / HZ) * 1000) + (jiffies % HZ) * (1000 / HZ);
-}
+#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i))
static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-/*************/
-/* Debugging */
-/*************/
-
enum ATH_DEBUG {
ATH_DBG_RESET = 0x00000001,
- ATH_DBG_PHY_IO = 0x00000002,
- ATH_DBG_REG_IO = 0x00000004,
- ATH_DBG_QUEUE = 0x00000008,
- ATH_DBG_EEPROM = 0x00000010,
- ATH_DBG_NF_CAL = 0x00000020,
- ATH_DBG_CALIBRATE = 0x00000040,
- ATH_DBG_CHANNEL = 0x00000080,
- ATH_DBG_INTERRUPT = 0x00000100,
- ATH_DBG_REGULATORY = 0x00000200,
- ATH_DBG_ANI = 0x00000400,
- ATH_DBG_POWER_MGMT = 0x00000800,
- ATH_DBG_XMIT = 0x00001000,
- ATH_DBG_BEACON = 0x00002000,
- ATH_DBG_RATE = 0x00004000,
- ATH_DBG_CONFIG = 0x00008000,
- ATH_DBG_KEYCACHE = 0x00010000,
- ATH_DBG_AGGR = 0x00020000,
- ATH_DBG_FATAL = 0x00040000,
+ ATH_DBG_REG_IO = 0x00000002,
+ ATH_DBG_QUEUE = 0x00000004,
+ ATH_DBG_EEPROM = 0x00000008,
+ ATH_DBG_CALIBRATE = 0x00000010,
+ ATH_DBG_CHANNEL = 0x00000020,
+ ATH_DBG_INTERRUPT = 0x00000040,
+ ATH_DBG_REGULATORY = 0x00000080,
+ ATH_DBG_ANI = 0x00000100,
+ ATH_DBG_POWER_MGMT = 0x00000200,
+ ATH_DBG_XMIT = 0x00000400,
+ ATH_DBG_BEACON = 0x00001000,
+ ATH_DBG_CONFIG = 0x00002000,
+ ATH_DBG_KEYCACHE = 0x00004000,
+ ATH_DBG_FATAL = 0x00008000,
ATH_DBG_ANY = 0xffffffff
};
#define DBG_DEFAULT (ATH_DBG_FATAL)
-#define DPRINTF(sc, _m, _fmt, ...) do { \
- if (sc->sc_debug & (_m)) \
- printk(_fmt , ##__VA_ARGS__); \
- } while (0)
+#ifdef CONFIG_ATH9K_DEBUG
+
+/**
+ * struct ath_interrupt_stats - Contains statistics about interrupts
+ * @total: Total no. of interrupts generated so far
+ * @rxok: RX with no errors
+ * @rxeol: RX with no more RXDESC available
+ * @rxorn: RX FIFO overrun
+ * @txok: TX completed at the requested rate
+ * @txurn: TX FIFO underrun
+ * @mib: MIB regs reaching its threshold
+ * @rxphyerr: RX with phy errors
+ * @rx_keycache_miss: RX with key cache misses
+ * @swba: Software Beacon Alert
+ * @bmiss: Beacon Miss
+ * @bnr: Beacon Not Ready
+ * @cst: Carrier Sense TImeout
+ * @gtt: Global TX Timeout
+ * @tim: RX beacon TIM occurrence
+ * @cabend: RX End of CAB traffic
+ * @dtimsync: DTIM sync lossage
+ * @dtim: RX Beacon with DTIM
+ */
+struct ath_interrupt_stats {
+ u32 total;
+ u32 rxok;
+ u32 rxeol;
+ u32 rxorn;
+ u32 txok;
+ u32 txeol;
+ u32 txurn;
+ u32 mib;
+ u32 rxphyerr;
+ u32 rx_keycache_miss;
+ u32 swba;
+ u32 bmiss;
+ u32 bnr;
+ u32 cst;
+ u32 gtt;
+ u32 tim;
+ u32 cabend;
+ u32 dtimsync;
+ u32 dtim;
+};
+
+struct ath_stats {
+ struct ath_interrupt_stats istats;
+};
+
+struct ath9k_debug {
+ int debug_mask;
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_phy;
+ struct dentry *debugfs_dma;
+ struct dentry *debugfs_interrupt;
+ struct ath_stats stats;
+};
+
+void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...);
+int ath9k_init_debug(struct ath_softc *sc);
+void ath9k_exit_debug(struct ath_softc *sc);
+void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
+
+#else
+
+static inline void DPRINTF(struct ath_softc *sc, int dbg_mask,
+ const char *fmt, ...)
+{
+}
+
+static inline int ath9k_init_debug(struct ath_softc *sc)
+{
+ return 0;
+}
-/***************************/
-/* Load-time Configuration */
-/***************************/
+static inline void ath9k_exit_debug(struct ath_softc *sc)
+{
+}
+
+static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
+ enum ath9k_int status)
+{
+}
+
+#endif /* CONFIG_ATH9K_DEBUG */
-/* Per-instance load-time (note: NOT run-time) configurations
- * for Atheros Device */
struct ath_config {
u32 ath_aggr_prot;
u16 txpowlimit;
u16 txpowlimit_override;
- u8 cabqReadytime; /* Cabq Readytime % */
- u8 swBeaconProcess; /* Process received beacons in SW (vs HW) */
-};
-
-/***********************/
-/* Chainmask Selection */
-/***********************/
-
-#define ATH_CHAINMASK_SEL_TIMEOUT 6000
-/* Default - Number of last RSSI values that is used for
- * chainmask selection */
-#define ATH_CHAINMASK_SEL_RSSI_CNT 10
-/* Means use 3x3 chainmask instead of configured chainmask */
-#define ATH_CHAINMASK_SEL_3X3 7
-/* Default - Rssi threshold below which we have to switch to 3x3 */
-#define ATH_CHAINMASK_SEL_UP_RSSI_THRES 20
-/* Default - Rssi threshold above which we have to switch to
- * user configured values */
-#define ATH_CHAINMASK_SEL_DOWN_RSSI_THRES 35
-/* Struct to store the chainmask select related info */
-struct ath_chainmask_sel {
- struct timer_list timer;
- int cur_tx_mask; /* user configured or 3x3 */
- int cur_rx_mask; /* user configured or 3x3 */
- int tx_avgrssi;
- u8 switch_allowed:1, /* timer will set this */
- cm_sel_enabled : 1;
+ u8 cabqReadytime;
+ u8 swBeaconProcess;
};
-int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an);
-void ath_update_chainmask(struct ath_softc *sc, int is_ht);
-
/*************************/
/* Descriptor Management */
/*************************/
@@ -200,15 +208,14 @@ enum buffer_type {
};
struct ath_buf_state {
- int bfs_nframes; /* # frames in aggregate */
- u16 bfs_al; /* length of aggregate */
- u16 bfs_frmlen; /* length of frame */
- int bfs_seqno; /* sequence number */
- int bfs_tidno; /* tid of this frame */
- int bfs_retries; /* current retries */
- struct ath_rc_series bfs_rcs[4]; /* rate series */
- u32 bf_type; /* BUF_* (enum buffer_type) */
- /* key type use to encrypt this frame */
+ int bfs_nframes; /* # frames in aggregate */
+ u16 bfs_al; /* length of aggregate */
+ u16 bfs_frmlen; /* length of frame */
+ int bfs_seqno; /* sequence number */
+ int bfs_tidno; /* tid of this frame */
+ int bfs_retries; /* current retries */
+ u32 bf_type; /* BUF_* (enum buffer_type) */
+ u32 bfs_keyix;
enum ath9k_key_type bfs_keytype;
};
@@ -219,6 +226,7 @@ struct ath_buf_state {
#define bf_seqno bf_state.bfs_seqno
#define bf_tidno bf_state.bfs_tidno
#define bf_rcs bf_state.bfs_rcs
+#define bf_keyix bf_state.bfs_keyix
#define bf_keytype bf_state.bfs_keytype
#define bf_isdata(bf) (bf->bf_state.bf_type & BUF_DATA)
#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR)
@@ -242,9 +250,7 @@ struct ath_buf {
an aggregate) */
struct ath_buf *bf_lastfrm; /* last buf of this frame */
struct ath_buf *bf_next; /* next subframe in the aggregate */
- struct ath_buf *bf_rifslast; /* last buf for RIFS burst */
void *bf_mpdu; /* enclosing frame structure */
- void *bf_node; /* pointer to the node */
struct ath_desc *bf_desc; /* virtual addr of desc */
dma_addr_t bf_daddr; /* physical addr of desc */
dma_addr_t bf_buf_addr; /* physical addr of data buffer */
@@ -254,13 +260,6 @@ struct ath_buf {
dma_addr_t bf_dmacontext;
};
-/*
- * reset the rx buffer.
- * any new fields added to the athbuf and require
- * reset need to be added to this macro.
- * currently bf_status is the only one requires that
- * requires reset.
- */
#define ATH_RXBUF_RESET(_bf) ((_bf)->bf_status = 0)
/* hw processing complete, desc processed by hal */
@@ -281,159 +280,81 @@ struct ath_descdma {
dma_addr_t dd_dmacontext;
};
-/* Abstraction of a received RX MPDU/MMPDU, or a RX fragment */
-
-struct ath_rx_context {
- struct ath_buf *ctx_rxbuf; /* associated ath_buf for rx */
-};
-#define ATH_RX_CONTEXT(skb) ((struct ath_rx_context *)skb->cb)
-
-int ath_descdma_setup(struct ath_softc *sc,
- struct ath_descdma *dd,
- struct list_head *head,
- const char *name,
- int nbuf,
- int ndesc);
-int ath_desc_alloc(struct ath_softc *sc);
-void ath_desc_free(struct ath_softc *sc);
-void ath_descdma_cleanup(struct ath_softc *sc,
- struct ath_descdma *dd,
+int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
+ struct list_head *head, const char *name,
+ int nbuf, int ndesc);
+void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
struct list_head *head);
-/******/
-/* RX */
-/******/
-
-#define ATH_MAX_ANTENNA 3
-#define ATH_RXBUF 512
-#define ATH_RX_TIMEOUT 40 /* 40 milliseconds */
-#define WME_NUM_TID 16
-#define IEEE80211_BAR_CTL_TID_M 0xF000 /* tid mask */
-#define IEEE80211_BAR_CTL_TID_S 12 /* tid shift */
-
-enum ATH_RX_TYPE {
- ATH_RX_NON_CONSUMED = 0,
- ATH_RX_CONSUMED
-};
-
-/* per frame rx status block */
-struct ath_recv_status {
- u64 tsf; /* mac tsf */
- int8_t rssi; /* RSSI (noise floor ajusted) */
- int8_t rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */
- int8_t rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */
- int8_t abs_rssi; /* absolute RSSI */
- u8 rateieee; /* data rate received (IEEE rate code) */
- u8 ratecode; /* phy rate code */
- int rateKbps; /* data rate received (Kbps) */
- int antenna; /* rx antenna */
- int flags; /* status of associated skb */
-#define ATH_RX_FCS_ERROR 0x01
-#define ATH_RX_MIC_ERROR 0x02
-#define ATH_RX_DECRYPT_ERROR 0x04
-#define ATH_RX_RSSI_VALID 0x08
-/* if any of ctl,extn chainrssis are valid */
-#define ATH_RX_CHAIN_RSSI_VALID 0x10
-/* if extn chain rssis are valid */
-#define ATH_RX_RSSI_EXTN_VALID 0x20
-/* set if 40Mhz, clear if 20Mhz */
-#define ATH_RX_40MHZ 0x40
-/* set if short GI, clear if full GI */
-#define ATH_RX_SHORT_GI 0x80
-};
-
-struct ath_rxbuf {
- struct sk_buff *rx_wbuf;
- unsigned long rx_time; /* system time when received */
- struct ath_recv_status rx_status; /* cached rx status */
-};
-
-/* Per-TID aggregate receiver state for a node */
-struct ath_arx_tid {
- struct ath_node *an;
- struct ath_rxbuf *rxbuf; /* re-ordering buffer */
- struct timer_list timer;
- spinlock_t tidlock;
- int baw_head; /* seq_next at head */
- int baw_tail; /* tail of block-ack window */
- int seq_reset; /* need to reset start sequence */
- int addba_exchangecomplete;
- u16 seq_next; /* next expected sequence */
- u16 baw_size; /* block-ack window size */
-};
-
-/* Per-node receiver aggregate state */
-struct ath_arx {
- struct ath_arx_tid tid[WME_NUM_TID];
-};
-
-int ath_startrecv(struct ath_softc *sc);
-bool ath_stoprecv(struct ath_softc *sc);
-void ath_flushrecv(struct ath_softc *sc);
-u32 ath_calcrxfilter(struct ath_softc *sc);
-void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an);
-void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an);
-void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
-void ath_handle_rx_intr(struct ath_softc *sc);
-int ath_rx_init(struct ath_softc *sc, int nbufs);
-void ath_rx_cleanup(struct ath_softc *sc);
-int ath_rx_tasklet(struct ath_softc *sc, int flush);
-int ath_rx_input(struct ath_softc *sc,
- struct ath_node *node,
- int is_ampdu,
- struct sk_buff *skb,
- struct ath_recv_status *rx_status,
- enum ATH_RX_TYPE *status);
-int _ath_rx_indicate(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_recv_status *status,
- u16 keyix);
-int ath_rx_subframe(struct ath_node *an, struct sk_buff *skb,
- struct ath_recv_status *status);
-
-/******/
-/* TX */
-/******/
+/***********/
+/* RX / TX */
+/***********/
+#define ATH_MAX_ANTENNA 3
+#define ATH_RXBUF 512
+#define WME_NUM_TID 16
#define ATH_TXBUF 512
-/* max number of transmit attempts (tries) */
#define ATH_TXMAXTRY 13
-/* max number of 11n transmit attempts (tries) */
#define ATH_11N_TXMAXTRY 10
-/* max number of tries for management and control frames */
#define ATH_MGT_TXMAXTRY 4
#define WME_BA_BMP_SIZE 64
#define WME_MAX_BA WME_BA_BMP_SIZE
#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA)
+
#define TID_TO_WME_AC(_tid) \
((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
(((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \
(((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
WME_AC_VO)
+#define WME_AC_BE 0
+#define WME_AC_BK 1
+#define WME_AC_VI 2
+#define WME_AC_VO 3
+#define WME_NUM_AC 4
+
+#define ADDBA_EXCHANGE_ATTEMPTS 10
+#define ATH_AGGR_DELIM_SZ 4
+#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */
+/* number of delimiters for encryption padding */
+#define ATH_AGGR_ENCRYPTDELIM 10
+/* minimum h/w qdepth to be sustained to maximize aggregation */
+#define ATH_AGGR_MIN_QDEPTH 2
+#define ATH_AMPDU_SUBFRAME_DEFAULT 32
+#define IEEE80211_SEQ_SEQ_SHIFT 4
+#define IEEE80211_SEQ_MAX 4096
+#define IEEE80211_MIN_AMPDU_BUF 0x8
+#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13
+
+/* return whether a bit at index _n in bitmap _bm is set
+ * _sz is the size of the bitmap */
+#define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \
+ ((_bm)[(_n) >> 5] & (1 << ((_n) & 31))))
+
+/* return block-ack bitmap index given sequence and starting sequence */
+#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1))
+
+/* returns delimiter padding required given the packet length */
+#define ATH_AGGR_GET_NDELIM(_len) \
+ (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \
+ (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
+
+#define BAW_WITHIN(_start, _bawsz, _seqno) \
+ ((((_seqno) - (_start)) & 4095) < (_bawsz))
-/* Wireless Multimedia Extension Defines */
-#define WME_AC_BE 0 /* best effort */
-#define WME_AC_BK 1 /* background */
-#define WME_AC_VI 2 /* video */
-#define WME_AC_VO 3 /* voice */
-#define WME_NUM_AC 4
+#define ATH_DS_BA_SEQ(_ds) ((_ds)->ds_us.tx.ts_seqnum)
+#define ATH_DS_BA_BITMAP(_ds) (&(_ds)->ds_us.tx.ba_low)
+#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA)
+#define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)])
-enum ATH_SM_PWRSAV{
- ATH_SM_ENABLE,
- ATH_SM_PWRSAV_STATIC,
- ATH_SM_PWRSAV_DYNAMIC,
+enum ATH_AGGR_STATUS {
+ ATH_AGGR_DONE,
+ ATH_AGGR_BAW_CLOSED,
+ ATH_AGGR_LIMITED,
+ ATH_AGGR_SHORTPKT,
+ ATH_AGGR_8K_LIMITED,
};
-/*
- * Data transmit queue state. One of these exists for each
- * hardware transmit queue. Packets sent to us from above
- * are assigned to queues based on their priority. Not all
- * devices support a complete set of hardware transmit queues.
- * For those devices the array sc_ac2q will map multiple
- * priorities to fewer hardware queues (typically all to one
- * hardware queue).
- */
struct ath_txq {
u32 axq_qnum; /* hardware q number */
u32 *axq_link; /* link ptr in last TX desc */
@@ -443,10 +364,6 @@ struct ath_txq {
u32 axq_depth; /* queue depth */
u8 axq_aggr_depth; /* aggregates queued */
u32 axq_totalqueued; /* total ever queued */
-
- /* count to determine if descriptor should generate int on this txq. */
- u32 axq_intrcnt;
-
bool stopped; /* Is mac80211 queue stopped ? */
struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/
@@ -460,6 +377,10 @@ struct ath_txq {
struct list_head axq_acq;
};
+#define AGGR_CLEANUP BIT(1)
+#define AGGR_ADDBA_COMPLETE BIT(2)
+#define AGGR_ADDBA_PROGRESS BIT(3)
+
/* per TID aggregate tx state for a destination */
struct ath_atx_tid {
struct list_head list; /* round-robin tid entry */
@@ -475,9 +396,7 @@ struct ath_atx_tid {
int baw_tail; /* next unused tx buffer slot */
int sched;
int paused;
- int cleanup_inprogress;
- u32 addba_exchangecomplete:1;
- int32_t addba_exchangeinprogress;
+ u8 state;
int addba_exchangeattempts;
};
@@ -490,32 +409,10 @@ struct ath_atx_ac {
struct list_head tid_q; /* queue of TIDs with buffers */
};
-/* per dest tx state */
-struct ath_atx {
- struct ath_atx_tid tid[WME_NUM_TID];
- struct ath_atx_ac ac[WME_NUM_AC];
-};
-
/* per-frame tx control block */
struct ath_tx_control {
- struct ath_node *an;
+ struct ath_txq *txq;
int if_id;
- int qnum;
- u32 ht:1;
- u32 ps:1;
- u32 use_minrate:1;
- enum ath9k_pkt_type atype;
- enum ath9k_key_type keytype;
- u32 flags;
- u16 seqno;
- u16 tidno;
- u16 txpower;
- u16 frmlen;
- u32 keyix;
- int min_rate;
- int mcast_rate;
- struct ath_softc *dev;
- dma_addr_t dmacontext;
};
/* per frame tx status block */
@@ -528,21 +425,63 @@ struct ath_xmit_status {
#define ATH_TX_BAR 0x04
};
+/* All RSSI values are noise floor adjusted */
struct ath_tx_stat {
- int rssi; /* RSSI (noise floor ajusted) */
- int rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */
- int rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */
- int rateieee; /* data rate xmitted (IEEE rate code) */
- int rateKbps; /* data rate xmitted (Kbps) */
- int ratecode; /* phy rate code */
- int flags; /* validity flags */
-/* if any of ctl,extn chain rssis are valid */
-#define ATH_TX_CHAIN_RSSI_VALID 0x01
-/* if extn chain rssis are valid */
-#define ATH_TX_RSSI_EXTN_VALID 0x02
+ int rssi;
+ int rssictl[ATH_MAX_ANTENNA];
+ int rssiextn[ATH_MAX_ANTENNA];
+ int rateieee;
+ int rateKbps;
+ int ratecode;
+ int flags;
u32 airtime; /* time on air per final tx rate */
};
+struct aggr_rifs_param {
+ int param_max_frames;
+ int param_max_len;
+ int param_rl;
+ int param_al;
+ struct ath_rc_series *param_rcs;
+};
+
+struct ath_node {
+ struct ath_softc *an_sc;
+ struct ath_atx_tid tid[WME_NUM_TID];
+ struct ath_atx_ac ac[WME_NUM_AC];
+ u16 maxampdu;
+ u8 mpdudensity;
+};
+
+struct ath_tx {
+ u16 seq_no;
+ u32 txqsetup;
+ int hwq_map[ATH9K_WME_AC_VO+1];
+ spinlock_t txbuflock;
+ struct list_head txbuf;
+ struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
+ struct ath_descdma txdma;
+};
+
+struct ath_rx {
+ u8 defant;
+ u8 rxotherant;
+ u32 *rxlink;
+ int bufsize;
+ unsigned int rxfilter;
+ spinlock_t rxflushlock;
+ spinlock_t rxbuflock;
+ struct list_head rxbuf;
+ struct ath_descdma rxdma;
+};
+
+int ath_startrecv(struct ath_softc *sc);
+bool ath_stoprecv(struct ath_softc *sc);
+void ath_flushrecv(struct ath_softc *sc);
+u32 ath_calcrxfilter(struct ath_softc *sc);
+int ath_rx_init(struct ath_softc *sc, int nbufs);
+void ath_rx_cleanup(struct ath_softc *sc);
+int ath_rx_tasklet(struct ath_softc *sc, int flush);
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
int ath_tx_setup(struct ath_softc *sc, int haltype);
@@ -550,139 +489,51 @@ void ath_draintxq(struct ath_softc *sc, bool retry_tx);
void ath_tx_draintxq(struct ath_softc *sc,
struct ath_txq *txq, bool retry_tx);
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
-void ath_tx_node_cleanup(struct ath_softc *sc,
- struct ath_node *an, bool bh_flag);
+void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an);
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
int ath_tx_init(struct ath_softc *sc, int nbufs);
int ath_tx_cleanup(struct ath_softc *sc);
int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype);
+struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb);
int ath_txq_update(struct ath_softc *sc, int qnum,
struct ath9k_tx_queue_info *q);
-int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb);
+int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
+ struct ath_tx_control *txctl);
void ath_tx_tasklet(struct ath_softc *sc);
u32 ath_txq_depth(struct ath_softc *sc, int qnum);
u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum);
-void ath_notify_txq_status(struct ath_softc *sc, u16 queue_depth);
-void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
- struct ath_xmit_status *tx_status, struct ath_node *an);
void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb);
+void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid);
+bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
+void ath_tx_aggr_teardown(struct ath_softc *sc, struct ath_node *an, u8 tidno);
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+ u16 tid, u16 *ssn);
+int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
-/**********************/
-/* Node / Aggregation */
-/**********************/
-
-/* indicates the node is clened up */
-#define ATH_NODE_CLEAN 0x1
-/* indicates the node is 80211 power save */
-#define ATH_NODE_PWRSAVE 0x2
-
-#define ADDBA_EXCHANGE_ATTEMPTS 10
-#define ATH_AGGR_DELIM_SZ 4 /* delimiter size */
-#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */
-/* number of delimiters for encryption padding */
-#define ATH_AGGR_ENCRYPTDELIM 10
-/* minimum h/w qdepth to be sustained to maximize aggregation */
-#define ATH_AGGR_MIN_QDEPTH 2
-#define ATH_AMPDU_SUBFRAME_DEFAULT 32
-#define IEEE80211_SEQ_SEQ_SHIFT 4
-#define IEEE80211_SEQ_MAX 4096
-#define IEEE80211_MIN_AMPDU_BUF 0x8
-
-/* return whether a bit at index _n in bitmap _bm is set
- * _sz is the size of the bitmap */
-#define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \
- ((_bm)[(_n) >> 5] & (1 << ((_n) & 31))))
-
-/* return block-ack bitmap index given sequence and starting sequence */
-#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1))
-
-/* returns delimiter padding required given the packet length */
-#define ATH_AGGR_GET_NDELIM(_len) \
- (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \
- (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
-
-#define BAW_WITHIN(_start, _bawsz, _seqno) \
- ((((_seqno) - (_start)) & 4095) < (_bawsz))
-
-#define ATH_DS_BA_SEQ(_ds) ((_ds)->ds_us.tx.ts_seqnum)
-#define ATH_DS_BA_BITMAP(_ds) (&(_ds)->ds_us.tx.ba_low)
-#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA)
-#define ATH_AN_2_TID(_an, _tidno) (&(_an)->an_aggr.tx.tid[(_tidno)])
-
-enum ATH_AGGR_STATUS {
- ATH_AGGR_DONE,
- ATH_AGGR_BAW_CLOSED,
- ATH_AGGR_LIMITED,
- ATH_AGGR_SHORTPKT,
- ATH_AGGR_8K_LIMITED,
-};
-
-enum ATH_AGGR_CHECK {
- AGGR_NOT_REQUIRED,
- AGGR_REQUIRED,
- AGGR_CLEANUP_PROGRESS,
- AGGR_EXCHANGE_PROGRESS,
- AGGR_EXCHANGE_DONE
-};
+/********/
+/* VAPs */
+/********/
-struct aggr_rifs_param {
- int param_max_frames;
- int param_max_len;
- int param_rl;
- int param_al;
- struct ath_rc_series *param_rcs;
-};
+/*
+ * Define the scheme that we select MAC address for multiple
+ * BSS on the same radio. The very first VAP will just use the MAC
+ * address from the EEPROM. For the next 3 VAPs, we set the
+ * U/L bit (bit 1) in MAC address, and use the next two bits as the
+ * index of the VAP.
+ */
-/* Per-node aggregation state */
-struct ath_node_aggr {
- struct ath_atx tx; /* node transmit state */
- struct ath_arx rx; /* node receive state */
-};
+#define ATH_SET_VAP_BSSID_MASK(bssid_mask) \
+ ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
-/* driver-specific node state */
-struct ath_node {
- struct list_head list;
- struct ath_softc *an_sc;
- atomic_t an_refcnt;
- struct ath_chainmask_sel an_chainmask_sel;
- struct ath_node_aggr an_aggr;
- u8 an_smmode; /* SM Power save mode */
- u8 an_flags;
- u8 an_addr[ETH_ALEN];
+struct ath_vap {
+ int av_bslot;
+ enum nl80211_iftype av_opmode;
+ struct ath_buf *av_bcbuf;
+ struct ath_tx_control av_btxctl;
};
-void ath_tx_resume_tid(struct ath_softc *sc,
- struct ath_atx_tid *tid);
-enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc,
- struct ath_node *an, u8 tidno);
-void ath_tx_aggr_teardown(struct ath_softc *sc,
- struct ath_node *an, u8 tidno);
-void ath_rx_aggr_teardown(struct ath_softc *sc,
- struct ath_node *an, u8 tidno);
-int ath_rx_aggr_start(struct ath_softc *sc,
- const u8 *addr,
- u16 tid,
- u16 *ssn);
-int ath_rx_aggr_stop(struct ath_softc *sc,
- const u8 *addr,
- u16 tid);
-int ath_tx_aggr_start(struct ath_softc *sc,
- const u8 *addr,
- u16 tid,
- u16 *ssn);
-int ath_tx_aggr_stop(struct ath_softc *sc,
- const u8 *addr,
- u16 tid);
-void ath_newassoc(struct ath_softc *sc,
- struct ath_node *node, int isnew, int isuapsd);
-struct ath_node *ath_node_attach(struct ath_softc *sc,
- u8 addr[ETH_ALEN], int if_id);
-void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag);
-struct ath_node *ath_node_get(struct ath_softc *sc, u8 addr[ETH_ALEN]);
-void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag);
-struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr);
-
/*******************/
/* Beacon Handling */
/*******************/
@@ -693,12 +544,11 @@ struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr);
* number of beacon intervals, the game's up.
*/
#define BSTUCK_THRESH (9 * ATH_BCBUF)
-#define ATH_BCBUF 4 /* number of beacon buffers */
-#define ATH_DEFAULT_BINTVAL 100 /* default beacon interval in TU */
+#define ATH_BCBUF 1
+#define ATH_DEFAULT_BINTVAL 100 /* TU */
#define ATH_DEFAULT_BMISS_LIMIT 10
#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
-/* beacon configuration */
struct ath_beacon_config {
u16 beacon_interval;
u16 listen_interval;
@@ -712,93 +562,32 @@ struct ath_beacon_config {
} u; /* last received beacon/probe response timestamp of this BSS. */
};
+struct ath_beacon {
+ enum {
+ OK, /* no change needed */
+ UPDATE, /* update pending */
+ COMMIT /* beacon sent, commit change */
+ } updateslot; /* slot time update fsm */
+
+ u32 beaconq;
+ u32 bmisscnt;
+ u32 ast_be_xmit;
+ u64 bc_tstamp;
+ int bslot[ATH_BCBUF];
+ int slottime;
+ int slotupdate;
+ struct ath9k_tx_queue_info beacon_qi;
+ struct ath_descdma bdma;
+ struct ath_txq *cabq;
+ struct list_head bbuf;
+};
+
void ath9k_beacon_tasklet(unsigned long data);
void ath_beacon_config(struct ath_softc *sc, int if_id);
int ath_beaconq_setup(struct ath_hal *ah);
int ath_beacon_alloc(struct ath_softc *sc, int if_id);
-void ath_bstuck_process(struct ath_softc *sc);
void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp);
void ath_beacon_sync(struct ath_softc *sc, int if_id);
-void ath_get_beaconconfig(struct ath_softc *sc,
- int if_id,
- struct ath_beacon_config *conf);
-/********/
-/* VAPs */
-/********/
-
-/*
- * Define the scheme that we select MAC address for multiple
- * BSS on the same radio. The very first VAP will just use the MAC
- * address from the EEPROM. For the next 3 VAPs, we set the
- * U/L bit (bit 1) in MAC address, and use the next two bits as the
- * index of the VAP.
- */
-
-#define ATH_SET_VAP_BSSID_MASK(bssid_mask) \
- ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
-
-/* VAP configuration (from protocol layer) */
-struct ath_vap_config {
- u32 av_fixed_rateset;
- u32 av_fixed_retryset;
-};
-
-/* driver-specific vap state */
-struct ath_vap {
- struct ieee80211_vif *av_if_data;
- enum ath9k_opmode av_opmode; /* VAP operational mode */
- struct ath_buf *av_bcbuf; /* beacon buffer */
- struct ath_tx_control av_btxctl; /* txctl information for beacon */
- int av_bslot; /* beacon slot index */
- struct ath_vap_config av_config;/* vap configuration parameters*/
- struct ath_rate_node *rc_node;
-};
-
-int ath_vap_attach(struct ath_softc *sc,
- int if_id,
- struct ieee80211_vif *if_data,
- enum ath9k_opmode opmode);
-int ath_vap_detach(struct ath_softc *sc, int if_id);
-int ath_vap_config(struct ath_softc *sc,
- int if_id, struct ath_vap_config *if_config);
-
-/*********************/
-/* Antenna diversity */
-/*********************/
-
-#define ATH_ANT_DIV_MAX_CFG 2
-#define ATH_ANT_DIV_MIN_IDLE_US 1000000 /* us */
-#define ATH_ANT_DIV_MIN_SCAN_US 50000 /* us */
-
-enum ATH_ANT_DIV_STATE{
- ATH_ANT_DIV_IDLE,
- ATH_ANT_DIV_SCAN, /* evaluating antenna */
-};
-
-struct ath_antdiv {
- struct ath_softc *antdiv_sc;
- u8 antdiv_start;
- enum ATH_ANT_DIV_STATE antdiv_state;
- u8 antdiv_num_antcfg;
- u8 antdiv_curcfg;
- u8 antdiv_bestcfg;
- int32_t antdivf_rssitrig;
- int32_t antdiv_lastbrssi[ATH_ANT_DIV_MAX_CFG];
- u64 antdiv_lastbtsf[ATH_ANT_DIV_MAX_CFG];
- u64 antdiv_laststatetsf;
- u8 antdiv_bssid[ETH_ALEN];
-};
-
-void ath_slow_ant_div_init(struct ath_antdiv *antdiv,
- struct ath_softc *sc, int32_t rssitrig);
-void ath_slow_ant_div_start(struct ath_antdiv *antdiv,
- u8 num_antcfg,
- const u8 *bssid);
-void ath_slow_ant_div_stop(struct ath_antdiv *antdiv);
-void ath_slow_ant_div(struct ath_antdiv *antdiv,
- struct ieee80211_hdr *wh,
- struct ath_rx_status *rx_stats);
-void ath_setdefantenna(void *sc, u32 antenna);
/*******/
/* ANI */
@@ -863,7 +652,7 @@ struct ath_rfkill {
#define DEFAULT_CACHELINE 32
#define ATH_DEFAULT_NOISE_FLOOR -95
#define ATH_REGCLASSIDS_MAX 10
-#define ATH_CABQ_READY_TIME 80 /* % of beacon interval */
+#define ATH_CABQ_READY_TIME 80 /* % of beacon interval */
#define ATH_MAX_SW_RETRIES 10
#define ATH_CHAN_MAX 255
#define IEEE80211_WEP_NKID 4 /* number of key ids */
@@ -876,34 +665,12 @@ struct ath_rfkill {
* Different parts have different size key caches. We handle
* up to ATH_KEYMAX entries (could dynamically allocate state).
*/
-#define ATH_KEYMAX 128 /* max key cache size we handle */
+#define ATH_KEYMAX 128 /* max key cache size we handle */
#define ATH_IF_ID_ANY 0xff
#define ATH_TXPOWER_MAX 100 /* .5 dBm units */
-
-#define RSSI_LPF_THRESHOLD -20
-#define ATH_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */
-#define ATH_RATE_DUMMY_MARKER 0
-#define ATH_RSSI_LPF_LEN 10
-#define ATH_RSSI_DUMMY_MARKER 0x127
-
-#define ATH_EP_MUL(x, mul) ((x) * (mul))
-#define ATH_EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
-#define ATH_RSSI_OUT(x) \
- (((x) != ATH_RSSI_DUMMY_MARKER) ? \
- (ATH_EP_RND((x), ATH_RSSI_EP_MULTIPLIER)) : ATH_RSSI_DUMMY_MARKER)
-#define ATH_RSSI_IN(x) \
- (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER))
-#define ATH_LPF_RSSI(x, y, len) \
- ((x != ATH_RSSI_DUMMY_MARKER) ? \
- (((x) * ((len) - 1) + (y)) / (len)) : (y))
-#define ATH_RSSI_LPF(x, y) do { \
- if ((y) >= RSSI_LPF_THRESHOLD) \
- x = ATH_LPF_RSSI((x), \
- ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \
- } while (0)
-
+#define ATH_RSSI_DUMMY_MARKER 0x127
+#define ATH_RATE_DUMMY_MARKER 0
enum PROT_MODE {
PROT_M_NONE = 0,
@@ -911,19 +678,6 @@ enum PROT_MODE {
PROT_M_CTSONLY
};
-enum RATE_TYPE {
- NORMAL_RATE = 0,
- HALF_RATE,
- QUARTER_RATE
-};
-
-struct ath_ht_info {
- enum ath9k_ht_macmode tx_chan_width;
- u16 maxampdu;
- u8 mpdudensity;
- u8 ext_chan_offset;
-};
-
#define SC_OP_INVALID BIT(0)
#define SC_OP_BEACONS BIT(1)
#define SC_OP_RXAGGR BIT(2)
@@ -944,141 +698,57 @@ struct ath_softc {
struct pci_dev *pdev;
struct tasklet_struct intr_tq;
struct tasklet_struct bcon_tasklet;
- struct ath_config sc_config;
struct ath_hal *sc_ah;
- struct ath_rate_softc *sc_rc;
void __iomem *mem;
+ spinlock_t sc_resetlock;
+ struct mutex mutex;
u8 sc_curbssid[ETH_ALEN];
u8 sc_myaddr[ETH_ALEN];
u8 sc_bssidmask[ETH_ALEN];
-
- int sc_debug;
u32 sc_intrstatus;
u32 sc_flags; /* SC_OP_* */
- unsigned int rx_filter;
u16 sc_curtxpow;
u16 sc_curaid;
u16 sc_cachelsz;
- int sc_slotupdate; /* slot to next advance fsm */
- int sc_slottime;
- int sc_bslot[ATH_BCBUF];
+ u8 sc_nbcnvaps;
+ u16 sc_nvaps;
u8 sc_tx_chainmask;
u8 sc_rx_chainmask;
+ u32 sc_keymax;
+ DECLARE_BITMAP(sc_keymap, ATH_KEYMAX);
+ u8 sc_splitmic;
+ u8 sc_protrix;
enum ath9k_int sc_imask;
- enum wireless_mode sc_curmode; /* current phy mode */
enum PROT_MODE sc_protmode;
-
- u8 sc_nbcnvaps; /* # of vaps sending beacons */
- u16 sc_nvaps; /* # of active virtual ap's */
- struct ath_vap *sc_vaps[ATH_BCBUF];
-
- u8 sc_mcastantenna;
- u8 sc_defant; /* current default antenna */
- u8 sc_rxotherant; /* rx's on non-default antenna */
-
- struct ath9k_node_stats sc_halstats; /* station-mode rssi stats */
- struct list_head node_list;
- struct ath_ht_info sc_ht_info;
enum ath9k_ht_extprotspacing sc_ht_extprotspacing;
+ enum ath9k_ht_macmode tx_chan_width;
-#ifdef CONFIG_SLOW_ANT_DIV
- struct ath_antdiv sc_antdiv;
-#endif
- enum {
- OK, /* no change needed */
- UPDATE, /* update pending */
- COMMIT /* beacon sent, commit change */
- } sc_updateslot; /* slot time update fsm */
-
- /* Crypto */
- u32 sc_keymax; /* size of key cache */
- DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */
- u8 sc_splitmic; /* split TKIP MIC keys */
-
- /* RX */
- struct list_head sc_rxbuf;
- struct ath_descdma sc_rxdma;
- int sc_rxbufsize; /* rx size based on mtu */
- u32 *sc_rxlink; /* link ptr in last RX desc */
-
- /* TX */
- struct list_head sc_txbuf;
- struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES];
- struct ath_descdma sc_txdma;
- u32 sc_txqsetup;
- u32 sc_txintrperiod; /* tx interrupt batching */
- int sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME AC -> h/w qnum */
- u16 seq_no; /* TX sequence number */
-
- /* Beacon */
- struct ath9k_tx_queue_info sc_beacon_qi;
- struct ath_descdma sc_bdma;
- struct ath_txq *sc_cabq;
- struct list_head sc_bbuf;
- u32 sc_bhalq;
- u32 sc_bmisscount;
- u32 ast_be_xmit; /* beacons transmitted */
- u64 bc_tstamp;
-
- /* Rate */
+ struct ath_config sc_config;
+ struct ath_rx rx;
+ struct ath_tx tx;
+ struct ath_beacon beacon;
+ struct ieee80211_vif *sc_vaps[ATH_BCBUF];
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
- const struct ath9k_rate_table *sc_currates;
- u8 sc_rixmap[256]; /* IEEE to h/w rate table ix */
- u8 sc_protrix; /* protection rate index */
- struct {
- u32 rateKbps; /* transfer rate in kbs */
- u8 ieeerate; /* IEEE rate */
- } sc_hwmap[256]; /* h/w rate ix mappings */
-
- /* Channel, Band */
+ struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX];
+ struct ath_rate_table *cur_rate_table;
struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX];
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
-
- /* Locks */
- spinlock_t sc_rxflushlock;
- spinlock_t sc_rxbuflock;
- spinlock_t sc_txbuflock;
- spinlock_t sc_resetlock;
- spinlock_t node_lock;
-
- /* LEDs */
struct ath_led radio_led;
struct ath_led assoc_led;
struct ath_led tx_led;
struct ath_led rx_led;
-
- /* Rfkill */
struct ath_rfkill rf_kill;
-
- /* ANI */
struct ath_ani sc_ani;
+ struct ath9k_node_stats sc_halstats;
+#ifdef CONFIG_ATH9K_DEBUG
+ struct ath9k_debug sc_debug;
+#endif
};
-int ath_init(u16 devid, struct ath_softc *sc);
-void ath_deinit(struct ath_softc *sc);
-int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan);
-int ath_suspend(struct ath_softc *sc);
-irqreturn_t ath_isr(int irq, void *dev);
int ath_reset(struct ath_softc *sc, bool retry_tx);
-int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan);
-
-/*********************/
-/* Utility Functions */
-/*********************/
-
-void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot);
-int ath_keyset(struct ath_softc *sc,
- u16 keyix,
- struct ath9k_keyval *hk,
- const u8 mac[ETH_ALEN]);
int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
-void ath_setslottime(struct ath_softc *sc);
-void ath_update_txpow(struct ath_softc *sc);
int ath_cabq_update(struct ath_softc *);
-void ath_get_currentCountry(struct ath_softc *sc,
- struct ath9k_country_entry *ctry);
-u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp);
#endif /* CORE_H */
diff --git a/drivers/net/wireless/ath9k/debug.c b/drivers/net/wireless/ath9k/debug.c
new file mode 100644
index 000000000000..a80ed576830f
--- /dev/null
+++ b/drivers/net/wireless/ath9k/debug.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "reg.h"
+#include "hw.h"
+
+static unsigned int ath9k_debug = DBG_DEFAULT;
+module_param_named(debug, ath9k_debug, uint, 0);
+
+void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...)
+{
+ if (!sc)
+ return;
+
+ if (sc->sc_debug.debug_mask & dbg_mask) {
+ va_list args;
+
+ va_start(args, fmt);
+ printk(KERN_DEBUG "ath9k: ");
+ vprintk(fmt, args);
+ va_end(args);
+ }
+}
+
+static int ath9k_debugfs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t read_file_dma(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ struct ath_hal *ah = sc->sc_ah;
+ char buf[1024];
+ unsigned int len = 0;
+ u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
+ int i, qcuOffset = 0, dcuOffset = 0;
+ u32 *qcuBase = &val[0], *dcuBase = &val[4];
+
+ REG_WRITE(ah, AR_MACMISC,
+ ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
+ (AR_MACMISC_MISC_OBS_BUS_1 <<
+ AR_MACMISC_MISC_OBS_BUS_MSB_S)));
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "Raw DMA Debug values:\n");
+
+ for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
+ if (i % 4 == 0)
+ len += snprintf(buf + len, sizeof(buf) - len, "\n");
+
+ val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32)));
+ len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ",
+ i, val[i]);
+ }
+
+ len += snprintf(buf + len, sizeof(buf) - len, "\n\n");
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
+
+ for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) {
+ if (i == 8) {
+ qcuOffset = 0;
+ qcuBase++;
+ }
+
+ if (i == 6) {
+ dcuOffset = 0;
+ dcuBase++;
+ }
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%2d %2x %1x %2x %2x\n",
+ i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
+ (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
+ val[2] & (0x7 << (i * 3)) >> (i * 3),
+ (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+ }
+
+ len += snprintf(buf + len, sizeof(buf) - len, "\n");
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "qcu_stitch state: %2x qcu_fetch state: %2x\n",
+ (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "qcu_complete state: %2x dcu_complete state: %2x\n",
+ (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "dcu_arb state: %2x dcu_fp state: %2x\n",
+ (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
+ (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
+ (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
+ (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
+
+ len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n",
+ REG_READ(ah, AR_OBS_BUS_1));
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "AR_CR: 0x%x \n", REG_READ(ah, AR_CR));
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_dma = {
+ .read = read_file_dma,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE
+};
+
+
+void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
+{
+ if (status)
+ sc->sc_debug.stats.istats.total++;
+ if (status & ATH9K_INT_RX)
+ sc->sc_debug.stats.istats.rxok++;
+ if (status & ATH9K_INT_RXEOL)
+ sc->sc_debug.stats.istats.rxeol++;
+ if (status & ATH9K_INT_RXORN)
+ sc->sc_debug.stats.istats.rxorn++;
+ if (status & ATH9K_INT_TX)
+ sc->sc_debug.stats.istats.txok++;
+ if (status & ATH9K_INT_TXURN)
+ sc->sc_debug.stats.istats.txurn++;
+ if (status & ATH9K_INT_MIB)
+ sc->sc_debug.stats.istats.mib++;
+ if (status & ATH9K_INT_RXPHY)
+ sc->sc_debug.stats.istats.rxphyerr++;
+ if (status & ATH9K_INT_RXKCM)
+ sc->sc_debug.stats.istats.rx_keycache_miss++;
+ if (status & ATH9K_INT_SWBA)
+ sc->sc_debug.stats.istats.swba++;
+ if (status & ATH9K_INT_BMISS)
+ sc->sc_debug.stats.istats.bmiss++;
+ if (status & ATH9K_INT_BNR)
+ sc->sc_debug.stats.istats.bnr++;
+ if (status & ATH9K_INT_CST)
+ sc->sc_debug.stats.istats.cst++;
+ if (status & ATH9K_INT_GTT)
+ sc->sc_debug.stats.istats.gtt++;
+ if (status & ATH9K_INT_TIM)
+ sc->sc_debug.stats.istats.tim++;
+ if (status & ATH9K_INT_CABEND)
+ sc->sc_debug.stats.istats.cabend++;
+ if (status & ATH9K_INT_DTIMSYNC)
+ sc->sc_debug.stats.istats.dtimsync++;
+ if (status & ATH9K_INT_DTIM)
+ sc->sc_debug.stats.istats.dtim++;
+}
+
+static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ char buf[512];
+ unsigned int len = 0;
+
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "RX", sc->sc_debug.stats.istats.rxok);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "RXEOL", sc->sc_debug.stats.istats.rxeol);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "RXORN", sc->sc_debug.stats.istats.rxorn);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "TX", sc->sc_debug.stats.istats.txok);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "TXURN", sc->sc_debug.stats.istats.txurn);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "MIB", sc->sc_debug.stats.istats.mib);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "RXPHY", sc->sc_debug.stats.istats.rxphyerr);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "RXKCM", sc->sc_debug.stats.istats.rx_keycache_miss);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "SWBA", sc->sc_debug.stats.istats.swba);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "BMISS", sc->sc_debug.stats.istats.bmiss);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "BNR", sc->sc_debug.stats.istats.bnr);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "CST", sc->sc_debug.stats.istats.cst);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "GTT", sc->sc_debug.stats.istats.gtt);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "TIM", sc->sc_debug.stats.istats.tim);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "CABEND", sc->sc_debug.stats.istats.cabend);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "DTIMSYNC", sc->sc_debug.stats.istats.dtimsync);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "DTIM", sc->sc_debug.stats.istats.dtim);
+ len += snprintf(buf + len, sizeof(buf) - len,
+ "%8s: %10u\n", "TOTAL", sc->sc_debug.stats.istats.total);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_interrupt = {
+ .read = read_file_interrupt,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE
+};
+
+int ath9k_init_debug(struct ath_softc *sc)
+{
+ sc->sc_debug.debug_mask = ath9k_debug;
+
+ sc->sc_debug.debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!sc->sc_debug.debugfs_root)
+ goto err;
+
+ sc->sc_debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
+ sc->sc_debug.debugfs_root);
+ if (!sc->sc_debug.debugfs_phy)
+ goto err;
+
+ sc->sc_debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO,
+ sc->sc_debug.debugfs_phy, sc, &fops_dma);
+ if (!sc->sc_debug.debugfs_dma)
+ goto err;
+
+ sc->sc_debug.debugfs_interrupt = debugfs_create_file("interrupt",
+ S_IRUGO,
+ sc->sc_debug.debugfs_phy,
+ sc, &fops_interrupt);
+ if (!sc->sc_debug.debugfs_interrupt)
+ goto err;
+
+ return 0;
+err:
+ ath9k_exit_debug(sc);
+ return -ENOMEM;
+}
+
+void ath9k_exit_debug(struct ath_softc *sc)
+{
+ debugfs_remove(sc->sc_debug.debugfs_interrupt);
+ debugfs_remove(sc->sc_debug.debugfs_dma);
+ debugfs_remove(sc->sc_debug.debugfs_phy);
+ debugfs_remove(sc->sc_debug.debugfs_root);
+}
diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c
new file mode 100644
index 000000000000..acd6c5374d44
--- /dev/null
+++ b/drivers/net/wireless/ath9k/eeprom.c
@@ -0,0 +1,2824 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hw.h"
+#include "reg.h"
+#include "phy.h"
+
+static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah,
+ u32 reg, u32 mask,
+ u32 shift, u32 val)
+{
+ u32 regVal;
+
+ regVal = REG_READ(ah, reg) & ~mask;
+ regVal |= (val << shift) & mask;
+
+ REG_WRITE(ah, reg, regVal);
+
+ if (ah->ah_config.analog_shiftreg)
+ udelay(100);
+
+ return;
+}
+
+static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz)
+{
+
+ if (fbin == AR5416_BCHAN_UNUSED)
+ return fbin;
+
+ return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
+}
+
+static inline int16_t ath9k_hw_interpolate(u16 target,
+ u16 srcLeft, u16 srcRight,
+ int16_t targetLeft,
+ int16_t targetRight)
+{
+ int16_t rv;
+
+ if (srcRight == srcLeft) {
+ rv = targetLeft;
+ } else {
+ rv = (int16_t) (((target - srcLeft) * targetRight +
+ (srcRight - target) * targetLeft) /
+ (srcRight - srcLeft));
+ }
+ return rv;
+}
+
+static inline bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList,
+ u16 listSize, u16 *indexL,
+ u16 *indexR)
+{
+ u16 i;
+
+ if (target <= pList[0]) {
+ *indexL = *indexR = 0;
+ return true;
+ }
+ if (target >= pList[listSize - 1]) {
+ *indexL = *indexR = (u16) (listSize - 1);
+ return true;
+ }
+
+ for (i = 0; i < listSize - 1; i++) {
+ if (pList[i] == target) {
+ *indexL = *indexR = i;
+ return true;
+ }
+ if (target < pList[i + 1]) {
+ *indexL = i;
+ *indexR = (u16) (i + 1);
+ return false;
+ }
+ }
+ return false;
+}
+
+static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off, u16 *data)
+{
+ (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
+
+ if (!ath9k_hw_wait(ah,
+ AR_EEPROM_STATUS_DATA,
+ AR_EEPROM_STATUS_DATA_BUSY |
+ AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) {
+ return false;
+ }
+
+ *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
+ AR_EEPROM_STATUS_DATA_VAL);
+
+ return true;
+}
+
+static int ath9k_hw_flash_map(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX);
+
+ if (!ahp->ah_cal_mem) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "cannot remap eeprom region \n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off, u16 *data)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ *data = ioread16(ahp->ah_cal_mem + off);
+
+ return true;
+}
+
+static inline bool ath9k_hw_nvram_read(struct ath_hal *ah, u32 off, u16 *data)
+{
+ if (ath9k_hw_use_flash(ah))
+ return ath9k_hw_flash_read(ah, off, data);
+ else
+ return ath9k_hw_eeprom_read(ah, off, data);
+}
+
+static bool ath9k_hw_fill_4k_eeprom(struct ath_hal *ah)
+{
+#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep = &ahp->ah_eeprom.map4k;
+ u16 *eep_data;
+ int addr, eep_start_loc = 0;
+
+ eep_start_loc = 64;
+
+ if (!ath9k_hw_use_flash(ah)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Reading from EEPROM, not flash\n");
+ }
+
+ eep_data = (u16 *)eep;
+
+ for (addr = 0; addr < SIZE_EEPROM_4K; addr++) {
+ if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Unable to read eeprom region \n");
+ return false;
+ }
+ eep_data++;
+ }
+ return true;
+#undef SIZE_EEPROM_4K
+}
+
+static bool ath9k_hw_fill_def_eeprom(struct ath_hal *ah)
+{
+#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16))
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ u16 *eep_data;
+ int addr, ar5416_eep_start_loc = 0x100;
+
+ eep_data = (u16 *)eep;
+
+ for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) {
+ if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
+ eep_data)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Unable to read eeprom region\n");
+ return false;
+ }
+ eep_data++;
+ }
+ return true;
+#undef SIZE_EEPROM_DEF
+}
+
+static bool (*ath9k_fill_eeprom[]) (struct ath_hal *) = {
+ ath9k_hw_fill_def_eeprom,
+ ath9k_hw_fill_4k_eeprom
+};
+
+static inline bool ath9k_hw_fill_eeprom(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_fill_eeprom[ahp->ah_eep_map](ah);
+}
+
+static int ath9k_hw_check_def_eeprom(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep =
+ (struct ar5416_eeprom_def *) &ahp->ah_eeprom.def;
+ u16 *eepdata, temp, magic, magic2;
+ u32 sum = 0, el;
+ bool need_swap = false;
+ int i, addr, size;
+
+ if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
+ &magic)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Reading Magic # failed\n");
+ return false;
+ }
+
+ if (!ath9k_hw_use_flash(ah)) {
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Read Magic = 0x%04X\n", magic);
+
+ if (magic != AR5416_EEPROM_MAGIC) {
+ magic2 = swab16(magic);
+
+ if (magic2 == AR5416_EEPROM_MAGIC) {
+ size = sizeof(struct ar5416_eeprom_def);
+ need_swap = true;
+ eepdata = (u16 *) (&ahp->ah_eeprom);
+
+ for (addr = 0; addr < size / sizeof(u16); addr++) {
+ temp = swab16(*eepdata);
+ *eepdata = temp;
+ eepdata++;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "0x%04X ", *eepdata);
+
+ if (((addr + 1) % 6) == 0)
+ DPRINTF(ah->ah_sc,
+ ATH_DBG_EEPROM, "\n");
+ }
+ } else {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Invalid EEPROM Magic. "
+ "endianness mismatch.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
+ need_swap ? "True" : "False");
+
+ if (need_swap)
+ el = swab16(ahp->ah_eeprom.def.baseEepHeader.length);
+ else
+ el = ahp->ah_eeprom.def.baseEepHeader.length;
+
+ if (el > sizeof(struct ar5416_eeprom_def))
+ el = sizeof(struct ar5416_eeprom_def) / sizeof(u16);
+ else
+ el = el / sizeof(u16);
+
+ eepdata = (u16 *)(&ahp->ah_eeprom);
+
+ for (i = 0; i < el; i++)
+ sum ^= *eepdata++;
+
+ if (need_swap) {
+ u32 integer, j;
+ u16 word;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "EEPROM Endianness is not native.. Changing \n");
+
+ word = swab16(eep->baseEepHeader.length);
+ eep->baseEepHeader.length = word;
+
+ word = swab16(eep->baseEepHeader.checksum);
+ eep->baseEepHeader.checksum = word;
+
+ word = swab16(eep->baseEepHeader.version);
+ eep->baseEepHeader.version = word;
+
+ word = swab16(eep->baseEepHeader.regDmn[0]);
+ eep->baseEepHeader.regDmn[0] = word;
+
+ word = swab16(eep->baseEepHeader.regDmn[1]);
+ eep->baseEepHeader.regDmn[1] = word;
+
+ word = swab16(eep->baseEepHeader.rfSilent);
+ eep->baseEepHeader.rfSilent = word;
+
+ word = swab16(eep->baseEepHeader.blueToothOptions);
+ eep->baseEepHeader.blueToothOptions = word;
+
+ word = swab16(eep->baseEepHeader.deviceCap);
+ eep->baseEepHeader.deviceCap = word;
+
+ for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) {
+ struct modal_eep_header *pModal =
+ &eep->modalHeader[j];
+ integer = swab32(pModal->antCtrlCommon);
+ pModal->antCtrlCommon = integer;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ integer = swab32(pModal->antCtrlChain[i]);
+ pModal->antCtrlChain[i] = integer;
+ }
+
+ for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
+ word = swab16(pModal->spurChans[i].spurChan);
+ pModal->spurChans[i].spurChan = word;
+ }
+ }
+ }
+
+ if (sum != 0xffff || ar5416_get_eep_ver(ahp) != AR5416_EEP_VER ||
+ ar5416_get_eep_rev(ahp) < AR5416_EEP_NO_BACK_VER) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+ sum, ar5416_get_eep_ver(ahp));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ath9k_hw_check_4k_eeprom(struct ath_hal *ah)
+{
+#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep =
+ (struct ar5416_eeprom_4k *) &ahp->ah_eeprom.map4k;
+ u16 *eepdata, temp, magic, magic2;
+ u32 sum = 0, el;
+ bool need_swap = false;
+ int i, addr;
+
+
+ if (!ath9k_hw_use_flash(ah)) {
+
+ if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
+ &magic)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Reading Magic # failed\n");
+ return false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Read Magic = 0x%04X\n", magic);
+
+ if (magic != AR5416_EEPROM_MAGIC) {
+ magic2 = swab16(magic);
+
+ if (magic2 == AR5416_EEPROM_MAGIC) {
+ need_swap = true;
+ eepdata = (u16 *) (&ahp->ah_eeprom);
+
+ for (addr = 0; addr < EEPROM_4K_SIZE; addr++) {
+ temp = swab16(*eepdata);
+ *eepdata = temp;
+ eepdata++;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "0x%04X ", *eepdata);
+
+ if (((addr + 1) % 6) == 0)
+ DPRINTF(ah->ah_sc,
+ ATH_DBG_EEPROM, "\n");
+ }
+ } else {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Invalid EEPROM Magic. "
+ "endianness mismatch.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
+ need_swap ? "True" : "False");
+
+ if (need_swap)
+ el = swab16(ahp->ah_eeprom.map4k.baseEepHeader.length);
+ else
+ el = ahp->ah_eeprom.map4k.baseEepHeader.length;
+
+ if (el > sizeof(struct ar5416_eeprom_def))
+ el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16);
+ else
+ el = el / sizeof(u16);
+
+ eepdata = (u16 *)(&ahp->ah_eeprom);
+
+ for (i = 0; i < el; i++)
+ sum ^= *eepdata++;
+
+ if (need_swap) {
+ u32 integer;
+ u16 word;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "EEPROM Endianness is not native.. Changing \n");
+
+ word = swab16(eep->baseEepHeader.length);
+ eep->baseEepHeader.length = word;
+
+ word = swab16(eep->baseEepHeader.checksum);
+ eep->baseEepHeader.checksum = word;
+
+ word = swab16(eep->baseEepHeader.version);
+ eep->baseEepHeader.version = word;
+
+ word = swab16(eep->baseEepHeader.regDmn[0]);
+ eep->baseEepHeader.regDmn[0] = word;
+
+ word = swab16(eep->baseEepHeader.regDmn[1]);
+ eep->baseEepHeader.regDmn[1] = word;
+
+ word = swab16(eep->baseEepHeader.rfSilent);
+ eep->baseEepHeader.rfSilent = word;
+
+ word = swab16(eep->baseEepHeader.blueToothOptions);
+ eep->baseEepHeader.blueToothOptions = word;
+
+ word = swab16(eep->baseEepHeader.deviceCap);
+ eep->baseEepHeader.deviceCap = word;
+
+ integer = swab32(eep->modalHeader.antCtrlCommon);
+ eep->modalHeader.antCtrlCommon = integer;
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ integer = swab32(eep->modalHeader.antCtrlChain[i]);
+ eep->modalHeader.antCtrlChain[i] = integer;
+ }
+
+ for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
+ word = swab16(eep->modalHeader.spurChans[i].spurChan);
+ eep->modalHeader.spurChans[i].spurChan = word;
+ }
+ }
+
+ if (sum != 0xffff || ar5416_get_eep4k_ver(ahp) != AR5416_EEP_VER ||
+ ar5416_get_eep4k_rev(ahp) < AR5416_EEP_NO_BACK_VER) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+ sum, ar5416_get_eep4k_ver(ahp));
+ return -EINVAL;
+ }
+
+ return 0;
+#undef EEPROM_4K_SIZE
+}
+
+static int (*ath9k_check_eeprom[]) (struct ath_hal *) = {
+ ath9k_hw_check_def_eeprom,
+ ath9k_hw_check_4k_eeprom
+};
+
+static inline int ath9k_hw_check_eeprom(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_check_eeprom[ahp->ah_eep_map](ah);
+}
+
+static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
+ u8 *pVpdList, u16 numIntercepts,
+ u8 *pRetVpdList)
+{
+ u16 i, k;
+ u8 currPwr = pwrMin;
+ u16 idxL = 0, idxR = 0;
+
+ for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
+ ath9k_hw_get_lower_upper_index(currPwr, pPwrList,
+ numIntercepts, &(idxL),
+ &(idxR));
+ if (idxR < 1)
+ idxR = 1;
+ if (idxL == numIntercepts - 1)
+ idxL = (u16) (numIntercepts - 2);
+ if (pPwrList[idxL] == pPwrList[idxR])
+ k = pVpdList[idxL];
+ else
+ k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] +
+ (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
+ (pPwrList[idxR] - pPwrList[idxL]));
+ pRetVpdList[i] = (u8) k;
+ currPwr += 2;
+ }
+
+ return true;
+}
+
+static void ath9k_hw_get_4k_gain_boundaries_pdadcs(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct cal_data_per_freq_4k *pRawDataSet,
+ u8 *bChans, u16 availPiers,
+ u16 tPdGainOverlap, int16_t *pMinCalPower,
+ u16 *pPdGainBoundaries, u8 *pPDADCValues,
+ u16 numXpdGains)
+{
+#define TMP_VAL_VPD_TABLE \
+ ((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep));
+ int i, j, k;
+ int16_t ss;
+ u16 idxL = 0, idxR = 0, numPiers;
+ static u8 vpdTableL[AR5416_EEP4K_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+ static u8 vpdTableR[AR5416_EEP4K_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+ static u8 vpdTableI[AR5416_EEP4K_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+
+ u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
+ u8 minPwrT4[AR5416_EEP4K_NUM_PD_GAINS];
+ u8 maxPwrT4[AR5416_EEP4K_NUM_PD_GAINS];
+ int16_t vpdStep;
+ int16_t tmpVal;
+ u16 sizeCurrVpdTable, maxIndex, tgtIndex;
+ bool match;
+ int16_t minDelta = 0;
+ struct chan_centers centers;
+#define PD_GAIN_BOUNDARY_DEFAULT 58;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+
+ for (numPiers = 0; numPiers < availPiers; numPiers++) {
+ if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
+ break;
+ }
+
+ match = ath9k_hw_get_lower_upper_index(
+ (u8)FREQ2FBIN(centers.synth_center,
+ IS_CHAN_2GHZ(chan)), bChans, numPiers,
+ &idxL, &idxR);
+
+ if (match) {
+ for (i = 0; i < numXpdGains; i++) {
+ minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0];
+ maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4];
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pRawDataSet[idxL].pwrPdg[i],
+ pRawDataSet[idxL].vpdPdg[i],
+ AR5416_EEP4K_PD_GAIN_ICEPTS,
+ vpdTableI[i]);
+ }
+ } else {
+ for (i = 0; i < numXpdGains; i++) {
+ pVpdL = pRawDataSet[idxL].vpdPdg[i];
+ pPwrL = pRawDataSet[idxL].pwrPdg[i];
+ pVpdR = pRawDataSet[idxR].vpdPdg[i];
+ pPwrR = pRawDataSet[idxR].pwrPdg[i];
+
+ minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
+
+ maxPwrT4[i] =
+ min(pPwrL[AR5416_EEP4K_PD_GAIN_ICEPTS - 1],
+ pPwrR[AR5416_EEP4K_PD_GAIN_ICEPTS - 1]);
+
+
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pPwrL, pVpdL,
+ AR5416_EEP4K_PD_GAIN_ICEPTS,
+ vpdTableL[i]);
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pPwrR, pVpdR,
+ AR5416_EEP4K_PD_GAIN_ICEPTS,
+ vpdTableR[i]);
+
+ for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
+ vpdTableI[i][j] =
+ (u8)(ath9k_hw_interpolate((u16)
+ FREQ2FBIN(centers.
+ synth_center,
+ IS_CHAN_2GHZ
+ (chan)),
+ bChans[idxL], bChans[idxR],
+ vpdTableL[i][j], vpdTableR[i][j]));
+ }
+ }
+ }
+
+ *pMinCalPower = (int16_t)(minPwrT4[0] / 2);
+
+ k = 0;
+
+ for (i = 0; i < numXpdGains; i++) {
+ if (i == (numXpdGains - 1))
+ pPdGainBoundaries[i] =
+ (u16)(maxPwrT4[i] / 2);
+ else
+ pPdGainBoundaries[i] =
+ (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4);
+
+ pPdGainBoundaries[i] =
+ min((u16)AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]);
+
+ if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) {
+ minDelta = pPdGainBoundaries[0] - 23;
+ pPdGainBoundaries[0] = 23;
+ } else {
+ minDelta = 0;
+ }
+
+ if (i == 0) {
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ ss = (int16_t)(0 - (minPwrT4[i] / 2));
+ else
+ ss = 0;
+ } else {
+ ss = (int16_t)((pPdGainBoundaries[i - 1] -
+ (minPwrT4[i] / 2)) -
+ tPdGainOverlap + 1 + minDelta);
+ }
+ vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
+ vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
+
+ while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
+ tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
+ pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
+ ss++;
+ }
+
+ sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
+ tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap -
+ (minPwrT4[i] / 2));
+ maxIndex = (tgtIndex < sizeCurrVpdTable) ?
+ tgtIndex : sizeCurrVpdTable;
+
+ while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1)))
+ pPDADCValues[k++] = vpdTableI[i][ss++];
+
+ vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
+ vpdTableI[i][sizeCurrVpdTable - 2]);
+ vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
+
+ if (tgtIndex > maxIndex) {
+ while ((ss <= tgtIndex) &&
+ (k < (AR5416_NUM_PDADC_VALUES - 1))) {
+ tmpVal = (int16_t) TMP_VAL_VPD_TABLE;
+ pPDADCValues[k++] = (u8)((tmpVal > 255) ?
+ 255 : tmpVal);
+ ss++;
+ }
+ }
+ }
+
+ while (i < AR5416_EEP4K_PD_GAINS_IN_MASK) {
+ pPdGainBoundaries[i] = PD_GAIN_BOUNDARY_DEFAULT;
+ i++;
+ }
+
+ while (k < AR5416_NUM_PDADC_VALUES) {
+ pPDADCValues[k] = pPDADCValues[k - 1];
+ k++;
+ }
+
+ return;
+#undef TMP_VAL_VPD_TABLE
+}
+
+static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct cal_data_per_freq *pRawDataSet,
+ u8 *bChans, u16 availPiers,
+ u16 tPdGainOverlap, int16_t *pMinCalPower,
+ u16 *pPdGainBoundaries, u8 *pPDADCValues,
+ u16 numXpdGains)
+{
+ int i, j, k;
+ int16_t ss;
+ u16 idxL = 0, idxR = 0, numPiers;
+ static u8 vpdTableL[AR5416_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+ static u8 vpdTableR[AR5416_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+ static u8 vpdTableI[AR5416_NUM_PD_GAINS]
+ [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
+
+ u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
+ u8 minPwrT4[AR5416_NUM_PD_GAINS];
+ u8 maxPwrT4[AR5416_NUM_PD_GAINS];
+ int16_t vpdStep;
+ int16_t tmpVal;
+ u16 sizeCurrVpdTable, maxIndex, tgtIndex;
+ bool match;
+ int16_t minDelta = 0;
+ struct chan_centers centers;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+
+ for (numPiers = 0; numPiers < availPiers; numPiers++) {
+ if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
+ break;
+ }
+
+ match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center,
+ IS_CHAN_2GHZ(chan)),
+ bChans, numPiers, &idxL, &idxR);
+
+ if (match) {
+ for (i = 0; i < numXpdGains; i++) {
+ minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0];
+ maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4];
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pRawDataSet[idxL].pwrPdg[i],
+ pRawDataSet[idxL].vpdPdg[i],
+ AR5416_PD_GAIN_ICEPTS,
+ vpdTableI[i]);
+ }
+ } else {
+ for (i = 0; i < numXpdGains; i++) {
+ pVpdL = pRawDataSet[idxL].vpdPdg[i];
+ pPwrL = pRawDataSet[idxL].pwrPdg[i];
+ pVpdR = pRawDataSet[idxR].vpdPdg[i];
+ pPwrR = pRawDataSet[idxR].pwrPdg[i];
+
+ minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
+
+ maxPwrT4[i] =
+ min(pPwrL[AR5416_PD_GAIN_ICEPTS - 1],
+ pPwrR[AR5416_PD_GAIN_ICEPTS - 1]);
+
+
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pPwrL, pVpdL,
+ AR5416_PD_GAIN_ICEPTS,
+ vpdTableL[i]);
+ ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
+ pPwrR, pVpdR,
+ AR5416_PD_GAIN_ICEPTS,
+ vpdTableR[i]);
+
+ for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
+ vpdTableI[i][j] =
+ (u8)(ath9k_hw_interpolate((u16)
+ FREQ2FBIN(centers.
+ synth_center,
+ IS_CHAN_2GHZ
+ (chan)),
+ bChans[idxL], bChans[idxR],
+ vpdTableL[i][j], vpdTableR[i][j]));
+ }
+ }
+ }
+
+ *pMinCalPower = (int16_t)(minPwrT4[0] / 2);
+
+ k = 0;
+
+ for (i = 0; i < numXpdGains; i++) {
+ if (i == (numXpdGains - 1))
+ pPdGainBoundaries[i] =
+ (u16)(maxPwrT4[i] / 2);
+ else
+ pPdGainBoundaries[i] =
+ (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4);
+
+ pPdGainBoundaries[i] =
+ min((u16)AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]);
+
+ if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) {
+ minDelta = pPdGainBoundaries[0] - 23;
+ pPdGainBoundaries[0] = 23;
+ } else {
+ minDelta = 0;
+ }
+
+ if (i == 0) {
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ ss = (int16_t)(0 - (minPwrT4[i] / 2));
+ else
+ ss = 0;
+ } else {
+ ss = (int16_t)((pPdGainBoundaries[i - 1] -
+ (minPwrT4[i] / 2)) -
+ tPdGainOverlap + 1 + minDelta);
+ }
+ vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
+ vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
+
+ while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
+ tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
+ pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
+ ss++;
+ }
+
+ sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
+ tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap -
+ (minPwrT4[i] / 2));
+ maxIndex = (tgtIndex < sizeCurrVpdTable) ?
+ tgtIndex : sizeCurrVpdTable;
+
+ while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
+ pPDADCValues[k++] = vpdTableI[i][ss++];
+ }
+
+ vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
+ vpdTableI[i][sizeCurrVpdTable - 2]);
+ vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
+
+ if (tgtIndex > maxIndex) {
+ while ((ss <= tgtIndex) &&
+ (k < (AR5416_NUM_PDADC_VALUES - 1))) {
+ tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
+ (ss - maxIndex + 1) * vpdStep));
+ pPDADCValues[k++] = (u8)((tmpVal > 255) ?
+ 255 : tmpVal);
+ ss++;
+ }
+ }
+ }
+
+ while (i < AR5416_PD_GAINS_IN_MASK) {
+ pPdGainBoundaries[i] = pPdGainBoundaries[i - 1];
+ i++;
+ }
+
+ while (k < AR5416_NUM_PDADC_VALUES) {
+ pPDADCValues[k] = pPDADCValues[k - 1];
+ k++;
+ }
+
+ return;
+}
+
+static void ath9k_hw_get_legacy_target_powers(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct cal_target_power_leg *powInfo,
+ u16 numChannels,
+ struct cal_target_power_leg *pNewPower,
+ u16 numRates, bool isExtTarget)
+{
+ struct chan_centers centers;
+ u16 clo, chi;
+ int i;
+ int matchIndex = -1, lowIndex = -1;
+ u16 freq;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+ freq = (isExtTarget) ? centers.ext_center : centers.ctl_center;
+
+ if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel,
+ IS_CHAN_2GHZ(chan))) {
+ matchIndex = 0;
+ } else {
+ for (i = 0; (i < numChannels) &&
+ (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
+ if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
+ IS_CHAN_2GHZ(chan))) {
+ matchIndex = i;
+ break;
+ } else if ((freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
+ IS_CHAN_2GHZ(chan))) &&
+ (freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
+ IS_CHAN_2GHZ(chan)))) {
+ lowIndex = i - 1;
+ break;
+ }
+ }
+ if ((matchIndex == -1) && (lowIndex == -1))
+ matchIndex = i - 1;
+ }
+
+ if (matchIndex != -1) {
+ *pNewPower = powInfo[matchIndex];
+ } else {
+ clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
+ IS_CHAN_2GHZ(chan));
+ chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
+ IS_CHAN_2GHZ(chan));
+
+ for (i = 0; i < numRates; i++) {
+ pNewPower->tPow2x[i] =
+ (u8)ath9k_hw_interpolate(freq, clo, chi,
+ powInfo[lowIndex].tPow2x[i],
+ powInfo[lowIndex + 1].tPow2x[i]);
+ }
+ }
+}
+
+static void ath9k_hw_get_target_powers(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct cal_target_power_ht *powInfo,
+ u16 numChannels,
+ struct cal_target_power_ht *pNewPower,
+ u16 numRates, bool isHt40Target)
+{
+ struct chan_centers centers;
+ u16 clo, chi;
+ int i;
+ int matchIndex = -1, lowIndex = -1;
+ u16 freq;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+ freq = isHt40Target ? centers.synth_center : centers.ctl_center;
+
+ if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) {
+ matchIndex = 0;
+ } else {
+ for (i = 0; (i < numChannels) &&
+ (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
+ if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
+ IS_CHAN_2GHZ(chan))) {
+ matchIndex = i;
+ break;
+ } else
+ if ((freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
+ IS_CHAN_2GHZ(chan))) &&
+ (freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
+ IS_CHAN_2GHZ(chan)))) {
+ lowIndex = i - 1;
+ break;
+ }
+ }
+ if ((matchIndex == -1) && (lowIndex == -1))
+ matchIndex = i - 1;
+ }
+
+ if (matchIndex != -1) {
+ *pNewPower = powInfo[matchIndex];
+ } else {
+ clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
+ IS_CHAN_2GHZ(chan));
+ chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
+ IS_CHAN_2GHZ(chan));
+
+ for (i = 0; i < numRates; i++) {
+ pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq,
+ clo, chi,
+ powInfo[lowIndex].tPow2x[i],
+ powInfo[lowIndex + 1].tPow2x[i]);
+ }
+ }
+}
+
+static u16 ath9k_hw_get_max_edge_power(u16 freq,
+ struct cal_ctl_edges *pRdEdgesPower,
+ bool is2GHz, int num_band_edges)
+{
+ u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+ int i;
+
+ for (i = 0; (i < num_band_edges) &&
+ (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
+ if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) {
+ twiceMaxEdgePower = pRdEdgesPower[i].tPower;
+ break;
+ } else if ((i > 0) &&
+ (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel,
+ is2GHz))) {
+ if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel,
+ is2GHz) < freq &&
+ pRdEdgesPower[i - 1].flag) {
+ twiceMaxEdgePower =
+ pRdEdgesPower[i - 1].tPower;
+ }
+ break;
+ }
+ }
+
+ return twiceMaxEdgePower;
+}
+
+static bool ath9k_hw_set_def_power_cal_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *pTxPowerIndexOffset)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *pEepData = &ahp->ah_eeprom.def;
+ struct cal_data_per_freq *pRawDataset;
+ u8 *pCalBChans = NULL;
+ u16 pdGainOverlap_t2;
+ static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
+ u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
+ u16 numPiers, i, j;
+ int16_t tMinCalPower;
+ u16 numXpdGain, xpdMask;
+ u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
+ u32 reg32, regOffset, regChainOffset;
+ int16_t modalIdx;
+
+ modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0;
+ xpdMask = pEepData->modalHeader[modalIdx].xpdGain;
+
+ if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ pdGainOverlap_t2 =
+ pEepData->modalHeader[modalIdx].pdGainOverlap;
+ } else {
+ pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5),
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
+ }
+
+ if (IS_CHAN_2GHZ(chan)) {
+ pCalBChans = pEepData->calFreqPier2G;
+ numPiers = AR5416_NUM_2G_CAL_PIERS;
+ } else {
+ pCalBChans = pEepData->calFreqPier5G;
+ numPiers = AR5416_NUM_5G_CAL_PIERS;
+ }
+
+ numXpdGain = 0;
+
+ for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
+ if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) {
+ if (numXpdGain >= AR5416_NUM_PD_GAINS)
+ break;
+ xpdGainValues[numXpdGain] =
+ (u16)(AR5416_PD_GAINS_IN_MASK - i);
+ numXpdGain++;
+ }
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
+ (numXpdGain - 1) & 0x3);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
+ xpdGainValues[0]);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
+ xpdGainValues[1]);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3,
+ xpdGainValues[2]);
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ if (AR_SREV_5416_V20_OR_LATER(ah) &&
+ (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) &&
+ (i != 0)) {
+ regChainOffset = (i == 1) ? 0x2000 : 0x1000;
+ } else
+ regChainOffset = i * 0x1000;
+
+ if (pEepData->baseEepHeader.txMask & (1 << i)) {
+ if (IS_CHAN_2GHZ(chan))
+ pRawDataset = pEepData->calPierData2G[i];
+ else
+ pRawDataset = pEepData->calPierData5G[i];
+
+ ath9k_hw_get_def_gain_boundaries_pdadcs(ah, chan,
+ pRawDataset, pCalBChans,
+ numPiers, pdGainOverlap_t2,
+ &tMinCalPower, gainBoundaries,
+ pdadcValues, numXpdGain);
+
+ if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
+ REG_WRITE(ah,
+ AR_PHY_TPCRG5 + regChainOffset,
+ SM(pdGainOverlap_t2,
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP)
+ | SM(gainBoundaries[0],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)
+ | SM(gainBoundaries[1],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)
+ | SM(gainBoundaries[2],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)
+ | SM(gainBoundaries[3],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
+ }
+
+ regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
+ for (j = 0; j < 32; j++) {
+ reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
+ ((pdadcValues[4 * j + 1] & 0xFF) << 8) |
+ ((pdadcValues[4 * j + 2] & 0xFF) << 16)|
+ ((pdadcValues[4 * j + 3] & 0xFF) << 24);
+ REG_WRITE(ah, regOffset, reg32);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "PDADC (%d,%4x): %4.4x %8.8x\n",
+ i, regChainOffset, regOffset,
+ reg32);
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "PDADC: Chain %d | PDADC %3d "
+ "Value %3d | PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | PDADC %3d "
+ "Value %3d |\n",
+ i, 4 * j, pdadcValues[4 * j],
+ 4 * j + 1, pdadcValues[4 * j + 1],
+ 4 * j + 2, pdadcValues[4 * j + 2],
+ 4 * j + 3,
+ pdadcValues[4 * j + 3]);
+
+ regOffset += 4;
+ }
+ }
+ }
+
+ *pTxPowerIndexOffset = 0;
+
+ return true;
+}
+
+static bool ath9k_hw_set_4k_power_cal_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *pTxPowerIndexOffset)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *pEepData = &ahp->ah_eeprom.map4k;
+ struct cal_data_per_freq_4k *pRawDataset;
+ u8 *pCalBChans = NULL;
+ u16 pdGainOverlap_t2;
+ static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
+ u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
+ u16 numPiers, i, j;
+ int16_t tMinCalPower;
+ u16 numXpdGain, xpdMask;
+ u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
+ u32 reg32, regOffset, regChainOffset;
+
+ xpdMask = pEepData->modalHeader.xpdGain;
+
+ if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ pdGainOverlap_t2 =
+ pEepData->modalHeader.pdGainOverlap;
+ } else {
+ pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5),
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
+ }
+
+ pCalBChans = pEepData->calFreqPier2G;
+ numPiers = AR5416_NUM_2G_CAL_PIERS;
+
+ numXpdGain = 0;
+
+ for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
+ if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) {
+ if (numXpdGain >= AR5416_NUM_PD_GAINS)
+ break;
+ xpdGainValues[numXpdGain] =
+ (u16)(AR5416_PD_GAINS_IN_MASK - i);
+ numXpdGain++;
+ }
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
+ (numXpdGain - 1) & 0x3);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
+ xpdGainValues[0]);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
+ xpdGainValues[1]);
+ REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3,
+ xpdGainValues[2]);
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ if (AR_SREV_5416_V20_OR_LATER(ah) &&
+ (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) &&
+ (i != 0)) {
+ regChainOffset = (i == 1) ? 0x2000 : 0x1000;
+ } else
+ regChainOffset = i * 0x1000;
+
+ if (pEepData->baseEepHeader.txMask & (1 << i)) {
+ pRawDataset = pEepData->calPierData2G[i];
+
+ ath9k_hw_get_4k_gain_boundaries_pdadcs(ah, chan,
+ pRawDataset, pCalBChans,
+ numPiers, pdGainOverlap_t2,
+ &tMinCalPower, gainBoundaries,
+ pdadcValues, numXpdGain);
+
+ if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
+ REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
+ SM(pdGainOverlap_t2,
+ AR_PHY_TPCRG5_PD_GAIN_OVERLAP)
+ | SM(gainBoundaries[0],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)
+ | SM(gainBoundaries[1],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)
+ | SM(gainBoundaries[2],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)
+ | SM(gainBoundaries[3],
+ AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
+ }
+
+ regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
+ for (j = 0; j < 32; j++) {
+ reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
+ ((pdadcValues[4 * j + 1] & 0xFF) << 8) |
+ ((pdadcValues[4 * j + 2] & 0xFF) << 16)|
+ ((pdadcValues[4 * j + 3] & 0xFF) << 24);
+ REG_WRITE(ah, regOffset, reg32);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "PDADC (%d,%4x): %4.4x %8.8x\n",
+ i, regChainOffset, regOffset,
+ reg32);
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "PDADC: Chain %d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d |\n",
+ i, 4 * j, pdadcValues[4 * j],
+ 4 * j + 1, pdadcValues[4 * j + 1],
+ 4 * j + 2, pdadcValues[4 * j + 2],
+ 4 * j + 3,
+ pdadcValues[4 * j + 3]);
+
+ regOffset += 4;
+ }
+ }
+ }
+
+ *pTxPowerIndexOffset = 0;
+
+ return true;
+}
+
+static bool ath9k_hw_set_def_power_per_rate_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *ratesArray,
+ u16 cfgCtl,
+ u16 AntennaReduction,
+ u16 twiceMaxRegulatoryPower,
+ u16 powerLimit)
+{
+#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */
+#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */
+
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *pEepData = &ahp->ah_eeprom.def;
+ u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+ static const u16 tpScaleReductionTable[5] =
+ { 0, 3, 6, 9, AR5416_MAX_RATE_POWER };
+
+ int i;
+ int16_t twiceLargestAntenna;
+ struct cal_ctl_data *rep;
+ struct cal_target_power_leg targetPowerOfdm, targetPowerCck = {
+ 0, { 0, 0, 0, 0}
+ };
+ struct cal_target_power_leg targetPowerOfdmExt = {
+ 0, { 0, 0, 0, 0} }, targetPowerCckExt = {
+ 0, { 0, 0, 0, 0 }
+ };
+ struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = {
+ 0, {0, 0, 0, 0}
+ };
+ u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 ctlModesFor11a[] =
+ { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 };
+ u16 ctlModesFor11g[] =
+ { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT,
+ CTL_2GHT40
+ };
+ u16 numCtlModes, *pCtlMode, ctlMode, freq;
+ struct chan_centers centers;
+ int tx_chainmask;
+ u16 twiceMinEdgePower;
+
+ tx_chainmask = ahp->ah_txchainmask;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+
+ twiceLargestAntenna = max(
+ pEepData->modalHeader
+ [IS_CHAN_2GHZ(chan)].antennaGainCh[0],
+ pEepData->modalHeader
+ [IS_CHAN_2GHZ(chan)].antennaGainCh[1]);
+
+ twiceLargestAntenna = max((u8)twiceLargestAntenna,
+ pEepData->modalHeader
+ [IS_CHAN_2GHZ(chan)].antennaGainCh[2]);
+
+ twiceLargestAntenna = (int16_t)min(AntennaReduction -
+ twiceLargestAntenna, 0);
+
+ maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
+
+ if (ah->ah_tpScale != ATH9K_TP_SCALE_MAX) {
+ maxRegAllowedPower -=
+ (tpScaleReductionTable[(ah->ah_tpScale)] * 2);
+ }
+
+ scaledPower = min(powerLimit, maxRegAllowedPower);
+
+ switch (ar5416_get_ntxchains(tx_chainmask)) {
+ case 1:
+ break;
+ case 2:
+ scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN;
+ break;
+ case 3:
+ scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN;
+ break;
+ }
+
+ scaledPower = max((u16)0, scaledPower);
+
+ if (IS_CHAN_2GHZ(chan)) {
+ numCtlModes = ARRAY_SIZE(ctlModesFor11g) -
+ SUB_NUM_CTL_MODES_AT_2G_40;
+ pCtlMode = ctlModesFor11g;
+
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPowerCck,
+ AR5416_NUM_2G_CCK_TARGET_POWERS,
+ &targetPowerCck, 4, false);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower2G,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerOfdm, 4, false);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower2GHT20,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerHt20, 8, false);
+
+ if (IS_CHAN_HT40(chan)) {
+ numCtlModes = ARRAY_SIZE(ctlModesFor11g);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower2GHT40,
+ AR5416_NUM_2G_40_TARGET_POWERS,
+ &targetPowerHt40, 8, true);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPowerCck,
+ AR5416_NUM_2G_CCK_TARGET_POWERS,
+ &targetPowerCckExt, 4, true);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower2G,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerOfdmExt, 4, true);
+ }
+ } else {
+ numCtlModes = ARRAY_SIZE(ctlModesFor11a) -
+ SUB_NUM_CTL_MODES_AT_5G_40;
+ pCtlMode = ctlModesFor11a;
+
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower5G,
+ AR5416_NUM_5G_20_TARGET_POWERS,
+ &targetPowerOfdm, 4, false);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower5GHT20,
+ AR5416_NUM_5G_20_TARGET_POWERS,
+ &targetPowerHt20, 8, false);
+
+ if (IS_CHAN_HT40(chan)) {
+ numCtlModes = ARRAY_SIZE(ctlModesFor11a);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower5GHT40,
+ AR5416_NUM_5G_40_TARGET_POWERS,
+ &targetPowerHt40, 8, true);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower5G,
+ AR5416_NUM_5G_20_TARGET_POWERS,
+ &targetPowerOfdmExt, 4, true);
+ }
+ }
+
+ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) {
+ bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) ||
+ (pCtlMode[ctlMode] == CTL_2GHT40);
+ if (isHt40CtlMode)
+ freq = centers.synth_center;
+ else if (pCtlMode[ctlMode] & EXT_ADDITIVE)
+ freq = centers.ext_center;
+ else
+ freq = centers.ctl_center;
+
+ if (ar5416_get_eep_ver(ahp) == 14 && ar5416_get_eep_rev(ahp) <= 2)
+ twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
+ "EXT_ADDITIVE %d\n",
+ ctlMode, numCtlModes, isHt40CtlMode,
+ (pCtlMode[ctlMode] & EXT_ADDITIVE));
+
+ for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
+ "pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
+ "chan %d\n",
+ i, cfgCtl, pCtlMode[ctlMode],
+ pEepData->ctlIndex[i], chan->channel);
+
+ if ((((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ pEepData->ctlIndex[i]) ||
+ (((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) {
+ rep = &(pEepData->ctlData[i]);
+
+ twiceMinEdgePower = ath9k_hw_get_max_edge_power(freq,
+ rep->ctlEdges[ar5416_get_ntxchains(tx_chainmask) - 1],
+ IS_CHAN_2GHZ(chan), AR5416_NUM_BAND_EDGES);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " MATCH-EE_IDX %d: ch %d is2 %d "
+ "2xMinEdge %d chainmask %d chains %d\n",
+ i, freq, IS_CHAN_2GHZ(chan),
+ twiceMinEdgePower, tx_chainmask,
+ ar5416_get_ntxchains
+ (tx_chainmask));
+ if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) {
+ twiceMaxEdgePower = min(twiceMaxEdgePower,
+ twiceMinEdgePower);
+ } else {
+ twiceMaxEdgePower = twiceMinEdgePower;
+ break;
+ }
+ }
+ }
+
+ minCtlPower = min(twiceMaxEdgePower, scaledPower);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " SEL-Min ctlMode %d pCtlMode %d "
+ "2xMaxEdge %d sP %d minCtlPwr %d\n",
+ ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower,
+ scaledPower, minCtlPower);
+
+ switch (pCtlMode[ctlMode]) {
+ case CTL_11B:
+ for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) {
+ targetPowerCck.tPow2x[i] =
+ min((u16)targetPowerCck.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_11A:
+ case CTL_11G:
+ for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) {
+ targetPowerOfdm.tPow2x[i] =
+ min((u16)targetPowerOfdm.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_5GHT20:
+ case CTL_2GHT20:
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) {
+ targetPowerHt20.tPow2x[i] =
+ min((u16)targetPowerHt20.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_11B_EXT:
+ targetPowerCckExt.tPow2x[0] = min((u16)
+ targetPowerCckExt.tPow2x[0],
+ minCtlPower);
+ break;
+ case CTL_11A_EXT:
+ case CTL_11G_EXT:
+ targetPowerOfdmExt.tPow2x[0] = min((u16)
+ targetPowerOfdmExt.tPow2x[0],
+ minCtlPower);
+ break;
+ case CTL_5GHT40:
+ case CTL_2GHT40:
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) {
+ targetPowerHt40.tPow2x[i] =
+ min((u16)targetPowerHt40.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] =
+ ratesArray[rate18mb] = ratesArray[rate24mb] =
+ targetPowerOfdm.tPow2x[0];
+ ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1];
+ ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2];
+ ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3];
+ ratesArray[rateXr] = targetPowerOfdm.tPow2x[0];
+
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++)
+ ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i];
+
+ if (IS_CHAN_2GHZ(chan)) {
+ ratesArray[rate1l] = targetPowerCck.tPow2x[0];
+ ratesArray[rate2s] = ratesArray[rate2l] =
+ targetPowerCck.tPow2x[1];
+ ratesArray[rate5_5s] = ratesArray[rate5_5l] =
+ targetPowerCck.tPow2x[2];
+ ;
+ ratesArray[rate11s] = ratesArray[rate11l] =
+ targetPowerCck.tPow2x[3];
+ ;
+ }
+ if (IS_CHAN_HT40(chan)) {
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) {
+ ratesArray[rateHt40_0 + i] =
+ targetPowerHt40.tPow2x[i];
+ }
+ ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0];
+ ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0];
+ ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0];
+ if (IS_CHAN_2GHZ(chan)) {
+ ratesArray[rateExtCck] =
+ targetPowerCckExt.tPow2x[0];
+ }
+ }
+ return true;
+}
+
+static bool ath9k_hw_set_4k_power_per_rate_table(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ int16_t *ratesArray,
+ u16 cfgCtl,
+ u16 AntennaReduction,
+ u16 twiceMaxRegulatoryPower,
+ u16 powerLimit)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *pEepData = &ahp->ah_eeprom.map4k;
+ u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+ static const u16 tpScaleReductionTable[5] =
+ { 0, 3, 6, 9, AR5416_MAX_RATE_POWER };
+
+ int i;
+ int16_t twiceLargestAntenna;
+ struct cal_ctl_data_4k *rep;
+ struct cal_target_power_leg targetPowerOfdm, targetPowerCck = {
+ 0, { 0, 0, 0, 0}
+ };
+ struct cal_target_power_leg targetPowerOfdmExt = {
+ 0, { 0, 0, 0, 0} }, targetPowerCckExt = {
+ 0, { 0, 0, 0, 0 }
+ };
+ struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = {
+ 0, {0, 0, 0, 0}
+ };
+ u16 scaledPower = 0, minCtlPower, maxRegAllowedPower;
+ u16 ctlModesFor11g[] =
+ { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT,
+ CTL_2GHT40
+ };
+ u16 numCtlModes, *pCtlMode, ctlMode, freq;
+ struct chan_centers centers;
+ int tx_chainmask;
+ u16 twiceMinEdgePower;
+
+ tx_chainmask = ahp->ah_txchainmask;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+
+ twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0];
+
+ twiceLargestAntenna = (int16_t)min(AntennaReduction -
+ twiceLargestAntenna, 0);
+
+ maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
+
+ if (ah->ah_tpScale != ATH9K_TP_SCALE_MAX) {
+ maxRegAllowedPower -=
+ (tpScaleReductionTable[(ah->ah_tpScale)] * 2);
+ }
+
+ scaledPower = min(powerLimit, maxRegAllowedPower);
+ scaledPower = max((u16)0, scaledPower);
+
+ numCtlModes = ARRAY_SIZE(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40;
+ pCtlMode = ctlModesFor11g;
+
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPowerCck,
+ AR5416_NUM_2G_CCK_TARGET_POWERS,
+ &targetPowerCck, 4, false);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower2G,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerOfdm, 4, false);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower2GHT20,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerHt20, 8, false);
+
+ if (IS_CHAN_HT40(chan)) {
+ numCtlModes = ARRAY_SIZE(ctlModesFor11g);
+ ath9k_hw_get_target_powers(ah, chan,
+ pEepData->calTargetPower2GHT40,
+ AR5416_NUM_2G_40_TARGET_POWERS,
+ &targetPowerHt40, 8, true);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPowerCck,
+ AR5416_NUM_2G_CCK_TARGET_POWERS,
+ &targetPowerCckExt, 4, true);
+ ath9k_hw_get_legacy_target_powers(ah, chan,
+ pEepData->calTargetPower2G,
+ AR5416_NUM_2G_20_TARGET_POWERS,
+ &targetPowerOfdmExt, 4, true);
+ }
+
+ for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) {
+ bool isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) ||
+ (pCtlMode[ctlMode] == CTL_2GHT40);
+ if (isHt40CtlMode)
+ freq = centers.synth_center;
+ else if (pCtlMode[ctlMode] & EXT_ADDITIVE)
+ freq = centers.ext_center;
+ else
+ freq = centers.ctl_center;
+
+ if (ar5416_get_eep_ver(ahp) == 14 &&
+ ar5416_get_eep_rev(ahp) <= 2)
+ twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
+ "EXT_ADDITIVE %d\n",
+ ctlMode, numCtlModes, isHt40CtlMode,
+ (pCtlMode[ctlMode] & EXT_ADDITIVE));
+
+ for (i = 0; (i < AR5416_NUM_CTLS) &&
+ pEepData->ctlIndex[i]; i++) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
+ "pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
+ "chan %d\n",
+ i, cfgCtl, pCtlMode[ctlMode],
+ pEepData->ctlIndex[i], chan->channel);
+
+ if ((((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ pEepData->ctlIndex[i]) ||
+ (((cfgCtl & ~CTL_MODE_M) |
+ (pCtlMode[ctlMode] & CTL_MODE_M)) ==
+ ((pEepData->ctlIndex[i] & CTL_MODE_M) |
+ SD_NO_CTL))) {
+ rep = &(pEepData->ctlData[i]);
+
+ twiceMinEdgePower =
+ ath9k_hw_get_max_edge_power(freq,
+ rep->ctlEdges[ar5416_get_ntxchains
+ (tx_chainmask) - 1],
+ IS_CHAN_2GHZ(chan),
+ AR5416_EEP4K_NUM_BAND_EDGES);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " MATCH-EE_IDX %d: ch %d is2 %d "
+ "2xMinEdge %d chainmask %d chains %d\n",
+ i, freq, IS_CHAN_2GHZ(chan),
+ twiceMinEdgePower, tx_chainmask,
+ ar5416_get_ntxchains
+ (tx_chainmask));
+ if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) {
+ twiceMaxEdgePower =
+ min(twiceMaxEdgePower,
+ twiceMinEdgePower);
+ } else {
+ twiceMaxEdgePower = twiceMinEdgePower;
+ break;
+ }
+ }
+ }
+
+ minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ " SEL-Min ctlMode %d pCtlMode %d "
+ "2xMaxEdge %d sP %d minCtlPwr %d\n",
+ ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower,
+ scaledPower, minCtlPower);
+
+ switch (pCtlMode[ctlMode]) {
+ case CTL_11B:
+ for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x);
+ i++) {
+ targetPowerCck.tPow2x[i] =
+ min((u16)targetPowerCck.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_11G:
+ for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x);
+ i++) {
+ targetPowerOfdm.tPow2x[i] =
+ min((u16)targetPowerOfdm.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_2GHT20:
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x);
+ i++) {
+ targetPowerHt20.tPow2x[i] =
+ min((u16)targetPowerHt20.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ case CTL_11B_EXT:
+ targetPowerCckExt.tPow2x[0] = min((u16)
+ targetPowerCckExt.tPow2x[0],
+ minCtlPower);
+ break;
+ case CTL_11G_EXT:
+ targetPowerOfdmExt.tPow2x[0] = min((u16)
+ targetPowerOfdmExt.tPow2x[0],
+ minCtlPower);
+ break;
+ case CTL_2GHT40:
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x);
+ i++) {
+ targetPowerHt40.tPow2x[i] =
+ min((u16)targetPowerHt40.tPow2x[i],
+ minCtlPower);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] =
+ ratesArray[rate18mb] = ratesArray[rate24mb] =
+ targetPowerOfdm.tPow2x[0];
+ ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1];
+ ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2];
+ ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3];
+ ratesArray[rateXr] = targetPowerOfdm.tPow2x[0];
+
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++)
+ ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i];
+
+ ratesArray[rate1l] = targetPowerCck.tPow2x[0];
+ ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1];
+ ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2];
+ ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3];
+
+ if (IS_CHAN_HT40(chan)) {
+ for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) {
+ ratesArray[rateHt40_0 + i] =
+ targetPowerHt40.tPow2x[i];
+ }
+ ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0];
+ ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0];
+ ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0];
+ ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0];
+ }
+ return true;
+}
+
+static int ath9k_hw_def_set_txpower(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u16 cfgCtl,
+ u8 twiceAntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u8 powerLimit)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *pEepData = &ahp->ah_eeprom.def;
+ struct modal_eep_header *pModal =
+ &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]);
+ int16_t ratesArray[Ar5416RateSize];
+ int16_t txPowerIndexOffset = 0;
+ u8 ht40PowerIncForPdadc = 2;
+ int i;
+
+ memset(ratesArray, 0, sizeof(ratesArray));
+
+ if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
+ }
+
+ if (!ath9k_hw_set_def_power_per_rate_table(ah, chan,
+ &ratesArray[0], cfgCtl,
+ twiceAntennaReduction,
+ twiceMaxRegulatoryPower,
+ powerLimit)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "ath9k_hw_set_txpower: unable to set "
+ "tx power per rate table\n");
+ return -EIO;
+ }
+
+ if (!ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "ath9k_hw_set_txpower: unable to set power table\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
+ ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
+ if (ratesArray[i] > AR5416_MAX_RATE_POWER)
+ ratesArray[i] = AR5416_MAX_RATE_POWER;
+ }
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ for (i = 0; i < Ar5416RateSize; i++)
+ ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+ }
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
+ ATH9K_POW_SM(ratesArray[rate18mb], 24)
+ | ATH9K_POW_SM(ratesArray[rate12mb], 16)
+ | ATH9K_POW_SM(ratesArray[rate9mb], 8)
+ | ATH9K_POW_SM(ratesArray[rate6mb], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE2,
+ ATH9K_POW_SM(ratesArray[rate54mb], 24)
+ | ATH9K_POW_SM(ratesArray[rate48mb], 16)
+ | ATH9K_POW_SM(ratesArray[rate36mb], 8)
+ | ATH9K_POW_SM(ratesArray[rate24mb], 0));
+
+ if (IS_CHAN_2GHZ(chan)) {
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE3,
+ ATH9K_POW_SM(ratesArray[rate2s], 24)
+ | ATH9K_POW_SM(ratesArray[rate2l], 16)
+ | ATH9K_POW_SM(ratesArray[rateXr], 8)
+ | ATH9K_POW_SM(ratesArray[rate1l], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE4,
+ ATH9K_POW_SM(ratesArray[rate11s], 24)
+ | ATH9K_POW_SM(ratesArray[rate11l], 16)
+ | ATH9K_POW_SM(ratesArray[rate5_5s], 8)
+ | ATH9K_POW_SM(ratesArray[rate5_5l], 0));
+ }
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE5,
+ ATH9K_POW_SM(ratesArray[rateHt20_3], 24)
+ | ATH9K_POW_SM(ratesArray[rateHt20_2], 16)
+ | ATH9K_POW_SM(ratesArray[rateHt20_1], 8)
+ | ATH9K_POW_SM(ratesArray[rateHt20_0], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE6,
+ ATH9K_POW_SM(ratesArray[rateHt20_7], 24)
+ | ATH9K_POW_SM(ratesArray[rateHt20_6], 16)
+ | ATH9K_POW_SM(ratesArray[rateHt20_5], 8)
+ | ATH9K_POW_SM(ratesArray[rateHt20_4], 0));
+
+ if (IS_CHAN_HT40(chan)) {
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE7,
+ ATH9K_POW_SM(ratesArray[rateHt40_3] +
+ ht40PowerIncForPdadc, 24)
+ | ATH9K_POW_SM(ratesArray[rateHt40_2] +
+ ht40PowerIncForPdadc, 16)
+ | ATH9K_POW_SM(ratesArray[rateHt40_1] +
+ ht40PowerIncForPdadc, 8)
+ | ATH9K_POW_SM(ratesArray[rateHt40_0] +
+ ht40PowerIncForPdadc, 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE8,
+ ATH9K_POW_SM(ratesArray[rateHt40_7] +
+ ht40PowerIncForPdadc, 24)
+ | ATH9K_POW_SM(ratesArray[rateHt40_6] +
+ ht40PowerIncForPdadc, 16)
+ | ATH9K_POW_SM(ratesArray[rateHt40_5] +
+ ht40PowerIncForPdadc, 8)
+ | ATH9K_POW_SM(ratesArray[rateHt40_4] +
+ ht40PowerIncForPdadc, 0));
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE9,
+ ATH9K_POW_SM(ratesArray[rateExtOfdm], 24)
+ | ATH9K_POW_SM(ratesArray[rateExtCck], 16)
+ | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8)
+ | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
+ }
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6)
+ | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0));
+
+ i = rate6mb;
+
+ if (IS_CHAN_HT40(chan))
+ i = rateHt40_0;
+ else if (IS_CHAN_HT20(chan))
+ i = rateHt20_0;
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ ah->ah_maxPowerLevel =
+ ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2;
+ else
+ ah->ah_maxPowerLevel = ratesArray[i];
+
+ return 0;
+}
+
+static int ath9k_hw_4k_set_txpower(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u16 cfgCtl,
+ u8 twiceAntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u8 powerLimit)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *pEepData = &ahp->ah_eeprom.map4k;
+ struct modal_eep_4k_header *pModal = &pEepData->modalHeader;
+ int16_t ratesArray[Ar5416RateSize];
+ int16_t txPowerIndexOffset = 0;
+ u8 ht40PowerIncForPdadc = 2;
+ int i;
+
+ memset(ratesArray, 0, sizeof(ratesArray));
+
+ if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
+ }
+
+ if (!ath9k_hw_set_4k_power_per_rate_table(ah, chan,
+ &ratesArray[0], cfgCtl,
+ twiceAntennaReduction,
+ twiceMaxRegulatoryPower,
+ powerLimit)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "ath9k_hw_set_txpower: unable to set "
+ "tx power per rate table\n");
+ return -EIO;
+ }
+
+ if (!ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "ath9k_hw_set_txpower: unable to set power table\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
+ ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
+ if (ratesArray[i] > AR5416_MAX_RATE_POWER)
+ ratesArray[i] = AR5416_MAX_RATE_POWER;
+ }
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ for (i = 0; i < Ar5416RateSize; i++)
+ ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+ }
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
+ ATH9K_POW_SM(ratesArray[rate18mb], 24)
+ | ATH9K_POW_SM(ratesArray[rate12mb], 16)
+ | ATH9K_POW_SM(ratesArray[rate9mb], 8)
+ | ATH9K_POW_SM(ratesArray[rate6mb], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE2,
+ ATH9K_POW_SM(ratesArray[rate54mb], 24)
+ | ATH9K_POW_SM(ratesArray[rate48mb], 16)
+ | ATH9K_POW_SM(ratesArray[rate36mb], 8)
+ | ATH9K_POW_SM(ratesArray[rate24mb], 0));
+
+ if (IS_CHAN_2GHZ(chan)) {
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE3,
+ ATH9K_POW_SM(ratesArray[rate2s], 24)
+ | ATH9K_POW_SM(ratesArray[rate2l], 16)
+ | ATH9K_POW_SM(ratesArray[rateXr], 8)
+ | ATH9K_POW_SM(ratesArray[rate1l], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE4,
+ ATH9K_POW_SM(ratesArray[rate11s], 24)
+ | ATH9K_POW_SM(ratesArray[rate11l], 16)
+ | ATH9K_POW_SM(ratesArray[rate5_5s], 8)
+ | ATH9K_POW_SM(ratesArray[rate5_5l], 0));
+ }
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE5,
+ ATH9K_POW_SM(ratesArray[rateHt20_3], 24)
+ | ATH9K_POW_SM(ratesArray[rateHt20_2], 16)
+ | ATH9K_POW_SM(ratesArray[rateHt20_1], 8)
+ | ATH9K_POW_SM(ratesArray[rateHt20_0], 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE6,
+ ATH9K_POW_SM(ratesArray[rateHt20_7], 24)
+ | ATH9K_POW_SM(ratesArray[rateHt20_6], 16)
+ | ATH9K_POW_SM(ratesArray[rateHt20_5], 8)
+ | ATH9K_POW_SM(ratesArray[rateHt20_4], 0));
+
+ if (IS_CHAN_HT40(chan)) {
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE7,
+ ATH9K_POW_SM(ratesArray[rateHt40_3] +
+ ht40PowerIncForPdadc, 24)
+ | ATH9K_POW_SM(ratesArray[rateHt40_2] +
+ ht40PowerIncForPdadc, 16)
+ | ATH9K_POW_SM(ratesArray[rateHt40_1] +
+ ht40PowerIncForPdadc, 8)
+ | ATH9K_POW_SM(ratesArray[rateHt40_0] +
+ ht40PowerIncForPdadc, 0));
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE8,
+ ATH9K_POW_SM(ratesArray[rateHt40_7] +
+ ht40PowerIncForPdadc, 24)
+ | ATH9K_POW_SM(ratesArray[rateHt40_6] +
+ ht40PowerIncForPdadc, 16)
+ | ATH9K_POW_SM(ratesArray[rateHt40_5] +
+ ht40PowerIncForPdadc, 8)
+ | ATH9K_POW_SM(ratesArray[rateHt40_4] +
+ ht40PowerIncForPdadc, 0));
+
+ REG_WRITE(ah, AR_PHY_POWER_TX_RATE9,
+ ATH9K_POW_SM(ratesArray[rateExtOfdm], 24)
+ | ATH9K_POW_SM(ratesArray[rateExtCck], 16)
+ | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8)
+ | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
+ }
+
+ i = rate6mb;
+
+ if (IS_CHAN_HT40(chan))
+ i = rateHt40_0;
+ else if (IS_CHAN_HT20(chan))
+ i = rateHt20_0;
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ ah->ah_maxPowerLevel =
+ ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2;
+ else
+ ah->ah_maxPowerLevel = ratesArray[i];
+
+ return 0;
+}
+
+static int (*ath9k_set_txpower[]) (struct ath_hal *,
+ struct ath9k_channel *,
+ u16, u8, u8, u8) = {
+ ath9k_hw_def_set_txpower,
+ ath9k_hw_4k_set_txpower
+};
+
+int ath9k_hw_set_txpower(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u16 cfgCtl,
+ u8 twiceAntennaReduction,
+ u8 twiceMaxRegulatoryPower,
+ u8 powerLimit)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_set_txpower[ahp->ah_eep_map](ah, chan, cfgCtl,
+ twiceAntennaReduction, twiceMaxRegulatoryPower,
+ powerLimit);
+}
+
+static void ath9k_hw_set_def_addac(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+#define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt])
+ struct modal_eep_header *pModal;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ u8 biaslevel;
+
+ if (ah->ah_macVersion != AR_SREV_VERSION_9160)
+ return;
+
+ if (ar5416_get_eep_rev(ahp) < AR5416_EEP_MINOR_VER_7)
+ return;
+
+ pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+
+ if (pModal->xpaBiasLvl != 0xff) {
+ biaslevel = pModal->xpaBiasLvl;
+ } else {
+ u16 resetFreqBin, freqBin, freqCount = 0;
+ struct chan_centers centers;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+
+ resetFreqBin = FREQ2FBIN(centers.synth_center,
+ IS_CHAN_2GHZ(chan));
+ freqBin = XPA_LVL_FREQ(0) & 0xff;
+ biaslevel = (u8) (XPA_LVL_FREQ(0) >> 14);
+
+ freqCount++;
+
+ while (freqCount < 3) {
+ if (XPA_LVL_FREQ(freqCount) == 0x0)
+ break;
+
+ freqBin = XPA_LVL_FREQ(freqCount) & 0xff;
+ if (resetFreqBin >= freqBin)
+ biaslevel = (u8)(XPA_LVL_FREQ(freqCount) >> 14);
+ else
+ break;
+ freqCount++;
+ }
+ }
+
+ if (IS_CHAN_2GHZ(chan)) {
+ INI_RA(&ahp->ah_iniAddac, 7, 1) = (INI_RA(&ahp->ah_iniAddac,
+ 7, 1) & (~0x18)) | biaslevel << 3;
+ } else {
+ INI_RA(&ahp->ah_iniAddac, 6, 1) = (INI_RA(&ahp->ah_iniAddac,
+ 6, 1) & (~0xc0)) | biaslevel << 6;
+ }
+#undef XPA_LVL_FREQ
+}
+
+static void ath9k_hw_set_4k_addac(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct modal_eep_4k_header *pModal;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep = &ahp->ah_eeprom.map4k;
+ u8 biaslevel;
+
+ if (ah->ah_macVersion != AR_SREV_VERSION_9160)
+ return;
+
+ if (ar5416_get_eep_rev(ahp) < AR5416_EEP_MINOR_VER_7)
+ return;
+
+ pModal = &eep->modalHeader;
+
+ if (pModal->xpaBiasLvl != 0xff) {
+ biaslevel = pModal->xpaBiasLvl;
+ INI_RA(&ahp->ah_iniAddac, 7, 1) =
+ (INI_RA(&ahp->ah_iniAddac, 7, 1) & (~0x18)) | biaslevel << 3;
+ }
+}
+
+static void (*ath9k_set_addac[]) (struct ath_hal *, struct ath9k_channel *) = {
+ ath9k_hw_set_def_addac,
+ ath9k_hw_set_4k_addac
+};
+
+void ath9k_hw_set_addac(struct ath_hal *ah, struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ ath9k_set_addac[ahp->ah_eep_map](ah, chan);
+}
+
+
+
+/* XXX: Clean me up, make me more legible */
+static bool ath9k_hw_eeprom_set_def_board_values(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct modal_eep_header *pModal;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ int i, regChainOffset;
+ u8 txRxAttenLocal;
+ u16 ant_config;
+
+ pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+
+ txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44;
+
+ ath9k_hw_get_eeprom_antenna_cfg(ah, chan, 0, &ant_config);
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
+
+ for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+ if (AR_SREV_9280(ah)) {
+ if (i >= 2)
+ break;
+ }
+
+ if (AR_SREV_5416_V20_OR_LATER(ah) &&
+ (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5)
+ && (i != 0))
+ regChainOffset = (i == 1) ? 0x2000 : 0x1000;
+ else
+ regChainOffset = i * 0x1000;
+
+ REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
+ pModal->antCtrlChain[i]);
+
+ REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
+ (REG_READ(ah,
+ AR_PHY_TIMING_CTRL4(0) +
+ regChainOffset) &
+ ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
+ SM(pModal->iqCalICh[i],
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+ SM(pModal->iqCalQCh[i],
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+
+ if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
+ if ((eep->baseEepHeader.version &
+ AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_3) {
+ txRxAttenLocal = pModal->txRxAttenCh[i];
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_RMW_FIELD(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
+ pModal->
+ bswMargin[i]);
+ REG_RMW_FIELD(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_DB,
+ pModal->
+ bswAtten[i]);
+ REG_RMW_FIELD(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+ pModal->
+ xatten2Margin[i]);
+ REG_RMW_FIELD(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_DB,
+ pModal->
+ xatten2Db[i]);
+ } else {
+ REG_WRITE(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ (REG_READ(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
+ | SM(pModal->
+ bswMargin[i],
+ AR_PHY_GAIN_2GHZ_BSW_MARGIN));
+ REG_WRITE(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ (REG_READ(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
+ | SM(pModal->bswAtten[i],
+ AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+ }
+ }
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_RMW_FIELD(ah,
+ AR_PHY_RXGAIN +
+ regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_ATTEN,
+ txRxAttenLocal);
+ REG_RMW_FIELD(ah,
+ AR_PHY_RXGAIN +
+ regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_MARGIN,
+ pModal->rxTxMarginCh[i]);
+ } else {
+ REG_WRITE(ah,
+ AR_PHY_RXGAIN + regChainOffset,
+ (REG_READ(ah,
+ AR_PHY_RXGAIN +
+ regChainOffset) &
+ ~AR_PHY_RXGAIN_TXRX_ATTEN) |
+ SM(txRxAttenLocal,
+ AR_PHY_RXGAIN_TXRX_ATTEN));
+ REG_WRITE(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset,
+ (REG_READ(ah,
+ AR_PHY_GAIN_2GHZ +
+ regChainOffset) &
+ ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
+ SM(pModal->rxTxMarginCh[i],
+ AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+ }
+ }
+ }
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ if (IS_CHAN_2GHZ(chan)) {
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
+ AR_AN_RF2G1_CH0_OB,
+ AR_AN_RF2G1_CH0_OB_S,
+ pModal->ob);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
+ AR_AN_RF2G1_CH0_DB,
+ AR_AN_RF2G1_CH0_DB_S,
+ pModal->db);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
+ AR_AN_RF2G1_CH1_OB,
+ AR_AN_RF2G1_CH1_OB_S,
+ pModal->ob_ch1);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
+ AR_AN_RF2G1_CH1_DB,
+ AR_AN_RF2G1_CH1_DB_S,
+ pModal->db_ch1);
+ } else {
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
+ AR_AN_RF5G1_CH0_OB5,
+ AR_AN_RF5G1_CH0_OB5_S,
+ pModal->ob);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
+ AR_AN_RF5G1_CH0_DB5,
+ AR_AN_RF5G1_CH0_DB5_S,
+ pModal->db);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
+ AR_AN_RF5G1_CH1_OB5,
+ AR_AN_RF5G1_CH1_OB5_S,
+ pModal->ob_ch1);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
+ AR_AN_RF5G1_CH1_DB5,
+ AR_AN_RF5G1_CH1_DB5_S,
+ pModal->db_ch1);
+ }
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
+ AR_AN_TOP2_XPABIAS_LVL,
+ AR_AN_TOP2_XPABIAS_LVL_S,
+ pModal->xpaBiasLvl);
+ ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
+ AR_AN_TOP2_LOCALBIAS,
+ AR_AN_TOP2_LOCALBIAS_S,
+ pModal->local_bias);
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "ForceXPAon: %d\n",
+ pModal->force_xpaon);
+ REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG,
+ pModal->force_xpaon);
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
+ pModal->switchSettling);
+ REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
+ pModal->adcDesiredSize);
+
+ if (!AR_SREV_9280_10_OR_LATER(ah))
+ REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
+ AR_PHY_DESIRED_SZ_PGA,
+ pModal->pgaDesiredSize);
+
+ REG_WRITE(ah, AR_PHY_RF_CTL4,
+ SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF)
+ | SM(pModal->txEndToXpaOff,
+ AR_PHY_RF_CTL4_TX_END_XPAB_OFF)
+ | SM(pModal->txFrameToXpaOn,
+ AR_PHY_RF_CTL4_FRAME_XPAA_ON)
+ | SM(pModal->txFrameToXpaOn,
+ AR_PHY_RF_CTL4_FRAME_XPAB_ON));
+
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
+ pModal->txEndToRxOn);
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
+ pModal->thresh62);
+ REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0,
+ AR_PHY_EXT_CCA0_THRESH62,
+ pModal->thresh62);
+ } else {
+ REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62,
+ pModal->thresh62);
+ REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
+ AR_PHY_EXT_CCA_THRESH62,
+ pModal->thresh62);
+ }
+
+ if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL2,
+ AR_PHY_TX_END_DATA_START,
+ pModal->txFrameToDataStart);
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON,
+ pModal->txFrameToPaOn);
+ }
+
+ if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_3) {
+ if (IS_CHAN_HT40(chan))
+ REG_RMW_FIELD(ah, AR_PHY_SETTLING,
+ AR_PHY_SETTLING_SWITCH,
+ pModal->swSettleHt40);
+ }
+
+ return true;
+}
+
+static bool ath9k_hw_eeprom_set_4k_board_values(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct modal_eep_4k_header *pModal;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep = &ahp->ah_eeprom.map4k;
+ int regChainOffset;
+ u8 txRxAttenLocal;
+ u16 ant_config = 0;
+ u8 ob[5], db1[5], db2[5];
+ u8 ant_div_control1, ant_div_control2;
+ u32 regVal;
+
+
+ pModal = &eep->modalHeader;
+
+ txRxAttenLocal = 23;
+
+ ath9k_hw_get_eeprom_antenna_cfg(ah, chan, 0, &ant_config);
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
+
+ regChainOffset = 0;
+ REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
+ pModal->antCtrlChain[0]);
+
+ REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
+ (REG_READ(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset) &
+ ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
+ SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+ SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+
+ if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_3) {
+ txRxAttenLocal = pModal->txRxAttenCh[0];
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+ pModal->xatten2Margin[0]);
+ REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+ REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
+ AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+
+ if (AR_SREV_9285_11(ah))
+ REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
+
+ /* Initialize Ant Diversity settings from EEPROM */
+ if (pModal->version == 3) {
+ ant_div_control1 = ((pModal->ob_234 >> 12) & 0xf);
+ ant_div_control2 = ((pModal->db1_234 >> 12) & 0xf);
+ regVal = REG_READ(ah, 0x99ac);
+ regVal &= (~(0x7f000000));
+ regVal |= ((ant_div_control1 & 0x1) << 24);
+ regVal |= (((ant_div_control1 >> 1) & 0x1) << 29);
+ regVal |= (((ant_div_control1 >> 2) & 0x1) << 30);
+ regVal |= ((ant_div_control2 & 0x3) << 25);
+ regVal |= (((ant_div_control2 >> 2) & 0x3) << 27);
+ REG_WRITE(ah, 0x99ac, regVal);
+ regVal = REG_READ(ah, 0x99ac);
+ regVal = REG_READ(ah, 0xa208);
+ regVal &= (~(0x1 << 13));
+ regVal |= (((ant_div_control1 >> 3) & 0x1) << 13);
+ REG_WRITE(ah, 0xa208, regVal);
+ regVal = REG_READ(ah, 0xa208);
+ }
+
+ if (pModal->version >= 2) {
+ ob[0] = (pModal->ob_01 & 0xf);
+ ob[1] = (pModal->ob_01 >> 4) & 0xf;
+ ob[2] = (pModal->ob_234 & 0xf);
+ ob[3] = ((pModal->ob_234 >> 4) & 0xf);
+ ob[4] = ((pModal->ob_234 >> 8) & 0xf);
+
+ db1[0] = (pModal->db1_01 & 0xf);
+ db1[1] = ((pModal->db1_01 >> 4) & 0xf);
+ db1[2] = (pModal->db1_234 & 0xf);
+ db1[3] = ((pModal->db1_234 >> 4) & 0xf);
+ db1[4] = ((pModal->db1_234 >> 8) & 0xf);
+
+ db2[0] = (pModal->db2_01 & 0xf);
+ db2[1] = ((pModal->db2_01 >> 4) & 0xf);
+ db2[2] = (pModal->db2_234 & 0xf);
+ db2[3] = ((pModal->db2_234 >> 4) & 0xf);
+ db2[4] = ((pModal->db2_234 >> 8) & 0xf);
+
+ } else if (pModal->version == 1) {
+
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "EEPROM Model version is set to 1 \n");
+ ob[0] = (pModal->ob_01 & 0xf);
+ ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf;
+ db1[0] = (pModal->db1_01 & 0xf);
+ db1[1] = db1[2] = db1[3] =
+ db1[4] = ((pModal->db1_01 >> 4) & 0xf);
+ db2[0] = (pModal->db2_01 & 0xf);
+ db2[1] = db2[2] = db2[3] =
+ db2[4] = ((pModal->db2_01 >> 4) & 0xf);
+ } else {
+ int i;
+ for (i = 0; i < 5; i++) {
+ ob[i] = pModal->ob_01;
+ db1[i] = pModal->db1_01;
+ db2[i] = pModal->db1_01;
+ }
+ }
+
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_OB_0, AR9285_AN_RF2G3_OB_0_S, ob[0]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_OB_1, AR9285_AN_RF2G3_OB_1_S, ob[1]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_OB_2, AR9285_AN_RF2G3_OB_2_S, ob[2]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_OB_3, AR9285_AN_RF2G3_OB_3_S, ob[3]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_OB_4, AR9285_AN_RF2G3_OB_4_S, ob[4]);
+
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_DB1_0, AR9285_AN_RF2G3_DB1_0_S, db1[0]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_DB1_1, AR9285_AN_RF2G3_DB1_1_S, db1[1]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3,
+ AR9285_AN_RF2G3_DB1_2, AR9285_AN_RF2G3_DB1_2_S, db1[2]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB1_3, AR9285_AN_RF2G4_DB1_3_S, db1[3]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB1_4, AR9285_AN_RF2G4_DB1_4_S, db1[4]);
+
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB2_0, AR9285_AN_RF2G4_DB2_0_S, db2[0]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB2_1, AR9285_AN_RF2G4_DB2_1_S, db2[1]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB2_2, AR9285_AN_RF2G4_DB2_2_S, db2[2]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB2_3, AR9285_AN_RF2G4_DB2_3_S, db2[3]);
+ ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4,
+ AR9285_AN_RF2G4_DB2_4, AR9285_AN_RF2G4_DB2_4_S, db2[4]);
+
+
+ if (AR_SREV_9285_11(ah))
+ REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT);
+
+ REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
+ pModal->switchSettling);
+ REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
+ pModal->adcDesiredSize);
+
+ REG_WRITE(ah, AR_PHY_RF_CTL4,
+ SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) |
+ SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) |
+ SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) |
+ SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON));
+
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
+ pModal->txEndToRxOn);
+ REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
+ pModal->thresh62);
+ REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62,
+ pModal->thresh62);
+
+ if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_2) {
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_DATA_START,
+ pModal->txFrameToDataStart);
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON,
+ pModal->txFrameToPaOn);
+ }
+
+ if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+ AR5416_EEP_MINOR_VER_3) {
+ if (IS_CHAN_HT40(chan))
+ REG_RMW_FIELD(ah, AR_PHY_SETTLING,
+ AR_PHY_SETTLING_SWITCH,
+ pModal->swSettleHt40);
+ }
+
+ return true;
+}
+
+static bool (*ath9k_eeprom_set_board_values[])(struct ath_hal *,
+ struct ath9k_channel *) = {
+ ath9k_hw_eeprom_set_def_board_values,
+ ath9k_hw_eeprom_set_4k_board_values
+};
+
+bool ath9k_hw_eeprom_set_board_values(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_eeprom_set_board_values[ahp->ah_eep_map](ah, chan);
+}
+
+static int ath9k_hw_get_def_eeprom_antenna_cfg(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u8 index, u16 *config)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ struct modal_eep_header *pModal =
+ &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+ struct base_eep_header *pBase = &eep->baseEepHeader;
+
+ switch (index) {
+ case 0:
+ *config = pModal->antCtrlCommon & 0xFFFF;
+ return 0;
+ case 1:
+ if (pBase->version >= 0x0E0D) {
+ if (pModal->useAnt1) {
+ *config =
+ ((pModal->antCtrlCommon & 0xFFFF0000) >> 16);
+ return 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ath9k_hw_get_4k_eeprom_antenna_cfg(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u8 index, u16 *config)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep = &ahp->ah_eeprom.map4k;
+ struct modal_eep_4k_header *pModal = &eep->modalHeader;
+
+ switch (index) {
+ case 0:
+ *config = pModal->antCtrlCommon & 0xFFFF;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int (*ath9k_get_eeprom_antenna_cfg[])(struct ath_hal *,
+ struct ath9k_channel *,
+ u8, u16 *) = {
+ ath9k_hw_get_def_eeprom_antenna_cfg,
+ ath9k_hw_get_4k_eeprom_antenna_cfg
+};
+
+int ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ u8 index, u16 *config)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_get_eeprom_antenna_cfg[ahp->ah_eep_map](ah, chan,
+ index, config);
+}
+
+static u8 ath9k_hw_get_4k_num_ant_config(struct ath_hal *ah,
+ enum ieee80211_band freq_band)
+{
+ return 1;
+}
+
+static u8 ath9k_hw_get_def_num_ant_config(struct ath_hal *ah,
+ enum ieee80211_band freq_band)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ struct modal_eep_header *pModal =
+ &(eep->modalHeader[ATH9K_HAL_FREQ_BAND_2GHZ == freq_band]);
+ struct base_eep_header *pBase = &eep->baseEepHeader;
+ u8 num_ant_config;
+
+ num_ant_config = 1;
+
+ if (pBase->version >= 0x0E0D)
+ if (pModal->useAnt1)
+ num_ant_config += 1;
+
+ return num_ant_config;
+}
+
+static u8 (*ath9k_get_num_ant_config[])(struct ath_hal *,
+ enum ieee80211_band) = {
+ ath9k_hw_get_def_num_ant_config,
+ ath9k_hw_get_4k_num_ant_config
+};
+
+u8 ath9k_hw_get_num_ant_config(struct ath_hal *ah,
+ enum ieee80211_band freq_band)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_get_num_ant_config[ahp->ah_eep_map](ah, freq_band);
+}
+
+u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah, u16 i, bool is2GHz)
+{
+#define EEP_MAP4K_SPURCHAN \
+ (ahp->ah_eeprom.map4k.modalHeader.spurChans[i].spurChan)
+#define EEP_DEF_SPURCHAN \
+ (ahp->ah_eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan)
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u16 spur_val = AR_NO_SPUR;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "Getting spur idx %d is2Ghz. %d val %x\n",
+ i, is2GHz, ah->ah_config.spurchans[i][is2GHz]);
+
+ switch (ah->ah_config.spurmode) {
+ case SPUR_DISABLE:
+ break;
+ case SPUR_ENABLE_IOCTL:
+ spur_val = ah->ah_config.spurchans[i][is2GHz];
+ DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ "Getting spur val from new loc. %d\n", spur_val);
+ break;
+ case SPUR_ENABLE_EEPROM:
+ if (ahp->ah_eep_map == EEP_MAP_4KBITS)
+ spur_val = EEP_MAP4K_SPURCHAN;
+ else
+ spur_val = EEP_DEF_SPURCHAN;
+ break;
+
+ }
+
+ return spur_val;
+#undef EEP_DEF_SPURCHAN
+#undef EEP_MAP4K_SPURCHAN
+}
+
+static u32 ath9k_hw_get_eeprom_4k(struct ath_hal *ah,
+ enum eeprom_param param)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_4k *eep = &ahp->ah_eeprom.map4k;
+ struct modal_eep_4k_header *pModal = &eep->modalHeader;
+ struct base_eep_header_4k *pBase = &eep->baseEepHeader;
+
+ switch (param) {
+ case EEP_NFTHRESH_2:
+ return pModal[1].noiseFloorThreshCh[0];
+ case AR_EEPROM_MAC(0):
+ return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+ case AR_EEPROM_MAC(1):
+ return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+ case AR_EEPROM_MAC(2):
+ return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+ case EEP_REG_0:
+ return pBase->regDmn[0];
+ case EEP_REG_1:
+ return pBase->regDmn[1];
+ case EEP_OP_CAP:
+ return pBase->deviceCap;
+ case EEP_OP_MODE:
+ return pBase->opCapFlags;
+ case EEP_RF_SILENT:
+ return pBase->rfSilent;
+ case EEP_OB_2:
+ return pModal->ob_01;
+ case EEP_DB_2:
+ return pModal->db1_01;
+ case EEP_MINOR_REV:
+ return pBase->version & AR5416_EEP_VER_MINOR_MASK;
+ case EEP_TX_MASK:
+ return pBase->txMask;
+ case EEP_RX_MASK:
+ return pBase->rxMask;
+ default:
+ return 0;
+ }
+}
+
+static u32 ath9k_hw_get_eeprom_def(struct ath_hal *ah,
+ enum eeprom_param param)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ar5416_eeprom_def *eep = &ahp->ah_eeprom.def;
+ struct modal_eep_header *pModal = eep->modalHeader;
+ struct base_eep_header *pBase = &eep->baseEepHeader;
+
+ switch (param) {
+ case EEP_NFTHRESH_5:
+ return pModal[0].noiseFloorThreshCh[0];
+ case EEP_NFTHRESH_2:
+ return pModal[1].noiseFloorThreshCh[0];
+ case AR_EEPROM_MAC(0):
+ return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+ case AR_EEPROM_MAC(1):
+ return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+ case AR_EEPROM_MAC(2):
+ return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+ case EEP_REG_0:
+ return pBase->regDmn[0];
+ case EEP_REG_1:
+ return pBase->regDmn[1];
+ case EEP_OP_CAP:
+ return pBase->deviceCap;
+ case EEP_OP_MODE:
+ return pBase->opCapFlags;
+ case EEP_RF_SILENT:
+ return pBase->rfSilent;
+ case EEP_OB_5:
+ return pModal[0].ob;
+ case EEP_DB_5:
+ return pModal[0].db;
+ case EEP_OB_2:
+ return pModal[1].ob;
+ case EEP_DB_2:
+ return pModal[1].db;
+ case EEP_MINOR_REV:
+ return pBase->version & AR5416_EEP_VER_MINOR_MASK;
+ case EEP_TX_MASK:
+ return pBase->txMask;
+ case EEP_RX_MASK:
+ return pBase->rxMask;
+ case EEP_RXGAIN_TYPE:
+ return pBase->rxGainType;
+ case EEP_TXGAIN_TYPE:
+ return pBase->txGainType;
+
+ default:
+ return 0;
+ }
+}
+
+static u32 (*ath9k_get_eeprom[])(struct ath_hal *, enum eeprom_param) = {
+ ath9k_hw_get_eeprom_def,
+ ath9k_hw_get_eeprom_4k
+};
+
+u32 ath9k_hw_get_eeprom(struct ath_hal *ah,
+ enum eeprom_param param)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ return ath9k_get_eeprom[ahp->ah_eep_map](ah, param);
+}
+
+int ath9k_hw_eeprom_attach(struct ath_hal *ah)
+{
+ int status;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ if (ath9k_hw_use_flash(ah))
+ ath9k_hw_flash_map(ah);
+
+ if (AR_SREV_9285(ah))
+ ahp->ah_eep_map = EEP_MAP_4KBITS;
+ else
+ ahp->ah_eep_map = EEP_MAP_DEFAULT;
+
+ if (!ath9k_hw_fill_eeprom(ah))
+ return -EIO;
+
+ status = ath9k_hw_check_eeprom(ah);
+
+ return status;
+}
diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c
index 98bc25c9b3cf..34474edefc97 100644
--- a/drivers/net/wireless/ath9k/hw.c
+++ b/drivers/net/wireless/ath9k/hw.c
@@ -23,277 +23,93 @@
#include "phy.h"
#include "initvals.h"
-static void ath9k_hw_iqcal_collect(struct ath_hal *ah);
-static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains);
-static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah);
-static void ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah,
- u8 numChains);
-static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah);
-static void ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah,
- u8 numChains);
-
static const u8 CLOCK_RATE[] = { 40, 80, 22, 44, 88, 40 };
-static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 };
-
-static const struct hal_percal_data iq_cal_multi_sample = {
- IQ_MISMATCH_CAL,
- MAX_CAL_SAMPLES,
- PER_MIN_LOG_COUNT,
- ath9k_hw_iqcal_collect,
- ath9k_hw_iqcalibrate
-};
-static const struct hal_percal_data iq_cal_single_sample = {
- IQ_MISMATCH_CAL,
- MIN_CAL_SAMPLES,
- PER_MAX_LOG_COUNT,
- ath9k_hw_iqcal_collect,
- ath9k_hw_iqcalibrate
-};
-static const struct hal_percal_data adc_gain_cal_multi_sample = {
- ADC_GAIN_CAL,
- MAX_CAL_SAMPLES,
- PER_MIN_LOG_COUNT,
- ath9k_hw_adc_gaincal_collect,
- ath9k_hw_adc_gaincal_calibrate
-};
-static const struct hal_percal_data adc_gain_cal_single_sample = {
- ADC_GAIN_CAL,
- MIN_CAL_SAMPLES,
- PER_MAX_LOG_COUNT,
- ath9k_hw_adc_gaincal_collect,
- ath9k_hw_adc_gaincal_calibrate
-};
-static const struct hal_percal_data adc_dc_cal_multi_sample = {
- ADC_DC_CAL,
- MAX_CAL_SAMPLES,
- PER_MIN_LOG_COUNT,
- ath9k_hw_adc_dccal_collect,
- ath9k_hw_adc_dccal_calibrate
-};
-static const struct hal_percal_data adc_dc_cal_single_sample = {
- ADC_DC_CAL,
- MIN_CAL_SAMPLES,
- PER_MAX_LOG_COUNT,
- ath9k_hw_adc_dccal_collect,
- ath9k_hw_adc_dccal_calibrate
-};
-static const struct hal_percal_data adc_init_dc_cal = {
- ADC_DC_INIT_CAL,
- MIN_CAL_SAMPLES,
- INIT_LOG_COUNT,
- ath9k_hw_adc_dccal_collect,
- ath9k_hw_adc_dccal_calibrate
-};
-
-static struct ath9k_rate_table ar5416_11a_table = {
- 8,
- {0},
- {
- {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0},
- {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0},
- {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2},
- {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2},
- {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4},
- {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4},
- {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4},
- {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4}
- },
-};
-
-static struct ath9k_rate_table ar5416_11b_table = {
- 4,
- {0},
- {
- {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
- {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
- {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 1},
- {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 1}
- },
-};
-
-static struct ath9k_rate_table ar5416_11g_table = {
- 12,
- {0},
- {
- {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
- {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
- {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2},
- {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3},
-
- {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4},
- {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4},
- {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6},
- {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6},
- {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8},
- {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8},
- {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8},
- {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8}
- },
-};
-
-static struct ath9k_rate_table ar5416_11ng_table = {
- 28,
- {0},
- {
- {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
- {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
- {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2},
- {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3},
-
- {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4},
- {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4},
- {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6},
- {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6},
- {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8},
- {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8},
- {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8},
- {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8},
- {true, PHY_HT, 6500, 0x80, 0x00, 0, 4},
- {true, PHY_HT, 13000, 0x81, 0x00, 1, 6},
- {true, PHY_HT, 19500, 0x82, 0x00, 2, 6},
- {true, PHY_HT, 26000, 0x83, 0x00, 3, 8},
- {true, PHY_HT, 39000, 0x84, 0x00, 4, 8},
- {true, PHY_HT, 52000, 0x85, 0x00, 5, 8},
- {true, PHY_HT, 58500, 0x86, 0x00, 6, 8},
- {true, PHY_HT, 65000, 0x87, 0x00, 7, 8},
- {true, PHY_HT, 13000, 0x88, 0x00, 8, 4},
- {true, PHY_HT, 26000, 0x89, 0x00, 9, 6},
- {true, PHY_HT, 39000, 0x8a, 0x00, 10, 6},
- {true, PHY_HT, 52000, 0x8b, 0x00, 11, 8},
- {true, PHY_HT, 78000, 0x8c, 0x00, 12, 8},
- {true, PHY_HT, 104000, 0x8d, 0x00, 13, 8},
- {true, PHY_HT, 117000, 0x8e, 0x00, 14, 8},
- {true, PHY_HT, 130000, 0x8f, 0x00, 15, 8},
- },
-};
-
-static struct ath9k_rate_table ar5416_11na_table = {
- 24,
- {0},
- {
- {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0},
- {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0},
- {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2},
- {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2},
- {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4},
- {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4},
- {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4},
- {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4},
- {true, PHY_HT, 6500, 0x80, 0x00, 0, 0},
- {true, PHY_HT, 13000, 0x81, 0x00, 1, 2},
- {true, PHY_HT, 19500, 0x82, 0x00, 2, 2},
- {true, PHY_HT, 26000, 0x83, 0x00, 3, 4},
- {true, PHY_HT, 39000, 0x84, 0x00, 4, 4},
- {true, PHY_HT, 52000, 0x85, 0x00, 5, 4},
- {true, PHY_HT, 58500, 0x86, 0x00, 6, 4},
- {true, PHY_HT, 65000, 0x87, 0x00, 7, 4},
- {true, PHY_HT, 13000, 0x88, 0x00, 8, 0},
- {true, PHY_HT, 26000, 0x89, 0x00, 9, 2},
- {true, PHY_HT, 39000, 0x8a, 0x00, 10, 2},
- {true, PHY_HT, 52000, 0x8b, 0x00, 11, 4},
- {true, PHY_HT, 78000, 0x8c, 0x00, 12, 4},
- {true, PHY_HT, 104000, 0x8d, 0x00, 13, 4},
- {true, PHY_HT, 117000, 0x8e, 0x00, 14, 4},
- {true, PHY_HT, 130000, 0x8f, 0x00, 15, 4},
- },
-};
-
-static enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah,
- const struct ath9k_channel *chan)
-{
- if (IS_CHAN_CCK(chan))
- return ATH9K_MODE_11A;
- if (IS_CHAN_G(chan))
- return ATH9K_MODE_11G;
- return ATH9K_MODE_11A;
-}
-static bool ath9k_hw_wait(struct ath_hal *ah,
- u32 reg,
- u32 mask,
- u32 val)
-{
- int i;
+extern struct hal_percal_data iq_cal_multi_sample;
+extern struct hal_percal_data iq_cal_single_sample;
+extern struct hal_percal_data adc_gain_cal_multi_sample;
+extern struct hal_percal_data adc_gain_cal_single_sample;
+extern struct hal_percal_data adc_dc_cal_multi_sample;
+extern struct hal_percal_data adc_dc_cal_single_sample;
+extern struct hal_percal_data adc_init_dc_cal;
- for (i = 0; i < (AH_TIMEOUT / AH_TIME_QUANTUM); i++) {
- if ((REG_READ(ah, reg) & mask) == val)
- return true;
+static bool ath9k_hw_set_reset_reg(struct ath_hal *ah, u32 type);
+static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan,
+ enum ath9k_ht_macmode macmode);
+static u32 ath9k_hw_ini_fixup(struct ath_hal *ah,
+ struct ar5416_eeprom_def *pEepData,
+ u32 reg, u32 value);
+static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, struct ath9k_channel *chan);
+static void ath9k_hw_spur_mitigate(struct ath_hal *ah, struct ath9k_channel *chan);
- udelay(AH_TIME_QUANTUM);
- }
- DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
- "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
- __func__, reg, REG_READ(ah, reg), mask, val);
- return false;
-}
+/********************/
+/* Helper Functions */
+/********************/
-static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off,
- u16 *data)
+static u32 ath9k_hw_mac_usec(struct ath_hal *ah, u32 clks)
{
- (void) REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
-
- if (!ath9k_hw_wait(ah,
- AR_EEPROM_STATUS_DATA,
- AR_EEPROM_STATUS_DATA_BUSY |
- AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) {
- return false;
- }
-
- *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
- AR_EEPROM_STATUS_DATA_VAL);
-
- return true;
+ if (ah->ah_curchan != NULL)
+ return clks / CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)];
+ else
+ return clks / CLOCK_RATE[ATH9K_MODE_11B];
}
-static int ath9k_hw_flash_map(struct ath_hal *ah)
+static u32 ath9k_hw_mac_to_usec(struct ath_hal *ah, u32 clks)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX);
+ struct ath9k_channel *chan = ah->ah_curchan;
- if (!ahp->ah_cal_mem) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: cannot remap eeprom region \n", __func__);
- return -EIO;
- }
+ if (chan && IS_CHAN_HT40(chan))
+ return ath9k_hw_mac_usec(ah, clks) / 2;
+ else
+ return ath9k_hw_mac_usec(ah, clks);
+}
- return 0;
+static u32 ath9k_hw_mac_clks(struct ath_hal *ah, u32 usecs)
+{
+ if (ah->ah_curchan != NULL)
+ return usecs * CLOCK_RATE[ath9k_hw_chan2wmode(ah,
+ ah->ah_curchan)];
+ else
+ return usecs * CLOCK_RATE[ATH9K_MODE_11B];
}
-static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off,
- u16 *data)
+static u32 ath9k_hw_mac_to_clks(struct ath_hal *ah, u32 usecs)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_channel *chan = ah->ah_curchan;
- *data = ioread16(ahp->ah_cal_mem + off);
- return true;
+ if (chan && IS_CHAN_HT40(chan))
+ return ath9k_hw_mac_clks(ah, usecs) * 2;
+ else
+ return ath9k_hw_mac_clks(ah, usecs);
}
-static void ath9k_hw_read_revisions(struct ath_hal *ah)
+enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah,
+ const struct ath9k_channel *chan)
{
- u32 val;
-
- val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
+ if (IS_CHAN_B(chan))
+ return ATH9K_MODE_11B;
+ if (IS_CHAN_G(chan))
+ return ATH9K_MODE_11G;
- if (val == 0xFF) {
- val = REG_READ(ah, AR_SREV);
+ return ATH9K_MODE_11A;
+}
- ah->ah_macVersion =
- (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S;
+bool ath9k_hw_wait(struct ath_hal *ah, u32 reg, u32 mask, u32 val)
+{
+ int i;
- ah->ah_macRev = MS(val, AR_SREV_REVISION2);
- ah->ah_isPciExpress =
- (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
+ for (i = 0; i < (AH_TIMEOUT / AH_TIME_QUANTUM); i++) {
+ if ((REG_READ(ah, reg) & mask) == val)
+ return true;
- } else {
- if (!AR_SREV_9100(ah))
- ah->ah_macVersion = MS(val, AR_SREV_VERSION);
+ udelay(AH_TIME_QUANTUM);
+ }
- ah->ah_macRev = val & AR_SREV_REVISION;
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
+ reg, REG_READ(ah, reg), mask, val);
- if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE)
- ah->ah_isPciExpress = true;
- }
+ return false;
}
u32 ath9k_hw_reverse_bits(u32 val, u32 n)
@@ -308,596 +124,215 @@ u32 ath9k_hw_reverse_bits(u32 val, u32 n)
return retval;
}
-static void ath9k_hw_set_defaults(struct ath_hal *ah)
+bool ath9k_get_channel_edges(struct ath_hal *ah,
+ u16 flags, u16 *low,
+ u16 *high)
{
- int i;
-
- ah->ah_config.dma_beacon_response_time = 2;
- ah->ah_config.sw_beacon_response_time = 10;
- ah->ah_config.additional_swba_backoff = 0;
- ah->ah_config.ack_6mb = 0x0;
- ah->ah_config.cwm_ignore_extcca = 0;
- ah->ah_config.pcie_powersave_enable = 0;
- ah->ah_config.pcie_l1skp_enable = 0;
- ah->ah_config.pcie_clock_req = 0;
- ah->ah_config.pcie_power_reset = 0x100;
- ah->ah_config.pcie_restore = 0;
- ah->ah_config.pcie_waen = 0;
- ah->ah_config.analog_shiftreg = 1;
- ah->ah_config.ht_enable = 1;
- ah->ah_config.ofdm_trig_low = 200;
- ah->ah_config.ofdm_trig_high = 500;
- ah->ah_config.cck_trig_high = 200;
- ah->ah_config.cck_trig_low = 100;
- ah->ah_config.enable_ani = 1;
- ah->ah_config.noise_immunity_level = 4;
- ah->ah_config.ofdm_weaksignal_det = 1;
- ah->ah_config.cck_weaksignal_thr = 0;
- ah->ah_config.spur_immunity_level = 2;
- ah->ah_config.firstep_level = 0;
- ah->ah_config.rssi_thr_high = 40;
- ah->ah_config.rssi_thr_low = 7;
- ah->ah_config.diversity_control = 0;
- ah->ah_config.antenna_switch_swap = 0;
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
- ah->ah_config.spurchans[i][0] = AR_NO_SPUR;
- ah->ah_config.spurchans[i][1] = AR_NO_SPUR;
+ if (flags & CHANNEL_5GHZ) {
+ *low = pCap->low_5ghz_chan;
+ *high = pCap->high_5ghz_chan;
+ return true;
}
-
- ah->ah_config.intr_mitigation = 0;
-}
-
-static void ath9k_hw_override_ini(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- if (!AR_SREV_5416_V20_OR_LATER(ah)
- || AR_SREV_9280_10_OR_LATER(ah))
- return;
-
- REG_WRITE(ah, 0x9800 + (651 << 2), 0x11);
-}
-
-static void ath9k_hw_init_bb(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- u32 synthDelay;
-
- synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
- if (IS_CHAN_CCK(chan))
- synthDelay = (4 * synthDelay) / 22;
- else
- synthDelay /= 10;
-
- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
-
- udelay(synthDelay + BASE_ACTIVATE_DELAY);
-}
-
-static void ath9k_hw_init_interrupt_masks(struct ath_hal *ah,
- enum ath9k_opmode opmode)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- ahp->ah_maskReg = AR_IMR_TXERR |
- AR_IMR_TXURN |
- AR_IMR_RXERR |
- AR_IMR_RXORN |
- AR_IMR_BCNMISC;
-
- if (ahp->ah_intrMitigation)
- ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
- else
- ahp->ah_maskReg |= AR_IMR_RXOK;
-
- ahp->ah_maskReg |= AR_IMR_TXOK;
-
- if (opmode == ATH9K_M_HOSTAP)
- ahp->ah_maskReg |= AR_IMR_MIB;
-
- REG_WRITE(ah, AR_IMR, ahp->ah_maskReg);
- REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT);
-
- if (!AR_SREV_9100(ah)) {
- REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
- REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT);
- REG_WRITE(ah, AR_INTR_SYNC_MASK, 0);
+ if ((flags & CHANNEL_2GHZ)) {
+ *low = pCap->low_2ghz_chan;
+ *high = pCap->high_2ghz_chan;
+ return true;
}
+ return false;
}
-static void ath9k_hw_init_qos(struct ath_hal *ah)
-{
- REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa);
- REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210);
-
- REG_WRITE(ah, AR_QOS_NO_ACK,
- SM(2, AR_QOS_NO_ACK_TWO_BIT) |
- SM(5, AR_QOS_NO_ACK_BIT_OFF) |
- SM(0, AR_QOS_NO_ACK_BYTE_OFF));
-
- REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL);
- REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF);
- REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF);
- REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF);
- REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
-}
-
-static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah,
- u32 reg,
- u32 mask,
- u32 shift,
- u32 val)
-{
- u32 regVal;
-
- regVal = REG_READ(ah, reg) & ~mask;
- regVal |= (val << shift) & mask;
-
- REG_WRITE(ah, reg, regVal);
-
- if (ah->ah_config.analog_shiftreg)
- udelay(100);
-
- return;
-}
-
-static u8 ath9k_hw_get_num_ant_config(struct ath_hal_5416 *ahp,
- enum ieee80211_band freq_band)
+u16 ath9k_hw_computetxtime(struct ath_hal *ah,
+ struct ath_rate_table *rates,
+ u32 frameLen, u16 rateix,
+ bool shortPreamble)
{
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- struct modal_eep_header *pModal =
- &(eep->modalHeader[IEEE80211_BAND_5GHZ == freq_band]);
- struct base_eep_header *pBase = &eep->baseEepHeader;
- u8 num_ant_config;
-
- num_ant_config = 1;
-
- if (pBase->version >= 0x0E0D)
- if (pModal->useAnt1)
- num_ant_config += 1;
-
- return num_ant_config;
-}
+ u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
+ u32 kbps;
-static int
-ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal_5416 *ahp,
- struct ath9k_channel *chan,
- u8 index,
- u16 *config)
-{
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- struct modal_eep_header *pModal =
- &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
- struct base_eep_header *pBase = &eep->baseEepHeader;
+ kbps = rates->info[rateix].ratekbps;
- switch (index) {
- case 0:
- *config = pModal->antCtrlCommon & 0xFFFF;
+ if (kbps == 0)
return 0;
- case 1:
- if (pBase->version >= 0x0E0D) {
- if (pModal->useAnt1) {
- *config =
- ((pModal->antCtrlCommon & 0xFFFF0000) >> 16);
- return 0;
- }
+
+ switch (rates->info[rateix].phy) {
+ case WLAN_RC_PHY_CCK:
+ phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
+ if (shortPreamble && rates->info[rateix].short_preamble)
+ phyTime >>= 1;
+ numBits = frameLen << 3;
+ txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps);
+ break;
+ case WLAN_RC_PHY_OFDM:
+ if (ah->ah_curchan && IS_CHAN_QUARTER_RATE(ah->ah_curchan)) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_QUARTER
+ + OFDM_PREAMBLE_TIME_QUARTER
+ + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
+ } else if (ah->ah_curchan &&
+ IS_CHAN_HALF_RATE(ah->ah_curchan)) {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME_HALF +
+ OFDM_PREAMBLE_TIME_HALF
+ + (numSymbols * OFDM_SYMBOL_TIME_HALF);
+ } else {
+ bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
+ txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME
+ + (numSymbols * OFDM_SYMBOL_TIME);
}
break;
default:
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "Unknown phy %u (rate ix %u)\n",
+ rates->info[rateix].phy, rateix);
+ txTime = 0;
break;
}
- return -EINVAL;
-}
-
-static inline bool ath9k_hw_nvram_read(struct ath_hal *ah,
- u32 off,
- u16 *data)
-{
- if (ath9k_hw_use_flash(ah))
- return ath9k_hw_flash_read(ah, off, data);
- else
- return ath9k_hw_eeprom_read(ah, off, data);
+ return txTime;
}
-static bool ath9k_hw_fill_eeprom(struct ath_hal *ah)
+u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- u16 *eep_data;
- int addr, ar5416_eep_start_loc = 0;
-
- if (!ath9k_hw_use_flash(ah)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: Reading from EEPROM, not flash\n", __func__);
- ar5416_eep_start_loc = 256;
- }
- if (AR_SREV_9100(ah))
- ar5416_eep_start_loc = 256;
-
- eep_data = (u16 *) eep;
- for (addr = 0;
- addr < sizeof(struct ar5416_eeprom) / sizeof(u16);
- addr++) {
- if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
- eep_data)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: Unable to read eeprom region \n",
- __func__);
- return false;
+ if (flags & CHANNEL_2GHZ) {
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ else
+ return 15 + ((freq - 2512) / 20);
+ } else if (flags & CHANNEL_5GHZ) {
+ if (ath9k_regd_is_public_safety_sku(ah) &&
+ IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
+ return ((freq * 10) +
+ (((freq % 5) == 2) ? 5 : 0) - 49400) / 5;
+ } else if ((flags & CHANNEL_A) && (freq <= 5000)) {
+ return (freq - 4000) / 5;
+ } else {
+ return (freq - 5000) / 5;
+ }
+ } else {
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ if (freq < 5000) {
+ if (ath9k_regd_is_public_safety_sku(ah)
+ && IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
+ return ((freq * 10) +
+ (((freq % 5) ==
+ 2) ? 5 : 0) - 49400) / 5;
+ } else if (freq > 4900) {
+ return (freq - 4000) / 5;
+ } else {
+ return 15 + ((freq - 2512) / 20);
+ }
}
- eep_data++;
+ return (freq - 5000) / 5;
}
- return true;
}
-/* XXX: Clean me up, make me more legible */
-static bool
-ath9k_hw_eeprom_set_board_values(struct ath_hal *ah,
- struct ath9k_channel *chan)
+void ath9k_hw_get_channel_centers(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ struct chan_centers *centers)
{
- struct modal_eep_header *pModal;
- int i, regChainOffset;
+ int8_t extoff;
struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- u8 txRxAttenLocal;
- u16 ant_config;
- pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+ if (!IS_CHAN_HT40(chan)) {
+ centers->ctl_center = centers->ext_center =
+ centers->synth_center = chan->channel;
+ return;
+ }
- txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44;
+ if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
+ (chan->chanmode == CHANNEL_G_HT40PLUS)) {
+ centers->synth_center =
+ chan->channel + HT40_CHANNEL_CENTER_SHIFT;
+ extoff = 1;
+ } else {
+ centers->synth_center =
+ chan->channel - HT40_CHANNEL_CENTER_SHIFT;
+ extoff = -1;
+ }
- ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, 1, &ant_config);
- REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
+ centers->ctl_center =
+ centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT);
+ centers->ext_center =
+ centers->synth_center + (extoff *
+ ((ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_20) ?
+ HT40_CHANNEL_CENTER_SHIFT : 15));
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- if (AR_SREV_9280(ah)) {
- if (i >= 2)
- break;
- }
+}
- if (AR_SREV_5416_V20_OR_LATER(ah) &&
- (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5)
- && (i != 0))
- regChainOffset = (i == 1) ? 0x2000 : 0x1000;
- else
- regChainOffset = i * 0x1000;
-
- REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
- pModal->antCtrlChain[i]);
-
- REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
- (REG_READ(ah,
- AR_PHY_TIMING_CTRL4(0) +
- regChainOffset) &
- ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
- SM(pModal->iqCalICh[i],
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
- SM(pModal->iqCalQCh[i],
- AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
-
- if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
- if ((eep->baseEepHeader.version &
- AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_3) {
- txRxAttenLocal = pModal->txRxAttenCh[i];
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
- pModal->
- bswMargin[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN1_DB,
- pModal->
- bswAtten[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
- pModal->
- xatten2Margin[i]);
- REG_RMW_FIELD(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- AR_PHY_GAIN_2GHZ_XATTEN2_DB,
- pModal->
- xatten2Db[i]);
- } else {
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
- | SM(pModal->
- bswMargin[i],
- AR_PHY_GAIN_2GHZ_BSW_MARGIN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
- | SM(pModal->bswAtten[i],
- AR_PHY_GAIN_2GHZ_BSW_ATTEN));
- }
- }
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- REG_RMW_FIELD(ah,
- AR_PHY_RXGAIN +
- regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_ATTEN,
- txRxAttenLocal);
- REG_RMW_FIELD(ah,
- AR_PHY_RXGAIN +
- regChainOffset,
- AR9280_PHY_RXGAIN_TXRX_MARGIN,
- pModal->rxTxMarginCh[i]);
- } else {
- REG_WRITE(ah,
- AR_PHY_RXGAIN + regChainOffset,
- (REG_READ(ah,
- AR_PHY_RXGAIN +
- regChainOffset) &
- ~AR_PHY_RXGAIN_TXRX_ATTEN) |
- SM(txRxAttenLocal,
- AR_PHY_RXGAIN_TXRX_ATTEN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset,
- (REG_READ(ah,
- AR_PHY_GAIN_2GHZ +
- regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
- SM(pModal->rxTxMarginCh[i],
- AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
- }
- }
- }
+/******************/
+/* Chip Revisions */
+/******************/
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- if (IS_CHAN_2GHZ(chan)) {
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
- AR_AN_RF2G1_CH0_OB,
- AR_AN_RF2G1_CH0_OB_S,
- pModal->ob);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
- AR_AN_RF2G1_CH0_DB,
- AR_AN_RF2G1_CH0_DB_S,
- pModal->db);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
- AR_AN_RF2G1_CH1_OB,
- AR_AN_RF2G1_CH1_OB_S,
- pModal->ob_ch1);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
- AR_AN_RF2G1_CH1_DB,
- AR_AN_RF2G1_CH1_DB_S,
- pModal->db_ch1);
- } else {
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
- AR_AN_RF5G1_CH0_OB5,
- AR_AN_RF5G1_CH0_OB5_S,
- pModal->ob);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
- AR_AN_RF5G1_CH0_DB5,
- AR_AN_RF5G1_CH0_DB5_S,
- pModal->db);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
- AR_AN_RF5G1_CH1_OB5,
- AR_AN_RF5G1_CH1_OB5_S,
- pModal->ob_ch1);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
- AR_AN_RF5G1_CH1_DB5,
- AR_AN_RF5G1_CH1_DB5_S,
- pModal->db_ch1);
- }
- ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
- AR_AN_TOP2_XPABIAS_LVL,
- AR_AN_TOP2_XPABIAS_LVL_S,
- pModal->xpaBiasLvl);
- ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
- AR_AN_TOP2_LOCALBIAS,
- AR_AN_TOP2_LOCALBIAS_S,
- pModal->local_bias);
- DPRINTF(ah->ah_sc, ATH_DBG_ANY, "ForceXPAon: %d\n",
- pModal->force_xpaon);
- REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG,
- pModal->force_xpaon);
- }
+static void ath9k_hw_read_revisions(struct ath_hal *ah)
+{
+ u32 val;
- REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
- pModal->switchSettling);
- REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
- pModal->adcDesiredSize);
+ val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
- if (!AR_SREV_9280_10_OR_LATER(ah))
- REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
- AR_PHY_DESIRED_SZ_PGA,
- pModal->pgaDesiredSize);
-
- REG_WRITE(ah, AR_PHY_RF_CTL4,
- SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF)
- | SM(pModal->txEndToXpaOff,
- AR_PHY_RF_CTL4_TX_END_XPAB_OFF)
- | SM(pModal->txFrameToXpaOn,
- AR_PHY_RF_CTL4_FRAME_XPAA_ON)
- | SM(pModal->txFrameToXpaOn,
- AR_PHY_RF_CTL4_FRAME_XPAB_ON));
-
- REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
- pModal->txEndToRxOn);
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
- pModal->thresh62);
- REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0,
- AR_PHY_EXT_CCA0_THRESH62,
- pModal->thresh62);
+ if (val == 0xFF) {
+ val = REG_READ(ah, AR_SREV);
+ ah->ah_macVersion = (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S;
+ ah->ah_macRev = MS(val, AR_SREV_REVISION2);
+ ah->ah_isPciExpress = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
} else {
- REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62,
- pModal->thresh62);
- REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
- AR_PHY_EXT_CCA_THRESH62,
- pModal->thresh62);
- }
+ if (!AR_SREV_9100(ah))
+ ah->ah_macVersion = MS(val, AR_SREV_VERSION);
- if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_2) {
- REG_RMW_FIELD(ah, AR_PHY_RF_CTL2,
- AR_PHY_TX_END_DATA_START,
- pModal->txFrameToDataStart);
- REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON,
- pModal->txFrameToPaOn);
- }
+ ah->ah_macRev = val & AR_SREV_REVISION;
- if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_3) {
- if (IS_CHAN_HT40(chan))
- REG_RMW_FIELD(ah, AR_PHY_SETTLING,
- AR_PHY_SETTLING_SWITCH,
- pModal->swSettleHt40);
+ if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE)
+ ah->ah_isPciExpress = true;
}
-
- return true;
}
-static int ath9k_hw_check_eeprom(struct ath_hal *ah)
+static int ath9k_hw_get_radiorev(struct ath_hal *ah)
{
- u32 sum = 0, el;
- u16 *eepdata;
+ u32 val;
int i;
- struct ath_hal_5416 *ahp = AH5416(ah);
- bool need_swap = false;
- struct ar5416_eeprom *eep =
- (struct ar5416_eeprom *) &ahp->ah_eeprom;
-
- if (!ath9k_hw_use_flash(ah)) {
- u16 magic, magic2;
- int addr;
-
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: Reading Magic # failed\n", __func__);
- return false;
- }
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "%s: Read Magic = 0x%04X\n",
- __func__, magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- need_swap = true;
- eepdata = (u16 *) (&ahp->ah_eeprom);
-
- for (addr = 0;
- addr <
- sizeof(struct ar5416_eeprom) /
- sizeof(u16); addr++) {
- u16 temp;
-
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
-
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "0x%04X ", *eepdata);
- if (((addr + 1) % 6) == 0)
- DPRINTF(ah->ah_sc,
- ATH_DBG_EEPROM,
- "\n");
- }
- } else {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Invalid EEPROM Magic. "
- "endianness missmatch.\n");
- return -EINVAL;
- }
- }
- }
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
- need_swap ? "True" : "False");
-
- if (need_swap)
- el = swab16(ahp->ah_eeprom.baseEepHeader.length);
- else
- el = ahp->ah_eeprom.baseEepHeader.length;
-
- if (el > sizeof(struct ar5416_eeprom))
- el = sizeof(struct ar5416_eeprom) / sizeof(u16);
- else
- el = el / sizeof(u16);
-
- eepdata = (u16 *) (&ahp->ah_eeprom);
-
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
-
- if (need_swap) {
- u32 integer, j;
- u16 word;
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Endianness is not native.. Changing \n");
-
- word = swab16(eep->baseEepHeader.length);
- eep->baseEepHeader.length = word;
-
- word = swab16(eep->baseEepHeader.checksum);
- eep->baseEepHeader.checksum = word;
-
- word = swab16(eep->baseEepHeader.version);
- eep->baseEepHeader.version = word;
-
- word = swab16(eep->baseEepHeader.regDmn[0]);
- eep->baseEepHeader.regDmn[0] = word;
-
- word = swab16(eep->baseEepHeader.regDmn[1]);
- eep->baseEepHeader.regDmn[1] = word;
-
- word = swab16(eep->baseEepHeader.rfSilent);
- eep->baseEepHeader.rfSilent = word;
-
- word = swab16(eep->baseEepHeader.blueToothOptions);
- eep->baseEepHeader.blueToothOptions = word;
+ REG_WRITE(ah, AR_PHY(0x36), 0x00007058);
- word = swab16(eep->baseEepHeader.deviceCap);
- eep->baseEepHeader.deviceCap = word;
+ for (i = 0; i < 8; i++)
+ REG_WRITE(ah, AR_PHY(0x20), 0x00010000);
+ val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff;
+ val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4);
- for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) {
- struct modal_eep_header *pModal =
- &eep->modalHeader[j];
- integer = swab32(pModal->antCtrlCommon);
- pModal->antCtrlCommon = integer;
+ return ath9k_hw_reverse_bits(val, 8);
+}
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- integer = swab32(pModal->antCtrlChain[i]);
- pModal->antCtrlChain[i] = integer;
- }
+/************************************/
+/* HW Attach, Detach, Init Routines */
+/************************************/
- for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
- word = swab16(pModal->spurChans[i].spurChan);
- pModal->spurChans[i].spurChan = word;
- }
- }
- }
+static void ath9k_hw_disablepcie(struct ath_hal *ah)
+{
+ if (!AR_SREV_9100(ah))
+ return;
- if (sum != 0xffff || ar5416_get_eep_ver(ahp) != AR5416_EEP_VER ||
- ar5416_get_eep_rev(ahp) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ar5416_get_eep_ver(ahp));
- return -EINVAL;
- }
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007);
- return 0;
+ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
}
static bool ath9k_hw_chip_test(struct ath_hal *ah)
@@ -905,9 +340,9 @@ static bool ath9k_hw_chip_test(struct ath_hal *ah)
u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) };
u32 regHold[2];
u32 patternData[4] = { 0x55555555,
- 0xaaaaaaaa,
- 0x66666666,
- 0x99999999 };
+ 0xaaaaaaaa,
+ 0x66666666,
+ 0x99999999 };
int i, j;
for (i = 0; i < 2; i++) {
@@ -921,9 +356,9 @@ static bool ath9k_hw_chip_test(struct ath_hal *ah)
rdData = REG_READ(ah, addr);
if (rdData != wrData) {
DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "%s: address test failed "
- "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
- __func__, addr, wrData, rdData);
+ "address test failed "
+ "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
+ addr, wrData, rdData);
return false;
}
}
@@ -933,9 +368,9 @@ static bool ath9k_hw_chip_test(struct ath_hal *ah)
rdData = REG_READ(ah, addr);
if (wrData != rdData) {
DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "%s: address test failed "
- "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
- __func__, addr, wrData, rdData);
+ "address test failed "
+ "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
+ addr, wrData, rdData);
return false;
}
}
@@ -945,213 +380,65 @@ static bool ath9k_hw_chip_test(struct ath_hal *ah)
return true;
}
-u32 ath9k_hw_getrxfilter(struct ath_hal *ah)
-{
- u32 bits = REG_READ(ah, AR_RX_FILTER);
- u32 phybits = REG_READ(ah, AR_PHY_ERR);
-
- if (phybits & AR_PHY_ERR_RADAR)
- bits |= ATH9K_RX_FILTER_PHYRADAR;
- if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING))
- bits |= ATH9K_RX_FILTER_PHYERR;
- return bits;
-}
-
-void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits)
-{
- u32 phybits;
-
- REG_WRITE(ah, AR_RX_FILTER, (bits & 0xffff) | AR_RX_COMPR_BAR);
- phybits = 0;
- if (bits & ATH9K_RX_FILTER_PHYRADAR)
- phybits |= AR_PHY_ERR_RADAR;
- if (bits & ATH9K_RX_FILTER_PHYERR)
- phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
- REG_WRITE(ah, AR_PHY_ERR, phybits);
-
- if (phybits)
- REG_WRITE(ah, AR_RXCFG,
- REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA);
- else
- REG_WRITE(ah, AR_RXCFG,
- REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA);
-}
-
-bool ath9k_hw_setcapability(struct ath_hal *ah,
- enum ath9k_capability_type type,
- u32 capability,
- u32 setting,
- int *status)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 v;
-
- switch (type) {
- case ATH9K_CAP_TKIP_MIC:
- if (setting)
- ahp->ah_staId1Defaults |=
- AR_STA_ID1_CRPT_MIC_ENABLE;
- else
- ahp->ah_staId1Defaults &=
- ~AR_STA_ID1_CRPT_MIC_ENABLE;
- return true;
- case ATH9K_CAP_DIVERSITY:
- v = REG_READ(ah, AR_PHY_CCK_DETECT);
- if (setting)
- v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- else
- v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
- REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
- return true;
- case ATH9K_CAP_MCAST_KEYSRCH:
- if (setting)
- ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH;
- else
- ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH;
- return true;
- case ATH9K_CAP_TSF_ADJUST:
- if (setting)
- ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF;
- else
- ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF;
- return true;
- default:
- return false;
- }
-}
-
-void ath9k_hw_dmaRegDump(struct ath_hal *ah)
+static const char *ath9k_hw_devname(u16 devid)
{
- u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
- int qcuOffset = 0, dcuOffset = 0;
- u32 *qcuBase = &val[0], *dcuBase = &val[4];
- int i;
-
- REG_WRITE(ah, AR_MACMISC,
- ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
- (AR_MACMISC_MISC_OBS_BUS_1 <<
- AR_MACMISC_MISC_OBS_BUS_MSB_S)));
-
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "Raw DMA Debug values:\n");
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
- if (i % 4 == 0)
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n");
-
- val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32)));
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "%d: %08x ", i, val[i]);
- }
-
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n\n");
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
-
- for (i = 0; i < ATH9K_NUM_QUEUES;
- i++, qcuOffset += 4, dcuOffset += 5) {
- if (i == 8) {
- qcuOffset = 0;
- qcuBase++;
- }
-
- if (i == 6) {
- dcuOffset = 0;
- dcuBase++;
- }
-
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "%2d %2x %1x %2x %2x\n",
- i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
- (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset +
- 3),
- val[2] & (0x7 << (i * 3)) >> (i * 3),
- (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+ switch (devid) {
+ case AR5416_DEVID_PCI:
+ return "Atheros 5416";
+ case AR5416_DEVID_PCIE:
+ return "Atheros 5418";
+ case AR9160_DEVID_PCI:
+ return "Atheros 9160";
+ case AR9280_DEVID_PCI:
+ case AR9280_DEVID_PCIE:
+ return "Atheros 9280";
+ case AR9285_DEVID_PCIE:
+ return "Atheros 9285";
}
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n");
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "qcu_stitch state: %2x qcu_fetch state: %2x\n",
- (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "qcu_complete state: %2x dcu_complete state: %2x\n",
- (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "dcu_arb state: %2x dcu_fp state: %2x\n",
- (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
- (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
- (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
- (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
-
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "pcu observe 0x%x \n",
- REG_READ(ah, AR_OBS_BUS_1));
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "AR_CR 0x%x \n", REG_READ(ah, AR_CR));
+ return NULL;
}
-u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
- u32 *rxc_pcnt,
- u32 *rxf_pcnt,
- u32 *txf_pcnt)
+static void ath9k_hw_set_defaults(struct ath_hal *ah)
{
- static u32 cycles, rx_clear, rx_frame, tx_frame;
- u32 good = 1;
+ int i;
- u32 rc = REG_READ(ah, AR_RCCNT);
- u32 rf = REG_READ(ah, AR_RFCNT);
- u32 tf = REG_READ(ah, AR_TFCNT);
- u32 cc = REG_READ(ah, AR_CCCNT);
+ ah->ah_config.dma_beacon_response_time = 2;
+ ah->ah_config.sw_beacon_response_time = 10;
+ ah->ah_config.additional_swba_backoff = 0;
+ ah->ah_config.ack_6mb = 0x0;
+ ah->ah_config.cwm_ignore_extcca = 0;
+ ah->ah_config.pcie_powersave_enable = 0;
+ ah->ah_config.pcie_l1skp_enable = 0;
+ ah->ah_config.pcie_clock_req = 0;
+ ah->ah_config.pcie_power_reset = 0x100;
+ ah->ah_config.pcie_restore = 0;
+ ah->ah_config.pcie_waen = 0;
+ ah->ah_config.analog_shiftreg = 1;
+ ah->ah_config.ht_enable = 1;
+ ah->ah_config.ofdm_trig_low = 200;
+ ah->ah_config.ofdm_trig_high = 500;
+ ah->ah_config.cck_trig_high = 200;
+ ah->ah_config.cck_trig_low = 100;
+ ah->ah_config.enable_ani = 1;
+ ah->ah_config.noise_immunity_level = 4;
+ ah->ah_config.ofdm_weaksignal_det = 1;
+ ah->ah_config.cck_weaksignal_thr = 0;
+ ah->ah_config.spur_immunity_level = 2;
+ ah->ah_config.firstep_level = 0;
+ ah->ah_config.rssi_thr_high = 40;
+ ah->ah_config.rssi_thr_low = 7;
+ ah->ah_config.diversity_control = 0;
+ ah->ah_config.antenna_switch_swap = 0;
- if (cycles == 0 || cycles > cc) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: cycle counter wrap. ExtBusy = 0\n",
- __func__);
- good = 0;
- } else {
- u32 cc_d = cc - cycles;
- u32 rc_d = rc - rx_clear;
- u32 rf_d = rf - rx_frame;
- u32 tf_d = tf - tx_frame;
-
- if (cc_d != 0) {
- *rxc_pcnt = rc_d * 100 / cc_d;
- *rxf_pcnt = rf_d * 100 / cc_d;
- *txf_pcnt = tf_d * 100 / cc_d;
- } else {
- good = 0;
- }
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
+ ah->ah_config.spurchans[i][0] = AR_NO_SPUR;
+ ah->ah_config.spurchans[i][1] = AR_NO_SPUR;
}
- cycles = cc;
- rx_frame = rf;
- rx_clear = rc;
- tx_frame = tf;
-
- return good;
+ ah->ah_config.intr_mitigation = 1;
}
-void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode)
-{
- u32 macmode;
-
- if (mode == ATH9K_HT_MACMODE_2040 &&
- !ah->ah_config.cwm_ignore_extcca)
- macmode = AR_2040_JOINED_RX_CLEAR;
- else
- macmode = 0;
-
- REG_WRITE(ah, AR_2040_MODE, macmode);
-}
-
-static void ath9k_hw_mark_phy_inactive(struct ath_hal *ah)
-{
- REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
-}
-
-
static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
struct ath_softc *sc,
void __iomem *mem,
@@ -1165,20 +452,16 @@ static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
ahp = kzalloc(sizeof(struct ath_hal_5416), GFP_KERNEL);
if (ahp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: cannot allocate memory for state block\n",
- __func__);
+ "Cannot allocate memory for state block\n");
*status = -ENOMEM;
return NULL;
}
ah = &ahp->ah;
-
ah->ah_sc = sc;
ah->ah_sh = mem;
-
ah->ah_magic = AR5416_MAGIC;
ah->ah_countryCode = CTRY_DEFAULT;
-
ah->ah_devid = devid;
ah->ah_subvendorid = 0;
@@ -1190,12 +473,10 @@ static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
ah->ah_powerLimit = MAX_RATE_POWER;
ah->ah_tpScale = ATH9K_TP_SCALE_MAX;
-
ahp->ah_atimWindow = 0;
ahp->ah_diversityControl = ah->ah_config.diversity_control;
ahp->ah_antennaSwitchSwap =
ah->ah_config.antenna_switch_swap;
-
ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE;
ahp->ah_beaconInterval = 100;
ahp->ah_enable32kHzClock = DONT_USE_32KHZ;
@@ -1210,163 +491,6 @@ static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
return ahp;
}
-static int ath9k_hw_eeprom_attach(struct ath_hal *ah)
-{
- int status;
-
- if (ath9k_hw_use_flash(ah))
- ath9k_hw_flash_map(ah);
-
- if (!ath9k_hw_fill_eeprom(ah))
- return -EIO;
-
- status = ath9k_hw_check_eeprom(ah);
-
- return status;
-}
-
-u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp,
- enum eeprom_param param)
-{
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- struct modal_eep_header *pModal = eep->modalHeader;
- struct base_eep_header *pBase = &eep->baseEepHeader;
-
- switch (param) {
- case EEP_NFTHRESH_5:
- return -pModal[0].noiseFloorThreshCh[0];
- case EEP_NFTHRESH_2:
- return -pModal[1].noiseFloorThreshCh[0];
- case AR_EEPROM_MAC(0):
- return pBase->macAddr[0] << 8 | pBase->macAddr[1];
- case AR_EEPROM_MAC(1):
- return pBase->macAddr[2] << 8 | pBase->macAddr[3];
- case AR_EEPROM_MAC(2):
- return pBase->macAddr[4] << 8 | pBase->macAddr[5];
- case EEP_REG_0:
- return pBase->regDmn[0];
- case EEP_REG_1:
- return pBase->regDmn[1];
- case EEP_OP_CAP:
- return pBase->deviceCap;
- case EEP_OP_MODE:
- return pBase->opCapFlags;
- case EEP_RF_SILENT:
- return pBase->rfSilent;
- case EEP_OB_5:
- return pModal[0].ob;
- case EEP_DB_5:
- return pModal[0].db;
- case EEP_OB_2:
- return pModal[1].ob;
- case EEP_DB_2:
- return pModal[1].db;
- case EEP_MINOR_REV:
- return pBase->version & AR5416_EEP_VER_MINOR_MASK;
- case EEP_TX_MASK:
- return pBase->txMask;
- case EEP_RX_MASK:
- return pBase->rxMask;
- default:
- return 0;
- }
-}
-
-static int ath9k_hw_get_radiorev(struct ath_hal *ah)
-{
- u32 val;
- int i;
-
- REG_WRITE(ah, AR_PHY(0x36), 0x00007058);
- for (i = 0; i < 8; i++)
- REG_WRITE(ah, AR_PHY(0x20), 0x00010000);
- val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff;
- val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4);
- return ath9k_hw_reverse_bits(val, 8);
-}
-
-static int ath9k_hw_init_macaddr(struct ath_hal *ah)
-{
- u32 sum;
- int i;
- u16 eeval;
- struct ath_hal_5416 *ahp = AH5416(ah);
- DECLARE_MAC_BUF(mac);
-
- sum = 0;
- for (i = 0; i < 3; i++) {
- eeval = ath9k_hw_get_eeprom(ahp, AR_EEPROM_MAC(i));
- sum += eeval;
- ahp->ah_macaddr[2 * i] = eeval >> 8;
- ahp->ah_macaddr[2 * i + 1] = eeval & 0xff;
- }
- if (sum == 0 || sum == 0xffff * 3) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: mac address read failed: %s\n", __func__,
- print_mac(mac, ahp->ah_macaddr));
- return -EADDRNOTAVAIL;
- }
-
- return 0;
-}
-
-static inline int16_t ath9k_hw_interpolate(u16 target,
- u16 srcLeft,
- u16 srcRight,
- int16_t targetLeft,
- int16_t targetRight)
-{
- int16_t rv;
-
- if (srcRight == srcLeft) {
- rv = targetLeft;
- } else {
- rv = (int16_t) (((target - srcLeft) * targetRight +
- (srcRight - target) * targetLeft) /
- (srcRight - srcLeft));
- }
- return rv;
-}
-
-static inline u16 ath9k_hw_fbin2freq(u8 fbin,
- bool is2GHz)
-{
-
- if (fbin == AR5416_BCHAN_UNUSED)
- return fbin;
-
- return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
-}
-
-static u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah,
- u16 i,
- bool is2GHz)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416_eeprom *eep =
- (struct ar5416_eeprom *) &ahp->ah_eeprom;
- u16 spur_val = AR_NO_SPUR;
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur idx %d is2Ghz. %d val %x\n",
- i, is2GHz, ah->ah_config.spurchans[i][is2GHz]);
-
- switch (ah->ah_config.spurmode) {
- case SPUR_DISABLE:
- break;
- case SPUR_ENABLE_IOCTL:
- spur_val = ah->ah_config.spurchans[i][is2GHz];
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur val from new loc. %d\n", spur_val);
- break;
- case SPUR_ENABLE_EEPROM:
- spur_val = eep->modalHeader[is2GHz].spurChans[i].spurChan;
- break;
-
- }
- return spur_val;
-}
-
static int ath9k_hw_rfattach(struct ath_hal *ah)
{
bool rfStatus = false;
@@ -1375,8 +499,7 @@ static int ath9k_hw_rfattach(struct ath_hal *ah)
rfStatus = ath9k_hw_init_rf(ah, &ecode);
if (!rfStatus) {
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: RF setup failed, status %u\n", __func__,
- ecode);
+ "RF setup failed, status %u\n", ecode);
return ecode;
}
@@ -1401,9 +524,9 @@ static int ath9k_hw_rf_claim(struct ath_hal *ah)
break;
default:
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: 5G Radio Chip Rev 0x%02X is not "
+ "5G Radio Chip Rev 0x%02X is not "
"supported by this driver\n",
- __func__, ah->ah_analog5GhzRev);
+ ah->ah_analog5GhzRev);
return -EOPNOTSUPP;
}
@@ -1412,1473 +535,76 @@ static int ath9k_hw_rf_claim(struct ath_hal *ah)
return 0;
}
-static void ath9k_hw_init_pll(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- u32 pll;
-
- if (AR_SREV_9100(ah)) {
- if (chan && IS_CHAN_5GHZ(chan))
- pll = 0x1450;
- else
- pll = 0x1458;
- } else {
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
-
- if (chan && IS_CHAN_HALF_RATE(chan))
- pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
- else if (chan && IS_CHAN_QUARTER_RATE(chan))
- pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
-
- if (chan && IS_CHAN_5GHZ(chan)) {
- pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
-
-
- if (AR_SREV_9280_20(ah)) {
- if (((chan->channel % 20) == 0)
- || ((chan->channel % 10) == 0))
- pll = 0x2850;
- else
- pll = 0x142c;
- }
- } else {
- pll |= SM(0x2c, AR_RTC_9160_PLL_DIV);
- }
-
- } else if (AR_SREV_9160_10_OR_LATER(ah)) {
-
- pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
-
- if (chan && IS_CHAN_HALF_RATE(chan))
- pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
- else if (chan && IS_CHAN_QUARTER_RATE(chan))
- pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
-
- if (chan && IS_CHAN_5GHZ(chan))
- pll |= SM(0x50, AR_RTC_9160_PLL_DIV);
- else
- pll |= SM(0x58, AR_RTC_9160_PLL_DIV);
- } else {
- pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2;
-
- if (chan && IS_CHAN_HALF_RATE(chan))
- pll |= SM(0x1, AR_RTC_PLL_CLKSEL);
- else if (chan && IS_CHAN_QUARTER_RATE(chan))
- pll |= SM(0x2, AR_RTC_PLL_CLKSEL);
-
- if (chan && IS_CHAN_5GHZ(chan))
- pll |= SM(0xa, AR_RTC_PLL_DIV);
- else
- pll |= SM(0xb, AR_RTC_PLL_DIV);
- }
- }
- REG_WRITE(ah, (u16) (AR_RTC_PLL_CONTROL), pll);
-
- udelay(RTC_PLL_SETTLE_DELAY);
-
- REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-}
-
-static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
-{
- u32 phymode;
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40
- | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH;
-
- if (IS_CHAN_HT40(chan)) {
- phymode |= AR_PHY_FC_DYN2040_EN;
-
- if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
- (chan->chanmode == CHANNEL_G_HT40PLUS))
- phymode |= AR_PHY_FC_DYN2040_PRI_CH;
-
- if (ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_25)
- phymode |= AR_PHY_FC_DYN2040_EXT_CH;
- }
- REG_WRITE(ah, AR_PHY_TURBO, phymode);
-
- ath9k_hw_set11nmac2040(ah, macmode);
-
- REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
- REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
-}
-
-static void ath9k_hw_set_operating_mode(struct ath_hal *ah, int opmode)
-{
- u32 val;
-
- val = REG_READ(ah, AR_STA_ID1);
- val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC);
- switch (opmode) {
- case ATH9K_M_HOSTAP:
- REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP
- | AR_STA_ID1_KSRCH_MODE);
- REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
- break;
- case ATH9K_M_IBSS:
- REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
- | AR_STA_ID1_KSRCH_MODE);
- REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
- break;
- case ATH9K_M_STA:
- case ATH9K_M_MONITOR:
- REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE);
- break;
- }
-}
-
-static void
-ath9k_hw_set_rfmode(struct ath_hal *ah, struct ath9k_channel *chan)
-{
- u32 rfMode = 0;
-
- if (chan == NULL)
- return;
-
- rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
- ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
-
- if (!AR_SREV_9280_10_OR_LATER(ah))
- rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ :
- AR_PHY_MODE_RF2GHZ;
-
- if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan))
- rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
-
- REG_WRITE(ah, AR_PHY_MODE, rfMode);
-}
-
-static bool ath9k_hw_set_reset(struct ath_hal *ah, int type)
-{
- u32 rst_flags;
- u32 tmpReg;
-
- REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
- AR_RTC_FORCE_WAKE_ON_INT);
-
- if (AR_SREV_9100(ah)) {
- rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD |
- AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET;
- } else {
- tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE);
- if (tmpReg &
- (AR_INTR_SYNC_LOCAL_TIMEOUT |
- AR_INTR_SYNC_RADM_CPL_TIMEOUT)) {
- REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
- REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
- } else {
- REG_WRITE(ah, AR_RC, AR_RC_AHB);
- }
-
- rst_flags = AR_RTC_RC_MAC_WARM;
- if (type == ATH9K_RESET_COLD)
- rst_flags |= AR_RTC_RC_MAC_COLD;
- }
-
- REG_WRITE(ah, (u16) (AR_RTC_RC), rst_flags);
- udelay(50);
-
- REG_WRITE(ah, (u16) (AR_RTC_RC), 0);
- if (!ath9k_hw_wait(ah, (u16) (AR_RTC_RC), AR_RTC_RC_M, 0)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: RTC stuck in MAC reset\n",
- __func__);
- return false;
- }
-
- if (!AR_SREV_9100(ah))
- REG_WRITE(ah, AR_RC, 0);
-
- ath9k_hw_init_pll(ah, NULL);
-
- if (AR_SREV_9100(ah))
- udelay(50);
-
- return true;
-}
-
-static bool ath9k_hw_set_reset_power_on(struct ath_hal *ah)
-{
- REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
- AR_RTC_FORCE_WAKE_ON_INT);
-
- REG_WRITE(ah, (u16) (AR_RTC_RESET), 0);
- REG_WRITE(ah, (u16) (AR_RTC_RESET), 1);
-
- if (!ath9k_hw_wait(ah,
- AR_RTC_STATUS,
- AR_RTC_STATUS_M,
- AR_RTC_STATUS_ON)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: RTC not waking up\n",
- __func__);
- return false;
- }
-
- ath9k_hw_read_revisions(ah);
-
- return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM);
-}
-
-static bool ath9k_hw_set_reset_reg(struct ath_hal *ah,
- u32 type)
-{
- REG_WRITE(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);
-
- switch (type) {
- case ATH9K_RESET_POWER_ON:
- return ath9k_hw_set_reset_power_on(ah);
- break;
- case ATH9K_RESET_WARM:
- case ATH9K_RESET_COLD:
- return ath9k_hw_set_reset(ah, type);
- break;
- default:
- return false;
- }
-}
-
-static
-struct ath9k_channel *ath9k_hw_check_chan(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- if (!(IS_CHAN_2GHZ(chan) ^ IS_CHAN_5GHZ(chan))) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u/0x%x; not marked as "
- "2GHz or 5GHz\n", __func__, chan->channel,
- chan->channelFlags);
- return NULL;
- }
-
- if (!IS_CHAN_OFDM(chan) &&
- !IS_CHAN_CCK(chan) &&
- !IS_CHAN_HT20(chan) &&
- !IS_CHAN_HT40(chan)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u/0x%x; not marked as "
- "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n",
- __func__, chan->channel, chan->channelFlags);
- return NULL;
- }
-
- return ath9k_regd_check_channel(ah, chan);
-}
-
-static inline bool
-ath9k_hw_get_lower_upper_index(u8 target,
- u8 *pList,
- u16 listSize,
- u16 *indexL,
- u16 *indexR)
-{
- u16 i;
-
- if (target <= pList[0]) {
- *indexL = *indexR = 0;
- return true;
- }
- if (target >= pList[listSize - 1]) {
- *indexL = *indexR = (u16) (listSize - 1);
- return true;
- }
-
- for (i = 0; i < listSize - 1; i++) {
- if (pList[i] == target) {
- *indexL = *indexR = i;
- return true;
- }
- if (target < pList[i + 1]) {
- *indexL = i;
- *indexR = (u16) (i + 1);
- return false;
- }
- }
- return false;
-}
-
-static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
-{
- int16_t nfval;
- int16_t sort[ATH9K_NF_CAL_HIST_MAX];
- int i, j;
-
- for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
- sort[i] = nfCalBuffer[i];
-
- for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
- for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
- if (sort[j] > sort[j - 1]) {
- nfval = sort[j];
- sort[j] = sort[j - 1];
- sort[j - 1] = nfval;
- }
- }
- }
- nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
-
- return nfval;
-}
-
-static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
- int16_t *nfarray)
+static int ath9k_hw_init_macaddr(struct ath_hal *ah)
{
+ u32 sum;
int i;
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
-
- if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
- h[i].currIndex = 0;
-
- if (h[i].invalidNFcount > 0) {
- if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE
- || nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
- h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
- } else {
- h[i].invalidNFcount--;
- h[i].privNF = nfarray[i];
- }
- } else {
- h[i].privNF =
- ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
- }
- }
- return;
-}
-
-static void ar5416GetNoiseFloor(struct ath_hal *ah,
- int16_t nfarray[NUM_NF_READINGS])
-{
- int16_t nf;
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
- else
- nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
-
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ctl] [chain 0] is %d\n", nf);
- nfarray[0] = nf;
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
- AR9280_PHY_CH1_MINCCA_PWR);
- else
- nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
- AR_PHY_CH1_MINCCA_PWR);
-
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "NF calibrated [ctl] [chain 1] is %d\n", nf);
- nfarray[1] = nf;
-
- if (!AR_SREV_9280(ah)) {
- nf = MS(REG_READ(ah, AR_PHY_CH2_CCA),
- AR_PHY_CH2_MINCCA_PWR);
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "NF calibrated [ctl] [chain 2] is %d\n", nf);
- nfarray[2] = nf;
- }
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
- AR9280_PHY_EXT_MINCCA_PWR);
- else
- nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
- AR_PHY_EXT_MINCCA_PWR);
-
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "NF calibrated [ext] [chain 0] is %d\n", nf);
- nfarray[3] = nf;
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
- AR9280_PHY_CH1_EXT_MINCCA_PWR);
- else
- nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
- AR_PHY_CH1_EXT_MINCCA_PWR);
-
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ext] [chain 1] is %d\n", nf);
- nfarray[4] = nf;
-
- if (!AR_SREV_9280(ah)) {
- nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA),
- AR_PHY_CH2_EXT_MINCCA_PWR);
- if (nf & 0x100)
- nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "NF calibrated [ext] [chain 2] is %d\n", nf);
- nfarray[5] = nf;
- }
-}
-
-static bool
-getNoiseFloorThresh(struct ath_hal *ah,
- const struct ath9k_channel *chan,
- int16_t *nft)
-{
+ u16 eeval;
struct ath_hal_5416 *ahp = AH5416(ah);
- switch (chan->chanmode) {
- case CHANNEL_A:
- case CHANNEL_A_HT20:
- case CHANNEL_A_HT40PLUS:
- case CHANNEL_A_HT40MINUS:
- *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_5);
- break;
- case CHANNEL_B:
- case CHANNEL_G:
- case CHANNEL_G_HT20:
- case CHANNEL_G_HT40PLUS:
- case CHANNEL_G_HT40MINUS:
- *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_2);
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel flags 0x%x\n", __func__,
- chan->channelFlags);
- return false;
- }
- return true;
-}
-
-static void ath9k_hw_start_nfcal(struct ath_hal *ah)
-{
- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
- AR_PHY_AGC_CONTROL_ENABLE_NF);
- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
- AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
-}
-
-static void
-ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
-{
- struct ath9k_nfcal_hist *h;
- int i, j;
- int32_t val;
- const u32 ar5416_cca_regs[6] = {
- AR_PHY_CCA,
- AR_PHY_CH1_CCA,
- AR_PHY_CH2_CCA,
- AR_PHY_EXT_CCA,
- AR_PHY_CH1_EXT_CCA,
- AR_PHY_CH2_EXT_CCA
- };
- u8 chainmask;
-
- if (AR_SREV_9280(ah))
- chainmask = 0x1B;
- else
- chainmask = 0x3F;
-
-#ifdef ATH_NF_PER_CHAN
- h = chan->nfCalHist;
-#else
- h = ah->nfCalHist;
-#endif
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- if (chainmask & (1 << i)) {
- val = REG_READ(ah, ar5416_cca_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
- REG_WRITE(ah, ar5416_cca_regs[i], val);
- }
- }
-
- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
- AR_PHY_AGC_CONTROL_ENABLE_NF);
- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
- AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
-
- for (j = 0; j < 1000; j++) {
- if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
- AR_PHY_AGC_CONTROL_NF) == 0)
- break;
- udelay(10);
- }
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- if (chainmask & (1 << i)) {
- val = REG_READ(ah, ar5416_cca_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) (-50) << 1) & 0x1ff);
- REG_WRITE(ah, ar5416_cca_regs[i], val);
- }
- }
-}
-
-static int16_t ath9k_hw_getnf(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- int16_t nf, nfThresh;
- int16_t nfarray[NUM_NF_READINGS] = { 0 };
- struct ath9k_nfcal_hist *h;
- u8 chainmask;
-
- if (AR_SREV_9280(ah))
- chainmask = 0x1B;
- else
- chainmask = 0x3F;
-
- chan->channelFlags &= (~CHANNEL_CW_INT);
- if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: NF did not complete in calibration window\n",
- __func__);
- nf = 0;
- chan->rawNoiseFloor = nf;
- return chan->rawNoiseFloor;
- } else {
- ar5416GetNoiseFloor(ah, nfarray);
- nf = nfarray[0];
- if (getNoiseFloorThresh(ah, chan, &nfThresh)
- && nf > nfThresh) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: noise floor failed detected; "
- "detected %d, threshold %d\n", __func__,
- nf, nfThresh);
- chan->channelFlags |= CHANNEL_CW_INT;
- }
+ sum = 0;
+ for (i = 0; i < 3; i++) {
+ eeval = ath9k_hw_get_eeprom(ah, AR_EEPROM_MAC(i));
+ sum += eeval;
+ ahp->ah_macaddr[2 * i] = eeval >> 8;
+ ahp->ah_macaddr[2 * i + 1] = eeval & 0xff;
}
-
-#ifdef ATH_NF_PER_CHAN
- h = chan->nfCalHist;
-#else
- h = ah->nfCalHist;
-#endif
-
- ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
- chan->rawNoiseFloor = h[0].privNF;
-
- return chan->rawNoiseFloor;
-}
-
-static void ath9k_hw_update_mibstats(struct ath_hal *ah,
- struct ath9k_mib_stats *stats)
-{
- stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
- stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
- stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
- stats->rts_good += REG_READ(ah, AR_RTS_OK);
- stats->beacons += REG_READ(ah, AR_BEACON_CNT);
-}
-
-static void ath9k_enable_mib_counters(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable mib counters\n");
-
- ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
-
- REG_WRITE(ah, AR_FILT_OFDM, 0);
- REG_WRITE(ah, AR_FILT_CCK, 0);
- REG_WRITE(ah, AR_MIBC,
- ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
- & 0x0f);
- REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
- REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-}
-
-static void ath9k_hw_disable_mib_counters(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling MIB counters\n");
-
- REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC);
-
- ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
-
- REG_WRITE(ah, AR_FILT_OFDM, 0);
- REG_WRITE(ah, AR_FILT_CCK, 0);
-}
-
-static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
- if (ahp->ah_ani[i].c.channel == chan->channel)
- return i;
- if (ahp->ah_ani[i].c.channel == 0) {
- ahp->ah_ani[i].c.channel = chan->channel;
- ahp->ah_ani[i].c.channelFlags = chan->channelFlags;
- return i;
- }
+ if (sum == 0 || sum == 0xffff * 3) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "mac address read failed: %pM\n",
+ ahp->ah_macaddr);
+ return -EADDRNOTAVAIL;
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "No more channel states left. Using channel 0\n");
return 0;
}
-static void ath9k_hw_ani_attach(struct ath_hal *ah)
+static void ath9k_hw_init_rxgain_ini(struct ath_hal *ah)
{
+ u32 rxgain_type;
struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
- ahp->ah_hasHwPhyCounters = 1;
-
- memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani));
- for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
- ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
- ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
- ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
- ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
- ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
- ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
- ahp->ah_ani[i].ofdmWeakSigDetectOff =
- !ATH9K_ANI_USE_OFDM_WEAK_SIG;
- ahp->ah_ani[i].cckWeakSigThreshold =
- ATH9K_ANI_CCK_WEAK_SIG_THR;
- ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
- ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
- if (ahp->ah_hasHwPhyCounters) {
- ahp->ah_ani[i].ofdmPhyErrBase =
- AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
- ahp->ah_ani[i].cckPhyErrBase =
- AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
- }
- }
- if (ahp->ah_hasHwPhyCounters) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Setting OfdmErrBase = 0x%08x\n",
- ahp->ah_ani[0].ofdmPhyErrBase);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
- ahp->ah_ani[0].cckPhyErrBase);
-
- REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase);
- ath9k_enable_mib_counters(ah);
- }
- ahp->ah_aniPeriod = ATH9K_ANI_PERIOD;
- if (ah->ah_config.enable_ani)
- ahp->ah_procPhyErr |= HAL_PROCESS_ANI;
-}
-
-static void ath9k_hw_ani_setup(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
+ if (ath9k_hw_get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_17) {
+ rxgain_type = ath9k_hw_get_eeprom(ah, EEP_RXGAIN_TYPE);
- const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
- const int coarseHigh[] = { -14, -14, -14, -14, -12 };
- const int coarseLow[] = { -64, -64, -64, -64, -70 };
- const int firpwr[] = { -78, -78, -78, -78, -80 };
-
- for (i = 0; i < 5; i++) {
- ahp->ah_totalSizeDesired[i] = totalSizeDesired[i];
- ahp->ah_coarseHigh[i] = coarseHigh[i];
- ahp->ah_coarseLow[i] = coarseLow[i];
- ahp->ah_firpwr[i] = firpwr[i];
- }
-}
-
-static void ath9k_hw_ani_detach(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detaching Ani\n");
- if (ahp->ah_hasHwPhyCounters) {
- ath9k_hw_disable_mib_counters(ah);
- REG_WRITE(ah, AR_PHY_ERR_1, 0);
- REG_WRITE(ah, AR_PHY_ERR_2, 0);
- }
-}
-
-
-static bool ath9k_hw_ani_control(struct ath_hal *ah,
- enum ath9k_ani_cmd cmd, int param)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState = ahp->ah_curani;
-
- switch (cmd & ahp->ah_ani_function) {
- case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
- u32 level = param;
-
- if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: level out of range (%u > %u)\n",
- __func__, level,
- (unsigned) ARRAY_SIZE(ahp->
- ah_totalSizeDesired));
- return false;
- }
-
- REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
- AR_PHY_DESIRED_SZ_TOT_DES,
- ahp->ah_totalSizeDesired[level]);
- REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
- AR_PHY_AGC_CTL1_COARSE_LOW,
- ahp->ah_coarseLow[level]);
- REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
- AR_PHY_AGC_CTL1_COARSE_HIGH,
- ahp->ah_coarseHigh[level]);
- REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
- AR_PHY_FIND_SIG_FIRPWR,
- ahp->ah_firpwr[level]);
-
- if (level > aniState->noiseImmunityLevel)
- ahp->ah_stats.ast_ani_niup++;
- else if (level < aniState->noiseImmunityLevel)
- ahp->ah_stats.ast_ani_nidown++;
- aniState->noiseImmunityLevel = level;
- break;
- }
- case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
- const int m1ThreshLow[] = { 127, 50 };
- const int m2ThreshLow[] = { 127, 40 };
- const int m1Thresh[] = { 127, 0x4d };
- const int m2Thresh[] = { 127, 0x40 };
- const int m2CountThr[] = { 31, 16 };
- const int m2CountThrLow[] = { 63, 48 };
- u32 on = param ? 1 : 0;
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2_THRESH,
- m2Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2COUNT_THR,
- m2CountThr[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
- m2CountThrLow[on]);
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH,
- m2Thresh[on]);
-
- if (on)
- REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+ if (rxgain_type == AR5416_EEP_RXGAIN_13DB_BACKOFF)
+ INIT_INI_ARRAY(&ahp->ah_iniModesRxGain,
+ ar9280Modes_backoff_13db_rxgain_9280_2,
+ ARRAY_SIZE(ar9280Modes_backoff_13db_rxgain_9280_2), 6);
+ else if (rxgain_type == AR5416_EEP_RXGAIN_23DB_BACKOFF)
+ INIT_INI_ARRAY(&ahp->ah_iniModesRxGain,
+ ar9280Modes_backoff_23db_rxgain_9280_2,
+ ARRAY_SIZE(ar9280Modes_backoff_23db_rxgain_9280_2), 6);
else
- REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
-
- if (!on != aniState->ofdmWeakSigDetectOff) {
- if (on)
- ahp->ah_stats.ast_ani_ofdmon++;
- else
- ahp->ah_stats.ast_ani_ofdmoff++;
- aniState->ofdmWeakSigDetectOff = !on;
- }
- break;
- }
- case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{
- const int weakSigThrCck[] = { 8, 6 };
- u32 high = param ? 1 : 0;
-
- REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
- AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK,
- weakSigThrCck[high]);
- if (high != aniState->cckWeakSigThreshold) {
- if (high)
- ahp->ah_stats.ast_ani_cckhigh++;
- else
- ahp->ah_stats.ast_ani_ccklow++;
- aniState->cckWeakSigThreshold = high;
- }
- break;
- }
- case ATH9K_ANI_FIRSTEP_LEVEL:{
- const int firstep[] = { 0, 4, 8 };
- u32 level = param;
-
- if (level >= ARRAY_SIZE(firstep)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: level out of range (%u > %u)\n",
- __func__, level,
- (unsigned) ARRAY_SIZE(firstep));
- return false;
- }
- REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
- AR_PHY_FIND_SIG_FIRSTEP,
- firstep[level]);
- if (level > aniState->firstepLevel)
- ahp->ah_stats.ast_ani_stepup++;
- else if (level < aniState->firstepLevel)
- ahp->ah_stats.ast_ani_stepdown++;
- aniState->firstepLevel = level;
- break;
- }
- case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
- const int cycpwrThr1[] =
- { 2, 4, 6, 8, 10, 12, 14, 16 };
- u32 level = param;
-
- if (level >= ARRAY_SIZE(cycpwrThr1)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: level out of range (%u > %u)\n",
- __func__, level,
- (unsigned)
- ARRAY_SIZE(cycpwrThr1));
- return false;
- }
- REG_RMW_FIELD(ah, AR_PHY_TIMING5,
- AR_PHY_TIMING5_CYCPWR_THR1,
- cycpwrThr1[level]);
- if (level > aniState->spurImmunityLevel)
- ahp->ah_stats.ast_ani_spurup++;
- else if (level < aniState->spurImmunityLevel)
- ahp->ah_stats.ast_ani_spurdown++;
- aniState->spurImmunityLevel = level;
- break;
- }
- case ATH9K_ANI_PRESENT:
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: invalid cmd %u\n", __func__, cmd);
- return false;
- }
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "%s: ANI parameters:\n", __func__);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
- "ofdmWeakSigDetectOff=%d\n",
- aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
- !aniState->ofdmWeakSigDetectOff);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "cckWeakSigThreshold=%d, "
- "firstepLevel=%d, listenTime=%d\n",
- aniState->cckWeakSigThreshold, aniState->firstepLevel,
- aniState->listenTime);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
- aniState->cycleCount, aniState->ofdmPhyErrCount,
- aniState->cckPhyErrCount);
- return true;
-}
-
-static void ath9k_ani_restart(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState;
-
- if (!DO_ANI(ah))
- return;
-
- aniState = ahp->ah_curani;
-
- aniState->listenTime = 0;
- if (ahp->ah_hasHwPhyCounters) {
- if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
- aniState->ofdmPhyErrBase = 0;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "OFDM Trigger is too high for hw counters\n");
- } else {
- aniState->ofdmPhyErrBase =
- AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
- }
- if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
- aniState->cckPhyErrBase = 0;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "CCK Trigger is too high for hw counters\n");
- } else {
- aniState->cckPhyErrBase =
- AR_PHY_COUNTMAX - aniState->cckTrigHigh;
- }
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: Writing ofdmbase=%u cckbase=%u\n",
- __func__, aniState->ofdmPhyErrBase,
- aniState->cckPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
- REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-
- ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
- }
- aniState->ofdmPhyErrCount = 0;
- aniState->cckPhyErrCount = 0;
-}
-
-static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *chan = ah->ah_curchan;
- struct ar5416AniState *aniState;
- enum wireless_mode mode;
- int32_t rssi;
-
- if (!DO_ANI(ah))
- return;
-
- aniState = ahp->ah_curani;
-
- if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
- if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
- aniState->noiseImmunityLevel + 1)) {
- return;
- }
- }
-
- if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
- if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
- aniState->spurImmunityLevel + 1)) {
- return;
- }
- }
-
- if (ah->ah_opmode == ATH9K_M_HOSTAP) {
- if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel + 1);
- }
- return;
- }
- rssi = BEACON_RSSI(ahp);
- if (rssi > aniState->rssiThrHigh) {
- if (!aniState->ofdmWeakSigDetectOff) {
- if (ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- false)) {
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
- 0);
- return;
- }
- }
- if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel + 1);
- return;
- }
- } else if (rssi > aniState->rssiThrLow) {
- if (aniState->ofdmWeakSigDetectOff)
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- true);
- if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel + 1);
- return;
- } else {
- mode = ath9k_hw_chan2wmode(ah, chan);
- if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) {
- if (!aniState->ofdmWeakSigDetectOff)
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- false);
- if (aniState->firstepLevel > 0)
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_FIRSTEP_LEVEL,
- 0);
- return;
- }
- }
-}
-
-static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *chan = ah->ah_curchan;
- struct ar5416AniState *aniState;
- enum wireless_mode mode;
- int32_t rssi;
-
- if (!DO_ANI(ah))
- return;
-
- aniState = ahp->ah_curani;
- if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
- if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
- aniState->noiseImmunityLevel + 1)) {
- return;
- }
- }
- if (ah->ah_opmode == ATH9K_M_HOSTAP) {
- if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel + 1);
- }
- return;
- }
- rssi = BEACON_RSSI(ahp);
- if (rssi > aniState->rssiThrLow) {
- if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel + 1);
- } else {
- mode = ath9k_hw_chan2wmode(ah, chan);
- if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) {
- if (aniState->firstepLevel > 0)
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_FIRSTEP_LEVEL,
- 0);
- }
- }
-}
-
-static void ath9k_ani_reset(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState;
- struct ath9k_channel *chan = ah->ah_curchan;
- int index;
-
- if (!DO_ANI(ah))
- return;
-
- index = ath9k_hw_get_ani_channel_idx(ah, chan);
- aniState = &ahp->ah_ani[index];
- ahp->ah_curani = aniState;
-
- if (DO_ANI(ah) && ah->ah_opmode != ATH9K_M_STA
- && ah->ah_opmode != ATH9K_M_IBSS) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: Reset ANI state opmode %u\n", __func__,
- ah->ah_opmode);
- ahp->ah_stats.ast_ani_reset++;
- ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
- ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- !ATH9K_ANI_USE_OFDM_WEAK_SIG);
- ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
- ATH9K_ANI_CCK_WEAK_SIG_THR);
- ath9k_hw_setrxfilter(ah,
- ath9k_hw_getrxfilter(ah) |
- ATH9K_RX_FILTER_PHYERR);
- if (ah->ah_opmode == ATH9K_M_HOSTAP) {
- ahp->ah_curani->ofdmTrigHigh =
- ah->ah_config.ofdm_trig_high;
- ahp->ah_curani->ofdmTrigLow =
- ah->ah_config.ofdm_trig_low;
- ahp->ah_curani->cckTrigHigh =
- ah->ah_config.cck_trig_high;
- ahp->ah_curani->cckTrigLow =
- ah->ah_config.cck_trig_low;
- }
- ath9k_ani_restart(ah);
- return;
- }
-
- if (aniState->noiseImmunityLevel != 0)
- ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
- aniState->noiseImmunityLevel);
- if (aniState->spurImmunityLevel != 0)
- ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
- aniState->spurImmunityLevel);
- if (aniState->ofdmWeakSigDetectOff)
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- !aniState->ofdmWeakSigDetectOff);
- if (aniState->cckWeakSigThreshold)
- ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
- aniState->cckWeakSigThreshold);
- if (aniState->firstepLevel != 0)
- ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel);
- if (ahp->ah_hasHwPhyCounters) {
- ath9k_hw_setrxfilter(ah,
- ath9k_hw_getrxfilter(ah) &
- ~ATH9K_RX_FILTER_PHYERR);
- ath9k_ani_restart(ah);
- REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
- REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
-
- } else {
- ath9k_ani_restart(ah);
- ath9k_hw_setrxfilter(ah,
- ath9k_hw_getrxfilter(ah) |
- ATH9K_RX_FILTER_PHYERR);
- }
-}
-
-/*
- * Process a MIB interrupt. We may potentially be invoked because
- * any of the MIB counters overflow/trigger so don't assume we're
- * here because a PHY error counter triggered.
- */
-void ath9k_hw_procmibevent(struct ath_hal *ah,
- const struct ath9k_node_stats *stats)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 phyCnt1, phyCnt2;
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Processing Mib Intr\n");
- /* Reset these counters regardless */
- REG_WRITE(ah, AR_FILT_OFDM, 0);
- REG_WRITE(ah, AR_FILT_CCK, 0);
- if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
- REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
-
- /* Clear the mib counters and save them in the stats */
- ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
- ahp->ah_stats.ast_nodestats = *stats;
-
- if (!DO_ANI(ah))
- return;
-
- /* NB: these are not reset-on-read */
- phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
- phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
- if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
- ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
- struct ar5416AniState *aniState = ahp->ah_curani;
- u32 ofdmPhyErrCnt, cckPhyErrCnt;
-
- /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
- ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
- ahp->ah_stats.ast_ani_ofdmerrs +=
- ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
- aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
-
- cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
- ahp->ah_stats.ast_ani_cckerrs +=
- cckPhyErrCnt - aniState->cckPhyErrCount;
- aniState->cckPhyErrCount = cckPhyErrCnt;
-
- /*
- * NB: figure out which counter triggered. If both
- * trigger we'll only deal with one as the processing
- * clobbers the error counter so the trigger threshold
- * check will never be true.
- */
- if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
- ath9k_hw_ani_ofdm_err_trigger(ah);
- if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
- ath9k_hw_ani_cck_err_trigger(ah);
- /* NB: always restart to insure the h/w counters are reset */
- ath9k_ani_restart(ah);
- }
-}
-
-static void ath9k_hw_ani_lower_immunity(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState;
- int32_t rssi;
-
- aniState = ahp->ah_curani;
-
- if (ah->ah_opmode == ATH9K_M_HOSTAP) {
- if (aniState->firstepLevel > 0) {
- if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel - 1)) {
- return;
- }
- }
- } else {
- rssi = BEACON_RSSI(ahp);
- if (rssi > aniState->rssiThrHigh) {
- /* XXX: Handle me */
- } else if (rssi > aniState->rssiThrLow) {
- if (aniState->ofdmWeakSigDetectOff) {
- if (ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- true) ==
- true) {
- return;
- }
- }
- if (aniState->firstepLevel > 0) {
- if (ath9k_hw_ani_control
- (ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel - 1) ==
- true) {
- return;
- }
- }
- } else {
- if (aniState->firstepLevel > 0) {
- if (ath9k_hw_ani_control
- (ah, ATH9K_ANI_FIRSTEP_LEVEL,
- aniState->firstepLevel - 1) ==
- true) {
- return;
- }
- }
- }
- }
-
- if (aniState->spurImmunityLevel > 0) {
- if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
- aniState->spurImmunityLevel - 1)) {
- return;
- }
- }
-
- if (aniState->noiseImmunityLevel > 0) {
- ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
- aniState->noiseImmunityLevel - 1);
- return;
- }
-}
-
-static int32_t ath9k_hw_ani_get_listen_time(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState;
- u32 txFrameCount, rxFrameCount, cycleCount;
- int32_t listenTime;
-
- txFrameCount = REG_READ(ah, AR_TFCNT);
- rxFrameCount = REG_READ(ah, AR_RFCNT);
- cycleCount = REG_READ(ah, AR_CCCNT);
-
- aniState = ahp->ah_curani;
- if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
-
- listenTime = 0;
- ahp->ah_stats.ast_ani_lzero++;
- } else {
- int32_t ccdelta = cycleCount - aniState->cycleCount;
- int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
- int32_t tfdelta = txFrameCount - aniState->txFrameCount;
- listenTime = (ccdelta - rfdelta - tfdelta) / 44000;
- }
- aniState->cycleCount = cycleCount;
- aniState->txFrameCount = txFrameCount;
- aniState->rxFrameCount = rxFrameCount;
-
- return listenTime;
+ INIT_INI_ARRAY(&ahp->ah_iniModesRxGain,
+ ar9280Modes_original_rxgain_9280_2,
+ ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6);
+ } else
+ INIT_INI_ARRAY(&ahp->ah_iniModesRxGain,
+ ar9280Modes_original_rxgain_9280_2,
+ ARRAY_SIZE(ar9280Modes_original_rxgain_9280_2), 6);
}
-void ath9k_hw_ani_monitor(struct ath_hal *ah,
- const struct ath9k_node_stats *stats,
- struct ath9k_channel *chan)
+static void ath9k_hw_init_txgain_ini(struct ath_hal *ah)
{
+ u32 txgain_type;
struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416AniState *aniState;
- int32_t listenTime;
-
- aniState = ahp->ah_curani;
- ahp->ah_stats.ast_nodestats = *stats;
-
- listenTime = ath9k_hw_ani_get_listen_time(ah);
- if (listenTime < 0) {
- ahp->ah_stats.ast_ani_lneg++;
- ath9k_ani_restart(ah);
- return;
- }
-
- aniState->listenTime += listenTime;
-
- if (ahp->ah_hasHwPhyCounters) {
- u32 phyCnt1, phyCnt2;
- u32 ofdmPhyErrCnt, cckPhyErrCnt;
-
- ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
-
- phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
- phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
-
- if (phyCnt1 < aniState->ofdmPhyErrBase ||
- phyCnt2 < aniState->cckPhyErrBase) {
- if (phyCnt1 < aniState->ofdmPhyErrBase) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: phyCnt1 0x%x, resetting "
- "counter value to 0x%x\n",
- __func__, phyCnt1,
- aniState->ofdmPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_1,
- aniState->ofdmPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_MASK_1,
- AR_PHY_ERR_OFDM_TIMING);
- }
- if (phyCnt2 < aniState->cckPhyErrBase) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "%s: phyCnt2 0x%x, resetting "
- "counter value to 0x%x\n",
- __func__, phyCnt2,
- aniState->cckPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_2,
- aniState->cckPhyErrBase);
- REG_WRITE(ah, AR_PHY_ERR_MASK_2,
- AR_PHY_ERR_CCK_TIMING);
- }
- return;
- }
-
- ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
- ahp->ah_stats.ast_ani_ofdmerrs +=
- ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
- aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
-
- cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
- ahp->ah_stats.ast_ani_cckerrs +=
- cckPhyErrCnt - aniState->cckPhyErrCount;
- aniState->cckPhyErrCount = cckPhyErrCnt;
- }
-
- if (!DO_ANI(ah))
- return;
-
- if (aniState->listenTime > 5 * ahp->ah_aniPeriod) {
- if (aniState->ofdmPhyErrCount <= aniState->listenTime *
- aniState->ofdmTrigLow / 1000 &&
- aniState->cckPhyErrCount <= aniState->listenTime *
- aniState->cckTrigLow / 1000)
- ath9k_hw_ani_lower_immunity(ah);
- ath9k_ani_restart(ah);
- } else if (aniState->listenTime > ahp->ah_aniPeriod) {
- if (aniState->ofdmPhyErrCount > aniState->listenTime *
- aniState->ofdmTrigHigh / 1000) {
- ath9k_hw_ani_ofdm_err_trigger(ah);
- ath9k_ani_restart(ah);
- } else if (aniState->cckPhyErrCount >
- aniState->listenTime * aniState->cckTrigHigh /
- 1000) {
- ath9k_hw_ani_cck_err_trigger(ah);
- ath9k_ani_restart(ah);
- }
- }
-}
-
-#ifndef ATH_NF_PER_CHAN
-static void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah)
-{
- int i, j;
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- ah->nfCalHist[i].currIndex = 0;
- ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE;
- ah->nfCalHist[i].invalidNFcount =
- AR_PHY_CCA_FILTERWINDOW_LENGTH;
- for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
- ah->nfCalHist[i].nfCalBuffer[j] =
- AR_PHY_CCA_MAX_GOOD_VALUE;
- }
- }
- return;
-}
-#endif
-
-static void ath9k_hw_gpio_cfg_output_mux(struct ath_hal *ah,
- u32 gpio, u32 type)
-{
- int addr;
- u32 gpio_shift, tmp;
-
- if (gpio > 11)
- addr = AR_GPIO_OUTPUT_MUX3;
- else if (gpio > 5)
- addr = AR_GPIO_OUTPUT_MUX2;
- else
- addr = AR_GPIO_OUTPUT_MUX1;
-
- gpio_shift = (gpio % 6) * 5;
-
- if (AR_SREV_9280_20_OR_LATER(ah)
- || (addr != AR_GPIO_OUTPUT_MUX1)) {
- REG_RMW(ah, addr, (type << gpio_shift),
- (0x1f << gpio_shift));
- } else {
- tmp = REG_READ(ah, addr);
- tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0);
- tmp &= ~(0x1f << gpio_shift);
- tmp |= (type << gpio_shift);
- REG_WRITE(ah, addr, tmp);
- }
-}
-
-void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio,
- u32 ah_signal_type)
-{
- u32 gpio_shift;
-
- ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type);
-
- gpio_shift = 2 * gpio;
-
- REG_RMW(ah,
- AR_GPIO_OE_OUT,
- (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift),
- (AR_GPIO_OE_OUT_DRV << gpio_shift));
-}
-
-void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 val)
-{
- REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
- AR_GPIO_BIT(gpio));
-}
-
-/*
- * Configure GPIO Input lines
- */
-void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio)
-{
- u32 gpio_shift;
-
- ASSERT(gpio < ah->ah_caps.num_gpio_pins);
-
- gpio_shift = gpio << 1;
-
- REG_RMW(ah,
- AR_GPIO_OE_OUT,
- (AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
- (AR_GPIO_OE_OUT_DRV << gpio_shift));
-}
-
-#ifdef CONFIG_RFKILL
-static void ath9k_enable_rfkill(struct ath_hal *ah)
-{
- REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
- AR_GPIO_INPUT_EN_VAL_RFSILENT_BB);
-
- REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2,
- AR_GPIO_INPUT_MUX2_RFSILENT);
- ath9k_hw_cfg_gpio_input(ah, ah->ah_rfkill_gpio);
- REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB);
-}
-#endif
-
-u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio)
-{
- if (gpio >= ah->ah_caps.num_gpio_pins)
- return 0xffffffff;
+ if (ath9k_hw_get_eeprom(ah, EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_19) {
+ txgain_type = ath9k_hw_get_eeprom(ah, EEP_TXGAIN_TYPE);
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- return (MS
- (REG_READ(ah, AR_GPIO_IN_OUT),
- AR928X_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0;
- } else {
- return (MS(REG_READ(ah, AR_GPIO_IN_OUT), AR_GPIO_IN_VAL) &
- AR_GPIO_BIT(gpio)) != 0;
- }
+ if (txgain_type == AR5416_EEP_TXGAIN_HIGH_POWER)
+ INIT_INI_ARRAY(&ahp->ah_iniModesTxGain,
+ ar9280Modes_high_power_tx_gain_9280_2,
+ ARRAY_SIZE(ar9280Modes_high_power_tx_gain_9280_2), 6);
+ else
+ INIT_INI_ARRAY(&ahp->ah_iniModesTxGain,
+ ar9280Modes_original_tx_gain_9280_2,
+ ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6);
+ } else
+ INIT_INI_ARRAY(&ahp->ah_iniModesTxGain,
+ ar9280Modes_original_tx_gain_9280_2,
+ ARRAY_SIZE(ar9280Modes_original_tx_gain_9280_2), 6);
}
static int ath9k_hw_post_attach(struct ath_hal *ah)
@@ -2887,7 +613,7 @@ static int ath9k_hw_post_attach(struct ath_hal *ah)
if (!ath9k_hw_chip_test(ah)) {
DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "%s: hardware self-test failed\n", __func__);
+ "hardware self-test failed\n");
return -ENODEV;
}
@@ -2906,365 +632,17 @@ static int ath9k_hw_post_attach(struct ath_hal *ah)
ath9k_hw_ani_setup(ah);
ath9k_hw_ani_attach(ah);
}
- return 0;
-}
-
-static u32 ath9k_hw_ini_fixup(struct ath_hal *ah,
- struct ar5416_eeprom *pEepData,
- u32 reg, u32 value)
-{
- struct base_eep_header *pBase = &(pEepData->baseEepHeader);
-
- switch (ah->ah_devid) {
- case AR9280_DEVID_PCI:
- if (reg == 0x7894) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "ini VAL: %x EEPROM: %x\n", value,
- (pBase->version & 0xff));
-
- if ((pBase->version & 0xff) > 0x0a) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "PWDCLKIND: %d\n",
- pBase->pwdclkind);
- value &= ~AR_AN_TOP2_PWDCLKIND;
- value |= AR_AN_TOP2_PWDCLKIND & (pBase->
- pwdclkind << AR_AN_TOP2_PWDCLKIND_S);
- } else {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "PWDCLKIND Earlier Rev\n");
- }
-
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "final ini VAL: %x\n", value);
- }
- break;
- }
- return value;
-}
-
-static bool ath9k_hw_fill_cap_info(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- u16 capField = 0, eeval;
-
- eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_0);
-
- ah->ah_currentRD = eeval;
-
- eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_1);
- ah->ah_currentRDExt = eeval;
-
- capField = ath9k_hw_get_eeprom(ahp, EEP_OP_CAP);
-
- if (ah->ah_opmode != ATH9K_M_HOSTAP &&
- ah->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) {
- if (ah->ah_currentRD == 0x64 || ah->ah_currentRD == 0x65)
- ah->ah_currentRD += 5;
- else if (ah->ah_currentRD == 0x41)
- ah->ah_currentRD = 0x43;
- DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: regdomain mapped to 0x%x\n", __func__,
- ah->ah_currentRD);
- }
-
- eeval = ath9k_hw_get_eeprom(ahp, EEP_OP_MODE);
- bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX);
-
- if (eeval & AR5416_OPFLAGS_11A) {
- set_bit(ATH9K_MODE_11A, pCap->wireless_modes);
- if (ah->ah_config.ht_enable) {
- if (!(eeval & AR5416_OPFLAGS_N_5G_HT20))
- set_bit(ATH9K_MODE_11NA_HT20,
- pCap->wireless_modes);
- if (!(eeval & AR5416_OPFLAGS_N_5G_HT40)) {
- set_bit(ATH9K_MODE_11NA_HT40PLUS,
- pCap->wireless_modes);
- set_bit(ATH9K_MODE_11NA_HT40MINUS,
- pCap->wireless_modes);
- }
- }
- }
-
- if (eeval & AR5416_OPFLAGS_11G) {
- set_bit(ATH9K_MODE_11B, pCap->wireless_modes);
- set_bit(ATH9K_MODE_11G, pCap->wireless_modes);
- if (ah->ah_config.ht_enable) {
- if (!(eeval & AR5416_OPFLAGS_N_2G_HT20))
- set_bit(ATH9K_MODE_11NG_HT20,
- pCap->wireless_modes);
- if (!(eeval & AR5416_OPFLAGS_N_2G_HT40)) {
- set_bit(ATH9K_MODE_11NG_HT40PLUS,
- pCap->wireless_modes);
- set_bit(ATH9K_MODE_11NG_HT40MINUS,
- pCap->wireless_modes);
- }
- }
- }
-
- pCap->tx_chainmask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK);
- if ((ah->ah_isPciExpress)
- || (eeval & AR5416_OPFLAGS_11A)) {
- pCap->rx_chainmask =
- ath9k_hw_get_eeprom(ahp, EEP_RX_MASK);
- } else {
- pCap->rx_chainmask =
- (ath9k_hw_gpio_get(ah, 0)) ? 0x5 : 0x7;
- }
-
- if (!(AR_SREV_9280(ah) && (ah->ah_macRev == 0)))
- ahp->ah_miscMode |= AR_PCU_MIC_NEW_LOC_ENA;
-
- pCap->low_2ghz_chan = 2312;
- pCap->high_2ghz_chan = 2732;
-
- pCap->low_5ghz_chan = 4920;
- pCap->high_5ghz_chan = 6100;
-
- pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP;
- pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP;
- pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM;
-
- pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP;
- pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP;
- pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM;
-
- pCap->hw_caps |= ATH9K_HW_CAP_CHAN_SPREAD;
-
- if (ah->ah_config.ht_enable)
- pCap->hw_caps |= ATH9K_HW_CAP_HT;
- else
- pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
-
- pCap->hw_caps |= ATH9K_HW_CAP_GTT;
- pCap->hw_caps |= ATH9K_HW_CAP_VEOL;
- pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK;
- pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH;
-
- if (capField & AR_EEPROM_EEPCAP_MAXQCU)
- pCap->total_queues =
- MS(capField, AR_EEPROM_EEPCAP_MAXQCU);
- else
- pCap->total_queues = ATH9K_NUM_TX_QUEUES;
-
- if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES)
- pCap->keycache_size =
- 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES);
- else
- pCap->keycache_size = AR_KEYTABLE_SIZE;
-
- pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
- pCap->num_mr_retries = 4;
- pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- pCap->num_gpio_pins = AR928X_NUM_GPIO;
- else
- pCap->num_gpio_pins = AR_NUM_GPIO;
-
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- pCap->hw_caps |= ATH9K_HW_CAP_WOW;
- pCap->hw_caps |= ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT;
- } else {
- pCap->hw_caps &= ~ATH9K_HW_CAP_WOW;
- pCap->hw_caps &= ~ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT;
- }
-
- if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) {
- pCap->hw_caps |= ATH9K_HW_CAP_CST;
- pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX;
- } else {
- pCap->rts_aggr_limit = (8 * 1024);
- }
-
- pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM;
-
-#ifdef CONFIG_RFKILL
- ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT);
- if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) {
- ah->ah_rfkill_gpio =
- MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL);
- ah->ah_rfkill_polarity =
- MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY);
-
- pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT;
- }
-#endif
-
- if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) ||
- (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) ||
- (ah->ah_macVersion == AR_SREV_VERSION_9160) ||
- (ah->ah_macVersion == AR_SREV_VERSION_9100) ||
- (ah->ah_macVersion == AR_SREV_VERSION_9280))
- pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP;
- else
- pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP;
-
- if (AR_SREV_9280(ah))
- pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS;
- else
- pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS;
-
- if (ah->ah_currentRDExt & (1 << REG_EXT_JAPAN_MIDBAND)) {
- pCap->reg_cap =
- AR_EEPROM_EEREGCAP_EN_KK_NEW_11A |
- AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
- AR_EEPROM_EEREGCAP_EN_KK_U2 |
- AR_EEPROM_EEREGCAP_EN_KK_MIDBAND;
- } else {
- pCap->reg_cap =
- AR_EEPROM_EEREGCAP_EN_KK_NEW_11A |
- AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN;
- }
-
- pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND;
-
- pCap->num_antcfg_5ghz =
- ath9k_hw_get_num_ant_config(ahp, IEEE80211_BAND_5GHZ);
- pCap->num_antcfg_2ghz =
- ath9k_hw_get_num_ant_config(ahp, IEEE80211_BAND_2GHZ);
-
- return true;
-}
-
-static void ar5416DisablePciePhy(struct ath_hal *ah)
-{
- if (!AR_SREV_9100(ah))
- return;
-
- REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
- REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007);
-
- REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
-}
-
-static void ath9k_set_power_sleep(struct ath_hal *ah, int setChip)
-{
- REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
- if (setChip) {
- REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN);
- if (!AR_SREV_9100(ah))
- REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
-
- REG_CLR_BIT(ah, (u16) (AR_RTC_RESET),
- AR_RTC_RESET_EN);
- }
-}
-
-static void ath9k_set_power_network_sleep(struct ath_hal *ah, int setChip)
-{
- REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
- if (setChip) {
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
-
- if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
- REG_WRITE(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_ON_INT);
- } else {
- REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN);
- }
- }
-}
-
-static bool ath9k_hw_set_power_awake(struct ath_hal *ah,
- int setChip)
-{
- u32 val;
- int i;
-
- if (setChip) {
- if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) ==
- AR_RTC_STATUS_SHUTDOWN) {
- if (ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)
- != true) {
- return false;
- }
- }
- if (AR_SREV_9100(ah))
- REG_SET_BIT(ah, AR_RTC_RESET,
- AR_RTC_RESET_EN);
-
- REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN);
- udelay(50);
-
- for (i = POWER_UP_TIME / 50; i > 0; i--) {
- val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
- if (val == AR_RTC_STATUS_ON)
- break;
- udelay(50);
- REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
- AR_RTC_FORCE_WAKE_EN);
- }
- if (i == 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- "%s: Failed to wakeup in %uus\n",
- __func__, POWER_UP_TIME / 20);
- return false;
- }
- }
-
- REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
- return true;
-}
-
-bool ath9k_hw_setpower(struct ath_hal *ah,
- enum ath9k_power_mode mode)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- static const char *modes[] = {
- "AWAKE",
- "FULL-SLEEP",
- "NETWORK SLEEP",
- "UNDEFINED"
- };
- int status = true, setChip = true;
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__,
- modes[ahp->ah_powerMode], modes[mode],
- setChip ? "set chip " : "");
-
- switch (mode) {
- case ATH9K_PM_AWAKE:
- status = ath9k_hw_set_power_awake(ah, setChip);
- break;
- case ATH9K_PM_FULL_SLEEP:
- ath9k_set_power_sleep(ah, setChip);
- ahp->ah_chipFullSleep = true;
- break;
- case ATH9K_PM_NETWORK_SLEEP:
- ath9k_set_power_network_sleep(ah, setChip);
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- "%s: unknown power mode %u\n", __func__, mode);
- return false;
- }
- ahp->ah_powerMode = mode;
- return status;
+ return 0;
}
-static struct ath_hal *ath9k_hw_do_attach(u16 devid,
- struct ath_softc *sc,
- void __iomem *mem,
- int *status)
+static struct ath_hal *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
+ void __iomem *mem, int *status)
{
struct ath_hal_5416 *ahp;
struct ath_hal *ah;
int ecode;
-#ifndef CONFIG_SLOW_ANT_DIV
- u32 i;
- u32 j;
-#endif
+ u32 i, j;
ahp = ath9k_hw_newstate(devid, sc, mem, status);
if (ahp == NULL)
@@ -3278,15 +656,13 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
ahp->ah_intrMitigation = true;
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't reset chip\n",
- __func__);
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "Couldn't reset chip\n");
ecode = -EIO;
goto bad;
}
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't wakeup chip\n",
- __func__);
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "Couldn't wakeup chip\n");
ecode = -EIO;
goto bad;
}
@@ -3300,18 +676,18 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
SER_REG_MODE_OFF;
}
}
+
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: serialize_regmode is %d\n",
- __func__, ah->ah_config.serialize_regmode);
+ "serialize_regmode is %d\n",
+ ah->ah_config.serialize_regmode);
if ((ah->ah_macVersion != AR_SREV_VERSION_5416_PCI) &&
(ah->ah_macVersion != AR_SREV_VERSION_5416_PCIE) &&
(ah->ah_macVersion != AR_SREV_VERSION_9160) &&
- (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah))) {
+ (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah)) && (!AR_SREV_9285(ah))) {
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: Mac Chip Rev 0x%02x.%x is not supported by "
- "this driver\n", __func__,
- ah->ah_macVersion, ah->ah_macRev);
+ "Mac Chip Rev 0x%02x.%x is not supported by "
+ "this driver\n", ah->ah_macVersion, ah->ah_macRev);
ecode = -EOPNOTSUPP;
goto bad;
}
@@ -3341,8 +717,7 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
ahp->ah_adcDcCalInitData.calData =
&adc_init_dc_cal;
}
- ahp->ah_suppCals =
- ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL;
+ ahp->ah_suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL;
}
if (AR_SREV_9160(ah)) {
@@ -3352,16 +727,46 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
} else {
ahp->ah_ani_function = ATH9K_ANI_ALL;
if (AR_SREV_9280_10_OR_LATER(ah)) {
- ahp->ah_ani_function &=
- ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL;
+ ahp->ah_ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL;
}
}
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: This Mac Chip Rev 0x%02x.%x is \n", __func__,
- ah->ah_macVersion, ah->ah_macRev);
+ "This Mac Chip Rev 0x%02x.%x is \n",
+ ah->ah_macVersion, ah->ah_macRev);
- if (AR_SREV_9280_20_OR_LATER(ah)) {
+ if (AR_SREV_9285_12_OR_LATER(ah)) {
+ INIT_INI_ARRAY(&ahp->ah_iniModes, ar9285Modes_9285_1_2,
+ ARRAY_SIZE(ar9285Modes_9285_1_2), 6);
+ INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9285Common_9285_1_2,
+ ARRAY_SIZE(ar9285Common_9285_1_2), 2);
+
+ if (ah->ah_config.pcie_clock_req) {
+ INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
+ ar9285PciePhy_clkreq_off_L1_9285_1_2,
+ ARRAY_SIZE(ar9285PciePhy_clkreq_off_L1_9285_1_2), 2);
+ } else {
+ INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
+ ar9285PciePhy_clkreq_always_on_L1_9285_1_2,
+ ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2),
+ 2);
+ }
+ } else if (AR_SREV_9285_10_OR_LATER(ah)) {
+ INIT_INI_ARRAY(&ahp->ah_iniModes, ar9285Modes_9285,
+ ARRAY_SIZE(ar9285Modes_9285), 6);
+ INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9285Common_9285,
+ ARRAY_SIZE(ar9285Common_9285), 2);
+
+ if (ah->ah_config.pcie_clock_req) {
+ INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
+ ar9285PciePhy_clkreq_off_L1_9285,
+ ARRAY_SIZE(ar9285PciePhy_clkreq_off_L1_9285), 2);
+ } else {
+ INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
+ ar9285PciePhy_clkreq_always_on_L1_9285,
+ ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285), 2);
+ }
+ } else if (AR_SREV_9280_20_OR_LATER(ah)) {
INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280_2,
ARRAY_SIZE(ar9280Modes_9280_2), 6);
INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280_2,
@@ -3369,21 +774,16 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
if (ah->ah_config.pcie_clock_req) {
INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
- ar9280PciePhy_clkreq_off_L1_9280,
- ARRAY_SIZE
- (ar9280PciePhy_clkreq_off_L1_9280),
- 2);
+ ar9280PciePhy_clkreq_off_L1_9280,
+ ARRAY_SIZE(ar9280PciePhy_clkreq_off_L1_9280),2);
} else {
INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes,
- ar9280PciePhy_clkreq_always_on_L1_9280,
- ARRAY_SIZE
- (ar9280PciePhy_clkreq_always_on_L1_9280),
- 2);
+ ar9280PciePhy_clkreq_always_on_L1_9280,
+ ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2);
}
INIT_INI_ARRAY(&ahp->ah_iniModesAdditional,
ar9280Modes_fast_clock_9280_2,
- ARRAY_SIZE(ar9280Modes_fast_clock_9280_2),
- 3);
+ ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3);
} else if (AR_SREV_9280_10_OR_LATER(ah)) {
INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280,
ARRAY_SIZE(ar9280Modes_9280), 6);
@@ -3469,13 +869,20 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
if (ah->ah_isPciExpress)
ath9k_hw_configpcipowersave(ah, 0);
else
- ar5416DisablePciePhy(ah);
+ ath9k_hw_disablepcie(ah);
ecode = ath9k_hw_post_attach(ah);
if (ecode != 0)
goto bad;
-#ifndef CONFIG_SLOW_ANT_DIV
+ /* rxgain table */
+ if (AR_SREV_9280_20(ah))
+ ath9k_hw_init_rxgain_ini(ah);
+
+ /* txgain table */
+ if (AR_SREV_9280_20(ah))
+ ath9k_hw_init_txgain_ini(ah);
+
if (ah->ah_devid == AR9280_DEVID_PCI) {
for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) {
u32 reg = INI_RA(&ahp->ah_iniModes, i, 0);
@@ -3484,16 +891,16 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
u32 val = INI_RA(&ahp->ah_iniModes, i, j);
INI_RA(&ahp->ah_iniModes, i, j) =
- ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom,
+ ath9k_hw_ini_fixup(ah,
+ &ahp->ah_eeprom.def,
reg, val);
}
}
}
-#endif
if (!ath9k_hw_fill_cap_info(ah)) {
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s:failed ath9k_hw_fill_cap_info\n", __func__);
+ "failed ath9k_hw_fill_cap_info\n");
ecode = -EINVAL;
goto bad;
}
@@ -3501,8 +908,7 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
ecode = ath9k_hw_init_macaddr(ah);
if (ecode != 0) {
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: failed initializing mac address\n",
- __func__);
+ "failed initializing mac address\n");
goto bad;
}
@@ -3511,1106 +917,569 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid,
else
ah->ah_txTrigLevel = (AR_FTRIG_512B >> AR_FTRIG_S);
-#ifndef ATH_NF_PER_CHAN
-
ath9k_init_nfcal_hist_buffer(ah);
-#endif
return ah;
-
bad:
if (ahp)
ath9k_hw_detach((struct ath_hal *) ahp);
if (status)
*status = ecode;
+
return NULL;
}
-void ath9k_hw_detach(struct ath_hal *ah)
+static void ath9k_hw_init_bb(struct ath_hal *ah,
+ struct ath9k_channel *chan)
{
- if (!AR_SREV_9100(ah))
- ath9k_hw_ani_detach(ah);
- ath9k_hw_rfdetach(ah);
+ u32 synthDelay;
- ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
- kfree(ah);
+ synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
+ if (IS_CHAN_B(chan))
+ synthDelay = (4 * synthDelay) / 22;
+ else
+ synthDelay /= 10;
+
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+ udelay(synthDelay + BASE_ACTIVATE_DELAY);
}
-bool ath9k_get_channel_edges(struct ath_hal *ah,
- u16 flags, u16 *low,
- u16 *high)
+static void ath9k_hw_init_qos(struct ath_hal *ah)
{
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa);
+ REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210);
- if (flags & CHANNEL_5GHZ) {
- *low = pCap->low_5ghz_chan;
- *high = pCap->high_5ghz_chan;
- return true;
- }
- if ((flags & CHANNEL_2GHZ)) {
- *low = pCap->low_2ghz_chan;
- *high = pCap->high_2ghz_chan;
+ REG_WRITE(ah, AR_QOS_NO_ACK,
+ SM(2, AR_QOS_NO_ACK_TWO_BIT) |
+ SM(5, AR_QOS_NO_ACK_BIT_OFF) |
+ SM(0, AR_QOS_NO_ACK_BYTE_OFF));
- return true;
- }
- return false;
+ REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL);
+ REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF);
+ REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF);
+ REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF);
+ REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
}
-static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin,
- u8 pwrMax,
- u8 *pPwrList,
- u8 *pVpdList,
- u16
- numIntercepts,
- u8 *pRetVpdList)
+static void ath9k_hw_init_pll(struct ath_hal *ah,
+ struct ath9k_channel *chan)
{
- u16 i, k;
- u8 currPwr = pwrMin;
- u16 idxL = 0, idxR = 0;
-
- for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
- ath9k_hw_get_lower_upper_index(currPwr, pPwrList,
- numIntercepts, &(idxL),
- &(idxR));
- if (idxR < 1)
- idxR = 1;
- if (idxL == numIntercepts - 1)
- idxL = (u16) (numIntercepts - 2);
- if (pPwrList[idxL] == pPwrList[idxR])
- k = pVpdList[idxL];
- else
- k = (u16) (((currPwr -
- pPwrList[idxL]) *
- pVpdList[idxR] +
- (pPwrList[idxR] -
- currPwr) * pVpdList[idxL]) /
- (pPwrList[idxR] -
- pPwrList[idxL]));
- pRetVpdList[i] = (u8) k;
- currPwr += 2;
- }
+ u32 pll;
- return true;
-}
+ if (AR_SREV_9100(ah)) {
+ if (chan && IS_CHAN_5GHZ(chan))
+ pll = 0x1450;
+ else
+ pll = 0x1458;
+ } else {
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
-static void
-ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hal *ah,
- struct ath9k_channel *chan,
- struct cal_data_per_freq *pRawDataSet,
- u8 *bChans,
- u16 availPiers,
- u16 tPdGainOverlap,
- int16_t *pMinCalPower,
- u16 *pPdGainBoundaries,
- u8 *pPDADCValues,
- u16 numXpdGains)
-{
- int i, j, k;
- int16_t ss;
- u16 idxL = 0, idxR = 0, numPiers;
- static u8 vpdTableL[AR5416_NUM_PD_GAINS]
- [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
- static u8 vpdTableR[AR5416_NUM_PD_GAINS]
- [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
- static u8 vpdTableI[AR5416_NUM_PD_GAINS]
- [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
-
- u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
- u8 minPwrT4[AR5416_NUM_PD_GAINS];
- u8 maxPwrT4[AR5416_NUM_PD_GAINS];
- int16_t vpdStep;
- int16_t tmpVal;
- u16 sizeCurrVpdTable, maxIndex, tgtIndex;
- bool match;
- int16_t minDelta = 0;
- struct chan_centers centers;
+ if (chan && IS_CHAN_HALF_RATE(chan))
+ pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
+ else if (chan && IS_CHAN_QUARTER_RATE(chan))
+ pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
- ath9k_hw_get_channel_centers(ah, chan, &centers);
+ if (chan && IS_CHAN_5GHZ(chan)) {
+ pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
- for (numPiers = 0; numPiers < availPiers; numPiers++) {
- if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
- break;
- }
- match = ath9k_hw_get_lower_upper_index((u8)
- FREQ2FBIN(centers.
- synth_center,
- IS_CHAN_2GHZ
- (chan)), bChans,
- numPiers, &idxL, &idxR);
-
- if (match) {
- for (i = 0; i < numXpdGains; i++) {
- minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0];
- maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4];
- ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
- pRawDataSet[idxL].
- pwrPdg[i],
- pRawDataSet[idxL].
- vpdPdg[i],
- AR5416_PD_GAIN_ICEPTS,
- vpdTableI[i]);
- }
- } else {
- for (i = 0; i < numXpdGains; i++) {
- pVpdL = pRawDataSet[idxL].vpdPdg[i];
- pPwrL = pRawDataSet[idxL].pwrPdg[i];
- pVpdR = pRawDataSet[idxR].vpdPdg[i];
- pPwrR = pRawDataSet[idxR].pwrPdg[i];
-
- minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
-
- maxPwrT4[i] =
- min(pPwrL[AR5416_PD_GAIN_ICEPTS - 1],
- pPwrR[AR5416_PD_GAIN_ICEPTS - 1]);
-
-
- ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
- pPwrL, pVpdL,
- AR5416_PD_GAIN_ICEPTS,
- vpdTableL[i]);
- ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
- pPwrR, pVpdR,
- AR5416_PD_GAIN_ICEPTS,
- vpdTableR[i]);
-
- for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
- vpdTableI[i][j] =
- (u8) (ath9k_hw_interpolate
- ((u16)
- FREQ2FBIN(centers.
- synth_center,
- IS_CHAN_2GHZ
- (chan)),
- bChans[idxL],
- bChans[idxR], vpdTableL[i]
- [j], vpdTableR[i]
- [j]));
+ if (AR_SREV_9280_20(ah)) {
+ if (((chan->channel % 20) == 0)
+ || ((chan->channel % 10) == 0))
+ pll = 0x2850;
+ else
+ pll = 0x142c;
+ }
+ } else {
+ pll |= SM(0x2c, AR_RTC_9160_PLL_DIV);
}
- }
- }
- *pMinCalPower = (int16_t) (minPwrT4[0] / 2);
-
- k = 0;
- for (i = 0; i < numXpdGains; i++) {
- if (i == (numXpdGains - 1))
- pPdGainBoundaries[i] =
- (u16) (maxPwrT4[i] / 2);
- else
- pPdGainBoundaries[i] =
- (u16) ((maxPwrT4[i] +
- minPwrT4[i + 1]) / 4);
+ } else if (AR_SREV_9160_10_OR_LATER(ah)) {
- pPdGainBoundaries[i] =
- min((u16) AR5416_MAX_RATE_POWER,
- pPdGainBoundaries[i]);
+ pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
- if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) {
- minDelta = pPdGainBoundaries[0] - 23;
- pPdGainBoundaries[0] = 23;
- } else {
- minDelta = 0;
- }
+ if (chan && IS_CHAN_HALF_RATE(chan))
+ pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
+ else if (chan && IS_CHAN_QUARTER_RATE(chan))
+ pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
- if (i == 0) {
- if (AR_SREV_9280_10_OR_LATER(ah))
- ss = (int16_t) (0 - (minPwrT4[i] / 2));
+ if (chan && IS_CHAN_5GHZ(chan))
+ pll |= SM(0x50, AR_RTC_9160_PLL_DIV);
else
- ss = 0;
+ pll |= SM(0x58, AR_RTC_9160_PLL_DIV);
} else {
- ss = (int16_t) ((pPdGainBoundaries[i - 1] -
- (minPwrT4[i] / 2)) -
- tPdGainOverlap + 1 + minDelta);
- }
- vpdStep = (int16_t) (vpdTableI[i][1] - vpdTableI[i][0]);
- vpdStep = (int16_t) ((vpdStep < 1) ? 1 : vpdStep);
-
- while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
- tmpVal = (int16_t) (vpdTableI[i][0] + ss * vpdStep);
- pPDADCValues[k++] =
- (u8) ((tmpVal < 0) ? 0 : tmpVal);
- ss++;
- }
-
- sizeCurrVpdTable =
- (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
- tgtIndex = (u8) (pPdGainBoundaries[i] + tPdGainOverlap -
- (minPwrT4[i] / 2));
- maxIndex = (tgtIndex <
- sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable;
+ pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2;
- while ((ss < maxIndex)
- && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
- pPDADCValues[k++] = vpdTableI[i][ss++];
- }
+ if (chan && IS_CHAN_HALF_RATE(chan))
+ pll |= SM(0x1, AR_RTC_PLL_CLKSEL);
+ else if (chan && IS_CHAN_QUARTER_RATE(chan))
+ pll |= SM(0x2, AR_RTC_PLL_CLKSEL);
- vpdStep = (int16_t) (vpdTableI[i][sizeCurrVpdTable - 1] -
- vpdTableI[i][sizeCurrVpdTable - 2]);
- vpdStep = (int16_t) ((vpdStep < 1) ? 1 : vpdStep);
-
- if (tgtIndex > maxIndex) {
- while ((ss <= tgtIndex)
- && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
- tmpVal = (int16_t) ((vpdTableI[i]
- [sizeCurrVpdTable -
- 1] + (ss - maxIndex +
- 1) * vpdStep));
- pPDADCValues[k++] = (u8) ((tmpVal >
- 255) ? 255 : tmpVal);
- ss++;
- }
+ if (chan && IS_CHAN_5GHZ(chan))
+ pll |= SM(0xa, AR_RTC_PLL_DIV);
+ else
+ pll |= SM(0xb, AR_RTC_PLL_DIV);
}
}
+ REG_WRITE(ah, (u16) (AR_RTC_PLL_CONTROL), pll);
- while (i < AR5416_PD_GAINS_IN_MASK) {
- pPdGainBoundaries[i] = pPdGainBoundaries[i - 1];
- i++;
- }
+ udelay(RTC_PLL_SETTLE_DELAY);
- while (k < AR5416_NUM_PDADC_VALUES) {
- pPDADCValues[k] = pPDADCValues[k - 1];
- k++;
- }
- return;
+ REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
}
-static bool
-ath9k_hw_set_power_cal_table(struct ath_hal *ah,
- struct ar5416_eeprom *pEepData,
- struct ath9k_channel *chan,
- int16_t *pTxPowerIndexOffset)
+static void ath9k_hw_init_chain_masks(struct ath_hal *ah)
{
- struct cal_data_per_freq *pRawDataset;
- u8 *pCalBChans = NULL;
- u16 pdGainOverlap_t2;
- static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
- u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
- u16 numPiers, i, j;
- int16_t tMinCalPower;
- u16 numXpdGain, xpdMask;
- u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
- u32 reg32, regOffset, regChainOffset;
- int16_t modalIdx;
struct ath_hal_5416 *ahp = AH5416(ah);
+ int rx_chainmask, tx_chainmask;
- modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0;
- xpdMask = pEepData->modalHeader[modalIdx].xpdGain;
-
- if ((pEepData->baseEepHeader.
- version & AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_2) {
- pdGainOverlap_t2 =
- pEepData->modalHeader[modalIdx].pdGainOverlap;
- } else {
- pdGainOverlap_t2 =
- (u16) (MS
- (REG_READ(ah, AR_PHY_TPCRG5),
- AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
- }
-
- if (IS_CHAN_2GHZ(chan)) {
- pCalBChans = pEepData->calFreqPier2G;
- numPiers = AR5416_NUM_2G_CAL_PIERS;
- } else {
- pCalBChans = pEepData->calFreqPier5G;
- numPiers = AR5416_NUM_5G_CAL_PIERS;
- }
-
- numXpdGain = 0;
+ rx_chainmask = ahp->ah_rxchainmask;
+ tx_chainmask = ahp->ah_txchainmask;
- for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
- if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) {
- if (numXpdGain >= AR5416_NUM_PD_GAINS)
- break;
- xpdGainValues[numXpdGain] =
- (u16) (AR5416_PD_GAINS_IN_MASK - i);
- numXpdGain++;
+ switch (rx_chainmask) {
+ case 0x5:
+ REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
+ AR_PHY_SWAP_ALT_CHAIN);
+ case 0x3:
+ if (((ah)->ah_macVersion <= AR_SREV_VERSION_9160)) {
+ REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7);
+ REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
+ break;
}
+ case 0x1:
+ case 0x2:
+ case 0x7:
+ REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask);
+ REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask);
+ break;
+ default:
+ break;
}
- REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
- (numXpdGain - 1) & 0x3);
- REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
- xpdGainValues[0]);
- REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
- xpdGainValues[1]);
- REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3,
- xpdGainValues[2]);
-
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- if (AR_SREV_5416_V20_OR_LATER(ah) &&
- (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5)
- && (i != 0)) {
- regChainOffset = (i == 1) ? 0x2000 : 0x1000;
- } else
- regChainOffset = i * 0x1000;
- if (pEepData->baseEepHeader.txMask & (1 << i)) {
- if (IS_CHAN_2GHZ(chan))
- pRawDataset = pEepData->calPierData2G[i];
- else
- pRawDataset = pEepData->calPierData5G[i];
-
- ath9k_hw_get_gain_boundaries_pdadcs(ah, chan,
- pRawDataset,
- pCalBChans,
- numPiers,
- pdGainOverlap_t2,
- &tMinCalPower,
- gainBoundaries,
- pdadcValues,
- numXpdGain);
-
- if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
-
- REG_WRITE(ah,
- AR_PHY_TPCRG5 + regChainOffset,
- SM(pdGainOverlap_t2,
- AR_PHY_TPCRG5_PD_GAIN_OVERLAP)
- | SM(gainBoundaries[0],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)
- | SM(gainBoundaries[1],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)
- | SM(gainBoundaries[2],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)
- | SM(gainBoundaries[3],
- AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
- }
-
- regOffset =
- AR_PHY_BASE + (672 << 2) + regChainOffset;
- for (j = 0; j < 32; j++) {
- reg32 =
- ((pdadcValues[4 * j + 0] & 0xFF) << 0)
- | ((pdadcValues[4 * j + 1] & 0xFF) <<
- 8) | ((pdadcValues[4 * j + 2] &
- 0xFF) << 16) |
- ((pdadcValues[4 * j + 3] & 0xFF) <<
- 24);
- REG_WRITE(ah, regOffset, reg32);
-
- DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
- "PDADC (%d,%4x): %4.4x %8.8x\n",
- i, regChainOffset, regOffset,
- reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
- "PDADC: Chain %d | PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | PDADC %3d Value %3d | "
- "PDADC %3d Value %3d |\n",
- i, 4 * j, pdadcValues[4 * j],
- 4 * j + 1, pdadcValues[4 * j + 1],
- 4 * j + 2, pdadcValues[4 * j + 2],
- 4 * j + 3,
- pdadcValues[4 * j + 3]);
-
- regOffset += 4;
- }
- }
+ REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask);
+ if (tx_chainmask == 0x5) {
+ REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
+ AR_PHY_SWAP_ALT_CHAIN);
}
- *pTxPowerIndexOffset = 0;
-
- return true;
+ if (AR_SREV_9100(ah))
+ REG_WRITE(ah, AR_PHY_ANALOG_SWAP,
+ REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001);
}
-void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore)
+static void ath9k_hw_init_interrupt_masks(struct ath_hal *ah,
+ enum nl80211_iftype opmode)
{
struct ath_hal_5416 *ahp = AH5416(ah);
- u8 i;
-
- if (ah->ah_isPciExpress != true)
- return;
- if (ah->ah_config.pcie_powersave_enable == 2)
- return;
+ ahp->ah_maskReg = AR_IMR_TXERR |
+ AR_IMR_TXURN |
+ AR_IMR_RXERR |
+ AR_IMR_RXORN |
+ AR_IMR_BCNMISC;
- if (restore)
- return;
+ if (ahp->ah_intrMitigation)
+ ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
+ else
+ ahp->ah_maskReg |= AR_IMR_RXOK;
- if (AR_SREV_9280_20_OR_LATER(ah)) {
- for (i = 0; i < ahp->ah_iniPcieSerdes.ia_rows; i++) {
- REG_WRITE(ah, INI_RA(&ahp->ah_iniPcieSerdes, i, 0),
- INI_RA(&ahp->ah_iniPcieSerdes, i, 1));
- }
- udelay(1000);
- } else if (AR_SREV_9280(ah)
- && (ah->ah_macRev == AR_SREV_REVISION_9280_10)) {
- REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
+ ahp->ah_maskReg |= AR_IMR_TXOK;
- REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820);
- REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560);
+ if (opmode == NL80211_IFTYPE_AP)
+ ahp->ah_maskReg |= AR_IMR_MIB;
- if (ah->ah_config.pcie_clock_req)
- REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc);
- else
- REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd);
+ REG_WRITE(ah, AR_IMR, ahp->ah_maskReg);
+ REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
- REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007);
+ if (!AR_SREV_9100(ah)) {
+ REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT);
+ REG_WRITE(ah, AR_INTR_SYNC_MASK, 0);
+ }
+}
- REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+static bool ath9k_hw_set_ack_timeout(struct ath_hal *ah, u32 us)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
- udelay(1000);
+ if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad ack timeout %u\n", us);
+ ahp->ah_acktimeout = (u32) -1;
+ return false;
} else {
- REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824);
- REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
- REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
- REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007);
- REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+ REG_RMW_FIELD(ah, AR_TIME_OUT,
+ AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us));
+ ahp->ah_acktimeout = us;
+ return true;
}
+}
- REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
+static bool ath9k_hw_set_cts_timeout(struct ath_hal *ah, u32 us)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
- if (ah->ah_config.pcie_waen) {
- REG_WRITE(ah, AR_WA, ah->ah_config.pcie_waen);
+ if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad cts timeout %u\n", us);
+ ahp->ah_ctstimeout = (u32) -1;
+ return false;
} else {
- if (AR_SREV_9280(ah))
- REG_WRITE(ah, AR_WA, 0x0040073f);
- else
- REG_WRITE(ah, AR_WA, 0x0000073f);
+ REG_RMW_FIELD(ah, AR_TIME_OUT,
+ AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us));
+ ahp->ah_ctstimeout = us;
+ return true;
}
}
-static void
-ath9k_hw_get_legacy_target_powers(struct ath_hal *ah,
- struct ath9k_channel *chan,
- struct cal_target_power_leg *powInfo,
- u16 numChannels,
- struct cal_target_power_leg *pNewPower,
- u16 numRates,
- bool isExtTarget)
+static bool ath9k_hw_set_global_txtimeout(struct ath_hal *ah, u32 tu)
{
- u16 clo, chi;
- int i;
- int matchIndex = -1, lowIndex = -1;
- u16 freq;
- struct chan_centers centers;
-
- ath9k_hw_get_channel_centers(ah, chan, &centers);
- freq = (isExtTarget) ? centers.ext_center : centers.ctl_center;
+ struct ath_hal_5416 *ahp = AH5416(ah);
- if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel,
- IS_CHAN_2GHZ(chan))) {
- matchIndex = 0;
+ if (tu > 0xFFFF) {
+ DPRINTF(ah->ah_sc, ATH_DBG_XMIT,
+ "bad global tx timeout %u\n", tu);
+ ahp->ah_globaltxtimeout = (u32) -1;
+ return false;
} else {
- for (i = 0; (i < numChannels)
- && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
- if (freq ==
- ath9k_hw_fbin2freq(powInfo[i].bChannel,
- IS_CHAN_2GHZ(chan))) {
- matchIndex = i;
- break;
- } else if ((freq <
- ath9k_hw_fbin2freq(powInfo[i].bChannel,
- IS_CHAN_2GHZ(chan)))
- && (freq >
- ath9k_hw_fbin2freq(powInfo[i - 1].
- bChannel,
- IS_CHAN_2GHZ
- (chan)))) {
- lowIndex = i - 1;
- break;
- }
- }
- if ((matchIndex == -1) && (lowIndex == -1))
- matchIndex = i - 1;
+ REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu);
+ ahp->ah_globaltxtimeout = tu;
+ return true;
}
+}
- if (matchIndex != -1) {
- *pNewPower = powInfo[matchIndex];
- } else {
- clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
- IS_CHAN_2GHZ(chan));
- chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
- IS_CHAN_2GHZ(chan));
-
- for (i = 0; i < numRates; i++) {
- pNewPower->tPow2x[i] =
- (u8) ath9k_hw_interpolate(freq, clo, chi,
- powInfo
- [lowIndex].
- tPow2x[i],
- powInfo
- [lowIndex +
- 1].tPow2x[i]);
- }
- }
+static void ath9k_hw_init_user_settings(struct ath_hal *ah)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "ahp->ah_miscMode 0x%x\n",
+ ahp->ah_miscMode);
+
+ if (ahp->ah_miscMode != 0)
+ REG_WRITE(ah, AR_PCU_MISC,
+ REG_READ(ah, AR_PCU_MISC) | ahp->ah_miscMode);
+ if (ahp->ah_slottime != (u32) -1)
+ ath9k_hw_setslottime(ah, ahp->ah_slottime);
+ if (ahp->ah_acktimeout != (u32) -1)
+ ath9k_hw_set_ack_timeout(ah, ahp->ah_acktimeout);
+ if (ahp->ah_ctstimeout != (u32) -1)
+ ath9k_hw_set_cts_timeout(ah, ahp->ah_ctstimeout);
+ if (ahp->ah_globaltxtimeout != (u32) -1)
+ ath9k_hw_set_global_txtimeout(ah, ahp->ah_globaltxtimeout);
}
-static void
-ath9k_hw_get_target_powers(struct ath_hal *ah,
- struct ath9k_channel *chan,
- struct cal_target_power_ht *powInfo,
- u16 numChannels,
- struct cal_target_power_ht *pNewPower,
- u16 numRates,
- bool isHt40Target)
+const char *ath9k_hw_probe(u16 vendorid, u16 devid)
{
- u16 clo, chi;
- int i;
- int matchIndex = -1, lowIndex = -1;
- u16 freq;
- struct chan_centers centers;
+ return vendorid == ATHEROS_VENDOR_ID ?
+ ath9k_hw_devname(devid) : NULL;
+}
- ath9k_hw_get_channel_centers(ah, chan, &centers);
- freq = isHt40Target ? centers.synth_center : centers.ctl_center;
+void ath9k_hw_detach(struct ath_hal *ah)
+{
+ if (!AR_SREV_9100(ah))
+ ath9k_hw_ani_detach(ah);
- if (freq <=
- ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) {
- matchIndex = 0;
- } else {
- for (i = 0; (i < numChannels)
- && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
- if (freq ==
- ath9k_hw_fbin2freq(powInfo[i].bChannel,
- IS_CHAN_2GHZ(chan))) {
- matchIndex = i;
- break;
- } else
- if ((freq <
- ath9k_hw_fbin2freq(powInfo[i].bChannel,
- IS_CHAN_2GHZ(chan)))
- && (freq >
- ath9k_hw_fbin2freq(powInfo[i - 1].
- bChannel,
- IS_CHAN_2GHZ
- (chan)))) {
- lowIndex = i - 1;
- break;
- }
- }
- if ((matchIndex == -1) && (lowIndex == -1))
- matchIndex = i - 1;
- }
+ ath9k_hw_rfdetach(ah);
+ ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+ kfree(ah);
+}
- if (matchIndex != -1) {
- *pNewPower = powInfo[matchIndex];
- } else {
- clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
- IS_CHAN_2GHZ(chan));
- chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
- IS_CHAN_2GHZ(chan));
-
- for (i = 0; i < numRates; i++) {
- pNewPower->tPow2x[i] =
- (u8) ath9k_hw_interpolate(freq, clo, chi,
- powInfo
- [lowIndex].
- tPow2x[i],
- powInfo
- [lowIndex +
- 1].tPow2x[i]);
- }
+struct ath_hal *ath9k_hw_attach(u16 devid, struct ath_softc *sc,
+ void __iomem *mem, int *error)
+{
+ struct ath_hal *ah = NULL;
+
+ switch (devid) {
+ case AR5416_DEVID_PCI:
+ case AR5416_DEVID_PCIE:
+ case AR9160_DEVID_PCI:
+ case AR9280_DEVID_PCI:
+ case AR9280_DEVID_PCIE:
+ case AR9285_DEVID_PCIE:
+ ah = ath9k_hw_do_attach(devid, sc, mem, error);
+ break;
+ default:
+ *error = -ENXIO;
+ break;
}
+
+ return ah;
}
-static u16
-ath9k_hw_get_max_edge_power(u16 freq,
- struct cal_ctl_edges *pRdEdgesPower,
- bool is2GHz)
+/*******/
+/* INI */
+/*******/
+
+static void ath9k_hw_override_ini(struct ath_hal *ah,
+ struct ath9k_channel *chan)
{
- u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
- int i;
+ /*
+ * Set the RX_ABORT and RX_DIS and clear if off only after
+ * RXE is set for MAC. This prevents frames with corrupted
+ * descriptor status.
+ */
+ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
- for (i = 0; (i < AR5416_NUM_BAND_EDGES)
- && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
- if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel,
- is2GHz)) {
- twiceMaxEdgePower = pRdEdgesPower[i].tPower;
- break;
- } else if ((i > 0)
- && (freq <
- ath9k_hw_fbin2freq(pRdEdgesPower[i].
- bChannel, is2GHz))) {
- if (ath9k_hw_fbin2freq
- (pRdEdgesPower[i - 1].bChannel, is2GHz) < freq
- && pRdEdgesPower[i - 1].flag) {
- twiceMaxEdgePower =
- pRdEdgesPower[i - 1].tPower;
+
+ if (!AR_SREV_5416_V20_OR_LATER(ah) ||
+ AR_SREV_9280_10_OR_LATER(ah))
+ return;
+
+ REG_WRITE(ah, 0x9800 + (651 << 2), 0x11);
+}
+
+static u32 ath9k_hw_def_ini_fixup(struct ath_hal *ah,
+ struct ar5416_eeprom_def *pEepData,
+ u32 reg, u32 value)
+{
+ struct base_eep_header *pBase = &(pEepData->baseEepHeader);
+
+ switch (ah->ah_devid) {
+ case AR9280_DEVID_PCI:
+ if (reg == 0x7894) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "ini VAL: %x EEPROM: %x\n", value,
+ (pBase->version & 0xff));
+
+ if ((pBase->version & 0xff) > 0x0a) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "PWDCLKIND: %d\n",
+ pBase->pwdclkind);
+ value &= ~AR_AN_TOP2_PWDCLKIND;
+ value |= AR_AN_TOP2_PWDCLKIND &
+ (pBase->pwdclkind << AR_AN_TOP2_PWDCLKIND_S);
+ } else {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "PWDCLKIND Earlier Rev\n");
}
- break;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "final ini VAL: %x\n", value);
}
+ break;
}
- return twiceMaxEdgePower;
+
+ return value;
}
-static bool
-ath9k_hw_set_power_per_rate_table(struct ath_hal *ah,
- struct ar5416_eeprom *pEepData,
- struct ath9k_channel *chan,
- int16_t *ratesArray,
- u16 cfgCtl,
- u8 AntennaReduction,
- u8 twiceMaxRegulatoryPower,
- u8 powerLimit)
+static u32 ath9k_hw_ini_fixup(struct ath_hal *ah,
+ struct ar5416_eeprom_def *pEepData,
+ u32 reg, u32 value)
{
- u8 twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
- static const u16 tpScaleReductionTable[5] =
- { 0, 3, 6, 9, AR5416_MAX_RATE_POWER };
+ struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
- int8_t twiceLargestAntenna;
- struct cal_ctl_data *rep;
- struct cal_target_power_leg targetPowerOfdm, targetPowerCck = {
- 0, { 0, 0, 0, 0}
- };
- struct cal_target_power_leg targetPowerOfdmExt = {
- 0, { 0, 0, 0, 0} }, targetPowerCckExt = {
- 0, { 0, 0, 0, 0 }
- };
- struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = {
- 0, {0, 0, 0, 0}
- };
- u8 scaledPower = 0, minCtlPower, maxRegAllowedPower;
- u16 ctlModesFor11a[] =
- { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 };
- u16 ctlModesFor11g[] =
- { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT,
- CTL_2GHT40
- };
- u16 numCtlModes, *pCtlMode, ctlMode, freq;
- struct chan_centers centers;
- int tx_chainmask;
- u8 twiceMinEdgePower;
+ if (ahp->ah_eep_map == EEP_MAP_4KBITS)
+ return value;
+ else
+ return ath9k_hw_def_ini_fixup(ah, pEepData, reg, value);
+}
+
+static int ath9k_hw_process_ini(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ enum ath9k_ht_macmode macmode)
+{
+ int i, regWrites = 0;
struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 modesIndex, freqIndex;
+ int status;
- tx_chainmask = ahp->ah_txchainmask;
+ switch (chan->chanmode) {
+ case CHANNEL_A:
+ case CHANNEL_A_HT20:
+ modesIndex = 1;
+ freqIndex = 1;
+ break;
+ case CHANNEL_A_HT40PLUS:
+ case CHANNEL_A_HT40MINUS:
+ modesIndex = 2;
+ freqIndex = 1;
+ break;
+ case CHANNEL_G:
+ case CHANNEL_G_HT20:
+ case CHANNEL_B:
+ modesIndex = 4;
+ freqIndex = 2;
+ break;
+ case CHANNEL_G_HT40PLUS:
+ case CHANNEL_G_HT40MINUS:
+ modesIndex = 3;
+ freqIndex = 2;
+ break;
- ath9k_hw_get_channel_centers(ah, chan, &centers);
+ default:
+ return -EINVAL;
+ }
- twiceLargestAntenna = max(
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[0],
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[1]);
+ REG_WRITE(ah, AR_PHY(0), 0x00000007);
- twiceLargestAntenna = max((u8) twiceLargestAntenna,
- pEepData->modalHeader
- [IS_CHAN_2GHZ(chan)].antennaGainCh[2]);
+ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
- twiceLargestAntenna =
- (int8_t) min(AntennaReduction - twiceLargestAntenna, 0);
+ ath9k_hw_set_addac(ah, chan);
- maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna;
+ if (AR_SREV_5416_V22_OR_LATER(ah)) {
+ REG_WRITE_ARRAY(&ahp->ah_iniAddac, 1, regWrites);
+ } else {
+ struct ar5416IniArray temp;
+ u32 addacSize =
+ sizeof(u32) * ahp->ah_iniAddac.ia_rows *
+ ahp->ah_iniAddac.ia_columns;
- if (ah->ah_tpScale != ATH9K_TP_SCALE_MAX) {
- maxRegAllowedPower -=
- (tpScaleReductionTable[(ah->ah_tpScale)] * 2);
- }
+ memcpy(ahp->ah_addac5416_21,
+ ahp->ah_iniAddac.ia_array, addacSize);
- scaledPower = min(powerLimit, maxRegAllowedPower);
+ (ahp->ah_addac5416_21)[31 * ahp->ah_iniAddac.ia_columns + 1] = 0;
- switch (ar5416_get_ntxchains(tx_chainmask)) {
- case 1:
- break;
- case 2:
- scaledPower -=
- pEepData->modalHeader[IS_CHAN_2GHZ(chan)].
- pwrDecreaseFor2Chain;
- break;
- case 3:
- scaledPower -=
- pEepData->modalHeader[IS_CHAN_2GHZ(chan)].
- pwrDecreaseFor3Chain;
- break;
+ temp.ia_array = ahp->ah_addac5416_21;
+ temp.ia_columns = ahp->ah_iniAddac.ia_columns;
+ temp.ia_rows = ahp->ah_iniAddac.ia_rows;
+ REG_WRITE_ARRAY(&temp, 1, regWrites);
}
- scaledPower = max(0, (int32_t) scaledPower);
-
- if (IS_CHAN_2GHZ(chan)) {
- numCtlModes =
- ARRAY_SIZE(ctlModesFor11g) -
- SUB_NUM_CTL_MODES_AT_2G_40;
- pCtlMode = ctlModesFor11g;
-
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPowerCck,
- AR5416_NUM_2G_CCK_TARGET_POWERS,
- &targetPowerCck, 4,
- false);
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPower2G,
- AR5416_NUM_2G_20_TARGET_POWERS,
- &targetPowerOfdm, 4,
- false);
- ath9k_hw_get_target_powers(ah, chan,
- pEepData->calTargetPower2GHT20,
- AR5416_NUM_2G_20_TARGET_POWERS,
- &targetPowerHt20, 8, false);
+ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);
- if (IS_CHAN_HT40(chan)) {
- numCtlModes = ARRAY_SIZE(ctlModesFor11g);
- ath9k_hw_get_target_powers(ah, chan,
- pEepData->
- calTargetPower2GHT40,
- AR5416_NUM_2G_40_TARGET_POWERS,
- &targetPowerHt40, 8,
- true);
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPowerCck,
- AR5416_NUM_2G_CCK_TARGET_POWERS,
- &targetPowerCckExt,
- 4, true);
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPower2G,
- AR5416_NUM_2G_20_TARGET_POWERS,
- &targetPowerOfdmExt,
- 4, true);
- }
- } else {
+ for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) {
+ u32 reg = INI_RA(&ahp->ah_iniModes, i, 0);
+ u32 val = INI_RA(&ahp->ah_iniModes, i, modesIndex);
- numCtlModes =
- ARRAY_SIZE(ctlModesFor11a) -
- SUB_NUM_CTL_MODES_AT_5G_40;
- pCtlMode = ctlModesFor11a;
-
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPower5G,
- AR5416_NUM_5G_20_TARGET_POWERS,
- &targetPowerOfdm, 4,
- false);
- ath9k_hw_get_target_powers(ah, chan,
- pEepData->calTargetPower5GHT20,
- AR5416_NUM_5G_20_TARGET_POWERS,
- &targetPowerHt20, 8, false);
+ REG_WRITE(ah, reg, val);
- if (IS_CHAN_HT40(chan)) {
- numCtlModes = ARRAY_SIZE(ctlModesFor11a);
- ath9k_hw_get_target_powers(ah, chan,
- pEepData->
- calTargetPower5GHT40,
- AR5416_NUM_5G_40_TARGET_POWERS,
- &targetPowerHt40, 8,
- true);
- ath9k_hw_get_legacy_target_powers(ah, chan,
- pEepData->
- calTargetPower5G,
- AR5416_NUM_5G_20_TARGET_POWERS,
- &targetPowerOfdmExt,
- 4, true);
+ if (reg >= 0x7800 && reg < 0x78a0
+ && ah->ah_config.analog_shiftreg) {
+ udelay(100);
}
+
+ DO_DELAY(regWrites);
}
- for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) {
- bool isHt40CtlMode =
- (pCtlMode[ctlMode] == CTL_5GHT40)
- || (pCtlMode[ctlMode] == CTL_2GHT40);
- if (isHt40CtlMode)
- freq = centers.synth_center;
- else if (pCtlMode[ctlMode] & EXT_ADDITIVE)
- freq = centers.ext_center;
- else
- freq = centers.ctl_center;
+ if (AR_SREV_9280(ah))
+ REG_WRITE_ARRAY(&ahp->ah_iniModesRxGain, modesIndex, regWrites);
- if (ar5416_get_eep_ver(ahp) == 14
- && ar5416_get_eep_rev(ahp) <= 2)
- twiceMaxEdgePower = AR5416_MAX_RATE_POWER;
+ if (AR_SREV_9280(ah))
+ REG_WRITE_ARRAY(&ahp->ah_iniModesTxGain, modesIndex, regWrites);
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
- "EXT_ADDITIVE %d\n",
- ctlMode, numCtlModes, isHt40CtlMode,
- (pCtlMode[ctlMode] & EXT_ADDITIVE));
+ for (i = 0; i < ahp->ah_iniCommon.ia_rows; i++) {
+ u32 reg = INI_RA(&ahp->ah_iniCommon, i, 0);
+ u32 val = INI_RA(&ahp->ah_iniCommon, i, 1);
- for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i];
- i++) {
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- " LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
- "pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
- "chan %d\n",
- i, cfgCtl, pCtlMode[ctlMode],
- pEepData->ctlIndex[i], chan->channel);
-
- if ((((cfgCtl & ~CTL_MODE_M) |
- (pCtlMode[ctlMode] & CTL_MODE_M)) ==
- pEepData->ctlIndex[i])
- ||
- (((cfgCtl & ~CTL_MODE_M) |
- (pCtlMode[ctlMode] & CTL_MODE_M)) ==
- ((pEepData->
- ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) {
- rep = &(pEepData->ctlData[i]);
-
- twiceMinEdgePower =
- ath9k_hw_get_max_edge_power(freq,
- rep->
- ctlEdges
- [ar5416_get_ntxchains
- (tx_chainmask)
- - 1],
- IS_CHAN_2GHZ
- (chan));
-
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- " MATCH-EE_IDX %d: ch %d is2 %d "
- "2xMinEdge %d chainmask %d chains %d\n",
- i, freq, IS_CHAN_2GHZ(chan),
- twiceMinEdgePower, tx_chainmask,
- ar5416_get_ntxchains
- (tx_chainmask));
- if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) {
- twiceMaxEdgePower =
- min(twiceMaxEdgePower,
- twiceMinEdgePower);
- } else {
- twiceMaxEdgePower =
- twiceMinEdgePower;
- break;
- }
- }
+ REG_WRITE(ah, reg, val);
+
+ if (reg >= 0x7800 && reg < 0x78a0
+ && ah->ah_config.analog_shiftreg) {
+ udelay(100);
}
- minCtlPower = min(twiceMaxEdgePower, scaledPower);
+ DO_DELAY(regWrites);
+ }
+
+ ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites);
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- " SEL-Min ctlMode %d pCtlMode %d "
- "2xMaxEdge %d sP %d minCtlPwr %d\n",
- ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower,
- scaledPower, minCtlPower);
-
- switch (pCtlMode[ctlMode]) {
- case CTL_11B:
- for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x);
- i++) {
- targetPowerCck.tPow2x[i] =
- min(targetPowerCck.tPow2x[i],
- minCtlPower);
- }
- break;
- case CTL_11A:
- case CTL_11G:
- for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x);
- i++) {
- targetPowerOfdm.tPow2x[i] =
- min(targetPowerOfdm.tPow2x[i],
- minCtlPower);
- }
- break;
- case CTL_5GHT20:
- case CTL_2GHT20:
- for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x);
- i++) {
- targetPowerHt20.tPow2x[i] =
- min(targetPowerHt20.tPow2x[i],
- minCtlPower);
- }
- break;
- case CTL_11B_EXT:
- targetPowerCckExt.tPow2x[0] =
- min(targetPowerCckExt.tPow2x[0], minCtlPower);
- break;
- case CTL_11A_EXT:
- case CTL_11G_EXT:
- targetPowerOfdmExt.tPow2x[0] =
- min(targetPowerOfdmExt.tPow2x[0], minCtlPower);
- break;
- case CTL_5GHT40:
- case CTL_2GHT40:
- for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x);
- i++) {
- targetPowerHt40.tPow2x[i] =
- min(targetPowerHt40.tPow2x[i],
- minCtlPower);
- }
- break;
- default:
- break;
- }
+ if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
+ REG_WRITE_ARRAY(&ahp->ah_iniModesAdditional, modesIndex,
+ regWrites);
}
- ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] =
- ratesArray[rate18mb] = ratesArray[rate24mb] =
- targetPowerOfdm.tPow2x[0];
- ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1];
- ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2];
- ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3];
- ratesArray[rateXr] = targetPowerOfdm.tPow2x[0];
-
- for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++)
- ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i];
-
- if (IS_CHAN_2GHZ(chan)) {
- ratesArray[rate1l] = targetPowerCck.tPow2x[0];
- ratesArray[rate2s] = ratesArray[rate2l] =
- targetPowerCck.tPow2x[1];
- ratesArray[rate5_5s] = ratesArray[rate5_5l] =
- targetPowerCck.tPow2x[2];
- ;
- ratesArray[rate11s] = ratesArray[rate11l] =
- targetPowerCck.tPow2x[3];
- ;
+ ath9k_hw_override_ini(ah, chan);
+ ath9k_hw_set_regs(ah, chan, macmode);
+ ath9k_hw_init_chain_masks(ah);
+
+ status = ath9k_hw_set_txpower(ah, chan,
+ ath9k_regd_get_ctl(ah, chan),
+ ath9k_regd_get_antenna_allowed(ah,
+ chan),
+ chan->maxRegTxPower * 2,
+ min((u32) MAX_RATE_POWER,
+ (u32) ah->ah_powerLimit));
+ if (status != 0) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ "error init'ing transmit power\n");
+ return -EIO;
}
- if (IS_CHAN_HT40(chan)) {
- for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) {
- ratesArray[rateHt40_0 + i] =
- targetPowerHt40.tPow2x[i];
- }
- ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0];
- ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0];
- ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0];
- if (IS_CHAN_2GHZ(chan)) {
- ratesArray[rateExtCck] =
- targetPowerCckExt.tPow2x[0];
- }
+
+ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "ar5416SetRfRegs failed\n");
+ return -EIO;
}
- return true;
+
+ return 0;
}
-static int
-ath9k_hw_set_txpower(struct ath_hal *ah,
- struct ar5416_eeprom *pEepData,
- struct ath9k_channel *chan,
- u16 cfgCtl,
- u8 twiceAntennaReduction,
- u8 twiceMaxRegulatoryPower,
- u8 powerLimit)
+/****************************************/
+/* Reset and Channel Switching Routines */
+/****************************************/
+
+static void ath9k_hw_set_rfmode(struct ath_hal *ah, struct ath9k_channel *chan)
{
- struct modal_eep_header *pModal =
- &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]);
- int16_t ratesArray[Ar5416RateSize];
- int16_t txPowerIndexOffset = 0;
- u8 ht40PowerIncForPdadc = 2;
- int i;
+ u32 rfMode = 0;
- memset(ratesArray, 0, sizeof(ratesArray));
+ if (chan == NULL)
+ return;
- if ((pEepData->baseEepHeader.
- version & AR5416_EEP_VER_MINOR_MASK) >=
- AR5416_EEP_MINOR_VER_2) {
- ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc;
- }
+ rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
+ ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
- if (!ath9k_hw_set_power_per_rate_table(ah, pEepData, chan,
- &ratesArray[0], cfgCtl,
- twiceAntennaReduction,
- twiceMaxRegulatoryPower,
- powerLimit)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "ath9k_hw_set_txpower: unable to set "
- "tx power per rate table\n");
- return -EIO;
- }
+ if (!AR_SREV_9280_10_OR_LATER(ah))
+ rfMode |= (IS_CHAN_5GHZ(chan)) ?
+ AR_PHY_MODE_RF5GHZ : AR_PHY_MODE_RF2GHZ;
- if (!ath9k_hw_set_power_cal_table
- (ah, pEepData, chan, &txPowerIndexOffset)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "ath9k_hw_set_txpower: unable to set power table\n");
- return -EIO;
- }
+ if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan))
+ rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
- for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
- ratesArray[i] =
- (int16_t) (txPowerIndexOffset + ratesArray[i]);
- if (ratesArray[i] > AR5416_MAX_RATE_POWER)
- ratesArray[i] = AR5416_MAX_RATE_POWER;
- }
+ REG_WRITE(ah, AR_PHY_MODE, rfMode);
+}
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- for (i = 0; i < Ar5416RateSize; i++)
- ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
- }
+static void ath9k_hw_mark_phy_inactive(struct ath_hal *ah)
+{
+ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
+}
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
- ATH9K_POW_SM(ratesArray[rate18mb], 24)
- | ATH9K_POW_SM(ratesArray[rate12mb], 16)
- | ATH9K_POW_SM(ratesArray[rate9mb], 8)
- | ATH9K_POW_SM(ratesArray[rate6mb], 0)
- );
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE2,
- ATH9K_POW_SM(ratesArray[rate54mb], 24)
- | ATH9K_POW_SM(ratesArray[rate48mb], 16)
- | ATH9K_POW_SM(ratesArray[rate36mb], 8)
- | ATH9K_POW_SM(ratesArray[rate24mb], 0)
- );
-
- if (IS_CHAN_2GHZ(chan)) {
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE3,
- ATH9K_POW_SM(ratesArray[rate2s], 24)
- | ATH9K_POW_SM(ratesArray[rate2l], 16)
- | ATH9K_POW_SM(ratesArray[rateXr], 8)
- | ATH9K_POW_SM(ratesArray[rate1l], 0)
- );
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE4,
- ATH9K_POW_SM(ratesArray[rate11s], 24)
- | ATH9K_POW_SM(ratesArray[rate11l], 16)
- | ATH9K_POW_SM(ratesArray[rate5_5s], 8)
- | ATH9K_POW_SM(ratesArray[rate5_5l], 0)
- );
- }
+static inline void ath9k_hw_set_dma(struct ath_hal *ah)
+{
+ u32 regval;
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE5,
- ATH9K_POW_SM(ratesArray[rateHt20_3], 24)
- | ATH9K_POW_SM(ratesArray[rateHt20_2], 16)
- | ATH9K_POW_SM(ratesArray[rateHt20_1], 8)
- | ATH9K_POW_SM(ratesArray[rateHt20_0], 0)
- );
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE6,
- ATH9K_POW_SM(ratesArray[rateHt20_7], 24)
- | ATH9K_POW_SM(ratesArray[rateHt20_6], 16)
- | ATH9K_POW_SM(ratesArray[rateHt20_5], 8)
- | ATH9K_POW_SM(ratesArray[rateHt20_4], 0)
- );
+ regval = REG_READ(ah, AR_AHB_MODE);
+ REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN);
- if (IS_CHAN_HT40(chan)) {
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE7,
- ATH9K_POW_SM(ratesArray[rateHt40_3] +
- ht40PowerIncForPdadc, 24)
- | ATH9K_POW_SM(ratesArray[rateHt40_2] +
- ht40PowerIncForPdadc, 16)
- | ATH9K_POW_SM(ratesArray[rateHt40_1] +
- ht40PowerIncForPdadc, 8)
- | ATH9K_POW_SM(ratesArray[rateHt40_0] +
- ht40PowerIncForPdadc, 0)
- );
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE8,
- ATH9K_POW_SM(ratesArray[rateHt40_7] +
- ht40PowerIncForPdadc, 24)
- | ATH9K_POW_SM(ratesArray[rateHt40_6] +
- ht40PowerIncForPdadc, 16)
- | ATH9K_POW_SM(ratesArray[rateHt40_5] +
- ht40PowerIncForPdadc, 8)
- | ATH9K_POW_SM(ratesArray[rateHt40_4] +
- ht40PowerIncForPdadc, 0)
- );
-
- REG_WRITE(ah, AR_PHY_POWER_TX_RATE9,
- ATH9K_POW_SM(ratesArray[rateExtOfdm], 24)
- | ATH9K_POW_SM(ratesArray[rateExtCck], 16)
- | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8)
- | ATH9K_POW_SM(ratesArray[rateDupCck], 0)
- );
- }
+ regval = REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK;
+ REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B);
- REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
- ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6)
- | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0)
- );
+ REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->ah_txTrigLevel);
- i = rate6mb;
- if (IS_CHAN_HT40(chan))
- i = rateHt40_0;
- else if (IS_CHAN_HT20(chan))
- i = rateHt20_0;
+ regval = REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK;
+ REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B);
- if (AR_SREV_9280_10_OR_LATER(ah))
- ah->ah_maxPowerLevel =
- ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2;
- else
- ah->ah_maxPowerLevel = ratesArray[i];
+ REG_WRITE(ah, AR_RXFIFO_CFG, 0x200);
- return 0;
+ if (AR_SREV_9285(ah)) {
+ REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
+ AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE);
+ } else {
+ REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
+ AR_PCU_TXBUF_CTRL_USABLE_SIZE);
+ }
+}
+
+static void ath9k_hw_set_operating_mode(struct ath_hal *ah, int opmode)
+{
+ u32 val;
+
+ val = REG_READ(ah, AR_STA_ID1);
+ val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC);
+ switch (opmode) {
+ case NL80211_IFTYPE_AP:
+ REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP
+ | AR_STA_ID1_KSRCH_MODE);
+ REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
+ | AR_STA_ID1_KSRCH_MODE);
+ REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_MONITOR:
+ REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE);
+ break;
+ }
}
static inline void ath9k_hw_get_delta_slope_vals(struct ath_hal *ah,
@@ -4632,9 +1501,8 @@ static inline void ath9k_hw_get_delta_slope_vals(struct ath_hal *ah,
*coef_exponent = coef_exp - 16;
}
-static void
-ath9k_hw_set_delta_slope(struct ath_hal *ah,
- struct ath9k_channel *chan)
+static void ath9k_hw_set_delta_slope(struct ath_hal *ah,
+ struct ath9k_channel *chan)
{
u32 coef_scaled, ds_coef_exp, ds_coef_man;
u32 clockMhzScaled = 0x64000000;
@@ -4667,8 +1535,243 @@ ath9k_hw_set_delta_slope(struct ath_hal *ah,
AR_PHY_HALFGI_DSC_EXP, ds_coef_exp);
}
-static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah,
- struct ath9k_channel *chan)
+static bool ath9k_hw_set_reset(struct ath_hal *ah, int type)
+{
+ u32 rst_flags;
+ u32 tmpReg;
+
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
+ AR_RTC_FORCE_WAKE_ON_INT);
+
+ if (AR_SREV_9100(ah)) {
+ rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD |
+ AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET;
+ } else {
+ tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE);
+ if (tmpReg &
+ (AR_INTR_SYNC_LOCAL_TIMEOUT |
+ AR_INTR_SYNC_RADM_CPL_TIMEOUT)) {
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
+ REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
+ } else {
+ REG_WRITE(ah, AR_RC, AR_RC_AHB);
+ }
+
+ rst_flags = AR_RTC_RC_MAC_WARM;
+ if (type == ATH9K_RESET_COLD)
+ rst_flags |= AR_RTC_RC_MAC_COLD;
+ }
+
+ REG_WRITE(ah, (u16) (AR_RTC_RC), rst_flags);
+ udelay(50);
+
+ REG_WRITE(ah, (u16) (AR_RTC_RC), 0);
+ if (!ath9k_hw_wait(ah, (u16) (AR_RTC_RC), AR_RTC_RC_M, 0)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+ "RTC stuck in MAC reset\n");
+ return false;
+ }
+
+ if (!AR_SREV_9100(ah))
+ REG_WRITE(ah, AR_RC, 0);
+
+ ath9k_hw_init_pll(ah, NULL);
+
+ if (AR_SREV_9100(ah))
+ udelay(50);
+
+ return true;
+}
+
+static bool ath9k_hw_set_reset_power_on(struct ath_hal *ah)
+{
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
+ AR_RTC_FORCE_WAKE_ON_INT);
+
+ REG_WRITE(ah, (u16) (AR_RTC_RESET), 0);
+ REG_WRITE(ah, (u16) (AR_RTC_RESET), 1);
+
+ if (!ath9k_hw_wait(ah,
+ AR_RTC_STATUS,
+ AR_RTC_STATUS_M,
+ AR_RTC_STATUS_ON)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "RTC not waking up\n");
+ return false;
+ }
+
+ ath9k_hw_read_revisions(ah);
+
+ return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM);
+}
+
+static bool ath9k_hw_set_reset_reg(struct ath_hal *ah, u32 type)
+{
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);
+
+ switch (type) {
+ case ATH9K_RESET_POWER_ON:
+ return ath9k_hw_set_reset_power_on(ah);
+ break;
+ case ATH9K_RESET_WARM:
+ case ATH9K_RESET_COLD:
+ return ath9k_hw_set_reset(ah, type);
+ break;
+ default:
+ return false;
+ }
+}
+
+static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan,
+ enum ath9k_ht_macmode macmode)
+{
+ u32 phymode;
+ u32 enableDacFifo = 0;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ if (AR_SREV_9285_10_OR_LATER(ah))
+ enableDacFifo = (REG_READ(ah, AR_PHY_TURBO) &
+ AR_PHY_FC_ENABLE_DAC_FIFO);
+
+ phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40
+ | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH | enableDacFifo;
+
+ if (IS_CHAN_HT40(chan)) {
+ phymode |= AR_PHY_FC_DYN2040_EN;
+
+ if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
+ (chan->chanmode == CHANNEL_G_HT40PLUS))
+ phymode |= AR_PHY_FC_DYN2040_PRI_CH;
+
+ if (ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_25)
+ phymode |= AR_PHY_FC_DYN2040_EXT_CH;
+ }
+ REG_WRITE(ah, AR_PHY_TURBO, phymode);
+
+ ath9k_hw_set11nmac2040(ah, macmode);
+
+ REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
+ REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
+}
+
+static bool ath9k_hw_chip_reset(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
+ return false;
+
+ if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
+ return false;
+
+ ahp->ah_chipFullSleep = false;
+
+ ath9k_hw_init_pll(ah, chan);
+
+ ath9k_hw_set_rfmode(ah, chan);
+
+ return true;
+}
+
+static struct ath9k_channel *ath9k_hw_check_chan(struct ath_hal *ah,
+ struct ath9k_channel *chan)
+{
+ if (!(IS_CHAN_2GHZ(chan) ^ IS_CHAN_5GHZ(chan))) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "invalid channel %u/0x%x; not marked as "
+ "2GHz or 5GHz\n", chan->channel, chan->channelFlags);
+ return NULL;
+ }
+
+ if (!IS_CHAN_OFDM(chan) &&
+ !IS_CHAN_B(chan) &&
+ !IS_CHAN_HT20(chan) &&
+ !IS_CHAN_HT40(chan)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "invalid channel %u/0x%x; not marked as "
+ "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n",
+ chan->channel, chan->channelFlags);
+ return NULL;
+ }
+
+ return ath9k_regd_check_channel(ah, chan);
+}
+
+static bool ath9k_hw_channel_change(struct ath_hal *ah,
+ struct ath9k_channel *chan,
+ enum ath9k_ht_macmode macmode)
+{
+ u32 synthDelay, qnum;
+
+ for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
+ if (ath9k_hw_numtxpending(ah, qnum)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "Transmit frames pending on queue %d\n", qnum);
+ return false;
+ }
+ }
+
+ REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN);
+ if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN,
+ AR_PHY_RFBUS_GRANT_EN)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+ "Could not kill baseband RX\n");
+ return false;
+ }
+
+ ath9k_hw_set_regs(ah, chan, macmode);
+
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ if (!(ath9k_hw_ar9280_set_channel(ah, chan))) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "failed to set channel\n");
+ return false;
+ }
+ } else {
+ if (!(ath9k_hw_set_channel(ah, chan))) {
+ DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+ "failed to set channel\n");
+ return false;
+ }
+ }
+
+ if (ath9k_hw_set_txpower(ah, chan,
+ ath9k_regd_get_ctl(ah, chan),
+ ath9k_regd_get_antenna_allowed(ah, chan),
+ chan->maxRegTxPower * 2,
+ min((u32) MAX_RATE_POWER,
+ (u32) ah->ah_powerLimit)) != 0) {
+ DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ "error init'ing transmit power\n");
+ return false;
+ }
+
+ synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
+ if (IS_CHAN_B(chan))
+ synthDelay = (4 * synthDelay) / 22;
+ else
+ synthDelay /= 10;
+
+ udelay(synthDelay + BASE_ACTIVATE_DELAY);
+
+ REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0);
+
+ if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
+ ath9k_hw_set_delta_slope(ah, chan);
+
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ ath9k_hw_9280_spur_mitigate(ah, chan);
+ else
+ ath9k_hw_spur_mitigate(ah, chan);
+
+ if (!chan->oneTimeCalsDone)
+ chan->oneTimeCalsDone = true;
+
+ return true;
+}
+
+static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, struct ath9k_channel *chan)
{
int bb_spur = AR_NO_SPUR;
int freq;
@@ -4918,8 +2021,7 @@ static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah,
REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
}
-static void ath9k_hw_spur_mitigate(struct ath_hal *ah,
- struct ath9k_channel *chan)
+static void ath9k_hw_spur_mitigate(struct ath_hal *ah, struct ath9k_channel *chan)
{
int bb_spur = AR_NO_SPUR;
int bin, cur_bin;
@@ -5120,752 +2222,11 @@ static void ath9k_hw_spur_mitigate(struct ath_hal *ah,
REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
}
-static void ath9k_hw_init_chain_masks(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int rx_chainmask, tx_chainmask;
-
- rx_chainmask = ahp->ah_rxchainmask;
- tx_chainmask = ahp->ah_txchainmask;
-
- switch (rx_chainmask) {
- case 0x5:
- REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
- AR_PHY_SWAP_ALT_CHAIN);
- case 0x3:
- if (((ah)->ah_macVersion <= AR_SREV_VERSION_9160)) {
- REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7);
- REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
- break;
- }
- case 0x1:
- case 0x2:
- if (!AR_SREV_9280(ah))
- break;
- case 0x7:
- REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask);
- REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask);
- break;
- default:
- break;
- }
-
- REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask);
- if (tx_chainmask == 0x5) {
- REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
- AR_PHY_SWAP_ALT_CHAIN);
- }
- if (AR_SREV_9100(ah))
- REG_WRITE(ah, AR_PHY_ANALOG_SWAP,
- REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001);
-}
-
-static void ath9k_hw_set_addac(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- struct modal_eep_header *pModal;
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ar5416_eeprom *eep = &ahp->ah_eeprom;
- u8 biaslevel;
-
- if (ah->ah_macVersion != AR_SREV_VERSION_9160)
- return;
-
- if (ar5416_get_eep_rev(ahp) < AR5416_EEP_MINOR_VER_7)
- return;
-
- pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
-
- if (pModal->xpaBiasLvl != 0xff) {
- biaslevel = pModal->xpaBiasLvl;
- } else {
-
- u16 resetFreqBin, freqBin, freqCount = 0;
- struct chan_centers centers;
-
- ath9k_hw_get_channel_centers(ah, chan, &centers);
-
- resetFreqBin =
- FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan));
- freqBin = pModal->xpaBiasLvlFreq[0] & 0xff;
- biaslevel = (u8) (pModal->xpaBiasLvlFreq[0] >> 14);
-
- freqCount++;
-
- while (freqCount < 3) {
- if (pModal->xpaBiasLvlFreq[freqCount] == 0x0)
- break;
-
- freqBin = pModal->xpaBiasLvlFreq[freqCount] & 0xff;
- if (resetFreqBin >= freqBin) {
- biaslevel =
- (u8) (pModal->
- xpaBiasLvlFreq[freqCount]
- >> 14);
- } else {
- break;
- }
- freqCount++;
- }
- }
-
- if (IS_CHAN_2GHZ(chan)) {
- INI_RA(&ahp->ah_iniAddac, 7, 1) =
- (INI_RA(&ahp->ah_iniAddac, 7, 1) & (~0x18)) | biaslevel
- << 3;
- } else {
- INI_RA(&ahp->ah_iniAddac, 6, 1) =
- (INI_RA(&ahp->ah_iniAddac, 6, 1) & (~0xc0)) | biaslevel
- << 6;
- }
-}
-
-static u32 ath9k_hw_mac_usec(struct ath_hal *ah, u32 clks)
-{
- if (ah->ah_curchan != NULL)
- return clks /
- CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)];
- else
- return clks / CLOCK_RATE[ATH9K_MODE_11B];
-}
-
-static u32 ath9k_hw_mac_to_usec(struct ath_hal *ah, u32 clks)
-{
- struct ath9k_channel *chan = ah->ah_curchan;
-
- if (chan && IS_CHAN_HT40(chan))
- return ath9k_hw_mac_usec(ah, clks) / 2;
- else
- return ath9k_hw_mac_usec(ah, clks);
-}
-
-static u32 ath9k_hw_mac_clks(struct ath_hal *ah, u32 usecs)
-{
- if (ah->ah_curchan != NULL)
- return usecs * CLOCK_RATE[ath9k_hw_chan2wmode(ah,
- ah->ah_curchan)];
- else
- return usecs * CLOCK_RATE[ATH9K_MODE_11B];
-}
-
-static u32 ath9k_hw_mac_to_clks(struct ath_hal *ah, u32 usecs)
-{
- struct ath9k_channel *chan = ah->ah_curchan;
-
- if (chan && IS_CHAN_HT40(chan))
- return ath9k_hw_mac_clks(ah, usecs) * 2;
- else
- return ath9k_hw_mac_clks(ah, usecs);
-}
-
-static bool ath9k_hw_set_ack_timeout(struct ath_hal *ah, u32 us)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad ack timeout %u\n",
- __func__, us);
- ahp->ah_acktimeout = (u32) -1;
- return false;
- } else {
- REG_RMW_FIELD(ah, AR_TIME_OUT,
- AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us));
- ahp->ah_acktimeout = us;
- return true;
- }
-}
-
-static bool ath9k_hw_set_cts_timeout(struct ath_hal *ah, u32 us)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad cts timeout %u\n",
- __func__, us);
- ahp->ah_ctstimeout = (u32) -1;
- return false;
- } else {
- REG_RMW_FIELD(ah, AR_TIME_OUT,
- AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us));
- ahp->ah_ctstimeout = us;
- return true;
- }
-}
-static bool ath9k_hw_set_global_txtimeout(struct ath_hal *ah,
- u32 tu)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (tu > 0xFFFF) {
- DPRINTF(ah->ah_sc, ATH_DBG_XMIT,
- "%s: bad global tx timeout %u\n", __func__, tu);
- ahp->ah_globaltxtimeout = (u32) -1;
- return false;
- } else {
- REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu);
- ahp->ah_globaltxtimeout = tu;
- return true;
- }
-}
-
-bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad slot time %u\n",
- __func__, us);
- ahp->ah_slottime = (u32) -1;
- return false;
- } else {
- REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us));
- ahp->ah_slottime = us;
- return true;
- }
-}
-
-static void ath9k_hw_init_user_settings(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "--AP %s ahp->ah_miscMode 0x%x\n",
- __func__, ahp->ah_miscMode);
- if (ahp->ah_miscMode != 0)
- REG_WRITE(ah, AR_PCU_MISC,
- REG_READ(ah, AR_PCU_MISC) | ahp->ah_miscMode);
- if (ahp->ah_slottime != (u32) -1)
- ath9k_hw_setslottime(ah, ahp->ah_slottime);
- if (ahp->ah_acktimeout != (u32) -1)
- ath9k_hw_set_ack_timeout(ah, ahp->ah_acktimeout);
- if (ahp->ah_ctstimeout != (u32) -1)
- ath9k_hw_set_cts_timeout(ah, ahp->ah_ctstimeout);
- if (ahp->ah_globaltxtimeout != (u32) -1)
- ath9k_hw_set_global_txtimeout(ah, ahp->ah_globaltxtimeout);
-}
-
-static int
-ath9k_hw_process_ini(struct ath_hal *ah,
- struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
-{
- int i, regWrites = 0;
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 modesIndex, freqIndex;
- int status;
-
- switch (chan->chanmode) {
- case CHANNEL_A:
- case CHANNEL_A_HT20:
- modesIndex = 1;
- freqIndex = 1;
- break;
- case CHANNEL_A_HT40PLUS:
- case CHANNEL_A_HT40MINUS:
- modesIndex = 2;
- freqIndex = 1;
- break;
- case CHANNEL_G:
- case CHANNEL_G_HT20:
- case CHANNEL_B:
- modesIndex = 4;
- freqIndex = 2;
- break;
- case CHANNEL_G_HT40PLUS:
- case CHANNEL_G_HT40MINUS:
- modesIndex = 3;
- freqIndex = 2;
- break;
-
- default:
- return -EINVAL;
- }
-
- REG_WRITE(ah, AR_PHY(0), 0x00000007);
-
- REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
-
- ath9k_hw_set_addac(ah, chan);
-
- if (AR_SREV_5416_V22_OR_LATER(ah)) {
- REG_WRITE_ARRAY(&ahp->ah_iniAddac, 1, regWrites);
- } else {
- struct ar5416IniArray temp;
- u32 addacSize =
- sizeof(u32) * ahp->ah_iniAddac.ia_rows *
- ahp->ah_iniAddac.ia_columns;
-
- memcpy(ahp->ah_addac5416_21,
- ahp->ah_iniAddac.ia_array, addacSize);
-
- (ahp->ah_addac5416_21)[31 *
- ahp->ah_iniAddac.ia_columns + 1] = 0;
-
- temp.ia_array = ahp->ah_addac5416_21;
- temp.ia_columns = ahp->ah_iniAddac.ia_columns;
- temp.ia_rows = ahp->ah_iniAddac.ia_rows;
- REG_WRITE_ARRAY(&temp, 1, regWrites);
- }
- REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);
-
- for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) {
- u32 reg = INI_RA(&ahp->ah_iniModes, i, 0);
- u32 val = INI_RA(&ahp->ah_iniModes, i, modesIndex);
-
-#ifdef CONFIG_SLOW_ANT_DIV
- if (ah->ah_devid == AR9280_DEVID_PCI)
- val = ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom, reg,
- val);
-#endif
-
- REG_WRITE(ah, reg, val);
-
- if (reg >= 0x7800 && reg < 0x78a0
- && ah->ah_config.analog_shiftreg) {
- udelay(100);
- }
-
- DO_DELAY(regWrites);
- }
-
- for (i = 0; i < ahp->ah_iniCommon.ia_rows; i++) {
- u32 reg = INI_RA(&ahp->ah_iniCommon, i, 0);
- u32 val = INI_RA(&ahp->ah_iniCommon, i, 1);
-
- REG_WRITE(ah, reg, val);
-
- if (reg >= 0x7800 && reg < 0x78a0
- && ah->ah_config.analog_shiftreg) {
- udelay(100);
- }
-
- DO_DELAY(regWrites);
- }
-
- ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites);
-
- if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
- REG_WRITE_ARRAY(&ahp->ah_iniModesAdditional, modesIndex,
- regWrites);
- }
-
- ath9k_hw_override_ini(ah, chan);
- ath9k_hw_set_regs(ah, chan, macmode);
- ath9k_hw_init_chain_masks(ah);
-
- status = ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan,
- ath9k_regd_get_ctl(ah, chan),
- ath9k_regd_get_antenna_allowed(ah,
- chan),
- chan->maxRegTxPower * 2,
- min((u32) MAX_RATE_POWER,
- (u32) ah->ah_powerLimit));
- if (status != 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
- "%s: error init'ing transmit power\n", __func__);
- return -EIO;
- }
-
- if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
- DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
- "%s: ar5416SetRfRegs failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static void ath9k_hw_setup_calibration(struct ath_hal *ah,
- struct hal_cal_list *currCal)
-{
- REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0),
- AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
- currCal->calData->calCountMax);
-
- switch (currCal->calData->calType) {
- case IQ_MISMATCH_CAL:
- REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: starting IQ Mismatch Calibration\n",
- __func__);
- break;
- case ADC_GAIN_CAL:
- REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: starting ADC Gain Calibration\n", __func__);
- break;
- case ADC_DC_CAL:
- REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: starting ADC DC Calibration\n", __func__);
- break;
- case ADC_DC_INIT_CAL:
- REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: starting Init ADC DC Calibration\n",
- __func__);
- break;
- }
-
- REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
- AR_PHY_TIMING_CTRL4_DO_CAL);
-}
-
-static void ath9k_hw_reset_calibration(struct ath_hal *ah,
- struct hal_cal_list *currCal)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
-
- ath9k_hw_setup_calibration(ah, currCal);
-
- currCal->calState = CAL_RUNNING;
-
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- ahp->ah_Meas0.sign[i] = 0;
- ahp->ah_Meas1.sign[i] = 0;
- ahp->ah_Meas2.sign[i] = 0;
- ahp->ah_Meas3.sign[i] = 0;
- }
-
- ahp->ah_CalSamples = 0;
-}
-
-static void
-ath9k_hw_per_calibration(struct ath_hal *ah,
- struct ath9k_channel *ichan,
- u8 rxchainmask,
- struct hal_cal_list *currCal,
- bool *isCalDone)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- *isCalDone = false;
-
- if (currCal->calState == CAL_RUNNING) {
- if (!(REG_READ(ah,
- AR_PHY_TIMING_CTRL4(0)) &
- AR_PHY_TIMING_CTRL4_DO_CAL)) {
-
- currCal->calData->calCollect(ah);
-
- ahp->ah_CalSamples++;
-
- if (ahp->ah_CalSamples >=
- currCal->calData->calNumSamples) {
- int i, numChains = 0;
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- if (rxchainmask & (1 << i))
- numChains++;
- }
-
- currCal->calData->calPostProc(ah,
- numChains);
-
- ichan->CalValid |=
- currCal->calData->calType;
- currCal->calState = CAL_DONE;
- *isCalDone = true;
- } else {
- ath9k_hw_setup_calibration(ah, currCal);
- }
- }
- } else if (!(ichan->CalValid & currCal->calData->calType)) {
- ath9k_hw_reset_calibration(ah, currCal);
- }
-}
-
-static inline bool ath9k_hw_run_init_cals(struct ath_hal *ah,
- int init_cal_count)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel ichan;
- bool isCalDone;
- struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
- const struct hal_percal_data *calData = currCal->calData;
- int i;
-
- if (currCal == NULL)
- return false;
-
- ichan.CalValid = 0;
-
- for (i = 0; i < init_cal_count; i++) {
- ath9k_hw_reset_calibration(ah, currCal);
-
- if (!ath9k_hw_wait(ah, AR_PHY_TIMING_CTRL4(0),
- AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: Cal %d failed to complete in 100ms.\n",
- __func__, calData->calType);
-
- ahp->ah_cal_list = ahp->ah_cal_list_last =
- ahp->ah_cal_list_curr = NULL;
- return false;
- }
-
- ath9k_hw_per_calibration(ah, &ichan, ahp->ah_rxchainmask,
- currCal, &isCalDone);
- if (!isCalDone) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: Not able to run Init Cal %d.\n",
- __func__, calData->calType);
- }
- if (currCal->calNext) {
- currCal = currCal->calNext;
- calData = currCal->calData;
- }
- }
-
- ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL;
- return true;
-}
-
-static bool
-ath9k_hw_channel_change(struct ath_hal *ah,
- struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
-{
- u32 synthDelay, qnum;
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
- if (ath9k_hw_numtxpending(ah, qnum)) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: Transmit frames pending on queue %d\n",
- __func__, qnum);
- return false;
- }
- }
-
- REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN);
- if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN,
- AR_PHY_RFBUS_GRANT_EN)) {
- DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
- "%s: Could not kill baseband RX\n", __func__);
- return false;
- }
-
- ath9k_hw_set_regs(ah, chan, macmode);
-
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- if (!(ath9k_hw_ar9280_set_channel(ah, chan))) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: failed to set channel\n", __func__);
- return false;
- }
- } else {
- if (!(ath9k_hw_set_channel(ah, chan))) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: failed to set channel\n", __func__);
- return false;
- }
- }
-
- if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan,
- ath9k_regd_get_ctl(ah, chan),
- ath9k_regd_get_antenna_allowed(ah, chan),
- chan->maxRegTxPower * 2,
- min((u32) MAX_RATE_POWER,
- (u32) ah->ah_powerLimit)) != 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: error init'ing transmit power\n", __func__);
- return false;
- }
-
- synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
- if (IS_CHAN_CCK(chan))
- synthDelay = (4 * synthDelay) / 22;
- else
- synthDelay /= 10;
-
- udelay(synthDelay + BASE_ACTIVATE_DELAY);
-
- REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0);
-
- if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
- ath9k_hw_set_delta_slope(ah, chan);
-
- if (AR_SREV_9280_10_OR_LATER(ah))
- ath9k_hw_9280_spur_mitigate(ah, chan);
- else
- ath9k_hw_spur_mitigate(ah, chan);
-
- if (!chan->oneTimeCalsDone)
- chan->oneTimeCalsDone = true;
-
- return true;
-}
-
-static bool ath9k_hw_chip_reset(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
- return false;
-
- if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
- return false;
-
- ahp->ah_chipFullSleep = false;
-
- ath9k_hw_init_pll(ah, chan);
-
- ath9k_hw_set_rfmode(ah, chan);
-
- return true;
-}
-
-static inline void ath9k_hw_set_dma(struct ath_hal *ah)
-{
- u32 regval;
-
- regval = REG_READ(ah, AR_AHB_MODE);
- REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN);
-
- regval = REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK;
- REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B);
-
- REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->ah_txTrigLevel);
-
- regval = REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK;
- REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B);
-
- REG_WRITE(ah, AR_RXFIFO_CFG, 0x200);
-
- if (AR_SREV_9285(ah)) {
- REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
- AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE);
- } else {
- REG_WRITE(ah, AR_PCU_TXBUF_CTRL,
- AR_PCU_TXBUF_CTRL_USABLE_SIZE);
- }
-}
-
-bool ath9k_hw_stopdmarecv(struct ath_hal *ah)
-{
- REG_WRITE(ah, AR_CR, AR_CR_RXD);
- if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0)) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: dma failed to stop in 10ms\n"
- "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
- __func__,
- REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
- return false;
- } else {
- return true;
- }
-}
-
-void ath9k_hw_startpcureceive(struct ath_hal *ah)
-{
- REG_CLR_BIT(ah, AR_DIAG_SW,
- (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
-
- ath9k_enable_mib_counters(ah);
-
- ath9k_ani_reset(ah);
-}
-
-void ath9k_hw_stoppcurecv(struct ath_hal *ah)
-{
- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
-
- ath9k_hw_disable_mib_counters(ah);
-}
-
-static bool ath9k_hw_iscal_supported(struct ath_hal *ah,
- struct ath9k_channel *chan,
- enum hal_cal_types calType)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- bool retval = false;
-
- switch (calType & ahp->ah_suppCals) {
- case IQ_MISMATCH_CAL:
- if (!IS_CHAN_B(chan))
- retval = true;
- break;
- case ADC_GAIN_CAL:
- case ADC_DC_CAL:
- if (!IS_CHAN_B(chan)
- && !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan)))
- retval = true;
- break;
- }
-
- return retval;
-}
-
-static bool ath9k_hw_init_cal(struct ath_hal *ah,
- struct ath9k_channel *chan)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *ichan =
- ath9k_regd_check_channel(ah, chan);
-
- REG_WRITE(ah, AR_PHY_AGC_CONTROL,
- REG_READ(ah, AR_PHY_AGC_CONTROL) |
- AR_PHY_AGC_CONTROL_CAL);
-
- if (!ath9k_hw_wait
- (ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: offset calibration failed to complete in 1ms; "
- "noisy environment?\n", __func__);
- return false;
- }
-
- REG_WRITE(ah, AR_PHY_AGC_CONTROL,
- REG_READ(ah, AR_PHY_AGC_CONTROL) |
- AR_PHY_AGC_CONTROL_NF);
-
- ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr =
- NULL;
-
- if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) {
- if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) {
- INIT_CAL(&ahp->ah_adcGainCalData);
- INSERT_CAL(ahp, &ahp->ah_adcGainCalData);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: enabling ADC Gain Calibration.\n",
- __func__);
- }
- if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) {
- INIT_CAL(&ahp->ah_adcDcCalData);
- INSERT_CAL(ahp, &ahp->ah_adcDcCalData);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: enabling ADC DC Calibration.\n",
- __func__);
- }
- if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) {
- INIT_CAL(&ahp->ah_iqCalData);
- INSERT_CAL(ahp, &ahp->ah_iqCalData);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: enabling IQ Calibration.\n",
- __func__);
- }
-
- ahp->ah_cal_list_curr = ahp->ah_cal_list;
-
- if (ahp->ah_cal_list_curr)
- ath9k_hw_reset_calibration(ah,
- ahp->ah_cal_list_curr);
- }
-
- ichan->CalValid = 0;
-
- return true;
-}
-
-
-bool ath9k_hw_reset(struct ath_hal *ah,
- struct ath9k_channel *chan,
+bool ath9k_hw_reset(struct ath_hal *ah, struct ath9k_channel *chan,
enum ath9k_ht_macmode macmode,
u8 txchainmask, u8 rxchainmask,
enum ath9k_ht_extprotspacing extprotspacing,
- bool bChannelChange,
- int *status)
+ bool bChannelChange, int *status)
{
u32 saveLedState;
struct ath_hal_5416 *ahp = AH5416(ah);
@@ -5886,8 +2247,8 @@ bool ath9k_hw_reset(struct ath_hal *ah,
if (ath9k_hw_check_chan(ah, chan) == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u/0x%x; no mapping\n",
- __func__, chan->channel, chan->channelFlags);
+ "invalid channel %u/0x%x; no mapping\n",
+ chan->channel, chan->channelFlags);
ecode = -EINVAL;
goto bad;
}
@@ -5907,8 +2268,7 @@ bool ath9k_hw_reset(struct ath_hal *ah,
((chan->channelFlags & CHANNEL_ALL) ==
(ah->ah_curchan->channelFlags & CHANNEL_ALL)) &&
(!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) &&
- !IS_CHAN_A_5MHZ_SPACED(ah->
- ah_curchan)))) {
+ !IS_CHAN_A_5MHZ_SPACED(ah->ah_curchan)))) {
if (ath9k_hw_channel_change(ah, chan, macmode)) {
ath9k_hw_loadnf(ah, ah->ah_curchan);
@@ -5930,8 +2290,7 @@ bool ath9k_hw_reset(struct ath_hal *ah,
ath9k_hw_mark_phy_inactive(ah);
if (!ath9k_hw_chip_reset(ah, chan)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: chip reset failed\n",
- __func__);
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "chip reset failed\n");
ecode = -EINVAL;
goto bad;
}
@@ -5965,7 +2324,7 @@ bool ath9k_hw_reset(struct ath_hal *ah,
if (!ath9k_hw_eeprom_set_board_values(ah, chan)) {
DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "%s: error setting board options\n", __func__);
+ "error setting board options\n");
ecode = -EIO;
goto bad;
}
@@ -6016,7 +2375,7 @@ bool ath9k_hw_reset(struct ath_hal *ah,
ath9k_hw_init_interrupt_masks(ah, ah->ah_opmode);
ath9k_hw_init_qos(ah);
-#ifdef CONFIG_RFKILL
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
ath9k_enable_rfkill(ah);
#endif
@@ -6055,15 +2414,13 @@ bool ath9k_hw_reset(struct ath_hal *ah,
mask = REG_READ(ah, AR_CFG);
if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) {
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s CFG Byte Swap Set 0x%x\n", __func__,
- mask);
+ "CFG Byte Swap Set 0x%x\n", mask);
} else {
mask =
INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB;
REG_WRITE(ah, AR_CFG, mask);
DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s Setting CFG 0x%x\n", __func__,
- REG_READ(ah, AR_CFG));
+ "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG));
}
} else {
#ifdef __BIG_ENDIAN
@@ -6078,693 +2435,402 @@ bad:
return false;
}
-bool ath9k_hw_phy_disable(struct ath_hal *ah)
-{
- return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM);
-}
-
-bool ath9k_hw_disable(struct ath_hal *ah)
-{
- if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
- return false;
-
- return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD);
-}
+/************************/
+/* Key Cache Management */
+/************************/
-bool
-ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan,
- u8 rxchainmask, bool longcal,
- bool *isCalDone)
+bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
- struct ath9k_channel *ichan =
- ath9k_regd_check_channel(ah, chan);
-
- *isCalDone = true;
+ u32 keyType;
- if (ichan == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u/0x%x; no mapping\n",
- __func__, chan->channel, chan->channelFlags);
+ if (entry >= ah->ah_caps.keycache_size) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "entry %u out of range\n", entry);
return false;
}
- if (currCal &&
- (currCal->calState == CAL_RUNNING ||
- currCal->calState == CAL_WAITING)) {
- ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal,
- isCalDone);
- if (*isCalDone) {
- ahp->ah_cal_list_curr = currCal = currCal->calNext;
-
- if (currCal->calState == CAL_WAITING) {
- *isCalDone = false;
- ath9k_hw_reset_calibration(ah, currCal);
- }
- }
- }
+ keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
- if (longcal) {
- ath9k_hw_getnf(ah, ichan);
- ath9k_hw_loadnf(ah, ah->ah_curchan);
- ath9k_hw_start_nfcal(ah);
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
+ REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
- if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
+ if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
+ u16 micentry = entry + 64;
+
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
- chan->channelFlags |= CHANNEL_CW_INT;
- ichan->channelFlags &= ~CHANNEL_CW_INT;
- }
}
+ if (ah->ah_curchan == NULL)
+ return true;
+
return true;
}
-static void ath9k_hw_iqcal_collect(struct ath_hal *ah)
+bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, const u8 *mac)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
+ u32 macHi, macLo;
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- ahp->ah_totalPowerMeasI[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
- ahp->ah_totalPowerMeasQ[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
- ahp->ah_totalIqCorrMeas[i] +=
- (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
- ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i],
- ahp->ah_totalPowerMeasQ[i],
- ahp->ah_totalIqCorrMeas[i]);
+ if (entry >= ah->ah_caps.keycache_size) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "entry %u out of range\n", entry);
+ return false;
}
-}
-static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
-
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- ahp->ah_totalAdcIOddPhase[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
- ahp->ah_totalAdcIEvenPhase[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
- ahp->ah_totalAdcQOddPhase[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
- ahp->ah_totalAdcQEvenPhase[i] +=
- REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
- "oddq=0x%08x; evenq=0x%08x;\n",
- ahp->ah_CalSamples, i,
- ahp->ah_totalAdcIOddPhase[i],
- ahp->ah_totalAdcIEvenPhase[i],
- ahp->ah_totalAdcQOddPhase[i],
- ahp->ah_totalAdcQEvenPhase[i]);
+ if (mac != NULL) {
+ macHi = (mac[5] << 8) | mac[4];
+ macLo = (mac[3] << 24) |
+ (mac[2] << 16) |
+ (mac[1] << 8) |
+ mac[0];
+ macLo >>= 1;
+ macLo |= (macHi & 1) << 31;
+ macHi >>= 1;
+ } else {
+ macLo = macHi = 0;
}
-}
-
-static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- int i;
+ REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
+ REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | AR_KEYTABLE_VALID);
- for (i = 0; i < AR5416_MAX_CHAINS; i++) {
- ahp->ah_totalAdcDcOffsetIOddPhase[i] +=
- (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i));
- ahp->ah_totalAdcDcOffsetIEvenPhase[i] +=
- (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
- ahp->ah_totalAdcDcOffsetQOddPhase[i] +=
- (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
- ahp->ah_totalAdcDcOffsetQEvenPhase[i] +=
- (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
- "oddq=0x%08x; evenq=0x%08x;\n",
- ahp->ah_CalSamples, i,
- ahp->ah_totalAdcDcOffsetIOddPhase[i],
- ahp->ah_totalAdcDcOffsetIEvenPhase[i],
- ahp->ah_totalAdcDcOffsetQOddPhase[i],
- ahp->ah_totalAdcDcOffsetQEvenPhase[i]);
- }
+ return true;
}
-static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains)
+bool ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry,
+ const struct ath9k_keyval *k,
+ const u8 *mac, int xorKey)
{
+ const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ u32 key0, key1, key2, key3, key4;
+ u32 keyType;
+ u32 xorMask = xorKey ?
+ (ATH9K_KEY_XOR << 24 | ATH9K_KEY_XOR << 16 | ATH9K_KEY_XOR << 8
+ | ATH9K_KEY_XOR) : 0;
struct ath_hal_5416 *ahp = AH5416(ah);
- u32 powerMeasQ, powerMeasI, iqCorrMeas;
- u32 qCoffDenom, iCoffDenom;
- int32_t qCoff, iCoff;
- int iqCorrNeg, i;
-
- for (i = 0; i < numChains; i++) {
- powerMeasI = ahp->ah_totalPowerMeasI[i];
- powerMeasQ = ahp->ah_totalPowerMeasQ[i];
- iqCorrMeas = ahp->ah_totalIqCorrMeas[i];
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting IQ Cal and Correction for Chain %d\n",
- i);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Orignal: Chn %diq_corr_meas = 0x%08x\n",
- i, ahp->ah_totalIqCorrMeas[i]);
-
- iqCorrNeg = 0;
+ if (entry >= pCap->keycache_size) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "entry %u out of range\n", entry);
+ return false;
+ }
- if (iqCorrMeas > 0x80000000) {
- iqCorrMeas = (0xffffffff - iqCorrMeas) + 1;
- iqCorrNeg = 1;
+ switch (k->kv_type) {
+ case ATH9K_CIPHER_AES_OCB:
+ keyType = AR_KEYTABLE_TYPE_AES;
+ break;
+ case ATH9K_CIPHER_AES_CCM:
+ if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "AES-CCM not supported by mac rev 0x%x\n",
+ ah->ah_macRev);
+ return false;
}
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
- iqCorrNeg);
-
- iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
- qCoffDenom = powerMeasQ / 64;
-
- if (powerMeasQ != 0) {
-
- iCoff = iqCorrMeas / iCoffDenom;
- qCoff = powerMeasI / qCoffDenom - 64;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d iCoff = 0x%08x\n", i, iCoff);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d qCoff = 0x%08x\n", i, qCoff);
-
-
- iCoff = iCoff & 0x3f;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "New: Chn %d iCoff = 0x%08x\n", i, iCoff);
- if (iqCorrNeg == 0x0)
- iCoff = 0x40 - iCoff;
-
- if (qCoff > 15)
- qCoff = 15;
- else if (qCoff <= -16)
- qCoff = 16;
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
- i, iCoff, qCoff);
-
- REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
- iCoff);
- REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
- AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
- qCoff);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "IQ Cal and Correction done for Chain %d\n",
- i);
+ keyType = AR_KEYTABLE_TYPE_CCM;
+ break;
+ case ATH9K_CIPHER_TKIP:
+ keyType = AR_KEYTABLE_TYPE_TKIP;
+ if (ATH9K_IS_MIC_ENABLED(ah)
+ && entry + 64 >= pCap->keycache_size) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "entry %u inappropriate for TKIP\n", entry);
+ return false;
}
- }
-
- REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0),
- AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
-}
-
-static void
-ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, u8 numChains)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset,
- qEvenMeasOffset;
- u32 qGainMismatch, iGainMismatch, val, i;
-
- for (i = 0; i < numChains; i++) {
- iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i];
- iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i];
- qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i];
- qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i];
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting ADC Gain Cal for Chain %d\n", i);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
- iOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_i = 0x%08x\n", i,
- iEvenMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
- qOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_q = 0x%08x\n", i,
- qEvenMeasOffset);
-
- if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) {
- iGainMismatch =
- ((iEvenMeasOffset * 32) /
- iOddMeasOffset) & 0x3f;
- qGainMismatch =
- ((qOddMeasOffset * 32) /
- qEvenMeasOffset) & 0x3f;
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d gain_mismatch_i = 0x%08x\n", i,
- iGainMismatch);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d gain_mismatch_q = 0x%08x\n", i,
- qGainMismatch);
-
- val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
- val &= 0xfffff000;
- val |= (qGainMismatch) | (iGainMismatch << 6);
- REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "ADC Gain Cal done for Chain %d\n", i);
+ break;
+ case ATH9K_CIPHER_WEP:
+ if (k->kv_len < LEN_WEP40) {
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "WEP key length %u too small\n", k->kv_len);
+ return false;
}
+ if (k->kv_len <= LEN_WEP40)
+ keyType = AR_KEYTABLE_TYPE_40;
+ else if (k->kv_len <= LEN_WEP104)
+ keyType = AR_KEYTABLE_TYPE_104;
+ else
+ keyType = AR_KEYTABLE_TYPE_128;
+ break;
+ case ATH9K_CIPHER_CLR:
+ keyType = AR_KEYTABLE_TYPE_CLR;
+ break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
+ "cipher %u not supported\n", k->kv_type);
+ return false;
}
- REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
- REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
- AR_PHY_NEW_ADC_GAIN_CORR_ENABLE);
-}
-
-static void
-ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, u8 numChains)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 iOddMeasOffset, iEvenMeasOffset, val, i;
- int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch;
- const struct hal_percal_data *calData =
- ahp->ah_cal_list_curr->calData;
- u32 numSamples =
- (1 << (calData->calCountMax + 5)) * calData->calNumSamples;
-
- for (i = 0; i < numChains; i++) {
- iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i];
- iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i];
- qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i];
- qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i];
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting ADC DC Offset Cal for Chain %d\n", i);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_i = %d\n", i,
- iOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_i = %d\n", i,
- iEvenMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_q = %d\n", i,
- qOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_q = %d\n", i,
- qEvenMeasOffset);
-
- iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) /
- numSamples) & 0x1ff;
- qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) /
- numSamples) & 0x1ff;
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
- iDcMismatch);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
- qDcMismatch);
-
- val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
- val &= 0xc0000fff;
- val |= (qDcMismatch << 12) | (iDcMismatch << 21);
- REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "ADC DC Offset Cal done for Chain %d\n", i);
- }
-
- REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
- REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) |
- AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE);
-}
-
-bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *chan = ah->ah_curchan;
+ key0 = get_unaligned_le32(k->kv_val + 0) ^ xorMask;
+ key1 = (get_unaligned_le16(k->kv_val + 4) ^ xorMask) & 0xffff;
+ key2 = get_unaligned_le32(k->kv_val + 6) ^ xorMask;
+ key3 = (get_unaligned_le16(k->kv_val + 10) ^ xorMask) & 0xffff;
+ key4 = get_unaligned_le32(k->kv_val + 12) ^ xorMask;
+ if (k->kv_len <= LEN_WEP104)
+ key4 &= 0xff;
- ah->ah_powerLimit = min(limit, (u32) MAX_RATE_POWER);
+ if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
+ u16 micentry = entry + 64;
- if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan,
- ath9k_regd_get_ctl(ah, chan),
- ath9k_regd_get_antenna_allowed(ah,
- chan),
- chan->maxRegTxPower * 2,
- min((u32) MAX_RATE_POWER,
- (u32) ah->ah_powerLimit)) != 0)
- return false;
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+ (void) ath9k_hw_keysetmac(ah, entry, mac);
- return true;
-}
+ if (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) {
+ u32 mic0, mic1, mic2, mic3, mic4;
-void
-ath9k_hw_get_channel_centers(struct ath_hal *ah,
- struct ath9k_channel *chan,
- struct chan_centers *centers)
-{
- int8_t extoff;
- struct ath_hal_5416 *ahp = AH5416(ah);
+ mic0 = get_unaligned_le32(k->kv_mic + 0);
+ mic2 = get_unaligned_le32(k->kv_mic + 4);
+ mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
+ mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
+ mic4 = get_unaligned_le32(k->kv_txmic + 4);
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
+ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+ AR_KEYTABLE_TYPE_CLR);
- if (!IS_CHAN_HT40(chan)) {
- centers->ctl_center = centers->ext_center =
- centers->synth_center = chan->channel;
- return;
- }
+ } else {
+ u32 mic0, mic2;
- if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
- (chan->chanmode == CHANNEL_G_HT40PLUS)) {
- centers->synth_center =
- chan->channel + HT40_CHANNEL_CENTER_SHIFT;
- extoff = 1;
+ mic0 = get_unaligned_le32(k->kv_mic + 0);
+ mic2 = get_unaligned_le32(k->kv_mic + 4);
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
+ AR_KEYTABLE_TYPE_CLR);
+ }
+ REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
} else {
- centers->synth_center =
- chan->channel - HT40_CHANNEL_CENTER_SHIFT;
- extoff = -1;
+ REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
+ REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+ REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
+ REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
+ REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
+
+ (void) ath9k_hw_keysetmac(ah, entry, mac);
}
- centers->ctl_center = centers->synth_center - (extoff *
- HT40_CHANNEL_CENTER_SHIFT);
- centers->ext_center = centers->synth_center + (extoff *
- ((ahp->
- ah_extprotspacing
- ==
- ATH9K_HT_EXTPROTSPACING_20)
- ?
- HT40_CHANNEL_CENTER_SHIFT
- : 15));
+ if (ah->ah_curchan == NULL)
+ return true;
+ return true;
}
-void
-ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
- bool *isCalDone)
+bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *ichan =
- ath9k_regd_check_channel(ah, chan);
- struct hal_cal_list *currCal = ahp->ah_cal_list_curr;
-
- *isCalDone = true;
-
- if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah))
- return;
-
- if (currCal == NULL)
- return;
-
- if (ichan == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: invalid channel %u/0x%x; no mapping\n",
- __func__, chan->channel, chan->channelFlags);
- return;
- }
-
-
- if (currCal->calState != CAL_DONE) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: Calibration state incorrect, %d\n",
- __func__, currCal->calState);
- return;
+ if (entry < ah->ah_caps.keycache_size) {
+ u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry));
+ if (val & AR_KEYTABLE_VALID)
+ return true;
}
-
-
- if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType))
- return;
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%s: Resetting Cal %d state for channel %u/0x%x\n",
- __func__, currCal->calData->calType, chan->channel,
- chan->channelFlags);
-
- ichan->CalValid &= ~currCal->calData->calType;
- currCal->calState = CAL_WAITING;
-
- *isCalDone = false;
+ return false;
}
-void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac)
+/******************************/
+/* Power Management (Chipset) */
+/******************************/
+
+static void ath9k_set_power_sleep(struct ath_hal *ah, int setChip)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+ if (setChip) {
+ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_EN);
+ if (!AR_SREV_9100(ah))
+ REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
- memcpy(mac, ahp->ah_macaddr, ETH_ALEN);
+ REG_CLR_BIT(ah, (u16) (AR_RTC_RESET),
+ AR_RTC_RESET_EN);
+ }
}
-bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac)
+static void ath9k_set_power_network_sleep(struct ath_hal *ah, int setChip)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+ if (setChip) {
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- memcpy(ahp->ah_macaddr, mac, ETH_ALEN);
- return true;
+ if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_ON_INT);
+ } else {
+ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_EN);
+ }
+ }
}
-void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask)
+static bool ath9k_hw_set_power_awake(struct ath_hal *ah,
+ int setChip)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 val;
+ int i;
- memcpy(mask, ahp->ah_bssidmask, ETH_ALEN);
-}
+ if (setChip) {
+ if ((REG_READ(ah, AR_RTC_STATUS) &
+ AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
+ if (ath9k_hw_set_reset_reg(ah,
+ ATH9K_RESET_POWER_ON) != true) {
+ return false;
+ }
+ }
+ if (AR_SREV_9100(ah))
+ REG_SET_BIT(ah, AR_RTC_RESET,
+ AR_RTC_RESET_EN);
-bool
-ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_EN);
+ udelay(50);
- memcpy(ahp->ah_bssidmask, mask, ETH_ALEN);
+ for (i = POWER_UP_TIME / 50; i > 0; i--) {
+ val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
+ if (val == AR_RTC_STATUS_ON)
+ break;
+ udelay(50);
+ REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_EN);
+ }
+ if (i == 0) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ "Failed to wakeup in %uus\n", POWER_UP_TIME / 20);
+ return false;
+ }
+ }
- REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask));
- REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4));
+ REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
return true;
}
-void
-ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
- u16 assocId)
+bool ath9k_hw_setpower(struct ath_hal *ah,
+ enum ath9k_power_mode mode)
{
struct ath_hal_5416 *ahp = AH5416(ah);
+ static const char *modes[] = {
+ "AWAKE",
+ "FULL-SLEEP",
+ "NETWORK SLEEP",
+ "UNDEFINED"
+ };
+ int status = true, setChip = true;
- memcpy(ahp->ah_bssid, bssid, ETH_ALEN);
- ahp->ah_assocId = assocId;
-
- REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid));
- REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) |
- ((assocId & 0x3fff) << AR_BSS_ID1_AID_S));
-}
-
-u64 ath9k_hw_gettsf64(struct ath_hal *ah)
-{
- u64 tsf;
-
- tsf = REG_READ(ah, AR_TSF_U32);
- tsf = (tsf << 32) | REG_READ(ah, AR_TSF_L32);
- return tsf;
-}
-
-void ath9k_hw_reset_tsf(struct ath_hal *ah)
-{
- int count;
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s -> %s (%s)\n",
+ modes[ahp->ah_powerMode], modes[mode],
+ setChip ? "set chip " : "");
- count = 0;
- while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) {
- count++;
- if (count > 10) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "%s: AR_SLP32_TSF_WRITE_STATUS limit exceeded\n",
- __func__);
- break;
- }
- udelay(10);
+ switch (mode) {
+ case ATH9K_PM_AWAKE:
+ status = ath9k_hw_set_power_awake(ah, setChip);
+ break;
+ case ATH9K_PM_FULL_SLEEP:
+ ath9k_set_power_sleep(ah, setChip);
+ ahp->ah_chipFullSleep = true;
+ break;
+ case ATH9K_PM_NETWORK_SLEEP:
+ ath9k_set_power_network_sleep(ah, setChip);
+ break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT,
+ "Unknown power mode %u\n", mode);
+ return false;
}
- REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
-}
-
-u32 ath9k_hw_getdefantenna(struct ath_hal *ah)
-{
- return REG_READ(ah, AR_DEF_ANTENNA) & 0x7;
-}
+ ahp->ah_powerMode = mode;
-void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna)
-{
- REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
+ return status;
}
-bool
-ath9k_hw_setantennaswitch(struct ath_hal *ah,
- enum ath9k_ant_setting settings,
- struct ath9k_channel *chan,
- u8 *tx_chainmask,
- u8 *rx_chainmask,
- u8 *antenna_cfgd)
+void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore)
{
struct ath_hal_5416 *ahp = AH5416(ah);
- static u8 tx_chainmask_cfg, rx_chainmask_cfg;
+ u8 i;
- if (AR_SREV_9280(ah)) {
- if (!tx_chainmask_cfg) {
+ if (ah->ah_isPciExpress != true)
+ return;
- tx_chainmask_cfg = *tx_chainmask;
- rx_chainmask_cfg = *rx_chainmask;
- }
+ if (ah->ah_config.pcie_powersave_enable == 2)
+ return;
- switch (settings) {
- case ATH9K_ANT_FIXED_A:
- *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
- *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
- *antenna_cfgd = true;
- break;
- case ATH9K_ANT_FIXED_B:
- if (ah->ah_caps.tx_chainmask >
- ATH9K_ANTENNA1_CHAINMASK) {
- *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
- }
- *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
- *antenna_cfgd = true;
- break;
- case ATH9K_ANT_VARIABLE:
- *tx_chainmask = tx_chainmask_cfg;
- *rx_chainmask = rx_chainmask_cfg;
- *antenna_cfgd = true;
- break;
- default:
- break;
+ if (restore)
+ return;
+
+ if (AR_SREV_9280_20_OR_LATER(ah)) {
+ for (i = 0; i < ahp->ah_iniPcieSerdes.ia_rows; i++) {
+ REG_WRITE(ah, INI_RA(&ahp->ah_iniPcieSerdes, i, 0),
+ INI_RA(&ahp->ah_iniPcieSerdes, i, 1));
}
- } else {
- ahp->ah_diversityControl = settings;
- }
+ udelay(1000);
+ } else if (AR_SREV_9280(ah) &&
+ (ah->ah_macRev == AR_SREV_REVISION_9280_10)) {
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
- return true;
-}
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560);
-void ath9k_hw_setopmode(struct ath_hal *ah)
-{
- ath9k_hw_set_operating_mode(ah, ah->ah_opmode);
-}
+ if (ah->ah_config.pcie_clock_req)
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc);
+ else
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd);
-bool
-ath9k_hw_getcapability(struct ath_hal *ah, enum ath9k_capability_type type,
- u32 capability, u32 *result)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007);
- switch (type) {
- case ATH9K_CAP_CIPHER:
- switch (capability) {
- case ATH9K_CIPHER_AES_CCM:
- case ATH9K_CIPHER_AES_OCB:
- case ATH9K_CIPHER_TKIP:
- case ATH9K_CIPHER_WEP:
- case ATH9K_CIPHER_MIC:
- case ATH9K_CIPHER_CLR:
- return true;
- default:
- return false;
- }
- case ATH9K_CAP_TKIP_MIC:
- switch (capability) {
- case 0:
- return true;
- case 1:
- return (ahp->ah_staId1Defaults &
- AR_STA_ID1_CRPT_MIC_ENABLE) ? true :
- false;
- }
- case ATH9K_CAP_TKIP_SPLIT:
- return (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) ?
- false : true;
- case ATH9K_CAP_WME_TKIPMIC:
- return 0;
- case ATH9K_CAP_PHYCOUNTERS:
- return ahp->ah_hasHwPhyCounters ? 0 : -ENXIO;
- case ATH9K_CAP_DIVERSITY:
- return (REG_READ(ah, AR_PHY_CCK_DETECT) &
- AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ?
- true : false;
- case ATH9K_CAP_PHYDIAG:
- return true;
- case ATH9K_CAP_MCAST_KEYSRCH:
- switch (capability) {
- case 0:
- return true;
- case 1:
- if (REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_ADHOC) {
- return false;
- } else {
- return (ahp->ah_staId1Defaults &
- AR_STA_ID1_MCAST_KSRCH) ? true :
- false;
- }
- }
- return false;
- case ATH9K_CAP_TSF_ADJUST:
- return (ahp->ah_miscMode & AR_PCU_TX_ADD_TSF) ?
- true : false;
- case ATH9K_CAP_RFSILENT:
- if (capability == 3)
- return false;
- case ATH9K_CAP_ANT_CFG_2GHZ:
- *result = pCap->num_antcfg_2ghz;
- return true;
- case ATH9K_CAP_ANT_CFG_5GHZ:
- *result = pCap->num_antcfg_5ghz;
- return true;
- case ATH9K_CAP_TXPOW:
- switch (capability) {
- case 0:
- return 0;
- case 1:
- *result = ah->ah_powerLimit;
- return 0;
- case 2:
- *result = ah->ah_maxPowerLevel;
- return 0;
- case 3:
- *result = ah->ah_tpScale;
- return 0;
- }
- return false;
- default:
- return false;
- }
-}
+ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
-int
-ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_channel *chan = ah->ah_curchan;
- const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- u16 ant_config;
- u32 halNumAntConfig;
+ udelay(1000);
+ } else {
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+ REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007);
+ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+ }
- halNumAntConfig =
- IS_CHAN_2GHZ(chan) ? pCap->num_antcfg_2ghz : pCap->
- num_antcfg_5ghz;
+ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
- if (cfg < halNumAntConfig) {
- if (!ath9k_hw_get_eeprom_antenna_cfg(ahp, chan,
- cfg, &ant_config)) {
- REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
- return 0;
- }
+ if (ah->ah_config.pcie_waen) {
+ REG_WRITE(ah, AR_WA, ah->ah_config.pcie_waen);
+ } else {
+ if (AR_SREV_9285(ah))
+ REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);
+ else if (AR_SREV_9280(ah))
+ REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT);
+ else
+ REG_WRITE(ah, AR_WA, AR_WA_DEFAULT);
}
- return -EINVAL;
}
+/**********************/
+/* Interrupt Handling */
+/**********************/
+
bool ath9k_hw_intrpend(struct ath_hal *ah)
{
u32 host_isr;
@@ -6791,6 +2857,7 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
u32 sync_cause = 0;
bool fatal_int = false;
+ struct ath_hal_5416 *ahp = AH5416(ah);
if (!AR_SREV_9100(ah)) {
if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) {
@@ -6800,9 +2867,8 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
}
}
- sync_cause =
- REG_READ(ah,
- AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT;
+ sync_cause = REG_READ(ah, AR_INTR_SYNC_CAUSE) &
+ AR_INTR_SYNC_DEFAULT;
*masked = 0;
@@ -6814,8 +2880,6 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
}
if (isr) {
- struct ath_hal_5416 *ahp = AH5416(ah);
-
if (isr & AR_ISR_BCNMISC) {
u32 isr2;
isr2 = REG_READ(ah, AR_ISR_S2);
@@ -6842,7 +2906,6 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
*masked = isr & ATH9K_INT_COMMON;
if (ahp->ah_intrMitigation) {
-
if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
*masked |= ATH9K_INT_RX;
}
@@ -6867,8 +2930,7 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
if (isr & AR_ISR_RXORN) {
DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "%s: receive FIFO overrun interrupt\n",
- __func__);
+ "receive FIFO overrun interrupt\n");
}
if (!AR_SREV_9100(ah)) {
@@ -6881,8 +2943,10 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
*masked |= mask2;
}
+
if (AR_SREV_9100(ah))
return true;
+
if (sync_cause) {
fatal_int =
(sync_cause &
@@ -6892,32 +2956,29 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked)
if (fatal_int) {
if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) {
DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "%s: received PCI FATAL interrupt\n",
- __func__);
+ "received PCI FATAL interrupt\n");
}
if (sync_cause & AR_INTR_SYNC_HOST1_PERR) {
DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "%s: received PCI PERR interrupt\n",
- __func__);
+ "received PCI PERR interrupt\n");
}
}
if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "%s: AR_INTR_SYNC_RADM_CPL_TIMEOUT\n",
- __func__);
+ "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n");
REG_WRITE(ah, AR_RC, AR_RC_HOSTIF);
REG_WRITE(ah, AR_RC, 0);
*masked |= ATH9K_INT_FATAL;
}
if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) {
DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "%s: AR_INTR_SYNC_LOCAL_TIMEOUT\n",
- __func__);
+ "AR_INTR_SYNC_LOCAL_TIMEOUT\n");
}
REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
(void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR);
}
+
return true;
}
@@ -6933,12 +2994,10 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints)
u32 mask, mask2;
struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: 0x%x => 0x%x\n", __func__,
- omask, ints);
+ DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
if (omask & ATH9K_INT_GLOBAL) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: disable IER\n",
- __func__);
+ DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
if (!AR_SREV_9100(ah)) {
@@ -6993,8 +3052,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints)
mask2 |= AR_IMR_S2_CST;
}
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: new IMR 0x%x\n", __func__,
- mask);
+ DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask);
REG_WRITE(ah, AR_IMR, mask);
mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
AR_IMR_S2_DTIM |
@@ -7014,8 +3072,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints)
}
if (ints & ATH9K_INT_GLOBAL) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: enable IER\n",
- __func__);
+ DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "enable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
if (!AR_SREV_9100(ah)) {
REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
@@ -7035,9 +3092,11 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints)
return omask;
}
-void
-ath9k_hw_beaconinit(struct ath_hal *ah,
- u32 next_beacon, u32 beacon_period)
+/*******************/
+/* Beacon Handling */
+/*******************/
+
+void ath9k_hw_beaconinit(struct ath_hal *ah, u32 next_beacon, u32 beacon_period)
{
struct ath_hal_5416 *ahp = AH5416(ah);
int flags = 0;
@@ -7045,14 +3104,14 @@ ath9k_hw_beaconinit(struct ath_hal *ah,
ahp->ah_beaconInterval = beacon_period;
switch (ah->ah_opmode) {
- case ATH9K_M_STA:
- case ATH9K_M_MONITOR:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_MONITOR:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon));
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, 0xffff);
REG_WRITE(ah, AR_NEXT_SWBA, 0x7ffff);
flags |= AR_TBTT_TIMER_EN;
break;
- case ATH9K_M_IBSS:
+ case NL80211_IFTYPE_ADHOC:
REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER,
@@ -7060,7 +3119,7 @@ ath9k_hw_beaconinit(struct ath_hal *ah,
(ahp->ah_atimWindow ? ahp->
ah_atimWindow : 1)));
flags |= AR_NDP_TIMER_EN;
- case ATH9K_M_HOSTAP:
+ case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon));
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT,
TU_TO_USEC(next_beacon -
@@ -7073,6 +3132,12 @@ ath9k_hw_beaconinit(struct ath_hal *ah,
flags |=
AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN;
break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_BEACON,
+ "%s: unsupported opmode: %d\n",
+ __func__, ah->ah_opmode);
+ return;
+ break;
}
REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(beacon_period));
@@ -7089,9 +3154,8 @@ ath9k_hw_beaconinit(struct ath_hal *ah,
REG_SET_BIT(ah, AR_TIMER_MODE, flags);
}
-void
-ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
- const struct ath9k_beacon_state *bs)
+void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
+ const struct ath9k_beacon_state *bs)
{
u32 nextTbtt, beaconintval, dtimperiod, beacontimeout;
struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
@@ -7120,14 +3184,10 @@ ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
else
nextTbtt = bs->bs_nexttbtt;
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next DTIM %d\n", __func__,
- bs->bs_nextdtim);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next beacon %d\n", __func__,
- nextTbtt);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: beacon period %d\n", __func__,
- beaconintval);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: DTIM period %d\n", __func__,
- dtimperiod);
+ DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim);
+ DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt);
+ DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "beacon period %d\n", beaconintval);
+ DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod);
REG_WRITE(ah, AR_NEXT_DTIM,
TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP));
@@ -7154,1424 +3214,682 @@ ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
}
-bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry)
-{
- if (entry < ah->ah_caps.keycache_size) {
- u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry));
- if (val & AR_KEYTABLE_VALID)
- return true;
- }
- return false;
-}
+/*******************/
+/* HW Capabilities */
+/*******************/
-bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry)
+bool ath9k_hw_fill_cap_info(struct ath_hal *ah)
{
- u32 keyType;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ u16 capField = 0, eeval;
- if (entry >= ah->ah_caps.keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: entry %u out of range\n", __func__, entry);
- return false;
- }
- keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
+ eeval = ath9k_hw_get_eeprom(ah, EEP_REG_0);
- REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR);
- REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0);
- REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0);
+ ah->ah_currentRD = eeval;
- if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
- u16 micentry = entry + 64;
+ eeval = ath9k_hw_get_eeprom(ah, EEP_REG_1);
+ ah->ah_currentRDExt = eeval;
- REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
+ capField = ath9k_hw_get_eeprom(ah, EEP_OP_CAP);
+ if (ah->ah_opmode != NL80211_IFTYPE_AP &&
+ ah->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) {
+ if (ah->ah_currentRD == 0x64 || ah->ah_currentRD == 0x65)
+ ah->ah_currentRD += 5;
+ else if (ah->ah_currentRD == 0x41)
+ ah->ah_currentRD = 0x43;
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
+ "regdomain mapped to 0x%x\n", ah->ah_currentRD);
}
- if (ah->ah_curchan == NULL)
- return true;
-
- return true;
-}
-
-bool
-ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry,
- const u8 *mac)
-{
- u32 macHi, macLo;
-
- if (entry >= ah->ah_caps.keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: entry %u out of range\n", __func__, entry);
- return false;
- }
+ eeval = ath9k_hw_get_eeprom(ah, EEP_OP_MODE);
+ bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX);
- if (mac != NULL) {
- macHi = (mac[5] << 8) | mac[4];
- macLo = (mac[3] << 24) | (mac[2] << 16)
- | (mac[1] << 8) | mac[0];
- macLo >>= 1;
- macLo |= (macHi & 1) << 31;
- macHi >>= 1;
- } else {
- macLo = macHi = 0;
+ if (eeval & AR5416_OPFLAGS_11A) {
+ set_bit(ATH9K_MODE_11A, pCap->wireless_modes);
+ if (ah->ah_config.ht_enable) {
+ if (!(eeval & AR5416_OPFLAGS_N_5G_HT20))
+ set_bit(ATH9K_MODE_11NA_HT20,
+ pCap->wireless_modes);
+ if (!(eeval & AR5416_OPFLAGS_N_5G_HT40)) {
+ set_bit(ATH9K_MODE_11NA_HT40PLUS,
+ pCap->wireless_modes);
+ set_bit(ATH9K_MODE_11NA_HT40MINUS,
+ pCap->wireless_modes);
+ }
+ }
}
- REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo);
- REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | AR_KEYTABLE_VALID);
- return true;
-}
-
-bool
-ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry,
- const struct ath9k_keyval *k,
- const u8 *mac, int xorKey)
-{
- const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- u32 key0, key1, key2, key3, key4;
- u32 keyType;
- u32 xorMask = xorKey ?
- (ATH9K_KEY_XOR << 24 | ATH9K_KEY_XOR << 16 | ATH9K_KEY_XOR << 8
- | ATH9K_KEY_XOR) : 0;
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (entry >= pCap->keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: entry %u out of range\n", __func__, entry);
- return false;
- }
- switch (k->kv_type) {
- case ATH9K_CIPHER_AES_OCB:
- keyType = AR_KEYTABLE_TYPE_AES;
- break;
- case ATH9K_CIPHER_AES_CCM:
- if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: AES-CCM not supported by "
- "mac rev 0x%x\n", __func__,
- ah->ah_macRev);
- return false;
- }
- keyType = AR_KEYTABLE_TYPE_CCM;
- break;
- case ATH9K_CIPHER_TKIP:
- keyType = AR_KEYTABLE_TYPE_TKIP;
- if (ATH9K_IS_MIC_ENABLED(ah)
- && entry + 64 >= pCap->keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: entry %u inappropriate for TKIP\n",
- __func__, entry);
- return false;
- }
- break;
- case ATH9K_CIPHER_WEP:
- if (k->kv_len < LEN_WEP40) {
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: WEP key length %u too small\n",
- __func__, k->kv_len);
- return false;
+ if (eeval & AR5416_OPFLAGS_11G) {
+ set_bit(ATH9K_MODE_11B, pCap->wireless_modes);
+ set_bit(ATH9K_MODE_11G, pCap->wireless_modes);
+ if (ah->ah_config.ht_enable) {
+ if (!(eeval & AR5416_OPFLAGS_N_2G_HT20))
+ set_bit(ATH9K_MODE_11NG_HT20,
+ pCap->wireless_modes);
+ if (!(eeval & AR5416_OPFLAGS_N_2G_HT40)) {
+ set_bit(ATH9K_MODE_11NG_HT40PLUS,
+ pCap->wireless_modes);
+ set_bit(ATH9K_MODE_11NG_HT40MINUS,
+ pCap->wireless_modes);
+ }
}
- if (k->kv_len <= LEN_WEP40)
- keyType = AR_KEYTABLE_TYPE_40;
- else if (k->kv_len <= LEN_WEP104)
- keyType = AR_KEYTABLE_TYPE_104;
- else
- keyType = AR_KEYTABLE_TYPE_128;
- break;
- case ATH9K_CIPHER_CLR:
- keyType = AR_KEYTABLE_TYPE_CLR;
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE,
- "%s: cipher %u not supported\n", __func__,
- k->kv_type);
- return false;
}
- key0 = get_unaligned_le32(k->kv_val + 0) ^ xorMask;
- key1 = (get_unaligned_le16(k->kv_val + 4) ^ xorMask) & 0xffff;
- key2 = get_unaligned_le32(k->kv_val + 6) ^ xorMask;
- key3 = (get_unaligned_le16(k->kv_val + 10) ^ xorMask) & 0xffff;
- key4 = get_unaligned_le32(k->kv_val + 12) ^ xorMask;
- if (k->kv_len <= LEN_WEP104)
- key4 &= 0xff;
-
- if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) {
- u16 micentry = entry + 64;
-
- REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
- REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
- REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
- (void) ath9k_hw_keysetmac(ah, entry, mac);
-
- if (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) {
- u32 mic0, mic1, mic2, mic3, mic4;
-
- mic0 = get_unaligned_le32(k->kv_mic + 0);
- mic2 = get_unaligned_le32(k->kv_mic + 4);
- mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff;
- mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff;
- mic4 = get_unaligned_le32(k->kv_txmic + 4);
- REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3);
- REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4);
- REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
- AR_KEYTABLE_TYPE_CLR);
-
- } else {
- u32 mic0, mic2;
-
- mic0 = get_unaligned_le32(k->kv_mic + 0);
- mic2 = get_unaligned_le32(k->kv_mic + 4);
- REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry),
- AR_KEYTABLE_TYPE_CLR);
- }
- REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0);
- REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
+ pCap->tx_chainmask = ath9k_hw_get_eeprom(ah, EEP_TX_MASK);
+ if ((ah->ah_isPciExpress)
+ || (eeval & AR5416_OPFLAGS_11A)) {
+ pCap->rx_chainmask =
+ ath9k_hw_get_eeprom(ah, EEP_RX_MASK);
} else {
- REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0);
- REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1);
- REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2);
- REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3);
- REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
- REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
-
- (void) ath9k_hw_keysetmac(ah, entry, mac);
+ pCap->rx_chainmask =
+ (ath9k_hw_gpio_get(ah, 0)) ? 0x5 : 0x7;
}
- if (ah->ah_curchan == NULL)
- return true;
-
- return true;
-}
-
-bool
-ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- u32 txcfg, curLevel, newLevel;
- enum ath9k_int omask;
-
- if (ah->ah_txTrigLevel >= MAX_TX_FIFO_THRESHOLD)
- return false;
-
- omask = ath9k_hw_set_interrupts(ah,
- ahp->ah_maskReg & ~ATH9K_INT_GLOBAL);
-
- txcfg = REG_READ(ah, AR_TXCFG);
- curLevel = MS(txcfg, AR_FTRIG);
- newLevel = curLevel;
- if (bIncTrigLevel) {
- if (curLevel < MAX_TX_FIFO_THRESHOLD)
- newLevel++;
- } else if (curLevel > MIN_TX_FIFO_THRESHOLD)
- newLevel--;
- if (newLevel != curLevel)
- REG_WRITE(ah, AR_TXCFG,
- (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
+ if (!(AR_SREV_9280(ah) && (ah->ah_macRev == 0)))
+ ahp->ah_miscMode |= AR_PCU_MIC_NEW_LOC_ENA;
- ath9k_hw_set_interrupts(ah, omask);
+ pCap->low_2ghz_chan = 2312;
+ pCap->high_2ghz_chan = 2732;
- ah->ah_txTrigLevel = newLevel;
+ pCap->low_5ghz_chan = 4920;
+ pCap->high_5ghz_chan = 6100;
- return newLevel != curLevel;
-}
+ pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP;
+ pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP;
+ pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM;
-bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q,
- const struct ath9k_tx_queue_info *qinfo)
-{
- u32 cw;
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- struct ath9k_tx_queue_info *qi;
+ pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP;
+ pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP;
+ pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM;
- if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n",
- __func__, q);
- return false;
- }
+ pCap->hw_caps |= ATH9K_HW_CAP_CHAN_SPREAD;
- qi = &ahp->ah_txq[q];
- if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n",
- __func__);
- return false;
- }
+ if (ah->ah_config.ht_enable)
+ pCap->hw_caps |= ATH9K_HW_CAP_HT;
+ else
+ pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %p\n", __func__, qi);
+ pCap->hw_caps |= ATH9K_HW_CAP_GTT;
+ pCap->hw_caps |= ATH9K_HW_CAP_VEOL;
+ pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK;
+ pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH;
- qi->tqi_ver = qinfo->tqi_ver;
- qi->tqi_subtype = qinfo->tqi_subtype;
- qi->tqi_qflags = qinfo->tqi_qflags;
- qi->tqi_priority = qinfo->tqi_priority;
- if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT)
- qi->tqi_aifs = min(qinfo->tqi_aifs, 255U);
+ if (capField & AR_EEPROM_EEPCAP_MAXQCU)
+ pCap->total_queues =
+ MS(capField, AR_EEPROM_EEPCAP_MAXQCU);
else
- qi->tqi_aifs = INIT_AIFS;
- if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) {
- cw = min(qinfo->tqi_cwmin, 1024U);
- qi->tqi_cwmin = 1;
- while (qi->tqi_cwmin < cw)
- qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
- } else
- qi->tqi_cwmin = qinfo->tqi_cwmin;
- if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) {
- cw = min(qinfo->tqi_cwmax, 1024U);
- qi->tqi_cwmax = 1;
- while (qi->tqi_cwmax < cw)
- qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
- } else
- qi->tqi_cwmax = INIT_CWMAX;
+ pCap->total_queues = ATH9K_NUM_TX_QUEUES;
- if (qinfo->tqi_shretry != 0)
- qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U);
- else
- qi->tqi_shretry = INIT_SH_RETRY;
- if (qinfo->tqi_lgretry != 0)
- qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U);
+ if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES)
+ pCap->keycache_size =
+ 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES);
else
- qi->tqi_lgretry = INIT_LG_RETRY;
- qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod;
- qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit;
- qi->tqi_burstTime = qinfo->tqi_burstTime;
- qi->tqi_readyTime = qinfo->tqi_readyTime;
-
- switch (qinfo->tqi_subtype) {
- case ATH9K_WME_UPSD:
- if (qi->tqi_type == ATH9K_TX_QUEUE_DATA)
- qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS;
- break;
- default:
- break;
- }
- return true;
-}
+ pCap->keycache_size = AR_KEYTABLE_SIZE;
-bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q,
- struct ath9k_tx_queue_info *qinfo)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- struct ath9k_tx_queue_info *qi;
+ pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
+ pCap->num_mr_retries = 4;
+ pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
- if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n",
- __func__, q);
- return false;
- }
+ if (AR_SREV_9280_10_OR_LATER(ah))
+ pCap->num_gpio_pins = AR928X_NUM_GPIO;
+ else
+ pCap->num_gpio_pins = AR_NUM_GPIO;
- qi = &ahp->ah_txq[q];
- if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n",
- __func__);
- return false;
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ pCap->hw_caps |= ATH9K_HW_CAP_WOW;
+ pCap->hw_caps |= ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT;
+ } else {
+ pCap->hw_caps &= ~ATH9K_HW_CAP_WOW;
+ pCap->hw_caps &= ~ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT;
}
- qinfo->tqi_qflags = qi->tqi_qflags;
- qinfo->tqi_ver = qi->tqi_ver;
- qinfo->tqi_subtype = qi->tqi_subtype;
- qinfo->tqi_qflags = qi->tqi_qflags;
- qinfo->tqi_priority = qi->tqi_priority;
- qinfo->tqi_aifs = qi->tqi_aifs;
- qinfo->tqi_cwmin = qi->tqi_cwmin;
- qinfo->tqi_cwmax = qi->tqi_cwmax;
- qinfo->tqi_shretry = qi->tqi_shretry;
- qinfo->tqi_lgretry = qi->tqi_lgretry;
- qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
- qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
- qinfo->tqi_burstTime = qi->tqi_burstTime;
- qinfo->tqi_readyTime = qi->tqi_readyTime;
+ if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) {
+ pCap->hw_caps |= ATH9K_HW_CAP_CST;
+ pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX;
+ } else {
+ pCap->rts_aggr_limit = (8 * 1024);
+ }
- return true;
-}
+ pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM;
-int
-ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type,
- const struct ath9k_tx_queue_info *qinfo)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_tx_queue_info *qi;
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- int q;
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+ ah->ah_rfsilent = ath9k_hw_get_eeprom(ah, EEP_RF_SILENT);
+ if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) {
+ ah->ah_rfkill_gpio =
+ MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL);
+ ah->ah_rfkill_polarity =
+ MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY);
- switch (type) {
- case ATH9K_TX_QUEUE_BEACON:
- q = pCap->total_queues - 1;
- break;
- case ATH9K_TX_QUEUE_CAB:
- q = pCap->total_queues - 2;
- break;
- case ATH9K_TX_QUEUE_PSPOLL:
- q = 1;
- break;
- case ATH9K_TX_QUEUE_UAPSD:
- q = pCap->total_queues - 3;
- break;
- case ATH9K_TX_QUEUE_DATA:
- for (q = 0; q < pCap->total_queues; q++)
- if (ahp->ah_txq[q].tqi_type ==
- ATH9K_TX_QUEUE_INACTIVE)
- break;
- if (q == pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: no available tx queue\n", __func__);
- return -1;
- }
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: bad tx queue type %u\n",
- __func__, type);
- return -1;
+ pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT;
}
+#endif
+
+ if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) ||
+ (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) ||
+ (ah->ah_macVersion == AR_SREV_VERSION_9160) ||
+ (ah->ah_macVersion == AR_SREV_VERSION_9100) ||
+ (ah->ah_macVersion == AR_SREV_VERSION_9280))
+ pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP;
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP;
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q);
+ if (AR_SREV_9280(ah) || AR_SREV_9285(ah))
+ pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS;
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS;
- qi = &ahp->ah_txq[q];
- if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: tx queue %u already active\n", __func__, q);
- return -1;
- }
- memset(qi, 0, sizeof(struct ath9k_tx_queue_info));
- qi->tqi_type = type;
- if (qinfo == NULL) {
- qi->tqi_qflags =
- TXQ_FLAG_TXOKINT_ENABLE
- | TXQ_FLAG_TXERRINT_ENABLE
- | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE;
- qi->tqi_aifs = INIT_AIFS;
- qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
- qi->tqi_cwmax = INIT_CWMAX;
- qi->tqi_shretry = INIT_SH_RETRY;
- qi->tqi_lgretry = INIT_LG_RETRY;
- qi->tqi_physCompBuf = 0;
+ if (ah->ah_currentRDExt & (1 << REG_EXT_JAPAN_MIDBAND)) {
+ pCap->reg_cap =
+ AR_EEPROM_EEREGCAP_EN_KK_NEW_11A |
+ AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
+ AR_EEPROM_EEREGCAP_EN_KK_U2 |
+ AR_EEPROM_EEREGCAP_EN_KK_MIDBAND;
} else {
- qi->tqi_physCompBuf = qinfo->tqi_physCompBuf;
- (void) ath9k_hw_set_txq_props(ah, q, qinfo);
+ pCap->reg_cap =
+ AR_EEPROM_EEREGCAP_EN_KK_NEW_11A |
+ AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN;
}
- return q;
-}
+ pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND;
-static void
-ath9k_hw_set_txq_interrupts(struct ath_hal *ah,
- struct ath9k_tx_queue_info *qi)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
+ pCap->num_antcfg_5ghz =
+ ath9k_hw_get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_5GHZ);
+ pCap->num_antcfg_2ghz =
+ ath9k_hw_get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ);
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
- __func__, ahp->ah_txOkInterruptMask,
- ahp->ah_txErrInterruptMask, ahp->ah_txDescInterruptMask,
- ahp->ah_txEolInterruptMask, ahp->ah_txUrnInterruptMask);
-
- REG_WRITE(ah, AR_IMR_S0,
- SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK)
- | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC));
- REG_WRITE(ah, AR_IMR_S1,
- SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR)
- | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL));
- REG_RMW_FIELD(ah, AR_IMR_S2,
- AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask);
+ return true;
}
-bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q)
+bool ath9k_hw_getcapability(struct ath_hal *ah, enum ath9k_capability_type type,
+ u32 capability, u32 *result)
{
struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- struct ath9k_tx_queue_info *qi;
+ const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n",
- __func__, q);
+ switch (type) {
+ case ATH9K_CAP_CIPHER:
+ switch (capability) {
+ case ATH9K_CIPHER_AES_CCM:
+ case ATH9K_CIPHER_AES_OCB:
+ case ATH9K_CIPHER_TKIP:
+ case ATH9K_CIPHER_WEP:
+ case ATH9K_CIPHER_MIC:
+ case ATH9K_CIPHER_CLR:
+ return true;
+ default:
+ return false;
+ }
+ case ATH9K_CAP_TKIP_MIC:
+ switch (capability) {
+ case 0:
+ return true;
+ case 1:
+ return (ahp->ah_staId1Defaults &
+ AR_STA_ID1_CRPT_MIC_ENABLE) ? true :
+ false;
+ }
+ case ATH9K_CAP_TKIP_SPLIT:
+ return (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) ?
+ false : true;
+ case ATH9K_CAP_WME_TKIPMIC:
+ return 0;
+ case ATH9K_CAP_PHYCOUNTERS:
+ return ahp->ah_hasHwPhyCounters ? 0 : -ENXIO;
+ case ATH9K_CAP_DIVERSITY:
+ return (REG_READ(ah, AR_PHY_CCK_DETECT) &
+ AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ?
+ true : false;
+ case ATH9K_CAP_PHYDIAG:
+ return true;
+ case ATH9K_CAP_MCAST_KEYSRCH:
+ switch (capability) {
+ case 0:
+ return true;
+ case 1:
+ if (REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_ADHOC) {
+ return false;
+ } else {
+ return (ahp->ah_staId1Defaults &
+ AR_STA_ID1_MCAST_KSRCH) ? true :
+ false;
+ }
+ }
return false;
- }
- qi = &ahp->ah_txq[q];
- if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n",
- __func__, q);
+ case ATH9K_CAP_TSF_ADJUST:
+ return (ahp->ah_miscMode & AR_PCU_TX_ADD_TSF) ?
+ true : false;
+ case ATH9K_CAP_RFSILENT:
+ if (capability == 3)
+ return false;
+ case ATH9K_CAP_ANT_CFG_2GHZ:
+ *result = pCap->num_antcfg_2ghz;
+ return true;
+ case ATH9K_CAP_ANT_CFG_5GHZ:
+ *result = pCap->num_antcfg_5ghz;
+ return true;
+ case ATH9K_CAP_TXPOW:
+ switch (capability) {
+ case 0:
+ return 0;
+ case 1:
+ *result = ah->ah_powerLimit;
+ return 0;
+ case 2:
+ *result = ah->ah_maxPowerLevel;
+ return 0;
+ case 3:
+ *result = ah->ah_tpScale;
+ return 0;
+ }
+ return false;
+ default:
return false;
}
-
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: release queue %u\n",
- __func__, q);
-
- qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE;
- ahp->ah_txOkInterruptMask &= ~(1 << q);
- ahp->ah_txErrInterruptMask &= ~(1 << q);
- ahp->ah_txDescInterruptMask &= ~(1 << q);
- ahp->ah_txEolInterruptMask &= ~(1 << q);
- ahp->ah_txUrnInterruptMask &= ~(1 << q);
- ath9k_hw_set_txq_interrupts(ah, qi);
-
- return true;
}
-bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q)
+bool ath9k_hw_setcapability(struct ath_hal *ah, enum ath9k_capability_type type,
+ u32 capability, u32 setting, int *status)
{
struct ath_hal_5416 *ahp = AH5416(ah);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
- struct ath9k_channel *chan = ah->ah_curchan;
- struct ath9k_tx_queue_info *qi;
- u32 cwMin, chanCwMin, value;
+ u32 v;
- if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n",
- __func__, q);
- return false;
- }
- qi = &ahp->ah_txq[q];
- if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n",
- __func__, q);
+ switch (type) {
+ case ATH9K_CAP_TKIP_MIC:
+ if (setting)
+ ahp->ah_staId1Defaults |=
+ AR_STA_ID1_CRPT_MIC_ENABLE;
+ else
+ ahp->ah_staId1Defaults &=
+ ~AR_STA_ID1_CRPT_MIC_ENABLE;
return true;
- }
-
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: reset queue %u\n", __func__, q);
-
- if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
- if (chan && IS_CHAN_B(chan))
- chanCwMin = INIT_CWMIN_11B;
+ case ATH9K_CAP_DIVERSITY:
+ v = REG_READ(ah, AR_PHY_CCK_DETECT);
+ if (setting)
+ v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
else
- chanCwMin = INIT_CWMIN;
-
- for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
- } else
- cwMin = qi->tqi_cwmin;
-
- REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN)
- | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX)
- | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS));
-
- REG_WRITE(ah, AR_DRETRY_LIMIT(q),
- SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH)
- | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG)
- | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH));
-
- REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ);
- REG_WRITE(ah, AR_DMISC(q),
- AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2);
-
- if (qi->tqi_cbrPeriod) {
- REG_WRITE(ah, AR_QCBRCFG(q),
- SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL)
- | SM(qi->tqi_cbrOverflowLimit,
- AR_Q_CBRCFG_OVF_THRESH));
- REG_WRITE(ah, AR_QMISC(q),
- REG_READ(ah,
- AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | (qi->
- tqi_cbrOverflowLimit
- ?
- AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN
- :
- 0));
- }
- if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) {
- REG_WRITE(ah, AR_QRDYTIMECFG(q),
- SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) |
- AR_Q_RDYTIMECFG_EN);
- }
-
- REG_WRITE(ah, AR_DCHNTIME(q),
- SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) |
- (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0));
-
- if (qi->tqi_burstTime
- && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) {
- REG_WRITE(ah, AR_QMISC(q),
- REG_READ(ah,
- AR_QMISC(q)) |
- AR_Q_MISC_RDYTIME_EXP_POLICY);
-
- }
-
- if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) {
- REG_WRITE(ah, AR_DMISC(q),
- REG_READ(ah, AR_DMISC(q)) |
- AR_D_MISC_POST_FR_BKOFF_DIS);
- }
- if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) {
- REG_WRITE(ah, AR_DMISC(q),
- REG_READ(ah, AR_DMISC(q)) |
- AR_D_MISC_FRAG_BKOFF_EN);
- }
- switch (qi->tqi_type) {
- case ATH9K_TX_QUEUE_BEACON:
- REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
- | AR_Q_MISC_FSP_DBA_GATED
- | AR_Q_MISC_BEACON_USE
- | AR_Q_MISC_CBR_INCR_DIS1);
-
- REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
- | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
- AR_D_MISC_ARB_LOCKOUT_CNTRL_S)
- | AR_D_MISC_BEACON_USE
- | AR_D_MISC_POST_FR_BKOFF_DIS);
- break;
- case ATH9K_TX_QUEUE_CAB:
- REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
- | AR_Q_MISC_FSP_DBA_GATED
- | AR_Q_MISC_CBR_INCR_DIS1
- | AR_Q_MISC_CBR_INCR_DIS0);
- value = (qi->tqi_readyTime
- - (ah->ah_config.sw_beacon_response_time -
- ah->ah_config.dma_beacon_response_time)
- -
- ah->ah_config.additional_swba_backoff) *
- 1024;
- REG_WRITE(ah, AR_QRDYTIMECFG(q),
- value | AR_Q_RDYTIMECFG_EN);
- REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
- | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
- AR_D_MISC_ARB_LOCKOUT_CNTRL_S));
- break;
- case ATH9K_TX_QUEUE_PSPOLL:
- REG_WRITE(ah, AR_QMISC(q),
- REG_READ(ah,
- AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1);
- break;
- case ATH9K_TX_QUEUE_UAPSD:
- REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
- | AR_D_MISC_POST_FR_BKOFF_DIS);
- break;
+ v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+ REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
+ return true;
+ case ATH9K_CAP_MCAST_KEYSRCH:
+ if (setting)
+ ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH;
+ else
+ ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH;
+ return true;
+ case ATH9K_CAP_TSF_ADJUST:
+ if (setting)
+ ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF;
+ else
+ ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF;
+ return true;
default:
- break;
- }
-
- if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) {
- REG_WRITE(ah, AR_DMISC(q),
- REG_READ(ah, AR_DMISC(q)) |
- SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
- AR_D_MISC_ARB_LOCKOUT_CNTRL) |
- AR_D_MISC_POST_FR_BKOFF_DIS);
+ return false;
}
-
- if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE)
- ahp->ah_txOkInterruptMask |= 1 << q;
- else
- ahp->ah_txOkInterruptMask &= ~(1 << q);
- if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE)
- ahp->ah_txErrInterruptMask |= 1 << q;
- else
- ahp->ah_txErrInterruptMask &= ~(1 << q);
- if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE)
- ahp->ah_txDescInterruptMask |= 1 << q;
- else
- ahp->ah_txDescInterruptMask &= ~(1 << q);
- if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE)
- ahp->ah_txEolInterruptMask |= 1 << q;
- else
- ahp->ah_txEolInterruptMask &= ~(1 << q);
- if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE)
- ahp->ah_txUrnInterruptMask |= 1 << q;
- else
- ahp->ah_txUrnInterruptMask &= ~(1 << q);
- ath9k_hw_set_txq_interrupts(ah, qi);
-
- return true;
}
-void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs)
-{
- struct ath_hal_5416 *ahp = AH5416(ah);
- *txqs &= ahp->ah_intrTxqs;
- ahp->ah_intrTxqs &= ~(*txqs);
-}
+/****************************/
+/* GPIO / RFKILL / Antennae */
+/****************************/
-bool
-ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 segLen, bool firstSeg,
- bool lastSeg, const struct ath_desc *ds0)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- if (firstSeg) {
- ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore);
- } else if (lastSeg) {
- ads->ds_ctl0 = 0;
- ads->ds_ctl1 = segLen;
- ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
- ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
- } else {
- ads->ds_ctl0 = 0;
- ads->ds_ctl1 = segLen | AR_TxMore;
- ads->ds_ctl2 = 0;
- ads->ds_ctl3 = 0;
- }
- ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
- ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
- ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
- ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
- ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
- return true;
-}
-
-void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds)
+static void ath9k_hw_gpio_cfg_output_mux(struct ath_hal *ah,
+ u32 gpio, u32 type)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
+ int addr;
+ u32 gpio_shift, tmp;
- ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
- ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
- ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
- ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
- ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
-}
+ if (gpio > 11)
+ addr = AR_GPIO_OUTPUT_MUX3;
+ else if (gpio > 5)
+ addr = AR_GPIO_OUTPUT_MUX2;
+ else
+ addr = AR_GPIO_OUTPUT_MUX1;
-int
-ath9k_hw_txprocdesc(struct ath_hal *ah, struct ath_desc *ds)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- if ((ads->ds_txstatus9 & AR_TxDone) == 0)
- return -EINPROGRESS;
-
- ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum);
- ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp;
- ds->ds_txstat.ts_status = 0;
- ds->ds_txstat.ts_flags = 0;
-
- if (ads->ds_txstatus1 & AR_ExcessiveRetries)
- ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
- if (ads->ds_txstatus1 & AR_Filtered)
- ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT;
- if (ads->ds_txstatus1 & AR_FIFOUnderrun)
- ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO;
- if (ads->ds_txstatus9 & AR_TxOpExceeded)
- ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP;
- if (ads->ds_txstatus1 & AR_TxTimerExpired)
- ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
-
- if (ads->ds_txstatus1 & AR_DescCfgErr)
- ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR;
- if (ads->ds_txstatus1 & AR_TxDataUnderrun) {
- ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN;
- ath9k_hw_updatetxtriglevel(ah, true);
- }
- if (ads->ds_txstatus1 & AR_TxDelimUnderrun) {
- ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
- ath9k_hw_updatetxtriglevel(ah, true);
- }
- if (ads->ds_txstatus0 & AR_TxBaStatus) {
- ds->ds_txstat.ts_flags |= ATH9K_TX_BA;
- ds->ds_txstat.ba_low = ads->AR_BaBitmapLow;
- ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh;
- }
+ gpio_shift = (gpio % 6) * 5;
- ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx);
- switch (ds->ds_txstat.ts_rateindex) {
- case 0:
- ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0);
- break;
- case 1:
- ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1);
- break;
- case 2:
- ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2);
- break;
- case 3:
- ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3);
- break;
+ if (AR_SREV_9280_20_OR_LATER(ah)
+ || (addr != AR_GPIO_OUTPUT_MUX1)) {
+ REG_RMW(ah, addr, (type << gpio_shift),
+ (0x1f << gpio_shift));
+ } else {
+ tmp = REG_READ(ah, addr);
+ tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0);
+ tmp &= ~(0x1f << gpio_shift);
+ tmp |= (type << gpio_shift);
+ REG_WRITE(ah, addr, tmp);
}
-
- ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined);
- ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00);
- ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01);
- ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02);
- ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10);
- ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11);
- ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12);
- ds->ds_txstat.evm0 = ads->AR_TxEVM0;
- ds->ds_txstat.evm1 = ads->AR_TxEVM1;
- ds->ds_txstat.evm2 = ads->AR_TxEVM2;
- ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
- ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
- ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
- ds->ds_txstat.ts_antenna = 1;
-
- return 0;
}
-void
-ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
- u32 keyIx, enum ath9k_key_type keyType, u32 flags)
+void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- txPower += ahp->ah_txPowerIndexOffset;
- if (txPower > 63)
- txPower = 63;
-
- ads->ds_ctl0 = (pktLen & AR_FrameLen)
- | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(txPower, AR_XmitPower)
- | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
- | (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
- | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
- | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0);
-
- ads->ds_ctl1 =
- (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0)
- | SM(type, AR_FrameType)
- | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
- | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
- | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
+ u32 gpio_shift;
- ads->ds_ctl6 = SM(keyType, AR_EncrType);
+ ASSERT(gpio < ah->ah_caps.num_gpio_pins);
- if (AR_SREV_9285(ah)) {
+ gpio_shift = gpio << 1;
- ads->ds_ctl8 = 0;
- ads->ds_ctl9 = 0;
- ads->ds_ctl10 = 0;
- ads->ds_ctl11 = 0;
- }
+ REG_RMW(ah,
+ AR_GPIO_OE_OUT,
+ (AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
+ (AR_GPIO_OE_OUT_DRV << gpio_shift));
}
-void
-ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds,
- struct ath_desc *lastds,
- u32 durUpdateEn, u32 rtsctsRate,
- u32 rtsctsDuration,
- struct ath9k_11n_rate_series series[],
- u32 nseries, u32 flags)
+u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
- struct ar5416_desc *last_ads = AR5416DESC(lastds);
- u32 ds_ctl0;
-
- (void) nseries;
- (void) rtsctsDuration;
-
- if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
- ds_ctl0 = ads->ds_ctl0;
-
- if (flags & ATH9K_TXDESC_RTSENA) {
- ds_ctl0 &= ~AR_CTSEnable;
- ds_ctl0 |= AR_RTSEnable;
- } else {
- ds_ctl0 &= ~AR_RTSEnable;
- ds_ctl0 |= AR_CTSEnable;
- }
+ if (gpio >= ah->ah_caps.num_gpio_pins)
+ return 0xffffffff;
- ads->ds_ctl0 = ds_ctl0;
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ return (MS
+ (REG_READ(ah, AR_GPIO_IN_OUT),
+ AR928X_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0;
} else {
- ads->ds_ctl0 =
- (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable));
+ return (MS(REG_READ(ah, AR_GPIO_IN_OUT), AR_GPIO_IN_VAL) &
+ AR_GPIO_BIT(gpio)) != 0;
}
-
- ads->ds_ctl2 = set11nTries(series, 0)
- | set11nTries(series, 1)
- | set11nTries(series, 2)
- | set11nTries(series, 3)
- | (durUpdateEn ? AR_DurUpdateEna : 0)
- | SM(0, AR_BurstDur);
-
- ads->ds_ctl3 = set11nRate(series, 0)
- | set11nRate(series, 1)
- | set11nRate(series, 2)
- | set11nRate(series, 3);
-
- ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0)
- | set11nPktDurRTSCTS(series, 1);
-
- ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2)
- | set11nPktDurRTSCTS(series, 3);
-
- ads->ds_ctl7 = set11nRateFlags(series, 0)
- | set11nRateFlags(series, 1)
- | set11nRateFlags(series, 2)
- | set11nRateFlags(series, 3)
- | SM(rtsctsRate, AR_RTSCTSRate);
- last_ads->ds_ctl2 = ads->ds_ctl2;
- last_ads->ds_ctl3 = ads->ds_ctl3;
}
-void
-ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds,
- u32 aggrLen)
+void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio,
+ u32 ah_signal_type)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
-
- ads->ds_ctl6 &= ~AR_AggrLen;
- ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
-}
+ u32 gpio_shift;
-void
-ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds,
- u32 numDelims)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
- unsigned int ctl6;
+ ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type);
- ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
+ gpio_shift = 2 * gpio;
- ctl6 = ads->ds_ctl6;
- ctl6 &= ~AR_PadDelim;
- ctl6 |= SM(numDelims, AR_PadDelim);
- ads->ds_ctl6 = ctl6;
+ REG_RMW(ah,
+ AR_GPIO_OE_OUT,
+ (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift),
+ (AR_GPIO_OE_OUT_DRV << gpio_shift));
}
-void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds)
+void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 val)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
-
- ads->ds_ctl1 |= AR_IsAggr;
- ads->ds_ctl1 &= ~AR_MoreAggr;
- ads->ds_ctl6 &= ~AR_PadDelim;
+ REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
+ AR_GPIO_BIT(gpio));
}
-void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds)
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+void ath9k_enable_rfkill(struct ath_hal *ah)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
+ REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+ AR_GPIO_INPUT_EN_VAL_RFSILENT_BB);
- ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
+ REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2,
+ AR_GPIO_INPUT_MUX2_RFSILENT);
+
+ ath9k_hw_cfg_gpio_input(ah, ah->ah_rfkill_gpio);
+ REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB);
}
+#endif
-void
-ath9k_hw_set11n_burstduration(struct ath_hal *ah, struct ath_desc *ds,
- u32 burstDuration)
+int ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
+ struct ath9k_channel *chan = ah->ah_curchan;
+ const struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ u16 ant_config;
+ u32 halNumAntConfig;
- ads->ds_ctl2 &= ~AR_BurstDur;
- ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
-}
+ halNumAntConfig = IS_CHAN_2GHZ(chan) ?
+ pCap->num_antcfg_2ghz : pCap->num_antcfg_5ghz;
-void
-ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, struct ath_desc *ds,
- u32 vmf)
-{
- struct ar5416_desc *ads = AR5416DESC(ds);
+ if (cfg < halNumAntConfig) {
+ if (!ath9k_hw_get_eeprom_antenna_cfg(ah, chan,
+ cfg, &ant_config)) {
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
+ return 0;
+ }
+ }
- if (vmf)
- ads->ds_ctl0 |= AR_VirtMoreFrag;
- else
- ads->ds_ctl0 &= ~AR_VirtMoreFrag;
+ return -EINVAL;
}
-void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp)
+u32 ath9k_hw_getdefantenna(struct ath_hal *ah)
{
- REG_WRITE(ah, AR_RXDP, rxdp);
+ return REG_READ(ah, AR_DEF_ANTENNA) & 0x7;
}
-void ath9k_hw_rxena(struct ath_hal *ah)
+void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna)
{
- REG_WRITE(ah, AR_CR, AR_CR_RXE);
+ REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
}
-bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set)
+bool ath9k_hw_setantennaswitch(struct ath_hal *ah,
+ enum ath9k_ant_setting settings,
+ struct ath9k_channel *chan,
+ u8 *tx_chainmask,
+ u8 *rx_chainmask,
+ u8 *antenna_cfgd)
{
- if (set) {
-
- REG_SET_BIT(ah, AR_DIAG_SW,
- (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
-
- if (!ath9k_hw_wait
- (ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0)) {
- u32 reg;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ static u8 tx_chainmask_cfg, rx_chainmask_cfg;
- REG_CLR_BIT(ah, AR_DIAG_SW,
- (AR_DIAG_RX_DIS |
- AR_DIAG_RX_ABORT));
+ if (AR_SREV_9280(ah)) {
+ if (!tx_chainmask_cfg) {
- reg = REG_READ(ah, AR_OBS_BUS_1);
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "%s: rx failed to go idle in 10 ms RXSM=0x%x\n",
- __func__, reg);
+ tx_chainmask_cfg = *tx_chainmask;
+ rx_chainmask_cfg = *rx_chainmask;
+ }
- return false;
+ switch (settings) {
+ case ATH9K_ANT_FIXED_A:
+ *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
+ *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
+ *antenna_cfgd = true;
+ break;
+ case ATH9K_ANT_FIXED_B:
+ if (ah->ah_caps.tx_chainmask >
+ ATH9K_ANTENNA1_CHAINMASK) {
+ *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
+ }
+ *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
+ *antenna_cfgd = true;
+ break;
+ case ATH9K_ANT_VARIABLE:
+ *tx_chainmask = tx_chainmask_cfg;
+ *rx_chainmask = rx_chainmask_cfg;
+ *antenna_cfgd = true;
+ break;
+ default:
+ break;
}
} else {
- REG_CLR_BIT(ah, AR_DIAG_SW,
- (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+ ahp->ah_diversityControl = settings;
}
return true;
}
-void
-ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0,
- u32 filter1)
-{
- REG_WRITE(ah, AR_MCAST_FIL0, filter0);
- REG_WRITE(ah, AR_MCAST_FIL1, filter1);
-}
+/*********************/
+/* General Operation */
+/*********************/
-bool
-ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 size, u32 flags)
+u32 ath9k_hw_getrxfilter(struct ath_hal *ah)
{
- struct ar5416_desc *ads = AR5416DESC(ds);
- struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ u32 bits = REG_READ(ah, AR_RX_FILTER);
+ u32 phybits = REG_READ(ah, AR_PHY_ERR);
- ads->ds_ctl1 = size & AR_BufLen;
- if (flags & ATH9K_RXDESC_INTREQ)
- ads->ds_ctl1 |= AR_RxIntrReq;
+ if (phybits & AR_PHY_ERR_RADAR)
+ bits |= ATH9K_RX_FILTER_PHYRADAR;
+ if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING))
+ bits |= ATH9K_RX_FILTER_PHYERR;
- ads->ds_rxstatus8 &= ~AR_RxDone;
- if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
- memset(&(ads->u), 0, sizeof(ads->u));
- return true;
+ return bits;
}
-int
-ath9k_hw_rxprocdesc(struct ath_hal *ah, struct ath_desc *ds,
- u32 pa, struct ath_desc *nds, u64 tsf)
+void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits)
{
- struct ar5416_desc ads;
- struct ar5416_desc *adsp = AR5416DESC(ds);
-
- if ((adsp->ds_rxstatus8 & AR_RxDone) == 0)
- return -EINPROGRESS;
-
- ads.u.rx = adsp->u.rx;
-
- ds->ds_rxstat.rs_status = 0;
- ds->ds_rxstat.rs_flags = 0;
+ u32 phybits;
- ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
- ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp;
+ REG_WRITE(ah, AR_RX_FILTER, (bits & 0xffff) | AR_RX_COMPR_BAR);
+ phybits = 0;
+ if (bits & ATH9K_RX_FILTER_PHYRADAR)
+ phybits |= AR_PHY_ERR_RADAR;
+ if (bits & ATH9K_RX_FILTER_PHYERR)
+ phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
+ REG_WRITE(ah, AR_PHY_ERR, phybits);
- ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
- ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00);
- ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01);
- ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02);
- ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10);
- ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11);
- ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12);
- if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
- ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx);
+ if (phybits)
+ REG_WRITE(ah, AR_RXCFG,
+ REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA);
else
- ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID;
-
- ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads));
- ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
-
- ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
- ds->ds_rxstat.rs_moreaggr =
- (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
- ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
- ds->ds_rxstat.rs_flags =
- (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
- ds->ds_rxstat.rs_flags |=
- (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
-
- if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
- ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
- if (ads.ds_rxstatus8 & AR_PostDelimCRCErr)
- ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST;
- if (ads.ds_rxstatus8 & AR_DecryptBusyErr)
- ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY;
-
- if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) {
-
- if (ads.ds_rxstatus8 & AR_CRCErr)
- ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC;
- else if (ads.ds_rxstatus8 & AR_PHYErr) {
- u32 phyerr;
-
- ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY;
- phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
- ds->ds_rxstat.rs_phyerr = phyerr;
- } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
- ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT;
- else if (ads.ds_rxstatus8 & AR_MichaelErr)
- ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC;
- }
-
- return 0;
+ REG_WRITE(ah, AR_RXCFG,
+ REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA);
}
-static void ath9k_hw_setup_rate_table(struct ath_hal *ah,
- struct ath9k_rate_table *rt)
+bool ath9k_hw_phy_disable(struct ath_hal *ah)
{
- int i;
-
- if (rt->rateCodeToIndex[0] != 0)
- return;
- for (i = 0; i < 256; i++)
- rt->rateCodeToIndex[i] = (u8) -1;
- for (i = 0; i < rt->rateCount; i++) {
- u8 code = rt->info[i].rateCode;
- u8 cix = rt->info[i].controlRate;
-
- rt->rateCodeToIndex[code] = i;
- rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i;
-
- rt->info[i].lpAckDuration =
- ath9k_hw_computetxtime(ah, rt,
- WLAN_CTRL_FRAME_SIZE,
- cix,
- false);
- rt->info[i].spAckDuration =
- ath9k_hw_computetxtime(ah, rt,
- WLAN_CTRL_FRAME_SIZE,
- cix,
- true);
- }
+ return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM);
}
-const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah,
- u32 mode)
+bool ath9k_hw_disable(struct ath_hal *ah)
{
- struct ath9k_rate_table *rt;
- switch (mode) {
- case ATH9K_MODE_11A:
- rt = &ar5416_11a_table;
- break;
- case ATH9K_MODE_11B:
- rt = &ar5416_11b_table;
- break;
- case ATH9K_MODE_11G:
- rt = &ar5416_11g_table;
- break;
- case ATH9K_MODE_11NG_HT20:
- case ATH9K_MODE_11NG_HT40PLUS:
- case ATH9K_MODE_11NG_HT40MINUS:
- rt = &ar5416_11ng_table;
- break;
- case ATH9K_MODE_11NA_HT20:
- case ATH9K_MODE_11NA_HT40PLUS:
- case ATH9K_MODE_11NA_HT40MINUS:
- rt = &ar5416_11na_table;
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, "%s: invalid mode 0x%x\n",
- __func__, mode);
- return NULL;
- }
- ath9k_hw_setup_rate_table(ah, rt);
- return rt;
-}
+ if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
+ return false;
-static const char *ath9k_hw_devname(u16 devid)
-{
- switch (devid) {
- case AR5416_DEVID_PCI:
- case AR5416_DEVID_PCIE:
- return "Atheros 5416";
- case AR9160_DEVID_PCI:
- return "Atheros 9160";
- case AR9280_DEVID_PCI:
- case AR9280_DEVID_PCIE:
- return "Atheros 9280";
- }
- return NULL;
+ return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD);
}
-const char *ath9k_hw_probe(u16 vendorid, u16 devid)
+bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit)
{
- return vendorid == ATHEROS_VENDOR_ID ?
- ath9k_hw_devname(devid) : NULL;
-}
+ struct ath9k_channel *chan = ah->ah_curchan;
-struct ath_hal *ath9k_hw_attach(u16 devid,
- struct ath_softc *sc,
- void __iomem *mem,
- int *error)
-{
- struct ath_hal *ah = NULL;
+ ah->ah_powerLimit = min(limit, (u32) MAX_RATE_POWER);
- switch (devid) {
- case AR5416_DEVID_PCI:
- case AR5416_DEVID_PCIE:
- case AR9160_DEVID_PCI:
- case AR9280_DEVID_PCI:
- case AR9280_DEVID_PCIE:
- ah = ath9k_hw_do_attach(devid, sc, mem, error);
- break;
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "devid=0x%x not supported.\n", devid);
- ah = NULL;
- *error = -ENXIO;
- break;
- }
+ if (ath9k_hw_set_txpower(ah, chan,
+ ath9k_regd_get_ctl(ah, chan),
+ ath9k_regd_get_antenna_allowed(ah, chan),
+ chan->maxRegTxPower * 2,
+ min((u32) MAX_RATE_POWER,
+ (u32) ah->ah_powerLimit)) != 0)
+ return false;
- return ah;
+ return true;
}
-u16
-ath9k_hw_computetxtime(struct ath_hal *ah,
- const struct ath9k_rate_table *rates,
- u32 frameLen, u16 rateix,
- bool shortPreamble)
+void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac)
{
- u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
- u32 kbps;
-
- kbps = rates->info[rateix].rateKbps;
-
- if (kbps == 0)
- return 0;
- switch (rates->info[rateix].phy) {
-
- case PHY_CCK:
- phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
- if (shortPreamble && rates->info[rateix].shortPreamble)
- phyTime >>= 1;
- numBits = frameLen << 3;
- txTime = CCK_SIFS_TIME + phyTime
- + ((numBits * 1000) / kbps);
- break;
- case PHY_OFDM:
- if (ah->ah_curchan && IS_CHAN_QUARTER_RATE(ah->ah_curchan)) {
- bitsPerSymbol =
- (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000;
-
- numBits = OFDM_PLCP_BITS + (frameLen << 3);
- numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
- txTime = OFDM_SIFS_TIME_QUARTER
- + OFDM_PREAMBLE_TIME_QUARTER
- + (numSymbols * OFDM_SYMBOL_TIME_QUARTER);
- } else if (ah->ah_curchan &&
- IS_CHAN_HALF_RATE(ah->ah_curchan)) {
- bitsPerSymbol =
- (kbps * OFDM_SYMBOL_TIME_HALF) / 1000;
-
- numBits = OFDM_PLCP_BITS + (frameLen << 3);
- numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
- txTime = OFDM_SIFS_TIME_HALF +
- OFDM_PREAMBLE_TIME_HALF
- + (numSymbols * OFDM_SYMBOL_TIME_HALF);
- } else {
- bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
-
- numBits = OFDM_PLCP_BITS + (frameLen << 3);
- numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol);
- txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME
- + (numSymbols * OFDM_SYMBOL_TIME);
- }
- break;
+ struct ath_hal_5416 *ahp = AH5416(ah);
- default:
- DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
- "%s: unknown phy %u (rate ix %u)\n", __func__,
- rates->info[rateix].phy, rateix);
- txTime = 0;
- break;
- }
- return txTime;
+ memcpy(mac, ahp->ah_macaddr, ETH_ALEN);
}
-u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags)
+bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac)
{
- if (flags & CHANNEL_2GHZ) {
- if (freq == 2484)
- return 14;
- if (freq < 2484)
- return (freq - 2407) / 5;
- else
- return 15 + ((freq - 2512) / 20);
- } else if (flags & CHANNEL_5GHZ) {
- if (ath9k_regd_is_public_safety_sku(ah) &&
- IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
- return ((freq * 10) +
- (((freq % 5) == 2) ? 5 : 0) - 49400) / 5;
- } else if ((flags & CHANNEL_A) && (freq <= 5000)) {
- return (freq - 4000) / 5;
- } else {
- return (freq - 5000) / 5;
- }
- } else {
- if (freq == 2484)
- return 14;
- if (freq < 2484)
- return (freq - 2407) / 5;
- if (freq < 5000) {
- if (ath9k_regd_is_public_safety_sku(ah)
- && IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) {
- return ((freq * 10) +
- (((freq % 5) ==
- 2) ? 5 : 0) - 49400) / 5;
- } else if (freq > 4900) {
- return (freq - 4000) / 5;
- } else {
- return 15 + ((freq - 2512) / 20);
- }
- }
- return (freq - 5000) / 5;
- }
-}
+ struct ath_hal_5416 *ahp = AH5416(ah);
-/* We can tune this as we go by monitoring really low values */
-#define ATH9K_NF_TOO_LOW -60
+ memcpy(ahp->ah_macaddr, mac, ETH_ALEN);
-/* AR5416 may return very high value (like -31 dBm), in those cases the nf
- * is incorrect and we should use the static NF value. Later we can try to
- * find out why they are reporting these values */
-static bool ath9k_hw_nf_in_range(struct ath_hal *ah, s16 nf)
-{
- if (nf > ATH9K_NF_TOO_LOW) {
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "%s: noise floor value detected (%d) is "
- "lower than what we think is a "
- "reasonable value (%d)\n",
- __func__, nf, ATH9K_NF_TOO_LOW);
- return false;
- }
return true;
}
-s16
-ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan)
+void ath9k_hw_setopmode(struct ath_hal *ah)
{
- struct ath9k_channel *ichan;
- s16 nf;
-
- ichan = ath9k_regd_check_channel(ah, chan);
- if (ichan == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
- "%s: invalid channel %u/0x%x; no mapping\n",
- __func__, chan->channel, chan->channelFlags);
- return ATH_DEFAULT_NOISE_FLOOR;
- }
- if (ichan->rawNoiseFloor == 0) {
- enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan);
- nf = NOISE_FLOOR[mode];
- } else
- nf = ichan->rawNoiseFloor;
-
- if (!ath9k_hw_nf_in_range(ah, nf))
- nf = ATH_DEFAULT_NOISE_FLOOR;
-
- return nf;
+ ath9k_hw_set_operating_mode(ah, ah->ah_opmode);
}
-bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, u32 setting)
+void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, u32 filter1)
{
- struct ath_hal_5416 *ahp = AH5416(ah);
-
- if (setting)
- ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF;
- else
- ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF;
- return true;
+ REG_WRITE(ah, AR_MCAST_FIL0, filter0);
+ REG_WRITE(ah, AR_MCAST_FIL1, filter1);
}
-bool ath9k_hw_phycounters(struct ath_hal *ah)
+void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask)
{
struct ath_hal_5416 *ahp = AH5416(ah);
- return ahp->ah_hasHwPhyCounters ? true : false;
+ memcpy(mask, ahp->ah_bssidmask, ETH_ALEN);
}
-u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q)
+bool ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask)
{
- return REG_READ(ah, AR_QTXDP(q));
-}
+ struct ath_hal_5416 *ahp = AH5416(ah);
-bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q,
- u32 txdp)
-{
- REG_WRITE(ah, AR_QTXDP(q), txdp);
+ memcpy(ahp->ah_bssidmask, mask, ETH_ALEN);
+
+ REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask));
+ REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4));
return true;
}
-bool ath9k_hw_txstart(struct ath_hal *ah, u32 q)
+void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, u16 assocId)
{
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q);
+ struct ath_hal_5416 *ahp = AH5416(ah);
- REG_WRITE(ah, AR_Q_TXE, 1 << q);
+ memcpy(ahp->ah_bssid, bssid, ETH_ALEN);
+ ahp->ah_assocId = assocId;
- return true;
+ REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid));
+ REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) |
+ ((assocId & 0x3fff) << AR_BSS_ID1_AID_S));
}
-u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q)
+u64 ath9k_hw_gettsf64(struct ath_hal *ah)
{
- u32 npend;
+ u64 tsf;
- npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
- if (npend == 0) {
+ tsf = REG_READ(ah, AR_TSF_U32);
+ tsf = (tsf << 32) | REG_READ(ah, AR_TSF_L32);
- if (REG_READ(ah, AR_Q_TXE) & (1 << q))
- npend = 1;
- }
- return npend;
+ return tsf;
}
-bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q)
+void ath9k_hw_reset_tsf(struct ath_hal *ah)
{
- u32 wait;
-
- REG_WRITE(ah, AR_Q_TXD, 1 << q);
+ int count;
- for (wait = 1000; wait != 0; wait--) {
- if (ath9k_hw_numtxpending(ah, q) == 0)
+ count = 0;
+ while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) {
+ count++;
+ if (count > 10) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+ "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
break;
- udelay(100);
+ }
+ udelay(10);
}
+ REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
+}
- if (ath9k_hw_numtxpending(ah, q)) {
- u32 tsfLow, j;
-
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: Num of pending TX Frames %d on Q %d\n",
- __func__, ath9k_hw_numtxpending(ah, q), q);
-
- for (j = 0; j < 2; j++) {
- tsfLow = REG_READ(ah, AR_TSF_L32);
- REG_WRITE(ah, AR_QUIET2,
- SM(10, AR_QUIET2_QUIET_DUR));
- REG_WRITE(ah, AR_QUIET_PERIOD, 100);
- REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
- REG_SET_BIT(ah, AR_TIMER_MODE,
- AR_QUIET_TIMER_EN);
+bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, u32 setting)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
- if ((REG_READ(ah, AR_TSF_L32) >> 10) ==
- (tsfLow >> 10)) {
- break;
- }
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: TSF have moved while trying to set "
- "quiet time TSF: 0x%08x\n",
- __func__, tsfLow);
- }
+ if (setting)
+ ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF;
+ else
+ ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF;
- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+ return true;
+}
- udelay(200);
- REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
+bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
- wait = 1000;
+ if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad slot time %u\n", us);
+ ahp->ah_slottime = (u32) -1;
+ return false;
+ } else {
+ REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us));
+ ahp->ah_slottime = us;
+ return true;
+ }
+}
- while (ath9k_hw_numtxpending(ah, q)) {
- if ((--wait) == 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_XMIT,
- "%s: Failed to stop Tx DMA in 100 "
- "msec after killing last frame\n",
- __func__);
- break;
- }
- udelay(100);
- }
+void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode)
+{
+ u32 macmode;
- REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
- }
+ if (mode == ATH9K_HT_MACMODE_2040 &&
+ !ah->ah_config.cwm_ignore_extcca)
+ macmode = AR_2040_JOINED_RX_CLEAR;
+ else
+ macmode = 0;
- REG_WRITE(ah, AR_Q_TXD, 0);
- return wait != 0;
+ REG_WRITE(ah, AR_2040_MODE, macmode);
}
diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h
index 2113818ee934..91d8f594af81 100644
--- a/drivers/net/wireless/ath9k/hw.h
+++ b/drivers/net/wireless/ath9k/hw.h
@@ -415,6 +415,9 @@ struct ar5416Stats {
#define AR5416_EEP_MINOR_VER_3 0x3
#define AR5416_EEP_MINOR_VER_7 0x7
#define AR5416_EEP_MINOR_VER_9 0x9
+#define AR5416_EEP_MINOR_VER_16 0x10
+#define AR5416_EEP_MINOR_VER_17 0x11
+#define AR5416_EEP_MINOR_VER_19 0x13
#define AR5416_NUM_5G_CAL_PIERS 8
#define AR5416_NUM_2G_CAL_PIERS 4
@@ -436,6 +439,27 @@ struct ar5416Stats {
#define AR5416_MAX_CHAINS 3
#define AR5416_PWR_TABLE_OFFSET -5
+/* Rx gain type values */
+#define AR5416_EEP_RXGAIN_23DB_BACKOFF 0
+#define AR5416_EEP_RXGAIN_13DB_BACKOFF 1
+#define AR5416_EEP_RXGAIN_ORIG 2
+
+/* Tx gain type values */
+#define AR5416_EEP_TXGAIN_ORIGINAL 0
+#define AR5416_EEP_TXGAIN_HIGH_POWER 1
+
+#define AR5416_EEP4K_START_LOC 64
+#define AR5416_EEP4K_NUM_2G_CAL_PIERS 3
+#define AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS 3
+#define AR5416_EEP4K_NUM_2G_20_TARGET_POWERS 3
+#define AR5416_EEP4K_NUM_2G_40_TARGET_POWERS 3
+#define AR5416_EEP4K_NUM_CTLS 12
+#define AR5416_EEP4K_NUM_BAND_EDGES 4
+#define AR5416_EEP4K_NUM_PD_GAINS 2
+#define AR5416_EEP4K_PD_GAINS_IN_MASK 4
+#define AR5416_EEP4K_PD_GAIN_ICEPTS 5
+#define AR5416_EEP4K_MAX_CHAINS 1
+
enum eeprom_param {
EEP_NFTHRESH_5,
EEP_NFTHRESH_2,
@@ -454,6 +478,8 @@ enum eeprom_param {
EEP_MINOR_REV,
EEP_TX_MASK,
EEP_RX_MASK,
+ EEP_RXGAIN_TYPE,
+ EEP_TXGAIN_TYPE,
};
enum ar5416_rates {
@@ -469,6 +495,11 @@ enum ar5416_rates {
Ar5416RateSize
};
+enum ath9k_hal_freq_band {
+ ATH9K_HAL_FREQ_BAND_5GHZ = 0,
+ ATH9K_HAL_FREQ_BAND_2GHZ = 1
+};
+
struct base_eep_header {
u16 length;
u16 checksum;
@@ -485,9 +516,32 @@ struct base_eep_header {
u32 binBuildNumber;
u8 deviceType;
u8 pwdclkind;
- u8 futureBase[32];
+ u8 futureBase_1[2];
+ u8 rxGainType;
+ u8 futureBase_2[3];
+ u8 txGainType;
+ u8 futureBase_3[25];
+} __packed;
+
+struct base_eep_header_4k {
+ u16 length;
+ u16 checksum;
+ u16 version;
+ u8 opCapFlags;
+ u8 eepMisc;
+ u16 regDmn[2];
+ u8 macAddr[6];
+ u8 rxMask;
+ u8 txMask;
+ u16 rfSilent;
+ u16 blueToothOptions;
+ u16 deviceCap;
+ u32 binBuildNumber;
+ u8 deviceType;
+ u8 futureBase[1];
} __packed;
+
struct spur_chan {
u16 spurChan;
u8 spurRangeLow;
@@ -540,11 +594,58 @@ struct modal_eep_header {
struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS];
} __packed;
+struct modal_eep_4k_header {
+ u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS];
+ u32 antCtrlCommon;
+ u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 switchSettling;
+ u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 adcDesiredSize;
+ u8 pgaDesiredSize;
+ u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 txEndToXpaOff;
+ u8 txEndToRxOn;
+ u8 txFrameToXpaOn;
+ u8 thresh62;
+ u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 xpdGain;
+ u8 xpd;
+ u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS];
+ u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS];
+ u8 pdGainOverlap;
+ u8 ob_01;
+ u8 db1_01;
+ u8 xpaBiasLvl;
+ u8 txFrameToDataStart;
+ u8 txFrameToPaOn;
+ u8 ht40PowerIncForPdadc;
+ u8 bswAtten[AR5416_EEP4K_MAX_CHAINS];
+ u8 bswMargin[AR5416_EEP4K_MAX_CHAINS];
+ u8 swSettleHt40;
+ u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS];
+ u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS];
+ u8 db2_01;
+ u8 version;
+ u16 ob_234;
+ u16 db1_234;
+ u16 db2_234;
+ u8 futureModal[4];
+
+ struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS];
+} __packed;
+
+
struct cal_data_per_freq {
u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
u8 vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
} __packed;
+struct cal_data_per_freq_4k {
+ u8 pwrPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_PD_GAIN_ICEPTS];
+ u8 vpdPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_PD_GAIN_ICEPTS];
+} __packed;
+
struct cal_target_power_leg {
u8 bChannel;
u8 tPow2x[4];
@@ -555,6 +656,7 @@ struct cal_target_power_ht {
u8 tPow2x[8];
} __packed;
+
#ifdef __BIG_ENDIAN_BITFIELD
struct cal_ctl_edges {
u8 bChannel;
@@ -569,10 +671,15 @@ struct cal_ctl_edges {
struct cal_ctl_data {
struct cal_ctl_edges
- ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+ ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
} __packed;
-struct ar5416_eeprom {
+struct cal_ctl_data_4k {
+ struct cal_ctl_edges
+ ctlEdges[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_BAND_EDGES];
+} __packed;
+
+struct ar5416_eeprom_def {
struct base_eep_header baseEepHeader;
u8 custData[64];
struct modal_eep_header modalHeader[2];
@@ -601,6 +708,26 @@ struct ar5416_eeprom {
u8 padding;
} __packed;
+struct ar5416_eeprom_4k {
+ struct base_eep_header_4k baseEepHeader;
+ u8 custData[20];
+ struct modal_eep_4k_header modalHeader;
+ u8 calFreqPier2G[AR5416_EEP4K_NUM_2G_CAL_PIERS];
+ struct cal_data_per_freq_4k
+ calPierData2G[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_2G_CAL_PIERS];
+ struct cal_target_power_leg
+ calTargetPowerCck[AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS];
+ struct cal_target_power_leg
+ calTargetPower2G[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS];
+ struct cal_target_power_ht
+ calTargetPower2GHT20[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS];
+ struct cal_target_power_ht
+ calTargetPower2GHT40[AR5416_EEP4K_NUM_2G_40_TARGET_POWERS];
+ u8 ctlIndex[AR5416_EEP4K_NUM_CTLS];
+ struct cal_ctl_data_4k ctlData[AR5416_EEP4K_NUM_CTLS];
+ u8 padding;
+} __packed;
+
struct ar5416IniArray {
u32 *ia_array;
u32 ia_rows;
@@ -668,9 +795,22 @@ struct hal_cal_list {
struct hal_cal_list *calNext;
};
+/*
+ * Enum to indentify the eeprom mappings
+ */
+enum hal_eep_map {
+ EEP_MAP_DEFAULT = 0x0,
+ EEP_MAP_4KBITS,
+ EEP_MAP_MAX
+};
+
+
struct ath_hal_5416 {
struct ath_hal ah;
- struct ar5416_eeprom ah_eeprom;
+ union {
+ struct ar5416_eeprom_def def;
+ struct ar5416_eeprom_4k map4k;
+ } ah_eeprom;
struct ar5416Stats ah_stats;
struct ath9k_tx_queue_info ah_txq[ATH9K_NUM_TX_QUEUES];
void __iomem *ah_cal_mem;
@@ -792,6 +932,10 @@ struct ath_hal_5416 {
struct ar5416IniArray ah_iniAddac;
struct ar5416IniArray ah_iniPcieSerdes;
struct ar5416IniArray ah_iniModesAdditional;
+ struct ar5416IniArray ah_iniModesRxGain;
+ struct ar5416IniArray ah_iniModesTxGain;
+ /* To indicate EEPROM mapping used */
+ enum hal_eep_map ah_eep_map;
};
#define AH5416(_ah) ((struct ath_hal_5416 *)(_ah))
@@ -833,13 +977,20 @@ struct ath_hal_5416 {
(AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200
#define AR5416_EEPROM_MAX 0xae0
#define ar5416_get_eep_ver(_ahp) \
- (((_ahp)->ah_eeprom.baseEepHeader.version >> 12) & 0xF)
+ (((_ahp)->ah_eeprom.def.baseEepHeader.version >> 12) & 0xF)
#define ar5416_get_eep_rev(_ahp) \
- (((_ahp)->ah_eeprom.baseEepHeader.version) & 0xFFF)
+ (((_ahp)->ah_eeprom.def.baseEepHeader.version) & 0xFFF)
#define ar5416_get_ntxchains(_txchainmask) \
(((_txchainmask >> 2) & 1) + \
((_txchainmask >> 1) & 1) + (_txchainmask & 1))
+/* EEPROM 4K bit map definations */
+#define ar5416_get_eep4k_ver(_ahp) \
+ (((_ahp)->ah_eeprom.map4k.baseEepHeader.version >> 12) & 0xF)
+#define ar5416_get_eep4k_rev(_ahp) \
+ (((_ahp)->ah_eeprom.map4k.baseEepHeader.version) & 0xFFF)
+
+
#ifdef __BIG_ENDIAN
#define AR5416_EEPROM_MAGIC 0x5aa5
#else
@@ -923,7 +1074,7 @@ struct ath_hal_5416 {
#define OFDM_PLCP_BITS_QUARTER 22
#define OFDM_SYMBOL_TIME_QUARTER 16
-u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp,
+u32 ath9k_hw_get_eeprom(struct ath_hal *ah,
enum eeprom_param param);
#endif
diff --git a/drivers/net/wireless/ath9k/initvals.h b/drivers/net/wireless/ath9k/initvals.h
index 3dd3815940a4..f3cfa16525e4 100644
--- a/drivers/net/wireless/ath9k/initvals.h
+++ b/drivers/net/wireless/ath9k/initvals.h
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+/* AR5416 to Fowl ar5146.ini */
static const u32 ar5416Modes_9100[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -31,17 +32,17 @@ static const u32 ar5416Modes_9100[][6] = {
{ 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
- { 0x00009850, 0x6de8b4e0, 0x6de8b4e0, 0x6de8b0de, 0x6de8b0de, 0x6de8b0de },
+ { 0x00009850, 0x6c48b4e0, 0x6c48b4e0, 0x6c48b0de, 0x6c48b0de, 0x6c48b0de },
{ 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e },
- { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e },
+ { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e },
{ 0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18 },
{ 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 },
{ 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 },
- { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 },
+ { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
{ 0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134, 0x00000134 },
{ 0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b },
- { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 },
+ { 0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020 },
{ 0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 },
{ 0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 },
{ 0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 },
@@ -207,7 +208,7 @@ static const u32 ar5416Common_9100[][2] = {
{ 0x00008134, 0x00000000 },
{ 0x00008138, 0x00000000 },
{ 0x0000813c, 0x00000000 },
- { 0x00008144, 0x00000000 },
+ { 0x00008144, 0xffffffff },
{ 0x00008168, 0x00000000 },
{ 0x0000816c, 0x00000000 },
{ 0x00008170, 0x32143320 },
@@ -266,7 +267,7 @@ static const u32 ar5416Common_9100[][2] = {
{ 0x0000832c, 0x00000007 },
{ 0x00008330, 0x00000302 },
{ 0x00008334, 0x00000e00 },
- { 0x00008338, 0x00000000 },
+ { 0x00008338, 0x00070000 },
{ 0x0000833c, 0x00000000 },
{ 0x00008340, 0x000107ff },
{ 0x00009808, 0x00000000 },
@@ -661,6 +662,7 @@ static const u32 ar5416Addac_9100[][2] = {
{0x000098c4, 0x00000000 },
};
+/* ar5416 - howl ar5416_howl.ini */
static const u32 ar5416Modes[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -952,7 +954,7 @@ static const u32 ar5416Common[][2] = {
{ 0x0000994c, 0x00020028 },
{ 0x0000c95c, 0x004b6a8e },
{ 0x0000c968, 0x000003ce },
- { 0x00009970, 0x190fb514 },
+ { 0x00009970, 0x190fb515 },
{ 0x00009974, 0x00000000 },
{ 0x00009978, 0x00000001 },
{ 0x0000997c, 0x00000000 },
@@ -1311,7 +1313,7 @@ static const u32 ar5416Addac[][2] = {
{0x000098cc, 0x00000000 },
};
-
+/* AR5416 9160 Sowl ar5416_sowl.ini */
static const u32 ar5416Modes_9160[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -1329,21 +1331,22 @@ static const u32 ar5416Modes_9160[][6] = {
{ 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
- { 0x00009850, 0x6d48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6d48b0e2, 0x6d48b0e2 },
+ { 0x00009850, 0x6c48b4e2, 0x6c48b4e2, 0x6c48b0e2, 0x6c48b0e2, 0x6c48b0e2 },
{ 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e },
- { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e },
+ { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e },
{ 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 },
{ 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0 },
{ 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 },
- { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 },
+ { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
{ 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
{ 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d },
- { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 },
+ { 0x00009944, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020, 0xffb81020 },
{ 0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 },
{ 0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 },
{ 0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 },
{ 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 },
+ { 0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce },
{ 0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00, 0x001a0c00 },
{ 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be },
{ 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
@@ -1505,7 +1508,7 @@ static const u32 ar5416Common_9160[][2] = {
{ 0x00008134, 0x00000000 },
{ 0x00008138, 0x00000000 },
{ 0x0000813c, 0x00000000 },
- { 0x00008144, 0x00000000 },
+ { 0x00008144, 0xffffffff },
{ 0x00008168, 0x00000000 },
{ 0x0000816c, 0x00000000 },
{ 0x00008170, 0x32143320 },
@@ -1564,7 +1567,7 @@ static const u32 ar5416Common_9160[][2] = {
{ 0x0000832c, 0x00000007 },
{ 0x00008330, 0x00000302 },
{ 0x00008334, 0x00000e00 },
- { 0x00008338, 0x00000000 },
+ { 0x00008338, 0x00ff0000 },
{ 0x0000833c, 0x00000000 },
{ 0x00008340, 0x000107ff },
{ 0x00009808, 0x00000000 },
@@ -1597,7 +1600,6 @@ static const u32 ar5416Common_9160[][2] = {
{ 0x00009958, 0x2108ecff },
{ 0x00009940, 0x00750604 },
{ 0x0000c95c, 0x004b6a8e },
- { 0x0000c968, 0x000003ce },
{ 0x00009970, 0x190fb515 },
{ 0x00009974, 0x00000000 },
{ 0x00009978, 0x00000001 },
@@ -1699,7 +1701,7 @@ static const u32 ar5416Common_9160[][2] = {
{ 0x0000a244, 0x00007bb6 },
{ 0x0000a248, 0x0fff3ffc },
{ 0x0000a24c, 0x00000001 },
- { 0x0000a250, 0x0000a000 },
+ { 0x0000a250, 0x0000e000 },
{ 0x0000a254, 0x00000000 },
{ 0x0000a258, 0x0cc75380 },
{ 0x0000a25c, 0x0f0f0f01 },
@@ -1719,7 +1721,7 @@ static const u32 ar5416Common_9160[][2] = {
{ 0x0000a34c, 0x3fffffff },
{ 0x0000a350, 0x3fffffff },
{ 0x0000a354, 0x0003ffff },
- { 0x0000a358, 0x79a8aa33 },
+ { 0x0000a358, 0x79bfaa03 },
{ 0x0000d35c, 0x07ffffef },
{ 0x0000d360, 0x0fffffe7 },
{ 0x0000d364, 0x17ffffe5 },
@@ -1842,7 +1844,6 @@ static const u32 ar5416Bank3_9160[][3] = {
};
static const u32 ar5416Bank6_9160[][3] = {
-
{ 0x0000989c, 0x00000000, 0x00000000 },
{ 0x0000989c, 0x00000000, 0x00000000 },
{ 0x0000989c, 0x00000000, 0x00000000 },
@@ -1920,7 +1921,6 @@ static const u32 ar5416Bank7_9160[][2] = {
{ 0x000098cc, 0x0000000e },
};
-
static u32 ar5416Addac_9160[][2] = {
{0x0000989c, 0x00000000 },
{0x0000989c, 0x00000000 },
@@ -1956,7 +1956,6 @@ static u32 ar5416Addac_9160[][2] = {
{0x000098cc, 0x00000000 },
};
-
static u32 ar5416Addac_91601_1[][2] = {
{0x0000989c, 0x00000000 },
{0x0000989c, 0x00000000 },
@@ -1992,8 +1991,7 @@ static u32 ar5416Addac_91601_1[][2] = {
{0x000098cc, 0x00000000 },
};
-
-
+/* XXX 9280 1 */
static const u32 ar9280Modes_9280[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -2543,9 +2541,7 @@ static const u32 ar9280Common_9280[][2] = {
{ 0x00007898, 0x2a850160 },
};
-
-
-
+/* XXX 9280 2 */
static const u32 ar9280Modes_9280_2[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -2560,26 +2556,24 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
{ 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
{ 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 },
- { 0x00009840, 0x206a012e, 0x206a012e, 0x206a022e, 0x206a022e, 0x206a022e },
+ { 0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e, 0x206a012e },
{ 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 },
- { 0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 },
- { 0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 },
- { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
- { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e },
- { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e },
+ { 0x00009850, 0x6c4000e2, 0x6c4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 },
+ { 0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
+ { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x3139605e, 0x31395d5e, 0x31395d5e },
{ 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 },
- { 0x0000c864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
+ { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
{ 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 },
- { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 },
+ { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
{ 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
- { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d },
- { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010 },
+ { 0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d },
+ { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010 },
{ 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 },
{ 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 },
{ 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 },
- { 0x0000c9b8, 0x0000000f, 0x0000000f, 0x0000001c, 0x0000001c, 0x0000001c },
- { 0x0000c9bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 },
+ { 0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c },
+ { 0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00, 0x00000c00 },
{ 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
{ 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
{ 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 },
@@ -2587,6 +2581,516 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 },
{ 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 },
+ { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 },
+ { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 },
+ { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
+ { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
+ { 0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000, 0x0004a000 },
+ { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
+ { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
+ { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 },
+};
+
+static const u32 ar9280Common_9280_2[][2] = {
+ { 0x0000000c, 0x00000000 },
+ { 0x00000030, 0x00020015 },
+ { 0x00000034, 0x00000005 },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000008 },
+ { 0x00000048, 0x00000008 },
+ { 0x0000004c, 0x00000010 },
+ { 0x00000050, 0x00000000 },
+ { 0x00000054, 0x0000001f },
+ { 0x00000800, 0x00000000 },
+ { 0x00000804, 0x00000000 },
+ { 0x00000808, 0x00000000 },
+ { 0x0000080c, 0x00000000 },
+ { 0x00000810, 0x00000000 },
+ { 0x00000814, 0x00000000 },
+ { 0x00000818, 0x00000000 },
+ { 0x0000081c, 0x00000000 },
+ { 0x00000820, 0x00000000 },
+ { 0x00000824, 0x00000000 },
+ { 0x00001040, 0x002ffc0f },
+ { 0x00001044, 0x002ffc0f },
+ { 0x00001048, 0x002ffc0f },
+ { 0x0000104c, 0x002ffc0f },
+ { 0x00001050, 0x002ffc0f },
+ { 0x00001054, 0x002ffc0f },
+ { 0x00001058, 0x002ffc0f },
+ { 0x0000105c, 0x002ffc0f },
+ { 0x00001060, 0x002ffc0f },
+ { 0x00001064, 0x002ffc0f },
+ { 0x00001230, 0x00000000 },
+ { 0x00001270, 0x00000000 },
+ { 0x00001038, 0x00000000 },
+ { 0x00001078, 0x00000000 },
+ { 0x000010b8, 0x00000000 },
+ { 0x000010f8, 0x00000000 },
+ { 0x00001138, 0x00000000 },
+ { 0x00001178, 0x00000000 },
+ { 0x000011b8, 0x00000000 },
+ { 0x000011f8, 0x00000000 },
+ { 0x00001238, 0x00000000 },
+ { 0x00001278, 0x00000000 },
+ { 0x000012b8, 0x00000000 },
+ { 0x000012f8, 0x00000000 },
+ { 0x00001338, 0x00000000 },
+ { 0x00001378, 0x00000000 },
+ { 0x000013b8, 0x00000000 },
+ { 0x000013f8, 0x00000000 },
+ { 0x00001438, 0x00000000 },
+ { 0x00001478, 0x00000000 },
+ { 0x000014b8, 0x00000000 },
+ { 0x000014f8, 0x00000000 },
+ { 0x00001538, 0x00000000 },
+ { 0x00001578, 0x00000000 },
+ { 0x000015b8, 0x00000000 },
+ { 0x000015f8, 0x00000000 },
+ { 0x00001638, 0x00000000 },
+ { 0x00001678, 0x00000000 },
+ { 0x000016b8, 0x00000000 },
+ { 0x000016f8, 0x00000000 },
+ { 0x00001738, 0x00000000 },
+ { 0x00001778, 0x00000000 },
+ { 0x000017b8, 0x00000000 },
+ { 0x000017f8, 0x00000000 },
+ { 0x0000103c, 0x00000000 },
+ { 0x0000107c, 0x00000000 },
+ { 0x000010bc, 0x00000000 },
+ { 0x000010fc, 0x00000000 },
+ { 0x0000113c, 0x00000000 },
+ { 0x0000117c, 0x00000000 },
+ { 0x000011bc, 0x00000000 },
+ { 0x000011fc, 0x00000000 },
+ { 0x0000123c, 0x00000000 },
+ { 0x0000127c, 0x00000000 },
+ { 0x000012bc, 0x00000000 },
+ { 0x000012fc, 0x00000000 },
+ { 0x0000133c, 0x00000000 },
+ { 0x0000137c, 0x00000000 },
+ { 0x000013bc, 0x00000000 },
+ { 0x000013fc, 0x00000000 },
+ { 0x0000143c, 0x00000000 },
+ { 0x0000147c, 0x00000000 },
+ { 0x00004030, 0x00000002 },
+ { 0x0000403c, 0x00000002 },
+ { 0x00004024, 0x0000001f },
+ { 0x00004060, 0x00000000 },
+ { 0x00004064, 0x00000000 },
+ { 0x00007010, 0x00000033 },
+ { 0x00007034, 0x00000002 },
+ { 0x00007038, 0x000004c2 },
+ { 0x00008004, 0x00000000 },
+ { 0x00008008, 0x00000000 },
+ { 0x0000800c, 0x00000000 },
+ { 0x00008018, 0x00000700 },
+ { 0x00008020, 0x00000000 },
+ { 0x00008038, 0x00000000 },
+ { 0x0000803c, 0x00000000 },
+ { 0x00008048, 0x40000000 },
+ { 0x00008054, 0x00000000 },
+ { 0x00008058, 0x00000000 },
+ { 0x0000805c, 0x000fc78f },
+ { 0x00008060, 0x0000000f },
+ { 0x00008064, 0x00000000 },
+ { 0x00008070, 0x00000000 },
+ { 0x000080c0, 0x2a80001a },
+ { 0x000080c4, 0x05dc01e0 },
+ { 0x000080c8, 0x1f402710 },
+ { 0x000080cc, 0x01f40000 },
+ { 0x000080d0, 0x00001e00 },
+ { 0x000080d4, 0x00000000 },
+ { 0x000080d8, 0x00400000 },
+ { 0x000080e0, 0xffffffff },
+ { 0x000080e4, 0x0000ffff },
+ { 0x000080e8, 0x003f3f3f },
+ { 0x000080ec, 0x00000000 },
+ { 0x000080f0, 0x00000000 },
+ { 0x000080f4, 0x00000000 },
+ { 0x000080f8, 0x00000000 },
+ { 0x000080fc, 0x00020000 },
+ { 0x00008100, 0x00020000 },
+ { 0x00008104, 0x00000001 },
+ { 0x00008108, 0x00000052 },
+ { 0x0000810c, 0x00000000 },
+ { 0x00008110, 0x00000168 },
+ { 0x00008118, 0x000100aa },
+ { 0x0000811c, 0x00003210 },
+ { 0x00008120, 0x08f04800 },
+ { 0x00008124, 0x00000000 },
+ { 0x00008128, 0x00000000 },
+ { 0x0000812c, 0x00000000 },
+ { 0x00008130, 0x00000000 },
+ { 0x00008134, 0x00000000 },
+ { 0x00008138, 0x00000000 },
+ { 0x0000813c, 0x00000000 },
+ { 0x00008144, 0xffffffff },
+ { 0x00008168, 0x00000000 },
+ { 0x0000816c, 0x00000000 },
+ { 0x00008170, 0x32143320 },
+ { 0x00008174, 0xfaa4fa50 },
+ { 0x00008178, 0x00000100 },
+ { 0x0000817c, 0x00000000 },
+ { 0x000081c0, 0x00000000 },
+ { 0x000081d0, 0x00003210 },
+ { 0x000081ec, 0x00000000 },
+ { 0x000081f0, 0x00000000 },
+ { 0x000081f4, 0x00000000 },
+ { 0x000081f8, 0x00000000 },
+ { 0x000081fc, 0x00000000 },
+ { 0x00008200, 0x00000000 },
+ { 0x00008204, 0x00000000 },
+ { 0x00008208, 0x00000000 },
+ { 0x0000820c, 0x00000000 },
+ { 0x00008210, 0x00000000 },
+ { 0x00008214, 0x00000000 },
+ { 0x00008218, 0x00000000 },
+ { 0x0000821c, 0x00000000 },
+ { 0x00008220, 0x00000000 },
+ { 0x00008224, 0x00000000 },
+ { 0x00008228, 0x00000000 },
+ { 0x0000822c, 0x00000000 },
+ { 0x00008230, 0x00000000 },
+ { 0x00008234, 0x00000000 },
+ { 0x00008238, 0x00000000 },
+ { 0x0000823c, 0x00000000 },
+ { 0x00008240, 0x00100000 },
+ { 0x00008244, 0x0010f400 },
+ { 0x00008248, 0x00000100 },
+ { 0x0000824c, 0x0001e800 },
+ { 0x00008250, 0x00000000 },
+ { 0x00008254, 0x00000000 },
+ { 0x00008258, 0x00000000 },
+ { 0x0000825c, 0x400000ff },
+ { 0x00008260, 0x00080922 },
+ { 0x00008264, 0xa8a00010 },
+ { 0x00008270, 0x00000000 },
+ { 0x00008274, 0x40000000 },
+ { 0x00008278, 0x003e4180 },
+ { 0x0000827c, 0x00000000 },
+ { 0x00008284, 0x0000002c },
+ { 0x00008288, 0x0000002c },
+ { 0x0000828c, 0x00000000 },
+ { 0x00008294, 0x00000000 },
+ { 0x00008298, 0x00000000 },
+ { 0x0000829c, 0x00000000 },
+ { 0x00008300, 0x00000040 },
+ { 0x00008314, 0x00000000 },
+ { 0x00008328, 0x00000000 },
+ { 0x0000832c, 0x00000007 },
+ { 0x00008330, 0x00000302 },
+ { 0x00008334, 0x00000e00 },
+ { 0x00008338, 0x00ff0000 },
+ { 0x0000833c, 0x00000000 },
+ { 0x00008340, 0x000107ff },
+ { 0x00008344, 0x00581043 },
+ { 0x00009808, 0x00000000 },
+ { 0x0000980c, 0xafa68e30 },
+ { 0x00009810, 0xfd14e000 },
+ { 0x00009814, 0x9c0a9f6b },
+ { 0x0000981c, 0x00000000 },
+ { 0x0000982c, 0x0000a000 },
+ { 0x00009830, 0x00000000 },
+ { 0x0000983c, 0x00200400 },
+ { 0x0000984c, 0x0040233c },
+ { 0x0000a84c, 0x0040233c },
+ { 0x00009854, 0x00000044 },
+ { 0x00009900, 0x00000000 },
+ { 0x00009904, 0x00000000 },
+ { 0x00009908, 0x00000000 },
+ { 0x0000990c, 0x00000000 },
+ { 0x00009910, 0x01002310 },
+ { 0x0000991c, 0x10000fff },
+ { 0x00009920, 0x04900000 },
+ { 0x0000a920, 0x04900000 },
+ { 0x00009928, 0x00000001 },
+ { 0x0000992c, 0x00000004 },
+ { 0x00009934, 0x1e1f2022 },
+ { 0x00009938, 0x0a0b0c0d },
+ { 0x0000993c, 0x00000000 },
+ { 0x00009948, 0x9280c00a },
+ { 0x0000994c, 0x00020028 },
+ { 0x00009954, 0x5f3ca3de },
+ { 0x00009958, 0x2108ecff },
+ { 0x00009940, 0x14750604 },
+ { 0x0000c95c, 0x004b6a8e },
+ { 0x00009968, 0x000003ce },
+ { 0x00009970, 0x190fb515 },
+ { 0x00009974, 0x00000000 },
+ { 0x00009978, 0x00000001 },
+ { 0x0000997c, 0x00000000 },
+ { 0x00009980, 0x00000000 },
+ { 0x00009984, 0x00000000 },
+ { 0x00009988, 0x00000000 },
+ { 0x0000998c, 0x00000000 },
+ { 0x00009990, 0x00000000 },
+ { 0x00009994, 0x00000000 },
+ { 0x00009998, 0x00000000 },
+ { 0x0000999c, 0x00000000 },
+ { 0x000099a0, 0x00000000 },
+ { 0x000099a4, 0x00000001 },
+ { 0x000099a8, 0x201fff00 },
+ { 0x000099ac, 0x006f0000 },
+ { 0x000099b0, 0x03051000 },
+ { 0x000099b4, 0x00000820 },
+ { 0x000099dc, 0x00000000 },
+ { 0x000099e0, 0x00000000 },
+ { 0x000099e4, 0xaaaaaaaa },
+ { 0x000099e8, 0x3c466478 },
+ { 0x000099ec, 0x0cc80caa },
+ { 0x000099f0, 0x00000000 },
+ { 0x000099fc, 0x00001042 },
+ { 0x0000a208, 0x803e4788 },
+ { 0x0000a210, 0x4080a333 },
+ { 0x0000a214, 0x40206c10 },
+ { 0x0000a218, 0x009c4060 },
+ { 0x0000a220, 0x01834061 },
+ { 0x0000a224, 0x00000400 },
+ { 0x0000a228, 0x000003b5 },
+ { 0x0000a22c, 0x233f7180 },
+ { 0x0000a234, 0x20202020 },
+ { 0x0000a238, 0x20202020 },
+ { 0x0000a23c, 0x13c88000 },
+ { 0x0000a240, 0x38490a20 },
+ { 0x0000a244, 0x00007bb6 },
+ { 0x0000a248, 0x0fff3ffc },
+ { 0x0000a24c, 0x00000000 },
+ { 0x0000a254, 0x00000000 },
+ { 0x0000a258, 0x0cdbd380 },
+ { 0x0000a25c, 0x0f0f0f01 },
+ { 0x0000a260, 0xdfa91f01 },
+ { 0x0000a268, 0x00000000 },
+ { 0x0000a26c, 0x0ebae9c6 },
+ { 0x0000b26c, 0x0ebae9c6 },
+ { 0x0000d270, 0x00820820 },
+ { 0x0000a278, 0x1ce739ce },
+ { 0x0000d35c, 0x07ffffef },
+ { 0x0000d360, 0x0fffffe7 },
+ { 0x0000d364, 0x17ffffe5 },
+ { 0x0000d368, 0x1fffffe4 },
+ { 0x0000d36c, 0x37ffffe3 },
+ { 0x0000d370, 0x3fffffe3 },
+ { 0x0000d374, 0x57ffffe3 },
+ { 0x0000d378, 0x5fffffe2 },
+ { 0x0000d37c, 0x7fffffe2 },
+ { 0x0000d380, 0x7f3c7bba },
+ { 0x0000d384, 0xf3307ff0 },
+ { 0x0000a388, 0x0c000000 },
+ { 0x0000a38c, 0x20202020 },
+ { 0x0000a390, 0x20202020 },
+ { 0x0000a394, 0x1ce739ce },
+ { 0x0000a398, 0x000001ce },
+ { 0x0000a39c, 0x00000001 },
+ { 0x0000a3a0, 0x00000000 },
+ { 0x0000a3a4, 0x00000000 },
+ { 0x0000a3a8, 0x00000000 },
+ { 0x0000a3ac, 0x00000000 },
+ { 0x0000a3b0, 0x00000000 },
+ { 0x0000a3b4, 0x00000000 },
+ { 0x0000a3b8, 0x00000000 },
+ { 0x0000a3bc, 0x00000000 },
+ { 0x0000a3c0, 0x00000000 },
+ { 0x0000a3c4, 0x00000000 },
+ { 0x0000a3c8, 0x00000246 },
+ { 0x0000a3cc, 0x20202020 },
+ { 0x0000a3d0, 0x20202020 },
+ { 0x0000a3d4, 0x20202020 },
+ { 0x0000a3dc, 0x1ce739ce },
+ { 0x0000a3e0, 0x000001ce },
+ { 0x0000a3e4, 0x00000000 },
+ { 0x0000a3e8, 0x18c43433 },
+ { 0x0000a3ec, 0x00f70081 },
+ { 0x00007800, 0x00040000 },
+ { 0x00007804, 0xdb005012 },
+ { 0x00007808, 0x04924914 },
+ { 0x0000780c, 0x21084210 },
+ { 0x00007810, 0x6d801300 },
+ { 0x00007818, 0x07e41000 },
+ { 0x0000781c, 0x00392000 },
+ { 0x00007820, 0x92592480 },
+ { 0x00007824, 0x00040000 },
+ { 0x00007828, 0xdb005012 },
+ { 0x0000782c, 0x04924914 },
+ { 0x00007830, 0x21084210 },
+ { 0x00007834, 0x6d801300 },
+ { 0x0000783c, 0x07e40000 },
+ { 0x00007840, 0x00392000 },
+ { 0x00007844, 0x92592480 },
+ { 0x00007848, 0x00100000 },
+ { 0x0000784c, 0x773f0567 },
+ { 0x00007850, 0x54214514 },
+ { 0x00007854, 0x12035828 },
+ { 0x00007858, 0x9259269a },
+ { 0x00007860, 0x52802000 },
+ { 0x00007864, 0x0a8e370e },
+ { 0x00007868, 0xc0102850 },
+ { 0x0000786c, 0x812d4000 },
+ { 0x00007870, 0x807ec400 },
+ { 0x00007874, 0x001b6db0 },
+ { 0x00007878, 0x00376b63 },
+ { 0x0000787c, 0x06db6db6 },
+ { 0x00007880, 0x006d8000 },
+ { 0x00007884, 0xffeffffe },
+ { 0x00007888, 0xffeffffe },
+ { 0x0000788c, 0x00010000 },
+ { 0x00007890, 0x02060aeb },
+ { 0x00007898, 0x2a850160 },
+};
+
+static const u32 ar9280Modes_fast_clock_9280_2[][3] = {
+ { 0x00001030, 0x00000268, 0x000004d0 },
+ { 0x00001070, 0x0000018c, 0x00000318 },
+ { 0x000010b0, 0x00000fd0, 0x00001fa0 },
+ { 0x00008014, 0x044c044c, 0x08980898 },
+ { 0x0000801c, 0x148ec02b, 0x148ec057 },
+ { 0x00008318, 0x000044c0, 0x00008980 },
+ { 0x00009820, 0x02020200, 0x02020200 },
+ { 0x00009824, 0x00000f0f, 0x00000f0f },
+ { 0x00009828, 0x0b020001, 0x0b020001 },
+ { 0x00009834, 0x00000f0f, 0x00000f0f },
+ { 0x00009844, 0x03721821, 0x03721821 },
+ { 0x00009914, 0x00000898, 0x00001130 },
+ { 0x00009918, 0x0000000b, 0x00000016 },
+ { 0x00009944, 0xdfbc1210, 0xdfbc1210 },
+};
+
+static const u32 ar9280Modes_backoff_23db_rxgain_9280_2[][6] = {
+ { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 },
+ { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 },
+ { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 },
+ { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 },
+ { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c },
+ { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 },
+ { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 },
+ { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 },
+ { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c },
+ { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 },
+ { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 },
+ { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 },
+ { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c },
+ { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 },
+ { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 },
+ { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 },
+ { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c },
+ { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 },
+ { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 },
+ { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 },
+ { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 },
+ { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 },
+ { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c },
+ { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 },
+ { 0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194 },
+ { 0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0 },
+ { 0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c },
+ { 0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8 },
+ { 0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284 },
+ { 0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288 },
+ { 0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224 },
+ { 0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290 },
+ { 0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300 },
+ { 0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304 },
+ { 0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308 },
+ { 0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c },
+ { 0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380 },
+ { 0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384 },
+ { 0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700 },
+ { 0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704 },
+ { 0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708 },
+ { 0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c },
+ { 0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780 },
+ { 0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784 },
+ { 0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00 },
+ { 0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04 },
+ { 0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08 },
+ { 0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c },
+ { 0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b10, 0x00008b10, 0x00008b10 },
+ { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b14, 0x00008b14, 0x00008b14 },
+ { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b01, 0x00008b01, 0x00008b01 },
+ { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b05, 0x00008b05, 0x00008b05 },
+ { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b09, 0x00008b09, 0x00008b09 },
+ { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008b0d, 0x00008b0d, 0x00008b0d },
+ { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008b11, 0x00008b11, 0x00008b11 },
+ { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008b15, 0x00008b15, 0x00008b15 },
+ { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008b02, 0x00008b02, 0x00008b02 },
+ { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008b06, 0x00008b06, 0x00008b06 },
+ { 0x00009ae8, 0x0000b780, 0x0000b780, 0x00008b0a, 0x00008b0a, 0x00008b0a },
+ { 0x00009aec, 0x0000b784, 0x0000b784, 0x00008b0e, 0x00008b0e, 0x00008b0e },
+ { 0x00009af0, 0x0000b788, 0x0000b788, 0x00008b12, 0x00008b12, 0x00008b12 },
+ { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00008b16, 0x00008b16, 0x00008b16 },
+ { 0x00009af8, 0x0000b790, 0x0000b790, 0x00008b03, 0x00008b03, 0x00008b03 },
+ { 0x00009afc, 0x0000b794, 0x0000b794, 0x00008b07, 0x00008b07, 0x00008b07 },
+ { 0x00009b00, 0x0000b798, 0x0000b798, 0x00008b0b, 0x00008b0b, 0x00008b0b },
+ { 0x00009b04, 0x0000d784, 0x0000d784, 0x00008b0f, 0x00008b0f, 0x00008b0f },
+ { 0x00009b08, 0x0000d788, 0x0000d788, 0x00008b13, 0x00008b13, 0x00008b13 },
+ { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00008b17, 0x00008b17, 0x00008b17 },
+ { 0x00009b10, 0x0000d790, 0x0000d790, 0x00008b23, 0x00008b23, 0x00008b23 },
+ { 0x00009b14, 0x0000f780, 0x0000f780, 0x00008b27, 0x00008b27, 0x00008b27 },
+ { 0x00009b18, 0x0000f784, 0x0000f784, 0x00008b2b, 0x00008b2b, 0x00008b2b },
+ { 0x00009b1c, 0x0000f788, 0x0000f788, 0x00008b2f, 0x00008b2f, 0x00008b2f },
+ { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x00008b33, 0x00008b33, 0x00008b33 },
+ { 0x00009b24, 0x0000f790, 0x0000f790, 0x00008b37, 0x00008b37, 0x00008b37 },
+ { 0x00009b28, 0x0000f794, 0x0000f794, 0x00008b43, 0x00008b43, 0x00008b43 },
+ { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x00008b47, 0x00008b47, 0x00008b47 },
+ { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00008b4b, 0x00008b4b, 0x00008b4b },
+ { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00008b4f, 0x00008b4f, 0x00008b4f },
+ { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00008b53, 0x00008b53, 0x00008b53 },
+ { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00008b57, 0x00008b57, 0x00008b57 },
+ { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x00008b5b, 0x00008b5b, 0x00008b5b },
+ { 0x00009848, 0x00001066, 0x00001066, 0x00001050, 0x00001050, 0x00001050 },
+ { 0x0000a848, 0x00001066, 0x00001066, 0x00001050, 0x00001050, 0x00001050 },
+};
+
+static const u32 ar9280Modes_original_rxgain_9280_2[][6] = {
{ 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 },
{ 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 },
{ 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 },
@@ -2715,14 +3219,172 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db },
{ 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db },
{ 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db },
- { 0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 },
- { 0x0000a208, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788 },
- { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 },
- { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 },
- { 0x0000a21c, 0x1463800a, 0x1463800a, 0x1463800a, 0x1463800a, 0x1463800a },
- { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
- { 0x0000a250, 0x001ff000, 0x001ff000, 0x001da000, 0x001da000, 0x001da000 },
- { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
+ { 0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 },
+ { 0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 },
+};
+
+static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = {
+ { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 },
+ { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 },
+ { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 },
+ { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 },
+ { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c },
+ { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 },
+ { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 },
+ { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 },
+ { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c },
+ { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 },
+ { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 },
+ { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 },
+ { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c },
+ { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 },
+ { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 },
+ { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 },
+ { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c },
+ { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 },
+ { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 },
+ { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 },
+ { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 },
+ { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 },
+ { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c },
+ { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 },
+ { 0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194 },
+ { 0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0 },
+ { 0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c },
+ { 0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8 },
+ { 0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284 },
+ { 0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288 },
+ { 0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224 },
+ { 0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290 },
+ { 0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300 },
+ { 0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304 },
+ { 0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308 },
+ { 0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c },
+ { 0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380 },
+ { 0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384 },
+ { 0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700 },
+ { 0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704 },
+ { 0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708 },
+ { 0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c },
+ { 0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780 },
+ { 0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784 },
+ { 0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00 },
+ { 0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04 },
+ { 0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08 },
+ { 0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c },
+ { 0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80 },
+ { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84 },
+ { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88 },
+ { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c },
+ { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90 },
+ { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80 },
+ { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84 },
+ { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88 },
+ { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c },
+ { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90 },
+ { 0x00009ae8, 0x0000b780, 0x0000b780, 0x00009310, 0x00009310, 0x00009310 },
+ { 0x00009aec, 0x0000b784, 0x0000b784, 0x00009314, 0x00009314, 0x00009314 },
+ { 0x00009af0, 0x0000b788, 0x0000b788, 0x00009320, 0x00009320, 0x00009320 },
+ { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009324, 0x00009324, 0x00009324 },
+ { 0x00009af8, 0x0000b790, 0x0000b790, 0x00009328, 0x00009328, 0x00009328 },
+ { 0x00009afc, 0x0000b794, 0x0000b794, 0x0000932c, 0x0000932c, 0x0000932c },
+ { 0x00009b00, 0x0000b798, 0x0000b798, 0x00009330, 0x00009330, 0x00009330 },
+ { 0x00009b04, 0x0000d784, 0x0000d784, 0x00009334, 0x00009334, 0x00009334 },
+ { 0x00009b08, 0x0000d788, 0x0000d788, 0x00009321, 0x00009321, 0x00009321 },
+ { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009325, 0x00009325, 0x00009325 },
+ { 0x00009b10, 0x0000d790, 0x0000d790, 0x00009329, 0x00009329, 0x00009329 },
+ { 0x00009b14, 0x0000f780, 0x0000f780, 0x0000932d, 0x0000932d, 0x0000932d },
+ { 0x00009b18, 0x0000f784, 0x0000f784, 0x00009331, 0x00009331, 0x00009331 },
+ { 0x00009b1c, 0x0000f788, 0x0000f788, 0x00009335, 0x00009335, 0x00009335 },
+ { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x00009322, 0x00009322, 0x00009322 },
+ { 0x00009b24, 0x0000f790, 0x0000f790, 0x00009326, 0x00009326, 0x00009326 },
+ { 0x00009b28, 0x0000f794, 0x0000f794, 0x0000932a, 0x0000932a, 0x0000932a },
+ { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x0000932e, 0x0000932e, 0x0000932e },
+ { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x00009332, 0x00009332, 0x00009332 },
+ { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x00009336, 0x00009336, 0x00009336 },
+ { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x00009323, 0x00009323, 0x00009323 },
+ { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x00009327, 0x00009327, 0x00009327 },
+ { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x0000932b, 0x0000932b, 0x0000932b },
+ { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x0000932f, 0x0000932f, 0x0000932f },
+ { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x00009333, 0x00009333, 0x00009333 },
+ { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x00009337, 0x00009337, 0x00009337 },
+ { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x00009343, 0x00009343, 0x00009343 },
+ { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x00009347, 0x00009347, 0x00009347 },
+ { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x0000934b, 0x0000934b, 0x0000934b },
+ { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x0000934f, 0x0000934f, 0x0000934f },
+ { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x00009353, 0x00009353, 0x00009353 },
+ { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x00009357, 0x00009357, 0x00009357 },
+ { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x0000935b, 0x0000935b, 0x0000935b },
+ { 0x00009848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a },
+ { 0x0000a848, 0x00001066, 0x00001066, 0x0000105a, 0x0000105a, 0x0000105a },
+};
+
+static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = {
+ { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002, 0x00004002 },
+ { 0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008, 0x00007008 },
+ { 0x0000a30c, 0x0000a006, 0x0000a006, 0x0000c010, 0x0000c010, 0x0000c010 },
+ { 0x0000a310, 0x0000e012, 0x0000e012, 0x00010012, 0x00010012, 0x00010012 },
+ { 0x0000a314, 0x00011014, 0x00011014, 0x00013014, 0x00013014, 0x00013014 },
+ { 0x0000a318, 0x0001504a, 0x0001504a, 0x0001820a, 0x0001820a, 0x0001820a },
+ { 0x0000a31c, 0x0001904c, 0x0001904c, 0x0001b211, 0x0001b211, 0x0001b211 },
+ { 0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213 },
+ { 0x0000a324, 0x00020092, 0x00020092, 0x00022411, 0x00022411, 0x00022411 },
+ { 0x0000a328, 0x0002410a, 0x0002410a, 0x00025413, 0x00025413, 0x00025413 },
+ { 0x0000a32c, 0x0002710c, 0x0002710c, 0x00029811, 0x00029811, 0x00029811 },
+ { 0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002c813, 0x0002c813, 0x0002c813 },
+ { 0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030a14, 0x00030a14, 0x00030a14 },
+ { 0x0000a338, 0x000321ec, 0x000321ec, 0x00035a50, 0x00035a50, 0x00035a50 },
+ { 0x0000a33c, 0x000321ec, 0x000321ec, 0x00039c4c, 0x00039c4c, 0x00039c4c },
+ { 0x0000a340, 0x000321ec, 0x000321ec, 0x0003de8a, 0x0003de8a, 0x0003de8a },
+ { 0x0000a344, 0x000321ec, 0x000321ec, 0x00042e92, 0x00042e92, 0x00042e92 },
+ { 0x0000a348, 0x000321ec, 0x000321ec, 0x00046ed2, 0x00046ed2, 0x00046ed2 },
+ { 0x0000a34c, 0x000321ec, 0x000321ec, 0x0004bed5, 0x0004bed5, 0x0004bed5 },
+ { 0x0000a350, 0x000321ec, 0x000321ec, 0x0004ff54, 0x0004ff54, 0x0004ff54 },
+ { 0x0000a354, 0x000321ec, 0x000321ec, 0x00053fd5, 0x00053fd5, 0x00053fd5 },
+ { 0x00007814, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff },
+ { 0x00007838, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff, 0x00198eff },
+ { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce },
+};
+
+static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = {
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 },
{ 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 },
@@ -2745,14 +3407,363 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a, 0x00046e8a },
{ 0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9, 0x00049ec9 },
{ 0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42, 0x0004bf42 },
+ { 0x00007814, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff },
+ { 0x00007838, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff, 0x0019beff },
+ { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce },
+};
+
+static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01dcffc },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+
+static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01dcffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+
+/* AR9285 */
+static const u_int32_t ar9285Modes_9285[][6] = {
+ { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
+ { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
+ { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 },
+ { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 },
+ { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 },
+ { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f },
+ { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 },
+ { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 },
+ { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 },
+ { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
+ { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
+ { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
+ { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 },
+ { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e },
+ { 0x00009844, 0x0372161e, 0x0372161e, 0x03720020, 0x03720020, 0x037216a0 },
+ { 0x00009848, 0x00001066, 0x00001066, 0x0000004e, 0x0000004e, 0x00001059 },
+ { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
+ { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
+ { 0x0000985c, 0x3139605e, 0x3139605e, 0x3136605e, 0x3136605e, 0x3139605e },
+ { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20, 0x00058d18 },
+ { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
+ { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
+ { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 },
+ { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
+ { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
+ { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d },
+ { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1020, 0xdfbc1020, 0xdfbc1010 },
+ { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x000099b8, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c, 0x00cf4d1c },
+ { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 },
+ { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
+ { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
+ { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 },
+ { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 },
+ { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 },
+ { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00009a00, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 },
+ { 0x00009a04, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 },
+ { 0x00009a08, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 },
+ { 0x00009a0c, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 },
+ { 0x00009a10, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 },
+ { 0x00009a14, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 },
+ { 0x00009a18, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 },
+ { 0x00009a1c, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x00009a20, 0x00000000, 0x00000000, 0x00068114, 0x00068114, 0x00000000 },
+ { 0x00009a24, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 },
+ { 0x00009a28, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 },
+ { 0x00009a2c, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 },
+ { 0x00009a30, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 },
+ { 0x00009a34, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 },
+ { 0x00009a38, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 },
+ { 0x00009a3c, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 },
+ { 0x00009a40, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 },
+ { 0x00009a44, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 },
+ { 0x00009a48, 0x00000000, 0x00000000, 0x00068284, 0x00068284, 0x00000000 },
+ { 0x00009a4c, 0x00000000, 0x00000000, 0x00068288, 0x00068288, 0x00000000 },
+ { 0x00009a50, 0x00000000, 0x00000000, 0x00068220, 0x00068220, 0x00000000 },
+ { 0x00009a54, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 },
+ { 0x00009a58, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 },
+ { 0x00009a5c, 0x00000000, 0x00000000, 0x00068304, 0x00068304, 0x00000000 },
+ { 0x00009a60, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 },
+ { 0x00009a64, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 },
+ { 0x00009a68, 0x00000000, 0x00000000, 0x00068380, 0x00068380, 0x00000000 },
+ { 0x00009a6c, 0x00000000, 0x00000000, 0x00068384, 0x00068384, 0x00000000 },
+ { 0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 },
+ { 0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 },
+ { 0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 },
+ { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
+ { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
+ { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
+ { 0x00009a88, 0x00000000, 0x00000000, 0x00068b04, 0x00068b04, 0x00000000 },
+ { 0x00009a8c, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 },
+ { 0x00009a90, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 },
+ { 0x00009a94, 0x00000000, 0x00000000, 0x00068b0c, 0x00068b0c, 0x00000000 },
+ { 0x00009a98, 0x00000000, 0x00000000, 0x00068b80, 0x00068b80, 0x00000000 },
+ { 0x00009a9c, 0x00000000, 0x00000000, 0x00068b84, 0x00068b84, 0x00000000 },
+ { 0x00009aa0, 0x00000000, 0x00000000, 0x00068b88, 0x00068b88, 0x00000000 },
+ { 0x00009aa4, 0x00000000, 0x00000000, 0x00068b8c, 0x00068b8c, 0x00000000 },
+ { 0x00009aa8, 0x00000000, 0x00000000, 0x000b8b90, 0x000b8b90, 0x00000000 },
+ { 0x00009aac, 0x00000000, 0x00000000, 0x000b8f80, 0x000b8f80, 0x00000000 },
+ { 0x00009ab0, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x00009ab4, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 },
+ { 0x00009ab8, 0x00000000, 0x00000000, 0x000b8f8c, 0x000b8f8c, 0x00000000 },
+ { 0x00009abc, 0x00000000, 0x00000000, 0x000b8f90, 0x000b8f90, 0x00000000 },
+ { 0x00009ac0, 0x00000000, 0x00000000, 0x000bb30c, 0x000bb30c, 0x00000000 },
+ { 0x00009ac4, 0x00000000, 0x00000000, 0x000bb310, 0x000bb310, 0x00000000 },
+ { 0x00009ac8, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 },
+ { 0x00009acc, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 },
+ { 0x00009ad0, 0x00000000, 0x00000000, 0x000bb324, 0x000bb324, 0x00000000 },
+ { 0x00009ad4, 0x00000000, 0x00000000, 0x000bb704, 0x000bb704, 0x00000000 },
+ { 0x00009ad8, 0x00000000, 0x00000000, 0x000f96a4, 0x000f96a4, 0x00000000 },
+ { 0x00009adc, 0x00000000, 0x00000000, 0x000f96a8, 0x000f96a8, 0x00000000 },
+ { 0x00009ae0, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 },
+ { 0x00009ae4, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 },
+ { 0x00009ae8, 0x00000000, 0x00000000, 0x000f9720, 0x000f9720, 0x00000000 },
+ { 0x00009aec, 0x00000000, 0x00000000, 0x000f9724, 0x000f9724, 0x00000000 },
+ { 0x00009af0, 0x00000000, 0x00000000, 0x000f9728, 0x000f9728, 0x00000000 },
+ { 0x00009af4, 0x00000000, 0x00000000, 0x000f972c, 0x000f972c, 0x00000000 },
+ { 0x00009af8, 0x00000000, 0x00000000, 0x000f97a0, 0x000f97a0, 0x00000000 },
+ { 0x00009afc, 0x00000000, 0x00000000, 0x000f97a4, 0x000f97a4, 0x00000000 },
+ { 0x00009b00, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 },
+ { 0x00009b04, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 },
+ { 0x00009b08, 0x00000000, 0x00000000, 0x000fb7b4, 0x000fb7b4, 0x00000000 },
+ { 0x00009b0c, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 },
+ { 0x00009b10, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 },
+ { 0x00009b14, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 },
+ { 0x00009b18, 0x00000000, 0x00000000, 0x000fb7ad, 0x000fb7ad, 0x00000000 },
+ { 0x00009b1c, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 },
+ { 0x00009b20, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 },
+ { 0x00009b24, 0x00000000, 0x00000000, 0x000fb7b9, 0x000fb7b9, 0x00000000 },
+ { 0x00009b28, 0x00000000, 0x00000000, 0x000fb7c5, 0x000fb7c5, 0x00000000 },
+ { 0x00009b2c, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 },
+ { 0x00009b30, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 },
+ { 0x00009b34, 0x00000000, 0x00000000, 0x000fb7d5, 0x000fb7d5, 0x00000000 },
+ { 0x00009b38, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 },
+ { 0x00009b3c, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 },
+ { 0x00009b40, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 },
+ { 0x00009b44, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 },
+ { 0x00009b48, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 },
+ { 0x00009b4c, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 },
+ { 0x00009b50, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 },
+ { 0x00009b54, 0x00000000, 0x00000000, 0x000fb7c7, 0x000fb7c7, 0x00000000 },
+ { 0x00009b58, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 },
+ { 0x00009b5c, 0x00000000, 0x00000000, 0x000fb7cf, 0x000fb7cf, 0x00000000 },
+ { 0x00009b60, 0x00000000, 0x00000000, 0x000fb7d7, 0x000fb7d7, 0x00000000 },
+ { 0x00009b64, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b68, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b6c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b70, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b74, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b78, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b7c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b80, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b84, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b88, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b8c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b90, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b94, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b98, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b9c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bac, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bbc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bcc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bdc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bec, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bfc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x0000aa00, 0x00000000, 0x00000000, 0x0006801c, 0x0006801c, 0x00000000 },
+ { 0x0000aa04, 0x00000000, 0x00000000, 0x00068080, 0x00068080, 0x00000000 },
+ { 0x0000aa08, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 },
+ { 0x0000aa0c, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 },
+ { 0x0000aa10, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 },
+ { 0x0000aa14, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 },
+ { 0x0000aa18, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 },
+ { 0x0000aa1c, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 },
+ { 0x0000aa20, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 },
+ { 0x0000aa24, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x0000aa28, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x0000aa2c, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 },
+ { 0x0000aa30, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 },
+ { 0x0000aa34, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 },
+ { 0x0000aa38, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 },
+ { 0x0000aa3c, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 },
+ { 0x0000aa40, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 },
+ { 0x0000aa44, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 },
+ { 0x0000aa48, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 },
+ { 0x0000aa4c, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 },
+ { 0x0000aa50, 0x00000000, 0x00000000, 0x000681ac, 0x000681ac, 0x00000000 },
+ { 0x0000aa54, 0x00000000, 0x00000000, 0x0006821c, 0x0006821c, 0x00000000 },
+ { 0x0000aa58, 0x00000000, 0x00000000, 0x00068224, 0x00068224, 0x00000000 },
+ { 0x0000aa5c, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 },
+ { 0x0000aa60, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 },
+ { 0x0000aa64, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 },
+ { 0x0000aa68, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 },
+ { 0x0000aa6c, 0x00000000, 0x00000000, 0x00068310, 0x00068310, 0x00000000 },
+ { 0x0000aa70, 0x00000000, 0x00000000, 0x00068788, 0x00068788, 0x00000000 },
+ { 0x0000aa74, 0x00000000, 0x00000000, 0x0006878c, 0x0006878c, 0x00000000 },
+ { 0x0000aa78, 0x00000000, 0x00000000, 0x00068790, 0x00068790, 0x00000000 },
+ { 0x0000aa7c, 0x00000000, 0x00000000, 0x00068794, 0x00068794, 0x00000000 },
+ { 0x0000aa80, 0x00000000, 0x00000000, 0x00068798, 0x00068798, 0x00000000 },
+ { 0x0000aa84, 0x00000000, 0x00000000, 0x0006879c, 0x0006879c, 0x00000000 },
+ { 0x0000aa88, 0x00000000, 0x00000000, 0x00068b89, 0x00068b89, 0x00000000 },
+ { 0x0000aa8c, 0x00000000, 0x00000000, 0x00068b8d, 0x00068b8d, 0x00000000 },
+ { 0x0000aa90, 0x00000000, 0x00000000, 0x00068b91, 0x00068b91, 0x00000000 },
+ { 0x0000aa94, 0x00000000, 0x00000000, 0x00068b95, 0x00068b95, 0x00000000 },
+ { 0x0000aa98, 0x00000000, 0x00000000, 0x00068b99, 0x00068b99, 0x00000000 },
+ { 0x0000aa9c, 0x00000000, 0x00000000, 0x00068ba5, 0x00068ba5, 0x00000000 },
+ { 0x0000aaa0, 0x00000000, 0x00000000, 0x00068ba9, 0x00068ba9, 0x00000000 },
+ { 0x0000aaa4, 0x00000000, 0x00000000, 0x00068bad, 0x00068bad, 0x00000000 },
+ { 0x0000aaa8, 0x00000000, 0x00000000, 0x000b8b0c, 0x000b8b0c, 0x00000000 },
+ { 0x0000aaac, 0x00000000, 0x00000000, 0x000b8f10, 0x000b8f10, 0x00000000 },
+ { 0x0000aab0, 0x00000000, 0x00000000, 0x000b8f14, 0x000b8f14, 0x00000000 },
+ { 0x0000aab4, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x0000aab8, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x0000aabc, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 },
+ { 0x0000aac0, 0x00000000, 0x00000000, 0x000bb380, 0x000bb380, 0x00000000 },
+ { 0x0000aac4, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 },
+ { 0x0000aac8, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 },
+ { 0x0000aacc, 0x00000000, 0x00000000, 0x000bb38c, 0x000bb38c, 0x00000000 },
+ { 0x0000aad0, 0x00000000, 0x00000000, 0x000bb394, 0x000bb394, 0x00000000 },
+ { 0x0000aad4, 0x00000000, 0x00000000, 0x000bb798, 0x000bb798, 0x00000000 },
+ { 0x0000aad8, 0x00000000, 0x00000000, 0x000f970c, 0x000f970c, 0x00000000 },
+ { 0x0000aadc, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 },
+ { 0x0000aae0, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 },
+ { 0x0000aae4, 0x00000000, 0x00000000, 0x000f9718, 0x000f9718, 0x00000000 },
+ { 0x0000aae8, 0x00000000, 0x00000000, 0x000f9705, 0x000f9705, 0x00000000 },
+ { 0x0000aaec, 0x00000000, 0x00000000, 0x000f9709, 0x000f9709, 0x00000000 },
+ { 0x0000aaf0, 0x00000000, 0x00000000, 0x000f970d, 0x000f970d, 0x00000000 },
+ { 0x0000aaf4, 0x00000000, 0x00000000, 0x000f9711, 0x000f9711, 0x00000000 },
+ { 0x0000aaf8, 0x00000000, 0x00000000, 0x000f9715, 0x000f9715, 0x00000000 },
+ { 0x0000aafc, 0x00000000, 0x00000000, 0x000f9719, 0x000f9719, 0x00000000 },
+ { 0x0000ab00, 0x00000000, 0x00000000, 0x000fb7a4, 0x000fb7a4, 0x00000000 },
+ { 0x0000ab04, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 },
+ { 0x0000ab08, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 },
+ { 0x0000ab0c, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 },
+ { 0x0000ab10, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 },
+ { 0x0000ab14, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 },
+ { 0x0000ab18, 0x00000000, 0x00000000, 0x000fb7bc, 0x000fb7bc, 0x00000000 },
+ { 0x0000ab1c, 0x00000000, 0x00000000, 0x000fb7a1, 0x000fb7a1, 0x00000000 },
+ { 0x0000ab20, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 },
+ { 0x0000ab24, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 },
+ { 0x0000ab28, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 },
+ { 0x0000ab2c, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 },
+ { 0x0000ab30, 0x00000000, 0x00000000, 0x000fb7bd, 0x000fb7bd, 0x00000000 },
+ { 0x0000ab34, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 },
+ { 0x0000ab38, 0x00000000, 0x00000000, 0x000fb7cd, 0x000fb7cd, 0x00000000 },
+ { 0x0000ab3c, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 },
+ { 0x0000ab40, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 },
+ { 0x0000ab44, 0x00000000, 0x00000000, 0x000fb7c2, 0x000fb7c2, 0x00000000 },
+ { 0x0000ab48, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 },
+ { 0x0000ab4c, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 },
+ { 0x0000ab50, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 },
+ { 0x0000ab54, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 },
+ { 0x0000ab58, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 },
+ { 0x0000ab5c, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 },
+ { 0x0000ab60, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 },
+ { 0x0000ab64, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab68, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab6c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab70, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab74, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab78, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab7c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab80, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab84, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab88, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab8c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab90, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab94, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab98, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab9c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abac, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abbc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abcc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abdc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abec, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abfc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 },
+ { 0x0000a20c, 0x00000014, 0x00000014, 0x00000000, 0x00000000, 0x0001f000 },
+ { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
+ { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
+ { 0x0000a250, 0x001ff000, 0x001ff000, 0x001ca000, 0x001ca000, 0x001da000 },
+ { 0x0000a274, 0x0a81c652, 0x0a81c652, 0x0a820652, 0x0a820652, 0x0a82a652 },
+ { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x0000a304, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 },
+ { 0x0000a308, 0x00000000, 0x00000000, 0x00010408, 0x00010408, 0x00000000 },
+ { 0x0000a30c, 0x00000000, 0x00000000, 0x0001860a, 0x0001860a, 0x00000000 },
+ { 0x0000a310, 0x00000000, 0x00000000, 0x00020818, 0x00020818, 0x00000000 },
+ { 0x0000a314, 0x00000000, 0x00000000, 0x00024858, 0x00024858, 0x00000000 },
+ { 0x0000a318, 0x00000000, 0x00000000, 0x00026859, 0x00026859, 0x00000000 },
+ { 0x0000a31c, 0x00000000, 0x00000000, 0x0002985b, 0x0002985b, 0x00000000 },
+ { 0x0000a320, 0x00000000, 0x00000000, 0x0002c89a, 0x0002c89a, 0x00000000 },
+ { 0x0000a324, 0x00000000, 0x00000000, 0x0002e89b, 0x0002e89b, 0x00000000 },
+ { 0x0000a328, 0x00000000, 0x00000000, 0x0003089c, 0x0003089c, 0x00000000 },
+ { 0x0000a32c, 0x00000000, 0x00000000, 0x0003289d, 0x0003289d, 0x00000000 },
+ { 0x0000a330, 0x00000000, 0x00000000, 0x0003489e, 0x0003489e, 0x00000000 },
+ { 0x0000a334, 0x00000000, 0x00000000, 0x000388de, 0x000388de, 0x00000000 },
+ { 0x0000a338, 0x00000000, 0x00000000, 0x0003b91e, 0x0003b91e, 0x00000000 },
+ { 0x0000a33c, 0x00000000, 0x00000000, 0x0003d95e, 0x0003d95e, 0x00000000 },
+ { 0x0000a340, 0x00000000, 0x00000000, 0x000419df, 0x000419df, 0x00000000 },
+ { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
{ 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
- { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
- { 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 },
};
-static const u32 ar9280Common_9280_2[][2] = {
+static const u_int32_t ar9285Common_9285[][2] = {
{ 0x0000000c, 0x00000000 },
- { 0x00000030, 0x00020015 },
+ { 0x00000030, 0x00020045 },
{ 0x00000034, 0x00000005 },
{ 0x00000040, 0x00000000 },
{ 0x00000044, 0x00000008 },
@@ -2837,7 +3848,7 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00004024, 0x0000001f },
{ 0x00004060, 0x00000000 },
{ 0x00004064, 0x00000000 },
- { 0x00007010, 0x00000033 },
+ { 0x00007010, 0x00000031 },
{ 0x00007034, 0x00000002 },
{ 0x00007038, 0x000004c2 },
{ 0x00008004, 0x00000000 },
@@ -2847,7 +3858,7 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00008020, 0x00000000 },
{ 0x00008038, 0x00000000 },
{ 0x0000803c, 0x00000000 },
- { 0x00008048, 0x40000000 },
+ { 0x00008048, 0x00000000 },
{ 0x00008054, 0x00000000 },
{ 0x00008058, 0x00000000 },
{ 0x0000805c, 0x000fc78f },
@@ -2923,6 +3934,7 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00008258, 0x00000000 },
{ 0x0000825c, 0x400000ff },
{ 0x00008260, 0x00080922 },
+ { 0x00008264, 0xa8a00010 },
{ 0x00008270, 0x00000000 },
{ 0x00008274, 0x40000000 },
{ 0x00008278, 0x003e4180 },
@@ -2936,15 +3948,15 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00008300, 0x00000040 },
{ 0x00008314, 0x00000000 },
{ 0x00008328, 0x00000000 },
- { 0x0000832c, 0x00000007 },
+ { 0x0000832c, 0x00000001 },
{ 0x00008330, 0x00000302 },
{ 0x00008334, 0x00000e00 },
{ 0x00008338, 0x00000000 },
{ 0x0000833c, 0x00000000 },
- { 0x00008340, 0x000107ff },
+ { 0x00008340, 0x00010380 },
{ 0x00008344, 0x00581043 },
{ 0x00009808, 0x00000000 },
- { 0x0000980c, 0xafa68e30 },
+ { 0x0000980c, 0xafe68e30 },
{ 0x00009810, 0xfd14e000 },
{ 0x00009814, 0x9c0a9f6b },
{ 0x0000981c, 0x00000000 },
@@ -2952,7 +3964,6 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00009830, 0x00000000 },
{ 0x0000983c, 0x00200400 },
{ 0x0000984c, 0x0040233c },
- { 0x0000a84c, 0x0040233c },
{ 0x00009854, 0x00000044 },
{ 0x00009900, 0x00000000 },
{ 0x00009904, 0x00000000 },
@@ -2961,20 +3972,18 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00009910, 0x01002310 },
{ 0x0000991c, 0x10000fff },
{ 0x00009920, 0x04900000 },
- { 0x0000a920, 0x04900000 },
{ 0x00009928, 0x00000001 },
{ 0x0000992c, 0x00000004 },
{ 0x00009934, 0x1e1f2022 },
{ 0x00009938, 0x0a0b0c0d },
{ 0x0000993c, 0x00000000 },
+ { 0x00009940, 0x14750604 },
{ 0x00009948, 0x9280c00a },
{ 0x0000994c, 0x00020028 },
{ 0x00009954, 0x5f3ca3de },
{ 0x00009958, 0x2108ecff },
- { 0x00009940, 0x14750604 },
- { 0x0000c95c, 0x004b6a8e },
- { 0x0000c968, 0x000003ce },
- { 0x00009970, 0x190fb515 },
+ { 0x00009968, 0x000003ce },
+ { 0x00009970, 0x1927b515 },
{ 0x00009974, 0x00000000 },
{ 0x00009978, 0x00000001 },
{ 0x0000997c, 0x00000000 },
@@ -2989,7 +3998,7 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x000099a0, 0x00000000 },
{ 0x000099a4, 0x00000001 },
{ 0x000099a8, 0x201fff00 },
- { 0x000099ac, 0x006f0000 },
+ { 0x000099ac, 0x2def0a00 },
{ 0x000099b0, 0x03051000 },
{ 0x000099b4, 0x00000820 },
{ 0x000099dc, 0x00000000 },
@@ -2998,31 +4007,28 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x000099e8, 0x3c466478 },
{ 0x000099ec, 0x0cc80caa },
{ 0x000099f0, 0x00000000 },
- { 0x000099fc, 0x00001042 },
+ { 0x0000a208, 0x803e6788 },
{ 0x0000a210, 0x4080a333 },
- { 0x0000a214, 0x40206c10 },
+ { 0x0000a214, 0x00206c10 },
{ 0x0000a218, 0x009c4060 },
{ 0x0000a220, 0x01834061 },
{ 0x0000a224, 0x00000400 },
{ 0x0000a228, 0x000003b5 },
- { 0x0000a22c, 0x233f71c0 },
+ { 0x0000a22c, 0x00000000 },
{ 0x0000a234, 0x20202020 },
{ 0x0000a238, 0x20202020 },
- { 0x0000a23c, 0x13c88000 },
- { 0x0000a240, 0x38490a20 },
- { 0x0000a244, 0x00007bb6 },
- { 0x0000a248, 0x0fff3ffc },
+ { 0x0000a244, 0x00000000 },
+ { 0x0000a248, 0xfffffffc },
{ 0x0000a24c, 0x00000000 },
{ 0x0000a254, 0x00000000 },
- { 0x0000a258, 0x0cdbd380 },
- { 0x0000a25c, 0x0f0f0f01 },
- { 0x0000a260, 0xdfa91f01 },
+ { 0x0000a258, 0x0ccb5380 },
+ { 0x0000a25c, 0x15151501 },
+ { 0x0000a260, 0xdfa90f01 },
{ 0x0000a268, 0x00000000 },
- { 0x0000a26c, 0x0ebae9c6 },
- { 0x0000b26c, 0x0ebae9c6 },
- { 0x0000d270, 0x00820820 },
- { 0x0000a278, 0x1ce739ce },
- { 0x0000a27c, 0x050701ce },
+ { 0x0000a26c, 0x0ebae9e6 },
+ { 0x0000d270, 0x0d820820 },
+ { 0x0000a278, 0x39ce739c },
+ { 0x0000a27c, 0x050e039c },
{ 0x0000d35c, 0x07ffffef },
{ 0x0000d360, 0x0fffffe7 },
{ 0x0000d364, 0x17ffffe5 },
@@ -3037,8 +4043,8 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x0000a388, 0x0c000000 },
{ 0x0000a38c, 0x20202020 },
{ 0x0000a390, 0x20202020 },
- { 0x0000a394, 0x1ce739ce },
- { 0x0000a398, 0x000001ce },
+ { 0x0000a394, 0x39ce739c },
+ { 0x0000a398, 0x0000039c },
{ 0x0000a39c, 0x00000001 },
{ 0x0000a3a0, 0x00000000 },
{ 0x0000a3a4, 0x00000000 },
@@ -3050,96 +4056,746 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x0000a3bc, 0x00000000 },
{ 0x0000a3c0, 0x00000000 },
{ 0x0000a3c4, 0x00000000 },
- { 0x0000a3c8, 0x00000246 },
{ 0x0000a3cc, 0x20202020 },
{ 0x0000a3d0, 0x20202020 },
{ 0x0000a3d4, 0x20202020 },
- { 0x0000a3dc, 0x1ce739ce },
- { 0x0000a3e0, 0x000001ce },
+ { 0x0000a3dc, 0x39ce739c },
+ { 0x0000a3e0, 0x0000039c },
{ 0x0000a3e4, 0x00000000 },
{ 0x0000a3e8, 0x18c43433 },
{ 0x0000a3ec, 0x00f70081 },
- { 0x00007800, 0x00040000 },
- { 0x00007804, 0xdb005012 },
- { 0x00007808, 0x04924914 },
- { 0x0000780c, 0x21084210 },
- { 0x00007810, 0x6d801300 },
- { 0x00007814, 0x0019beff },
- { 0x00007818, 0x07e41000 },
- { 0x0000781c, 0x00392000 },
- { 0x00007820, 0x92592480 },
- { 0x00007824, 0x00040000 },
- { 0x00007828, 0xdb005012 },
- { 0x0000782c, 0x04924914 },
- { 0x00007830, 0x21084210 },
- { 0x00007834, 0x6d801300 },
- { 0x00007838, 0x0019beff },
- { 0x0000783c, 0x07e40000 },
- { 0x00007840, 0x00392000 },
- { 0x00007844, 0x92592480 },
- { 0x00007848, 0x00100000 },
- { 0x0000784c, 0x773f0567 },
- { 0x00007850, 0x54214514 },
- { 0x00007854, 0x12035828 },
- { 0x00007858, 0x9259269a },
- { 0x00007860, 0x52802000 },
- { 0x00007864, 0x0a8e370e },
- { 0x00007868, 0xc0102850 },
- { 0x0000786c, 0x812d4000 },
- { 0x00007870, 0x807ec400 },
- { 0x00007874, 0x001b6db0 },
- { 0x00007878, 0x00376b63 },
- { 0x0000787c, 0x06db6db6 },
- { 0x00007880, 0x006d8000 },
- { 0x00007884, 0xffeffffe },
- { 0x00007888, 0xffeffffe },
- { 0x0000788c, 0x00010000 },
- { 0x00007890, 0x02060aeb },
- { 0x00007898, 0x2a850160 },
+ { 0x00007800, 0x00140000 },
+ { 0x00007804, 0x0e4548d8 },
+ { 0x00007808, 0x54214514 },
+ { 0x0000780c, 0x02025820 },
+ { 0x00007810, 0x71c0d388 },
+ { 0x00007814, 0x924934a8 },
+ { 0x0000781c, 0x00000000 },
+ { 0x00007820, 0x00000c04 },
+ { 0x00007824, 0x00d86fff },
+ { 0x00007828, 0x26d2491b },
+ { 0x0000782c, 0x6e36d97b },
+ { 0x00007830, 0xedb6d96c },
+ { 0x00007834, 0x71400086 },
+ { 0x00007838, 0xfac68800 },
+ { 0x0000783c, 0x0001fffe },
+ { 0x00007840, 0xffeb1a20 },
+ { 0x00007844, 0x000c0db6 },
+ { 0x00007848, 0x6db61b6f },
+ { 0x0000784c, 0x6d9b66db },
+ { 0x00007850, 0x6d8c6dba },
+ { 0x00007854, 0x00040000 },
+ { 0x00007858, 0xdb003012 },
+ { 0x0000785c, 0x04924914 },
+ { 0x00007860, 0x21084210 },
+ { 0x00007864, 0xf7d7ffde },
+ { 0x00007868, 0xc2034080 },
+ { 0x0000786c, 0x48609eb4 },
+ { 0x00007870, 0x10142c00 },
};
-static const u32 ar9280Modes_fast_clock_9280_2[][3] = {
- { 0x00001030, 0x00000268, 0x000004d0 },
- { 0x00001070, 0x0000018c, 0x00000318 },
- { 0x000010b0, 0x00000fd0, 0x00001fa0 },
- { 0x00008014, 0x044c044c, 0x08980898 },
- { 0x0000801c, 0x148ec02b, 0x148ec057 },
- { 0x00008318, 0x000044c0, 0x00008980 },
- { 0x00009820, 0x02020200, 0x02020200 },
- { 0x00009824, 0x00000f0f, 0x00000f0f },
- { 0x00009828, 0x0b020001, 0x0b020001 },
- { 0x00009834, 0x00000f0f, 0x00000f0f },
- { 0x00009844, 0x03721821, 0x03721821 },
- { 0x00009914, 0x00000898, 0x00000898 },
- { 0x00009918, 0x0000000b, 0x00000016 },
- { 0x00009944, 0xdfbc1210, 0xdfbc1210 },
+static const u_int32_t ar9285PciePhy_clkreq_always_on_L1_9285[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01dcffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
};
-
-
-static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = {
+static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285[][2] = {
{0x00004040, 0x9248fd00 },
{0x00004040, 0x24924924 },
{0x00004040, 0xa8000019 },
{0x00004040, 0x13160820 },
{0x00004040, 0xe5980560 },
- {0x00004040, 0x401dcffc },
- {0x00004040, 0x1aaabe40 },
+ {0x00004040, 0xc01dcffc },
+ {0x00004040, 0x1aaabe41 },
{0x00004040, 0xbe105554 },
{0x00004040, 0x00043007 },
{0x00004044, 0x00000000 },
};
+static const u_int32_t ar9285Modes_9285_1_2[][6] = {
+ { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
+ { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
+ { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 },
+ { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 },
+ { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 },
+ { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f },
+ { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 },
+ { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 },
+ { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 },
+ { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
+ { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
+ { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
+ { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 },
+ { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e },
+ { 0x00009844, 0x0372161e, 0x0372161e, 0x03720020, 0x03720020, 0x037216a0 },
+ { 0x00009848, 0x00001066, 0x00001066, 0x00000057, 0x00000057, 0x00001059 },
+ { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
+ { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
+ { 0x0000985c, 0x3139605e, 0x3139605e, 0x3136605e, 0x3136605e, 0x3139605e },
+ { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d20, 0x00058d20, 0x00058d18 },
+ { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
+ { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
+ { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 },
+ { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
+ { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
+ { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d },
+ { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020, 0xffbc1010 },
+ { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c },
+ { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 },
+ { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
+ { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
+ { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 },
+ { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 },
+ { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 },
+ { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00009a00, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 },
+ { 0x00009a04, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 },
+ { 0x00009a08, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 },
+ { 0x00009a0c, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 },
+ { 0x00009a10, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 },
+ { 0x00009a14, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 },
+ { 0x00009a18, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 },
+ { 0x00009a1c, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x00009a20, 0x00000000, 0x00000000, 0x00068114, 0x00068114, 0x00000000 },
+ { 0x00009a24, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 },
+ { 0x00009a28, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 },
+ { 0x00009a2c, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 },
+ { 0x00009a30, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 },
+ { 0x00009a34, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 },
+ { 0x00009a38, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 },
+ { 0x00009a3c, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 },
+ { 0x00009a40, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 },
+ { 0x00009a44, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 },
+ { 0x00009a48, 0x00000000, 0x00000000, 0x00068284, 0x00068284, 0x00000000 },
+ { 0x00009a4c, 0x00000000, 0x00000000, 0x00068288, 0x00068288, 0x00000000 },
+ { 0x00009a50, 0x00000000, 0x00000000, 0x00068220, 0x00068220, 0x00000000 },
+ { 0x00009a54, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 },
+ { 0x00009a58, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 },
+ { 0x00009a5c, 0x00000000, 0x00000000, 0x00068304, 0x00068304, 0x00000000 },
+ { 0x00009a60, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 },
+ { 0x00009a64, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 },
+ { 0x00009a68, 0x00000000, 0x00000000, 0x00068380, 0x00068380, 0x00000000 },
+ { 0x00009a6c, 0x00000000, 0x00000000, 0x00068384, 0x00068384, 0x00000000 },
+ { 0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 },
+ { 0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 },
+ { 0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 },
+ { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
+ { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
+ { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
+ { 0x00009a88, 0x00000000, 0x00000000, 0x00068b04, 0x00068b04, 0x00000000 },
+ { 0x00009a8c, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 },
+ { 0x00009a90, 0x00000000, 0x00000000, 0x00068b08, 0x00068b08, 0x00000000 },
+ { 0x00009a94, 0x00000000, 0x00000000, 0x00068b0c, 0x00068b0c, 0x00000000 },
+ { 0x00009a98, 0x00000000, 0x00000000, 0x00068b80, 0x00068b80, 0x00000000 },
+ { 0x00009a9c, 0x00000000, 0x00000000, 0x00068b84, 0x00068b84, 0x00000000 },
+ { 0x00009aa0, 0x00000000, 0x00000000, 0x00068b88, 0x00068b88, 0x00000000 },
+ { 0x00009aa4, 0x00000000, 0x00000000, 0x00068b8c, 0x00068b8c, 0x00000000 },
+ { 0x00009aa8, 0x00000000, 0x00000000, 0x000b8b90, 0x000b8b90, 0x00000000 },
+ { 0x00009aac, 0x00000000, 0x00000000, 0x000b8f80, 0x000b8f80, 0x00000000 },
+ { 0x00009ab0, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x00009ab4, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 },
+ { 0x00009ab8, 0x00000000, 0x00000000, 0x000b8f8c, 0x000b8f8c, 0x00000000 },
+ { 0x00009abc, 0x00000000, 0x00000000, 0x000b8f90, 0x000b8f90, 0x00000000 },
+ { 0x00009ac0, 0x00000000, 0x00000000, 0x000bb30c, 0x000bb30c, 0x00000000 },
+ { 0x00009ac4, 0x00000000, 0x00000000, 0x000bb310, 0x000bb310, 0x00000000 },
+ { 0x00009ac8, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 },
+ { 0x00009acc, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 },
+ { 0x00009ad0, 0x00000000, 0x00000000, 0x000bb324, 0x000bb324, 0x00000000 },
+ { 0x00009ad4, 0x00000000, 0x00000000, 0x000bb704, 0x000bb704, 0x00000000 },
+ { 0x00009ad8, 0x00000000, 0x00000000, 0x000f96a4, 0x000f96a4, 0x00000000 },
+ { 0x00009adc, 0x00000000, 0x00000000, 0x000f96a8, 0x000f96a8, 0x00000000 },
+ { 0x00009ae0, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 },
+ { 0x00009ae4, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 },
+ { 0x00009ae8, 0x00000000, 0x00000000, 0x000f9720, 0x000f9720, 0x00000000 },
+ { 0x00009aec, 0x00000000, 0x00000000, 0x000f9724, 0x000f9724, 0x00000000 },
+ { 0x00009af0, 0x00000000, 0x00000000, 0x000f9728, 0x000f9728, 0x00000000 },
+ { 0x00009af4, 0x00000000, 0x00000000, 0x000f972c, 0x000f972c, 0x00000000 },
+ { 0x00009af8, 0x00000000, 0x00000000, 0x000f97a0, 0x000f97a0, 0x00000000 },
+ { 0x00009afc, 0x00000000, 0x00000000, 0x000f97a4, 0x000f97a4, 0x00000000 },
+ { 0x00009b00, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 },
+ { 0x00009b04, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 },
+ { 0x00009b08, 0x00000000, 0x00000000, 0x000fb7b4, 0x000fb7b4, 0x00000000 },
+ { 0x00009b0c, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 },
+ { 0x00009b10, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 },
+ { 0x00009b14, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 },
+ { 0x00009b18, 0x00000000, 0x00000000, 0x000fb7ad, 0x000fb7ad, 0x00000000 },
+ { 0x00009b1c, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 },
+ { 0x00009b20, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 },
+ { 0x00009b24, 0x00000000, 0x00000000, 0x000fb7b9, 0x000fb7b9, 0x00000000 },
+ { 0x00009b28, 0x00000000, 0x00000000, 0x000fb7c5, 0x000fb7c5, 0x00000000 },
+ { 0x00009b2c, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 },
+ { 0x00009b30, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 },
+ { 0x00009b34, 0x00000000, 0x00000000, 0x000fb7d5, 0x000fb7d5, 0x00000000 },
+ { 0x00009b38, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 },
+ { 0x00009b3c, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 },
+ { 0x00009b40, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 },
+ { 0x00009b44, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 },
+ { 0x00009b48, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 },
+ { 0x00009b4c, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 },
+ { 0x00009b50, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 },
+ { 0x00009b54, 0x00000000, 0x00000000, 0x000fb7c7, 0x000fb7c7, 0x00000000 },
+ { 0x00009b58, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 },
+ { 0x00009b5c, 0x00000000, 0x00000000, 0x000fb7cf, 0x000fb7cf, 0x00000000 },
+ { 0x00009b60, 0x00000000, 0x00000000, 0x000fb7d7, 0x000fb7d7, 0x00000000 },
+ { 0x00009b64, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b68, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b6c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b70, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b74, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b78, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b7c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b80, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b84, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b88, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b8c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b90, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b94, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b98, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009b9c, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009ba8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bac, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bb8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bbc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bc8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bcc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bd8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bdc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009be8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bec, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf0, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf4, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bf8, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x00009bfc, 0x00000000, 0x00000000, 0x000fb7db, 0x000fb7db, 0x00000000 },
+ { 0x0000aa00, 0x00000000, 0x00000000, 0x0006801c, 0x0006801c, 0x00000000 },
+ { 0x0000aa04, 0x00000000, 0x00000000, 0x0006801c, 0x0006801c, 0x00000000 },
+ { 0x0000aa08, 0x00000000, 0x00000000, 0x0006801c, 0x0006801c, 0x00000000 },
+ { 0x0000aa0c, 0x00000000, 0x00000000, 0x00068080, 0x00068080, 0x00000000 },
+ { 0x0000aa10, 0x00000000, 0x00000000, 0x00068084, 0x00068084, 0x00000000 },
+ { 0x0000aa14, 0x00000000, 0x00000000, 0x00068088, 0x00068088, 0x00000000 },
+ { 0x0000aa18, 0x00000000, 0x00000000, 0x0006808c, 0x0006808c, 0x00000000 },
+ { 0x0000aa1c, 0x00000000, 0x00000000, 0x00068100, 0x00068100, 0x00000000 },
+ { 0x0000aa20, 0x00000000, 0x00000000, 0x00068104, 0x00068104, 0x00000000 },
+ { 0x0000aa24, 0x00000000, 0x00000000, 0x00068108, 0x00068108, 0x00000000 },
+ { 0x0000aa28, 0x00000000, 0x00000000, 0x0006810c, 0x0006810c, 0x00000000 },
+ { 0x0000aa2c, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x0000aa30, 0x00000000, 0x00000000, 0x00068110, 0x00068110, 0x00000000 },
+ { 0x0000aa34, 0x00000000, 0x00000000, 0x00068180, 0x00068180, 0x00000000 },
+ { 0x0000aa38, 0x00000000, 0x00000000, 0x00068184, 0x00068184, 0x00000000 },
+ { 0x0000aa3c, 0x00000000, 0x00000000, 0x00068188, 0x00068188, 0x00000000 },
+ { 0x0000aa40, 0x00000000, 0x00000000, 0x0006818c, 0x0006818c, 0x00000000 },
+ { 0x0000aa44, 0x00000000, 0x00000000, 0x00068190, 0x00068190, 0x00000000 },
+ { 0x0000aa48, 0x00000000, 0x00000000, 0x00068194, 0x00068194, 0x00000000 },
+ { 0x0000aa4c, 0x00000000, 0x00000000, 0x000681a0, 0x000681a0, 0x00000000 },
+ { 0x0000aa50, 0x00000000, 0x00000000, 0x0006820c, 0x0006820c, 0x00000000 },
+ { 0x0000aa54, 0x00000000, 0x00000000, 0x000681a8, 0x000681a8, 0x00000000 },
+ { 0x0000aa58, 0x00000000, 0x00000000, 0x000681ac, 0x000681ac, 0x00000000 },
+ { 0x0000aa5c, 0x00000000, 0x00000000, 0x0006821c, 0x0006821c, 0x00000000 },
+ { 0x0000aa60, 0x00000000, 0x00000000, 0x00068224, 0x00068224, 0x00000000 },
+ { 0x0000aa64, 0x00000000, 0x00000000, 0x00068290, 0x00068290, 0x00000000 },
+ { 0x0000aa68, 0x00000000, 0x00000000, 0x00068300, 0x00068300, 0x00000000 },
+ { 0x0000aa6c, 0x00000000, 0x00000000, 0x00068308, 0x00068308, 0x00000000 },
+ { 0x0000aa70, 0x00000000, 0x00000000, 0x0006830c, 0x0006830c, 0x00000000 },
+ { 0x0000aa74, 0x00000000, 0x00000000, 0x00068310, 0x00068310, 0x00000000 },
+ { 0x0000aa78, 0x00000000, 0x00000000, 0x00068788, 0x00068788, 0x00000000 },
+ { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006878c, 0x0006878c, 0x00000000 },
+ { 0x0000aa80, 0x00000000, 0x00000000, 0x00068790, 0x00068790, 0x00000000 },
+ { 0x0000aa84, 0x00000000, 0x00000000, 0x00068794, 0x00068794, 0x00000000 },
+ { 0x0000aa88, 0x00000000, 0x00000000, 0x00068798, 0x00068798, 0x00000000 },
+ { 0x0000aa8c, 0x00000000, 0x00000000, 0x0006879c, 0x0006879c, 0x00000000 },
+ { 0x0000aa90, 0x00000000, 0x00000000, 0x00068b89, 0x00068b89, 0x00000000 },
+ { 0x0000aa94, 0x00000000, 0x00000000, 0x00068b8d, 0x00068b8d, 0x00000000 },
+ { 0x0000aa98, 0x00000000, 0x00000000, 0x00068b91, 0x00068b91, 0x00000000 },
+ { 0x0000aa9c, 0x00000000, 0x00000000, 0x00068b95, 0x00068b95, 0x00000000 },
+ { 0x0000aaa0, 0x00000000, 0x00000000, 0x00068b99, 0x00068b99, 0x00000000 },
+ { 0x0000aaa4, 0x00000000, 0x00000000, 0x00068ba5, 0x00068ba5, 0x00000000 },
+ { 0x0000aaa8, 0x00000000, 0x00000000, 0x00068ba9, 0x00068ba9, 0x00000000 },
+ { 0x0000aaac, 0x00000000, 0x00000000, 0x00068bad, 0x00068bad, 0x00000000 },
+ { 0x0000aab0, 0x00000000, 0x00000000, 0x000b8b0c, 0x000b8b0c, 0x00000000 },
+ { 0x0000aab4, 0x00000000, 0x00000000, 0x000b8f10, 0x000b8f10, 0x00000000 },
+ { 0x0000aab8, 0x00000000, 0x00000000, 0x000b8f14, 0x000b8f14, 0x00000000 },
+ { 0x0000aabc, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x0000aac0, 0x00000000, 0x00000000, 0x000b8f84, 0x000b8f84, 0x00000000 },
+ { 0x0000aac4, 0x00000000, 0x00000000, 0x000b8f88, 0x000b8f88, 0x00000000 },
+ { 0x0000aac8, 0x00000000, 0x00000000, 0x000bb380, 0x000bb380, 0x00000000 },
+ { 0x0000aacc, 0x00000000, 0x00000000, 0x000bb384, 0x000bb384, 0x00000000 },
+ { 0x0000aad0, 0x00000000, 0x00000000, 0x000bb388, 0x000bb388, 0x00000000 },
+ { 0x0000aad4, 0x00000000, 0x00000000, 0x000bb38c, 0x000bb38c, 0x00000000 },
+ { 0x0000aad8, 0x00000000, 0x00000000, 0x000bb394, 0x000bb394, 0x00000000 },
+ { 0x0000aadc, 0x00000000, 0x00000000, 0x000bb798, 0x000bb798, 0x00000000 },
+ { 0x0000aae0, 0x00000000, 0x00000000, 0x000f970c, 0x000f970c, 0x00000000 },
+ { 0x0000aae4, 0x00000000, 0x00000000, 0x000f9710, 0x000f9710, 0x00000000 },
+ { 0x0000aae8, 0x00000000, 0x00000000, 0x000f9714, 0x000f9714, 0x00000000 },
+ { 0x0000aaec, 0x00000000, 0x00000000, 0x000f9718, 0x000f9718, 0x00000000 },
+ { 0x0000aaf0, 0x00000000, 0x00000000, 0x000f9705, 0x000f9705, 0x00000000 },
+ { 0x0000aaf4, 0x00000000, 0x00000000, 0x000f9709, 0x000f9709, 0x00000000 },
+ { 0x0000aaf8, 0x00000000, 0x00000000, 0x000f970d, 0x000f970d, 0x00000000 },
+ { 0x0000aafc, 0x00000000, 0x00000000, 0x000f9711, 0x000f9711, 0x00000000 },
+ { 0x0000ab00, 0x00000000, 0x00000000, 0x000f9715, 0x000f9715, 0x00000000 },
+ { 0x0000ab04, 0x00000000, 0x00000000, 0x000f9719, 0x000f9719, 0x00000000 },
+ { 0x0000ab08, 0x00000000, 0x00000000, 0x000fb7a4, 0x000fb7a4, 0x00000000 },
+ { 0x0000ab0c, 0x00000000, 0x00000000, 0x000fb7a8, 0x000fb7a8, 0x00000000 },
+ { 0x0000ab10, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 },
+ { 0x0000ab14, 0x00000000, 0x00000000, 0x000fb7ac, 0x000fb7ac, 0x00000000 },
+ { 0x0000ab18, 0x00000000, 0x00000000, 0x000fb7b0, 0x000fb7b0, 0x00000000 },
+ { 0x0000ab1c, 0x00000000, 0x00000000, 0x000fb7b8, 0x000fb7b8, 0x00000000 },
+ { 0x0000ab20, 0x00000000, 0x00000000, 0x000fb7bc, 0x000fb7bc, 0x00000000 },
+ { 0x0000ab24, 0x00000000, 0x00000000, 0x000fb7a1, 0x000fb7a1, 0x00000000 },
+ { 0x0000ab28, 0x00000000, 0x00000000, 0x000fb7a5, 0x000fb7a5, 0x00000000 },
+ { 0x0000ab2c, 0x00000000, 0x00000000, 0x000fb7a9, 0x000fb7a9, 0x00000000 },
+ { 0x0000ab30, 0x00000000, 0x00000000, 0x000fb7b1, 0x000fb7b1, 0x00000000 },
+ { 0x0000ab34, 0x00000000, 0x00000000, 0x000fb7b5, 0x000fb7b5, 0x00000000 },
+ { 0x0000ab38, 0x00000000, 0x00000000, 0x000fb7bd, 0x000fb7bd, 0x00000000 },
+ { 0x0000ab3c, 0x00000000, 0x00000000, 0x000fb7c9, 0x000fb7c9, 0x00000000 },
+ { 0x0000ab40, 0x00000000, 0x00000000, 0x000fb7cd, 0x000fb7cd, 0x00000000 },
+ { 0x0000ab44, 0x00000000, 0x00000000, 0x000fb7d1, 0x000fb7d1, 0x00000000 },
+ { 0x0000ab48, 0x00000000, 0x00000000, 0x000fb7d9, 0x000fb7d9, 0x00000000 },
+ { 0x0000ab4c, 0x00000000, 0x00000000, 0x000fb7c2, 0x000fb7c2, 0x00000000 },
+ { 0x0000ab50, 0x00000000, 0x00000000, 0x000fb7c6, 0x000fb7c6, 0x00000000 },
+ { 0x0000ab54, 0x00000000, 0x00000000, 0x000fb7ca, 0x000fb7ca, 0x00000000 },
+ { 0x0000ab58, 0x00000000, 0x00000000, 0x000fb7ce, 0x000fb7ce, 0x00000000 },
+ { 0x0000ab5c, 0x00000000, 0x00000000, 0x000fb7d2, 0x000fb7d2, 0x00000000 },
+ { 0x0000ab60, 0x00000000, 0x00000000, 0x000fb7d6, 0x000fb7d6, 0x00000000 },
+ { 0x0000ab64, 0x00000000, 0x00000000, 0x000fb7c3, 0x000fb7c3, 0x00000000 },
+ { 0x0000ab68, 0x00000000, 0x00000000, 0x000fb7cb, 0x000fb7cb, 0x00000000 },
+ { 0x0000ab6c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab70, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab74, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab78, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab7c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab80, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab84, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab88, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab8c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab90, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab94, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab98, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000ab9c, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000aba8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abac, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abb8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abbc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abc8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abcc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abd8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abdc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abe8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abec, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf0, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf4, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abf8, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000abfc, 0x00000000, 0x00000000, 0x000fb7d3, 0x000fb7d3, 0x00000000 },
+ { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 },
+ { 0x0000a20c, 0x00000014, 0x00000014, 0x00000000, 0x00000000, 0x0001f000 },
+ { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
+ { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
+ { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 },
+ { 0x0000a274, 0x0a81c652, 0x0a81c652, 0x0a820652, 0x0a820652, 0x0a82a652 },
+ { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x0000a304, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 },
+ { 0x0000a308, 0x00000000, 0x00000000, 0x00010408, 0x00010408, 0x00000000 },
+ { 0x0000a30c, 0x00000000, 0x00000000, 0x0001860a, 0x0001860a, 0x00000000 },
+ { 0x0000a310, 0x00000000, 0x00000000, 0x00020818, 0x00020818, 0x00000000 },
+ { 0x0000a314, 0x00000000, 0x00000000, 0x00024858, 0x00024858, 0x00000000 },
+ { 0x0000a318, 0x00000000, 0x00000000, 0x00026859, 0x00026859, 0x00000000 },
+ { 0x0000a31c, 0x00000000, 0x00000000, 0x0002985b, 0x0002985b, 0x00000000 },
+ { 0x0000a320, 0x00000000, 0x00000000, 0x0002b89a, 0x0002b89a, 0x00000000 },
+ { 0x0000a324, 0x00000000, 0x00000000, 0x0002d89b, 0x0002d89b, 0x00000000 },
+ { 0x0000a328, 0x00000000, 0x00000000, 0x0002f89c, 0x0002f89c, 0x00000000 },
+ { 0x0000a32c, 0x00000000, 0x00000000, 0x0003189d, 0x0003189d, 0x00000000 },
+ { 0x0000a330, 0x00000000, 0x00000000, 0x0003389e, 0x0003389e, 0x00000000 },
+ { 0x0000a334, 0x00000000, 0x00000000, 0x000368de, 0x000368de, 0x00000000 },
+ { 0x0000a338, 0x00000000, 0x00000000, 0x0003891e, 0x0003891e, 0x00000000 },
+ { 0x0000a33c, 0x00000000, 0x00000000, 0x0003a95e, 0x0003a95e, 0x00000000 },
+ { 0x0000a340, 0x00000000, 0x00000000, 0x0003e9df, 0x0003e9df, 0x00000000 },
+ { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
+ { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
+};
+static const u_int32_t ar9285Common_9285_1_2[][2] = {
+ { 0x0000000c, 0x00000000 },
+ { 0x00000030, 0x00020045 },
+ { 0x00000034, 0x00000005 },
+ { 0x00000040, 0x00000000 },
+ { 0x00000044, 0x00000008 },
+ { 0x00000048, 0x00000008 },
+ { 0x0000004c, 0x00000010 },
+ { 0x00000050, 0x00000000 },
+ { 0x00000054, 0x0000001f },
+ { 0x00000800, 0x00000000 },
+ { 0x00000804, 0x00000000 },
+ { 0x00000808, 0x00000000 },
+ { 0x0000080c, 0x00000000 },
+ { 0x00000810, 0x00000000 },
+ { 0x00000814, 0x00000000 },
+ { 0x00000818, 0x00000000 },
+ { 0x0000081c, 0x00000000 },
+ { 0x00000820, 0x00000000 },
+ { 0x00000824, 0x00000000 },
+ { 0x00001040, 0x002ffc0f },
+ { 0x00001044, 0x002ffc0f },
+ { 0x00001048, 0x002ffc0f },
+ { 0x0000104c, 0x002ffc0f },
+ { 0x00001050, 0x002ffc0f },
+ { 0x00001054, 0x002ffc0f },
+ { 0x00001058, 0x002ffc0f },
+ { 0x0000105c, 0x002ffc0f },
+ { 0x00001060, 0x002ffc0f },
+ { 0x00001064, 0x002ffc0f },
+ { 0x00001230, 0x00000000 },
+ { 0x00001270, 0x00000000 },
+ { 0x00001038, 0x00000000 },
+ { 0x00001078, 0x00000000 },
+ { 0x000010b8, 0x00000000 },
+ { 0x000010f8, 0x00000000 },
+ { 0x00001138, 0x00000000 },
+ { 0x00001178, 0x00000000 },
+ { 0x000011b8, 0x00000000 },
+ { 0x000011f8, 0x00000000 },
+ { 0x00001238, 0x00000000 },
+ { 0x00001278, 0x00000000 },
+ { 0x000012b8, 0x00000000 },
+ { 0x000012f8, 0x00000000 },
+ { 0x00001338, 0x00000000 },
+ { 0x00001378, 0x00000000 },
+ { 0x000013b8, 0x00000000 },
+ { 0x000013f8, 0x00000000 },
+ { 0x00001438, 0x00000000 },
+ { 0x00001478, 0x00000000 },
+ { 0x000014b8, 0x00000000 },
+ { 0x000014f8, 0x00000000 },
+ { 0x00001538, 0x00000000 },
+ { 0x00001578, 0x00000000 },
+ { 0x000015b8, 0x00000000 },
+ { 0x000015f8, 0x00000000 },
+ { 0x00001638, 0x00000000 },
+ { 0x00001678, 0x00000000 },
+ { 0x000016b8, 0x00000000 },
+ { 0x000016f8, 0x00000000 },
+ { 0x00001738, 0x00000000 },
+ { 0x00001778, 0x00000000 },
+ { 0x000017b8, 0x00000000 },
+ { 0x000017f8, 0x00000000 },
+ { 0x0000103c, 0x00000000 },
+ { 0x0000107c, 0x00000000 },
+ { 0x000010bc, 0x00000000 },
+ { 0x000010fc, 0x00000000 },
+ { 0x0000113c, 0x00000000 },
+ { 0x0000117c, 0x00000000 },
+ { 0x000011bc, 0x00000000 },
+ { 0x000011fc, 0x00000000 },
+ { 0x0000123c, 0x00000000 },
+ { 0x0000127c, 0x00000000 },
+ { 0x000012bc, 0x00000000 },
+ { 0x000012fc, 0x00000000 },
+ { 0x0000133c, 0x00000000 },
+ { 0x0000137c, 0x00000000 },
+ { 0x000013bc, 0x00000000 },
+ { 0x000013fc, 0x00000000 },
+ { 0x0000143c, 0x00000000 },
+ { 0x0000147c, 0x00000000 },
+ { 0x00004030, 0x00000002 },
+ { 0x0000403c, 0x00000002 },
+ { 0x00004024, 0x0000001f },
+ { 0x00004060, 0x00000000 },
+ { 0x00004064, 0x00000000 },
+ { 0x00007010, 0x00000031 },
+ { 0x00007034, 0x00000002 },
+ { 0x00007038, 0x000004c2 },
+ { 0x00008004, 0x00000000 },
+ { 0x00008008, 0x00000000 },
+ { 0x0000800c, 0x00000000 },
+ { 0x00008018, 0x00000700 },
+ { 0x00008020, 0x00000000 },
+ { 0x00008038, 0x00000000 },
+ { 0x0000803c, 0x00000000 },
+ { 0x00008048, 0x00000000 },
+ { 0x00008054, 0x00000000 },
+ { 0x00008058, 0x00000000 },
+ { 0x0000805c, 0x000fc78f },
+ { 0x00008060, 0x0000000f },
+ { 0x00008064, 0x00000000 },
+ { 0x00008070, 0x00000000 },
+ { 0x000080c0, 0x2a80001a },
+ { 0x000080c4, 0x05dc01e0 },
+ { 0x000080c8, 0x1f402710 },
+ { 0x000080cc, 0x01f40000 },
+ { 0x000080d0, 0x00001e00 },
+ { 0x000080d4, 0x00000000 },
+ { 0x000080d8, 0x00400000 },
+ { 0x000080e0, 0xffffffff },
+ { 0x000080e4, 0x0000ffff },
+ { 0x000080e8, 0x003f3f3f },
+ { 0x000080ec, 0x00000000 },
+ { 0x000080f0, 0x00000000 },
+ { 0x000080f4, 0x00000000 },
+ { 0x000080f8, 0x00000000 },
+ { 0x000080fc, 0x00020000 },
+ { 0x00008100, 0x00020000 },
+ { 0x00008104, 0x00000001 },
+ { 0x00008108, 0x00000052 },
+ { 0x0000810c, 0x00000000 },
+ { 0x00008110, 0x00000168 },
+ { 0x00008118, 0x000100aa },
+ { 0x0000811c, 0x00003210 },
+ { 0x00008120, 0x08f04800 },
+ { 0x00008124, 0x00000000 },
+ { 0x00008128, 0x00000000 },
+ { 0x0000812c, 0x00000000 },
+ { 0x00008130, 0x00000000 },
+ { 0x00008134, 0x00000000 },
+ { 0x00008138, 0x00000000 },
+ { 0x0000813c, 0x00000000 },
+ { 0x00008144, 0xffffffff },
+ { 0x00008168, 0x00000000 },
+ { 0x0000816c, 0x00000000 },
+ { 0x00008170, 0x32143320 },
+ { 0x00008174, 0xfaa4fa50 },
+ { 0x00008178, 0x00000100 },
+ { 0x0000817c, 0x00000000 },
+ { 0x000081c0, 0x00000000 },
+ { 0x000081d0, 0x00003210 },
+ { 0x000081ec, 0x00000000 },
+ { 0x000081f0, 0x00000000 },
+ { 0x000081f4, 0x00000000 },
+ { 0x000081f8, 0x00000000 },
+ { 0x000081fc, 0x00000000 },
+ { 0x00008200, 0x00000000 },
+ { 0x00008204, 0x00000000 },
+ { 0x00008208, 0x00000000 },
+ { 0x0000820c, 0x00000000 },
+ { 0x00008210, 0x00000000 },
+ { 0x00008214, 0x00000000 },
+ { 0x00008218, 0x00000000 },
+ { 0x0000821c, 0x00000000 },
+ { 0x00008220, 0x00000000 },
+ { 0x00008224, 0x00000000 },
+ { 0x00008228, 0x00000000 },
+ { 0x0000822c, 0x00000000 },
+ { 0x00008230, 0x00000000 },
+ { 0x00008234, 0x00000000 },
+ { 0x00008238, 0x00000000 },
+ { 0x0000823c, 0x00000000 },
+ { 0x00008240, 0x00100000 },
+ { 0x00008244, 0x0010f400 },
+ { 0x00008248, 0x00000100 },
+ { 0x0000824c, 0x0001e800 },
+ { 0x00008250, 0x00000000 },
+ { 0x00008254, 0x00000000 },
+ { 0x00008258, 0x00000000 },
+ { 0x0000825c, 0x400000ff },
+ { 0x00008260, 0x00080922 },
+ { 0x00008264, 0xa8a00010 },
+ { 0x00008270, 0x00000000 },
+ { 0x00008274, 0x40000000 },
+ { 0x00008278, 0x003e4180 },
+ { 0x0000827c, 0x00000000 },
+ { 0x00008284, 0x0000002c },
+ { 0x00008288, 0x0000002c },
+ { 0x0000828c, 0x00000000 },
+ { 0x00008294, 0x00000000 },
+ { 0x00008298, 0x00000000 },
+ { 0x0000829c, 0x00000000 },
+ { 0x00008300, 0x00000040 },
+ { 0x00008314, 0x00000000 },
+ { 0x00008328, 0x00000000 },
+ { 0x0000832c, 0x00000001 },
+ { 0x00008330, 0x00000302 },
+ { 0x00008334, 0x00000e00 },
+ { 0x00008338, 0x00ff0000 },
+ { 0x0000833c, 0x00000000 },
+ { 0x00008340, 0x00010380 },
+ { 0x00008344, 0x00581043 },
+ { 0x00009808, 0x00000000 },
+ { 0x0000980c, 0xafe68e30 },
+ { 0x00009810, 0xfd14e000 },
+ { 0x00009814, 0x9c0a9f6b },
+ { 0x0000981c, 0x00000000 },
+ { 0x0000982c, 0x0000a000 },
+ { 0x00009830, 0x00000000 },
+ { 0x0000983c, 0x00200400 },
+ { 0x0000984c, 0x0040233c },
+ { 0x00009854, 0x00000044 },
+ { 0x00009900, 0x00000000 },
+ { 0x00009904, 0x00000000 },
+ { 0x00009908, 0x00000000 },
+ { 0x0000990c, 0x00000000 },
+ { 0x00009910, 0x01002310 },
+ { 0x0000991c, 0x10000fff },
+ { 0x00009920, 0x04900000 },
+ { 0x00009928, 0x00000001 },
+ { 0x0000992c, 0x00000004 },
+ { 0x00009934, 0x1e1f2022 },
+ { 0x00009938, 0x0a0b0c0d },
+ { 0x0000993c, 0x00000000 },
+ { 0x00009940, 0x14750604 },
+ { 0x00009948, 0x9280c00a },
+ { 0x0000994c, 0x00020028 },
+ { 0x00009954, 0x5f3ca3de },
+ { 0x00009958, 0x2108ecff },
+ { 0x00009968, 0x000003ce },
+ { 0x00009970, 0x192bb515 },
+ { 0x00009974, 0x00000000 },
+ { 0x00009978, 0x00000001 },
+ { 0x0000997c, 0x00000000 },
+ { 0x00009980, 0x00000000 },
+ { 0x00009984, 0x00000000 },
+ { 0x00009988, 0x00000000 },
+ { 0x0000998c, 0x00000000 },
+ { 0x00009990, 0x00000000 },
+ { 0x00009994, 0x00000000 },
+ { 0x00009998, 0x00000000 },
+ { 0x0000999c, 0x00000000 },
+ { 0x000099a0, 0x00000000 },
+ { 0x000099a4, 0x00000001 },
+ { 0x000099a8, 0x201fff00 },
+ { 0x000099ac, 0x2def1000 },
+ { 0x000099b0, 0x03051000 },
+ { 0x000099b4, 0x00000820 },
+ { 0x000099dc, 0x00000000 },
+ { 0x000099e0, 0x00000000 },
+ { 0x000099e4, 0xaaaaaaaa },
+ { 0x000099e8, 0x3c466478 },
+ { 0x000099ec, 0x0cc80caa },
+ { 0x000099f0, 0x00000000 },
+ { 0x0000a208, 0x803e6788 },
+ { 0x0000a210, 0x4080a333 },
+ { 0x0000a214, 0x00206c10 },
+ { 0x0000a218, 0x009c4060 },
+ { 0x0000a220, 0x01834061 },
+ { 0x0000a224, 0x00000400 },
+ { 0x0000a228, 0x000003b5 },
+ { 0x0000a22c, 0x00000000 },
+ { 0x0000a234, 0x20202020 },
+ { 0x0000a238, 0x20202020 },
+ { 0x0000a244, 0x00000000 },
+ { 0x0000a248, 0xfffffffc },
+ { 0x0000a24c, 0x00000000 },
+ { 0x0000a254, 0x00000000 },
+ { 0x0000a258, 0x0ccb5380 },
+ { 0x0000a25c, 0x15151501 },
+ { 0x0000a260, 0xdfa90f01 },
+ { 0x0000a268, 0x00000000 },
+ { 0x0000a26c, 0x0ebae9e6 },
+ { 0x0000d270, 0x0d820820 },
+ { 0x0000a278, 0x318c6318 },
+ { 0x0000a27c, 0x050c0318 },
+ { 0x0000d35c, 0x07ffffef },
+ { 0x0000d360, 0x0fffffe7 },
+ { 0x0000d364, 0x17ffffe5 },
+ { 0x0000d368, 0x1fffffe4 },
+ { 0x0000d36c, 0x37ffffe3 },
+ { 0x0000d370, 0x3fffffe3 },
+ { 0x0000d374, 0x57ffffe3 },
+ { 0x0000d378, 0x5fffffe2 },
+ { 0x0000d37c, 0x7fffffe2 },
+ { 0x0000d380, 0x7f3c7bba },
+ { 0x0000d384, 0xf3307ff0 },
+ { 0x0000a388, 0x0c000000 },
+ { 0x0000a38c, 0x20202020 },
+ { 0x0000a390, 0x20202020 },
+ { 0x0000a394, 0x318c6318 },
+ { 0x0000a398, 0x00000318 },
+ { 0x0000a39c, 0x00000001 },
+ { 0x0000a3a0, 0x00000000 },
+ { 0x0000a3a4, 0x00000000 },
+ { 0x0000a3a8, 0x00000000 },
+ { 0x0000a3ac, 0x00000000 },
+ { 0x0000a3b0, 0x00000000 },
+ { 0x0000a3b4, 0x00000000 },
+ { 0x0000a3b8, 0x00000000 },
+ { 0x0000a3bc, 0x00000000 },
+ { 0x0000a3c0, 0x00000000 },
+ { 0x0000a3c4, 0x00000000 },
+ { 0x0000a3cc, 0x20202020 },
+ { 0x0000a3d0, 0x20202020 },
+ { 0x0000a3d4, 0x20202020 },
+ { 0x0000a3dc, 0x318c6318 },
+ { 0x0000a3e0, 0x00000318 },
+ { 0x0000a3e4, 0x00000000 },
+ { 0x0000a3e8, 0x18c43433 },
+ { 0x0000a3ec, 0x00f70081 },
+ { 0x00007800, 0x00140000 },
+ { 0x00007804, 0x0e4548d8 },
+ { 0x00007808, 0x54214514 },
+ { 0x0000780c, 0x02025820 },
+ { 0x00007810, 0x71c0d388 },
+ { 0x00007814, 0x924934a8 },
+ { 0x0000781c, 0x00000000 },
+ { 0x00007820, 0x00000c04 },
+ { 0x00007824, 0x00d86fff },
+ { 0x00007828, 0x26d2491b },
+ { 0x0000782c, 0x6e36d97b },
+ { 0x00007830, 0xedb6d96e },
+ { 0x00007834, 0x71400087 },
+ { 0x00007838, 0xfac68801 },
+ { 0x0000783c, 0x0001fffe },
+ { 0x00007840, 0xffeb1a20 },
+ { 0x00007844, 0x000c0db6 },
+ { 0x00007848, 0x6db61b6f },
+ { 0x0000784c, 0x6d9b66db },
+ { 0x00007850, 0x6d8c6dba },
+ { 0x00007854, 0x00040000 },
+ { 0x00007858, 0xdb003012 },
+ { 0x0000785c, 0x04924914 },
+ { 0x00007860, 0x21084210 },
+ { 0x00007864, 0xf7d7ffde },
+ { 0x00007868, 0xc2034080 },
+ { 0x0000786c, 0x48609eb4 },
+ { 0x00007870, 0x10142c00 },
+};
-static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
+static const u_int32_t ar9285PciePhy_clkreq_always_on_L1_9285_1_2[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01dcffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+
+static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = {
{0x00004040, 0x9248fd00 },
{0x00004040, 0x24924924 },
{0x00004040, 0xa8000019 },
{0x00004040, 0x13160820 },
{0x00004040, 0xe5980560 },
- {0x00004040, 0x401dcffd },
- {0x00004040, 0x1aaabe40 },
+ {0x00004040, 0xc01dcffc },
+ {0x00004040, 0x1aaabe41 },
{0x00004040, 0xbe105554 },
{0x00004040, 0x00043007 },
{0x00004044, 0x00000000 },
diff --git a/drivers/net/wireless/ath9k/mac.c b/drivers/net/wireless/ath9k/mac.c
new file mode 100644
index 000000000000..af32d091dc38
--- /dev/null
+++ b/drivers/net/wireless/ath9k/mac.c
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+#include "hw.h"
+#include "reg.h"
+#include "phy.h"
+
+static void ath9k_hw_set_txq_interrupts(struct ath_hal *ah,
+ struct ath9k_tx_queue_info *qi)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
+ "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
+ ahp->ah_txOkInterruptMask, ahp->ah_txErrInterruptMask,
+ ahp->ah_txDescInterruptMask, ahp->ah_txEolInterruptMask,
+ ahp->ah_txUrnInterruptMask);
+
+ REG_WRITE(ah, AR_IMR_S0,
+ SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK)
+ | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC));
+ REG_WRITE(ah, AR_IMR_S1,
+ SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR)
+ | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL));
+ REG_RMW_FIELD(ah, AR_IMR_S2,
+ AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask);
+}
+
+u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q)
+{
+ return REG_READ(ah, AR_QTXDP(q));
+}
+
+bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, u32 txdp)
+{
+ REG_WRITE(ah, AR_QTXDP(q), txdp);
+
+ return true;
+}
+
+bool ath9k_hw_txstart(struct ath_hal *ah, u32 q)
+{
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "queue %u\n", q);
+
+ REG_WRITE(ah, AR_Q_TXE, 1 << q);
+
+ return true;
+}
+
+u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q)
+{
+ u32 npend;
+
+ npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
+ if (npend == 0) {
+
+ if (REG_READ(ah, AR_Q_TXE) & (1 << q))
+ npend = 1;
+ }
+
+ return npend;
+}
+
+bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ u32 txcfg, curLevel, newLevel;
+ enum ath9k_int omask;
+
+ if (ah->ah_txTrigLevel >= MAX_TX_FIFO_THRESHOLD)
+ return false;
+
+ omask = ath9k_hw_set_interrupts(ah, ahp->ah_maskReg & ~ATH9K_INT_GLOBAL);
+
+ txcfg = REG_READ(ah, AR_TXCFG);
+ curLevel = MS(txcfg, AR_FTRIG);
+ newLevel = curLevel;
+ if (bIncTrigLevel) {
+ if (curLevel < MAX_TX_FIFO_THRESHOLD)
+ newLevel++;
+ } else if (curLevel > MIN_TX_FIFO_THRESHOLD)
+ newLevel--;
+ if (newLevel != curLevel)
+ REG_WRITE(ah, AR_TXCFG,
+ (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
+
+ ath9k_hw_set_interrupts(ah, omask);
+
+ ah->ah_txTrigLevel = newLevel;
+
+ return newLevel != curLevel;
+}
+
+bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q)
+{
+ u32 tsfLow, j, wait;
+
+ REG_WRITE(ah, AR_Q_TXD, 1 << q);
+
+ for (wait = 1000; wait != 0; wait--) {
+ if (ath9k_hw_numtxpending(ah, q) == 0)
+ break;
+ udelay(100);
+ }
+
+ if (ath9k_hw_numtxpending(ah, q)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "%s: Num of pending TX Frames %d on Q %d\n",
+ __func__, ath9k_hw_numtxpending(ah, q), q);
+
+ for (j = 0; j < 2; j++) {
+ tsfLow = REG_READ(ah, AR_TSF_L32);
+ REG_WRITE(ah, AR_QUIET2,
+ SM(10, AR_QUIET2_QUIET_DUR));
+ REG_WRITE(ah, AR_QUIET_PERIOD, 100);
+ REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
+ REG_SET_BIT(ah, AR_TIMER_MODE,
+ AR_QUIET_TIMER_EN);
+
+ if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
+ break;
+
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "TSF have moved while trying to set "
+ "quiet time TSF: 0x%08x\n", tsfLow);
+ }
+
+ REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+
+ udelay(200);
+ REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
+
+ wait = 1000;
+
+ while (ath9k_hw_numtxpending(ah, q)) {
+ if ((--wait) == 0) {
+ DPRINTF(ah->ah_sc, ATH_DBG_XMIT,
+ "Failed to stop Tx DMA in 100 "
+ "msec after killing last frame\n");
+ break;
+ }
+ udelay(100);
+ }
+
+ REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
+ }
+
+ REG_WRITE(ah, AR_Q_TXD, 0);
+
+ return wait != 0;
+}
+
+bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 segLen, bool firstSeg,
+ bool lastSeg, const struct ath_desc *ds0)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ if (firstSeg) {
+ ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore);
+ } else if (lastSeg) {
+ ads->ds_ctl0 = 0;
+ ads->ds_ctl1 = segLen;
+ ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
+ ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
+ } else {
+ ads->ds_ctl0 = 0;
+ ads->ds_ctl1 = segLen | AR_TxMore;
+ ads->ds_ctl2 = 0;
+ ads->ds_ctl3 = 0;
+ }
+ ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
+ ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
+ ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
+ ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
+ ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
+
+ return true;
+}
+
+void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
+ ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
+ ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
+ ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
+ ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
+}
+
+int ath9k_hw_txprocdesc(struct ath_hal *ah, struct ath_desc *ds)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ if ((ads->ds_txstatus9 & AR_TxDone) == 0)
+ return -EINPROGRESS;
+
+ ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum);
+ ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp;
+ ds->ds_txstat.ts_status = 0;
+ ds->ds_txstat.ts_flags = 0;
+
+ if (ads->ds_txstatus1 & AR_ExcessiveRetries)
+ ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
+ if (ads->ds_txstatus1 & AR_Filtered)
+ ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT;
+ if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
+ ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+ if (ads->ds_txstatus9 & AR_TxOpExceeded)
+ ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP;
+ if (ads->ds_txstatus1 & AR_TxTimerExpired)
+ ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
+
+ if (ads->ds_txstatus1 & AR_DescCfgErr)
+ ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR;
+ if (ads->ds_txstatus1 & AR_TxDataUnderrun) {
+ ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+ if (ads->ds_txstatus1 & AR_TxDelimUnderrun) {
+ ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
+ if (ads->ds_txstatus0 & AR_TxBaStatus) {
+ ds->ds_txstat.ts_flags |= ATH9K_TX_BA;
+ ds->ds_txstat.ba_low = ads->AR_BaBitmapLow;
+ ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh;
+ }
+
+ ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx);
+ switch (ds->ds_txstat.ts_rateindex) {
+ case 0:
+ ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0);
+ break;
+ case 1:
+ ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1);
+ break;
+ case 2:
+ ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2);
+ break;
+ case 3:
+ ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3);
+ break;
+ }
+
+ ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined);
+ ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00);
+ ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01);
+ ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02);
+ ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10);
+ ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11);
+ ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12);
+ ds->ds_txstat.evm0 = ads->AR_TxEVM0;
+ ds->ds_txstat.evm1 = ads->AR_TxEVM1;
+ ds->ds_txstat.evm2 = ads->AR_TxEVM2;
+ ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
+ ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
+ ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
+ ds->ds_txstat.ts_antenna = 1;
+
+ return 0;
+}
+
+void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
+ u32 keyIx, enum ath9k_key_type keyType, u32 flags)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ txPower += ahp->ah_txPowerIndexOffset;
+ if (txPower > 63)
+ txPower = 63;
+
+ ads->ds_ctl0 = (pktLen & AR_FrameLen)
+ | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
+ | SM(txPower, AR_XmitPower)
+ | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
+ | (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
+ | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
+ | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0);
+
+ ads->ds_ctl1 =
+ (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0)
+ | SM(type, AR_FrameType)
+ | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
+ | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
+ | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
+
+ ads->ds_ctl6 = SM(keyType, AR_EncrType);
+
+ if (AR_SREV_9285(ah)) {
+ ads->ds_ctl8 = 0;
+ ads->ds_ctl9 = 0;
+ ads->ds_ctl10 = 0;
+ ads->ds_ctl11 = 0;
+ }
+}
+
+void ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds,
+ struct ath_desc *lastds,
+ u32 durUpdateEn, u32 rtsctsRate,
+ u32 rtsctsDuration,
+ struct ath9k_11n_rate_series series[],
+ u32 nseries, u32 flags)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+ struct ar5416_desc *last_ads = AR5416DESC(lastds);
+ u32 ds_ctl0;
+
+ (void) nseries;
+ (void) rtsctsDuration;
+
+ if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
+ ds_ctl0 = ads->ds_ctl0;
+
+ if (flags & ATH9K_TXDESC_RTSENA) {
+ ds_ctl0 &= ~AR_CTSEnable;
+ ds_ctl0 |= AR_RTSEnable;
+ } else {
+ ds_ctl0 &= ~AR_RTSEnable;
+ ds_ctl0 |= AR_CTSEnable;
+ }
+
+ ads->ds_ctl0 = ds_ctl0;
+ } else {
+ ads->ds_ctl0 =
+ (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable));
+ }
+
+ ads->ds_ctl2 = set11nTries(series, 0)
+ | set11nTries(series, 1)
+ | set11nTries(series, 2)
+ | set11nTries(series, 3)
+ | (durUpdateEn ? AR_DurUpdateEna : 0)
+ | SM(0, AR_BurstDur);
+
+ ads->ds_ctl3 = set11nRate(series, 0)
+ | set11nRate(series, 1)
+ | set11nRate(series, 2)
+ | set11nRate(series, 3);
+
+ ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0)
+ | set11nPktDurRTSCTS(series, 1);
+
+ ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2)
+ | set11nPktDurRTSCTS(series, 3);
+
+ ads->ds_ctl7 = set11nRateFlags(series, 0)
+ | set11nRateFlags(series, 1)
+ | set11nRateFlags(series, 2)
+ | set11nRateFlags(series, 3)
+ | SM(rtsctsRate, AR_RTSCTSRate);
+ last_ads->ds_ctl2 = ads->ds_ctl2;
+ last_ads->ds_ctl3 = ads->ds_ctl3;
+}
+
+void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds,
+ u32 aggrLen)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
+ ads->ds_ctl6 &= ~AR_AggrLen;
+ ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
+}
+
+void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds,
+ u32 numDelims)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+ unsigned int ctl6;
+
+ ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
+
+ ctl6 = ads->ds_ctl6;
+ ctl6 &= ~AR_PadDelim;
+ ctl6 |= SM(numDelims, AR_PadDelim);
+ ads->ds_ctl6 = ctl6;
+}
+
+void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ ads->ds_ctl1 |= AR_IsAggr;
+ ads->ds_ctl1 &= ~AR_MoreAggr;
+ ads->ds_ctl6 &= ~AR_PadDelim;
+}
+
+void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
+}
+
+void ath9k_hw_set11n_burstduration(struct ath_hal *ah, struct ath_desc *ds,
+ u32 burstDuration)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ ads->ds_ctl2 &= ~AR_BurstDur;
+ ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
+}
+
+void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, struct ath_desc *ds,
+ u32 vmf)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+
+ if (vmf)
+ ads->ds_ctl0 |= AR_VirtMoreFrag;
+ else
+ ads->ds_ctl0 &= ~AR_VirtMoreFrag;
+}
+
+void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+
+ *txqs &= ahp->ah_intrTxqs;
+ ahp->ah_intrTxqs &= ~(*txqs);
+}
+
+bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q,
+ const struct ath9k_tx_queue_info *qinfo)
+{
+ u32 cw;
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ struct ath9k_tx_queue_info *qi;
+
+ if (q >= pCap->total_queues) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "invalid queue num %u\n", q);
+ return false;
+ }
+
+ qi = &ahp->ah_txq[q];
+ if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "inactive queue\n");
+ return false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "queue %p\n", qi);
+
+ qi->tqi_ver = qinfo->tqi_ver;
+ qi->tqi_subtype = qinfo->tqi_subtype;
+ qi->tqi_qflags = qinfo->tqi_qflags;
+ qi->tqi_priority = qinfo->tqi_priority;
+ if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT)
+ qi->tqi_aifs = min(qinfo->tqi_aifs, 255U);
+ else
+ qi->tqi_aifs = INIT_AIFS;
+ if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) {
+ cw = min(qinfo->tqi_cwmin, 1024U);
+ qi->tqi_cwmin = 1;
+ while (qi->tqi_cwmin < cw)
+ qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
+ } else
+ qi->tqi_cwmin = qinfo->tqi_cwmin;
+ if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) {
+ cw = min(qinfo->tqi_cwmax, 1024U);
+ qi->tqi_cwmax = 1;
+ while (qi->tqi_cwmax < cw)
+ qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
+ } else
+ qi->tqi_cwmax = INIT_CWMAX;
+
+ if (qinfo->tqi_shretry != 0)
+ qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U);
+ else
+ qi->tqi_shretry = INIT_SH_RETRY;
+ if (qinfo->tqi_lgretry != 0)
+ qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U);
+ else
+ qi->tqi_lgretry = INIT_LG_RETRY;
+ qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod;
+ qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit;
+ qi->tqi_burstTime = qinfo->tqi_burstTime;
+ qi->tqi_readyTime = qinfo->tqi_readyTime;
+
+ switch (qinfo->tqi_subtype) {
+ case ATH9K_WME_UPSD:
+ if (qi->tqi_type == ATH9K_TX_QUEUE_DATA)
+ qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q,
+ struct ath9k_tx_queue_info *qinfo)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ struct ath9k_tx_queue_info *qi;
+
+ if (q >= pCap->total_queues) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "invalid queue num %u\n", q);
+ return false;
+ }
+
+ qi = &ahp->ah_txq[q];
+ if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "inactive queue\n");
+ return false;
+ }
+
+ qinfo->tqi_qflags = qi->tqi_qflags;
+ qinfo->tqi_ver = qi->tqi_ver;
+ qinfo->tqi_subtype = qi->tqi_subtype;
+ qinfo->tqi_qflags = qi->tqi_qflags;
+ qinfo->tqi_priority = qi->tqi_priority;
+ qinfo->tqi_aifs = qi->tqi_aifs;
+ qinfo->tqi_cwmin = qi->tqi_cwmin;
+ qinfo->tqi_cwmax = qi->tqi_cwmax;
+ qinfo->tqi_shretry = qi->tqi_shretry;
+ qinfo->tqi_lgretry = qi->tqi_lgretry;
+ qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
+ qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
+ qinfo->tqi_burstTime = qi->tqi_burstTime;
+ qinfo->tqi_readyTime = qi->tqi_readyTime;
+
+ return true;
+}
+
+int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type,
+ const struct ath9k_tx_queue_info *qinfo)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_tx_queue_info *qi;
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ int q;
+
+ switch (type) {
+ case ATH9K_TX_QUEUE_BEACON:
+ q = pCap->total_queues - 1;
+ break;
+ case ATH9K_TX_QUEUE_CAB:
+ q = pCap->total_queues - 2;
+ break;
+ case ATH9K_TX_QUEUE_PSPOLL:
+ q = 1;
+ break;
+ case ATH9K_TX_QUEUE_UAPSD:
+ q = pCap->total_queues - 3;
+ break;
+ case ATH9K_TX_QUEUE_DATA:
+ for (q = 0; q < pCap->total_queues; q++)
+ if (ahp->ah_txq[q].tqi_type ==
+ ATH9K_TX_QUEUE_INACTIVE)
+ break;
+ if (q == pCap->total_queues) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "no available tx queue\n");
+ return -1;
+ }
+ break;
+ default:
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "bad tx queue type %u\n", type);
+ return -1;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "queue %u\n", q);
+
+ qi = &ahp->ah_txq[q];
+ if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "tx queue %u already active\n", q);
+ return -1;
+ }
+ memset(qi, 0, sizeof(struct ath9k_tx_queue_info));
+ qi->tqi_type = type;
+ if (qinfo == NULL) {
+ qi->tqi_qflags =
+ TXQ_FLAG_TXOKINT_ENABLE
+ | TXQ_FLAG_TXERRINT_ENABLE
+ | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE;
+ qi->tqi_aifs = INIT_AIFS;
+ qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
+ qi->tqi_cwmax = INIT_CWMAX;
+ qi->tqi_shretry = INIT_SH_RETRY;
+ qi->tqi_lgretry = INIT_LG_RETRY;
+ qi->tqi_physCompBuf = 0;
+ } else {
+ qi->tqi_physCompBuf = qinfo->tqi_physCompBuf;
+ (void) ath9k_hw_set_txq_props(ah, q, qinfo);
+ }
+
+ return q;
+}
+
+bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ struct ath9k_tx_queue_info *qi;
+
+ if (q >= pCap->total_queues) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "invalid queue num %u\n", q);
+ return false;
+ }
+ qi = &ahp->ah_txq[q];
+ if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "inactive queue %u\n", q);
+ return false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "release queue %u\n", q);
+
+ qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE;
+ ahp->ah_txOkInterruptMask &= ~(1 << q);
+ ahp->ah_txErrInterruptMask &= ~(1 << q);
+ ahp->ah_txDescInterruptMask &= ~(1 << q);
+ ahp->ah_txEolInterruptMask &= ~(1 << q);
+ ahp->ah_txUrnInterruptMask &= ~(1 << q);
+ ath9k_hw_set_txq_interrupts(ah, qi);
+
+ return true;
+}
+
+bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q)
+{
+ struct ath_hal_5416 *ahp = AH5416(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+ struct ath9k_channel *chan = ah->ah_curchan;
+ struct ath9k_tx_queue_info *qi;
+ u32 cwMin, chanCwMin, value;
+
+ if (q >= pCap->total_queues) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "invalid queue num %u\n", q);
+ return false;
+ }
+
+ qi = &ahp->ah_txq[q];
+ if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "inactive queue %u\n", q);
+ return true;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "reset queue %u\n", q);
+
+ if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
+ if (chan && IS_CHAN_B(chan))
+ chanCwMin = INIT_CWMIN_11B;
+ else
+ chanCwMin = INIT_CWMIN;
+
+ for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
+ } else
+ cwMin = qi->tqi_cwmin;
+
+ REG_WRITE(ah, AR_DLCL_IFS(q),
+ SM(cwMin, AR_D_LCL_IFS_CWMIN) |
+ SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) |
+ SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS));
+
+ REG_WRITE(ah, AR_DRETRY_LIMIT(q),
+ SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) |
+ SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) |
+ SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH));
+
+ REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ);
+ REG_WRITE(ah, AR_DMISC(q),
+ AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2);
+
+ if (qi->tqi_cbrPeriod) {
+ REG_WRITE(ah, AR_QCBRCFG(q),
+ SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) |
+ SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_OVF_THRESH));
+ REG_WRITE(ah, AR_QMISC(q),
+ REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_CBR |
+ (qi->tqi_cbrOverflowLimit ?
+ AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN : 0));
+ }
+ if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) {
+ REG_WRITE(ah, AR_QRDYTIMECFG(q),
+ SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) |
+ AR_Q_RDYTIMECFG_EN);
+ }
+
+ REG_WRITE(ah, AR_DCHNTIME(q),
+ SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) |
+ (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0));
+
+ if (qi->tqi_burstTime
+ && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) {
+ REG_WRITE(ah, AR_QMISC(q),
+ REG_READ(ah, AR_QMISC(q)) |
+ AR_Q_MISC_RDYTIME_EXP_POLICY);
+
+ }
+
+ if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) {
+ REG_WRITE(ah, AR_DMISC(q),
+ REG_READ(ah, AR_DMISC(q)) |
+ AR_D_MISC_POST_FR_BKOFF_DIS);
+ }
+ if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) {
+ REG_WRITE(ah, AR_DMISC(q),
+ REG_READ(ah, AR_DMISC(q)) |
+ AR_D_MISC_FRAG_BKOFF_EN);
+ }
+ switch (qi->tqi_type) {
+ case ATH9K_TX_QUEUE_BEACON:
+ REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
+ | AR_Q_MISC_FSP_DBA_GATED
+ | AR_Q_MISC_BEACON_USE
+ | AR_Q_MISC_CBR_INCR_DIS1);
+
+ REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
+ | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
+ AR_D_MISC_ARB_LOCKOUT_CNTRL_S)
+ | AR_D_MISC_BEACON_USE
+ | AR_D_MISC_POST_FR_BKOFF_DIS);
+ break;
+ case ATH9K_TX_QUEUE_CAB:
+ REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
+ | AR_Q_MISC_FSP_DBA_GATED
+ | AR_Q_MISC_CBR_INCR_DIS1
+ | AR_Q_MISC_CBR_INCR_DIS0);
+ value = (qi->tqi_readyTime -
+ (ah->ah_config.sw_beacon_response_time -
+ ah->ah_config.dma_beacon_response_time) -
+ ah->ah_config.additional_swba_backoff) * 1024;
+ REG_WRITE(ah, AR_QRDYTIMECFG(q),
+ value | AR_Q_RDYTIMECFG_EN);
+ REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
+ | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
+ AR_D_MISC_ARB_LOCKOUT_CNTRL_S));
+ break;
+ case ATH9K_TX_QUEUE_PSPOLL:
+ REG_WRITE(ah, AR_QMISC(q),
+ REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1);
+ break;
+ case ATH9K_TX_QUEUE_UAPSD:
+ REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) |
+ AR_D_MISC_POST_FR_BKOFF_DIS);
+ break;
+ default:
+ break;
+ }
+
+ if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) {
+ REG_WRITE(ah, AR_DMISC(q),
+ REG_READ(ah, AR_DMISC(q)) |
+ SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
+ AR_D_MISC_ARB_LOCKOUT_CNTRL) |
+ AR_D_MISC_POST_FR_BKOFF_DIS);
+ }
+
+ if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE)
+ ahp->ah_txOkInterruptMask |= 1 << q;
+ else
+ ahp->ah_txOkInterruptMask &= ~(1 << q);
+ if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE)
+ ahp->ah_txErrInterruptMask |= 1 << q;
+ else
+ ahp->ah_txErrInterruptMask &= ~(1 << q);
+ if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE)
+ ahp->ah_txDescInterruptMask |= 1 << q;
+ else
+ ahp->ah_txDescInterruptMask &= ~(1 << q);
+ if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE)
+ ahp->ah_txEolInterruptMask |= 1 << q;
+ else
+ ahp->ah_txEolInterruptMask &= ~(1 << q);
+ if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE)
+ ahp->ah_txUrnInterruptMask |= 1 << q;
+ else
+ ahp->ah_txUrnInterruptMask &= ~(1 << q);
+ ath9k_hw_set_txq_interrupts(ah, qi);
+
+ return true;
+}
+
+int ath9k_hw_rxprocdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 pa, struct ath_desc *nds, u64 tsf)
+{
+ struct ar5416_desc ads;
+ struct ar5416_desc *adsp = AR5416DESC(ds);
+ u32 phyerr;
+
+ if ((adsp->ds_rxstatus8 & AR_RxDone) == 0)
+ return -EINPROGRESS;
+
+ ads.u.rx = adsp->u.rx;
+
+ ds->ds_rxstat.rs_status = 0;
+ ds->ds_rxstat.rs_flags = 0;
+
+ ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
+ ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp;
+
+ ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
+ ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00);
+ ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01);
+ ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02);
+ ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10);
+ ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11);
+ ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12);
+ if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
+ ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx);
+ else
+ ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID;
+
+ ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads));
+ ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
+
+ ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
+ ds->ds_rxstat.rs_moreaggr =
+ (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
+ ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
+ ds->ds_rxstat.rs_flags =
+ (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
+ ds->ds_rxstat.rs_flags |=
+ (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
+
+ if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
+ ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
+ if (ads.ds_rxstatus8 & AR_PostDelimCRCErr)
+ ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST;
+ if (ads.ds_rxstatus8 & AR_DecryptBusyErr)
+ ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY;
+
+ if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) {
+ if (ads.ds_rxstatus8 & AR_CRCErr)
+ ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC;
+ else if (ads.ds_rxstatus8 & AR_PHYErr) {
+ ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY;
+ phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
+ ds->ds_rxstat.rs_phyerr = phyerr;
+ } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
+ ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT;
+ else if (ads.ds_rxstatus8 & AR_MichaelErr)
+ ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC;
+ }
+
+ return 0;
+}
+
+bool ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds,
+ u32 size, u32 flags)
+{
+ struct ar5416_desc *ads = AR5416DESC(ds);
+ struct ath9k_hw_capabilities *pCap = &ah->ah_caps;
+
+ ads->ds_ctl1 = size & AR_BufLen;
+ if (flags & ATH9K_RXDESC_INTREQ)
+ ads->ds_ctl1 |= AR_RxIntrReq;
+
+ ads->ds_rxstatus8 &= ~AR_RxDone;
+ if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
+ memset(&(ads->u), 0, sizeof(ads->u));
+
+ return true;
+}
+
+bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set)
+{
+ u32 reg;
+
+ if (set) {
+ REG_SET_BIT(ah, AR_DIAG_SW,
+ (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+
+ if (!ath9k_hw_wait(ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0)) {
+ REG_CLR_BIT(ah, AR_DIAG_SW,
+ (AR_DIAG_RX_DIS |
+ AR_DIAG_RX_ABORT));
+
+ reg = REG_READ(ah, AR_OBS_BUS_1);
+ DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
+ "rx failed to go idle in 10 ms RXSM=0x%x\n", reg);
+
+ return false;
+ }
+ } else {
+ REG_CLR_BIT(ah, AR_DIAG_SW,
+ (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+ }
+
+ return true;
+}
+
+void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp)
+{
+ REG_WRITE(ah, AR_RXDP, rxdp);
+}
+
+void ath9k_hw_rxena(struct ath_hal *ah)
+{
+ REG_WRITE(ah, AR_CR, AR_CR_RXE);
+}
+
+void ath9k_hw_startpcureceive(struct ath_hal *ah)
+{
+ ath9k_enable_mib_counters(ah);
+
+ ath9k_ani_reset(ah);
+
+ REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
+}
+
+void ath9k_hw_stoppcurecv(struct ath_hal *ah)
+{
+ REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+
+ ath9k_hw_disable_mib_counters(ah);
+}
+
+bool ath9k_hw_stopdmarecv(struct ath_hal *ah)
+{
+ REG_WRITE(ah, AR_CR, AR_CR_RXD);
+
+ if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
+ "dma failed to stop in 10ms\n"
+ "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
+ REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
+ return false;
+ } else {
+ return true;
+ }
+}
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index f05f584ab7bc..191eec50dc75 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -14,15 +14,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* mac80211 and PCI callbacks */
-
#include <linux/nl80211.h>
#include "core.h"
+#include "reg.h"
+#include "hw.h"
#define ATH_PCI_VERSION "0.1"
-#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13
-
static char *dev_info = "ath9k";
MODULE_AUTHOR("Atheros Communications");
@@ -36,9 +34,581 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = {
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+ { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
{ 0 }
};
+static void ath_detach(struct ath_softc *sc);
+
+/* return bus cachesize in 4B word units */
+
+static void bus_read_cachesize(struct ath_softc *sc, int *csz)
+{
+ u8 u8tmp;
+
+ pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp);
+ *csz = (int)u8tmp;
+
+ /*
+ * This check was put in to avoid "unplesant" consequences if
+ * the bootrom has not fully initialized all PCI devices.
+ * Sometimes the cache line size register is not set
+ */
+
+ if (*csz == 0)
+ *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */
+}
+
+static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
+{
+ sc->cur_rate_table = sc->hw_rate_table[mode];
+ /*
+ * All protection frames are transmited at 2Mb/s for
+ * 11g, otherwise at 1Mb/s.
+ * XXX select protection rate index from rate table.
+ */
+ sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
+}
+
+static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan)
+{
+ if (chan->chanmode == CHANNEL_A)
+ return ATH9K_MODE_11A;
+ else if (chan->chanmode == CHANNEL_G)
+ return ATH9K_MODE_11G;
+ else if (chan->chanmode == CHANNEL_B)
+ return ATH9K_MODE_11B;
+ else if (chan->chanmode == CHANNEL_A_HT20)
+ return ATH9K_MODE_11NA_HT20;
+ else if (chan->chanmode == CHANNEL_G_HT20)
+ return ATH9K_MODE_11NG_HT20;
+ else if (chan->chanmode == CHANNEL_A_HT40PLUS)
+ return ATH9K_MODE_11NA_HT40PLUS;
+ else if (chan->chanmode == CHANNEL_A_HT40MINUS)
+ return ATH9K_MODE_11NA_HT40MINUS;
+ else if (chan->chanmode == CHANNEL_G_HT40PLUS)
+ return ATH9K_MODE_11NG_HT40PLUS;
+ else if (chan->chanmode == CHANNEL_G_HT40MINUS)
+ return ATH9K_MODE_11NG_HT40MINUS;
+
+ WARN_ON(1); /* should not get here */
+
+ return ATH9K_MODE_11B;
+}
+
+static void ath_update_txpow(struct ath_softc *sc)
+{
+ struct ath_hal *ah = sc->sc_ah;
+ u32 txpow;
+
+ if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
+ ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
+ /* read back in case value is clamped */
+ ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
+ sc->sc_curtxpow = txpow;
+ }
+}
+
+static u8 parse_mpdudensity(u8 mpdudensity)
+{
+ /*
+ * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
+ * 0 for no restriction
+ * 1 for 1/4 us
+ * 2 for 1/2 us
+ * 3 for 1 us
+ * 4 for 2 us
+ * 5 for 4 us
+ * 6 for 8 us
+ * 7 for 16 us
+ */
+ switch (mpdudensity) {
+ case 0:
+ return 0;
+ case 1:
+ case 2:
+ case 3:
+ /* Our lower layer calculations limit our precision to
+ 1 microsecond */
+ return 1;
+ case 4:
+ return 2;
+ case 5:
+ return 4;
+ case 6:
+ return 8;
+ case 7:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
+{
+ struct ath_rate_table *rate_table = NULL;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate;
+ int i, maxrates;
+
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
+ rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
+ break;
+ case IEEE80211_BAND_5GHZ:
+ rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
+ break;
+ default:
+ break;
+ }
+
+ if (rate_table == NULL)
+ return;
+
+ sband = &sc->sbands[band];
+ rate = sc->rates[band];
+
+ if (rate_table->rate_cnt > ATH_RATE_MAX)
+ maxrates = ATH_RATE_MAX;
+ else
+ maxrates = rate_table->rate_cnt;
+
+ for (i = 0; i < maxrates; i++) {
+ rate[i].bitrate = rate_table->info[i].ratekbps / 100;
+ rate[i].hw_value = rate_table->info[i].ratecode;
+ sband->n_bitrates++;
+ DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n",
+ rate[i].bitrate / 10, rate[i].hw_value);
+ }
+}
+
+static int ath_setup_channels(struct ath_softc *sc)
+{
+ struct ath_hal *ah = sc->sc_ah;
+ int nchan, i, a = 0, b = 0;
+ u8 regclassids[ATH_REGCLASSIDS_MAX];
+ u32 nregclass = 0;
+ struct ieee80211_supported_band *band_2ghz;
+ struct ieee80211_supported_band *band_5ghz;
+ struct ieee80211_channel *chan_2ghz;
+ struct ieee80211_channel *chan_5ghz;
+ struct ath9k_channel *c;
+
+ /* Fill in ah->ah_channels */
+ if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (u32 *)&nchan,
+ regclassids, ATH_REGCLASSIDS_MAX,
+ &nregclass, CTRY_DEFAULT, false, 1)) {
+ u32 rd = ah->ah_currentRD;
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to collect channel list; "
+ "regdomain likely %u country code %u\n",
+ rd, CTRY_DEFAULT);
+ return -EINVAL;
+ }
+
+ band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ];
+ band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ];
+ chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ];
+ chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ];
+
+ for (i = 0; i < nchan; i++) {
+ c = &ah->ah_channels[i];
+ if (IS_CHAN_2GHZ(c)) {
+ chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
+ chan_2ghz[a].center_freq = c->channel;
+ chan_2ghz[a].max_power = c->maxTxPower;
+
+ if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
+ chan_2ghz[a].flags |= IEEE80211_CHAN_NO_IBSS;
+ if (c->channelFlags & CHANNEL_PASSIVE)
+ chan_2ghz[a].flags |= IEEE80211_CHAN_PASSIVE_SCAN;
+
+ band_2ghz->n_channels = ++a;
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "2MHz channel: %d, "
+ "channelFlags: 0x%x\n",
+ c->channel, c->channelFlags);
+ } else if (IS_CHAN_5GHZ(c)) {
+ chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
+ chan_5ghz[b].center_freq = c->channel;
+ chan_5ghz[b].max_power = c->maxTxPower;
+
+ if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
+ chan_5ghz[b].flags |= IEEE80211_CHAN_NO_IBSS;
+ if (c->channelFlags & CHANNEL_PASSIVE)
+ chan_5ghz[b].flags |= IEEE80211_CHAN_PASSIVE_SCAN;
+
+ band_5ghz->n_channels = ++b;
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "5MHz channel: %d, "
+ "channelFlags: 0x%x\n",
+ c->channel, c->channelFlags);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Set/change channels. If the channel is really being changed, it's done
+ * by reseting the chip. To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff.
+*/
+static int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
+{
+ struct ath_hal *ah = sc->sc_ah;
+ bool fastcc = true, stopped;
+
+ if (sc->sc_flags & SC_OP_INVALID)
+ return -EIO;
+
+ if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
+ hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
+ (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
+ (sc->sc_flags & SC_OP_FULL_RESET)) {
+ int status;
+ /*
+ * This is only performed if the channel settings have
+ * actually changed.
+ *
+ * To switch channels clear any pending DMA operations;
+ * wait long enough for the RX fifo to drain, reset the
+ * hardware at the new frequency, and then re-enable
+ * the relevant bits of the h/w.
+ */
+ ath9k_hw_set_interrupts(ah, 0);
+ ath_draintxq(sc, false);
+ stopped = ath_stoprecv(sc);
+
+ /* XXX: do not flush receive queue here. We don't want
+ * to flush data frames already in queue because of
+ * changing channel. */
+
+ if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
+ fastcc = false;
+
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
+ sc->sc_ah->ah_curchan->channel,
+ hchan->channel, hchan->channelFlags, sc->tx_chan_width);
+
+ spin_lock_bh(&sc->sc_resetlock);
+ if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
+ sc->sc_tx_chainmask, sc->sc_rx_chainmask,
+ sc->sc_ht_extprotspacing, fastcc, &status)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to reset channel %u (%uMhz) "
+ "flags 0x%x hal status %u\n",
+ ath9k_hw_mhz2ieee(ah, hchan->channel,
+ hchan->channelFlags),
+ hchan->channel, hchan->channelFlags, status);
+ spin_unlock_bh(&sc->sc_resetlock);
+ return -EIO;
+ }
+ spin_unlock_bh(&sc->sc_resetlock);
+
+ sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
+ sc->sc_flags &= ~SC_OP_FULL_RESET;
+
+ if (ath_startrecv(sc) != 0) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to restart recv logic\n");
+ return -EIO;
+ }
+
+ ath_setcurmode(sc, ath_chan2mode(hchan));
+ ath_update_txpow(sc);
+ ath9k_hw_set_interrupts(ah, sc->sc_imask);
+ }
+ return 0;
+}
+
+/*
+ * This routine performs the periodic noise floor calibration function
+ * that is used to adjust and optimize the chip performance. This
+ * takes environmental changes (location, temperature) into account.
+ * When the task is complete, it reschedules itself depending on the
+ * appropriate interval that was calculated.
+ */
+static void ath_ani_calibrate(unsigned long data)
+{
+ struct ath_softc *sc;
+ struct ath_hal *ah;
+ bool longcal = false;
+ bool shortcal = false;
+ bool aniflag = false;
+ unsigned int timestamp = jiffies_to_msecs(jiffies);
+ u32 cal_interval;
+
+ sc = (struct ath_softc *)data;
+ ah = sc->sc_ah;
+
+ /*
+ * don't calibrate when we're scanning.
+ * we are most likely not on our home channel.
+ */
+ if (sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC)
+ return;
+
+ /* Long calibration runs independently of short calibration. */
+ if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
+ longcal = true;
+ DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
+ sc->sc_ani.sc_longcal_timer = timestamp;
+ }
+
+ /* Short calibration applies only while sc_caldone is false */
+ if (!sc->sc_ani.sc_caldone) {
+ if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
+ ATH_SHORT_CALINTERVAL) {
+ shortcal = true;
+ DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies);
+ sc->sc_ani.sc_shortcal_timer = timestamp;
+ sc->sc_ani.sc_resetcal_timer = timestamp;
+ }
+ } else {
+ if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
+ ATH_RESTART_CALINTERVAL) {
+ ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
+ &sc->sc_ani.sc_caldone);
+ if (sc->sc_ani.sc_caldone)
+ sc->sc_ani.sc_resetcal_timer = timestamp;
+ }
+ }
+
+ /* Verify whether we must check ANI */
+ if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
+ ATH_ANI_POLLINTERVAL) {
+ aniflag = true;
+ sc->sc_ani.sc_checkani_timer = timestamp;
+ }
+
+ /* Skip all processing if there's nothing to do. */
+ if (longcal || shortcal || aniflag) {
+ /* Call ANI routine if necessary */
+ if (aniflag)
+ ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
+ ah->ah_curchan);
+
+ /* Perform calibration if necessary */
+ if (longcal || shortcal) {
+ bool iscaldone = false;
+
+ if (ath9k_hw_calibrate(ah, ah->ah_curchan,
+ sc->sc_rx_chainmask, longcal,
+ &iscaldone)) {
+ if (longcal)
+ sc->sc_ani.sc_noise_floor =
+ ath9k_hw_getchan_noise(ah,
+ ah->ah_curchan);
+
+ DPRINTF(sc, ATH_DBG_ANI,
+ "calibrate chan %u/%x nf: %d\n",
+ ah->ah_curchan->channel,
+ ah->ah_curchan->channelFlags,
+ sc->sc_ani.sc_noise_floor);
+ } else {
+ DPRINTF(sc, ATH_DBG_ANY,
+ "calibrate chan %u/%x failed\n",
+ ah->ah_curchan->channel,
+ ah->ah_curchan->channelFlags);
+ }
+ sc->sc_ani.sc_caldone = iscaldone;
+ }
+ }
+
+ /*
+ * Set timer interval based on previous results.
+ * The interval must be the shortest necessary to satisfy ANI,
+ * short calibration and long calibration.
+ */
+ cal_interval = ATH_LONG_CALINTERVAL;
+ if (sc->sc_ah->ah_config.enable_ani)
+ cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
+ if (!sc->sc_ani.sc_caldone)
+ cal_interval = min(cal_interval, (u32)ATH_SHORT_CALINTERVAL);
+
+ mod_timer(&sc->sc_ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+}
+
+/*
+ * Update tx/rx chainmask. For legacy association,
+ * hard code chainmask to 1x1, for 11n association, use
+ * the chainmask configuration.
+ */
+static void ath_update_chainmask(struct ath_softc *sc, int is_ht)
+{
+ sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
+ if (is_ht) {
+ sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
+ sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
+ } else {
+ sc->sc_tx_chainmask = 1;
+ sc->sc_rx_chainmask = 1;
+ }
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n",
+ sc->sc_tx_chainmask, sc->sc_rx_chainmask);
+}
+
+static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
+{
+ struct ath_node *an;
+
+ an = (struct ath_node *)sta->drv_priv;
+
+ if (sc->sc_flags & SC_OP_TXAGGR)
+ ath_tx_node_init(sc, an);
+
+ an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
+ sta->ht_cap.ampdu_factor);
+ an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
+}
+
+static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
+{
+ struct ath_node *an = (struct ath_node *)sta->drv_priv;
+
+ if (sc->sc_flags & SC_OP_TXAGGR)
+ ath_tx_node_cleanup(sc, an);
+}
+
+static void ath9k_tasklet(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+ u32 status = sc->sc_intrstatus;
+
+ if (status & ATH9K_INT_FATAL) {
+ /* need a chip reset */
+ ath_reset(sc, false);
+ return;
+ } else {
+
+ if (status &
+ (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) {
+ spin_lock_bh(&sc->rx.rxflushlock);
+ ath_rx_tasklet(sc, 0);
+ spin_unlock_bh(&sc->rx.rxflushlock);
+ }
+ /* XXX: optimize this */
+ if (status & ATH9K_INT_TX)
+ ath_tx_tasklet(sc);
+ }
+
+ /* re-enable hardware interrupt */
+ ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
+}
+
+static irqreturn_t ath_isr(int irq, void *dev)
+{
+ struct ath_softc *sc = dev;
+ struct ath_hal *ah = sc->sc_ah;
+ enum ath9k_int status;
+ bool sched = false;
+
+ do {
+ if (sc->sc_flags & SC_OP_INVALID) {
+ /*
+ * The hardware is not ready/present, don't
+ * touch anything. Note this can happen early
+ * on if the IRQ is shared.
+ */
+ return IRQ_NONE;
+ }
+ if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */
+ return IRQ_NONE;
+ }
+
+ /*
+ * Figure out the reason(s) for the interrupt. Note
+ * that the hal returns a pseudo-ISR that may include
+ * bits we haven't explicitly enabled so we mask the
+ * value to insure we only process bits we requested.
+ */
+ ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
+
+ status &= sc->sc_imask; /* discard unasked-for bits */
+
+ /*
+ * If there are no status bits set, then this interrupt was not
+ * for me (should have been caught above).
+ */
+ if (!status)
+ return IRQ_NONE;
+
+ sc->sc_intrstatus = status;
+
+ if (status & ATH9K_INT_FATAL) {
+ /* need a chip reset */
+ sched = true;
+ } else if (status & ATH9K_INT_RXORN) {
+ /* need a chip reset */
+ sched = true;
+ } else {
+ if (status & ATH9K_INT_SWBA) {
+ /* schedule a tasklet for beacon handling */
+ tasklet_schedule(&sc->bcon_tasklet);
+ }
+ if (status & ATH9K_INT_RXEOL) {
+ /*
+ * NB: the hardware should re-read the link when
+ * RXE bit is written, but it doesn't work
+ * at least on older hardware revs.
+ */
+ sched = true;
+ }
+
+ if (status & ATH9K_INT_TXURN)
+ /* bump tx trigger level */
+ ath9k_hw_updatetxtriglevel(ah, true);
+ /* XXX: optimize this */
+ if (status & ATH9K_INT_RX)
+ sched = true;
+ if (status & ATH9K_INT_TX)
+ sched = true;
+ if (status & ATH9K_INT_BMISS)
+ sched = true;
+ /* carrier sense timeout */
+ if (status & ATH9K_INT_CST)
+ sched = true;
+ if (status & ATH9K_INT_MIB) {
+ /*
+ * Disable interrupts until we service the MIB
+ * interrupt; otherwise it will continue to
+ * fire.
+ */
+ ath9k_hw_set_interrupts(ah, 0);
+ /*
+ * Let the hal handle the event. We assume
+ * it will clear whatever condition caused
+ * the interrupt.
+ */
+ ath9k_hw_procmibevent(ah, &sc->sc_halstats);
+ ath9k_hw_set_interrupts(ah, sc->sc_imask);
+ }
+ if (status & ATH9K_INT_TIM_TIMER) {
+ if (!(ah->ah_caps.hw_caps &
+ ATH9K_HW_CAP_AUTOSLEEP)) {
+ /* Clear RxAbort bit so that we can
+ * receive frames */
+ ath9k_hw_setrxabort(ah, 0);
+ sched = true;
+ }
+ }
+ }
+ } while (0);
+
+ ath_debug_stat_interrupt(sc, status);
+
+ if (sched) {
+ /* turn off every interrupt except SWBA */
+ ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA));
+ tasklet_schedule(&sc->intr_tq);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int ath_get_channel(struct ath_softc *sc,
struct ieee80211_channel *chan)
{
@@ -53,34 +623,39 @@ static int ath_get_channel(struct ath_softc *sc,
}
static u32 ath_get_extchanmode(struct ath_softc *sc,
- struct ieee80211_channel *chan)
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
{
u32 chanmode = 0;
- u8 ext_chan_offset = sc->sc_ht_info.ext_chan_offset;
- enum ath9k_ht_macmode tx_chan_width = sc->sc_ht_info.tx_chan_width;
switch (chan->band) {
case IEEE80211_BAND_2GHZ:
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) &&
- (tx_chan_width == ATH9K_HT_MACMODE_20))
+ switch(channel_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
chanmode = CHANNEL_G_HT20;
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40PLUS:
chanmode = CHANNEL_G_HT40PLUS;
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40MINUS:
chanmode = CHANNEL_G_HT40MINUS;
+ break;
+ }
break;
case IEEE80211_BAND_5GHZ:
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) &&
- (tx_chan_width == ATH9K_HT_MACMODE_20))
+ switch(channel_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
chanmode = CHANNEL_A_HT20;
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40PLUS:
chanmode = CHANNEL_A_HT40PLUS;
- if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) &&
- (tx_chan_width == ATH9K_HT_MACMODE_2040))
+ break;
+ case NL80211_CHAN_HT40MINUS:
chanmode = CHANNEL_A_HT40MINUS;
+ break;
+ }
break;
default:
break;
@@ -89,22 +664,31 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
return chanmode;
}
+static int ath_keyset(struct ath_softc *sc, u16 keyix,
+ struct ath9k_keyval *hk, const u8 mac[ETH_ALEN])
+{
+ bool status;
+
+ status = ath9k_hw_set_keycache_entry(sc->sc_ah,
+ keyix, hk, mac, false);
+
+ return status != false;
+}
-static int ath_setkey_tkip(struct ath_softc *sc,
- struct ieee80211_key_conf *key,
+static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
struct ath9k_keyval *hk,
const u8 *addr)
{
- u8 *key_rxmic = NULL;
- u8 *key_txmic = NULL;
+ const u8 *key_rxmic;
+ const u8 *key_txmic;
- key_txmic = key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
- key_rxmic = key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
+ key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
+ key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
if (addr == NULL) {
/* Group key installation */
- memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
- return ath_keyset(sc, key->keyidx, hk, addr);
+ memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
+ return ath_keyset(sc, keyix, hk, addr);
}
if (!sc->sc_splitmic) {
/*
@@ -113,34 +697,113 @@ static int ath_setkey_tkip(struct ath_softc *sc,
*/
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
- return ath_keyset(sc, key->keyidx, hk, addr);
+ return ath_keyset(sc, keyix, hk, addr);
}
/*
* TX key goes at first index, RX key at +32.
* The hal handles the MIC keys at index+64.
*/
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
- if (!ath_keyset(sc, key->keyidx, hk, NULL)) {
+ if (!ath_keyset(sc, keyix, hk, NULL)) {
/* Txmic entry failed. No need to proceed further */
DPRINTF(sc, ATH_DBG_KEYCACHE,
- "%s Setting TX MIC Key Failed\n", __func__);
+ "Setting TX MIC Key Failed\n");
return 0;
}
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
/* XXX delete tx key on failure? */
- return ath_keyset(sc, key->keyidx+32, hk, addr);
+ return ath_keyset(sc, keyix + 32, hk, addr);
+}
+
+static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc)
+{
+ int i;
+
+ for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) {
+ if (test_bit(i, sc->sc_keymap) ||
+ test_bit(i + 64, sc->sc_keymap))
+ continue; /* At least one part of TKIP key allocated */
+ if (sc->sc_splitmic &&
+ (test_bit(i + 32, sc->sc_keymap) ||
+ test_bit(i + 64 + 32, sc->sc_keymap)))
+ continue; /* At least one part of TKIP key allocated */
+
+ /* Found a free slot for a TKIP key */
+ return i;
+ }
+ return -1;
+}
+
+static int ath_reserve_key_cache_slot(struct ath_softc *sc)
+{
+ int i;
+
+ /* First, try to find slots that would not be available for TKIP. */
+ if (sc->sc_splitmic) {
+ for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 4; i++) {
+ if (!test_bit(i, sc->sc_keymap) &&
+ (test_bit(i + 32, sc->sc_keymap) ||
+ test_bit(i + 64, sc->sc_keymap) ||
+ test_bit(i + 64 + 32, sc->sc_keymap)))
+ return i;
+ if (!test_bit(i + 32, sc->sc_keymap) &&
+ (test_bit(i, sc->sc_keymap) ||
+ test_bit(i + 64, sc->sc_keymap) ||
+ test_bit(i + 64 + 32, sc->sc_keymap)))
+ return i + 32;
+ if (!test_bit(i + 64, sc->sc_keymap) &&
+ (test_bit(i , sc->sc_keymap) ||
+ test_bit(i + 32, sc->sc_keymap) ||
+ test_bit(i + 64 + 32, sc->sc_keymap)))
+ return i + 64;
+ if (!test_bit(i + 64 + 32, sc->sc_keymap) &&
+ (test_bit(i, sc->sc_keymap) ||
+ test_bit(i + 32, sc->sc_keymap) ||
+ test_bit(i + 64, sc->sc_keymap)))
+ return i + 64 + 32;
+ }
+ } else {
+ for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) {
+ if (!test_bit(i, sc->sc_keymap) &&
+ test_bit(i + 64, sc->sc_keymap))
+ return i;
+ if (test_bit(i, sc->sc_keymap) &&
+ !test_bit(i + 64, sc->sc_keymap))
+ return i + 64;
+ }
+ }
+
+ /* No partially used TKIP slots, pick any available slot */
+ for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax; i++) {
+ /* Do not allow slots that could be needed for TKIP group keys
+ * to be used. This limitation could be removed if we know that
+ * TKIP will not be used. */
+ if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
+ continue;
+ if (sc->sc_splitmic) {
+ if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
+ continue;
+ if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
+ continue;
+ }
+
+ if (!test_bit(i, sc->sc_keymap))
+ return i; /* Found a free slot for a key */
+ }
+
+ /* No free slot found */
+ return -1;
}
static int ath_key_config(struct ath_softc *sc,
const u8 *addr,
struct ieee80211_key_conf *key)
{
- struct ieee80211_vif *vif;
struct ath9k_keyval hk;
const u8 *mac = NULL;
int ret = 0;
- enum nl80211_iftype opmode;
+ int idx;
memset(&hk, 0, sizeof(hk));
@@ -158,226 +821,103 @@ static int ath_key_config(struct ath_softc *sc,
return -EINVAL;
}
- hk.kv_len = key->keylen;
+ hk.kv_len = key->keylen;
memcpy(hk.kv_val, key->key, key->keylen);
- if (!sc->sc_vaps[0])
- return -EIO;
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ /* For now, use the default keys for broadcast keys. This may
+ * need to change with virtual interfaces. */
+ idx = key->keyidx;
+ } else if (key->keyidx) {
+ struct ieee80211_vif *vif;
- vif = sc->sc_vaps[0]->av_if_data;
- opmode = vif->type;
-
- /*
- * Strategy:
- * For _M_STA mc tx, we will not setup a key at all since we never
- * tx mc.
- * _M_STA mc rx, we will use the keyID.
- * for _M_IBSS mc tx, we will use the keyID, and no macaddr.
- * for _M_IBSS mc rx, we will alloc a slot and plumb the mac of the
- * peer node. BUT we will plumb a cleartext key so that we can do
- * perSta default key table lookup in software.
- */
- if (is_broadcast_ether_addr(addr)) {
- switch (opmode) {
- case NL80211_IFTYPE_STATION:
- /* default key: could be group WPA key
- * or could be static WEP key */
- mac = NULL;
- break;
- case NL80211_IFTYPE_ADHOC:
- break;
- case NL80211_IFTYPE_AP:
- break;
- default:
- ASSERT(0);
- break;
- }
+ mac = addr;
+ vif = sc->sc_vaps[0];
+ if (vif->type != NL80211_IFTYPE_AP) {
+ /* Only keyidx 0 should be used with unicast key, but
+ * allow this for client mode for now. */
+ idx = key->keyidx;
+ } else
+ return -EIO;
} else {
mac = addr;
+ if (key->alg == ALG_TKIP)
+ idx = ath_reserve_key_cache_slot_tkip(sc);
+ else
+ idx = ath_reserve_key_cache_slot(sc);
+ if (idx < 0)
+ return -EIO; /* no free key cache entries */
}
if (key->alg == ALG_TKIP)
- ret = ath_setkey_tkip(sc, key, &hk, mac);
+ ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac);
else
- ret = ath_keyset(sc, key->keyidx, &hk, mac);
+ ret = ath_keyset(sc, idx, &hk, mac);
if (!ret)
return -EIO;
- return 0;
+ set_bit(idx, sc->sc_keymap);
+ if (key->alg == ALG_TKIP) {
+ set_bit(idx + 64, sc->sc_keymap);
+ if (sc->sc_splitmic) {
+ set_bit(idx + 32, sc->sc_keymap);
+ set_bit(idx + 64 + 32, sc->sc_keymap);
+ }
+ }
+
+ return idx;
}
static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key)
{
- int freeslot;
+ ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx);
+ if (key->hw_key_idx < IEEE80211_WEP_NKID)
+ return;
- freeslot = (key->keyidx >= 4) ? 1 : 0;
- ath_key_reset(sc, key->keyidx, freeslot);
+ clear_bit(key->hw_key_idx, sc->sc_keymap);
+ if (key->alg != ALG_TKIP)
+ return;
+
+ clear_bit(key->hw_key_idx + 64, sc->sc_keymap);
+ if (sc->sc_splitmic) {
+ clear_bit(key->hw_key_idx + 32, sc->sc_keymap);
+ clear_bit(key->hw_key_idx + 64 + 32, sc->sc_keymap);
+ }
}
-static void setup_ht_cap(struct ieee80211_ht_info *ht_info)
+static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info)
{
#define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
#define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
- ht_info->ht_supported = 1;
- ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH
- |(u16)IEEE80211_HT_CAP_SM_PS
- |(u16)IEEE80211_HT_CAP_SGI_40
- |(u16)IEEE80211_HT_CAP_DSSSCCK40;
+ ht_info->ht_supported = true;
+ ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SM_PS |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40;
ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536;
ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8;
- /* setup supported mcs set */
- memset(ht_info->supp_mcs_set, 0, 16);
- ht_info->supp_mcs_set[0] = 0xff;
- ht_info->supp_mcs_set[1] = 0xff;
- ht_info->supp_mcs_set[12] = IEEE80211_HT_CAP_MCS_TX_DEFINED;
-}
-
-static int ath_rate2idx(struct ath_softc *sc, int rate)
-{
- int i = 0, cur_band, n_rates;
- struct ieee80211_hw *hw = sc->hw;
-
- cur_band = hw->conf.channel->band;
- n_rates = sc->sbands[cur_band].n_bitrates;
-
- for (i = 0; i < n_rates; i++) {
- if (sc->sbands[cur_band].bitrates[i].bitrate == rate)
- break;
- }
-
- /*
- * NB:mac80211 validates rx rate index against the supported legacy rate
- * index only (should be done against ht rates also), return the highest
- * legacy rate index for rx rate which does not match any one of the
- * supported basic and extended rates to make mac80211 happy.
- * The following hack will be cleaned up once the issue with
- * the rx rate index validation in mac80211 is fixed.
- */
- if (i == n_rates)
- return n_rates - 1;
- return i;
-}
-
-static void ath9k_rx_prepare(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_recv_status *status,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_channel *curchan = hw->conf.channel;
-
- memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
-
- rx_status->mactime = status->tsf;
- rx_status->band = curchan->band;
- rx_status->freq = curchan->center_freq;
- rx_status->noise = sc->sc_ani.sc_noise_floor;
- rx_status->signal = rx_status->noise + status->rssi;
- rx_status->rate_idx = ath_rate2idx(sc, (status->rateKbps / 100));
- rx_status->antenna = status->antenna;
-
- /* XXX Fix me, 64 cannot be the max rssi value, rigure it out */
- rx_status->qual = status->rssi * 100 / 64;
-
- if (status->flags & ATH_RX_MIC_ERROR)
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
- if (status->flags & ATH_RX_FCS_ERROR)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
-
- rx_status->flag |= RX_FLAG_TSFT;
-}
-
-static u8 parse_mpdudensity(u8 mpdudensity)
-{
- /*
- * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
- * 0 for no restriction
- * 1 for 1/4 us
- * 2 for 1/2 us
- * 3 for 1 us
- * 4 for 2 us
- * 5 for 4 us
- * 6 for 8 us
- * 7 for 16 us
- */
- switch (mpdudensity) {
- case 0:
- return 0;
- case 1:
- case 2:
- case 3:
- /* Our lower layer calculations limit our precision to
- 1 microsecond */
- return 1;
- case 4:
- return 2;
- case 5:
- return 4;
- case 6:
- return 8;
- case 7:
- return 16;
- default:
- return 0;
- }
-}
-
-static void ath9k_ht_conf(struct ath_softc *sc,
- struct ieee80211_bss_conf *bss_conf)
-{
-#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14)
- struct ath_ht_info *ht_info = &sc->sc_ht_info;
-
- if (bss_conf->assoc_ht) {
- ht_info->ext_chan_offset =
- bss_conf->ht_bss_conf->bss_cap &
- IEEE80211_HT_IE_CHA_SEC_OFFSET;
-
- if (!(bss_conf->ht_conf->cap &
- IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- (bss_conf->ht_bss_conf->bss_cap &
- IEEE80211_HT_IE_CHA_WIDTH))
- ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
- else
- ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
-
- ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
- ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
- bss_conf->ht_conf->ampdu_factor);
- ht_info->mpdudensity =
- parse_mpdudensity(bss_conf->ht_conf->ampdu_density);
-
- }
-
-#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT
+ /* set up supported mcs set */
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+ ht_info->mcs.rx_mask[0] = 0xff;
+ ht_info->mcs.rx_mask[1] = 0xff;
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
static void ath9k_bss_assoc_info(struct ath_softc *sc,
+ struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_channel *curchan = hw->conf.channel;
- struct ath_vap *avp;
- int pos;
- DECLARE_MAC_BUF(mac);
+ struct ath_vap *avp = (void *)vif->drv_priv;
if (bss_conf->assoc) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
- __func__,
- bss_conf->aid);
-
- avp = sc->sc_vaps[0];
- if (avp == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
- __func__);
- return;
- }
+ DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n",
+ bss_conf->aid, sc->sc_curbssid);
/* New association, store aid */
- if (avp->av_opmode == ATH9K_M_STA) {
+ if (avp->av_opmode == NL80211_IFTYPE_STATION) {
sc->sc_curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
@@ -393,175 +933,16 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
- /* Update chainmask */
- ath_update_chainmask(sc, bss_conf->assoc_ht);
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: bssid %s aid 0x%x\n",
- __func__,
- print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
- __func__,
- curchan->center_freq);
-
- pos = ath_get_channel(sc, curchan);
- if (pos == -1) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Invalid channel\n", __func__);
- return;
- }
-
- if (hw->conf.ht_conf.ht_supported)
- sc->sc_ah->ah_channels[pos].chanmode =
- ath_get_extchanmode(sc, curchan);
- else
- sc->sc_ah->ah_channels[pos].chanmode =
- (curchan->band == IEEE80211_BAND_2GHZ) ?
- CHANNEL_G : CHANNEL_A;
-
- /* set h/w channel */
- if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to set channel\n",
- __func__);
-
- ath_rate_newstate(sc, avp);
- /* Update ratectrl about the new state */
- ath_rc_node_update(hw, avp->rc_node);
-
/* Start ANI */
mod_timer(&sc->sc_ani.timer,
jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
} else {
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: Bss Info DISSOC\n", __func__);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISSOC\n");
sc->sc_curaid = 0;
}
}
-void ath_get_beaconconfig(struct ath_softc *sc,
- int if_id,
- struct ath_beacon_config *conf)
-{
- struct ieee80211_hw *hw = sc->hw;
-
- /* fill in beacon config data */
-
- conf->beacon_interval = hw->conf.beacon_int;
- conf->listen_interval = 100;
- conf->dtim_count = 1;
- conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
-}
-
-void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
- struct ath_xmit_status *tx_status, struct ath_node *an)
-{
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-
- DPRINTF(sc, ATH_DBG_XMIT,
- "%s: TX complete: skb: %p\n", __func__, skb);
-
- if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
- tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
- /* free driver's private data area of tx_info */
- if (tx_info->driver_data[0] != NULL)
- kfree(tx_info->driver_data[0]);
- tx_info->driver_data[0] = NULL;
- }
-
- if (tx_status->flags & ATH_TX_BAR) {
- tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
- tx_status->flags &= ~ATH_TX_BAR;
- }
-
- if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) {
- if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
- /* Frame was not ACKed, but an ACK was expected */
- tx_info->status.excessive_retries = 1;
- }
- } else {
- /* Frame was ACKed */
- tx_info->flags |= IEEE80211_TX_STAT_ACK;
- }
-
- tx_info->status.retry_count = tx_status->retries;
-
- ieee80211_tx_status(hw, skb);
- if (an)
- ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
-}
-
-int _ath_rx_indicate(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_recv_status *status,
- u16 keyix)
-{
- struct ieee80211_hw *hw = sc->hw;
- struct ath_node *an = NULL;
- struct ieee80211_rx_status rx_status;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- int padsize;
- enum ATH_RX_TYPE st;
-
- /* see if any padding is done by the hw and remove it */
- if (hdrlen & 3) {
- padsize = hdrlen % 4;
- memmove(skb->data + padsize, skb->data, hdrlen);
- skb_pull(skb, padsize);
- }
-
- /* Prepare rx status */
- ath9k_rx_prepare(sc, skb, status, &rx_status);
-
- if (!(keyix == ATH9K_RXKEYIX_INVALID) &&
- !(status->flags & ATH_RX_DECRYPT_ERROR)) {
- rx_status.flag |= RX_FLAG_DECRYPTED;
- } else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
- && !(status->flags & ATH_RX_DECRYPT_ERROR)
- && skb->len >= hdrlen + 4) {
- keyix = skb->data[hdrlen + 3] >> 6;
-
- if (test_bit(keyix, sc->sc_keymap))
- rx_status.flag |= RX_FLAG_DECRYPTED;
- }
-
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, hdr->addr2);
- spin_unlock_bh(&sc->node_lock);
-
- if (an) {
- ath_rx_input(sc, an,
- hw->conf.ht_conf.ht_supported,
- skb, status, &st);
- }
- if (!an || (st != ATH_RX_CONSUMED))
- __ieee80211_rx(hw, skb, &rx_status);
-
- return 0;
-}
-
-int ath_rx_subframe(struct ath_node *an,
- struct sk_buff *skb,
- struct ath_recv_status *status)
-{
- struct ath_softc *sc = an->an_sc;
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_rx_status rx_status;
-
- /* Prepare rx status */
- ath9k_rx_prepare(sc, skb, status, &rx_status);
- if (!(status->flags & ATH_RX_DECRYPT_ERROR))
- rx_status.flag |= RX_FLAG_DECRYPTED;
-
- __ieee80211_rx(hw, skb, &rx_status);
-
- return 0;
-}
-
/********************************/
/* LED functions */
/********************************/
@@ -677,7 +1058,8 @@ fail:
ath_deinit_leds(sc);
}
-#ifdef CONFIG_RFKILL
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+
/*******************/
/* Rfkill */
/*******************/
@@ -689,14 +1071,14 @@ static void ath_radio_enable(struct ath_softc *sc)
spin_lock_bh(&sc->sc_resetlock);
if (!ath9k_hw_reset(ah, ah->ah_curchan,
- sc->sc_ht_info.tx_chan_width,
+ sc->tx_chan_width,
sc->sc_tx_chainmask,
sc->sc_rx_chainmask,
sc->sc_ht_extprotspacing,
false, &status)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset channel %u (%uMhz) "
- "flags 0x%x hal status %u\n", __func__,
+ "Unable to reset channel %u (%uMhz) "
+ "flags 0x%x hal status %u\n",
ath9k_hw_mhz2ieee(ah,
ah->ah_curchan->channel,
ah->ah_curchan->channelFlags),
@@ -708,7 +1090,7 @@ static void ath_radio_enable(struct ath_softc *sc)
ath_update_txpow(sc);
if (ath_startrecv(sc) != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to restart recv logic\n", __func__);
+ "Unable to restart recv logic\n");
return;
}
@@ -747,14 +1129,14 @@ static void ath_radio_disable(struct ath_softc *sc)
spin_lock_bh(&sc->sc_resetlock);
if (!ath9k_hw_reset(ah, ah->ah_curchan,
- sc->sc_ht_info.tx_chan_width,
+ sc->tx_chan_width,
sc->sc_tx_chainmask,
sc->sc_rx_chainmask,
sc->sc_ht_extprotspacing,
false, &status)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset channel %u (%uMhz) "
- "flags 0x%x hal status %u\n", __func__,
+ "Unable to reset channel %u (%uMhz) "
+ "flags 0x%x hal status %u\n",
ath9k_hw_mhz2ieee(ah,
ah->ah_curchan->channel,
ah->ah_curchan->channelFlags),
@@ -834,7 +1216,7 @@ static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
- "radio as it is disabled by h/w \n");
+ "radio as it is disabled by h/w\n");
return -EPERM;
}
ath_radio_enable(sc);
@@ -878,61 +1260,258 @@ static void ath_deinit_rfkill(struct ath_softc *sc)
sc->rf_kill.rfkill = NULL;
}
}
+
+static int ath_start_rfkill_poll(struct ath_softc *sc)
+{
+ if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+ queue_delayed_work(sc->hw->workqueue,
+ &sc->rf_kill.rfkill_poll, 0);
+
+ if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
+ if (rfkill_register(sc->rf_kill.rfkill)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to register rfkill\n");
+ rfkill_free(sc->rf_kill.rfkill);
+
+ /* Deinitialize the device */
+ ath_detach(sc);
+ if (sc->pdev->irq)
+ free_irq(sc->pdev->irq, sc);
+ pci_iounmap(sc->pdev, sc->mem);
+ pci_release_region(sc->pdev, 0);
+ pci_disable_device(sc->pdev);
+ ieee80211_free_hw(sc->hw);
+ return -EIO;
+ } else {
+ sc->sc_flags |= SC_OP_RFKILL_REGISTERED;
+ }
+ }
+
+ return 0;
+}
#endif /* CONFIG_RFKILL */
-static int ath_detach(struct ath_softc *sc)
+static void ath_detach(struct ath_softc *sc)
{
struct ieee80211_hw *hw = sc->hw;
+ int i = 0;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
-
- /* Deinit LED control */
- ath_deinit_leds(sc);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n");
-#ifdef CONFIG_RFKILL
- /* deinit rfkill */
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
ath_deinit_rfkill(sc);
#endif
-
- /* Unregister hw */
+ ath_deinit_leds(sc);
ieee80211_unregister_hw(hw);
-
- /* unregister Rate control */
- ath_rate_control_unregister();
-
- /* tx/rx cleanup */
-
ath_rx_cleanup(sc);
ath_tx_cleanup(sc);
- /* Deinit */
+ tasklet_kill(&sc->intr_tq);
+ tasklet_kill(&sc->bcon_tasklet);
- ath_deinit(sc);
+ if (!(sc->sc_flags & SC_OP_INVALID))
+ ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
- return 0;
+ /* cleanup tx queues */
+ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+ if (ATH_TXQ_SETUP(sc, i))
+ ath_tx_cleanupq(sc, &sc->tx.txq[i]);
+
+ ath9k_hw_detach(sc->sc_ah);
+ ath9k_exit_debug(sc);
}
-static int ath_attach(u16 devid,
- struct ath_softc *sc)
+static int ath_init(u16 devid, struct ath_softc *sc)
{
- struct ieee80211_hw *hw = sc->hw;
- int error = 0;
+ struct ath_hal *ah = NULL;
+ int status;
+ int error = 0, i;
+ int csz = 0;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
+ /* XXX: hardware will not be ready until ath_open() being called */
+ sc->sc_flags |= SC_OP_INVALID;
- error = ath_init(devid, sc);
- if (error != 0)
- return error;
+ if (ath9k_init_debug(sc) < 0)
+ printk(KERN_ERR "Unable to create debugfs files\n");
- /* Init nodes */
+ spin_lock_init(&sc->sc_resetlock);
+ mutex_init(&sc->mutex);
+ tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
+ tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
+ (unsigned long)sc);
- INIT_LIST_HEAD(&sc->node_list);
- spin_lock_init(&sc->node_lock);
+ /*
+ * Cache line size is used to size and align various
+ * structures used to communicate with the hardware.
+ */
+ bus_read_cachesize(sc, &csz);
+ /* XXX assert csz is non-zero */
+ sc->sc_cachelsz = csz << 2; /* convert to bytes */
- /* get mac address from hardware and set in mac80211 */
+ ah = ath9k_hw_attach(devid, sc, sc->mem, &status);
+ if (ah == NULL) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to attach hardware; HAL status %u\n", status);
+ error = -ENXIO;
+ goto bad;
+ }
+ sc->sc_ah = ah;
- SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
+ /* Get the hardware key cache size. */
+ sc->sc_keymax = ah->ah_caps.keycache_size;
+ if (sc->sc_keymax > ATH_KEYMAX) {
+ DPRINTF(sc, ATH_DBG_KEYCACHE,
+ "Warning, using only %u entries in %u key cache\n",
+ ATH_KEYMAX, sc->sc_keymax);
+ sc->sc_keymax = ATH_KEYMAX;
+ }
+
+ /*
+ * Reset the key cache since some parts do not
+ * reset the contents on initial power up.
+ */
+ for (i = 0; i < sc->sc_keymax; i++)
+ ath9k_hw_keyreset(ah, (u16) i);
+
+ /* Collect the channel list using the default country code */
+
+ error = ath_setup_channels(sc);
+ if (error)
+ goto bad;
+
+ /* default to MONITOR mode */
+ sc->sc_ah->ah_opmode = NL80211_IFTYPE_MONITOR;
+
+
+ /* Setup rate tables */
+
+ ath_rate_attach(sc);
+ ath_setup_rates(sc, IEEE80211_BAND_2GHZ);
+ ath_setup_rates(sc, IEEE80211_BAND_5GHZ);
+
+ /*
+ * Allocate hardware transmit queues: one queue for
+ * beacon frames and one data queue for each QoS
+ * priority. Note that the hal handles reseting
+ * these queues at the needed time.
+ */
+ sc->beacon.beaconq = ath_beaconq_setup(ah);
+ if (sc->beacon.beaconq == -1) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup a beacon xmit queue\n");
+ error = -EIO;
+ goto bad2;
+ }
+ sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
+ if (sc->beacon.cabq == NULL) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup CAB xmit queue\n");
+ error = -EIO;
+ goto bad2;
+ }
+
+ sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
+ ath_cabq_update(sc);
+
+ for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++)
+ sc->tx.hwq_map[i] = -1;
+
+ /* Setup data queues */
+ /* NB: ensure BK queue is the lowest priority h/w queue */
+ if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for BK traffic\n");
+ error = -EIO;
+ goto bad2;
+ }
+
+ if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for BE traffic\n");
+ error = -EIO;
+ goto bad2;
+ }
+ if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for VI traffic\n");
+ error = -EIO;
+ goto bad2;
+ }
+ if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for VO traffic\n");
+ error = -EIO;
+ goto bad2;
+ }
+
+ /* Initializes the noise floor to a reasonable default value.
+ * Later on this will be updated during ANI processing. */
+
+ sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
+ setup_timer(&sc->sc_ani.timer, ath_ani_calibrate, (unsigned long)sc);
+
+ if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
+ ATH9K_CIPHER_TKIP, NULL)) {
+ /*
+ * Whether we should enable h/w TKIP MIC.
+ * XXX: if we don't support WME TKIP MIC, then we wouldn't
+ * report WMM capable, so it's always safe to turn on
+ * TKIP MIC in this case.
+ */
+ ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
+ 0, 1, NULL);
+ }
+
+ /*
+ * Check whether the separate key cache entries
+ * are required to handle both tx+rx MIC keys.
+ * With split mic keys the number of stations is limited
+ * to 27 otherwise 59.
+ */
+ if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
+ ATH9K_CIPHER_TKIP, NULL)
+ && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
+ ATH9K_CIPHER_MIC, NULL)
+ && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
+ 0, NULL))
+ sc->sc_splitmic = 1;
+
+ /* turn on mcast key search if possible */
+ if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
+ (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
+ 1, NULL);
+
+ sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
+ sc->sc_config.txpowlimit_override = 0;
+
+ /* 11n Capabilities */
+ if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
+ sc->sc_flags |= SC_OP_TXAGGR;
+ sc->sc_flags |= SC_OP_RXAGGR;
+ }
+
+ sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask;
+ sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
+
+ ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
+ sc->rx.defant = ath9k_hw_getdefantenna(ah);
+
+ ath9k_hw_getmac(ah, sc->sc_myaddr);
+ if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
+ ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
+ ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
+ ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
+ }
+
+ sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
+
+ /* initialize beacon slots */
+ for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++)
+ sc->beacon.bslot[i] = ATH_IF_ID_ANY;
+
+ /* save MISC configurations */
+ sc->sc_config.swBeaconProcess = 1;
/* setup channels and rates */
@@ -942,55 +1521,81 @@ static int ath_attach(u16 devid,
sc->rates[IEEE80211_BAND_2GHZ];
sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
- if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
- /* Setup HT capabilities for 2.4Ghz*/
- setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
-
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &sc->sbands[IEEE80211_BAND_2GHZ];
-
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
sc->sbands[IEEE80211_BAND_5GHZ].channels =
sc->channels[IEEE80211_BAND_5GHZ];
sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
sc->rates[IEEE80211_BAND_5GHZ];
- sc->sbands[IEEE80211_BAND_5GHZ].band =
- IEEE80211_BAND_5GHZ;
+ sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
+ }
- if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
- /* Setup HT capabilities for 5Ghz*/
- setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info);
+ return 0;
+bad2:
+ /* cleanup tx queues */
+ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+ if (ATH_TXQ_SETUP(sc, i))
+ ath_tx_cleanupq(sc, &sc->tx.txq[i]);
+bad:
+ if (ah)
+ ath9k_hw_detach(ah);
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &sc->sbands[IEEE80211_BAND_5GHZ];
- }
+ return error;
+}
+
+static int ath_attach(u16 devid, struct ath_softc *sc)
+{
+ struct ieee80211_hw *hw = sc->hw;
+ int error = 0;
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n");
+
+ error = ath_init(devid, sc);
+ if (error != 0)
+ return error;
+
+ /* get mac address from hardware and set in mac80211 */
+
+ SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
+
+ hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_AMPDU_AGGREGATION;
- /* FIXME: Have to figure out proper hw init values later */
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
hw->queues = 4;
- hw->ampdu_queues = 1;
+ hw->max_rates = 4;
+ hw->max_rate_tries = ATH_11N_TXMAXTRY;
+ hw->sta_data_size = sizeof(struct ath_node);
+ hw->vif_data_size = sizeof(struct ath_vap);
- /* Register rate control */
hw->rate_control_algorithm = "ath9k_rate_control";
- error = ath_rate_control_register();
- if (error != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to register rate control "
- "algorithm:%d\n", __func__, error);
- ath_rate_control_unregister();
- goto bad;
- }
- error = ieee80211_register_hw(hw);
- if (error != 0) {
- ath_rate_control_unregister();
- goto bad;
+ if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
+ setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+ if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes))
+ setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
}
- /* Initialize LED control */
- ath_init_leds(sc);
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ];
+ if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes))
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &sc->sbands[IEEE80211_BAND_5GHZ];
-#ifdef CONFIG_RFKILL
+ /* initialize tx/rx engine */
+ error = ath_tx_init(sc, ATH_TXBUF);
+ if (error != 0)
+ goto detach;
+
+ error = ath_rx_init(sc, ATH_RXBUF);
+ if (error != 0)
+ goto detach;
+
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/* Initialze h/w Rfkill */
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
@@ -1000,88 +1605,371 @@ static int ath_attach(u16 devid,
goto detach;
#endif
- /* initialize tx/rx engine */
-
- error = ath_tx_init(sc, ATH_TXBUF);
- if (error != 0)
- goto detach;
+ error = ieee80211_register_hw(hw);
- error = ath_rx_init(sc, ATH_RXBUF);
- if (error != 0)
- goto detach;
+ /* Initialize LED control */
+ ath_init_leds(sc);
return 0;
detach:
ath_detach(sc);
-bad:
return error;
}
+int ath_reset(struct ath_softc *sc, bool retry_tx)
+{
+ struct ath_hal *ah = sc->sc_ah;
+ int status;
+ int error = 0;
+
+ ath9k_hw_set_interrupts(ah, 0);
+ ath_draintxq(sc, retry_tx);
+ ath_stoprecv(sc);
+ ath_flushrecv(sc);
+
+ spin_lock_bh(&sc->sc_resetlock);
+ if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan,
+ sc->tx_chan_width,
+ sc->sc_tx_chainmask, sc->sc_rx_chainmask,
+ sc->sc_ht_extprotspacing, false, &status)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to reset hardware; hal status %u\n", status);
+ error = -EIO;
+ }
+ spin_unlock_bh(&sc->sc_resetlock);
+
+ if (ath_startrecv(sc) != 0)
+ DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n");
+
+ /*
+ * We may be doing a reset in response to a request
+ * that changes the channel so update any state that
+ * might change as a result.
+ */
+ ath_setcurmode(sc, ath_chan2mode(sc->sc_ah->ah_curchan));
+
+ ath_update_txpow(sc);
+
+ if (sc->sc_flags & SC_OP_BEACONS)
+ ath_beacon_config(sc, ATH_IF_ID_ANY); /* restart beacons */
+
+ ath9k_hw_set_interrupts(ah, sc->sc_imask);
+
+ if (retry_tx) {
+ int i;
+ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+ if (ATH_TXQ_SETUP(sc, i)) {
+ spin_lock_bh(&sc->tx.txq[i].axq_lock);
+ ath_txq_schedule(sc, &sc->tx.txq[i]);
+ spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+ }
+ }
+ }
+
+ return error;
+}
+
+/*
+ * This function will allocate both the DMA descriptor structure, and the
+ * buffers it contains. These are used to contain the descriptors used
+ * by the system.
+*/
+int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
+ struct list_head *head, const char *name,
+ int nbuf, int ndesc)
+{
+#define DS2PHYS(_dd, _ds) \
+ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
+#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
+
+ struct ath_desc *ds;
+ struct ath_buf *bf;
+ int i, bsize, error;
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
+ name, nbuf, ndesc);
+
+ /* ath_desc must be a multiple of DWORDs */
+ if ((sizeof(struct ath_desc) % 4) != 0) {
+ DPRINTF(sc, ATH_DBG_FATAL, "ath_desc not DWORD aligned\n");
+ ASSERT((sizeof(struct ath_desc) % 4) == 0);
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ dd->dd_name = name;
+ dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
+
+ /*
+ * Need additional DMA memory because we can't use
+ * descriptors that cross the 4K page boundary. Assume
+ * one skipped descriptor per 4K page.
+ */
+ if (!(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+ u32 ndesc_skipped =
+ ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
+ u32 dma_len;
+
+ while (ndesc_skipped) {
+ dma_len = ndesc_skipped * sizeof(struct ath_desc);
+ dd->dd_desc_len += dma_len;
+
+ ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
+ };
+ }
+
+ /* allocate descriptors */
+ dd->dd_desc = pci_alloc_consistent(sc->pdev,
+ dd->dd_desc_len,
+ &dd->dd_desc_paddr);
+ if (dd->dd_desc == NULL) {
+ error = -ENOMEM;
+ goto fail;
+ }
+ ds = dd->dd_desc;
+ DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
+ dd->dd_name, ds, (u32) dd->dd_desc_len,
+ ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
+
+ /* allocate buffers */
+ bsize = sizeof(struct ath_buf) * nbuf;
+ bf = kmalloc(bsize, GFP_KERNEL);
+ if (bf == NULL) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+ memset(bf, 0, bsize);
+ dd->dd_bufptr = bf;
+
+ INIT_LIST_HEAD(head);
+ for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
+ bf->bf_desc = ds;
+ bf->bf_daddr = DS2PHYS(dd, ds);
+
+ if (!(sc->sc_ah->ah_caps.hw_caps &
+ ATH9K_HW_CAP_4KB_SPLITTRANS)) {
+ /*
+ * Skip descriptor addresses which can cause 4KB
+ * boundary crossing (addr + length) with a 32 dword
+ * descriptor fetch.
+ */
+ while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+ ASSERT((caddr_t) bf->bf_desc <
+ ((caddr_t) dd->dd_desc +
+ dd->dd_desc_len));
+
+ ds += ndesc;
+ bf->bf_desc = ds;
+ bf->bf_daddr = DS2PHYS(dd, ds);
+ }
+ }
+ list_add_tail(&bf->list, head);
+ }
+ return 0;
+fail2:
+ pci_free_consistent(sc->pdev,
+ dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+fail:
+ memset(dd, 0, sizeof(*dd));
+ return error;
+#undef ATH_DESC_4KB_BOUND_CHECK
+#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
+#undef DS2PHYS
+}
+
+void ath_descdma_cleanup(struct ath_softc *sc,
+ struct ath_descdma *dd,
+ struct list_head *head)
+{
+ pci_free_consistent(sc->pdev,
+ dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+
+ INIT_LIST_HEAD(head);
+ kfree(dd->dd_bufptr);
+ memset(dd, 0, sizeof(*dd));
+}
+
+int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
+{
+ int qnum;
+
+ switch (queue) {
+ case 0:
+ qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO];
+ break;
+ case 1:
+ qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI];
+ break;
+ case 2:
+ qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
+ break;
+ case 3:
+ qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK];
+ break;
+ default:
+ qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
+ break;
+ }
+
+ return qnum;
+}
+
+int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
+{
+ int qnum;
+
+ switch (queue) {
+ case ATH9K_WME_AC_VO:
+ qnum = 0;
+ break;
+ case ATH9K_WME_AC_VI:
+ qnum = 1;
+ break;
+ case ATH9K_WME_AC_BE:
+ qnum = 2;
+ break;
+ case ATH9K_WME_AC_BK:
+ qnum = 3;
+ break;
+ default:
+ qnum = -1;
+ break;
+ }
+
+ return qnum;
+}
+
+/**********************/
+/* mac80211 callbacks */
+/**********************/
+
static int ath9k_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ieee80211_channel *curchan = hw->conf.channel;
- int error = 0, pos;
+ struct ath9k_channel *init_channel;
+ int error = 0, pos, status;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
- "initial channel: %d MHz\n", __func__, curchan->center_freq);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
+ "initial channel: %d MHz\n", curchan->center_freq);
/* setup initial channel */
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
- return -EINVAL;
+ DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n", curchan->center_freq);
+ error = -EINVAL;
+ goto error;
}
+ sc->tx_chan_width = ATH9K_HT_MACMODE_20;
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
+ init_channel = &sc->sc_ah->ah_channels[pos];
- /* open ath_dev */
- error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
- if (error) {
+ /* Reset SERDES registers */
+ ath9k_hw_configpcipowersave(sc->sc_ah, 0);
+
+ /*
+ * The basic interface to setting the hardware in a good
+ * state is ``reset''. On return the hardware is known to
+ * be powered up and with interrupts disabled. This must
+ * be followed by initialization of the appropriate bits
+ * and then setup of the interrupt mask.
+ */
+ spin_lock_bh(&sc->sc_resetlock);
+ if (!ath9k_hw_reset(sc->sc_ah, init_channel,
+ sc->tx_chan_width,
+ sc->sc_tx_chainmask, sc->sc_rx_chainmask,
+ sc->sc_ht_extprotspacing, false, &status)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to complete ath_open\n", __func__);
- return error;
+ "Unable to reset hardware; hal status %u "
+ "(freq %u flags 0x%x)\n", status,
+ init_channel->channel, init_channel->channelFlags);
+ error = -EIO;
+ spin_unlock_bh(&sc->sc_resetlock);
+ goto error;
}
+ spin_unlock_bh(&sc->sc_resetlock);
-#ifdef CONFIG_RFKILL
- /* Start rfkill polling */
- if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
- queue_delayed_work(sc->hw->workqueue,
- &sc->rf_kill.rfkill_poll, 0);
-
- if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
- if (rfkill_register(sc->rf_kill.rfkill)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to register rfkill\n");
- rfkill_free(sc->rf_kill.rfkill);
+ /*
+ * This is needed only to setup initial state
+ * but it's best done after a reset.
+ */
+ ath_update_txpow(sc);
- /* Deinitialize the device */
- if (sc->pdev->irq)
- free_irq(sc->pdev->irq, sc);
- ath_detach(sc);
- pci_iounmap(sc->pdev, sc->mem);
- pci_release_region(sc->pdev, 0);
- pci_disable_device(sc->pdev);
- ieee80211_free_hw(hw);
- return -EIO;
- } else {
- sc->sc_flags |= SC_OP_RFKILL_REGISTERED;
- }
+ /*
+ * Setup the hardware after reset:
+ * The receive engine is set going.
+ * Frame transmit is handled entirely
+ * in the frame output path; there's nothing to do
+ * here except setup the interrupt mask.
+ */
+ if (ath_startrecv(sc) != 0) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "Unable to start recv logic\n");
+ error = -EIO;
+ goto error;
}
+
+ /* Setup our intr mask. */
+ sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX
+ | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
+ | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
+
+ if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT)
+ sc->sc_imask |= ATH9K_INT_GTT;
+
+ if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
+ sc->sc_imask |= ATH9K_INT_CST;
+
+ /*
+ * Enable MIB interrupts when there are hardware phy counters.
+ * Note we only do this (at the moment) for station mode.
+ */
+ if (ath9k_hw_phycounters(sc->sc_ah) &&
+ ((sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) ||
+ (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC)))
+ sc->sc_imask |= ATH9K_INT_MIB;
+ /*
+ * Some hardware processes the TIM IE and fires an
+ * interrupt when the TIM bit is set. For hardware
+ * that does, if not overridden by configuration,
+ * enable the TIM interrupt when operating as station.
+ */
+ if ((sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
+ (sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) &&
+ !sc->sc_config.swBeaconProcess)
+ sc->sc_imask |= ATH9K_INT_TIM;
+
+ ath_setcurmode(sc, ath_chan2mode(init_channel));
+
+ sc->sc_flags &= ~SC_OP_INVALID;
+
+ /* Disable BMISS interrupt when we're not associated */
+ sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+ ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
+
+ ieee80211_wake_queues(sc->hw);
+
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+ error = ath_start_rfkill_poll(sc);
#endif
- ieee80211_wake_queues(hw);
- return 0;
+error:
+ return error;
}
static int ath9k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ath_softc *sc = hw->priv;
+ struct ath_tx_control txctl;
int hdrlen, padsize;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ memset(&txctl, 0, sizeof(struct ath_tx_control));
/*
* As a temporary workaround, assign seq# here; this will likely need
@@ -1091,9 +1979,9 @@ static int ath9k_tx(struct ieee80211_hw *hw,
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
- sc->seq_no += 0x10;
+ sc->tx.seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
+ hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
/* Add the padding after the header if this is not already done */
@@ -1106,45 +1994,68 @@ static int ath9k_tx(struct ieee80211_hw *hw,
memmove(skb->data, skb->data + padsize, hdrlen);
}
- DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
- __func__,
- skb);
+ /* Check if a tx queue is available */
- if (ath_tx_start(sc, skb) != 0) {
- DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
- dev_kfree_skb_any(skb);
- /* FIXME: Check for proper return value from ATH_DEV */
- return 0;
+ txctl.txq = ath_test_get_txq(sc, skb);
+ if (!txctl.txq)
+ goto exit;
+
+ DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
+
+ if (ath_tx_start(sc, skb, &txctl) != 0) {
+ DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n");
+ goto exit;
}
return 0;
+exit:
+ dev_kfree_skb_any(skb);
+ return 0;
}
static void ath9k_stop(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
- int error;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
+ if (sc->sc_flags & SC_OP_INVALID) {
+ DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
+ return;
+ }
- error = ath_suspend(sc);
- if (error)
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: Device is no longer present\n", __func__);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Cleaning up\n");
+
+ ieee80211_stop_queues(sc->hw);
+
+ /* make sure h/w will not generate any interrupt
+ * before setting the invalid flag. */
+ ath9k_hw_set_interrupts(sc->sc_ah, 0);
- ieee80211_stop_queues(hw);
+ if (!(sc->sc_flags & SC_OP_INVALID)) {
+ ath_draintxq(sc, false);
+ ath_stoprecv(sc);
+ ath9k_hw_phy_disable(sc->sc_ah);
+ } else
+ sc->rx.rxlink = NULL;
-#ifdef CONFIG_RFKILL
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
+ /* disable HAL and put h/w to sleep */
+ ath9k_hw_disable(sc->sc_ah);
+ ath9k_hw_configpcipowersave(sc->sc_ah, 1);
+
+ sc->sc_flags |= SC_OP_INVALID;
+
+ DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n");
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ath_softc *sc = hw->priv;
- int error, ic_opmode = 0;
+ struct ath_vap *avp = (void *)conf->vif->drv_priv;
+ enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
/* Support only vap for now */
@@ -1153,32 +2064,34 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
switch (conf->type) {
case NL80211_IFTYPE_STATION:
- ic_opmode = ATH9K_M_STA;
+ ic_opmode = NL80211_IFTYPE_STATION;
break;
case NL80211_IFTYPE_ADHOC:
- ic_opmode = ATH9K_M_IBSS;
+ ic_opmode = NL80211_IFTYPE_ADHOC;
break;
case NL80211_IFTYPE_AP:
- ic_opmode = ATH9K_M_HOSTAP;
+ ic_opmode = NL80211_IFTYPE_AP;
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Interface type %d not yet supported\n",
- __func__, conf->type);
+ "Interface type %d not yet supported\n", conf->type);
return -EOPNOTSUPP;
}
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n",
- __func__,
- ic_opmode);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VAP of type: %d\n", ic_opmode);
- error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
- if (error) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to attach vap, error: %d\n",
- __func__, error);
- return error;
- }
+ /* Set the VAP opmode */
+ avp->av_opmode = ic_opmode;
+ avp->av_bslot = -1;
+
+ if (ic_opmode == NL80211_IFTYPE_AP)
+ ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
+
+ sc->sc_vaps[0] = conf->vif;
+ sc->sc_nvaps++;
+
+ /* Set the device opmode */
+ sc->sc_ah->ah_opmode = ic_opmode;
if (conf->type == NL80211_IFTYPE_AP) {
/* TODO: is this a suitable place to start ANI for AP mode? */
@@ -1194,78 +2107,76 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ath_softc *sc = hw->priv;
- struct ath_vap *avp;
- int error;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__);
+ struct ath_vap *avp = (void *)conf->vif->drv_priv;
- avp = sc->sc_vaps[0];
- if (avp == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
- __func__);
- return;
- }
+ DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n");
-#ifdef CONFIG_SLOW_ANT_DIV
- ath_slow_ant_div_stop(&sc->sc_antdiv);
-#endif
/* Stop ANI */
del_timer_sync(&sc->sc_ani.timer);
- /* Update ratectrl */
- ath_rate_newstate(sc, avp);
-
/* Reclaim beacon resources */
- if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
- sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
- ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP ||
+ sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC) {
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
ath_beacon_return(sc, avp);
}
- /* Set interrupt mask */
- sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
- ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL);
sc->sc_flags &= ~SC_OP_BEACONS;
- error = ath_vap_detach(sc, 0);
- if (error)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to detach vap, error: %d\n",
- __func__, error);
+ sc->sc_vaps[0] = NULL;
+ sc->sc_nvaps--;
}
-static int ath9k_config(struct ieee80211_hw *hw,
- struct ieee80211_conf *conf)
+static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath_softc *sc = hw->priv;
- struct ieee80211_channel *curchan = hw->conf.channel;
- int pos;
+ struct ieee80211_conf *conf = &hw->conf;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
- __func__,
- curchan->center_freq);
+ mutex_lock(&sc->mutex);
+ if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |
+ IEEE80211_CONF_CHANGE_HT)) {
+ struct ieee80211_channel *curchan = hw->conf.channel;
+ int pos;
- pos = ath_get_channel(sc, curchan);
- if (pos == -1) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
- return -EINVAL;
- }
+ DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
+ curchan->center_freq);
- sc->sc_ah->ah_channels[pos].chanmode =
- (curchan->band == IEEE80211_BAND_2GHZ) ?
- CHANNEL_G : CHANNEL_A;
+ pos = ath_get_channel(sc, curchan);
+ if (pos == -1) {
+ DPRINTF(sc, ATH_DBG_FATAL, "Invalid channel: %d\n",
+ curchan->center_freq);
+ mutex_unlock(&sc->mutex);
+ return -EINVAL;
+ }
- if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
+ sc->tx_chan_width = ATH9K_HT_MACMODE_20;
sc->sc_ah->ah_channels[pos].chanmode =
- ath_get_extchanmode(sc, curchan);
+ (curchan->band == IEEE80211_BAND_2GHZ) ?
+ CHANNEL_G : CHANNEL_A;
- sc->sc_config.txpowlimit = 2 * conf->power_level;
+ if (conf->ht.enabled) {
+ if (conf->ht.channel_type == NL80211_CHAN_HT40PLUS ||
+ conf->ht.channel_type == NL80211_CHAN_HT40MINUS)
+ sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
- /* set h/w channel */
- if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n",
- __func__);
+ sc->sc_ah->ah_channels[pos].chanmode =
+ ath_get_extchanmode(sc, curchan,
+ conf->ht.channel_type);
+ }
+
+ if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) {
+ DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
+ mutex_unlock(&sc->mutex);
+ return -EINVAL;
+ }
+
+ ath_update_chainmask(sc, conf->ht.enabled);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER)
+ sc->sc_config.txpowlimit = 2 * conf->power_level;
+ mutex_unlock(&sc->mutex);
return 0;
}
@@ -1275,23 +2186,15 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
{
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
- struct ath_vap *avp;
+ struct ath_vap *avp = (void *)vif->drv_priv;
u32 rfilt = 0;
int error, i;
- DECLARE_MAC_BUF(mac);
-
- avp = sc->sc_vaps[0];
- if (avp == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
- __func__);
- return -EINVAL;
- }
/* TODO: Need to decide which hw opmode to use for multi-interface
* cases */
if (vif->type == NL80211_IFTYPE_AP &&
- ah->ah_opmode != ATH9K_M_HOSTAP) {
- ah->ah_opmode = ATH9K_M_HOSTAP;
+ ah->ah_opmode != NL80211_IFTYPE_AP) {
+ ah->ah_opmode = NL80211_IFTYPE_STATION;
ath9k_hw_setopmode(ah);
ath9k_hw_write_associd(ah, sc->sc_myaddr, 0);
/* Request full reset to get hw opmode changed properly */
@@ -1303,9 +2206,6 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
switch (vif->type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- /* Update ratectrl about the new state */
- ath_rate_newstate(sc, avp);
-
/* Set BSSID */
memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN);
sc->sc_curaid = 0;
@@ -1315,27 +2215,9 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
/* Set aggregation protection mode parameters */
sc->sc_config.ath_aggr_prot = 0;
- /*
- * Reset our TSF so that its value is lower than the
- * beacon that we are trying to catch.
- * Only then hw will update its TSF register with the
- * new beacon. Reset the TSF before setting the BSSID
- * to avoid allowing in any frames that would update
- * our TSF only to have us clear it
- * immediately thereafter.
- */
- ath9k_hw_reset_tsf(sc->sc_ah);
-
- /* Disable BMISS interrupt when we're not associated */
- ath9k_hw_set_interrupts(sc->sc_ah,
- sc->sc_imask &
- ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
- sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
-
DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: RX filter 0x%x bssid %s aid 0x%x\n",
- __func__, rfilt,
- print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
+ "RX filter 0x%x bssid %pM aid 0x%x\n",
+ rfilt, sc->sc_curbssid, sc->sc_curaid);
/* need to reconfigure the beacon */
sc->sc_flags &= ~SC_OP_BEACONS ;
@@ -1357,7 +2239,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
* causes reconfiguration; we may be called
* with beacon transmission active.
*/
- ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
error = ath_beacon_alloc(sc, 0);
if (error != 0)
@@ -1403,7 +2285,7 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw,
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
- sc->rx_filter = *total_flags;
+ sc->rx.rxfilter = *total_flags;
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
@@ -1412,8 +2294,7 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw,
ath9k_hw_write_associd(sc->sc_ah, ath_bcast_mac, 0);
}
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n",
- __func__, sc->rx_filter);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", sc->rx.rxfilter);
}
static void ath9k_sta_notify(struct ieee80211_hw *hw,
@@ -1422,37 +2303,13 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
struct ieee80211_sta *sta)
{
struct ath_softc *sc = hw->priv;
- struct ath_node *an;
- unsigned long flags;
- DECLARE_MAC_BUF(mac);
-
- spin_lock_irqsave(&sc->node_lock, flags);
- an = ath_node_find(sc, sta->addr);
- spin_unlock_irqrestore(&sc->node_lock, flags);
switch (cmd) {
case STA_NOTIFY_ADD:
- spin_lock_irqsave(&sc->node_lock, flags);
- if (!an) {
- ath_node_attach(sc, sta->addr, 0);
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
- __func__, print_mac(mac, sta->addr));
- } else {
- ath_node_get(sc, sta->addr);
- }
- spin_unlock_irqrestore(&sc->node_lock, flags);
+ ath_node_attach(sc, sta);
break;
case STA_NOTIFY_REMOVE:
- if (!an)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Removal of a non-existent node\n",
- __func__);
- else {
- ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
- __func__,
- print_mac(mac, sta->addr));
- }
+ ath_node_detach(sc, sta);
break;
default:
break;
@@ -1477,20 +2334,14 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw,
qnum = ath_get_hal_qnum(queue, sc);
DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: Configure tx [queue/halq] [%d/%d], "
+ "Configure tx [queue/halq] [%d/%d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
- __func__,
- queue,
- qnum,
- params->aifs,
- params->cw_min,
- params->cw_max,
- params->txop);
+ queue, qnum, params->aifs, params->cw_min,
+ params->cw_max, params->txop);
ret = ath_txq_update(sc, qnum, &qi);
if (ret)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: TXQ Update failed\n", __func__);
+ DPRINTF(sc, ATH_DBG_FATAL, "TXQ Update failed\n");
return ret;
}
@@ -1504,23 +2355,22 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
int ret = 0;
- DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
+ DPRINTF(sc, ATH_DBG_KEYCACHE, "Set HW Key\n");
switch (cmd) {
case SET_KEY:
ret = ath_key_config(sc, addr, key);
- if (!ret) {
- set_bit(key->keyidx, sc->sc_keymap);
- key->hw_key_idx = key->keyidx;
+ if (ret >= 0) {
+ key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->alg == ALG_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ ret = 0;
}
break;
case DISABLE_KEY:
ath_key_delete(sc, key);
- clear_bit(key->keyidx, sc->sc_keymap);
break;
default:
ret = -EINVAL;
@@ -1537,8 +2387,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
- __func__,
+ DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
bss_conf->use_short_preamble);
if (bss_conf->use_short_preamble)
sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
@@ -1547,8 +2396,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n",
- __func__,
+ DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
bss_conf->use_cts_prot);
if (bss_conf->use_cts_prot &&
hw->conf.channel->band != IEEE80211_BAND_5GHZ)
@@ -1557,18 +2405,10 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
}
- if (changed & BSS_CHANGED_HT) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
- __func__,
- bss_conf->assoc_ht);
- ath9k_ht_conf(sc, bss_conf);
- }
-
if (changed & BSS_CHANGED_ASSOC) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n",
- __func__,
+ DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
bss_conf->assoc);
- ath9k_bss_assoc_info(sc, bss_conf);
+ ath9k_bss_assoc_info(sc, vif, bss_conf);
}
}
@@ -1601,50 +2441,37 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
switch (action) {
case IEEE80211_AMPDU_RX_START:
- ret = ath_rx_aggr_start(sc, sta->addr, tid, ssn);
- if (ret < 0)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to start RX aggregation\n",
- __func__);
+ if (!(sc->sc_flags & SC_OP_RXAGGR))
+ ret = -ENOTSUPP;
break;
case IEEE80211_AMPDU_RX_STOP:
- ret = ath_rx_aggr_stop(sc, sta->addr, tid);
- if (ret < 0)
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to stop RX aggregation\n",
- __func__);
break;
case IEEE80211_AMPDU_TX_START:
- ret = ath_tx_aggr_start(sc, sta->addr, tid, ssn);
+ ret = ath_tx_aggr_start(sc, sta, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to start TX aggregation\n",
- __func__);
+ "Unable to start TX aggregation\n");
else
ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
- ret = ath_tx_aggr_stop(sc, sta->addr, tid);
+ ret = ath_tx_aggr_stop(sc, sta, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to stop TX aggregation\n",
- __func__);
+ "Unable to stop TX aggregation\n");
ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
break;
+ case IEEE80211_AMPDU_TX_RESUME:
+ ath_tx_aggr_resume(sc, sta, tid);
+ break;
default:
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unknown AMPDU action\n", __func__);
+ DPRINTF(sc, ATH_DBG_FATAL, "Unknown AMPDU action\n");
}
return ret;
}
-static int ath9k_no_fragmentation(struct ieee80211_hw *hw, u32 value)
-{
- return -EOPNOTSUPP;
-}
-
static struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -1654,42 +2481,97 @@ static struct ieee80211_ops ath9k_ops = {
.config = ath9k_config,
.config_interface = ath9k_config_interface,
.configure_filter = ath9k_configure_filter,
- .get_stats = NULL,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
- .get_tx_stats = NULL,
.bss_info_changed = ath9k_bss_info_changed,
- .set_tim = NULL,
.set_key = ath9k_set_key,
- .hw_scan = NULL,
- .get_tkip_seq = NULL,
- .set_rts_threshold = NULL,
- .set_frag_threshold = NULL,
- .set_retry_limit = NULL,
.get_tsf = ath9k_get_tsf,
.reset_tsf = ath9k_reset_tsf,
- .tx_last_beacon = NULL,
.ampdu_action = ath9k_ampdu_action,
- .set_frag_threshold = ath9k_no_fragmentation,
};
+static struct {
+ u32 version;
+ const char * name;
+} ath_mac_bb_names[] = {
+ { AR_SREV_VERSION_5416_PCI, "5416" },
+ { AR_SREV_VERSION_5416_PCIE, "5418" },
+ { AR_SREV_VERSION_9100, "9100" },
+ { AR_SREV_VERSION_9160, "9160" },
+ { AR_SREV_VERSION_9280, "9280" },
+ { AR_SREV_VERSION_9285, "9285" }
+};
+
+static struct {
+ u16 version;
+ const char * name;
+} ath_rf_names[] = {
+ { 0, "5133" },
+ { AR_RAD5133_SREV_MAJOR, "5133" },
+ { AR_RAD5122_SREV_MAJOR, "5122" },
+ { AR_RAD2133_SREV_MAJOR, "2133" },
+ { AR_RAD2122_SREV_MAJOR, "2122" }
+};
+
+/*
+ * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
+ */
+static const char *
+ath_mac_bb_name(u32 mac_bb_version)
+{
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) {
+ if (ath_mac_bb_names[i].version == mac_bb_version) {
+ return ath_mac_bb_names[i].name;
+ }
+ }
+
+ return "????";
+}
+
+/*
+ * Return the RF name. "????" is returned if the RF is unknown.
+ */
+static const char *
+ath_rf_name(u16 rf_version)
+{
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) {
+ if (ath_rf_names[i].version == rf_version) {
+ return ath_rf_names[i].name;
+ }
+ }
+
+ return "????";
+}
+
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
void __iomem *mem;
struct ath_softc *sc;
struct ieee80211_hw *hw;
- const char *athname;
u8 csz;
u32 val;
int ret = 0;
+ struct ath_hal *ah;
if (pci_enable_device(pdev))
return -EIO;
- /* XXX 32-bit addressing only */
- if (pci_set_dma_mask(pdev, 0xffffffff)) {
- printk(KERN_ERR "ath_pci: 32-bit DMA not available\n");
- ret = -ENODEV;
+ ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+
+ if (ret) {
+ printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
+ goto bad;
+ }
+
+ ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+
+ if (ret) {
+ printk(KERN_ERR "ath9k: 32-bit DMA consistent "
+ "DMA enable failed\n");
goto bad;
}
@@ -1746,16 +2628,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto bad2;
}
- hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
- IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
-
- hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
-
SET_IEEE80211_DEV(hw, &pdev->dev);
pci_set_drvdata(pdev, hw);
@@ -1778,11 +2650,15 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto bad4;
}
- athname = ath9k_hw_probe(id->vendor, id->device);
-
- printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n",
+ ah = sc->sc_ah;
+ printk(KERN_INFO
+ "%s: Atheros AR%s MAC/BB Rev:%x "
+ "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
- athname ? athname : "Atheros ???",
+ ath_mac_bb_name(ah->ah_macVersion),
+ ah->ah_macRev,
+ ath_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
+ ah->ah_phyRev,
(unsigned long)mem, pdev->irq);
return 0;
@@ -1803,17 +2679,10 @@ static void ath_pci_remove(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
- enum ath9k_int status;
- if (pdev->irq) {
- ath9k_hw_set_interrupts(sc->sc_ah, 0);
- /* clear the ISR */
- ath9k_hw_getisr(sc->sc_ah, &status);
- sc->sc_flags |= SC_OP_INVALID;
- free_irq(pdev->irq, sc);
- }
ath_detach(sc);
-
+ if (pdev->irq)
+ free_irq(pdev->irq, sc);
pci_iounmap(pdev, sc->mem);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
@@ -1829,7 +2698,7 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-#ifdef CONFIG_RFKILL
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
@@ -1866,7 +2735,7 @@ static int ath_pci_resume(struct pci_dev *pdev)
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-#ifdef CONFIG_RFKILL
+#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/*
* check the h/w rfkill state on resume
* and start the rfkill poll timer
@@ -1896,11 +2765,24 @@ static struct pci_driver ath_pci_driver = {
static int __init init_ath_pci(void)
{
+ int error;
+
printk(KERN_INFO "%s: %s\n", dev_info, ATH_PCI_VERSION);
+ /* Register rate control algorithm */
+ error = ath_rate_control_register();
+ if (error != 0) {
+ printk(KERN_ERR
+ "Unable to register rate control algorithm: %d\n",
+ error);
+ ath_rate_control_unregister();
+ return error;
+ }
+
if (pci_register_driver(&ath_pci_driver) < 0) {
printk(KERN_ERR
"ath_pci: No devices found, driver not installed.\n");
+ ath_rate_control_unregister();
pci_unregister_driver(&ath_pci_driver);
return -ENODEV;
}
@@ -1911,7 +2793,8 @@ module_init(init_ath_pci);
static void __exit exit_ath_pci(void)
{
+ ath_rate_control_unregister();
pci_unregister_driver(&ath_pci_driver);
- printk(KERN_INFO "%s: driver unloaded\n", dev_info);
+ printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
}
module_exit(exit_ath_pci);
diff --git a/drivers/net/wireless/ath9k/phy.c b/drivers/net/wireless/ath9k/phy.c
index eb9121fdfd38..766982a8196e 100644
--- a/drivers/net/wireless/ath9k/phy.c
+++ b/drivers/net/wireless/ath9k/phy.c
@@ -52,8 +52,7 @@ ath9k_hw_set_channel(struct ath_hal *ah, struct ath9k_channel *chan)
bModeSynth = 1;
} else {
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u MHz\n", __func__,
- freq);
+ "Invalid channel %u MHz\n", freq);
return false;
}
@@ -86,7 +85,7 @@ ath9k_hw_set_channel(struct ath_hal *ah, struct ath9k_channel *chan)
aModeRefSel = ath9k_hw_reverse_bits(1, 2);
} else {
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
- "%s: invalid channel %u MHz\n", __func__, freq);
+ "Invalid channel %u MHz\n", freq);
return false;
}
@@ -215,7 +214,7 @@ ath9k_hw_set_rf_regs(struct ath_hal *ah, struct ath9k_channel *chan,
if (AR_SREV_9280_10_OR_LATER(ah))
return true;
- eepMinorRev = ath9k_hw_get_eeprom(ahp, EEP_MINOR_REV);
+ eepMinorRev = ath9k_hw_get_eeprom(ah, EEP_MINOR_REV);
RF_BANK_SETUP(ahp->ah_analogBank0Data, &ahp->ah_iniBank0, 1);
@@ -235,15 +234,15 @@ ath9k_hw_set_rf_regs(struct ath_hal *ah, struct ath9k_channel *chan,
if (eepMinorRev >= 2) {
if (IS_CHAN_2GHZ(chan)) {
- ob2GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_2);
- db2GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_2);
+ ob2GHz = ath9k_hw_get_eeprom(ah, EEP_OB_2);
+ db2GHz = ath9k_hw_get_eeprom(ah, EEP_DB_2);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
ob2GHz, 3, 197, 0);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
db2GHz, 3, 194, 0);
} else {
- ob5GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_5);
- db5GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_5);
+ ob5GHz = ath9k_hw_get_eeprom(ah, EEP_OB_5);
+ db5GHz = ath9k_hw_get_eeprom(ah, EEP_DB_5);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
ob5GHz, 3, 203, 0);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
@@ -348,8 +347,7 @@ bool ath9k_hw_init_rf(struct ath_hal *ah, int *status)
|| ahp->ah_analogBank6TPCData == NULL
|| ahp->ah_analogBank7Data == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "%s: cannot allocate RF banks\n",
- __func__);
+ "Cannot allocate RF banks\n");
*status = -ENOMEM;
return false;
}
@@ -360,8 +358,7 @@ bool ath9k_hw_init_rf(struct ath_hal *ah, int *status)
ahp->ah_iniAddac.ia_columns), GFP_KERNEL);
if (ahp->ah_addac5416_21 == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "%s: cannot allocate ah_addac5416_21\n",
- __func__);
+ "Cannot allocate ah_addac5416_21\n");
*status = -ENOMEM;
return false;
}
@@ -371,8 +368,7 @@ bool ath9k_hw_init_rf(struct ath_hal *ah, int *status)
ahp->ah_iniBank6.ia_rows), GFP_KERNEL);
if (ahp->ah_bank6Temp == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "%s: cannot allocate ah_bank6Temp\n",
- __func__);
+ "Cannot allocate ah_bank6Temp\n");
*status = -ENOMEM;
return false;
}
diff --git a/drivers/net/wireless/ath9k/phy.h b/drivers/net/wireless/ath9k/phy.h
index 14702344448b..3a406a5c0593 100644
--- a/drivers/net/wireless/ath9k/phy.h
+++ b/drivers/net/wireless/ath9k/phy.h
@@ -50,6 +50,9 @@ bool ath9k_hw_init_rf(struct ath_hal *ah,
#define AR_PHY_FC_SHORT_GI_40 0x00000080
#define AR_PHY_FC_WALSH 0x00000100
#define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200
+#define AR_PHY_FC_ENABLE_DAC_FIFO 0x00000800
+
+#define AR_PHY_TEST2 0x9808
#define AR_PHY_TIMING2 0x9810
#define AR_PHY_TIMING3 0x9814
@@ -100,6 +103,8 @@ bool ath9k_hw_init_rf(struct ath_hal *ah,
#define AR_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000FF
#define AR_PHY_RF_CTL4_FRAME_XPAA_ON_S 0
+#define AR_PHY_TSTDAC_CONST 0x983c
+
#define AR_PHY_SETTLING 0x9844
#define AR_PHY_SETTLING_SWITCH 0x00003F80
#define AR_PHY_SETTLING_SWITCH_S 7
diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c
index cca2fc5b0765..04ab457a8faa 100644
--- a/drivers/net/wireless/ath9k/rc.c
+++ b/drivers/net/wireless/ath9k/rc.c
@@ -15,143 +15,136 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * Atheros rate control algorithm
- */
-
#include "core.h"
-/* FIXME: remove this include! */
-#include "../net/mac80211/rate.h"
-
-static u32 tx_triglevel_max;
static struct ath_rate_table ar5416_11na_ratetable = {
42,
+ {0},
{
- { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
0, 2, 1, 0, 0, 0, 0, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
0, 3, 1, 1, 1, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, 24,
2, 4, 2, 2, 2, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
2, 6, 2, 3, 3, 3, 3, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, 48,
4, 10, 3, 4, 4, 4, 4, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
4, 14, 3, 5, 5, 5, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
4, 20, 3, 6, 6, 6, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
4, 23, 3, 7, 7, 7, 7, 0 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
6400, 0x80, 0x00, 0,
0, 2, 3, 8, 24, 8, 24, 3216 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
12700, 0x81, 0x00, 1,
2, 4, 3, 9, 25, 9, 25, 6434 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
18800, 0x82, 0x00, 2,
2, 6, 3, 10, 26, 10, 26, 9650 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
25000, 0x83, 0x00, 3,
4, 10, 3, 11, 27, 11, 27, 12868 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
36700, 0x84, 0x00, 4,
4, 14, 3, 12, 28, 12, 28, 19304 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
48100, 0x85, 0x00, 5,
4, 20, 3, 13, 29, 13, 29, 25740 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
53500, 0x86, 0x00, 6,
4, 23, 3, 14, 30, 14, 30, 28956 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
59000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 15, 32, 32180 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
12700, 0x88, 0x00,
8, 0, 2, 3, 16, 33, 16, 33, 6430 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
24800, 0x89, 0x00, 9,
2, 4, 3, 17, 34, 17, 34, 12860 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
36600, 0x8a, 0x00, 10,
2, 6, 3, 18, 35, 18, 35, 19300 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
48100, 0x8b, 0x00, 11,
4, 10, 3, 19, 36, 19, 36, 25736 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
69500, 0x8c, 0x00, 12,
4, 14, 3, 20, 37, 20, 37, 38600 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
89500, 0x8d, 0x00, 13,
4, 20, 3, 21, 38, 21, 38, 51472 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
98900, 0x8e, 0x00, 14,
4, 23, 3, 22, 39, 22, 39, 57890 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
108300, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 23, 41, 64320 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
13200, 0x80, 0x00, 0,
0, 2, 3, 8, 24, 24, 24, 6684 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
25900, 0x81, 0x00, 1,
2, 4, 3, 9, 25, 25, 25, 13368 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
38600, 0x82, 0x00, 2,
2, 6, 3, 10, 26, 26, 26, 20052 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
49800, 0x83, 0x00, 3,
4, 10, 3, 11, 27, 27, 27, 26738 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
72200, 0x84, 0x00, 4,
4, 14, 3, 12, 28, 28, 28, 40104 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
92900, 0x85, 0x00, 5,
4, 20, 3, 13, 29, 29, 29, 53476 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
102700, 0x86, 0x00, 6,
4, 23, 3, 14, 30, 30, 30, 60156 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
112000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 32, 32, 66840 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
122000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 32, 32, 74200 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
25800, 0x88, 0x00, 8,
0, 2, 3, 16, 33, 33, 33, 13360 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
49800, 0x89, 0x00, 9,
2, 4, 3, 17, 34, 34, 34, 26720 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
71900, 0x8a, 0x00, 10,
2, 6, 3, 18, 35, 35, 35, 40080 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
92500, 0x8b, 0x00, 11,
4, 10, 3, 19, 36, 36, 36, 53440 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
130300, 0x8c, 0x00, 12,
4, 14, 3, 20, 37, 37, 37, 80160 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
162800, 0x8d, 0x00, 13,
4, 20, 3, 21, 38, 38, 38, 106880 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
178200, 0x8e, 0x00, 14,
4, 23, 3, 22, 39, 39, 39, 120240 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
192100, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 41, 41, 133600 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
207000, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 41, 41, 148400 },
},
@@ -160,153 +153,149 @@ static struct ath_rate_table ar5416_11na_ratetable = {
WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
};
-/* TRUE_ALL - valid for 20/40/Legacy,
- * TRUE - Legacy only,
- * TRUE_20 - HT 20 only,
- * TRUE_40 - HT 40 only */
-
/* 4ms frame limit not used for NG mode. The values filled
* for HT are the 64K max aggregate limit */
static struct ath_rate_table ar5416_11ng_ratetable = {
46,
+ {0},
{
- { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 1000, /* 1 Mb */
+ { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, 2,
0, 0, 1, 0, 0, 0, 0, 0 },
- { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 2000, /* 2 Mb */
+ { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
1900, 0x1a, 0x04, 4,
1, 1, 1, 1, 1, 1, 1, 0 },
- { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
+ { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
4900, 0x19, 0x04, 11,
2, 2, 2, 2, 2, 2, 2, 0 },
- { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 11000, /* 11 Mb */
+ { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
8100, 0x18, 0x04, 22,
3, 3, 2, 3, 3, 3, 3, 0 },
- { FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
4, 2, 1, 4, 4, 4, 4, 0 },
- { FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
4, 3, 1, 5, 5, 5, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10100, 0x0a, 0x00, 24,
6, 4, 1, 6, 6, 6, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
14100, 0x0e, 0x00, 36,
6, 6, 2, 7, 7, 7, 7, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17700, 0x09, 0x00, 48,
8, 10, 3, 8, 8, 8, 8, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23700, 0x0d, 0x00, 72,
8, 14, 3, 9, 9, 9, 9, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
8, 20, 3, 10, 10, 10, 10, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
30900, 0x0c, 0x00, 108,
8, 23, 3, 11, 11, 11, 11, 0 },
- { FALSE, FALSE, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
6400, 0x80, 0x00, 0,
4, 2, 3, 12, 28, 12, 28, 3216 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
12700, 0x81, 0x00, 1,
6, 4, 3, 13, 29, 13, 29, 6434 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
18800, 0x82, 0x00, 2,
6, 6, 3, 14, 30, 14, 30, 9650 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
25000, 0x83, 0x00, 3,
8, 10, 3, 15, 31, 15, 31, 12868 },
- { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */
+ { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
36700, 0x84, 0x00, 4,
8, 14, 3, 16, 32, 16, 32, 19304 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
48100, 0x85, 0x00, 5,
8, 20, 3, 17, 33, 17, 33, 25740 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
53500, 0x86, 0x00, 6,
8, 23, 3, 18, 34, 18, 34, 28956 },
- { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */
+ { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
59000, 0x87, 0x00, 7,
8, 25, 3, 19, 35, 19, 36, 32180 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
12700, 0x88, 0x00, 8,
4, 2, 3, 20, 37, 20, 37, 6430 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
24800, 0x89, 0x00, 9,
6, 4, 3, 21, 38, 21, 38, 12860 },
- { FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
36600, 0x8a, 0x00, 10,
6, 6, 3, 22, 39, 22, 39, 19300 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
48100, 0x8b, 0x00, 11,
8, 10, 3, 23, 40, 23, 40, 25736 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
69500, 0x8c, 0x00, 12,
8, 14, 3, 24, 41, 24, 41, 38600 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
89500, 0x8d, 0x00, 13,
8, 20, 3, 25, 42, 25, 42, 51472 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
98900, 0x8e, 0x00, 14,
8, 23, 3, 26, 43, 26, 44, 57890 },
- { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */
+ { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
108300, 0x8f, 0x00, 15,
8, 25, 3, 27, 44, 27, 45, 64320 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
13200, 0x80, 0x00, 0,
8, 2, 3, 12, 28, 28, 28, 6684 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
25900, 0x81, 0x00, 1,
8, 4, 3, 13, 29, 29, 29, 13368 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
38600, 0x82, 0x00, 2,
8, 6, 3, 14, 30, 30, 30, 20052 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
49800, 0x83, 0x00, 3,
8, 10, 3, 15, 31, 31, 31, 26738 },
- { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */
+ { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
72200, 0x84, 0x00, 4,
8, 14, 3, 16, 32, 32, 32, 40104 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
92900, 0x85, 0x00, 5,
8, 20, 3, 17, 33, 33, 33, 53476 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
102700, 0x86, 0x00, 6,
8, 23, 3, 18, 34, 34, 34, 60156 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
112000, 0x87, 0x00, 7,
8, 23, 3, 19, 35, 36, 36, 66840 },
- { FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
+ { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
122000, 0x87, 0x00, 7,
8, 25, 3, 19, 35, 36, 36, 74200 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
25800, 0x88, 0x00, 8,
8, 2, 3, 20, 37, 37, 37, 13360 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
49800, 0x89, 0x00, 9,
8, 4, 3, 21, 38, 38, 38, 26720 },
- { FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
71900, 0x8a, 0x00, 10,
8, 6, 3, 22, 39, 39, 39, 40080 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
92500, 0x8b, 0x00, 11,
8, 10, 3, 23, 40, 40, 40, 53440 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
130300, 0x8c, 0x00, 12,
8, 14, 3, 24, 41, 41, 41, 80160 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
162800, 0x8d, 0x00, 13,
8, 20, 3, 25, 42, 42, 42, 106880 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
178200, 0x8e, 0x00, 14,
8, 23, 3, 26, 43, 43, 43, 120240 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
192100, 0x8f, 0x00, 15,
8, 23, 3, 27, 44, 45, 45, 133600 },
- { TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
+ { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
207000, 0x8f, 0x00, 15,
8, 25, 3, 27, 44, 45, 45, 148400 },
},
@@ -317,29 +306,30 @@ static struct ath_rate_table ar5416_11ng_ratetable = {
static struct ath_rate_table ar5416_11a_ratetable = {
8,
+ {0},
{
- { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, (0x80|12),
0, 2, 1, 0, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
0, 3, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, (0x80|24),
2, 4, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
2, 6, 2, 3, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, (0x80|48),
4, 10, 3, 4, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
4, 14, 3, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
4, 19, 3, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
4, 23, 3, 7, 0 },
},
@@ -348,109 +338,44 @@ static struct ath_rate_table ar5416_11a_ratetable = {
0, /* Phy rates allowed initially */
};
-static struct ath_rate_table ar5416_11a_ratetable_Half = {
- 8,
- {
- { TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 6 Mb */
- 2700, 0x0b, 0x00, (0x80|6),
- 0, 2, 1, 0, 0},
- { TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 9 Mb */
- 3900, 0x0f, 0x00, 9,
- 0, 3, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 12 Mb */
- 5000, 0x0a, 0x00, (0x80|12),
- 2, 4, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 18 Mb */
- 6950, 0x0e, 0x00, 18,
- 2, 6, 2, 3, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 24 Mb */
- 8650, 0x09, 0x00, (0x80|24),
- 4, 10, 3, 4, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 36 Mb */
- 11500, 0x0d, 0x00, 36,
- 4, 14, 3, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 48 Mb */
- 13700, 0x08, 0x00, 48,
- 4, 19, 3, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 27000, /* 54 Mb */
- 14650, 0x0c, 0x00, 54,
- 4, 23, 3, 7, 0 },
- },
- 50, /* probe interval */
- 50, /* rssi reduce interval */
- 0, /* Phy rates allowed initially */
-};
-
-static struct ath_rate_table ar5416_11a_ratetable_Quarter = {
- 8,
- {
- { TRUE, TRUE, WLAN_PHY_OFDM, 1500, /* 6 Mb */
- 1350, 0x0b, 0x00, (0x80|3),
- 0, 2, 1, 0, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 2250, /* 9 Mb */
- 1950, 0x0f, 0x00, 4,
- 0, 3, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 12 Mb */
- 2500, 0x0a, 0x00, (0x80|6),
- 2, 4, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 18 Mb */
- 3475, 0x0e, 0x00, 9,
- 2, 6, 2, 3, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 25 Mb */
- 4325, 0x09, 0x00, (0x80|12),
- 4, 10, 3, 4, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 36 Mb */
- 5750, 0x0d, 0x00, 18,
- 4, 14, 3, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 48 Mb */
- 6850, 0x08, 0x00, 24,
- 4, 19, 3, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 13500, /* 54 Mb */
- 7325, 0x0c, 0x00, 27,
- 4, 23, 3, 7, 0 },
- },
- 50, /* probe interval */
- 50, /* rssi reduce interval */
- 0, /* Phy rates allowed initially */
-};
-
static struct ath_rate_table ar5416_11g_ratetable = {
12,
+ {0},
{
- { TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, 2,
0, 0, 1, 0, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
1900, 0x1a, 0x04, 4,
1, 1, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
4900, 0x19, 0x04, 11,
2, 2, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
8100, 0x18, 0x04, 22,
3, 3, 2, 3, 0 },
- { FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
4, 2, 1, 4, 0 },
- { FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
+ { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
4, 3, 1, 5, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, 24,
6, 4, 1, 6, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
6, 6, 2, 7, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, 48,
8, 10, 3, 8, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
8, 14, 3, 9, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
8, 19, 3, 10, 0 },
- { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
+ { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
8, 23, 3, 11, 0 },
},
@@ -461,17 +386,18 @@ static struct ath_rate_table ar5416_11g_ratetable = {
static struct ath_rate_table ar5416_11b_ratetable = {
4,
+ {0},
{
- { TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, (0x80|2),
0, 0, 1, 0, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
1800, 0x1a, 0x04, (0x80|4),
1, 1, 1, 1, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
4300, 0x19, 0x04, (0x80|11),
1, 2, 2, 2, 0 },
- { TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */
+ { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
7100, 0x18, 0x04, (0x80|22),
1, 4, 100, 3, 0 },
},
@@ -480,48 +406,6 @@ static struct ath_rate_table ar5416_11b_ratetable = {
0, /* Phy rates allowed initially */
};
-static void ar5416_attach_ratetables(struct ath_rate_softc *sc)
-{
- /*
- * Attach rate tables.
- */
- sc->hw_rate_table[ATH9K_MODE_11B] = &ar5416_11b_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11G] = &ar5416_11g_ratetable;
-
- sc->hw_rate_table[ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] =
- &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] =
- &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] =
- &ar5416_11ng_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] =
- &ar5416_11ng_ratetable;
-}
-
-static void ar5416_setquarter_ratetable(struct ath_rate_softc *sc)
-{
- sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Quarter;
- return;
-}
-
-static void ar5416_sethalf_ratetable(struct ath_rate_softc *sc)
-{
- sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Half;
- return;
-}
-
-static void ar5416_setfull_ratetable(struct ath_rate_softc *sc)
-{
- sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable;
- return;
-}
-
-/*
- * Return the median of three numbers
- */
static inline int8_t median(int8_t a, int8_t b, int8_t c)
{
if (a >= b) {
@@ -541,68 +425,65 @@ static inline int8_t median(int8_t a, int8_t b, int8_t c)
}
}
-static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table,
- struct ath_tx_ratectrl *rate_ctrl)
+static void ath_rc_sort_validrates(struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv)
{
u8 i, j, idx, idx_next;
- for (i = rate_ctrl->max_valid_rate - 1; i > 0; i--) {
+ for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) {
for (j = 0; j <= i-1; j++) {
- idx = rate_ctrl->valid_rate_index[j];
- idx_next = rate_ctrl->valid_rate_index[j+1];
+ idx = ath_rc_priv->valid_rate_index[j];
+ idx_next = ath_rc_priv->valid_rate_index[j+1];
if (rate_table->info[idx].ratekbps >
rate_table->info[idx_next].ratekbps) {
- rate_ctrl->valid_rate_index[j] = idx_next;
- rate_ctrl->valid_rate_index[j+1] = idx;
+ ath_rc_priv->valid_rate_index[j] = idx_next;
+ ath_rc_priv->valid_rate_index[j+1] = idx;
}
}
}
}
-/* Access functions for valid_txrate_mask */
-
-static void ath_rc_init_valid_txmask(struct ath_tx_ratectrl *rate_ctrl)
+static void ath_rc_init_valid_txmask(struct ath_rate_priv *ath_rc_priv)
{
u8 i;
- for (i = 0; i < rate_ctrl->rate_table_size; i++)
- rate_ctrl->valid_rate_index[i] = FALSE;
+ for (i = 0; i < ath_rc_priv->rate_table_size; i++)
+ ath_rc_priv->valid_rate_index[i] = 0;
}
-static inline void ath_rc_set_valid_txmask(struct ath_tx_ratectrl *rate_ctrl,
+static inline void ath_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv,
u8 index, int valid_tx_rate)
{
- ASSERT(index <= rate_ctrl->rate_table_size);
- rate_ctrl->valid_rate_index[index] = valid_tx_rate ? TRUE : FALSE;
+ ASSERT(index <= ath_rc_priv->rate_table_size);
+ ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0;
}
-static inline int ath_rc_isvalid_txmask(struct ath_tx_ratectrl *rate_ctrl,
+static inline int ath_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv,
u8 index)
{
- ASSERT(index <= rate_ctrl->rate_table_size);
- return rate_ctrl->valid_rate_index[index];
+ ASSERT(index <= ath_rc_priv->rate_table_size);
+ return ath_rc_priv->valid_rate_index[index];
}
-/* Iterators for valid_txrate_mask */
-static inline int
-ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table,
- struct ath_tx_ratectrl *rate_ctrl,
- u8 cur_valid_txrate,
- u8 *next_idx)
+static inline int ath_rc_get_nextvalid_txrate(struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv,
+ u8 cur_valid_txrate,
+ u8 *next_idx)
{
u8 i;
- for (i = 0; i < rate_ctrl->max_valid_rate - 1; i++) {
- if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) {
- *next_idx = rate_ctrl->valid_rate_index[i+1];
- return TRUE;
+ for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) {
+ if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
+ *next_idx = ath_rc_priv->valid_rate_index[i+1];
+ return 1;
}
}
/* No more valid rates */
*next_idx = 0;
- return FALSE;
+
+ return 0;
}
/* Return true only for single stream */
@@ -610,83 +491,72 @@ ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table,
static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw)
{
if (WLAN_RC_PHY_HT(phy) & !(capflag & WLAN_RC_HT_FLAG))
- return FALSE;
+ return 0;
if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))
- return FALSE;
+ return 0;
if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG))
- return FALSE;
+ return 0;
if (!ignore_cw && WLAN_RC_PHY_HT(phy))
if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG))
- return FALSE;
+ return 0;
if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG))
- return FALSE;
- return TRUE;
+ return 0;
+ return 1;
}
static inline int
-ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table,
- struct ath_tx_ratectrl *rate_ctrl,
+ath_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv,
u8 cur_valid_txrate, u8 *next_idx)
{
int8_t i;
- for (i = 1; i < rate_ctrl->max_valid_rate ; i++) {
- if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) {
- *next_idx = rate_ctrl->valid_rate_index[i-1];
- return TRUE;
+ for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) {
+ if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) {
+ *next_idx = ath_rc_priv->valid_rate_index[i-1];
+ return 1;
}
}
- return FALSE;
+
+ return 0;
}
-/*
- * Initialize the Valid Rate Index from valid entries in Rate Table
- */
-static u8
-ath_rc_sib_init_validrates(struct ath_rate_node *ath_rc_priv,
- const struct ath_rate_table *rate_table,
- u32 capflag)
+static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv,
+ struct ath_rate_table *rate_table,
+ u32 capflag)
{
- struct ath_tx_ratectrl *rate_ctrl;
u8 i, hi = 0;
u32 valid;
- rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
for (i = 0; i < rate_table->rate_cnt; i++) {
valid = (ath_rc_priv->single_stream ?
rate_table->info[i].valid_single_stream :
rate_table->info[i].valid);
- if (valid == TRUE) {
+ if (valid == 1) {
u32 phy = rate_table->info[i].phy;
u8 valid_rate_count = 0;
- if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
+ if (!ath_rc_valid_phyrate(phy, capflag, 0))
continue;
- valid_rate_count = rate_ctrl->valid_phy_ratecnt[phy];
+ valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy];
- rate_ctrl->valid_phy_rateidx[phy][valid_rate_count] = i;
- rate_ctrl->valid_phy_ratecnt[phy] += 1;
- ath_rc_set_valid_txmask(rate_ctrl, i, TRUE);
+ ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i;
+ ath_rc_priv->valid_phy_ratecnt[phy] += 1;
+ ath_rc_set_valid_txmask(ath_rc_priv, i, 1);
hi = A_MAX(hi, i);
}
}
+
return hi;
}
-/*
- * Initialize the Valid Rate Index from Rate Set
- */
-static u8
-ath_rc_sib_setvalid_rates(struct ath_rate_node *ath_rc_priv,
- const struct ath_rate_table *rate_table,
- struct ath_rateset *rateset,
- u32 capflag)
+static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv,
+ struct ath_rate_table *rate_table,
+ struct ath_rateset *rateset,
+ u32 capflag)
{
- /* XXX: Clean me up and make identation friendly */
u8 i, j, hi = 0;
- struct ath_tx_ratectrl *rate_ctrl =
- (struct ath_tx_ratectrl *)(ath_rc_priv);
/* Use intersection of working rates and valid rates */
for (i = 0; i < rateset->rs_nrates; i++) {
@@ -695,196 +565,89 @@ ath_rc_sib_setvalid_rates(struct ath_rate_node *ath_rc_priv,
u32 valid = (ath_rc_priv->single_stream ?
rate_table->info[j].valid_single_stream :
rate_table->info[j].valid);
+ u8 rate = rateset->rs_rates[i];
+ u8 dot11rate = rate_table->info[j].dot11rate;
/* We allow a rate only if its valid and the
* capflag matches one of the validity
- * (TRUE/TRUE_20/TRUE_40) flags */
-
- /* XXX: catch the negative of this branch
- * first and then continue */
- if (((rateset->rs_rates[i] & 0x7F) ==
- (rate_table->info[j].dot11rate & 0x7F)) &&
- ((valid & WLAN_RC_CAP_MODE(capflag)) ==
- WLAN_RC_CAP_MODE(capflag)) &&
- !WLAN_RC_PHY_HT(phy)) {
+ * (VALID/VALID_20/VALID_40) flags */
+ if (((rate & 0x7F) == (dot11rate & 0x7F)) &&
+ ((valid & WLAN_RC_CAP_MODE(capflag)) ==
+ WLAN_RC_CAP_MODE(capflag)) &&
+ !WLAN_RC_PHY_HT(phy)) {
u8 valid_rate_count = 0;
- if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
+ if (!ath_rc_valid_phyrate(phy, capflag, 0))
continue;
valid_rate_count =
- rate_ctrl->valid_phy_ratecnt[phy];
+ ath_rc_priv->valid_phy_ratecnt[phy];
- rate_ctrl->valid_phy_rateidx[phy]
+ ath_rc_priv->valid_phy_rateidx[phy]
[valid_rate_count] = j;
- rate_ctrl->valid_phy_ratecnt[phy] += 1;
- ath_rc_set_valid_txmask(rate_ctrl, j, TRUE);
+ ath_rc_priv->valid_phy_ratecnt[phy] += 1;
+ ath_rc_set_valid_txmask(ath_rc_priv, j, 1);
hi = A_MAX(hi, j);
}
}
}
+
return hi;
}
-static u8
-ath_rc_sib_setvalid_htrates(struct ath_rate_node *ath_rc_priv,
- const struct ath_rate_table *rate_table,
- u8 *mcs_set, u32 capflag)
+static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
+ struct ath_rate_table *rate_table,
+ u8 *mcs_set, u32 capflag)
{
+ struct ath_rateset *rateset = (struct ath_rateset *)mcs_set;
+
u8 i, j, hi = 0;
- struct ath_tx_ratectrl *rate_ctrl =
- (struct ath_tx_ratectrl *)(ath_rc_priv);
/* Use intersection of working rates and valid rates */
- for (i = 0; i < ((struct ath_rateset *)mcs_set)->rs_nrates; i++) {
+ for (i = 0; i < rateset->rs_nrates; i++) {
for (j = 0; j < rate_table->rate_cnt; j++) {
u32 phy = rate_table->info[j].phy;
u32 valid = (ath_rc_priv->single_stream ?
rate_table->info[j].valid_single_stream :
rate_table->info[j].valid);
+ u8 rate = rateset->rs_rates[i];
+ u8 dot11rate = rate_table->info[j].dot11rate;
- if (((((struct ath_rateset *)
- mcs_set)->rs_rates[i] & 0x7F) !=
- (rate_table->info[j].dot11rate & 0x7F)) ||
+ if (((rate & 0x7F) != (dot11rate & 0x7F)) ||
!WLAN_RC_PHY_HT(phy) ||
!WLAN_RC_PHY_HT_VALID(valid, capflag))
continue;
- if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
+ if (!ath_rc_valid_phyrate(phy, capflag, 0))
continue;
- rate_ctrl->valid_phy_rateidx[phy]
- [rate_ctrl->valid_phy_ratecnt[phy]] = j;
- rate_ctrl->valid_phy_ratecnt[phy] += 1;
- ath_rc_set_valid_txmask(rate_ctrl, j, TRUE);
+ ath_rc_priv->valid_phy_rateidx[phy]
+ [ath_rc_priv->valid_phy_ratecnt[phy]] = j;
+ ath_rc_priv->valid_phy_ratecnt[phy] += 1;
+ ath_rc_set_valid_txmask(ath_rc_priv, j, 1);
hi = A_MAX(hi, j);
}
}
- return hi;
-}
-
-/*
- * Attach to a device instance. Setup the public definition
- * of how much per-node space we need and setup the private
- * phy tables that have rate control parameters.
- */
-struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah)
-{
- struct ath_rate_softc *asc;
-
- /* we are only in user context so we can sleep for memory */
- asc = kzalloc(sizeof(struct ath_rate_softc), GFP_KERNEL);
- if (asc == NULL)
- return NULL;
-
- ar5416_attach_ratetables(asc);
-
- /* Save Maximum TX Trigger Level (used for 11n) */
- tx_triglevel_max = ah->ah_caps.tx_triglevel_max;
- /* return alias for ath_rate_softc * */
- return asc;
-}
-
-static struct ath_rate_node *ath_rate_node_alloc(struct ath_vap *avp,
- struct ath_rate_softc *rsc,
- gfp_t gfp)
-{
- struct ath_rate_node *anode;
-
- anode = kzalloc(sizeof(struct ath_rate_node), gfp);
- if (anode == NULL)
- return NULL;
-
- anode->avp = avp;
- anode->asc = rsc;
- avp->rc_node = anode;
-
- return anode;
-}
-
-static void ath_rate_node_free(struct ath_rate_node *anode)
-{
- if (anode != NULL)
- kfree(anode);
-}
-
-void ath_rate_detach(struct ath_rate_softc *asc)
-{
- if (asc != NULL)
- kfree(asc);
-}
-
-u8 ath_rate_findrateix(struct ath_softc *sc,
- u8 dot11rate)
-{
- const struct ath_rate_table *ratetable;
- struct ath_rate_softc *rsc = sc->sc_rc;
- int i;
-
- ratetable = rsc->hw_rate_table[sc->sc_curmode];
-
- if (WARN_ON(!ratetable))
- return 0;
-
- for (i = 0; i < ratetable->rate_cnt; i++) {
- if ((ratetable->info[i].dot11rate & 0x7f) == (dot11rate & 0x7f))
- return i;
- }
- return 0;
-}
-
-/*
- * Update rate-control state on a device state change. When
- * operating as a station this includes associate/reassociate
- * with an AP. Otherwise this gets called, for example, when
- * the we transition to run state when operating as an AP.
- */
-void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp)
-{
- struct ath_rate_softc *asc = sc->sc_rc;
-
- /* For half and quarter rate channles use different
- * rate tables
- */
- if (sc->sc_ah->ah_curchan->channelFlags & CHANNEL_HALF)
- ar5416_sethalf_ratetable(asc);
- else if (sc->sc_ah->ah_curchan->channelFlags & CHANNEL_QUARTER)
- ar5416_setquarter_ratetable(asc);
- else /* full rate */
- ar5416_setfull_ratetable(asc);
-
- if (avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE) {
- asc->fixedrix =
- sc->sc_rixmap[avp->av_config.av_fixed_rateset & 0xff];
- /* NB: check the fixed rate exists */
- if (asc->fixedrix == 0xff)
- asc->fixedrix = IEEE80211_FIXED_RATE_NONE;
- } else {
- asc->fixedrix = IEEE80211_FIXED_RATE_NONE;
- }
+ return hi;
}
static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- const struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ath_rate_table *rate_table,
int probe_allowed, int *is_probing,
int is_retry)
{
u32 dt, best_thruput, this_thruput, now_msec;
u8 rate, next_rate, best_rate, maxindex, minindex;
int8_t rssi_last, rssi_reduce = 0, index = 0;
- struct ath_tx_ratectrl *rate_ctrl = NULL;
-
- rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv ?
- (ath_rc_priv) : NULL);
- *is_probing = FALSE;
+ *is_probing = 0;
- rssi_last = median(rate_ctrl->rssi_last,
- rate_ctrl->rssi_last_prev,
- rate_ctrl->rssi_last_prev2);
+ rssi_last = median(ath_rc_priv->rssi_last,
+ ath_rc_priv->rssi_last_prev,
+ ath_rc_priv->rssi_last_prev2);
/*
* Age (reduce) last ack rssi based on how old it is.
@@ -896,7 +659,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
*/
now_msec = jiffies_to_msecs(jiffies);
- dt = now_msec - rate_ctrl->rssi_time;
+ dt = now_msec - ath_rc_priv->rssi_time;
if (dt >= 185)
rssi_reduce = 10;
@@ -915,7 +678,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
*/
best_thruput = 0;
- maxindex = rate_ctrl->max_valid_rate-1;
+ maxindex = ath_rc_priv->max_valid_rate-1;
minindex = 0;
best_rate = minindex;
@@ -927,8 +690,8 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
for (index = maxindex; index >= minindex ; index--) {
u8 per_thres;
- rate = rate_ctrl->valid_rate_index[index];
- if (rate > rate_ctrl->rate_max_phy)
+ rate = ath_rc_priv->valid_rate_index[index];
+ if (rate > ath_rc_priv->rate_max_phy)
continue;
/*
@@ -942,7 +705,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
* 10-15 and we would be worse off then staying
* at the current rate.
*/
- per_thres = rate_ctrl->state[rate].per;
+ per_thres = ath_rc_priv->state[rate].per;
if (per_thres < 12)
per_thres = 12;
@@ -961,41 +724,35 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
* of max retries, use the min rate for the next retry
*/
if (is_retry)
- rate = rate_ctrl->valid_rate_index[minindex];
+ rate = ath_rc_priv->valid_rate_index[minindex];
- rate_ctrl->rssi_last_lookup = rssi_last;
+ ath_rc_priv->rssi_last_lookup = rssi_last;
/*
* Must check the actual rate (ratekbps) to account for
* non-monoticity of 11g's rate table
*/
- if (rate >= rate_ctrl->rate_max_phy && probe_allowed) {
- rate = rate_ctrl->rate_max_phy;
+ if (rate >= ath_rc_priv->rate_max_phy && probe_allowed) {
+ rate = ath_rc_priv->rate_max_phy;
/* Probe the next allowed phy state */
/* FIXME:XXXX Check to make sure ratMax is checked properly */
if (ath_rc_get_nextvalid_txrate(rate_table,
- rate_ctrl, rate, &next_rate) &&
- (now_msec - rate_ctrl->probe_time >
+ ath_rc_priv, rate, &next_rate) &&
+ (now_msec - ath_rc_priv->probe_time >
rate_table->probe_interval) &&
- (rate_ctrl->hw_maxretry_pktcnt >= 1)) {
+ (ath_rc_priv->hw_maxretry_pktcnt >= 1)) {
rate = next_rate;
- rate_ctrl->probe_rate = rate;
- rate_ctrl->probe_time = now_msec;
- rate_ctrl->hw_maxretry_pktcnt = 0;
- *is_probing = TRUE;
+ ath_rc_priv->probe_rate = rate;
+ ath_rc_priv->probe_time = now_msec;
+ ath_rc_priv->hw_maxretry_pktcnt = 0;
+ *is_probing = 1;
}
}
- /*
- * Make sure rate is not higher than the allowed maximum.
- * We should also enforce the min, but I suspect the min is
- * normally 1 rather than 0 because of the rate 9 vs 6 issue
- * in the old code.
- */
- if (rate > (rate_ctrl->rate_table_size - 1))
- rate = rate_ctrl->rate_table_size - 1;
+ if (rate > (ath_rc_priv->rate_table_size - 1))
+ rate = ath_rc_priv->rate_table_size - 1;
ASSERT((rate_table->info[rate].valid && !ath_rc_priv->single_stream) ||
(rate_table->info[rate].valid_single_stream &&
@@ -1004,40 +761,36 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
return rate;
}
-static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table ,
- struct ath_rc_series *series,
- u8 tries,
- u8 rix,
- int rtsctsenable)
+static void ath_rc_rate_set_series(struct ath_rate_table *rate_table ,
+ struct ieee80211_tx_rate *rate,
+ u8 tries, u8 rix, int rtsctsenable)
{
- series->tries = tries;
- series->flags = (rtsctsenable ? ATH_RC_RTSCTS_FLAG : 0) |
- (WLAN_RC_PHY_DS(rate_table->info[rix].phy) ?
- ATH_RC_DS_FLAG : 0) |
- (WLAN_RC_PHY_40(rate_table->info[rix].phy) ?
- ATH_RC_CW40_FLAG : 0) |
- (WLAN_RC_PHY_SGI(rate_table->info[rix].phy) ?
- ATH_RC_SGI_FLAG : 0);
-
- series->rix = rate_table->info[rix].base_index;
- series->max_4ms_framelen = rate_table->info[rix].max_4ms_framelen;
+ rate->count = tries;
+ rate->idx = rix;
+
+ if (rtsctsenable)
+ rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
+ if (WLAN_RC_PHY_40(rate_table->info[rix].phy))
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
+ rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+ if (WLAN_RC_PHY_HT(rate_table->info[rix].phy))
+ rate->flags |= IEEE80211_TX_RC_MCS;
}
static u8 ath_rc_rate_getidx(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- const struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ath_rate_table *rate_table,
u8 rix, u16 stepdown,
u16 min_rate)
{
u32 j;
u8 nextindex;
- struct ath_tx_ratectrl *rate_ctrl =
- (struct ath_tx_ratectrl *)(ath_rc_priv);
if (min_rate) {
for (j = RATE_TABLE_SIZE; j > 0; j--) {
if (ath_rc_get_nextlowervalid_txrate(rate_table,
- rate_ctrl, rix, &nextindex))
+ ath_rc_priv, rix, &nextindex))
rix = nextindex;
else
break;
@@ -1045,7 +798,7 @@ static u8 ath_rc_rate_getidx(struct ath_softc *sc,
} else {
for (j = stepdown; j > 0; j--) {
if (ath_rc_get_nextlowervalid_txrate(rate_table,
- rate_ctrl, rix, &nextindex))
+ ath_rc_priv, rix, &nextindex))
rix = nextindex;
else
break;
@@ -1055,41 +808,39 @@ static u8 ath_rc_rate_getidx(struct ath_softc *sc,
}
static void ath_rc_ratefind(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- int num_tries, int num_rates, unsigned int rcflag,
- struct ath_rc_series series[], int *is_probe,
+ struct ath_rate_priv *ath_rc_priv,
+ int num_tries, int num_rates,
+ struct ieee80211_tx_info *tx_info, int *is_probe,
int is_retry)
{
u8 try_per_rate = 0, i = 0, rix, nrix;
- struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
struct ath_rate_table *rate_table;
+ struct ieee80211_tx_rate *rates = tx_info->control.rates;
- rate_table =
- (struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode];
- rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table,
- (rcflag & ATH_RC_PROBE_ALLOWED) ? 1 : 0,
+ rate_table = sc->cur_rate_table;
+ rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table, 1,
is_probe, is_retry);
nrix = rix;
- if ((rcflag & ATH_RC_PROBE_ALLOWED) && (*is_probe)) {
+ if (*is_probe) {
/* set one try for probe rates. For the
* probes don't enable rts */
ath_rc_rate_set_series(rate_table,
- &series[i++], 1, nrix, FALSE);
+ &rates[i++], 1, nrix, 0);
try_per_rate = (num_tries/num_rates);
/* Get the next tried/allowed rate. No RTS for the next series
* after the probe rate
*/
nrix = ath_rc_rate_getidx(sc,
- ath_rc_priv, rate_table, nrix, 1, FALSE);
+ ath_rc_priv, rate_table, nrix, 1, 0);
ath_rc_rate_set_series(rate_table,
- &series[i++], try_per_rate, nrix, 0);
+ &rates[i++], try_per_rate, nrix, 0);
} else {
try_per_rate = (num_tries/num_rates);
/* Set the choosen rate. No RTS for first series entry. */
ath_rc_rate_set_series(rate_table,
- &series[i++], try_per_rate, nrix, FALSE);
+ &rates[i++], try_per_rate, nrix, 0);
}
/* Fill in the other rates for multirate retry */
@@ -1099,14 +850,13 @@ static void ath_rc_ratefind(struct ath_softc *sc,
try_num = ((i + 1) == num_rates) ?
num_tries - (try_per_rate * i) : try_per_rate ;
- min_rate = (((i + 1) == num_rates) &&
- (rcflag & ATH_RC_MINRATE_LASTRATE)) ? 1 : 0;
+ min_rate = (((i + 1) == num_rates) && 0);
nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
rate_table, nrix, 1, min_rate);
/* All other rates in the series have RTS enabled */
ath_rc_rate_set_series(rate_table,
- &series[i], try_num, nrix, TRUE);
+ &rates[i], try_num, nrix, 1);
}
/*
@@ -1124,115 +874,29 @@ static void ath_rc_ratefind(struct ath_softc *sc,
* So, set fourth rate in series to be same as third one for
* above conditions.
*/
- if ((sc->sc_curmode == ATH9K_MODE_11NG_HT20) ||
- (sc->sc_curmode == ATH9K_MODE_11NG_HT40PLUS) ||
- (sc->sc_curmode == ATH9K_MODE_11NG_HT40MINUS)) {
- u8 dot11rate = rate_table->info[rix].dot11rate;
+ if ((sc->hw->conf.channel->band == IEEE80211_BAND_2GHZ) &&
+ (sc->hw->conf.ht.enabled)) {
+ u8 dot11rate = rate_table->info[rix].dot11rate;
u8 phy = rate_table->info[rix].phy;
if (i == 4 &&
((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
(dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
- series[3].rix = series[2].rix;
- series[3].flags = series[2].flags;
- series[3].max_4ms_framelen = series[2].max_4ms_framelen;
- }
- }
-}
-
-/*
- * Return the Tx rate series.
- */
-static void ath_rate_findrate(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- int num_tries,
- int num_rates,
- unsigned int rcflag,
- struct ath_rc_series series[],
- int *is_probe,
- int is_retry)
-{
- struct ath_vap *avp = ath_rc_priv->avp;
-
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
-
- if (!num_rates || !num_tries)
- return;
-
- if (avp->av_config.av_fixed_rateset == IEEE80211_FIXED_RATE_NONE) {
- ath_rc_ratefind(sc, ath_rc_priv, num_tries, num_rates,
- rcflag, series, is_probe, is_retry);
- } else {
- /* Fixed rate */
- int idx;
- u8 flags;
- u32 rix;
- struct ath_rate_softc *asc = ath_rc_priv->asc;
- struct ath_rate_table *rate_table;
-
- rate_table = (struct ath_rate_table *)
- asc->hw_rate_table[sc->sc_curmode];
-
- for (idx = 0; idx < 4; idx++) {
- unsigned int mcs;
- u8 series_rix = 0;
-
- series[idx].tries = IEEE80211_RATE_IDX_ENTRY(
- avp->av_config.av_fixed_retryset, idx);
-
- mcs = IEEE80211_RATE_IDX_ENTRY(
- avp->av_config.av_fixed_rateset, idx);
-
- if (idx == 3 && (mcs & 0xf0) == 0x70)
- mcs = (mcs & ~0xf0)|0x80;
-
- if (!(mcs & 0x80))
- flags = 0;
- else
- flags = ((ath_rc_priv->ht_cap &
- WLAN_RC_DS_FLAG) ?
- ATH_RC_DS_FLAG : 0) |
- ((ath_rc_priv->ht_cap &
- WLAN_RC_40_FLAG) ?
- ATH_RC_CW40_FLAG : 0) |
- ((ath_rc_priv->ht_cap &
- WLAN_RC_SGI_FLAG) ?
- ((ath_rc_priv->ht_cap &
- WLAN_RC_40_FLAG) ?
- ATH_RC_SGI_FLAG : 0) : 0);
-
- series[idx].rix = sc->sc_rixmap[mcs];
- series_rix = series[idx].rix;
-
- /* XXX: Give me some cleanup love */
- if ((flags & ATH_RC_CW40_FLAG) &&
- (flags & ATH_RC_SGI_FLAG))
- rix = rate_table->info[series_rix].ht_index;
- else if (flags & ATH_RC_SGI_FLAG)
- rix = rate_table->info[series_rix].sgi_index;
- else if (flags & ATH_RC_CW40_FLAG)
- rix = rate_table->info[series_rix].cw40index;
- else
- rix = rate_table->info[series_rix].base_index;
- series[idx].max_4ms_framelen =
- rate_table->info[rix].max_4ms_framelen;
- series[idx].flags = flags;
+ rates[3].idx = rates[2].idx;
+ rates[3].flags = rates[2].flags;
}
}
}
-static void ath_rc_update_ht(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- struct ath_tx_info_priv *info_priv,
- int tx_rate, int xretries, int retries)
+static bool ath_rc_update_per(struct ath_softc *sc,
+ struct ath_rate_table *rate_table,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ath_tx_info_priv *tx_info_priv,
+ int tx_rate, int xretries, int retries,
+ u32 now_msec)
{
- struct ath_tx_ratectrl *rate_ctrl;
- u32 now_msec = jiffies_to_msecs(jiffies);
- int state_change = FALSE, rate, count;
+ bool state_change = false;
+ int count;
u8 last_per;
- struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
- struct ath_rate_table *rate_table =
- (struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode];
-
static u32 nretry_to_per_lookup[10] = {
100 * 0 / 1,
100 * 1 / 4,
@@ -1246,56 +910,35 @@ static void ath_rc_update_ht(struct ath_softc *sc,
100 * 9 / 10
};
- if (!ath_rc_priv)
- return;
-
- rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
-
- ASSERT(tx_rate >= 0);
- if (tx_rate < 0)
- return;
-
- /* To compensate for some imbalance between ctrl and ext. channel */
-
- if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy))
- info_priv->tx.ts_rssi =
- info_priv->tx.ts_rssi < 3 ? 0 :
- info_priv->tx.ts_rssi - 3;
-
- last_per = rate_ctrl->state[tx_rate].per;
+ last_per = ath_rc_priv->state[tx_rate].per;
if (xretries) {
- /* Update the PER. */
if (xretries == 1) {
- rate_ctrl->state[tx_rate].per += 30;
- if (rate_ctrl->state[tx_rate].per > 100)
- rate_ctrl->state[tx_rate].per = 100;
+ ath_rc_priv->state[tx_rate].per += 30;
+ if (ath_rc_priv->state[tx_rate].per > 100)
+ ath_rc_priv->state[tx_rate].per = 100;
} else {
/* xretries == 2 */
- count = sizeof(nretry_to_per_lookup) /
- sizeof(nretry_to_per_lookup[0]);
+ count = ARRAY_SIZE(nretry_to_per_lookup);
if (retries >= count)
retries = count - 1;
+
/* new_PER = 7/8*old_PER + 1/8*(currentPER) */
- rate_ctrl->state[tx_rate].per =
- (u8)(rate_ctrl->state[tx_rate].per -
- (rate_ctrl->state[tx_rate].per >> 3) +
- ((100) >> 3));
+ ath_rc_priv->state[tx_rate].per =
+ (u8)(last_per - (last_per >> 3) + (100 >> 3));
}
/* xretries == 1 or 2 */
- if (rate_ctrl->probe_rate == tx_rate)
- rate_ctrl->probe_rate = 0;
+ if (ath_rc_priv->probe_rate == tx_rate)
+ ath_rc_priv->probe_rate = 0;
- } else { /* xretries == 0 */
- /* Update the PER. */
- /* Make sure it doesn't index out of array's bounds. */
- count = sizeof(nretry_to_per_lookup) /
- sizeof(nretry_to_per_lookup[0]);
+ } else { /* xretries == 0 */
+ count = ARRAY_SIZE(nretry_to_per_lookup);
if (retries >= count)
retries = count - 1;
- if (info_priv->n_bad_frames) {
+
+ if (tx_info_priv->n_bad_frames) {
/* new_PER = 7/8*old_PER + 1/8*(currentPER)
* Assuming that n_frames is not 0. The current PER
* from the retries is 100 * retries / (retries+1),
@@ -1308,37 +951,35 @@ static void ath_rc_update_ht(struct ath_softc *sc,
* the above PER. The expression below is a
* simplified version of the sum of these two terms.
*/
- if (info_priv->n_frames > 0)
- rate_ctrl->state[tx_rate].per
- = (u8)
- (rate_ctrl->state[tx_rate].per -
- (rate_ctrl->state[tx_rate].per >> 3) +
- ((100*(retries*info_priv->n_frames +
- info_priv->n_bad_frames) /
- (info_priv->n_frames *
- (retries+1))) >> 3));
+ if (tx_info_priv->n_frames > 0) {
+ int n_frames, n_bad_frames;
+ u8 cur_per, new_per;
+
+ n_bad_frames = retries * tx_info_priv->n_frames +
+ tx_info_priv->n_bad_frames;
+ n_frames = tx_info_priv->n_frames * (retries + 1);
+ cur_per = (100 * n_bad_frames / n_frames) >> 3;
+ new_per = (u8)(last_per - (last_per >> 3) + cur_per);
+ ath_rc_priv->state[tx_rate].per = new_per;
+ }
} else {
- /* new_PER = 7/8*old_PER + 1/8*(currentPER) */
-
- rate_ctrl->state[tx_rate].per = (u8)
- (rate_ctrl->state[tx_rate].per -
- (rate_ctrl->state[tx_rate].per >> 3) +
- (nretry_to_per_lookup[retries] >> 3));
+ ath_rc_priv->state[tx_rate].per =
+ (u8)(last_per - (last_per >> 3) +
+ (nretry_to_per_lookup[retries] >> 3));
}
- rate_ctrl->rssi_last_prev2 = rate_ctrl->rssi_last_prev;
- rate_ctrl->rssi_last_prev = rate_ctrl->rssi_last;
- rate_ctrl->rssi_last = info_priv->tx.ts_rssi;
- rate_ctrl->rssi_time = now_msec;
+ ath_rc_priv->rssi_last_prev2 = ath_rc_priv->rssi_last_prev;
+ ath_rc_priv->rssi_last_prev = ath_rc_priv->rssi_last;
+ ath_rc_priv->rssi_last = tx_info_priv->tx.ts_rssi;
+ ath_rc_priv->rssi_time = now_msec;
/*
* If we got at most one retry then increase the max rate if
* this was a probe. Otherwise, ignore the probe.
*/
-
- if (rate_ctrl->probe_rate && rate_ctrl->probe_rate == tx_rate) {
- if (retries > 0 || 2 * info_priv->n_bad_frames >
- info_priv->n_frames) {
+ if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) {
+ if (retries > 0 || 2 * tx_info_priv->n_bad_frames >
+ tx_info_priv->n_frames) {
/*
* Since we probed with just a single attempt,
* any retries means the probe failed. Also,
@@ -1346,17 +987,18 @@ static void ath_rc_update_ht(struct ath_softc *sc,
* the subframes were bad then also consider
* the probe a failure.
*/
- rate_ctrl->probe_rate = 0;
+ ath_rc_priv->probe_rate = 0;
} else {
u8 probe_rate = 0;
- rate_ctrl->rate_max_phy = rate_ctrl->probe_rate;
- probe_rate = rate_ctrl->probe_rate;
+ ath_rc_priv->rate_max_phy =
+ ath_rc_priv->probe_rate;
+ probe_rate = ath_rc_priv->probe_rate;
- if (rate_ctrl->state[probe_rate].per > 30)
- rate_ctrl->state[probe_rate].per = 20;
+ if (ath_rc_priv->state[probe_rate].per > 30)
+ ath_rc_priv->state[probe_rate].per = 20;
- rate_ctrl->probe_rate = 0;
+ ath_rc_priv->probe_rate = 0;
/*
* Since this probe succeeded, we allow the next
@@ -1364,8 +1006,8 @@ static void ath_rc_update_ht(struct ath_softc *sc,
* to move up faster if the probes are
* succesful.
*/
- rate_ctrl->probe_time = now_msec -
- rate_table->probe_interval / 2;
+ ath_rc_priv->probe_time =
+ now_msec - rate_table->probe_interval / 2;
}
}
@@ -1375,74 +1017,114 @@ static void ath_rc_update_ht(struct ath_softc *sc,
* this was because of collisions or poor signal.
*
* Later: if rssi_ack is close to
- * rate_ctrl->state[txRate].rssi_thres and we see lots
+ * ath_rc_priv->state[txRate].rssi_thres and we see lots
* of retries, then we could increase
- * rate_ctrl->state[txRate].rssi_thres.
+ * ath_rc_priv->state[txRate].rssi_thres.
*/
- rate_ctrl->hw_maxretry_pktcnt = 0;
+ ath_rc_priv->hw_maxretry_pktcnt = 0;
} else {
+ int32_t rssi_ackAvg;
+ int8_t rssi_thres;
+ int8_t rssi_ack_vmin;
+
/*
* It worked with no retries. First ignore bogus (small)
* rssi_ack values.
*/
- if (tx_rate == rate_ctrl->rate_max_phy &&
- rate_ctrl->hw_maxretry_pktcnt < 255) {
- rate_ctrl->hw_maxretry_pktcnt++;
+ if (tx_rate == ath_rc_priv->rate_max_phy &&
+ ath_rc_priv->hw_maxretry_pktcnt < 255) {
+ ath_rc_priv->hw_maxretry_pktcnt++;
}
- if (info_priv->tx.ts_rssi >=
- rate_table->info[tx_rate].rssi_ack_validmin) {
- /* Average the rssi */
- if (tx_rate != rate_ctrl->rssi_sum_rate) {
- rate_ctrl->rssi_sum_rate = tx_rate;
- rate_ctrl->rssi_sum =
- rate_ctrl->rssi_sum_cnt = 0;
- }
+ if (tx_info_priv->tx.ts_rssi <
+ rate_table->info[tx_rate].rssi_ack_validmin)
+ goto exit;
- rate_ctrl->rssi_sum += info_priv->tx.ts_rssi;
- rate_ctrl->rssi_sum_cnt++;
-
- if (rate_ctrl->rssi_sum_cnt > 4) {
- int32_t rssi_ackAvg =
- (rate_ctrl->rssi_sum + 2) / 4;
- int8_t rssi_thres =
- rate_ctrl->state[tx_rate].
- rssi_thres;
- int8_t rssi_ack_vmin =
- rate_table->info[tx_rate].
- rssi_ack_validmin;
-
- rate_ctrl->rssi_sum =
- rate_ctrl->rssi_sum_cnt = 0;
-
- /* Now reduce the current
- * rssi threshold. */
- if ((rssi_ackAvg < rssi_thres + 2) &&
- (rssi_thres > rssi_ack_vmin)) {
- rate_ctrl->state[tx_rate].
- rssi_thres--;
- }
-
- state_change = TRUE;
- }
+ /* Average the rssi */
+ if (tx_rate != ath_rc_priv->rssi_sum_rate) {
+ ath_rc_priv->rssi_sum_rate = tx_rate;
+ ath_rc_priv->rssi_sum =
+ ath_rc_priv->rssi_sum_cnt = 0;
}
+
+ ath_rc_priv->rssi_sum += tx_info_priv->tx.ts_rssi;
+ ath_rc_priv->rssi_sum_cnt++;
+
+ if (ath_rc_priv->rssi_sum_cnt < 4)
+ goto exit;
+
+ rssi_ackAvg =
+ (ath_rc_priv->rssi_sum + 2) / 4;
+ rssi_thres =
+ ath_rc_priv->state[tx_rate].rssi_thres;
+ rssi_ack_vmin =
+ rate_table->info[tx_rate].rssi_ack_validmin;
+
+ ath_rc_priv->rssi_sum =
+ ath_rc_priv->rssi_sum_cnt = 0;
+
+ /* Now reduce the current rssi threshold */
+ if ((rssi_ackAvg < rssi_thres + 2) &&
+ (rssi_thres > rssi_ack_vmin)) {
+ ath_rc_priv->state[tx_rate].rssi_thres--;
+ }
+
+ state_change = true;
}
}
+exit:
+ return state_change;
+}
+
+/* Update PER, RSSI and whatever else that the code thinks it is doing.
+ If you can make sense of all this, you really need to go out more. */
+
+static void ath_rc_update_ht(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ath_tx_info_priv *tx_info_priv,
+ int tx_rate, int xretries, int retries)
+{
+#define CHK_RSSI(rate) \
+ ((ath_rc_priv->state[(rate)].rssi_thres + \
+ rate_table->info[(rate)].rssi_ack_deltamin) > \
+ ath_rc_priv->state[(rate)+1].rssi_thres)
- /* For all cases */
+ u32 now_msec = jiffies_to_msecs(jiffies);
+ int rate;
+ u8 last_per;
+ bool state_change = false;
+ struct ath_rate_table *rate_table = sc->cur_rate_table;
+ int size = ath_rc_priv->rate_table_size;
+
+ if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt))
+ return;
+
+ /* To compensate for some imbalance between ctrl and ext. channel */
+
+ if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy))
+ tx_info_priv->tx.ts_rssi =
+ tx_info_priv->tx.ts_rssi < 3 ? 0 :
+ tx_info_priv->tx.ts_rssi - 3;
+
+ last_per = ath_rc_priv->state[tx_rate].per;
+
+ /* Update PER first */
+ state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv,
+ tx_info_priv, tx_rate, xretries,
+ retries, now_msec);
/*
* If this rate looks bad (high PER) then stop using it for
* a while (except if we are probing).
*/
- if (rate_ctrl->state[tx_rate].per >= 55 && tx_rate > 0 &&
+ if (ath_rc_priv->state[tx_rate].per >= 55 && tx_rate > 0 &&
rate_table->info[tx_rate].ratekbps <=
- rate_table->info[rate_ctrl->rate_max_phy].ratekbps) {
- ath_rc_get_nextlowervalid_txrate(rate_table, rate_ctrl,
- (u8) tx_rate, &rate_ctrl->rate_max_phy);
+ rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) {
+ ath_rc_get_nextlowervalid_txrate(rate_table, ath_rc_priv,
+ (u8)tx_rate, &ath_rc_priv->rate_max_phy);
/* Don't probe for a little while. */
- rate_ctrl->probe_time = now_msec;
+ ath_rc_priv->probe_time = now_msec;
}
if (state_change) {
@@ -1453,20 +1135,15 @@ static void ath_rc_update_ht(struct ath_softc *sc,
* made to keep the rssi thresholds monotonically
* increasing between the CCK and OFDM rates.)
*/
- for (rate = tx_rate; rate <
- rate_ctrl->rate_table_size - 1; rate++) {
+ for (rate = tx_rate; rate < size - 1; rate++) {
if (rate_table->info[rate+1].phy !=
- rate_table->info[tx_rate].phy)
+ rate_table->info[tx_rate].phy)
break;
- if (rate_ctrl->state[rate].rssi_thres +
- rate_table->info[rate].rssi_ack_deltamin >
- rate_ctrl->state[rate+1].rssi_thres) {
- rate_ctrl->state[rate+1].rssi_thres =
- rate_ctrl->state[rate].
- rssi_thres +
- rate_table->info[rate].
- rssi_ack_deltamin;
+ if (CHK_RSSI(rate)) {
+ ath_rc_priv->state[rate+1].rssi_thres =
+ ath_rc_priv->state[rate].rssi_thres +
+ rate_table->info[rate].rssi_ack_deltamin;
}
}
@@ -1476,27 +1153,20 @@ static void ath_rc_update_ht(struct ath_softc *sc,
rate_table->info[tx_rate].phy)
break;
- if (rate_ctrl->state[rate].rssi_thres +
- rate_table->info[rate].rssi_ack_deltamin >
- rate_ctrl->state[rate+1].rssi_thres) {
- if (rate_ctrl->state[rate+1].rssi_thres <
- rate_table->info[rate].
- rssi_ack_deltamin)
- rate_ctrl->state[rate].rssi_thres = 0;
+ if (CHK_RSSI(rate)) {
+ if (ath_rc_priv->state[rate+1].rssi_thres <
+ rate_table->info[rate].rssi_ack_deltamin)
+ ath_rc_priv->state[rate].rssi_thres = 0;
else {
- rate_ctrl->state[rate].rssi_thres =
- rate_ctrl->state[rate+1].
- rssi_thres -
- rate_table->info[rate].
- rssi_ack_deltamin;
+ ath_rc_priv->state[rate].rssi_thres =
+ ath_rc_priv->state[rate+1].rssi_thres -
+ rate_table->info[rate].rssi_ack_deltamin;
}
- if (rate_ctrl->state[rate].rssi_thres <
- rate_table->info[rate].
- rssi_ack_validmin) {
- rate_ctrl->state[rate].rssi_thres =
- rate_table->info[rate].
- rssi_ack_validmin;
+ if (ath_rc_priv->state[rate].rssi_thres <
+ rate_table->info[rate].rssi_ack_validmin) {
+ ath_rc_priv->state[rate].rssi_thres =
+ rate_table->info[rate].rssi_ack_validmin;
}
}
}
@@ -1504,74 +1174,86 @@ static void ath_rc_update_ht(struct ath_softc *sc,
/* Make sure the rates below this have lower PER */
/* Monotonicity is kept only for rates below the current rate. */
- if (rate_ctrl->state[tx_rate].per < last_per) {
+ if (ath_rc_priv->state[tx_rate].per < last_per) {
for (rate = tx_rate - 1; rate >= 0; rate--) {
if (rate_table->info[rate].phy !=
rate_table->info[tx_rate].phy)
break;
- if (rate_ctrl->state[rate].per >
- rate_ctrl->state[rate+1].per) {
- rate_ctrl->state[rate].per =
- rate_ctrl->state[rate+1].per;
+ if (ath_rc_priv->state[rate].per >
+ ath_rc_priv->state[rate+1].per) {
+ ath_rc_priv->state[rate].per =
+ ath_rc_priv->state[rate+1].per;
}
}
}
/* Maintain monotonicity for rates above the current rate */
- for (rate = tx_rate; rate < rate_ctrl->rate_table_size - 1; rate++) {
- if (rate_ctrl->state[rate+1].per < rate_ctrl->state[rate].per)
- rate_ctrl->state[rate+1].per =
- rate_ctrl->state[rate].per;
+ for (rate = tx_rate; rate < size - 1; rate++) {
+ if (ath_rc_priv->state[rate+1].per <
+ ath_rc_priv->state[rate].per)
+ ath_rc_priv->state[rate+1].per =
+ ath_rc_priv->state[rate].per;
}
/* Every so often, we reduce the thresholds and
* PER (different for CCK and OFDM). */
- if (now_msec - rate_ctrl->rssi_down_time >=
+ if (now_msec - ath_rc_priv->rssi_down_time >=
rate_table->rssi_reduce_interval) {
- for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) {
- if (rate_ctrl->state[rate].rssi_thres >
+ for (rate = 0; rate < size; rate++) {
+ if (ath_rc_priv->state[rate].rssi_thres >
rate_table->info[rate].rssi_ack_validmin)
- rate_ctrl->state[rate].rssi_thres -= 1;
+ ath_rc_priv->state[rate].rssi_thres -= 1;
}
- rate_ctrl->rssi_down_time = now_msec;
+ ath_rc_priv->rssi_down_time = now_msec;
}
/* Every so often, we reduce the thresholds
* and PER (different for CCK and OFDM). */
- if (now_msec - rate_ctrl->per_down_time >=
+ if (now_msec - ath_rc_priv->per_down_time >=
rate_table->rssi_reduce_interval) {
- for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) {
- rate_ctrl->state[rate].per =
- 7 * rate_ctrl->state[rate].per / 8;
+ for (rate = 0; rate < size; rate++) {
+ ath_rc_priv->state[rate].per =
+ 7 * ath_rc_priv->state[rate].per / 8;
}
- rate_ctrl->per_down_time = now_msec;
+ ath_rc_priv->per_down_time = now_msec;
}
+
+#undef CHK_RSSI
}
-/*
- * This routine is called in rate control callback tx_status() to give
- * the status of previous frames.
- */
-static void ath_rc_update(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- struct ath_tx_info_priv *info_priv, int final_ts_idx,
- int xretries, int long_retry)
+static int ath_rc_get_rateindex(struct ath_rate_table *rate_table,
+ struct ieee80211_tx_rate *rate)
{
- struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
+ int rix;
+
+ if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+ (rate->flags & IEEE80211_TX_RC_SHORT_GI))
+ rix = rate_table->info[rate->idx].ht_index;
+ else if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
+ rix = rate_table->info[rate->idx].sgi_index;
+ else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ rix = rate_table->info[rate->idx].cw40index;
+ else
+ rix = rate_table->info[rate->idx].base_index;
+
+ return rix;
+}
+
+static void ath_rc_tx_status(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ieee80211_tx_info *tx_info,
+ int final_ts_idx, int xretries, int long_retry)
+{
+ struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
struct ath_rate_table *rate_table;
- struct ath_tx_ratectrl *rate_ctrl;
- struct ath_rc_series rcs[4];
+ struct ieee80211_tx_rate *rates = tx_info->status.rates;
u8 flags;
- u32 series = 0, rix;
+ u32 i = 0, rix;
- memcpy(rcs, info_priv->rcs, 4 * sizeof(rcs[0]));
- rate_table = (struct ath_rate_table *)
- asc->hw_rate_table[sc->sc_curmode];
- rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
- ASSERT(rcs[0].tries != 0);
+ rate_table = sc->cur_rate_table;
/*
* If the first rate is not the final index, there
@@ -1579,32 +1261,22 @@ static void ath_rc_update(struct ath_softc *sc,
*/
if (final_ts_idx != 0) {
/* Process intermediate rates that failed.*/
- for (series = 0; series < final_ts_idx ; series++) {
- if (rcs[series].tries != 0) {
- flags = rcs[series].flags;
+ for (i = 0; i < final_ts_idx ; i++) {
+ if (rates[i].count != 0 && (rates[i].idx >= 0)) {
+ flags = rates[i].flags;
+
/* If HT40 and we have switched mode from
* 40 to 20 => don't update */
- if ((flags & ATH_RC_CW40_FLAG) &&
- (rate_ctrl->rc_phy_mode !=
- (flags & ATH_RC_CW40_FLAG)))
+
+ if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+ (ath_rc_priv->rc_phy_mode != WLAN_RC_40_FLAG))
return;
- if ((flags & ATH_RC_CW40_FLAG) &&
- (flags & ATH_RC_SGI_FLAG))
- rix = rate_table->info[
- rcs[series].rix].ht_index;
- else if (flags & ATH_RC_SGI_FLAG)
- rix = rate_table->info[
- rcs[series].rix].sgi_index;
- else if (flags & ATH_RC_CW40_FLAG)
- rix = rate_table->info[
- rcs[series].rix].cw40index;
- else
- rix = rate_table->info[
- rcs[series].rix].base_index;
+
+ rix = ath_rc_get_rateindex(rate_table, &rates[i]);
ath_rc_update_ht(sc, ath_rc_priv,
- info_priv, rix,
+ tx_info_priv, rix,
xretries ? 1 : 2,
- rcs[series].tries);
+ rates[i].count);
}
}
} else {
@@ -1614,240 +1286,152 @@ static void ath_rc_update(struct ath_softc *sc,
* Treating it as an excessive retry penalizes the rate
* inordinately.
*/
- if (rcs[0].tries == 1 && xretries == 1)
+ if (rates[0].count == 1 && xretries == 1)
xretries = 2;
}
- flags = rcs[series].flags;
+ flags = rates[i].flags;
+
/* If HT40 and we have switched mode from 40 to 20 => don't update */
- if ((flags & ATH_RC_CW40_FLAG) &&
- (rate_ctrl->rc_phy_mode != (flags & ATH_RC_CW40_FLAG)))
+ if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
+ (ath_rc_priv->rc_phy_mode != WLAN_RC_40_FLAG)) {
return;
+ }
- if ((flags & ATH_RC_CW40_FLAG) && (flags & ATH_RC_SGI_FLAG))
- rix = rate_table->info[rcs[series].rix].ht_index;
- else if (flags & ATH_RC_SGI_FLAG)
- rix = rate_table->info[rcs[series].rix].sgi_index;
- else if (flags & ATH_RC_CW40_FLAG)
- rix = rate_table->info[rcs[series].rix].cw40index;
- else
- rix = rate_table->info[rcs[series].rix].base_index;
-
- ath_rc_update_ht(sc, ath_rc_priv, info_priv, rix,
- xretries, long_retry);
+ rix = ath_rc_get_rateindex(rate_table, &rates[i]);
+ ath_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix,
+ xretries, long_retry);
}
-/*
- * Process a tx descriptor for a completed transmit (success or failure).
- */
-static void ath_rate_tx_complete(struct ath_softc *sc,
- struct ath_node *an,
- struct ath_rate_node *rc_priv,
- struct ath_tx_info_priv *info_priv)
+static struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
+ enum ieee80211_band band,
+ bool is_ht, bool is_cw_40)
{
- int final_ts_idx = info_priv->tx.ts_rateindex;
- int tx_status = 0, is_underrun = 0;
- struct ath_vap *avp;
-
- avp = rc_priv->avp;
- if ((avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE) ||
- (info_priv->tx.ts_status & ATH9K_TXERR_FILT))
- return;
-
- if (info_priv->tx.ts_rssi > 0) {
- ATH_RSSI_LPF(an->an_chainmask_sel.tx_avgrssi,
- info_priv->tx.ts_rssi);
- }
-
- /*
- * If underrun error is seen assume it as an excessive retry only
- * if prefetch trigger level have reached the max (0x3f for 5416)
- * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY
- * times. This affects how ratectrl updates PER for the failed rate.
- */
- if (info_priv->tx.ts_flags &
- (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
- ((sc->sc_ah->ah_txTrigLevel) >= tx_triglevel_max)) {
- tx_status = 1;
- is_underrun = 1;
+ int mode = 0;
+
+ switch(band) {
+ case IEEE80211_BAND_2GHZ:
+ mode = ATH9K_MODE_11G;
+ if (is_ht)
+ mode = ATH9K_MODE_11NG_HT20;
+ if (is_cw_40)
+ mode = ATH9K_MODE_11NG_HT40PLUS;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ mode = ATH9K_MODE_11A;
+ if (is_ht)
+ mode = ATH9K_MODE_11NA_HT20;
+ if (is_cw_40)
+ mode = ATH9K_MODE_11NA_HT40PLUS;
+ break;
+ default:
+ DPRINTF(sc, ATH_DBG_CONFIG, "Invalid band\n");
+ return NULL;
}
- if ((info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) ||
- (info_priv->tx.ts_status & ATH9K_TXERR_FIFO))
- tx_status = 1;
+ BUG_ON(mode >= ATH9K_MODE_MAX);
- ath_rc_update(sc, rc_priv, info_priv, final_ts_idx, tx_status,
- (is_underrun) ? ATH_11N_TXMAXTRY :
- info_priv->tx.ts_longretry);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Choosing rate table for mode: %d\n", mode);
+ return sc->hw_rate_table[mode];
}
-/*
- * Update the SIB's rate control information
- *
- * This should be called when the supported rates change
- * (e.g. SME operation, wireless mode change)
- *
- * It will determine which rates are valid for use.
- */
-static void ath_rc_sib_update(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- u32 capflag, int keep_state,
- struct ath_rateset *negotiated_rates,
- struct ath_rateset *negotiated_htrates)
+static void ath_rc_init(struct ath_softc *sc,
+ struct ath_rate_priv *ath_rc_priv,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta)
{
struct ath_rate_table *rate_table = NULL;
- struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
- struct ath_rateset *rateset = negotiated_rates;
- u8 *ht_mcs = (u8 *)negotiated_htrates;
- struct ath_tx_ratectrl *rate_ctrl =
- (struct ath_tx_ratectrl *)ath_rc_priv;
+ struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
+ u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates;
u8 i, j, k, hi = 0, hthi = 0;
- rate_table = (struct ath_rate_table *)
- asc->hw_rate_table[sc->sc_curmode];
+ /* FIXME: Adhoc */
+ if ((sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION) ||
+ (sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC)) {
+ bool is_cw_40 = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ rate_table = ath_choose_rate_table(sc, sband->band,
+ sta->ht_cap.ht_supported,
+ is_cw_40);
+ } else if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP) {
+ /* cur_rate_table would be set on init through config() */
+ rate_table = sc->cur_rate_table;
+ }
+
+ if (!rate_table) {
+ DPRINTF(sc, ATH_DBG_FATAL, "Rate table not initialized\n");
+ return;
+ }
+
+ if (sta->ht_cap.ht_supported) {
+ ath_rc_priv->ht_cap = (WLAN_RC_HT_FLAG | WLAN_RC_DS_FLAG);
+ if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+ ath_rc_priv->ht_cap |= WLAN_RC_40_FLAG;
+ }
/* Initial rate table size. Will change depending
* on the working rate set */
- rate_ctrl->rate_table_size = MAX_TX_RATE_TBL;
+ ath_rc_priv->rate_table_size = RATE_TABLE_SIZE;
/* Initialize thresholds according to the global rate table */
- for (i = 0 ; (i < rate_ctrl->rate_table_size) && (!keep_state); i++) {
- rate_ctrl->state[i].rssi_thres =
+ for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) {
+ ath_rc_priv->state[i].rssi_thres =
rate_table->info[i].rssi_ack_validmin;
- rate_ctrl->state[i].per = 0;
+ ath_rc_priv->state[i].per = 0;
}
/* Determine the valid rates */
- ath_rc_init_valid_txmask(rate_ctrl);
+ ath_rc_init_valid_txmask(ath_rc_priv);
for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
for (j = 0; j < MAX_TX_RATE_PHY; j++)
- rate_ctrl->valid_phy_rateidx[i][j] = 0;
- rate_ctrl->valid_phy_ratecnt[i] = 0;
+ ath_rc_priv->valid_phy_rateidx[i][j] = 0;
+ ath_rc_priv->valid_phy_ratecnt[i] = 0;
}
- rate_ctrl->rc_phy_mode = (capflag & WLAN_RC_40_FLAG);
+ ath_rc_priv->rc_phy_mode = (ath_rc_priv->ht_cap & WLAN_RC_40_FLAG);
/* Set stream capability */
- ath_rc_priv->single_stream = (capflag & WLAN_RC_DS_FLAG) ? 0 : 1;
+ ath_rc_priv->single_stream = (ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG) ? 0 : 1;
if (!rateset->rs_nrates) {
/* No working rate, just initialize valid rates */
- hi = ath_rc_sib_init_validrates(ath_rc_priv, rate_table,
- capflag);
+ hi = ath_rc_init_validrates(ath_rc_priv, rate_table,
+ ath_rc_priv->ht_cap);
} else {
/* Use intersection of working rates and valid rates */
- hi = ath_rc_sib_setvalid_rates(ath_rc_priv, rate_table,
- rateset, capflag);
- if (capflag & WLAN_RC_HT_FLAG) {
- hthi = ath_rc_sib_setvalid_htrates(ath_rc_priv,
+ hi = ath_rc_setvalid_rates(ath_rc_priv, rate_table,
+ rateset, ath_rc_priv->ht_cap);
+ if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) {
+ hthi = ath_rc_setvalid_htrates(ath_rc_priv,
rate_table,
ht_mcs,
- capflag);
+ ath_rc_priv->ht_cap);
}
hi = A_MAX(hi, hthi);
}
- rate_ctrl->rate_table_size = hi + 1;
- rate_ctrl->rate_max_phy = 0;
- ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL);
+ ath_rc_priv->rate_table_size = hi + 1;
+ ath_rc_priv->rate_max_phy = 0;
+ ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
- for (j = 0; j < rate_ctrl->valid_phy_ratecnt[i]; j++) {
- rate_ctrl->valid_rate_index[k++] =
- rate_ctrl->valid_phy_rateidx[i][j];
+ for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) {
+ ath_rc_priv->valid_rate_index[k++] =
+ ath_rc_priv->valid_phy_rateidx[i][j];
}
- if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, TRUE)
- || !rate_ctrl->valid_phy_ratecnt[i])
+ if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1)
+ || !ath_rc_priv->valid_phy_ratecnt[i])
continue;
- rate_ctrl->rate_max_phy = rate_ctrl->valid_phy_rateidx[i][j-1];
+ ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1];
}
- ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL);
- ASSERT(k <= MAX_TX_RATE_TBL);
-
- rate_ctrl->max_valid_rate = k;
- /*
- * Some third party vendors don't send the supported rate series in
- * order. So sorting to make sure its in order, otherwise our RateFind
- * Algo will select wrong rates
- */
- ath_rc_sort_validrates(rate_table, rate_ctrl);
- rate_ctrl->rate_max_phy = rate_ctrl->valid_rate_index[k-4];
-}
-
-/*
- * Update rate-control state on station associate/reassociate.
- */
-static int ath_rate_newassoc(struct ath_softc *sc,
- struct ath_rate_node *ath_rc_priv,
- unsigned int capflag,
- struct ath_rateset *negotiated_rates,
- struct ath_rateset *negotiated_htrates)
-{
-
-
- ath_rc_priv->ht_cap =
- ((capflag & ATH_RC_DS_FLAG) ? WLAN_RC_DS_FLAG : 0) |
- ((capflag & ATH_RC_SGI_FLAG) ? WLAN_RC_SGI_FLAG : 0) |
- ((capflag & ATH_RC_HT_FLAG) ? WLAN_RC_HT_FLAG : 0) |
- ((capflag & ATH_RC_CW40_FLAG) ? WLAN_RC_40_FLAG : 0);
-
- ath_rc_sib_update(sc, ath_rc_priv, ath_rc_priv->ht_cap, 0,
- negotiated_rates, negotiated_htrates);
-
- return 0;
-}
-
-/*
- * This routine is called to initialize the rate control parameters
- * in the SIB. It is called initially during system initialization
- * or when a station is associated with the AP.
- */
-static void ath_rc_sib_init(struct ath_rate_node *ath_rc_priv)
-{
- struct ath_tx_ratectrl *rate_ctrl;
-
- rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
- rate_ctrl->rssi_down_time = jiffies_to_msecs(jiffies);
-}
-
-
-static void ath_setup_rates(struct ath_softc *sc,
- struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta,
- struct ath_rate_node *rc_priv)
-
-{
- int i, j = 0;
-
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
-
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sta->supp_rates[sband->band] & BIT(i)) {
- rc_priv->neg_rates.rs_rates[j]
- = (sband->bitrates[i].bitrate * 2) / 10;
- j++;
- }
- }
- rc_priv->neg_rates.rs_nrates = j;
-}
-
-void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv)
-{
- struct ath_softc *sc = hw->priv;
- u32 capflag = 0;
-
- if (hw->conf.ht_conf.ht_supported) {
- capflag |= ATH_RC_HT_FLAG | ATH_RC_DS_FLAG;
- if (sc->sc_ht_info.tx_chan_width == ATH9K_HT_MACMODE_2040)
- capflag |= ATH_RC_CW40_FLAG;
- }
-
- ath_rate_newassoc(sc, rc_priv, capflag,
- &rc_priv->neg_rates,
- &rc_priv->neg_ht_rates);
+ ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
+ ASSERT(k <= RATE_TABLE_SIZE);
+ ath_rc_priv->max_valid_rate = k;
+ ath_rc_sort_validrates(rate_table, ath_rc_priv);
+ ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4];
+ sc->cur_rate_table = rate_table;
}
/* Rate Control callbacks */
@@ -1856,163 +1440,88 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct sk_buff *skb)
{
struct ath_softc *sc = priv;
- struct ath_tx_info_priv *tx_info_priv;
- struct ath_node *an;
+ struct ath_rate_priv *ath_rc_priv = priv_sta;
+ struct ath_tx_info_priv *tx_info_priv = NULL;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr;
+ int final_ts_idx, tx_status = 0, is_underrun = 0;
__le16 fc;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
+ tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+ final_ts_idx = tx_info_priv->tx.ts_rateindex;
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, hdr->addr1);
- spin_unlock_bh(&sc->node_lock);
+ if (!priv_sta || !ieee80211_is_data(fc) ||
+ !tx_info_priv->update_rc)
+ goto exit;
- if (!an || !priv_sta || !ieee80211_is_data(fc)) {
- if (tx_info->driver_data[0] != NULL) {
- kfree(tx_info->driver_data[0]);
- tx_info->driver_data[0] = NULL;
- }
- return;
- }
- if (tx_info->driver_data[0] != NULL) {
- ath_rate_tx_complete(sc, an, priv_sta, tx_info_priv);
- kfree(tx_info->driver_data[0]);
- tx_info->driver_data[0] = NULL;
- }
-}
-
-static void ath_tx_aggr_resp(struct ath_softc *sc,
- struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta,
- struct ath_node *an,
- u8 tidno)
-{
- struct ath_atx_tid *txtid;
- u16 buffersize = 0;
- int state;
- struct sta_info *si;
-
- if (!(sc->sc_flags & SC_OP_TXAGGR))
- return;
-
- txtid = ATH_AN_2_TID(an, tidno);
- if (!txtid->paused)
- return;
+ if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT)
+ goto exit;
/*
- * XXX: This is entirely busted, we aren't supposed to
- * access the sta from here because it's internal
- * to mac80211, and looking at the state without
- * locking is wrong too.
+ * If underrun error is seen assume it as an excessive retry only
+ * if prefetch trigger level have reached the max (0x3f for 5416)
+ * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY
+ * times. This affects how ratectrl updates PER for the failed rate.
*/
- si = container_of(sta, struct sta_info, sta);
- buffersize = IEEE80211_MIN_AMPDU_BUF <<
- sband->ht_info.ampdu_factor; /* FIXME */
- state = si->ampdu_mlme.tid_state_tx[tidno];
-
- if (state & HT_ADDBA_RECEIVED_MSK) {
- txtid->addba_exchangecomplete = 1;
- txtid->addba_exchangeinprogress = 0;
- txtid->baw_size = buffersize;
-
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Resuming tid, buffersize: %d\n",
- __func__,
- buffersize);
-
- ath_tx_resume_tid(sc, txtid);
+ if (tx_info_priv->tx.ts_flags &
+ (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
+ ((sc->sc_ah->ah_txTrigLevel) >= ath_rc_priv->tx_triglevel_max)) {
+ tx_status = 1;
+ is_underrun = 1;
}
+
+ if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) ||
+ (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO))
+ tx_status = 1;
+
+ ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
+ (is_underrun) ? ATH_11N_TXMAXTRY :
+ tx_info_priv->tx.ts_longretry);
+
+exit:
+ kfree(tx_info_priv);
}
-static void ath_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
{
+ struct ieee80211_supported_band *sband = txrc->sband;
+ struct sk_buff *skb = txrc->skb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ath_softc *sc = priv;
struct ieee80211_hw *hw = sc->hw;
- struct ath_tx_info_priv *tx_info_priv;
- struct ath_rate_node *ath_rc_priv = priv_sta;
- struct ath_node *an;
+ struct ath_rate_priv *ath_rc_priv = priv_sta;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- int is_probe = FALSE, chk, ret;
- s8 lowest_idx;
+ int is_probe = 0;
__le16 fc = hdr->frame_control;
- u8 *qc, tid;
- DECLARE_MAC_BUF(mac);
-
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
-
- /* allocate driver private area of tx_info */
- tx_info->driver_data[0] = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC);
- ASSERT(tx_info->driver_data[0] != NULL);
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- lowest_idx = rate_lowest_index(sband, sta);
- tx_info_priv->min_rate = (sband->bitrates[lowest_idx].bitrate * 2) / 10;
/* lowest rate for management and multicast/broadcast frames */
- if (!ieee80211_is_data(fc) ||
- is_multicast_ether_addr(hdr->addr1) || !sta) {
- sel->rate_idx = lowest_idx;
+ if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
+ !sta) {
+ tx_info->control.rates[0].idx = rate_lowest_index(sband, sta);
+ tx_info->control.rates[0].count =
+ is_multicast_ether_addr(hdr->addr1) ? 1 : ATH_MGT_TXMAXTRY;
return;
}
/* Find tx rate for unicast frames */
- ath_rate_findrate(sc, ath_rc_priv,
- ATH_11N_TXMAXTRY, 4,
- ATH_RC_PROBE_ALLOWED,
- tx_info_priv->rcs,
- &is_probe,
- false);
- if (is_probe)
- sel->probe_idx = ath_rc_priv->tx_ratectrl.probe_rate;
-
- /* Ratecontrol sometimes returns invalid rate index */
- if (tx_info_priv->rcs[0].rix != 0xff)
- ath_rc_priv->prev_data_rix = tx_info_priv->rcs[0].rix;
- else
- tx_info_priv->rcs[0].rix = ath_rc_priv->prev_data_rix;
-
- sel->rate_idx = tx_info_priv->rcs[0].rix;
+ ath_rc_ratefind(sc, ath_rc_priv, ATH_11N_TXMAXTRY, 4,
+ tx_info, &is_probe, false);
/* Check if aggregation has to be enabled for this tid */
-
- if (hw->conf.ht_conf.ht_supported) {
+ if (hw->conf.ht.enabled) {
if (ieee80211_is_data_qos(fc)) {
+ u8 *qc, tid;
+ struct ath_node *an;
+
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
+ an = (struct ath_node *)sta->drv_priv;
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, hdr->addr1);
- spin_unlock_bh(&sc->node_lock);
-
- if (!an) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Node not found to "
- "init/chk TX aggr\n", __func__);
- return;
- }
-
- chk = ath_tx_aggr_check(sc, an, tid);
- if (chk == AGGR_REQUIRED) {
- ret = ieee80211_start_tx_ba_session(hw,
- hdr->addr1, tid);
- if (ret)
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Unable to start tx "
- "aggr for: %s\n",
- __func__,
- print_mac(mac, hdr->addr1));
- else
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Started tx aggr for: %s\n",
- __func__,
- print_mac(mac, hdr->addr1));
- } else if (chk == AGGR_EXCHANGE_PROGRESS)
- ath_tx_aggr_resp(sc, sband, sta, an, tid);
+ if(ath_tx_aggr_check(sc, an, tid))
+ ieee80211_start_tx_ba_session(hw, hdr->addr1, tid);
}
}
}
@@ -2021,34 +1530,33 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta)
{
struct ath_softc *sc = priv;
- struct ath_rate_node *ath_rc_priv = priv_sta;
+ struct ath_rate_priv *ath_rc_priv = priv_sta;
int i, j = 0;
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sta->supp_rates[sband->band] & BIT(i)) {
+ ath_rc_priv->neg_rates.rs_rates[j]
+ = (sband->bitrates[i].bitrate * 2) / 10;
+ j++;
+ }
+ }
+ ath_rc_priv->neg_rates.rs_nrates = j;
- ath_setup_rates(sc, sband, sta, ath_rc_priv);
- if (sc->hw->conf.flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- for (i = 0; i < MCS_SET_SIZE; i++) {
- if (sc->hw->conf.ht_conf.supp_mcs_set[i/8] & (1<<(i%8)))
+ if (sta->ht_cap.ht_supported) {
+ for (i = 0, j = 0; i < 77; i++) {
+ if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
ath_rc_priv->neg_ht_rates.rs_rates[j++] = i;
if (j == ATH_RATE_MAX)
break;
}
ath_rc_priv->neg_ht_rates.rs_nrates = j;
}
- ath_rc_node_update(sc->hw, priv_sta);
-}
-static void ath_rate_clear(void *priv)
-{
- return;
+ ath_rc_init(sc, priv_sta, sband, sta);
}
static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
- struct ath_softc *sc = hw->priv;
-
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
return hw->priv;
}
@@ -2060,19 +1568,17 @@ static void ath_rate_free(void *priv)
static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
{
struct ath_softc *sc = priv;
- struct ath_vap *avp = sc->sc_vaps[0];
- struct ath_rate_node *rate_priv;
-
- DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
+ struct ath_rate_priv *rate_priv;
- rate_priv = ath_rate_node_alloc(avp, sc->sc_rc, gfp);
+ rate_priv = kzalloc(sizeof(struct ath_rate_priv), gfp);
if (!rate_priv) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to allocate private rc structure\n",
- __func__);
+ "Unable to allocate private rc structure\n");
return NULL;
}
- ath_rc_sib_init(rate_priv);
+
+ rate_priv->rssi_down_time = jiffies_to_msecs(jiffies);
+ rate_priv->tx_triglevel_max = sc->sc_ah->ah_caps.tx_triglevel_max;
return rate_priv;
}
@@ -2080,11 +1586,8 @@ static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp
static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta,
void *priv_sta)
{
- struct ath_rate_node *rate_priv = priv_sta;
- struct ath_softc *sc = priv;
-
- DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
- ath_rate_node_free(rate_priv);
+ struct ath_rate_priv *rate_priv = priv_sta;
+ kfree(rate_priv);
}
static struct rate_control_ops ath_rate_ops = {
@@ -2093,13 +1596,69 @@ static struct rate_control_ops ath_rate_ops = {
.tx_status = ath_tx_status,
.get_rate = ath_get_rate,
.rate_init = ath_rate_init,
- .clear = ath_rate_clear,
.alloc = ath_rate_alloc,
.free = ath_rate_free,
.alloc_sta = ath_rate_alloc_sta,
.free_sta = ath_rate_free_sta,
};
+static void ath_setup_rate_table(struct ath_softc *sc,
+ struct ath_rate_table *rate_table)
+{
+ int i;
+
+ for (i = 0; i < 256; i++)
+ rate_table->rateCodeToIndex[i] = (u8)-1;
+
+ for (i = 0; i < rate_table->rate_cnt; i++) {
+ u8 code = rate_table->info[i].ratecode;
+ u8 cix = rate_table->info[i].ctrl_rate;
+ u8 sh = rate_table->info[i].short_preamble;
+
+ rate_table->rateCodeToIndex[code] = i;
+ rate_table->rateCodeToIndex[code | sh] = i;
+
+ rate_table->info[i].lpAckDuration =
+ ath9k_hw_computetxtime(sc->sc_ah, rate_table,
+ WLAN_CTRL_FRAME_SIZE,
+ cix,
+ false);
+ rate_table->info[i].spAckDuration =
+ ath9k_hw_computetxtime(sc->sc_ah, rate_table,
+ WLAN_CTRL_FRAME_SIZE,
+ cix,
+ true);
+ }
+}
+
+void ath_rate_attach(struct ath_softc *sc)
+{
+ sc->hw_rate_table[ATH9K_MODE_11B] =
+ &ar5416_11b_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11A] =
+ &ar5416_11a_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11G] =
+ &ar5416_11g_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT20] =
+ &ar5416_11na_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT20] =
+ &ar5416_11ng_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] =
+ &ar5416_11na_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] =
+ &ar5416_11na_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] =
+ &ar5416_11ng_ratetable;
+ sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] =
+ &ar5416_11ng_ratetable;
+
+ ath_setup_rate_table(sc, &ar5416_11b_ratetable);
+ ath_setup_rate_table(sc, &ar5416_11a_ratetable);
+ ath_setup_rate_table(sc, &ar5416_11g_ratetable);
+ ath_setup_rate_table(sc, &ar5416_11na_ratetable);
+ ath_setup_rate_table(sc, &ar5416_11ng_ratetable);
+}
+
int ath_rate_control_register(void)
{
return ieee80211_rate_control_register(&ath_rate_ops);
@@ -2109,4 +1668,3 @@ void ath_rate_control_unregister(void)
{
ieee80211_rate_control_unregister(&ath_rate_ops);
}
-
diff --git a/drivers/net/wireless/ath9k/rc.h b/drivers/net/wireless/ath9k/rc.h
index b95b41508b98..97c60d12e8aa 100644
--- a/drivers/net/wireless/ath9k/rc.h
+++ b/drivers/net/wireless/ath9k/rc.h
@@ -20,84 +20,24 @@
#define RC_H
#include "ath9k.h"
-/*
- * Interface definitions for transmit rate control modules for the
- * Atheros driver.
- *
- * A rate control module is responsible for choosing the transmit rate
- * for each data frame. Management+control frames are always sent at
- * a fixed rate.
- *
- * Only one module may be present at a time; the driver references
- * rate control interfaces by symbol name. If multiple modules are
- * to be supported we'll need to switch to a registration-based scheme
- * as is currently done, for example, for authentication modules.
- *
- * An instance of the rate control module is attached to each device
- * at attach time and detached when the device is destroyed. The module
- * may associate data with each device and each node (station). Both
- * sets of storage are opaque except for the size of the per-node storage
- * which must be provided when the module is attached.
- *
- * The rate control module is notified for each state transition and
- * station association/reassociation. Otherwise it is queried for a
- * rate for each outgoing frame and provided status from each transmitted
- * frame. Any ancillary processing is the responsibility of the module
- * (e.g. if periodic processing is required then the module should setup
- * it's own timer).
- *
- * In addition to the transmit rate for each frame the module must also
- * indicate the number of attempts to make at the specified rate. If this
- * number is != ATH_TXMAXTRY then an additional callback is made to setup
- * additional transmit state. The rate control code is assumed to write
- * this additional data directly to the transmit descriptor.
- */
struct ath_softc;
-#define TRUE 1
-#define FALSE 0
+#define ATH_RATE_MAX 30
+#define RATE_TABLE_SIZE 64
+#define MAX_TX_RATE_PHY 48
-#define ATH_RATE_MAX 30
-#define MCS_SET_SIZE 128
+/* VALID_ALL - valid for 20/40/Legacy,
+ * VALID - Legacy only,
+ * VALID_20 - HT 20 only,
+ * VALID_40 - HT 40 only */
-enum ieee80211_fixed_rate_mode {
- IEEE80211_FIXED_RATE_NONE = 0,
- IEEE80211_FIXED_RATE_MCS = 1 /* HT rates */
-};
-
-/*
- * Use the hal os glue code to get ms time
- */
-#define IEEE80211_RATE_IDX_ENTRY(val, idx) (((val&(0xff<<(idx*8)))>>(idx*8)))
-
-#define WLAN_PHY_HT_20_SS WLAN_RC_PHY_HT_20_SS
-#define WLAN_PHY_HT_20_DS WLAN_RC_PHY_HT_20_DS
-#define WLAN_PHY_HT_20_DS_HGI WLAN_RC_PHY_HT_20_DS_HGI
-#define WLAN_PHY_HT_40_SS WLAN_RC_PHY_HT_40_SS
-#define WLAN_PHY_HT_40_SS_HGI WLAN_RC_PHY_HT_40_SS_HGI
-#define WLAN_PHY_HT_40_DS WLAN_RC_PHY_HT_40_DS
-#define WLAN_PHY_HT_40_DS_HGI WLAN_RC_PHY_HT_40_DS_HGI
-
-#define WLAN_PHY_OFDM PHY_OFDM
-#define WLAN_PHY_CCK PHY_CCK
-
-#define TRUE_20 0x2
-#define TRUE_40 0x4
-#define TRUE_2040 (TRUE_20|TRUE_40)
-#define TRUE_ALL (TRUE_2040|TRUE)
-
-enum {
- WLAN_RC_PHY_HT_20_SS = 4,
- WLAN_RC_PHY_HT_20_DS,
- WLAN_RC_PHY_HT_40_SS,
- WLAN_RC_PHY_HT_40_DS,
- WLAN_RC_PHY_HT_20_SS_HGI,
- WLAN_RC_PHY_HT_20_DS_HGI,
- WLAN_RC_PHY_HT_40_SS_HGI,
- WLAN_RC_PHY_HT_40_DS_HGI,
- WLAN_RC_PHY_MAX
-};
+#define INVALID 0x0
+#define VALID 0x1
+#define VALID_20 0x2
+#define VALID_40 0x4
+#define VALID_2040 (VALID_20|VALID_40)
+#define VALID_ALL (VALID_2040|VALID)
#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \
|| (_phy == WLAN_RC_PHY_HT_40_DS) \
@@ -114,26 +54,22 @@ enum {
#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS)
-/* Returns the capflag mode */
#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \
- (capflag & WLAN_RC_40_FLAG) ? TRUE_40 : TRUE_20 : TRUE))
+ (capflag & WLAN_RC_40_FLAG) ? VALID_40 : VALID_20 : VALID))
/* Return TRUE if flag supports HT20 && client supports HT20 or
* return TRUE if flag supports HT40 && client supports HT40.
* This is used becos some rates overlap between HT20/HT40.
*/
-
-#define WLAN_RC_PHY_HT_VALID(flag, capflag) (((flag & TRUE_20) && !(capflag \
- & WLAN_RC_40_FLAG)) || ((flag & TRUE_40) && \
- (capflag & WLAN_RC_40_FLAG)))
+#define WLAN_RC_PHY_HT_VALID(flag, capflag) \
+ (((flag & VALID_20) && !(capflag & WLAN_RC_40_FLAG)) || \
+ ((flag & VALID_40) && (capflag & WLAN_RC_40_FLAG)))
#define WLAN_RC_DS_FLAG (0x01)
#define WLAN_RC_40_FLAG (0x02)
#define WLAN_RC_SGI_FLAG (0x04)
#define WLAN_RC_HT_FLAG (0x08)
-#define RATE_TABLE_SIZE 64
-
/**
* struct ath_rate_table - Rate Control table
* @valid: valid for use in rate control
@@ -150,10 +86,11 @@ enum {
* @max_4ms_framelen: maximum frame length(bytes) for tx duration
* @probe_interval: interval for rate control to probe for other rates
* @rssi_reduce_interval: interval for rate control to reduce rssi
- * @initial_ratemax: initial ratemax value used in ath_rc_sib_update()
+ * @initial_ratemax: initial ratemax value
*/
struct ath_rate_table {
int rate_cnt;
+ u8 rateCodeToIndex[256];
struct {
int valid;
int valid_single_stream;
@@ -171,42 +108,26 @@ struct ath_rate_table {
u8 sgi_index;
u8 ht_index;
u32 max_4ms_framelen;
+ u16 lpAckDuration;
+ u16 spAckDuration;
} info[RATE_TABLE_SIZE];
u32 probe_interval;
u32 rssi_reduce_interval;
u8 initial_ratemax;
};
-#define ATH_RC_PROBE_ALLOWED 0x00000001
-#define ATH_RC_MINRATE_LASTRATE 0x00000002
-
-struct ath_rc_series {
- u8 rix;
- u8 tries;
- u8 flags;
- u32 max_4ms_framelen;
-};
-
-/* rcs_flags definition */
-#define ATH_RC_DS_FLAG 0x01
-#define ATH_RC_CW40_FLAG 0x02 /* CW 40 */
-#define ATH_RC_SGI_FLAG 0x04 /* Short Guard Interval */
-#define ATH_RC_HT_FLAG 0x08 /* HT */
-#define ATH_RC_RTSCTS_FLAG 0x10 /* RTS-CTS */
-
-/*
- * State structures for new rate adaptation code
- */
-#define MAX_TX_RATE_TBL 64
-#define MAX_TX_RATE_PHY 48
-
struct ath_tx_ratectrl_state {
int8_t rssi_thres; /* required rssi for this rate (dB) */
u8 per; /* recent estimate of packet error rate (%) */
};
+struct ath_rateset {
+ u8 rs_nrates;
+ u8 rs_rates[ATH_RATE_MAX];
+};
+
/**
- * struct ath_tx_ratectrl - TX Rate control Information
+ * struct ath_rate_priv - Rate Control priv data
* @state: RC state
* @rssi_last: last ACK rssi
* @rssi_last_lookup: last ACK rssi used for lookup
@@ -225,9 +146,13 @@ struct ath_tx_ratectrl_state {
* @valid_phy_ratecnt: valid rate count
* @rate_max_phy: phy index for the max rate
* @probe_interval: interval for ratectrl to probe for other rates
+ * @prev_data_rix: rate idx of last data frame
+ * @ht_cap: HT capabilities
+ * @single_stream: When TRUE, only single TX stream possible
+ * @neg_rates: Negotatied rates
+ * @neg_ht_rates: Negotiated HT rates
*/
-struct ath_tx_ratectrl {
- struct ath_tx_ratectrl_state state[MAX_TX_RATE_TBL];
+struct ath_rate_priv {
int8_t rssi_last;
int8_t rssi_last_lookup;
int8_t rssi_last_prev;
@@ -237,89 +162,40 @@ struct ath_tx_ratectrl {
int32_t rssi_sum;
u8 rate_table_size;
u8 probe_rate;
- u32 rssi_time;
- u32 rssi_down_time;
- u32 probe_time;
u8 hw_maxretry_pktcnt;
u8 max_valid_rate;
- u8 valid_rate_index[MAX_TX_RATE_TBL];
- u32 per_down_time;
-
- /* 11n state */
+ u8 valid_rate_index[RATE_TABLE_SIZE];
+ u8 ht_cap;
+ u8 single_stream;
u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
- u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][MAX_TX_RATE_TBL];
+ u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
u8 rc_phy_mode;
u8 rate_max_phy;
+ u32 rssi_time;
+ u32 rssi_down_time;
+ u32 probe_time;
+ u32 per_down_time;
u32 probe_interval;
-};
-
-struct ath_rateset {
- u8 rs_nrates;
- u8 rs_rates[ATH_RATE_MAX];
-};
-
-/* per-device state */
-struct ath_rate_softc {
- /* phy tables that contain rate control data */
- const void *hw_rate_table[ATH9K_MODE_MAX];
-
- /* -1 or index of fixed rate */
- int fixedrix;
-};
-
-/* per-node state */
-struct ath_rate_node {
- struct ath_tx_ratectrl tx_ratectrl;
-
- /* rate idx of last data frame */
u32 prev_data_rix;
-
- /* ht capabilities */
- u8 ht_cap;
-
- /* When TRUE, only single stream Tx possible */
- u8 single_stream;
-
- /* Negotiated rates */
+ u32 tx_triglevel_max;
+ struct ath_tx_ratectrl_state state[RATE_TABLE_SIZE];
struct ath_rateset neg_rates;
-
- /* Negotiated HT rates */
struct ath_rateset neg_ht_rates;
-
struct ath_rate_softc *asc;
- struct ath_vap *avp;
};
-/* Driver data of ieee80211_tx_info */
struct ath_tx_info_priv {
- struct ath_rc_series rcs[4];
struct ath_tx_status tx;
int n_frames;
int n_bad_frames;
- u8 min_rate;
+ bool update_rc;
};
-/*
- * Attach/detach a rate control module.
- */
-struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah);
-void ath_rate_detach(struct ath_rate_softc *asc);
-
-/*
- * Update/reset rate control state for 802.11 state transitions.
- * Important mostly as the analog to ath_rate_newassoc when operating
- * in station mode.
- */
-void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv);
-void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp);
-
-/*
- * Return rate index for given Dot11 Rate.
- */
-u8 ath_rate_findrateix(struct ath_softc *sc,
- u8 dot11_rate);
+#define ATH_TX_INFO_PRIV(tx_info) \
+ ((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0]))
-/* Routines to register/unregister rate control algorithm */
+void ath_rate_attach(struct ath_softc *sc);
+u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate);
int ath_rate_control_register(void);
void ath_rate_control_unregister(void);
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c
index 504a0444d89f..462e08c3d09d 100644
--- a/drivers/net/wireless/ath9k/recv.c
+++ b/drivers/net/wireless/ath9k/recv.c
@@ -14,10 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * Implementation of receive path.
- */
-
#include "core.h"
/*
@@ -27,10 +23,7 @@
* MAC acknowledges BA status as long as it copies frames to host
* buffer (or rx fifo). This can incorrectly acknowledge packets
* to a sender if last desc is self-linked.
- *
- * NOTE: Caller should hold the rxbuf lock.
*/
-
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
@@ -40,356 +33,53 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
ATH_RXBUF_RESET(bf);
ds = bf->bf_desc;
- ds->ds_link = 0; /* link to null */
+ ds->ds_link = 0; /* link to null */
ds->ds_data = bf->bf_buf_addr;
- /* XXX For RADAR?
- * virtual addr of the beginning of the buffer. */
+ /* virtual addr of the beginning of the buffer. */
skb = bf->bf_mpdu;
ASSERT(skb != NULL);
ds->ds_vdata = skb->data;
- /* setup rx descriptors. The sc_rxbufsize here tells the harware
+ /* setup rx descriptors. The rx.bufsize here tells the harware
* how much data it can DMA to us and that we are prepared
* to process */
- ath9k_hw_setuprxdesc(ah,
- ds,
- sc->sc_rxbufsize,
+ ath9k_hw_setuprxdesc(ah, ds,
+ sc->rx.bufsize,
0);
- if (sc->sc_rxlink == NULL)
+ if (sc->rx.rxlink == NULL)
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
else
- *sc->sc_rxlink = bf->bf_daddr;
+ *sc->rx.rxlink = bf->bf_daddr;
- sc->sc_rxlink = &ds->ds_link;
+ sc->rx.rxlink = &ds->ds_link;
ath9k_hw_rxena(ah);
}
-/* Process received BAR frame */
-
-static int ath_bar_rx(struct ath_softc *sc,
- struct ath_node *an,
- struct sk_buff *skb)
-{
- struct ieee80211_bar *bar;
- struct ath_arx_tid *rxtid;
- struct sk_buff *tskb;
- struct ath_recv_status *rx_status;
- int tidno, index, cindex;
- u16 seqno;
-
- /* look at BAR contents */
-
- bar = (struct ieee80211_bar *)skb->data;
- tidno = (le16_to_cpu(bar->control) & IEEE80211_BAR_CTL_TID_M)
- >> IEEE80211_BAR_CTL_TID_S;
- seqno = le16_to_cpu(bar->start_seq_num) >> IEEE80211_SEQ_SEQ_SHIFT;
-
- /* process BAR - indicate all pending RX frames till the BAR seqno */
-
- rxtid = &an->an_aggr.rx.tid[tidno];
-
- spin_lock_bh(&rxtid->tidlock);
-
- /* get relative index */
-
- index = ATH_BA_INDEX(rxtid->seq_next, seqno);
-
- /* drop BAR if old sequence (index is too large) */
-
- if ((index > rxtid->baw_size) &&
- (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))))
- /* discard frame, ieee layer may not treat frame as a dup */
- goto unlock_and_free;
-
- /* complete receive processing for all pending frames upto BAR seqno */
-
- cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
- while ((rxtid->baw_head != rxtid->baw_tail) &&
- (rxtid->baw_head != cindex)) {
- tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf;
- rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status;
- rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL;
-
- if (tskb != NULL)
- ath_rx_subframe(an, tskb, rx_status);
-
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- }
-
- /* ... and indicate rest of the frames in-order */
-
- while (rxtid->baw_head != rxtid->baw_tail &&
- rxtid->rxbuf[rxtid->baw_head].rx_wbuf != NULL) {
- tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf;
- rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status;
- rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL;
-
- ath_rx_subframe(an, tskb, rx_status);
-
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- }
-
-unlock_and_free:
- spin_unlock_bh(&rxtid->tidlock);
- /* free bar itself */
- dev_kfree_skb(skb);
- return IEEE80211_FTYPE_CTL;
-}
-
-/* Function to handle a subframe of aggregation when HT is enabled */
-
-static int ath_ampdu_input(struct ath_softc *sc,
- struct ath_node *an,
- struct sk_buff *skb,
- struct ath_recv_status *rx_status)
-{
- struct ieee80211_hdr *hdr;
- struct ath_arx_tid *rxtid;
- struct ath_rxbuf *rxbuf;
- u8 type, subtype;
- u16 rxseq;
- int tid = 0, index, cindex, rxdiff;
- __le16 fc;
- u8 *qc;
-
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
-
- /* collect stats of frames with non-zero version */
-
- if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_VERS) != 0) {
- dev_kfree_skb(skb);
- return -1;
- }
-
- type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
- subtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE;
-
- if (ieee80211_is_back_req(fc))
- return ath_bar_rx(sc, an, skb);
-
- /* special aggregate processing only for qos unicast data frames */
-
- if (!ieee80211_is_data(fc) ||
- !ieee80211_is_data_qos(fc) ||
- is_multicast_ether_addr(hdr->addr1))
- return ath_rx_subframe(an, skb, rx_status);
-
- /* lookup rx tid state */
-
- if (ieee80211_is_data_qos(fc)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- }
-
- if (sc->sc_ah->ah_opmode == ATH9K_M_STA) {
- /* Drop the frame not belonging to me. */
- if (memcmp(hdr->addr1, sc->sc_myaddr, ETH_ALEN)) {
- dev_kfree_skb(skb);
- return -1;
- }
- }
-
- rxtid = &an->an_aggr.rx.tid[tid];
-
- spin_lock(&rxtid->tidlock);
-
- rxdiff = (rxtid->baw_tail - rxtid->baw_head) &
- (ATH_TID_MAX_BUFS - 1);
-
- /*
- * If the ADDBA exchange has not been completed by the source,
- * process via legacy path (i.e. no reordering buffer is needed)
- */
- if (!rxtid->addba_exchangecomplete) {
- spin_unlock(&rxtid->tidlock);
- return ath_rx_subframe(an, skb, rx_status);
- }
-
- /* extract sequence number from recvd frame */
-
- rxseq = le16_to_cpu(hdr->seq_ctrl) >> IEEE80211_SEQ_SEQ_SHIFT;
-
- if (rxtid->seq_reset) {
- rxtid->seq_reset = 0;
- rxtid->seq_next = rxseq;
- }
-
- index = ATH_BA_INDEX(rxtid->seq_next, rxseq);
-
- /* drop frame if old sequence (index is too large) */
-
- if (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))) {
- /* discard frame, ieee layer may not treat frame as a dup */
- spin_unlock(&rxtid->tidlock);
- dev_kfree_skb(skb);
- return IEEE80211_FTYPE_DATA;
- }
-
- /* sequence number is beyond block-ack window */
-
- if (index >= rxtid->baw_size) {
-
- /* complete receive processing for all pending frames */
-
- while (index >= rxtid->baw_size) {
-
- rxbuf = rxtid->rxbuf + rxtid->baw_head;
-
- if (rxbuf->rx_wbuf != NULL) {
- ath_rx_subframe(an, rxbuf->rx_wbuf,
- &rxbuf->rx_status);
- rxbuf->rx_wbuf = NULL;
- }
-
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
-
- index--;
- }
- }
-
- /* add buffer to the recv ba window */
-
- cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
- rxbuf = rxtid->rxbuf + cindex;
-
- if (rxbuf->rx_wbuf != NULL) {
- spin_unlock(&rxtid->tidlock);
- /* duplicate frame */
- dev_kfree_skb(skb);
- return IEEE80211_FTYPE_DATA;
- }
-
- rxbuf->rx_wbuf = skb;
- rxbuf->rx_time = get_timestamp();
- rxbuf->rx_status = *rx_status;
-
- /* advance tail if sequence received is newer
- * than any received so far */
-
- if (index >= rxdiff) {
- rxtid->baw_tail = cindex;
- INCR(rxtid->baw_tail, ATH_TID_MAX_BUFS);
- }
-
- /* indicate all in-order received frames */
-
- while (rxtid->baw_head != rxtid->baw_tail) {
- rxbuf = rxtid->rxbuf + rxtid->baw_head;
- if (!rxbuf->rx_wbuf)
- break;
-
- ath_rx_subframe(an, rxbuf->rx_wbuf, &rxbuf->rx_status);
- rxbuf->rx_wbuf = NULL;
-
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- }
-
- /*
- * start a timer to flush all received frames if there are pending
- * receive frames
- */
- if (rxtid->baw_head != rxtid->baw_tail)
- mod_timer(&rxtid->timer, ATH_RX_TIMEOUT);
- else
- del_timer_sync(&rxtid->timer);
-
- spin_unlock(&rxtid->tidlock);
- return IEEE80211_FTYPE_DATA;
-}
-
-/* Timer to flush all received sub-frames */
-
-static void ath_rx_timer(unsigned long data)
+static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
{
- struct ath_arx_tid *rxtid = (struct ath_arx_tid *)data;
- struct ath_node *an = rxtid->an;
- struct ath_rxbuf *rxbuf;
- int nosched;
-
- spin_lock_bh(&rxtid->tidlock);
- while (rxtid->baw_head != rxtid->baw_tail) {
- rxbuf = rxtid->rxbuf + rxtid->baw_head;
- if (!rxbuf->rx_wbuf) {
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- continue;
- }
-
- /*
- * Stop if the next one is a very recent frame.
- *
- * Call get_timestamp in every iteration to protect against the
- * case in which a new frame is received while we are executing
- * this function. Using a timestamp obtained before entering
- * the loop could lead to a very large time interval
- * (a negative value typecast to unsigned), breaking the
- * function's logic.
- */
- if ((get_timestamp() - rxbuf->rx_time) <
- (ATH_RX_TIMEOUT * HZ / 1000))
- break;
-
- ath_rx_subframe(an, rxbuf->rx_wbuf,
- &rxbuf->rx_status);
- rxbuf->rx_wbuf = NULL;
-
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- }
-
- /*
- * start a timer to flush all received frames if there are pending
- * receive frames
- */
- if (rxtid->baw_head != rxtid->baw_tail)
- nosched = 0;
- else
- nosched = 1; /* no need to re-arm the timer again */
-
- spin_unlock_bh(&rxtid->tidlock);
+ /* XXX block beacon interrupts */
+ ath9k_hw_setantenna(sc->sc_ah, antenna);
+ sc->rx.defant = antenna;
+ sc->rx.rxotherant = 0;
}
-/* Free all pending sub-frames in the re-ordering buffer */
-
-static void ath_rx_flush_tid(struct ath_softc *sc,
- struct ath_arx_tid *rxtid, int drop)
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the current h/w TSF.
+*/
+static u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp)
{
- struct ath_rxbuf *rxbuf;
- unsigned long flag;
-
- spin_lock_irqsave(&rxtid->tidlock, flag);
- while (rxtid->baw_head != rxtid->baw_tail) {
- rxbuf = rxtid->rxbuf + rxtid->baw_head;
- if (!rxbuf->rx_wbuf) {
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- continue;
- }
-
- if (drop)
- dev_kfree_skb(rxbuf->rx_wbuf);
- else
- ath_rx_subframe(rxtid->an,
- rxbuf->rx_wbuf,
- &rxbuf->rx_status);
-
- rxbuf->rx_wbuf = NULL;
+ u64 tsf;
- INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
- INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
- }
- spin_unlock_irqrestore(&rxtid->tidlock, flag);
+ tsf = ath9k_hw_gettsf64(sc->sc_ah);
+ if ((tsf & 0x7fff) < rstamp)
+ tsf -= 0x8000;
+ return (tsf & ~0x7fff) | rstamp;
}
-static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc,
- u32 len)
+static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len)
{
struct sk_buff *skb;
u32 off;
@@ -414,67 +104,131 @@ static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc,
skb_reserve(skb, sc->sc_cachelsz - off);
} else {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: skbuff alloc of size %u failed\n",
- __func__, len);
+ "skbuff alloc of size %u failed\n", len);
return NULL;
}
return skb;
}
-static void ath_rx_requeue(struct ath_softc *sc, struct sk_buff *skb)
+/*
+ * For Decrypt or Demic errors, we only mark packet status here and always push
+ * up the frame up to let mac80211 handle the actual error case, be it no
+ * decryption key or real decryption error. This let us keep statistics there.
+ */
+static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
+ struct ieee80211_rx_status *rx_status, bool *decrypt_error,
+ struct ath_softc *sc)
{
- struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf;
+ struct ieee80211_hdr *hdr;
+ u8 ratecode;
+ __le16 fc;
- ASSERT(bf != NULL);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+ memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
- spin_lock_bh(&sc->sc_rxbuflock);
- if (bf->bf_status & ATH_BUFSTATUS_STALE) {
+ if (ds->ds_rxstat.rs_more) {
/*
- * This buffer is still held for hw acess.
- * Mark it as free to be re-queued it later.
+ * Frame spans multiple descriptors; this cannot happen yet
+ * as we don't support jumbograms. If not in monitor mode,
+ * discard the frame. Enable this if you want to see
+ * error frames in Monitor mode.
*/
- bf->bf_status |= ATH_BUFSTATUS_FREE;
- } else {
- /* XXX: we probably never enter here, remove after
- * verification */
- list_add_tail(&bf->list, &sc->sc_rxbuf);
- ath_rx_buf_link(sc, bf);
+ if (sc->sc_ah->ah_opmode != NL80211_IFTYPE_MONITOR)
+ goto rx_next;
+ } else if (ds->ds_rxstat.rs_status != 0) {
+ if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC)
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY)
+ goto rx_next;
+
+ if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) {
+ *decrypt_error = true;
+ } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) {
+ if (ieee80211_is_ctl(fc))
+ /*
+ * Sometimes, we get invalid
+ * MIC failures on valid control frames.
+ * Remove these mic errors.
+ */
+ ds->ds_rxstat.rs_status &= ~ATH9K_RXERR_MIC;
+ else
+ rx_status->flag |= RX_FLAG_MMIC_ERROR;
+ }
+ /*
+ * Reject error frames with the exception of
+ * decryption and MIC failures. For monitor mode,
+ * we also ignore the CRC error.
+ */
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_MONITOR) {
+ if (ds->ds_rxstat.rs_status &
+ ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_CRC))
+ goto rx_next;
+ } else {
+ if (ds->ds_rxstat.rs_status &
+ ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
+ goto rx_next;
+ }
+ }
}
- spin_unlock_bh(&sc->sc_rxbuflock);
-}
-/*
- * The skb indicated to upper stack won't be returned to us.
- * So we have to allocate a new one and queue it by ourselves.
- */
-static int ath_rx_indicate(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_recv_status *status,
- u16 keyix)
-{
- struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf;
- struct sk_buff *nskb;
- int type;
-
- /* indicate frame to the stack, which will free the old skb. */
- type = _ath_rx_indicate(sc, skb, status, keyix);
-
- /* allocate a new skb and queue it to for H/W processing */
- nskb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize);
- if (nskb != NULL) {
- bf->bf_mpdu = nskb;
- bf->bf_buf_addr = pci_map_single(sc->pdev, nskb->data,
- sc->sc_rxbufsize,
- PCI_DMA_FROMDEVICE);
- bf->bf_dmacontext = bf->bf_buf_addr;
- ATH_RX_CONTEXT(nskb)->ctx_rxbuf = bf;
+ ratecode = ds->ds_rxstat.rs_rate;
+
+ if (ratecode & 0x80) {
+ /* HT rate */
+ rx_status->flag |= RX_FLAG_HT;
+ if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040)
+ rx_status->flag |= RX_FLAG_40MHZ;
+ if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+ rx_status->rate_idx = ratecode & 0x7f;
+ } else {
+ int i = 0, cur_band, n_rates;
+ struct ieee80211_hw *hw = sc->hw;
- /* queue the new wbuf to H/W */
- ath_rx_requeue(sc, nskb);
+ cur_band = hw->conf.channel->band;
+ n_rates = sc->sbands[cur_band].n_bitrates;
+
+ for (i = 0; i < n_rates; i++) {
+ if (sc->sbands[cur_band].bitrates[i].hw_value ==
+ ratecode) {
+ rx_status->rate_idx = i;
+ break;
+ }
+
+ if (sc->sbands[cur_band].bitrates[i].hw_value_short ==
+ ratecode) {
+ rx_status->rate_idx = i;
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ break;
+ }
+ }
}
- return type;
+ rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
+ rx_status->band = sc->hw->conf.channel->band;
+ rx_status->freq = sc->hw->conf.channel->center_freq;
+ rx_status->noise = sc->sc_ani.sc_noise_floor;
+ rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi;
+ rx_status->antenna = ds->ds_rxstat.rs_antenna;
+
+ /* at 45 you will be able to use MCS 15 reliably. A more elaborate
+ * scheme can be used here but it requires tables of SNR/throughput for
+ * each possible mode used. */
+ rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45;
+
+ /* rssi can be more than 45 though, anything above that
+ * should be considered at 100% */
+ if (rx_status->qual > 100)
+ rx_status->qual = 100;
+
+ rx_status->flag |= RX_FLAG_TSFT;
+
+ return 1;
+rx_next:
+ return 0;
}
static void ath_opmode_init(struct ath_softc *sc)
@@ -498,11 +252,7 @@ static void ath_opmode_init(struct ath_softc *sc)
/* calculate and install multicast filter */
mfilt[0] = mfilt[1] = ~0;
-
ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
- DPRINTF(sc, ATH_DBG_CONFIG ,
- "%s: RX filter 0x%x, MC filter %08x:%08x\n",
- __func__, rfilt, mfilt[0], mfilt[1]);
}
int ath_rx_init(struct ath_softc *sc, int nbufs)
@@ -512,38 +262,29 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
int error = 0;
do {
- spin_lock_init(&sc->sc_rxflushlock);
+ spin_lock_init(&sc->rx.rxflushlock);
sc->sc_flags &= ~SC_OP_RXFLUSH;
- spin_lock_init(&sc->sc_rxbuflock);
+ spin_lock_init(&sc->rx.rxbuflock);
- /*
- * Cisco's VPN software requires that drivers be able to
- * receive encapsulated frames that are larger than the MTU.
- * Since we can't be sure how large a frame we'll get, setup
- * to handle the larges on possible.
- */
- sc->sc_rxbufsize = roundup(IEEE80211_MAX_MPDU_LEN,
+ sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
min(sc->sc_cachelsz,
(u16)64));
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: cachelsz %u rxbufsize %u\n",
- __func__, sc->sc_cachelsz, sc->sc_rxbufsize);
+ DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
+ sc->sc_cachelsz, sc->rx.bufsize);
/* Initialize rx descriptors */
- error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+ error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf,
"rx", nbufs, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: failed to allocate rx descriptors: %d\n",
- __func__, error);
+ "failed to allocate rx descriptors: %d\n", error);
break;
}
- /* Pre-allocate a wbuf for each rx buffer */
-
- list_for_each_entry(bf, &sc->sc_rxbuf, list) {
- skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize);
+ list_for_each_entry(bf, &sc->rx.rxbuf, list) {
+ skb = ath_rxbuf_alloc(sc, sc->rx.bufsize);
if (skb == NULL) {
error = -ENOMEM;
break;
@@ -551,12 +292,20 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
bf->bf_mpdu = skb;
bf->bf_buf_addr = pci_map_single(sc->pdev, skb->data,
- sc->sc_rxbufsize,
- PCI_DMA_FROMDEVICE);
+ sc->rx.bufsize,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(sc->pdev,
+ bf->bf_buf_addr))) {
+ dev_kfree_skb_any(skb);
+ bf->bf_mpdu = NULL;
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "pci_dma_mapping_error() on RX init\n");
+ error = -ENOMEM;
+ break;
+ }
bf->bf_dmacontext = bf->bf_buf_addr;
- ATH_RX_CONTEXT(skb)->ctx_rxbuf = bf;
}
- sc->sc_rxlink = NULL;
+ sc->rx.rxlink = NULL;
} while (0);
@@ -566,23 +315,19 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
return error;
}
-/* Reclaim all rx queue resources */
-
void ath_rx_cleanup(struct ath_softc *sc)
{
struct sk_buff *skb;
struct ath_buf *bf;
- list_for_each_entry(bf, &sc->sc_rxbuf, list) {
+ list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = bf->bf_mpdu;
if (skb)
dev_kfree_skb(skb);
}
- /* cleanup rx descriptors */
-
- if (sc->sc_rxdma.dd_desc_len != 0)
- ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+ if (sc->rx.rxdma.dd_desc_len != 0)
+ ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.rxbuf);
}
/*
@@ -615,201 +360,115 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
| ATH9K_RX_FILTER_MCAST;
/* If not a STA, enable processing of Probe Requests */
- if (sc->sc_ah->ah_opmode != ATH9K_M_STA)
+ if (sc->sc_ah->ah_opmode != NL80211_IFTYPE_STATION)
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
/* Can't set HOSTAP into promiscous mode */
- if (((sc->sc_ah->ah_opmode != ATH9K_M_HOSTAP) &&
- (sc->rx_filter & FIF_PROMISC_IN_BSS)) ||
- (sc->sc_ah->ah_opmode == ATH9K_M_MONITOR)) {
+ if (((sc->sc_ah->ah_opmode != NL80211_IFTYPE_AP) &&
+ (sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) ||
+ (sc->sc_ah->ah_opmode == NL80211_IFTYPE_MONITOR)) {
rfilt |= ATH9K_RX_FILTER_PROM;
/* ??? To prevent from sending ACK */
rfilt &= ~ATH9K_RX_FILTER_UCAST;
}
- if (((sc->sc_ah->ah_opmode == ATH9K_M_STA) &&
- (sc->rx_filter & FIF_BCN_PRBRESP_PROMISC)) ||
- (sc->sc_ah->ah_opmode == ATH9K_M_IBSS))
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_STATION ||
+ sc->sc_ah->ah_opmode == NL80211_IFTYPE_ADHOC)
rfilt |= ATH9K_RX_FILTER_BEACON;
/* If in HOSTAP mode, want to enable reception of PSPOLL frames
& beacon frames */
- if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP)
+ if (sc->sc_ah->ah_opmode == NL80211_IFTYPE_AP)
rfilt |= (ATH9K_RX_FILTER_BEACON | ATH9K_RX_FILTER_PSPOLL);
+
return rfilt;
#undef RX_FILTER_PRESERVE
}
-/* Enable the receive h/w following a reset. */
-
int ath_startrecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf, *tbf;
- spin_lock_bh(&sc->sc_rxbuflock);
- if (list_empty(&sc->sc_rxbuf))
+ spin_lock_bh(&sc->rx.rxbuflock);
+ if (list_empty(&sc->rx.rxbuf))
goto start_recv;
- sc->sc_rxlink = NULL;
- list_for_each_entry_safe(bf, tbf, &sc->sc_rxbuf, list) {
- if (bf->bf_status & ATH_BUFSTATUS_STALE) {
- /* restarting h/w, no need for holding descriptors */
- bf->bf_status &= ~ATH_BUFSTATUS_STALE;
- /*
- * Upper layer may not be done with the frame yet so
- * we can't just re-queue it to hardware. Remove it
- * from h/w queue. It'll be re-queued when upper layer
- * returns the frame and ath_rx_requeue_mpdu is called.
- */
- if (!(bf->bf_status & ATH_BUFSTATUS_FREE)) {
- list_del(&bf->list);
- continue;
- }
- }
- /* chain descriptors */
+ sc->rx.rxlink = NULL;
+ list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
ath_rx_buf_link(sc, bf);
}
/* We could have deleted elements so the list may be empty now */
- if (list_empty(&sc->sc_rxbuf))
+ if (list_empty(&sc->rx.rxbuf))
goto start_recv;
- bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list);
+ bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
- ath9k_hw_rxena(ah); /* enable recv descriptors */
+ ath9k_hw_rxena(ah);
start_recv:
- spin_unlock_bh(&sc->sc_rxbuflock);
- ath_opmode_init(sc); /* set filters, etc. */
- ath9k_hw_startpcureceive(ah); /* re-enable PCU/DMA engine */
+ spin_unlock_bh(&sc->rx.rxbuflock);
+ ath_opmode_init(sc);
+ ath9k_hw_startpcureceive(ah);
+
return 0;
}
-/* Disable the receive h/w in preparation for a reset. */
-
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
- u64 tsf;
bool stopped;
- ath9k_hw_stoppcurecv(ah); /* disable PCU */
- ath9k_hw_setrxfilter(ah, 0); /* clear recv filter */
- stopped = ath9k_hw_stopdmarecv(ah); /* disable DMA engine */
- mdelay(3); /* 3ms is long enough for 1 frame */
- tsf = ath9k_hw_gettsf64(ah);
- sc->sc_rxlink = NULL; /* just in case */
+ ath9k_hw_stoppcurecv(ah);
+ ath9k_hw_setrxfilter(ah, 0);
+ stopped = ath9k_hw_stopdmarecv(ah);
+ mdelay(3); /* 3ms is long enough for 1 frame */
+ sc->rx.rxlink = NULL;
+
return stopped;
}
-/* Flush receive queue */
-
void ath_flushrecv(struct ath_softc *sc)
{
- /*
- * ath_rx_tasklet may be used to handle rx interrupt and flush receive
- * queue at the same time. Use a lock to serialize the access of rx
- * queue.
- * ath_rx_tasklet cannot hold the spinlock while indicating packets.
- * Instead, do not claim the spinlock but check for a flush in
- * progress (see references to sc_rxflush)
- */
- spin_lock_bh(&sc->sc_rxflushlock);
+ spin_lock_bh(&sc->rx.rxflushlock);
sc->sc_flags |= SC_OP_RXFLUSH;
-
ath_rx_tasklet(sc, 1);
-
sc->sc_flags &= ~SC_OP_RXFLUSH;
- spin_unlock_bh(&sc->sc_rxflushlock);
+ spin_unlock_bh(&sc->rx.rxflushlock);
}
-/* Process an individual frame */
-
-int ath_rx_input(struct ath_softc *sc,
- struct ath_node *an,
- int is_ampdu,
- struct sk_buff *skb,
- struct ath_recv_status *rx_status,
- enum ATH_RX_TYPE *status)
-{
- if (is_ampdu && (sc->sc_flags & SC_OP_RXAGGR)) {
- *status = ATH_RX_CONSUMED;
- return ath_ampdu_input(sc, an, skb, rx_status);
- } else {
- *status = ATH_RX_NON_CONSUMED;
- return -1;
- }
-}
-
-/* Process receive queue, as well as LED, etc. */
-
int ath_rx_tasklet(struct ath_softc *sc, int flush)
{
#define PA2DESC(_sc, _pa) \
- ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
- ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+ ((struct ath_desc *)((caddr_t)(_sc)->rx.rxdma.dd_desc + \
+ ((_pa) - (_sc)->rx.rxdma.dd_desc_paddr)))
- struct ath_buf *bf, *bf_held = NULL;
+ struct ath_buf *bf;
struct ath_desc *ds;
- struct ieee80211_hdr *hdr;
- struct sk_buff *skb = NULL;
- struct ath_recv_status rx_status;
+ struct sk_buff *skb = NULL, *requeue_skb;
+ struct ieee80211_rx_status rx_status;
struct ath_hal *ah = sc->sc_ah;
- int type, rx_processed = 0;
- u32 phyerr;
- u8 chainreset = 0;
- int retval;
- __le16 fc;
+ struct ieee80211_hdr *hdr;
+ int hdrlen, padsize, retval;
+ bool decrypt_error = false;
+ u8 keyix;
+
+ spin_lock_bh(&sc->rx.rxbuflock);
do {
/* If handling rx interrupt and flush is in progress => exit */
if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0))
break;
- spin_lock_bh(&sc->sc_rxbuflock);
- if (list_empty(&sc->sc_rxbuf)) {
- sc->sc_rxlink = NULL;
- spin_unlock_bh(&sc->sc_rxbuflock);
+ if (list_empty(&sc->rx.rxbuf)) {
+ sc->rx.rxlink = NULL;
break;
}
- bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list);
-
- /*
- * There is a race condition that BH gets scheduled after sw
- * writes RxE and before hw re-load the last descriptor to get
- * the newly chained one. Software must keep the last DONE
- * descriptor as a holding descriptor - software does so by
- * marking it with the STALE flag.
- */
- if (bf->bf_status & ATH_BUFSTATUS_STALE) {
- bf_held = bf;
- if (list_is_last(&bf_held->list, &sc->sc_rxbuf)) {
- /*
- * The holding descriptor is the last
- * descriptor in queue. It's safe to
- * remove the last holding descriptor
- * in BH context.
- */
- list_del(&bf_held->list);
- bf_held->bf_status &= ~ATH_BUFSTATUS_STALE;
- sc->sc_rxlink = NULL;
-
- if (bf_held->bf_status & ATH_BUFSTATUS_FREE) {
- list_add_tail(&bf_held->list,
- &sc->sc_rxbuf);
- ath_rx_buf_link(sc, bf_held);
- }
- spin_unlock_bh(&sc->sc_rxbuflock);
- break;
- }
- bf = list_entry(bf->list.next, struct ath_buf, list);
- }
-
+ bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
ds = bf->bf_desc;
- ++rx_processed;
/*
* Must provide the virtual address of the current
@@ -822,8 +481,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
* on. All this is necessary because of our use of
* a self-linked list to avoid rx overruns.
*/
- retval = ath9k_hw_rxprocdesc(ah,
- ds,
+ retval = ath9k_hw_rxprocdesc(ah, ds,
bf->bf_daddr,
PA2DESC(sc, ds->ds_link),
0);
@@ -831,8 +489,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
struct ath_buf *tbf;
struct ath_desc *tds;
- if (list_is_last(&bf->list, &sc->sc_rxbuf)) {
- spin_unlock_bh(&sc->sc_rxbuflock);
+ if (list_is_last(&bf->list, &sc->rx.rxbuf)) {
+ sc->rx.rxlink = NULL;
break;
}
@@ -850,451 +508,127 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
*/
tds = tbf->bf_desc;
- retval = ath9k_hw_rxprocdesc(ah,
- tds, tbf->bf_daddr,
- PA2DESC(sc, tds->ds_link), 0);
+ retval = ath9k_hw_rxprocdesc(ah, tds, tbf->bf_daddr,
+ PA2DESC(sc, tds->ds_link), 0);
if (retval == -EINPROGRESS) {
- spin_unlock_bh(&sc->sc_rxbuflock);
break;
}
}
- /* XXX: we do not support frames spanning
- * multiple descriptors */
- bf->bf_status |= ATH_BUFSTATUS_DONE;
-
skb = bf->bf_mpdu;
- if (skb == NULL) { /* XXX ??? can this happen */
- spin_unlock_bh(&sc->sc_rxbuflock);
+ if (!skb)
continue;
- }
+
/*
- * Now we know it's a completed frame, we can indicate the
- * frame. Remove the previous holding descriptor and leave
- * this one in the queue as the new holding descriptor.
+ * Synchronize the DMA transfer with CPU before
+ * 1. accessing the frame
+ * 2. requeueing the same buffer to h/w
*/
- if (bf_held) {
- list_del(&bf_held->list);
- bf_held->bf_status &= ~ATH_BUFSTATUS_STALE;
- if (bf_held->bf_status & ATH_BUFSTATUS_FREE) {
- list_add_tail(&bf_held->list, &sc->sc_rxbuf);
- /* try to requeue this descriptor */
- ath_rx_buf_link(sc, bf_held);
- }
- }
+ pci_dma_sync_single_for_cpu(sc->pdev, bf->bf_buf_addr,
+ sc->rx.bufsize,
+ PCI_DMA_FROMDEVICE);
- bf->bf_status |= ATH_BUFSTATUS_STALE;
- bf_held = bf;
/*
- * Release the lock here in case ieee80211_input() return
- * the frame immediately by calling ath_rx_mpdu_requeue().
+ * If we're asked to flush receive queue, directly
+ * chain it back at the queue without processing it.
*/
- spin_unlock_bh(&sc->sc_rxbuflock);
+ if (flush)
+ goto requeue;
- if (flush) {
- /*
- * If we're asked to flush receive queue, directly
- * chain it back at the queue without processing it.
- */
- goto rx_next;
- }
+ if (!ds->ds_rxstat.rs_datalen)
+ goto requeue;
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
- memset(&rx_status, 0, sizeof(struct ath_recv_status));
+ /* The status portion of the descriptor could get corrupted. */
+ if (sc->rx.bufsize < ds->ds_rxstat.rs_datalen)
+ goto requeue;
- if (ds->ds_rxstat.rs_more) {
- /*
- * Frame spans multiple descriptors; this
- * cannot happen yet as we don't support
- * jumbograms. If not in monitor mode,
- * discard the frame.
- */
-#ifndef ERROR_FRAMES
- /*
- * Enable this if you want to see
- * error frames in Monitor mode.
- */
- if (sc->sc_ah->ah_opmode != ATH9K_M_MONITOR)
- goto rx_next;
-#endif
- /* fall thru for monitor mode handling... */
- } else if (ds->ds_rxstat.rs_status != 0) {
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC)
- rx_status.flags |= ATH_RX_FCS_ERROR;
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) {
- phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
- goto rx_next;
- }
+ if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc))
+ goto requeue;
+
+ /* Ensure we always have an skb to requeue once we are done
+ * processing the current buffer's skb */
+ requeue_skb = ath_rxbuf_alloc(sc, sc->rx.bufsize);
+
+ /* If there is no memory we ignore the current RX'd frame,
+ * tell hardware it can give us a new frame using the old
+ * skb and put it at the tail of the sc->rx.rxbuf list for
+ * processing. */
+ if (!requeue_skb)
+ goto requeue;
+
+ /* Unmap the frame */
+ pci_unmap_single(sc->pdev, bf->bf_buf_addr,
+ sc->rx.bufsize,
+ PCI_DMA_FROMDEVICE);
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) {
- /*
- * Decrypt error. We only mark packet status
- * here and always push up the frame up to let
- * mac80211 handle the actual error case, be
- * it no decryption key or real decryption
- * error. This let us keep statistics there.
- */
- rx_status.flags |= ATH_RX_DECRYPT_ERROR;
- } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) {
- /*
- * Demic error. We only mark frame status here
- * and always push up the frame up to let
- * mac80211 handle the actual error case. This
- * let us keep statistics there. Hardware may
- * post a false-positive MIC error.
- */
- if (ieee80211_is_ctl(fc))
- /*
- * Sometimes, we get invalid
- * MIC failures on valid control frames.
- * Remove these mic errors.
- */
- ds->ds_rxstat.rs_status &=
- ~ATH9K_RXERR_MIC;
- else
- rx_status.flags |= ATH_RX_MIC_ERROR;
- }
- /*
- * Reject error frames with the exception of
- * decryption and MIC failures. For monitor mode,
- * we also ignore the CRC error.
- */
- if (sc->sc_ah->ah_opmode == ATH9K_M_MONITOR) {
- if (ds->ds_rxstat.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
- ATH9K_RXERR_CRC))
- goto rx_next;
- } else {
- if (ds->ds_rxstat.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
- goto rx_next;
- }
- }
- }
- /*
- * The status portion of the descriptor could get corrupted.
- */
- if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen)
- goto rx_next;
- /*
- * Sync and unmap the frame. At this point we're
- * committed to passing the sk_buff somewhere so
- * clear buf_skb; this means a new sk_buff must be
- * allocated when the rx descriptor is setup again
- * to receive another frame.
- */
skb_put(skb, ds->ds_rxstat.rs_datalen);
skb->protocol = cpu_to_be16(ETH_P_CONTROL);
- rx_status.tsf = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
- rx_status.rateieee =
- sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate;
- rx_status.rateKbps =
- sc->sc_hwmap[ds->ds_rxstat.rs_rate].rateKbps;
- rx_status.ratecode = ds->ds_rxstat.rs_rate;
- /* HT rate */
- if (rx_status.ratecode & 0x80) {
- /* TODO - add table to avoid division */
- if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) {
- rx_status.flags |= ATH_RX_40MHZ;
- rx_status.rateKbps =
- (rx_status.rateKbps * 27) / 13;
- }
- if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
- rx_status.rateKbps =
- (rx_status.rateKbps * 10) / 9;
- else
- rx_status.flags |= ATH_RX_SHORT_GI;
+ /* see if any padding is done by the hw and remove it */
+ hdr = (struct ieee80211_hdr *)skb->data;
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+
+ /* The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = (4 - hdrlen % 4) % 4; However, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit. In addition, we must
+ * not try to remove padding from short control frames that do
+ * not have payload. */
+ padsize = hdrlen & 3;
+ if (padsize && hdrlen >= 24) {
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
}
- /* sc_noise_floor is only available when the station
- attaches to an AP, so we use a default value
- if we are not yet attached. */
- rx_status.abs_rssi =
- ds->ds_rxstat.rs_rssi + sc->sc_ani.sc_noise_floor;
-
- pci_dma_sync_single_for_cpu(sc->pdev,
- bf->bf_buf_addr,
- sc->sc_rxbufsize,
- PCI_DMA_FROMDEVICE);
- pci_unmap_single(sc->pdev,
- bf->bf_buf_addr,
- sc->sc_rxbufsize,
- PCI_DMA_FROMDEVICE);
+ keyix = ds->ds_rxstat.rs_keyix;
- /* XXX: Ah! make me more readable, use a helper */
- if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
- if (ds->ds_rxstat.rs_moreaggr == 0) {
- rx_status.rssictl[0] =
- ds->ds_rxstat.rs_rssi_ctl0;
- rx_status.rssictl[1] =
- ds->ds_rxstat.rs_rssi_ctl1;
- rx_status.rssictl[2] =
- ds->ds_rxstat.rs_rssi_ctl2;
- rx_status.rssi = ds->ds_rxstat.rs_rssi;
- if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) {
- rx_status.rssiextn[0] =
- ds->ds_rxstat.rs_rssi_ext0;
- rx_status.rssiextn[1] =
- ds->ds_rxstat.rs_rssi_ext1;
- rx_status.rssiextn[2] =
- ds->ds_rxstat.rs_rssi_ext2;
- rx_status.flags |=
- ATH_RX_RSSI_EXTN_VALID;
- }
- rx_status.flags |= ATH_RX_RSSI_VALID |
- ATH_RX_CHAIN_RSSI_VALID;
- }
- } else {
- /*
- * Need to insert the "combined" rssi into the
- * status structure for upper layer processing
- */
- rx_status.rssi = ds->ds_rxstat.rs_rssi;
- rx_status.flags |= ATH_RX_RSSI_VALID;
+ if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+ } else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
+ && !decrypt_error && skb->len >= hdrlen + 4) {
+ keyix = skb->data[hdrlen + 3] >> 6;
+
+ if (test_bit(keyix, sc->sc_keymap))
+ rx_status.flag |= RX_FLAG_DECRYPTED;
}
- /* Pass frames up to the stack. */
+ /* Send the frame to mac80211 */
+ __ieee80211_rx(sc->hw, skb, &rx_status);
- type = ath_rx_indicate(sc, skb,
- &rx_status, ds->ds_rxstat.rs_keyix);
+ /* We will now give hardware our shiny new allocated skb */
+ bf->bf_mpdu = requeue_skb;
+ bf->bf_buf_addr = pci_map_single(sc->pdev, requeue_skb->data,
+ sc->rx.bufsize,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(sc->pdev,
+ bf->bf_buf_addr))) {
+ dev_kfree_skb_any(requeue_skb);
+ bf->bf_mpdu = NULL;
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "pci_dma_mapping_error() on RX\n");
+ break;
+ }
+ bf->bf_dmacontext = bf->bf_buf_addr;
/*
* change the default rx antenna if rx diversity chooses the
* other antenna 3 times in a row.
*/
- if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
- if (++sc->sc_rxotherant >= 3)
- ath_setdefantenna(sc,
- ds->ds_rxstat.rs_antenna);
+ if (sc->rx.defant != ds->ds_rxstat.rs_antenna) {
+ if (++sc->rx.rxotherant >= 3)
+ ath_setdefantenna(sc, ds->ds_rxstat.rs_antenna);
} else {
- sc->sc_rxotherant = 0;
+ sc->rx.rxotherant = 0;
}
+requeue:
+ list_move_tail(&bf->list, &sc->rx.rxbuf);
+ ath_rx_buf_link(sc, bf);
+ } while (1);
-#ifdef CONFIG_SLOW_ANT_DIV
- if ((rx_status.flags & ATH_RX_RSSI_VALID) &&
- ieee80211_is_beacon(fc)) {
- ath_slow_ant_div(&sc->sc_antdiv, hdr, &ds->ds_rxstat);
- }
-#endif
- /*
- * For frames successfully indicated, the buffer will be
- * returned to us by upper layers by calling
- * ath_rx_mpdu_requeue, either synchronusly or asynchronously.
- * So we don't want to do it here in this loop.
- */
- continue;
-
-rx_next:
- bf->bf_status |= ATH_BUFSTATUS_FREE;
- } while (TRUE);
-
- if (chainreset) {
- DPRINTF(sc, ATH_DBG_CONFIG,
- "%s: Reset rx chain mask. "
- "Do internal reset\n", __func__);
- ASSERT(flush == 0);
- ath_reset(sc, false);
- }
+ spin_unlock_bh(&sc->rx.rxbuflock);
return 0;
#undef PA2DESC
}
-
-/* Process ADDBA request in per-TID data structure */
-
-int ath_rx_aggr_start(struct ath_softc *sc,
- const u8 *addr,
- u16 tid,
- u16 *ssn)
-{
- struct ath_arx_tid *rxtid;
- struct ath_node *an;
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_supported_band *sband;
- u16 buffersize = 0;
-
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, (u8 *) addr);
- spin_unlock_bh(&sc->node_lock);
-
- if (!an) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Node not found to initialize RX aggregation\n",
- __func__);
- return -1;
- }
-
- sband = hw->wiphy->bands[hw->conf.channel->band];
- buffersize = IEEE80211_MIN_AMPDU_BUF <<
- sband->ht_info.ampdu_factor; /* FIXME */
-
- rxtid = &an->an_aggr.rx.tid[tid];
-
- spin_lock_bh(&rxtid->tidlock);
- if (sc->sc_flags & SC_OP_RXAGGR) {
- /* Allow aggregation reception
- * Adjust rx BA window size. Peer might indicate a
- * zero buffer size for a _dont_care_ condition.
- */
- if (buffersize)
- rxtid->baw_size = min(buffersize, rxtid->baw_size);
-
- /* set rx sequence number */
- rxtid->seq_next = *ssn;
-
- /* Allocate the receive buffers for this TID */
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Allcating rxbuffer for TID %d\n", __func__, tid);
-
- if (rxtid->rxbuf == NULL) {
- /*
- * If the rxbuff is not NULL at this point, we *probably*
- * already allocated the buffer on a previous ADDBA,
- * and this is a subsequent ADDBA that got through.
- * Don't allocate, but use the value in the pointer,
- * we zero it out when we de-allocate.
- */
- rxtid->rxbuf = kmalloc(ATH_TID_MAX_BUFS *
- sizeof(struct ath_rxbuf), GFP_ATOMIC);
- }
- if (rxtid->rxbuf == NULL) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Unable to allocate RX buffer, "
- "refusing ADDBA\n", __func__);
- } else {
- /* Ensure the memory is zeroed out (all internal
- * pointers are null) */
- memset(rxtid->rxbuf, 0, ATH_TID_MAX_BUFS *
- sizeof(struct ath_rxbuf));
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Allocated @%p\n", __func__, rxtid->rxbuf);
-
- /* Allow aggregation reception */
- rxtid->addba_exchangecomplete = 1;
- }
- }
- spin_unlock_bh(&rxtid->tidlock);
-
- return 0;
-}
-
-/* Process DELBA */
-
-int ath_rx_aggr_stop(struct ath_softc *sc,
- const u8 *addr,
- u16 tid)
-{
- struct ath_node *an;
-
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, (u8 *) addr);
- spin_unlock_bh(&sc->node_lock);
-
- if (!an) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: RX aggr stop for non-existent node\n", __func__);
- return -1;
- }
-
- ath_rx_aggr_teardown(sc, an, tid);
- return 0;
-}
-
-/* Rx aggregation tear down */
-
-void ath_rx_aggr_teardown(struct ath_softc *sc,
- struct ath_node *an, u8 tid)
-{
- struct ath_arx_tid *rxtid = &an->an_aggr.rx.tid[tid];
-
- if (!rxtid->addba_exchangecomplete)
- return;
-
- del_timer_sync(&rxtid->timer);
- ath_rx_flush_tid(sc, rxtid, 0);
- rxtid->addba_exchangecomplete = 0;
-
- /* De-allocate the receive buffer array allocated when addba started */
-
- if (rxtid->rxbuf) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Deallocating TID %d rxbuff @%p\n",
- __func__, tid, rxtid->rxbuf);
- kfree(rxtid->rxbuf);
-
- /* Set pointer to null to avoid reuse*/
- rxtid->rxbuf = NULL;
- }
-}
-
-/* Initialize per-node receive state */
-
-void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an)
-{
- if (sc->sc_flags & SC_OP_RXAGGR) {
- struct ath_arx_tid *rxtid;
- int tidno;
-
- /* Init per tid rx state */
- for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno];
- tidno < WME_NUM_TID;
- tidno++, rxtid++) {
- rxtid->an = an;
- rxtid->seq_reset = 1;
- rxtid->seq_next = 0;
- rxtid->baw_size = WME_MAX_BA;
- rxtid->baw_head = rxtid->baw_tail = 0;
-
- /*
- * Ensure the buffer pointer is null at this point
- * (needs to be allocated when addba is received)
- */
-
- rxtid->rxbuf = NULL;
- setup_timer(&rxtid->timer, ath_rx_timer,
- (unsigned long)rxtid);
- spin_lock_init(&rxtid->tidlock);
-
- /* ADDBA state */
- rxtid->addba_exchangecomplete = 0;
- }
- }
-}
-
-void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
-{
- if (sc->sc_flags & SC_OP_RXAGGR) {
- struct ath_arx_tid *rxtid;
- int tidno, i;
-
- /* Init per tid rx state */
- for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno];
- tidno < WME_NUM_TID;
- tidno++, rxtid++) {
-
- if (!rxtid->addba_exchangecomplete)
- continue;
-
- /* must cancel timer first */
- del_timer_sync(&rxtid->timer);
-
- /* drop any pending sub-frames */
- ath_rx_flush_tid(sc, rxtid, 1);
-
- for (i = 0; i < ATH_TID_MAX_BUFS; i++)
- ASSERT(rxtid->rxbuf[i].rx_wbuf == NULL);
-
- rxtid->addba_exchangecomplete = 0;
- }
- }
-
-}
-
-/* Cleanup per-node receive state */
-
-void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an)
-{
- ath_rx_node_cleanup(sc, an);
-}
diff --git a/drivers/net/wireless/ath9k/reg.h b/drivers/net/wireless/ath9k/reg.h
index 60617ae66209..9fedb4911bc3 100644
--- a/drivers/net/wireless/ath9k/reg.h
+++ b/drivers/net/wireless/ath9k/reg.h
@@ -671,7 +671,11 @@
#define AR_RC_APB 0x00000002
#define AR_RC_HOSTIF 0x00000100
-#define AR_WA 0x4004
+#define AR_WA 0x4004
+#define AR9285_WA_DEFAULT 0x004a05cb
+#define AR9280_WA_DEFAULT 0x0040073f
+#define AR_WA_DEFAULT 0x0000073f
+
#define AR_PM_STATE 0x4008
#define AR_PM_STATE_PME_D3COLD_VAUX 0x00100000
@@ -738,6 +742,8 @@
#define AR_SREV_REVISION_9280_21 2
#define AR_SREV_VERSION_9285 0xC0
#define AR_SREV_REVISION_9285_10 0
+#define AR_SREV_REVISION_9285_11 1
+#define AR_SREV_REVISION_9285_12 2
#define AR_SREV_9100_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_5416_PCIE))
@@ -768,6 +774,16 @@
#define AR_SREV_9285(_ah) (((_ah)->ah_macVersion == AR_SREV_VERSION_9285))
#define AR_SREV_9285_10_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9285))
+#define AR_SREV_9285_11(_ah) \
+ (AR_SREV_9280(ah) && ((_ah)->ah_macRev == AR_SREV_REVISION_9285_11))
+#define AR_SREV_9285_11_OR_LATER(_ah) \
+ (((_ah)->ah_macVersion > AR_SREV_VERSION_9285) || \
+ (AR_SREV_9285(ah) && ((_ah)->ah_macRev >= AR_SREV_REVISION_9285_11)))
+#define AR_SREV_9285_12(_ah) \
+ (AR_SREV_9280(ah) && ((_ah)->ah_macRev == AR_SREV_REVISION_9285_12))
+#define AR_SREV_9285_12_OR_LATER(_ah) \
+ (((_ah)->ah_macVersion > AR_SREV_VERSION_9285) || \
+ (AR_SREV_9285(ah) && ((_ah)->ah_macRev >= AR_SREV_REVISION_9285_12)))
#define AR_RADIO_SREV_MAJOR 0xf0
#define AR_RAD5133_SREV_MAJOR 0xc0
@@ -1017,6 +1033,97 @@ enum {
#define AR_AN_SYNTH9_REFDIVA 0xf8000000
#define AR_AN_SYNTH9_REFDIVA_S 27
+#define AR9285_AN_RF2G1 0x7820
+#define AR9285_AN_RF2G1_ENPACAL 0x00000800
+#define AR9285_AN_RF2G1_ENPACAL_S 11
+#define AR9285_AN_RF2G1_PDPADRV1 0x02000000
+#define AR9285_AN_RF2G1_PDPADRV1_S 25
+#define AR9285_AN_RF2G1_PDPADRV2 0x01000000
+#define AR9285_AN_RF2G1_PDPADRV2_S 24
+#define AR9285_AN_RF2G1_PDPAOUT 0x00800000
+#define AR9285_AN_RF2G1_PDPAOUT_S 23
+
+
+#define AR9285_AN_RF2G2 0x7824
+#define AR9285_AN_RF2G2_OFFCAL 0x00001000
+#define AR9285_AN_RF2G2_OFFCAL_S 12
+
+#define AR9285_AN_RF2G3 0x7828
+#define AR9285_AN_RF2G3_PDVCCOMP 0x02000000
+#define AR9285_AN_RF2G3_PDVCCOMP_S 25
+#define AR9285_AN_RF2G3_OB_0 0x00E00000
+#define AR9285_AN_RF2G3_OB_0_S 21
+#define AR9285_AN_RF2G3_OB_1 0x001C0000
+#define AR9285_AN_RF2G3_OB_1_S 18
+#define AR9285_AN_RF2G3_OB_2 0x00038000
+#define AR9285_AN_RF2G3_OB_2_S 15
+#define AR9285_AN_RF2G3_OB_3 0x00007000
+#define AR9285_AN_RF2G3_OB_3_S 12
+#define AR9285_AN_RF2G3_OB_4 0x00000E00
+#define AR9285_AN_RF2G3_OB_4_S 9
+
+#define AR9285_AN_RF2G3_DB1_0 0x000001C0
+#define AR9285_AN_RF2G3_DB1_0_S 6
+#define AR9285_AN_RF2G3_DB1_1 0x00000038
+#define AR9285_AN_RF2G3_DB1_1_S 3
+#define AR9285_AN_RF2G3_DB1_2 0x00000007
+#define AR9285_AN_RF2G3_DB1_2_S 0
+#define AR9285_AN_RF2G4 0x782C
+#define AR9285_AN_RF2G4_DB1_3 0xE0000000
+#define AR9285_AN_RF2G4_DB1_3_S 29
+#define AR9285_AN_RF2G4_DB1_4 0x1C000000
+#define AR9285_AN_RF2G4_DB1_4_S 26
+
+#define AR9285_AN_RF2G4_DB2_0 0x03800000
+#define AR9285_AN_RF2G4_DB2_0_S 23
+#define AR9285_AN_RF2G4_DB2_1 0x00700000
+#define AR9285_AN_RF2G4_DB2_1_S 20
+#define AR9285_AN_RF2G4_DB2_2 0x000E0000
+#define AR9285_AN_RF2G4_DB2_2_S 17
+#define AR9285_AN_RF2G4_DB2_3 0x0001C000
+#define AR9285_AN_RF2G4_DB2_3_S 14
+#define AR9285_AN_RF2G4_DB2_4 0x00003800
+#define AR9285_AN_RF2G4_DB2_4_S 11
+
+#define AR9285_AN_RF2G6 0x7834
+#define AR9285_AN_RF2G6_CCOMP 0x00007800
+#define AR9285_AN_RF2G6_CCOMP_S 11
+#define AR9285_AN_RF2G6_OFFS 0x03f00000
+#define AR9285_AN_RF2G6_OFFS_S 20
+
+#define AR9285_AN_RF2G7 0x7838
+#define AR9285_AN_RF2G7_PWDDB 0x00000002
+#define AR9285_AN_RF2G7_PWDDB_S 1
+#define AR9285_AN_RF2G7_PADRVGN2TAB0 0xE0000000
+#define AR9285_AN_RF2G7_PADRVGN2TAB0_S 29
+
+#define AR9285_AN_RF2G8 0x783C
+#define AR9285_AN_RF2G8_PADRVGN2TAB0 0x0001C000
+#define AR9285_AN_RF2G8_PADRVGN2TAB0_S 14
+
+
+#define AR9285_AN_RF2G9 0x7840
+#define AR9285_AN_RXTXBB1 0x7854
+#define AR9285_AN_RXTXBB1_PDRXTXBB1 0x00000020
+#define AR9285_AN_RXTXBB1_PDRXTXBB1_S 5
+#define AR9285_AN_RXTXBB1_PDV2I 0x00000080
+#define AR9285_AN_RXTXBB1_PDV2I_S 7
+#define AR9285_AN_RXTXBB1_PDDACIF 0x00000100
+#define AR9285_AN_RXTXBB1_PDDACIF_S 8
+#define AR9285_AN_RXTXBB1_SPARE9 0x00000001
+#define AR9285_AN_RXTXBB1_SPARE9_S 0
+
+#define AR9285_AN_TOP2 0x7868
+
+#define AR9285_AN_TOP3 0x786c
+#define AR9285_AN_TOP3_XPABIAS_LVL 0x0000000C
+#define AR9285_AN_TOP3_XPABIAS_LVL_S 2
+#define AR9285_AN_TOP3_PWDDAC 0x00800000
+#define AR9285_AN_TOP3_PWDDAC_S 23
+
+#define AR9285_AN_TOP4 0x7870
+#define AR9285_AN_TOP4_DEFAULT 0x10142c00
+
#define AR_STA_ID0 0x8000
#define AR_STA_ID1 0x8004
#define AR_STA_ID1_SADH_MASK 0x0000FFFF
diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c
index 62e28887ccd3..64043e99facf 100644
--- a/drivers/net/wireless/ath9k/regd.c
+++ b/drivers/net/wireless/ath9k/regd.c
@@ -42,7 +42,7 @@ ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp)
u8 *u = t - size;
if (cmp(u, t) <= 0)
break;
- swap(u, t, size);
+ swap_array(u, t, size);
}
}
@@ -78,8 +78,7 @@ static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
return true;
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: invalid regulatory domain/country code 0x%x\n",
- __func__, rd);
+ "invalid regulatory domain/country code 0x%x\n", rd);
return false;
}
@@ -107,13 +106,12 @@ static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
return true;
rd = ath9k_regd_get_eepromRD(ah);
- DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
- __func__, rd);
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "EEPROM regdomain 0x%x\n", rd);
if (rd & COUNTRY_ERD_FLAG) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: EEPROM setting is country code %u\n",
- __func__, rd & ~COUNTRY_ERD_FLAG);
+ "EEPROM setting is country code %u\n",
+ rd & ~COUNTRY_ERD_FLAG);
return cc == (rd & ~COUNTRY_ERD_FLAG);
}
@@ -290,8 +288,7 @@ ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
}
if (!found) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Failed to find reg domain pair %u\n",
- __func__, regDmn);
+ "Failed to find reg domain pair %u\n", regDmn);
return false;
}
if (!(channelFlag & CHANNEL_2GHZ)) {
@@ -307,8 +304,7 @@ ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
found = ath9k_regd_is_valid_reg_domain(regDmn, rd);
if (!found) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Failed to find unitary reg domain %u\n",
- __func__, regDmn);
+ "Failed to find unitary reg domain %u\n", regDmn);
return false;
} else {
rd->pscan &= regPair->pscanMask;
@@ -430,30 +426,27 @@ ath9k_regd_add_channel(struct ath_hal *ah,
if (!(c_lo <= c && c <= c_hi)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: c %u out of range [%u..%u]\n",
- __func__, c, c_lo, c_hi);
+ "c %u out of range [%u..%u]\n",
+ c, c_lo, c_hi);
return false;
}
if ((fband->channelBW == CHANNEL_HALF_BW) &&
!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_HALFRATE)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Skipping %u half rate channel\n",
- __func__, c);
+ "Skipping %u half rate channel\n", c);
return false;
}
if ((fband->channelBW == CHANNEL_QUARTER_BW) &&
!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_QUARTERRATE)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Skipping %u quarter rate channel\n",
- __func__, c);
+ "Skipping %u quarter rate channel\n", c);
return false;
}
if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: c %u > maxChan %u\n",
- __func__, c, maxChan);
+ "c %u > maxChan %u\n", c, maxChan);
return false;
}
@@ -463,7 +456,7 @@ ath9k_regd_add_channel(struct ath_hal *ah,
return false;
}
- if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == ATH9K_M_HOSTAP)) {
+ if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == NL80211_IFTYPE_AP)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping HOSTAP channel\n");
return false;
@@ -606,8 +599,7 @@ static bool ath9k_regd_japan_check(struct ath_hal *ah,
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Skipping %d freq band\n",
- __func__, j_bandcheck[i].freqbandbit);
+ "Skipping %d freq band\n", j_bandcheck[i].freqbandbit);
return skipband;
}
@@ -632,20 +624,19 @@ ath9k_regd_init_channels(struct ath_hal *ah,
unsigned long *modes_avail;
DECLARE_BITMAP(modes_allowed, ATH9K_MODE_MAX);
- DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u %s %s\n",
- __func__, cc,
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "cc %u %s %s\n", cc,
enableOutdoor ? "Enable outdoor" : "",
enableExtendedChannels ? "Enable ecm" : "");
if (!ath9k_regd_is_ccode_valid(ah, cc)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: invalid country code %d\n", __func__, cc);
+ "Invalid country code %d\n", cc);
return false;
}
if (!ath9k_regd_is_eeprom_valid(ah)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: invalid EEPROM contents\n", __func__);
+ "Invalid EEPROM contents\n");
return false;
}
@@ -693,9 +684,9 @@ ath9k_regd_init_channels(struct ath_hal *ah,
~CHANNEL_2GHZ,
&rd5GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: couldn't find unitary "
+ "Couldn't find unitary "
"5GHz reg domain for country %u\n",
- __func__, ah->ah_countryCode);
+ ah->ah_countryCode);
return false;
}
if (!ath9k_regd_get_wmode_regdomain(ah,
@@ -703,9 +694,9 @@ ath9k_regd_init_channels(struct ath_hal *ah,
CHANNEL_2GHZ,
&rd2GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: couldn't find unitary 2GHz "
+ "Couldn't find unitary 2GHz "
"reg domain for country %u\n",
- __func__, ah->ah_countryCode);
+ ah->ah_countryCode);
return false;
}
@@ -717,9 +708,9 @@ ath9k_regd_init_channels(struct ath_hal *ah,
~CHANNEL_2GHZ,
&rd5GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: couldn't find unitary 5GHz "
+ "Couldn't find unitary 5GHz "
"reg domain for country %u\n",
- __func__, ah->ah_countryCode);
+ ah->ah_countryCode);
return false;
}
}
@@ -749,15 +740,14 @@ ath9k_regd_init_channels(struct ath_hal *ah,
if (!test_bit(cm->mode, modes_avail)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: !avail mode %d flags 0x%x\n",
- __func__, cm->mode, cm->flags);
+ "!avail mode %d flags 0x%x\n",
+ cm->mode, cm->flags);
continue;
}
if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: channels 0x%x not supported "
- "by hardware\n",
- __func__, cm->flags);
+ "channels 0x%x not supported "
+ "by hardware\n", cm->flags);
continue;
}
@@ -788,8 +778,7 @@ ath9k_regd_init_channels(struct ath_hal *ah,
break;
default:
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: Unknown HAL mode 0x%x\n", __func__,
- cm->mode);
+ "Unknown HAL mode 0x%x\n", cm->mode);
continue;
}
@@ -841,9 +830,8 @@ ath9k_regd_init_channels(struct ath_hal *ah,
if (next >= maxchans) {
DPRINTF(ah->ah_sc,
ATH_DBG_REGULATORY,
- "%s: too many channels "
- "for channel table\n",
- __func__);
+ "too many channels "
+ "for channel table\n");
goto done;
}
if (ath9k_regd_add_channel(ah,
@@ -869,9 +857,8 @@ done:
if (next > ARRAY_SIZE(ah->ah_channels)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: too many channels %u; truncating to %u\n",
- __func__, next,
- (int) ARRAY_SIZE(ah->ah_channels));
+ "too many channels %u; truncating to %u\n",
+ next, (int) ARRAY_SIZE(ah->ah_channels));
next = ARRAY_SIZE(ah->ah_channels);
}
#ifdef ATH_NF_PER_CHAN
@@ -919,7 +906,7 @@ ath9k_regd_check_channel(struct ath_hal *ah,
int n, lim;
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: channel %u/0x%x (0x%x) requested\n", __func__,
+ "channel %u/0x%x (0x%x) requested\n",
c->channel, c->channelFlags, flags);
cc = ah->ah_curchan;
@@ -950,15 +937,15 @@ ath9k_regd_check_channel(struct ath_hal *ah,
d = flags - (cc->channelFlags & CHAN_FLAGS);
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "%s: channel %u/0x%x d %d\n", __func__,
+ "channel %u/0x%x d %d\n",
cc->channel, cc->channelFlags, d);
if (d > 0) {
base = cc + 1;
lim--;
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: no match for %u/0x%x\n",
- __func__, c->channel, c->channelFlags);
+ DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "no match for %u/0x%x\n",
+ c->channel, c->channelFlags);
return NULL;
}
diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h
index 0ecd344fbd98..512d990aa7ea 100644
--- a/drivers/net/wireless/ath9k/regd.h
+++ b/drivers/net/wireless/ath9k/regd.h
@@ -125,7 +125,7 @@
#define CHAN_FLAGS (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER)
-#define swap(_a, _b, _size) { \
+#define swap_array(_a, _b, _size) { \
u8 *s = _b; \
int i = _size; \
do { \
diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c
index 3a4757942b3f..3bfc3b90f256 100644
--- a/drivers/net/wireless/ath9k/xmit.c
+++ b/drivers/net/wireless/ath9k/xmit.c
@@ -14,10 +14,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/*
- * Implementation of transmit path.
- */
-
#include "core.h"
#define BITS_PER_BYTE 8
@@ -65,11 +61,12 @@ static u32 bits_per_symbol[][2] = {
* NB: must be called with txq lock held
*/
-static void ath_tx_txqaddbuf(struct ath_softc *sc,
- struct ath_txq *txq, struct list_head *head)
+static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
+ struct list_head *head)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
+
/*
* Insert the frame on the outbound list and
* pass it on to the hardware.
@@ -86,18 +83,16 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc,
txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
DPRINTF(sc, ATH_DBG_QUEUE,
- "%s: txq depth = %d\n", __func__, txq->axq_depth);
+ "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
if (txq->axq_link == NULL) {
ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
DPRINTF(sc, ATH_DBG_XMIT,
- "%s: TXDP[%u] = %llx (%p)\n",
- __func__, txq->axq_qnum,
- ito64(bf->bf_daddr), bf->bf_desc);
+ "TXDP[%u] = %llx (%p)\n",
+ txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
} else {
*txq->axq_link = bf->bf_daddr;
- DPRINTF(sc, ATH_DBG_XMIT, "%s: link[%u] (%p)=%llx (%p)\n",
- __func__,
+ DPRINTF(sc, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
txq->axq_qnum, txq->axq_link,
ito64(bf->bf_daddr), bf->bf_desc);
}
@@ -105,46 +100,94 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc,
ath9k_hw_txstart(ah, txq->axq_qnum);
}
-/* Get transmit rate index using rate in Kbps */
-
-static int ath_tx_findindex(const struct ath9k_rate_table *rt, int rate)
+static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
+ struct ath_xmit_status *tx_status)
{
- int i;
- int ndx = 0;
+ struct ieee80211_hw *hw = sc->hw;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+ int hdrlen, padsize;
- for (i = 0; i < rt->rateCount; i++) {
- if (rt->info[i].rateKbps == rate) {
- ndx = i;
- break;
- }
+ DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
+
+ if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
+ tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+ kfree(tx_info_priv);
+ tx_info->rate_driver_data[0] = NULL;
+ }
+
+ if (tx_status->flags & ATH_TX_BAR) {
+ tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+ tx_status->flags &= ~ATH_TX_BAR;
+ }
+
+ if (!(tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY))) {
+ /* Frame was ACKed */
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+
+ tx_info->status.rates[0].count = tx_status->retries;
+ if (tx_info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+ /* Change idx from internal table index to MCS index */
+ int idx = tx_info->status.rates[0].idx;
+ struct ath_rate_table *rate_table = sc->cur_rate_table;
+ if (idx >= 0 && idx < rate_table->rate_cnt)
+ tx_info->status.rates[0].idx =
+ rate_table->info[idx].ratecode & 0x7f;
+ }
+
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ padsize = hdrlen & 3;
+ if (padsize && hdrlen >= 24) {
+ /*
+ * Remove MAC header padding before giving the frame back to
+ * mac80211.
+ */
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
}
- return ndx;
+ ieee80211_tx_status(hw, skb);
}
/* Check if it's okay to send out aggregates */
-static int ath_aggr_query(struct ath_softc *sc,
- struct ath_node *an, u8 tidno)
+static int ath_aggr_query(struct ath_softc *sc, struct ath_node *an, u8 tidno)
{
struct ath_atx_tid *tid;
tid = ATH_AN_2_TID(an, tidno);
- if (tid->addba_exchangecomplete || tid->addba_exchangeinprogress)
+ if (tid->state & AGGR_ADDBA_COMPLETE ||
+ tid->state & AGGR_ADDBA_PROGRESS)
return 1;
else
return 0;
}
-static enum ath9k_pkt_type get_hal_packet_type(struct ieee80211_hdr *hdr)
+static void ath_get_beaconconfig(struct ath_softc *sc, int if_id,
+ struct ath_beacon_config *conf)
+{
+ struct ieee80211_hw *hw = sc->hw;
+
+ /* fill in beacon config data */
+
+ conf->beacon_interval = hw->conf.beacon_int;
+ conf->listen_interval = 100;
+ conf->dtim_count = 1;
+ conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
+}
+
+/* Calculate Atheros packet type from IEEE80211 packet header */
+
+static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
{
+ struct ieee80211_hdr *hdr;
enum ath9k_pkt_type htype;
__le16 fc;
+ hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
- /* Calculate Atheros packet type from IEEE80211 packet header */
-
if (ieee80211_is_beacon(fc))
htype = ATH9K_PKT_TYPE_BEACON;
else if (ieee80211_is_probe_resp(fc))
@@ -159,232 +202,123 @@ static enum ath9k_pkt_type get_hal_packet_type(struct ieee80211_hdr *hdr)
return htype;
}
-static void fill_min_rates(struct sk_buff *skb, struct ath_tx_control *txctl)
+static bool is_pae(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ath_tx_info_priv *tx_info_priv;
__le16 fc;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) {
- txctl->use_minrate = 1;
- txctl->min_rate = tx_info_priv->min_rate;
- } else if (ieee80211_is_data(fc)) {
+ if (ieee80211_is_data(fc)) {
if (ieee80211_is_nullfunc(fc) ||
- /* Port Access Entity (IEEE 802.1X) */
- (skb->protocol == cpu_to_be16(0x888E))) {
- txctl->use_minrate = 1;
- txctl->min_rate = tx_info_priv->min_rate;
+ /* Port Access Entity (IEEE 802.1X) */
+ (skb->protocol == cpu_to_be16(ETH_P_PAE))) {
+ return true;
}
- if (is_multicast_ether_addr(hdr->addr1))
- txctl->mcast_rate = tx_info_priv->min_rate;
}
+ return false;
}
-/* This function will setup additional txctl information, mostly rate stuff */
-/* FIXME: seqno, ps */
-static int ath_tx_prepare(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_tx_control *txctl)
+static int get_hw_crypto_keytype(struct sk_buff *skb)
{
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_hdr *hdr;
- struct ath_rc_series *rcs;
- struct ath_txq *txq = NULL;
- const struct ath9k_rate_table *rt;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ath_tx_info_priv *tx_info_priv;
- int hdrlen;
- u8 rix, antenna;
- __le16 fc;
- u8 *qc;
-
- txctl->dev = sc;
- hdr = (struct ieee80211_hdr *)skb->data;
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- fc = hdr->frame_control;
-
- rt = sc->sc_currates;
- BUG_ON(!rt);
-
- /* Fill misc fields */
-
- spin_lock_bh(&sc->node_lock);
- txctl->an = ath_node_get(sc, hdr->addr1);
- /* create a temp node, if the node is not there already */
- if (!txctl->an)
- txctl->an = ath_node_attach(sc, hdr->addr1, 0);
- spin_unlock_bh(&sc->node_lock);
-
- if (ieee80211_is_data_qos(fc)) {
- qc = ieee80211_get_qos_ctl(hdr);
- txctl->tidno = qc[0] & 0xf;
- }
-
- txctl->if_id = 0;
- txctl->frmlen = skb->len + FCS_LEN - (hdrlen & 3);
- txctl->txpower = MAX_RATE_POWER; /* FIXME */
-
- /* Fill Key related fields */
-
- txctl->keytype = ATH9K_KEY_TYPE_CLEAR;
- txctl->keyix = ATH9K_TXKEYIX_INVALID;
if (tx_info->control.hw_key) {
- txctl->keyix = tx_info->control.hw_key->hw_key_idx;
- txctl->frmlen += tx_info->control.hw_key->icv_len;
-
if (tx_info->control.hw_key->alg == ALG_WEP)
- txctl->keytype = ATH9K_KEY_TYPE_WEP;
+ return ATH9K_KEY_TYPE_WEP;
else if (tx_info->control.hw_key->alg == ALG_TKIP)
- txctl->keytype = ATH9K_KEY_TYPE_TKIP;
+ return ATH9K_KEY_TYPE_TKIP;
else if (tx_info->control.hw_key->alg == ALG_CCMP)
- txctl->keytype = ATH9K_KEY_TYPE_AES;
- }
-
- /* Fill packet type */
-
- txctl->atype = get_hal_packet_type(hdr);
-
- /* Fill qnum */
-
- if (unlikely(txctl->flags & ATH9K_TXDESC_CAB)) {
- txctl->qnum = 0;
- txq = sc->sc_cabq;
- } else {
- txctl->qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
- txq = &sc->sc_txq[txctl->qnum];
- }
- spin_lock_bh(&txq->axq_lock);
-
- /* Try to avoid running out of descriptors */
- if (txq->axq_depth >= (ATH_TXBUF - 20) &&
- !(txctl->flags & ATH9K_TXDESC_CAB)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "%s: TX queue: %d is full, depth: %d\n",
- __func__,
- txctl->qnum,
- txq->axq_depth);
- ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
- txq->stopped = 1;
- spin_unlock_bh(&txq->axq_lock);
- return -1;
+ return ATH9K_KEY_TYPE_AES;
}
- spin_unlock_bh(&txq->axq_lock);
-
- /* Fill rate */
-
- fill_min_rates(skb, txctl);
+ return ATH9K_KEY_TYPE_CLEAR;
+}
- /* Fill flags */
+/* Called only when tx aggregation is enabled and HT is supported */
- txctl->flags |= ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */
+static void assign_aggr_tid_seqno(struct sk_buff *skb,
+ struct ath_buf *bf)
+{
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr;
+ struct ath_node *an;
+ struct ath_atx_tid *tid;
+ __le16 fc;
+ u8 *qc;
- if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
- txctl->flags |= ATH9K_TXDESC_NOACK;
- if (tx_info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
- txctl->flags |= ATH9K_TXDESC_RTSENA;
+ if (!tx_info->control.sta)
+ return;
- /*
- * Setup for rate calculations.
- */
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- rcs = tx_info_priv->rcs;
+ an = (struct ath_node *)tx_info->control.sta->drv_priv;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
- if (ieee80211_is_data(fc) && !txctl->use_minrate) {
+ /* Get tidno */
- /* Enable HT only for DATA frames and not for EAPOL */
- txctl->ht = (hw->conf.ht_conf.ht_supported &&
- (tx_info->flags & IEEE80211_TX_CTL_AMPDU));
+ if (ieee80211_is_data_qos(fc)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ bf->bf_tidno = qc[0] & 0xf;
+ }
- if (is_multicast_ether_addr(hdr->addr1)) {
- rcs[0].rix = (u8)
- ath_tx_findindex(rt, txctl->mcast_rate);
+ /* Get seqno */
- /*
- * mcast packets are not re-tried.
- */
- rcs[0].tries = 1;
- }
+ if (ieee80211_is_data(fc) && !is_pae(skb)) {
/* For HT capable stations, we save tidno for later use.
* We also override seqno set by upper layer with the one
* in tx aggregation state.
*
- * First, the fragmentation stat is determined.
* If fragmentation is on, the sequence number is
* not overridden, since it has been
* incremented by the fragmentation routine.
+ *
+ * FIXME: check if the fragmentation threshold exceeds
+ * IEEE80211 max.
*/
- if (likely(!(txctl->flags & ATH9K_TXDESC_FRAG_IS_ON)) &&
- txctl->ht && (sc->sc_flags & SC_OP_TXAGGR)) {
- struct ath_atx_tid *tid;
+ tid = ATH_AN_2_TID(an, bf->bf_tidno);
+ hdr->seq_ctrl = cpu_to_le16(tid->seq_next <<
+ IEEE80211_SEQ_SEQ_SHIFT);
+ bf->bf_seqno = tid->seq_next;
+ INCR(tid->seq_next, IEEE80211_SEQ_MAX);
+ }
+}
- tid = ATH_AN_2_TID(txctl->an, txctl->tidno);
+static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb,
+ struct ath_txq *txq)
+{
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ int flags = 0;
- hdr->seq_ctrl = cpu_to_le16(tid->seq_next <<
- IEEE80211_SEQ_SEQ_SHIFT);
- txctl->seqno = tid->seq_next;
- INCR(tid->seq_next, IEEE80211_SEQ_MAX);
- }
- } else {
- /* for management and control frames,
- * or for NULL and EAPOL frames */
- if (txctl->min_rate)
- rcs[0].rix = ath_rate_findrateix(sc, txctl->min_rate);
- else
- rcs[0].rix = 0;
- rcs[0].tries = ATH_MGT_TXMAXTRY;
- }
- rix = rcs[0].rix;
+ flags |= ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */
+ flags |= ATH9K_TXDESC_INTREQ;
- if (ieee80211_has_morefrags(fc) ||
- (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
- /*
- ** Force hardware to use computed duration for next
- ** fragment by disabling multi-rate retry, which
- ** updates duration based on the multi-rate
- ** duration table.
- */
- rcs[1].tries = rcs[2].tries = rcs[3].tries = 0;
- rcs[1].rix = rcs[2].rix = rcs[3].rix = 0;
- /* reset tries but keep rate index */
- rcs[0].tries = ATH_TXMAXTRY;
- }
+ if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+ flags |= ATH9K_TXDESC_NOACK;
+ if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ flags |= ATH9K_TXDESC_RTSENA;
- /*
- * Determine if a tx interrupt should be generated for
- * this descriptor. We take a tx interrupt to reap
- * descriptors when the h/w hits an EOL condition or
- * when the descriptor is specifically marked to generate
- * an interrupt. We periodically mark descriptors in this
- * way to insure timely replenishing of the supply needed
- * for sending frames. Defering interrupts reduces system
- * load and potentially allows more concurrent work to be
- * done but if done to aggressively can cause senders to
- * backup.
- *
- * NB: use >= to deal with sc_txintrperiod changing
- * dynamically through sysctl.
- */
- spin_lock_bh(&txq->axq_lock);
- if ((++txq->axq_intrcnt >= sc->sc_txintrperiod)) {
- txctl->flags |= ATH9K_TXDESC_INTREQ;
- txq->axq_intrcnt = 0;
- }
- spin_unlock_bh(&txq->axq_lock);
+ return flags;
+}
+
+static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc)
+{
+ struct ath_buf *bf = NULL;
- if (is_multicast_ether_addr(hdr->addr1)) {
- antenna = sc->sc_mcastantenna + 1;
- sc->sc_mcastantenna = (sc->sc_mcastantenna + 1) & 0x1;
+ spin_lock_bh(&sc->tx.txbuflock);
+
+ if (unlikely(list_empty(&sc->tx.txbuf))) {
+ spin_unlock_bh(&sc->tx.txbuflock);
+ return NULL;
}
- return 0;
+ bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
+ list_del(&bf->list);
+
+ spin_unlock_bh(&sc->tx.txbuflock);
+
+ return bf;
}
/* To complete a chain of buffers associated a frame */
@@ -396,6 +330,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc,
{
struct sk_buff *skb = bf->bf_mpdu;
struct ath_xmit_status tx_status;
+ unsigned long flags;
/*
* Set retry information.
@@ -414,20 +349,21 @@ static void ath_tx_complete_buf(struct ath_softc *sc,
if (bf_isxretried(bf))
tx_status.flags |= ATH_TX_XRETRY;
}
+
/* Unmap this frame */
pci_unmap_single(sc->pdev,
bf->bf_dmacontext,
skb->len,
PCI_DMA_TODEVICE);
/* complete this frame */
- ath_tx_complete(sc, skb, &tx_status, bf->bf_node);
+ ath_tx_complete(sc, skb, &tx_status);
/*
* Return the list of ath_buf of this mpdu to free queue
*/
- spin_lock_bh(&sc->sc_txbuflock);
- list_splice_tail_init(bf_q, &sc->sc_txbuf);
- spin_unlock_bh(&sc->sc_txbuflock);
+ spin_lock_irqsave(&sc->tx.txbuflock, flags);
+ list_splice_tail_init(bf_q, &sc->tx.txbuf);
+ spin_unlock_irqrestore(&sc->tx.txbuflock, flags);
}
/*
@@ -468,7 +404,7 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
- struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
+ struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
spin_lock_bh(&txq->axq_lock);
@@ -481,7 +417,7 @@ static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
- struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
+ struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
ASSERT(tid->paused > 0);
spin_lock_bh(&txq->axq_lock);
@@ -505,11 +441,9 @@ unlock:
/* Compute the number of bad frames */
-static int ath_tx_num_badfrms(struct ath_softc *sc,
- struct ath_buf *bf, int txok)
+static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
+ int txok)
{
- struct ath_node *an = bf->bf_node;
- int isnodegone = (an->an_flags & ATH_NODE_CLEAN);
struct ath_buf *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
u16 seq_st = 0;
@@ -518,7 +452,7 @@ static int ath_tx_num_badfrms(struct ath_softc *sc,
int nbad = 0;
int isaggr = 0;
- if (isnodegone || ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED)
+ if (ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED)
return 0;
isaggr = bf_isaggr(bf);
@@ -553,8 +487,8 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
/* Update block ack window */
-static void ath_tx_update_baw(struct ath_softc *sc,
- struct ath_atx_tid *tid, int seqno)
+static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
+ int seqno)
{
int index, cindex;
@@ -577,34 +511,23 @@ static void ath_tx_update_baw(struct ath_softc *sc,
* width - 0 for 20 MHz, 1 for 40 MHz
* half_gi - to use 4us v/s 3.6 us for symbol time
*/
-
-static u32 ath_pkt_duration(struct ath_softc *sc,
- u8 rix,
- struct ath_buf *bf,
- int width,
- int half_gi,
- bool shortPreamble)
+static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
+ int width, int half_gi, bool shortPreamble)
{
- const struct ath9k_rate_table *rt = sc->sc_currates;
+ struct ath_rate_table *rate_table = sc->cur_rate_table;
u32 nbits, nsymbits, duration, nsymbols;
u8 rc;
int streams, pktlen;
pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen;
- rc = rt->info[rix].rateCode;
+ rc = rate_table->info[rix].ratecode;
- /*
- * for legacy rates, use old function to compute packet duration
- */
+ /* for legacy rates, use old function to compute packet duration */
if (!IS_HT_RATE(rc))
- return ath9k_hw_computetxtime(sc->sc_ah,
- rt,
- pktlen,
- rix,
- shortPreamble);
- /*
- * find number of symbols: PLCP + data
- */
+ return ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen,
+ rix, shortPreamble);
+
+ /* find number of symbols: PLCP + data */
nbits = (pktlen << 3) + OFDM_PLCP_BITS;
nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
nsymbols = (nbits + nsymbits - 1) / nsymbits;
@@ -614,11 +537,10 @@ static u32 ath_pkt_duration(struct ath_softc *sc,
else
duration = SYMBOL_TIME_HALFGI(nsymbols);
- /*
- * addup duration for legacy/ht training and signal fields
- */
+ /* addup duration for legacy/ht training and signal fields */
streams = HT_RC_2_STREAMS(rc);
duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+
return duration;
}
@@ -627,207 +549,127 @@ static u32 ath_pkt_duration(struct ath_softc *sc,
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
- const struct ath9k_rate_table *rt;
+ struct ath_rate_table *rt;
struct ath_desc *ds = bf->bf_desc;
struct ath_desc *lastds = bf->bf_lastbf->bf_desc;
struct ath9k_11n_rate_series series[4];
- int i, flags, rtsctsena = 0, dynamic_mimops = 0;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *rates;
+ struct ieee80211_hdr *hdr;
+ int i, flags, rtsctsena = 0;
u32 ctsduration = 0;
u8 rix = 0, cix, ctsrate = 0;
- u32 aggr_limit_with_rts = ah->ah_caps.rts_aggr_limit;
- struct ath_node *an = (struct ath_node *) bf->bf_node;
+ __le16 fc;
- /*
- * get the cix for the lowest valid rix.
- */
- rt = sc->sc_currates;
- for (i = 4; i--;) {
- if (bf->bf_rcs[i].tries) {
- rix = bf->bf_rcs[i].rix;
+ memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
+
+ skb = (struct sk_buff *)bf->bf_mpdu;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+ tx_info = IEEE80211_SKB_CB(skb);
+ rates = tx_info->control.rates;
+
+ if (ieee80211_has_morefrags(fc) ||
+ (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
+ rates[1].count = rates[2].count = rates[3].count = 0;
+ rates[1].idx = rates[2].idx = rates[3].idx = 0;
+ rates[0].count = ATH_TXMAXTRY;
+ }
+
+ /* get the cix for the lowest valid rix */
+ rt = sc->cur_rate_table;
+ for (i = 3; i >= 0; i--) {
+ if (rates[i].count && (rates[i].idx >= 0)) {
+ rix = rates[i].idx;
break;
}
}
+
flags = (bf->bf_flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA));
- cix = rt->info[rix].controlRate;
+ cix = rt->info[rix].ctrl_rate;
/*
- * If 802.11g protection is enabled, determine whether
- * to use RTS/CTS or just CTS. Note that this is only
- * done for OFDM/HT unicast frames.
+ * If 802.11g protection is enabled, determine whether to use RTS/CTS or
+ * just CTS. Note that this is only done for OFDM/HT unicast frames.
*/
- if (sc->sc_protmode != PROT_M_NONE &&
- (rt->info[rix].phy == PHY_OFDM ||
- rt->info[rix].phy == PHY_HT) &&
- (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
+ if (sc->sc_protmode != PROT_M_NONE && !(bf->bf_flags & ATH9K_TXDESC_NOACK)
+ && (rt->info[rix].phy == WLAN_RC_PHY_OFDM ||
+ WLAN_RC_PHY_HT(rt->info[rix].phy))) {
if (sc->sc_protmode == PROT_M_RTSCTS)
flags = ATH9K_TXDESC_RTSENA;
else if (sc->sc_protmode == PROT_M_CTSONLY)
flags = ATH9K_TXDESC_CTSENA;
- cix = rt->info[sc->sc_protrix].controlRate;
+ cix = rt->info[sc->sc_protrix].ctrl_rate;
rtsctsena = 1;
}
- /* For 11n, the default behavior is to enable RTS for
- * hw retried frames. We enable the global flag here and
- * let rate series flags determine which rates will actually
- * use RTS.
+ /* For 11n, the default behavior is to enable RTS for hw retried frames.
+ * We enable the global flag here and let rate series flags determine
+ * which rates will actually use RTS.
*/
if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) && bf_isdata(bf)) {
- BUG_ON(!an);
- /*
- * 802.11g protection not needed, use our default behavior
- */
+ /* 802.11g protection not needed, use our default behavior */
if (!rtsctsena)
flags = ATH9K_TXDESC_RTSENA;
- /*
- * For dynamic MIMO PS, RTS needs to precede the first aggregate
- * and the second aggregate should have any protection at all.
- */
- if (an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) {
- if (!bf_isaggrburst(bf)) {
- flags = ATH9K_TXDESC_RTSENA;
- dynamic_mimops = 1;
- } else {
- flags = 0;
- }
- }
}
- /*
- * Set protection if aggregate protection on
- */
+ /* Set protection if aggregate protection on */
if (sc->sc_config.ath_aggr_prot &&
(!bf_isaggr(bf) || (bf_isaggr(bf) && bf->bf_al < 8192))) {
flags = ATH9K_TXDESC_RTSENA;
- cix = rt->info[sc->sc_protrix].controlRate;
+ cix = rt->info[sc->sc_protrix].ctrl_rate;
rtsctsena = 1;
}
- /*
- * For AR5416 - RTS cannot be followed by a frame larger than 8K.
- */
- if (bf_isaggr(bf) && (bf->bf_al > aggr_limit_with_rts)) {
- /*
- * Ensure that in the case of SM Dynamic power save
- * while we are bursting the second aggregate the
- * RTS is cleared.
- */
+ /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
+ if (bf_isaggr(bf) && (bf->bf_al > ah->ah_caps.rts_aggr_limit))
flags &= ~(ATH9K_TXDESC_RTSENA);
- }
/*
- * CTS transmit rate is derived from the transmit rate
- * by looking in the h/w rate table. We must also factor
- * in whether or not a short preamble is to be used.
+ * CTS transmit rate is derived from the transmit rate by looking in the
+ * h/w rate table. We must also factor in whether or not a short
+ * preamble is to be used. NB: cix is set above where RTS/CTS is enabled
*/
- /* NB: cix is set above where RTS/CTS is enabled */
- BUG_ON(cix == 0xff);
- ctsrate = rt->info[cix].rateCode |
- (bf_isshpreamble(bf) ? rt->info[cix].shortPreamble : 0);
-
- /*
- * Setup HAL rate series
- */
- memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
+ ctsrate = rt->info[cix].ratecode |
+ (bf_isshpreamble(bf) ? rt->info[cix].short_preamble : 0);
for (i = 0; i < 4; i++) {
- if (!bf->bf_rcs[i].tries)
+ if (!rates[i].count || (rates[i].idx < 0))
continue;
- rix = bf->bf_rcs[i].rix;
+ rix = rates[i].idx;
- series[i].Rate = rt->info[rix].rateCode |
- (bf_isshpreamble(bf) ? rt->info[rix].shortPreamble : 0);
+ series[i].Rate = rt->info[rix].ratecode |
+ (bf_isshpreamble(bf) ? rt->info[rix].short_preamble : 0);
- series[i].Tries = bf->bf_rcs[i].tries;
+ series[i].Tries = rates[i].count;
series[i].RateFlags = (
- (bf->bf_rcs[i].flags & ATH_RC_RTSCTS_FLAG) ?
+ (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) ?
ATH9K_RATESERIES_RTS_CTS : 0) |
- ((bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) ?
+ ((rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ?
ATH9K_RATESERIES_2040 : 0) |
- ((bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG) ?
+ ((rates[i].flags & IEEE80211_TX_RC_SHORT_GI) ?
ATH9K_RATESERIES_HALFGI : 0);
- series[i].PktDuration = ath_pkt_duration(
- sc, rix, bf,
- (bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) != 0,
- (bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG),
- bf_isshpreamble(bf));
+ series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
+ (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) != 0,
+ (rates[i].flags & IEEE80211_TX_RC_SHORT_GI),
+ bf_isshpreamble(bf));
- if ((an->an_smmode == ATH_SM_PWRSAV_STATIC) &&
- (bf->bf_rcs[i].flags & ATH_RC_DS_FLAG) == 0) {
- /*
- * When sending to an HT node that has enabled static
- * SM/MIMO power save, send at single stream rates but
- * use maximum allowed transmit chains per user,
- * hardware, regulatory, or country limits for
- * better range.
- */
- series[i].ChSel = sc->sc_tx_chainmask;
- } else {
- if (bf_isht(bf))
- series[i].ChSel =
- ath_chainmask_sel_logic(sc, an);
- else
- series[i].ChSel = sc->sc_tx_chainmask;
- }
+ series[i].ChSel = sc->sc_tx_chainmask;
if (rtsctsena)
series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
-
- /*
- * Set RTS for all rates if node is in dynamic powersave
- * mode and we are using dual stream rates.
- */
- if (dynamic_mimops && (bf->bf_rcs[i].flags & ATH_RC_DS_FLAG))
- series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
- }
-
- /*
- * For non-HT devices, calculate RTS/CTS duration in software
- * and disable multi-rate retry.
- */
- if (flags && !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)) {
- /*
- * Compute the transmit duration based on the frame
- * size and the size of an ACK frame. We call into the
- * HAL to do the computation since it depends on the
- * characteristics of the actual PHY being used.
- *
- * NB: CTS is assumed the same size as an ACK so we can
- * use the precalculated ACK durations.
- */
- if (flags & ATH9K_TXDESC_RTSENA) { /* SIFS + CTS */
- ctsduration += bf_isshpreamble(bf) ?
- rt->info[cix].spAckDuration :
- rt->info[cix].lpAckDuration;
- }
-
- ctsduration += series[0].PktDuration;
-
- if ((bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { /* SIFS + ACK */
- ctsduration += bf_isshpreamble(bf) ?
- rt->info[rix].spAckDuration :
- rt->info[rix].lpAckDuration;
- }
-
- /*
- * Disable multi-rate retry when using RTS/CTS by clearing
- * series 1, 2 and 3.
- */
- memset(&series[1], 0, sizeof(struct ath9k_11n_rate_series) * 3);
}
- /*
- * set dur_update_en for l-sig computation except for PS-Poll frames
- */
- ath9k_hw_set11n_ratescenario(ah, ds, lastds,
- !bf_ispspoll(bf),
- ctsrate,
- ctsduration,
+ /* set dur_update_en for l-sig computation except for PS-Poll frames */
+ ath9k_hw_set11n_ratescenario(ah, ds, lastds, !bf_ispspoll(bf),
+ ctsrate, ctsduration,
series, 4, flags);
+
if (sc->sc_config.ath_aggr_prot && flags)
ath9k_hw_set11n_burstduration(ah, ds, 8192);
}
@@ -836,27 +678,18 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
* Function to send a normal HT (non-AMPDU) frame
* NB: must be called with txq lock held
*/
-
static int ath_tx_send_normal(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_atx_tid *tid,
struct list_head *bf_head)
{
struct ath_buf *bf;
- struct sk_buff *skb;
- struct ieee80211_tx_info *tx_info;
- struct ath_tx_info_priv *tx_info_priv;
BUG_ON(list_empty(bf_head));
bf = list_first_entry(bf_head, struct ath_buf, list);
bf->bf_state.bf_type &= ~BUF_AMPDU; /* regular HT frame */
- skb = (struct sk_buff *)bf->bf_mpdu;
- tx_info = IEEE80211_SKB_CB(skb);
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
-
/* update starting sequence number for subsequent ADDBA request */
INCR(tid->seq_start, IEEE80211_SEQ_MAX);
@@ -873,7 +706,7 @@ static int ath_tx_send_normal(struct ath_softc *sc,
static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
- struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
+ struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
struct ath_buf *bf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
@@ -906,8 +739,10 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
struct list_head *bf_q,
int txok)
{
- struct ath_node *an = bf->bf_node;
- struct ath_atx_tid *tid = ATH_AN_2_TID(an, bf->bf_tidno);
+ struct ath_node *an = NULL;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *tx_info;
+ struct ath_atx_tid *tid = NULL;
struct ath_buf *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
struct ath_buf *bf_next, *bf_lastq = NULL;
@@ -915,7 +750,14 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
u16 seq_st = 0;
u32 ba[WME_BA_BMP_SIZE >> 5];
int isaggr, txfail, txpending, sendbar = 0, needreset = 0;
- int isnodegone = (an->an_flags & ATH_NODE_CLEAN);
+
+ skb = (struct sk_buff *)bf->bf_mpdu;
+ tx_info = IEEE80211_SKB_CB(skb);
+
+ if (tx_info->control.sta) {
+ an = (struct ath_node *)tx_info->control.sta->drv_priv;
+ tid = ATH_AN_2_TID(an, bf->bf_tidno);
+ }
isaggr = bf_isaggr(bf);
if (isaggr) {
@@ -939,7 +781,8 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
* when perform internal reset in this routine.
* Only enable reset in STA mode for now.
*/
- if (sc->sc_ah->ah_opmode == ATH9K_M_STA)
+ if (sc->sc_ah->ah_opmode ==
+ NL80211_IFTYPE_STATION)
needreset = 1;
}
} else {
@@ -961,7 +804,7 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
/* transmit completion */
} else {
- if (!tid->cleanup_inprogress && !isnodegone &&
+ if (!(tid->state & AGGR_CLEANUP) &&
ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
ath_tx_set_retry(sc, bf);
@@ -1038,18 +881,17 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
struct ath_buf *tbf;
/* allocate new descriptor */
- spin_lock_bh(&sc->sc_txbuflock);
- ASSERT(!list_empty((&sc->sc_txbuf)));
- tbf = list_first_entry(&sc->sc_txbuf,
+ spin_lock_bh(&sc->tx.txbuflock);
+ ASSERT(!list_empty((&sc->tx.txbuf)));
+ tbf = list_first_entry(&sc->tx.txbuf,
struct ath_buf, list);
list_del(&tbf->list);
- spin_unlock_bh(&sc->sc_txbuflock);
+ spin_unlock_bh(&sc->tx.txbuflock);
ATH_TXBUF_RESET(tbf);
/* copy descriptor content */
tbf->bf_mpdu = bf_last->bf_mpdu;
- tbf->bf_node = bf_last->bf_node;
tbf->bf_buf_addr = bf_last->bf_buf_addr;
*(tbf->bf_desc) = *(bf_last->bf_desc);
@@ -1090,25 +932,16 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
bf = bf_next;
}
- /*
- * node is already gone. no more assocication
- * with the node. the node might have been freed
- * any node acces can result in panic.note tid
- * is part of the node.
- */
- if (isnodegone)
- return;
-
- if (tid->cleanup_inprogress) {
+ if (tid->state & AGGR_CLEANUP) {
/* check to see if we're done with cleaning the h/w queue */
spin_lock_bh(&txq->axq_lock);
if (tid->baw_head == tid->baw_tail) {
- tid->addba_exchangecomplete = 0;
+ tid->state &= ~AGGR_ADDBA_COMPLETE;
tid->addba_exchangeattempts = 0;
spin_unlock_bh(&txq->axq_lock);
- tid->cleanup_inprogress = false;
+ tid->state &= ~AGGR_CLEANUP;
/* send buffered frames as singles */
ath_tx_flush_tid(sc, tid);
@@ -1136,29 +969,45 @@ static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
return;
}
+static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, int nbad)
+{
+ struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+
+ tx_info_priv->update_rc = false;
+ if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
+ tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+
+ if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
+ (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
+ if (bf_isdata(bf)) {
+ memcpy(&tx_info_priv->tx, &ds->ds_txstat,
+ sizeof(tx_info_priv->tx));
+ tx_info_priv->n_frames = bf->bf_nframes;
+ tx_info_priv->n_bad_frames = nbad;
+ tx_info_priv->update_rc = true;
+ }
+ }
+}
+
/* Process completed xmit descriptors from the specified queue */
-static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf, *lastbf, *bf_held = NULL;
struct list_head bf_head;
- struct ath_desc *ds, *tmp_ds;
- struct sk_buff *skb;
- struct ieee80211_tx_info *tx_info;
- struct ath_tx_info_priv *tx_info_priv;
- int nacked, txok, nbad = 0, isrifs = 0;
+ struct ath_desc *ds;
+ int txok, nbad = 0;
int status;
- DPRINTF(sc, ATH_DBG_QUEUE,
- "%s: tx queue %d (%x), link %p\n", __func__,
+ DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
txq->axq_link);
- nacked = 0;
for (;;) {
spin_lock_bh(&txq->axq_lock);
- txq->axq_intrcnt = 0; /* reset periodic desc intr count */
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
txq->axq_linkbuf = NULL;
@@ -1229,9 +1078,9 @@ static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
if (bf_held) {
list_del(&bf_held->list);
- spin_lock_bh(&sc->sc_txbuflock);
- list_add_tail(&bf_held->list, &sc->sc_txbuf);
- spin_unlock_bh(&sc->sc_txbuflock);
+ spin_lock_bh(&sc->tx.txbuflock);
+ list_add_tail(&bf_held->list, &sc->tx.txbuf);
+ spin_unlock_bh(&sc->tx.txbuflock);
}
if (!bf_isampdu(bf)) {
@@ -1246,29 +1095,8 @@ static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
} else {
nbad = ath_tx_num_badfrms(sc, bf, txok);
}
- skb = bf->bf_mpdu;
- tx_info = IEEE80211_SKB_CB(skb);
- tx_info_priv = (struct ath_tx_info_priv *)
- tx_info->driver_data[0];
- if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
- tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
- if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
- (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
- if (ds->ds_txstat.ts_status == 0)
- nacked++;
-
- if (bf_isdata(bf)) {
- if (isrifs)
- tmp_ds = bf->bf_rifslast->bf_desc;
- else
- tmp_ds = ds;
- memcpy(&tx_info_priv->tx,
- &tmp_ds->ds_txstat,
- sizeof(tx_info_priv->tx));
- tx_info_priv->n_frames = bf->bf_nframes;
- tx_info_priv->n_bad_frames = nbad;
- }
- }
+
+ ath_tx_rc_status(bf, ds, nbad);
/*
* Complete this transmit unit
@@ -1299,7 +1127,6 @@ static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
ath_txq_schedule(sc, txq);
spin_unlock_bh(&txq->axq_lock);
}
- return nacked;
}
static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
@@ -1307,9 +1134,9 @@ static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
struct ath_hal *ah = sc->sc_ah;
(void) ath9k_hw_stoptxdma(ah, txq->axq_qnum);
- DPRINTF(sc, ATH_DBG_XMIT, "%s: tx queue [%u] %x, link %p\n",
- __func__, txq->axq_qnum,
- ath9k_hw_gettxbuf(ah, txq->axq_qnum), txq->axq_link);
+ DPRINTF(sc, ATH_DBG_XMIT, "tx queue [%u] %x, link %p\n",
+ txq->axq_qnum, ath9k_hw_gettxbuf(ah, txq->axq_qnum),
+ txq->axq_link);
}
/* Drain only the data queues */
@@ -1317,40 +1144,33 @@ static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
static void ath_drain_txdataq(struct ath_softc *sc, bool retry_tx)
{
struct ath_hal *ah = sc->sc_ah;
- int i;
- int npend = 0;
+ int i, status, npend = 0;
- /* XXX return value */
if (!(sc->sc_flags & SC_OP_INVALID)) {
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) {
- ath_tx_stopdma(sc, &sc->sc_txq[i]);
-
+ ath_tx_stopdma(sc, &sc->tx.txq[i]);
/* The TxDMA may not really be stopped.
* Double check the hal tx pending count */
npend += ath9k_hw_numtxpending(ah,
- sc->sc_txq[i].axq_qnum);
+ sc->tx.txq[i].axq_qnum);
}
}
}
if (npend) {
- int status;
-
/* TxDMA not stopped, reset the hal */
- DPRINTF(sc, ATH_DBG_XMIT,
- "%s: Unable to stop TxDMA. Reset HAL!\n", __func__);
+ DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n");
spin_lock_bh(&sc->sc_resetlock);
if (!ath9k_hw_reset(ah,
sc->sc_ah->ah_curchan,
- sc->sc_ht_info.tx_chan_width,
+ sc->tx_chan_width,
sc->sc_tx_chainmask, sc->sc_rx_chainmask,
sc->sc_ht_extprotspacing, true, &status)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to reset hardware; hal status %u\n",
- __func__,
+ "Unable to reset hardware; hal status %u\n",
status);
}
spin_unlock_bh(&sc->sc_resetlock);
@@ -1358,7 +1178,7 @@ static void ath_drain_txdataq(struct ath_softc *sc, bool retry_tx)
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i))
- ath_tx_draintxq(sc, &sc->sc_txq[i], retry_tx);
+ ath_tx_draintxq(sc, &sc->tx.txq[i], retry_tx);
}
}
@@ -1390,24 +1210,17 @@ static void ath_tx_addto_baw(struct ath_softc *sc,
* Function to send an A-MPDU
* NB: must be called with txq lock held
*/
-
static int ath_tx_send_ampdu(struct ath_softc *sc,
- struct ath_txq *txq,
struct ath_atx_tid *tid,
struct list_head *bf_head,
struct ath_tx_control *txctl)
{
struct ath_buf *bf;
- struct sk_buff *skb;
- struct ieee80211_tx_info *tx_info;
- struct ath_tx_info_priv *tx_info_priv;
BUG_ON(list_empty(bf_head));
bf = list_first_entry(bf_head, struct ath_buf, list);
bf->bf_state.bf_type |= BUF_AMPDU;
- bf->bf_seqno = txctl->seqno; /* save seqno and tidno in buffer */
- bf->bf_tidno = txctl->tidno;
/*
* Do not queue to h/w when any of the following conditions is true:
@@ -1418,21 +1231,16 @@ static int ath_tx_send_ampdu(struct ath_softc *sc,
*/
if (!list_empty(&tid->buf_q) || tid->paused ||
!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno) ||
- txq->axq_depth >= ATH_AGGR_MIN_QDEPTH) {
+ txctl->txq->axq_depth >= ATH_AGGR_MIN_QDEPTH) {
/*
* Add this frame to software queue for scheduling later
* for aggregation.
*/
list_splice_tail_init(bf_head, &tid->buf_q);
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(txctl->txq, tid);
return 0;
}
- skb = (struct sk_buff *)bf->bf_mpdu;
- tx_info = IEEE80211_SKB_CB(skb);
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
-
/* Add sub-frame to BAW */
ath_tx_addto_baw(sc, tid, bf);
@@ -1440,7 +1248,8 @@ static int ath_tx_send_ampdu(struct ath_softc *sc,
bf->bf_nframes = 1;
bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */
ath_buf_set_rate(sc, bf);
- ath_tx_txqaddbuf(sc, txq, bf_head);
+ ath_tx_txqaddbuf(sc, txctl->txq, bf_head);
+
return 0;
}
@@ -1448,25 +1257,24 @@ static int ath_tx_send_ampdu(struct ath_softc *sc,
* looks up the rate
* returns aggr limit based on lowest of the rates
*/
-
static u32 ath_lookup_rate(struct ath_softc *sc,
- struct ath_buf *bf)
+ struct ath_buf *bf,
+ struct ath_atx_tid *tid)
{
- const struct ath9k_rate_table *rt = sc->sc_currates;
+ struct ath_rate_table *rate_table = sc->cur_rate_table;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *rates;
struct ath_tx_info_priv *tx_info_priv;
u32 max_4ms_framelen, frame_length;
u16 aggr_limit, legacy = 0, maxampdu;
int i;
-
skb = (struct sk_buff *)bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
- tx_info_priv = (struct ath_tx_info_priv *)
- tx_info->driver_data[0];
- memcpy(bf->bf_rcs,
- tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
+ rates = tx_info->control.rates;
+ tx_info_priv =
+ (struct ath_tx_info_priv *)tx_info->rate_driver_data[0];
/*
* Find the lowest frame length among the rate series that will have a
@@ -1476,14 +1284,14 @@ static u32 ath_lookup_rate(struct ath_softc *sc,
max_4ms_framelen = ATH_AMPDU_LIMIT_MAX;
for (i = 0; i < 4; i++) {
- if (bf->bf_rcs[i].tries) {
- frame_length = bf->bf_rcs[i].max_4ms_framelen;
-
- if (rt->info[bf->bf_rcs[i].rix].phy != PHY_HT) {
+ if (rates[i].count) {
+ if (!WLAN_RC_PHY_HT(rate_table->info[rates[i].idx].phy)) {
legacy = 1;
break;
}
+ frame_length =
+ rate_table->info[rates[i].idx].max_4ms_framelen;
max_4ms_framelen = min(max_4ms_framelen, frame_length);
}
}
@@ -1504,7 +1312,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc,
* The IE, however can hold upto 65536, which shows up here
* as zero. Ignore 65536 since we are constrained by hw.
*/
- maxampdu = sc->sc_ht_info.maxampdu;
+ maxampdu = tid->an->maxampdu;
if (maxampdu)
aggr_limit = min(aggr_limit, maxampdu);
@@ -1516,12 +1324,14 @@ static u32 ath_lookup_rate(struct ath_softc *sc,
* meet the minimum required mpdudensity.
* caller should make sure that the rate is HT rate .
*/
-
static int ath_compute_num_delims(struct ath_softc *sc,
+ struct ath_atx_tid *tid,
struct ath_buf *bf,
u16 frmlen)
{
- const struct ath9k_rate_table *rt = sc->sc_currates;
+ struct ath_rate_table *rt = sc->cur_rate_table;
+ struct sk_buff *skb = bf->bf_mpdu;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
u32 nsymbits, nsymbols, mpdudensity;
u16 minlen;
u8 rc, flags, rix;
@@ -1545,7 +1355,7 @@ static int ath_compute_num_delims(struct ath_softc *sc,
* required minimum length for subframe. Take into account
* whether high rate is 20 or 40Mhz and half or full GI.
*/
- mpdudensity = sc->sc_ht_info.mpdudensity;
+ mpdudensity = tid->an->mpdudensity;
/*
* If there is no mpdu density restriction, no further calculation
@@ -1554,11 +1364,11 @@ static int ath_compute_num_delims(struct ath_softc *sc,
if (mpdudensity == 0)
return ndelim;
- rix = bf->bf_rcs[0].rix;
- flags = bf->bf_rcs[0].flags;
- rc = rt->info[rix].rateCode;
- width = (flags & ATH_RC_CW40_FLAG) ? 1 : 0;
- half_gi = (flags & ATH_RC_SGI_FLAG) ? 1 : 0;
+ rix = tx_info->control.rates[0].idx;
+ flags = tx_info->control.rates[0].flags;
+ rc = rt->info[rix].ratecode;
+ width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0;
+ half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0;
if (half_gi)
nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
@@ -1585,7 +1395,6 @@ static int ath_compute_num_delims(struct ath_softc *sc,
* For aggregation from software buffer queue.
* NB: must be called with txq lock held
*/
-
static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
struct ath_atx_tid *tid,
struct list_head *bf_q,
@@ -1600,7 +1409,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
u16 aggr_limit = 0, al = 0, bpad = 0,
al_delta, h_baw = tid->baw_size / 2;
enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
- int prev_al = 0, is_ds_rate = 0;
+ int prev_al = 0;
INIT_LIST_HEAD(&bf_head);
BUG_ON(list_empty(&tid->buf_q));
@@ -1619,13 +1428,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
}
if (!rl) {
- aggr_limit = ath_lookup_rate(sc, bf);
+ aggr_limit = ath_lookup_rate(sc, bf, tid);
rl = 1;
- /*
- * Is rate dual stream
- */
- is_ds_rate =
- (bf->bf_rcs[0].flags & ATH_RC_DS_FLAG) ? 1 : 0;
}
/*
@@ -1657,7 +1461,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
* Get the delimiters needed to meet the MPDU
* density for this node.
*/
- ndelim = ath_compute_num_delims(sc, bf_first, bf->bf_frmlen);
+ ndelim = ath_compute_num_delims(sc, tid, bf_first, bf->bf_frmlen);
bpad = PADBYTES(al_delta) + (ndelim << 2);
@@ -1713,7 +1517,6 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
* process pending frames possibly doing a-mpdu aggregation
* NB: must be called with txq lock held
*/
-
static void ath_tx_sched_aggr(struct ath_softc *sc,
struct ath_txq *txq, struct ath_atx_tid *tid)
{
@@ -1799,8 +1602,8 @@ static void ath_tx_sched_aggr(struct ath_softc *sc,
static void ath_tid_drain(struct ath_softc *sc,
struct ath_txq *txq,
- struct ath_atx_tid *tid,
- bool bh_flag)
+ struct ath_atx_tid *tid)
+
{
struct ath_buf *bf;
struct list_head bf_head;
@@ -1821,18 +1624,12 @@ static void ath_tid_drain(struct ath_softc *sc,
* do not indicate packets while holding txq spinlock.
* unlock is intentional here
*/
- if (likely(bh_flag))
- spin_unlock_bh(&txq->axq_lock);
- else
- spin_unlock(&txq->axq_lock);
+ spin_unlock(&txq->axq_lock);
/* complete this sub-frame */
ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
- if (likely(bh_flag))
- spin_lock_bh(&txq->axq_lock);
- else
- spin_lock(&txq->axq_lock);
+ spin_lock(&txq->axq_lock);
}
/*
@@ -1849,10 +1646,8 @@ static void ath_tid_drain(struct ath_softc *sc,
* Drain all pending buffers
* NB: must be called with txq lock held
*/
-
static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
- struct ath_txq *txq,
- bool bh_flag)
+ struct ath_txq *txq)
{
struct ath_atx_ac *ac, *ac_tmp;
struct ath_atx_tid *tid, *tid_tmp;
@@ -1863,51 +1658,33 @@ static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) {
list_del(&tid->list);
tid->sched = false;
- ath_tid_drain(sc, txq, tid, bh_flag);
+ ath_tid_drain(sc, txq, tid);
}
}
}
-static int ath_tx_start_dma(struct ath_softc *sc,
- struct sk_buff *skb,
- struct scatterlist *sg,
- u32 n_sg,
- struct ath_tx_control *txctl)
+static int ath_tx_setup_buffer(struct ath_softc *sc, struct ath_buf *bf,
+ struct sk_buff *skb,
+ struct ath_tx_control *txctl)
{
- struct ath_node *an = txctl->an;
- struct ath_buf *bf = NULL;
- struct list_head bf_head;
- struct ath_desc *ds;
- struct ath_hal *ah = sc->sc_ah;
- struct ath_txq *txq;
- struct ath_tx_info_priv *tx_info_priv;
- struct ath_rc_series *rcs;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- __le16 fc = hdr->frame_control;
-
- if (unlikely(txctl->flags & ATH9K_TXDESC_CAB))
- txq = sc->sc_cabq;
- else
- txq = &sc->sc_txq[txctl->qnum];
+ struct ath_tx_info_priv *tx_info_priv;
+ int hdrlen;
+ __le16 fc;
- /* For each sglist entry, allocate an ath_buf for DMA */
- INIT_LIST_HEAD(&bf_head);
- spin_lock_bh(&sc->sc_txbuflock);
- if (unlikely(list_empty(&sc->sc_txbuf))) {
- spin_unlock_bh(&sc->sc_txbuflock);
+ tx_info_priv = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC);
+ if (unlikely(!tx_info_priv))
return -ENOMEM;
- }
+ tx_info->rate_driver_data[0] = tx_info_priv;
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ fc = hdr->frame_control;
- bf = list_first_entry(&sc->sc_txbuf, struct ath_buf, list);
- list_del(&bf->list);
- spin_unlock_bh(&sc->sc_txbuflock);
+ ATH_TXBUF_RESET(bf);
- list_add_tail(&bf->list, &bf_head);
+ /* Frame type */
- /* set up this buffer */
- ATH_TXBUF_RESET(bf);
- bf->bf_frmlen = txctl->frmlen;
+ bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3);
ieee80211_is_data(fc) ?
(bf->bf_state.bf_type |= BUF_DATA) :
@@ -1921,120 +1698,158 @@ static int ath_tx_start_dma(struct ath_softc *sc,
(sc->sc_flags & SC_OP_PREAMBLE_SHORT) ?
(bf->bf_state.bf_type |= BUF_SHORT_PREAMBLE) :
(bf->bf_state.bf_type &= ~BUF_SHORT_PREAMBLE);
+ (sc->hw->conf.ht.enabled && !is_pae(skb) &&
+ (tx_info->flags & IEEE80211_TX_CTL_AMPDU)) ?
+ (bf->bf_state.bf_type |= BUF_HT) :
+ (bf->bf_state.bf_type &= ~BUF_HT);
+
+ bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq);
+
+ /* Crypto */
+
+ bf->bf_keytype = get_hw_crypto_keytype(skb);
+
+ if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) {
+ bf->bf_frmlen += tx_info->control.hw_key->icv_len;
+ bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;
+ } else {
+ bf->bf_keyix = ATH9K_TXKEYIX_INVALID;
+ }
+
+ /* Assign seqno, tidno */
+
+ if (bf_isht(bf) && (sc->sc_flags & SC_OP_TXAGGR))
+ assign_aggr_tid_seqno(skb, bf);
+
+ /* DMA setup */
- bf->bf_flags = txctl->flags;
- bf->bf_keytype = txctl->keytype;
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
- rcs = tx_info_priv->rcs;
- bf->bf_rcs[0] = rcs[0];
- bf->bf_rcs[1] = rcs[1];
- bf->bf_rcs[2] = rcs[2];
- bf->bf_rcs[3] = rcs[3];
- bf->bf_node = an;
bf->bf_mpdu = skb;
- bf->bf_buf_addr = sg_dma_address(sg);
+
+ bf->bf_dmacontext = pci_map_single(sc->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ if (unlikely(pci_dma_mapping_error(sc->pdev, bf->bf_dmacontext))) {
+ bf->bf_mpdu = NULL;
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "pci_dma_mapping_error() on TX\n");
+ return -ENOMEM;
+ }
+
+ bf->bf_buf_addr = bf->bf_dmacontext;
+ return 0;
+}
+
+/* FIXME: tx power */
+static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
+ struct ath_tx_control *txctl)
+{
+ struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ath_node *an = NULL;
+ struct list_head bf_head;
+ struct ath_desc *ds;
+ struct ath_atx_tid *tid;
+ struct ath_hal *ah = sc->sc_ah;
+ int frm_type;
+
+ frm_type = get_hw_packet_type(skb);
+
+ INIT_LIST_HEAD(&bf_head);
+ list_add_tail(&bf->list, &bf_head);
/* setup descriptor */
+
ds = bf->bf_desc;
ds->ds_link = 0;
ds->ds_data = bf->bf_buf_addr;
- /*
- * Save the DMA context in the first ath_buf
- */
- bf->bf_dmacontext = txctl->dmacontext;
+ /* Formulate first tx descriptor with tx controls */
- /*
- * Formulate first tx descriptor with tx controls.
- */
- ath9k_hw_set11n_txdesc(ah,
- ds,
- bf->bf_frmlen, /* frame length */
- txctl->atype, /* Atheros packet type */
- min(txctl->txpower, (u16)60), /* txpower */
- txctl->keyix, /* key cache index */
- txctl->keytype, /* key type */
- txctl->flags); /* flags */
- ath9k_hw_filltxdesc(ah,
- ds,
- sg_dma_len(sg), /* segment length */
- true, /* first segment */
- (n_sg == 1) ? true : false, /* last segment */
- ds); /* first descriptor */
+ ath9k_hw_set11n_txdesc(ah, ds, bf->bf_frmlen, frm_type, MAX_RATE_POWER,
+ bf->bf_keyix, bf->bf_keytype, bf->bf_flags);
+
+ ath9k_hw_filltxdesc(ah, ds,
+ skb->len, /* segment length */
+ true, /* first segment */
+ true, /* last segment */
+ ds); /* first descriptor */
bf->bf_lastfrm = bf;
- (txctl->ht) ?
- (bf->bf_state.bf_type |= BUF_HT) :
- (bf->bf_state.bf_type &= ~BUF_HT);
- spin_lock_bh(&txq->axq_lock);
+ spin_lock_bh(&txctl->txq->axq_lock);
- if (txctl->ht && (sc->sc_flags & SC_OP_TXAGGR)) {
- struct ath_atx_tid *tid = ATH_AN_2_TID(an, txctl->tidno);
- if (ath_aggr_query(sc, an, txctl->tidno)) {
+ if (bf_isht(bf) && (sc->sc_flags & SC_OP_TXAGGR) &&
+ tx_info->control.sta) {
+ an = (struct ath_node *)tx_info->control.sta->drv_priv;
+ tid = ATH_AN_2_TID(an, bf->bf_tidno);
+
+ if (ath_aggr_query(sc, an, bf->bf_tidno)) {
/*
* Try aggregation if it's a unicast data frame
* and the destination is HT capable.
*/
- ath_tx_send_ampdu(sc, txq, tid, &bf_head, txctl);
+ ath_tx_send_ampdu(sc, tid, &bf_head, txctl);
} else {
/*
- * Send this frame as regular when ADDBA exchange
- * is neither complete nor pending.
+ * Send this frame as regular when ADDBA
+ * exchange is neither complete nor pending.
*/
- ath_tx_send_normal(sc, txq, tid, &bf_head);
+ ath_tx_send_normal(sc, txctl->txq,
+ tid, &bf_head);
}
} else {
bf->bf_lastbf = bf;
bf->bf_nframes = 1;
- ath_buf_set_rate(sc, bf);
-
- if (ieee80211_is_back_req(fc)) {
- /* This is required for resuming tid
- * during BAR completion */
- bf->bf_tidno = txctl->tidno;
- }
- ath_tx_txqaddbuf(sc, txq, &bf_head);
+ ath_buf_set_rate(sc, bf);
+ ath_tx_txqaddbuf(sc, txctl->txq, &bf_head);
}
- spin_unlock_bh(&txq->axq_lock);
- return 0;
+
+ spin_unlock_bh(&txctl->txq->axq_lock);
}
-static void xmit_map_sg(struct ath_softc *sc,
- struct sk_buff *skb,
- struct ath_tx_control *txctl)
+/* Upon failure caller should free skb */
+int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb,
+ struct ath_tx_control *txctl)
{
- struct ath_xmit_status tx_status;
- struct ath_atx_tid *tid;
- struct scatterlist sg;
+ struct ath_buf *bf;
+ int r;
- txctl->dmacontext = pci_map_single(sc->pdev, skb->data,
- skb->len, PCI_DMA_TODEVICE);
+ /* Check if a tx buffer is available */
- /* setup S/G list */
- memset(&sg, 0, sizeof(struct scatterlist));
- sg_dma_address(&sg) = txctl->dmacontext;
- sg_dma_len(&sg) = skb->len;
+ bf = ath_tx_get_buffer(sc);
+ if (!bf) {
+ DPRINTF(sc, ATH_DBG_XMIT, "TX buffers are full\n");
+ return -1;
+ }
- if (ath_tx_start_dma(sc, skb, &sg, 1, txctl) != 0) {
- /*
- * We have to do drop frame here.
- */
- pci_unmap_single(sc->pdev, txctl->dmacontext,
- skb->len, PCI_DMA_TODEVICE);
+ r = ath_tx_setup_buffer(sc, bf, skb, txctl);
+ if (unlikely(r)) {
+ struct ath_txq *txq = txctl->txq;
- tx_status.retries = 0;
- tx_status.flags = ATH_TX_ERROR;
+ DPRINTF(sc, ATH_DBG_FATAL, "TX mem alloc failure\n");
- if (txctl->ht && (sc->sc_flags & SC_OP_TXAGGR)) {
- /* Reclaim the seqno. */
- tid = ATH_AN_2_TID((struct ath_node *)
- txctl->an, txctl->tidno);
- DECR(tid->seq_next, IEEE80211_SEQ_MAX);
+ /* upon ath_tx_processq() this TX queue will be resumed, we
+ * guarantee this will happen by knowing beforehand that
+ * we will at least have to run TX completionon one buffer
+ * on the queue */
+ spin_lock_bh(&txq->axq_lock);
+ if (ath_txq_depth(sc, txq->axq_qnum) > 1) {
+ ieee80211_stop_queue(sc->hw,
+ skb_get_queue_mapping(skb));
+ txq->stopped = 1;
}
- ath_tx_complete(sc, skb, &tx_status, txctl->an);
+ spin_unlock_bh(&txq->axq_lock);
+
+ spin_lock_bh(&sc->tx.txbuflock);
+ list_add_tail(&bf->list, &sc->tx.txbuf);
+ spin_unlock_bh(&sc->tx.txbuflock);
+
+ return r;
}
+
+ ath_tx_start_dma(sc, bf, txctl);
+
+ return 0;
}
/* Initialize TX queue and h/w */
@@ -2044,26 +1859,25 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
int error = 0;
do {
- spin_lock_init(&sc->sc_txbuflock);
+ spin_lock_init(&sc->tx.txbuflock);
/* Setup tx descriptors */
- error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+ error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf,
"tx", nbufs, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: failed to allocate tx descriptors: %d\n",
- __func__, error);
+ "Failed to allocate tx descriptors: %d\n",
+ error);
break;
}
/* XXX allocate beacon state together with vap */
- error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+ error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf,
"beacon", ATH_BCBUF, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: failed to allocate "
- "beacon descripotrs: %d\n",
- __func__, error);
+ "Failed to allocate beacon descriptors: %d\n",
+ error);
break;
}
@@ -2080,12 +1894,12 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
int ath_tx_cleanup(struct ath_softc *sc)
{
/* cleanup beacon descriptors */
- if (sc->sc_bdma.dd_desc_len != 0)
- ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf);
+ if (sc->beacon.bdma.dd_desc_len != 0)
+ ath_descdma_cleanup(sc, &sc->beacon.bdma, &sc->beacon.bbuf);
/* cleanup tx descriptors */
- if (sc->sc_txdma.dd_desc_len != 0)
- ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
+ if (sc->tx.txdma.dd_desc_len != 0)
+ ath_descdma_cleanup(sc, &sc->tx.txdma, &sc->tx.txbuf);
return 0;
}
@@ -2133,15 +1947,15 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
*/
return NULL;
}
- if (qnum >= ARRAY_SIZE(sc->sc_txq)) {
+ if (qnum >= ARRAY_SIZE(sc->tx.txq)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: hal qnum %u out of range, max %u!\n",
- __func__, qnum, (unsigned int)ARRAY_SIZE(sc->sc_txq));
+ "qnum %u out of range, max %u!\n",
+ qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq));
ath9k_hw_releasetxqueue(ah, qnum);
return NULL;
}
if (!ATH_TXQ_SETUP(sc, qnum)) {
- struct ath_txq *txq = &sc->sc_txq[qnum];
+ struct ath_txq *txq = &sc->tx.txq[qnum];
txq->axq_qnum = qnum;
txq->axq_link = NULL;
@@ -2151,11 +1965,10 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->axq_depth = 0;
txq->axq_aggr_depth = 0;
txq->axq_totalqueued = 0;
- txq->axq_intrcnt = 0;
txq->axq_linkbuf = NULL;
- sc->sc_txqsetup |= 1<<qnum;
+ sc->tx.txqsetup |= 1<<qnum;
}
- return &sc->sc_txq[qnum];
+ return &sc->tx.txq[qnum];
}
/* Reclaim resources for a setup queue */
@@ -2163,7 +1976,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
{
ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum);
- sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
+ sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
}
/*
@@ -2180,15 +1993,15 @@ int ath_tx_setup(struct ath_softc *sc, int haltype)
{
struct ath_txq *txq;
- if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
+ if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: HAL AC %u out of range, max %zu!\n",
- __func__, haltype, ARRAY_SIZE(sc->sc_haltype2q));
+ "HAL AC %u out of range, max %zu!\n",
+ haltype, ARRAY_SIZE(sc->tx.hwq_map));
return 0;
}
txq = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype);
if (txq != NULL) {
- sc->sc_haltype2q[haltype] = txq->axq_qnum;
+ sc->tx.hwq_map[haltype] = txq->axq_qnum;
return 1;
} else
return 0;
@@ -2200,20 +2013,19 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
switch (qtype) {
case ATH9K_TX_QUEUE_DATA:
- if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
+ if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: HAL AC %u out of range, max %zu!\n",
- __func__,
- haltype, ARRAY_SIZE(sc->sc_haltype2q));
+ "HAL AC %u out of range, max %zu!\n",
+ haltype, ARRAY_SIZE(sc->tx.hwq_map));
return -1;
}
- qnum = sc->sc_haltype2q[haltype];
+ qnum = sc->tx.hwq_map[haltype];
break;
case ATH9K_TX_QUEUE_BEACON:
- qnum = sc->sc_bhalq;
+ qnum = sc->beacon.beaconq;
break;
case ATH9K_TX_QUEUE_CAB:
- qnum = sc->sc_cabq->axq_qnum;
+ qnum = sc->beacon.cabq->axq_qnum;
break;
default:
qnum = -1;
@@ -2221,6 +2033,34 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
return qnum;
}
+/* Get a transmit queue, if available */
+
+struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb)
+{
+ struct ath_txq *txq = NULL;
+ int qnum;
+
+ qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
+ txq = &sc->tx.txq[qnum];
+
+ spin_lock_bh(&txq->axq_lock);
+
+ /* Try to avoid running out of descriptors */
+ if (txq->axq_depth >= (ATH_TXBUF - 20)) {
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "TX queue: %d is full, depth: %d\n",
+ qnum, txq->axq_depth);
+ ieee80211_stop_queue(sc->hw, skb_get_queue_mapping(skb));
+ txq->stopped = 1;
+ spin_unlock_bh(&txq->axq_lock);
+ return NULL;
+ }
+
+ spin_unlock_bh(&txq->axq_lock);
+
+ return txq;
+}
+
/* Update parameters for a transmit queue */
int ath_txq_update(struct ath_softc *sc, int qnum,
@@ -2230,17 +2070,17 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
int error = 0;
struct ath9k_tx_queue_info qi;
- if (qnum == sc->sc_bhalq) {
+ if (qnum == sc->beacon.beaconq) {
/*
* XXX: for beacon queue, we just save the parameter.
* It will be picked up by ath_beaconq_config when
* it's necessary.
*/
- sc->sc_beacon_qi = *qinfo;
+ sc->beacon.beacon_qi = *qinfo;
return 0;
}
- ASSERT(sc->sc_txq[qnum].axq_qnum == qnum);
+ ASSERT(sc->tx.txq[qnum].axq_qnum == qnum);
ath9k_hw_get_txq_props(ah, qnum, &qi);
qi.tqi_aifs = qinfo->tqi_aifs;
@@ -2251,8 +2091,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: unable to update hardware queue %u!\n",
- __func__, qnum);
+ "Unable to update hardware queue %u!\n", qnum);
error = -EIO;
} else {
ath9k_hw_resettxqueue(ah, qnum); /* push to h/w */
@@ -2264,7 +2103,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
int ath_cabq_update(struct ath_softc *sc)
{
struct ath9k_tx_queue_info qi;
- int qnum = sc->sc_cabq->axq_qnum;
+ int qnum = sc->beacon.cabq->axq_qnum;
struct ath_beacon_config conf;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
@@ -2284,27 +2123,6 @@ int ath_cabq_update(struct ath_softc *sc)
return 0;
}
-int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb)
-{
- struct ath_tx_control txctl;
- int error = 0;
-
- memset(&txctl, 0, sizeof(struct ath_tx_control));
- error = ath_tx_prepare(sc, skb, &txctl);
- if (error == 0)
- /*
- * Start DMA mapping.
- * ath_tx_start_dma() will be called either synchronously
- * or asynchrounsly once DMA is complete.
- */
- xmit_map_sg(sc, skb, &txctl);
- else
- ath_node_put(sc, txctl.an, ATH9K_BH_STATUS_CHANGE);
-
- /* failed packets will be dropped by the caller */
- return error;
-}
-
/* Deferred processing of transmit interrupt */
void ath_tx_tasklet(struct ath_softc *sc)
@@ -2319,7 +2137,7 @@ void ath_tx_tasklet(struct ath_softc *sc)
*/
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i)))
- ath_tx_processq(sc, &sc->sc_txq[i]);
+ ath_tx_processq(sc, &sc->tx.txq[i]);
}
}
@@ -2351,9 +2169,9 @@ void ath_tx_draintxq(struct ath_softc *sc,
list_del(&bf->list);
spin_unlock_bh(&txq->axq_lock);
- spin_lock_bh(&sc->sc_txbuflock);
- list_add_tail(&bf->list, &sc->sc_txbuf);
- spin_unlock_bh(&sc->sc_txbuflock);
+ spin_lock_bh(&sc->tx.txbuflock);
+ list_add_tail(&bf->list, &sc->tx.txbuf);
+ spin_unlock_bh(&sc->tx.txbuflock);
continue;
}
@@ -2378,8 +2196,7 @@ void ath_tx_draintxq(struct ath_softc *sc,
if (sc->sc_flags & SC_OP_TXAGGR) {
if (!retry_tx) {
spin_lock_bh(&txq->axq_lock);
- ath_txq_drain_pending_buffers(sc, txq,
- ATH9K_BH_STATUS_CHANGE);
+ ath_txq_drain_pending_buffers(sc, txq);
spin_unlock_bh(&txq->axq_lock);
}
}
@@ -2392,9 +2209,9 @@ void ath_draintxq(struct ath_softc *sc, bool retry_tx)
/* stop beacon queue. The beacon will be freed when
* we go to INIT state */
if (!(sc->sc_flags & SC_OP_INVALID)) {
- (void) ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
- DPRINTF(sc, ATH_DBG_XMIT, "%s: beacon queue %x\n", __func__,
- ath9k_hw_gettxbuf(sc->sc_ah, sc->sc_bhalq));
+ (void) ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+ DPRINTF(sc, ATH_DBG_XMIT, "beacon queue %x\n",
+ ath9k_hw_gettxbuf(sc->sc_ah, sc->beacon.beaconq));
}
ath_drain_txdataq(sc, retry_tx);
@@ -2402,72 +2219,47 @@ void ath_draintxq(struct ath_softc *sc, bool retry_tx)
u32 ath_txq_depth(struct ath_softc *sc, int qnum)
{
- return sc->sc_txq[qnum].axq_depth;
+ return sc->tx.txq[qnum].axq_depth;
}
u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum)
{
- return sc->sc_txq[qnum].axq_aggr_depth;
+ return sc->tx.txq[qnum].axq_aggr_depth;
}
-/* Check if an ADDBA is required. A valid node must be passed. */
-enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc,
- struct ath_node *an,
- u8 tidno)
+bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno)
{
struct ath_atx_tid *txtid;
- DECLARE_MAC_BUF(mac);
if (!(sc->sc_flags & SC_OP_TXAGGR))
- return AGGR_NOT_REQUIRED;
+ return false;
- /* ADDBA exchange must be completed before sending aggregates */
txtid = ATH_AN_2_TID(an, tidno);
- if (txtid->addba_exchangecomplete)
- return AGGR_EXCHANGE_DONE;
-
- if (txtid->cleanup_inprogress)
- return AGGR_CLEANUP_PROGRESS;
-
- if (txtid->addba_exchangeinprogress)
- return AGGR_EXCHANGE_PROGRESS;
-
- if (!txtid->addba_exchangecomplete) {
- if (!txtid->addba_exchangeinprogress &&
+ if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
+ if (!(txtid->state & AGGR_ADDBA_PROGRESS) &&
(txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) {
txtid->addba_exchangeattempts++;
- return AGGR_REQUIRED;
+ return true;
}
}
- return AGGR_NOT_REQUIRED;
+ return false;
}
/* Start TX aggregation */
-int ath_tx_aggr_start(struct ath_softc *sc,
- const u8 *addr,
- u16 tid,
- u16 *ssn)
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+ u16 tid, u16 *ssn)
{
struct ath_atx_tid *txtid;
struct ath_node *an;
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, (u8 *) addr);
- spin_unlock_bh(&sc->node_lock);
-
- if (!an) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: Node not found to initialize "
- "TX aggregation\n", __func__);
- return -1;
- }
+ an = (struct ath_node *)sta->drv_priv;
if (sc->sc_flags & SC_OP_TXAGGR) {
txtid = ATH_AN_2_TID(an, tid);
- txtid->addba_exchangeinprogress = 1;
+ txtid->state |= AGGR_ADDBA_PROGRESS;
ath_tx_pause_tid(sc, txtid);
}
@@ -2476,24 +2268,31 @@ int ath_tx_aggr_start(struct ath_softc *sc,
/* Stop tx aggregation */
-int ath_tx_aggr_stop(struct ath_softc *sc,
- const u8 *addr,
- u16 tid)
+int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
{
+ struct ath_node *an = (struct ath_node *)sta->drv_priv;
+
+ ath_tx_aggr_teardown(sc, an, tid);
+ return 0;
+}
+
+/* Resume tx aggregation */
+
+void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
+{
+ struct ath_atx_tid *txtid;
struct ath_node *an;
- spin_lock_bh(&sc->node_lock);
- an = ath_node_find(sc, (u8 *) addr);
- spin_unlock_bh(&sc->node_lock);
+ an = (struct ath_node *)sta->drv_priv;
- if (!an) {
- DPRINTF(sc, ATH_DBG_AGGR,
- "%s: TX aggr stop for non-existent node\n", __func__);
- return -1;
+ if (sc->sc_flags & SC_OP_TXAGGR) {
+ txtid = ATH_AN_2_TID(an, tid);
+ txtid->baw_size =
+ IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
+ txtid->state |= AGGR_ADDBA_COMPLETE;
+ txtid->state &= ~AGGR_ADDBA_PROGRESS;
+ ath_tx_resume_tid(sc, txtid);
}
-
- ath_tx_aggr_teardown(sc, an, tid);
- return 0;
}
/*
@@ -2503,21 +2302,18 @@ int ath_tx_aggr_stop(struct ath_softc *sc,
* - Discard all retry frames from the s/w queue.
*/
-void ath_tx_aggr_teardown(struct ath_softc *sc,
- struct ath_node *an, u8 tid)
+void ath_tx_aggr_teardown(struct ath_softc *sc, struct ath_node *an, u8 tid)
{
struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
- struct ath_txq *txq = &sc->sc_txq[txtid->ac->qnum];
+ struct ath_txq *txq = &sc->tx.txq[txtid->ac->qnum];
struct ath_buf *bf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
- DPRINTF(sc, ATH_DBG_AGGR, "%s: teardown TX aggregation\n", __func__);
-
- if (txtid->cleanup_inprogress) /* cleanup is in progress */
+ if (txtid->state & AGGR_CLEANUP) /* cleanup is in progress */
return;
- if (!txtid->addba_exchangecomplete) {
+ if (!(txtid->state & AGGR_ADDBA_COMPLETE)) {
txtid->addba_exchangeattempts = 0;
return;
}
@@ -2547,9 +2343,9 @@ void ath_tx_aggr_teardown(struct ath_softc *sc,
if (txtid->baw_head != txtid->baw_tail) {
spin_unlock_bh(&txq->axq_lock);
- txtid->cleanup_inprogress = true;
+ txtid->state |= AGGR_CLEANUP;
} else {
- txtid->addba_exchangecomplete = 0;
+ txtid->state &= ~AGGR_ADDBA_COMPLETE;
txtid->addba_exchangeattempts = 0;
spin_unlock_bh(&txq->axq_lock);
ath_tx_flush_tid(sc, txtid);
@@ -2591,10 +2387,8 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
if (tid->paused) /* check next tid to keep h/w busy */
continue;
- if (!(tid->an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) ||
- ((txq->axq_depth % 2) == 0)) {
+ if ((txq->axq_depth % 2) == 0)
ath_tx_sched_aggr(sc, txq, tid);
- }
/*
* add tid to round-robin queue if more frames
@@ -2625,72 +2419,67 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
{
- if (sc->sc_flags & SC_OP_TXAGGR) {
- struct ath_atx_tid *tid;
- struct ath_atx_ac *ac;
- int tidno, acno;
-
- sc->sc_ht_info.maxampdu = ATH_AMPDU_LIMIT_DEFAULT;
+ struct ath_atx_tid *tid;
+ struct ath_atx_ac *ac;
+ int tidno, acno;
- /*
- * Init per tid tx state
- */
- for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno];
- tidno < WME_NUM_TID;
- tidno++, tid++) {
- tid->an = an;
- tid->tidno = tidno;
- tid->seq_start = tid->seq_next = 0;
- tid->baw_size = WME_MAX_BA;
- tid->baw_head = tid->baw_tail = 0;
- tid->sched = false;
- tid->paused = false;
- tid->cleanup_inprogress = false;
- INIT_LIST_HEAD(&tid->buf_q);
-
- acno = TID_TO_WME_AC(tidno);
- tid->ac = &an->an_aggr.tx.ac[acno];
-
- /* ADDBA state */
- tid->addba_exchangecomplete = 0;
- tid->addba_exchangeinprogress = 0;
- tid->addba_exchangeattempts = 0;
- }
+ /*
+ * Init per tid tx state
+ */
+ for (tidno = 0, tid = &an->tid[tidno];
+ tidno < WME_NUM_TID;
+ tidno++, tid++) {
+ tid->an = an;
+ tid->tidno = tidno;
+ tid->seq_start = tid->seq_next = 0;
+ tid->baw_size = WME_MAX_BA;
+ tid->baw_head = tid->baw_tail = 0;
+ tid->sched = false;
+ tid->paused = false;
+ tid->state &= ~AGGR_CLEANUP;
+ INIT_LIST_HEAD(&tid->buf_q);
+
+ acno = TID_TO_WME_AC(tidno);
+ tid->ac = &an->ac[acno];
+
+ /* ADDBA state */
+ tid->state &= ~AGGR_ADDBA_COMPLETE;
+ tid->state &= ~AGGR_ADDBA_PROGRESS;
+ tid->addba_exchangeattempts = 0;
+ }
- /*
- * Init per ac tx state
- */
- for (acno = 0, ac = &an->an_aggr.tx.ac[acno];
- acno < WME_NUM_AC; acno++, ac++) {
- ac->sched = false;
- INIT_LIST_HEAD(&ac->tid_q);
-
- switch (acno) {
- case WME_AC_BE:
- ac->qnum = ath_tx_get_qnum(sc,
- ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
- break;
- case WME_AC_BK:
- ac->qnum = ath_tx_get_qnum(sc,
- ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK);
- break;
- case WME_AC_VI:
- ac->qnum = ath_tx_get_qnum(sc,
- ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI);
- break;
- case WME_AC_VO:
- ac->qnum = ath_tx_get_qnum(sc,
- ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO);
- break;
- }
+ /*
+ * Init per ac tx state
+ */
+ for (acno = 0, ac = &an->ac[acno];
+ acno < WME_NUM_AC; acno++, ac++) {
+ ac->sched = false;
+ INIT_LIST_HEAD(&ac->tid_q);
+
+ switch (acno) {
+ case WME_AC_BE:
+ ac->qnum = ath_tx_get_qnum(sc,
+ ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
+ break;
+ case WME_AC_BK:
+ ac->qnum = ath_tx_get_qnum(sc,
+ ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK);
+ break;
+ case WME_AC_VI:
+ ac->qnum = ath_tx_get_qnum(sc,
+ ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI);
+ break;
+ case WME_AC_VO:
+ ac->qnum = ath_tx_get_qnum(sc,
+ ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO);
+ break;
}
}
}
/* Cleanupthe pending buffers for the node. */
-void ath_tx_node_cleanup(struct ath_softc *sc,
- struct ath_node *an, bool bh_flag)
+void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
{
int i;
struct ath_atx_ac *ac, *ac_tmp;
@@ -2698,12 +2487,9 @@ void ath_tx_node_cleanup(struct ath_softc *sc,
struct ath_txq *txq;
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) {
- txq = &sc->sc_txq[i];
+ txq = &sc->tx.txq[i];
- if (likely(bh_flag))
- spin_lock_bh(&txq->axq_lock);
- else
- spin_lock(&txq->axq_lock);
+ spin_lock(&txq->axq_lock);
list_for_each_entry_safe(ac,
ac_tmp, &txq->axq_acq, list) {
@@ -2718,36 +2504,14 @@ void ath_tx_node_cleanup(struct ath_softc *sc,
tid_tmp, &ac->tid_q, list) {
list_del(&tid->list);
tid->sched = false;
- ath_tid_drain(sc, txq, tid, bh_flag);
- tid->addba_exchangecomplete = 0;
+ ath_tid_drain(sc, txq, tid);
+ tid->state &= ~AGGR_ADDBA_COMPLETE;
tid->addba_exchangeattempts = 0;
- tid->cleanup_inprogress = false;
+ tid->state &= ~AGGR_CLEANUP;
}
}
- if (likely(bh_flag))
- spin_unlock_bh(&txq->axq_lock);
- else
- spin_unlock(&txq->axq_lock);
- }
- }
-}
-
-/* Cleanup per node transmit state */
-
-void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an)
-{
- if (sc->sc_flags & SC_OP_TXAGGR) {
- struct ath_atx_tid *tid;
- int tidno, i;
-
- /* Init per tid rx state */
- for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno];
- tidno < WME_NUM_TID;
- tidno++, tid++) {
-
- for (i = 0; i < ATH_TID_MAX_BUFS; i++)
- ASSERT(tid->tx_buf[i] == NULL);
+ spin_unlock(&txq->axq_lock);
}
}
}
@@ -2758,6 +2522,8 @@ void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ath_tx_control txctl;
+ memset(&txctl, 0, sizeof(struct ath_tx_control));
+
/*
* As a temporary workaround, assign seq# here; this will likely need
* to be cleaned up to work better with Beacon transmission and virtual
@@ -2766,9 +2532,9 @@ void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
- sc->seq_no += 0x10;
+ sc->tx.seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
+ hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
/* Add the padding after the header if this is not already done */
@@ -2776,8 +2542,7 @@ void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
if (hdrlen & 3) {
padsize = hdrlen % 4;
if (skb_headroom(skb) < padsize) {
- DPRINTF(sc, ATH_DBG_XMIT, "%s: TX CABQ padding "
- "failed\n", __func__);
+ DPRINTF(sc, ATH_DBG_XMIT, "TX CABQ padding failed\n");
dev_kfree_skb_any(skb);
return;
}
@@ -2785,23 +2550,16 @@ void ath_tx_cabq(struct ath_softc *sc, struct sk_buff *skb)
memmove(skb->data, skb->data + padsize, hdrlen);
}
- DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting CABQ packet, skb: %p\n",
- __func__,
- skb);
+ txctl.txq = sc->beacon.cabq;
- memset(&txctl, 0, sizeof(struct ath_tx_control));
- txctl.flags = ATH9K_TXDESC_CAB;
- if (ath_tx_prepare(sc, skb, &txctl) == 0) {
- /*
- * Start DMA mapping.
- * ath_tx_start_dma() will be called either synchronously
- * or asynchrounsly once DMA is complete.
- */
- xmit_map_sg(sc, skb, &txctl);
- } else {
- ath_node_put(sc, txctl.an, ATH9K_BH_STATUS_CHANGE);
- DPRINTF(sc, ATH_DBG_XMIT, "%s: TX CABQ failed\n", __func__);
- dev_kfree_skb_any(skb);
+ DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb);
+
+ if (ath_tx_start(sc, skb, &txctl) != 0) {
+ DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n");
+ goto exit;
}
-}
+ return;
+exit:
+ dev_kfree_skb_any(skb);
+}
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index ecb02bdaab5b..350157fcd080 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -67,7 +67,7 @@
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/jiffies.h>
-#include <net/ieee80211.h>
+#include <linux/ieee80211.h>
#include "atmel.h"
#define DRIVER_MAJOR 0
@@ -569,7 +569,7 @@ static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
static void atmel_command_irq(struct atmel_private *priv);
static int atmel_validate_channel(struct atmel_private *priv, int channel);
static void atmel_management_frame(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u16 frame_len, u8 rssi);
static void atmel_management_timer(u_long a);
static void atmel_send_command(struct atmel_private *priv, int command,
@@ -577,7 +577,7 @@ static void atmel_send_command(struct atmel_private *priv, int command,
static int atmel_send_command_wait(struct atmel_private *priv, int command,
void *cmd, int cmd_size);
static void atmel_transmit_management_frame(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u8 *body, int body_len);
static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
@@ -785,7 +785,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
{
static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
struct atmel_private *priv = netdev_priv(dev);
- struct ieee80211_hdr_4addr header;
+ struct ieee80211_hdr header;
unsigned long flags;
u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
@@ -823,7 +823,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
frame_ctl = IEEE80211_FTYPE_DATA;
header.duration_id = 0;
- header.seq_ctl = 0;
+ header.seq_ctrl = 0;
if (priv->wep_is_on)
frame_ctl |= IEEE80211_FCTL_PROTECTED;
if (priv->operating_mode == IW_MODE_ADHOC) {
@@ -840,7 +840,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
if (priv->use_wpa)
memcpy(&header.addr4, SNAP_RFC1024, 6);
- header.frame_ctl = cpu_to_le16(frame_ctl);
+ header.frame_control = cpu_to_le16(frame_ctl);
/* Copy the wireless header into the card */
atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE);
/* Copy the packet sans its 802.3 header addresses which have been replaced */
@@ -860,7 +860,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
}
static void atmel_transmit_management_frame(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u8 *body, int body_len)
{
u16 buff;
@@ -876,7 +876,7 @@ static void atmel_transmit_management_frame(struct atmel_private *priv,
}
static void fast_rx_path(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
/* fast path: unfragmented packet copy directly into skbuf */
@@ -914,12 +914,11 @@ static void fast_rx_path(struct atmel_private *priv,
}
memcpy(skbp, header->addr1, 6); /* destination address */
- if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
memcpy(&skbp[6], header->addr3, 6);
else
memcpy(&skbp[6], header->addr2, 6); /* source address */
- priv->dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
@@ -950,7 +949,7 @@ static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
}
static void frag_rx_path(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
u8 frag_no, int more_frags)
{
@@ -958,7 +957,7 @@ static void frag_rx_path(struct atmel_private *priv,
u8 source[6];
struct sk_buff *skb;
- if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
memcpy(source, header->addr3, 6);
else
memcpy(source, header->addr2, 6);
@@ -1026,7 +1025,6 @@ static void frag_rx_path(struct atmel_private *priv,
memcpy(skb_put(skb, priv->frag_len + 12),
priv->rx_buf,
priv->frag_len + 12);
- priv->dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, priv->dev);
skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
@@ -1041,7 +1039,7 @@ static void frag_rx_path(struct atmel_private *priv,
static void rx_done_irq(struct atmel_private *priv)
{
int i;
- struct ieee80211_hdr_4addr header;
+ struct ieee80211_hdr header;
for (i = 0;
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
@@ -1068,10 +1066,10 @@ static void rx_done_irq(struct atmel_private *priv)
goto next;
}
- /* Get header as far as end of seq_ctl */
+ /* Get header as far as end of seq_ctrl */
atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
- frame_ctl = le16_to_cpu(header.frame_ctl);
- seq_control = le16_to_cpu(header.seq_ctl);
+ frame_ctl = le16_to_cpu(header.frame_control);
+ seq_control = le16_to_cpu(header.seq_ctrl);
/* probe for CRC use here if needed once five packets have
arrived with the same crc status, we assume we know what's
@@ -1479,7 +1477,6 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
struct net_device *dev;
struct atmel_private *priv;
int rc;
- DECLARE_MAC_BUF(mac);
/* Create the network device object. */
dev = alloc_etherdev(sizeof(*priv));
@@ -1591,8 +1588,8 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
if (!ent)
printk(KERN_WARNING "atmel: unable to create /proc entry.\n");
- printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %s\n",
- dev->name, DRIVER_MAJOR, DRIVER_MINOR, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n",
+ dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr);
return dev;
@@ -1822,7 +1819,7 @@ static int atmel_set_encodeext(struct net_device *dev,
/* Determine and validate the key index */
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
- if (idx < 1 || idx > WEP_KEYS)
+ if (idx < 1 || idx > 4)
return -EINVAL;
idx--;
} else
@@ -1885,7 +1882,7 @@ static int atmel_get_encodeext(struct net_device *dev,
idx = encoding->flags & IW_ENCODE_INDEX;
if (idx) {
- if (idx < 1 || idx > WEP_KEYS)
+ if (idx < 1 || idx > 4)
return -EINVAL;
idx--;
} else
@@ -2800,7 +2797,7 @@ static void handle_beacon_probe(struct atmel_private *priv, u16 capability,
u8 channel)
{
int rejoin = 0;
- int new = capability & MFIE_TYPE_POWER_CONSTRAINT ?
+ int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
SHORT_PREAMBLE : LONG_PREAMBLE;
if (priv->preamble != new) {
@@ -2829,19 +2826,19 @@ static void handle_beacon_probe(struct atmel_private *priv, u16 capability,
static void send_authentication_request(struct atmel_private *priv, u16 system,
u8 *challenge, int challenge_len)
{
- struct ieee80211_hdr_4addr header;
+ struct ieee80211_hdr header;
struct auth_body auth;
- header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
header.duration_id = cpu_to_le16(0x8000);
- header.seq_ctl = 0;
+ header.seq_ctrl = 0;
memcpy(header.addr1, priv->CurrentBSSID, 6);
memcpy(header.addr2, priv->dev->dev_addr, 6);
memcpy(header.addr3, priv->CurrentBSSID, 6);
if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
/* no WEP for authentication frames with TrSeqNo 1 */
- header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
auth.alg = cpu_to_le16(system);
@@ -2864,7 +2861,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
{
u8 *ssid_el_p;
int bodysize;
- struct ieee80211_hdr_4addr header;
+ struct ieee80211_hdr header;
struct ass_req_format {
__le16 capability;
__le16 listen_interval;
@@ -2877,10 +2874,10 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
u8 rates[4];
} body;
- header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
(is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
header.duration_id = cpu_to_le16(0x8000);
- header.seq_ctl = 0;
+ header.seq_ctrl = 0;
memcpy(header.addr1, priv->CurrentBSSID, 6);
memcpy(header.addr2, priv->dev->dev_addr, 6);
@@ -2890,7 +2887,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
if (priv->wep_is_on)
body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
if (priv->preamble == SHORT_PREAMBLE)
- body.capability |= cpu_to_le16(MFIE_TYPE_POWER_CONSTRAINT);
+ body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period);
@@ -2904,10 +2901,10 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
bodysize = 12 + priv->SSID_size;
}
- ssid_el_p[0] = MFIE_TYPE_SSID;
+ ssid_el_p[0] = WLAN_EID_SSID;
ssid_el_p[1] = priv->SSID_size;
memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size);
- ssid_el_p[2 + priv->SSID_size] = MFIE_TYPE_RATES;
+ ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES;
ssid_el_p[3 + priv->SSID_size] = 4; /* len of suported rates */
memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4);
@@ -2915,9 +2912,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc)
}
static int is_frame_from_current_bss(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header)
+ struct ieee80211_hdr *header)
{
- if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS)
return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
else
return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0;
@@ -2965,7 +2962,7 @@ static int retrieve_bss(struct atmel_private *priv)
}
static void store_bss_info(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header, u16 capability,
+ struct ieee80211_hdr *header, u16 capability,
u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len,
u8 *ssid, int is_beacon)
{
@@ -3004,7 +3001,7 @@ static void store_bss_info(struct atmel_private *priv,
else if (capability & WLAN_CAPABILITY_ESS)
priv->BSSinfo[index].BSStype =IW_MODE_INFRA;
- priv->BSSinfo[index].preamble = capability & MFIE_TYPE_POWER_CONSTRAINT ?
+ priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ?
SHORT_PREAMBLE : LONG_PREAMBLE;
}
@@ -3040,7 +3037,7 @@ static void authenticate(struct atmel_private *priv, u16 frame_len)
}
} else if (system == WLAN_AUTH_SHARED_KEY) {
if (trans_seq_no == 0x0002 &&
- auth->el_id == MFIE_TYPE_CHALLENGE) {
+ auth->el_id == WLAN_EID_CHALLENGE) {
send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len);
return;
} else if (trans_seq_no == 0x0004) {
@@ -3183,7 +3180,7 @@ static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype)
}
}
-void atmel_join_bss(struct atmel_private *priv, int bss_index)
+static void atmel_join_bss(struct atmel_private *priv, int bss_index)
{
struct bss_info *bss = &priv->BSSinfo[bss_index];
@@ -3291,12 +3288,12 @@ static void atmel_smooth_qual(struct atmel_private *priv)
/* deals with incoming managment frames. */
static void atmel_management_frame(struct atmel_private *priv,
- struct ieee80211_hdr_4addr *header,
+ struct ieee80211_hdr *header,
u16 frame_len, u8 rssi)
{
u16 subtype;
- subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE;
+ subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE;
switch (subtype) {
case IEEE80211_STYPE_BEACON:
case IEEE80211_STYPE_PROBE_RESP:
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index 427b8203e3f9..a53c378e7484 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -718,7 +718,6 @@ struct b43_wldev {
bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */
bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */
- bool short_slot; /* TRUE, if short slot timing is enabled. */
bool radio_hw_enable; /* saved state of radio hardware enabled state */
bool suspend_in_progress; /* TRUE, if we are in a suspend/resume cycle */
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c
index 06a01da80160..e04fc91f569e 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/b43/debugfs.c
@@ -731,6 +731,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev)
add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0);
+ add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0);
#undef add_dyn_dbg
}
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h
index 22ffd02ba554..7886cbe2d1d1 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/b43/debugfs.h
@@ -12,6 +12,7 @@ enum b43_dyndbg { /* Dynamic debugging features */
B43_DBG_PWORK_STOP,
B43_DBG_LO,
B43_DBG_FIRMWARE,
+ B43_DBG_KEYS,
__B43_NR_DYNDBG,
};
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 098f886976f6..6d65a02b7052 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -1387,13 +1387,11 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
info = IEEE80211_SKB_CB(meta->skb);
- memset(&info->status, 0, sizeof(info->status));
-
/*
* Call back to inform the ieee80211 subsystem about
* the status of the transmission.
*/
- frame_succeed = b43_fill_txstatus_report(info, status);
+ frame_succeed = b43_fill_txstatus_report(dev, info, status);
#ifdef CONFIG_B43_DEBUG
if (frame_succeed)
ring->nr_succeed_tx_packets++;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 14c44df584d0..7b31a327b24a 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -703,13 +703,11 @@ static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
static void b43_short_slot_timing_enable(struct b43_wldev *dev)
{
b43_set_slot_time(dev, 9);
- dev->short_slot = 1;
}
static void b43_short_slot_timing_disable(struct b43_wldev *dev)
{
b43_set_slot_time(dev, 20);
- dev->short_slot = 0;
}
/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
@@ -994,6 +992,52 @@ static void b43_clear_keys(struct b43_wldev *dev)
b43_key_clear(dev, i);
}
+static void b43_dump_keymemory(struct b43_wldev *dev)
+{
+ unsigned int i, index, offset;
+ DECLARE_MAC_BUF(macbuf);
+ u8 mac[ETH_ALEN];
+ u16 algo;
+ u32 rcmta0;
+ u16 rcmta1;
+ u64 hf;
+ struct b43_key *key;
+
+ if (!b43_debug(dev, B43_DBG_KEYS))
+ return;
+
+ hf = b43_hf_read(dev);
+ b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n",
+ !!(hf & B43_HF_USEDEFKEYS));
+ for (index = 0; index < dev->max_nr_keys; index++) {
+ key = &(dev->key[index]);
+ printk(KERN_DEBUG "Key slot %02u: %s",
+ index, (key->keyconf == NULL) ? " " : "*");
+ offset = dev->ktp + (index * B43_SEC_KEYSIZE);
+ for (i = 0; i < B43_SEC_KEYSIZE; i += 2) {
+ u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i);
+ printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF));
+ }
+
+ algo = b43_shm_read16(dev, B43_SHM_SHARED,
+ B43_SHM_SH_KEYIDXBLOCK + (index * 2));
+ printk(" Algo: %04X/%02X", algo, key->algorithm);
+
+ if (index >= 4) {
+ rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA,
+ ((index - 4) * 2) + 0);
+ rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA,
+ ((index - 4) * 2) + 1);
+ *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0);
+ *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1);
+ printk(" MAC: %s",
+ print_mac(macbuf, mac));
+ } else
+ printk(" DEFAULT KEY");
+ printk("\n");
+ }
+}
+
void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags)
{
u32 macctl;
@@ -1339,25 +1383,6 @@ u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
return antenna_nr;
}
-static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
-{
- antenna = b43_ieee80211_antenna_sanitize(dev, antenna);
- switch (antenna) {
- case 0: /* default/diversity */
- return B43_ANTENNA_DEFAULT;
- case 1: /* Antenna 0 */
- return B43_ANTENNA0;
- case 2: /* Antenna 1 */
- return B43_ANTENNA1;
- case 3: /* Antenna 2 */
- return B43_ANTENNA2;
- case 4: /* Antenna 3 */
- return B43_ANTENNA3;
- default:
- return B43_ANTENNA_DEFAULT;
- }
-}
-
/* Convert a b43 antenna number value to the PHY TX control value. */
static u16 b43_antenna_to_phyctl(int antenna)
{
@@ -1399,7 +1424,7 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
len, ram_offset, shm_size_offset, rate);
/* Write the PHY TX control parameters. */
- antenna = b43_antenna_from_ieee80211(dev, info->antenna_sel_tx);
+ antenna = B43_ANTENNA_DEFAULT;
antenna = b43_antenna_to_phyctl(antenna);
ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
/* We can't send beacons with short preamble. Would get PHY errors. */
@@ -1693,25 +1718,6 @@ static void b43_update_templates(struct b43_wl *wl)
queue_work(wl->hw->workqueue, &wl->beacon_update_trigger);
}
-static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len)
-{
- u32 tmp;
- u16 i, len;
-
- len = min((u16) ssid_len, (u16) 0x100);
- for (i = 0; i < len; i += sizeof(u32)) {
- tmp = (u32) (ssid[i + 0]);
- if (i + 1 < len)
- tmp |= (u32) (ssid[i + 1]) << 8;
- if (i + 2 < len)
- tmp |= (u32) (ssid[i + 2]) << 16;
- if (i + 3 < len)
- tmp |= (u32) (ssid[i + 3]) << 24;
- b43_shm_write32(dev, B43_SHM_SHARED, 0x380 + i, tmp);
- }
- b43_shm_write16(dev, B43_SHM_SHARED, 0x48, len);
-}
-
static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
{
b43_time_lock(dev);
@@ -3339,15 +3345,31 @@ init_failure:
return err;
}
-static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+/* Write the short and long frame retry limit values. */
+static void b43_set_retry_limits(struct b43_wldev *dev,
+ unsigned int short_retry,
+ unsigned int long_retry)
+{
+ /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing
+ * the chip-internal counter. */
+ short_retry = min(short_retry, (unsigned int)0xF);
+ long_retry = min(long_retry, (unsigned int)0xF);
+
+ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_SRLIMIT,
+ short_retry);
+ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_LRLIMIT,
+ long_retry);
+}
+
+static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
struct b43_phy *phy;
+ struct ieee80211_conf *conf = &hw->conf;
unsigned long flags;
int antenna;
int err = 0;
- u32 savedirqs;
mutex_lock(&wl->mutex);
@@ -3358,33 +3380,20 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
dev = wl->current_dev;
phy = &dev->phy;
- /* Disable IRQs while reconfiguring the device.
- * This makes it possible to drop the spinlock throughout
- * the reconfiguration process. */
- spin_lock_irqsave(&wl->irq_lock, flags);
- if (b43_status(dev) < B43_STAT_STARTED) {
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- goto out_unlock_mutex;
- }
- savedirqs = b43_interrupt_disable(dev, B43_IRQ_ALL);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- b43_synchronize_irq(dev);
+ b43_mac_suspend(dev);
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
+ conf->long_frame_max_tx_count);
+ changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS;
+ if (!changed)
+ goto out_mac_enable;
/* Switch to the requested channel.
* The firmware takes care of races with the TX handler. */
if (conf->channel->hw_value != phy->channel)
b43_switch_channel(dev, conf->channel->hw_value);
- /* Enable/Disable ShortSlot timing. */
- if ((!!(conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME)) !=
- dev->short_slot) {
- B43_WARN_ON(phy->type != B43_PHYTYPE_G);
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME)
- b43_short_slot_timing_enable(dev);
- else
- b43_short_slot_timing_disable(dev);
- }
-
dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP);
/* Adjust the desired TX power level. */
@@ -3399,9 +3408,9 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
}
/* Antennas for RX and management frame TX. */
- antenna = b43_antenna_from_ieee80211(dev, conf->antenna_sel_tx);
+ antenna = B43_ANTENNA_DEFAULT;
b43_mgmtframe_txantenna(dev, antenna);
- antenna = b43_antenna_from_ieee80211(dev, conf->antenna_sel_rx);
+ antenna = B43_ANTENNA_DEFAULT;
if (phy->ops->set_rx_antenna)
phy->ops->set_rx_antenna(dev, antenna);
@@ -3425,16 +3434,91 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
}
}
- spin_lock_irqsave(&wl->irq_lock, flags);
- b43_interrupt_enable(dev, savedirqs);
- mmiowb();
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- out_unlock_mutex:
+out_mac_enable:
+ b43_mac_enable(dev);
+out_unlock_mutex:
mutex_unlock(&wl->mutex);
return err;
}
+static void b43_update_basic_rates(struct b43_wldev *dev, u64 brates)
+{
+ struct ieee80211_supported_band *sband =
+ dev->wl->hw->wiphy->bands[b43_current_band(dev->wl)];
+ struct ieee80211_rate *rate;
+ int i;
+ u16 basic, direct, offset, basic_offset, rateptr;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ rate = &sband->bitrates[i];
+
+ if (b43_is_cck_rate(rate->hw_value)) {
+ direct = B43_SHM_SH_CCKDIRECT;
+ basic = B43_SHM_SH_CCKBASIC;
+ offset = b43_plcp_get_ratecode_cck(rate->hw_value);
+ offset &= 0xF;
+ } else {
+ direct = B43_SHM_SH_OFDMDIRECT;
+ basic = B43_SHM_SH_OFDMBASIC;
+ offset = b43_plcp_get_ratecode_ofdm(rate->hw_value);
+ offset &= 0xF;
+ }
+
+ rate = ieee80211_get_response_rate(sband, brates, rate->bitrate);
+
+ if (b43_is_cck_rate(rate->hw_value)) {
+ basic_offset = b43_plcp_get_ratecode_cck(rate->hw_value);
+ basic_offset &= 0xF;
+ } else {
+ basic_offset = b43_plcp_get_ratecode_ofdm(rate->hw_value);
+ basic_offset &= 0xF;
+ }
+
+ /*
+ * Get the pointer that we need to point to
+ * from the direct map
+ */
+ rateptr = b43_shm_read16(dev, B43_SHM_SHARED,
+ direct + 2 * basic_offset);
+ /* and write it to the basic map */
+ b43_shm_write16(dev, B43_SHM_SHARED, basic + 2 * offset,
+ rateptr);
+ }
+}
+
+static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf,
+ u32 changed)
+{
+ struct b43_wl *wl = hw_to_b43_wl(hw);
+ struct b43_wldev *dev;
+
+ mutex_lock(&wl->mutex);
+
+ dev = wl->current_dev;
+ if (!dev || b43_status(dev) < B43_STAT_STARTED)
+ goto out_unlock_mutex;
+ b43_mac_suspend(dev);
+
+ if (changed & BSS_CHANGED_BASIC_RATES)
+ b43_update_basic_rates(dev, conf->basic_rates);
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (conf->use_short_slot)
+ b43_short_slot_timing_enable(dev);
+ else
+ b43_short_slot_timing_disable(dev);
+ }
+
+ b43_mac_enable(dev);
+out_unlock_mutex:
+ mutex_unlock(&wl->mutex);
+
+ return;
+}
+
static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
const u8 *local_addr, const u8 *addr,
struct ieee80211_key_conf *key)
@@ -3445,7 +3529,6 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
u8 algorithm;
u8 index;
int err;
- DECLARE_MAC_BUF(mac);
if (modparam_nohwcrypt)
return -ENOSPC; /* User disabled HW-crypto */
@@ -3528,15 +3611,18 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
default:
B43_WARN_ON(1);
}
+
out_unlock:
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- mutex_unlock(&wl->mutex);
if (!err) {
b43dbg(wl, "%s hardware based encryption for keyidx: %d, "
- "mac: %s\n",
+ "mac: %pM\n",
cmd == SET_KEY ? "Using" : "Disabling", key->keyidx,
- print_mac(mac, addr));
+ addr);
+ b43_dump_keymemory(dev);
}
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ mutex_unlock(&wl->mutex);
+
return err;
}
@@ -3598,8 +3684,6 @@ static int b43_op_config_interface(struct ieee80211_hw *hw,
if (b43_is_mode(wl, NL80211_IFTYPE_AP) ||
b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) {
B43_WARN_ON(vif->type != wl->if_type);
- if (conf->changed & IEEE80211_IFCC_SSID)
- b43_set_ssid(dev, conf->ssid, conf->ssid_len);
if (conf->changed & IEEE80211_IFCC_BEACON)
b43_update_templates(wl);
} else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
@@ -3878,22 +3962,6 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
#endif /* CONFIG_SSB_DRIVER_PCICORE */
}
-/* Write the short and long frame retry limit values. */
-static void b43_set_retry_limits(struct b43_wldev *dev,
- unsigned int short_retry,
- unsigned int long_retry)
-{
- /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing
- * the chip-internal counter. */
- short_retry = min(short_retry, (unsigned int)0xF);
- long_retry = min(long_retry, (unsigned int)0xF);
-
- b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_SRLIMIT,
- short_retry);
- b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_LRLIMIT,
- long_retry);
-}
-
static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle)
{
u16 pu_delay;
@@ -4214,26 +4282,6 @@ static void b43_op_stop(struct ieee80211_hw *hw)
cancel_work_sync(&(wl->txpower_adjust_work));
}
-static int b43_op_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry_limit, u32 long_retry_limit)
-{
- struct b43_wl *wl = hw_to_b43_wl(hw);
- struct b43_wldev *dev;
- int err = 0;
-
- mutex_lock(&wl->mutex);
- dev = wl->current_dev;
- if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED))) {
- err = -ENODEV;
- goto out_unlock;
- }
- b43_set_retry_limits(dev, short_retry_limit, long_retry_limit);
-out_unlock:
- mutex_unlock(&wl->mutex);
-
- return err;
-}
-
static int b43_op_beacon_set_tim(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, bool set)
{
@@ -4263,6 +4311,7 @@ static const struct ieee80211_ops b43_hw_ops = {
.add_interface = b43_op_add_interface,
.remove_interface = b43_op_remove_interface,
.config = b43_op_config,
+ .bss_info_changed = b43_op_bss_info_changed,
.config_interface = b43_op_config_interface,
.configure_filter = b43_op_configure_filter,
.set_key = b43_op_set_key,
@@ -4270,7 +4319,6 @@ static const struct ieee80211_ops b43_hw_ops = {
.get_tx_stats = b43_op_get_tx_stats,
.start = b43_op_start,
.stop = b43_op_stop,
- .set_retry_limit = b43_op_set_retry_limit,
.set_tim = b43_op_beacon_set_tim,
.sta_notify = b43_op_sta_notify,
};
@@ -4588,7 +4636,7 @@ static int b43_wireless_init(struct ssb_device *dev)
BIT(NL80211_IFTYPE_ADHOC);
hw->queues = b43_modparam_qos ? 4 : 1;
- hw->max_altrates = 1;
+ hw->max_rates = 2;
SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac))
SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c
index 0f1a84c9de61..7fe9d1701624 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/b43/phy_a.c
@@ -77,7 +77,7 @@ static s8 b43_aphy_estimate_power_out(struct b43_wldev *dev, s8 tssi)
}
#endif
-void b43_radio_set_tx_iq(struct b43_wldev *dev)
+static void b43_radio_set_tx_iq(struct b43_wldev *dev)
{
static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 };
static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A };
@@ -147,7 +147,7 @@ static void aphy_channel_switch(struct b43_wldev *dev, unsigned int channel)
//FIXME b43_phy_xmitpower(dev);
}
-void b43_radio_init2060(struct b43_wldev *dev)
+static void b43_radio_init2060(struct b43_wldev *dev)
{
b43_radio_write16(dev, 0x0004, 0x00C0);
b43_radio_write16(dev, 0x0005, 0x0008);
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index af37abccccb3..026b61c03fb9 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -178,13 +178,27 @@ void b43_phy_unlock(struct b43_wldev *dev)
b43_power_saving_ctl_bits(dev, 0);
}
+static inline void assert_mac_suspended(struct b43_wldev *dev)
+{
+ if (!B43_DEBUG)
+ return;
+ if ((b43_status(dev) >= B43_STAT_INITIALIZED) &&
+ (dev->mac_suspended <= 0)) {
+ b43dbg(dev->wl, "PHY/RADIO register access with "
+ "enabled MAC.\n");
+ dump_stack();
+ }
+}
+
u16 b43_radio_read(struct b43_wldev *dev, u16 reg)
{
+ assert_mac_suspended(dev);
return dev->phy.ops->radio_read(dev, reg);
}
void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
{
+ assert_mac_suspended(dev);
dev->phy.ops->radio_write(dev, reg, value);
}
@@ -208,11 +222,13 @@ void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set)
u16 b43_phy_read(struct b43_wldev *dev, u16 reg)
{
+ assert_mac_suspended(dev);
return dev->phy.ops->phy_read(dev, reg);
}
void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value)
{
+ assert_mac_suspended(dev);
dev->phy.ops->phy_write(dev, reg, value);
}
@@ -280,8 +296,10 @@ void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
state = RFKILL_STATE_SOFT_BLOCKED;
}
+ b43_mac_suspend(dev);
phy->ops->software_rfkill(dev, state);
phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
+ b43_mac_enable(dev);
}
/**
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index 232181f6333c..caac4a45f0bf 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -54,7 +54,7 @@ static const s8 b43_tssi2dbm_g_table[] = {
-20, -20, -20, -20,
};
-const u8 b43_radio_channel_codes_bg[] = {
+static const u8 b43_radio_channel_codes_bg[] = {
12, 17, 22, 27,
32, 37, 42, 47,
52, 57, 62, 67,
@@ -215,9 +215,9 @@ void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev,
}
/* Adjust the transmission power output (G-PHY) */
-void b43_set_txpower_g(struct b43_wldev *dev,
- const struct b43_bbatt *bbatt,
- const struct b43_rfatt *rfatt, u8 tx_control)
+static void b43_set_txpower_g(struct b43_wldev *dev,
+ const struct b43_bbatt *bbatt,
+ const struct b43_rfatt *rfatt, u8 tx_control)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
@@ -383,14 +383,14 @@ static void b43_set_original_gains(struct b43_wldev *dev)
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
-void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val)
+static void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val)
{
b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset);
b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val);
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
-s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset)
+static s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset)
{
u16 val;
@@ -401,7 +401,7 @@ s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset)
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
-void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val)
+static void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val)
{
u16 i;
s16 tmp;
@@ -415,7 +415,7 @@ void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val)
}
/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
-void b43_nrssi_mem_update(struct b43_wldev *dev)
+static void b43_nrssi_mem_update(struct b43_wldev *dev)
{
struct b43_phy_g *gphy = dev->phy.g;
s16 i, delta;
@@ -589,7 +589,7 @@ static void b43_calc_nrssi_offset(struct b43_wldev *dev)
b43_phy_write(dev, 0x0811, backup[1]);
}
-void b43_calc_nrssi_slope(struct b43_wldev *dev)
+static void b43_calc_nrssi_slope(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
@@ -1354,7 +1354,7 @@ struct init2050_saved_values {
u16 phy_syncctl;
};
-u16 b43_radio_init2050(struct b43_wldev *dev)
+static u16 b43_radio_init2050(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
struct init2050_saved_values sav;
@@ -3047,6 +3047,8 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
int rfatt, bbatt;
u8 tx_control;
+ b43_mac_suspend(dev);
+
spin_lock_irq(&dev->wl->irq_lock);
/* Calculate the new attenuation values. */
@@ -3103,6 +3105,8 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
gphy->tx_control);
b43_radio_unlock(dev);
b43_phy_unlock(dev);
+
+ b43_mac_enable(dev);
}
static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev,
@@ -3215,9 +3219,9 @@ static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev)
struct b43_phy *phy = &dev->phy;
struct b43_phy_g *gphy = phy->g;
+ b43_mac_suspend(dev);
//TODO: update_aci_moving_average
if (gphy->aci_enable && gphy->aci_wlan_automatic) {
- b43_mac_suspend(dev);
if (!gphy->aci_enable && 1 /*TODO: not scanning? */ ) {
if (0 /*TODO: bunch of conditions */ ) {
phy->ops->interf_mitigation(dev,
@@ -3227,12 +3231,12 @@ static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev)
if (/*(aci_average > 1000) &&*/ !b43_gphy_aci_scan(dev))
phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE);
}
- b43_mac_enable(dev);
} else if (gphy->interfmode == B43_INTERFMODE_NONWLAN &&
phy->rev == 1) {
//TODO: implement rev1 workaround
}
b43_lo_g_maintanance_work(dev);
+ b43_mac_enable(dev);
}
static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index 401591267592..1036bef8c4cc 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -587,9 +587,8 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
spin_lock(&q->lock); /* IRQs are already disabled. */
info = IEEE80211_SKB_CB(pack->skb);
- memset(&info->status, 0, sizeof(info->status));
- b43_fill_txstatus_report(info, status);
+ b43_fill_txstatus_report(dev, info, status);
total_len = pack->skb->len + b43_txhdr_size(dev);
total_len = roundup(total_len, 4);
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 2fabcf8f0474..eae9b8052658 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -46,7 +46,6 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp)
case 0x6E:
return 3;
}
- B43_WARN_ON(1);
return -1;
}
@@ -73,7 +72,6 @@ static u8 b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
case 0xC:
return base + 7;
}
- B43_WARN_ON(1);
return -1;
}
@@ -185,7 +183,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
u8 *_txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_info *info,
+ struct ieee80211_tx_info *info,
u16 cookie)
{
struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr;
@@ -202,6 +200,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
u16 phy_ctl = 0;
u8 extra_ft = 0;
struct ieee80211_rate *txrate;
+ struct ieee80211_tx_rate *rates;
memset(txhdr, 0, sizeof(*txhdr));
@@ -291,7 +290,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
phy_ctl |= B43_TXH_PHY_ENC_OFDM;
else
phy_ctl |= B43_TXH_PHY_ENC_CCK;
- if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
phy_ctl |= B43_TXH_PHY_SHORTPRMBL;
switch (b43_ieee80211_antenna_sanitize(dev, info->antenna_sel_tx)) {
@@ -314,6 +313,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
B43_WARN_ON(1);
}
+ rates = info->control.rates;
/* MAC control */
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
mac_ctl |= B43_TXH_MAC_ACK;
@@ -324,12 +324,22 @@ int b43_generate_txhdr(struct b43_wldev *dev,
mac_ctl |= B43_TXH_MAC_STMSDU;
if (phy->type == B43_PHYTYPE_A)
mac_ctl |= B43_TXH_MAC_5GHZ;
- if (info->flags & IEEE80211_TX_CTL_LONG_RETRY_LIMIT)
+
+ /* Overwrite rates[0].count to make the retry calculation
+ * in the tx status easier. need the actual retry limit to
+ * detect whether the fallback rate was used.
+ */
+ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) {
+ rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count;
mac_ctl |= B43_TXH_MAC_LONGFRAME;
+ } else {
+ rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count;
+ }
/* Generate the RTS or CTS-to-self frame */
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
+ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) {
unsigned int len;
struct ieee80211_hdr *hdr;
int rts_rate, rts_rate_fb;
@@ -344,7 +354,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
rts_rate_fb = b43_calc_fallback_rate(rts_rate);
rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
- if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
+ if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
struct ieee80211_cts *cts;
if (b43_is_old_txhdr_format(dev)) {
@@ -596,6 +606,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
phytype == B43_PHYTYPE_A);
else
status.rate_idx = b43_plcp_get_bitrate_idx_cck(plcp);
+ if (unlikely(status.rate_idx == -1))
+ goto drop;
status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT);
/*
@@ -687,10 +699,18 @@ void b43_handle_txstatus(struct b43_wldev *dev,
/* Fill out the mac80211 TXstatus report based on the b43-specific
* txstatus report data. This returns a boolean whether the frame was
* successfully transmitted. */
-bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+bool b43_fill_txstatus_report(struct b43_wldev *dev,
+ struct ieee80211_tx_info *report,
const struct b43_txstatus *status)
{
bool frame_success = 1;
+ int retry_limit;
+
+ /* preserve the confiured retry limit before clearing the status
+ * The xmit function has overwritten the rc's value with the actual
+ * retry limit done by the hardware */
+ retry_limit = report->status.rates[0].count;
+ ieee80211_tx_info_clear_status(report);
if (status->acked) {
/* The frame was ACKed. */
@@ -700,14 +720,32 @@ bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) {
/* ...but we expected an ACK. */
frame_success = 0;
- report->status.excessive_retries = 1;
}
}
if (status->frame_count == 0) {
/* The frame was not transmitted at all. */
- report->status.retry_count = 0;
- } else
- report->status.retry_count = status->frame_count - 1;
+ report->status.rates[0].count = 0;
+ } else if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) {
+ /*
+ * If the short retries (RTS, not data frame) have exceeded
+ * the limit, the hw will not have tried the selected rate,
+ * but will have used the fallback rate instead.
+ * Don't let the rate control count attempts for the selected
+ * rate in this case, otherwise the statistics will be off.
+ */
+ report->status.rates[0].count = 0;
+ report->status.rates[1].count = status->frame_count;
+ } else {
+ if (status->frame_count > retry_limit) {
+ report->status.rates[0].count = retry_limit;
+ report->status.rates[1].count = status->frame_count -
+ retry_limit;
+
+ } else {
+ report->status.rates[0].count = status->frame_count;
+ report->status.rates[1].idx = -1;
+ }
+ }
return frame_success;
}
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h
index 0215faf47541..4fb2a190f7a7 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/b43/xmit.h
@@ -178,7 +178,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
u8 * txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_info *txctl, u16 cookie);
+ struct ieee80211_tx_info *txctl, u16 cookie);
/* Transmit Status */
struct b43_txstatus {
@@ -294,7 +294,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr);
void b43_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status);
-bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+bool b43_fill_txstatus_report(struct b43_wldev *dev,
+ struct ieee80211_tx_info *report,
const struct b43_txstatus *status);
void b43_tx_suspend(struct b43_wldev *dev);
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index c40078e1fff9..97b0e06dfe21 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -145,6 +145,10 @@
#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */
#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */
/* SHM_SHARED rate tables */
+#define B43legacy_SHM_SH_OFDMDIRECT 0x0480 /* Pointer to OFDM direct map */
+#define B43legacy_SHM_SH_OFDMBASIC 0x04A0 /* Pointer to OFDM basic rate map */
+#define B43legacy_SHM_SH_CCKDIRECT 0x04C0 /* Pointer to CCK direct map */
+#define B43legacy_SHM_SH_CCKBASIC 0x04E0 /* Pointer to CCK basic rate map */
/* SHM_SHARED microcode soft registers */
#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */
#define B43legacy_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */
@@ -663,7 +667,6 @@ struct b43legacy_wldev {
bool bad_frames_preempt;/* Use "Bad Frames Preemption". */
bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM). */
bool short_preamble; /* TRUE if using short preamble. */
- bool short_slot; /* TRUE if using short slot timing. */
bool radio_hw_enable; /* State of radio hardware enable bit. */
/* PHY/Radio device. */
diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/b43legacy/debugfs.c
index 03ce0821a60e..1f85ac569fec 100644
--- a/drivers/net/wireless/b43legacy/debugfs.c
+++ b/drivers/net/wireless/b43legacy/debugfs.c
@@ -211,7 +211,7 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
struct b43legacy_dfs_file *dfile;
ssize_t uninitialized_var(ret);
char *buf;
- const size_t bufsize = 1024 * 128;
+ const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
const size_t buforder = get_order(bufsize);
int err = 0;
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index fb6819e40f38..3649fc367098 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -919,7 +919,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev,
if (!ring->txhdr_cache)
goto err_kfree_meta;
- dma_test = ssb_dma_map_single(dev->dev,
+ dma_test = ssb_dma_map_single(dev->dev,
ring->txhdr_cache,
sizeof(struct b43legacy_txhdr_fw3),
DMA_TO_DEVICE);
@@ -1411,6 +1411,7 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
struct b43legacy_dmaring *ring;
struct b43legacy_dmadesc_generic *desc;
struct b43legacy_dmadesc_meta *meta;
+ int retry_limit;
int slot;
ring = parse_cookie(dev, status->cookie, &slot);
@@ -1437,25 +1438,42 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
struct ieee80211_tx_info *info;
BUG_ON(!meta->skb);
info = IEEE80211_SKB_CB(meta->skb);
- /* Call back to inform the ieee80211 subsystem about the
- * status of the transmission.
- * Some fields of txstat are already filled in dma_tx().
- */
- memset(&info->status, 0, sizeof(info->status));
+ /* preserve the confiured retry limit before clearing the status
+ * The xmit function has overwritten the rc's value with the actual
+ * retry limit done by the hardware */
+ retry_limit = info->status.rates[0].count;
+ ieee80211_tx_info_clear_status(info);
- if (status->acked) {
+ if (status->acked)
info->flags |= IEEE80211_TX_STAT_ACK;
+
+ if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) {
+ /*
+ * If the short retries (RTS, not data frame) have exceeded
+ * the limit, the hw will not have tried the selected rate,
+ * but will have used the fallback rate instead.
+ * Don't let the rate control count attempts for the selected
+ * rate in this case, otherwise the statistics will be off.
+ */
+ info->status.rates[0].count = 0;
+ info->status.rates[1].count = status->frame_count;
} else {
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
- info->status.excessive_retries = 1;
+ if (status->frame_count > retry_limit) {
+ info->status.rates[0].count = retry_limit;
+ info->status.rates[1].count = status->frame_count -
+ retry_limit;
+
+ } else {
+ info->status.rates[0].count = status->frame_count;
+ info->status.rates[1].idx = -1;
+ }
}
- if (status->frame_count == 0) {
- /* The frame was not transmitted at all. */
- info->status.retry_count = 0;
- } else
- info->status.retry_count = status->frame_count
- - 1;
+
+ /* Call back to inform the ieee80211 subsystem about the
+ * status of the transmission.
+ * Some fields of txstat are already filled in dma_tx().
+ */
ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb);
/* skb is freed by ieee80211_tx_status_irqsafe() */
meta->skb = NULL;
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index c66d57560e7c..c1324e31d2f6 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -576,13 +576,11 @@ static void b43legacy_set_slot_time(struct b43legacy_wldev *dev,
static void b43legacy_short_slot_timing_enable(struct b43legacy_wldev *dev)
{
b43legacy_set_slot_time(dev, 9);
- dev->short_slot = 1;
}
static void b43legacy_short_slot_timing_disable(struct b43legacy_wldev *dev)
{
b43legacy_set_slot_time(dev, 20);
- dev->short_slot = 0;
}
/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
@@ -1160,29 +1158,6 @@ static void b43legacy_update_templates(struct b43legacy_wl *wl)
wl->beacon1_uploaded = 0;
}
-static void b43legacy_set_ssid(struct b43legacy_wldev *dev,
- const u8 *ssid, u8 ssid_len)
-{
- u32 tmp;
- u16 i;
- u16 len;
-
- len = min((u16)ssid_len, (u16)0x100);
- for (i = 0; i < len; i += sizeof(u32)) {
- tmp = (u32)(ssid[i + 0]);
- if (i + 1 < len)
- tmp |= (u32)(ssid[i + 1]) << 8;
- if (i + 2 < len)
- tmp |= (u32)(ssid[i + 2]) << 16;
- if (i + 3 < len)
- tmp |= (u32)(ssid[i + 3]) << 24;
- b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
- 0x380 + i, tmp);
- }
- b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
- 0x48, len);
-}
-
static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev,
u16 beacon_int)
{
@@ -2556,26 +2531,27 @@ init_failure:
return err;
}
-static int b43legacy_antenna_from_ieee80211(u8 antenna)
+/* Write the short and long frame retry limit values. */
+static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev,
+ unsigned int short_retry,
+ unsigned int long_retry)
{
- switch (antenna) {
- case 0: /* default/diversity */
- return B43legacy_ANTENNA_DEFAULT;
- case 1: /* Antenna 0 */
- return B43legacy_ANTENNA0;
- case 2: /* Antenna 1 */
- return B43legacy_ANTENNA1;
- default:
- return B43legacy_ANTENNA_DEFAULT;
- }
+ /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing
+ * the chip-internal counter. */
+ short_retry = min(short_retry, (unsigned int)0xF);
+ long_retry = min(long_retry, (unsigned int)0xF);
+
+ b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry);
+ b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry);
}
static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
- struct ieee80211_conf *conf)
+ u32 changed)
{
struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
struct b43legacy_wldev *dev;
struct b43legacy_phy *phy;
+ struct ieee80211_conf *conf = &hw->conf;
unsigned long flags;
unsigned int new_phymode = 0xFFFF;
int antenna_tx;
@@ -2583,13 +2559,21 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
int err = 0;
u32 savedirqs;
- antenna_tx = b43legacy_antenna_from_ieee80211(conf->antenna_sel_tx);
- antenna_rx = b43legacy_antenna_from_ieee80211(conf->antenna_sel_rx);
+ antenna_tx = B43legacy_ANTENNA_DEFAULT;
+ antenna_rx = B43legacy_ANTENNA_DEFAULT;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
phy = &dev->phy;
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ b43legacy_set_retry_limits(dev,
+ conf->short_frame_max_tx_count,
+ conf->long_frame_max_tx_count);
+ changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS;
+ if (!changed)
+ goto out_unlock_mutex;
+
/* Switch the PHY mode (if necessary). */
switch (conf->channel->band) {
case IEEE80211_BAND_2GHZ:
@@ -2622,16 +2606,6 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
if (conf->channel->hw_value != phy->channel)
b43legacy_radio_selectchannel(dev, conf->channel->hw_value, 0);
- /* Enable/Disable ShortSlot timing. */
- if ((!!(conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME))
- != dev->short_slot) {
- B43legacy_WARN_ON(phy->type != B43legacy_PHYTYPE_G);
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME)
- b43legacy_short_slot_timing_enable(dev);
- else
- b43legacy_short_slot_timing_disable(dev);
- }
-
dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP);
/* Adjust the desired TX power level. */
@@ -2676,6 +2650,104 @@ out_unlock_mutex:
return err;
}
+static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u64 brates)
+{
+ struct ieee80211_supported_band *sband =
+ dev->wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
+ struct ieee80211_rate *rate;
+ int i;
+ u16 basic, direct, offset, basic_offset, rateptr;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ rate = &sband->bitrates[i];
+
+ if (b43legacy_is_cck_rate(rate->hw_value)) {
+ direct = B43legacy_SHM_SH_CCKDIRECT;
+ basic = B43legacy_SHM_SH_CCKBASIC;
+ offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value);
+ offset &= 0xF;
+ } else {
+ direct = B43legacy_SHM_SH_OFDMDIRECT;
+ basic = B43legacy_SHM_SH_OFDMBASIC;
+ offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value);
+ offset &= 0xF;
+ }
+
+ rate = ieee80211_get_response_rate(sband, brates, rate->bitrate);
+
+ if (b43legacy_is_cck_rate(rate->hw_value)) {
+ basic_offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value);
+ basic_offset &= 0xF;
+ } else {
+ basic_offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value);
+ basic_offset &= 0xF;
+ }
+
+ /*
+ * Get the pointer that we need to point to
+ * from the direct map
+ */
+ rateptr = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED,
+ direct + 2 * basic_offset);
+ /* and write it to the basic map */
+ b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+ basic + 2 * offset, rateptr);
+ }
+}
+
+static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf,
+ u32 changed)
+{
+ struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+ struct b43legacy_wldev *dev;
+ struct b43legacy_phy *phy;
+ unsigned long flags;
+ u32 savedirqs;
+
+ mutex_lock(&wl->mutex);
+
+ dev = wl->current_dev;
+ phy = &dev->phy;
+
+ /* Disable IRQs while reconfiguring the device.
+ * This makes it possible to drop the spinlock throughout
+ * the reconfiguration process. */
+ spin_lock_irqsave(&wl->irq_lock, flags);
+ if (b43legacy_status(dev) < B43legacy_STAT_STARTED) {
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ goto out_unlock_mutex;
+ }
+ savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ b43legacy_synchronize_irq(dev);
+
+ b43legacy_mac_suspend(dev);
+
+ if (changed & BSS_CHANGED_BASIC_RATES)
+ b43legacy_update_basic_rates(dev, conf->basic_rates);
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (conf->use_short_slot)
+ b43legacy_short_slot_timing_enable(dev);
+ else
+ b43legacy_short_slot_timing_disable(dev);
+ }
+
+ b43legacy_mac_enable(dev);
+
+ spin_lock_irqsave(&wl->irq_lock, flags);
+ b43legacy_interrupt_enable(dev, savedirqs);
+ /* XXX: why? */
+ mmiowb();
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ out_unlock_mutex:
+ mutex_unlock(&wl->mutex);
+
+ return;
+}
+
static void b43legacy_op_configure_filter(struct ieee80211_hw *hw,
unsigned int changed,
unsigned int *fflags,
@@ -2735,7 +2807,6 @@ static int b43legacy_op_config_interface(struct ieee80211_hw *hw,
if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) {
B43legacy_WARN_ON(vif->type != NL80211_IFTYPE_AP);
- b43legacy_set_ssid(dev, conf->ssid, conf->ssid_len);
if (conf->changed & IEEE80211_IFCC_BEACON)
b43legacy_update_templates(wl);
} else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
@@ -3002,20 +3073,6 @@ static void b43legacy_imcfglo_timeouts_workaround(struct b43legacy_wldev *dev)
#endif /* CONFIG_SSB_DRIVER_PCICORE */
}
-/* Write the short and long frame retry limit values. */
-static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev,
- unsigned int short_retry,
- unsigned int long_retry)
-{
- /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing
- * the chip-internal counter. */
- short_retry = min(short_retry, (unsigned int)0xF);
- long_retry = min(long_retry, (unsigned int)0xF);
-
- b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry);
- b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry);
-}
-
static void b43legacy_set_synth_pu_delay(struct b43legacy_wldev *dev,
bool idle) {
u16 pu_delay = 1050;
@@ -3380,28 +3437,6 @@ static void b43legacy_op_stop(struct ieee80211_hw *hw)
mutex_unlock(&wl->mutex);
}
-static int b43legacy_op_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry_limit,
- u32 long_retry_limit)
-{
- struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
- struct b43legacy_wldev *dev;
- int err = 0;
-
- mutex_lock(&wl->mutex);
- dev = wl->current_dev;
- if (unlikely(!dev ||
- (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED))) {
- err = -ENODEV;
- goto out_unlock;
- }
- b43legacy_set_retry_limits(dev, short_retry_limit, long_retry_limit);
-out_unlock:
- mutex_unlock(&wl->mutex);
-
- return err;
-}
-
static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, bool set)
{
@@ -3421,13 +3456,13 @@ static const struct ieee80211_ops b43legacy_hw_ops = {
.add_interface = b43legacy_op_add_interface,
.remove_interface = b43legacy_op_remove_interface,
.config = b43legacy_op_dev_config,
+ .bss_info_changed = b43legacy_op_bss_info_changed,
.config_interface = b43legacy_op_config_interface,
.configure_filter = b43legacy_op_configure_filter,
.get_stats = b43legacy_op_get_stats,
.get_tx_stats = b43legacy_op_get_tx_stats,
.start = b43legacy_op_start,
.stop = b43legacy_op_stop,
- .set_retry_limit = b43legacy_op_set_retry_limit,
.set_tim = b43legacy_op_beacon_set_tim,
};
@@ -3710,7 +3745,7 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
BIT(NL80211_IFTYPE_WDS) |
BIT(NL80211_IFTYPE_ADHOC);
hw->queues = 1; /* FIXME: hardware has more queues */
- hw->max_altrates = 1;
+ hw->max_rates = 2;
SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->et1mac))
SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c
index 4c9442b16f3f..11319ec2d64a 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/b43legacy/phy.c
@@ -1296,12 +1296,10 @@ void b43legacy_lo_write(struct b43legacy_wldev *dev,
/* Sanity check. */
if (pair->low < -8 || pair->low > 8 ||
pair->high < -8 || pair->high > 8) {
- struct b43legacy_phy *phy = &dev->phy;
b43legacydbg(dev->wl,
"WARNING: Writing invalid LOpair "
- "(low: %d, high: %d, index: %lu)\n",
- pair->low, pair->high,
- (unsigned long)(pair - phy->_lo_pairs));
+ "(low: %d, high: %d)\n",
+ pair->low, pair->high);
dump_stack();
}
#endif
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c
index a86c7647fa2d..746d5361bba0 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/b43legacy/pio.c
@@ -491,6 +491,7 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
struct b43legacy_pioqueue *queue;
struct b43legacy_pio_txpacket *packet;
struct ieee80211_tx_info *info;
+ int retry_limit;
queue = parse_cookie(dev, status->cookie, &packet);
B43legacy_WARN_ON(!queue);
@@ -503,11 +504,37 @@ void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev,
sizeof(struct b43legacy_txhdr_fw3));
info = IEEE80211_SKB_CB(packet->skb);
- memset(&info->status, 0, sizeof(info->status));
+
+ /* preserve the confiured retry limit before clearing the status
+ * The xmit function has overwritten the rc's value with the actual
+ * retry limit done by the hardware */
+ retry_limit = info->status.rates[0].count;
+ ieee80211_tx_info_clear_status(info);
if (status->acked)
info->flags |= IEEE80211_TX_STAT_ACK;
- info->status.retry_count = status->frame_count - 1;
+
+ if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) {
+ /*
+ * If the short retries (RTS, not data frame) have exceeded
+ * the limit, the hw will not have tried the selected rate,
+ * but will have used the fallback rate instead.
+ * Don't let the rate control count attempts for the selected
+ * rate in this case, otherwise the statistics will be off.
+ */
+ info->status.rates[0].count = 0;
+ info->status.rates[1].count = status->frame_count;
+ } else {
+ if (status->frame_count > retry_limit) {
+ info->status.rates[0].count = retry_limit;
+ info->status.rates[1].count = status->frame_count -
+ retry_limit;
+
+ } else {
+ info->status.rates[0].count = status->frame_count;
+ info->status.rates[1].idx = -1;
+ }
+ }
ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb);
packet->skb = NULL;
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 65e833781608..12fca99f7578 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -188,7 +188,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
struct b43legacy_txhdr_fw3 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_info *info,
+ struct ieee80211_tx_info *info,
u16 cookie)
{
const struct ieee80211_hdr *wlhdr;
@@ -201,6 +201,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
u32 mac_ctl = 0;
u16 phy_ctl = 0;
struct ieee80211_rate *tx_rate;
+ struct ieee80211_tx_rate *rates;
wlhdr = (const struct ieee80211_hdr *)fragment_data;
@@ -274,7 +275,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
/* PHY TX Control word */
if (rate_ofdm)
phy_ctl |= B43legacy_TX4_PHY_OFDM;
- if (dev->short_preamble)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL;
switch (info->antenna_sel_tx) {
case 0:
@@ -291,6 +292,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
}
/* MAC control */
+ rates = info->control.rates;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
mac_ctl |= B43legacy_TX4_MAC_ACK;
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
@@ -299,12 +301,22 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
mac_ctl |= B43legacy_TX4_MAC_STMSDU;
if (rate_fb_ofdm)
mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM;
- if (info->flags & IEEE80211_TX_CTL_LONG_RETRY_LIMIT)
+
+ /* Overwrite rates[0].count to make the retry calculation
+ * in the tx status easier. need the actual retry limit to
+ * detect whether the fallback rate was used.
+ */
+ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) {
+ rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count;
mac_ctl |= B43legacy_TX4_MAC_LONGFRAME;
+ } else {
+ rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count;
+ }
/* Generate the RTS or CTS-to-self frame */
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
+ if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) {
unsigned int len;
struct ieee80211_hdr *hdr;
int rts_rate;
@@ -319,7 +331,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev,
if (rts_rate_fb_ofdm)
mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM;
- if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
+ if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
ieee80211_ctstoself_get(dev->wl->hw,
info->control.vif,
fragment_data,
@@ -362,7 +374,7 @@ int b43legacy_generate_txhdr(struct b43legacy_wldev *dev,
u8 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_info *info,
+ struct ieee80211_tx_info *info,
u16 cookie)
{
return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr,
diff --git a/drivers/net/wireless/b43legacy/xmit.h b/drivers/net/wireless/b43legacy/xmit.h
index e56777e0feab..62e09d02788f 100644
--- a/drivers/net/wireless/b43legacy/xmit.h
+++ b/drivers/net/wireless/b43legacy/xmit.h
@@ -80,7 +80,7 @@ int b43legacy_generate_txhdr(struct b43legacy_wldev *dev,
u8 *txhdr,
const unsigned char *fragment_data,
unsigned int fragment_len,
- const struct ieee80211_tx_info *info,
+ struct ieee80211_tx_info *info,
u16 cookie);
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
index 1fef33169fdd..932d207bce23 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -2,8 +2,17 @@ config HOSTAP
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
depends on WLAN_80211
select WIRELESS_EXT
- select IEEE80211
- select IEEE80211_CRYPT_WEP
+ select CRYPTO
+ select CRYPTO_ARC4
+ select CRYPTO_ECB
+ select CRYPTO_AES
+ select CRYPTO_MICHAEL_MIC
+ select CRYPTO_ECB
+ select CRC32
+ select LIB80211
+ select LIB80211_CRYPT_WEP
+ select LIB80211_CRYPT_TKIP
+ select LIB80211_CRYPT_CCMP
---help---
Shared driver code for IEEE 802.11b wireless cards based on
Intersil Prism2/2.5/3 chipset. This driver supports so called
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h
index 3a386a636cca..2453deaa3e00 100644
--- a/drivers/net/wireless/hostap/hostap.h
+++ b/drivers/net/wireless/hostap/hostap.h
@@ -63,7 +63,7 @@ void ap_control_flush_macs(struct mac_restrictions *mac_restrictions);
int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac);
void ap_control_kickall(struct ap_data *ap);
void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
- struct ieee80211_crypt_data ***crypt);
+ struct lib80211_crypt_data ***crypt);
int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
struct iw_quality qual[], int buf_size,
int aplist);
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
index 3694b1eba521..3a9474d9a907 100644
--- a/drivers/net/wireless/hostap/hostap_80211.h
+++ b/drivers/net/wireless/hostap/hostap_80211.h
@@ -2,7 +2,7 @@
#define HOSTAP_80211_H
#include <linux/types.h>
-#include <net/ieee80211_crypt.h>
+#include <net/ieee80211.h>
struct hostap_ieee80211_mgmt {
__le16 frame_control;
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c
index f106bc1585a4..19b1bf0478bd 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_rx.c
@@ -1,5 +1,5 @@
#include <linux/etherdevice.h>
-#include <net/ieee80211_crypt.h>
+#include <net/lib80211.h>
#include "hostap_80211.h"
#include "hostap.h"
@@ -19,7 +19,6 @@ void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
{
struct ieee80211_hdr_4addr *hdr;
u16 fc;
- DECLARE_MAC_BUF(mac);
hdr = (struct ieee80211_hdr_4addr *) skb->data;
@@ -45,11 +44,11 @@ void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
le16_to_cpu(hdr->seq_ctl));
- printk(KERN_DEBUG " A1=%s", print_mac(mac, hdr->addr1));
- printk(" A2=%s", print_mac(mac, hdr->addr2));
- printk(" A3=%s", print_mac(mac, hdr->addr3));
+ printk(KERN_DEBUG " A1=%pM", hdr->addr1);
+ printk(" A2=%pM", hdr->addr2);
+ printk(" A3=%pM", hdr->addr3);
if (skb->len >= 30)
- printk(" A4=%s", print_mac(mac, hdr->addr4));
+ printk(" A4=%pM", hdr->addr4);
printk("\n");
}
@@ -68,7 +67,6 @@ int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
iface = netdev_priv(dev);
local = iface->local;
- dev->last_rx = jiffies;
if (dev->type == ARPHRD_IEEE80211_PRISM) {
if (local->monitor_type == PRISM2_MONITOR_PRISM) {
@@ -557,7 +555,6 @@ static int
hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr,
u16 fc, struct net_device **wds)
{
- DECLARE_MAC_BUF(mac);
/* FIX: is this really supposed to accept WDS frames only in Master
* mode? What about Repeater or Managed with WDS frames? */
if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) !=
@@ -573,10 +570,10 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr,
hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) {
/* RA (or BSSID) is not ours - drop */
PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with "
- "not own or broadcast %s=%s\n",
+ "not own or broadcast %s=%pM\n",
local->dev->name,
fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID",
- print_mac(mac, hdr->addr1));
+ hdr->addr1);
return -1;
}
@@ -589,8 +586,8 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr_4addr *hdr,
/* require that WDS link has been registered with TA or the
* frame is from current AP when using 'AP client mode' */
PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame "
- "from unknown TA=%s\n",
- local->dev->name, print_mac(mac, hdr->addr2));
+ "from unknown TA=%pM\n",
+ local->dev->name, hdr->addr2);
if (local->ap && local->ap->autom_ap_wds)
hostap_wds_link_oper(local, hdr->addr2, WDS_ADD);
return -1;
@@ -652,7 +649,7 @@ static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb)
/* Called only as a tasklet (software IRQ) */
static int
hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
- struct ieee80211_crypt_data *crypt)
+ struct lib80211_crypt_data *crypt)
{
struct ieee80211_hdr_4addr *hdr;
int res, hdrlen;
@@ -667,10 +664,8 @@ hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
strcmp(crypt->ops->name, "TKIP") == 0) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
- "received packet from " MAC_FMT "\n",
- local->dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "received packet from %pM\n",
+ local->dev->name, hdr->addr2);
}
return -1;
}
@@ -679,12 +674,8 @@ hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
atomic_dec(&crypt->refcnt);
if (res < 0) {
- printk(KERN_DEBUG "%s: decryption failed (SA=" MAC_FMT
- ") res=%d\n",
- local->dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
- res);
+ printk(KERN_DEBUG "%s: decryption failed (SA=%pM) res=%d\n",
+ local->dev->name, hdr->addr2, res);
local->comm_tallies.rx_discards_wep_undecryptable++;
return -1;
}
@@ -696,11 +687,10 @@ hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb,
/* Called only as a tasklet (software IRQ) */
static int
hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb,
- int keyidx, struct ieee80211_crypt_data *crypt)
+ int keyidx, struct lib80211_crypt_data *crypt)
{
struct ieee80211_hdr_4addr *hdr;
int res, hdrlen;
- DECLARE_MAC_BUF(mac);
if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
return 0;
@@ -713,8 +703,8 @@ hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb,
atomic_dec(&crypt->refcnt);
if (res < 0) {
printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
- " (SA=%s keyidx=%d)\n",
- local->dev->name, print_mac(mac, hdr->addr2), keyidx);
+ " (SA=%pM keyidx=%d)\n",
+ local->dev->name, hdr->addr2, keyidx);
return -1;
}
@@ -743,7 +733,7 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
int from_assoc_ap = 0;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
- struct ieee80211_crypt_data *crypt = NULL;
+ struct lib80211_crypt_data *crypt = NULL;
void *sta = NULL;
int keyidx = 0;
@@ -795,7 +785,7 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
int idx = 0;
if (skb->len >= hdrlen + 3)
idx = skb->data[hdrlen + 3] >> 6;
- crypt = local->crypt[idx];
+ crypt = local->crypt_info.crypt[idx];
sta = NULL;
/* Use station specific key to override default keys if the
@@ -822,10 +812,8 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
* frames silently instead of filling system log with
* these reports. */
printk(KERN_DEBUG "%s: WEP decryption failed (not set)"
- " (SA=" MAC_FMT ")\n",
- local->dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ " (SA=%pM)\n",
+ local->dev->name, hdr->addr2);
#endif
local->comm_tallies.rx_discards_wep_undecryptable++;
goto rx_dropped;
@@ -839,9 +827,7 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
(keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0)
{
printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
- "from " MAC_FMT "\n", dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "from %pM\n", dev->name, hdr->addr2);
/* TODO: could inform hostapd about this so that it
* could send auth failure report */
goto rx_dropped;
@@ -896,8 +882,6 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
from_assoc_ap = 1;
}
- dev->last_rx = jiffies;
-
if ((local->iw_mode == IW_MODE_MASTER ||
local->iw_mode == IW_MODE_REPEAT) &&
!from_assoc_ap) {
@@ -1009,10 +993,8 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
"unencrypted EAPOL frame\n", local->dev->name);
} else {
printk(KERN_DEBUG "%s: encryption configured, but RX "
- "frame not encrypted (SA=" MAC_FMT ")\n",
- local->dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "frame not encrypted (SA=%pM)\n",
+ local->dev->name, hdr->addr2);
goto rx_dropped;
}
}
@@ -1021,10 +1003,8 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
!hostap_is_eapol_frame(local, skb)) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: dropped unencrypted RX data "
- "frame from " MAC_FMT " (drop_unencrypted=1)\n",
- dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "frame from %pM (drop_unencrypted=1)\n",
+ dev->name, hdr->addr2);
}
goto rx_dropped;
}
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
index 921c984416f8..078a010f39a0 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -17,7 +17,6 @@ void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
{
struct ieee80211_hdr_4addr *hdr;
u16 fc;
- DECLARE_MAC_BUF(mac);
hdr = (struct ieee80211_hdr_4addr *) skb->data;
@@ -41,11 +40,11 @@ void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
le16_to_cpu(hdr->seq_ctl));
- printk(KERN_DEBUG " A1=%s", print_mac(mac, hdr->addr1));
- printk(" A2=%s", print_mac(mac, hdr->addr2));
- printk(" A3=%s", print_mac(mac, hdr->addr3));
+ printk(KERN_DEBUG " A1=%pM", hdr->addr1);
+ printk(" A2=%pM", hdr->addr2);
+ printk(" A3=%pM", hdr->addr3);
if (skb->len >= 30)
- printk(" A4=%s", print_mac(mac, hdr->addr4));
+ printk(" A4=%pM", hdr->addr4);
printk("\n");
}
@@ -307,7 +306,7 @@ int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Called only from software IRQ */
static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
- struct ieee80211_crypt_data *crypt)
+ struct lib80211_crypt_data *crypt)
{
struct hostap_interface *iface;
local_info_t *local;
@@ -328,10 +327,8 @@ static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
hdr = (struct ieee80211_hdr_4addr *) skb->data;
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
- "TX packet to " MAC_FMT "\n",
- local->dev->name,
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ "TX packet to %pM\n",
+ local->dev->name, hdr->addr1);
}
kfree_skb(skb);
return NULL;
@@ -408,7 +405,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (local->host_encrypt) {
/* Set crypt to default algorithm and key; will be replaced in
* AP code if STA has own alg/key */
- tx.crypt = local->crypt[local->tx_keyidx];
+ tx.crypt = local->crypt_info.crypt[local->crypt_info.tx_keyidx];
tx.host_encrypt = 1;
} else {
tx.crypt = NULL;
@@ -490,7 +487,9 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu))
tx.crypt = NULL;
- else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) {
+ else if ((tx.crypt ||
+ local->crypt_info.crypt[local->crypt_info.tx_keyidx]) &&
+ !no_encrypt) {
/* Add ISWEP flag both for firmware and host based encryption
*/
fc |= IEEE80211_FCTL_PROTECTED;
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c
index af3d4ef2a80b..0903db786d5f 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/hostap/hostap_ap.c
@@ -94,7 +94,6 @@ static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta)
static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
{
struct sta_info *s;
- DECLARE_MAC_BUF(mac);
s = ap->sta_hash[STA_HASH(sta->addr)];
if (s == NULL) return;
@@ -109,20 +108,18 @@ static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta)
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
else
- printk("AP: could not remove STA %s"
- " from hash table\n",
- print_mac(mac, sta->addr));
+ printk("AP: could not remove STA %pM from hash table\n",
+ sta->addr);
}
static void ap_free_sta(struct ap_data *ap, struct sta_info *sta)
{
- DECLARE_MAC_BUF(mac);
if (sta->ap && sta->local)
hostap_event_expired_sta(sta->local->dev, sta);
if (ap->proc != NULL) {
char name[20];
- sprintf(name, "%s", print_mac(mac, sta->addr));
+ sprintf(name, "%pM", sta->addr);
remove_proc_entry(name, ap->proc);
}
@@ -185,7 +182,6 @@ static void ap_handle_timer(unsigned long data)
struct ap_data *ap;
unsigned long next_time = 0;
int was_assoc;
- DECLARE_MAC_BUF(mac);
if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) {
PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n");
@@ -242,8 +238,8 @@ static void ap_handle_timer(unsigned long data)
if (sta->ap) {
if (ap->autom_ap_wds) {
PDEBUG(DEBUG_AP, "%s: removing automatic WDS "
- "connection to AP %s\n",
- local->dev->name, print_mac(mac, sta->addr));
+ "connection to AP %pM\n",
+ local->dev->name, sta->addr);
hostap_wds_link_oper(local, sta->addr, WDS_DEL);
}
} else if (sta->timeout_next == STA_NULLFUNC) {
@@ -259,11 +255,11 @@ static void ap_handle_timer(unsigned long data)
} else {
int deauth = sta->timeout_next == STA_DEAUTH;
__le16 resp;
- PDEBUG(DEBUG_AP, "%s: sending %s info to STA %s"
+ PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM"
"(last=%lu, jiffies=%lu)\n",
local->dev->name,
deauth ? "deauthentication" : "disassociation",
- print_mac(mac, sta->addr), sta->last_rx, jiffies);
+ sta->addr, sta->last_rx, jiffies);
resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID :
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
@@ -275,10 +271,10 @@ static void ap_handle_timer(unsigned long data)
if (sta->timeout_next == STA_DEAUTH) {
if (sta->flags & WLAN_STA_PERM) {
- PDEBUG(DEBUG_AP, "%s: STA %s"
+ PDEBUG(DEBUG_AP, "%s: STA %pM"
" would have been removed, "
"but it has 'perm' flag\n",
- local->dev->name, print_mac(mac, sta->addr));
+ local->dev->name, sta->addr);
} else
ap_free_sta(ap, sta);
return;
@@ -332,7 +328,6 @@ static int ap_control_proc_read(char *page, char **start, off_t off,
struct ap_data *ap = (struct ap_data *) data;
char *policy_txt;
struct mac_entry *entry;
- DECLARE_MAC_BUF(mac);
if (off != 0) {
*eof = 1;
@@ -363,7 +358,7 @@ static int ap_control_proc_read(char *page, char **start, off_t off,
break;
}
- p += sprintf(p, "%s\n", print_mac(mac, entry->addr));
+ p += sprintf(p, "%pM\n", entry->addr);
}
spin_unlock_bh(&ap->mac_restrictions.lock);
@@ -520,7 +515,6 @@ static int prism2_ap_proc_read(char *page, char **start, off_t off,
struct ap_data *ap = (struct ap_data *) data;
struct sta_info *sta;
int i;
- DECLARE_MAC_BUF(mac);
if (off > PROC_LIMIT) {
*eof = 1;
@@ -533,8 +527,8 @@ static int prism2_ap_proc_read(char *page, char **start, off_t off,
if (!sta->ap)
continue;
- p += sprintf(p, "%s %d %d %d %d '",
- print_mac(mac, sta->addr),
+ p += sprintf(p, "%pM %d %d %d %d '",
+ sta->addr,
sta->u.ap.channel, sta->last_rx_signal,
sta->last_rx_silence, sta->last_rx_rate);
for (i = 0; i < sta->u.ap.ssid_len; i++)
@@ -683,11 +677,9 @@ static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data)
if (sta)
atomic_dec(&sta->users);
if (txt) {
- PDEBUG(DEBUG_AP, "%s: " MAC_FMT " auth_cb - alg=%d "
+ PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d "
"trans#=%d status=%d - %s\n",
- dev->name,
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
+ dev->name, hdr->addr1,
auth_alg, auth_transaction, status, txt);
}
dev_kfree_skb(skb);
@@ -754,11 +746,8 @@ static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data)
if (sta)
atomic_dec(&sta->users);
if (txt) {
- PDEBUG(DEBUG_AP, "%s: " MAC_FMT " assoc_cb - %s\n",
- dev->name,
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
- txt);
+ PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n",
+ dev->name, hdr->addr1, txt);
}
dev_kfree_skb(skb);
}
@@ -781,11 +770,9 @@ static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data)
sta->flags &= ~WLAN_STA_PENDING_POLL;
spin_unlock(&ap->sta_table_lock);
} else {
- PDEBUG(DEBUG_AP, "%s: STA " MAC_FMT
- " did not ACK activity poll frame\n",
- ap->local->dev->name,
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ PDEBUG(DEBUG_AP,
+ "%s: STA %pM did not ACK activity poll frame\n",
+ ap->local->dev->name, hdr->addr1);
}
fail:
@@ -1002,7 +989,6 @@ static int prism2_sta_proc_read(char *page, char **start, off_t off,
char *p = page;
struct sta_info *sta = (struct sta_info *) data;
int i;
- DECLARE_MAC_BUF(mac);
/* FIX: possible race condition.. the STA data could have just expired,
* but proc entry was still here so that the read could have started;
@@ -1013,11 +999,11 @@ static int prism2_sta_proc_read(char *page, char **start, off_t off,
return 0;
}
- p += sprintf(p, "%s=%s\nusers=%d\naid=%d\n"
+ p += sprintf(p, "%s=%pM\nusers=%d\naid=%d\n"
"flags=0x%04x%s%s%s%s%s%s%s\n"
"capability=0x%02x\nlisten_interval=%d\nsupported_rates=",
sta->ap ? "AP" : "STA",
- print_mac(mac, sta->addr), atomic_read(&sta->users), sta->aid,
+ sta->addr, atomic_read(&sta->users), sta->aid,
sta->flags,
sta->flags & WLAN_STA_AUTH ? " AUTH" : "",
sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "",
@@ -1078,7 +1064,6 @@ static void handle_add_proc_queue(struct work_struct *work)
struct sta_info *sta;
char name[20];
struct add_sta_proc_data *entry, *prev;
- DECLARE_MAC_BUF(mac);
entry = ap->add_sta_proc_entries;
ap->add_sta_proc_entries = NULL;
@@ -1091,7 +1076,7 @@ static void handle_add_proc_queue(struct work_struct *work)
spin_unlock_bh(&ap->sta_table_lock);
if (sta) {
- sprintf(name, "%s", print_mac(mac, sta->addr));
+ sprintf(name, "%pM", sta->addr);
sta->proc = create_proc_read_entry(
name, 0, ap->proc,
prism2_sta_proc_read, sta);
@@ -1221,7 +1206,7 @@ static void prism2_check_tx_rates(struct sta_info *sta)
static void ap_crypt_init(struct ap_data *ap)
{
- ap->crypt = ieee80211_get_crypto_ops("WEP");
+ ap->crypt = lib80211_get_crypto_ops("WEP");
if (ap->crypt) {
if (ap->crypt->init) {
@@ -1239,7 +1224,7 @@ static void ap_crypt_init(struct ap_data *ap)
if (ap->crypt == NULL) {
printk(KERN_WARNING "AP could not initialize WEP: load module "
- "ieee80211_crypt_wep.ko\n");
+ "lib80211_crypt_wep.ko\n");
}
}
@@ -1308,7 +1293,7 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb,
__le16 *pos;
u16 resp = WLAN_STATUS_SUCCESS, fc;
struct sta_info *sta = NULL;
- struct ieee80211_crypt_data *crypt;
+ struct lib80211_crypt_data *crypt;
char *txt = "";
len = skb->len - IEEE80211_MGMT_HDR_LEN;
@@ -1318,9 +1303,7 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb,
if (len < 6) {
PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload "
- "(len=%d) from " MAC_FMT "\n", dev->name, len,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "(len=%d) from %pM\n", dev->name, len, hdr->addr2);
return;
}
@@ -1336,7 +1319,7 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb,
int idx = 0;
if (skb->len >= hdrlen + 3)
idx = skb->data[hdrlen + 3] >> 6;
- crypt = local->crypt[idx];
+ crypt = local->crypt_info.crypt[idx];
}
pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN);
@@ -1385,10 +1368,8 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb,
if (time_after(jiffies, sta->u.ap.last_beacon +
(10 * sta->listen_interval * HZ) / 1024)) {
PDEBUG(DEBUG_AP, "%s: no beacons received for a while,"
- " assuming AP " MAC_FMT " is now STA\n",
- dev->name,
- sta->addr[0], sta->addr[1], sta->addr[2],
- sta->addr[3], sta->addr[4], sta->addr[5]);
+ " assuming AP %pM is now STA\n",
+ dev->name, sta->addr);
sta->ap = 0;
sta->flags = 0;
sta->u.sta.challenge = NULL;
@@ -1503,11 +1484,9 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb,
}
if (resp) {
- PDEBUG(DEBUG_AP, "%s: " MAC_FMT " auth (alg=%d "
+ PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d "
"trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n",
- dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
+ dev->name, hdr->addr2,
auth_alg, auth_transaction, status_code, len,
fc, resp, txt);
}
@@ -1533,10 +1512,8 @@ static void handle_assoc(local_info_t *local, struct sk_buff *skb,
if (len < (reassoc ? 10 : 4)) {
PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload "
- "(len=%d, reassoc=%d) from " MAC_FMT "\n",
- dev->name, len, reassoc,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
+ "(len=%d, reassoc=%d) from %pM\n",
+ dev->name, len, reassoc, hdr->addr2);
return;
}
@@ -1613,12 +1590,9 @@ static void handle_assoc(local_info_t *local, struct sk_buff *skb,
}
if (left > 0) {
- PDEBUG(DEBUG_AP, "%s: assoc from " MAC_FMT
+ PDEBUG(DEBUG_AP, "%s: assoc from %pM"
" with extra data (%d bytes) [",
- dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
- left);
+ dev->name, hdr->addr2, left);
while (left > 0) {
PDEBUG2(DEBUG_AP, "<%02x>", *u);
u++; left--;
@@ -1717,14 +1691,12 @@ static void handle_assoc(local_info_t *local, struct sk_buff *skb,
}
#if 0
- PDEBUG(DEBUG_AP, "%s: " MAC_FMT" %sassoc (len=%d "
- "prev_ap=" MAC_FMT") => %d(%d) (%s)\n",
+ PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d "
+ "prev_ap=%pM) => %d(%d) (%s)\n",
dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
+ hdr->addr2,
reassoc ? "re" : "", len,
- prev_ap[0], prev_ap[1], prev_ap[2],
- prev_ap[3], prev_ap[4], prev_ap[5],
+ prev_ap,
resp, send_deauth, txt);
#endif
}
@@ -1741,7 +1713,6 @@ static void handle_deauth(local_info_t *local, struct sk_buff *skb,
u16 reason_code;
__le16 *pos;
struct sta_info *sta = NULL;
- DECLARE_MAC_BUF(mac);
len = skb->len - IEEE80211_MGMT_HDR_LEN;
@@ -1753,10 +1724,8 @@ static void handle_deauth(local_info_t *local, struct sk_buff *skb,
pos = (__le16 *) body;
reason_code = le16_to_cpu(*pos);
- PDEBUG(DEBUG_AP, "%s: deauthentication: " MAC_FMT " len=%d, "
- "reason_code=%d\n", dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
+ PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, "
+ "reason_code=%d\n", dev->name, hdr->addr2,
len, reason_code);
spin_lock_bh(&local->ap->sta_table_lock);
@@ -1768,11 +1737,9 @@ static void handle_deauth(local_info_t *local, struct sk_buff *skb,
}
spin_unlock_bh(&local->ap->sta_table_lock);
if (sta == NULL) {
- printk("%s: deauthentication from " MAC_FMT ", "
+ printk("%s: deauthentication from %pM, "
"reason_code=%d, but STA not authenticated\n", dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
- reason_code);
+ hdr->addr2, reason_code);
}
}
@@ -1799,10 +1766,8 @@ static void handle_disassoc(local_info_t *local, struct sk_buff *skb,
pos = (__le16 *) body;
reason_code = le16_to_cpu(*pos);
- PDEBUG(DEBUG_AP, "%s: disassociation: " MAC_FMT " len=%d, "
- "reason_code=%d\n", dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
+ PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, "
+ "reason_code=%d\n", dev->name, hdr->addr2,
len, reason_code);
spin_lock_bh(&local->ap->sta_table_lock);
@@ -1814,12 +1779,9 @@ static void handle_disassoc(local_info_t *local, struct sk_buff *skb,
}
spin_unlock_bh(&local->ap->sta_table_lock);
if (sta == NULL) {
- printk("%s: disassociation from " MAC_FMT ", "
+ printk("%s: disassociation from %pM, "
"reason_code=%d, but STA not authenticated\n",
- dev->name,
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
- reason_code);
+ dev->name, hdr->addr2, reason_code);
}
}
@@ -1909,19 +1871,14 @@ static void handle_pspoll(local_info_t *local,
u16 aid;
struct sk_buff *skb;
- PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MAC_FMT
- ", TA=" MAC_FMT " PWRMGT=%d\n",
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
- hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
- hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
+ PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n",
+ hdr->addr1, hdr->addr2,
!!(le16_to_cpu(hdr->frame_ctl) & IEEE80211_FCTL_PM));
if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
- PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MAC_FMT
- " not own MAC\n",
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ PDEBUG(DEBUG_AP,
+ "handle_pspoll - addr1(BSSID)=%pM not own MAC\n",
+ hdr->addr1);
return;
}
@@ -2007,11 +1964,10 @@ static void handle_wds_oper_queue(struct work_struct *work)
while (entry) {
PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection "
- "to AP " MAC_FMT "\n",
+ "to AP %pM\n",
local->dev->name,
entry->type == WDS_ADD ? "adding" : "removing",
- entry->addr[0], entry->addr[1], entry->addr[2],
- entry->addr[3], entry->addr[4], entry->addr[5]);
+ entry->addr);
if (entry->type == WDS_ADD)
prism2_wds_add(local, entry->addr, 0);
else if (entry->type == WDS_DEL)
@@ -2215,10 +2171,8 @@ static void handle_ap_item(local_info_t *local, struct sk_buff *skb,
}
if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
- PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)="
- MAC_FMT " not own MAC\n",
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM"
+ " not own MAC\n", hdr->addr1);
goto done;
}
@@ -2254,18 +2208,14 @@ static void handle_ap_item(local_info_t *local, struct sk_buff *skb,
}
if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) {
- PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MAC_FMT
- " not own MAC\n",
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM"
+ " not own MAC\n", hdr->addr1);
goto done;
}
if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) {
- PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MAC_FMT
- " not own MAC\n",
- hdr->addr3[0], hdr->addr3[1], hdr->addr3[2],
- hdr->addr3[3], hdr->addr3[4], hdr->addr3[5]);
+ PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM"
+ " not own MAC\n", hdr->addr3);
goto done;
}
@@ -2366,10 +2316,9 @@ static void schedule_packet_send(local_info_t *local, struct sta_info *sta)
memcpy(hdr->addr2, sta->addr, ETH_ALEN);
hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14));
- PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for STA "
- MAC_FMT "\n", local->dev->name,
- sta->addr[0], sta->addr[1], sta->addr[2],
- sta->addr[3], sta->addr[4], sta->addr[5]);
+ PDEBUG(DEBUG_PS2,
+ "%s: Scheduling buffered packet delivery for STA %pM\n",
+ local->dev->name, sta->addr);
skb->dev = local->dev;
@@ -2723,12 +2672,8 @@ static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev)
case 3: sta->tx_rate = 110; break;
default: sta->tx_rate = 0; break;
}
- PDEBUG(DEBUG_AP, "%s: STA " MAC_FMT
- " TX rate raised to %d\n",
- dev->name,
- sta->addr[0], sta->addr[1], sta->addr[2],
- sta->addr[3], sta->addr[4], sta->addr[5],
- sta->tx_rate);
+ PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n",
+ dev->name, sta->addr, sta->tx_rate);
}
sta->tx_since_last_failure = 0;
}
@@ -2781,9 +2726,7 @@ ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
* print out any errors here. */
if (net_ratelimit()) {
printk(KERN_DEBUG "AP: drop packet to non-associated "
- "STA " MAC_FMT "\n",
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
+ "STA %pM\n", hdr->addr1);
}
#endif
local->ap->tx_drop_nonassoc++;
@@ -2821,11 +2764,9 @@ ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx)
}
if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) {
- PDEBUG(DEBUG_PS, "%s: No more space in STA (" MAC_FMT
- ")'s PS mode buffer\n",
- local->dev->name,
- sta->addr[0], sta->addr[1], sta->addr[2],
- sta->addr[3], sta->addr[4], sta->addr[5]);
+ PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s"
+ "PS mode buffer\n",
+ local->dev->name, sta->addr);
/* Make sure that TIM is set for the station (it might not be
* after AP wlan hw reset). */
/* FIX: should fix hw reset to restore bits based on STA
@@ -2897,12 +2838,9 @@ void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
sta = ap_get_sta(local->ap, hdr->addr1);
if (!sta) {
spin_unlock(&local->ap->sta_table_lock);
- PDEBUG(DEBUG_AP, "%s: Could not find STA " MAC_FMT
+ PDEBUG(DEBUG_AP, "%s: Could not find STA %pM"
" for this TX error (@%lu)\n",
- local->dev->name,
- hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
- hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
- jiffies);
+ local->dev->name, hdr->addr1, jiffies);
return;
}
@@ -2929,12 +2867,9 @@ void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
case 3: sta->tx_rate = 110; break;
default: sta->tx_rate = 0; break;
}
- PDEBUG(DEBUG_AP, "%s: STA " MAC_FMT
- " TX rate lowered to %d\n",
- local->dev->name,
- sta->addr[0], sta->addr[1], sta->addr[2],
- sta->addr[3], sta->addr[4], sta->addr[5],
- sta->tx_rate);
+ PDEBUG(DEBUG_AP,
+ "%s: STA %pM TX rate lowered to %d\n",
+ local->dev->name, sta->addr, sta->tx_rate);
}
sta->tx_consecutive_exc = 0;
}
@@ -2945,17 +2880,16 @@ void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb)
static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta,
int pwrmgt, int type, int stype)
{
- DECLARE_MAC_BUF(mac);
if (pwrmgt && !(sta->flags & WLAN_STA_PS)) {
sta->flags |= WLAN_STA_PS;
- PDEBUG(DEBUG_PS2, "STA %s changed to use PS "
+ PDEBUG(DEBUG_PS2, "STA %pM changed to use PS "
"mode (type=0x%02X, stype=0x%02X)\n",
- print_mac(mac, sta->addr), type >> 2, stype >> 4);
+ sta->addr, type >> 2, stype >> 4);
} else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) {
sta->flags &= ~WLAN_STA_PS;
- PDEBUG(DEBUG_PS2, "STA %s changed to not use "
+ PDEBUG(DEBUG_PS2, "STA %pM changed to not use "
"PS mode (type=0x%02X, stype=0x%02X)\n",
- print_mac(mac, sta->addr), type >> 2, stype >> 4);
+ sta->addr, type >> 2, stype >> 4);
if (type != IEEE80211_FTYPE_CTL ||
stype != IEEE80211_STYPE_PSPOLL)
schedule_packet_send(local, sta);
@@ -3029,13 +2963,9 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
} else {
printk(KERN_DEBUG "%s: dropped received packet"
- " from non-associated STA "
- MAC_FMT
+ " from non-associated STA %pM"
" (type=0x%02x, subtype=0x%02x)\n",
- dev->name,
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5],
+ dev->name, hdr->addr2,
type >> 2, stype >> 4);
hostap_rx(dev, skb, rx_stats);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
@@ -3068,13 +2998,9 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
* after being unavailable for some time. Speed up
* re-association by informing the station about it not
* being associated. */
- printk(KERN_DEBUG "%s: rejected received nullfunc "
- "frame without ToDS from not associated STA "
- MAC_FMT "\n",
- dev->name,
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5]);
+ printk(KERN_DEBUG "%s: rejected received nullfunc frame"
+ " without ToDS from not associated STA %pM\n",
+ dev->name, hdr->addr2);
hostap_rx(dev, skb, rx_stats);
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
}
@@ -3090,13 +3016,10 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
* broadcast frame from an IBSS network. Drop it silently.
* If BSSID is own, report the dropping of this frame. */
if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
- printk(KERN_DEBUG "%s: dropped received packet from "
- MAC_FMT " with no ToDS flag "
+ printk(KERN_DEBUG "%s: dropped received packet from %pM"
+ " with no ToDS flag "
"(type=0x%02x, subtype=0x%02x)\n", dev->name,
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5],
- type >> 2, stype >> 4);
+ hdr->addr2, type >> 2, stype >> 4);
hostap_dump_rx_80211(dev->name, skb, rx_stats);
}
ret = AP_RX_DROP;
@@ -3142,7 +3065,7 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
/* Called only as a tasklet (software IRQ) */
int hostap_handle_sta_crypto(local_info_t *local,
struct ieee80211_hdr_4addr *hdr,
- struct ieee80211_crypt_data **crypt,
+ struct lib80211_crypt_data **crypt,
void **sta_ptr)
{
struct sta_info *sta;
@@ -3290,7 +3213,7 @@ void hostap_update_rates(local_info_t *local)
void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
- struct ieee80211_crypt_data ***crypt)
+ struct lib80211_crypt_data ***crypt)
{
struct sta_info *sta;
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h
index 2fa2452b6b07..d36e4b175336 100644
--- a/drivers/net/wireless/hostap/hostap_ap.h
+++ b/drivers/net/wireless/hostap/hostap_ap.h
@@ -74,7 +74,7 @@ struct sta_info {
u32 tx_since_last_failure;
u32 tx_consecutive_exc;
- struct ieee80211_crypt_data *crypt;
+ struct lib80211_crypt_data *crypt;
int ap; /* whether this station is an AP */
@@ -209,7 +209,7 @@ struct ap_data {
/* WEP operations for generating challenges to be used with shared key
* authentication */
- struct ieee80211_crypto_ops *crypt;
+ struct lib80211_crypto_ops *crypt;
void *crypt_priv;
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
};
@@ -229,7 +229,7 @@ typedef enum {
struct hostap_tx_data {
struct sk_buff *skb;
int host_encrypt;
- struct ieee80211_crypt_data *crypt;
+ struct lib80211_crypt_data *crypt;
void *sta_ptr;
};
ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx);
@@ -244,7 +244,7 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
struct hostap_80211_rx_status *rx_stats,
int wds);
int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr_4addr *hdr,
- struct ieee80211_crypt_data **crypt,
+ struct lib80211_crypt_data **crypt,
void **sta_ptr);
int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr);
int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr);
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h
index b470c743c2d1..90b64b092007 100644
--- a/drivers/net/wireless/hostap/hostap_common.h
+++ b/drivers/net/wireless/hostap/hostap_common.h
@@ -6,19 +6,6 @@
/* IEEE 802.11 defines */
-/* Information Element IDs */
-#define WLAN_EID_SSID 0
-#define WLAN_EID_SUPP_RATES 1
-#define WLAN_EID_FH_PARAMS 2
-#define WLAN_EID_DS_PARAMS 3
-#define WLAN_EID_CF_PARAMS 4
-#define WLAN_EID_TIM 5
-#define WLAN_EID_IBSS_PARAMS 6
-#define WLAN_EID_CHALLENGE 16
-#define WLAN_EID_RSN 48
-#define WLAN_EID_GENERIC 221
-
-
/* HFA384X Configuration RIDs */
#define HFA384X_RID_CNFPORTTYPE 0xFC00
#define HFA384X_RID_CNFOWNMACADDR 0xFC01
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index 3153fe9d7ce0..0f27059bbe85 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -47,7 +47,7 @@
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>
-#include <net/ieee80211_crypt.h>
+#include <net/lib80211.h>
#include <asm/irq.h>
#include "hostap_80211.h"
@@ -2335,10 +2335,6 @@ static void prism2_txexc(local_info_t *local)
int show_dump, res;
char *payload = NULL;
struct hfa384x_tx_frame txdesc;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR;
local->stats.tx_errors++;
@@ -2404,9 +2400,9 @@ static void prism2_txexc(local_info_t *local)
WLAN_FC_GET_STYPE(fc) >> 4,
fc & IEEE80211_FCTL_TODS ? " ToDS" : "",
fc & IEEE80211_FCTL_FROMDS ? " FromDS" : "");
- PDEBUG(DEBUG_EXTRA, " A1=%s A2=%s A3=%s A4=%s\n",
- print_mac(mac, txdesc.addr1), print_mac(mac2, txdesc.addr2),
- print_mac(mac3, txdesc.addr3), print_mac(mac4, txdesc.addr4));
+ PDEBUG(DEBUG_EXTRA, " A1=%pM A2=%pM A3=%pM A4=%pM\n",
+ txdesc.addr1, txdesc.addr2,
+ txdesc.addr3, txdesc.addr4);
}
@@ -2792,45 +2788,6 @@ static void prism2_check_sta_fw_version(local_info_t *local)
}
-static void prism2_crypt_deinit_entries(local_info_t *local, int force)
-{
- struct list_head *ptr, *n;
- struct ieee80211_crypt_data *entry;
-
- for (ptr = local->crypt_deinit_list.next, n = ptr->next;
- ptr != &local->crypt_deinit_list; ptr = n, n = ptr->next) {
- entry = list_entry(ptr, struct ieee80211_crypt_data, list);
-
- if (atomic_read(&entry->refcnt) != 0 && !force)
- continue;
-
- list_del(ptr);
-
- if (entry->ops)
- entry->ops->deinit(entry->priv);
- kfree(entry);
- }
-}
-
-
-static void prism2_crypt_deinit_handler(unsigned long data)
-{
- local_info_t *local = (local_info_t *) data;
- unsigned long flags;
-
- spin_lock_irqsave(&local->lock, flags);
- prism2_crypt_deinit_entries(local, 0);
- if (!list_empty(&local->crypt_deinit_list)) {
- printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
- "deletion list\n", local->dev->name);
- local->crypt_deinit_timer.expires = jiffies + HZ;
- add_timer(&local->crypt_deinit_timer);
- }
- spin_unlock_irqrestore(&local->lock, flags);
-
-}
-
-
static void hostap_passive_scan(unsigned long data)
{
local_info_t *local = (local_info_t *) data;
@@ -3254,10 +3211,8 @@ while (0)
INIT_LIST_HEAD(&local->cmd_queue);
init_waitqueue_head(&local->hostscan_wq);
- INIT_LIST_HEAD(&local->crypt_deinit_list);
- init_timer(&local->crypt_deinit_timer);
- local->crypt_deinit_timer.data = (unsigned long) local;
- local->crypt_deinit_timer.function = prism2_crypt_deinit_handler;
+
+ lib80211_crypt_info_init(&local->crypt_info, dev->name, &local->lock);
init_timer(&local->passive_scan_timer);
local->passive_scan_timer.data = (unsigned long) local;
@@ -3358,9 +3313,7 @@ static void prism2_free_local_data(struct net_device *dev)
flush_scheduled_work();
- if (timer_pending(&local->crypt_deinit_timer))
- del_timer(&local->crypt_deinit_timer);
- prism2_crypt_deinit_entries(local, 1);
+ lib80211_crypt_info_free(&local->crypt_info);
if (timer_pending(&local->passive_scan_timer))
del_timer(&local->passive_scan_timer);
@@ -3377,16 +3330,6 @@ static void prism2_free_local_data(struct net_device *dev)
if (local->dev_enabled)
prism2_callback(local, PRISM2_CALLBACK_DISABLE);
- for (i = 0; i < WEP_KEYS; i++) {
- struct ieee80211_crypt_data *crypt = local->crypt[i];
- if (crypt) {
- if (crypt->ops)
- crypt->ops->deinit(crypt->priv);
- kfree(crypt);
- local->crypt[i] = NULL;
- }
- }
-
if (local->ap != NULL)
hostap_free_data(local->ap);
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c
index 7cd3fb79230e..99b4cf41edf2 100644
--- a/drivers/net/wireless/hostap/hostap_info.c
+++ b/drivers/net/wireless/hostap/hostap_info.c
@@ -166,7 +166,6 @@ static void prism2_host_roaming(local_info_t *local)
struct hfa384x_hostscan_result *selected, *entry;
int i;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
if (local->last_join_time &&
time_before(jiffies, local->last_join_time + 10 * HZ)) {
@@ -199,9 +198,8 @@ static void prism2_host_roaming(local_info_t *local)
local->preferred_ap[2] || local->preferred_ap[3] ||
local->preferred_ap[4] || local->preferred_ap[5]) {
/* Try to find preferred AP */
- PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID "
- "%s\n",
- dev->name, print_mac(mac, local->preferred_ap));
+ PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID %pM\n",
+ dev->name, local->preferred_ap);
for (i = 0; i < local->last_scan_results_count; i++) {
entry = &local->last_scan_results[i];
if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
@@ -218,9 +216,9 @@ static void prism2_host_roaming(local_info_t *local)
req.channel = selected->chid;
spin_unlock_irqrestore(&local->lock, flags);
- PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%s"
+ PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%pM"
" channel=%d\n",
- dev->name, print_mac(mac, req.bssid), le16_to_cpu(req.channel));
+ dev->name, req.bssid, le16_to_cpu(req.channel));
if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
sizeof(req))) {
printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
@@ -413,7 +411,6 @@ static void handle_info_queue_linkstatus(local_info_t *local)
int val = local->prev_link_status;
int connected;
union iwreq_data wrqu;
- DECLARE_MAC_BUF(mac);
connected =
val == HFA384X_LINKSTATUS_CONNECTED ||
@@ -425,10 +422,9 @@ static void handle_info_queue_linkstatus(local_info_t *local)
printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
"LinkStatus event\n", local->dev->name);
} else {
- PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID="
- "%s\n",
+ PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=%pM\n",
local->dev->name,
- print_mac(mac, (unsigned char *) local->bssid));
+ (unsigned char *) local->bssid);
if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
hostap_add_sta(local->ap, local->bssid);
}
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index 3f8b1d7036e5..c40fdf4c79de 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -2,7 +2,7 @@
#include <linux/types.h>
#include <linux/ethtool.h>
-#include <net/ieee80211_crypt.h>
+#include <net/lib80211.h>
#include "hostap_wlan.h"
#include "hostap.h"
@@ -116,32 +116,6 @@ static int prism2_get_name(struct net_device *dev,
}
-static void prism2_crypt_delayed_deinit(local_info_t *local,
- struct ieee80211_crypt_data **crypt)
-{
- struct ieee80211_crypt_data *tmp;
- unsigned long flags;
-
- tmp = *crypt;
- *crypt = NULL;
-
- if (tmp == NULL)
- return;
-
- /* must not run ops->deinit() while there may be pending encrypt or
- * decrypt operations. Use a list of delayed deinits to avoid needing
- * locking. */
-
- spin_lock_irqsave(&local->lock, flags);
- list_add(&tmp->list, &local->crypt_deinit_list);
- if (!timer_pending(&local->crypt_deinit_timer)) {
- local->crypt_deinit_timer.expires = jiffies + HZ;
- add_timer(&local->crypt_deinit_timer);
- }
- spin_unlock_irqrestore(&local->lock, flags);
-}
-
-
static int prism2_ioctl_siwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq, char *keybuf)
@@ -149,47 +123,47 @@ static int prism2_ioctl_siwencode(struct net_device *dev,
struct hostap_interface *iface;
local_info_t *local;
int i;
- struct ieee80211_crypt_data **crypt;
+ struct lib80211_crypt_data **crypt;
iface = netdev_priv(dev);
local = iface->local;
i = erq->flags & IW_ENCODE_INDEX;
if (i < 1 || i > 4)
- i = local->tx_keyidx;
+ i = local->crypt_info.tx_keyidx;
else
i--;
if (i < 0 || i >= WEP_KEYS)
return -EINVAL;
- crypt = &local->crypt[i];
+ crypt = &local->crypt_info.crypt[i];
if (erq->flags & IW_ENCODE_DISABLED) {
if (*crypt)
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
goto done;
}
if (*crypt != NULL && (*crypt)->ops != NULL &&
strcmp((*crypt)->ops->name, "WEP") != 0) {
/* changing to use WEP; deinit previously used algorithm */
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
}
if (*crypt == NULL) {
- struct ieee80211_crypt_data *new_crypt;
+ struct lib80211_crypt_data *new_crypt;
/* take WEP into use */
- new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
+ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
GFP_KERNEL);
if (new_crypt == NULL)
return -ENOMEM;
- new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ new_crypt->ops = lib80211_get_crypto_ops("WEP");
if (!new_crypt->ops) {
- request_module("ieee80211_crypt_wep");
- new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ request_module("lib80211_crypt_wep");
+ new_crypt->ops = lib80211_get_crypto_ops("WEP");
}
- if (new_crypt->ops)
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
new_crypt->priv = new_crypt->ops->init(i);
if (!new_crypt->ops || !new_crypt->priv) {
kfree(new_crypt);
@@ -210,16 +184,16 @@ static int prism2_ioctl_siwencode(struct net_device *dev,
memset(keybuf + erq->length, 0, len - erq->length);
(*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv);
for (j = 0; j < WEP_KEYS; j++) {
- if (j != i && local->crypt[j]) {
+ if (j != i && local->crypt_info.crypt[j]) {
first = 0;
break;
}
}
if (first)
- local->tx_keyidx = i;
+ local->crypt_info.tx_keyidx = i;
} else {
/* No key data - just set the default TX key index */
- local->tx_keyidx = i;
+ local->crypt_info.tx_keyidx = i;
}
done:
@@ -252,20 +226,20 @@ static int prism2_ioctl_giwencode(struct net_device *dev,
local_info_t *local;
int i, len;
u16 val;
- struct ieee80211_crypt_data *crypt;
+ struct lib80211_crypt_data *crypt;
iface = netdev_priv(dev);
local = iface->local;
i = erq->flags & IW_ENCODE_INDEX;
if (i < 1 || i > 4)
- i = local->tx_keyidx;
+ i = local->crypt_info.tx_keyidx;
else
i--;
if (i < 0 || i >= WEP_KEYS)
return -EINVAL;
- crypt = local->crypt[i];
+ crypt = local->crypt_info.crypt[i];
erq->flags = i + 1;
if (crypt == NULL || crypt->ops == NULL) {
@@ -664,7 +638,6 @@ static int hostap_join_ap(struct net_device *dev)
unsigned long flags;
int i;
struct hfa384x_hostscan_result *entry;
- DECLARE_MAC_BUF(mac);
iface = netdev_priv(dev);
local = iface->local;
@@ -686,14 +659,13 @@ static int hostap_join_ap(struct net_device *dev)
if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
sizeof(req))) {
- printk(KERN_DEBUG "%s: JoinRequest %s"
- " failed\n",
- dev->name, print_mac(mac, local->preferred_ap));
+ printk(KERN_DEBUG "%s: JoinRequest %pM failed\n",
+ dev->name, local->preferred_ap);
return -1;
}
- printk(KERN_DEBUG "%s: Trying to join BSSID %s\n",
- dev->name, print_mac(mac, local->preferred_ap));
+ printk(KERN_DEBUG "%s: Trying to join BSSID %pM\n",
+ dev->name, local->preferred_ap);
return 0;
}
@@ -3229,8 +3201,8 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
local_info_t *local = iface->local;
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
int i, ret = 0;
- struct ieee80211_crypto_ops *ops;
- struct ieee80211_crypt_data **crypt;
+ struct lib80211_crypto_ops *ops;
+ struct lib80211_crypt_data **crypt;
void *sta_ptr;
u8 *addr;
const char *alg, *module;
@@ -3239,7 +3211,7 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
if (i > WEP_KEYS)
return -EINVAL;
if (i < 1 || i > WEP_KEYS)
- i = local->tx_keyidx;
+ i = local->crypt_info.tx_keyidx;
else
i--;
if (i < 0 || i >= WEP_KEYS)
@@ -3249,7 +3221,7 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
sta_ptr = NULL;
- crypt = &local->crypt[i];
+ crypt = &local->crypt_info.crypt[i];
} else {
if (i != 0)
return -EINVAL;
@@ -3262,7 +3234,7 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
* is emulated by using default key idx 0.
*/
i = 0;
- crypt = &local->crypt[i];
+ crypt = &local->crypt_info.crypt[i];
} else
return -EINVAL;
}
@@ -3271,22 +3243,22 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
if ((erq->flags & IW_ENCODE_DISABLED) ||
ext->alg == IW_ENCODE_ALG_NONE) {
if (*crypt)
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
goto done;
}
switch (ext->alg) {
case IW_ENCODE_ALG_WEP:
alg = "WEP";
- module = "ieee80211_crypt_wep";
+ module = "lib80211_crypt_wep";
break;
case IW_ENCODE_ALG_TKIP:
alg = "TKIP";
- module = "ieee80211_crypt_tkip";
+ module = "lib80211_crypt_tkip";
break;
case IW_ENCODE_ALG_CCMP:
alg = "CCMP";
- module = "ieee80211_crypt_ccmp";
+ module = "lib80211_crypt_ccmp";
break;
default:
printk(KERN_DEBUG "%s: unsupported algorithm %d\n",
@@ -3295,10 +3267,10 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
goto done;
}
- ops = ieee80211_get_crypto_ops(alg);
+ ops = lib80211_get_crypto_ops(alg);
if (ops == NULL) {
request_module(module);
- ops = ieee80211_get_crypto_ops(alg);
+ ops = lib80211_get_crypto_ops(alg);
}
if (ops == NULL) {
printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
@@ -3317,18 +3289,19 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
}
if (*crypt == NULL || (*crypt)->ops != ops) {
- struct ieee80211_crypt_data *new_crypt;
+ struct lib80211_crypt_data *new_crypt;
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
- new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
+ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
GFP_KERNEL);
if (new_crypt == NULL) {
ret = -ENOMEM;
goto done;
}
new_crypt->ops = ops;
- new_crypt->priv = new_crypt->ops->init(i);
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(i);
if (new_crypt->priv == NULL) {
kfree(new_crypt);
ret = -EINVAL;
@@ -3356,20 +3329,20 @@ static int prism2_ioctl_siwencodeext(struct net_device *dev,
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
if (!sta_ptr)
- local->tx_keyidx = i;
+ local->crypt_info.tx_keyidx = i;
}
if (sta_ptr == NULL && ext->key_len > 0) {
int first = 1, j;
for (j = 0; j < WEP_KEYS; j++) {
- if (j != i && local->crypt[j]) {
+ if (j != i && local->crypt_info.crypt[j]) {
first = 0;
break;
}
}
if (first)
- local->tx_keyidx = i;
+ local->crypt_info.tx_keyidx = i;
}
done:
@@ -3401,7 +3374,7 @@ static int prism2_ioctl_giwencodeext(struct net_device *dev,
{
struct hostap_interface *iface = netdev_priv(dev);
local_info_t *local = iface->local;
- struct ieee80211_crypt_data **crypt;
+ struct lib80211_crypt_data **crypt;
void *sta_ptr;
int max_key_len, i;
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
@@ -3413,7 +3386,7 @@ static int prism2_ioctl_giwencodeext(struct net_device *dev,
i = erq->flags & IW_ENCODE_INDEX;
if (i < 1 || i > WEP_KEYS)
- i = local->tx_keyidx;
+ i = local->crypt_info.tx_keyidx;
else
i--;
@@ -3421,7 +3394,7 @@ static int prism2_ioctl_giwencodeext(struct net_device *dev,
if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
sta_ptr = NULL;
- crypt = &local->crypt[i];
+ crypt = &local->crypt_info.crypt[i];
} else {
i = 0;
sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt);
@@ -3470,8 +3443,8 @@ static int prism2_ioctl_set_encryption(local_info_t *local,
int param_len)
{
int ret = 0;
- struct ieee80211_crypto_ops *ops;
- struct ieee80211_crypt_data **crypt;
+ struct lib80211_crypto_ops *ops;
+ struct lib80211_crypt_data **crypt;
void *sta_ptr;
param->u.crypt.err = 0;
@@ -3488,7 +3461,7 @@ static int prism2_ioctl_set_encryption(local_info_t *local,
if (param->u.crypt.idx >= WEP_KEYS)
return -EINVAL;
sta_ptr = NULL;
- crypt = &local->crypt[param->u.crypt.idx];
+ crypt = &local->crypt_info.crypt[param->u.crypt.idx];
} else {
if (param->u.crypt.idx)
return -EINVAL;
@@ -3505,20 +3478,20 @@ static int prism2_ioctl_set_encryption(local_info_t *local,
if (strcmp(param->u.crypt.alg, "none") == 0) {
if (crypt)
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
goto done;
}
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
- request_module("ieee80211_crypt_wep");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ request_module("lib80211_crypt_wep");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
} else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
- request_module("ieee80211_crypt_tkip");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ request_module("lib80211_crypt_tkip");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
} else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
- request_module("ieee80211_crypt_ccmp");
- ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ request_module("lib80211_crypt_ccmp");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
}
if (ops == NULL) {
printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n",
@@ -3533,11 +3506,11 @@ static int prism2_ioctl_set_encryption(local_info_t *local,
local->host_decrypt = local->host_encrypt = 1;
if (*crypt == NULL || (*crypt)->ops != ops) {
- struct ieee80211_crypt_data *new_crypt;
+ struct lib80211_crypt_data *new_crypt;
- prism2_crypt_delayed_deinit(local, crypt);
+ lib80211_crypt_delayed_deinit(&local->crypt_info, crypt);
- new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
+ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
GFP_KERNEL);
if (new_crypt == NULL) {
ret = -ENOMEM;
@@ -3570,7 +3543,7 @@ static int prism2_ioctl_set_encryption(local_info_t *local,
if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) {
if (!sta_ptr)
- local->tx_keyidx = param->u.crypt.idx;
+ local->crypt_info.tx_keyidx = param->u.crypt.idx;
else if (param->u.crypt.idx) {
printk(KERN_DEBUG "%s: TX key idx setting failed\n",
local->dev->name);
@@ -3606,7 +3579,7 @@ static int prism2_ioctl_get_encryption(local_info_t *local,
struct prism2_hostapd_param *param,
int param_len)
{
- struct ieee80211_crypt_data **crypt;
+ struct lib80211_crypt_data **crypt;
void *sta_ptr;
int max_key_len;
@@ -3622,8 +3595,8 @@ static int prism2_ioctl_get_encryption(local_info_t *local,
param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
sta_ptr = NULL;
if (param->u.crypt.idx >= WEP_KEYS)
- param->u.crypt.idx = local->tx_keyidx;
- crypt = &local->crypt[param->u.crypt.idx];
+ param->u.crypt.idx = local->crypt_info.tx_keyidx;
+ crypt = &local->crypt_info.crypt[param->u.crypt.idx];
} else {
param->u.crypt.idx = 0;
sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0,
@@ -3701,10 +3674,8 @@ static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local,
struct prism2_hostapd_param *param,
int param_len)
{
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%ssta: associated as client with AP "
- "%s\n",
- local->dev->name, print_mac(mac, param->sta_addr));
+ printk(KERN_DEBUG "%ssta: associated as client with AP %pM\n",
+ local->dev->name, param->sta_addr);
memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN);
return 0;
}
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index 756ab56c1f40..02a312ca8607 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -27,7 +27,7 @@
#include <net/net_namespace.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>
-#include <net/ieee80211_crypt.h>
+#include <net/lib80211.h>
#include <asm/uaccess.h>
#include "hostap_wlan.h"
@@ -343,10 +343,11 @@ int hostap_set_encryption(local_info_t *local)
char keybuf[WEP_KEY_LEN + 1];
enum { NONE, WEP, OTHER } encrypt_type;
- idx = local->tx_keyidx;
- if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL)
+ idx = local->crypt_info.tx_keyidx;
+ if (local->crypt_info.crypt[idx] == NULL ||
+ local->crypt_info.crypt[idx]->ops == NULL)
encrypt_type = NONE;
- else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0)
+ else if (strcmp(local->crypt_info.crypt[idx]->ops->name, "WEP") == 0)
encrypt_type = WEP;
else
encrypt_type = OTHER;
@@ -394,17 +395,17 @@ int hostap_set_encryption(local_info_t *local)
/* 104-bit support seems to require that all the keys are set to the
* same keylen */
keylen = 6; /* first 5 octets */
- len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf),
- NULL, local->crypt[idx]->priv);
+ len = local->crypt_info.crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), NULL,
+ local->crypt_info.crypt[idx]->priv);
if (idx >= 0 && idx < WEP_KEYS && len > 5)
keylen = WEP_KEY_LEN + 1; /* first 13 octets */
for (i = 0; i < WEP_KEYS; i++) {
memset(keybuf, 0, sizeof(keybuf));
- if (local->crypt[i]) {
- (void) local->crypt[i]->ops->get_key(
+ if (local->crypt_info.crypt[i]) {
+ (void) local->crypt_info.crypt[i]->ops->get_key(
keybuf, sizeof(keybuf),
- NULL, local->crypt[i]->priv);
+ NULL, local->crypt_info.crypt[i]->priv);
}
if (local->func->set_rid(local->dev,
HFA384X_RID_CNFDEFAULTKEY0 + i,
@@ -530,10 +531,6 @@ int hostap_set_auth_algs(local_info_t *local)
void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
{
u16 status, fc;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
status = __le16_to_cpu(rx->status);
@@ -552,12 +549,11 @@ void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
- printk(KERN_DEBUG " A1=%s A2=%s A3=%s A4=%s\n",
- print_mac(mac, rx->addr1), print_mac(mac2, rx->addr2),
- print_mac(mac3, rx->addr3), print_mac(mac4, rx->addr4));
+ printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n",
+ rx->addr1, rx->addr2, rx->addr3, rx->addr4);
- printk(KERN_DEBUG " dst=%s src=%s len=%d\n",
- print_mac(mac, rx->dst_addr), print_mac(mac2, rx->src_addr),
+ printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n",
+ rx->dst_addr, rx->src_addr,
__be16_to_cpu(rx->len));
}
@@ -565,10 +561,6 @@ void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx)
void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
{
u16 fc;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d "
"tx_control=0x%04x; jiffies=%ld\n",
@@ -584,12 +576,11 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx)
fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : "");
- printk(KERN_DEBUG " A1=%s A2=%s A3=%s A4=%s\n",
- print_mac(mac, tx->addr1), print_mac(mac2, tx->addr2),
- print_mac(mac3, tx->addr3), print_mac(mac4, tx->addr4));
+ printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n",
+ tx->addr1, tx->addr2, tx->addr3, tx->addr4);
- printk(KERN_DEBUG " dst=%s src=%s len=%d\n",
- print_mac(mac, tx->dst_addr), print_mac(mac2, tx->src_addr),
+ printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n",
+ tx->dst_addr, tx->src_addr,
__be16_to_cpu(tx->len));
}
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c
index 3a874fc621d3..8fdd41f4b4f2 100644
--- a/drivers/net/wireless/hostap/hostap_pci.c
+++ b/drivers/net/wireless/hostap/hostap_pci.c
@@ -312,7 +312,7 @@ static int prism2_pci_probe(struct pci_dev *pdev,
goto err_out_disable;
}
- mem = ioremap(phymem, pci_resource_len(pdev, 0));
+ mem = pci_ioremap_bar(pdev, 0);
if (mem == NULL) {
printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
goto fail;
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c
index b03536008ad9..005ff25a405f 100644
--- a/drivers/net/wireless/hostap/hostap_proc.c
+++ b/drivers/net/wireless/hostap/hostap_proc.c
@@ -2,7 +2,7 @@
#include <linux/types.h>
#include <linux/proc_fs.h>
-#include <net/ieee80211_crypt.h>
+#include <net/lib80211.h>
#include "hostap_wlan.h"
#include "hostap.h"
@@ -36,9 +36,10 @@ static int prism2_debug_proc_read(char *page, char **start, off_t off,
p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled);
p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck);
for (i = 0; i < WEP_KEYS; i++) {
- if (local->crypt[i] && local->crypt[i]->ops) {
- p += sprintf(p, "crypt[%d]=%s\n",
- i, local->crypt[i]->ops->name);
+ if (local->crypt_info.crypt[i] &&
+ local->crypt_info.crypt[i]->ops) {
+ p += sprintf(p, "crypt[%d]=%s\n", i,
+ local->crypt_info.crypt[i]->ops->name);
}
}
p += sprintf(p, "pri_only=%d\n", local->pri_only);
@@ -106,7 +107,6 @@ static int prism2_wds_proc_read(char *page, char **start, off_t off,
local_info_t *local = (local_info_t *) data;
struct list_head *ptr;
struct hostap_interface *iface;
- DECLARE_MAC_BUF(mac);
if (off > PROC_LIMIT) {
*eof = 1;
@@ -118,9 +118,9 @@ static int prism2_wds_proc_read(char *page, char **start, off_t off,
iface = list_entry(ptr, struct hostap_interface, list);
if (iface->type != HOSTAP_INTERFACE_WDS)
continue;
- p += sprintf(p, "%s\t%s\n",
+ p += sprintf(p, "%s\t%pM\n",
iface->dev->name,
- print_mac(mac, iface->u.wds.remote_addr));
+ iface->u.wds.remote_addr);
if ((p - page) > PROC_LIMIT) {
printk(KERN_DEBUG "%s: wds proc did not fit\n",
local->dev->name);
@@ -148,7 +148,6 @@ static int prism2_bss_list_proc_read(char *page, char **start, off_t off,
struct list_head *ptr;
struct hostap_bss_info *bss;
int i;
- DECLARE_MAC_BUF(mac);
if (off > PROC_LIMIT) {
*eof = 1;
@@ -160,8 +159,8 @@ static int prism2_bss_list_proc_read(char *page, char **start, off_t off,
spin_lock_bh(&local->lock);
list_for_each(ptr, &local->bss_list) {
bss = list_entry(ptr, struct hostap_bss_info, list);
- p += sprintf(p, "%s\t%lu\t%u\t0x%x\t",
- print_mac(mac, bss->bssid), bss->last_update,
+ p += sprintf(p, "%pM\t%lu\t%u\t0x%x\t",
+ bss->bssid, bss->last_update,
bss->count, bss->capab_info);
for (i = 0; i < bss->ssid_len; i++) {
p += sprintf(p, "%c",
@@ -208,12 +207,13 @@ static int prism2_crypt_proc_read(char *page, char **start, off_t off,
return 0;
}
- p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx);
+ p += sprintf(p, "tx_keyidx=%d\n", local->crypt_info.tx_keyidx);
for (i = 0; i < WEP_KEYS; i++) {
- if (local->crypt[i] && local->crypt[i]->ops &&
- local->crypt[i]->ops->print_stats) {
- p = local->crypt[i]->ops->print_stats(
- p, local->crypt[i]->priv);
+ if (local->crypt_info.crypt[i] &&
+ local->crypt_info.crypt[i]->ops &&
+ local->crypt_info.crypt[i]->ops->print_stats) {
+ p = local->crypt_info.crypt[i]->ops->print_stats(
+ p, local->crypt_info.crypt[i]->priv);
}
}
@@ -314,7 +314,6 @@ static int prism2_scan_results_proc_read(char *page, char **start, off_t off,
int entry, i, len, total = 0;
struct hfa384x_hostscan_result *scanres;
u8 *pos;
- DECLARE_MAC_BUF(mac);
p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates "
"SSID\n");
@@ -332,14 +331,14 @@ static int prism2_scan_results_proc_read(char *page, char **start, off_t off,
if ((p - page) > (PAGE_SIZE - 200))
break;
- p += sprintf(p, "%d %d %d %d 0x%02x %d %s %d ",
+ p += sprintf(p, "%d %d %d %d 0x%02x %d %pM %d ",
le16_to_cpu(scanres->chid),
(s16) le16_to_cpu(scanres->anl),
(s16) le16_to_cpu(scanres->sl),
le16_to_cpu(scanres->beacon_interval),
le16_to_cpu(scanres->capability),
le16_to_cpu(scanres->rate),
- print_mac(mac, scanres->bssid),
+ scanres->bssid,
le16_to_cpu(scanres->atim));
pos = scanres->sup_rates;
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h
index a68f97c39359..4d8d51a353cd 100644
--- a/drivers/net/wireless/hostap/hostap_wlan.h
+++ b/drivers/net/wireless/hostap/hostap_wlan.h
@@ -6,6 +6,7 @@
#include <linux/mutex.h>
#include <net/iw_handler.h>
#include <net/ieee80211_radiotap.h>
+#include <net/lib80211.h>
#include "hostap_config.h"
#include "hostap_common.h"
@@ -763,10 +764,7 @@ struct local_info {
#define WEP_KEYS 4
#define WEP_KEY_LEN 13
- struct ieee80211_crypt_data *crypt[WEP_KEYS];
- int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
- struct timer_list crypt_deinit_timer;
- struct list_head crypt_deinit_list;
+ struct lib80211_crypt_info crypt_info;
int open_wep; /* allow unencrypted frames */
int host_encrypt;
@@ -822,7 +820,7 @@ struct local_info {
int last_scan_results_count;
enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type;
struct work_struct info_queue;
- long pending_info; /* bit field of pending info_queue items */
+ unsigned long pending_info; /* bit field of pending info_queue items */
#define PRISM2_INFO_PENDING_LINKSTATUS 0
#define PRISM2_INFO_PENDING_SCANRESULTS 1
int prev_link_status; /* previous received LinkStatus info */
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
deleted file mode 100644
index bca74811bc7f..000000000000
--- a/drivers/net/wireless/ipw2100.c
+++ /dev/null
@@ -1,8654 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
- Portions of this file are based on the sample_* files provided by Wireless
- Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
- <jt@hpl.hp.com>
-
- Portions of this file are based on the Host AP project,
- Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- <j@w1.fi>
- Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
-
- Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
- ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
- available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
-
-******************************************************************************/
-/*
-
- Initial driver on which this is based was developed by Janusz Gorycki,
- Maciej Urbaniak, and Maciej Sosnowski.
-
- Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
-
-Theory of Operation
-
-Tx - Commands and Data
-
-Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
-Each TBD contains a pointer to the physical (dma_addr_t) address of data being
-sent to the firmware as well as the length of the data.
-
-The host writes to the TBD queue at the WRITE index. The WRITE index points
-to the _next_ packet to be written and is advanced when after the TBD has been
-filled.
-
-The firmware pulls from the TBD queue at the READ index. The READ index points
-to the currently being read entry, and is advanced once the firmware is
-done with a packet.
-
-When data is sent to the firmware, the first TBD is used to indicate to the
-firmware if a Command or Data is being sent. If it is Command, all of the
-command information is contained within the physical address referred to by the
-TBD. If it is Data, the first TBD indicates the type of data packet, number
-of fragments, etc. The next TBD then referrs to the actual packet location.
-
-The Tx flow cycle is as follows:
-
-1) ipw2100_tx() is called by kernel with SKB to transmit
-2) Packet is move from the tx_free_list and appended to the transmit pending
- list (tx_pend_list)
-3) work is scheduled to move pending packets into the shared circular queue.
-4) when placing packet in the circular queue, the incoming SKB is DMA mapped
- to a physical address. That address is entered into a TBD. Two TBDs are
- filled out. The first indicating a data packet, the second referring to the
- actual payload data.
-5) the packet is removed from tx_pend_list and placed on the end of the
- firmware pending list (fw_pend_list)
-6) firmware is notified that the WRITE index has
-7) Once the firmware has processed the TBD, INTA is triggered.
-8) For each Tx interrupt received from the firmware, the READ index is checked
- to see which TBDs are done being processed.
-9) For each TBD that has been processed, the ISR pulls the oldest packet
- from the fw_pend_list.
-10)The packet structure contained in the fw_pend_list is then used
- to unmap the DMA address and to free the SKB originally passed to the driver
- from the kernel.
-11)The packet structure is placed onto the tx_free_list
-
-The above steps are the same for commands, only the msg_free_list/msg_pend_list
-are used instead of tx_free_list/tx_pend_list
-
-...
-
-Critical Sections / Locking :
-
-There are two locks utilized. The first is the low level lock (priv->low_lock)
-that protects the following:
-
-- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
-
- tx_free_list : Holds pre-allocated Tx buffers.
- TAIL modified in __ipw2100_tx_process()
- HEAD modified in ipw2100_tx()
-
- tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
- TAIL modified ipw2100_tx()
- HEAD modified by ipw2100_tx_send_data()
-
- msg_free_list : Holds pre-allocated Msg (Command) buffers
- TAIL modified in __ipw2100_tx_process()
- HEAD modified in ipw2100_hw_send_command()
-
- msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
- TAIL modified in ipw2100_hw_send_command()
- HEAD modified in ipw2100_tx_send_commands()
-
- The flow of data on the TX side is as follows:
-
- MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
- TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
-
- The methods that work on the TBD ring are protected via priv->low_lock.
-
-- The internal data state of the device itself
-- Access to the firmware read/write indexes for the BD queues
- and associated logic
-
-All external entry functions are locked with the priv->action_lock to ensure
-that only one external action is invoked at a time.
-
-
-*/
-
-#include <linux/compiler.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/kmod.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/unistd.h>
-#include <linux/stringify.h>
-#include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/time.h>
-#include <linux/firmware.h>
-#include <linux/acpi.h>
-#include <linux/ctype.h>
-#include <linux/pm_qos_params.h>
-
-#include "ipw2100.h"
-
-#define IPW2100_VERSION "git-1.2.2"
-
-#define DRV_NAME "ipw2100"
-#define DRV_VERSION IPW2100_VERSION
-#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
-#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
-
-/* Debugging stuff */
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW2100_RX_DEBUG /* Reception debugging */
-#endif
-
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
-MODULE_LICENSE("GPL");
-
-static int debug = 0;
-static int mode = 0;
-static int channel = 0;
-static int associate = 1;
-static int disable = 0;
-#ifdef CONFIG_PM
-static struct ipw2100_fw ipw2100_firmware;
-#endif
-
-#include <linux/moduleparam.h>
-module_param(debug, int, 0444);
-module_param(mode, int, 0444);
-module_param(channel, int, 0444);
-module_param(associate, int, 0444);
-module_param(disable, int, 0444);
-
-MODULE_PARM_DESC(debug, "debug level");
-MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
-MODULE_PARM_DESC(channel, "channel");
-MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
-MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
-
-static u32 ipw2100_debug_level = IPW_DL_NONE;
-
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW_DEBUG(level, message...) \
-do { \
- if (ipw2100_debug_level & (level)) { \
- printk(KERN_DEBUG "ipw2100: %c %s ", \
- in_interrupt() ? 'I' : 'U', __func__); \
- printk(message); \
- } \
-} while (0)
-#else
-#define IPW_DEBUG(level, message...) do {} while (0)
-#endif /* CONFIG_IPW2100_DEBUG */
-
-#ifdef CONFIG_IPW2100_DEBUG
-static const char *command_types[] = {
- "undefined",
- "unused", /* HOST_ATTENTION */
- "HOST_COMPLETE",
- "unused", /* SLEEP */
- "unused", /* HOST_POWER_DOWN */
- "unused",
- "SYSTEM_CONFIG",
- "unused", /* SET_IMR */
- "SSID",
- "MANDATORY_BSSID",
- "AUTHENTICATION_TYPE",
- "ADAPTER_ADDRESS",
- "PORT_TYPE",
- "INTERNATIONAL_MODE",
- "CHANNEL",
- "RTS_THRESHOLD",
- "FRAG_THRESHOLD",
- "POWER_MODE",
- "TX_RATES",
- "BASIC_TX_RATES",
- "WEP_KEY_INFO",
- "unused",
- "unused",
- "unused",
- "unused",
- "WEP_KEY_INDEX",
- "WEP_FLAGS",
- "ADD_MULTICAST",
- "CLEAR_ALL_MULTICAST",
- "BEACON_INTERVAL",
- "ATIM_WINDOW",
- "CLEAR_STATISTICS",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "TX_POWER_INDEX",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "undefined",
- "BROADCAST_SCAN",
- "CARD_DISABLE",
- "PREFERRED_BSSID",
- "SET_SCAN_OPTIONS",
- "SCAN_DWELL_TIME",
- "SWEEP_TABLE",
- "AP_OR_STATION_TABLE",
- "GROUP_ORDINALS",
- "SHORT_RETRY_LIMIT",
- "LONG_RETRY_LIMIT",
- "unused", /* SAVE_CALIBRATION */
- "unused", /* RESTORE_CALIBRATION */
- "undefined",
- "undefined",
- "undefined",
- "HOST_PRE_POWER_DOWN",
- "unused", /* HOST_INTERRUPT_COALESCING */
- "undefined",
- "CARD_DISABLE_PHY_OFF",
- "MSDU_TX_RATES" "undefined",
- "undefined",
- "SET_STATION_STAT_BITS",
- "CLEAR_STATIONS_STAT_BITS",
- "LEAP_ROGUE_MODE",
- "SET_SECURITY_INFORMATION",
- "DISASSOCIATION_BSSID",
- "SET_WPA_ASS_IE"
-};
-#endif
-
-/* Pre-decl until we get the code solid and then we can clean it up */
-static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
-static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
-static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
-
-static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
-static void ipw2100_queues_free(struct ipw2100_priv *priv);
-static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
-
-static int ipw2100_fw_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_get_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
- size_t max);
-static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
- size_t max);
-static void ipw2100_release_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static int ipw2100_ucode_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw);
-static void ipw2100_wx_event_work(struct work_struct *work);
-static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
-static struct iw_handler_def ipw2100_wx_handler_def;
-
-static inline void read_register(struct net_device *dev, u32 reg, u32 * val)
-{
- *val = readl((void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
-}
-
-static inline void write_register(struct net_device *dev, u32 reg, u32 val)
-{
- writel(val, (void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
-}
-
-static inline void read_register_word(struct net_device *dev, u32 reg,
- u16 * val)
-{
- *val = readw((void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
-}
-
-static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val)
-{
- *val = readb((void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
-}
-
-static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
-{
- writew(val, (void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
-}
-
-static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
-{
- writeb(val, (void __iomem *)(dev->base_addr + reg));
- IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
-}
-
-static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
-{
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
- write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
-}
-
-static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
-{
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
- addr & IPW_REG_INDIRECT_ADDR_MASK);
-}
-
-static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
-{
- write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
-}
-
-static void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
- const u8 * buf)
-{
- u32 aligned_addr;
- u32 aligned_len;
- u32 dif_len;
- u32 i;
-
- /* read first nibble byte by byte */
- aligned_addr = addr & (~0x3);
- dif_len = addr - aligned_addr;
- if (dif_len) {
- /* Start reading at aligned_addr + dif_len */
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- aligned_addr);
- for (i = dif_len; i < 4; i++, buf++)
- write_register_byte(dev,
- IPW_REG_INDIRECT_ACCESS_DATA + i,
- *buf);
-
- len -= dif_len;
- aligned_addr += 4;
- }
-
- /* read DWs through autoincrement registers */
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
- aligned_len = len & (~0x3);
- for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
- write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf);
-
- /* copy the last nibble */
- dif_len = len - aligned_len;
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
- for (i = 0; i < dif_len; i++, buf++)
- write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
- *buf);
-}
-
-static void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
- u8 * buf)
-{
- u32 aligned_addr;
- u32 aligned_len;
- u32 dif_len;
- u32 i;
-
- /* read first nibble byte by byte */
- aligned_addr = addr & (~0x3);
- dif_len = addr - aligned_addr;
- if (dif_len) {
- /* Start reading at aligned_addr + dif_len */
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
- aligned_addr);
- for (i = dif_len; i < 4; i++, buf++)
- read_register_byte(dev,
- IPW_REG_INDIRECT_ACCESS_DATA + i,
- buf);
-
- len -= dif_len;
- aligned_addr += 4;
- }
-
- /* read DWs through autoincrement registers */
- write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
- aligned_len = len & (~0x3);
- for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
- read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf);
-
- /* copy the last nibble */
- dif_len = len - aligned_len;
- write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
- for (i = 0; i < dif_len; i++, buf++)
- read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
-}
-
-static inline int ipw2100_hw_is_adapter_in_system(struct net_device *dev)
-{
- return (dev->base_addr &&
- (readl
- ((void __iomem *)(dev->base_addr +
- IPW_REG_DOA_DEBUG_AREA_START))
- == IPW_DATA_DOA_DEBUG_VALUE));
-}
-
-static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
- void *val, u32 * len)
-{
- struct ipw2100_ordinals *ordinals = &priv->ordinals;
- u32 addr;
- u32 field_info;
- u16 field_len;
- u16 field_count;
- u32 total_length;
-
- if (ordinals->table1_addr == 0) {
- printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
- "before they have been loaded.\n");
- return -EINVAL;
- }
-
- if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
- if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- printk(KERN_WARNING DRV_NAME
- ": ordinal buffer length too small, need %zd\n",
- IPW_ORD_TAB_1_ENTRY_SIZE);
-
- return -EINVAL;
- }
-
- read_nic_dword(priv->net_dev,
- ordinals->table1_addr + (ord << 2), &addr);
- read_nic_dword(priv->net_dev, addr, val);
-
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- return 0;
- }
-
- if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
-
- ord -= IPW_START_ORD_TAB_2;
-
- /* get the address of statistic */
- read_nic_dword(priv->net_dev,
- ordinals->table2_addr + (ord << 3), &addr);
-
- /* get the second DW of statistics ;
- * two 16-bit words - first is length, second is count */
- read_nic_dword(priv->net_dev,
- ordinals->table2_addr + (ord << 3) + sizeof(u32),
- &field_info);
-
- /* get each entry length */
- field_len = *((u16 *) & field_info);
-
- /* get number of entries */
- field_count = *(((u16 *) & field_info) + 1);
-
- /* abort if no enought memory */
- total_length = field_len * field_count;
- if (total_length > *len) {
- *len = total_length;
- return -EINVAL;
- }
-
- *len = total_length;
- if (!total_length)
- return 0;
-
- /* read the ordinal data from the SRAM */
- read_nic_memory(priv->net_dev, addr, total_length, val);
-
- return 0;
- }
-
- printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
- "in table 2\n", ord);
-
- return -EINVAL;
-}
-
-static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val,
- u32 * len)
-{
- struct ipw2100_ordinals *ordinals = &priv->ordinals;
- u32 addr;
-
- if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
- if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
- IPW_DEBUG_INFO("wrong size\n");
- return -EINVAL;
- }
-
- read_nic_dword(priv->net_dev,
- ordinals->table1_addr + (ord << 2), &addr);
-
- write_nic_dword(priv->net_dev, addr, *val);
-
- *len = IPW_ORD_TAB_1_ENTRY_SIZE;
-
- return 0;
- }
-
- IPW_DEBUG_INFO("wrong table\n");
- if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
- return -EINVAL;
-
- return -EINVAL;
-}
-
-static char *snprint_line(char *buf, size_t count,
- const u8 * data, u32 len, u32 ofs)
-{
- int out, i, j, l;
- char c;
-
- out = snprintf(buf, count, "%08X", ofs);
-
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++)
- out += snprintf(buf + out, count - out, "%02X ",
- data[(i * 8 + j)]);
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- out += snprintf(buf + out, count - out, " ");
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++) {
- c = data[(i * 8 + j)];
- if (!isascii(c) || !isprint(c))
- c = '.';
-
- out += snprintf(buf + out, count - out, "%c", c);
- }
-
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- return buf;
-}
-
-static void printk_buf(int level, const u8 * data, u32 len)
-{
- char line[81];
- u32 ofs = 0;
- if (!(ipw2100_debug_level & level))
- return;
-
- while (len) {
- printk(KERN_DEBUG "%s\n",
- snprint_line(line, sizeof(line), &data[ofs],
- min(len, 16U), ofs));
- ofs += 16;
- len -= min(len, 16U);
- }
-}
-
-#define MAX_RESET_BACKOFF 10
-
-static void schedule_reset(struct ipw2100_priv *priv)
-{
- unsigned long now = get_seconds();
-
- /* If we haven't received a reset request within the backoff period,
- * then we can reset the backoff interval so this reset occurs
- * immediately */
- if (priv->reset_backoff &&
- (now - priv->last_reset > priv->reset_backoff))
- priv->reset_backoff = 0;
-
- priv->last_reset = get_seconds();
-
- if (!(priv->status & STATUS_RESET_PENDING)) {
- IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
- priv->net_dev->name, priv->reset_backoff);
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
- priv->status |= STATUS_RESET_PENDING;
- if (priv->reset_backoff)
- queue_delayed_work(priv->workqueue, &priv->reset_work,
- priv->reset_backoff * HZ);
- else
- queue_delayed_work(priv->workqueue, &priv->reset_work,
- 0);
-
- if (priv->reset_backoff < MAX_RESET_BACKOFF)
- priv->reset_backoff++;
-
- wake_up_interruptible(&priv->wait_command_queue);
- } else
- IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
- priv->net_dev->name);
-
-}
-
-#define HOST_COMPLETE_TIMEOUT (2 * HZ)
-static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
- struct host_command *cmd)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- unsigned long flags;
- int err = 0;
-
- IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
- command_types[cmd->host_command], cmd->host_command,
- cmd->host_command_length);
- printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters,
- cmd->host_command_length);
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->fatal_error) {
- IPW_DEBUG_INFO
- ("Attempt to send command while hardware in fatal error condition.\n");
- err = -EIO;
- goto fail_unlock;
- }
-
- if (!(priv->status & STATUS_RUNNING)) {
- IPW_DEBUG_INFO
- ("Attempt to send command while hardware is not running.\n");
- err = -EIO;
- goto fail_unlock;
- }
-
- if (priv->status & STATUS_CMD_ACTIVE) {
- IPW_DEBUG_INFO
- ("Attempt to send command while another command is pending.\n");
- err = -EBUSY;
- goto fail_unlock;
- }
-
- if (list_empty(&priv->msg_free_list)) {
- IPW_DEBUG_INFO("no available msg buffers\n");
- goto fail_unlock;
- }
-
- priv->status |= STATUS_CMD_ACTIVE;
- priv->messages_sent++;
-
- element = priv->msg_free_list.next;
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
- packet->jiffy_start = jiffies;
-
- /* initialize the firmware command packet */
- packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
- packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
- packet->info.c_struct.cmd->host_command_len_reg =
- cmd->host_command_length;
- packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
-
- memcpy(packet->info.c_struct.cmd->host_command_params_reg,
- cmd->host_command_parameters,
- sizeof(packet->info.c_struct.cmd->host_command_params_reg));
-
- list_del(element);
- DEC_STAT(&priv->msg_free_stat);
-
- list_add_tail(element, &priv->msg_pend_list);
- INC_STAT(&priv->msg_pend_stat);
-
- ipw2100_tx_send_commands(priv);
- ipw2100_tx_send_data(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- /*
- * We must wait for this command to complete before another
- * command can be sent... but if we wait more than 3 seconds
- * then there is a problem.
- */
-
- err =
- wait_event_interruptible_timeout(priv->wait_command_queue,
- !(priv->
- status & STATUS_CMD_ACTIVE),
- HOST_COMPLETE_TIMEOUT);
-
- if (err == 0) {
- IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
- 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
- priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
- priv->status &= ~STATUS_CMD_ACTIVE;
- schedule_reset(priv);
- return -EIO;
- }
-
- if (priv->fatal_error) {
- printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* !!!!! HACK TEST !!!!!
- * When lots of debug trace statements are enabled, the driver
- * doesn't seem to have as many firmware restart cycles...
- *
- * As a test, we're sticking in a 1/100s delay here */
- schedule_timeout_uninterruptible(msecs_to_jiffies(10));
-
- return 0;
-
- fail_unlock:
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- return err;
-}
-
-/*
- * Verify the values and data access of the hardware
- * No locks needed or used. No functions called.
- */
-static int ipw2100_verify(struct ipw2100_priv *priv)
-{
- u32 data1, data2;
- u32 address;
-
- u32 val1 = 0x76543210;
- u32 val2 = 0xFEDCBA98;
-
- /* Domain 0 check - all values should be DOA_DEBUG */
- for (address = IPW_REG_DOA_DEBUG_AREA_START;
- address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) {
- read_register(priv->net_dev, address, &data1);
- if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
- return -EIO;
- }
-
- /* Domain 1 check - use arbitrary read/write compare */
- for (address = 0; address < 5; address++) {
- /* The memory area is not used now */
- write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
- val1);
- write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
- val2);
- read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
- &data1);
- read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
- &data2);
- if (val1 == data1 && val2 == data2)
- return 0;
- }
-
- return -EIO;
-}
-
-/*
- *
- * Loop until the CARD_DISABLED bit is the same value as the
- * supplied parameter
- *
- * TODO: See if it would be more efficient to do a wait/wake
- * cycle and have the completion event trigger the wakeup
- *
- */
-#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli
-static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
-{
- int i;
- u32 card_state;
- u32 len = sizeof(card_state);
- int err;
-
- for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
- err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
- &card_state, &len);
- if (err) {
- IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
- "failed.\n");
- return 0;
- }
-
- /* We'll break out if either the HW state says it is
- * in the state we want, or if HOST_COMPLETE command
- * finishes */
- if ((card_state == state) ||
- ((priv->status & STATUS_ENABLED) ?
- IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
- if (state == IPW_HW_STATE_ENABLED)
- priv->status |= STATUS_ENABLED;
- else
- priv->status &= ~STATUS_ENABLED;
-
- return 0;
- }
-
- udelay(50);
- }
-
- IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
- state ? "DISABLED" : "ENABLED");
- return -EIO;
-}
-
-/*********************************************************************
- Procedure : sw_reset_and_clock
- Purpose : Asserts s/w reset, asserts clock initialization
- and waits for clock stabilization
- ********************************************************************/
-static int sw_reset_and_clock(struct ipw2100_priv *priv)
-{
- int i;
- u32 r;
-
- // assert s/w reset
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- // wait for clock stabilization
- for (i = 0; i < 1000; i++) {
- udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
-
- // check clock ready bit
- read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
- if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
- break;
- }
-
- if (i == 1000)
- return -EIO; // TODO: better error value
-
- /* set "initialization complete" bit to move adapter to
- * D0 state */
- write_register(priv->net_dev, IPW_REG_GP_CNTRL,
- IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
-
- /* wait for clock stabilization */
- for (i = 0; i < 10000; i++) {
- udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
-
- /* check clock ready bit */
- read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
- if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
- break;
- }
-
- if (i == 10000)
- return -EIO; /* TODO: better error value */
-
- /* set D0 standby bit */
- read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
- write_register(priv->net_dev, IPW_REG_GP_CNTRL,
- r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
-
- return 0;
-}
-
-/*********************************************************************
- Procedure : ipw2100_download_firmware
- Purpose : Initiaze adapter after power on.
- The sequence is:
- 1. assert s/w reset first!
- 2. awake clocks & wait for clock stabilization
- 3. hold ARC (don't ask me why...)
- 4. load Dino ucode and reset/clock init again
- 5. zero-out shared mem
- 6. download f/w
- *******************************************************************/
-static int ipw2100_download_firmware(struct ipw2100_priv *priv)
-{
- u32 address;
- int err;
-
-#ifndef CONFIG_PM
- /* Fetch the firmware and microcode */
- struct ipw2100_fw ipw2100_firmware;
-#endif
-
- if (priv->fatal_error) {
- IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
- "fatal error %d. Interface must be brought down.\n",
- priv->net_dev->name, priv->fatal_error);
- return -EINVAL;
- }
-#ifdef CONFIG_PM
- if (!ipw2100_firmware.version) {
- err = ipw2100_get_firmware(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
- priv->net_dev->name, err);
- priv->fatal_error = IPW2100_ERR_FW_LOAD;
- goto fail;
- }
- }
-#else
- err = ipw2100_get_firmware(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
- priv->net_dev->name, err);
- priv->fatal_error = IPW2100_ERR_FW_LOAD;
- goto fail;
- }
-#endif
- priv->firmware_version = ipw2100_firmware.version;
-
- /* s/w reset and clock stabilization */
- err = sw_reset_and_clock(priv);
- if (err) {
- IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- err = ipw2100_verify(priv);
- if (err) {
- IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* Hold ARC */
- write_nic_dword(priv->net_dev,
- IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000);
-
- /* allow ARC to run */
- write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
-
- /* load microcode */
- err = ipw2100_ucode_download(priv, &ipw2100_firmware);
- if (err) {
- printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* release ARC */
- write_nic_dword(priv->net_dev,
- IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000);
-
- /* s/w reset and clock stabilization (again!!!) */
- err = sw_reset_and_clock(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: sw_reset_and_clock failed: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-
- /* load f/w */
- err = ipw2100_fw_download(priv, &ipw2100_firmware);
- if (err) {
- IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
- priv->net_dev->name, err);
- goto fail;
- }
-#ifndef CONFIG_PM
- /*
- * When the .resume method of the driver is called, the other
- * part of the system, i.e. the ide driver could still stay in
- * the suspend stage. This prevents us from loading the firmware
- * from the disk. --YZ
- */
-
- /* free any storage allocated for firmware image */
- ipw2100_release_firmware(priv, &ipw2100_firmware);
-#endif
-
- /* zero out Domain 1 area indirectly (Si requirement) */
- for (address = IPW_HOST_FW_SHARED_AREA0;
- address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA1;
- address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA2;
- address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_SHARED_AREA3;
- address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
- for (address = IPW_HOST_FW_INTERRUPT_AREA;
- address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
- write_nic_dword(priv->net_dev, address, 0);
-
- return 0;
-
- fail:
- ipw2100_release_firmware(priv, &ipw2100_firmware);
- return err;
-}
-
-static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
-{
- if (priv->status & STATUS_INT_ENABLED)
- return;
- priv->status |= STATUS_INT_ENABLED;
- write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
-}
-
-static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
-{
- if (!(priv->status & STATUS_INT_ENABLED))
- return;
- priv->status &= ~STATUS_INT_ENABLED;
- write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
-}
-
-static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
-{
- struct ipw2100_ordinals *ord = &priv->ordinals;
-
- IPW_DEBUG_INFO("enter\n");
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
- &ord->table1_addr);
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
- &ord->table2_addr);
-
- read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
- read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
-
- ord->table2_size &= 0x0000FFFF;
-
- IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
- IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
- IPW_DEBUG_INFO("exit\n");
-}
-
-static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
-{
- u32 reg = 0;
- /*
- * Set GPIO 3 writable by FW; GPIO 1 writable
- * by driver and enable clock
- */
- reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
- IPW_BIT_GPIO_LED_OFF);
- write_register(priv->net_dev, IPW_REG_GPIO, reg);
-}
-
-static int rf_kill_active(struct ipw2100_priv *priv)
-{
-#define MAX_RF_KILL_CHECKS 5
-#define RF_KILL_CHECK_DELAY 40
-
- unsigned short value = 0;
- u32 reg = 0;
- int i;
-
- if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
- priv->status &= ~STATUS_RF_KILL_HW;
- return 0;
- }
-
- for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
- udelay(RF_KILL_CHECK_DELAY);
- read_register(priv->net_dev, IPW_REG_GPIO, &reg);
- value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
- }
-
- if (value == 0)
- priv->status |= STATUS_RF_KILL_HW;
- else
- priv->status &= ~STATUS_RF_KILL_HW;
-
- return (value == 0);
-}
-
-static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
-{
- u32 addr, len;
- u32 val;
-
- /*
- * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
- */
- len = sizeof(addr);
- if (ipw2100_get_ordinal
- (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return -EIO;
- }
-
- IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
-
- /*
- * EEPROM version is the byte at offset 0xfd in firmware
- * We read 4 bytes, then shift out the byte we actually want */
- read_nic_dword(priv->net_dev, addr + 0xFC, &val);
- priv->eeprom_version = (val >> 24) & 0xFF;
- IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
-
- /*
- * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
- *
- * notice that the EEPROM bit is reverse polarity, i.e.
- * bit = 0 signifies HW RF kill switch is supported
- * bit = 1 signifies HW RF kill switch is NOT supported
- */
- read_nic_dword(priv->net_dev, addr + 0x20, &val);
- if (!((val >> 24) & 0x01))
- priv->hw_features |= HW_FEATURE_RFKILL;
-
- IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
- (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not ");
-
- return 0;
-}
-
-/*
- * Start firmware execution after power on and intialization
- * The sequence is:
- * 1. Release ARC
- * 2. Wait for f/w initialization completes;
- */
-static int ipw2100_start_adapter(struct ipw2100_priv *priv)
-{
- int i;
- u32 inta, inta_mask, gpio;
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->status & STATUS_RUNNING)
- return 0;
-
- /*
- * Initialize the hw - drive adapter to DO state by setting
- * init_done bit. Wait for clk_ready bit and Download
- * fw & dino ucode
- */
- if (ipw2100_download_firmware(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to power on the adapter.\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* Clear the Tx, Rx and Msg queues and the r/w indexes
- * in the firmware RBD and TBD ring queue */
- ipw2100_queues_initialize(priv);
-
- ipw2100_hw_set_gpio(priv);
-
- /* TODO -- Look at disabling interrupts here to make sure none
- * get fired during FW initialization */
-
- /* Release ARC - clear reset bit */
- write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
-
- /* wait for f/w intialization complete */
- IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
- i = 5000;
- do {
- schedule_timeout_uninterruptible(msecs_to_jiffies(40));
- /* Todo... wait for sync command ... */
-
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
-
- /* check "init done" bit */
- if (inta & IPW2100_INTA_FW_INIT_DONE) {
- /* reset "init done" bit */
- write_register(priv->net_dev, IPW_REG_INTA,
- IPW2100_INTA_FW_INIT_DONE);
- break;
- }
-
- /* check error conditions : we check these after the firmware
- * check so that if there is an error, the interrupt handler
- * will see it and the adapter will be reset */
- if (inta &
- (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
- /* clear error conditions */
- write_register(priv->net_dev, IPW_REG_INTA,
- IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR);
- }
- } while (--i);
-
- /* Clear out any pending INTAs since we aren't supposed to have
- * interrupts enabled at this point... */
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
- read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
- inta &= IPW_INTERRUPT_MASK;
- /* Clear out any pending interrupts */
- if (inta & inta_mask)
- write_register(priv->net_dev, IPW_REG_INTA, inta);
-
- IPW_DEBUG_FW("f/w initialization complete: %s\n",
- i ? "SUCCESS" : "FAILED");
-
- if (!i) {
- printk(KERN_WARNING DRV_NAME
- ": %s: Firmware did not initialize.\n",
- priv->net_dev->name);
- return -EIO;
- }
-
- /* allow firmware to write to GPIO1 & GPIO3 */
- read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
-
- gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
-
- write_register(priv->net_dev, IPW_REG_GPIO, gpio);
-
- /* Ready to receive commands */
- priv->status |= STATUS_RUNNING;
-
- /* The adapter has been reset; we are not associated */
- priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
-{
- if (!priv->fatal_error)
- return;
-
- priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
- priv->fatal_index %= IPW2100_ERROR_QUEUE;
- priv->fatal_error = 0;
-}
-
-/* NOTE: Our interrupt is disabled when this method is called */
-static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
-{
- u32 reg;
- int i;
-
- IPW_DEBUG_INFO("Power cycling the hardware.\n");
-
- ipw2100_hw_set_gpio(priv);
-
- /* Step 1. Stop Master Assert */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
-
- /* Step 2. Wait for stop Master Assert
- * (not more then 50us, otherwise ret error */
- i = 5;
- do {
- udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- } while (--i);
-
- priv->status &= ~STATUS_RESET_PENDING;
-
- if (!i) {
- IPW_DEBUG_INFO
- ("exit - waited too long for master assert stop\n");
- return -EIO;
- }
-
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- /* Reset any fatal_error conditions */
- ipw2100_reset_fatalerror(priv);
-
- /* At this point, the adapter is now stopped and disabled */
- priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
- STATUS_ASSOCIATED | STATUS_ENABLED);
-
- return 0;
-}
-
-/*
- * Send the CARD_DISABLE_PHY_OFF comamnd to the card to disable it
- *
- * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
- *
- * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
- * if STATUS_ASSN_LOST is sent.
- */
-static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
-{
-
-#define HW_PHY_OFF_LOOP_DELAY (HZ / 5000)
-
- struct host_command cmd = {
- .host_command = CARD_DISABLE_PHY_OFF,
- .host_command_sequence = 0,
- .host_command_length = 0,
- };
- int err, i;
- u32 val1, val2;
-
- IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
-
- /* Turn off the radio */
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- for (i = 0; i < 2500; i++) {
- read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
- read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
-
- if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
- (val2 & IPW2100_COMMAND_PHY_OFF))
- return 0;
-
- schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY);
- }
-
- return -EIO;
-}
-
-static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = HOST_COMPLETE,
- .host_command_sequence = 0,
- .host_command_length = 0
- };
- int err = 0;
-
- IPW_DEBUG_HC("HOST_COMPLETE\n");
-
- if (priv->status & STATUS_ENABLED)
- return 0;
-
- mutex_lock(&priv->adapter_mutex);
-
- if (rf_kill_active(priv)) {
- IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
- goto fail_up;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
- goto fail_up;
- }
-
- err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
- if (err) {
- IPW_DEBUG_INFO("%s: card not responding to init command.\n",
- priv->net_dev->name);
- goto fail_up;
- }
-
- if (priv->stop_hang_check) {
- priv->stop_hang_check = 0;
- queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
- }
-
- fail_up:
- mutex_unlock(&priv->adapter_mutex);
- return err;
-}
-
-static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
-{
-#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100))
-
- struct host_command cmd = {
- .host_command = HOST_PRE_POWER_DOWN,
- .host_command_sequence = 0,
- .host_command_length = 0,
- };
- int err, i;
- u32 reg;
-
- if (!(priv->status & STATUS_RUNNING))
- return 0;
-
- priv->status |= STATUS_STOPPING;
-
- /* We can only shut down the card if the firmware is operational. So,
- * if we haven't reset since a fatal_error, then we can not send the
- * shutdown commands. */
- if (!priv->fatal_error) {
- /* First, make sure the adapter is enabled so that the PHY_OFF
- * command can shut it down */
- ipw2100_enable_adapter(priv);
-
- err = ipw2100_hw_phy_off(priv);
- if (err)
- printk(KERN_WARNING DRV_NAME
- ": Error disabling radio %d\n", err);
-
- /*
- * If in D0-standby mode going directly to D3 may cause a
- * PCI bus violation. Therefore we must change out of the D0
- * state.
- *
- * Sending the PREPARE_FOR_POWER_DOWN will restrict the
- * hardware from going into standby mode and will transition
- * out of D0-standby if it is already in that state.
- *
- * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
- * driver upon completion. Once received, the driver can
- * proceed to the D3 state.
- *
- * Prepare for power down command to fw. This command would
- * take HW out of D0-standby and prepare it for D3 state.
- *
- * Currently FW does not support event notification for this
- * event. Therefore, skip waiting for it. Just wait a fixed
- * 100ms
- */
- IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Power down command failed: Error %d\n",
- priv->net_dev->name, err);
- else
- schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY);
- }
-
- priv->status &= ~STATUS_ENABLED;
-
- /*
- * Set GPIO 3 writable by FW; GPIO 1 writable
- * by driver and enable clock
- */
- ipw2100_hw_set_gpio(priv);
-
- /*
- * Power down adapter. Sequence:
- * 1. Stop master assert (RESET_REG[9]=1)
- * 2. Wait for stop master (RESET_REG[8]==1)
- * 3. S/w reset assert (RESET_REG[7] = 1)
- */
-
- /* Stop master assert */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
-
- /* wait stop master not more than 50 usec.
- * Otherwise return error. */
- for (i = 5; i > 0; i--) {
- udelay(10);
-
- /* Check master stop bit */
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- }
-
- if (i == 0)
- printk(KERN_WARNING DRV_NAME
- ": %s: Could now power down adapter.\n",
- priv->net_dev->name);
-
- /* assert s/w reset */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_SW_RESET);
-
- priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
-
- return 0;
-}
-
-static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = CARD_DISABLE,
- .host_command_sequence = 0,
- .host_command_length = 0
- };
- int err = 0;
-
- IPW_DEBUG_HC("CARD_DISABLE\n");
-
- if (!(priv->status & STATUS_ENABLED))
- return 0;
-
- /* Make sure we clear the associated state */
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
-
- if (!priv->stop_hang_check) {
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
- }
-
- mutex_lock(&priv->adapter_mutex);
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- ": exit - failed to send CARD_DISABLE command\n");
- goto fail_up;
- }
-
- err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- ": exit - card failed to change to DISABLED\n");
- goto fail_up;
- }
-
- IPW_DEBUG_INFO("TODO: implement scan state machine\n");
-
- fail_up:
- mutex_unlock(&priv->adapter_mutex);
- return err;
-}
-
-static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = SET_SCAN_OPTIONS,
- .host_command_sequence = 0,
- .host_command_length = 8
- };
- int err;
-
- IPW_DEBUG_INFO("enter\n");
-
- IPW_DEBUG_SCAN("setting scan options\n");
-
- cmd.host_command_parameters[0] = 0;
-
- if (!(priv->config & CFG_ASSOCIATE))
- cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
- if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled)
- cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
- if (priv->config & CFG_PASSIVE_SCAN)
- cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
-
- cmd.host_command_parameters[1] = priv->channel_mask;
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
- cmd.host_command_parameters[0]);
-
- return err;
-}
-
-static int ipw2100_start_scan(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = BROADCAST_SCAN,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- IPW_DEBUG_HC("START_SCAN\n");
-
- cmd.host_command_parameters[0] = 0;
-
- /* No scanning if in monitor mode */
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- return 1;
-
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
- return 0;
- }
-
- IPW_DEBUG_INFO("enter\n");
-
- /* Not clearing here; doing so makes iwlist always return nothing...
- *
- * We should modify the table logic to use aging tables vs. clearing
- * the table on each scan start.
- */
- IPW_DEBUG_SCAN("starting scan\n");
-
- priv->status |= STATUS_SCANNING;
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- priv->status &= ~STATUS_SCANNING;
-
- IPW_DEBUG_INFO("exit\n");
-
- return err;
-}
-
-static const struct ieee80211_geo ipw_geos[] = {
- { /* Restricted */
- "---",
- .bg_channels = 14,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}, {2484, 14}},
- },
-};
-
-static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
-{
- unsigned long flags;
- int rc = 0;
- u32 lock;
- u32 ord_len = sizeof(lock);
-
- /* Quite if manually disabled. */
- if (priv->status & STATUS_RF_KILL_SW) {
- IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
- "switch\n", priv->net_dev->name);
- return 0;
- }
-
- /* the ipw2100 hardware really doesn't want power management delays
- * longer than 175usec
- */
- pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", 175);
-
- /* If the interrupt is enabled, turn it off... */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
-
- /* Reset any fatal_error conditions */
- ipw2100_reset_fatalerror(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- if (priv->status & STATUS_POWERED ||
- (priv->status & STATUS_RESET_PENDING)) {
- /* Power cycle the card ... */
- if (ipw2100_power_cycle_adapter(priv)) {
- printk(KERN_WARNING DRV_NAME
- ": %s: Could not cycle adapter.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
- } else
- priv->status |= STATUS_POWERED;
-
- /* Load the firmware, start the clocks, etc. */
- if (ipw2100_start_adapter(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to start the firmware.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- ipw2100_initialize_ordinals(priv);
-
- /* Determine capabilities of this particular HW configuration */
- if (ipw2100_get_hw_features(priv)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to determine HW features.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- /* Initialize the geo */
- if (ieee80211_set_geo(priv->ieee, &ipw_geos[0])) {
- printk(KERN_WARNING DRV_NAME "Could not set geo\n");
- return 0;
- }
- priv->ieee->freq_band = IEEE80211_24GHZ_BAND;
-
- lock = LOCK_NONE;
- if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
- printk(KERN_ERR DRV_NAME
- ": %s: Failed to clear ordinal lock.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- priv->status &= ~STATUS_SCANNING;
-
- if (rf_kill_active(priv)) {
- printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
- priv->net_dev->name);
-
- if (priv->stop_rf_kill) {
- priv->stop_rf_kill = 0;
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- round_jiffies_relative(HZ));
- }
-
- deferred = 1;
- }
-
- /* Turn on the interrupt so that commands can be processed */
- ipw2100_enable_interrupts(priv);
-
- /* Send all of the commands that must be sent prior to
- * HOST_COMPLETE */
- if (ipw2100_adapter_setup(priv)) {
- printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
- priv->net_dev->name);
- rc = 1;
- goto exit;
- }
-
- if (!deferred) {
- /* Enable the adapter - sends HOST_COMPLETE */
- if (ipw2100_enable_adapter(priv)) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: failed in call to enable adapter.\n",
- priv->net_dev->name);
- ipw2100_hw_stop_adapter(priv);
- rc = 1;
- goto exit;
- }
-
- /* Start a scan . . . */
- ipw2100_set_scan_options(priv);
- ipw2100_start_scan(priv);
- }
-
- exit:
- return rc;
-}
-
-/* Called by register_netdev() */
-static int ipw2100_net_init(struct net_device *dev)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ipw2100_up(priv, 1);
-}
-
-static void ipw2100_down(struct ipw2100_priv *priv)
-{
- unsigned long flags;
- union iwreq_data wrqu = {
- .ap_addr = {
- .sa_family = ARPHRD_ETHER}
- };
- int associated = priv->status & STATUS_ASSOCIATED;
-
- /* Kill the RF switch timer */
- if (!priv->stop_rf_kill) {
- priv->stop_rf_kill = 1;
- cancel_delayed_work(&priv->rf_kill);
- }
-
- /* Kill the firmare hang check timer */
- if (!priv->stop_hang_check) {
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
- }
-
- /* Kill any pending resets */
- if (priv->status & STATUS_RESET_PENDING)
- cancel_delayed_work(&priv->reset_work);
-
- /* Make sure the interrupt is on so that FW commands will be
- * processed correctly */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_enable_interrupts(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- if (ipw2100_hw_stop_adapter(priv))
- printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
- priv->net_dev->name);
-
- /* Do not disable the interrupt until _after_ we disable
- * the adaptor. Otherwise the CARD_DISABLE command will never
- * be ack'd by the firmware */
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100",
- PM_QOS_DEFAULT_VALUE);
-
- /* We have to signal any supplicant if we are disassociating */
- if (associated)
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
-}
-
-static void ipw2100_reset_adapter(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, reset_work.work);
- unsigned long flags;
- union iwreq_data wrqu = {
- .ap_addr = {
- .sa_family = ARPHRD_ETHER}
- };
- int associated = priv->status & STATUS_ASSOCIATED;
-
- spin_lock_irqsave(&priv->low_lock, flags);
- IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name);
- priv->resets++;
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
- priv->status |= STATUS_SECURITY_UPDATED;
-
- /* Force a power cycle even if interface hasn't been opened
- * yet */
- cancel_delayed_work(&priv->reset_work);
- priv->status |= STATUS_RESET_PENDING;
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- mutex_lock(&priv->action_mutex);
- /* stop timed checks so that they don't interfere with reset */
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->hang_check);
-
- /* We have to signal any supplicant if we are disassociating */
- if (associated)
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-
- ipw2100_up(priv, 0);
- mutex_unlock(&priv->action_mutex);
-
-}
-
-static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
-{
-
-#define MAC_ASSOCIATION_READ_DELAY (HZ)
- int ret, len, essid_len;
- char essid[IW_ESSID_MAX_SIZE];
- u32 txrate;
- u32 chan;
- char *txratename;
- u8 bssid[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
-
- /*
- * TBD: BSSID is usually 00:00:00:00:00:00 here and not
- * an actual MAC of the AP. Seems like FW sets this
- * address too late. Read it later and expose through
- * /proc or schedule a later task to query and update
- */
-
- essid_len = IW_ESSID_MAX_SIZE;
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
- essid, &essid_len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
-
- len = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
-
- len = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
- len = ETH_ALEN;
- ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid, &len);
- if (ret) {
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
- return;
- }
- memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
-
- switch (txrate) {
- case TX_RATE_1_MBIT:
- txratename = "1Mbps";
- break;
- case TX_RATE_2_MBIT:
- txratename = "2Mbsp";
- break;
- case TX_RATE_5_5_MBIT:
- txratename = "5.5Mbps";
- break;
- case TX_RATE_11_MBIT:
- txratename = "11Mbps";
- break;
- default:
- IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
- txratename = "unknown rate";
- break;
- }
-
- IPW_DEBUG_INFO("%s: Associated with '%s' at %s, channel %d (BSSID="
- "%s)\n",
- priv->net_dev->name, escape_essid(essid, essid_len),
- txratename, chan, print_mac(mac, bssid));
-
- /* now we copy read ssid into dev */
- if (!(priv->config & CFG_STATIC_ESSID)) {
- priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE);
- memcpy(priv->essid, essid, priv->essid_len);
- }
- priv->channel = chan;
- memcpy(priv->bssid, bssid, ETH_ALEN);
-
- priv->status |= STATUS_ASSOCIATING;
- priv->connect_start = get_seconds();
-
- queue_delayed_work(priv->workqueue, &priv->wx_event_work, HZ / 10);
-}
-
-static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
- int length, int batch_mode)
-{
- int ssid_len = min(length, IW_ESSID_MAX_SIZE);
- struct host_command cmd = {
- .host_command = SSID,
- .host_command_sequence = 0,
- .host_command_length = ssid_len
- };
- int err;
-
- IPW_DEBUG_HC("SSID: '%s'\n", escape_essid(essid, ssid_len));
-
- if (ssid_len)
- memcpy(cmd.host_command_parameters, essid, ssid_len);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
- * disable auto association -- so we cheat by setting a bogus SSID */
- if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
- int i;
- u8 *bogus = (u8 *) cmd.host_command_parameters;
- for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
- bogus[i] = 0x18 + i;
- cmd.host_command_length = IW_ESSID_MAX_SIZE;
- }
-
- /* NOTE: We always send the SSID command even if the provided ESSID is
- * the same as what we currently think is set. */
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (!err) {
- memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len);
- memcpy(priv->essid, essid, ssid_len);
- priv->essid_len = ssid_len;
- }
-
- if (!batch_mode) {
- if (ipw2100_enable_adapter(priv))
- err = -EIO;
- }
-
- return err;
-}
-
-static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
-{
- DECLARE_MAC_BUF(mac);
-
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
- "disassociated: '%s' %s \n",
- escape_essid(priv->essid, priv->essid_len),
- print_mac(mac, priv->bssid));
-
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
-
- if (priv->status & STATUS_STOPPING) {
- IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
- return;
- }
-
- memset(priv->bssid, 0, ETH_ALEN);
- memset(priv->ieee->bssid, 0, ETH_ALEN);
-
- netif_carrier_off(priv->net_dev);
- netif_stop_queue(priv->net_dev);
-
- if (!(priv->status & STATUS_RUNNING))
- return;
-
- if (priv->status & STATUS_SECURITY_UPDATED)
- queue_delayed_work(priv->workqueue, &priv->security_work, 0);
-
- queue_delayed_work(priv->workqueue, &priv->wx_event_work, 0);
-}
-
-static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
- priv->net_dev->name);
-
- /* RF_KILL is now enabled (else we wouldn't be here) */
- priv->status |= STATUS_RF_KILL_HW;
-
- /* Make sure the RF Kill check timer is running */
- priv->stop_rf_kill = 0;
- cancel_delayed_work(&priv->rf_kill);
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- round_jiffies_relative(HZ));
-}
-
-static void send_scan_event(void *data)
-{
- struct ipw2100_priv *priv = data;
- union iwreq_data wrqu;
-
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
-}
-
-static void ipw2100_scan_event_later(struct work_struct *work)
-{
- send_scan_event(container_of(work, struct ipw2100_priv,
- scan_event_later.work));
-}
-
-static void ipw2100_scan_event_now(struct work_struct *work)
-{
- send_scan_event(container_of(work, struct ipw2100_priv,
- scan_event_now));
-}
-
-static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_SCAN("scan complete\n");
- /* Age the scan results... */
- priv->ieee->scans++;
- priv->status &= ~STATUS_SCANNING;
-
- /* Only userspace-requested scan completion events go out immediately */
- if (!priv->user_requested_scan) {
- if (!delayed_work_pending(&priv->scan_event_later))
- queue_delayed_work(priv->workqueue,
- &priv->scan_event_later,
- round_jiffies_relative(msecs_to_jiffies(4000)));
- } else {
- priv->user_requested_scan = 0;
- cancel_delayed_work(&priv->scan_event_later);
- queue_work(priv->workqueue, &priv->scan_event_now);
- }
-}
-
-#ifdef CONFIG_IPW2100_DEBUG
-#define IPW2100_HANDLER(v, f) { v, f, # v }
-struct ipw2100_status_indicator {
- int status;
- void (*cb) (struct ipw2100_priv * priv, u32 status);
- char *name;
-};
-#else
-#define IPW2100_HANDLER(v, f) { v, f }
-struct ipw2100_status_indicator {
- int status;
- void (*cb) (struct ipw2100_priv * priv, u32 status);
-};
-#endif /* CONFIG_IPW2100_DEBUG */
-
-static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
-{
- IPW_DEBUG_SCAN("Scanning...\n");
- priv->status |= STATUS_SCANNING;
-}
-
-static const struct ipw2100_status_indicator status_handlers[] = {
- IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL),
- IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL),
- IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
- IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
- IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL),
- IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
- IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL),
- IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL),
- IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
- IPW2100_HANDLER(IPW_STATE_DISABLED, NULL),
- IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL),
- IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
- IPW2100_HANDLER(-1, NULL)
-};
-
-static void isr_status_change(struct ipw2100_priv *priv, int status)
-{
- int i;
-
- if (status == IPW_STATE_SCANNING &&
- priv->status & STATUS_ASSOCIATED &&
- !(priv->status & STATUS_SCANNING)) {
- IPW_DEBUG_INFO("Scan detected while associated, with "
- "no scan request. Restarting firmware.\n");
-
- /* Wake up any sleeping jobs */
- schedule_reset(priv);
- }
-
- for (i = 0; status_handlers[i].status != -1; i++) {
- if (status == status_handlers[i].status) {
- IPW_DEBUG_NOTIF("Status change: %s\n",
- status_handlers[i].name);
- if (status_handlers[i].cb)
- status_handlers[i].cb(priv, status);
- priv->wstats.status = status;
- return;
- }
- }
-
- IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
-}
-
-static void isr_rx_complete_command(struct ipw2100_priv *priv,
- struct ipw2100_cmd_header *cmd)
-{
-#ifdef CONFIG_IPW2100_DEBUG
- if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
- IPW_DEBUG_HC("Command completed '%s (%d)'\n",
- command_types[cmd->host_command_reg],
- cmd->host_command_reg);
- }
-#endif
- if (cmd->host_command_reg == HOST_COMPLETE)
- priv->status |= STATUS_ENABLED;
-
- if (cmd->host_command_reg == CARD_DISABLE)
- priv->status &= ~STATUS_ENABLED;
-
- priv->status &= ~STATUS_CMD_ACTIVE;
-
- wake_up_interruptible(&priv->wait_command_queue);
-}
-
-#ifdef CONFIG_IPW2100_DEBUG
-static const char *frame_types[] = {
- "COMMAND_STATUS_VAL",
- "STATUS_CHANGE_VAL",
- "P80211_DATA_VAL",
- "P8023_DATA_VAL",
- "HOST_NOTIFICATION_VAL"
-};
-#endif
-
-static int ipw2100_alloc_skb(struct ipw2100_priv *priv,
- struct ipw2100_rx_packet *packet)
-{
- packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
- if (!packet->skb)
- return -ENOMEM;
-
- packet->rxp = (struct ipw2100_rx *)packet->skb->data;
- packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
- /* NOTE: pci_map_single does not return an error code, and 0 is a valid
- * dma_addr */
-
- return 0;
-}
-
-#define SEARCH_ERROR 0xffffffff
-#define SEARCH_FAIL 0xfffffffe
-#define SEARCH_SUCCESS 0xfffffff0
-#define SEARCH_DISCARD 0
-#define SEARCH_SNAPSHOT 1
-
-#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
-static void ipw2100_snapshot_free(struct ipw2100_priv *priv)
-{
- int i;
- if (!priv->snapshot[0])
- return;
- for (i = 0; i < 0x30; i++)
- kfree(priv->snapshot[i]);
- priv->snapshot[0] = NULL;
-}
-
-#ifdef IPW2100_DEBUG_C3
-static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
-{
- int i;
- if (priv->snapshot[0])
- return 1;
- for (i = 0; i < 0x30; i++) {
- priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
- if (!priv->snapshot[i]) {
- IPW_DEBUG_INFO("%s: Error allocating snapshot "
- "buffer %d\n", priv->net_dev->name, i);
- while (i > 0)
- kfree(priv->snapshot[--i]);
- priv->snapshot[0] = NULL;
- return 0;
- }
- }
-
- return 1;
-}
-
-static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf,
- size_t len, int mode)
-{
- u32 i, j;
- u32 tmp;
- u8 *s, *d;
- u32 ret;
-
- s = in_buf;
- if (mode == SEARCH_SNAPSHOT) {
- if (!ipw2100_snapshot_alloc(priv))
- mode = SEARCH_DISCARD;
- }
-
- for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
- read_nic_dword(priv->net_dev, i, &tmp);
- if (mode == SEARCH_SNAPSHOT)
- *(u32 *) SNAPSHOT_ADDR(i) = tmp;
- if (ret == SEARCH_FAIL) {
- d = (u8 *) & tmp;
- for (j = 0; j < 4; j++) {
- if (*s != *d) {
- s = in_buf;
- continue;
- }
-
- s++;
- d++;
-
- if ((s - in_buf) == len)
- ret = (i + j) - len + 1;
- }
- } else if (mode == SEARCH_DISCARD)
- return ret;
- }
-
- return ret;
-}
-#endif
-
-/*
- *
- * 0) Disconnect the SKB from the firmware (just unmap)
- * 1) Pack the ETH header into the SKB
- * 2) Pass the SKB to the network stack
- *
- * When packet is provided by the firmware, it contains the following:
- *
- * . ieee80211_hdr
- * . ieee80211_snap_hdr
- *
- * The size of the constructed ethernet
- *
- */
-#ifdef IPW2100_RX_DEBUG
-static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
-#endif
-
-static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i)
-{
-#ifdef IPW2100_DEBUG_C3
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- u32 match, reg;
- int j;
-#endif
-
- IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n",
- i * sizeof(struct ipw2100_status));
-
-#ifdef IPW2100_DEBUG_C3
- /* Halt the fimrware so we can get a good image */
- write_register(priv->net_dev, IPW_REG_RESET_REG,
- IPW_AUX_HOST_RESET_REG_STOP_MASTER);
- j = 5;
- do {
- udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
- read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
-
- if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
- break;
- } while (j--);
-
- match = ipw2100_match_buf(priv, (u8 *) status,
- sizeof(struct ipw2100_status),
- SEARCH_SNAPSHOT);
- if (match < SEARCH_SUCCESS)
- IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
- "offset 0x%06X, length %d:\n",
- priv->net_dev->name, match,
- sizeof(struct ipw2100_status));
- else
- IPW_DEBUG_INFO("%s: No DMA status match in "
- "Firmware.\n", priv->net_dev->name);
-
- printk_buf((u8 *) priv->status_queue.drv,
- sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
-#endif
-
- priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
- priv->ieee->stats.rx_errors++;
- schedule_reset(priv);
-}
-
-static void isr_rx(struct ipw2100_priv *priv, int i,
- struct ieee80211_rx_stats *stats)
-{
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- IPW_DEBUG_RX("Handler...\n");
-
- if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
- IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
- " Dropping.\n",
- priv->net_dev->name,
- status->frame_size, skb_tailroom(packet->skb));
- priv->ieee->stats.rx_errors++;
- return;
- }
-
- if (unlikely(!netif_running(priv->net_dev))) {
- priv->ieee->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
- !(priv->status & STATUS_ASSOCIATED))) {
- IPW_DEBUG_DROP("Dropping packet while not associated.\n");
- priv->wstats.discard.misc++;
- return;
- }
-
- pci_unmap_single(priv->pci_dev,
- packet->dma_addr,
- sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
-
- skb_put(packet->skb, status->frame_size);
-
-#ifdef IPW2100_RX_DEBUG
- /* Make a copy of the frame so we can dump it to the logs if
- * ieee80211_rx fails */
- skb_copy_from_linear_data(packet->skb, packet_data,
- min_t(u32, status->frame_size,
- IPW_RX_NIC_BUFFER_LENGTH));
-#endif
-
- if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
-#ifdef IPW2100_RX_DEBUG
- IPW_DEBUG_DROP("%s: Non consumed packet:\n",
- priv->net_dev->name);
- printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
-#endif
- priv->ieee->stats.rx_errors++;
-
- /* ieee80211_rx failed, so it didn't free the SKB */
- dev_kfree_skb_any(packet->skb);
- packet->skb = NULL;
- }
-
- /* We need to allocate a new SKB and attach it to the RDB. */
- if (unlikely(ipw2100_alloc_skb(priv, packet))) {
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Unable to allocate SKB onto RBD ring - disabling "
- "adapter.\n", priv->net_dev->name);
- /* TODO: schedule adapter shutdown */
- IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
- }
-
- /* Update the RDB entry */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
-}
-
-#ifdef CONFIG_IPW2100_MONITOR
-
-static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
- struct ieee80211_rx_stats *stats)
-{
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- /* Magic struct that slots into the radiotap header -- no reason
- * to build this manually element by element, we can write it much
- * more efficiently than we can parse it. ORDER MATTERS HERE */
- struct ipw_rt_hdr {
- struct ieee80211_radiotap_header rt_hdr;
- s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
- } *ipw_rt;
-
- IPW_DEBUG_RX("Handler...\n");
-
- if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
- sizeof(struct ipw_rt_hdr))) {
- IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
- " Dropping.\n",
- priv->net_dev->name,
- status->frame_size,
- skb_tailroom(packet->skb));
- priv->ieee->stats.rx_errors++;
- return;
- }
-
- if (unlikely(!netif_running(priv->net_dev))) {
- priv->ieee->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- if (unlikely(priv->config & CFG_CRC_CHECK &&
- status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
- IPW_DEBUG_RX("CRC error in packet. Dropping.\n");
- priv->ieee->stats.rx_errors++;
- return;
- }
-
- pci_unmap_single(priv->pci_dev, packet->dma_addr,
- sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
- memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
- packet->skb->data, status->frame_size);
-
- ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
-
- ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
- ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
- ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */
-
- ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
-
- ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
-
- skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
-
- if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
- priv->ieee->stats.rx_errors++;
-
- /* ieee80211_rx failed, so it didn't free the SKB */
- dev_kfree_skb_any(packet->skb);
- packet->skb = NULL;
- }
-
- /* We need to allocate a new SKB and attach it to the RDB. */
- if (unlikely(ipw2100_alloc_skb(priv, packet))) {
- IPW_DEBUG_WARNING(
- "%s: Unable to allocate SKB onto RBD ring - disabling "
- "adapter.\n", priv->net_dev->name);
- /* TODO: schedule adapter shutdown */
- IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
- }
-
- /* Update the RDB entry */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
-}
-
-#endif
-
-static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
-{
- struct ipw2100_status *status = &priv->status_queue.drv[i];
- struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
- u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
-
- switch (frame_type) {
- case COMMAND_STATUS_VAL:
- return (status->frame_size != sizeof(u->rx_data.command));
- case STATUS_CHANGE_VAL:
- return (status->frame_size != sizeof(u->rx_data.status));
- case HOST_NOTIFICATION_VAL:
- return (status->frame_size < sizeof(u->rx_data.notification));
- case P80211_DATA_VAL:
- case P8023_DATA_VAL:
-#ifdef CONFIG_IPW2100_MONITOR
- return 0;
-#else
- switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
- case IEEE80211_FTYPE_MGMT:
- case IEEE80211_FTYPE_CTL:
- return 0;
- case IEEE80211_FTYPE_DATA:
- return (status->frame_size >
- IPW_MAX_802_11_PAYLOAD_LENGTH);
- }
-#endif
- }
-
- return 1;
-}
-
-/*
- * ipw2100 interrupts are disabled at this point, and the ISR
- * is the only code that calls this method. So, we do not need
- * to play with any locks.
- *
- * RX Queue works as follows:
- *
- * Read index - firmware places packet in entry identified by the
- * Read index and advances Read index. In this manner,
- * Read index will always point to the next packet to
- * be filled--but not yet valid.
- *
- * Write index - driver fills this entry with an unused RBD entry.
- * This entry has not filled by the firmware yet.
- *
- * In between the W and R indexes are the RBDs that have been received
- * but not yet processed.
- *
- * The process of handling packets will start at WRITE + 1 and advance
- * until it reaches the READ index.
- *
- * The WRITE index is cached in the variable 'priv->rx_queue.next'.
- *
- */
-static void __ipw2100_rx_process(struct ipw2100_priv *priv)
-{
- struct ipw2100_bd_queue *rxq = &priv->rx_queue;
- struct ipw2100_status_queue *sq = &priv->status_queue;
- struct ipw2100_rx_packet *packet;
- u16 frame_type;
- u32 r, w, i, s;
- struct ipw2100_rx *u;
- struct ieee80211_rx_stats stats = {
- .mac_time = jiffies,
- };
-
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
-
- if (r >= rxq->entries) {
- IPW_DEBUG_RX("exit - bad read index\n");
- return;
- }
-
- i = (rxq->next + 1) % rxq->entries;
- s = i;
- while (i != r) {
- /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
- r, rxq->next, i); */
-
- packet = &priv->rx_buffers[i];
-
- /* Sync the DMA for the STATUS buffer so CPU is sure to get
- * the correct values */
- pci_dma_sync_single_for_cpu(priv->pci_dev,
- sq->nic +
- sizeof(struct ipw2100_status) * i,
- sizeof(struct ipw2100_status),
- PCI_DMA_FROMDEVICE);
-
- /* Sync the DMA for the RX buffer so CPU is sure to get
- * the correct values */
- pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
-
- if (unlikely(ipw2100_corruption_check(priv, i))) {
- ipw2100_corruption_detected(priv, i);
- goto increment;
- }
-
- u = packet->rxp;
- frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK;
- stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
- stats.len = sq->drv[i].frame_size;
-
- stats.mask = 0;
- if (stats.rssi != 0)
- stats.mask |= IEEE80211_STATMASK_RSSI;
- stats.freq = IEEE80211_24GHZ_BAND;
-
- IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n",
- priv->net_dev->name, frame_types[frame_type],
- stats.len);
-
- switch (frame_type) {
- case COMMAND_STATUS_VAL:
- /* Reset Rx watchdog */
- isr_rx_complete_command(priv, &u->rx_data.command);
- break;
-
- case STATUS_CHANGE_VAL:
- isr_status_change(priv, u->rx_data.status);
- break;
-
- case P80211_DATA_VAL:
- case P8023_DATA_VAL:
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- isr_rx_monitor(priv, i, &stats);
- break;
- }
-#endif
- if (stats.len < sizeof(struct ieee80211_hdr_3addr))
- break;
- switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
- case IEEE80211_FTYPE_MGMT:
- ieee80211_rx_mgt(priv->ieee,
- &u->rx_data.header, &stats);
- break;
-
- case IEEE80211_FTYPE_CTL:
- break;
-
- case IEEE80211_FTYPE_DATA:
- isr_rx(priv, i, &stats);
- break;
-
- }
- break;
- }
-
- increment:
- /* clear status field associated with this RBD */
- rxq->drv[i].status.info.field = 0;
-
- i = (i + 1) % rxq->entries;
- }
-
- if (i != s) {
- /* backtrack one entry, wrapping to end if at 0 */
- rxq->next = (i ? i : rxq->entries) - 1;
-
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next);
- }
-}
-
-/*
- * __ipw2100_tx_process
- *
- * This routine will determine whether the next packet on
- * the fw_pend_list has been processed by the firmware yet.
- *
- * If not, then it does nothing and returns.
- *
- * If so, then it removes the item from the fw_pend_list, frees
- * any associated storage, and places the item back on the
- * free list of its source (either msg_free_list or tx_free_list)
- *
- * TX Queue works as follows:
- *
- * Read index - points to the next TBD that the firmware will
- * process. The firmware will read the data, and once
- * done processing, it will advance the Read index.
- *
- * Write index - driver fills this entry with an constructed TBD
- * entry. The Write index is not advanced until the
- * packet has been configured.
- *
- * In between the W and R indexes are the TBDs that have NOT been
- * processed. Lagging behind the R index are packets that have
- * been processed but have not been freed by the driver.
- *
- * In order to free old storage, an internal index will be maintained
- * that points to the next packet to be freed. When all used
- * packets have been freed, the oldest index will be the same as the
- * firmware's read index.
- *
- * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
- *
- * Because the TBD structure can not contain arbitrary data, the
- * driver must keep an internal queue of cached allocations such that
- * it can put that data back into the tx_free_list and msg_free_list
- * for use by future command and data packets.
- *
- */
-static int __ipw2100_tx_process(struct ipw2100_priv *priv)
-{
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- int descriptors_used;
- int e, i;
- u32 r, w, frag_num = 0;
-
- if (list_empty(&priv->fw_pend_list))
- return 0;
-
- element = priv->fw_pend_list.next;
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
- tbd = &txq->drv[packet->index];
-
- /* Determine how many TBD entries must be finished... */
- switch (packet->type) {
- case COMMAND:
- /* COMMAND uses only one slot; don't advance */
- descriptors_used = 1;
- e = txq->oldest;
- break;
-
- case DATA:
- /* DATA uses two slots; advance and loop position. */
- descriptors_used = tbd->num_fragments;
- frag_num = tbd->num_fragments - 1;
- e = txq->oldest + frag_num;
- e %= txq->entries;
- break;
-
- default:
- printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
- priv->net_dev->name);
- return 0;
- }
-
- /* if the last TBD is not done by NIC yet, then packet is
- * not ready to be released.
- *
- */
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
- &r);
- read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- &w);
- if (w != txq->next)
- printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
- priv->net_dev->name);
-
- /*
- * txq->next is the index of the last packet written txq->oldest is
- * the index of the r is the index of the next packet to be read by
- * firmware
- */
-
- /*
- * Quick graphic to help you visualize the following
- * if / else statement
- *
- * ===>| s---->|===============
- * e>|
- * | a | b | c | d | e | f | g | h | i | j | k | l
- * r---->|
- * w
- *
- * w - updated by driver
- * r - updated by firmware
- * s - start of oldest BD entry (txq->oldest)
- * e - end of oldest BD entry
- *
- */
- if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
- IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
- return 0;
- }
-
- list_del(element);
- DEC_STAT(&priv->fw_pend_stat);
-
-#ifdef CONFIG_IPW2100_DEBUG
- {
- int i = txq->oldest;
- IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
- &txq->drv[i],
- (u32) (txq->nic + i * sizeof(struct ipw2100_bd)),
- txq->drv[i].host_addr, txq->drv[i].buf_length);
-
- if (packet->type == DATA) {
- i = (i + 1) % txq->entries;
-
- IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
- &txq->drv[i],
- (u32) (txq->nic + i *
- sizeof(struct ipw2100_bd)),
- (u32) txq->drv[i].host_addr,
- txq->drv[i].buf_length);
- }
- }
-#endif
-
- switch (packet->type) {
- case DATA:
- if (txq->drv[txq->oldest].status.info.fields.txType != 0)
- printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
- "Expecting DATA TBD but pulled "
- "something else: ids %d=%d.\n",
- priv->net_dev->name, txq->oldest, packet->index);
-
- /* DATA packet; we have to unmap and free the SKB */
- for (i = 0; i < frag_num; i++) {
- tbd = &txq->drv[(packet->index + 1 + i) % txq->entries];
-
- IPW_DEBUG_TX("TX%d P=%08x L=%d\n",
- (packet->index + 1 + i) % txq->entries,
- tbd->host_addr, tbd->buf_length);
-
- pci_unmap_single(priv->pci_dev,
- tbd->host_addr,
- tbd->buf_length, PCI_DMA_TODEVICE);
- }
-
- ieee80211_txb_free(packet->info.d_struct.txb);
- packet->info.d_struct.txb = NULL;
-
- list_add_tail(element, &priv->tx_free_list);
- INC_STAT(&priv->tx_free_stat);
-
- /* We have a free slot in the Tx queue, so wake up the
- * transmit layer if it is stopped. */
- if (priv->status & STATUS_ASSOCIATED)
- netif_wake_queue(priv->net_dev);
-
- /* A packet was processed by the hardware, so update the
- * watchdog */
- priv->net_dev->trans_start = jiffies;
-
- break;
-
- case COMMAND:
- if (txq->drv[txq->oldest].status.info.fields.txType != 1)
- printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
- "Expecting COMMAND TBD but pulled "
- "something else: ids %d=%d.\n",
- priv->net_dev->name, txq->oldest, packet->index);
-
-#ifdef CONFIG_IPW2100_DEBUG
- if (packet->info.c_struct.cmd->host_command_reg <
- ARRAY_SIZE(command_types))
- IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n",
- command_types[packet->info.c_struct.cmd->
- host_command_reg],
- packet->info.c_struct.cmd->
- host_command_reg,
- packet->info.c_struct.cmd->cmd_status_reg);
-#endif
-
- list_add_tail(element, &priv->msg_free_list);
- INC_STAT(&priv->msg_free_stat);
- break;
- }
-
- /* advance oldest used TBD pointer to start of next entry */
- txq->oldest = (e + 1) % txq->entries;
- /* increase available TBDs number */
- txq->available += descriptors_used;
- SET_STAT(&priv->txq_stat, txq->available);
-
- IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n",
- jiffies - packet->jiffy_start);
-
- return (!list_empty(&priv->fw_pend_list));
-}
-
-static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
-{
- int i = 0;
-
- while (__ipw2100_tx_process(priv) && i < 200)
- i++;
-
- if (i == 200) {
- printk(KERN_WARNING DRV_NAME ": "
- "%s: Driver is running slow (%d iters).\n",
- priv->net_dev->name, i);
- }
-}
-
-static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- int next = txq->next;
-
- while (!list_empty(&priv->msg_pend_list)) {
- /* if there isn't enough space in TBD queue, then
- * don't stuff a new one in.
- * NOTE: 3 are needed as a command will take one,
- * and there is a minimum of 2 that must be
- * maintained between the r and w indexes
- */
- if (txq->available <= 3) {
- IPW_DEBUG_TX("no room in tx_queue\n");
- break;
- }
-
- element = priv->msg_pend_list.next;
- list_del(element);
- DEC_STAT(&priv->msg_pend_stat);
-
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- IPW_DEBUG_TX("using TBD at virt=%p, phys=%p\n",
- &txq->drv[txq->next],
- (void *)(txq->nic + txq->next *
- sizeof(struct ipw2100_bd)));
-
- packet->index = txq->next;
-
- tbd = &txq->drv[txq->next];
-
- /* initialize TBD */
- tbd->host_addr = packet->info.c_struct.cmd_phys;
- tbd->buf_length = sizeof(struct ipw2100_cmd_header);
- /* not marking number of fragments causes problems
- * with f/w debug version */
- tbd->num_fragments = 1;
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_COMMAND |
- IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
-
- /* update TBD queue counters */
- txq->next++;
- txq->next %= txq->entries;
- txq->available--;
- DEC_STAT(&priv->txq_stat);
-
- list_add_tail(element, &priv->fw_pend_list);
- INC_STAT(&priv->fw_pend_stat);
- }
-
- if (txq->next != next) {
- /* kick off the DMA by notifying firmware the
- * write index has moved; make sure TBD stores are sync'd */
- wmb();
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- txq->next);
- }
-}
-
-/*
- * ipw2100_tx_send_data
- *
- */
-static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
-{
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- struct ipw2100_bd_queue *txq = &priv->tx_queue;
- struct ipw2100_bd *tbd;
- int next = txq->next;
- int i = 0;
- struct ipw2100_data_header *ipw_hdr;
- struct ieee80211_hdr_3addr *hdr;
-
- while (!list_empty(&priv->tx_pend_list)) {
- /* if there isn't enough space in TBD queue, then
- * don't stuff a new one in.
- * NOTE: 4 are needed as a data will take two,
- * and there is a minimum of 2 that must be
- * maintained between the r and w indexes
- */
- element = priv->tx_pend_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
- IPW_MAX_BDS)) {
- /* TODO: Support merging buffers if more than
- * IPW_MAX_BDS are used */
- IPW_DEBUG_INFO("%s: Maximum BD theshold exceeded. "
- "Increase fragmentation level.\n",
- priv->net_dev->name);
- }
-
- if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) {
- IPW_DEBUG_TX("no room in tx_queue\n");
- break;
- }
-
- list_del(element);
- DEC_STAT(&priv->tx_pend_stat);
-
- tbd = &txq->drv[txq->next];
-
- packet->index = txq->next;
-
- ipw_hdr = packet->info.d_struct.data;
- hdr = (struct ieee80211_hdr_3addr *)packet->info.d_struct.txb->
- fragments[0]->data;
-
- if (priv->ieee->iw_mode == IW_MODE_INFRA) {
- /* To DS: Addr1 = BSSID, Addr2 = SA,
- Addr3 = DA */
- memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
- memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
- } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- /* not From/To DS: Addr1 = DA, Addr2 = SA,
- Addr3 = BSSID */
- memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
- memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
- }
-
- ipw_hdr->host_command_reg = SEND;
- ipw_hdr->host_command_reg1 = 0;
-
- /* For now we only support host based encryption */
- ipw_hdr->needs_encryption = 0;
- ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
- if (packet->info.d_struct.txb->nr_frags > 1)
- ipw_hdr->fragment_size =
- packet->info.d_struct.txb->frag_size -
- IEEE80211_3ADDR_LEN;
- else
- ipw_hdr->fragment_size = 0;
-
- tbd->host_addr = packet->info.d_struct.data_phys;
- tbd->buf_length = sizeof(struct ipw2100_data_header);
- tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
- txq->next++;
- txq->next %= txq->entries;
-
- IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n",
- packet->index, tbd->host_addr, tbd->buf_length);
-#ifdef CONFIG_IPW2100_DEBUG
- if (packet->info.d_struct.txb->nr_frags > 1)
- IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
- packet->info.d_struct.txb->nr_frags);
-#endif
-
- for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
- tbd = &txq->drv[txq->next];
- if (i == packet->info.d_struct.txb->nr_frags - 1)
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
- else
- tbd->status.info.field =
- IPW_BD_STATUS_TX_FRAME_802_3 |
- IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
-
- tbd->buf_length = packet->info.d_struct.txb->
- fragments[i]->len - IEEE80211_3ADDR_LEN;
-
- tbd->host_addr = pci_map_single(priv->pci_dev,
- packet->info.d_struct.
- txb->fragments[i]->
- data +
- IEEE80211_3ADDR_LEN,
- tbd->buf_length,
- PCI_DMA_TODEVICE);
-
- IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
- txq->next, tbd->host_addr,
- tbd->buf_length);
-
- pci_dma_sync_single_for_device(priv->pci_dev,
- tbd->host_addr,
- tbd->buf_length,
- PCI_DMA_TODEVICE);
-
- txq->next++;
- txq->next %= txq->entries;
- }
-
- txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
- SET_STAT(&priv->txq_stat, txq->available);
-
- list_add_tail(element, &priv->fw_pend_list);
- INC_STAT(&priv->fw_pend_stat);
- }
-
- if (txq->next != next) {
- /* kick off the DMA by notifying firmware the
- * write index has moved; make sure TBD stores are sync'd */
- write_register(priv->net_dev,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
- txq->next);
- }
- return;
-}
-
-static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
-{
- struct net_device *dev = priv->net_dev;
- unsigned long flags;
- u32 inta, tmp;
-
- spin_lock_irqsave(&priv->low_lock, flags);
- ipw2100_disable_interrupts(priv);
-
- read_register(dev, IPW_REG_INTA, &inta);
-
- IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
- (unsigned long)inta & IPW_INTERRUPT_MASK);
-
- priv->in_isr++;
- priv->interrupts++;
-
- /* We do not loop and keep polling for more interrupts as this
- * is frowned upon and doesn't play nicely with other potentially
- * chained IRQs */
- IPW_DEBUG_ISR("INTA: 0x%08lX\n",
- (unsigned long)inta & IPW_INTERRUPT_MASK);
-
- if (inta & IPW2100_INTA_FATAL_ERROR) {
- printk(KERN_WARNING DRV_NAME
- ": Fatal interrupt. Scheduling firmware restart.\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR);
-
- read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
- IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
- priv->net_dev->name, priv->fatal_error);
-
- read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
- IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
- priv->net_dev->name, tmp);
-
- /* Wake up any sleeping jobs */
- schedule_reset(priv);
- }
-
- if (inta & IPW2100_INTA_PARITY_ERROR) {
- printk(KERN_ERR DRV_NAME
- ": ***** PARITY ERROR INTERRUPT !!!! \n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR);
- }
-
- if (inta & IPW2100_INTA_RX_TRANSFER) {
- IPW_DEBUG_ISR("RX interrupt\n");
-
- priv->rx_interrupts++;
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER);
-
- __ipw2100_rx_process(priv);
- __ipw2100_tx_complete(priv);
- }
-
- if (inta & IPW2100_INTA_TX_TRANSFER) {
- IPW_DEBUG_ISR("TX interrupt\n");
-
- priv->tx_interrupts++;
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER);
-
- __ipw2100_tx_complete(priv);
- ipw2100_tx_send_commands(priv);
- ipw2100_tx_send_data(priv);
- }
-
- if (inta & IPW2100_INTA_TX_COMPLETE) {
- IPW_DEBUG_ISR("TX complete\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE);
-
- __ipw2100_tx_complete(priv);
- }
-
- if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
- /* ipw2100_handle_event(dev); */
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT);
- }
-
- if (inta & IPW2100_INTA_FW_INIT_DONE) {
- IPW_DEBUG_ISR("FW init done interrupt\n");
- priv->inta_other++;
-
- read_register(dev, IPW_REG_INTA, &tmp);
- if (tmp & (IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR)) {
- write_register(dev, IPW_REG_INTA,
- IPW2100_INTA_FATAL_ERROR |
- IPW2100_INTA_PARITY_ERROR);
- }
-
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE);
- }
-
- if (inta & IPW2100_INTA_STATUS_CHANGE) {
- IPW_DEBUG_ISR("Status change interrupt\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE);
- }
-
- if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
- IPW_DEBUG_ISR("slave host mode interrupt\n");
- priv->inta_other++;
- write_register(dev, IPW_REG_INTA,
- IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
- }
-
- priv->in_isr--;
- ipw2100_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- IPW_DEBUG_ISR("exit\n");
-}
-
-static irqreturn_t ipw2100_interrupt(int irq, void *data)
-{
- struct ipw2100_priv *priv = data;
- u32 inta, inta_mask;
-
- if (!data)
- return IRQ_NONE;
-
- spin_lock(&priv->low_lock);
-
- /* We check to see if we should be ignoring interrupts before
- * we touch the hardware. During ucode load if we try and handle
- * an interrupt we can cause keyboard problems as well as cause
- * the ucode to fail to initialize */
- if (!(priv->status & STATUS_INT_ENABLED)) {
- /* Shared IRQ */
- goto none;
- }
-
- read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
- read_register(priv->net_dev, IPW_REG_INTA, &inta);
-
- if (inta == 0xFFFFFFFF) {
- /* Hardware disappeared */
- printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
- goto none;
- }
-
- inta &= IPW_INTERRUPT_MASK;
-
- if (!(inta & inta_mask)) {
- /* Shared interrupt */
- goto none;
- }
-
- /* We disable the hardware interrupt here just to prevent unneeded
- * calls to be made. We disable this again within the actual
- * work tasklet, so if another part of the code re-enables the
- * interrupt, that is fine */
- ipw2100_disable_interrupts(priv);
-
- tasklet_schedule(&priv->irq_tasklet);
- spin_unlock(&priv->low_lock);
-
- return IRQ_HANDLED;
- none:
- spin_unlock(&priv->low_lock);
- return IRQ_NONE;
-}
-
-static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev,
- int pri)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- IPW_DEBUG_INFO("Can not transmit when not connected.\n");
- priv->ieee->stats.tx_carrier_errors++;
- netif_stop_queue(dev);
- goto fail_unlock;
- }
-
- if (list_empty(&priv->tx_free_list))
- goto fail_unlock;
-
- element = priv->tx_free_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- packet->info.d_struct.txb = txb;
-
- IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len);
- printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len);
-
- packet->jiffy_start = jiffies;
-
- list_del(element);
- DEC_STAT(&priv->tx_free_stat);
-
- list_add_tail(element, &priv->tx_pend_list);
- INC_STAT(&priv->tx_pend_stat);
-
- ipw2100_tx_send_data(priv);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
- return 0;
-
- fail_unlock:
- netif_stop_queue(dev);
- spin_unlock_irqrestore(&priv->low_lock, flags);
- return 1;
-}
-
-static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
- void *v;
- dma_addr_t p;
-
- priv->msg_buffers =
- (struct ipw2100_tx_packet *)kmalloc(IPW_COMMAND_POOL_SIZE *
- sizeof(struct
- ipw2100_tx_packet),
- GFP_KERNEL);
- if (!priv->msg_buffers) {
- printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for msg "
- "buffers.\n", priv->net_dev->name);
- return -ENOMEM;
- }
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
- v = pci_alloc_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header), &p);
- if (!v) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: PCI alloc failed for msg "
- "buffers.\n", priv->net_dev->name);
- err = -ENOMEM;
- break;
- }
-
- memset(v, 0, sizeof(struct ipw2100_cmd_header));
-
- priv->msg_buffers[i].type = COMMAND;
- priv->msg_buffers[i].info.c_struct.cmd =
- (struct ipw2100_cmd_header *)v;
- priv->msg_buffers[i].info.c_struct.cmd_phys = p;
- }
-
- if (i == IPW_COMMAND_POOL_SIZE)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header),
- priv->msg_buffers[j].info.c_struct.cmd,
- priv->msg_buffers[j].info.c_struct.
- cmd_phys);
- }
-
- kfree(priv->msg_buffers);
- priv->msg_buffers = NULL;
-
- return err;
-}
-
-static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
-{
- int i;
-
- INIT_LIST_HEAD(&priv->msg_free_list);
- INIT_LIST_HEAD(&priv->msg_pend_list);
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
- list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
- SET_STAT(&priv->msg_free_stat, i);
-
- return 0;
-}
-
-static void ipw2100_msg_free(struct ipw2100_priv *priv)
-{
- int i;
-
- if (!priv->msg_buffers)
- return;
-
- for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_cmd_header),
- priv->msg_buffers[i].info.c_struct.cmd,
- priv->msg_buffers[i].info.c_struct.
- cmd_phys);
- }
-
- kfree(priv->msg_buffers);
- priv->msg_buffers = NULL;
-}
-
-static ssize_t show_pci(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
- char *out = buf;
- int i, j;
- u32 val;
-
- for (i = 0; i < 16; i++) {
- out += sprintf(out, "[%08X] ", i * 16);
- for (j = 0; j < 16; j += 4) {
- pci_read_config_dword(pci_dev, i * 16 + j, &val);
- out += sprintf(out, "%08X ", val);
- }
- out += sprintf(out, "\n");
- }
-
- return out - buf;
-}
-
-static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
-
-static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = d->driver_data;
- return sprintf(buf, "0x%08x\n", (int)p->config);
-}
-
-static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
-
-static ssize_t show_status(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = d->driver_data;
- return sprintf(buf, "0x%08x\n", (int)p->status);
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
-
-static ssize_t show_capability(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *p = d->driver_data;
- return sprintf(buf, "0x%08x\n", (int)p->capability);
-}
-
-static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
-
-#define IPW2100_REG(x) { IPW_ ##x, #x }
-static const struct {
- u32 addr;
- const char *name;
-} hw_data[] = {
-IPW2100_REG(REG_GP_CNTRL),
- IPW2100_REG(REG_GPIO),
- IPW2100_REG(REG_INTA),
- IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),};
-#define IPW2100_NIC(x, s) { x, #x, s }
-static const struct {
- u32 addr;
- const char *name;
- size_t size;
-} nic_data[] = {
-IPW2100_NIC(IPW2100_CONTROL_REG, 2),
- IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),};
-#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
-static const struct {
- u8 index;
- const char *name;
- const char *desc;
-} ord_data[] = {
-IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_HOST_COMPLETE,
- "successful Host Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_DIR_DATA,
- "successful Directed Tx's (MSDU)"),
- IPW2100_ORD(STAT_TX_DIR_DATA1,
- "successful Directed Tx's (MSDU) @ 1MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA2,
- "successful Directed Tx's (MSDU) @ 2MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA5_5,
- "successful Directed Tx's (MSDU) @ 5_5MB"),
- IPW2100_ORD(STAT_TX_DIR_DATA11,
- "successful Directed Tx's (MSDU) @ 11MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA1,
- "successful Non_Directed Tx's (MSDU) @ 1MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA2,
- "successful Non_Directed Tx's (MSDU) @ 2MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA5_5,
- "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
- IPW2100_ORD(STAT_TX_NODIR_DATA11,
- "successful Non_Directed Tx's (MSDU) @ 11MB"),
- IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
- IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
- IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
- IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
- IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
- IPW2100_ORD(STAT_TX_ASSN_RESP,
- "successful Association response Tx's"),
- IPW2100_ORD(STAT_TX_REASSN,
- "successful Reassociation Tx's"),
- IPW2100_ORD(STAT_TX_REASSN_RESP,
- "successful Reassociation response Tx's"),
- IPW2100_ORD(STAT_TX_PROBE,
- "probes successfully transmitted"),
- IPW2100_ORD(STAT_TX_PROBE_RESP,
- "probe responses successfully transmitted"),
- IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
- IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
- IPW2100_ORD(STAT_TX_DISASSN,
- "successful Disassociation TX"),
- IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
- IPW2100_ORD(STAT_TX_DEAUTH,
- "successful Deauthentication TX"),
- IPW2100_ORD(STAT_TX_TOTAL_BYTES,
- "Total successful Tx data bytes"),
- IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
- IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
- IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
- IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
- IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
- IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
- IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,
- "times max tries in a hop failed"),
- IPW2100_ORD(STAT_TX_DISASSN_FAIL,
- "times disassociation failed"),
- IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
- IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
- IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
- IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
- IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA5_5,
- "directed packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"),
- IPW2100_ORD(STAT_RX_NODIR_DATA1,
- "nondirected packets at 1MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA2,
- "nondirected packets at 2MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA5_5,
- "nondirected packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_NODIR_DATA11,
- "nondirected packets at 11MB"),
- IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
- IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS,
- "Rx CTS"),
- IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
- IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
- IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
- IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
- IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
- IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
- IPW2100_ORD(STAT_RX_REASSN_RESP,
- "Reassociation response Rx's"),
- IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
- IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
- IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
- IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
- IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
- IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
- IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
- IPW2100_ORD(STAT_RX_TOTAL_BYTES,
- "Total rx data bytes received"),
- IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
- IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
- IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE1,
- "duplicate rx packets at 1MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE2,
- "duplicate rx packets at 2MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE5_5,
- "duplicate rx packets at 5.5MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE11,
- "duplicate rx packets at 11MB"),
- IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
- IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"),
- IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"),
- IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"),
- IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,
- "rx frames with invalid protocol"),
- IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
- IPW2100_ORD(STAT_RX_NO_BUFFER,
- "rx frames rejected due to no buffer"),
- IPW2100_ORD(STAT_RX_MISSING_FRAG,
- "rx frames dropped due to missing fragment"),
- IPW2100_ORD(STAT_RX_ORPHAN_FRAG,
- "rx frames dropped due to non-sequential fragment"),
- IPW2100_ORD(STAT_RX_ORPHAN_FRAME,
- "rx frames dropped due to unmatched 1st frame"),
- IPW2100_ORD(STAT_RX_FRAG_AGEOUT,
- "rx frames dropped due to uncompleted frame"),
- IPW2100_ORD(STAT_RX_ICV_ERRORS,
- "ICV errors during decryption"),
- IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"),
- IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
- IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,
- "poll response timeouts"),
- IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT,
- "timeouts waiting for last {broad,multi}cast pkt"),
- IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
- IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
- IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"),
- IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
- IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,
- "current calculation of % missed beacons"),
- IPW2100_ORD(STAT_PERCENT_RETRIES,
- "current calculation of % missed tx retries"),
- IPW2100_ORD(ASSOCIATED_AP_PTR,
- "0 if not associated, else pointer to AP table entry"),
- IPW2100_ORD(AVAILABLE_AP_CNT,
- "AP's decsribed in the AP table"),
- IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
- IPW2100_ORD(STAT_AP_ASSNS, "associations"),
- IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
- IPW2100_ORD(STAT_ASSN_RESP_FAIL,
- "failures due to response fail"),
- IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
- IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
- IPW2100_ORD(STAT_ROAM_INHIBIT,
- "times roaming was inhibited due to activity"),
- IPW2100_ORD(RSSI_AT_ASSN,
- "RSSI of associated AP at time of association"),
- IPW2100_ORD(STAT_ASSN_CAUSE1,
- "reassociation: no probe response or TX on hop"),
- IPW2100_ORD(STAT_ASSN_CAUSE2,
- "reassociation: poor tx/rx quality"),
- IPW2100_ORD(STAT_ASSN_CAUSE3,
- "reassociation: tx/rx quality (excessive AP load"),
- IPW2100_ORD(STAT_ASSN_CAUSE4,
- "reassociation: AP RSSI level"),
- IPW2100_ORD(STAT_ASSN_CAUSE5,
- "reassociations due to load leveling"),
- IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
- IPW2100_ORD(STAT_AUTH_RESP_FAIL,
- "times authentication response failed"),
- IPW2100_ORD(STATION_TABLE_CNT,
- "entries in association table"),
- IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
- IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
- IPW2100_ORD(COUNTRY_CODE,
- "IEEE country code as recv'd from beacon"),
- IPW2100_ORD(COUNTRY_CHANNELS,
- "channels suported by country"),
- IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
- IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
- IPW2100_ORD(ANTENNA_DIVERSITY,
- "TRUE if antenna diversity is disabled"),
- IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
- IPW2100_ORD(OUR_FREQ,
- "current radio freq lower digits - channel ID"),
- IPW2100_ORD(RTC_TIME, "current RTC time"),
- IPW2100_ORD(PORT_TYPE, "operating mode"),
- IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
- IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
- IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
- IPW2100_ORD(BASIC_RATES, "basic tx rates"),
- IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
- IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
- IPW2100_ORD(CAPABILITIES,
- "Management frame capability field"),
- IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
- IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
- IPW2100_ORD(RTS_THRESHOLD,
- "Min packet length for RTS handshaking"),
- IPW2100_ORD(INT_MODE, "International mode"),
- IPW2100_ORD(FRAGMENTATION_THRESHOLD,
- "protocol frag threshold"),
- IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
- "EEPROM offset in SRAM"),
- IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,
- "EEPROM size in SRAM"),
- IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
- IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,
- "EEPROM IBSS 11b channel set"),
- IPW2100_ORD(MAC_VERSION, "MAC Version"),
- IPW2100_ORD(MAC_REVISION, "MAC Revision"),
- IPW2100_ORD(RADIO_VERSION, "Radio Version"),
- IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
- IPW2100_ORD(UCODE_VERSION, "Ucode Version"),};
-
-static ssize_t show_registers(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- int i;
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char *out = buf;
- u32 val = 0;
-
- out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
-
- for (i = 0; i < ARRAY_SIZE(hw_data); i++) {
- read_register(dev, hw_data[i].addr, &val);
- out += sprintf(out, "%30s [%08X] : %08X\n",
- hw_data[i].name, hw_data[i].addr, val);
- }
-
- return out - buf;
-}
-
-static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
-
-static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char *out = buf;
- int i;
-
- out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
-
- for (i = 0; i < ARRAY_SIZE(nic_data); i++) {
- u8 tmp8;
- u16 tmp16;
- u32 tmp32;
-
- switch (nic_data[i].size) {
- case 1:
- read_nic_byte(dev, nic_data[i].addr, &tmp8);
- out += sprintf(out, "%30s [%08X] : %02X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp8);
- break;
- case 2:
- read_nic_word(dev, nic_data[i].addr, &tmp16);
- out += sprintf(out, "%30s [%08X] : %04X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp16);
- break;
- case 4:
- read_nic_dword(dev, nic_data[i].addr, &tmp32);
- out += sprintf(out, "%30s [%08X] : %08X\n",
- nic_data[i].name, nic_data[i].addr,
- tmp32);
- break;
- }
- }
- return out - buf;
-}
-
-static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
-
-static ssize_t show_memory(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- static unsigned long loop = 0;
- int len = 0;
- u32 buffer[4];
- int i;
- char line[81];
-
- if (loop >= 0x30000)
- loop = 0;
-
- /* sysfs provides us PAGE_SIZE buffer */
- while (len < PAGE_SIZE - 128 && loop < 0x30000) {
-
- if (priv->snapshot[0])
- for (i = 0; i < 4; i++)
- buffer[i] =
- *(u32 *) SNAPSHOT_ADDR(loop + i * 4);
- else
- for (i = 0; i < 4; i++)
- read_nic_dword(dev, loop + i * 4, &buffer[i]);
-
- if (priv->dump_raw)
- len += sprintf(buf + len,
- "%c%c%c%c"
- "%c%c%c%c"
- "%c%c%c%c"
- "%c%c%c%c",
- ((u8 *) buffer)[0x0],
- ((u8 *) buffer)[0x1],
- ((u8 *) buffer)[0x2],
- ((u8 *) buffer)[0x3],
- ((u8 *) buffer)[0x4],
- ((u8 *) buffer)[0x5],
- ((u8 *) buffer)[0x6],
- ((u8 *) buffer)[0x7],
- ((u8 *) buffer)[0x8],
- ((u8 *) buffer)[0x9],
- ((u8 *) buffer)[0xa],
- ((u8 *) buffer)[0xb],
- ((u8 *) buffer)[0xc],
- ((u8 *) buffer)[0xd],
- ((u8 *) buffer)[0xe],
- ((u8 *) buffer)[0xf]);
- else
- len += sprintf(buf + len, "%s\n",
- snprint_line(line, sizeof(line),
- (u8 *) buffer, 16, loop));
- loop += 16;
- }
-
- return len;
-}
-
-static ssize_t store_memory(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- const char *p = buf;
-
- (void)dev; /* kill unused-var warning for debug-only code */
-
- if (count < 1)
- return count;
-
- if (p[0] == '1' ||
- (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
- IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
- dev->name);
- priv->dump_raw = 1;
-
- } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
- tolower(p[1]) == 'f')) {
- IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
- dev->name);
- priv->dump_raw = 0;
-
- } else if (tolower(p[0]) == 'r') {
- IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name);
- ipw2100_snapshot_free(priv);
-
- } else
- IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
- "reset = clear memory snapshot\n", dev->name);
-
- return count;
-}
-
-static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory);
-
-static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- u32 val = 0;
- int len = 0;
- u32 val_len;
- static int loop = 0;
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return 0;
-
- if (loop >= ARRAY_SIZE(ord_data))
- loop = 0;
-
- /* sysfs provides us PAGE_SIZE buffer */
- while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) {
- val_len = sizeof(u32);
-
- if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
- &val_len))
- len += sprintf(buf + len, "[0x%02X] = ERROR %s\n",
- ord_data[loop].index,
- ord_data[loop].desc);
- else
- len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
- ord_data[loop].index, val,
- ord_data[loop].desc);
- loop++;
- }
-
- return len;
-}
-
-static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
-
-static ssize_t show_stats(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char *out = buf;
-
- out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
- priv->interrupts, priv->tx_interrupts,
- priv->rx_interrupts, priv->inta_other);
- out += sprintf(out, "firmware resets: %d\n", priv->resets);
- out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
-#ifdef CONFIG_IPW2100_DEBUG
- out += sprintf(out, "packet mismatch image: %s\n",
- priv->snapshot[0] ? "YES" : "NO");
-#endif
-
- return out - buf;
-}
-
-static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
-
-static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
-{
- int err;
-
- if (mode == priv->ieee->iw_mode)
- return 0;
-
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
-
- switch (mode) {
- case IW_MODE_INFRA:
- priv->net_dev->type = ARPHRD_ETHER;
- break;
- case IW_MODE_ADHOC:
- priv->net_dev->type = ARPHRD_ETHER;
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- priv->last_mode = priv->ieee->iw_mode;
- priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
- break;
-#endif /* CONFIG_IPW2100_MONITOR */
- }
-
- priv->ieee->iw_mode = mode;
-
-#ifdef CONFIG_PM
- /* Indicate ipw2100_download_firmware download firmware
- * from disk instead of memory. */
- ipw2100_firmware.version = 0;
-#endif
-
- printk(KERN_INFO "%s: Reseting on mode change.\n", priv->net_dev->name);
- priv->reset_backoff = 0;
- schedule_reset(priv);
-
- return 0;
-}
-
-static ssize_t show_internals(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- int len = 0;
-
-#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x)
-
- if (priv->status & STATUS_ASSOCIATED)
- len += sprintf(buf + len, "connected: %lu\n",
- get_seconds() - priv->connect_start);
- else
- len += sprintf(buf + len, "not connected\n");
-
- DUMP_VAR(ieee->crypt[priv->ieee->tx_keyidx], "p");
- DUMP_VAR(status, "08lx");
- DUMP_VAR(config, "08lx");
- DUMP_VAR(capability, "08lx");
-
- len +=
- sprintf(buf + len, "last_rtc: %lu\n",
- (unsigned long)priv->last_rtc);
-
- DUMP_VAR(fatal_error, "d");
- DUMP_VAR(stop_hang_check, "d");
- DUMP_VAR(stop_rf_kill, "d");
- DUMP_VAR(messages_sent, "d");
-
- DUMP_VAR(tx_pend_stat.value, "d");
- DUMP_VAR(tx_pend_stat.hi, "d");
-
- DUMP_VAR(tx_free_stat.value, "d");
- DUMP_VAR(tx_free_stat.lo, "d");
-
- DUMP_VAR(msg_free_stat.value, "d");
- DUMP_VAR(msg_free_stat.lo, "d");
-
- DUMP_VAR(msg_pend_stat.value, "d");
- DUMP_VAR(msg_pend_stat.hi, "d");
-
- DUMP_VAR(fw_pend_stat.value, "d");
- DUMP_VAR(fw_pend_stat.hi, "d");
-
- DUMP_VAR(txq_stat.value, "d");
- DUMP_VAR(txq_stat.lo, "d");
-
- DUMP_VAR(ieee->scans, "d");
- DUMP_VAR(reset_backoff, "d");
-
- return len;
-}
-
-static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
-
-static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char essid[IW_ESSID_MAX_SIZE + 1];
- u8 bssid[ETH_ALEN];
- u32 chan = 0;
- char *out = buf;
- int length;
- int ret;
- DECLARE_MAC_BUF(mac);
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return 0;
-
- memset(essid, 0, sizeof(essid));
- memset(bssid, 0, sizeof(bssid));
-
- length = IW_ESSID_MAX_SIZE;
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- length = sizeof(bssid);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
- bssid, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- length = sizeof(u32);
- ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
- if (ret)
- IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
- __LINE__);
-
- out += sprintf(out, "ESSID: %s\n", essid);
- out += sprintf(out, "BSSID: %s\n", print_mac(mac, bssid));
- out += sprintf(out, "Channel: %d\n", chan);
-
- return out - buf;
-}
-
-static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
-
-#ifdef CONFIG_IPW2100_DEBUG
-static ssize_t show_debug_level(struct device_driver *d, char *buf)
-{
- return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
-}
-
-static ssize_t store_debug_level(struct device_driver *d,
- const char *buf, size_t count)
-{
- char *p = (char *)buf;
- u32 val;
-
- if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
- p++;
- if (p[0] == 'x' || p[0] == 'X')
- p++;
- val = simple_strtoul(p, &p, 16);
- } else
- val = simple_strtoul(p, &p, 10);
- if (p == buf)
- IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf);
- else
- ipw2100_debug_level = val;
-
- return strnlen(buf, count);
-}
-
-static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
- store_debug_level);
-#endif /* CONFIG_IPW2100_DEBUG */
-
-static ssize_t show_fatal_error(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- char *out = buf;
- int i;
-
- if (priv->fatal_error)
- out += sprintf(out, "0x%08X\n", priv->fatal_error);
- else
- out += sprintf(out, "0\n");
-
- for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
- if (!priv->fatal_errors[(priv->fatal_index - i) %
- IPW2100_ERROR_QUEUE])
- continue;
-
- out += sprintf(out, "%d. 0x%08X\n", i,
- priv->fatal_errors[(priv->fatal_index - i) %
- IPW2100_ERROR_QUEUE]);
- }
-
- return out - buf;
-}
-
-static ssize_t store_fatal_error(struct device *d,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- schedule_reset(priv);
- return count;
-}
-
-static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error,
- store_fatal_error);
-
-static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "%d\n", priv->ieee->scan_age);
-}
-
-static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char buffer[] = "00000000";
- unsigned long len =
- (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
- unsigned long val;
- char *p = buffer;
-
- (void)dev; /* kill unused-var warning for debug-only code */
-
- IPW_DEBUG_INFO("enter\n");
-
- strncpy(buffer, buf, len);
- buffer[len] = 0;
-
- if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
- p++;
- if (p[0] == 'x' || p[0] == 'X')
- p++;
- val = simple_strtoul(p, &p, 16);
- } else
- val = simple_strtoul(p, &p, 10);
- if (p == buffer) {
- IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
- } else {
- priv->ieee->scan_age = val;
- IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
- }
-
- IPW_DEBUG_INFO("exit\n");
- return len;
-}
-
-static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
-
-static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- /* 0 - RF kill not enabled
- 1 - SW based RF kill active (sysfs)
- 2 - HW based RF kill active
- 3 - Both HW and SW baed RF kill active */
- struct ipw2100_priv *priv = (struct ipw2100_priv *)d->driver_data;
- int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
- (rf_kill_active(priv) ? 0x2 : 0x0);
- return sprintf(buf, "%i\n", val);
-}
-
-static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
-{
- if ((disable_radio ? 1 : 0) ==
- (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
- return 0;
-
- IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
- disable_radio ? "OFF" : "ON");
-
- mutex_lock(&priv->action_mutex);
-
- if (disable_radio) {
- priv->status |= STATUS_RF_KILL_SW;
- ipw2100_down(priv);
- } else {
- priv->status &= ~STATUS_RF_KILL_SW;
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("Can not turn radio back on - "
- "disabled by HW switch\n");
- /* Make sure the RF_KILL check timer is running */
- priv->stop_rf_kill = 0;
- cancel_delayed_work(&priv->rf_kill);
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- round_jiffies_relative(HZ));
- } else
- schedule_reset(priv);
- }
-
- mutex_unlock(&priv->action_mutex);
- return 1;
-}
-
-static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw2100_priv *priv = dev_get_drvdata(d);
- ipw_radio_kill_sw(priv, buf[0] == '1');
- return count;
-}
-
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
-
-static struct attribute *ipw2100_sysfs_entries[] = {
- &dev_attr_hardware.attr,
- &dev_attr_registers.attr,
- &dev_attr_ordinals.attr,
- &dev_attr_pci.attr,
- &dev_attr_stats.attr,
- &dev_attr_internals.attr,
- &dev_attr_bssinfo.attr,
- &dev_attr_memory.attr,
- &dev_attr_scan_age.attr,
- &dev_attr_fatal_error.attr,
- &dev_attr_rf_kill.attr,
- &dev_attr_cfg.attr,
- &dev_attr_status.attr,
- &dev_attr_capability.attr,
- NULL,
-};
-
-static struct attribute_group ipw2100_attribute_group = {
- .attrs = ipw2100_sysfs_entries,
-};
-
-static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
-{
- struct ipw2100_status_queue *q = &priv->status_queue;
-
- IPW_DEBUG_INFO("enter\n");
-
- q->size = entries * sizeof(struct ipw2100_status);
- q->drv =
- (struct ipw2100_status *)pci_alloc_consistent(priv->pci_dev,
- q->size, &q->nic);
- if (!q->drv) {
- IPW_DEBUG_WARNING("Can not allocate status queue.\n");
- return -ENOMEM;
- }
-
- memset(q->drv, 0, q->size);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static void status_queue_free(struct ipw2100_priv *priv)
-{
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->status_queue.drv) {
- pci_free_consistent(priv->pci_dev, priv->status_queue.size,
- priv->status_queue.drv,
- priv->status_queue.nic);
- priv->status_queue.drv = NULL;
- }
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int bd_queue_allocate(struct ipw2100_priv *priv,
- struct ipw2100_bd_queue *q, int entries)
-{
- IPW_DEBUG_INFO("enter\n");
-
- memset(q, 0, sizeof(struct ipw2100_bd_queue));
-
- q->entries = entries;
- q->size = entries * sizeof(struct ipw2100_bd);
- q->drv = pci_alloc_consistent(priv->pci_dev, q->size, &q->nic);
- if (!q->drv) {
- IPW_DEBUG_INFO
- ("can't allocate shared memory for buffer descriptors\n");
- return -ENOMEM;
- }
- memset(q->drv, 0, q->size);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q)
-{
- IPW_DEBUG_INFO("enter\n");
-
- if (!q)
- return;
-
- if (q->drv) {
- pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic);
- q->drv = NULL;
- }
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void bd_queue_initialize(struct ipw2100_priv *priv,
- struct ipw2100_bd_queue *q, u32 base, u32 size,
- u32 r, u32 w)
-{
- IPW_DEBUG_INFO("enter\n");
-
- IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv,
- (u32) q->nic);
-
- write_register(priv->net_dev, base, q->nic);
- write_register(priv->net_dev, size, q->entries);
- write_register(priv->net_dev, r, q->oldest);
- write_register(priv->net_dev, w, q->next);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void ipw2100_kill_workqueue(struct ipw2100_priv *priv)
-{
- if (priv->workqueue) {
- priv->stop_rf_kill = 1;
- priv->stop_hang_check = 1;
- cancel_delayed_work(&priv->reset_work);
- cancel_delayed_work(&priv->security_work);
- cancel_delayed_work(&priv->wx_event_work);
- cancel_delayed_work(&priv->hang_check);
- cancel_delayed_work(&priv->rf_kill);
- cancel_delayed_work(&priv->scan_event_later);
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
- }
-}
-
-static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
- void *v;
- dma_addr_t p;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
- priv->net_dev->name);
- return err;
- }
-
- priv->tx_buffers =
- (struct ipw2100_tx_packet *)kmalloc(TX_PENDED_QUEUE_LENGTH *
- sizeof(struct
- ipw2100_tx_packet),
- GFP_ATOMIC);
- if (!priv->tx_buffers) {
- printk(KERN_ERR DRV_NAME
- ": %s: alloc failed form tx buffers.\n",
- priv->net_dev->name);
- bd_queue_free(priv, &priv->tx_queue);
- return -ENOMEM;
- }
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- v = pci_alloc_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- &p);
- if (!v) {
- printk(KERN_ERR DRV_NAME
- ": %s: PCI alloc failed for tx " "buffers.\n",
- priv->net_dev->name);
- err = -ENOMEM;
- break;
- }
-
- priv->tx_buffers[i].type = DATA;
- priv->tx_buffers[i].info.d_struct.data =
- (struct ipw2100_data_header *)v;
- priv->tx_buffers[i].info.d_struct.data_phys = p;
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
-
- if (i == TX_PENDED_QUEUE_LENGTH)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- priv->tx_buffers[j].info.d_struct.data,
- priv->tx_buffers[j].info.d_struct.
- data_phys);
- }
-
- kfree(priv->tx_buffers);
- priv->tx_buffers = NULL;
-
- return err;
-}
-
-static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- /*
- * reinitialize packet info lists
- */
- INIT_LIST_HEAD(&priv->fw_pend_list);
- INIT_STAT(&priv->fw_pend_stat);
-
- /*
- * reinitialize lists
- */
- INIT_LIST_HEAD(&priv->tx_pend_list);
- INIT_LIST_HEAD(&priv->tx_free_list);
- INIT_STAT(&priv->tx_pend_stat);
- INIT_STAT(&priv->tx_free_stat);
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- /* We simply drop any SKBs that have been queued for
- * transmit */
- if (priv->tx_buffers[i].info.d_struct.txb) {
- ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.
- txb);
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
-
- list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
- }
-
- SET_STAT(&priv->tx_free_stat, i);
-
- priv->tx_queue.oldest = 0;
- priv->tx_queue.available = priv->tx_queue.entries;
- priv->tx_queue.next = 0;
- INIT_STAT(&priv->txq_stat);
- SET_STAT(&priv->txq_stat, priv->tx_queue.available);
-
- bd_queue_initialize(priv, &priv->tx_queue,
- IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
- IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
- IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
- IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
-
- IPW_DEBUG_INFO("exit\n");
-
-}
-
-static void ipw2100_tx_free(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- bd_queue_free(priv, &priv->tx_queue);
-
- if (!priv->tx_buffers)
- return;
-
- for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
- if (priv->tx_buffers[i].info.d_struct.txb) {
- ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.
- txb);
- priv->tx_buffers[i].info.d_struct.txb = NULL;
- }
- if (priv->tx_buffers[i].info.d_struct.data)
- pci_free_consistent(priv->pci_dev,
- sizeof(struct ipw2100_data_header),
- priv->tx_buffers[i].info.d_struct.
- data,
- priv->tx_buffers[i].info.d_struct.
- data_phys);
- }
-
- kfree(priv->tx_buffers);
- priv->tx_buffers = NULL;
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
-{
- int i, j, err = -EINVAL;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_INFO("failed bd_queue_allocate\n");
- return err;
- }
-
- err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
- if (err) {
- IPW_DEBUG_INFO("failed status_queue_allocate\n");
- bd_queue_free(priv, &priv->rx_queue);
- return err;
- }
-
- /*
- * allocate packets
- */
- priv->rx_buffers = (struct ipw2100_rx_packet *)
- kmalloc(RX_QUEUE_LENGTH * sizeof(struct ipw2100_rx_packet),
- GFP_KERNEL);
- if (!priv->rx_buffers) {
- IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
-
- bd_queue_free(priv, &priv->rx_queue);
-
- status_queue_free(priv);
-
- return -ENOMEM;
- }
-
- for (i = 0; i < RX_QUEUE_LENGTH; i++) {
- struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
-
- err = ipw2100_alloc_skb(priv, packet);
- if (unlikely(err)) {
- err = -ENOMEM;
- break;
- }
-
- /* The BD holds the cache aligned address */
- priv->rx_queue.drv[i].host_addr = packet->dma_addr;
- priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
- priv->status_queue.drv[i].status_fields = 0;
- }
-
- if (i == RX_QUEUE_LENGTH)
- return 0;
-
- for (j = 0; j < i; j++) {
- pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
- sizeof(struct ipw2100_rx_packet),
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(priv->rx_buffers[j].skb);
- }
-
- kfree(priv->rx_buffers);
- priv->rx_buffers = NULL;
-
- bd_queue_free(priv, &priv->rx_queue);
-
- status_queue_free(priv);
-
- return err;
-}
-
-static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
-{
- IPW_DEBUG_INFO("enter\n");
-
- priv->rx_queue.oldest = 0;
- priv->rx_queue.available = priv->rx_queue.entries - 1;
- priv->rx_queue.next = priv->rx_queue.entries - 1;
-
- INIT_STAT(&priv->rxq_stat);
- SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
-
- bd_queue_initialize(priv, &priv->rx_queue,
- IPW_MEM_HOST_SHARED_RX_BD_BASE,
- IPW_MEM_HOST_SHARED_RX_BD_SIZE,
- IPW_MEM_HOST_SHARED_RX_READ_INDEX,
- IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
-
- /* set up the status queue */
- write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
- priv->status_queue.nic);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static void ipw2100_rx_free(struct ipw2100_priv *priv)
-{
- int i;
-
- IPW_DEBUG_INFO("enter\n");
-
- bd_queue_free(priv, &priv->rx_queue);
- status_queue_free(priv);
-
- if (!priv->rx_buffers)
- return;
-
- for (i = 0; i < RX_QUEUE_LENGTH; i++) {
- if (priv->rx_buffers[i].rxp) {
- pci_unmap_single(priv->pci_dev,
- priv->rx_buffers[i].dma_addr,
- sizeof(struct ipw2100_rx),
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(priv->rx_buffers[i].skb);
- }
- }
-
- kfree(priv->rx_buffers);
- priv->rx_buffers = NULL;
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
-{
- u32 length = ETH_ALEN;
- u8 addr[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
-
- int err;
-
- err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length);
- if (err) {
- IPW_DEBUG_INFO("MAC address read failed\n");
- return -EIO;
- }
-
- memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN);
- IPW_DEBUG_INFO("card MAC is %s\n",
- print_mac(mac, priv->net_dev->dev_addr));
-
- return 0;
-}
-
-/********************************************************************
- *
- * Firmware Commands
- *
- ********************************************************************/
-
-static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = ADAPTER_ADDRESS,
- .host_command_sequence = 0,
- .host_command_length = ETH_ALEN
- };
- int err;
-
- IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->config & CFG_CUSTOM_MAC) {
- memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN);
- memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
- } else
- memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
- ETH_ALEN);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- IPW_DEBUG_INFO("exit\n");
- return err;
-}
-
-static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = PORT_TYPE,
- .host_command_sequence = 0,
- .host_command_length = sizeof(u32)
- };
- int err;
-
- switch (port_type) {
- case IW_MODE_INFRA:
- cmd.host_command_parameters[0] = IPW_BSS;
- break;
- case IW_MODE_ADHOC:
- cmd.host_command_parameters[0] = IPW_IBSS;
- break;
- }
-
- IPW_DEBUG_HC("PORT_TYPE: %s\n",
- port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = CHANNEL,
- .host_command_sequence = 0,
- .host_command_length = sizeof(u32)
- };
- int err;
-
- cmd.host_command_parameters[0] = channel;
-
- IPW_DEBUG_HC("CHANNEL: %d\n", channel);
-
- /* If BSS then we don't support channel selection */
- if (priv->ieee->iw_mode == IW_MODE_INFRA)
- return 0;
-
- if ((channel != 0) &&
- ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
- return -EINVAL;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err) {
- IPW_DEBUG_INFO("Failed to set channel to %d", channel);
- return err;
- }
-
- if (channel)
- priv->config |= CFG_STATIC_CHANNEL;
- else
- priv->config &= ~CFG_STATIC_CHANNEL;
-
- priv->channel = channel;
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SYSTEM_CONFIG,
- .host_command_sequence = 0,
- .host_command_length = 12,
- };
- u32 ibss_mask, len = sizeof(u32);
- int err;
-
- /* Set system configuration */
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
-
- cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
- IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE;
-
- if (!(priv->config & CFG_LONG_PREAMBLE))
- cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
-
- err = ipw2100_get_ordinal(priv,
- IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
- &ibss_mask, &len);
- if (err)
- ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
-
- cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
- cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
-
- /* 11b only */
- /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
-/* If IPv6 is configured in the kernel then we don't want to filter out all
- * of the multicast packets as IPv6 needs some. */
-#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
- cmd.host_command = ADD_MULTICAST;
- cmd.host_command_sequence = 0;
- cmd.host_command_length = 0;
-
- ipw2100_hw_send_command(priv, &cmd);
-#endif
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = BASIC_TX_RATES,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- /* Set BASIC TX Rate first */
- ipw2100_hw_send_command(priv, &cmd);
-
- /* Set TX Rate */
- cmd.host_command = TX_RATES;
- ipw2100_hw_send_command(priv, &cmd);
-
- /* Set MSDU TX Rate */
- cmd.host_command = MSDU_TX_RATES;
- ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
-
- priv->tx_rates = rate;
-
- return 0;
-}
-
-static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level)
-{
- struct host_command cmd = {
- .host_command = POWER_MODE,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = power_level;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- if (power_level == IPW_POWER_MODE_CAM)
- priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
- else
- priv->power_mode = IPW_POWER_ENABLED | power_level;
-
-#ifdef IPW2100_TX_POWER
- if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) {
- /* Set beacon interval */
- cmd.host_command = TX_POWER_INDEX;
- cmd.host_command_parameters[0] = (u32) priv->adhoc_power;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
- }
-#endif
-
- return 0;
-}
-
-static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
-{
- struct host_command cmd = {
- .host_command = RTS_THRESHOLD,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- if (threshold & RTS_DISABLED)
- cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
- else
- cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->rts_threshold = threshold;
-
- return 0;
-}
-
-#if 0
-int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
- u32 threshold, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = FRAG_THRESHOLD,
- .host_command_sequence = 0,
- .host_command_length = 4,
- .host_command_parameters[0] = 0,
- };
- int err;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (threshold == 0)
- threshold = DEFAULT_FRAG_THRESHOLD;
- else {
- threshold = max(threshold, MIN_FRAG_THRESHOLD);
- threshold = min(threshold, MAX_FRAG_THRESHOLD);
- }
-
- cmd.host_command_parameters[0] = threshold;
-
- IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- if (!err)
- priv->frag_threshold = threshold;
-
- return err;
-}
-#endif
-
-static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
-{
- struct host_command cmd = {
- .host_command = SHORT_RETRY_LIMIT,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = retry;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->short_retry_limit = retry;
-
- return 0;
-}
-
-static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
-{
- struct host_command cmd = {
- .host_command = LONG_RETRY_LIMIT,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = retry;
-
- err = ipw2100_hw_send_command(priv, &cmd);
- if (err)
- return err;
-
- priv->long_retry_limit = retry;
-
- return 0;
-}
-
-static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = MANDATORY_BSSID,
- .host_command_sequence = 0,
- .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
- };
- int err;
-
-#ifdef CONFIG_IPW2100_DEBUG
- DECLARE_MAC_BUF(mac);
- if (bssid != NULL)
- IPW_DEBUG_HC("MANDATORY_BSSID: %s\n",
- print_mac(mac, bssid));
- else
- IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
-#endif
- /* if BSSID is empty then we disable mandatory bssid mode */
- if (bssid != NULL)
- memcpy(cmd.host_command_parameters, bssid, ETH_ALEN);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
-{
- struct host_command cmd = {
- .host_command = DISASSOCIATION_BSSID,
- .host_command_sequence = 0,
- .host_command_length = ETH_ALEN
- };
- int err;
- int len;
-
- IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
-
- len = ETH_ALEN;
- /* The Firmware currently ignores the BSSID and just disassociates from
- * the currently associated AP -- but in the off chance that a future
- * firmware does use the BSSID provided here, we go ahead and try and
- * set it to the currently associated AP's BSSID */
- memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- return err;
-}
-
-static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
- struct ipw2100_wpa_assoc_frame *, int)
- __attribute__ ((unused));
-
-static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
- struct ipw2100_wpa_assoc_frame *wpa_frame,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SET_WPA_IE,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
- };
- int err;
-
- IPW_DEBUG_HC("SET_WPA_IE\n");
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- memcpy(cmd.host_command_parameters, wpa_frame,
- sizeof(struct ipw2100_wpa_assoc_frame));
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- if (ipw2100_enable_adapter(priv))
- err = -EIO;
- }
-
- return err;
-}
-
-struct security_info_params {
- u32 allowed_ciphers;
- u16 version;
- u8 auth_mode;
- u8 replay_counters_number;
- u8 unicast_using_group;
-} __attribute__ ((packed));
-
-static int ipw2100_set_security_information(struct ipw2100_priv *priv,
- int auth_mode,
- int security_level,
- int unicast_using_group,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = SET_SECURITY_INFORMATION,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct security_info_params)
- };
- struct security_info_params *security =
- (struct security_info_params *)&cmd.host_command_parameters;
- int err;
- memset(security, 0, sizeof(*security));
-
- /* If shared key AP authentication is turned on, then we need to
- * configure the firmware to try and use it.
- *
- * Actual data encryption/decryption is handled by the host. */
- security->auth_mode = auth_mode;
- security->unicast_using_group = unicast_using_group;
-
- switch (security_level) {
- default:
- case SEC_LEVEL_0:
- security->allowed_ciphers = IPW_NONE_CIPHER;
- break;
- case SEC_LEVEL_1:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER;
- break;
- case SEC_LEVEL_2:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
- break;
- case SEC_LEVEL_2_CKIP:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
- break;
- case SEC_LEVEL_3:
- security->allowed_ciphers = IPW_WEP40_CIPHER |
- IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
- break;
- }
-
- IPW_DEBUG_HC
- ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
- security->auth_mode, security->allowed_ciphers, security_level);
-
- security->replay_counters_number = 0;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
-{
- struct host_command cmd = {
- .host_command = TX_POWER_INDEX,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err = 0;
- u32 tmp = tx_power;
-
- if (tx_power != IPW_TX_POWER_DEFAULT)
- tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
- (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
-
- cmd.host_command_parameters[0] = tmp;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- err = ipw2100_hw_send_command(priv, &cmd);
- if (!err)
- priv->tx_power = tx_power;
-
- return 0;
-}
-
-static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
- u32 interval, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = BEACON_INTERVAL,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = interval;
-
- IPW_DEBUG_INFO("enter\n");
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- err = ipw2100_enable_adapter(priv);
- if (err)
- return err;
- }
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-void ipw2100_queues_initialize(struct ipw2100_priv *priv)
-{
- ipw2100_tx_initialize(priv);
- ipw2100_rx_initialize(priv);
- ipw2100_msg_initialize(priv);
-}
-
-void ipw2100_queues_free(struct ipw2100_priv *priv)
-{
- ipw2100_tx_free(priv);
- ipw2100_rx_free(priv);
- ipw2100_msg_free(priv);
-}
-
-int ipw2100_queues_allocate(struct ipw2100_priv *priv)
-{
- if (ipw2100_tx_allocate(priv) ||
- ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv))
- goto fail;
-
- return 0;
-
- fail:
- ipw2100_tx_free(priv);
- ipw2100_rx_free(priv);
- ipw2100_msg_free(priv);
- return -ENOMEM;
-}
-
-#define IPW_PRIVACY_CAPABLE 0x0008
-
-static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
- int batch_mode)
-{
- struct host_command cmd = {
- .host_command = WEP_FLAGS,
- .host_command_sequence = 0,
- .host_command_length = 4
- };
- int err;
-
- cmd.host_command_parameters[0] = flags;
-
- IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-struct ipw2100_wep_key {
- u8 idx;
- u8 len;
- u8 key[13];
-};
-
-/* Macros to ease up priting WEP keys */
-#define WEP_FMT_64 "%02X%02X%02X%02X-%02X"
-#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
-#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
-#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
-
-/**
- * Set a the wep key
- *
- * @priv: struct to work on
- * @idx: index of the key we want to set
- * @key: ptr to the key data to set
- * @len: length of the buffer at @key
- * @batch_mode: FIXME perform the operation in batch mode, not
- * disabling the device.
- *
- * @returns 0 if OK, < 0 errno code on error.
- *
- * Fill out a command structure with the new wep key, length an
- * index and send it down the wire.
- */
-static int ipw2100_set_key(struct ipw2100_priv *priv,
- int idx, char *key, int len, int batch_mode)
-{
- int keylen = len ? (len <= 5 ? 5 : 13) : 0;
- struct host_command cmd = {
- .host_command = WEP_KEY_INFO,
- .host_command_sequence = 0,
- .host_command_length = sizeof(struct ipw2100_wep_key),
- };
- struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters;
- int err;
-
- IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
- idx, keylen, len);
-
- /* NOTE: We don't check cached values in case the firmware was reset
- * or some other problem is occurring. If the user is setting the key,
- * then we push the change */
-
- wep_key->idx = idx;
- wep_key->len = keylen;
-
- if (keylen) {
- memcpy(wep_key->key, key, len);
- memset(wep_key->key + len, 0, keylen - len);
- }
-
- /* Will be optimized out on debug not being configured in */
- if (keylen == 0)
- IPW_DEBUG_WEP("%s: Clearing key %d\n",
- priv->net_dev->name, wep_key->idx);
- else if (keylen == 5)
- IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
- priv->net_dev->name, wep_key->idx, wep_key->len,
- WEP_STR_64(wep_key->key));
- else
- IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
- "\n",
- priv->net_dev->name, wep_key->idx, wep_key->len,
- WEP_STR_128(wep_key->key));
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode) {
- int err2 = ipw2100_enable_adapter(priv);
- if (err == 0)
- err = err2;
- }
- return err;
-}
-
-static int ipw2100_set_key_index(struct ipw2100_priv *priv,
- int idx, int batch_mode)
-{
- struct host_command cmd = {
- .host_command = WEP_KEY_INDEX,
- .host_command_sequence = 0,
- .host_command_length = 4,
- .host_command_parameters = {idx},
- };
- int err;
-
- IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
-
- if (idx < 0 || idx > 3)
- return -EINVAL;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err) {
- printk(KERN_ERR DRV_NAME
- ": %s: Could not disable adapter %d\n",
- priv->net_dev->name, err);
- return err;
- }
- }
-
- /* send cmd to firmware */
- err = ipw2100_hw_send_command(priv, &cmd);
-
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
-{
- int i, err, auth_mode, sec_level, use_group;
-
- if (!(priv->status & STATUS_RUNNING))
- return 0;
-
- if (!batch_mode) {
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
- }
-
- if (!priv->ieee->sec.enabled) {
- err =
- ipw2100_set_security_information(priv, IPW_AUTH_OPEN,
- SEC_LEVEL_0, 0, 1);
- } else {
- auth_mode = IPW_AUTH_OPEN;
- if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
- if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
- auth_mode = IPW_AUTH_SHARED;
- else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
- auth_mode = IPW_AUTH_LEAP_CISCO_ID;
- }
-
- sec_level = SEC_LEVEL_0;
- if (priv->ieee->sec.flags & SEC_LEVEL)
- sec_level = priv->ieee->sec.level;
-
- use_group = 0;
- if (priv->ieee->sec.flags & SEC_UNICAST_GROUP)
- use_group = priv->ieee->sec.unicast_uses_group;
-
- err =
- ipw2100_set_security_information(priv, auth_mode, sec_level,
- use_group, 1);
- }
-
- if (err)
- goto exit;
-
- if (priv->ieee->sec.enabled) {
- for (i = 0; i < 4; i++) {
- if (!(priv->ieee->sec.flags & (1 << i))) {
- memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN);
- priv->ieee->sec.key_sizes[i] = 0;
- } else {
- err = ipw2100_set_key(priv, i,
- priv->ieee->sec.keys[i],
- priv->ieee->sec.
- key_sizes[i], 1);
- if (err)
- goto exit;
- }
- }
-
- ipw2100_set_key_index(priv, priv->ieee->tx_keyidx, 1);
- }
-
- /* Always enable privacy so the Host can filter WEP packets if
- * encrypted data is sent up */
- err =
- ipw2100_set_wep_flags(priv,
- priv->ieee->sec.
- enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
- if (err)
- goto exit;
-
- priv->status &= ~STATUS_SECURITY_UPDATED;
-
- exit:
- if (!batch_mode)
- ipw2100_enable_adapter(priv);
-
- return err;
-}
-
-static void ipw2100_security_work(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, security_work.work);
-
- /* If we happen to have reconnected before we get a chance to
- * process this, then update the security settings--which causes
- * a disassociation to occur */
- if (!(priv->status & STATUS_ASSOCIATED) &&
- priv->status & STATUS_SECURITY_UPDATED)
- ipw2100_configure_security(priv, 0);
-}
-
-static void shim__set_security(struct net_device *dev,
- struct ieee80211_security *sec)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int i, force_update = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED))
- goto done;
-
- for (i = 0; i < 4; i++) {
- if (sec->flags & (1 << i)) {
- priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
- if (sec->key_sizes[i] == 0)
- priv->ieee->sec.flags &= ~(1 << i);
- else
- memcpy(priv->ieee->sec.keys[i], sec->keys[i],
- sec->key_sizes[i]);
- if (sec->level == SEC_LEVEL_1) {
- priv->ieee->sec.flags |= (1 << i);
- priv->status |= STATUS_SECURITY_UPDATED;
- } else
- priv->ieee->sec.flags &= ~(1 << i);
- }
- }
-
- if ((sec->flags & SEC_ACTIVE_KEY) &&
- priv->ieee->sec.active_key != sec->active_key) {
- if (sec->active_key <= 3) {
- priv->ieee->sec.active_key = sec->active_key;
- priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
- } else
- priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
-
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if ((sec->flags & SEC_AUTH_MODE) &&
- (priv->ieee->sec.auth_mode != sec->auth_mode)) {
- priv->ieee->sec.auth_mode = sec->auth_mode;
- priv->ieee->sec.flags |= SEC_AUTH_MODE;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
- priv->ieee->sec.flags |= SEC_ENABLED;
- priv->ieee->sec.enabled = sec->enabled;
- priv->status |= STATUS_SECURITY_UPDATED;
- force_update = 1;
- }
-
- if (sec->flags & SEC_ENCRYPT)
- priv->ieee->sec.encrypt = sec->encrypt;
-
- if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
- priv->ieee->sec.level = sec->level;
- priv->ieee->sec.flags |= SEC_LEVEL;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
- priv->ieee->sec.flags & (1 << 8) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 7) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 6) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 5) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 4) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 3) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 2) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 1) ? '1' : '0',
- priv->ieee->sec.flags & (1 << 0) ? '1' : '0');
-
-/* As a temporary work around to enable WPA until we figure out why
- * wpa_supplicant toggles the security capability of the driver, which
- * forces a disassocation with force_update...
- *
- * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
- if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
- ipw2100_configure_security(priv, 0);
- done:
- mutex_unlock(&priv->action_mutex);
-}
-
-static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
-{
- int err;
- int batch_mode = 1;
- u8 *bssid;
-
- IPW_DEBUG_INFO("enter\n");
-
- err = ipw2100_disable_adapter(priv);
- if (err)
- return err;
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- err = ipw2100_set_channel(priv, priv->channel, batch_mode);
- if (err)
- return err;
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
- }
-#endif /* CONFIG_IPW2100_MONITOR */
-
- err = ipw2100_read_mac_address(priv);
- if (err)
- return -EIO;
-
- err = ipw2100_set_mac_address(priv, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
- if (err)
- return err;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- err = ipw2100_set_channel(priv, priv->channel, batch_mode);
- if (err)
- return err;
- }
-
- err = ipw2100_system_config(priv, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
- if (err)
- return err;
-
- /* Default to power mode OFF */
- err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
- if (err)
- return err;
-
- err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
- if (err)
- return err;
-
- if (priv->config & CFG_STATIC_BSSID)
- bssid = priv->bssid;
- else
- bssid = NULL;
- err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
- if (err)
- return err;
-
- if (priv->config & CFG_STATIC_ESSID)
- err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
- batch_mode);
- else
- err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
- if (err)
- return err;
-
- err = ipw2100_configure_security(priv, batch_mode);
- if (err)
- return err;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- err =
- ipw2100_set_ibss_beacon_interval(priv,
- priv->beacon_interval,
- batch_mode);
- if (err)
- return err;
-
- err = ipw2100_set_tx_power(priv, priv->tx_power);
- if (err)
- return err;
- }
-
- /*
- err = ipw2100_set_fragmentation_threshold(
- priv, priv->frag_threshold, batch_mode);
- if (err)
- return err;
- */
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-/*************************************************************************
- *
- * EXTERNALLY CALLED METHODS
- *
- *************************************************************************/
-
-/* This method is called by the network layer -- not to be confused with
- * ipw2100_set_mac_address() declared above called by this driver (and this
- * method as well) to talk to the firmware */
-static int ipw2100_set_address(struct net_device *dev, void *p)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct sockaddr *addr = p;
- int err = 0;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- mutex_lock(&priv->action_mutex);
-
- priv->config |= CFG_CUSTOM_MAC;
- memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
-
- err = ipw2100_set_mac_address(priv, 0);
- if (err)
- goto done;
-
- priv->reset_backoff = 0;
- mutex_unlock(&priv->action_mutex);
- ipw2100_reset_adapter(&priv->reset_work.work);
- return 0;
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_open(struct net_device *dev)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- unsigned long flags;
- IPW_DEBUG_INFO("dev->open\n");
-
- spin_lock_irqsave(&priv->low_lock, flags);
- if (priv->status & STATUS_ASSOCIATED) {
- netif_carrier_on(dev);
- netif_start_queue(dev);
- }
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- return 0;
-}
-
-static int ipw2100_close(struct net_device *dev)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- unsigned long flags;
- struct list_head *element;
- struct ipw2100_tx_packet *packet;
-
- IPW_DEBUG_INFO("enter\n");
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->status & STATUS_ASSOCIATED)
- netif_carrier_off(dev);
- netif_stop_queue(dev);
-
- /* Flush the TX queue ... */
- while (!list_empty(&priv->tx_pend_list)) {
- element = priv->tx_pend_list.next;
- packet = list_entry(element, struct ipw2100_tx_packet, list);
-
- list_del(element);
- DEC_STAT(&priv->tx_pend_stat);
-
- ieee80211_txb_free(packet->info.d_struct.txb);
- packet->info.d_struct.txb = NULL;
-
- list_add_tail(element, &priv->tx_free_list);
- INC_STAT(&priv->tx_free_stat);
- }
- spin_unlock_irqrestore(&priv->low_lock, flags);
-
- IPW_DEBUG_INFO("exit\n");
-
- return 0;
-}
-
-/*
- * TODO: Fix this function... its just wrong
- */
-static void ipw2100_tx_timeout(struct net_device *dev)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- priv->ieee->stats.tx_errors++;
-
-#ifdef CONFIG_IPW2100_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- return;
-#endif
-
- IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n",
- dev->name);
- schedule_reset(priv);
-}
-
-static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
-{
- /* This is called when wpa_supplicant loads and closes the driver
- * interface. */
- priv->ieee->wpa_enabled = value;
- return 0;
-}
-
-static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
-{
-
- struct ieee80211_device *ieee = priv->ieee;
- struct ieee80211_security sec = {
- .flags = SEC_AUTH_MODE,
- };
- int ret = 0;
-
- if (value & IW_AUTH_ALG_SHARED_KEY) {
- sec.auth_mode = WLAN_AUTH_SHARED_KEY;
- ieee->open_wep = 0;
- } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
- sec.auth_mode = WLAN_AUTH_OPEN;
- ieee->open_wep = 1;
- } else if (value & IW_AUTH_ALG_LEAP) {
- sec.auth_mode = WLAN_AUTH_LEAP;
- ieee->open_wep = 1;
- } else
- return -EINVAL;
-
- if (ieee->set_security)
- ieee->set_security(ieee->dev, &sec);
- else
- ret = -EOPNOTSUPP;
-
- return ret;
-}
-
-static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
- char *wpa_ie, int wpa_ie_len)
-{
-
- struct ipw2100_wpa_assoc_frame frame;
-
- frame.fixed_ie_mask = 0;
-
- /* copy WPA IE */
- memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
- frame.var_ie_len = wpa_ie_len;
-
- /* make sure WPA is enabled */
- ipw2100_wpa_enable(priv, 1);
- ipw2100_set_wpa_ie(priv, &frame, 0);
-}
-
-static void ipw_ethtool_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- char fw_ver[64], ucode_ver[64];
-
- strcpy(info->driver, DRV_NAME);
- strcpy(info->version, DRV_VERSION);
-
- ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
- ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
-
- snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
- fw_ver, priv->eeprom_version, ucode_ver);
-
- strcpy(info->bus_info, pci_name(priv->pci_dev));
-}
-
-static u32 ipw2100_ethtool_get_link(struct net_device *dev)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
-}
-
-static const struct ethtool_ops ipw2100_ethtool_ops = {
- .get_link = ipw2100_ethtool_get_link,
- .get_drvinfo = ipw_ethtool_get_drvinfo,
-};
-
-static void ipw2100_hang_check(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, hang_check.work);
- unsigned long flags;
- u32 rtc = 0xa5a5a5a5;
- u32 len = sizeof(rtc);
- int restart = 0;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (priv->fatal_error != 0) {
- /* If fatal_error is set then we need to restart */
- IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
- priv->net_dev->name);
-
- restart = 1;
- } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
- (rtc == priv->last_rtc)) {
- /* Check if firmware is hung */
- IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
- priv->net_dev->name);
-
- restart = 1;
- }
-
- if (restart) {
- /* Kill timer */
- priv->stop_hang_check = 1;
- priv->hangs++;
-
- /* Restart the NIC */
- schedule_reset(priv);
- }
-
- priv->last_rtc = rtc;
-
- if (!priv->stop_hang_check)
- queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
-
- spin_unlock_irqrestore(&priv->low_lock, flags);
-}
-
-static void ipw2100_rf_kill(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, rf_kill.work);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->low_lock, flags);
-
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
- if (!priv->stop_rf_kill)
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- round_jiffies_relative(HZ));
- goto exit_unlock;
- }
-
- /* RF Kill is now disabled, so bring the device back up */
-
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
- "device\n");
- schedule_reset(priv);
- } else
- IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
- "enabled\n");
-
- exit_unlock:
- spin_unlock_irqrestore(&priv->low_lock, flags);
-}
-
-static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
-
-/* Look into using netdev destructor to shutdown ieee80211? */
-
-static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
- void __iomem * base_addr,
- unsigned long mem_start,
- unsigned long mem_len)
-{
- struct ipw2100_priv *priv;
- struct net_device *dev;
-
- dev = alloc_ieee80211(sizeof(struct ipw2100_priv));
- if (!dev)
- return NULL;
- priv = ieee80211_priv(dev);
- priv->ieee = netdev_priv(dev);
- priv->pci_dev = pci_dev;
- priv->net_dev = dev;
-
- priv->ieee->hard_start_xmit = ipw2100_tx;
- priv->ieee->set_security = shim__set_security;
-
- priv->ieee->perfect_rssi = -20;
- priv->ieee->worst_rssi = -85;
-
- dev->open = ipw2100_open;
- dev->stop = ipw2100_close;
- dev->init = ipw2100_net_init;
- dev->ethtool_ops = &ipw2100_ethtool_ops;
- dev->tx_timeout = ipw2100_tx_timeout;
- dev->wireless_handlers = &ipw2100_wx_handler_def;
- priv->wireless_data.ieee80211 = priv->ieee;
- dev->wireless_data = &priv->wireless_data;
- dev->set_mac_address = ipw2100_set_address;
- dev->watchdog_timeo = 3 * HZ;
- dev->irq = 0;
-
- dev->base_addr = (unsigned long)base_addr;
- dev->mem_start = mem_start;
- dev->mem_end = dev->mem_start + mem_len - 1;
-
- /* NOTE: We don't use the wireless_handlers hook
- * in dev as the system will start throwing WX requests
- * to us before we're actually initialized and it just
- * ends up causing problems. So, we just handle
- * the WX extensions through the ipw2100_ioctl interface */
-
- /* memset() puts everything to 0, so we only have explicitly set
- * those values that need to be something else */
-
- /* If power management is turned on, default to AUTO mode */
- priv->power_mode = IPW_POWER_AUTO;
-
-#ifdef CONFIG_IPW2100_MONITOR
- priv->config |= CFG_CRC_CHECK;
-#endif
- priv->ieee->wpa_enabled = 0;
- priv->ieee->drop_unencrypted = 0;
- priv->ieee->privacy_invoked = 0;
- priv->ieee->ieee802_1x = 1;
-
- /* Set module parameters */
- switch (mode) {
- case 1:
- priv->ieee->iw_mode = IW_MODE_ADHOC;
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case 2:
- priv->ieee->iw_mode = IW_MODE_MONITOR;
- break;
-#endif
- default:
- case 0:
- priv->ieee->iw_mode = IW_MODE_INFRA;
- break;
- }
-
- if (disable == 1)
- priv->status |= STATUS_RF_KILL_SW;
-
- if (channel != 0 &&
- ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) {
- priv->config |= CFG_STATIC_CHANNEL;
- priv->channel = channel;
- }
-
- if (associate)
- priv->config |= CFG_ASSOCIATE;
-
- priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
- priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
- priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
- priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
- priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
- priv->tx_power = IPW_TX_POWER_DEFAULT;
- priv->tx_rates = DEFAULT_TX_RATES;
-
- strcpy(priv->nick, "ipw2100");
-
- spin_lock_init(&priv->low_lock);
- mutex_init(&priv->action_mutex);
- mutex_init(&priv->adapter_mutex);
-
- init_waitqueue_head(&priv->wait_command_queue);
-
- netif_carrier_off(dev);
-
- INIT_LIST_HEAD(&priv->msg_free_list);
- INIT_LIST_HEAD(&priv->msg_pend_list);
- INIT_STAT(&priv->msg_free_stat);
- INIT_STAT(&priv->msg_pend_stat);
-
- INIT_LIST_HEAD(&priv->tx_free_list);
- INIT_LIST_HEAD(&priv->tx_pend_list);
- INIT_STAT(&priv->tx_free_stat);
- INIT_STAT(&priv->tx_pend_stat);
-
- INIT_LIST_HEAD(&priv->fw_pend_list);
- INIT_STAT(&priv->fw_pend_stat);
-
- priv->workqueue = create_workqueue(DRV_NAME);
-
- INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
- INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
- INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
- INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
- INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
- INIT_WORK(&priv->scan_event_now, ipw2100_scan_event_now);
- INIT_DELAYED_WORK(&priv->scan_event_later, ipw2100_scan_event_later);
-
- tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
- ipw2100_irq_tasklet, (unsigned long)priv);
-
- /* NOTE: We do not start the deferred work for status checks yet */
- priv->stop_rf_kill = 1;
- priv->stop_hang_check = 1;
-
- return dev;
-}
-
-static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
- const struct pci_device_id *ent)
-{
- unsigned long mem_start, mem_len, mem_flags;
- void __iomem *base_addr = NULL;
- struct net_device *dev = NULL;
- struct ipw2100_priv *priv = NULL;
- int err = 0;
- int registered = 0;
- u32 val;
-
- IPW_DEBUG_INFO("enter\n");
-
- mem_start = pci_resource_start(pci_dev, 0);
- mem_len = pci_resource_len(pci_dev, 0);
- mem_flags = pci_resource_flags(pci_dev, 0);
-
- if ((mem_flags & IORESOURCE_MEM) != IORESOURCE_MEM) {
- IPW_DEBUG_INFO("weird - resource type is not memory\n");
- err = -ENODEV;
- goto fail;
- }
-
- base_addr = ioremap_nocache(mem_start, mem_len);
- if (!base_addr) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ioremap_nocache.\n");
- err = -EIO;
- goto fail;
- }
-
- /* allocate and initialize our net_device */
- dev = ipw2100_alloc_device(pci_dev, base_addr, mem_start, mem_len);
- if (!dev) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ipw2100_alloc_device.\n");
- err = -ENOMEM;
- goto fail;
- }
-
- /* set up PCI mappings for device */
- err = pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_enable_device.\n");
- return err;
- }
-
- priv = ieee80211_priv(dev);
-
- pci_set_master(pci_dev);
- pci_set_drvdata(pci_dev, priv);
-
- err = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_set_dma_mask.\n");
- pci_disable_device(pci_dev);
- return err;
- }
-
- err = pci_request_regions(pci_dev, DRV_NAME);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling pci_request_regions.\n");
- pci_disable_device(pci_dev);
- return err;
- }
-
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_read_config_dword(pci_dev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
-
- pci_set_power_state(pci_dev, PCI_D0);
-
- if (!ipw2100_hw_is_adapter_in_system(dev)) {
- printk(KERN_WARNING DRV_NAME
- "Device not found via register read.\n");
- err = -ENODEV;
- goto fail;
- }
-
- SET_NETDEV_DEV(dev, &pci_dev->dev);
-
- /* Force interrupts to be shut off on the device */
- priv->status |= STATUS_INT_ENABLED;
- ipw2100_disable_interrupts(priv);
-
- /* Allocate and initialize the Tx/Rx queues and lists */
- if (ipw2100_queues_allocate(priv)) {
- printk(KERN_WARNING DRV_NAME
- "Error calling ipw2100_queues_allocate.\n");
- err = -ENOMEM;
- goto fail;
- }
- ipw2100_queues_initialize(priv);
-
- err = request_irq(pci_dev->irq,
- ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling request_irq: %d.\n", pci_dev->irq);
- goto fail;
- }
- dev->irq = pci_dev->irq;
-
- IPW_DEBUG_INFO("Attempting to register device...\n");
-
- printk(KERN_INFO DRV_NAME
- ": Detected Intel PRO/Wireless 2100 Network Connection\n");
-
- /* Bring up the interface. Pre 0.46, after we registered the
- * network device we would call ipw2100_up. This introduced a race
- * condition with newer hotplug configurations (network was coming
- * up and making calls before the device was initialized).
- *
- * If we called ipw2100_up before we registered the device, then the
- * device name wasn't registered. So, we instead use the net_dev->init
- * member to call a function that then just turns and calls ipw2100_up.
- * net_dev->init is called after name allocation but before the
- * notifier chain is called */
- err = register_netdev(dev);
- if (err) {
- printk(KERN_WARNING DRV_NAME
- "Error calling register_netdev.\n");
- goto fail;
- }
-
- mutex_lock(&priv->action_mutex);
- registered = 1;
-
- IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
-
- /* perform this after register_netdev so that dev->name is set */
- err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
- if (err)
- goto fail_unlock;
-
- /* If the RF Kill switch is disabled, go ahead and complete the
- * startup sequence */
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- /* Enable the adapter - sends HOST_COMPLETE */
- if (ipw2100_enable_adapter(priv)) {
- printk(KERN_WARNING DRV_NAME
- ": %s: failed in call to enable adapter.\n",
- priv->net_dev->name);
- ipw2100_hw_stop_adapter(priv);
- err = -EIO;
- goto fail_unlock;
- }
-
- /* Start a scan . . . */
- ipw2100_set_scan_options(priv);
- ipw2100_start_scan(priv);
- }
-
- IPW_DEBUG_INFO("exit\n");
-
- priv->status |= STATUS_INITIALIZED;
-
- mutex_unlock(&priv->action_mutex);
-
- return 0;
-
- fail_unlock:
- mutex_unlock(&priv->action_mutex);
-
- fail:
- if (dev) {
- if (registered)
- unregister_netdev(dev);
-
- ipw2100_hw_stop_adapter(priv);
-
- ipw2100_disable_interrupts(priv);
-
- if (dev->irq)
- free_irq(dev->irq, priv);
-
- ipw2100_kill_workqueue(priv);
-
- /* These are safe to call even if they weren't allocated */
- ipw2100_queues_free(priv);
- sysfs_remove_group(&pci_dev->dev.kobj,
- &ipw2100_attribute_group);
-
- free_ieee80211(dev);
- pci_set_drvdata(pci_dev, NULL);
- }
-
- if (base_addr)
- iounmap(base_addr);
-
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
-
- return err;
-}
-
-static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev;
-
- if (priv) {
- mutex_lock(&priv->action_mutex);
-
- priv->status &= ~STATUS_INITIALIZED;
-
- dev = priv->net_dev;
- sysfs_remove_group(&pci_dev->dev.kobj,
- &ipw2100_attribute_group);
-
-#ifdef CONFIG_PM
- if (ipw2100_firmware.version)
- ipw2100_release_firmware(priv, &ipw2100_firmware);
-#endif
- /* Take down the hardware */
- ipw2100_down(priv);
-
- /* Release the mutex so that the network subsystem can
- * complete any needed calls into the driver... */
- mutex_unlock(&priv->action_mutex);
-
- /* Unregister the device first - this results in close()
- * being called if the device is open. If we free storage
- * first, then close() will crash. */
- unregister_netdev(dev);
-
- /* ipw2100_down will ensure that there is no more pending work
- * in the workqueue's, so we can safely remove them now. */
- ipw2100_kill_workqueue(priv);
-
- ipw2100_queues_free(priv);
-
- /* Free potential debugging firmware snapshot */
- ipw2100_snapshot_free(priv);
-
- if (dev->irq)
- free_irq(dev->irq, priv);
-
- if (dev->base_addr)
- iounmap((void __iomem *)dev->base_addr);
-
- free_ieee80211(dev);
- }
-
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
-
- IPW_DEBUG_INFO("exit\n");
-}
-
-#ifdef CONFIG_PM
-static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev = priv->net_dev;
-
- IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
-
- mutex_lock(&priv->action_mutex);
- if (priv->status & STATUS_INITIALIZED) {
- /* Take down the device; powers it off, etc. */
- ipw2100_down(priv);
- }
-
- /* Remove the PRESENT state of the device */
- netif_device_detach(dev);
-
- pci_save_state(pci_dev);
- pci_disable_device(pci_dev);
- pci_set_power_state(pci_dev, PCI_D3hot);
-
- mutex_unlock(&priv->action_mutex);
-
- return 0;
-}
-
-static int ipw2100_resume(struct pci_dev *pci_dev)
-{
- struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
- struct net_device *dev = priv->net_dev;
- int err;
- u32 val;
-
- if (IPW2100_PM_DISABLED)
- return 0;
-
- mutex_lock(&priv->action_mutex);
-
- IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
-
- pci_set_power_state(pci_dev, PCI_D0);
- err = pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
- dev->name);
- mutex_unlock(&priv->action_mutex);
- return err;
- }
- pci_restore_state(pci_dev);
-
- /*
- * Suspend/Resume resets the PCI configuration space, so we have to
- * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
- * from interfering with C3 CPU state. pci_restore_state won't help
- * here since it only restores the first 64 bytes pci config header.
- */
- pci_read_config_dword(pci_dev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
-
- /* Set the device back into the PRESENT state; this will also wake
- * the queue of needed */
- netif_device_attach(dev);
-
- /* Bring the device back up */
- if (!(priv->status & STATUS_RF_KILL_SW))
- ipw2100_up(priv, 0);
-
- mutex_unlock(&priv->action_mutex);
-
- return 0;
-}
-#endif
-
-#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
-
-static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = {
- IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
- IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
- IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
- IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
-
- IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
- IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
- IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
-
- IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
- IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
- IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
-
- IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
- {0,},
-};
-
-MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
-
-static struct pci_driver ipw2100_pci_driver = {
- .name = DRV_NAME,
- .id_table = ipw2100_pci_id_table,
- .probe = ipw2100_pci_init_one,
- .remove = __devexit_p(ipw2100_pci_remove_one),
-#ifdef CONFIG_PM
- .suspend = ipw2100_suspend,
- .resume = ipw2100_resume,
-#endif
-};
-
-/**
- * Initialize the ipw2100 driver/module
- *
- * @returns 0 if ok, < 0 errno node con error.
- *
- * Note: we cannot init the /proc stuff until the PCI driver is there,
- * or we risk an unlikely race condition on someone accessing
- * uninitialized data in the PCI dev struct through /proc.
- */
-static int __init ipw2100_init(void)
-{
- int ret;
-
- printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
- printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
-
- ret = pci_register_driver(&ipw2100_pci_driver);
- if (ret)
- goto out;
-
- pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100",
- PM_QOS_DEFAULT_VALUE);
-#ifdef CONFIG_IPW2100_DEBUG
- ipw2100_debug_level = debug;
- ret = driver_create_file(&ipw2100_pci_driver.driver,
- &driver_attr_debug_level);
-#endif
-
-out:
- return ret;
-}
-
-/**
- * Cleanup ipw2100 driver registration
- */
-static void __exit ipw2100_exit(void)
-{
- /* FIXME: IPG: check that we have no instances of the devices open */
-#ifdef CONFIG_IPW2100_DEBUG
- driver_remove_file(&ipw2100_pci_driver.driver,
- &driver_attr_debug_level);
-#endif
- pci_unregister_driver(&ipw2100_pci_driver);
- pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100");
-}
-
-module_init(ipw2100_init);
-module_exit(ipw2100_exit);
-
-#define WEXT_USECHANNELS 1
-
-static const long ipw2100_frequencies[] = {
- 2412, 2417, 2422, 2427,
- 2432, 2437, 2442, 2447,
- 2452, 2457, 2462, 2467,
- 2472, 2484
-};
-
-#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
-
-static const long ipw2100_rates_11b[] = {
- 1000000,
- 2000000,
- 5500000,
- 11000000
-};
-
-#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b)
-
-static int ipw2100_wx_get_name(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- if (!(priv->status & STATUS_ASSOCIATED))
- strcpy(wrqu->name, "unassociated");
- else
- snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
-
- IPW_DEBUG_WX("Name: %s\n", wrqu->name);
- return 0;
-}
-
-static int ipw2100_wx_set_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct iw_freq *fwrq = &wrqu->freq;
- int err = 0;
-
- if (priv->ieee->iw_mode == IW_MODE_INFRA)
- return -EOPNOTSUPP;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- /* if setting by freq convert to channel */
- if (fwrq->e == 1) {
- if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
- int f = fwrq->m / 100000;
- int c = 0;
-
- while ((c < REG_MAX_CHANNEL) &&
- (f != ipw2100_frequencies[c]))
- c++;
-
- /* hack to fall through */
- fwrq->e = 0;
- fwrq->m = c + 1;
- }
- }
-
- if (fwrq->e > 0 || fwrq->m > 1000) {
- err = -EOPNOTSUPP;
- goto done;
- } else { /* Set the channel */
- IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
- err = ipw2100_set_channel(priv, fwrq->m, 0);
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->freq.e = 0;
-
- /* If we are associated, trying to associate, or have a statically
- * configured CHANNEL then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_CHANNEL ||
- priv->status & STATUS_ASSOCIATED)
- wrqu->freq.m = priv->channel;
- else
- wrqu->freq.m = 0;
-
- IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
- return 0;
-
-}
-
-static int ipw2100_wx_set_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode);
-
- if (wrqu->mode == priv->ieee->iw_mode)
- return 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- switch (wrqu->mode) {
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
- break;
-#endif /* CONFIG_IPW2100_MONITOR */
- case IW_MODE_ADHOC:
- err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
- break;
- case IW_MODE_INFRA:
- case IW_MODE_AUTO:
- default:
- err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
- break;
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->mode = priv->ieee->iw_mode;
- IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
-
- return 0;
-}
-
-#define POWER_MODES 5
-
-/* Values are in microsecond */
-static const s32 timeout_duration[POWER_MODES] = {
- 350000,
- 250000,
- 75000,
- 37000,
- 25000,
-};
-
-static const s32 period_duration[POWER_MODES] = {
- 400000,
- 700000,
- 1000000,
- 1000000,
- 1000000
-};
-
-static int ipw2100_wx_get_range(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct iw_range *range = (struct iw_range *)extra;
- u16 val;
- int i, level;
-
- wrqu->data.length = sizeof(*range);
- memset(range, 0, sizeof(*range));
-
- /* Let's try to keep this struct in the same order as in
- * linux/include/wireless.h
- */
-
- /* TODO: See what values we can set, and remove the ones we can't
- * set, or fill them with some default data.
- */
-
- /* ~5 Mb/s real (802.11b) */
- range->throughput = 5 * 1000 * 1000;
-
-// range->sensitivity; /* signal level threshold range */
-
- range->max_qual.qual = 100;
- /* TODO: Find real max RSSI and stick here */
- range->max_qual.level = 0;
- range->max_qual.noise = 0;
- range->max_qual.updated = 7; /* Updated all three */
-
- range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
- /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
- range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
- range->avg_qual.noise = 0;
- range->avg_qual.updated = 7; /* Updated all three */
-
- range->num_bitrates = RATE_COUNT;
-
- for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
- range->bitrate[i] = ipw2100_rates_11b[i];
- }
-
- range->min_rts = MIN_RTS_THRESHOLD;
- range->max_rts = MAX_RTS_THRESHOLD;
- range->min_frag = MIN_FRAG_THRESHOLD;
- range->max_frag = MAX_FRAG_THRESHOLD;
-
- range->min_pmp = period_duration[0]; /* Minimal PM period */
- range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */
- range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */
- range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */
-
- /* How to decode max/min PM period */
- range->pmp_flags = IW_POWER_PERIOD;
- /* How to decode max/min PM period */
- range->pmt_flags = IW_POWER_TIMEOUT;
- /* What PM options are supported */
- range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
-
- range->encoding_size[0] = 5;
- range->encoding_size[1] = 13; /* Different token sizes */
- range->num_encoding_sizes = 2; /* Number of entry in the list */
- range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */
-// range->encoding_login_index; /* token index for login token */
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- range->txpower_capa = IW_TXPOW_DBM;
- range->num_txpower = IW_MAX_TXPOWER;
- for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16);
- i < IW_MAX_TXPOWER;
- i++, level -=
- ((IPW_TX_POWER_MAX_DBM -
- IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
- range->txpower[i] = level / 16;
- } else {
- range->txpower_capa = 0;
- range->num_txpower = 0;
- }
-
- /* Set the Wireless Extension versions */
- range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 18;
-
-// range->retry_capa; /* What retry options are supported */
-// range->retry_flags; /* How to decode max/min retry limit */
-// range->r_time_flags; /* How to decode max/min retry life */
-// range->min_retry; /* Minimal number of retries */
-// range->max_retry; /* Maximal number of retries */
-// range->min_r_time; /* Minimal retry lifetime */
-// range->max_r_time; /* Maximal retry lifetime */
-
- range->num_channels = FREQ_COUNT;
-
- val = 0;
- for (i = 0; i < FREQ_COUNT; i++) {
- // TODO: Include only legal frequencies for some countries
-// if (local->channel_mask & (1 << i)) {
- range->freq[val].i = i + 1;
- range->freq[val].m = ipw2100_frequencies[i] * 100000;
- range->freq[val].e = 1;
- val++;
-// }
- if (val == IW_MAX_FREQUENCIES)
- break;
- }
- range->num_frequency = val;
-
- /* Event capability (kernel + driver) */
- range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
- IW_EVENT_CAPA_MASK(SIOCGIWAP));
- range->event_capa[1] = IW_EVENT_CAPA_K_1;
-
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
- IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
- IPW_DEBUG_WX("GET Range\n");
-
- return 0;
-}
-
-static int ipw2100_wx_set_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- static const unsigned char any[] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
- };
- static const unsigned char off[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- DECLARE_MAC_BUF(mac);
-
- // sanity checks
- if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
- return -EINVAL;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
- !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
- /* we disable mandatory BSSID association */
- IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
- priv->config &= ~CFG_STATIC_BSSID;
- err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
- goto done;
- }
-
- priv->config |= CFG_STATIC_BSSID;
- memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
-
- err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
-
- IPW_DEBUG_WX("SET BSSID -> %s\n",
- print_mac(mac, wrqu->ap_addr.sa_data));
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- DECLARE_MAC_BUF(mac);
-
- /* If we are associated, trying to associate, or have a statically
- * configured BSSID then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) {
- wrqu->ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
- } else
- memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
-
- IPW_DEBUG_WX("Getting WAP BSSID: %s\n",
- print_mac(mac, wrqu->ap_addr.sa_data));
- return 0;
-}
-
-static int ipw2100_wx_set_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- char *essid = ""; /* ANY */
- int length = 0;
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->essid.flags && wrqu->essid.length) {
- length = wrqu->essid.length;
- essid = extra;
- }
-
- if (length == 0) {
- IPW_DEBUG_WX("Setting ESSID to ANY\n");
- priv->config &= ~CFG_STATIC_ESSID;
- err = ipw2100_set_essid(priv, NULL, 0, 0);
- goto done;
- }
-
- length = min(length, IW_ESSID_MAX_SIZE);
-
- priv->config |= CFG_STATIC_ESSID;
-
- if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
- IPW_DEBUG_WX("ESSID set to current ESSID.\n");
- err = 0;
- goto done;
- }
-
- IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length),
- length);
-
- priv->essid_len = length;
- memcpy(priv->essid, essid, priv->essid_len);
-
- err = ipw2100_set_essid(priv, essid, length, 0);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- /* If we are associated, trying to associate, or have a statically
- * configured ESSID then return that; otherwise return ANY */
- if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) {
- IPW_DEBUG_WX("Getting essid: '%s'\n",
- escape_essid(priv->essid, priv->essid_len));
- memcpy(extra, priv->essid, priv->essid_len);
- wrqu->essid.length = priv->essid_len;
- wrqu->essid.flags = 1; /* active */
- } else {
- IPW_DEBUG_WX("Getting essid: ANY\n");
- wrqu->essid.length = 0;
- wrqu->essid.flags = 0; /* active */
- }
-
- return 0;
-}
-
-static int ipw2100_wx_set_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- if (wrqu->data.length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
-
- wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
- memset(priv->nick, 0, sizeof(priv->nick));
- memcpy(priv->nick, extra, wrqu->data.length);
-
- IPW_DEBUG_WX("SET Nickname -> %s \n", priv->nick);
-
- return 0;
-}
-
-static int ipw2100_wx_get_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->data.length = strlen(priv->nick);
- memcpy(extra, priv->nick, wrqu->data.length);
- wrqu->data.flags = 1; /* active */
-
- IPW_DEBUG_WX("GET Nickname -> %s \n", extra);
-
- return 0;
-}
-
-static int ipw2100_wx_set_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- u32 target_rate = wrqu->bitrate.value;
- u32 rate;
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- rate = 0;
-
- if (target_rate == 1000000 ||
- (!wrqu->bitrate.fixed && target_rate > 1000000))
- rate |= TX_RATE_1_MBIT;
- if (target_rate == 2000000 ||
- (!wrqu->bitrate.fixed && target_rate > 2000000))
- rate |= TX_RATE_2_MBIT;
- if (target_rate == 5500000 ||
- (!wrqu->bitrate.fixed && target_rate > 5500000))
- rate |= TX_RATE_5_5_MBIT;
- if (target_rate == 11000000 ||
- (!wrqu->bitrate.fixed && target_rate > 11000000))
- rate |= TX_RATE_11_MBIT;
- if (rate == 0)
- rate = DEFAULT_TX_RATES;
-
- err = ipw2100_set_tx_rates(priv, rate, 0);
-
- IPW_DEBUG_WX("SET Rate -> %04X \n", rate);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int val;
- int len = sizeof(val);
- int err = 0;
-
- if (!(priv->status & STATUS_ENABLED) ||
- priv->status & STATUS_RF_KILL_MASK ||
- !(priv->status & STATUS_ASSOCIATED)) {
- wrqu->bitrate.value = 0;
- return 0;
- }
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
- if (err) {
- IPW_DEBUG_WX("failed querying ordinals.\n");
- goto done;
- }
-
- switch (val & TX_RATE_MASK) {
- case TX_RATE_1_MBIT:
- wrqu->bitrate.value = 1000000;
- break;
- case TX_RATE_2_MBIT:
- wrqu->bitrate.value = 2000000;
- break;
- case TX_RATE_5_5_MBIT:
- wrqu->bitrate.value = 5500000;
- break;
- case TX_RATE_11_MBIT:
- wrqu->bitrate.value = 11000000;
- break;
- default:
- wrqu->bitrate.value = 0;
- }
-
- IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_set_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int value, err;
-
- /* Auto RTS not yet supported */
- if (wrqu->rts.fixed == 0)
- return -EINVAL;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->rts.disabled)
- value = priv->rts_threshold | RTS_DISABLED;
- else {
- if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) {
- err = -EINVAL;
- goto done;
- }
- value = wrqu->rts.value;
- }
-
- err = ipw2100_set_rts_threshold(priv, value);
-
- IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
- wrqu->rts.fixed = 1; /* no auto select */
-
- /* If RTS is set to the default value, then it is disabled */
- wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
-
- IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X \n", wrqu->rts.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0, value;
-
- if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled))
- return -EINPROGRESS;
-
- if (priv->ieee->iw_mode != IW_MODE_ADHOC)
- return 0;
-
- if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
- return -EINVAL;
-
- if (wrqu->txpower.fixed == 0)
- value = IPW_TX_POWER_DEFAULT;
- else {
- if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
- wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
- return -EINVAL;
-
- value = wrqu->txpower.value;
- }
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- err = ipw2100_set_tx_power(priv, value);
-
- IPW_DEBUG_WX("SET TX Power -> %d \n", value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
-
- if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
- wrqu->txpower.fixed = 0;
- wrqu->txpower.value = IPW_TX_POWER_MAX_DBM;
- } else {
- wrqu->txpower.fixed = 1;
- wrqu->txpower.value = priv->tx_power;
- }
-
- wrqu->txpower.flags = IW_TXPOW_DBM;
-
- IPW_DEBUG_WX("GET TX Power -> %d \n", wrqu->txpower.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- if (!wrqu->frag.fixed)
- return -EINVAL;
-
- if (wrqu->frag.disabled) {
- priv->frag_threshold |= FRAG_DISABLED;
- priv->ieee->fts = DEFAULT_FTS;
- } else {
- if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
- wrqu->frag.value > MAX_FRAG_THRESHOLD)
- return -EINVAL;
-
- priv->ieee->fts = wrqu->frag.value & ~0x1;
- priv->frag_threshold = priv->ieee->fts;
- }
-
- IPW_DEBUG_WX("SET Frag Threshold -> %d \n", priv->ieee->fts);
-
- return 0;
-}
-
-static int ipw2100_wx_get_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
- wrqu->frag.fixed = 0; /* no auto select */
- wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
-
- IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
- return -EINVAL;
-
- if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
- return 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->retry.flags & IW_RETRY_SHORT) {
- err = ipw2100_set_short_retry(priv, wrqu->retry.value);
- IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
- wrqu->retry.value);
- goto done;
- }
-
- if (wrqu->retry.flags & IW_RETRY_LONG) {
- err = ipw2100_set_long_retry(priv, wrqu->retry.value);
- IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
- wrqu->retry.value);
- goto done;
- }
-
- err = ipw2100_set_short_retry(priv, wrqu->retry.value);
- if (!err)
- err = ipw2100_set_long_retry(priv, wrqu->retry.value);
-
- IPW_DEBUG_WX("SET Both Retry Limits -> %d \n", wrqu->retry.value);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- wrqu->retry.disabled = 0; /* can't be disabled */
-
- if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
- return -EINVAL;
-
- if (wrqu->retry.flags & IW_RETRY_LONG) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- wrqu->retry.value = priv->long_retry_limit;
- } else {
- wrqu->retry.flags =
- (priv->short_retry_limit !=
- priv->long_retry_limit) ?
- IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
-
- wrqu->retry.value = priv->short_retry_limit;
- }
-
- IPW_DEBUG_WX("GET Retry -> %d \n", wrqu->retry.value);
-
- return 0;
-}
-
-static int ipw2100_wx_set_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- IPW_DEBUG_WX("Initiating scan...\n");
-
- priv->user_requested_scan = 1;
- if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) {
- IPW_DEBUG_WX("Start scan failed.\n");
-
- /* TODO: Mark a scan as pending so when hardware initialized
- * a scan starts */
- }
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
-}
-
-/*
- * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
- */
-static int ipw2100_wx_set_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- /*
- * No check of STATUS_INITIALIZED required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
-}
-
-static int ipw2100_wx_get_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
-}
-
-static int ipw2100_wx_set_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (wrqu->power.disabled) {
- priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
- err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
- IPW_DEBUG_WX("SET Power Management Mode -> off\n");
- goto done;
- }
-
- switch (wrqu->power.flags & IW_POWER_MODE) {
- case IW_POWER_ON: /* If not specified */
- case IW_POWER_MODE: /* If set all mask */
- case IW_POWER_ALL_R: /* If explicitly state all */
- break;
- default: /* Otherwise we don't support it */
- IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
- wrqu->power.flags);
- err = -EOPNOTSUPP;
- goto done;
- }
-
- /* If the user hasn't specified a power management mode yet, default
- * to BATTERY */
- priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
- err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
-
- IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-
-}
-
-static int ipw2100_wx_get_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- if (!(priv->power_mode & IPW_POWER_ENABLED))
- wrqu->power.disabled = 1;
- else {
- wrqu->power.disabled = 0;
- wrqu->power.flags = 0;
- }
-
- IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
-
- return 0;
-}
-
-/*
- * WE-18 WPA support
- */
-
-/* SIOCSIWGENIE */
-static int ipw2100_wx_set_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- u8 *buf;
-
- if (!ieee->wpa_enabled)
- return -EOPNOTSUPP;
-
- if (wrqu->data.length > MAX_WPA_IE_LEN ||
- (wrqu->data.length && extra == NULL))
- return -EINVAL;
-
- if (wrqu->data.length) {
- buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = buf;
- ieee->wpa_ie_len = wrqu->data.length;
- } else {
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = NULL;
- ieee->wpa_ie_len = 0;
- }
-
- ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
-
- return 0;
-}
-
-/* SIOCGIWGENIE */
-static int ipw2100_wx_get_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
-
- if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
- wrqu->data.length = 0;
- return 0;
- }
-
- if (wrqu->data.length < ieee->wpa_ie_len)
- return -E2BIG;
-
- wrqu->data.length = ieee->wpa_ie_len;
- memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
-
- return 0;
-}
-
-/* SIOCSIWAUTH */
-static int ipw2100_wx_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- struct iw_param *param = &wrqu->param;
- struct ieee80211_crypt_data *crypt;
- unsigned long flags;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * ipw2200 does not use these parameters
- */
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
- break;
-
- flags = crypt->ops->get_flags(crypt->priv);
-
- if (param->value)
- flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
- else
- flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-
- crypt->ops->set_flags(flags, crypt->priv);
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:{
- /* HACK:
- *
- * wpa_supplicant calls set_wpa_enabled when the driver
- * is loaded and unloaded, regardless of if WPA is being
- * used. No other calls are made which can be used to
- * determine if encryption will be used or not prior to
- * association being expected. If encryption is not being
- * used, drop_unencrypted is set to false, else true -- we
- * can use this to determine if the CAP_PRIVACY_ON bit should
- * be set.
- */
- struct ieee80211_security sec = {
- .flags = SEC_ENABLED,
- .enabled = param->value,
- };
- priv->ieee->drop_unencrypted = param->value;
- /* We only change SEC_LEVEL for open mode. Others
- * are set by ipw_wpa_set_encryption.
- */
- if (!param->value) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_0;
- } else {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- }
- if (priv->ieee->set_security)
- priv->ieee->set_security(priv->ieee->dev, &sec);
- break;
- }
-
- case IW_AUTH_80211_AUTH_ALG:
- ret = ipw2100_wpa_set_auth_algs(priv, param->value);
- break;
-
- case IW_AUTH_WPA_ENABLED:
- ret = ipw2100_wpa_enable(priv, param->value);
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- ieee->ieee802_1x = param->value;
- break;
-
- //case IW_AUTH_ROAMING_CONTROL:
- case IW_AUTH_PRIVACY_INVOKED:
- ieee->privacy_invoked = param->value;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return ret;
-}
-
-/* SIOCGIWAUTH */
-static int ipw2100_wx_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- struct ieee80211_crypt_data *crypt;
- struct iw_param *param = &wrqu->param;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * wpa_supplicant will control these internally
- */
- ret = -EOPNOTSUPP;
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->get_flags) {
- IPW_DEBUG_WARNING("Can't get TKIP countermeasures: "
- "crypt not set!\n");
- break;
- }
-
- param->value = (crypt->ops->get_flags(crypt->priv) &
- IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- param->value = ieee->drop_unencrypted;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- param->value = priv->ieee->sec.auth_mode;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = ieee->wpa_enabled;
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- param->value = ieee->ieee802_1x;
- break;
-
- case IW_AUTH_ROAMING_CONTROL:
- case IW_AUTH_PRIVACY_INVOKED:
- param->value = ieee->privacy_invoked;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-/* SIOCSIWENCODEEXT */
-static int ipw2100_wx_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCGIWENCODEEXT */
-static int ipw2100_wx_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCSIWMLME */
-static int ipw2100_wx_set_mlme(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct iw_mlme *mlme = (struct iw_mlme *)extra;
- __le16 reason;
-
- reason = cpu_to_le16(mlme->reason_code);
-
- switch (mlme->cmd) {
- case IW_MLME_DEAUTH:
- // silently ignore
- break;
-
- case IW_MLME_DISASSOC:
- ipw2100_disassociate_bssid(priv);
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-/*
- *
- * IWPRIV handlers
- *
- */
-#ifdef CONFIG_IPW2100_MONITOR
-static int ipw2100_wx_set_promisc(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int *parms = (int *)extra;
- int enable = (parms[0] > 0);
- int err = 0;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (enable) {
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- err = ipw2100_set_channel(priv, parms[1], 0);
- goto done;
- }
- priv->channel = parms[1];
- err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
- } else {
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- err = ipw2100_switch_mode(priv, priv->last_mode);
- }
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_reset(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- if (priv->status & STATUS_INITIALIZED)
- schedule_reset(priv);
- return 0;
-}
-
-#endif
-
-static int ipw2100_wx_set_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err = 0, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if ((mode < 0) || (mode > POWER_MODES))
- mode = IPW_POWER_AUTO;
-
- if (IPW_POWER_LEVEL(priv->power_mode) != mode)
- err = ipw2100_set_power_mode(priv, mode);
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-#define MAX_POWER_STRING 80
-static int ipw2100_wx_get_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int level = IPW_POWER_LEVEL(priv->power_mode);
- s32 timeout, period;
-
- if (!(priv->power_mode & IPW_POWER_ENABLED)) {
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (Off)", level);
- } else {
- switch (level) {
- case IPW_POWER_MODE_CAM:
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (None)", level);
- break;
- case IPW_POWER_AUTO:
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d (Auto)", level);
- break;
- default:
- timeout = timeout_duration[level - 1] / 1000;
- period = period_duration[level - 1] / 1000;
- snprintf(extra, MAX_POWER_STRING,
- "Power save level: %d "
- "(Timeout %dms, Period %dms)",
- level, timeout, period);
- }
- }
-
- wrqu->data.length = strlen(extra) + 1;
-
- return 0;
-}
-
-static int ipw2100_wx_set_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (mode == 1)
- priv->config |= CFG_LONG_PREAMBLE;
- else if (mode == 0)
- priv->config &= ~CFG_LONG_PREAMBLE;
- else {
- err = -EINVAL;
- goto done;
- }
-
- err = ipw2100_system_config(priv, 0);
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- if (priv->config & CFG_LONG_PREAMBLE)
- snprintf(wrqu->name, IFNAMSIZ, "long (1)");
- else
- snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
-
- return 0;
-}
-
-#ifdef CONFIG_IPW2100_MONITOR
-static int ipw2100_wx_set_crc_check(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- int err, mode = *(int *)extra;
-
- mutex_lock(&priv->action_mutex);
- if (!(priv->status & STATUS_INITIALIZED)) {
- err = -EIO;
- goto done;
- }
-
- if (mode == 1)
- priv->config |= CFG_CRC_CHECK;
- else if (mode == 0)
- priv->config &= ~CFG_CRC_CHECK;
- else {
- err = -EINVAL;
- goto done;
- }
- err = 0;
-
- done:
- mutex_unlock(&priv->action_mutex);
- return err;
-}
-
-static int ipw2100_wx_get_crc_check(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /*
- * This can be called at any time. No action lock required
- */
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
-
- if (priv->config & CFG_CRC_CHECK)
- snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)");
- else
- snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)");
-
- return 0;
-}
-#endif /* CONFIG_IPW2100_MONITOR */
-
-static iw_handler ipw2100_wx_handlers[] = {
- NULL, /* SIOCSIWCOMMIT */
- ipw2100_wx_get_name, /* SIOCGIWNAME */
- NULL, /* SIOCSIWNWID */
- NULL, /* SIOCGIWNWID */
- ipw2100_wx_set_freq, /* SIOCSIWFREQ */
- ipw2100_wx_get_freq, /* SIOCGIWFREQ */
- ipw2100_wx_set_mode, /* SIOCSIWMODE */
- ipw2100_wx_get_mode, /* SIOCGIWMODE */
- NULL, /* SIOCSIWSENS */
- NULL, /* SIOCGIWSENS */
- NULL, /* SIOCSIWRANGE */
- ipw2100_wx_get_range, /* SIOCGIWRANGE */
- NULL, /* SIOCSIWPRIV */
- NULL, /* SIOCGIWPRIV */
- NULL, /* SIOCSIWSTATS */
- NULL, /* SIOCGIWSTATS */
- NULL, /* SIOCSIWSPY */
- NULL, /* SIOCGIWSPY */
- NULL, /* SIOCGIWTHRSPY */
- NULL, /* SIOCWIWTHRSPY */
- ipw2100_wx_set_wap, /* SIOCSIWAP */
- ipw2100_wx_get_wap, /* SIOCGIWAP */
- ipw2100_wx_set_mlme, /* SIOCSIWMLME */
- NULL, /* SIOCGIWAPLIST -- deprecated */
- ipw2100_wx_set_scan, /* SIOCSIWSCAN */
- ipw2100_wx_get_scan, /* SIOCGIWSCAN */
- ipw2100_wx_set_essid, /* SIOCSIWESSID */
- ipw2100_wx_get_essid, /* SIOCGIWESSID */
- ipw2100_wx_set_nick, /* SIOCSIWNICKN */
- ipw2100_wx_get_nick, /* SIOCGIWNICKN */
- NULL, /* -- hole -- */
- NULL, /* -- hole -- */
- ipw2100_wx_set_rate, /* SIOCSIWRATE */
- ipw2100_wx_get_rate, /* SIOCGIWRATE */
- ipw2100_wx_set_rts, /* SIOCSIWRTS */
- ipw2100_wx_get_rts, /* SIOCGIWRTS */
- ipw2100_wx_set_frag, /* SIOCSIWFRAG */
- ipw2100_wx_get_frag, /* SIOCGIWFRAG */
- ipw2100_wx_set_txpow, /* SIOCSIWTXPOW */
- ipw2100_wx_get_txpow, /* SIOCGIWTXPOW */
- ipw2100_wx_set_retry, /* SIOCSIWRETRY */
- ipw2100_wx_get_retry, /* SIOCGIWRETRY */
- ipw2100_wx_set_encode, /* SIOCSIWENCODE */
- ipw2100_wx_get_encode, /* SIOCGIWENCODE */
- ipw2100_wx_set_power, /* SIOCSIWPOWER */
- ipw2100_wx_get_power, /* SIOCGIWPOWER */
- NULL, /* -- hole -- */
- NULL, /* -- hole -- */
- ipw2100_wx_set_genie, /* SIOCSIWGENIE */
- ipw2100_wx_get_genie, /* SIOCGIWGENIE */
- ipw2100_wx_set_auth, /* SIOCSIWAUTH */
- ipw2100_wx_get_auth, /* SIOCGIWAUTH */
- ipw2100_wx_set_encodeext, /* SIOCSIWENCODEEXT */
- ipw2100_wx_get_encodeext, /* SIOCGIWENCODEEXT */
- NULL, /* SIOCSIWPMKSA */
-};
-
-#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV
-#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1
-#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2
-#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3
-#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4
-#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5
-#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6
-#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7
-
-static const struct iw_priv_args ipw2100_private_args[] = {
-
-#ifdef CONFIG_IPW2100_MONITOR
- {
- IPW2100_PRIV_SET_MONITOR,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
- {
- IPW2100_PRIV_RESET,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
-#endif /* CONFIG_IPW2100_MONITOR */
-
- {
- IPW2100_PRIV_SET_POWER,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"},
- {
- IPW2100_PRIV_GET_POWER,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING,
- "get_power"},
- {
- IPW2100_PRIV_SET_LONGPREAMBLE,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
- {
- IPW2100_PRIV_GET_LONGPREAMBLE,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"},
-#ifdef CONFIG_IPW2100_MONITOR
- {
- IPW2100_PRIV_SET_CRC_CHECK,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"},
- {
- IPW2100_PRIV_GET_CRC_CHECK,
- 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"},
-#endif /* CONFIG_IPW2100_MONITOR */
-};
-
-static iw_handler ipw2100_private_handler[] = {
-#ifdef CONFIG_IPW2100_MONITOR
- ipw2100_wx_set_promisc,
- ipw2100_wx_reset,
-#else /* CONFIG_IPW2100_MONITOR */
- NULL,
- NULL,
-#endif /* CONFIG_IPW2100_MONITOR */
- ipw2100_wx_set_powermode,
- ipw2100_wx_get_powermode,
- ipw2100_wx_set_preamble,
- ipw2100_wx_get_preamble,
-#ifdef CONFIG_IPW2100_MONITOR
- ipw2100_wx_set_crc_check,
- ipw2100_wx_get_crc_check,
-#else /* CONFIG_IPW2100_MONITOR */
- NULL,
- NULL,
-#endif /* CONFIG_IPW2100_MONITOR */
-};
-
-/*
- * Get wireless statistics.
- * Called by /proc/net/wireless
- * Also called by SIOCGIWSTATS
- */
-static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev)
-{
- enum {
- POOR = 30,
- FAIR = 60,
- GOOD = 80,
- VERY_GOOD = 90,
- EXCELLENT = 95,
- PERFECT = 100
- };
- int rssi_qual;
- int tx_qual;
- int beacon_qual;
-
- struct ipw2100_priv *priv = ieee80211_priv(dev);
- struct iw_statistics *wstats;
- u32 rssi, quality, tx_retries, missed_beacons, tx_failures;
- u32 ord_len = sizeof(u32);
-
- if (!priv)
- return (struct iw_statistics *)NULL;
-
- wstats = &priv->wstats;
-
- /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
- * ipw2100_wx_wireless_stats seems to be called before fw is
- * initialized. STATUS_ASSOCIATED will only be set if the hw is up
- * and associated; if not associcated, the values are all meaningless
- * anyway, so set them all to NULL and INVALID */
- if (!(priv->status & STATUS_ASSOCIATED)) {
- wstats->miss.beacon = 0;
- wstats->discard.retries = 0;
- wstats->qual.qual = 0;
- wstats->qual.level = 0;
- wstats->qual.noise = 0;
- wstats->qual.updated = 7;
- wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
- IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
- return wstats;
- }
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
- &missed_beacons, &ord_len))
- goto fail_get_ordinal;
-
- /* If we don't have a connection the quality and level is 0 */
- if (!(priv->status & STATUS_ASSOCIATED)) {
- wstats->qual.qual = 0;
- wstats->qual.level = 0;
- } else {
- if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
- &rssi, &ord_len))
- goto fail_get_ordinal;
- wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
- if (rssi < 10)
- rssi_qual = rssi * POOR / 10;
- else if (rssi < 15)
- rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
- else if (rssi < 20)
- rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
- else if (rssi < 30)
- rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
- 10 + GOOD;
- else
- rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
- 10 + VERY_GOOD;
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
- &tx_retries, &ord_len))
- goto fail_get_ordinal;
-
- if (tx_retries > 75)
- tx_qual = (90 - tx_retries) * POOR / 15;
- else if (tx_retries > 70)
- tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
- else if (tx_retries > 65)
- tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
- else if (tx_retries > 50)
- tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
- 15 + GOOD;
- else
- tx_qual = (50 - tx_retries) *
- (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
-
- if (missed_beacons > 50)
- beacon_qual = (60 - missed_beacons) * POOR / 10;
- else if (missed_beacons > 40)
- beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
- 10 + POOR;
- else if (missed_beacons > 32)
- beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
- 18 + FAIR;
- else if (missed_beacons > 20)
- beacon_qual = (32 - missed_beacons) *
- (VERY_GOOD - GOOD) / 20 + GOOD;
- else
- beacon_qual = (20 - missed_beacons) *
- (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
-
- quality = min(beacon_qual, min(tx_qual, rssi_qual));
-
-#ifdef CONFIG_IPW2100_DEBUG
- if (beacon_qual == quality)
- IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
- else if (tx_qual == quality)
- IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
- else if (quality != 100)
- IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
- else
- IPW_DEBUG_WX("Quality not clamped.\n");
-#endif
-
- wstats->qual.qual = quality;
- wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
- }
-
- wstats->qual.noise = 0;
- wstats->qual.updated = 7;
- wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
-
- /* FIXME: this is percent and not a # */
- wstats->miss.beacon = missed_beacons;
-
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
- &tx_failures, &ord_len))
- goto fail_get_ordinal;
- wstats->discard.retries = tx_failures;
-
- return wstats;
-
- fail_get_ordinal:
- IPW_DEBUG_WX("failed querying ordinals.\n");
-
- return (struct iw_statistics *)NULL;
-}
-
-static struct iw_handler_def ipw2100_wx_handler_def = {
- .standard = ipw2100_wx_handlers,
- .num_standard = ARRAY_SIZE(ipw2100_wx_handlers),
- .num_private = ARRAY_SIZE(ipw2100_private_handler),
- .num_private_args = ARRAY_SIZE(ipw2100_private_args),
- .private = (iw_handler *) ipw2100_private_handler,
- .private_args = (struct iw_priv_args *)ipw2100_private_args,
- .get_wireless_stats = ipw2100_wx_wireless_stats,
-};
-
-static void ipw2100_wx_event_work(struct work_struct *work)
-{
- struct ipw2100_priv *priv =
- container_of(work, struct ipw2100_priv, wx_event_work.work);
- union iwreq_data wrqu;
- int len = ETH_ALEN;
-
- if (priv->status & STATUS_STOPPING)
- return;
-
- mutex_lock(&priv->action_mutex);
-
- IPW_DEBUG_WX("enter\n");
-
- mutex_unlock(&priv->action_mutex);
-
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-
- /* Fetch BSSID from the hardware */
- if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
- priv->status & STATUS_RF_KILL_MASK ||
- ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
- &priv->bssid, &len)) {
- memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
- } else {
- /* We now have the BSSID, so can finish setting to the full
- * associated state */
- memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
- memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
- priv->status &= ~STATUS_ASSOCIATING;
- priv->status |= STATUS_ASSOCIATED;
- netif_carrier_on(priv->net_dev);
- netif_wake_queue(priv->net_dev);
- }
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- IPW_DEBUG_WX("Configuring ESSID\n");
- mutex_lock(&priv->action_mutex);
- /* This is a disassociation event, so kick the firmware to
- * look for another AP */
- if (priv->config & CFG_STATIC_ESSID)
- ipw2100_set_essid(priv, priv->essid, priv->essid_len,
- 0);
- else
- ipw2100_set_essid(priv, NULL, 0, 0);
- mutex_unlock(&priv->action_mutex);
- }
-
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-#define IPW2100_FW_MAJOR_VERSION 1
-#define IPW2100_FW_MINOR_VERSION 3
-
-#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
-#define IPW2100_FW_MAJOR(x) (x & 0xff)
-
-#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
- IPW2100_FW_MAJOR_VERSION)
-
-#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
-"." __stringify(IPW2100_FW_MINOR_VERSION)
-
-#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
-
-/*
-
-BINARY FIRMWARE HEADER FORMAT
-
-offset length desc
-0 2 version
-2 2 mode == 0:BSS,1:IBSS,2:MONITOR
-4 4 fw_len
-8 4 uc_len
-C fw_len firmware data
-12 + fw_len uc_len microcode data
-
-*/
-
-struct ipw2100_fw_header {
- short version;
- short mode;
- unsigned int fw_size;
- unsigned int uc_size;
-} __attribute__ ((packed));
-
-static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
-{
- struct ipw2100_fw_header *h =
- (struct ipw2100_fw_header *)fw->fw_entry->data;
-
- if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
- printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
- "(detected version id of %u). "
- "See Documentation/networking/README.ipw2100\n",
- h->version);
- return 1;
- }
-
- fw->version = h->version;
- fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
- fw->fw.size = h->fw_size;
- fw->uc.data = fw->fw.data + h->fw_size;
- fw->uc.size = h->uc_size;
-
- return 0;
-}
-
-static int ipw2100_get_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- char *fw_name;
- int rc;
-
- IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
- priv->net_dev->name);
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- fw_name = IPW2100_FW_NAME("-i");
- break;
-#ifdef CONFIG_IPW2100_MONITOR
- case IW_MODE_MONITOR:
- fw_name = IPW2100_FW_NAME("-p");
- break;
-#endif
- case IW_MODE_INFRA:
- default:
- fw_name = IPW2100_FW_NAME("");
- break;
- }
-
- rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
-
- if (rc < 0) {
- printk(KERN_ERR DRV_NAME ": "
- "%s: Firmware '%s' not available or load failed.\n",
- priv->net_dev->name, fw_name);
- return rc;
- }
- IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
- fw->fw_entry->size);
-
- ipw2100_mod_firmware_load(fw);
-
- return 0;
-}
-
-static void ipw2100_release_firmware(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- fw->version = 0;
- if (fw->fw_entry)
- release_firmware(fw->fw_entry);
- fw->fw_entry = NULL;
-}
-
-static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
- size_t max)
-{
- char ver[MAX_FW_VERSION_LEN];
- u32 len = MAX_FW_VERSION_LEN;
- u32 tmp;
- int i;
- /* firmware version is an ascii string (max len of 14) */
- if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len))
- return -EIO;
- tmp = max;
- if (len >= max)
- len = max - 1;
- for (i = 0; i < len; i++)
- buf[i] = ver[i];
- buf[i] = '\0';
- return tmp;
-}
-
-static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
- size_t max)
-{
- u32 ver;
- u32 len = sizeof(ver);
- /* microcode version is a 32 bit integer */
- if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len))
- return -EIO;
- return snprintf(buf, max, "%08X", ver);
-}
-
-/*
- * On exit, the firmware will have been freed from the fw list
- */
-static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw)
-{
- /* firmware is constructed of N contiguous entries, each entry is
- * structured as:
- *
- * offset sie desc
- * 0 4 address to write to
- * 4 2 length of data run
- * 6 length data
- */
- unsigned int addr;
- unsigned short len;
-
- const unsigned char *firmware_data = fw->fw.data;
- unsigned int firmware_data_left = fw->fw.size;
-
- while (firmware_data_left > 0) {
- addr = *(u32 *) (firmware_data);
- firmware_data += 4;
- firmware_data_left -= 4;
-
- len = *(u16 *) (firmware_data);
- firmware_data += 2;
- firmware_data_left -= 2;
-
- if (len > 32) {
- printk(KERN_ERR DRV_NAME ": "
- "Invalid firmware run-length of %d bytes\n",
- len);
- return -EINVAL;
- }
-
- write_nic_memory(priv->net_dev, addr, len, firmware_data);
- firmware_data += len;
- firmware_data_left -= len;
- }
-
- return 0;
-}
-
-struct symbol_alive_response {
- u8 cmd_id;
- u8 seq_num;
- u8 ucode_rev;
- u8 eeprom_valid;
- u16 valid_flags;
- u8 IEEE_addr[6];
- u16 flags;
- u16 pcb_rev;
- u16 clock_settle_time; // 1us LSB
- u16 powerup_settle_time; // 1us LSB
- u16 hop_settle_time; // 1us LSB
- u8 date[3]; // month, day, year
- u8 time[2]; // hours, minutes
- u8 ucode_valid;
-};
-
-static int ipw2100_ucode_download(struct ipw2100_priv *priv,
- struct ipw2100_fw *fw)
-{
- struct net_device *dev = priv->net_dev;
- const unsigned char *microcode_data = fw->uc.data;
- unsigned int microcode_data_left = fw->uc.size;
- void __iomem *reg = (void __iomem *)dev->base_addr;
-
- struct symbol_alive_response response;
- int i, j;
- u8 data;
-
- /* Symbol control */
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
- readl(reg);
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
- readl(reg);
-
- /* HW config */
- write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
- readl(reg);
- write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
- readl(reg);
-
- /* EN_CS_ACCESS bit to reset control store pointer */
- write_nic_byte(dev, 0x210000, 0x40);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x40);
- readl(reg);
-
- /* copy microcode from buffer into Symbol */
-
- while (microcode_data_left > 0) {
- write_nic_byte(dev, 0x210010, *microcode_data++);
- write_nic_byte(dev, 0x210010, *microcode_data++);
- microcode_data_left -= 2;
- }
-
- /* EN_CS_ACCESS bit to reset the control store pointer */
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
-
- /* Enable System (Reg 0)
- * first enable causes garbage in RX FIFO */
- write_nic_byte(dev, 0x210000, 0x0);
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x80);
- readl(reg);
-
- /* Reset External Baseband Reg */
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
- readl(reg);
- write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
- readl(reg);
-
- /* HW Config (Reg 5) */
- write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
- readl(reg);
- write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
- readl(reg);
-
- /* Enable System (Reg 0)
- * second enable should be OK */
- write_nic_byte(dev, 0x210000, 0x00); // clear enable system
- readl(reg);
- write_nic_byte(dev, 0x210000, 0x80); // set enable system
-
- /* check Symbol is enabled - upped this from 5 as it wasn't always
- * catching the update */
- for (i = 0; i < 10; i++) {
- udelay(10);
-
- /* check Dino is enabled bit */
- read_nic_byte(dev, 0x210000, &data);
- if (data & 0x1)
- break;
- }
-
- if (i == 10) {
- printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
- dev->name);
- return -EIO;
- }
-
- /* Get Symbol alive response */
- for (i = 0; i < 30; i++) {
- /* Read alive response structure */
- for (j = 0;
- j < (sizeof(struct symbol_alive_response) >> 1); j++)
- read_nic_word(dev, 0x210004, ((u16 *) & response) + j);
-
- if ((response.cmd_id == 1) && (response.ucode_valid == 0x1))
- break;
- udelay(10);
- }
-
- if (i == 30) {
- printk(KERN_ERR DRV_NAME
- ": %s: No response from Symbol - hw not alive\n",
- dev->name);
- printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response));
- return -EIO;
- }
-
- return 0;
-}
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
deleted file mode 100644
index 7a9f901d4ff6..000000000000
--- a/drivers/net/wireless/ipw2200.c
+++ /dev/null
@@ -1,12008 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
- 802.11 status code portion of this file from ethereal-0.10.6:
- Copyright 2000, Axis Communications AB
- Ethereal - Network traffic analyzer
- By Gerald Combs <gerald@ethereal.com>
- Copyright 1998 Gerald Combs
-
- 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., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
-
-#include "ipw2200.h"
-
-
-#ifndef KBUILD_EXTMOD
-#define VK "k"
-#else
-#define VK
-#endif
-
-#ifdef CONFIG_IPW2200_DEBUG
-#define VD "d"
-#else
-#define VD
-#endif
-
-#ifdef CONFIG_IPW2200_MONITOR
-#define VM "m"
-#else
-#define VM
-#endif
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-#define VP "p"
-#else
-#define VP
-#endif
-
-#ifdef CONFIG_IPW2200_RADIOTAP
-#define VR "r"
-#else
-#define VR
-#endif
-
-#ifdef CONFIG_IPW2200_QOS
-#define VQ "q"
-#else
-#define VQ
-#endif
-
-#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
-#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
-#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
-#define DRV_VERSION IPW2200_VERSION
-
-#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
-
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
-MODULE_LICENSE("GPL");
-
-static int cmdlog = 0;
-static int debug = 0;
-static int channel = 0;
-static int mode = 0;
-
-static u32 ipw_debug_level;
-static int associate = 1;
-static int auto_create = 1;
-static int led = 0;
-static int disable = 0;
-static int bt_coexist = 0;
-static int hwcrypto = 0;
-static int roaming = 1;
-static const char ipw_modes[] = {
- 'a', 'b', 'g', '?'
-};
-static int antenna = CFG_SYS_ANTENNA_BOTH;
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
-#endif
-
-
-#ifdef CONFIG_IPW2200_QOS
-static int qos_enable = 0;
-static int qos_burst_enable = 0;
-static int qos_no_ack_mask = 0;
-static int burst_duration_CCK = 0;
-static int burst_duration_OFDM = 0;
-
-static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
- {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
- QOS_TX3_CW_MIN_OFDM},
- {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
- QOS_TX3_CW_MAX_OFDM},
- {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
- {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
- {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
- QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
-};
-
-static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
- {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
- QOS_TX3_CW_MIN_CCK},
- {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
- QOS_TX3_CW_MAX_CCK},
- {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
- {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
- {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
- QOS_TX3_TXOP_LIMIT_CCK}
-};
-
-static struct ieee80211_qos_parameters def_parameters_OFDM = {
- {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
- DEF_TX3_CW_MIN_OFDM},
- {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
- DEF_TX3_CW_MAX_OFDM},
- {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
- {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
- {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
- DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
-};
-
-static struct ieee80211_qos_parameters def_parameters_CCK = {
- {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
- DEF_TX3_CW_MIN_CCK},
- {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
- DEF_TX3_CW_MAX_CCK},
- {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
- {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
- {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
- DEF_TX3_TXOP_LIMIT_CCK}
-};
-
-static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
-
-static int from_priority_to_tx_queue[] = {
- IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
- IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
-};
-
-static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
-
-static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
- *qos_param);
-static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
- *qos_param);
-#endif /* CONFIG_IPW2200_QOS */
-
-static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
-static void ipw_remove_current_network(struct ipw_priv *priv);
-static void ipw_rx(struct ipw_priv *priv);
-static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
- struct clx2_tx_queue *txq, int qindex);
-static int ipw_queue_reset(struct ipw_priv *priv);
-
-static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
- int len, int sync);
-
-static void ipw_tx_queue_free(struct ipw_priv *);
-
-static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
-static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
-static void ipw_rx_queue_replenish(void *);
-static int ipw_up(struct ipw_priv *);
-static void ipw_bg_up(struct work_struct *work);
-static void ipw_down(struct ipw_priv *);
-static void ipw_bg_down(struct work_struct *work);
-static int ipw_config(struct ipw_priv *);
-static int init_supported_rates(struct ipw_priv *priv,
- struct ipw_supported_rates *prates);
-static void ipw_set_hwcrypto_keys(struct ipw_priv *);
-static void ipw_send_wep_keys(struct ipw_priv *, int);
-
-static int snprint_line(char *buf, size_t count,
- const u8 * data, u32 len, u32 ofs)
-{
- int out, i, j, l;
- char c;
-
- out = snprintf(buf, count, "%08X", ofs);
-
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++)
- out += snprintf(buf + out, count - out, "%02X ",
- data[(i * 8 + j)]);
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- out += snprintf(buf + out, count - out, " ");
- for (l = 0, i = 0; i < 2; i++) {
- out += snprintf(buf + out, count - out, " ");
- for (j = 0; j < 8 && l < len; j++, l++) {
- c = data[(i * 8 + j)];
- if (!isascii(c) || !isprint(c))
- c = '.';
-
- out += snprintf(buf + out, count - out, "%c", c);
- }
-
- for (; j < 8; j++)
- out += snprintf(buf + out, count - out, " ");
- }
-
- return out;
-}
-
-static void printk_buf(int level, const u8 * data, u32 len)
-{
- char line[81];
- u32 ofs = 0;
- if (!(ipw_debug_level & level))
- return;
-
- while (len) {
- snprint_line(line, sizeof(line), &data[ofs],
- min(len, 16U), ofs);
- printk(KERN_DEBUG "%s\n", line);
- ofs += 16;
- len -= min(len, 16U);
- }
-}
-
-static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
-{
- size_t out = size;
- u32 ofs = 0;
- int total = 0;
-
- while (size && len) {
- out = snprint_line(output, size, &data[ofs],
- min_t(size_t, len, 16U), ofs);
-
- ofs += 16;
- output += out;
- size -= out;
- len -= min_t(size_t, len, 16U);
- total += out;
- }
- return total;
-}
-
-/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
-static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
-#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
-
-/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
-static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
-#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
-
-/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
-static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
-static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
-{
- IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
- __LINE__, (u32) (b), (u32) (c));
- _ipw_write_reg8(a, b, c);
-}
-
-/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
-static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
-static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
-{
- IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
- __LINE__, (u32) (b), (u32) (c));
- _ipw_write_reg16(a, b, c);
-}
-
-/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
-static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
-static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
-{
- IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
- __LINE__, (u32) (b), (u32) (c));
- _ipw_write_reg32(a, b, c);
-}
-
-/* 8-bit direct write (low 4K) */
-#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
-
-/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
-#define ipw_write8(ipw, ofs, val) do { \
- IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
- _ipw_write8(ipw, ofs, val); \
- } while (0)
-
-/* 16-bit direct write (low 4K) */
-#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
-
-/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
-#define ipw_write16(ipw, ofs, val) \
- IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
- _ipw_write16(ipw, ofs, val)
-
-/* 32-bit direct write (low 4K) */
-#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
-
-/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
-#define ipw_write32(ipw, ofs, val) \
- IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
- _ipw_write32(ipw, ofs, val)
-
-/* 8-bit direct read (low 4K) */
-#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
-
-/* 8-bit direct read (low 4K), with debug wrapper */
-static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
-{
- IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
- return _ipw_read8(ipw, ofs);
-}
-
-/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
-#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
-
-/* 16-bit direct read (low 4K) */
-#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
-
-/* 16-bit direct read (low 4K), with debug wrapper */
-static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
-{
- IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
- return _ipw_read16(ipw, ofs);
-}
-
-/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
-#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
-
-/* 32-bit direct read (low 4K) */
-#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
-
-/* 32-bit direct read (low 4K), with debug wrapper */
-static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
-{
- IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
- return _ipw_read32(ipw, ofs);
-}
-
-/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
-#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
-
-/* multi-byte read (above 4K), with debug wrapper */
-static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
-static inline void __ipw_read_indirect(const char *f, int l,
- struct ipw_priv *a, u32 b, u8 * c, int d)
-{
- IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
- d);
- _ipw_read_indirect(a, b, c, d);
-}
-
-/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
-#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
-
-/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
-static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
- int num);
-#define ipw_write_indirect(a, b, c, d) \
- IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
- _ipw_write_indirect(a, b, c, d)
-
-/* 32-bit indirect write (above 4K) */
-static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
-{
- IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
- _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
- _ipw_write32(priv, IPW_INDIRECT_DATA, value);
-}
-
-/* 8-bit indirect write (above 4K) */
-static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
-{
- u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
- u32 dif_len = reg - aligned_addr;
-
- IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
-}
-
-/* 16-bit indirect write (above 4K) */
-static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
-{
- u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
- u32 dif_len = (reg - aligned_addr) & (~0x1ul);
-
- IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
-}
-
-/* 8-bit indirect read (above 4K) */
-static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
-{
- u32 word;
- _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
- IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
- word = _ipw_read32(priv, IPW_INDIRECT_DATA);
- return (word >> ((reg & 0x3) * 8)) & 0xff;
-}
-
-/* 32-bit indirect read (above 4K) */
-static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
-{
- u32 value;
-
- IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
-
- _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
- value = _ipw_read32(priv, IPW_INDIRECT_DATA);
- IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
- return value;
-}
-
-/* General purpose, no alignment requirement, iterative (multi-byte) read, */
-/* for area above 1st 4K of SRAM/reg space */
-static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
- int num)
-{
- u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
- u32 dif_len = addr - aligned_addr;
- u32 i;
-
- IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
-
- if (num <= 0) {
- return;
- }
-
- /* Read the first dword (or portion) byte by byte */
- if (unlikely(dif_len)) {
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- /* Start reading at aligned_addr + dif_len */
- for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
- *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
- aligned_addr += 4;
- }
-
- /* Read all of the middle dwords as dwords, with auto-increment */
- _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
- for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
- *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
-
- /* Read the last dword (or portion) byte by byte */
- if (unlikely(num)) {
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- for (i = 0; num > 0; i++, num--)
- *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
- }
-}
-
-/* General purpose, no alignment requirement, iterative (multi-byte) write, */
-/* for area above 1st 4K of SRAM/reg space */
-static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
- int num)
-{
- u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
- u32 dif_len = addr - aligned_addr;
- u32 i;
-
- IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
-
- if (num <= 0) {
- return;
- }
-
- /* Write the first dword (or portion) byte by byte */
- if (unlikely(dif_len)) {
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- /* Start writing at aligned_addr + dif_len */
- for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
- _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
- aligned_addr += 4;
- }
-
- /* Write all of the middle dwords as dwords, with auto-increment */
- _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
- for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
- _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
-
- /* Write the last dword (or portion) byte by byte */
- if (unlikely(num)) {
- _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
- for (i = 0; num > 0; i++, num--, buf++)
- _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
- }
-}
-
-/* General purpose, no alignment requirement, iterative (multi-byte) write, */
-/* for 1st 4K of SRAM/regs space */
-static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
- int num)
-{
- memcpy_toio((priv->hw_base + addr), buf, num);
-}
-
-/* Set bit(s) in low 4K of SRAM/regs */
-static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
-{
- ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
-}
-
-/* Clear bit(s) in low 4K of SRAM/regs */
-static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
-{
- ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
-}
-
-static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
-{
- if (priv->status & STATUS_INT_ENABLED)
- return;
- priv->status |= STATUS_INT_ENABLED;
- ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
-}
-
-static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
-{
- if (!(priv->status & STATUS_INT_ENABLED))
- return;
- priv->status &= ~STATUS_INT_ENABLED;
- ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
-}
-
-static inline void ipw_enable_interrupts(struct ipw_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->irq_lock, flags);
- __ipw_enable_interrupts(priv);
- spin_unlock_irqrestore(&priv->irq_lock, flags);
-}
-
-static inline void ipw_disable_interrupts(struct ipw_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->irq_lock, flags);
- __ipw_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->irq_lock, flags);
-}
-
-static char *ipw_error_desc(u32 val)
-{
- switch (val) {
- case IPW_FW_ERROR_OK:
- return "ERROR_OK";
- case IPW_FW_ERROR_FAIL:
- return "ERROR_FAIL";
- case IPW_FW_ERROR_MEMORY_UNDERFLOW:
- return "MEMORY_UNDERFLOW";
- case IPW_FW_ERROR_MEMORY_OVERFLOW:
- return "MEMORY_OVERFLOW";
- case IPW_FW_ERROR_BAD_PARAM:
- return "BAD_PARAM";
- case IPW_FW_ERROR_BAD_CHECKSUM:
- return "BAD_CHECKSUM";
- case IPW_FW_ERROR_NMI_INTERRUPT:
- return "NMI_INTERRUPT";
- case IPW_FW_ERROR_BAD_DATABASE:
- return "BAD_DATABASE";
- case IPW_FW_ERROR_ALLOC_FAIL:
- return "ALLOC_FAIL";
- case IPW_FW_ERROR_DMA_UNDERRUN:
- return "DMA_UNDERRUN";
- case IPW_FW_ERROR_DMA_STATUS:
- return "DMA_STATUS";
- case IPW_FW_ERROR_DINO_ERROR:
- return "DINO_ERROR";
- case IPW_FW_ERROR_EEPROM_ERROR:
- return "EEPROM_ERROR";
- case IPW_FW_ERROR_SYSASSERT:
- return "SYSASSERT";
- case IPW_FW_ERROR_FATAL_ERROR:
- return "FATAL_ERROR";
- default:
- return "UNKNOWN_ERROR";
- }
-}
-
-static void ipw_dump_error_log(struct ipw_priv *priv,
- struct ipw_fw_error *error)
-{
- u32 i;
-
- if (!error) {
- IPW_ERROR("Error allocating and capturing error log. "
- "Nothing to dump.\n");
- return;
- }
-
- IPW_ERROR("Start IPW Error Log Dump:\n");
- IPW_ERROR("Status: 0x%08X, Config: %08X\n",
- error->status, error->config);
-
- for (i = 0; i < error->elem_len; i++)
- IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
- ipw_error_desc(error->elem[i].desc),
- error->elem[i].time,
- error->elem[i].blink1,
- error->elem[i].blink2,
- error->elem[i].link1,
- error->elem[i].link2, error->elem[i].data);
- for (i = 0; i < error->log_len; i++)
- IPW_ERROR("%i\t0x%08x\t%i\n",
- error->log[i].time,
- error->log[i].data, error->log[i].event);
-}
-
-static inline int ipw_is_init(struct ipw_priv *priv)
-{
- return (priv->status & STATUS_INIT) ? 1 : 0;
-}
-
-static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
-{
- u32 addr, field_info, field_len, field_count, total_len;
-
- IPW_DEBUG_ORD("ordinal = %i\n", ord);
-
- if (!priv || !val || !len) {
- IPW_DEBUG_ORD("Invalid argument\n");
- return -EINVAL;
- }
-
- /* verify device ordinal tables have been initialized */
- if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
- IPW_DEBUG_ORD("Access ordinals before initialization\n");
- return -EINVAL;
- }
-
- switch (IPW_ORD_TABLE_ID_MASK & ord) {
- case IPW_ORD_TABLE_0_MASK:
- /*
- * TABLE 0: Direct access to a table of 32 bit values
- *
- * This is a very simple table with the data directly
- * read from the table
- */
-
- /* remove the table id from the ordinal */
- ord &= IPW_ORD_TABLE_VALUE_MASK;
-
- /* boundary check */
- if (ord > priv->table0_len) {
- IPW_DEBUG_ORD("ordinal value (%i) longer then "
- "max (%i)\n", ord, priv->table0_len);
- return -EINVAL;
- }
-
- /* verify we have enough room to store the value */
- if (*len < sizeof(u32)) {
- IPW_DEBUG_ORD("ordinal buffer length too small, "
- "need %zd\n", sizeof(u32));
- return -EINVAL;
- }
-
- IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
- ord, priv->table0_addr + (ord << 2));
-
- *len = sizeof(u32);
- ord <<= 2;
- *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
- break;
-
- case IPW_ORD_TABLE_1_MASK:
- /*
- * TABLE 1: Indirect access to a table of 32 bit values
- *
- * This is a fairly large table of u32 values each
- * representing starting addr for the data (which is
- * also a u32)
- */
-
- /* remove the table id from the ordinal */
- ord &= IPW_ORD_TABLE_VALUE_MASK;
-
- /* boundary check */
- if (ord > priv->table1_len) {
- IPW_DEBUG_ORD("ordinal value too long\n");
- return -EINVAL;
- }
-
- /* verify we have enough room to store the value */
- if (*len < sizeof(u32)) {
- IPW_DEBUG_ORD("ordinal buffer length too small, "
- "need %zd\n", sizeof(u32));
- return -EINVAL;
- }
-
- *((u32 *) val) =
- ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
- *len = sizeof(u32);
- break;
-
- case IPW_ORD_TABLE_2_MASK:
- /*
- * TABLE 2: Indirect access to a table of variable sized values
- *
- * This table consist of six values, each containing
- * - dword containing the starting offset of the data
- * - dword containing the lengh in the first 16bits
- * and the count in the second 16bits
- */
-
- /* remove the table id from the ordinal */
- ord &= IPW_ORD_TABLE_VALUE_MASK;
-
- /* boundary check */
- if (ord > priv->table2_len) {
- IPW_DEBUG_ORD("ordinal value too long\n");
- return -EINVAL;
- }
-
- /* get the address of statistic */
- addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
-
- /* get the second DW of statistics ;
- * two 16-bit words - first is length, second is count */
- field_info =
- ipw_read_reg32(priv,
- priv->table2_addr + (ord << 3) +
- sizeof(u32));
-
- /* get each entry length */
- field_len = *((u16 *) & field_info);
-
- /* get number of entries */
- field_count = *(((u16 *) & field_info) + 1);
-
- /* abort if not enought memory */
- total_len = field_len * field_count;
- if (total_len > *len) {
- *len = total_len;
- return -EINVAL;
- }
-
- *len = total_len;
- if (!total_len)
- return 0;
-
- IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
- "field_info = 0x%08x\n",
- addr, total_len, field_info);
- ipw_read_indirect(priv, addr, val, total_len);
- break;
-
- default:
- IPW_DEBUG_ORD("Invalid ordinal!\n");
- return -EINVAL;
-
- }
-
- return 0;
-}
-
-static void ipw_init_ordinals(struct ipw_priv *priv)
-{
- priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
- priv->table0_len = ipw_read32(priv, priv->table0_addr);
-
- IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
- priv->table0_addr, priv->table0_len);
-
- priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
- priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
-
- IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
- priv->table1_addr, priv->table1_len);
-
- priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
- priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
- priv->table2_len &= 0x0000ffff; /* use first two bytes */
-
- IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
- priv->table2_addr, priv->table2_len);
-
-}
-
-static u32 ipw_register_toggle(u32 reg)
-{
- reg &= ~IPW_START_STANDBY;
- if (reg & IPW_GATE_ODMA)
- reg &= ~IPW_GATE_ODMA;
- if (reg & IPW_GATE_IDMA)
- reg &= ~IPW_GATE_IDMA;
- if (reg & IPW_GATE_ADMA)
- reg &= ~IPW_GATE_ADMA;
- return reg;
-}
-
-/*
- * LED behavior:
- * - On radio ON, turn on any LEDs that require to be on during start
- * - On initialization, start unassociated blink
- * - On association, disable unassociated blink
- * - On disassociation, start unassociated blink
- * - On radio OFF, turn off any LEDs started during radio on
- *
- */
-#define LD_TIME_LINK_ON msecs_to_jiffies(300)
-#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
-#define LD_TIME_ACT_ON msecs_to_jiffies(250)
-
-static void ipw_led_link_on(struct ipw_priv *priv)
-{
- unsigned long flags;
- u32 led;
-
- /* If configured to not use LEDs, or nic_type is 1,
- * then we don't toggle a LINK led */
- if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
- return;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (!(priv->status & STATUS_RF_KILL_MASK) &&
- !(priv->status & STATUS_LED_LINK_ON)) {
- IPW_DEBUG_LED("Link LED On\n");
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- led |= priv->led_association_on;
-
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- priv->status |= STATUS_LED_LINK_ON;
-
- /* If we aren't associated, schedule turning the LED off */
- if (!(priv->status & STATUS_ASSOCIATED))
- queue_delayed_work(priv->workqueue,
- &priv->led_link_off,
- LD_TIME_LINK_ON);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_bg_led_link_on(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, led_link_on.work);
- mutex_lock(&priv->mutex);
- ipw_led_link_on(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_led_link_off(struct ipw_priv *priv)
-{
- unsigned long flags;
- u32 led;
-
- /* If configured not to use LEDs, or nic type is 1,
- * then we don't goggle the LINK led. */
- if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
- return;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (priv->status & STATUS_LED_LINK_ON) {
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- led &= priv->led_association_off;
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- IPW_DEBUG_LED("Link LED Off\n");
-
- priv->status &= ~STATUS_LED_LINK_ON;
-
- /* If we aren't associated and the radio is on, schedule
- * turning the LED on (blink while unassociated) */
- if (!(priv->status & STATUS_RF_KILL_MASK) &&
- !(priv->status & STATUS_ASSOCIATED))
- queue_delayed_work(priv->workqueue, &priv->led_link_on,
- LD_TIME_LINK_OFF);
-
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_bg_led_link_off(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, led_link_off.work);
- mutex_lock(&priv->mutex);
- ipw_led_link_off(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void __ipw_led_activity_on(struct ipw_priv *priv)
-{
- u32 led;
-
- if (priv->config & CFG_NO_LED)
- return;
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return;
-
- if (!(priv->status & STATUS_LED_ACT_ON)) {
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- led |= priv->led_activity_on;
-
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- IPW_DEBUG_LED("Activity LED On\n");
-
- priv->status |= STATUS_LED_ACT_ON;
-
- cancel_delayed_work(&priv->led_act_off);
- queue_delayed_work(priv->workqueue, &priv->led_act_off,
- LD_TIME_ACT_ON);
- } else {
- /* Reschedule LED off for full time period */
- cancel_delayed_work(&priv->led_act_off);
- queue_delayed_work(priv->workqueue, &priv->led_act_off,
- LD_TIME_ACT_ON);
- }
-}
-
-#if 0
-void ipw_led_activity_on(struct ipw_priv *priv)
-{
- unsigned long flags;
- spin_lock_irqsave(&priv->lock, flags);
- __ipw_led_activity_on(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-#endif /* 0 */
-
-static void ipw_led_activity_off(struct ipw_priv *priv)
-{
- unsigned long flags;
- u32 led;
-
- if (priv->config & CFG_NO_LED)
- return;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (priv->status & STATUS_LED_ACT_ON) {
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- led &= priv->led_activity_off;
-
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- IPW_DEBUG_LED("Activity LED Off\n");
-
- priv->status &= ~STATUS_LED_ACT_ON;
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_bg_led_activity_off(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, led_act_off.work);
- mutex_lock(&priv->mutex);
- ipw_led_activity_off(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_led_band_on(struct ipw_priv *priv)
-{
- unsigned long flags;
- u32 led;
-
- /* Only nic type 1 supports mode LEDs */
- if (priv->config & CFG_NO_LED ||
- priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
- return;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- if (priv->assoc_network->mode == IEEE_A) {
- led |= priv->led_ofdm_on;
- led &= priv->led_association_off;
- IPW_DEBUG_LED("Mode LED On: 802.11a\n");
- } else if (priv->assoc_network->mode == IEEE_G) {
- led |= priv->led_ofdm_on;
- led |= priv->led_association_on;
- IPW_DEBUG_LED("Mode LED On: 802.11g\n");
- } else {
- led &= priv->led_ofdm_off;
- led |= priv->led_association_on;
- IPW_DEBUG_LED("Mode LED On: 802.11b\n");
- }
-
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_led_band_off(struct ipw_priv *priv)
-{
- unsigned long flags;
- u32 led;
-
- /* Only nic type 1 supports mode LEDs */
- if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
- return;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- led = ipw_read_reg32(priv, IPW_EVENT_REG);
- led &= priv->led_ofdm_off;
- led &= priv->led_association_off;
-
- led = ipw_register_toggle(led);
-
- IPW_DEBUG_LED("Reg: 0x%08X\n", led);
- ipw_write_reg32(priv, IPW_EVENT_REG, led);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_led_radio_on(struct ipw_priv *priv)
-{
- ipw_led_link_on(priv);
-}
-
-static void ipw_led_radio_off(struct ipw_priv *priv)
-{
- ipw_led_activity_off(priv);
- ipw_led_link_off(priv);
-}
-
-static void ipw_led_link_up(struct ipw_priv *priv)
-{
- /* Set the Link Led on for all nic types */
- ipw_led_link_on(priv);
-}
-
-static void ipw_led_link_down(struct ipw_priv *priv)
-{
- ipw_led_activity_off(priv);
- ipw_led_link_off(priv);
-
- if (priv->status & STATUS_RF_KILL_MASK)
- ipw_led_radio_off(priv);
-}
-
-static void ipw_led_init(struct ipw_priv *priv)
-{
- priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
-
- /* Set the default PINs for the link and activity leds */
- priv->led_activity_on = IPW_ACTIVITY_LED;
- priv->led_activity_off = ~(IPW_ACTIVITY_LED);
-
- priv->led_association_on = IPW_ASSOCIATED_LED;
- priv->led_association_off = ~(IPW_ASSOCIATED_LED);
-
- /* Set the default PINs for the OFDM leds */
- priv->led_ofdm_on = IPW_OFDM_LED;
- priv->led_ofdm_off = ~(IPW_OFDM_LED);
-
- switch (priv->nic_type) {
- case EEPROM_NIC_TYPE_1:
- /* In this NIC type, the LEDs are reversed.... */
- priv->led_activity_on = IPW_ASSOCIATED_LED;
- priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
- priv->led_association_on = IPW_ACTIVITY_LED;
- priv->led_association_off = ~(IPW_ACTIVITY_LED);
-
- if (!(priv->config & CFG_NO_LED))
- ipw_led_band_on(priv);
-
- /* And we don't blink link LEDs for this nic, so
- * just return here */
- return;
-
- case EEPROM_NIC_TYPE_3:
- case EEPROM_NIC_TYPE_2:
- case EEPROM_NIC_TYPE_4:
- case EEPROM_NIC_TYPE_0:
- break;
-
- default:
- IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
- priv->nic_type);
- priv->nic_type = EEPROM_NIC_TYPE_0;
- break;
- }
-
- if (!(priv->config & CFG_NO_LED)) {
- if (priv->status & STATUS_ASSOCIATED)
- ipw_led_link_on(priv);
- else
- ipw_led_link_off(priv);
- }
-}
-
-static void ipw_led_shutdown(struct ipw_priv *priv)
-{
- ipw_led_activity_off(priv);
- ipw_led_link_off(priv);
- ipw_led_band_off(priv);
- cancel_delayed_work(&priv->led_link_on);
- cancel_delayed_work(&priv->led_link_off);
- cancel_delayed_work(&priv->led_act_off);
-}
-
-/*
- * The following adds a new attribute to the sysfs representation
- * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
- * used for controling the debug level.
- *
- * See the level definitions in ipw for details.
- */
-static ssize_t show_debug_level(struct device_driver *d, char *buf)
-{
- return sprintf(buf, "0x%08X\n", ipw_debug_level);
-}
-
-static ssize_t store_debug_level(struct device_driver *d, const char *buf,
- size_t count)
-{
- char *p = (char *)buf;
- u32 val;
-
- if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
- p++;
- if (p[0] == 'x' || p[0] == 'X')
- p++;
- val = simple_strtoul(p, &p, 16);
- } else
- val = simple_strtoul(p, &p, 10);
- if (p == buf)
- printk(KERN_INFO DRV_NAME
- ": %s is not in hex or decimal form.\n", buf);
- else
- ipw_debug_level = val;
-
- return strnlen(buf, count);
-}
-
-static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
- show_debug_level, store_debug_level);
-
-static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
-{
- /* length = 1st dword in log */
- return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
-}
-
-static void ipw_capture_event_log(struct ipw_priv *priv,
- u32 log_len, struct ipw_event *log)
-{
- u32 base;
-
- if (log_len) {
- base = ipw_read32(priv, IPW_EVENT_LOG);
- ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
- (u8 *) log, sizeof(*log) * log_len);
- }
-}
-
-static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
-{
- struct ipw_fw_error *error;
- u32 log_len = ipw_get_event_log_len(priv);
- u32 base = ipw_read32(priv, IPW_ERROR_LOG);
- u32 elem_len = ipw_read_reg32(priv, base);
-
- error = kmalloc(sizeof(*error) +
- sizeof(*error->elem) * elem_len +
- sizeof(*error->log) * log_len, GFP_ATOMIC);
- if (!error) {
- IPW_ERROR("Memory allocation for firmware error log "
- "failed.\n");
- return NULL;
- }
- error->jiffies = jiffies;
- error->status = priv->status;
- error->config = priv->config;
- error->elem_len = elem_len;
- error->log_len = log_len;
- error->elem = (struct ipw_error_elem *)error->payload;
- error->log = (struct ipw_event *)(error->elem + elem_len);
-
- ipw_capture_event_log(priv, log_len, error->log);
-
- if (elem_len)
- ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
- sizeof(*error->elem) * elem_len);
-
- return error;
-}
-
-static ssize_t show_event_log(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- u32 log_len = ipw_get_event_log_len(priv);
- u32 log_size;
- struct ipw_event *log;
- u32 len = 0, i;
-
- /* not using min() because of its strict type checking */
- log_size = PAGE_SIZE / sizeof(*log) > log_len ?
- sizeof(*log) * log_len : PAGE_SIZE;
- log = kzalloc(log_size, GFP_KERNEL);
- if (!log) {
- IPW_ERROR("Unable to allocate memory for log\n");
- return 0;
- }
- log_len = log_size / sizeof(*log);
- ipw_capture_event_log(priv, log_len, log);
-
- len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
- for (i = 0; i < log_len; i++)
- len += snprintf(buf + len, PAGE_SIZE - len,
- "\n%08X%08X%08X",
- log[i].time, log[i].event, log[i].data);
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
- kfree(log);
- return len;
-}
-
-static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
-
-static ssize_t show_error(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- u32 len = 0, i;
- if (!priv->error)
- return 0;
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%08lX%08X%08X%08X",
- priv->error->jiffies,
- priv->error->status,
- priv->error->config, priv->error->elem_len);
- for (i = 0; i < priv->error->elem_len; i++)
- len += snprintf(buf + len, PAGE_SIZE - len,
- "\n%08X%08X%08X%08X%08X%08X%08X",
- priv->error->elem[i].time,
- priv->error->elem[i].desc,
- priv->error->elem[i].blink1,
- priv->error->elem[i].blink2,
- priv->error->elem[i].link1,
- priv->error->elem[i].link2,
- priv->error->elem[i].data);
-
- len += snprintf(buf + len, PAGE_SIZE - len,
- "\n%08X", priv->error->log_len);
- for (i = 0; i < priv->error->log_len; i++)
- len += snprintf(buf + len, PAGE_SIZE - len,
- "\n%08X%08X%08X",
- priv->error->log[i].time,
- priv->error->log[i].event,
- priv->error->log[i].data);
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
- return len;
-}
-
-static ssize_t clear_error(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
-
- kfree(priv->error);
- priv->error = NULL;
- return count;
-}
-
-static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
-
-static ssize_t show_cmd_log(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- u32 len = 0, i;
- if (!priv->cmdlog)
- return 0;
- for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
- (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
- i = (i + 1) % priv->cmdlog_len) {
- len +=
- snprintf(buf + len, PAGE_SIZE - len,
- "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
- priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
- priv->cmdlog[i].cmd.len);
- len +=
- snprintk_buf(buf + len, PAGE_SIZE - len,
- (u8 *) priv->cmdlog[i].cmd.param,
- priv->cmdlog[i].cmd.len);
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
- }
- len += snprintf(buf + len, PAGE_SIZE - len, "\n");
- return len;
-}
-
-static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-static void ipw_prom_free(struct ipw_priv *priv);
-static int ipw_prom_alloc(struct ipw_priv *priv);
-static ssize_t store_rtap_iface(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- int rc = 0;
-
- if (count < 1)
- return -EINVAL;
-
- switch (buf[0]) {
- case '0':
- if (!rtap_iface)
- return count;
-
- if (netif_running(priv->prom_net_dev)) {
- IPW_WARNING("Interface is up. Cannot unregister.\n");
- return count;
- }
-
- ipw_prom_free(priv);
- rtap_iface = 0;
- break;
-
- case '1':
- if (rtap_iface)
- return count;
-
- rc = ipw_prom_alloc(priv);
- if (!rc)
- rtap_iface = 1;
- break;
-
- default:
- return -EINVAL;
- }
-
- if (rc) {
- IPW_ERROR("Failed to register promiscuous network "
- "device (error %d).\n", rc);
- }
-
- return count;
-}
-
-static ssize_t show_rtap_iface(struct device *d,
- struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- if (rtap_iface)
- return sprintf(buf, "%s", priv->prom_net_dev->name);
- else {
- buf[0] = '-';
- buf[1] = '1';
- buf[2] = '\0';
- return 3;
- }
-}
-
-static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface,
- store_rtap_iface);
-
-static ssize_t store_rtap_filter(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
-
- if (!priv->prom_priv) {
- IPW_ERROR("Attempting to set filter without "
- "rtap_iface enabled.\n");
- return -EPERM;
- }
-
- priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
-
- IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
- BIT_ARG16(priv->prom_priv->filter));
-
- return count;
-}
-
-static ssize_t show_rtap_filter(struct device *d,
- struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "0x%04X",
- priv->prom_priv ? priv->prom_priv->filter : 0);
-}
-
-static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter,
- store_rtap_filter);
-#endif
-
-static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "%d\n", priv->ieee->scan_age);
-}
-
-static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- struct net_device *dev = priv->net_dev;
- char buffer[] = "00000000";
- unsigned long len =
- (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
- unsigned long val;
- char *p = buffer;
-
- IPW_DEBUG_INFO("enter\n");
-
- strncpy(buffer, buf, len);
- buffer[len] = 0;
-
- if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
- p++;
- if (p[0] == 'x' || p[0] == 'X')
- p++;
- val = simple_strtoul(p, &p, 16);
- } else
- val = simple_strtoul(p, &p, 10);
- if (p == buffer) {
- IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
- } else {
- priv->ieee->scan_age = val;
- IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
- }
-
- IPW_DEBUG_INFO("exit\n");
- return len;
-}
-
-static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
-
-static ssize_t show_led(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
-}
-
-static ssize_t store_led(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
-
- IPW_DEBUG_INFO("enter\n");
-
- if (count == 0)
- return 0;
-
- if (*buf == 0) {
- IPW_DEBUG_LED("Disabling LED control.\n");
- priv->config |= CFG_NO_LED;
- ipw_led_shutdown(priv);
- } else {
- IPW_DEBUG_LED("Enabling LED control.\n");
- priv->config &= ~CFG_NO_LED;
- ipw_led_init(priv);
- }
-
- IPW_DEBUG_INFO("exit\n");
- return count;
-}
-
-static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
-
-static ssize_t show_status(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw_priv *p = d->driver_data;
- return sprintf(buf, "0x%08x\n", (int)p->status);
-}
-
-static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
-
-static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *p = d->driver_data;
- return sprintf(buf, "0x%08x\n", (int)p->config);
-}
-
-static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
-
-static ssize_t show_nic_type(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct ipw_priv *priv = d->driver_data;
- return sprintf(buf, "TYPE: %d\n", priv->nic_type);
-}
-
-static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
-
-static ssize_t show_ucode_version(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u32 len = sizeof(u32), tmp = 0;
- struct ipw_priv *p = d->driver_data;
-
- if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
- return 0;
-
- return sprintf(buf, "0x%08x\n", tmp);
-}
-
-static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
-
-static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- u32 len = sizeof(u32), tmp = 0;
- struct ipw_priv *p = d->driver_data;
-
- if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
- return 0;
-
- return sprintf(buf, "0x%08x\n", tmp);
-}
-
-static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
-
-/*
- * Add a device attribute to view/control the delay between eeprom
- * operations.
- */
-static ssize_t show_eeprom_delay(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
- return sprintf(buf, "%i\n", n);
-}
-static ssize_t store_eeprom_delay(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *p = d->driver_data;
- sscanf(buf, "%i", &p->eeprom_delay);
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
- show_eeprom_delay, store_eeprom_delay);
-
-static ssize_t show_command_event_reg(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u32 reg = 0;
- struct ipw_priv *p = d->driver_data;
-
- reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
- return sprintf(buf, "0x%08x\n", reg);
-}
-static ssize_t store_command_event_reg(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- u32 reg;
- struct ipw_priv *p = d->driver_data;
-
- sscanf(buf, "%x", &reg);
- ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
- show_command_event_reg, store_command_event_reg);
-
-static ssize_t show_mem_gpio_reg(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u32 reg = 0;
- struct ipw_priv *p = d->driver_data;
-
- reg = ipw_read_reg32(p, 0x301100);
- return sprintf(buf, "0x%08x\n", reg);
-}
-static ssize_t store_mem_gpio_reg(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- u32 reg;
- struct ipw_priv *p = d->driver_data;
-
- sscanf(buf, "%x", &reg);
- ipw_write_reg32(p, 0x301100, reg);
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
- show_mem_gpio_reg, store_mem_gpio_reg);
-
-static ssize_t show_indirect_dword(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u32 reg = 0;
- struct ipw_priv *priv = d->driver_data;
-
- if (priv->status & STATUS_INDIRECT_DWORD)
- reg = ipw_read_reg32(priv, priv->indirect_dword);
- else
- reg = 0;
-
- return sprintf(buf, "0x%08x\n", reg);
-}
-static ssize_t store_indirect_dword(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = d->driver_data;
-
- sscanf(buf, "%x", &priv->indirect_dword);
- priv->status |= STATUS_INDIRECT_DWORD;
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
- show_indirect_dword, store_indirect_dword);
-
-static ssize_t show_indirect_byte(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u8 reg = 0;
- struct ipw_priv *priv = d->driver_data;
-
- if (priv->status & STATUS_INDIRECT_BYTE)
- reg = ipw_read_reg8(priv, priv->indirect_byte);
- else
- reg = 0;
-
- return sprintf(buf, "0x%02x\n", reg);
-}
-static ssize_t store_indirect_byte(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = d->driver_data;
-
- sscanf(buf, "%x", &priv->indirect_byte);
- priv->status |= STATUS_INDIRECT_BYTE;
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
- show_indirect_byte, store_indirect_byte);
-
-static ssize_t show_direct_dword(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- u32 reg = 0;
- struct ipw_priv *priv = d->driver_data;
-
- if (priv->status & STATUS_DIRECT_DWORD)
- reg = ipw_read32(priv, priv->direct_dword);
- else
- reg = 0;
-
- return sprintf(buf, "0x%08x\n", reg);
-}
-static ssize_t store_direct_dword(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = d->driver_data;
-
- sscanf(buf, "%x", &priv->direct_dword);
- priv->status |= STATUS_DIRECT_DWORD;
- return strnlen(buf, count);
-}
-
-static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
- show_direct_dword, store_direct_dword);
-
-static int rf_kill_active(struct ipw_priv *priv)
-{
- if (0 == (ipw_read32(priv, 0x30) & 0x10000))
- priv->status |= STATUS_RF_KILL_HW;
- else
- priv->status &= ~STATUS_RF_KILL_HW;
-
- return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
-}
-
-static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- /* 0 - RF kill not enabled
- 1 - SW based RF kill active (sysfs)
- 2 - HW based RF kill active
- 3 - Both HW and SW baed RF kill active */
- struct ipw_priv *priv = d->driver_data;
- int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
- (rf_kill_active(priv) ? 0x2 : 0x0);
- return sprintf(buf, "%i\n", val);
-}
-
-static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
-{
- if ((disable_radio ? 1 : 0) ==
- ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
- return 0;
-
- IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
- disable_radio ? "OFF" : "ON");
-
- if (disable_radio) {
- priv->status |= STATUS_RF_KILL_SW;
-
- if (priv->workqueue) {
- cancel_delayed_work(&priv->request_scan);
- cancel_delayed_work(&priv->request_direct_scan);
- cancel_delayed_work(&priv->request_passive_scan);
- cancel_delayed_work(&priv->scan_event);
- }
- queue_work(priv->workqueue, &priv->down);
- } else {
- priv->status &= ~STATUS_RF_KILL_SW;
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("Can not turn radio back on - "
- "disabled by HW switch\n");
- /* Make sure the RF_KILL check timer is running */
- cancel_delayed_work(&priv->rf_kill);
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- round_jiffies_relative(2 * HZ));
- } else
- queue_work(priv->workqueue, &priv->up);
- }
-
- return 1;
-}
-
-static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = d->driver_data;
-
- ipw_radio_kill_sw(priv, buf[0] == '1');
-
- return count;
-}
-
-static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
-
-static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
- int pos = 0, len = 0;
- if (priv->config & CFG_SPEED_SCAN) {
- while (priv->speed_scan[pos] != 0)
- len += sprintf(&buf[len], "%d ",
- priv->speed_scan[pos++]);
- return len + sprintf(&buf[len], "\n");
- }
-
- return sprintf(buf, "0\n");
-}
-
-static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
- int channel, pos = 0;
- const char *p = buf;
-
- /* list of space separated channels to scan, optionally ending with 0 */
- while ((channel = simple_strtol(p, NULL, 0))) {
- if (pos == MAX_SPEED_SCAN - 1) {
- priv->speed_scan[pos] = 0;
- break;
- }
-
- if (ieee80211_is_valid_channel(priv->ieee, channel))
- priv->speed_scan[pos++] = channel;
- else
- IPW_WARNING("Skipping invalid channel request: %d\n",
- channel);
- p = strchr(p, ' ');
- if (!p)
- break;
- while (*p == ' ' || *p == '\t')
- p++;
- }
-
- if (pos == 0)
- priv->config &= ~CFG_SPEED_SCAN;
- else {
- priv->speed_scan_pos = 0;
- priv->config |= CFG_SPEED_SCAN;
- }
-
- return count;
-}
-
-static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
- store_speed_scan);
-
-static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
- return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
-}
-
-static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
- if (buf[0] == '1')
- priv->config |= CFG_NET_STATS;
- else
- priv->config &= ~CFG_NET_STATS;
-
- return count;
-}
-
-static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
- show_net_stats, store_net_stats);
-
-static ssize_t show_channels(struct device *d,
- struct device_attribute *attr,
- char *buf)
-{
- struct ipw_priv *priv = dev_get_drvdata(d);
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
- int len = 0, i;
-
- len = sprintf(&buf[len],
- "Displaying %d channels in 2.4Ghz band "
- "(802.11bg):\n", geo->bg_channels);
-
- for (i = 0; i < geo->bg_channels; i++) {
- len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
- geo->bg[i].channel,
- geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ?
- " (radar spectrum)" : "",
- ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) ||
- (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT))
- ? "" : ", IBSS",
- geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
- "passive only" : "active/passive",
- geo->bg[i].flags & IEEE80211_CH_B_ONLY ?
- "B" : "B/G");
- }
-
- len += sprintf(&buf[len],
- "Displaying %d channels in 5.2Ghz band "
- "(802.11a):\n", geo->a_channels);
- for (i = 0; i < geo->a_channels; i++) {
- len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
- geo->a[i].channel,
- geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ?
- " (radar spectrum)" : "",
- ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) ||
- (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT))
- ? "" : ", IBSS",
- geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
- "passive only" : "active/passive");
- }
-
- return len;
-}
-
-static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
-
-static void notify_wx_assoc_event(struct ipw_priv *priv)
-{
- union iwreq_data wrqu;
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- if (priv->status & STATUS_ASSOCIATED)
- memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
- else
- memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
- wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-static void ipw_irq_tasklet(struct ipw_priv *priv)
-{
- u32 inta, inta_mask, handled = 0;
- unsigned long flags;
- int rc = 0;
-
- spin_lock_irqsave(&priv->irq_lock, flags);
-
- inta = ipw_read32(priv, IPW_INTA_RW);
- inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
- inta &= (IPW_INTA_MASK_ALL & inta_mask);
-
- /* Add any cached INTA values that need to be handled */
- inta |= priv->isr_inta;
-
- spin_unlock_irqrestore(&priv->irq_lock, flags);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* handle all the justifications for the interrupt */
- if (inta & IPW_INTA_BIT_RX_TRANSFER) {
- ipw_rx(priv);
- handled |= IPW_INTA_BIT_RX_TRANSFER;
- }
-
- if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
- IPW_DEBUG_HC("Command completed.\n");
- rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
- priv->status &= ~STATUS_HCMD_ACTIVE;
- wake_up_interruptible(&priv->wait_command_queue);
- handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
- }
-
- if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
- IPW_DEBUG_TX("TX_QUEUE_1\n");
- rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
- handled |= IPW_INTA_BIT_TX_QUEUE_1;
- }
-
- if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
- IPW_DEBUG_TX("TX_QUEUE_2\n");
- rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
- handled |= IPW_INTA_BIT_TX_QUEUE_2;
- }
-
- if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
- IPW_DEBUG_TX("TX_QUEUE_3\n");
- rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
- handled |= IPW_INTA_BIT_TX_QUEUE_3;
- }
-
- if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
- IPW_DEBUG_TX("TX_QUEUE_4\n");
- rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
- handled |= IPW_INTA_BIT_TX_QUEUE_4;
- }
-
- if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
- IPW_WARNING("STATUS_CHANGE\n");
- handled |= IPW_INTA_BIT_STATUS_CHANGE;
- }
-
- if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
- IPW_WARNING("TX_PERIOD_EXPIRED\n");
- handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
- }
-
- if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
- IPW_WARNING("HOST_CMD_DONE\n");
- handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
- }
-
- if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
- IPW_WARNING("FW_INITIALIZATION_DONE\n");
- handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
- }
-
- if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
- IPW_WARNING("PHY_OFF_DONE\n");
- handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
- }
-
- if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
- IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
- priv->status |= STATUS_RF_KILL_HW;
- wake_up_interruptible(&priv->wait_command_queue);
- priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
- cancel_delayed_work(&priv->request_scan);
- cancel_delayed_work(&priv->request_direct_scan);
- cancel_delayed_work(&priv->request_passive_scan);
- cancel_delayed_work(&priv->scan_event);
- schedule_work(&priv->link_down);
- queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
- handled |= IPW_INTA_BIT_RF_KILL_DONE;
- }
-
- if (inta & IPW_INTA_BIT_FATAL_ERROR) {
- IPW_WARNING("Firmware error detected. Restarting.\n");
- if (priv->error) {
- IPW_DEBUG_FW("Sysfs 'error' log already exists.\n");
- if (ipw_debug_level & IPW_DL_FW_ERRORS) {
- struct ipw_fw_error *error =
- ipw_alloc_error_log(priv);
- ipw_dump_error_log(priv, error);
- kfree(error);
- }
- } else {
- priv->error = ipw_alloc_error_log(priv);
- if (priv->error)
- IPW_DEBUG_FW("Sysfs 'error' log captured.\n");
- else
- IPW_DEBUG_FW("Error allocating sysfs 'error' "
- "log.\n");
- if (ipw_debug_level & IPW_DL_FW_ERRORS)
- ipw_dump_error_log(priv, priv->error);
- }
-
- /* XXX: If hardware encryption is for WPA/WPA2,
- * we have to notify the supplicant. */
- if (priv->ieee->sec.encrypt) {
- priv->status &= ~STATUS_ASSOCIATED;
- notify_wx_assoc_event(priv);
- }
-
- /* Keep the restart process from trying to send host
- * commands by clearing the INIT status bit */
- priv->status &= ~STATUS_INIT;
-
- /* Cancel currently queued command. */
- priv->status &= ~STATUS_HCMD_ACTIVE;
- wake_up_interruptible(&priv->wait_command_queue);
-
- queue_work(priv->workqueue, &priv->adapter_restart);
- handled |= IPW_INTA_BIT_FATAL_ERROR;
- }
-
- if (inta & IPW_INTA_BIT_PARITY_ERROR) {
- IPW_ERROR("Parity error\n");
- handled |= IPW_INTA_BIT_PARITY_ERROR;
- }
-
- if (handled != inta) {
- IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* enable all interrupts */
- ipw_enable_interrupts(priv);
-}
-
-#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
-static char *get_cmd_string(u8 cmd)
-{
- switch (cmd) {
- IPW_CMD(HOST_COMPLETE);
- IPW_CMD(POWER_DOWN);
- IPW_CMD(SYSTEM_CONFIG);
- IPW_CMD(MULTICAST_ADDRESS);
- IPW_CMD(SSID);
- IPW_CMD(ADAPTER_ADDRESS);
- IPW_CMD(PORT_TYPE);
- IPW_CMD(RTS_THRESHOLD);
- IPW_CMD(FRAG_THRESHOLD);
- IPW_CMD(POWER_MODE);
- IPW_CMD(WEP_KEY);
- IPW_CMD(TGI_TX_KEY);
- IPW_CMD(SCAN_REQUEST);
- IPW_CMD(SCAN_REQUEST_EXT);
- IPW_CMD(ASSOCIATE);
- IPW_CMD(SUPPORTED_RATES);
- IPW_CMD(SCAN_ABORT);
- IPW_CMD(TX_FLUSH);
- IPW_CMD(QOS_PARAMETERS);
- IPW_CMD(DINO_CONFIG);
- IPW_CMD(RSN_CAPABILITIES);
- IPW_CMD(RX_KEY);
- IPW_CMD(CARD_DISABLE);
- IPW_CMD(SEED_NUMBER);
- IPW_CMD(TX_POWER);
- IPW_CMD(COUNTRY_INFO);
- IPW_CMD(AIRONET_INFO);
- IPW_CMD(AP_TX_POWER);
- IPW_CMD(CCKM_INFO);
- IPW_CMD(CCX_VER_INFO);
- IPW_CMD(SET_CALIBRATION);
- IPW_CMD(SENSITIVITY_CALIB);
- IPW_CMD(RETRY_LIMIT);
- IPW_CMD(IPW_PRE_POWER_DOWN);
- IPW_CMD(VAP_BEACON_TEMPLATE);
- IPW_CMD(VAP_DTIM_PERIOD);
- IPW_CMD(EXT_SUPPORTED_RATES);
- IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
- IPW_CMD(VAP_QUIET_INTERVALS);
- IPW_CMD(VAP_CHANNEL_SWITCH);
- IPW_CMD(VAP_MANDATORY_CHANNELS);
- IPW_CMD(VAP_CELL_PWR_LIMIT);
- IPW_CMD(VAP_CF_PARAM_SET);
- IPW_CMD(VAP_SET_BEACONING_STATE);
- IPW_CMD(MEASUREMENT);
- IPW_CMD(POWER_CAPABILITY);
- IPW_CMD(SUPPORTED_CHANNELS);
- IPW_CMD(TPC_REPORT);
- IPW_CMD(WME_INFO);
- IPW_CMD(PRODUCTION_COMMAND);
- default:
- return "UNKNOWN";
- }
-}
-
-#define HOST_COMPLETE_TIMEOUT HZ
-
-static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
-{
- int rc = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- if (priv->status & STATUS_HCMD_ACTIVE) {
- IPW_ERROR("Failed to send %s: Already sending a command.\n",
- get_cmd_string(cmd->cmd));
- spin_unlock_irqrestore(&priv->lock, flags);
- return -EAGAIN;
- }
-
- priv->status |= STATUS_HCMD_ACTIVE;
-
- if (priv->cmdlog) {
- priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
- priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
- priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
- memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
- cmd->len);
- priv->cmdlog[priv->cmdlog_pos].retcode = -1;
- }
-
- IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
- get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
- priv->status);
-
-#ifndef DEBUG_CMD_WEP_KEY
- if (cmd->cmd == IPW_CMD_WEP_KEY)
- IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n");
- else
-#endif
- printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
-
- rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0);
- if (rc) {
- priv->status &= ~STATUS_HCMD_ACTIVE;
- IPW_ERROR("Failed to send %s: Reason %d\n",
- get_cmd_string(cmd->cmd), rc);
- spin_unlock_irqrestore(&priv->lock, flags);
- goto exit;
- }
- spin_unlock_irqrestore(&priv->lock, flags);
-
- rc = wait_event_interruptible_timeout(priv->wait_command_queue,
- !(priv->
- status & STATUS_HCMD_ACTIVE),
- HOST_COMPLETE_TIMEOUT);
- if (rc == 0) {
- spin_lock_irqsave(&priv->lock, flags);
- if (priv->status & STATUS_HCMD_ACTIVE) {
- IPW_ERROR("Failed to send %s: Command timed out.\n",
- get_cmd_string(cmd->cmd));
- priv->status &= ~STATUS_HCMD_ACTIVE;
- spin_unlock_irqrestore(&priv->lock, flags);
- rc = -EIO;
- goto exit;
- }
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- rc = 0;
-
- if (priv->status & STATUS_RF_KILL_HW) {
- IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
- get_cmd_string(cmd->cmd));
- rc = -EIO;
- goto exit;
- }
-
- exit:
- if (priv->cmdlog) {
- priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
- priv->cmdlog_pos %= priv->cmdlog_len;
- }
- return rc;
-}
-
-static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command)
-{
- struct host_cmd cmd = {
- .cmd = command,
- };
-
- return __ipw_send_cmd(priv, &cmd);
-}
-
-static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len,
- void *data)
-{
- struct host_cmd cmd = {
- .cmd = command,
- .len = len,
- .param = data,
- };
-
- return __ipw_send_cmd(priv, &cmd);
-}
-
-static int ipw_send_host_complete(struct ipw_priv *priv)
-{
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
-}
-
-static int ipw_send_system_config(struct ipw_priv *priv)
-{
- return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
- sizeof(priv->sys_config),
- &priv->sys_config);
-}
-
-static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
-{
- if (!priv || !ssid) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE),
- ssid);
-}
-
-static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
-{
- if (!priv || !mac) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- IPW_DEBUG_INFO("%s: Setting MAC to %s\n",
- priv->net_dev->name, print_mac(mac, mac));
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac);
-}
-
-/*
- * NOTE: This must be executed from our workqueue as it results in udelay
- * being called which may corrupt the keyboard if executed on default
- * workqueue
- */
-static void ipw_adapter_restart(void *adapter)
-{
- struct ipw_priv *priv = adapter;
-
- if (priv->status & STATUS_RF_KILL_MASK)
- return;
-
- ipw_down(priv);
-
- if (priv->assoc_network &&
- (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
- ipw_remove_current_network(priv);
-
- if (ipw_up(priv)) {
- IPW_ERROR("Failed to up device\n");
- return;
- }
-}
-
-static void ipw_bg_adapter_restart(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, adapter_restart);
- mutex_lock(&priv->mutex);
- ipw_adapter_restart(priv);
- mutex_unlock(&priv->mutex);
-}
-
-#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
-
-static void ipw_scan_check(void *data)
-{
- struct ipw_priv *priv = data;
- if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
- IPW_DEBUG_SCAN("Scan completion watchdog resetting "
- "adapter after (%dms).\n",
- jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
- queue_work(priv->workqueue, &priv->adapter_restart);
- }
-}
-
-static void ipw_bg_scan_check(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, scan_check.work);
- mutex_lock(&priv->mutex);
- ipw_scan_check(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static int ipw_send_scan_request_ext(struct ipw_priv *priv,
- struct ipw_scan_request_ext *request)
-{
- return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT,
- sizeof(*request), request);
-}
-
-static int ipw_send_scan_abort(struct ipw_priv *priv)
-{
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT);
-}
-
-static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
-{
- struct ipw_sensitivity_calib calib = {
- .beacon_rssi_raw = cpu_to_le16(sens),
- };
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib),
- &calib);
-}
-
-static int ipw_send_associate(struct ipw_priv *priv,
- struct ipw_associate *associate)
-{
- if (!priv || !associate) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate),
- associate);
-}
-
-static int ipw_send_supported_rates(struct ipw_priv *priv,
- struct ipw_supported_rates *rates)
-{
- if (!priv || !rates) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates),
- rates);
-}
-
-static int ipw_set_random_seed(struct ipw_priv *priv)
-{
- u32 val;
-
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- get_random_bytes(&val, sizeof(val));
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val);
-}
-
-static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
-{
- __le32 v = cpu_to_le32(phy_off);
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v);
-}
-
-static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
-{
- if (!priv || !power) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power);
-}
-
-static int ipw_set_tx_power(struct ipw_priv *priv)
-{
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
- struct ipw_tx_power tx_power;
- s8 max_power;
- int i;
-
- memset(&tx_power, 0, sizeof(tx_power));
-
- /* configure device for 'G' band */
- tx_power.ieee_mode = IPW_G_MODE;
- tx_power.num_channels = geo->bg_channels;
- for (i = 0; i < geo->bg_channels; i++) {
- max_power = geo->bg[i].max_power;
- tx_power.channels_tx_power[i].channel_number =
- geo->bg[i].channel;
- tx_power.channels_tx_power[i].tx_power = max_power ?
- min(max_power, priv->tx_power) : priv->tx_power;
- }
- if (ipw_send_tx_power(priv, &tx_power))
- return -EIO;
-
- /* configure device to also handle 'B' band */
- tx_power.ieee_mode = IPW_B_MODE;
- if (ipw_send_tx_power(priv, &tx_power))
- return -EIO;
-
- /* configure device to also handle 'A' band */
- if (priv->ieee->abg_true) {
- tx_power.ieee_mode = IPW_A_MODE;
- tx_power.num_channels = geo->a_channels;
- for (i = 0; i < tx_power.num_channels; i++) {
- max_power = geo->a[i].max_power;
- tx_power.channels_tx_power[i].channel_number =
- geo->a[i].channel;
- tx_power.channels_tx_power[i].tx_power = max_power ?
- min(max_power, priv->tx_power) : priv->tx_power;
- }
- if (ipw_send_tx_power(priv, &tx_power))
- return -EIO;
- }
- return 0;
-}
-
-static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
-{
- struct ipw_rts_threshold rts_threshold = {
- .rts_threshold = cpu_to_le16(rts),
- };
-
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD,
- sizeof(rts_threshold), &rts_threshold);
-}
-
-static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
-{
- struct ipw_frag_threshold frag_threshold = {
- .frag_threshold = cpu_to_le16(frag),
- };
-
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD,
- sizeof(frag_threshold), &frag_threshold);
-}
-
-static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
-{
- __le32 param;
-
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- /* If on battery, set to 3, if AC set to CAM, else user
- * level */
- switch (mode) {
- case IPW_POWER_BATTERY:
- param = cpu_to_le32(IPW_POWER_INDEX_3);
- break;
- case IPW_POWER_AC:
- param = cpu_to_le32(IPW_POWER_MODE_CAM);
- break;
- default:
- param = cpu_to_le32(mode);
- break;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
- &param);
-}
-
-static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
-{
- struct ipw_retry_limit retry_limit = {
- .short_retry_limit = slimit,
- .long_retry_limit = llimit
- };
-
- if (!priv) {
- IPW_ERROR("Invalid args\n");
- return -1;
- }
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit),
- &retry_limit);
-}
-
-/*
- * The IPW device contains a Microwire compatible EEPROM that stores
- * various data like the MAC address. Usually the firmware has exclusive
- * access to the eeprom, but during device initialization (before the
- * device driver has sent the HostComplete command to the firmware) the
- * device driver has read access to the EEPROM by way of indirect addressing
- * through a couple of memory mapped registers.
- *
- * The following is a simplified implementation for pulling data out of the
- * the eeprom, along with some helper functions to find information in
- * the per device private data's copy of the eeprom.
- *
- * NOTE: To better understand how these functions work (i.e what is a chip
- * select and why do have to keep driving the eeprom clock?), read
- * just about any data sheet for a Microwire compatible EEPROM.
- */
-
-/* write a 32 bit value into the indirect accessor register */
-static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
-{
- ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
-
- /* the eeprom requires some time to complete the operation */
- udelay(p->eeprom_delay);
-
- return;
-}
-
-/* perform a chip select operation */
-static void eeprom_cs(struct ipw_priv *priv)
-{
- eeprom_write_reg(priv, 0);
- eeprom_write_reg(priv, EEPROM_BIT_CS);
- eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
- eeprom_write_reg(priv, EEPROM_BIT_CS);
-}
-
-/* perform a chip select operation */
-static void eeprom_disable_cs(struct ipw_priv *priv)
-{
- eeprom_write_reg(priv, EEPROM_BIT_CS);
- eeprom_write_reg(priv, 0);
- eeprom_write_reg(priv, EEPROM_BIT_SK);
-}
-
-/* push a single bit down to the eeprom */
-static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
-{
- int d = (bit ? EEPROM_BIT_DI : 0);
- eeprom_write_reg(p, EEPROM_BIT_CS | d);
- eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
-}
-
-/* push an opcode followed by an address down to the eeprom */
-static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
-{
- int i;
-
- eeprom_cs(priv);
- eeprom_write_bit(priv, 1);
- eeprom_write_bit(priv, op & 2);
- eeprom_write_bit(priv, op & 1);
- for (i = 7; i >= 0; i--) {
- eeprom_write_bit(priv, addr & (1 << i));
- }
-}
-
-/* pull 16 bits off the eeprom, one bit at a time */
-static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
-{
- int i;
- u16 r = 0;
-
- /* Send READ Opcode */
- eeprom_op(priv, EEPROM_CMD_READ, addr);
-
- /* Send dummy bit */
- eeprom_write_reg(priv, EEPROM_BIT_CS);
-
- /* Read the byte off the eeprom one bit at a time */
- for (i = 0; i < 16; i++) {
- u32 data = 0;
- eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
- eeprom_write_reg(priv, EEPROM_BIT_CS);
- data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
- r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
- }
-
- /* Send another dummy bit */
- eeprom_write_reg(priv, 0);
- eeprom_disable_cs(priv);
-
- return r;
-}
-
-/* helper function for pulling the mac address out of the private */
-/* data's copy of the eeprom data */
-static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
-{
- memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6);
-}
-
-/*
- * Either the device driver (i.e. the host) or the firmware can
- * load eeprom data into the designated region in SRAM. If neither
- * happens then the FW will shutdown with a fatal error.
- *
- * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
- * bit needs region of shared SRAM needs to be non-zero.
- */
-static void ipw_eeprom_init_sram(struct ipw_priv *priv)
-{
- int i;
- __le16 *eeprom = (__le16 *) priv->eeprom;
-
- IPW_DEBUG_TRACE(">>\n");
-
- /* read entire contents of eeprom into private buffer */
- for (i = 0; i < 128; i++)
- eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i));
-
- /*
- If the data looks correct, then copy it to our private
- copy. Otherwise let the firmware know to perform the operation
- on its own.
- */
- if (priv->eeprom[EEPROM_VERSION] != 0) {
- IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
-
- /* write the eeprom data to sram */
- for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
- ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
-
- /* Do not load eeprom data on fatal error or suspend */
- ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
- } else {
- IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
-
- /* Load eeprom data on fatal error or suspend */
- ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
- }
-
- IPW_DEBUG_TRACE("<<\n");
-}
-
-static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
-{
- count >>= 2;
- if (!count)
- return;
- _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
- while (count--)
- _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
-}
-
-static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
-{
- ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
- CB_NUMBER_OF_ELEMENTS_SMALL *
- sizeof(struct command_block));
-}
-
-static int ipw_fw_dma_enable(struct ipw_priv *priv)
-{ /* start dma engine but no transfers yet */
-
- IPW_DEBUG_FW(">> : \n");
-
- /* Start the dma */
- ipw_fw_dma_reset_command_blocks(priv);
-
- /* Write CB base address */
- ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
-
- IPW_DEBUG_FW("<< : \n");
- return 0;
-}
-
-static void ipw_fw_dma_abort(struct ipw_priv *priv)
-{
- u32 control = 0;
-
- IPW_DEBUG_FW(">> :\n");
-
- /* set the Stop and Abort bit */
- control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
- ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
- priv->sram_desc.last_cb_index = 0;
-
- IPW_DEBUG_FW("<< \n");
-}
-
-static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
- struct command_block *cb)
-{
- u32 address =
- IPW_SHARED_SRAM_DMA_CONTROL +
- (sizeof(struct command_block) * index);
- IPW_DEBUG_FW(">> :\n");
-
- ipw_write_indirect(priv, address, (u8 *) cb,
- (int)sizeof(struct command_block));
-
- IPW_DEBUG_FW("<< :\n");
- return 0;
-
-}
-
-static int ipw_fw_dma_kick(struct ipw_priv *priv)
-{
- u32 control = 0;
- u32 index = 0;
-
- IPW_DEBUG_FW(">> :\n");
-
- for (index = 0; index < priv->sram_desc.last_cb_index; index++)
- ipw_fw_dma_write_command_block(priv, index,
- &priv->sram_desc.cb_list[index]);
-
- /* Enable the DMA in the CSR register */
- ipw_clear_bit(priv, IPW_RESET_REG,
- IPW_RESET_REG_MASTER_DISABLED |
- IPW_RESET_REG_STOP_MASTER);
-
- /* Set the Start bit. */
- control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
- ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
-
- IPW_DEBUG_FW("<< :\n");
- return 0;
-}
-
-static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
-{
- u32 address;
- u32 register_value = 0;
- u32 cb_fields_address = 0;
-
- IPW_DEBUG_FW(">> :\n");
- address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
- IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
-
- /* Read the DMA Controlor register */
- register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
- IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
-
- /* Print the CB values */
- cb_fields_address = address;
- register_value = ipw_read_reg32(priv, cb_fields_address);
- IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
-
- cb_fields_address += sizeof(u32);
- register_value = ipw_read_reg32(priv, cb_fields_address);
- IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
-
- cb_fields_address += sizeof(u32);
- register_value = ipw_read_reg32(priv, cb_fields_address);
- IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
- register_value);
-
- cb_fields_address += sizeof(u32);
- register_value = ipw_read_reg32(priv, cb_fields_address);
- IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
-
- IPW_DEBUG_FW(">> :\n");
-}
-
-static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
-{
- u32 current_cb_address = 0;
- u32 current_cb_index = 0;
-
- IPW_DEBUG_FW("<< :\n");
- current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
-
- current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
- sizeof(struct command_block);
-
- IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
- current_cb_index, current_cb_address);
-
- IPW_DEBUG_FW(">> :\n");
- return current_cb_index;
-
-}
-
-static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
- u32 src_address,
- u32 dest_address,
- u32 length,
- int interrupt_enabled, int is_last)
-{
-
- u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
- CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
- CB_DEST_SIZE_LONG;
- struct command_block *cb;
- u32 last_cb_element = 0;
-
- IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
- src_address, dest_address, length);
-
- if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
- return -1;
-
- last_cb_element = priv->sram_desc.last_cb_index;
- cb = &priv->sram_desc.cb_list[last_cb_element];
- priv->sram_desc.last_cb_index++;
-
- /* Calculate the new CB control word */
- if (interrupt_enabled)
- control |= CB_INT_ENABLED;
-
- if (is_last)
- control |= CB_LAST_VALID;
-
- control |= length;
-
- /* Calculate the CB Element's checksum value */
- cb->status = control ^ src_address ^ dest_address;
-
- /* Copy the Source and Destination addresses */
- cb->dest_addr = dest_address;
- cb->source_addr = src_address;
-
- /* Copy the Control Word last */
- cb->control = control;
-
- return 0;
-}
-
-static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
- u32 src_phys, u32 dest_address, u32 length)
-{
- u32 bytes_left = length;
- u32 src_offset = 0;
- u32 dest_offset = 0;
- int status = 0;
- IPW_DEBUG_FW(">> \n");
- IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
- src_phys, dest_address, length);
- while (bytes_left > CB_MAX_LENGTH) {
- status = ipw_fw_dma_add_command_block(priv,
- src_phys + src_offset,
- dest_address +
- dest_offset,
- CB_MAX_LENGTH, 0, 0);
- if (status) {
- IPW_DEBUG_FW_INFO(": Failed\n");
- return -1;
- } else
- IPW_DEBUG_FW_INFO(": Added new cb\n");
-
- src_offset += CB_MAX_LENGTH;
- dest_offset += CB_MAX_LENGTH;
- bytes_left -= CB_MAX_LENGTH;
- }
-
- /* add the buffer tail */
- if (bytes_left > 0) {
- status =
- ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
- dest_address + dest_offset,
- bytes_left, 0, 0);
- if (status) {
- IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
- return -1;
- } else
- IPW_DEBUG_FW_INFO
- (": Adding new cb - the buffer tail\n");
- }
-
- IPW_DEBUG_FW("<< \n");
- return 0;
-}
-
-static int ipw_fw_dma_wait(struct ipw_priv *priv)
-{
- u32 current_index = 0, previous_index;
- u32 watchdog = 0;
-
- IPW_DEBUG_FW(">> : \n");
-
- current_index = ipw_fw_dma_command_block_index(priv);
- IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n",
- (int)priv->sram_desc.last_cb_index);
-
- while (current_index < priv->sram_desc.last_cb_index) {
- udelay(50);
- previous_index = current_index;
- current_index = ipw_fw_dma_command_block_index(priv);
-
- if (previous_index < current_index) {
- watchdog = 0;
- continue;
- }
- if (++watchdog > 400) {
- IPW_DEBUG_FW_INFO("Timeout\n");
- ipw_fw_dma_dump_command_block(priv);
- ipw_fw_dma_abort(priv);
- return -1;
- }
- }
-
- ipw_fw_dma_abort(priv);
-
- /*Disable the DMA in the CSR register */
- ipw_set_bit(priv, IPW_RESET_REG,
- IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
-
- IPW_DEBUG_FW("<< dmaWaitSync \n");
- return 0;
-}
-
-static void ipw_remove_current_network(struct ipw_priv *priv)
-{
- struct list_head *element, *safe;
- struct ieee80211_network *network = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->ieee->lock, flags);
- list_for_each_safe(element, safe, &priv->ieee->network_list) {
- network = list_entry(element, struct ieee80211_network, list);
- if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
- list_del(element);
- list_add_tail(&network->list,
- &priv->ieee->network_free_list);
- }
- }
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-}
-
-/**
- * Check that card is still alive.
- * Reads debug register from domain0.
- * If card is present, pre-defined value should
- * be found there.
- *
- * @param priv
- * @return 1 if card is present, 0 otherwise
- */
-static inline int ipw_alive(struct ipw_priv *priv)
-{
- return ipw_read32(priv, 0x90) == 0xd55555d5;
-}
-
-/* timeout in msec, attempted in 10-msec quanta */
-static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
- int timeout)
-{
- int i = 0;
-
- do {
- if ((ipw_read32(priv, addr) & mask) == mask)
- return i;
- mdelay(10);
- i += 10;
- } while (i < timeout);
-
- return -ETIME;
-}
-
-/* These functions load the firmware and micro code for the operation of
- * the ipw hardware. It assumes the buffer has all the bits for the
- * image and the caller is handling the memory allocation and clean up.
- */
-
-static int ipw_stop_master(struct ipw_priv *priv)
-{
- int rc;
-
- IPW_DEBUG_TRACE(">> \n");
- /* stop master. typical delay - 0 */
- ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
-
- /* timeout is in msec, polled in 10-msec quanta */
- rc = ipw_poll_bit(priv, IPW_RESET_REG,
- IPW_RESET_REG_MASTER_DISABLED, 100);
- if (rc < 0) {
- IPW_ERROR("wait for stop master failed after 100ms\n");
- return -1;
- }
-
- IPW_DEBUG_INFO("stop master %dms\n", rc);
-
- return rc;
-}
-
-static void ipw_arc_release(struct ipw_priv *priv)
-{
- IPW_DEBUG_TRACE(">> \n");
- mdelay(5);
-
- ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
-
- /* no one knows timing, for safety add some delay */
- mdelay(5);
-}
-
-struct fw_chunk {
- __le32 address;
- __le32 length;
-};
-
-static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
-{
- int rc = 0, i, addr;
- u8 cr = 0;
- __le16 *image;
-
- image = (__le16 *) data;
-
- IPW_DEBUG_TRACE(">> \n");
-
- rc = ipw_stop_master(priv);
-
- if (rc < 0)
- return rc;
-
- for (addr = IPW_SHARED_LOWER_BOUND;
- addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
- ipw_write32(priv, addr, 0);
- }
-
- /* no ucode (yet) */
- memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
- /* destroy DMA queues */
- /* reset sequence */
-
- ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
- ipw_arc_release(priv);
- ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
- mdelay(1);
-
- /* reset PHY */
- ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
- mdelay(1);
-
- ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
- mdelay(1);
-
- /* enable ucode store */
- ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
- ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
- mdelay(1);
-
- /* write ucode */
- /**
- * @bug
- * Do NOT set indirect address register once and then
- * store data to indirect data register in the loop.
- * It seems very reasonable, but in this case DINO do not
- * accept ucode. It is essential to set address each time.
- */
- /* load new ipw uCode */
- for (i = 0; i < len / 2; i++)
- ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
- le16_to_cpu(image[i]));
-
- /* enable DINO */
- ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
- ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
-
- /* this is where the igx / win driver deveates from the VAP driver. */
-
- /* wait for alive response */
- for (i = 0; i < 100; i++) {
- /* poll for incoming data */
- cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
- if (cr & DINO_RXFIFO_DATA)
- break;
- mdelay(1);
- }
-
- if (cr & DINO_RXFIFO_DATA) {
- /* alive_command_responce size is NOT multiple of 4 */
- __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
-
- for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
- response_buffer[i] =
- cpu_to_le32(ipw_read_reg32(priv,
- IPW_BASEBAND_RX_FIFO_READ));
- memcpy(&priv->dino_alive, response_buffer,
- sizeof(priv->dino_alive));
- if (priv->dino_alive.alive_command == 1
- && priv->dino_alive.ucode_valid == 1) {
- rc = 0;
- IPW_DEBUG_INFO
- ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
- "of %02d/%02d/%02d %02d:%02d\n",
- priv->dino_alive.software_revision,
- priv->dino_alive.software_revision,
- priv->dino_alive.device_identifier,
- priv->dino_alive.device_identifier,
- priv->dino_alive.time_stamp[0],
- priv->dino_alive.time_stamp[1],
- priv->dino_alive.time_stamp[2],
- priv->dino_alive.time_stamp[3],
- priv->dino_alive.time_stamp[4]);
- } else {
- IPW_DEBUG_INFO("Microcode is not alive\n");
- rc = -EINVAL;
- }
- } else {
- IPW_DEBUG_INFO("No alive response from DINO\n");
- rc = -ETIME;
- }
-
- /* disable DINO, otherwise for some reason
- firmware have problem getting alive resp. */
- ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
-
- return rc;
-}
-
-static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
-{
- int rc = -1;
- int offset = 0;
- struct fw_chunk *chunk;
- dma_addr_t shared_phys;
- u8 *shared_virt;
-
- IPW_DEBUG_TRACE("<< : \n");
- shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
-
- if (!shared_virt)
- return -ENOMEM;
-
- memmove(shared_virt, data, len);
-
- /* Start the Dma */
- rc = ipw_fw_dma_enable(priv);
-
- if (priv->sram_desc.last_cb_index > 0) {
- /* the DMA is already ready this would be a bug. */
- BUG();
- goto out;
- }
-
- do {
- chunk = (struct fw_chunk *)(data + offset);
- offset += sizeof(struct fw_chunk);
- /* build DMA packet and queue up for sending */
- /* dma to chunk->address, the chunk->length bytes from data +
- * offeset*/
- /* Dma loading */
- rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
- le32_to_cpu(chunk->address),
- le32_to_cpu(chunk->length));
- if (rc) {
- IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
- goto out;
- }
-
- offset += le32_to_cpu(chunk->length);
- } while (offset < len);
-
- /* Run the DMA and wait for the answer */
- rc = ipw_fw_dma_kick(priv);
- if (rc) {
- IPW_ERROR("dmaKick Failed\n");
- goto out;
- }
-
- rc = ipw_fw_dma_wait(priv);
- if (rc) {
- IPW_ERROR("dmaWaitSync Failed\n");
- goto out;
- }
- out:
- pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
- return rc;
-}
-
-/* stop nic */
-static int ipw_stop_nic(struct ipw_priv *priv)
-{
- int rc = 0;
-
- /* stop */
- ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
-
- rc = ipw_poll_bit(priv, IPW_RESET_REG,
- IPW_RESET_REG_MASTER_DISABLED, 500);
- if (rc < 0) {
- IPW_ERROR("wait for reg master disabled failed after 500ms\n");
- return rc;
- }
-
- ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
-
- return rc;
-}
-
-static void ipw_start_nic(struct ipw_priv *priv)
-{
- IPW_DEBUG_TRACE(">>\n");
-
- /* prvHwStartNic release ARC */
- ipw_clear_bit(priv, IPW_RESET_REG,
- IPW_RESET_REG_MASTER_DISABLED |
- IPW_RESET_REG_STOP_MASTER |
- CBD_RESET_REG_PRINCETON_RESET);
-
- /* enable power management */
- ipw_set_bit(priv, IPW_GP_CNTRL_RW,
- IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
-
- IPW_DEBUG_TRACE("<<\n");
-}
-
-static int ipw_init_nic(struct ipw_priv *priv)
-{
- int rc;
-
- IPW_DEBUG_TRACE(">>\n");
- /* reset */
- /*prvHwInitNic */
- /* set "initialization complete" bit to move adapter to D0 state */
- ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
-
- /* low-level PLL activation */
- ipw_write32(priv, IPW_READ_INT_REGISTER,
- IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
-
- /* wait for clock stabilization */
- rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
- IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
- if (rc < 0)
- IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
-
- /* assert SW reset */
- ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
-
- udelay(10);
-
- /* set "initialization complete" bit to move adapter to D0 state */
- ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
-
- IPW_DEBUG_TRACE(">>\n");
- return 0;
-}
-
-/* Call this function from process context, it will sleep in request_firmware.
- * Probe is an ok place to call this from.
- */
-static int ipw_reset_nic(struct ipw_priv *priv)
-{
- int rc = 0;
- unsigned long flags;
-
- IPW_DEBUG_TRACE(">>\n");
-
- rc = ipw_init_nic(priv);
-
- spin_lock_irqsave(&priv->lock, flags);
- /* Clear the 'host command active' bit... */
- priv->status &= ~STATUS_HCMD_ACTIVE;
- wake_up_interruptible(&priv->wait_command_queue);
- priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
- wake_up_interruptible(&priv->wait_state);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- IPW_DEBUG_TRACE("<<\n");
- return rc;
-}
-
-
-struct ipw_fw {
- __le32 ver;
- __le32 boot_size;
- __le32 ucode_size;
- __le32 fw_size;
- u8 data[0];
-};
-
-static int ipw_get_fw(struct ipw_priv *priv,
- const struct firmware **raw, const char *name)
-{
- struct ipw_fw *fw;
- int rc;
-
- /* ask firmware_class module to get the boot firmware off disk */
- rc = request_firmware(raw, name, &priv->pci_dev->dev);
- if (rc < 0) {
- IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc);
- return rc;
- }
-
- if ((*raw)->size < sizeof(*fw)) {
- IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size);
- return -EINVAL;
- }
-
- fw = (void *)(*raw)->data;
-
- if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
- le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
- IPW_ERROR("%s is too small or corrupt (%zd)\n",
- name, (*raw)->size);
- return -EINVAL;
- }
-
- IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n",
- name,
- le32_to_cpu(fw->ver) >> 16,
- le32_to_cpu(fw->ver) & 0xff,
- (*raw)->size - sizeof(*fw));
- return 0;
-}
-
-#define IPW_RX_BUF_SIZE (3000)
-
-static void ipw_rx_queue_reset(struct ipw_priv *priv,
- struct ipw_rx_queue *rxq)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&rxq->lock, flags);
-
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
-
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
- /* In the reset function, these buffers may have been allocated
- * to an SKB, so we need to unmap and free potential storage */
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
- IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
- dev_kfree_skb(rxq->pool[i].skb);
- rxq->pool[i].skb = NULL;
- }
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
- }
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->free_count = 0;
- spin_unlock_irqrestore(&rxq->lock, flags);
-}
-
-#ifdef CONFIG_PM
-static int fw_loaded = 0;
-static const struct firmware *raw = NULL;
-
-static void free_firmware(void)
-{
- if (fw_loaded) {
- release_firmware(raw);
- raw = NULL;
- fw_loaded = 0;
- }
-}
-#else
-#define free_firmware() do {} while (0)
-#endif
-
-static int ipw_load(struct ipw_priv *priv)
-{
-#ifndef CONFIG_PM
- const struct firmware *raw = NULL;
-#endif
- struct ipw_fw *fw;
- u8 *boot_img, *ucode_img, *fw_img;
- u8 *name = NULL;
- int rc = 0, retries = 3;
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- name = "ipw2200-ibss.fw";
- break;
-#ifdef CONFIG_IPW2200_MONITOR
- case IW_MODE_MONITOR:
- name = "ipw2200-sniffer.fw";
- break;
-#endif
- case IW_MODE_INFRA:
- name = "ipw2200-bss.fw";
- break;
- }
-
- if (!name) {
- rc = -EINVAL;
- goto error;
- }
-
-#ifdef CONFIG_PM
- if (!fw_loaded) {
-#endif
- rc = ipw_get_fw(priv, &raw, name);
- if (rc < 0)
- goto error;
-#ifdef CONFIG_PM
- }
-#endif
-
- fw = (void *)raw->data;
- boot_img = &fw->data[0];
- ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
- fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
- le32_to_cpu(fw->ucode_size)];
-
- if (rc < 0)
- goto error;
-
- if (!priv->rxq)
- priv->rxq = ipw_rx_queue_alloc(priv);
- else
- ipw_rx_queue_reset(priv, priv->rxq);
- if (!priv->rxq) {
- IPW_ERROR("Unable to initialize Rx queue\n");
- goto error;
- }
-
- retry:
- /* Ensure interrupts are disabled */
- ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
- priv->status &= ~STATUS_INT_ENABLED;
-
- /* ack pending interrupts */
- ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
-
- ipw_stop_nic(priv);
-
- rc = ipw_reset_nic(priv);
- if (rc < 0) {
- IPW_ERROR("Unable to reset NIC\n");
- goto error;
- }
-
- ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
- IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
-
- /* DMA the initial boot firmware into the device */
- rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
- if (rc < 0) {
- IPW_ERROR("Unable to load boot firmware: %d\n", rc);
- goto error;
- }
-
- /* kick start the device */
- ipw_start_nic(priv);
-
- /* wait for the device to finish its initial startup sequence */
- rc = ipw_poll_bit(priv, IPW_INTA_RW,
- IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
- if (rc < 0) {
- IPW_ERROR("device failed to boot initial fw image\n");
- goto error;
- }
- IPW_DEBUG_INFO("initial device response after %dms\n", rc);
-
- /* ack fw init done interrupt */
- ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
-
- /* DMA the ucode into the device */
- rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
- if (rc < 0) {
- IPW_ERROR("Unable to load ucode: %d\n", rc);
- goto error;
- }
-
- /* stop nic */
- ipw_stop_nic(priv);
-
- /* DMA bss firmware into the device */
- rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
- if (rc < 0) {
- IPW_ERROR("Unable to load firmware: %d\n", rc);
- goto error;
- }
-#ifdef CONFIG_PM
- fw_loaded = 1;
-#endif
-
- ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
-
- rc = ipw_queue_reset(priv);
- if (rc < 0) {
- IPW_ERROR("Unable to initialize queues\n");
- goto error;
- }
-
- /* Ensure interrupts are disabled */
- ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
- /* ack pending interrupts */
- ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
-
- /* kick start the device */
- ipw_start_nic(priv);
-
- if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
- if (retries > 0) {
- IPW_WARNING("Parity error. Retrying init.\n");
- retries--;
- goto retry;
- }
-
- IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
- rc = -EIO;
- goto error;
- }
-
- /* wait for the device */
- rc = ipw_poll_bit(priv, IPW_INTA_RW,
- IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
- if (rc < 0) {
- IPW_ERROR("device failed to start within 500ms\n");
- goto error;
- }
- IPW_DEBUG_INFO("device response after %dms\n", rc);
-
- /* ack fw init done interrupt */
- ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
-
- /* read eeprom data and initialize the eeprom region of sram */
- priv->eeprom_delay = 1;
- ipw_eeprom_init_sram(priv);
-
- /* enable interrupts */
- ipw_enable_interrupts(priv);
-
- /* Ensure our queue has valid packets */
- ipw_rx_queue_replenish(priv);
-
- ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
-
- /* ack pending interrupts */
- ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
-
-#ifndef CONFIG_PM
- release_firmware(raw);
-#endif
- return 0;
-
- error:
- if (priv->rxq) {
- ipw_rx_queue_free(priv, priv->rxq);
- priv->rxq = NULL;
- }
- ipw_tx_queue_free(priv);
- if (raw)
- release_firmware(raw);
-#ifdef CONFIG_PM
- fw_loaded = 0;
- raw = NULL;
-#endif
-
- return rc;
-}
-
-/**
- * DMA services
- *
- * Theory of operation
- *
- * A queue is a circular buffers with 'Read' and 'Write' pointers.
- * 2 empty entries always kept in the buffer to protect from overflow.
- *
- * For Tx queue, there are low mark and high mark limits. If, after queuing
- * the packet for Tx, free space become < low mark, Tx queue stopped. When
- * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
- * Tx queue resumed.
- *
- * The IPW operates with six queues, one receive queue in the device's
- * sram, one transmit queue for sending commands to the device firmware,
- * and four transmit queues for data.
- *
- * The four transmit queues allow for performing quality of service (qos)
- * transmissions as per the 802.11 protocol. Currently Linux does not
- * provide a mechanism to the user for utilizing prioritized queues, so
- * we only utilize the first data transmit queue (queue1).
- */
-
-/**
- * Driver allocates buffers of this size for Rx
- */
-
-/**
- * ipw_rx_queue_space - Return number of free slots available in queue.
- */
-static int ipw_rx_queue_space(const struct ipw_rx_queue *q)
-{
- int s = q->read - q->write;
- if (s <= 0)
- s += RX_QUEUE_SIZE;
- /* keep some buffer to not confuse full and empty queue */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
-}
-
-static inline int ipw_tx_queue_space(const struct clx2_queue *q)
-{
- int s = q->last_used - q->first_empty;
- if (s <= 0)
- s += q->n_bd;
- s -= 2; /* keep some reserve to not confuse empty and full situations */
- if (s < 0)
- s = 0;
- return s;
-}
-
-static inline int ipw_queue_inc_wrap(int index, int n_bd)
-{
- return (++index == n_bd) ? 0 : index;
-}
-
-/**
- * Initialize common DMA queue structure
- *
- * @param q queue to init
- * @param count Number of BD's to allocate. Should be power of 2
- * @param read_register Address for 'read' register
- * (not offset within BAR, full address)
- * @param write_register Address for 'write' register
- * (not offset within BAR, full address)
- * @param base_register Address for 'base' register
- * (not offset within BAR, full address)
- * @param size Address for 'size' register
- * (not offset within BAR, full address)
- */
-static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
- int count, u32 read, u32 write, u32 base, u32 size)
-{
- q->n_bd = count;
-
- q->low_mark = q->n_bd / 4;
- if (q->low_mark < 4)
- q->low_mark = 4;
-
- q->high_mark = q->n_bd / 8;
- if (q->high_mark < 2)
- q->high_mark = 2;
-
- q->first_empty = q->last_used = 0;
- q->reg_r = read;
- q->reg_w = write;
-
- ipw_write32(priv, base, q->dma_addr);
- ipw_write32(priv, size, count);
- ipw_write32(priv, read, 0);
- ipw_write32(priv, write, 0);
-
- _ipw_read32(priv, 0x90);
-}
-
-static int ipw_queue_tx_init(struct ipw_priv *priv,
- struct clx2_tx_queue *q,
- int count, u32 read, u32 write, u32 base, u32 size)
-{
- struct pci_dev *dev = priv->pci_dev;
-
- q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
- if (!q->txb) {
- IPW_ERROR("vmalloc for auxilary BD structures failed\n");
- return -ENOMEM;
- }
-
- q->bd =
- pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
- if (!q->bd) {
- IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
- sizeof(q->bd[0]) * count);
- kfree(q->txb);
- q->txb = NULL;
- return -ENOMEM;
- }
-
- ipw_queue_init(priv, &q->q, count, read, write, base, size);
- return 0;
-}
-
-/**
- * Free one TFD, those at index [txq->q.last_used].
- * Do NOT advance any indexes
- *
- * @param dev
- * @param txq
- */
-static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
- struct clx2_tx_queue *txq)
-{
- struct tfd_frame *bd = &txq->bd[txq->q.last_used];
- struct pci_dev *dev = priv->pci_dev;
- int i;
-
- /* classify bd */
- if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
- /* nothing to cleanup after for host commands */
- return;
-
- /* sanity check */
- if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
- IPW_ERROR("Too many chunks: %i\n",
- le32_to_cpu(bd->u.data.num_chunks));
- /** @todo issue fatal error, it is quite serious situation */
- return;
- }
-
- /* unmap chunks if any */
- for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
- pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
- le16_to_cpu(bd->u.data.chunk_len[i]),
- PCI_DMA_TODEVICE);
- if (txq->txb[txq->q.last_used]) {
- ieee80211_txb_free(txq->txb[txq->q.last_used]);
- txq->txb[txq->q.last_used] = NULL;
- }
- }
-}
-
-/**
- * Deallocate DMA queue.
- *
- * Empty queue by removing and destroying all BD's.
- * Free all buffers.
- *
- * @param dev
- * @param q
- */
-static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
-{
- struct clx2_queue *q = &txq->q;
- struct pci_dev *dev = priv->pci_dev;
-
- if (q->n_bd == 0)
- return;
-
- /* first, empty all BD's */
- for (; q->first_empty != q->last_used;
- q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
- ipw_queue_tx_free_tfd(priv, txq);
- }
-
- /* free buffers belonging to queue itself */
- pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
- q->dma_addr);
- kfree(txq->txb);
-
- /* 0 fill whole structure */
- memset(txq, 0, sizeof(*txq));
-}
-
-/**
- * Destroy all DMA queues and structures
- *
- * @param priv
- */
-static void ipw_tx_queue_free(struct ipw_priv *priv)
-{
- /* Tx CMD queue */
- ipw_queue_tx_free(priv, &priv->txq_cmd);
-
- /* Tx queues */
- ipw_queue_tx_free(priv, &priv->txq[0]);
- ipw_queue_tx_free(priv, &priv->txq[1]);
- ipw_queue_tx_free(priv, &priv->txq[2]);
- ipw_queue_tx_free(priv, &priv->txq[3]);
-}
-
-static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
-{
- /* First 3 bytes are manufacturer */
- bssid[0] = priv->mac_addr[0];
- bssid[1] = priv->mac_addr[1];
- bssid[2] = priv->mac_addr[2];
-
- /* Last bytes are random */
- get_random_bytes(&bssid[3], ETH_ALEN - 3);
-
- bssid[0] &= 0xfe; /* clear multicast bit */
- bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
-}
-
-static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
-{
- struct ipw_station_entry entry;
- int i;
- DECLARE_MAC_BUF(mac);
-
- for (i = 0; i < priv->num_stations; i++) {
- if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
- /* Another node is active in network */
- priv->missed_adhoc_beacons = 0;
- if (!(priv->config & CFG_STATIC_CHANNEL))
- /* when other nodes drop out, we drop out */
- priv->config &= ~CFG_ADHOC_PERSIST;
-
- return i;
- }
- }
-
- if (i == MAX_STATIONS)
- return IPW_INVALID_STATION;
-
- IPW_DEBUG_SCAN("Adding AdHoc station: %s\n", print_mac(mac, bssid));
-
- entry.reserved = 0;
- entry.support_mode = 0;
- memcpy(entry.mac_addr, bssid, ETH_ALEN);
- memcpy(priv->stations[i], bssid, ETH_ALEN);
- ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
- &entry, sizeof(entry));
- priv->num_stations++;
-
- return i;
-}
-
-static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
-{
- int i;
-
- for (i = 0; i < priv->num_stations; i++)
- if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
- return i;
-
- return IPW_INVALID_STATION;
-}
-
-static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
-{
- int err;
- DECLARE_MAC_BUF(mac);
-
- if (priv->status & STATUS_ASSOCIATING) {
- IPW_DEBUG_ASSOC("Disassociating while associating.\n");
- queue_work(priv->workqueue, &priv->disassociate);
- return;
- }
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
- return;
- }
-
- IPW_DEBUG_ASSOC("Disassocation attempt from %s "
- "on channel %d.\n",
- print_mac(mac, priv->assoc_request.bssid),
- priv->assoc_request.channel);
-
- priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
- priv->status |= STATUS_DISASSOCIATING;
-
- if (quiet)
- priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
- else
- priv->assoc_request.assoc_type = HC_DISASSOCIATE;
-
- err = ipw_send_associate(priv, &priv->assoc_request);
- if (err) {
- IPW_DEBUG_HC("Attempt to send [dis]associate command "
- "failed.\n");
- return;
- }
-
-}
-
-static int ipw_disassociate(void *data)
-{
- struct ipw_priv *priv = data;
- if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
- return 0;
- ipw_send_disassociate(data, 0);
- netif_carrier_off(priv->net_dev);
- return 1;
-}
-
-static void ipw_bg_disassociate(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, disassociate);
- mutex_lock(&priv->mutex);
- ipw_disassociate(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_system_config(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, system_config);
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
- priv->sys_config.accept_all_data_frames = 1;
- priv->sys_config.accept_non_directed_frames = 1;
- priv->sys_config.accept_all_mgmt_bcpr = 1;
- priv->sys_config.accept_all_mgmt_frames = 1;
- }
-#endif
-
- ipw_send_system_config(priv);
-}
-
-struct ipw_status_code {
- u16 status;
- const char *reason;
-};
-
-static const struct ipw_status_code ipw_status_codes[] = {
- {0x00, "Successful"},
- {0x01, "Unspecified failure"},
- {0x0A, "Cannot support all requested capabilities in the "
- "Capability information field"},
- {0x0B, "Reassociation denied due to inability to confirm that "
- "association exists"},
- {0x0C, "Association denied due to reason outside the scope of this "
- "standard"},
- {0x0D,
- "Responding station does not support the specified authentication "
- "algorithm"},
- {0x0E,
- "Received an Authentication frame with authentication sequence "
- "transaction sequence number out of expected sequence"},
- {0x0F, "Authentication rejected because of challenge failure"},
- {0x10, "Authentication rejected due to timeout waiting for next "
- "frame in sequence"},
- {0x11, "Association denied because AP is unable to handle additional "
- "associated stations"},
- {0x12,
- "Association denied due to requesting station not supporting all "
- "of the datarates in the BSSBasicServiceSet Parameter"},
- {0x13,
- "Association denied due to requesting station not supporting "
- "short preamble operation"},
- {0x14,
- "Association denied due to requesting station not supporting "
- "PBCC encoding"},
- {0x15,
- "Association denied due to requesting station not supporting "
- "channel agility"},
- {0x19,
- "Association denied due to requesting station not supporting "
- "short slot operation"},
- {0x1A,
- "Association denied due to requesting station not supporting "
- "DSSS-OFDM operation"},
- {0x28, "Invalid Information Element"},
- {0x29, "Group Cipher is not valid"},
- {0x2A, "Pairwise Cipher is not valid"},
- {0x2B, "AKMP is not valid"},
- {0x2C, "Unsupported RSN IE version"},
- {0x2D, "Invalid RSN IE Capabilities"},
- {0x2E, "Cipher suite is rejected per security policy"},
-};
-
-static const char *ipw_get_status_code(u16 status)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
- if (ipw_status_codes[i].status == (status & 0xff))
- return ipw_status_codes[i].reason;
- return "Unknown status value.";
-}
-
-static void inline average_init(struct average *avg)
-{
- memset(avg, 0, sizeof(*avg));
-}
-
-#define DEPTH_RSSI 8
-#define DEPTH_NOISE 16
-static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
-{
- return ((depth-1)*prev_avg + val)/depth;
-}
-
-static void average_add(struct average *avg, s16 val)
-{
- avg->sum -= avg->entries[avg->pos];
- avg->sum += val;
- avg->entries[avg->pos++] = val;
- if (unlikely(avg->pos == AVG_ENTRIES)) {
- avg->init = 1;
- avg->pos = 0;
- }
-}
-
-static s16 average_value(struct average *avg)
-{
- if (!unlikely(avg->init)) {
- if (avg->pos)
- return avg->sum / avg->pos;
- return 0;
- }
-
- return avg->sum / AVG_ENTRIES;
-}
-
-static void ipw_reset_stats(struct ipw_priv *priv)
-{
- u32 len = sizeof(u32);
-
- priv->quality = 0;
-
- average_init(&priv->average_missed_beacons);
- priv->exp_avg_rssi = -60;
- priv->exp_avg_noise = -85 + 0x100;
-
- priv->last_rate = 0;
- priv->last_missed_beacons = 0;
- priv->last_rx_packets = 0;
- priv->last_tx_packets = 0;
- priv->last_tx_failures = 0;
-
- /* Firmware managed, reset only when NIC is restarted, so we have to
- * normalize on the current value */
- ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
- &priv->last_rx_err, &len);
- ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
- &priv->last_tx_failures, &len);
-
- /* Driver managed, reset with each association */
- priv->missed_adhoc_beacons = 0;
- priv->missed_beacons = 0;
- priv->tx_packets = 0;
- priv->rx_packets = 0;
-
-}
-
-static u32 ipw_get_max_rate(struct ipw_priv *priv)
-{
- u32 i = 0x80000000;
- u32 mask = priv->rates_mask;
- /* If currently associated in B mode, restrict the maximum
- * rate match to B rates */
- if (priv->assoc_request.ieee_mode == IPW_B_MODE)
- mask &= IEEE80211_CCK_RATES_MASK;
-
- /* TODO: Verify that the rate is supported by the current rates
- * list. */
-
- while (i && !(mask & i))
- i >>= 1;
- switch (i) {
- case IEEE80211_CCK_RATE_1MB_MASK:
- return 1000000;
- case IEEE80211_CCK_RATE_2MB_MASK:
- return 2000000;
- case IEEE80211_CCK_RATE_5MB_MASK:
- return 5500000;
- case IEEE80211_OFDM_RATE_6MB_MASK:
- return 6000000;
- case IEEE80211_OFDM_RATE_9MB_MASK:
- return 9000000;
- case IEEE80211_CCK_RATE_11MB_MASK:
- return 11000000;
- case IEEE80211_OFDM_RATE_12MB_MASK:
- return 12000000;
- case IEEE80211_OFDM_RATE_18MB_MASK:
- return 18000000;
- case IEEE80211_OFDM_RATE_24MB_MASK:
- return 24000000;
- case IEEE80211_OFDM_RATE_36MB_MASK:
- return 36000000;
- case IEEE80211_OFDM_RATE_48MB_MASK:
- return 48000000;
- case IEEE80211_OFDM_RATE_54MB_MASK:
- return 54000000;
- }
-
- if (priv->ieee->mode == IEEE_B)
- return 11000000;
- else
- return 54000000;
-}
-
-static u32 ipw_get_current_rate(struct ipw_priv *priv)
-{
- u32 rate, len = sizeof(rate);
- int err;
-
- if (!(priv->status & STATUS_ASSOCIATED))
- return 0;
-
- if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
- err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
- &len);
- if (err) {
- IPW_DEBUG_INFO("failed querying ordinals.\n");
- return 0;
- }
- } else
- return ipw_get_max_rate(priv);
-
- switch (rate) {
- case IPW_TX_RATE_1MB:
- return 1000000;
- case IPW_TX_RATE_2MB:
- return 2000000;
- case IPW_TX_RATE_5MB:
- return 5500000;
- case IPW_TX_RATE_6MB:
- return 6000000;
- case IPW_TX_RATE_9MB:
- return 9000000;
- case IPW_TX_RATE_11MB:
- return 11000000;
- case IPW_TX_RATE_12MB:
- return 12000000;
- case IPW_TX_RATE_18MB:
- return 18000000;
- case IPW_TX_RATE_24MB:
- return 24000000;
- case IPW_TX_RATE_36MB:
- return 36000000;
- case IPW_TX_RATE_48MB:
- return 48000000;
- case IPW_TX_RATE_54MB:
- return 54000000;
- }
-
- return 0;
-}
-
-#define IPW_STATS_INTERVAL (2 * HZ)
-static void ipw_gather_stats(struct ipw_priv *priv)
-{
- u32 rx_err, rx_err_delta, rx_packets_delta;
- u32 tx_failures, tx_failures_delta, tx_packets_delta;
- u32 missed_beacons_percent, missed_beacons_delta;
- u32 quality = 0;
- u32 len = sizeof(u32);
- s16 rssi;
- u32 beacon_quality, signal_quality, tx_quality, rx_quality,
- rate_quality;
- u32 max_rate;
-
- if (!(priv->status & STATUS_ASSOCIATED)) {
- priv->quality = 0;
- return;
- }
-
- /* Update the statistics */
- ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
- &priv->missed_beacons, &len);
- missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
- priv->last_missed_beacons = priv->missed_beacons;
- if (priv->assoc_request.beacon_interval) {
- missed_beacons_percent = missed_beacons_delta *
- (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) /
- (IPW_STATS_INTERVAL * 10);
- } else {
- missed_beacons_percent = 0;
- }
- average_add(&priv->average_missed_beacons, missed_beacons_percent);
-
- ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
- rx_err_delta = rx_err - priv->last_rx_err;
- priv->last_rx_err = rx_err;
-
- ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
- tx_failures_delta = tx_failures - priv->last_tx_failures;
- priv->last_tx_failures = tx_failures;
-
- rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
- priv->last_rx_packets = priv->rx_packets;
-
- tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
- priv->last_tx_packets = priv->tx_packets;
-
- /* Calculate quality based on the following:
- *
- * Missed beacon: 100% = 0, 0% = 70% missed
- * Rate: 60% = 1Mbs, 100% = Max
- * Rx and Tx errors represent a straight % of total Rx/Tx
- * RSSI: 100% = > -50, 0% = < -80
- * Rx errors: 100% = 0, 0% = 50% missed
- *
- * The lowest computed quality is used.
- *
- */
-#define BEACON_THRESHOLD 5
- beacon_quality = 100 - missed_beacons_percent;
- if (beacon_quality < BEACON_THRESHOLD)
- beacon_quality = 0;
- else
- beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
- (100 - BEACON_THRESHOLD);
- IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
- beacon_quality, missed_beacons_percent);
-
- priv->last_rate = ipw_get_current_rate(priv);
- max_rate = ipw_get_max_rate(priv);
- rate_quality = priv->last_rate * 40 / max_rate + 60;
- IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
- rate_quality, priv->last_rate / 1000000);
-
- if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
- rx_quality = 100 - (rx_err_delta * 100) /
- (rx_packets_delta + rx_err_delta);
- else
- rx_quality = 100;
- IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
- rx_quality, rx_err_delta, rx_packets_delta);
-
- if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
- tx_quality = 100 - (tx_failures_delta * 100) /
- (tx_packets_delta + tx_failures_delta);
- else
- tx_quality = 100;
- IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
- tx_quality, tx_failures_delta, tx_packets_delta);
-
- rssi = priv->exp_avg_rssi;
- signal_quality =
- (100 *
- (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
- (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
- (priv->ieee->perfect_rssi - rssi) *
- (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
- 62 * (priv->ieee->perfect_rssi - rssi))) /
- ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
- (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
- if (signal_quality > 100)
- signal_quality = 100;
- else if (signal_quality < 1)
- signal_quality = 0;
-
- IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
- signal_quality, rssi);
-
- quality = min(beacon_quality,
- min(rate_quality,
- min(tx_quality, min(rx_quality, signal_quality))));
- if (quality == beacon_quality)
- IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
- quality);
- if (quality == rate_quality)
- IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
- quality);
- if (quality == tx_quality)
- IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
- quality);
- if (quality == rx_quality)
- IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
- quality);
- if (quality == signal_quality)
- IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
- quality);
-
- priv->quality = quality;
-
- queue_delayed_work(priv->workqueue, &priv->gather_stats,
- IPW_STATS_INTERVAL);
-}
-
-static void ipw_bg_gather_stats(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, gather_stats.work);
- mutex_lock(&priv->mutex);
- ipw_gather_stats(priv);
- mutex_unlock(&priv->mutex);
-}
-
-/* Missed beacon behavior:
- * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
- * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
- * Above disassociate threshold, give up and stop scanning.
- * Roaming is disabled if disassociate_threshold <= roaming_threshold */
-static void ipw_handle_missed_beacon(struct ipw_priv *priv,
- int missed_count)
-{
- priv->notif_missed_beacons = missed_count;
-
- if (missed_count > priv->disassociate_threshold &&
- priv->status & STATUS_ASSOCIATED) {
- /* If associated and we've hit the missed
- * beacon threshold, disassociate, turn
- * off roaming, and abort any active scans */
- IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
- IPW_DL_STATE | IPW_DL_ASSOC,
- "Missed beacon: %d - disassociate\n", missed_count);
- priv->status &= ~STATUS_ROAMING;
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
- IPW_DL_STATE,
- "Aborting scan with missed beacon.\n");
- queue_work(priv->workqueue, &priv->abort_scan);
- }
-
- queue_work(priv->workqueue, &priv->disassociate);
- return;
- }
-
- if (priv->status & STATUS_ROAMING) {
- /* If we are currently roaming, then just
- * print a debug statement... */
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
- "Missed beacon: %d - roam in progress\n",
- missed_count);
- return;
- }
-
- if (roaming &&
- (missed_count > priv->roaming_threshold &&
- missed_count <= priv->disassociate_threshold)) {
- /* If we are not already roaming, set the ROAM
- * bit in the status and kick off a scan.
- * This can happen several times before we reach
- * disassociate_threshold. */
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
- "Missed beacon: %d - initiate "
- "roaming\n", missed_count);
- if (!(priv->status & STATUS_ROAMING)) {
- priv->status |= STATUS_ROAMING;
- if (!(priv->status & STATUS_SCANNING))
- queue_delayed_work(priv->workqueue,
- &priv->request_scan, 0);
- }
- return;
- }
-
- if (priv->status & STATUS_SCANNING) {
- /* Stop scan to keep fw from getting
- * stuck (only if we aren't roaming --
- * otherwise we'll never scan more than 2 or 3
- * channels..) */
- IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
- "Aborting scan with missed beacon.\n");
- queue_work(priv->workqueue, &priv->abort_scan);
- }
-
- IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
-}
-
-static void ipw_scan_event(struct work_struct *work)
-{
- union iwreq_data wrqu;
-
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, scan_event.work);
-
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
-}
-
-static void handle_scan_event(struct ipw_priv *priv)
-{
- /* Only userspace-requested scan completion events go out immediately */
- if (!priv->user_requested_scan) {
- if (!delayed_work_pending(&priv->scan_event))
- queue_delayed_work(priv->workqueue, &priv->scan_event,
- round_jiffies_relative(msecs_to_jiffies(4000)));
- } else {
- union iwreq_data wrqu;
-
- priv->user_requested_scan = 0;
- cancel_delayed_work(&priv->scan_event);
-
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
- }
-}
-
-/**
- * Handle host notification packet.
- * Called from interrupt routine
- */
-static void ipw_rx_notification(struct ipw_priv *priv,
- struct ipw_rx_notification *notif)
-{
- DECLARE_MAC_BUF(mac);
- u16 size = le16_to_cpu(notif->size);
- notif->size = le16_to_cpu(notif->size);
-
- IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size);
-
- switch (notif->subtype) {
- case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
- struct notif_association *assoc = &notif->u.assoc;
-
- switch (assoc->state) {
- case CMAS_ASSOCIATED:{
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC,
- "associated: '%s' %s"
- " \n",
- escape_essid(priv->essid,
- priv->essid_len),
- print_mac(mac, priv->bssid));
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_INFRA:
- memcpy(priv->ieee->bssid,
- priv->bssid, ETH_ALEN);
- break;
-
- case IW_MODE_ADHOC:
- memcpy(priv->ieee->bssid,
- priv->bssid, ETH_ALEN);
-
- /* clear out the station table */
- priv->num_stations = 0;
-
- IPW_DEBUG_ASSOC
- ("queueing adhoc check\n");
- queue_delayed_work(priv->
- workqueue,
- &priv->
- adhoc_check,
- le16_to_cpu(priv->
- assoc_request.
- beacon_interval));
- break;
- }
-
- priv->status &= ~STATUS_ASSOCIATING;
- priv->status |= STATUS_ASSOCIATED;
- queue_work(priv->workqueue,
- &priv->system_config);
-
-#ifdef CONFIG_IPW2200_QOS
-#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
- le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_ctl))
- if ((priv->status & STATUS_AUTH) &&
- (IPW_GET_PACKET_STYPE(&notif->u.raw)
- == IEEE80211_STYPE_ASSOC_RESP)) {
- if ((sizeof
- (struct
- ieee80211_assoc_response)
- <= size)
- && (size <= 2314)) {
- struct
- ieee80211_rx_stats
- stats = {
- .len = size - 1,
- };
-
- IPW_DEBUG_QOS
- ("QoS Associate "
- "size %d\n", size);
- ieee80211_rx_mgt(priv->
- ieee,
- (struct
- ieee80211_hdr_4addr
- *)
- &notif->u.raw, &stats);
- }
- }
-#endif
-
- schedule_work(&priv->link_up);
-
- break;
- }
-
- case CMAS_AUTHENTICATED:{
- if (priv->
- status & (STATUS_ASSOCIATED |
- STATUS_AUTH)) {
- struct notif_authenticate *auth
- = &notif->u.auth;
- IPW_DEBUG(IPW_DL_NOTIF |
- IPW_DL_STATE |
- IPW_DL_ASSOC,
- "deauthenticated: '%s' "
- "%s"
- ": (0x%04X) - %s \n",
- escape_essid(priv->
- essid,
- priv->
- essid_len),
- print_mac(mac, priv->bssid),
- le16_to_cpu(auth->status),
- ipw_get_status_code
- (le16_to_cpu
- (auth->status)));
-
- priv->status &=
- ~(STATUS_ASSOCIATING |
- STATUS_AUTH |
- STATUS_ASSOCIATED);
-
- schedule_work(&priv->link_down);
- break;
- }
-
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC,
- "authenticated: '%s' %s"
- "\n",
- escape_essid(priv->essid,
- priv->essid_len),
- print_mac(mac, priv->bssid));
- break;
- }
-
- case CMAS_INIT:{
- if (priv->status & STATUS_AUTH) {
- struct
- ieee80211_assoc_response
- *resp;
- resp =
- (struct
- ieee80211_assoc_response
- *)&notif->u.raw;
- IPW_DEBUG(IPW_DL_NOTIF |
- IPW_DL_STATE |
- IPW_DL_ASSOC,
- "association failed (0x%04X): %s\n",
- le16_to_cpu(resp->status),
- ipw_get_status_code
- (le16_to_cpu
- (resp->status)));
- }
-
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC,
- "disassociated: '%s' %s"
- " \n",
- escape_essid(priv->essid,
- priv->essid_len),
- print_mac(mac, priv->bssid));
-
- priv->status &=
- ~(STATUS_DISASSOCIATING |
- STATUS_ASSOCIATING |
- STATUS_ASSOCIATED | STATUS_AUTH);
- if (priv->assoc_network
- && (priv->assoc_network->
- capability &
- WLAN_CAPABILITY_IBSS))
- ipw_remove_current_network
- (priv);
-
- schedule_work(&priv->link_down);
-
- break;
- }
-
- case CMAS_RX_ASSOC_RESP:
- break;
-
- default:
- IPW_ERROR("assoc: unknown (%d)\n",
- assoc->state);
- break;
- }
-
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
- struct notif_authenticate *auth = &notif->u.auth;
- switch (auth->state) {
- case CMAS_AUTHENTICATED:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
- "authenticated: '%s' %s \n",
- escape_essid(priv->essid,
- priv->essid_len),
- print_mac(mac, priv->bssid));
- priv->status |= STATUS_AUTH;
- break;
-
- case CMAS_INIT:
- if (priv->status & STATUS_AUTH) {
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC,
- "authentication failed (0x%04X): %s\n",
- le16_to_cpu(auth->status),
- ipw_get_status_code(le16_to_cpu
- (auth->
- status)));
- }
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC,
- "deauthenticated: '%s' %s\n",
- escape_essid(priv->essid,
- priv->essid_len),
- print_mac(mac, priv->bssid));
-
- priv->status &= ~(STATUS_ASSOCIATING |
- STATUS_AUTH |
- STATUS_ASSOCIATED);
-
- schedule_work(&priv->link_down);
- break;
-
- case CMAS_TX_AUTH_SEQ_1:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_1\n");
- break;
- case CMAS_RX_AUTH_SEQ_2:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_2\n");
- break;
- case CMAS_AUTH_SEQ_1_PASS:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
- break;
- case CMAS_AUTH_SEQ_1_FAIL:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
- break;
- case CMAS_TX_AUTH_SEQ_3:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_3\n");
- break;
- case CMAS_RX_AUTH_SEQ_4:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
- break;
- case CMAS_AUTH_SEQ_2_PASS:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
- break;
- case CMAS_AUTH_SEQ_2_FAIL:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
- break;
- case CMAS_TX_ASSOC:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "TX_ASSOC\n");
- break;
- case CMAS_RX_ASSOC_RESP:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
-
- break;
- case CMAS_ASSOCIATED:
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
- IPW_DL_ASSOC, "ASSOCIATED\n");
- break;
- default:
- IPW_DEBUG_NOTIF("auth: failure - %d\n",
- auth->state);
- break;
- }
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
- struct notif_channel_result *x =
- &notif->u.channel_result;
-
- if (size == sizeof(*x)) {
- IPW_DEBUG_SCAN("Scan result for channel %d\n",
- x->channel_num);
- } else {
- IPW_DEBUG_SCAN("Scan result of wrong size %d "
- "(should be %zd)\n",
- size, sizeof(*x));
- }
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
- struct notif_scan_complete *x = &notif->u.scan_complete;
- if (size == sizeof(*x)) {
- IPW_DEBUG_SCAN
- ("Scan completed: type %d, %d channels, "
- "%d status\n", x->scan_type,
- x->num_channels, x->status);
- } else {
- IPW_ERROR("Scan completed of wrong size %d "
- "(should be %zd)\n",
- size, sizeof(*x));
- }
-
- priv->status &=
- ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
-
- wake_up_interruptible(&priv->wait_state);
- cancel_delayed_work(&priv->scan_check);
-
- if (priv->status & STATUS_EXIT_PENDING)
- break;
-
- priv->ieee->scans++;
-
-#ifdef CONFIG_IPW2200_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- priv->status |= STATUS_SCAN_FORCED;
- queue_delayed_work(priv->workqueue,
- &priv->request_scan, 0);
- break;
- }
- priv->status &= ~STATUS_SCAN_FORCED;
-#endif /* CONFIG_IPW2200_MONITOR */
-
- /* Do queued direct scans first */
- if (priv->status & STATUS_DIRECT_SCAN_PENDING) {
- queue_delayed_work(priv->workqueue,
- &priv->request_direct_scan, 0);
- }
-
- if (!(priv->status & (STATUS_ASSOCIATED |
- STATUS_ASSOCIATING |
- STATUS_ROAMING |
- STATUS_DISASSOCIATING)))
- queue_work(priv->workqueue, &priv->associate);
- else if (priv->status & STATUS_ROAMING) {
- if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
- /* If a scan completed and we are in roam mode, then
- * the scan that completed was the one requested as a
- * result of entering roam... so, schedule the
- * roam work */
- queue_work(priv->workqueue,
- &priv->roam);
- else
- /* Don't schedule if we aborted the scan */
- priv->status &= ~STATUS_ROAMING;
- } else if (priv->status & STATUS_SCAN_PENDING)
- queue_delayed_work(priv->workqueue,
- &priv->request_scan, 0);
- else if (priv->config & CFG_BACKGROUND_SCAN
- && priv->status & STATUS_ASSOCIATED)
- queue_delayed_work(priv->workqueue,
- &priv->request_scan,
- round_jiffies_relative(HZ));
-
- /* Send an empty event to user space.
- * We don't send the received data on the event because
- * it would require us to do complex transcoding, and
- * we want to minimise the work done in the irq handler
- * Use a request to extract the data.
- * Also, we generate this even for any scan, regardless
- * on how the scan was initiated. User space can just
- * sync on periodic scan to get fresh data...
- * Jean II */
- if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
- handle_scan_event(priv);
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
- struct notif_frag_length *x = &notif->u.frag_len;
-
- if (size == sizeof(*x))
- IPW_ERROR("Frag length: %d\n",
- le16_to_cpu(x->frag_length));
- else
- IPW_ERROR("Frag length of wrong size %d "
- "(should be %zd)\n",
- size, sizeof(*x));
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
- struct notif_link_deterioration *x =
- &notif->u.link_deterioration;
-
- if (size == sizeof(*x)) {
- IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
- "link deterioration: type %d, cnt %d\n",
- x->silence_notification_type,
- x->silence_count);
- memcpy(&priv->last_link_deterioration, x,
- sizeof(*x));
- } else {
- IPW_ERROR("Link Deterioration of wrong size %d "
- "(should be %zd)\n",
- size, sizeof(*x));
- }
- break;
- }
-
- case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
- IPW_ERROR("Dino config\n");
- if (priv->hcmd
- && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
- IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
-
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
- struct notif_beacon_state *x = &notif->u.beacon_state;
- if (size != sizeof(*x)) {
- IPW_ERROR
- ("Beacon state of wrong size %d (should "
- "be %zd)\n", size, sizeof(*x));
- break;
- }
-
- if (le32_to_cpu(x->state) ==
- HOST_NOTIFICATION_STATUS_BEACON_MISSING)
- ipw_handle_missed_beacon(priv,
- le32_to_cpu(x->
- number));
-
- break;
- }
-
- case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
- struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
- if (size == sizeof(*x)) {
- IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
- "0x%02x station %d\n",
- x->key_state, x->security_type,
- x->station_index);
- break;
- }
-
- IPW_ERROR
- ("TGi Tx Key of wrong size %d (should be %zd)\n",
- size, sizeof(*x));
- break;
- }
-
- case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
- struct notif_calibration *x = &notif->u.calibration;
-
- if (size == sizeof(*x)) {
- memcpy(&priv->calib, x, sizeof(*x));
- IPW_DEBUG_INFO("TODO: Calibration\n");
- break;
- }
-
- IPW_ERROR
- ("Calibration of wrong size %d (should be %zd)\n",
- size, sizeof(*x));
- break;
- }
-
- case HOST_NOTIFICATION_NOISE_STATS:{
- if (size == sizeof(u32)) {
- priv->exp_avg_noise =
- exponential_average(priv->exp_avg_noise,
- (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
- DEPTH_NOISE);
- break;
- }
-
- IPW_ERROR
- ("Noise stat is wrong size %d (should be %zd)\n",
- size, sizeof(u32));
- break;
- }
-
- default:
- IPW_DEBUG_NOTIF("Unknown notification: "
- "subtype=%d,flags=0x%2x,size=%d\n",
- notif->subtype, notif->flags, size);
- }
-}
-
-/**
- * Destroys all DMA structures and initialise them again
- *
- * @param priv
- * @return error code
- */
-static int ipw_queue_reset(struct ipw_priv *priv)
-{
- int rc = 0;
- /** @todo customize queue sizes */
- int nTx = 64, nTxCmd = 8;
- ipw_tx_queue_free(priv);
- /* Tx CMD queue */
- rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
- IPW_TX_CMD_QUEUE_READ_INDEX,
- IPW_TX_CMD_QUEUE_WRITE_INDEX,
- IPW_TX_CMD_QUEUE_BD_BASE,
- IPW_TX_CMD_QUEUE_BD_SIZE);
- if (rc) {
- IPW_ERROR("Tx Cmd queue init failed\n");
- goto error;
- }
- /* Tx queue(s) */
- rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
- IPW_TX_QUEUE_0_READ_INDEX,
- IPW_TX_QUEUE_0_WRITE_INDEX,
- IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
- if (rc) {
- IPW_ERROR("Tx 0 queue init failed\n");
- goto error;
- }
- rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
- IPW_TX_QUEUE_1_READ_INDEX,
- IPW_TX_QUEUE_1_WRITE_INDEX,
- IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
- if (rc) {
- IPW_ERROR("Tx 1 queue init failed\n");
- goto error;
- }
- rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
- IPW_TX_QUEUE_2_READ_INDEX,
- IPW_TX_QUEUE_2_WRITE_INDEX,
- IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
- if (rc) {
- IPW_ERROR("Tx 2 queue init failed\n");
- goto error;
- }
- rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
- IPW_TX_QUEUE_3_READ_INDEX,
- IPW_TX_QUEUE_3_WRITE_INDEX,
- IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
- if (rc) {
- IPW_ERROR("Tx 3 queue init failed\n");
- goto error;
- }
- /* statistics */
- priv->rx_bufs_min = 0;
- priv->rx_pend_max = 0;
- return rc;
-
- error:
- ipw_tx_queue_free(priv);
- return rc;
-}
-
-/**
- * Reclaim Tx queue entries no more used by NIC.
- *
- * When FW advances 'R' index, all entries between old and
- * new 'R' index need to be reclaimed. As result, some free space
- * forms. If there is enough free space (> low mark), wake Tx queue.
- *
- * @note Need to protect against garbage in 'R' index
- * @param priv
- * @param txq
- * @param qindex
- * @return Number of used entries remains in the queue
- */
-static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
- struct clx2_tx_queue *txq, int qindex)
-{
- u32 hw_tail;
- int used;
- struct clx2_queue *q = &txq->q;
-
- hw_tail = ipw_read32(priv, q->reg_r);
- if (hw_tail >= q->n_bd) {
- IPW_ERROR
- ("Read index for DMA queue (%d) is out of range [0-%d)\n",
- hw_tail, q->n_bd);
- goto done;
- }
- for (; q->last_used != hw_tail;
- q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
- ipw_queue_tx_free_tfd(priv, txq);
- priv->tx_packets++;
- }
- done:
- if ((ipw_tx_queue_space(q) > q->low_mark) &&
- (qindex >= 0))
- netif_wake_queue(priv->net_dev);
- used = q->first_empty - q->last_used;
- if (used < 0)
- used += q->n_bd;
-
- return used;
-}
-
-static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
- int len, int sync)
-{
- struct clx2_tx_queue *txq = &priv->txq_cmd;
- struct clx2_queue *q = &txq->q;
- struct tfd_frame *tfd;
-
- if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) {
- IPW_ERROR("No space for Tx\n");
- return -EBUSY;
- }
-
- tfd = &txq->bd[q->first_empty];
- txq->txb[q->first_empty] = NULL;
-
- memset(tfd, 0, sizeof(*tfd));
- tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
- tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
- priv->hcmd_seq++;
- tfd->u.cmd.index = hcmd;
- tfd->u.cmd.length = len;
- memcpy(tfd->u.cmd.payload, buf, len);
- q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
- ipw_write32(priv, q->reg_w, q->first_empty);
- _ipw_read32(priv, 0x90);
-
- return 0;
-}
-
-/*
- * Rx theory of operation
- *
- * The host allocates 32 DMA target addresses and passes the host address
- * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
- * 0 to 31
- *
- * Rx Queue Indexes
- * The host/firmware share two index registers for managing the Rx buffers.
- *
- * The READ index maps to the first position that the firmware may be writing
- * to -- the driver can read up to (but not including) this position and get
- * good data.
- * The READ index is managed by the firmware once the card is enabled.
- *
- * The WRITE index maps to the last position the driver has read from -- the
- * position preceding WRITE is the last slot the firmware can place a packet.
- *
- * The queue is empty (no good data) if WRITE = READ - 1, and is full if
- * WRITE = READ.
- *
- * During initialization the host sets up the READ queue position to the first
- * INDEX position, and WRITE to the last (READ - 1 wrapped)
- *
- * When the firmware places a packet in a buffer it will advance the READ index
- * and fire the RX interrupt. The driver can then query the READ index and
- * process as many packets as possible, moving the WRITE index forward as it
- * resets the Rx queue buffers with new memory.
- *
- * The management in the driver is as follows:
- * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
- * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
- * to replensish the ipw->rxq->rx_free.
- * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
- * ipw->rxq is replenished and the READ INDEX is updated (updating the
- * 'processed' and 'read' driver indexes as well)
- * + A received packet is processed and handed to the kernel network stack,
- * detached from the ipw->rxq. The driver 'processed' index is updated.
- * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
- * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
- * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
- * were enough free buffers and RX_STALLED is set it is cleared.
- *
- *
- * Driver sequence:
- *
- * ipw_rx_queue_alloc() Allocates rx_free
- * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
- * ipw_rx_queue_restock
- * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
- * queue, updates firmware pointers, and updates
- * the WRITE index. If insufficient rx_free buffers
- * are available, schedules ipw_rx_queue_replenish
- *
- * -- enable interrupts --
- * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
- * READ INDEX, detaching the SKB from the pool.
- * Moves the packet buffer from queue to rx_used.
- * Calls ipw_rx_queue_restock to refill any empty
- * slots.
- * ...
- *
- */
-
-/*
- * If there are slots in the RX queue that need to be restocked,
- * and we have free pre-allocated buffers, fill the ranks as much
- * as we can pulling from rx_free.
- *
- * This moves the 'write' index forward to catch up with 'processed', and
- * also updates the memory address in the firmware to reference the new
- * target buffer.
- */
-static void ipw_rx_queue_restock(struct ipw_priv *priv)
-{
- struct ipw_rx_queue *rxq = priv->rxq;
- struct list_head *element;
- struct ipw_rx_mem_buffer *rxb;
- unsigned long flags;
- int write;
-
- spin_lock_irqsave(&rxq->lock, flags);
- write = rxq->write;
- while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
- element = rxq->rx_free.next;
- rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
- list_del(element);
-
- ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
- rxb->dma_addr);
- rxq->queue[rxq->write] = rxb;
- rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
- rxq->free_count--;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- /* If the pre-allocated buffer pool is dropping low, schedule to
- * refill it */
- if (rxq->free_count <= RX_LOW_WATERMARK)
- queue_work(priv->workqueue, &priv->rx_replenish);
-
- /* If we've added more space for the firmware to place data, tell it */
- if (write != rxq->write)
- ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
-}
-
-/*
- * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
- * Also restock the Rx queue via ipw_rx_queue_restock.
- *
- * This is called as a scheduled work item (except for during intialization)
- */
-static void ipw_rx_queue_replenish(void *data)
-{
- struct ipw_priv *priv = data;
- struct ipw_rx_queue *rxq = priv->rxq;
- struct list_head *element;
- struct ipw_rx_mem_buffer *rxb;
- unsigned long flags;
-
- spin_lock_irqsave(&rxq->lock, flags);
- while (!list_empty(&rxq->rx_used)) {
- element = rxq->rx_used.next;
- rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
- rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
- if (!rxb->skb) {
- printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
- priv->net_dev->name);
- /* We don't reschedule replenish work here -- we will
- * call the restock method and if it still needs
- * more buffers it will schedule replenish */
- break;
- }
- list_del(element);
-
- rxb->dma_addr =
- pci_map_single(priv->pci_dev, rxb->skb->data,
- IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
-
- list_add_tail(&rxb->list, &rxq->rx_free);
- rxq->free_count++;
- }
- spin_unlock_irqrestore(&rxq->lock, flags);
-
- ipw_rx_queue_restock(priv);
-}
-
-static void ipw_bg_rx_queue_replenish(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, rx_replenish);
- mutex_lock(&priv->mutex);
- ipw_rx_queue_replenish(priv);
- mutex_unlock(&priv->mutex);
-}
-
-/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
- * If an SKB has been detached, the POOL needs to have its SKB set to NULL
- * This free routine walks the list of POOL entries and if SKB is set to
- * non NULL it is unmapped and freed
- */
-static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
-{
- int i;
-
- if (!rxq)
- return;
-
- for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
- IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
- dev_kfree_skb(rxq->pool[i].skb);
- }
- }
-
- kfree(rxq);
-}
-
-static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
-{
- struct ipw_rx_queue *rxq;
- int i;
-
- rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
- if (unlikely(!rxq)) {
- IPW_ERROR("memory allocation failed\n");
- return NULL;
- }
- spin_lock_init(&rxq->lock);
- INIT_LIST_HEAD(&rxq->rx_free);
- INIT_LIST_HEAD(&rxq->rx_used);
-
- /* Fill the rx_used queue with _all_ of the Rx buffers */
- for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
- list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
-
- /* Set us so that we have processed and used all buffers, but have
- * not restocked the Rx queue with fresh buffers */
- rxq->read = rxq->write = 0;
- rxq->free_count = 0;
-
- return rxq;
-}
-
-static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
-{
- rate &= ~IEEE80211_BASIC_RATE_MASK;
- if (ieee_mode == IEEE_A) {
- switch (rate) {
- case IEEE80211_OFDM_RATE_6MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
- 1 : 0;
- case IEEE80211_OFDM_RATE_9MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
- 1 : 0;
- case IEEE80211_OFDM_RATE_12MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_18MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_24MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_36MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_48MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_54MB:
- return priv->
- rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
- default:
- return 0;
- }
- }
-
- /* B and G mixed */
- switch (rate) {
- case IEEE80211_CCK_RATE_1MB:
- return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
- case IEEE80211_CCK_RATE_2MB:
- return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
- case IEEE80211_CCK_RATE_5MB:
- return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
- case IEEE80211_CCK_RATE_11MB:
- return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
- }
-
- /* If we are limited to B modulations, bail at this point */
- if (ieee_mode == IEEE_B)
- return 0;
-
- /* G */
- switch (rate) {
- case IEEE80211_OFDM_RATE_6MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_9MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_12MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_18MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_24MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_36MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_48MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
- case IEEE80211_OFDM_RATE_54MB:
- return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
- }
-
- return 0;
-}
-
-static int ipw_compatible_rates(struct ipw_priv *priv,
- const struct ieee80211_network *network,
- struct ipw_supported_rates *rates)
-{
- int num_rates, i;
-
- memset(rates, 0, sizeof(*rates));
- num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
- rates->num_rates = 0;
- for (i = 0; i < num_rates; i++) {
- if (!ipw_is_rate_in_mask(priv, network->mode,
- network->rates[i])) {
-
- if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
- IPW_DEBUG_SCAN("Adding masked mandatory "
- "rate %02X\n",
- network->rates[i]);
- rates->supported_rates[rates->num_rates++] =
- network->rates[i];
- continue;
- }
-
- IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
- network->rates[i], priv->rates_mask);
- continue;
- }
-
- rates->supported_rates[rates->num_rates++] = network->rates[i];
- }
-
- num_rates = min(network->rates_ex_len,
- (u8) (IPW_MAX_RATES - num_rates));
- for (i = 0; i < num_rates; i++) {
- if (!ipw_is_rate_in_mask(priv, network->mode,
- network->rates_ex[i])) {
- if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
- IPW_DEBUG_SCAN("Adding masked mandatory "
- "rate %02X\n",
- network->rates_ex[i]);
- rates->supported_rates[rates->num_rates++] =
- network->rates[i];
- continue;
- }
-
- IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
- network->rates_ex[i], priv->rates_mask);
- continue;
- }
-
- rates->supported_rates[rates->num_rates++] =
- network->rates_ex[i];
- }
-
- return 1;
-}
-
-static void ipw_copy_rates(struct ipw_supported_rates *dest,
- const struct ipw_supported_rates *src)
-{
- u8 i;
- for (i = 0; i < src->num_rates; i++)
- dest->supported_rates[i] = src->supported_rates[i];
- dest->num_rates = src->num_rates;
-}
-
-/* TODO: Look at sniffed packets in the air to determine if the basic rate
- * mask should ever be used -- right now all callers to add the scan rates are
- * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
-static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
- u8 modulation, u32 rate_mask)
-{
- u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
- IEEE80211_BASIC_RATE_MASK : 0;
-
- if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
-
- if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
-
- if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
- rates->supported_rates[rates->num_rates++] = basic_mask |
- IEEE80211_CCK_RATE_5MB;
-
- if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
- rates->supported_rates[rates->num_rates++] = basic_mask |
- IEEE80211_CCK_RATE_11MB;
-}
-
-static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
- u8 modulation, u32 rate_mask)
-{
- u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
- IEEE80211_BASIC_RATE_MASK : 0;
-
- if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
- rates->supported_rates[rates->num_rates++] = basic_mask |
- IEEE80211_OFDM_RATE_6MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_OFDM_RATE_9MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
- rates->supported_rates[rates->num_rates++] = basic_mask |
- IEEE80211_OFDM_RATE_12MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_OFDM_RATE_18MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
- rates->supported_rates[rates->num_rates++] = basic_mask |
- IEEE80211_OFDM_RATE_24MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_OFDM_RATE_36MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_OFDM_RATE_48MB;
-
- if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
- rates->supported_rates[rates->num_rates++] =
- IEEE80211_OFDM_RATE_54MB;
-}
-
-struct ipw_network_match {
- struct ieee80211_network *network;
- struct ipw_supported_rates rates;
-};
-
-static int ipw_find_adhoc_network(struct ipw_priv *priv,
- struct ipw_network_match *match,
- struct ieee80211_network *network,
- int roaming)
-{
- struct ipw_supported_rates rates;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
-
- /* Verify that this network's capability is compatible with the
- * current mode (AdHoc or Infrastructure) */
- if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
- !(network->capability & WLAN_CAPABILITY_IBSS))) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded due to "
- "capability mismatch.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* If we do not have an ESSID for this AP, we can not associate with
- * it */
- if (network->flags & NETWORK_EMPTY_ESSID) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of hidden ESSID.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- if (unlikely(roaming)) {
- /* If we are roaming, then ensure check if this is a valid
- * network to try and roam to */
- if ((network->ssid_len != match->network->ssid_len) ||
- memcmp(network->ssid, match->network->ssid,
- network->ssid_len)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of non-network ESSID.\n",
- escape_essid(network->ssid,
- network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
- } else {
- /* If an ESSID has been configured then compare the broadcast
- * ESSID to ours */
- if ((priv->config & CFG_STATIC_ESSID) &&
- ((network->ssid_len != priv->essid_len) ||
- memcmp(network->ssid, priv->essid,
- min(network->ssid_len, priv->essid_len)))) {
- char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
-
- strncpy(escaped,
- escape_essid(network->ssid, network->ssid_len),
- sizeof(escaped));
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of ESSID mismatch: '%s'.\n",
- escaped, print_mac(mac, network->bssid),
- escape_essid(priv->essid,
- priv->essid_len));
- return 0;
- }
- }
-
- /* If the old network rate is better than this one, don't bother
- * testing everything else. */
-
- if (network->time_stamp[0] < match->network->time_stamp[0]) {
- IPW_DEBUG_MERGE("Network '%s excluded because newer than "
- "current network.\n",
- escape_essid(match->network->ssid,
- match->network->ssid_len));
- return 0;
- } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
- IPW_DEBUG_MERGE("Network '%s excluded because newer than "
- "current network.\n",
- escape_essid(match->network->ssid,
- match->network->ssid_len));
- return 0;
- }
-
- /* Now go through and see if the requested network is valid... */
- if (priv->ieee->scan_age != 0 &&
- time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of age: %ums.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- jiffies_to_msecs(jiffies -
- network->last_scanned));
- return 0;
- }
-
- if ((priv->config & CFG_STATIC_CHANNEL) &&
- (network->channel != priv->channel)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of channel mismatch: %d != %d.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- network->channel, priv->channel);
- return 0;
- }
-
- /* Verify privacy compatability */
- if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
- ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of privacy mismatch: %s != %s.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- priv->
- capability & CAP_PRIVACY_ON ? "on" : "off",
- network->
- capability & WLAN_CAPABILITY_PRIVACY ? "on" :
- "off");
- return 0;
- }
-
- if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of the same BSSID match: %s"
- ".\n", escape_essid(network->ssid,
- network->ssid_len),
- print_mac(mac, network->bssid),
- print_mac(mac2, priv->bssid));
- return 0;
- }
-
- /* Filter out any incompatible freq / mode combinations */
- if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of invalid frequency/mode "
- "combination.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* Ensure that the rates supported by the driver are compatible with
- * this AP, including verification of basic rates (mandatory) */
- if (!ipw_compatible_rates(priv, network, &rates)) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because configured rate mask excludes "
- "AP mandatory rate.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- if (rates.num_rates == 0) {
- IPW_DEBUG_MERGE("Network '%s (%s)' excluded "
- "because of no compatible rates.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* TODO: Perform any further minimal comparititive tests. We do not
- * want to put too much policy logic here; intelligent scan selection
- * should occur within a generic IEEE 802.11 user space tool. */
-
- /* Set up 'new' AP to this network */
- ipw_copy_rates(&match->rates, &rates);
- match->network = network;
- IPW_DEBUG_MERGE("Network '%s (%s)' is a viable match.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
-
- return 1;
-}
-
-static void ipw_merge_adhoc_network(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, merge_networks);
- struct ieee80211_network *network = NULL;
- struct ipw_network_match match = {
- .network = priv->assoc_network
- };
-
- if ((priv->status & STATUS_ASSOCIATED) &&
- (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
- /* First pass through ROAM process -- look for a better
- * network */
- unsigned long flags;
-
- spin_lock_irqsave(&priv->ieee->lock, flags);
- list_for_each_entry(network, &priv->ieee->network_list, list) {
- if (network != priv->assoc_network)
- ipw_find_adhoc_network(priv, &match, network,
- 1);
- }
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
- if (match.network == priv->assoc_network) {
- IPW_DEBUG_MERGE("No better ADHOC in this network to "
- "merge to.\n");
- return;
- }
-
- mutex_lock(&priv->mutex);
- if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
- IPW_DEBUG_MERGE("remove network %s\n",
- escape_essid(priv->essid,
- priv->essid_len));
- ipw_remove_current_network(priv);
- }
-
- ipw_disassociate(priv);
- priv->assoc_network = match.network;
- mutex_unlock(&priv->mutex);
- return;
- }
-}
-
-static int ipw_best_network(struct ipw_priv *priv,
- struct ipw_network_match *match,
- struct ieee80211_network *network, int roaming)
-{
- struct ipw_supported_rates rates;
- DECLARE_MAC_BUF(mac);
-
- /* Verify that this network's capability is compatible with the
- * current mode (AdHoc or Infrastructure) */
- if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
- !(network->capability & WLAN_CAPABILITY_ESS)) ||
- (priv->ieee->iw_mode == IW_MODE_ADHOC &&
- !(network->capability & WLAN_CAPABILITY_IBSS))) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded due to "
- "capability mismatch.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* If we do not have an ESSID for this AP, we can not associate with
- * it */
- if (network->flags & NETWORK_EMPTY_ESSID) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of hidden ESSID.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- if (unlikely(roaming)) {
- /* If we are roaming, then ensure check if this is a valid
- * network to try and roam to */
- if ((network->ssid_len != match->network->ssid_len) ||
- memcmp(network->ssid, match->network->ssid,
- network->ssid_len)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of non-network ESSID.\n",
- escape_essid(network->ssid,
- network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
- } else {
- /* If an ESSID has been configured then compare the broadcast
- * ESSID to ours */
- if ((priv->config & CFG_STATIC_ESSID) &&
- ((network->ssid_len != priv->essid_len) ||
- memcmp(network->ssid, priv->essid,
- min(network->ssid_len, priv->essid_len)))) {
- char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- strncpy(escaped,
- escape_essid(network->ssid, network->ssid_len),
- sizeof(escaped));
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of ESSID mismatch: '%s'.\n",
- escaped, print_mac(mac, network->bssid),
- escape_essid(priv->essid,
- priv->essid_len));
- return 0;
- }
- }
-
- /* If the old network rate is better than this one, don't bother
- * testing everything else. */
- if (match->network && match->network->stats.rssi > network->stats.rssi) {
- char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- strncpy(escaped,
- escape_essid(network->ssid, network->ssid_len),
- sizeof(escaped));
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded because "
- "'%s (%s)' has a stronger signal.\n",
- escaped, print_mac(mac, network->bssid),
- escape_essid(match->network->ssid,
- match->network->ssid_len),
- print_mac(mac, match->network->bssid));
- return 0;
- }
-
- /* If this network has already had an association attempt within the
- * last 3 seconds, do not try and associate again... */
- if (network->last_associate &&
- time_after(network->last_associate + (HZ * 3UL), jiffies)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of storming (%ums since last "
- "assoc attempt).\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- jiffies_to_msecs(jiffies -
- network->last_associate));
- return 0;
- }
-
- /* Now go through and see if the requested network is valid... */
- if (priv->ieee->scan_age != 0 &&
- time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of age: %ums.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- jiffies_to_msecs(jiffies -
- network->last_scanned));
- return 0;
- }
-
- if ((priv->config & CFG_STATIC_CHANNEL) &&
- (network->channel != priv->channel)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of channel mismatch: %d != %d.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- network->channel, priv->channel);
- return 0;
- }
-
- /* Verify privacy compatability */
- if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
- ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of privacy mismatch: %s != %s.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid),
- priv->capability & CAP_PRIVACY_ON ? "on" :
- "off",
- network->capability &
- WLAN_CAPABILITY_PRIVACY ? "on" : "off");
- return 0;
- }
-
- if ((priv->config & CFG_STATIC_BSSID) &&
- memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of BSSID mismatch: %s.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid), print_mac(mac, priv->bssid));
- return 0;
- }
-
- /* Filter out any incompatible freq / mode combinations */
- if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of invalid frequency/mode "
- "combination.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* Filter out invalid channel in current GEO */
- if (!ieee80211_is_valid_channel(priv->ieee, network->channel)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of invalid channel in current GEO\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* Ensure that the rates supported by the driver are compatible with
- * this AP, including verification of basic rates (mandatory) */
- if (!ipw_compatible_rates(priv, network, &rates)) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because configured rate mask excludes "
- "AP mandatory rate.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- if (rates.num_rates == 0) {
- IPW_DEBUG_ASSOC("Network '%s (%s)' excluded "
- "because of no compatible rates.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
- return 0;
- }
-
- /* TODO: Perform any further minimal comparititive tests. We do not
- * want to put too much policy logic here; intelligent scan selection
- * should occur within a generic IEEE 802.11 user space tool. */
-
- /* Set up 'new' AP to this network */
- ipw_copy_rates(&match->rates, &rates);
- match->network = network;
-
- IPW_DEBUG_ASSOC("Network '%s (%s)' is a viable match.\n",
- escape_essid(network->ssid, network->ssid_len),
- print_mac(mac, network->bssid));
-
- return 1;
-}
-
-static void ipw_adhoc_create(struct ipw_priv *priv,
- struct ieee80211_network *network)
-{
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
- int i;
-
- /*
- * For the purposes of scanning, we can set our wireless mode
- * to trigger scans across combinations of bands, but when it
- * comes to creating a new ad-hoc network, we have tell the FW
- * exactly which band to use.
- *
- * We also have the possibility of an invalid channel for the
- * chossen band. Attempting to create a new ad-hoc network
- * with an invalid channel for wireless mode will trigger a
- * FW fatal error.
- *
- */
- switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
- case IEEE80211_52GHZ_BAND:
- network->mode = IEEE_A;
- i = ieee80211_channel_to_index(priv->ieee, priv->channel);
- BUG_ON(i == -1);
- if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
- IPW_WARNING("Overriding invalid channel\n");
- priv->channel = geo->a[0].channel;
- }
- break;
-
- case IEEE80211_24GHZ_BAND:
- if (priv->ieee->mode & IEEE_G)
- network->mode = IEEE_G;
- else
- network->mode = IEEE_B;
- i = ieee80211_channel_to_index(priv->ieee, priv->channel);
- BUG_ON(i == -1);
- if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
- IPW_WARNING("Overriding invalid channel\n");
- priv->channel = geo->bg[0].channel;
- }
- break;
-
- default:
- IPW_WARNING("Overriding invalid channel\n");
- if (priv->ieee->mode & IEEE_A) {
- network->mode = IEEE_A;
- priv->channel = geo->a[0].channel;
- } else if (priv->ieee->mode & IEEE_G) {
- network->mode = IEEE_G;
- priv->channel = geo->bg[0].channel;
- } else {
- network->mode = IEEE_B;
- priv->channel = geo->bg[0].channel;
- }
- break;
- }
-
- network->channel = priv->channel;
- priv->config |= CFG_ADHOC_PERSIST;
- ipw_create_bssid(priv, network->bssid);
- network->ssid_len = priv->essid_len;
- memcpy(network->ssid, priv->essid, priv->essid_len);
- memset(&network->stats, 0, sizeof(network->stats));
- network->capability = WLAN_CAPABILITY_IBSS;
- if (!(priv->config & CFG_PREAMBLE_LONG))
- network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
- if (priv->capability & CAP_PRIVACY_ON)
- network->capability |= WLAN_CAPABILITY_PRIVACY;
- network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
- memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
- network->rates_ex_len = priv->rates.num_rates - network->rates_len;
- memcpy(network->rates_ex,
- &priv->rates.supported_rates[network->rates_len],
- network->rates_ex_len);
- network->last_scanned = 0;
- network->flags = 0;
- network->last_associate = 0;
- network->time_stamp[0] = 0;
- network->time_stamp[1] = 0;
- network->beacon_interval = 100; /* Default */
- network->listen_interval = 10; /* Default */
- network->atim_window = 0; /* Default */
- network->wpa_ie_len = 0;
- network->rsn_ie_len = 0;
-}
-
-static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
-{
- struct ipw_tgi_tx_key key;
-
- if (!(priv->ieee->sec.flags & (1 << index)))
- return;
-
- key.key_id = index;
- memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
- key.security_type = type;
- key.station_index = 0; /* always 0 for BSS */
- key.flags = 0;
- /* 0 for new key; previous value of counter (after fatal error) */
- key.tx_counter[0] = cpu_to_le32(0);
- key.tx_counter[1] = cpu_to_le32(0);
-
- ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key);
-}
-
-static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
-{
- struct ipw_wep_key key;
- int i;
-
- key.cmd_id = DINO_CMD_WEP_KEY;
- key.seq_num = 0;
-
- /* Note: AES keys cannot be set for multiple times.
- * Only set it at the first time. */
- for (i = 0; i < 4; i++) {
- key.key_index = i | type;
- if (!(priv->ieee->sec.flags & (1 << i))) {
- key.key_size = 0;
- continue;
- }
-
- key.key_size = priv->ieee->sec.key_sizes[i];
- memcpy(key.key, priv->ieee->sec.keys[i], key.key_size);
-
- ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key);
- }
-}
-
-static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
-{
- if (priv->ieee->host_encrypt)
- return;
-
- switch (level) {
- case SEC_LEVEL_3:
- priv->sys_config.disable_unicast_decryption = 0;
- priv->ieee->host_decrypt = 0;
- break;
- case SEC_LEVEL_2:
- priv->sys_config.disable_unicast_decryption = 1;
- priv->ieee->host_decrypt = 1;
- break;
- case SEC_LEVEL_1:
- priv->sys_config.disable_unicast_decryption = 0;
- priv->ieee->host_decrypt = 0;
- break;
- case SEC_LEVEL_0:
- priv->sys_config.disable_unicast_decryption = 1;
- break;
- default:
- break;
- }
-}
-
-static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
-{
- if (priv->ieee->host_encrypt)
- return;
-
- switch (level) {
- case SEC_LEVEL_3:
- priv->sys_config.disable_multicast_decryption = 0;
- break;
- case SEC_LEVEL_2:
- priv->sys_config.disable_multicast_decryption = 1;
- break;
- case SEC_LEVEL_1:
- priv->sys_config.disable_multicast_decryption = 0;
- break;
- case SEC_LEVEL_0:
- priv->sys_config.disable_multicast_decryption = 1;
- break;
- default:
- break;
- }
-}
-
-static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
-{
- switch (priv->ieee->sec.level) {
- case SEC_LEVEL_3:
- if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
- ipw_send_tgi_tx_key(priv,
- DCT_FLAG_EXT_SECURITY_CCM,
- priv->ieee->sec.active_key);
-
- if (!priv->ieee->host_mc_decrypt)
- ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
- break;
- case SEC_LEVEL_2:
- if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
- ipw_send_tgi_tx_key(priv,
- DCT_FLAG_EXT_SECURITY_TKIP,
- priv->ieee->sec.active_key);
- break;
- case SEC_LEVEL_1:
- ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
- ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
- ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
- break;
- case SEC_LEVEL_0:
- default:
- break;
- }
-}
-
-static void ipw_adhoc_check(void *data)
-{
- struct ipw_priv *priv = data;
-
- if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
- !(priv->config & CFG_ADHOC_PERSIST)) {
- IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
- IPW_DL_STATE | IPW_DL_ASSOC,
- "Missed beacon: %d - disassociate\n",
- priv->missed_adhoc_beacons);
- ipw_remove_current_network(priv);
- ipw_disassociate(priv);
- return;
- }
-
- queue_delayed_work(priv->workqueue, &priv->adhoc_check,
- le16_to_cpu(priv->assoc_request.beacon_interval));
-}
-
-static void ipw_bg_adhoc_check(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, adhoc_check.work);
- mutex_lock(&priv->mutex);
- ipw_adhoc_check(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_debug_config(struct ipw_priv *priv)
-{
- DECLARE_MAC_BUF(mac);
- IPW_DEBUG_INFO("Scan completed, no valid APs matched "
- "[CFG 0x%08X]\n", priv->config);
- if (priv->config & CFG_STATIC_CHANNEL)
- IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
- else
- IPW_DEBUG_INFO("Channel unlocked.\n");
- if (priv->config & CFG_STATIC_ESSID)
- IPW_DEBUG_INFO("ESSID locked to '%s'\n",
- escape_essid(priv->essid, priv->essid_len));
- else
- IPW_DEBUG_INFO("ESSID unlocked.\n");
- if (priv->config & CFG_STATIC_BSSID)
- IPW_DEBUG_INFO("BSSID locked to %s\n",
- print_mac(mac, priv->bssid));
- else
- IPW_DEBUG_INFO("BSSID unlocked.\n");
- if (priv->capability & CAP_PRIVACY_ON)
- IPW_DEBUG_INFO("PRIVACY on\n");
- else
- IPW_DEBUG_INFO("PRIVACY off\n");
- IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
-}
-
-static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
-{
- /* TODO: Verify that this works... */
- struct ipw_fixed_rate fr = {
- .tx_rates = priv->rates_mask
- };
- u32 reg;
- u16 mask = 0;
-
- /* Identify 'current FW band' and match it with the fixed
- * Tx rates */
-
- switch (priv->ieee->freq_band) {
- case IEEE80211_52GHZ_BAND: /* A only */
- /* IEEE_A */
- if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
- /* Invalid fixed rate mask */
- IPW_DEBUG_WX
- ("invalid fixed rate mask in ipw_set_fixed_rate\n");
- fr.tx_rates = 0;
- break;
- }
-
- fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
- break;
-
- default: /* 2.4Ghz or Mixed */
- /* IEEE_B */
- if (mode == IEEE_B) {
- if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
- /* Invalid fixed rate mask */
- IPW_DEBUG_WX
- ("invalid fixed rate mask in ipw_set_fixed_rate\n");
- fr.tx_rates = 0;
- }
- break;
- }
-
- /* IEEE_G */
- if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
- IEEE80211_OFDM_RATES_MASK)) {
- /* Invalid fixed rate mask */
- IPW_DEBUG_WX
- ("invalid fixed rate mask in ipw_set_fixed_rate\n");
- fr.tx_rates = 0;
- break;
- }
-
- if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
- mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
- fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
- }
-
- if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
- mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
- fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
- }
-
- if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
- mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
- fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
- }
-
- fr.tx_rates |= mask;
- break;
- }
-
- reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
- ipw_write_reg32(priv, reg, *(u32 *) & fr);
-}
-
-static void ipw_abort_scan(struct ipw_priv *priv)
-{
- int err;
-
- if (priv->status & STATUS_SCAN_ABORTING) {
- IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
- return;
- }
- priv->status |= STATUS_SCAN_ABORTING;
-
- err = ipw_send_scan_abort(priv);
- if (err)
- IPW_DEBUG_HC("Request to abort scan failed.\n");
-}
-
-static void ipw_add_scan_channels(struct ipw_priv *priv,
- struct ipw_scan_request_ext *scan,
- int scan_type)
-{
- int channel_index = 0;
- const struct ieee80211_geo *geo;
- int i;
-
- geo = ieee80211_get_geo(priv->ieee);
-
- if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
- int start = channel_index;
- for (i = 0; i < geo->a_channels; i++) {
- if ((priv->status & STATUS_ASSOCIATED) &&
- geo->a[i].channel == priv->channel)
- continue;
- channel_index++;
- scan->channels_list[channel_index] = geo->a[i].channel;
- ipw_set_scan_type(scan, channel_index,
- geo->a[i].
- flags & IEEE80211_CH_PASSIVE_ONLY ?
- IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
- scan_type);
- }
-
- if (start != channel_index) {
- scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
- (channel_index - start);
- channel_index++;
- }
- }
-
- if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
- int start = channel_index;
- if (priv->config & CFG_SPEED_SCAN) {
- int index;
- u8 channels[IEEE80211_24GHZ_CHANNELS] = {
- /* nop out the list */
- [0] = 0
- };
-
- u8 channel;
- while (channel_index < IPW_SCAN_CHANNELS) {
- channel =
- priv->speed_scan[priv->speed_scan_pos];
- if (channel == 0) {
- priv->speed_scan_pos = 0;
- channel = priv->speed_scan[0];
- }
- if ((priv->status & STATUS_ASSOCIATED) &&
- channel == priv->channel) {
- priv->speed_scan_pos++;
- continue;
- }
-
- /* If this channel has already been
- * added in scan, break from loop
- * and this will be the first channel
- * in the next scan.
- */
- if (channels[channel - 1] != 0)
- break;
-
- channels[channel - 1] = 1;
- priv->speed_scan_pos++;
- channel_index++;
- scan->channels_list[channel_index] = channel;
- index =
- ieee80211_channel_to_index(priv->ieee, channel);
- ipw_set_scan_type(scan, channel_index,
- geo->bg[index].
- flags &
- IEEE80211_CH_PASSIVE_ONLY ?
- IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
- : scan_type);
- }
- } else {
- for (i = 0; i < geo->bg_channels; i++) {
- if ((priv->status & STATUS_ASSOCIATED) &&
- geo->bg[i].channel == priv->channel)
- continue;
- channel_index++;
- scan->channels_list[channel_index] =
- geo->bg[i].channel;
- ipw_set_scan_type(scan, channel_index,
- geo->bg[i].
- flags &
- IEEE80211_CH_PASSIVE_ONLY ?
- IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
- : scan_type);
- }
- }
-
- if (start != channel_index) {
- scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
- (channel_index - start);
- }
- }
-}
-
-static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
-{
- struct ipw_scan_request_ext scan;
- int err = 0, scan_type;
-
- if (!(priv->status & STATUS_INIT) ||
- (priv->status & STATUS_EXIT_PENDING))
- return 0;
-
- mutex_lock(&priv->mutex);
-
- if (direct && (priv->direct_scan_ssid_len == 0)) {
- IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
- priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
- goto done;
- }
-
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n");
- priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
- STATUS_SCAN_PENDING;
- goto done;
- }
-
- if (!(priv->status & STATUS_SCAN_FORCED) &&
- priv->status & STATUS_SCAN_ABORTING) {
- IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
- priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
- STATUS_SCAN_PENDING;
- goto done;
- }
-
- if (priv->status & STATUS_RF_KILL_MASK) {
- IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
- priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
- STATUS_SCAN_PENDING;
- goto done;
- }
-
- memset(&scan, 0, sizeof(scan));
- scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
-
- if (type == IW_SCAN_TYPE_PASSIVE) {
- IPW_DEBUG_WX("use passive scanning\n");
- scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN;
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
- cpu_to_le16(120);
- ipw_add_scan_channels(priv, &scan, scan_type);
- goto send_request;
- }
-
- /* Use active scan by default. */
- if (priv->config & CFG_SPEED_SCAN)
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
- cpu_to_le16(30);
- else
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
- cpu_to_le16(20);
-
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
- cpu_to_le16(20);
-
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
- scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
-
-#ifdef CONFIG_IPW2200_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- u8 channel;
- u8 band = 0;
-
- switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
- case IEEE80211_52GHZ_BAND:
- band = (u8) (IPW_A_MODE << 6) | 1;
- channel = priv->channel;
- break;
-
- case IEEE80211_24GHZ_BAND:
- band = (u8) (IPW_B_MODE << 6) | 1;
- channel = priv->channel;
- break;
-
- default:
- band = (u8) (IPW_B_MODE << 6) | 1;
- channel = 9;
- break;
- }
-
- scan.channels_list[0] = band;
- scan.channels_list[1] = channel;
- ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
-
- /* NOTE: The card will sit on this channel for this time
- * period. Scan aborts are timing sensitive and frequently
- * result in firmware restarts. As such, it is best to
- * set a small dwell_time here and just keep re-issuing
- * scans. Otherwise fast channel hopping will not actually
- * hop channels.
- *
- * TODO: Move SPEED SCAN support to all modes and bands */
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
- cpu_to_le16(2000);
- } else {
-#endif /* CONFIG_IPW2200_MONITOR */
- /* Honor direct scans first, otherwise if we are roaming make
- * this a direct scan for the current network. Finally,
- * ensure that every other scan is a fast channel hop scan */
- if (direct) {
- err = ipw_send_ssid(priv, priv->direct_scan_ssid,
- priv->direct_scan_ssid_len);
- if (err) {
- IPW_DEBUG_HC("Attempt to send SSID command "
- "failed\n");
- goto done;
- }
-
- scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
- } else if ((priv->status & STATUS_ROAMING)
- || (!(priv->status & STATUS_ASSOCIATED)
- && (priv->config & CFG_STATIC_ESSID)
- && (le32_to_cpu(scan.full_scan_index) % 2))) {
- err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
- if (err) {
- IPW_DEBUG_HC("Attempt to send SSID command "
- "failed.\n");
- goto done;
- }
-
- scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
- } else
- scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
-
- ipw_add_scan_channels(priv, &scan, scan_type);
-#ifdef CONFIG_IPW2200_MONITOR
- }
-#endif
-
-send_request:
- err = ipw_send_scan_request_ext(priv, &scan);
- if (err) {
- IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
- goto done;
- }
-
- priv->status |= STATUS_SCANNING;
- if (direct) {
- priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
- priv->direct_scan_ssid_len = 0;
- } else
- priv->status &= ~STATUS_SCAN_PENDING;
-
- queue_delayed_work(priv->workqueue, &priv->scan_check,
- IPW_SCAN_CHECK_WATCHDOG);
-done:
- mutex_unlock(&priv->mutex);
- return err;
-}
-
-static void ipw_request_passive_scan(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, request_passive_scan.work);
- ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
-}
-
-static void ipw_request_scan(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, request_scan.work);
- ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
-}
-
-static void ipw_request_direct_scan(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, request_direct_scan.work);
- ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
-}
-
-static void ipw_bg_abort_scan(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, abort_scan);
- mutex_lock(&priv->mutex);
- ipw_abort_scan(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static int ipw_wpa_enable(struct ipw_priv *priv, int value)
-{
- /* This is called when wpa_supplicant loads and closes the driver
- * interface. */
- priv->ieee->wpa_enabled = value;
- return 0;
-}
-
-static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
-{
- struct ieee80211_device *ieee = priv->ieee;
- struct ieee80211_security sec = {
- .flags = SEC_AUTH_MODE,
- };
- int ret = 0;
-
- if (value & IW_AUTH_ALG_SHARED_KEY) {
- sec.auth_mode = WLAN_AUTH_SHARED_KEY;
- ieee->open_wep = 0;
- } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
- sec.auth_mode = WLAN_AUTH_OPEN;
- ieee->open_wep = 1;
- } else if (value & IW_AUTH_ALG_LEAP) {
- sec.auth_mode = WLAN_AUTH_LEAP;
- ieee->open_wep = 1;
- } else
- return -EINVAL;
-
- if (ieee->set_security)
- ieee->set_security(ieee->dev, &sec);
- else
- ret = -EOPNOTSUPP;
-
- return ret;
-}
-
-static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
- int wpa_ie_len)
-{
- /* make sure WPA is enabled */
- ipw_wpa_enable(priv, 1);
-}
-
-static int ipw_set_rsn_capa(struct ipw_priv *priv,
- char *capabilities, int length)
-{
- IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
-
- return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length,
- capabilities);
-}
-
-/*
- * WE-18 support
- */
-
-/* SIOCSIWGENIE */
-static int ipw_wx_set_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- u8 *buf;
- int err = 0;
-
- if (wrqu->data.length > MAX_WPA_IE_LEN ||
- (wrqu->data.length && extra == NULL))
- return -EINVAL;
-
- if (wrqu->data.length) {
- buf = kmalloc(wrqu->data.length, GFP_KERNEL);
- if (buf == NULL) {
- err = -ENOMEM;
- goto out;
- }
-
- memcpy(buf, extra, wrqu->data.length);
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = buf;
- ieee->wpa_ie_len = wrqu->data.length;
- } else {
- kfree(ieee->wpa_ie);
- ieee->wpa_ie = NULL;
- ieee->wpa_ie_len = 0;
- }
-
- ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
- out:
- return err;
-}
-
-/* SIOCGIWGENIE */
-static int ipw_wx_get_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- int err = 0;
-
- if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
- wrqu->data.length = 0;
- goto out;
- }
-
- if (wrqu->data.length < ieee->wpa_ie_len) {
- err = -E2BIG;
- goto out;
- }
-
- wrqu->data.length = ieee->wpa_ie_len;
- memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
-
- out:
- return err;
-}
-
-static int wext_cipher2level(int cipher)
-{
- switch (cipher) {
- case IW_AUTH_CIPHER_NONE:
- return SEC_LEVEL_0;
- case IW_AUTH_CIPHER_WEP40:
- case IW_AUTH_CIPHER_WEP104:
- return SEC_LEVEL_1;
- case IW_AUTH_CIPHER_TKIP:
- return SEC_LEVEL_2;
- case IW_AUTH_CIPHER_CCMP:
- return SEC_LEVEL_3;
- default:
- return -1;
- }
-}
-
-/* SIOCSIWAUTH */
-static int ipw_wx_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- struct iw_param *param = &wrqu->param;
- struct ieee80211_crypt_data *crypt;
- unsigned long flags;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- break;
- case IW_AUTH_CIPHER_PAIRWISE:
- ipw_set_hw_decrypt_unicast(priv,
- wext_cipher2level(param->value));
- break;
- case IW_AUTH_CIPHER_GROUP:
- ipw_set_hw_decrypt_multicast(priv,
- wext_cipher2level(param->value));
- break;
- case IW_AUTH_KEY_MGMT:
- /*
- * ipw2200 does not use these parameters
- */
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
- break;
-
- flags = crypt->ops->get_flags(crypt->priv);
-
- if (param->value)
- flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
- else
- flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
-
- crypt->ops->set_flags(flags, crypt->priv);
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:{
- /* HACK:
- *
- * wpa_supplicant calls set_wpa_enabled when the driver
- * is loaded and unloaded, regardless of if WPA is being
- * used. No other calls are made which can be used to
- * determine if encryption will be used or not prior to
- * association being expected. If encryption is not being
- * used, drop_unencrypted is set to false, else true -- we
- * can use this to determine if the CAP_PRIVACY_ON bit should
- * be set.
- */
- struct ieee80211_security sec = {
- .flags = SEC_ENABLED,
- .enabled = param->value,
- };
- priv->ieee->drop_unencrypted = param->value;
- /* We only change SEC_LEVEL for open mode. Others
- * are set by ipw_wpa_set_encryption.
- */
- if (!param->value) {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_0;
- } else {
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- }
- if (priv->ieee->set_security)
- priv->ieee->set_security(priv->ieee->dev, &sec);
- break;
- }
-
- case IW_AUTH_80211_AUTH_ALG:
- ret = ipw_wpa_set_auth_algs(priv, param->value);
- break;
-
- case IW_AUTH_WPA_ENABLED:
- ret = ipw_wpa_enable(priv, param->value);
- ipw_disassociate(priv);
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- ieee->ieee802_1x = param->value;
- break;
-
- case IW_AUTH_PRIVACY_INVOKED:
- ieee->privacy_invoked = param->value;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return ret;
-}
-
-/* SIOCGIWAUTH */
-static int ipw_wx_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_device *ieee = priv->ieee;
- struct ieee80211_crypt_data *crypt;
- struct iw_param *param = &wrqu->param;
- int ret = 0;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_KEY_MGMT:
- /*
- * wpa_supplicant will control these internally
- */
- ret = -EOPNOTSUPP;
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- crypt = priv->ieee->crypt[priv->ieee->tx_keyidx];
- if (!crypt || !crypt->ops->get_flags)
- break;
-
- param->value = (crypt->ops->get_flags(crypt->priv) &
- IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
-
- break;
-
- case IW_AUTH_DROP_UNENCRYPTED:
- param->value = ieee->drop_unencrypted;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- param->value = ieee->sec.auth_mode;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = ieee->wpa_enabled;
- break;
-
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- param->value = ieee->ieee802_1x;
- break;
-
- case IW_AUTH_ROAMING_CONTROL:
- case IW_AUTH_PRIVACY_INVOKED:
- param->value = ieee->privacy_invoked;
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-/* SIOCSIWENCODEEXT */
-static int ipw_wx_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
-
- if (hwcrypto) {
- if (ext->alg == IW_ENCODE_ALG_TKIP) {
- /* IPW HW can't build TKIP MIC,
- host decryption still needed */
- if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
- priv->ieee->host_mc_decrypt = 1;
- else {
- priv->ieee->host_encrypt = 0;
- priv->ieee->host_encrypt_msdu = 1;
- priv->ieee->host_decrypt = 1;
- }
- } else {
- priv->ieee->host_encrypt = 0;
- priv->ieee->host_encrypt_msdu = 0;
- priv->ieee->host_decrypt = 0;
- priv->ieee->host_mc_decrypt = 0;
- }
- }
-
- return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCGIWENCODEEXT */
-static int ipw_wx_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
-}
-
-/* SIOCSIWMLME */
-static int ipw_wx_set_mlme(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct iw_mlme *mlme = (struct iw_mlme *)extra;
- __le16 reason;
-
- reason = cpu_to_le16(mlme->reason_code);
-
- switch (mlme->cmd) {
- case IW_MLME_DEAUTH:
- /* silently ignore */
- break;
-
- case IW_MLME_DISASSOC:
- ipw_disassociate(priv);
- break;
-
- default:
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-#ifdef CONFIG_IPW2200_QOS
-
-/* QoS */
-/*
-* get the modulation type of the current network or
-* the card current mode
-*/
-static u8 ipw_qos_current_mode(struct ipw_priv * priv)
-{
- u8 mode = 0;
-
- if (priv->status & STATUS_ASSOCIATED) {
- unsigned long flags;
-
- spin_lock_irqsave(&priv->ieee->lock, flags);
- mode = priv->assoc_network->mode;
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
- } else {
- mode = priv->ieee->mode;
- }
- IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
- return mode;
-}
-
-/*
-* Handle management frame beacon and probe response
-*/
-static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
- int active_network,
- struct ieee80211_network *network)
-{
- u32 size = sizeof(struct ieee80211_qos_parameters);
-
- if (network->capability & WLAN_CAPABILITY_IBSS)
- network->qos_data.active = network->qos_data.supported;
-
- if (network->flags & NETWORK_HAS_QOS_MASK) {
- if (active_network &&
- (network->flags & NETWORK_HAS_QOS_PARAMETERS))
- network->qos_data.active = network->qos_data.supported;
-
- if ((network->qos_data.active == 1) && (active_network == 1) &&
- (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
- (network->qos_data.old_param_count !=
- network->qos_data.param_count)) {
- network->qos_data.old_param_count =
- network->qos_data.param_count;
- schedule_work(&priv->qos_activate);
- IPW_DEBUG_QOS("QoS parameters change call "
- "qos_activate\n");
- }
- } else {
- if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
- memcpy(&network->qos_data.parameters,
- &def_parameters_CCK, size);
- else
- memcpy(&network->qos_data.parameters,
- &def_parameters_OFDM, size);
-
- if ((network->qos_data.active == 1) && (active_network == 1)) {
- IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
- schedule_work(&priv->qos_activate);
- }
-
- network->qos_data.active = 0;
- network->qos_data.supported = 0;
- }
- if ((priv->status & STATUS_ASSOCIATED) &&
- (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
- if (memcmp(network->bssid, priv->bssid, ETH_ALEN))
- if ((network->capability & WLAN_CAPABILITY_IBSS) &&
- !(network->flags & NETWORK_EMPTY_ESSID))
- if ((network->ssid_len ==
- priv->assoc_network->ssid_len) &&
- !memcmp(network->ssid,
- priv->assoc_network->ssid,
- network->ssid_len)) {
- queue_work(priv->workqueue,
- &priv->merge_networks);
- }
- }
-
- return 0;
-}
-
-/*
-* This function set up the firmware to support QoS. It sends
-* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
-*/
-static int ipw_qos_activate(struct ipw_priv *priv,
- struct ieee80211_qos_data *qos_network_data)
-{
- int err;
- struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
- struct ieee80211_qos_parameters *active_one = NULL;
- u32 size = sizeof(struct ieee80211_qos_parameters);
- u32 burst_duration;
- int i;
- u8 type;
-
- type = ipw_qos_current_mode(priv);
-
- active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
- memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
- active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
- memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
-
- if (qos_network_data == NULL) {
- if (type == IEEE_B) {
- IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
- active_one = &def_parameters_CCK;
- } else
- active_one = &def_parameters_OFDM;
-
- memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
- burst_duration = ipw_qos_get_burst_duration(priv);
- for (i = 0; i < QOS_QUEUE_NUM; i++)
- qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
- cpu_to_le16(burst_duration);
- } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- if (type == IEEE_B) {
- IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
- type);
- if (priv->qos_data.qos_enable == 0)
- active_one = &def_parameters_CCK;
- else
- active_one = priv->qos_data.def_qos_parm_CCK;
- } else {
- if (priv->qos_data.qos_enable == 0)
- active_one = &def_parameters_OFDM;
- else
- active_one = priv->qos_data.def_qos_parm_OFDM;
- }
- memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
- } else {
- unsigned long flags;
- int active;
-
- spin_lock_irqsave(&priv->ieee->lock, flags);
- active_one = &(qos_network_data->parameters);
- qos_network_data->old_param_count =
- qos_network_data->param_count;
- memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
- active = qos_network_data->supported;
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
- if (active == 0) {
- burst_duration = ipw_qos_get_burst_duration(priv);
- for (i = 0; i < QOS_QUEUE_NUM; i++)
- qos_parameters[QOS_PARAM_SET_ACTIVE].
- tx_op_limit[i] = cpu_to_le16(burst_duration);
- }
- }
-
- IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
- err = ipw_send_qos_params_command(priv,
- (struct ieee80211_qos_parameters *)
- &(qos_parameters[0]));
- if (err)
- IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
-
- return err;
-}
-
-/*
-* send IPW_CMD_WME_INFO to the firmware
-*/
-static int ipw_qos_set_info_element(struct ipw_priv *priv)
-{
- int ret = 0;
- struct ieee80211_qos_information_element qos_info;
-
- if (priv == NULL)
- return -1;
-
- qos_info.elementID = QOS_ELEMENT_ID;
- qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
-
- qos_info.version = QOS_VERSION_1;
- qos_info.ac_info = 0;
-
- memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
- qos_info.qui_type = QOS_OUI_TYPE;
- qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
-
- ret = ipw_send_qos_info_command(priv, &qos_info);
- if (ret != 0) {
- IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
- }
- return ret;
-}
-
-/*
-* Set the QoS parameter with the association request structure
-*/
-static int ipw_qos_association(struct ipw_priv *priv,
- struct ieee80211_network *network)
-{
- int err = 0;
- struct ieee80211_qos_data *qos_data = NULL;
- struct ieee80211_qos_data ibss_data = {
- .supported = 1,
- .active = 1,
- };
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS));
-
- qos_data = &ibss_data;
- break;
-
- case IW_MODE_INFRA:
- qos_data = &network->qos_data;
- break;
-
- default:
- BUG();
- break;
- }
-
- err = ipw_qos_activate(priv, qos_data);
- if (err) {
- priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
- return err;
- }
-
- if (priv->qos_data.qos_enable && qos_data->supported) {
- IPW_DEBUG_QOS("QoS will be enabled for this association\n");
- priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
- return ipw_qos_set_info_element(priv);
- }
-
- return 0;
-}
-
-/*
-* handling the beaconing responses. if we get different QoS setting
-* off the network from the associated setting, adjust the QoS
-* setting
-*/
-static int ipw_qos_association_resp(struct ipw_priv *priv,
- struct ieee80211_network *network)
-{
- int ret = 0;
- unsigned long flags;
- u32 size = sizeof(struct ieee80211_qos_parameters);
- int set_qos_param = 0;
-
- if ((priv == NULL) || (network == NULL) ||
- (priv->assoc_network == NULL))
- return ret;
-
- if (!(priv->status & STATUS_ASSOCIATED))
- return ret;
-
- if ((priv->ieee->iw_mode != IW_MODE_INFRA))
- return ret;
-
- spin_lock_irqsave(&priv->ieee->lock, flags);
- if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
- memcpy(&priv->assoc_network->qos_data, &network->qos_data,
- sizeof(struct ieee80211_qos_data));
- priv->assoc_network->qos_data.active = 1;
- if ((network->qos_data.old_param_count !=
- network->qos_data.param_count)) {
- set_qos_param = 1;
- network->qos_data.old_param_count =
- network->qos_data.param_count;
- }
-
- } else {
- if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
- memcpy(&priv->assoc_network->qos_data.parameters,
- &def_parameters_CCK, size);
- else
- memcpy(&priv->assoc_network->qos_data.parameters,
- &def_parameters_OFDM, size);
- priv->assoc_network->qos_data.active = 0;
- priv->assoc_network->qos_data.supported = 0;
- set_qos_param = 1;
- }
-
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
- if (set_qos_param == 1)
- schedule_work(&priv->qos_activate);
-
- return ret;
-}
-
-static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
-{
- u32 ret = 0;
-
- if ((priv == NULL))
- return 0;
-
- if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION))
- ret = priv->qos_data.burst_duration_CCK;
- else
- ret = priv->qos_data.burst_duration_OFDM;
-
- return ret;
-}
-
-/*
-* Initialize the setting of QoS global
-*/
-static void ipw_qos_init(struct ipw_priv *priv, int enable,
- int burst_enable, u32 burst_duration_CCK,
- u32 burst_duration_OFDM)
-{
- priv->qos_data.qos_enable = enable;
-
- if (priv->qos_data.qos_enable) {
- priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
- priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
- IPW_DEBUG_QOS("QoS is enabled\n");
- } else {
- priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
- priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
- IPW_DEBUG_QOS("QoS is not enabled\n");
- }
-
- priv->qos_data.burst_enable = burst_enable;
-
- if (burst_enable) {
- priv->qos_data.burst_duration_CCK = burst_duration_CCK;
- priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
- } else {
- priv->qos_data.burst_duration_CCK = 0;
- priv->qos_data.burst_duration_OFDM = 0;
- }
-}
-
-/*
-* map the packet priority to the right TX Queue
-*/
-static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
-{
- if (priority > 7 || !priv->qos_data.qos_enable)
- priority = 0;
-
- return from_priority_to_tx_queue[priority] - 1;
-}
-
-static int ipw_is_qos_active(struct net_device *dev,
- struct sk_buff *skb)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct ieee80211_qos_data *qos_data = NULL;
- int active, supported;
- u8 *daddr = skb->data + ETH_ALEN;
- int unicast = !is_multicast_ether_addr(daddr);
-
- if (!(priv->status & STATUS_ASSOCIATED))
- return 0;
-
- qos_data = &priv->assoc_network->qos_data;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- if (unicast == 0)
- qos_data->active = 0;
- else
- qos_data->active = qos_data->supported;
- }
- active = qos_data->active;
- supported = qos_data->supported;
- IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
- "unicast %d\n",
- priv->qos_data.qos_enable, active, supported, unicast);
- if (active && priv->qos_data.qos_enable)
- return 1;
-
- return 0;
-
-}
-/*
-* add QoS parameter to the TX command
-*/
-static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
- u16 priority,
- struct tfd_data *tfd)
-{
- int tx_queue_id = 0;
-
-
- tx_queue_id = from_priority_to_tx_queue[priority] - 1;
- tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
-
- if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) {
- tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
- tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK);
- }
- return 0;
-}
-
-/*
-* background support to run QoS activate functionality
-*/
-static void ipw_bg_qos_activate(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, qos_activate);
-
- if (priv == NULL)
- return;
-
- mutex_lock(&priv->mutex);
-
- if (priv->status & STATUS_ASSOCIATED)
- ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
-
- mutex_unlock(&priv->mutex);
-}
-
-static int ipw_handle_probe_response(struct net_device *dev,
- struct ieee80211_probe_response *resp,
- struct ieee80211_network *network)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int active_network = ((priv->status & STATUS_ASSOCIATED) &&
- (network == priv->assoc_network));
-
- ipw_qos_handle_probe_response(priv, active_network, network);
-
- return 0;
-}
-
-static int ipw_handle_beacon(struct net_device *dev,
- struct ieee80211_beacon *resp,
- struct ieee80211_network *network)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int active_network = ((priv->status & STATUS_ASSOCIATED) &&
- (network == priv->assoc_network));
-
- ipw_qos_handle_probe_response(priv, active_network, network);
-
- return 0;
-}
-
-static int ipw_handle_assoc_response(struct net_device *dev,
- struct ieee80211_assoc_response *resp,
- struct ieee80211_network *network)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- ipw_qos_association_resp(priv, network);
- return 0;
-}
-
-static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
- *qos_param)
-{
- return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS,
- sizeof(*qos_param) * 3, qos_param);
-}
-
-static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
- *qos_param)
-{
- return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param),
- qos_param);
-}
-
-#endif /* CONFIG_IPW2200_QOS */
-
-static int ipw_associate_network(struct ipw_priv *priv,
- struct ieee80211_network *network,
- struct ipw_supported_rates *rates, int roaming)
-{
- int err;
- DECLARE_MAC_BUF(mac);
-
- if (priv->config & CFG_FIXED_RATE)
- ipw_set_fixed_rate(priv, network->mode);
-
- if (!(priv->config & CFG_STATIC_ESSID)) {
- priv->essid_len = min(network->ssid_len,
- (u8) IW_ESSID_MAX_SIZE);
- memcpy(priv->essid, network->ssid, priv->essid_len);
- }
-
- network->last_associate = jiffies;
-
- memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
- priv->assoc_request.channel = network->channel;
- priv->assoc_request.auth_key = 0;
-
- if ((priv->capability & CAP_PRIVACY_ON) &&
- (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) {
- priv->assoc_request.auth_type = AUTH_SHARED_KEY;
- priv->assoc_request.auth_key = priv->ieee->sec.active_key;
-
- if (priv->ieee->sec.level == SEC_LEVEL_1)
- ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
-
- } else if ((priv->capability & CAP_PRIVACY_ON) &&
- (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP))
- priv->assoc_request.auth_type = AUTH_LEAP;
- else
- priv->assoc_request.auth_type = AUTH_OPEN;
-
- if (priv->ieee->wpa_ie_len) {
- priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */
- ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
- priv->ieee->wpa_ie_len);
- }
-
- /*
- * It is valid for our ieee device to support multiple modes, but
- * when it comes to associating to a given network we have to choose
- * just one mode.
- */
- if (network->mode & priv->ieee->mode & IEEE_A)
- priv->assoc_request.ieee_mode = IPW_A_MODE;
- else if (network->mode & priv->ieee->mode & IEEE_G)
- priv->assoc_request.ieee_mode = IPW_G_MODE;
- else if (network->mode & priv->ieee->mode & IEEE_B)
- priv->assoc_request.ieee_mode = IPW_B_MODE;
-
- priv->assoc_request.capability = cpu_to_le16(network->capability);
- if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
- && !(priv->config & CFG_PREAMBLE_LONG)) {
- priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
- } else {
- priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
-
- /* Clear the short preamble if we won't be supporting it */
- priv->assoc_request.capability &=
- ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
- }
-
- /* Clear capability bits that aren't used in Ad Hoc */
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- priv->assoc_request.capability &=
- ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
-
- IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
- "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
- roaming ? "Rea" : "A",
- escape_essid(priv->essid, priv->essid_len),
- network->channel,
- ipw_modes[priv->assoc_request.ieee_mode],
- rates->num_rates,
- (priv->assoc_request.preamble_length ==
- DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
- network->capability &
- WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
- priv->capability & CAP_PRIVACY_ON ? "on " : "off",
- priv->capability & CAP_PRIVACY_ON ?
- (priv->capability & CAP_SHARED_KEY ? "(shared)" :
- "(open)") : "",
- priv->capability & CAP_PRIVACY_ON ? " key=" : "",
- priv->capability & CAP_PRIVACY_ON ?
- '1' + priv->ieee->sec.active_key : '.',
- priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
-
- priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval);
- if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
- (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
- priv->assoc_request.assoc_type = HC_IBSS_START;
- priv->assoc_request.assoc_tsf_msw = 0;
- priv->assoc_request.assoc_tsf_lsw = 0;
- } else {
- if (unlikely(roaming))
- priv->assoc_request.assoc_type = HC_REASSOCIATE;
- else
- priv->assoc_request.assoc_type = HC_ASSOCIATE;
- priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]);
- priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]);
- }
-
- memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
- priv->assoc_request.atim_window = cpu_to_le16(network->atim_window);
- } else {
- memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
- priv->assoc_request.atim_window = 0;
- }
-
- priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval);
-
- err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
- if (err) {
- IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
- return err;
- }
-
- rates->ieee_mode = priv->assoc_request.ieee_mode;
- rates->purpose = IPW_RATE_CONNECT;
- ipw_send_supported_rates(priv, rates);
-
- if (priv->assoc_request.ieee_mode == IPW_G_MODE)
- priv->sys_config.dot11g_auto_detection = 1;
- else
- priv->sys_config.dot11g_auto_detection = 0;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- priv->sys_config.answer_broadcast_ssid_probe = 1;
- else
- priv->sys_config.answer_broadcast_ssid_probe = 0;
-
- err = ipw_send_system_config(priv);
- if (err) {
- IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
- return err;
- }
-
- IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
- err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
- if (err) {
- IPW_DEBUG_HC("Attempt to send associate command failed.\n");
- return err;
- }
-
- /*
- * If preemption is enabled, it is possible for the association
- * to complete before we return from ipw_send_associate. Therefore
- * we have to be sure and update our priviate data first.
- */
- priv->channel = network->channel;
- memcpy(priv->bssid, network->bssid, ETH_ALEN);
- priv->status |= STATUS_ASSOCIATING;
- priv->status &= ~STATUS_SECURITY_UPDATED;
-
- priv->assoc_network = network;
-
-#ifdef CONFIG_IPW2200_QOS
- ipw_qos_association(priv, network);
-#endif
-
- err = ipw_send_associate(priv, &priv->assoc_request);
- if (err) {
- IPW_DEBUG_HC("Attempt to send associate command failed.\n");
- return err;
- }
-
- IPW_DEBUG(IPW_DL_STATE, "associating: '%s' %s \n",
- escape_essid(priv->essid, priv->essid_len),
- print_mac(mac, priv->bssid));
-
- return 0;
-}
-
-static void ipw_roam(void *data)
-{
- struct ipw_priv *priv = data;
- struct ieee80211_network *network = NULL;
- struct ipw_network_match match = {
- .network = priv->assoc_network
- };
-
- /* The roaming process is as follows:
- *
- * 1. Missed beacon threshold triggers the roaming process by
- * setting the status ROAM bit and requesting a scan.
- * 2. When the scan completes, it schedules the ROAM work
- * 3. The ROAM work looks at all of the known networks for one that
- * is a better network than the currently associated. If none
- * found, the ROAM process is over (ROAM bit cleared)
- * 4. If a better network is found, a disassociation request is
- * sent.
- * 5. When the disassociation completes, the roam work is again
- * scheduled. The second time through, the driver is no longer
- * associated, and the newly selected network is sent an
- * association request.
- * 6. At this point ,the roaming process is complete and the ROAM
- * status bit is cleared.
- */
-
- /* If we are no longer associated, and the roaming bit is no longer
- * set, then we are not actively roaming, so just return */
- if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
- return;
-
- if (priv->status & STATUS_ASSOCIATED) {
- /* First pass through ROAM process -- look for a better
- * network */
- unsigned long flags;
- u8 rssi = priv->assoc_network->stats.rssi;
- priv->assoc_network->stats.rssi = -128;
- spin_lock_irqsave(&priv->ieee->lock, flags);
- list_for_each_entry(network, &priv->ieee->network_list, list) {
- if (network != priv->assoc_network)
- ipw_best_network(priv, &match, network, 1);
- }
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
- priv->assoc_network->stats.rssi = rssi;
-
- if (match.network == priv->assoc_network) {
- IPW_DEBUG_ASSOC("No better APs in this network to "
- "roam to.\n");
- priv->status &= ~STATUS_ROAMING;
- ipw_debug_config(priv);
- return;
- }
-
- ipw_send_disassociate(priv, 1);
- priv->assoc_network = match.network;
-
- return;
- }
-
- /* Second pass through ROAM process -- request association */
- ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
- ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
- priv->status &= ~STATUS_ROAMING;
-}
-
-static void ipw_bg_roam(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, roam);
- mutex_lock(&priv->mutex);
- ipw_roam(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static int ipw_associate(void *data)
-{
- struct ipw_priv *priv = data;
-
- struct ieee80211_network *network = NULL;
- struct ipw_network_match match = {
- .network = NULL
- };
- struct ipw_supported_rates *rates;
- struct list_head *element;
- unsigned long flags;
-
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
- return 0;
- }
-
- if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
- IPW_DEBUG_ASSOC("Not attempting association (already in "
- "progress)\n");
- return 0;
- }
-
- if (priv->status & STATUS_DISASSOCIATING) {
- IPW_DEBUG_ASSOC("Not attempting association (in "
- "disassociating)\n ");
- queue_work(priv->workqueue, &priv->associate);
- return 0;
- }
-
- if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
- IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
- "initialized)\n");
- return 0;
- }
-
- if (!(priv->config & CFG_ASSOCIATE) &&
- !(priv->config & (CFG_STATIC_ESSID |
- CFG_STATIC_CHANNEL | CFG_STATIC_BSSID))) {
- IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
- return 0;
- }
-
- /* Protect our use of the network_list */
- spin_lock_irqsave(&priv->ieee->lock, flags);
- list_for_each_entry(network, &priv->ieee->network_list, list)
- ipw_best_network(priv, &match, network, 0);
-
- network = match.network;
- rates = &match.rates;
-
- if (network == NULL &&
- priv->ieee->iw_mode == IW_MODE_ADHOC &&
- priv->config & CFG_ADHOC_CREATE &&
- priv->config & CFG_STATIC_ESSID &&
- priv->config & CFG_STATIC_CHANNEL) {
- /* Use oldest network if the free list is empty */
- if (list_empty(&priv->ieee->network_free_list)) {
- struct ieee80211_network *oldest = NULL;
- struct ieee80211_network *target;
- DECLARE_MAC_BUF(mac);
-
- list_for_each_entry(target, &priv->ieee->network_list, list) {
- if ((oldest == NULL) ||
- (target->last_scanned < oldest->last_scanned))
- oldest = target;
- }
-
- /* If there are no more slots, expire the oldest */
- list_del(&oldest->list);
- target = oldest;
- IPW_DEBUG_ASSOC("Expired '%s' (%s) from "
- "network list.\n",
- escape_essid(target->ssid,
- target->ssid_len),
- print_mac(mac, target->bssid));
- list_add_tail(&target->list,
- &priv->ieee->network_free_list);
- }
-
- element = priv->ieee->network_free_list.next;
- network = list_entry(element, struct ieee80211_network, list);
- ipw_adhoc_create(priv, network);
- rates = &priv->rates;
- list_del(element);
- list_add_tail(&network->list, &priv->ieee->network_list);
- }
- spin_unlock_irqrestore(&priv->ieee->lock, flags);
-
- /* If we reached the end of the list, then we don't have any valid
- * matching APs */
- if (!network) {
- ipw_debug_config(priv);
-
- if (!(priv->status & STATUS_SCANNING)) {
- if (!(priv->config & CFG_SPEED_SCAN))
- queue_delayed_work(priv->workqueue,
- &priv->request_scan,
- SCAN_INTERVAL);
- else
- queue_delayed_work(priv->workqueue,
- &priv->request_scan, 0);
- }
-
- return 0;
- }
-
- ipw_associate_network(priv, network, rates, 0);
-
- return 1;
-}
-
-static void ipw_bg_associate(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, associate);
- mutex_lock(&priv->mutex);
- ipw_associate(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
- struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr;
- u16 fc;
-
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = le16_to_cpu(hdr->frame_ctl);
- if (!(fc & IEEE80211_FCTL_PROTECTED))
- return;
-
- fc &= ~IEEE80211_FCTL_PROTECTED;
- hdr->frame_ctl = cpu_to_le16(fc);
- switch (priv->ieee->sec.level) {
- case SEC_LEVEL_3:
- /* Remove CCMP HDR */
- memmove(skb->data + IEEE80211_3ADDR_LEN,
- skb->data + IEEE80211_3ADDR_LEN + 8,
- skb->len - IEEE80211_3ADDR_LEN - 8);
- skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
- break;
- case SEC_LEVEL_2:
- break;
- case SEC_LEVEL_1:
- /* Remove IV */
- memmove(skb->data + IEEE80211_3ADDR_LEN,
- skb->data + IEEE80211_3ADDR_LEN + 4,
- skb->len - IEEE80211_3ADDR_LEN - 4);
- skb_trim(skb, skb->len - 8); /* IV + ICV */
- break;
- case SEC_LEVEL_0:
- break;
- default:
- printk(KERN_ERR "Unknow security level %d\n",
- priv->ieee->sec.level);
- break;
- }
-}
-
-static void ipw_handle_data_packet(struct ipw_priv *priv,
- struct ipw_rx_mem_buffer *rxb,
- struct ieee80211_rx_stats *stats)
-{
- struct ieee80211_hdr_4addr *hdr;
- struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
-
- /* We received data from the HW, so stop the watchdog */
- priv->net_dev->trans_start = jiffies;
-
- /* We only process data packets if the
- * interface is open */
- if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
- skb_tailroom(rxb->skb))) {
- priv->ieee->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
- return;
- } else if (unlikely(!netif_running(priv->net_dev))) {
- priv->ieee->stats.rx_dropped++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- /* Advance skb->data to the start of the actual payload */
- skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
-
- /* Set the size of the skb to the size of the frame */
- skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
-
- IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
-
- /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
- hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
- if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
- (is_multicast_ether_addr(hdr->addr1) ?
- !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
- ipw_rebuild_decrypted_skb(priv, rxb->skb);
-
- if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
- priv->ieee->stats.rx_errors++;
- else { /* ieee80211_rx succeeded, so it now owns the SKB */
- rxb->skb = NULL;
- __ipw_led_activity_on(priv);
- }
-}
-
-#ifdef CONFIG_IPW2200_RADIOTAP
-static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
- struct ipw_rx_mem_buffer *rxb,
- struct ieee80211_rx_stats *stats)
-{
- struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
- struct ipw_rx_frame *frame = &pkt->u.frame;
-
- /* initial pull of some data */
- u16 received_channel = frame->received_channel;
- u8 antennaAndPhy = frame->antennaAndPhy;
- s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
- u16 pktrate = frame->rate;
-
- /* Magic struct that slots into the radiotap header -- no reason
- * to build this manually element by element, we can write it much
- * more efficiently than we can parse it. ORDER MATTERS HERE */
- struct ipw_rt_hdr *ipw_rt;
-
- short len = le16_to_cpu(pkt->u.frame.length);
-
- /* We received data from the HW, so stop the watchdog */
- priv->net_dev->trans_start = jiffies;
-
- /* We only process data packets if the
- * interface is open */
- if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
- skb_tailroom(rxb->skb))) {
- priv->ieee->stats.rx_errors++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
- return;
- } else if (unlikely(!netif_running(priv->net_dev))) {
- priv->ieee->stats.rx_dropped++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
- * that now */
- if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
- /* FIXME: Should alloc bigger skb instead */
- priv->ieee->stats.rx_dropped++;
- priv->wstats.discard.misc++;
- IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
- return;
- }
-
- /* copy the frame itself */
- memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
- rxb->skb->data + IPW_RX_FRAME_SIZE, len);
-
- /* Zero the radiotap static buffer ... We only need to zero the bytes NOT
- * part of our real header, saves a little time.
- *
- * No longer necessary since we fill in all our data. Purge before merging
- * patch officially.
- * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
- * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
- */
-
- ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
-
- ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
- ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
- ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */
-
- /* Big bitfield of all the fields we provide in radiotap */
- ipw_rt->rt_hdr.it_present = cpu_to_le32(
- (1 << IEEE80211_RADIOTAP_TSFT) |
- (1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
- (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
- (1 << IEEE80211_RADIOTAP_ANTENNA));
-
- /* Zero the flags, we'll add to them as we go */
- ipw_rt->rt_flags = 0;
- ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
- frame->parent_tsf[2] << 16 |
- frame->parent_tsf[1] << 8 |
- frame->parent_tsf[0]);
-
- /* Convert signal to DBM */
- ipw_rt->rt_dbmsignal = antsignal;
- ipw_rt->rt_dbmnoise = frame->noise;
-
- /* Convert the channel data and set the flags */
- ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
- if (received_channel > 14) { /* 802.11a */
- ipw_rt->rt_chbitmask =
- cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
- } else if (antennaAndPhy & 32) { /* 802.11b */
- ipw_rt->rt_chbitmask =
- cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
- } else { /* 802.11g */
- ipw_rt->rt_chbitmask =
- cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
- }
-
- /* set the rate in multiples of 500k/s */
- switch (pktrate) {
- case IPW_TX_RATE_1MB:
- ipw_rt->rt_rate = 2;
- break;
- case IPW_TX_RATE_2MB:
- ipw_rt->rt_rate = 4;
- break;
- case IPW_TX_RATE_5MB:
- ipw_rt->rt_rate = 10;
- break;
- case IPW_TX_RATE_6MB:
- ipw_rt->rt_rate = 12;
- break;
- case IPW_TX_RATE_9MB:
- ipw_rt->rt_rate = 18;
- break;
- case IPW_TX_RATE_11MB:
- ipw_rt->rt_rate = 22;
- break;
- case IPW_TX_RATE_12MB:
- ipw_rt->rt_rate = 24;
- break;
- case IPW_TX_RATE_18MB:
- ipw_rt->rt_rate = 36;
- break;
- case IPW_TX_RATE_24MB:
- ipw_rt->rt_rate = 48;
- break;
- case IPW_TX_RATE_36MB:
- ipw_rt->rt_rate = 72;
- break;
- case IPW_TX_RATE_48MB:
- ipw_rt->rt_rate = 96;
- break;
- case IPW_TX_RATE_54MB:
- ipw_rt->rt_rate = 108;
- break;
- default:
- ipw_rt->rt_rate = 0;
- break;
- }
-
- /* antenna number */
- ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
-
- /* set the preamble flag if we have it */
- if ((antennaAndPhy & 64))
- ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
-
- /* Set the size of the skb to the size of the frame */
- skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
-
- IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
-
- if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
- priv->ieee->stats.rx_errors++;
- else { /* ieee80211_rx succeeded, so it now owns the SKB */
- rxb->skb = NULL;
- /* no LED during capture */
- }
-}
-#endif
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-#define ieee80211_is_probe_response(fc) \
- ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
- (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
-
-#define ieee80211_is_management(fc) \
- ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
-
-#define ieee80211_is_control(fc) \
- ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
-
-#define ieee80211_is_data(fc) \
- ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
-
-#define ieee80211_is_assoc_request(fc) \
- ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
-
-#define ieee80211_is_reassoc_request(fc) \
- ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
-
-static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
- struct ipw_rx_mem_buffer *rxb,
- struct ieee80211_rx_stats *stats)
-{
- struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
- struct ipw_rx_frame *frame = &pkt->u.frame;
- struct ipw_rt_hdr *ipw_rt;
-
- /* First cache any information we need before we overwrite
- * the information provided in the skb from the hardware */
- struct ieee80211_hdr *hdr;
- u16 channel = frame->received_channel;
- u8 phy_flags = frame->antennaAndPhy;
- s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
- s8 noise = frame->noise;
- u8 rate = frame->rate;
- short len = le16_to_cpu(pkt->u.frame.length);
- struct sk_buff *skb;
- int hdr_only = 0;
- u16 filter = priv->prom_priv->filter;
-
- /* If the filter is set to not include Rx frames then return */
- if (filter & IPW_PROM_NO_RX)
- return;
-
- /* We received data from the HW, so stop the watchdog */
- priv->prom_net_dev->trans_start = jiffies;
-
- if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
- priv->prom_priv->ieee->stats.rx_errors++;
- IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
- return;
- }
-
- /* We only process data packets if the interface is open */
- if (unlikely(!netif_running(priv->prom_net_dev))) {
- priv->prom_priv->ieee->stats.rx_dropped++;
- IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
- return;
- }
-
- /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
- * that now */
- if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
- /* FIXME: Should alloc bigger skb instead */
- priv->prom_priv->ieee->stats.rx_dropped++;
- IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
- return;
- }
-
- hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
- if (ieee80211_is_management(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_MGMT)
- return;
- if (filter & IPW_PROM_MGMT_HEADER_ONLY)
- hdr_only = 1;
- } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_CTL)
- return;
- if (filter & IPW_PROM_CTL_HEADER_ONLY)
- hdr_only = 1;
- } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_DATA)
- return;
- if (filter & IPW_PROM_DATA_HEADER_ONLY)
- hdr_only = 1;
- }
-
- /* Copy the SKB since this is for the promiscuous side */
- skb = skb_copy(rxb->skb, GFP_ATOMIC);
- if (skb == NULL) {
- IPW_ERROR("skb_clone failed for promiscuous copy.\n");
- return;
- }
-
- /* copy the frame data to write after where the radiotap header goes */
- ipw_rt = (void *)skb->data;
-
- if (hdr_only)
- len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
-
- memcpy(ipw_rt->payload, hdr, len);
-
- /* Zero the radiotap static buffer ... We only need to zero the bytes
- * NOT part of our real header, saves a little time.
- *
- * No longer necessary since we fill in all our data. Purge before
- * merging patch officially.
- * memset(rxb->skb->data + sizeof(struct ipw_rt_hdr), 0,
- * IEEE80211_RADIOTAP_HDRLEN - sizeof(struct ipw_rt_hdr));
- */
-
- ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
- ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
- ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */
-
- /* Set the size of the skb to the size of the frame */
- skb_put(skb, sizeof(*ipw_rt) + len);
-
- /* Big bitfield of all the fields we provide in radiotap */
- ipw_rt->rt_hdr.it_present = cpu_to_le32(
- (1 << IEEE80211_RADIOTAP_TSFT) |
- (1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
- (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
- (1 << IEEE80211_RADIOTAP_ANTENNA));
-
- /* Zero the flags, we'll add to them as we go */
- ipw_rt->rt_flags = 0;
- ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
- frame->parent_tsf[2] << 16 |
- frame->parent_tsf[1] << 8 |
- frame->parent_tsf[0]);
-
- /* Convert to DBM */
- ipw_rt->rt_dbmsignal = signal;
- ipw_rt->rt_dbmnoise = noise;
-
- /* Convert the channel data and set the flags */
- ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
- if (channel > 14) { /* 802.11a */
- ipw_rt->rt_chbitmask =
- cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
- } else if (phy_flags & (1 << 5)) { /* 802.11b */
- ipw_rt->rt_chbitmask =
- cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
- } else { /* 802.11g */
- ipw_rt->rt_chbitmask =
- cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
- }
-
- /* set the rate in multiples of 500k/s */
- switch (rate) {
- case IPW_TX_RATE_1MB:
- ipw_rt->rt_rate = 2;
- break;
- case IPW_TX_RATE_2MB:
- ipw_rt->rt_rate = 4;
- break;
- case IPW_TX_RATE_5MB:
- ipw_rt->rt_rate = 10;
- break;
- case IPW_TX_RATE_6MB:
- ipw_rt->rt_rate = 12;
- break;
- case IPW_TX_RATE_9MB:
- ipw_rt->rt_rate = 18;
- break;
- case IPW_TX_RATE_11MB:
- ipw_rt->rt_rate = 22;
- break;
- case IPW_TX_RATE_12MB:
- ipw_rt->rt_rate = 24;
- break;
- case IPW_TX_RATE_18MB:
- ipw_rt->rt_rate = 36;
- break;
- case IPW_TX_RATE_24MB:
- ipw_rt->rt_rate = 48;
- break;
- case IPW_TX_RATE_36MB:
- ipw_rt->rt_rate = 72;
- break;
- case IPW_TX_RATE_48MB:
- ipw_rt->rt_rate = 96;
- break;
- case IPW_TX_RATE_54MB:
- ipw_rt->rt_rate = 108;
- break;
- default:
- ipw_rt->rt_rate = 0;
- break;
- }
-
- /* antenna number */
- ipw_rt->rt_antenna = (phy_flags & 3);
-
- /* set the preamble flag if we have it */
- if (phy_flags & (1 << 6))
- ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
-
- IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
-
- if (!ieee80211_rx(priv->prom_priv->ieee, skb, stats)) {
- priv->prom_priv->ieee->stats.rx_errors++;
- dev_kfree_skb_any(skb);
- }
-}
-#endif
-
-static int is_network_packet(struct ipw_priv *priv,
- struct ieee80211_hdr_4addr *header)
-{
- /* Filter incoming packets to determine if they are targetted toward
- * this network, discarding packets coming from ourselves */
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
- /* packets from our adapter are dropped (echo) */
- if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
- return 0;
-
- /* {broad,multi}cast packets to our BSSID go through */
- if (is_multicast_ether_addr(header->addr1))
- return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
-
- /* packets to our adapter go through */
- return !memcmp(header->addr1, priv->net_dev->dev_addr,
- ETH_ALEN);
-
- case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
- /* packets from our adapter are dropped (echo) */
- if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
- return 0;
-
- /* {broad,multi}cast packets to our BSS go through */
- if (is_multicast_ether_addr(header->addr1))
- return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
-
- /* packets to our adapter go through */
- return !memcmp(header->addr1, priv->net_dev->dev_addr,
- ETH_ALEN);
- }
-
- return 1;
-}
-
-#define IPW_PACKET_RETRY_TIME HZ
-
-static int is_duplicate_packet(struct ipw_priv *priv,
- struct ieee80211_hdr_4addr *header)
-{
- u16 sc = le16_to_cpu(header->seq_ctl);
- u16 seq = WLAN_GET_SEQ_SEQ(sc);
- u16 frag = WLAN_GET_SEQ_FRAG(sc);
- u16 *last_seq, *last_frag;
- unsigned long *last_time;
-
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- {
- struct list_head *p;
- struct ipw_ibss_seq *entry = NULL;
- u8 *mac = header->addr2;
- int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
-
- __list_for_each(p, &priv->ibss_mac_hash[index]) {
- entry =
- list_entry(p, struct ipw_ibss_seq, list);
- if (!memcmp(entry->mac, mac, ETH_ALEN))
- break;
- }
- if (p == &priv->ibss_mac_hash[index]) {
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (!entry) {
- IPW_ERROR
- ("Cannot malloc new mac entry\n");
- return 0;
- }
- memcpy(entry->mac, mac, ETH_ALEN);
- entry->seq_num = seq;
- entry->frag_num = frag;
- entry->packet_time = jiffies;
- list_add(&entry->list,
- &priv->ibss_mac_hash[index]);
- return 0;
- }
- last_seq = &entry->seq_num;
- last_frag = &entry->frag_num;
- last_time = &entry->packet_time;
- break;
- }
- case IW_MODE_INFRA:
- last_seq = &priv->last_seq_num;
- last_frag = &priv->last_frag_num;
- last_time = &priv->last_packet_time;
- break;
- default:
- return 0;
- }
- if ((*last_seq == seq) &&
- time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
- if (*last_frag == frag)
- goto drop;
- if (*last_frag + 1 != frag)
- /* out-of-order fragment */
- goto drop;
- } else
- *last_seq = seq;
-
- *last_frag = frag;
- *last_time = jiffies;
- return 0;
-
- drop:
- /* Comment this line now since we observed the card receives
- * duplicate packets but the FCTL_RETRY bit is not set in the
- * IBSS mode with fragmentation enabled.
- BUG_ON(!(le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_RETRY)); */
- return 1;
-}
-
-static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
- struct ipw_rx_mem_buffer *rxb,
- struct ieee80211_rx_stats *stats)
-{
- struct sk_buff *skb = rxb->skb;
- struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
- struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
- (skb->data + IPW_RX_FRAME_SIZE);
-
- ieee80211_rx_mgt(priv->ieee, header, stats);
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
- ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
- IEEE80211_STYPE_PROBE_RESP) ||
- (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
- IEEE80211_STYPE_BEACON))) {
- if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
- ipw_add_station(priv, header->addr2);
- }
-
- if (priv->config & CFG_NET_STATS) {
- IPW_DEBUG_HC("sending stat packet\n");
-
- /* Set the size of the skb to the size of the full
- * ipw header and 802.11 frame */
- skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
- IPW_RX_FRAME_SIZE);
-
- /* Advance past the ipw packet header to the 802.11 frame */
- skb_pull(skb, IPW_RX_FRAME_SIZE);
-
- /* Push the ieee80211_rx_stats before the 802.11 frame */
- memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
-
- skb->dev = priv->ieee->dev;
-
- /* Point raw at the ieee80211_stats */
- skb_reset_mac_header(skb);
-
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = __constant_htons(ETH_P_80211_STATS);
- memset(skb->cb, 0, sizeof(rxb->skb->cb));
- netif_rx(skb);
- rxb->skb = NULL;
- }
-}
-
-/*
- * Main entry function for recieving a packet with 80211 headers. This
- * should be called when ever the FW has notified us that there is a new
- * skb in the recieve queue.
- */
-static void ipw_rx(struct ipw_priv *priv)
-{
- struct ipw_rx_mem_buffer *rxb;
- struct ipw_rx_packet *pkt;
- struct ieee80211_hdr_4addr *header;
- u32 r, w, i;
- u8 network_packet;
- u8 fill_rx = 0;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
-
- r = ipw_read32(priv, IPW_RX_READ_INDEX);
- w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
- i = priv->rxq->read;
-
- if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2))
- fill_rx = 1;
-
- while (i != r) {
- rxb = priv->rxq->queue[i];
- if (unlikely(rxb == NULL)) {
- printk(KERN_CRIT "Queue not allocated!\n");
- break;
- }
- priv->rxq->queue[i] = NULL;
-
- pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
- IPW_RX_BUF_SIZE,
- PCI_DMA_FROMDEVICE);
-
- pkt = (struct ipw_rx_packet *)rxb->skb->data;
- IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
- pkt->header.message_type,
- pkt->header.rx_seq_num, pkt->header.control_bits);
-
- switch (pkt->header.message_type) {
- case RX_FRAME_TYPE: /* 802.11 frame */ {
- struct ieee80211_rx_stats stats = {
- .rssi = pkt->u.frame.rssi_dbm -
- IPW_RSSI_TO_DBM,
- .signal =
- le16_to_cpu(pkt->u.frame.rssi_dbm) -
- IPW_RSSI_TO_DBM + 0x100,
- .noise =
- le16_to_cpu(pkt->u.frame.noise),
- .rate = pkt->u.frame.rate,
- .mac_time = jiffies,
- .received_channel =
- pkt->u.frame.received_channel,
- .freq =
- (pkt->u.frame.
- control & (1 << 0)) ?
- IEEE80211_24GHZ_BAND :
- IEEE80211_52GHZ_BAND,
- .len = le16_to_cpu(pkt->u.frame.length),
- };
-
- if (stats.rssi != 0)
- stats.mask |= IEEE80211_STATMASK_RSSI;
- if (stats.signal != 0)
- stats.mask |= IEEE80211_STATMASK_SIGNAL;
- if (stats.noise != 0)
- stats.mask |= IEEE80211_STATMASK_NOISE;
- if (stats.rate != 0)
- stats.mask |= IEEE80211_STATMASK_RATE;
-
- priv->rx_packets++;
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
- ipw_handle_promiscuous_rx(priv, rxb, &stats);
-#endif
-
-#ifdef CONFIG_IPW2200_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
-#ifdef CONFIG_IPW2200_RADIOTAP
-
- ipw_handle_data_packet_monitor(priv,
- rxb,
- &stats);
-#else
- ipw_handle_data_packet(priv, rxb,
- &stats);
-#endif
- break;
- }
-#endif
-
- header =
- (struct ieee80211_hdr_4addr *)(rxb->skb->
- data +
- IPW_RX_FRAME_SIZE);
- /* TODO: Check Ad-Hoc dest/source and make sure
- * that we are actually parsing these packets
- * correctly -- we should probably use the
- * frame control of the packet and disregard
- * the current iw_mode */
-
- network_packet =
- is_network_packet(priv, header);
- if (network_packet && priv->assoc_network) {
- priv->assoc_network->stats.rssi =
- stats.rssi;
- priv->exp_avg_rssi =
- exponential_average(priv->exp_avg_rssi,
- stats.rssi, DEPTH_RSSI);
- }
-
- IPW_DEBUG_RX("Frame: len=%u\n",
- le16_to_cpu(pkt->u.frame.length));
-
- if (le16_to_cpu(pkt->u.frame.length) <
- ieee80211_get_hdrlen(le16_to_cpu(
- header->frame_ctl))) {
- IPW_DEBUG_DROP
- ("Received packet is too small. "
- "Dropping.\n");
- priv->ieee->stats.rx_errors++;
- priv->wstats.discard.misc++;
- break;
- }
-
- switch (WLAN_FC_GET_TYPE
- (le16_to_cpu(header->frame_ctl))) {
-
- case IEEE80211_FTYPE_MGMT:
- ipw_handle_mgmt_packet(priv, rxb,
- &stats);
- break;
-
- case IEEE80211_FTYPE_CTL:
- break;
-
- case IEEE80211_FTYPE_DATA:
- if (unlikely(!network_packet ||
- is_duplicate_packet(priv,
- header)))
- {
- IPW_DEBUG_DROP("Dropping: "
- "%s, "
- "%s, "
- "%s\n",
- print_mac(mac,
- header->
- addr1),
- print_mac(mac2,
- header->
- addr2),
- print_mac(mac3,
- header->
- addr3));
- break;
- }
-
- ipw_handle_data_packet(priv, rxb,
- &stats);
-
- break;
- }
- break;
- }
-
- case RX_HOST_NOTIFICATION_TYPE:{
- IPW_DEBUG_RX
- ("Notification: subtype=%02X flags=%02X size=%d\n",
- pkt->u.notification.subtype,
- pkt->u.notification.flags,
- le16_to_cpu(pkt->u.notification.size));
- ipw_rx_notification(priv, &pkt->u.notification);
- break;
- }
-
- default:
- IPW_DEBUG_RX("Bad Rx packet of type %d\n",
- pkt->header.message_type);
- break;
- }
-
- /* For now we just don't re-use anything. We can tweak this
- * later to try and re-use notification packets and SKBs that
- * fail to Rx correctly */
- if (rxb->skb != NULL) {
- dev_kfree_skb_any(rxb->skb);
- rxb->skb = NULL;
- }
-
- pci_unmap_single(priv->pci_dev, rxb->dma_addr,
- IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
- list_add_tail(&rxb->list, &priv->rxq->rx_used);
-
- i = (i + 1) % RX_QUEUE_SIZE;
-
- /* If there are a lot of unsued frames, restock the Rx queue
- * so the ucode won't assert */
- if (fill_rx) {
- priv->rxq->read = i;
- ipw_rx_queue_replenish(priv);
- }
- }
-
- /* Backtrack one entry */
- priv->rxq->read = i;
- ipw_rx_queue_restock(priv);
-}
-
-#define DEFAULT_RTS_THRESHOLD 2304U
-#define MIN_RTS_THRESHOLD 1U
-#define MAX_RTS_THRESHOLD 2304U
-#define DEFAULT_BEACON_INTERVAL 100U
-#define DEFAULT_SHORT_RETRY_LIMIT 7U
-#define DEFAULT_LONG_RETRY_LIMIT 4U
-
-/**
- * ipw_sw_reset
- * @option: options to control different reset behaviour
- * 0 = reset everything except the 'disable' module_param
- * 1 = reset everything and print out driver info (for probe only)
- * 2 = reset everything
- */
-static int ipw_sw_reset(struct ipw_priv *priv, int option)
-{
- int band, modulation;
- int old_mode = priv->ieee->iw_mode;
-
- /* Initialize module parameter values here */
- priv->config = 0;
-
- /* We default to disabling the LED code as right now it causes
- * too many systems to lock up... */
- if (!led)
- priv->config |= CFG_NO_LED;
-
- if (associate)
- priv->config |= CFG_ASSOCIATE;
- else
- IPW_DEBUG_INFO("Auto associate disabled.\n");
-
- if (auto_create)
- priv->config |= CFG_ADHOC_CREATE;
- else
- IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
-
- priv->config &= ~CFG_STATIC_ESSID;
- priv->essid_len = 0;
- memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
-
- if (disable && option) {
- priv->status |= STATUS_RF_KILL_SW;
- IPW_DEBUG_INFO("Radio disabled.\n");
- }
-
- if (channel != 0) {
- priv->config |= CFG_STATIC_CHANNEL;
- priv->channel = channel;
- IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
- /* TODO: Validate that provided channel is in range */
- }
-#ifdef CONFIG_IPW2200_QOS
- ipw_qos_init(priv, qos_enable, qos_burst_enable,
- burst_duration_CCK, burst_duration_OFDM);
-#endif /* CONFIG_IPW2200_QOS */
-
- switch (mode) {
- case 1:
- priv->ieee->iw_mode = IW_MODE_ADHOC;
- priv->net_dev->type = ARPHRD_ETHER;
-
- break;
-#ifdef CONFIG_IPW2200_MONITOR
- case 2:
- priv->ieee->iw_mode = IW_MODE_MONITOR;
-#ifdef CONFIG_IPW2200_RADIOTAP
- priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
-#else
- priv->net_dev->type = ARPHRD_IEEE80211;
-#endif
- break;
-#endif
- default:
- case 0:
- priv->net_dev->type = ARPHRD_ETHER;
- priv->ieee->iw_mode = IW_MODE_INFRA;
- break;
- }
-
- if (hwcrypto) {
- priv->ieee->host_encrypt = 0;
- priv->ieee->host_encrypt_msdu = 0;
- priv->ieee->host_decrypt = 0;
- priv->ieee->host_mc_decrypt = 0;
- }
- IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
-
- /* IPW2200/2915 is abled to do hardware fragmentation. */
- priv->ieee->host_open_frag = 0;
-
- if ((priv->pci_dev->device == 0x4223) ||
- (priv->pci_dev->device == 0x4224)) {
- if (option == 1)
- printk(KERN_INFO DRV_NAME
- ": Detected Intel PRO/Wireless 2915ABG Network "
- "Connection\n");
- priv->ieee->abg_true = 1;
- band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
- modulation = IEEE80211_OFDM_MODULATION |
- IEEE80211_CCK_MODULATION;
- priv->adapter = IPW_2915ABG;
- priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
- } else {
- if (option == 1)
- printk(KERN_INFO DRV_NAME
- ": Detected Intel PRO/Wireless 2200BG Network "
- "Connection\n");
-
- priv->ieee->abg_true = 0;
- band = IEEE80211_24GHZ_BAND;
- modulation = IEEE80211_OFDM_MODULATION |
- IEEE80211_CCK_MODULATION;
- priv->adapter = IPW_2200BG;
- priv->ieee->mode = IEEE_G | IEEE_B;
- }
-
- priv->ieee->freq_band = band;
- priv->ieee->modulation = modulation;
-
- priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
-
- priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
- priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
-
- priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
- priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
- priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
-
- /* If power management is turned on, default to AC mode */
- priv->power_mode = IPW_POWER_AC;
- priv->tx_power = IPW_TX_POWER_DEFAULT;
-
- return old_mode == priv->ieee->iw_mode;
-}
-
-/*
- * This file defines the Wireless Extension handlers. It does not
- * define any methods of hardware manipulation and relies on the
- * functions defined in ipw_main to provide the HW interaction.
- *
- * The exception to this is the use of the ipw_get_ordinal()
- * function used to poll the hardware vs. making unecessary calls.
- *
- */
-
-static int ipw_wx_get_name(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- if (priv->status & STATUS_RF_KILL_MASK)
- strcpy(wrqu->name, "radio off");
- else if (!(priv->status & STATUS_ASSOCIATED))
- strcpy(wrqu->name, "unassociated");
- else
- snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
- ipw_modes[priv->assoc_request.ieee_mode]);
- IPW_DEBUG_WX("Name: %s\n", wrqu->name);
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
-{
- if (channel == 0) {
- IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
- priv->config &= ~CFG_STATIC_CHANNEL;
- IPW_DEBUG_ASSOC("Attempting to associate with new "
- "parameters.\n");
- ipw_associate(priv);
- return 0;
- }
-
- priv->config |= CFG_STATIC_CHANNEL;
-
- if (priv->channel == channel) {
- IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
- channel);
- return 0;
- }
-
- IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
- priv->channel = channel;
-
-#ifdef CONFIG_IPW2200_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
- int i;
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_SCAN("Scan abort triggered due to "
- "channel change.\n");
- ipw_abort_scan(priv);
- }
-
- for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
- udelay(10);
-
- if (priv->status & STATUS_SCANNING)
- IPW_DEBUG_SCAN("Still scanning...\n");
- else
- IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
- 1000 - i);
-
- return 0;
- }
-#endif /* CONFIG_IPW2200_MONITOR */
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
-
- return 0;
-}
-
-static int ipw_wx_set_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
- struct iw_freq *fwrq = &wrqu->freq;
- int ret = 0, i;
- u8 channel, flags;
- int band;
-
- if (fwrq->m == 0) {
- IPW_DEBUG_WX("SET Freq/Channel -> any\n");
- mutex_lock(&priv->mutex);
- ret = ipw_set_channel(priv, 0);
- mutex_unlock(&priv->mutex);
- return ret;
- }
- /* if setting by freq convert to channel */
- if (fwrq->e == 1) {
- channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
- if (channel == 0)
- return -EINVAL;
- } else
- channel = fwrq->m;
-
- if (!(band = ieee80211_is_valid_channel(priv->ieee, channel)))
- return -EINVAL;
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
- i = ieee80211_channel_to_index(priv->ieee, channel);
- if (i == -1)
- return -EINVAL;
-
- flags = (band == IEEE80211_24GHZ_BAND) ?
- geo->bg[i].flags : geo->a[i].flags;
- if (flags & IEEE80211_CH_PASSIVE_ONLY) {
- IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
- return -EINVAL;
- }
- }
-
- IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
- mutex_lock(&priv->mutex);
- ret = ipw_set_channel(priv, channel);
- mutex_unlock(&priv->mutex);
- return ret;
-}
-
-static int ipw_wx_get_freq(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- wrqu->freq.e = 0;
-
- /* If we are associated, trying to associate, or have a statically
- * configured CHANNEL then return that; otherwise return ANY */
- mutex_lock(&priv->mutex);
- if (priv->config & CFG_STATIC_CHANNEL ||
- priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) {
- int i;
-
- i = ieee80211_channel_to_index(priv->ieee, priv->channel);
- BUG_ON(i == -1);
- wrqu->freq.e = 1;
-
- switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
- case IEEE80211_52GHZ_BAND:
- wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000;
- break;
-
- case IEEE80211_24GHZ_BAND:
- wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000;
- break;
-
- default:
- BUG();
- }
- } else
- wrqu->freq.m = 0;
-
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
- return 0;
-}
-
-static int ipw_wx_set_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
-
- switch (wrqu->mode) {
-#ifdef CONFIG_IPW2200_MONITOR
- case IW_MODE_MONITOR:
-#endif
- case IW_MODE_ADHOC:
- case IW_MODE_INFRA:
- break;
- case IW_MODE_AUTO:
- wrqu->mode = IW_MODE_INFRA;
- break;
- default:
- return -EINVAL;
- }
- if (wrqu->mode == priv->ieee->iw_mode)
- return 0;
-
- mutex_lock(&priv->mutex);
-
- ipw_sw_reset(priv, 0);
-
-#ifdef CONFIG_IPW2200_MONITOR
- if (priv->ieee->iw_mode == IW_MODE_MONITOR)
- priv->net_dev->type = ARPHRD_ETHER;
-
- if (wrqu->mode == IW_MODE_MONITOR)
-#ifdef CONFIG_IPW2200_RADIOTAP
- priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
-#else
- priv->net_dev->type = ARPHRD_IEEE80211;
-#endif
-#endif /* CONFIG_IPW2200_MONITOR */
-
- /* Free the existing firmware and reset the fw_loaded
- * flag so ipw_load() will bring in the new firmawre */
- free_firmware();
-
- priv->ieee->iw_mode = wrqu->mode;
-
- queue_work(priv->workqueue, &priv->adapter_restart);
- mutex_unlock(&priv->mutex);
- return err;
-}
-
-static int ipw_wx_get_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->mode = priv->ieee->iw_mode;
- IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-/* Values are in microsecond */
-static const s32 timeout_duration[] = {
- 350000,
- 250000,
- 75000,
- 37000,
- 25000,
-};
-
-static const s32 period_duration[] = {
- 400000,
- 700000,
- 1000000,
- 1000000,
- 1000000
-};
-
-static int ipw_wx_get_range(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct iw_range *range = (struct iw_range *)extra;
- const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
- int i = 0, j;
-
- wrqu->data.length = sizeof(*range);
- memset(range, 0, sizeof(*range));
-
- /* 54Mbs == ~27 Mb/s real (802.11g) */
- range->throughput = 27 * 1000 * 1000;
-
- range->max_qual.qual = 100;
- /* TODO: Find real max RSSI and stick here */
- range->max_qual.level = 0;
- range->max_qual.noise = 0;
- range->max_qual.updated = 7; /* Updated all three */
-
- range->avg_qual.qual = 70;
- /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
- range->avg_qual.level = 0; /* FIXME to real average level */
- range->avg_qual.noise = 0;
- range->avg_qual.updated = 7; /* Updated all three */
- mutex_lock(&priv->mutex);
- range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
-
- for (i = 0; i < range->num_bitrates; i++)
- range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
- 500000;
-
- range->max_rts = DEFAULT_RTS_THRESHOLD;
- range->min_frag = MIN_FRAG_THRESHOLD;
- range->max_frag = MAX_FRAG_THRESHOLD;
-
- range->encoding_size[0] = 5;
- range->encoding_size[1] = 13;
- range->num_encoding_sizes = 2;
- range->max_encoding_tokens = WEP_KEYS;
-
- /* Set the Wireless Extension versions */
- range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 18;
-
- i = 0;
- if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
- for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) {
- if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
- (geo->bg[j].flags & IEEE80211_CH_PASSIVE_ONLY))
- continue;
-
- range->freq[i].i = geo->bg[j].channel;
- range->freq[i].m = geo->bg[j].freq * 100000;
- range->freq[i].e = 1;
- i++;
- }
- }
-
- if (priv->ieee->mode & IEEE_A) {
- for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) {
- if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
- (geo->a[j].flags & IEEE80211_CH_PASSIVE_ONLY))
- continue;
-
- range->freq[i].i = geo->a[j].channel;
- range->freq[i].m = geo->a[j].freq * 100000;
- range->freq[i].e = 1;
- i++;
- }
- }
-
- range->num_channels = i;
- range->num_frequency = i;
-
- mutex_unlock(&priv->mutex);
-
- /* Event capability (kernel + driver) */
- range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
- IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
- IW_EVENT_CAPA_MASK(SIOCGIWAP) |
- IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
- range->event_capa[1] = IW_EVENT_CAPA_K_1;
-
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
- IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
- range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
-
- IPW_DEBUG_WX("GET Range\n");
- return 0;
-}
-
-static int ipw_wx_set_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- DECLARE_MAC_BUF(mac);
-
- static const unsigned char any[] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
- };
- static const unsigned char off[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
- return -EINVAL;
- mutex_lock(&priv->mutex);
- if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
- !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
- /* we disable mandatory BSSID association */
- IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
- priv->config &= ~CFG_STATIC_BSSID;
- IPW_DEBUG_ASSOC("Attempting to associate with new "
- "parameters.\n");
- ipw_associate(priv);
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- priv->config |= CFG_STATIC_BSSID;
- if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
- IPW_DEBUG_WX("BSSID set to current BSSID.\n");
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- IPW_DEBUG_WX("Setting mandatory BSSID to %s\n",
- print_mac(mac, wrqu->ap_addr.sa_data));
-
- memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
-
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_wap(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- DECLARE_MAC_BUF(mac);
-
- /* If we are associated, trying to associate, or have a statically
- * configured BSSID then return that; otherwise return ANY */
- mutex_lock(&priv->mutex);
- if (priv->config & CFG_STATIC_BSSID ||
- priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
- wrqu->ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
- } else
- memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
-
- IPW_DEBUG_WX("Getting WAP BSSID: %s\n",
- print_mac(mac, wrqu->ap_addr.sa_data));
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_set_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int length;
-
- mutex_lock(&priv->mutex);
-
- if (!wrqu->essid.flags)
- {
- IPW_DEBUG_WX("Setting ESSID to ANY\n");
- ipw_disassociate(priv);
- priv->config &= ~CFG_STATIC_ESSID;
- ipw_associate(priv);
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
-
- priv->config |= CFG_STATIC_ESSID;
-
- if (priv->essid_len == length && !memcmp(priv->essid, extra, length)
- && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
- IPW_DEBUG_WX("ESSID set to current ESSID.\n");
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(extra, length),
- length);
-
- priv->essid_len = length;
- memcpy(priv->essid, extra, priv->essid_len);
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
-
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_essid(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- /* If we are associated, trying to associate, or have a statically
- * configured ESSID then return that; otherwise return ANY */
- mutex_lock(&priv->mutex);
- if (priv->config & CFG_STATIC_ESSID ||
- priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
- IPW_DEBUG_WX("Getting essid: '%s'\n",
- escape_essid(priv->essid, priv->essid_len));
- memcpy(extra, priv->essid, priv->essid_len);
- wrqu->essid.length = priv->essid_len;
- wrqu->essid.flags = 1; /* active */
- } else {
- IPW_DEBUG_WX("Getting essid: ANY\n");
- wrqu->essid.length = 0;
- wrqu->essid.flags = 0; /* active */
- }
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_set_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
- if (wrqu->data.length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
- mutex_lock(&priv->mutex);
- wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
- memset(priv->nick, 0, sizeof(priv->nick));
- memcpy(priv->nick, extra, wrqu->data.length);
- IPW_DEBUG_TRACE("<<\n");
- mutex_unlock(&priv->mutex);
- return 0;
-
-}
-
-static int ipw_wx_get_nick(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- IPW_DEBUG_WX("Getting nick\n");
- mutex_lock(&priv->mutex);
- wrqu->data.length = strlen(priv->nick);
- memcpy(extra, priv->nick, wrqu->data.length);
- wrqu->data.flags = 1; /* active */
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_set_sens(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value);
- IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value);
- mutex_lock(&priv->mutex);
-
- if (wrqu->sens.fixed == 0)
- {
- priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
- priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
- goto out;
- }
- if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) ||
- (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) {
- err = -EINVAL;
- goto out;
- }
-
- priv->roaming_threshold = wrqu->sens.value;
- priv->disassociate_threshold = 3*wrqu->sens.value;
- out:
- mutex_unlock(&priv->mutex);
- return err;
-}
-
-static int ipw_wx_get_sens(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->sens.fixed = 1;
- wrqu->sens.value = priv->roaming_threshold;
- mutex_unlock(&priv->mutex);
-
- IPW_DEBUG_WX("GET roaming threshold -> %s %d \n",
- wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
-
- return 0;
-}
-
-static int ipw_wx_set_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- /* TODO: We should use semaphores or locks for access to priv */
- struct ipw_priv *priv = ieee80211_priv(dev);
- u32 target_rate = wrqu->bitrate.value;
- u32 fixed, mask;
-
- /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
- /* value = X, fixed = 1 means only rate X */
- /* value = X, fixed = 0 means all rates lower equal X */
-
- if (target_rate == -1) {
- fixed = 0;
- mask = IEEE80211_DEFAULT_RATES_MASK;
- /* Now we should reassociate */
- goto apply;
- }
-
- mask = 0;
- fixed = wrqu->bitrate.fixed;
-
- if (target_rate == 1000000 || !fixed)
- mask |= IEEE80211_CCK_RATE_1MB_MASK;
- if (target_rate == 1000000)
- goto apply;
-
- if (target_rate == 2000000 || !fixed)
- mask |= IEEE80211_CCK_RATE_2MB_MASK;
- if (target_rate == 2000000)
- goto apply;
-
- if (target_rate == 5500000 || !fixed)
- mask |= IEEE80211_CCK_RATE_5MB_MASK;
- if (target_rate == 5500000)
- goto apply;
-
- if (target_rate == 6000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_6MB_MASK;
- if (target_rate == 6000000)
- goto apply;
-
- if (target_rate == 9000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_9MB_MASK;
- if (target_rate == 9000000)
- goto apply;
-
- if (target_rate == 11000000 || !fixed)
- mask |= IEEE80211_CCK_RATE_11MB_MASK;
- if (target_rate == 11000000)
- goto apply;
-
- if (target_rate == 12000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_12MB_MASK;
- if (target_rate == 12000000)
- goto apply;
-
- if (target_rate == 18000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_18MB_MASK;
- if (target_rate == 18000000)
- goto apply;
-
- if (target_rate == 24000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_24MB_MASK;
- if (target_rate == 24000000)
- goto apply;
-
- if (target_rate == 36000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_36MB_MASK;
- if (target_rate == 36000000)
- goto apply;
-
- if (target_rate == 48000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_48MB_MASK;
- if (target_rate == 48000000)
- goto apply;
-
- if (target_rate == 54000000 || !fixed)
- mask |= IEEE80211_OFDM_RATE_54MB_MASK;
- if (target_rate == 54000000)
- goto apply;
-
- IPW_DEBUG_WX("invalid rate specified, returning error\n");
- return -EINVAL;
-
- apply:
- IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
- mask, fixed ? "fixed" : "sub-rates");
- mutex_lock(&priv->mutex);
- if (mask == IEEE80211_DEFAULT_RATES_MASK) {
- priv->config &= ~CFG_FIXED_RATE;
- ipw_set_fixed_rate(priv, priv->ieee->mode);
- } else
- priv->config |= CFG_FIXED_RATE;
-
- if (priv->rates_mask == mask) {
- IPW_DEBUG_WX("Mask set to current mask.\n");
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- priv->rates_mask = mask;
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
-
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_rate(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->bitrate.value = priv->last_rate;
- wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
- return 0;
-}
-
-static int ipw_wx_set_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- if (wrqu->rts.disabled || !wrqu->rts.fixed)
- priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
- else {
- if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
- wrqu->rts.value > MAX_RTS_THRESHOLD) {
- mutex_unlock(&priv->mutex);
- return -EINVAL;
- }
- priv->rts_threshold = wrqu->rts.value;
- }
-
- ipw_send_rts_threshold(priv, priv->rts_threshold);
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
- return 0;
-}
-
-static int ipw_wx_get_rts(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->rts.value = priv->rts_threshold;
- wrqu->rts.fixed = 0; /* no auto select */
- wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
- return 0;
-}
-
-static int ipw_wx_set_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int err = 0;
-
- mutex_lock(&priv->mutex);
- if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
- err = -EINPROGRESS;
- goto out;
- }
-
- if (!wrqu->power.fixed)
- wrqu->power.value = IPW_TX_POWER_DEFAULT;
-
- if (wrqu->power.flags != IW_TXPOW_DBM) {
- err = -EINVAL;
- goto out;
- }
-
- if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
- (wrqu->power.value < IPW_TX_POWER_MIN)) {
- err = -EINVAL;
- goto out;
- }
-
- priv->tx_power = wrqu->power.value;
- err = ipw_set_tx_power(priv);
- out:
- mutex_unlock(&priv->mutex);
- return err;
-}
-
-static int ipw_wx_get_txpow(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->power.value = priv->tx_power;
- wrqu->power.fixed = 1;
- wrqu->power.flags = IW_TXPOW_DBM;
- wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
- mutex_unlock(&priv->mutex);
-
- IPW_DEBUG_WX("GET TX Power -> %s %d \n",
- wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
-
- return 0;
-}
-
-static int ipw_wx_set_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- if (wrqu->frag.disabled || !wrqu->frag.fixed)
- priv->ieee->fts = DEFAULT_FTS;
- else {
- if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
- wrqu->frag.value > MAX_FRAG_THRESHOLD) {
- mutex_unlock(&priv->mutex);
- return -EINVAL;
- }
-
- priv->ieee->fts = wrqu->frag.value & ~0x1;
- }
-
- ipw_send_frag_threshold(priv, wrqu->frag.value);
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
- return 0;
-}
-
-static int ipw_wx_get_frag(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- wrqu->frag.value = priv->ieee->fts;
- wrqu->frag.fixed = 0; /* no auto select */
- wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
-
- return 0;
-}
-
-static int ipw_wx_set_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
- return -EINVAL;
-
- if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
- return 0;
-
- if (wrqu->retry.value < 0 || wrqu->retry.value >= 255)
- return -EINVAL;
-
- mutex_lock(&priv->mutex);
- if (wrqu->retry.flags & IW_RETRY_SHORT)
- priv->short_retry_limit = (u8) wrqu->retry.value;
- else if (wrqu->retry.flags & IW_RETRY_LONG)
- priv->long_retry_limit = (u8) wrqu->retry.value;
- else {
- priv->short_retry_limit = (u8) wrqu->retry.value;
- priv->long_retry_limit = (u8) wrqu->retry.value;
- }
-
- ipw_send_retry_limit(priv, priv->short_retry_limit,
- priv->long_retry_limit);
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
- priv->short_retry_limit, priv->long_retry_limit);
- return 0;
-}
-
-static int ipw_wx_get_retry(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- mutex_lock(&priv->mutex);
- wrqu->retry.disabled = 0;
-
- if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
- mutex_unlock(&priv->mutex);
- return -EINVAL;
- }
-
- if (wrqu->retry.flags & IW_RETRY_LONG) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- wrqu->retry.value = priv->long_retry_limit;
- } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
- wrqu->retry.value = priv->short_retry_limit;
- } else {
- wrqu->retry.flags = IW_RETRY_LIMIT;
- wrqu->retry.value = priv->short_retry_limit;
- }
- mutex_unlock(&priv->mutex);
-
- IPW_DEBUG_WX("GET retry -> %d \n", wrqu->retry.value);
-
- return 0;
-}
-
-static int ipw_wx_set_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct iw_scan_req *req = (struct iw_scan_req *)extra;
- struct delayed_work *work = NULL;
-
- mutex_lock(&priv->mutex);
-
- priv->user_requested_scan = 1;
-
- if (wrqu->data.length == sizeof(struct iw_scan_req)) {
- if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- int len = min((int)req->essid_len,
- (int)sizeof(priv->direct_scan_ssid));
- memcpy(priv->direct_scan_ssid, req->essid, len);
- priv->direct_scan_ssid_len = len;
- work = &priv->request_direct_scan;
- } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
- work = &priv->request_passive_scan;
- }
- } else {
- /* Normal active broadcast scan */
- work = &priv->request_scan;
- }
-
- mutex_unlock(&priv->mutex);
-
- IPW_DEBUG_WX("Start scan\n");
-
- queue_delayed_work(priv->workqueue, work, 0);
-
- return 0;
-}
-
-static int ipw_wx_get_scan(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
-}
-
-static int ipw_wx_set_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int ret;
- u32 cap = priv->capability;
-
- mutex_lock(&priv->mutex);
- ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
-
- /* In IBSS mode, we need to notify the firmware to update
- * the beacon info after we changed the capability. */
- if (cap != priv->capability &&
- priv->ieee->iw_mode == IW_MODE_ADHOC &&
- priv->status & STATUS_ASSOCIATED)
- ipw_disassociate(priv);
-
- mutex_unlock(&priv->mutex);
- return ret;
-}
-
-static int ipw_wx_get_encode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *key)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
-}
-
-static int ipw_wx_set_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int err;
- mutex_lock(&priv->mutex);
- if (wrqu->power.disabled) {
- priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
- err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
- if (err) {
- IPW_DEBUG_WX("failed setting power mode.\n");
- mutex_unlock(&priv->mutex);
- return err;
- }
- IPW_DEBUG_WX("SET Power Management Mode -> off\n");
- mutex_unlock(&priv->mutex);
- return 0;
- }
-
- switch (wrqu->power.flags & IW_POWER_MODE) {
- case IW_POWER_ON: /* If not specified */
- case IW_POWER_MODE: /* If set all mask */
- case IW_POWER_ALL_R: /* If explicitly state all */
- break;
- default: /* Otherwise we don't support it */
- IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
- wrqu->power.flags);
- mutex_unlock(&priv->mutex);
- return -EOPNOTSUPP;
- }
-
- /* If the user hasn't specified a power management mode yet, default
- * to BATTERY */
- if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
- priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
- else
- priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
-
- err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
- if (err) {
- IPW_DEBUG_WX("failed setting power mode.\n");
- mutex_unlock(&priv->mutex);
- return err;
- }
-
- IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_power(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- if (!(priv->power_mode & IPW_POWER_ENABLED))
- wrqu->power.disabled = 1;
- else
- wrqu->power.disabled = 0;
-
- mutex_unlock(&priv->mutex);
- IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
-
- return 0;
-}
-
-static int ipw_wx_set_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int mode = *(int *)extra;
- int err;
-
- mutex_lock(&priv->mutex);
- if ((mode < 1) || (mode > IPW_POWER_LIMIT))
- mode = IPW_POWER_AC;
-
- if (IPW_POWER_LEVEL(priv->power_mode) != mode) {
- err = ipw_send_power_mode(priv, mode);
- if (err) {
- IPW_DEBUG_WX("failed setting power mode.\n");
- mutex_unlock(&priv->mutex);
- return err;
- }
- priv->power_mode = IPW_POWER_ENABLED | mode;
- }
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-#define MAX_WX_STRING 80
-static int ipw_wx_get_powermode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int level = IPW_POWER_LEVEL(priv->power_mode);
- char *p = extra;
-
- p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
-
- switch (level) {
- case IPW_POWER_AC:
- p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
- break;
- case IPW_POWER_BATTERY:
- p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
- break;
- default:
- p += snprintf(p, MAX_WX_STRING - (p - extra),
- "(Timeout %dms, Period %dms)",
- timeout_duration[level - 1] / 1000,
- period_duration[level - 1] / 1000);
- }
-
- if (!(priv->power_mode & IPW_POWER_ENABLED))
- p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
-
- wrqu->data.length = p - extra + 1;
-
- return 0;
-}
-
-static int ipw_wx_set_wireless_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int mode = *(int *)extra;
- u8 band = 0, modulation = 0;
-
- if (mode == 0 || mode & ~IEEE_MODE_MASK) {
- IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
- return -EINVAL;
- }
- mutex_lock(&priv->mutex);
- if (priv->adapter == IPW_2915ABG) {
- priv->ieee->abg_true = 1;
- if (mode & IEEE_A) {
- band |= IEEE80211_52GHZ_BAND;
- modulation |= IEEE80211_OFDM_MODULATION;
- } else
- priv->ieee->abg_true = 0;
- } else {
- if (mode & IEEE_A) {
- IPW_WARNING("Attempt to set 2200BG into "
- "802.11a mode\n");
- mutex_unlock(&priv->mutex);
- return -EINVAL;
- }
-
- priv->ieee->abg_true = 0;
- }
-
- if (mode & IEEE_B) {
- band |= IEEE80211_24GHZ_BAND;
- modulation |= IEEE80211_CCK_MODULATION;
- } else
- priv->ieee->abg_true = 0;
-
- if (mode & IEEE_G) {
- band |= IEEE80211_24GHZ_BAND;
- modulation |= IEEE80211_OFDM_MODULATION;
- } else
- priv->ieee->abg_true = 0;
-
- priv->ieee->mode = mode;
- priv->ieee->freq_band = band;
- priv->ieee->modulation = modulation;
- init_supported_rates(priv, &priv->rates);
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
- if (!ipw_disassociate(priv)) {
- ipw_send_supported_rates(priv, &priv->rates);
- ipw_associate(priv);
- }
-
- /* Update the band LEDs */
- ipw_led_band_on(priv);
-
- IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
- mode & IEEE_A ? 'a' : '.',
- mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_wireless_mode(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- switch (priv->ieee->mode) {
- case IEEE_A:
- strncpy(extra, "802.11a (1)", MAX_WX_STRING);
- break;
- case IEEE_B:
- strncpy(extra, "802.11b (2)", MAX_WX_STRING);
- break;
- case IEEE_A | IEEE_B:
- strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
- break;
- case IEEE_G:
- strncpy(extra, "802.11g (4)", MAX_WX_STRING);
- break;
- case IEEE_A | IEEE_G:
- strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
- break;
- case IEEE_B | IEEE_G:
- strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
- break;
- case IEEE_A | IEEE_B | IEEE_G:
- strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
- break;
- default:
- strncpy(extra, "unknown", MAX_WX_STRING);
- break;
- }
-
- IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
-
- wrqu->data.length = strlen(extra) + 1;
- mutex_unlock(&priv->mutex);
-
- return 0;
-}
-
-static int ipw_wx_set_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int mode = *(int *)extra;
- mutex_lock(&priv->mutex);
- /* Switching from SHORT -> LONG requires a disassociation */
- if (mode == 1) {
- if (!(priv->config & CFG_PREAMBLE_LONG)) {
- priv->config |= CFG_PREAMBLE_LONG;
-
- /* Network configuration changed -- force [re]association */
- IPW_DEBUG_ASSOC
- ("[re]association triggered due to preamble change.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
- }
- goto done;
- }
-
- if (mode == 0) {
- priv->config &= ~CFG_PREAMBLE_LONG;
- goto done;
- }
- mutex_unlock(&priv->mutex);
- return -EINVAL;
-
- done:
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static int ipw_wx_get_preamble(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
- if (priv->config & CFG_PREAMBLE_LONG)
- snprintf(wrqu->name, IFNAMSIZ, "long (1)");
- else
- snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-#ifdef CONFIG_IPW2200_MONITOR
-static int ipw_wx_set_monitor(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int *parms = (int *)extra;
- int enable = (parms[0] > 0);
- mutex_lock(&priv->mutex);
- IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
- if (enable) {
- if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
-#ifdef CONFIG_IPW2200_RADIOTAP
- priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
-#else
- priv->net_dev->type = ARPHRD_IEEE80211;
-#endif
- queue_work(priv->workqueue, &priv->adapter_restart);
- }
-
- ipw_set_channel(priv, parms[1]);
- } else {
- if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
- mutex_unlock(&priv->mutex);
- return 0;
- }
- priv->net_dev->type = ARPHRD_ETHER;
- queue_work(priv->workqueue, &priv->adapter_restart);
- }
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-#endif /* CONFIG_IPW2200_MONITOR */
-
-static int ipw_wx_reset(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- IPW_DEBUG_WX("RESET\n");
- queue_work(priv->workqueue, &priv->adapter_restart);
- return 0;
-}
-
-static int ipw_wx_sw_reset(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- union iwreq_data wrqu_sec = {
- .encoding = {
- .flags = IW_ENCODE_DISABLED,
- },
- };
- int ret;
-
- IPW_DEBUG_WX("SW_RESET\n");
-
- mutex_lock(&priv->mutex);
-
- ret = ipw_sw_reset(priv, 2);
- if (!ret) {
- free_firmware();
- ipw_adapter_restart(priv);
- }
-
- /* The SW reset bit might have been toggled on by the 'disable'
- * module parameter, so take appropriate action */
- ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
-
- mutex_unlock(&priv->mutex);
- ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
- mutex_lock(&priv->mutex);
-
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- /* Configuration likely changed -- force [re]association */
- IPW_DEBUG_ASSOC("[re]association triggered due to sw "
- "reset.\n");
- if (!ipw_disassociate(priv))
- ipw_associate(priv);
- }
-
- mutex_unlock(&priv->mutex);
-
- return 0;
-}
-
-/* Rebase the WE IOCTLs to zero for the handler array */
-#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
-static iw_handler ipw_wx_handlers[] = {
- IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
- IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
- IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
- IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
- IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
- IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens,
- IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens,
- IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
- IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
- IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
- IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
- IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
- IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
- IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
- IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
- IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
- IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
- IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
- IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
- IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
- IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
- IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
- IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
- IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
- IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
- IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
- IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
- IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
- IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
- IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
- IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
- IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
- IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
- IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
- IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
- IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
- IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
- IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
- IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
- IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
- IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
-};
-
-enum {
- IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
- IPW_PRIV_GET_POWER,
- IPW_PRIV_SET_MODE,
- IPW_PRIV_GET_MODE,
- IPW_PRIV_SET_PREAMBLE,
- IPW_PRIV_GET_PREAMBLE,
- IPW_PRIV_RESET,
- IPW_PRIV_SW_RESET,
-#ifdef CONFIG_IPW2200_MONITOR
- IPW_PRIV_SET_MONITOR,
-#endif
-};
-
-static struct iw_priv_args ipw_priv_args[] = {
- {
- .cmd = IPW_PRIV_SET_POWER,
- .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- .name = "set_power"},
- {
- .cmd = IPW_PRIV_GET_POWER,
- .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
- .name = "get_power"},
- {
- .cmd = IPW_PRIV_SET_MODE,
- .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- .name = "set_mode"},
- {
- .cmd = IPW_PRIV_GET_MODE,
- .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
- .name = "get_mode"},
- {
- .cmd = IPW_PRIV_SET_PREAMBLE,
- .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- .name = "set_preamble"},
- {
- .cmd = IPW_PRIV_GET_PREAMBLE,
- .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
- .name = "get_preamble"},
- {
- IPW_PRIV_RESET,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
- {
- IPW_PRIV_SW_RESET,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
-#ifdef CONFIG_IPW2200_MONITOR
- {
- IPW_PRIV_SET_MONITOR,
- IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
-#endif /* CONFIG_IPW2200_MONITOR */
-};
-
-static iw_handler ipw_priv_handler[] = {
- ipw_wx_set_powermode,
- ipw_wx_get_powermode,
- ipw_wx_set_wireless_mode,
- ipw_wx_get_wireless_mode,
- ipw_wx_set_preamble,
- ipw_wx_get_preamble,
- ipw_wx_reset,
- ipw_wx_sw_reset,
-#ifdef CONFIG_IPW2200_MONITOR
- ipw_wx_set_monitor,
-#endif
-};
-
-static struct iw_handler_def ipw_wx_handler_def = {
- .standard = ipw_wx_handlers,
- .num_standard = ARRAY_SIZE(ipw_wx_handlers),
- .num_private = ARRAY_SIZE(ipw_priv_handler),
- .num_private_args = ARRAY_SIZE(ipw_priv_args),
- .private = ipw_priv_handler,
- .private_args = ipw_priv_args,
- .get_wireless_stats = ipw_get_wireless_stats,
-};
-
-/*
- * Get wireless statistics.
- * Called by /proc/net/wireless
- * Also called by SIOCGIWSTATS
- */
-static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct iw_statistics *wstats;
-
- wstats = &priv->wstats;
-
- /* if hw is disabled, then ipw_get_ordinal() can't be called.
- * netdev->get_wireless_stats seems to be called before fw is
- * initialized. STATUS_ASSOCIATED will only be set if the hw is up
- * and associated; if not associcated, the values are all meaningless
- * anyway, so set them all to NULL and INVALID */
- if (!(priv->status & STATUS_ASSOCIATED)) {
- wstats->miss.beacon = 0;
- wstats->discard.retries = 0;
- wstats->qual.qual = 0;
- wstats->qual.level = 0;
- wstats->qual.noise = 0;
- wstats->qual.updated = 7;
- wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
- IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
- return wstats;
- }
-
- wstats->qual.qual = priv->quality;
- wstats->qual.level = priv->exp_avg_rssi;
- wstats->qual.noise = priv->exp_avg_noise;
- wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
- IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
-
- wstats->miss.beacon = average_value(&priv->average_missed_beacons);
- wstats->discard.retries = priv->last_tx_failures;
- wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
-
-/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
- goto fail_get_ordinal;
- wstats->discard.retries += tx_retry; */
-
- return wstats;
-}
-
-/* net device stuff */
-
-static void init_sys_config(struct ipw_sys_config *sys_config)
-{
- memset(sys_config, 0, sizeof(struct ipw_sys_config));
- sys_config->bt_coexistence = 0;
- sys_config->answer_broadcast_ssid_probe = 0;
- sys_config->accept_all_data_frames = 0;
- sys_config->accept_non_directed_frames = 1;
- sys_config->exclude_unicast_unencrypted = 0;
- sys_config->disable_unicast_decryption = 1;
- sys_config->exclude_multicast_unencrypted = 0;
- sys_config->disable_multicast_decryption = 1;
- if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
- antenna = CFG_SYS_ANTENNA_BOTH;
- sys_config->antenna_diversity = antenna;
- sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
- sys_config->dot11g_auto_detection = 0;
- sys_config->enable_cts_to_self = 0;
- sys_config->bt_coexist_collision_thr = 0;
- sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */
- sys_config->silence_threshold = 0x1e;
-}
-
-static int ipw_net_open(struct net_device *dev)
-{
- IPW_DEBUG_INFO("dev->open\n");
- netif_start_queue(dev);
- return 0;
-}
-
-static int ipw_net_stop(struct net_device *dev)
-{
- IPW_DEBUG_INFO("dev->close\n");
- netif_stop_queue(dev);
- return 0;
-}
-
-/*
-todo:
-
-modify to send one tfd per fragment instead of using chunking. otherwise
-we need to heavily modify the ieee80211_skb_to_txb.
-*/
-
-static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
- int pri)
-{
- struct ieee80211_hdr_3addrqos *hdr = (struct ieee80211_hdr_3addrqos *)
- txb->fragments[0]->data;
- int i = 0;
- struct tfd_frame *tfd;
-#ifdef CONFIG_IPW2200_QOS
- int tx_id = ipw_get_tx_queue_number(priv, pri);
- struct clx2_tx_queue *txq = &priv->txq[tx_id];
-#else
- struct clx2_tx_queue *txq = &priv->txq[0];
-#endif
- struct clx2_queue *q = &txq->q;
- u8 id, hdr_len, unicast;
- u16 remaining_bytes;
- int fc;
-
- if (!(priv->status & STATUS_ASSOCIATED))
- goto drop;
-
- hdr_len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
- switch (priv->ieee->iw_mode) {
- case IW_MODE_ADHOC:
- unicast = !is_multicast_ether_addr(hdr->addr1);
- id = ipw_find_station(priv, hdr->addr1);
- if (id == IPW_INVALID_STATION) {
- id = ipw_add_station(priv, hdr->addr1);
- if (id == IPW_INVALID_STATION) {
- IPW_WARNING("Attempt to send data to "
- "invalid cell: " MAC_FMT "\n",
- hdr->addr1[0], hdr->addr1[1],
- hdr->addr1[2], hdr->addr1[3],
- hdr->addr1[4], hdr->addr1[5]);
- goto drop;
- }
- }
- break;
-
- case IW_MODE_INFRA:
- default:
- unicast = !is_multicast_ether_addr(hdr->addr3);
- id = 0;
- break;
- }
-
- tfd = &txq->bd[q->first_empty];
- txq->txb[q->first_empty] = txb;
- memset(tfd, 0, sizeof(*tfd));
- tfd->u.data.station_number = id;
-
- tfd->control_flags.message_type = TX_FRAME_TYPE;
- tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
-
- tfd->u.data.cmd_id = DINO_CMD_TX;
- tfd->u.data.len = cpu_to_le16(txb->payload_size);
- remaining_bytes = txb->payload_size;
-
- if (priv->assoc_request.ieee_mode == IPW_B_MODE)
- tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
- else
- tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
-
- if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
- tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
-
- fc = le16_to_cpu(hdr->frame_ctl);
- hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
-
- memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
-
- if (likely(unicast))
- tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
-
- if (txb->encrypted && !priv->ieee->host_encrypt) {
- switch (priv->ieee->sec.level) {
- case SEC_LEVEL_3:
- tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
- cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- /* XXX: ACK flag must be set for CCMP even if it
- * is a multicast/broadcast packet, because CCMP
- * group communication encrypted by GTK is
- * actually done by the AP. */
- if (!unicast)
- tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
-
- tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
- tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
- tfd->u.data.key_index = 0;
- tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
- break;
- case SEC_LEVEL_2:
- tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
- cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
- tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
- tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
- break;
- case SEC_LEVEL_1:
- tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
- cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- tfd->u.data.key_index = priv->ieee->tx_keyidx;
- if (priv->ieee->sec.key_sizes[priv->ieee->tx_keyidx] <=
- 40)
- tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
- else
- tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
- break;
- case SEC_LEVEL_0:
- break;
- default:
- printk(KERN_ERR "Unknow security level %d\n",
- priv->ieee->sec.level);
- break;
- }
- } else
- /* No hardware encryption */
- tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
-
-#ifdef CONFIG_IPW2200_QOS
- if (fc & IEEE80211_STYPE_QOS_DATA)
- ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
-#endif /* CONFIG_IPW2200_QOS */
-
- /* payload */
- tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
- txb->nr_frags));
- IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
- txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
- for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
- IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
- i, le32_to_cpu(tfd->u.data.num_chunks),
- txb->fragments[i]->len - hdr_len);
- IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
- i, tfd->u.data.num_chunks,
- txb->fragments[i]->len - hdr_len);
- printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
- txb->fragments[i]->len - hdr_len);
-
- tfd->u.data.chunk_ptr[i] =
- cpu_to_le32(pci_map_single
- (priv->pci_dev,
- txb->fragments[i]->data + hdr_len,
- txb->fragments[i]->len - hdr_len,
- PCI_DMA_TODEVICE));
- tfd->u.data.chunk_len[i] =
- cpu_to_le16(txb->fragments[i]->len - hdr_len);
- }
-
- if (i != txb->nr_frags) {
- struct sk_buff *skb;
- u16 remaining_bytes = 0;
- int j;
-
- for (j = i; j < txb->nr_frags; j++)
- remaining_bytes += txb->fragments[j]->len - hdr_len;
-
- printk(KERN_INFO "Trying to reallocate for %d bytes\n",
- remaining_bytes);
- skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
- if (skb != NULL) {
- tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
- for (j = i; j < txb->nr_frags; j++) {
- int size = txb->fragments[j]->len - hdr_len;
-
- printk(KERN_INFO "Adding frag %d %d...\n",
- j, size);
- memcpy(skb_put(skb, size),
- txb->fragments[j]->data + hdr_len, size);
- }
- dev_kfree_skb_any(txb->fragments[i]);
- txb->fragments[i] = skb;
- tfd->u.data.chunk_ptr[i] =
- cpu_to_le32(pci_map_single
- (priv->pci_dev, skb->data,
- remaining_bytes,
- PCI_DMA_TODEVICE));
-
- le32_add_cpu(&tfd->u.data.num_chunks, 1);
- }
- }
-
- /* kick DMA */
- q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
- ipw_write32(priv, q->reg_w, q->first_empty);
-
- if (ipw_tx_queue_space(q) < q->high_mark)
- netif_stop_queue(priv->net_dev);
-
- return NETDEV_TX_OK;
-
- drop:
- IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
- ieee80211_txb_free(txb);
- return NETDEV_TX_OK;
-}
-
-static int ipw_net_is_queue_full(struct net_device *dev, int pri)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-#ifdef CONFIG_IPW2200_QOS
- int tx_id = ipw_get_tx_queue_number(priv, pri);
- struct clx2_tx_queue *txq = &priv->txq[tx_id];
-#else
- struct clx2_tx_queue *txq = &priv->txq[0];
-#endif /* CONFIG_IPW2200_QOS */
-
- if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark)
- return 1;
-
- return 0;
-}
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
- struct ieee80211_txb *txb)
-{
- struct ieee80211_rx_stats dummystats;
- struct ieee80211_hdr *hdr;
- u8 n;
- u16 filter = priv->prom_priv->filter;
- int hdr_only = 0;
-
- if (filter & IPW_PROM_NO_TX)
- return;
-
- memset(&dummystats, 0, sizeof(dummystats));
-
- /* Filtering of fragment chains is done agains the first fragment */
- hdr = (void *)txb->fragments[0]->data;
- if (ieee80211_is_management(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_MGMT)
- return;
- if (filter & IPW_PROM_MGMT_HEADER_ONLY)
- hdr_only = 1;
- } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_CTL)
- return;
- if (filter & IPW_PROM_CTL_HEADER_ONLY)
- hdr_only = 1;
- } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_ctl))) {
- if (filter & IPW_PROM_NO_DATA)
- return;
- if (filter & IPW_PROM_DATA_HEADER_ONLY)
- hdr_only = 1;
- }
-
- for(n=0; n<txb->nr_frags; ++n) {
- struct sk_buff *src = txb->fragments[n];
- struct sk_buff *dst;
- struct ieee80211_radiotap_header *rt_hdr;
- int len;
-
- if (hdr_only) {
- hdr = (void *)src->data;
- len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
- } else
- len = src->len;
-
- dst = alloc_skb(
- len + IEEE80211_RADIOTAP_HDRLEN, GFP_ATOMIC);
- if (!dst) continue;
-
- rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr));
-
- rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
- rt_hdr->it_pad = 0;
- rt_hdr->it_present = 0; /* after all, it's just an idea */
- rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
-
- *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
- ieee80211chan2mhz(priv->channel));
- if (priv->channel > 14) /* 802.11a */
- *(__le16*)skb_put(dst, sizeof(u16)) =
- cpu_to_le16(IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_5GHZ);
- else if (priv->ieee->mode == IEEE_B) /* 802.11b */
- *(__le16*)skb_put(dst, sizeof(u16)) =
- cpu_to_le16(IEEE80211_CHAN_CCK |
- IEEE80211_CHAN_2GHZ);
- else /* 802.11g */
- *(__le16*)skb_put(dst, sizeof(u16)) =
- cpu_to_le16(IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_2GHZ);
-
- rt_hdr->it_len = cpu_to_le16(dst->len);
-
- skb_copy_from_linear_data(src, skb_put(dst, len), len);
-
- if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
- dev_kfree_skb_any(dst);
- }
-}
-#endif
-
-static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
- struct net_device *dev, int pri)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- unsigned long flags;
- int ret;
-
- IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
- spin_lock_irqsave(&priv->lock, flags);
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- if (rtap_iface && netif_running(priv->prom_net_dev))
- ipw_handle_promiscuous_tx(priv, txb);
-#endif
-
- ret = ipw_tx_skb(priv, txb, pri);
- if (ret == NETDEV_TX_OK)
- __ipw_led_activity_on(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return ret;
-}
-
-static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
-
- priv->ieee->stats.tx_packets = priv->tx_packets;
- priv->ieee->stats.rx_packets = priv->rx_packets;
- return &priv->ieee->stats;
-}
-
-static void ipw_net_set_multicast_list(struct net_device *dev)
-{
-
-}
-
-static int ipw_net_set_mac_address(struct net_device *dev, void *p)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- struct sockaddr *addr = p;
- DECLARE_MAC_BUF(mac);
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
- mutex_lock(&priv->mutex);
- priv->config |= CFG_CUSTOM_MAC;
- memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
- printk(KERN_INFO "%s: Setting MAC to %s\n",
- priv->net_dev->name, print_mac(mac, priv->mac_addr));
- queue_work(priv->workqueue, &priv->adapter_restart);
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-static void ipw_ethtool_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct ipw_priv *p = ieee80211_priv(dev);
- char vers[64];
- char date[32];
- u32 len;
-
- strcpy(info->driver, DRV_NAME);
- strcpy(info->version, DRV_VERSION);
-
- len = sizeof(vers);
- ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
- len = sizeof(date);
- ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
-
- snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
- vers, date);
- strcpy(info->bus_info, pci_name(p->pci_dev));
- info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
-}
-
-static u32 ipw_ethtool_get_link(struct net_device *dev)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- return (priv->status & STATUS_ASSOCIATED) != 0;
-}
-
-static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
-{
- return IPW_EEPROM_IMAGE_SIZE;
-}
-
-static int ipw_ethtool_get_eeprom(struct net_device *dev,
- struct ethtool_eeprom *eeprom, u8 * bytes)
-{
- struct ipw_priv *p = ieee80211_priv(dev);
-
- if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
- return -EINVAL;
- mutex_lock(&p->mutex);
- memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
- mutex_unlock(&p->mutex);
- return 0;
-}
-
-static int ipw_ethtool_set_eeprom(struct net_device *dev,
- struct ethtool_eeprom *eeprom, u8 * bytes)
-{
- struct ipw_priv *p = ieee80211_priv(dev);
- int i;
-
- if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
- return -EINVAL;
- mutex_lock(&p->mutex);
- memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
- for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
- ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]);
- mutex_unlock(&p->mutex);
- return 0;
-}
-
-static const struct ethtool_ops ipw_ethtool_ops = {
- .get_link = ipw_ethtool_get_link,
- .get_drvinfo = ipw_ethtool_get_drvinfo,
- .get_eeprom_len = ipw_ethtool_get_eeprom_len,
- .get_eeprom = ipw_ethtool_get_eeprom,
- .set_eeprom = ipw_ethtool_set_eeprom,
-};
-
-static irqreturn_t ipw_isr(int irq, void *data)
-{
- struct ipw_priv *priv = data;
- u32 inta, inta_mask;
-
- if (!priv)
- return IRQ_NONE;
-
- spin_lock(&priv->irq_lock);
-
- if (!(priv->status & STATUS_INT_ENABLED)) {
- /* IRQ is disabled */
- goto none;
- }
-
- inta = ipw_read32(priv, IPW_INTA_RW);
- inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
-
- if (inta == 0xFFFFFFFF) {
- /* Hardware disappeared */
- IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
- goto none;
- }
-
- if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
- /* Shared interrupt */
- goto none;
- }
-
- /* tell the device to stop sending interrupts */
- __ipw_disable_interrupts(priv);
-
- /* ack current interrupts */
- inta &= (IPW_INTA_MASK_ALL & inta_mask);
- ipw_write32(priv, IPW_INTA_RW, inta);
-
- /* Cache INTA value for our tasklet */
- priv->isr_inta = inta;
-
- tasklet_schedule(&priv->irq_tasklet);
-
- spin_unlock(&priv->irq_lock);
-
- return IRQ_HANDLED;
- none:
- spin_unlock(&priv->irq_lock);
- return IRQ_NONE;
-}
-
-static void ipw_rf_kill(void *adapter)
-{
- struct ipw_priv *priv = adapter;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (rf_kill_active(priv)) {
- IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
- if (priv->workqueue)
- queue_delayed_work(priv->workqueue,
- &priv->rf_kill, 2 * HZ);
- goto exit_unlock;
- }
-
- /* RF Kill is now disabled, so bring the device back up */
-
- if (!(priv->status & STATUS_RF_KILL_MASK)) {
- IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
- "device\n");
-
- /* we can not do an adapter restart while inside an irq lock */
- queue_work(priv->workqueue, &priv->adapter_restart);
- } else
- IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
- "enabled\n");
-
- exit_unlock:
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static void ipw_bg_rf_kill(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, rf_kill.work);
- mutex_lock(&priv->mutex);
- ipw_rf_kill(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_link_up(struct ipw_priv *priv)
-{
- priv->last_seq_num = -1;
- priv->last_frag_num = -1;
- priv->last_packet_time = 0;
-
- netif_carrier_on(priv->net_dev);
-
- cancel_delayed_work(&priv->request_scan);
- cancel_delayed_work(&priv->request_direct_scan);
- cancel_delayed_work(&priv->request_passive_scan);
- cancel_delayed_work(&priv->scan_event);
- ipw_reset_stats(priv);
- /* Ensure the rate is updated immediately */
- priv->last_rate = ipw_get_current_rate(priv);
- ipw_gather_stats(priv);
- ipw_led_link_up(priv);
- notify_wx_assoc_event(priv);
-
- if (priv->config & CFG_BACKGROUND_SCAN)
- queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
-}
-
-static void ipw_bg_link_up(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, link_up);
- mutex_lock(&priv->mutex);
- ipw_link_up(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_link_down(struct ipw_priv *priv)
-{
- ipw_led_link_down(priv);
- netif_carrier_off(priv->net_dev);
- notify_wx_assoc_event(priv);
-
- /* Cancel any queued work ... */
- cancel_delayed_work(&priv->request_scan);
- cancel_delayed_work(&priv->request_direct_scan);
- cancel_delayed_work(&priv->request_passive_scan);
- cancel_delayed_work(&priv->adhoc_check);
- cancel_delayed_work(&priv->gather_stats);
-
- ipw_reset_stats(priv);
-
- if (!(priv->status & STATUS_EXIT_PENDING)) {
- /* Queue up another scan... */
- queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
- } else
- cancel_delayed_work(&priv->scan_event);
-}
-
-static void ipw_bg_link_down(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, link_down);
- mutex_lock(&priv->mutex);
- ipw_link_down(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv)
-{
- int ret = 0;
-
- priv->workqueue = create_workqueue(DRV_NAME);
- init_waitqueue_head(&priv->wait_command_queue);
- init_waitqueue_head(&priv->wait_state);
-
- INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check);
- INIT_WORK(&priv->associate, ipw_bg_associate);
- INIT_WORK(&priv->disassociate, ipw_bg_disassociate);
- INIT_WORK(&priv->system_config, ipw_system_config);
- INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish);
- INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart);
- INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill);
- INIT_WORK(&priv->up, ipw_bg_up);
- INIT_WORK(&priv->down, ipw_bg_down);
- INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
- INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
- INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
- INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
- INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
- INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
- INIT_WORK(&priv->roam, ipw_bg_roam);
- INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check);
- INIT_WORK(&priv->link_up, ipw_bg_link_up);
- INIT_WORK(&priv->link_down, ipw_bg_link_down);
- INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on);
- INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off);
- INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off);
- INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network);
-
-#ifdef CONFIG_IPW2200_QOS
- INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate);
-#endif /* CONFIG_IPW2200_QOS */
-
- tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
- ipw_irq_tasklet, (unsigned long)priv);
-
- return ret;
-}
-
-static void shim__set_security(struct net_device *dev,
- struct ieee80211_security *sec)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- int i;
- for (i = 0; i < 4; i++) {
- if (sec->flags & (1 << i)) {
- priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
- priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
- if (sec->key_sizes[i] == 0)
- priv->ieee->sec.flags &= ~(1 << i);
- else {
- memcpy(priv->ieee->sec.keys[i], sec->keys[i],
- sec->key_sizes[i]);
- priv->ieee->sec.flags |= (1 << i);
- }
- priv->status |= STATUS_SECURITY_UPDATED;
- } else if (sec->level != SEC_LEVEL_1)
- priv->ieee->sec.flags &= ~(1 << i);
- }
-
- if (sec->flags & SEC_ACTIVE_KEY) {
- if (sec->active_key <= 3) {
- priv->ieee->sec.active_key = sec->active_key;
- priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
- } else
- priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
- priv->status |= STATUS_SECURITY_UPDATED;
- } else
- priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
-
- if ((sec->flags & SEC_AUTH_MODE) &&
- (priv->ieee->sec.auth_mode != sec->auth_mode)) {
- priv->ieee->sec.auth_mode = sec->auth_mode;
- priv->ieee->sec.flags |= SEC_AUTH_MODE;
- if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
- priv->capability |= CAP_SHARED_KEY;
- else
- priv->capability &= ~CAP_SHARED_KEY;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
- priv->ieee->sec.flags |= SEC_ENABLED;
- priv->ieee->sec.enabled = sec->enabled;
- priv->status |= STATUS_SECURITY_UPDATED;
- if (sec->enabled)
- priv->capability |= CAP_PRIVACY_ON;
- else
- priv->capability &= ~CAP_PRIVACY_ON;
- }
-
- if (sec->flags & SEC_ENCRYPT)
- priv->ieee->sec.encrypt = sec->encrypt;
-
- if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
- priv->ieee->sec.level = sec->level;
- priv->ieee->sec.flags |= SEC_LEVEL;
- priv->status |= STATUS_SECURITY_UPDATED;
- }
-
- if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
- ipw_set_hwcrypto_keys(priv);
-
- /* To match current functionality of ipw2100 (which works well w/
- * various supplicants, we don't force a disassociate if the
- * privacy capability changes ... */
-#if 0
- if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
- (((priv->assoc_request.capability &
- cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) ||
- (!(priv->assoc_request.capability &
- cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) {
- IPW_DEBUG_ASSOC("Disassociating due to capability "
- "change.\n");
- ipw_disassociate(priv);
- }
-#endif
-}
-
-static int init_supported_rates(struct ipw_priv *priv,
- struct ipw_supported_rates *rates)
-{
- /* TODO: Mask out rates based on priv->rates_mask */
-
- memset(rates, 0, sizeof(*rates));
- /* configure supported rates */
- switch (priv->ieee->freq_band) {
- case IEEE80211_52GHZ_BAND:
- rates->ieee_mode = IPW_A_MODE;
- rates->purpose = IPW_RATE_CAPABILITIES;
- ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
- IEEE80211_OFDM_DEFAULT_RATES_MASK);
- break;
-
- default: /* Mixed or 2.4Ghz */
- rates->ieee_mode = IPW_G_MODE;
- rates->purpose = IPW_RATE_CAPABILITIES;
- ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
- IEEE80211_CCK_DEFAULT_RATES_MASK);
- if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
- ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
- IEEE80211_OFDM_DEFAULT_RATES_MASK);
- }
- break;
- }
-
- return 0;
-}
-
-static int ipw_config(struct ipw_priv *priv)
-{
- /* This is only called from ipw_up, which resets/reloads the firmware
- so, we don't need to first disable the card before we configure
- it */
- if (ipw_set_tx_power(priv))
- goto error;
-
- /* initialize adapter address */
- if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
- goto error;
-
- /* set basic system config settings */
- init_sys_config(&priv->sys_config);
-
- /* Support Bluetooth if we have BT h/w on board, and user wants to.
- * Does not support BT priority yet (don't abort or defer our Tx) */
- if (bt_coexist) {
- unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
-
- if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
- priv->sys_config.bt_coexistence
- |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
- if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
- priv->sys_config.bt_coexistence
- |= CFG_BT_COEXISTENCE_OOB;
- }
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
- priv->sys_config.accept_all_data_frames = 1;
- priv->sys_config.accept_non_directed_frames = 1;
- priv->sys_config.accept_all_mgmt_bcpr = 1;
- priv->sys_config.accept_all_mgmt_frames = 1;
- }
-#endif
-
- if (priv->ieee->iw_mode == IW_MODE_ADHOC)
- priv->sys_config.answer_broadcast_ssid_probe = 1;
- else
- priv->sys_config.answer_broadcast_ssid_probe = 0;
-
- if (ipw_send_system_config(priv))
- goto error;
-
- init_supported_rates(priv, &priv->rates);
- if (ipw_send_supported_rates(priv, &priv->rates))
- goto error;
-
- /* Set request-to-send threshold */
- if (priv->rts_threshold) {
- if (ipw_send_rts_threshold(priv, priv->rts_threshold))
- goto error;
- }
-#ifdef CONFIG_IPW2200_QOS
- IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
- ipw_qos_activate(priv, NULL);
-#endif /* CONFIG_IPW2200_QOS */
-
- if (ipw_set_random_seed(priv))
- goto error;
-
- /* final state transition to the RUN state */
- if (ipw_send_host_complete(priv))
- goto error;
-
- priv->status |= STATUS_INIT;
-
- ipw_led_init(priv);
- ipw_led_radio_on(priv);
- priv->notif_missed_beacons = 0;
-
- /* Set hardware WEP key if it is configured. */
- if ((priv->capability & CAP_PRIVACY_ON) &&
- (priv->ieee->sec.level == SEC_LEVEL_1) &&
- !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
- ipw_set_hwcrypto_keys(priv);
-
- return 0;
-
- error:
- return -EIO;
-}
-
-/*
- * NOTE:
- *
- * These tables have been tested in conjunction with the
- * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
- *
- * Altering this values, using it on other hardware, or in geographies
- * not intended for resale of the above mentioned Intel adapters has
- * not been tested.
- *
- * Remember to update the table in README.ipw2200 when changing this
- * table.
- *
- */
-static const struct ieee80211_geo ipw_geos[] = {
- { /* Restricted */
- "---",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- },
-
- { /* Custom US/Canada */
- "ZZF",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 8,
- .a = {{5180, 36},
- {5200, 40},
- {5220, 44},
- {5240, 48},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
- },
-
- { /* Rest of World */
- "ZZD",
- .bg_channels = 13,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}},
- },
-
- { /* Custom USA & Europe & High */
- "ZZA",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 13,
- .a = {{5180, 36},
- {5200, 40},
- {5220, 44},
- {5240, 48},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
- {5745, 149},
- {5765, 153},
- {5785, 157},
- {5805, 161},
- {5825, 165}},
- },
-
- { /* Custom NA & Europe */
- "ZZB",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 13,
- .a = {{5180, 36},
- {5200, 40},
- {5220, 44},
- {5240, 48},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
- {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
- {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
- {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
- {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
- {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
- },
-
- { /* Custom Japan */
- "ZZC",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 4,
- .a = {{5170, 34}, {5190, 38},
- {5210, 42}, {5230, 46}},
- },
-
- { /* Custom */
- "ZZM",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- },
-
- { /* Europe */
- "ZZE",
- .bg_channels = 13,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}},
- .a_channels = 19,
- .a = {{5180, 36},
- {5200, 40},
- {5220, 44},
- {5240, 48},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
- {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
- {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
- {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
- {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
- {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
- {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
- {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
- {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
- {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
- {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
- {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
- },
-
- { /* Custom Japan */
- "ZZJ",
- .bg_channels = 14,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
- .a_channels = 4,
- .a = {{5170, 34}, {5190, 38},
- {5210, 42}, {5230, 46}},
- },
-
- { /* Rest of World */
- "ZZR",
- .bg_channels = 14,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}, {2467, 12},
- {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY |
- IEEE80211_CH_PASSIVE_ONLY}},
- },
-
- { /* High Band */
- "ZZH",
- .bg_channels = 13,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11},
- {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
- {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
- .a_channels = 4,
- .a = {{5745, 149}, {5765, 153},
- {5785, 157}, {5805, 161}},
- },
-
- { /* Custom Europe */
- "ZZG",
- .bg_channels = 13,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11},
- {2467, 12}, {2472, 13}},
- .a_channels = 4,
- .a = {{5180, 36}, {5200, 40},
- {5220, 44}, {5240, 48}},
- },
-
- { /* Europe */
- "ZZK",
- .bg_channels = 13,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11},
- {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
- {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
- .a_channels = 24,
- .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
- {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
- {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
- {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
- {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
- {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
- {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
- {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
- {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
- {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
- {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
- {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
- {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
- {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
- {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
- {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
- {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
- {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
- {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
- {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
- },
-
- { /* Europe */
- "ZZL",
- .bg_channels = 11,
- .bg = {{2412, 1}, {2417, 2}, {2422, 3},
- {2427, 4}, {2432, 5}, {2437, 6},
- {2442, 7}, {2447, 8}, {2452, 9},
- {2457, 10}, {2462, 11}},
- .a_channels = 13,
- .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
- {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
- {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
- {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
- {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
- {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
- {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
- {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
- {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
- {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
- {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
- {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
- {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
- }
-};
-
-#define MAX_HW_RESTARTS 5
-static int ipw_up(struct ipw_priv *priv)
-{
- int rc, i, j;
-
- if (priv->status & STATUS_EXIT_PENDING)
- return -EIO;
-
- if (cmdlog && !priv->cmdlog) {
- priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog),
- GFP_KERNEL);
- if (priv->cmdlog == NULL) {
- IPW_ERROR("Error allocating %d command log entries.\n",
- cmdlog);
- return -ENOMEM;
- } else {
- priv->cmdlog_len = cmdlog;
- }
- }
-
- for (i = 0; i < MAX_HW_RESTARTS; i++) {
- /* Load the microcode, firmware, and eeprom.
- * Also start the clocks. */
- rc = ipw_load(priv);
- if (rc) {
- IPW_ERROR("Unable to load firmware: %d\n", rc);
- return rc;
- }
-
- ipw_init_ordinals(priv);
- if (!(priv->config & CFG_CUSTOM_MAC))
- eeprom_parse_mac(priv, priv->mac_addr);
- memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
-
- for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
- if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
- ipw_geos[j].name, 3))
- break;
- }
- if (j == ARRAY_SIZE(ipw_geos)) {
- IPW_WARNING("SKU [%c%c%c] not recognized.\n",
- priv->eeprom[EEPROM_COUNTRY_CODE + 0],
- priv->eeprom[EEPROM_COUNTRY_CODE + 1],
- priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
- j = 0;
- }
- if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) {
- IPW_WARNING("Could not set geography.");
- return 0;
- }
-
- if (priv->status & STATUS_RF_KILL_SW) {
- IPW_WARNING("Radio disabled by module parameter.\n");
- return 0;
- } else if (rf_kill_active(priv)) {
- IPW_WARNING("Radio Frequency Kill Switch is On:\n"
- "Kill switch must be turned off for "
- "wireless networking to work.\n");
- queue_delayed_work(priv->workqueue, &priv->rf_kill,
- 2 * HZ);
- return 0;
- }
-
- rc = ipw_config(priv);
- if (!rc) {
- IPW_DEBUG_INFO("Configured device on count %i\n", i);
-
- /* If configure to try and auto-associate, kick
- * off a scan. */
- queue_delayed_work(priv->workqueue,
- &priv->request_scan, 0);
-
- return 0;
- }
-
- IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
- IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
- i, MAX_HW_RESTARTS);
-
- /* We had an error bringing up the hardware, so take it
- * all the way back down so we can try again */
- ipw_down(priv);
- }
-
- /* tried to restart and config the device for as long as our
- * patience could withstand */
- IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
-
- return -EIO;
-}
-
-static void ipw_bg_up(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, up);
- mutex_lock(&priv->mutex);
- ipw_up(priv);
- mutex_unlock(&priv->mutex);
-}
-
-static void ipw_deinit(struct ipw_priv *priv)
-{
- int i;
-
- if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
- ipw_abort_scan(priv);
- }
-
- if (priv->status & STATUS_ASSOCIATED) {
- IPW_DEBUG_INFO("Disassociating during shutdown.\n");
- ipw_disassociate(priv);
- }
-
- ipw_led_shutdown(priv);
-
- /* Wait up to 1s for status to change to not scanning and not
- * associated (disassociation can take a while for a ful 802.11
- * exchange */
- for (i = 1000; i && (priv->status &
- (STATUS_DISASSOCIATING |
- STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
- udelay(10);
-
- if (priv->status & (STATUS_DISASSOCIATING |
- STATUS_ASSOCIATED | STATUS_SCANNING))
- IPW_DEBUG_INFO("Still associated or scanning...\n");
- else
- IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
-
- /* Attempt to disable the card */
- ipw_send_card_disable(priv, 0);
-
- priv->status &= ~STATUS_INIT;
-}
-
-static void ipw_down(struct ipw_priv *priv)
-{
- int exit_pending = priv->status & STATUS_EXIT_PENDING;
-
- priv->status |= STATUS_EXIT_PENDING;
-
- if (ipw_is_init(priv))
- ipw_deinit(priv);
-
- /* Wipe out the EXIT_PENDING status bit if we are not actually
- * exiting the module */
- if (!exit_pending)
- priv->status &= ~STATUS_EXIT_PENDING;
-
- /* tell the device to stop sending interrupts */
- ipw_disable_interrupts(priv);
-
- /* Clear all bits but the RF Kill */
- priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
- netif_carrier_off(priv->net_dev);
-
- ipw_stop_nic(priv);
-
- ipw_led_radio_off(priv);
-}
-
-static void ipw_bg_down(struct work_struct *work)
-{
- struct ipw_priv *priv =
- container_of(work, struct ipw_priv, down);
- mutex_lock(&priv->mutex);
- ipw_down(priv);
- mutex_unlock(&priv->mutex);
-}
-
-/* Called by register_netdev() */
-static int ipw_net_init(struct net_device *dev)
-{
- struct ipw_priv *priv = ieee80211_priv(dev);
- mutex_lock(&priv->mutex);
-
- if (ipw_up(priv)) {
- mutex_unlock(&priv->mutex);
- return -EIO;
- }
-
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
-/* PCI driver stuff */
-static struct pci_device_id card_ids[] = {
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
- {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
- {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
- {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
-
- /* required last entry */
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, card_ids);
-
-static struct attribute *ipw_sysfs_entries[] = {
- &dev_attr_rf_kill.attr,
- &dev_attr_direct_dword.attr,
- &dev_attr_indirect_byte.attr,
- &dev_attr_indirect_dword.attr,
- &dev_attr_mem_gpio_reg.attr,
- &dev_attr_command_event_reg.attr,
- &dev_attr_nic_type.attr,
- &dev_attr_status.attr,
- &dev_attr_cfg.attr,
- &dev_attr_error.attr,
- &dev_attr_event_log.attr,
- &dev_attr_cmd_log.attr,
- &dev_attr_eeprom_delay.attr,
- &dev_attr_ucode_version.attr,
- &dev_attr_rtc.attr,
- &dev_attr_scan_age.attr,
- &dev_attr_led.attr,
- &dev_attr_speed_scan.attr,
- &dev_attr_net_stats.attr,
- &dev_attr_channels.attr,
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- &dev_attr_rtap_iface.attr,
- &dev_attr_rtap_filter.attr,
-#endif
- NULL
-};
-
-static struct attribute_group ipw_attribute_group = {
- .name = NULL, /* put in device directory */
- .attrs = ipw_sysfs_entries,
-};
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-static int ipw_prom_open(struct net_device *dev)
-{
- struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
- struct ipw_priv *priv = prom_priv->priv;
-
- IPW_DEBUG_INFO("prom dev->open\n");
- netif_carrier_off(dev);
-
- if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
- priv->sys_config.accept_all_data_frames = 1;
- priv->sys_config.accept_non_directed_frames = 1;
- priv->sys_config.accept_all_mgmt_bcpr = 1;
- priv->sys_config.accept_all_mgmt_frames = 1;
-
- ipw_send_system_config(priv);
- }
-
- return 0;
-}
-
-static int ipw_prom_stop(struct net_device *dev)
-{
- struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
- struct ipw_priv *priv = prom_priv->priv;
-
- IPW_DEBUG_INFO("prom dev->stop\n");
-
- if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
- priv->sys_config.accept_all_data_frames = 0;
- priv->sys_config.accept_non_directed_frames = 0;
- priv->sys_config.accept_all_mgmt_bcpr = 0;
- priv->sys_config.accept_all_mgmt_frames = 0;
-
- ipw_send_system_config(priv);
- }
-
- return 0;
-}
-
-static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- IPW_DEBUG_INFO("prom dev->xmit\n");
- return -EOPNOTSUPP;
-}
-
-static struct net_device_stats *ipw_prom_get_stats(struct net_device *dev)
-{
- struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
- return &prom_priv->ieee->stats;
-}
-
-static int ipw_prom_alloc(struct ipw_priv *priv)
-{
- int rc = 0;
-
- if (priv->prom_net_dev)
- return -EPERM;
-
- priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv));
- if (priv->prom_net_dev == NULL)
- return -ENOMEM;
-
- priv->prom_priv = ieee80211_priv(priv->prom_net_dev);
- priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
- priv->prom_priv->priv = priv;
-
- strcpy(priv->prom_net_dev->name, "rtap%d");
- memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
-
- priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
- priv->prom_net_dev->open = ipw_prom_open;
- priv->prom_net_dev->stop = ipw_prom_stop;
- priv->prom_net_dev->get_stats = ipw_prom_get_stats;
- priv->prom_net_dev->hard_start_xmit = ipw_prom_hard_start_xmit;
-
- priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
- SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev);
-
- rc = register_netdev(priv->prom_net_dev);
- if (rc) {
- free_ieee80211(priv->prom_net_dev);
- priv->prom_net_dev = NULL;
- return rc;
- }
-
- return 0;
-}
-
-static void ipw_prom_free(struct ipw_priv *priv)
-{
- if (!priv->prom_net_dev)
- return;
-
- unregister_netdev(priv->prom_net_dev);
- free_ieee80211(priv->prom_net_dev);
-
- priv->prom_net_dev = NULL;
-}
-
-#endif
-
-
-static int __devinit ipw_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- int err = 0;
- struct net_device *net_dev;
- void __iomem *base;
- u32 length, val;
- struct ipw_priv *priv;
- int i;
-
- net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
- if (net_dev == NULL) {
- err = -ENOMEM;
- goto out;
- }
-
- priv = ieee80211_priv(net_dev);
- priv->ieee = netdev_priv(net_dev);
-
- priv->net_dev = net_dev;
- priv->pci_dev = pdev;
- ipw_debug_level = debug;
- spin_lock_init(&priv->irq_lock);
- spin_lock_init(&priv->lock);
- for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
- INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
-
- mutex_init(&priv->mutex);
- if (pci_enable_device(pdev)) {
- err = -ENODEV;
- goto out_free_ieee80211;
- }
-
- pci_set_master(pdev);
-
- err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
- if (!err)
- err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
- if (err) {
- printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
- goto out_pci_disable_device;
- }
-
- pci_set_drvdata(pdev, priv);
-
- err = pci_request_regions(pdev, DRV_NAME);
- if (err)
- goto out_pci_disable_device;
-
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_read_config_dword(pdev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
- length = pci_resource_len(pdev, 0);
- priv->hw_len = length;
-
- base = ioremap_nocache(pci_resource_start(pdev, 0), length);
- if (!base) {
- err = -ENODEV;
- goto out_pci_release_regions;
- }
-
- priv->hw_base = base;
- IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
- IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
-
- err = ipw_setup_deferred_work(priv);
- if (err) {
- IPW_ERROR("Unable to setup deferred work\n");
- goto out_iounmap;
- }
-
- ipw_sw_reset(priv, 1);
-
- err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv);
- if (err) {
- IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
- goto out_destroy_workqueue;
- }
-
- SET_NETDEV_DEV(net_dev, &pdev->dev);
-
- mutex_lock(&priv->mutex);
-
- priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
- priv->ieee->set_security = shim__set_security;
- priv->ieee->is_queue_full = ipw_net_is_queue_full;
-
-#ifdef CONFIG_IPW2200_QOS
- priv->ieee->is_qos_active = ipw_is_qos_active;
- priv->ieee->handle_probe_response = ipw_handle_beacon;
- priv->ieee->handle_beacon = ipw_handle_probe_response;
- priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
-#endif /* CONFIG_IPW2200_QOS */
-
- priv->ieee->perfect_rssi = -20;
- priv->ieee->worst_rssi = -85;
-
- net_dev->open = ipw_net_open;
- net_dev->stop = ipw_net_stop;
- net_dev->init = ipw_net_init;
- net_dev->get_stats = ipw_net_get_stats;
- net_dev->set_multicast_list = ipw_net_set_multicast_list;
- net_dev->set_mac_address = ipw_net_set_mac_address;
- priv->wireless_data.spy_data = &priv->ieee->spy_data;
- net_dev->wireless_data = &priv->wireless_data;
- net_dev->wireless_handlers = &ipw_wx_handler_def;
- net_dev->ethtool_ops = &ipw_ethtool_ops;
- net_dev->irq = pdev->irq;
- net_dev->base_addr = (unsigned long)priv->hw_base;
- net_dev->mem_start = pci_resource_start(pdev, 0);
- net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
-
- err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
- if (err) {
- IPW_ERROR("failed to create sysfs device attributes\n");
- mutex_unlock(&priv->mutex);
- goto out_release_irq;
- }
-
- mutex_unlock(&priv->mutex);
- err = register_netdev(net_dev);
- if (err) {
- IPW_ERROR("failed to register network device\n");
- goto out_remove_sysfs;
- }
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- if (rtap_iface) {
- err = ipw_prom_alloc(priv);
- if (err) {
- IPW_ERROR("Failed to register promiscuous network "
- "device (error %d).\n", err);
- unregister_netdev(priv->net_dev);
- goto out_remove_sysfs;
- }
- }
-#endif
-
- printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
- "channels, %d 802.11a channels)\n",
- priv->ieee->geo.name, priv->ieee->geo.bg_channels,
- priv->ieee->geo.a_channels);
-
- return 0;
-
- out_remove_sysfs:
- sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
- out_release_irq:
- free_irq(pdev->irq, priv);
- out_destroy_workqueue:
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
- out_iounmap:
- iounmap(priv->hw_base);
- out_pci_release_regions:
- pci_release_regions(pdev);
- out_pci_disable_device:
- pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
- out_free_ieee80211:
- free_ieee80211(priv->net_dev);
- out:
- return err;
-}
-
-static void __devexit ipw_pci_remove(struct pci_dev *pdev)
-{
- struct ipw_priv *priv = pci_get_drvdata(pdev);
- struct list_head *p, *q;
- int i;
-
- if (!priv)
- return;
-
- mutex_lock(&priv->mutex);
-
- priv->status |= STATUS_EXIT_PENDING;
- ipw_down(priv);
- sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
-
- mutex_unlock(&priv->mutex);
-
- unregister_netdev(priv->net_dev);
-
- if (priv->rxq) {
- ipw_rx_queue_free(priv, priv->rxq);
- priv->rxq = NULL;
- }
- ipw_tx_queue_free(priv);
-
- if (priv->cmdlog) {
- kfree(priv->cmdlog);
- priv->cmdlog = NULL;
- }
- /* ipw_down will ensure that there is no more pending work
- * in the workqueue's, so we can safely remove them now. */
- cancel_delayed_work(&priv->adhoc_check);
- cancel_delayed_work(&priv->gather_stats);
- cancel_delayed_work(&priv->request_scan);
- cancel_delayed_work(&priv->request_direct_scan);
- cancel_delayed_work(&priv->request_passive_scan);
- cancel_delayed_work(&priv->scan_event);
- cancel_delayed_work(&priv->rf_kill);
- cancel_delayed_work(&priv->scan_check);
- destroy_workqueue(priv->workqueue);
- priv->workqueue = NULL;
-
- /* Free MAC hash list for ADHOC */
- for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
- list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
- list_del(p);
- kfree(list_entry(p, struct ipw_ibss_seq, list));
- }
- }
-
- kfree(priv->error);
- priv->error = NULL;
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- ipw_prom_free(priv);
-#endif
-
- free_irq(pdev->irq, priv);
- iounmap(priv->hw_base);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
- free_ieee80211(priv->net_dev);
- free_firmware();
-}
-
-#ifdef CONFIG_PM
-static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct ipw_priv *priv = pci_get_drvdata(pdev);
- struct net_device *dev = priv->net_dev;
-
- printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
-
- /* Take down the device; powers it off, etc. */
- ipw_down(priv);
-
- /* Remove the PRESENT state of the device */
- netif_device_detach(dev);
-
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
- return 0;
-}
-
-static int ipw_pci_resume(struct pci_dev *pdev)
-{
- struct ipw_priv *priv = pci_get_drvdata(pdev);
- struct net_device *dev = priv->net_dev;
- int err;
- u32 val;
-
- printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
-
- pci_set_power_state(pdev, PCI_D0);
- err = pci_enable_device(pdev);
- if (err) {
- printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
- dev->name);
- return err;
- }
- pci_restore_state(pdev);
-
- /*
- * Suspend/Resume resets the PCI configuration space, so we have to
- * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
- * from interfering with C3 CPU state. pci_restore_state won't help
- * here since it only restores the first 64 bytes pci config header.
- */
- pci_read_config_dword(pdev, 0x40, &val);
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
-
- /* Set the device back into the PRESENT state; this will also wake
- * the queue of needed */
- netif_device_attach(dev);
-
- /* Bring the device back up */
- queue_work(priv->workqueue, &priv->up);
-
- return 0;
-}
-#endif
-
-static void ipw_pci_shutdown(struct pci_dev *pdev)
-{
- struct ipw_priv *priv = pci_get_drvdata(pdev);
-
- /* Take down the device; powers it off, etc. */
- ipw_down(priv);
-
- pci_disable_device(pdev);
-}
-
-/* driver initialization stuff */
-static struct pci_driver ipw_driver = {
- .name = DRV_NAME,
- .id_table = card_ids,
- .probe = ipw_pci_probe,
- .remove = __devexit_p(ipw_pci_remove),
-#ifdef CONFIG_PM
- .suspend = ipw_pci_suspend,
- .resume = ipw_pci_resume,
-#endif
- .shutdown = ipw_pci_shutdown,
-};
-
-static int __init ipw_init(void)
-{
- int ret;
-
- printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
- printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
-
- ret = pci_register_driver(&ipw_driver);
- if (ret) {
- IPW_ERROR("Unable to initialize PCI module\n");
- return ret;
- }
-
- ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
- if (ret) {
- IPW_ERROR("Unable to create driver sysfs file\n");
- pci_unregister_driver(&ipw_driver);
- return ret;
- }
-
- return ret;
-}
-
-static void __exit ipw_exit(void)
-{
- driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
- pci_unregister_driver(&ipw_driver);
-}
-
-module_param(disable, int, 0444);
-MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
-
-module_param(associate, int, 0444);
-MODULE_PARM_DESC(associate, "auto associate when scanning (default on)");
-
-module_param(auto_create, int, 0444);
-MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
-
-module_param(led, int, 0444);
-MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)");
-
-module_param(debug, int, 0444);
-MODULE_PARM_DESC(debug, "debug output mask");
-
-module_param(channel, int, 0444);
-MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-module_param(rtap_iface, int, 0444);
-MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
-#endif
-
-#ifdef CONFIG_IPW2200_QOS
-module_param(qos_enable, int, 0444);
-MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
-
-module_param(qos_burst_enable, int, 0444);
-MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
-
-module_param(qos_no_ack_mask, int, 0444);
-MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
-
-module_param(burst_duration_CCK, int, 0444);
-MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
-
-module_param(burst_duration_OFDM, int, 0444);
-MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
-#endif /* CONFIG_IPW2200_QOS */
-
-#ifdef CONFIG_IPW2200_MONITOR
-module_param(mode, int, 0444);
-MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
-#else
-module_param(mode, int, 0444);
-MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
-#endif
-
-module_param(bt_coexist, int, 0444);
-MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
-
-module_param(hwcrypto, int, 0444);
-MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)");
-
-module_param(cmdlog, int, 0444);
-MODULE_PARM_DESC(cmdlog,
- "allocate a ring buffer for logging firmware commands");
-
-module_param(roaming, int, 0444);
-MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
-
-module_param(antenna, int, 0444);
-MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
-
-module_exit(ipw_exit);
-module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
deleted file mode 100644
index 0bad1ec3e7e0..000000000000
--- a/drivers/net/wireless/ipw2200.h
+++ /dev/null
@@ -1,2010 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
-
-#ifndef __ipw2200_h__
-#define __ipw2200_h__
-
-#define WEXT_USECHANNELS 1
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/random.h>
-#include <linux/dma-mapping.h>
-
-#include <linux/firmware.h>
-#include <linux/wireless.h>
-#include <linux/jiffies.h>
-#include <asm/io.h>
-
-#include <net/ieee80211.h>
-#include <net/ieee80211_radiotap.h>
-
-#define DRV_NAME "ipw2200"
-
-#include <linux/workqueue.h>
-
-/* Authentication and Association States */
-enum connection_manager_assoc_states {
- CMAS_INIT = 0,
- CMAS_TX_AUTH_SEQ_1,
- CMAS_RX_AUTH_SEQ_2,
- CMAS_AUTH_SEQ_1_PASS,
- CMAS_AUTH_SEQ_1_FAIL,
- CMAS_TX_AUTH_SEQ_3,
- CMAS_RX_AUTH_SEQ_4,
- CMAS_AUTH_SEQ_2_PASS,
- CMAS_AUTH_SEQ_2_FAIL,
- CMAS_AUTHENTICATED,
- CMAS_TX_ASSOC,
- CMAS_RX_ASSOC_RESP,
- CMAS_ASSOCIATED,
- CMAS_LAST
-};
-
-#define IPW_WAIT (1<<0)
-#define IPW_QUIET (1<<1)
-#define IPW_ROAMING (1<<2)
-
-#define IPW_POWER_MODE_CAM 0x00 //(always on)
-#define IPW_POWER_INDEX_1 0x01
-#define IPW_POWER_INDEX_2 0x02
-#define IPW_POWER_INDEX_3 0x03
-#define IPW_POWER_INDEX_4 0x04
-#define IPW_POWER_INDEX_5 0x05
-#define IPW_POWER_AC 0x06
-#define IPW_POWER_BATTERY 0x07
-#define IPW_POWER_LIMIT 0x07
-#define IPW_POWER_MASK 0x0F
-#define IPW_POWER_ENABLED 0x10
-#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK)
-
-#define IPW_CMD_HOST_COMPLETE 2
-#define IPW_CMD_POWER_DOWN 4
-#define IPW_CMD_SYSTEM_CONFIG 6
-#define IPW_CMD_MULTICAST_ADDRESS 7
-#define IPW_CMD_SSID 8
-#define IPW_CMD_ADAPTER_ADDRESS 11
-#define IPW_CMD_PORT_TYPE 12
-#define IPW_CMD_RTS_THRESHOLD 15
-#define IPW_CMD_FRAG_THRESHOLD 16
-#define IPW_CMD_POWER_MODE 17
-#define IPW_CMD_WEP_KEY 18
-#define IPW_CMD_TGI_TX_KEY 19
-#define IPW_CMD_SCAN_REQUEST 20
-#define IPW_CMD_ASSOCIATE 21
-#define IPW_CMD_SUPPORTED_RATES 22
-#define IPW_CMD_SCAN_ABORT 23
-#define IPW_CMD_TX_FLUSH 24
-#define IPW_CMD_QOS_PARAMETERS 25
-#define IPW_CMD_SCAN_REQUEST_EXT 26
-#define IPW_CMD_DINO_CONFIG 30
-#define IPW_CMD_RSN_CAPABILITIES 31
-#define IPW_CMD_RX_KEY 32
-#define IPW_CMD_CARD_DISABLE 33
-#define IPW_CMD_SEED_NUMBER 34
-#define IPW_CMD_TX_POWER 35
-#define IPW_CMD_COUNTRY_INFO 36
-#define IPW_CMD_AIRONET_INFO 37
-#define IPW_CMD_AP_TX_POWER 38
-#define IPW_CMD_CCKM_INFO 39
-#define IPW_CMD_CCX_VER_INFO 40
-#define IPW_CMD_SET_CALIBRATION 41
-#define IPW_CMD_SENSITIVITY_CALIB 42
-#define IPW_CMD_RETRY_LIMIT 51
-#define IPW_CMD_IPW_PRE_POWER_DOWN 58
-#define IPW_CMD_VAP_BEACON_TEMPLATE 60
-#define IPW_CMD_VAP_DTIM_PERIOD 61
-#define IPW_CMD_EXT_SUPPORTED_RATES 62
-#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63
-#define IPW_CMD_VAP_QUIET_INTERVALS 64
-#define IPW_CMD_VAP_CHANNEL_SWITCH 65
-#define IPW_CMD_VAP_MANDATORY_CHANNELS 66
-#define IPW_CMD_VAP_CELL_PWR_LIMIT 67
-#define IPW_CMD_VAP_CF_PARAM_SET 68
-#define IPW_CMD_VAP_SET_BEACONING_STATE 69
-#define IPW_CMD_MEASUREMENT 80
-#define IPW_CMD_POWER_CAPABILITY 81
-#define IPW_CMD_SUPPORTED_CHANNELS 82
-#define IPW_CMD_TPC_REPORT 83
-#define IPW_CMD_WME_INFO 84
-#define IPW_CMD_PRODUCTION_COMMAND 85
-#define IPW_CMD_LINKSYS_EOU_INFO 90
-
-#define RFD_SIZE 4
-#define NUM_TFD_CHUNKS 6
-
-#define TX_QUEUE_SIZE 32
-#define RX_QUEUE_SIZE 32
-
-#define DINO_CMD_WEP_KEY 0x08
-#define DINO_CMD_TX 0x0B
-#define DCT_ANTENNA_A 0x01
-#define DCT_ANTENNA_B 0x02
-
-#define IPW_A_MODE 0
-#define IPW_B_MODE 1
-#define IPW_G_MODE 2
-
-/*
- * TX Queue Flag Definitions
- */
-
-/* tx wep key definition */
-#define DCT_WEP_KEY_NOT_IMMIDIATE 0x00
-#define DCT_WEP_KEY_64Bit 0x40
-#define DCT_WEP_KEY_128Bit 0x80
-#define DCT_WEP_KEY_128bitIV 0xC0
-#define DCT_WEP_KEY_SIZE_MASK 0xC0
-
-#define DCT_WEP_KEY_INDEX_MASK 0x0F
-#define DCT_WEP_INDEX_USE_IMMEDIATE 0x20
-
-/* abort attempt if mgmt frame is rx'd */
-#define DCT_FLAG_ABORT_MGMT 0x01
-
-/* require CTS */
-#define DCT_FLAG_CTS_REQUIRED 0x02
-
-/* use short preamble */
-#define DCT_FLAG_LONG_PREAMBLE 0x00
-#define DCT_FLAG_SHORT_PREAMBLE 0x04
-
-/* RTS/CTS first */
-#define DCT_FLAG_RTS_REQD 0x08
-
-/* dont calculate duration field */
-#define DCT_FLAG_DUR_SET 0x10
-
-/* even if MAC WEP set (allows pre-encrypt) */
-#define DCT_FLAG_NO_WEP 0x20
-
-/* overwrite TSF field */
-#define DCT_FLAG_TSF_REQD 0x40
-
-/* ACK rx is expected to follow */
-#define DCT_FLAG_ACK_REQD 0x80
-
-/* TX flags extension */
-#define DCT_FLAG_EXT_MODE_CCK 0x01
-#define DCT_FLAG_EXT_MODE_OFDM 0x00
-
-#define DCT_FLAG_EXT_SECURITY_WEP 0x00
-#define DCT_FLAG_EXT_SECURITY_NO DCT_FLAG_EXT_SECURITY_WEP
-#define DCT_FLAG_EXT_SECURITY_CKIP 0x04
-#define DCT_FLAG_EXT_SECURITY_CCM 0x08
-#define DCT_FLAG_EXT_SECURITY_TKIP 0x0C
-#define DCT_FLAG_EXT_SECURITY_MASK 0x0C
-
-#define DCT_FLAG_EXT_QOS_ENABLED 0x10
-
-#define DCT_FLAG_EXT_HC_NO_SIFS_PIFS 0x00
-#define DCT_FLAG_EXT_HC_SIFS 0x20
-#define DCT_FLAG_EXT_HC_PIFS 0x40
-
-#define TX_RX_TYPE_MASK 0xFF
-#define TX_FRAME_TYPE 0x00
-#define TX_HOST_COMMAND_TYPE 0x01
-#define RX_FRAME_TYPE 0x09
-#define RX_HOST_NOTIFICATION_TYPE 0x03
-#define RX_HOST_CMD_RESPONSE_TYPE 0x04
-#define RX_TX_FRAME_RESPONSE_TYPE 0x05
-#define TFD_NEED_IRQ_MASK 0x04
-
-#define HOST_CMD_DINO_CONFIG 30
-
-#define HOST_NOTIFICATION_STATUS_ASSOCIATED 10
-#define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11
-#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12
-#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13
-#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14
-#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15
-#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16
-#define HOST_NOTIFICATION_STATUS_BEACON_STATE 17
-#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18
-#define HOST_NOTIFICATION_TX_STATUS 19
-#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20
-#define HOST_NOTIFICATION_MEASUREMENT_STARTED 21
-#define HOST_NOTIFICATION_MEASUREMENT_ENDED 22
-#define HOST_NOTIFICATION_CHANNEL_SWITCHED 23
-#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24
-#define HOST_NOTIFICATION_NOISE_STATS 25
-#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30
-#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31
-
-#define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1
-#define IPW_MB_ROAMING_THRESHOLD_MIN 1
-#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8
-#define IPW_MB_ROAMING_THRESHOLD_MAX 30
-#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 3*IPW_MB_ROAMING_THRESHOLD_DEFAULT
-#define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300
-
-#define MACADRR_BYTE_LEN 6
-
-#define DCR_TYPE_AP 0x01
-#define DCR_TYPE_WLAP 0x02
-#define DCR_TYPE_MU_ESS 0x03
-#define DCR_TYPE_MU_IBSS 0x04
-#define DCR_TYPE_MU_PIBSS 0x05
-#define DCR_TYPE_SNIFFER 0x06
-#define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS
-
-/* QoS definitions */
-
-#define CW_MIN_OFDM 15
-#define CW_MAX_OFDM 1023
-#define CW_MIN_CCK 31
-#define CW_MAX_CCK 1023
-
-#define QOS_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define QOS_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define QOS_TX2_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1)
-#define QOS_TX3_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/4 - 1)
-
-#define QOS_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-#define QOS_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-#define QOS_TX2_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1)
-#define QOS_TX3_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/4 - 1)
-
-#define QOS_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-#define QOS_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-#define QOS_TX2_CW_MAX_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define QOS_TX3_CW_MAX_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1)
-
-#define QOS_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-#define QOS_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-#define QOS_TX2_CW_MAX_CCK cpu_to_le16(CW_MIN_CCK)
-#define QOS_TX3_CW_MAX_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1)
-
-#define QOS_TX0_AIFS (3 - QOS_AIFSN_MIN_VALUE)
-#define QOS_TX1_AIFS (7 - QOS_AIFSN_MIN_VALUE)
-#define QOS_TX2_AIFS (2 - QOS_AIFSN_MIN_VALUE)
-#define QOS_TX3_AIFS (2 - QOS_AIFSN_MIN_VALUE)
-
-#define QOS_TX0_ACM 0
-#define QOS_TX1_ACM 0
-#define QOS_TX2_ACM 0
-#define QOS_TX3_ACM 0
-
-#define QOS_TX0_TXOP_LIMIT_CCK 0
-#define QOS_TX1_TXOP_LIMIT_CCK 0
-#define QOS_TX2_TXOP_LIMIT_CCK cpu_to_le16(6016)
-#define QOS_TX3_TXOP_LIMIT_CCK cpu_to_le16(3264)
-
-#define QOS_TX0_TXOP_LIMIT_OFDM 0
-#define QOS_TX1_TXOP_LIMIT_OFDM 0
-#define QOS_TX2_TXOP_LIMIT_OFDM cpu_to_le16(3008)
-#define QOS_TX3_TXOP_LIMIT_OFDM cpu_to_le16(1504)
-
-#define DEF_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define DEF_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define DEF_TX2_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-#define DEF_TX3_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
-
-#define DEF_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-#define DEF_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-#define DEF_TX2_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-#define DEF_TX3_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
-
-#define DEF_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-#define DEF_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-#define DEF_TX2_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-#define DEF_TX3_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
-
-#define DEF_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-#define DEF_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-#define DEF_TX2_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-#define DEF_TX3_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
-
-#define DEF_TX0_AIFS 0
-#define DEF_TX1_AIFS 0
-#define DEF_TX2_AIFS 0
-#define DEF_TX3_AIFS 0
-
-#define DEF_TX0_ACM 0
-#define DEF_TX1_ACM 0
-#define DEF_TX2_ACM 0
-#define DEF_TX3_ACM 0
-
-#define DEF_TX0_TXOP_LIMIT_CCK 0
-#define DEF_TX1_TXOP_LIMIT_CCK 0
-#define DEF_TX2_TXOP_LIMIT_CCK 0
-#define DEF_TX3_TXOP_LIMIT_CCK 0
-
-#define DEF_TX0_TXOP_LIMIT_OFDM 0
-#define DEF_TX1_TXOP_LIMIT_OFDM 0
-#define DEF_TX2_TXOP_LIMIT_OFDM 0
-#define DEF_TX3_TXOP_LIMIT_OFDM 0
-
-#define QOS_QOS_SETS 3
-#define QOS_PARAM_SET_ACTIVE 0
-#define QOS_PARAM_SET_DEF_CCK 1
-#define QOS_PARAM_SET_DEF_OFDM 2
-
-#define CTRL_QOS_NO_ACK (0x0020)
-
-#define IPW_TX_QUEUE_1 1
-#define IPW_TX_QUEUE_2 2
-#define IPW_TX_QUEUE_3 3
-#define IPW_TX_QUEUE_4 4
-
-/* QoS sturctures */
-struct ipw_qos_info {
- int qos_enable;
- struct ieee80211_qos_parameters *def_qos_parm_OFDM;
- struct ieee80211_qos_parameters *def_qos_parm_CCK;
- u32 burst_duration_CCK;
- u32 burst_duration_OFDM;
- u16 qos_no_ack_mask;
- int burst_enable;
-};
-
-/**************************************************************/
-/**
- * Generic queue structure
- *
- * Contains common data for Rx and Tx queues
- */
-struct clx2_queue {
- int n_bd; /**< number of BDs in this queue */
- int first_empty; /**< 1-st empty entry (index) */
- int last_used; /**< last used entry (index) */
- u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */
- u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */
- dma_addr_t dma_addr; /**< physical addr for BD's */
- int low_mark; /**< low watermark, resume queue if free space more than this */
- int high_mark; /**< high watermark, stop queue if free space less than this */
-} __attribute__ ((packed)); /* XXX */
-
-struct machdr32 {
- __le16 frame_ctl;
- __le16 duration; // watch out for endians!
- u8 addr1[MACADRR_BYTE_LEN];
- u8 addr2[MACADRR_BYTE_LEN];
- u8 addr3[MACADRR_BYTE_LEN];
- __le16 seq_ctrl; // more endians!
- u8 addr4[MACADRR_BYTE_LEN];
- __le16 qos_ctrl;
-} __attribute__ ((packed));
-
-struct machdr30 {
- __le16 frame_ctl;
- __le16 duration; // watch out for endians!
- u8 addr1[MACADRR_BYTE_LEN];
- u8 addr2[MACADRR_BYTE_LEN];
- u8 addr3[MACADRR_BYTE_LEN];
- __le16 seq_ctrl; // more endians!
- u8 addr4[MACADRR_BYTE_LEN];
-} __attribute__ ((packed));
-
-struct machdr26 {
- __le16 frame_ctl;
- __le16 duration; // watch out for endians!
- u8 addr1[MACADRR_BYTE_LEN];
- u8 addr2[MACADRR_BYTE_LEN];
- u8 addr3[MACADRR_BYTE_LEN];
- __le16 seq_ctrl; // more endians!
- __le16 qos_ctrl;
-} __attribute__ ((packed));
-
-struct machdr24 {
- __le16 frame_ctl;
- __le16 duration; // watch out for endians!
- u8 addr1[MACADRR_BYTE_LEN];
- u8 addr2[MACADRR_BYTE_LEN];
- u8 addr3[MACADRR_BYTE_LEN];
- __le16 seq_ctrl; // more endians!
-} __attribute__ ((packed));
-
-// TX TFD with 32 byte MAC Header
-struct tx_tfd_32 {
- struct machdr32 mchdr; // 32
- __le32 uivplaceholder[2]; // 8
-} __attribute__ ((packed));
-
-// TX TFD with 30 byte MAC Header
-struct tx_tfd_30 {
- struct machdr30 mchdr; // 30
- u8 reserved[2]; // 2
- __le32 uivplaceholder[2]; // 8
-} __attribute__ ((packed));
-
-// tx tfd with 26 byte mac header
-struct tx_tfd_26 {
- struct machdr26 mchdr; // 26
- u8 reserved1[2]; // 2
- __le32 uivplaceholder[2]; // 8
- u8 reserved2[4]; // 4
-} __attribute__ ((packed));
-
-// tx tfd with 24 byte mac header
-struct tx_tfd_24 {
- struct machdr24 mchdr; // 24
- __le32 uivplaceholder[2]; // 8
- u8 reserved[8]; // 8
-} __attribute__ ((packed));
-
-#define DCT_WEP_KEY_FIELD_LENGTH 16
-
-struct tfd_command {
- u8 index;
- u8 length;
- __le16 reserved;
- u8 payload[0];
-} __attribute__ ((packed));
-
-struct tfd_data {
- /* Header */
- __le32 work_area_ptr;
- u8 station_number; /* 0 for BSS */
- u8 reserved1;
- __le16 reserved2;
-
- /* Tx Parameters */
- u8 cmd_id;
- u8 seq_num;
- __le16 len;
- u8 priority;
- u8 tx_flags;
- u8 tx_flags_ext;
- u8 key_index;
- u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH];
- u8 rate;
- u8 antenna;
- __le16 next_packet_duration;
- __le16 next_frag_len;
- __le16 back_off_counter; //////txop;
- u8 retrylimit;
- __le16 cwcurrent;
- u8 reserved3;
-
- /* 802.11 MAC Header */
- union {
- struct tx_tfd_24 tfd_24;
- struct tx_tfd_26 tfd_26;
- struct tx_tfd_30 tfd_30;
- struct tx_tfd_32 tfd_32;
- } tfd;
-
- /* Payload DMA info */
- __le32 num_chunks;
- __le32 chunk_ptr[NUM_TFD_CHUNKS];
- __le16 chunk_len[NUM_TFD_CHUNKS];
-} __attribute__ ((packed));
-
-struct txrx_control_flags {
- u8 message_type;
- u8 rx_seq_num;
- u8 control_bits;
- u8 reserved;
-} __attribute__ ((packed));
-
-#define TFD_SIZE 128
-#define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags))
-
-struct tfd_frame {
- struct txrx_control_flags control_flags;
- union {
- struct tfd_data data;
- struct tfd_command cmd;
- u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
- } u;
-} __attribute__ ((packed));
-
-typedef void destructor_func(const void *);
-
-/**
- * Tx Queue for DMA. Queue consists of circular buffer of
- * BD's and required locking structures.
- */
-struct clx2_tx_queue {
- struct clx2_queue q;
- struct tfd_frame *bd;
- struct ieee80211_txb **txb;
-};
-
-/*
- * RX related structures and functions
- */
-#define RX_FREE_BUFFERS 32
-#define RX_LOW_WATERMARK 8
-
-#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
-#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
-#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
-
-// Used for passing to driver number of successes and failures per rate
-struct rate_histogram {
- union {
- __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
- __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
- __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
- } success;
- union {
- __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
- __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
- __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
- } failed;
-} __attribute__ ((packed));
-
-/* statistics command response */
-struct ipw_cmd_stats {
- u8 cmd_id;
- u8 seq_num;
- __le16 good_sfd;
- __le16 bad_plcp;
- __le16 wrong_bssid;
- __le16 valid_mpdu;
- __le16 bad_mac_header;
- __le16 reserved_frame_types;
- __le16 rx_ina;
- __le16 bad_crc32;
- __le16 invalid_cts;
- __le16 invalid_acks;
- __le16 long_distance_ina_fina;
- __le16 dsp_silence_unreachable;
- __le16 accumulated_rssi;
- __le16 rx_ovfl_frame_tossed;
- __le16 rssi_silence_threshold;
- __le16 rx_ovfl_frame_supplied;
- __le16 last_rx_frame_signal;
- __le16 last_rx_frame_noise;
- __le16 rx_autodetec_no_ofdm;
- __le16 rx_autodetec_no_barker;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct notif_channel_result {
- u8 channel_num;
- struct ipw_cmd_stats stats;
- u8 uReserved;
-} __attribute__ ((packed));
-
-#define SCAN_COMPLETED_STATUS_COMPLETE 1
-#define SCAN_COMPLETED_STATUS_ABORTED 2
-
-struct notif_scan_complete {
- u8 scan_type;
- u8 num_channels;
- u8 status;
- u8 reserved;
-} __attribute__ ((packed));
-
-struct notif_frag_length {
- __le16 frag_length;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct notif_beacon_state {
- __le32 state;
- __le32 number;
-} __attribute__ ((packed));
-
-struct notif_tgi_tx_key {
- u8 key_state;
- u8 security_type;
- u8 station_index;
- u8 reserved;
-} __attribute__ ((packed));
-
-#define SILENCE_OVER_THRESH (1)
-#define SILENCE_UNDER_THRESH (2)
-
-struct notif_link_deterioration {
- struct ipw_cmd_stats stats;
- u8 rate;
- u8 modulation;
- struct rate_histogram histogram;
- u8 silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */
- __le16 silence_count;
-} __attribute__ ((packed));
-
-struct notif_association {
- u8 state;
-} __attribute__ ((packed));
-
-struct notif_authenticate {
- u8 state;
- struct machdr24 addr;
- __le16 status;
-} __attribute__ ((packed));
-
-struct notif_calibration {
- u8 data[104];
-} __attribute__ ((packed));
-
-struct notif_noise {
- __le32 value;
-} __attribute__ ((packed));
-
-struct ipw_rx_notification {
- u8 reserved[8];
- u8 subtype;
- u8 flags;
- __le16 size;
- union {
- struct notif_association assoc;
- struct notif_authenticate auth;
- struct notif_channel_result channel_result;
- struct notif_scan_complete scan_complete;
- struct notif_frag_length frag_len;
- struct notif_beacon_state beacon_state;
- struct notif_tgi_tx_key tgi_tx_key;
- struct notif_link_deterioration link_deterioration;
- struct notif_calibration calibration;
- struct notif_noise noise;
- u8 raw[0];
- } u;
-} __attribute__ ((packed));
-
-struct ipw_rx_frame {
- __le32 reserved1;
- u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER
- u8 received_channel; // The channel that this frame was received on.
- // Note that for .11b this does not have to be
- // the same as the channel that it was sent.
- // Filled by LMAC
- u8 frameStatus;
- u8 rate;
- u8 rssi;
- u8 agc;
- u8 rssi_dbm;
- __le16 signal;
- __le16 noise;
- u8 antennaAndPhy;
- u8 control; // control bit should be on in bg
- u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate
- // is identical)
- u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen
- __le16 length;
- u8 data[0];
-} __attribute__ ((packed));
-
-struct ipw_rx_header {
- u8 message_type;
- u8 rx_seq_num;
- u8 control_bits;
- u8 reserved;
-} __attribute__ ((packed));
-
-struct ipw_rx_packet {
- struct ipw_rx_header header;
- union {
- struct ipw_rx_frame frame;
- struct ipw_rx_notification notification;
- } u;
-} __attribute__ ((packed));
-
-#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12
-#define IPW_RX_FRAME_SIZE (unsigned int)(sizeof(struct ipw_rx_header) + \
- sizeof(struct ipw_rx_frame))
-
-struct ipw_rx_mem_buffer {
- dma_addr_t dma_addr;
- struct sk_buff *skb;
- struct list_head list;
-}; /* Not transferred over network, so not __attribute__ ((packed)) */
-
-struct ipw_rx_queue {
- struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
- struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE];
- u32 processed; /* Internal index to last handled Rx packet */
- u32 read; /* Shared index to newest available Rx buffer */
- u32 write; /* Shared index to oldest written Rx packet */
- u32 free_count; /* Number of pre-allocated buffers in rx_free */
- /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */
- struct list_head rx_free; /* Own an SKBs */
- struct list_head rx_used; /* No SKB allocated */
- spinlock_t lock;
-}; /* Not transferred over network, so not __attribute__ ((packed)) */
-
-struct alive_command_responce {
- u8 alive_command;
- u8 sequence_number;
- __le16 software_revision;
- u8 device_identifier;
- u8 reserved1[5];
- __le16 reserved2;
- __le16 reserved3;
- __le16 clock_settle_time;
- __le16 powerup_settle_time;
- __le16 reserved4;
- u8 time_stamp[5]; /* month, day, year, hours, minutes */
- u8 ucode_valid;
-} __attribute__ ((packed));
-
-#define IPW_MAX_RATES 12
-
-struct ipw_rates {
- u8 num_rates;
- u8 rates[IPW_MAX_RATES];
-} __attribute__ ((packed));
-
-struct command_block {
- unsigned int control;
- u32 source_addr;
- u32 dest_addr;
- unsigned int status;
-} __attribute__ ((packed));
-
-#define CB_NUMBER_OF_ELEMENTS_SMALL 64
-struct fw_image_desc {
- unsigned long last_cb_index;
- unsigned long current_cb_index;
- struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL];
- void *v_addr;
- unsigned long p_addr;
- unsigned long len;
-};
-
-struct ipw_sys_config {
- u8 bt_coexistence;
- u8 reserved1;
- u8 answer_broadcast_ssid_probe;
- u8 accept_all_data_frames;
- u8 accept_non_directed_frames;
- u8 exclude_unicast_unencrypted;
- u8 disable_unicast_decryption;
- u8 exclude_multicast_unencrypted;
- u8 disable_multicast_decryption;
- u8 antenna_diversity;
- u8 pass_crc_to_host;
- u8 dot11g_auto_detection;
- u8 enable_cts_to_self;
- u8 enable_multicast_filtering;
- u8 bt_coexist_collision_thr;
- u8 silence_threshold;
- u8 accept_all_mgmt_bcpr;
- u8 accept_all_mgmt_frames;
- u8 pass_noise_stats_to_host;
- u8 reserved3;
-} __attribute__ ((packed));
-
-struct ipw_multicast_addr {
- u8 num_of_multicast_addresses;
- u8 reserved[3];
- u8 mac1[6];
- u8 mac2[6];
- u8 mac3[6];
- u8 mac4[6];
-} __attribute__ ((packed));
-
-#define DCW_WEP_KEY_INDEX_MASK 0x03 /* bits [0:1] */
-#define DCW_WEP_KEY_SEC_TYPE_MASK 0x30 /* bits [4:5] */
-
-#define DCW_WEP_KEY_SEC_TYPE_WEP 0x00
-#define DCW_WEP_KEY_SEC_TYPE_CCM 0x20
-#define DCW_WEP_KEY_SEC_TYPE_TKIP 0x30
-
-#define DCW_WEP_KEY_INVALID_SIZE 0x00 /* 0 = Invalid key */
-#define DCW_WEP_KEY64Bit_SIZE 0x05 /* 64-bit encryption */
-#define DCW_WEP_KEY128Bit_SIZE 0x0D /* 128-bit encryption */
-#define DCW_CCM_KEY128Bit_SIZE 0x10 /* 128-bit key */
-//#define DCW_WEP_KEY128BitIV_SIZE 0x10 /* 128-bit key and 128-bit IV */
-
-struct ipw_wep_key {
- u8 cmd_id;
- u8 seq_num;
- u8 key_index;
- u8 key_size;
- u8 key[16];
-} __attribute__ ((packed));
-
-struct ipw_tgi_tx_key {
- u8 key_id;
- u8 security_type;
- u8 station_index;
- u8 flags;
- u8 key[16];
- __le32 tx_counter[2];
-} __attribute__ ((packed));
-
-#define IPW_SCAN_CHANNELS 54
-
-struct ipw_scan_request {
- u8 scan_type;
- __le16 dwell_time;
- u8 channels_list[IPW_SCAN_CHANNELS];
- u8 channels_reserved[3];
-} __attribute__ ((packed));
-
-enum {
- IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0,
- IPW_SCAN_PASSIVE_FULL_DWELL_SCAN,
- IPW_SCAN_ACTIVE_DIRECT_SCAN,
- IPW_SCAN_ACTIVE_BROADCAST_SCAN,
- IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN,
- IPW_SCAN_TYPES
-};
-
-struct ipw_scan_request_ext {
- __le32 full_scan_index;
- u8 channels_list[IPW_SCAN_CHANNELS];
- u8 scan_type[IPW_SCAN_CHANNELS / 2];
- u8 reserved;
- __le16 dwell_time[IPW_SCAN_TYPES];
-} __attribute__ ((packed));
-
-static inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index)
-{
- if (index % 2)
- return scan->scan_type[index / 2] & 0x0F;
- else
- return (scan->scan_type[index / 2] & 0xF0) >> 4;
-}
-
-static inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan,
- u8 index, u8 scan_type)
-{
- if (index % 2)
- scan->scan_type[index / 2] =
- (scan->scan_type[index / 2] & 0xF0) | (scan_type & 0x0F);
- else
- scan->scan_type[index / 2] =
- (scan->scan_type[index / 2] & 0x0F) |
- ((scan_type & 0x0F) << 4);
-}
-
-struct ipw_associate {
- u8 channel;
-#ifdef __LITTLE_ENDIAN_BITFIELD
- u8 auth_type:4, auth_key:4;
-#else
- u8 auth_key:4, auth_type:4;
-#endif
- u8 assoc_type;
- u8 reserved;
- __le16 policy_support;
- u8 preamble_length;
- u8 ieee_mode;
- u8 bssid[ETH_ALEN];
- __le32 assoc_tsf_msw;
- __le32 assoc_tsf_lsw;
- __le16 capability;
- __le16 listen_interval;
- __le16 beacon_interval;
- u8 dest[ETH_ALEN];
- __le16 atim_window;
- u8 smr;
- u8 reserved1;
- __le16 reserved2;
-} __attribute__ ((packed));
-
-struct ipw_supported_rates {
- u8 ieee_mode;
- u8 num_rates;
- u8 purpose;
- u8 reserved;
- u8 supported_rates[IPW_MAX_RATES];
-} __attribute__ ((packed));
-
-struct ipw_rts_threshold {
- __le16 rts_threshold;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct ipw_frag_threshold {
- __le16 frag_threshold;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct ipw_retry_limit {
- u8 short_retry_limit;
- u8 long_retry_limit;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct ipw_dino_config {
- __le32 dino_config_addr;
- __le16 dino_config_size;
- u8 dino_response;
- u8 reserved;
-} __attribute__ ((packed));
-
-struct ipw_aironet_info {
- u8 id;
- u8 length;
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct ipw_rx_key {
- u8 station_index;
- u8 key_type;
- u8 key_id;
- u8 key_flag;
- u8 key[16];
- u8 station_address[6];
- u8 key_index;
- u8 reserved;
-} __attribute__ ((packed));
-
-struct ipw_country_channel_info {
- u8 first_channel;
- u8 no_channels;
- s8 max_tx_power;
-} __attribute__ ((packed));
-
-struct ipw_country_info {
- u8 id;
- u8 length;
- u8 country_str[3];
- struct ipw_country_channel_info groups[7];
-} __attribute__ ((packed));
-
-struct ipw_channel_tx_power {
- u8 channel_number;
- s8 tx_power;
-} __attribute__ ((packed));
-
-#define SCAN_ASSOCIATED_INTERVAL (HZ)
-#define SCAN_INTERVAL (HZ / 10)
-#define MAX_A_CHANNELS 37
-#define MAX_B_CHANNELS 14
-
-struct ipw_tx_power {
- u8 num_channels;
- u8 ieee_mode;
- struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS];
-} __attribute__ ((packed));
-
-struct ipw_rsn_capabilities {
- u8 id;
- u8 length;
- __le16 version;
-} __attribute__ ((packed));
-
-struct ipw_sensitivity_calib {
- __le16 beacon_rssi_raw;
- __le16 reserved;
-} __attribute__ ((packed));
-
-/**
- * Host command structure.
- *
- * On input, the following fields should be filled:
- * - cmd
- * - len
- * - status_len
- * - param (if needed)
- *
- * On output,
- * - \a status contains status;
- * - \a param filled with status parameters.
- */
-struct ipw_cmd { /* XXX */
- u32 cmd; /**< Host command */
- u32 status;/**< Status */
- u32 status_len;
- /**< How many 32 bit parameters in the status */
- u32 len; /**< incoming parameters length, bytes */
- /**
- * command parameters.
- * There should be enough space for incoming and
- * outcoming parameters.
- * Incoming parameters listed 1-st, followed by outcoming params.
- * nParams=(len+3)/4+status_len
- */
- u32 param[0];
-} __attribute__ ((packed));
-
-#define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */
-
-#define STATUS_INT_ENABLED (1<<1)
-#define STATUS_RF_KILL_HW (1<<2)
-#define STATUS_RF_KILL_SW (1<<3)
-#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
-
-#define STATUS_INIT (1<<5)
-#define STATUS_AUTH (1<<6)
-#define STATUS_ASSOCIATED (1<<7)
-#define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED)
-
-#define STATUS_ASSOCIATING (1<<8)
-#define STATUS_DISASSOCIATING (1<<9)
-#define STATUS_ROAMING (1<<10)
-#define STATUS_EXIT_PENDING (1<<11)
-#define STATUS_DISASSOC_PENDING (1<<12)
-#define STATUS_STATE_PENDING (1<<13)
-
-#define STATUS_DIRECT_SCAN_PENDING (1<<19)
-#define STATUS_SCAN_PENDING (1<<20)
-#define STATUS_SCANNING (1<<21)
-#define STATUS_SCAN_ABORTING (1<<22)
-#define STATUS_SCAN_FORCED (1<<23)
-
-#define STATUS_LED_LINK_ON (1<<24)
-#define STATUS_LED_ACT_ON (1<<25)
-
-#define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */
-#define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */
-#define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */
-
-#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */
-
-#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */
-#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */
-#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */
-#define CFG_CUSTOM_MAC (1<<3)
-#define CFG_PREAMBLE_LONG (1<<4)
-#define CFG_ADHOC_PERSIST (1<<5)
-#define CFG_ASSOCIATE (1<<6)
-#define CFG_FIXED_RATE (1<<7)
-#define CFG_ADHOC_CREATE (1<<8)
-#define CFG_NO_LED (1<<9)
-#define CFG_BACKGROUND_SCAN (1<<10)
-#define CFG_SPEED_SCAN (1<<11)
-#define CFG_NET_STATS (1<<12)
-
-#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */
-#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */
-
-#define MAX_STATIONS 32
-#define IPW_INVALID_STATION (0xff)
-
-struct ipw_station_entry {
- u8 mac_addr[ETH_ALEN];
- u8 reserved;
- u8 support_mode;
-};
-
-#define AVG_ENTRIES 8
-struct average {
- s16 entries[AVG_ENTRIES];
- u8 pos;
- u8 init;
- s32 sum;
-};
-
-#define MAX_SPEED_SCAN 100
-#define IPW_IBSS_MAC_HASH_SIZE 31
-
-struct ipw_ibss_seq {
- u8 mac[ETH_ALEN];
- u16 seq_num;
- u16 frag_num;
- unsigned long packet_time;
- struct list_head list;
-};
-
-struct ipw_error_elem { /* XXX */
- u32 desc;
- u32 time;
- u32 blink1;
- u32 blink2;
- u32 link1;
- u32 link2;
- u32 data;
-};
-
-struct ipw_event { /* XXX */
- u32 event;
- u32 time;
- u32 data;
-} __attribute__ ((packed));
-
-struct ipw_fw_error { /* XXX */
- unsigned long jiffies;
- u32 status;
- u32 config;
- u32 elem_len;
- u32 log_len;
- struct ipw_error_elem *elem;
- struct ipw_event *log;
- u8 payload[0];
-} __attribute__ ((packed));
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
-
-enum ipw_prom_filter {
- IPW_PROM_CTL_HEADER_ONLY = (1 << 0),
- IPW_PROM_MGMT_HEADER_ONLY = (1 << 1),
- IPW_PROM_DATA_HEADER_ONLY = (1 << 2),
- IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */
- IPW_PROM_NO_TX = (1 << 4),
- IPW_PROM_NO_RX = (1 << 5),
- IPW_PROM_NO_CTL = (1 << 6),
- IPW_PROM_NO_MGMT = (1 << 7),
- IPW_PROM_NO_DATA = (1 << 8),
-};
-
-struct ipw_priv;
-struct ipw_prom_priv {
- struct ipw_priv *priv;
- struct ieee80211_device *ieee;
- enum ipw_prom_filter filter;
- int tx_packets;
- int rx_packets;
-};
-#endif
-
-#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS)
-/* Magic struct that slots into the radiotap header -- no reason
- * to build this manually element by element, we can write it much
- * more efficiently than we can parse it. ORDER MATTERS HERE
- *
- * When sent to us via the simulated Rx interface in sysfs, the entire
- * structure is provided regardless of any bits unset.
- */
-struct ipw_rt_hdr {
- struct ieee80211_radiotap_header rt_hdr;
- u64 rt_tsf; /* TSF */ /* XXX */
- u8 rt_flags; /* radiotap packet flags */
- u8 rt_rate; /* rate in 500kb/s */
- __le16 rt_channel; /* channel in mhz */
- __le16 rt_chbitmask; /* channel bitfield */
- s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
- s8 rt_dbmnoise;
- u8 rt_antenna; /* antenna number */
- u8 payload[0]; /* payload... */
-} __attribute__ ((packed));
-#endif
-
-struct ipw_priv {
- /* ieee device used by generic ieee processing code */
- struct ieee80211_device *ieee;
-
- spinlock_t lock;
- spinlock_t irq_lock;
- struct mutex mutex;
-
- /* basic pci-network driver stuff */
- struct pci_dev *pci_dev;
- struct net_device *net_dev;
-
-#ifdef CONFIG_IPW2200_PROMISCUOUS
- /* Promiscuous mode */
- struct ipw_prom_priv *prom_priv;
- struct net_device *prom_net_dev;
-#endif
-
- /* pci hardware address support */
- void __iomem *hw_base;
- unsigned long hw_len;
-
- struct fw_image_desc sram_desc;
-
- /* result of ucode download */
- struct alive_command_responce dino_alive;
-
- wait_queue_head_t wait_command_queue;
- wait_queue_head_t wait_state;
-
- /* Rx and Tx DMA processing queues */
- struct ipw_rx_queue *rxq;
- struct clx2_tx_queue txq_cmd;
- struct clx2_tx_queue txq[4];
- u32 status;
- u32 config;
- u32 capability;
-
- struct average average_missed_beacons;
- s16 exp_avg_rssi;
- s16 exp_avg_noise;
- u32 port_type;
- int rx_bufs_min; /**< minimum number of bufs in Rx queue */
- int rx_pend_max; /**< maximum pending buffers for one IRQ */
- u32 hcmd_seq; /**< sequence number for hcmd */
- u32 disassociate_threshold;
- u32 roaming_threshold;
-
- struct ipw_associate assoc_request;
- struct ieee80211_network *assoc_network;
-
- unsigned long ts_scan_abort;
- struct ipw_supported_rates rates;
- struct ipw_rates phy[3]; /**< PHY restrictions, per band */
- struct ipw_rates supp; /**< software defined */
- struct ipw_rates extended; /**< use for corresp. IE, AP only */
-
- struct notif_link_deterioration last_link_deterioration; /** for statistics */
- struct ipw_cmd *hcmd; /**< host command currently executed */
-
- wait_queue_head_t hcmd_wq; /**< host command waits for execution */
- u32 tsf_bcn[2]; /**< TSF from latest beacon */
-
- struct notif_calibration calib; /**< last calibration */
-
- /* ordinal interface with firmware */
- u32 table0_addr;
- u32 table0_len;
- u32 table1_addr;
- u32 table1_len;
- u32 table2_addr;
- u32 table2_len;
-
- /* context information */
- u8 essid[IW_ESSID_MAX_SIZE];
- u8 essid_len;
- u8 nick[IW_ESSID_MAX_SIZE];
- u16 rates_mask;
- u8 channel;
- struct ipw_sys_config sys_config;
- u32 power_mode;
- u8 bssid[ETH_ALEN];
- u16 rts_threshold;
- u8 mac_addr[ETH_ALEN];
- u8 num_stations;
- u8 stations[MAX_STATIONS][ETH_ALEN];
- u8 short_retry_limit;
- u8 long_retry_limit;
-
- u32 notif_missed_beacons;
-
- /* Statistics and counters normalized with each association */
- u32 last_missed_beacons;
- u32 last_tx_packets;
- u32 last_rx_packets;
- u32 last_tx_failures;
- u32 last_rx_err;
- u32 last_rate;
-
- u32 missed_adhoc_beacons;
- u32 missed_beacons;
- u32 rx_packets;
- u32 tx_packets;
- u32 quality;
-
- u8 speed_scan[MAX_SPEED_SCAN];
- u8 speed_scan_pos;
-
- u16 last_seq_num;
- u16 last_frag_num;
- unsigned long last_packet_time;
- struct list_head ibss_mac_hash[IPW_IBSS_MAC_HASH_SIZE];
-
- /* eeprom */
- u8 eeprom[0x100]; /* 256 bytes of eeprom */
- u8 country[4];
- int eeprom_delay;
-
- struct iw_statistics wstats;
-
- struct iw_public_data wireless_data;
-
- int user_requested_scan;
- u8 direct_scan_ssid[IW_ESSID_MAX_SIZE];
- u8 direct_scan_ssid_len;
-
- struct workqueue_struct *workqueue;
-
- struct delayed_work adhoc_check;
- struct work_struct associate;
- struct work_struct disassociate;
- struct work_struct system_config;
- struct work_struct rx_replenish;
- struct delayed_work request_scan;
- struct delayed_work request_direct_scan;
- struct delayed_work request_passive_scan;
- struct delayed_work scan_event;
- struct work_struct adapter_restart;
- struct delayed_work rf_kill;
- struct work_struct up;
- struct work_struct down;
- struct delayed_work gather_stats;
- struct work_struct abort_scan;
- struct work_struct roam;
- struct delayed_work scan_check;
- struct work_struct link_up;
- struct work_struct link_down;
-
- struct tasklet_struct irq_tasklet;
-
- /* LED related variables and work_struct */
- u8 nic_type;
- u32 led_activity_on;
- u32 led_activity_off;
- u32 led_association_on;
- u32 led_association_off;
- u32 led_ofdm_on;
- u32 led_ofdm_off;
-
- struct delayed_work led_link_on;
- struct delayed_work led_link_off;
- struct delayed_work led_act_off;
- struct work_struct merge_networks;
-
- struct ipw_cmd_log *cmdlog;
- int cmdlog_len;
- int cmdlog_pos;
-
-#define IPW_2200BG 1
-#define IPW_2915ABG 2
- u8 adapter;
-
- s8 tx_power;
-
-#ifdef CONFIG_PM
- u32 pm_state[16];
-#endif
-
- struct ipw_fw_error *error;
-
- /* network state */
-
- /* Used to pass the current INTA value from ISR to Tasklet */
- u32 isr_inta;
-
- /* QoS */
- struct ipw_qos_info qos_data;
- struct work_struct qos_activate;
- /*********************************/
-
- /* debugging info */
- u32 indirect_dword;
- u32 direct_dword;
- u32 indirect_byte;
-}; /*ipw_priv */
-
-/* debug macros */
-
-/* Debug and printf string expansion helpers for printing bitfields */
-#define BIT_FMT8 "%c%c%c%c-%c%c%c%c"
-#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8
-#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16
-
-#define BITC(x,y) (((x>>y)&1)?'1':'0')
-#define BIT_ARG8(x) \
-BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\
-BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0)
-
-#define BIT_ARG16(x) \
-BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\
-BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\
-BIT_ARG8(x)
-
-#define BIT_ARG32(x) \
-BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\
-BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\
-BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\
-BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\
-BIT_ARG16(x)
-
-
-#define IPW_DEBUG(level, fmt, args...) \
-do { if (ipw_debug_level & (level)) \
- printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
- in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0)
-
-#ifdef CONFIG_IPW2200_DEBUG
-#define IPW_LL_DEBUG(level, fmt, args...) \
-do { if (ipw_debug_level & (level)) \
- printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
- in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0)
-#else
-#define IPW_LL_DEBUG(level, fmt, args...) do {} while (0)
-#endif /* CONFIG_IPW2200_DEBUG */
-
-/*
- * To use the debug system;
- *
- * If you are defining a new debug classification, simply add it to the #define
- * list here in the form of:
- *
- * #define IPW_DL_xxxx VALUE
- *
- * shifting value to the left one bit from the previous entry. xxxx should be
- * the name of the classification (for example, WEP)
- *
- * You then need to either add a IPW_xxxx_DEBUG() macro definition for your
- * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
- * to send output to that classification.
- *
- * To add your debug level to the list of levels seen when you perform
- *
- * % cat /proc/net/ipw/debug_level
- *
- * you simply need to add your entry to the ipw_debug_levels array.
- *
- * If you do not see debug_level in /proc/net/ipw then you do not have
- * CONFIG_IPW2200_DEBUG defined in your kernel configuration
- *
- */
-
-#define IPW_DL_ERROR (1<<0)
-#define IPW_DL_WARNING (1<<1)
-#define IPW_DL_INFO (1<<2)
-#define IPW_DL_WX (1<<3)
-#define IPW_DL_HOST_COMMAND (1<<5)
-#define IPW_DL_STATE (1<<6)
-
-#define IPW_DL_NOTIF (1<<10)
-#define IPW_DL_SCAN (1<<11)
-#define IPW_DL_ASSOC (1<<12)
-#define IPW_DL_DROP (1<<13)
-#define IPW_DL_IOCTL (1<<14)
-
-#define IPW_DL_MANAGE (1<<15)
-#define IPW_DL_FW (1<<16)
-#define IPW_DL_RF_KILL (1<<17)
-#define IPW_DL_FW_ERRORS (1<<18)
-
-#define IPW_DL_LED (1<<19)
-
-#define IPW_DL_ORD (1<<20)
-
-#define IPW_DL_FRAG (1<<21)
-#define IPW_DL_WEP (1<<22)
-#define IPW_DL_TX (1<<23)
-#define IPW_DL_RX (1<<24)
-#define IPW_DL_ISR (1<<25)
-#define IPW_DL_FW_INFO (1<<26)
-#define IPW_DL_IO (1<<27)
-#define IPW_DL_TRACE (1<<28)
-
-#define IPW_DL_STATS (1<<29)
-#define IPW_DL_MERGE (1<<30)
-#define IPW_DL_QOS (1<<31)
-
-#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
-#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
-#define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a)
-
-#define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a)
-#define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a)
-#define IPW_DEBUG_TRACE(f, a...) IPW_LL_DEBUG(IPW_DL_TRACE, f, ## a)
-#define IPW_DEBUG_RX(f, a...) IPW_LL_DEBUG(IPW_DL_RX, f, ## a)
-#define IPW_DEBUG_TX(f, a...) IPW_LL_DEBUG(IPW_DL_TX, f, ## a)
-#define IPW_DEBUG_ISR(f, a...) IPW_LL_DEBUG(IPW_DL_ISR, f, ## a)
-#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a)
-#define IPW_DEBUG_LED(f, a...) IPW_LL_DEBUG(IPW_DL_LED, f, ## a)
-#define IPW_DEBUG_WEP(f, a...) IPW_LL_DEBUG(IPW_DL_WEP, f, ## a)
-#define IPW_DEBUG_HC(f, a...) IPW_LL_DEBUG(IPW_DL_HOST_COMMAND, f, ## a)
-#define IPW_DEBUG_FRAG(f, a...) IPW_LL_DEBUG(IPW_DL_FRAG, f, ## a)
-#define IPW_DEBUG_FW(f, a...) IPW_LL_DEBUG(IPW_DL_FW, f, ## a)
-#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a)
-#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a)
-#define IPW_DEBUG_IO(f, a...) IPW_LL_DEBUG(IPW_DL_IO, f, ## a)
-#define IPW_DEBUG_ORD(f, a...) IPW_LL_DEBUG(IPW_DL_ORD, f, ## a)
-#define IPW_DEBUG_FW_INFO(f, a...) IPW_LL_DEBUG(IPW_DL_FW_INFO, f, ## a)
-#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a)
-#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
-#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
-#define IPW_DEBUG_STATS(f, a...) IPW_LL_DEBUG(IPW_DL_STATS, f, ## a)
-#define IPW_DEBUG_MERGE(f, a...) IPW_LL_DEBUG(IPW_DL_MERGE, f, ## a)
-#define IPW_DEBUG_QOS(f, a...) IPW_LL_DEBUG(IPW_DL_QOS, f, ## a)
-
-#include <linux/ctype.h>
-
-/*
-* Register bit definitions
-*/
-
-#define IPW_INTA_RW 0x00000008
-#define IPW_INTA_MASK_R 0x0000000C
-#define IPW_INDIRECT_ADDR 0x00000010
-#define IPW_INDIRECT_DATA 0x00000014
-#define IPW_AUTOINC_ADDR 0x00000018
-#define IPW_AUTOINC_DATA 0x0000001C
-#define IPW_RESET_REG 0x00000020
-#define IPW_GP_CNTRL_RW 0x00000024
-
-#define IPW_READ_INT_REGISTER 0xFF4
-
-#define IPW_GP_CNTRL_BIT_INIT_DONE 0x00000004
-
-#define IPW_REGISTER_DOMAIN1_END 0x00001000
-#define IPW_SRAM_READ_INT_REGISTER 0x00000ff4
-
-#define IPW_SHARED_LOWER_BOUND 0x00000200
-#define IPW_INTERRUPT_AREA_LOWER_BOUND 0x00000f80
-
-#define IPW_NIC_SRAM_LOWER_BOUND 0x00000000
-#define IPW_NIC_SRAM_UPPER_BOUND 0x00030000
-
-#define IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29)
-#define IPW_GP_CNTRL_BIT_CLOCK_READY 0x00000001
-#define IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002
-
-/*
- * RESET Register Bit Indexes
- */
-#define CBD_RESET_REG_PRINCETON_RESET (1<<0)
-#define IPW_START_STANDBY (1<<2)
-#define IPW_ACTIVITY_LED (1<<4)
-#define IPW_ASSOCIATED_LED (1<<5)
-#define IPW_OFDM_LED (1<<6)
-#define IPW_RESET_REG_SW_RESET (1<<7)
-#define IPW_RESET_REG_MASTER_DISABLED (1<<8)
-#define IPW_RESET_REG_STOP_MASTER (1<<9)
-#define IPW_GATE_ODMA (1<<25)
-#define IPW_GATE_IDMA (1<<26)
-#define IPW_ARC_KESHET_CONFIG (1<<27)
-#define IPW_GATE_ADMA (1<<29)
-
-#define IPW_CSR_CIS_UPPER_BOUND 0x00000200
-#define IPW_DOMAIN_0_END 0x1000
-#define CLX_MEM_BAR_SIZE 0x1000
-
-/* Dino/baseband control registers bits */
-
-#define DINO_ENABLE_SYSTEM 0x80 /* 1 = baseband processor on, 0 = reset */
-#define DINO_ENABLE_CS 0x40 /* 1 = enable ucode load */
-#define DINO_RXFIFO_DATA 0x01 /* 1 = data available */
-#define IPW_BASEBAND_CONTROL_STATUS 0X00200000
-#define IPW_BASEBAND_TX_FIFO_WRITE 0X00200004
-#define IPW_BASEBAND_RX_FIFO_READ 0X00200004
-#define IPW_BASEBAND_CONTROL_STORE 0X00200010
-
-#define IPW_INTERNAL_CMD_EVENT 0X00300004
-#define IPW_BASEBAND_POWER_DOWN 0x00000001
-
-#define IPW_MEM_HALT_AND_RESET 0x003000e0
-
-/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */
-#define IPW_BIT_HALT_RESET_ON 0x80000000
-#define IPW_BIT_HALT_RESET_OFF 0x00000000
-
-#define CB_LAST_VALID 0x20000000
-#define CB_INT_ENABLED 0x40000000
-#define CB_VALID 0x80000000
-#define CB_SRC_LE 0x08000000
-#define CB_DEST_LE 0x04000000
-#define CB_SRC_AUTOINC 0x00800000
-#define CB_SRC_IO_GATED 0x00400000
-#define CB_DEST_AUTOINC 0x00080000
-#define CB_SRC_SIZE_LONG 0x00200000
-#define CB_DEST_SIZE_LONG 0x00020000
-
-/* DMA DEFINES */
-
-#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000
-#define DMA_CB_STOP_AND_ABORT 0x00000C00
-#define DMA_CB_START 0x00000100
-
-#define IPW_SHARED_SRAM_SIZE 0x00030000
-#define IPW_SHARED_SRAM_DMA_CONTROL 0x00027000
-#define CB_MAX_LENGTH 0x1FFF
-
-#define IPW_HOST_EEPROM_DATA_SRAM_SIZE 0xA18
-#define IPW_EEPROM_IMAGE_SIZE 0x100
-
-/* DMA defs */
-#define IPW_DMA_I_CURRENT_CB 0x003000D0
-#define IPW_DMA_O_CURRENT_CB 0x003000D4
-#define IPW_DMA_I_DMA_CONTROL 0x003000A4
-#define IPW_DMA_I_CB_BASE 0x003000A0
-
-#define IPW_TX_CMD_QUEUE_BD_BASE 0x00000200
-#define IPW_TX_CMD_QUEUE_BD_SIZE 0x00000204
-#define IPW_TX_QUEUE_0_BD_BASE 0x00000208
-#define IPW_TX_QUEUE_0_BD_SIZE (0x0000020C)
-#define IPW_TX_QUEUE_1_BD_BASE 0x00000210
-#define IPW_TX_QUEUE_1_BD_SIZE 0x00000214
-#define IPW_TX_QUEUE_2_BD_BASE 0x00000218
-#define IPW_TX_QUEUE_2_BD_SIZE (0x0000021C)
-#define IPW_TX_QUEUE_3_BD_BASE 0x00000220
-#define IPW_TX_QUEUE_3_BD_SIZE 0x00000224
-#define IPW_RX_BD_BASE 0x00000240
-#define IPW_RX_BD_SIZE 0x00000244
-#define IPW_RFDS_TABLE_LOWER 0x00000500
-
-#define IPW_TX_CMD_QUEUE_READ_INDEX 0x00000280
-#define IPW_TX_QUEUE_0_READ_INDEX 0x00000284
-#define IPW_TX_QUEUE_1_READ_INDEX 0x00000288
-#define IPW_TX_QUEUE_2_READ_INDEX (0x0000028C)
-#define IPW_TX_QUEUE_3_READ_INDEX 0x00000290
-#define IPW_RX_READ_INDEX (0x000002A0)
-
-#define IPW_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80)
-#define IPW_TX_QUEUE_0_WRITE_INDEX (0x00000F84)
-#define IPW_TX_QUEUE_1_WRITE_INDEX (0x00000F88)
-#define IPW_TX_QUEUE_2_WRITE_INDEX (0x00000F8C)
-#define IPW_TX_QUEUE_3_WRITE_INDEX (0x00000F90)
-#define IPW_RX_WRITE_INDEX (0x00000FA0)
-
-/*
- * EEPROM Related Definitions
- */
-
-#define IPW_EEPROM_DATA_SRAM_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x814)
-#define IPW_EEPROM_DATA_SRAM_SIZE (IPW_SHARED_LOWER_BOUND + 0x818)
-#define IPW_EEPROM_LOAD_DISABLE (IPW_SHARED_LOWER_BOUND + 0x81C)
-#define IPW_EEPROM_DATA (IPW_SHARED_LOWER_BOUND + 0x820)
-#define IPW_EEPROM_UPPER_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x9E0)
-
-#define IPW_STATION_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0xA0C)
-#define IPW_STATION_TABLE_UPPER (IPW_SHARED_LOWER_BOUND + 0xB0C)
-#define IPW_REQUEST_ATIM (IPW_SHARED_LOWER_BOUND + 0xB0C)
-#define IPW_ATIM_SENT (IPW_SHARED_LOWER_BOUND + 0xB10)
-#define IPW_WHO_IS_AWAKE (IPW_SHARED_LOWER_BOUND + 0xB14)
-#define IPW_DURING_ATIM_WINDOW (IPW_SHARED_LOWER_BOUND + 0xB18)
-
-#define MSB 1
-#define LSB 0
-#define WORD_TO_BYTE(_word) ((_word) * sizeof(u16))
-
-#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \
- ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) )
-
-/* EEPROM access by BYTE */
-#define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */
-#define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */
-#define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */
-#define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */
-#define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */
-#define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */
-#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */
-#define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */
-#define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */
-#define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */
-
-/* NIC type as found in the one byte EEPROM_NIC_TYPE offset */
-#define EEPROM_NIC_TYPE_0 0
-#define EEPROM_NIC_TYPE_1 1
-#define EEPROM_NIC_TYPE_2 2
-#define EEPROM_NIC_TYPE_3 3
-#define EEPROM_NIC_TYPE_4 4
-
-/* Bluetooth Coexistence capabilities as found in EEPROM_SKU_CAPABILITY */
-#define EEPROM_SKU_CAP_BT_CHANNEL_SIG 0x01 /* we can tell BT our channel # */
-#define EEPROM_SKU_CAP_BT_PRIORITY 0x02 /* BT can take priority over us */
-#define EEPROM_SKU_CAP_BT_OOB 0x04 /* we can signal BT out-of-band */
-
-#define FW_MEM_REG_LOWER_BOUND 0x00300000
-#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40)
-#define IPW_EVENT_REG (FW_MEM_REG_LOWER_BOUND + 0x04)
-#define EEPROM_BIT_SK (1<<0)
-#define EEPROM_BIT_CS (1<<1)
-#define EEPROM_BIT_DI (1<<2)
-#define EEPROM_BIT_DO (1<<4)
-
-#define EEPROM_CMD_READ 0x2
-
-/* Interrupts masks */
-#define IPW_INTA_NONE 0x00000000
-
-#define IPW_INTA_BIT_RX_TRANSFER 0x00000002
-#define IPW_INTA_BIT_STATUS_CHANGE 0x00000010
-#define IPW_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020
-
-//Inta Bits for CF
-#define IPW_INTA_BIT_TX_CMD_QUEUE 0x00000800
-#define IPW_INTA_BIT_TX_QUEUE_1 0x00001000
-#define IPW_INTA_BIT_TX_QUEUE_2 0x00002000
-#define IPW_INTA_BIT_TX_QUEUE_3 0x00004000
-#define IPW_INTA_BIT_TX_QUEUE_4 0x00008000
-
-#define IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000
-
-#define IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000
-#define IPW_INTA_BIT_POWER_DOWN 0x00200000
-
-#define IPW_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000
-#define IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000
-#define IPW_INTA_BIT_RF_KILL_DONE 0x04000000
-#define IPW_INTA_BIT_FATAL_ERROR 0x40000000
-#define IPW_INTA_BIT_PARITY_ERROR 0x80000000
-
-/* Interrupts enabled at init time. */
-#define IPW_INTA_MASK_ALL \
- (IPW_INTA_BIT_TX_QUEUE_1 | \
- IPW_INTA_BIT_TX_QUEUE_2 | \
- IPW_INTA_BIT_TX_QUEUE_3 | \
- IPW_INTA_BIT_TX_QUEUE_4 | \
- IPW_INTA_BIT_TX_CMD_QUEUE | \
- IPW_INTA_BIT_RX_TRANSFER | \
- IPW_INTA_BIT_FATAL_ERROR | \
- IPW_INTA_BIT_PARITY_ERROR | \
- IPW_INTA_BIT_STATUS_CHANGE | \
- IPW_INTA_BIT_FW_INITIALIZATION_DONE | \
- IPW_INTA_BIT_BEACON_PERIOD_EXPIRED | \
- IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \
- IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN | \
- IPW_INTA_BIT_POWER_DOWN | \
- IPW_INTA_BIT_RF_KILL_DONE )
-
-/* FW event log definitions */
-#define EVENT_ELEM_SIZE (3 * sizeof(u32))
-#define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16))
-
-/* FW error log definitions */
-#define ERROR_ELEM_SIZE (7 * sizeof(u32))
-#define ERROR_START_OFFSET (1 * sizeof(u32))
-
-/* TX power level (dbm) */
-#define IPW_TX_POWER_MIN -12
-#define IPW_TX_POWER_MAX 20
-#define IPW_TX_POWER_DEFAULT IPW_TX_POWER_MAX
-
-enum {
- IPW_FW_ERROR_OK = 0,
- IPW_FW_ERROR_FAIL,
- IPW_FW_ERROR_MEMORY_UNDERFLOW,
- IPW_FW_ERROR_MEMORY_OVERFLOW,
- IPW_FW_ERROR_BAD_PARAM,
- IPW_FW_ERROR_BAD_CHECKSUM,
- IPW_FW_ERROR_NMI_INTERRUPT,
- IPW_FW_ERROR_BAD_DATABASE,
- IPW_FW_ERROR_ALLOC_FAIL,
- IPW_FW_ERROR_DMA_UNDERRUN,
- IPW_FW_ERROR_DMA_STATUS,
- IPW_FW_ERROR_DINO_ERROR,
- IPW_FW_ERROR_EEPROM_ERROR,
- IPW_FW_ERROR_SYSASSERT,
- IPW_FW_ERROR_FATAL_ERROR
-};
-
-#define AUTH_OPEN 0
-#define AUTH_SHARED_KEY 1
-#define AUTH_LEAP 2
-#define AUTH_IGNORE 3
-
-#define HC_ASSOCIATE 0
-#define HC_REASSOCIATE 1
-#define HC_DISASSOCIATE 2
-#define HC_IBSS_START 3
-#define HC_IBSS_RECONF 4
-#define HC_DISASSOC_QUIET 5
-
-#define HC_QOS_SUPPORT_ASSOC cpu_to_le16(0x01)
-
-#define IPW_RATE_CAPABILITIES 1
-#define IPW_RATE_CONNECT 0
-
-/*
- * Rate values and masks
- */
-#define IPW_TX_RATE_1MB 0x0A
-#define IPW_TX_RATE_2MB 0x14
-#define IPW_TX_RATE_5MB 0x37
-#define IPW_TX_RATE_6MB 0x0D
-#define IPW_TX_RATE_9MB 0x0F
-#define IPW_TX_RATE_11MB 0x6E
-#define IPW_TX_RATE_12MB 0x05
-#define IPW_TX_RATE_18MB 0x07
-#define IPW_TX_RATE_24MB 0x09
-#define IPW_TX_RATE_36MB 0x0B
-#define IPW_TX_RATE_48MB 0x01
-#define IPW_TX_RATE_54MB 0x03
-
-#define IPW_ORD_TABLE_ID_MASK 0x0000FF00
-#define IPW_ORD_TABLE_VALUE_MASK 0x000000FF
-
-#define IPW_ORD_TABLE_0_MASK 0x0000F000
-#define IPW_ORD_TABLE_1_MASK 0x0000F100
-#define IPW_ORD_TABLE_2_MASK 0x0000F200
-#define IPW_ORD_TABLE_3_MASK 0x0000F300
-#define IPW_ORD_TABLE_4_MASK 0x0000F400
-#define IPW_ORD_TABLE_5_MASK 0x0000F500
-#define IPW_ORD_TABLE_6_MASK 0x0000F600
-#define IPW_ORD_TABLE_7_MASK 0x0000F700
-
-/*
- * Table 0 Entries (all entries are 32 bits)
- */
-enum {
- IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1,
- IPW_ORD_STAT_FRAG_TRESHOLD,
- IPW_ORD_STAT_RTS_THRESHOLD,
- IPW_ORD_STAT_TX_HOST_REQUESTS,
- IPW_ORD_STAT_TX_HOST_COMPLETE,
- IPW_ORD_STAT_TX_DIR_DATA,
- IPW_ORD_STAT_TX_DIR_DATA_B_1,
- IPW_ORD_STAT_TX_DIR_DATA_B_2,
- IPW_ORD_STAT_TX_DIR_DATA_B_5_5,
- IPW_ORD_STAT_TX_DIR_DATA_B_11,
- /* Hole */
-
- IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19,
- IPW_ORD_STAT_TX_DIR_DATA_G_2,
- IPW_ORD_STAT_TX_DIR_DATA_G_5_5,
- IPW_ORD_STAT_TX_DIR_DATA_G_6,
- IPW_ORD_STAT_TX_DIR_DATA_G_9,
- IPW_ORD_STAT_TX_DIR_DATA_G_11,
- IPW_ORD_STAT_TX_DIR_DATA_G_12,
- IPW_ORD_STAT_TX_DIR_DATA_G_18,
- IPW_ORD_STAT_TX_DIR_DATA_G_24,
- IPW_ORD_STAT_TX_DIR_DATA_G_36,
- IPW_ORD_STAT_TX_DIR_DATA_G_48,
- IPW_ORD_STAT_TX_DIR_DATA_G_54,
- IPW_ORD_STAT_TX_NON_DIR_DATA,
- IPW_ORD_STAT_TX_NON_DIR_DATA_B_1,
- IPW_ORD_STAT_TX_NON_DIR_DATA_B_2,
- IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5,
- IPW_ORD_STAT_TX_NON_DIR_DATA_B_11,
- /* Hole */
-
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_2,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_6,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_9,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_11,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_12,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_18,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_24,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_36,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_48,
- IPW_ORD_STAT_TX_NON_DIR_DATA_G_54,
- IPW_ORD_STAT_TX_RETRY,
- IPW_ORD_STAT_TX_FAILURE,
- IPW_ORD_STAT_RX_ERR_CRC,
- IPW_ORD_STAT_RX_ERR_ICV,
- IPW_ORD_STAT_RX_NO_BUFFER,
- IPW_ORD_STAT_FULL_SCANS,
- IPW_ORD_STAT_PARTIAL_SCANS,
- IPW_ORD_STAT_TGH_ABORTED_SCANS,
- IPW_ORD_STAT_TX_TOTAL_BYTES,
- IPW_ORD_STAT_CURR_RSSI_RAW,
- IPW_ORD_STAT_RX_BEACON,
- IPW_ORD_STAT_MISSED_BEACONS,
- IPW_ORD_TABLE_0_LAST
-};
-
-#define IPW_RSSI_TO_DBM 112
-
-/* Table 1 Entries
- */
-enum {
- IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1,
-};
-
-/*
- * Table 2 Entries
- *
- * FW_VERSION: 16 byte string
- * FW_DATE: 16 byte string (only 14 bytes used)
- * UCODE_VERSION: 4 byte version code
- * UCODE_DATE: 5 bytes code code
- * ADDAPTER_MAC: 6 byte MAC address
- * RTC: 4 byte clock
- */
-enum {
- IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1,
- IPW_ORD_STAT_FW_DATE,
- IPW_ORD_STAT_UCODE_VERSION,
- IPW_ORD_STAT_UCODE_DATE,
- IPW_ORD_STAT_ADAPTER_MAC,
- IPW_ORD_STAT_RTC,
- IPW_ORD_TABLE_2_LAST
-};
-
-/* Table 3 */
-enum {
- IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0,
- IPW_ORD_STAT_TX_PACKET_FAILURE,
- IPW_ORD_STAT_TX_PACKET_SUCCESS,
- IPW_ORD_STAT_TX_PACKET_ABORTED,
- IPW_ORD_TABLE_3_LAST
-};
-
-/* Table 4 */
-enum {
- IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK
-};
-
-/* Table 5 */
-enum {
- IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK,
- IPW_ORD_STAT_AP_ASSNS,
- IPW_ORD_STAT_ROAM,
- IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS,
- IPW_ORD_STAT_ROAM_CAUSE_UNASSOC,
- IPW_ORD_STAT_ROAM_CAUSE_RSSI,
- IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY,
- IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE,
- IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX,
- IPW_ORD_STAT_LINK_UP,
- IPW_ORD_STAT_LINK_DOWN,
- IPW_ORD_ANTENNA_DIVERSITY,
- IPW_ORD_CURR_FREQ,
- IPW_ORD_TABLE_5_LAST
-};
-
-/* Table 6 */
-enum {
- IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK,
- IPW_ORD_CURR_BSSID,
- IPW_ORD_CURR_SSID,
- IPW_ORD_TABLE_6_LAST
-};
-
-/* Table 7 */
-enum {
- IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK,
- IPW_ORD_STAT_PERCENT_TX_RETRIES,
- IPW_ORD_STAT_PERCENT_LINK_QUALITY,
- IPW_ORD_STAT_CURR_RSSI_DBM,
- IPW_ORD_TABLE_7_LAST
-};
-
-#define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410)
-#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414)
-#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500)
-#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180)
-#define IPW_ORDINALS_TABLE_1 (IPW_SHARED_LOWER_BOUND + 0x184)
-#define IPW_ORDINALS_TABLE_2 (IPW_SHARED_LOWER_BOUND + 0x188)
-#define IPW_MEM_FIXED_OVERRIDE (IPW_SHARED_LOWER_BOUND + 0x41C)
-
-struct ipw_fixed_rate {
- __le16 tx_rates;
- __le16 reserved;
-} __attribute__ ((packed));
-
-#define IPW_INDIRECT_ADDR_MASK (~0x3ul)
-
-struct host_cmd {
- u8 cmd;
- u8 len;
- u16 reserved;
- u32 *param;
-} __attribute__ ((packed)); /* XXX */
-
-struct cmdlog_host_cmd {
- u8 cmd;
- u8 len;
- __le16 reserved;
- char param[124];
-} __attribute__ ((packed));
-
-struct ipw_cmd_log {
- unsigned long jiffies;
- int retcode;
- struct cmdlog_host_cmd cmd;
-};
-
-/* SysConfig command parameters ... */
-/* bt_coexistence param */
-#define CFG_BT_COEXISTENCE_SIGNAL_CHNL 0x01 /* tell BT our chnl # */
-#define CFG_BT_COEXISTENCE_DEFER 0x02 /* defer our Tx if BT traffic */
-#define CFG_BT_COEXISTENCE_KILL 0x04 /* kill our Tx if BT traffic */
-#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 /* multimedia extensions */
-#define CFG_BT_COEXISTENCE_OOB 0x10 /* signal BT via out-of-band */
-
-/* clear-to-send to self param */
-#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x00
-#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x01
-#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN
-
-/* Antenna diversity param (h/w can select best antenna, based on signal) */
-#define CFG_SYS_ANTENNA_BOTH 0x00 /* NIC selects best antenna */
-#define CFG_SYS_ANTENNA_A 0x01 /* force antenna A */
-#define CFG_SYS_ANTENNA_B 0x03 /* force antenna B */
-#define CFG_SYS_ANTENNA_SLOW_DIV 0x02 /* consider background noise */
-
-/*
- * The definitions below were lifted off the ipw2100 driver, which only
- * supports 'b' mode, so I'm sure these are not exactly correct.
- *
- * Somebody fix these!!
- */
-#define REG_MIN_CHANNEL 0
-#define REG_MAX_CHANNEL 14
-
-#define REG_CHANNEL_MASK 0x00003FFF
-#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff
-
-#define IPW_MAX_CONFIG_RETRIES 10
-
-#endif /* __ipw2200_h__ */
diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig
new file mode 100644
index 000000000000..3d5cc4463d4d
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/Kconfig
@@ -0,0 +1,191 @@
+#
+# Intel Centrino wireless drivers
+#
+
+config IPW2100
+ tristate "Intel PRO/Wireless 2100 Network Connection"
+ depends on PCI && WLAN_80211
+ select WIRELESS_EXT
+ select FW_LOADER
+ select LIB80211
+ select LIBIPW
+ ---help---
+ A driver for the Intel PRO/Wireless 2100 Network
+ Connection 802.11b wireless network adapter.
+
+ See <file:Documentation/networking/README.ipw2100> for information on
+ the capabilities currently enabled in this driver and for tips
+ for debugging issues and problems.
+
+ In order to use this driver, you will need a firmware image for it.
+ You can obtain the firmware from
+ <http://ipw2100.sf.net/>. Once you have the firmware image, you
+ will need to place it in /lib/firmware.
+
+ You will also very likely need the Wireless Tools in order to
+ configure your card:
+
+ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+ It is recommended that you compile this driver as a module (M)
+ rather than built-in (Y). This driver requires firmware at device
+ initialization time, and when built-in this typically happens
+ before the filesystem is accessible (hence firmware will be
+ unavailable and initialization will fail). If you do choose to build
+ this driver into your kernel image, you can avoid this problem by
+ including the firmware and a firmware loader in an initramfs.
+
+config IPW2100_MONITOR
+ bool "Enable promiscuous mode"
+ depends on IPW2100
+ ---help---
+ Enables promiscuous/monitor mode support for the ipw2100 driver.
+ With this feature compiled into the driver, you can switch to
+ promiscuous mode via the Wireless Tool's Monitor mode. While in this
+ mode, no packets can be sent.
+
+config IPW2100_DEBUG
+ bool "Enable full debugging output in IPW2100 module."
+ depends on IPW2100
+ ---help---
+ This option will enable debug tracing output for the IPW2100.
+
+ This will result in the kernel module being ~60k larger. You can
+ control which debug output is sent to the kernel log by setting the
+ value in
+
+ /sys/bus/pci/drivers/ipw2100/debug_level
+
+ This entry will only exist if this option is enabled.
+
+ If you are not trying to debug or develop the IPW2100 driver, you
+ most likely want to say N here.
+
+config IPW2200
+ tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
+ depends on PCI && WLAN_80211
+ select WIRELESS_EXT
+ select FW_LOADER
+ select LIB80211
+ select LIBIPW
+ ---help---
+ A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
+ Connection adapters.
+
+ See <file:Documentation/networking/README.ipw2200> for
+ information on the capabilities currently enabled in this
+ driver and for tips for debugging issues and problems.
+
+ In order to use this driver, you will need a firmware image for it.
+ You can obtain the firmware from
+ <http://ipw2200.sf.net/>. See the above referenced README.ipw2200
+ for information on where to install the firmware images.
+
+ You will also very likely need the Wireless Tools in order to
+ configure your card:
+
+ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+
+ It is recommended that you compile this driver as a module (M)
+ rather than built-in (Y). This driver requires firmware at device
+ initialization time, and when built-in this typically happens
+ before the filesystem is accessible (hence firmware will be
+ unavailable and initialization will fail). If you do choose to build
+ this driver into your kernel image, you can avoid this problem by
+ including the firmware and a firmware loader in an initramfs.
+
+config IPW2200_MONITOR
+ bool "Enable promiscuous mode"
+ depends on IPW2200
+ ---help---
+ Enables promiscuous/monitor mode support for the ipw2200 driver.
+ With this feature compiled into the driver, you can switch to
+ promiscuous mode via the Wireless Tool's Monitor mode. While in this
+ mode, no packets can be sent.
+
+config IPW2200_RADIOTAP
+ bool "Enable radiotap format 802.11 raw packet support"
+ depends on IPW2200_MONITOR
+
+config IPW2200_PROMISCUOUS
+ bool "Enable creation of a RF radiotap promiscuous interface"
+ depends on IPW2200_MONITOR
+ select IPW2200_RADIOTAP
+ ---help---
+ Enables the creation of a second interface prefixed 'rtap'.
+ This second interface will provide every received in radiotap
+ format.
+
+ This is useful for performing wireless network analysis while
+ maintaining an active association.
+
+ Example usage:
+
+ % modprobe ipw2200 rtap_iface=1
+ % ifconfig rtap0 up
+ % tethereal -i rtap0
+
+ If you do not specify 'rtap_iface=1' as a module parameter then
+ the rtap interface will not be created and you will need to turn
+ it on via sysfs:
+
+ % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface
+
+config IPW2200_QOS
+ bool "Enable QoS support"
+ depends on IPW2200 && EXPERIMENTAL
+
+config IPW2200_DEBUG
+ bool "Enable full debugging output in IPW2200 module."
+ depends on IPW2200
+ ---help---
+ This option will enable low level debug tracing output for IPW2200.
+
+ Note, normal debug code is already compiled in. This low level
+ debug option enables debug on hot paths (e.g Tx, Rx, ISR) and
+ will result in the kernel module being ~70 larger. Most users
+ will typically not need this high verbosity debug information.
+
+ If you are not sure, say N here.
+
+config LIBIPW
+ tristate
+ select WIRELESS_EXT
+ select CRYPTO
+ select CRYPTO_ARC4
+ select CRYPTO_ECB
+ select CRYPTO_AES
+ select CRYPTO_MICHAEL_MIC
+ select CRYPTO_ECB
+ select CRC32
+ select LIB80211
+ select LIB80211_CRYPT_WEP
+ select LIB80211_CRYPT_TKIP
+ select LIB80211_CRYPT_CCMP
+ ---help---
+ This option enables the hardware independent IEEE 802.11
+ networking stack. This component is deprecated in favor of the
+ mac80211 component.
+
+config LIBIPW_DEBUG
+ bool "Full debugging output for the LIBIPW component"
+ depends on LIBIPW
+ ---help---
+ This option will enable debug tracing output for the
+ libipw component.
+
+ This will result in the kernel module being ~70k larger. You
+ can control which debug output is sent to the kernel log by
+ setting the value in
+
+ /proc/net/ieee80211/debug_level
+
+ For example:
+
+ % echo 0x00000FFO > /proc/net/ieee80211/debug_level
+
+ For a list of values you can assign to debug_level, you
+ can look at the bit mask values in <net/ieee80211.h>
+
+ If you are not trying to debug or develop the libipw
+ component, you most likely want to say N here.
diff --git a/drivers/net/wireless/ipw2x00/Makefile b/drivers/net/wireless/ipw2x00/Makefile
new file mode 100644
index 000000000000..aecd2cff462b
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the Intel Centrino wireless drivers
+#
+
+obj-$(CONFIG_IPW2100) += ipw2100.o
+obj-$(CONFIG_IPW2200) += ipw2200.o
+
+obj-$(CONFIG_LIBIPW) += libipw.o
+libipw-objs := \
+ libipw_module.o \
+ libipw_tx.o \
+ libipw_rx.o \
+ libipw_wx.o \
+ libipw_geo.o
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
new file mode 100644
index 000000000000..1667065b86a7
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -0,0 +1,8649 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+ Portions of this file are based on the sample_* files provided by Wireless
+ Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes
+ <jt@hpl.hp.com>
+
+ Portions of this file are based on the Host AP project,
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <j@w1.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+
+ Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and
+ ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c
+ available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox
+
+******************************************************************************/
+/*
+
+ Initial driver on which this is based was developed by Janusz Gorycki,
+ Maciej Urbaniak, and Maciej Sosnowski.
+
+ Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak.
+
+Theory of Operation
+
+Tx - Commands and Data
+
+Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs)
+Each TBD contains a pointer to the physical (dma_addr_t) address of data being
+sent to the firmware as well as the length of the data.
+
+The host writes to the TBD queue at the WRITE index. The WRITE index points
+to the _next_ packet to be written and is advanced when after the TBD has been
+filled.
+
+The firmware pulls from the TBD queue at the READ index. The READ index points
+to the currently being read entry, and is advanced once the firmware is
+done with a packet.
+
+When data is sent to the firmware, the first TBD is used to indicate to the
+firmware if a Command or Data is being sent. If it is Command, all of the
+command information is contained within the physical address referred to by the
+TBD. If it is Data, the first TBD indicates the type of data packet, number
+of fragments, etc. The next TBD then referrs to the actual packet location.
+
+The Tx flow cycle is as follows:
+
+1) ipw2100_tx() is called by kernel with SKB to transmit
+2) Packet is move from the tx_free_list and appended to the transmit pending
+ list (tx_pend_list)
+3) work is scheduled to move pending packets into the shared circular queue.
+4) when placing packet in the circular queue, the incoming SKB is DMA mapped
+ to a physical address. That address is entered into a TBD. Two TBDs are
+ filled out. The first indicating a data packet, the second referring to the
+ actual payload data.
+5) the packet is removed from tx_pend_list and placed on the end of the
+ firmware pending list (fw_pend_list)
+6) firmware is notified that the WRITE index has
+7) Once the firmware has processed the TBD, INTA is triggered.
+8) For each Tx interrupt received from the firmware, the READ index is checked
+ to see which TBDs are done being processed.
+9) For each TBD that has been processed, the ISR pulls the oldest packet
+ from the fw_pend_list.
+10)The packet structure contained in the fw_pend_list is then used
+ to unmap the DMA address and to free the SKB originally passed to the driver
+ from the kernel.
+11)The packet structure is placed onto the tx_free_list
+
+The above steps are the same for commands, only the msg_free_list/msg_pend_list
+are used instead of tx_free_list/tx_pend_list
+
+...
+
+Critical Sections / Locking :
+
+There are two locks utilized. The first is the low level lock (priv->low_lock)
+that protects the following:
+
+- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows:
+
+ tx_free_list : Holds pre-allocated Tx buffers.
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_tx()
+
+ tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring
+ TAIL modified ipw2100_tx()
+ HEAD modified by ipw2100_tx_send_data()
+
+ msg_free_list : Holds pre-allocated Msg (Command) buffers
+ TAIL modified in __ipw2100_tx_process()
+ HEAD modified in ipw2100_hw_send_command()
+
+ msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring
+ TAIL modified in ipw2100_hw_send_command()
+ HEAD modified in ipw2100_tx_send_commands()
+
+ The flow of data on the TX side is as follows:
+
+ MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST
+ TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST
+
+ The methods that work on the TBD ring are protected via priv->low_lock.
+
+- The internal data state of the device itself
+- Access to the firmware read/write indexes for the BD queues
+ and associated logic
+
+All external entry functions are locked with the priv->action_lock to ensure
+that only one external action is invoked at a time.
+
+
+*/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/stringify.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/firmware.h>
+#include <linux/acpi.h>
+#include <linux/ctype.h>
+#include <linux/pm_qos_params.h>
+
+#include <net/lib80211.h>
+
+#include "ipw2100.h"
+
+#define IPW2100_VERSION "git-1.2.2"
+
+#define DRV_NAME "ipw2100"
+#define DRV_VERSION IPW2100_VERSION
+#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
+
+/* Debugging stuff */
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW2100_RX_DEBUG /* Reception debugging */
+#endif
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+static int mode = 0;
+static int channel = 0;
+static int associate = 0;
+static int disable = 0;
+#ifdef CONFIG_PM
+static struct ipw2100_fw ipw2100_firmware;
+#endif
+
+#include <linux/moduleparam.h>
+module_param(debug, int, 0444);
+module_param(mode, int, 0444);
+module_param(channel, int, 0444);
+module_param(associate, int, 0444);
+module_param(disable, int, 0444);
+
+MODULE_PARM_DESC(debug, "debug level");
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+MODULE_PARM_DESC(channel, "channel");
+MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+static u32 ipw2100_debug_level = IPW_DL_NONE;
+
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW_DEBUG(level, message...) \
+do { \
+ if (ipw2100_debug_level & (level)) { \
+ printk(KERN_DEBUG "ipw2100: %c %s ", \
+ in_interrupt() ? 'I' : 'U', __func__); \
+ printk(message); \
+ } \
+} while (0)
+#else
+#define IPW_DEBUG(level, message...) do {} while (0)
+#endif /* CONFIG_IPW2100_DEBUG */
+
+#ifdef CONFIG_IPW2100_DEBUG
+static const char *command_types[] = {
+ "undefined",
+ "unused", /* HOST_ATTENTION */
+ "HOST_COMPLETE",
+ "unused", /* SLEEP */
+ "unused", /* HOST_POWER_DOWN */
+ "unused",
+ "SYSTEM_CONFIG",
+ "unused", /* SET_IMR */
+ "SSID",
+ "MANDATORY_BSSID",
+ "AUTHENTICATION_TYPE",
+ "ADAPTER_ADDRESS",
+ "PORT_TYPE",
+ "INTERNATIONAL_MODE",
+ "CHANNEL",
+ "RTS_THRESHOLD",
+ "FRAG_THRESHOLD",
+ "POWER_MODE",
+ "TX_RATES",
+ "BASIC_TX_RATES",
+ "WEP_KEY_INFO",
+ "unused",
+ "unused",
+ "unused",
+ "unused",
+ "WEP_KEY_INDEX",
+ "WEP_FLAGS",
+ "ADD_MULTICAST",
+ "CLEAR_ALL_MULTICAST",
+ "BEACON_INTERVAL",
+ "ATIM_WINDOW",
+ "CLEAR_STATISTICS",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "TX_POWER_INDEX",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "BROADCAST_SCAN",
+ "CARD_DISABLE",
+ "PREFERRED_BSSID",
+ "SET_SCAN_OPTIONS",
+ "SCAN_DWELL_TIME",
+ "SWEEP_TABLE",
+ "AP_OR_STATION_TABLE",
+ "GROUP_ORDINALS",
+ "SHORT_RETRY_LIMIT",
+ "LONG_RETRY_LIMIT",
+ "unused", /* SAVE_CALIBRATION */
+ "unused", /* RESTORE_CALIBRATION */
+ "undefined",
+ "undefined",
+ "undefined",
+ "HOST_PRE_POWER_DOWN",
+ "unused", /* HOST_INTERRUPT_COALESCING */
+ "undefined",
+ "CARD_DISABLE_PHY_OFF",
+ "MSDU_TX_RATES" "undefined",
+ "undefined",
+ "SET_STATION_STAT_BITS",
+ "CLEAR_STATIONS_STAT_BITS",
+ "LEAP_ROGUE_MODE",
+ "SET_SECURITY_INFORMATION",
+ "DISASSOCIATION_BSSID",
+ "SET_WPA_ASS_IE"
+};
+#endif
+
+/* Pre-decl until we get the code solid and then we can clean it up */
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv);
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv);
+static void ipw2100_queues_free(struct ipw2100_priv *priv);
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv);
+
+static int ipw2100_fw_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max);
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw);
+static void ipw2100_wx_event_work(struct work_struct *work);
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev);
+static struct iw_handler_def ipw2100_wx_handler_def;
+
+static inline void read_register(struct net_device *dev, u32 reg, u32 * val)
+{
+ *val = readl((void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val);
+}
+
+static inline void write_register(struct net_device *dev, u32 reg, u32 val)
+{
+ writel(val, (void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val);
+}
+
+static inline void read_register_word(struct net_device *dev, u32 reg,
+ u16 * val)
+{
+ *val = readw((void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val);
+}
+
+static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val)
+{
+ *val = readb((void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val);
+}
+
+static inline void write_register_word(struct net_device *dev, u32 reg, u16 val)
+{
+ writew(val, (void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val);
+}
+
+static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val)
+{
+ writeb(val, (void __iomem *)(dev->base_addr + reg));
+ IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val);
+}
+
+static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val)
+{
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+ write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val);
+}
+
+static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS,
+ addr & IPW_REG_INDIRECT_ADDR_MASK);
+}
+
+static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val)
+{
+ write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val);
+}
+
+static void write_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ const u8 * buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ write_register_byte(dev,
+ IPW_REG_INDIRECT_ACCESS_DATA + i,
+ *buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i,
+ *buf);
+}
+
+static void read_nic_memory(struct net_device *dev, u32 addr, u32 len,
+ u8 * buf)
+{
+ u32 aligned_addr;
+ u32 aligned_len;
+ u32 dif_len;
+ u32 i;
+
+ /* read first nibble byte by byte */
+ aligned_addr = addr & (~0x3);
+ dif_len = addr - aligned_addr;
+ if (dif_len) {
+ /* Start reading at aligned_addr + dif_len */
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS,
+ aligned_addr);
+ for (i = dif_len; i < 4; i++, buf++)
+ read_register_byte(dev,
+ IPW_REG_INDIRECT_ACCESS_DATA + i,
+ buf);
+
+ len -= dif_len;
+ aligned_addr += 4;
+ }
+
+ /* read DWs through autoincrement registers */
+ write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr);
+ aligned_len = len & (~0x3);
+ for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4)
+ read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf);
+
+ /* copy the last nibble */
+ dif_len = len - aligned_len;
+ write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr);
+ for (i = 0; i < dif_len; i++, buf++)
+ read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf);
+}
+
+static inline int ipw2100_hw_is_adapter_in_system(struct net_device *dev)
+{
+ return (dev->base_addr &&
+ (readl
+ ((void __iomem *)(dev->base_addr +
+ IPW_REG_DOA_DEBUG_AREA_START))
+ == IPW_DATA_DOA_DEBUG_VALUE));
+}
+
+static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
+ void *val, u32 * len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+ u32 field_info;
+ u16 field_len;
+ u16 field_count;
+ u32 total_length;
+
+ if (ordinals->table1_addr == 0) {
+ printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals "
+ "before they have been loaded.\n");
+ return -EINVAL;
+ }
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ printk(KERN_WARNING DRV_NAME
+ ": ordinal buffer length too small, need %zd\n",
+ IPW_ORD_TAB_1_ENTRY_SIZE);
+
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev,
+ ordinals->table1_addr + (ord << 2), &addr);
+ read_nic_dword(priv->net_dev, addr, val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) {
+
+ ord -= IPW_START_ORD_TAB_2;
+
+ /* get the address of statistic */
+ read_nic_dword(priv->net_dev,
+ ordinals->table2_addr + (ord << 3), &addr);
+
+ /* get the second DW of statistics ;
+ * two 16-bit words - first is length, second is count */
+ read_nic_dword(priv->net_dev,
+ ordinals->table2_addr + (ord << 3) + sizeof(u32),
+ &field_info);
+
+ /* get each entry length */
+ field_len = *((u16 *) & field_info);
+
+ /* get number of entries */
+ field_count = *(((u16 *) & field_info) + 1);
+
+ /* abort if no enought memory */
+ total_length = field_len * field_count;
+ if (total_length > *len) {
+ *len = total_length;
+ return -EINVAL;
+ }
+
+ *len = total_length;
+ if (!total_length)
+ return 0;
+
+ /* read the ordinal data from the SRAM */
+ read_nic_memory(priv->net_dev, addr, total_length, val);
+
+ return 0;
+ }
+
+ printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor "
+ "in table 2\n", ord);
+
+ return -EINVAL;
+}
+
+static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val,
+ u32 * len)
+{
+ struct ipw2100_ordinals *ordinals = &priv->ordinals;
+ u32 addr;
+
+ if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) {
+ if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) {
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+ IPW_DEBUG_INFO("wrong size\n");
+ return -EINVAL;
+ }
+
+ read_nic_dword(priv->net_dev,
+ ordinals->table1_addr + (ord << 2), &addr);
+
+ write_nic_dword(priv->net_dev, addr, *val);
+
+ *len = IPW_ORD_TAB_1_ENTRY_SIZE;
+
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("wrong table\n");
+ if (IS_ORDINAL_TABLE_TWO(ordinals, ord))
+ return -EINVAL;
+
+ return -EINVAL;
+}
+
+static char *snprint_line(char *buf, size_t count,
+ const u8 * data, u32 len, u32 ofs)
+{
+ int out, i, j, l;
+ char c;
+
+ out = snprintf(buf, count, "%08X", ofs);
+
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++)
+ out += snprintf(buf + out, count - out, "%02X ",
+ data[(i * 8 + j)]);
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ out += snprintf(buf + out, count - out, " ");
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++) {
+ c = data[(i * 8 + j)];
+ if (!isascii(c) || !isprint(c))
+ c = '.';
+
+ out += snprintf(buf + out, count - out, "%c", c);
+ }
+
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ return buf;
+}
+
+static void printk_buf(int level, const u8 * data, u32 len)
+{
+ char line[81];
+ u32 ofs = 0;
+ if (!(ipw2100_debug_level & level))
+ return;
+
+ while (len) {
+ printk(KERN_DEBUG "%s\n",
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs));
+ ofs += 16;
+ len -= min(len, 16U);
+ }
+}
+
+#define MAX_RESET_BACKOFF 10
+
+static void schedule_reset(struct ipw2100_priv *priv)
+{
+ unsigned long now = get_seconds();
+
+ /* If we haven't received a reset request within the backoff period,
+ * then we can reset the backoff interval so this reset occurs
+ * immediately */
+ if (priv->reset_backoff &&
+ (now - priv->last_reset > priv->reset_backoff))
+ priv->reset_backoff = 0;
+
+ priv->last_reset = get_seconds();
+
+ if (!(priv->status & STATUS_RESET_PENDING)) {
+ IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n",
+ priv->net_dev->name, priv->reset_backoff);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+ priv->status |= STATUS_RESET_PENDING;
+ if (priv->reset_backoff)
+ queue_delayed_work(priv->workqueue, &priv->reset_work,
+ priv->reset_backoff * HZ);
+ else
+ queue_delayed_work(priv->workqueue, &priv->reset_work,
+ 0);
+
+ if (priv->reset_backoff < MAX_RESET_BACKOFF)
+ priv->reset_backoff++;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+ } else
+ IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n",
+ priv->net_dev->name);
+
+}
+
+#define HOST_COMPLETE_TIMEOUT (2 * HZ)
+static int ipw2100_hw_send_command(struct ipw2100_priv *priv,
+ struct host_command *cmd)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+ int err = 0;
+
+ IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n",
+ command_types[cmd->host_command], cmd->host_command,
+ cmd->host_command_length);
+ printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters,
+ cmd->host_command_length);
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while hardware in fatal error condition.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (!(priv->status & STATUS_RUNNING)) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while hardware is not running.\n");
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ if (priv->status & STATUS_CMD_ACTIVE) {
+ IPW_DEBUG_INFO
+ ("Attempt to send command while another command is pending.\n");
+ err = -EBUSY;
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->msg_free_list)) {
+ IPW_DEBUG_INFO("no available msg buffers\n");
+ goto fail_unlock;
+ }
+
+ priv->status |= STATUS_CMD_ACTIVE;
+ priv->messages_sent++;
+
+ element = priv->msg_free_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ packet->jiffy_start = jiffies;
+
+ /* initialize the firmware command packet */
+ packet->info.c_struct.cmd->host_command_reg = cmd->host_command;
+ packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1;
+ packet->info.c_struct.cmd->host_command_len_reg =
+ cmd->host_command_length;
+ packet->info.c_struct.cmd->sequence = cmd->host_command_sequence;
+
+ memcpy(packet->info.c_struct.cmd->host_command_params_reg,
+ cmd->host_command_parameters,
+ sizeof(packet->info.c_struct.cmd->host_command_params_reg));
+
+ list_del(element);
+ DEC_STAT(&priv->msg_free_stat);
+
+ list_add_tail(element, &priv->msg_pend_list);
+ INC_STAT(&priv->msg_pend_stat);
+
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ /*
+ * We must wait for this command to complete before another
+ * command can be sent... but if we wait more than 3 seconds
+ * then there is a problem.
+ */
+
+ err =
+ wait_event_interruptible_timeout(priv->wait_command_queue,
+ !(priv->
+ status & STATUS_CMD_ACTIVE),
+ HOST_COMPLETE_TIMEOUT);
+
+ if (err == 0) {
+ IPW_DEBUG_INFO("Command completion failed out after %dms.\n",
+ 1000 * (HOST_COMPLETE_TIMEOUT / HZ));
+ priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT;
+ priv->status &= ~STATUS_CMD_ACTIVE;
+ schedule_reset(priv);
+ return -EIO;
+ }
+
+ if (priv->fatal_error) {
+ printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* !!!!! HACK TEST !!!!!
+ * When lots of debug trace statements are enabled, the driver
+ * doesn't seem to have as many firmware restart cycles...
+ *
+ * As a test, we're sticking in a 1/100s delay here */
+ schedule_timeout_uninterruptible(msecs_to_jiffies(10));
+
+ return 0;
+
+ fail_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return err;
+}
+
+/*
+ * Verify the values and data access of the hardware
+ * No locks needed or used. No functions called.
+ */
+static int ipw2100_verify(struct ipw2100_priv *priv)
+{
+ u32 data1, data2;
+ u32 address;
+
+ u32 val1 = 0x76543210;
+ u32 val2 = 0xFEDCBA98;
+
+ /* Domain 0 check - all values should be DOA_DEBUG */
+ for (address = IPW_REG_DOA_DEBUG_AREA_START;
+ address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) {
+ read_register(priv->net_dev, address, &data1);
+ if (data1 != IPW_DATA_DOA_DEBUG_VALUE)
+ return -EIO;
+ }
+
+ /* Domain 1 check - use arbitrary read/write compare */
+ for (address = 0; address < 5; address++) {
+ /* The memory area is not used now */
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ val1);
+ write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ val2);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32,
+ &data1);
+ read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36,
+ &data2);
+ if (val1 == data1 && val2 == data2)
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ *
+ * Loop until the CARD_DISABLED bit is the same value as the
+ * supplied parameter
+ *
+ * TODO: See if it would be more efficient to do a wait/wake
+ * cycle and have the completion event trigger the wakeup
+ *
+ */
+#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli
+static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state)
+{
+ int i;
+ u32 card_state;
+ u32 len = sizeof(card_state);
+ int err;
+
+ for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) {
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED,
+ &card_state, &len);
+ if (err) {
+ IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal "
+ "failed.\n");
+ return 0;
+ }
+
+ /* We'll break out if either the HW state says it is
+ * in the state we want, or if HOST_COMPLETE command
+ * finishes */
+ if ((card_state == state) ||
+ ((priv->status & STATUS_ENABLED) ?
+ IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) {
+ if (state == IPW_HW_STATE_ENABLED)
+ priv->status |= STATUS_ENABLED;
+ else
+ priv->status &= ~STATUS_ENABLED;
+
+ return 0;
+ }
+
+ udelay(50);
+ }
+
+ IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n",
+ state ? "DISABLED" : "ENABLED");
+ return -EIO;
+}
+
+/*********************************************************************
+ Procedure : sw_reset_and_clock
+ Purpose : Asserts s/w reset, asserts clock initialization
+ and waits for clock stabilization
+ ********************************************************************/
+static int sw_reset_and_clock(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 r;
+
+ // assert s/w reset
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ // wait for clock stabilization
+ for (i = 0; i < 1000; i++) {
+ udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY);
+
+ // check clock ready bit
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &r);
+ if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET)
+ break;
+ }
+
+ if (i == 1000)
+ return -EIO; // TODO: better error value
+
+ /* set "initialization complete" bit to move adapter to
+ * D0 state */
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE);
+
+ /* wait for clock stabilization */
+ for (i = 0; i < 10000; i++) {
+ udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4);
+
+ /* check clock ready bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY)
+ break;
+ }
+
+ if (i == 10000)
+ return -EIO; /* TODO: better error value */
+
+ /* set D0 standby bit */
+ read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r);
+ write_register(priv->net_dev, IPW_REG_GP_CNTRL,
+ r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+ return 0;
+}
+
+/*********************************************************************
+ Procedure : ipw2100_download_firmware
+ Purpose : Initiaze adapter after power on.
+ The sequence is:
+ 1. assert s/w reset first!
+ 2. awake clocks & wait for clock stabilization
+ 3. hold ARC (don't ask me why...)
+ 4. load Dino ucode and reset/clock init again
+ 5. zero-out shared mem
+ 6. download f/w
+ *******************************************************************/
+static int ipw2100_download_firmware(struct ipw2100_priv *priv)
+{
+ u32 address;
+ int err;
+
+#ifndef CONFIG_PM
+ /* Fetch the firmware and microcode */
+ struct ipw2100_fw ipw2100_firmware;
+#endif
+
+ if (priv->fatal_error) {
+ IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after "
+ "fatal error %d. Interface must be brought down.\n",
+ priv->net_dev->name, priv->fatal_error);
+ return -EINVAL;
+ }
+#ifdef CONFIG_PM
+ if (!ipw2100_firmware.version) {
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+ }
+#else
+ err = ipw2100_get_firmware(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n",
+ priv->net_dev->name, err);
+ priv->fatal_error = IPW2100_ERR_FW_LOAD;
+ goto fail;
+ }
+#endif
+ priv->firmware_version = ipw2100_firmware.version;
+
+ /* s/w reset and clock stabilization */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ err = ipw2100_verify(priv);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* Hold ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000);
+
+ /* allow ARC to run */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* load microcode */
+ err = ipw2100_ucode_download(priv, &ipw2100_firmware);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* release ARC */
+ write_nic_dword(priv->net_dev,
+ IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000);
+
+ /* s/w reset and clock stabilization (again!!!) */
+ err = sw_reset_and_clock(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: sw_reset_and_clock failed: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+
+ /* load f/w */
+ err = ipw2100_fw_download(priv, &ipw2100_firmware);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n",
+ priv->net_dev->name, err);
+ goto fail;
+ }
+#ifndef CONFIG_PM
+ /*
+ * When the .resume method of the driver is called, the other
+ * part of the system, i.e. the ide driver could still stay in
+ * the suspend stage. This prevents us from loading the firmware
+ * from the disk. --YZ
+ */
+
+ /* free any storage allocated for firmware image */
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+
+ /* zero out Domain 1 area indirectly (Si requirement) */
+ for (address = IPW_HOST_FW_SHARED_AREA0;
+ address < IPW_HOST_FW_SHARED_AREA0_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA1;
+ address < IPW_HOST_FW_SHARED_AREA1_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA2;
+ address < IPW_HOST_FW_SHARED_AREA2_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_SHARED_AREA3;
+ address < IPW_HOST_FW_SHARED_AREA3_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+ for (address = IPW_HOST_FW_INTERRUPT_AREA;
+ address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4)
+ write_nic_dword(priv->net_dev, address, 0);
+
+ return 0;
+
+ fail:
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+ return err;
+}
+
+static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv)
+{
+ if (priv->status & STATUS_INT_ENABLED)
+ return;
+ priv->status |= STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK);
+}
+
+static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv)
+{
+ if (!(priv->status & STATUS_INT_ENABLED))
+ return;
+ priv->status &= ~STATUS_INT_ENABLED;
+ write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0);
+}
+
+static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv)
+{
+ struct ipw2100_ordinals *ord = &priv->ordinals;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1,
+ &ord->table1_addr);
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2,
+ &ord->table2_addr);
+
+ read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size);
+ read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size);
+
+ ord->table2_size &= 0x0000FFFF;
+
+ IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size);
+ IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size);
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv)
+{
+ u32 reg = 0;
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE |
+ IPW_BIT_GPIO_LED_OFF);
+ write_register(priv->net_dev, IPW_REG_GPIO, reg);
+}
+
+static int rf_kill_active(struct ipw2100_priv *priv)
+{
+#define MAX_RF_KILL_CHECKS 5
+#define RF_KILL_CHECK_DELAY 40
+
+ unsigned short value = 0;
+ u32 reg = 0;
+ int i;
+
+ if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
+ priv->status &= ~STATUS_RF_KILL_HW;
+ return 0;
+ }
+
+ for (i = 0; i < MAX_RF_KILL_CHECKS; i++) {
+ udelay(RF_KILL_CHECK_DELAY);
+ read_register(priv->net_dev, IPW_REG_GPIO, &reg);
+ value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
+ }
+
+ if (value == 0)
+ priv->status |= STATUS_RF_KILL_HW;
+ else
+ priv->status &= ~STATUS_RF_KILL_HW;
+
+ return (value == 0);
+}
+
+static int ipw2100_get_hw_features(struct ipw2100_priv *priv)
+{
+ u32 addr, len;
+ u32 val;
+
+ /*
+ * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1
+ */
+ len = sizeof(addr);
+ if (ipw2100_get_ordinal
+ (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return -EIO;
+ }
+
+ IPW_DEBUG_INFO("EEPROM address: %08X\n", addr);
+
+ /*
+ * EEPROM version is the byte at offset 0xfd in firmware
+ * We read 4 bytes, then shift out the byte we actually want */
+ read_nic_dword(priv->net_dev, addr + 0xFC, &val);
+ priv->eeprom_version = (val >> 24) & 0xFF;
+ IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version);
+
+ /*
+ * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware
+ *
+ * notice that the EEPROM bit is reverse polarity, i.e.
+ * bit = 0 signifies HW RF kill switch is supported
+ * bit = 1 signifies HW RF kill switch is NOT supported
+ */
+ read_nic_dword(priv->net_dev, addr + 0x20, &val);
+ if (!((val >> 24) & 0x01))
+ priv->hw_features |= HW_FEATURE_RFKILL;
+
+ IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n",
+ (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not ");
+
+ return 0;
+}
+
+/*
+ * Start firmware execution after power on and intialization
+ * The sequence is:
+ * 1. Release ARC
+ * 2. Wait for f/w initialization completes;
+ */
+static int ipw2100_start_adapter(struct ipw2100_priv *priv)
+{
+ int i;
+ u32 inta, inta_mask, gpio;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status & STATUS_RUNNING)
+ return 0;
+
+ /*
+ * Initialize the hw - drive adapter to DO state by setting
+ * init_done bit. Wait for clk_ready bit and Download
+ * fw & dino ucode
+ */
+ if (ipw2100_download_firmware(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to power on the adapter.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* Clear the Tx, Rx and Msg queues and the r/w indexes
+ * in the firmware RBD and TBD ring queue */
+ ipw2100_queues_initialize(priv);
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* TODO -- Look at disabling interrupts here to make sure none
+ * get fired during FW initialization */
+
+ /* Release ARC - clear reset bit */
+ write_register(priv->net_dev, IPW_REG_RESET_REG, 0);
+
+ /* wait for f/w intialization complete */
+ IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n");
+ i = 5000;
+ do {
+ schedule_timeout_uninterruptible(msecs_to_jiffies(40));
+ /* Todo... wait for sync command ... */
+
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ /* check "init done" bit */
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ /* reset "init done" bit */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FW_INIT_DONE);
+ break;
+ }
+
+ /* check error conditions : we check these after the firmware
+ * check so that if there is an error, the interrupt handler
+ * will see it and the adapter will be reset */
+ if (inta &
+ (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) {
+ /* clear error conditions */
+ write_register(priv->net_dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+ } while (--i);
+
+ /* Clear out any pending INTAs since we aren't supposed to have
+ * interrupts enabled at this point... */
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ inta &= IPW_INTERRUPT_MASK;
+ /* Clear out any pending interrupts */
+ if (inta & inta_mask)
+ write_register(priv->net_dev, IPW_REG_INTA, inta);
+
+ IPW_DEBUG_FW("f/w initialization complete: %s\n",
+ i ? "SUCCESS" : "FAILED");
+
+ if (!i) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Firmware did not initialize.\n",
+ priv->net_dev->name);
+ return -EIO;
+ }
+
+ /* allow firmware to write to GPIO1 & GPIO3 */
+ read_register(priv->net_dev, IPW_REG_GPIO, &gpio);
+
+ gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK);
+
+ write_register(priv->net_dev, IPW_REG_GPIO, gpio);
+
+ /* Ready to receive commands */
+ priv->status |= STATUS_RUNNING;
+
+ /* The adapter has been reset; we are not associated */
+ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv)
+{
+ if (!priv->fatal_error)
+ return;
+
+ priv->fatal_errors[priv->fatal_index++] = priv->fatal_error;
+ priv->fatal_index %= IPW2100_ERROR_QUEUE;
+ priv->fatal_error = 0;
+}
+
+/* NOTE: Our interrupt is disabled when this method is called */
+static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv)
+{
+ u32 reg;
+ int i;
+
+ IPW_DEBUG_INFO("Power cycling the hardware.\n");
+
+ ipw2100_hw_set_gpio(priv);
+
+ /* Step 1. Stop Master Assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* Step 2. Wait for stop Master Assert
+ * (not more then 50us, otherwise ret error */
+ i = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while (--i);
+
+ priv->status &= ~STATUS_RESET_PENDING;
+
+ if (!i) {
+ IPW_DEBUG_INFO
+ ("exit - waited too long for master assert stop\n");
+ return -EIO;
+ }
+
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+
+ /* At this point, the adapter is now stopped and disabled */
+ priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_ENABLED);
+
+ return 0;
+}
+
+/*
+ * Send the CARD_DISABLE_PHY_OFF comamnd to the card to disable it
+ *
+ * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent.
+ *
+ * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of
+ * if STATUS_ASSN_LOST is sent.
+ */
+static int ipw2100_hw_phy_off(struct ipw2100_priv *priv)
+{
+
+#define HW_PHY_OFF_LOOP_DELAY (HZ / 5000)
+
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE_PHY_OFF,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 val1, val2;
+
+ IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n");
+
+ /* Turn off the radio */
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2500; i++) {
+ read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1);
+ read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2);
+
+ if ((val1 & IPW2100_CONTROL_PHY_OFF) &&
+ (val2 & IPW2100_COMMAND_PHY_OFF))
+ return 0;
+
+ schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY);
+ }
+
+ return -EIO;
+}
+
+static int ipw2100_enable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = HOST_COMPLETE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("HOST_COMPLETE\n");
+
+ if (priv->status & STATUS_ENABLED)
+ return 0;
+
+ mutex_lock(&priv->adapter_mutex);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_HC("Command aborted due to RF kill active.\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED);
+ if (err) {
+ IPW_DEBUG_INFO("%s: card not responding to init command.\n",
+ priv->net_dev->name);
+ goto fail_up;
+ }
+
+ if (priv->stop_hang_check) {
+ priv->stop_hang_check = 0;
+ queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+ }
+
+ fail_up:
+ mutex_unlock(&priv->adapter_mutex);
+ return err;
+}
+
+static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
+{
+#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100))
+
+ struct host_command cmd = {
+ .host_command = HOST_PRE_POWER_DOWN,
+ .host_command_sequence = 0,
+ .host_command_length = 0,
+ };
+ int err, i;
+ u32 reg;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ priv->status |= STATUS_STOPPING;
+
+ /* We can only shut down the card if the firmware is operational. So,
+ * if we haven't reset since a fatal_error, then we can not send the
+ * shutdown commands. */
+ if (!priv->fatal_error) {
+ /* First, make sure the adapter is enabled so that the PHY_OFF
+ * command can shut it down */
+ ipw2100_enable_adapter(priv);
+
+ err = ipw2100_hw_phy_off(priv);
+ if (err)
+ printk(KERN_WARNING DRV_NAME
+ ": Error disabling radio %d\n", err);
+
+ /*
+ * If in D0-standby mode going directly to D3 may cause a
+ * PCI bus violation. Therefore we must change out of the D0
+ * state.
+ *
+ * Sending the PREPARE_FOR_POWER_DOWN will restrict the
+ * hardware from going into standby mode and will transition
+ * out of D0-standby if it is already in that state.
+ *
+ * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
+ * driver upon completion. Once received, the driver can
+ * proceed to the D3 state.
+ *
+ * Prepare for power down command to fw. This command would
+ * take HW out of D0-standby and prepare it for D3 state.
+ *
+ * Currently FW does not support event notification for this
+ * event. Therefore, skip waiting for it. Just wait a fixed
+ * 100ms
+ */
+ IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n");
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Power down command failed: Error %d\n",
+ priv->net_dev->name, err);
+ else
+ schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY);
+ }
+
+ priv->status &= ~STATUS_ENABLED;
+
+ /*
+ * Set GPIO 3 writable by FW; GPIO 1 writable
+ * by driver and enable clock
+ */
+ ipw2100_hw_set_gpio(priv);
+
+ /*
+ * Power down adapter. Sequence:
+ * 1. Stop master assert (RESET_REG[9]=1)
+ * 2. Wait for stop master (RESET_REG[8]==1)
+ * 3. S/w reset assert (RESET_REG[7] = 1)
+ */
+
+ /* Stop master assert */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+
+ /* wait stop master not more than 50 usec.
+ * Otherwise return error. */
+ for (i = 5; i > 0; i--) {
+ udelay(10);
+
+ /* Check master stop bit */
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ }
+
+ if (i == 0)
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Could now power down adapter.\n",
+ priv->net_dev->name);
+
+ /* assert s/w reset */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_SW_RESET);
+
+ priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING);
+
+ return 0;
+}
+
+static int ipw2100_disable_adapter(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = CARD_DISABLE,
+ .host_command_sequence = 0,
+ .host_command_length = 0
+ };
+ int err = 0;
+
+ IPW_DEBUG_HC("CARD_DISABLE\n");
+
+ if (!(priv->status & STATUS_ENABLED))
+ return 0;
+
+ /* Make sure we clear the associated state */
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ mutex_lock(&priv->adapter_mutex);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ ": exit - failed to send CARD_DISABLE command\n");
+ goto fail_up;
+ }
+
+ err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ ": exit - card failed to change to DISABLED\n");
+ goto fail_up;
+ }
+
+ IPW_DEBUG_INFO("TODO: implement scan state machine\n");
+
+ fail_up:
+ mutex_unlock(&priv->adapter_mutex);
+ return err;
+}
+
+static int ipw2100_set_scan_options(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = SET_SCAN_OPTIONS,
+ .host_command_sequence = 0,
+ .host_command_length = 8
+ };
+ int err;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_SCAN("setting scan options\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ if (!(priv->config & CFG_ASSOCIATE))
+ cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE;
+ if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled)
+ cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL;
+ if (priv->config & CFG_PASSIVE_SCAN)
+ cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE;
+
+ cmd.host_command_parameters[1] = priv->channel_mask;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n",
+ cmd.host_command_parameters[0]);
+
+ return err;
+}
+
+static int ipw2100_start_scan(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = BROADCAST_SCAN,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ IPW_DEBUG_HC("START_SCAN\n");
+
+ cmd.host_command_parameters[0] = 0;
+
+ /* No scanning if in monitor mode */
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return 1;
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_SCAN("Scan requested while already in scan...\n");
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /* Not clearing here; doing so makes iwlist always return nothing...
+ *
+ * We should modify the table logic to use aging tables vs. clearing
+ * the table on each scan start.
+ */
+ IPW_DEBUG_SCAN("starting scan\n");
+
+ priv->status |= STATUS_SCANNING;
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ priv->status &= ~STATUS_SCANNING;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return err;
+}
+
+static const struct ieee80211_geo ipw_geos[] = {
+ { /* Restricted */
+ "---",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14}},
+ },
+};
+
+static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
+{
+ unsigned long flags;
+ int rc = 0;
+ u32 lock;
+ u32 ord_len = sizeof(lock);
+
+ /* Quite if manually disabled. */
+ if (priv->status & STATUS_RF_KILL_SW) {
+ IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable "
+ "switch\n", priv->net_dev->name);
+ return 0;
+ }
+
+ /* the ipw2100 hardware really doesn't want power management delays
+ * longer than 175usec
+ */
+ pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", 175);
+
+ /* If the interrupt is enabled, turn it off... */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ /* Reset any fatal_error conditions */
+ ipw2100_reset_fatalerror(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_POWERED ||
+ (priv->status & STATUS_RESET_PENDING)) {
+ /* Power cycle the card ... */
+ if (ipw2100_power_cycle_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Could not cycle adapter.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+ } else
+ priv->status |= STATUS_POWERED;
+
+ /* Load the firmware, start the clocks, etc. */
+ if (ipw2100_start_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to start the firmware.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ ipw2100_initialize_ordinals(priv);
+
+ /* Determine capabilities of this particular HW configuration */
+ if (ipw2100_get_hw_features(priv)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to determine HW features.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ /* Initialize the geo */
+ if (ieee80211_set_geo(priv->ieee, &ipw_geos[0])) {
+ printk(KERN_WARNING DRV_NAME "Could not set geo\n");
+ return 0;
+ }
+ priv->ieee->freq_band = IEEE80211_24GHZ_BAND;
+
+ lock = LOCK_NONE;
+ if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Failed to clear ordinal lock.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ priv->status &= ~STATUS_SCANNING;
+
+ if (rf_kill_active(priv)) {
+ printk(KERN_INFO "%s: Radio is disabled by RF switch.\n",
+ priv->net_dev->name);
+
+ if (priv->stop_rf_kill) {
+ priv->stop_rf_kill = 0;
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ round_jiffies_relative(HZ));
+ }
+
+ deferred = 1;
+ }
+
+ /* Turn on the interrupt so that commands can be processed */
+ ipw2100_enable_interrupts(priv);
+
+ /* Send all of the commands that must be sent prior to
+ * HOST_COMPLETE */
+ if (ipw2100_adapter_setup(priv)) {
+ printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n",
+ priv->net_dev->name);
+ rc = 1;
+ goto exit;
+ }
+
+ if (!deferred) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ rc = 1;
+ goto exit;
+ }
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ exit:
+ return rc;
+}
+
+/* Called by register_netdev() */
+static int ipw2100_net_init(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ipw2100_up(priv, 1);
+}
+
+static void ipw2100_down(struct ipw2100_priv *priv)
+{
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER}
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ /* Kill the RF switch timer */
+ if (!priv->stop_rf_kill) {
+ priv->stop_rf_kill = 1;
+ cancel_delayed_work(&priv->rf_kill);
+ }
+
+ /* Kill the firmare hang check timer */
+ if (!priv->stop_hang_check) {
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+ }
+
+ /* Kill any pending resets */
+ if (priv->status & STATUS_RESET_PENDING)
+ cancel_delayed_work(&priv->reset_work);
+
+ /* Make sure the interrupt is on so that FW commands will be
+ * processed correctly */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_enable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ if (ipw2100_hw_stop_adapter(priv))
+ printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n",
+ priv->net_dev->name);
+
+ /* Do not disable the interrupt until _after_ we disable
+ * the adaptor. Otherwise the CARD_DISABLE command will never
+ * be ack'd by the firmware */
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100",
+ PM_QOS_DEFAULT_VALUE);
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+}
+
+static void ipw2100_reset_adapter(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, reset_work.work);
+ unsigned long flags;
+ union iwreq_data wrqu = {
+ .ap_addr = {
+ .sa_family = ARPHRD_ETHER}
+ };
+ int associated = priv->status & STATUS_ASSOCIATED;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name);
+ priv->resets++;
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ priv->status |= STATUS_SECURITY_UPDATED;
+
+ /* Force a power cycle even if interface hasn't been opened
+ * yet */
+ cancel_delayed_work(&priv->reset_work);
+ priv->status |= STATUS_RESET_PENDING;
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ mutex_lock(&priv->action_mutex);
+ /* stop timed checks so that they don't interfere with reset */
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->hang_check);
+
+ /* We have to signal any supplicant if we are disassociating */
+ if (associated)
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+
+ ipw2100_up(priv, 0);
+ mutex_unlock(&priv->action_mutex);
+
+}
+
+static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
+{
+
+#define MAC_ASSOCIATION_READ_DELAY (HZ)
+ int ret, len, essid_len;
+ char essid[IW_ESSID_MAX_SIZE];
+ u32 txrate;
+ u32 chan;
+ char *txratename;
+ u8 bssid[ETH_ALEN];
+ DECLARE_SSID_BUF(ssid);
+
+ /*
+ * TBD: BSSID is usually 00:00:00:00:00:00 here and not
+ * an actual MAC of the AP. Seems like FW sets this
+ * address too late. Read it later and expose through
+ * /proc or schedule a later task to query and update
+ */
+
+ essid_len = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID,
+ essid, &essid_len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+
+ len = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ len = ETH_ALEN;
+ ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid, &len);
+ if (ret) {
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+ return;
+ }
+ memcpy(priv->ieee->bssid, bssid, ETH_ALEN);
+
+ switch (txrate) {
+ case TX_RATE_1_MBIT:
+ txratename = "1Mbps";
+ break;
+ case TX_RATE_2_MBIT:
+ txratename = "2Mbsp";
+ break;
+ case TX_RATE_5_5_MBIT:
+ txratename = "5.5Mbps";
+ break;
+ case TX_RATE_11_MBIT:
+ txratename = "11Mbps";
+ break;
+ default:
+ IPW_DEBUG_INFO("Unknown rate: %d\n", txrate);
+ txratename = "unknown rate";
+ break;
+ }
+
+ IPW_DEBUG_INFO("%s: Associated with '%s' at %s, channel %d (BSSID=%pM)\n",
+ priv->net_dev->name, print_ssid(ssid, essid, essid_len),
+ txratename, chan, bssid);
+
+ /* now we copy read ssid into dev */
+ if (!(priv->config & CFG_STATIC_ESSID)) {
+ priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE);
+ memcpy(priv->essid, essid, priv->essid_len);
+ }
+ priv->channel = chan;
+ memcpy(priv->bssid, bssid, ETH_ALEN);
+
+ priv->status |= STATUS_ASSOCIATING;
+ priv->connect_start = get_seconds();
+
+ queue_delayed_work(priv->workqueue, &priv->wx_event_work, HZ / 10);
+}
+
+static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid,
+ int length, int batch_mode)
+{
+ int ssid_len = min(length, IW_ESSID_MAX_SIZE);
+ struct host_command cmd = {
+ .host_command = SSID,
+ .host_command_sequence = 0,
+ .host_command_length = ssid_len
+ };
+ int err;
+ DECLARE_SSID_BUF(ssid);
+
+ IPW_DEBUG_HC("SSID: '%s'\n", print_ssid(ssid, essid, ssid_len));
+
+ if (ssid_len)
+ memcpy(cmd.host_command_parameters, essid, ssid_len);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to
+ * disable auto association -- so we cheat by setting a bogus SSID */
+ if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) {
+ int i;
+ u8 *bogus = (u8 *) cmd.host_command_parameters;
+ for (i = 0; i < IW_ESSID_MAX_SIZE; i++)
+ bogus[i] = 0x18 + i;
+ cmd.host_command_length = IW_ESSID_MAX_SIZE;
+ }
+
+ /* NOTE: We always send the SSID command even if the provided ESSID is
+ * the same as what we currently think is set. */
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err) {
+ memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len);
+ memcpy(priv->essid, essid, ssid_len);
+ priv->essid_len = ssid_len;
+ }
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status)
+{
+ DECLARE_SSID_BUF(ssid);
+
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC,
+ "disassociated: '%s' %pM \n",
+ print_ssid(ssid, priv->essid, priv->essid_len),
+ priv->bssid);
+
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+
+ if (priv->status & STATUS_STOPPING) {
+ IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n");
+ return;
+ }
+
+ memset(priv->bssid, 0, ETH_ALEN);
+ memset(priv->ieee->bssid, 0, ETH_ALEN);
+
+ netif_carrier_off(priv->net_dev);
+ netif_stop_queue(priv->net_dev);
+
+ if (!(priv->status & STATUS_RUNNING))
+ return;
+
+ if (priv->status & STATUS_SECURITY_UPDATED)
+ queue_delayed_work(priv->workqueue, &priv->security_work, 0);
+
+ queue_delayed_work(priv->workqueue, &priv->wx_event_work, 0);
+}
+
+static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n",
+ priv->net_dev->name);
+
+ /* RF_KILL is now enabled (else we wouldn't be here) */
+ priv->status |= STATUS_RF_KILL_HW;
+
+ /* Make sure the RF Kill check timer is running */
+ priv->stop_rf_kill = 0;
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ round_jiffies_relative(HZ));
+}
+
+static void send_scan_event(void *data)
+{
+ struct ipw2100_priv *priv = data;
+ union iwreq_data wrqu;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static void ipw2100_scan_event_later(struct work_struct *work)
+{
+ send_scan_event(container_of(work, struct ipw2100_priv,
+ scan_event_later.work));
+}
+
+static void ipw2100_scan_event_now(struct work_struct *work)
+{
+ send_scan_event(container_of(work, struct ipw2100_priv,
+ scan_event_now));
+}
+
+static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("scan complete\n");
+ /* Age the scan results... */
+ priv->ieee->scans++;
+ priv->status &= ~STATUS_SCANNING;
+
+ /* Only userspace-requested scan completion events go out immediately */
+ if (!priv->user_requested_scan) {
+ if (!delayed_work_pending(&priv->scan_event_later))
+ queue_delayed_work(priv->workqueue,
+ &priv->scan_event_later,
+ round_jiffies_relative(msecs_to_jiffies(4000)));
+ } else {
+ priv->user_requested_scan = 0;
+ cancel_delayed_work(&priv->scan_event_later);
+ queue_work(priv->workqueue, &priv->scan_event_now);
+ }
+}
+
+#ifdef CONFIG_IPW2100_DEBUG
+#define IPW2100_HANDLER(v, f) { v, f, # v }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb) (struct ipw2100_priv * priv, u32 status);
+ char *name;
+};
+#else
+#define IPW2100_HANDLER(v, f) { v, f }
+struct ipw2100_status_indicator {
+ int status;
+ void (*cb) (struct ipw2100_priv * priv, u32 status);
+};
+#endif /* CONFIG_IPW2100_DEBUG */
+
+static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status)
+{
+ IPW_DEBUG_SCAN("Scanning...\n");
+ priv->status |= STATUS_SCANNING;
+}
+
+static const struct ipw2100_status_indicator status_handlers[] = {
+ IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL),
+ IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL),
+ IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated),
+ IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost),
+ IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL),
+ IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete),
+ IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL),
+ IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL),
+ IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill),
+ IPW2100_HANDLER(IPW_STATE_DISABLED, NULL),
+ IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL),
+ IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning),
+ IPW2100_HANDLER(-1, NULL)
+};
+
+static void isr_status_change(struct ipw2100_priv *priv, int status)
+{
+ int i;
+
+ if (status == IPW_STATE_SCANNING &&
+ priv->status & STATUS_ASSOCIATED &&
+ !(priv->status & STATUS_SCANNING)) {
+ IPW_DEBUG_INFO("Scan detected while associated, with "
+ "no scan request. Restarting firmware.\n");
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ for (i = 0; status_handlers[i].status != -1; i++) {
+ if (status == status_handlers[i].status) {
+ IPW_DEBUG_NOTIF("Status change: %s\n",
+ status_handlers[i].name);
+ if (status_handlers[i].cb)
+ status_handlers[i].cb(priv, status);
+ priv->wstats.status = status;
+ return;
+ }
+ }
+
+ IPW_DEBUG_NOTIF("unknown status received: %04x\n", status);
+}
+
+static void isr_rx_complete_command(struct ipw2100_priv *priv,
+ struct ipw2100_cmd_header *cmd)
+{
+#ifdef CONFIG_IPW2100_DEBUG
+ if (cmd->host_command_reg < ARRAY_SIZE(command_types)) {
+ IPW_DEBUG_HC("Command completed '%s (%d)'\n",
+ command_types[cmd->host_command_reg],
+ cmd->host_command_reg);
+ }
+#endif
+ if (cmd->host_command_reg == HOST_COMPLETE)
+ priv->status |= STATUS_ENABLED;
+
+ if (cmd->host_command_reg == CARD_DISABLE)
+ priv->status &= ~STATUS_ENABLED;
+
+ priv->status &= ~STATUS_CMD_ACTIVE;
+
+ wake_up_interruptible(&priv->wait_command_queue);
+}
+
+#ifdef CONFIG_IPW2100_DEBUG
+static const char *frame_types[] = {
+ "COMMAND_STATUS_VAL",
+ "STATUS_CHANGE_VAL",
+ "P80211_DATA_VAL",
+ "P8023_DATA_VAL",
+ "HOST_NOTIFICATION_VAL"
+};
+#endif
+
+static int ipw2100_alloc_skb(struct ipw2100_priv *priv,
+ struct ipw2100_rx_packet *packet)
+{
+ packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx));
+ if (!packet->skb)
+ return -ENOMEM;
+
+ packet->rxp = (struct ipw2100_rx *)packet->skb->data;
+ packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ /* NOTE: pci_map_single does not return an error code, and 0 is a valid
+ * dma_addr */
+
+ return 0;
+}
+
+#define SEARCH_ERROR 0xffffffff
+#define SEARCH_FAIL 0xfffffffe
+#define SEARCH_SUCCESS 0xfffffff0
+#define SEARCH_DISCARD 0
+#define SEARCH_SNAPSHOT 1
+
+#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff))
+static void ipw2100_snapshot_free(struct ipw2100_priv *priv)
+{
+ int i;
+ if (!priv->snapshot[0])
+ return;
+ for (i = 0; i < 0x30; i++)
+ kfree(priv->snapshot[i]);
+ priv->snapshot[0] = NULL;
+}
+
+#ifdef IPW2100_DEBUG_C3
+static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv)
+{
+ int i;
+ if (priv->snapshot[0])
+ return 1;
+ for (i = 0; i < 0x30; i++) {
+ priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC);
+ if (!priv->snapshot[i]) {
+ IPW_DEBUG_INFO("%s: Error allocating snapshot "
+ "buffer %d\n", priv->net_dev->name, i);
+ while (i > 0)
+ kfree(priv->snapshot[--i]);
+ priv->snapshot[0] = NULL;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf,
+ size_t len, int mode)
+{
+ u32 i, j;
+ u32 tmp;
+ u8 *s, *d;
+ u32 ret;
+
+ s = in_buf;
+ if (mode == SEARCH_SNAPSHOT) {
+ if (!ipw2100_snapshot_alloc(priv))
+ mode = SEARCH_DISCARD;
+ }
+
+ for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) {
+ read_nic_dword(priv->net_dev, i, &tmp);
+ if (mode == SEARCH_SNAPSHOT)
+ *(u32 *) SNAPSHOT_ADDR(i) = tmp;
+ if (ret == SEARCH_FAIL) {
+ d = (u8 *) & tmp;
+ for (j = 0; j < 4; j++) {
+ if (*s != *d) {
+ s = in_buf;
+ continue;
+ }
+
+ s++;
+ d++;
+
+ if ((s - in_buf) == len)
+ ret = (i + j) - len + 1;
+ }
+ } else if (mode == SEARCH_DISCARD)
+ return ret;
+ }
+
+ return ret;
+}
+#endif
+
+/*
+ *
+ * 0) Disconnect the SKB from the firmware (just unmap)
+ * 1) Pack the ETH header into the SKB
+ * 2) Pass the SKB to the network stack
+ *
+ * When packet is provided by the firmware, it contains the following:
+ *
+ * . ieee80211_hdr
+ * . ieee80211_snap_hdr
+ *
+ * The size of the constructed ethernet
+ *
+ */
+#ifdef IPW2100_RX_DEBUG
+static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH];
+#endif
+
+static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i)
+{
+#ifdef IPW2100_DEBUG_C3
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ u32 match, reg;
+ int j;
+#endif
+
+ IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n",
+ i * sizeof(struct ipw2100_status));
+
+#ifdef IPW2100_DEBUG_C3
+ /* Halt the fimrware so we can get a good image */
+ write_register(priv->net_dev, IPW_REG_RESET_REG,
+ IPW_AUX_HOST_RESET_REG_STOP_MASTER);
+ j = 5;
+ do {
+ udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY);
+ read_register(priv->net_dev, IPW_REG_RESET_REG, &reg);
+
+ if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED)
+ break;
+ } while (j--);
+
+ match = ipw2100_match_buf(priv, (u8 *) status,
+ sizeof(struct ipw2100_status),
+ SEARCH_SNAPSHOT);
+ if (match < SEARCH_SUCCESS)
+ IPW_DEBUG_INFO("%s: DMA status match in Firmware at "
+ "offset 0x%06X, length %d:\n",
+ priv->net_dev->name, match,
+ sizeof(struct ipw2100_status));
+ else
+ IPW_DEBUG_INFO("%s: No DMA status match in "
+ "Firmware.\n", priv->net_dev->name);
+
+ printk_buf((u8 *) priv->status_queue.drv,
+ sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH);
+#endif
+
+ priv->fatal_error = IPW2100_ERR_C3_CORRUPTION;
+ priv->ieee->stats.rx_errors++;
+ schedule_reset(priv);
+}
+
+static void isr_rx(struct ipw2100_priv *priv, int i,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ IPW_DEBUG_RX("Handler...\n");
+
+ if (unlikely(status->frame_size > skb_tailroom(packet->skb))) {
+ IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+ " Dropping.\n",
+ priv->net_dev->name,
+ status->frame_size, skb_tailroom(packet->skb));
+ priv->ieee->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
+ !(priv->status & STATUS_ASSOCIATED))) {
+ IPW_DEBUG_DROP("Dropping packet while not associated.\n");
+ priv->wstats.discard.misc++;
+ return;
+ }
+
+ pci_unmap_single(priv->pci_dev,
+ packet->dma_addr,
+ sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+
+ skb_put(packet->skb, status->frame_size);
+
+#ifdef IPW2100_RX_DEBUG
+ /* Make a copy of the frame so we can dump it to the logs if
+ * ieee80211_rx fails */
+ skb_copy_from_linear_data(packet->skb, packet_data,
+ min_t(u32, status->frame_size,
+ IPW_RX_NIC_BUFFER_LENGTH));
+#endif
+
+ if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+#ifdef IPW2100_RX_DEBUG
+ IPW_DEBUG_DROP("%s: Non consumed packet:\n",
+ priv->net_dev->name);
+ printk_buf(IPW_DL_DROP, packet_data, status->frame_size);
+#endif
+ priv->ieee->stats.rx_errors++;
+
+ /* ieee80211_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(packet->skb);
+ packet->skb = NULL;
+ }
+
+ /* We need to allocate a new SKB and attach it to the RDB. */
+ if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Unable to allocate SKB onto RBD ring - disabling "
+ "adapter.\n", priv->net_dev->name);
+ /* TODO: schedule adapter shutdown */
+ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+ }
+
+ /* Update the RDB entry */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#ifdef CONFIG_IPW2100_MONITOR
+
+static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ /* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE */
+ struct ipw_rt_hdr {
+ struct ieee80211_radiotap_header rt_hdr;
+ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+ } *ipw_rt;
+
+ IPW_DEBUG_RX("Handler...\n");
+
+ if (unlikely(status->frame_size > skb_tailroom(packet->skb) -
+ sizeof(struct ipw_rt_hdr))) {
+ IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+ " Dropping.\n",
+ priv->net_dev->name,
+ status->frame_size,
+ skb_tailroom(packet->skb));
+ priv->ieee->stats.rx_errors++;
+ return;
+ }
+
+ if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ if (unlikely(priv->config & CFG_CRC_CHECK &&
+ status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+ IPW_DEBUG_RX("CRC error in packet. Dropping.\n");
+ priv->ieee->stats.rx_errors++;
+ return;
+ }
+
+ pci_unmap_single(priv->pci_dev, packet->dma_addr,
+ sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+ memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
+ packet->skb->data, status->frame_size);
+
+ ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */
+
+ ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+
+ ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
+
+ skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
+
+ if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+ priv->ieee->stats.rx_errors++;
+
+ /* ieee80211_rx failed, so it didn't free the SKB */
+ dev_kfree_skb_any(packet->skb);
+ packet->skb = NULL;
+ }
+
+ /* We need to allocate a new SKB and attach it to the RDB. */
+ if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+ IPW_DEBUG_WARNING(
+ "%s: Unable to allocate SKB onto RBD ring - disabling "
+ "adapter.\n", priv->net_dev->name);
+ /* TODO: schedule adapter shutdown */
+ IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+ }
+
+ /* Update the RDB entry */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#endif
+
+static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
+{
+ struct ipw2100_status *status = &priv->status_queue.drv[i];
+ struct ipw2100_rx *u = priv->rx_buffers[i].rxp;
+ u16 frame_type = status->status_fields & STATUS_TYPE_MASK;
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ return (status->frame_size != sizeof(u->rx_data.command));
+ case STATUS_CHANGE_VAL:
+ return (status->frame_size != sizeof(u->rx_data.status));
+ case HOST_NOTIFICATION_VAL:
+ return (status->frame_size < sizeof(u->rx_data.notification));
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ return 0;
+#else
+ switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
+ case IEEE80211_FTYPE_MGMT:
+ case IEEE80211_FTYPE_CTL:
+ return 0;
+ case IEEE80211_FTYPE_DATA:
+ return (status->frame_size >
+ IPW_MAX_802_11_PAYLOAD_LENGTH);
+ }
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * ipw2100 interrupts are disabled at this point, and the ISR
+ * is the only code that calls this method. So, we do not need
+ * to play with any locks.
+ *
+ * RX Queue works as follows:
+ *
+ * Read index - firmware places packet in entry identified by the
+ * Read index and advances Read index. In this manner,
+ * Read index will always point to the next packet to
+ * be filled--but not yet valid.
+ *
+ * Write index - driver fills this entry with an unused RBD entry.
+ * This entry has not filled by the firmware yet.
+ *
+ * In between the W and R indexes are the RBDs that have been received
+ * but not yet processed.
+ *
+ * The process of handling packets will start at WRITE + 1 and advance
+ * until it reaches the READ index.
+ *
+ * The WRITE index is cached in the variable 'priv->rx_queue.next'.
+ *
+ */
+static void __ipw2100_rx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *rxq = &priv->rx_queue;
+ struct ipw2100_status_queue *sq = &priv->status_queue;
+ struct ipw2100_rx_packet *packet;
+ u16 frame_type;
+ u32 r, w, i, s;
+ struct ipw2100_rx *u;
+ struct ieee80211_rx_stats stats = {
+ .mac_time = jiffies,
+ };
+
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w);
+
+ if (r >= rxq->entries) {
+ IPW_DEBUG_RX("exit - bad read index\n");
+ return;
+ }
+
+ i = (rxq->next + 1) % rxq->entries;
+ s = i;
+ while (i != r) {
+ /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n",
+ r, rxq->next, i); */
+
+ packet = &priv->rx_buffers[i];
+
+ /* Sync the DMA for the STATUS buffer so CPU is sure to get
+ * the correct values */
+ pci_dma_sync_single_for_cpu(priv->pci_dev,
+ sq->nic +
+ sizeof(struct ipw2100_status) * i,
+ sizeof(struct ipw2100_status),
+ PCI_DMA_FROMDEVICE);
+
+ /* Sync the DMA for the RX buffer so CPU is sure to get
+ * the correct values */
+ pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+
+ if (unlikely(ipw2100_corruption_check(priv, i))) {
+ ipw2100_corruption_detected(priv, i);
+ goto increment;
+ }
+
+ u = packet->rxp;
+ frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK;
+ stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM;
+ stats.len = sq->drv[i].frame_size;
+
+ stats.mask = 0;
+ if (stats.rssi != 0)
+ stats.mask |= IEEE80211_STATMASK_RSSI;
+ stats.freq = IEEE80211_24GHZ_BAND;
+
+ IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n",
+ priv->net_dev->name, frame_types[frame_type],
+ stats.len);
+
+ switch (frame_type) {
+ case COMMAND_STATUS_VAL:
+ /* Reset Rx watchdog */
+ isr_rx_complete_command(priv, &u->rx_data.command);
+ break;
+
+ case STATUS_CHANGE_VAL:
+ isr_status_change(priv, u->rx_data.status);
+ break;
+
+ case P80211_DATA_VAL:
+ case P8023_DATA_VAL:
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ isr_rx_monitor(priv, i, &stats);
+ break;
+ }
+#endif
+ if (stats.len < sizeof(struct ieee80211_hdr_3addr))
+ break;
+ switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
+ case IEEE80211_FTYPE_MGMT:
+ ieee80211_rx_mgt(priv->ieee,
+ &u->rx_data.header, &stats);
+ break;
+
+ case IEEE80211_FTYPE_CTL:
+ break;
+
+ case IEEE80211_FTYPE_DATA:
+ isr_rx(priv, i, &stats);
+ break;
+
+ }
+ break;
+ }
+
+ increment:
+ /* clear status field associated with this RBD */
+ rxq->drv[i].status.info.field = 0;
+
+ i = (i + 1) % rxq->entries;
+ }
+
+ if (i != s) {
+ /* backtrack one entry, wrapping to end if at 0 */
+ rxq->next = (i ? i : rxq->entries) - 1;
+
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next);
+ }
+}
+
+/*
+ * __ipw2100_tx_process
+ *
+ * This routine will determine whether the next packet on
+ * the fw_pend_list has been processed by the firmware yet.
+ *
+ * If not, then it does nothing and returns.
+ *
+ * If so, then it removes the item from the fw_pend_list, frees
+ * any associated storage, and places the item back on the
+ * free list of its source (either msg_free_list or tx_free_list)
+ *
+ * TX Queue works as follows:
+ *
+ * Read index - points to the next TBD that the firmware will
+ * process. The firmware will read the data, and once
+ * done processing, it will advance the Read index.
+ *
+ * Write index - driver fills this entry with an constructed TBD
+ * entry. The Write index is not advanced until the
+ * packet has been configured.
+ *
+ * In between the W and R indexes are the TBDs that have NOT been
+ * processed. Lagging behind the R index are packets that have
+ * been processed but have not been freed by the driver.
+ *
+ * In order to free old storage, an internal index will be maintained
+ * that points to the next packet to be freed. When all used
+ * packets have been freed, the oldest index will be the same as the
+ * firmware's read index.
+ *
+ * The OLDEST index is cached in the variable 'priv->tx_queue.oldest'
+ *
+ * Because the TBD structure can not contain arbitrary data, the
+ * driver must keep an internal queue of cached allocations such that
+ * it can put that data back into the tx_free_list and msg_free_list
+ * for use by future command and data packets.
+ *
+ */
+static int __ipw2100_tx_process(struct ipw2100_priv *priv)
+{
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ int descriptors_used;
+ int e, i;
+ u32 r, w, frag_num = 0;
+
+ if (list_empty(&priv->fw_pend_list))
+ return 0;
+
+ element = priv->fw_pend_list.next;
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+ tbd = &txq->drv[packet->index];
+
+ /* Determine how many TBD entries must be finished... */
+ switch (packet->type) {
+ case COMMAND:
+ /* COMMAND uses only one slot; don't advance */
+ descriptors_used = 1;
+ e = txq->oldest;
+ break;
+
+ case DATA:
+ /* DATA uses two slots; advance and loop position. */
+ descriptors_used = tbd->num_fragments;
+ frag_num = tbd->num_fragments - 1;
+ e = txq->oldest + frag_num;
+ e %= txq->entries;
+ break;
+
+ default:
+ printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n",
+ priv->net_dev->name);
+ return 0;
+ }
+
+ /* if the last TBD is not done by NIC yet, then packet is
+ * not ready to be released.
+ *
+ */
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ &r);
+ read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ &w);
+ if (w != txq->next)
+ printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n",
+ priv->net_dev->name);
+
+ /*
+ * txq->next is the index of the last packet written txq->oldest is
+ * the index of the r is the index of the next packet to be read by
+ * firmware
+ */
+
+ /*
+ * Quick graphic to help you visualize the following
+ * if / else statement
+ *
+ * ===>| s---->|===============
+ * e>|
+ * | a | b | c | d | e | f | g | h | i | j | k | l
+ * r---->|
+ * w
+ *
+ * w - updated by driver
+ * r - updated by firmware
+ * s - start of oldest BD entry (txq->oldest)
+ * e - end of oldest BD entry
+ *
+ */
+ if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) {
+ IPW_DEBUG_TX("exit - no processed packets ready to release.\n");
+ return 0;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->fw_pend_stat);
+
+#ifdef CONFIG_IPW2100_DEBUG
+ {
+ int i = txq->oldest;
+ IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32) (txq->nic + i * sizeof(struct ipw2100_bd)),
+ txq->drv[i].host_addr, txq->drv[i].buf_length);
+
+ if (packet->type == DATA) {
+ i = (i + 1) % txq->entries;
+
+ IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i,
+ &txq->drv[i],
+ (u32) (txq->nic + i *
+ sizeof(struct ipw2100_bd)),
+ (u32) txq->drv[i].host_addr,
+ txq->drv[i].buf_length);
+ }
+ }
+#endif
+
+ switch (packet->type) {
+ case DATA:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 0)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting DATA TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+ /* DATA packet; we have to unmap and free the SKB */
+ for (i = 0; i < frag_num; i++) {
+ tbd = &txq->drv[(packet->index + 1 + i) % txq->entries];
+
+ IPW_DEBUG_TX("TX%d P=%08x L=%d\n",
+ (packet->index + 1 + i) % txq->entries,
+ tbd->host_addr, tbd->buf_length);
+
+ pci_unmap_single(priv->pci_dev,
+ tbd->host_addr,
+ tbd->buf_length, PCI_DMA_TODEVICE);
+ }
+
+ ieee80211_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+
+ /* We have a free slot in the Tx queue, so wake up the
+ * transmit layer if it is stopped. */
+ if (priv->status & STATUS_ASSOCIATED)
+ netif_wake_queue(priv->net_dev);
+
+ /* A packet was processed by the hardware, so update the
+ * watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ break;
+
+ case COMMAND:
+ if (txq->drv[txq->oldest].status.info.fields.txType != 1)
+ printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. "
+ "Expecting COMMAND TBD but pulled "
+ "something else: ids %d=%d.\n",
+ priv->net_dev->name, txq->oldest, packet->index);
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (packet->info.c_struct.cmd->host_command_reg <
+ ARRAY_SIZE(command_types))
+ IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n",
+ command_types[packet->info.c_struct.cmd->
+ host_command_reg],
+ packet->info.c_struct.cmd->
+ host_command_reg,
+ packet->info.c_struct.cmd->cmd_status_reg);
+#endif
+
+ list_add_tail(element, &priv->msg_free_list);
+ INC_STAT(&priv->msg_free_stat);
+ break;
+ }
+
+ /* advance oldest used TBD pointer to start of next entry */
+ txq->oldest = (e + 1) % txq->entries;
+ /* increase available TBDs number */
+ txq->available += descriptors_used;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n",
+ jiffies - packet->jiffy_start);
+
+ return (!list_empty(&priv->fw_pend_list));
+}
+
+static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv)
+{
+ int i = 0;
+
+ while (__ipw2100_tx_process(priv) && i < 200)
+ i++;
+
+ if (i == 200) {
+ printk(KERN_WARNING DRV_NAME ": "
+ "%s: Driver is running slow (%d iters).\n",
+ priv->net_dev->name, i);
+ }
+}
+
+static void ipw2100_tx_send_commands(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+
+ while (!list_empty(&priv->msg_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 3 are needed as a command will take one,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ if (txq->available <= 3) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ element = priv->msg_pend_list.next;
+ list_del(element);
+ DEC_STAT(&priv->msg_pend_stat);
+
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ IPW_DEBUG_TX("using TBD at virt=%p, phys=%p\n",
+ &txq->drv[txq->next],
+ (void *)(txq->nic + txq->next *
+ sizeof(struct ipw2100_bd)));
+
+ packet->index = txq->next;
+
+ tbd = &txq->drv[txq->next];
+
+ /* initialize TBD */
+ tbd->host_addr = packet->info.c_struct.cmd_phys;
+ tbd->buf_length = sizeof(struct ipw2100_cmd_header);
+ /* not marking number of fragments causes problems
+ * with f/w debug version */
+ tbd->num_fragments = 1;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_COMMAND |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+
+ /* update TBD queue counters */
+ txq->next++;
+ txq->next %= txq->entries;
+ txq->available--;
+ DEC_STAT(&priv->txq_stat);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ wmb();
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+}
+
+/*
+ * ipw2100_tx_send_data
+ *
+ */
+static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
+{
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ struct ipw2100_bd_queue *txq = &priv->tx_queue;
+ struct ipw2100_bd *tbd;
+ int next = txq->next;
+ int i = 0;
+ struct ipw2100_data_header *ipw_hdr;
+ struct ieee80211_hdr_3addr *hdr;
+
+ while (!list_empty(&priv->tx_pend_list)) {
+ /* if there isn't enough space in TBD queue, then
+ * don't stuff a new one in.
+ * NOTE: 4 are needed as a data will take two,
+ * and there is a minimum of 2 that must be
+ * maintained between the r and w indexes
+ */
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ if (unlikely(1 + packet->info.d_struct.txb->nr_frags >
+ IPW_MAX_BDS)) {
+ /* TODO: Support merging buffers if more than
+ * IPW_MAX_BDS are used */
+ IPW_DEBUG_INFO("%s: Maximum BD theshold exceeded. "
+ "Increase fragmentation level.\n",
+ priv->net_dev->name);
+ }
+
+ if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) {
+ IPW_DEBUG_TX("no room in tx_queue\n");
+ break;
+ }
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ tbd = &txq->drv[txq->next];
+
+ packet->index = txq->next;
+
+ ipw_hdr = packet->info.d_struct.data;
+ hdr = (struct ieee80211_hdr_3addr *)packet->info.d_struct.txb->
+ fragments[0]->data;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA) {
+ /* To DS: Addr1 = BSSID, Addr2 = SA,
+ Addr3 = DA */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN);
+ } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA,
+ Addr3 = BSSID */
+ memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN);
+ }
+
+ ipw_hdr->host_command_reg = SEND;
+ ipw_hdr->host_command_reg1 = 0;
+
+ /* For now we only support host based encryption */
+ ipw_hdr->needs_encryption = 0;
+ ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted;
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ ipw_hdr->fragment_size =
+ packet->info.d_struct.txb->frag_size -
+ IEEE80211_3ADDR_LEN;
+ else
+ ipw_hdr->fragment_size = 0;
+
+ tbd->host_addr = packet->info.d_struct.data_phys;
+ tbd->buf_length = sizeof(struct ipw2100_data_header);
+ tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags;
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+ txq->next++;
+ txq->next %= txq->entries;
+
+ IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n",
+ packet->index, tbd->host_addr, tbd->buf_length);
+#ifdef CONFIG_IPW2100_DEBUG
+ if (packet->info.d_struct.txb->nr_frags > 1)
+ IPW_DEBUG_FRAG("fragment Tx: %d frames\n",
+ packet->info.d_struct.txb->nr_frags);
+#endif
+
+ for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) {
+ tbd = &txq->drv[txq->next];
+ if (i == packet->info.d_struct.txb->nr_frags - 1)
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_INTERRUPT_ENABLE;
+ else
+ tbd->status.info.field =
+ IPW_BD_STATUS_TX_FRAME_802_3 |
+ IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT;
+
+ tbd->buf_length = packet->info.d_struct.txb->
+ fragments[i]->len - IEEE80211_3ADDR_LEN;
+
+ tbd->host_addr = pci_map_single(priv->pci_dev,
+ packet->info.d_struct.
+ txb->fragments[i]->
+ data +
+ IEEE80211_3ADDR_LEN,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+
+ IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
+ txq->next, tbd->host_addr,
+ tbd->buf_length);
+
+ pci_dma_sync_single_for_device(priv->pci_dev,
+ tbd->host_addr,
+ tbd->buf_length,
+ PCI_DMA_TODEVICE);
+
+ txq->next++;
+ txq->next %= txq->entries;
+ }
+
+ txq->available -= 1 + packet->info.d_struct.txb->nr_frags;
+ SET_STAT(&priv->txq_stat, txq->available);
+
+ list_add_tail(element, &priv->fw_pend_list);
+ INC_STAT(&priv->fw_pend_stat);
+ }
+
+ if (txq->next != next) {
+ /* kick off the DMA by notifying firmware the
+ * write index has moved; make sure TBD stores are sync'd */
+ write_register(priv->net_dev,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX,
+ txq->next);
+ }
+ return;
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv)
+{
+ struct net_device *dev = priv->net_dev;
+ unsigned long flags;
+ u32 inta, tmp;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ ipw2100_disable_interrupts(priv);
+
+ read_register(dev, IPW_REG_INTA, &inta);
+
+ IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ priv->in_isr++;
+ priv->interrupts++;
+
+ /* We do not loop and keep polling for more interrupts as this
+ * is frowned upon and doesn't play nicely with other potentially
+ * chained IRQs */
+ IPW_DEBUG_ISR("INTA: 0x%08lX\n",
+ (unsigned long)inta & IPW_INTERRUPT_MASK);
+
+ if (inta & IPW2100_INTA_FATAL_ERROR) {
+ printk(KERN_WARNING DRV_NAME
+ ": Fatal interrupt. Scheduling firmware restart.\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR);
+
+ read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error);
+ IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n",
+ priv->net_dev->name, priv->fatal_error);
+
+ read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp);
+ IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n",
+ priv->net_dev->name, tmp);
+
+ /* Wake up any sleeping jobs */
+ schedule_reset(priv);
+ }
+
+ if (inta & IPW2100_INTA_PARITY_ERROR) {
+ printk(KERN_ERR DRV_NAME
+ ": ***** PARITY ERROR INTERRUPT !!!! \n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR);
+ }
+
+ if (inta & IPW2100_INTA_RX_TRANSFER) {
+ IPW_DEBUG_ISR("RX interrupt\n");
+
+ priv->rx_interrupts++;
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER);
+
+ __ipw2100_rx_process(priv);
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_TRANSFER) {
+ IPW_DEBUG_ISR("TX interrupt\n");
+
+ priv->tx_interrupts++;
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER);
+
+ __ipw2100_tx_complete(priv);
+ ipw2100_tx_send_commands(priv);
+ ipw2100_tx_send_data(priv);
+ }
+
+ if (inta & IPW2100_INTA_TX_COMPLETE) {
+ IPW_DEBUG_ISR("TX complete\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE);
+
+ __ipw2100_tx_complete(priv);
+ }
+
+ if (inta & IPW2100_INTA_EVENT_INTERRUPT) {
+ /* ipw2100_handle_event(dev); */
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT);
+ }
+
+ if (inta & IPW2100_INTA_FW_INIT_DONE) {
+ IPW_DEBUG_ISR("FW init done interrupt\n");
+ priv->inta_other++;
+
+ read_register(dev, IPW_REG_INTA, &tmp);
+ if (tmp & (IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR)) {
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_FATAL_ERROR |
+ IPW2100_INTA_PARITY_ERROR);
+ }
+
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE);
+ }
+
+ if (inta & IPW2100_INTA_STATUS_CHANGE) {
+ IPW_DEBUG_ISR("Status change interrupt\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE);
+ }
+
+ if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) {
+ IPW_DEBUG_ISR("slave host mode interrupt\n");
+ priv->inta_other++;
+ write_register(dev, IPW_REG_INTA,
+ IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE);
+ }
+
+ priv->in_isr--;
+ ipw2100_enable_interrupts(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_ISR("exit\n");
+}
+
+static irqreturn_t ipw2100_interrupt(int irq, void *data)
+{
+ struct ipw2100_priv *priv = data;
+ u32 inta, inta_mask;
+
+ if (!data)
+ return IRQ_NONE;
+
+ spin_lock(&priv->low_lock);
+
+ /* We check to see if we should be ignoring interrupts before
+ * we touch the hardware. During ucode load if we try and handle
+ * an interrupt we can cause keyboard problems as well as cause
+ * the ucode to fail to initialize */
+ if (!(priv->status & STATUS_INT_ENABLED)) {
+ /* Shared IRQ */
+ goto none;
+ }
+
+ read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask);
+ read_register(priv->net_dev, IPW_REG_INTA, &inta);
+
+ if (inta == 0xFFFFFFFF) {
+ /* Hardware disappeared */
+ printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n");
+ goto none;
+ }
+
+ inta &= IPW_INTERRUPT_MASK;
+
+ if (!(inta & inta_mask)) {
+ /* Shared interrupt */
+ goto none;
+ }
+
+ /* We disable the hardware interrupt here just to prevent unneeded
+ * calls to be made. We disable this again within the actual
+ * work tasklet, so if another part of the code re-enables the
+ * interrupt, that is fine */
+ ipw2100_disable_interrupts(priv);
+
+ tasklet_schedule(&priv->irq_tasklet);
+ spin_unlock(&priv->low_lock);
+
+ return IRQ_HANDLED;
+ none:
+ spin_unlock(&priv->low_lock);
+ return IRQ_NONE;
+}
+
+static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev,
+ int pri)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_INFO("Can not transmit when not connected.\n");
+ priv->ieee->stats.tx_carrier_errors++;
+ netif_stop_queue(dev);
+ goto fail_unlock;
+ }
+
+ if (list_empty(&priv->tx_free_list))
+ goto fail_unlock;
+
+ element = priv->tx_free_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ packet->info.d_struct.txb = txb;
+
+ IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len);
+ printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len);
+
+ packet->jiffy_start = jiffies;
+
+ list_del(element);
+ DEC_STAT(&priv->tx_free_stat);
+
+ list_add_tail(element, &priv->tx_pend_list);
+ INC_STAT(&priv->tx_pend_stat);
+
+ ipw2100_tx_send_data(priv);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return 0;
+
+ fail_unlock:
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+ return 1;
+}
+
+static int ipw2100_msg_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ priv->msg_buffers =
+ (struct ipw2100_tx_packet *)kmalloc(IPW_COMMAND_POOL_SIZE *
+ sizeof(struct
+ ipw2100_tx_packet),
+ GFP_KERNEL);
+ if (!priv->msg_buffers) {
+ printk(KERN_ERR DRV_NAME ": %s: PCI alloc failed for msg "
+ "buffers.\n", priv->net_dev->name);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ v = pci_alloc_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header), &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: PCI alloc failed for msg "
+ "buffers.\n", priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ memset(v, 0, sizeof(struct ipw2100_cmd_header));
+
+ priv->msg_buffers[i].type = COMMAND;
+ priv->msg_buffers[i].info.c_struct.cmd =
+ (struct ipw2100_cmd_header *)v;
+ priv->msg_buffers[i].info.c_struct.cmd_phys = p;
+ }
+
+ if (i == IPW_COMMAND_POOL_SIZE)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[j].info.c_struct.cmd,
+ priv->msg_buffers[j].info.c_struct.
+ cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+
+ return err;
+}
+
+static int ipw2100_msg_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++)
+ list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list);
+ SET_STAT(&priv->msg_free_stat, i);
+
+ return 0;
+}
+
+static void ipw2100_msg_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ if (!priv->msg_buffers)
+ return;
+
+ for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_cmd_header),
+ priv->msg_buffers[i].info.c_struct.cmd,
+ priv->msg_buffers[i].info.c_struct.
+ cmd_phys);
+ }
+
+ kfree(priv->msg_buffers);
+ priv->msg_buffers = NULL;
+}
+
+static ssize_t show_pci(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev);
+ char *out = buf;
+ int i, j;
+ u32 val;
+
+ for (i = 0; i < 16; i++) {
+ out += sprintf(out, "[%08X] ", i * 16);
+ for (j = 0; j < 16; j += 4) {
+ pci_read_config_dword(pci_dev, i * 16 + j, &val);
+ out += sprintf(out, "%08X ", val);
+ }
+ out += sprintf(out, "\n");
+ }
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_status(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_capability(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->capability);
+}
+
+static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL);
+
+#define IPW2100_REG(x) { IPW_ ##x, #x }
+static const struct {
+ u32 addr;
+ const char *name;
+} hw_data[] = {
+IPW2100_REG(REG_GP_CNTRL),
+ IPW2100_REG(REG_GPIO),
+ IPW2100_REG(REG_INTA),
+ IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),};
+#define IPW2100_NIC(x, s) { x, #x, s }
+static const struct {
+ u32 addr;
+ const char *name;
+ size_t size;
+} nic_data[] = {
+IPW2100_NIC(IPW2100_CONTROL_REG, 2),
+ IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),};
+#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d }
+static const struct {
+ u8 index;
+ const char *name;
+ const char *desc;
+} ord_data[] = {
+IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_HOST_COMPLETE,
+ "successful Host Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA,
+ "successful Directed Tx's (MSDU)"),
+ IPW2100_ORD(STAT_TX_DIR_DATA1,
+ "successful Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA2,
+ "successful Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA5_5,
+ "successful Directed Tx's (MSDU) @ 5_5MB"),
+ IPW2100_ORD(STAT_TX_DIR_DATA11,
+ "successful Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA1,
+ "successful Non_Directed Tx's (MSDU) @ 1MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA2,
+ "successful Non_Directed Tx's (MSDU) @ 2MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA5_5,
+ "successful Non_Directed Tx's (MSDU) @ 5.5MB"),
+ IPW2100_ORD(STAT_TX_NODIR_DATA11,
+ "successful Non_Directed Tx's (MSDU) @ 11MB"),
+ IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"),
+ IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"),
+ IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"),
+ IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"),
+ IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"),
+ IPW2100_ORD(STAT_TX_ASSN_RESP,
+ "successful Association response Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN,
+ "successful Reassociation Tx's"),
+ IPW2100_ORD(STAT_TX_REASSN_RESP,
+ "successful Reassociation response Tx's"),
+ IPW2100_ORD(STAT_TX_PROBE,
+ "probes successfully transmitted"),
+ IPW2100_ORD(STAT_TX_PROBE_RESP,
+ "probe responses successfully transmitted"),
+ IPW2100_ORD(STAT_TX_BEACON, "tx beacon"),
+ IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"),
+ IPW2100_ORD(STAT_TX_DISASSN,
+ "successful Disassociation TX"),
+ IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"),
+ IPW2100_ORD(STAT_TX_DEAUTH,
+ "successful Deauthentication TX"),
+ IPW2100_ORD(STAT_TX_TOTAL_BYTES,
+ "Total successful Tx data bytes"),
+ IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"),
+ IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"),
+ IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"),
+ IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"),
+ IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP,
+ "times max tries in a hop failed"),
+ IPW2100_ORD(STAT_TX_DISASSN_FAIL,
+ "times disassociation failed"),
+ IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"),
+ IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"),
+ IPW2100_ORD(STAT_RX_HOST, "packets passed to host"),
+ IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"),
+ IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA5_5,
+ "directed packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA1,
+ "nondirected packets at 1MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA2,
+ "nondirected packets at 2MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA5_5,
+ "nondirected packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_NODIR_DATA11,
+ "nondirected packets at 11MB"),
+ IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"),
+ IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS,
+ "Rx CTS"),
+ IPW2100_ORD(STAT_RX_ACK, "Rx ACK"),
+ IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"),
+ IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"),
+ IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"),
+ IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"),
+ IPW2100_ORD(STAT_RX_REASSN_RESP,
+ "Reassociation response Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"),
+ IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"),
+ IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"),
+ IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"),
+ IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"),
+ IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"),
+ IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"),
+ IPW2100_ORD(STAT_RX_TOTAL_BYTES,
+ "Total rx data bytes received"),
+ IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"),
+ IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"),
+ IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE1,
+ "duplicate rx packets at 1MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE2,
+ "duplicate rx packets at 2MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE5_5,
+ "duplicate rx packets at 5.5MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE11,
+ "duplicate rx packets at 11MB"),
+ IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"),
+ IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"),
+ IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"),
+ IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"),
+ IPW2100_ORD(STAT_RX_INVALID_PROTOCOL,
+ "rx frames with invalid protocol"),
+ IPW2100_ORD(SYS_BOOT_TIME, "Boot time"),
+ IPW2100_ORD(STAT_RX_NO_BUFFER,
+ "rx frames rejected due to no buffer"),
+ IPW2100_ORD(STAT_RX_MISSING_FRAG,
+ "rx frames dropped due to missing fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAG,
+ "rx frames dropped due to non-sequential fragment"),
+ IPW2100_ORD(STAT_RX_ORPHAN_FRAME,
+ "rx frames dropped due to unmatched 1st frame"),
+ IPW2100_ORD(STAT_RX_FRAG_AGEOUT,
+ "rx frames dropped due to uncompleted frame"),
+ IPW2100_ORD(STAT_RX_ICV_ERRORS,
+ "ICV errors during decryption"),
+ IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"),
+ IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"),
+ IPW2100_ORD(STAT_PSP_POLL_TIMEOUT,
+ "poll response timeouts"),
+ IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT,
+ "timeouts waiting for last {broad,multi}cast pkt"),
+ IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"),
+ IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"),
+ IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"),
+ IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"),
+ IPW2100_ORD(STAT_PERCENT_MISSED_BCNS,
+ "current calculation of % missed beacons"),
+ IPW2100_ORD(STAT_PERCENT_RETRIES,
+ "current calculation of % missed tx retries"),
+ IPW2100_ORD(ASSOCIATED_AP_PTR,
+ "0 if not associated, else pointer to AP table entry"),
+ IPW2100_ORD(AVAILABLE_AP_CNT,
+ "AP's decsribed in the AP table"),
+ IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"),
+ IPW2100_ORD(STAT_AP_ASSNS, "associations"),
+ IPW2100_ORD(STAT_ASSN_FAIL, "association failures"),
+ IPW2100_ORD(STAT_ASSN_RESP_FAIL,
+ "failures due to response fail"),
+ IPW2100_ORD(STAT_FULL_SCANS, "full scans"),
+ IPW2100_ORD(CARD_DISABLED, "Card Disabled"),
+ IPW2100_ORD(STAT_ROAM_INHIBIT,
+ "times roaming was inhibited due to activity"),
+ IPW2100_ORD(RSSI_AT_ASSN,
+ "RSSI of associated AP at time of association"),
+ IPW2100_ORD(STAT_ASSN_CAUSE1,
+ "reassociation: no probe response or TX on hop"),
+ IPW2100_ORD(STAT_ASSN_CAUSE2,
+ "reassociation: poor tx/rx quality"),
+ IPW2100_ORD(STAT_ASSN_CAUSE3,
+ "reassociation: tx/rx quality (excessive AP load"),
+ IPW2100_ORD(STAT_ASSN_CAUSE4,
+ "reassociation: AP RSSI level"),
+ IPW2100_ORD(STAT_ASSN_CAUSE5,
+ "reassociations due to load leveling"),
+ IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"),
+ IPW2100_ORD(STAT_AUTH_RESP_FAIL,
+ "times authentication response failed"),
+ IPW2100_ORD(STATION_TABLE_CNT,
+ "entries in association table"),
+ IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"),
+ IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"),
+ IPW2100_ORD(COUNTRY_CODE,
+ "IEEE country code as recv'd from beacon"),
+ IPW2100_ORD(COUNTRY_CHANNELS,
+ "channels suported by country"),
+ IPW2100_ORD(RESET_CNT, "adapter resets (warm)"),
+ IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"),
+ IPW2100_ORD(ANTENNA_DIVERSITY,
+ "TRUE if antenna diversity is disabled"),
+ IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"),
+ IPW2100_ORD(OUR_FREQ,
+ "current radio freq lower digits - channel ID"),
+ IPW2100_ORD(RTC_TIME, "current RTC time"),
+ IPW2100_ORD(PORT_TYPE, "operating mode"),
+ IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"),
+ IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"),
+ IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"),
+ IPW2100_ORD(BASIC_RATES, "basic tx rates"),
+ IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"),
+ IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"),
+ IPW2100_ORD(CAPABILITIES,
+ "Management frame capability field"),
+ IPW2100_ORD(AUTH_TYPE, "Type of authentication"),
+ IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"),
+ IPW2100_ORD(RTS_THRESHOLD,
+ "Min packet length for RTS handshaking"),
+ IPW2100_ORD(INT_MODE, "International mode"),
+ IPW2100_ORD(FRAGMENTATION_THRESHOLD,
+ "protocol frag threshold"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS,
+ "EEPROM offset in SRAM"),
+ IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE,
+ "EEPROM size in SRAM"),
+ IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"),
+ IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS,
+ "EEPROM IBSS 11b channel set"),
+ IPW2100_ORD(MAC_VERSION, "MAC Version"),
+ IPW2100_ORD(MAC_REVISION, "MAC Revision"),
+ IPW2100_ORD(RADIO_VERSION, "Radio Version"),
+ IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"),
+ IPW2100_ORD(UCODE_VERSION, "Ucode Version"),};
+
+static ssize_t show_registers(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char *out = buf;
+ u32 val = 0;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "Register");
+
+ for (i = 0; i < ARRAY_SIZE(hw_data); i++) {
+ read_register(dev, hw_data[i].addr, &val);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ hw_data[i].name, hw_data[i].addr, val);
+ }
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL);
+
+static ssize_t show_hardware(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char *out = buf;
+ int i;
+
+ out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry");
+
+ for (i = 0; i < ARRAY_SIZE(nic_data); i++) {
+ u8 tmp8;
+ u16 tmp16;
+ u32 tmp32;
+
+ switch (nic_data[i].size) {
+ case 1:
+ read_nic_byte(dev, nic_data[i].addr, &tmp8);
+ out += sprintf(out, "%30s [%08X] : %02X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp8);
+ break;
+ case 2:
+ read_nic_word(dev, nic_data[i].addr, &tmp16);
+ out += sprintf(out, "%30s [%08X] : %04X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp16);
+ break;
+ case 4:
+ read_nic_dword(dev, nic_data[i].addr, &tmp32);
+ out += sprintf(out, "%30s [%08X] : %08X\n",
+ nic_data[i].name, nic_data[i].addr,
+ tmp32);
+ break;
+ }
+ }
+ return out - buf;
+}
+
+static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL);
+
+static ssize_t show_memory(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ static unsigned long loop = 0;
+ int len = 0;
+ u32 buffer[4];
+ int i;
+ char line[81];
+
+ if (loop >= 0x30000)
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 && loop < 0x30000) {
+
+ if (priv->snapshot[0])
+ for (i = 0; i < 4; i++)
+ buffer[i] =
+ *(u32 *) SNAPSHOT_ADDR(loop + i * 4);
+ else
+ for (i = 0; i < 4; i++)
+ read_nic_dword(dev, loop + i * 4, &buffer[i]);
+
+ if (priv->dump_raw)
+ len += sprintf(buf + len,
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c"
+ "%c%c%c%c",
+ ((u8 *) buffer)[0x0],
+ ((u8 *) buffer)[0x1],
+ ((u8 *) buffer)[0x2],
+ ((u8 *) buffer)[0x3],
+ ((u8 *) buffer)[0x4],
+ ((u8 *) buffer)[0x5],
+ ((u8 *) buffer)[0x6],
+ ((u8 *) buffer)[0x7],
+ ((u8 *) buffer)[0x8],
+ ((u8 *) buffer)[0x9],
+ ((u8 *) buffer)[0xa],
+ ((u8 *) buffer)[0xb],
+ ((u8 *) buffer)[0xc],
+ ((u8 *) buffer)[0xd],
+ ((u8 *) buffer)[0xe],
+ ((u8 *) buffer)[0xf]);
+ else
+ len += sprintf(buf + len, "%s\n",
+ snprint_line(line, sizeof(line),
+ (u8 *) buffer, 16, loop));
+ loop += 16;
+ }
+
+ return len;
+}
+
+static ssize_t store_memory(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ const char *p = buf;
+
+ (void)dev; /* kill unused-var warning for debug-only code */
+
+ if (count < 1)
+ return count;
+
+ if (p[0] == '1' ||
+ (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n",
+ dev->name);
+ priv->dump_raw = 1;
+
+ } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' &&
+ tolower(p[1]) == 'f')) {
+ IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n",
+ dev->name);
+ priv->dump_raw = 0;
+
+ } else if (tolower(p[0]) == 'r') {
+ IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name);
+ ipw2100_snapshot_free(priv);
+
+ } else
+ IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, "
+ "reset = clear memory snapshot\n", dev->name);
+
+ return count;
+}
+
+static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory);
+
+static ssize_t show_ordinals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ u32 val = 0;
+ int len = 0;
+ u32 val_len;
+ static int loop = 0;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return 0;
+
+ if (loop >= ARRAY_SIZE(ord_data))
+ loop = 0;
+
+ /* sysfs provides us PAGE_SIZE buffer */
+ while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) {
+ val_len = sizeof(u32);
+
+ if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val,
+ &val_len))
+ len += sprintf(buf + len, "[0x%02X] = ERROR %s\n",
+ ord_data[loop].index,
+ ord_data[loop].desc);
+ else
+ len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n",
+ ord_data[loop].index, val,
+ ord_data[loop].desc);
+ loop++;
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL);
+
+static ssize_t show_stats(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char *out = buf;
+
+ out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n",
+ priv->interrupts, priv->tx_interrupts,
+ priv->rx_interrupts, priv->inta_other);
+ out += sprintf(out, "firmware resets: %d\n", priv->resets);
+ out += sprintf(out, "firmware hangs: %d\n", priv->hangs);
+#ifdef CONFIG_IPW2100_DEBUG
+ out += sprintf(out, "packet mismatch image: %s\n",
+ priv->snapshot[0] ? "YES" : "NO");
+#endif
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL);
+
+static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
+{
+ int err;
+
+ if (mode == priv->ieee->iw_mode)
+ return 0;
+
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+
+ switch (mode) {
+ case IW_MODE_INFRA:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+ case IW_MODE_ADHOC:
+ priv->net_dev->type = ARPHRD_ETHER;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ priv->last_mode = priv->ieee->iw_mode;
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ }
+
+ priv->ieee->iw_mode = mode;
+
+#ifdef CONFIG_PM
+ /* Indicate ipw2100_download_firmware download firmware
+ * from disk instead of memory. */
+ ipw2100_firmware.version = 0;
+#endif
+
+ printk(KERN_INFO "%s: Reseting on mode change.\n", priv->net_dev->name);
+ priv->reset_backoff = 0;
+ schedule_reset(priv);
+
+ return 0;
+}
+
+static ssize_t show_internals(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ int len = 0;
+
+#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x)
+
+ if (priv->status & STATUS_ASSOCIATED)
+ len += sprintf(buf + len, "connected: %lu\n",
+ get_seconds() - priv->connect_start);
+ else
+ len += sprintf(buf + len, "not connected\n");
+
+ DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p");
+ DUMP_VAR(status, "08lx");
+ DUMP_VAR(config, "08lx");
+ DUMP_VAR(capability, "08lx");
+
+ len +=
+ sprintf(buf + len, "last_rtc: %lu\n",
+ (unsigned long)priv->last_rtc);
+
+ DUMP_VAR(fatal_error, "d");
+ DUMP_VAR(stop_hang_check, "d");
+ DUMP_VAR(stop_rf_kill, "d");
+ DUMP_VAR(messages_sent, "d");
+
+ DUMP_VAR(tx_pend_stat.value, "d");
+ DUMP_VAR(tx_pend_stat.hi, "d");
+
+ DUMP_VAR(tx_free_stat.value, "d");
+ DUMP_VAR(tx_free_stat.lo, "d");
+
+ DUMP_VAR(msg_free_stat.value, "d");
+ DUMP_VAR(msg_free_stat.lo, "d");
+
+ DUMP_VAR(msg_pend_stat.value, "d");
+ DUMP_VAR(msg_pend_stat.hi, "d");
+
+ DUMP_VAR(fw_pend_stat.value, "d");
+ DUMP_VAR(fw_pend_stat.hi, "d");
+
+ DUMP_VAR(txq_stat.value, "d");
+ DUMP_VAR(txq_stat.lo, "d");
+
+ DUMP_VAR(ieee->scans, "d");
+ DUMP_VAR(reset_backoff, "d");
+
+ return len;
+}
+
+static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL);
+
+static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char essid[IW_ESSID_MAX_SIZE + 1];
+ u8 bssid[ETH_ALEN];
+ u32 chan = 0;
+ char *out = buf;
+ int length;
+ int ret;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return 0;
+
+ memset(essid, 0, sizeof(essid));
+ memset(bssid, 0, sizeof(bssid));
+
+ length = IW_ESSID_MAX_SIZE;
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(bssid);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ bssid, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ length = sizeof(u32);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length);
+ if (ret)
+ IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
+ __LINE__);
+
+ out += sprintf(out, "ESSID: %s\n", essid);
+ out += sprintf(out, "BSSID: %pM\n", bssid);
+ out += sprintf(out, "Channel: %d\n", chan);
+
+ return out - buf;
+}
+
+static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL);
+
+#ifdef CONFIG_IPW2100_DEBUG
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+ return sprintf(buf, "0x%08X\n", ipw2100_debug_level);
+}
+
+static ssize_t store_debug_level(struct device_driver *d,
+ const char *buf, size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buf)
+ IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf);
+ else
+ ipw2100_debug_level = val;
+
+ return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level,
+ store_debug_level);
+#endif /* CONFIG_IPW2100_DEBUG */
+
+static ssize_t show_fatal_error(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ char *out = buf;
+ int i;
+
+ if (priv->fatal_error)
+ out += sprintf(out, "0x%08X\n", priv->fatal_error);
+ else
+ out += sprintf(out, "0\n");
+
+ for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) {
+ if (!priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE])
+ continue;
+
+ out += sprintf(out, "%d. 0x%08X\n", i,
+ priv->fatal_errors[(priv->fatal_index - i) %
+ IPW2100_ERROR_QUEUE]);
+ }
+
+ return out - buf;
+}
+
+static ssize_t store_fatal_error(struct device *d,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ schedule_reset(priv);
+ return count;
+}
+
+static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error,
+ store_fatal_error);
+
+static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", priv->ieee->scan_age);
+}
+
+static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char buffer[] = "00000000";
+ unsigned long len =
+ (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
+ unsigned long val;
+ char *p = buffer;
+
+ (void)dev; /* kill unused-var warning for debug-only code */
+
+ IPW_DEBUG_INFO("enter\n");
+
+ strncpy(buffer, buf, len);
+ buffer[len] = 0;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buffer) {
+ IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
+ } else {
+ priv->ieee->scan_age = val;
+ IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+ return len;
+}
+
+static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ /* 0 - RF kill not enabled
+ 1 - SW based RF kill active (sysfs)
+ 2 - HW based RF kill active
+ 3 - Both HW and SW baed RF kill active */
+ struct ipw2100_priv *priv = (struct ipw2100_priv *)d->driver_data;
+ int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+ (rf_kill_active(priv) ? 0x2 : 0x0);
+ return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
+{
+ if ((disable_radio ? 1 : 0) ==
+ (priv->status & STATUS_RF_KILL_SW ? 1 : 0))
+ return 0;
+
+ IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
+ disable_radio ? "OFF" : "ON");
+
+ mutex_lock(&priv->action_mutex);
+
+ if (disable_radio) {
+ priv->status |= STATUS_RF_KILL_SW;
+ ipw2100_down(priv);
+ } else {
+ priv->status &= ~STATUS_RF_KILL_SW;
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+ "disabled by HW switch\n");
+ /* Make sure the RF_KILL check timer is running */
+ priv->stop_rf_kill = 0;
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ round_jiffies_relative(HZ));
+ } else
+ schedule_reset(priv);
+ }
+
+ mutex_unlock(&priv->action_mutex);
+ return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw2100_priv *priv = dev_get_drvdata(d);
+ ipw_radio_kill_sw(priv, buf[0] == '1');
+ return count;
+}
+
+static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
+
+static struct attribute *ipw2100_sysfs_entries[] = {
+ &dev_attr_hardware.attr,
+ &dev_attr_registers.attr,
+ &dev_attr_ordinals.attr,
+ &dev_attr_pci.attr,
+ &dev_attr_stats.attr,
+ &dev_attr_internals.attr,
+ &dev_attr_bssinfo.attr,
+ &dev_attr_memory.attr,
+ &dev_attr_scan_age.attr,
+ &dev_attr_fatal_error.attr,
+ &dev_attr_rf_kill.attr,
+ &dev_attr_cfg.attr,
+ &dev_attr_status.attr,
+ &dev_attr_capability.attr,
+ NULL,
+};
+
+static struct attribute_group ipw2100_attribute_group = {
+ .attrs = ipw2100_sysfs_entries,
+};
+
+static int status_queue_allocate(struct ipw2100_priv *priv, int entries)
+{
+ struct ipw2100_status_queue *q = &priv->status_queue;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ q->size = entries * sizeof(struct ipw2100_status);
+ q->drv =
+ (struct ipw2100_status *)pci_alloc_consistent(priv->pci_dev,
+ q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_WARNING("Can not allocate status queue.\n");
+ return -ENOMEM;
+ }
+
+ memset(q->drv, 0, q->size);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void status_queue_free(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->status_queue.drv) {
+ pci_free_consistent(priv->pci_dev, priv->status_queue.size,
+ priv->status_queue.drv,
+ priv->status_queue.nic);
+ priv->status_queue.drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int bd_queue_allocate(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q, int entries)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ memset(q, 0, sizeof(struct ipw2100_bd_queue));
+
+ q->entries = entries;
+ q->size = entries * sizeof(struct ipw2100_bd);
+ q->drv = pci_alloc_consistent(priv->pci_dev, q->size, &q->nic);
+ if (!q->drv) {
+ IPW_DEBUG_INFO
+ ("can't allocate shared memory for buffer descriptors\n");
+ return -ENOMEM;
+ }
+ memset(q->drv, 0, q->size);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ if (!q)
+ return;
+
+ if (q->drv) {
+ pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic);
+ q->drv = NULL;
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void bd_queue_initialize(struct ipw2100_priv *priv,
+ struct ipw2100_bd_queue *q, u32 base, u32 size,
+ u32 r, u32 w)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv,
+ (u32) q->nic);
+
+ write_register(priv->net_dev, base, q->nic);
+ write_register(priv->net_dev, size, q->entries);
+ write_register(priv->net_dev, r, q->oldest);
+ write_register(priv->net_dev, w, q->next);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_kill_workqueue(struct ipw2100_priv *priv)
+{
+ if (priv->workqueue) {
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+ cancel_delayed_work(&priv->reset_work);
+ cancel_delayed_work(&priv->security_work);
+ cancel_delayed_work(&priv->wx_event_work);
+ cancel_delayed_work(&priv->hang_check);
+ cancel_delayed_work(&priv->rf_kill);
+ cancel_delayed_work(&priv->scan_event_later);
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ }
+}
+
+static int ipw2100_tx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+ void *v;
+ dma_addr_t p;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n",
+ priv->net_dev->name);
+ return err;
+ }
+
+ priv->tx_buffers =
+ (struct ipw2100_tx_packet *)kmalloc(TX_PENDED_QUEUE_LENGTH *
+ sizeof(struct
+ ipw2100_tx_packet),
+ GFP_ATOMIC);
+ if (!priv->tx_buffers) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: alloc failed form tx buffers.\n",
+ priv->net_dev->name);
+ bd_queue_free(priv, &priv->tx_queue);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ v = pci_alloc_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ &p);
+ if (!v) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: PCI alloc failed for tx " "buffers.\n",
+ priv->net_dev->name);
+ err = -ENOMEM;
+ break;
+ }
+
+ priv->tx_buffers[i].type = DATA;
+ priv->tx_buffers[i].info.d_struct.data =
+ (struct ipw2100_data_header *)v;
+ priv->tx_buffers[i].info.d_struct.data_phys = p;
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ if (i == TX_PENDED_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[j].info.d_struct.data,
+ priv->tx_buffers[j].info.d_struct.
+ data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ return err;
+}
+
+static void ipw2100_tx_initialize(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ /*
+ * reinitialize packet info lists
+ */
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+ /*
+ * reinitialize lists
+ */
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_STAT(&priv->tx_pend_stat);
+ INIT_STAT(&priv->tx_free_stat);
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ /* We simply drop any SKBs that have been queued for
+ * transmit */
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.
+ txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+
+ list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list);
+ }
+
+ SET_STAT(&priv->tx_free_stat, i);
+
+ priv->tx_queue.oldest = 0;
+ priv->tx_queue.available = priv->tx_queue.entries;
+ priv->tx_queue.next = 0;
+ INIT_STAT(&priv->txq_stat);
+ SET_STAT(&priv->txq_stat, priv->tx_queue.available);
+
+ bd_queue_initialize(priv, &priv->tx_queue,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX,
+ IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX);
+
+ IPW_DEBUG_INFO("exit\n");
+
+}
+
+static void ipw2100_tx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->tx_queue);
+
+ if (!priv->tx_buffers)
+ return;
+
+ for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) {
+ if (priv->tx_buffers[i].info.d_struct.txb) {
+ ieee80211_txb_free(priv->tx_buffers[i].info.d_struct.
+ txb);
+ priv->tx_buffers[i].info.d_struct.txb = NULL;
+ }
+ if (priv->tx_buffers[i].info.d_struct.data)
+ pci_free_consistent(priv->pci_dev,
+ sizeof(struct ipw2100_data_header),
+ priv->tx_buffers[i].info.d_struct.
+ data,
+ priv->tx_buffers[i].info.d_struct.
+ data_phys);
+ }
+
+ kfree(priv->tx_buffers);
+ priv->tx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_rx_allocate(struct ipw2100_priv *priv)
+{
+ int i, j, err = -EINVAL;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed bd_queue_allocate\n");
+ return err;
+ }
+
+ err = status_queue_allocate(priv, RX_QUEUE_LENGTH);
+ if (err) {
+ IPW_DEBUG_INFO("failed status_queue_allocate\n");
+ bd_queue_free(priv, &priv->rx_queue);
+ return err;
+ }
+
+ /*
+ * allocate packets
+ */
+ priv->rx_buffers = (struct ipw2100_rx_packet *)
+ kmalloc(RX_QUEUE_LENGTH * sizeof(struct ipw2100_rx_packet),
+ GFP_KERNEL);
+ if (!priv->rx_buffers) {
+ IPW_DEBUG_INFO("can't allocate rx packet buffer table\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+ err = ipw2100_alloc_skb(priv, packet);
+ if (unlikely(err)) {
+ err = -ENOMEM;
+ break;
+ }
+
+ /* The BD holds the cache aligned address */
+ priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+ priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH;
+ priv->status_queue.drv[i].status_fields = 0;
+ }
+
+ if (i == RX_QUEUE_LENGTH)
+ return 0;
+
+ for (j = 0; j < i; j++) {
+ pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr,
+ sizeof(struct ipw2100_rx_packet),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[j].skb);
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ bd_queue_free(priv, &priv->rx_queue);
+
+ status_queue_free(priv);
+
+ return err;
+}
+
+static void ipw2100_rx_initialize(struct ipw2100_priv *priv)
+{
+ IPW_DEBUG_INFO("enter\n");
+
+ priv->rx_queue.oldest = 0;
+ priv->rx_queue.available = priv->rx_queue.entries - 1;
+ priv->rx_queue.next = priv->rx_queue.entries - 1;
+
+ INIT_STAT(&priv->rxq_stat);
+ SET_STAT(&priv->rxq_stat, priv->rx_queue.available);
+
+ bd_queue_initialize(priv, &priv->rx_queue,
+ IPW_MEM_HOST_SHARED_RX_BD_BASE,
+ IPW_MEM_HOST_SHARED_RX_BD_SIZE,
+ IPW_MEM_HOST_SHARED_RX_READ_INDEX,
+ IPW_MEM_HOST_SHARED_RX_WRITE_INDEX);
+
+ /* set up the status queue */
+ write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE,
+ priv->status_queue.nic);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static void ipw2100_rx_free(struct ipw2100_priv *priv)
+{
+ int i;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ bd_queue_free(priv, &priv->rx_queue);
+ status_queue_free(priv);
+
+ if (!priv->rx_buffers)
+ return;
+
+ for (i = 0; i < RX_QUEUE_LENGTH; i++) {
+ if (priv->rx_buffers[i].rxp) {
+ pci_unmap_single(priv->pci_dev,
+ priv->rx_buffers[i].dma_addr,
+ sizeof(struct ipw2100_rx),
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(priv->rx_buffers[i].skb);
+ }
+ }
+
+ kfree(priv->rx_buffers);
+ priv->rx_buffers = NULL;
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+static int ipw2100_read_mac_address(struct ipw2100_priv *priv)
+{
+ u32 length = ETH_ALEN;
+ u8 addr[ETH_ALEN];
+
+ int err;
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length);
+ if (err) {
+ IPW_DEBUG_INFO("MAC address read failed\n");
+ return -EIO;
+ }
+
+ memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN);
+ IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr);
+
+ return 0;
+}
+
+/********************************************************************
+ *
+ * Firmware Commands
+ *
+ ********************************************************************/
+
+static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = ADAPTER_ADDRESS,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_MAC_ADDRESS\n");
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->config & CFG_CUSTOM_MAC) {
+ memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN);
+ memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+ } else
+ memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr,
+ ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ IPW_DEBUG_INFO("exit\n");
+ return err;
+}
+
+static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = PORT_TYPE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ switch (port_type) {
+ case IW_MODE_INFRA:
+ cmd.host_command_parameters[0] = IPW_BSS;
+ break;
+ case IW_MODE_ADHOC:
+ cmd.host_command_parameters[0] = IPW_IBSS;
+ break;
+ }
+
+ IPW_DEBUG_HC("PORT_TYPE: %s\n",
+ port_type == IPW_IBSS ? "Ad-Hoc" : "Managed");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = CHANNEL,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(u32)
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = channel;
+
+ IPW_DEBUG_HC("CHANNEL: %d\n", channel);
+
+ /* If BSS then we don't support channel selection */
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return 0;
+
+ if ((channel != 0) &&
+ ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL)))
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err) {
+ IPW_DEBUG_INFO("Failed to set channel to %d", channel);
+ return err;
+ }
+
+ if (channel)
+ priv->config |= CFG_STATIC_CHANNEL;
+ else
+ priv->config &= ~CFG_STATIC_CHANNEL;
+
+ priv->channel = channel;
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SYSTEM_CONFIG,
+ .host_command_sequence = 0,
+ .host_command_length = 12,
+ };
+ u32 ibss_mask, len = sizeof(u32);
+ int err;
+
+ /* Set system configuration */
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START;
+
+ cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK |
+ IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE;
+
+ if (!(priv->config & CFG_LONG_PREAMBLE))
+ cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO;
+
+ err = ipw2100_get_ordinal(priv,
+ IPW_ORD_EEPROM_IBSS_11B_CHANNELS,
+ &ibss_mask, &len);
+ if (err)
+ ibss_mask = IPW_IBSS_11B_DEFAULT_MASK;
+
+ cmd.host_command_parameters[1] = REG_CHANNEL_MASK;
+ cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask;
+
+ /* 11b only */
+ /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+/* If IPv6 is configured in the kernel then we don't want to filter out all
+ * of the multicast packets as IPv6 needs some. */
+#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+ cmd.host_command = ADD_MULTICAST;
+ cmd.host_command_sequence = 0;
+ cmd.host_command_length = 0;
+
+ ipw2100_hw_send_command(priv, &cmd);
+#endif
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BASIC_TX_RATES,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = rate & TX_RATE_MASK;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ /* Set BASIC TX Rate first */
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set TX Rate */
+ cmd.host_command = TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ /* Set MSDU TX Rate */
+ cmd.host_command = MSDU_TX_RATES;
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ priv->tx_rates = rate;
+
+ return 0;
+}
+
+static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level)
+{
+ struct host_command cmd = {
+ .host_command = POWER_MODE,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = power_level;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ if (power_level == IPW_POWER_MODE_CAM)
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ else
+ priv->power_mode = IPW_POWER_ENABLED | power_level;
+
+#ifdef IPW2100_TX_POWER
+ if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) {
+ /* Set beacon interval */
+ cmd.host_command = TX_POWER_INDEX;
+ cmd.host_command_parameters[0] = (u32) priv->adhoc_power;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold)
+{
+ struct host_command cmd = {
+ .host_command = RTS_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ if (threshold & RTS_DISABLED)
+ cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD;
+ else
+ cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->rts_threshold = threshold;
+
+ return 0;
+}
+
+#if 0
+int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv,
+ u32 threshold, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = FRAG_THRESHOLD,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters[0] = 0,
+ };
+ int err;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (threshold == 0)
+ threshold = DEFAULT_FRAG_THRESHOLD;
+ else {
+ threshold = max(threshold, MIN_FRAG_THRESHOLD);
+ threshold = min(threshold, MAX_FRAG_THRESHOLD);
+ }
+
+ cmd.host_command_parameters[0] = threshold;
+
+ IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ if (!err)
+ priv->frag_threshold = threshold;
+
+ return err;
+}
+#endif
+
+static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = SHORT_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->short_retry_limit = retry;
+
+ return 0;
+}
+
+static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry)
+{
+ struct host_command cmd = {
+ .host_command = LONG_RETRY_LIMIT,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = retry;
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (err)
+ return err;
+
+ priv->long_retry_limit = retry;
+
+ return 0;
+}
+
+static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = MANDATORY_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN
+ };
+ int err;
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (bssid != NULL)
+ IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid);
+ else
+ IPW_DEBUG_HC("MANDATORY_BSSID: <clear>\n");
+#endif
+ /* if BSSID is empty then we disable mandatory bssid mode */
+ if (bssid != NULL)
+ memcpy(cmd.host_command_parameters, bssid, ETH_ALEN);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv)
+{
+ struct host_command cmd = {
+ .host_command = DISASSOCIATION_BSSID,
+ .host_command_sequence = 0,
+ .host_command_length = ETH_ALEN
+ };
+ int err;
+ int len;
+
+ IPW_DEBUG_HC("DISASSOCIATION_BSSID\n");
+
+ len = ETH_ALEN;
+ /* The Firmware currently ignores the BSSID and just disassociates from
+ * the currently associated AP -- but in the off chance that a future
+ * firmware does use the BSSID provided here, we go ahead and try and
+ * set it to the currently associated AP's BSSID */
+ memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN);
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ return err;
+}
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *,
+ struct ipw2100_wpa_assoc_frame *, int)
+ __attribute__ ((unused));
+
+static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv,
+ struct ipw2100_wpa_assoc_frame *wpa_frame,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_WPA_IE,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame),
+ };
+ int err;
+
+ IPW_DEBUG_HC("SET_WPA_IE\n");
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ memcpy(cmd.host_command_parameters, wpa_frame,
+ sizeof(struct ipw2100_wpa_assoc_frame));
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ if (ipw2100_enable_adapter(priv))
+ err = -EIO;
+ }
+
+ return err;
+}
+
+struct security_info_params {
+ u32 allowed_ciphers;
+ u16 version;
+ u8 auth_mode;
+ u8 replay_counters_number;
+ u8 unicast_using_group;
+} __attribute__ ((packed));
+
+static int ipw2100_set_security_information(struct ipw2100_priv *priv,
+ int auth_mode,
+ int security_level,
+ int unicast_using_group,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = SET_SECURITY_INFORMATION,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct security_info_params)
+ };
+ struct security_info_params *security =
+ (struct security_info_params *)&cmd.host_command_parameters;
+ int err;
+ memset(security, 0, sizeof(*security));
+
+ /* If shared key AP authentication is turned on, then we need to
+ * configure the firmware to try and use it.
+ *
+ * Actual data encryption/decryption is handled by the host. */
+ security->auth_mode = auth_mode;
+ security->unicast_using_group = unicast_using_group;
+
+ switch (security_level) {
+ default:
+ case SEC_LEVEL_0:
+ security->allowed_ciphers = IPW_NONE_CIPHER;
+ break;
+ case SEC_LEVEL_1:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER;
+ break;
+ case SEC_LEVEL_2:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER;
+ break;
+ case SEC_LEVEL_2_CKIP:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_CKIP_CIPHER;
+ break;
+ case SEC_LEVEL_3:
+ security->allowed_ciphers = IPW_WEP40_CIPHER |
+ IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER;
+ break;
+ }
+
+ IPW_DEBUG_HC
+ ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n",
+ security->auth_mode, security->allowed_ciphers, security_level);
+
+ security->replay_counters_number = 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power)
+{
+ struct host_command cmd = {
+ .host_command = TX_POWER_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err = 0;
+ u32 tmp = tx_power;
+
+ if (tx_power != IPW_TX_POWER_DEFAULT)
+ tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 /
+ (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM);
+
+ cmd.host_command_parameters[0] = tmp;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ err = ipw2100_hw_send_command(priv, &cmd);
+ if (!err)
+ priv->tx_power = tx_power;
+
+ return 0;
+}
+
+static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv,
+ u32 interval, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = BEACON_INTERVAL,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = interval;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ err = ipw2100_enable_adapter(priv);
+ if (err)
+ return err;
+ }
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+static void ipw2100_queues_initialize(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_initialize(priv);
+ ipw2100_rx_initialize(priv);
+ ipw2100_msg_initialize(priv);
+}
+
+static void ipw2100_queues_free(struct ipw2100_priv *priv)
+{
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+}
+
+static int ipw2100_queues_allocate(struct ipw2100_priv *priv)
+{
+ if (ipw2100_tx_allocate(priv) ||
+ ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv))
+ goto fail;
+
+ return 0;
+
+ fail:
+ ipw2100_tx_free(priv);
+ ipw2100_rx_free(priv);
+ ipw2100_msg_free(priv);
+ return -ENOMEM;
+}
+
+#define IPW_PRIVACY_CAPABLE 0x0008
+
+static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags,
+ int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_FLAGS,
+ .host_command_sequence = 0,
+ .host_command_length = 4
+ };
+ int err;
+
+ cmd.host_command_parameters[0] = flags;
+
+ IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags);
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+struct ipw2100_wep_key {
+ u8 idx;
+ u8 len;
+ u8 key[13];
+};
+
+/* Macros to ease up priting WEP keys */
+#define WEP_FMT_64 "%02X%02X%02X%02X-%02X"
+#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X"
+#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4]
+#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10]
+
+/**
+ * Set a the wep key
+ *
+ * @priv: struct to work on
+ * @idx: index of the key we want to set
+ * @key: ptr to the key data to set
+ * @len: length of the buffer at @key
+ * @batch_mode: FIXME perform the operation in batch mode, not
+ * disabling the device.
+ *
+ * @returns 0 if OK, < 0 errno code on error.
+ *
+ * Fill out a command structure with the new wep key, length an
+ * index and send it down the wire.
+ */
+static int ipw2100_set_key(struct ipw2100_priv *priv,
+ int idx, char *key, int len, int batch_mode)
+{
+ int keylen = len ? (len <= 5 ? 5 : 13) : 0;
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INFO,
+ .host_command_sequence = 0,
+ .host_command_length = sizeof(struct ipw2100_wep_key),
+ };
+ struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters;
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n",
+ idx, keylen, len);
+
+ /* NOTE: We don't check cached values in case the firmware was reset
+ * or some other problem is occurring. If the user is setting the key,
+ * then we push the change */
+
+ wep_key->idx = idx;
+ wep_key->len = keylen;
+
+ if (keylen) {
+ memcpy(wep_key->key, key, len);
+ memset(wep_key->key + len, 0, keylen - len);
+ }
+
+ /* Will be optimized out on debug not being configured in */
+ if (keylen == 0)
+ IPW_DEBUG_WEP("%s: Clearing key %d\n",
+ priv->net_dev->name, wep_key->idx);
+ else if (keylen == 5)
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_64(wep_key->key));
+ else
+ IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128
+ "\n",
+ priv->net_dev->name, wep_key->idx, wep_key->len,
+ WEP_STR_128(wep_key->key));
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode) {
+ int err2 = ipw2100_enable_adapter(priv);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static int ipw2100_set_key_index(struct ipw2100_priv *priv,
+ int idx, int batch_mode)
+{
+ struct host_command cmd = {
+ .host_command = WEP_KEY_INDEX,
+ .host_command_sequence = 0,
+ .host_command_length = 4,
+ .host_command_parameters = {idx},
+ };
+ int err;
+
+ IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx);
+
+ if (idx < 0 || idx > 3)
+ return -EINVAL;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Could not disable adapter %d\n",
+ priv->net_dev->name, err);
+ return err;
+ }
+ }
+
+ /* send cmd to firmware */
+ err = ipw2100_hw_send_command(priv, &cmd);
+
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode)
+{
+ int i, err, auth_mode, sec_level, use_group;
+
+ if (!(priv->status & STATUS_RUNNING))
+ return 0;
+
+ if (!batch_mode) {
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+ }
+
+ if (!priv->ieee->sec.enabled) {
+ err =
+ ipw2100_set_security_information(priv, IPW_AUTH_OPEN,
+ SEC_LEVEL_0, 0, 1);
+ } else {
+ auth_mode = IPW_AUTH_OPEN;
+ if (priv->ieee->sec.flags & SEC_AUTH_MODE) {
+ if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)
+ auth_mode = IPW_AUTH_SHARED;
+ else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)
+ auth_mode = IPW_AUTH_LEAP_CISCO_ID;
+ }
+
+ sec_level = SEC_LEVEL_0;
+ if (priv->ieee->sec.flags & SEC_LEVEL)
+ sec_level = priv->ieee->sec.level;
+
+ use_group = 0;
+ if (priv->ieee->sec.flags & SEC_UNICAST_GROUP)
+ use_group = priv->ieee->sec.unicast_uses_group;
+
+ err =
+ ipw2100_set_security_information(priv, auth_mode, sec_level,
+ use_group, 1);
+ }
+
+ if (err)
+ goto exit;
+
+ if (priv->ieee->sec.enabled) {
+ for (i = 0; i < 4; i++) {
+ if (!(priv->ieee->sec.flags & (1 << i))) {
+ memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN);
+ priv->ieee->sec.key_sizes[i] = 0;
+ } else {
+ err = ipw2100_set_key(priv, i,
+ priv->ieee->sec.keys[i],
+ priv->ieee->sec.
+ key_sizes[i], 1);
+ if (err)
+ goto exit;
+ }
+ }
+
+ ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1);
+ }
+
+ /* Always enable privacy so the Host can filter WEP packets if
+ * encrypted data is sent up */
+ err =
+ ipw2100_set_wep_flags(priv,
+ priv->ieee->sec.
+ enabled ? IPW_PRIVACY_CAPABLE : 0, 1);
+ if (err)
+ goto exit;
+
+ priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ exit:
+ if (!batch_mode)
+ ipw2100_enable_adapter(priv);
+
+ return err;
+}
+
+static void ipw2100_security_work(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, security_work.work);
+
+ /* If we happen to have reconnected before we get a chance to
+ * process this, then update the security settings--which causes
+ * a disassociation to occur */
+ if (!(priv->status & STATUS_ASSOCIATED) &&
+ priv->status & STATUS_SECURITY_UPDATED)
+ ipw2100_configure_security(priv, 0);
+}
+
+static void shim__set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int i, force_update = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED))
+ goto done;
+
+ for (i = 0; i < 4; i++) {
+ if (sec->flags & (1 << i)) {
+ priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->ieee->sec.flags &= ~(1 << i);
+ else
+ memcpy(priv->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ if (sec->level == SEC_LEVEL_1) {
+ priv->ieee->sec.flags |= (1 << i);
+ priv->status |= STATUS_SECURITY_UPDATED;
+ } else
+ priv->ieee->sec.flags &= ~(1 << i);
+ }
+ }
+
+ if ((sec->flags & SEC_ACTIVE_KEY) &&
+ priv->ieee->sec.active_key != sec->active_key) {
+ if (sec->active_key <= 3) {
+ priv->ieee->sec.active_key = sec->active_key;
+ priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->ieee->sec.auth_mode != sec->auth_mode)) {
+ priv->ieee->sec.auth_mode = sec->auth_mode;
+ priv->ieee->sec.flags |= SEC_AUTH_MODE;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
+ priv->ieee->sec.flags |= SEC_ENABLED;
+ priv->ieee->sec.enabled = sec->enabled;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ force_update = 1;
+ }
+
+ if (sec->flags & SEC_ENCRYPT)
+ priv->ieee->sec.encrypt = sec->encrypt;
+
+ if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
+ priv->ieee->sec.level = sec->level;
+ priv->ieee->sec.flags |= SEC_LEVEL;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n",
+ priv->ieee->sec.flags & (1 << 8) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 7) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 6) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 5) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 4) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 3) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 2) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 1) ? '1' : '0',
+ priv->ieee->sec.flags & (1 << 0) ? '1' : '0');
+
+/* As a temporary work around to enable WPA until we figure out why
+ * wpa_supplicant toggles the security capability of the driver, which
+ * forces a disassocation with force_update...
+ *
+ * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
+ ipw2100_configure_security(priv, 0);
+ done:
+ mutex_unlock(&priv->action_mutex);
+}
+
+static int ipw2100_adapter_setup(struct ipw2100_priv *priv)
+{
+ int err;
+ int batch_mode = 1;
+ u8 *bssid;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ err = ipw2100_disable_adapter(priv);
+ if (err)
+ return err;
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+ }
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ err = ipw2100_read_mac_address(priv);
+ if (err)
+ return -EIO;
+
+ err = ipw2100_set_mac_address(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err = ipw2100_set_channel(priv, priv->channel, batch_mode);
+ if (err)
+ return err;
+ }
+
+ err = ipw2100_system_config(priv, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode);
+ if (err)
+ return err;
+
+ /* Default to power mode OFF */
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ if (err)
+ return err;
+
+ err = ipw2100_set_rts_threshold(priv, priv->rts_threshold);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_BSSID)
+ bssid = priv->bssid;
+ else
+ bssid = NULL;
+ err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->config & CFG_STATIC_ESSID)
+ err = ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+ batch_mode);
+ else
+ err = ipw2100_set_essid(priv, NULL, 0, batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_configure_security(priv, batch_mode);
+ if (err)
+ return err;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ err =
+ ipw2100_set_ibss_beacon_interval(priv,
+ priv->beacon_interval,
+ batch_mode);
+ if (err)
+ return err;
+
+ err = ipw2100_set_tx_power(priv, priv->tx_power);
+ if (err)
+ return err;
+ }
+
+ /*
+ err = ipw2100_set_fragmentation_threshold(
+ priv, priv->frag_threshold, batch_mode);
+ if (err)
+ return err;
+ */
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+/*************************************************************************
+ *
+ * EXTERNALLY CALLED METHODS
+ *
+ *************************************************************************/
+
+/* This method is called by the network layer -- not to be confused with
+ * ipw2100_set_mac_address() declared above called by this driver (and this
+ * method as well) to talk to the firmware */
+static int ipw2100_set_address(struct net_device *dev, void *p)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct sockaddr *addr = p;
+ int err = 0;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ mutex_lock(&priv->action_mutex);
+
+ priv->config |= CFG_CUSTOM_MAC;
+ memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mac_address(priv, 0);
+ if (err)
+ goto done;
+
+ priv->reset_backoff = 0;
+ mutex_unlock(&priv->action_mutex);
+ ipw2100_reset_adapter(&priv->reset_work.work);
+ return 0;
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_open(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+ IPW_DEBUG_INFO("dev->open\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+ if (priv->status & STATUS_ASSOCIATED) {
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ return 0;
+}
+
+static int ipw2100_close(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+ struct list_head *element;
+ struct ipw2100_tx_packet *packet;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->status & STATUS_ASSOCIATED)
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ /* Flush the TX queue ... */
+ while (!list_empty(&priv->tx_pend_list)) {
+ element = priv->tx_pend_list.next;
+ packet = list_entry(element, struct ipw2100_tx_packet, list);
+
+ list_del(element);
+ DEC_STAT(&priv->tx_pend_stat);
+
+ ieee80211_txb_free(packet->info.d_struct.txb);
+ packet->info.d_struct.txb = NULL;
+
+ list_add_tail(element, &priv->tx_free_list);
+ INC_STAT(&priv->tx_free_stat);
+ }
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+
+ IPW_DEBUG_INFO("exit\n");
+
+ return 0;
+}
+
+/*
+ * TODO: Fix this function... its just wrong
+ */
+static void ipw2100_tx_timeout(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee->stats.tx_errors++;
+
+#ifdef CONFIG_IPW2100_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ return;
+#endif
+
+ IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n",
+ dev->name);
+ schedule_reset(priv);
+}
+
+static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value)
+{
+ /* This is called when wpa_supplicant loads and closes the driver
+ * interface. */
+ priv->ieee->wpa_enabled = value;
+ return 0;
+}
+
+static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value)
+{
+
+ struct ieee80211_device *ieee = priv->ieee;
+ struct ieee80211_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+ int ret = 0;
+
+ if (value & IW_AUTH_ALG_SHARED_KEY) {
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ } else if (value & IW_AUTH_ALG_LEAP) {
+ sec.auth_mode = WLAN_AUTH_LEAP;
+ ieee->open_wep = 1;
+ } else
+ return -EINVAL;
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv,
+ char *wpa_ie, int wpa_ie_len)
+{
+
+ struct ipw2100_wpa_assoc_frame frame;
+
+ frame.fixed_ie_mask = 0;
+
+ /* copy WPA IE */
+ memcpy(frame.var_ie, wpa_ie, wpa_ie_len);
+ frame.var_ie_len = wpa_ie_len;
+
+ /* make sure WPA is enabled */
+ ipw2100_wpa_enable(priv, 1);
+ ipw2100_set_wpa_ie(priv, &frame, 0);
+}
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ char fw_ver[64], ucode_ver[64];
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+
+ ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver));
+ ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver));
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s",
+ fw_ver, priv->eeprom_version, ucode_ver);
+
+ strcpy(info->bus_info, pci_name(priv->pci_dev));
+}
+
+static u32 ipw2100_ethtool_get_link(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return (priv->status & STATUS_ASSOCIATED) ? 1 : 0;
+}
+
+static const struct ethtool_ops ipw2100_ethtool_ops = {
+ .get_link = ipw2100_ethtool_get_link,
+ .get_drvinfo = ipw_ethtool_get_drvinfo,
+};
+
+static void ipw2100_hang_check(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, hang_check.work);
+ unsigned long flags;
+ u32 rtc = 0xa5a5a5a5;
+ u32 len = sizeof(rtc);
+ int restart = 0;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (priv->fatal_error != 0) {
+ /* If fatal_error is set then we need to restart */
+ IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) ||
+ (rtc == priv->last_rtc)) {
+ /* Check if firmware is hung */
+ IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n",
+ priv->net_dev->name);
+
+ restart = 1;
+ }
+
+ if (restart) {
+ /* Kill timer */
+ priv->stop_hang_check = 1;
+ priv->hangs++;
+
+ /* Restart the NIC */
+ schedule_reset(priv);
+ }
+
+ priv->last_rtc = rtc;
+
+ if (!priv->stop_hang_check)
+ queue_delayed_work(priv->workqueue, &priv->hang_check, HZ / 2);
+
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_rf_kill(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, rf_kill.work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->low_lock, flags);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+ if (!priv->stop_rf_kill)
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ round_jiffies_relative(HZ));
+ goto exit_unlock;
+ }
+
+ /* RF Kill is now disabled, so bring the device back up */
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+ "device\n");
+ schedule_reset(priv);
+ } else
+ IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
+ "enabled\n");
+
+ exit_unlock:
+ spin_unlock_irqrestore(&priv->low_lock, flags);
+}
+
+static void ipw2100_irq_tasklet(struct ipw2100_priv *priv);
+
+/* Look into using netdev destructor to shutdown ieee80211? */
+
+static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
+ void __iomem * base_addr,
+ unsigned long mem_start,
+ unsigned long mem_len)
+{
+ struct ipw2100_priv *priv;
+ struct net_device *dev;
+
+ dev = alloc_ieee80211(sizeof(struct ipw2100_priv));
+ if (!dev)
+ return NULL;
+ priv = ieee80211_priv(dev);
+ priv->ieee = netdev_priv(dev);
+ priv->pci_dev = pci_dev;
+ priv->net_dev = dev;
+
+ priv->ieee->hard_start_xmit = ipw2100_tx;
+ priv->ieee->set_security = shim__set_security;
+
+ priv->ieee->perfect_rssi = -20;
+ priv->ieee->worst_rssi = -85;
+
+ dev->open = ipw2100_open;
+ dev->stop = ipw2100_close;
+ dev->init = ipw2100_net_init;
+ dev->ethtool_ops = &ipw2100_ethtool_ops;
+ dev->tx_timeout = ipw2100_tx_timeout;
+ dev->wireless_handlers = &ipw2100_wx_handler_def;
+ priv->wireless_data.ieee80211 = priv->ieee;
+ dev->wireless_data = &priv->wireless_data;
+ dev->set_mac_address = ipw2100_set_address;
+ dev->watchdog_timeo = 3 * HZ;
+ dev->irq = 0;
+
+ dev->base_addr = (unsigned long)base_addr;
+ dev->mem_start = mem_start;
+ dev->mem_end = dev->mem_start + mem_len - 1;
+
+ /* NOTE: We don't use the wireless_handlers hook
+ * in dev as the system will start throwing WX requests
+ * to us before we're actually initialized and it just
+ * ends up causing problems. So, we just handle
+ * the WX extensions through the ipw2100_ioctl interface */
+
+ /* memset() puts everything to 0, so we only have explicitly set
+ * those values that need to be something else */
+
+ /* If power management is turned on, default to AUTO mode */
+ priv->power_mode = IPW_POWER_AUTO;
+
+#ifdef CONFIG_IPW2100_MONITOR
+ priv->config |= CFG_CRC_CHECK;
+#endif
+ priv->ieee->wpa_enabled = 0;
+ priv->ieee->drop_unencrypted = 0;
+ priv->ieee->privacy_invoked = 0;
+ priv->ieee->ieee802_1x = 1;
+
+ /* Set module parameters */
+ switch (mode) {
+ case 1:
+ priv->ieee->iw_mode = IW_MODE_ADHOC;
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case 2:
+ priv->ieee->iw_mode = IW_MODE_MONITOR;
+ break;
+#endif
+ default:
+ case 0:
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ break;
+ }
+
+ if (disable == 1)
+ priv->status |= STATUS_RF_KILL_SW;
+
+ if (channel != 0 &&
+ ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) {
+ priv->config |= CFG_STATIC_CHANNEL;
+ priv->channel = channel;
+ }
+
+ if (associate)
+ priv->config |= CFG_ASSOCIATE;
+
+ priv->beacon_interval = DEFAULT_BEACON_INTERVAL;
+ priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED;
+ priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED;
+ priv->tx_power = IPW_TX_POWER_DEFAULT;
+ priv->tx_rates = DEFAULT_TX_RATES;
+
+ strcpy(priv->nick, "ipw2100");
+
+ spin_lock_init(&priv->low_lock);
+ mutex_init(&priv->action_mutex);
+ mutex_init(&priv->adapter_mutex);
+
+ init_waitqueue_head(&priv->wait_command_queue);
+
+ netif_carrier_off(dev);
+
+ INIT_LIST_HEAD(&priv->msg_free_list);
+ INIT_LIST_HEAD(&priv->msg_pend_list);
+ INIT_STAT(&priv->msg_free_stat);
+ INIT_STAT(&priv->msg_pend_stat);
+
+ INIT_LIST_HEAD(&priv->tx_free_list);
+ INIT_LIST_HEAD(&priv->tx_pend_list);
+ INIT_STAT(&priv->tx_free_stat);
+ INIT_STAT(&priv->tx_pend_stat);
+
+ INIT_LIST_HEAD(&priv->fw_pend_list);
+ INIT_STAT(&priv->fw_pend_stat);
+
+ priv->workqueue = create_workqueue(DRV_NAME);
+
+ INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter);
+ INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work);
+ INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work);
+ INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check);
+ INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill);
+ INIT_WORK(&priv->scan_event_now, ipw2100_scan_event_now);
+ INIT_DELAYED_WORK(&priv->scan_event_later, ipw2100_scan_event_later);
+
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ ipw2100_irq_tasklet, (unsigned long)priv);
+
+ /* NOTE: We do not start the deferred work for status checks yet */
+ priv->stop_rf_kill = 1;
+ priv->stop_hang_check = 1;
+
+ return dev;
+}
+
+static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ unsigned long mem_start, mem_len, mem_flags;
+ void __iomem *base_addr = NULL;
+ struct net_device *dev = NULL;
+ struct ipw2100_priv *priv = NULL;
+ int err = 0;
+ int registered = 0;
+ u32 val;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ mem_start = pci_resource_start(pci_dev, 0);
+ mem_len = pci_resource_len(pci_dev, 0);
+ mem_flags = pci_resource_flags(pci_dev, 0);
+
+ if ((mem_flags & IORESOURCE_MEM) != IORESOURCE_MEM) {
+ IPW_DEBUG_INFO("weird - resource type is not memory\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ base_addr = ioremap_nocache(mem_start, mem_len);
+ if (!base_addr) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ioremap_nocache.\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* allocate and initialize our net_device */
+ dev = ipw2100_alloc_device(pci_dev, base_addr, mem_start, mem_len);
+ if (!dev) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ipw2100_alloc_device.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* set up PCI mappings for device */
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_enable_device.\n");
+ return err;
+ }
+
+ priv = ieee80211_priv(dev);
+
+ pci_set_master(pci_dev);
+ pci_set_drvdata(pci_dev, priv);
+
+ err = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_set_dma_mask.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ err = pci_request_regions(pci_dev, DRV_NAME);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling pci_request_regions.\n");
+ pci_disable_device(pci_dev);
+ return err;
+ }
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ pci_set_power_state(pci_dev, PCI_D0);
+
+ if (!ipw2100_hw_is_adapter_in_system(dev)) {
+ printk(KERN_WARNING DRV_NAME
+ "Device not found via register read.\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ SET_NETDEV_DEV(dev, &pci_dev->dev);
+
+ /* Force interrupts to be shut off on the device */
+ priv->status |= STATUS_INT_ENABLED;
+ ipw2100_disable_interrupts(priv);
+
+ /* Allocate and initialize the Tx/Rx queues and lists */
+ if (ipw2100_queues_allocate(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling ipw2100_queues_allocate.\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+ ipw2100_queues_initialize(priv);
+
+ err = request_irq(pci_dev->irq,
+ ipw2100_interrupt, IRQF_SHARED, dev->name, priv);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling request_irq: %d.\n", pci_dev->irq);
+ goto fail;
+ }
+ dev->irq = pci_dev->irq;
+
+ IPW_DEBUG_INFO("Attempting to register device...\n");
+
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2100 Network Connection\n");
+
+ /* Bring up the interface. Pre 0.46, after we registered the
+ * network device we would call ipw2100_up. This introduced a race
+ * condition with newer hotplug configurations (network was coming
+ * up and making calls before the device was initialized).
+ *
+ * If we called ipw2100_up before we registered the device, then the
+ * device name wasn't registered. So, we instead use the net_dev->init
+ * member to call a function that then just turns and calls ipw2100_up.
+ * net_dev->init is called after name allocation but before the
+ * notifier chain is called */
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME
+ "Error calling register_netdev.\n");
+ goto fail;
+ }
+
+ mutex_lock(&priv->action_mutex);
+ registered = 1;
+
+ IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
+
+ /* perform this after register_netdev so that dev->name is set */
+ err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+ if (err)
+ goto fail_unlock;
+
+ /* If the RF Kill switch is disabled, go ahead and complete the
+ * startup sequence */
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ /* Enable the adapter - sends HOST_COMPLETE */
+ if (ipw2100_enable_adapter(priv)) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: failed in call to enable adapter.\n",
+ priv->net_dev->name);
+ ipw2100_hw_stop_adapter(priv);
+ err = -EIO;
+ goto fail_unlock;
+ }
+
+ /* Start a scan . . . */
+ ipw2100_set_scan_options(priv);
+ ipw2100_start_scan(priv);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+
+ priv->status |= STATUS_INITIALIZED;
+
+ mutex_unlock(&priv->action_mutex);
+
+ return 0;
+
+ fail_unlock:
+ mutex_unlock(&priv->action_mutex);
+
+ fail:
+ if (dev) {
+ if (registered)
+ unregister_netdev(dev);
+
+ ipw2100_hw_stop_adapter(priv);
+
+ ipw2100_disable_interrupts(priv);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ ipw2100_kill_workqueue(priv);
+
+ /* These are safe to call even if they weren't allocated */
+ ipw2100_queues_free(priv);
+ sysfs_remove_group(&pci_dev->dev.kobj,
+ &ipw2100_attribute_group);
+
+ free_ieee80211(dev);
+ pci_set_drvdata(pci_dev, NULL);
+ }
+
+ if (base_addr)
+ iounmap(base_addr);
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ return err;
+}
+
+static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev;
+
+ if (priv) {
+ mutex_lock(&priv->action_mutex);
+
+ priv->status &= ~STATUS_INITIALIZED;
+
+ dev = priv->net_dev;
+ sysfs_remove_group(&pci_dev->dev.kobj,
+ &ipw2100_attribute_group);
+
+#ifdef CONFIG_PM
+ if (ipw2100_firmware.version)
+ ipw2100_release_firmware(priv, &ipw2100_firmware);
+#endif
+ /* Take down the hardware */
+ ipw2100_down(priv);
+
+ /* Release the mutex so that the network subsystem can
+ * complete any needed calls into the driver... */
+ mutex_unlock(&priv->action_mutex);
+
+ /* Unregister the device first - this results in close()
+ * being called if the device is open. If we free storage
+ * first, then close() will crash. */
+ unregister_netdev(dev);
+
+ /* ipw2100_down will ensure that there is no more pending work
+ * in the workqueue's, so we can safely remove them now. */
+ ipw2100_kill_workqueue(priv);
+
+ ipw2100_queues_free(priv);
+
+ /* Free potential debugging firmware snapshot */
+ ipw2100_snapshot_free(priv);
+
+ if (dev->irq)
+ free_irq(dev->irq, priv);
+
+ if (dev->base_addr)
+ iounmap((void __iomem *)dev->base_addr);
+
+ free_ieee80211(dev);
+ }
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ IPW_DEBUG_INFO("exit\n");
+}
+
+#ifdef CONFIG_PM
+static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+
+ IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name);
+
+ mutex_lock(&priv->action_mutex);
+ if (priv->status & STATUS_INITIALIZED) {
+ /* Take down the device; powers it off, etc. */
+ ipw2100_down(priv);
+ }
+
+ /* Remove the PRESENT state of the device */
+ netif_device_detach(dev);
+
+ pci_save_state(pci_dev);
+ pci_disable_device(pci_dev);
+ pci_set_power_state(pci_dev, PCI_D3hot);
+
+ mutex_unlock(&priv->action_mutex);
+
+ return 0;
+}
+
+static int ipw2100_resume(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+ struct net_device *dev = priv->net_dev;
+ int err;
+ u32 val;
+
+ if (IPW2100_PM_DISABLED)
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+
+ IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name);
+
+ pci_set_power_state(pci_dev, PCI_D0);
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
+ dev->name);
+ mutex_unlock(&priv->action_mutex);
+ return err;
+ }
+ pci_restore_state(pci_dev);
+
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+ * from interfering with C3 CPU state. pci_restore_state won't help
+ * here since it only restores the first 64 bytes pci config header.
+ */
+ pci_read_config_dword(pci_dev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
+
+ /* Set the device back into the PRESENT state; this will also wake
+ * the queue of needed */
+ netif_device_attach(dev);
+
+ /* Bring the device back up */
+ if (!(priv->status & STATUS_RF_KILL_SW))
+ ipw2100_up(priv, 0);
+
+ mutex_unlock(&priv->action_mutex);
+
+ return 0;
+}
+#endif
+
+#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
+
+static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = {
+ IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */
+ IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */
+ IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */
+ IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */
+
+ IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */
+ IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */
+ IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */
+
+ IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */
+ {0,},
+};
+
+MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table);
+
+static struct pci_driver ipw2100_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ipw2100_pci_id_table,
+ .probe = ipw2100_pci_init_one,
+ .remove = __devexit_p(ipw2100_pci_remove_one),
+#ifdef CONFIG_PM
+ .suspend = ipw2100_suspend,
+ .resume = ipw2100_resume,
+#endif
+};
+
+/**
+ * Initialize the ipw2100 driver/module
+ *
+ * @returns 0 if ok, < 0 errno node con error.
+ *
+ * Note: we cannot init the /proc stuff until the PCI driver is there,
+ * or we risk an unlikely race condition on someone accessing
+ * uninitialized data in the PCI dev struct through /proc.
+ */
+static int __init ipw2100_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
+ printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
+
+ ret = pci_register_driver(&ipw2100_pci_driver);
+ if (ret)
+ goto out;
+
+ pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100",
+ PM_QOS_DEFAULT_VALUE);
+#ifdef CONFIG_IPW2100_DEBUG
+ ipw2100_debug_level = debug;
+ ret = driver_create_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+
+out:
+ return ret;
+}
+
+/**
+ * Cleanup ipw2100 driver registration
+ */
+static void __exit ipw2100_exit(void)
+{
+ /* FIXME: IPG: check that we have no instances of the devices open */
+#ifdef CONFIG_IPW2100_DEBUG
+ driver_remove_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
+#endif
+ pci_unregister_driver(&ipw2100_pci_driver);
+ pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100");
+}
+
+module_init(ipw2100_init);
+module_exit(ipw2100_exit);
+
+#define WEXT_USECHANNELS 1
+
+static const long ipw2100_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
+
+static const long ipw2100_rates_11b[] = {
+ 1000000,
+ 2000000,
+ 5500000,
+ 11000000
+};
+
+#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b)
+
+static int ipw2100_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ if (!(priv->status & STATUS_ASSOCIATED))
+ strcpy(wrqu->name, "unassociated");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
+
+ IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+ return 0;
+}
+
+static int ipw2100_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_freq *fwrq = &wrqu->freq;
+ int err = 0;
+
+ if (priv->ieee->iw_mode == IW_MODE_INFRA)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < REG_MAX_CHANNEL) &&
+ (f != ipw2100_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 1000) {
+ err = -EOPNOTSUPP;
+ goto done;
+ } else { /* Set the channel */
+ IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+ err = ipw2100_set_channel(priv, fwrq->m, 0);
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->freq.e = 0;
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured CHANNEL then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_CHANNEL ||
+ priv->status & STATUS_ASSOCIATED)
+ wrqu->freq.m = priv->channel;
+ else
+ wrqu->freq.m = 0;
+
+ IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+ return 0;
+
+}
+
+static int ipw2100_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode);
+
+ if (wrqu->mode == priv->ieee->iw_mode)
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ switch (wrqu->mode) {
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ break;
+#endif /* CONFIG_IPW2100_MONITOR */
+ case IW_MODE_ADHOC:
+ err = ipw2100_switch_mode(priv, IW_MODE_ADHOC);
+ break;
+ case IW_MODE_INFRA:
+ case IW_MODE_AUTO:
+ default:
+ err = ipw2100_switch_mode(priv, IW_MODE_INFRA);
+ break;
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->mode = priv->ieee->iw_mode;
+ IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode);
+
+ return 0;
+}
+
+#define POWER_MODES 5
+
+/* Values are in microsecond */
+static const s32 timeout_duration[POWER_MODES] = {
+ 350000,
+ 250000,
+ 75000,
+ 37000,
+ 25000,
+};
+
+static const s32 period_duration[POWER_MODES] = {
+ 400000,
+ 700000,
+ 1000000,
+ 1000000,
+ 1000000
+};
+
+static int ipw2100_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val;
+ int i, level;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+// range->sensitivity; /* signal level threshold range */
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) {
+ range->bitrate[i] = ipw2100_rates_11b[i];
+ }
+
+ range->min_rts = MIN_RTS_THRESHOLD;
+ range->max_rts = MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->min_pmp = period_duration[0]; /* Minimal PM period */
+ range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */
+ range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */
+ range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */
+
+ /* How to decode max/min PM period */
+ range->pmp_flags = IW_POWER_PERIOD;
+ /* How to decode max/min PM period */
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ /* What PM options are supported */
+ range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13; /* Different token sizes */
+ range->num_encoding_sizes = 2; /* Number of entry in the list */
+ range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */
+// range->encoding_login_index; /* token index for login token */
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ range->txpower_capa = IW_TXPOW_DBM;
+ range->num_txpower = IW_MAX_TXPOWER;
+ for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16);
+ i < IW_MAX_TXPOWER;
+ i++, level -=
+ ((IPW_TX_POWER_MAX_DBM -
+ IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
+ range->txpower[i] = level / 16;
+ } else {
+ range->txpower_capa = 0;
+ range->num_txpower = 0;
+ }
+
+ /* Set the Wireless Extension versions */
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 18;
+
+// range->retry_capa; /* What retry options are supported */
+// range->retry_flags; /* How to decode max/min retry limit */
+// range->r_time_flags; /* How to decode max/min retry life */
+// range->min_retry; /* Minimal number of retries */
+// range->max_retry; /* Maximal number of retries */
+// range->min_r_time; /* Minimal retry lifetime */
+// range->max_r_time; /* Maximal retry lifetime */
+
+ range->num_channels = FREQ_COUNT;
+
+ val = 0;
+ for (i = 0; i < FREQ_COUNT; i++) {
+ // TODO: Include only legal frequencies for some countries
+// if (local->channel_mask & (1 << i)) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ipw2100_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+// }
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+ IPW_DEBUG_WX("GET Range\n");
+
+ return 0;
+}
+
+static int ipw2100_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ static const unsigned char any[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ static const unsigned char off[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ // sanity checks
+ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+ !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ /* we disable mandatory BSSID association */
+ IPW_DEBUG_WX("exit - disable mandatory BSSID\n");
+ priv->config &= ~CFG_STATIC_BSSID;
+ err = ipw2100_set_mandatory_bssid(priv, NULL, 0);
+ goto done;
+ }
+
+ priv->config |= CFG_STATIC_BSSID;
+ memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+ err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0);
+
+ IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured BSSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) {
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ } else
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+ IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data);
+ return 0;
+}
+
+static int ipw2100_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ char *essid = ""; /* ANY */
+ int length = 0;
+ int err = 0;
+ DECLARE_SSID_BUF(ssid);
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ length = wrqu->essid.length;
+ essid = extra;
+ }
+
+ if (length == 0) {
+ IPW_DEBUG_WX("Setting ESSID to ANY\n");
+ priv->config &= ~CFG_STATIC_ESSID;
+ err = ipw2100_set_essid(priv, NULL, 0, 0);
+ goto done;
+ }
+
+ length = min(length, IW_ESSID_MAX_SIZE);
+
+ priv->config |= CFG_STATIC_ESSID;
+
+ if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) {
+ IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+ err = 0;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n",
+ print_ssid(ssid, essid, length), length);
+
+ priv->essid_len = length;
+ memcpy(priv->essid, essid, priv->essid_len);
+
+ err = ipw2100_set_essid(priv, essid, length, 0);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ DECLARE_SSID_BUF(ssid);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured ESSID then return that; otherwise return ANY */
+ if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) {
+ IPW_DEBUG_WX("Getting essid: '%s'\n",
+ print_ssid(ssid, priv->essid, priv->essid_len));
+ memcpy(extra, priv->essid, priv->essid_len);
+ wrqu->essid.length = priv->essid_len;
+ wrqu->essid.flags = 1; /* active */
+ } else {
+ IPW_DEBUG_WX("Getting essid: ANY\n");
+ wrqu->essid.length = 0;
+ wrqu->essid.flags = 0; /* active */
+ }
+
+ return 0;
+}
+
+static int ipw2100_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+
+ IPW_DEBUG_WX("SET Nickname -> %s \n", priv->nick);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->data.length = strlen(priv->nick);
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+
+ IPW_DEBUG_WX("GET Nickname -> %s \n", extra);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ u32 target_rate = wrqu->bitrate.value;
+ u32 rate;
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ rate = 0;
+
+ if (target_rate == 1000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 1000000))
+ rate |= TX_RATE_1_MBIT;
+ if (target_rate == 2000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 2000000))
+ rate |= TX_RATE_2_MBIT;
+ if (target_rate == 5500000 ||
+ (!wrqu->bitrate.fixed && target_rate > 5500000))
+ rate |= TX_RATE_5_5_MBIT;
+ if (target_rate == 11000000 ||
+ (!wrqu->bitrate.fixed && target_rate > 11000000))
+ rate |= TX_RATE_11_MBIT;
+ if (rate == 0)
+ rate = DEFAULT_TX_RATES;
+
+ err = ipw2100_set_tx_rates(priv, rate, 0);
+
+ IPW_DEBUG_WX("SET Rate -> %04X \n", rate);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int val;
+ int len = sizeof(val);
+ int err = 0;
+
+ if (!(priv->status & STATUS_ENABLED) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ !(priv->status & STATUS_ASSOCIATED)) {
+ wrqu->bitrate.value = 0;
+ return 0;
+ }
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len);
+ if (err) {
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+ goto done;
+ }
+
+ switch (val & TX_RATE_MASK) {
+ case TX_RATE_1_MBIT:
+ wrqu->bitrate.value = 1000000;
+ break;
+ case TX_RATE_2_MBIT:
+ wrqu->bitrate.value = 2000000;
+ break;
+ case TX_RATE_5_5_MBIT:
+ wrqu->bitrate.value = 5500000;
+ break;
+ case TX_RATE_11_MBIT:
+ wrqu->bitrate.value = 11000000;
+ break;
+ default:
+ wrqu->bitrate.value = 0;
+ }
+
+ IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int value, err;
+
+ /* Auto RTS not yet supported */
+ if (wrqu->rts.fixed == 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->rts.disabled)
+ value = priv->rts_threshold | RTS_DISABLED;
+ else {
+ if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) {
+ err = -EINVAL;
+ goto done;
+ }
+ value = wrqu->rts.value;
+ }
+
+ err = ipw2100_set_rts_threshold(priv, value);
+
+ IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED;
+ wrqu->rts.fixed = 1; /* no auto select */
+
+ /* If RTS is set to the default value, then it is disabled */
+ wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X \n", wrqu->rts.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0, value;
+
+ if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled))
+ return -EINPROGRESS;
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC)
+ return 0;
+
+ if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
+ return -EINVAL;
+
+ if (wrqu->txpower.fixed == 0)
+ value = IPW_TX_POWER_DEFAULT;
+ else {
+ if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM ||
+ wrqu->txpower.value > IPW_TX_POWER_MAX_DBM)
+ return -EINVAL;
+
+ value = wrqu->txpower.value;
+ }
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ err = ipw2100_set_tx_power(priv, value);
+
+ IPW_DEBUG_WX("SET TX Power -> %d \n", value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
+
+ if (priv->tx_power == IPW_TX_POWER_DEFAULT) {
+ wrqu->txpower.fixed = 0;
+ wrqu->txpower.value = IPW_TX_POWER_MAX_DBM;
+ } else {
+ wrqu->txpower.fixed = 1;
+ wrqu->txpower.value = priv->tx_power;
+ }
+
+ wrqu->txpower.flags = IW_TXPOW_DBM;
+
+ IPW_DEBUG_WX("GET TX Power -> %d \n", wrqu->txpower.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (!wrqu->frag.fixed)
+ return -EINVAL;
+
+ if (wrqu->frag.disabled) {
+ priv->frag_threshold |= FRAG_DISABLED;
+ priv->ieee->fts = DEFAULT_FTS;
+ } else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->ieee->fts = wrqu->frag.value & ~0x1;
+ priv->frag_threshold = priv->ieee->fts;
+ }
+
+ IPW_DEBUG_WX("SET Frag Threshold -> %d \n", priv->ieee->fts);
+
+ return 0;
+}
+
+static int ipw2100_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0;
+
+ IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_SHORT) {
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+ IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
+ wrqu->retry.value);
+ goto done;
+ }
+
+ err = ipw2100_set_short_retry(priv, wrqu->retry.value);
+ if (!err)
+ err = ipw2100_set_long_retry(priv, wrqu->retry.value);
+
+ IPW_DEBUG_WX("SET Both Retry Limits -> %d \n", wrqu->retry.value);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ wrqu->retry.disabled = 0; /* can't be disabled */
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else {
+ wrqu->retry.flags =
+ (priv->short_retry_limit !=
+ priv->long_retry_limit) ?
+ IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
+
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+
+ IPW_DEBUG_WX("GET Retry -> %d \n", wrqu->retry.value);
+
+ return 0;
+}
+
+static int ipw2100_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ IPW_DEBUG_WX("Initiating scan...\n");
+
+ priv->user_requested_scan = 1;
+ if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) {
+ IPW_DEBUG_WX("Start scan failed.\n");
+
+ /* TODO: Mark a scan as pending so when hardware initialized
+ * a scan starts */
+ }
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+/*
+ * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c
+ */
+static int ipw2100_wx_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * No check of STATUS_INITIALIZED required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw2100_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (wrqu->power.disabled) {
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM);
+ IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+ goto done;
+ }
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitly state all */
+ break;
+ default: /* Otherwise we don't support it */
+ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+ wrqu->power.flags);
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ /* If the user hasn't specified a power management mode yet, default
+ * to BATTERY */
+ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+ err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+
+ IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+
+}
+
+static int ipw2100_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED))
+ wrqu->power.disabled = 1;
+ else {
+ wrqu->power.disabled = 0;
+ wrqu->power.flags = 0;
+ }
+
+ IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+ return 0;
+}
+
+/*
+ * WE-18 WPA support
+ */
+
+/* SIOCSIWGENIE */
+static int ipw2100_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ u8 *buf;
+
+ if (!ieee->wpa_enabled)
+ return -EOPNOTSUPP;
+
+ if (wrqu->data.length > MAX_WPA_IE_LEN ||
+ (wrqu->data.length && extra == NULL))
+ return -EINVAL;
+
+ if (wrqu->data.length) {
+ buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ return 0;
+}
+
+/* SIOCGIWGENIE */
+static int ipw2100_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+
+ if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
+ wrqu->data.length = 0;
+ return 0;
+ }
+
+ if (wrqu->data.length < ieee->wpa_ie_len)
+ return -E2BIG;
+
+ wrqu->data.length = ieee->wpa_ie_len;
+ memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ return 0;
+}
+
+/* SIOCSIWAUTH */
+static int ipw2100_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ struct iw_param *param = &wrqu->param;
+ struct lib80211_crypt_data *crypt;
+ unsigned long flags;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * ipw2200 does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
+ break;
+
+ flags = crypt->ops->get_flags(crypt->priv);
+
+ if (param->value)
+ flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+ else
+ flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+
+ crypt->ops->set_flags(flags, crypt->priv);
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:{
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ struct ieee80211_security sec = {
+ .flags = SEC_ENABLED,
+ .enabled = param->value,
+ };
+ priv->ieee->drop_unencrypted = param->value;
+ /* We only change SEC_LEVEL for open mode. Others
+ * are set by ipw_wpa_set_encryption.
+ */
+ if (!param->value) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_0;
+ } else {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ }
+ if (priv->ieee->set_security)
+ priv->ieee->set_security(priv->ieee->dev, &sec);
+ break;
+ }
+
+ case IW_AUTH_80211_AUTH_ALG:
+ ret = ipw2100_wpa_set_auth_algs(priv, param->value);
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ ret = ipw2100_wpa_enable(priv, param->value);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = param->value;
+ break;
+
+ //case IW_AUTH_ROAMING_CONTROL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = param->value;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+/* SIOCGIWAUTH */
+static int ipw2100_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ struct lib80211_crypt_data *crypt;
+ struct iw_param *param = &wrqu->param;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * wpa_supplicant will control these internally
+ */
+ ret = -EOPNOTSUPP;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->get_flags) {
+ IPW_DEBUG_WARNING("Can't get TKIP countermeasures: "
+ "crypt not set!\n");
+ break;
+ }
+
+ param->value = (crypt->ops->get_flags(crypt->priv) &
+ IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ param->value = ieee->drop_unencrypted;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ param->value = priv->ieee->sec.auth_mode;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = ieee->wpa_enabled;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ param->value = ieee->ieee802_1x;
+ break;
+
+ case IW_AUTH_ROAMING_CONTROL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ param->value = ieee->privacy_invoked;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/* SIOCSIWENCODEEXT */
+static int ipw2100_wx_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCGIWENCODEEXT */
+static int ipw2100_wx_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCSIWMLME */
+static int ipw2100_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ __le16 reason;
+
+ reason = cpu_to_le16(mlme->reason_code);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ // silently ignore
+ break;
+
+ case IW_MLME_DISASSOC:
+ ipw2100_disassociate_bssid(priv);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*
+ *
+ * IWPRIV handlers
+ *
+ */
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_promisc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ int err = 0;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (enable) {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ipw2100_set_channel(priv, parms[1], 0);
+ goto done;
+ }
+ priv->channel = parms[1];
+ err = ipw2100_switch_mode(priv, IW_MODE_MONITOR);
+ } else {
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ err = ipw2100_switch_mode(priv, priv->last_mode);
+ }
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ if (priv->status & STATUS_INITIALIZED)
+ schedule_reset(priv);
+ return 0;
+}
+
+#endif
+
+static int ipw2100_wx_set_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err = 0, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if ((mode < 0) || (mode > POWER_MODES))
+ mode = IPW_POWER_AUTO;
+
+ if (IPW_POWER_LEVEL(priv->power_mode) != mode)
+ err = ipw2100_set_power_mode(priv, mode);
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+#define MAX_POWER_STRING 80
+static int ipw2100_wx_get_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int level = IPW_POWER_LEVEL(priv->power_mode);
+ s32 timeout, period;
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED)) {
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Off)", level);
+ } else {
+ switch (level) {
+ case IPW_POWER_MODE_CAM:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (None)", level);
+ break;
+ case IPW_POWER_AUTO:
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d (Auto)", level);
+ break;
+ default:
+ timeout = timeout_duration[level - 1] / 1000;
+ period = period_duration[level - 1] / 1000;
+ snprintf(extra, MAX_POWER_STRING,
+ "Power save level: %d "
+ "(Timeout %dms, Period %dms)",
+ level, timeout, period);
+ }
+ }
+
+ wrqu->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int ipw2100_wx_set_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (mode == 1)
+ priv->config |= CFG_LONG_PREAMBLE;
+ else if (mode == 0)
+ priv->config &= ~CFG_LONG_PREAMBLE;
+ else {
+ err = -EINVAL;
+ goto done;
+ }
+
+ err = ipw2100_system_config(priv, 0);
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (priv->config & CFG_LONG_PREAMBLE)
+ snprintf(wrqu->name, IFNAMSIZ, "long (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
+
+ return 0;
+}
+
+#ifdef CONFIG_IPW2100_MONITOR
+static int ipw2100_wx_set_crc_check(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ int err, mode = *(int *)extra;
+
+ mutex_lock(&priv->action_mutex);
+ if (!(priv->status & STATUS_INITIALIZED)) {
+ err = -EIO;
+ goto done;
+ }
+
+ if (mode == 1)
+ priv->config |= CFG_CRC_CHECK;
+ else if (mode == 0)
+ priv->config &= ~CFG_CRC_CHECK;
+ else {
+ err = -EINVAL;
+ goto done;
+ }
+ err = 0;
+
+ done:
+ mutex_unlock(&priv->action_mutex);
+ return err;
+}
+
+static int ipw2100_wx_get_crc_check(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /*
+ * This can be called at any time. No action lock required
+ */
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+
+ if (priv->config & CFG_CRC_CHECK)
+ snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)");
+
+ return 0;
+}
+#endif /* CONFIG_IPW2100_MONITOR */
+
+static iw_handler ipw2100_wx_handlers[] = {
+ NULL, /* SIOCSIWCOMMIT */
+ ipw2100_wx_get_name, /* SIOCGIWNAME */
+ NULL, /* SIOCSIWNWID */
+ NULL, /* SIOCGIWNWID */
+ ipw2100_wx_set_freq, /* SIOCSIWFREQ */
+ ipw2100_wx_get_freq, /* SIOCGIWFREQ */
+ ipw2100_wx_set_mode, /* SIOCSIWMODE */
+ ipw2100_wx_get_mode, /* SIOCGIWMODE */
+ NULL, /* SIOCSIWSENS */
+ NULL, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ ipw2100_wx_get_range, /* SIOCGIWRANGE */
+ NULL, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ NULL, /* SIOCSIWSPY */
+ NULL, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ ipw2100_wx_set_wap, /* SIOCSIWAP */
+ ipw2100_wx_get_wap, /* SIOCGIWAP */
+ ipw2100_wx_set_mlme, /* SIOCSIWMLME */
+ NULL, /* SIOCGIWAPLIST -- deprecated */
+ ipw2100_wx_set_scan, /* SIOCSIWSCAN */
+ ipw2100_wx_get_scan, /* SIOCGIWSCAN */
+ ipw2100_wx_set_essid, /* SIOCSIWESSID */
+ ipw2100_wx_get_essid, /* SIOCGIWESSID */
+ ipw2100_wx_set_nick, /* SIOCSIWNICKN */
+ ipw2100_wx_get_nick, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ ipw2100_wx_set_rate, /* SIOCSIWRATE */
+ ipw2100_wx_get_rate, /* SIOCGIWRATE */
+ ipw2100_wx_set_rts, /* SIOCSIWRTS */
+ ipw2100_wx_get_rts, /* SIOCGIWRTS */
+ ipw2100_wx_set_frag, /* SIOCSIWFRAG */
+ ipw2100_wx_get_frag, /* SIOCGIWFRAG */
+ ipw2100_wx_set_txpow, /* SIOCSIWTXPOW */
+ ipw2100_wx_get_txpow, /* SIOCGIWTXPOW */
+ ipw2100_wx_set_retry, /* SIOCSIWRETRY */
+ ipw2100_wx_get_retry, /* SIOCGIWRETRY */
+ ipw2100_wx_set_encode, /* SIOCSIWENCODE */
+ ipw2100_wx_get_encode, /* SIOCGIWENCODE */
+ ipw2100_wx_set_power, /* SIOCSIWPOWER */
+ ipw2100_wx_get_power, /* SIOCGIWPOWER */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ ipw2100_wx_set_genie, /* SIOCSIWGENIE */
+ ipw2100_wx_get_genie, /* SIOCGIWGENIE */
+ ipw2100_wx_set_auth, /* SIOCSIWAUTH */
+ ipw2100_wx_get_auth, /* SIOCGIWAUTH */
+ ipw2100_wx_set_encodeext, /* SIOCSIWENCODEEXT */
+ ipw2100_wx_get_encodeext, /* SIOCGIWENCODEEXT */
+ NULL, /* SIOCSIWPMKSA */
+};
+
+#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV
+#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1
+#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2
+#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3
+#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4
+#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5
+#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6
+#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7
+
+static const struct iw_priv_args ipw2100_private_args[] = {
+
+#ifdef CONFIG_IPW2100_MONITOR
+ {
+ IPW2100_PRIV_SET_MONITOR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
+ {
+ IPW2100_PRIV_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
+#endif /* CONFIG_IPW2100_MONITOR */
+
+ {
+ IPW2100_PRIV_SET_POWER,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"},
+ {
+ IPW2100_PRIV_GET_POWER,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING,
+ "get_power"},
+ {
+ IPW2100_PRIV_SET_LONGPREAMBLE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
+ {
+ IPW2100_PRIV_GET_LONGPREAMBLE,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"},
+#ifdef CONFIG_IPW2100_MONITOR
+ {
+ IPW2100_PRIV_SET_CRC_CHECK,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"},
+ {
+ IPW2100_PRIV_GET_CRC_CHECK,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"},
+#endif /* CONFIG_IPW2100_MONITOR */
+};
+
+static iw_handler ipw2100_private_handler[] = {
+#ifdef CONFIG_IPW2100_MONITOR
+ ipw2100_wx_set_promisc,
+ ipw2100_wx_reset,
+#else /* CONFIG_IPW2100_MONITOR */
+ NULL,
+ NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+ ipw2100_wx_set_powermode,
+ ipw2100_wx_get_powermode,
+ ipw2100_wx_set_preamble,
+ ipw2100_wx_get_preamble,
+#ifdef CONFIG_IPW2100_MONITOR
+ ipw2100_wx_set_crc_check,
+ ipw2100_wx_get_crc_check,
+#else /* CONFIG_IPW2100_MONITOR */
+ NULL,
+ NULL,
+#endif /* CONFIG_IPW2100_MONITOR */
+};
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev)
+{
+ enum {
+ POOR = 30,
+ FAIR = 60,
+ GOOD = 80,
+ VERY_GOOD = 90,
+ EXCELLENT = 95,
+ PERFECT = 100
+ };
+ int rssi_qual;
+ int tx_qual;
+ int beacon_qual;
+
+ struct ipw2100_priv *priv = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+ u32 rssi, quality, tx_retries, missed_beacons, tx_failures;
+ u32 ord_len = sizeof(u32);
+
+ if (!priv)
+ return (struct iw_statistics *)NULL;
+
+ wstats = &priv->wstats;
+
+ /* if hw is disabled, then ipw2100_get_ordinal() can't be called.
+ * ipw2100_wx_wireless_stats seems to be called before fw is
+ * initialized. STATUS_ASSOCIATED will only be set if the hw is up
+ * and associated; if not associcated, the values are all meaningless
+ * anyway, so set them all to NULL and INVALID */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS,
+ &missed_beacons, &ord_len))
+ goto fail_get_ordinal;
+
+ /* If we don't have a connection the quality and level is 0 */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ } else {
+ if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR,
+ &rssi, &ord_len))
+ goto fail_get_ordinal;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ if (rssi < 10)
+ rssi_qual = rssi * POOR / 10;
+ else if (rssi < 15)
+ rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR;
+ else if (rssi < 20)
+ rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR;
+ else if (rssi < 30)
+ rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) /
+ 10 + GOOD;
+ else
+ rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) /
+ 10 + VERY_GOOD;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES,
+ &tx_retries, &ord_len))
+ goto fail_get_ordinal;
+
+ if (tx_retries > 75)
+ tx_qual = (90 - tx_retries) * POOR / 15;
+ else if (tx_retries > 70)
+ tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+ else if (tx_retries > 65)
+ tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+ else if (tx_retries > 50)
+ tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+ 15 + GOOD;
+ else
+ tx_qual = (50 - tx_retries) *
+ (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+
+ if (missed_beacons > 50)
+ beacon_qual = (60 - missed_beacons) * POOR / 10;
+ else if (missed_beacons > 40)
+ beacon_qual = (50 - missed_beacons) * (FAIR - POOR) /
+ 10 + POOR;
+ else if (missed_beacons > 32)
+ beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) /
+ 18 + FAIR;
+ else if (missed_beacons > 20)
+ beacon_qual = (32 - missed_beacons) *
+ (VERY_GOOD - GOOD) / 20 + GOOD;
+ else
+ beacon_qual = (20 - missed_beacons) *
+ (PERFECT - VERY_GOOD) / 20 + VERY_GOOD;
+
+ quality = min(beacon_qual, min(tx_qual, rssi_qual));
+
+#ifdef CONFIG_IPW2100_DEBUG
+ if (beacon_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Missed Beacons\n");
+ else if (tx_qual == quality)
+ IPW_DEBUG_WX("Quality clamped by Tx Retries\n");
+ else if (quality != 100)
+ IPW_DEBUG_WX("Quality clamped by Signal Strength\n");
+ else
+ IPW_DEBUG_WX("Quality not clamped.\n");
+#endif
+
+ wstats->qual.qual = quality;
+ wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM;
+ }
+
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID;
+
+ /* FIXME: this is percent and not a # */
+ wstats->miss.beacon = missed_beacons;
+
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES,
+ &tx_failures, &ord_len))
+ goto fail_get_ordinal;
+ wstats->discard.retries = tx_failures;
+
+ return wstats;
+
+ fail_get_ordinal:
+ IPW_DEBUG_WX("failed querying ordinals.\n");
+
+ return (struct iw_statistics *)NULL;
+}
+
+static struct iw_handler_def ipw2100_wx_handler_def = {
+ .standard = ipw2100_wx_handlers,
+ .num_standard = ARRAY_SIZE(ipw2100_wx_handlers),
+ .num_private = ARRAY_SIZE(ipw2100_private_handler),
+ .num_private_args = ARRAY_SIZE(ipw2100_private_args),
+ .private = (iw_handler *) ipw2100_private_handler,
+ .private_args = (struct iw_priv_args *)ipw2100_private_args,
+ .get_wireless_stats = ipw2100_wx_wireless_stats,
+};
+
+static void ipw2100_wx_event_work(struct work_struct *work)
+{
+ struct ipw2100_priv *priv =
+ container_of(work, struct ipw2100_priv, wx_event_work.work);
+ union iwreq_data wrqu;
+ int len = ETH_ALEN;
+
+ if (priv->status & STATUS_STOPPING)
+ return;
+
+ mutex_lock(&priv->action_mutex);
+
+ IPW_DEBUG_WX("enter\n");
+
+ mutex_unlock(&priv->action_mutex);
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /* Fetch BSSID from the hardware */
+ if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) ||
+ priv->status & STATUS_RF_KILL_MASK ||
+ ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID,
+ &priv->bssid, &len)) {
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ } else {
+ /* We now have the BSSID, so can finish setting to the full
+ * associated state */
+ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN);
+ priv->status &= ~STATUS_ASSOCIATING;
+ priv->status |= STATUS_ASSOCIATED;
+ netif_carrier_on(priv->net_dev);
+ netif_wake_queue(priv->net_dev);
+ }
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_WX("Configuring ESSID\n");
+ mutex_lock(&priv->action_mutex);
+ /* This is a disassociation event, so kick the firmware to
+ * look for another AP */
+ if (priv->config & CFG_STATIC_ESSID)
+ ipw2100_set_essid(priv, priv->essid, priv->essid_len,
+ 0);
+ else
+ ipw2100_set_essid(priv, NULL, 0, 0);
+ mutex_unlock(&priv->action_mutex);
+ }
+
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+#define IPW2100_FW_MAJOR_VERSION 1
+#define IPW2100_FW_MINOR_VERSION 3
+
+#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8)
+#define IPW2100_FW_MAJOR(x) (x & 0xff)
+
+#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \
+ IPW2100_FW_MAJOR_VERSION)
+
+#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \
+"." __stringify(IPW2100_FW_MINOR_VERSION)
+
+#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw"
+
+/*
+
+BINARY FIRMWARE HEADER FORMAT
+
+offset length desc
+0 2 version
+2 2 mode == 0:BSS,1:IBSS,2:MONITOR
+4 4 fw_len
+8 4 uc_len
+C fw_len firmware data
+12 + fw_len uc_len microcode data
+
+*/
+
+struct ipw2100_fw_header {
+ short version;
+ short mode;
+ unsigned int fw_size;
+ unsigned int uc_size;
+} __attribute__ ((packed));
+
+static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
+{
+ struct ipw2100_fw_header *h =
+ (struct ipw2100_fw_header *)fw->fw_entry->data;
+
+ if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
+ printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
+ "(detected version id of %u). "
+ "See Documentation/networking/README.ipw2100\n",
+ h->version);
+ return 1;
+ }
+
+ fw->version = h->version;
+ fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header);
+ fw->fw.size = h->fw_size;
+ fw->uc.data = fw->fw.data + h->fw_size;
+ fw->uc.size = h->uc_size;
+
+ return 0;
+}
+
+static int ipw2100_get_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ char *fw_name;
+ int rc;
+
+ IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n",
+ priv->net_dev->name);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ fw_name = IPW2100_FW_NAME("-i");
+ break;
+#ifdef CONFIG_IPW2100_MONITOR
+ case IW_MODE_MONITOR:
+ fw_name = IPW2100_FW_NAME("-p");
+ break;
+#endif
+ case IW_MODE_INFRA:
+ default:
+ fw_name = IPW2100_FW_NAME("");
+ break;
+ }
+
+ rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev);
+
+ if (rc < 0) {
+ printk(KERN_ERR DRV_NAME ": "
+ "%s: Firmware '%s' not available or load failed.\n",
+ priv->net_dev->name, fw_name);
+ return rc;
+ }
+ IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data,
+ fw->fw_entry->size);
+
+ ipw2100_mod_firmware_load(fw);
+
+ return 0;
+}
+
+static void ipw2100_release_firmware(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ fw->version = 0;
+ if (fw->fw_entry)
+ release_firmware(fw->fw_entry);
+ fw->fw_entry = NULL;
+}
+
+static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ char ver[MAX_FW_VERSION_LEN];
+ u32 len = MAX_FW_VERSION_LEN;
+ u32 tmp;
+ int i;
+ /* firmware version is an ascii string (max len of 14) */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len))
+ return -EIO;
+ tmp = max;
+ if (len >= max)
+ len = max - 1;
+ for (i = 0; i < len; i++)
+ buf[i] = ver[i];
+ buf[i] = '\0';
+ return tmp;
+}
+
+static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf,
+ size_t max)
+{
+ u32 ver;
+ u32 len = sizeof(ver);
+ /* microcode version is a 32 bit integer */
+ if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len))
+ return -EIO;
+ return snprintf(buf, max, "%08X", ver);
+}
+
+/*
+ * On exit, the firmware will have been freed from the fw list
+ */
+static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw)
+{
+ /* firmware is constructed of N contiguous entries, each entry is
+ * structured as:
+ *
+ * offset sie desc
+ * 0 4 address to write to
+ * 4 2 length of data run
+ * 6 length data
+ */
+ unsigned int addr;
+ unsigned short len;
+
+ const unsigned char *firmware_data = fw->fw.data;
+ unsigned int firmware_data_left = fw->fw.size;
+
+ while (firmware_data_left > 0) {
+ addr = *(u32 *) (firmware_data);
+ firmware_data += 4;
+ firmware_data_left -= 4;
+
+ len = *(u16 *) (firmware_data);
+ firmware_data += 2;
+ firmware_data_left -= 2;
+
+ if (len > 32) {
+ printk(KERN_ERR DRV_NAME ": "
+ "Invalid firmware run-length of %d bytes\n",
+ len);
+ return -EINVAL;
+ }
+
+ write_nic_memory(priv->net_dev, addr, len, firmware_data);
+ firmware_data += len;
+ firmware_data_left -= len;
+ }
+
+ return 0;
+}
+
+struct symbol_alive_response {
+ u8 cmd_id;
+ u8 seq_num;
+ u8 ucode_rev;
+ u8 eeprom_valid;
+ u16 valid_flags;
+ u8 IEEE_addr[6];
+ u16 flags;
+ u16 pcb_rev;
+ u16 clock_settle_time; // 1us LSB
+ u16 powerup_settle_time; // 1us LSB
+ u16 hop_settle_time; // 1us LSB
+ u8 date[3]; // month, day, year
+ u8 time[2]; // hours, minutes
+ u8 ucode_valid;
+};
+
+static int ipw2100_ucode_download(struct ipw2100_priv *priv,
+ struct ipw2100_fw *fw)
+{
+ struct net_device *dev = priv->net_dev;
+ const unsigned char *microcode_data = fw->uc.data;
+ unsigned int microcode_data_left = fw->uc.size;
+ void __iomem *reg = (void __iomem *)dev->base_addr;
+
+ struct symbol_alive_response response;
+ int i, j;
+ u8 data;
+
+ /* Symbol control */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl(reg);
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl(reg);
+
+ /* HW config */
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl(reg);
+ write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */
+ readl(reg);
+
+ /* EN_CS_ACCESS bit to reset control store pointer */
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x40);
+ readl(reg);
+
+ /* copy microcode from buffer into Symbol */
+
+ while (microcode_data_left > 0) {
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ write_nic_byte(dev, 0x210010, *microcode_data++);
+ microcode_data_left -= 2;
+ }
+
+ /* EN_CS_ACCESS bit to reset the control store pointer */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+
+ /* Enable System (Reg 0)
+ * first enable causes garbage in RX FIFO */
+ write_nic_byte(dev, 0x210000, 0x0);
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x80);
+ readl(reg);
+
+ /* Reset External Baseband Reg */
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x703);
+ readl(reg);
+ write_nic_word(dev, IPW2100_CONTROL_REG, 0x707);
+ readl(reg);
+
+ /* HW Config (Reg 5) */
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl(reg);
+ write_nic_byte(dev, 0x210014, 0x72); // fifo width =16
+ readl(reg);
+
+ /* Enable System (Reg 0)
+ * second enable should be OK */
+ write_nic_byte(dev, 0x210000, 0x00); // clear enable system
+ readl(reg);
+ write_nic_byte(dev, 0x210000, 0x80); // set enable system
+
+ /* check Symbol is enabled - upped this from 5 as it wasn't always
+ * catching the update */
+ for (i = 0; i < 10; i++) {
+ udelay(10);
+
+ /* check Dino is enabled bit */
+ read_nic_byte(dev, 0x210000, &data);
+ if (data & 0x1)
+ break;
+ }
+
+ if (i == 10) {
+ printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n",
+ dev->name);
+ return -EIO;
+ }
+
+ /* Get Symbol alive response */
+ for (i = 0; i < 30; i++) {
+ /* Read alive response structure */
+ for (j = 0;
+ j < (sizeof(struct symbol_alive_response) >> 1); j++)
+ read_nic_word(dev, 0x210004, ((u16 *) & response) + j);
+
+ if ((response.cmd_id == 1) && (response.ucode_valid == 0x1))
+ break;
+ udelay(10);
+ }
+
+ if (i == 30) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: No response from Symbol - hw not alive\n",
+ dev->name);
+ printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response));
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h
index bbf1ddcafba8..bbf1ddcafba8 100644
--- a/drivers/net/wireless/ipw2100.h
+++ b/drivers/net/wireless/ipw2x00/ipw2100.h
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
new file mode 100644
index 000000000000..625f2cf99fa9
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -0,0 +1,11986 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
+
+ 802.11 status code portion of this file from ethereal-0.10.6:
+ Copyright 2000, Axis Communications AB
+ Ethereal - Network traffic analyzer
+ By Gerald Combs <gerald@ethereal.com>
+ Copyright 1998 Gerald Combs
+
+ 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., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+
+#include "ipw2200.h"
+
+
+#ifndef KBUILD_EXTMOD
+#define VK "k"
+#else
+#define VK
+#endif
+
+#ifdef CONFIG_IPW2200_DEBUG
+#define VD "d"
+#else
+#define VD
+#endif
+
+#ifdef CONFIG_IPW2200_MONITOR
+#define VM "m"
+#else
+#define VM
+#endif
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define VP "p"
+#else
+#define VP
+#endif
+
+#ifdef CONFIG_IPW2200_RADIOTAP
+#define VR "r"
+#else
+#define VR
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
+#define VQ "q"
+#else
+#define VQ
+#endif
+
+#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ
+#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver"
+#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
+#define DRV_VERSION IPW2200_VERSION
+
+#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1)
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int cmdlog = 0;
+static int debug = 0;
+static int channel = 0;
+static int mode = 0;
+
+static u32 ipw_debug_level;
+static int associate;
+static int auto_create = 1;
+static int led = 0;
+static int disable = 0;
+static int bt_coexist = 0;
+static int hwcrypto = 0;
+static int roaming = 1;
+static const char ipw_modes[] = {
+ 'a', 'b', 'g', '?'
+};
+static int antenna = CFG_SYS_ANTENNA_BOTH;
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
+#endif
+
+
+#ifdef CONFIG_IPW2200_QOS
+static int qos_enable = 0;
+static int qos_burst_enable = 0;
+static int qos_no_ack_mask = 0;
+static int burst_duration_CCK = 0;
+static int burst_duration_OFDM = 0;
+
+static struct ieee80211_qos_parameters def_qos_parameters_OFDM = {
+ {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM,
+ QOS_TX3_CW_MIN_OFDM},
+ {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM,
+ QOS_TX3_CW_MAX_OFDM},
+ {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
+ {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
+ {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM,
+ QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM}
+};
+
+static struct ieee80211_qos_parameters def_qos_parameters_CCK = {
+ {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK,
+ QOS_TX3_CW_MIN_CCK},
+ {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK,
+ QOS_TX3_CW_MAX_CCK},
+ {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS},
+ {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM},
+ {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK,
+ QOS_TX3_TXOP_LIMIT_CCK}
+};
+
+static struct ieee80211_qos_parameters def_parameters_OFDM = {
+ {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM,
+ DEF_TX3_CW_MIN_OFDM},
+ {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM,
+ DEF_TX3_CW_MAX_OFDM},
+ {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
+ {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
+ {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM,
+ DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM}
+};
+
+static struct ieee80211_qos_parameters def_parameters_CCK = {
+ {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK,
+ DEF_TX3_CW_MIN_CCK},
+ {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK,
+ DEF_TX3_CW_MAX_CCK},
+ {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS},
+ {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM},
+ {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK,
+ DEF_TX3_TXOP_LIMIT_CCK}
+};
+
+static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
+
+static int from_priority_to_tx_queue[] = {
+ IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1,
+ IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4
+};
+
+static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv);
+
+static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
+ *qos_param);
+static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
+ *qos_param);
+#endif /* CONFIG_IPW2200_QOS */
+
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev);
+static void ipw_remove_current_network(struct ipw_priv *priv);
+static void ipw_rx(struct ipw_priv *priv);
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq, int qindex);
+static int ipw_queue_reset(struct ipw_priv *priv);
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+ int len, int sync);
+
+static void ipw_tx_queue_free(struct ipw_priv *);
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *);
+static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *);
+static void ipw_rx_queue_replenish(void *);
+static int ipw_up(struct ipw_priv *);
+static void ipw_bg_up(struct work_struct *work);
+static void ipw_down(struct ipw_priv *);
+static void ipw_bg_down(struct work_struct *work);
+static int ipw_config(struct ipw_priv *);
+static int init_supported_rates(struct ipw_priv *priv,
+ struct ipw_supported_rates *prates);
+static void ipw_set_hwcrypto_keys(struct ipw_priv *);
+static void ipw_send_wep_keys(struct ipw_priv *, int);
+
+static int snprint_line(char *buf, size_t count,
+ const u8 * data, u32 len, u32 ofs)
+{
+ int out, i, j, l;
+ char c;
+
+ out = snprintf(buf, count, "%08X", ofs);
+
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++)
+ out += snprintf(buf + out, count - out, "%02X ",
+ data[(i * 8 + j)]);
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ out += snprintf(buf + out, count - out, " ");
+ for (l = 0, i = 0; i < 2; i++) {
+ out += snprintf(buf + out, count - out, " ");
+ for (j = 0; j < 8 && l < len; j++, l++) {
+ c = data[(i * 8 + j)];
+ if (!isascii(c) || !isprint(c))
+ c = '.';
+
+ out += snprintf(buf + out, count - out, "%c", c);
+ }
+
+ for (; j < 8; j++)
+ out += snprintf(buf + out, count - out, " ");
+ }
+
+ return out;
+}
+
+static void printk_buf(int level, const u8 * data, u32 len)
+{
+ char line[81];
+ u32 ofs = 0;
+ if (!(ipw_debug_level & level))
+ return;
+
+ while (len) {
+ snprint_line(line, sizeof(line), &data[ofs],
+ min(len, 16U), ofs);
+ printk(KERN_DEBUG "%s\n", line);
+ ofs += 16;
+ len -= min(len, 16U);
+ }
+}
+
+static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len)
+{
+ size_t out = size;
+ u32 ofs = 0;
+ int total = 0;
+
+ while (size && len) {
+ out = snprint_line(output, size, &data[ofs],
+ min_t(size_t, len, 16U), ofs);
+
+ ofs += 16;
+ output += out;
+ size -= out;
+ len -= min_t(size_t, len, 16U);
+ total += out;
+ }
+ return total;
+}
+
+/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg);
+#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b)
+
+/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */
+static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg);
+#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b)
+
+/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value);
+static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__,
+ __LINE__, (u32) (b), (u32) (c));
+ _ipw_write_reg8(a, b, c);
+}
+
+/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value);
+static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__,
+ __LINE__, (u32) (b), (u32) (c));
+ _ipw_write_reg16(a, b, c);
+}
+
+/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value);
+static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c)
+{
+ IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__,
+ __LINE__, (u32) (b), (u32) (c));
+ _ipw_write_reg32(a, b, c);
+}
+
+/* 8-bit direct write (low 4K) */
+#define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs))
+
+/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
+#define ipw_write8(ipw, ofs, val) do { \
+ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write8(ipw, ofs, val); \
+ } while (0)
+
+/* 16-bit direct write (low 4K) */
+#define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs))
+
+/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
+#define ipw_write16(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write16(ipw, ofs, val)
+
+/* 32-bit direct write (low 4K) */
+#define _ipw_write32(ipw, ofs, val) writel((val), (ipw)->hw_base + (ofs))
+
+/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */
+#define ipw_write32(ipw, ofs, val) \
+ IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \
+ _ipw_write32(ipw, ofs, val)
+
+/* 8-bit direct read (low 4K) */
+#define _ipw_read8(ipw, ofs) readb((ipw)->hw_base + (ofs))
+
+/* 8-bit direct read (low 4K), with debug wrapper */
+static inline u8 __ipw_read8(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
+{
+ IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", f, l, (u32) (ofs));
+ return _ipw_read8(ipw, ofs);
+}
+
+/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */
+#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs)
+
+/* 16-bit direct read (low 4K) */
+#define _ipw_read16(ipw, ofs) readw((ipw)->hw_base + (ofs))
+
+/* 16-bit direct read (low 4K), with debug wrapper */
+static inline u16 __ipw_read16(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
+{
+ IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", f, l, (u32) (ofs));
+ return _ipw_read16(ipw, ofs);
+}
+
+/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */
+#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs)
+
+/* 32-bit direct read (low 4K) */
+#define _ipw_read32(ipw, ofs) readl((ipw)->hw_base + (ofs))
+
+/* 32-bit direct read (low 4K), with debug wrapper */
+static inline u32 __ipw_read32(char *f, u32 l, struct ipw_priv *ipw, u32 ofs)
+{
+ IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", f, l, (u32) (ofs));
+ return _ipw_read32(ipw, ofs);
+}
+
+/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */
+#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs)
+
+/* multi-byte read (above 4K), with debug wrapper */
+static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int);
+static inline void __ipw_read_indirect(const char *f, int l,
+ struct ipw_priv *a, u32 b, u8 * c, int d)
+{
+ IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %d bytes\n", f, l, (u32) (b),
+ d);
+ _ipw_read_indirect(a, b, c, d);
+}
+
+/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
+#define ipw_read_indirect(a, b, c, d) __ipw_read_indirect(__FILE__, __LINE__, a, b, c, d)
+
+/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data,
+ int num);
+#define ipw_write_indirect(a, b, c, d) \
+ IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \
+ _ipw_write_indirect(a, b, c, d)
+
+/* 32-bit indirect write (above 4K) */
+static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value)
+{
+ IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value);
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
+ _ipw_write32(priv, IPW_INDIRECT_DATA, value);
+}
+
+/* 8-bit indirect write (above 4K) */
+static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value)
+{
+ u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
+ u32 dif_len = reg - aligned_addr;
+
+ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value);
+}
+
+/* 16-bit indirect write (above 4K) */
+static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value)
+{
+ u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */
+ u32 dif_len = (reg - aligned_addr) & (~0x1ul);
+
+ IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value);
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value);
+}
+
+/* 8-bit indirect read (above 4K) */
+static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg)
+{
+ u32 word;
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK);
+ IPW_DEBUG_IO(" reg = 0x%8X : \n", reg);
+ word = _ipw_read32(priv, IPW_INDIRECT_DATA);
+ return (word >> ((reg & 0x3) * 8)) & 0xff;
+}
+
+/* 32-bit indirect read (above 4K) */
+static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg)
+{
+ u32 value;
+
+ IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg);
+
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, reg);
+ value = _ipw_read32(priv, IPW_INDIRECT_DATA);
+ IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value);
+ return value;
+}
+
+/* General purpose, no alignment requirement, iterative (multi-byte) read, */
+/* for area above 1st 4K of SRAM/reg space */
+static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
+ int num)
+{
+ u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
+ u32 dif_len = addr - aligned_addr;
+ u32 i;
+
+ IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+ if (num <= 0) {
+ return;
+ }
+
+ /* Read the first dword (or portion) byte by byte */
+ if (unlikely(dif_len)) {
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ /* Start reading at aligned_addr + dif_len */
+ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--)
+ *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i);
+ aligned_addr += 4;
+ }
+
+ /* Read all of the middle dwords as dwords, with auto-increment */
+ _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
+ for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
+ *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA);
+
+ /* Read the last dword (or portion) byte by byte */
+ if (unlikely(num)) {
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ for (i = 0; num > 0; i++, num--)
+ *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i);
+ }
+}
+
+/* General purpose, no alignment requirement, iterative (multi-byte) write, */
+/* for area above 1st 4K of SRAM/reg space */
+static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf,
+ int num)
+{
+ u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */
+ u32 dif_len = addr - aligned_addr;
+ u32 i;
+
+ IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num);
+
+ if (num <= 0) {
+ return;
+ }
+
+ /* Write the first dword (or portion) byte by byte */
+ if (unlikely(dif_len)) {
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ /* Start writing at aligned_addr + dif_len */
+ for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++)
+ _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
+ aligned_addr += 4;
+ }
+
+ /* Write all of the middle dwords as dwords, with auto-increment */
+ _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr);
+ for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4)
+ _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf);
+
+ /* Write the last dword (or portion) byte by byte */
+ if (unlikely(num)) {
+ _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr);
+ for (i = 0; num > 0; i++, num--, buf++)
+ _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf);
+ }
+}
+
+/* General purpose, no alignment requirement, iterative (multi-byte) write, */
+/* for 1st 4K of SRAM/regs space */
+static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf,
+ int num)
+{
+ memcpy_toio((priv->hw_base + addr), buf, num);
+}
+
+/* Set bit(s) in low 4K of SRAM/regs */
+static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+ ipw_write32(priv, reg, ipw_read32(priv, reg) | mask);
+}
+
+/* Clear bit(s) in low 4K of SRAM/regs */
+static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask)
+{
+ ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask);
+}
+
+static inline void __ipw_enable_interrupts(struct ipw_priv *priv)
+{
+ if (priv->status & STATUS_INT_ENABLED)
+ return;
+ priv->status |= STATUS_INT_ENABLED;
+ ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL);
+}
+
+static inline void __ipw_disable_interrupts(struct ipw_priv *priv)
+{
+ if (!(priv->status & STATUS_INT_ENABLED))
+ return;
+ priv->status &= ~STATUS_INT_ENABLED;
+ ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
+}
+
+static inline void ipw_enable_interrupts(struct ipw_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->irq_lock, flags);
+ __ipw_enable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->irq_lock, flags);
+}
+
+static inline void ipw_disable_interrupts(struct ipw_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->irq_lock, flags);
+ __ipw_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->irq_lock, flags);
+}
+
+static char *ipw_error_desc(u32 val)
+{
+ switch (val) {
+ case IPW_FW_ERROR_OK:
+ return "ERROR_OK";
+ case IPW_FW_ERROR_FAIL:
+ return "ERROR_FAIL";
+ case IPW_FW_ERROR_MEMORY_UNDERFLOW:
+ return "MEMORY_UNDERFLOW";
+ case IPW_FW_ERROR_MEMORY_OVERFLOW:
+ return "MEMORY_OVERFLOW";
+ case IPW_FW_ERROR_BAD_PARAM:
+ return "BAD_PARAM";
+ case IPW_FW_ERROR_BAD_CHECKSUM:
+ return "BAD_CHECKSUM";
+ case IPW_FW_ERROR_NMI_INTERRUPT:
+ return "NMI_INTERRUPT";
+ case IPW_FW_ERROR_BAD_DATABASE:
+ return "BAD_DATABASE";
+ case IPW_FW_ERROR_ALLOC_FAIL:
+ return "ALLOC_FAIL";
+ case IPW_FW_ERROR_DMA_UNDERRUN:
+ return "DMA_UNDERRUN";
+ case IPW_FW_ERROR_DMA_STATUS:
+ return "DMA_STATUS";
+ case IPW_FW_ERROR_DINO_ERROR:
+ return "DINO_ERROR";
+ case IPW_FW_ERROR_EEPROM_ERROR:
+ return "EEPROM_ERROR";
+ case IPW_FW_ERROR_SYSASSERT:
+ return "SYSASSERT";
+ case IPW_FW_ERROR_FATAL_ERROR:
+ return "FATAL_ERROR";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+}
+
+static void ipw_dump_error_log(struct ipw_priv *priv,
+ struct ipw_fw_error *error)
+{
+ u32 i;
+
+ if (!error) {
+ IPW_ERROR("Error allocating and capturing error log. "
+ "Nothing to dump.\n");
+ return;
+ }
+
+ IPW_ERROR("Start IPW Error Log Dump:\n");
+ IPW_ERROR("Status: 0x%08X, Config: %08X\n",
+ error->status, error->config);
+
+ for (i = 0; i < error->elem_len; i++)
+ IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ipw_error_desc(error->elem[i].desc),
+ error->elem[i].time,
+ error->elem[i].blink1,
+ error->elem[i].blink2,
+ error->elem[i].link1,
+ error->elem[i].link2, error->elem[i].data);
+ for (i = 0; i < error->log_len; i++)
+ IPW_ERROR("%i\t0x%08x\t%i\n",
+ error->log[i].time,
+ error->log[i].data, error->log[i].event);
+}
+
+static inline int ipw_is_init(struct ipw_priv *priv)
+{
+ return (priv->status & STATUS_INIT) ? 1 : 0;
+}
+
+static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
+{
+ u32 addr, field_info, field_len, field_count, total_len;
+
+ IPW_DEBUG_ORD("ordinal = %i\n", ord);
+
+ if (!priv || !val || !len) {
+ IPW_DEBUG_ORD("Invalid argument\n");
+ return -EINVAL;
+ }
+
+ /* verify device ordinal tables have been initialized */
+ if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) {
+ IPW_DEBUG_ORD("Access ordinals before initialization\n");
+ return -EINVAL;
+ }
+
+ switch (IPW_ORD_TABLE_ID_MASK & ord) {
+ case IPW_ORD_TABLE_0_MASK:
+ /*
+ * TABLE 0: Direct access to a table of 32 bit values
+ *
+ * This is a very simple table with the data directly
+ * read from the table
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table0_len) {
+ IPW_DEBUG_ORD("ordinal value (%i) longer then "
+ "max (%i)\n", ord, priv->table0_len);
+ return -EINVAL;
+ }
+
+ /* verify we have enough room to store the value */
+ if (*len < sizeof(u32)) {
+ IPW_DEBUG_ORD("ordinal buffer length too small, "
+ "need %zd\n", sizeof(u32));
+ return -EINVAL;
+ }
+
+ IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n",
+ ord, priv->table0_addr + (ord << 2));
+
+ *len = sizeof(u32);
+ ord <<= 2;
+ *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord);
+ break;
+
+ case IPW_ORD_TABLE_1_MASK:
+ /*
+ * TABLE 1: Indirect access to a table of 32 bit values
+ *
+ * This is a fairly large table of u32 values each
+ * representing starting addr for the data (which is
+ * also a u32)
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table1_len) {
+ IPW_DEBUG_ORD("ordinal value too long\n");
+ return -EINVAL;
+ }
+
+ /* verify we have enough room to store the value */
+ if (*len < sizeof(u32)) {
+ IPW_DEBUG_ORD("ordinal buffer length too small, "
+ "need %zd\n", sizeof(u32));
+ return -EINVAL;
+ }
+
+ *((u32 *) val) =
+ ipw_read_reg32(priv, (priv->table1_addr + (ord << 2)));
+ *len = sizeof(u32);
+ break;
+
+ case IPW_ORD_TABLE_2_MASK:
+ /*
+ * TABLE 2: Indirect access to a table of variable sized values
+ *
+ * This table consist of six values, each containing
+ * - dword containing the starting offset of the data
+ * - dword containing the lengh in the first 16bits
+ * and the count in the second 16bits
+ */
+
+ /* remove the table id from the ordinal */
+ ord &= IPW_ORD_TABLE_VALUE_MASK;
+
+ /* boundary check */
+ if (ord > priv->table2_len) {
+ IPW_DEBUG_ORD("ordinal value too long\n");
+ return -EINVAL;
+ }
+
+ /* get the address of statistic */
+ addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3));
+
+ /* get the second DW of statistics ;
+ * two 16-bit words - first is length, second is count */
+ field_info =
+ ipw_read_reg32(priv,
+ priv->table2_addr + (ord << 3) +
+ sizeof(u32));
+
+ /* get each entry length */
+ field_len = *((u16 *) & field_info);
+
+ /* get number of entries */
+ field_count = *(((u16 *) & field_info) + 1);
+
+ /* abort if not enought memory */
+ total_len = field_len * field_count;
+ if (total_len > *len) {
+ *len = total_len;
+ return -EINVAL;
+ }
+
+ *len = total_len;
+ if (!total_len)
+ return 0;
+
+ IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, "
+ "field_info = 0x%08x\n",
+ addr, total_len, field_info);
+ ipw_read_indirect(priv, addr, val, total_len);
+ break;
+
+ default:
+ IPW_DEBUG_ORD("Invalid ordinal!\n");
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+static void ipw_init_ordinals(struct ipw_priv *priv)
+{
+ priv->table0_addr = IPW_ORDINALS_TABLE_LOWER;
+ priv->table0_len = ipw_read32(priv, priv->table0_addr);
+
+ IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n",
+ priv->table0_addr, priv->table0_len);
+
+ priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1);
+ priv->table1_len = ipw_read_reg32(priv, priv->table1_addr);
+
+ IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n",
+ priv->table1_addr, priv->table1_len);
+
+ priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2);
+ priv->table2_len = ipw_read_reg32(priv, priv->table2_addr);
+ priv->table2_len &= 0x0000ffff; /* use first two bytes */
+
+ IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n",
+ priv->table2_addr, priv->table2_len);
+
+}
+
+static u32 ipw_register_toggle(u32 reg)
+{
+ reg &= ~IPW_START_STANDBY;
+ if (reg & IPW_GATE_ODMA)
+ reg &= ~IPW_GATE_ODMA;
+ if (reg & IPW_GATE_IDMA)
+ reg &= ~IPW_GATE_IDMA;
+ if (reg & IPW_GATE_ADMA)
+ reg &= ~IPW_GATE_ADMA;
+ return reg;
+}
+
+/*
+ * LED behavior:
+ * - On radio ON, turn on any LEDs that require to be on during start
+ * - On initialization, start unassociated blink
+ * - On association, disable unassociated blink
+ * - On disassociation, start unassociated blink
+ * - On radio OFF, turn off any LEDs started during radio on
+ *
+ */
+#define LD_TIME_LINK_ON msecs_to_jiffies(300)
+#define LD_TIME_LINK_OFF msecs_to_jiffies(2700)
+#define LD_TIME_ACT_ON msecs_to_jiffies(250)
+
+static void ipw_led_link_on(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ u32 led;
+
+ /* If configured to not use LEDs, or nic_type is 1,
+ * then we don't toggle a LINK led */
+ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!(priv->status & STATUS_RF_KILL_MASK) &&
+ !(priv->status & STATUS_LED_LINK_ON)) {
+ IPW_DEBUG_LED("Link LED On\n");
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ led |= priv->led_association_on;
+
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ priv->status |= STATUS_LED_LINK_ON;
+
+ /* If we aren't associated, schedule turning the LED off */
+ if (!(priv->status & STATUS_ASSOCIATED))
+ queue_delayed_work(priv->workqueue,
+ &priv->led_link_off,
+ LD_TIME_LINK_ON);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_bg_led_link_on(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, led_link_on.work);
+ mutex_lock(&priv->mutex);
+ ipw_led_link_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_led_link_off(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ u32 led;
+
+ /* If configured not to use LEDs, or nic type is 1,
+ * then we don't goggle the LINK led. */
+ if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1)
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->status & STATUS_LED_LINK_ON) {
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ led &= priv->led_association_off;
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ IPW_DEBUG_LED("Link LED Off\n");
+
+ priv->status &= ~STATUS_LED_LINK_ON;
+
+ /* If we aren't associated and the radio is on, schedule
+ * turning the LED on (blink while unassociated) */
+ if (!(priv->status & STATUS_RF_KILL_MASK) &&
+ !(priv->status & STATUS_ASSOCIATED))
+ queue_delayed_work(priv->workqueue, &priv->led_link_on,
+ LD_TIME_LINK_OFF);
+
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_bg_led_link_off(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, led_link_off.work);
+ mutex_lock(&priv->mutex);
+ ipw_led_link_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void __ipw_led_activity_on(struct ipw_priv *priv)
+{
+ u32 led;
+
+ if (priv->config & CFG_NO_LED)
+ return;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return;
+
+ if (!(priv->status & STATUS_LED_ACT_ON)) {
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ led |= priv->led_activity_on;
+
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ IPW_DEBUG_LED("Activity LED On\n");
+
+ priv->status |= STATUS_LED_ACT_ON;
+
+ cancel_delayed_work(&priv->led_act_off);
+ queue_delayed_work(priv->workqueue, &priv->led_act_off,
+ LD_TIME_ACT_ON);
+ } else {
+ /* Reschedule LED off for full time period */
+ cancel_delayed_work(&priv->led_act_off);
+ queue_delayed_work(priv->workqueue, &priv->led_act_off,
+ LD_TIME_ACT_ON);
+ }
+}
+
+#if 0
+void ipw_led_activity_on(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
+ __ipw_led_activity_on(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+#endif /* 0 */
+
+static void ipw_led_activity_off(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ u32 led;
+
+ if (priv->config & CFG_NO_LED)
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->status & STATUS_LED_ACT_ON) {
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ led &= priv->led_activity_off;
+
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ IPW_DEBUG_LED("Activity LED Off\n");
+
+ priv->status &= ~STATUS_LED_ACT_ON;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_bg_led_activity_off(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, led_act_off.work);
+ mutex_lock(&priv->mutex);
+ ipw_led_activity_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_led_band_on(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ u32 led;
+
+ /* Only nic type 1 supports mode LEDs */
+ if (priv->config & CFG_NO_LED ||
+ priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network)
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ if (priv->assoc_network->mode == IEEE_A) {
+ led |= priv->led_ofdm_on;
+ led &= priv->led_association_off;
+ IPW_DEBUG_LED("Mode LED On: 802.11a\n");
+ } else if (priv->assoc_network->mode == IEEE_G) {
+ led |= priv->led_ofdm_on;
+ led |= priv->led_association_on;
+ IPW_DEBUG_LED("Mode LED On: 802.11g\n");
+ } else {
+ led &= priv->led_ofdm_off;
+ led |= priv->led_association_on;
+ IPW_DEBUG_LED("Mode LED On: 802.11b\n");
+ }
+
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_led_band_off(struct ipw_priv *priv)
+{
+ unsigned long flags;
+ u32 led;
+
+ /* Only nic type 1 supports mode LEDs */
+ if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1)
+ return;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ led = ipw_read_reg32(priv, IPW_EVENT_REG);
+ led &= priv->led_ofdm_off;
+ led &= priv->led_association_off;
+
+ led = ipw_register_toggle(led);
+
+ IPW_DEBUG_LED("Reg: 0x%08X\n", led);
+ ipw_write_reg32(priv, IPW_EVENT_REG, led);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_led_radio_on(struct ipw_priv *priv)
+{
+ ipw_led_link_on(priv);
+}
+
+static void ipw_led_radio_off(struct ipw_priv *priv)
+{
+ ipw_led_activity_off(priv);
+ ipw_led_link_off(priv);
+}
+
+static void ipw_led_link_up(struct ipw_priv *priv)
+{
+ /* Set the Link Led on for all nic types */
+ ipw_led_link_on(priv);
+}
+
+static void ipw_led_link_down(struct ipw_priv *priv)
+{
+ ipw_led_activity_off(priv);
+ ipw_led_link_off(priv);
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ ipw_led_radio_off(priv);
+}
+
+static void ipw_led_init(struct ipw_priv *priv)
+{
+ priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE];
+
+ /* Set the default PINs for the link and activity leds */
+ priv->led_activity_on = IPW_ACTIVITY_LED;
+ priv->led_activity_off = ~(IPW_ACTIVITY_LED);
+
+ priv->led_association_on = IPW_ASSOCIATED_LED;
+ priv->led_association_off = ~(IPW_ASSOCIATED_LED);
+
+ /* Set the default PINs for the OFDM leds */
+ priv->led_ofdm_on = IPW_OFDM_LED;
+ priv->led_ofdm_off = ~(IPW_OFDM_LED);
+
+ switch (priv->nic_type) {
+ case EEPROM_NIC_TYPE_1:
+ /* In this NIC type, the LEDs are reversed.... */
+ priv->led_activity_on = IPW_ASSOCIATED_LED;
+ priv->led_activity_off = ~(IPW_ASSOCIATED_LED);
+ priv->led_association_on = IPW_ACTIVITY_LED;
+ priv->led_association_off = ~(IPW_ACTIVITY_LED);
+
+ if (!(priv->config & CFG_NO_LED))
+ ipw_led_band_on(priv);
+
+ /* And we don't blink link LEDs for this nic, so
+ * just return here */
+ return;
+
+ case EEPROM_NIC_TYPE_3:
+ case EEPROM_NIC_TYPE_2:
+ case EEPROM_NIC_TYPE_4:
+ case EEPROM_NIC_TYPE_0:
+ break;
+
+ default:
+ IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n",
+ priv->nic_type);
+ priv->nic_type = EEPROM_NIC_TYPE_0;
+ break;
+ }
+
+ if (!(priv->config & CFG_NO_LED)) {
+ if (priv->status & STATUS_ASSOCIATED)
+ ipw_led_link_on(priv);
+ else
+ ipw_led_link_off(priv);
+ }
+}
+
+static void ipw_led_shutdown(struct ipw_priv *priv)
+{
+ ipw_led_activity_off(priv);
+ ipw_led_link_off(priv);
+ ipw_led_band_off(priv);
+ cancel_delayed_work(&priv->led_link_on);
+ cancel_delayed_work(&priv->led_link_off);
+ cancel_delayed_work(&priv->led_act_off);
+}
+
+/*
+ * The following adds a new attribute to the sysfs representation
+ * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/)
+ * used for controling the debug level.
+ *
+ * See the level definitions in ipw for details.
+ */
+static ssize_t show_debug_level(struct device_driver *d, char *buf)
+{
+ return sprintf(buf, "0x%08X\n", ipw_debug_level);
+}
+
+static ssize_t store_debug_level(struct device_driver *d, const char *buf,
+ size_t count)
+{
+ char *p = (char *)buf;
+ u32 val;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buf)
+ printk(KERN_INFO DRV_NAME
+ ": %s is not in hex or decimal form.\n", buf);
+ else
+ ipw_debug_level = val;
+
+ return strnlen(buf, count);
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+ show_debug_level, store_debug_level);
+
+static inline u32 ipw_get_event_log_len(struct ipw_priv *priv)
+{
+ /* length = 1st dword in log */
+ return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG));
+}
+
+static void ipw_capture_event_log(struct ipw_priv *priv,
+ u32 log_len, struct ipw_event *log)
+{
+ u32 base;
+
+ if (log_len) {
+ base = ipw_read32(priv, IPW_EVENT_LOG);
+ ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32),
+ (u8 *) log, sizeof(*log) * log_len);
+ }
+}
+
+static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
+{
+ struct ipw_fw_error *error;
+ u32 log_len = ipw_get_event_log_len(priv);
+ u32 base = ipw_read32(priv, IPW_ERROR_LOG);
+ u32 elem_len = ipw_read_reg32(priv, base);
+
+ error = kmalloc(sizeof(*error) +
+ sizeof(*error->elem) * elem_len +
+ sizeof(*error->log) * log_len, GFP_ATOMIC);
+ if (!error) {
+ IPW_ERROR("Memory allocation for firmware error log "
+ "failed.\n");
+ return NULL;
+ }
+ error->jiffies = jiffies;
+ error->status = priv->status;
+ error->config = priv->config;
+ error->elem_len = elem_len;
+ error->log_len = log_len;
+ error->elem = (struct ipw_error_elem *)error->payload;
+ error->log = (struct ipw_event *)(error->elem + elem_len);
+
+ ipw_capture_event_log(priv, log_len, error->log);
+
+ if (elem_len)
+ ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem,
+ sizeof(*error->elem) * elem_len);
+
+ return error;
+}
+
+static ssize_t show_event_log(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 log_len = ipw_get_event_log_len(priv);
+ u32 log_size;
+ struct ipw_event *log;
+ u32 len = 0, i;
+
+ /* not using min() because of its strict type checking */
+ log_size = PAGE_SIZE / sizeof(*log) > log_len ?
+ sizeof(*log) * log_len : PAGE_SIZE;
+ log = kzalloc(log_size, GFP_KERNEL);
+ if (!log) {
+ IPW_ERROR("Unable to allocate memory for log\n");
+ return 0;
+ }
+ log_len = log_size / sizeof(*log);
+ ipw_capture_event_log(priv, log_len, log);
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len);
+ for (i = 0; i < log_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X",
+ log[i].time, log[i].event, log[i].data);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ kfree(log);
+ return len;
+}
+
+static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL);
+
+static ssize_t show_error(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 len = 0, i;
+ if (!priv->error)
+ return 0;
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%08lX%08X%08X%08X",
+ priv->error->jiffies,
+ priv->error->status,
+ priv->error->config, priv->error->elem_len);
+ for (i = 0; i < priv->error->elem_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X%08X%08X%08X%08X",
+ priv->error->elem[i].time,
+ priv->error->elem[i].desc,
+ priv->error->elem[i].blink1,
+ priv->error->elem[i].blink2,
+ priv->error->elem[i].link1,
+ priv->error->elem[i].link2,
+ priv->error->elem[i].data);
+
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X", priv->error->log_len);
+ for (i = 0; i < priv->error->log_len; i++)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08X%08X%08X",
+ priv->error->log[i].time,
+ priv->error->log[i].event,
+ priv->error->log[i].data);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static ssize_t clear_error(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+
+ kfree(priv->error);
+ priv->error = NULL;
+ return count;
+}
+
+static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error);
+
+static ssize_t show_cmd_log(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ u32 len = 0, i;
+ if (!priv->cmdlog)
+ return 0;
+ for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
+ (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
+ i = (i + 1) % priv->cmdlog_len) {
+ len +=
+ snprintf(buf + len, PAGE_SIZE - len,
+ "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies,
+ priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd,
+ priv->cmdlog[i].cmd.len);
+ len +=
+ snprintk_buf(buf + len, PAGE_SIZE - len,
+ (u8 *) priv->cmdlog[i].cmd.param,
+ priv->cmdlog[i].cmd.len);
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ }
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+ return len;
+}
+
+static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL);
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_prom_free(struct ipw_priv *priv);
+static int ipw_prom_alloc(struct ipw_priv *priv);
+static ssize_t store_rtap_iface(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ int rc = 0;
+
+ if (count < 1)
+ return -EINVAL;
+
+ switch (buf[0]) {
+ case '0':
+ if (!rtap_iface)
+ return count;
+
+ if (netif_running(priv->prom_net_dev)) {
+ IPW_WARNING("Interface is up. Cannot unregister.\n");
+ return count;
+ }
+
+ ipw_prom_free(priv);
+ rtap_iface = 0;
+ break;
+
+ case '1':
+ if (rtap_iface)
+ return count;
+
+ rc = ipw_prom_alloc(priv);
+ if (!rc)
+ rtap_iface = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (rc) {
+ IPW_ERROR("Failed to register promiscuous network "
+ "device (error %d).\n", rc);
+ }
+
+ return count;
+}
+
+static ssize_t show_rtap_iface(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ if (rtap_iface)
+ return sprintf(buf, "%s", priv->prom_net_dev->name);
+ else {
+ buf[0] = '-';
+ buf[1] = '1';
+ buf[2] = '\0';
+ return 3;
+ }
+}
+
+static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface,
+ store_rtap_iface);
+
+static ssize_t store_rtap_filter(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+
+ if (!priv->prom_priv) {
+ IPW_ERROR("Attempting to set filter without "
+ "rtap_iface enabled.\n");
+ return -EPERM;
+ }
+
+ priv->prom_priv->filter = simple_strtol(buf, NULL, 0);
+
+ IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n",
+ BIT_ARG16(priv->prom_priv->filter));
+
+ return count;
+}
+
+static ssize_t show_rtap_filter(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "0x%04X",
+ priv->prom_priv ? priv->prom_priv->filter : 0);
+}
+
+static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter,
+ store_rtap_filter);
+#endif
+
+static ssize_t show_scan_age(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", priv->ieee->scan_age);
+}
+
+static ssize_t store_scan_age(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ struct net_device *dev = priv->net_dev;
+ char buffer[] = "00000000";
+ unsigned long len =
+ (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1;
+ unsigned long val;
+ char *p = buffer;
+
+ IPW_DEBUG_INFO("enter\n");
+
+ strncpy(buffer, buf, len);
+ buffer[len] = 0;
+
+ if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') {
+ p++;
+ if (p[0] == 'x' || p[0] == 'X')
+ p++;
+ val = simple_strtoul(p, &p, 16);
+ } else
+ val = simple_strtoul(p, &p, 10);
+ if (p == buffer) {
+ IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name);
+ } else {
+ priv->ieee->scan_age = val;
+ IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+ return len;
+}
+
+static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age);
+
+static ssize_t show_led(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1);
+}
+
+static ssize_t store_led(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+
+ IPW_DEBUG_INFO("enter\n");
+
+ if (count == 0)
+ return 0;
+
+ if (*buf == 0) {
+ IPW_DEBUG_LED("Disabling LED control.\n");
+ priv->config |= CFG_NO_LED;
+ ipw_led_shutdown(priv);
+ } else {
+ IPW_DEBUG_LED("Enabling LED control.\n");
+ priv->config &= ~CFG_NO_LED;
+ ipw_led_init(priv);
+ }
+
+ IPW_DEBUG_INFO("exit\n");
+ return count;
+}
+
+static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led);
+
+static ssize_t show_status(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->status);
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static ssize_t show_cfg(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *p = d->driver_data;
+ return sprintf(buf, "0x%08x\n", (int)p->config);
+}
+
+static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL);
+
+static ssize_t show_nic_type(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ipw_priv *priv = d->driver_data;
+ return sprintf(buf, "TYPE: %d\n", priv->nic_type);
+}
+
+static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL);
+
+static ssize_t show_ucode_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 len = sizeof(u32), tmp = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len))
+ return 0;
+
+ return sprintf(buf, "0x%08x\n", tmp);
+}
+
+static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL);
+
+static ssize_t show_rtc(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ u32 len = sizeof(u32), tmp = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len))
+ return 0;
+
+ return sprintf(buf, "0x%08x\n", tmp);
+}
+
+static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL);
+
+/*
+ * Add a device attribute to view/control the delay between eeprom
+ * operations.
+ */
+static ssize_t show_eeprom_delay(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ int n = ((struct ipw_priv *)d->driver_data)->eeprom_delay;
+ return sprintf(buf, "%i\n", n);
+}
+static ssize_t store_eeprom_delay(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *p = d->driver_data;
+ sscanf(buf, "%i", &p->eeprom_delay);
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO,
+ show_eeprom_delay, store_eeprom_delay);
+
+static ssize_t show_command_event_reg(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT);
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_command_event_reg(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 reg;
+ struct ipw_priv *p = d->driver_data;
+
+ sscanf(buf, "%x", &reg);
+ ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg);
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO,
+ show_command_event_reg, store_command_event_reg);
+
+static ssize_t show_mem_gpio_reg(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *p = d->driver_data;
+
+ reg = ipw_read_reg32(p, 0x301100);
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_mem_gpio_reg(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 reg;
+ struct ipw_priv *p = d->driver_data;
+
+ sscanf(buf, "%x", &reg);
+ ipw_write_reg32(p, 0x301100, reg);
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO,
+ show_mem_gpio_reg, store_mem_gpio_reg);
+
+static ssize_t show_indirect_dword(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+
+ if (priv->status & STATUS_INDIRECT_DWORD)
+ reg = ipw_read_reg32(priv, priv->indirect_dword);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_indirect_dword(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->indirect_dword);
+ priv->status |= STATUS_INDIRECT_DWORD;
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO,
+ show_indirect_dword, store_indirect_dword);
+
+static ssize_t show_indirect_byte(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u8 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+
+ if (priv->status & STATUS_INDIRECT_BYTE)
+ reg = ipw_read_reg8(priv, priv->indirect_byte);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%02x\n", reg);
+}
+static ssize_t store_indirect_byte(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->indirect_byte);
+ priv->status |= STATUS_INDIRECT_BYTE;
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO,
+ show_indirect_byte, store_indirect_byte);
+
+static ssize_t show_direct_dword(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ u32 reg = 0;
+ struct ipw_priv *priv = d->driver_data;
+
+ if (priv->status & STATUS_DIRECT_DWORD)
+ reg = ipw_read32(priv, priv->direct_dword);
+ else
+ reg = 0;
+
+ return sprintf(buf, "0x%08x\n", reg);
+}
+static ssize_t store_direct_dword(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ sscanf(buf, "%x", &priv->direct_dword);
+ priv->status |= STATUS_DIRECT_DWORD;
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
+ show_direct_dword, store_direct_dword);
+
+static int rf_kill_active(struct ipw_priv *priv)
+{
+ if (0 == (ipw_read32(priv, 0x30) & 0x10000))
+ priv->status |= STATUS_RF_KILL_HW;
+ else
+ priv->status &= ~STATUS_RF_KILL_HW;
+
+ return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
+}
+
+static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ /* 0 - RF kill not enabled
+ 1 - SW based RF kill active (sysfs)
+ 2 - HW based RF kill active
+ 3 - Both HW and SW baed RF kill active */
+ struct ipw_priv *priv = d->driver_data;
+ int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) |
+ (rf_kill_active(priv) ? 0x2 : 0x0);
+ return sprintf(buf, "%i\n", val);
+}
+
+static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
+{
+ if ((disable_radio ? 1 : 0) ==
+ ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0))
+ return 0;
+
+ IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n",
+ disable_radio ? "OFF" : "ON");
+
+ if (disable_radio) {
+ priv->status |= STATUS_RF_KILL_SW;
+
+ if (priv->workqueue) {
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
+ cancel_delayed_work(&priv->scan_event);
+ }
+ queue_work(priv->workqueue, &priv->down);
+ } else {
+ priv->status &= ~STATUS_RF_KILL_SW;
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("Can not turn radio back on - "
+ "disabled by HW switch\n");
+ /* Make sure the RF_KILL check timer is running */
+ cancel_delayed_work(&priv->rf_kill);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ round_jiffies_relative(2 * HZ));
+ } else
+ queue_work(priv->workqueue, &priv->up);
+ }
+
+ return 1;
+}
+
+static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = d->driver_data;
+
+ ipw_radio_kill_sw(priv, buf[0] == '1');
+
+ return count;
+}
+
+static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill);
+
+static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
+ int pos = 0, len = 0;
+ if (priv->config & CFG_SPEED_SCAN) {
+ while (priv->speed_scan[pos] != 0)
+ len += sprintf(&buf[len], "%d ",
+ priv->speed_scan[pos++]);
+ return len + sprintf(&buf[len], "\n");
+ }
+
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
+ int channel, pos = 0;
+ const char *p = buf;
+
+ /* list of space separated channels to scan, optionally ending with 0 */
+ while ((channel = simple_strtol(p, NULL, 0))) {
+ if (pos == MAX_SPEED_SCAN - 1) {
+ priv->speed_scan[pos] = 0;
+ break;
+ }
+
+ if (ieee80211_is_valid_channel(priv->ieee, channel))
+ priv->speed_scan[pos++] = channel;
+ else
+ IPW_WARNING("Skipping invalid channel request: %d\n",
+ channel);
+ p = strchr(p, ' ');
+ if (!p)
+ break;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+
+ if (pos == 0)
+ priv->config &= ~CFG_SPEED_SCAN;
+ else {
+ priv->speed_scan_pos = 0;
+ priv->config |= CFG_SPEED_SCAN;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan,
+ store_speed_scan);
+
+static ssize_t show_net_stats(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
+ return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0');
+}
+
+static ssize_t store_net_stats(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ipw_priv *priv = (struct ipw_priv *)d->driver_data;
+ if (buf[0] == '1')
+ priv->config |= CFG_NET_STATS;
+ else
+ priv->config &= ~CFG_NET_STATS;
+
+ return count;
+}
+
+static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO,
+ show_net_stats, store_net_stats);
+
+static ssize_t show_channels(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipw_priv *priv = dev_get_drvdata(d);
+ const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ int len = 0, i;
+
+ len = sprintf(&buf[len],
+ "Displaying %d channels in 2.4Ghz band "
+ "(802.11bg):\n", geo->bg_channels);
+
+ for (i = 0; i < geo->bg_channels; i++) {
+ len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n",
+ geo->bg[i].channel,
+ geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ?
+ " (radar spectrum)" : "",
+ ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) ||
+ (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT))
+ ? "" : ", IBSS",
+ geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+ "passive only" : "active/passive",
+ geo->bg[i].flags & IEEE80211_CH_B_ONLY ?
+ "B" : "B/G");
+ }
+
+ len += sprintf(&buf[len],
+ "Displaying %d channels in 5.2Ghz band "
+ "(802.11a):\n", geo->a_channels);
+ for (i = 0; i < geo->a_channels; i++) {
+ len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n",
+ geo->a[i].channel,
+ geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ?
+ " (radar spectrum)" : "",
+ ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) ||
+ (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT))
+ ? "" : ", IBSS",
+ geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ?
+ "passive only" : "active/passive");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
+
+static void notify_wx_assoc_event(struct ipw_priv *priv)
+{
+ union iwreq_data wrqu;
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ if (priv->status & STATUS_ASSOCIATED)
+ memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ else
+ memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
+ wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void ipw_irq_tasklet(struct ipw_priv *priv)
+{
+ u32 inta, inta_mask, handled = 0;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&priv->irq_lock, flags);
+
+ inta = ipw_read32(priv, IPW_INTA_RW);
+ inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
+ inta &= (IPW_INTA_MASK_ALL & inta_mask);
+
+ /* Add any cached INTA values that need to be handled */
+ inta |= priv->isr_inta;
+
+ spin_unlock_irqrestore(&priv->irq_lock, flags);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* handle all the justifications for the interrupt */
+ if (inta & IPW_INTA_BIT_RX_TRANSFER) {
+ ipw_rx(priv);
+ handled |= IPW_INTA_BIT_RX_TRANSFER;
+ }
+
+ if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) {
+ IPW_DEBUG_HC("Command completed.\n");
+ rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1);
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ wake_up_interruptible(&priv->wait_command_queue);
+ handled |= IPW_INTA_BIT_TX_CMD_QUEUE;
+ }
+
+ if (inta & IPW_INTA_BIT_TX_QUEUE_1) {
+ IPW_DEBUG_TX("TX_QUEUE_1\n");
+ rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0);
+ handled |= IPW_INTA_BIT_TX_QUEUE_1;
+ }
+
+ if (inta & IPW_INTA_BIT_TX_QUEUE_2) {
+ IPW_DEBUG_TX("TX_QUEUE_2\n");
+ rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1);
+ handled |= IPW_INTA_BIT_TX_QUEUE_2;
+ }
+
+ if (inta & IPW_INTA_BIT_TX_QUEUE_3) {
+ IPW_DEBUG_TX("TX_QUEUE_3\n");
+ rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2);
+ handled |= IPW_INTA_BIT_TX_QUEUE_3;
+ }
+
+ if (inta & IPW_INTA_BIT_TX_QUEUE_4) {
+ IPW_DEBUG_TX("TX_QUEUE_4\n");
+ rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3);
+ handled |= IPW_INTA_BIT_TX_QUEUE_4;
+ }
+
+ if (inta & IPW_INTA_BIT_STATUS_CHANGE) {
+ IPW_WARNING("STATUS_CHANGE\n");
+ handled |= IPW_INTA_BIT_STATUS_CHANGE;
+ }
+
+ if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) {
+ IPW_WARNING("TX_PERIOD_EXPIRED\n");
+ handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED;
+ }
+
+ if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) {
+ IPW_WARNING("HOST_CMD_DONE\n");
+ handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE;
+ }
+
+ if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) {
+ IPW_WARNING("FW_INITIALIZATION_DONE\n");
+ handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE;
+ }
+
+ if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) {
+ IPW_WARNING("PHY_OFF_DONE\n");
+ handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE;
+ }
+
+ if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
+ IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
+ priv->status |= STATUS_RF_KILL_HW;
+ wake_up_interruptible(&priv->wait_command_queue);
+ priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
+ cancel_delayed_work(&priv->scan_event);
+ schedule_work(&priv->link_down);
+ queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
+ handled |= IPW_INTA_BIT_RF_KILL_DONE;
+ }
+
+ if (inta & IPW_INTA_BIT_FATAL_ERROR) {
+ IPW_WARNING("Firmware error detected. Restarting.\n");
+ if (priv->error) {
+ IPW_DEBUG_FW("Sysfs 'error' log already exists.\n");
+ if (ipw_debug_level & IPW_DL_FW_ERRORS) {
+ struct ipw_fw_error *error =
+ ipw_alloc_error_log(priv);
+ ipw_dump_error_log(priv, error);
+ kfree(error);
+ }
+ } else {
+ priv->error = ipw_alloc_error_log(priv);
+ if (priv->error)
+ IPW_DEBUG_FW("Sysfs 'error' log captured.\n");
+ else
+ IPW_DEBUG_FW("Error allocating sysfs 'error' "
+ "log.\n");
+ if (ipw_debug_level & IPW_DL_FW_ERRORS)
+ ipw_dump_error_log(priv, priv->error);
+ }
+
+ /* XXX: If hardware encryption is for WPA/WPA2,
+ * we have to notify the supplicant. */
+ if (priv->ieee->sec.encrypt) {
+ priv->status &= ~STATUS_ASSOCIATED;
+ notify_wx_assoc_event(priv);
+ }
+
+ /* Keep the restart process from trying to send host
+ * commands by clearing the INIT status bit */
+ priv->status &= ~STATUS_INIT;
+
+ /* Cancel currently queued command. */
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ wake_up_interruptible(&priv->wait_command_queue);
+
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ handled |= IPW_INTA_BIT_FATAL_ERROR;
+ }
+
+ if (inta & IPW_INTA_BIT_PARITY_ERROR) {
+ IPW_ERROR("Parity error\n");
+ handled |= IPW_INTA_BIT_PARITY_ERROR;
+ }
+
+ if (handled != inta) {
+ IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* enable all interrupts */
+ ipw_enable_interrupts(priv);
+}
+
+#define IPW_CMD(x) case IPW_CMD_ ## x : return #x
+static char *get_cmd_string(u8 cmd)
+{
+ switch (cmd) {
+ IPW_CMD(HOST_COMPLETE);
+ IPW_CMD(POWER_DOWN);
+ IPW_CMD(SYSTEM_CONFIG);
+ IPW_CMD(MULTICAST_ADDRESS);
+ IPW_CMD(SSID);
+ IPW_CMD(ADAPTER_ADDRESS);
+ IPW_CMD(PORT_TYPE);
+ IPW_CMD(RTS_THRESHOLD);
+ IPW_CMD(FRAG_THRESHOLD);
+ IPW_CMD(POWER_MODE);
+ IPW_CMD(WEP_KEY);
+ IPW_CMD(TGI_TX_KEY);
+ IPW_CMD(SCAN_REQUEST);
+ IPW_CMD(SCAN_REQUEST_EXT);
+ IPW_CMD(ASSOCIATE);
+ IPW_CMD(SUPPORTED_RATES);
+ IPW_CMD(SCAN_ABORT);
+ IPW_CMD(TX_FLUSH);
+ IPW_CMD(QOS_PARAMETERS);
+ IPW_CMD(DINO_CONFIG);
+ IPW_CMD(RSN_CAPABILITIES);
+ IPW_CMD(RX_KEY);
+ IPW_CMD(CARD_DISABLE);
+ IPW_CMD(SEED_NUMBER);
+ IPW_CMD(TX_POWER);
+ IPW_CMD(COUNTRY_INFO);
+ IPW_CMD(AIRONET_INFO);
+ IPW_CMD(AP_TX_POWER);
+ IPW_CMD(CCKM_INFO);
+ IPW_CMD(CCX_VER_INFO);
+ IPW_CMD(SET_CALIBRATION);
+ IPW_CMD(SENSITIVITY_CALIB);
+ IPW_CMD(RETRY_LIMIT);
+ IPW_CMD(IPW_PRE_POWER_DOWN);
+ IPW_CMD(VAP_BEACON_TEMPLATE);
+ IPW_CMD(VAP_DTIM_PERIOD);
+ IPW_CMD(EXT_SUPPORTED_RATES);
+ IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT);
+ IPW_CMD(VAP_QUIET_INTERVALS);
+ IPW_CMD(VAP_CHANNEL_SWITCH);
+ IPW_CMD(VAP_MANDATORY_CHANNELS);
+ IPW_CMD(VAP_CELL_PWR_LIMIT);
+ IPW_CMD(VAP_CF_PARAM_SET);
+ IPW_CMD(VAP_SET_BEACONING_STATE);
+ IPW_CMD(MEASUREMENT);
+ IPW_CMD(POWER_CAPABILITY);
+ IPW_CMD(SUPPORTED_CHANNELS);
+ IPW_CMD(TPC_REPORT);
+ IPW_CMD(WME_INFO);
+ IPW_CMD(PRODUCTION_COMMAND);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+#define HOST_COMPLETE_TIMEOUT HZ
+
+static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->status & STATUS_HCMD_ACTIVE) {
+ IPW_ERROR("Failed to send %s: Already sending a command.\n",
+ get_cmd_string(cmd->cmd));
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -EAGAIN;
+ }
+
+ priv->status |= STATUS_HCMD_ACTIVE;
+
+ if (priv->cmdlog) {
+ priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies;
+ priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd;
+ priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len;
+ memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param,
+ cmd->len);
+ priv->cmdlog[priv->cmdlog_pos].retcode = -1;
+ }
+
+ IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n",
+ get_cmd_string(cmd->cmd), cmd->cmd, cmd->len,
+ priv->status);
+
+#ifndef DEBUG_CMD_WEP_KEY
+ if (cmd->cmd == IPW_CMD_WEP_KEY)
+ IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n");
+ else
+#endif
+ printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len);
+
+ rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0);
+ if (rc) {
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ IPW_ERROR("Failed to send %s: Reason %d\n",
+ get_cmd_string(cmd->cmd), rc);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ goto exit;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ rc = wait_event_interruptible_timeout(priv->wait_command_queue,
+ !(priv->
+ status & STATUS_HCMD_ACTIVE),
+ HOST_COMPLETE_TIMEOUT);
+ if (rc == 0) {
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->status & STATUS_HCMD_ACTIVE) {
+ IPW_ERROR("Failed to send %s: Command timed out.\n",
+ get_cmd_string(cmd->cmd));
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ rc = -EIO;
+ goto exit;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ } else
+ rc = 0;
+
+ if (priv->status & STATUS_RF_KILL_HW) {
+ IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n",
+ get_cmd_string(cmd->cmd));
+ rc = -EIO;
+ goto exit;
+ }
+
+ exit:
+ if (priv->cmdlog) {
+ priv->cmdlog[priv->cmdlog_pos++].retcode = rc;
+ priv->cmdlog_pos %= priv->cmdlog_len;
+ }
+ return rc;
+}
+
+static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command)
+{
+ struct host_cmd cmd = {
+ .cmd = command,
+ };
+
+ return __ipw_send_cmd(priv, &cmd);
+}
+
+static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len,
+ void *data)
+{
+ struct host_cmd cmd = {
+ .cmd = command,
+ .len = len,
+ .param = data,
+ };
+
+ return __ipw_send_cmd(priv, &cmd);
+}
+
+static int ipw_send_host_complete(struct ipw_priv *priv)
+{
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE);
+}
+
+static int ipw_send_system_config(struct ipw_priv *priv)
+{
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG,
+ sizeof(priv->sys_config),
+ &priv->sys_config);
+}
+
+static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len)
+{
+ if (!priv || !ssid) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE),
+ ssid);
+}
+
+static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac)
+{
+ if (!priv || !mac) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ IPW_DEBUG_INFO("%s: Setting MAC to %pM\n",
+ priv->net_dev->name, mac);
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac);
+}
+
+/*
+ * NOTE: This must be executed from our workqueue as it results in udelay
+ * being called which may corrupt the keyboard if executed on default
+ * workqueue
+ */
+static void ipw_adapter_restart(void *adapter)
+{
+ struct ipw_priv *priv = adapter;
+
+ if (priv->status & STATUS_RF_KILL_MASK)
+ return;
+
+ ipw_down(priv);
+
+ if (priv->assoc_network &&
+ (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS))
+ ipw_remove_current_network(priv);
+
+ if (ipw_up(priv)) {
+ IPW_ERROR("Failed to up device\n");
+ return;
+ }
+}
+
+static void ipw_bg_adapter_restart(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, adapter_restart);
+ mutex_lock(&priv->mutex);
+ ipw_adapter_restart(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ)
+
+static void ipw_scan_check(void *data)
+{
+ struct ipw_priv *priv = data;
+ if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
+ IPW_DEBUG_SCAN("Scan completion watchdog resetting "
+ "adapter after (%dms).\n",
+ jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG));
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ }
+}
+
+static void ipw_bg_scan_check(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, scan_check.work);
+ mutex_lock(&priv->mutex);
+ ipw_scan_check(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static int ipw_send_scan_request_ext(struct ipw_priv *priv,
+ struct ipw_scan_request_ext *request)
+{
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT,
+ sizeof(*request), request);
+}
+
+static int ipw_send_scan_abort(struct ipw_priv *priv)
+{
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT);
+}
+
+static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens)
+{
+ struct ipw_sensitivity_calib calib = {
+ .beacon_rssi_raw = cpu_to_le16(sens),
+ };
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib),
+ &calib);
+}
+
+static int ipw_send_associate(struct ipw_priv *priv,
+ struct ipw_associate *associate)
+{
+ if (!priv || !associate) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate),
+ associate);
+}
+
+static int ipw_send_supported_rates(struct ipw_priv *priv,
+ struct ipw_supported_rates *rates)
+{
+ if (!priv || !rates) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates),
+ rates);
+}
+
+static int ipw_set_random_seed(struct ipw_priv *priv)
+{
+ u32 val;
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ get_random_bytes(&val, sizeof(val));
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val);
+}
+
+static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off)
+{
+ __le32 v = cpu_to_le32(phy_off);
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v);
+}
+
+static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power)
+{
+ if (!priv || !power) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power);
+}
+
+static int ipw_set_tx_power(struct ipw_priv *priv)
+{
+ const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ struct ipw_tx_power tx_power;
+ s8 max_power;
+ int i;
+
+ memset(&tx_power, 0, sizeof(tx_power));
+
+ /* configure device for 'G' band */
+ tx_power.ieee_mode = IPW_G_MODE;
+ tx_power.num_channels = geo->bg_channels;
+ for (i = 0; i < geo->bg_channels; i++) {
+ max_power = geo->bg[i].max_power;
+ tx_power.channels_tx_power[i].channel_number =
+ geo->bg[i].channel;
+ tx_power.channels_tx_power[i].tx_power = max_power ?
+ min(max_power, priv->tx_power) : priv->tx_power;
+ }
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+
+ /* configure device to also handle 'B' band */
+ tx_power.ieee_mode = IPW_B_MODE;
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+
+ /* configure device to also handle 'A' band */
+ if (priv->ieee->abg_true) {
+ tx_power.ieee_mode = IPW_A_MODE;
+ tx_power.num_channels = geo->a_channels;
+ for (i = 0; i < tx_power.num_channels; i++) {
+ max_power = geo->a[i].max_power;
+ tx_power.channels_tx_power[i].channel_number =
+ geo->a[i].channel;
+ tx_power.channels_tx_power[i].tx_power = max_power ?
+ min(max_power, priv->tx_power) : priv->tx_power;
+ }
+ if (ipw_send_tx_power(priv, &tx_power))
+ return -EIO;
+ }
+ return 0;
+}
+
+static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts)
+{
+ struct ipw_rts_threshold rts_threshold = {
+ .rts_threshold = cpu_to_le16(rts),
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD,
+ sizeof(rts_threshold), &rts_threshold);
+}
+
+static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag)
+{
+ struct ipw_frag_threshold frag_threshold = {
+ .frag_threshold = cpu_to_le16(frag),
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD,
+ sizeof(frag_threshold), &frag_threshold);
+}
+
+static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode)
+{
+ __le32 param;
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ /* If on battery, set to 3, if AC set to CAM, else user
+ * level */
+ switch (mode) {
+ case IPW_POWER_BATTERY:
+ param = cpu_to_le32(IPW_POWER_INDEX_3);
+ break;
+ case IPW_POWER_AC:
+ param = cpu_to_le32(IPW_POWER_MODE_CAM);
+ break;
+ default:
+ param = cpu_to_le32(mode);
+ break;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param),
+ &param);
+}
+
+static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit)
+{
+ struct ipw_retry_limit retry_limit = {
+ .short_retry_limit = slimit,
+ .long_retry_limit = llimit
+ };
+
+ if (!priv) {
+ IPW_ERROR("Invalid args\n");
+ return -1;
+ }
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit),
+ &retry_limit);
+}
+
+/*
+ * The IPW device contains a Microwire compatible EEPROM that stores
+ * various data like the MAC address. Usually the firmware has exclusive
+ * access to the eeprom, but during device initialization (before the
+ * device driver has sent the HostComplete command to the firmware) the
+ * device driver has read access to the EEPROM by way of indirect addressing
+ * through a couple of memory mapped registers.
+ *
+ * The following is a simplified implementation for pulling data out of the
+ * the eeprom, along with some helper functions to find information in
+ * the per device private data's copy of the eeprom.
+ *
+ * NOTE: To better understand how these functions work (i.e what is a chip
+ * select and why do have to keep driving the eeprom clock?), read
+ * just about any data sheet for a Microwire compatible EEPROM.
+ */
+
+/* write a 32 bit value into the indirect accessor register */
+static inline void eeprom_write_reg(struct ipw_priv *p, u32 data)
+{
+ ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data);
+
+ /* the eeprom requires some time to complete the operation */
+ udelay(p->eeprom_delay);
+
+ return;
+}
+
+/* perform a chip select operation */
+static void eeprom_cs(struct ipw_priv *priv)
+{
+ eeprom_write_reg(priv, 0);
+ eeprom_write_reg(priv, EEPROM_BIT_CS);
+ eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
+ eeprom_write_reg(priv, EEPROM_BIT_CS);
+}
+
+/* perform a chip select operation */
+static void eeprom_disable_cs(struct ipw_priv *priv)
+{
+ eeprom_write_reg(priv, EEPROM_BIT_CS);
+ eeprom_write_reg(priv, 0);
+ eeprom_write_reg(priv, EEPROM_BIT_SK);
+}
+
+/* push a single bit down to the eeprom */
+static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit)
+{
+ int d = (bit ? EEPROM_BIT_DI : 0);
+ eeprom_write_reg(p, EEPROM_BIT_CS | d);
+ eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK);
+}
+
+/* push an opcode followed by an address down to the eeprom */
+static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr)
+{
+ int i;
+
+ eeprom_cs(priv);
+ eeprom_write_bit(priv, 1);
+ eeprom_write_bit(priv, op & 2);
+ eeprom_write_bit(priv, op & 1);
+ for (i = 7; i >= 0; i--) {
+ eeprom_write_bit(priv, addr & (1 << i));
+ }
+}
+
+/* pull 16 bits off the eeprom, one bit at a time */
+static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr)
+{
+ int i;
+ u16 r = 0;
+
+ /* Send READ Opcode */
+ eeprom_op(priv, EEPROM_CMD_READ, addr);
+
+ /* Send dummy bit */
+ eeprom_write_reg(priv, EEPROM_BIT_CS);
+
+ /* Read the byte off the eeprom one bit at a time */
+ for (i = 0; i < 16; i++) {
+ u32 data = 0;
+ eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK);
+ eeprom_write_reg(priv, EEPROM_BIT_CS);
+ data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS);
+ r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0);
+ }
+
+ /* Send another dummy bit */
+ eeprom_write_reg(priv, 0);
+ eeprom_disable_cs(priv);
+
+ return r;
+}
+
+/* helper function for pulling the mac address out of the private */
+/* data's copy of the eeprom data */
+static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac)
+{
+ memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6);
+}
+
+/*
+ * Either the device driver (i.e. the host) or the firmware can
+ * load eeprom data into the designated region in SRAM. If neither
+ * happens then the FW will shutdown with a fatal error.
+ *
+ * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE
+ * bit needs region of shared SRAM needs to be non-zero.
+ */
+static void ipw_eeprom_init_sram(struct ipw_priv *priv)
+{
+ int i;
+ __le16 *eeprom = (__le16 *) priv->eeprom;
+
+ IPW_DEBUG_TRACE(">>\n");
+
+ /* read entire contents of eeprom into private buffer */
+ for (i = 0; i < 128; i++)
+ eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i));
+
+ /*
+ If the data looks correct, then copy it to our private
+ copy. Otherwise let the firmware know to perform the operation
+ on its own.
+ */
+ if (priv->eeprom[EEPROM_VERSION] != 0) {
+ IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n");
+
+ /* write the eeprom data to sram */
+ for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
+ ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]);
+
+ /* Do not load eeprom data on fatal error or suspend */
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+ } else {
+ IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n");
+
+ /* Load eeprom data on fatal error or suspend */
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1);
+ }
+
+ IPW_DEBUG_TRACE("<<\n");
+}
+
+static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count)
+{
+ count >>= 2;
+ if (!count)
+ return;
+ _ipw_write32(priv, IPW_AUTOINC_ADDR, start);
+ while (count--)
+ _ipw_write32(priv, IPW_AUTOINC_DATA, 0);
+}
+
+static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv)
+{
+ ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL,
+ CB_NUMBER_OF_ELEMENTS_SMALL *
+ sizeof(struct command_block));
+}
+
+static int ipw_fw_dma_enable(struct ipw_priv *priv)
+{ /* start dma engine but no transfers yet */
+
+ IPW_DEBUG_FW(">> : \n");
+
+ /* Start the dma */
+ ipw_fw_dma_reset_command_blocks(priv);
+
+ /* Write CB base address */
+ ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL);
+
+ IPW_DEBUG_FW("<< : \n");
+ return 0;
+}
+
+static void ipw_fw_dma_abort(struct ipw_priv *priv)
+{
+ u32 control = 0;
+
+ IPW_DEBUG_FW(">> :\n");
+
+ /* set the Stop and Abort bit */
+ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT;
+ ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
+ priv->sram_desc.last_cb_index = 0;
+
+ IPW_DEBUG_FW("<< \n");
+}
+
+static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index,
+ struct command_block *cb)
+{
+ u32 address =
+ IPW_SHARED_SRAM_DMA_CONTROL +
+ (sizeof(struct command_block) * index);
+ IPW_DEBUG_FW(">> :\n");
+
+ ipw_write_indirect(priv, address, (u8 *) cb,
+ (int)sizeof(struct command_block));
+
+ IPW_DEBUG_FW("<< :\n");
+ return 0;
+
+}
+
+static int ipw_fw_dma_kick(struct ipw_priv *priv)
+{
+ u32 control = 0;
+ u32 index = 0;
+
+ IPW_DEBUG_FW(">> :\n");
+
+ for (index = 0; index < priv->sram_desc.last_cb_index; index++)
+ ipw_fw_dma_write_command_block(priv, index,
+ &priv->sram_desc.cb_list[index]);
+
+ /* Enable the DMA in the CSR register */
+ ipw_clear_bit(priv, IPW_RESET_REG,
+ IPW_RESET_REG_MASTER_DISABLED |
+ IPW_RESET_REG_STOP_MASTER);
+
+ /* Set the Start bit. */
+ control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START;
+ ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control);
+
+ IPW_DEBUG_FW("<< :\n");
+ return 0;
+}
+
+static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv)
+{
+ u32 address;
+ u32 register_value = 0;
+ u32 cb_fields_address = 0;
+
+ IPW_DEBUG_FW(">> :\n");
+ address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
+ IPW_DEBUG_FW_INFO("Current CB is 0x%x \n", address);
+
+ /* Read the DMA Controlor register */
+ register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL);
+ IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x \n", register_value);
+
+ /* Print the CB values */
+ cb_fields_address = address;
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB ControlField is 0x%x \n", register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x \n", register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x \n",
+ register_value);
+
+ cb_fields_address += sizeof(u32);
+ register_value = ipw_read_reg32(priv, cb_fields_address);
+ IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x \n", register_value);
+
+ IPW_DEBUG_FW(">> :\n");
+}
+
+static int ipw_fw_dma_command_block_index(struct ipw_priv *priv)
+{
+ u32 current_cb_address = 0;
+ u32 current_cb_index = 0;
+
+ IPW_DEBUG_FW("<< :\n");
+ current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB);
+
+ current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) /
+ sizeof(struct command_block);
+
+ IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X \n",
+ current_cb_index, current_cb_address);
+
+ IPW_DEBUG_FW(">> :\n");
+ return current_cb_index;
+
+}
+
+static int ipw_fw_dma_add_command_block(struct ipw_priv *priv,
+ u32 src_address,
+ u32 dest_address,
+ u32 length,
+ int interrupt_enabled, int is_last)
+{
+
+ u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC |
+ CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG |
+ CB_DEST_SIZE_LONG;
+ struct command_block *cb;
+ u32 last_cb_element = 0;
+
+ IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n",
+ src_address, dest_address, length);
+
+ if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL)
+ return -1;
+
+ last_cb_element = priv->sram_desc.last_cb_index;
+ cb = &priv->sram_desc.cb_list[last_cb_element];
+ priv->sram_desc.last_cb_index++;
+
+ /* Calculate the new CB control word */
+ if (interrupt_enabled)
+ control |= CB_INT_ENABLED;
+
+ if (is_last)
+ control |= CB_LAST_VALID;
+
+ control |= length;
+
+ /* Calculate the CB Element's checksum value */
+ cb->status = control ^ src_address ^ dest_address;
+
+ /* Copy the Source and Destination addresses */
+ cb->dest_addr = dest_address;
+ cb->source_addr = src_address;
+
+ /* Copy the Control Word last */
+ cb->control = control;
+
+ return 0;
+}
+
+static int ipw_fw_dma_add_buffer(struct ipw_priv *priv,
+ u32 src_phys, u32 dest_address, u32 length)
+{
+ u32 bytes_left = length;
+ u32 src_offset = 0;
+ u32 dest_offset = 0;
+ int status = 0;
+ IPW_DEBUG_FW(">> \n");
+ IPW_DEBUG_FW_INFO("src_phys=0x%x dest_address=0x%x length=0x%x\n",
+ src_phys, dest_address, length);
+ while (bytes_left > CB_MAX_LENGTH) {
+ status = ipw_fw_dma_add_command_block(priv,
+ src_phys + src_offset,
+ dest_address +
+ dest_offset,
+ CB_MAX_LENGTH, 0, 0);
+ if (status) {
+ IPW_DEBUG_FW_INFO(": Failed\n");
+ return -1;
+ } else
+ IPW_DEBUG_FW_INFO(": Added new cb\n");
+
+ src_offset += CB_MAX_LENGTH;
+ dest_offset += CB_MAX_LENGTH;
+ bytes_left -= CB_MAX_LENGTH;
+ }
+
+ /* add the buffer tail */
+ if (bytes_left > 0) {
+ status =
+ ipw_fw_dma_add_command_block(priv, src_phys + src_offset,
+ dest_address + dest_offset,
+ bytes_left, 0, 0);
+ if (status) {
+ IPW_DEBUG_FW_INFO(": Failed on the buffer tail\n");
+ return -1;
+ } else
+ IPW_DEBUG_FW_INFO
+ (": Adding new cb - the buffer tail\n");
+ }
+
+ IPW_DEBUG_FW("<< \n");
+ return 0;
+}
+
+static int ipw_fw_dma_wait(struct ipw_priv *priv)
+{
+ u32 current_index = 0, previous_index;
+ u32 watchdog = 0;
+
+ IPW_DEBUG_FW(">> : \n");
+
+ current_index = ipw_fw_dma_command_block_index(priv);
+ IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n",
+ (int)priv->sram_desc.last_cb_index);
+
+ while (current_index < priv->sram_desc.last_cb_index) {
+ udelay(50);
+ previous_index = current_index;
+ current_index = ipw_fw_dma_command_block_index(priv);
+
+ if (previous_index < current_index) {
+ watchdog = 0;
+ continue;
+ }
+ if (++watchdog > 400) {
+ IPW_DEBUG_FW_INFO("Timeout\n");
+ ipw_fw_dma_dump_command_block(priv);
+ ipw_fw_dma_abort(priv);
+ return -1;
+ }
+ }
+
+ ipw_fw_dma_abort(priv);
+
+ /*Disable the DMA in the CSR register */
+ ipw_set_bit(priv, IPW_RESET_REG,
+ IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER);
+
+ IPW_DEBUG_FW("<< dmaWaitSync \n");
+ return 0;
+}
+
+static void ipw_remove_current_network(struct ipw_priv *priv)
+{
+ struct list_head *element, *safe;
+ struct ieee80211_network *network = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ list_for_each_safe(element, safe, &priv->ieee->network_list) {
+ network = list_entry(element, struct ieee80211_network, list);
+ if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+ list_del(element);
+ list_add_tail(&network->list,
+ &priv->ieee->network_free_list);
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+}
+
+/**
+ * Check that card is still alive.
+ * Reads debug register from domain0.
+ * If card is present, pre-defined value should
+ * be found there.
+ *
+ * @param priv
+ * @return 1 if card is present, 0 otherwise
+ */
+static inline int ipw_alive(struct ipw_priv *priv)
+{
+ return ipw_read32(priv, 0x90) == 0xd55555d5;
+}
+
+/* timeout in msec, attempted in 10-msec quanta */
+static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask,
+ int timeout)
+{
+ int i = 0;
+
+ do {
+ if ((ipw_read32(priv, addr) & mask) == mask)
+ return i;
+ mdelay(10);
+ i += 10;
+ } while (i < timeout);
+
+ return -ETIME;
+}
+
+/* These functions load the firmware and micro code for the operation of
+ * the ipw hardware. It assumes the buffer has all the bits for the
+ * image and the caller is handling the memory allocation and clean up.
+ */
+
+static int ipw_stop_master(struct ipw_priv *priv)
+{
+ int rc;
+
+ IPW_DEBUG_TRACE(">> \n");
+ /* stop master. typical delay - 0 */
+ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
+
+ /* timeout is in msec, polled in 10-msec quanta */
+ rc = ipw_poll_bit(priv, IPW_RESET_REG,
+ IPW_RESET_REG_MASTER_DISABLED, 100);
+ if (rc < 0) {
+ IPW_ERROR("wait for stop master failed after 100ms\n");
+ return -1;
+ }
+
+ IPW_DEBUG_INFO("stop master %dms\n", rc);
+
+ return rc;
+}
+
+static void ipw_arc_release(struct ipw_priv *priv)
+{
+ IPW_DEBUG_TRACE(">> \n");
+ mdelay(5);
+
+ ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+ /* no one knows timing, for safety add some delay */
+ mdelay(5);
+}
+
+struct fw_chunk {
+ __le32 address;
+ __le32 length;
+};
+
+static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len)
+{
+ int rc = 0, i, addr;
+ u8 cr = 0;
+ __le16 *image;
+
+ image = (__le16 *) data;
+
+ IPW_DEBUG_TRACE(">> \n");
+
+ rc = ipw_stop_master(priv);
+
+ if (rc < 0)
+ return rc;
+
+ for (addr = IPW_SHARED_LOWER_BOUND;
+ addr < IPW_REGISTER_DOMAIN1_END; addr += 4) {
+ ipw_write32(priv, addr, 0);
+ }
+
+ /* no ucode (yet) */
+ memset(&priv->dino_alive, 0, sizeof(priv->dino_alive));
+ /* destroy DMA queues */
+ /* reset sequence */
+
+ ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON);
+ ipw_arc_release(priv);
+ ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF);
+ mdelay(1);
+
+ /* reset PHY */
+ ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN);
+ mdelay(1);
+
+ ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0);
+ mdelay(1);
+
+ /* enable ucode store */
+ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0);
+ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS);
+ mdelay(1);
+
+ /* write ucode */
+ /**
+ * @bug
+ * Do NOT set indirect address register once and then
+ * store data to indirect data register in the loop.
+ * It seems very reasonable, but in this case DINO do not
+ * accept ucode. It is essential to set address each time.
+ */
+ /* load new ipw uCode */
+ for (i = 0; i < len / 2; i++)
+ ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE,
+ le16_to_cpu(image[i]));
+
+ /* enable DINO */
+ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
+ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM);
+
+ /* this is where the igx / win driver deveates from the VAP driver. */
+
+ /* wait for alive response */
+ for (i = 0; i < 100; i++) {
+ /* poll for incoming data */
+ cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS);
+ if (cr & DINO_RXFIFO_DATA)
+ break;
+ mdelay(1);
+ }
+
+ if (cr & DINO_RXFIFO_DATA) {
+ /* alive_command_responce size is NOT multiple of 4 */
+ __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4];
+
+ for (i = 0; i < ARRAY_SIZE(response_buffer); i++)
+ response_buffer[i] =
+ cpu_to_le32(ipw_read_reg32(priv,
+ IPW_BASEBAND_RX_FIFO_READ));
+ memcpy(&priv->dino_alive, response_buffer,
+ sizeof(priv->dino_alive));
+ if (priv->dino_alive.alive_command == 1
+ && priv->dino_alive.ucode_valid == 1) {
+ rc = 0;
+ IPW_DEBUG_INFO
+ ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) "
+ "of %02d/%02d/%02d %02d:%02d\n",
+ priv->dino_alive.software_revision,
+ priv->dino_alive.software_revision,
+ priv->dino_alive.device_identifier,
+ priv->dino_alive.device_identifier,
+ priv->dino_alive.time_stamp[0],
+ priv->dino_alive.time_stamp[1],
+ priv->dino_alive.time_stamp[2],
+ priv->dino_alive.time_stamp[3],
+ priv->dino_alive.time_stamp[4]);
+ } else {
+ IPW_DEBUG_INFO("Microcode is not alive\n");
+ rc = -EINVAL;
+ }
+ } else {
+ IPW_DEBUG_INFO("No alive response from DINO\n");
+ rc = -ETIME;
+ }
+
+ /* disable DINO, otherwise for some reason
+ firmware have problem getting alive resp. */
+ ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0);
+
+ return rc;
+}
+
+static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len)
+{
+ int rc = -1;
+ int offset = 0;
+ struct fw_chunk *chunk;
+ dma_addr_t shared_phys;
+ u8 *shared_virt;
+
+ IPW_DEBUG_TRACE("<< : \n");
+ shared_virt = pci_alloc_consistent(priv->pci_dev, len, &shared_phys);
+
+ if (!shared_virt)
+ return -ENOMEM;
+
+ memmove(shared_virt, data, len);
+
+ /* Start the Dma */
+ rc = ipw_fw_dma_enable(priv);
+
+ if (priv->sram_desc.last_cb_index > 0) {
+ /* the DMA is already ready this would be a bug. */
+ BUG();
+ goto out;
+ }
+
+ do {
+ chunk = (struct fw_chunk *)(data + offset);
+ offset += sizeof(struct fw_chunk);
+ /* build DMA packet and queue up for sending */
+ /* dma to chunk->address, the chunk->length bytes from data +
+ * offeset*/
+ /* Dma loading */
+ rc = ipw_fw_dma_add_buffer(priv, shared_phys + offset,
+ le32_to_cpu(chunk->address),
+ le32_to_cpu(chunk->length));
+ if (rc) {
+ IPW_DEBUG_INFO("dmaAddBuffer Failed\n");
+ goto out;
+ }
+
+ offset += le32_to_cpu(chunk->length);
+ } while (offset < len);
+
+ /* Run the DMA and wait for the answer */
+ rc = ipw_fw_dma_kick(priv);
+ if (rc) {
+ IPW_ERROR("dmaKick Failed\n");
+ goto out;
+ }
+
+ rc = ipw_fw_dma_wait(priv);
+ if (rc) {
+ IPW_ERROR("dmaWaitSync Failed\n");
+ goto out;
+ }
+ out:
+ pci_free_consistent(priv->pci_dev, len, shared_virt, shared_phys);
+ return rc;
+}
+
+/* stop nic */
+static int ipw_stop_nic(struct ipw_priv *priv)
+{
+ int rc = 0;
+
+ /* stop */
+ ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER);
+
+ rc = ipw_poll_bit(priv, IPW_RESET_REG,
+ IPW_RESET_REG_MASTER_DISABLED, 500);
+ if (rc < 0) {
+ IPW_ERROR("wait for reg master disabled failed after 500ms\n");
+ return rc;
+ }
+
+ ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET);
+
+ return rc;
+}
+
+static void ipw_start_nic(struct ipw_priv *priv)
+{
+ IPW_DEBUG_TRACE(">>\n");
+
+ /* prvHwStartNic release ARC */
+ ipw_clear_bit(priv, IPW_RESET_REG,
+ IPW_RESET_REG_MASTER_DISABLED |
+ IPW_RESET_REG_STOP_MASTER |
+ CBD_RESET_REG_PRINCETON_RESET);
+
+ /* enable power management */
+ ipw_set_bit(priv, IPW_GP_CNTRL_RW,
+ IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY);
+
+ IPW_DEBUG_TRACE("<<\n");
+}
+
+static int ipw_init_nic(struct ipw_priv *priv)
+{
+ int rc;
+
+ IPW_DEBUG_TRACE(">>\n");
+ /* reset */
+ /*prvHwInitNic */
+ /* set "initialization complete" bit to move adapter to D0 state */
+ ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
+
+ /* low-level PLL activation */
+ ipw_write32(priv, IPW_READ_INT_REGISTER,
+ IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER);
+
+ /* wait for clock stabilization */
+ rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW,
+ IPW_GP_CNTRL_BIT_CLOCK_READY, 250);
+ if (rc < 0)
+ IPW_DEBUG_INFO("FAILED wait for clock stablization\n");
+
+ /* assert SW reset */
+ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET);
+
+ udelay(10);
+
+ /* set "initialization complete" bit to move adapter to D0 state */
+ ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE);
+
+ IPW_DEBUG_TRACE(">>\n");
+ return 0;
+}
+
+/* Call this function from process context, it will sleep in request_firmware.
+ * Probe is an ok place to call this from.
+ */
+static int ipw_reset_nic(struct ipw_priv *priv)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ IPW_DEBUG_TRACE(">>\n");
+
+ rc = ipw_init_nic(priv);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ /* Clear the 'host command active' bit... */
+ priv->status &= ~STATUS_HCMD_ACTIVE;
+ wake_up_interruptible(&priv->wait_command_queue);
+ priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
+ wake_up_interruptible(&priv->wait_state);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ IPW_DEBUG_TRACE("<<\n");
+ return rc;
+}
+
+
+struct ipw_fw {
+ __le32 ver;
+ __le32 boot_size;
+ __le32 ucode_size;
+ __le32 fw_size;
+ u8 data[0];
+};
+
+static int ipw_get_fw(struct ipw_priv *priv,
+ const struct firmware **raw, const char *name)
+{
+ struct ipw_fw *fw;
+ int rc;
+
+ /* ask firmware_class module to get the boot firmware off disk */
+ rc = request_firmware(raw, name, &priv->pci_dev->dev);
+ if (rc < 0) {
+ IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc);
+ return rc;
+ }
+
+ if ((*raw)->size < sizeof(*fw)) {
+ IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size);
+ return -EINVAL;
+ }
+
+ fw = (void *)(*raw)->data;
+
+ if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) +
+ le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) {
+ IPW_ERROR("%s is too small or corrupt (%zd)\n",
+ name, (*raw)->size);
+ return -EINVAL;
+ }
+
+ IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n",
+ name,
+ le32_to_cpu(fw->ver) >> 16,
+ le32_to_cpu(fw->ver) & 0xff,
+ (*raw)->size - sizeof(*fw));
+ return 0;
+}
+
+#define IPW_RX_BUF_SIZE (3000)
+
+static void ipw_rx_queue_reset(struct ipw_priv *priv,
+ struct ipw_rx_queue *rxq)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
+ /* In the reset function, these buffers may have been allocated
+ * to an SKB, so we need to unmap and free potential storage */
+ if (rxq->pool[i].skb != NULL) {
+ pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+ IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(rxq->pool[i].skb);
+ rxq->pool[i].skb = NULL;
+ }
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+ }
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->free_count = 0;
+ spin_unlock_irqrestore(&rxq->lock, flags);
+}
+
+#ifdef CONFIG_PM
+static int fw_loaded = 0;
+static const struct firmware *raw = NULL;
+
+static void free_firmware(void)
+{
+ if (fw_loaded) {
+ release_firmware(raw);
+ raw = NULL;
+ fw_loaded = 0;
+ }
+}
+#else
+#define free_firmware() do {} while (0)
+#endif
+
+static int ipw_load(struct ipw_priv *priv)
+{
+#ifndef CONFIG_PM
+ const struct firmware *raw = NULL;
+#endif
+ struct ipw_fw *fw;
+ u8 *boot_img, *ucode_img, *fw_img;
+ u8 *name = NULL;
+ int rc = 0, retries = 3;
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ name = "ipw2200-ibss.fw";
+ break;
+#ifdef CONFIG_IPW2200_MONITOR
+ case IW_MODE_MONITOR:
+ name = "ipw2200-sniffer.fw";
+ break;
+#endif
+ case IW_MODE_INFRA:
+ name = "ipw2200-bss.fw";
+ break;
+ }
+
+ if (!name) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+#ifdef CONFIG_PM
+ if (!fw_loaded) {
+#endif
+ rc = ipw_get_fw(priv, &raw, name);
+ if (rc < 0)
+ goto error;
+#ifdef CONFIG_PM
+ }
+#endif
+
+ fw = (void *)raw->data;
+ boot_img = &fw->data[0];
+ ucode_img = &fw->data[le32_to_cpu(fw->boot_size)];
+ fw_img = &fw->data[le32_to_cpu(fw->boot_size) +
+ le32_to_cpu(fw->ucode_size)];
+
+ if (rc < 0)
+ goto error;
+
+ if (!priv->rxq)
+ priv->rxq = ipw_rx_queue_alloc(priv);
+ else
+ ipw_rx_queue_reset(priv, priv->rxq);
+ if (!priv->rxq) {
+ IPW_ERROR("Unable to initialize Rx queue\n");
+ goto error;
+ }
+
+ retry:
+ /* Ensure interrupts are disabled */
+ ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
+ priv->status &= ~STATUS_INT_ENABLED;
+
+ /* ack pending interrupts */
+ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
+
+ ipw_stop_nic(priv);
+
+ rc = ipw_reset_nic(priv);
+ if (rc < 0) {
+ IPW_ERROR("Unable to reset NIC\n");
+ goto error;
+ }
+
+ ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND,
+ IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND);
+
+ /* DMA the initial boot firmware into the device */
+ rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size));
+ if (rc < 0) {
+ IPW_ERROR("Unable to load boot firmware: %d\n", rc);
+ goto error;
+ }
+
+ /* kick start the device */
+ ipw_start_nic(priv);
+
+ /* wait for the device to finish its initial startup sequence */
+ rc = ipw_poll_bit(priv, IPW_INTA_RW,
+ IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+ if (rc < 0) {
+ IPW_ERROR("device failed to boot initial fw image\n");
+ goto error;
+ }
+ IPW_DEBUG_INFO("initial device response after %dms\n", rc);
+
+ /* ack fw init done interrupt */
+ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
+
+ /* DMA the ucode into the device */
+ rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size));
+ if (rc < 0) {
+ IPW_ERROR("Unable to load ucode: %d\n", rc);
+ goto error;
+ }
+
+ /* stop nic */
+ ipw_stop_nic(priv);
+
+ /* DMA bss firmware into the device */
+ rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size));
+ if (rc < 0) {
+ IPW_ERROR("Unable to load firmware: %d\n", rc);
+ goto error;
+ }
+#ifdef CONFIG_PM
+ fw_loaded = 1;
+#endif
+
+ ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0);
+
+ rc = ipw_queue_reset(priv);
+ if (rc < 0) {
+ IPW_ERROR("Unable to initialize queues\n");
+ goto error;
+ }
+
+ /* Ensure interrupts are disabled */
+ ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL);
+ /* ack pending interrupts */
+ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
+
+ /* kick start the device */
+ ipw_start_nic(priv);
+
+ if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) {
+ if (retries > 0) {
+ IPW_WARNING("Parity error. Retrying init.\n");
+ retries--;
+ goto retry;
+ }
+
+ IPW_ERROR("TODO: Handle parity error -- schedule restart?\n");
+ rc = -EIO;
+ goto error;
+ }
+
+ /* wait for the device */
+ rc = ipw_poll_bit(priv, IPW_INTA_RW,
+ IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500);
+ if (rc < 0) {
+ IPW_ERROR("device failed to start within 500ms\n");
+ goto error;
+ }
+ IPW_DEBUG_INFO("device response after %dms\n", rc);
+
+ /* ack fw init done interrupt */
+ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE);
+
+ /* read eeprom data and initialize the eeprom region of sram */
+ priv->eeprom_delay = 1;
+ ipw_eeprom_init_sram(priv);
+
+ /* enable interrupts */
+ ipw_enable_interrupts(priv);
+
+ /* Ensure our queue has valid packets */
+ ipw_rx_queue_replenish(priv);
+
+ ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read);
+
+ /* ack pending interrupts */
+ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL);
+
+#ifndef CONFIG_PM
+ release_firmware(raw);
+#endif
+ return 0;
+
+ error:
+ if (priv->rxq) {
+ ipw_rx_queue_free(priv, priv->rxq);
+ priv->rxq = NULL;
+ }
+ ipw_tx_queue_free(priv);
+ if (raw)
+ release_firmware(raw);
+#ifdef CONFIG_PM
+ fw_loaded = 0;
+ raw = NULL;
+#endif
+
+ return rc;
+}
+
+/**
+ * DMA services
+ *
+ * Theory of operation
+ *
+ * A queue is a circular buffers with 'Read' and 'Write' pointers.
+ * 2 empty entries always kept in the buffer to protect from overflow.
+ *
+ * For Tx queue, there are low mark and high mark limits. If, after queuing
+ * the packet for Tx, free space become < low mark, Tx queue stopped. When
+ * reclaiming packets (on 'tx done IRQ), if free space become > high mark,
+ * Tx queue resumed.
+ *
+ * The IPW operates with six queues, one receive queue in the device's
+ * sram, one transmit queue for sending commands to the device firmware,
+ * and four transmit queues for data.
+ *
+ * The four transmit queues allow for performing quality of service (qos)
+ * transmissions as per the 802.11 protocol. Currently Linux does not
+ * provide a mechanism to the user for utilizing prioritized queues, so
+ * we only utilize the first data transmit queue (queue1).
+ */
+
+/**
+ * Driver allocates buffers of this size for Rx
+ */
+
+/**
+ * ipw_rx_queue_space - Return number of free slots available in queue.
+ */
+static int ipw_rx_queue_space(const struct ipw_rx_queue *q)
+{
+ int s = q->read - q->write;
+ if (s <= 0)
+ s += RX_QUEUE_SIZE;
+ /* keep some buffer to not confuse full and empty queue */
+ s -= 2;
+ if (s < 0)
+ s = 0;
+ return s;
+}
+
+static inline int ipw_tx_queue_space(const struct clx2_queue *q)
+{
+ int s = q->last_used - q->first_empty;
+ if (s <= 0)
+ s += q->n_bd;
+ s -= 2; /* keep some reserve to not confuse empty and full situations */
+ if (s < 0)
+ s = 0;
+ return s;
+}
+
+static inline int ipw_queue_inc_wrap(int index, int n_bd)
+{
+ return (++index == n_bd) ? 0 : index;
+}
+
+/**
+ * Initialize common DMA queue structure
+ *
+ * @param q queue to init
+ * @param count Number of BD's to allocate. Should be power of 2
+ * @param read_register Address for 'read' register
+ * (not offset within BAR, full address)
+ * @param write_register Address for 'write' register
+ * (not offset within BAR, full address)
+ * @param base_register Address for 'base' register
+ * (not offset within BAR, full address)
+ * @param size Address for 'size' register
+ * (not offset within BAR, full address)
+ */
+static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q,
+ int count, u32 read, u32 write, u32 base, u32 size)
+{
+ q->n_bd = count;
+
+ q->low_mark = q->n_bd / 4;
+ if (q->low_mark < 4)
+ q->low_mark = 4;
+
+ q->high_mark = q->n_bd / 8;
+ if (q->high_mark < 2)
+ q->high_mark = 2;
+
+ q->first_empty = q->last_used = 0;
+ q->reg_r = read;
+ q->reg_w = write;
+
+ ipw_write32(priv, base, q->dma_addr);
+ ipw_write32(priv, size, count);
+ ipw_write32(priv, read, 0);
+ ipw_write32(priv, write, 0);
+
+ _ipw_read32(priv, 0x90);
+}
+
+static int ipw_queue_tx_init(struct ipw_priv *priv,
+ struct clx2_tx_queue *q,
+ int count, u32 read, u32 write, u32 base, u32 size)
+{
+ struct pci_dev *dev = priv->pci_dev;
+
+ q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL);
+ if (!q->txb) {
+ IPW_ERROR("vmalloc for auxilary BD structures failed\n");
+ return -ENOMEM;
+ }
+
+ q->bd =
+ pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr);
+ if (!q->bd) {
+ IPW_ERROR("pci_alloc_consistent(%zd) failed\n",
+ sizeof(q->bd[0]) * count);
+ kfree(q->txb);
+ q->txb = NULL;
+ return -ENOMEM;
+ }
+
+ ipw_queue_init(priv, &q->q, count, read, write, base, size);
+ return 0;
+}
+
+/**
+ * Free one TFD, those at index [txq->q.last_used].
+ * Do NOT advance any indexes
+ *
+ * @param dev
+ * @param txq
+ */
+static void ipw_queue_tx_free_tfd(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq)
+{
+ struct tfd_frame *bd = &txq->bd[txq->q.last_used];
+ struct pci_dev *dev = priv->pci_dev;
+ int i;
+
+ /* classify bd */
+ if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE)
+ /* nothing to cleanup after for host commands */
+ return;
+
+ /* sanity check */
+ if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) {
+ IPW_ERROR("Too many chunks: %i\n",
+ le32_to_cpu(bd->u.data.num_chunks));
+ /** @todo issue fatal error, it is quite serious situation */
+ return;
+ }
+
+ /* unmap chunks if any */
+ for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) {
+ pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]),
+ le16_to_cpu(bd->u.data.chunk_len[i]),
+ PCI_DMA_TODEVICE);
+ if (txq->txb[txq->q.last_used]) {
+ ieee80211_txb_free(txq->txb[txq->q.last_used]);
+ txq->txb[txq->q.last_used] = NULL;
+ }
+ }
+}
+
+/**
+ * Deallocate DMA queue.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ *
+ * @param dev
+ * @param q
+ */
+static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq)
+{
+ struct clx2_queue *q = &txq->q;
+ struct pci_dev *dev = priv->pci_dev;
+
+ if (q->n_bd == 0)
+ return;
+
+ /* first, empty all BD's */
+ for (; q->first_empty != q->last_used;
+ q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+ ipw_queue_tx_free_tfd(priv, txq);
+ }
+
+ /* free buffers belonging to queue itself */
+ pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd,
+ q->dma_addr);
+ kfree(txq->txb);
+
+ /* 0 fill whole structure */
+ memset(txq, 0, sizeof(*txq));
+}
+
+/**
+ * Destroy all DMA queues and structures
+ *
+ * @param priv
+ */
+static void ipw_tx_queue_free(struct ipw_priv *priv)
+{
+ /* Tx CMD queue */
+ ipw_queue_tx_free(priv, &priv->txq_cmd);
+
+ /* Tx queues */
+ ipw_queue_tx_free(priv, &priv->txq[0]);
+ ipw_queue_tx_free(priv, &priv->txq[1]);
+ ipw_queue_tx_free(priv, &priv->txq[2]);
+ ipw_queue_tx_free(priv, &priv->txq[3]);
+}
+
+static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid)
+{
+ /* First 3 bytes are manufacturer */
+ bssid[0] = priv->mac_addr[0];
+ bssid[1] = priv->mac_addr[1];
+ bssid[2] = priv->mac_addr[2];
+
+ /* Last bytes are random */
+ get_random_bytes(&bssid[3], ETH_ALEN - 3);
+
+ bssid[0] &= 0xfe; /* clear multicast bit */
+ bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
+
+static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid)
+{
+ struct ipw_station_entry entry;
+ int i;
+
+ for (i = 0; i < priv->num_stations; i++) {
+ if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) {
+ /* Another node is active in network */
+ priv->missed_adhoc_beacons = 0;
+ if (!(priv->config & CFG_STATIC_CHANNEL))
+ /* when other nodes drop out, we drop out */
+ priv->config &= ~CFG_ADHOC_PERSIST;
+
+ return i;
+ }
+ }
+
+ if (i == MAX_STATIONS)
+ return IPW_INVALID_STATION;
+
+ IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid);
+
+ entry.reserved = 0;
+ entry.support_mode = 0;
+ memcpy(entry.mac_addr, bssid, ETH_ALEN);
+ memcpy(priv->stations[i], bssid, ETH_ALEN);
+ ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry),
+ &entry, sizeof(entry));
+ priv->num_stations++;
+
+ return i;
+}
+
+static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid)
+{
+ int i;
+
+ for (i = 0; i < priv->num_stations; i++)
+ if (!memcmp(priv->stations[i], bssid, ETH_ALEN))
+ return i;
+
+ return IPW_INVALID_STATION;
+}
+
+static void ipw_send_disassociate(struct ipw_priv *priv, int quiet)
+{
+ int err;
+
+ if (priv->status & STATUS_ASSOCIATING) {
+ IPW_DEBUG_ASSOC("Disassociating while associating.\n");
+ queue_work(priv->workqueue, &priv->disassociate);
+ return;
+ }
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ IPW_DEBUG_ASSOC("Disassociating while not associated.\n");
+ return;
+ }
+
+ IPW_DEBUG_ASSOC("Disassocation attempt from %pM "
+ "on channel %d.\n",
+ priv->assoc_request.bssid,
+ priv->assoc_request.channel);
+
+ priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED);
+ priv->status |= STATUS_DISASSOCIATING;
+
+ if (quiet)
+ priv->assoc_request.assoc_type = HC_DISASSOC_QUIET;
+ else
+ priv->assoc_request.assoc_type = HC_DISASSOCIATE;
+
+ err = ipw_send_associate(priv, &priv->assoc_request);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send [dis]associate command "
+ "failed.\n");
+ return;
+ }
+
+}
+
+static int ipw_disassociate(void *data)
+{
+ struct ipw_priv *priv = data;
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)))
+ return 0;
+ ipw_send_disassociate(data, 0);
+ netif_carrier_off(priv->net_dev);
+ return 1;
+}
+
+static void ipw_bg_disassociate(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, disassociate);
+ mutex_lock(&priv->mutex);
+ ipw_disassociate(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_system_config(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, system_config);
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+ }
+#endif
+
+ ipw_send_system_config(priv);
+}
+
+struct ipw_status_code {
+ u16 status;
+ const char *reason;
+};
+
+static const struct ipw_status_code ipw_status_codes[] = {
+ {0x00, "Successful"},
+ {0x01, "Unspecified failure"},
+ {0x0A, "Cannot support all requested capabilities in the "
+ "Capability information field"},
+ {0x0B, "Reassociation denied due to inability to confirm that "
+ "association exists"},
+ {0x0C, "Association denied due to reason outside the scope of this "
+ "standard"},
+ {0x0D,
+ "Responding station does not support the specified authentication "
+ "algorithm"},
+ {0x0E,
+ "Received an Authentication frame with authentication sequence "
+ "transaction sequence number out of expected sequence"},
+ {0x0F, "Authentication rejected because of challenge failure"},
+ {0x10, "Authentication rejected due to timeout waiting for next "
+ "frame in sequence"},
+ {0x11, "Association denied because AP is unable to handle additional "
+ "associated stations"},
+ {0x12,
+ "Association denied due to requesting station not supporting all "
+ "of the datarates in the BSSBasicServiceSet Parameter"},
+ {0x13,
+ "Association denied due to requesting station not supporting "
+ "short preamble operation"},
+ {0x14,
+ "Association denied due to requesting station not supporting "
+ "PBCC encoding"},
+ {0x15,
+ "Association denied due to requesting station not supporting "
+ "channel agility"},
+ {0x19,
+ "Association denied due to requesting station not supporting "
+ "short slot operation"},
+ {0x1A,
+ "Association denied due to requesting station not supporting "
+ "DSSS-OFDM operation"},
+ {0x28, "Invalid Information Element"},
+ {0x29, "Group Cipher is not valid"},
+ {0x2A, "Pairwise Cipher is not valid"},
+ {0x2B, "AKMP is not valid"},
+ {0x2C, "Unsupported RSN IE version"},
+ {0x2D, "Invalid RSN IE Capabilities"},
+ {0x2E, "Cipher suite is rejected per security policy"},
+};
+
+static const char *ipw_get_status_code(u16 status)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++)
+ if (ipw_status_codes[i].status == (status & 0xff))
+ return ipw_status_codes[i].reason;
+ return "Unknown status value.";
+}
+
+static void inline average_init(struct average *avg)
+{
+ memset(avg, 0, sizeof(*avg));
+}
+
+#define DEPTH_RSSI 8
+#define DEPTH_NOISE 16
+static s16 exponential_average(s16 prev_avg, s16 val, u8 depth)
+{
+ return ((depth-1)*prev_avg + val)/depth;
+}
+
+static void average_add(struct average *avg, s16 val)
+{
+ avg->sum -= avg->entries[avg->pos];
+ avg->sum += val;
+ avg->entries[avg->pos++] = val;
+ if (unlikely(avg->pos == AVG_ENTRIES)) {
+ avg->init = 1;
+ avg->pos = 0;
+ }
+}
+
+static s16 average_value(struct average *avg)
+{
+ if (!unlikely(avg->init)) {
+ if (avg->pos)
+ return avg->sum / avg->pos;
+ return 0;
+ }
+
+ return avg->sum / AVG_ENTRIES;
+}
+
+static void ipw_reset_stats(struct ipw_priv *priv)
+{
+ u32 len = sizeof(u32);
+
+ priv->quality = 0;
+
+ average_init(&priv->average_missed_beacons);
+ priv->exp_avg_rssi = -60;
+ priv->exp_avg_noise = -85 + 0x100;
+
+ priv->last_rate = 0;
+ priv->last_missed_beacons = 0;
+ priv->last_rx_packets = 0;
+ priv->last_tx_packets = 0;
+ priv->last_tx_failures = 0;
+
+ /* Firmware managed, reset only when NIC is restarted, so we have to
+ * normalize on the current value */
+ ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC,
+ &priv->last_rx_err, &len);
+ ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE,
+ &priv->last_tx_failures, &len);
+
+ /* Driver managed, reset with each association */
+ priv->missed_adhoc_beacons = 0;
+ priv->missed_beacons = 0;
+ priv->tx_packets = 0;
+ priv->rx_packets = 0;
+
+}
+
+static u32 ipw_get_max_rate(struct ipw_priv *priv)
+{
+ u32 i = 0x80000000;
+ u32 mask = priv->rates_mask;
+ /* If currently associated in B mode, restrict the maximum
+ * rate match to B rates */
+ if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+ mask &= IEEE80211_CCK_RATES_MASK;
+
+ /* TODO: Verify that the rate is supported by the current rates
+ * list. */
+
+ while (i && !(mask & i))
+ i >>= 1;
+ switch (i) {
+ case IEEE80211_CCK_RATE_1MB_MASK:
+ return 1000000;
+ case IEEE80211_CCK_RATE_2MB_MASK:
+ return 2000000;
+ case IEEE80211_CCK_RATE_5MB_MASK:
+ return 5500000;
+ case IEEE80211_OFDM_RATE_6MB_MASK:
+ return 6000000;
+ case IEEE80211_OFDM_RATE_9MB_MASK:
+ return 9000000;
+ case IEEE80211_CCK_RATE_11MB_MASK:
+ return 11000000;
+ case IEEE80211_OFDM_RATE_12MB_MASK:
+ return 12000000;
+ case IEEE80211_OFDM_RATE_18MB_MASK:
+ return 18000000;
+ case IEEE80211_OFDM_RATE_24MB_MASK:
+ return 24000000;
+ case IEEE80211_OFDM_RATE_36MB_MASK:
+ return 36000000;
+ case IEEE80211_OFDM_RATE_48MB_MASK:
+ return 48000000;
+ case IEEE80211_OFDM_RATE_54MB_MASK:
+ return 54000000;
+ }
+
+ if (priv->ieee->mode == IEEE_B)
+ return 11000000;
+ else
+ return 54000000;
+}
+
+static u32 ipw_get_current_rate(struct ipw_priv *priv)
+{
+ u32 rate, len = sizeof(rate);
+ int err;
+
+ if (!(priv->status & STATUS_ASSOCIATED))
+ return 0;
+
+ if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) {
+ err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate,
+ &len);
+ if (err) {
+ IPW_DEBUG_INFO("failed querying ordinals.\n");
+ return 0;
+ }
+ } else
+ return ipw_get_max_rate(priv);
+
+ switch (rate) {
+ case IPW_TX_RATE_1MB:
+ return 1000000;
+ case IPW_TX_RATE_2MB:
+ return 2000000;
+ case IPW_TX_RATE_5MB:
+ return 5500000;
+ case IPW_TX_RATE_6MB:
+ return 6000000;
+ case IPW_TX_RATE_9MB:
+ return 9000000;
+ case IPW_TX_RATE_11MB:
+ return 11000000;
+ case IPW_TX_RATE_12MB:
+ return 12000000;
+ case IPW_TX_RATE_18MB:
+ return 18000000;
+ case IPW_TX_RATE_24MB:
+ return 24000000;
+ case IPW_TX_RATE_36MB:
+ return 36000000;
+ case IPW_TX_RATE_48MB:
+ return 48000000;
+ case IPW_TX_RATE_54MB:
+ return 54000000;
+ }
+
+ return 0;
+}
+
+#define IPW_STATS_INTERVAL (2 * HZ)
+static void ipw_gather_stats(struct ipw_priv *priv)
+{
+ u32 rx_err, rx_err_delta, rx_packets_delta;
+ u32 tx_failures, tx_failures_delta, tx_packets_delta;
+ u32 missed_beacons_percent, missed_beacons_delta;
+ u32 quality = 0;
+ u32 len = sizeof(u32);
+ s16 rssi;
+ u32 beacon_quality, signal_quality, tx_quality, rx_quality,
+ rate_quality;
+ u32 max_rate;
+
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ priv->quality = 0;
+ return;
+ }
+
+ /* Update the statistics */
+ ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS,
+ &priv->missed_beacons, &len);
+ missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons;
+ priv->last_missed_beacons = priv->missed_beacons;
+ if (priv->assoc_request.beacon_interval) {
+ missed_beacons_percent = missed_beacons_delta *
+ (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) /
+ (IPW_STATS_INTERVAL * 10);
+ } else {
+ missed_beacons_percent = 0;
+ }
+ average_add(&priv->average_missed_beacons, missed_beacons_percent);
+
+ ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len);
+ rx_err_delta = rx_err - priv->last_rx_err;
+ priv->last_rx_err = rx_err;
+
+ ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len);
+ tx_failures_delta = tx_failures - priv->last_tx_failures;
+ priv->last_tx_failures = tx_failures;
+
+ rx_packets_delta = priv->rx_packets - priv->last_rx_packets;
+ priv->last_rx_packets = priv->rx_packets;
+
+ tx_packets_delta = priv->tx_packets - priv->last_tx_packets;
+ priv->last_tx_packets = priv->tx_packets;
+
+ /* Calculate quality based on the following:
+ *
+ * Missed beacon: 100% = 0, 0% = 70% missed
+ * Rate: 60% = 1Mbs, 100% = Max
+ * Rx and Tx errors represent a straight % of total Rx/Tx
+ * RSSI: 100% = > -50, 0% = < -80
+ * Rx errors: 100% = 0, 0% = 50% missed
+ *
+ * The lowest computed quality is used.
+ *
+ */
+#define BEACON_THRESHOLD 5
+ beacon_quality = 100 - missed_beacons_percent;
+ if (beacon_quality < BEACON_THRESHOLD)
+ beacon_quality = 0;
+ else
+ beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 /
+ (100 - BEACON_THRESHOLD);
+ IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n",
+ beacon_quality, missed_beacons_percent);
+
+ priv->last_rate = ipw_get_current_rate(priv);
+ max_rate = ipw_get_max_rate(priv);
+ rate_quality = priv->last_rate * 40 / max_rate + 60;
+ IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n",
+ rate_quality, priv->last_rate / 1000000);
+
+ if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta)
+ rx_quality = 100 - (rx_err_delta * 100) /
+ (rx_packets_delta + rx_err_delta);
+ else
+ rx_quality = 100;
+ IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n",
+ rx_quality, rx_err_delta, rx_packets_delta);
+
+ if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta)
+ tx_quality = 100 - (tx_failures_delta * 100) /
+ (tx_packets_delta + tx_failures_delta);
+ else
+ tx_quality = 100;
+ IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n",
+ tx_quality, tx_failures_delta, tx_packets_delta);
+
+ rssi = priv->exp_avg_rssi;
+ signal_quality =
+ (100 *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
+ (priv->ieee->perfect_rssi - rssi) *
+ (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
+ 62 * (priv->ieee->perfect_rssi - rssi))) /
+ ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi));
+ if (signal_quality > 100)
+ signal_quality = 100;
+ else if (signal_quality < 1)
+ signal_quality = 0;
+
+ IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n",
+ signal_quality, rssi);
+
+ quality = min(beacon_quality,
+ min(rate_quality,
+ min(tx_quality, min(rx_quality, signal_quality))));
+ if (quality == beacon_quality)
+ IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n",
+ quality);
+ if (quality == rate_quality)
+ IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n",
+ quality);
+ if (quality == tx_quality)
+ IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n",
+ quality);
+ if (quality == rx_quality)
+ IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n",
+ quality);
+ if (quality == signal_quality)
+ IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n",
+ quality);
+
+ priv->quality = quality;
+
+ queue_delayed_work(priv->workqueue, &priv->gather_stats,
+ IPW_STATS_INTERVAL);
+}
+
+static void ipw_bg_gather_stats(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, gather_stats.work);
+ mutex_lock(&priv->mutex);
+ ipw_gather_stats(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+/* Missed beacon behavior:
+ * 1st missed -> roaming_threshold, just wait, don't do any scan/roam.
+ * roaming_threshold -> disassociate_threshold, scan and roam for better signal.
+ * Above disassociate threshold, give up and stop scanning.
+ * Roaming is disabled if disassociate_threshold <= roaming_threshold */
+static void ipw_handle_missed_beacon(struct ipw_priv *priv,
+ int missed_count)
+{
+ priv->notif_missed_beacons = missed_count;
+
+ if (missed_count > priv->disassociate_threshold &&
+ priv->status & STATUS_ASSOCIATED) {
+ /* If associated and we've hit the missed
+ * beacon threshold, disassociate, turn
+ * off roaming, and abort any active scans */
+ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
+ IPW_DL_STATE | IPW_DL_ASSOC,
+ "Missed beacon: %d - disassociate\n", missed_count);
+ priv->status &= ~STATUS_ROAMING;
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
+ IPW_DL_STATE,
+ "Aborting scan with missed beacon.\n");
+ queue_work(priv->workqueue, &priv->abort_scan);
+ }
+
+ queue_work(priv->workqueue, &priv->disassociate);
+ return;
+ }
+
+ if (priv->status & STATUS_ROAMING) {
+ /* If we are currently roaming, then just
+ * print a debug statement... */
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "Missed beacon: %d - roam in progress\n",
+ missed_count);
+ return;
+ }
+
+ if (roaming &&
+ (missed_count > priv->roaming_threshold &&
+ missed_count <= priv->disassociate_threshold)) {
+ /* If we are not already roaming, set the ROAM
+ * bit in the status and kick off a scan.
+ * This can happen several times before we reach
+ * disassociate_threshold. */
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "Missed beacon: %d - initiate "
+ "roaming\n", missed_count);
+ if (!(priv->status & STATUS_ROAMING)) {
+ priv->status |= STATUS_ROAMING;
+ if (!(priv->status & STATUS_SCANNING))
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan, 0);
+ }
+ return;
+ }
+
+ if (priv->status & STATUS_SCANNING &&
+ missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) {
+ /* Stop scan to keep fw from getting
+ * stuck (only if we aren't roaming --
+ * otherwise we'll never scan more than 2 or 3
+ * channels..) */
+ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE,
+ "Aborting scan with missed beacon.\n");
+ queue_work(priv->workqueue, &priv->abort_scan);
+ }
+
+ IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count);
+}
+
+static void ipw_scan_event(struct work_struct *work)
+{
+ union iwreq_data wrqu;
+
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, scan_event.work);
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static void handle_scan_event(struct ipw_priv *priv)
+{
+ /* Only userspace-requested scan completion events go out immediately */
+ if (!priv->user_requested_scan) {
+ if (!delayed_work_pending(&priv->scan_event))
+ queue_delayed_work(priv->workqueue, &priv->scan_event,
+ round_jiffies_relative(msecs_to_jiffies(4000)));
+ } else {
+ union iwreq_data wrqu;
+
+ priv->user_requested_scan = 0;
+ cancel_delayed_work(&priv->scan_event);
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL);
+ }
+}
+
+/**
+ * Handle host notification packet.
+ * Called from interrupt routine
+ */
+static void ipw_rx_notification(struct ipw_priv *priv,
+ struct ipw_rx_notification *notif)
+{
+ DECLARE_SSID_BUF(ssid);
+ u16 size = le16_to_cpu(notif->size);
+ notif->size = le16_to_cpu(notif->size);
+
+ IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size);
+
+ switch (notif->subtype) {
+ case HOST_NOTIFICATION_STATUS_ASSOCIATED:{
+ struct notif_association *assoc = &notif->u.assoc;
+
+ switch (assoc->state) {
+ case CMAS_ASSOCIATED:{
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "associated: '%s' %pM \n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len),
+ priv->bssid);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_INFRA:
+ memcpy(priv->ieee->bssid,
+ priv->bssid, ETH_ALEN);
+ break;
+
+ case IW_MODE_ADHOC:
+ memcpy(priv->ieee->bssid,
+ priv->bssid, ETH_ALEN);
+
+ /* clear out the station table */
+ priv->num_stations = 0;
+
+ IPW_DEBUG_ASSOC
+ ("queueing adhoc check\n");
+ queue_delayed_work(priv->
+ workqueue,
+ &priv->
+ adhoc_check,
+ le16_to_cpu(priv->
+ assoc_request.
+ beacon_interval));
+ break;
+ }
+
+ priv->status &= ~STATUS_ASSOCIATING;
+ priv->status |= STATUS_ASSOCIATED;
+ queue_work(priv->workqueue,
+ &priv->system_config);
+
+#ifdef CONFIG_IPW2200_QOS
+#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
+ le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control))
+ if ((priv->status & STATUS_AUTH) &&
+ (IPW_GET_PACKET_STYPE(&notif->u.raw)
+ == IEEE80211_STYPE_ASSOC_RESP)) {
+ if ((sizeof
+ (struct
+ ieee80211_assoc_response)
+ <= size)
+ && (size <= 2314)) {
+ struct
+ ieee80211_rx_stats
+ stats = {
+ .len = size - 1,
+ };
+
+ IPW_DEBUG_QOS
+ ("QoS Associate "
+ "size %d\n", size);
+ ieee80211_rx_mgt(priv->
+ ieee,
+ (struct
+ ieee80211_hdr_4addr
+ *)
+ &notif->u.raw, &stats);
+ }
+ }
+#endif
+
+ schedule_work(&priv->link_up);
+
+ break;
+ }
+
+ case CMAS_AUTHENTICATED:{
+ if (priv->
+ status & (STATUS_ASSOCIATED |
+ STATUS_AUTH)) {
+ struct notif_authenticate *auth
+ = &notif->u.auth;
+ IPW_DEBUG(IPW_DL_NOTIF |
+ IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "deauthenticated: '%s' "
+ "%pM"
+ ": (0x%04X) - %s \n",
+ print_ssid(ssid,
+ priv->
+ essid,
+ priv->
+ essid_len),
+ priv->bssid,
+ le16_to_cpu(auth->status),
+ ipw_get_status_code
+ (le16_to_cpu
+ (auth->status)));
+
+ priv->status &=
+ ~(STATUS_ASSOCIATING |
+ STATUS_AUTH |
+ STATUS_ASSOCIATED);
+
+ schedule_work(&priv->link_down);
+ break;
+ }
+
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "authenticated: '%s' %pM\n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len),
+ priv->bssid);
+ break;
+ }
+
+ case CMAS_INIT:{
+ if (priv->status & STATUS_AUTH) {
+ struct
+ ieee80211_assoc_response
+ *resp;
+ resp =
+ (struct
+ ieee80211_assoc_response
+ *)&notif->u.raw;
+ IPW_DEBUG(IPW_DL_NOTIF |
+ IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "association failed (0x%04X): %s\n",
+ le16_to_cpu(resp->status),
+ ipw_get_status_code
+ (le16_to_cpu
+ (resp->status)));
+ }
+
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "disassociated: '%s' %pM \n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len),
+ priv->bssid);
+
+ priv->status &=
+ ~(STATUS_DISASSOCIATING |
+ STATUS_ASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_AUTH);
+ if (priv->assoc_network
+ && (priv->assoc_network->
+ capability &
+ WLAN_CAPABILITY_IBSS))
+ ipw_remove_current_network
+ (priv);
+
+ schedule_work(&priv->link_down);
+
+ break;
+ }
+
+ case CMAS_RX_ASSOC_RESP:
+ break;
+
+ default:
+ IPW_ERROR("assoc: unknown (%d)\n",
+ assoc->state);
+ break;
+ }
+
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{
+ struct notif_authenticate *auth = &notif->u.auth;
+ switch (auth->state) {
+ case CMAS_AUTHENTICATED:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "authenticated: '%s' %pM \n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len),
+ priv->bssid);
+ priv->status |= STATUS_AUTH;
+ break;
+
+ case CMAS_INIT:
+ if (priv->status & STATUS_AUTH) {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "authentication failed (0x%04X): %s\n",
+ le16_to_cpu(auth->status),
+ ipw_get_status_code(le16_to_cpu
+ (auth->
+ status)));
+ }
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC,
+ "deauthenticated: '%s' %pM\n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len),
+ priv->bssid);
+
+ priv->status &= ~(STATUS_ASSOCIATING |
+ STATUS_AUTH |
+ STATUS_ASSOCIATED);
+
+ schedule_work(&priv->link_down);
+ break;
+
+ case CMAS_TX_AUTH_SEQ_1:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_1\n");
+ break;
+ case CMAS_RX_AUTH_SEQ_2:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_2\n");
+ break;
+ case CMAS_AUTH_SEQ_1_PASS:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n");
+ break;
+ case CMAS_AUTH_SEQ_1_FAIL:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n");
+ break;
+ case CMAS_TX_AUTH_SEQ_3:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_3\n");
+ break;
+ case CMAS_RX_AUTH_SEQ_4:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n");
+ break;
+ case CMAS_AUTH_SEQ_2_PASS:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n");
+ break;
+ case CMAS_AUTH_SEQ_2_FAIL:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n");
+ break;
+ case CMAS_TX_ASSOC:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "TX_ASSOC\n");
+ break;
+ case CMAS_RX_ASSOC_RESP:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "RX_ASSOC_RESP\n");
+
+ break;
+ case CMAS_ASSOCIATED:
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE |
+ IPW_DL_ASSOC, "ASSOCIATED\n");
+ break;
+ default:
+ IPW_DEBUG_NOTIF("auth: failure - %d\n",
+ auth->state);
+ break;
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{
+ struct notif_channel_result *x =
+ &notif->u.channel_result;
+
+ if (size == sizeof(*x)) {
+ IPW_DEBUG_SCAN("Scan result for channel %d\n",
+ x->channel_num);
+ } else {
+ IPW_DEBUG_SCAN("Scan result of wrong size %d "
+ "(should be %zd)\n",
+ size, sizeof(*x));
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{
+ struct notif_scan_complete *x = &notif->u.scan_complete;
+ if (size == sizeof(*x)) {
+ IPW_DEBUG_SCAN
+ ("Scan completed: type %d, %d channels, "
+ "%d status\n", x->scan_type,
+ x->num_channels, x->status);
+ } else {
+ IPW_ERROR("Scan completed of wrong size %d "
+ "(should be %zd)\n",
+ size, sizeof(*x));
+ }
+
+ priv->status &=
+ ~(STATUS_SCANNING | STATUS_SCAN_ABORTING);
+
+ wake_up_interruptible(&priv->wait_state);
+ cancel_delayed_work(&priv->scan_check);
+
+ if (priv->status & STATUS_EXIT_PENDING)
+ break;
+
+ priv->ieee->scans++;
+
+#ifdef CONFIG_IPW2200_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ priv->status |= STATUS_SCAN_FORCED;
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan, 0);
+ break;
+ }
+ priv->status &= ~STATUS_SCAN_FORCED;
+#endif /* CONFIG_IPW2200_MONITOR */
+
+ /* Do queued direct scans first */
+ if (priv->status & STATUS_DIRECT_SCAN_PENDING) {
+ queue_delayed_work(priv->workqueue,
+ &priv->request_direct_scan, 0);
+ }
+
+ if (!(priv->status & (STATUS_ASSOCIATED |
+ STATUS_ASSOCIATING |
+ STATUS_ROAMING |
+ STATUS_DISASSOCIATING)))
+ queue_work(priv->workqueue, &priv->associate);
+ else if (priv->status & STATUS_ROAMING) {
+ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
+ /* If a scan completed and we are in roam mode, then
+ * the scan that completed was the one requested as a
+ * result of entering roam... so, schedule the
+ * roam work */
+ queue_work(priv->workqueue,
+ &priv->roam);
+ else
+ /* Don't schedule if we aborted the scan */
+ priv->status &= ~STATUS_ROAMING;
+ } else if (priv->status & STATUS_SCAN_PENDING)
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan, 0);
+ else if (priv->config & CFG_BACKGROUND_SCAN
+ && priv->status & STATUS_ASSOCIATED)
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan,
+ round_jiffies_relative(HZ));
+
+ /* Send an empty event to user space.
+ * We don't send the received data on the event because
+ * it would require us to do complex transcoding, and
+ * we want to minimise the work done in the irq handler
+ * Use a request to extract the data.
+ * Also, we generate this even for any scan, regardless
+ * on how the scan was initiated. User space can just
+ * sync on periodic scan to get fresh data...
+ * Jean II */
+ if (x->status == SCAN_COMPLETED_STATUS_COMPLETE)
+ handle_scan_event(priv);
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{
+ struct notif_frag_length *x = &notif->u.frag_len;
+
+ if (size == sizeof(*x))
+ IPW_ERROR("Frag length: %d\n",
+ le16_to_cpu(x->frag_length));
+ else
+ IPW_ERROR("Frag length of wrong size %d "
+ "(should be %zd)\n",
+ size, sizeof(*x));
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{
+ struct notif_link_deterioration *x =
+ &notif->u.link_deterioration;
+
+ if (size == sizeof(*x)) {
+ IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE,
+ "link deterioration: type %d, cnt %d\n",
+ x->silence_notification_type,
+ x->silence_count);
+ memcpy(&priv->last_link_deterioration, x,
+ sizeof(*x));
+ } else {
+ IPW_ERROR("Link Deterioration of wrong size %d "
+ "(should be %zd)\n",
+ size, sizeof(*x));
+ }
+ break;
+ }
+
+ case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{
+ IPW_ERROR("Dino config\n");
+ if (priv->hcmd
+ && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG)
+ IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n");
+
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_BEACON_STATE:{
+ struct notif_beacon_state *x = &notif->u.beacon_state;
+ if (size != sizeof(*x)) {
+ IPW_ERROR
+ ("Beacon state of wrong size %d (should "
+ "be %zd)\n", size, sizeof(*x));
+ break;
+ }
+
+ if (le32_to_cpu(x->state) ==
+ HOST_NOTIFICATION_STATUS_BEACON_MISSING)
+ ipw_handle_missed_beacon(priv,
+ le32_to_cpu(x->
+ number));
+
+ break;
+ }
+
+ case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{
+ struct notif_tgi_tx_key *x = &notif->u.tgi_tx_key;
+ if (size == sizeof(*x)) {
+ IPW_ERROR("TGi Tx Key: state 0x%02x sec type "
+ "0x%02x station %d\n",
+ x->key_state, x->security_type,
+ x->station_index);
+ break;
+ }
+
+ IPW_ERROR
+ ("TGi Tx Key of wrong size %d (should be %zd)\n",
+ size, sizeof(*x));
+ break;
+ }
+
+ case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{
+ struct notif_calibration *x = &notif->u.calibration;
+
+ if (size == sizeof(*x)) {
+ memcpy(&priv->calib, x, sizeof(*x));
+ IPW_DEBUG_INFO("TODO: Calibration\n");
+ break;
+ }
+
+ IPW_ERROR
+ ("Calibration of wrong size %d (should be %zd)\n",
+ size, sizeof(*x));
+ break;
+ }
+
+ case HOST_NOTIFICATION_NOISE_STATS:{
+ if (size == sizeof(u32)) {
+ priv->exp_avg_noise =
+ exponential_average(priv->exp_avg_noise,
+ (u8) (le32_to_cpu(notif->u.noise.value) & 0xff),
+ DEPTH_NOISE);
+ break;
+ }
+
+ IPW_ERROR
+ ("Noise stat is wrong size %d (should be %zd)\n",
+ size, sizeof(u32));
+ break;
+ }
+
+ default:
+ IPW_DEBUG_NOTIF("Unknown notification: "
+ "subtype=%d,flags=0x%2x,size=%d\n",
+ notif->subtype, notif->flags, size);
+ }
+}
+
+/**
+ * Destroys all DMA structures and initialise them again
+ *
+ * @param priv
+ * @return error code
+ */
+static int ipw_queue_reset(struct ipw_priv *priv)
+{
+ int rc = 0;
+ /** @todo customize queue sizes */
+ int nTx = 64, nTxCmd = 8;
+ ipw_tx_queue_free(priv);
+ /* Tx CMD queue */
+ rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd,
+ IPW_TX_CMD_QUEUE_READ_INDEX,
+ IPW_TX_CMD_QUEUE_WRITE_INDEX,
+ IPW_TX_CMD_QUEUE_BD_BASE,
+ IPW_TX_CMD_QUEUE_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx Cmd queue init failed\n");
+ goto error;
+ }
+ /* Tx queue(s) */
+ rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx,
+ IPW_TX_QUEUE_0_READ_INDEX,
+ IPW_TX_QUEUE_0_WRITE_INDEX,
+ IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 0 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx,
+ IPW_TX_QUEUE_1_READ_INDEX,
+ IPW_TX_QUEUE_1_WRITE_INDEX,
+ IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 1 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx,
+ IPW_TX_QUEUE_2_READ_INDEX,
+ IPW_TX_QUEUE_2_WRITE_INDEX,
+ IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 2 queue init failed\n");
+ goto error;
+ }
+ rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx,
+ IPW_TX_QUEUE_3_READ_INDEX,
+ IPW_TX_QUEUE_3_WRITE_INDEX,
+ IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE);
+ if (rc) {
+ IPW_ERROR("Tx 3 queue init failed\n");
+ goto error;
+ }
+ /* statistics */
+ priv->rx_bufs_min = 0;
+ priv->rx_pend_max = 0;
+ return rc;
+
+ error:
+ ipw_tx_queue_free(priv);
+ return rc;
+}
+
+/**
+ * Reclaim Tx queue entries no more used by NIC.
+ *
+ * When FW advances 'R' index, all entries between old and
+ * new 'R' index need to be reclaimed. As result, some free space
+ * forms. If there is enough free space (> low mark), wake Tx queue.
+ *
+ * @note Need to protect against garbage in 'R' index
+ * @param priv
+ * @param txq
+ * @param qindex
+ * @return Number of used entries remains in the queue
+ */
+static int ipw_queue_tx_reclaim(struct ipw_priv *priv,
+ struct clx2_tx_queue *txq, int qindex)
+{
+ u32 hw_tail;
+ int used;
+ struct clx2_queue *q = &txq->q;
+
+ hw_tail = ipw_read32(priv, q->reg_r);
+ if (hw_tail >= q->n_bd) {
+ IPW_ERROR
+ ("Read index for DMA queue (%d) is out of range [0-%d)\n",
+ hw_tail, q->n_bd);
+ goto done;
+ }
+ for (; q->last_used != hw_tail;
+ q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) {
+ ipw_queue_tx_free_tfd(priv, txq);
+ priv->tx_packets++;
+ }
+ done:
+ if ((ipw_tx_queue_space(q) > q->low_mark) &&
+ (qindex >= 0))
+ netif_wake_queue(priv->net_dev);
+ used = q->first_empty - q->last_used;
+ if (used < 0)
+ used += q->n_bd;
+
+ return used;
+}
+
+static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf,
+ int len, int sync)
+{
+ struct clx2_tx_queue *txq = &priv->txq_cmd;
+ struct clx2_queue *q = &txq->q;
+ struct tfd_frame *tfd;
+
+ if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) {
+ IPW_ERROR("No space for Tx\n");
+ return -EBUSY;
+ }
+
+ tfd = &txq->bd[q->first_empty];
+ txq->txb[q->first_empty] = NULL;
+
+ memset(tfd, 0, sizeof(*tfd));
+ tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE;
+ tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+ priv->hcmd_seq++;
+ tfd->u.cmd.index = hcmd;
+ tfd->u.cmd.length = len;
+ memcpy(tfd->u.cmd.payload, buf, len);
+ q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+ ipw_write32(priv, q->reg_w, q->first_empty);
+ _ipw_read32(priv, 0x90);
+
+ return 0;
+}
+
+/*
+ * Rx theory of operation
+ *
+ * The host allocates 32 DMA target addresses and passes the host address
+ * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is
+ * 0 to 31
+ *
+ * Rx Queue Indexes
+ * The host/firmware share two index registers for managing the Rx buffers.
+ *
+ * The READ index maps to the first position that the firmware may be writing
+ * to -- the driver can read up to (but not including) this position and get
+ * good data.
+ * The READ index is managed by the firmware once the card is enabled.
+ *
+ * The WRITE index maps to the last position the driver has read from -- the
+ * position preceding WRITE is the last slot the firmware can place a packet.
+ *
+ * The queue is empty (no good data) if WRITE = READ - 1, and is full if
+ * WRITE = READ.
+ *
+ * During initialization the host sets up the READ queue position to the first
+ * INDEX position, and WRITE to the last (READ - 1 wrapped)
+ *
+ * When the firmware places a packet in a buffer it will advance the READ index
+ * and fire the RX interrupt. The driver can then query the READ index and
+ * process as many packets as possible, moving the WRITE index forward as it
+ * resets the Rx queue buffers with new memory.
+ *
+ * The management in the driver is as follows:
+ * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When
+ * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled
+ * to replensish the ipw->rxq->rx_free.
+ * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the
+ * ipw->rxq is replenished and the READ INDEX is updated (updating the
+ * 'processed' and 'read' driver indexes as well)
+ * + A received packet is processed and handed to the kernel network stack,
+ * detached from the ipw->rxq. The driver 'processed' index is updated.
+ * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free
+ * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ
+ * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there
+ * were enough free buffers and RX_STALLED is set it is cleared.
+ *
+ *
+ * Driver sequence:
+ *
+ * ipw_rx_queue_alloc() Allocates rx_free
+ * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls
+ * ipw_rx_queue_restock
+ * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx
+ * queue, updates firmware pointers, and updates
+ * the WRITE index. If insufficient rx_free buffers
+ * are available, schedules ipw_rx_queue_replenish
+ *
+ * -- enable interrupts --
+ * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the
+ * READ INDEX, detaching the SKB from the pool.
+ * Moves the packet buffer from queue to rx_used.
+ * Calls ipw_rx_queue_restock to refill any empty
+ * slots.
+ * ...
+ *
+ */
+
+/*
+ * If there are slots in the RX queue that need to be restocked,
+ * and we have free pre-allocated buffers, fill the ranks as much
+ * as we can pulling from rx_free.
+ *
+ * This moves the 'write' index forward to catch up with 'processed', and
+ * also updates the memory address in the firmware to reference the new
+ * target buffer.
+ */
+static void ipw_rx_queue_restock(struct ipw_priv *priv)
+{
+ struct ipw_rx_queue *rxq = priv->rxq;
+ struct list_head *element;
+ struct ipw_rx_mem_buffer *rxb;
+ unsigned long flags;
+ int write;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ write = rxq->write;
+ while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) {
+ element = rxq->rx_free.next;
+ rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+ list_del(element);
+
+ ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE,
+ rxb->dma_addr);
+ rxq->queue[rxq->write] = rxb;
+ rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE;
+ rxq->free_count--;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ /* If the pre-allocated buffer pool is dropping low, schedule to
+ * refill it */
+ if (rxq->free_count <= RX_LOW_WATERMARK)
+ queue_work(priv->workqueue, &priv->rx_replenish);
+
+ /* If we've added more space for the firmware to place data, tell it */
+ if (write != rxq->write)
+ ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write);
+}
+
+/*
+ * Move all used packet from rx_used to rx_free, allocating a new SKB for each.
+ * Also restock the Rx queue via ipw_rx_queue_restock.
+ *
+ * This is called as a scheduled work item (except for during intialization)
+ */
+static void ipw_rx_queue_replenish(void *data)
+{
+ struct ipw_priv *priv = data;
+ struct ipw_rx_queue *rxq = priv->rxq;
+ struct list_head *element;
+ struct ipw_rx_mem_buffer *rxb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rxq->lock, flags);
+ while (!list_empty(&rxq->rx_used)) {
+ element = rxq->rx_used.next;
+ rxb = list_entry(element, struct ipw_rx_mem_buffer, list);
+ rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC);
+ if (!rxb->skb) {
+ printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n",
+ priv->net_dev->name);
+ /* We don't reschedule replenish work here -- we will
+ * call the restock method and if it still needs
+ * more buffers it will schedule replenish */
+ break;
+ }
+ list_del(element);
+
+ rxb->dma_addr =
+ pci_map_single(priv->pci_dev, rxb->skb->data,
+ IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ }
+ spin_unlock_irqrestore(&rxq->lock, flags);
+
+ ipw_rx_queue_restock(priv);
+}
+
+static void ipw_bg_rx_queue_replenish(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, rx_replenish);
+ mutex_lock(&priv->mutex);
+ ipw_rx_queue_replenish(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
+ * If an SKB has been detached, the POOL needs to have its SKB set to NULL
+ * This free routine walks the list of POOL entries and if SKB is set to
+ * non NULL it is unmapped and freed
+ */
+static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq)
+{
+ int i;
+
+ if (!rxq)
+ return;
+
+ for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
+ if (rxq->pool[i].skb != NULL) {
+ pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr,
+ IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(rxq->pool[i].skb);
+ }
+ }
+
+ kfree(rxq);
+}
+
+static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv)
+{
+ struct ipw_rx_queue *rxq;
+ int i;
+
+ rxq = kzalloc(sizeof(*rxq), GFP_KERNEL);
+ if (unlikely(!rxq)) {
+ IPW_ERROR("memory allocation failed\n");
+ return NULL;
+ }
+ spin_lock_init(&rxq->lock);
+ INIT_LIST_HEAD(&rxq->rx_free);
+ INIT_LIST_HEAD(&rxq->rx_used);
+
+ /* Fill the rx_used queue with _all_ of the Rx buffers */
+ for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
+ list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
+
+ /* Set us so that we have processed and used all buffers, but have
+ * not restocked the Rx queue with fresh buffers */
+ rxq->read = rxq->write = 0;
+ rxq->free_count = 0;
+
+ return rxq;
+}
+
+static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate)
+{
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (ieee_mode == IEEE_A) {
+ switch (rate) {
+ case IEEE80211_OFDM_RATE_6MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_9MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ?
+ 1 : 0;
+ case IEEE80211_OFDM_RATE_12MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_18MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_24MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_36MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_48MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_54MB:
+ return priv->
+ rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
+ default:
+ return 0;
+ }
+ }
+
+ /* B and G mixed */
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_2MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_5MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0;
+ case IEEE80211_CCK_RATE_11MB:
+ return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0;
+ }
+
+ /* If we are limited to B modulations, bail at this point */
+ if (ieee_mode == IEEE_B)
+ return 0;
+
+ /* G */
+ switch (rate) {
+ case IEEE80211_OFDM_RATE_6MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_9MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_12MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_18MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_24MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_36MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_48MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0;
+ case IEEE80211_OFDM_RATE_54MB:
+ return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0;
+ }
+
+ return 0;
+}
+
+static int ipw_compatible_rates(struct ipw_priv *priv,
+ const struct ieee80211_network *network,
+ struct ipw_supported_rates *rates)
+{
+ int num_rates, i;
+
+ memset(rates, 0, sizeof(*rates));
+ num_rates = min(network->rates_len, (u8) IPW_MAX_RATES);
+ rates->num_rates = 0;
+ for (i = 0; i < num_rates; i++) {
+ if (!ipw_is_rate_in_mask(priv, network->mode,
+ network->rates[i])) {
+
+ if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) {
+ IPW_DEBUG_SCAN("Adding masked mandatory "
+ "rate %02X\n",
+ network->rates[i]);
+ rates->supported_rates[rates->num_rates++] =
+ network->rates[i];
+ continue;
+ }
+
+ IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+ network->rates[i], priv->rates_mask);
+ continue;
+ }
+
+ rates->supported_rates[rates->num_rates++] = network->rates[i];
+ }
+
+ num_rates = min(network->rates_ex_len,
+ (u8) (IPW_MAX_RATES - num_rates));
+ for (i = 0; i < num_rates; i++) {
+ if (!ipw_is_rate_in_mask(priv, network->mode,
+ network->rates_ex[i])) {
+ if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) {
+ IPW_DEBUG_SCAN("Adding masked mandatory "
+ "rate %02X\n",
+ network->rates_ex[i]);
+ rates->supported_rates[rates->num_rates++] =
+ network->rates[i];
+ continue;
+ }
+
+ IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n",
+ network->rates_ex[i], priv->rates_mask);
+ continue;
+ }
+
+ rates->supported_rates[rates->num_rates++] =
+ network->rates_ex[i];
+ }
+
+ return 1;
+}
+
+static void ipw_copy_rates(struct ipw_supported_rates *dest,
+ const struct ipw_supported_rates *src)
+{
+ u8 i;
+ for (i = 0; i < src->num_rates; i++)
+ dest->supported_rates[i] = src->supported_rates[i];
+ dest->num_rates = src->num_rates;
+}
+
+/* TODO: Look at sniffed packets in the air to determine if the basic rate
+ * mask should ever be used -- right now all callers to add the scan rates are
+ * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */
+static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates,
+ u8 modulation, u32 rate_mask)
+{
+ u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+ IEEE80211_BASIC_RATE_MASK : 0;
+
+ if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_CCK_RATE_5MB;
+
+ if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_CCK_RATE_11MB;
+}
+
+static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates,
+ u8 modulation, u32 rate_mask)
+{
+ u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ?
+ IEEE80211_BASIC_RATE_MASK : 0;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_6MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_9MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_12MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_18MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK)
+ rates->supported_rates[rates->num_rates++] = basic_mask |
+ IEEE80211_OFDM_RATE_24MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_36MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_48MB;
+
+ if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK)
+ rates->supported_rates[rates->num_rates++] =
+ IEEE80211_OFDM_RATE_54MB;
+}
+
+struct ipw_network_match {
+ struct ieee80211_network *network;
+ struct ipw_supported_rates rates;
+};
+
+static int ipw_find_adhoc_network(struct ipw_priv *priv,
+ struct ipw_network_match *match,
+ struct ieee80211_network *network,
+ int roaming)
+{
+ struct ipw_supported_rates rates;
+ DECLARE_SSID_BUF(ssid);
+
+ /* Verify that this network's capability is compatible with the
+ * current mode (AdHoc or Infrastructure) */
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ !(network->capability & WLAN_CAPABILITY_IBSS))) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded due to "
+ "capability mismatch.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ if (unlikely(roaming)) {
+ /* If we are roaming, then ensure check if this is a valid
+ * network to try and roam to */
+ if ((network->ssid_len != match->network->ssid_len) ||
+ memcmp(network->ssid, match->network->ssid,
+ network->ssid_len)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of non-network ESSID.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+ } else {
+ /* If an ESSID has been configured then compare the broadcast
+ * ESSID to ours */
+ if ((priv->config & CFG_STATIC_ESSID) &&
+ ((network->ssid_len != priv->essid_len) ||
+ memcmp(network->ssid, priv->essid,
+ min(network->ssid_len, priv->essid_len)))) {
+ char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+
+ strncpy(escaped,
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ sizeof(escaped));
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of ESSID mismatch: '%s'.\n",
+ escaped, network->bssid,
+ print_ssid(ssid, priv->essid,
+ priv->essid_len));
+ return 0;
+ }
+ }
+
+ /* If the old network rate is better than this one, don't bother
+ * testing everything else. */
+
+ if (network->time_stamp[0] < match->network->time_stamp[0]) {
+ IPW_DEBUG_MERGE("Network '%s excluded because newer than "
+ "current network.\n",
+ print_ssid(ssid, match->network->ssid,
+ match->network->ssid_len));
+ return 0;
+ } else if (network->time_stamp[1] < match->network->time_stamp[1]) {
+ IPW_DEBUG_MERGE("Network '%s excluded because newer than "
+ "current network.\n",
+ print_ssid(ssid, match->network->ssid,
+ match->network->ssid_len));
+ return 0;
+ }
+
+ /* Now go through and see if the requested network is valid... */
+ if (priv->ieee->scan_age != 0 &&
+ time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of age: %ums.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ jiffies_to_msecs(jiffies -
+ network->last_scanned));
+ return 0;
+ }
+
+ if ((priv->config & CFG_STATIC_CHANNEL) &&
+ (network->channel != priv->channel)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of channel mismatch: %d != %d.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ network->channel, priv->channel);
+ return 0;
+ }
+
+ /* Verify privacy compatability */
+ if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
+ ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of privacy mismatch: %s != %s.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ priv->
+ capability & CAP_PRIVACY_ON ? "on" : "off",
+ network->
+ capability & WLAN_CAPABILITY_PRIVACY ? "on" :
+ "off");
+ return 0;
+ }
+
+ if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of the same BSSID match: %pM"
+ ".\n", print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ priv->bssid);
+ return 0;
+ }
+
+ /* Filter out any incompatible freq / mode combinations */
+ if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of invalid frequency/mode "
+ "combination.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ /* Ensure that the rates supported by the driver are compatible with
+ * this AP, including verification of basic rates (mandatory) */
+ if (!ipw_compatible_rates(priv, network, &rates)) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because configured rate mask excludes "
+ "AP mandatory rate.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ if (rates.num_rates == 0) {
+ IPW_DEBUG_MERGE("Network '%s (%pM)' excluded "
+ "because of no compatible rates.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ /* TODO: Perform any further minimal comparititive tests. We do not
+ * want to put too much policy logic here; intelligent scan selection
+ * should occur within a generic IEEE 802.11 user space tool. */
+
+ /* Set up 'new' AP to this network */
+ ipw_copy_rates(&match->rates, &rates);
+ match->network = network;
+ IPW_DEBUG_MERGE("Network '%s (%pM)' is a viable match.\n",
+ print_ssid(ssid, network->ssid, network->ssid_len),
+ network->bssid);
+
+ return 1;
+}
+
+static void ipw_merge_adhoc_network(struct work_struct *work)
+{
+ DECLARE_SSID_BUF(ssid);
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, merge_networks);
+ struct ieee80211_network *network = NULL;
+ struct ipw_network_match match = {
+ .network = priv->assoc_network
+ };
+
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ (priv->ieee->iw_mode == IW_MODE_ADHOC)) {
+ /* First pass through ROAM process -- look for a better
+ * network */
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ list_for_each_entry(network, &priv->ieee->network_list, list) {
+ if (network != priv->assoc_network)
+ ipw_find_adhoc_network(priv, &match, network,
+ 1);
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+
+ if (match.network == priv->assoc_network) {
+ IPW_DEBUG_MERGE("No better ADHOC in this network to "
+ "merge to.\n");
+ return;
+ }
+
+ mutex_lock(&priv->mutex);
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) {
+ IPW_DEBUG_MERGE("remove network %s\n",
+ print_ssid(ssid, priv->essid,
+ priv->essid_len));
+ ipw_remove_current_network(priv);
+ }
+
+ ipw_disassociate(priv);
+ priv->assoc_network = match.network;
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+}
+
+static int ipw_best_network(struct ipw_priv *priv,
+ struct ipw_network_match *match,
+ struct ieee80211_network *network, int roaming)
+{
+ struct ipw_supported_rates rates;
+ DECLARE_SSID_BUF(ssid);
+
+ /* Verify that this network's capability is compatible with the
+ * current mode (AdHoc or Infrastructure) */
+ if ((priv->ieee->iw_mode == IW_MODE_INFRA &&
+ !(network->capability & WLAN_CAPABILITY_ESS)) ||
+ (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ !(network->capability & WLAN_CAPABILITY_IBSS))) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded due to "
+ "capability mismatch.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ if (unlikely(roaming)) {
+ /* If we are roaming, then ensure check if this is a valid
+ * network to try and roam to */
+ if ((network->ssid_len != match->network->ssid_len) ||
+ memcmp(network->ssid, match->network->ssid,
+ network->ssid_len)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of non-network ESSID.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+ } else {
+ /* If an ESSID has been configured then compare the broadcast
+ * ESSID to ours */
+ if ((priv->config & CFG_STATIC_ESSID) &&
+ ((network->ssid_len != priv->essid_len) ||
+ memcmp(network->ssid, priv->essid,
+ min(network->ssid_len, priv->essid_len)))) {
+ char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+ strncpy(escaped,
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ sizeof(escaped));
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of ESSID mismatch: '%s'.\n",
+ escaped, network->bssid,
+ print_ssid(ssid, priv->essid,
+ priv->essid_len));
+ return 0;
+ }
+ }
+
+ /* If the old network rate is better than this one, don't bother
+ * testing everything else. */
+ if (match->network && match->network->stats.rssi > network->stats.rssi) {
+ char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+ strncpy(escaped,
+ print_ssid(ssid, network->ssid, network->ssid_len),
+ sizeof(escaped));
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded because "
+ "'%s (%pM)' has a stronger signal.\n",
+ escaped, network->bssid,
+ print_ssid(ssid, match->network->ssid,
+ match->network->ssid_len),
+ match->network->bssid);
+ return 0;
+ }
+
+ /* If this network has already had an association attempt within the
+ * last 3 seconds, do not try and associate again... */
+ if (network->last_associate &&
+ time_after(network->last_associate + (HZ * 3UL), jiffies)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of storming (%ums since last "
+ "assoc attempt).\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ jiffies_to_msecs(jiffies -
+ network->last_associate));
+ return 0;
+ }
+
+ /* Now go through and see if the requested network is valid... */
+ if (priv->ieee->scan_age != 0 &&
+ time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of age: %ums.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ jiffies_to_msecs(jiffies -
+ network->last_scanned));
+ return 0;
+ }
+
+ if ((priv->config & CFG_STATIC_CHANNEL) &&
+ (network->channel != priv->channel)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of channel mismatch: %d != %d.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ network->channel, priv->channel);
+ return 0;
+ }
+
+ /* Verify privacy compatability */
+ if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) !=
+ ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of privacy mismatch: %s != %s.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ priv->capability & CAP_PRIVACY_ON ? "on" :
+ "off",
+ network->capability &
+ WLAN_CAPABILITY_PRIVACY ? "on" : "off");
+ return 0;
+ }
+
+ if ((priv->config & CFG_STATIC_BSSID) &&
+ memcmp(network->bssid, priv->bssid, ETH_ALEN)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of BSSID mismatch: %pM.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid, priv->bssid);
+ return 0;
+ }
+
+ /* Filter out any incompatible freq / mode combinations */
+ if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of invalid frequency/mode "
+ "combination.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ /* Filter out invalid channel in current GEO */
+ if (!ieee80211_is_valid_channel(priv->ieee, network->channel)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of invalid channel in current GEO\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ /* Ensure that the rates supported by the driver are compatible with
+ * this AP, including verification of basic rates (mandatory) */
+ if (!ipw_compatible_rates(priv, network, &rates)) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because configured rate mask excludes "
+ "AP mandatory rate.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ if (rates.num_rates == 0) {
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded "
+ "because of no compatible rates.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 0;
+ }
+
+ /* TODO: Perform any further minimal comparititive tests. We do not
+ * want to put too much policy logic here; intelligent scan selection
+ * should occur within a generic IEEE 802.11 user space tool. */
+
+ /* Set up 'new' AP to this network */
+ ipw_copy_rates(&match->rates, &rates);
+ match->network = network;
+
+ IPW_DEBUG_ASSOC("Network '%s (%pM)' is a viable match.\n",
+ print_ssid(ssid, network->ssid, network->ssid_len),
+ network->bssid);
+
+ return 1;
+}
+
+static void ipw_adhoc_create(struct ipw_priv *priv,
+ struct ieee80211_network *network)
+{
+ const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ int i;
+
+ /*
+ * For the purposes of scanning, we can set our wireless mode
+ * to trigger scans across combinations of bands, but when it
+ * comes to creating a new ad-hoc network, we have tell the FW
+ * exactly which band to use.
+ *
+ * We also have the possibility of an invalid channel for the
+ * chossen band. Attempting to create a new ad-hoc network
+ * with an invalid channel for wireless mode will trigger a
+ * FW fatal error.
+ *
+ */
+ switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+ case IEEE80211_52GHZ_BAND:
+ network->mode = IEEE_A;
+ i = ieee80211_channel_to_index(priv->ieee, priv->channel);
+ BUG_ON(i == -1);
+ if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+ IPW_WARNING("Overriding invalid channel\n");
+ priv->channel = geo->a[0].channel;
+ }
+ break;
+
+ case IEEE80211_24GHZ_BAND:
+ if (priv->ieee->mode & IEEE_G)
+ network->mode = IEEE_G;
+ else
+ network->mode = IEEE_B;
+ i = ieee80211_channel_to_index(priv->ieee, priv->channel);
+ BUG_ON(i == -1);
+ if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) {
+ IPW_WARNING("Overriding invalid channel\n");
+ priv->channel = geo->bg[0].channel;
+ }
+ break;
+
+ default:
+ IPW_WARNING("Overriding invalid channel\n");
+ if (priv->ieee->mode & IEEE_A) {
+ network->mode = IEEE_A;
+ priv->channel = geo->a[0].channel;
+ } else if (priv->ieee->mode & IEEE_G) {
+ network->mode = IEEE_G;
+ priv->channel = geo->bg[0].channel;
+ } else {
+ network->mode = IEEE_B;
+ priv->channel = geo->bg[0].channel;
+ }
+ break;
+ }
+
+ network->channel = priv->channel;
+ priv->config |= CFG_ADHOC_PERSIST;
+ ipw_create_bssid(priv, network->bssid);
+ network->ssid_len = priv->essid_len;
+ memcpy(network->ssid, priv->essid, priv->essid_len);
+ memset(&network->stats, 0, sizeof(network->stats));
+ network->capability = WLAN_CAPABILITY_IBSS;
+ if (!(priv->config & CFG_PREAMBLE_LONG))
+ network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+ if (priv->capability & CAP_PRIVACY_ON)
+ network->capability |= WLAN_CAPABILITY_PRIVACY;
+ network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH);
+ memcpy(network->rates, priv->rates.supported_rates, network->rates_len);
+ network->rates_ex_len = priv->rates.num_rates - network->rates_len;
+ memcpy(network->rates_ex,
+ &priv->rates.supported_rates[network->rates_len],
+ network->rates_ex_len);
+ network->last_scanned = 0;
+ network->flags = 0;
+ network->last_associate = 0;
+ network->time_stamp[0] = 0;
+ network->time_stamp[1] = 0;
+ network->beacon_interval = 100; /* Default */
+ network->listen_interval = 10; /* Default */
+ network->atim_window = 0; /* Default */
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+}
+
+static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index)
+{
+ struct ipw_tgi_tx_key key;
+
+ if (!(priv->ieee->sec.flags & (1 << index)))
+ return;
+
+ key.key_id = index;
+ memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH);
+ key.security_type = type;
+ key.station_index = 0; /* always 0 for BSS */
+ key.flags = 0;
+ /* 0 for new key; previous value of counter (after fatal error) */
+ key.tx_counter[0] = cpu_to_le32(0);
+ key.tx_counter[1] = cpu_to_le32(0);
+
+ ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key);
+}
+
+static void ipw_send_wep_keys(struct ipw_priv *priv, int type)
+{
+ struct ipw_wep_key key;
+ int i;
+
+ key.cmd_id = DINO_CMD_WEP_KEY;
+ key.seq_num = 0;
+
+ /* Note: AES keys cannot be set for multiple times.
+ * Only set it at the first time. */
+ for (i = 0; i < 4; i++) {
+ key.key_index = i | type;
+ if (!(priv->ieee->sec.flags & (1 << i))) {
+ key.key_size = 0;
+ continue;
+ }
+
+ key.key_size = priv->ieee->sec.key_sizes[i];
+ memcpy(key.key, priv->ieee->sec.keys[i], key.key_size);
+
+ ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key);
+ }
+}
+
+static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level)
+{
+ if (priv->ieee->host_encrypt)
+ return;
+
+ switch (level) {
+ case SEC_LEVEL_3:
+ priv->sys_config.disable_unicast_decryption = 0;
+ priv->ieee->host_decrypt = 0;
+ break;
+ case SEC_LEVEL_2:
+ priv->sys_config.disable_unicast_decryption = 1;
+ priv->ieee->host_decrypt = 1;
+ break;
+ case SEC_LEVEL_1:
+ priv->sys_config.disable_unicast_decryption = 0;
+ priv->ieee->host_decrypt = 0;
+ break;
+ case SEC_LEVEL_0:
+ priv->sys_config.disable_unicast_decryption = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level)
+{
+ if (priv->ieee->host_encrypt)
+ return;
+
+ switch (level) {
+ case SEC_LEVEL_3:
+ priv->sys_config.disable_multicast_decryption = 0;
+ break;
+ case SEC_LEVEL_2:
+ priv->sys_config.disable_multicast_decryption = 1;
+ break;
+ case SEC_LEVEL_1:
+ priv->sys_config.disable_multicast_decryption = 0;
+ break;
+ case SEC_LEVEL_0:
+ priv->sys_config.disable_multicast_decryption = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
+{
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ ipw_send_tgi_tx_key(priv,
+ DCT_FLAG_EXT_SECURITY_CCM,
+ priv->ieee->sec.active_key);
+
+ if (!priv->ieee->host_mc_decrypt)
+ ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
+ break;
+ case SEC_LEVEL_2:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ ipw_send_tgi_tx_key(priv,
+ DCT_FLAG_EXT_SECURITY_TKIP,
+ priv->ieee->sec.active_key);
+ break;
+ case SEC_LEVEL_1:
+ ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
+ ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level);
+ ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level);
+ break;
+ case SEC_LEVEL_0:
+ default:
+ break;
+ }
+}
+
+static void ipw_adhoc_check(void *data)
+{
+ struct ipw_priv *priv = data;
+
+ if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold &&
+ !(priv->config & CFG_ADHOC_PERSIST)) {
+ IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF |
+ IPW_DL_STATE | IPW_DL_ASSOC,
+ "Missed beacon: %d - disassociate\n",
+ priv->missed_adhoc_beacons);
+ ipw_remove_current_network(priv);
+ ipw_disassociate(priv);
+ return;
+ }
+
+ queue_delayed_work(priv->workqueue, &priv->adhoc_check,
+ le16_to_cpu(priv->assoc_request.beacon_interval));
+}
+
+static void ipw_bg_adhoc_check(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, adhoc_check.work);
+ mutex_lock(&priv->mutex);
+ ipw_adhoc_check(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_debug_config(struct ipw_priv *priv)
+{
+ DECLARE_SSID_BUF(ssid);
+ IPW_DEBUG_INFO("Scan completed, no valid APs matched "
+ "[CFG 0x%08X]\n", priv->config);
+ if (priv->config & CFG_STATIC_CHANNEL)
+ IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel);
+ else
+ IPW_DEBUG_INFO("Channel unlocked.\n");
+ if (priv->config & CFG_STATIC_ESSID)
+ IPW_DEBUG_INFO("ESSID locked to '%s'\n",
+ print_ssid(ssid, priv->essid, priv->essid_len));
+ else
+ IPW_DEBUG_INFO("ESSID unlocked.\n");
+ if (priv->config & CFG_STATIC_BSSID)
+ IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid);
+ else
+ IPW_DEBUG_INFO("BSSID unlocked.\n");
+ if (priv->capability & CAP_PRIVACY_ON)
+ IPW_DEBUG_INFO("PRIVACY on\n");
+ else
+ IPW_DEBUG_INFO("PRIVACY off\n");
+ IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask);
+}
+
+static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode)
+{
+ /* TODO: Verify that this works... */
+ struct ipw_fixed_rate fr = {
+ .tx_rates = priv->rates_mask
+ };
+ u32 reg;
+ u16 mask = 0;
+
+ /* Identify 'current FW band' and match it with the fixed
+ * Tx rates */
+
+ switch (priv->ieee->freq_band) {
+ case IEEE80211_52GHZ_BAND: /* A only */
+ /* IEEE_A */
+ if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) {
+ /* Invalid fixed rate mask */
+ IPW_DEBUG_WX
+ ("invalid fixed rate mask in ipw_set_fixed_rate\n");
+ fr.tx_rates = 0;
+ break;
+ }
+
+ fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A;
+ break;
+
+ default: /* 2.4Ghz or Mixed */
+ /* IEEE_B */
+ if (mode == IEEE_B) {
+ if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) {
+ /* Invalid fixed rate mask */
+ IPW_DEBUG_WX
+ ("invalid fixed rate mask in ipw_set_fixed_rate\n");
+ fr.tx_rates = 0;
+ }
+ break;
+ }
+
+ /* IEEE_G */
+ if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK |
+ IEEE80211_OFDM_RATES_MASK)) {
+ /* Invalid fixed rate mask */
+ IPW_DEBUG_WX
+ ("invalid fixed rate mask in ipw_set_fixed_rate\n");
+ fr.tx_rates = 0;
+ break;
+ }
+
+ if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK;
+ }
+
+ if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK;
+ }
+
+ if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) {
+ mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1);
+ fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK;
+ }
+
+ fr.tx_rates |= mask;
+ break;
+ }
+
+ reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE);
+ ipw_write_reg32(priv, reg, *(u32 *) & fr);
+}
+
+static void ipw_abort_scan(struct ipw_priv *priv)
+{
+ int err;
+
+ if (priv->status & STATUS_SCAN_ABORTING) {
+ IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n");
+ return;
+ }
+ priv->status |= STATUS_SCAN_ABORTING;
+
+ err = ipw_send_scan_abort(priv);
+ if (err)
+ IPW_DEBUG_HC("Request to abort scan failed.\n");
+}
+
+static void ipw_add_scan_channels(struct ipw_priv *priv,
+ struct ipw_scan_request_ext *scan,
+ int scan_type)
+{
+ int channel_index = 0;
+ const struct ieee80211_geo *geo;
+ int i;
+
+ geo = ieee80211_get_geo(priv->ieee);
+
+ if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) {
+ int start = channel_index;
+ for (i = 0; i < geo->a_channels; i++) {
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ geo->a[i].channel == priv->channel)
+ continue;
+ channel_index++;
+ scan->channels_list[channel_index] = geo->a[i].channel;
+ ipw_set_scan_type(scan, channel_index,
+ geo->a[i].
+ flags & IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN :
+ scan_type);
+ }
+
+ if (start != channel_index) {
+ scan->channels_list[start] = (u8) (IPW_A_MODE << 6) |
+ (channel_index - start);
+ channel_index++;
+ }
+ }
+
+ if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) {
+ int start = channel_index;
+ if (priv->config & CFG_SPEED_SCAN) {
+ int index;
+ u8 channels[IEEE80211_24GHZ_CHANNELS] = {
+ /* nop out the list */
+ [0] = 0
+ };
+
+ u8 channel;
+ while (channel_index < IPW_SCAN_CHANNELS) {
+ channel =
+ priv->speed_scan[priv->speed_scan_pos];
+ if (channel == 0) {
+ priv->speed_scan_pos = 0;
+ channel = priv->speed_scan[0];
+ }
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ channel == priv->channel) {
+ priv->speed_scan_pos++;
+ continue;
+ }
+
+ /* If this channel has already been
+ * added in scan, break from loop
+ * and this will be the first channel
+ * in the next scan.
+ */
+ if (channels[channel - 1] != 0)
+ break;
+
+ channels[channel - 1] = 1;
+ priv->speed_scan_pos++;
+ channel_index++;
+ scan->channels_list[channel_index] = channel;
+ index =
+ ieee80211_channel_to_index(priv->ieee, channel);
+ ipw_set_scan_type(scan, channel_index,
+ geo->bg[index].
+ flags &
+ IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+ : scan_type);
+ }
+ } else {
+ for (i = 0; i < geo->bg_channels; i++) {
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ geo->bg[i].channel == priv->channel)
+ continue;
+ channel_index++;
+ scan->channels_list[channel_index] =
+ geo->bg[i].channel;
+ ipw_set_scan_type(scan, channel_index,
+ geo->bg[i].
+ flags &
+ IEEE80211_CH_PASSIVE_ONLY ?
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN
+ : scan_type);
+ }
+ }
+
+ if (start != channel_index) {
+ scan->channels_list[start] = (u8) (IPW_B_MODE << 6) |
+ (channel_index - start);
+ }
+ }
+}
+
+static int ipw_passive_dwell_time(struct ipw_priv *priv)
+{
+ /* staying on passive channels longer than the DTIM interval during a
+ * scan, while associated, causes the firmware to cancel the scan
+ * without notification. Hence, don't stay on passive channels longer
+ * than the beacon interval.
+ */
+ if (priv->status & STATUS_ASSOCIATED
+ && priv->assoc_network->beacon_interval > 10)
+ return priv->assoc_network->beacon_interval - 10;
+ else
+ return 120;
+}
+
+static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
+{
+ struct ipw_scan_request_ext scan;
+ int err = 0, scan_type;
+
+ if (!(priv->status & STATUS_INIT) ||
+ (priv->status & STATUS_EXIT_PENDING))
+ return 0;
+
+ mutex_lock(&priv->mutex);
+
+ if (direct && (priv->direct_scan_ssid_len == 0)) {
+ IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
+ priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+ goto done;
+ }
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n");
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
+ goto done;
+ }
+
+ if (!(priv->status & STATUS_SCAN_FORCED) &&
+ priv->status & STATUS_SCAN_ABORTING) {
+ IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
+ goto done;
+ }
+
+ if (priv->status & STATUS_RF_KILL_MASK) {
+ IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
+ goto done;
+ }
+
+ memset(&scan, 0, sizeof(scan));
+ scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
+
+ if (type == IW_SCAN_TYPE_PASSIVE) {
+ IPW_DEBUG_WX("use passive scanning\n");
+ scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN;
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
+ cpu_to_le16(ipw_passive_dwell_time(priv));
+ ipw_add_scan_channels(priv, &scan, scan_type);
+ goto send_request;
+ }
+
+ /* Use active scan by default. */
+ if (priv->config & CFG_SPEED_SCAN)
+ scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
+ cpu_to_le16(30);
+ else
+ scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
+ cpu_to_le16(20);
+
+ scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
+ cpu_to_le16(20);
+
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
+ cpu_to_le16(ipw_passive_dwell_time(priv));
+ scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
+
+#ifdef CONFIG_IPW2200_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ u8 channel;
+ u8 band = 0;
+
+ switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+ case IEEE80211_52GHZ_BAND:
+ band = (u8) (IPW_A_MODE << 6) | 1;
+ channel = priv->channel;
+ break;
+
+ case IEEE80211_24GHZ_BAND:
+ band = (u8) (IPW_B_MODE << 6) | 1;
+ channel = priv->channel;
+ break;
+
+ default:
+ band = (u8) (IPW_B_MODE << 6) | 1;
+ channel = 9;
+ break;
+ }
+
+ scan.channels_list[0] = band;
+ scan.channels_list[1] = channel;
+ ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN);
+
+ /* NOTE: The card will sit on this channel for this time
+ * period. Scan aborts are timing sensitive and frequently
+ * result in firmware restarts. As such, it is best to
+ * set a small dwell_time here and just keep re-issuing
+ * scans. Otherwise fast channel hopping will not actually
+ * hop channels.
+ *
+ * TODO: Move SPEED SCAN support to all modes and bands */
+ scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] =
+ cpu_to_le16(2000);
+ } else {
+#endif /* CONFIG_IPW2200_MONITOR */
+ /* Honor direct scans first, otherwise if we are roaming make
+ * this a direct scan for the current network. Finally,
+ * ensure that every other scan is a fast channel hop scan */
+ if (direct) {
+ err = ipw_send_ssid(priv, priv->direct_scan_ssid,
+ priv->direct_scan_ssid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command "
+ "failed\n");
+ goto done;
+ }
+
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+ } else if ((priv->status & STATUS_ROAMING)
+ || (!(priv->status & STATUS_ASSOCIATED)
+ && (priv->config & CFG_STATIC_ESSID)
+ && (le32_to_cpu(scan.full_scan_index) % 2))) {
+ err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command "
+ "failed.\n");
+ goto done;
+ }
+
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+ } else
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN;
+
+ ipw_add_scan_channels(priv, &scan, scan_type);
+#ifdef CONFIG_IPW2200_MONITOR
+ }
+#endif
+
+send_request:
+ err = ipw_send_scan_request_ext(priv, &scan);
+ if (err) {
+ IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
+ goto done;
+ }
+
+ priv->status |= STATUS_SCANNING;
+ if (direct) {
+ priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+ priv->direct_scan_ssid_len = 0;
+ } else
+ priv->status &= ~STATUS_SCAN_PENDING;
+
+ queue_delayed_work(priv->workqueue, &priv->scan_check,
+ IPW_SCAN_CHECK_WATCHDOG);
+done:
+ mutex_unlock(&priv->mutex);
+ return err;
+}
+
+static void ipw_request_passive_scan(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, request_passive_scan.work);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
+}
+
+static void ipw_request_scan(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, request_scan.work);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
+}
+
+static void ipw_request_direct_scan(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, request_direct_scan.work);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
+}
+
+static void ipw_bg_abort_scan(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, abort_scan);
+ mutex_lock(&priv->mutex);
+ ipw_abort_scan(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static int ipw_wpa_enable(struct ipw_priv *priv, int value)
+{
+ /* This is called when wpa_supplicant loads and closes the driver
+ * interface. */
+ priv->ieee->wpa_enabled = value;
+ return 0;
+}
+
+static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value)
+{
+ struct ieee80211_device *ieee = priv->ieee;
+ struct ieee80211_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+ int ret = 0;
+
+ if (value & IW_AUTH_ALG_SHARED_KEY) {
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ } else if (value & IW_AUTH_ALG_LEAP) {
+ sec.auth_mode = WLAN_AUTH_LEAP;
+ ieee->open_wep = 1;
+ } else
+ return -EINVAL;
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+
+static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie,
+ int wpa_ie_len)
+{
+ /* make sure WPA is enabled */
+ ipw_wpa_enable(priv, 1);
+}
+
+static int ipw_set_rsn_capa(struct ipw_priv *priv,
+ char *capabilities, int length)
+{
+ IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n");
+
+ return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length,
+ capabilities);
+}
+
+/*
+ * WE-18 support
+ */
+
+/* SIOCSIWGENIE */
+static int ipw_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ u8 *buf;
+ int err = 0;
+
+ if (wrqu->data.length > MAX_WPA_IE_LEN ||
+ (wrqu->data.length && extra == NULL))
+ return -EINVAL;
+
+ if (wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(buf, extra, wrqu->data.length);
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len);
+ out:
+ return err;
+}
+
+/* SIOCGIWGENIE */
+static int ipw_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ int err = 0;
+
+ if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
+ wrqu->data.length = 0;
+ goto out;
+ }
+
+ if (wrqu->data.length < ieee->wpa_ie_len) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ wrqu->data.length = ieee->wpa_ie_len;
+ memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ out:
+ return err;
+}
+
+static int wext_cipher2level(int cipher)
+{
+ switch (cipher) {
+ case IW_AUTH_CIPHER_NONE:
+ return SEC_LEVEL_0;
+ case IW_AUTH_CIPHER_WEP40:
+ case IW_AUTH_CIPHER_WEP104:
+ return SEC_LEVEL_1;
+ case IW_AUTH_CIPHER_TKIP:
+ return SEC_LEVEL_2;
+ case IW_AUTH_CIPHER_CCMP:
+ return SEC_LEVEL_3;
+ default:
+ return -1;
+ }
+}
+
+/* SIOCSIWAUTH */
+static int ipw_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ struct iw_param *param = &wrqu->param;
+ struct lib80211_crypt_data *crypt;
+ unsigned long flags;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ ipw_set_hw_decrypt_unicast(priv,
+ wext_cipher2level(param->value));
+ break;
+ case IW_AUTH_CIPHER_GROUP:
+ ipw_set_hw_decrypt_multicast(priv,
+ wext_cipher2level(param->value));
+ break;
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * ipw2200 does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags)
+ break;
+
+ flags = crypt->ops->get_flags(crypt->priv);
+
+ if (param->value)
+ flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+ else
+ flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES;
+
+ crypt->ops->set_flags(flags, crypt->priv);
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:{
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ struct ieee80211_security sec = {
+ .flags = SEC_ENABLED,
+ .enabled = param->value,
+ };
+ priv->ieee->drop_unencrypted = param->value;
+ /* We only change SEC_LEVEL for open mode. Others
+ * are set by ipw_wpa_set_encryption.
+ */
+ if (!param->value) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_0;
+ } else {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ }
+ if (priv->ieee->set_security)
+ priv->ieee->set_security(priv->ieee->dev, &sec);
+ break;
+ }
+
+ case IW_AUTH_80211_AUTH_ALG:
+ ret = ipw_wpa_set_auth_algs(priv, param->value);
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ ret = ipw_wpa_enable(priv, param->value);
+ ipw_disassociate(priv);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = param->value;
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = param->value;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+/* SIOCGIWAUTH */
+static int ipw_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee;
+ struct lib80211_crypt_data *crypt;
+ struct iw_param *param = &wrqu->param;
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * wpa_supplicant will control these internally
+ */
+ ret = -EOPNOTSUPP;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx];
+ if (!crypt || !crypt->ops->get_flags)
+ break;
+
+ param->value = (crypt->ops->get_flags(crypt->priv) &
+ IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0;
+
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ param->value = ieee->drop_unencrypted;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ param->value = ieee->sec.auth_mode;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = ieee->wpa_enabled;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ param->value = ieee->ieee802_1x;
+ break;
+
+ case IW_AUTH_ROAMING_CONTROL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ param->value = ieee->privacy_invoked;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/* SIOCSIWENCODEEXT */
+static int ipw_wx_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+
+ if (hwcrypto) {
+ if (ext->alg == IW_ENCODE_ALG_TKIP) {
+ /* IPW HW can't build TKIP MIC,
+ host decryption still needed */
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+ priv->ieee->host_mc_decrypt = 1;
+ else {
+ priv->ieee->host_encrypt = 0;
+ priv->ieee->host_encrypt_msdu = 1;
+ priv->ieee->host_decrypt = 1;
+ }
+ } else {
+ priv->ieee->host_encrypt = 0;
+ priv->ieee->host_encrypt_msdu = 0;
+ priv->ieee->host_decrypt = 0;
+ priv->ieee->host_mc_decrypt = 0;
+ }
+ }
+
+ return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCGIWENCODEEXT */
+static int ipw_wx_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra);
+}
+
+/* SIOCSIWMLME */
+static int ipw_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ __le16 reason;
+
+ reason = cpu_to_le16(mlme->reason_code);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ /* silently ignore */
+ break;
+
+ case IW_MLME_DISASSOC:
+ ipw_disassociate(priv);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_IPW2200_QOS
+
+/* QoS */
+/*
+* get the modulation type of the current network or
+* the card current mode
+*/
+static u8 ipw_qos_current_mode(struct ipw_priv * priv)
+{
+ u8 mode = 0;
+
+ if (priv->status & STATUS_ASSOCIATED) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ mode = priv->assoc_network->mode;
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+ } else {
+ mode = priv->ieee->mode;
+ }
+ IPW_DEBUG_QOS("QoS network/card mode %d \n", mode);
+ return mode;
+}
+
+/*
+* Handle management frame beacon and probe response
+*/
+static int ipw_qos_handle_probe_response(struct ipw_priv *priv,
+ int active_network,
+ struct ieee80211_network *network)
+{
+ u32 size = sizeof(struct ieee80211_qos_parameters);
+
+ if (network->capability & WLAN_CAPABILITY_IBSS)
+ network->qos_data.active = network->qos_data.supported;
+
+ if (network->flags & NETWORK_HAS_QOS_MASK) {
+ if (active_network &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS))
+ network->qos_data.active = network->qos_data.supported;
+
+ if ((network->qos_data.active == 1) && (active_network == 1) &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
+ (network->qos_data.old_param_count !=
+ network->qos_data.param_count)) {
+ network->qos_data.old_param_count =
+ network->qos_data.param_count;
+ schedule_work(&priv->qos_activate);
+ IPW_DEBUG_QOS("QoS parameters change call "
+ "qos_activate\n");
+ }
+ } else {
+ if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B))
+ memcpy(&network->qos_data.parameters,
+ &def_parameters_CCK, size);
+ else
+ memcpy(&network->qos_data.parameters,
+ &def_parameters_OFDM, size);
+
+ if ((network->qos_data.active == 1) && (active_network == 1)) {
+ IPW_DEBUG_QOS("QoS was disabled call qos_activate \n");
+ schedule_work(&priv->qos_activate);
+ }
+
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ }
+ if ((priv->status & STATUS_ASSOCIATED) &&
+ (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) {
+ if (memcmp(network->bssid, priv->bssid, ETH_ALEN))
+ if (network->capability & WLAN_CAPABILITY_IBSS)
+ if ((network->ssid_len ==
+ priv->assoc_network->ssid_len) &&
+ !memcmp(network->ssid,
+ priv->assoc_network->ssid,
+ network->ssid_len)) {
+ queue_work(priv->workqueue,
+ &priv->merge_networks);
+ }
+ }
+
+ return 0;
+}
+
+/*
+* This function set up the firmware to support QoS. It sends
+* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO
+*/
+static int ipw_qos_activate(struct ipw_priv *priv,
+ struct ieee80211_qos_data *qos_network_data)
+{
+ int err;
+ struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS];
+ struct ieee80211_qos_parameters *active_one = NULL;
+ u32 size = sizeof(struct ieee80211_qos_parameters);
+ u32 burst_duration;
+ int i;
+ u8 type;
+
+ type = ipw_qos_current_mode(priv);
+
+ active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]);
+ memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size);
+ active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]);
+ memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size);
+
+ if (qos_network_data == NULL) {
+ if (type == IEEE_B) {
+ IPW_DEBUG_QOS("QoS activate network mode %d\n", type);
+ active_one = &def_parameters_CCK;
+ } else
+ active_one = &def_parameters_OFDM;
+
+ memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
+ burst_duration = ipw_qos_get_burst_duration(priv);
+ for (i = 0; i < QOS_QUEUE_NUM; i++)
+ qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] =
+ cpu_to_le16(burst_duration);
+ } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ if (type == IEEE_B) {
+ IPW_DEBUG_QOS("QoS activate IBSS nework mode %d\n",
+ type);
+ if (priv->qos_data.qos_enable == 0)
+ active_one = &def_parameters_CCK;
+ else
+ active_one = priv->qos_data.def_qos_parm_CCK;
+ } else {
+ if (priv->qos_data.qos_enable == 0)
+ active_one = &def_parameters_OFDM;
+ else
+ active_one = priv->qos_data.def_qos_parm_OFDM;
+ }
+ memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
+ } else {
+ unsigned long flags;
+ int active;
+
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ active_one = &(qos_network_data->parameters);
+ qos_network_data->old_param_count =
+ qos_network_data->param_count;
+ memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size);
+ active = qos_network_data->supported;
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+
+ if (active == 0) {
+ burst_duration = ipw_qos_get_burst_duration(priv);
+ for (i = 0; i < QOS_QUEUE_NUM; i++)
+ qos_parameters[QOS_PARAM_SET_ACTIVE].
+ tx_op_limit[i] = cpu_to_le16(burst_duration);
+ }
+ }
+
+ IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n");
+ err = ipw_send_qos_params_command(priv,
+ (struct ieee80211_qos_parameters *)
+ &(qos_parameters[0]));
+ if (err)
+ IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n");
+
+ return err;
+}
+
+/*
+* send IPW_CMD_WME_INFO to the firmware
+*/
+static int ipw_qos_set_info_element(struct ipw_priv *priv)
+{
+ int ret = 0;
+ struct ieee80211_qos_information_element qos_info;
+
+ if (priv == NULL)
+ return -1;
+
+ qos_info.elementID = QOS_ELEMENT_ID;
+ qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2;
+
+ qos_info.version = QOS_VERSION_1;
+ qos_info.ac_info = 0;
+
+ memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN);
+ qos_info.qui_type = QOS_OUI_TYPE;
+ qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE;
+
+ ret = ipw_send_qos_info_command(priv, &qos_info);
+ if (ret != 0) {
+ IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n");
+ }
+ return ret;
+}
+
+/*
+* Set the QoS parameter with the association request structure
+*/
+static int ipw_qos_association(struct ipw_priv *priv,
+ struct ieee80211_network *network)
+{
+ int err = 0;
+ struct ieee80211_qos_data *qos_data = NULL;
+ struct ieee80211_qos_data ibss_data = {
+ .supported = 1,
+ .active = 1,
+ };
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS));
+
+ qos_data = &ibss_data;
+ break;
+
+ case IW_MODE_INFRA:
+ qos_data = &network->qos_data;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ err = ipw_qos_activate(priv, qos_data);
+ if (err) {
+ priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC;
+ return err;
+ }
+
+ if (priv->qos_data.qos_enable && qos_data->supported) {
+ IPW_DEBUG_QOS("QoS will be enabled for this association\n");
+ priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC;
+ return ipw_qos_set_info_element(priv);
+ }
+
+ return 0;
+}
+
+/*
+* handling the beaconing responses. if we get different QoS setting
+* off the network from the associated setting, adjust the QoS
+* setting
+*/
+static int ipw_qos_association_resp(struct ipw_priv *priv,
+ struct ieee80211_network *network)
+{
+ int ret = 0;
+ unsigned long flags;
+ u32 size = sizeof(struct ieee80211_qos_parameters);
+ int set_qos_param = 0;
+
+ if ((priv == NULL) || (network == NULL) ||
+ (priv->assoc_network == NULL))
+ return ret;
+
+ if (!(priv->status & STATUS_ASSOCIATED))
+ return ret;
+
+ if ((priv->ieee->iw_mode != IW_MODE_INFRA))
+ return ret;
+
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
+ memcpy(&priv->assoc_network->qos_data, &network->qos_data,
+ sizeof(struct ieee80211_qos_data));
+ priv->assoc_network->qos_data.active = 1;
+ if ((network->qos_data.old_param_count !=
+ network->qos_data.param_count)) {
+ set_qos_param = 1;
+ network->qos_data.old_param_count =
+ network->qos_data.param_count;
+ }
+
+ } else {
+ if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B))
+ memcpy(&priv->assoc_network->qos_data.parameters,
+ &def_parameters_CCK, size);
+ else
+ memcpy(&priv->assoc_network->qos_data.parameters,
+ &def_parameters_OFDM, size);
+ priv->assoc_network->qos_data.active = 0;
+ priv->assoc_network->qos_data.supported = 0;
+ set_qos_param = 1;
+ }
+
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+
+ if (set_qos_param == 1)
+ schedule_work(&priv->qos_activate);
+
+ return ret;
+}
+
+static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv)
+{
+ u32 ret = 0;
+
+ if ((priv == NULL))
+ return 0;
+
+ if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION))
+ ret = priv->qos_data.burst_duration_CCK;
+ else
+ ret = priv->qos_data.burst_duration_OFDM;
+
+ return ret;
+}
+
+/*
+* Initialize the setting of QoS global
+*/
+static void ipw_qos_init(struct ipw_priv *priv, int enable,
+ int burst_enable, u32 burst_duration_CCK,
+ u32 burst_duration_OFDM)
+{
+ priv->qos_data.qos_enable = enable;
+
+ if (priv->qos_data.qos_enable) {
+ priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK;
+ priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM;
+ IPW_DEBUG_QOS("QoS is enabled\n");
+ } else {
+ priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK;
+ priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM;
+ IPW_DEBUG_QOS("QoS is not enabled\n");
+ }
+
+ priv->qos_data.burst_enable = burst_enable;
+
+ if (burst_enable) {
+ priv->qos_data.burst_duration_CCK = burst_duration_CCK;
+ priv->qos_data.burst_duration_OFDM = burst_duration_OFDM;
+ } else {
+ priv->qos_data.burst_duration_CCK = 0;
+ priv->qos_data.burst_duration_OFDM = 0;
+ }
+}
+
+/*
+* map the packet priority to the right TX Queue
+*/
+static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority)
+{
+ if (priority > 7 || !priv->qos_data.qos_enable)
+ priority = 0;
+
+ return from_priority_to_tx_queue[priority] - 1;
+}
+
+static int ipw_is_qos_active(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_qos_data *qos_data = NULL;
+ int active, supported;
+ u8 *daddr = skb->data + ETH_ALEN;
+ int unicast = !is_multicast_ether_addr(daddr);
+
+ if (!(priv->status & STATUS_ASSOCIATED))
+ return 0;
+
+ qos_data = &priv->assoc_network->qos_data;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ if (unicast == 0)
+ qos_data->active = 0;
+ else
+ qos_data->active = qos_data->supported;
+ }
+ active = qos_data->active;
+ supported = qos_data->supported;
+ IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d "
+ "unicast %d\n",
+ priv->qos_data.qos_enable, active, supported, unicast);
+ if (active && priv->qos_data.qos_enable)
+ return 1;
+
+ return 0;
+
+}
+/*
+* add QoS parameter to the TX command
+*/
+static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv,
+ u16 priority,
+ struct tfd_data *tfd)
+{
+ int tx_queue_id = 0;
+
+
+ tx_queue_id = from_priority_to_tx_queue[priority] - 1;
+ tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED;
+
+ if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) {
+ tfd->tx_flags &= ~DCT_FLAG_ACK_REQD;
+ tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK);
+ }
+ return 0;
+}
+
+/*
+* background support to run QoS activate functionality
+*/
+static void ipw_bg_qos_activate(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, qos_activate);
+
+ if (priv == NULL)
+ return;
+
+ mutex_lock(&priv->mutex);
+
+ if (priv->status & STATUS_ASSOCIATED)
+ ipw_qos_activate(priv, &(priv->assoc_network->qos_data));
+
+ mutex_unlock(&priv->mutex);
+}
+
+static int ipw_handle_probe_response(struct net_device *dev,
+ struct ieee80211_probe_response *resp,
+ struct ieee80211_network *network)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+ (network == priv->assoc_network));
+
+ ipw_qos_handle_probe_response(priv, active_network, network);
+
+ return 0;
+}
+
+static int ipw_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *resp,
+ struct ieee80211_network *network)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int active_network = ((priv->status & STATUS_ASSOCIATED) &&
+ (network == priv->assoc_network));
+
+ ipw_qos_handle_probe_response(priv, active_network, network);
+
+ return 0;
+}
+
+static int ipw_handle_assoc_response(struct net_device *dev,
+ struct ieee80211_assoc_response *resp,
+ struct ieee80211_network *network)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ ipw_qos_association_resp(priv, network);
+ return 0;
+}
+
+static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters
+ *qos_param)
+{
+ return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS,
+ sizeof(*qos_param) * 3, qos_param);
+}
+
+static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element
+ *qos_param)
+{
+ return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param),
+ qos_param);
+}
+
+#endif /* CONFIG_IPW2200_QOS */
+
+static int ipw_associate_network(struct ipw_priv *priv,
+ struct ieee80211_network *network,
+ struct ipw_supported_rates *rates, int roaming)
+{
+ int err;
+ DECLARE_SSID_BUF(ssid);
+
+ if (priv->config & CFG_FIXED_RATE)
+ ipw_set_fixed_rate(priv, network->mode);
+
+ if (!(priv->config & CFG_STATIC_ESSID)) {
+ priv->essid_len = min(network->ssid_len,
+ (u8) IW_ESSID_MAX_SIZE);
+ memcpy(priv->essid, network->ssid, priv->essid_len);
+ }
+
+ network->last_associate = jiffies;
+
+ memset(&priv->assoc_request, 0, sizeof(priv->assoc_request));
+ priv->assoc_request.channel = network->channel;
+ priv->assoc_request.auth_key = 0;
+
+ if ((priv->capability & CAP_PRIVACY_ON) &&
+ (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) {
+ priv->assoc_request.auth_type = AUTH_SHARED_KEY;
+ priv->assoc_request.auth_key = priv->ieee->sec.active_key;
+
+ if (priv->ieee->sec.level == SEC_LEVEL_1)
+ ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
+
+ } else if ((priv->capability & CAP_PRIVACY_ON) &&
+ (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP))
+ priv->assoc_request.auth_type = AUTH_LEAP;
+ else
+ priv->assoc_request.auth_type = AUTH_OPEN;
+
+ if (priv->ieee->wpa_ie_len) {
+ priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */
+ ipw_set_rsn_capa(priv, priv->ieee->wpa_ie,
+ priv->ieee->wpa_ie_len);
+ }
+
+ /*
+ * It is valid for our ieee device to support multiple modes, but
+ * when it comes to associating to a given network we have to choose
+ * just one mode.
+ */
+ if (network->mode & priv->ieee->mode & IEEE_A)
+ priv->assoc_request.ieee_mode = IPW_A_MODE;
+ else if (network->mode & priv->ieee->mode & IEEE_G)
+ priv->assoc_request.ieee_mode = IPW_G_MODE;
+ else if (network->mode & priv->ieee->mode & IEEE_B)
+ priv->assoc_request.ieee_mode = IPW_B_MODE;
+
+ priv->assoc_request.capability = cpu_to_le16(network->capability);
+ if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ && !(priv->config & CFG_PREAMBLE_LONG)) {
+ priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE;
+ } else {
+ priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE;
+
+ /* Clear the short preamble if we won't be supporting it */
+ priv->assoc_request.capability &=
+ ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
+ }
+
+ /* Clear capability bits that aren't used in Ad Hoc */
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ priv->assoc_request.capability &=
+ ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ IPW_DEBUG_ASSOC("%sssocation attempt: '%s', channel %d, "
+ "802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n",
+ roaming ? "Rea" : "A",
+ print_ssid(ssid, priv->essid, priv->essid_len),
+ network->channel,
+ ipw_modes[priv->assoc_request.ieee_mode],
+ rates->num_rates,
+ (priv->assoc_request.preamble_length ==
+ DCT_FLAG_LONG_PREAMBLE) ? "long" : "short",
+ network->capability &
+ WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long",
+ priv->capability & CAP_PRIVACY_ON ? "on " : "off",
+ priv->capability & CAP_PRIVACY_ON ?
+ (priv->capability & CAP_SHARED_KEY ? "(shared)" :
+ "(open)") : "",
+ priv->capability & CAP_PRIVACY_ON ? " key=" : "",
+ priv->capability & CAP_PRIVACY_ON ?
+ '1' + priv->ieee->sec.active_key : '.',
+ priv->capability & CAP_PRIVACY_ON ? '.' : ' ');
+
+ priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval);
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
+ (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) {
+ priv->assoc_request.assoc_type = HC_IBSS_START;
+ priv->assoc_request.assoc_tsf_msw = 0;
+ priv->assoc_request.assoc_tsf_lsw = 0;
+ } else {
+ if (unlikely(roaming))
+ priv->assoc_request.assoc_type = HC_REASSOCIATE;
+ else
+ priv->assoc_request.assoc_type = HC_ASSOCIATE;
+ priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]);
+ priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]);
+ }
+
+ memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN);
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ memset(&priv->assoc_request.dest, 0xFF, ETH_ALEN);
+ priv->assoc_request.atim_window = cpu_to_le16(network->atim_window);
+ } else {
+ memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN);
+ priv->assoc_request.atim_window = 0;
+ }
+
+ priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval);
+
+ err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command failed.\n");
+ return err;
+ }
+
+ rates->ieee_mode = priv->assoc_request.ieee_mode;
+ rates->purpose = IPW_RATE_CONNECT;
+ ipw_send_supported_rates(priv, rates);
+
+ if (priv->assoc_request.ieee_mode == IPW_G_MODE)
+ priv->sys_config.dot11g_auto_detection = 1;
+ else
+ priv->sys_config.dot11g_auto_detection = 0;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ priv->sys_config.answer_broadcast_ssid_probe = 1;
+ else
+ priv->sys_config.answer_broadcast_ssid_probe = 0;
+
+ err = ipw_send_system_config(priv);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send sys config command failed.\n");
+ return err;
+ }
+
+ IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi);
+ err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+ return err;
+ }
+
+ /*
+ * If preemption is enabled, it is possible for the association
+ * to complete before we return from ipw_send_associate. Therefore
+ * we have to be sure and update our priviate data first.
+ */
+ priv->channel = network->channel;
+ memcpy(priv->bssid, network->bssid, ETH_ALEN);
+ priv->status |= STATUS_ASSOCIATING;
+ priv->status &= ~STATUS_SECURITY_UPDATED;
+
+ priv->assoc_network = network;
+
+#ifdef CONFIG_IPW2200_QOS
+ ipw_qos_association(priv, network);
+#endif
+
+ err = ipw_send_associate(priv, &priv->assoc_request);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send associate command failed.\n");
+ return err;
+ }
+
+ IPW_DEBUG(IPW_DL_STATE, "associating: '%s' %pM \n",
+ print_ssid(ssid, priv->essid, priv->essid_len),
+ priv->bssid);
+
+ return 0;
+}
+
+static void ipw_roam(void *data)
+{
+ struct ipw_priv *priv = data;
+ struct ieee80211_network *network = NULL;
+ struct ipw_network_match match = {
+ .network = priv->assoc_network
+ };
+
+ /* The roaming process is as follows:
+ *
+ * 1. Missed beacon threshold triggers the roaming process by
+ * setting the status ROAM bit and requesting a scan.
+ * 2. When the scan completes, it schedules the ROAM work
+ * 3. The ROAM work looks at all of the known networks for one that
+ * is a better network than the currently associated. If none
+ * found, the ROAM process is over (ROAM bit cleared)
+ * 4. If a better network is found, a disassociation request is
+ * sent.
+ * 5. When the disassociation completes, the roam work is again
+ * scheduled. The second time through, the driver is no longer
+ * associated, and the newly selected network is sent an
+ * association request.
+ * 6. At this point ,the roaming process is complete and the ROAM
+ * status bit is cleared.
+ */
+
+ /* If we are no longer associated, and the roaming bit is no longer
+ * set, then we are not actively roaming, so just return */
+ if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING)))
+ return;
+
+ if (priv->status & STATUS_ASSOCIATED) {
+ /* First pass through ROAM process -- look for a better
+ * network */
+ unsigned long flags;
+ u8 rssi = priv->assoc_network->stats.rssi;
+ priv->assoc_network->stats.rssi = -128;
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ list_for_each_entry(network, &priv->ieee->network_list, list) {
+ if (network != priv->assoc_network)
+ ipw_best_network(priv, &match, network, 1);
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+ priv->assoc_network->stats.rssi = rssi;
+
+ if (match.network == priv->assoc_network) {
+ IPW_DEBUG_ASSOC("No better APs in this network to "
+ "roam to.\n");
+ priv->status &= ~STATUS_ROAMING;
+ ipw_debug_config(priv);
+ return;
+ }
+
+ ipw_send_disassociate(priv, 1);
+ priv->assoc_network = match.network;
+
+ return;
+ }
+
+ /* Second pass through ROAM process -- request association */
+ ipw_compatible_rates(priv, priv->assoc_network, &match.rates);
+ ipw_associate_network(priv, priv->assoc_network, &match.rates, 1);
+ priv->status &= ~STATUS_ROAMING;
+}
+
+static void ipw_bg_roam(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, roam);
+ mutex_lock(&priv->mutex);
+ ipw_roam(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static int ipw_associate(void *data)
+{
+ struct ipw_priv *priv = data;
+
+ struct ieee80211_network *network = NULL;
+ struct ipw_network_match match = {
+ .network = NULL
+ };
+ struct ipw_supported_rates *rates;
+ struct list_head *element;
+ unsigned long flags;
+ DECLARE_SSID_BUF(ssid);
+
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n");
+ return 0;
+ }
+
+ if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_ASSOC("Not attempting association (already in "
+ "progress)\n");
+ return 0;
+ }
+
+ if (priv->status & STATUS_DISASSOCIATING) {
+ IPW_DEBUG_ASSOC("Not attempting association (in "
+ "disassociating)\n ");
+ queue_work(priv->workqueue, &priv->associate);
+ return 0;
+ }
+
+ if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) {
+ IPW_DEBUG_ASSOC("Not attempting association (scanning or not "
+ "initialized)\n");
+ return 0;
+ }
+
+ if (!(priv->config & CFG_ASSOCIATE) &&
+ !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) {
+ IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n");
+ return 0;
+ }
+
+ /* Protect our use of the network_list */
+ spin_lock_irqsave(&priv->ieee->lock, flags);
+ list_for_each_entry(network, &priv->ieee->network_list, list)
+ ipw_best_network(priv, &match, network, 0);
+
+ network = match.network;
+ rates = &match.rates;
+
+ if (network == NULL &&
+ priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ priv->config & CFG_ADHOC_CREATE &&
+ priv->config & CFG_STATIC_ESSID &&
+ priv->config & CFG_STATIC_CHANNEL) {
+ /* Use oldest network if the free list is empty */
+ if (list_empty(&priv->ieee->network_free_list)) {
+ struct ieee80211_network *oldest = NULL;
+ struct ieee80211_network *target;
+
+ list_for_each_entry(target, &priv->ieee->network_list, list) {
+ if ((oldest == NULL) ||
+ (target->last_scanned < oldest->last_scanned))
+ oldest = target;
+ }
+
+ /* If there are no more slots, expire the oldest */
+ list_del(&oldest->list);
+ target = oldest;
+ IPW_DEBUG_ASSOC("Expired '%s' (%pM) from "
+ "network list.\n",
+ print_ssid(ssid, target->ssid,
+ target->ssid_len),
+ target->bssid);
+ list_add_tail(&target->list,
+ &priv->ieee->network_free_list);
+ }
+
+ element = priv->ieee->network_free_list.next;
+ network = list_entry(element, struct ieee80211_network, list);
+ ipw_adhoc_create(priv, network);
+ rates = &priv->rates;
+ list_del(element);
+ list_add_tail(&network->list, &priv->ieee->network_list);
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, flags);
+
+ /* If we reached the end of the list, then we don't have any valid
+ * matching APs */
+ if (!network) {
+ ipw_debug_config(priv);
+
+ if (!(priv->status & STATUS_SCANNING)) {
+ if (!(priv->config & CFG_SPEED_SCAN))
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan,
+ SCAN_INTERVAL);
+ else
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan, 0);
+ }
+
+ return 0;
+ }
+
+ ipw_associate_network(priv, network, rates, 0);
+
+ return 1;
+}
+
+static void ipw_bg_associate(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, associate);
+ mutex_lock(&priv->mutex);
+ ipw_associate(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_control);
+ if (!(fc & IEEE80211_FCTL_PROTECTED))
+ return;
+
+ fc &= ~IEEE80211_FCTL_PROTECTED;
+ hdr->frame_control = cpu_to_le16(fc);
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ /* Remove CCMP HDR */
+ memmove(skb->data + IEEE80211_3ADDR_LEN,
+ skb->data + IEEE80211_3ADDR_LEN + 8,
+ skb->len - IEEE80211_3ADDR_LEN - 8);
+ skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */
+ break;
+ case SEC_LEVEL_2:
+ break;
+ case SEC_LEVEL_1:
+ /* Remove IV */
+ memmove(skb->data + IEEE80211_3ADDR_LEN,
+ skb->data + IEEE80211_3ADDR_LEN + 4,
+ skb->len - IEEE80211_3ADDR_LEN - 4);
+ skb_trim(skb, skb->len - 8); /* IV + ICV */
+ break;
+ case SEC_LEVEL_0:
+ break;
+ default:
+ printk(KERN_ERR "Unknow security level %d\n",
+ priv->ieee->sec.level);
+ break;
+ }
+}
+
+static void ipw_handle_data_packet(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ieee80211_hdr_4addr *hdr;
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ /* We only process data packets if the
+ * interface is open */
+ if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
+ skb_tailroom(rxb->skb))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ } else if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Advance skb->data to the start of the actual payload */
+ skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data));
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length));
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+ /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */
+ hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data;
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR &&
+ (is_multicast_ether_addr(hdr->addr1) ?
+ !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt))
+ ipw_rebuild_decrypted_skb(priv, rxb->skb);
+
+ if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+ priv->ieee->stats.rx_errors++;
+ else { /* ieee80211_rx succeeded, so it now owns the SKB */
+ rxb->skb = NULL;
+ __ipw_led_activity_on(priv);
+ }
+}
+
+#ifdef CONFIG_IPW2200_RADIOTAP
+static void ipw_handle_data_packet_monitor(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+ struct ipw_rx_frame *frame = &pkt->u.frame;
+
+ /* initial pull of some data */
+ u16 received_channel = frame->received_channel;
+ u8 antennaAndPhy = frame->antennaAndPhy;
+ s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */
+ u16 pktrate = frame->rate;
+
+ /* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE */
+ struct ipw_rt_hdr *ipw_rt;
+
+ short len = le16_to_cpu(pkt->u.frame.length);
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->net_dev->trans_start = jiffies;
+
+ /* We only process data packets if the
+ * interface is open */
+ if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) >
+ skb_tailroom(rxb->skb))) {
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ } else if (unlikely(!netif_running(priv->net_dev))) {
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+ * that now */
+ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+ /* FIXME: Should alloc bigger skb instead */
+ priv->ieee->stats.rx_dropped++;
+ priv->wstats.discard.misc++;
+ IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+ return;
+ }
+
+ /* copy the frame itself */
+ memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr),
+ rxb->skb->data + IPW_RX_FRAME_SIZE, len);
+
+ ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data;
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */
+
+ /* Big bitfield of all the fields we provide in radiotap */
+ ipw_rt->rt_hdr.it_present = cpu_to_le32(
+ (1 << IEEE80211_RADIOTAP_TSFT) |
+ (1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+ /* Zero the flags, we'll add to them as we go */
+ ipw_rt->rt_flags = 0;
+ ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
+ frame->parent_tsf[2] << 16 |
+ frame->parent_tsf[1] << 8 |
+ frame->parent_tsf[0]);
+
+ /* Convert signal to DBM */
+ ipw_rt->rt_dbmsignal = antsignal;
+ ipw_rt->rt_dbmnoise = frame->noise;
+
+ /* Convert the channel data and set the flags */
+ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel));
+ if (received_channel > 14) { /* 802.11a */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+ } else if (antennaAndPhy & 32) { /* 802.11b */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+ } else { /* 802.11g */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+ }
+
+ /* set the rate in multiples of 500k/s */
+ switch (pktrate) {
+ case IPW_TX_RATE_1MB:
+ ipw_rt->rt_rate = 2;
+ break;
+ case IPW_TX_RATE_2MB:
+ ipw_rt->rt_rate = 4;
+ break;
+ case IPW_TX_RATE_5MB:
+ ipw_rt->rt_rate = 10;
+ break;
+ case IPW_TX_RATE_6MB:
+ ipw_rt->rt_rate = 12;
+ break;
+ case IPW_TX_RATE_9MB:
+ ipw_rt->rt_rate = 18;
+ break;
+ case IPW_TX_RATE_11MB:
+ ipw_rt->rt_rate = 22;
+ break;
+ case IPW_TX_RATE_12MB:
+ ipw_rt->rt_rate = 24;
+ break;
+ case IPW_TX_RATE_18MB:
+ ipw_rt->rt_rate = 36;
+ break;
+ case IPW_TX_RATE_24MB:
+ ipw_rt->rt_rate = 48;
+ break;
+ case IPW_TX_RATE_36MB:
+ ipw_rt->rt_rate = 72;
+ break;
+ case IPW_TX_RATE_48MB:
+ ipw_rt->rt_rate = 96;
+ break;
+ case IPW_TX_RATE_54MB:
+ ipw_rt->rt_rate = 108;
+ break;
+ default:
+ ipw_rt->rt_rate = 0;
+ break;
+ }
+
+ /* antenna number */
+ ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */
+
+ /* set the preamble flag if we have it */
+ if ((antennaAndPhy & 64))
+ ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr));
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len);
+
+ if (!ieee80211_rx(priv->ieee, rxb->skb, stats))
+ priv->ieee->stats.rx_errors++;
+ else { /* ieee80211_rx succeeded, so it now owns the SKB */
+ rxb->skb = NULL;
+ /* no LED during capture */
+ }
+}
+#endif
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+#define ieee80211_is_probe_response(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \
+ (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP )
+
+#define ieee80211_is_management(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
+
+#define ieee80211_is_control(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL)
+
+#define ieee80211_is_data(fc) \
+ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
+
+#define ieee80211_is_assoc_request(fc) \
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ)
+
+#define ieee80211_is_reassoc_request(fc) \
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ)
+
+static void ipw_handle_promiscuous_rx(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data;
+ struct ipw_rx_frame *frame = &pkt->u.frame;
+ struct ipw_rt_hdr *ipw_rt;
+
+ /* First cache any information we need before we overwrite
+ * the information provided in the skb from the hardware */
+ struct ieee80211_hdr *hdr;
+ u16 channel = frame->received_channel;
+ u8 phy_flags = frame->antennaAndPhy;
+ s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM;
+ s8 noise = frame->noise;
+ u8 rate = frame->rate;
+ short len = le16_to_cpu(pkt->u.frame.length);
+ struct sk_buff *skb;
+ int hdr_only = 0;
+ u16 filter = priv->prom_priv->filter;
+
+ /* If the filter is set to not include Rx frames then return */
+ if (filter & IPW_PROM_NO_RX)
+ return;
+
+ /* We received data from the HW, so stop the watchdog */
+ priv->prom_net_dev->trans_start = jiffies;
+
+ if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
+ priv->prom_priv->ieee->stats.rx_errors++;
+ IPW_DEBUG_DROP("Corruption detected! Oh no!\n");
+ return;
+ }
+
+ /* We only process data packets if the interface is open */
+ if (unlikely(!netif_running(priv->prom_net_dev))) {
+ priv->prom_priv->ieee->stats.rx_dropped++;
+ IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+ return;
+ }
+
+ /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use
+ * that now */
+ if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) {
+ /* FIXME: Should alloc bigger skb instead */
+ priv->prom_priv->ieee->stats.rx_dropped++;
+ IPW_DEBUG_DROP("Dropping too large packet in monitor\n");
+ return;
+ }
+
+ hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE;
+ if (ieee80211_is_management(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_MGMT)
+ return;
+ if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_CTL)
+ return;
+ if (filter & IPW_PROM_CTL_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_DATA)
+ return;
+ if (filter & IPW_PROM_DATA_HEADER_ONLY)
+ hdr_only = 1;
+ }
+
+ /* Copy the SKB since this is for the promiscuous side */
+ skb = skb_copy(rxb->skb, GFP_ATOMIC);
+ if (skb == NULL) {
+ IPW_ERROR("skb_clone failed for promiscuous copy.\n");
+ return;
+ }
+
+ /* copy the frame data to write after where the radiotap header goes */
+ ipw_rt = (void *)skb->data;
+
+ if (hdr_only)
+ len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
+
+ memcpy(ipw_rt->payload, hdr, len);
+
+ ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */
+
+ /* Set the size of the skb to the size of the frame */
+ skb_put(skb, sizeof(*ipw_rt) + len);
+
+ /* Big bitfield of all the fields we provide in radiotap */
+ ipw_rt->rt_hdr.it_present = cpu_to_le32(
+ (1 << IEEE80211_RADIOTAP_TSFT) |
+ (1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) |
+ (1 << IEEE80211_RADIOTAP_ANTENNA));
+
+ /* Zero the flags, we'll add to them as we go */
+ ipw_rt->rt_flags = 0;
+ ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 |
+ frame->parent_tsf[2] << 16 |
+ frame->parent_tsf[1] << 8 |
+ frame->parent_tsf[0]);
+
+ /* Convert to DBM */
+ ipw_rt->rt_dbmsignal = signal;
+ ipw_rt->rt_dbmnoise = noise;
+
+ /* Convert the channel data and set the flags */
+ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel));
+ if (channel > 14) { /* 802.11a */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ));
+ } else if (phy_flags & (1 << 5)) { /* 802.11b */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ));
+ } else { /* 802.11g */
+ ipw_rt->rt_chbitmask =
+ cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ);
+ }
+
+ /* set the rate in multiples of 500k/s */
+ switch (rate) {
+ case IPW_TX_RATE_1MB:
+ ipw_rt->rt_rate = 2;
+ break;
+ case IPW_TX_RATE_2MB:
+ ipw_rt->rt_rate = 4;
+ break;
+ case IPW_TX_RATE_5MB:
+ ipw_rt->rt_rate = 10;
+ break;
+ case IPW_TX_RATE_6MB:
+ ipw_rt->rt_rate = 12;
+ break;
+ case IPW_TX_RATE_9MB:
+ ipw_rt->rt_rate = 18;
+ break;
+ case IPW_TX_RATE_11MB:
+ ipw_rt->rt_rate = 22;
+ break;
+ case IPW_TX_RATE_12MB:
+ ipw_rt->rt_rate = 24;
+ break;
+ case IPW_TX_RATE_18MB:
+ ipw_rt->rt_rate = 36;
+ break;
+ case IPW_TX_RATE_24MB:
+ ipw_rt->rt_rate = 48;
+ break;
+ case IPW_TX_RATE_36MB:
+ ipw_rt->rt_rate = 72;
+ break;
+ case IPW_TX_RATE_48MB:
+ ipw_rt->rt_rate = 96;
+ break;
+ case IPW_TX_RATE_54MB:
+ ipw_rt->rt_rate = 108;
+ break;
+ default:
+ ipw_rt->rt_rate = 0;
+ break;
+ }
+
+ /* antenna number */
+ ipw_rt->rt_antenna = (phy_flags & 3);
+
+ /* set the preamble flag if we have it */
+ if (phy_flags & (1 << 6))
+ ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+
+ IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len);
+
+ if (!ieee80211_rx(priv->prom_priv->ieee, skb, stats)) {
+ priv->prom_priv->ieee->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ }
+}
+#endif
+
+static int is_network_packet(struct ipw_priv *priv,
+ struct ieee80211_hdr_4addr *header)
+{
+ /* Filter incoming packets to determine if they are targetted toward
+ * this network, discarding packets coming from ourselves */
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */
+ /* packets from our adapter are dropped (echo) */
+ if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN))
+ return 0;
+
+ /* {broad,multi}cast packets to our BSSID go through */
+ if (is_multicast_ether_addr(header->addr1))
+ return !memcmp(header->addr3, priv->bssid, ETH_ALEN);
+
+ /* packets to our adapter go through */
+ return !memcmp(header->addr1, priv->net_dev->dev_addr,
+ ETH_ALEN);
+
+ case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */
+ /* packets from our adapter are dropped (echo) */
+ if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN))
+ return 0;
+
+ /* {broad,multi}cast packets to our BSS go through */
+ if (is_multicast_ether_addr(header->addr1))
+ return !memcmp(header->addr2, priv->bssid, ETH_ALEN);
+
+ /* packets to our adapter go through */
+ return !memcmp(header->addr1, priv->net_dev->dev_addr,
+ ETH_ALEN);
+ }
+
+ return 1;
+}
+
+#define IPW_PACKET_RETRY_TIME HZ
+
+static int is_duplicate_packet(struct ipw_priv *priv,
+ struct ieee80211_hdr_4addr *header)
+{
+ u16 sc = le16_to_cpu(header->seq_ctl);
+ u16 seq = WLAN_GET_SEQ_SEQ(sc);
+ u16 frag = WLAN_GET_SEQ_FRAG(sc);
+ u16 *last_seq, *last_frag;
+ unsigned long *last_time;
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ {
+ struct list_head *p;
+ struct ipw_ibss_seq *entry = NULL;
+ u8 *mac = header->addr2;
+ int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE;
+
+ __list_for_each(p, &priv->ibss_mac_hash[index]) {
+ entry =
+ list_entry(p, struct ipw_ibss_seq, list);
+ if (!memcmp(entry->mac, mac, ETH_ALEN))
+ break;
+ }
+ if (p == &priv->ibss_mac_hash[index]) {
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ IPW_ERROR
+ ("Cannot malloc new mac entry\n");
+ return 0;
+ }
+ memcpy(entry->mac, mac, ETH_ALEN);
+ entry->seq_num = seq;
+ entry->frag_num = frag;
+ entry->packet_time = jiffies;
+ list_add(&entry->list,
+ &priv->ibss_mac_hash[index]);
+ return 0;
+ }
+ last_seq = &entry->seq_num;
+ last_frag = &entry->frag_num;
+ last_time = &entry->packet_time;
+ break;
+ }
+ case IW_MODE_INFRA:
+ last_seq = &priv->last_seq_num;
+ last_frag = &priv->last_frag_num;
+ last_time = &priv->last_packet_time;
+ break;
+ default:
+ return 0;
+ }
+ if ((*last_seq == seq) &&
+ time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) {
+ if (*last_frag == frag)
+ goto drop;
+ if (*last_frag + 1 != frag)
+ /* out-of-order fragment */
+ goto drop;
+ } else
+ *last_seq = seq;
+
+ *last_frag = frag;
+ *last_time = jiffies;
+ return 0;
+
+ drop:
+ /* Comment this line now since we observed the card receives
+ * duplicate packets but the FCTL_RETRY bit is not set in the
+ * IBSS mode with fragmentation enabled.
+ BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */
+ return 1;
+}
+
+static void ipw_handle_mgmt_packet(struct ipw_priv *priv,
+ struct ipw_rx_mem_buffer *rxb,
+ struct ieee80211_rx_stats *stats)
+{
+ struct sk_buff *skb = rxb->skb;
+ struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data;
+ struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *)
+ (skb->data + IPW_RX_FRAME_SIZE);
+
+ ieee80211_rx_mgt(priv->ieee, header, stats);
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
+ IEEE80211_STYPE_PROBE_RESP) ||
+ (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) ==
+ IEEE80211_STYPE_BEACON))) {
+ if (!memcmp(header->addr3, priv->bssid, ETH_ALEN))
+ ipw_add_station(priv, header->addr2);
+ }
+
+ if (priv->config & CFG_NET_STATS) {
+ IPW_DEBUG_HC("sending stat packet\n");
+
+ /* Set the size of the skb to the size of the full
+ * ipw header and 802.11 frame */
+ skb_put(skb, le16_to_cpu(pkt->u.frame.length) +
+ IPW_RX_FRAME_SIZE);
+
+ /* Advance past the ipw packet header to the 802.11 frame */
+ skb_pull(skb, IPW_RX_FRAME_SIZE);
+
+ /* Push the ieee80211_rx_stats before the 802.11 frame */
+ memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats));
+
+ skb->dev = priv->ieee->dev;
+
+ /* Point raw at the ieee80211_stats */
+ skb_reset_mac_header(skb);
+
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_80211_STATS);
+ memset(skb->cb, 0, sizeof(rxb->skb->cb));
+ netif_rx(skb);
+ rxb->skb = NULL;
+ }
+}
+
+/*
+ * Main entry function for recieving a packet with 80211 headers. This
+ * should be called when ever the FW has notified us that there is a new
+ * skb in the recieve queue.
+ */
+static void ipw_rx(struct ipw_priv *priv)
+{
+ struct ipw_rx_mem_buffer *rxb;
+ struct ipw_rx_packet *pkt;
+ struct ieee80211_hdr_4addr *header;
+ u32 r, w, i;
+ u8 network_packet;
+ u8 fill_rx = 0;
+
+ r = ipw_read32(priv, IPW_RX_READ_INDEX);
+ w = ipw_read32(priv, IPW_RX_WRITE_INDEX);
+ i = priv->rxq->read;
+
+ if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2))
+ fill_rx = 1;
+
+ while (i != r) {
+ rxb = priv->rxq->queue[i];
+ if (unlikely(rxb == NULL)) {
+ printk(KERN_CRIT "Queue not allocated!\n");
+ break;
+ }
+ priv->rxq->queue[i] = NULL;
+
+ pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
+ IPW_RX_BUF_SIZE,
+ PCI_DMA_FROMDEVICE);
+
+ pkt = (struct ipw_rx_packet *)rxb->skb->data;
+ IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n",
+ pkt->header.message_type,
+ pkt->header.rx_seq_num, pkt->header.control_bits);
+
+ switch (pkt->header.message_type) {
+ case RX_FRAME_TYPE: /* 802.11 frame */ {
+ struct ieee80211_rx_stats stats = {
+ .rssi = pkt->u.frame.rssi_dbm -
+ IPW_RSSI_TO_DBM,
+ .signal =
+ le16_to_cpu(pkt->u.frame.rssi_dbm) -
+ IPW_RSSI_TO_DBM + 0x100,
+ .noise =
+ le16_to_cpu(pkt->u.frame.noise),
+ .rate = pkt->u.frame.rate,
+ .mac_time = jiffies,
+ .received_channel =
+ pkt->u.frame.received_channel,
+ .freq =
+ (pkt->u.frame.
+ control & (1 << 0)) ?
+ IEEE80211_24GHZ_BAND :
+ IEEE80211_52GHZ_BAND,
+ .len = le16_to_cpu(pkt->u.frame.length),
+ };
+
+ if (stats.rssi != 0)
+ stats.mask |= IEEE80211_STATMASK_RSSI;
+ if (stats.signal != 0)
+ stats.mask |= IEEE80211_STATMASK_SIGNAL;
+ if (stats.noise != 0)
+ stats.mask |= IEEE80211_STATMASK_NOISE;
+ if (stats.rate != 0)
+ stats.mask |= IEEE80211_STATMASK_RATE;
+
+ priv->rx_packets++;
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev))
+ ipw_handle_promiscuous_rx(priv, rxb, &stats);
+#endif
+
+#ifdef CONFIG_IPW2200_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+#ifdef CONFIG_IPW2200_RADIOTAP
+
+ ipw_handle_data_packet_monitor(priv,
+ rxb,
+ &stats);
+#else
+ ipw_handle_data_packet(priv, rxb,
+ &stats);
+#endif
+ break;
+ }
+#endif
+
+ header =
+ (struct ieee80211_hdr_4addr *)(rxb->skb->
+ data +
+ IPW_RX_FRAME_SIZE);
+ /* TODO: Check Ad-Hoc dest/source and make sure
+ * that we are actually parsing these packets
+ * correctly -- we should probably use the
+ * frame control of the packet and disregard
+ * the current iw_mode */
+
+ network_packet =
+ is_network_packet(priv, header);
+ if (network_packet && priv->assoc_network) {
+ priv->assoc_network->stats.rssi =
+ stats.rssi;
+ priv->exp_avg_rssi =
+ exponential_average(priv->exp_avg_rssi,
+ stats.rssi, DEPTH_RSSI);
+ }
+
+ IPW_DEBUG_RX("Frame: len=%u\n",
+ le16_to_cpu(pkt->u.frame.length));
+
+ if (le16_to_cpu(pkt->u.frame.length) <
+ ieee80211_get_hdrlen(le16_to_cpu(
+ header->frame_ctl))) {
+ IPW_DEBUG_DROP
+ ("Received packet is too small. "
+ "Dropping.\n");
+ priv->ieee->stats.rx_errors++;
+ priv->wstats.discard.misc++;
+ break;
+ }
+
+ switch (WLAN_FC_GET_TYPE
+ (le16_to_cpu(header->frame_ctl))) {
+
+ case IEEE80211_FTYPE_MGMT:
+ ipw_handle_mgmt_packet(priv, rxb,
+ &stats);
+ break;
+
+ case IEEE80211_FTYPE_CTL:
+ break;
+
+ case IEEE80211_FTYPE_DATA:
+ if (unlikely(!network_packet ||
+ is_duplicate_packet(priv,
+ header)))
+ {
+ IPW_DEBUG_DROP("Dropping: "
+ "%pM, "
+ "%pM, "
+ "%pM\n",
+ header->addr1,
+ header->addr2,
+ header->addr3);
+ break;
+ }
+
+ ipw_handle_data_packet(priv, rxb,
+ &stats);
+
+ break;
+ }
+ break;
+ }
+
+ case RX_HOST_NOTIFICATION_TYPE:{
+ IPW_DEBUG_RX
+ ("Notification: subtype=%02X flags=%02X size=%d\n",
+ pkt->u.notification.subtype,
+ pkt->u.notification.flags,
+ le16_to_cpu(pkt->u.notification.size));
+ ipw_rx_notification(priv, &pkt->u.notification);
+ break;
+ }
+
+ default:
+ IPW_DEBUG_RX("Bad Rx packet of type %d\n",
+ pkt->header.message_type);
+ break;
+ }
+
+ /* For now we just don't re-use anything. We can tweak this
+ * later to try and re-use notification packets and SKBs that
+ * fail to Rx correctly */
+ if (rxb->skb != NULL) {
+ dev_kfree_skb_any(rxb->skb);
+ rxb->skb = NULL;
+ }
+
+ pci_unmap_single(priv->pci_dev, rxb->dma_addr,
+ IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE);
+ list_add_tail(&rxb->list, &priv->rxq->rx_used);
+
+ i = (i + 1) % RX_QUEUE_SIZE;
+
+ /* If there are a lot of unsued frames, restock the Rx queue
+ * so the ucode won't assert */
+ if (fill_rx) {
+ priv->rxq->read = i;
+ ipw_rx_queue_replenish(priv);
+ }
+ }
+
+ /* Backtrack one entry */
+ priv->rxq->read = i;
+ ipw_rx_queue_restock(priv);
+}
+
+#define DEFAULT_RTS_THRESHOLD 2304U
+#define MIN_RTS_THRESHOLD 1U
+#define MAX_RTS_THRESHOLD 2304U
+#define DEFAULT_BEACON_INTERVAL 100U
+#define DEFAULT_SHORT_RETRY_LIMIT 7U
+#define DEFAULT_LONG_RETRY_LIMIT 4U
+
+/**
+ * ipw_sw_reset
+ * @option: options to control different reset behaviour
+ * 0 = reset everything except the 'disable' module_param
+ * 1 = reset everything and print out driver info (for probe only)
+ * 2 = reset everything
+ */
+static int ipw_sw_reset(struct ipw_priv *priv, int option)
+{
+ int band, modulation;
+ int old_mode = priv->ieee->iw_mode;
+
+ /* Initialize module parameter values here */
+ priv->config = 0;
+
+ /* We default to disabling the LED code as right now it causes
+ * too many systems to lock up... */
+ if (!led)
+ priv->config |= CFG_NO_LED;
+
+ if (associate)
+ priv->config |= CFG_ASSOCIATE;
+ else
+ IPW_DEBUG_INFO("Auto associate disabled.\n");
+
+ if (auto_create)
+ priv->config |= CFG_ADHOC_CREATE;
+ else
+ IPW_DEBUG_INFO("Auto adhoc creation disabled.\n");
+
+ priv->config &= ~CFG_STATIC_ESSID;
+ priv->essid_len = 0;
+ memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
+
+ if (disable && option) {
+ priv->status |= STATUS_RF_KILL_SW;
+ IPW_DEBUG_INFO("Radio disabled.\n");
+ }
+
+ if (channel != 0) {
+ priv->config |= CFG_STATIC_CHANNEL;
+ priv->channel = channel;
+ IPW_DEBUG_INFO("Bind to static channel %d\n", channel);
+ /* TODO: Validate that provided channel is in range */
+ }
+#ifdef CONFIG_IPW2200_QOS
+ ipw_qos_init(priv, qos_enable, qos_burst_enable,
+ burst_duration_CCK, burst_duration_OFDM);
+#endif /* CONFIG_IPW2200_QOS */
+
+ switch (mode) {
+ case 1:
+ priv->ieee->iw_mode = IW_MODE_ADHOC;
+ priv->net_dev->type = ARPHRD_ETHER;
+
+ break;
+#ifdef CONFIG_IPW2200_MONITOR
+ case 2:
+ priv->ieee->iw_mode = IW_MODE_MONITOR;
+#ifdef CONFIG_IPW2200_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
+ priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
+ break;
+#endif
+ default:
+ case 0:
+ priv->net_dev->type = ARPHRD_ETHER;
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ break;
+ }
+
+ if (hwcrypto) {
+ priv->ieee->host_encrypt = 0;
+ priv->ieee->host_encrypt_msdu = 0;
+ priv->ieee->host_decrypt = 0;
+ priv->ieee->host_mc_decrypt = 0;
+ }
+ IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off");
+
+ /* IPW2200/2915 is abled to do hardware fragmentation. */
+ priv->ieee->host_open_frag = 0;
+
+ if ((priv->pci_dev->device == 0x4223) ||
+ (priv->pci_dev->device == 0x4224)) {
+ if (option == 1)
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2915ABG Network "
+ "Connection\n");
+ priv->ieee->abg_true = 1;
+ band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND;
+ modulation = IEEE80211_OFDM_MODULATION |
+ IEEE80211_CCK_MODULATION;
+ priv->adapter = IPW_2915ABG;
+ priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B;
+ } else {
+ if (option == 1)
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel PRO/Wireless 2200BG Network "
+ "Connection\n");
+
+ priv->ieee->abg_true = 0;
+ band = IEEE80211_24GHZ_BAND;
+ modulation = IEEE80211_OFDM_MODULATION |
+ IEEE80211_CCK_MODULATION;
+ priv->adapter = IPW_2200BG;
+ priv->ieee->mode = IEEE_G | IEEE_B;
+ }
+
+ priv->ieee->freq_band = band;
+ priv->ieee->modulation = modulation;
+
+ priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK;
+
+ priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
+ priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
+
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+ priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT;
+
+ /* If power management is turned on, default to AC mode */
+ priv->power_mode = IPW_POWER_AC;
+ priv->tx_power = IPW_TX_POWER_DEFAULT;
+
+ return old_mode == priv->ieee->iw_mode;
+}
+
+/*
+ * This file defines the Wireless Extension handlers. It does not
+ * define any methods of hardware manipulation and relies on the
+ * functions defined in ipw_main to provide the HW interaction.
+ *
+ * The exception to this is the use of the ipw_get_ordinal()
+ * function used to poll the hardware vs. making unecessary calls.
+ *
+ */
+
+static int ipw_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ if (priv->status & STATUS_RF_KILL_MASK)
+ strcpy(wrqu->name, "radio off");
+ else if (!(priv->status & STATUS_ASSOCIATED))
+ strcpy(wrqu->name, "unassociated");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
+ ipw_modes[priv->assoc_request.ieee_mode]);
+ IPW_DEBUG_WX("Name: %s\n", wrqu->name);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
+{
+ if (channel == 0) {
+ IPW_DEBUG_INFO("Setting channel to ANY (0)\n");
+ priv->config &= ~CFG_STATIC_CHANNEL;
+ IPW_DEBUG_ASSOC("Attempting to associate with new "
+ "parameters.\n");
+ ipw_associate(priv);
+ return 0;
+ }
+
+ priv->config |= CFG_STATIC_CHANNEL;
+
+ if (priv->channel == channel) {
+ IPW_DEBUG_INFO("Request to set channel to current value (%d)\n",
+ channel);
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel);
+ priv->channel = channel;
+
+#ifdef CONFIG_IPW2200_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
+ int i;
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_SCAN("Scan abort triggered due to "
+ "channel change.\n");
+ ipw_abort_scan(priv);
+ }
+
+ for (i = 1000; i && (priv->status & STATUS_SCANNING); i--)
+ udelay(10);
+
+ if (priv->status & STATUS_SCANNING)
+ IPW_DEBUG_SCAN("Still scanning...\n");
+ else
+ IPW_DEBUG_SCAN("Took %dms to abort current scan\n",
+ 1000 - i);
+
+ return 0;
+ }
+#endif /* CONFIG_IPW2200_MONITOR */
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+
+ return 0;
+}
+
+static int ipw_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ struct iw_freq *fwrq = &wrqu->freq;
+ int ret = 0, i;
+ u8 channel, flags;
+ int band;
+
+ if (fwrq->m == 0) {
+ IPW_DEBUG_WX("SET Freq/Channel -> any\n");
+ mutex_lock(&priv->mutex);
+ ret = ipw_set_channel(priv, 0);
+ mutex_unlock(&priv->mutex);
+ return ret;
+ }
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m);
+ if (channel == 0)
+ return -EINVAL;
+ } else
+ channel = fwrq->m;
+
+ if (!(band = ieee80211_is_valid_channel(priv->ieee, channel)))
+ return -EINVAL;
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ i = ieee80211_channel_to_index(priv->ieee, channel);
+ if (i == -1)
+ return -EINVAL;
+
+ flags = (band == IEEE80211_24GHZ_BAND) ?
+ geo->bg[i].flags : geo->a[i].flags;
+ if (flags & IEEE80211_CH_PASSIVE_ONLY) {
+ IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n");
+ return -EINVAL;
+ }
+ }
+
+ IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m);
+ mutex_lock(&priv->mutex);
+ ret = ipw_set_channel(priv, channel);
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int ipw_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ wrqu->freq.e = 0;
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured CHANNEL then return that; otherwise return ANY */
+ mutex_lock(&priv->mutex);
+ if (priv->config & CFG_STATIC_CHANNEL ||
+ priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) {
+ int i;
+
+ i = ieee80211_channel_to_index(priv->ieee, priv->channel);
+ BUG_ON(i == -1);
+ wrqu->freq.e = 1;
+
+ switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) {
+ case IEEE80211_52GHZ_BAND:
+ wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000;
+ break;
+
+ case IEEE80211_24GHZ_BAND:
+ wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000;
+ break;
+
+ default:
+ BUG();
+ }
+ } else
+ wrqu->freq.m = 0;
+
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("GET Freq/Channel -> %d \n", priv->channel);
+ return 0;
+}
+
+static int ipw_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode);
+
+ switch (wrqu->mode) {
+#ifdef CONFIG_IPW2200_MONITOR
+ case IW_MODE_MONITOR:
+#endif
+ case IW_MODE_ADHOC:
+ case IW_MODE_INFRA:
+ break;
+ case IW_MODE_AUTO:
+ wrqu->mode = IW_MODE_INFRA;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (wrqu->mode == priv->ieee->iw_mode)
+ return 0;
+
+ mutex_lock(&priv->mutex);
+
+ ipw_sw_reset(priv, 0);
+
+#ifdef CONFIG_IPW2200_MONITOR
+ if (priv->ieee->iw_mode == IW_MODE_MONITOR)
+ priv->net_dev->type = ARPHRD_ETHER;
+
+ if (wrqu->mode == IW_MODE_MONITOR)
+#ifdef CONFIG_IPW2200_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
+ priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
+#endif /* CONFIG_IPW2200_MONITOR */
+
+ /* Free the existing firmware and reset the fw_loaded
+ * flag so ipw_load() will bring in the new firmawre */
+ free_firmware();
+
+ priv->ieee->iw_mode = wrqu->mode;
+
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ mutex_unlock(&priv->mutex);
+ return err;
+}
+
+static int ipw_wx_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->mode = priv->ieee->iw_mode;
+ IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+/* Values are in microsecond */
+static const s32 timeout_duration[] = {
+ 350000,
+ 250000,
+ 75000,
+ 37000,
+ 25000,
+};
+
+static const s32 period_duration[] = {
+ 400000,
+ 700000,
+ 1000000,
+ 1000000,
+ 1000000
+};
+
+static int ipw_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee);
+ int i = 0, j;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* 54Mbs == ~27 Mb/s real (802.11g) */
+ range->throughput = 27 * 1000 * 1000;
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70;
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 0; /* FIXME to real average level */
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+ mutex_lock(&priv->mutex);
+ range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES);
+
+ for (i = 0; i < range->num_bitrates; i++)
+ range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) *
+ 500000;
+
+ range->max_rts = DEFAULT_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = WEP_KEYS;
+
+ /* Set the Wireless Extension versions */
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 18;
+
+ i = 0;
+ if (priv->ieee->mode & (IEEE_B | IEEE_G)) {
+ for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) {
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
+ (geo->bg[j].flags & IEEE80211_CH_PASSIVE_ONLY))
+ continue;
+
+ range->freq[i].i = geo->bg[j].channel;
+ range->freq[i].m = geo->bg[j].freq * 100000;
+ range->freq[i].e = 1;
+ i++;
+ }
+ }
+
+ if (priv->ieee->mode & IEEE_A) {
+ for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) {
+ if ((priv->ieee->iw_mode == IW_MODE_ADHOC) &&
+ (geo->a[j].flags & IEEE80211_CH_PASSIVE_ONLY))
+ continue;
+
+ range->freq[i].i = geo->a[j].channel;
+ range->freq[i].m = geo->a[j].freq * 100000;
+ range->freq[i].e = 1;
+ i++;
+ }
+ }
+
+ range->num_channels = i;
+ range->num_frequency = i;
+
+ mutex_unlock(&priv->mutex);
+
+ /* Event capability (kernel + driver) */
+ range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+ IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+ IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+ IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+ range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
+
+ IPW_DEBUG_WX("GET Range\n");
+ return 0;
+}
+
+static int ipw_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ static const unsigned char any[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ static const unsigned char off[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+ mutex_lock(&priv->mutex);
+ if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) ||
+ !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ /* we disable mandatory BSSID association */
+ IPW_DEBUG_WX("Setting AP BSSID to ANY\n");
+ priv->config &= ~CFG_STATIC_BSSID;
+ IPW_DEBUG_ASSOC("Attempting to associate with new "
+ "parameters.\n");
+ ipw_associate(priv);
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ priv->config |= CFG_STATIC_BSSID;
+ if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) {
+ IPW_DEBUG_WX("BSSID set to current BSSID.\n");
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n",
+ wrqu->ap_addr.sa_data);
+
+ memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured BSSID then return that; otherwise return ANY */
+ mutex_lock(&priv->mutex);
+ if (priv->config & CFG_STATIC_BSSID ||
+ priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN);
+ } else
+ memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
+
+ IPW_DEBUG_WX("Getting WAP BSSID: %pM\n",
+ wrqu->ap_addr.sa_data);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int length;
+ DECLARE_SSID_BUF(ssid);
+
+ mutex_lock(&priv->mutex);
+
+ if (!wrqu->essid.flags)
+ {
+ IPW_DEBUG_WX("Setting ESSID to ANY\n");
+ ipw_disassociate(priv);
+ priv->config &= ~CFG_STATIC_ESSID;
+ ipw_associate(priv);
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
+
+ priv->config |= CFG_STATIC_ESSID;
+
+ if (priv->essid_len == length && !memcmp(priv->essid, extra, length)
+ && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) {
+ IPW_DEBUG_WX("ESSID set to current ESSID.\n");
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n",
+ print_ssid(ssid, extra, length), length);
+
+ priv->essid_len = length;
+ memcpy(priv->essid, extra, priv->essid_len);
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ DECLARE_SSID_BUF(ssid);
+
+ /* If we are associated, trying to associate, or have a statically
+ * configured ESSID then return that; otherwise return ANY */
+ mutex_lock(&priv->mutex);
+ if (priv->config & CFG_STATIC_ESSID ||
+ priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) {
+ IPW_DEBUG_WX("Getting essid: '%s'\n",
+ print_ssid(ssid, priv->essid, priv->essid_len));
+ memcpy(extra, priv->essid, priv->essid_len);
+ wrqu->essid.length = priv->essid_len;
+ wrqu->essid.flags = 1; /* active */
+ } else {
+ IPW_DEBUG_WX("Getting essid: ANY\n");
+ wrqu->essid.length = 0;
+ wrqu->essid.flags = 0; /* active */
+ }
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ IPW_DEBUG_WX("Setting nick to '%s'\n", extra);
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+ mutex_lock(&priv->mutex);
+ wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+ IPW_DEBUG_TRACE("<<\n");
+ mutex_unlock(&priv->mutex);
+ return 0;
+
+}
+
+static int ipw_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_WX("Getting nick\n");
+ mutex_lock(&priv->mutex);
+ wrqu->data.length = strlen(priv->nick);
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_set_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value);
+ IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value);
+ mutex_lock(&priv->mutex);
+
+ if (wrqu->sens.fixed == 0)
+ {
+ priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT;
+ priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT;
+ goto out;
+ }
+ if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) ||
+ (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ priv->roaming_threshold = wrqu->sens.value;
+ priv->disassociate_threshold = 3*wrqu->sens.value;
+ out:
+ mutex_unlock(&priv->mutex);
+ return err;
+}
+
+static int ipw_wx_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->sens.fixed = 1;
+ wrqu->sens.value = priv->roaming_threshold;
+ mutex_unlock(&priv->mutex);
+
+ IPW_DEBUG_WX("GET roaming threshold -> %s %d \n",
+ wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ /* TODO: We should use semaphores or locks for access to priv */
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ u32 target_rate = wrqu->bitrate.value;
+ u32 fixed, mask;
+
+ /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */
+ /* value = X, fixed = 1 means only rate X */
+ /* value = X, fixed = 0 means all rates lower equal X */
+
+ if (target_rate == -1) {
+ fixed = 0;
+ mask = IEEE80211_DEFAULT_RATES_MASK;
+ /* Now we should reassociate */
+ goto apply;
+ }
+
+ mask = 0;
+ fixed = wrqu->bitrate.fixed;
+
+ if (target_rate == 1000000 || !fixed)
+ mask |= IEEE80211_CCK_RATE_1MB_MASK;
+ if (target_rate == 1000000)
+ goto apply;
+
+ if (target_rate == 2000000 || !fixed)
+ mask |= IEEE80211_CCK_RATE_2MB_MASK;
+ if (target_rate == 2000000)
+ goto apply;
+
+ if (target_rate == 5500000 || !fixed)
+ mask |= IEEE80211_CCK_RATE_5MB_MASK;
+ if (target_rate == 5500000)
+ goto apply;
+
+ if (target_rate == 6000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_6MB_MASK;
+ if (target_rate == 6000000)
+ goto apply;
+
+ if (target_rate == 9000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_9MB_MASK;
+ if (target_rate == 9000000)
+ goto apply;
+
+ if (target_rate == 11000000 || !fixed)
+ mask |= IEEE80211_CCK_RATE_11MB_MASK;
+ if (target_rate == 11000000)
+ goto apply;
+
+ if (target_rate == 12000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_12MB_MASK;
+ if (target_rate == 12000000)
+ goto apply;
+
+ if (target_rate == 18000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_18MB_MASK;
+ if (target_rate == 18000000)
+ goto apply;
+
+ if (target_rate == 24000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_24MB_MASK;
+ if (target_rate == 24000000)
+ goto apply;
+
+ if (target_rate == 36000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_36MB_MASK;
+ if (target_rate == 36000000)
+ goto apply;
+
+ if (target_rate == 48000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_48MB_MASK;
+ if (target_rate == 48000000)
+ goto apply;
+
+ if (target_rate == 54000000 || !fixed)
+ mask |= IEEE80211_OFDM_RATE_54MB_MASK;
+ if (target_rate == 54000000)
+ goto apply;
+
+ IPW_DEBUG_WX("invalid rate specified, returning error\n");
+ return -EINVAL;
+
+ apply:
+ IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n",
+ mask, fixed ? "fixed" : "sub-rates");
+ mutex_lock(&priv->mutex);
+ if (mask == IEEE80211_DEFAULT_RATES_MASK) {
+ priv->config &= ~CFG_FIXED_RATE;
+ ipw_set_fixed_rate(priv, priv->ieee->mode);
+ } else
+ priv->config |= CFG_FIXED_RATE;
+
+ if (priv->rates_mask == mask) {
+ IPW_DEBUG_WX("Mask set to current mask.\n");
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ priv->rates_mask = mask;
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->bitrate.value = priv->last_rate;
+ wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0;
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("GET Rate -> %d \n", wrqu->bitrate.value);
+ return 0;
+}
+
+static int ipw_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ if (wrqu->rts.disabled || !wrqu->rts.fixed)
+ priv->rts_threshold = DEFAULT_RTS_THRESHOLD;
+ else {
+ if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
+ wrqu->rts.value > MAX_RTS_THRESHOLD) {
+ mutex_unlock(&priv->mutex);
+ return -EINVAL;
+ }
+ priv->rts_threshold = wrqu->rts.value;
+ }
+
+ ipw_send_rts_threshold(priv, priv->rts_threshold);
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("SET RTS Threshold -> %d \n", priv->rts_threshold);
+ return 0;
+}
+
+static int ipw_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->rts.value = priv->rts_threshold;
+ wrqu->rts.fixed = 0; /* no auto select */
+ wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("GET RTS Threshold -> %d \n", wrqu->rts.value);
+ return 0;
+}
+
+static int ipw_wx_set_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ mutex_lock(&priv->mutex);
+ if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) {
+ err = -EINPROGRESS;
+ goto out;
+ }
+
+ if (!wrqu->power.fixed)
+ wrqu->power.value = IPW_TX_POWER_DEFAULT;
+
+ if (wrqu->power.flags != IW_TXPOW_DBM) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if ((wrqu->power.value > IPW_TX_POWER_MAX) ||
+ (wrqu->power.value < IPW_TX_POWER_MIN)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ priv->tx_power = wrqu->power.value;
+ err = ipw_set_tx_power(priv);
+ out:
+ mutex_unlock(&priv->mutex);
+ return err;
+}
+
+static int ipw_wx_get_txpow(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->power.value = priv->tx_power;
+ wrqu->power.fixed = 1;
+ wrqu->power.flags = IW_TXPOW_DBM;
+ wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0;
+ mutex_unlock(&priv->mutex);
+
+ IPW_DEBUG_WX("GET TX Power -> %s %d \n",
+ wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ if (wrqu->frag.disabled || !wrqu->frag.fixed)
+ priv->ieee->fts = DEFAULT_FTS;
+ else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD) {
+ mutex_unlock(&priv->mutex);
+ return -EINVAL;
+ }
+
+ priv->ieee->fts = wrqu->frag.value & ~0x1;
+ }
+
+ ipw_send_frag_threshold(priv, wrqu->frag.value);
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("SET Frag Threshold -> %d \n", wrqu->frag.value);
+ return 0;
+}
+
+static int ipw_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ wrqu->frag.value = priv->ieee->fts;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS);
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("GET Frag Threshold -> %d \n", wrqu->frag.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ if (wrqu->retry.value < 0 || wrqu->retry.value >= 255)
+ return -EINVAL;
+
+ mutex_lock(&priv->mutex);
+ if (wrqu->retry.flags & IW_RETRY_SHORT)
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ else if (wrqu->retry.flags & IW_RETRY_LONG)
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ else {
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ }
+
+ ipw_send_retry_limit(priv, priv->short_retry_limit,
+ priv->long_retry_limit);
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n",
+ priv->short_retry_limit, priv->long_retry_limit);
+ return 0;
+}
+
+static int ipw_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ mutex_lock(&priv->mutex);
+ wrqu->retry.disabled = 0;
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ mutex_unlock(&priv->mutex);
+ return -EINVAL;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
+ wrqu->retry.value = priv->short_retry_limit;
+ } else {
+ wrqu->retry.flags = IW_RETRY_LIMIT;
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+ mutex_unlock(&priv->mutex);
+
+ IPW_DEBUG_WX("GET retry -> %d \n", wrqu->retry.value);
+
+ return 0;
+}
+
+static int ipw_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+ struct delayed_work *work = NULL;
+
+ mutex_lock(&priv->mutex);
+
+ priv->user_requested_scan = 1;
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ int len = min((int)req->essid_len,
+ (int)sizeof(priv->direct_scan_ssid));
+ memcpy(priv->direct_scan_ssid, req->essid, len);
+ priv->direct_scan_ssid_len = len;
+ work = &priv->request_direct_scan;
+ } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
+ work = &priv->request_passive_scan;
+ }
+ } else {
+ /* Normal active broadcast scan */
+ work = &priv->request_scan;
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ IPW_DEBUG_WX("Start scan\n");
+
+ queue_delayed_work(priv->workqueue, work, 0);
+
+ return 0;
+}
+
+static int ipw_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra);
+}
+
+static int ipw_wx_set_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int ret;
+ u32 cap = priv->capability;
+
+ mutex_lock(&priv->mutex);
+ ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key);
+
+ /* In IBSS mode, we need to notify the firmware to update
+ * the beacon info after we changed the capability. */
+ if (cap != priv->capability &&
+ priv->ieee->iw_mode == IW_MODE_ADHOC &&
+ priv->status & STATUS_ASSOCIATED)
+ ipw_disassociate(priv);
+
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int ipw_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key);
+}
+
+static int ipw_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int err;
+ mutex_lock(&priv->mutex);
+ if (wrqu->power.disabled) {
+ priv->power_mode = IPW_POWER_LEVEL(priv->power_mode);
+ err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM);
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ mutex_unlock(&priv->mutex);
+ return err;
+ }
+ IPW_DEBUG_WX("SET Power Management Mode -> off\n");
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_ON: /* If not specified */
+ case IW_POWER_MODE: /* If set all mask */
+ case IW_POWER_ALL_R: /* If explicitly state all */
+ break;
+ default: /* Otherwise we don't support it */
+ IPW_DEBUG_WX("SET PM Mode: %X not supported.\n",
+ wrqu->power.flags);
+ mutex_unlock(&priv->mutex);
+ return -EOPNOTSUPP;
+ }
+
+ /* If the user hasn't specified a power management mode yet, default
+ * to BATTERY */
+ if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC)
+ priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY;
+ else
+ priv->power_mode = IPW_POWER_ENABLED | priv->power_mode;
+
+ err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode));
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ mutex_unlock(&priv->mutex);
+ return err;
+ }
+
+ IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ if (!(priv->power_mode & IPW_POWER_ENABLED))
+ wrqu->power.disabled = 1;
+ else
+ wrqu->power.disabled = 0;
+
+ mutex_unlock(&priv->mutex);
+ IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode);
+
+ return 0;
+}
+
+static int ipw_wx_set_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int mode = *(int *)extra;
+ int err;
+
+ mutex_lock(&priv->mutex);
+ if ((mode < 1) || (mode > IPW_POWER_LIMIT))
+ mode = IPW_POWER_AC;
+
+ if (IPW_POWER_LEVEL(priv->power_mode) != mode) {
+ err = ipw_send_power_mode(priv, mode);
+ if (err) {
+ IPW_DEBUG_WX("failed setting power mode.\n");
+ mutex_unlock(&priv->mutex);
+ return err;
+ }
+ priv->power_mode = IPW_POWER_ENABLED | mode;
+ }
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+#define MAX_WX_STRING 80
+static int ipw_wx_get_powermode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int level = IPW_POWER_LEVEL(priv->power_mode);
+ char *p = extra;
+
+ p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level);
+
+ switch (level) {
+ case IPW_POWER_AC:
+ p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)");
+ break;
+ case IPW_POWER_BATTERY:
+ p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)");
+ break;
+ default:
+ p += snprintf(p, MAX_WX_STRING - (p - extra),
+ "(Timeout %dms, Period %dms)",
+ timeout_duration[level - 1] / 1000,
+ period_duration[level - 1] / 1000);
+ }
+
+ if (!(priv->power_mode & IPW_POWER_ENABLED))
+ p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF");
+
+ wrqu->data.length = p - extra + 1;
+
+ return 0;
+}
+
+static int ipw_wx_set_wireless_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int mode = *(int *)extra;
+ u8 band = 0, modulation = 0;
+
+ if (mode == 0 || mode & ~IEEE_MODE_MASK) {
+ IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode);
+ return -EINVAL;
+ }
+ mutex_lock(&priv->mutex);
+ if (priv->adapter == IPW_2915ABG) {
+ priv->ieee->abg_true = 1;
+ if (mode & IEEE_A) {
+ band |= IEEE80211_52GHZ_BAND;
+ modulation |= IEEE80211_OFDM_MODULATION;
+ } else
+ priv->ieee->abg_true = 0;
+ } else {
+ if (mode & IEEE_A) {
+ IPW_WARNING("Attempt to set 2200BG into "
+ "802.11a mode\n");
+ mutex_unlock(&priv->mutex);
+ return -EINVAL;
+ }
+
+ priv->ieee->abg_true = 0;
+ }
+
+ if (mode & IEEE_B) {
+ band |= IEEE80211_24GHZ_BAND;
+ modulation |= IEEE80211_CCK_MODULATION;
+ } else
+ priv->ieee->abg_true = 0;
+
+ if (mode & IEEE_G) {
+ band |= IEEE80211_24GHZ_BAND;
+ modulation |= IEEE80211_OFDM_MODULATION;
+ } else
+ priv->ieee->abg_true = 0;
+
+ priv->ieee->mode = mode;
+ priv->ieee->freq_band = band;
+ priv->ieee->modulation = modulation;
+ init_supported_rates(priv, &priv->rates);
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n");
+ if (!ipw_disassociate(priv)) {
+ ipw_send_supported_rates(priv, &priv->rates);
+ ipw_associate(priv);
+ }
+
+ /* Update the band LEDs */
+ ipw_led_band_on(priv);
+
+ IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n",
+ mode & IEEE_A ? 'a' : '.',
+ mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.');
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_wireless_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ switch (priv->ieee->mode) {
+ case IEEE_A:
+ strncpy(extra, "802.11a (1)", MAX_WX_STRING);
+ break;
+ case IEEE_B:
+ strncpy(extra, "802.11b (2)", MAX_WX_STRING);
+ break;
+ case IEEE_A | IEEE_B:
+ strncpy(extra, "802.11ab (3)", MAX_WX_STRING);
+ break;
+ case IEEE_G:
+ strncpy(extra, "802.11g (4)", MAX_WX_STRING);
+ break;
+ case IEEE_A | IEEE_G:
+ strncpy(extra, "802.11ag (5)", MAX_WX_STRING);
+ break;
+ case IEEE_B | IEEE_G:
+ strncpy(extra, "802.11bg (6)", MAX_WX_STRING);
+ break;
+ case IEEE_A | IEEE_B | IEEE_G:
+ strncpy(extra, "802.11abg (7)", MAX_WX_STRING);
+ break;
+ default:
+ strncpy(extra, "unknown", MAX_WX_STRING);
+ break;
+ }
+
+ IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra);
+
+ wrqu->data.length = strlen(extra) + 1;
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+
+static int ipw_wx_set_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int mode = *(int *)extra;
+ mutex_lock(&priv->mutex);
+ /* Switching from SHORT -> LONG requires a disassociation */
+ if (mode == 1) {
+ if (!(priv->config & CFG_PREAMBLE_LONG)) {
+ priv->config |= CFG_PREAMBLE_LONG;
+
+ /* Network configuration changed -- force [re]association */
+ IPW_DEBUG_ASSOC
+ ("[re]association triggered due to preamble change.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+ }
+ goto done;
+ }
+
+ if (mode == 0) {
+ priv->config &= ~CFG_PREAMBLE_LONG;
+ goto done;
+ }
+ mutex_unlock(&priv->mutex);
+ return -EINVAL;
+
+ done:
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static int ipw_wx_get_preamble(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+ if (priv->config & CFG_PREAMBLE_LONG)
+ snprintf(wrqu->name, IFNAMSIZ, "long (1)");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "auto (0)");
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+#ifdef CONFIG_IPW2200_MONITOR
+static int ipw_wx_set_monitor(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ mutex_lock(&priv->mutex);
+ IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]);
+ if (enable) {
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+#ifdef CONFIG_IPW2200_RADIOTAP
+ priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+#else
+ priv->net_dev->type = ARPHRD_IEEE80211;
+#endif
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ }
+
+ ipw_set_channel(priv, parms[1]);
+ } else {
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ mutex_unlock(&priv->mutex);
+ return 0;
+ }
+ priv->net_dev->type = ARPHRD_ETHER;
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ }
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+#endif /* CONFIG_IPW2200_MONITOR */
+
+static int ipw_wx_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ IPW_DEBUG_WX("RESET\n");
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ return 0;
+}
+
+static int ipw_wx_sw_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ union iwreq_data wrqu_sec = {
+ .encoding = {
+ .flags = IW_ENCODE_DISABLED,
+ },
+ };
+ int ret;
+
+ IPW_DEBUG_WX("SW_RESET\n");
+
+ mutex_lock(&priv->mutex);
+
+ ret = ipw_sw_reset(priv, 2);
+ if (!ret) {
+ free_firmware();
+ ipw_adapter_restart(priv);
+ }
+
+ /* The SW reset bit might have been toggled on by the 'disable'
+ * module parameter, so take appropriate action */
+ ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW);
+
+ mutex_unlock(&priv->mutex);
+ ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL);
+ mutex_lock(&priv->mutex);
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ /* Configuration likely changed -- force [re]association */
+ IPW_DEBUG_ASSOC("[re]association triggered due to sw "
+ "reset.\n");
+ if (!ipw_disassociate(priv))
+ ipw_associate(priv);
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+
+/* Rebase the WE IOCTLs to zero for the handler array */
+#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
+static iw_handler ipw_wx_handlers[] = {
+ IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
+ IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
+ IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
+ IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
+ IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
+ IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens,
+ IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens,
+ IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
+ IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
+ IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
+ IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
+ IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
+ IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
+ IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
+ IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
+ IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
+ IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
+ IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
+ IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
+ IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
+ IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
+ IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
+ IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
+ IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
+ IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
+ IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
+ IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
+ IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
+ IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
+ IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
+ IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
+ IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
+ IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
+ IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
+ IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
+ IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
+ IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
+ IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
+ IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
+ IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
+ IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
+};
+
+enum {
+ IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV,
+ IPW_PRIV_GET_POWER,
+ IPW_PRIV_SET_MODE,
+ IPW_PRIV_GET_MODE,
+ IPW_PRIV_SET_PREAMBLE,
+ IPW_PRIV_GET_PREAMBLE,
+ IPW_PRIV_RESET,
+ IPW_PRIV_SW_RESET,
+#ifdef CONFIG_IPW2200_MONITOR
+ IPW_PRIV_SET_MONITOR,
+#endif
+};
+
+static struct iw_priv_args ipw_priv_args[] = {
+ {
+ .cmd = IPW_PRIV_SET_POWER,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_power"},
+ {
+ .cmd = IPW_PRIV_GET_POWER,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_power"},
+ {
+ .cmd = IPW_PRIV_SET_MODE,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_mode"},
+ {
+ .cmd = IPW_PRIV_GET_MODE,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_mode"},
+ {
+ .cmd = IPW_PRIV_SET_PREAMBLE,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_preamble"},
+ {
+ .cmd = IPW_PRIV_GET_PREAMBLE,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ,
+ .name = "get_preamble"},
+ {
+ IPW_PRIV_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"},
+ {
+ IPW_PRIV_SW_RESET,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"},
+#ifdef CONFIG_IPW2200_MONITOR
+ {
+ IPW_PRIV_SET_MONITOR,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"},
+#endif /* CONFIG_IPW2200_MONITOR */
+};
+
+static iw_handler ipw_priv_handler[] = {
+ ipw_wx_set_powermode,
+ ipw_wx_get_powermode,
+ ipw_wx_set_wireless_mode,
+ ipw_wx_get_wireless_mode,
+ ipw_wx_set_preamble,
+ ipw_wx_get_preamble,
+ ipw_wx_reset,
+ ipw_wx_sw_reset,
+#ifdef CONFIG_IPW2200_MONITOR
+ ipw_wx_set_monitor,
+#endif
+};
+
+static struct iw_handler_def ipw_wx_handler_def = {
+ .standard = ipw_wx_handlers,
+ .num_standard = ARRAY_SIZE(ipw_wx_handlers),
+ .num_private = ARRAY_SIZE(ipw_priv_handler),
+ .num_private_args = ARRAY_SIZE(ipw_priv_args),
+ .private = ipw_priv_handler,
+ .private_args = ipw_priv_args,
+ .get_wireless_stats = ipw_get_wireless_stats,
+};
+
+/*
+ * Get wireless statistics.
+ * Called by /proc/net/wireless
+ * Also called by SIOCGIWSTATS
+ */
+static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+
+ wstats = &priv->wstats;
+
+ /* if hw is disabled, then ipw_get_ordinal() can't be called.
+ * netdev->get_wireless_stats seems to be called before fw is
+ * initialized. STATUS_ASSOCIATED will only be set if the hw is up
+ * and associated; if not associcated, the values are all meaningless
+ * anyway, so set them all to NULL and INVALID */
+ if (!(priv->status & STATUS_ASSOCIATED)) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+
+ wstats->qual.qual = priv->quality;
+ wstats->qual.level = priv->exp_avg_rssi;
+ wstats->qual.noise = priv->exp_avg_noise;
+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM;
+
+ wstats->miss.beacon = average_value(&priv->average_missed_beacons);
+ wstats->discard.retries = priv->last_tx_failures;
+ wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
+
+/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len))
+ goto fail_get_ordinal;
+ wstats->discard.retries += tx_retry; */
+
+ return wstats;
+}
+
+/* net device stuff */
+
+static void init_sys_config(struct ipw_sys_config *sys_config)
+{
+ memset(sys_config, 0, sizeof(struct ipw_sys_config));
+ sys_config->bt_coexistence = 0;
+ sys_config->answer_broadcast_ssid_probe = 0;
+ sys_config->accept_all_data_frames = 0;
+ sys_config->accept_non_directed_frames = 1;
+ sys_config->exclude_unicast_unencrypted = 0;
+ sys_config->disable_unicast_decryption = 1;
+ sys_config->exclude_multicast_unencrypted = 0;
+ sys_config->disable_multicast_decryption = 1;
+ if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B)
+ antenna = CFG_SYS_ANTENNA_BOTH;
+ sys_config->antenna_diversity = antenna;
+ sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */
+ sys_config->dot11g_auto_detection = 0;
+ sys_config->enable_cts_to_self = 0;
+ sys_config->bt_coexist_collision_thr = 0;
+ sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */
+ sys_config->silence_threshold = 0x1e;
+}
+
+static int ipw_net_open(struct net_device *dev)
+{
+ IPW_DEBUG_INFO("dev->open\n");
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int ipw_net_stop(struct net_device *dev)
+{
+ IPW_DEBUG_INFO("dev->close\n");
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/*
+todo:
+
+modify to send one tfd per fragment instead of using chunking. otherwise
+we need to heavily modify the ieee80211_skb_to_txb.
+*/
+
+static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb,
+ int pri)
+{
+ struct ieee80211_hdr_3addrqos *hdr = (struct ieee80211_hdr_3addrqos *)
+ txb->fragments[0]->data;
+ int i = 0;
+ struct tfd_frame *tfd;
+#ifdef CONFIG_IPW2200_QOS
+ int tx_id = ipw_get_tx_queue_number(priv, pri);
+ struct clx2_tx_queue *txq = &priv->txq[tx_id];
+#else
+ struct clx2_tx_queue *txq = &priv->txq[0];
+#endif
+ struct clx2_queue *q = &txq->q;
+ u8 id, hdr_len, unicast;
+ u16 remaining_bytes;
+ int fc;
+
+ if (!(priv->status & STATUS_ASSOCIATED))
+ goto drop;
+
+ hdr_len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ unicast = !is_multicast_ether_addr(hdr->addr1);
+ id = ipw_find_station(priv, hdr->addr1);
+ if (id == IPW_INVALID_STATION) {
+ id = ipw_add_station(priv, hdr->addr1);
+ if (id == IPW_INVALID_STATION) {
+ IPW_WARNING("Attempt to send data to "
+ "invalid cell: %pM\n",
+ hdr->addr1);
+ goto drop;
+ }
+ }
+ break;
+
+ case IW_MODE_INFRA:
+ default:
+ unicast = !is_multicast_ether_addr(hdr->addr3);
+ id = 0;
+ break;
+ }
+
+ tfd = &txq->bd[q->first_empty];
+ txq->txb[q->first_empty] = txb;
+ memset(tfd, 0, sizeof(*tfd));
+ tfd->u.data.station_number = id;
+
+ tfd->control_flags.message_type = TX_FRAME_TYPE;
+ tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK;
+
+ tfd->u.data.cmd_id = DINO_CMD_TX;
+ tfd->u.data.len = cpu_to_le16(txb->payload_size);
+ remaining_bytes = txb->payload_size;
+
+ if (priv->assoc_request.ieee_mode == IPW_B_MODE)
+ tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK;
+ else
+ tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM;
+
+ if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE)
+ tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS);
+
+ memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len);
+
+ if (likely(unicast))
+ tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
+
+ if (txb->encrypted && !priv->ieee->host_encrypt) {
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ /* XXX: ACK flag must be set for CCMP even if it
+ * is a multicast/broadcast packet, because CCMP
+ * group communication encrypted by GTK is
+ * actually done by the AP. */
+ if (!unicast)
+ tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD;
+
+ tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
+ tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM;
+ tfd->u.data.key_index = 0;
+ tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE;
+ break;
+ case SEC_LEVEL_2:
+ tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP;
+ tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP;
+ tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE;
+ break;
+ case SEC_LEVEL_1:
+ tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx;
+ if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <=
+ 40)
+ tfd->u.data.key_index |= DCT_WEP_KEY_64Bit;
+ else
+ tfd->u.data.key_index |= DCT_WEP_KEY_128Bit;
+ break;
+ case SEC_LEVEL_0:
+ break;
+ default:
+ printk(KERN_ERR "Unknow security level %d\n",
+ priv->ieee->sec.level);
+ break;
+ }
+ } else
+ /* No hardware encryption */
+ tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP;
+
+#ifdef CONFIG_IPW2200_QOS
+ if (fc & IEEE80211_STYPE_QOS_DATA)
+ ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data));
+#endif /* CONFIG_IPW2200_QOS */
+
+ /* payload */
+ tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2),
+ txb->nr_frags));
+ IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n",
+ txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks));
+ for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) {
+ IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n",
+ i, le32_to_cpu(tfd->u.data.num_chunks),
+ txb->fragments[i]->len - hdr_len);
+ IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n",
+ i, tfd->u.data.num_chunks,
+ txb->fragments[i]->len - hdr_len);
+ printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len,
+ txb->fragments[i]->len - hdr_len);
+
+ tfd->u.data.chunk_ptr[i] =
+ cpu_to_le32(pci_map_single
+ (priv->pci_dev,
+ txb->fragments[i]->data + hdr_len,
+ txb->fragments[i]->len - hdr_len,
+ PCI_DMA_TODEVICE));
+ tfd->u.data.chunk_len[i] =
+ cpu_to_le16(txb->fragments[i]->len - hdr_len);
+ }
+
+ if (i != txb->nr_frags) {
+ struct sk_buff *skb;
+ u16 remaining_bytes = 0;
+ int j;
+
+ for (j = i; j < txb->nr_frags; j++)
+ remaining_bytes += txb->fragments[j]->len - hdr_len;
+
+ printk(KERN_INFO "Trying to reallocate for %d bytes\n",
+ remaining_bytes);
+ skb = alloc_skb(remaining_bytes, GFP_ATOMIC);
+ if (skb != NULL) {
+ tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes);
+ for (j = i; j < txb->nr_frags; j++) {
+ int size = txb->fragments[j]->len - hdr_len;
+
+ printk(KERN_INFO "Adding frag %d %d...\n",
+ j, size);
+ memcpy(skb_put(skb, size),
+ txb->fragments[j]->data + hdr_len, size);
+ }
+ dev_kfree_skb_any(txb->fragments[i]);
+ txb->fragments[i] = skb;
+ tfd->u.data.chunk_ptr[i] =
+ cpu_to_le32(pci_map_single
+ (priv->pci_dev, skb->data,
+ remaining_bytes,
+ PCI_DMA_TODEVICE));
+
+ le32_add_cpu(&tfd->u.data.num_chunks, 1);
+ }
+ }
+
+ /* kick DMA */
+ q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd);
+ ipw_write32(priv, q->reg_w, q->first_empty);
+
+ if (ipw_tx_queue_space(q) < q->high_mark)
+ netif_stop_queue(priv->net_dev);
+
+ return NETDEV_TX_OK;
+
+ drop:
+ IPW_DEBUG_DROP("Silently dropping Tx packet.\n");
+ ieee80211_txb_free(txb);
+ return NETDEV_TX_OK;
+}
+
+static int ipw_net_is_queue_full(struct net_device *dev, int pri)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+#ifdef CONFIG_IPW2200_QOS
+ int tx_id = ipw_get_tx_queue_number(priv, pri);
+ struct clx2_tx_queue *txq = &priv->txq[tx_id];
+#else
+ struct clx2_tx_queue *txq = &priv->txq[0];
+#endif /* CONFIG_IPW2200_QOS */
+
+ if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark)
+ return 1;
+
+ return 0;
+}
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static void ipw_handle_promiscuous_tx(struct ipw_priv *priv,
+ struct ieee80211_txb *txb)
+{
+ struct ieee80211_rx_stats dummystats;
+ struct ieee80211_hdr *hdr;
+ u8 n;
+ u16 filter = priv->prom_priv->filter;
+ int hdr_only = 0;
+
+ if (filter & IPW_PROM_NO_TX)
+ return;
+
+ memset(&dummystats, 0, sizeof(dummystats));
+
+ /* Filtering of fragment chains is done agains the first fragment */
+ hdr = (void *)txb->fragments[0]->data;
+ if (ieee80211_is_management(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_MGMT)
+ return;
+ if (filter & IPW_PROM_MGMT_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_CTL)
+ return;
+ if (filter & IPW_PROM_CTL_HEADER_ONLY)
+ hdr_only = 1;
+ } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_control))) {
+ if (filter & IPW_PROM_NO_DATA)
+ return;
+ if (filter & IPW_PROM_DATA_HEADER_ONLY)
+ hdr_only = 1;
+ }
+
+ for(n=0; n<txb->nr_frags; ++n) {
+ struct sk_buff *src = txb->fragments[n];
+ struct sk_buff *dst;
+ struct ieee80211_radiotap_header *rt_hdr;
+ int len;
+
+ if (hdr_only) {
+ hdr = (void *)src->data;
+ len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control));
+ } else
+ len = src->len;
+
+ dst = alloc_skb(len + sizeof(*rt_hdr), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr));
+
+ rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION;
+ rt_hdr->it_pad = 0;
+ rt_hdr->it_present = 0; /* after all, it's just an idea */
+ rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+
+ *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16(
+ ieee80211chan2mhz(priv->channel));
+ if (priv->channel > 14) /* 802.11a */
+ *(__le16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_5GHZ);
+ else if (priv->ieee->mode == IEEE_B) /* 802.11b */
+ *(__le16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_CCK |
+ IEEE80211_CHAN_2GHZ);
+ else /* 802.11g */
+ *(__le16*)skb_put(dst, sizeof(u16)) =
+ cpu_to_le16(IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_2GHZ);
+
+ rt_hdr->it_len = cpu_to_le16(dst->len);
+
+ skb_copy_from_linear_data(src, skb_put(dst, len), len);
+
+ if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats))
+ dev_kfree_skb_any(dst);
+ }
+}
+#endif
+
+static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb,
+ struct net_device *dev, int pri)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ unsigned long flags;
+ int ret;
+
+ IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size);
+ spin_lock_irqsave(&priv->lock, flags);
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (rtap_iface && netif_running(priv->prom_net_dev))
+ ipw_handle_promiscuous_tx(priv, txb);
+#endif
+
+ ret = ipw_tx_skb(priv, txb, pri);
+ if (ret == NETDEV_TX_OK)
+ __ipw_led_activity_on(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static struct net_device_stats *ipw_net_get_stats(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee->stats.tx_packets = priv->tx_packets;
+ priv->ieee->stats.rx_packets = priv->rx_packets;
+ return &priv->ieee->stats;
+}
+
+static void ipw_net_set_multicast_list(struct net_device *dev)
+{
+
+}
+
+static int ipw_net_set_mac_address(struct net_device *dev, void *p)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ mutex_lock(&priv->mutex);
+ priv->config |= CFG_CUSTOM_MAC;
+ memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN);
+ printk(KERN_INFO "%s: Setting MAC to %pM\n",
+ priv->net_dev->name, priv->mac_addr);
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+static void ipw_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+ char vers[64];
+ char date[32];
+ u32 len;
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+
+ len = sizeof(vers);
+ ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len);
+ len = sizeof(date);
+ ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len);
+
+ snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)",
+ vers, date);
+ strcpy(info->bus_info, pci_name(p->pci_dev));
+ info->eedump_len = IPW_EEPROM_IMAGE_SIZE;
+}
+
+static u32 ipw_ethtool_get_link(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ return (priv->status & STATUS_ASSOCIATED) != 0;
+}
+
+static int ipw_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return IPW_EEPROM_IMAGE_SIZE;
+}
+
+static int ipw_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 * bytes)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+
+ if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
+ return -EINVAL;
+ mutex_lock(&p->mutex);
+ memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len);
+ mutex_unlock(&p->mutex);
+ return 0;
+}
+
+static int ipw_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 * bytes)
+{
+ struct ipw_priv *p = ieee80211_priv(dev);
+ int i;
+
+ if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE)
+ return -EINVAL;
+ mutex_lock(&p->mutex);
+ memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len);
+ for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++)
+ ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]);
+ mutex_unlock(&p->mutex);
+ return 0;
+}
+
+static const struct ethtool_ops ipw_ethtool_ops = {
+ .get_link = ipw_ethtool_get_link,
+ .get_drvinfo = ipw_ethtool_get_drvinfo,
+ .get_eeprom_len = ipw_ethtool_get_eeprom_len,
+ .get_eeprom = ipw_ethtool_get_eeprom,
+ .set_eeprom = ipw_ethtool_set_eeprom,
+};
+
+static irqreturn_t ipw_isr(int irq, void *data)
+{
+ struct ipw_priv *priv = data;
+ u32 inta, inta_mask;
+
+ if (!priv)
+ return IRQ_NONE;
+
+ spin_lock(&priv->irq_lock);
+
+ if (!(priv->status & STATUS_INT_ENABLED)) {
+ /* IRQ is disabled */
+ goto none;
+ }
+
+ inta = ipw_read32(priv, IPW_INTA_RW);
+ inta_mask = ipw_read32(priv, IPW_INTA_MASK_R);
+
+ if (inta == 0xFFFFFFFF) {
+ /* Hardware disappeared */
+ IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n");
+ goto none;
+ }
+
+ if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) {
+ /* Shared interrupt */
+ goto none;
+ }
+
+ /* tell the device to stop sending interrupts */
+ __ipw_disable_interrupts(priv);
+
+ /* ack current interrupts */
+ inta &= (IPW_INTA_MASK_ALL & inta_mask);
+ ipw_write32(priv, IPW_INTA_RW, inta);
+
+ /* Cache INTA value for our tasklet */
+ priv->isr_inta = inta;
+
+ tasklet_schedule(&priv->irq_tasklet);
+
+ spin_unlock(&priv->irq_lock);
+
+ return IRQ_HANDLED;
+ none:
+ spin_unlock(&priv->irq_lock);
+ return IRQ_NONE;
+}
+
+static void ipw_rf_kill(void *adapter)
+{
+ struct ipw_priv *priv = adapter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (rf_kill_active(priv)) {
+ IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n");
+ if (priv->workqueue)
+ queue_delayed_work(priv->workqueue,
+ &priv->rf_kill, 2 * HZ);
+ goto exit_unlock;
+ }
+
+ /* RF Kill is now disabled, so bring the device back up */
+
+ if (!(priv->status & STATUS_RF_KILL_MASK)) {
+ IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting "
+ "device\n");
+
+ /* we can not do an adapter restart while inside an irq lock */
+ queue_work(priv->workqueue, &priv->adapter_restart);
+ } else
+ IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still "
+ "enabled\n");
+
+ exit_unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipw_bg_rf_kill(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, rf_kill.work);
+ mutex_lock(&priv->mutex);
+ ipw_rf_kill(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_link_up(struct ipw_priv *priv)
+{
+ priv->last_seq_num = -1;
+ priv->last_frag_num = -1;
+ priv->last_packet_time = 0;
+
+ netif_carrier_on(priv->net_dev);
+
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
+ cancel_delayed_work(&priv->scan_event);
+ ipw_reset_stats(priv);
+ /* Ensure the rate is updated immediately */
+ priv->last_rate = ipw_get_current_rate(priv);
+ ipw_gather_stats(priv);
+ ipw_led_link_up(priv);
+ notify_wx_assoc_event(priv);
+
+ if (priv->config & CFG_BACKGROUND_SCAN)
+ queue_delayed_work(priv->workqueue, &priv->request_scan, HZ);
+}
+
+static void ipw_bg_link_up(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, link_up);
+ mutex_lock(&priv->mutex);
+ ipw_link_up(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_link_down(struct ipw_priv *priv)
+{
+ ipw_led_link_down(priv);
+ netif_carrier_off(priv->net_dev);
+ notify_wx_assoc_event(priv);
+
+ /* Cancel any queued work ... */
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
+ cancel_delayed_work(&priv->adhoc_check);
+ cancel_delayed_work(&priv->gather_stats);
+
+ ipw_reset_stats(priv);
+
+ if (!(priv->status & STATUS_EXIT_PENDING)) {
+ /* Queue up another scan... */
+ queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
+ } else
+ cancel_delayed_work(&priv->scan_event);
+}
+
+static void ipw_bg_link_down(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, link_down);
+ mutex_lock(&priv->mutex);
+ ipw_link_down(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv)
+{
+ int ret = 0;
+
+ priv->workqueue = create_workqueue(DRV_NAME);
+ init_waitqueue_head(&priv->wait_command_queue);
+ init_waitqueue_head(&priv->wait_state);
+
+ INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check);
+ INIT_WORK(&priv->associate, ipw_bg_associate);
+ INIT_WORK(&priv->disassociate, ipw_bg_disassociate);
+ INIT_WORK(&priv->system_config, ipw_system_config);
+ INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish);
+ INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart);
+ INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill);
+ INIT_WORK(&priv->up, ipw_bg_up);
+ INIT_WORK(&priv->down, ipw_bg_down);
+ INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
+ INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
+ INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
+ INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
+ INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
+ INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
+ INIT_WORK(&priv->roam, ipw_bg_roam);
+ INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check);
+ INIT_WORK(&priv->link_up, ipw_bg_link_up);
+ INIT_WORK(&priv->link_down, ipw_bg_link_down);
+ INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on);
+ INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off);
+ INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off);
+ INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network);
+
+#ifdef CONFIG_IPW2200_QOS
+ INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate);
+#endif /* CONFIG_IPW2200_QOS */
+
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ ipw_irq_tasklet, (unsigned long)priv);
+
+ return ret;
+}
+
+static void shim__set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (sec->flags & (1 << i)) {
+ priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
+ priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->ieee->sec.flags &= ~(1 << i);
+ else {
+ memcpy(priv->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ priv->ieee->sec.flags |= (1 << i);
+ }
+ priv->status |= STATUS_SECURITY_UPDATED;
+ } else if (sec->level != SEC_LEVEL_1)
+ priv->ieee->sec.flags &= ~(1 << i);
+ }
+
+ if (sec->flags & SEC_ACTIVE_KEY) {
+ if (sec->active_key <= 3) {
+ priv->ieee->sec.active_key = sec->active_key;
+ priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->ieee->sec.auth_mode != sec->auth_mode)) {
+ priv->ieee->sec.auth_mode = sec->auth_mode;
+ priv->ieee->sec.flags |= SEC_AUTH_MODE;
+ if (sec->auth_mode == WLAN_AUTH_SHARED_KEY)
+ priv->capability |= CAP_SHARED_KEY;
+ else
+ priv->capability &= ~CAP_SHARED_KEY;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
+ priv->ieee->sec.flags |= SEC_ENABLED;
+ priv->ieee->sec.enabled = sec->enabled;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ if (sec->enabled)
+ priv->capability |= CAP_PRIVACY_ON;
+ else
+ priv->capability &= ~CAP_PRIVACY_ON;
+ }
+
+ if (sec->flags & SEC_ENCRYPT)
+ priv->ieee->sec.encrypt = sec->encrypt;
+
+ if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
+ priv->ieee->sec.level = sec->level;
+ priv->ieee->sec.flags |= SEC_LEVEL;
+ priv->status |= STATUS_SECURITY_UPDATED;
+ }
+
+ if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
+ ipw_set_hwcrypto_keys(priv);
+
+ /* To match current functionality of ipw2100 (which works well w/
+ * various supplicants, we don't force a disassociate if the
+ * privacy capability changes ... */
+#if 0
+ if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) &&
+ (((priv->assoc_request.capability &
+ cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) ||
+ (!(priv->assoc_request.capability &
+ cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) {
+ IPW_DEBUG_ASSOC("Disassociating due to capability "
+ "change.\n");
+ ipw_disassociate(priv);
+ }
+#endif
+}
+
+static int init_supported_rates(struct ipw_priv *priv,
+ struct ipw_supported_rates *rates)
+{
+ /* TODO: Mask out rates based on priv->rates_mask */
+
+ memset(rates, 0, sizeof(*rates));
+ /* configure supported rates */
+ switch (priv->ieee->freq_band) {
+ case IEEE80211_52GHZ_BAND:
+ rates->ieee_mode = IPW_A_MODE;
+ rates->purpose = IPW_RATE_CAPABILITIES;
+ ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_OFDM_DEFAULT_RATES_MASK);
+ break;
+
+ default: /* Mixed or 2.4Ghz */
+ rates->ieee_mode = IPW_G_MODE;
+ rates->purpose = IPW_RATE_CAPABILITIES;
+ ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_CCK_DEFAULT_RATES_MASK);
+ if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION,
+ IEEE80211_OFDM_DEFAULT_RATES_MASK);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int ipw_config(struct ipw_priv *priv)
+{
+ /* This is only called from ipw_up, which resets/reloads the firmware
+ so, we don't need to first disable the card before we configure
+ it */
+ if (ipw_set_tx_power(priv))
+ goto error;
+
+ /* initialize adapter address */
+ if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr))
+ goto error;
+
+ /* set basic system config settings */
+ init_sys_config(&priv->sys_config);
+
+ /* Support Bluetooth if we have BT h/w on board, and user wants to.
+ * Does not support BT priority yet (don't abort or defer our Tx) */
+ if (bt_coexist) {
+ unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY];
+
+ if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG)
+ priv->sys_config.bt_coexistence
+ |= CFG_BT_COEXISTENCE_SIGNAL_CHNL;
+ if (bt_caps & EEPROM_SKU_CAP_BT_OOB)
+ priv->sys_config.bt_coexistence
+ |= CFG_BT_COEXISTENCE_OOB;
+ }
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+ }
+#endif
+
+ if (priv->ieee->iw_mode == IW_MODE_ADHOC)
+ priv->sys_config.answer_broadcast_ssid_probe = 1;
+ else
+ priv->sys_config.answer_broadcast_ssid_probe = 0;
+
+ if (ipw_send_system_config(priv))
+ goto error;
+
+ init_supported_rates(priv, &priv->rates);
+ if (ipw_send_supported_rates(priv, &priv->rates))
+ goto error;
+
+ /* Set request-to-send threshold */
+ if (priv->rts_threshold) {
+ if (ipw_send_rts_threshold(priv, priv->rts_threshold))
+ goto error;
+ }
+#ifdef CONFIG_IPW2200_QOS
+ IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n");
+ ipw_qos_activate(priv, NULL);
+#endif /* CONFIG_IPW2200_QOS */
+
+ if (ipw_set_random_seed(priv))
+ goto error;
+
+ /* final state transition to the RUN state */
+ if (ipw_send_host_complete(priv))
+ goto error;
+
+ priv->status |= STATUS_INIT;
+
+ ipw_led_init(priv);
+ ipw_led_radio_on(priv);
+ priv->notif_missed_beacons = 0;
+
+ /* Set hardware WEP key if it is configured. */
+ if ((priv->capability & CAP_PRIVACY_ON) &&
+ (priv->ieee->sec.level == SEC_LEVEL_1) &&
+ !(priv->ieee->host_encrypt || priv->ieee->host_decrypt))
+ ipw_set_hwcrypto_keys(priv);
+
+ return 0;
+
+ error:
+ return -EIO;
+}
+
+/*
+ * NOTE:
+ *
+ * These tables have been tested in conjunction with the
+ * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters.
+ *
+ * Altering this values, using it on other hardware, or in geographies
+ * not intended for resale of the above mentioned Intel adapters has
+ * not been tested.
+ *
+ * Remember to update the table in README.ipw2200 when changing this
+ * table.
+ *
+ */
+static const struct ieee80211_geo ipw_geos[] = {
+ { /* Restricted */
+ "---",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ },
+
+ { /* Custom US/Canada */
+ "ZZF",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 8,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Rest of World */
+ "ZZD",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}},
+ },
+
+ { /* Custom USA & Europe & High */
+ "ZZA",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149},
+ {5765, 153},
+ {5785, 157},
+ {5805, 161},
+ {5825, 165}},
+ },
+
+ { /* Custom NA & Europe */
+ "ZZB",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Custom Japan */
+ "ZZC",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 4,
+ .a = {{5170, 34}, {5190, 38},
+ {5210, 42}, {5230, 46}},
+ },
+
+ { /* Custom */
+ "ZZM",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ },
+
+ { /* Europe */
+ "ZZE",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}},
+ .a_channels = 19,
+ .a = {{5180, 36},
+ {5200, 40},
+ {5220, 44},
+ {5240, 48},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
+ {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
+ {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
+ {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
+ {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
+ {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
+ {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
+ {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
+ {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
+ {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
+ {5700, 140, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Custom Japan */
+ "ZZJ",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}},
+ .a_channels = 4,
+ .a = {{5170, 34}, {5190, 38},
+ {5210, 42}, {5230, 46}},
+ },
+
+ { /* Rest of World */
+ "ZZR",
+ .bg_channels = 14,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}, {2467, 12},
+ {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY |
+ IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* High Band */
+ "ZZH",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
+ {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
+ .a_channels = 4,
+ .a = {{5745, 149}, {5765, 153},
+ {5785, 157}, {5805, 161}},
+ },
+
+ { /* Custom Europe */
+ "ZZG",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12}, {2472, 13}},
+ .a_channels = 4,
+ .a = {{5180, 36}, {5200, 40},
+ {5220, 44}, {5240, 48}},
+ },
+
+ { /* Europe */
+ "ZZK",
+ .bg_channels = 13,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11},
+ {2467, 12, IEEE80211_CH_PASSIVE_ONLY},
+ {2472, 13, IEEE80211_CH_PASSIVE_ONLY}},
+ .a_channels = 24,
+ .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
+ {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
+ {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
+ {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5500, 100, IEEE80211_CH_PASSIVE_ONLY},
+ {5520, 104, IEEE80211_CH_PASSIVE_ONLY},
+ {5540, 108, IEEE80211_CH_PASSIVE_ONLY},
+ {5560, 112, IEEE80211_CH_PASSIVE_ONLY},
+ {5580, 116, IEEE80211_CH_PASSIVE_ONLY},
+ {5600, 120, IEEE80211_CH_PASSIVE_ONLY},
+ {5620, 124, IEEE80211_CH_PASSIVE_ONLY},
+ {5640, 128, IEEE80211_CH_PASSIVE_ONLY},
+ {5660, 132, IEEE80211_CH_PASSIVE_ONLY},
+ {5680, 136, IEEE80211_CH_PASSIVE_ONLY},
+ {5700, 140, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ },
+
+ { /* Europe */
+ "ZZL",
+ .bg_channels = 11,
+ .bg = {{2412, 1}, {2417, 2}, {2422, 3},
+ {2427, 4}, {2432, 5}, {2437, 6},
+ {2442, 7}, {2447, 8}, {2452, 9},
+ {2457, 10}, {2462, 11}},
+ .a_channels = 13,
+ .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY},
+ {5200, 40, IEEE80211_CH_PASSIVE_ONLY},
+ {5220, 44, IEEE80211_CH_PASSIVE_ONLY},
+ {5240, 48, IEEE80211_CH_PASSIVE_ONLY},
+ {5260, 52, IEEE80211_CH_PASSIVE_ONLY},
+ {5280, 56, IEEE80211_CH_PASSIVE_ONLY},
+ {5300, 60, IEEE80211_CH_PASSIVE_ONLY},
+ {5320, 64, IEEE80211_CH_PASSIVE_ONLY},
+ {5745, 149, IEEE80211_CH_PASSIVE_ONLY},
+ {5765, 153, IEEE80211_CH_PASSIVE_ONLY},
+ {5785, 157, IEEE80211_CH_PASSIVE_ONLY},
+ {5805, 161, IEEE80211_CH_PASSIVE_ONLY},
+ {5825, 165, IEEE80211_CH_PASSIVE_ONLY}},
+ }
+};
+
+#define MAX_HW_RESTARTS 5
+static int ipw_up(struct ipw_priv *priv)
+{
+ int rc, i, j;
+
+ if (priv->status & STATUS_EXIT_PENDING)
+ return -EIO;
+
+ if (cmdlog && !priv->cmdlog) {
+ priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog),
+ GFP_KERNEL);
+ if (priv->cmdlog == NULL) {
+ IPW_ERROR("Error allocating %d command log entries.\n",
+ cmdlog);
+ return -ENOMEM;
+ } else {
+ priv->cmdlog_len = cmdlog;
+ }
+ }
+
+ for (i = 0; i < MAX_HW_RESTARTS; i++) {
+ /* Load the microcode, firmware, and eeprom.
+ * Also start the clocks. */
+ rc = ipw_load(priv);
+ if (rc) {
+ IPW_ERROR("Unable to load firmware: %d\n", rc);
+ return rc;
+ }
+
+ ipw_init_ordinals(priv);
+ if (!(priv->config & CFG_CUSTOM_MAC))
+ eeprom_parse_mac(priv, priv->mac_addr);
+ memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+ for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
+ if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
+ ipw_geos[j].name, 3))
+ break;
+ }
+ if (j == ARRAY_SIZE(ipw_geos)) {
+ IPW_WARNING("SKU [%c%c%c] not recognized.\n",
+ priv->eeprom[EEPROM_COUNTRY_CODE + 0],
+ priv->eeprom[EEPROM_COUNTRY_CODE + 1],
+ priv->eeprom[EEPROM_COUNTRY_CODE + 2]);
+ j = 0;
+ }
+ if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) {
+ IPW_WARNING("Could not set geography.");
+ return 0;
+ }
+
+ if (priv->status & STATUS_RF_KILL_SW) {
+ IPW_WARNING("Radio disabled by module parameter.\n");
+ return 0;
+ } else if (rf_kill_active(priv)) {
+ IPW_WARNING("Radio Frequency Kill Switch is On:\n"
+ "Kill switch must be turned off for "
+ "wireless networking to work.\n");
+ queue_delayed_work(priv->workqueue, &priv->rf_kill,
+ 2 * HZ);
+ return 0;
+ }
+
+ rc = ipw_config(priv);
+ if (!rc) {
+ IPW_DEBUG_INFO("Configured device on count %i\n", i);
+
+ /* If configure to try and auto-associate, kick
+ * off a scan. */
+ queue_delayed_work(priv->workqueue,
+ &priv->request_scan, 0);
+
+ return 0;
+ }
+
+ IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc);
+ IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n",
+ i, MAX_HW_RESTARTS);
+
+ /* We had an error bringing up the hardware, so take it
+ * all the way back down so we can try again */
+ ipw_down(priv);
+ }
+
+ /* tried to restart and config the device for as long as our
+ * patience could withstand */
+ IPW_ERROR("Unable to initialize device after %d attempts.\n", i);
+
+ return -EIO;
+}
+
+static void ipw_bg_up(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, up);
+ mutex_lock(&priv->mutex);
+ ipw_up(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void ipw_deinit(struct ipw_priv *priv)
+{
+ int i;
+
+ if (priv->status & STATUS_SCANNING) {
+ IPW_DEBUG_INFO("Aborting scan during shutdown.\n");
+ ipw_abort_scan(priv);
+ }
+
+ if (priv->status & STATUS_ASSOCIATED) {
+ IPW_DEBUG_INFO("Disassociating during shutdown.\n");
+ ipw_disassociate(priv);
+ }
+
+ ipw_led_shutdown(priv);
+
+ /* Wait up to 1s for status to change to not scanning and not
+ * associated (disassociation can take a while for a ful 802.11
+ * exchange */
+ for (i = 1000; i && (priv->status &
+ (STATUS_DISASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_SCANNING)); i--)
+ udelay(10);
+
+ if (priv->status & (STATUS_DISASSOCIATING |
+ STATUS_ASSOCIATED | STATUS_SCANNING))
+ IPW_DEBUG_INFO("Still associated or scanning...\n");
+ else
+ IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i);
+
+ /* Attempt to disable the card */
+ ipw_send_card_disable(priv, 0);
+
+ priv->status &= ~STATUS_INIT;
+}
+
+static void ipw_down(struct ipw_priv *priv)
+{
+ int exit_pending = priv->status & STATUS_EXIT_PENDING;
+
+ priv->status |= STATUS_EXIT_PENDING;
+
+ if (ipw_is_init(priv))
+ ipw_deinit(priv);
+
+ /* Wipe out the EXIT_PENDING status bit if we are not actually
+ * exiting the module */
+ if (!exit_pending)
+ priv->status &= ~STATUS_EXIT_PENDING;
+
+ /* tell the device to stop sending interrupts */
+ ipw_disable_interrupts(priv);
+
+ /* Clear all bits but the RF Kill */
+ priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING;
+ netif_carrier_off(priv->net_dev);
+
+ ipw_stop_nic(priv);
+
+ ipw_led_radio_off(priv);
+}
+
+static void ipw_bg_down(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, down);
+ mutex_lock(&priv->mutex);
+ ipw_down(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+/* Called by register_netdev() */
+static int ipw_net_init(struct net_device *dev)
+{
+ struct ipw_priv *priv = ieee80211_priv(dev);
+ mutex_lock(&priv->mutex);
+
+ if (ipw_up(priv)) {
+ mutex_unlock(&priv->mutex);
+ return -EIO;
+ }
+
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+/* PCI driver stuff */
+static struct pci_device_id card_ids[] = {
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
+ {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */
+ {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+ {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */
+
+ /* required last entry */
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, card_ids);
+
+static struct attribute *ipw_sysfs_entries[] = {
+ &dev_attr_rf_kill.attr,
+ &dev_attr_direct_dword.attr,
+ &dev_attr_indirect_byte.attr,
+ &dev_attr_indirect_dword.attr,
+ &dev_attr_mem_gpio_reg.attr,
+ &dev_attr_command_event_reg.attr,
+ &dev_attr_nic_type.attr,
+ &dev_attr_status.attr,
+ &dev_attr_cfg.attr,
+ &dev_attr_error.attr,
+ &dev_attr_event_log.attr,
+ &dev_attr_cmd_log.attr,
+ &dev_attr_eeprom_delay.attr,
+ &dev_attr_ucode_version.attr,
+ &dev_attr_rtc.attr,
+ &dev_attr_scan_age.attr,
+ &dev_attr_led.attr,
+ &dev_attr_speed_scan.attr,
+ &dev_attr_net_stats.attr,
+ &dev_attr_channels.attr,
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ &dev_attr_rtap_iface.attr,
+ &dev_attr_rtap_filter.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group ipw_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = ipw_sysfs_entries,
+};
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+static int ipw_prom_open(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ struct ipw_priv *priv = prom_priv->priv;
+
+ IPW_DEBUG_INFO("prom dev->open\n");
+ netif_carrier_off(dev);
+
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ priv->sys_config.accept_all_data_frames = 1;
+ priv->sys_config.accept_non_directed_frames = 1;
+ priv->sys_config.accept_all_mgmt_bcpr = 1;
+ priv->sys_config.accept_all_mgmt_frames = 1;
+
+ ipw_send_system_config(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_prom_stop(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ struct ipw_priv *priv = prom_priv->priv;
+
+ IPW_DEBUG_INFO("prom dev->stop\n");
+
+ if (priv->ieee->iw_mode != IW_MODE_MONITOR) {
+ priv->sys_config.accept_all_data_frames = 0;
+ priv->sys_config.accept_non_directed_frames = 0;
+ priv->sys_config.accept_all_mgmt_bcpr = 0;
+ priv->sys_config.accept_all_mgmt_frames = 0;
+
+ ipw_send_system_config(priv);
+ }
+
+ return 0;
+}
+
+static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ IPW_DEBUG_INFO("prom dev->xmit\n");
+ return -EOPNOTSUPP;
+}
+
+static struct net_device_stats *ipw_prom_get_stats(struct net_device *dev)
+{
+ struct ipw_prom_priv *prom_priv = ieee80211_priv(dev);
+ return &prom_priv->ieee->stats;
+}
+
+static int ipw_prom_alloc(struct ipw_priv *priv)
+{
+ int rc = 0;
+
+ if (priv->prom_net_dev)
+ return -EPERM;
+
+ priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv));
+ if (priv->prom_net_dev == NULL)
+ return -ENOMEM;
+
+ priv->prom_priv = ieee80211_priv(priv->prom_net_dev);
+ priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev);
+ priv->prom_priv->priv = priv;
+
+ strcpy(priv->prom_net_dev->name, "rtap%d");
+ memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+
+ priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ priv->prom_net_dev->open = ipw_prom_open;
+ priv->prom_net_dev->stop = ipw_prom_stop;
+ priv->prom_net_dev->get_stats = ipw_prom_get_stats;
+ priv->prom_net_dev->hard_start_xmit = ipw_prom_hard_start_xmit;
+
+ priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR;
+ SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev);
+
+ rc = register_netdev(priv->prom_net_dev);
+ if (rc) {
+ free_ieee80211(priv->prom_net_dev);
+ priv->prom_net_dev = NULL;
+ return rc;
+ }
+
+ return 0;
+}
+
+static void ipw_prom_free(struct ipw_priv *priv)
+{
+ if (!priv->prom_net_dev)
+ return;
+
+ unregister_netdev(priv->prom_net_dev);
+ free_ieee80211(priv->prom_net_dev);
+
+ priv->prom_net_dev = NULL;
+}
+
+#endif
+
+
+static int __devinit ipw_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err = 0;
+ struct net_device *net_dev;
+ void __iomem *base;
+ u32 length, val;
+ struct ipw_priv *priv;
+ int i;
+
+ net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
+ if (net_dev == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ priv = ieee80211_priv(net_dev);
+ priv->ieee = netdev_priv(net_dev);
+
+ priv->net_dev = net_dev;
+ priv->pci_dev = pdev;
+ ipw_debug_level = debug;
+ spin_lock_init(&priv->irq_lock);
+ spin_lock_init(&priv->lock);
+ for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&priv->ibss_mac_hash[i]);
+
+ mutex_init(&priv->mutex);
+ if (pci_enable_device(pdev)) {
+ err = -ENODEV;
+ goto out_free_ieee80211;
+ }
+
+ pci_set_master(pdev);
+
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n");
+ goto out_pci_disable_device;
+ }
+
+ pci_set_drvdata(pdev, priv);
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto out_pci_disable_device;
+
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ length = pci_resource_len(pdev, 0);
+ priv->hw_len = length;
+
+ base = pci_ioremap_bar(pdev, 0);
+ if (!base) {
+ err = -ENODEV;
+ goto out_pci_release_regions;
+ }
+
+ priv->hw_base = base;
+ IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length);
+ IPW_DEBUG_INFO("pci_resource_base = %p\n", base);
+
+ err = ipw_setup_deferred_work(priv);
+ if (err) {
+ IPW_ERROR("Unable to setup deferred work\n");
+ goto out_iounmap;
+ }
+
+ ipw_sw_reset(priv, 1);
+
+ err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv);
+ if (err) {
+ IPW_ERROR("Error allocating IRQ %d\n", pdev->irq);
+ goto out_destroy_workqueue;
+ }
+
+ SET_NETDEV_DEV(net_dev, &pdev->dev);
+
+ mutex_lock(&priv->mutex);
+
+ priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit;
+ priv->ieee->set_security = shim__set_security;
+ priv->ieee->is_queue_full = ipw_net_is_queue_full;
+
+#ifdef CONFIG_IPW2200_QOS
+ priv->ieee->is_qos_active = ipw_is_qos_active;
+ priv->ieee->handle_probe_response = ipw_handle_beacon;
+ priv->ieee->handle_beacon = ipw_handle_probe_response;
+ priv->ieee->handle_assoc_response = ipw_handle_assoc_response;
+#endif /* CONFIG_IPW2200_QOS */
+
+ priv->ieee->perfect_rssi = -20;
+ priv->ieee->worst_rssi = -85;
+
+ net_dev->open = ipw_net_open;
+ net_dev->stop = ipw_net_stop;
+ net_dev->init = ipw_net_init;
+ net_dev->get_stats = ipw_net_get_stats;
+ net_dev->set_multicast_list = ipw_net_set_multicast_list;
+ net_dev->set_mac_address = ipw_net_set_mac_address;
+ priv->wireless_data.spy_data = &priv->ieee->spy_data;
+ net_dev->wireless_data = &priv->wireless_data;
+ net_dev->wireless_handlers = &ipw_wx_handler_def;
+ net_dev->ethtool_ops = &ipw_ethtool_ops;
+ net_dev->irq = pdev->irq;
+ net_dev->base_addr = (unsigned long)priv->hw_base;
+ net_dev->mem_start = pci_resource_start(pdev, 0);
+ net_dev->mem_end = net_dev->mem_start + pci_resource_len(pdev, 0) - 1;
+
+ err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group);
+ if (err) {
+ IPW_ERROR("failed to create sysfs device attributes\n");
+ mutex_unlock(&priv->mutex);
+ goto out_release_irq;
+ }
+
+ mutex_unlock(&priv->mutex);
+ err = register_netdev(net_dev);
+ if (err) {
+ IPW_ERROR("failed to register network device\n");
+ goto out_remove_sysfs;
+ }
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ if (rtap_iface) {
+ err = ipw_prom_alloc(priv);
+ if (err) {
+ IPW_ERROR("Failed to register promiscuous network "
+ "device (error %d).\n", err);
+ unregister_netdev(priv->net_dev);
+ goto out_remove_sysfs;
+ }
+ }
+#endif
+
+ printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg "
+ "channels, %d 802.11a channels)\n",
+ priv->ieee->geo.name, priv->ieee->geo.bg_channels,
+ priv->ieee->geo.a_channels);
+
+ return 0;
+
+ out_remove_sysfs:
+ sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+ out_release_irq:
+ free_irq(pdev->irq, priv);
+ out_destroy_workqueue:
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+ out_iounmap:
+ iounmap(priv->hw_base);
+ out_pci_release_regions:
+ pci_release_regions(pdev);
+ out_pci_disable_device:
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ out_free_ieee80211:
+ free_ieee80211(priv->net_dev);
+ out:
+ return err;
+}
+
+static void __devexit ipw_pci_remove(struct pci_dev *pdev)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ struct list_head *p, *q;
+ int i;
+
+ if (!priv)
+ return;
+
+ mutex_lock(&priv->mutex);
+
+ priv->status |= STATUS_EXIT_PENDING;
+ ipw_down(priv);
+ sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
+
+ mutex_unlock(&priv->mutex);
+
+ unregister_netdev(priv->net_dev);
+
+ if (priv->rxq) {
+ ipw_rx_queue_free(priv, priv->rxq);
+ priv->rxq = NULL;
+ }
+ ipw_tx_queue_free(priv);
+
+ if (priv->cmdlog) {
+ kfree(priv->cmdlog);
+ priv->cmdlog = NULL;
+ }
+ /* ipw_down will ensure that there is no more pending work
+ * in the workqueue's, so we can safely remove them now. */
+ cancel_delayed_work(&priv->adhoc_check);
+ cancel_delayed_work(&priv->gather_stats);
+ cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
+ cancel_delayed_work(&priv->scan_event);
+ cancel_delayed_work(&priv->rf_kill);
+ cancel_delayed_work(&priv->scan_check);
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+
+ /* Free MAC hash list for ADHOC */
+ for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) {
+ list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) {
+ list_del(p);
+ kfree(list_entry(p, struct ipw_ibss_seq, list));
+ }
+ }
+
+ kfree(priv->error);
+ priv->error = NULL;
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ ipw_prom_free(priv);
+#endif
+
+ free_irq(pdev->irq, priv);
+ iounmap(priv->hw_base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ free_ieee80211(priv->net_dev);
+ free_firmware();
+}
+
+#ifdef CONFIG_PM
+static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ struct net_device *dev = priv->net_dev;
+
+ printk(KERN_INFO "%s: Going into suspend...\n", dev->name);
+
+ /* Take down the device; powers it off, etc. */
+ ipw_down(priv);
+
+ /* Remove the PRESENT state of the device */
+ netif_device_detach(dev);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int ipw_pci_resume(struct pci_dev *pdev)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+ struct net_device *dev = priv->net_dev;
+ int err;
+ u32 val;
+
+ printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name);
+
+ pci_set_power_state(pdev, PCI_D0);
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
+ dev->name);
+ return err;
+ }
+ pci_restore_state(pdev);
+
+ /*
+ * Suspend/Resume resets the PCI configuration space, so we have to
+ * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
+ * from interfering with C3 CPU state. pci_restore_state won't help
+ * here since it only restores the first 64 bytes pci config header.
+ */
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ /* Set the device back into the PRESENT state; this will also wake
+ * the queue of needed */
+ netif_device_attach(dev);
+
+ /* Bring the device back up */
+ queue_work(priv->workqueue, &priv->up);
+
+ return 0;
+}
+#endif
+
+static void ipw_pci_shutdown(struct pci_dev *pdev)
+{
+ struct ipw_priv *priv = pci_get_drvdata(pdev);
+
+ /* Take down the device; powers it off, etc. */
+ ipw_down(priv);
+
+ pci_disable_device(pdev);
+}
+
+/* driver initialization stuff */
+static struct pci_driver ipw_driver = {
+ .name = DRV_NAME,
+ .id_table = card_ids,
+ .probe = ipw_pci_probe,
+ .remove = __devexit_p(ipw_pci_remove),
+#ifdef CONFIG_PM
+ .suspend = ipw_pci_suspend,
+ .resume = ipw_pci_resume,
+#endif
+ .shutdown = ipw_pci_shutdown,
+};
+
+static int __init ipw_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
+ printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
+
+ ret = pci_register_driver(&ipw_driver);
+ if (ret) {
+ IPW_ERROR("Unable to initialize PCI module\n");
+ return ret;
+ }
+
+ ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level);
+ if (ret) {
+ IPW_ERROR("Unable to create driver sysfs file\n");
+ pci_unregister_driver(&ipw_driver);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit ipw_exit(void)
+{
+ driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level);
+ pci_unregister_driver(&ipw_driver);
+}
+
+module_param(disable, int, 0444);
+MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
+
+module_param(associate, int, 0444);
+MODULE_PARM_DESC(associate, "auto associate when scanning (default off)");
+
+module_param(auto_create, int, 0444);
+MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)");
+
+module_param(led, int, 0444);
+MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)");
+
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "debug output mask");
+
+module_param(channel, int, 0444);
+MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])");
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+module_param(rtap_iface, int, 0444);
+MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)");
+#endif
+
+#ifdef CONFIG_IPW2200_QOS
+module_param(qos_enable, int, 0444);
+MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis");
+
+module_param(qos_burst_enable, int, 0444);
+MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode");
+
+module_param(qos_no_ack_mask, int, 0444);
+MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack");
+
+module_param(burst_duration_CCK, int, 0444);
+MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value");
+
+module_param(burst_duration_OFDM, int, 0444);
+MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value");
+#endif /* CONFIG_IPW2200_QOS */
+
+#ifdef CONFIG_IPW2200_MONITOR
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)");
+#else
+module_param(mode, int, 0444);
+MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)");
+#endif
+
+module_param(bt_coexist, int, 0444);
+MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)");
+
+module_param(hwcrypto, int, 0444);
+MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)");
+
+module_param(cmdlog, int, 0444);
+MODULE_PARM_DESC(cmdlog,
+ "allocate a ring buffer for logging firmware commands");
+
+module_param(roaming, int, 0444);
+MODULE_PARM_DESC(roaming, "enable roaming support (default on)");
+
+module_param(antenna, int, 0444);
+MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)");
+
+module_exit(ipw_exit);
+module_init(ipw_init);
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h
new file mode 100644
index 000000000000..277b274d4be5
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/ipw2200.h
@@ -0,0 +1,2012 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+
+#ifndef __ipw2200_h__
+#define __ipw2200_h__
+
+#define WEXT_USECHANNELS 1
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/firmware.h>
+#include <linux/wireless.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include <net/lib80211.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211_radiotap.h>
+
+#define DRV_NAME "ipw2200"
+
+#include <linux/workqueue.h>
+
+/* Authentication and Association States */
+enum connection_manager_assoc_states {
+ CMAS_INIT = 0,
+ CMAS_TX_AUTH_SEQ_1,
+ CMAS_RX_AUTH_SEQ_2,
+ CMAS_AUTH_SEQ_1_PASS,
+ CMAS_AUTH_SEQ_1_FAIL,
+ CMAS_TX_AUTH_SEQ_3,
+ CMAS_RX_AUTH_SEQ_4,
+ CMAS_AUTH_SEQ_2_PASS,
+ CMAS_AUTH_SEQ_2_FAIL,
+ CMAS_AUTHENTICATED,
+ CMAS_TX_ASSOC,
+ CMAS_RX_ASSOC_RESP,
+ CMAS_ASSOCIATED,
+ CMAS_LAST
+};
+
+#define IPW_WAIT (1<<0)
+#define IPW_QUIET (1<<1)
+#define IPW_ROAMING (1<<2)
+
+#define IPW_POWER_MODE_CAM 0x00 //(always on)
+#define IPW_POWER_INDEX_1 0x01
+#define IPW_POWER_INDEX_2 0x02
+#define IPW_POWER_INDEX_3 0x03
+#define IPW_POWER_INDEX_4 0x04
+#define IPW_POWER_INDEX_5 0x05
+#define IPW_POWER_AC 0x06
+#define IPW_POWER_BATTERY 0x07
+#define IPW_POWER_LIMIT 0x07
+#define IPW_POWER_MASK 0x0F
+#define IPW_POWER_ENABLED 0x10
+#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK)
+
+#define IPW_CMD_HOST_COMPLETE 2
+#define IPW_CMD_POWER_DOWN 4
+#define IPW_CMD_SYSTEM_CONFIG 6
+#define IPW_CMD_MULTICAST_ADDRESS 7
+#define IPW_CMD_SSID 8
+#define IPW_CMD_ADAPTER_ADDRESS 11
+#define IPW_CMD_PORT_TYPE 12
+#define IPW_CMD_RTS_THRESHOLD 15
+#define IPW_CMD_FRAG_THRESHOLD 16
+#define IPW_CMD_POWER_MODE 17
+#define IPW_CMD_WEP_KEY 18
+#define IPW_CMD_TGI_TX_KEY 19
+#define IPW_CMD_SCAN_REQUEST 20
+#define IPW_CMD_ASSOCIATE 21
+#define IPW_CMD_SUPPORTED_RATES 22
+#define IPW_CMD_SCAN_ABORT 23
+#define IPW_CMD_TX_FLUSH 24
+#define IPW_CMD_QOS_PARAMETERS 25
+#define IPW_CMD_SCAN_REQUEST_EXT 26
+#define IPW_CMD_DINO_CONFIG 30
+#define IPW_CMD_RSN_CAPABILITIES 31
+#define IPW_CMD_RX_KEY 32
+#define IPW_CMD_CARD_DISABLE 33
+#define IPW_CMD_SEED_NUMBER 34
+#define IPW_CMD_TX_POWER 35
+#define IPW_CMD_COUNTRY_INFO 36
+#define IPW_CMD_AIRONET_INFO 37
+#define IPW_CMD_AP_TX_POWER 38
+#define IPW_CMD_CCKM_INFO 39
+#define IPW_CMD_CCX_VER_INFO 40
+#define IPW_CMD_SET_CALIBRATION 41
+#define IPW_CMD_SENSITIVITY_CALIB 42
+#define IPW_CMD_RETRY_LIMIT 51
+#define IPW_CMD_IPW_PRE_POWER_DOWN 58
+#define IPW_CMD_VAP_BEACON_TEMPLATE 60
+#define IPW_CMD_VAP_DTIM_PERIOD 61
+#define IPW_CMD_EXT_SUPPORTED_RATES 62
+#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63
+#define IPW_CMD_VAP_QUIET_INTERVALS 64
+#define IPW_CMD_VAP_CHANNEL_SWITCH 65
+#define IPW_CMD_VAP_MANDATORY_CHANNELS 66
+#define IPW_CMD_VAP_CELL_PWR_LIMIT 67
+#define IPW_CMD_VAP_CF_PARAM_SET 68
+#define IPW_CMD_VAP_SET_BEACONING_STATE 69
+#define IPW_CMD_MEASUREMENT 80
+#define IPW_CMD_POWER_CAPABILITY 81
+#define IPW_CMD_SUPPORTED_CHANNELS 82
+#define IPW_CMD_TPC_REPORT 83
+#define IPW_CMD_WME_INFO 84
+#define IPW_CMD_PRODUCTION_COMMAND 85
+#define IPW_CMD_LINKSYS_EOU_INFO 90
+
+#define RFD_SIZE 4
+#define NUM_TFD_CHUNKS 6
+
+#define TX_QUEUE_SIZE 32
+#define RX_QUEUE_SIZE 32
+
+#define DINO_CMD_WEP_KEY 0x08
+#define DINO_CMD_TX 0x0B
+#define DCT_ANTENNA_A 0x01
+#define DCT_ANTENNA_B 0x02
+
+#define IPW_A_MODE 0
+#define IPW_B_MODE 1
+#define IPW_G_MODE 2
+
+/*
+ * TX Queue Flag Definitions
+ */
+
+/* tx wep key definition */
+#define DCT_WEP_KEY_NOT_IMMIDIATE 0x00
+#define DCT_WEP_KEY_64Bit 0x40
+#define DCT_WEP_KEY_128Bit 0x80
+#define DCT_WEP_KEY_128bitIV 0xC0
+#define DCT_WEP_KEY_SIZE_MASK 0xC0
+
+#define DCT_WEP_KEY_INDEX_MASK 0x0F
+#define DCT_WEP_INDEX_USE_IMMEDIATE 0x20
+
+/* abort attempt if mgmt frame is rx'd */
+#define DCT_FLAG_ABORT_MGMT 0x01
+
+/* require CTS */
+#define DCT_FLAG_CTS_REQUIRED 0x02
+
+/* use short preamble */
+#define DCT_FLAG_LONG_PREAMBLE 0x00
+#define DCT_FLAG_SHORT_PREAMBLE 0x04
+
+/* RTS/CTS first */
+#define DCT_FLAG_RTS_REQD 0x08
+
+/* dont calculate duration field */
+#define DCT_FLAG_DUR_SET 0x10
+
+/* even if MAC WEP set (allows pre-encrypt) */
+#define DCT_FLAG_NO_WEP 0x20
+
+/* overwrite TSF field */
+#define DCT_FLAG_TSF_REQD 0x40
+
+/* ACK rx is expected to follow */
+#define DCT_FLAG_ACK_REQD 0x80
+
+/* TX flags extension */
+#define DCT_FLAG_EXT_MODE_CCK 0x01
+#define DCT_FLAG_EXT_MODE_OFDM 0x00
+
+#define DCT_FLAG_EXT_SECURITY_WEP 0x00
+#define DCT_FLAG_EXT_SECURITY_NO DCT_FLAG_EXT_SECURITY_WEP
+#define DCT_FLAG_EXT_SECURITY_CKIP 0x04
+#define DCT_FLAG_EXT_SECURITY_CCM 0x08
+#define DCT_FLAG_EXT_SECURITY_TKIP 0x0C
+#define DCT_FLAG_EXT_SECURITY_MASK 0x0C
+
+#define DCT_FLAG_EXT_QOS_ENABLED 0x10
+
+#define DCT_FLAG_EXT_HC_NO_SIFS_PIFS 0x00
+#define DCT_FLAG_EXT_HC_SIFS 0x20
+#define DCT_FLAG_EXT_HC_PIFS 0x40
+
+#define TX_RX_TYPE_MASK 0xFF
+#define TX_FRAME_TYPE 0x00
+#define TX_HOST_COMMAND_TYPE 0x01
+#define RX_FRAME_TYPE 0x09
+#define RX_HOST_NOTIFICATION_TYPE 0x03
+#define RX_HOST_CMD_RESPONSE_TYPE 0x04
+#define RX_TX_FRAME_RESPONSE_TYPE 0x05
+#define TFD_NEED_IRQ_MASK 0x04
+
+#define HOST_CMD_DINO_CONFIG 30
+
+#define HOST_NOTIFICATION_STATUS_ASSOCIATED 10
+#define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11
+#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12
+#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13
+#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14
+#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15
+#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16
+#define HOST_NOTIFICATION_STATUS_BEACON_STATE 17
+#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18
+#define HOST_NOTIFICATION_TX_STATUS 19
+#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20
+#define HOST_NOTIFICATION_MEASUREMENT_STARTED 21
+#define HOST_NOTIFICATION_MEASUREMENT_ENDED 22
+#define HOST_NOTIFICATION_CHANNEL_SWITCHED 23
+#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24
+#define HOST_NOTIFICATION_NOISE_STATS 25
+#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30
+#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31
+
+#define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1
+#define IPW_MB_SCAN_CANCEL_THRESHOLD 3
+#define IPW_MB_ROAMING_THRESHOLD_MIN 1
+#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8
+#define IPW_MB_ROAMING_THRESHOLD_MAX 30
+#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 3*IPW_MB_ROAMING_THRESHOLD_DEFAULT
+#define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300
+
+#define MACADRR_BYTE_LEN 6
+
+#define DCR_TYPE_AP 0x01
+#define DCR_TYPE_WLAP 0x02
+#define DCR_TYPE_MU_ESS 0x03
+#define DCR_TYPE_MU_IBSS 0x04
+#define DCR_TYPE_MU_PIBSS 0x05
+#define DCR_TYPE_SNIFFER 0x06
+#define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS
+
+/* QoS definitions */
+
+#define CW_MIN_OFDM 15
+#define CW_MAX_OFDM 1023
+#define CW_MIN_CCK 31
+#define CW_MAX_CCK 1023
+
+#define QOS_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define QOS_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define QOS_TX2_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1)
+#define QOS_TX3_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/4 - 1)
+
+#define QOS_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+#define QOS_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+#define QOS_TX2_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1)
+#define QOS_TX3_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/4 - 1)
+
+#define QOS_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+#define QOS_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+#define QOS_TX2_CW_MAX_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define QOS_TX3_CW_MAX_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1)
+
+#define QOS_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+#define QOS_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+#define QOS_TX2_CW_MAX_CCK cpu_to_le16(CW_MIN_CCK)
+#define QOS_TX3_CW_MAX_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1)
+
+#define QOS_TX0_AIFS (3 - QOS_AIFSN_MIN_VALUE)
+#define QOS_TX1_AIFS (7 - QOS_AIFSN_MIN_VALUE)
+#define QOS_TX2_AIFS (2 - QOS_AIFSN_MIN_VALUE)
+#define QOS_TX3_AIFS (2 - QOS_AIFSN_MIN_VALUE)
+
+#define QOS_TX0_ACM 0
+#define QOS_TX1_ACM 0
+#define QOS_TX2_ACM 0
+#define QOS_TX3_ACM 0
+
+#define QOS_TX0_TXOP_LIMIT_CCK 0
+#define QOS_TX1_TXOP_LIMIT_CCK 0
+#define QOS_TX2_TXOP_LIMIT_CCK cpu_to_le16(6016)
+#define QOS_TX3_TXOP_LIMIT_CCK cpu_to_le16(3264)
+
+#define QOS_TX0_TXOP_LIMIT_OFDM 0
+#define QOS_TX1_TXOP_LIMIT_OFDM 0
+#define QOS_TX2_TXOP_LIMIT_OFDM cpu_to_le16(3008)
+#define QOS_TX3_TXOP_LIMIT_OFDM cpu_to_le16(1504)
+
+#define DEF_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define DEF_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define DEF_TX2_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+#define DEF_TX3_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM)
+
+#define DEF_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+#define DEF_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+#define DEF_TX2_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+#define DEF_TX3_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK)
+
+#define DEF_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+#define DEF_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+#define DEF_TX2_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+#define DEF_TX3_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM)
+
+#define DEF_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+#define DEF_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+#define DEF_TX2_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+#define DEF_TX3_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK)
+
+#define DEF_TX0_AIFS 0
+#define DEF_TX1_AIFS 0
+#define DEF_TX2_AIFS 0
+#define DEF_TX3_AIFS 0
+
+#define DEF_TX0_ACM 0
+#define DEF_TX1_ACM 0
+#define DEF_TX2_ACM 0
+#define DEF_TX3_ACM 0
+
+#define DEF_TX0_TXOP_LIMIT_CCK 0
+#define DEF_TX1_TXOP_LIMIT_CCK 0
+#define DEF_TX2_TXOP_LIMIT_CCK 0
+#define DEF_TX3_TXOP_LIMIT_CCK 0
+
+#define DEF_TX0_TXOP_LIMIT_OFDM 0
+#define DEF_TX1_TXOP_LIMIT_OFDM 0
+#define DEF_TX2_TXOP_LIMIT_OFDM 0
+#define DEF_TX3_TXOP_LIMIT_OFDM 0
+
+#define QOS_QOS_SETS 3
+#define QOS_PARAM_SET_ACTIVE 0
+#define QOS_PARAM_SET_DEF_CCK 1
+#define QOS_PARAM_SET_DEF_OFDM 2
+
+#define CTRL_QOS_NO_ACK (0x0020)
+
+#define IPW_TX_QUEUE_1 1
+#define IPW_TX_QUEUE_2 2
+#define IPW_TX_QUEUE_3 3
+#define IPW_TX_QUEUE_4 4
+
+/* QoS sturctures */
+struct ipw_qos_info {
+ int qos_enable;
+ struct ieee80211_qos_parameters *def_qos_parm_OFDM;
+ struct ieee80211_qos_parameters *def_qos_parm_CCK;
+ u32 burst_duration_CCK;
+ u32 burst_duration_OFDM;
+ u16 qos_no_ack_mask;
+ int burst_enable;
+};
+
+/**************************************************************/
+/**
+ * Generic queue structure
+ *
+ * Contains common data for Rx and Tx queues
+ */
+struct clx2_queue {
+ int n_bd; /**< number of BDs in this queue */
+ int first_empty; /**< 1-st empty entry (index) */
+ int last_used; /**< last used entry (index) */
+ u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */
+ u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */
+ dma_addr_t dma_addr; /**< physical addr for BD's */
+ int low_mark; /**< low watermark, resume queue if free space more than this */
+ int high_mark; /**< high watermark, stop queue if free space less than this */
+} __attribute__ ((packed)); /* XXX */
+
+struct machdr32 {
+ __le16 frame_ctl;
+ __le16 duration; // watch out for endians!
+ u8 addr1[MACADRR_BYTE_LEN];
+ u8 addr2[MACADRR_BYTE_LEN];
+ u8 addr3[MACADRR_BYTE_LEN];
+ __le16 seq_ctrl; // more endians!
+ u8 addr4[MACADRR_BYTE_LEN];
+ __le16 qos_ctrl;
+} __attribute__ ((packed));
+
+struct machdr30 {
+ __le16 frame_ctl;
+ __le16 duration; // watch out for endians!
+ u8 addr1[MACADRR_BYTE_LEN];
+ u8 addr2[MACADRR_BYTE_LEN];
+ u8 addr3[MACADRR_BYTE_LEN];
+ __le16 seq_ctrl; // more endians!
+ u8 addr4[MACADRR_BYTE_LEN];
+} __attribute__ ((packed));
+
+struct machdr26 {
+ __le16 frame_ctl;
+ __le16 duration; // watch out for endians!
+ u8 addr1[MACADRR_BYTE_LEN];
+ u8 addr2[MACADRR_BYTE_LEN];
+ u8 addr3[MACADRR_BYTE_LEN];
+ __le16 seq_ctrl; // more endians!
+ __le16 qos_ctrl;
+} __attribute__ ((packed));
+
+struct machdr24 {
+ __le16 frame_ctl;
+ __le16 duration; // watch out for endians!
+ u8 addr1[MACADRR_BYTE_LEN];
+ u8 addr2[MACADRR_BYTE_LEN];
+ u8 addr3[MACADRR_BYTE_LEN];
+ __le16 seq_ctrl; // more endians!
+} __attribute__ ((packed));
+
+// TX TFD with 32 byte MAC Header
+struct tx_tfd_32 {
+ struct machdr32 mchdr; // 32
+ __le32 uivplaceholder[2]; // 8
+} __attribute__ ((packed));
+
+// TX TFD with 30 byte MAC Header
+struct tx_tfd_30 {
+ struct machdr30 mchdr; // 30
+ u8 reserved[2]; // 2
+ __le32 uivplaceholder[2]; // 8
+} __attribute__ ((packed));
+
+// tx tfd with 26 byte mac header
+struct tx_tfd_26 {
+ struct machdr26 mchdr; // 26
+ u8 reserved1[2]; // 2
+ __le32 uivplaceholder[2]; // 8
+ u8 reserved2[4]; // 4
+} __attribute__ ((packed));
+
+// tx tfd with 24 byte mac header
+struct tx_tfd_24 {
+ struct machdr24 mchdr; // 24
+ __le32 uivplaceholder[2]; // 8
+ u8 reserved[8]; // 8
+} __attribute__ ((packed));
+
+#define DCT_WEP_KEY_FIELD_LENGTH 16
+
+struct tfd_command {
+ u8 index;
+ u8 length;
+ __le16 reserved;
+ u8 payload[0];
+} __attribute__ ((packed));
+
+struct tfd_data {
+ /* Header */
+ __le32 work_area_ptr;
+ u8 station_number; /* 0 for BSS */
+ u8 reserved1;
+ __le16 reserved2;
+
+ /* Tx Parameters */
+ u8 cmd_id;
+ u8 seq_num;
+ __le16 len;
+ u8 priority;
+ u8 tx_flags;
+ u8 tx_flags_ext;
+ u8 key_index;
+ u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH];
+ u8 rate;
+ u8 antenna;
+ __le16 next_packet_duration;
+ __le16 next_frag_len;
+ __le16 back_off_counter; //////txop;
+ u8 retrylimit;
+ __le16 cwcurrent;
+ u8 reserved3;
+
+ /* 802.11 MAC Header */
+ union {
+ struct tx_tfd_24 tfd_24;
+ struct tx_tfd_26 tfd_26;
+ struct tx_tfd_30 tfd_30;
+ struct tx_tfd_32 tfd_32;
+ } tfd;
+
+ /* Payload DMA info */
+ __le32 num_chunks;
+ __le32 chunk_ptr[NUM_TFD_CHUNKS];
+ __le16 chunk_len[NUM_TFD_CHUNKS];
+} __attribute__ ((packed));
+
+struct txrx_control_flags {
+ u8 message_type;
+ u8 rx_seq_num;
+ u8 control_bits;
+ u8 reserved;
+} __attribute__ ((packed));
+
+#define TFD_SIZE 128
+#define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags))
+
+struct tfd_frame {
+ struct txrx_control_flags control_flags;
+ union {
+ struct tfd_data data;
+ struct tfd_command cmd;
+ u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH];
+ } u;
+} __attribute__ ((packed));
+
+typedef void destructor_func(const void *);
+
+/**
+ * Tx Queue for DMA. Queue consists of circular buffer of
+ * BD's and required locking structures.
+ */
+struct clx2_tx_queue {
+ struct clx2_queue q;
+ struct tfd_frame *bd;
+ struct ieee80211_txb **txb;
+};
+
+/*
+ * RX related structures and functions
+ */
+#define RX_FREE_BUFFERS 32
+#define RX_LOW_WATERMARK 8
+
+#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
+#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
+#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
+
+// Used for passing to driver number of successes and failures per rate
+struct rate_histogram {
+ union {
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } success;
+ union {
+ __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS];
+ __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS];
+ __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS];
+ } failed;
+} __attribute__ ((packed));
+
+/* statistics command response */
+struct ipw_cmd_stats {
+ u8 cmd_id;
+ u8 seq_num;
+ __le16 good_sfd;
+ __le16 bad_plcp;
+ __le16 wrong_bssid;
+ __le16 valid_mpdu;
+ __le16 bad_mac_header;
+ __le16 reserved_frame_types;
+ __le16 rx_ina;
+ __le16 bad_crc32;
+ __le16 invalid_cts;
+ __le16 invalid_acks;
+ __le16 long_distance_ina_fina;
+ __le16 dsp_silence_unreachable;
+ __le16 accumulated_rssi;
+ __le16 rx_ovfl_frame_tossed;
+ __le16 rssi_silence_threshold;
+ __le16 rx_ovfl_frame_supplied;
+ __le16 last_rx_frame_signal;
+ __le16 last_rx_frame_noise;
+ __le16 rx_autodetec_no_ofdm;
+ __le16 rx_autodetec_no_barker;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct notif_channel_result {
+ u8 channel_num;
+ struct ipw_cmd_stats stats;
+ u8 uReserved;
+} __attribute__ ((packed));
+
+#define SCAN_COMPLETED_STATUS_COMPLETE 1
+#define SCAN_COMPLETED_STATUS_ABORTED 2
+
+struct notif_scan_complete {
+ u8 scan_type;
+ u8 num_channels;
+ u8 status;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct notif_frag_length {
+ __le16 frag_length;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct notif_beacon_state {
+ __le32 state;
+ __le32 number;
+} __attribute__ ((packed));
+
+struct notif_tgi_tx_key {
+ u8 key_state;
+ u8 security_type;
+ u8 station_index;
+ u8 reserved;
+} __attribute__ ((packed));
+
+#define SILENCE_OVER_THRESH (1)
+#define SILENCE_UNDER_THRESH (2)
+
+struct notif_link_deterioration {
+ struct ipw_cmd_stats stats;
+ u8 rate;
+ u8 modulation;
+ struct rate_histogram histogram;
+ u8 silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */
+ __le16 silence_count;
+} __attribute__ ((packed));
+
+struct notif_association {
+ u8 state;
+} __attribute__ ((packed));
+
+struct notif_authenticate {
+ u8 state;
+ struct machdr24 addr;
+ __le16 status;
+} __attribute__ ((packed));
+
+struct notif_calibration {
+ u8 data[104];
+} __attribute__ ((packed));
+
+struct notif_noise {
+ __le32 value;
+} __attribute__ ((packed));
+
+struct ipw_rx_notification {
+ u8 reserved[8];
+ u8 subtype;
+ u8 flags;
+ __le16 size;
+ union {
+ struct notif_association assoc;
+ struct notif_authenticate auth;
+ struct notif_channel_result channel_result;
+ struct notif_scan_complete scan_complete;
+ struct notif_frag_length frag_len;
+ struct notif_beacon_state beacon_state;
+ struct notif_tgi_tx_key tgi_tx_key;
+ struct notif_link_deterioration link_deterioration;
+ struct notif_calibration calibration;
+ struct notif_noise noise;
+ u8 raw[0];
+ } u;
+} __attribute__ ((packed));
+
+struct ipw_rx_frame {
+ __le32 reserved1;
+ u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER
+ u8 received_channel; // The channel that this frame was received on.
+ // Note that for .11b this does not have to be
+ // the same as the channel that it was sent.
+ // Filled by LMAC
+ u8 frameStatus;
+ u8 rate;
+ u8 rssi;
+ u8 agc;
+ u8 rssi_dbm;
+ __le16 signal;
+ __le16 noise;
+ u8 antennaAndPhy;
+ u8 control; // control bit should be on in bg
+ u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate
+ // is identical)
+ u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen
+ __le16 length;
+ u8 data[0];
+} __attribute__ ((packed));
+
+struct ipw_rx_header {
+ u8 message_type;
+ u8 rx_seq_num;
+ u8 control_bits;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_packet {
+ struct ipw_rx_header header;
+ union {
+ struct ipw_rx_frame frame;
+ struct ipw_rx_notification notification;
+ } u;
+} __attribute__ ((packed));
+
+#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12
+#define IPW_RX_FRAME_SIZE (unsigned int)(sizeof(struct ipw_rx_header) + \
+ sizeof(struct ipw_rx_frame))
+
+struct ipw_rx_mem_buffer {
+ dma_addr_t dma_addr;
+ struct sk_buff *skb;
+ struct list_head list;
+}; /* Not transferred over network, so not __attribute__ ((packed)) */
+
+struct ipw_rx_queue {
+ struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
+ struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE];
+ u32 processed; /* Internal index to last handled Rx packet */
+ u32 read; /* Shared index to newest available Rx buffer */
+ u32 write; /* Shared index to oldest written Rx packet */
+ u32 free_count; /* Number of pre-allocated buffers in rx_free */
+ /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */
+ struct list_head rx_free; /* Own an SKBs */
+ struct list_head rx_used; /* No SKB allocated */
+ spinlock_t lock;
+}; /* Not transferred over network, so not __attribute__ ((packed)) */
+
+struct alive_command_responce {
+ u8 alive_command;
+ u8 sequence_number;
+ __le16 software_revision;
+ u8 device_identifier;
+ u8 reserved1[5];
+ __le16 reserved2;
+ __le16 reserved3;
+ __le16 clock_settle_time;
+ __le16 powerup_settle_time;
+ __le16 reserved4;
+ u8 time_stamp[5]; /* month, day, year, hours, minutes */
+ u8 ucode_valid;
+} __attribute__ ((packed));
+
+#define IPW_MAX_RATES 12
+
+struct ipw_rates {
+ u8 num_rates;
+ u8 rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct command_block {
+ unsigned int control;
+ u32 source_addr;
+ u32 dest_addr;
+ unsigned int status;
+} __attribute__ ((packed));
+
+#define CB_NUMBER_OF_ELEMENTS_SMALL 64
+struct fw_image_desc {
+ unsigned long last_cb_index;
+ unsigned long current_cb_index;
+ struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL];
+ void *v_addr;
+ unsigned long p_addr;
+ unsigned long len;
+};
+
+struct ipw_sys_config {
+ u8 bt_coexistence;
+ u8 reserved1;
+ u8 answer_broadcast_ssid_probe;
+ u8 accept_all_data_frames;
+ u8 accept_non_directed_frames;
+ u8 exclude_unicast_unencrypted;
+ u8 disable_unicast_decryption;
+ u8 exclude_multicast_unencrypted;
+ u8 disable_multicast_decryption;
+ u8 antenna_diversity;
+ u8 pass_crc_to_host;
+ u8 dot11g_auto_detection;
+ u8 enable_cts_to_self;
+ u8 enable_multicast_filtering;
+ u8 bt_coexist_collision_thr;
+ u8 silence_threshold;
+ u8 accept_all_mgmt_bcpr;
+ u8 accept_all_mgmt_frames;
+ u8 pass_noise_stats_to_host;
+ u8 reserved3;
+} __attribute__ ((packed));
+
+struct ipw_multicast_addr {
+ u8 num_of_multicast_addresses;
+ u8 reserved[3];
+ u8 mac1[6];
+ u8 mac2[6];
+ u8 mac3[6];
+ u8 mac4[6];
+} __attribute__ ((packed));
+
+#define DCW_WEP_KEY_INDEX_MASK 0x03 /* bits [0:1] */
+#define DCW_WEP_KEY_SEC_TYPE_MASK 0x30 /* bits [4:5] */
+
+#define DCW_WEP_KEY_SEC_TYPE_WEP 0x00
+#define DCW_WEP_KEY_SEC_TYPE_CCM 0x20
+#define DCW_WEP_KEY_SEC_TYPE_TKIP 0x30
+
+#define DCW_WEP_KEY_INVALID_SIZE 0x00 /* 0 = Invalid key */
+#define DCW_WEP_KEY64Bit_SIZE 0x05 /* 64-bit encryption */
+#define DCW_WEP_KEY128Bit_SIZE 0x0D /* 128-bit encryption */
+#define DCW_CCM_KEY128Bit_SIZE 0x10 /* 128-bit key */
+//#define DCW_WEP_KEY128BitIV_SIZE 0x10 /* 128-bit key and 128-bit IV */
+
+struct ipw_wep_key {
+ u8 cmd_id;
+ u8 seq_num;
+ u8 key_index;
+ u8 key_size;
+ u8 key[16];
+} __attribute__ ((packed));
+
+struct ipw_tgi_tx_key {
+ u8 key_id;
+ u8 security_type;
+ u8 station_index;
+ u8 flags;
+ u8 key[16];
+ __le32 tx_counter[2];
+} __attribute__ ((packed));
+
+#define IPW_SCAN_CHANNELS 54
+
+struct ipw_scan_request {
+ u8 scan_type;
+ __le16 dwell_time;
+ u8 channels_list[IPW_SCAN_CHANNELS];
+ u8 channels_reserved[3];
+} __attribute__ ((packed));
+
+enum {
+ IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0,
+ IPW_SCAN_PASSIVE_FULL_DWELL_SCAN,
+ IPW_SCAN_ACTIVE_DIRECT_SCAN,
+ IPW_SCAN_ACTIVE_BROADCAST_SCAN,
+ IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN,
+ IPW_SCAN_TYPES
+};
+
+struct ipw_scan_request_ext {
+ __le32 full_scan_index;
+ u8 channels_list[IPW_SCAN_CHANNELS];
+ u8 scan_type[IPW_SCAN_CHANNELS / 2];
+ u8 reserved;
+ __le16 dwell_time[IPW_SCAN_TYPES];
+} __attribute__ ((packed));
+
+static inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index)
+{
+ if (index % 2)
+ return scan->scan_type[index / 2] & 0x0F;
+ else
+ return (scan->scan_type[index / 2] & 0xF0) >> 4;
+}
+
+static inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan,
+ u8 index, u8 scan_type)
+{
+ if (index % 2)
+ scan->scan_type[index / 2] =
+ (scan->scan_type[index / 2] & 0xF0) | (scan_type & 0x0F);
+ else
+ scan->scan_type[index / 2] =
+ (scan->scan_type[index / 2] & 0x0F) |
+ ((scan_type & 0x0F) << 4);
+}
+
+struct ipw_associate {
+ u8 channel;
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u8 auth_type:4, auth_key:4;
+#else
+ u8 auth_key:4, auth_type:4;
+#endif
+ u8 assoc_type;
+ u8 reserved;
+ __le16 policy_support;
+ u8 preamble_length;
+ u8 ieee_mode;
+ u8 bssid[ETH_ALEN];
+ __le32 assoc_tsf_msw;
+ __le32 assoc_tsf_lsw;
+ __le16 capability;
+ __le16 listen_interval;
+ __le16 beacon_interval;
+ u8 dest[ETH_ALEN];
+ __le16 atim_window;
+ u8 smr;
+ u8 reserved1;
+ __le16 reserved2;
+} __attribute__ ((packed));
+
+struct ipw_supported_rates {
+ u8 ieee_mode;
+ u8 num_rates;
+ u8 purpose;
+ u8 reserved;
+ u8 supported_rates[IPW_MAX_RATES];
+} __attribute__ ((packed));
+
+struct ipw_rts_threshold {
+ __le16 rts_threshold;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_frag_threshold {
+ __le16 frag_threshold;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_retry_limit {
+ u8 short_retry_limit;
+ u8 long_retry_limit;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_dino_config {
+ __le32 dino_config_addr;
+ __le16 dino_config_size;
+ u8 dino_response;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_aironet_info {
+ u8 id;
+ u8 length;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct ipw_rx_key {
+ u8 station_index;
+ u8 key_type;
+ u8 key_id;
+ u8 key_flag;
+ u8 key[16];
+ u8 station_address[6];
+ u8 key_index;
+ u8 reserved;
+} __attribute__ ((packed));
+
+struct ipw_country_channel_info {
+ u8 first_channel;
+ u8 no_channels;
+ s8 max_tx_power;
+} __attribute__ ((packed));
+
+struct ipw_country_info {
+ u8 id;
+ u8 length;
+ u8 country_str[3];
+ struct ipw_country_channel_info groups[7];
+} __attribute__ ((packed));
+
+struct ipw_channel_tx_power {
+ u8 channel_number;
+ s8 tx_power;
+} __attribute__ ((packed));
+
+#define SCAN_ASSOCIATED_INTERVAL (HZ)
+#define SCAN_INTERVAL (HZ / 10)
+#define MAX_A_CHANNELS 37
+#define MAX_B_CHANNELS 14
+
+struct ipw_tx_power {
+ u8 num_channels;
+ u8 ieee_mode;
+ struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS];
+} __attribute__ ((packed));
+
+struct ipw_rsn_capabilities {
+ u8 id;
+ u8 length;
+ __le16 version;
+} __attribute__ ((packed));
+
+struct ipw_sensitivity_calib {
+ __le16 beacon_rssi_raw;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+/**
+ * Host command structure.
+ *
+ * On input, the following fields should be filled:
+ * - cmd
+ * - len
+ * - status_len
+ * - param (if needed)
+ *
+ * On output,
+ * - \a status contains status;
+ * - \a param filled with status parameters.
+ */
+struct ipw_cmd { /* XXX */
+ u32 cmd; /**< Host command */
+ u32 status;/**< Status */
+ u32 status_len;
+ /**< How many 32 bit parameters in the status */
+ u32 len; /**< incoming parameters length, bytes */
+ /**
+ * command parameters.
+ * There should be enough space for incoming and
+ * outcoming parameters.
+ * Incoming parameters listed 1-st, followed by outcoming params.
+ * nParams=(len+3)/4+status_len
+ */
+ u32 param[0];
+} __attribute__ ((packed));
+
+#define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */
+
+#define STATUS_INT_ENABLED (1<<1)
+#define STATUS_RF_KILL_HW (1<<2)
+#define STATUS_RF_KILL_SW (1<<3)
+#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW)
+
+#define STATUS_INIT (1<<5)
+#define STATUS_AUTH (1<<6)
+#define STATUS_ASSOCIATED (1<<7)
+#define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED)
+
+#define STATUS_ASSOCIATING (1<<8)
+#define STATUS_DISASSOCIATING (1<<9)
+#define STATUS_ROAMING (1<<10)
+#define STATUS_EXIT_PENDING (1<<11)
+#define STATUS_DISASSOC_PENDING (1<<12)
+#define STATUS_STATE_PENDING (1<<13)
+
+#define STATUS_DIRECT_SCAN_PENDING (1<<19)
+#define STATUS_SCAN_PENDING (1<<20)
+#define STATUS_SCANNING (1<<21)
+#define STATUS_SCAN_ABORTING (1<<22)
+#define STATUS_SCAN_FORCED (1<<23)
+
+#define STATUS_LED_LINK_ON (1<<24)
+#define STATUS_LED_ACT_ON (1<<25)
+
+#define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */
+#define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */
+#define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */
+
+#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */
+
+#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */
+#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */
+#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */
+#define CFG_CUSTOM_MAC (1<<3)
+#define CFG_PREAMBLE_LONG (1<<4)
+#define CFG_ADHOC_PERSIST (1<<5)
+#define CFG_ASSOCIATE (1<<6)
+#define CFG_FIXED_RATE (1<<7)
+#define CFG_ADHOC_CREATE (1<<8)
+#define CFG_NO_LED (1<<9)
+#define CFG_BACKGROUND_SCAN (1<<10)
+#define CFG_SPEED_SCAN (1<<11)
+#define CFG_NET_STATS (1<<12)
+
+#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */
+#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */
+
+#define MAX_STATIONS 32
+#define IPW_INVALID_STATION (0xff)
+
+struct ipw_station_entry {
+ u8 mac_addr[ETH_ALEN];
+ u8 reserved;
+ u8 support_mode;
+};
+
+#define AVG_ENTRIES 8
+struct average {
+ s16 entries[AVG_ENTRIES];
+ u8 pos;
+ u8 init;
+ s32 sum;
+};
+
+#define MAX_SPEED_SCAN 100
+#define IPW_IBSS_MAC_HASH_SIZE 31
+
+struct ipw_ibss_seq {
+ u8 mac[ETH_ALEN];
+ u16 seq_num;
+ u16 frag_num;
+ unsigned long packet_time;
+ struct list_head list;
+};
+
+struct ipw_error_elem { /* XXX */
+ u32 desc;
+ u32 time;
+ u32 blink1;
+ u32 blink2;
+ u32 link1;
+ u32 link2;
+ u32 data;
+};
+
+struct ipw_event { /* XXX */
+ u32 event;
+ u32 time;
+ u32 data;
+} __attribute__ ((packed));
+
+struct ipw_fw_error { /* XXX */
+ unsigned long jiffies;
+ u32 status;
+ u32 config;
+ u32 elem_len;
+ u32 log_len;
+ struct ipw_error_elem *elem;
+ struct ipw_event *log;
+ u8 payload[0];
+} __attribute__ ((packed));
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+
+enum ipw_prom_filter {
+ IPW_PROM_CTL_HEADER_ONLY = (1 << 0),
+ IPW_PROM_MGMT_HEADER_ONLY = (1 << 1),
+ IPW_PROM_DATA_HEADER_ONLY = (1 << 2),
+ IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */
+ IPW_PROM_NO_TX = (1 << 4),
+ IPW_PROM_NO_RX = (1 << 5),
+ IPW_PROM_NO_CTL = (1 << 6),
+ IPW_PROM_NO_MGMT = (1 << 7),
+ IPW_PROM_NO_DATA = (1 << 8),
+};
+
+struct ipw_priv;
+struct ipw_prom_priv {
+ struct ipw_priv *priv;
+ struct ieee80211_device *ieee;
+ enum ipw_prom_filter filter;
+ int tx_packets;
+ int rx_packets;
+};
+#endif
+
+#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS)
+/* Magic struct that slots into the radiotap header -- no reason
+ * to build this manually element by element, we can write it much
+ * more efficiently than we can parse it. ORDER MATTERS HERE
+ *
+ * When sent to us via the simulated Rx interface in sysfs, the entire
+ * structure is provided regardless of any bits unset.
+ */
+struct ipw_rt_hdr {
+ struct ieee80211_radiotap_header rt_hdr;
+ u64 rt_tsf; /* TSF */ /* XXX */
+ u8 rt_flags; /* radiotap packet flags */
+ u8 rt_rate; /* rate in 500kb/s */
+ __le16 rt_channel; /* channel in mhz */
+ __le16 rt_chbitmask; /* channel bitfield */
+ s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+ s8 rt_dbmnoise;
+ u8 rt_antenna; /* antenna number */
+ u8 payload[0]; /* payload... */
+} __attribute__ ((packed));
+#endif
+
+struct ipw_priv {
+ /* ieee device used by generic ieee processing code */
+ struct ieee80211_device *ieee;
+
+ spinlock_t lock;
+ spinlock_t irq_lock;
+ struct mutex mutex;
+
+ /* basic pci-network driver stuff */
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev;
+
+#ifdef CONFIG_IPW2200_PROMISCUOUS
+ /* Promiscuous mode */
+ struct ipw_prom_priv *prom_priv;
+ struct net_device *prom_net_dev;
+#endif
+
+ /* pci hardware address support */
+ void __iomem *hw_base;
+ unsigned long hw_len;
+
+ struct fw_image_desc sram_desc;
+
+ /* result of ucode download */
+ struct alive_command_responce dino_alive;
+
+ wait_queue_head_t wait_command_queue;
+ wait_queue_head_t wait_state;
+
+ /* Rx and Tx DMA processing queues */
+ struct ipw_rx_queue *rxq;
+ struct clx2_tx_queue txq_cmd;
+ struct clx2_tx_queue txq[4];
+ u32 status;
+ u32 config;
+ u32 capability;
+
+ struct average average_missed_beacons;
+ s16 exp_avg_rssi;
+ s16 exp_avg_noise;
+ u32 port_type;
+ int rx_bufs_min; /**< minimum number of bufs in Rx queue */
+ int rx_pend_max; /**< maximum pending buffers for one IRQ */
+ u32 hcmd_seq; /**< sequence number for hcmd */
+ u32 disassociate_threshold;
+ u32 roaming_threshold;
+
+ struct ipw_associate assoc_request;
+ struct ieee80211_network *assoc_network;
+
+ unsigned long ts_scan_abort;
+ struct ipw_supported_rates rates;
+ struct ipw_rates phy[3]; /**< PHY restrictions, per band */
+ struct ipw_rates supp; /**< software defined */
+ struct ipw_rates extended; /**< use for corresp. IE, AP only */
+
+ struct notif_link_deterioration last_link_deterioration; /** for statistics */
+ struct ipw_cmd *hcmd; /**< host command currently executed */
+
+ wait_queue_head_t hcmd_wq; /**< host command waits for execution */
+ u32 tsf_bcn[2]; /**< TSF from latest beacon */
+
+ struct notif_calibration calib; /**< last calibration */
+
+ /* ordinal interface with firmware */
+ u32 table0_addr;
+ u32 table0_len;
+ u32 table1_addr;
+ u32 table1_len;
+ u32 table2_addr;
+ u32 table2_len;
+
+ /* context information */
+ u8 essid[IW_ESSID_MAX_SIZE];
+ u8 essid_len;
+ u8 nick[IW_ESSID_MAX_SIZE];
+ u16 rates_mask;
+ u8 channel;
+ struct ipw_sys_config sys_config;
+ u32 power_mode;
+ u8 bssid[ETH_ALEN];
+ u16 rts_threshold;
+ u8 mac_addr[ETH_ALEN];
+ u8 num_stations;
+ u8 stations[MAX_STATIONS][ETH_ALEN];
+ u8 short_retry_limit;
+ u8 long_retry_limit;
+
+ u32 notif_missed_beacons;
+
+ /* Statistics and counters normalized with each association */
+ u32 last_missed_beacons;
+ u32 last_tx_packets;
+ u32 last_rx_packets;
+ u32 last_tx_failures;
+ u32 last_rx_err;
+ u32 last_rate;
+
+ u32 missed_adhoc_beacons;
+ u32 missed_beacons;
+ u32 rx_packets;
+ u32 tx_packets;
+ u32 quality;
+
+ u8 speed_scan[MAX_SPEED_SCAN];
+ u8 speed_scan_pos;
+
+ u16 last_seq_num;
+ u16 last_frag_num;
+ unsigned long last_packet_time;
+ struct list_head ibss_mac_hash[IPW_IBSS_MAC_HASH_SIZE];
+
+ /* eeprom */
+ u8 eeprom[0x100]; /* 256 bytes of eeprom */
+ u8 country[4];
+ int eeprom_delay;
+
+ struct iw_statistics wstats;
+
+ struct iw_public_data wireless_data;
+
+ int user_requested_scan;
+ u8 direct_scan_ssid[IW_ESSID_MAX_SIZE];
+ u8 direct_scan_ssid_len;
+
+ struct workqueue_struct *workqueue;
+
+ struct delayed_work adhoc_check;
+ struct work_struct associate;
+ struct work_struct disassociate;
+ struct work_struct system_config;
+ struct work_struct rx_replenish;
+ struct delayed_work request_scan;
+ struct delayed_work request_direct_scan;
+ struct delayed_work request_passive_scan;
+ struct delayed_work scan_event;
+ struct work_struct adapter_restart;
+ struct delayed_work rf_kill;
+ struct work_struct up;
+ struct work_struct down;
+ struct delayed_work gather_stats;
+ struct work_struct abort_scan;
+ struct work_struct roam;
+ struct delayed_work scan_check;
+ struct work_struct link_up;
+ struct work_struct link_down;
+
+ struct tasklet_struct irq_tasklet;
+
+ /* LED related variables and work_struct */
+ u8 nic_type;
+ u32 led_activity_on;
+ u32 led_activity_off;
+ u32 led_association_on;
+ u32 led_association_off;
+ u32 led_ofdm_on;
+ u32 led_ofdm_off;
+
+ struct delayed_work led_link_on;
+ struct delayed_work led_link_off;
+ struct delayed_work led_act_off;
+ struct work_struct merge_networks;
+
+ struct ipw_cmd_log *cmdlog;
+ int cmdlog_len;
+ int cmdlog_pos;
+
+#define IPW_2200BG 1
+#define IPW_2915ABG 2
+ u8 adapter;
+
+ s8 tx_power;
+
+#ifdef CONFIG_PM
+ u32 pm_state[16];
+#endif
+
+ struct ipw_fw_error *error;
+
+ /* network state */
+
+ /* Used to pass the current INTA value from ISR to Tasklet */
+ u32 isr_inta;
+
+ /* QoS */
+ struct ipw_qos_info qos_data;
+ struct work_struct qos_activate;
+ /*********************************/
+
+ /* debugging info */
+ u32 indirect_dword;
+ u32 direct_dword;
+ u32 indirect_byte;
+}; /*ipw_priv */
+
+/* debug macros */
+
+/* Debug and printf string expansion helpers for printing bitfields */
+#define BIT_FMT8 "%c%c%c%c-%c%c%c%c"
+#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8
+#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16
+
+#define BITC(x,y) (((x>>y)&1)?'1':'0')
+#define BIT_ARG8(x) \
+BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\
+BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0)
+
+#define BIT_ARG16(x) \
+BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\
+BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\
+BIT_ARG8(x)
+
+#define BIT_ARG32(x) \
+BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\
+BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\
+BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\
+BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\
+BIT_ARG16(x)
+
+
+#define IPW_DEBUG(level, fmt, args...) \
+do { if (ipw_debug_level & (level)) \
+ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
+ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0)
+
+#ifdef CONFIG_IPW2200_DEBUG
+#define IPW_LL_DEBUG(level, fmt, args...) \
+do { if (ipw_debug_level & (level)) \
+ printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \
+ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0)
+#else
+#define IPW_LL_DEBUG(level, fmt, args...) do {} while (0)
+#endif /* CONFIG_IPW2200_DEBUG */
+
+/*
+ * To use the debug system;
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of:
+ *
+ * #define IPW_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry. xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IPW_xxxx_DEBUG() macro definition for your
+ * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw/debug_level
+ *
+ * you simply need to add your entry to the ipw_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw then you do not have
+ * CONFIG_IPW2200_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IPW_DL_ERROR (1<<0)
+#define IPW_DL_WARNING (1<<1)
+#define IPW_DL_INFO (1<<2)
+#define IPW_DL_WX (1<<3)
+#define IPW_DL_HOST_COMMAND (1<<5)
+#define IPW_DL_STATE (1<<6)
+
+#define IPW_DL_NOTIF (1<<10)
+#define IPW_DL_SCAN (1<<11)
+#define IPW_DL_ASSOC (1<<12)
+#define IPW_DL_DROP (1<<13)
+#define IPW_DL_IOCTL (1<<14)
+
+#define IPW_DL_MANAGE (1<<15)
+#define IPW_DL_FW (1<<16)
+#define IPW_DL_RF_KILL (1<<17)
+#define IPW_DL_FW_ERRORS (1<<18)
+
+#define IPW_DL_LED (1<<19)
+
+#define IPW_DL_ORD (1<<20)
+
+#define IPW_DL_FRAG (1<<21)
+#define IPW_DL_WEP (1<<22)
+#define IPW_DL_TX (1<<23)
+#define IPW_DL_RX (1<<24)
+#define IPW_DL_ISR (1<<25)
+#define IPW_DL_FW_INFO (1<<26)
+#define IPW_DL_IO (1<<27)
+#define IPW_DL_TRACE (1<<28)
+
+#define IPW_DL_STATS (1<<29)
+#define IPW_DL_MERGE (1<<30)
+#define IPW_DL_QOS (1<<31)
+
+#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a)
+#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a)
+#define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a)
+
+#define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a)
+#define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a)
+#define IPW_DEBUG_TRACE(f, a...) IPW_LL_DEBUG(IPW_DL_TRACE, f, ## a)
+#define IPW_DEBUG_RX(f, a...) IPW_LL_DEBUG(IPW_DL_RX, f, ## a)
+#define IPW_DEBUG_TX(f, a...) IPW_LL_DEBUG(IPW_DL_TX, f, ## a)
+#define IPW_DEBUG_ISR(f, a...) IPW_LL_DEBUG(IPW_DL_ISR, f, ## a)
+#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a)
+#define IPW_DEBUG_LED(f, a...) IPW_LL_DEBUG(IPW_DL_LED, f, ## a)
+#define IPW_DEBUG_WEP(f, a...) IPW_LL_DEBUG(IPW_DL_WEP, f, ## a)
+#define IPW_DEBUG_HC(f, a...) IPW_LL_DEBUG(IPW_DL_HOST_COMMAND, f, ## a)
+#define IPW_DEBUG_FRAG(f, a...) IPW_LL_DEBUG(IPW_DL_FRAG, f, ## a)
+#define IPW_DEBUG_FW(f, a...) IPW_LL_DEBUG(IPW_DL_FW, f, ## a)
+#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a)
+#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a)
+#define IPW_DEBUG_IO(f, a...) IPW_LL_DEBUG(IPW_DL_IO, f, ## a)
+#define IPW_DEBUG_ORD(f, a...) IPW_LL_DEBUG(IPW_DL_ORD, f, ## a)
+#define IPW_DEBUG_FW_INFO(f, a...) IPW_LL_DEBUG(IPW_DL_FW_INFO, f, ## a)
+#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a)
+#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a)
+#define IPW_DEBUG_STATS(f, a...) IPW_LL_DEBUG(IPW_DL_STATS, f, ## a)
+#define IPW_DEBUG_MERGE(f, a...) IPW_LL_DEBUG(IPW_DL_MERGE, f, ## a)
+#define IPW_DEBUG_QOS(f, a...) IPW_LL_DEBUG(IPW_DL_QOS, f, ## a)
+
+#include <linux/ctype.h>
+
+/*
+* Register bit definitions
+*/
+
+#define IPW_INTA_RW 0x00000008
+#define IPW_INTA_MASK_R 0x0000000C
+#define IPW_INDIRECT_ADDR 0x00000010
+#define IPW_INDIRECT_DATA 0x00000014
+#define IPW_AUTOINC_ADDR 0x00000018
+#define IPW_AUTOINC_DATA 0x0000001C
+#define IPW_RESET_REG 0x00000020
+#define IPW_GP_CNTRL_RW 0x00000024
+
+#define IPW_READ_INT_REGISTER 0xFF4
+
+#define IPW_GP_CNTRL_BIT_INIT_DONE 0x00000004
+
+#define IPW_REGISTER_DOMAIN1_END 0x00001000
+#define IPW_SRAM_READ_INT_REGISTER 0x00000ff4
+
+#define IPW_SHARED_LOWER_BOUND 0x00000200
+#define IPW_INTERRUPT_AREA_LOWER_BOUND 0x00000f80
+
+#define IPW_NIC_SRAM_LOWER_BOUND 0x00000000
+#define IPW_NIC_SRAM_UPPER_BOUND 0x00030000
+
+#define IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29)
+#define IPW_GP_CNTRL_BIT_CLOCK_READY 0x00000001
+#define IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002
+
+/*
+ * RESET Register Bit Indexes
+ */
+#define CBD_RESET_REG_PRINCETON_RESET (1<<0)
+#define IPW_START_STANDBY (1<<2)
+#define IPW_ACTIVITY_LED (1<<4)
+#define IPW_ASSOCIATED_LED (1<<5)
+#define IPW_OFDM_LED (1<<6)
+#define IPW_RESET_REG_SW_RESET (1<<7)
+#define IPW_RESET_REG_MASTER_DISABLED (1<<8)
+#define IPW_RESET_REG_STOP_MASTER (1<<9)
+#define IPW_GATE_ODMA (1<<25)
+#define IPW_GATE_IDMA (1<<26)
+#define IPW_ARC_KESHET_CONFIG (1<<27)
+#define IPW_GATE_ADMA (1<<29)
+
+#define IPW_CSR_CIS_UPPER_BOUND 0x00000200
+#define IPW_DOMAIN_0_END 0x1000
+#define CLX_MEM_BAR_SIZE 0x1000
+
+/* Dino/baseband control registers bits */
+
+#define DINO_ENABLE_SYSTEM 0x80 /* 1 = baseband processor on, 0 = reset */
+#define DINO_ENABLE_CS 0x40 /* 1 = enable ucode load */
+#define DINO_RXFIFO_DATA 0x01 /* 1 = data available */
+#define IPW_BASEBAND_CONTROL_STATUS 0X00200000
+#define IPW_BASEBAND_TX_FIFO_WRITE 0X00200004
+#define IPW_BASEBAND_RX_FIFO_READ 0X00200004
+#define IPW_BASEBAND_CONTROL_STORE 0X00200010
+
+#define IPW_INTERNAL_CMD_EVENT 0X00300004
+#define IPW_BASEBAND_POWER_DOWN 0x00000001
+
+#define IPW_MEM_HALT_AND_RESET 0x003000e0
+
+/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */
+#define IPW_BIT_HALT_RESET_ON 0x80000000
+#define IPW_BIT_HALT_RESET_OFF 0x00000000
+
+#define CB_LAST_VALID 0x20000000
+#define CB_INT_ENABLED 0x40000000
+#define CB_VALID 0x80000000
+#define CB_SRC_LE 0x08000000
+#define CB_DEST_LE 0x04000000
+#define CB_SRC_AUTOINC 0x00800000
+#define CB_SRC_IO_GATED 0x00400000
+#define CB_DEST_AUTOINC 0x00080000
+#define CB_SRC_SIZE_LONG 0x00200000
+#define CB_DEST_SIZE_LONG 0x00020000
+
+/* DMA DEFINES */
+
+#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000
+#define DMA_CB_STOP_AND_ABORT 0x00000C00
+#define DMA_CB_START 0x00000100
+
+#define IPW_SHARED_SRAM_SIZE 0x00030000
+#define IPW_SHARED_SRAM_DMA_CONTROL 0x00027000
+#define CB_MAX_LENGTH 0x1FFF
+
+#define IPW_HOST_EEPROM_DATA_SRAM_SIZE 0xA18
+#define IPW_EEPROM_IMAGE_SIZE 0x100
+
+/* DMA defs */
+#define IPW_DMA_I_CURRENT_CB 0x003000D0
+#define IPW_DMA_O_CURRENT_CB 0x003000D4
+#define IPW_DMA_I_DMA_CONTROL 0x003000A4
+#define IPW_DMA_I_CB_BASE 0x003000A0
+
+#define IPW_TX_CMD_QUEUE_BD_BASE 0x00000200
+#define IPW_TX_CMD_QUEUE_BD_SIZE 0x00000204
+#define IPW_TX_QUEUE_0_BD_BASE 0x00000208
+#define IPW_TX_QUEUE_0_BD_SIZE (0x0000020C)
+#define IPW_TX_QUEUE_1_BD_BASE 0x00000210
+#define IPW_TX_QUEUE_1_BD_SIZE 0x00000214
+#define IPW_TX_QUEUE_2_BD_BASE 0x00000218
+#define IPW_TX_QUEUE_2_BD_SIZE (0x0000021C)
+#define IPW_TX_QUEUE_3_BD_BASE 0x00000220
+#define IPW_TX_QUEUE_3_BD_SIZE 0x00000224
+#define IPW_RX_BD_BASE 0x00000240
+#define IPW_RX_BD_SIZE 0x00000244
+#define IPW_RFDS_TABLE_LOWER 0x00000500
+
+#define IPW_TX_CMD_QUEUE_READ_INDEX 0x00000280
+#define IPW_TX_QUEUE_0_READ_INDEX 0x00000284
+#define IPW_TX_QUEUE_1_READ_INDEX 0x00000288
+#define IPW_TX_QUEUE_2_READ_INDEX (0x0000028C)
+#define IPW_TX_QUEUE_3_READ_INDEX 0x00000290
+#define IPW_RX_READ_INDEX (0x000002A0)
+
+#define IPW_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80)
+#define IPW_TX_QUEUE_0_WRITE_INDEX (0x00000F84)
+#define IPW_TX_QUEUE_1_WRITE_INDEX (0x00000F88)
+#define IPW_TX_QUEUE_2_WRITE_INDEX (0x00000F8C)
+#define IPW_TX_QUEUE_3_WRITE_INDEX (0x00000F90)
+#define IPW_RX_WRITE_INDEX (0x00000FA0)
+
+/*
+ * EEPROM Related Definitions
+ */
+
+#define IPW_EEPROM_DATA_SRAM_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x814)
+#define IPW_EEPROM_DATA_SRAM_SIZE (IPW_SHARED_LOWER_BOUND + 0x818)
+#define IPW_EEPROM_LOAD_DISABLE (IPW_SHARED_LOWER_BOUND + 0x81C)
+#define IPW_EEPROM_DATA (IPW_SHARED_LOWER_BOUND + 0x820)
+#define IPW_EEPROM_UPPER_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x9E0)
+
+#define IPW_STATION_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0xA0C)
+#define IPW_STATION_TABLE_UPPER (IPW_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_REQUEST_ATIM (IPW_SHARED_LOWER_BOUND + 0xB0C)
+#define IPW_ATIM_SENT (IPW_SHARED_LOWER_BOUND + 0xB10)
+#define IPW_WHO_IS_AWAKE (IPW_SHARED_LOWER_BOUND + 0xB14)
+#define IPW_DURING_ATIM_WINDOW (IPW_SHARED_LOWER_BOUND + 0xB18)
+
+#define MSB 1
+#define LSB 0
+#define WORD_TO_BYTE(_word) ((_word) * sizeof(u16))
+
+#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \
+ ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) )
+
+/* EEPROM access by BYTE */
+#define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */
+#define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */
+#define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */
+#define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */
+#define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */
+#define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */
+#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */
+#define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */
+#define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */
+#define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */
+
+/* NIC type as found in the one byte EEPROM_NIC_TYPE offset */
+#define EEPROM_NIC_TYPE_0 0
+#define EEPROM_NIC_TYPE_1 1
+#define EEPROM_NIC_TYPE_2 2
+#define EEPROM_NIC_TYPE_3 3
+#define EEPROM_NIC_TYPE_4 4
+
+/* Bluetooth Coexistence capabilities as found in EEPROM_SKU_CAPABILITY */
+#define EEPROM_SKU_CAP_BT_CHANNEL_SIG 0x01 /* we can tell BT our channel # */
+#define EEPROM_SKU_CAP_BT_PRIORITY 0x02 /* BT can take priority over us */
+#define EEPROM_SKU_CAP_BT_OOB 0x04 /* we can signal BT out-of-band */
+
+#define FW_MEM_REG_LOWER_BOUND 0x00300000
+#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40)
+#define IPW_EVENT_REG (FW_MEM_REG_LOWER_BOUND + 0x04)
+#define EEPROM_BIT_SK (1<<0)
+#define EEPROM_BIT_CS (1<<1)
+#define EEPROM_BIT_DI (1<<2)
+#define EEPROM_BIT_DO (1<<4)
+
+#define EEPROM_CMD_READ 0x2
+
+/* Interrupts masks */
+#define IPW_INTA_NONE 0x00000000
+
+#define IPW_INTA_BIT_RX_TRANSFER 0x00000002
+#define IPW_INTA_BIT_STATUS_CHANGE 0x00000010
+#define IPW_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020
+
+//Inta Bits for CF
+#define IPW_INTA_BIT_TX_CMD_QUEUE 0x00000800
+#define IPW_INTA_BIT_TX_QUEUE_1 0x00001000
+#define IPW_INTA_BIT_TX_QUEUE_2 0x00002000
+#define IPW_INTA_BIT_TX_QUEUE_3 0x00004000
+#define IPW_INTA_BIT_TX_QUEUE_4 0x00008000
+
+#define IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000
+
+#define IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000
+#define IPW_INTA_BIT_POWER_DOWN 0x00200000
+
+#define IPW_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000
+#define IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000
+#define IPW_INTA_BIT_RF_KILL_DONE 0x04000000
+#define IPW_INTA_BIT_FATAL_ERROR 0x40000000
+#define IPW_INTA_BIT_PARITY_ERROR 0x80000000
+
+/* Interrupts enabled at init time. */
+#define IPW_INTA_MASK_ALL \
+ (IPW_INTA_BIT_TX_QUEUE_1 | \
+ IPW_INTA_BIT_TX_QUEUE_2 | \
+ IPW_INTA_BIT_TX_QUEUE_3 | \
+ IPW_INTA_BIT_TX_QUEUE_4 | \
+ IPW_INTA_BIT_TX_CMD_QUEUE | \
+ IPW_INTA_BIT_RX_TRANSFER | \
+ IPW_INTA_BIT_FATAL_ERROR | \
+ IPW_INTA_BIT_PARITY_ERROR | \
+ IPW_INTA_BIT_STATUS_CHANGE | \
+ IPW_INTA_BIT_FW_INITIALIZATION_DONE | \
+ IPW_INTA_BIT_BEACON_PERIOD_EXPIRED | \
+ IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \
+ IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN | \
+ IPW_INTA_BIT_POWER_DOWN | \
+ IPW_INTA_BIT_RF_KILL_DONE )
+
+/* FW event log definitions */
+#define EVENT_ELEM_SIZE (3 * sizeof(u32))
+#define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16))
+
+/* FW error log definitions */
+#define ERROR_ELEM_SIZE (7 * sizeof(u32))
+#define ERROR_START_OFFSET (1 * sizeof(u32))
+
+/* TX power level (dbm) */
+#define IPW_TX_POWER_MIN -12
+#define IPW_TX_POWER_MAX 20
+#define IPW_TX_POWER_DEFAULT IPW_TX_POWER_MAX
+
+enum {
+ IPW_FW_ERROR_OK = 0,
+ IPW_FW_ERROR_FAIL,
+ IPW_FW_ERROR_MEMORY_UNDERFLOW,
+ IPW_FW_ERROR_MEMORY_OVERFLOW,
+ IPW_FW_ERROR_BAD_PARAM,
+ IPW_FW_ERROR_BAD_CHECKSUM,
+ IPW_FW_ERROR_NMI_INTERRUPT,
+ IPW_FW_ERROR_BAD_DATABASE,
+ IPW_FW_ERROR_ALLOC_FAIL,
+ IPW_FW_ERROR_DMA_UNDERRUN,
+ IPW_FW_ERROR_DMA_STATUS,
+ IPW_FW_ERROR_DINO_ERROR,
+ IPW_FW_ERROR_EEPROM_ERROR,
+ IPW_FW_ERROR_SYSASSERT,
+ IPW_FW_ERROR_FATAL_ERROR
+};
+
+#define AUTH_OPEN 0
+#define AUTH_SHARED_KEY 1
+#define AUTH_LEAP 2
+#define AUTH_IGNORE 3
+
+#define HC_ASSOCIATE 0
+#define HC_REASSOCIATE 1
+#define HC_DISASSOCIATE 2
+#define HC_IBSS_START 3
+#define HC_IBSS_RECONF 4
+#define HC_DISASSOC_QUIET 5
+
+#define HC_QOS_SUPPORT_ASSOC cpu_to_le16(0x01)
+
+#define IPW_RATE_CAPABILITIES 1
+#define IPW_RATE_CONNECT 0
+
+/*
+ * Rate values and masks
+ */
+#define IPW_TX_RATE_1MB 0x0A
+#define IPW_TX_RATE_2MB 0x14
+#define IPW_TX_RATE_5MB 0x37
+#define IPW_TX_RATE_6MB 0x0D
+#define IPW_TX_RATE_9MB 0x0F
+#define IPW_TX_RATE_11MB 0x6E
+#define IPW_TX_RATE_12MB 0x05
+#define IPW_TX_RATE_18MB 0x07
+#define IPW_TX_RATE_24MB 0x09
+#define IPW_TX_RATE_36MB 0x0B
+#define IPW_TX_RATE_48MB 0x01
+#define IPW_TX_RATE_54MB 0x03
+
+#define IPW_ORD_TABLE_ID_MASK 0x0000FF00
+#define IPW_ORD_TABLE_VALUE_MASK 0x000000FF
+
+#define IPW_ORD_TABLE_0_MASK 0x0000F000
+#define IPW_ORD_TABLE_1_MASK 0x0000F100
+#define IPW_ORD_TABLE_2_MASK 0x0000F200
+#define IPW_ORD_TABLE_3_MASK 0x0000F300
+#define IPW_ORD_TABLE_4_MASK 0x0000F400
+#define IPW_ORD_TABLE_5_MASK 0x0000F500
+#define IPW_ORD_TABLE_6_MASK 0x0000F600
+#define IPW_ORD_TABLE_7_MASK 0x0000F700
+
+/*
+ * Table 0 Entries (all entries are 32 bits)
+ */
+enum {
+ IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1,
+ IPW_ORD_STAT_FRAG_TRESHOLD,
+ IPW_ORD_STAT_RTS_THRESHOLD,
+ IPW_ORD_STAT_TX_HOST_REQUESTS,
+ IPW_ORD_STAT_TX_HOST_COMPLETE,
+ IPW_ORD_STAT_TX_DIR_DATA,
+ IPW_ORD_STAT_TX_DIR_DATA_B_1,
+ IPW_ORD_STAT_TX_DIR_DATA_B_2,
+ IPW_ORD_STAT_TX_DIR_DATA_B_5_5,
+ IPW_ORD_STAT_TX_DIR_DATA_B_11,
+ /* Hole */
+
+ IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19,
+ IPW_ORD_STAT_TX_DIR_DATA_G_2,
+ IPW_ORD_STAT_TX_DIR_DATA_G_5_5,
+ IPW_ORD_STAT_TX_DIR_DATA_G_6,
+ IPW_ORD_STAT_TX_DIR_DATA_G_9,
+ IPW_ORD_STAT_TX_DIR_DATA_G_11,
+ IPW_ORD_STAT_TX_DIR_DATA_G_12,
+ IPW_ORD_STAT_TX_DIR_DATA_G_18,
+ IPW_ORD_STAT_TX_DIR_DATA_G_24,
+ IPW_ORD_STAT_TX_DIR_DATA_G_36,
+ IPW_ORD_STAT_TX_DIR_DATA_G_48,
+ IPW_ORD_STAT_TX_DIR_DATA_G_54,
+ IPW_ORD_STAT_TX_NON_DIR_DATA,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_1,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_2,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_B_11,
+ /* Hole */
+
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_2,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_6,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_9,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_11,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_12,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_18,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_24,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_36,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_48,
+ IPW_ORD_STAT_TX_NON_DIR_DATA_G_54,
+ IPW_ORD_STAT_TX_RETRY,
+ IPW_ORD_STAT_TX_FAILURE,
+ IPW_ORD_STAT_RX_ERR_CRC,
+ IPW_ORD_STAT_RX_ERR_ICV,
+ IPW_ORD_STAT_RX_NO_BUFFER,
+ IPW_ORD_STAT_FULL_SCANS,
+ IPW_ORD_STAT_PARTIAL_SCANS,
+ IPW_ORD_STAT_TGH_ABORTED_SCANS,
+ IPW_ORD_STAT_TX_TOTAL_BYTES,
+ IPW_ORD_STAT_CURR_RSSI_RAW,
+ IPW_ORD_STAT_RX_BEACON,
+ IPW_ORD_STAT_MISSED_BEACONS,
+ IPW_ORD_TABLE_0_LAST
+};
+
+#define IPW_RSSI_TO_DBM 112
+
+/* Table 1 Entries
+ */
+enum {
+ IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1,
+};
+
+/*
+ * Table 2 Entries
+ *
+ * FW_VERSION: 16 byte string
+ * FW_DATE: 16 byte string (only 14 bytes used)
+ * UCODE_VERSION: 4 byte version code
+ * UCODE_DATE: 5 bytes code code
+ * ADDAPTER_MAC: 6 byte MAC address
+ * RTC: 4 byte clock
+ */
+enum {
+ IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1,
+ IPW_ORD_STAT_FW_DATE,
+ IPW_ORD_STAT_UCODE_VERSION,
+ IPW_ORD_STAT_UCODE_DATE,
+ IPW_ORD_STAT_ADAPTER_MAC,
+ IPW_ORD_STAT_RTC,
+ IPW_ORD_TABLE_2_LAST
+};
+
+/* Table 3 */
+enum {
+ IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0,
+ IPW_ORD_STAT_TX_PACKET_FAILURE,
+ IPW_ORD_STAT_TX_PACKET_SUCCESS,
+ IPW_ORD_STAT_TX_PACKET_ABORTED,
+ IPW_ORD_TABLE_3_LAST
+};
+
+/* Table 4 */
+enum {
+ IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK
+};
+
+/* Table 5 */
+enum {
+ IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK,
+ IPW_ORD_STAT_AP_ASSNS,
+ IPW_ORD_STAT_ROAM,
+ IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS,
+ IPW_ORD_STAT_ROAM_CAUSE_UNASSOC,
+ IPW_ORD_STAT_ROAM_CAUSE_RSSI,
+ IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY,
+ IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE,
+ IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX,
+ IPW_ORD_STAT_LINK_UP,
+ IPW_ORD_STAT_LINK_DOWN,
+ IPW_ORD_ANTENNA_DIVERSITY,
+ IPW_ORD_CURR_FREQ,
+ IPW_ORD_TABLE_5_LAST
+};
+
+/* Table 6 */
+enum {
+ IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK,
+ IPW_ORD_CURR_BSSID,
+ IPW_ORD_CURR_SSID,
+ IPW_ORD_TABLE_6_LAST
+};
+
+/* Table 7 */
+enum {
+ IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK,
+ IPW_ORD_STAT_PERCENT_TX_RETRIES,
+ IPW_ORD_STAT_PERCENT_LINK_QUALITY,
+ IPW_ORD_STAT_CURR_RSSI_DBM,
+ IPW_ORD_TABLE_7_LAST
+};
+
+#define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410)
+#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414)
+#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500)
+#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180)
+#define IPW_ORDINALS_TABLE_1 (IPW_SHARED_LOWER_BOUND + 0x184)
+#define IPW_ORDINALS_TABLE_2 (IPW_SHARED_LOWER_BOUND + 0x188)
+#define IPW_MEM_FIXED_OVERRIDE (IPW_SHARED_LOWER_BOUND + 0x41C)
+
+struct ipw_fixed_rate {
+ __le16 tx_rates;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+#define IPW_INDIRECT_ADDR_MASK (~0x3ul)
+
+struct host_cmd {
+ u8 cmd;
+ u8 len;
+ u16 reserved;
+ u32 *param;
+} __attribute__ ((packed)); /* XXX */
+
+struct cmdlog_host_cmd {
+ u8 cmd;
+ u8 len;
+ __le16 reserved;
+ char param[124];
+} __attribute__ ((packed));
+
+struct ipw_cmd_log {
+ unsigned long jiffies;
+ int retcode;
+ struct cmdlog_host_cmd cmd;
+};
+
+/* SysConfig command parameters ... */
+/* bt_coexistence param */
+#define CFG_BT_COEXISTENCE_SIGNAL_CHNL 0x01 /* tell BT our chnl # */
+#define CFG_BT_COEXISTENCE_DEFER 0x02 /* defer our Tx if BT traffic */
+#define CFG_BT_COEXISTENCE_KILL 0x04 /* kill our Tx if BT traffic */
+#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 /* multimedia extensions */
+#define CFG_BT_COEXISTENCE_OOB 0x10 /* signal BT via out-of-band */
+
+/* clear-to-send to self param */
+#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x00
+#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x01
+#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN
+
+/* Antenna diversity param (h/w can select best antenna, based on signal) */
+#define CFG_SYS_ANTENNA_BOTH 0x00 /* NIC selects best antenna */
+#define CFG_SYS_ANTENNA_A 0x01 /* force antenna A */
+#define CFG_SYS_ANTENNA_B 0x03 /* force antenna B */
+#define CFG_SYS_ANTENNA_SLOW_DIV 0x02 /* consider background noise */
+
+/*
+ * The definitions below were lifted off the ipw2100 driver, which only
+ * supports 'b' mode, so I'm sure these are not exactly correct.
+ *
+ * Somebody fix these!!
+ */
+#define REG_MIN_CHANNEL 0
+#define REG_MAX_CHANNEL 14
+
+#define REG_CHANNEL_MASK 0x00003FFF
+#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff
+
+#define IPW_MAX_CONFIG_RETRIES 10
+
+#endif /* __ipw2200_h__ */
diff --git a/net/ieee80211/ieee80211_geo.c b/drivers/net/wireless/ipw2x00/libipw_geo.c
index 960ad13f5e9f..960ad13f5e9f 100644
--- a/net/ieee80211/ieee80211_geo.c
+++ b/drivers/net/wireless/ipw2x00/libipw_geo.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c
new file mode 100644
index 000000000000..a2f5616d5b09
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/libipw_module.c
@@ -0,0 +1,293 @@
+/*******************************************************************************
+
+ Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <j@w1.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+
+ 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., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <asm/uaccess.h>
+#include <net/net_namespace.h>
+#include <net/arp.h>
+
+#include <net/ieee80211.h>
+
+#define DRV_DESCRIPTION "802.11 data/management/control stack"
+#define DRV_NAME "ieee80211"
+#define DRV_VERSION IEEE80211_VERSION
+#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>"
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_LICENSE("GPL");
+
+static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
+{
+ if (ieee->networks)
+ return 0;
+
+ ieee->networks =
+ kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
+ GFP_KERNEL);
+ if (!ieee->networks) {
+ printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
+ ieee->dev->name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void ieee80211_network_reset(struct ieee80211_network *network)
+{
+ if (!network)
+ return;
+
+ if (network->ibss_dfs) {
+ kfree(network->ibss_dfs);
+ network->ibss_dfs = NULL;
+ }
+}
+
+static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
+{
+ int i;
+
+ if (!ieee->networks)
+ return;
+
+ for (i = 0; i < MAX_NETWORK_COUNT; i++)
+ if (ieee->networks[i].ibss_dfs)
+ kfree(ieee->networks[i].ibss_dfs);
+
+ kfree(ieee->networks);
+ ieee->networks = NULL;
+}
+
+static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
+{
+ int i;
+
+ INIT_LIST_HEAD(&ieee->network_free_list);
+ INIT_LIST_HEAD(&ieee->network_list);
+ for (i = 0; i < MAX_NETWORK_COUNT; i++)
+ list_add_tail(&ieee->networks[i].list,
+ &ieee->network_free_list);
+}
+
+static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static struct net_device_stats *ieee80211_generic_get_stats(
+ struct net_device *dev)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ return &ieee->stats;
+}
+
+struct net_device *alloc_ieee80211(int sizeof_priv)
+{
+ struct ieee80211_device *ieee;
+ struct net_device *dev;
+ int err;
+
+ IEEE80211_DEBUG_INFO("Initializing...\n");
+
+ dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
+ if (!dev) {
+ IEEE80211_ERROR("Unable to allocate network device.\n");
+ goto failed;
+ }
+ ieee = netdev_priv(dev);
+ dev->hard_start_xmit = ieee80211_xmit;
+ dev->change_mtu = ieee80211_change_mtu;
+
+ /* Drivers are free to override this if the generic implementation
+ * does not meet their needs. */
+ dev->get_stats = ieee80211_generic_get_stats;
+
+ ieee->dev = dev;
+
+ err = ieee80211_networks_allocate(ieee);
+ if (err) {
+ IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
+ goto failed_free_netdev;
+ }
+ ieee80211_networks_initialize(ieee);
+
+ /* Default fragmentation threshold is maximum payload size */
+ ieee->fts = DEFAULT_FTS;
+ ieee->rts = DEFAULT_FTS;
+ ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
+ ieee->open_wep = 1;
+
+ /* Default to enabling full open WEP with host based encrypt/decrypt */
+ ieee->host_encrypt = 1;
+ ieee->host_decrypt = 1;
+ ieee->host_mc_decrypt = 1;
+
+ /* Host fragementation in Open mode. Default is enabled.
+ * Note: host fragmentation is always enabled if host encryption
+ * is enabled. For cards can do hardware encryption, they must do
+ * hardware fragmentation as well. So we don't need a variable
+ * like host_enc_frag. */
+ ieee->host_open_frag = 1;
+ ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
+
+ spin_lock_init(&ieee->lock);
+
+ lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock);
+
+ ieee->wpa_enabled = 0;
+ ieee->drop_unencrypted = 0;
+ ieee->privacy_invoked = 0;
+
+ return dev;
+
+failed_free_netdev:
+ free_netdev(dev);
+failed:
+ return NULL;
+}
+
+void free_ieee80211(struct net_device *dev)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+
+ lib80211_crypt_info_free(&ieee->crypt_info);
+
+ ieee80211_networks_free(ieee);
+ free_netdev(dev);
+}
+
+#ifdef CONFIG_IEEE80211_DEBUG
+
+static int debug = 0;
+u32 ieee80211_debug_level = 0;
+EXPORT_SYMBOL_GPL(ieee80211_debug_level);
+static struct proc_dir_entry *ieee80211_proc = NULL;
+
+static int show_debug_level(char *page, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
+}
+
+static int store_debug_level(struct file *file, const char __user * buffer,
+ unsigned long count, void *data)
+{
+ char buf[] = "0x00000000\n";
+ unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
+ unsigned long val;
+
+ if (copy_from_user(buf, buffer, len))
+ return count;
+ buf[len] = 0;
+ if (sscanf(buf, "%li", &val) != 1)
+ printk(KERN_INFO DRV_NAME
+ ": %s is not in hex or decimal form.\n", buf);
+ else
+ ieee80211_debug_level = val;
+
+ return strnlen(buf, len);
+}
+#endif /* CONFIG_IEEE80211_DEBUG */
+
+static int __init ieee80211_init(void)
+{
+#ifdef CONFIG_IEEE80211_DEBUG
+ struct proc_dir_entry *e;
+
+ ieee80211_debug_level = debug;
+ ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
+ if (ieee80211_proc == NULL) {
+ IEEE80211_ERROR("Unable to create " DRV_NAME
+ " proc directory\n");
+ return -EIO;
+ }
+ e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
+ ieee80211_proc);
+ if (!e) {
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ ieee80211_proc = NULL;
+ return -EIO;
+ }
+ e->read_proc = show_debug_level;
+ e->write_proc = store_debug_level;
+ e->data = NULL;
+#endif /* CONFIG_IEEE80211_DEBUG */
+
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
+ printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
+
+ return 0;
+}
+
+static void __exit ieee80211_exit(void)
+{
+#ifdef CONFIG_IEEE80211_DEBUG
+ if (ieee80211_proc) {
+ remove_proc_entry("debug_level", ieee80211_proc);
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ ieee80211_proc = NULL;
+ }
+#endif /* CONFIG_IEEE80211_DEBUG */
+}
+
+#ifdef CONFIG_IEEE80211_DEBUG
+#include <linux/moduleparam.h>
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "debug output mask");
+#endif /* CONFIG_IEEE80211_DEBUG */
+
+module_exit(ieee80211_exit);
+module_init(ieee80211_init);
+
+EXPORT_SYMBOL(alloc_ieee80211);
+EXPORT_SYMBOL(free_ieee80211);
diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c
new file mode 100644
index 000000000000..9c67dfae4320
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/libipw_rx.c
@@ -0,0 +1,1799 @@
+/*
+ * Original code based Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2005, Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+
+#include <net/lib80211.h>
+#include <net/ieee80211.h>
+
+static void ieee80211_monitor_rx(struct ieee80211_device *ieee,
+ struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_control);
+
+ skb->dev = ieee->dev;
+ skb_reset_mac_header(skb);
+ skb_pull(skb, ieee80211_get_hdrlen(fc));
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_80211_RAW);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct ieee80211_frag_entry *ieee80211_frag_cache_find(struct
+ ieee80211_device
+ *ieee,
+ unsigned int seq,
+ unsigned int frag,
+ u8 * src,
+ u8 * dst)
+{
+ struct ieee80211_frag_entry *entry;
+ int i;
+
+ for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) {
+ entry = &ieee->frag_cache[i];
+ if (entry->skb != NULL &&
+ time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+ IEEE80211_DEBUG_FRAG("expiring fragment cache entry "
+ "seq=%u last_frag=%u\n",
+ entry->seq, entry->last_frag);
+ dev_kfree_skb_any(entry->skb);
+ entry->skb = NULL;
+ }
+
+ if (entry->skb != NULL && entry->seq == seq &&
+ (entry->last_frag + 1 == frag || frag == -1) &&
+ !compare_ether_addr(entry->src_addr, src) &&
+ !compare_ether_addr(entry->dst_addr, dst))
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *hdr)
+{
+ struct sk_buff *skb = NULL;
+ u16 sc;
+ unsigned int frag, seq;
+ struct ieee80211_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ seq = WLAN_GET_SEQ_SEQ(sc);
+
+ if (frag == 0) {
+ /* Reserve enough space to fit maximum frame length */
+ skb = dev_alloc_skb(ieee->dev->mtu +
+ sizeof(struct ieee80211_hdr_4addr) +
+ 8 /* LLC */ +
+ 2 /* alignment */ +
+ 8 /* WEP */ + ETH_ALEN /* WDS */ );
+ if (skb == NULL)
+ return NULL;
+
+ entry = &ieee->frag_cache[ieee->frag_next_idx];
+ ieee->frag_next_idx++;
+ if (ieee->frag_next_idx >= IEEE80211_FRAG_CACHE_LEN)
+ ieee->frag_next_idx = 0;
+
+ if (entry->skb != NULL)
+ dev_kfree_skb_any(entry->skb);
+
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->last_frag = frag;
+ entry->skb = skb;
+ memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+ } else {
+ /* received a fragment of a frame for which the head fragment
+ * should have already been received */
+ entry = ieee80211_frag_cache_find(ieee, seq, frag, hdr->addr2,
+ hdr->addr1);
+ if (entry != NULL) {
+ entry->last_frag = frag;
+ skb = entry->skb;
+ }
+ }
+
+ return skb;
+}
+
+/* Called only as a tasklet (software IRQ) */
+static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *hdr)
+{
+ u16 sc;
+ unsigned int seq;
+ struct ieee80211_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctl);
+ seq = WLAN_GET_SEQ_SEQ(sc);
+
+ entry = ieee80211_frag_cache_find(ieee, seq, -1, hdr->addr2,
+ hdr->addr1);
+
+ if (entry == NULL) {
+ IEEE80211_DEBUG_FRAG("could not invalidate fragment cache "
+ "entry (seq=%u)\n", seq);
+ return -1;
+ }
+
+ entry->skb = NULL;
+ return 0;
+}
+
+#ifdef NOT_YET
+/* ieee80211_rx_frame_mgtmt
+ *
+ * Responsible for handling management control frames
+ *
+ * Called by ieee80211_rx */
+static int
+ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats, u16 type,
+ u16 stype)
+{
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ printk(KERN_DEBUG "%s: Master mode not yet suppported.\n",
+ ieee->dev->name);
+ return 0;
+/*
+ hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr_4addr *)
+ skb->data);*/
+ }
+
+ if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) {
+ if (stype == WLAN_FC_STYPE_BEACON &&
+ ieee->iw_mode == IW_MODE_MASTER) {
+ struct sk_buff *skb2;
+ /* Process beacon frames also in kernel driver to
+ * update STA(AP) table statistics */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ hostap_rx(skb2->dev, skb2, rx_stats);
+ }
+
+ /* send management frames to the user space daemon for
+ * processing */
+ ieee->apdevstats.rx_packets++;
+ ieee->apdevstats.rx_bytes += skb->len;
+ prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+ return 0;
+ }
+
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) {
+ printk(KERN_DEBUG "%s: unknown management frame "
+ "(type=0x%02x, stype=0x%02x) dropped\n",
+ skb->dev->name, type, stype);
+ return -1;
+ }
+
+ hostap_rx(skb->dev, skb, rx_stats);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame "
+ "received in non-Host AP mode\n", skb->dev->name);
+ return -1;
+}
+#endif
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+ { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+/* Called by ieee80211_rx_frame_decrypt */
+static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee,
+ struct sk_buff *skb)
+{
+ struct net_device *dev = ieee->dev;
+ u16 fc, ethertype;
+ struct ieee80211_hdr_3addr *hdr;
+ u8 *pos;
+
+ if (skb->len < 24)
+ return 0;
+
+ hdr = (struct ieee80211_hdr_3addr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /* check that the frame is unicast frame to us */
+ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_TODS &&
+ !compare_ether_addr(hdr->addr1, dev->dev_addr) &&
+ !compare_ether_addr(hdr->addr3, dev->dev_addr)) {
+ /* ToDS frame with own addr BSSID and DA */
+ } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_FROMDS &&
+ !compare_ether_addr(hdr->addr1, dev->dev_addr)) {
+ /* FromDS frame with own addr as DA */
+ } else
+ return 0;
+
+ if (skb->len < 24 + 8)
+ return 0;
+
+ /* check for port access entity Ethernet type */
+ pos = skb->data + 24;
+ ethertype = (pos[6] << 8) | pos[7];
+ if (ethertype == ETH_P_PAE)
+ return 1;
+
+ return 0;
+}
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static int
+ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct lib80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr_3addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+ return 0;
+
+ hdr = (struct ieee80211_hdr_3addr *)skb->data;
+ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ IEEE80211_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n",
+ hdr->addr2, res);
+ if (res == -2)
+ IEEE80211_DEBUG_DROP("Decryption failed ICV "
+ "mismatch (key %d)\n",
+ skb->data[hdrlen + 3] >> 6);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ return -1;
+ }
+
+ return res;
+}
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static int
+ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee,
+ struct sk_buff *skb, int keyidx,
+ struct lib80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr_3addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+ return 0;
+
+ hdr = (struct ieee80211_hdr_3addr *)skb->data;
+ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+ " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2,
+ keyidx);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats)
+{
+ struct net_device *dev = ieee->dev;
+ struct ieee80211_hdr_4addr *hdr;
+ size_t hdrlen;
+ u16 fc, type, stype, sc;
+ struct net_device_stats *stats;
+ unsigned int frag;
+ u8 *payload;
+ u16 ethertype;
+#ifdef NOT_YET
+ struct net_device *wds = NULL;
+ struct sk_buff *skb2 = NULL;
+ struct net_device *wds = NULL;
+ int frame_authorized = 0;
+ int from_assoc_ap = 0;
+ void *sta = NULL;
+#endif
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ struct lib80211_crypt_data *crypt = NULL;
+ int keyidx = 0;
+ int can_be_decrypted = 0;
+
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ stats = &ieee->stats;
+
+ if (skb->len < 10) {
+ printk(KERN_INFO "%s: SKB length < 10\n", dev->name);
+ goto rx_dropped;
+ }
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ hdrlen = ieee80211_get_hdrlen(fc);
+
+ if (skb->len < hdrlen) {
+ printk(KERN_INFO "%s: invalid SKB length %d\n",
+ dev->name, skb->len);
+ goto rx_dropped;
+ }
+
+ /* Put this code here so that we avoid duplicating it in all
+ * Rx paths. - Jean II */
+#ifdef CONFIG_WIRELESS_EXT
+#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
+ /* If spy monitoring on */
+ if (ieee->spy_data.spy_number > 0) {
+ struct iw_quality wstats;
+
+ wstats.updated = 0;
+ if (rx_stats->mask & IEEE80211_STATMASK_RSSI) {
+ wstats.level = rx_stats->signal;
+ wstats.updated |= IW_QUAL_LEVEL_UPDATED;
+ } else
+ wstats.updated |= IW_QUAL_LEVEL_INVALID;
+
+ if (rx_stats->mask & IEEE80211_STATMASK_NOISE) {
+ wstats.noise = rx_stats->noise;
+ wstats.updated |= IW_QUAL_NOISE_UPDATED;
+ } else
+ wstats.updated |= IW_QUAL_NOISE_INVALID;
+
+ if (rx_stats->mask & IEEE80211_STATMASK_SIGNAL) {
+ wstats.qual = rx_stats->signal;
+ wstats.updated |= IW_QUAL_QUAL_UPDATED;
+ } else
+ wstats.updated |= IW_QUAL_QUAL_INVALID;
+
+ /* Update spy records */
+ wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
+ }
+#endif /* IW_WIRELESS_SPY */
+#endif /* CONFIG_WIRELESS_EXT */
+
+#ifdef NOT_YET
+ hostap_update_rx_stats(local->ap, hdr, rx_stats);
+#endif
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+ ieee80211_monitor_rx(ieee, skb, rx_stats);
+ return 1;
+ }
+
+ can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) ||
+ is_broadcast_ether_addr(hdr->addr2)) ?
+ ieee->host_mc_decrypt : ieee->host_decrypt;
+
+ if (can_be_decrypted) {
+ if (skb->len >= hdrlen + 3) {
+ /* Top two-bits of byte 3 are the key index */
+ keyidx = skb->data[hdrlen + 3] >> 6;
+ }
+
+ /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx
+ * is only allowed 2-bits of storage, no value of keyidx can
+ * be provided via above code that would result in keyidx
+ * being out of range */
+ crypt = ieee->crypt_info.crypt[keyidx];
+
+#ifdef NOT_YET
+ sta = NULL;
+
+ /* Use station specific key to override default keys if the
+ * receiver address is a unicast address ("individual RA"). If
+ * bcrx_sta_key parameter is set, station specific key is used
+ * even with broad/multicast targets (this is against IEEE
+ * 802.11, but makes it easier to use different keys with
+ * stations that do not support WEP key mapping). */
+
+ if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+ (void)hostap_handle_sta_crypto(local, hdr, &crypt,
+ &sta);
+#endif
+
+ /* allow NULL decrypt to indicate an station specific override
+ * for default encryption */
+ if (crypt && (crypt->ops == NULL ||
+ crypt->ops->decrypt_mpdu == NULL))
+ crypt = NULL;
+
+ if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
+ /* This seems to be triggered by some (multicast?)
+ * frames from other than current BSS, so just drop the
+ * frames silently instead of filling system log with
+ * these reports. */
+ IEEE80211_DEBUG_DROP("Decryption failed (not set)"
+ " (SA=%pM)\n", hdr->addr2);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ goto rx_dropped;
+ }
+ }
+#ifdef NOT_YET
+ if (type != WLAN_FC_TYPE_DATA) {
+ if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH &&
+ fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt &&
+ (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) {
+ printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
+ "from %pM\n", dev->name, hdr->addr2);
+ /* TODO: could inform hostapd about this so that it
+ * could send auth failure report */
+ goto rx_dropped;
+ }
+
+ if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
+ goto rx_dropped;
+ else
+ goto rx_exit;
+ }
+#endif
+ /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */
+ if (sc == ieee->prev_seq_ctl)
+ goto rx_dropped;
+ else
+ ieee->prev_seq_ctl = sc;
+
+ /* Data frame - extract src/dst addresses */
+ if (skb->len < IEEE80211_3ADDR_LEN)
+ goto rx_dropped;
+
+ switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr3, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_TODS:
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ if (skb->len < IEEE80211_4ADDR_LEN)
+ goto rx_dropped;
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr4, ETH_ALEN);
+ break;
+ case 0:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ break;
+ }
+
+#ifdef NOT_YET
+ if (hostap_rx_frame_wds(ieee, hdr, fc, &wds))
+ goto rx_dropped;
+ if (wds) {
+ skb->dev = dev = wds;
+ stats = hostap_get_stats(dev);
+ }
+
+ if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
+ (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_FROMDS && ieee->stadev
+ && !compare_ether_addr(hdr->addr2, ieee->assoc_ap_addr)) {
+ /* Frame from BSSID of the AP for which we are a client */
+ skb->dev = dev = ieee->stadev;
+ stats = hostap_get_stats(dev);
+ from_assoc_ap = 1;
+ }
+#endif
+
+#ifdef NOT_YET
+ if ((ieee->iw_mode == IW_MODE_MASTER ||
+ ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) {
+ switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats,
+ wds != NULL)) {
+ case AP_RX_CONTINUE_NOT_AUTHORIZED:
+ frame_authorized = 0;
+ break;
+ case AP_RX_CONTINUE:
+ frame_authorized = 1;
+ break;
+ case AP_RX_DROP:
+ goto rx_dropped;
+ case AP_RX_EXIT:
+ goto rx_exit;
+ }
+ }
+#endif
+
+ /* Nullfunc frames may have PS-bit set, so they must be passed to
+ * hostap_handle_sta_rx() before being dropped here. */
+
+ stype &= ~IEEE80211_STYPE_QOS_DATA;
+
+ if (stype != IEEE80211_STYPE_DATA &&
+ stype != IEEE80211_STYPE_DATA_CFACK &&
+ stype != IEEE80211_STYPE_DATA_CFPOLL &&
+ stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
+ if (stype != IEEE80211_STYPE_NULLFUNC)
+ IEEE80211_DEBUG_DROP("RX: dropped data frame "
+ "with no data (type=0x%02x, "
+ "subtype=0x%02x, len=%d)\n",
+ type, stype, skb->len);
+ goto rx_dropped;
+ }
+
+ /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
+ (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0)
+ goto rx_dropped;
+
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+
+ /* skb: hdr + (possibly fragmented) plaintext payload */
+ // PR: FIXME: hostap has additional conditions in the "if" below:
+ // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
+ if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) {
+ int flen;
+ struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr);
+ IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
+
+ if (!frag_skb) {
+ IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG,
+ "Rx cannot get skb from fragment "
+ "cache (morefrag=%d seq=%u frag=%u)\n",
+ (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
+ WLAN_GET_SEQ_SEQ(sc), frag);
+ goto rx_dropped;
+ }
+
+ flen = skb->len;
+ if (frag != 0)
+ flen -= hdrlen;
+
+ if (frag_skb->tail + flen > frag_skb->end) {
+ printk(KERN_WARNING "%s: host decrypted and "
+ "reassembled frame did not fit skb\n",
+ dev->name);
+ ieee80211_frag_cache_invalidate(ieee, hdr);
+ goto rx_dropped;
+ }
+
+ if (frag == 0) {
+ /* copy first fragment (including full headers) into
+ * beginning of the fragment cache skb */
+ skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen);
+ } else {
+ /* append frame payload to the end of the fragment
+ * cache skb */
+ skb_copy_from_linear_data_offset(skb, hdrlen,
+ skb_put(frag_skb, flen), flen);
+ }
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+
+ if (fc & IEEE80211_FCTL_MOREFRAGS) {
+ /* more fragments expected - leave the skb in fragment
+ * cache for now; it will be delivered to upper layers
+ * after all fragments have been received */
+ goto rx_exit;
+ }
+
+ /* this was the last fragment and the frame will be
+ * delivered, so remove skb from fragment cache */
+ skb = frag_skb;
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ ieee80211_frag_cache_invalidate(ieee, hdr);
+ }
+
+ /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+ * encrypted/authenticated */
+ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
+ ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
+ goto rx_dropped;
+
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) {
+ if ( /*ieee->ieee802_1x && */
+ ieee80211_is_eapol_frame(ieee, skb)) {
+ /* pass unencrypted EAPOL frames even if encryption is
+ * configured */
+ } else {
+ IEEE80211_DEBUG_DROP("encryption configured, but RX "
+ "frame not encrypted (SA=%pM)\n",
+ hdr->addr2);
+ goto rx_dropped;
+ }
+ }
+
+ if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep &&
+ !ieee80211_is_eapol_frame(ieee, skb)) {
+ IEEE80211_DEBUG_DROP("dropped unencrypted RX data "
+ "frame from %pM (drop_unencrypted=1)\n",
+ hdr->addr2);
+ goto rx_dropped;
+ }
+
+ /* If the frame was decrypted in hardware, we may need to strip off
+ * any security data (IV, ICV, etc) that was left behind */
+ if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) &&
+ ieee->host_strip_iv_icv) {
+ int trimlen = 0;
+
+ /* Top two-bits of byte 3 are the key index */
+ if (skb->len >= hdrlen + 3)
+ keyidx = skb->data[hdrlen + 3] >> 6;
+
+ /* To strip off any security data which appears before the
+ * payload, we simply increase hdrlen (as the header gets
+ * chopped off immediately below). For the security data which
+ * appears after the payload, we use skb_trim. */
+
+ switch (ieee->sec.encode_alg[keyidx]) {
+ case SEC_ALG_WEP:
+ /* 4 byte IV */
+ hdrlen += 4;
+ /* 4 byte ICV */
+ trimlen = 4;
+ break;
+ case SEC_ALG_TKIP:
+ /* 4 byte IV, 4 byte ExtIV */
+ hdrlen += 8;
+ /* 8 byte MIC, 4 byte ICV */
+ trimlen = 12;
+ break;
+ case SEC_ALG_CCMP:
+ /* 8 byte CCMP header */
+ hdrlen += 8;
+ /* 8 byte MIC */
+ trimlen = 8;
+ break;
+ }
+
+ if (skb->len < trimlen)
+ goto rx_dropped;
+
+ __skb_trim(skb, skb->len - trimlen);
+
+ if (skb->len < hdrlen)
+ goto rx_dropped;
+ }
+
+ /* skb: hdr + (possible reassembled) full plaintext payload */
+
+ payload = skb->data + hdrlen;
+ ethertype = (payload[6] << 8) | payload[7];
+
+#ifdef NOT_YET
+ /* If IEEE 802.1X is used, check whether the port is authorized to send
+ * the received frame. */
+ if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) {
+ if (ethertype == ETH_P_PAE) {
+ printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n",
+ dev->name);
+ if (ieee->hostapd && ieee->apdev) {
+ /* Send IEEE 802.1X frames to the user
+ * space daemon for processing */
+ prism2_rx_80211(ieee->apdev, skb, rx_stats,
+ PRISM2_RX_MGMT);
+ ieee->apdevstats.rx_packets++;
+ ieee->apdevstats.rx_bytes += skb->len;
+ goto rx_exit;
+ }
+ } else if (!frame_authorized) {
+ printk(KERN_DEBUG "%s: dropped frame from "
+ "unauthorized port (IEEE 802.1X): "
+ "ethertype=0x%04x\n", dev->name, ethertype);
+ goto rx_dropped;
+ }
+ }
+#endif
+
+ /* convert hdr + possible LLC headers into Ethernet header */
+ if (skb->len - hdrlen >= 8 &&
+ ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(skb, hdrlen + SNAP_SIZE);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ __be16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ skb_pull(skb, hdrlen);
+ len = htons(skb->len);
+ memcpy(skb_push(skb, 2), &len, 2);
+ memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
+ }
+
+#ifdef NOT_YET
+ if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) {
+ /* Non-standard frame: get addr4 from its bogus location after
+ * the payload */
+ skb_copy_to_linear_data_offset(skb, ETH_ALEN,
+ skb->data + skb->len - ETH_ALEN,
+ ETH_ALEN);
+ skb_trim(skb, skb->len - ETH_ALEN);
+ }
+#endif
+
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
+#ifdef NOT_YET
+ if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) {
+ if (dst[0] & 0x01) {
+ /* copy multicast frame both to the higher layers and
+ * to the wireless media */
+ ieee->ap->bridged_multicast++;
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 == NULL)
+ printk(KERN_DEBUG "%s: skb_clone failed for "
+ "multicast frame\n", dev->name);
+ } else if (hostap_is_sta_assoc(ieee->ap, dst)) {
+ /* send frame directly to the associated STA using
+ * wireless media and not passing to higher layers */
+ ieee->ap->bridged_unicast++;
+ skb2 = skb;
+ skb = NULL;
+ }
+ }
+
+ if (skb2 != NULL) {
+ /* send to wireless media */
+ skb2->dev = dev;
+ skb2->protocol = htons(ETH_P_802_3);
+ skb_reset_mac_header(skb2);
+ skb_reset_network_header(skb2);
+ /* skb2->network_header += ETH_HLEN; */
+ dev_queue_xmit(skb2);
+ }
+#endif
+
+ if (skb) {
+ skb->protocol = eth_type_trans(skb, dev);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+ if (netif_rx(skb) == NET_RX_DROP) {
+ /* netif_rx always succeeds, but it might drop
+ * the packet. If it drops the packet, we log that
+ * in our stats. */
+ IEEE80211_DEBUG_DROP
+ ("RX: netif_rx dropped the packet\n");
+ stats->rx_dropped++;
+ }
+ }
+
+ rx_exit:
+#ifdef NOT_YET
+ if (sta)
+ hostap_handle_sta_release(sta);
+#endif
+ return 1;
+
+ rx_dropped:
+ stats->rx_dropped++;
+
+ /* Returning 0 indicates to caller that we have not handled the SKB--
+ * so it is still allocated and can be used again by underlying
+ * hardware as a DMA target */
+ return 0;
+}
+
+/* Filter out unrelated packets, call ieee80211_rx[_mgt]
+ * This function takes over the skb, it should not be used again after calling
+ * this function. */
+void ieee80211_rx_any(struct ieee80211_device *ieee,
+ struct sk_buff *skb, struct ieee80211_rx_stats *stats)
+{
+ struct ieee80211_hdr_4addr *hdr;
+ int is_packet_for_us;
+ u16 fc;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ if (!ieee80211_rx(ieee, skb, stats))
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ if (skb->len < sizeof(struct ieee80211_hdr))
+ goto drop_free;
+
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if ((fc & IEEE80211_FCTL_VERS) != 0)
+ goto drop_free;
+
+ switch (fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_MGMT:
+ if (skb->len < sizeof(struct ieee80211_hdr_3addr))
+ goto drop_free;
+ ieee80211_rx_mgt(ieee, hdr, stats);
+ dev_kfree_skb_irq(skb);
+ return;
+ case IEEE80211_FTYPE_DATA:
+ break;
+ case IEEE80211_FTYPE_CTL:
+ return;
+ default:
+ return;
+ }
+
+ is_packet_for_us = 0;
+ switch (ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ /* our BSS and not from/to DS */
+ if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0)
+ if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
+ /* promisc: get all */
+ if (ieee->dev->flags & IFF_PROMISC)
+ is_packet_for_us = 1;
+ /* to us */
+ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
+ is_packet_for_us = 1;
+ /* mcast */
+ else if (is_multicast_ether_addr(hdr->addr1))
+ is_packet_for_us = 1;
+ }
+ break;
+ case IW_MODE_INFRA:
+ /* our BSS (== from our AP) and from DS */
+ if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0)
+ if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
+ /* promisc: get all */
+ if (ieee->dev->flags & IFF_PROMISC)
+ is_packet_for_us = 1;
+ /* to us */
+ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
+ is_packet_for_us = 1;
+ /* mcast */
+ else if (is_multicast_ether_addr(hdr->addr1)) {
+ /* not our own packet bcasted from AP */
+ if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN))
+ is_packet_for_us = 1;
+ }
+ }
+ break;
+ default:
+ /* ? */
+ break;
+ }
+
+ if (is_packet_for_us)
+ if (!ieee80211_rx(ieee, skb, stats))
+ dev_kfree_skb_irq(skb);
+ return;
+
+drop_free:
+ dev_kfree_skb_irq(skb);
+ ieee->stats.rx_dropped++;
+ return;
+}
+
+#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
+
+static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
+
+/*
+* Make ther structure we read from the beacon packet has
+* the right values
+*/
+static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element
+ *info_element, int sub_type)
+{
+
+ if (info_element->qui_subtype != sub_type)
+ return -1;
+ if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN))
+ return -1;
+ if (info_element->qui_type != QOS_OUI_TYPE)
+ return -1;
+ if (info_element->version != QOS_VERSION_1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Parse a QoS parameter element
+ */
+static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info
+ *element_param, struct ieee80211_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct ieee80211_qos_parameter_info) - 2;
+
+ if ((info_element == NULL) || (element_param == NULL))
+ return -1;
+
+ if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) {
+ memcpy(element_param->info_element.qui, info_element->data,
+ info_element->len);
+ element_param->info_element.elementID = info_element->id;
+ element_param->info_element.length = info_element->len;
+ } else
+ ret = -1;
+ if (ret == 0)
+ ret = ieee80211_verify_qos_info(&element_param->info_element,
+ QOS_OUI_PARAM_SUB_TYPE);
+ return ret;
+}
+
+/*
+ * Parse a QoS information element
+ */
+static int ieee80211_read_qos_info_element(struct
+ ieee80211_qos_information_element
+ *element_info, struct ieee80211_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct ieee80211_qos_information_element) - 2;
+
+ if (element_info == NULL)
+ return -1;
+ if (info_element == NULL)
+ return -1;
+
+ if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) {
+ memcpy(element_info->qui, info_element->data,
+ info_element->len);
+ element_info->elementID = info_element->id;
+ element_info->length = info_element->len;
+ } else
+ ret = -1;
+
+ if (ret == 0)
+ ret = ieee80211_verify_qos_info(element_info,
+ QOS_OUI_INFO_SUB_TYPE);
+ return ret;
+}
+
+/*
+ * Write QoS parameters from the ac parameters.
+ */
+static int ieee80211_qos_convert_ac_to_parameters(struct
+ ieee80211_qos_parameter_info
+ *param_elm, struct
+ ieee80211_qos_parameters
+ *qos_param)
+{
+ int rc = 0;
+ int i;
+ struct ieee80211_qos_ac_parameter *ac_params;
+ u32 txop;
+ u8 cw_min;
+ u8 cw_max;
+
+ for (i = 0; i < QOS_QUEUE_NUM; i++) {
+ ac_params = &(param_elm->ac_params_record[i]);
+
+ qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F;
+ qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2;
+
+ cw_min = ac_params->ecw_min_max & 0x0F;
+ qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1);
+
+ cw_max = (ac_params->ecw_min_max & 0xF0) >> 4;
+ qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1);
+
+ qos_param->flag[i] =
+ (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00;
+
+ txop = le16_to_cpu(ac_params->tx_op_limit) * 32;
+ qos_param->tx_op_limit[i] = cpu_to_le16(txop);
+ }
+ return rc;
+}
+
+/*
+ * we have a generic data element which it may contain QoS information or
+ * parameters element. check the information element length to decide
+ * which type to read
+ */
+static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element
+ *info_element,
+ struct ieee80211_network *network)
+{
+ int rc = 0;
+ struct ieee80211_qos_parameters *qos_param = NULL;
+ struct ieee80211_qos_information_element qos_info_element;
+
+ rc = ieee80211_read_qos_info_element(&qos_info_element, info_element);
+
+ if (rc == 0) {
+ network->qos_data.param_count = qos_info_element.ac_info & 0x0F;
+ network->flags |= NETWORK_HAS_QOS_INFORMATION;
+ } else {
+ struct ieee80211_qos_parameter_info param_element;
+
+ rc = ieee80211_read_qos_param_element(&param_element,
+ info_element);
+ if (rc == 0) {
+ qos_param = &(network->qos_data.parameters);
+ ieee80211_qos_convert_ac_to_parameters(&param_element,
+ qos_param);
+ network->flags |= NETWORK_HAS_QOS_PARAMETERS;
+ network->qos_data.param_count =
+ param_element.info_element.ac_info & 0x0F;
+ }
+ }
+
+ if (rc == 0) {
+ IEEE80211_DEBUG_QOS("QoS is supported\n");
+ network->qos_data.supported = 1;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_IEEE80211_DEBUG
+#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
+
+static const char *get_info_element_string(u16 id)
+{
+ switch (id) {
+ MFIE_STRING(SSID);
+ MFIE_STRING(RATES);
+ MFIE_STRING(FH_SET);
+ MFIE_STRING(DS_SET);
+ MFIE_STRING(CF_SET);
+ MFIE_STRING(TIM);
+ MFIE_STRING(IBSS_SET);
+ MFIE_STRING(COUNTRY);
+ MFIE_STRING(HOP_PARAMS);
+ MFIE_STRING(HOP_TABLE);
+ MFIE_STRING(REQUEST);
+ MFIE_STRING(CHALLENGE);
+ MFIE_STRING(POWER_CONSTRAINT);
+ MFIE_STRING(POWER_CAPABILITY);
+ MFIE_STRING(TPC_REQUEST);
+ MFIE_STRING(TPC_REPORT);
+ MFIE_STRING(SUPP_CHANNELS);
+ MFIE_STRING(CSA);
+ MFIE_STRING(MEASURE_REQUEST);
+ MFIE_STRING(MEASURE_REPORT);
+ MFIE_STRING(QUIET);
+ MFIE_STRING(IBSS_DFS);
+ MFIE_STRING(ERP_INFO);
+ MFIE_STRING(RSN);
+ MFIE_STRING(RATES_EX);
+ MFIE_STRING(GENERIC);
+ MFIE_STRING(QOS_PARAMETER);
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif
+
+static int ieee80211_parse_info_param(struct ieee80211_info_element
+ *info_element, u16 length,
+ struct ieee80211_network *network)
+{
+ DECLARE_SSID_BUF(ssid);
+ u8 i;
+#ifdef CONFIG_IEEE80211_DEBUG
+ char rates_str[64];
+ char *p;
+#endif
+
+ while (length >= sizeof(*info_element)) {
+ if (sizeof(*info_element) + info_element->len > length) {
+ IEEE80211_DEBUG_MGMT("Info elem: parse failed: "
+ "info_element->len + 2 > left : "
+ "info_element->len+2=%zd left=%d, id=%d.\n",
+ info_element->len +
+ sizeof(*info_element),
+ length, info_element->id);
+ /* We stop processing but don't return an error here
+ * because some misbehaviour APs break this rule. ie.
+ * Orinoco AP1000. */
+ break;
+ }
+
+ switch (info_element->id) {
+ case MFIE_TYPE_SSID:
+ network->ssid_len = min(info_element->len,
+ (u8) IW_ESSID_MAX_SIZE);
+ memcpy(network->ssid, info_element->data,
+ network->ssid_len);
+ if (network->ssid_len < IW_ESSID_MAX_SIZE)
+ memset(network->ssid + network->ssid_len, 0,
+ IW_ESSID_MAX_SIZE - network->ssid_len);
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_SSID: '%s' len=%d.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->ssid_len);
+ break;
+
+ case MFIE_TYPE_RATES:
+#ifdef CONFIG_IEEE80211_DEBUG
+ p = rates_str;
+#endif
+ network->rates_len = min(info_element->len,
+ MAX_RATES_LENGTH);
+ for (i = 0; i < network->rates_len; i++) {
+ network->rates[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates[i]);
+#endif
+ if (ieee80211_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ IEEE80211_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+ }
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES: '%s' (%d)\n",
+ rates_str, network->rates_len);
+ break;
+
+ case MFIE_TYPE_RATES_EX:
+#ifdef CONFIG_IEEE80211_DEBUG
+ p = rates_str;
+#endif
+ network->rates_ex_len = min(info_element->len,
+ MAX_RATES_EX_LENGTH);
+ for (i = 0; i < network->rates_ex_len; i++) {
+ network->rates_ex[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates[i]);
+#endif
+ if (ieee80211_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ IEEE80211_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+ }
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
+ rates_str, network->rates_ex_len);
+ break;
+
+ case MFIE_TYPE_DS_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_DS_SET: %d\n",
+ info_element->data[0]);
+ network->channel = info_element->data[0];
+ break;
+
+ case MFIE_TYPE_FH_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_FH_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_CF_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_CF_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_TIM:
+ network->tim.tim_count = info_element->data[0];
+ network->tim.tim_period = info_element->data[1];
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: partially ignored\n");
+ break;
+
+ case MFIE_TYPE_ERP_INFO:
+ network->erp_value = info_element->data[0];
+ network->flags |= NETWORK_HAS_ERP_VALUE;
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
+ network->erp_value);
+ break;
+
+ case MFIE_TYPE_IBSS_SET:
+ network->atim_window = info_element->data[0];
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_IBSS_SET: %d\n",
+ network->atim_window);
+ break;
+
+ case MFIE_TYPE_CHALLENGE:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_CHALLENGE: ignored\n");
+ break;
+
+ case MFIE_TYPE_GENERIC:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_GENERIC: %d bytes\n",
+ info_element->len);
+ if (!ieee80211_parse_qos_info_param_IE(info_element,
+ network))
+ break;
+
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0xf2 &&
+ info_element->data[3] == 0x01) {
+ network->wpa_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->wpa_ie, info_element,
+ network->wpa_ie_len);
+ }
+ break;
+
+ case MFIE_TYPE_RSN:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RSN: %d bytes\n",
+ info_element->len);
+ network->rsn_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->rsn_ie, info_element,
+ network->rsn_ie_len);
+ break;
+
+ case MFIE_TYPE_QOS_PARAMETER:
+ printk(KERN_ERR
+ "QoS Error need to parse QOS_PARAMETER IE\n");
+ break;
+ /* 802.11h */
+ case MFIE_TYPE_POWER_CONSTRAINT:
+ network->power_constraint = info_element->data[0];
+ network->flags |= NETWORK_HAS_POWER_CONSTRAINT;
+ break;
+
+ case MFIE_TYPE_CSA:
+ network->power_constraint = info_element->data[0];
+ network->flags |= NETWORK_HAS_CSA;
+ break;
+
+ case MFIE_TYPE_QUIET:
+ network->quiet.count = info_element->data[0];
+ network->quiet.period = info_element->data[1];
+ network->quiet.duration = info_element->data[2];
+ network->quiet.offset = info_element->data[3];
+ network->flags |= NETWORK_HAS_QUIET;
+ break;
+
+ case MFIE_TYPE_IBSS_DFS:
+ if (network->ibss_dfs)
+ break;
+ network->ibss_dfs = kmemdup(info_element->data,
+ info_element->len,
+ GFP_ATOMIC);
+ if (!network->ibss_dfs)
+ return 1;
+ network->flags |= NETWORK_HAS_IBSS_DFS;
+ break;
+
+ case MFIE_TYPE_TPC_REPORT:
+ network->tpc_report.transmit_power =
+ info_element->data[0];
+ network->tpc_report.link_margin = info_element->data[1];
+ network->flags |= NETWORK_HAS_TPC_REPORT;
+ break;
+
+ default:
+ IEEE80211_DEBUG_MGMT
+ ("Unsupported info element: %s (%d)\n",
+ get_info_element_string(info_element->id),
+ info_element->id);
+ break;
+ }
+
+ length -= sizeof(*info_element) + info_element->len;
+ info_element =
+ (struct ieee80211_info_element *)&info_element->
+ data[info_element->len];
+ }
+
+ return 0;
+}
+
+static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response
+ *frame, struct ieee80211_rx_stats *stats)
+{
+ struct ieee80211_network network_resp = {
+ .ibss_dfs = NULL,
+ };
+ struct ieee80211_network *network = &network_resp;
+ struct net_device *dev = ieee->dev;
+
+ network->flags = 0;
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ network->qos_data.param_count = 0;
+ network->qos_data.old_param_count = 0;
+
+ //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF);
+ network->atim_window = le16_to_cpu(frame->aid);
+ network->listen_interval = le16_to_cpu(frame->status);
+ memcpy(network->bssid, frame->header.addr3, ETH_ALEN);
+ network->capability = le16_to_cpu(frame->capability);
+ network->last_scanned = jiffies;
+ network->rates_len = network->rates_ex_len = 0;
+ network->last_associate = 0;
+ network->ssid_len = 0;
+ network->erp_value =
+ (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0;
+
+ if (stats->freq == IEEE80211_52GHZ_BAND) {
+ /* for A band (No DS info) */
+ network->channel = stats->received_channel;
+ } else
+ network->flags |= NETWORK_HAS_CCK;
+
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+
+ if (ieee80211_parse_info_param
+ (frame->info_element, stats->len - sizeof(*frame), network))
+ return 1;
+
+ network->mode = 0;
+ if (stats->freq == IEEE80211_52GHZ_BAND)
+ network->mode = IEEE_A;
+ else {
+ if (network->flags & NETWORK_HAS_OFDM)
+ network->mode |= IEEE_G;
+ if (network->flags & NETWORK_HAS_CCK)
+ network->mode |= IEEE_B;
+ }
+
+ memcpy(&network->stats, stats, sizeof(network->stats));
+
+ if (ieee->handle_assoc_response != NULL)
+ ieee->handle_assoc_response(dev, frame, network);
+
+ return 0;
+}
+
+/***************************************************/
+
+static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response
+ *beacon,
+ struct ieee80211_network *network,
+ struct ieee80211_rx_stats *stats)
+{
+ DECLARE_SSID_BUF(ssid);
+
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ network->qos_data.param_count = 0;
+ network->qos_data.old_param_count = 0;
+
+ /* Pull out fixed field data */
+ memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
+ network->capability = le16_to_cpu(beacon->capability);
+ network->last_scanned = jiffies;
+ network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]);
+ network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]);
+ network->beacon_interval = le16_to_cpu(beacon->beacon_interval);
+ /* Where to pull this? beacon->listen_interval; */
+ network->listen_interval = 0x0A;
+ network->rates_len = network->rates_ex_len = 0;
+ network->last_associate = 0;
+ network->ssid_len = 0;
+ network->flags = 0;
+ network->atim_window = 0;
+ network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ?
+ 0x3 : 0x0;
+
+ if (stats->freq == IEEE80211_52GHZ_BAND) {
+ /* for A band (No DS info) */
+ network->channel = stats->received_channel;
+ } else
+ network->flags |= NETWORK_HAS_CCK;
+
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+
+ if (ieee80211_parse_info_param
+ (beacon->info_element, stats->len - sizeof(*beacon), network))
+ return 1;
+
+ network->mode = 0;
+ if (stats->freq == IEEE80211_52GHZ_BAND)
+ network->mode = IEEE_A;
+ else {
+ if (network->flags & NETWORK_HAS_OFDM)
+ network->mode |= IEEE_G;
+ if (network->flags & NETWORK_HAS_CCK)
+ network->mode |= IEEE_B;
+ }
+
+ if (network->mode == 0) {
+ IEEE80211_DEBUG_SCAN("Filtered out '%s (%pM)' "
+ "network.\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 1;
+ }
+
+ memcpy(&network->stats, stats, sizeof(network->stats));
+
+ return 0;
+}
+
+static inline int is_same_network(struct ieee80211_network *src,
+ struct ieee80211_network *dst)
+{
+ /* A network is only a duplicate if the channel, BSSID, and ESSID
+ * all match. We treat all <hidden> with the same BSSID and channel
+ * as one network */
+ return ((src->ssid_len == dst->ssid_len) &&
+ (src->channel == dst->channel) &&
+ !compare_ether_addr(src->bssid, dst->bssid) &&
+ !memcmp(src->ssid, dst->ssid, src->ssid_len));
+}
+
+static void update_network(struct ieee80211_network *dst,
+ struct ieee80211_network *src)
+{
+ int qos_active;
+ u8 old_param;
+
+ ieee80211_network_reset(dst);
+ dst->ibss_dfs = src->ibss_dfs;
+
+ /* We only update the statistics if they were created by receiving
+ * the network information on the actual channel the network is on.
+ *
+ * This keeps beacons received on neighbor channels from bringing
+ * down the signal level of an AP. */
+ if (dst->channel == src->stats.received_channel)
+ memcpy(&dst->stats, &src->stats,
+ sizeof(struct ieee80211_rx_stats));
+ else
+ IEEE80211_DEBUG_SCAN("Network %pM info received "
+ "off channel (%d vs. %d)\n", src->bssid,
+ dst->channel, src->stats.received_channel);
+
+ dst->capability = src->capability;
+ memcpy(dst->rates, src->rates, src->rates_len);
+ dst->rates_len = src->rates_len;
+ memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
+ dst->rates_ex_len = src->rates_ex_len;
+
+ dst->mode = src->mode;
+ dst->flags = src->flags;
+ dst->time_stamp[0] = src->time_stamp[0];
+ dst->time_stamp[1] = src->time_stamp[1];
+
+ dst->beacon_interval = src->beacon_interval;
+ dst->listen_interval = src->listen_interval;
+ dst->atim_window = src->atim_window;
+ dst->erp_value = src->erp_value;
+ dst->tim = src->tim;
+
+ memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
+ dst->wpa_ie_len = src->wpa_ie_len;
+ memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
+ dst->rsn_ie_len = src->rsn_ie_len;
+
+ dst->last_scanned = jiffies;
+ qos_active = src->qos_data.active;
+ old_param = dst->qos_data.old_param_count;
+ if (dst->flags & NETWORK_HAS_QOS_MASK)
+ memcpy(&dst->qos_data, &src->qos_data,
+ sizeof(struct ieee80211_qos_data));
+ else {
+ dst->qos_data.supported = src->qos_data.supported;
+ dst->qos_data.param_count = src->qos_data.param_count;
+ }
+
+ if (dst->qos_data.supported == 1) {
+ if (dst->ssid_len)
+ IEEE80211_DEBUG_QOS
+ ("QoS the network %s is QoS supported\n",
+ dst->ssid);
+ else
+ IEEE80211_DEBUG_QOS
+ ("QoS the network is QoS supported\n");
+ }
+ dst->qos_data.active = qos_active;
+ dst->qos_data.old_param_count = old_param;
+
+ /* dst->last_associate is not overwritten */
+}
+
+static inline int is_beacon(__le16 fc)
+{
+ return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON);
+}
+
+static void ieee80211_process_probe_response(struct ieee80211_device
+ *ieee, struct
+ ieee80211_probe_response
+ *beacon, struct ieee80211_rx_stats
+ *stats)
+{
+ struct net_device *dev = ieee->dev;
+ struct ieee80211_network network = {
+ .ibss_dfs = NULL,
+ };
+ struct ieee80211_network *target;
+ struct ieee80211_network *oldest = NULL;
+#ifdef CONFIG_IEEE80211_DEBUG
+ struct ieee80211_info_element *info_element = beacon->info_element;
+#endif
+ unsigned long flags;
+ DECLARE_SSID_BUF(ssid);
+
+ IEEE80211_DEBUG_SCAN("'%s' (%pM"
+ "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
+ print_ssid(ssid, info_element->data, info_element->len),
+ beacon->header.addr3,
+ (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0',
+ (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0');
+
+ if (ieee80211_network_init(ieee, beacon, &network, stats)) {
+ IEEE80211_DEBUG_SCAN("Dropped '%s' (%pM) via %s.\n",
+ print_ssid(ssid, info_element->data,
+ info_element->len),
+ beacon->header.addr3,
+ is_beacon(beacon->header.frame_ctl) ?
+ "BEACON" : "PROBE RESPONSE");
+ return;
+ }
+
+ /* The network parsed correctly -- so now we scan our known networks
+ * to see if we can find it in our list.
+ *
+ * NOTE: This search is definitely not optimized. Once its doing
+ * the "right thing" we'll optimize it for efficiency if
+ * necessary */
+
+ /* Search for this entry in the list and update it if it is
+ * already there. */
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(target, &ieee->network_list, list) {
+ if (is_same_network(target, &network))
+ break;
+
+ if ((oldest == NULL) ||
+ (target->last_scanned < oldest->last_scanned))
+ oldest = target;
+ }
+
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information */
+ if (&target->list == &ieee->network_list) {
+ if (list_empty(&ieee->network_free_list)) {
+ /* If there are no more slots, expire the oldest */
+ list_del(&oldest->list);
+ target = oldest;
+ IEEE80211_DEBUG_SCAN("Expired '%s' (%pM) from "
+ "network list.\n",
+ print_ssid(ssid, target->ssid,
+ target->ssid_len),
+ target->bssid);
+ ieee80211_network_reset(target);
+ } else {
+ /* Otherwise just pull from the free list */
+ target = list_entry(ieee->network_free_list.next,
+ struct ieee80211_network, list);
+ list_del(ieee->network_free_list.next);
+ }
+
+#ifdef CONFIG_IEEE80211_DEBUG
+ IEEE80211_DEBUG_SCAN("Adding '%s' (%pM) via %s.\n",
+ print_ssid(ssid, network.ssid,
+ network.ssid_len),
+ network.bssid,
+ is_beacon(beacon->header.frame_ctl) ?
+ "BEACON" : "PROBE RESPONSE");
+#endif
+ memcpy(target, &network, sizeof(*target));
+ network.ibss_dfs = NULL;
+ list_add_tail(&target->list, &ieee->network_list);
+ } else {
+ IEEE80211_DEBUG_SCAN("Updating '%s' (%pM) via %s.\n",
+ print_ssid(ssid, target->ssid,
+ target->ssid_len),
+ target->bssid,
+ is_beacon(beacon->header.frame_ctl) ?
+ "BEACON" : "PROBE RESPONSE");
+ update_network(target, &network);
+ network.ibss_dfs = NULL;
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ if (is_beacon(beacon->header.frame_ctl)) {
+ if (ieee->handle_beacon != NULL)
+ ieee->handle_beacon(dev, beacon, target);
+ } else {
+ if (ieee->handle_probe_response != NULL)
+ ieee->handle_probe_response(dev, beacon, target);
+ }
+}
+
+void ieee80211_rx_mgt(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *header,
+ struct ieee80211_rx_stats *stats)
+{
+ switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) {
+ case IEEE80211_STYPE_ASSOC_RESP:
+ IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ ieee80211_handle_assoc_resp(ieee,
+ (struct ieee80211_assoc_response *)
+ header, stats);
+ break;
+
+ case IEEE80211_STYPE_REASSOC_RESP:
+ IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ break;
+
+ case IEEE80211_STYPE_PROBE_REQ:
+ IEEE80211_DEBUG_MGMT("received auth (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+
+ if (ieee->handle_probe_request != NULL)
+ ieee->handle_probe_request(ieee->dev,
+ (struct
+ ieee80211_probe_request *)
+ header, stats);
+ break;
+
+ case IEEE80211_STYPE_PROBE_RESP:
+ IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ IEEE80211_DEBUG_SCAN("Probe response\n");
+ ieee80211_process_probe_response(ieee,
+ (struct
+ ieee80211_probe_response *)
+ header, stats);
+ break;
+
+ case IEEE80211_STYPE_BEACON:
+ IEEE80211_DEBUG_MGMT("received BEACON (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ IEEE80211_DEBUG_SCAN("Beacon\n");
+ ieee80211_process_probe_response(ieee,
+ (struct
+ ieee80211_probe_response *)
+ header, stats);
+ break;
+ case IEEE80211_STYPE_AUTH:
+
+ IEEE80211_DEBUG_MGMT("received auth (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+
+ if (ieee->handle_auth != NULL)
+ ieee->handle_auth(ieee->dev,
+ (struct ieee80211_auth *)header);
+ break;
+
+ case IEEE80211_STYPE_DISASSOC:
+ if (ieee->handle_disassoc != NULL)
+ ieee->handle_disassoc(ieee->dev,
+ (struct ieee80211_disassoc *)
+ header);
+ break;
+
+ case IEEE80211_STYPE_ACTION:
+ IEEE80211_DEBUG_MGMT("ACTION\n");
+ if (ieee->handle_action)
+ ieee->handle_action(ieee->dev,
+ (struct ieee80211_action *)
+ header, stats);
+ break;
+
+ case IEEE80211_STYPE_REASSOC_REQ:
+ IEEE80211_DEBUG_MGMT("received reassoc (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+
+ IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n",
+ ieee->dev->name);
+ if (ieee->handle_reassoc_request != NULL)
+ ieee->handle_reassoc_request(ieee->dev,
+ (struct ieee80211_reassoc_request *)
+ header);
+ break;
+
+ case IEEE80211_STYPE_ASSOC_REQ:
+ IEEE80211_DEBUG_MGMT("received assoc (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+
+ IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n",
+ ieee->dev->name);
+ if (ieee->handle_assoc_request != NULL)
+ ieee->handle_assoc_request(ieee->dev);
+ break;
+
+ case IEEE80211_STYPE_DEAUTH:
+ IEEE80211_DEBUG_MGMT("DEAUTH\n");
+ if (ieee->handle_deauth != NULL)
+ ieee->handle_deauth(ieee->dev,
+ (struct ieee80211_deauth *)
+ header);
+ break;
+ default:
+ IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n",
+ ieee->dev->name,
+ WLAN_FC_GET_STYPE(le16_to_cpu
+ (header->frame_ctl)));
+ break;
+ }
+}
+
+EXPORT_SYMBOL_GPL(ieee80211_rx_any);
+EXPORT_SYMBOL(ieee80211_rx_mgt);
+EXPORT_SYMBOL(ieee80211_rx);
diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c
new file mode 100644
index 000000000000..f78f57e8844a
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/libipw_tx.c
@@ -0,0 +1,546 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <asm/uaccess.h>
+
+#include <net/ieee80211.h>
+
+/*
+
+802.11 Data Frame
+
+ ,-------------------------------------------------------------------.
+Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ |------|------|---------|---------|---------|------|---------|------|
+Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
+ | | tion | (BSSID) | | | ence | data | |
+ `--------------------------------------------------| |------'
+Total: 28 non-data bytes `----.----'
+ |
+ .- 'Frame data' expands, if WEP enabled, to <----------'
+ |
+ V
+ ,-----------------------.
+Bytes | 4 | 0-2296 | 4 |
+ |-----|-----------|-----|
+Desc. | IV | Encrypted | ICV |
+ | | Packet | |
+ `-----| |-----'
+ `-----.-----'
+ |
+ .- 'Encrypted Packet' expands to
+ |
+ V
+ ,---------------------------------------------------.
+Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
+ |------|------|---------|----------|------|---------|
+Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
+ | DSAP | SSAP | | | | Packet |
+ | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
+ `----------------------------------------------------
+Total: 8 non-data bytes
+
+802.3 Ethernet Data Frame
+
+ ,-----------------------------------------.
+Bytes | 6 | 6 | 2 | Variable | 4 |
+ |-------|-------|------|-----------|------|
+Desc. | Dest. | Source| Type | IP Packet | fcs |
+ | MAC | MAC | | | |
+ `-----------------------------------------'
+Total: 18 non-data bytes
+
+In the event that fragmentation is required, the incoming payload is split into
+N parts of size ieee->fts. The first fragment contains the SNAP header and the
+remaining packets are just data.
+
+If encryption is enabled, each fragment payload size is reduced by enough space
+to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
+So if you have 1500 bytes of payload with ieee->fts set to 500 without
+encryption it will take 3 frames. With WEP it will take 4 frames as the
+payload of each frame is reduced to 492 bytes.
+
+* SKB visualization
+*
+* ,- skb->data
+* |
+* | ETHERNET HEADER ,-<-- PAYLOAD
+* | | 14 bytes from skb->data
+* | 2 bytes for Type --> ,T. | (sizeof ethhdr)
+* | | | |
+* |,-Dest.--. ,--Src.---. | | |
+* | 6 bytes| | 6 bytes | | | |
+* v | | | | | |
+* 0 | v 1 | v | v 2
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+* ^ | ^ | ^ |
+* | | | | | |
+* | | | | `T' <---- 2 bytes for Type
+* | | | |
+* | | '---SNAP--' <-------- 6 bytes for SNAP
+* | |
+* `-IV--' <-------------------- 4 bytes for IV (WEP)
+*
+* SNAP HEADER
+*
+*/
+
+static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
+static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
+
+static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
+{
+ struct ieee80211_snap_hdr *snap;
+ u8 *oui;
+
+ snap = (struct ieee80211_snap_hdr *)data;
+ snap->dsap = 0xaa;
+ snap->ssap = 0xaa;
+ snap->ctrl = 0x03;
+
+ if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
+ oui = P802_1H_OUI;
+ else
+ oui = RFC1042_OUI;
+ snap->oui[0] = oui[0];
+ snap->oui[1] = oui[1];
+ snap->oui[2] = oui[2];
+
+ memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
+
+ return SNAP_SIZE + sizeof(u16);
+}
+
+static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
+ struct sk_buff *frag, int hdr_len)
+{
+ struct lib80211_crypt_data *crypt =
+ ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ int res;
+
+ if (crypt == NULL)
+ return -1;
+
+ /* To encrypt, frame format is:
+ * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
+ atomic_inc(&crypt->refcnt);
+ res = 0;
+ if (crypt->ops && crypt->ops->encrypt_mpdu)
+ res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
+
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
+ ieee->dev->name, frag->len);
+ ieee->ieee_stats.tx_discards++;
+ return -1;
+ }
+
+ return 0;
+}
+
+void ieee80211_txb_free(struct ieee80211_txb *txb)
+{
+ int i;
+ if (unlikely(!txb))
+ return;
+ for (i = 0; i < txb->nr_frags; i++)
+ if (txb->fragments[i])
+ dev_kfree_skb_any(txb->fragments[i]);
+ kfree(txb);
+}
+
+static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
+ int headroom, gfp_t gfp_mask)
+{
+ struct ieee80211_txb *txb;
+ int i;
+ txb = kmalloc(sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags),
+ gfp_mask);
+ if (!txb)
+ return NULL;
+
+ memset(txb, 0, sizeof(struct ieee80211_txb));
+ txb->nr_frags = nr_frags;
+ txb->frag_size = txb_size;
+
+ for (i = 0; i < nr_frags; i++) {
+ txb->fragments[i] = __dev_alloc_skb(txb_size + headroom,
+ gfp_mask);
+ if (unlikely(!txb->fragments[i])) {
+ i--;
+ break;
+ }
+ skb_reserve(txb->fragments[i], headroom);
+ }
+ if (unlikely(i != nr_frags)) {
+ while (i >= 0)
+ dev_kfree_skb_any(txb->fragments[i--]);
+ kfree(txb);
+ return NULL;
+ }
+ return txb;
+}
+
+static int ieee80211_classify(struct sk_buff *skb)
+{
+ struct ethhdr *eth;
+ struct iphdr *ip;
+
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto != htons(ETH_P_IP))
+ return 0;
+
+ ip = ip_hdr(skb);
+ switch (ip->tos & 0xfc) {
+ case 0x20:
+ return 2;
+ case 0x40:
+ return 1;
+ case 0x60:
+ return 3;
+ case 0x80:
+ return 4;
+ case 0xa0:
+ return 5;
+ case 0xc0:
+ return 6;
+ case 0xe0:
+ return 7;
+ default:
+ return 0;
+ }
+}
+
+/* Incoming skb is converted to a txb which consists of
+ * a block of 802.11 fragment packets (stored as skbs) */
+int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ struct ieee80211_txb *txb = NULL;
+ struct ieee80211_hdr_3addrqos *frag_hdr;
+ int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
+ rts_required;
+ unsigned long flags;
+ struct net_device_stats *stats = &ieee->stats;
+ int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
+ __be16 ether_type;
+ int bytes, fc, hdr_len;
+ struct sk_buff *skb_frag;
+ struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
+ .duration_id = 0,
+ .seq_ctl = 0,
+ .qos_ctl = 0
+ };
+ u8 dest[ETH_ALEN], src[ETH_ALEN];
+ struct lib80211_crypt_data *crypt;
+ int priority = skb->priority;
+ int snapped = 0;
+
+ if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
+ return NETDEV_TX_BUSY;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* If there is no driver handler to take the TXB, dont' bother
+ * creating it... */
+ if (!ieee->hard_start_xmit) {
+ printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
+ goto success;
+ }
+
+ if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
+ printk(KERN_WARNING "%s: skb too small (%d).\n",
+ ieee->dev->name, skb->len);
+ goto success;
+ }
+
+ ether_type = ((struct ethhdr *)skb->data)->h_proto;
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+
+ encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
+ ieee->sec.encrypt;
+
+ host_encrypt = ieee->host_encrypt && encrypt && crypt;
+ host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt;
+ host_build_iv = ieee->host_build_iv && encrypt && crypt;
+
+ if (!encrypt && ieee->ieee802_1x &&
+ ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
+ stats->tx_dropped++;
+ goto success;
+ }
+
+ /* Save source and destination addresses */
+ skb_copy_from_linear_data(skb, dest, ETH_ALEN);
+ skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
+
+ if (host_encrypt || host_build_iv)
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
+ IEEE80211_FCTL_PROTECTED;
+ else
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
+
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ fc |= IEEE80211_FCTL_TODS;
+ /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
+ memcpy(header.addr1, ieee->bssid, ETH_ALEN);
+ memcpy(header.addr2, src, ETH_ALEN);
+ memcpy(header.addr3, dest, ETH_ALEN);
+ } else if (ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
+ memcpy(header.addr1, dest, ETH_ALEN);
+ memcpy(header.addr2, src, ETH_ALEN);
+ memcpy(header.addr3, ieee->bssid, ETH_ALEN);
+ }
+ hdr_len = IEEE80211_3ADDR_LEN;
+
+ if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
+ fc |= IEEE80211_STYPE_QOS_DATA;
+ hdr_len += 2;
+
+ skb->priority = ieee80211_classify(skb);
+ header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID);
+ }
+ header.frame_ctl = cpu_to_le16(fc);
+
+ /* Advance the SKB to the start of the payload */
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ /* Determine total amount of storage required for TXB packets */
+ bytes = skb->len + SNAP_SIZE + sizeof(u16);
+
+ /* Encrypt msdu first on the whole data packet. */
+ if ((host_encrypt || host_encrypt_msdu) &&
+ crypt && crypt->ops && crypt->ops->encrypt_msdu) {
+ int res = 0;
+ int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
+ crypt->ops->extra_msdu_postfix_len;
+ struct sk_buff *skb_new = dev_alloc_skb(len);
+
+ if (unlikely(!skb_new))
+ goto failed;
+
+ skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
+ memcpy(skb_put(skb_new, hdr_len), &header, hdr_len);
+ snapped = 1;
+ ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
+ ether_type);
+ skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
+ res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
+ if (res < 0) {
+ IEEE80211_ERROR("msdu encryption failed\n");
+ dev_kfree_skb_any(skb_new);
+ goto failed;
+ }
+ dev_kfree_skb_any(skb);
+ skb = skb_new;
+ bytes += crypt->ops->extra_msdu_prefix_len +
+ crypt->ops->extra_msdu_postfix_len;
+ skb_pull(skb, hdr_len);
+ }
+
+ if (host_encrypt || ieee->host_open_frag) {
+ /* Determine fragmentation size based on destination (multicast
+ * and broadcast are not fragmented) */
+ if (is_multicast_ether_addr(dest) ||
+ is_broadcast_ether_addr(dest))
+ frag_size = MAX_FRAG_THRESHOLD;
+ else
+ frag_size = ieee->fts;
+
+ /* Determine amount of payload per fragment. Regardless of if
+ * this stack is providing the full 802.11 header, one will
+ * eventually be affixed to this fragment -- so we must account
+ * for it when determining the amount of payload space. */
+ bytes_per_frag = frag_size - hdr_len;
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ bytes_per_frag -= IEEE80211_FCS_LEN;
+
+ /* Each fragment may need to have room for encryptiong
+ * pre/postfix */
+ if (host_encrypt)
+ bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
+ crypt->ops->extra_mpdu_postfix_len;
+
+ /* Number of fragments is the total
+ * bytes_per_frag / payload_per_fragment */
+ nr_frags = bytes / bytes_per_frag;
+ bytes_last_frag = bytes % bytes_per_frag;
+ if (bytes_last_frag)
+ nr_frags++;
+ else
+ bytes_last_frag = bytes_per_frag;
+ } else {
+ nr_frags = 1;
+ bytes_per_frag = bytes_last_frag = bytes;
+ frag_size = bytes + hdr_len;
+ }
+
+ rts_required = (frag_size > ieee->rts
+ && ieee->config & CFG_IEEE80211_RTS);
+ if (rts_required)
+ nr_frags++;
+
+ /* When we allocate the TXB we allocate enough space for the reserve
+ * and full fragment bytes (bytes_per_frag doesn't include prefix,
+ * postfix, header, FCS, etc.) */
+ txb = ieee80211_alloc_txb(nr_frags, frag_size,
+ ieee->tx_headroom, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ printk(KERN_WARNING "%s: Could not allocate TXB\n",
+ ieee->dev->name);
+ goto failed;
+ }
+ txb->encrypted = encrypt;
+ if (host_encrypt)
+ txb->payload_size = frag_size * (nr_frags - 1) +
+ bytes_last_frag;
+ else
+ txb->payload_size = bytes;
+
+ if (rts_required) {
+ skb_frag = txb->fragments[0];
+ frag_hdr =
+ (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
+
+ /*
+ * Set header frame_ctl to the RTS.
+ */
+ header.frame_ctl =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
+ memcpy(frag_hdr, &header, hdr_len);
+
+ /*
+ * Restore header frame_ctl to the original data setting.
+ */
+ header.frame_ctl = cpu_to_le16(fc);
+
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+
+ txb->rts_included = 1;
+ i = 1;
+ } else
+ i = 0;
+
+ for (; i < nr_frags; i++) {
+ skb_frag = txb->fragments[i];
+
+ if (host_encrypt || host_build_iv)
+ skb_reserve(skb_frag,
+ crypt->ops->extra_mpdu_prefix_len);
+
+ frag_hdr =
+ (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
+ memcpy(frag_hdr, &header, hdr_len);
+
+ /* If this is not the last fragment, then add the MOREFRAGS
+ * bit to the frame control */
+ if (i != nr_frags - 1) {
+ frag_hdr->frame_ctl =
+ cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS);
+ bytes = bytes_per_frag;
+ } else {
+ /* The last fragment takes the remaining length */
+ bytes = bytes_last_frag;
+ }
+
+ if (i == 0 && !snapped) {
+ ieee80211_copy_snap(skb_put
+ (skb_frag, SNAP_SIZE + sizeof(u16)),
+ ether_type);
+ bytes -= SNAP_SIZE + sizeof(u16);
+ }
+
+ skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
+
+ /* Advance the SKB... */
+ skb_pull(skb, bytes);
+
+ /* Encryption routine will move the header forward in order
+ * to insert the IV between the header and the payload */
+ if (host_encrypt)
+ ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
+ else if (host_build_iv) {
+ atomic_inc(&crypt->refcnt);
+ if (crypt->ops->build_iv)
+ crypt->ops->build_iv(skb_frag, hdr_len,
+ ieee->sec.keys[ieee->sec.active_key],
+ ieee->sec.key_sizes[ieee->sec.active_key],
+ crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ }
+
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+ }
+
+ success:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ dev_kfree_skb_any(skb);
+
+ if (txb) {
+ int ret = (*ieee->hard_start_xmit) (txb, dev, priority);
+ if (ret == 0) {
+ stats->tx_packets++;
+ stats->tx_bytes += txb->payload_size;
+ return 0;
+ }
+
+ ieee80211_txb_free(txb);
+ }
+
+ return 0;
+
+ failed:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ netif_stop_queue(dev);
+ stats->tx_errors++;
+ return 1;
+}
+
+EXPORT_SYMBOL(ieee80211_txb_free);
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c
new file mode 100644
index 000000000000..31ea3abfc327
--- /dev/null
+++ b/drivers/net/wireless/ipw2x00/libipw_wx.c
@@ -0,0 +1,760 @@
+/******************************************************************************
+
+ Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <j@w1.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+
+ 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., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+
+#include <net/lib80211.h>
+#include <net/ieee80211.h>
+#include <linux/wireless.h>
+
+static const char *ieee80211_modes[] = {
+ "?", "a", "b", "ab", "g", "ag", "bg", "abg"
+};
+
+#define MAX_CUSTOM_LEN 64
+static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
+ char *start, char *stop,
+ struct ieee80211_network *network,
+ struct iw_request_info *info)
+{
+ char custom[MAX_CUSTOM_LEN];
+ char *p;
+ struct iw_event iwe;
+ int i, j;
+ char *current_val; /* For rates */
+ u8 rate;
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+
+ /* Remaining entries will be displayed in the order we provide them */
+
+ /* Add the ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = min(network->ssid_len, (u8) 32);
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, network->ssid);
+
+ /* Add the protocol name */
+ iwe.cmd = SIOCGIWNAME;
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
+ ieee80211_modes[network->mode]);
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
+
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (network->capability & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+
+ start = iwe_stream_add_event(info, start, stop,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ /* Add channel and frequency */
+ /* Note : userspace automatically computes channel using iwrange */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
+ iwe.u.freq.e = 6;
+ iwe.u.freq.i = 0;
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (network->capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, network->ssid);
+
+ /* Add basic and extended rates */
+ /* Rate : stuffing multiple values in a single event require a bit
+ * more of magic - Jean II */
+ current_val = start + iwe_stream_lcp_len(info);
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 0, j = 0; i < network->rates_len;) {
+ if (j < network->rates_ex_len &&
+ ((network->rates_ex[j] & 0x7F) <
+ (network->rates[i] & 0x7F)))
+ rate = network->rates_ex[j++] & 0x7F;
+ else
+ rate = network->rates[i++] & 0x7F;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+ /* Add new value to event */
+ current_val = iwe_stream_add_value(info, start, current_val,
+ stop, &iwe, IW_EV_PARAM_LEN);
+ }
+ for (; j < network->rates_ex_len; j++) {
+ rate = network->rates_ex[j] & 0x7F;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
+ /* Add new value to event */
+ current_val = iwe_stream_add_value(info, start, current_val,
+ stop, &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any rate */
+ if ((current_val - start) > iwe_stream_lcp_len(info))
+ start = current_val;
+
+ /* Add quality statistics */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_UPDATED;
+
+ if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
+ iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
+ IW_QUAL_LEVEL_INVALID;
+ iwe.u.qual.qual = 0;
+ } else {
+ if (ieee->perfect_rssi == ieee->worst_rssi)
+ iwe.u.qual.qual = 100;
+ else
+ iwe.u.qual.qual =
+ (100 *
+ (ieee->perfect_rssi - ieee->worst_rssi) *
+ (ieee->perfect_rssi - ieee->worst_rssi) -
+ (ieee->perfect_rssi - network->stats.rssi) *
+ (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
+ 62 * (ieee->perfect_rssi -
+ network->stats.rssi))) /
+ ((ieee->perfect_rssi -
+ ieee->worst_rssi) * (ieee->perfect_rssi -
+ ieee->worst_rssi));
+ if (iwe.u.qual.qual > 100)
+ iwe.u.qual.qual = 100;
+ else if (iwe.u.qual.qual < 1)
+ iwe.u.qual.qual = 0;
+ }
+
+ if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
+ iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
+ iwe.u.qual.noise = 0;
+ } else {
+ iwe.u.qual.noise = network->stats.noise;
+ }
+
+ if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
+ iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
+ iwe.u.qual.level = 0;
+ } else {
+ iwe.u.qual.level = network->stats.signal;
+ }
+
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+
+ memset(&iwe, 0, sizeof(iwe));
+ if (network->wpa_ie_len) {
+ char buf[MAX_WPA_IE_LEN];
+ memcpy(buf, network->wpa_ie, network->wpa_ie_len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = network->wpa_ie_len;
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ if (network->rsn_ie_len) {
+ char buf[MAX_WPA_IE_LEN];
+ memcpy(buf, network->rsn_ie, network->rsn_ie_len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = network->rsn_ie_len;
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+ }
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network. */
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ " Last beacon: %dms ago",
+ jiffies_to_msecs(jiffies - network->last_scanned));
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+
+ /* Add spectrum management information */
+ iwe.cmd = -1;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
+
+ if (ieee80211_get_channel_flags(ieee, network->channel) &
+ IEEE80211_CH_INVALID) {
+ iwe.cmd = IWEVCUSTOM;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
+ }
+
+ if (ieee80211_get_channel_flags(ieee, network->channel) &
+ IEEE80211_CH_RADAR_DETECT) {
+ iwe.cmd = IWEVCUSTOM;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
+ }
+
+ if (iwe.cmd == IWEVCUSTOM) {
+ iwe.u.data.length = p - custom;
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+ }
+
+ return start;
+}
+
+#define SCAN_ITEM_SIZE 128
+
+int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ieee80211_network *network;
+ unsigned long flags;
+ int err = 0;
+
+ char *ev = extra;
+ char *stop = ev + wrqu->data.length;
+ int i = 0;
+ DECLARE_SSID_BUF(ssid);
+
+ IEEE80211_DEBUG_WX("Getting scan\n");
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(network, &ieee->network_list, list) {
+ i++;
+ if (stop - ev < SCAN_ITEM_SIZE) {
+ err = -E2BIG;
+ break;
+ }
+
+ if (ieee->scan_age == 0 ||
+ time_after(network->last_scanned + ieee->scan_age, jiffies))
+ ev = ieee80211_translate_scan(ieee, ev, stop, network,
+ info);
+ else
+ IEEE80211_DEBUG_SCAN("Not showing network '%s ("
+ "%pM)' due to age (%dms).\n",
+ print_ssid(ssid, network->ssid,
+ network->ssid_len),
+ network->bssid,
+ jiffies_to_msecs(jiffies -
+ network->
+ last_scanned));
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ wrqu->data.length = ev - extra;
+ wrqu->data.flags = 0;
+
+ IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
+
+ return err;
+}
+
+int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ struct net_device *dev = ieee->dev;
+ struct ieee80211_security sec = {
+ .flags = 0
+ };
+ int i, key, key_provided, len;
+ struct lib80211_crypt_data **crypt;
+ int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
+ DECLARE_SSID_BUF(ssid);
+
+ IEEE80211_DEBUG_WX("SET_ENCODE\n");
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ key_provided = 1;
+ } else {
+ key_provided = 0;
+ key = ieee->crypt_info.tx_keyidx;
+ }
+
+ IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
+ "provided" : "default");
+
+ crypt = &ieee->crypt_info.crypt[key];
+
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ if (key_provided && *crypt) {
+ IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
+ key);
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+ } else
+ IEEE80211_DEBUG_WX("Disabling encryption.\n");
+
+ /* Check all the keys to see if any are still configured,
+ * and if no key index was provided, de-init them all */
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (ieee->crypt_info.crypt[i] != NULL) {
+ if (key_provided)
+ break;
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info,
+ &ieee->crypt_info.crypt[i]);
+ }
+ }
+
+ if (i == WEP_KEYS) {
+ sec.enabled = 0;
+ sec.encrypt = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
+ }
+
+ goto done;
+ }
+
+ sec.enabled = 1;
+ sec.encrypt = 1;
+ sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
+
+ if (*crypt != NULL && (*crypt)->ops != NULL &&
+ strcmp((*crypt)->ops->name, "WEP") != 0) {
+ /* changing to use WEP; deinit previously used algorithm
+ * on this key */
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+ }
+
+ if (*crypt == NULL && host_crypto) {
+ struct lib80211_crypt_data *new_crypt;
+
+ /* take WEP into use */
+ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL)
+ return -ENOMEM;
+ new_crypt->ops = lib80211_get_crypto_ops("WEP");
+ if (!new_crypt->ops) {
+ request_module("lib80211_crypt_wep");
+ new_crypt->ops = lib80211_get_crypto_ops("WEP");
+ }
+
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(key);
+
+ if (!new_crypt->ops || !new_crypt->priv) {
+ kfree(new_crypt);
+ new_crypt = NULL;
+
+ printk(KERN_WARNING "%s: could not initialize WEP: "
+ "load module lib80211_crypt_wep\n", dev->name);
+ return -EOPNOTSUPP;
+ }
+ *crypt = new_crypt;
+ }
+
+ /* If a new key was provided, set it up */
+ if (erq->length > 0) {
+#ifdef CONFIG_IEEE80211_DEBUG
+ DECLARE_SSID_BUF(ssid);
+#endif
+
+ len = erq->length <= 5 ? 5 : 13;
+ memcpy(sec.keys[key], keybuf, erq->length);
+ if (len > erq->length)
+ memset(sec.keys[key] + erq->length, 0,
+ len - erq->length);
+ IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
+ key, print_ssid(ssid, sec.keys[key], len),
+ erq->length, len);
+ sec.key_sizes[key] = len;
+ if (*crypt)
+ (*crypt)->ops->set_key(sec.keys[key], len, NULL,
+ (*crypt)->priv);
+ sec.flags |= (1 << key);
+ /* This ensures a key will be activated if no key is
+ * explicitly set */
+ if (key == sec.active_key)
+ sec.flags |= SEC_ACTIVE_KEY;
+
+ } else {
+ if (host_crypto) {
+ len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
+ NULL, (*crypt)->priv);
+ if (len == 0) {
+ /* Set a default key of all 0 */
+ IEEE80211_DEBUG_WX("Setting key %d to all "
+ "zero.\n", key);
+ memset(sec.keys[key], 0, 13);
+ (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
+ (*crypt)->priv);
+ sec.key_sizes[key] = 13;
+ sec.flags |= (1 << key);
+ }
+ }
+ /* No key data - just set the default TX key index */
+ if (key_provided) {
+ IEEE80211_DEBUG_WX("Setting key %d to default Tx "
+ "key.\n", key);
+ ieee->crypt_info.tx_keyidx = key;
+ sec.active_key = key;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+ }
+ if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
+ ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
+ sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
+ WLAN_AUTH_SHARED_KEY;
+ sec.flags |= SEC_AUTH_MODE;
+ IEEE80211_DEBUG_WX("Auth: %s\n",
+ sec.auth_mode == WLAN_AUTH_OPEN ?
+ "OPEN" : "SHARED KEY");
+ }
+
+ /* For now we just support WEP, so only set that security level...
+ * TODO: When WPA is added this is one place that needs to change */
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
+ sec.encode_alg[key] = SEC_ALG_WEP;
+
+ done:
+ if (ieee->set_security)
+ ieee->set_security(dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack. */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ int len, key;
+ struct lib80211_crypt_data *crypt;
+ struct ieee80211_security *sec = &ieee->sec;
+
+ IEEE80211_DEBUG_WX("GET_ENCODE\n");
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ } else
+ key = ieee->crypt_info.tx_keyidx;
+
+ crypt = ieee->crypt_info.crypt[key];
+ erq->flags = key + 1;
+
+ if (!sec->enabled) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+
+ len = sec->key_sizes[key];
+ memcpy(keybuf, sec->keys[key], len);
+
+ erq->length = len;
+ erq->flags |= IW_ENCODE_ENABLED;
+
+ if (ieee->open_wep)
+ erq->flags |= IW_ENCODE_OPEN;
+ else
+ erq->flags |= IW_ENCODE_RESTRICTED;
+
+ return 0;
+}
+
+int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct net_device *dev = ieee->dev;
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int i, idx, ret = 0;
+ int group_key = 0;
+ const char *alg, *module;
+ struct lib80211_crypto_ops *ops;
+ struct lib80211_crypt_data **crypt;
+
+ struct ieee80211_security sec = {
+ .flags = 0,
+ };
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else
+ idx = ieee->crypt_info.tx_keyidx;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+ crypt = &ieee->crypt_info.crypt[idx];
+ group_key = 1;
+ } else {
+ /* some Cisco APs use idx>0 for unicast in dynamic WEP */
+ if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
+ return -EINVAL;
+ if (ieee->iw_mode == IW_MODE_INFRA)
+ crypt = &ieee->crypt_info.crypt[idx];
+ else
+ return -EINVAL;
+ }
+
+ sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
+ if ((encoding->flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE) {
+ if (*crypt)
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+
+ for (i = 0; i < WEP_KEYS; i++)
+ if (ieee->crypt_info.crypt[i] != NULL)
+ break;
+
+ if (i == WEP_KEYS) {
+ sec.enabled = 0;
+ sec.encrypt = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_LEVEL;
+ }
+ goto done;
+ }
+
+ sec.enabled = 1;
+ sec.encrypt = 1;
+
+ if (group_key ? !ieee->host_mc_decrypt :
+ !(ieee->host_encrypt || ieee->host_decrypt ||
+ ieee->host_encrypt_msdu))
+ goto skip_host_crypt;
+
+ switch (ext->alg) {
+ case IW_ENCODE_ALG_WEP:
+ alg = "WEP";
+ module = "lib80211_crypt_wep";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg = "TKIP";
+ module = "lib80211_crypt_tkip";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg = "CCMP";
+ module = "lib80211_crypt_ccmp";
+ break;
+ default:
+ IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
+ dev->name, ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ops = lib80211_get_crypto_ops(alg);
+ if (ops == NULL) {
+ request_module(module);
+ ops = lib80211_get_crypto_ops(alg);
+ }
+ if (ops == NULL) {
+ IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
+ dev->name, ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct lib80211_crypt_data *new_crypt;
+
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ new_crypt->ops = ops;
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(idx);
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ ret = -EINVAL;
+ goto done;
+ }
+ *crypt = new_crypt;
+ }
+
+ if (ext->key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
+ (*crypt)->priv) < 0) {
+ IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ skip_host_crypt:
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ ieee->crypt_info.tx_keyidx = idx;
+ sec.active_key = idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+
+ if (ext->alg != IW_ENCODE_ALG_NONE) {
+ memcpy(sec.keys[idx], ext->key, ext->key_len);
+ sec.key_sizes[idx] = ext->key_len;
+ sec.flags |= (1 << idx);
+ if (ext->alg == IW_ENCODE_ALG_WEP) {
+ sec.encode_alg[idx] = SEC_ALG_WEP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
+ sec.encode_alg[idx] = SEC_ALG_TKIP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
+ sec.encode_alg[idx] = SEC_ALG_CCMP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ /* Don't set sec level for group keys. */
+ if (group_key)
+ sec.flags &= ~SEC_LEVEL;
+ }
+ done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ /*
+ * Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack.
+ */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct ieee80211_security *sec = &ieee->sec;
+ int idx, max_key_len;
+
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else
+ idx = ieee->crypt_info.tx_keyidx;
+
+ if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
+ ext->alg != IW_ENCODE_ALG_WEP)
+ if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
+ return -EINVAL;
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ if (!sec->enabled) {
+ ext->alg = IW_ENCODE_ALG_NONE;
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (sec->encode_alg[idx] == SEC_ALG_WEP)
+ ext->alg = IW_ENCODE_ALG_WEP;
+ else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ else
+ return -EINVAL;
+
+ ext->key_len = sec->key_sizes[idx];
+ memcpy(ext->key, sec->keys[idx], ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ if (ext->key_len &&
+ (ext->alg == IW_ENCODE_ALG_TKIP ||
+ ext->alg == IW_ENCODE_ALG_CCMP))
+ ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
+
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
+EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
+
+EXPORT_SYMBOL(ieee80211_wx_get_scan);
+EXPORT_SYMBOL(ieee80211_wx_set_encode);
+EXPORT_SYMBOL(ieee80211_wx_get_encode);
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index b0ac0ce3fb9f..47bee0ee0a7c 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -4,6 +4,7 @@ config IWLWIFI
config IWLCORE
tristate "Intel Wireless Wifi Core"
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
+ select LIB80211
select IWLWIFI
select MAC80211_LEDS if IWLWIFI_LEDS
select LEDS_CLASS if IWLWIFI_LEDS
@@ -105,6 +106,7 @@ config IWL3945
tristate "Intel PRO/Wireless 3945ABG/BG Network Connection"
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
select FW_LOADER
+ select LIB80211
select IWLWIFI
select MAC80211_LEDS if IWL3945_LEDS
select LEDS_CLASS if IWL3945_LEDS
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 47aa28f6a513..0be9e6b66aa0 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -5,9 +5,10 @@ iwlcore-objs += iwl-scan.o
iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o
iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o
+iwlcore-$(CONFIG_IWLAGN_SPECTRUM_MEASUREMENT) += iwl-spectrum.o
obj-$(CONFIG_IWLAGN) += iwlagn.o
-iwlagn-objs := iwl-agn.o iwl-agn-rs.o
+iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-hcmd-check.o
iwlagn-$(CONFIG_IWL4965) += iwl-4965.o
iwlagn-$(CONFIG_IWL5000) += iwl-5000.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-commands.h b/drivers/net/wireless/iwlwifi/iwl-3945-commands.h
index 817ece773643..c6f4eb54a2b1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-commands.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -69,6 +69,12 @@
#ifndef __iwl_3945_commands_h__
#define __iwl_3945_commands_h__
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
+
enum {
REPLY_ALIVE = 0x1,
REPLY_ERROR = 0x2,
@@ -121,7 +127,7 @@ enum {
REPLY_TX_PWR_TABLE_CMD = 0x97,
MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
- /* Bluetooth device coexistance config command */
+ /* Bluetooth device coexistence config command */
REPLY_BT_CONFIG = 0x9b,
/* Statistics */
@@ -158,7 +164,7 @@ struct iwl3945_cmd_header {
u8 cmd; /* Command ID: REPLY_RXON, etc. */
u8 flags; /* IWL_CMD_* */
/*
- * The driver sets up the sequence number to values of its chosing.
+ * The driver sets up the sequence number to values of its choosing.
* uCode does not use this value, but passes it back to the driver
* when sending the response to each driver-originated command, so
* the driver can match the response to the command. Since the values
@@ -220,7 +226,7 @@ struct iwl3945_power_per_rate {
*
*****************************************************************************/
-#define UCODE_VALID_OK __constant_cpu_to_le32(0x1)
+#define UCODE_VALID_OK cpu_to_le32(0x1)
#define INITIALIZE_SUBTYPE (9)
/*
@@ -322,42 +328,42 @@ enum {
/* rx_config flags */
/* band & modulation selection */
-#define RXON_FLG_BAND_24G_MSK __constant_cpu_to_le32(1 << 0)
-#define RXON_FLG_CCK_MSK __constant_cpu_to_le32(1 << 1)
+#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0)
+#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1)
/* auto detection enable */
-#define RXON_FLG_AUTO_DETECT_MSK __constant_cpu_to_le32(1 << 2)
+#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2)
/* TGg protection when tx */
-#define RXON_FLG_TGG_PROTECT_MSK __constant_cpu_to_le32(1 << 3)
+#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3)
/* cck short slot & preamble */
-#define RXON_FLG_SHORT_SLOT_MSK __constant_cpu_to_le32(1 << 4)
-#define RXON_FLG_SHORT_PREAMBLE_MSK __constant_cpu_to_le32(1 << 5)
+#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4)
+#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5)
/* antenna selection */
-#define RXON_FLG_DIS_DIV_MSK __constant_cpu_to_le32(1 << 7)
-#define RXON_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0x0f00)
-#define RXON_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
-#define RXON_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
+#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7)
+#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00)
+#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
+#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
/* radar detection enable */
-#define RXON_FLG_RADAR_DETECT_MSK __constant_cpu_to_le32(1 << 12)
-#define RXON_FLG_TGJ_NARROW_BAND_MSK __constant_cpu_to_le32(1 << 13)
+#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12)
+#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13)
/* rx response to host with 8-byte TSF
* (according to ON_AIR deassertion) */
-#define RXON_FLG_TSF2HOST_MSK __constant_cpu_to_le32(1 << 15)
+#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15)
/* rx_config filter flags */
/* accept all data frames */
-#define RXON_FILTER_PROMISC_MSK __constant_cpu_to_le32(1 << 0)
+#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0)
/* pass control & management to host */
-#define RXON_FILTER_CTL2HOST_MSK __constant_cpu_to_le32(1 << 1)
+#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1)
/* accept multi-cast */
-#define RXON_FILTER_ACCEPT_GRP_MSK __constant_cpu_to_le32(1 << 2)
+#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2)
/* don't decrypt uni-cast frames */
-#define RXON_FILTER_DIS_DECRYPT_MSK __constant_cpu_to_le32(1 << 3)
+#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3)
/* don't decrypt multi-cast frames */
-#define RXON_FILTER_DIS_GRP_DECRYPT_MSK __constant_cpu_to_le32(1 << 4)
+#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4)
/* STA is associated */
-#define RXON_FILTER_ASSOC_MSK __constant_cpu_to_le32(1 << 5)
+#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5)
/* transfer to host non bssid beacons in associated state */
-#define RXON_FILTER_BCON_AWARE_MSK __constant_cpu_to_le32(1 << 6)
+#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6)
/**
* REPLY_RXON = 0x10 (command, has simple generic response)
@@ -471,9 +477,9 @@ struct iwl3945_ac_qos {
} __attribute__ ((packed));
/* QoS flags defines */
-#define QOS_PARAM_FLG_UPDATE_EDCA_MSK __constant_cpu_to_le32(0x01)
-#define QOS_PARAM_FLG_TGN_MSK __constant_cpu_to_le32(0x02)
-#define QOS_PARAM_FLG_TXOP_TYPE_MSK __constant_cpu_to_le32(0x10)
+#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01)
+#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02)
+#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10)
/* Number of Access Categories (AC) (EDCA), queues 0..3 */
#define AC_NUM 4
@@ -508,27 +514,27 @@ struct iwl3945_qosparam_cmd {
#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/
#define IWL_INVALID_STATION 255
-#define STA_FLG_TX_RATE_MSK __constant_cpu_to_le32(1 << 2);
-#define STA_FLG_PWR_SAVE_MSK __constant_cpu_to_le32(1 << 8);
+#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2);
+#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8);
/* Use in mode field. 1: modify existing entry, 0: add new station entry */
#define STA_CONTROL_MODIFY_MSK 0x01
/* key flags __le16*/
-#define STA_KEY_FLG_ENCRYPT_MSK __constant_cpu_to_le16(0x0007)
-#define STA_KEY_FLG_NO_ENC __constant_cpu_to_le16(0x0000)
-#define STA_KEY_FLG_WEP __constant_cpu_to_le16(0x0001)
-#define STA_KEY_FLG_CCMP __constant_cpu_to_le16(0x0002)
-#define STA_KEY_FLG_TKIP __constant_cpu_to_le16(0x0003)
+#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007)
+#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000)
+#define STA_KEY_FLG_WEP cpu_to_le16(0x0001)
+#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002)
+#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003)
#define STA_KEY_FLG_KEYID_POS 8
-#define STA_KEY_FLG_INVALID __constant_cpu_to_le16(0x0800)
+#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800)
/* wep key is either from global key (0) or from station info array (1) */
-#define STA_KEY_FLG_WEP_KEY_MAP_MSK __constant_cpu_to_le16(0x0008)
+#define STA_KEY_FLG_WEP_KEY_MAP_MSK cpu_to_le16(0x0008)
/* wep key in STA: 5-bytes (0) or 13-bytes (1) */
-#define STA_KEY_FLG_KEY_SIZE_MSK __constant_cpu_to_le16(0x1000)
-#define STA_KEY_MULTICAST_MSK __constant_cpu_to_le16(0x4000)
+#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000)
+#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000)
/* Flags indicate whether to modify vs. don't change various station params */
#define STA_MODIFY_KEY_MASK 0x01
@@ -666,14 +672,14 @@ struct iwl3945_rx_frame_hdr {
u8 payload[0];
} __attribute__ ((packed));
-#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
-#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
+#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0)
+#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1)
-#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
-#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
-#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
-#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
-#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
+#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0)
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1)
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2)
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3)
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK cpu_to_le16(0xf0)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
@@ -733,57 +739,57 @@ struct iwl3945_rx_frame {
/* 1: Use Request-To-Send protocol before this frame.
* Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. */
-#define TX_CMD_FLG_RTS_MSK __constant_cpu_to_le32(1 << 1)
+#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1)
/* 1: Transmit Clear-To-Send to self before this frame.
* Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames.
* Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. */
-#define TX_CMD_FLG_CTS_MSK __constant_cpu_to_le32(1 << 2)
+#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2)
/* 1: Expect ACK from receiving station
* 0: Don't expect ACK (MAC header's duration field s/b 0)
* Set this for unicast frames, but not broadcast/multicast. */
-#define TX_CMD_FLG_ACK_MSK __constant_cpu_to_le32(1 << 3)
+#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3)
/* 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD).
* Tx command's initial_rate_index indicates first rate to try;
* uCode walks through table for additional Tx attempts.
* 0: Use Tx rate/MCS from Tx command's rate_n_flags field.
* This rate will be used for all Tx attempts; it will not be scaled. */
-#define TX_CMD_FLG_STA_RATE_MSK __constant_cpu_to_le32(1 << 4)
+#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4)
/* 1: Expect immediate block-ack.
* Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */
-#define TX_CMD_FLG_IMM_BA_RSP_MASK __constant_cpu_to_le32(1 << 6)
+#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6)
/* 1: Frame requires full Tx-Op protection.
* Set this if either RTS or CTS Tx Flag gets set. */
-#define TX_CMD_FLG_FULL_TXOP_PROT_MSK __constant_cpu_to_le32(1 << 7)
+#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7)
/* Tx antenna selection field; used only for 3945, reserved (0) for 4965.
* Set field to "0" to allow 3945 uCode to select antenna (normal usage). */
-#define TX_CMD_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0xf00)
-#define TX_CMD_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
-#define TX_CMD_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
+#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00)
+#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
+#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
/* 1: Ignore Bluetooth priority for this frame.
* 0: Delay Tx until Bluetooth device is done (normal usage). */
-#define TX_CMD_FLG_BT_DIS_MSK __constant_cpu_to_le32(1 << 12)
+#define TX_CMD_FLG_BT_DIS_MSK cpu_to_le32(1 << 12)
/* 1: uCode overrides sequence control field in MAC header.
* 0: Driver provides sequence control field in MAC header.
* Set this for management frames, non-QOS data frames, non-unicast frames,
* and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */
-#define TX_CMD_FLG_SEQ_CTL_MSK __constant_cpu_to_le32(1 << 13)
+#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13)
/* 1: This frame is non-last MPDU; more fragments are coming.
* 0: Last fragment, or not using fragmentation. */
-#define TX_CMD_FLG_MORE_FRAG_MSK __constant_cpu_to_le32(1 << 14)
+#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14)
/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame.
* 0: No TSF required in outgoing frame.
* Set this for transmitting beacons and probe responses. */
-#define TX_CMD_FLG_TSF_MSK __constant_cpu_to_le32(1 << 16)
+#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16)
/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword
* alignment of frame's payload data field.
@@ -791,10 +797,10 @@ struct iwl3945_rx_frame {
* Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4
* field (but not both). Driver must align frame data (i.e. data following
* MAC header) to DWORD boundary. */
-#define TX_CMD_FLG_MH_PAD_MSK __constant_cpu_to_le32(1 << 20)
+#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20)
/* HCCA-AP - disable duration overwriting. */
-#define TX_CMD_FLG_DUR_MSK __constant_cpu_to_le32(1 << 25)
+#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25)
/*
* TX command security control
@@ -991,7 +997,7 @@ struct iwl3945_rate_scaling_cmd {
*
* 3945 and 4965 support hardware handshake with Bluetooth device on
* same platform. Bluetooth device alerts wireless device when it will Tx;
- * wireless device can delay or kill its own Tx to accomodate.
+ * wireless device can delay or kill its own Tx to accommodate.
*/
struct iwl3945_bt_cmd {
u8 flags;
@@ -1158,9 +1164,9 @@ struct iwl3945_spectrum_notification {
*/
#define IWL_POWER_VEC_SIZE 5
-#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le32(1 << 0)
-#define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le32(1 << 2)
-#define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le32(1 << 3)
+#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le32(1 << 0)
+#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le32(1 << 2)
+#define IWL_POWER_PCI_PM_MSK cpu_to_le32(1 << 3)
struct iwl3945_powertable_cmd {
__le32 flags;
__le32 rx_data_timeout;
@@ -1278,8 +1284,8 @@ struct iwl3945_ssid_ie {
} __attribute__ ((packed));
#define PROBE_OPTION_MAX 0x4
-#define TX_CMD_LIFE_TIME_INFINITE __constant_cpu_to_le32(0xFFFFFFFF)
-#define IWL_GOOD_CRC_TH __constant_cpu_to_le16(1)
+#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF)
+#define IWL_GOOD_CRC_TH cpu_to_le16(1)
#define IWL_MAX_SCAN_SIZE 1024
/*
@@ -1379,7 +1385,7 @@ struct iwl3945_scan_cmd {
} __attribute__ ((packed));
/* Can abort will notify by complete notification with abort status. */
-#define CAN_ABORT_STATUS __constant_cpu_to_le32(0x1)
+#define CAN_ABORT_STATUS cpu_to_le32(0x1)
/* complete notification statuses */
#define ABORT_STATUS 0x2
@@ -1572,8 +1578,8 @@ struct statistics_general {
* STATISTICS_NOTIFICATIONs after received beacons (see below). This flag
* does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself.
*/
-#define IWL_STATS_CONF_CLEAR_STATS __constant_cpu_to_le32(0x1) /* see above */
-#define IWL_STATS_CONF_DISABLE_NOTIF __constant_cpu_to_le32(0x2)/* see above */
+#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */
+#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */
struct iwl3945_statistics_cmd {
__le32 configuration_flags; /* IWL_STATS_CONF_* */
} __attribute__ ((packed));
@@ -1593,8 +1599,8 @@ struct iwl3945_statistics_cmd {
* appropriately so that each notification contains statistics for only the
* one channel that has just been scanned.
*/
-#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2)
-#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8)
+#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2)
+#define STATISTICS_REPLY_FLG_FAT_MODE_MSK cpu_to_le32(0x8)
struct iwl3945_notif_statistics {
__le32 flag;
struct statistics_rx rx;
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-core.h b/drivers/net/wireless/iwlwifi/iwl-3945-core.h
index bc12f97ba0b1..6f463555402c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-core.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -71,9 +71,33 @@
#define IWL_SKU_G 0x1
#define IWL_SKU_A 0x2
+/**
+ * struct iwl_3945_cfg
+ * @fw_name_pre: Firmware filename prefix. The api version and extension
+ * (.ucode) will be added to filename before loading from disk. The
+ * filename is constructed as fw_name_pre<api>.ucode.
+ * @ucode_api_max: Highest version of uCode API supported by driver.
+ * @ucode_api_min: Lowest version of uCode API supported by driver.
+ *
+ * We enable the driver to be backward compatible wrt API version. The
+ * driver specifies which APIs it supports (with @ucode_api_max being the
+ * highest and @ucode_api_min the lowest). Firmware will only be loaded if
+ * it has a supported API version. The firmware's API version will be
+ * stored in @iwl_priv, enabling the driver to make runtime changes based
+ * on firmware version used.
+ *
+ * For example,
+ * if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
+ * Driver interacts with Firmware API version >= 2.
+ * } else {
+ * Driver interacts with Firmware API version 1.
+ * }
+ */
struct iwl_3945_cfg {
const char *name;
- const char *fw_name;
+ const char *fw_name_pre;
+ const unsigned int ucode_api_max;
+ const unsigned int ucode_api_min;
unsigned int sku;
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debug.h b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h
index 33016fb5e9b3..85eb778f9df1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-debug.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
index 644bd9e08052..94ea0e60c410 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -103,7 +103,6 @@
* Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
*/
#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
-#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */
/*
* Regulatory channel usage flags in EEPROM struct iwl_eeprom_channel.flags.
@@ -321,6 +320,7 @@ struct iwl3945_eeprom {
/* RSSR */
#define FH_RSSR_CTRL (FH_RSSR_TABLE+0x000)
#define FH_RSSR_STATUS (FH_RSSR_TABLE+0x004)
+#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000)
/* TCSR */
#define FH_TCSR(_channel) (FH_TCSR_TABLE+(_channel)*0x20)
#define FH_TCSR_CONFIG(_channel) (FH_TCSR(_channel)+0x00)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-io.h b/drivers/net/wireless/iwlwifi/iwl-3945-io.h
index b3fe48de3ae7..2440fd664dd5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-io.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -53,7 +53,7 @@
* _iwl3945_read32.)
*
* These declarations are *extremely* useful in quickly isolating code deltas
- * which result in misconfiguring of the hardware I/O. In combination with
+ * which result in misconfiguration of the hardware I/O. In combination with
* git-bisect and the IO debug level you can quickly determine the specific
* commit which breaks the IO sequence to the hardware.
*
@@ -93,7 +93,7 @@ static inline int _iwl3945_poll_bit(struct iwl3945_priv *priv, u32 addr,
do {
if ((_iwl3945_read32(priv, addr) & mask) == (bits & mask))
return i;
- mdelay(10);
+ udelay(10);
i += 10;
} while (i < timeout);
@@ -107,7 +107,7 @@ static inline int __iwl3945_poll_bit(const char *f, u32 l,
int ret = _iwl3945_poll_bit(priv, addr, bits, mask, timeout);
IWL_DEBUG_IO("poll_bit(0x%08X, 0x%08X, 0x%08X) - %s- %s %d\n",
addr, bits, mask,
- unlikely(ret == -ETIMEDOUT)?"timeout":"", f, l);
+ unlikely(ret == -ETIMEDOUT) ? "timeout" : "", f, l);
return ret;
}
#define iwl3945_poll_bit(priv, addr, bits, mask, timeout) \
@@ -271,16 +271,7 @@ static inline void iwl3945_write_reg_buf(struct iwl3945_priv *priv,
static inline int _iwl3945_poll_direct_bit(struct iwl3945_priv *priv,
u32 addr, u32 mask, int timeout)
{
- int i = 0;
-
- do {
- if ((_iwl3945_read_direct32(priv, addr) & mask) == mask)
- return i;
- mdelay(10);
- i += 10;
- } while (i < timeout);
-
- return -ETIMEDOUT;
+ return _iwl3945_poll_bit(priv, addr, mask, mask, timeout);
}
#ifdef CONFIG_IWL3945_DEBUG
@@ -307,6 +298,7 @@ static inline int __iwl3945_poll_direct_bit(const char *f, u32 l,
static inline u32 _iwl3945_read_prph(struct iwl3945_priv *priv, u32 reg)
{
_iwl3945_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
+ rmb();
return _iwl3945_read_direct32(priv, HBUS_TARG_PRPH_RDAT);
}
#ifdef CONFIG_IWL3945_DEBUG
@@ -328,6 +320,7 @@ static inline void _iwl3945_write_prph(struct iwl3945_priv *priv,
{
_iwl3945_write_direct32(priv, HBUS_TARG_PRPH_WADDR,
((addr & 0x0000FFFF) | (3 << 24)));
+ wmb();
_iwl3945_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val);
}
#ifdef CONFIG_IWL3945_DEBUG
@@ -389,12 +382,14 @@ static inline void iwl3945_clear_bits_prph(struct iwl3945_priv
static inline u32 iwl3945_read_targ_mem(struct iwl3945_priv *priv, u32 addr)
{
iwl3945_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
+ rmb();
return iwl3945_read_direct32(priv, HBUS_TARG_MEM_RDAT);
}
static inline void iwl3945_write_targ_mem(struct iwl3945_priv *priv, u32 addr, u32 val)
{
iwl3945_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+ wmb();
iwl3945_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
}
@@ -402,6 +397,7 @@ static inline void iwl3945_write_targ_mem_buf(struct iwl3945_priv *priv, u32 add
u32 len, u32 *values)
{
iwl3945_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+ wmb();
for (; 0 < len; len -= sizeof(u32), values++)
iwl3945_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
index 705c65bed9fd..4c638909a7db 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlwifi/iwl-3945-led.h
index 2fbd126c1347..749ac035fd6a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
index 6fc5e7361f26..9b60a0c5de5f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -63,6 +63,9 @@ struct iwl3945_rs_sta {
u8 ibss_sta_added;
struct timer_list rate_scale_flush;
struct iwl3945_rate_scale_data win[IWL_RATE_COUNT];
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *rs_sta_dbgfs_stats_table_file;
+#endif
/* used to be in sta_info */
int last_txrate_idx;
@@ -114,9 +117,11 @@ static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = {
};
#define IWL_RATE_MAX_WINDOW 62
-#define IWL_RATE_FLUSH (3*HZ/10)
+#define IWL_RATE_FLUSH (3*HZ)
#define IWL_RATE_WIN_FLUSH (HZ/2)
#define IWL_RATE_HIGH_TH 11520
+#define IWL_SUCCESS_UP_TH 8960
+#define IWL_SUCCESS_DOWN_TH 10880
#define IWL_RATE_MIN_FAILURE_TH 8
#define IWL_RATE_MIN_SUCCESS_TH 8
#define IWL_RATE_DECREASE_TH 1920
@@ -203,6 +208,7 @@ static int iwl3945_rate_scale_flush_windows(struct iwl3945_rs_sta *rs_sta)
#define IWL_RATE_FLUSH_MAX 5000 /* msec */
#define IWL_RATE_FLUSH_MIN 50 /* msec */
+#define IWL_AVERAGE_PACKETS 1500
static void iwl3945_bg_rate_scale_flush(unsigned long data)
{
@@ -217,8 +223,6 @@ static void iwl3945_bg_rate_scale_flush(unsigned long data)
spin_lock_irqsave(&rs_sta->lock, flags);
- rs_sta->flush_pending = 0;
-
/* Number of packets Rx'd since last time this timer ran */
packet_count = (rs_sta->tx_packets - rs_sta->last_tx_packets) + 1;
@@ -227,7 +231,6 @@ static void iwl3945_bg_rate_scale_flush(unsigned long data)
if (unflushed) {
duration =
jiffies_to_msecs(jiffies - rs_sta->last_partial_flush);
-/* duration = jiffies_to_msecs(rs_sta->flush_time); */
IWL_DEBUG_RATE("Tx'd %d packets in %dms\n",
packet_count, duration);
@@ -239,9 +242,11 @@ static void iwl3945_bg_rate_scale_flush(unsigned long data)
pps = 0;
if (pps) {
- duration = IWL_RATE_FLUSH_MAX / pps;
+ duration = (IWL_AVERAGE_PACKETS * 1000) / pps;
if (duration < IWL_RATE_FLUSH_MIN)
duration = IWL_RATE_FLUSH_MIN;
+ else if (duration > IWL_RATE_FLUSH_MAX)
+ duration = IWL_RATE_FLUSH_MAX;
} else
duration = IWL_RATE_FLUSH_MAX;
@@ -254,8 +259,10 @@ static void iwl3945_bg_rate_scale_flush(unsigned long data)
rs_sta->flush_time);
rs_sta->last_partial_flush = jiffies;
+ } else {
+ rs_sta->flush_time = IWL_RATE_FLUSH;
+ rs_sta->flush_pending = 0;
}
-
/* If there weren't any unflushed entries, we don't schedule the timer
* to run again */
@@ -275,17 +282,18 @@ static void iwl3945_bg_rate_scale_flush(unsigned long data)
*/
static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta,
struct iwl3945_rate_scale_data *window,
- int success, int retries)
+ int success, int retries, int index)
{
unsigned long flags;
+ s32 fail_count;
if (!retries) {
IWL_DEBUG_RATE("leave: retries == 0 -- should be at least 1\n");
return;
}
+ spin_lock_irqsave(&rs_sta->lock, flags);
while (retries--) {
- spin_lock_irqsave(&rs_sta->lock, flags);
/* If we have filled up the window then subtract one from the
* success counter if the high-bit is counting toward
@@ -313,14 +321,25 @@ static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta,
/* Tag this window as having been updated */
window->stamp = jiffies;
- spin_unlock_irqrestore(&rs_sta->lock, flags);
}
+
+ fail_count = window->counter - window->success_counter;
+ if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
+ (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+ window->average_tpt = ((window->success_ratio *
+ rs_sta->expected_tpt[index] + 64) / 128);
+ else
+ window->average_tpt = IWL_INV_TPT;
+
+ spin_unlock_irqrestore(&rs_sta->lock, flags);
+
}
-static void rs_rate_init(void *priv, struct ieee80211_supported_band *sband,
+static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta)
{
struct iwl3945_rs_sta *rs_sta = priv_sta;
+ struct iwl3945_priv *priv = (struct iwl3945_priv *)priv_r;
int i;
IWL_DEBUG_RATE("enter\n");
@@ -330,16 +349,21 @@ static void rs_rate_init(void *priv, struct ieee80211_supported_band *sband,
* previous packets? Need to have IEEE 802.1X auth succeed immediately
* after assoc.. */
- for (i = IWL_RATE_COUNT - 1; i >= 0; i--) {
+ for (i = sband->n_bitrates - 1; i >= 0; i--) {
if (sta->supp_rates[sband->band] & (1 << i)) {
rs_sta->last_txrate_idx = i;
break;
}
}
+ priv->sta_supp_rates = sta->supp_rates[sband->band];
/* For 5 GHz band it start at IWL_FIRST_OFDM_RATE */
- if (sband->band == IEEE80211_BAND_5GHZ)
+ if (sband->band == IEEE80211_BAND_5GHZ) {
rs_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+ priv->sta_supp_rates = priv->sta_supp_rates <<
+ IWL_FIRST_OFDM_RATE;
+ }
+
IWL_DEBUG_RATE("leave\n");
}
@@ -355,12 +379,6 @@ static void rs_free(void *priv)
return;
}
-static void rs_clear(void *priv)
-{
- return;
-}
-
-
static void *rs_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
{
struct iwl3945_rs_sta *rs_sta;
@@ -422,34 +440,6 @@ static void rs_free_sta(void *priv, struct ieee80211_sta *sta,
}
-/*
- * get ieee prev rate from rate scale table.
- * for A and B mode we need to overright prev
- * value
- */
-static int rs_adjust_next_rate(struct iwl3945_priv *priv, int rate)
-{
- int next_rate = iwl3945_get_prev_ieee_rate(rate);
-
- switch (priv->band) {
- case IEEE80211_BAND_5GHZ:
- if (rate == IWL_RATE_12M_INDEX)
- next_rate = IWL_RATE_9M_INDEX;
- else if (rate == IWL_RATE_6M_INDEX)
- next_rate = IWL_RATE_6M_INDEX;
- break;
-/* XXX cannot be invoked in current mac80211 so not a regression
- case MODE_IEEE80211B:
- if (rate == IWL_RATE_11M_INDEX_TABLE)
- next_rate = IWL_RATE_5M_INDEX_TABLE;
- break;
- */
- default:
- break;
- }
-
- return next_rate;
-}
/**
* rs_tx_status - Update rate control values based on Tx results
*
@@ -460,7 +450,7 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
- u8 retries, current_count;
+ s8 retries = 0, current_count;
int scale_rate_index, first_index, last_index;
unsigned long flags;
struct iwl3945_priv *priv = (struct iwl3945_priv *)priv_rate;
@@ -469,8 +459,9 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
IWL_DEBUG_RATE("enter\n");
- retries = info->status.retry_count;
- first_index = sband->bitrates[info->tx_rate_idx].hw_value;
+ retries = info->status.rates[0].count;
+
+ first_index = sband->bitrates[info->status.rates[0].idx].hw_value;
if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
IWL_DEBUG_RATE("leave: Rate out of bounds: %d\n", first_index);
return;
@@ -496,13 +487,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
* at which the frame was finally transmitted (or failed if no
* ACK)
*/
- while (retries > 0) {
- if (retries < priv->retry_rate) {
- current_count = retries;
+ while (retries > 1) {
+ if ((retries - 1) < priv->retry_rate) {
+ current_count = (retries - 1);
last_index = scale_rate_index;
} else {
current_count = priv->retry_rate;
- last_index = rs_adjust_next_rate(priv,
+ last_index = iwl3945_rs_next_rate(priv,
scale_rate_index);
}
@@ -510,15 +501,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
* as was used for it (per current_count) */
iwl3945_collect_tx_data(rs_sta,
&rs_sta->win[scale_rate_index],
- 0, current_count);
+ 0, current_count, scale_rate_index);
IWL_DEBUG_RATE("Update rate %d for %d retries.\n",
scale_rate_index, current_count);
retries -= current_count;
- if (retries)
- scale_rate_index =
- rs_adjust_next_rate(priv, scale_rate_index);
+ scale_rate_index = last_index;
}
@@ -529,7 +518,7 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
"success" : "failure");
iwl3945_collect_tx_data(rs_sta,
&rs_sta->win[last_index],
- info->flags & IEEE80211_TX_STAT_ACK, 1);
+ info->flags & IEEE80211_TX_STAT_ACK, 1, last_index);
/* We updated the rate scale window -- if its been more than
* flush_time since the last run, schedule the flush
@@ -537,9 +526,10 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
spin_lock_irqsave(&rs_sta->lock, flags);
if (!rs_sta->flush_pending &&
- time_after(jiffies, rs_sta->last_partial_flush +
+ time_after(jiffies, rs_sta->last_flush +
rs_sta->flush_time)) {
+ rs_sta->last_partial_flush = jiffies;
rs_sta->flush_pending = 1;
mod_timer(&rs_sta->rate_scale_flush,
jiffies + rs_sta->flush_time);
@@ -630,10 +620,11 @@ static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta,
* rate table and must reference the driver allocated rate table
*
*/
-static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
+ void *priv_sta, struct ieee80211_tx_rate_control *txrc)
{
+ struct ieee80211_supported_band *sband = txrc->sband;
+ struct sk_buff *skb = txrc->skb;
u8 low = IWL_RATE_INVALID;
u8 high = IWL_RATE_INVALID;
u16 high_low;
@@ -649,7 +640,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
u16 fc, rate_mask;
struct iwl3945_priv *priv = (struct iwl3945_priv *)priv_r;
- DECLARE_MAC_BUF(mac);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
IWL_DEBUG_RATE("enter\n");
@@ -660,7 +651,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
is_multicast_ether_addr(hdr->addr1) ||
!sta || !priv_sta) {
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
- sel->rate_idx = rate_lowest_index(sband, sta);
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
return;
}
@@ -675,8 +666,8 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
u8 sta_id = iwl3945_hw_find_station(priv, hdr->addr1);
if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_RATE("LQ: ADD station %s\n",
- print_mac(mac, hdr->addr1));
+ IWL_DEBUG_RATE("LQ: ADD station %pm\n",
+ hdr->addr1);
sta_id = iwl3945_add_station(priv,
hdr->addr1, 0, CMD_ASYNC);
}
@@ -686,8 +677,13 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
spin_lock_irqsave(&rs_sta->lock, flags);
+ /* for recent assoc, choose best rate regarding
+ * to rssi value
+ */
if (rs_sta->start_rate != IWL_RATE_INVALID) {
- index = rs_sta->start_rate;
+ if (rs_sta->start_rate < index &&
+ (rate_mask & (1 << rs_sta->start_rate)))
+ index = rs_sta->start_rate;
rs_sta->start_rate = IWL_RATE_INVALID;
}
@@ -697,7 +693,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
if (((fail_count <= IWL_RATE_MIN_FAILURE_TH) &&
(window->success_counter < IWL_RATE_MIN_SUCCESS_TH))) {
- window->average_tpt = IWL_INV_TPT;
spin_unlock_irqrestore(&rs_sta->lock, flags);
IWL_DEBUG_RATE("Invalid average_tpt on rate %d: "
@@ -711,8 +706,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
}
- window->average_tpt = ((window->success_ratio *
- rs_sta->expected_tpt[index] + 64) / 128);
current_tpt = window->average_tpt;
high_low = iwl3945_get_adjacent_rate(rs_sta, index, rate_mask,
@@ -760,13 +753,15 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
}
}
- if ((window->success_ratio > IWL_RATE_HIGH_TH) ||
- (current_tpt > window->average_tpt)) {
- IWL_DEBUG_RATE("No action -- success_ratio [%d] > HIGH_TH or "
- "current_tpt [%d] > average_tpt [%d]\n",
- window->success_ratio,
- current_tpt, window->average_tpt);
- scale_action = 0;
+ if (scale_action == -1) {
+ if (window->success_ratio > IWL_SUCCESS_DOWN_TH)
+ scale_action = 0;
+ } else if (scale_action == 1) {
+ if (window->success_ratio < IWL_SUCCESS_UP_TH) {
+ IWL_DEBUG_RATE("No action -- success_ratio [%d] < "
+ "SUCCESS UP\n", window->success_ratio);
+ scale_action = 0;
+ }
}
switch (scale_action) {
@@ -793,24 +788,83 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
rs_sta->last_txrate_idx = index;
if (sband->band == IEEE80211_BAND_5GHZ)
- sel->rate_idx = rs_sta->last_txrate_idx - IWL_FIRST_OFDM_RATE;
+ info->control.rates[0].idx = rs_sta->last_txrate_idx -
+ IWL_FIRST_OFDM_RATE;
else
- sel->rate_idx = rs_sta->last_txrate_idx;
+ info->control.rates[0].idx = rs_sta->last_txrate_idx;
IWL_DEBUG_RATE("leave: %d\n", index);
}
+#ifdef CONFIG_MAC80211_DEBUGFS
+static int iwl3945_open_file_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t iwl3945_sta_dbgfs_stats_table_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buff[1024];
+ int desc = 0;
+ int j;
+ struct iwl3945_rs_sta *lq_sta = file->private_data;
+
+ desc += sprintf(buff + desc, "tx packets=%d last rate index=%d\n"
+ "rate=0x%X flush time %d\n",
+ lq_sta->tx_packets,
+ lq_sta->last_txrate_idx,
+ lq_sta->start_rate, jiffies_to_msecs(lq_sta->flush_time));
+ for (j = 0; j < IWL_RATE_COUNT; j++) {
+ desc += sprintf(buff+desc,
+ "counter=%d success=%d %%=%d\n",
+ lq_sta->win[j].counter,
+ lq_sta->win[j].success_counter,
+ lq_sta->win[j].success_ratio);
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+}
+
+static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
+ .read = iwl3945_sta_dbgfs_stats_table_read,
+ .open = iwl3945_open_file_generic,
+};
+
+static void iwl3945_add_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir)
+{
+ struct iwl3945_rs_sta *lq_sta = priv_sta;
+
+ lq_sta->rs_sta_dbgfs_stats_table_file =
+ debugfs_create_file("rate_stats_table", 0600, dir,
+ lq_sta, &rs_sta_dbgfs_stats_table_ops);
+
+}
+
+static void iwl3945_remove_debugfs(void *priv, void *priv_sta)
+{
+ struct iwl3945_rs_sta *lq_sta = priv_sta;
+ debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+}
+#endif
+
static struct rate_control_ops rs_ops = {
.module = NULL,
.name = RS_NAME,
.tx_status = rs_tx_status,
.get_rate = rs_get_rate,
.rate_init = rs_rate_init,
- .clear = rs_clear,
.alloc = rs_alloc,
.free = rs_free,
.alloc_sta = rs_alloc_sta,
.free_sta = rs_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = iwl3945_add_debugfs,
+ .remove_sta_debugfs = iwl3945_remove_debugfs,
+#endif
+
};
void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
@@ -827,13 +881,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
rcu_read_lock();
sta = ieee80211_find_sta(hw, priv->stations[sta_id].sta.sta.addr);
- psta = (void *) sta->drv_priv;
- if (!sta || !psta) {
- IWL_DEBUG_RATE("leave - no private rate data!\n");
+ if (!sta) {
rcu_read_unlock();
return;
}
+ psta = (void *) sta->drv_priv;
rs_sta = psta->rs_sta;
spin_lock_irqsave(&rs_sta->lock, flags);
@@ -857,7 +910,6 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
break;
}
- rcu_read_unlock();
spin_unlock_irqrestore(&rs_sta->lock, flags);
rssi = priv->last_rx_rssi;
@@ -871,6 +923,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
IWL_DEBUG_RATE("leave: rssi %d assign rate index: "
"%d (plcp 0x%x)\n", rssi, rs_sta->start_rate,
iwl3945_rates[rs_sta->start_rate].plcp);
+ rcu_read_unlock();
}
int iwl3945_rate_control_register(void)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.h b/drivers/net/wireless/iwlwifi/iwl-3945-rs.h
index 98b17ae6ef24..b5a66135dedd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 7ca5627cc078..8fdb34222c0a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -200,7 +200,7 @@ static int iwl3945_hwrate_to_plcp_idx(u8 plcp)
* priv->eeprom is used to determine if antenna AUX/MAIN are reversed
* priv->antenna specifies the antenna diversity mode:
*
- * IWL_ANTENNA_DIVERISTY - NIC selects best antenna by itself
+ * IWL_ANTENNA_DIVERSITY - NIC selects best antenna by itself
* IWL_ANTENNA_MAIN - Force MAIN antenna
* IWL_ANTENNA_AUX - Force AUX antenna
*/
@@ -261,6 +261,37 @@ static inline const char *iwl3945_get_tx_fail_reason(u32 status)
}
#endif
+/*
+ * get ieee prev rate from rate scale table.
+ * for A and B mode we need to overright prev
+ * value
+ */
+int iwl3945_rs_next_rate(struct iwl3945_priv *priv, int rate)
+{
+ int next_rate = iwl3945_get_prev_ieee_rate(rate);
+
+ switch (priv->band) {
+ case IEEE80211_BAND_5GHZ:
+ if (rate == IWL_RATE_12M_INDEX)
+ next_rate = IWL_RATE_9M_INDEX;
+ else if (rate == IWL_RATE_6M_INDEX)
+ next_rate = IWL_RATE_6M_INDEX;
+ break;
+ case IEEE80211_BAND_2GHZ:
+ if (!(priv->sta_supp_rates & IWL_OFDM_RATES_MASK) &&
+ iwl3945_is_associated(priv)) {
+ if (rate == IWL_RATE_11M_INDEX)
+ next_rate = IWL_RATE_5M_INDEX;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return next_rate;
+}
+
/**
* iwl3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd
@@ -308,6 +339,7 @@ static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
struct iwl3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->status);
int rate_idx;
+ int fail;
if ((index >= txq->q.n_bd) || (iwl3945_x2_queue_used(&txq->q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
@@ -318,9 +350,18 @@ static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
}
info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb[0]);
- memset(&info->status, 0, sizeof(info->status));
+ ieee80211_tx_info_clear_status(info);
+
+ /* Fill the MRR chain with some info about on-chip retransmissions */
+ rate_idx = iwl3945_hwrate_to_plcp_idx(tx_resp->rate);
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate_idx -= IWL_FIRST_OFDM_RATE;
+
+ fail = tx_resp->failure_frame;
+
+ info->status.rates[0].idx = rate_idx;
+ info->status.rates[0].count = fail + 1; /* add final attempt */
- info->status.retry_count = tx_resp->failure_frame;
/* tx_status->rts_retry_count = tx_resp->failure_rts; */
info->flags |= ((status & TX_STATUS_MSK) == TX_STATUS_SUCCESS) ?
IEEE80211_TX_STAT_ACK : 0;
@@ -329,10 +370,6 @@ static void iwl3945_rx_reply_tx(struct iwl3945_priv *priv,
txq_id, iwl3945_get_tx_fail_reason(status), status,
tx_resp->rate, tx_resp->failure_frame);
- rate_idx = iwl3945_hwrate_to_plcp_idx(tx_resp->rate);
- if (info->band == IEEE80211_BAND_5GHZ)
- rate_idx -= IWL_FIRST_OFDM_RATE;
- info->tx_rate_idx = rate_idx;
IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
iwl3945_tx_queue_reclaim(priv, txq_id, index);
@@ -756,13 +793,19 @@ int iwl3945_hw_txq_free_tfd(struct iwl3945_priv *priv, struct iwl3945_tx_queue *
u8 iwl3945_hw_find_station(struct iwl3945_priv *priv, const u8 *addr)
{
- int i;
+ int i, start = IWL_AP_ID;
int ret = IWL_INVALID_STATION;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
+
+ if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) ||
+ (priv->iw_mode == NL80211_IFTYPE_AP))
+ start = IWL_STA_ID;
+
+ if (is_broadcast_ether_addr(addr))
+ return priv->hw_setting.bcast_sta_id;
spin_lock_irqsave(&priv->sta_lock, flags);
- for (i = IWL_STA_ID; i < priv->hw_setting.max_stations; i++)
+ for (i = start; i < priv->hw_setting.max_stations; i++)
if ((priv->stations[i].used) &&
(!compare_ether_addr
(priv->stations[i].sta.sta.addr, addr))) {
@@ -770,8 +813,8 @@ u8 iwl3945_hw_find_station(struct iwl3945_priv *priv, const u8 *addr)
goto out;
}
- IWL_DEBUG_INFO("can not find STA %s (total %d)\n",
- print_mac(mac, addr), priv->num_stations);
+ IWL_DEBUG_INFO("can not find STA %pM (total %d)\n",
+ addr, priv->num_stations);
out:
spin_unlock_irqrestore(&priv->sta_lock, flags);
return ret;
@@ -1060,9 +1103,8 @@ int iwl3945_hw_nic_init(struct iwl3945_priv *priv)
CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- rc = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ rc = iwl3945_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
if (rc < 0) {
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_INFO("Failed to init the card\n");
@@ -1243,8 +1285,7 @@ int iwl3945_hw_nic_stop_master(struct iwl3945_priv *priv)
IWL_DEBUG_INFO("Card in power save, master is already "
"stopped\n");
else {
- rc = iwl3945_poll_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ rc = iwl3945_poll_direct_bit(priv, CSR_RESET,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
if (rc < 0) {
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1269,9 +1310,8 @@ int iwl3945_hw_nic_reset(struct iwl3945_priv *priv)
iwl3945_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
- rc = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ iwl3945_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
rc = iwl3945_grab_nic_access(priv);
if (!rc) {
@@ -1830,7 +1870,7 @@ static int iwl3945_hw_reg_comp_txpower_temp(struct iwl3945_priv *priv)
ref_temp = (s16)priv->eeprom.groups[ch_info->group_index].
temperature;
- /* get power index adjustment based on curr and factory
+ /* get power index adjustment based on current and factory
* temps */
delta_index = iwl3945_hw_reg_adjust_power_by_temp(temperature,
ref_temp);
@@ -2268,7 +2308,8 @@ int iwl3945_hw_rxq_stop(struct iwl3945_priv *priv)
}
iwl3945_write_direct32(priv, FH_RCSR_CONFIG(0), 0);
- rc = iwl3945_poll_direct_bit(priv, FH_RSSR_STATUS, (1 << 24), 1000);
+ rc = iwl3945_poll_direct_bit(priv, FH_RSSR_STATUS,
+ FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
if (rc < 0)
IWL_ERROR("Can't stop Rx DMA.\n");
@@ -2337,7 +2378,8 @@ int iwl3945_init_hw_rate_table(struct iwl3945_priv *priv)
iwl3945_hw_set_rate_n_flags(iwl3945_rates[i].plcp, 0);
table[index].try_cnt = priv->retry_rate;
prev_index = iwl3945_get_prev_ieee_rate(i);
- table[index].next_rate_index = iwl3945_rates[prev_index].table_rs_index;
+ table[index].next_rate_index =
+ iwl3945_rates[prev_index].table_rs_index;
}
switch (priv->band) {
@@ -2345,11 +2387,14 @@ int iwl3945_init_hw_rate_table(struct iwl3945_priv *priv)
IWL_DEBUG_RATE("Select A mode rate scale\n");
/* If one of the following CCK rates is used,
* have it fall back to the 6M OFDM rate */
- for (i = IWL_RATE_1M_INDEX_TABLE; i <= IWL_RATE_11M_INDEX_TABLE; i++)
- table[i].next_rate_index = iwl3945_rates[IWL_FIRST_OFDM_RATE].table_rs_index;
+ for (i = IWL_RATE_1M_INDEX_TABLE;
+ i <= IWL_RATE_11M_INDEX_TABLE; i++)
+ table[i].next_rate_index =
+ iwl3945_rates[IWL_FIRST_OFDM_RATE].table_rs_index;
/* Don't fall back to CCK rates */
- table[IWL_RATE_12M_INDEX_TABLE].next_rate_index = IWL_RATE_9M_INDEX_TABLE;
+ table[IWL_RATE_12M_INDEX_TABLE].next_rate_index =
+ IWL_RATE_9M_INDEX_TABLE;
/* Don't drop out of OFDM rates */
table[IWL_RATE_6M_INDEX_TABLE].next_rate_index =
@@ -2360,11 +2405,20 @@ int iwl3945_init_hw_rate_table(struct iwl3945_priv *priv)
IWL_DEBUG_RATE("Select B/G mode rate scale\n");
/* If an OFDM rate is used, have it fall back to the
* 1M CCK rates */
- for (i = IWL_RATE_6M_INDEX_TABLE; i <= IWL_RATE_54M_INDEX_TABLE; i++)
- table[i].next_rate_index = iwl3945_rates[IWL_FIRST_CCK_RATE].table_rs_index;
- /* CCK shouldn't fall back to OFDM... */
- table[IWL_RATE_11M_INDEX_TABLE].next_rate_index = IWL_RATE_5M_INDEX_TABLE;
+ if (!(priv->sta_supp_rates & IWL_OFDM_RATES_MASK) &&
+ iwl3945_is_associated(priv)) {
+
+ index = IWL_FIRST_CCK_RATE;
+ for (i = IWL_RATE_6M_INDEX_TABLE;
+ i <= IWL_RATE_54M_INDEX_TABLE; i++)
+ table[i].next_rate_index =
+ iwl3945_rates[index].table_rs_index;
+
+ index = IWL_RATE_11M_INDEX_TABLE;
+ /* CCK shouldn't fall back to OFDM... */
+ table[index].next_rate_index = IWL_RATE_5M_INDEX_TABLE;
+ }
break;
default:
@@ -2428,7 +2482,6 @@ unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv,
frame_size = iwl3945_fill_beacon_frame(priv,
tx_beacon_cmd->frame,
- iwl3945_broadcast_addr,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
BUG_ON(frame_size > MAX_MPDU_SIZE);
@@ -2467,13 +2520,17 @@ void iwl3945_hw_cancel_deferred_work(struct iwl3945_priv *priv)
static struct iwl_3945_cfg iwl3945_bg_cfg = {
.name = "3945BG",
- .fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
+ .fw_name_pre = IWL3945_FW_PRE,
+ .ucode_api_max = IWL3945_UCODE_API_MAX,
+ .ucode_api_min = IWL3945_UCODE_API_MIN,
.sku = IWL_SKU_G,
};
static struct iwl_3945_cfg iwl3945_abg_cfg = {
.name = "3945ABG",
- .fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
+ .fw_name_pre = IWL3945_FW_PRE,
+ .ucode_api_max = IWL3945_UCODE_API_MAX,
+ .ucode_api_min = IWL3945_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index bdd32475b99c..2c0ddc5110c6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -50,11 +50,15 @@ extern struct pci_device_id iwl3945_hw_card_ids[];
#include "iwl-3945-debug.h"
#include "iwl-3945-led.h"
-/* Change firmware file name, using "-" and incrementing number,
- * *only* when uCode interface or architecture changes so that it
- * is not compatible with earlier drivers.
- * This number will also appear in << 8 position of 1st dword of uCode file */
-#define IWL3945_UCODE_API "-1"
+/* Highest firmware API version supported */
+#define IWL3945_UCODE_API_MAX 2
+
+/* Lowest firmware API version supported */
+#define IWL3945_UCODE_API_MIN 1
+
+#define IWL3945_FW_PRE "iwlwifi-3945-"
+#define _IWL3945_MODULE_FIRMWARE(api) IWL3945_FW_PRE #api ".ucode"
+#define IWL3945_MODULE_FIRMWARE(api) _IWL3945_MODULE_FIRMWARE(api)
/* Default noise level to report when noise measurement is not available.
* This may be because we're:
@@ -401,12 +405,6 @@ struct iwl3945_rx_queue {
#define SCAN_INTERVAL 100
-#define MAX_A_CHANNELS 252
-#define MIN_A_CHANNELS 7
-
-#define MAX_B_CHANNELS 14
-#define MIN_B_CHANNELS 1
-
#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */
#define STATUS_INT_ENABLED 2
@@ -472,7 +470,6 @@ union iwl3945_qos_capabity {
/* QoS structures */
struct iwl3945_qos_info {
- int qos_enable;
int qos_active;
union iwl3945_qos_capabity qos_cap;
struct iwl3945_qosparam_cmd def_qos_parm;
@@ -505,7 +502,7 @@ struct fw_desc {
/* uCode file layout */
struct iwl3945_ucode {
- __le32 ver; /* major/minor/subminor */
+ __le32 ver; /* major/minor/API/serial */
__le32 inst_size; /* bytes of runtime instructions */
__le32 data_size; /* bytes of runtime data */
__le32 init_size; /* bytes of initialization instructions */
@@ -587,8 +584,7 @@ extern int iwl3945_send_cmd_pdu(struct iwl3945_priv *priv, u8 id, u16 len,
extern int __must_check iwl3945_send_cmd(struct iwl3945_priv *priv,
struct iwl3945_host_cmd *cmd);
extern unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv,
- struct ieee80211_hdr *hdr,
- const u8 *dest, int left);
+ struct ieee80211_hdr *hdr,int left);
extern int iwl3945_rx_queue_update_write_ptr(struct iwl3945_priv *priv,
struct iwl3945_rx_queue *q);
extern int iwl3945_send_statistics_request(struct iwl3945_priv *priv);
@@ -762,6 +758,8 @@ struct iwl3945_priv {
void __iomem *hw_base;
/* uCode images, save to reload in case of failure */
+ u32 ucode_ver; /* ucode version, copy of
+ iwl3945_ucode.ver */
struct fw_desc ucode_code; /* runtime inst */
struct fw_desc ucode_data; /* runtime data original */
struct fw_desc ucode_data_backup; /* runtime data save/restore */
@@ -804,6 +802,8 @@ struct iwl3945_priv {
u16 active_rate;
u16 active_rate_basic;
+ u32 sta_supp_rates;
+
u8 call_post_assoc_from_beacon;
/* Rate scaling data */
s8 data_retry_limit;
@@ -828,8 +828,6 @@ struct iwl3945_priv {
unsigned long last_statistics_time;
/* context information */
- u8 essid[IW_ESSID_MAX_SIZE];
- u8 essid_len;
u16 rates_mask;
u32 power_mode;
@@ -888,7 +886,6 @@ struct iwl3945_priv {
struct work_struct report_work;
struct work_struct request_scan;
struct work_struct beacon_update;
- struct work_struct set_monitor;
struct tasklet_struct irq_tasklet;
@@ -903,9 +900,6 @@ struct iwl3945_priv {
s8 user_txpower_limit;
s8 max_channel_txpower_limit;
-#ifdef CONFIG_PM
- u32 pm_state[16];
-#endif
#ifdef CONFIG_IWL3945_DEBUG
/* debugging info */
@@ -954,6 +948,8 @@ static inline int is_channel_ibss(const struct iwl3945_channel_info *ch)
extern const struct iwl3945_channel_info *iwl3945_get_channel_info(
const struct iwl3945_priv *priv, enum ieee80211_band band, u16 channel);
+extern int iwl3945_rs_next_rate(struct iwl3945_priv *priv, int rate);
+
/* Requires full declaration of iwl3945_priv before including */
#include "iwl-3945-io.h"
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
index f4793a609443..6649f7b55650 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -71,7 +71,7 @@
#include "iwl-fh.h"
-/* EERPROM */
+/* EEPROM */
#define IWL4965_EEPROM_IMG_SIZE 1024
/*
@@ -84,12 +84,6 @@
#define IWL_CMD_FIFO_NUM 4
#define IWL49_FIRST_AMPDU_QUEUE 7
-/* Tx rates */
-#define IWL_CCK_RATES 4
-#define IWL_OFDM_RATES 8
-#define IWL_HT_RATES 16
-#define IWL_MAX_RATES (IWL_CCK_RATES+IWL_OFDM_RATES+IWL_HT_RATES)
-
/* Time constants */
#define SHORT_SLOT_TIME 9
#define LONG_SLOT_TIME 20
@@ -111,7 +105,6 @@
#define PCI_CFG_CMD_REG_INT_DIS_MSK 0x04
#define PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT (0x80000000)
-#define TFD_QUEUE_SIZE_MAX (256)
#define IWL_NUM_SCAN_RATES (2)
@@ -287,13 +280,13 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr)
* that target txpower.
*
*
- * 3) Determine (EEPROM) calibration subband for the target channel, by
- * comparing against first and last channels in each subband
+ * 3) Determine (EEPROM) calibration sub band for the target channel, by
+ * comparing against first and last channels in each sub band
* (see struct iwl4965_eeprom_calib_subband_info).
*
*
* 4) Linearly interpolate (EEPROM) factory calibration measurement sets,
- * referencing the 2 factory-measured (sample) channels within the subband.
+ * referencing the 2 factory-measured (sample) channels within the sub band.
*
* Interpolation is based on difference between target channel's frequency
* and the sample channels' frequencies. Since channel numbers are based
@@ -301,7 +294,7 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr)
* to interpolating based on channel number differences.
*
* Note that the sample channels may or may not be the channels at the
- * edges of the subband. The target channel may be "outside" of the
+ * edges of the sub band. The target channel may be "outside" of the
* span of the sampled channels.
*
* Driver may choose the pair (for 2 Tx chains) of measurements (see
@@ -345,7 +338,7 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr)
* "4965 temperature calculation".
*
* If current temperature is higher than factory temperature, driver must
- * increase gain (lower gain table index), and vice versa.
+ * increase gain (lower gain table index), and vice verse.
*
* Temperature affects gain differently for different channels:
*
@@ -815,125 +808,14 @@ enum {
* up to 7 DMA channels (FIFOs). Each Tx queue is supported by a circular array
* in DRAM containing 256 Transmit Frame Descriptors (TFDs).
*/
-#define IWL49_MAX_WIN_SIZE 64
-#define IWL49_QUEUE_SIZE 256
#define IWL49_NUM_FIFOS 7
#define IWL49_CMD_FIFO_NUM 4
#define IWL49_NUM_QUEUES 16
#define IWL49_NUM_AMPDU_QUEUES 8
-/**
- * struct iwl_tfd_frame_data
- *
- * Describes up to 2 buffers containing (contiguous) portions of a Tx frame.
- * Each buffer must be on dword boundary.
- * Up to 10 iwl_tfd_frame_data structures, describing up to 20 buffers,
- * may be filled within a TFD (iwl_tfd_frame).
- *
- * Bit fields in tb1_addr:
- * 31- 0: Tx buffer 1 address bits [31:0]
- *
- * Bit fields in val1:
- * 31-16: Tx buffer 2 address bits [15:0]
- * 15- 4: Tx buffer 1 length (bytes)
- * 3- 0: Tx buffer 1 address bits [32:32]
- *
- * Bit fields in val2:
- * 31-20: Tx buffer 2 length (bytes)
- * 19- 0: Tx buffer 2 address bits [35:16]
- */
-struct iwl_tfd_frame_data {
- __le32 tb1_addr;
-
- __le32 val1;
- /* __le32 ptb1_32_35:4; */
-#define IWL_tb1_addr_hi_POS 0
-#define IWL_tb1_addr_hi_LEN 4
-#define IWL_tb1_addr_hi_SYM val1
- /* __le32 tb_len1:12; */
-#define IWL_tb1_len_POS 4
-#define IWL_tb1_len_LEN 12
-#define IWL_tb1_len_SYM val1
- /* __le32 ptb2_0_15:16; */
-#define IWL_tb2_addr_lo16_POS 16
-#define IWL_tb2_addr_lo16_LEN 16
-#define IWL_tb2_addr_lo16_SYM val1
-
- __le32 val2;
- /* __le32 ptb2_16_35:20; */
-#define IWL_tb2_addr_hi20_POS 0
-#define IWL_tb2_addr_hi20_LEN 20
-#define IWL_tb2_addr_hi20_SYM val2
- /* __le32 tb_len2:12; */
-#define IWL_tb2_len_POS 20
-#define IWL_tb2_len_LEN 12
-#define IWL_tb2_len_SYM val2
-} __attribute__ ((packed));
-
/**
- * struct iwl_tfd_frame
- *
- * Transmit Frame Descriptor (TFD)
- *
- * 4965 supports up to 16 Tx queues resident in host DRAM.
- * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM.
- * Both driver and device share these circular buffers, each of which must be
- * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes for 4965.
- *
- * Driver must indicate the physical address of the base of each
- * circular buffer via the 4965's FH_MEM_CBBC_QUEUE registers.
- *
- * Each TFD contains pointer/size information for up to 20 data buffers
- * in host DRAM. These buffers collectively contain the (one) frame described
- * by the TFD. Each buffer must be a single contiguous block of memory within
- * itself, but buffers may be scattered in host DRAM. Each buffer has max size
- * of (4K - 4). The 4965 concatenates all of a TFD's buffers into a single
- * Tx frame, up to 8 KBytes in size.
- *
- * Bit fields in the control dword (val0):
- * 31-30: # dwords (0-3) of padding required at end of frame for 16-byte bound
- * 29: reserved
- * 28-24: # Transmit Buffer Descriptors in TFD
- * 23- 0: reserved
- *
- * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx.
- */
-struct iwl_tfd_frame {
- __le32 val0;
- /* __le32 rsvd1:24; */
- /* __le32 num_tbs:5; */
-#define IWL_num_tbs_POS 24
-#define IWL_num_tbs_LEN 5
-#define IWL_num_tbs_SYM val0
- /* __le32 rsvd2:1; */
- /* __le32 padding:2; */
- struct iwl_tfd_frame_data pa[10];
- __le32 reserved;
-} __attribute__ ((packed));
-
-
-/**
- * struct iwl4965_queue_byte_cnt_entry
- *
- * Byte Count Table Entry
- *
- * Bit fields:
- * 15-12: reserved
- * 11- 0: total to-be-transmitted byte count of frame (does not include command)
- */
-struct iwl4965_queue_byte_cnt_entry {
- __le16 val;
- /* __le16 byte_cnt:12; */
-#define IWL_byte_cnt_POS 0
-#define IWL_byte_cnt_LEN 12
-#define IWL_byte_cnt_SYM val
- /* __le16 rsvd:4; */
-} __attribute__ ((packed));
-
-
-/**
- * struct iwl4965_sched_queue_byte_cnt_tbl
+ * struct iwl4965_schedq_bc_tbl
*
* Byte Count table
*
@@ -947,71 +829,12 @@ struct iwl4965_queue_byte_cnt_entry {
* count table for the chosen Tx queue. If the TFD index is 0-63, the driver
* must duplicate the byte count entry in corresponding index 256-319.
*
- * "dont_care" padding puts each byte count table on a 1024-byte boundary;
+ * padding puts each byte count table on a 1024-byte boundary;
* 4965 assumes tables are separated by 1024 bytes.
*/
-struct iwl4965_sched_queue_byte_cnt_tbl {
- struct iwl4965_queue_byte_cnt_entry tfd_offset[IWL49_QUEUE_SIZE +
- IWL49_MAX_WIN_SIZE];
- u8 dont_care[1024 -
- (IWL49_QUEUE_SIZE + IWL49_MAX_WIN_SIZE) *
- sizeof(__le16)];
-} __attribute__ ((packed));
-
-
-/**
- * struct iwl4965_shared - handshake area for Tx and Rx
- *
- * For convenience in allocating memory, this structure combines 2 areas of
- * DRAM which must be shared between driver and 4965. These do not need to
- * be combined, if better allocation would result from keeping them separate:
- *
- * 1) The Tx byte count tables occupy 1024 bytes each (16 KBytes total for
- * 16 queues). Driver uses SCD_DRAM_BASE_ADDR to tell 4965 where to find
- * the first of these tables. 4965 assumes tables are 1024 bytes apart.
- *
- * 2) The Rx status (val0 and val1) occupies only 8 bytes. Driver uses
- * FH_RSCSR_CHNL0_STTS_WPTR_REG to tell 4965 where to find this area.
- * Driver reads val0 to determine the latest Receive Buffer Descriptor (RBD)
- * that has been filled by the 4965.
- *
- * Bit fields val0:
- * 31-12: Not used
- * 11- 0: Index of last filled Rx buffer descriptor (4965 writes, driver reads)
- *
- * Bit fields val1:
- * 31- 0: Not used
- */
-struct iwl4965_shared {
- struct iwl4965_sched_queue_byte_cnt_tbl
- queues_byte_cnt_tbls[IWL49_NUM_QUEUES];
- __le32 rb_closed;
-
- /* __le32 rb_closed_stts_rb_num:12; */
-#define IWL_rb_closed_stts_rb_num_POS 0
-#define IWL_rb_closed_stts_rb_num_LEN 12
-#define IWL_rb_closed_stts_rb_num_SYM rb_closed
- /* __le32 rsrv1:4; */
- /* __le32 rb_closed_stts_rx_frame_num:12; */
-#define IWL_rb_closed_stts_rx_frame_num_POS 16
-#define IWL_rb_closed_stts_rx_frame_num_LEN 12
-#define IWL_rb_closed_stts_rx_frame_num_SYM rb_closed
- /* __le32 rsrv2:4; */
-
- __le32 frm_finished;
- /* __le32 frame_finished_stts_rb_num:12; */
-#define IWL_frame_finished_stts_rb_num_POS 0
-#define IWL_frame_finished_stts_rb_num_LEN 12
-#define IWL_frame_finished_stts_rb_num_SYM frm_finished
- /* __le32 rsrv3:4; */
- /* __le32 frame_finished_stts_rx_frame_num:12; */
-#define IWL_frame_finished_stts_rx_frame_num_POS 16
-#define IWL_frame_finished_stts_rx_frame_num_LEN 12
-#define IWL_frame_finished_stts_rx_frame_num_SYM frm_finished
- /* __le32 rsrv4:4; */
-
- __le32 padding1; /* so that allocation will be aligned to 16B */
- __le32 padding2;
+struct iwl4965_scd_bc_tbl {
+ __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
+ u8 pad[1024 - (TFD_QUEUE_BC_SIZE) * sizeof(__le16)];
} __attribute__ ((packed));
-#endif /* __iwl4965_4965_hw_h__ */
+#endif /* !__iwl_4965_hw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 9838de5f4369..5a72bc0377de 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -48,18 +48,21 @@
static int iwl4965_send_tx_power(struct iwl_priv *priv);
static int iwl4965_hw_get_temperature(const struct iwl_priv *priv);
-/* Change firmware file name, using "-" and incrementing number,
- * *only* when uCode interface or architecture changes so that it
- * is not compatible with earlier drivers.
- * This number will also appear in << 8 position of 1st dword of uCode file */
-#define IWL4965_UCODE_API "-2"
+/* Highest firmware API version supported */
+#define IWL4965_UCODE_API_MAX 2
+
+/* Lowest firmware API version supported */
+#define IWL4965_UCODE_API_MIN 2
+
+#define IWL4965_FW_PRE "iwlwifi-4965-"
+#define _IWL4965_MODULE_FIRMWARE(api) IWL4965_FW_PRE #api ".ucode"
+#define IWL4965_MODULE_FIRMWARE(api) _IWL4965_MODULE_FIRMWARE(api)
/* module parameters */
static struct iwl_mod_params iwl4965_mod_params = {
.num_of_queues = IWL49_NUM_QUEUES,
.num_of_ampdu_queues = IWL49_NUM_AMPDU_QUEUES,
- .enable_qos = 1,
.amsdu_size_8K = 1,
.restart_fw = 1,
/* the rest are 0 by default */
@@ -246,7 +249,7 @@ static int iwl4965_set_ucode_ptrs(struct iwl_priv *priv)
iwl_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG,
priv->ucode_data.len);
- /* Inst bytecount must be last to set up, bit 31 signals uCode
+ /* Inst byte count must be last to set up, bit 31 signals uCode
* that all new ptr/size info is in place */
iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
priv->ucode_code.len | BSM_DRAM_INST_LOAD);
@@ -318,31 +321,13 @@ static int is_fat_channel(__le32 rxon_flags)
/*
* EEPROM handlers
*/
-
-static int iwl4965_eeprom_check_version(struct iwl_priv *priv)
+static u16 iwl4965_eeprom_calib_version(struct iwl_priv *priv)
{
- u16 eeprom_ver;
- u16 calib_ver;
-
- eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
-
- calib_ver = iwl_eeprom_query16(priv, EEPROM_4965_CALIB_VERSION_OFFSET);
-
- if (eeprom_ver < EEPROM_4965_EEPROM_VERSION ||
- calib_ver < EEPROM_4965_TX_POWER_VERSION)
- goto err;
-
- return 0;
-err:
- IWL_ERROR("Unsuported EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
- eeprom_ver, EEPROM_4965_EEPROM_VERSION,
- calib_ver, EEPROM_4965_TX_POWER_VERSION);
- return -EINVAL;
-
+ return iwl_eeprom_query16(priv, EEPROM_4965_CALIB_VERSION_OFFSET);
}
/*
- * Activate/Deactivat Tx DMA/FIFO channels according tx fifos mask
+ * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
* must be called under priv->lock and mac access
*/
static void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask)
@@ -366,9 +351,8 @@ static int iwl4965_apm_init(struct iwl_priv *priv)
iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* wait for clock stabilization */
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
if (ret < 0) {
IWL_DEBUG_INFO("Failed to init the card\n");
goto out;
@@ -414,7 +398,7 @@ static void iwl4965_nic_config(struct iwl_priv *priv)
/* L1 is enabled by BIOS */
if ((link & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN)
- /* diable L0S disabled L1A enabled */
+ /* disable L0S disabled L1A enabled */
iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
else
/* L0S enabled L1A disabled */
@@ -442,7 +426,6 @@ static void iwl4965_nic_config(struct iwl_priv *priv)
static int iwl4965_apm_stop_master(struct iwl_priv *priv)
{
- int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
@@ -450,17 +433,13 @@ static int iwl4965_apm_stop_master(struct iwl_priv *priv)
/* set stop master bit */
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
- ret = iwl_poll_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- goto out;
+ iwl_poll_direct_bit(priv, CSR_RESET,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
-out:
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_INFO("stop master\n");
- return ret;
+ return 0;
}
static void iwl4965_apm_stop(struct iwl_priv *priv)
@@ -496,11 +475,9 @@ static int iwl4965_apm_reset(struct iwl_priv *priv)
iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- ret = iwl_poll_bit(priv, CSR_RESET,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25);
-
- if (ret)
+ ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ if (ret < 0)
goto out;
udelay(10);
@@ -537,10 +514,10 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv)
struct iwl_chain_noise_data *data = &(priv->chain_noise_data);
if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
- struct iwl4965_calibration_cmd cmd;
+ struct iwl_calib_diff_gain_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
- cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
+ cmd.hdr.op_code = IWL_PHY_CALIBRATE_DIFF_GAIN_CMD;
cmd.diff_gain_a = 0;
cmd.diff_gain_b = 0;
cmd.diff_gain_c = 0;
@@ -587,11 +564,11 @@ static void iwl4965_gain_computation(struct iwl_priv *priv,
/* Differential gain gets sent to uCode only once */
if (!data->radio_write) {
- struct iwl4965_calibration_cmd cmd;
+ struct iwl_calib_diff_gain_cmd cmd;
data->radio_write = 1;
memset(&cmd, 0, sizeof(cmd));
- cmd.opCode = PHY_CALIBRATE_DIFF_GAIN_CMD;
+ cmd.hdr.op_code = IWL_PHY_CALIBRATE_DIFF_GAIN_CMD;
cmd.diff_gain_a = data->delta_gain_code[0];
cmd.diff_gain_b = data->delta_gain_code[1];
cmd.diff_gain_c = data->delta_gain_code[2];
@@ -619,10 +596,10 @@ static void iwl4965_gain_computation(struct iwl_priv *priv,
static void iwl4965_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
__le32 *tx_flags)
{
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
*tx_flags |= TX_CMD_FLG_RTS_MSK;
*tx_flags &= ~TX_CMD_FLG_CTS_MSK;
- } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
+ } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
*tx_flags &= ~TX_CMD_FLG_RTS_MSK;
*tx_flags |= TX_CMD_FLG_CTS_MSK;
}
@@ -643,7 +620,7 @@ static void iwl4965_bg_txpower_work(struct work_struct *work)
mutex_lock(&priv->mutex);
- /* Regardless of if we are assocaited, we must reconfigure the
+ /* Regardless of if we are associated, we must reconfigure the
* TX power since frames can be sent on non-radar channels while
* not associated */
iwl4965_send_tx_power(priv);
@@ -679,7 +656,7 @@ static void iwl4965_tx_queue_set_status(struct iwl_priv *priv,
int txq_id = txq->q.id;
/* Find out whether to activate Tx queue */
- int active = test_bit(txq_id, &priv->txq_ctx_active_msk)?1:0;
+ int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
/* Set up and activate */
iwl_write_prph(priv, IWL49_SCD_QUEUE_STATUS_BITS(txq_id),
@@ -709,9 +686,10 @@ static const u16 default_queue_to_tx_fifo[] = {
static int iwl4965_alive_notify(struct iwl_priv *priv)
{
u32 a;
- int i = 0;
unsigned long flags;
int ret;
+ int i, chan;
+ u32 reg_val;
spin_lock_irqsave(&priv->lock, flags);
@@ -733,8 +711,18 @@ static int iwl4965_alive_notify(struct iwl_priv *priv)
/* Tel 4965 where to find Tx byte count tables */
iwl_write_prph(priv, IWL49_SCD_DRAM_BASE_ADDR,
- (priv->shared_phys +
- offsetof(struct iwl4965_shared, queues_byte_cnt_tbls)) >> 10);
+ priv->scd_bc_tbls.dma >> 10);
+
+ /* Enable DMA channel */
+ for (chan = 0; chan < FH49_TCSR_CHNL_NUM ; chan++)
+ iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(chan),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
+
+ /* Update FH chicken bits */
+ reg_val = iwl_read_direct32(priv, FH_TX_CHICKEN_BITS_REG);
+ iwl_write_direct32(priv, FH_TX_CHICKEN_BITS_REG,
+ reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
/* Disable chain mode for all queues */
iwl_write_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, 0);
@@ -766,7 +754,7 @@ static int iwl4965_alive_notify(struct iwl_priv *priv)
(1 << priv->hw_params.max_txq_num) - 1);
/* Activate all Tx DMA/FIFO channels */
- priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 7));
+ priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 6));
iwl4965_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0);
@@ -822,7 +810,9 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv)
}
priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues;
- priv->hw_params.first_ampdu_q = IWL49_FIRST_AMPDU_QUEUE;
+ priv->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM;
+ priv->hw_params.scd_bc_tbls_size =
+ IWL49_NUM_QUEUES * sizeof(struct iwl4965_scd_bc_tbl);
priv->hw_params.max_stations = IWL4965_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL4965_BROADCAST_ID;
priv->hw_params.max_data_size = IWL49_RTC_DATA_SIZE;
@@ -1650,36 +1640,6 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
}
#endif
-static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv)
-{
- struct iwl4965_shared *s = priv->shared_virt;
- return le32_to_cpu(s->rb_closed) & 0xFFF;
-}
-
-static int iwl4965_alloc_shared_mem(struct iwl_priv *priv)
-{
- priv->shared_virt = pci_alloc_consistent(priv->pci_dev,
- sizeof(struct iwl4965_shared),
- &priv->shared_phys);
- if (!priv->shared_virt)
- return -ENOMEM;
-
- memset(priv->shared_virt, 0, sizeof(struct iwl4965_shared));
-
- priv->rb_closed_offset = offsetof(struct iwl4965_shared, rb_closed);
-
- return 0;
-}
-
-static void iwl4965_free_shared_mem(struct iwl_priv *priv)
-{
- if (priv->shared_virt)
- pci_free_consistent(priv->pci_dev,
- sizeof(struct iwl4965_shared),
- priv->shared_virt,
- priv->shared_phys);
-}
-
/**
* iwl4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
*/
@@ -1687,21 +1647,22 @@ static void iwl4965_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
struct iwl_tx_queue *txq,
u16 byte_cnt)
{
- int len;
+ struct iwl4965_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
int txq_id = txq->q.id;
- struct iwl4965_shared *shared_data = priv->shared_virt;
+ int write_ptr = txq->q.write_ptr;
+ int len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ __le16 bc_ent;
- len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
+ bc_ent = cpu_to_le16(len & 0xFFF);
/* Set up byte count within first 256 entries */
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[txq->q.write_ptr], byte_cnt, len);
+ scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
/* If within first 64 entries, duplicate at end */
- if (txq->q.write_ptr < IWL49_MAX_WIN_SIZE)
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[IWL49_QUEUE_SIZE + txq->q.write_ptr],
- byte_cnt, len);
+ if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
}
/**
@@ -1956,7 +1917,7 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
ra_tid = BUILD_RAxTID(sta_id, tid);
/* Modify device's station table to Tx this TID */
- iwl_sta_modify_enable_tid_tx(priv, sta_id, tid);
+ iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
spin_lock_irqsave(&priv->lock, flags);
ret = iwl_grab_nic_access(priv);
@@ -2037,7 +1998,7 @@ static inline u32 iwl4965_get_scd_ssn(struct iwl4965_tx_resp *tx_resp)
}
/**
- * iwl4965_tx_status_reply_tx - Handle Tx rspnse for frames in aggregation queue
+ * iwl4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue
*/
static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
struct iwl_ht_agg *agg,
@@ -2059,7 +2020,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
agg->rate_n_flags = rate_n_flags;
agg->bitmap = 0;
- /* # frames attempted by Tx command */
+ /* num frames attempted by Tx command */
if (agg->frame_count == 1) {
/* Only one frame was attempted; no block-ack will arrive */
status = le16_to_cpu(frame_status[0].status);
@@ -2070,9 +2031,9 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
agg->frame_count, agg->start_idx, idx);
info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]);
- info->status.retry_count = tx_resp->failure_frame;
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= iwl_is_tx_success(status)?
+ info->flags |= iwl_is_tx_success(status) ?
IEEE80211_TX_STAT_ACK : 0;
iwl_hwrate_to_tx_control(priv, rate_n_flags, info);
/* FIXME: code repetition end */
@@ -2158,12 +2119,13 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
struct iwl_tx_queue *txq = &priv->txq[txq_id];
+ struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info;
struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le32_to_cpu(tx_resp->u.status);
- int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION;
- __le16 fc;
- struct ieee80211_hdr *hdr;
+ int tid = MAX_TID_COUNT;
+ int sta_id;
+ int freed;
u8 *qc = NULL;
if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) {
@@ -2178,8 +2140,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
memset(&info->status, 0, sizeof(info->status));
hdr = iwl_tx_queue_get_hdr(priv, txq_id, index);
- fc = hdr->frame_control;
- if (ieee80211_is_data_qos(fc)) {
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
}
@@ -2194,8 +2155,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
const u32 scd_ssn = iwl4965_get_scd_ssn(tx_resp);
struct iwl_ht_agg *agg = NULL;
- if (!qc)
- return;
+ WARN_ON(!qc);
agg = &priv->stations[sta_id].tid[tid].agg;
@@ -2206,54 +2166,49 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
if (txq->q.read_ptr != (scd_ssn & 0xff)) {
- int freed, ampdu_q;
index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
"%d index %d\n", scd_ssn , index);
freed = iwl_tx_queue_reclaim(priv, txq_id, index);
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
- txq_id >= 0 && priv->mac80211_registered &&
- agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) {
- /* calculate mac80211 ampdu sw queue to wake */
- ampdu_q = txq_id - IWL49_FIRST_AMPDU_QUEUE +
- priv->hw->queues;
+ if (priv->mac80211_registered &&
+ (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
+ (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
if (agg->state == IWL_AGG_OFF)
ieee80211_wake_queue(priv->hw, txq_id);
else
- ieee80211_wake_queue(priv->hw, ampdu_q);
+ ieee80211_wake_queue(priv->hw,
+ txq->swq_id);
}
- iwl_txq_check_empty(priv, sta_id, tid, txq_id);
}
} else {
- info->status.retry_count = tx_resp->failure_frame;
- info->flags |=
- iwl_is_tx_success(status) ? IEEE80211_TX_STAT_ACK : 0;
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ info->flags |= iwl_is_tx_success(status) ?
+ IEEE80211_TX_STAT_ACK : 0;
iwl_hwrate_to_tx_control(priv,
le32_to_cpu(tx_resp->rate_n_flags),
info);
- IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags "
- "0x%x retries %d\n", txq_id,
- iwl_get_tx_fail_reason(status),
- status, le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame);
+ IWL_DEBUG_TX_REPLY("TXQ %d status %s (0x%08x) "
+ "rate_n_flags 0x%x retries %d\n",
+ txq_id,
+ iwl_get_tx_fail_reason(status), status,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame);
- IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
-
- if (index != -1) {
- int freed = iwl_tx_queue_reclaim(priv, txq_id, index);
- if (tid != MAX_TID_COUNT)
+ freed = iwl_tx_queue_reclaim(priv, txq_id, index);
+ if (qc && likely(sta_id != IWL_INVALID_STATION))
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
- (txq_id >= 0) && priv->mac80211_registered)
+
+ if (priv->mac80211_registered &&
+ (iwl_queue_space(&txq->q) > txq->q.low_mark))
ieee80211_wake_queue(priv->hw, txq_id);
- if (tid != MAX_TID_COUNT)
- iwl_txq_check_empty(priv, sta_id, tid, txq_id);
- }
}
+ if (qc && likely(sta_id != IWL_INVALID_STATION))
+ iwl_txq_check_empty(priv, sta_id, tid, txq_id);
+
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n");
}
@@ -2328,9 +2283,6 @@ static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = {
static struct iwl_lib_ops iwl4965_lib = {
.set_hw_params = iwl4965_hw_set_hw_params,
- .alloc_shared_mem = iwl4965_alloc_shared_mem,
- .free_shared_mem = iwl4965_free_shared_mem,
- .shared_mem_rx_idx = iwl4965_shared_mem_rx_idx,
.txq_update_byte_cnt_tbl = iwl4965_txq_update_byte_cnt_tbl,
.txq_set_sched = iwl4965_txq_set_sched,
.txq_agg_enable = iwl4965_txq_agg_enable,
@@ -2347,7 +2299,7 @@ static struct iwl_lib_ops iwl4965_lib = {
.reset = iwl4965_apm_reset,
.stop = iwl4965_apm_stop,
.config = iwl4965_nic_config,
- .set_pwr_src = iwl4965_set_pwr_src,
+ .set_pwr_src = iwl_set_pwr_src,
},
.eeprom_ops = {
.regulatory_bands = {
@@ -2362,11 +2314,11 @@ static struct iwl_lib_ops iwl4965_lib = {
.verify_signature = iwlcore_eeprom_verify_signature,
.acquire_semaphore = iwlcore_eeprom_acquire_semaphore,
.release_semaphore = iwlcore_eeprom_release_semaphore,
- .check_version = iwl4965_eeprom_check_version,
+ .calib_version = iwl4965_eeprom_calib_version,
.query_addr = iwlcore_eeprom_query_addr,
},
.send_tx_power = iwl4965_send_tx_power,
- .update_chain_flags = iwl4965_update_chain_flags,
+ .update_chain_flags = iwl_update_chain_flags,
.temperature = iwl4965_temperature_calib,
};
@@ -2378,15 +2330,19 @@ static struct iwl_ops iwl4965_ops = {
struct iwl_cfg iwl4965_agn_cfg = {
.name = "4965AGN",
- .fw_name = "iwlwifi-4965" IWL4965_UCODE_API ".ucode",
+ .fw_name_pre = IWL4965_FW_PRE,
+ .ucode_api_max = IWL4965_UCODE_API_MAX,
+ .ucode_api_min = IWL4965_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.eeprom_size = IWL4965_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_4965_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION,
.ops = &iwl4965_ops,
.mod_params = &iwl4965_mod_params,
};
/* Module firmware */
-MODULE_FIRMWARE("iwlwifi-4965" IWL4965_UCODE_API ".ucode");
+MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE(IWL4965_UCODE_API_MAX));
module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
@@ -2394,7 +2350,7 @@ module_param_named(disable, iwl4965_mod_params.disable, int, 0444);
MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444);
MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
-module_param_named(debug, iwl4965_mod_params.debug, int, 0444);
+module_param_named(debug, iwl4965_mod_params.debug, uint, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
module_param_named(
disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444);
@@ -2402,9 +2358,6 @@ MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, 0444);
MODULE_PARM_DESC(queues_num, "number of hw queues.");
-/* QoS */
-module_param_named(qos_enable, iwl4965_mod_params.enable_qos, int, 0444);
-MODULE_PARM_DESC(qos_enable, "enable all QoS functionality");
/* 11n */
module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, 0444);
MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
index c479ee211c5c..82c3859ce0f8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -73,69 +73,27 @@
#define IWL50_RTC_INST_SIZE (IWL50_RTC_INST_UPPER_BOUND - RTC_INST_LOWER_BOUND)
#define IWL50_RTC_DATA_SIZE (IWL50_RTC_DATA_UPPER_BOUND - RTC_DATA_LOWER_BOUND)
-/* EERPROM */
+/* EEPROM */
#define IWL_5000_EEPROM_IMG_SIZE 2048
-
-#define IWL50_MAX_WIN_SIZE 64
-#define IWL50_QUEUE_SIZE 256
#define IWL50_CMD_FIFO_NUM 7
#define IWL50_NUM_QUEUES 20
#define IWL50_NUM_AMPDU_QUEUES 10
#define IWL50_FIRST_AMPDU_QUEUE 10
-#define IWL_sta_id_POS 12
-#define IWL_sta_id_LEN 4
-#define IWL_sta_id_SYM val
-
/* Fixed (non-configurable) rx data from phy */
-/* Base physical address of iwl5000_shared is provided to SCD_DRAM_BASE_ADDR
- * and &iwl5000_shared.val0 is provided to FH_RSCSR_CHNL0_STTS_WPTR_REG */
-struct iwl5000_sched_queue_byte_cnt_tbl {
- struct iwl4965_queue_byte_cnt_entry tfd_offset[IWL50_QUEUE_SIZE +
- IWL50_MAX_WIN_SIZE];
-} __attribute__ ((packed));
-
-struct iwl5000_shared {
- struct iwl5000_sched_queue_byte_cnt_tbl
- queues_byte_cnt_tbls[IWL50_NUM_QUEUES];
- __le32 rb_closed;
-
- /* __le32 rb_closed_stts_rb_num:12; */
-#define IWL_rb_closed_stts_rb_num_POS 0
-#define IWL_rb_closed_stts_rb_num_LEN 12
-#define IWL_rb_closed_stts_rb_num_SYM rb_closed
- /* __le32 rsrv1:4; */
- /* __le32 rb_closed_stts_rx_frame_num:12; */
-#define IWL_rb_closed_stts_rx_frame_num_POS 16
-#define IWL_rb_closed_stts_rx_frame_num_LEN 12
-#define IWL_rb_closed_stts_rx_frame_num_SYM rb_closed
- /* __le32 rsrv2:4; */
-
- __le32 frm_finished;
- /* __le32 frame_finished_stts_rb_num:12; */
-#define IWL_frame_finished_stts_rb_num_POS 0
-#define IWL_frame_finished_stts_rb_num_LEN 12
-#define IWL_frame_finished_stts_rb_num_SYM frm_finished
- /* __le32 rsrv3:4; */
- /* __le32 frame_finished_stts_rx_frame_num:12; */
-#define IWL_frame_finished_stts_rx_frame_num_POS 16
-#define IWL_frame_finished_stts_rx_frame_num_LEN 12
-#define IWL_frame_finished_stts_rx_frame_num_SYM frm_finished
- /* __le32 rsrv4:4; */
-
- __le32 padding1; /* so that allocation will be aligned to 16B */
- __le32 padding2;
+/**
+ * struct iwl5000_schedq_bc_tbl scheduler byte count table
+ * base physical address of iwl5000_shared
+ * is provided to SCD_DRAM_BASE_ADDR
+ * @tfd_offset 0-12 - tx command byte count
+ * 12-16 - station index
+ */
+struct iwl5000_scd_bc_tbl {
+ __le16 tfd_offset[TFD_QUEUE_BC_SIZE];
} __attribute__ ((packed));
-/* calibrations defined for 5000 */
-/* defines the order in which results should be sent to the runtime uCode */
-enum iwl5000_calib {
- IWL5000_CALIB_LO,
- IWL5000_CALIB_TX_IQ,
- IWL5000_CALIB_TX_IQ_PERD,
-};
#endif /* __iwl_5000_hw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 5155b8a760a7..66d053d28a74 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -44,7 +44,21 @@
#include "iwl-helpers.h"
#include "iwl-5000-hw.h"
-#define IWL5000_UCODE_API "-1"
+/* Highest firmware API version supported */
+#define IWL5000_UCODE_API_MAX 1
+#define IWL5150_UCODE_API_MAX 1
+
+/* Lowest firmware API version supported */
+#define IWL5000_UCODE_API_MIN 1
+#define IWL5150_UCODE_API_MIN 1
+
+#define IWL5000_FW_PRE "iwlwifi-5000-"
+#define _IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE #api ".ucode"
+#define IWL5000_MODULE_FIRMWARE(api) _IWL5000_MODULE_FIRMWARE(api)
+
+#define IWL5150_FW_PRE "iwlwifi-5150-"
+#define _IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE #api ".ucode"
+#define IWL5150_MODULE_FIRMWARE(api) _IWL5150_MODULE_FIRMWARE(api)
static const u16 iwl5000_default_queue_to_tx_fifo[] = {
IWL_TX_FIFO_AC3,
@@ -59,7 +73,6 @@ static const u16 iwl5000_default_queue_to_tx_fifo[] = {
/* FIXME: same implementation as 4965 */
static int iwl5000_apm_stop_master(struct iwl_priv *priv)
{
- int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
@@ -67,17 +80,13 @@ static int iwl5000_apm_stop_master(struct iwl_priv *priv)
/* set stop master bit */
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
- ret = iwl_poll_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ iwl_poll_direct_bit(priv, CSR_RESET,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret < 0)
- goto out;
-out:
spin_unlock_irqrestore(&priv->lock, flags);
IWL_DEBUG_INFO("stop master\n");
- return ret;
+ return 0;
}
@@ -92,7 +101,7 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
- /* Set FH wait treshold to maximum (HW error during stress W/A) */
+ /* Set FH wait threshold to maximum (HW error during stress W/A) */
iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
/* enable HAP INTA to move device L1a -> L0s */
@@ -106,9 +115,8 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* wait for clock stabilization */
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
if (ret < 0) {
IWL_DEBUG_INFO("Failed to init the card\n");
return ret;
@@ -132,7 +140,7 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
return ret;
}
-/* FIXME: this is indentical to 4965 */
+/* FIXME: this is identical to 4965 */
static void iwl5000_apm_stop(struct iwl_priv *priv)
{
unsigned long flags;
@@ -175,9 +183,8 @@ static int iwl5000_apm_reset(struct iwl_priv *priv)
iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* wait for clock stabilization */
- ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
if (ret < 0) {
IWL_DEBUG_INFO("Failed to init the card\n");
goto out;
@@ -217,7 +224,7 @@ static void iwl5000_nic_config(struct iwl_priv *priv)
/* L1 is enabled by BIOS */
if ((link & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN)
- /* diable L0S disabled L1A enabled */
+ /* disable L0S disabled L1A enabled */
iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
else
/* L0S enabled L1A disabled */
@@ -291,30 +298,17 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address)
return (address & ADDRESS_MSK) + (offset << 1);
}
-static int iwl5000_eeprom_check_version(struct iwl_priv *priv)
+static u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv)
{
- u16 eeprom_ver;
struct iwl_eeprom_calib_hdr {
u8 version;
u8 pa_type;
u16 voltage;
} *hdr;
- eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
-
hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv,
EEPROM_5000_CALIB_ALL);
-
- if (eeprom_ver < EEPROM_5000_EEPROM_VERSION ||
- hdr->version < EEPROM_5000_TX_POWER_VERSION)
- goto err;
-
- return 0;
-err:
- IWL_ERROR("Unsuported EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
- eeprom_ver, EEPROM_5000_EEPROM_VERSION,
- hdr->version, EEPROM_5000_TX_POWER_VERSION);
- return -EINVAL;
+ return hdr->version;
}
@@ -348,10 +342,14 @@ static void iwl5000_gain_computation(struct iwl_priv *priv,
data->delta_gain_code[1], data->delta_gain_code[2]);
if (!data->radio_write) {
- struct iwl5000_calibration_chain_noise_gain_cmd cmd;
+ struct iwl_calib_chain_noise_gain_cmd cmd;
+
memset(&cmd, 0, sizeof(cmd));
- cmd.op_code = IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD;
+ cmd.hdr.op_code = IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD;
+ cmd.hdr.first_group = 0;
+ cmd.hdr.groups_num = 1;
+ cmd.hdr.data_valid = 1;
cmd.delta_gain_1 = data->delta_gain_code[1];
cmd.delta_gain_2 = data->delta_gain_code[2];
iwl_send_cmd_pdu_async(priv, REPLY_PHY_CALIBRATION_CMD,
@@ -373,14 +371,19 @@ static void iwl5000_gain_computation(struct iwl_priv *priv,
static void iwl5000_chain_noise_reset(struct iwl_priv *priv)
{
struct iwl_chain_noise_data *data = &priv->chain_noise_data;
+ int ret;
if ((data->state == IWL_CHAIN_NOISE_ALIVE) && iwl_is_associated(priv)) {
- struct iwl5000_calibration_chain_noise_reset_cmd cmd;
-
+ struct iwl_calib_chain_noise_reset_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
- cmd.op_code = IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD;
- if (iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
- sizeof(cmd), &cmd))
+
+ cmd.hdr.op_code = IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD;
+ cmd.hdr.first_group = 0;
+ cmd.hdr.groups_num = 1;
+ cmd.hdr.data_valid = 1;
+ ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
+ sizeof(cmd), &cmd);
+ if (ret)
IWL_ERROR("Could not send REPLY_PHY_CALIBRATION_CMD\n");
data->state = IWL_CHAIN_NOISE_ACCUMULATE;
IWL_DEBUG_CALIB("Run chain_noise_calibrate\n");
@@ -390,8 +393,8 @@ static void iwl5000_chain_noise_reset(struct iwl_priv *priv)
static void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
__le32 *tx_flags)
{
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
+ if ((info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
*tx_flags |= TX_CMD_FLG_RTS_CTS_MSK;
else
*tx_flags &= ~TX_CMD_FLG_RTS_CTS_MSK;
@@ -426,31 +429,41 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
return &priv->eeprom[address];
}
+static s32 iwl5150_get_ct_threshold(struct iwl_priv *priv)
+{
+ const s32 volt2temp_coef = -5;
+ u16 *temp_calib = (u16 *)iwl_eeprom_query_addr(priv,
+ EEPROM_5000_TEMPERATURE);
+ /* offset = temperate - voltage / coef */
+ s32 offset = temp_calib[0] - temp_calib[1] / volt2temp_coef;
+ s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) - offset;
+ return threshold * volt2temp_coef;
+}
+
/*
* Calibration
*/
-static int iwl5000_send_Xtal_calib(struct iwl_priv *priv)
+static int iwl5000_set_Xtal_calib(struct iwl_priv *priv)
{
+ struct iwl_calib_xtal_freq_cmd cmd;
u16 *xtal_calib = (u16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL);
- struct iwl5000_calibration cal_cmd = {
- .op_code = IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD,
- .data = {
- (u8)xtal_calib[0],
- (u8)xtal_calib[1],
- }
- };
-
- return iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD,
- sizeof(cal_cmd), &cal_cmd);
+ cmd.hdr.op_code = IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD;
+ cmd.hdr.first_group = 0;
+ cmd.hdr.groups_num = 1;
+ cmd.hdr.data_valid = 1;
+ cmd.cap_pin1 = (u8)xtal_calib[0];
+ cmd.cap_pin2 = (u8)xtal_calib[1];
+ return iwl_calib_set(&priv->calib_results[IWL_CALIB_XTAL],
+ (u8 *)&cmd, sizeof(cmd));
}
static int iwl5000_send_calib_cfg(struct iwl_priv *priv)
{
- struct iwl5000_calib_cfg_cmd calib_cfg_cmd;
+ struct iwl_calib_cfg_cmd calib_cfg_cmd;
struct iwl_host_cmd cmd = {
.id = CALIBRATION_CFG_CMD,
- .len = sizeof(struct iwl5000_calib_cfg_cmd),
+ .len = sizeof(struct iwl_calib_cfg_cmd),
.data = &calib_cfg_cmd,
};
@@ -467,7 +480,7 @@ static void iwl5000_rx_calib_result(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
- struct iwl5000_calib_hdr *hdr = (struct iwl5000_calib_hdr *)pkt->u.raw;
+ struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw;
int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK;
int index;
@@ -478,14 +491,20 @@ static void iwl5000_rx_calib_result(struct iwl_priv *priv,
* uCode. iwl_send_calib_results sends them in a row according to their
* index. We sort them here */
switch (hdr->op_code) {
- case IWL5000_PHY_CALIBRATE_LO_CMD:
- index = IWL5000_CALIB_LO;
+ case IWL_PHY_CALIBRATE_DC_CMD:
+ index = IWL_CALIB_DC;
+ break;
+ case IWL_PHY_CALIBRATE_LO_CMD:
+ index = IWL_CALIB_LO;
break;
- case IWL5000_PHY_CALIBRATE_TX_IQ_CMD:
- index = IWL5000_CALIB_TX_IQ;
+ case IWL_PHY_CALIBRATE_TX_IQ_CMD:
+ index = IWL_CALIB_TX_IQ;
break;
- case IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD:
- index = IWL5000_CALIB_TX_IQ_PERD;
+ case IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD:
+ index = IWL_CALIB_TX_IQ_PERD;
+ break;
+ case IWL_PHY_CALIBRATE_BASE_BAND_CMD:
+ index = IWL_CALIB_BASE_BAND;
break;
default:
IWL_ERROR("Unknown calibration notification %d\n",
@@ -535,7 +554,7 @@ static int iwl5000_load_section(struct iwl_priv *priv,
iwl_write_direct32(priv,
FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL),
- (iwl_get_dma_hi_address(phy_addr)
+ (iwl_get_dma_hi_addr(phy_addr)
<< FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt);
iwl_write_direct32(priv,
@@ -547,7 +566,7 @@ static int iwl5000_load_section(struct iwl_priv *priv,
iwl_write_direct32(priv,
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
iwl_release_nic_access(priv);
@@ -561,14 +580,13 @@ static int iwl5000_load_given_ucode(struct iwl_priv *priv,
{
int ret = 0;
- ret = iwl5000_load_section(
- priv, inst_image, RTC_INST_LOWER_BOUND);
+ ret = iwl5000_load_section(priv, inst_image, RTC_INST_LOWER_BOUND);
if (ret)
return ret;
IWL_DEBUG_INFO("INST uCode section being loaded...\n");
ret = wait_event_interruptible_timeout(priv->wait_command_queue,
- priv->ucode_write_complete, 5 * HZ);
+ priv->ucode_write_complete, 5 * HZ);
if (ret == -ERESTARTSYS) {
IWL_ERROR("Could not load the INST uCode section due "
"to interrupt\n");
@@ -682,7 +700,7 @@ static void iwl5000_tx_queue_set_status(struct iwl_priv *priv,
int tx_fifo_id, int scd_retry)
{
int txq_id = txq->q.id;
- int active = test_bit(txq_id, &priv->txq_ctx_active_msk)?1:0;
+ int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
iwl_write_prph(priv, IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
(active << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
@@ -710,9 +728,10 @@ static int iwl5000_send_wimax_coex(struct iwl_priv *priv)
static int iwl5000_alive_notify(struct iwl_priv *priv)
{
u32 a;
- int i = 0;
unsigned long flags;
int ret;
+ int i, chan;
+ u32 reg_val;
spin_lock_irqsave(&priv->lock, flags);
@@ -734,11 +753,21 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
iwl_write_targ_mem(priv, a, 0);
iwl_write_prph(priv, IWL50_SCD_DRAM_BASE_ADDR,
- (priv->shared_phys +
- offsetof(struct iwl5000_shared, queues_byte_cnt_tbls)) >> 10);
+ priv->scd_bc_tbls.dma >> 10);
+
+ /* Enable DMA channel */
+ for (chan = 0; chan < FH50_TCSR_CHNL_NUM ; chan++)
+ iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(chan),
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
+ FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE);
+
+ /* Update FH chicken bits */
+ reg_val = iwl_read_direct32(priv, FH_TX_CHICKEN_BITS_REG);
+ iwl_write_direct32(priv, FH_TX_CHICKEN_BITS_REG,
+ reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN);
+
iwl_write_prph(priv, IWL50_SCD_QUEUECHAIN_SEL,
- IWL50_SCD_QUEUECHAIN_SEL_ALL(
- priv->hw_params.max_txq_num));
+ IWL50_SCD_QUEUECHAIN_SEL_ALL(priv->hw_params.max_txq_num));
iwl_write_prph(priv, IWL50_SCD_AGGR_SEL, 0);
/* initiate the queues */
@@ -765,6 +794,7 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 7));
iwl5000_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0);
+
/* map qos queues to fifos one-to-one */
for (i = 0; i < ARRAY_SIZE(iwl5000_default_queue_to_tx_fifo); i++) {
int ac = iwl5000_default_queue_to_tx_fifo[i];
@@ -784,10 +814,8 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
iwl5000_send_wimax_coex(priv);
- iwl5000_send_Xtal_calib(priv);
-
- if (priv->ucode_type == UCODE_RT)
- iwl_send_calib_results(priv);
+ iwl5000_set_Xtal_calib(priv);
+ iwl_send_calib_results(priv);
return 0;
}
@@ -802,7 +830,9 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
}
priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues;
- priv->hw_params.first_ampdu_q = IWL50_FIRST_AMPDU_QUEUE;
+ priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
+ priv->hw_params.scd_bc_tbls_size =
+ IWL50_NUM_QUEUES * sizeof(struct iwl5000_scd_bc_tbl);
priv->hw_params.max_stations = IWL5000_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID;
priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE;
@@ -814,10 +844,14 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
case CSR_HW_REV_TYPE_5100:
+ priv->hw_params.tx_chains_num = 1;
+ priv->hw_params.rx_chains_num = 2;
+ priv->hw_params.valid_tx_ant = ANT_B;
+ priv->hw_params.valid_rx_ant = ANT_AB;
+ break;
case CSR_HW_REV_TYPE_5150:
priv->hw_params.tx_chains_num = 1;
priv->hw_params.rx_chains_num = 2;
- /* FIXME: move to ANT_A, ANT_B, ANT_C enum */
priv->hw_params.valid_tx_ant = ANT_A;
priv->hw_params.valid_rx_ant = ANT_AB;
break;
@@ -840,43 +874,36 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
case CSR_HW_REV_TYPE_5150:
/* 5150 wants in Kelvin */
priv->hw_params.ct_kill_threshold =
- CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD);
+ iwl5150_get_ct_threshold(priv);
break;
}
- return 0;
-}
-
-static int iwl5000_alloc_shared_mem(struct iwl_priv *priv)
-{
- priv->shared_virt = pci_alloc_consistent(priv->pci_dev,
- sizeof(struct iwl5000_shared),
- &priv->shared_phys);
- if (!priv->shared_virt)
- return -ENOMEM;
+ /* Set initial calibration set */
+ switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
+ case CSR_HW_REV_TYPE_5100:
+ case CSR_HW_REV_TYPE_5300:
+ case CSR_HW_REV_TYPE_5350:
+ priv->hw_params.calib_init_cfg =
+ BIT(IWL_CALIB_XTAL) |
+ BIT(IWL_CALIB_LO) |
+ BIT(IWL_CALIB_TX_IQ) |
+ BIT(IWL_CALIB_TX_IQ_PERD) |
+ BIT(IWL_CALIB_BASE_BAND);
+ break;
+ case CSR_HW_REV_TYPE_5150:
+ priv->hw_params.calib_init_cfg =
+ BIT(IWL_CALIB_DC) |
+ BIT(IWL_CALIB_LO) |
+ BIT(IWL_CALIB_TX_IQ) |
+ BIT(IWL_CALIB_BASE_BAND);
- memset(priv->shared_virt, 0, sizeof(struct iwl5000_shared));
+ break;
+ }
- priv->rb_closed_offset = offsetof(struct iwl5000_shared, rb_closed);
return 0;
}
-static void iwl5000_free_shared_mem(struct iwl_priv *priv)
-{
- if (priv->shared_virt)
- pci_free_consistent(priv->pci_dev,
- sizeof(struct iwl5000_shared),
- priv->shared_virt,
- priv->shared_phys);
-}
-
-static int iwl5000_shared_mem_rx_idx(struct iwl_priv *priv)
-{
- struct iwl5000_shared *s = priv->shared_virt;
- return le32_to_cpu(s->rb_closed) & 0xFFF;
-}
-
/**
* iwl5000_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
*/
@@ -884,16 +911,18 @@ static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
struct iwl_tx_queue *txq,
u16 byte_cnt)
{
- struct iwl5000_shared *shared_data = priv->shared_virt;
+ struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
+ int write_ptr = txq->q.write_ptr;
int txq_id = txq->q.id;
u8 sec_ctl = 0;
- u8 sta = 0;
- int len;
+ u8 sta_id = 0;
+ u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ __le16 bc_ent;
- len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
+ WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
if (txq_id != IWL_CMD_QUEUE_NUM) {
- sta = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
+ sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
switch (sec_ctl & TX_CMD_SEC_MSK) {
@@ -909,40 +938,35 @@ static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
}
}
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[txq->q.write_ptr], byte_cnt, len);
+ bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[txq->q.write_ptr], sta_id, sta);
+ scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
- if (txq->q.write_ptr < IWL50_MAX_WIN_SIZE) {
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[IWL50_QUEUE_SIZE + txq->q.write_ptr],
- byte_cnt, len);
- IWL_SET_BITS16(shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[IWL50_QUEUE_SIZE + txq->q.write_ptr],
- sta_id, sta);
- }
+ if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
}
static void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
struct iwl_tx_queue *txq)
{
+ struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
int txq_id = txq->q.id;
- struct iwl5000_shared *shared_data = priv->shared_virt;
- u8 sta = 0;
+ int read_ptr = txq->q.read_ptr;
+ u8 sta_id = 0;
+ __le16 bc_ent;
+
+ WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
if (txq_id != IWL_CMD_QUEUE_NUM)
- sta = txq->cmd[txq->q.read_ptr]->cmd.tx.sta_id;
+ sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
- shared_data->queues_byte_cnt_tbls[txq_id].tfd_offset[txq->q.read_ptr].
- val = cpu_to_le16(1 | (sta << 12));
+ bc_ent = cpu_to_le16(1 | (sta_id << 12));
+ scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
- if (txq->q.write_ptr < IWL50_MAX_WIN_SIZE) {
- shared_data->queues_byte_cnt_tbls[txq_id].
- tfd_offset[IWL50_QUEUE_SIZE + txq->q.read_ptr].
- val = cpu_to_le16(1 | (sta << 12));
- }
+ if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP)
+ scd_bc_tbl[txq_id].
+ tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
}
static int iwl5000_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
@@ -996,7 +1020,7 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
ra_tid = BUILD_RAxTID(sta_id, tid);
/* Modify device's station table to Tx this TID */
- iwl_sta_modify_enable_tid_tx(priv, sta_id, tid);
+ iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
spin_lock_irqsave(&priv->lock, flags);
ret = iwl_grab_nic_access(priv);
@@ -1089,7 +1113,7 @@ static u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data)
/*
- * Activate/Deactivat Tx DMA/FIFO channels according tx fifos mask
+ * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
* must be called under priv->lock and mac access
*/
static void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask)
@@ -1136,10 +1160,10 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv,
agg->frame_count, agg->start_idx, idx);
info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]);
- info->status.retry_count = tx_resp->failure_frame;
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= iwl_is_tx_success(status)?
- IEEE80211_TX_STAT_ACK : 0;
+ info->flags |= iwl_is_tx_success(status) ?
+ IEEE80211_TX_STAT_ACK : 0;
iwl_hwrate_to_tx_control(priv, rate_n_flags, info);
/* FIXME: code repetition end */
@@ -1225,9 +1249,9 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
struct ieee80211_tx_info *info;
struct iwl5000_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
u32 status = le16_to_cpu(tx_resp->status.status);
- int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION;
- struct ieee80211_hdr *hdr;
- u8 *qc = NULL;
+ int tid;
+ int sta_id;
+ int freed;
if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) {
IWL_ERROR("Read index for DMA queue txq_id (%d) index %d "
@@ -1240,25 +1264,13 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb[0]);
memset(&info->status, 0, sizeof(info->status));
- hdr = iwl_tx_queue_get_hdr(priv, txq_id, index);
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- }
-
- sta_id = iwl_get_ra_sta_id(priv, hdr);
- if (txq->sched_retry && unlikely(sta_id == IWL_INVALID_STATION)) {
- IWL_ERROR("Station not known\n");
- return;
- }
+ tid = (tx_resp->ra_tid & IWL50_TX_RES_TID_MSK) >> IWL50_TX_RES_TID_POS;
+ sta_id = (tx_resp->ra_tid & IWL50_TX_RES_RA_MSK) >> IWL50_TX_RES_RA_POS;
if (txq->sched_retry) {
const u32 scd_ssn = iwl5000_get_scd_ssn(tx_resp);
struct iwl_ht_agg *agg = NULL;
- if (!qc)
- return;
-
agg = &priv->stations[sta_id].tid[tid].agg;
iwl5000_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index);
@@ -1268,58 +1280,58 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
if (txq->q.read_ptr != (scd_ssn & 0xff)) {
- int freed, ampdu_q;
index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd);
- IWL_DEBUG_TX_REPLY("Retry scheduler reclaim scd_ssn "
- "%d index %d\n", scd_ssn , index);
+ IWL_DEBUG_TX_REPLY("Retry scheduler reclaim "
+ "scd_ssn=%d idx=%d txq=%d swq=%d\n",
+ scd_ssn , index, txq_id, txq->swq_id);
+
freed = iwl_tx_queue_reclaim(priv, txq_id, index);
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
- txq_id >= 0 && priv->mac80211_registered &&
- agg->state != IWL_EMPTYING_HW_QUEUE_DELBA) {
- /* calculate mac80211 ampdu sw queue to wake */
- ampdu_q = txq_id - IWL50_FIRST_AMPDU_QUEUE +
- priv->hw->queues;
+ if (priv->mac80211_registered &&
+ (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
+ (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) {
if (agg->state == IWL_AGG_OFF)
ieee80211_wake_queue(priv->hw, txq_id);
else
- ieee80211_wake_queue(priv->hw, ampdu_q);
+ ieee80211_wake_queue(priv->hw,
+ txq->swq_id);
}
- iwl_txq_check_empty(priv, sta_id, tid, txq_id);
}
} else {
- info->status.retry_count = tx_resp->failure_frame;
- info->flags =
- iwl_is_tx_success(status) ? IEEE80211_TX_STAT_ACK : 0;
+ BUG_ON(txq_id != txq->swq_id);
+
+ info->status.rates[0].count = tx_resp->failure_frame + 1;
+ info->flags |= iwl_is_tx_success(status) ?
+ IEEE80211_TX_STAT_ACK : 0;
iwl_hwrate_to_tx_control(priv,
le32_to_cpu(tx_resp->rate_n_flags),
info);
- IWL_DEBUG_TX("Tx queue %d Status %s (0x%08x) rate_n_flags "
- "0x%x retries %d\n", txq_id,
- iwl_get_tx_fail_reason(status),
- status, le32_to_cpu(tx_resp->rate_n_flags),
- tx_resp->failure_frame);
+ IWL_DEBUG_TX_REPLY("TXQ %d status %s (0x%08x) rate_n_flags "
+ "0x%x retries %d\n",
+ txq_id,
+ iwl_get_tx_fail_reason(status), status,
+ le32_to_cpu(tx_resp->rate_n_flags),
+ tx_resp->failure_frame);
- IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
- if (index != -1) {
- int freed = iwl_tx_queue_reclaim(priv, txq_id, index);
- if (tid != MAX_TID_COUNT)
+ freed = iwl_tx_queue_reclaim(priv, txq_id, index);
+ if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
- if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
- (txq_id >= 0) && priv->mac80211_registered)
+
+ if (priv->mac80211_registered &&
+ (iwl_queue_space(&txq->q) > txq->q.low_mark))
ieee80211_wake_queue(priv->hw, txq_id);
- if (tid != MAX_TID_COUNT)
- iwl_txq_check_empty(priv, sta_id, tid, txq_id);
- }
}
+ if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
+ iwl_txq_check_empty(priv, sta_id, tid, txq_id);
+
if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n");
}
-/* Currently 5000 is the supperset of everything */
+/* Currently 5000 is the superset of everything */
static u16 iwl5000_get_hcmd_size(u8 cmd_id, u16 len)
{
return len;
@@ -1466,9 +1478,6 @@ static struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = {
static struct iwl_lib_ops iwl5000_lib = {
.set_hw_params = iwl5000_hw_set_hw_params,
- .alloc_shared_mem = iwl5000_alloc_shared_mem,
- .free_shared_mem = iwl5000_free_shared_mem,
- .shared_mem_rx_idx = iwl5000_shared_mem_rx_idx,
.txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl,
.txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl,
.txq_set_sched = iwl5000_txq_set_sched,
@@ -1482,13 +1491,13 @@ static struct iwl_lib_ops iwl5000_lib = {
.alive_notify = iwl5000_alive_notify,
.send_tx_power = iwl5000_send_tx_power,
.temperature = iwl5000_temperature,
- .update_chain_flags = iwl4965_update_chain_flags,
+ .update_chain_flags = iwl_update_chain_flags,
.apm_ops = {
.init = iwl5000_apm_init,
.reset = iwl5000_apm_reset,
.stop = iwl5000_apm_stop,
.config = iwl5000_nic_config,
- .set_pwr_src = iwl4965_set_pwr_src,
+ .set_pwr_src = iwl_set_pwr_src,
},
.eeprom_ops = {
.regulatory_bands = {
@@ -1503,7 +1512,7 @@ static struct iwl_lib_ops iwl5000_lib = {
.verify_signature = iwlcore_eeprom_verify_signature,
.acquire_semaphore = iwlcore_eeprom_acquire_semaphore,
.release_semaphore = iwlcore_eeprom_release_semaphore,
- .check_version = iwl5000_eeprom_check_version,
+ .calib_version = iwl5000_eeprom_calib_version,
.query_addr = iwl5000_eeprom_query_addr,
},
};
@@ -1517,7 +1526,6 @@ static struct iwl_ops iwl5000_ops = {
static struct iwl_mod_params iwl50_mod_params = {
.num_of_queues = IWL50_NUM_QUEUES,
.num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
- .enable_qos = 1,
.amsdu_size_8K = 1,
.restart_fw = 1,
/* the rest are 0 by default */
@@ -1526,50 +1534,84 @@ static struct iwl_mod_params iwl50_mod_params = {
struct iwl_cfg iwl5300_agn_cfg = {
.name = "5300AGN",
- .fw_name = "iwlwifi-5000" IWL5000_UCODE_API ".ucode",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
};
struct iwl_cfg iwl5100_bg_cfg = {
.name = "5100BG",
- .fw_name = "iwlwifi-5000" IWL5000_UCODE_API ".ucode",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_G,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
};
struct iwl_cfg iwl5100_abg_cfg = {
.name = "5100ABG",
- .fw_name = "iwlwifi-5000" IWL5000_UCODE_API ".ucode",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
};
struct iwl_cfg iwl5100_agn_cfg = {
.name = "5100AGN",
- .fw_name = "iwlwifi-5000" IWL5000_UCODE_API ".ucode",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
};
struct iwl_cfg iwl5350_agn_cfg = {
.name = "5350AGN",
- .fw_name = "iwlwifi-5000" IWL5000_UCODE_API ".ucode",
+ .fw_name_pre = IWL5000_FW_PRE,
+ .ucode_api_max = IWL5000_UCODE_API_MAX,
+ .ucode_api_min = IWL5000_UCODE_API_MIN,
+ .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
+ .ops = &iwl5000_ops,
+ .eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5050_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
+ .mod_params = &iwl50_mod_params,
+};
+
+struct iwl_cfg iwl5150_agn_cfg = {
+ .name = "5150AGN",
+ .fw_name_pre = IWL5150_FW_PRE,
+ .ucode_api_max = IWL5150_UCODE_API_MAX,
+ .ucode_api_min = IWL5150_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5050_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
.mod_params = &iwl50_mod_params,
};
-MODULE_FIRMWARE("iwlwifi-5000" IWL5000_UCODE_API ".ucode");
+MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
+MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX));
module_param_named(disable50, iwl50_mod_params.disable, int, 0444);
MODULE_PARM_DESC(disable50,
@@ -1577,12 +1619,10 @@ MODULE_PARM_DESC(disable50,
module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444);
MODULE_PARM_DESC(swcrypto50,
"using software crypto engine (default 0 [hardware])\n");
-module_param_named(debug50, iwl50_mod_params.debug, int, 0444);
+module_param_named(debug50, iwl50_mod_params.debug, uint, 0444);
MODULE_PARM_DESC(debug50, "50XX debug output mask");
module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444);
MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series");
-module_param_named(qos_enable50, iwl50_mod_params.enable_qos, int, 0444);
-MODULE_PARM_DESC(qos_enable50, "enable all 50XX QoS functionality");
module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, 0444);
MODULE_PARM_DESC(11n_disable50, "disable 50XX 11n functionality");
module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K, int, 0444);
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hcmd-check.c b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd-check.c
new file mode 100644
index 000000000000..b8137eeae1db
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hcmd-check.c
@@ -0,0 +1,108 @@
+/******************************************************************************
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+#include "iwl-dev.h"
+#include "iwl-debug.h"
+#include "iwl-commands.h"
+
+
+/**
+ * iwl_check_rxon_cmd - validate RXON structure is valid
+ *
+ * NOTE: This is really only useful during development and can eventually
+ * be #ifdef'd out once the driver is stable and folks aren't actively
+ * making changes
+ */
+int iwl_agn_check_rxon_cmd(struct iwl_rxon_cmd *rxon)
+{
+ int error = 0;
+ int counter = 1;
+
+ if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
+ error |= le32_to_cpu(rxon->flags &
+ (RXON_FLG_TGJ_NARROW_BAND_MSK |
+ RXON_FLG_RADAR_DETECT_MSK));
+ if (error)
+ IWL_WARNING("check 24G fields %d | %d\n",
+ counter++, error);
+ } else {
+ error |= (rxon->flags & RXON_FLG_SHORT_SLOT_MSK) ?
+ 0 : le32_to_cpu(RXON_FLG_SHORT_SLOT_MSK);
+ if (error)
+ IWL_WARNING("check 52 fields %d | %d\n",
+ counter++, error);
+ error |= le32_to_cpu(rxon->flags & RXON_FLG_CCK_MSK);
+ if (error)
+ IWL_WARNING("check 52 CCK %d | %d\n",
+ counter++, error);
+ }
+ error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1;
+ if (error)
+ IWL_WARNING("check mac addr %d | %d\n", counter++, error);
+
+ /* make sure basic rates 6Mbps and 1Mbps are supported */
+ error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) &&
+ ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0));
+ if (error)
+ IWL_WARNING("check basic rate %d | %d\n", counter++, error);
+
+ error |= (le16_to_cpu(rxon->assoc_id) > 2007);
+ if (error)
+ IWL_WARNING("check assoc id %d | %d\n", counter++, error);
+
+ error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK));
+ if (error)
+ IWL_WARNING("check CCK and short slot %d | %d\n",
+ counter++, error);
+
+ error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
+ == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK));
+ if (error)
+ IWL_WARNING("check CCK & auto detect %d | %d\n",
+ counter++, error);
+
+ error |= ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
+ RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK);
+ if (error)
+ IWL_WARNING("check TGG and auto detect %d | %d\n",
+ counter++, error);
+
+ if (error)
+ IWL_WARNING("Tuning to channel %d\n",
+ le16_to_cpu(rxon->channel));
+
+ if (error) {
+ IWL_ERROR("Not a valid iwl4965_rxon_assoc_cmd field values\n");
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index e2a58e477036..f3f17929ca0b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -38,7 +38,6 @@
#include "iwl-dev.h"
#include "iwl-sta.h"
#include "iwl-core.h"
-#include "iwl-helpers.h"
#define RS_NAME "iwl-agn-rs"
@@ -188,7 +187,7 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
* 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
* "G" is the only table that supports CCK (the first 4 rates).
*/
-/*FIXME:RS:need to spearate tables for MIMO2/MIMO3*/
+/*FIXME:RS:need to separate tables for MIMO2/MIMO3*/
static s32 expected_tpt_A[IWL_RATE_COUNT] = {
0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186
};
@@ -281,10 +280,9 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
u32 time_diff;
s32 index;
struct iwl_traffic_load *tl = NULL;
- __le16 fc = hdr->frame_control;
u8 tid;
- if (ieee80211_is_data_qos(fc)) {
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
} else
@@ -357,11 +355,9 @@ static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
struct iwl_lq_sta *lq_data, u8 tid,
struct ieee80211_sta *sta)
{
- DECLARE_MAC_BUF(mac);
-
if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) {
- IWL_DEBUG_HT("Starting Tx agg: STA: %s tid: %d\n",
- print_mac(mac, sta->addr), tid);
+ IWL_DEBUG_HT("Starting Tx agg: STA: %pM tid: %d\n",
+ sta->addr, tid);
ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid);
}
}
@@ -775,7 +771,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
int status;
u8 retries;
int rs_index, index = 0;
- struct iwl_lq_sta *lq_sta;
+ struct iwl_lq_sta *lq_sta = priv_sta;
struct iwl_link_quality_cmd *table;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
@@ -787,12 +783,12 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
struct iwl_scale_tbl_info tbl_type;
struct iwl_scale_tbl_info *curr_tbl, *search_tbl;
u8 active_index = 0;
- __le16 fc = hdr->frame_control;
s32 tpt = 0;
IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n");
- if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1))
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
return;
/* This packet was aggregated but doesn't carry rate scale info */
@@ -800,13 +796,11 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
!(info->flags & IEEE80211_TX_STAT_AMPDU))
return;
- retries = info->status.retry_count;
+ retries = info->status.rates[0].count - 1;
if (retries > 15)
retries = 15;
- lq_sta = (struct iwl_lq_sta *)priv_sta;
-
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
!lq_sta->ibss_sta_added)
goto out;
@@ -832,21 +826,20 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
if (priv->band == IEEE80211_BAND_5GHZ)
rs_index -= IWL_FIRST_OFDM_RATE;
- if ((info->tx_rate_idx < 0) ||
- (tbl_type.is_SGI ^
- !!(info->flags & IEEE80211_TX_CTL_SHORT_GI)) ||
- (tbl_type.is_fat ^
- !!(info->flags & IEEE80211_TX_CTL_40_MHZ_WIDTH)) ||
- (tbl_type.is_dup ^
- !!(info->flags & IEEE80211_TX_CTL_DUP_DATA)) ||
- (tbl_type.ant_type ^ info->antenna_sel_tx) ||
- (!!(tx_rate & RATE_MCS_HT_MSK) ^
- !!(info->flags & IEEE80211_TX_CTL_OFDM_HT)) ||
- (!!(tx_rate & RATE_MCS_GF_MSK) ^
- !!(info->flags & IEEE80211_TX_CTL_GREEN_FIELD)) ||
+ if ((info->status.rates[0].idx < 0) ||
+ (tbl_type.is_SGI != !!(info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)) ||
+ (tbl_type.is_fat != !!(info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+ (tbl_type.is_dup != !!(info->status.rates[0].flags & IEEE80211_TX_RC_DUP_DATA)) ||
+ (tbl_type.ant_type != info->antenna_sel_tx) ||
+ (!!(tx_rate & RATE_MCS_HT_MSK) != !!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) ||
+ (!!(tx_rate & RATE_MCS_GF_MSK) != !!(info->status.rates[0].flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
(hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate !=
- hw->wiphy->bands[info->band]->bitrates[info->tx_rate_idx].bitrate)) {
+ hw->wiphy->bands[info->band]->bitrates[info->status.rates[0].idx].bitrate)) {
IWL_DEBUG_RATE("initial rate does not match 0x%x\n", tx_rate);
+ /* the last LQ command could failed so the LQ in ucode not
+ * the same in driver sync up
+ */
+ iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
goto out;
}
@@ -1135,11 +1128,10 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv,
s32 rate;
s8 is_green = lq_sta->is_green;
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) ||
- !sta->ht_info.ht_supported)
+ if (!conf->ht.enabled || !sta->ht_cap.ht_supported)
return -1;
- if (((sta->ht_info.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+ if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
== WLAN_HT_CAP_SM_PS_STATIC)
return -1;
@@ -1203,8 +1195,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv,
u8 is_green = lq_sta->is_green;
s32 rate;
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) ||
- !sta->ht_info.ht_supported)
+ if (!conf->ht.enabled || !sta->ht_cap.ht_supported)
return -1;
IWL_DEBUG_RATE("LQ: try to switch to SISO\n");
@@ -1684,7 +1675,6 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
int high_tpt = IWL_INVALID_VALUE;
u32 fail_count;
s8 scale_action = 0;
- __le16 fc;
u16 rate_mask;
u8 update_lq = 0;
struct iwl_scale_tbl_info *tbl, *tbl1;
@@ -1699,13 +1689,12 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
IWL_DEBUG_RATE("rate scale calculate new rate for skb\n");
- fc = hdr->frame_control;
- if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) {
- /* Send management frames and broadcast/multicast data using
- * lowest rate. */
- /* TODO: this could probably be improved.. */
+ /* Send management frames and broadcast/multicast data using
+ * lowest rate. */
+ /* TODO: this could probably be improved.. */
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
return;
- }
if (!sta || !lq_sta)
return;
@@ -2003,9 +1992,8 @@ lq_update:
* stay with best antenna legacy modulation for a while
* before next round of mode comparisons. */
tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
- if (is_legacy(tbl1->lq_type) &&
- (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) &&
- (lq_sta->action_counter >= 1)) {
+ if (is_legacy(tbl1->lq_type) && !conf->ht.enabled &&
+ lq_sta->action_counter >= 1) {
lq_sta->action_counter = 0;
IWL_DEBUG_RATE("LQ: STAY in legacy table\n");
rs_set_stay_in_table(priv, 1, lq_sta);
@@ -2081,15 +2069,13 @@ static void rs_initialize_lq(struct iwl_priv *priv,
if ((i < 0) || (i >= IWL_RATE_COUNT))
i = 0;
- /* FIXME:RS: This is also wrong in 4965 */
rate = iwl_rates[i].plcp;
- rate |= RATE_MCS_ANT_B_MSK;
- rate &= ~RATE_MCS_ANT_A_MSK;
+ tbl->ant_type = first_antenna(valid_tx_ant);
+ rate |= tbl->ant_type << RATE_MCS_ANT_POS;
if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
rate |= RATE_MCS_CCK_MSK;
- tbl->ant_type = ANT_B;
rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx);
if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
rs_toggle_antenna(valid_tx_ant, &rate, tbl);
@@ -2103,40 +2089,38 @@ static void rs_initialize_lq(struct iwl_priv *priv,
return;
}
-static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
{
- int i;
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
struct ieee80211_conf *conf = &priv->hw->conf;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc;
- struct iwl_lq_sta *lq_sta;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ int rate_idx;
IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
/* Send management frames and broadcast/multicast data using lowest
* rate. */
- fc = hdr->frame_control;
- if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
- !sta || !priv_sta) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1) || !sta || !lq_sta) {
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
return;
}
- lq_sta = (struct iwl_lq_sta *)priv_sta;
- i = lq_sta->last_txrate_idx;
+ rate_idx = lq_sta->last_txrate_idx;
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
!lq_sta->ibss_sta_added) {
u8 sta_id = iwl_find_station(priv, hdr->addr1);
- DECLARE_MAC_BUF(mac);
if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_RATE("LQ: ADD station %s\n",
- print_mac(mac, hdr->addr1));
+ IWL_DEBUG_RATE("LQ: ADD station %pM\n",
+ hdr->addr1);
sta_id = iwl_add_station_flags(priv, hdr->addr1,
0, CMD_ASYNC, NULL);
}
@@ -2148,14 +2132,12 @@ static void rs_get_rate(void *priv_r, struct ieee80211_supported_band *sband,
}
}
- if ((i < 0) || (i > IWL_RATE_COUNT)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
- return;
- }
+ if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT)
+ rate_idx = rate_lowest_index(sband, sta);
+ else if (sband->band == IEEE80211_BAND_5GHZ)
+ rate_idx -= IWL_FIRST_OFDM_RATE;
- if (sband->band == IEEE80211_BAND_5GHZ)
- i -= IWL_FIRST_OFDM_RATE;
- sel->rate_idx = i;
+ info->control.rates[0].idx = rate_idx;
}
static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
@@ -2189,6 +2171,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
struct ieee80211_conf *conf = &priv->hw->conf;
struct iwl_lq_sta *lq_sta = priv_sta;
+ u16 mask_bit = 0;
lq_sta->flush_timer = 0;
lq_sta->supp_rates = sta->supp_rates[sband->band];
@@ -2205,15 +2188,12 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
lq_sta->ibss_sta_added = 0;
if (priv->iw_mode == NL80211_IFTYPE_AP) {
u8 sta_id = iwl_find_station(priv, sta->addr);
- DECLARE_MAC_BUF(mac);
/* for IBSS the call are from tasklet */
- IWL_DEBUG_RATE("LQ: ADD station %s\n",
- print_mac(mac, sta->addr));
+ IWL_DEBUG_RATE("LQ: ADD station %pM\n", sta->addr);
if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_RATE("LQ: ADD station %s\n",
- print_mac(mac, sta->addr));
+ IWL_DEBUG_RATE("LQ: ADD station %pM\n", sta->addr);
sta_id = iwl_add_station_flags(priv, sta->addr,
0, CMD_ASYNC, NULL);
}
@@ -2225,16 +2205,6 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
priv->assoc_station_added = 1;
}
- /* Find highest tx rate supported by hardware and destination station */
- lq_sta->last_txrate_idx = 3;
- for (i = 0; i < sband->n_bitrates; i++)
- if (sta->supp_rates[sband->band] & BIT(i))
- lq_sta->last_txrate_idx = i;
-
- /* For MODE_IEEE80211A, skip over cck rates in global rate table */
- if (sband->band == IEEE80211_BAND_5GHZ)
- lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
-
lq_sta->is_dup = 0;
lq_sta->is_green = rs_use_green(priv, conf);
lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000);
@@ -2244,19 +2214,19 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
* active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
* supp_rates[] does not; shift to convert format, force 9 MBits off.
*/
- lq_sta->active_siso_rate = conf->ht_conf.supp_mcs_set[0] << 1;
- lq_sta->active_siso_rate |= conf->ht_conf.supp_mcs_set[0] & 0x1;
+ lq_sta->active_siso_rate = sta->ht_cap.mcs.rx_mask[0] << 1;
+ lq_sta->active_siso_rate |= sta->ht_cap.mcs.rx_mask[0] & 0x1;
lq_sta->active_siso_rate &= ~((u16)0x2);
lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
/* Same here */
- lq_sta->active_mimo2_rate = conf->ht_conf.supp_mcs_set[1] << 1;
- lq_sta->active_mimo2_rate |= conf->ht_conf.supp_mcs_set[1] & 0x1;
+ lq_sta->active_mimo2_rate = sta->ht_cap.mcs.rx_mask[1] << 1;
+ lq_sta->active_mimo2_rate |= sta->ht_cap.mcs.rx_mask[1] & 0x1;
lq_sta->active_mimo2_rate &= ~((u16)0x2);
lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
- lq_sta->active_mimo3_rate = conf->ht_conf.supp_mcs_set[2] << 1;
- lq_sta->active_mimo3_rate |= conf->ht_conf.supp_mcs_set[2] & 0x1;
+ lq_sta->active_mimo3_rate = sta->ht_cap.mcs.rx_mask[2] << 1;
+ lq_sta->active_mimo3_rate |= sta->ht_cap.mcs.rx_mask[2] & 0x1;
lq_sta->active_mimo3_rate &= ~((u16)0x2);
lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
@@ -2265,7 +2235,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
lq_sta->active_mimo2_rate,
lq_sta->active_mimo3_rate);
- /* These values will be overriden later */
+ /* These values will be overridden later */
lq_sta->lq.general_params.single_stream_ant_msk = ANT_A;
lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB;
@@ -2273,6 +2243,17 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
lq_sta->drv = priv;
+ /* Find highest tx rate supported by hardware and destination station */
+ mask_bit = sta->supp_rates[sband->band] & lq_sta->active_legacy_rate;
+ lq_sta->last_txrate_idx = 3;
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (mask_bit & BIT(i))
+ lq_sta->last_txrate_idx = i;
+
+ /* For MODE_IEEE80211A, skip over cck rates in global rate table */
+ if (sband->band == IEEE80211_BAND_5GHZ)
+ lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+
rs_initialize_lq(priv, conf, sta, lq_sta);
}
@@ -2405,19 +2386,6 @@ static void rs_free(void *priv_rate)
return;
}
-static void rs_clear(void *priv_rate)
-{
-#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_priv *priv = (struct iwl_priv *) priv_rate;
-
- IWL_DEBUG_RATE("enter\n");
-
- /* TODO - add rate scale state reset */
-
- IWL_DEBUG_RATE("leave\n");
-#endif /* CONFIG_IWLWIFI_DEBUG */
-}
-
static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta,
void *priv_sta)
{
@@ -2552,7 +2520,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
for (i = 0; i < LQ_SIZE; i++) {
desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n"
"rate=0x%X\n",
- lq_sta->active_tbl == i?"*":"x",
+ lq_sta->active_tbl == i ? "*" : "x",
lq_sta->lq_info[i].lq_type,
lq_sta->lq_info[i].is_SGI,
lq_sta->lq_info[i].is_fat,
@@ -2605,7 +2573,6 @@ static struct rate_control_ops rs_ops = {
.tx_status = rs_tx_status,
.get_rate = rs_get_rate,
.rate_init = rs_rate_init,
- .clear = rs_clear,
.alloc = rs_alloc,
.free = rs_free,
.alloc_sta = rs_alloc_sta,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
index d148d73635eb..78ee83adf742 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -229,7 +229,7 @@ enum {
#define IWL_MIMO2_SWITCH_SISO_C 4
#define IWL_MIMO2_SWITCH_GI 5
-/*FIXME:RS:add posible acctions for MIMO3*/
+/*FIXME:RS:add possible actions for MIMO3*/
#define IWL_ACTION_LIMIT 3 /* # possible actions */
@@ -284,7 +284,17 @@ static inline u8 num_of_ant(u8 mask)
!!((mask) & ANT_C);
}
-static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index)
+static inline u8 first_antenna(u8 mask)
+{
+ if (mask & ANT_A)
+ return ANT_A;
+ if (mask & ANT_B)
+ return ANT_B;
+ return ANT_C;
+}
+
+
+static inline u8 iwl_get_prev_ieee_rate(u8 rate_index)
{
u8 rate = iwl_rates[rate_index].prev_ieee;
@@ -294,11 +304,11 @@ static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index)
}
/**
- * iwl4965_rate_control_register - Register the rate control algorithm callbacks
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
*
* Since the rate control algorithm is hardware specific, there is no need
* or reason to place it as a stand alone module. The driver can call
- * iwl4965_rate_control_register in order to register the rate control callbacks
+ * iwl_rate_control_register in order to register the rate control callbacks
* with the mac80211 subsystem. This should be performed prior to calling
* ieee80211_register_hw
*
@@ -306,7 +316,7 @@ static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index)
extern int iwlagn_rate_control_register(void);
/**
- * iwl4965_rate_control_unregister - Unregister the rate control callbacks
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
*
* This should be called after calling ieee80211_unregister_hw, but before
* the driver is unloaded.
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index c4c0371c763b..5da6b35cd26d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -83,7 +83,7 @@
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_ALIAS("iwl4965");
@@ -96,7 +96,7 @@ MODULE_ALIAS("iwl4965");
-static void iwl4965_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
+static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
{
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
@@ -108,79 +108,6 @@ static void iwl4965_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt)
}
/**
- * iwl4965_check_rxon_cmd - validate RXON structure is valid
- *
- * NOTE: This is really only useful during development and can eventually
- * be #ifdef'd out once the driver is stable and folks aren't actively
- * making changes
- */
-static int iwl4965_check_rxon_cmd(struct iwl_rxon_cmd *rxon)
-{
- int error = 0;
- int counter = 1;
-
- if (rxon->flags & RXON_FLG_BAND_24G_MSK) {
- error |= le32_to_cpu(rxon->flags &
- (RXON_FLG_TGJ_NARROW_BAND_MSK |
- RXON_FLG_RADAR_DETECT_MSK));
- if (error)
- IWL_WARNING("check 24G fields %d | %d\n",
- counter++, error);
- } else {
- error |= (rxon->flags & RXON_FLG_SHORT_SLOT_MSK) ?
- 0 : le32_to_cpu(RXON_FLG_SHORT_SLOT_MSK);
- if (error)
- IWL_WARNING("check 52 fields %d | %d\n",
- counter++, error);
- error |= le32_to_cpu(rxon->flags & RXON_FLG_CCK_MSK);
- if (error)
- IWL_WARNING("check 52 CCK %d | %d\n",
- counter++, error);
- }
- error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1;
- if (error)
- IWL_WARNING("check mac addr %d | %d\n", counter++, error);
-
- /* make sure basic rates 6Mbps and 1Mbps are supported */
- error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) &&
- ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0));
- if (error)
- IWL_WARNING("check basic rate %d | %d\n", counter++, error);
-
- error |= (le16_to_cpu(rxon->assoc_id) > 2007);
- if (error)
- IWL_WARNING("check assoc id %d | %d\n", counter++, error);
-
- error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK));
- if (error)
- IWL_WARNING("check CCK and short slot %d | %d\n",
- counter++, error);
-
- error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK))
- == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK));
- if (error)
- IWL_WARNING("check CCK & auto detect %d | %d\n",
- counter++, error);
-
- error |= ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK |
- RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK);
- if (error)
- IWL_WARNING("check TGG and auto detect %d | %d\n",
- counter++, error);
-
- if (error)
- IWL_WARNING("Tuning to channel %d\n",
- le16_to_cpu(rxon->channel));
-
- if (error) {
- IWL_ERROR("Not a valid iwl4965_rxon_assoc_cmd field values\n");
- return -1;
- }
- return 0;
-}
-
-/**
* iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed
* @priv: staging_rxon is compared to active_rxon
*
@@ -228,18 +155,17 @@ static int iwl_full_rxon_required(struct iwl_priv *priv)
}
/**
- * iwl4965_commit_rxon - commit staging_rxon to hardware
+ * iwl_commit_rxon - commit staging_rxon to hardware
*
* The RXON command in staging_rxon is committed to the hardware and
* the active_rxon structure is updated with the new data. This
* function correctly transitions out of the RXON_ASSOC_MSK state if
* a HW tune is required based on the RXON structure changes.
*/
-static int iwl4965_commit_rxon(struct iwl_priv *priv)
+static int iwl_commit_rxon(struct iwl_priv *priv)
{
/* cast away the const for active_rxon in this function */
struct iwl_rxon_cmd *active_rxon = (void *)&priv->active_rxon;
- DECLARE_MAC_BUF(mac);
int ret;
bool new_assoc =
!!(priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK);
@@ -253,14 +179,14 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
* 5000, but will not damage 4965 */
priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN;
- ret = iwl4965_check_rxon_cmd(&priv->staging_rxon);
+ ret = iwl_agn_check_rxon_cmd(&priv->staging_rxon);
if (ret) {
IWL_ERROR("Invalid RXON configuration. Not committing.\n");
return -EINVAL;
}
/* If we don't need to send a full RXON, we can use
- * iwl4965_rxon_assoc_cmd which is used to reconfigure filter
+ * iwl_rxon_assoc_cmd which is used to reconfigure filter
* and other flags for the current radio configuration. */
if (!iwl_full_rxon_required(priv)) {
ret = iwl_send_rxon_assoc(priv);
@@ -300,12 +226,12 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
IWL_DEBUG_INFO("Sending RXON\n"
"* with%s RXON_FILTER_ASSOC_MSK\n"
"* channel = %d\n"
- "* bssid = %s\n",
+ "* bssid = %pM\n",
(new_assoc ? "" : "out"),
le16_to_cpu(priv->staging_rxon.channel),
- print_mac(mac, priv->staging_rxon.bssid_addr));
+ priv->staging_rxon.bssid_addr);
- iwl4965_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto);
+ iwl_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto);
/* Apply the new configuration
* RXON unassoc clears the station table in uCode, send it before
@@ -375,16 +301,16 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
return 0;
}
-void iwl4965_update_chain_flags(struct iwl_priv *priv)
+void iwl_update_chain_flags(struct iwl_priv *priv)
{
iwl_set_rxon_chain(priv);
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
-static int iwl4965_send_bt_config(struct iwl_priv *priv)
+static int iwl_send_bt_config(struct iwl_priv *priv)
{
- struct iwl4965_bt_cmd bt_cmd = {
+ struct iwl_bt_cmd bt_cmd = {
.flags = 3,
.lead_time = 0xAA,
.max_kill = 1,
@@ -393,7 +319,7 @@ static int iwl4965_send_bt_config(struct iwl_priv *priv)
};
return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG,
- sizeof(struct iwl4965_bt_cmd), &bt_cmd);
+ sizeof(struct iwl_bt_cmd), &bt_cmd);
}
static void iwl_clear_free_frames(struct iwl_priv *priv)
@@ -445,7 +371,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
- const u8 *dest, int left)
+ int left)
{
if (!iwl_is_associated(priv) || !priv->ibss_beacon ||
((priv->iw_mode != NL80211_IFTYPE_ADHOC) &&
@@ -460,16 +386,16 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
return priv->ibss_beacon->len;
}
-static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv)
+static u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv)
{
int i;
int rate_mask;
/* Set rate mask*/
if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)
- rate_mask = priv->active_rate_basic & 0xF;
+ rate_mask = priv->active_rate_basic & IWL_CCK_RATES_MASK;
else
- rate_mask = priv->active_rate_basic & 0xFF0;
+ rate_mask = priv->active_rate_basic & IWL_OFDM_RATES_MASK;
/* Find lowest valid rate */
for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
@@ -485,7 +411,7 @@ static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv)
return IWL_RATE_6M_PLCP;
}
-static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
+static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
struct iwl_frame *frame, u8 rate)
{
struct iwl_tx_beacon_cmd *tx_beacon_cmd;
@@ -498,7 +424,6 @@ static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame,
- iwl_bcast_addr,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
BUG_ON(frame_size > MAX_MPDU_SIZE);
@@ -517,7 +442,7 @@ static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv,
return sizeof(*tx_beacon_cmd) + frame_size;
}
-static int iwl4965_send_beacon_cmd(struct iwl_priv *priv)
+static int iwl_send_beacon_cmd(struct iwl_priv *priv)
{
struct iwl_frame *frame;
unsigned int frame_size;
@@ -532,9 +457,9 @@ static int iwl4965_send_beacon_cmd(struct iwl_priv *priv)
return -ENOMEM;
}
- rate = iwl4965_rate_get_lowest_plcp(priv);
+ rate = iwl_rate_get_lowest_plcp(priv);
- frame_size = iwl4965_hw_get_beacon_cmd(priv, frame, rate);
+ frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate);
rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
&frame->u.cmd[0]);
@@ -550,20 +475,33 @@ static int iwl4965_send_beacon_cmd(struct iwl_priv *priv)
*
******************************************************************************/
-static void iwl4965_ht_conf(struct iwl_priv *priv,
+static void iwl_ht_conf(struct iwl_priv *priv,
struct ieee80211_bss_conf *bss_conf)
{
- struct ieee80211_ht_info *ht_conf = bss_conf->ht_conf;
- struct ieee80211_ht_bss_info *ht_bss_conf = bss_conf->ht_bss_conf;
+ struct ieee80211_sta_ht_cap *ht_conf;
struct iwl_ht_info *iwl_conf = &priv->current_ht_config;
+ struct ieee80211_sta *sta;
IWL_DEBUG_MAC80211("enter: \n");
- iwl_conf->is_ht = bss_conf->assoc_ht;
-
if (!iwl_conf->is_ht)
return;
+
+ /*
+ * It is totally wrong to base global information on something
+ * that is valid only when associated, alas, this driver works
+ * that way and I don't know how to fix it.
+ */
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->hw, priv->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+ ht_conf = &sta->ht_cap;
+
if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20)
iwl_conf->sgf |= HT_SHORT_GI_20MHZ;
if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40)
@@ -574,29 +512,36 @@ static void iwl4965_ht_conf(struct iwl_priv *priv,
!!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU);
iwl_conf->supported_chan_width =
- !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH);
- iwl_conf->extension_chan_offset =
- ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET;
+ !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+ /*
+ * XXX: The HT configuration needs to be moved into iwl_mac_config()
+ * to be done there correctly.
+ */
+
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ if (priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40MINUS)
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ else if(priv->hw->conf.ht.channel_type == NL80211_CHAN_HT40PLUS)
+ iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+
/* If no above or below channel supplied disable FAT channel */
- if (iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_ABOVE &&
- iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_BELOW) {
- iwl_conf->extension_chan_offset = IEEE80211_HT_IE_CHA_SEC_NONE;
+ if (iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_ABOVE &&
+ iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW)
iwl_conf->supported_chan_width = 0;
- }
iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
- memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16);
+ memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16);
- iwl_conf->control_channel = ht_bss_conf->primary_channel;
- iwl_conf->tx_chan_width =
- !!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH);
+ iwl_conf->tx_chan_width = iwl_conf->supported_chan_width != 0;
iwl_conf->ht_protection =
- ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_HT_PROTECTION;
+ bss_conf->ht.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
iwl_conf->non_GF_STA_present =
- !!(ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_NON_GF_STA_PRSNT);
+ !!(bss_conf->ht.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+ rcu_read_unlock();
- IWL_DEBUG_MAC80211("control channel %d\n", iwl_conf->control_channel);
IWL_DEBUG_MAC80211("leave\n");
}
@@ -608,9 +553,6 @@ static void iwl_activate_qos(struct iwl_priv *priv, u8 force)
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
- if (!priv->qos_data.qos_enable)
- return;
-
priv->qos_data.def_qos_parm.qos_flags = 0;
if (priv->qos_data.qos_cap.q_AP.queue_request &&
@@ -637,23 +579,22 @@ static void iwl_activate_qos(struct iwl_priv *priv, u8 force)
#define MAX_UCODE_BEACON_INTERVAL 4096
-static __le16 iwl4965_adjust_beacon_interval(u16 beacon_val)
+static u16 iwl_adjust_beacon_interval(u16 beacon_val)
{
u16 new_val = 0;
u16 beacon_factor = 0;
- beacon_factor =
- (beacon_val + MAX_UCODE_BEACON_INTERVAL)
- / MAX_UCODE_BEACON_INTERVAL;
+ beacon_factor = (beacon_val + MAX_UCODE_BEACON_INTERVAL)
+ / MAX_UCODE_BEACON_INTERVAL;
new_val = beacon_val / beacon_factor;
- return cpu_to_le16(new_val);
+ return new_val;
}
-static void iwl4965_setup_rxon_timing(struct iwl_priv *priv)
+static void iwl_setup_rxon_timing(struct iwl_priv *priv)
{
- u64 interval_tm_unit;
- u64 tsf, result;
+ u64 tsf;
+ s32 interval_tm, rem;
unsigned long flags;
struct ieee80211_conf *conf = NULL;
u16 beacon_int = 0;
@@ -661,49 +602,32 @@ static void iwl4965_setup_rxon_timing(struct iwl_priv *priv)
conf = ieee80211_get_hw_conf(priv->hw);
spin_lock_irqsave(&priv->lock, flags);
- priv->rxon_timing.timestamp.dw[1] = cpu_to_le32(priv->timestamp >> 32);
- priv->rxon_timing.timestamp.dw[0] =
- cpu_to_le32(priv->timestamp & 0xFFFFFFFF);
-
+ priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp);
priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval);
- tsf = priv->timestamp;
-
- beacon_int = priv->beacon_int;
- spin_unlock_irqrestore(&priv->lock, flags);
-
if (priv->iw_mode == NL80211_IFTYPE_STATION) {
- if (beacon_int == 0) {
- priv->rxon_timing.beacon_interval = cpu_to_le16(100);
- priv->rxon_timing.beacon_init_val = cpu_to_le32(102400);
- } else {
- priv->rxon_timing.beacon_interval =
- cpu_to_le16(beacon_int);
- priv->rxon_timing.beacon_interval =
- iwl4965_adjust_beacon_interval(
- le16_to_cpu(priv->rxon_timing.beacon_interval));
- }
-
+ beacon_int = iwl_adjust_beacon_interval(priv->beacon_int);
priv->rxon_timing.atim_window = 0;
} else {
- priv->rxon_timing.beacon_interval =
- iwl4965_adjust_beacon_interval(conf->beacon_int);
+ beacon_int = iwl_adjust_beacon_interval(conf->beacon_int);
+
/* TODO: we need to get atim_window from upper stack
* for now we set to 0 */
priv->rxon_timing.atim_window = 0;
}
- interval_tm_unit =
- (le16_to_cpu(priv->rxon_timing.beacon_interval) * 1024);
- result = do_div(tsf, interval_tm_unit);
- priv->rxon_timing.beacon_init_val =
- cpu_to_le32((u32) ((u64) interval_tm_unit - result));
+ priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int);
- IWL_DEBUG_ASSOC
- ("beacon interval %d beacon timer %d beacon tim %d\n",
- le16_to_cpu(priv->rxon_timing.beacon_interval),
- le32_to_cpu(priv->rxon_timing.beacon_init_val),
- le16_to_cpu(priv->rxon_timing.atim_window));
+ tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */
+ interval_tm = beacon_int * 1024;
+ rem = do_div(tsf, interval_tm);
+ priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ IWL_DEBUG_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n",
+ le16_to_cpu(priv->rxon_timing.beacon_interval),
+ le32_to_cpu(priv->rxon_timing.beacon_init_val),
+ le16_to_cpu(priv->rxon_timing.atim_window));
}
static void iwl_set_flags_for_band(struct iwl_priv *priv,
@@ -715,7 +639,7 @@ static void iwl_set_flags_for_band(struct iwl_priv *priv,
| RXON_FLG_CCK_MSK);
priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
} else {
- /* Copied from iwl4965_post_associate() */
+ /* Copied from iwl_post_associate() */
if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK;
else
@@ -733,13 +657,13 @@ static void iwl_set_flags_for_band(struct iwl_priv *priv,
/*
* initialize rxon structure with default values from eeprom
*/
-static void iwl4965_connection_init_rx_config(struct iwl_priv *priv)
+static void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode)
{
const struct iwl_channel_info *ch_info;
memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon));
- switch (priv->iw_mode) {
+ switch (mode) {
case NL80211_IFTYPE_AP:
priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP;
break;
@@ -762,7 +686,7 @@ static void iwl4965_connection_init_rx_config(struct iwl_priv *priv)
RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
break;
default:
- IWL_ERROR("Unsupported interface type %d\n", priv->iw_mode);
+ IWL_ERROR("Unsupported interface type %d\n", mode);
break;
}
@@ -808,11 +732,9 @@ static void iwl4965_connection_init_rx_config(struct iwl_priv *priv)
iwl_set_rxon_chain(priv);
}
-static int iwl4965_set_mode(struct iwl_priv *priv, int mode)
+static int iwl_set_mode(struct iwl_priv *priv, int mode)
{
- priv->iw_mode = mode;
-
- iwl4965_connection_init_rx_config(priv);
+ iwl_connection_init_rx_config(priv, mode);
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
iwl_clear_stations_table(priv);
@@ -828,12 +750,12 @@ static int iwl4965_set_mode(struct iwl_priv *priv, int mode)
return -EAGAIN;
}
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
return 0;
}
-static void iwl4965_set_rate(struct iwl_priv *priv)
+static void iwl_set_rate(struct iwl_priv *priv)
{
const struct ieee80211_supported_band *hw = NULL;
struct ieee80211_rate *rate;
@@ -880,138 +802,6 @@ static void iwl4965_set_rate(struct iwl_priv *priv)
(IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF;
}
-#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
-
-#include "iwl-spectrum.h"
-
-#define BEACON_TIME_MASK_LOW 0x00FFFFFF
-#define BEACON_TIME_MASK_HIGH 0xFF000000
-#define TIME_UNIT 1024
-
-/*
- * extended beacon time format
- * time in usec will be changed into a 32-bit value in 8:24 format
- * the high 1 byte is the beacon counts
- * the lower 3 bytes is the time in usec within one beacon interval
- */
-
-static u32 iwl4965_usecs_to_beacons(u32 usec, u32 beacon_interval)
-{
- u32 quot;
- u32 rem;
- u32 interval = beacon_interval * 1024;
-
- if (!interval || !usec)
- return 0;
-
- quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24);
- rem = (usec % interval) & BEACON_TIME_MASK_LOW;
-
- return (quot << 24) + rem;
-}
-
-/* base is usually what we get from ucode with each received frame,
- * the same as HW timer counter counting down
- */
-
-static __le32 iwl4965_add_beacon_time(u32 base, u32 addon, u32 beacon_interval)
-{
- u32 base_low = base & BEACON_TIME_MASK_LOW;
- u32 addon_low = addon & BEACON_TIME_MASK_LOW;
- u32 interval = beacon_interval * TIME_UNIT;
- u32 res = (base & BEACON_TIME_MASK_HIGH) +
- (addon & BEACON_TIME_MASK_HIGH);
-
- if (base_low > addon_low)
- res += base_low - addon_low;
- else if (base_low < addon_low) {
- res += interval + base_low - addon_low;
- res += (1 << 24);
- } else
- res += (1 << 24);
-
- return cpu_to_le32(res);
-}
-
-static int iwl4965_get_measurement(struct iwl_priv *priv,
- struct ieee80211_measurement_params *params,
- u8 type)
-{
- struct iwl4965_spectrum_cmd spectrum;
- struct iwl_rx_packet *res;
- struct iwl_host_cmd cmd = {
- .id = REPLY_SPECTRUM_MEASUREMENT_CMD,
- .data = (void *)&spectrum,
- .meta.flags = CMD_WANT_SKB,
- };
- u32 add_time = le64_to_cpu(params->start_time);
- int rc;
- int spectrum_resp_status;
- int duration = le16_to_cpu(params->duration);
-
- if (iwl_is_associated(priv))
- add_time =
- iwl4965_usecs_to_beacons(
- le64_to_cpu(params->start_time) - priv->last_tsf,
- le16_to_cpu(priv->rxon_timing.beacon_interval));
-
- memset(&spectrum, 0, sizeof(spectrum));
-
- spectrum.channel_count = cpu_to_le16(1);
- spectrum.flags =
- RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK;
- spectrum.filter_flags = MEASUREMENT_FILTER_FLAG;
- cmd.len = sizeof(spectrum);
- spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len));
-
- if (iwl_is_associated(priv))
- spectrum.start_time =
- iwl4965_add_beacon_time(priv->last_beacon_time,
- add_time,
- le16_to_cpu(priv->rxon_timing.beacon_interval));
- else
- spectrum.start_time = 0;
-
- spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT);
- spectrum.channels[0].channel = params->channel;
- spectrum.channels[0].type = type;
- if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)
- spectrum.flags |= RXON_FLG_BAND_24G_MSK |
- RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK;
-
- rc = iwl_send_cmd_sync(priv, &cmd);
- if (rc)
- return rc;
-
- res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
- IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n");
- rc = -EIO;
- }
-
- spectrum_resp_status = le16_to_cpu(res->u.spectrum.status);
- switch (spectrum_resp_status) {
- case 0: /* Command will be handled */
- if (res->u.spectrum.id != 0xff) {
- IWL_DEBUG_INFO
- ("Replaced existing measurement: %d\n",
- res->u.spectrum.id);
- priv->measurement_status &= ~MEASUREMENT_READY;
- }
- priv->measurement_status |= MEASUREMENT_ACTIVE;
- rc = 0;
- break;
-
- case 1: /* Command will not be handled */
- rc = -EAGAIN;
- break;
- }
-
- dev_kfree_skb_any(cmd.meta.u.skb);
-
- return rc;
-}
-#endif
/******************************************************************************
*
@@ -1054,7 +844,7 @@ static void iwl_rx_reply_alive(struct iwl_priv *priv,
IWL_WARNING("uCode did not respond OK.\n");
}
-static void iwl4965_rx_reply_error(struct iwl_priv *priv,
+static void iwl_rx_reply_error(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
@@ -1070,47 +860,29 @@ static void iwl4965_rx_reply_error(struct iwl_priv *priv,
#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x
-static void iwl4965_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
+static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
- struct iwl4965_csa_notification *csa = &(pkt->u.csa_notif);
+ struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
IWL_DEBUG_11H("CSA notif: channel %d, status %d\n",
le16_to_cpu(csa->channel), le32_to_cpu(csa->status));
rxon->channel = csa->channel;
priv->staging_rxon.channel = csa->channel;
}
-static void iwl4965_rx_spectrum_measure_notif(struct iwl_priv *priv,
- struct iwl_rx_mem_buffer *rxb)
-{
-#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
- struct iwl4965_spectrum_notification *report = &(pkt->u.spectrum_notif);
-
- if (!report->state) {
- IWL_DEBUG(IWL_DL_11H,
- "Spectrum Measure Notification: Start\n");
- return;
- }
-
- memcpy(&priv->measure_report, report, sizeof(*report));
- priv->measurement_status |= MEASUREMENT_READY;
-#endif
-}
-
-static void iwl4965_rx_pm_sleep_notif(struct iwl_priv *priv,
+static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
- struct iwl4965_sleep_notification *sleep = &(pkt->u.sleep_notif);
+ struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif);
IWL_DEBUG_RX("sleep mode: %d, src: %d\n",
sleep->pm_sleep_mode, sleep->pm_wakeup_src);
#endif
}
-static void iwl4965_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
+static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
@@ -1120,7 +892,7 @@ static void iwl4965_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len));
}
-static void iwl4965_bg_beacon_update(struct work_struct *work)
+static void iwl_bg_beacon_update(struct work_struct *work)
{
struct iwl_priv *priv =
container_of(work, struct iwl_priv, beacon_update);
@@ -1142,11 +914,11 @@ static void iwl4965_bg_beacon_update(struct work_struct *work)
priv->ibss_beacon = beacon;
mutex_unlock(&priv->mutex);
- iwl4965_send_beacon_cmd(priv);
+ iwl_send_beacon_cmd(priv);
}
/**
- * iwl4965_bg_statistics_periodic - Timer callback to queue statistics
+ * iwl_bg_statistics_periodic - Timer callback to queue statistics
*
* This callback is provided in order to send a statistics request.
*
@@ -1155,22 +927,27 @@ static void iwl4965_bg_beacon_update(struct work_struct *work)
* was received. We need to ensure we receive the statistics in order
* to update the temperature used for calibrating the TXPOWER.
*/
-static void iwl4965_bg_statistics_periodic(unsigned long data)
+static void iwl_bg_statistics_periodic(unsigned long data)
{
struct iwl_priv *priv = (struct iwl_priv *)data;
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
+ /* dont send host command if rf-kill is on */
+ if (!iwl_is_ready_rf(priv))
+ return;
+
iwl_send_statistics_request(priv, CMD_ASYNC);
}
-static void iwl4965_rx_beacon_notif(struct iwl_priv *priv,
+static void iwl_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
- struct iwl4965_beacon_notif *beacon = &(pkt->u.beacon_status);
+ struct iwl4965_beacon_notif *beacon =
+ (struct iwl4965_beacon_notif *)pkt->u.raw;
u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
IWL_DEBUG_RX("beacon status %x retries %d iss %d "
@@ -1189,7 +966,7 @@ static void iwl4965_rx_beacon_notif(struct iwl_priv *priv,
/* Handle notification from uCode that card's power state is changing
* due to software, hardware, or critical temperature RFKILL */
-static void iwl4965_rx_card_state_notif(struct iwl_priv *priv,
+static void iwl_rx_card_state_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
@@ -1258,7 +1035,7 @@ static void iwl4965_rx_card_state_notif(struct iwl_priv *priv,
wake_up_interruptible(&priv->wait_command_queue);
}
-int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
+int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
{
int ret;
unsigned long flags;
@@ -1290,7 +1067,7 @@ err:
}
/**
- * iwl4965_setup_rx_handlers - Initialize Rx handler callbacks
+ * iwl_setup_rx_handlers - Initialize Rx handler callbacks
*
* Setup the RX handlers for each of the reply types sent from the uCode
* to the host.
@@ -1301,14 +1078,12 @@ err:
static void iwl_setup_rx_handlers(struct iwl_priv *priv)
{
priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive;
- priv->rx_handlers[REPLY_ERROR] = iwl4965_rx_reply_error;
- priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl4965_rx_csa;
- priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] =
- iwl4965_rx_spectrum_measure_notif;
- priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl4965_rx_pm_sleep_notif;
+ priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error;
+ priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa;
+ priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif;
priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] =
- iwl4965_rx_pm_debug_statistics_notif;
- priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif;
+ iwl_rx_pm_debug_statistics_notif;
+ priv->rx_handlers[BEACON_NOTIFICATION] = iwl_rx_beacon_notif;
/*
* The same handler is used for both the REPLY to a discrete
@@ -1318,10 +1093,11 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv)
priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics;
priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics;
+ iwl_setup_spectrum_handlers(priv);
iwl_setup_rx_scan_handlers(priv);
/* status change handler */
- priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl4965_rx_card_state_notif;
+ priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl_rx_card_state_notif;
priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] =
iwl_rx_missed_beacon_notif;
@@ -1334,16 +1110,6 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv)
priv->cfg->ops->lib->rx_handler_setup(priv);
}
-/*
- * this should be called while priv->lock is locked
-*/
-static void __iwl_rx_replenish(struct iwl_priv *priv)
-{
- iwl_rx_allocate(priv);
- iwl_rx_queue_restock(priv);
-}
-
-
/**
* iwl_rx_handle - Main entry function for receiving responses from uCode
*
@@ -1364,7 +1130,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
/* uCode's read index (stored in shared DRAM) indicates the last Rx
* buffer that the driver may process (last buffer filled by ucode). */
- r = priv->cfg->ops->lib->shared_mem_rx_idx(priv);
+ r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF;
i = rxq->read;
/* Rx interrupt, but nothing sent from uCode */
@@ -1400,13 +1166,14 @@ void iwl_rx_handle(struct iwl_priv *priv)
reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) &&
(pkt->hdr.cmd != REPLY_RX_PHY_CMD) &&
(pkt->hdr.cmd != REPLY_RX) &&
+ (pkt->hdr.cmd != REPLY_RX_MPDU_CMD) &&
(pkt->hdr.cmd != REPLY_COMPRESSED_BA) &&
(pkt->hdr.cmd != STATISTICS_NOTIFICATION) &&
(pkt->hdr.cmd != REPLY_TX);
/* Based on type of command response or notification,
* handle those that need handling via function in
- * rx_handlers table. See iwl4965_setup_rx_handlers() */
+ * rx_handlers table. See iwl_setup_rx_handlers() */
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG(IWL_DL_RX, "r = %d, i = %d, %s, 0x%02x\n", r,
i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
@@ -1451,7 +1218,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
count++;
if (count >= 8) {
priv->rxq.read = i;
- __iwl_rx_replenish(priv);
+ iwl_rx_queue_restock(priv);
count = 0;
}
}
@@ -1463,10 +1230,9 @@ void iwl_rx_handle(struct iwl_priv *priv)
}
#ifdef CONFIG_IWLWIFI_DEBUG
-static void iwl4965_print_rx_config_cmd(struct iwl_priv *priv)
+static void iwl_print_rx_config_cmd(struct iwl_priv *priv)
{
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
- DECLARE_MAC_BUF(mac);
IWL_DEBUG_RADIO("RX CONFIG:\n");
iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
@@ -1478,50 +1244,26 @@ static void iwl4965_print_rx_config_cmd(struct iwl_priv *priv)
IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x\n",
rxon->ofdm_basic_rates);
IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
- IWL_DEBUG_RADIO("u8[6] node_addr: %s\n",
- print_mac(mac, rxon->node_addr));
- IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n",
- print_mac(mac, rxon->bssid_addr));
+ IWL_DEBUG_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr);
+ IWL_DEBUG_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
}
#endif
-static void iwl4965_enable_interrupts(struct iwl_priv *priv)
-{
- IWL_DEBUG_ISR("Enabling interrupts\n");
- set_bit(STATUS_INT_ENABLED, &priv->status);
- iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
-}
-
/* call this function to flush any scheduled tasklet */
static inline void iwl_synchronize_irq(struct iwl_priv *priv)
{
- /* wait to make sure we flush pedding tasklet*/
+ /* wait to make sure we flush pending tasklet*/
synchronize_irq(priv->pci_dev->irq);
tasklet_kill(&priv->irq_tasklet);
}
-static inline void iwl4965_disable_interrupts(struct iwl_priv *priv)
-{
- clear_bit(STATUS_INT_ENABLED, &priv->status);
-
- /* disable interrupts from uCode/NIC to host */
- iwl_write32(priv, CSR_INT_MASK, 0x00000000);
-
- /* acknowledge/clear/reset any interrupts still pending
- * from uCode or flow handler (Rx/Tx DMA) */
- iwl_write32(priv, CSR_INT, 0xffffffff);
- iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff);
- IWL_DEBUG_ISR("Disabled interrupts\n");
-}
-
-
/**
- * iwl4965_irq_handle_error - called for HW or SW error interrupt from card
+ * iwl_irq_handle_error - called for HW or SW error interrupt from card
*/
-static void iwl4965_irq_handle_error(struct iwl_priv *priv)
+static void iwl_irq_handle_error(struct iwl_priv *priv)
{
- /* Set the FW error flag -- cleared on iwl4965_down */
+ /* Set the FW error flag -- cleared on iwl_down */
set_bit(STATUS_FW_ERROR, &priv->status);
/* Cancel currently queued command. */
@@ -1531,7 +1273,7 @@ static void iwl4965_irq_handle_error(struct iwl_priv *priv)
if (priv->debug_level & IWL_DL_FW_ERRORS) {
iwl_dump_nic_error_log(priv);
iwl_dump_nic_event_log(priv);
- iwl4965_print_rx_config_cmd(priv);
+ iwl_print_rx_config_cmd(priv);
}
#endif
@@ -1555,14 +1297,14 @@ static void iwl4965_irq_handle_error(struct iwl_priv *priv)
}
}
-static void iwl4965_error_recovery(struct iwl_priv *priv)
+static void iwl_error_recovery(struct iwl_priv *priv)
{
unsigned long flags;
memcpy(&priv->staging_rxon, &priv->recovery_rxon,
sizeof(priv->staging_rxon));
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
iwl_rxon_add_station(priv, priv->bssid, 1);
@@ -1572,7 +1314,7 @@ static void iwl4965_error_recovery(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
}
-static void iwl4965_irq_tasklet(struct iwl_priv *priv)
+static void iwl_irq_tasklet(struct iwl_priv *priv)
{
u32 inta, handled = 0;
u32 inta_fh;
@@ -1618,9 +1360,9 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
IWL_ERROR("Microcode HW error detected. Restarting.\n");
/* Tell the device to stop sending interrupts */
- iwl4965_disable_interrupts(priv);
+ iwl_disable_interrupts(priv);
- iwl4965_irq_handle_error(priv);
+ iwl_irq_handle_error(priv);
handled |= CSR_INT_BIT_HW_ERR;
@@ -1652,14 +1394,17 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
hw_rf_kill = 1;
IWL_DEBUG(IWL_DL_RF_KILL, "RF_KILL bit toggled to %s.\n",
- hw_rf_kill ? "disable radio":"enable radio");
+ hw_rf_kill ? "disable radio" : "enable radio");
/* driver only loads ucode once setting the interface up.
* the driver as well won't allow loading if RFKILL is set
* therefore no need to restart the driver from this handler
*/
- if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status))
+ if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) {
clear_bit(STATUS_RF_KILL_HW, &priv->status);
+ if (priv->is_open && !iwl_is_rfkill(priv))
+ queue_work(priv->workqueue, &priv->up);
+ }
handled |= CSR_INT_BIT_RF_KILL;
}
@@ -1674,7 +1419,7 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
if (inta & CSR_INT_BIT_SW_ERR) {
IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n",
inta);
- iwl4965_irq_handle_error(priv);
+ iwl_irq_handle_error(priv);
handled |= CSR_INT_BIT_SW_ERR;
}
@@ -1720,7 +1465,7 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
/* Re-enable all interrupts */
/* only Re-enable if diabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
- iwl4965_enable_interrupts(priv);
+ iwl_enable_interrupts(priv);
#ifdef CONFIG_IWLWIFI_DEBUG
if (priv->debug_level & (IWL_DL_ISR)) {
@@ -1734,7 +1479,7 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
}
-static irqreturn_t iwl4965_isr(int irq, void *data)
+static irqreturn_t iwl_isr(int irq, void *data)
{
struct iwl_priv *priv = data;
u32 inta, inta_mask;
@@ -1766,7 +1511,7 @@ static irqreturn_t iwl4965_isr(int irq, void *data)
if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
/* Hardware disappeared. It might have already raised
* an interrupt */
- IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta);
+ IWL_WARNING("HARDWARE GONE?? INTA == 0x%08x\n", inta);
goto unplugged;
}
@@ -1775,7 +1520,7 @@ static irqreturn_t iwl4965_isr(int irq, void *data)
inta &= ~CSR_INT_BIT_SCD;
- /* iwl4965_irq_tasklet() will service interrupts and re-enable them */
+ /* iwl_irq_tasklet() will service interrupts and re-enable them */
if (likely(inta || inta_fh))
tasklet_schedule(&priv->irq_tasklet);
@@ -1787,7 +1532,7 @@ static irqreturn_t iwl4965_isr(int irq, void *data)
/* re-enable interrupts here since we don't have anything to service. */
/* only Re-enable if diabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
- iwl4965_enable_interrupts(priv);
+ iwl_enable_interrupts(priv);
spin_unlock(&priv->lock);
return IRQ_NONE;
}
@@ -1798,7 +1543,7 @@ static irqreturn_t iwl4965_isr(int irq, void *data)
*
******************************************************************************/
-static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv)
+static void iwl_dealloc_ucode_pci(struct iwl_priv *priv)
{
iwl_free_fw_desc(priv->pci_dev, &priv->ucode_code);
iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data);
@@ -1808,7 +1553,7 @@ static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv)
iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot);
}
-static void iwl4965_nic_start(struct iwl_priv *priv)
+static void iwl_nic_start(struct iwl_priv *priv)
{
/* Remove all resets to allow NIC to operate */
iwl_write32(priv, CSR_RESET, 0);
@@ -1816,31 +1561,47 @@ static void iwl4965_nic_start(struct iwl_priv *priv)
/**
- * iwl4965_read_ucode - Read uCode images from disk file.
+ * iwl_read_ucode - Read uCode images from disk file.
*
* Copy into buffers for card to fetch via bus-mastering
*/
-static int iwl4965_read_ucode(struct iwl_priv *priv)
+static int iwl_read_ucode(struct iwl_priv *priv)
{
struct iwl_ucode *ucode;
- int ret;
+ int ret = -EINVAL, index;
const struct firmware *ucode_raw;
- const char *name = priv->cfg->fw_name;
+ const char *name_pre = priv->cfg->fw_name_pre;
+ const unsigned int api_max = priv->cfg->ucode_api_max;
+ const unsigned int api_min = priv->cfg->ucode_api_min;
+ char buf[25];
u8 *src;
size_t len;
- u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
+ u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size;
/* Ask kernel firmware_class module to get the boot firmware off disk.
* request_firmware() is synchronous, file is in memory on return. */
- ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev);
- if (ret < 0) {
- IWL_ERROR("%s firmware file req failed: Reason %d\n",
- name, ret);
- goto error;
+ for (index = api_max; index >= api_min; index--) {
+ sprintf(buf, "%s%d%s", name_pre, index, ".ucode");
+ ret = request_firmware(&ucode_raw, buf, &priv->pci_dev->dev);
+ if (ret < 0) {
+ IWL_ERROR("%s firmware file req failed: Reason %d\n",
+ buf, ret);
+ if (ret == -ENOENT)
+ continue;
+ else
+ goto error;
+ } else {
+ if (index < api_max)
+ IWL_ERROR("Loaded firmware %s, which is deprecated. Please use API v%u instead.\n",
+ buf, api_max);
+ IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
+ buf, ucode_raw->size);
+ break;
+ }
}
- IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
- name, ucode_raw->size);
+ if (ret < 0)
+ goto error;
/* Make sure that we got at least our header! */
if (ucode_raw->size < sizeof(*ucode)) {
@@ -1852,14 +1613,40 @@ static int iwl4965_read_ucode(struct iwl_priv *priv)
/* Data from ucode file: header followed by uCode images */
ucode = (void *)ucode_raw->data;
- ver = le32_to_cpu(ucode->ver);
+ priv->ucode_ver = le32_to_cpu(ucode->ver);
+ api_ver = IWL_UCODE_API(priv->ucode_ver);
inst_size = le32_to_cpu(ucode->inst_size);
data_size = le32_to_cpu(ucode->data_size);
init_size = le32_to_cpu(ucode->init_size);
init_data_size = le32_to_cpu(ucode->init_data_size);
boot_size = le32_to_cpu(ucode->boot_size);
- IWL_DEBUG_INFO("f/w package hdr ucode version = 0x%x\n", ver);
+ /* api_ver should match the api version forming part of the
+ * firmware filename ... but we don't check for that and only rely
+ * on the API version read from firware header from here on forward */
+
+ if (api_ver < api_min || api_ver > api_max) {
+ IWL_ERROR("Driver unable to support your firmware API. "
+ "Driver supports v%u, firmware is v%u.\n",
+ api_max, api_ver);
+ priv->ucode_ver = 0;
+ ret = -EINVAL;
+ goto err_release;
+ }
+ if (api_ver != api_max)
+ IWL_ERROR("Firmware has old API version. Expected v%u, "
+ "got v%u. New firmware can be obtained "
+ "from http://www.intellinuxwireless.org.\n",
+ api_max, api_ver);
+
+ printk(KERN_INFO DRV_NAME " loaded firmware version %u.%u.%u.%u\n",
+ IWL_UCODE_MAJOR(priv->ucode_ver),
+ IWL_UCODE_MINOR(priv->ucode_ver),
+ IWL_UCODE_API(priv->ucode_ver),
+ IWL_UCODE_SERIAL(priv->ucode_ver));
+
+ IWL_DEBUG_INFO("f/w package hdr ucode version raw = 0x%x\n",
+ priv->ucode_ver);
IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n",
inst_size);
IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n",
@@ -1964,7 +1751,7 @@ static int iwl4965_read_ucode(struct iwl_priv *priv)
priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr);
/* Runtime data (2nd block)
- * NOTE: Copy into backup buffer will be done in iwl4965_up() */
+ * NOTE: Copy into backup buffer will be done in iwl_up() */
src = &ucode->data[inst_size];
len = priv->ucode_data.len;
IWL_DEBUG_INFO("Copying (but not loading) uCode data len %Zd\n", len);
@@ -2002,7 +1789,7 @@ static int iwl4965_read_ucode(struct iwl_priv *priv)
err_pci_alloc:
IWL_ERROR("failed to allocate pci memory\n");
ret = -ENOMEM;
- iwl4965_dealloc_ucode_pci(priv);
+ iwl_dealloc_ucode_pci(priv);
err_release:
release_firmware(ucode_raw);
@@ -2011,6 +1798,10 @@ static int iwl4965_read_ucode(struct iwl_priv *priv)
return ret;
}
+/* temporary */
+static int iwl_mac_beacon_update(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
/**
* iwl_alive_start - called after REPLY_ALIVE notification received
* from protocol/runtime uCode (initialization uCode's
@@ -2047,7 +1838,7 @@ static void iwl_alive_start(struct iwl_priv *priv)
goto restart;
}
- /* After the ALIVE response, we can send host commands to 4965 uCode */
+ /* After the ALIVE response, we can send host commands to the uCode */
set_bit(STATUS_ALIVE, &priv->status);
if (iwl_is_rfkill(priv))
@@ -2067,17 +1858,17 @@ static void iwl_alive_start(struct iwl_priv *priv)
active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
} else {
/* Initialize our rx_config data */
- iwl4965_connection_init_rx_config(priv);
+ iwl_connection_init_rx_config(priv, priv->iw_mode);
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
}
/* Configure Bluetooth device coexistence support */
- iwl4965_send_bt_config(priv);
+ iwl_send_bt_config(priv);
iwl_reset_run_time_calib(priv);
/* Configure the adapter for unassociated operation */
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
/* At this point, the NIC is initialized and operational */
iwl_rf_kill_ct_config(priv);
@@ -2089,12 +1880,21 @@ static void iwl_alive_start(struct iwl_priv *priv)
wake_up_interruptible(&priv->wait_command_queue);
if (priv->error_recovering)
- iwl4965_error_recovery(priv);
+ iwl_error_recovery(priv);
iwl_power_update_mode(priv, 1);
+ /* reassociate for ADHOC mode */
+ if (priv->vif && (priv->iw_mode == NL80211_IFTYPE_ADHOC)) {
+ struct sk_buff *beacon = ieee80211_beacon_get(priv->hw,
+ priv->vif);
+ if (beacon)
+ iwl_mac_beacon_update(priv->hw, beacon);
+ }
+
+
if (test_and_clear_bit(STATUS_MODE_PENDING, &priv->status))
- iwl4965_set_mode(priv, priv->iw_mode);
+ iwl_set_mode(priv, priv->iw_mode);
return;
@@ -2104,7 +1904,7 @@ static void iwl_alive_start(struct iwl_priv *priv)
static void iwl_cancel_deferred_work(struct iwl_priv *priv);
-static void __iwl4965_down(struct iwl_priv *priv)
+static void __iwl_down(struct iwl_priv *priv)
{
unsigned long flags;
int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status);
@@ -2131,14 +1931,14 @@ static void __iwl4965_down(struct iwl_priv *priv)
/* tell the device to stop sending interrupts */
spin_lock_irqsave(&priv->lock, flags);
- iwl4965_disable_interrupts(priv);
+ iwl_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->lock, flags);
iwl_synchronize_irq(priv);
if (priv->mac80211_registered)
ieee80211_stop_queues(priv->hw);
- /* If we have not previously called iwl4965_init() then
+ /* If we have not previously called iwl_init() then
* clear all bits but the RF Kill and SUSPEND bits and return */
if (!iwl_is_init(priv)) {
priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) <<
@@ -2192,8 +1992,6 @@ static void __iwl4965_down(struct iwl_priv *priv)
priv->cfg->ops->lib->apm_ops.stop(priv);
else
priv->cfg->ops->lib->apm_ops.reset(priv);
- priv->cfg->ops->lib->free_shared_mem(priv);
-
exit:
memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp));
@@ -2205,10 +2003,10 @@ static void __iwl4965_down(struct iwl_priv *priv)
iwl_clear_free_frames(priv);
}
-static void iwl4965_down(struct iwl_priv *priv)
+static void iwl_down(struct iwl_priv *priv)
{
mutex_lock(&priv->mutex);
- __iwl4965_down(priv);
+ __iwl_down(priv);
mutex_unlock(&priv->mutex);
iwl_cancel_deferred_work(priv);
@@ -2216,7 +2014,7 @@ static void iwl4965_down(struct iwl_priv *priv)
#define MAX_HW_RESTARTS 5
-static int __iwl4965_up(struct iwl_priv *priv)
+static int __iwl_up(struct iwl_priv *priv)
{
int i;
int ret;
@@ -2238,7 +2036,7 @@ static int __iwl4965_up(struct iwl_priv *priv)
set_bit(STATUS_RF_KILL_HW, &priv->status);
if (iwl_is_rfkill(priv)) {
- iwl4965_enable_interrupts(priv);
+ iwl_enable_interrupts(priv);
IWL_WARNING("Radio disabled by %s RF Kill switch\n",
test_bit(STATUS_RF_KILL_HW, &priv->status) ? "HW" : "SW");
return 0;
@@ -2246,12 +2044,6 @@ static int __iwl4965_up(struct iwl_priv *priv)
iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
- ret = priv->cfg->ops->lib->alloc_shared_mem(priv);
- if (ret) {
- IWL_ERROR("Unable to allocate shared memory\n");
- return ret;
- }
-
ret = iwl_hw_nic_init(priv);
if (ret) {
IWL_ERROR("Unable to init nic\n");
@@ -2265,7 +2057,7 @@ static int __iwl4965_up(struct iwl_priv *priv)
/* clear (again), then enable host interrupts */
iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
- iwl4965_enable_interrupts(priv);
+ iwl_enable_interrupts(priv);
/* really make sure rfkill handshake bits are cleared */
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
@@ -2295,7 +2087,7 @@ static int __iwl4965_up(struct iwl_priv *priv)
clear_bit(STATUS_FW_ERROR, &priv->status);
/* start card; "initialize" will load runtime ucode */
- iwl4965_nic_start(priv);
+ iwl_nic_start(priv);
IWL_DEBUG_INFO(DRV_NAME " is coming up\n");
@@ -2303,7 +2095,7 @@ static int __iwl4965_up(struct iwl_priv *priv)
}
set_bit(STATUS_EXIT_PENDING, &priv->status);
- __iwl4965_down(priv);
+ __iwl_down(priv);
clear_bit(STATUS_EXIT_PENDING, &priv->status);
/* tried to restart and config the device for as long as our
@@ -2345,7 +2137,7 @@ static void iwl_bg_alive_start(struct work_struct *data)
mutex_unlock(&priv->mutex);
}
-static void iwl4965_bg_rf_kill(struct work_struct *work)
+static void iwl_bg_rf_kill(struct work_struct *work)
{
struct iwl_priv *priv = container_of(work, struct iwl_priv, rf_kill);
@@ -2379,28 +2171,6 @@ static void iwl4965_bg_rf_kill(struct work_struct *work)
iwl_rfkill_set_hw_state(priv);
}
-static void iwl4965_bg_set_monitor(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work,
- struct iwl_priv, set_monitor);
- int ret;
-
- IWL_DEBUG(IWL_DL_STATE, "setting monitor mode\n");
-
- mutex_lock(&priv->mutex);
-
- ret = iwl4965_set_mode(priv, NL80211_IFTYPE_MONITOR);
-
- if (ret) {
- if (ret == -EAGAIN)
- IWL_DEBUG(IWL_DL_STATE, "leave - not ready\n");
- else
- IWL_ERROR("iwl4965_set_mode() failed ret = %d\n", ret);
- }
-
- mutex_unlock(&priv->mutex);
-}
-
static void iwl_bg_run_time_calib_work(struct work_struct *work)
{
struct iwl_priv *priv = container_of(work, struct iwl_priv,
@@ -2424,7 +2194,7 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work)
return;
}
-static void iwl4965_bg_up(struct work_struct *data)
+static void iwl_bg_up(struct work_struct *data)
{
struct iwl_priv *priv = container_of(data, struct iwl_priv, up);
@@ -2432,23 +2202,23 @@ static void iwl4965_bg_up(struct work_struct *data)
return;
mutex_lock(&priv->mutex);
- __iwl4965_up(priv);
+ __iwl_up(priv);
mutex_unlock(&priv->mutex);
iwl_rfkill_set_hw_state(priv);
}
-static void iwl4965_bg_restart(struct work_struct *data)
+static void iwl_bg_restart(struct work_struct *data)
{
struct iwl_priv *priv = container_of(data, struct iwl_priv, restart);
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
- iwl4965_down(priv);
+ iwl_down(priv);
queue_work(priv->workqueue, &priv->up);
}
-static void iwl4965_bg_rx_replenish(struct work_struct *data)
+static void iwl_bg_rx_replenish(struct work_struct *data)
{
struct iwl_priv *priv =
container_of(data, struct iwl_priv, rx_replenish);
@@ -2463,11 +2233,10 @@ static void iwl4965_bg_rx_replenish(struct work_struct *data)
#define IWL_DELAY_NEXT_SCAN (HZ*2)
-static void iwl4965_post_associate(struct iwl_priv *priv)
+static void iwl_post_associate(struct iwl_priv *priv)
{
struct ieee80211_conf *conf = NULL;
int ret = 0;
- DECLARE_MAC_BUF(mac);
unsigned long flags;
if (priv->iw_mode == NL80211_IFTYPE_AP) {
@@ -2475,9 +2244,8 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
return;
}
- IWL_DEBUG_ASSOC("Associated as %d to: %s\n",
- priv->assoc_id,
- print_mac(mac, priv->active_rxon.bssid_addr));
+ IWL_DEBUG_ASSOC("Associated as %d to: %pM\n",
+ priv->assoc_id, priv->active_rxon.bssid_addr);
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
@@ -2493,10 +2261,9 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
conf = ieee80211_get_hw_conf(priv->hw);
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
- memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd));
- iwl4965_setup_rxon_timing(priv);
+ iwl_setup_rxon_timing(priv);
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing), &priv->rxon_timing);
if (ret)
@@ -2529,7 +2296,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
}
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
switch (priv->iw_mode) {
case NL80211_IFTYPE_STATION:
@@ -2541,7 +2308,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
priv->assoc_id = 1;
iwl_rxon_add_station(priv, priv->bssid, 0);
- iwl4965_send_beacon_cmd(priv);
+ iwl_send_beacon_cmd(priv);
break;
@@ -2578,7 +2345,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv)
#define UCODE_READY_TIMEOUT (4 * HZ)
-static int iwl4965_mac_start(struct ieee80211_hw *hw)
+static int iwl_mac_start(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
int ret;
@@ -2600,7 +2367,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd);
}
- ret = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED,
+ ret = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
DRV_NAME, priv);
if (ret) {
IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq);
@@ -2615,7 +2382,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
* ucode filename and max sizes are card-specific. */
if (!priv->ucode_code.len) {
- ret = iwl4965_read_ucode(priv);
+ ret = iwl_read_ucode(priv);
if (ret) {
IWL_ERROR("Could not read microcode: %d\n", ret);
mutex_unlock(&priv->mutex);
@@ -2623,7 +2390,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw)
}
}
- ret = __iwl4965_up(priv);
+ ret = __iwl_up(priv);
mutex_unlock(&priv->mutex);
@@ -2669,7 +2436,7 @@ out_disable_msi:
return ret;
}
-static void iwl4965_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
@@ -2691,7 +2458,7 @@ static void iwl4965_mac_stop(struct ieee80211_hw *hw)
mutex_unlock(&priv->mutex);
}
- iwl4965_down(priv);
+ iwl_down(priv);
flush_workqueue(priv->workqueue);
free_irq(priv->pci_dev->irq, priv);
@@ -2702,7 +2469,7 @@ static void iwl4965_mac_stop(struct ieee80211_hw *hw)
IWL_DEBUG_MAC80211("leave\n");
}
-static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int iwl_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct iwl_priv *priv = hw->priv;
@@ -2718,12 +2485,11 @@ static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return 0;
}
-static int iwl4965_mac_add_interface(struct ieee80211_hw *hw,
+static int iwl_mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct iwl_priv *priv = hw->priv;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
IWL_DEBUG_MAC80211("enter: type %d\n", conf->type);
@@ -2734,17 +2500,18 @@ static int iwl4965_mac_add_interface(struct ieee80211_hw *hw,
spin_lock_irqsave(&priv->lock, flags);
priv->vif = conf->vif;
+ priv->iw_mode = conf->type;
spin_unlock_irqrestore(&priv->lock, flags);
mutex_lock(&priv->mutex);
if (conf->mac_addr) {
- IWL_DEBUG_MAC80211("Set %s\n", print_mac(mac, conf->mac_addr));
+ IWL_DEBUG_MAC80211("Set %pM\n", conf->mac_addr);
memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
}
- if (iwl4965_set_mode(priv, conf->type) == -EAGAIN)
+ if (iwl_set_mode(priv, conf->type) == -EAGAIN)
/* we are not ready, will run again when ready */
set_bit(STATUS_MODE_PENDING, &priv->status);
@@ -2755,16 +2522,17 @@ static int iwl4965_mac_add_interface(struct ieee80211_hw *hw,
}
/**
- * iwl4965_mac_config - mac80211 config callback
+ * iwl_mac_config - mac80211 config callback
*
* We ignore conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME since it seems to
* be set inappropriately and the driver currently sets the hardware up to
* use it whenever needed.
*/
-static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+static int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
{
struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info;
+ struct ieee80211_conf *conf = &hw->conf;
unsigned long flags;
int ret = 0;
u16 channel;
@@ -2772,6 +2540,8 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel->hw_value);
+ priv->current_ht_config.is_ht = conf->ht.enabled;
+
if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) {
IWL_DEBUG_MAC80211("leave - RF-KILL - waiting for uCode\n");
goto out;
@@ -2829,13 +2599,13 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
/* The list of supported rates and rate mask can be different
* for each band; since the band may have changed, reset
* the rate mask to what mac80211 lists */
- iwl4965_set_rate(priv);
+ iwl_set_rate(priv);
spin_unlock_irqrestore(&priv->lock, flags);
#ifdef IEEE80211_CONF_CHANNEL_SWITCH
if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) {
- iwl4965_hw_channel_switch(priv, conf->channel);
+ iwl_hw_channel_switch(priv, conf->channel);
goto out;
}
#endif
@@ -2863,11 +2633,11 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co
iwl_set_tx_power(priv, conf->power_level, false);
- iwl4965_set_rate(priv);
+ iwl_set_rate(priv);
if (memcmp(&priv->active_rxon,
&priv->staging_rxon, sizeof(priv->staging_rxon)))
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
else
IWL_DEBUG_INFO("No re-sending same RXON configuration.\n");
@@ -2878,7 +2648,7 @@ out:
return ret;
}
-static void iwl4965_config_ap(struct iwl_priv *priv)
+static void iwl_config_ap(struct iwl_priv *priv)
{
int ret = 0;
unsigned long flags;
@@ -2887,15 +2657,14 @@ static void iwl4965_config_ap(struct iwl_priv *priv)
return;
/* The following should be done only at AP bring up */
- if (!(iwl_is_associated(priv))) {
+ if (!iwl_is_associated(priv)) {
/* RXON - unassoc (to set timing command) */
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
/* RXON Timing */
- memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd));
- iwl4965_setup_rxon_timing(priv);
+ iwl_setup_rxon_timing(priv);
ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING,
sizeof(priv->rxon_timing), &priv->rxon_timing);
if (ret)
@@ -2928,29 +2697,25 @@ static void iwl4965_config_ap(struct iwl_priv *priv)
}
/* restore RXON assoc */
priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
spin_lock_irqsave(&priv->lock, flags);
iwl_activate_qos(priv, 1);
spin_unlock_irqrestore(&priv->lock, flags);
iwl_rxon_add_station(priv, iwl_bcast_addr, 0);
}
- iwl4965_send_beacon_cmd(priv);
+ iwl_send_beacon_cmd(priv);
/* FIXME - we need to add code here to detect a totally new
* configuration, reset the AP, unassoc, rxon timing, assoc,
* clear sta table, add BCAST sta... */
}
-/* temporary */
-static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
+static int iwl_mac_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{
struct iwl_priv *priv = hw->priv;
- DECLARE_MAC_BUF(mac);
- unsigned long flags;
int rc;
if (conf == NULL)
@@ -2966,26 +2731,20 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
if (!beacon)
return -ENOMEM;
- rc = iwl4965_mac_beacon_update(hw, beacon);
+ mutex_lock(&priv->mutex);
+ rc = iwl_mac_beacon_update(hw, beacon);
+ mutex_unlock(&priv->mutex);
if (rc)
return rc;
}
- if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
- (!conf->ssid_len)) {
- IWL_DEBUG_MAC80211
- ("Leaving in AP mode because HostAPD is not ready.\n");
- return 0;
- }
-
if (!iwl_is_alive(priv))
return -EAGAIN;
mutex_lock(&priv->mutex);
if (conf->bssid)
- IWL_DEBUG_MAC80211("bssid: %s\n",
- print_mac(mac, conf->bssid));
+ IWL_DEBUG_MAC80211("bssid: %pM\n", conf->bssid);
/*
* very dubious code was here; the probe filtering flag is never set:
@@ -2998,8 +2757,8 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
if (!conf->bssid) {
conf->bssid = priv->mac_addr;
memcpy(priv->bssid, priv->mac_addr, ETH_ALEN);
- IWL_DEBUG_MAC80211("bssid was set to: %s\n",
- print_mac(mac, conf->bssid));
+ IWL_DEBUG_MAC80211("bssid was set to: %pM\n",
+ conf->bssid);
}
if (priv->ibss_beacon)
dev_kfree_skb(priv->ibss_beacon);
@@ -3030,9 +2789,9 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
memcpy(priv->bssid, conf->bssid, ETH_ALEN);
if (priv->iw_mode == NL80211_IFTYPE_AP)
- iwl4965_config_ap(priv);
+ iwl_config_ap(priv);
else {
- rc = iwl4965_commit_rxon(priv);
+ rc = iwl_commit_rxon(priv);
if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc)
iwl_rxon_add_station(
priv, priv->active_rxon.bssid_addr, 1);
@@ -3041,45 +2800,63 @@ static int iwl4965_mac_config_interface(struct ieee80211_hw *hw,
} else {
iwl_scan_cancel_timeout(priv, 100);
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
done:
- spin_lock_irqsave(&priv->lock, flags);
- if (!conf->ssid_len)
- memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
- else
- memcpy(priv->essid, conf->ssid, conf->ssid_len);
-
- priv->essid_len = conf->ssid_len;
- spin_unlock_irqrestore(&priv->lock, flags);
-
IWL_DEBUG_MAC80211("leave\n");
mutex_unlock(&priv->mutex);
return 0;
}
-static void iwl4965_configure_filter(struct ieee80211_hw *hw,
+static void iwl_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
int mc_count, struct dev_addr_list *mc_list)
{
struct iwl_priv *priv = hw->priv;
+ __le32 *filter_flags = &priv->staging_rxon.filter_flags;
- if (changed_flags & (*total_flags) & FIF_OTHER_BSS) {
- IWL_DEBUG_MAC80211("Enter: type %d (0x%x, 0x%x)\n",
- NL80211_IFTYPE_MONITOR,
- changed_flags, *total_flags);
- /* queue work 'cuz mac80211 is holding a lock which
- * prevents us from issuing (synchronous) f/w cmds */
- queue_work(priv->workqueue, &priv->set_monitor);
+ IWL_DEBUG_MAC80211("Enter: changed: 0x%x, total: 0x%x\n",
+ changed_flags, *total_flags);
+
+ if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) {
+ if (*total_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS))
+ *filter_flags |= RXON_FILTER_PROMISC_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_PROMISC_MSK;
+ }
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*total_flags & FIF_ALLMULTI)
+ *filter_flags |= RXON_FILTER_ACCEPT_GRP_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_ACCEPT_GRP_MSK;
+ }
+ if (changed_flags & FIF_CONTROL) {
+ if (*total_flags & FIF_CONTROL)
+ *filter_flags |= RXON_FILTER_CTL2HOST_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_CTL2HOST_MSK;
}
- *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI |
+ if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+ *filter_flags |= RXON_FILTER_BCON_AWARE_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_BCON_AWARE_MSK;
+ }
+
+ /* We avoid iwl_commit_rxon here to commit the new filter flags
+ * since mac80211 will call ieee80211_hw_config immediately.
+ * (mc_list is not supported at this time). Otherwise, we need to
+ * queue a background iwl_commit_rxon work.
+ */
+
+ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS |
FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
}
-static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw,
+static void iwl_mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct iwl_priv *priv = hw->priv;
@@ -3091,13 +2868,11 @@ static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw,
if (iwl_is_ready_rf(priv)) {
iwl_scan_cancel_timeout(priv, 100);
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
if (priv->vif == conf->vif) {
priv->vif = NULL;
memset(priv->bssid, 0, ETH_ALEN);
- memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
- priv->essid_len = 0;
}
mutex_unlock(&priv->mutex);
@@ -3106,7 +2881,7 @@ static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw,
}
#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
-static void iwl4965_bss_info_changed(struct ieee80211_hw *hw,
+static void iwl_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changes)
@@ -3133,8 +2908,7 @@ static void iwl4965_bss_info_changed(struct ieee80211_hw *hw,
}
if (changes & BSS_CHANGED_HT) {
- IWL_DEBUG_MAC80211("HT %d\n", bss_conf->assoc_ht);
- iwl4965_ht_conf(priv, bss_conf);
+ iwl_ht_conf(priv, bss_conf);
iwl_set_rxon_chain(priv);
}
@@ -3157,7 +2931,7 @@ static void iwl4965_bss_info_changed(struct ieee80211_hw *hw,
priv->next_scan_jiffies = jiffies +
IWL_DELAY_NEXT_SCAN_AFTER_ASSOC;
mutex_lock(&priv->mutex);
- iwl4965_post_associate(priv);
+ iwl_post_associate(priv);
mutex_unlock(&priv->mutex);
} else {
priv->assoc_id = 0;
@@ -3187,12 +2961,6 @@ static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len)
goto out_unlock;
}
- if (priv->iw_mode == NL80211_IFTYPE_AP) { /* APs don't scan */
- ret = -EIO;
- IWL_ERROR("ERROR: APs don't scan\n");
- goto out_unlock;
- }
-
/* We don't schedule scan within next_scan_jiffies period.
* Avoid scanning during possible EAPOL exchange, return
* success immediately.
@@ -3233,64 +3001,24 @@ out_unlock:
return ret;
}
-static void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw,
+static void iwl_mac_update_tkip_key(struct ieee80211_hw *hw,
struct ieee80211_key_conf *keyconf, const u8 *addr,
u32 iv32, u16 *phase1key)
{
- struct iwl_priv *priv = hw->priv;
- u8 sta_id = IWL_INVALID_STATION;
- unsigned long flags;
- __le16 key_flags = 0;
- int i;
- DECLARE_MAC_BUF(mac);
+ struct iwl_priv *priv = hw->priv;
IWL_DEBUG_MAC80211("enter\n");
- sta_id = iwl_find_station(priv, addr);
- if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_MAC80211("leave - %s not in station map.\n",
- print_mac(mac, addr));
- return;
- }
-
- if (iwl_scan_cancel(priv)) {
- /* cancel scan failed, just live w/ bad key and rely
- briefly on SW decryption */
- return;
- }
-
- key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
- key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
- key_flags &= ~STA_KEY_FLG_INVALID;
-
- if (sta_id == priv->hw_params.bcast_sta_id)
- key_flags |= STA_KEY_MULTICAST_MSK;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- priv->stations[sta_id].sta.key.key_flags = key_flags;
- priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
-
- for (i = 0; i < 5; i++)
- priv->stations[sta_id].sta.key.tkip_rx_ttak[i] =
- cpu_to_le16(phase1key[i]);
-
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
-
- iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
+ iwl_update_tkip_key(priv, keyconf, addr, iv32, phase1key);
IWL_DEBUG_MAC80211("leave\n");
}
-static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
const u8 *local_addr, const u8 *addr,
struct ieee80211_key_conf *key)
{
struct iwl_priv *priv = hw->priv;
- DECLARE_MAC_BUF(mac);
int ret = 0;
u8 sta_id = IWL_INVALID_STATION;
u8 is_default_wep_key = 0;
@@ -3308,8 +3036,8 @@ static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
sta_id = iwl_find_station(priv, addr);
if (sta_id == IWL_INVALID_STATION) {
- IWL_DEBUG_MAC80211("leave - %s not in station map.\n",
- print_mac(mac, addr));
+ IWL_DEBUG_MAC80211("leave - %pM not in station map.\n",
+ addr);
return -EINVAL;
}
@@ -3357,7 +3085,7 @@ static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return ret;
}
-static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+static int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct iwl_priv *priv = hw->priv;
@@ -3376,11 +3104,6 @@ static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
return 0;
}
- if (!priv->qos_data.qos_enable) {
- priv->qos_data.qos_active = 0;
- IWL_DEBUG_MAC80211("leave - qos not enabled\n");
- return 0;
- }
q = AC_NUM - 1 - queue;
spin_lock_irqsave(&priv->lock, flags);
@@ -3405,15 +3128,14 @@ static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
return 0;
}
-static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
+static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
struct iwl_priv *priv = hw->priv;
- DECLARE_MAC_BUF(mac);
- IWL_DEBUG_HT("A-MPDU action on addr %s tid %d\n",
- print_mac(mac, sta->addr), tid);
+ IWL_DEBUG_HT("A-MPDU action on addr %pM tid %d\n",
+ sta->addr, tid);
if (!(priv->cfg->sku & IWL_SKU_N))
return -EACCES;
@@ -3421,10 +3143,10 @@ static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
switch (action) {
case IEEE80211_AMPDU_RX_START:
IWL_DEBUG_HT("start Rx\n");
- return iwl_rx_agg_start(priv, sta->addr, tid, *ssn);
+ return iwl_sta_rx_agg_start(priv, sta->addr, tid, *ssn);
case IEEE80211_AMPDU_RX_STOP:
IWL_DEBUG_HT("stop Rx\n");
- return iwl_rx_agg_stop(priv, sta->addr, tid);
+ return iwl_sta_rx_agg_stop(priv, sta->addr, tid);
case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT("start Tx\n");
return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
@@ -3438,7 +3160,8 @@ static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw,
}
return 0;
}
-static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw,
+
+static int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct iwl_priv *priv = hw->priv;
@@ -3473,7 +3196,7 @@ static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw,
return 0;
}
-static int iwl4965_mac_get_stats(struct ieee80211_hw *hw,
+static int iwl_mac_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
struct iwl_priv *priv = hw->priv;
@@ -3485,7 +3208,7 @@ static int iwl4965_mac_get_stats(struct ieee80211_hw *hw,
return 0;
}
-static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw)
+static void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
unsigned long flags;
@@ -3529,7 +3252,7 @@ static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw)
if (priv->iw_mode != NL80211_IFTYPE_AP) {
iwl_scan_cancel_timeout(priv, 100);
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
iwl_power_update_mode(priv, 0);
@@ -3552,31 +3275,28 @@ static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw)
return;
}
- iwl4965_set_rate(priv);
+ iwl_set_rate(priv);
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211("leave\n");
}
-static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct iwl_priv *priv = hw->priv;
unsigned long flags;
__le64 timestamp;
- mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211("enter\n");
if (!iwl_is_ready_rf(priv)) {
IWL_DEBUG_MAC80211("leave - RF not ready\n");
- mutex_unlock(&priv->mutex);
return -EIO;
}
if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
IWL_DEBUG_MAC80211("leave - not IBSS\n");
- mutex_unlock(&priv->mutex);
return -EIO;
}
@@ -3596,9 +3316,8 @@ static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *sk
iwl_reset_qos(priv);
- iwl4965_post_associate(priv);
+ iwl_post_associate(priv);
- mutex_unlock(&priv->mutex);
return 0;
}
@@ -3613,7 +3332,7 @@ static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *sk
/*
* The following adds a new attribute to the sysfs representation
- * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/)
+ * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/)
* used for controlling the debug level.
*
* See the level definitions in iwl for details.
@@ -3699,7 +3418,11 @@ static ssize_t show_tx_power(struct device *d,
struct device_attribute *attr, char *buf)
{
struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
- return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
+
+ if (!iwl_is_ready_rf(priv))
+ return sprintf(buf, "off\n");
+ else
+ return sprintf(buf, "%d\n", priv->tx_power_user_lmt);
}
static ssize_t store_tx_power(struct device *d,
@@ -3750,7 +3473,7 @@ static ssize_t store_flags(struct device *d,
else {
IWL_DEBUG_INFO("Commit rxon.flags = 0x%04X\n", flags);
priv->staging_rxon.flags = cpu_to_le32(flags);
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
}
mutex_unlock(&priv->mutex);
@@ -3791,7 +3514,7 @@ static ssize_t store_filter_flags(struct device *d,
"0x%04X\n", filter_flags);
priv->staging_rxon.filter_flags =
cpu_to_le32(filter_flags);
- iwl4965_commit_rxon(priv);
+ iwl_commit_rxon(priv);
}
}
mutex_unlock(&priv->mutex);
@@ -3802,79 +3525,6 @@ static ssize_t store_filter_flags(struct device *d,
static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags,
store_filter_flags);
-#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
-
-static ssize_t show_measurement(struct device *d,
- struct device_attribute *attr, char *buf)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- struct iwl4965_spectrum_notification measure_report;
- u32 size = sizeof(measure_report), len = 0, ofs = 0;
- u8 *data = (u8 *)&measure_report;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- if (!(priv->measurement_status & MEASUREMENT_READY)) {
- spin_unlock_irqrestore(&priv->lock, flags);
- return 0;
- }
- memcpy(&measure_report, &priv->measure_report, size);
- priv->measurement_status = 0;
- spin_unlock_irqrestore(&priv->lock, flags);
-
- while (size && (PAGE_SIZE - len)) {
- hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len,
- PAGE_SIZE - len, 1);
- len = strlen(buf);
- if (PAGE_SIZE - len)
- buf[len++] = '\n';
-
- ofs += 16;
- size -= min(size, 16U);
- }
-
- return len;
-}
-
-static ssize_t store_measurement(struct device *d,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct iwl_priv *priv = dev_get_drvdata(d);
- struct ieee80211_measurement_params params = {
- .channel = le16_to_cpu(priv->active_rxon.channel),
- .start_time = cpu_to_le64(priv->last_tsf),
- .duration = cpu_to_le16(1),
- };
- u8 type = IWL_MEASURE_BASIC;
- u8 buffer[32];
- u8 channel;
-
- if (count) {
- char *p = buffer;
- strncpy(buffer, buf, min(sizeof(buffer), count));
- channel = simple_strtoul(p, NULL, 0);
- if (channel)
- params.channel = channel;
-
- p = buffer;
- while (*p && *p != ' ')
- p++;
- if (*p)
- type = simple_strtoul(p + 1, NULL, 0);
- }
-
- IWL_DEBUG_INFO("Invoking measurement of type %d on "
- "channel %d (for '%s')\n", type, params.channel, buf);
- iwl4965_get_measurement(priv, &params, type);
-
- return count;
-}
-
-static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR,
- show_measurement, store_measurement);
-#endif /* CONFIG_IWLAGN_SPECTRUM_MEASUREMENT */
-
static ssize_t store_retry_rate(struct device *d,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -3953,7 +3603,8 @@ static ssize_t show_power_level(struct device *d,
break;
}
- p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO)?"fixed":"auto");
+ p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO) ?
+ "fixed" : "auto");
p += sprintf(p, "\tINDEX:%d", level);
p += sprintf(p, "\n");
return p - buf + 1;
@@ -3962,68 +3613,6 @@ static ssize_t show_power_level(struct device *d,
static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level,
store_power_level);
-static ssize_t show_channels(struct device *d,
- struct device_attribute *attr, char *buf)
-{
-
- struct iwl_priv *priv = dev_get_drvdata(d);
- struct ieee80211_channel *channels = NULL;
- const struct ieee80211_supported_band *supp_band = NULL;
- int len = 0, i;
- int count = 0;
-
- if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status))
- return -EAGAIN;
-
- supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
- channels = supp_band->channels;
- count = supp_band->n_channels;
-
- len += sprintf(&buf[len],
- "Displaying %d channels in 2.4GHz band "
- "(802.11bg):\n", count);
-
- for (i = 0; i < count; i++)
- len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n",
- ieee80211_frequency_to_channel(
- channels[i].center_freq),
- channels[i].max_power,
- channels[i].flags & IEEE80211_CHAN_RADAR ?
- " (IEEE 802.11h required)" : "",
- (!(channels[i].flags & IEEE80211_CHAN_NO_IBSS)
- || (channels[i].flags &
- IEEE80211_CHAN_RADAR)) ? "" :
- ", IBSS",
- channels[i].flags &
- IEEE80211_CHAN_PASSIVE_SCAN ?
- "passive only" : "active/passive");
-
- supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
- channels = supp_band->channels;
- count = supp_band->n_channels;
-
- len += sprintf(&buf[len], "Displaying %d channels in 5.2GHz band "
- "(802.11a):\n", count);
-
- for (i = 0; i < count; i++)
- len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n",
- ieee80211_frequency_to_channel(
- channels[i].center_freq),
- channels[i].max_power,
- channels[i].flags & IEEE80211_CHAN_RADAR ?
- " (IEEE 802.11h required)" : "",
- ((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
- || (channels[i].flags &
- IEEE80211_CHAN_RADAR)) ? "" :
- ", IBSS",
- channels[i].flags &
- IEEE80211_CHAN_PASSIVE_SCAN ?
- "passive only" : "active/passive");
-
- return len;
-}
-
-static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL);
static ssize_t show_statistics(struct device *d,
struct device_attribute *attr, char *buf)
@@ -4086,12 +3675,11 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
init_waitqueue_head(&priv->wait_command_queue);
- INIT_WORK(&priv->up, iwl4965_bg_up);
- INIT_WORK(&priv->restart, iwl4965_bg_restart);
- INIT_WORK(&priv->rx_replenish, iwl4965_bg_rx_replenish);
- INIT_WORK(&priv->rf_kill, iwl4965_bg_rf_kill);
- INIT_WORK(&priv->beacon_update, iwl4965_bg_beacon_update);
- INIT_WORK(&priv->set_monitor, iwl4965_bg_set_monitor);
+ INIT_WORK(&priv->up, iwl_bg_up);
+ INIT_WORK(&priv->restart, iwl_bg_restart);
+ INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish);
+ INIT_WORK(&priv->rf_kill, iwl_bg_rf_kill);
+ INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
@@ -4104,10 +3692,10 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
init_timer(&priv->statistics_periodic);
priv->statistics_periodic.data = (unsigned long)priv;
- priv->statistics_periodic.function = iwl4965_bg_statistics_periodic;
+ priv->statistics_periodic.function = iwl_bg_statistics_periodic;
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
- iwl4965_irq_tasklet, (unsigned long)priv);
+ iwl_irq_tasklet, (unsigned long)priv);
}
static void iwl_cancel_deferred_work(struct iwl_priv *priv)
@@ -4123,13 +3711,9 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
del_timer_sync(&priv->statistics_periodic);
}
-static struct attribute *iwl4965_sysfs_entries[] = {
- &dev_attr_channels.attr,
+static struct attribute *iwl_sysfs_entries[] = {
&dev_attr_flags.attr,
&dev_attr_filter_flags.attr,
-#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
- &dev_attr_measurement.attr,
-#endif
&dev_attr_power_level.attr,
&dev_attr_retry_rate.attr,
&dev_attr_statistics.attr,
@@ -4144,39 +3728,38 @@ static struct attribute *iwl4965_sysfs_entries[] = {
NULL
};
-static struct attribute_group iwl4965_attribute_group = {
+static struct attribute_group iwl_attribute_group = {
.name = NULL, /* put in device directory */
- .attrs = iwl4965_sysfs_entries,
+ .attrs = iwl_sysfs_entries,
};
-static struct ieee80211_ops iwl4965_hw_ops = {
- .tx = iwl4965_mac_tx,
- .start = iwl4965_mac_start,
- .stop = iwl4965_mac_stop,
- .add_interface = iwl4965_mac_add_interface,
- .remove_interface = iwl4965_mac_remove_interface,
- .config = iwl4965_mac_config,
- .config_interface = iwl4965_mac_config_interface,
- .configure_filter = iwl4965_configure_filter,
- .set_key = iwl4965_mac_set_key,
- .update_tkip_key = iwl4965_mac_update_tkip_key,
- .get_stats = iwl4965_mac_get_stats,
- .get_tx_stats = iwl4965_mac_get_tx_stats,
- .conf_tx = iwl4965_mac_conf_tx,
- .reset_tsf = iwl4965_mac_reset_tsf,
- .bss_info_changed = iwl4965_bss_info_changed,
- .ampdu_action = iwl4965_mac_ampdu_action,
+static struct ieee80211_ops iwl_hw_ops = {
+ .tx = iwl_mac_tx,
+ .start = iwl_mac_start,
+ .stop = iwl_mac_stop,
+ .add_interface = iwl_mac_add_interface,
+ .remove_interface = iwl_mac_remove_interface,
+ .config = iwl_mac_config,
+ .config_interface = iwl_mac_config_interface,
+ .configure_filter = iwl_configure_filter,
+ .set_key = iwl_mac_set_key,
+ .update_tkip_key = iwl_mac_update_tkip_key,
+ .get_stats = iwl_mac_get_stats,
+ .get_tx_stats = iwl_mac_get_tx_stats,
+ .conf_tx = iwl_mac_conf_tx,
+ .reset_tsf = iwl_mac_reset_tsf,
+ .bss_info_changed = iwl_bss_info_changed,
+ .ampdu_action = iwl_mac_ampdu_action,
.hw_scan = iwl_mac_hw_scan
};
-static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err = 0;
struct iwl_priv *priv;
struct ieee80211_hw *hw;
struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
unsigned long flags;
- DECLARE_MAC_BUF(mac);
/************************
* 1. Allocating HW data
@@ -4188,10 +3771,10 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
if (cfg->mod_params->debug & IWL_DL_INFO)
dev_printk(KERN_DEBUG, &(pdev->dev),
"Disabling hw_scan\n");
- iwl4965_hw_ops.hw_scan = NULL;
+ iwl_hw_ops.hw_scan = NULL;
}
- hw = iwl_alloc_all(cfg, &iwl4965_hw_ops);
+ hw = iwl_alloc_all(cfg, &iwl_hw_ops);
if (!hw) {
err = -ENOMEM;
goto out;
@@ -4285,7 +3868,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
/* extract MAC Address */
iwl_eeprom_get_mac(priv, priv->mac_addr);
- IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr));
+ IWL_DEBUG_INFO("MAC address: %pM\n", priv->mac_addr);
SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
/************************
@@ -4319,10 +3902,10 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
* 8. Setup services
********************/
spin_lock_irqsave(&priv->lock, flags);
- iwl4965_disable_interrupts(priv);
+ iwl_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->lock, flags);
- err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group);
+ err = sysfs_create_group(&pdev->dev.kobj, &iwl_attribute_group);
if (err) {
IWL_ERROR("failed to create sysfs device attributes\n");
goto out_uninit_drv;
@@ -4358,7 +3941,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
return 0;
out_remove_sysfs:
- sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group);
+ sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
out_uninit_drv:
iwl_uninit_drv(priv);
out_free_eeprom:
@@ -4376,7 +3959,7 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
return err;
}
-static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
+static void __devexit iwl_pci_remove(struct pci_dev *pdev)
{
struct iwl_priv *priv = pci_get_drvdata(pdev);
unsigned long flags;
@@ -4387,10 +3970,10 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
IWL_DEBUG_INFO("*** UNLOAD DRIVER ***\n");
iwl_dbgfs_unregister(priv);
- sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group);
+ sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
- /* ieee80211_unregister_hw call wil cause iwl4965_mac_stop to
- * to be called and iwl4965_down since we are removing the device
+ /* ieee80211_unregister_hw call wil cause iwl_mac_stop to
+ * to be called and iwl_down since we are removing the device
* we need to set STATUS_EXIT_PENDING bit.
*/
set_bit(STATUS_EXIT_PENDING, &priv->status);
@@ -4398,20 +3981,20 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
ieee80211_unregister_hw(priv->hw);
priv->mac80211_registered = 0;
} else {
- iwl4965_down(priv);
+ iwl_down(priv);
}
/* make sure we flush any pending irq or
* tasklet for the driver
*/
spin_lock_irqsave(&priv->lock, flags);
- iwl4965_disable_interrupts(priv);
+ iwl_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->lock, flags);
iwl_synchronize_irq(priv);
iwl_rfkill_unregister(priv);
- iwl4965_dealloc_ucode_pci(priv);
+ iwl_dealloc_ucode_pci(priv);
if (priv->rxq.bd)
iwl_rx_queue_free(priv, &priv->rxq);
@@ -4424,7 +4007,7 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
/*netif_stop_queue(dev); */
flush_workqueue(priv->workqueue);
- /* ieee80211_unregister_hw calls iwl4965_mac_stop, which flushes
+ /* ieee80211_unregister_hw calls iwl_mac_stop, which flushes
* priv->workqueue... so we can't take down the workqueue
* until now... */
destroy_workqueue(priv->workqueue);
@@ -4445,13 +4028,13 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
-static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct iwl_priv *priv = pci_get_drvdata(pdev);
if (priv->is_open) {
set_bit(STATUS_IN_SUSPEND, &priv->status);
- iwl4965_mac_stop(priv->hw);
+ iwl_mac_stop(priv->hw);
priv->is_open = 1;
}
@@ -4460,14 +4043,14 @@ static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state)
return 0;
}
-static int iwl4965_pci_resume(struct pci_dev *pdev)
+static int iwl_pci_resume(struct pci_dev *pdev)
{
struct iwl_priv *priv = pci_get_drvdata(pdev);
pci_set_power_state(pdev, PCI_D0);
if (priv->is_open)
- iwl4965_mac_start(priv->hw);
+ iwl_mac_start(priv->hw);
clear_bit(STATUS_IN_SUSPEND, &priv->status);
return 0;
@@ -4502,7 +4085,11 @@ static struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)},
{IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)},
{IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)},
+/* 5150 Wifi/WiMax */
+ {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)},
+ {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)},
#endif /* CONFIG_IWL5000 */
+
{0}
};
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
@@ -4510,15 +4097,15 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
static struct pci_driver iwl_driver = {
.name = DRV_NAME,
.id_table = iwl_hw_card_ids,
- .probe = iwl4965_pci_probe,
- .remove = __devexit_p(iwl4965_pci_remove),
+ .probe = iwl_pci_probe,
+ .remove = __devexit_p(iwl_pci_remove),
#ifdef CONFIG_PM
- .suspend = iwl4965_pci_suspend,
- .resume = iwl4965_pci_resume,
+ .suspend = iwl_pci_suspend,
+ .resume = iwl_pci_resume,
#endif
};
-static int __init iwl4965_init(void)
+static int __init iwl_init(void)
{
int ret;
@@ -4544,11 +4131,11 @@ error_register:
return ret;
}
-static void __exit iwl4965_exit(void)
+static void __exit iwl_exit(void)
{
pci_unregister_driver(&iwl_driver);
iwlagn_rate_control_unregister();
}
-module_exit(iwl4965_exit);
-module_init(iwl4965_init);
+module_exit(iwl_exit);
+module_init(iwl_init);
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c
index 72fbf47229db..f836ecc55758 100644
--- a/drivers/net/wireless/iwlwifi/iwl-calib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.c
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -70,7 +70,16 @@
* INIT calibrations framework
*****************************************************************************/
- int iwl_send_calib_results(struct iwl_priv *priv)
+struct statistics_general_data {
+ u32 beacon_silence_rssi_a;
+ u32 beacon_silence_rssi_b;
+ u32 beacon_silence_rssi_c;
+ u32 beacon_energy_a;
+ u32 beacon_energy_b;
+ u32 beacon_energy_c;
+};
+
+int iwl_send_calib_results(struct iwl_priv *priv)
{
int ret = 0;
int i = 0;
@@ -80,14 +89,16 @@
.meta.flags = CMD_SIZE_HUGE,
};
- for (i = 0; i < IWL_CALIB_MAX; i++)
- if (priv->calib_results[i].buf) {
+ for (i = 0; i < IWL_CALIB_MAX; i++) {
+ if ((BIT(i) & priv->hw_params.calib_init_cfg) &&
+ priv->calib_results[i].buf) {
hcmd.len = priv->calib_results[i].buf_len;
hcmd.data = priv->calib_results[i].buf;
ret = iwl_send_cmd_sync(priv, &hcmd);
if (ret)
goto err;
}
+ }
return 0;
err:
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.h b/drivers/net/wireless/iwlwifi/iwl-calib.h
index 94c8e316382a..1abe84bb74ad 100644
--- a/drivers/net/wireless/iwlwifi/iwl-calib.h
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 8d04e966ad48..52966ffbef6e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -66,8 +66,14 @@
* Please use iwl-dev.h for driver implementation definitions.
*/
-#ifndef __iwl4965_commands_h__
-#define __iwl4965_commands_h__
+#ifndef __iwl_commands_h__
+#define __iwl_commands_h__
+
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
enum {
REPLY_ALIVE = 0x1,
@@ -88,6 +94,7 @@ enum {
REPLY_WEPKEY = 0x20,
/* RX, TX, LEDs */
+ REPLY_3945_RX = 0x1b, /* 3945 only */
REPLY_TX = 0x1c,
REPLY_RATE_SCALE = 0x47, /* 3945 only */
REPLY_LEDS_CMD = 0x48,
@@ -98,6 +105,11 @@ enum {
COEX_MEDIUM_NOTIFICATION = 0x5b,
COEX_EVENT_CMD = 0x5c,
+ /* Calibration */
+ CALIBRATION_CFG_CMD = 0x65,
+ CALIBRATION_RES_NOTIFICATION = 0x66,
+ CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
+
/* 802.11h related */
RADAR_NOTIFICATION = 0x70, /* not used */
REPLY_QUIET_CMD = 0x71, /* not used */
@@ -129,7 +141,7 @@ enum {
REPLY_TX_POWER_DBM_CMD = 0x98,
MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
- /* Bluetooth device coexistance config command */
+ /* Bluetooth device coexistence config command */
REPLY_BT_CONFIG = 0x9b,
/* Statistics */
@@ -167,8 +179,8 @@ enum {
#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8)
#define SEQ_TO_INDEX(s) ((s) & 0xff)
#define INDEX_TO_SEQ(i) ((i) & 0xff)
-#define SEQ_HUGE_FRAME __constant_cpu_to_le16(0x4000)
-#define SEQ_RX_FRAME __constant_cpu_to_le16(0x8000)
+#define SEQ_HUGE_FRAME cpu_to_le16(0x4000)
+#define SEQ_RX_FRAME cpu_to_le16(0x8000)
/**
* struct iwl_cmd_header
@@ -180,7 +192,7 @@ struct iwl_cmd_header {
u8 cmd; /* Command ID: REPLY_RXON, etc. */
u8 flags; /* 0:5 reserved, 6 abort, 7 internal */
/*
- * The driver sets up the sequence number to values of its chosing.
+ * The driver sets up the sequence number to values of its choosing.
* uCode does not use this value, but passes it back to the driver
* when sending the response to each driver-originated command, so
* the driver can match the response to the command. Since the values
@@ -208,10 +220,11 @@ struct iwl_cmd_header {
} __attribute__ ((packed));
/**
- * 4965 rate_n_flags bit fields
+ * iwlagn rate_n_flags bit fields
*
- * rate_n_flags format is used in following 4965 commands:
+ * rate_n_flags format is used in following iwlagn commands:
* REPLY_RX (response only)
+ * REPLY_RX_MPDU (response only)
* REPLY_TX (both command and response)
* REPLY_TX_LINK_QUALITY_CMD
*
@@ -225,8 +238,9 @@ struct iwl_cmd_header {
* 6) 54 Mbps
* 7) 60 Mbps
*
- * 3: 0) Single stream (SISO)
+ * 4-3: 0) Single stream (SISO)
* 1) Dual stream (MIMO)
+ * 2) Triple stream (MIMO)
*
* 5: Value of 0x20 in bits 7:0 indicates 6 Mbps FAT duplicate data
*
@@ -247,8 +261,8 @@ struct iwl_cmd_header {
* 110) 11 Mbps
*/
#define RATE_MCS_CODE_MSK 0x7
-#define RATE_MCS_MIMO_POS 3
-#define RATE_MCS_MIMO_MSK 0x8
+#define RATE_MCS_SPATIAL_POS 3
+#define RATE_MCS_SPATIAL_MSK 0x18
#define RATE_MCS_HT_DUP_POS 5
#define RATE_MCS_HT_DUP_MSK 0x20
@@ -278,18 +292,20 @@ struct iwl_cmd_header {
#define RATE_MCS_SGI_MSK 0x2000
/**
- * rate_n_flags Tx antenna masks (4965 has 2 transmitters):
- * bit14:15 01 B inactive, A active
- * 10 B active, A inactive
- * 11 Both active
+ * rate_n_flags Tx antenna masks
+ * 4965 has 2 transmitters
+ * 5100 has 1 transmitter B
+ * 5150 has 1 transmitter A
+ * 5300 has 3 transmitters
+ * 5350 has 3 transmitters
+ * bit14:16
*/
#define RATE_MCS_ANT_POS 14
#define RATE_MCS_ANT_A_MSK 0x04000
#define RATE_MCS_ANT_B_MSK 0x08000
#define RATE_MCS_ANT_C_MSK 0x10000
#define RATE_MCS_ANT_ABC_MSK 0x1C000
-
-#define RATE_MCS_ANT_INIT_IND 1
+#define RATE_ANT_NUM 3
#define POWER_TABLE_NUM_ENTRIES 33
#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32
@@ -340,7 +356,7 @@ struct iwl4965_tx_power_db {
} __attribute__ ((packed));
/**
- * Commad REPLY_TX_POWER_DBM_CMD = 0x98
+ * Command REPLY_TX_POWER_DBM_CMD = 0x98
* struct iwl5000_tx_power_dbm_cmd
*/
#define IWL50_TX_POWER_AUTO 0x7f
@@ -359,7 +375,7 @@ struct iwl5000_tx_power_dbm_cmd {
*
*****************************************************************************/
-#define UCODE_VALID_OK __constant_cpu_to_le32(0x1)
+#define UCODE_VALID_OK cpu_to_le32(0x1)
#define INITIALIZE_SUBTYPE (9)
/*
@@ -376,7 +392,7 @@ struct iwl5000_tx_power_dbm_cmd {
* calculating txpower settings:
*
* 1) Power supply voltage indication. The voltage sensor outputs higher
- * values for lower voltage, and vice versa.
+ * values for lower voltage, and vice verse.
*
* 2) Temperature measurement parameters, for each of two channel widths
* (20 MHz and 40 MHz) supported by the radios. Temperature sensing
@@ -477,11 +493,6 @@ struct iwl_alive_resp {
} __attribute__ ((packed));
-union tsf {
- u8 byte[8];
- __le16 word[4];
- __le32 dw[2];
-};
/*
* REPLY_ERROR = 0x2 (response only, not a command)
@@ -492,7 +503,7 @@ struct iwl_error_resp {
u8 reserved1;
__le16 bad_cmd_seq_num;
__le32 error_info;
- union tsf timestamp;
+ __le64 timestamp;
} __attribute__ ((packed));
/******************************************************************************
@@ -513,75 +524,75 @@ enum {
};
-#define RXON_RX_CHAIN_DRIVER_FORCE_MSK __constant_cpu_to_le16(0x1 << 0)
-#define RXON_RX_CHAIN_VALID_MSK __constant_cpu_to_le16(0x7 << 1)
+#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0)
+#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1)
#define RXON_RX_CHAIN_VALID_POS (1)
-#define RXON_RX_CHAIN_FORCE_SEL_MSK __constant_cpu_to_le16(0x7 << 4)
+#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4)
#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
-#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK __constant_cpu_to_le16(0x7 << 7)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7)
#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
-#define RXON_RX_CHAIN_CNT_MSK __constant_cpu_to_le16(0x3 << 10)
+#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10)
#define RXON_RX_CHAIN_CNT_POS (10)
-#define RXON_RX_CHAIN_MIMO_CNT_MSK __constant_cpu_to_le16(0x3 << 12)
+#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12)
#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
-#define RXON_RX_CHAIN_MIMO_FORCE_MSK __constant_cpu_to_le16(0x1 << 14)
+#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14)
#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
/* rx_config flags */
/* band & modulation selection */
-#define RXON_FLG_BAND_24G_MSK __constant_cpu_to_le32(1 << 0)
-#define RXON_FLG_CCK_MSK __constant_cpu_to_le32(1 << 1)
+#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0)
+#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1)
/* auto detection enable */
-#define RXON_FLG_AUTO_DETECT_MSK __constant_cpu_to_le32(1 << 2)
+#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2)
/* TGg protection when tx */
-#define RXON_FLG_TGG_PROTECT_MSK __constant_cpu_to_le32(1 << 3)
+#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3)
/* cck short slot & preamble */
-#define RXON_FLG_SHORT_SLOT_MSK __constant_cpu_to_le32(1 << 4)
-#define RXON_FLG_SHORT_PREAMBLE_MSK __constant_cpu_to_le32(1 << 5)
+#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4)
+#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5)
/* antenna selection */
-#define RXON_FLG_DIS_DIV_MSK __constant_cpu_to_le32(1 << 7)
-#define RXON_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0x0f00)
-#define RXON_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
-#define RXON_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
+#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7)
+#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00)
+#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
+#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
/* radar detection enable */
-#define RXON_FLG_RADAR_DETECT_MSK __constant_cpu_to_le32(1 << 12)
-#define RXON_FLG_TGJ_NARROW_BAND_MSK __constant_cpu_to_le32(1 << 13)
+#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12)
+#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13)
/* rx response to host with 8-byte TSF
* (according to ON_AIR deassertion) */
-#define RXON_FLG_TSF2HOST_MSK __constant_cpu_to_le32(1 << 15)
+#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15)
/* HT flags */
#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22)
-#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK __constant_cpu_to_le32(0x1 << 22)
+#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22)
#define RXON_FLG_HT_OPERATING_MODE_POS (23)
-#define RXON_FLG_HT_PROT_MSK __constant_cpu_to_le32(0x1 << 23)
-#define RXON_FLG_FAT_PROT_MSK __constant_cpu_to_le32(0x2 << 23)
+#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23)
+#define RXON_FLG_FAT_PROT_MSK cpu_to_le32(0x2 << 23)
#define RXON_FLG_CHANNEL_MODE_POS (25)
-#define RXON_FLG_CHANNEL_MODE_MSK __constant_cpu_to_le32(0x3 << 25)
-#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK __constant_cpu_to_le32(0x1 << 25)
-#define RXON_FLG_CHANNEL_MODE_MIXED_MSK __constant_cpu_to_le32(0x2 << 25)
+#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25)
+#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK cpu_to_le32(0x1 << 25)
+#define RXON_FLG_CHANNEL_MODE_MIXED_MSK cpu_to_le32(0x2 << 25)
/* CTS to self (if spec allows) flag */
-#define RXON_FLG_SELF_CTS_EN __constant_cpu_to_le32(0x1<<30)
+#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30)
/* rx_config filter flags */
/* accept all data frames */
-#define RXON_FILTER_PROMISC_MSK __constant_cpu_to_le32(1 << 0)
+#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0)
/* pass control & management to host */
-#define RXON_FILTER_CTL2HOST_MSK __constant_cpu_to_le32(1 << 1)
+#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1)
/* accept multi-cast */
-#define RXON_FILTER_ACCEPT_GRP_MSK __constant_cpu_to_le32(1 << 2)
+#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2)
/* don't decrypt uni-cast frames */
-#define RXON_FILTER_DIS_DECRYPT_MSK __constant_cpu_to_le32(1 << 3)
+#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3)
/* don't decrypt multi-cast frames */
-#define RXON_FILTER_DIS_GRP_DECRYPT_MSK __constant_cpu_to_le32(1 << 4)
+#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4)
/* STA is associated */
-#define RXON_FILTER_ASSOC_MSK __constant_cpu_to_le32(1 << 5)
+#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5)
/* transfer to host non bssid beacons in associated state */
-#define RXON_FILTER_BCON_AWARE_MSK __constant_cpu_to_le32(1 << 6)
+#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6)
/**
* REPLY_RXON = 0x10 (command, has simple generic response)
@@ -620,7 +631,7 @@ struct iwl4965_rxon_cmd {
u8 ofdm_ht_dual_stream_basic_rates;
} __attribute__ ((packed));
-/* 5000 HW just extend this cmmand */
+/* 5000 HW just extend this command */
struct iwl_rxon_cmd {
u8 node_addr[6];
__le16 reserved1;
@@ -679,8 +690,8 @@ struct iwl4965_rxon_assoc_cmd {
/*
* REPLY_RXON_TIMING = 0x14 (command, has simple generic response)
*/
-struct iwl4965_rxon_time_cmd {
- union tsf timestamp;
+struct iwl_rxon_time_cmd {
+ __le64 timestamp;
__le16 beacon_interval;
__le16 atim_window;
__le32 beacon_init_val;
@@ -691,7 +702,7 @@ struct iwl4965_rxon_time_cmd {
/*
* REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response)
*/
-struct iwl4965_channel_switch_cmd {
+struct iwl_channel_switch_cmd {
u8 band;
u8 expect_beacon;
__le16 channel;
@@ -704,7 +715,7 @@ struct iwl4965_channel_switch_cmd {
/*
* CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command)
*/
-struct iwl4965_csa_notification {
+struct iwl_csa_notification {
__le16 band;
__le16 channel;
__le32 status; /* 0 - OK, 1 - fail */
@@ -741,9 +752,9 @@ struct iwl_ac_qos {
} __attribute__ ((packed));
/* QoS flags defines */
-#define QOS_PARAM_FLG_UPDATE_EDCA_MSK __constant_cpu_to_le32(0x01)
-#define QOS_PARAM_FLG_TGN_MSK __constant_cpu_to_le32(0x02)
-#define QOS_PARAM_FLG_TXOP_TYPE_MSK __constant_cpu_to_le32(0x10)
+#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01)
+#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02)
+#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10)
/* Number of Access Categories (AC) (EDCA), queues 0..3 */
#define AC_NUM 4
@@ -780,34 +791,34 @@ struct iwl_qosparam_cmd {
#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/
#define IWL_INVALID_STATION 255
-#define STA_FLG_PWR_SAVE_MSK __constant_cpu_to_le32(1 << 8);
-#define STA_FLG_RTS_MIMO_PROT_MSK __constant_cpu_to_le32(1 << 17)
-#define STA_FLG_AGG_MPDU_8US_MSK __constant_cpu_to_le32(1 << 18)
+#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8);
+#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17)
+#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18)
#define STA_FLG_MAX_AGG_SIZE_POS (19)
-#define STA_FLG_MAX_AGG_SIZE_MSK __constant_cpu_to_le32(3 << 19)
-#define STA_FLG_FAT_EN_MSK __constant_cpu_to_le32(1 << 21)
-#define STA_FLG_MIMO_DIS_MSK __constant_cpu_to_le32(1 << 22)
+#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19)
+#define STA_FLG_FAT_EN_MSK cpu_to_le32(1 << 21)
+#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22)
#define STA_FLG_AGG_MPDU_DENSITY_POS (23)
-#define STA_FLG_AGG_MPDU_DENSITY_MSK __constant_cpu_to_le32(7 << 23)
+#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23)
/* Use in mode field. 1: modify existing entry, 0: add new station entry */
#define STA_CONTROL_MODIFY_MSK 0x01
/* key flags __le16*/
-#define STA_KEY_FLG_ENCRYPT_MSK __constant_cpu_to_le16(0x0007)
-#define STA_KEY_FLG_NO_ENC __constant_cpu_to_le16(0x0000)
-#define STA_KEY_FLG_WEP __constant_cpu_to_le16(0x0001)
-#define STA_KEY_FLG_CCMP __constant_cpu_to_le16(0x0002)
-#define STA_KEY_FLG_TKIP __constant_cpu_to_le16(0x0003)
+#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007)
+#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000)
+#define STA_KEY_FLG_WEP cpu_to_le16(0x0001)
+#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002)
+#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003)
#define STA_KEY_FLG_KEYID_POS 8
-#define STA_KEY_FLG_INVALID __constant_cpu_to_le16(0x0800)
+#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800)
/* wep key is either from global key (0) or from station info array (1) */
-#define STA_KEY_FLG_MAP_KEY_MSK __constant_cpu_to_le16(0x0008)
+#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008)
/* wep key in STA: 5-bytes (0) or 13-bytes (1) */
-#define STA_KEY_FLG_KEY_SIZE_MSK __constant_cpu_to_le16(0x1000)
-#define STA_KEY_MULTICAST_MSK __constant_cpu_to_le16(0x4000)
+#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000)
+#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000)
#define STA_KEY_MAX_NUM 8
/* Flags indicate whether to modify vs. don't change various station params */
@@ -1013,33 +1024,14 @@ struct iwl_wep_cmd {
*
*****************************************************************************/
-struct iwl4965_rx_frame_stats {
- u8 phy_count;
- u8 id;
- u8 rssi;
- u8 agc;
- __le16 sig_avg;
- __le16 noise_diff;
- u8 payload[0];
-} __attribute__ ((packed));
-
-struct iwl4965_rx_frame_hdr {
- __le16 channel;
- __le16 phy_flags;
- u8 reserved1;
- u8 rate;
- __le16 len;
- u8 payload[0];
-} __attribute__ ((packed));
-
-#define RX_RES_STATUS_NO_CRC32_ERROR __constant_cpu_to_le32(1 << 0)
-#define RX_RES_STATUS_NO_RXE_OVERFLOW __constant_cpu_to_le32(1 << 1)
+#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0)
+#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1)
-#define RX_RES_PHY_FLAGS_BAND_24_MSK __constant_cpu_to_le16(1 << 0)
-#define RX_RES_PHY_FLAGS_MOD_CCK_MSK __constant_cpu_to_le16(1 << 1)
-#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK __constant_cpu_to_le16(1 << 2)
-#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK __constant_cpu_to_le16(1 << 3)
-#define RX_RES_PHY_FLAGS_ANTENNA_MSK __constant_cpu_to_le16(0xf0)
+#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0)
+#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1)
+#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2)
+#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3)
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK cpu_to_le16(0xf0)
#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8)
#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8)
@@ -1062,26 +1054,6 @@ struct iwl4965_rx_frame_hdr {
#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7)
#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800)
-struct iwl4965_rx_frame_end {
- __le32 status;
- __le64 timestamp;
- __le32 beacon_timestamp;
-} __attribute__ ((packed));
-
-/*
- * REPLY_3945_RX = 0x1b (response only, not a command)
- *
- * NOTE: DO NOT dereference from casts to this structure
- * It is provided only for calculating minimum data set size.
- * The actual offsets of the hdr and end are dynamic based on
- * stats.phy_count
- */
-struct iwl4965_rx_frame {
- struct iwl4965_rx_frame_stats stats;
- struct iwl4965_rx_frame_hdr hdr;
- struct iwl4965_rx_frame_end end;
-} __attribute__ ((packed));
-
/* Fixed (non-configurable) rx data from phy */
#define IWL49_RX_RES_PHY_CNT 14
@@ -1111,7 +1083,7 @@ struct iwl4965_rx_non_cfg_phy {
#define IWL50_OFDM_RSSI_C_BIT_POS 0
struct iwl5000_non_cfg_phy {
- __le32 non_cfg_phy[IWL50_RX_RES_PHY_CNT]; /* upto 8 phy entries */
+ __le32 non_cfg_phy[IWL50_RX_RES_PHY_CNT]; /* up to 8 phy entries */
} __attribute__ ((packed));
@@ -1167,24 +1139,24 @@ struct iwl4965_rx_mpdu_res_start {
/* REPLY_TX Tx flags field */
-/* 1: Use RTS/CTS protocol or CTS-to-self if spec alows it
+/* 1: Use RTS/CTS protocol or CTS-to-self if spec allows it
* before this frame. if CTS-to-self required check
* RXON_FLG_SELF_CTS_EN status. */
-#define TX_CMD_FLG_RTS_CTS_MSK __constant_cpu_to_le32(1 << 0)
+#define TX_CMD_FLG_RTS_CTS_MSK cpu_to_le32(1 << 0)
/* 1: Use Request-To-Send protocol before this frame.
* Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. */
-#define TX_CMD_FLG_RTS_MSK __constant_cpu_to_le32(1 << 1)
+#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1)
/* 1: Transmit Clear-To-Send to self before this frame.
* Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames.
* Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. */
-#define TX_CMD_FLG_CTS_MSK __constant_cpu_to_le32(1 << 2)
+#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2)
/* 1: Expect ACK from receiving station
* 0: Don't expect ACK (MAC header's duration field s/b 0)
* Set this for unicast frames, but not broadcast/multicast. */
-#define TX_CMD_FLG_ACK_MSK __constant_cpu_to_le32(1 << 3)
+#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3)
/* For 4965:
* 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD).
@@ -1192,40 +1164,40 @@ struct iwl4965_rx_mpdu_res_start {
* uCode walks through table for additional Tx attempts.
* 0: Use Tx rate/MCS from Tx command's rate_n_flags field.
* This rate will be used for all Tx attempts; it will not be scaled. */
-#define TX_CMD_FLG_STA_RATE_MSK __constant_cpu_to_le32(1 << 4)
+#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4)
/* 1: Expect immediate block-ack.
* Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */
-#define TX_CMD_FLG_IMM_BA_RSP_MASK __constant_cpu_to_le32(1 << 6)
+#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6)
/* 1: Frame requires full Tx-Op protection.
* Set this if either RTS or CTS Tx Flag gets set. */
-#define TX_CMD_FLG_FULL_TXOP_PROT_MSK __constant_cpu_to_le32(1 << 7)
+#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7)
/* Tx antenna selection field; used only for 3945, reserved (0) for 4965.
* Set field to "0" to allow 3945 uCode to select antenna (normal usage). */
-#define TX_CMD_FLG_ANT_SEL_MSK __constant_cpu_to_le32(0xf00)
-#define TX_CMD_FLG_ANT_A_MSK __constant_cpu_to_le32(1 << 8)
-#define TX_CMD_FLG_ANT_B_MSK __constant_cpu_to_le32(1 << 9)
+#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00)
+#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8)
+#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9)
/* 1: Ignore Bluetooth priority for this frame.
* 0: Delay Tx until Bluetooth device is done (normal usage). */
-#define TX_CMD_FLG_BT_DIS_MSK __constant_cpu_to_le32(1 << 12)
+#define TX_CMD_FLG_BT_DIS_MSK cpu_to_le32(1 << 12)
/* 1: uCode overrides sequence control field in MAC header.
* 0: Driver provides sequence control field in MAC header.
* Set this for management frames, non-QOS data frames, non-unicast frames,
* and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */
-#define TX_CMD_FLG_SEQ_CTL_MSK __constant_cpu_to_le32(1 << 13)
+#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13)
/* 1: This frame is non-last MPDU; more fragments are coming.
* 0: Last fragment, or not using fragmentation. */
-#define TX_CMD_FLG_MORE_FRAG_MSK __constant_cpu_to_le32(1 << 14)
+#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14)
/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame.
* 0: No TSF required in outgoing frame.
* Set this for transmitting beacons and probe responses. */
-#define TX_CMD_FLG_TSF_MSK __constant_cpu_to_le32(1 << 16)
+#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16)
/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword
* alignment of frame's payload data field.
@@ -1233,14 +1205,14 @@ struct iwl4965_rx_mpdu_res_start {
* Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4
* field (but not both). Driver must align frame data (i.e. data following
* MAC header) to DWORD boundary. */
-#define TX_CMD_FLG_MH_PAD_MSK __constant_cpu_to_le32(1 << 20)
+#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20)
/* accelerate aggregation support
* 0 - no CCMP encryption; 1 - CCMP encryption */
-#define TX_CMD_FLG_AGG_CCMP_MSK __constant_cpu_to_le32(1 << 22)
+#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22)
/* HCCA-AP - disable duration overwriting. */
-#define TX_CMD_FLG_DUR_MSK __constant_cpu_to_le32(1 << 25)
+#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25)
/*
@@ -1266,7 +1238,7 @@ struct iwl4965_rx_mpdu_res_start {
* Used for managing Tx retries when expecting block-acks.
* Driver should set these fields to 0.
*/
-struct iwl4965_dram_scratch {
+struct iwl_dram_scratch {
u8 try_cnt; /* Tx attempts */
u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */
__le16 reserved;
@@ -1297,9 +1269,9 @@ struct iwl_tx_cmd {
__le32 tx_flags; /* TX_CMD_FLG_* */
- /* 4965's uCode may modify this field of the Tx command (in host DRAM!).
+ /* uCode may modify this field of the Tx command (in host DRAM!).
* Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */
- struct iwl4965_dram_scratch scratch;
+ struct iwl_dram_scratch scratch;
/* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */
__le32 rate_n_flags; /* RATE_MCS_* */
@@ -1411,21 +1383,21 @@ enum {
};
enum {
- TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */
+ TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */
TX_STATUS_DELAY_MSK = 0x00000040,
TX_STATUS_ABORT_MSK = 0x00000080,
TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */
TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */
- TX_RESERVED = 0x00780000, /* bits 19:22 */
+ TX_RESERVED = 0x00780000, /* bits 19:22 */
TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */
TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */
};
-static inline int iwl_is_tx_success(u32 status)
+static inline bool iwl_is_tx_success(u32 status)
{
status &= TX_STATUS_MSK;
- return (status == TX_STATUS_SUCCESS)
- || (status == TX_STATUS_DIRECT_DONE);
+ return (status == TX_STATUS_SUCCESS) ||
+ (status == TX_STATUS_DIRECT_DONE);
}
@@ -1450,10 +1422,9 @@ enum {
AGG_TX_STATE_DELAY_TX_MSK = 0x400
};
-#define AGG_TX_STATE_LAST_SENT_MSK \
-(AGG_TX_STATE_LAST_SENT_TTL_MSK | \
- AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
- AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
+#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \
+ AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \
+ AGG_TX_STATE_LAST_SENT_BT_KILL_MSK)
/* # tx attempts for first frame in aggregation */
#define AGG_TX_STATE_TRY_CNT_POS 12
@@ -1526,6 +1497,28 @@ struct iwl4965_tx_resp {
} u;
} __attribute__ ((packed));
+/*
+ * definitions for initial rate index field
+ * bits [3:0] initial rate index
+ * bits [6:4] rate table color, used for the initial rate
+ * bit-7 invalid rate indication
+ * i.e. rate was not chosen from rate table
+ * or rate table color was changed during frame retries
+ * refer tlc rate info
+ */
+
+#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0
+#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f
+#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4
+#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70
+#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80
+
+/* refer to ra_tid */
+#define IWL50_TX_RES_TID_POS 0
+#define IWL50_TX_RES_TID_MSK 0x0f
+#define IWL50_TX_RES_RA_POS 4
+#define IWL50_TX_RES_RA_MSK 0xf0
+
struct iwl5000_tx_resp {
u8 frame_count; /* 1 no aggregation, >1 aggregation */
u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */
@@ -1540,14 +1533,17 @@ struct iwl5000_tx_resp {
* For agg: RTS + CTS + aggregation tx time + block-ack time. */
__le16 wireless_media_time; /* uSecs */
- __le16 reserved;
- __le32 pa_power1; /* RF power amplifier measurement (not used) */
- __le32 pa_power2;
+ u8 pa_status; /* RF power amplifier measurement (not used) */
+ u8 pa_integ_res_a[3];
+ u8 pa_integ_res_b[3];
+ u8 pa_integ_res_C[3];
__le32 tfd_info;
__le16 seq_ctl;
__le16 byte_cnt;
- __le32 tlc_info;
+ u8 tlc_info;
+ u8 ra_tid; /* tid (0:3), sta_id (4:7) */
+ __le16 frame_ctrl;
/*
* For non-agg: frame status TX_STATUS_*
* For agg: status of 1st frame, AGG_TX_STATE_*; other frame status
@@ -1742,7 +1738,7 @@ struct iwl_link_qual_agg_params {
* match the modulation characteristics of the history set.
*
* When using block-ack (aggregation), all frames are transmitted at the same
- * rate, since there is no per-attempt acknowledgement from the destination
+ * rate, since there is no per-attempt acknowledgment from the destination
* station. The Tx response struct iwl_tx_resp indicates the Tx rate in
* rate_n_flags field. After receiving a block-ack, the driver can update
* history for the entire block all at once.
@@ -1881,9 +1877,9 @@ struct iwl_link_quality_cmd {
*
* 3945 and 4965 support hardware handshake with Bluetooth device on
* same platform. Bluetooth device alerts wireless device when it will Tx;
- * wireless device can delay or kill its own Tx to accomodate.
+ * wireless device can delay or kill its own Tx to accommodate.
*/
-struct iwl4965_bt_cmd {
+struct iwl_bt_cmd {
u8 flags;
u8 lead_time;
u8 max_kill;
@@ -1909,18 +1905,18 @@ struct iwl4965_bt_cmd {
RXON_FILTER_ASSOC_MSK | \
RXON_FILTER_BCON_AWARE_MSK)
-struct iwl4965_measure_channel {
+struct iwl_measure_channel {
__le32 duration; /* measurement duration in extended beacon
* format */
u8 channel; /* channel to measure */
- u8 type; /* see enum iwl4965_measure_type */
+ u8 type; /* see enum iwl_measure_type */
__le16 reserved;
} __attribute__ ((packed));
/*
* REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command)
*/
-struct iwl4965_spectrum_cmd {
+struct iwl_spectrum_cmd {
__le16 len; /* number of bytes starting from token */
u8 token; /* token id */
u8 id; /* measurement id -- 0 or 1 */
@@ -1933,13 +1929,13 @@ struct iwl4965_spectrum_cmd {
__le32 filter_flags; /* rxon filter flags */
__le16 channel_count; /* minimum 1, maximum 10 */
__le16 reserved3;
- struct iwl4965_measure_channel channels[10];
+ struct iwl_measure_channel channels[10];
} __attribute__ ((packed));
/*
* REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response)
*/
-struct iwl4965_spectrum_resp {
+struct iwl_spectrum_resp {
u8 token;
u8 id; /* id of the prior command replaced, or 0xff */
__le16 status; /* 0 - command will be handled
@@ -1947,12 +1943,12 @@ struct iwl4965_spectrum_resp {
* measurement) */
} __attribute__ ((packed));
-enum iwl4965_measurement_state {
+enum iwl_measurement_state {
IWL_MEASUREMENT_START = 0,
IWL_MEASUREMENT_STOP = 1,
};
-enum iwl4965_measurement_status {
+enum iwl_measurement_status {
IWL_MEASUREMENT_OK = 0,
IWL_MEASUREMENT_CONCURRENT = 1,
IWL_MEASUREMENT_CSA_CONFLICT = 2,
@@ -1965,18 +1961,18 @@ enum iwl4965_measurement_status {
#define NUM_ELEMENTS_IN_HISTOGRAM 8
-struct iwl4965_measurement_histogram {
+struct iwl_measurement_histogram {
__le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */
__le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */
} __attribute__ ((packed));
/* clear channel availability counters */
-struct iwl4965_measurement_cca_counters {
+struct iwl_measurement_cca_counters {
__le32 ofdm;
__le32 cck;
} __attribute__ ((packed));
-enum iwl4965_measure_type {
+enum iwl_measure_type {
IWL_MEASURE_BASIC = (1 << 0),
IWL_MEASURE_CHANNEL_LOAD = (1 << 1),
IWL_MEASURE_HISTOGRAM_RPI = (1 << 2),
@@ -1989,7 +1985,7 @@ enum iwl4965_measure_type {
/*
* SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command)
*/
-struct iwl4965_spectrum_notification {
+struct iwl_spectrum_notification {
u8 id; /* measurement id -- 0 or 1 */
u8 token;
u8 channel_index; /* index in measurement channel list */
@@ -1997,7 +1993,7 @@ struct iwl4965_spectrum_notification {
__le32 start_time; /* lower 32-bits of TSF */
u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */
u8 channel;
- u8 type; /* see enum iwl4965_measurement_type */
+ u8 type; /* see enum iwl_measurement_type */
u8 reserved1;
/* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only
* valid if applicable for measurement type requested. */
@@ -2007,9 +2003,9 @@ struct iwl4965_spectrum_notification {
u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 -
* unidentified */
u8 reserved2[3];
- struct iwl4965_measurement_histogram histogram;
+ struct iwl_measurement_histogram histogram;
__le32 stop_time; /* lower 32-bits of TSF */
- __le32 status; /* see iwl4965_measurement_status */
+ __le32 status; /* see iwl_measurement_status */
} __attribute__ ((packed));
/******************************************************************************
@@ -2043,15 +2039,15 @@ struct iwl4965_spectrum_notification {
* '11' Illegal set
*
* NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then
- * ucode assume sleep over DTIM is allowed and we don't need to wakeup
+ * ucode assume sleep over DTIM is allowed and we don't need to wake up
* for every DTIM.
*/
#define IWL_POWER_VEC_SIZE 5
-#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK __constant_cpu_to_le16(1 << 0)
-#define IWL_POWER_SLEEP_OVER_DTIM_MSK __constant_cpu_to_le16(1 << 2)
-#define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le16(1 << 3)
-#define IWL_POWER_FAST_PD __constant_cpu_to_le16(1 << 4)
+#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(1 << 0)
+#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(1 << 2)
+#define IWL_POWER_PCI_PM_MSK cpu_to_le16(1 << 3)
+#define IWL_POWER_FAST_PD cpu_to_le16(1 << 4)
struct iwl_powertable_cmd {
__le16 flags;
@@ -2067,7 +2063,7 @@ struct iwl_powertable_cmd {
* PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command)
* 3945 and 4965 identical.
*/
-struct iwl4965_sleep_notification {
+struct iwl_sleep_notification {
u8 pm_sleep_mode;
u8 pm_wakeup_src;
__le16 reserved;
@@ -2097,14 +2093,14 @@ enum {
#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */
#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */
#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */
-struct iwl4965_card_state_cmd {
+struct iwl_card_state_cmd {
__le32 status; /* CARD_STATE_CMD_* request new power state */
} __attribute__ ((packed));
/*
* CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command)
*/
-struct iwl4965_card_state_notif {
+struct iwl_card_state_notif {
__le32 flags;
} __attribute__ ((packed));
@@ -2125,8 +2121,8 @@ struct iwl_ct_kill_config {
*
*****************************************************************************/
-#define SCAN_CHANNEL_TYPE_PASSIVE __constant_cpu_to_le32(0)
-#define SCAN_CHANNEL_TYPE_ACTIVE __constant_cpu_to_le32(1)
+#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0)
+#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1)
/**
* struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
@@ -2167,7 +2163,7 @@ struct iwl_scan_channel {
* struct iwl_ssid_ie - directed scan network information element
*
* Up to 4 of these may appear in REPLY_SCAN_CMD, selected by "type" field
- * in struct iwl4965_scan_channel; each channel may select different ssids from
+ * in struct iwl_scan_channel; each channel may select different ssids from
* among the 4 entries. SSID IEs get transmitted in reverse order of entry.
*/
struct iwl_ssid_ie {
@@ -2177,8 +2173,8 @@ struct iwl_ssid_ie {
} __attribute__ ((packed));
#define PROBE_OPTION_MAX 0x14
-#define TX_CMD_LIFE_TIME_INFINITE __constant_cpu_to_le32(0xFFFFFFFF)
-#define IWL_GOOD_CRC_TH __constant_cpu_to_le16(1)
+#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF)
+#define IWL_GOOD_CRC_TH cpu_to_le16(1)
#define IWL_MAX_SCAN_SIZE 1024
/*
@@ -2267,7 +2263,7 @@ struct iwl_scan_cmd {
* Number of channels in list is specified by channel_count.
* Each channel in list is of type:
*
- * struct iwl4965_scan_channel channels[0];
+ * struct iwl_scan_channel channels[0];
*
* NOTE: Only one band of channels can be scanned per pass. You
* must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
@@ -2278,7 +2274,7 @@ struct iwl_scan_cmd {
} __attribute__ ((packed));
/* Can abort will notify by complete notification with abort status. */
-#define CAN_ABORT_STATUS __constant_cpu_to_le32(0x1)
+#define CAN_ABORT_STATUS cpu_to_le32(0x1)
/* complete notification statuses */
#define ABORT_STATUS 0x2
@@ -2422,6 +2418,8 @@ struct statistics_rx_ht_phy {
__le32 reserved2;
} __attribute__ ((packed));
+#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
+
struct statistics_rx_non_phy {
__le32 bogus_cts; /* CTS received when not expecting CTS */
__le32 bogus_ack; /* ACK received when not expecting ACK */
@@ -2540,8 +2538,8 @@ struct statistics_general {
* STATISTICS_NOTIFICATIONs after received beacons (see below). This flag
* does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself.
*/
-#define IWL_STATS_CONF_CLEAR_STATS __constant_cpu_to_le32(0x1) /* see above */
-#define IWL_STATS_CONF_DISABLE_NOTIF __constant_cpu_to_le32(0x2)/* see above */
+#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */
+#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */
struct iwl_statistics_cmd {
__le32 configuration_flags; /* IWL_STATS_CONF_* */
} __attribute__ ((packed));
@@ -2561,8 +2559,8 @@ struct iwl_statistics_cmd {
* appropriately so that each notification contains statistics for only the
* one channel that has just been scanned.
*/
-#define STATISTICS_REPLY_FLG_BAND_24G_MSK __constant_cpu_to_le32(0x2)
-#define STATISTICS_REPLY_FLG_FAT_MODE_MSK __constant_cpu_to_le32(0x8)
+#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2)
+#define STATISTICS_REPLY_FLG_FAT_MODE_MSK cpu_to_le32(0x8)
struct iwl_notif_statistics {
__le32 flag;
struct statistics_rx rx;
@@ -2578,7 +2576,7 @@ struct iwl_notif_statistics {
* then this notification will be sent. */
#define CONSECUTIVE_MISSED_BCONS_TH 20
-struct iwl4965_missed_beacon_notif {
+struct iwl_missed_beacon_notif {
__le32 consequtive_missed_beacons;
__le32 total_missed_becons;
__le32 num_expected_beacons;
@@ -2778,8 +2776,8 @@ struct iwl4965_missed_beacon_notif {
#define HD_OFDM_ENERGY_TH_IN_INDEX (10)
/* Control field in struct iwl_sensitivity_cmd */
-#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE __constant_cpu_to_le16(0)
-#define SENSITIVITY_CMD_CONTROL_WORK_TABLE __constant_cpu_to_le16(1)
+#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0)
+#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1)
/**
* struct iwl_sensitivity_cmd
@@ -2849,56 +2847,26 @@ struct iwl_sensitivity_cmd {
* 1-0: amount of gain, units of 1.5 dB
*/
-/* "Differential Gain" opcode used in REPLY_PHY_CALIBRATION_CMD. */
-#define PHY_CALIBRATE_DIFF_GAIN_CMD (7)
-
-struct iwl4965_calibration_cmd {
- u8 opCode; /* PHY_CALIBRATE_DIFF_GAIN_CMD (7) */
- u8 flags; /* not used */
- __le16 reserved;
- s8 diff_gain_a; /* see above */
- s8 diff_gain_b;
- s8 diff_gain_c;
- u8 reserved1;
-} __attribute__ ((packed));
-
-/* Phy calibration command for 5000 series */
-
-enum {
- IWL5000_PHY_CALIBRATE_DC_CMD = 8,
- IWL5000_PHY_CALIBRATE_LO_CMD = 9,
- IWL5000_PHY_CALIBRATE_RX_BB_CMD = 10,
- IWL5000_PHY_CALIBRATE_TX_IQ_CMD = 11,
- IWL5000_PHY_CALIBRATE_RX_IQ_CMD = 12,
- IWL5000_PHY_CALIBRATION_NOISE_CMD = 13,
- IWL5000_PHY_CALIBRATE_AGC_TABLE_CMD = 14,
- IWL5000_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
- IWL5000_PHY_CALIBRATE_BASE_BAND_CMD = 16,
- IWL5000_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17,
- IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18,
- IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19,
-};
+/* Phy calibration command for series */
enum {
- CALIBRATION_CFG_CMD = 0x65,
- CALIBRATION_RES_NOTIFICATION = 0x66,
- CALIBRATION_COMPLETE_NOTIFICATION = 0x67
+ IWL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7,
+ IWL_PHY_CALIBRATE_DC_CMD = 8,
+ IWL_PHY_CALIBRATE_LO_CMD = 9,
+ IWL_PHY_CALIBRATE_RX_BB_CMD = 10,
+ IWL_PHY_CALIBRATE_TX_IQ_CMD = 11,
+ IWL_PHY_CALIBRATE_RX_IQ_CMD = 12,
+ IWL_PHY_CALIBRATION_NOISE_CMD = 13,
+ IWL_PHY_CALIBRATE_AGC_TABLE_CMD = 14,
+ IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
+ IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16,
+ IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17,
+ IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD = 18,
+ IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD = 19,
};
-struct iwl_cal_crystal_freq_cmd {
- u8 cap_pin1;
- u8 cap_pin2;
-} __attribute__ ((packed));
-
-struct iwl5000_calibration {
- u8 op_code;
- u8 first_group;
- u8 num_groups;
- u8 all_data_valid;
- struct iwl_cal_crystal_freq_cmd data;
-} __attribute__ ((packed));
-#define IWL_CALIB_INIT_CFG_ALL __constant_cpu_to_le32(0xffffffff)
+#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(0xffffffff)
struct iwl_calib_cfg_elmnt_s {
__le32 is_enable;
@@ -2914,32 +2882,52 @@ struct iwl_calib_cfg_status_s {
__le32 flags;
} __attribute__ ((packed));
-struct iwl5000_calib_cfg_cmd {
+struct iwl_calib_cfg_cmd {
struct iwl_calib_cfg_status_s ucd_calib_cfg;
struct iwl_calib_cfg_status_s drv_calib_cfg;
__le32 reserved1;
} __attribute__ ((packed));
-struct iwl5000_calib_hdr {
+struct iwl_calib_hdr {
u8 op_code;
u8 first_group;
u8 groups_num;
u8 data_valid;
} __attribute__ ((packed));
-struct iwl5000_calibration_chain_noise_reset_cmd {
- u8 op_code; /* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
- u8 flags; /* not used */
- __le16 reserved;
+struct iwl_calib_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 data[0];
} __attribute__ ((packed));
-struct iwl5000_calibration_chain_noise_gain_cmd {
- u8 op_code; /* IWL5000_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
- u8 flags; /* not used */
- __le16 reserved;
+/* IWL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */
+struct iwl_calib_diff_gain_cmd {
+ struct iwl_calib_hdr hdr;
+ s8 diff_gain_a; /* see above */
+ s8 diff_gain_b;
+ s8 diff_gain_c;
+ u8 reserved1;
+} __attribute__ ((packed));
+
+struct iwl_calib_xtal_freq_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 cap_pin1;
+ u8 cap_pin2;
+ u8 pad[2];
+} __attribute__ ((packed));
+
+/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */
+struct iwl_calib_chain_noise_reset_cmd {
+ struct iwl_calib_hdr hdr;
+ u8 data[0];
+};
+
+/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */
+struct iwl_calib_chain_noise_gain_cmd {
+ struct iwl_calib_hdr hdr;
u8 delta_gain_1;
u8 delta_gain_2;
- __le16 reserved1;
+ u8 pad[2];
} __attribute__ ((packed));
/******************************************************************************
@@ -2999,11 +2987,11 @@ struct iwl_wimax_coex_event_entry {
/* COEX flag masks */
-/* Staion table is valid */
+/* Station table is valid */
#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1)
-/* UnMask wakeup src at unassociated sleep */
+/* UnMask wake up src at unassociated sleep */
#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4)
-/* UnMask wakeup src at associated sleep */
+/* UnMask wake up src at associated sleep */
#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8)
/* Enable CoEx feature. */
#define COEX_FLAGS_COEX_ENABLE_MSK (0x80)
@@ -3025,26 +3013,22 @@ struct iwl_rx_packet {
struct iwl_cmd_header hdr;
union {
struct iwl_alive_resp alive_frame;
- struct iwl4965_rx_frame rx_frame;
- struct iwl4965_tx_resp tx_resp;
- struct iwl4965_spectrum_notification spectrum_notif;
- struct iwl4965_csa_notification csa_notif;
+ struct iwl_spectrum_notification spectrum_notif;
+ struct iwl_csa_notification csa_notif;
struct iwl_error_resp err_resp;
- struct iwl4965_card_state_notif card_state_notif;
- struct iwl4965_beacon_notif beacon_status;
+ struct iwl_card_state_notif card_state_notif;
struct iwl_add_sta_resp add_sta;
struct iwl_rem_sta_resp rem_sta;
- struct iwl4965_sleep_notification sleep_notif;
- struct iwl4965_spectrum_resp spectrum;
+ struct iwl_sleep_notification sleep_notif;
+ struct iwl_spectrum_resp spectrum;
struct iwl_notif_statistics stats;
struct iwl_compressed_ba_resp compressed_ba;
- struct iwl4965_missed_beacon_notif missed_beacon;
- struct iwl5000_calibration calib;
+ struct iwl_missed_beacon_notif missed_beacon;
__le32 status;
u8 raw[0];
} u;
} __attribute__ ((packed));
-#define IWL_RX_FRAME_SIZE (4 + sizeof(struct iwl4965_rx_frame))
+int iwl_agn_check_rxon_cmd(struct iwl_rxon_cmd *rxon);
-#endif /* __iwl4965_commands_h__ */
+#endif /* __iwl_commands_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 01a845851338..73d7973707eb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -22,7 +22,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -30,19 +30,19 @@
#include <linux/module.h>
#include <net/mac80211.h>
-struct iwl_priv; /* FIXME: remove */
-#include "iwl-debug.h"
#include "iwl-eeprom.h"
#include "iwl-dev.h" /* FIXME: remove */
+#include "iwl-debug.h"
#include "iwl-core.h"
#include "iwl-io.h"
#include "iwl-rfkill.h"
#include "iwl-power.h"
+#include "iwl-sta.h"
MODULE_DESCRIPTION("iwl core");
MODULE_VERSION(IWLWIFI_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
@@ -88,26 +88,27 @@ EXPORT_SYMBOL(iwl_rates);
* translate ucode response to mac80211 tx status control values
*/
void iwl_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
- struct ieee80211_tx_info *control)
+ struct ieee80211_tx_info *info)
{
int rate_index;
+ struct ieee80211_tx_rate *r = &info->control.rates[0];
- control->antenna_sel_tx =
+ info->antenna_sel_tx =
((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
if (rate_n_flags & RATE_MCS_HT_MSK)
- control->flags |= IEEE80211_TX_CTL_OFDM_HT;
+ r->flags |= IEEE80211_TX_RC_MCS;
if (rate_n_flags & RATE_MCS_GF_MSK)
- control->flags |= IEEE80211_TX_CTL_GREEN_FIELD;
+ r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
if (rate_n_flags & RATE_MCS_FAT_MSK)
- control->flags |= IEEE80211_TX_CTL_40_MHZ_WIDTH;
+ r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_n_flags & RATE_MCS_DUP_MSK)
- control->flags |= IEEE80211_TX_CTL_DUP_DATA;
+ r->flags |= IEEE80211_TX_RC_DUP_DATA;
if (rate_n_flags & RATE_MCS_SGI_MSK)
- control->flags |= IEEE80211_TX_CTL_SHORT_GI;
+ r->flags |= IEEE80211_TX_RC_SHORT_GI;
rate_index = iwl_hwrate_to_plcp_idx(rate_n_flags);
- if (control->band == IEEE80211_BAND_5GHZ)
+ if (info->band == IEEE80211_BAND_5GHZ)
rate_index -= IWL_FIRST_OFDM_RATE;
- control->tx_rate_idx = rate_index;
+ r->idx = rate_index;
}
EXPORT_SYMBOL(iwl_hwrate_to_tx_control);
@@ -119,7 +120,9 @@ int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
if (rate_n_flags & RATE_MCS_HT_MSK) {
idx = (rate_n_flags & 0xff);
- if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+ if (idx >= IWL_RATE_MIMO3_6M_PLCP)
+ idx = idx - IWL_RATE_MIMO3_6M_PLCP;
+ else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
idx = idx - IWL_RATE_MIMO2_6M_PLCP;
idx += IWL_FIRST_OFDM_RATE;
@@ -140,7 +143,17 @@ int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
}
EXPORT_SYMBOL(iwl_hwrate_to_plcp_idx);
-
+u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant)
+{
+ int i;
+ u8 ind = ant;
+ for (i = 0; i < RATE_ANT_NUM - 1; i++) {
+ ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0;
+ if (priv->hw_params.valid_tx_ant & BIT(ind))
+ return ind;
+ }
+ return ant;
+}
const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
EXPORT_SYMBOL(iwl_bcast_addr);
@@ -177,52 +190,6 @@ void iwl_hw_detect(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_hw_detect);
-/* Tell nic where to find the "keep warm" buffer */
-int iwl_kw_init(struct iwl_priv *priv)
-{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&priv->lock, flags);
- ret = iwl_grab_nic_access(priv);
- if (ret)
- goto out;
-
- iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG,
- priv->kw.dma_addr >> 4);
- iwl_release_nic_access(priv);
-out:
- spin_unlock_irqrestore(&priv->lock, flags);
- return ret;
-}
-
-int iwl_kw_alloc(struct iwl_priv *priv)
-{
- struct pci_dev *dev = priv->pci_dev;
- struct iwl_kw *kw = &priv->kw;
-
- kw->size = IWL_KW_SIZE;
- kw->v_addr = pci_alloc_consistent(dev, kw->size, &kw->dma_addr);
- if (!kw->v_addr)
- return -ENOMEM;
-
- return 0;
-}
-
-/**
- * iwl_kw_free - Free the "keep warm" buffer
- */
-void iwl_kw_free(struct iwl_priv *priv)
-{
- struct pci_dev *dev = priv->pci_dev;
- struct iwl_kw *kw = &priv->kw;
-
- if (kw->v_addr) {
- pci_free_consistent(dev, kw->size, kw->v_addr, kw->dma_addr);
- memset(kw, 0, sizeof(*kw));
- }
-}
-
int iwl_hw_nic_init(struct iwl_priv *priv)
{
unsigned long flags;
@@ -271,55 +238,30 @@ int iwl_hw_nic_init(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_hw_nic_init);
-/**
- * iwl_clear_stations_table - Clear the driver's station table
- *
- * NOTE: This does not clear or otherwise alter the device's station table.
- */
-void iwl_clear_stations_table(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
-
- if (iwl_is_alive(priv) &&
- !test_bit(STATUS_EXIT_PENDING, &priv->status) &&
- iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
- IWL_ERROR("Couldn't clear the station table\n");
-
- priv->num_stations = 0;
- memset(priv->stations, 0, sizeof(priv->stations));
-
- /* clean ucode key table bit map */
- priv->ucode_key_table = 0;
-
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-}
-EXPORT_SYMBOL(iwl_clear_stations_table);
-
void iwl_reset_qos(struct iwl_priv *priv)
{
u16 cw_min = 15;
u16 cw_max = 1023;
u8 aifs = 2;
- u8 is_legacy = 0;
+ bool is_legacy = false;
unsigned long flags;
int i;
spin_lock_irqsave(&priv->lock, flags);
- priv->qos_data.qos_active = 0;
+ /* QoS always active in AP and ADHOC mode
+ * In STA mode wait for association
+ */
+ if (priv->iw_mode == NL80211_IFTYPE_ADHOC ||
+ priv->iw_mode == NL80211_IFTYPE_AP)
+ priv->qos_data.qos_active = 1;
+ else
+ priv->qos_data.qos_active = 0;
- if (priv->iw_mode == NL80211_IFTYPE_ADHOC) {
- if (priv->qos_data.qos_enable)
- priv->qos_data.qos_active = 1;
- if (!(priv->active_rate & 0xfff0)) {
- cw_min = 31;
- is_legacy = 1;
- }
- } else if (priv->iw_mode == NL80211_IFTYPE_AP) {
- if (priv->qos_data.qos_enable)
- priv->qos_data.qos_active = 1;
- } else if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) {
+ /* check for legacy mode */
+ if ((priv->iw_mode == NL80211_IFTYPE_ADHOC &&
+ (priv->active_rate & IWL_OFDM_RATES_MASK) == 0) ||
+ (priv->iw_mode == NL80211_IFTYPE_STATION &&
+ (priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK) == 0)) {
cw_min = 31;
is_legacy = 1;
}
@@ -385,10 +327,10 @@ void iwl_reset_qos(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_reset_qos);
-#define MAX_BIT_RATE_40_MHZ 0x96 /* 150 Mbps */
-#define MAX_BIT_RATE_20_MHZ 0x48 /* 72 Mbps */
+#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
+#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
- struct ieee80211_ht_info *ht_info,
+ struct ieee80211_sta_ht_cap *ht_info,
enum ieee80211_band band)
{
u16 max_bit_rate = 0;
@@ -396,45 +338,46 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
u8 tx_chains_num = priv->hw_params.tx_chains_num;
ht_info->cap = 0;
- memset(ht_info->supp_mcs_set, 0, 16);
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
- ht_info->ht_supported = 1;
+ ht_info->ht_supported = true;
- ht_info->cap |= (u16)IEEE80211_HT_CAP_GRN_FLD;
- ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_20;
- ht_info->cap |= (u16)(IEEE80211_HT_CAP_SM_PS &
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ ht_info->cap |= (IEEE80211_HT_CAP_SM_PS &
(WLAN_HT_CAP_SM_PS_DISABLED << 2));
max_bit_rate = MAX_BIT_RATE_20_MHZ;
if (priv->hw_params.fat_channel & BIT(band)) {
- ht_info->cap |= (u16)IEEE80211_HT_CAP_SUP_WIDTH;
- ht_info->cap |= (u16)IEEE80211_HT_CAP_SGI_40;
- ht_info->supp_mcs_set[4] = 0x01;
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ ht_info->mcs.rx_mask[4] = 0x01;
max_bit_rate = MAX_BIT_RATE_40_MHZ;
}
if (priv->cfg->mod_params->amsdu_size_8K)
- ht_info->cap |= (u16)IEEE80211_HT_CAP_MAX_AMSDU;
+ ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF;
ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF;
- ht_info->supp_mcs_set[0] = 0xFF;
+ ht_info->mcs.rx_mask[0] = 0xFF;
if (rx_chains_num >= 2)
- ht_info->supp_mcs_set[1] = 0xFF;
+ ht_info->mcs.rx_mask[1] = 0xFF;
if (rx_chains_num >= 3)
- ht_info->supp_mcs_set[2] = 0xFF;
+ ht_info->mcs.rx_mask[2] = 0xFF;
/* Highest supported Rx data rate */
max_bit_rate *= rx_chains_num;
- ht_info->supp_mcs_set[10] = (u8)(max_bit_rate & 0x00FF);
- ht_info->supp_mcs_set[11] = (u8)((max_bit_rate & 0xFF00) >> 8);
+ WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK);
+ ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate);
/* Tx MCS capabilities */
- ht_info->supp_mcs_set[12] = IEEE80211_HT_CAP_MCS_TX_DEFINED;
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
if (tx_chains_num != rx_chains_num) {
- ht_info->supp_mcs_set[12] |= IEEE80211_HT_CAP_MCS_TX_RX_DIFF;
- ht_info->supp_mcs_set[12] |= ((tx_chains_num - 1) << 2);
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |= ((tx_chains_num - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
}
}
@@ -498,7 +441,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv)
sband->n_bitrates = IWL_RATE_COUNT - IWL_FIRST_OFDM_RATE;
if (priv->cfg->sku & IWL_SKU_N)
- iwlcore_init_ht_hw_capab(priv, &sband->ht_info,
+ iwlcore_init_ht_hw_capab(priv, &sband->ht_cap,
IEEE80211_BAND_5GHZ);
sband = &priv->bands[IEEE80211_BAND_2GHZ];
@@ -508,7 +451,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv)
sband->n_bitrates = IWL_RATE_COUNT;
if (priv->cfg->sku & IWL_SKU_N)
- iwlcore_init_ht_hw_capab(priv, &sband->ht_info,
+ iwlcore_init_ht_hw_capab(priv, &sband->ht_cap,
IEEE80211_BAND_2GHZ);
priv->ieee_channels = channels;
@@ -598,8 +541,8 @@ static void iwlcore_free_geos(struct iwl_priv *priv)
static bool is_single_rx_stream(struct iwl_priv *priv)
{
return !priv->current_ht_config.is_ht ||
- ((priv->current_ht_config.supp_mcs_set[1] == 0) &&
- (priv->current_ht_config.supp_mcs_set[2] == 0));
+ ((priv->current_ht_config.mcs.rx_mask[1] == 0) &&
+ (priv->current_ht_config.mcs.rx_mask[2] == 0));
}
static u8 iwl_is_channel_extension(struct iwl_priv *priv,
@@ -612,10 +555,10 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv,
if (!is_channel_valid(ch_info))
return 0;
- if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE)
+ if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
return !(ch_info->fat_extension_channel &
IEEE80211_CHAN_NO_FAT_ABOVE);
- else if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW)
+ else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
return !(ch_info->fat_extension_channel &
IEEE80211_CHAN_NO_FAT_BELOW);
@@ -623,24 +566,24 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv,
}
u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
- struct ieee80211_ht_info *sta_ht_inf)
+ struct ieee80211_sta_ht_cap *sta_ht_inf)
{
struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
if ((!iwl_ht_conf->is_ht) ||
(iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
- (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE))
+ (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE))
return 0;
if (sta_ht_inf) {
if ((!sta_ht_inf->ht_supported) ||
- (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH)))
+ (!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))
return 0;
}
return iwl_is_channel_extension(priv, priv->band,
- iwl_ht_conf->control_channel,
- iwl_ht_conf->extension_chan_offset);
+ le16_to_cpu(priv->staging_rxon.channel),
+ iwl_ht_conf->extension_chan_offset);
}
EXPORT_SYMBOL(iwl_is_fat_tx_allowed);
@@ -665,22 +608,15 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
- if (le16_to_cpu(rxon->channel) != ht_info->control_channel) {
- IWL_DEBUG_ASSOC("control diff than current %d %d\n",
- le16_to_cpu(rxon->channel),
- ht_info->control_channel);
- return;
- }
-
/* Note: control channel is opposite of extension channel */
switch (ht_info->extension_chan_offset) {
- case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
break;
- case IEEE80211_HT_IE_CHA_SEC_BELOW:
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
break;
- case IEEE80211_HT_IE_CHA_SEC_NONE:
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
default:
rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
break;
@@ -694,14 +630,12 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
IWL_DEBUG_ASSOC("supported HT rate 0x%X 0x%X 0x%X "
"rxon flags 0x%X operation mode :0x%X "
- "extension channel offset 0x%x "
- "control chan %d\n",
- ht_info->supp_mcs_set[0],
- ht_info->supp_mcs_set[1],
- ht_info->supp_mcs_set[2],
+ "extension channel offset 0x%x\n",
+ ht_info->mcs.rx_mask[0],
+ ht_info->mcs.rx_mask[1],
+ ht_info->mcs.rx_mask[2],
le32_to_cpu(rxon->flags), ht_info->ht_protection,
- ht_info->extension_chan_offset,
- ht_info->control_channel);
+ ht_info->extension_chan_offset);
return;
}
EXPORT_SYMBOL(iwl_set_rxon_ht);
@@ -745,7 +679,7 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
break;
case WLAN_HT_CAP_SM_PS_INVALID:
default:
- IWL_ERROR("invalide mimo ps mode %d\n",
+ IWL_ERROR("invalid mimo ps mode %d\n",
priv->current_ht_config.sm_ps);
WARN_ON(1);
idle_cnt = -1;
@@ -871,11 +805,14 @@ int iwl_setup_mac(struct iwl_priv *priv)
/* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_AMPDU_AGGREGATION;
hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
+
+ hw->wiphy->fw_handles_regulatory = true;
+
/* Default value; 4 EDCA QOS priorities */
hw->queues = 4;
/* queues to support 11n aggregation */
@@ -948,16 +885,12 @@ int iwl_init_drv(struct iwl_priv *priv)
priv->iw_mode = NL80211_IFTYPE_STATION;
- priv->use_ant_b_for_management_frame = 1; /* start with ant B */
priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
/* Choose which receivers/antennas to use */
iwl_set_rxon_chain(priv);
iwl_init_scan_params(priv);
- if (priv->cfg->mod_params->enable_qos)
- priv->qos_data.qos_enable = 1;
-
iwl_reset_qos(priv);
priv->qos_data.qos_active = 0;
@@ -1025,6 +958,30 @@ void iwl_uninit_drv(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_uninit_drv);
+
+void iwl_disable_interrupts(struct iwl_priv *priv)
+{
+ clear_bit(STATUS_INT_ENABLED, &priv->status);
+
+ /* disable interrupts from uCode/NIC to host */
+ iwl_write32(priv, CSR_INT_MASK, 0x00000000);
+
+ /* acknowledge/clear/reset any interrupts still pending
+ * from uCode or flow handler (Rx/Tx DMA) */
+ iwl_write32(priv, CSR_INT, 0xffffffff);
+ iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff);
+ IWL_DEBUG_ISR("Disabled interrupts\n");
+}
+EXPORT_SYMBOL(iwl_disable_interrupts);
+
+void iwl_enable_interrupts(struct iwl_priv *priv)
+{
+ IWL_DEBUG_ISR("Enabling interrupts\n");
+ set_bit(STATUS_INT_ENABLED, &priv->status);
+ iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
+}
+EXPORT_SYMBOL(iwl_enable_interrupts);
+
int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags)
{
u32 stat_flags = 0;
@@ -1172,24 +1129,47 @@ int iwl_verify_ucode(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_verify_ucode);
+
+static const char *desc_lookup_text[] = {
+ "OK",
+ "FAIL",
+ "BAD_PARAM",
+ "BAD_CHECKSUM",
+ "NMI_INTERRUPT_WDG",
+ "SYSASSERT",
+ "FATAL_ERROR",
+ "BAD_COMMAND",
+ "HW_ERROR_TUNE_LOCK",
+ "HW_ERROR_TEMPERATURE",
+ "ILLEGAL_CHAN_FREQ",
+ "VCC_NOT_STABLE",
+ "FH_ERROR",
+ "NMI_INTERRUPT_HOST",
+ "NMI_INTERRUPT_ACTION_PT",
+ "NMI_INTERRUPT_UNKNOWN",
+ "UCODE_VERSION_MISMATCH",
+ "HW_ERROR_ABS_LOCK",
+ "HW_ERROR_CAL_LOCK_FAIL",
+ "NMI_INTERRUPT_INST_ACTION_PT",
+ "NMI_INTERRUPT_DATA_ACTION_PT",
+ "NMI_TRM_HW_ER",
+ "NMI_INTERRUPT_TRM",
+ "NMI_INTERRUPT_BREAK_POINT"
+ "DEBUG_0",
+ "DEBUG_1",
+ "DEBUG_2",
+ "DEBUG_3",
+ "UNKNOWN"
+};
+
static const char *desc_lookup(int i)
{
- switch (i) {
- case 1:
- return "FAIL";
- case 2:
- return "BAD_PARAM";
- case 3:
- return "BAD_CHECKSUM";
- case 4:
- return "NMI_INTERRUPT";
- case 5:
- return "SYSASSERT";
- case 6:
- return "FATAL_ERROR";
- }
+ int max = ARRAY_SIZE(desc_lookup_text) - 1;
- return "UNKNOWN";
+ if (i < 0 || i > max)
+ i = max;
+
+ return desc_lookup_text[i];
}
#define ERROR_START_OFFSET (1 * sizeof(u32))
@@ -1235,9 +1215,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32));
time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32));
- IWL_ERROR("Desc Time "
+ IWL_ERROR("Desc Time "
"data1 data2 line\n");
- IWL_ERROR("%-13s (#%d) %010u 0x%08X 0x%08X %u\n",
+ IWL_ERROR("%-28s (#%02d) %010u 0x%08X 0x%08X %u\n",
desc_lookup(desc), desc, time, data1, data2, line);
IWL_ERROR("blink1 blink2 ilink1 ilink2\n");
IWL_ERROR("0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2,
@@ -1377,6 +1357,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_rf_kill_ct_config);
+
/*
* CARD_STATE_CMD
*
@@ -1465,6 +1446,16 @@ int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv)
return 0;
}
+ /* when driver is up while rfkill is on, it wont receive
+ * any CARD_STATE_NOTIFICATION notifications so we have to
+ * restart it in here
+ */
+ if (priv->is_open && !test_bit(STATUS_ALIVE, &priv->status)) {
+ clear_bit(STATUS_RF_KILL_SW, &priv->status);
+ if (!iwl_is_rfkill(priv))
+ queue_work(priv->workqueue, &priv->up);
+ }
+
/* If the driver is already loaded, it will receive
* CARD_STATE_NOTIFICATION notifications and the handler will
* call restart to reload the driver.
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 288b6a800e03..7c3a20a986bb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -72,6 +72,7 @@ struct iwl_cmd;
#define IWLWIFI_VERSION "1.3.27k"
#define DRV_COPYRIGHT "Copyright(c) 2003-2008 Intel Corporation"
+#define DRV_AUTHOR "<ilw@linux.intel.com>"
#define IWL_PCI_DEVICE(dev, subdev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
@@ -100,12 +101,8 @@ struct iwl_hcmd_utils_ops {
};
struct iwl_lib_ops {
- /* set hw dependant perameters */
+ /* set hw dependent parameters */
int (*set_hw_params)(struct iwl_priv *priv);
- /* ucode shared memory */
- int (*alloc_shared_mem)(struct iwl_priv *priv);
- void (*free_shared_mem)(struct iwl_priv *priv);
- int (*shared_mem_rx_idx)(struct iwl_priv *priv);
/* Handling TX */
void (*txq_update_byte_cnt_tbl)(struct iwl_priv *priv,
struct iwl_tx_queue *txq,
@@ -157,22 +154,53 @@ struct iwl_ops {
struct iwl_mod_params {
int disable; /* def: 0 = enable radio */
int sw_crypto; /* def: 0 = using hardware encryption */
- int debug; /* def: 0 = minimal debug log messages */
+ u32 debug; /* def: 0 = minimal debug log messages */
int disable_hw_scan; /* def: 0 = use h/w scan */
int num_of_queues; /* def: HW dependent */
int num_of_ampdu_queues;/* def: HW dependent */
- int enable_qos; /* def: 1 = use quality of service */
int disable_11n; /* def: 0 = disable 11n capabilities */
int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */
int antenna; /* def: 0 = both antennas (use diversity) */
int restart_fw; /* def: 1 = restart firmware */
};
+/**
+ * struct iwl_cfg
+ * @fw_name_pre: Firmware filename prefix. The api version and extension
+ * (.ucode) will be added to filename before loading from disk. The
+ * filename is constructed as fw_name_pre<api>.ucode.
+ * @ucode_api_max: Highest version of uCode API supported by driver.
+ * @ucode_api_min: Lowest version of uCode API supported by driver.
+ *
+ * We enable the driver to be backward compatible wrt API version. The
+ * driver specifies which APIs it supports (with @ucode_api_max being the
+ * highest and @ucode_api_min the lowest). Firmware will only be loaded if
+ * it has a supported API version. The firmware's API version will be
+ * stored in @iwl_priv, enabling the driver to make runtime changes based
+ * on firmware version used.
+ *
+ * For example,
+ * if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
+ * Driver interacts with Firmware API version >= 2.
+ * } else {
+ * Driver interacts with Firmware API version 1.
+ * }
+ *
+ * The ideal usage of this infrastructure is to treat a new ucode API
+ * release as a new hardware revision. That is, through utilizing the
+ * iwl_hcmd_utils_ops etc. we accommodate different command structures
+ * and flows between hardware versions (4965/5000) as well as their API
+ * versions.
+ */
struct iwl_cfg {
const char *name;
- const char *fw_name;
+ const char *fw_name_pre;
+ const unsigned int ucode_api_max;
+ const unsigned int ucode_api_min;
unsigned int sku;
int eeprom_size;
+ u16 eeprom_ver;
+ u16 eeprom_calib_ver;
const struct iwl_ops *ops;
const struct iwl_mod_params *mod_params;
};
@@ -184,22 +212,17 @@ struct iwl_cfg {
struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
struct ieee80211_ops *hw_ops);
void iwl_hw_detect(struct iwl_priv *priv);
-void iwl_clear_stations_table(struct iwl_priv *priv);
void iwl_reset_qos(struct iwl_priv *priv);
void iwl_set_rxon_chain(struct iwl_priv *priv);
int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch);
void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info);
u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
- struct ieee80211_ht_info *sta_ht_inf);
+ struct ieee80211_sta_ht_cap *sta_ht_inf);
int iwl_hw_nic_init(struct iwl_priv *priv);
int iwl_setup_mac(struct iwl_priv *priv);
int iwl_set_hw_params(struct iwl_priv *priv);
int iwl_init_drv(struct iwl_priv *priv);
void iwl_uninit_drv(struct iwl_priv *priv);
-/* "keep warm" functions */
-int iwl_kw_init(struct iwl_priv *priv);
-int iwl_kw_alloc(struct iwl_priv *priv);
-void iwl_kw_free(struct iwl_priv *priv);
/*****************************************************
* RX
@@ -212,8 +235,6 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
void iwl_rx_replenish(struct iwl_priv *priv);
int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
-int iwl_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn);
-int iwl_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
int iwl_rx_queue_restock(struct iwl_priv *priv);
int iwl_rx_queue_space(const struct iwl_rx_queue *q);
void iwl_rx_allocate(struct iwl_priv *priv);
@@ -237,7 +258,6 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn);
int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid);
int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id);
-
/*****************************************************
* TX power
****************************************************/
@@ -259,6 +279,13 @@ void iwl_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags,
struct ieee80211_tx_info *info);
int iwl_hwrate_to_plcp_idx(u32 rate_n_flags);
+u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx);
+
+static inline u32 iwl_ant_idx_to_flags(u8 ant_idx)
+{
+ return BIT(ant_idx) << RATE_MCS_ANT_POS;
+}
+
static inline u8 iwl_hw_get_rate(__le32 rate_n_flags)
{
return le32_to_cpu(rate_n_flags) & 0xFF;
@@ -289,6 +316,14 @@ int iwl_send_calib_results(struct iwl_priv *priv);
int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len);
void iwl_calib_free_results(struct iwl_priv *priv);
+/*******************************************************************************
+ * Spectrum Measureemtns in iwl-spectrum.c
+ ******************************************************************************/
+#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
+void iwl_setup_spectrum_handlers(struct iwl_priv *priv);
+#else
+static inline void iwl_setup_spectrum_handlers(struct iwl_priv *priv) {}
+#endif
/*****************************************************
* S e n d i n g H o s t C o m m a n d s *
*****************************************************/
@@ -308,11 +343,18 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len,
int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
/*****************************************************
+ * PCI *
+ *****************************************************/
+void iwl_disable_interrupts(struct iwl_priv *priv);
+void iwl_enable_interrupts(struct iwl_priv *priv);
+
+/*****************************************************
* Error Handling Debugging
******************************************************/
void iwl_dump_nic_error_log(struct iwl_priv *priv);
void iwl_dump_nic_event_log(struct iwl_priv *priv);
+
/*************** DRIVER STATUS FUNCTIONS *****/
#define STATUS_HCMD_ACTIVE 0 /* host command in progress */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 662edf4f8d22..f34ede44ed10 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -60,6 +60,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+#ifndef __iwl_csr_h__
+#define __iwl_csr_h__
/*=== CSR (control and status registers) ===*/
#define CSR_BASE (0x000)
@@ -214,6 +216,8 @@
/* EEPROM REG */
#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
#define CSR_EEPROM_REG_BIT_CMD (0x00000002)
+#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC)
+#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000)
/* EEPROM GP */
#define CSR_EEPROM_GP_VALID_MSK (0x00000006)
@@ -286,4 +290,4 @@
#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
-
+#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index e548d67f87fd..56c13b458de7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -40,6 +40,13 @@ do { if ((priv->debug_level & (level)) && net_ratelimit()) \
dev_printk(KERN_ERR, &(priv->hw->wiphy->dev), "%c %s " fmt, \
in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0)
+#define iwl_print_hex_dump(priv, level, p, len) \
+do { \
+ if (priv->debug_level & level) \
+ print_hex_dump(KERN_DEBUG, "iwl data: ", \
+ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \
+} while (0)
+
#ifdef CONFIG_IWLWIFI_DEBUGFS
struct iwl_debugfs {
const char *name;
@@ -53,6 +60,7 @@ struct iwl_debugfs {
struct dentry *file_rx_statistics;
struct dentry *file_tx_statistics;
struct dentry *file_log_event;
+ struct dentry *file_channels;
} dbgfs_data_files;
struct dir_rf_files {
struct dentry *file_disable_sensitivity;
@@ -70,6 +78,9 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv);
#else
#define IWL_DEBUG(level, fmt, args...)
#define IWL_DEBUG_LIMIT(level, fmt, args...)
+static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level,
+ void *p, u32 len)
+{}
#endif /* CONFIG_IWLWIFI_DEBUG */
@@ -85,29 +96,25 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#endif /* CONFIG_IWLWIFI_DEBUGFS */
/*
- * To use the debug system;
+ * To use the debug system:
*
* If you are defining a new debug classification, simply add it to the #define
- * list here in the form of:
+ * list here in the form of
*
* #define IWL_DL_xxxx VALUE
*
- * shifting value to the left one bit from the previous entry. xxxx should be
- * the name of the classification (for example, WEP)
+ * where xxxx should be the name of the classification (for example, WEP).
*
* You then need to either add a IWL_xxxx_DEBUG() macro definition for your
* classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want
* to send output to that classification.
*
- * To add your debug level to the list of levels seen when you perform
- *
- * % cat /proc/net/iwl/debug_level
- *
- * you simply need to add your entry to the iwl_debug_levels array.
+ * The active debug levels can be accessed via files
*
- * If you do not see debug_level in /proc/net/iwl then you do not have
- * CONFIG_IWLWIFI_DEBUG defined in your kernel configuration
+ * /sys/module/iwlagn/parameters/debug{50}
+ * /sys/class/net/wlan0/device/debug_level
*
+ * when CONFIG_IWLWIFI_DEBUG=y.
*/
#define IWL_DL_INFO (1 << 0)
@@ -183,6 +190,8 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv)
#define IWL_DEBUG_STATS(f, a...) IWL_DEBUG(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_STATS_LIMIT(f, a...) IWL_DEBUG_LIMIT(IWL_DL_STATS, f, ## a)
#define IWL_DEBUG_TX_REPLY(f, a...) IWL_DEBUG(IWL_DL_TX_REPLY, f, ## a)
+#define IWL_DEBUG_TX_REPLY_LIMIT(f, a...) \
+ IWL_DEBUG_LIMIT(IWL_DL_TX_REPLY, f, ## a)
#define IWL_DEBUG_QOS(f, a...) IWL_DEBUG(IWL_DL_QOS, f, ## a)
#define IWL_DEBUG_RADIO(f, a...) IWL_DEBUG(IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_POWER(f, a...) IWL_DEBUG(IWL_DL_POWER, f, ## a)
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index 20db0eb636a8..d5253a179dec 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -22,7 +22,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -58,7 +58,8 @@
#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \
dbgfs->dbgfs_##parent##_files.file_##name = \
debugfs_create_bool(#name, 0644, dbgfs->dir_##parent, ptr); \
- if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name)) \
+ if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \
+ || !dbgfs->dbgfs_##parent##_files.file_##name) \
goto err; \
} while (0)
@@ -228,7 +229,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
ssize_t ret;
/* Add 30 for initial string */
const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations);
- DECLARE_MAC_BUF(mac);
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)
@@ -242,7 +242,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
if (station->used) {
pos += scnprintf(buf + pos, bufsz - pos,
"station %d:\ngeneral data:\n", i+1);
- print_mac(mac, station->sta.sta.addr);
pos += scnprintf(buf + pos, bufsz - pos, "id: %u\n",
station->sta.sta.sta_id);
pos += scnprintf(buf + pos, bufsz - pos, "mode: %u\n",
@@ -349,12 +348,86 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file,
return count;
}
+
+
+static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ struct ieee80211_channel *channels = NULL;
+ const struct ieee80211_supported_band *supp_band = NULL;
+ int pos = 0, i, bufsz = PAGE_SIZE;
+ char *buf;
+ ssize_t ret;
+
+ if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status))
+ return -EAGAIN;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf) {
+ IWL_ERROR("Can not allocate Buffer\n");
+ return -ENOMEM;
+ }
+
+ supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
+ channels = supp_band->channels;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Displaying %d channels in 2.4GHz band 802.11bg):\n",
+ supp_band->n_channels);
+
+ for (i = 0; i < supp_band->n_channels; i++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%d: %ddBm: BSS%s%s, %s.\n",
+ ieee80211_frequency_to_channel(
+ channels[i].center_freq),
+ channels[i].max_power,
+ channels[i].flags & IEEE80211_CHAN_RADAR ?
+ " (IEEE 802.11h required)" : "",
+ (!(channels[i].flags & IEEE80211_CHAN_NO_IBSS)
+ || (channels[i].flags &
+ IEEE80211_CHAN_RADAR)) ? "" :
+ ", IBSS",
+ channels[i].flags &
+ IEEE80211_CHAN_PASSIVE_SCAN ?
+ "passive only" : "active/passive");
+
+ supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
+ channels = supp_band->channels;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Displaying %d channels in 5.2GHz band (802.11a)\n",
+ supp_band->n_channels);
+
+ for (i = 0; i < supp_band->n_channels; i++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "%d: %ddBm: BSS%s%s, %s.\n",
+ ieee80211_frequency_to_channel(
+ channels[i].center_freq),
+ channels[i].max_power,
+ channels[i].flags & IEEE80211_CHAN_RADAR ?
+ " (IEEE 802.11h required)" : "",
+ ((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
+ || (channels[i].flags &
+ IEEE80211_CHAN_RADAR)) ? "" :
+ ", IBSS",
+ channels[i].flags &
+ IEEE80211_CHAN_PASSIVE_SCAN ?
+ "passive only" : "active/passive");
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+ return ret;
+}
+
+
DEBUGFS_READ_WRITE_FILE_OPS(sram);
DEBUGFS_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_FILE_OPS(eeprom);
DEBUGFS_READ_FILE_OPS(stations);
DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics);
+DEBUGFS_READ_FILE_OPS(channels);
/*
* Create the debugfs files and directories
@@ -388,6 +461,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(stations, data);
DEBUGFS_ADD_FILE(rx_statistics, data);
DEBUGFS_ADD_FILE(tx_statistics, data);
+ DEBUGFS_ADD_FILE(channels, data);
DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal);
DEBUGFS_ADD_BOOL(disable_chain_noise, rf,
&priv->disable_chain_noise_cal);
@@ -416,6 +490,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels);
DEBUGFS_REMOVE(priv->dbgfs->dir_data);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 9966d4e384ce..0468fcc1ea98 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -54,6 +54,7 @@ extern struct iwl_cfg iwl5100_agn_cfg;
extern struct iwl_cfg iwl5350_agn_cfg;
extern struct iwl_cfg iwl5100_bg_cfg;
extern struct iwl_cfg iwl5100_abg_cfg;
+extern struct iwl_cfg iwl5150_agn_cfg;
/* CT-KILL constants */
#define CT_KILL_THRESHOLD 110 /* in Celsius */
@@ -113,11 +114,9 @@ struct iwl_queue {
* space less than this */
} __attribute__ ((packed));
-#define MAX_NUM_OF_TBS (20)
-
/* One for each TFD */
struct iwl_tx_info {
- struct sk_buff *skb[MAX_NUM_OF_TBS];
+ struct sk_buff *skb[IWL_NUM_OF_TBS - 1];
};
/**
@@ -135,12 +134,13 @@ struct iwl_tx_info {
*/
struct iwl_tx_queue {
struct iwl_queue q;
- struct iwl_tfd_frame *bd;
+ struct iwl_tfd *tfds;
struct iwl_cmd *cmd[TFD_TX_CMD_SLOTS];
struct iwl_tx_info *txb;
- int need_update;
- int sched_retry;
- int active;
+ u8 need_update;
+ u8 sched_retry;
+ u8 active;
+ u8 swq_id;
};
#define IWL_NUM_SCAN_RATES (2)
@@ -186,12 +186,6 @@ struct iwl_channel_info {
u8 fat_extension_channel; /* HT_IE_EXT_CHANNEL_* */
};
-struct iwl4965_clip_group {
- /* maximum power level to prevent clipping for each rate, derived by
- * us from this band's saturation power in EEPROM */
- const s8 clip_powers[IWL_MAX_RATES];
-};
-
#define IWL_TX_FIFO_AC0 0
#define IWL_TX_FIFO_AC1 1
@@ -253,7 +247,8 @@ struct iwl_cmd_meta {
/* The CMD_SIZE_HUGE flag bit indicates that the command
* structure is stored at the end of the shared queue memory. */
u32 flags;
-
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+ DECLARE_PCI_UNMAP_LEN(len)
} __attribute__ ((packed));
#define IWL_CMD_MAX_PAYLOAD 320
@@ -269,24 +264,16 @@ struct iwl_cmd {
struct iwl_cmd_meta meta; /* driver data */
struct iwl_cmd_header hdr; /* uCode API */
union {
- struct iwl_addsta_cmd addsta;
- struct iwl_led_cmd led;
u32 flags;
u8 val8;
u16 val16;
u32 val32;
- struct iwl4965_bt_cmd bt;
- struct iwl4965_rxon_time_cmd rxon_time;
- struct iwl_powertable_cmd powertable;
- struct iwl_qosparam_cmd qosparam;
struct iwl_tx_cmd tx;
- struct iwl4965_rxon_assoc_cmd rxon_assoc;
- struct iwl_rem_sta_cmd rm_sta;
- u8 *indirect;
u8 payload[IWL_CMD_MAX_PAYLOAD];
} __attribute__ ((packed)) cmd;
} __attribute__ ((packed));
+
struct iwl_host_cmd {
u8 id;
u16 len;
@@ -309,7 +296,6 @@ struct iwl_host_cmd {
/**
* struct iwl_rx_queue - Rx queue
- * @processed: Internal index to last handled Rx packet
* @read: Shared index to newest available Rx buffer
* @write: Shared index to oldest written Rx packet
* @free_count: Number of pre-allocated buffers in rx_free
@@ -324,26 +310,19 @@ struct iwl_rx_queue {
dma_addr_t dma_addr;
struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS];
struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE];
- u32 processed;
u32 read;
u32 write;
u32 free_count;
struct list_head rx_free;
struct list_head rx_used;
int need_update;
+ struct iwl_rb_status *rb_stts;
+ dma_addr_t rb_stts_dma;
spinlock_t lock;
};
#define IWL_SUPPORTED_RATES_IE_LEN 8
-#define SCAN_INTERVAL 100
-
-#define MAX_A_CHANNELS 252
-#define MIN_A_CHANNELS 7
-
-#define MAX_B_CHANNELS 14
-#define MIN_B_CHANNELS 1
-
#define MAX_TID_COUNT 9
#define IWL_INVALID_RATE 0xFF
@@ -413,9 +392,8 @@ struct iwl_ht_info {
u8 max_amsdu_size;
u8 ampdu_factor;
u8 mpdu_density;
- u8 supp_mcs_set[16];
+ struct ieee80211_mcs_info mcs;
/* BSS related data */
- u8 control_channel;
u8 extension_chan_offset;
u8 tx_chan_width;
u8 ht_protection;
@@ -444,7 +422,6 @@ union iwl_qos_capabity {
/* QoS structures */
struct iwl_qos_info {
- int qos_enable;
int qos_active;
union iwl_qos_capabity qos_cap;
struct iwl_qosparam_cmd def_qos_parm;
@@ -470,7 +447,7 @@ struct fw_desc {
/* uCode file layout */
struct iwl_ucode {
- __le32 ver; /* major/minor/subminor */
+ __le32 ver; /* major/minor/API/serial */
__le32 inst_size; /* bytes of runtime instructions */
__le32 data_size; /* bytes of runtime data */
__le32 init_size; /* bytes of initialization instructions */
@@ -511,11 +488,15 @@ struct iwl_sensitivity_ranges {
};
-#define IWL_FAT_CHANNEL_52 BIT(IEEE80211_BAND_5GHZ)
+#define KELVIN_TO_CELSIUS(x) ((x)-273)
+#define CELSIUS_TO_KELVIN(x) ((x)+273)
+
/**
* struct iwl_hw_params
* @max_txq_num: Max # Tx queues supported
+ * @dma_chnl_num: Number of Tx DMA/FIFO channels
+ * @scd_bc_tbls_size: size of scheduler byte count tables
* @tx/rx_chains_num: Number of TX/RX chains
* @valid_tx/rx_ant: usable antennas
* @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2)
@@ -528,11 +509,13 @@ struct iwl_sensitivity_ranges {
* @sw_crypto: 0 for hw, 1 for sw
* @max_xxx_size: for ucode uses
* @ct_kill_threshold: temperature threshold
+ * @calib_init_cfg: setup initial calibrations for the hw
* @struct iwl_sensitivity_ranges: range of sensitivity values
- * @first_ampdu_q: first HW queue available for ampdu
*/
struct iwl_hw_params {
- u16 max_txq_num;
+ u8 max_txq_num;
+ u8 dma_chnl_num;
+ u16 scd_bc_tbls_size;
u8 tx_chains_num;
u8 rx_chains_num;
u8 valid_tx_ant;
@@ -549,22 +532,10 @@ struct iwl_hw_params {
u32 max_data_size;
u32 max_bsm_size;
u32 ct_kill_threshold; /* value in hw-dependent units */
+ u32 calib_init_cfg;
const struct iwl_sensitivity_ranges *sens;
- u8 first_ampdu_q;
};
-#define HT_SHORT_GI_20MHZ (1 << 0)
-#define HT_SHORT_GI_40MHZ (1 << 1)
-
-
-#define IWL_RX_HDR(x) ((struct iwl4965_rx_frame_hdr *)(\
- x->u.rx_frame.stats.payload + \
- x->u.rx_frame.stats.phy_count))
-#define IWL_RX_END(x) ((struct iwl4965_rx_frame_end *)(\
- IWL_RX_HDR(x)->payload + \
- le16_to_cpu(IWL_RX_HDR(x)->len)))
-#define IWL_RX_STATS(x) (&x->u.rx_frame.stats)
-#define IWL_RX_DATA(x) (IWL_RX_HDR(x)->payload)
/******************************************************************************
*
@@ -581,13 +552,8 @@ struct iwl_hw_params {
* iwl4965_mac_ <-- mac80211 callback
*
****************************************************************************/
-struct iwl_addsta_cmd;
-extern int iwl_send_add_sta(struct iwl_priv *priv,
- struct iwl_addsta_cmd *sta, u8 flags);
-extern u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr,
- int is_ap, u8 flags, struct ieee80211_ht_info *ht_info);
-extern void iwl4965_update_chain_flags(struct iwl_priv *priv);
-extern int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
+extern void iwl_update_chain_flags(struct iwl_priv *priv);
+extern int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src);
extern const u8 iwl_bcast_addr[ETH_ALEN];
extern int iwl_rxq_stop(struct iwl_priv *priv);
extern void iwl_txq_ctx_stop(struct iwl_priv *priv);
@@ -611,19 +577,15 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge)
}
-struct iwl_priv;
-
-
-/* Structures, enum, and defines specific to the 4965 */
-
-#define IWL_KW_SIZE 0x1000 /*4k */
-
-struct iwl_kw {
- dma_addr_t dma_addr;
- void *v_addr;
+struct iwl_dma_ptr {
+ dma_addr_t dma;
+ void *addr;
size_t size;
};
+#define HT_SHORT_GI_20MHZ (1 << 0)
+#define HT_SHORT_GI_40MHZ (1 << 1)
+
#define IWL_CHANNEL_WIDTH_20MHZ 0
#define IWL_CHANNEL_WIDTH_40MHZ 1
@@ -638,7 +600,6 @@ struct iwl_kw {
#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000
/* Sensitivity and chain noise calibration */
-#define INTERFERENCE_DATA_AVAILABLE __constant_cpu_to_le32(1)
#define INITIALIZATION_VALUE 0xFFFF
#define CAL_NUM_OF_BEACONS 20
#define MAXIMUM_ALLOWED_PATHLOSS 15
@@ -691,13 +652,20 @@ enum iwl4965_calib_enabled_state {
IWL_CALIB_ENABLED = 1,
};
-struct statistics_general_data {
- u32 beacon_silence_rssi_a;
- u32 beacon_silence_rssi_b;
- u32 beacon_silence_rssi_c;
- u32 beacon_energy_a;
- u32 beacon_energy_b;
- u32 beacon_energy_c;
+
+/*
+ * enum iwl_calib
+ * defines the order in which results of initial calibrations
+ * should be sent to the runtime uCode
+ */
+enum iwl_calib {
+ IWL_CALIB_XTAL,
+ IWL_CALIB_DC,
+ IWL_CALIB_LO,
+ IWL_CALIB_TX_IQ,
+ IWL_CALIB_TX_IQ_PERD,
+ IWL_CALIB_BASE_BAND,
+ IWL_CALIB_MAX
};
/* Opaque calibration results */
@@ -766,7 +734,6 @@ enum {
#define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */
-#define IWL_CALIB_MAX 3
struct iwl_priv {
@@ -790,7 +757,7 @@ struct iwl_priv {
#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT
/* spectrum measurement report caching */
- struct iwl4965_spectrum_notification measure_report;
+ struct iwl_spectrum_notification measure_report;
u8 measurement_status;
#endif
/* ucode beacon time */
@@ -801,10 +768,6 @@ struct iwl_priv {
struct iwl_channel_info *channel_info; /* channel info array */
u8 channel_count; /* # of channels */
- /* each calibration channel group in the EEPROM has a derived
- * clip setting for each rate. */
- const struct iwl4965_clip_group clip_groups[5];
-
/* thermal calibration */
s32 temperature; /* degrees Kelvin */
s32 last_temperature;
@@ -818,12 +781,13 @@ struct iwl_priv {
unsigned long scan_start;
unsigned long scan_pass_start;
unsigned long scan_start_tsf;
+ struct iwl_scan_cmd *scan;
int scan_bands;
int one_direct_scan;
u8 direct_ssid_len;
u8 direct_ssid[IW_ESSID_MAX_SIZE];
- struct iwl_scan_cmd *scan;
- u32 scan_tx_ant[IEEE80211_NUM_BANDS];
+ u8 scan_tx_ant[IEEE80211_NUM_BANDS];
+ u8 mgmt_tx_ant;
/* spinlock */
spinlock_t lock; /* protect general shared data */
@@ -840,6 +804,8 @@ struct iwl_priv {
u8 rev_id;
/* uCode images, save to reload in case of failure */
+ u32 ucode_ver; /* version of ucode, copy of
+ iwl_ucode.ver */
struct fw_desc ucode_code; /* runtime inst */
struct fw_desc ucode_data; /* runtime data original */
struct fw_desc ucode_data_backup; /* runtime data save/restore */
@@ -850,7 +816,7 @@ struct iwl_priv {
u8 ucode_write_complete; /* the image write is complete */
- struct iwl4965_rxon_time_cmd rxon_timing;
+ struct iwl_rxon_time_cmd rxon_timing;
/* We declare this const so it can only be
* changed via explicit cast within the
@@ -882,7 +848,6 @@ struct iwl_priv {
u16 active_rate_basic;
u8 assoc_station_added;
- u8 use_ant_b_for_management_frame; /* Tx antenna selection */
u8 start_calib;
struct iwl_sensitivity_data sensitivity_data;
struct iwl_chain_noise_data chain_noise_data;
@@ -903,12 +868,14 @@ struct iwl_priv {
struct iwl_rx_queue rxq;
struct iwl_tx_queue txq[IWL_MAX_NUM_QUEUES];
unsigned long txq_ctx_active_msk;
- struct iwl_kw kw; /* keep warm address */
+ struct iwl_dma_ptr kw; /* keep warm address */
+ struct iwl_dma_ptr scd_bc_tbls;
+
u32 scd_base_addr; /* scheduler sram base address */
unsigned long status;
- int last_rx_rssi; /* From Rx packet statisitics */
+ int last_rx_rssi; /* From Rx packet statistics */
int last_rx_noise; /* From beacon statistics */
/* counts mgmt, ctl, and data packets */
@@ -923,8 +890,6 @@ struct iwl_priv {
unsigned long last_statistics_time;
/* context information */
- u8 essid[IW_ESSID_MAX_SIZE];
- u8 essid_len;
u16 rates_mask;
u32 power_mode;
@@ -965,11 +930,7 @@ struct iwl_priv {
struct ieee80211_vif *vif;
struct iwl_hw_params hw_params;
- /* driver/uCode shared Tx Byte Counts and Rx status */
- void *shared_virt;
- int rb_closed_offset;
- /* Physical Pointer to Tx Byte Counts and Rx status */
- dma_addr_t shared_phys;
+
/* Current association information needed to configure the
* hardware */
@@ -992,7 +953,6 @@ struct iwl_priv {
struct work_struct report_work;
struct work_struct request_scan;
struct work_struct beacon_update;
- struct work_struct set_monitor;
struct tasklet_struct irq_tasklet;
@@ -1004,9 +964,6 @@ struct iwl_priv {
s8 tx_power_user_lmt;
s8 tx_power_channel_lmt;
-#ifdef CONFIG_PM
- u32 pm_state[16];
-#endif
#ifdef CONFIG_IWLWIFI_DEBUG
/* debugging info */
@@ -1091,26 +1048,4 @@ static inline int is_channel_ibss(const struct iwl_channel_info *ch)
return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
}
-#ifdef CONFIG_IWLWIFI_DEBUG
-static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level,
- void *p, u32 len)
-{
- if (!(priv->debug_level & level))
- return;
-
- print_hex_dump(KERN_DEBUG, "iwl data: ", DUMP_PREFIX_OFFSET, 16, 1,
- p, len, 1);
-}
-#else
-static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level,
- void *p, u32 len)
-{
-}
-#endif
-
-extern const struct iwl_channel_info *iwl_get_channel_info(
- const struct iwl_priv *priv, enum ieee80211_band band, u16 channel);
-
-/* Requires full declaration of iwl_priv before including */
-
#endif /* __iwl_dev_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
index 37155755efc5..ce2f47306cea 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -169,10 +169,9 @@ int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv)
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
/* See if we got it */
- ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
- EEPROM_SEM_TIMEOUT);
+ ret = iwl_poll_direct_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
+ EEPROM_SEM_TIMEOUT);
if (ret >= 0) {
IWL_DEBUG_IO("Acquired semaphore after %d tries.\n",
count+1);
@@ -210,10 +209,8 @@ int iwl_eeprom_init(struct iwl_priv *priv)
{
u16 *e;
u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
- u32 r;
int sz = priv->cfg->eeprom_size;
int ret;
- int i;
u16 addr;
/* allocate eeprom */
@@ -241,22 +238,19 @@ int iwl_eeprom_init(struct iwl_priv *priv)
/* eeprom is an array of 16bit values */
for (addr = 0; addr < sz; addr += sizeof(u16)) {
- _iwl_write32(priv, CSR_EEPROM_REG, addr << 1);
- _iwl_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
-
- for (i = 0; i < IWL_EEPROM_ACCESS_TIMEOUT;
- i += IWL_EEPROM_ACCESS_DELAY) {
- r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
- if (r & CSR_EEPROM_REG_READ_VALID_MSK)
- break;
- udelay(IWL_EEPROM_ACCESS_DELAY);
- }
+ u32 r;
+
+ _iwl_write32(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) {
+ ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ IWL_EEPROM_ACCESS_TIMEOUT);
+ if (ret < 0) {
IWL_ERROR("Time out reading EEPROM[%d]\n", addr);
- ret = -ETIMEDOUT;
goto done;
}
+ r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
}
ret = 0;
@@ -279,7 +273,23 @@ EXPORT_SYMBOL(iwl_eeprom_free);
int iwl_eeprom_check_version(struct iwl_priv *priv)
{
- return priv->cfg->ops->lib->eeprom_ops.check_version(priv);
+ u16 eeprom_ver;
+ u16 calib_ver;
+
+ eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
+ calib_ver = priv->cfg->ops->lib->eeprom_ops.calib_version(priv);
+
+ if (eeprom_ver < priv->cfg->eeprom_ver ||
+ calib_ver < priv->cfg->eeprom_calib_ver)
+ goto err;
+
+ return 0;
+err:
+ IWL_ERROR("Unsupported EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n",
+ eeprom_ver, priv->cfg->eeprom_ver,
+ calib_ver, priv->cfg->eeprom_calib_ver);
+ return -EINVAL;
+
}
EXPORT_SYMBOL(iwl_eeprom_check_version);
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
index d3a2a5b4ac56..603c84bed630 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -68,17 +68,14 @@ struct iwl_priv;
/*
* EEPROM access time values:
*
- * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG,
- * then clearing (with subsequent read/modify/write) CSR_EEPROM_REG bit
- * CSR_EEPROM_REG_BIT_CMD (0x2).
+ * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG.
* Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
* When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
* Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
*/
#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */
-#define IWL_EEPROM_ACCESS_DELAY 10 /* uSec */
-#define IWL_EEPROM_SEM_TIMEOUT 10 /* milliseconds */
+#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */
#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */
@@ -147,6 +144,7 @@ struct iwl_eeprom_channel {
/*5000 calibrations */
#define EEPROM_5000_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION)
#define EEPROM_5000_XTAL ((2*0x128) | EEPROM_5000_CALIB_ALL)
+#define EEPROM_5000_TEMPERATURE ((2*0x12A) | EEPROM_5000_CALIB_ALL)
/* 5000 links */
#define EEPROM_5000_LINK_HOST (2*0x64)
@@ -174,6 +172,9 @@ struct iwl_eeprom_channel {
#define EEPROM_5000_REG_BAND_52_FAT_CHANNELS ((0x92)\
| INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */
+/* 5050 Specific */
+#define EEPROM_5050_TX_POWER_VERSION (4)
+#define EEPROM_5050_EEPROM_VERSION (0x21E)
/* 2.4 GHz */
extern const u8 iwl_eeprom_band_1[14];
@@ -371,7 +372,7 @@ struct iwl_eeprom_ops {
int (*verify_signature) (struct iwl_priv *priv);
int (*acquire_semaphore) (struct iwl_priv *priv);
void (*release_semaphore) (struct iwl_priv *priv);
- int (*check_version) (struct iwl_priv *priv);
+ u16 (*calib_version) (struct iwl_priv *priv);
const u8* (*query_addr) (const struct iwl_priv *priv, size_t offset);
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index a72efdf6d1dd..d7da19864550 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -60,6 +60,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+#ifndef __iwl_fh_h__
+#define __iwl_fh_h__
/****************************/
/* Flow Handler Definitions */
@@ -70,7 +72,7 @@
* Addresses are offsets from device's PCI hardware base address.
*/
#define FH_MEM_LOWER_BOUND (0x1000)
-#define FH_MEM_UPPER_BOUND (0x1EF0)
+#define FH_MEM_UPPER_BOUND (0x2000)
/**
* Keep-Warm (KW) buffer base address.
@@ -264,6 +266,7 @@
#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000)
#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000)
+#define FH_RSCSR_FRAME_SIZE_MSK (0x00003FFF) /* bits 0-13 */
/**
* Rx Shared Status Registers (RSSR)
@@ -290,6 +293,13 @@
#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28
+/* TFDB Area - TFDs buffer table */
+#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF)
+#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900)
+#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958)
+#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl))
+#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4)
+
/**
* Transmit DMA Channel Control/Status Registers (TCSR)
*
@@ -316,34 +326,41 @@
#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60)
/* Find Control/Status reg for given Tx DMA/FIFO channel */
-#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * _chnl)
+#define FH49_TCSR_CHNL_NUM (7)
+#define FH50_TCSR_CHNL_NUM (8)
+
+/* TCSR: tx_config register values */
+#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl))
+#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
+ (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8)
+
+#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
-#define FH_TCSR_CHNL_NUM (7)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000)
+#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000)
-#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12)
-#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * _chnl)
-#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x4)
-#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \
- (FH_TCSR_LOWER_BOUND + 0x20 * _chnl + 0x8)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20)
+#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12)
/**
* Tx Shared Status Registers (TSSR)
@@ -360,7 +377,7 @@
#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0)
#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0)
-#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010)
+#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010)
#define FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) ((1 << (_chnl)) << 24)
#define FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl) ((1 << (_chnl)) << 16)
@@ -369,25 +386,99 @@
(FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) | \
FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl))
-
-
-#define FH_REGS_LOWER_BOUND (0x1000)
-#define FH_REGS_UPPER_BOUND (0x2000)
-
/* Tx service channels */
-#define FH_SRVC_CHNL (9)
-#define FH_SRVC_LOWER_BOUND (FH_REGS_LOWER_BOUND + 0x9C8)
-#define FH_SRVC_UPPER_BOUND (FH_REGS_LOWER_BOUND + 0x9D0)
+#define FH_SRVC_CHNL (9)
+#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8)
+#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \
(FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4)
-/* TFDB Area - TFDs buffer table */
-#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF)
-#define FH_TFDIB_LOWER_BOUND (FH_REGS_LOWER_BOUND + 0x900)
-#define FH_TFDIB_UPPER_BOUND (FH_REGS_LOWER_BOUND + 0x958)
-#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl))
-#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4)
+#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98)
+/* Instruct FH to increment the retry count of a packet when
+ * it is brought from the memory to TX-FIFO
+ */
+#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002)
-/* TCSR: tx_config register values */
-#define FH_RSCSR_FRAME_SIZE_MSK (0x00003FFF) /* bits 0-13 */
+/**
+ * struct iwl_rb_status - reseve buffer status
+ * host memory mapped FH registers
+ * @closed_rb_num [0:11] - Indicates the index of the RB which was closed
+ * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
+ * @finished_rb_num [0:11] - Indicates the index of the current RB
+ * in which the last frame was written to
+ * @finished_fr_num [0:11] - Indicates the index of the RX Frame
+ * which was transfered
+ */
+struct iwl_rb_status {
+ __le16 closed_rb_num;
+ __le16 closed_fr_num;
+ __le16 finished_rb_num;
+ __le16 finished_fr_nam;
+} __attribute__ ((packed));
+
+
+#define TFD_QUEUE_SIZE_MAX (256)
+#define TFD_QUEUE_SIZE_BC_DUP (64)
+#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP)
+#define IWL_TX_DMA_MASK DMA_BIT_MASK(36)
+#define IWL_NUM_OF_TBS 20
+
+static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr)
+{
+ return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF;
+}
+/**
+ * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor
+ *
+ * This structure contains dma address and length of transmission address
+ *
+ * @lo: low [31:0] portion of the dma address of TX buffer
+ * every even is unaligned on 16 bit boundary
+ * @hi_n_len 0-3 [35:32] portion of dma
+ * 4-15 length of the tx buffer
+ */
+struct iwl_tfd_tb {
+ __le32 lo;
+ __le16 hi_n_len;
+} __attribute__((packed));
+
+/**
+ * struct iwl_tfd
+ *
+ * Transmit Frame Descriptor (TFD)
+ *
+ * @ __reserved1[3] reserved
+ * @ num_tbs 0-4 number of active tbs
+ * 5 reserved
+ * 6-7 padding (not used)
+ * @ tbs[20] transmit frame buffer descriptors
+ * @ __pad padding
+ *
+ * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM.
+ * Both driver and device share these circular buffers, each of which must be
+ * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes
+ *
+ * Driver must indicate the physical address of the base of each
+ * circular buffer via the FH_MEM_CBBC_QUEUE registers.
+ *
+ * Each TFD contains pointer/size information for up to 20 data buffers
+ * in host DRAM. These buffers collectively contain the (one) frame described
+ * by the TFD. Each buffer must be a single contiguous block of memory within
+ * itself, but buffers may be scattered in host DRAM. Each buffer has max size
+ * of (4K - 4). The concatenates all of a TFD's buffers into a single
+ * Tx frame, up to 8 KBytes in size.
+ *
+ * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx.
+ */
+struct iwl_tfd {
+ u8 __reserved1[3];
+ u8 num_tbs;
+ struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS];
+ __le32 __pad;
+} __attribute__ ((packed));
+
+
+/* Keep Warm Size */
+#define IWL_KW_SIZE 0x1000 /* 4k */
+#endif /* !__iwl_fh_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
index 8300f3d00a06..01a2169cecec 100644
--- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
@@ -22,7 +22,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -36,7 +36,7 @@
#include "iwl-core.h"
-#define IWL_CMD(x) case x : return #x
+#define IWL_CMD(x) case x: return #x
const char *get_cmd_string(u8 cmd)
{
diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h
index 41eed6793328..ca4f638ab9d0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-helpers.h
+++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -32,110 +32,6 @@
#include <linux/ctype.h>
-/*
- * The structures defined by the hardware/uCode interface
- * have bit-wise operations. For each bit-field there is
- * a data symbol in the structure, the start bit position
- * and the length of the bit-field.
- *
- * iwl_get_bits and iwl_set_bits will return or set the
- * appropriate bits on a 32-bit value.
- *
- * IWL_GET_BITS and IWL_SET_BITS use symbol expansion to
- * expand out to the appropriate call to iwl_get_bits
- * and iwl_set_bits without having to reference all of the
- * numerical constants and defines provided in the hardware
- * definition
- */
-
-/**
- * iwl_get_bits - Extract a hardware bit-field value
- * @src: source hardware value (__le32)
- * @pos: bit-position (0-based) of first bit of value
- * @len: length of bit-field
- *
- * iwl_get_bits will return the bit-field in cpu endian ordering.
- *
- * NOTE: If used from IWL_GET_BITS then pos and len are compile-constants and
- * will collapse to minimal code by the compiler.
- */
-static inline u32 iwl_get_bits(__le32 src, u8 pos, u8 len)
-{
- u32 tmp = le32_to_cpu(src);
-
- tmp >>= pos;
- tmp &= (1UL << len) - 1;
- return tmp;
-}
-
-/**
- * iwl_set_bits - Set a hardware bit-field value
- * @dst: Address of __le32 hardware value
- * @pos: bit-position (0-based) of first bit of value
- * @len: length of bit-field
- * @val: cpu endian value to encode into the bit-field
- *
- * iwl_set_bits will encode val into dst, masked to be len bits long at bit
- * position pos.
- *
- * NOTE: If used IWL_SET_BITS pos and len will be compile-constants and
- * will collapse to minimal code by the compiler.
- */
-static inline void iwl_set_bits(__le32 *dst, u8 pos, u8 len, int val)
-{
- u32 tmp = le32_to_cpu(*dst);
-
- tmp &= ~(((1UL << len) - 1) << pos);
- tmp |= (val & ((1UL << len) - 1)) << pos;
- *dst = cpu_to_le32(tmp);
-}
-
-static inline void iwl_set_bits16(__le16 *dst, u8 pos, u8 len, int val)
-{
- u16 tmp = le16_to_cpu(*dst);
-
- tmp &= ~((1UL << (pos + len)) - (1UL << pos));
- tmp |= (val & ((1UL << len) - 1)) << pos;
- *dst = cpu_to_le16(tmp);
-}
-
-/*
- * The bit-field definitions in iwl-xxxx-hw.h are in the form of:
- *
- * struct example {
- * __le32 val1;
- * #define IWL_name_POS 8
- * #define IWL_name_LEN 4
- * #define IWL_name_SYM val1
- * };
- *
- * The IWL_SET_BITS and IWL_GET_BITS macros are provided to allow the driver
- * to call:
- *
- * struct example bar;
- * u32 val = IWL_GET_BITS(bar, name);
- * val = val * 2;
- * IWL_SET_BITS(bar, name, val);
- *
- * All cpu / host ordering, masking, and shifts are performed by the macros
- * and iwl_{get,set}_bits.
- *
- */
-#define IWL_SET_BITS(s, sym, v) \
- iwl_set_bits(&(s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
- IWL_ ## sym ## _LEN, (v))
-
-#define IWL_SET_BITS16(s, sym, v) \
- iwl_set_bits16(&(s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
- IWL_ ## sym ## _LEN, (v))
-
-#define IWL_GET_BITS(s, sym) \
- iwl_get_bits((s).IWL_ ## sym ## _SYM, IWL_ ## sym ## _POS, \
- IWL_ ## sym ## _LEN)
-
-
-#define KELVIN_TO_CELSIUS(x) ((x)-273)
-#define CELSIUS_TO_KELVIN(x) ((x)+273)
#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
@@ -159,11 +55,6 @@ static inline unsigned long elapsed_jiffies(unsigned long start,
return end + (MAX_JIFFY_OFFSET - start) + 1;
}
-static inline u8 iwl_get_dma_hi_address(dma_addr_t addr)
-{
- return sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0;
-}
-
/**
* iwl_queue_inc_wrap - increment queue index, wrap back to beginning
* @index -- current index
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 9740fcc1805e..0a92e7431ada 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -55,7 +55,7 @@
* _iwl_read32.)
*
* These declarations are *extremely* useful in quickly isolating code deltas
- * which result in misconfiguring of the hardware I/O. In combination with
+ * which result in misconfiguration of the hardware I/O. In combination with
* git-bisect and the IO debug level you can quickly determine the specific
* commit which breaks the IO sequence to the hardware.
*
@@ -87,17 +87,18 @@ static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs)
#define iwl_read32(p, o) _iwl_read32(p, o)
#endif
+#define IWL_POLL_INTERVAL 10 /* microseconds */
static inline int _iwl_poll_bit(struct iwl_priv *priv, u32 addr,
u32 bits, u32 mask, int timeout)
{
- int i = 0;
+ int t = 0;
do {
if ((_iwl_read32(priv, addr) & mask) == (bits & mask))
- return i;
- mdelay(10);
- i += 10;
- } while (i < timeout);
+ return t;
+ udelay(IWL_POLL_INTERVAL);
+ t += IWL_POLL_INTERVAL;
+ } while (t < timeout);
return -ETIMEDOUT;
}
@@ -109,7 +110,7 @@ static inline int __iwl_poll_bit(const char *f, u32 l,
int ret = _iwl_poll_bit(priv, addr, bits, mask, timeout);
IWL_DEBUG_IO("poll_bit(0x%08X, 0x%08X, 0x%08X) - %s- %s %d\n",
addr, bits, mask,
- unlikely(ret == -ETIMEDOUT)?"timeout":"", f, l);
+ unlikely(ret == -ETIMEDOUT) ? "timeout" : "", f, l);
return ret;
}
#define iwl_poll_bit(priv, addr, bits, mask, timeout) \
@@ -269,19 +270,10 @@ static inline void iwl_write_reg_buf(struct iwl_priv *priv,
}
}
-static inline int _iwl_poll_direct_bit(struct iwl_priv *priv,
- u32 addr, u32 mask, int timeout)
+static inline int _iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr,
+ u32 mask, int timeout)
{
- int i = 0;
-
- do {
- if ((_iwl_read_direct32(priv, addr) & mask) == mask)
- return i;
- mdelay(10);
- i += 10;
- } while (i < timeout);
-
- return -ETIMEDOUT;
+ return _iwl_poll_bit(priv, addr, mask, mask, timeout);
}
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -308,6 +300,7 @@ static inline int __iwl_poll_direct_bit(const char *f, u32 l,
static inline u32 _iwl_read_prph(struct iwl_priv *priv, u32 reg)
{
_iwl_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
+ rmb();
return _iwl_read_direct32(priv, HBUS_TARG_PRPH_RDAT);
}
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -330,6 +323,7 @@ static inline void _iwl_write_prph(struct iwl_priv *priv,
{
_iwl_write_direct32(priv, HBUS_TARG_PRPH_WADDR,
((addr & 0x0000FFFF) | (3 << 24)));
+ wmb();
_iwl_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val);
}
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -392,12 +386,14 @@ static inline void iwl_clear_bits_prph(struct iwl_priv
static inline u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr)
{
iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
+ rmb();
return iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
}
static inline void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val)
{
iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+ wmb();
iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
}
@@ -405,6 +401,7 @@ static inline void iwl_write_targ_mem_buf(struct iwl_priv *priv, u32 addr,
u32 len, u32 *values)
{
iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
+ wmb();
for (; 0 < len; len -= sizeof(u32), values++)
iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
index 4eee1b163cd2..11eccd7d268c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -41,7 +41,6 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-io.h"
-#include "iwl-helpers.h"
#ifdef CONFIG_IWLWIFI_DEBUG
static const char *led_type_str[] = {
@@ -278,7 +277,7 @@ static int iwl_get_blink_rate(struct iwl_priv *priv)
/* FIXME: + priv->rx_stats[2].bytes; */
s64 tpt = current_tpt - priv->led_tpt;
- if (tpt < 0) /* wrapparound */
+ if (tpt < 0) /* wraparound */
tpt = -tpt;
IWL_DEBUG_LED("tpt %lld current_tpt %llu\n",
@@ -293,7 +292,7 @@ static int iwl_get_blink_rate(struct iwl_priv *priv)
if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
break;
- IWL_DEBUG_LED("LED BLINK IDX=%d", i);
+ IWL_DEBUG_LED("LED BLINK IDX=%d\n", i);
return i;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h
index 588c9ad20e83..021e00bcd1be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.h
+++ b/drivers/net/wireless/iwlwifi/iwl-led.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index 60a03d2d2d0e..75ca6a542174 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -39,7 +39,6 @@
#include "iwl-commands.h"
#include "iwl-debug.h"
#include "iwl-power.h"
-#include "iwl-helpers.h"
/*
* Setting power level allow the card to go to sleep when not busy
@@ -80,7 +79,7 @@
#define IWL_REDUCED_POWER_TEMPERATURE 95
/* default power management (not Tx power) table values */
-/* for tim 0-10 */
+/* for TIM 0-10 */
static struct iwl_power_vec_entry range_0[IWL_POWER_MAX] = {
{{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
@@ -91,7 +90,7 @@ static struct iwl_power_vec_entry range_0[IWL_POWER_MAX] = {
};
-/* for tim = 3-10 */
+/* for TIM = 3-10 */
static struct iwl_power_vec_entry range_1[IWL_POWER_MAX] = {
{{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
@@ -101,7 +100,7 @@ static struct iwl_power_vec_entry range_1[IWL_POWER_MAX] = {
{{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2}
};
-/* for tim > 11 */
+/* for TIM > 11 */
static struct iwl_power_vec_entry range_2[IWL_POWER_MAX] = {
{{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
@@ -183,7 +182,7 @@ static int iwl_power_init_handle(struct iwl_priv *priv)
return 0;
}
-/* adjust power command according to dtim period and power level*/
+/* adjust power command according to DTIM period and power level*/
static int iwl_update_power_command(struct iwl_priv *priv,
struct iwl_powertable_cmd *cmd,
u16 mode)
@@ -257,15 +256,11 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
struct iwl_power_mgr *setting = &(priv->power_data);
int ret = 0;
u16 uninitialized_var(final_mode);
+ bool update_chains;
/* Don't update the RX chain when chain noise calibration is running */
- if (priv->chain_noise_data.state != IWL_CHAIN_NOISE_DONE &&
- priv->chain_noise_data.state != IWL_CHAIN_NOISE_ALIVE) {
- IWL_DEBUG_POWER("Cannot update the power, chain noise "
- "calibration running: %d\n",
- priv->chain_noise_data.state);
- return -EAGAIN;
- }
+ update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE ||
+ priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE;
/* If on battery, set to 3,
* if plugged into AC power, set to CAM ("continuously aware mode"),
@@ -313,9 +308,12 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
else
set_bit(STATUS_POWER_PMI, &priv->status);
- if (priv->cfg->ops->lib->update_chain_flags)
+ if (priv->cfg->ops->lib->update_chain_flags && update_chains)
priv->cfg->ops->lib->update_chain_flags(priv);
-
+ else
+ IWL_DEBUG_POWER("Cannot update the power, chain noise "
+ "calibration running: %d\n",
+ priv->chain_noise_data.state);
if (!ret)
setting->power_mode = final_mode;
}
@@ -325,7 +323,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
EXPORT_SYMBOL(iwl_power_update_mode);
/* Allow other iwl code to disable/enable power management active
- * this will be usefull for rate scale to disable PM during heavy
+ * this will be useful for rate scale to disable PM during heavy
* Tx/Rx activities
*/
int iwl_power_disable_management(struct iwl_priv *priv, u32 ms)
@@ -352,8 +350,8 @@ int iwl_power_disable_management(struct iwl_priv *priv, u32 ms)
EXPORT_SYMBOL(iwl_power_disable_management);
/* Allow other iwl code to disable/enable power management active
- * this will be usefull for rate scale to disable PM during hight
- * valume activities
+ * this will be useful for rate scale to disable PM during high
+ * volume activities
*/
int iwl_power_enable_management(struct iwl_priv *priv)
{
@@ -391,7 +389,7 @@ int iwl_power_set_system_mode(struct iwl_priv *priv, u16 mode)
}
EXPORT_SYMBOL(iwl_power_set_system_mode);
-/* initilize to default */
+/* initialize to default */
void iwl_power_initialize(struct iwl_priv *priv)
{
@@ -443,7 +441,7 @@ static void iwl_bg_set_power_save(struct work_struct *work)
mutex_lock(&priv->mutex);
- /* on starting association we disable power managment
+ /* on starting association we disable power management
* until association, if association failed then this
* timer will expire and enable PM again.
*/
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h
index df484a90ae64..fa098d8975ce 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.h
+++ b/drivers/net/wireless/iwlwifi/iwl-power.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#ifndef __iwl_power_setting_h__
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index ee5afd48d3af..b7a5f23351c3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -25,7 +25,7 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -158,9 +158,9 @@
*
* 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction
* images in host DRAM. The last register loaded must be the instruction
- * bytecount register ("1" in MSbit tells initialization uCode to load
+ * byte count register ("1" in MSbit tells initialization uCode to load
* the runtime uCode):
- * BSM_DRAM_INST_BYTECOUNT_REG = bytecount | BSM_DRAM_INST_LOAD
+ * BSM_DRAM_INST_BYTECOUNT_REG = byte count | BSM_DRAM_INST_LOAD
*
* 5) Wait for "alive" notification, then issue normal runtime commands.
*
@@ -244,7 +244,7 @@
/**
* Tx Scheduler
*
- * The Tx Scheduler selects the next frame to be transmitted, chosing TFDs
+ * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs
* (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in
* host DRAM. It steers each frame's Tx command (which contains the frame
* data) into one of up to 7 prioritized Tx DMA FIFO channels within the
diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.c b/drivers/net/wireless/iwlwifi/iwl-rfkill.c
index 5d642298f04c..4b69da30665c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rfkill.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#include <linux/kernel.h>
@@ -34,8 +34,6 @@
#include "iwl-eeprom.h"
#include "iwl-dev.h"
#include "iwl-core.h"
-#include "iwl-helpers.h"
-
/* software rf-kill from user */
static int iwl_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
@@ -64,7 +62,7 @@ static int iwl_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
iwl_radio_kill_sw_disable_radio(priv);
break;
default:
- IWL_WARNING("we recieved unexpected RFKILL state %d\n", state);
+ IWL_WARNING("we received unexpected RFKILL state %d\n", state);
break;
}
out_unlock:
@@ -83,7 +81,7 @@ int iwl_rfkill_init(struct iwl_priv *priv)
IWL_DEBUG_RF_KILL("Initializing RFKILL.\n");
priv->rfkill = rfkill_allocate(device, RFKILL_TYPE_WLAN);
if (!priv->rfkill) {
- IWL_ERROR("Unable to allocate rfkill device.\n");
+ IWL_ERROR("Unable to allocate RFKILL device.\n");
ret = -ENOMEM;
goto error;
}
@@ -99,7 +97,7 @@ int iwl_rfkill_init(struct iwl_priv *priv)
ret = rfkill_register(priv->rfkill);
if (ret) {
- IWL_ERROR("Unable to register rfkill: %d\n", ret);
+ IWL_ERROR("Unable to register RFKILL: %d\n", ret);
goto free_rfkill;
}
@@ -127,7 +125,7 @@ void iwl_rfkill_unregister(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_rfkill_unregister);
-/* set rf-kill to the right state. */
+/* set RFKILL to the right state. */
void iwl_rfkill_set_hw_state(struct iwl_priv *priv)
{
if (!priv->rfkill)
diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.h b/drivers/net/wireless/iwlwifi/iwl-rfkill.h
index 402fd4c781da..86dc055a2e94 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rfkill.h
+++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#ifndef __iwl_rf_kill_h__
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 0509c16dbe75..c5f1aa0feac8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -218,8 +218,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv)
/* If we've added more space for the firmware to place data, tell it.
* Increment device's write pointer in multiples of 8. */
- if ((write != (rxq->write & ~0x7))
- || (abs(rxq->write - rxq->read) > 7)) {
+ if (write != (rxq->write & ~0x7)) {
spin_lock_irqsave(&rxq->lock, flags);
rxq->need_update = 1;
spin_unlock_irqrestore(&rxq->lock, flags);
@@ -245,25 +244,31 @@ void iwl_rx_allocate(struct iwl_priv *priv)
struct list_head *element;
struct iwl_rx_mem_buffer *rxb;
unsigned long flags;
- spin_lock_irqsave(&rxq->lock, flags);
- while (!list_empty(&rxq->rx_used)) {
+
+ while (1) {
+ spin_lock_irqsave(&rxq->lock, flags);
+
+ if (list_empty(&rxq->rx_used)) {
+ spin_unlock_irqrestore(&rxq->lock, flags);
+ return;
+ }
element = rxq->rx_used.next;
rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
+ list_del(element);
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
/* Alloc a new receive buffer */
rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
- __GFP_NOWARN | GFP_ATOMIC);
+ GFP_KERNEL);
if (!rxb->skb) {
- if (net_ratelimit())
- printk(KERN_CRIT DRV_NAME
- ": Can not allocate SKB buffers\n");
+ printk(KERN_CRIT DRV_NAME
+ "Can not allocate SKB buffers\n");
/* We don't reschedule replenish work here -- we will
* call the restock method and if it still needs
* more buffers it will schedule replenish */
break;
}
- priv->alloc_rxb_skb++;
- list_del(element);
/* Get physical address of RB/SKB */
rxb->real_dma_addr = pci_map_single(
@@ -277,12 +282,15 @@ void iwl_rx_allocate(struct iwl_priv *priv)
rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256);
skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr);
+ spin_lock_irqsave(&rxq->lock, flags);
+
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
+ priv->alloc_rxb_skb++;
+
+ spin_unlock_irqrestore(&rxq->lock, flags);
}
- spin_unlock_irqrestore(&rxq->lock, flags);
}
-EXPORT_SYMBOL(iwl_rx_allocate);
void iwl_rx_replenish(struct iwl_priv *priv)
{
@@ -317,7 +325,10 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd,
rxq->dma_addr);
+ pci_free_consistent(priv->pci_dev, sizeof(struct iwl_rb_status),
+ rxq->rb_stts, rxq->rb_stts_dma);
rxq->bd = NULL;
+ rxq->rb_stts = NULL;
}
EXPORT_SYMBOL(iwl_rx_queue_free);
@@ -334,7 +345,12 @@ int iwl_rx_queue_alloc(struct iwl_priv *priv)
/* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */
rxq->bd = pci_alloc_consistent(dev, 4 * RX_QUEUE_SIZE, &rxq->dma_addr);
if (!rxq->bd)
- return -ENOMEM;
+ goto err_bd;
+
+ rxq->rb_stts = pci_alloc_consistent(dev, sizeof(struct iwl_rb_status),
+ &rxq->rb_stts_dma);
+ if (!rxq->rb_stts)
+ goto err_rb;
/* Fill the rx_used queue with _all_ of the Rx buffers */
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++)
@@ -346,6 +362,12 @@ int iwl_rx_queue_alloc(struct iwl_priv *priv)
rxq->free_count = 0;
rxq->need_update = 0;
return 0;
+
+err_rb:
+ pci_free_consistent(priv->pci_dev, 4 * RX_QUEUE_SIZE, rxq->bd,
+ rxq->dma_addr);
+err_bd:
+ return -ENOMEM;
}
EXPORT_SYMBOL(iwl_rx_queue_alloc);
@@ -412,10 +434,10 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
/* Tell device where in DRAM to update its Rx status */
iwl_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG,
- (priv->shared_phys + priv->rb_closed_offset) >> 4);
+ rxq->rb_stts_dma >> 4);
/* Enable Rx DMA
- * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set becuase of HW bug in
+ * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
* the credit mechanism in 5000 HW RX FIFO
* Direct rx interrupts to hosts
* Rx buffer size 4 or 8k
@@ -426,6 +448,7 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
+ FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
rb_size|
(rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
(rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
@@ -453,10 +476,8 @@ int iwl_rxq_stop(struct iwl_priv *priv)
/* stop Rx DMA */
iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
- ret = iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
- (1 << 24), 1000);
- if (ret < 0)
- IWL_ERROR("Can't stop Rx DMA.\n");
+ iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
+ FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
iwl_release_nic_access(priv);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -470,7 +491,7 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
- struct iwl4965_missed_beacon_notif *missed_beacon;
+ struct iwl_missed_beacon_notif *missed_beacon;
missed_beacon = &pkt->u.missed_beacon;
if (le32_to_cpu(missed_beacon->consequtive_missed_beacons) > 5) {
@@ -485,49 +506,6 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
}
EXPORT_SYMBOL(iwl_rx_missed_beacon_notif);
-int iwl_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn)
-{
- unsigned long flags;
- int sta_id;
-
- sta_id = iwl_find_station(priv, addr);
- if (sta_id == IWL_INVALID_STATION)
- return -ENXIO;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].sta.station_flags_msk = 0;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
- priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
- priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return iwl_send_add_sta(priv, &priv->stations[sta_id].sta,
- CMD_ASYNC);
-}
-EXPORT_SYMBOL(iwl_rx_agg_start);
-
-int iwl_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
-{
- unsigned long flags;
- int sta_id;
-
- sta_id = iwl_find_station(priv, addr);
- if (sta_id == IWL_INVALID_STATION)
- return -ENXIO;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].sta.station_flags_msk = 0;
- priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
- priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- return iwl_send_add_sta(priv, &priv->stations[sta_id].sta,
- CMD_ASYNC);
-}
-EXPORT_SYMBOL(iwl_rx_agg_stop);
-
/* Calculate noise level, based on measurements during network silence just
* before arriving beacon. This measurement can be done only if we know
@@ -651,20 +629,24 @@ static int iwl_calc_sig_qual(int rssi_dbm, int noise_dbm)
return sig_qual;
}
-#ifdef CONFIG_IWLWIFI_DEBUG
+/* Calc max signal level (dBm) among 3 possible receivers */
+static inline int iwl_calc_rssi(struct iwl_priv *priv,
+ struct iwl_rx_phy_res *rx_resp)
+{
+ return priv->cfg->ops->utils->calc_rssi(priv, rx_resp);
+}
+#ifdef CONFIG_IWLWIFI_DEBUG
/**
* iwl_dbg_report_frame - dump frame to syslog during debug sessions
*
* You may hack this function to show different aspects of received frames,
* including selective frame dumps.
- * group100 parameter selects whether to show 1 out of 100 good frames.
- *
- * TODO: This was originally written for 3945, need to audit for
- * proper operation with 4965.
+ * group100 parameter selects whether to show 1 out of 100 good data frames.
+ * All beacon and probe response frames are printed.
*/
static void iwl_dbg_report_frame(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt,
+ struct iwl_rx_phy_res *phy_res, u16 length,
struct ieee80211_hdr *header, int group100)
{
u32 to_us;
@@ -676,20 +658,9 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv,
u16 seq_ctl;
u16 channel;
u16 phy_flags;
- int rate_sym;
- u16 length;
- u16 status;
- u16 bcn_tmr;
+ u32 rate_n_flags;
u32 tsf_low;
- u64 tsf;
- u8 rssi;
- u8 agc;
- u16 sig_avg;
- u16 noise_diff;
- struct iwl4965_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
- struct iwl4965_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
- struct iwl4965_rx_frame_end *rx_end = IWL_RX_END(pkt);
- u8 *data = IWL_RX_DATA(pkt);
+ int rssi;
if (likely(!(priv->debug_level & IWL_DL_RX)))
return;
@@ -699,22 +670,13 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv,
seq_ctl = le16_to_cpu(header->seq_ctrl);
/* metadata */
- channel = le16_to_cpu(rx_hdr->channel);
- phy_flags = le16_to_cpu(rx_hdr->phy_flags);
- rate_sym = rx_hdr->rate;
- length = le16_to_cpu(rx_hdr->len);
-
- /* end-of-frame status and timestamp */
- status = le32_to_cpu(rx_end->status);
- bcn_tmr = le32_to_cpu(rx_end->beacon_timestamp);
- tsf_low = le64_to_cpu(rx_end->timestamp) & 0x0ffffffff;
- tsf = le64_to_cpu(rx_end->timestamp);
+ channel = le16_to_cpu(phy_res->channel);
+ phy_flags = le16_to_cpu(phy_res->phy_flags);
+ rate_n_flags = le32_to_cpu(phy_res->rate_n_flags);
/* signal statistics */
- rssi = rx_stats->rssi;
- agc = rx_stats->agc;
- sig_avg = le16_to_cpu(rx_stats->sig_avg);
- noise_diff = le16_to_cpu(rx_stats->noise_diff);
+ rssi = iwl_calc_rssi(priv, phy_res);
+ tsf_low = le64_to_cpu(phy_res->timestamp) & 0x0ffffffff;
to_us = !compare_ether_addr(header->addr1, priv->mac_addr);
@@ -768,11 +730,13 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv,
else
title = "Frame";
- rate_idx = iwl_hwrate_to_plcp_idx(rate_sym);
- if (unlikely(rate_idx == -1))
+ rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
+ if (unlikely((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT))) {
bitrate = 0;
- else
+ WARN_ON_ONCE(1);
+ } else {
bitrate = iwl_rates[rate_idx].ieee / 2;
+ }
/* print frame summary.
* MAC addresses show just the last byte (for brevity),
@@ -784,24 +748,17 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv,
length, rssi, channel, bitrate);
else {
/* src/dst addresses assume managed mode */
- IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, "
- "src=0x%02x, rssi=%u, tim=%lu usec, "
+ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, src=0x%02x, "
+ "len=%u, rssi=%d, tim=%lu usec, "
"phy=0x%02x, chnl=%d\n",
title, le16_to_cpu(fc), header->addr1[5],
- header->addr3[5], rssi,
+ header->addr3[5], length, rssi,
tsf_low - priv->scan_start_tsf,
phy_flags, channel);
}
}
if (print_dump)
- iwl_print_hex_dump(priv, IWL_DL_RX, data, length);
-}
-#else
-static inline void iwl_dbg_report_frame(struct iwl_priv *priv,
- struct iwl_rx_packet *pkt,
- struct ieee80211_hdr *header,
- int group100)
-{
+ iwl_print_hex_dump(priv, IWL_DL_RX, header, length);
}
#endif
@@ -995,46 +952,6 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
rxb->skb = NULL;
}
-/* Calc max signal level (dBm) among 3 possible receivers */
-static inline int iwl_calc_rssi(struct iwl_priv *priv,
- struct iwl_rx_phy_res *rx_resp)
-{
- return priv->cfg->ops->utils->calc_rssi(priv, rx_resp);
-}
-
-
-static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sta_lock, flags);
- priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
- priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
- priv->stations[sta_id].sta.sta.modify_mask = 0;
- priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
- spin_unlock_irqrestore(&priv->sta_lock, flags);
-
- iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
-}
-
-static void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr)
-{
- /* FIXME: need locking over ps_status ??? */
- u8 sta_id = iwl_find_station(priv, addr);
-
- if (sta_id != IWL_INVALID_STATION) {
- u8 sta_awake = priv->stations[sta_id].
- ps_status == STA_PS_STATUS_WAKE;
-
- if (sta_awake && ps_bit)
- priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
- else if (!sta_awake && !ps_bit) {
- iwl_sta_modify_ps_wake(priv, sta_id);
- priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
- }
- }
-}
-
/* This is necessary only for a number of statistics, see the caller. */
static int iwl_is_network_packet(struct iwl_priv *priv,
struct ieee80211_hdr *header)
@@ -1157,9 +1074,10 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE;
/* Set "1" to report good data frames in groups of 100 */
- /* FIXME: need to optimze the call: */
- iwl_dbg_report_frame(priv, pkt, header, 1);
-
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (unlikely(priv->debug_level & IWL_DL_RX))
+ iwl_dbg_report_frame(priv, rx_start, len, header, 1);
+#endif
IWL_DEBUG_STATS_LIMIT("Rssi %d, noise %d, qual %d, TSF %llu\n",
rx_status.signal, rx_status.noise, rx_status.signal,
(unsigned long long)rx_status.mactime);
@@ -1168,12 +1086,12 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
* "antenna number"
*
* It seems that the antenna field in the phy flags value
- * is actually a bitfield. This is undefined by radiotap,
+ * is actually a bit field. This is undefined by radiotap,
* it wants an actual antenna number but I always get "7"
* for most legacy frames I receive indicating that the
* same frame was received on all three RX chains.
*
- * I think this field should be removed in favour of a
+ * I think this field should be removed in favor of a
* new 802.11n radiotap field "RX chains" that is defined
* as a bitmask.
*/
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index c89365e2ca58..3c803f6922ef 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -22,11 +22,13 @@
* in the file called LICENSE.GPL.
*
* Contact Information:
- * Tomas Winkler <tomas.winkler@intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
-#include <net/mac80211.h>
+#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <net/lib80211.h>
+#include <net/mac80211.h>
#include "iwl-eeprom.h"
#include "iwl-dev.h"
@@ -64,54 +66,6 @@
#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1))))
-static int scan_tx_ant[3] = {
- RATE_MCS_ANT_A_MSK, RATE_MCS_ANT_B_MSK, RATE_MCS_ANT_C_MSK
-};
-
-
-
-static int iwl_is_empty_essid(const char *essid, int essid_len)
-{
- /* Single white space is for Linksys APs */
- if (essid_len == 1 && essid[0] == ' ')
- return 1;
-
- /* Otherwise, if the entire essid is 0, we assume it is hidden */
- while (essid_len) {
- essid_len--;
- if (essid[essid_len] != '\0')
- return 0;
- }
-
- return 1;
-}
-
-
-
-static const char *iwl_escape_essid(const char *essid, u8 essid_len)
-{
- static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- const char *s = essid;
- char *d = escaped;
-
- if (iwl_is_empty_essid(essid, essid_len)) {
- memcpy(escaped, "<hidden>", sizeof("<hidden>"));
- return escaped;
- }
-
- essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
- while (essid_len--) {
- if (*s == '\0') {
- *d++ = '\\';
- *d++ = '0';
- s++;
- } else
- *d++ = *s++;
- }
- *d = '\0';
- return escaped;
-}
-
/**
* iwl_scan_cancel - Cancel any currently executing HW scan
*
@@ -455,10 +409,11 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
void iwl_init_scan_params(struct iwl_priv *priv)
{
+ u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1;
if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ])
- priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = RATE_MCS_ANT_INIT_IND;
+ priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx;
if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
- priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = RATE_MCS_ANT_INIT_IND;
+ priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
}
int iwl_scan_initiate(struct iwl_priv *priv)
@@ -550,7 +505,7 @@ static void iwl_ht_cap_to_ie(const struct ieee80211_supported_band *sband,
{
struct ieee80211_ht_cap *ht_cap;
- if (!sband || !sband->ht_info.ht_supported)
+ if (!sband || !sband->ht_cap.ht_supported)
return;
if (*left < sizeof(struct ieee80211_ht_cap))
@@ -559,12 +514,12 @@ static void iwl_ht_cap_to_ie(const struct ieee80211_supported_band *sband,
*pos++ = sizeof(struct ieee80211_ht_cap);
ht_cap = (struct ieee80211_ht_cap *) pos;
- ht_cap->cap_info = cpu_to_le16(sband->ht_info.cap);
- memcpy(ht_cap->supp_mcs_set, sband->ht_info.supp_mcs_set, 16);
+ ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
+ memcpy(&ht_cap->mcs, &sband->ht_cap.mcs, 16);
ht_cap->ampdu_params_info =
- (sband->ht_info.ampdu_factor & IEEE80211_HT_CAP_AMPDU_FACTOR) |
- ((sband->ht_info.ampdu_density << 2) &
- IEEE80211_HT_CAP_AMPDU_DENSITY);
+ (sband->ht_cap.ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) |
+ ((sband->ht_cap.ampdu_density << 2) &
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
*left -= sizeof(struct ieee80211_ht_cap);
}
@@ -670,23 +625,6 @@ static u16 iwl_fill_probe_req(struct iwl_priv *priv,
return (u16)len;
}
-static u32 iwl_scan_tx_ant(struct iwl_priv *priv, enum ieee80211_band band)
-{
- int i, ind;
-
- ind = priv->scan_tx_ant[band];
- for (i = 0; i < priv->hw_params.tx_chains_num; i++) {
- ind = (ind+1) >= priv->hw_params.tx_chains_num ? 0 : ind+1;
- if (priv->hw_params.valid_tx_ant & (1 << ind)) {
- priv->scan_tx_ant[band] = ind;
- break;
- }
- }
- IWL_DEBUG_SCAN("select TX ANT = %c\n", 'A' + ind);
- return scan_tx_ant[ind];
-}
-
-
static void iwl_bg_request_scan(struct work_struct *data)
{
struct iwl_priv *priv =
@@ -699,11 +637,13 @@ static void iwl_bg_request_scan(struct work_struct *data)
struct iwl_scan_cmd *scan;
struct ieee80211_conf *conf = NULL;
int ret = 0;
- u32 tx_ant;
+ u32 rate_flags = 0;
u16 cmd_len;
enum ieee80211_band band;
u8 n_probes = 2;
u8 rx_chain = priv->hw_params.valid_rx_ant;
+ u8 rate;
+ DECLARE_SSID_BUF(ssid);
conf = ieee80211_get_hw_conf(priv->hw);
@@ -714,7 +654,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
goto done;
}
- /* Make sure the scan wasn't cancelled before this queued work
+ /* Make sure the scan wasn't canceled before this queued work
* was given the chance to run... */
if (!test_bit(STATUS_SCANNING, &priv->status))
goto done;
@@ -796,20 +736,13 @@ static void iwl_bg_request_scan(struct work_struct *data)
/* We should add the ability for user to lock to PASSIVE ONLY */
if (priv->one_direct_scan) {
IWL_DEBUG_SCAN("Start direct scan for '%s'\n",
- iwl_escape_essid(priv->direct_ssid,
- priv->direct_ssid_len));
+ print_ssid(ssid, priv->direct_ssid,
+ priv->direct_ssid_len));
scan->direct_scan[0].id = WLAN_EID_SSID;
scan->direct_scan[0].len = priv->direct_ssid_len;
memcpy(scan->direct_scan[0].ssid,
priv->direct_ssid, priv->direct_ssid_len);
n_probes++;
- } else if (!iwl_is_associated(priv) && priv->essid_len) {
- IWL_DEBUG_SCAN("Start direct scan for '%s' (not associated)\n",
- iwl_escape_essid(priv->essid, priv->essid_len));
- scan->direct_scan[0].id = WLAN_EID_SSID;
- scan->direct_scan[0].len = priv->essid_len;
- memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len);
- n_probes++;
} else {
IWL_DEBUG_SCAN("Start indirect scan.\n");
}
@@ -822,23 +755,16 @@ static void iwl_bg_request_scan(struct work_struct *data)
if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
band = IEEE80211_BAND_2GHZ;
scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
- tx_ant = iwl_scan_tx_ant(priv, band);
- if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK)
- scan->tx_cmd.rate_n_flags =
- iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
- tx_ant);
- else
- scan->tx_cmd.rate_n_flags =
- iwl_hw_set_rate_n_flags(IWL_RATE_1M_PLCP,
- tx_ant |
- RATE_MCS_CCK_MSK);
+ if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) {
+ rate = IWL_RATE_6M_PLCP;
+ } else {
+ rate = IWL_RATE_1M_PLCP;
+ rate_flags = RATE_MCS_CCK_MSK;
+ }
scan->good_CRC_th = 0;
} else if (priv->scan_bands & BIT(IEEE80211_BAND_5GHZ)) {
band = IEEE80211_BAND_5GHZ;
- tx_ant = iwl_scan_tx_ant(priv, band);
- scan->tx_cmd.rate_n_flags =
- iwl_hw_set_rate_n_flags(IWL_RATE_6M_PLCP,
- tx_ant);
+ rate = IWL_RATE_6M_PLCP;
scan->good_CRC_th = IWL_GOOD_CRC_TH;
/* Force use of chains B and C (0x6) for scan Rx for 4965
@@ -851,6 +777,11 @@ static void iwl_bg_request_scan(struct work_struct *data)
goto done;
}
+ priv->scan_tx_ant[band] =
+ iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band]);
+ rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
+ scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
+
/* MIMO is not used here, but value is required */
scan->rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
cpu_to_le16((0x7 << RXON_RX_CHAIN_VALID_POS) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.c b/drivers/net/wireless/iwlwifi/iwl-spectrum.c
new file mode 100644
index 000000000000..836c3c80b69e
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-spectrum.c
@@ -0,0 +1,198 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved.
+ *
+ * Portions of this file are derived from the ipw3945 project, as well
+ * as portions of the ieee80211 subsystem header files.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-eeprom.h"
+#include "iwl-dev.h"
+#include "iwl-core.h"
+#include "iwl-io.h"
+#include "iwl-spectrum.h"
+
+#define BEACON_TIME_MASK_LOW 0x00FFFFFF
+#define BEACON_TIME_MASK_HIGH 0xFF000000
+#define TIME_UNIT 1024
+
+/*
+ * extended beacon time format
+ * time in usec will be changed into a 32-bit value in 8:24 format
+ * the high 1 byte is the beacon counts
+ * the lower 3 bytes is the time in usec within one beacon interval
+ */
+
+/* TOOD: was used in sysfs debug interface need to add to mac */
+#if 0
+static u32 iwl_usecs_to_beacons(u32 usec, u32 beacon_interval)
+{
+ u32 quot;
+ u32 rem;
+ u32 interval = beacon_interval * 1024;
+
+ if (!interval || !usec)
+ return 0;
+
+ quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24);
+ rem = (usec % interval) & BEACON_TIME_MASK_LOW;
+
+ return (quot << 24) + rem;
+}
+
+/* base is usually what we get from ucode with each received frame,
+ * the same as HW timer counter counting down
+ */
+
+static __le32 iwl_add_beacon_time(u32 base, u32 addon, u32 beacon_interval)
+{
+ u32 base_low = base & BEACON_TIME_MASK_LOW;
+ u32 addon_low = addon & BEACON_TIME_MASK_LOW;
+ u32 interval = beacon_interval * TIME_UNIT;
+ u32 res = (base & BEACON_TIME_MASK_HIGH) +
+ (addon & BEACON_TIME_MASK_HIGH);
+
+ if (base_low > addon_low)
+ res += base_low - addon_low;
+ else if (base_low < addon_low) {
+ res += interval + base_low - addon_low;
+ res += (1 << 24);
+ } else
+ res += (1 << 24);
+
+ return cpu_to_le32(res);
+}
+static int iwl_get_measurement(struct iwl_priv *priv,
+ struct ieee80211_measurement_params *params,
+ u8 type)
+{
+ struct iwl4965_spectrum_cmd spectrum;
+ struct iwl_rx_packet *res;
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_SPECTRUM_MEASUREMENT_CMD,
+ .data = (void *)&spectrum,
+ .meta.flags = CMD_WANT_SKB,
+ };
+ u32 add_time = le64_to_cpu(params->start_time);
+ int rc;
+ int spectrum_resp_status;
+ int duration = le16_to_cpu(params->duration);
+
+ if (iwl_is_associated(priv))
+ add_time =
+ iwl_usecs_to_beacons(
+ le64_to_cpu(params->start_time) - priv->last_tsf,
+ le16_to_cpu(priv->rxon_timing.beacon_interval));
+
+ memset(&spectrum, 0, sizeof(spectrum));
+
+ spectrum.channel_count = cpu_to_le16(1);
+ spectrum.flags =
+ RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK;
+ spectrum.filter_flags = MEASUREMENT_FILTER_FLAG;
+ cmd.len = sizeof(spectrum);
+ spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len));
+
+ if (iwl_is_associated(priv))
+ spectrum.start_time =
+ iwl_add_beacon_time(priv->last_beacon_time,
+ add_time,
+ le16_to_cpu(priv->rxon_timing.beacon_interval));
+ else
+ spectrum.start_time = 0;
+
+ spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT);
+ spectrum.channels[0].channel = params->channel;
+ spectrum.channels[0].type = type;
+ if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)
+ spectrum.flags |= RXON_FLG_BAND_24G_MSK |
+ RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK;
+
+ rc = iwl_send_cmd_sync(priv, &cmd);
+ if (rc)
+ return rc;
+
+ res = (struct iwl_rx_packet *)cmd.meta.u.skb->data;
+ if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n");
+ rc = -EIO;
+ }
+
+ spectrum_resp_status = le16_to_cpu(res->u.spectrum.status);
+ switch (spectrum_resp_status) {
+ case 0: /* Command will be handled */
+ if (res->u.spectrum.id != 0xff) {
+ IWL_DEBUG_INFO
+ ("Replaced existing measurement: %d\n",
+ res->u.spectrum.id);
+ priv->measurement_status &= ~MEASUREMENT_READY;
+ }
+ priv->measurement_status |= MEASUREMENT_ACTIVE;
+ rc = 0;
+ break;
+
+ case 1: /* Command will not be handled */
+ rc = -EAGAIN;
+ break;
+ }
+
+ dev_kfree_skb_any(cmd.meta.u.skb);
+
+ return rc;
+}
+#endif
+
+static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
+
+ if (!report->state) {
+ IWL_DEBUG(IWL_DL_11H,
+ "Spectrum Measure Notification: Start\n");
+ return;
+ }
+
+ memcpy(&priv->measure_report, report, sizeof(*report));
+ priv->measurement_status |= MEASUREMENT_READY;
+}
+
+void iwl_setup_spectrum_handlers(struct iwl_priv *priv)
+{
+ priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] =
+ iwl_rx_spectrum_measure_notif;
+}
+EXPORT_SYMBOL(iwl_setup_spectrum_handlers);
diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.h b/drivers/net/wireless/iwlwifi/iwl-spectrum.h
index a40a2174df98..b7d7943e476b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-spectrum.h
+++ b/drivers/net/wireless/iwlwifi/iwl-spectrum.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -88,4 +88,5 @@ struct ieee80211_measurement_report {
struct ieee80211_basic_report basic[0];
} u;
} __attribute__ ((packed));
+
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 26f7084d3011..412f66bac1af 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -33,8 +33,6 @@
#include "iwl-dev.h"
#include "iwl-core.h"
#include "iwl-sta.h"
-#include "iwl-helpers.h"
-
#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
@@ -45,7 +43,6 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
int start = 0;
int ret = IWL_INVALID_STATION;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) ||
(priv->iw_mode == NL80211_IFTYPE_AP))
@@ -63,8 +60,8 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
goto out;
}
- IWL_DEBUG_ASSOC_LIMIT("can not find STA %s total %d\n",
- print_mac(mac, addr), priv->num_stations);
+ IWL_DEBUG_ASSOC_LIMIT("can not find STA %pM total %d\n",
+ addr, priv->num_stations);
out:
spin_unlock_irqrestore(&priv->sta_lock, flags);
@@ -86,7 +83,6 @@ EXPORT_SYMBOL(iwl_get_ra_sta_id);
static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
{
unsigned long flags;
- DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags);
@@ -94,8 +90,8 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
IWL_ERROR("ACTIVATE a non DRIVER active station %d\n", sta_id);
priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
- IWL_DEBUG_ASSOC("Added STA to Ucode: %s\n",
- print_mac(mac, priv->stations[sta_id].sta.sta.addr));
+ IWL_DEBUG_ASSOC("Added STA to Ucode: %pM\n",
+ priv->stations[sta_id].sta.sta.addr);
spin_unlock_irqrestore(&priv->sta_lock, flags);
}
@@ -104,7 +100,9 @@ static int iwl_add_sta_callback(struct iwl_priv *priv,
struct iwl_cmd *cmd, struct sk_buff *skb)
{
struct iwl_rx_packet *res = NULL;
- u8 sta_id = cmd->cmd.addsta.sta.sta_id;
+ struct iwl_addsta_cmd *addsta =
+ (struct iwl_addsta_cmd *)cmd->cmd.payload;
+ u8 sta_id = addsta->sta.sta_id;
if (!skb) {
IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n");
@@ -132,7 +130,7 @@ static int iwl_add_sta_callback(struct iwl_priv *priv,
return 1;
}
-int iwl_send_add_sta(struct iwl_priv *priv,
+static int iwl_send_add_sta(struct iwl_priv *priv,
struct iwl_addsta_cmd *sta, u8 flags)
{
struct iwl_rx_packet *res = NULL;
@@ -180,10 +178,9 @@ int iwl_send_add_sta(struct iwl_priv *priv,
return ret;
}
-EXPORT_SYMBOL(iwl_send_add_sta);
static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
- struct ieee80211_ht_info *sta_ht_inf)
+ struct ieee80211_sta_ht_cap *sta_ht_inf)
{
__le32 sta_flags;
u8 mimo_ps_mode;
@@ -231,13 +228,12 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
* iwl_add_station_flags - Add station to tables in driver and device
*/
u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap,
- u8 flags, struct ieee80211_ht_info *ht_info)
+ u8 flags, struct ieee80211_sta_ht_cap *ht_info)
{
int i;
int sta_id = IWL_INVALID_STATION;
struct iwl_station_entry *station;
unsigned long flags_spin;
- DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags_spin);
if (is_ap)
@@ -273,8 +269,8 @@ u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap,
station = &priv->stations[sta_id];
station->used = IWL_STA_DRIVER_ACTIVE;
- IWL_DEBUG_ASSOC("Add STA to driver ID %d: %s\n",
- sta_id, print_mac(mac, addr));
+ IWL_DEBUG_ASSOC("Add STA to driver ID %d: %pM\n",
+ sta_id, addr);
priv->num_stations++;
/* Set up the REPLY_ADD_STA command to send to device */
@@ -301,14 +297,11 @@ EXPORT_SYMBOL(iwl_add_station_flags);
static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr)
{
unsigned long flags;
- DECLARE_MAC_BUF(mac);
-
u8 sta_id = iwl_find_station(priv, addr);
BUG_ON(sta_id == IWL_INVALID_STATION);
- IWL_DEBUG_ASSOC("Removed STA from Ucode: %s\n",
- print_mac(mac, addr));
+ IWL_DEBUG_ASSOC("Removed STA from Ucode: %pM\n", addr);
spin_lock_irqsave(&priv->sta_lock, flags);
@@ -326,7 +319,9 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv,
struct iwl_cmd *cmd, struct sk_buff *skb)
{
struct iwl_rx_packet *res = NULL;
- const char *addr = cmd->cmd.rm_sta.addr;
+ struct iwl_rem_sta_cmd *rm_sta =
+ (struct iwl_rem_sta_cmd *)cmd->cmd.payload;
+ const char *addr = rm_sta->addr;
if (!skb) {
IWL_ERROR("Error: Response NULL in REPLY_REMOVE_STA.\n");
@@ -415,7 +410,6 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap)
int sta_id = IWL_INVALID_STATION;
int i, ret = -EINVAL;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags);
@@ -435,18 +429,18 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap)
if (unlikely(sta_id == IWL_INVALID_STATION))
goto out;
- IWL_DEBUG_ASSOC("Removing STA from driver:%d %s\n",
- sta_id, print_mac(mac, addr));
+ IWL_DEBUG_ASSOC("Removing STA from driver:%d %pM\n",
+ sta_id, addr);
if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
- IWL_ERROR("Removing %s but non DRIVER active\n",
- print_mac(mac, addr));
+ IWL_ERROR("Removing %pM but non DRIVER active\n",
+ addr);
goto out;
}
if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
- IWL_ERROR("Removing %s but non UCODE active\n",
- print_mac(mac, addr));
+ IWL_ERROR("Removing %pM but non UCODE active\n",
+ addr);
goto out;
}
@@ -467,6 +461,29 @@ out:
}
EXPORT_SYMBOL(iwl_remove_station);
+/**
+ * iwl_clear_stations_table - Clear the driver's station table
+ *
+ * NOTE: This does not clear or otherwise alter the device's station table.
+ */
+void iwl_clear_stations_table(struct iwl_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_lock, flags);
+
+ if (iwl_is_alive(priv) &&
+ !test_bit(STATUS_EXIT_PENDING, &priv->status) &&
+ iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
+ IWL_ERROR("Couldn't clear the station table\n");
+
+ priv->num_stations = 0;
+ memset(priv->stations, 0, sizeof(priv->stations));
+
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+}
+EXPORT_SYMBOL(iwl_clear_stations_table);
+
static int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
{
int i;
@@ -717,6 +734,55 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
return ret;
}
+void iwl_update_tkip_key(struct iwl_priv *priv,
+ struct ieee80211_key_conf *keyconf,
+ const u8 *addr, u32 iv32, u16 *phase1key)
+{
+ u8 sta_id = IWL_INVALID_STATION;
+ unsigned long flags;
+ __le16 key_flags = 0;
+ int i;
+ DECLARE_MAC_BUF(mac);
+
+ sta_id = iwl_find_station(priv, addr);
+ if (sta_id == IWL_INVALID_STATION) {
+ IWL_DEBUG_MAC80211("leave - %pM not in station map.\n",
+ addr);
+ return;
+ }
+
+ if (iwl_scan_cancel(priv)) {
+ /* cancel scan failed, just live w/ bad key and rely
+ briefly on SW decryption */
+ return;
+ }
+
+ key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK);
+ key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
+ key_flags &= ~STA_KEY_FLG_INVALID;
+
+ if (sta_id == priv->hw_params.bcast_sta_id)
+ key_flags |= STA_KEY_MULTICAST_MSK;
+
+ spin_lock_irqsave(&priv->sta_lock, flags);
+
+ priv->stations[sta_id].sta.key.key_flags = key_flags;
+ priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32;
+
+ for (i = 0; i < 5; i++)
+ priv->stations[sta_id].sta.key.tkip_rx_ttak[i] =
+ cpu_to_le16(phase1key[i]);
+
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+
+ iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+}
+EXPORT_SYMBOL(iwl_update_tkip_key);
+
int iwl_remove_dynamic_key(struct iwl_priv *priv,
struct ieee80211_key_conf *keyconf,
u8 sta_id)
@@ -809,7 +875,7 @@ static void iwl_dump_lq_cmd(struct iwl_priv *priv,
{
int i;
IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id);
- IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n",
+ IWL_DEBUG_RATE("lq ant 0x%X 0x%X\n",
lq->general_params.single_stream_ant_msk,
lq->general_params.dual_stream_ant_msk);
@@ -870,7 +936,7 @@ static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, int is_ap)
struct iwl_link_quality_cmd link_cmd = {
.reserved1 = 0,
};
- u16 rate_flags;
+ u32 rate_flags;
/* Set up the rate scaling to start at selected rate, fall back
* all the way down to 1M in IEEE order, and then spin on 1M */
@@ -886,15 +952,16 @@ static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, int is_ap)
if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
rate_flags |= RATE_MCS_CCK_MSK;
- /* Use Tx antenna B only */
- rate_flags |= RATE_MCS_ANT_B_MSK; /*FIXME:RS*/
+ rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+ RATE_MCS_ANT_POS;
link_cmd.rs_table[i].rate_n_flags =
iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
- r = iwl4965_get_prev_ieee_rate(r);
+ r = iwl_get_prev_ieee_rate(r);
}
- link_cmd.general_params.single_stream_ant_msk = 2;
+ link_cmd.general_params.single_stream_ant_msk =
+ first_antenna(priv->hw_params.valid_tx_ant);
link_cmd.general_params.dual_stream_ant_msk = 3;
link_cmd.agg_params.agg_dis_start_th = 3;
link_cmd.agg_params.agg_time_limit = cpu_to_le16(4000);
@@ -910,24 +977,35 @@ static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, int is_ap)
* iwl_rxon_add_station - add station into station table.
*
* there is only one AP station with id= IWL_AP_ID
- * NOTE: mutex must be held before calling this fnction
+ * NOTE: mutex must be held before calling this function
*/
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap)
{
+ struct ieee80211_sta *sta;
+ struct ieee80211_sta_ht_cap ht_config;
+ struct ieee80211_sta_ht_cap *cur_ht_config = NULL;
u8 sta_id;
/* Add station to device's station table */
- struct ieee80211_conf *conf = &priv->hw->conf;
- struct ieee80211_ht_info *cur_ht_config = &conf->ht_conf;
-
- if ((is_ap) &&
- (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) &&
- (priv->iw_mode == NL80211_IFTYPE_STATION))
- sta_id = iwl_add_station_flags(priv, addr, is_ap,
- 0, cur_ht_config);
- else
- sta_id = iwl_add_station_flags(priv, addr, is_ap,
- 0, NULL);
+
+ /*
+ * XXX: This check is definitely not correct, if we're an AP
+ * it'll always be false which is not what we want, but
+ * it doesn't look like iwlagn is prepared to be an HT
+ * AP anyway.
+ */
+ if (priv->current_ht_config.is_ht) {
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->hw, addr);
+ if (sta) {
+ memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config));
+ cur_ht_config = &ht_config;
+ }
+ rcu_read_unlock();
+ }
+
+ sta_id = iwl_add_station_flags(priv, addr, is_ap,
+ 0, cur_ht_config);
/* Set up default rate scaling table in device's station table */
iwl_sta_init_lq(priv, addr, is_ap);
@@ -945,7 +1023,6 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
{
int sta_id;
u16 fc = le16_to_cpu(hdr->frame_control);
- DECLARE_MAC_BUF(mac);
/* If this frame is broadcast or management, use broadcast station id */
if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) ||
@@ -980,9 +1057,9 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
if (sta_id != IWL_INVALID_STATION)
return sta_id;
- IWL_DEBUG_DROP("Station %s not in station map. "
+ IWL_DEBUG_DROP("Station %pM not in station map. "
"Defaulting to broadcast...\n",
- print_mac(mac, hdr->addr1));
+ hdr->addr1);
iwl_print_hex_dump(priv, IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr));
return priv->hw_params.bcast_sta_id;
@@ -999,9 +1076,9 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
EXPORT_SYMBOL(iwl_get_sta_id);
/**
- * iwl_sta_modify_enable_tid_tx - Enable Tx for this TID in station table
+ * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table
*/
-void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, int tid)
+void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid)
{
unsigned long flags;
@@ -1014,5 +1091,81 @@ void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, int tid)
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}
-EXPORT_SYMBOL(iwl_sta_modify_enable_tid_tx);
+EXPORT_SYMBOL(iwl_sta_tx_modify_enable_tid);
+
+int iwl_sta_rx_agg_start(struct iwl_priv *priv,
+ const u8 *addr, int tid, u16 ssn)
+{
+ unsigned long flags;
+ int sta_id;
+
+ sta_id = iwl_find_station(priv, addr);
+ if (sta_id == IWL_INVALID_STATION)
+ return -ENXIO;
+
+ spin_lock_irqsave(&priv->sta_lock, flags);
+ priv->stations[sta_id].sta.station_flags_msk = 0;
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+ priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid;
+ priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn);
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+ return iwl_send_add_sta(priv, &priv->stations[sta_id].sta,
+ CMD_ASYNC);
+}
+EXPORT_SYMBOL(iwl_sta_rx_agg_start);
+
+int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
+{
+ unsigned long flags;
+ int sta_id;
+
+ sta_id = iwl_find_station(priv, addr);
+ if (sta_id == IWL_INVALID_STATION)
+ return -ENXIO;
+
+ spin_lock_irqsave(&priv->sta_lock, flags);
+ priv->stations[sta_id].sta.station_flags_msk = 0;
+ priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+ priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid;
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+ return iwl_send_add_sta(priv, &priv->stations[sta_id].sta,
+ CMD_ASYNC);
+}
+EXPORT_SYMBOL(iwl_sta_rx_agg_stop);
+
+static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->sta_lock, flags);
+ priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
+ priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
+ priv->stations[sta_id].sta.sta.modify_mask = 0;
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
+
+ iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+}
+
+void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr)
+{
+ /* FIXME: need locking over ps_status ??? */
+ u8 sta_id = iwl_find_station(priv, addr);
+
+ if (sta_id != IWL_INVALID_STATION) {
+ u8 sta_awake = priv->stations[sta_id].
+ ps_status == STA_PS_STATUS_WAKE;
+
+ if (sta_awake && ps_bit)
+ priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
+ else if (!sta_awake && !ps_bit) {
+ iwl_sta_modify_ps_wake(priv, sta_id);
+ priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
+ }
+ }
+}
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h
index 221b93e670a6..9bb7cefc1f3c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.h
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -47,9 +47,21 @@ int iwl_set_dynamic_key(struct iwl_priv *priv,
struct ieee80211_key_conf *key, u8 sta_id);
int iwl_remove_dynamic_key(struct iwl_priv *priv,
struct ieee80211_key_conf *key, u8 sta_id);
+void iwl_update_tkip_key(struct iwl_priv *priv,
+ struct ieee80211_key_conf *keyconf,
+ const u8 *addr, u32 iv32, u16 *phase1key);
+
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap);
int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap);
+void iwl_clear_stations_table(struct iwl_priv *priv);
int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
-void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, int tid);
int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
+u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr,
+ int is_ap, u8 flags,
+ struct ieee80211_sta_ht_cap *ht_info);
+void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
+int iwl_sta_rx_agg_start(struct iwl_priv *priv,
+ const u8 *addr, int tid, u16 ssn);
+int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
+void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr);
#endif /* __iwl_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 907a53ebc6e4..b0ee86c62685 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -56,96 +56,132 @@ static const u16 default_tid_to_tx_fifo[] = {
IWL_TX_FIFO_AC3
};
+static inline int iwl_alloc_dma_ptr(struct iwl_priv *priv,
+ struct iwl_dma_ptr *ptr, size_t size)
+{
+ ptr->addr = pci_alloc_consistent(priv->pci_dev, size, &ptr->dma);
+ if (!ptr->addr)
+ return -ENOMEM;
+ ptr->size = size;
+ return 0;
+}
+
+static inline void iwl_free_dma_ptr(struct iwl_priv *priv,
+ struct iwl_dma_ptr *ptr)
+{
+ if (unlikely(!ptr->addr))
+ return;
+
+ pci_free_consistent(priv->pci_dev, ptr->size, ptr->addr, ptr->dma);
+ memset(ptr, 0, sizeof(*ptr));
+}
+
+static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ dma_addr_t addr = get_unaligned_le32(&tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ addr |=
+ ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16;
+
+ return addr;
+}
+
+static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+
+ return le16_to_cpu(tb->hi_n_len) >> 4;
+}
+
+static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx,
+ dma_addr_t addr, u16 len)
+{
+ struct iwl_tfd_tb *tb = &tfd->tbs[idx];
+ u16 hi_n_len = len << 4;
+
+ put_unaligned_le32(addr, &tb->lo);
+ if (sizeof(dma_addr_t) > sizeof(u32))
+ hi_n_len |= ((addr >> 16) >> 16) & 0xF;
+
+ tb->hi_n_len = cpu_to_le16(hi_n_len);
+
+ tfd->num_tbs = idx + 1;
+}
+
+static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd)
+{
+ return tfd->num_tbs & 0x1f;
+}
/**
* iwl_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr]
+ * @priv - driver private data
+ * @txq - tx queue
*
* Does NOT advance any TFD circular buffer read/write indexes
* Does NOT free the TFD itself (which is within circular buffer)
*/
-static int iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
+static void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq)
{
- struct iwl_tfd_frame *bd_tmp = (struct iwl_tfd_frame *)&txq->bd[0];
- struct iwl_tfd_frame *bd = &bd_tmp[txq->q.read_ptr];
+ struct iwl_tfd *tfd_tmp = (struct iwl_tfd *)&txq->tfds[0];
+ struct iwl_tfd *tfd;
struct pci_dev *dev = priv->pci_dev;
+ int index = txq->q.read_ptr;
int i;
- int counter = 0;
- int index, is_odd;
+ int num_tbs;
- /* Host command buffers stay mapped in memory, nothing to clean */
- if (txq->q.id == IWL_CMD_QUEUE_NUM)
- return 0;
+ tfd = &tfd_tmp[index];
/* Sanity check on number of chunks */
- counter = IWL_GET_BITS(*bd, num_tbs);
- if (counter > MAX_NUM_OF_TBS) {
- IWL_ERROR("Too many chunks: %i\n", counter);
+ num_tbs = iwl_tfd_get_num_tbs(tfd);
+
+ if (num_tbs >= IWL_NUM_OF_TBS) {
+ IWL_ERROR("Too many chunks: %i\n", num_tbs);
/* @todo issue fatal error, it is quite serious situation */
- return 0;
+ return;
}
- /* Unmap chunks, if any.
- * TFD info for odd chunks is different format than for even chunks. */
- for (i = 0; i < counter; i++) {
- index = i / 2;
- is_odd = i & 0x1;
-
- if (is_odd)
- pci_unmap_single(
- dev,
- IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) |
- (IWL_GET_BITS(bd->pa[index],
- tb2_addr_hi20) << 16),
- IWL_GET_BITS(bd->pa[index], tb2_len),
+ /* Unmap tx_cmd */
+ if (num_tbs)
+ pci_unmap_single(dev,
+ pci_unmap_addr(&txq->cmd[index]->meta, mapping),
+ pci_unmap_len(&txq->cmd[index]->meta, len),
PCI_DMA_TODEVICE);
- else if (i > 0)
- pci_unmap_single(dev,
- le32_to_cpu(bd->pa[index].tb1_addr),
- IWL_GET_BITS(bd->pa[index], tb1_len),
- PCI_DMA_TODEVICE);
+ /* Unmap chunks, if any. */
+ for (i = 1; i < num_tbs; i++) {
+ pci_unmap_single(dev, iwl_tfd_tb_get_addr(tfd, i),
+ iwl_tfd_tb_get_len(tfd, i), PCI_DMA_TODEVICE);
- /* Free SKB, if any, for this chunk */
- if (txq->txb[txq->q.read_ptr].skb[i]) {
- struct sk_buff *skb = txq->txb[txq->q.read_ptr].skb[i];
-
- dev_kfree_skb(skb);
- txq->txb[txq->q.read_ptr].skb[i] = NULL;
+ if (txq->txb) {
+ dev_kfree_skb(txq->txb[txq->q.read_ptr].skb[i - 1]);
+ txq->txb[txq->q.read_ptr].skb[i - 1] = NULL;
}
}
- return 0;
}
-static int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, void *ptr,
- dma_addr_t addr, u16 len)
+static int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv,
+ struct iwl_tfd *tfd,
+ dma_addr_t addr, u16 len)
{
- int index, is_odd;
- struct iwl_tfd_frame *tfd = ptr;
- u32 num_tbs = IWL_GET_BITS(*tfd, num_tbs);
+
+ u32 num_tbs = iwl_tfd_get_num_tbs(tfd);
/* Each TFD can point to a maximum 20 Tx buffers */
- if (num_tbs >= MAX_NUM_OF_TBS) {
+ if (num_tbs >= IWL_NUM_OF_TBS) {
IWL_ERROR("Error can not send more than %d chunks\n",
- MAX_NUM_OF_TBS);
+ IWL_NUM_OF_TBS);
return -EINVAL;
}
- index = num_tbs / 2;
- is_odd = num_tbs & 0x1;
-
- if (!is_odd) {
- tfd->pa[index].tb1_addr = cpu_to_le32(addr);
- IWL_SET_BITS(tfd->pa[index], tb1_addr_hi,
- iwl_get_dma_hi_address(addr));
- IWL_SET_BITS(tfd->pa[index], tb1_len, len);
- } else {
- IWL_SET_BITS(tfd->pa[index], tb2_addr_lo16,
- (u32) (addr & 0xffff));
- IWL_SET_BITS(tfd->pa[index], tb2_addr_hi20, addr >> 16);
- IWL_SET_BITS(tfd->pa[index], tb2_len, len);
- }
+ BUG_ON(addr & ~DMA_BIT_MASK(36));
+ if (unlikely(addr & ~IWL_TX_DMA_MASK))
+ IWL_ERROR("Unaligned address = %llx\n",
+ (unsigned long long)addr);
- IWL_SET_BITS(*tfd, num_tbs, num_tbs + 1);
+ iwl_tfd_set_tb(tfd, num_tbs, addr, len);
return 0;
}
@@ -210,7 +246,7 @@ static void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct iwl_queue *q = &txq->q;
struct pci_dev *dev = priv->pci_dev;
- int i, slots_num, len;
+ int i, len;
if (q->n_bd == 0)
return;
@@ -221,21 +257,15 @@ static void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
iwl_hw_txq_free_tfd(priv, txq);
len = sizeof(struct iwl_cmd) * q->n_window;
- if (q->id == IWL_CMD_QUEUE_NUM)
- len += IWL_MAX_SCAN_SIZE;
/* De-alloc array of command/tx buffers */
- slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
- TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS;
- for (i = 0; i < slots_num; i++)
+ for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
kfree(txq->cmd[i]);
- if (txq_id == IWL_CMD_QUEUE_NUM)
- kfree(txq->cmd[slots_num]);
/* De-alloc circular buffer of TFDs */
if (txq->q.n_bd)
- pci_free_consistent(dev, sizeof(struct iwl_tfd_frame) *
- txq->q.n_bd, txq->bd, txq->q.dma_addr);
+ pci_free_consistent(dev, sizeof(struct iwl_tfd) *
+ txq->q.n_bd, txq->tfds, txq->q.dma_addr);
/* De-alloc array of per-TFD driver data */
kfree(txq->txb);
@@ -245,6 +275,40 @@ static void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
memset(txq, 0, sizeof(*txq));
}
+
+/**
+ * iwl_cmd_queue_free - Deallocate DMA queue.
+ * @txq: Transmit queue to deallocate.
+ *
+ * Empty queue by removing and destroying all BD's.
+ * Free all buffers.
+ * 0-fill, but do not free "txq" descriptor structure.
+ */
+static void iwl_cmd_queue_free(struct iwl_priv *priv)
+{
+ struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
+ struct iwl_queue *q = &txq->q;
+ struct pci_dev *dev = priv->pci_dev;
+ int i, len;
+
+ if (q->n_bd == 0)
+ return;
+
+ len = sizeof(struct iwl_cmd) * q->n_window;
+ len += IWL_MAX_SCAN_SIZE;
+
+ /* De-alloc array of command/tx buffers */
+ for (i = 0; i <= TFD_CMD_SLOTS; i++)
+ kfree(txq->cmd[i]);
+
+ /* De-alloc circular buffer of TFDs */
+ if (txq->q.n_bd)
+ pci_free_consistent(dev, sizeof(struct iwl_tfd) *
+ txq->q.n_bd, txq->tfds, txq->q.dma_addr);
+
+ /* 0-fill queue descriptor structure */
+ memset(txq, 0, sizeof(*txq));
+}
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
*
@@ -340,13 +404,13 @@ static int iwl_tx_queue_alloc(struct iwl_priv *priv,
/* Circular buffer of transmit frame descriptors (TFDs),
* shared with device */
- txq->bd = pci_alloc_consistent(dev,
- sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX,
+ txq->tfds = pci_alloc_consistent(dev,
+ sizeof(txq->tfds[0]) * TFD_QUEUE_SIZE_MAX,
&txq->q.dma_addr);
- if (!txq->bd) {
+ if (!txq->tfds) {
IWL_ERROR("pci_alloc_consistent(%zd) failed\n",
- sizeof(txq->bd[0]) * TFD_QUEUE_SIZE_MAX);
+ sizeof(txq->tfds[0]) * TFD_QUEUE_SIZE_MAX);
goto error;
}
txq->q.id = id;
@@ -370,26 +434,21 @@ static int iwl_tx_queue_alloc(struct iwl_priv *priv,
static int iwl_hw_tx_queue_init(struct iwl_priv *priv,
struct iwl_tx_queue *txq)
{
- int rc;
+ int ret;
unsigned long flags;
int txq_id = txq->q.id;
spin_lock_irqsave(&priv->lock, flags);
- rc = iwl_grab_nic_access(priv);
- if (rc) {
+ ret = iwl_grab_nic_access(priv);
+ if (ret) {
spin_unlock_irqrestore(&priv->lock, flags);
- return rc;
+ return ret;
}
/* Circular buffer (TFD queue in DRAM) physical base address */
iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
txq->q.dma_addr >> 8);
- /* Enable DMA channel, using same id as for TFD queue */
- iwl_write_direct32(
- priv, FH_TCSR_CHNL_TX_CONFIG_REG(txq_id),
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE |
- FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL);
iwl_release_nic_access(priv);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -468,16 +527,20 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
/* Tx queues */
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- iwl_tx_queue_free(priv, txq_id);
+ if (txq_id == IWL_CMD_QUEUE_NUM)
+ iwl_cmd_queue_free(priv);
+ else
+ iwl_tx_queue_free(priv, txq_id);
- /* Keep-warm buffer */
- iwl_kw_free(priv);
+ iwl_free_dma_ptr(priv, &priv->kw);
+
+ iwl_free_dma_ptr(priv, &priv->scd_bc_tbls);
}
EXPORT_SYMBOL(iwl_hw_txq_ctx_free);
/**
* iwl_txq_ctx_reset - Reset TX queue context
- * Destroys all DMA structures and initialise them again
+ * Destroys all DMA structures and initialize them again
*
* @param priv
* @return error code
@@ -488,13 +551,17 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
int txq_id, slots_num;
unsigned long flags;
- iwl_kw_free(priv);
-
/* Free all tx/cmd queues and keep-warm buffer */
iwl_hw_txq_ctx_free(priv);
+ ret = iwl_alloc_dma_ptr(priv, &priv->scd_bc_tbls,
+ priv->hw_params.scd_bc_tbls_size);
+ if (ret) {
+ IWL_ERROR("Scheduler BC Table allocation failed\n");
+ goto error_bc_tbls;
+ }
/* Alloc keep-warm buffer */
- ret = iwl_kw_alloc(priv);
+ ret = iwl_alloc_dma_ptr(priv, &priv->kw, IWL_KW_SIZE);
if (ret) {
IWL_ERROR("Keep Warm allocation failed\n");
goto error_kw;
@@ -509,17 +576,12 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
/* Turn off all Tx DMA fifos */
priv->cfg->ops->lib->txq_set_sched(priv, 0);
+ /* Tell NIC where to find the "keep warm" buffer */
+ iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
+
iwl_release_nic_access(priv);
spin_unlock_irqrestore(&priv->lock, flags);
-
- /* Tell nic where to find the keep-warm buffer */
- ret = iwl_kw_init(priv);
- if (ret) {
- IWL_ERROR("kw_init failed\n");
- goto error_reset;
- }
-
/* Alloc and init all Tx queues, including the command queue (#4) */
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ?
@@ -537,8 +599,10 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
error:
iwl_hw_txq_ctx_free(priv);
error_reset:
- iwl_kw_free(priv);
+ iwl_free_dma_ptr(priv, &priv->kw);
error_kw:
+ iwl_free_dma_ptr(priv, &priv->scd_bc_tbls);
+ error_bc_tbls:
return ret;
}
@@ -547,11 +611,9 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
*/
void iwl_txq_ctx_stop(struct iwl_priv *priv)
{
-
- int txq_id;
+ int ch;
unsigned long flags;
-
/* Turn off all Tx DMA fifos */
spin_lock_irqsave(&priv->lock, flags);
if (iwl_grab_nic_access(priv)) {
@@ -562,12 +624,11 @@ void iwl_txq_ctx_stop(struct iwl_priv *priv)
priv->cfg->ops->lib->txq_set_sched(priv, 0);
/* Stop each Tx DMA channel, and wait for it to be idle */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
- iwl_write_direct32(priv,
- FH_TCSR_CHNL_TX_CONFIG_REG(txq_id), 0x0);
+ for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) {
+ iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE
- (txq_id), 200);
+ FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
+ 1000);
}
iwl_release_nic_access(priv);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -584,7 +645,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv,
struct iwl_tx_cmd *tx_cmd,
struct ieee80211_tx_info *info,
struct ieee80211_hdr *hdr,
- int is_unicast, u8 std_id)
+ u8 std_id)
{
__le16 fc = hdr->frame_control;
__le32 tx_flags = tx_cmd->tx_flags;
@@ -647,11 +708,11 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv,
__le16 fc, int sta_id,
int is_hcca)
{
+ u32 rate_flags = 0;
+ int rate_idx;
u8 rts_retry_limit = 0;
u8 data_retry_limit = 0;
u8 rate_plcp;
- u16 rate_flags = 0;
- int rate_idx;
rate_idx = min(ieee80211_get_tx_rate(priv->hw, info)->hw_value & 0xffff,
IWL_RATE_COUNT - 1);
@@ -694,14 +755,8 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv,
break;
}
- /* Alternate between antenna A and B for successive frames */
- if (priv->use_ant_b_for_management_frame) {
- priv->use_ant_b_for_management_frame = 0;
- rate_flags |= RATE_MCS_ANT_B_MSK;
- } else {
- priv->use_ant_b_for_management_frame = 1;
- rate_flags |= RATE_MCS_ANT_A_MSK;
- }
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant);
+ rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
}
tx_cmd->rts_retry_limit = rts_retry_limit;
@@ -723,7 +778,7 @@ static void iwl_tx_cmd_build_hwcrypto(struct iwl_priv *priv,
memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
if (info->flags & IEEE80211_TX_CTL_AMPDU)
tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK;
- IWL_DEBUG_TX("tx_cmd with aes hwcrypto\n");
+ IWL_DEBUG_TX("tx_cmd with AES hwcrypto\n");
break;
case ALG_TKIP:
@@ -767,7 +822,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl_tfd_frame *tfd;
+ struct iwl_tfd *tfd;
struct iwl_tx_queue *txq;
struct iwl_queue *q;
struct iwl_cmd *out_cmd;
@@ -776,10 +831,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
dma_addr_t phys_addr;
dma_addr_t txcmd_phys;
dma_addr_t scratch_phys;
- u16 len, idx, len_org;
+ u16 len, len_org;
u16 seq_number = 0;
__le16 fc;
- u8 hdr_len, unicast;
+ u8 hdr_len;
u8 sta_id;
u8 wait_write_ptr = 0;
u8 tid = 0;
@@ -799,8 +854,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
goto drop_unlock;
}
- unicast = !is_multicast_ether_addr(hdr->addr1);
-
fc = hdr->frame_control;
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -830,10 +883,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Find (or create) index into station table for destination station */
sta_id = iwl_get_sta_id(priv, hdr);
if (sta_id == IWL_INVALID_STATION) {
- DECLARE_MAC_BUF(mac);
-
- IWL_DEBUG_DROP("Dropping - INVALID STATION: %s\n",
- print_mac(mac, hdr->addr1));
+ IWL_DEBUG_DROP("Dropping - INVALID STATION: %pM\n",
+ hdr->addr1);
goto drop;
}
@@ -856,23 +907,22 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
priv->stations[sta_id].tid[tid].tfds_in_queue++;
}
- /* Descriptor for chosen Tx queue */
txq = &priv->txq[txq_id];
q = &txq->q;
+ txq->swq_id = swq_id;
spin_lock_irqsave(&priv->lock, flags);
/* Set up first empty TFD within this queue's circular TFD buffer */
- tfd = &txq->bd[q->write_ptr];
+ tfd = &txq->tfds[q->write_ptr];
memset(tfd, 0, sizeof(*tfd));
- idx = get_cmd_index(q, q->write_ptr, 0);
/* Set up driver data for this TFD */
memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info));
txq->txb[q->write_ptr].skb[0] = skb;
/* Set up first empty entry in queue's array of Tx/cmd buffers */
- out_cmd = txq->cmd[idx];
+ out_cmd = txq->cmd[q->write_ptr];
tx_cmd = &out_cmd->cmd.tx;
memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd));
@@ -912,12 +962,14 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Physical address of this Tx command's header (not MAC header!),
* within command buffer array. */
- txcmd_phys = pci_map_single(priv->pci_dev, out_cmd,
- sizeof(struct iwl_cmd), PCI_DMA_TODEVICE);
- txcmd_phys += offsetof(struct iwl_cmd, hdr);
-
+ txcmd_phys = pci_map_single(priv->pci_dev,
+ out_cmd, sizeof(struct iwl_cmd),
+ PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys);
+ pci_unmap_len_set(&out_cmd->meta, len, sizeof(struct iwl_cmd));
/* Add buffer containing Tx command and MAC(!) header to TFD's
* first entry */
+ txcmd_phys += offsetof(struct iwl_cmd, hdr);
iwl_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len);
if (info->control.hw_key)
@@ -940,7 +992,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
len = (u16)skb->len;
tx_cmd->len = cpu_to_le16(len);
/* TODO need this for burst mode later on */
- iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, unicast, sta_id);
+ iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, sta_id);
/* set is_hcca to 0; it probably will never be implemented */
iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0);
@@ -950,7 +1002,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) +
offsetof(struct iwl_tx_cmd, scratch);
tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys);
- tx_cmd->dram_msb_ptr = iwl_get_dma_hi_address(scratch_phys);
+ tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys);
if (!ieee80211_has_morefrags(hdr->frame_control)) {
txq->need_update = 1;
@@ -983,7 +1035,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
iwl_txq_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->lock, flags);
} else {
- ieee80211_stop_queue(priv->hw, swq_id);
+ ieee80211_stop_queue(priv->hw, txq->swq_id);
}
}
@@ -1011,7 +1063,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
{
struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
struct iwl_queue *q = &txq->q;
- struct iwl_tfd_frame *tfd;
+ struct iwl_tfd *tfd;
struct iwl_cmd *out_cmd;
dma_addr_t phys_addr;
unsigned long flags;
@@ -1040,7 +1092,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
spin_lock_irqsave(&priv->hcmd_lock, flags);
- tfd = &txq->bd[q->write_ptr];
+ tfd = &txq->tfds[q->write_ptr];
memset(tfd, 0, sizeof(*tfd));
@@ -1061,9 +1113,13 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
out_cmd->hdr.sequence |= SEQ_HUGE_FRAME;
len = (idx == TFD_CMD_SLOTS) ?
IWL_MAX_SCAN_SIZE : sizeof(struct iwl_cmd);
- phys_addr = pci_map_single(priv->pci_dev, out_cmd, len,
- PCI_DMA_TODEVICE);
+
+ phys_addr = pci_map_single(priv->pci_dev, out_cmd,
+ len, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&out_cmd->meta, mapping, phys_addr);
+ pci_unmap_len_set(&out_cmd->meta, len, len);
phys_addr += offsetof(struct iwl_cmd, hdr);
+
iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size);
#ifdef CONFIG_IWLWIFI_DEBUG
@@ -1113,8 +1169,9 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
return 0;
}
- for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+ for (index = iwl_queue_inc_wrap(index, q->n_bd);
+ q->read_ptr != index;
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
tx_info = &txq->txb[txq->q.read_ptr];
ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]);
@@ -1138,44 +1195,34 @@ EXPORT_SYMBOL(iwl_tx_queue_reclaim);
* need to be reclaimed. As result, some free space forms. If there is
* enough free space (> low mark), wake the stack that feeds us.
*/
-static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
+static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id,
+ int idx, int cmd_idx)
{
struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct iwl_queue *q = &txq->q;
- struct iwl_tfd_frame *bd = &txq->bd[index];
- dma_addr_t dma_addr;
- int is_odd, buf_len;
int nfreed = 0;
- if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) {
+ if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) {
IWL_ERROR("Read index for DMA queue txq id (%d), index %d, "
"is out of range [0-%d] %d %d.\n", txq_id,
- index, q->n_bd, q->write_ptr, q->read_ptr);
+ idx, q->n_bd, q->write_ptr, q->read_ptr);
return;
}
- for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index;
- q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
+ pci_unmap_single(priv->pci_dev,
+ pci_unmap_addr(&txq->cmd[cmd_idx]->meta, mapping),
+ pci_unmap_len(&txq->cmd[cmd_idx]->meta, len),
+ PCI_DMA_TODEVICE);
+
+ for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
+ q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
- if (nfreed > 1) {
- IWL_ERROR("HCMD skipped: index (%d) %d %d\n", index,
+ if (nfreed++ > 0) {
+ IWL_ERROR("HCMD skipped: index (%d) %d %d\n", idx,
q->write_ptr, q->read_ptr);
queue_work(priv->workqueue, &priv->restart);
}
- is_odd = (index/2) & 0x1;
- if (is_odd) {
- dma_addr = IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) |
- (IWL_GET_BITS(bd->pa[index],
- tb2_addr_hi20) << 16);
- buf_len = IWL_GET_BITS(bd->pa[index], tb2_len);
- } else {
- dma_addr = le32_to_cpu(bd->pa[index].tb1_addr);
- buf_len = IWL_GET_BITS(bd->pa[index], tb1_len);
- }
- pci_unmap_single(priv->pci_dev, dma_addr, buf_len,
- PCI_DMA_TODEVICE);
- nfreed++;
}
}
@@ -1201,8 +1248,13 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
* command queue then there a command routing bug has been introduced
* in the queue management code. */
if (WARN(txq_id != IWL_CMD_QUEUE_NUM,
- "wrong command queue %d, command id 0x%X\n", txq_id, pkt->hdr.cmd))
+ "wrong command queue %d, sequence 0x%X readp=%d writep=%d\n",
+ txq_id, sequence,
+ priv->txq[IWL_CMD_QUEUE_NUM].q.read_ptr,
+ priv->txq[IWL_CMD_QUEUE_NUM].q.write_ptr)) {
+ iwl_print_hex_dump(priv, IWL_DL_INFO , rxb, 32);
return;
+ }
cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge);
cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index];
@@ -1215,7 +1267,7 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
!cmd->meta.u.callback(priv, cmd, rxb->skb))
rxb->skb = NULL;
- iwl_hcmd_queue_reclaim(priv, txq_id, index);
+ iwl_hcmd_queue_reclaim(priv, txq_id, index, cmd_index);
if (!(cmd->meta.flags & CMD_ASYNC)) {
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
@@ -1248,15 +1300,14 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn)
int ret;
unsigned long flags;
struct iwl_tid_data *tid_data;
- DECLARE_MAC_BUF(mac);
if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo)))
tx_fifo = default_tid_to_tx_fifo[tid];
else
return -EINVAL;
- IWL_WARNING("%s on ra = %s tid = %d\n",
- __func__, print_mac(mac, ra), tid);
+ IWL_WARNING("%s on ra = %pM tid = %d\n",
+ __func__, ra, tid);
sta_id = iwl_find_station(priv, ra);
if (sta_id == IWL_INVALID_STATION)
@@ -1301,7 +1352,6 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid)
struct iwl_tid_data *tid_data;
int ret, write_ptr, read_ptr;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
if (!ra) {
IWL_ERROR("ra = NULL\n");
@@ -1362,8 +1412,8 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id)
case IWL_EMPTYING_HW_QUEUE_DELBA:
/* We are reclaiming the last packet of the */
/* aggregated HW queue */
- if (txq_id == tid_data->agg.txq_id &&
- q->read_ptr == q->write_ptr) {
+ if ((txq_id == tid_data->agg.txq_id) &&
+ (q->read_ptr == q->write_ptr)) {
u16 ssn = SEQ_TO_SN(tid_data->seq_number);
int tx_fifo = default_tid_to_tx_fifo[tid];
IWL_DEBUG_HT("HW queue empty: continue DELBA flow\n");
@@ -1414,7 +1464,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv,
IWL_DEBUG_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl);
/* Calculate shift to align block-ack bits with our Tx window bits */
- sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl>>4);
+ sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4);
if (sh < 0) /* tbw something is wrong with indices */
sh += 0x100;
@@ -1436,7 +1486,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv,
ack = bitmap & (1ULL << i);
successes += !!ack;
IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n",
- ack? "ACK":"NACK", i, (agg->start_idx + i) & 0xff,
+ ack ? "ACK" : "NACK", i, (agg->start_idx + i) & 0xff,
agg->start_idx + i);
}
@@ -1464,10 +1514,11 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
{
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
- int index;
struct iwl_tx_queue *txq = NULL;
struct iwl_ht_agg *agg;
- DECLARE_MAC_BUF(mac);
+ int index;
+ int sta_id;
+ int tid;
/* "flow" corresponds to Tx queue */
u16 scd_flow = le16_to_cpu(ba_resp->scd_flow);
@@ -1482,17 +1533,19 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
}
txq = &priv->txq[scd_flow];
- agg = &priv->stations[ba_resp->sta_id].tid[ba_resp->tid].agg;
+ sta_id = ba_resp->sta_id;
+ tid = ba_resp->tid;
+ agg = &priv->stations[sta_id].tid[tid].agg;
/* Find index just before block-ack window */
index = iwl_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd);
/* TODO: Need to get this copy more safely - now good for debug */
- IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d]Received from %s, "
+ IWL_DEBUG_TX_REPLY("REPLY_COMPRESSED_BA [%d] Received from %pM, "
"sta_id = %d\n",
agg->wait_for_ba,
- print_mac(mac, (u8 *) &ba_resp->sta_addr_lo32),
+ (u8 *) &ba_resp->sta_addr_lo32,
ba_resp->sta_id);
IWL_DEBUG_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = "
"%d, scd_ssn = %d\n",
@@ -1513,18 +1566,15 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
* transmitted ... if not, it's too late anyway). */
if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
/* calculate mac80211 ampdu sw queue to wake */
- int ampdu_q =
- scd_flow - priv->hw_params.first_ampdu_q + priv->hw->queues;
int freed = iwl_tx_queue_reclaim(priv, scd_flow, index);
- priv->stations[ba_resp->sta_id].
- tid[ba_resp->tid].tfds_in_queue -= freed;
- if (iwl_queue_space(&txq->q) > txq->q.low_mark &&
- priv->mac80211_registered &&
- agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)
- ieee80211_wake_queue(priv->hw, ampdu_q);
-
- iwl_txq_check_empty(priv, ba_resp->sta_id,
- ba_resp->tid, scd_flow);
+ priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+
+ if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
+ priv->mac80211_registered &&
+ (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA))
+ ieee80211_wake_queue(priv->hw, txq->swq_id);
+
+ iwl_txq_check_empty(priv, sta_id, tid, scd_flow);
}
}
EXPORT_SYMBOL(iwl_rx_reply_compressed_ba);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 45a6b0c35695..d64580805d6e 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ * Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -41,6 +41,7 @@
#include <linux/if_arp.h>
#include <net/ieee80211_radiotap.h>
+#include <net/lib80211.h>
#include <net/mac80211.h>
#include <asm/div64.h>
@@ -64,11 +65,10 @@ static int iwl3945_tx_queue_update_write_ptr(struct iwl3945_priv *priv,
/* module parameters */
static int iwl3945_param_disable_hw_scan; /* def: 0 = use 3945's h/w scan */
-static int iwl3945_param_debug; /* def: 0 = minimal debug log messages */
+static u32 iwl3945_param_debug; /* def: 0 = minimal debug log messages */
static int iwl3945_param_disable; /* def: 0 = enable radio */
static int iwl3945_param_antenna; /* def: 0 = both antennas (use diversity) */
int iwl3945_param_hwcrypto; /* def: 0 = use software encryption */
-static int iwl3945_param_qos_enable = 1; /* def: 1 = use quality of service */
int iwl3945_param_queues_num = IWL39_MAX_NUM_QUEUES; /* def: 8 Tx queues */
/*
@@ -93,12 +93,13 @@ int iwl3945_param_queues_num = IWL39_MAX_NUM_QUEUES; /* def: 8 Tx queues */
#define IWLWIFI_VERSION "1.2.26k" VD VS
#define DRV_COPYRIGHT "Copyright(c) 2003-2008 Intel Corporation"
+#define DRV_AUTHOR "<ilw@linux.intel.com>"
#define DRV_VERSION IWLWIFI_VERSION
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
static const struct ieee80211_supported_band *iwl3945_get_band(
@@ -107,46 +108,6 @@ static const struct ieee80211_supported_band *iwl3945_get_band(
return priv->hw->wiphy->bands[band];
}
-static int iwl3945_is_empty_essid(const char *essid, int essid_len)
-{
- /* Single white space is for Linksys APs */
- if (essid_len == 1 && essid[0] == ' ')
- return 1;
-
- /* Otherwise, if the entire essid is 0, we assume it is hidden */
- while (essid_len) {
- essid_len--;
- if (essid[essid_len] != '\0')
- return 0;
- }
-
- return 1;
-}
-
-static const char *iwl3945_escape_essid(const char *essid, u8 essid_len)
-{
- static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- const char *s = essid;
- char *d = escaped;
-
- if (iwl3945_is_empty_essid(essid, essid_len)) {
- memcpy(escaped, "<hidden>", sizeof("<hidden>"));
- return escaped;
- }
-
- essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
- while (essid_len--) {
- if (*s == '\0') {
- *d++ = '\\';
- *d++ = '0';
- s++;
- } else
- *d++ = *s++;
- }
- *d = '\0';
- return escaped;
-}
-
/*************** DMA-QUEUE-GENERAL-FUNCTIONS *****
* DMA services
*
@@ -446,7 +407,6 @@ u8 iwl3945_add_station(struct iwl3945_priv *priv, const u8 *addr, int is_ap, u8
int index = IWL_INVALID_STATION;
struct iwl3945_station_entry *station;
unsigned long flags_spin;
- DECLARE_MAC_BUF(mac);
u8 rate;
spin_lock_irqsave(&priv->sta_lock, flags_spin);
@@ -480,7 +440,7 @@ u8 iwl3945_add_station(struct iwl3945_priv *priv, const u8 *addr, int is_ap, u8
return index;
}
- IWL_DEBUG_ASSOC("Add STA ID %d: %s\n", index, print_mac(mac, addr));
+ IWL_DEBUG_ASSOC("Add STA ID %d: %pM\n", index, addr);
station = &priv->stations[index];
station->used = 1;
priv->num_stations++;
@@ -559,7 +519,7 @@ static inline int iwl3945_is_ready_rf(struct iwl3945_priv *priv)
/*************** HOST COMMAND QUEUE FUNCTIONS *****/
-#define IWL_CMD(x) case x : return #x
+#define IWL_CMD(x) case x: return #x
static const char *get_cmd_string(u8 cmd)
{
@@ -1063,7 +1023,6 @@ static int iwl3945_commit_rxon(struct iwl3945_priv *priv)
/* cast away the const for active_rxon in this function */
struct iwl3945_rxon_cmd *active_rxon = (void *)&priv->active_rxon;
int rc = 0;
- DECLARE_MAC_BUF(mac);
if (!iwl3945_is_alive(priv))
return -1;
@@ -1124,11 +1083,11 @@ static int iwl3945_commit_rxon(struct iwl3945_priv *priv)
IWL_DEBUG_INFO("Sending RXON\n"
"* with%s RXON_FILTER_ASSOC_MSK\n"
"* channel = %d\n"
- "* bssid = %s\n",
+ "* bssid = %pM\n",
((priv->staging_rxon.filter_flags &
RXON_FILTER_ASSOC_MSK) ? "" : "out"),
le16_to_cpu(priv->staging_rxon.channel),
- print_mac(mac, priv->staging_rxon.bssid_addr));
+ priv->staging_rxon.bssid_addr);
/* Apply the new configuration */
rc = iwl3945_send_cmd_pdu(priv, REPLY_RXON,
@@ -1443,7 +1402,7 @@ static void iwl3945_free_frame(struct iwl3945_priv *priv, struct iwl3945_frame *
unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv,
struct ieee80211_hdr *hdr,
- const u8 *dest, int left)
+ int left)
{
if (!iwl3945_is_associated(priv) || !priv->ibss_beacon ||
@@ -1459,9 +1418,16 @@ unsigned int iwl3945_fill_beacon_frame(struct iwl3945_priv *priv,
return priv->ibss_beacon->len;
}
-static u8 iwl3945_rate_get_lowest_plcp(int rate_mask)
+static u8 iwl3945_rate_get_lowest_plcp(struct iwl3945_priv *priv)
{
u8 i;
+ int rate_mask;
+
+ /* Set rate mask*/
+ if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)
+ rate_mask = priv->active_rate_basic & IWL_CCK_RATES_MASK;
+ else
+ rate_mask = priv->active_rate_basic & IWL_OFDM_RATES_MASK;
for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID;
i = iwl3945_rates[i].next_ieee) {
@@ -1469,7 +1435,11 @@ static u8 iwl3945_rate_get_lowest_plcp(int rate_mask)
return iwl3945_rates[i].plcp;
}
- return IWL_RATE_INVALID;
+ /* No valid rate was found. Assign the lowest one */
+ if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)
+ return IWL_RATE_1M_PLCP;
+ else
+ return IWL_RATE_6M_PLCP;
}
static int iwl3945_send_beacon_cmd(struct iwl3945_priv *priv)
@@ -1487,16 +1457,7 @@ static int iwl3945_send_beacon_cmd(struct iwl3945_priv *priv)
return -ENOMEM;
}
- if (!(priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK)) {
- rate = iwl3945_rate_get_lowest_plcp(priv->active_rate_basic &
- 0xFF0);
- if (rate == IWL_INVALID_RATE)
- rate = IWL_RATE_6M_PLCP;
- } else {
- rate = iwl3945_rate_get_lowest_plcp(priv->active_rate_basic & 0xF);
- if (rate == IWL_INVALID_RATE)
- rate = IWL_RATE_1M_PLCP;
- }
+ rate = iwl3945_rate_get_lowest_plcp(priv);
frame_size = iwl3945_hw_get_beacon_cmd(priv, frame, rate);
@@ -1544,10 +1505,8 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv)
{
u16 *e = (u16 *)&priv->eeprom;
u32 gp = iwl3945_read32(priv, CSR_EEPROM_GP);
- u32 r;
int sz = sizeof(priv->eeprom);
- int rc;
- int i;
+ int ret;
u16 addr;
/* The EEPROM structure has several padding buffers within it
@@ -1562,29 +1521,28 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv)
}
/* Make sure driver (instead of uCode) is allowed to read EEPROM */
- rc = iwl3945_eeprom_acquire_semaphore(priv);
- if (rc < 0) {
+ ret = iwl3945_eeprom_acquire_semaphore(priv);
+ if (ret < 0) {
IWL_ERROR("Failed to acquire EEPROM semaphore.\n");
return -ENOENT;
}
/* eeprom is an array of 16bit values */
for (addr = 0; addr < sz; addr += sizeof(u16)) {
- _iwl3945_write32(priv, CSR_EEPROM_REG, addr << 1);
- _iwl3945_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
-
- for (i = 0; i < IWL_EEPROM_ACCESS_TIMEOUT;
- i += IWL_EEPROM_ACCESS_DELAY) {
- r = _iwl3945_read_direct32(priv, CSR_EEPROM_REG);
- if (r & CSR_EEPROM_REG_READ_VALID_MSK)
- break;
- udelay(IWL_EEPROM_ACCESS_DELAY);
- }
+ u32 r;
- if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) {
+ _iwl3945_write32(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+ _iwl3945_clear_bit(priv, CSR_EEPROM_REG, CSR_EEPROM_REG_BIT_CMD);
+ ret = iwl3945_poll_direct_bit(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
+ IWL_EEPROM_ACCESS_TIMEOUT);
+ if (ret < 0) {
IWL_ERROR("Time out reading EEPROM[%d]\n", addr);
- return -ETIMEDOUT;
+ return ret;
}
+
+ r = _iwl3945_read_direct32(priv, CSR_EEPROM_REG);
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
}
@@ -1634,7 +1592,7 @@ static u16 iwl3945_supported_rate_to_ie(u8 *ie, u16 supported_rate,
*/
static u16 iwl3945_fill_probe_req(struct iwl3945_priv *priv,
struct ieee80211_mgmt *frame,
- int left, int is_direct)
+ int left)
{
int len = 0;
u8 *pos = NULL;
@@ -1664,20 +1622,6 @@ static u16 iwl3945_fill_probe_req(struct iwl3945_priv *priv,
*pos++ = WLAN_EID_SSID;
*pos++ = 0;
- /* fill in our direct SSID IE... */
- if (is_direct) {
- /* ...next IE... */
- left -= 2 + priv->essid_len;
- if (left < 0)
- return 0;
- /* ... fill it in... */
- *pos++ = WLAN_EID_SSID;
- *pos++ = priv->essid_len;
- memcpy(pos, priv->essid, priv->essid_len);
- pos += priv->essid_len;
- len += 2 + priv->essid_len;
- }
-
/* fill in supported rate */
/* ...next IE... */
left -= 2;
@@ -1746,17 +1690,21 @@ static void iwl3945_reset_qos(struct iwl3945_priv *priv)
spin_lock_irqsave(&priv->lock, flags);
priv->qos_data.qos_active = 0;
- if (priv->iw_mode == NL80211_IFTYPE_ADHOC) {
- if (priv->qos_data.qos_enable)
- priv->qos_data.qos_active = 1;
- if (!(priv->active_rate & 0xfff0)) {
- cw_min = 31;
- is_legacy = 1;
- }
- } else if (priv->iw_mode == NL80211_IFTYPE_AP) {
- if (priv->qos_data.qos_enable)
- priv->qos_data.qos_active = 1;
- } else if (!(priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK)) {
+ /* QoS always active in AP and ADHOC mode
+ * In STA mode wait for association
+ */
+ if (priv->iw_mode == NL80211_IFTYPE_ADHOC ||
+ priv->iw_mode == NL80211_IFTYPE_AP)
+ priv->qos_data.qos_active = 1;
+ else
+ priv->qos_data.qos_active = 0;
+
+
+ /* check for legacy mode */
+ if ((priv->iw_mode == NL80211_IFTYPE_ADHOC &&
+ (priv->active_rate & IWL_OFDM_RATES_MASK) == 0) ||
+ (priv->iw_mode == NL80211_IFTYPE_STATION &&
+ (priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK) == 0)) {
cw_min = 31;
is_legacy = 1;
}
@@ -1828,9 +1776,6 @@ static void iwl3945_activate_qos(struct iwl3945_priv *priv, u8 force)
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
- if (!priv->qos_data.qos_enable)
- return;
-
spin_lock_irqsave(&priv->lock, flags);
priv->qos_data.def_qos_parm.qos_flags = 0;
@@ -1846,7 +1791,7 @@ static void iwl3945_activate_qos(struct iwl3945_priv *priv, u8 force)
spin_unlock_irqrestore(&priv->lock, flags);
if (force || iwl3945_is_associated(priv)) {
- IWL_DEBUG_QOS("send QoS cmd with Qos active %d \n",
+ IWL_DEBUG_QOS("send QoS cmd with QoS active %d \n",
priv->qos_data.qos_active);
iwl3945_send_qos_params_command(priv,
@@ -1870,7 +1815,7 @@ static void iwl3945_activate_qos(struct iwl3945_priv *priv, u8 force)
/* default power management (not Tx power) table values */
-/* for tim 0-10 */
+/* for TIM 0-10 */
static struct iwl3945_power_vec_entry range_0[IWL_POWER_AC] = {
{{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
@@ -1880,7 +1825,7 @@ static struct iwl3945_power_vec_entry range_0[IWL_POWER_AC] = {
{{SLP, SLP_TIMEOUT(25), SLP_TIMEOUT(25), SLP_VEC(4, 7, 10, 10, 10)}, 1}
};
-/* for tim > 10 */
+/* for TIM > 10 */
static struct iwl3945_power_vec_entry range_1[IWL_POWER_AC] = {
{{NOSLP, SLP_TIMEOUT(0), SLP_TIMEOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0},
{{SLP, SLP_TIMEOUT(200), SLP_TIMEOUT(500),
@@ -2156,11 +2101,6 @@ static void iwl3945_setup_rxon_timing(struct iwl3945_priv *priv)
static int iwl3945_scan_initiate(struct iwl3945_priv *priv)
{
- if (priv->iw_mode == NL80211_IFTYPE_AP) {
- IWL_ERROR("APs don't scan.\n");
- return 0;
- }
-
if (!iwl3945_is_ready_rf(priv)) {
IWL_DEBUG_SCAN("Aborting scan due to not ready.\n");
return -EIO;
@@ -2230,13 +2170,14 @@ static void iwl3945_set_flags_for_phymode(struct iwl3945_priv *priv,
/*
* initialize rxon structure with default values from eeprom
*/
-static void iwl3945_connection_init_rx_config(struct iwl3945_priv *priv)
+static void iwl3945_connection_init_rx_config(struct iwl3945_priv *priv,
+ int mode)
{
const struct iwl3945_channel_info *ch_info;
memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon));
- switch (priv->iw_mode) {
+ switch (mode) {
case NL80211_IFTYPE_AP:
priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP;
break;
@@ -2259,7 +2200,7 @@ static void iwl3945_connection_init_rx_config(struct iwl3945_priv *priv)
RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
break;
default:
- IWL_ERROR("Unsupported interface type %d\n", priv->iw_mode);
+ IWL_ERROR("Unsupported interface type %d\n", mode);
break;
}
@@ -2282,8 +2223,7 @@ static void iwl3945_connection_init_rx_config(struct iwl3945_priv *priv)
* in some case A channels are all non IBSS
* in this case force B/G channel
*/
- if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
- !(is_channel_ibss(ch_info)))
+ if ((mode == NL80211_IFTYPE_ADHOC) && !(is_channel_ibss(ch_info)))
ch_info = &priv->channel_info[0];
priv->staging_rxon.channel = cpu_to_le16(ch_info->channel);
@@ -2316,14 +2256,12 @@ static int iwl3945_set_mode(struct iwl3945_priv *priv, int mode)
}
}
- priv->iw_mode = mode;
-
- iwl3945_connection_init_rx_config(priv);
+ iwl3945_connection_init_rx_config(priv, mode);
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
iwl3945_clear_stations_table(priv);
- /* dont commit rxon if rf-kill is on*/
+ /* don't commit rxon if rf-kill is on*/
if (!iwl3945_is_ready_rf(priv))
return -EAGAIN;
@@ -2352,7 +2290,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl3945_priv *priv,
case ALG_CCMP:
cmd->cmd.tx.sec_ctl = TX_CMD_SEC_CCM;
memcpy(cmd->cmd.tx.key, keyinfo->key, keyinfo->keylen);
- IWL_DEBUG_TX("tx_cmd with aes hwcrypto\n");
+ IWL_DEBUG_TX("tx_cmd with AES hwcrypto\n");
break;
case ALG_TKIP:
@@ -2397,6 +2335,7 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv,
{
__le16 fc = hdr->frame_control;
__le32 tx_flags = cmd->cmd.tx.tx_flags;
+ u8 rc_flags = info->control.rates[0].flags;
cmd->cmd.tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
@@ -2423,10 +2362,10 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
tx_flags |= TX_CMD_FLG_RTS_MSK;
tx_flags &= ~TX_CMD_FLG_CTS_MSK;
- } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
+ } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
tx_flags &= ~TX_CMD_FLG_RTS_MSK;
tx_flags |= TX_CMD_FLG_CTS_MSK;
}
@@ -2482,8 +2421,6 @@ static int iwl3945_get_sta_id(struct iwl3945_priv *priv, struct ieee80211_hdr *h
/* If this frame is going out to an IBSS network, find the station,
* or create a new station table entry */
case NL80211_IFTYPE_ADHOC: {
- DECLARE_MAC_BUF(mac);
-
/* Create new station table entry */
sta_id = iwl3945_hw_find_station(priv, hdr->addr1);
if (sta_id != IWL_INVALID_STATION)
@@ -2494,9 +2431,9 @@ static int iwl3945_get_sta_id(struct iwl3945_priv *priv, struct ieee80211_hdr *h
if (sta_id != IWL_INVALID_STATION)
return sta_id;
- IWL_DEBUG_DROP("Station %s not in station map. "
+ IWL_DEBUG_DROP("Station %pM not in station map. "
"Defaulting to broadcast...\n",
- print_mac(mac, hdr->addr1));
+ hdr->addr1);
iwl3945_print_hex_dump(IWL_DL_DROP, (u8 *) hdr, sizeof(*hdr));
return priv->hw_setting.bcast_sta_id;
}
@@ -2579,10 +2516,8 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb)
/* Find (or create) index into station table for destination station */
sta_id = iwl3945_get_sta_id(priv, hdr);
if (sta_id == IWL_INVALID_STATION) {
- DECLARE_MAC_BUF(mac);
-
- IWL_DEBUG_DROP("Dropping - INVALID STATION: %s\n",
- print_mac(mac, hdr->addr1));
+ IWL_DEBUG_DROP("Dropping - INVALID STATION: %pM\n",
+ hdr->addr1);
goto drop;
}
@@ -4019,8 +3954,6 @@ static int iwl3945_tx_queue_update_write_ptr(struct iwl3945_priv *priv,
#ifdef CONFIG_IWL3945_DEBUG
static void iwl3945_print_rx_config_cmd(struct iwl3945_rxon_cmd *rxon)
{
- DECLARE_MAC_BUF(mac);
-
IWL_DEBUG_RADIO("RX CONFIG:\n");
iwl3945_print_hex_dump(IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon));
IWL_DEBUG_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel));
@@ -4031,10 +3964,8 @@ static void iwl3945_print_rx_config_cmd(struct iwl3945_rxon_cmd *rxon)
IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x\n",
rxon->ofdm_basic_rates);
IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates);
- IWL_DEBUG_RADIO("u8[6] node_addr: %s\n",
- print_mac(mac, rxon->node_addr));
- IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n",
- print_mac(mac, rxon->bssid_addr));
+ IWL_DEBUG_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr);
+ IWL_DEBUG_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
}
#endif
@@ -4050,7 +3981,7 @@ static void iwl3945_enable_interrupts(struct iwl3945_priv *priv)
/* call this function to flush any scheduled tasklet */
static inline void iwl_synchronize_irq(struct iwl3945_priv *priv)
{
- /* wait to make sure we flush pedding tasklet*/
+ /* wait to make sure we flush pending tasklet*/
synchronize_irq(priv->pci_dev->irq);
tasklet_kill(&priv->irq_tasklet);
}
@@ -4373,35 +4304,6 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv)
/* Safely ignore these bits for debug checks below */
inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
- /* HW RF KILL switch toggled (4965 only) */
- if (inta & CSR_INT_BIT_RF_KILL) {
- int hw_rf_kill = 0;
- if (!(iwl3945_read32(priv, CSR_GP_CNTRL) &
- CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
- hw_rf_kill = 1;
-
- IWL_DEBUG(IWL_DL_INFO | IWL_DL_RF_KILL | IWL_DL_ISR,
- "RF_KILL bit toggled to %s.\n",
- hw_rf_kill ? "disable radio":"enable radio");
-
- /* Queue restart only if RF_KILL switch was set to "kill"
- * when we loaded driver, and is now set to "enable".
- * After we're Alive, RF_KILL gets handled by
- * iwl3945_rx_card_state_notif() */
- if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) {
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
- queue_work(priv->workqueue, &priv->restart);
- }
-
- handled |= CSR_INT_BIT_RF_KILL;
- }
-
- /* Chip got too hot and stopped itself (4965 only) */
- if (inta & CSR_INT_BIT_CT_KILL) {
- IWL_ERROR("Microcode CT kill error detected.\n");
- handled |= CSR_INT_BIT_CT_KILL;
- }
-
/* Error detected by uCode */
if (inta & CSR_INT_BIT_SW_ERR) {
IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n",
@@ -4502,7 +4404,7 @@ static irqreturn_t iwl3945_isr(int irq, void *data)
if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
/* Hardware disappeared */
- IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta);
+ IWL_WARNING("HARDWARE GONE?? INTA == 0x%08x\n", inta);
goto unplugged;
}
@@ -4805,7 +4707,7 @@ static void iwl3945_free_channel_map(struct iwl3945_priv *priv)
#define IWL_PASSIVE_DWELL_BASE (100)
#define IWL_CHANNEL_TUNE_TIME 5
-#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1))))
+#define IWL_SCAN_PROBE_MASK(n) (BIT(n) | (BIT(n) - BIT(1)))
static inline u16 iwl3945_get_active_dwell_time(struct iwl3945_priv *priv,
enum ieee80211_band band,
@@ -4876,17 +4778,33 @@ static int iwl3945_get_channels_for_scan(struct iwl3945_priv *priv,
continue;
}
+ scan_ch->active_dwell = cpu_to_le16(active_dwell);
+ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+ /* If passive , set up for auto-switch
+ * and use long active_dwell time.
+ */
if (!is_active || is_channel_passive(ch_info) ||
- (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN))
+ (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) {
scan_ch->type = 0; /* passive */
- else
+ if (IWL_UCODE_API(priv->ucode_ver) == 1)
+ scan_ch->active_dwell = cpu_to_le16(passive_dwell - 1);
+ } else {
scan_ch->type = 1; /* active */
+ }
- if ((scan_ch->type & 1) && n_probes)
- scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
-
- scan_ch->active_dwell = cpu_to_le16(active_dwell);
- scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
+ /* Set direct probe bits. These may be used both for active
+ * scan channels (probes gets sent right away),
+ * or for passive channels (probes get se sent only after
+ * hearing clear Rx packet).*/
+ if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
+ if (n_probes)
+ scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
+ } else {
+ /* uCode v1 does not allow setting direct probe bits on
+ * passive channel. */
+ if ((scan_ch->type & 1) && n_probes)
+ scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes);
+ }
/* Set txpower levels to defaults */
scan_ch->tpc.dsp_atten = 110;
@@ -5387,25 +5305,41 @@ static void iwl3945_nic_start(struct iwl3945_priv *priv)
static int iwl3945_read_ucode(struct iwl3945_priv *priv)
{
struct iwl3945_ucode *ucode;
- int ret = 0;
+ int ret = -EINVAL, index;
const struct firmware *ucode_raw;
/* firmware file name contains uCode/driver compatibility version */
- const char *name = priv->cfg->fw_name;
+ const char *name_pre = priv->cfg->fw_name_pre;
+ const unsigned int api_max = priv->cfg->ucode_api_max;
+ const unsigned int api_min = priv->cfg->ucode_api_min;
+ char buf[25];
u8 *src;
size_t len;
- u32 ver, inst_size, data_size, init_size, init_data_size, boot_size;
+ u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size;
/* Ask kernel firmware_class module to get the boot firmware off disk.
* request_firmware() is synchronous, file is in memory on return. */
- ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev);
- if (ret < 0) {
- IWL_ERROR("%s firmware file req failed: Reason %d\n",
- name, ret);
- goto error;
+ for (index = api_max; index >= api_min; index--) {
+ sprintf(buf, "%s%u%s", name_pre, index, ".ucode");
+ ret = request_firmware(&ucode_raw, buf, &priv->pci_dev->dev);
+ if (ret < 0) {
+ IWL_ERROR("%s firmware file req failed: Reason %d\n",
+ buf, ret);
+ if (ret == -ENOENT)
+ continue;
+ else
+ goto error;
+ } else {
+ if (index < api_max)
+ IWL_ERROR("Loaded firmware %s, which is deprecated. Please use API v%u instead.\n",
+ buf, api_max);
+ IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
+ buf, ucode_raw->size);
+ break;
+ }
}
- IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
- name, ucode_raw->size);
+ if (ret < 0)
+ goto error;
/* Make sure that we got at least our header! */
if (ucode_raw->size < sizeof(*ucode)) {
@@ -5417,20 +5351,46 @@ static int iwl3945_read_ucode(struct iwl3945_priv *priv)
/* Data from ucode file: header followed by uCode images */
ucode = (void *)ucode_raw->data;
- ver = le32_to_cpu(ucode->ver);
+ priv->ucode_ver = le32_to_cpu(ucode->ver);
+ api_ver = IWL_UCODE_API(priv->ucode_ver);
inst_size = le32_to_cpu(ucode->inst_size);
data_size = le32_to_cpu(ucode->data_size);
init_size = le32_to_cpu(ucode->init_size);
init_data_size = le32_to_cpu(ucode->init_data_size);
boot_size = le32_to_cpu(ucode->boot_size);
- IWL_DEBUG_INFO("f/w package hdr ucode version = 0x%x\n", ver);
+ /* api_ver should match the api version forming part of the
+ * firmware filename ... but we don't check for that and only rely
+ * on the API version read from firware header from here on forward */
+
+ if (api_ver < api_min || api_ver > api_max) {
+ IWL_ERROR("Driver unable to support your firmware API. "
+ "Driver supports v%u, firmware is v%u.\n",
+ api_max, api_ver);
+ priv->ucode_ver = 0;
+ ret = -EINVAL;
+ goto err_release;
+ }
+ if (api_ver != api_max)
+ IWL_ERROR("Firmware has old API version. Expected %u, "
+ "got %u. New firmware can be obtained "
+ "from http://www.intellinuxwireless.org.\n",
+ api_max, api_ver);
+
+ printk(KERN_INFO DRV_NAME " loaded firmware version %u.%u.%u.%u\n",
+ IWL_UCODE_MAJOR(priv->ucode_ver),
+ IWL_UCODE_MINOR(priv->ucode_ver),
+ IWL_UCODE_API(priv->ucode_ver),
+ IWL_UCODE_SERIAL(priv->ucode_ver));
+ IWL_DEBUG_INFO("f/w package hdr ucode version raw = 0x%x\n",
+ priv->ucode_ver);
IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n", inst_size);
IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n", data_size);
IWL_DEBUG_INFO("f/w package hdr init inst size = %u\n", init_size);
IWL_DEBUG_INFO("f/w package hdr init data size = %u\n", init_data_size);
IWL_DEBUG_INFO("f/w package hdr boot inst size = %u\n", boot_size);
+
/* Verify size of file vs. image size info in file's header */
if (ucode_raw->size < sizeof(*ucode) +
inst_size + data_size + init_size +
@@ -5607,7 +5567,7 @@ static int iwl3945_set_ucode_ptrs(struct iwl3945_priv *priv)
iwl3945_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG,
priv->ucode_data.len);
- /* Inst bytecount must be last to set up, bit 31 signals uCode
+ /* Inst byte count must be last to set up, bit 31 signals uCode
* that all new ptr/size info is in place */
iwl3945_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
priv->ucode_code.len | BSM_DRAM_INST_LOAD);
@@ -5665,6 +5625,10 @@ static void iwl3945_init_alive_start(struct iwl3945_priv *priv)
}
+/* temporary */
+static int iwl3945_mac_beacon_update(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
/**
* iwl3945_alive_start - called after REPLY_ALIVE notification received
* from protocol/runtime uCode (initialization uCode's
@@ -5699,7 +5663,7 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
rc = iwl3945_grab_nic_access(priv);
if (rc) {
- IWL_WARNING("Can not read rfkill status from adapter\n");
+ IWL_WARNING("Can not read RFKILL status from adapter\n");
return;
}
@@ -5709,7 +5673,7 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
if (rfkill & 0x1) {
clear_bit(STATUS_RF_KILL_HW, &priv->status);
- /* if rfkill is not on, then wait for thermal
+ /* if RFKILL is not on, then wait for thermal
* sensor in adapter to kick in */
while (iwl3945_hw_get_temperature(priv) == 0) {
thermal_spin++;
@@ -5747,7 +5711,7 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK;
} else {
/* Initialize our rx_config data */
- iwl3945_connection_init_rx_config(priv);
+ iwl3945_connection_init_rx_config(priv, priv->iw_mode);
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
}
@@ -5768,6 +5732,14 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
if (priv->error_recovering)
iwl3945_error_recovery(priv);
+ /* reassociate for ADHOC mode */
+ if (priv->vif && (priv->iw_mode == NL80211_IFTYPE_ADHOC)) {
+ struct sk_buff *beacon = ieee80211_beacon_get(priv->hw,
+ priv->vif);
+ if (beacon)
+ iwl3945_mac_beacon_update(priv->hw, beacon);
+ }
+
return;
restart:
@@ -5902,7 +5874,7 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
}
if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) {
- IWL_ERROR("ucode not available for device bringup\n");
+ IWL_ERROR("ucode not available for device bring up\n");
return -EIO;
}
@@ -6046,24 +6018,6 @@ static void iwl3945_bg_rf_kill(struct work_struct *work)
iwl3945_rfkill_set_hw_state(priv);
}
-static void iwl3945_bg_set_monitor(struct work_struct *work)
-{
- struct iwl3945_priv *priv = container_of(work,
- struct iwl3945_priv, set_monitor);
-
- IWL_DEBUG(IWL_DL_STATE, "setting monitor mode\n");
-
- mutex_lock(&priv->mutex);
-
- if (!iwl3945_is_ready(priv))
- IWL_DEBUG(IWL_DL_STATE, "leave - not ready\n");
- else
- if (iwl3945_set_mode(priv, NL80211_IFTYPE_MONITOR) != 0)
- IWL_ERROR("iwl3945_set_mode() failed\n");
-
- mutex_unlock(&priv->mutex);
-}
-
#define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
static void iwl3945_bg_scan_check(struct work_struct *data)
@@ -6101,6 +6055,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
struct ieee80211_conf *conf = NULL;
u8 n_probes = 2;
enum ieee80211_band band;
+ DECLARE_SSID_BUF(ssid);
conf = ieee80211_get_hw_conf(priv->hw);
@@ -6111,7 +6066,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
goto done;
}
- /* Make sure the scan wasn't cancelled before this queued work
+ /* Make sure the scan wasn't canceled before this queued work
* was given the chance to run... */
if (!test_bit(STATUS_SCANNING, &priv->status))
goto done;
@@ -6201,21 +6156,13 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
if (priv->one_direct_scan) {
IWL_DEBUG_SCAN
("Kicking off one direct scan for '%s'\n",
- iwl3945_escape_essid(priv->direct_ssid,
- priv->direct_ssid_len));
+ print_ssid(ssid, priv->direct_ssid,
+ priv->direct_ssid_len));
scan->direct_scan[0].id = WLAN_EID_SSID;
scan->direct_scan[0].len = priv->direct_ssid_len;
memcpy(scan->direct_scan[0].ssid,
priv->direct_ssid, priv->direct_ssid_len);
n_probes++;
- } else if (!iwl3945_is_associated(priv) && priv->essid_len) {
- IWL_DEBUG_SCAN
- ("Kicking off one direct scan for '%s' when not associated\n",
- iwl3945_escape_essid(priv->essid, priv->essid_len));
- scan->direct_scan[0].id = WLAN_EID_SSID;
- scan->direct_scan[0].len = priv->essid_len;
- memcpy(scan->direct_scan[0].ssid, priv->essid, priv->essid_len);
- n_probes++;
} else
IWL_DEBUG_SCAN("Kicking off one indirect scan.\n");
@@ -6223,7 +6170,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
* that based on the direct_mask added to each channel entry */
scan->tx_cmd.len = cpu_to_le16(
iwl3945_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data,
- IWL_MAX_SCAN_SIZE - sizeof(*scan), 0));
+ IWL_MAX_SCAN_SIZE - sizeof(*scan)));
scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
scan->tx_cmd.sta_id = priv->hw_setting.bcast_sta_id;
scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
@@ -6333,7 +6280,6 @@ static void iwl3945_post_associate(struct iwl3945_priv *priv)
{
int rc = 0;
struct ieee80211_conf *conf = NULL;
- DECLARE_MAC_BUF(mac);
if (priv->iw_mode == NL80211_IFTYPE_AP) {
IWL_ERROR("%s Should not be called in AP mode\n", __func__);
@@ -6341,9 +6287,8 @@ static void iwl3945_post_associate(struct iwl3945_priv *priv)
}
- IWL_DEBUG_ASSOC("Associated as %d to: %s\n",
- priv->assoc_id,
- print_mac(mac, priv->active_rxon.bssid_addr));
+ IWL_DEBUG_ASSOC("Associated as %d to: %pM\n",
+ priv->assoc_id, priv->active_rxon.bssid_addr);
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
@@ -6398,10 +6343,7 @@ static void iwl3945_post_associate(struct iwl3945_priv *priv)
case NL80211_IFTYPE_ADHOC:
- /* clear out the station table */
- iwl3945_clear_stations_table(priv);
-
- iwl3945_add_station(priv, iwl3945_broadcast_addr, 0, 0);
+ priv->assoc_id = 1;
iwl3945_add_station(priv, priv->bssid, 0, 0);
iwl3945_sync_sta(priv, IWL_STA_ID,
(priv->band == IEEE80211_BAND_5GHZ) ?
@@ -6439,7 +6381,7 @@ static void iwl3945_bg_abort_scan(struct work_struct *work)
mutex_unlock(&priv->mutex);
}
-static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+static int iwl3945_mac_config(struct ieee80211_hw *hw, u32 changed);
static void iwl3945_bg_scan_completed(struct work_struct *work)
{
@@ -6452,7 +6394,7 @@ static void iwl3945_bg_scan_completed(struct work_struct *work)
return;
if (test_bit(STATUS_CONF_PENDING, &priv->status))
- iwl3945_mac_config(priv->hw, ieee80211_get_hw_conf(priv->hw));
+ iwl3945_mac_config(priv->hw, 0);
ieee80211_scan_completed(priv->hw);
@@ -6604,7 +6546,6 @@ static int iwl3945_mac_add_interface(struct ieee80211_hw *hw,
{
struct iwl3945_priv *priv = hw->priv;
unsigned long flags;
- DECLARE_MAC_BUF(mac);
IWL_DEBUG_MAC80211("enter: type %d\n", conf->type);
@@ -6615,13 +6556,14 @@ static int iwl3945_mac_add_interface(struct ieee80211_hw *hw,
spin_lock_irqsave(&priv->lock, flags);
priv->vif = conf->vif;
+ priv->iw_mode = conf->type;
spin_unlock_irqrestore(&priv->lock, flags);
mutex_lock(&priv->mutex);
if (conf->mac_addr) {
- IWL_DEBUG_MAC80211("Set: %s\n", print_mac(mac, conf->mac_addr));
+ IWL_DEBUG_MAC80211("Set: %pM\n", conf->mac_addr);
memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
}
@@ -6641,10 +6583,11 @@ static int iwl3945_mac_add_interface(struct ieee80211_hw *hw,
* be set inappropriately and the driver currently sets the hardware up to
* use it whenever needed.
*/
-static int iwl3945_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+static int iwl3945_mac_config(struct ieee80211_hw *hw, u32 changed)
{
struct iwl3945_priv *priv = hw->priv;
const struct iwl3945_channel_info *ch_info;
+ struct ieee80211_conf *conf = &hw->conf;
unsigned long flags;
int ret = 0;
@@ -6782,16 +6725,11 @@ static void iwl3945_config_ap(struct iwl3945_priv *priv)
* clear sta table, add BCAST sta... */
}
-/* temporary */
-static int iwl3945_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb);
-
static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{
struct iwl3945_priv *priv = hw->priv;
- DECLARE_MAC_BUF(mac);
- unsigned long flags;
int rc;
if (conf == NULL)
@@ -6808,28 +6746,20 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
if (!beacon)
return -ENOMEM;
+ mutex_lock(&priv->mutex);
rc = iwl3945_mac_beacon_update(hw, beacon);
+ mutex_unlock(&priv->mutex);
if (rc)
return rc;
}
- /* XXX: this MUST use conf->mac_addr */
-
- if ((priv->iw_mode == NL80211_IFTYPE_AP) &&
- (!conf->ssid_len)) {
- IWL_DEBUG_MAC80211
- ("Leaving in AP mode because HostAPD is not ready.\n");
- return 0;
- }
-
if (!iwl3945_is_alive(priv))
return -EAGAIN;
mutex_lock(&priv->mutex);
if (conf->bssid)
- IWL_DEBUG_MAC80211("bssid: %s\n",
- print_mac(mac, conf->bssid));
+ IWL_DEBUG_MAC80211("bssid: %pM\n", conf->bssid);
/*
* very dubious code was here; the probe filtering flag is never set:
@@ -6842,8 +6772,8 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
if (!conf->bssid) {
conf->bssid = priv->mac_addr;
memcpy(priv->bssid, priv->mac_addr, ETH_ALEN);
- IWL_DEBUG_MAC80211("bssid was set to: %s\n",
- print_mac(mac, conf->bssid));
+ IWL_DEBUG_MAC80211("bssid was set to: %pM\n",
+ conf->bssid);
}
if (priv->ibss_beacon)
dev_kfree_skb(priv->ibss_beacon);
@@ -6889,15 +6819,6 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
}
done:
- spin_lock_irqsave(&priv->lock, flags);
- if (!conf->ssid_len)
- memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
- else
- memcpy(priv->essid, conf->ssid, conf->ssid_len);
-
- priv->essid_len = conf->ssid_len;
- spin_unlock_irqrestore(&priv->lock, flags);
-
IWL_DEBUG_MAC80211("leave\n");
mutex_unlock(&priv->mutex);
@@ -6910,16 +6831,43 @@ static void iwl3945_configure_filter(struct ieee80211_hw *hw,
int mc_count, struct dev_addr_list *mc_list)
{
struct iwl3945_priv *priv = hw->priv;
+ __le32 *filter_flags = &priv->staging_rxon.filter_flags;
- if (changed_flags & (*total_flags) & FIF_OTHER_BSS) {
- IWL_DEBUG_MAC80211("Enter: type %d (0x%x, 0x%x)\n",
- NL80211_IFTYPE_MONITOR,
- changed_flags, *total_flags);
- /* queue work 'cuz mac80211 is holding a lock which
- * prevents us from issuing (synchronous) f/w cmds */
- queue_work(priv->workqueue, &priv->set_monitor);
+ IWL_DEBUG_MAC80211("Enter: changed: 0x%x, total: 0x%x\n",
+ changed_flags, *total_flags);
+
+ if (changed_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS)) {
+ if (*total_flags & (FIF_OTHER_BSS | FIF_PROMISC_IN_BSS))
+ *filter_flags |= RXON_FILTER_PROMISC_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_PROMISC_MSK;
}
- *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI |
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*total_flags & FIF_ALLMULTI)
+ *filter_flags |= RXON_FILTER_ACCEPT_GRP_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_ACCEPT_GRP_MSK;
+ }
+ if (changed_flags & FIF_CONTROL) {
+ if (*total_flags & FIF_CONTROL)
+ *filter_flags |= RXON_FILTER_CTL2HOST_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_CTL2HOST_MSK;
+ }
+ if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+ *filter_flags |= RXON_FILTER_BCON_AWARE_MSK;
+ else
+ *filter_flags &= ~RXON_FILTER_BCON_AWARE_MSK;
+ }
+
+ /* We avoid iwl_commit_rxon here to commit the new filter flags
+ * since mac80211 will call ieee80211_hw_config immediately.
+ * (mc_list is not supported at this time). Otherwise, we need to
+ * queue a background iwl_commit_rxon work.
+ */
+
+ *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS |
FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL;
}
@@ -6940,8 +6888,6 @@ static void iwl3945_mac_remove_interface(struct ieee80211_hw *hw,
if (priv->vif == conf->vif) {
priv->vif = NULL;
memset(priv->bssid, 0, ETH_ALEN);
- memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
- priv->essid_len = 0;
}
mutex_unlock(&priv->mutex);
@@ -7010,6 +6956,7 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
int rc = 0;
unsigned long flags;
struct iwl3945_priv *priv = hw->priv;
+ DECLARE_SSID_BUF(ssid_buf);
IWL_DEBUG_MAC80211("enter\n");
@@ -7022,12 +6969,6 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
goto out_unlock;
}
- if (priv->iw_mode == NL80211_IFTYPE_AP) { /* APs don't scan */
- rc = -EIO;
- IWL_ERROR("ERROR: APs don't scan\n");
- goto out_unlock;
- }
-
/* we don't schedule scan within next_scan_jiffies period */
if (priv->next_scan_jiffies &&
time_after(priv->next_scan_jiffies, jiffies)) {
@@ -7043,7 +6984,7 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
}
if (len) {
IWL_DEBUG_SCAN("direct scan for %s [%d]\n ",
- iwl3945_escape_essid(ssid, len), (int)len);
+ print_ssid(ssid_buf, ssid, len), (int)len);
priv->one_direct_scan = 1;
priv->direct_ssid_len = (u8)
@@ -7084,10 +7025,8 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
sta_id = iwl3945_hw_find_station(priv, addr);
if (sta_id == IWL_INVALID_STATION) {
- DECLARE_MAC_BUF(mac);
-
- IWL_DEBUG_MAC80211("leave - %s not in station map.\n",
- print_mac(mac, addr));
+ IWL_DEBUG_MAC80211("leave - %pM not in station map.\n",
+ addr);
return -EINVAL;
}
@@ -7143,11 +7082,6 @@ static int iwl3945_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
return 0;
}
- if (!priv->qos_data.qos_enable) {
- priv->qos_data.qos_active = 0;
- IWL_DEBUG_MAC80211("leave - qos not enabled\n");
- return 0;
- }
q = AC_NUM - 1 - queue;
spin_lock_irqsave(&priv->lock, flags);
@@ -7219,14 +7153,6 @@ static int iwl3945_mac_get_stats(struct ieee80211_hw *hw,
return 0;
}
-static u64 iwl3945_mac_get_tsf(struct ieee80211_hw *hw)
-{
- IWL_DEBUG_MAC80211("enter\n");
- IWL_DEBUG_MAC80211("leave\n");
-
- return 0;
-}
-
static void iwl3945_mac_reset_tsf(struct ieee80211_hw *hw)
{
struct iwl3945_priv *priv = hw->priv;
@@ -7292,18 +7218,15 @@ static int iwl3945_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *sk
struct iwl3945_priv *priv = hw->priv;
unsigned long flags;
- mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211("enter\n");
if (!iwl3945_is_ready_rf(priv)) {
IWL_DEBUG_MAC80211("leave - RF not ready\n");
- mutex_unlock(&priv->mutex);
return -EIO;
}
if (priv->iw_mode != NL80211_IFTYPE_ADHOC) {
IWL_DEBUG_MAC80211("leave - not IBSS\n");
- mutex_unlock(&priv->mutex);
return -EIO;
}
@@ -7323,7 +7246,6 @@ static int iwl3945_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *sk
iwl3945_post_associate(priv);
- mutex_unlock(&priv->mutex);
return 0;
}
@@ -7792,7 +7714,7 @@ static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log);
/*****************************************************************************
*
- * driver setup and teardown
+ * driver setup and tear down
*
*****************************************************************************/
@@ -7810,7 +7732,6 @@ static void iwl3945_setup_deferred_work(struct iwl3945_priv *priv)
INIT_WORK(&priv->abort_scan, iwl3945_bg_abort_scan);
INIT_WORK(&priv->rf_kill, iwl3945_bg_rf_kill);
INIT_WORK(&priv->beacon_update, iwl3945_bg_beacon_update);
- INIT_WORK(&priv->set_monitor, iwl3945_bg_set_monitor);
INIT_DELAYED_WORK(&priv->init_alive_start, iwl3945_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start);
INIT_DELAYED_WORK(&priv->scan_check, iwl3945_bg_scan_check);
@@ -7869,7 +7790,6 @@ static struct ieee80211_ops iwl3945_hw_ops = {
.get_stats = iwl3945_mac_get_stats,
.get_tx_stats = iwl3945_mac_get_tx_stats,
.conf_tx = iwl3945_mac_conf_tx,
- .get_tsf = iwl3945_mac_get_tsf,
.reset_tsf = iwl3945_mac_reset_tsf,
.bss_info_changed = iwl3945_bss_info_changed,
.hw_scan = iwl3945_mac_hw_scan
@@ -7882,7 +7802,10 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
struct ieee80211_hw *hw;
struct iwl_3945_cfg *cfg = (struct iwl_3945_cfg *)(ent->driver_data);
unsigned long flags;
- DECLARE_MAC_BUF(mac);
+
+ /***********************
+ * 1. Allocating HW data
+ * ********************/
/* Disabling hardware scan means that mac80211 will perform scans
* "the hard way", rather than using device's scan. */
@@ -7907,48 +7830,41 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
err = -ENOMEM;
goto out;
}
- SET_IEEE80211_DEV(hw, &pdev->dev);
- hw->rate_control_algorithm = "iwl-3945-rs";
- hw->sta_data_size = sizeof(struct iwl3945_sta_priv);
+ SET_IEEE80211_DEV(hw, &pdev->dev);
- IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
priv = hw->priv;
priv->hw = hw;
-
priv->pci_dev = pdev;
priv->cfg = cfg;
+ IWL_DEBUG_INFO("*** LOAD DRIVER ***\n");
+ hw->rate_control_algorithm = "iwl-3945-rs";
+ hw->sta_data_size = sizeof(struct iwl3945_sta_priv);
+
/* Select antenna (may be helpful if only one antenna is connected) */
priv->antenna = (enum iwl3945_antenna)iwl3945_param_antenna;
#ifdef CONFIG_IWL3945_DEBUG
iwl3945_debug_level = iwl3945_param_debug;
atomic_set(&priv->restrict_refcnt, 0);
#endif
- priv->retry_rate = 1;
-
- priv->ibss_beacon = NULL;
/* Tell mac80211 our characteristics */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM;
hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
+ hw->wiphy->fw_handles_regulatory = true;
+
/* 4 EDCA QOS priorities */
hw->queues = 4;
- spin_lock_init(&priv->lock);
- spin_lock_init(&priv->power_data.lock);
- spin_lock_init(&priv->sta_lock);
- spin_lock_init(&priv->hcmd_lock);
-
- INIT_LIST_HEAD(&priv->free_frames);
-
- mutex_init(&priv->mutex);
+ /***************************
+ * 2. Initializing PCI bus
+ * *************************/
if (pci_enable_device(pdev)) {
err = -ENODEV;
goto out_ieee80211_free_hw;
@@ -7956,14 +7872,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
pci_set_master(pdev);
- /* Clear the driver's (not device's) station table */
- iwl3945_clear_stations_table(priv);
-
- priv->data_retry_limit = -1;
- priv->ieee_channels = NULL;
- priv->ieee_rates = NULL;
- priv->band = IEEE80211_BAND_2GHZ;
-
err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (!err)
err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
@@ -7977,10 +7885,9 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
if (err)
goto out_pci_disable_device;
- /* We disable the RETRY_TIMEOUT register (0x41) to keep
- * PCI Tx retries from interfering with C3 CPU state */
- pci_write_config_byte(pdev, 0x41, 0x00);
-
+ /***********************
+ * 3. Read REV Register
+ * ********************/
priv->hw_base = pci_iomap(pdev, 0, 0);
if (!priv->hw_base) {
err = -ENODEV;
@@ -7991,97 +7898,144 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
(unsigned long long) pci_resource_len(pdev, 0));
IWL_DEBUG_INFO("pci_resource_base = %p\n", priv->hw_base);
- /* Initialize module parameter values here */
+ /* We disable the RETRY_TIMEOUT register (0x41) to keep
+ * PCI Tx retries from interfering with C3 CPU state */
+ pci_write_config_byte(pdev, 0x41, 0x00);
- /* Disable radio (SW RF KILL) via parameter when loading driver */
- if (iwl3945_param_disable) {
- set_bit(STATUS_RF_KILL_SW, &priv->status);
- IWL_DEBUG_INFO("Radio disabled.\n");
- }
+ /* nic init */
+ iwl3945_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
- priv->iw_mode = NL80211_IFTYPE_STATION;
+ iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ err = iwl3945_poll_direct_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ if (err < 0) {
+ IWL_DEBUG_INFO("Failed to init the card\n");
+ goto out_remove_sysfs;
+ }
- printk(KERN_INFO DRV_NAME
- ": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
+ /***********************
+ * 4. Read EEPROM
+ * ********************/
+ /* Read the EEPROM */
+ err = iwl3945_eeprom_init(priv);
+ if (err) {
+ IWL_ERROR("Unable to init EEPROM\n");
+ goto out_remove_sysfs;
+ }
+ /* MAC Address location in EEPROM same for 3945/4965 */
+ get_eeprom_mac(priv, priv->mac_addr);
+ IWL_DEBUG_INFO("MAC address: %pM\n", priv->mac_addr);
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
+ /***********************
+ * 5. Setup HW Constants
+ * ********************/
/* Device-specific setup */
if (iwl3945_hw_set_hw_setting(priv)) {
IWL_ERROR("failed to set hw settings\n");
goto out_iounmap;
}
- if (iwl3945_param_qos_enable)
- priv->qos_data.qos_enable = 1;
+ /***********************
+ * 6. Setup priv
+ * ********************/
+ priv->retry_rate = 1;
+ priv->ibss_beacon = NULL;
+
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->power_data.lock);
+ spin_lock_init(&priv->sta_lock);
+ spin_lock_init(&priv->hcmd_lock);
+
+ INIT_LIST_HEAD(&priv->free_frames);
+ mutex_init(&priv->mutex);
+
+ /* Clear the driver's (not device's) station table */
+ iwl3945_clear_stations_table(priv);
+
+ priv->data_retry_limit = -1;
+ priv->ieee_channels = NULL;
+ priv->ieee_rates = NULL;
+ priv->band = IEEE80211_BAND_2GHZ;
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
iwl3945_reset_qos(priv);
priv->qos_data.qos_active = 0;
priv->qos_data.qos_cap.val = 0;
- iwl3945_set_rxon_channel(priv, IEEE80211_BAND_2GHZ, 6);
- iwl3945_setup_deferred_work(priv);
- iwl3945_setup_rx_handlers(priv);
priv->rates_mask = IWL_RATES_MASK;
/* If power management is turned on, default to AC mode */
priv->power_mode = IWL_POWER_AC;
priv->user_txpower_limit = IWL_DEFAULT_TX_POWER;
- spin_lock_irqsave(&priv->lock, flags);
- iwl3945_disable_interrupts(priv);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- err = sysfs_create_group(&pdev->dev.kobj, &iwl3945_attribute_group);
+ err = iwl3945_init_channel_map(priv);
if (err) {
- IWL_ERROR("failed to create sysfs device attributes\n");
+ IWL_ERROR("initializing regulatory failed: %d\n", err);
goto out_release_irq;
}
- /* nic init */
- iwl3945_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
-
- iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- err = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (err < 0) {
- IWL_DEBUG_INFO("Failed to init the card\n");
- goto out_remove_sysfs;
- }
- /* Read the EEPROM */
- err = iwl3945_eeprom_init(priv);
+ err = iwl3945_init_geos(priv);
if (err) {
- IWL_ERROR("Unable to init EEPROM\n");
- goto out_remove_sysfs;
+ IWL_ERROR("initializing geos failed: %d\n", err);
+ goto out_free_channel_map;
}
- /* MAC Address location in EEPROM same for 3945/4965 */
- get_eeprom_mac(priv, priv->mac_addr);
- IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr));
- SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
- err = iwl3945_init_channel_map(priv);
- if (err) {
- IWL_ERROR("initializing regulatory failed: %d\n", err);
- goto out_remove_sysfs;
+ printk(KERN_INFO DRV_NAME
+ ": Detected Intel Wireless WiFi Link %s\n", priv->cfg->name);
+
+ /***********************************
+ * 7. Initialize Module Parameters
+ * **********************************/
+
+ /* Initialize module parameter values here */
+ /* Disable radio (SW RF KILL) via parameter when loading driver */
+ if (iwl3945_param_disable) {
+ set_bit(STATUS_RF_KILL_SW, &priv->status);
+ IWL_DEBUG_INFO("Radio disabled.\n");
}
- err = iwl3945_init_geos(priv);
+
+ /***********************
+ * 8. Setup Services
+ * ********************/
+
+ spin_lock_irqsave(&priv->lock, flags);
+ iwl3945_disable_interrupts(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &iwl3945_attribute_group);
if (err) {
- IWL_ERROR("initializing geos failed: %d\n", err);
- goto out_free_channel_map;
+ IWL_ERROR("failed to create sysfs device attributes\n");
+ goto out_free_geos;
}
+ iwl3945_set_rxon_channel(priv, IEEE80211_BAND_2GHZ, 6);
+ iwl3945_setup_deferred_work(priv);
+ iwl3945_setup_rx_handlers(priv);
+
+ /***********************
+ * 9. Conclude
+ * ********************/
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+
+ /*********************************
+ * 10. Setup and Register mac80211
+ * *******************************/
+
err = ieee80211_register_hw(priv->hw);
if (err) {
IWL_ERROR("Failed to register network device (error %d)\n", err);
- goto out_free_geos;
+ goto out_remove_sysfs;
}
priv->hw->conf.beacon_int = 100;
priv->mac80211_registered = 1;
- pci_save_state(pdev);
- pci_disable_device(pdev);
+
err = iwl3945_rfkill_init(priv);
if (err)
@@ -8090,12 +8044,13 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
return 0;
+ out_remove_sysfs:
+ sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
out_free_geos:
iwl3945_free_geos(priv);
out_free_channel_map:
iwl3945_free_channel_map(priv);
- out_remove_sysfs:
- sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
+
out_release_irq:
destroy_workqueue(priv->workqueue);
@@ -8222,7 +8177,7 @@ static int iwl3945_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0;
- IWL_DEBUG_RF_KILL("we recieved soft RFKILL set to state %d\n", state);
+ IWL_DEBUG_RF_KILL("we received soft RFKILL set to state %d\n", state);
mutex_lock(&priv->mutex);
switch (state) {
@@ -8237,7 +8192,7 @@ static int iwl3945_rfkill_soft_rf_kill(void *data, enum rfkill_state state)
iwl3945_radio_kill_sw(priv, 1);
break;
default:
- IWL_WARNING("we recieved unexpected RFKILL state %d\n", state);
+ IWL_WARNING("we received unexpected RFKILL state %d\n", state);
break;
}
out_unlock:
@@ -8379,7 +8334,7 @@ static void __exit iwl3945_exit(void)
iwl3945_rate_control_unregister();
}
-MODULE_FIRMWARE("iwlwifi-3945" IWL3945_UCODE_API ".ucode");
+MODULE_FIRMWARE(IWL3945_MODULE_FIRMWARE(IWL3945_UCODE_API_MAX));
module_param_named(antenna, iwl3945_param_antenna, int, 0444);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
@@ -8388,7 +8343,7 @@ MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])");
module_param_named(hwcrypto, iwl3945_param_hwcrypto, int, 0444);
MODULE_PARM_DESC(hwcrypto,
"using hardware crypto engine (default 0 [software])\n");
-module_param_named(debug, iwl3945_param_debug, int, 0444);
+module_param_named(debug, iwl3945_param_debug, uint, 0444);
MODULE_PARM_DESC(debug, "debug output mask");
module_param_named(disable_hw_scan, iwl3945_param_disable_hw_scan, int, 0444);
MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
@@ -8396,9 +8351,5 @@ MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
module_param_named(queues_num, iwl3945_param_queues_num, int, 0444);
MODULE_PARM_DESC(queues_num, "number of hw queues.");
-/* QoS */
-module_param_named(qos_enable, iwl3945_param_qos_enable, int, 0444);
-MODULE_PARM_DESC(qos_enable, "enable all QoS functionality");
-
module_exit(iwl3945_exit);
module_init(iwl3945_init);
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
index 92be60415d04..a0e440cd8967 100644
--- a/drivers/net/wireless/libertas/assoc.c
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -1,6 +1,10 @@
/* Copyright (C) 2006, Red Hat, Inc. */
+#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/if_arp.h>
+#include <net/lib80211.h>
#include "assoc.h"
#include "decl.h"
@@ -151,18 +155,18 @@ static int lbs_adhoc_join(struct lbs_private *priv,
struct cmd_ds_802_11_ad_hoc_join cmd;
struct bss_descriptor *bss = &assoc_req->bss;
u8 preamble = RADIO_PREAMBLE_LONG;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
u16 ratesize = 0;
int ret = 0;
lbs_deb_enter(LBS_DEB_ASSOC);
lbs_deb_join("current SSID '%s', ssid length %u\n",
- escape_essid(priv->curbssparams.ssid,
+ print_ssid(ssid, priv->curbssparams.ssid,
priv->curbssparams.ssid_len),
priv->curbssparams.ssid_len);
lbs_deb_join("requested ssid '%s', ssid length %u\n",
- escape_essid(bss->ssid, bss->ssid_len),
+ print_ssid(ssid, bss->ssid, bss->ssid_len),
bss->ssid_len);
/* check if the requested SSID is already joined */
@@ -226,8 +230,8 @@ static int lbs_adhoc_join(struct lbs_private *priv,
bss->capability, CAPINFO_MASK);
/* information on BSSID descriptor passed to FW */
- lbs_deb_join("ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n",
- print_mac(mac, cmd.bss.bssid), cmd.bss.ssid);
+ lbs_deb_join("ADHOC_J_CMD: BSSID = %pM, SSID = '%s'\n",
+ cmd.bss.bssid, cmd.bss.ssid);
/* Only v8 and below support setting these */
if (priv->fwrelease < 0x09000000) {
@@ -307,6 +311,7 @@ static int lbs_adhoc_start(struct lbs_private *priv,
size_t ratesize = 0;
u16 tmpcap = 0;
int ret = 0;
+ DECLARE_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_ASSOC);
@@ -326,7 +331,7 @@ static int lbs_adhoc_start(struct lbs_private *priv,
memcpy(cmd.ssid, assoc_req->ssid, assoc_req->ssid_len);
lbs_deb_join("ADHOC_START: SSID '%s', ssid length %u\n",
- escape_essid(assoc_req->ssid, assoc_req->ssid_len),
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
assoc_req->ssid_len);
cmd.bsstype = CMD_BSS_TYPE_IBSS;
@@ -338,12 +343,12 @@ static int lbs_adhoc_start(struct lbs_private *priv,
WARN_ON(!assoc_req->channel);
/* set Physical parameter set */
- cmd.phyparamset.dsparamset.elementid = MFIE_TYPE_DS_SET;
+ cmd.phyparamset.dsparamset.elementid = WLAN_EID_DS_PARAMS;
cmd.phyparamset.dsparamset.len = 1;
cmd.phyparamset.dsparamset.currentchan = assoc_req->channel;
/* set IBSS parameter set */
- cmd.ssparamset.ibssparamset.elementid = MFIE_TYPE_IBSS_SET;
+ cmd.ssparamset.ibssparamset.elementid = WLAN_EID_IBSS_PARAMS;
cmd.ssparamset.ibssparamset.len = 2;
cmd.ssparamset.ibssparamset.atimwindow = 0;
@@ -427,8 +432,8 @@ static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
{
if (!secinfo->wep_enabled && !secinfo->WPAenabled
&& !secinfo->WPA2enabled
- && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC
- && match_bss->rsn_ie[0] != MFIE_TYPE_RSN
+ && match_bss->wpa_ie[0] != WLAN_EID_GENERIC
+ && match_bss->rsn_ie[0] != WLAN_EID_RSN
&& !(match_bss->capability & WLAN_CAPABILITY_PRIVACY))
return 1;
else
@@ -450,7 +455,7 @@ static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
struct bss_descriptor *match_bss)
{
if (!secinfo->wep_enabled && secinfo->WPAenabled
- && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC)
+ && (match_bss->wpa_ie[0] == WLAN_EID_GENERIC)
/* privacy bit may NOT be set in some APs like LinkSys WRT54G
&& (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
)
@@ -463,7 +468,7 @@ static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
struct bss_descriptor *match_bss)
{
if (!secinfo->wep_enabled && secinfo->WPA2enabled &&
- (match_bss->rsn_ie[0] == MFIE_TYPE_RSN)
+ (match_bss->rsn_ie[0] == WLAN_EID_RSN)
/* privacy bit may NOT be set in some APs like LinkSys WRT54G
(match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
)
@@ -477,8 +482,8 @@ static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
{
if (!secinfo->wep_enabled && !secinfo->WPAenabled
&& !secinfo->WPA2enabled
- && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC)
- && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN)
+ && (match_bss->wpa_ie[0] != WLAN_EID_GENERIC)
+ && (match_bss->rsn_ie[0] != WLAN_EID_RSN)
&& (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
return 1;
else
@@ -694,6 +699,7 @@ static int assoc_helper_essid(struct lbs_private *priv,
int ret = 0;
struct bss_descriptor * bss;
int channel = -1;
+ DECLARE_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_ASSOC);
@@ -705,7 +711,7 @@ static int assoc_helper_essid(struct lbs_private *priv,
channel = assoc_req->channel;
lbs_deb_assoc("SSID '%s' requested\n",
- escape_essid(assoc_req->ssid, assoc_req->ssid_len));
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len));
if (assoc_req->mode == IW_MODE_INFRA) {
lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
assoc_req->ssid_len);
@@ -752,17 +758,15 @@ static int assoc_helper_bssid(struct lbs_private *priv,
{
int ret = 0;
struct bss_descriptor * bss;
- DECLARE_MAC_BUF(mac);
- lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
- print_mac(mac, assoc_req->bssid));
+ lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %pM", assoc_req->bssid);
/* Search for index position in list for requested MAC */
bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
assoc_req->mode);
if (bss == NULL) {
- lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
- "cannot associate.\n", print_mac(mac, assoc_req->bssid));
+ lbs_deb_assoc("ASSOC: WAP: BSSID %pM not found, "
+ "cannot associate.\n", assoc_req->bssid);
goto out;
}
@@ -1208,7 +1212,7 @@ void lbs_association_worker(struct work_struct *work)
struct assoc_request * assoc_req = NULL;
int ret = 0;
int find_any_ssid = 0;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_ASSOC);
@@ -1228,13 +1232,13 @@ void lbs_association_worker(struct work_struct *work)
" chann: %d\n"
" band: %d\n"
" mode: %d\n"
- " BSSID: %s\n"
+ " BSSID: %pM\n"
" secinfo: %s%s%s\n"
" auth_mode: %d\n",
assoc_req->flags,
- escape_essid(assoc_req->ssid, assoc_req->ssid_len),
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
assoc_req->channel, assoc_req->band, assoc_req->mode,
- print_mac(mac, assoc_req->bssid),
+ assoc_req->bssid,
assoc_req->secinfo.WPAenabled ? " WPA" : "",
assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
assoc_req->secinfo.wep_enabled ? " WEP" : "",
@@ -1357,8 +1361,8 @@ void lbs_association_worker(struct work_struct *work)
}
if (success) {
- lbs_deb_assoc("associated to %s\n",
- print_mac(mac, priv->curbssparams.bssid));
+ lbs_deb_assoc("associated to %pM\n",
+ priv->curbssparams.bssid);
lbs_prepare_and_send_command(priv,
CMD_802_11_RSSI,
0, CMD_OPTION_WAITFORRSP, 0, NULL);
@@ -1478,7 +1482,6 @@ int lbs_cmd_80211_authenticate(struct lbs_private *priv,
struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth;
int ret = -1;
u8 *bssid = pdata_buf;
- DECLARE_MAC_BUF(mac);
lbs_deb_enter(LBS_DEB_JOIN);
@@ -1505,8 +1508,8 @@ int lbs_cmd_80211_authenticate(struct lbs_private *priv,
memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
- lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
- print_mac(mac, bssid), pauthenticate->authtype);
+ lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n",
+ bssid, pauthenticate->authtype);
ret = 0;
out:
@@ -1770,7 +1773,7 @@ static int lbs_adhoc_post(struct lbs_private *priv, struct cmd_header *resp)
struct cmd_ds_802_11_ad_hoc_result *adhoc_resp;
union iwreq_data wrqu;
struct bss_descriptor *bss;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_JOIN);
@@ -1819,9 +1822,9 @@ static int lbs_adhoc_post(struct lbs_private *priv, struct cmd_header *resp)
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
- lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %s, channel %d\n",
- escape_essid(bss->ssid, bss->ssid_len),
- print_mac(mac, priv->curbssparams.bssid),
+ lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n",
+ print_ssid(ssid, bss->ssid, bss->ssid_len),
+ priv->curbssparams.bssid,
priv->curbssparams.channel);
done:
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 8265c7d25edc..639dd02d3d31 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -4,7 +4,7 @@
*/
#include <net/iw_handler.h>
-#include <net/ieee80211.h>
+#include <net/lib80211.h>
#include <linux/kfifo.h>
#include "host.h"
#include "hostcmd.h"
@@ -87,7 +87,6 @@ int lbs_update_hw_spec(struct lbs_private *priv)
struct cmd_ds_get_hw_spec cmd;
int ret = -1;
u32 i;
- DECLARE_MAC_BUF(mac);
lbs_deb_enter(LBS_DEB_CMD);
@@ -110,8 +109,8 @@ int lbs_update_hw_spec(struct lbs_private *priv)
* CF card firmware 5.0.16p0: cap 0x00000303
* USB dongle firmware 5.110.17p2: cap 0x00000303
*/
- lbs_pr_info("%s, fw %u.%u.%up%u, cap 0x%08x\n",
- print_mac(mac, cmd.permanentaddr),
+ lbs_pr_info("%pM, fw %u.%u.%up%u, cap 0x%08x\n",
+ cmd.permanentaddr,
priv->fwrelease >> 24 & 0xff,
priv->fwrelease >> 16 & 0xff,
priv->fwrelease >> 8 & 0xff,
@@ -160,7 +159,8 @@ out:
return ret;
}
-int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria)
+int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
+ struct wol_config *p_wol_config)
{
struct cmd_ds_host_sleep cmd_config;
int ret;
@@ -170,10 +170,21 @@ int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria)
cmd_config.gpio = priv->wol_gpio;
cmd_config.gap = priv->wol_gap;
+ if (p_wol_config != NULL)
+ memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config,
+ sizeof(struct wol_config));
+ else
+ cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
+
ret = lbs_cmd_with_response(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config);
if (!ret) {
- lbs_deb_cmd("Set WOL criteria to %x\n", criteria);
- priv->wol_criteria = criteria;
+ if (criteria) {
+ lbs_deb_cmd("Set WOL criteria to %x\n", criteria);
+ priv->wol_criteria = criteria;
+ } else
+ memcpy((uint8_t *) p_wol_config,
+ (uint8_t *)&cmd_config.wol_conf,
+ sizeof(struct wol_config));
} else {
lbs_pr_info("HOST_SLEEP_CFG failed %d\n", ret);
}
@@ -1063,6 +1074,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
{
struct cmd_ds_mesh_config cmd;
struct mrvl_meshie *ie;
+ DECLARE_SSID_BUF(ssid);
memset(&cmd, 0, sizeof(cmd));
cmd.channel = cpu_to_le16(chan);
@@ -1070,7 +1082,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
switch (action) {
case CMD_ACT_MESH_CONFIG_START:
- ie->hdr.id = MFIE_TYPE_GENERIC;
+ ie->id = WLAN_EID_GENERIC;
ie->val.oui[0] = 0x00;
ie->val.oui[1] = 0x50;
ie->val.oui[2] = 0x43;
@@ -1082,7 +1094,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
ie->val.mesh_id_len = priv->mesh_ssid_len;
memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
- ie->hdr.len = sizeof(struct mrvl_meshie_val) -
+ ie->len = sizeof(struct mrvl_meshie_val) -
IW_ESSID_MAX_SIZE + priv->mesh_ssid_len;
cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
break;
@@ -1093,7 +1105,7 @@ int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
}
lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
action, priv->mesh_tlv, chan,
- escape_essid(priv->mesh_ssid, priv->mesh_ssid_len));
+ print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
}
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 36be4c9703e0..392e578ca095 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -56,7 +56,8 @@ int lbs_mesh_config_send(struct lbs_private *priv,
uint16_t action, uint16_t type);
int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
-int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria);
+int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
+ struct wol_config *p_wol_config);
int lbs_suspend(struct lbs_private *priv);
void lbs_resume(struct lbs_private *priv);
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 0aa0ce3b2c42..ec4efd7ff3c8 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -5,6 +5,7 @@
#include <linux/mm.h>
#include <linux/string.h>
#include <net/iw_handler.h>
+#include <net/lib80211.h>
#include "dev.h"
#include "decl.h"
@@ -65,7 +66,7 @@ static ssize_t lbs_getscantable(struct file *file, char __user *userbuf,
int numscansdone = 0, res;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
struct bss_descriptor * iter_bss;
pos += snprintf(buf+pos, len-pos,
@@ -77,17 +78,17 @@ static ssize_t lbs_getscantable(struct file *file, char __user *userbuf,
u16 privacy = (iter_bss->capability & WLAN_CAPABILITY_PRIVACY);
u16 spectrum_mgmt = (iter_bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT);
- pos += snprintf(buf+pos, len-pos,
- "%02u| %03d | %04d | %s |",
+ pos += snprintf(buf+pos, len-pos, "%02u| %03d | %04d | %pM |",
numscansdone, iter_bss->channel, iter_bss->rssi,
- print_mac(mac, iter_bss->bssid));
+ iter_bss->bssid);
pos += snprintf(buf+pos, len-pos, " %04x-", iter_bss->capability);
pos += snprintf(buf+pos, len-pos, "%c%c%c |",
ibss ? 'A' : 'I', privacy ? 'P' : ' ',
spectrum_mgmt ? 'S' : ' ');
pos += snprintf(buf+pos, len-pos, " %04d |", SCAN_RSSI(iter_bss->rssi));
pos += snprintf(buf+pos, len-pos, " %s\n",
- escape_essid(iter_bss->ssid, iter_bss->ssid_len));
+ print_ssid(ssid, iter_bss->ssid,
+ iter_bss->ssid_len));
numscansdone++;
}
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index 1a8888cceadc..0b84bdca0726 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -74,8 +74,4 @@ void lbs_host_to_card_done(struct lbs_private *priv);
int lbs_update_channel(struct lbs_private *priv);
-#ifndef CONFIG_IEEE80211
-const char *escape_essid(const char *essid, u8 essid_len);
-#endif
-
#endif
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
index 076a636e8f62..c364e4c01d1b 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/libertas/defs.h
@@ -79,7 +79,7 @@ do { if ((lbs_debug & (grp)) == (grp)) \
#define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args)
#define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args)
#define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args)
-#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, (dev)->bus_id, ##args)
+#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args)
#define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args)
#define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args)
#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args)
@@ -149,6 +149,18 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
#define EHS_WAKE_ON_MAC_EVENT 0x0004
#define EHS_WAKE_ON_MULTICAST_DATA 0x0008
#define EHS_REMOVE_WAKEUP 0xFFFFFFFF
+/* Wake rules for Host_Sleep_CFG command */
+#define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00
+#define WOL_RULE_NET_TYPE_MESH 0x10
+#define WOL_RULE_ADDR_TYPE_BCAST 0x01
+#define WOL_RULE_ADDR_TYPE_MCAST 0x08
+#define WOL_RULE_ADDR_TYPE_UCAST 0x02
+#define WOL_RULE_OP_AND 0x01
+#define WOL_RULE_OP_OR 0x02
+#define WOL_RULE_OP_INVALID 0xFF
+#define WOL_RESULT_VALID_CMD 0
+#define WOL_RESULT_NOSPC_ERR 1
+#define WOL_RESULT_EEXIST_ERR 2
/** Misc constants */
/* This section defines 802.11 specific contants */
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index f6f3753da303..dd682c4cfde8 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -10,7 +10,6 @@
#include <linux/wireless.h>
#include <linux/ethtool.h>
#include <linux/debugfs.h>
-#include <net/ieee80211.h>
#include "defs.h"
#include "hostcmd.h"
@@ -278,6 +277,12 @@ struct lbs_private {
struct enc_key wpa_mcast_key;
struct enc_key wpa_unicast_key;
+/*
+ * In theory, the IE is limited to the IE length, 255,
+ * but in practice 64 bytes are enough.
+ */
+#define MAX_WPA_IE_LEN 64
+
/** WPA Information Elements*/
u8 wpa_ie[MAX_WPA_IE_LEN];
u8 wpa_ie_len;
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index 688d60de55cb..61d2f50470c8 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -23,7 +23,7 @@ static const char * mesh_stat_strings[]= {
static void lbs_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
snprintf(info->fw_version, 32, "%u.%u.%u.p%u",
priv->fwrelease >> 24 & 0xff,
@@ -47,7 +47,7 @@ static int lbs_ethtool_get_eeprom_len(struct net_device *dev)
static int lbs_ethtool_get_eeprom(struct net_device *dev,
struct ethtool_eeprom *eeprom, u8 * bytes)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct cmd_ds_802_11_eeprom_access cmd;
int ret;
@@ -76,7 +76,7 @@ out:
static void lbs_ethtool_get_stats(struct net_device *dev,
struct ethtool_stats *stats, uint64_t *data)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct cmd_ds_mesh_access mesh_access;
int ret;
@@ -113,7 +113,7 @@ static void lbs_ethtool_get_stats(struct net_device *dev,
static int lbs_ethtool_get_sset_count(struct net_device *dev, int sset)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
return MESH_STATS_NUM;
@@ -143,7 +143,7 @@ static void lbs_ethtool_get_strings(struct net_device *dev,
static void lbs_ethtool_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
if (priv->wol_criteria == 0xffffffff) {
/* Interface driver didn't configure wake */
@@ -166,7 +166,7 @@ static void lbs_ethtool_get_wol(struct net_device *dev,
static int lbs_ethtool_set_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
uint32_t criteria = 0;
if (priv->wol_criteria == 0xffffffff && wol->wolopts)
@@ -180,7 +180,7 @@ static int lbs_ethtool_set_wol(struct net_device *dev,
if (wol->wolopts & WAKE_BCAST) criteria |= EHS_WAKE_ON_BROADCAST_DATA;
if (wol->wolopts & WAKE_PHY) criteria |= EHS_WAKE_ON_MAC_EVENT;
- return lbs_host_sleep_cfg(priv, criteria);
+ return lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL);
}
struct ethtool_ops lbs_ethtool_ops = {
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index 5004d7679c02..277ff1975bde 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -220,6 +220,14 @@ enum cmd_fwt_access_opts {
CMD_ACT_FWT_ACCESS_TIME,
};
+/* Define action or option for CMD_802_11_HOST_SLEEP_CFG */
+enum cmd_wol_cfg_opts {
+ CMD_ACT_ACTION_NONE = 0,
+ CMD_ACT_SET_WOL_RULE,
+ CMD_ACT_GET_WOL_RULE,
+ CMD_ACT_RESET_WOL_RULE,
+};
+
/* Define action or option for CMD_MESH_ACCESS */
enum cmd_mesh_access_opts {
CMD_ACT_MESH_GET_TTL = 1,
@@ -237,6 +245,7 @@ enum cmd_mesh_access_opts {
CMD_ACT_MESH_GET_ROUTE_EXP,
CMD_ACT_MESH_SET_AUTOSTART_ENABLED,
CMD_ACT_MESH_GET_AUTOSTART_ENABLED,
+ CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17,
};
/* Define actions and types for CMD_MESH_CONFIG */
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
index d9f9a12a739e..e173b1b46c23 100644
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -580,13 +580,37 @@ struct MrvlIEtype_keyParamSet {
u8 key[32];
};
+#define MAX_WOL_RULES 16
+
+struct host_wol_rule {
+ uint8_t rule_no;
+ uint8_t rule_ops;
+ __le16 sig_offset;
+ __le16 sig_length;
+ __le16 reserve;
+ __be32 sig_mask;
+ __be32 signature;
+};
+
+struct wol_config {
+ uint8_t action;
+ uint8_t pattern;
+ uint8_t no_rules_in_cmd;
+ uint8_t result;
+ struct host_wol_rule rule[MAX_WOL_RULES];
+};
+
+
struct cmd_ds_host_sleep {
struct cmd_header hdr;
__le32 criteria;
uint8_t gpio;
- uint8_t gap;
+ uint16_t gap;
+ struct wol_config wol_conf;
} __attribute__ ((packed));
+
+
struct cmd_ds_802_11_key_material {
struct cmd_header hdr;
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index b54e2ea8346b..4519d7314f47 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -26,6 +26,7 @@
* if_sdio_card_to_host() to pad the data.
*/
+#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/firmware.h>
#include <linux/netdevice.h>
@@ -581,7 +582,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
chunk_size, (chunk_size + 31) / 32 * 32);
*/
ret = sdio_writesb(card->func, card->ioport,
- chunk_buffer, (chunk_size + 31) / 32 * 32);
+ chunk_buffer, roundup(chunk_size, 32));
if (ret)
goto release;
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index cafbccb74143..2fc637ad85c7 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -59,7 +59,7 @@ static int if_usb_reset_device(struct if_usb_card *cardp);
static ssize_t if_usb_firmware_set(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct if_usb_card *cardp = priv->card;
char fwname[FIRMWARE_NAME_MAX];
int ret;
@@ -86,7 +86,7 @@ static DEVICE_ATTR(lbs_flash_fw, 0200, NULL, if_usb_firmware_set);
static ssize_t if_usb_boot2_set(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct if_usb_card *cardp = priv->card;
char fwname[FIRMWARE_NAME_MAX];
int ret;
@@ -178,7 +178,8 @@ static void if_usb_setup_firmware(struct lbs_private *priv)
priv->wol_gpio = 2; /* Wake via GPIO2... */
priv->wol_gap = 20; /* ... after 20ms */
- lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA);
+ lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA,
+ (struct wol_config *) NULL);
wake_method.hdr.size = cpu_to_le16(sizeof(wake_method));
wake_method.action = cpu_to_le16(CMD_ACT_GET);
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 73dc8c72402a..3dba83679444 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -12,9 +12,8 @@
#include <linux/kthread.h>
#include <linux/kfifo.h>
#include <linux/stddef.h>
-
+#include <linux/ieee80211.h>
#include <net/iw_handler.h>
-#include <net/ieee80211.h>
#include "host.h"
#include "decl.h"
@@ -223,7 +222,7 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
static ssize_t lbs_anycast_get(struct device *dev,
struct device_attribute *attr, char * buf)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_access mesh_access;
int ret;
@@ -242,7 +241,7 @@ static ssize_t lbs_anycast_get(struct device *dev,
static ssize_t lbs_anycast_set(struct device *dev,
struct device_attribute *attr, const char * buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_access mesh_access;
uint32_t datum;
int ret;
@@ -258,6 +257,58 @@ static ssize_t lbs_anycast_set(struct device *dev,
return strlen(buf);
}
+/**
+ * @brief Get function for sysfs attribute prb_rsp_limit
+ */
+static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ u32 retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ retry_limit = le32_to_cpu(mesh_access.data[1]);
+ return snprintf(buf, 10, "%d\n", retry_limit);
+}
+
+/**
+ * @brief Set function for sysfs attribute prb_rsp_limit
+ */
+static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ unsigned long retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
+
+ if (!strict_strtoul(buf, 10, &retry_limit))
+ return -ENOTSUPP;
+ if (retry_limit > 15)
+ return -ENOTSUPP;
+
+ mesh_access.data[1] = cpu_to_le32(retry_limit);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
static int lbs_add_rtap(struct lbs_private *priv);
static void lbs_remove_rtap(struct lbs_private *priv);
static int lbs_add_mesh(struct lbs_private *priv);
@@ -270,7 +321,7 @@ static void lbs_remove_mesh(struct lbs_private *priv);
static ssize_t lbs_rtap_get(struct device *dev,
struct device_attribute *attr, char * buf)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
return snprintf(buf, 5, "0x%X\n", priv->monitormode);
}
@@ -281,7 +332,7 @@ static ssize_t lbs_rtap_set(struct device *dev,
struct device_attribute *attr, const char * buf, size_t count)
{
int monitor_mode;
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
sscanf(buf, "%x", &monitor_mode);
if (monitor_mode) {
@@ -332,7 +383,7 @@ static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set );
static ssize_t lbs_mesh_get(struct device *dev,
struct device_attribute *attr, char * buf)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
}
@@ -342,7 +393,7 @@ static ssize_t lbs_mesh_get(struct device *dev,
static ssize_t lbs_mesh_set(struct device *dev,
struct device_attribute *attr, const char * buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
int enable;
int ret, action = CMD_ACT_MESH_CONFIG_STOP;
@@ -376,8 +427,16 @@ static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
*/
static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
+/**
+ * prb_rsp_limit attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
+ */
+static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
+ lbs_prb_rsp_limit_set);
+
static struct attribute *lbs_mesh_sysfs_entries[] = {
&dev_attr_anycast_mask.attr,
+ &dev_attr_prb_rsp_limit.attr,
NULL,
};
@@ -393,7 +452,7 @@ static struct attribute_group lbs_mesh_attr_group = {
*/
static int lbs_dev_open(struct net_device *dev)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv ;
+ struct lbs_private *priv = netdev_priv(dev) ;
int ret = 0;
lbs_deb_enter(LBS_DEB_NET);
@@ -435,7 +494,7 @@ static int lbs_dev_open(struct net_device *dev)
*/
static int lbs_mesh_stop(struct net_device *dev)
{
- struct lbs_private *priv = (struct lbs_private *) (dev->priv);
+ struct lbs_private *priv = dev->ml_priv;
lbs_deb_enter(LBS_DEB_MESH);
spin_lock_irq(&priv->driver_lock);
@@ -462,7 +521,7 @@ static int lbs_mesh_stop(struct net_device *dev)
*/
static int lbs_eth_stop(struct net_device *dev)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_NET);
@@ -479,7 +538,7 @@ static int lbs_eth_stop(struct net_device *dev)
static void lbs_tx_timeout(struct net_device *dev)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_TX);
@@ -531,7 +590,7 @@ EXPORT_SYMBOL_GPL(lbs_host_to_card_done);
*/
static struct net_device_stats *lbs_get_stats(struct net_device *dev)
{
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_NET);
return &priv->stats;
@@ -540,7 +599,7 @@ static struct net_device_stats *lbs_get_stats(struct net_device *dev)
static int lbs_set_mac_address(struct net_device *dev, void *addr)
{
int ret = 0;
- struct lbs_private *priv = (struct lbs_private *) dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct sockaddr *phwaddr = addr;
struct cmd_ds_802_11_mac_address cmd;
@@ -588,7 +647,6 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
{
int i = nr_addrs;
struct dev_mc_list *mc_list;
- DECLARE_MAC_BUF(mac);
if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST))
return nr_addrs;
@@ -596,16 +654,16 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
netif_addr_lock_bh(dev);
for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) {
- lbs_deb_net("mcast address %s:%s skipped\n", dev->name,
- print_mac(mac, mc_list->dmi_addr));
+ lbs_deb_net("mcast address %s:%pM skipped\n", dev->name,
+ mc_list->dmi_addr);
continue;
}
if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE)
break;
memcpy(&cmd->maclist[6*i], mc_list->dmi_addr, ETH_ALEN);
- lbs_deb_net("mcast address %s:%s added to filter\n", dev->name,
- print_mac(mac, mc_list->dmi_addr));
+ lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name,
+ mc_list->dmi_addr);
i++;
}
netif_addr_unlock_bh(dev);
@@ -674,7 +732,7 @@ static void lbs_set_mcast_worker(struct work_struct *work)
static void lbs_set_multicast_list(struct net_device *dev)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
schedule_work(&priv->mcast_work);
}
@@ -690,7 +748,7 @@ static void lbs_set_multicast_list(struct net_device *dev)
static int lbs_thread(void *data)
{
struct net_device *dev = data;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
wait_queue_t wait;
lbs_deb_enter(LBS_DEB_THREAD);
@@ -1125,7 +1183,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
lbs_pr_err("init ethX device failed\n");
goto done;
}
- priv = dev->priv;
+ priv = netdev_priv(dev);
if (lbs_init_adapter(priv)) {
lbs_pr_err("failed to initialize adapter structure.\n");
@@ -1378,7 +1436,7 @@ static int lbs_add_mesh(struct lbs_private *priv)
ret = -ENOMEM;
goto done;
}
- mesh_dev->priv = priv;
+ mesh_dev->ml_priv = priv;
priv->mesh_dev = mesh_dev;
mesh_dev->open = lbs_dev_open;
@@ -1591,7 +1649,7 @@ static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
static struct net_device_stats *lbs_rtap_get_stats(struct net_device *dev)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = dev->ml_priv;
lbs_deb_enter(LBS_DEB_NET);
return &priv->stats;
}
@@ -1632,7 +1690,7 @@ static int lbs_add_rtap(struct lbs_private *priv)
rtap_dev->stop = lbs_rtap_stop;
rtap_dev->get_stats = lbs_rtap_get_stats;
rtap_dev->hard_start_xmit = lbs_rtap_hard_start_xmit;
- rtap_dev->priv = priv;
+ rtap_dev->ml_priv = priv;
SET_NETDEV_DEV(rtap_dev, priv->dev->dev.parent);
ret = register_netdev(rtap_dev);
@@ -1647,33 +1705,6 @@ out:
return ret;
}
-#ifndef CONFIG_IEEE80211
-const char *escape_essid(const char *essid, u8 essid_len)
-{
- static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- const char *s = essid;
- char *d = escaped;
-
- if (ieee80211_is_empty_essid(essid, essid_len)) {
- memcpy(escaped, "<hidden>", sizeof("<hidden>"));
- return escaped;
- }
-
- essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
- while (essid_len--) {
- if (*s == '\0') {
- *d++ = '\\';
- *d++ = '0';
- s++;
- } else {
- *d++ = *s++;
- }
- }
- *d = '\0';
- return escaped;
-}
-#endif
-
module_init(lbs_init_module);
module_exit(lbs_exit_module);
diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c
index 3309a9c3cfef..d42b7a5a1b3f 100644
--- a/drivers/net/wireless/libertas/persistcfg.c
+++ b/drivers/net/wireless/libertas/persistcfg.c
@@ -18,7 +18,7 @@
static int mesh_get_default_parameters(struct device *dev,
struct mrvl_mesh_defaults *defs)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_config cmd;
int ret;
@@ -57,7 +57,7 @@ static ssize_t bootflag_get(struct device *dev,
static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_config cmd;
uint32_t datum;
int ret;
@@ -100,7 +100,7 @@ static ssize_t boottime_get(struct device *dev,
static ssize_t boottime_set(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_config cmd;
uint32_t datum;
int ret;
@@ -152,7 +152,7 @@ static ssize_t channel_get(struct device *dev,
static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
struct cmd_ds_mesh_config cmd;
uint32_t datum;
int ret;
@@ -210,7 +210,7 @@ static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
struct cmd_ds_mesh_config cmd;
struct mrvl_mesh_defaults defs;
struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
int len;
int ret;
@@ -233,7 +233,7 @@ static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
/* SSID len */
ie->val.mesh_id_len = len;
/* IE len */
- ie->hdr.len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len;
+ ie->len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len;
ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
CMD_TYPE_MESH_SET_MESH_IE);
@@ -269,7 +269,7 @@ static ssize_t protocol_id_set(struct device *dev,
struct cmd_ds_mesh_config cmd;
struct mrvl_mesh_defaults defs;
struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
uint32_t datum;
int ret;
@@ -323,7 +323,7 @@ static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
struct cmd_ds_mesh_config cmd;
struct mrvl_mesh_defaults defs;
struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
uint32_t datum;
int ret;
@@ -377,7 +377,7 @@ static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
struct cmd_ds_mesh_config cmd;
struct mrvl_mesh_defaults defs;
struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->priv;
+ struct lbs_private *priv = netdev_priv(to_net_dev(dev));
uint32_t datum;
int ret;
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h
index 5d118f40cfbc..f8eb9097ff0a 100644
--- a/drivers/net/wireless/libertas/radiotap.h
+++ b/drivers/net/wireless/libertas/radiotap.h
@@ -6,9 +6,6 @@ struct tx_radiotap_hdr {
u8 txpower;
u8 rts_retries;
u8 data_retries;
-#if 0
- u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12];
-#endif
} __attribute__ ((packed));
#define TX_RADIOTAP_PRESENT ( \
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index 22c4c6110521..57f6c12cda20 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -4,8 +4,11 @@
* IOCTL handlers as well as command preperation and response routines
* for sending scan commands to the firmware.
*/
+#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include <asm/unaligned.h>
+#include <net/lib80211.h>
#include "host.h"
#include "decl.h"
@@ -52,6 +55,8 @@
//! Scan time specified in the channel TLV for each channel for active scans
#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100
+#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
+
static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
struct cmd_header *resp);
@@ -359,7 +364,7 @@ int lbs_scan_networks(struct lbs_private *priv, int full_scan)
#ifdef CONFIG_LIBERTAS_DEBUG
struct bss_descriptor *iter;
int i = 0;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
#endif
lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d", full_scan);
@@ -451,9 +456,9 @@ int lbs_scan_networks(struct lbs_private *priv, int full_scan)
mutex_lock(&priv->lock);
lbs_deb_scan("scan table:\n");
list_for_each_entry(iter, &priv->network_list, list)
- lbs_deb_scan("%02d: BSSID %s, RSSI %d, SSID '%s'\n",
- i++, print_mac(mac, iter->bssid), iter->rssi,
- escape_essid(iter->ssid, iter->ssid_len));
+ lbs_deb_scan("%02d: BSSID %pM, RSSI %d, SSID '%s'\n",
+ i++, iter->bssid, iter->rssi,
+ print_ssid(ssid, iter->ssid, iter->ssid_len));
mutex_unlock(&priv->lock);
#endif
@@ -512,7 +517,7 @@ static int lbs_process_bss(struct bss_descriptor *bss,
struct ieeetypes_dsparamset *pDS;
struct ieeetypes_cfparamset *pCF;
struct ieeetypes_ibssparamset *pibss;
- DECLARE_MAC_BUF(mac);
+ DECLARE_SSID_BUF(ssid);
struct ieeetypes_countryinfoset *pcountryinfo;
uint8_t *pos, *end, *p;
uint8_t n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;
@@ -544,7 +549,7 @@ static int lbs_process_bss(struct bss_descriptor *bss,
*bytesleft -= beaconsize;
memcpy(bss->bssid, pos, ETH_ALEN);
- lbs_deb_scan("process_bss: BSSID %s\n", print_mac(mac, bss->bssid));
+ lbs_deb_scan("process_bss: BSSID %pM\n", bss->bssid);
pos += ETH_ALEN;
if ((end - pos) < 12) {
@@ -588,38 +593,36 @@ static int lbs_process_bss(struct bss_descriptor *bss,
/* process variable IE */
while (pos <= end - 2) {
- struct ieee80211_info_element * elem = (void *)pos;
-
- if (pos + elem->len > end) {
+ if (pos + pos[1] > end) {
lbs_deb_scan("process_bss: error in processing IE, "
"bytes left < IE length\n");
break;
}
- switch (elem->id) {
- case MFIE_TYPE_SSID:
- bss->ssid_len = min_t(int, 32, elem->len);
- memcpy(bss->ssid, elem->data, bss->ssid_len);
+ switch (pos[0]) {
+ case WLAN_EID_SSID:
+ bss->ssid_len = min_t(int, IEEE80211_MAX_SSID_LEN, pos[1]);
+ memcpy(bss->ssid, pos + 2, bss->ssid_len);
lbs_deb_scan("got SSID IE: '%s', len %u\n",
- escape_essid(bss->ssid, bss->ssid_len),
+ print_ssid(ssid, bss->ssid, bss->ssid_len),
bss->ssid_len);
break;
- case MFIE_TYPE_RATES:
- n_basic_rates = min_t(uint8_t, MAX_RATES, elem->len);
- memcpy(bss->rates, elem->data, n_basic_rates);
+ case WLAN_EID_SUPP_RATES:
+ n_basic_rates = min_t(uint8_t, MAX_RATES, pos[1]);
+ memcpy(bss->rates, pos + 2, n_basic_rates);
got_basic_rates = 1;
lbs_deb_scan("got RATES IE\n");
break;
- case MFIE_TYPE_FH_SET:
+ case WLAN_EID_FH_PARAMS:
pFH = (struct ieeetypes_fhparamset *) pos;
memmove(&bss->phyparamset.fhparamset, pFH,
sizeof(struct ieeetypes_fhparamset));
lbs_deb_scan("got FH IE\n");
break;
- case MFIE_TYPE_DS_SET:
+ case WLAN_EID_DS_PARAMS:
pDS = (struct ieeetypes_dsparamset *) pos;
bss->channel = pDS->currentchan;
memcpy(&bss->phyparamset.dsparamset, pDS,
@@ -627,14 +630,14 @@ static int lbs_process_bss(struct bss_descriptor *bss,
lbs_deb_scan("got DS IE, channel %d\n", bss->channel);
break;
- case MFIE_TYPE_CF_SET:
+ case WLAN_EID_CF_PARAMS:
pCF = (struct ieeetypes_cfparamset *) pos;
memcpy(&bss->ssparamset.cfparamset, pCF,
sizeof(struct ieeetypes_cfparamset));
lbs_deb_scan("got CF IE\n");
break;
- case MFIE_TYPE_IBSS_SET:
+ case WLAN_EID_IBSS_PARAMS:
pibss = (struct ieeetypes_ibssparamset *) pos;
bss->atimwindow = le16_to_cpu(pibss->atimwindow);
memmove(&bss->ssparamset.ibssparamset, pibss,
@@ -642,7 +645,7 @@ static int lbs_process_bss(struct bss_descriptor *bss,
lbs_deb_scan("got IBSS IE\n");
break;
- case MFIE_TYPE_COUNTRY:
+ case WLAN_EID_COUNTRY:
pcountryinfo = (struct ieeetypes_countryinfoset *) pos;
lbs_deb_scan("got COUNTRY IE\n");
if (pcountryinfo->len < sizeof(pcountryinfo->countrycode)
@@ -659,7 +662,7 @@ static int lbs_process_bss(struct bss_descriptor *bss,
(int) (pcountryinfo->len + 2));
break;
- case MFIE_TYPE_RATES_EX:
+ case WLAN_EID_EXT_SUPP_RATES:
/* only process extended supported rate if data rate is
* already found. Data rate IE should come before
* extended supported rate IE
@@ -670,50 +673,51 @@ static int lbs_process_bss(struct bss_descriptor *bss,
break;
}
- n_ex_rates = elem->len;
+ n_ex_rates = pos[1];
if (n_basic_rates + n_ex_rates > MAX_RATES)
n_ex_rates = MAX_RATES - n_basic_rates;
p = bss->rates + n_basic_rates;
- memcpy(p, elem->data, n_ex_rates);
+ memcpy(p, pos + 2, n_ex_rates);
break;
- case MFIE_TYPE_GENERIC:
- if (elem->len >= 4 &&
- elem->data[0] == 0x00 && elem->data[1] == 0x50 &&
- elem->data[2] == 0xf2 && elem->data[3] == 0x01) {
- bss->wpa_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN);
- memcpy(bss->wpa_ie, elem, bss->wpa_ie_len);
+ case WLAN_EID_GENERIC:
+ if (pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 &&
+ pos[4] == 0xf2 && pos[5] == 0x01) {
+ bss->wpa_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);
+ memcpy(bss->wpa_ie, pos, bss->wpa_ie_len);
lbs_deb_scan("got WPA IE\n");
- lbs_deb_hex(LBS_DEB_SCAN, "WPA IE", bss->wpa_ie, elem->len);
- } else if (elem->len >= MARVELL_MESH_IE_LENGTH &&
- elem->data[0] == 0x00 && elem->data[1] == 0x50 &&
- elem->data[2] == 0x43 && elem->data[3] == 0x04) {
+ lbs_deb_hex(LBS_DEB_SCAN, "WPA IE", bss->wpa_ie,
+ bss->wpa_ie_len);
+ } else if (pos[1] >= MARVELL_MESH_IE_LENGTH &&
+ pos[2] == 0x00 && pos[3] == 0x50 &&
+ pos[4] == 0x43 && pos[4] == 0x04) {
lbs_deb_scan("got mesh IE\n");
bss->mesh = 1;
} else {
lbs_deb_scan("got generic IE: %02x:%02x:%02x:%02x, len %d\n",
- elem->data[0], elem->data[1],
- elem->data[2], elem->data[3],
- elem->len);
+ pos[2], pos[3],
+ pos[4], pos[5],
+ pos[1]);
}
break;
- case MFIE_TYPE_RSN:
+ case WLAN_EID_RSN:
lbs_deb_scan("got RSN IE\n");
- bss->rsn_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN);
- memcpy(bss->rsn_ie, elem, bss->rsn_ie_len);
+ bss->rsn_ie_len = min(pos[1] + 2, MAX_WPA_IE_LEN);
+ memcpy(bss->rsn_ie, pos, bss->rsn_ie_len);
lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE",
- bss->rsn_ie, elem->len);
+ bss->rsn_ie, bss->rsn_ie_len);
break;
default:
lbs_deb_scan("got IE 0x%04x, len %d\n",
- elem->id, elem->len);
+ pos[0], pos[1]);
break;
}
- pos += elem->len + 2;
+ pos += pos[1] + 2;
}
/* Timestamp */
@@ -741,10 +745,11 @@ done:
int lbs_send_specific_ssid_scan(struct lbs_private *priv, uint8_t *ssid,
uint8_t ssid_len)
{
+ DECLARE_SSID_BUF(ssid_buf);
int ret = 0;
lbs_deb_enter_args(LBS_DEB_SCAN, "SSID '%s'\n",
- escape_essid(ssid, ssid_len));
+ print_ssid(ssid_buf, ssid, ssid_len));
if (!ssid_len)
goto out;
@@ -939,7 +944,8 @@ out:
int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ DECLARE_SSID_BUF(ssid);
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -968,7 +974,7 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
priv->scan_ssid_len = req->essid_len;
memcpy(priv->scan_ssid, req->essid, priv->scan_ssid_len);
lbs_deb_wext("set_scan, essid '%s'\n",
- escape_essid(priv->scan_ssid, priv->scan_ssid_len));
+ print_ssid(ssid, priv->scan_ssid, priv->scan_ssid_len));
} else {
priv->scan_ssid_len = 0;
}
@@ -1002,7 +1008,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
#define SCAN_ITEM_SIZE 128
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int err = 0;
char *ev = extra;
char *stop = ev + dwrq->length;
@@ -1151,7 +1157,6 @@ static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
struct bss_descriptor new;
struct bss_descriptor *found = NULL;
struct bss_descriptor *oldest = NULL;
- DECLARE_MAC_BUF(mac);
/* Process the data fields and IEs returned for this BSS */
memset(&new, 0, sizeof (struct bss_descriptor));
@@ -1190,7 +1195,7 @@ static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
continue;
}
- lbs_deb_scan("SCAN_RESP: BSSID %s\n", print_mac(mac, new.bssid));
+ lbs_deb_scan("SCAN_RESP: BSSID %pM\n", new.bssid);
/* Copy the locally created newbssentry to the scan table */
memcpy(found, &new, offsetof(struct bss_descriptor, list));
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h
index 9e07b0464a8e..fab7d5d097fc 100644
--- a/drivers/net/wireless/libertas/scan.h
+++ b/drivers/net/wireless/libertas/scan.h
@@ -7,6 +7,10 @@
#ifndef _LBS_SCAN_H
#define _LBS_SCAN_H
+#include <net/iw_handler.h>
+
+#define MAX_NETWORK_COUNT 128
+
/**
* @brief Maximum number of channels that can be sent in a setuserscan ioctl
*/
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index a4972fed2941..dac462641170 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -60,7 +60,7 @@ static u32 convert_radiotap_rate_to_mv(u8 rate)
int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct txpd *txpd;
char *p802x_hdr;
uint16_t pkt_len;
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h
index e0c2599da92f..fb7a2d1a2525 100644
--- a/drivers/net/wireless/libertas/types.h
+++ b/drivers/net/wireless/libertas/types.h
@@ -7,7 +7,6 @@
#include <linux/if_ether.h>
#include <asm/byteorder.h>
#include <linux/wireless.h>
-#include <net/ieee80211.h>
struct ieeetypes_cfparamset {
u8 elementid;
@@ -258,7 +257,7 @@ struct mrvlietypes_ledbhv {
* Note that the len member of the ieee80211_info_element varies depending on
* the mesh_id_len */
struct mrvl_meshie_val {
- uint8_t oui[P80211_OUI_LEN];
+ uint8_t oui[3];
uint8_t type;
uint8_t subtype;
uint8_t version;
@@ -270,7 +269,7 @@ struct mrvl_meshie_val {
} __attribute__ ((packed));
struct mrvl_meshie {
- struct ieee80211_info_element hdr;
+ u8 id, len;
struct mrvl_meshie_val val;
} __attribute__ ((packed));
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index 82c3e5a50ea6..c6102e08179e 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -8,7 +8,7 @@
#include <linux/wireless.h>
#include <linux/bitops.h>
-#include <net/ieee80211.h>
+#include <net/lib80211.h>
#include <net/iw_handler.h>
#include "host.h"
@@ -163,7 +163,7 @@ static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct chan_freq_power *cfp;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -189,7 +189,7 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -207,7 +207,7 @@ static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -231,7 +231,7 @@ static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -248,7 +248,7 @@ static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -273,7 +273,7 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
u32 val = vwrq->value;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -293,7 +293,7 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u16 val = 0;
@@ -315,7 +315,7 @@ out:
static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u32 val = vwrq->value;
@@ -336,7 +336,7 @@ static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u16 val = 0;
@@ -359,7 +359,7 @@ out:
static int lbs_get_mode(struct net_device *dev,
struct iw_request_info *info, u32 * uwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -385,7 +385,7 @@ static int lbs_get_txpow(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
s16 curlevel = 0;
int ret = 0;
@@ -418,7 +418,7 @@ out:
static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u16 slimit = 0, llimit = 0;
@@ -466,7 +466,7 @@ out:
static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u16 val = 0;
@@ -542,7 +542,7 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
int i, j;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct iw_range *range = (struct iw_range *)extra;
struct chan_freq_power *cfp;
u8 rates[MAX_RATES + 1];
@@ -708,7 +708,7 @@ out:
static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -758,7 +758,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -781,7 +781,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
EXCELLENT = 95,
PERFECT = 100
};
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
u32 rssi_qual;
u32 tx_qual;
u32 quality = 0;
@@ -886,7 +886,7 @@ static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
int ret = -EINVAL;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct chan_freq_power *cfp;
struct assoc_request * assoc_req;
@@ -943,7 +943,7 @@ static int lbs_mesh_set_freq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct chan_freq_power *cfp;
int ret = -EINVAL;
@@ -994,7 +994,7 @@ out:
static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
u8 new_rate = 0;
int ret = -EINVAL;
u8 rates[MAX_RATES + 1];
@@ -1054,7 +1054,7 @@ out:
static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1079,7 +1079,7 @@ static int lbs_set_mode(struct net_device *dev,
struct iw_request_info *info, u32 * uwrq, char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct assoc_request * assoc_req;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1124,7 +1124,7 @@ static int lbs_get_encode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq, u8 * extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1319,7 +1319,7 @@ static int lbs_set_encode(struct net_device *dev,
struct iw_point *dwrq, char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct assoc_request * assoc_req;
u16 is_default = 0, index = 0, set_tx_key = 0;
@@ -1395,7 +1395,7 @@ static int lbs_get_encodeext(struct net_device *dev,
char *extra)
{
int ret = -EINVAL;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
int index, max_key_len;
@@ -1501,7 +1501,7 @@ static int lbs_set_encodeext(struct net_device *dev,
char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
int alg = ext->alg;
struct assoc_request * assoc_req;
@@ -1639,7 +1639,7 @@ static int lbs_set_genie(struct net_device *dev,
struct iw_point *dwrq,
char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
struct assoc_request * assoc_req;
@@ -1685,7 +1685,7 @@ static int lbs_get_genie(struct net_device *dev,
char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1713,7 +1713,7 @@ static int lbs_set_auth(struct net_device *dev,
struct iw_param *dwrq,
char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct assoc_request * assoc_req;
int ret = 0;
int updated = 0;
@@ -1816,7 +1816,7 @@ static int lbs_get_auth(struct net_device *dev,
char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1857,7 +1857,7 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
int ret = 0;
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
s16 dbm = (s16) vwrq->value;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1936,7 +1936,7 @@ out:
static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -1971,12 +1971,13 @@ static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
u8 ssid[IW_ESSID_MAX_SIZE];
u8 ssid_len = 0;
struct assoc_request * assoc_req;
int in_ssid_len = dwrq->length;
+ DECLARE_SSID_BUF(ssid_buf);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -2005,7 +2006,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
lbs_deb_wext("requested any SSID\n");
} else {
lbs_deb_wext("requested SSID '%s'\n",
- escape_essid(ssid, ssid_len));
+ print_ssid(ssid_buf, ssid, ssid_len));
}
out:
@@ -2039,7 +2040,7 @@ static int lbs_mesh_get_essid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -2057,7 +2058,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
int ret = 0;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -2101,10 +2102,9 @@ static int lbs_mesh_set_essid(struct net_device *dev,
static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
- struct lbs_private *priv = dev->priv;
+ struct lbs_private *priv = netdev_priv(dev);
struct assoc_request * assoc_req;
int ret = 0;
- DECLARE_MAC_BUF(mac);
lbs_deb_enter(LBS_DEB_WEXT);
@@ -2114,7 +2114,7 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
if (awrq->sa_family != ARPHRD_ETHER)
return -EINVAL;
- lbs_deb_wext("ASSOC: WAP: sa_data %s\n", print_mac(mac, awrq->sa_data));
+ lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data);
mutex_lock(&priv->lock);
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c
index fdbcf8ba3e8a..3d3914c83b14 100644
--- a/drivers/net/wireless/libertas_tf/cmd.c
+++ b/drivers/net/wireless/libertas_tf/cmd.c
@@ -79,7 +79,6 @@ int lbtf_update_hw_spec(struct lbtf_private *priv)
struct cmd_ds_get_hw_spec cmd;
int ret = -1;
u32 i;
- DECLARE_MAC_BUF(mac);
memset(&cmd, 0, sizeof(cmd));
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
@@ -96,8 +95,8 @@ int lbtf_update_hw_spec(struct lbtf_private *priv)
priv->fwrelease = (priv->fwrelease << 8) |
(priv->fwrelease >> 24 & 0xff);
- printk(KERN_INFO "libertastf: %s, fw %u.%u.%up%u, cap 0x%08x\n",
- print_mac(mac, cmd.permanentaddr),
+ printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n",
+ cmd.permanentaddr,
priv->fwrelease >> 24 & 0xff,
priv->fwrelease >> 16 & 0xff,
priv->fwrelease >> 8 & 0xff,
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index feff945ad856..d1fc305de5fe 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -354,9 +354,11 @@ static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
priv->vif = NULL;
}
-static int lbtf_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct lbtf_private *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+
if (conf->channel->center_freq != priv->cur_freq) {
priv->cur_freq = conf->channel->center_freq;
lbtf_set_channel(priv, conf->channel->hw_value);
@@ -590,14 +592,14 @@ EXPORT_SYMBOL_GPL(lbtf_remove_card);
void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
- memset(&info->status, 0, sizeof(info->status));
+
+ ieee80211_tx_info_clear_status(info);
/*
* Commented out, otherwise we never go beyond 1Mbit/s using mac80211
* default pid rc algorithm.
*
* info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
*/
- info->status.excessive_retries = fail ? 1 : 0;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
info->flags |= IEEE80211_TX_STAT_ACK;
skb_pull(priv->tx_skb, sizeof(struct txpd));
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 1a019e98dac3..f83d69e813d3 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -21,6 +21,7 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
+#include <linux/debugfs.h>
MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
@@ -32,6 +33,9 @@ MODULE_PARM_DESC(radios, "Number of simulated radios");
struct hwsim_vif_priv {
u32 magic;
+ u8 bssid[ETH_ALEN];
+ bool assoc;
+ u16 aid;
};
#define HWSIM_VIF_MAGIC 0x69537748
@@ -63,13 +67,13 @@ struct hwsim_sta_priv {
static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta)
{
struct hwsim_sta_priv *sp = (void *)sta->drv_priv;
- WARN_ON(sp->magic != HWSIM_VIF_MAGIC);
+ WARN_ON(sp->magic != HWSIM_STA_MAGIC);
}
static inline void hwsim_set_sta_magic(struct ieee80211_sta *sta)
{
struct hwsim_sta_priv *sp = (void *)sta->drv_priv;
- sp->magic = HWSIM_VIF_MAGIC;
+ sp->magic = HWSIM_STA_MAGIC;
}
static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta)
@@ -132,6 +136,12 @@ struct mac80211_hwsim_data {
unsigned int rx_filter;
int started;
struct timer_list beacon_timer;
+ enum ps_mode {
+ PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
+ } ps;
+ bool ps_poll_pending;
+ struct dentry *debugfs;
+ struct dentry *debugfs_ps;
};
@@ -196,6 +206,34 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
}
+static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
+ struct sk_buff *skb)
+{
+ switch (data->ps) {
+ case PS_DISABLED:
+ return true;
+ case PS_ENABLED:
+ return false;
+ case PS_AUTO_POLL:
+ /* TODO: accept (some) Beacons by default and other frames only
+ * if pending PS-Poll has been sent */
+ return true;
+ case PS_MANUAL_POLL:
+ /* Allow unicast frames to own address if there is a pending
+ * PS-Poll */
+ if (data->ps_poll_pending &&
+ memcmp(data->hw->wiphy->perm_addr, skb->data + 4,
+ ETH_ALEN) == 0) {
+ data->ps_poll_pending = false;
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
@@ -209,9 +247,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
rx_status.band = data->channel->band;
- rx_status.rate_idx = info->tx_rate_idx;
+ rx_status.rate_idx = info->control.rates[0].idx;
/* TODO: simulate signal strength (and optional packet drop) */
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
/* Copy skb to all enabled radios that are on the current frequency */
spin_lock(&hwsim_radio_lock);
list_for_each_entry(data2, &hwsim_radios, list) {
@@ -221,6 +262,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
continue;
if (!data2->started || !data2->radio_enabled ||
+ !hwsim_ps_rx_ok(data2, skb) ||
data->channel->center_freq != data2->channel->center_freq)
continue;
@@ -269,13 +311,9 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (txi->control.sta)
hwsim_check_sta_magic(txi->control.sta);
- memset(&txi->status, 0, sizeof(txi->status));
- if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK)) {
- if (ack)
- txi->flags |= IEEE80211_TX_STAT_ACK;
- else
- txi->status.excessive_retries = 1;
- }
+ ieee80211_tx_info_clear_status(txi);
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack)
+ txi->flags |= IEEE80211_TX_STAT_ACK;
ieee80211_tx_status_irqsafe(hw, skb);
return NETDEV_TX_OK;
}
@@ -294,6 +332,7 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
{
struct mac80211_hwsim_data *data = hw->priv;
data->started = 0;
+ del_timer(&data->beacon_timer);
printk(KERN_DEBUG "%s:%s\n", wiphy_name(hw->wiphy), __func__);
}
@@ -301,10 +340,9 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n",
+ printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n",
wiphy_name(hw->wiphy), __func__, conf->type,
- print_mac(mac, conf->mac_addr));
+ conf->mac_addr);
hwsim_set_magic(conf->vif);
return 0;
}
@@ -313,10 +351,9 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
static void mac80211_hwsim_remove_interface(
struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf)
{
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n",
+ printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n",
wiphy_name(hw->wiphy), __func__, conf->type,
- print_mac(mac, conf->mac_addr));
+ conf->mac_addr);
hwsim_check_magic(conf->vif);
hwsim_clear_magic(conf->vif);
}
@@ -331,7 +368,8 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
hwsim_check_magic(vif);
- if (vif->type != NL80211_IFTYPE_AP)
+ if (vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_MESH_POINT)
return;
skb = ieee80211_beacon_get(hw, vif);
@@ -361,10 +399,10 @@ static void mac80211_hwsim_beacon(unsigned long arg)
}
-static int mac80211_hwsim_config(struct ieee80211_hw *hw,
- struct ieee80211_conf *conf)
+static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
{
struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d beacon_int=%d)\n",
wiphy_name(hw->wiphy), __func__,
@@ -409,7 +447,16 @@ static int mac80211_hwsim_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{
+ struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+
hwsim_check_magic(vif);
+ if (conf->changed & IEEE80211_IFCC_BSSID) {
+ DECLARE_MAC_BUF(mac);
+ printk(KERN_DEBUG "%s:%s: BSSID changed: %pM\n",
+ wiphy_name(hw->wiphy), __func__,
+ conf->bssid);
+ memcpy(vp->bssid, conf->bssid, ETH_ALEN);
+ }
return 0;
}
@@ -418,7 +465,46 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_bss_conf *info,
u32 changed)
{
+ struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+
hwsim_check_magic(vif);
+
+ printk(KERN_DEBUG "%s:%s(changed=0x%x)\n",
+ wiphy_name(hw->wiphy), __func__, changed);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ printk(KERN_DEBUG " %s: ASSOC: assoc=%d aid=%d\n",
+ wiphy_name(hw->wiphy), info->assoc, info->aid);
+ vp->assoc = info->assoc;
+ vp->aid = info->aid;
+ }
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ printk(KERN_DEBUG " %s: ERP_CTS_PROT: %d\n",
+ wiphy_name(hw->wiphy), info->use_cts_prot);
+ }
+
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+ printk(KERN_DEBUG " %s: ERP_PREAMBLE: %d\n",
+ wiphy_name(hw->wiphy), info->use_short_preamble);
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ printk(KERN_DEBUG " %s: ERP_SLOT: %d\n",
+ wiphy_name(hw->wiphy), info->use_short_slot);
+ }
+
+ if (changed & BSS_CHANGED_HT) {
+ printk(KERN_DEBUG " %s: HT: op_mode=0x%x\n",
+ wiphy_name(hw->wiphy),
+ info->ht.operation_mode);
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ printk(KERN_DEBUG " %s: BASIC_RATES: 0x%llx\n",
+ wiphy_name(hw->wiphy),
+ (unsigned long long) info->basic_rates);
+ }
}
static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw,
@@ -434,6 +520,10 @@ static void mac80211_hwsim_sta_notify(struct ieee80211_hw *hw,
case STA_NOTIFY_REMOVE:
hwsim_clear_sta_magic(sta);
break;
+ case STA_NOTIFY_SLEEP:
+ case STA_NOTIFY_AWAKE:
+ /* TODO: make good use of these flags */
+ break;
}
}
@@ -445,6 +535,17 @@ static int mac80211_hwsim_set_tim(struct ieee80211_hw *hw,
return 0;
}
+static int mac80211_hwsim_conf_tx(
+ struct ieee80211_hw *hw, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ printk(KERN_DEBUG "%s:%s (queue=%d txop=%d cw_min=%d cw_max=%d "
+ "aifs=%d)\n",
+ wiphy_name(hw->wiphy), __func__, queue,
+ params->txop, params->cw_min, params->cw_max, params->aifs);
+ return 0;
+}
+
static const struct ieee80211_ops mac80211_hwsim_ops =
{
.tx = mac80211_hwsim_tx,
@@ -458,6 +559,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops =
.bss_info_changed = mac80211_hwsim_bss_info_changed,
.sta_notify = mac80211_hwsim_sta_notify,
.set_tim = mac80211_hwsim_set_tim,
+ .conf_tx = mac80211_hwsim_conf_tx,
};
@@ -474,6 +576,8 @@ static void mac80211_hwsim_free(void)
spin_unlock_bh(&hwsim_radio_lock);
list_for_each_entry(data, &tmplist, list) {
+ debugfs_remove(data->debugfs_ps);
+ debugfs_remove(data->debugfs);
ieee80211_unregister_hw(data->hw);
device_unregister(data->dev);
ieee80211_free_hw(data->hw);
@@ -499,13 +603,131 @@ static void hwsim_mon_setup(struct net_device *dev)
}
+static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct mac80211_hwsim_data *data = dat;
+ struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+ DECLARE_MAC_BUF(buf);
+ struct sk_buff *skb;
+ struct ieee80211_pspoll *pspoll;
+
+ if (!vp->assoc)
+ return;
+
+ printk(KERN_DEBUG "%s:%s: send PS-Poll to %pM for aid %d\n",
+ wiphy_name(data->hw->wiphy), __func__, vp->bssid, vp->aid);
+
+ skb = dev_alloc_skb(sizeof(*pspoll));
+ if (!skb)
+ return;
+ pspoll = (void *) skb_put(skb, sizeof(*pspoll));
+ pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_PSPOLL |
+ IEEE80211_FCTL_PM);
+ pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
+ memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
+ memcpy(pspoll->ta, mac, ETH_ALEN);
+ if (data->radio_enabled &&
+ !mac80211_hwsim_tx_frame(data->hw, skb))
+ printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
+ dev_kfree_skb(skb);
+}
+
+
+static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
+ struct ieee80211_vif *vif, int ps)
+{
+ struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+ DECLARE_MAC_BUF(buf);
+ struct sk_buff *skb;
+ struct ieee80211_hdr *hdr;
+
+ if (!vp->assoc)
+ return;
+
+ printk(KERN_DEBUG "%s:%s: send data::nullfunc to %pM ps=%d\n",
+ wiphy_name(data->hw->wiphy), __func__, vp->bssid, ps);
+
+ skb = dev_alloc_skb(sizeof(*hdr));
+ if (!skb)
+ return;
+ hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN);
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC |
+ (ps ? IEEE80211_FCTL_PM : 0));
+ hdr->duration_id = cpu_to_le16(0);
+ memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
+ memcpy(hdr->addr2, mac, ETH_ALEN);
+ memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
+ if (data->radio_enabled &&
+ !mac80211_hwsim_tx_frame(data->hw, skb))
+ printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
+ dev_kfree_skb(skb);
+}
+
+
+static void hwsim_send_nullfunc_ps(void *dat, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mac80211_hwsim_data *data = dat;
+ hwsim_send_nullfunc(data, mac, vif, 1);
+}
+
+
+static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mac80211_hwsim_data *data = dat;
+ hwsim_send_nullfunc(data, mac, vif, 0);
+}
+
+
+static int hwsim_fops_ps_read(void *dat, u64 *val)
+{
+ struct mac80211_hwsim_data *data = dat;
+ *val = data->ps;
+ return 0;
+}
+
+static int hwsim_fops_ps_write(void *dat, u64 val)
+{
+ struct mac80211_hwsim_data *data = dat;
+ enum ps_mode old_ps;
+
+ if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL &&
+ val != PS_MANUAL_POLL)
+ return -EINVAL;
+
+ old_ps = data->ps;
+ data->ps = val;
+
+ if (val == PS_MANUAL_POLL) {
+ ieee80211_iterate_active_interfaces(data->hw,
+ hwsim_send_ps_poll, data);
+ data->ps_poll_pending = true;
+ } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
+ ieee80211_iterate_active_interfaces(data->hw,
+ hwsim_send_nullfunc_ps,
+ data);
+ } else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
+ ieee80211_iterate_active_interfaces(data->hw,
+ hwsim_send_nullfunc_no_ps,
+ data);
+ }
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
+ "%llu\n");
+
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
u8 addr[ETH_ALEN];
struct mac80211_hwsim_data *data;
struct ieee80211_hw *hw;
- DECLARE_MAC_BUF(mac);
if (radios < 1 || radios > 100)
return -EINVAL;
@@ -553,7 +775,8 @@ static int __init init_mac80211_hwsim(void)
hw->queues = 4;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_AP);
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
hw->ampdu_queues = 1;
/* ask mac80211 to reserve space for magic */
@@ -566,19 +789,18 @@ static int __init init_mac80211_hwsim(void)
data->band.n_channels = ARRAY_SIZE(hwsim_channels);
data->band.bitrates = data->rates;
data->band.n_bitrates = ARRAY_SIZE(hwsim_rates);
- data->band.ht_info.ht_supported = 1;
- data->band.ht_info.cap = IEEE80211_HT_CAP_SUP_WIDTH |
+ data->band.ht_cap.ht_supported = true;
+ data->band.ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40;
- data->band.ht_info.ampdu_factor = 0x3;
- data->band.ht_info.ampdu_density = 0x6;
- memset(data->band.ht_info.supp_mcs_set, 0,
- sizeof(data->band.ht_info.supp_mcs_set));
- data->band.ht_info.supp_mcs_set[0] = 0xff;
- data->band.ht_info.supp_mcs_set[1] = 0xff;
- data->band.ht_info.supp_mcs_set[12] =
- IEEE80211_HT_CAP_MCS_TX_DEFINED;
+ data->band.ht_cap.ampdu_factor = 0x3;
+ data->band.ht_cap.ampdu_density = 0x6;
+ memset(&data->band.ht_cap.mcs, 0,
+ sizeof(data->band.ht_cap.mcs));
+ data->band.ht_cap.mcs.rx_mask[0] = 0xff;
+ data->band.ht_cap.mcs.rx_mask[1] = 0xff;
+ data->band.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &data->band;
err = ieee80211_register_hw(hw);
@@ -588,9 +810,15 @@ static int __init init_mac80211_hwsim(void)
goto failed_hw;
}
- printk(KERN_DEBUG "%s: hwaddr %s registered\n",
+ printk(KERN_DEBUG "%s: hwaddr %pM registered\n",
wiphy_name(hw->wiphy),
- print_mac(mac, hw->wiphy->perm_addr));
+ hw->wiphy->perm_addr);
+
+ data->debugfs = debugfs_create_dir("hwsim",
+ hw->wiphy->debugfsdir);
+ data->debugfs_ps = debugfs_create_file("ps", 0666,
+ data->debugfs, data,
+ &hwsim_fops_ps);
setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
(unsigned long) hw);
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c
index a670f36b5f3f..24caec6caf1f 100644
--- a/drivers/net/wireless/netwave_cs.c
+++ b/drivers/net/wireless/netwave_cs.c
@@ -737,7 +737,6 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
win_req_t req;
memreq_t mem;
u_char __iomem *ramBase = NULL;
- DECLARE_MAC_BUF(mac);
DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
@@ -808,12 +807,12 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
dev->dev_addr[i] = readb(ramBase + NETWAVE_EREG_PA + i);
printk(KERN_INFO "%s: Netwave: port %#3lx, irq %d, mem %lx, "
- "id %c%c, hw_addr %s\n",
+ "id %c%c, hw_addr %pM\n",
dev->name, dev->base_addr, dev->irq,
(u_long) ramBase,
(int) readb(ramBase+NETWAVE_EREG_NI),
(int) readb(ramBase+NETWAVE_EREG_NI+1),
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
/* get revision words */
printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n",
@@ -1308,7 +1307,6 @@ static int netwave_rx(struct net_device *dev)
/* Queue packet for network layer */
netif_rx(skb);
- dev->last_rx = jiffies;
priv->stats.rx_packets++;
priv->stats.rx_bytes += rcvLen;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
deleted file mode 100644
index e0512e49d6d3..000000000000
--- a/drivers/net/wireless/orinoco.c
+++ /dev/null
@@ -1,6021 +0,0 @@
-/* orinoco.c - (formerly known as dldwd_cs.c and orinoco_cs.c)
- *
- * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
- * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
- *
- * Current maintainers (as of 29 September 2003) are:
- * Pavel Roskin <proski AT gnu.org>
- * and David Gibson <hermes AT gibson.dropbear.id.au>
- *
- * (C) Copyright David Gibson, IBM Corporation 2001-2003.
- * Copyright (C) 2000 David Gibson, Linuxcare Australia.
- * With some help from :
- * Copyright (C) 2001 Jean Tourrilhes, HP Labs
- * Copyright (C) 2001 Benjamin Herrenschmidt
- *
- * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
- *
- * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy
- * AT fasta.fh-dortmund.de>
- * http://www.stud.fh-dortmund.de/~andy/wvlan/
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License
- * at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and
- * limitations under the License.
- *
- * The initial developer of the original code is David A. Hinds
- * <dahinds AT users.sourceforge.net>. Portions created by David
- * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights
- * Reserved.
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above. If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the MPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL. If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the MPL or the GPL. */
-
-/*
- * TODO
- * o Handle de-encapsulation within network layer, provide 802.11
- * headers (patch from Thomas 'Dent' Mirlacher)
- * o Fix possible races in SPY handling.
- * o Disconnect wireless extensions from fundamental configuration.
- * o (maybe) Software WEP support (patch from Stano Meduna).
- * o (maybe) Use multiple Tx buffers - driver handling queue
- * rather than firmware.
- */
-
-/* Locking and synchronization:
- *
- * The basic principle is that everything is serialized through a
- * single spinlock, priv->lock. The lock is used in user, bh and irq
- * context, so when taken outside hardirq context it should always be
- * taken with interrupts disabled. The lock protects both the
- * hardware and the struct orinoco_private.
- *
- * Another flag, priv->hw_unavailable indicates that the hardware is
- * unavailable for an extended period of time (e.g. suspended, or in
- * the middle of a hard reset). This flag is protected by the
- * spinlock. All code which touches the hardware should check the
- * flag after taking the lock, and if it is set, give up on whatever
- * they are doing and drop the lock again. The orinoco_lock()
- * function handles this (it unlocks and returns -EBUSY if
- * hw_unavailable is non-zero).
- */
-
-#define DRIVER_NAME "orinoco"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/firmware.h>
-#include <linux/if_arp.h>
-#include <linux/wireless.h>
-#include <net/iw_handler.h>
-#include <net/ieee80211.h>
-
-#include <linux/scatterlist.h>
-#include <linux/crypto.h>
-
-#include "hermes_rid.h"
-#include "hermes_dld.h"
-#include "orinoco.h"
-
-/********************************************************************/
-/* Module information */
-/********************************************************************/
-
-MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & David Gibson <hermes@gibson.dropbear.id.au>");
-MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
-MODULE_LICENSE("Dual MPL/GPL");
-
-/* Level of debugging. Used in the macros in orinoco.h */
-#ifdef ORINOCO_DEBUG
-int orinoco_debug = ORINOCO_DEBUG;
-module_param(orinoco_debug, int, 0644);
-MODULE_PARM_DESC(orinoco_debug, "Debug level");
-EXPORT_SYMBOL(orinoco_debug);
-#endif
-
-static int suppress_linkstatus; /* = 0 */
-module_param(suppress_linkstatus, bool, 0644);
-MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
-static int ignore_disconnect; /* = 0 */
-module_param(ignore_disconnect, int, 0644);
-MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer");
-
-static int force_monitor; /* = 0 */
-module_param(force_monitor, int, 0644);
-MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
-
-/********************************************************************/
-/* Compile time configuration and compatibility stuff */
-/********************************************************************/
-
-/* We do this this way to avoid ifdefs in the actual code */
-#ifdef WIRELESS_SPY
-#define SPY_NUMBER(priv) (priv->spy_data.spy_number)
-#else
-#define SPY_NUMBER(priv) 0
-#endif /* WIRELESS_SPY */
-
-/********************************************************************/
-/* Internal constants */
-/********************************************************************/
-
-/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
-static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
-#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
-
-#define ORINOCO_MIN_MTU 256
-#define ORINOCO_MAX_MTU (IEEE80211_DATA_LEN - ENCAPS_OVERHEAD)
-
-#define SYMBOL_MAX_VER_LEN (14)
-#define USER_BAP 0
-#define IRQ_BAP 1
-#define MAX_IRQLOOPS_PER_IRQ 10
-#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
- * how many events the
- * device could
- * legitimately generate */
-#define SMALL_KEY_SIZE 5
-#define LARGE_KEY_SIZE 13
-#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
-
-#define DUMMY_FID 0xFFFF
-
-/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
- HERMES_MAX_MULTICAST : 0)*/
-#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
-
-#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
- | HERMES_EV_TX | HERMES_EV_TXEXC \
- | HERMES_EV_WTERR | HERMES_EV_INFO \
- | HERMES_EV_INFDROP )
-
-#define MAX_RID_LEN 1024
-
-static const struct iw_handler_def orinoco_handler_def;
-static const struct ethtool_ops orinoco_ethtool_ops;
-
-/********************************************************************/
-/* Data tables */
-/********************************************************************/
-
-/* The frequency of each channel in MHz */
-static const long channel_frequency[] = {
- 2412, 2417, 2422, 2427, 2432, 2437, 2442,
- 2447, 2452, 2457, 2462, 2467, 2472, 2484
-};
-#define NUM_CHANNELS ARRAY_SIZE(channel_frequency)
-
-/* This tables gives the actual meanings of the bitrate IDs returned
- * by the firmware. */
-static struct {
- int bitrate; /* in 100s of kilobits */
- int automatic;
- u16 agere_txratectrl;
- u16 intersil_txratectrl;
-} bitrate_table[] = {
- {110, 1, 3, 15}, /* Entry 0 is the default */
- {10, 0, 1, 1},
- {10, 1, 1, 1},
- {20, 0, 2, 2},
- {20, 1, 6, 3},
- {55, 0, 4, 4},
- {55, 1, 7, 7},
- {110, 0, 5, 8},
-};
-#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
-
-/********************************************************************/
-/* Data types */
-/********************************************************************/
-
-/* Beginning of the Tx descriptor, used in TxExc handling */
-struct hermes_txexc_data {
- struct hermes_tx_descriptor desc;
- __le16 frame_ctl;
- __le16 duration_id;
- u8 addr1[ETH_ALEN];
-} __attribute__ ((packed));
-
-/* Rx frame header except compatibility 802.3 header */
-struct hermes_rx_descriptor {
- /* Control */
- __le16 status;
- __le32 time;
- u8 silence;
- u8 signal;
- u8 rate;
- u8 rxflow;
- __le32 reserved;
-
- /* 802.11 header */
- __le16 frame_ctl;
- __le16 duration_id;
- u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
- u8 addr3[ETH_ALEN];
- __le16 seq_ctl;
- u8 addr4[ETH_ALEN];
-
- /* Data length */
- __le16 data_len;
-} __attribute__ ((packed));
-
-/********************************************************************/
-/* Function prototypes */
-/********************************************************************/
-
-static int __orinoco_program_rids(struct net_device *dev);
-static void __orinoco_set_multicast_list(struct net_device *dev);
-
-/********************************************************************/
-/* Michael MIC crypto setup */
-/********************************************************************/
-#define MICHAEL_MIC_LEN 8
-static int orinoco_mic_init(struct orinoco_private *priv)
-{
- priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
- if (IS_ERR(priv->tx_tfm_mic)) {
- printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
- "crypto API michael_mic\n");
- priv->tx_tfm_mic = NULL;
- return -ENOMEM;
- }
-
- priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
- if (IS_ERR(priv->rx_tfm_mic)) {
- printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
- "crypto API michael_mic\n");
- priv->rx_tfm_mic = NULL;
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void orinoco_mic_free(struct orinoco_private *priv)
-{
- if (priv->tx_tfm_mic)
- crypto_free_hash(priv->tx_tfm_mic);
- if (priv->rx_tfm_mic)
- crypto_free_hash(priv->rx_tfm_mic);
-}
-
-static int michael_mic(struct crypto_hash *tfm_michael, u8 *key,
- u8 *da, u8 *sa, u8 priority,
- u8 *data, size_t data_len, u8 *mic)
-{
- struct hash_desc desc;
- struct scatterlist sg[2];
- u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
-
- if (tfm_michael == NULL) {
- printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
- return -1;
- }
-
- /* Copy header into buffer. We need the padding on the end zeroed */
- memcpy(&hdr[0], da, ETH_ALEN);
- memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
- hdr[ETH_ALEN*2] = priority;
- hdr[ETH_ALEN*2+1] = 0;
- hdr[ETH_ALEN*2+2] = 0;
- hdr[ETH_ALEN*2+3] = 0;
-
- /* Use scatter gather to MIC header and data in one go */
- sg_init_table(sg, 2);
- sg_set_buf(&sg[0], hdr, sizeof(hdr));
- sg_set_buf(&sg[1], data, data_len);
-
- if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN))
- return -1;
-
- desc.tfm = tfm_michael;
- desc.flags = 0;
- return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr),
- mic);
-}
-
-/********************************************************************/
-/* Internal helper functions */
-/********************************************************************/
-
-static inline void set_port_type(struct orinoco_private *priv)
-{
- switch (priv->iw_mode) {
- case IW_MODE_INFRA:
- priv->port_type = 1;
- priv->createibss = 0;
- break;
- case IW_MODE_ADHOC:
- if (priv->prefer_port3) {
- priv->port_type = 3;
- priv->createibss = 0;
- } else {
- priv->port_type = priv->ibss_port;
- priv->createibss = 1;
- }
- break;
- case IW_MODE_MONITOR:
- priv->port_type = 3;
- priv->createibss = 0;
- break;
- default:
- printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
- priv->ndev->name);
- }
-}
-
-#define ORINOCO_MAX_BSS_COUNT 64
-static int orinoco_bss_data_allocate(struct orinoco_private *priv)
-{
- if (priv->bss_xbss_data)
- return 0;
-
- if (priv->has_ext_scan)
- priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
- sizeof(struct xbss_element),
- GFP_KERNEL);
- else
- priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
- sizeof(struct bss_element),
- GFP_KERNEL);
-
- if (!priv->bss_xbss_data) {
- printk(KERN_WARNING "Out of memory allocating beacons");
- return -ENOMEM;
- }
- return 0;
-}
-
-static void orinoco_bss_data_free(struct orinoco_private *priv)
-{
- kfree(priv->bss_xbss_data);
- priv->bss_xbss_data = NULL;
-}
-
-#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
-#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
-static void orinoco_bss_data_init(struct orinoco_private *priv)
-{
- int i;
-
- INIT_LIST_HEAD(&priv->bss_free_list);
- INIT_LIST_HEAD(&priv->bss_list);
- if (priv->has_ext_scan)
- for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
- list_add_tail(&(PRIV_XBSS[i].list),
- &priv->bss_free_list);
- else
- for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
- list_add_tail(&(PRIV_BSS[i].list),
- &priv->bss_free_list);
-
-}
-
-static inline u8 *orinoco_get_ie(u8 *data, size_t len,
- enum ieee80211_mfie eid)
-{
- u8 *p = data;
- while ((p + 2) < (data + len)) {
- if (p[0] == eid)
- return p;
- p += p[1] + 2;
- }
- return NULL;
-}
-
-#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
-#define WPA_SELECTOR_LEN 4
-static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
-{
- u8 *p = data;
- while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
- if ((p[0] == MFIE_TYPE_GENERIC) &&
- (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
- return p;
- p += p[1] + 2;
- }
- return NULL;
-}
-
-
-/********************************************************************/
-/* Download functionality */
-/********************************************************************/
-
-struct fw_info {
- char *pri_fw;
- char *sta_fw;
- char *ap_fw;
- u32 pda_addr;
- u16 pda_size;
-};
-
-const static struct fw_info orinoco_fw[] = {
- { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
- { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
- { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 512 }
-};
-
-/* Structure used to access fields in FW
- * Make sure LE decoding macros are used
- */
-struct orinoco_fw_header {
- char hdr_vers[6]; /* ASCII string for header version */
- __le16 headersize; /* Total length of header */
- __le32 entry_point; /* NIC entry point */
- __le32 blocks; /* Number of blocks to program */
- __le32 block_offset; /* Offset of block data from eof header */
- __le32 pdr_offset; /* Offset to PDR data from eof header */
- __le32 pri_offset; /* Offset to primary plug data */
- __le32 compat_offset; /* Offset to compatibility data*/
- char signature[0]; /* FW signature length headersize-20 */
-} __attribute__ ((packed));
-
-/* Download either STA or AP firmware into the card. */
-static int
-orinoco_dl_firmware(struct orinoco_private *priv,
- const struct fw_info *fw,
- int ap)
-{
- /* Plug Data Area (PDA) */
- __le16 *pda;
-
- hermes_t *hw = &priv->hw;
- const struct firmware *fw_entry;
- const struct orinoco_fw_header *hdr;
- const unsigned char *first_block;
- const unsigned char *end;
- const char *firmware;
- struct net_device *dev = priv->ndev;
- int err = 0;
-
- pda = kzalloc(fw->pda_size, GFP_KERNEL);
- if (!pda)
- return -ENOMEM;
-
- if (ap)
- firmware = fw->ap_fw;
- else
- firmware = fw->sta_fw;
-
- printk(KERN_DEBUG "%s: Attempting to download firmware %s\n",
- dev->name, firmware);
-
- /* Read current plug data */
- err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
- printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
- if (err)
- goto free;
-
- err = request_firmware(&fw_entry, firmware, priv->dev);
- if (err) {
- printk(KERN_ERR "%s: Cannot find firmware %s\n",
- dev->name, firmware);
- err = -ENOENT;
- goto free;
- }
-
- hdr = (const struct orinoco_fw_header *) fw_entry->data;
-
- /* Enable aux port to allow programming */
- err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
- printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err);
- if (err != 0)
- goto abort;
-
- /* Program data */
- first_block = (fw_entry->data +
- le16_to_cpu(hdr->headersize) +
- le32_to_cpu(hdr->block_offset));
- end = fw_entry->data + fw_entry->size;
-
- err = hermes_program(hw, first_block, end);
- printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, err);
- if (err != 0)
- goto abort;
-
- /* Update production data */
- first_block = (fw_entry->data +
- le16_to_cpu(hdr->headersize) +
- le32_to_cpu(hdr->pdr_offset));
-
- err = hermes_apply_pda_with_defaults(hw, first_block, pda);
- printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err);
- if (err)
- goto abort;
-
- /* Tell card we've finished */
- err = hermesi_program_end(hw);
- printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err);
- if (err != 0)
- goto abort;
-
- /* Check if we're running */
- printk(KERN_DEBUG "%s: hermes_present returned %d\n",
- dev->name, hermes_present(hw));
-
-abort:
- release_firmware(fw_entry);
-
-free:
- kfree(pda);
- return err;
-}
-
-/* End markers */
-#define TEXT_END 0x1A /* End of text header */
-
-/*
- * Process a firmware image - stop the card, load the firmware, reset
- * the card and make sure it responds. For the secondary firmware take
- * care of the PDA - read it and then write it on top of the firmware.
- */
-static int
-symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
- const unsigned char *image, const unsigned char *end,
- int secondary)
-{
- hermes_t *hw = &priv->hw;
- int ret = 0;
- const unsigned char *ptr;
- const unsigned char *first_block;
-
- /* Plug Data Area (PDA) */
- __le16 *pda = NULL;
-
- /* Binary block begins after the 0x1A marker */
- ptr = image;
- while (*ptr++ != TEXT_END);
- first_block = ptr;
-
- /* Read the PDA from EEPROM */
- if (secondary) {
- pda = kzalloc(fw->pda_size, GFP_KERNEL);
- if (!pda)
- return -ENOMEM;
-
- ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
- if (ret)
- goto free;
- }
-
- /* Stop the firmware, so that it can be safely rewritten */
- if (priv->stop_fw) {
- ret = priv->stop_fw(priv, 1);
- if (ret)
- goto free;
- }
-
- /* Program the adapter with new firmware */
- ret = hermes_program(hw, first_block, end);
- if (ret)
- goto free;
-
- /* Write the PDA to the adapter */
- if (secondary) {
- size_t len = hermes_blocks_length(first_block);
- ptr = first_block + len;
- ret = hermes_apply_pda(hw, ptr, pda);
- kfree(pda);
- if (ret)
- return ret;
- }
-
- /* Run the firmware */
- if (priv->stop_fw) {
- ret = priv->stop_fw(priv, 0);
- if (ret)
- return ret;
- }
-
- /* Reset hermes chip and make sure it responds */
- ret = hermes_init(hw);
-
- /* hermes_reset() should return 0 with the secondary firmware */
- if (secondary && ret != 0)
- return -ENODEV;
-
- /* And this should work with any firmware */
- if (!hermes_present(hw))
- return -ENODEV;
-
- return 0;
-
-free:
- kfree(pda);
- return ret;
-}
-
-
-/*
- * Download the firmware into the card, this also does a PCMCIA soft
- * reset on the card, to make sure it's in a sane state.
- */
-static int
-symbol_dl_firmware(struct orinoco_private *priv,
- const struct fw_info *fw)
-{
- struct net_device *dev = priv->ndev;
- int ret;
- const struct firmware *fw_entry;
-
- if (request_firmware(&fw_entry, fw->pri_fw,
- priv->dev) != 0) {
- printk(KERN_ERR "%s: Cannot find firmware: %s\n",
- dev->name, fw->pri_fw);
- return -ENOENT;
- }
-
- /* Load primary firmware */
- ret = symbol_dl_image(priv, fw, fw_entry->data,
- fw_entry->data + fw_entry->size, 0);
- release_firmware(fw_entry);
- if (ret) {
- printk(KERN_ERR "%s: Primary firmware download failed\n",
- dev->name);
- return ret;
- }
-
- if (request_firmware(&fw_entry, fw->sta_fw,
- priv->dev) != 0) {
- printk(KERN_ERR "%s: Cannot find firmware: %s\n",
- dev->name, fw->sta_fw);
- return -ENOENT;
- }
-
- /* Load secondary firmware */
- ret = symbol_dl_image(priv, fw, fw_entry->data,
- fw_entry->data + fw_entry->size, 1);
- release_firmware(fw_entry);
- if (ret) {
- printk(KERN_ERR "%s: Secondary firmware download failed\n",
- dev->name);
- }
-
- return ret;
-}
-
-static int orinoco_download(struct orinoco_private *priv)
-{
- int err = 0;
- /* Reload firmware */
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- /* case FIRMWARE_TYPE_INTERSIL: */
- err = orinoco_dl_firmware(priv,
- &orinoco_fw[priv->firmware_type], 0);
- break;
-
- case FIRMWARE_TYPE_SYMBOL:
- err = symbol_dl_firmware(priv,
- &orinoco_fw[priv->firmware_type]);
- break;
- case FIRMWARE_TYPE_INTERSIL:
- break;
- }
- /* TODO: if we fail we probably need to reinitialise
- * the driver */
-
- return err;
-}
-
-/********************************************************************/
-/* Device methods */
-/********************************************************************/
-
-static int orinoco_open(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = __orinoco_up(dev);
-
- if (! err)
- priv->open = 1;
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_stop(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = 0;
-
- /* We mustn't use orinoco_lock() here, because we need to be
- able to close the interface even if hw_unavailable is set
- (e.g. as we're released after a PC Card removal) */
- spin_lock_irq(&priv->lock);
-
- priv->open = 0;
-
- err = __orinoco_down(dev);
-
- spin_unlock_irq(&priv->lock);
-
- return err;
-}
-
-static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- return &priv->stats;
-}
-
-static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- struct iw_statistics *wstats = &priv->wstats;
- int err;
- unsigned long flags;
-
- if (! netif_device_present(dev)) {
- printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
- dev->name);
- return NULL; /* FIXME: Can we do better than this? */
- }
-
- /* If busy, return the old stats. Returning NULL may cause
- * the interface to disappear from /proc/net/wireless */
- if (orinoco_lock(priv, &flags) != 0)
- return wstats;
-
- /* We can't really wait for the tallies inquiry command to
- * complete, so we just use the previous results and trigger
- * a new tallies inquiry command for next time - Jean II */
- /* FIXME: Really we should wait for the inquiry to come back -
- * as it is the stats we give don't make a whole lot of sense.
- * Unfortunately, it's not clear how to do that within the
- * wireless extensions framework: I think we're in user
- * context, but a lock seems to be held by the time we get in
- * here so we're not safe to sleep here. */
- hermes_inquire(hw, HERMES_INQ_TALLIES);
-
- if (priv->iw_mode == IW_MODE_ADHOC) {
- memset(&wstats->qual, 0, sizeof(wstats->qual));
- /* If a spy address is defined, we report stats of the
- * first spy address - Jean II */
- if (SPY_NUMBER(priv)) {
- wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
- wstats->qual.level = priv->spy_data.spy_stat[0].level;
- wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
- wstats->qual.updated = priv->spy_data.spy_stat[0].updated;
- }
- } else {
- struct {
- __le16 qual, signal, noise, unused;
- } __attribute__ ((packed)) cq;
-
- err = HERMES_READ_RECORD(hw, USER_BAP,
- HERMES_RID_COMMSQUALITY, &cq);
-
- if (!err) {
- wstats->qual.qual = (int)le16_to_cpu(cq.qual);
- wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
- wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
- wstats->qual.updated = 7;
- }
- }
-
- orinoco_unlock(priv, &flags);
- return wstats;
-}
-
-static void orinoco_set_multicast_list(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0) {
- printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
- "called when hw_unavailable\n", dev->name);
- return;
- }
-
- __orinoco_set_multicast_list(dev);
- orinoco_unlock(priv, &flags);
-}
-
-static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
- return -EINVAL;
-
- if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) >
- (priv->nicbuf_size - ETH_HLEN) )
- return -EINVAL;
-
- dev->mtu = new_mtu;
-
- return 0;
-}
-
-/********************************************************************/
-/* Tx path */
-/********************************************************************/
-
-static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- hermes_t *hw = &priv->hw;
- int err = 0;
- u16 txfid = priv->txfid;
- struct ethhdr *eh;
- int tx_control;
- unsigned long flags;
-
- if (! netif_running(dev)) {
- printk(KERN_ERR "%s: Tx on stopped device!\n",
- dev->name);
- return NETDEV_TX_BUSY;
- }
-
- if (netif_queue_stopped(dev)) {
- printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
- dev->name);
- return NETDEV_TX_BUSY;
- }
-
- if (orinoco_lock(priv, &flags) != 0) {
- printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
- dev->name);
- return NETDEV_TX_BUSY;
- }
-
- if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
- /* Oops, the firmware hasn't established a connection,
- silently drop the packet (this seems to be the
- safest approach). */
- goto drop;
- }
-
- /* Check packet length */
- if (skb->len < ETH_HLEN)
- goto drop;
-
- tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
-
- if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
- tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
- HERMES_TXCTRL_MIC;
-
- if (priv->has_alt_txcntl) {
- /* WPA enabled firmwares have tx_cntl at the end of
- * the 802.11 header. So write zeroed descriptor and
- * 802.11 header at the same time
- */
- char desc[HERMES_802_3_OFFSET];
- __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
-
- memset(&desc, 0, sizeof(desc));
-
- *txcntl = cpu_to_le16(tx_control);
- err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
- txfid, 0);
- if (err) {
- if (net_ratelimit())
- printk(KERN_ERR "%s: Error %d writing Tx "
- "descriptor to BAP\n", dev->name, err);
- goto busy;
- }
- } else {
- struct hermes_tx_descriptor desc;
-
- memset(&desc, 0, sizeof(desc));
-
- desc.tx_control = cpu_to_le16(tx_control);
- err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
- txfid, 0);
- if (err) {
- if (net_ratelimit())
- printk(KERN_ERR "%s: Error %d writing Tx "
- "descriptor to BAP\n", dev->name, err);
- goto busy;
- }
-
- /* Clear the 802.11 header and data length fields - some
- * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
- * if this isn't done. */
- hermes_clear_words(hw, HERMES_DATA0,
- HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
- }
-
- eh = (struct ethhdr *)skb->data;
-
- /* Encapsulate Ethernet-II frames */
- if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
- struct header_struct {
- struct ethhdr eth; /* 802.3 header */
- u8 encap[6]; /* 802.2 header */
- } __attribute__ ((packed)) hdr;
-
- /* Strip destination and source from the data */
- skb_pull(skb, 2 * ETH_ALEN);
-
- /* And move them to a separate header */
- memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
- hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
- memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
-
- /* Insert the SNAP header */
- if (skb_headroom(skb) < sizeof(hdr)) {
- printk(KERN_ERR
- "%s: Not enough headroom for 802.2 headers %d\n",
- dev->name, skb_headroom(skb));
- goto drop;
- }
- eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
- memcpy(eh, &hdr, sizeof(hdr));
- }
-
- err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
- txfid, HERMES_802_3_OFFSET);
- if (err) {
- printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
- dev->name, err);
- goto busy;
- }
-
- /* Calculate Michael MIC */
- if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
- u8 mic_buf[MICHAEL_MIC_LEN + 1];
- u8 *mic;
- size_t offset;
- size_t len;
-
- if (skb->len % 2) {
- /* MIC start is on an odd boundary */
- mic_buf[0] = skb->data[skb->len - 1];
- mic = &mic_buf[1];
- offset = skb->len - 1;
- len = MICHAEL_MIC_LEN + 1;
- } else {
- mic = &mic_buf[0];
- offset = skb->len;
- len = MICHAEL_MIC_LEN;
- }
-
- michael_mic(priv->tx_tfm_mic,
- priv->tkip_key[priv->tx_key].tx_mic,
- eh->h_dest, eh->h_source, 0 /* priority */,
- skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
-
- /* Write the MIC */
- err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
- txfid, HERMES_802_3_OFFSET + offset);
- if (err) {
- printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
- dev->name, err);
- goto busy;
- }
- }
-
- /* Finally, we actually initiate the send */
- netif_stop_queue(dev);
-
- err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
- txfid, NULL);
- if (err) {
- netif_start_queue(dev);
- if (net_ratelimit())
- printk(KERN_ERR "%s: Error %d transmitting packet\n",
- dev->name, err);
- goto busy;
- }
-
- dev->trans_start = jiffies;
- stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
- goto ok;
-
- drop:
- stats->tx_errors++;
- stats->tx_dropped++;
-
- ok:
- orinoco_unlock(priv, &flags);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-
- busy:
- if (err == -EIO)
- schedule_work(&priv->reset_work);
- orinoco_unlock(priv, &flags);
- return NETDEV_TX_BUSY;
-}
-
-static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- u16 fid = hermes_read_regn(hw, ALLOCFID);
-
- if (fid != priv->txfid) {
- if (fid != DUMMY_FID)
- printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
- dev->name, fid);
- return;
- }
-
- hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
-}
-
-static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
-
- stats->tx_packets++;
-
- netif_wake_queue(dev);
-
- hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
-}
-
-static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- u16 fid = hermes_read_regn(hw, TXCOMPLFID);
- u16 status;
- struct hermes_txexc_data hdr;
- int err = 0;
-
- if (fid == DUMMY_FID)
- return; /* Nothing's really happened */
-
- /* Read part of the frame header - we need status and addr1 */
- err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
- sizeof(struct hermes_txexc_data),
- fid, 0);
-
- hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
- stats->tx_errors++;
-
- if (err) {
- printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
- "(FID=%04X error %d)\n",
- dev->name, fid, err);
- return;
- }
-
- DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
- err, fid);
-
- /* We produce a TXDROP event only for retry or lifetime
- * exceeded, because that's the only status that really mean
- * that this particular node went away.
- * Other errors means that *we* screwed up. - Jean II */
- status = le16_to_cpu(hdr.desc.status);
- if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
- union iwreq_data wrqu;
-
- /* Copy 802.11 dest address.
- * We use the 802.11 header because the frame may
- * not be 802.3 or may be mangled...
- * In Ad-Hoc mode, it will be the node address.
- * In managed mode, it will be most likely the AP addr
- * User space will figure out how to convert it to
- * whatever it needs (IP address or else).
- * - Jean II */
- memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
- wrqu.addr.sa_family = ARPHRD_ETHER;
-
- /* Send event to user space */
- wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
- }
-
- netif_wake_queue(dev);
-}
-
-static void orinoco_tx_timeout(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- struct hermes *hw = &priv->hw;
-
- printk(KERN_WARNING "%s: Tx timeout! "
- "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
- dev->name, hermes_read_regn(hw, ALLOCFID),
- hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
-
- stats->tx_errors++;
-
- schedule_work(&priv->reset_work);
-}
-
-/********************************************************************/
-/* Rx path (data frames) */
-/********************************************************************/
-
-/* Does the frame have a SNAP header indicating it should be
- * de-encapsulated to Ethernet-II? */
-static inline int is_ethersnap(void *_hdr)
-{
- u8 *hdr = _hdr;
-
- /* We de-encapsulate all packets which, a) have SNAP headers
- * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
- * and where b) the OUI of the SNAP header is 00:00:00 or
- * 00:00:f8 - we need both because different APs appear to use
- * different OUIs for some reason */
- return (memcmp(hdr, &encaps_hdr, 5) == 0)
- && ( (hdr[5] == 0x00) || (hdr[5] == 0xf8) );
-}
-
-static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
- int level, int noise)
-{
- struct iw_quality wstats;
- wstats.level = level - 0x95;
- wstats.noise = noise - 0x95;
- wstats.qual = (level > noise) ? (level - noise) : 0;
- wstats.updated = 7;
- /* Update spy records */
- wireless_spy_update(dev, mac, &wstats);
-}
-
-static void orinoco_stat_gather(struct net_device *dev,
- struct sk_buff *skb,
- struct hermes_rx_descriptor *desc)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- /* Using spy support with lots of Rx packets, like in an
- * infrastructure (AP), will really slow down everything, because
- * the MAC address must be compared to each entry of the spy list.
- * If the user really asks for it (set some address in the
- * spy list), we do it, but he will pay the price.
- * Note that to get here, you need both WIRELESS_SPY
- * compiled in AND some addresses in the list !!!
- */
- /* Note : gcc will optimise the whole section away if
- * WIRELESS_SPY is not defined... - Jean II */
- if (SPY_NUMBER(priv)) {
- orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
- desc->signal, desc->silence);
- }
-}
-
-/*
- * orinoco_rx_monitor - handle received monitor frames.
- *
- * Arguments:
- * dev network device
- * rxfid received FID
- * desc rx descriptor of the frame
- *
- * Call context: interrupt
- */
-static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
- struct hermes_rx_descriptor *desc)
-{
- u32 hdrlen = 30; /* return full header by default */
- u32 datalen = 0;
- u16 fc;
- int err;
- int len;
- struct sk_buff *skb;
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- hermes_t *hw = &priv->hw;
-
- len = le16_to_cpu(desc->data_len);
-
- /* Determine the size of the header and the data */
- fc = le16_to_cpu(desc->frame_ctl);
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_DATA:
- if ((fc & IEEE80211_FCTL_TODS)
- && (fc & IEEE80211_FCTL_FROMDS))
- hdrlen = 30;
- else
- hdrlen = 24;
- datalen = len;
- break;
- case IEEE80211_FTYPE_MGMT:
- hdrlen = 24;
- datalen = len;
- break;
- case IEEE80211_FTYPE_CTL:
- switch (fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_PSPOLL:
- case IEEE80211_STYPE_RTS:
- case IEEE80211_STYPE_CFEND:
- case IEEE80211_STYPE_CFENDACK:
- hdrlen = 16;
- break;
- case IEEE80211_STYPE_CTS:
- case IEEE80211_STYPE_ACK:
- hdrlen = 10;
- break;
- }
- break;
- default:
- /* Unknown frame type */
- break;
- }
-
- /* sanity check the length */
- if (datalen > IEEE80211_DATA_LEN + 12) {
- printk(KERN_DEBUG "%s: oversized monitor frame, "
- "data length = %d\n", dev->name, datalen);
- stats->rx_length_errors++;
- goto update_stats;
- }
-
- skb = dev_alloc_skb(hdrlen + datalen);
- if (!skb) {
- printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
- dev->name);
- goto update_stats;
- }
-
- /* Copy the 802.11 header to the skb */
- memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
- skb_reset_mac_header(skb);
-
- /* If any, copy the data from the card to the skb */
- if (datalen > 0) {
- err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
- ALIGN(datalen, 2), rxfid,
- HERMES_802_2_OFFSET);
- if (err) {
- printk(KERN_ERR "%s: error %d reading monitor frame\n",
- dev->name, err);
- goto drop;
- }
- }
-
- skb->dev = dev;
- skb->ip_summed = CHECKSUM_NONE;
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = __constant_htons(ETH_P_802_2);
-
- dev->last_rx = jiffies;
- stats->rx_packets++;
- stats->rx_bytes += skb->len;
-
- netif_rx(skb);
- return;
-
- drop:
- dev_kfree_skb_irq(skb);
- update_stats:
- stats->rx_errors++;
- stats->rx_dropped++;
-}
-
-/* Get tsc from the firmware */
-static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
- u8 *tsc)
-{
- hermes_t *hw = &priv->hw;
- int err = 0;
- u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
-
- if ((key < 0) || (key > 4))
- return -EINVAL;
-
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
- sizeof(tsc_arr), NULL, &tsc_arr);
- if (!err)
- memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
-
- return err;
-}
-
-static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- struct iw_statistics *wstats = &priv->wstats;
- struct sk_buff *skb = NULL;
- u16 rxfid, status;
- int length;
- struct hermes_rx_descriptor *desc;
- struct orinoco_rx_data *rx_data;
- int err;
-
- desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
- if (!desc) {
- printk(KERN_WARNING
- "%s: Can't allocate space for RX descriptor\n",
- dev->name);
- goto update_stats;
- }
-
- rxfid = hermes_read_regn(hw, RXFID);
-
- err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
- rxfid, 0);
- if (err) {
- printk(KERN_ERR "%s: error %d reading Rx descriptor. "
- "Frame dropped.\n", dev->name, err);
- goto update_stats;
- }
-
- status = le16_to_cpu(desc->status);
-
- if (status & HERMES_RXSTAT_BADCRC) {
- DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
- dev->name);
- stats->rx_crc_errors++;
- goto update_stats;
- }
-
- /* Handle frames in monitor mode */
- if (priv->iw_mode == IW_MODE_MONITOR) {
- orinoco_rx_monitor(dev, rxfid, desc);
- goto out;
- }
-
- if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
- DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
- dev->name);
- wstats->discard.code++;
- goto update_stats;
- }
-
- length = le16_to_cpu(desc->data_len);
-
- /* Sanity checks */
- if (length < 3) { /* No for even an 802.2 LLC header */
- /* At least on Symbol firmware with PCF we get quite a
- lot of these legitimately - Poll frames with no
- data. */
- goto out;
- }
- if (length > IEEE80211_DATA_LEN) {
- printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
- dev->name, length);
- stats->rx_length_errors++;
- goto update_stats;
- }
-
- /* Payload size does not include Michael MIC. Increase payload
- * size to read it together with the data. */
- if (status & HERMES_RXSTAT_MIC)
- length += MICHAEL_MIC_LEN;
-
- /* We need space for the packet data itself, plus an ethernet
- header, plus 2 bytes so we can align the IP header on a
- 32bit boundary, plus 1 byte so we can read in odd length
- packets from the card, which has an IO granularity of 16
- bits */
- skb = dev_alloc_skb(length+ETH_HLEN+2+1);
- if (!skb) {
- printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
- dev->name);
- goto update_stats;
- }
-
- /* We'll prepend the header, so reserve space for it. The worst
- case is no decapsulation, when 802.3 header is prepended and
- nothing is removed. 2 is for aligning the IP header. */
- skb_reserve(skb, ETH_HLEN + 2);
-
- err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
- ALIGN(length, 2), rxfid,
- HERMES_802_2_OFFSET);
- if (err) {
- printk(KERN_ERR "%s: error %d reading frame. "
- "Frame dropped.\n", dev->name, err);
- goto drop;
- }
-
- /* Add desc and skb to rx queue */
- rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
- if (!rx_data) {
- printk(KERN_WARNING "%s: Can't allocate RX packet\n",
- dev->name);
- goto drop;
- }
- rx_data->desc = desc;
- rx_data->skb = skb;
- list_add_tail(&rx_data->list, &priv->rx_list);
- tasklet_schedule(&priv->rx_tasklet);
-
- return;
-
-drop:
- dev_kfree_skb_irq(skb);
-update_stats:
- stats->rx_errors++;
- stats->rx_dropped++;
-out:
- kfree(desc);
-}
-
-static void orinoco_rx(struct net_device *dev,
- struct hermes_rx_descriptor *desc,
- struct sk_buff *skb)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct net_device_stats *stats = &priv->stats;
- u16 status, fc;
- int length;
- struct ethhdr *hdr;
-
- status = le16_to_cpu(desc->status);
- length = le16_to_cpu(desc->data_len);
- fc = le16_to_cpu(desc->frame_ctl);
-
- /* Calculate and check MIC */
- if (status & HERMES_RXSTAT_MIC) {
- int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
- HERMES_MIC_KEY_ID_SHIFT);
- u8 mic[MICHAEL_MIC_LEN];
- u8 *rxmic;
- u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
- desc->addr3 : desc->addr2;
-
- /* Extract Michael MIC from payload */
- rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
-
- skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
- length -= MICHAEL_MIC_LEN;
-
- michael_mic(priv->rx_tfm_mic,
- priv->tkip_key[key_id].rx_mic,
- desc->addr1,
- src,
- 0, /* priority or QoS? */
- skb->data,
- skb->len,
- &mic[0]);
-
- if (memcmp(mic, rxmic,
- MICHAEL_MIC_LEN)) {
- union iwreq_data wrqu;
- struct iw_michaelmicfailure wxmic;
- DECLARE_MAC_BUF(mac);
-
- printk(KERN_WARNING "%s: "
- "Invalid Michael MIC in data frame from %s, "
- "using key %i\n",
- dev->name, print_mac(mac, src), key_id);
-
- /* TODO: update stats */
-
- /* Notify userspace */
- memset(&wxmic, 0, sizeof(wxmic));
- wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
- wxmic.flags |= (desc->addr1[0] & 1) ?
- IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
- wxmic.src_addr.sa_family = ARPHRD_ETHER;
- memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
-
- (void) orinoco_hw_get_tkip_iv(priv, key_id,
- &wxmic.tsc[0]);
-
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = sizeof(wxmic);
- wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
- (char *) &wxmic);
-
- goto drop;
- }
- }
-
- /* Handle decapsulation
- * In most cases, the firmware tell us about SNAP frames.
- * For some reason, the SNAP frames sent by LinkSys APs
- * are not properly recognised by most firmwares.
- * So, check ourselves */
- if (length >= ENCAPS_OVERHEAD &&
- (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
- ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
- is_ethersnap(skb->data))) {
- /* These indicate a SNAP within 802.2 LLC within
- 802.11 frame which we'll need to de-encapsulate to
- the original EthernetII frame. */
- hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD);
- } else {
- /* 802.3 frame - prepend 802.3 header as is */
- hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
- hdr->h_proto = htons(length);
- }
- memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
- if (fc & IEEE80211_FCTL_FROMDS)
- memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
- else
- memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
-
- dev->last_rx = jiffies;
- skb->protocol = eth_type_trans(skb, dev);
- skb->ip_summed = CHECKSUM_NONE;
- if (fc & IEEE80211_FCTL_TODS)
- skb->pkt_type = PACKET_OTHERHOST;
-
- /* Process the wireless stats if needed */
- orinoco_stat_gather(dev, skb, desc);
-
- /* Pass the packet to the networking stack */
- netif_rx(skb);
- stats->rx_packets++;
- stats->rx_bytes += length;
-
- return;
-
- drop:
- dev_kfree_skb(skb);
- stats->rx_errors++;
- stats->rx_dropped++;
-}
-
-static void orinoco_rx_isr_tasklet(unsigned long data)
-{
- struct net_device *dev = (struct net_device *) data;
- struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_rx_data *rx_data, *temp;
- struct hermes_rx_descriptor *desc;
- struct sk_buff *skb;
-
- /* extract desc and skb from queue */
- list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
- desc = rx_data->desc;
- skb = rx_data->skb;
- list_del(&rx_data->list);
- kfree(rx_data);
-
- orinoco_rx(dev, desc, skb);
-
- kfree(desc);
- }
-}
-
-/********************************************************************/
-/* Rx path (info frames) */
-/********************************************************************/
-
-static void print_linkstatus(struct net_device *dev, u16 status)
-{
- char * s;
-
- if (suppress_linkstatus)
- return;
-
- switch (status) {
- case HERMES_LINKSTATUS_NOT_CONNECTED:
- s = "Not Connected";
- break;
- case HERMES_LINKSTATUS_CONNECTED:
- s = "Connected";
- break;
- case HERMES_LINKSTATUS_DISCONNECTED:
- s = "Disconnected";
- break;
- case HERMES_LINKSTATUS_AP_CHANGE:
- s = "AP Changed";
- break;
- case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
- s = "AP Out of Range";
- break;
- case HERMES_LINKSTATUS_AP_IN_RANGE:
- s = "AP In Range";
- break;
- case HERMES_LINKSTATUS_ASSOC_FAILED:
- s = "Association Failed";
- break;
- default:
- s = "UNKNOWN";
- }
-
- printk(KERN_INFO "%s: New link status: %s (%04x)\n",
- dev->name, s, status);
-}
-
-/* Search scan results for requested BSSID, join it if found */
-static void orinoco_join_ap(struct work_struct *work)
-{
- struct orinoco_private *priv =
- container_of(work, struct orinoco_private, join_work);
- struct net_device *dev = priv->ndev;
- struct hermes *hw = &priv->hw;
- int err;
- unsigned long flags;
- struct join_req {
- u8 bssid[ETH_ALEN];
- __le16 channel;
- } __attribute__ ((packed)) req;
- const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
- struct prism2_scan_apinfo *atom = NULL;
- int offset = 4;
- int found = 0;
- u8 *buf;
- u16 len;
-
- /* Allocate buffer for scan results */
- buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
- if (! buf)
- return;
-
- if (orinoco_lock(priv, &flags) != 0)
- goto fail_lock;
-
- /* Sanity checks in case user changed something in the meantime */
- if (! priv->bssid_fixed)
- goto out;
-
- if (strlen(priv->desired_essid) == 0)
- goto out;
-
- /* Read scan results from the firmware */
- err = hermes_read_ltv(hw, USER_BAP,
- HERMES_RID_SCANRESULTSTABLE,
- MAX_SCAN_LEN, &len, buf);
- if (err) {
- printk(KERN_ERR "%s: Cannot read scan results\n",
- dev->name);
- goto out;
- }
-
- len = HERMES_RECLEN_TO_BYTES(len);
-
- /* Go through the scan results looking for the channel of the AP
- * we were requested to join */
- for (; offset + atom_len <= len; offset += atom_len) {
- atom = (struct prism2_scan_apinfo *) (buf + offset);
- if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
- found = 1;
- break;
- }
- }
-
- if (! found) {
- DEBUG(1, "%s: Requested AP not found in scan results\n",
- dev->name);
- goto out;
- }
-
- memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
- req.channel = atom->channel; /* both are little-endian */
- err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
- &req);
- if (err)
- printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
-
- out:
- orinoco_unlock(priv, &flags);
-
- fail_lock:
- kfree(buf);
-}
-
-/* Send new BSSID to userspace */
-static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
-{
- struct net_device *dev = priv->ndev;
- struct hermes *hw = &priv->hw;
- union iwreq_data wrqu;
- int err;
-
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID,
- ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
- if (err != 0)
- return;
-
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-
- /* Send event to user space */
- wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
-{
- struct net_device *dev = priv->ndev;
- struct hermes *hw = &priv->hw;
- union iwreq_data wrqu;
- int err;
- u8 buf[88];
- u8 *ie;
-
- if (!priv->has_wpa)
- return;
-
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
- sizeof(buf), NULL, &buf);
- if (err != 0)
- return;
-
- ie = orinoco_get_wpa_ie(buf, sizeof(buf));
- if (ie) {
- int rem = sizeof(buf) - (ie - &buf[0]);
- wrqu.data.length = ie[1] + 2;
- if (wrqu.data.length > rem)
- wrqu.data.length = rem;
-
- if (wrqu.data.length)
- /* Send event to user space */
- wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
- }
-}
-
-static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
-{
- struct net_device *dev = priv->ndev;
- struct hermes *hw = &priv->hw;
- union iwreq_data wrqu;
- int err;
- u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
- u8 *ie;
-
- if (!priv->has_wpa)
- return;
-
- err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
- sizeof(buf), NULL, &buf);
- if (err != 0)
- return;
-
- ie = orinoco_get_wpa_ie(buf, sizeof(buf));
- if (ie) {
- int rem = sizeof(buf) - (ie - &buf[0]);
- wrqu.data.length = ie[1] + 2;
- if (wrqu.data.length > rem)
- wrqu.data.length = rem;
-
- if (wrqu.data.length)
- /* Send event to user space */
- wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
- }
-}
-
-static void orinoco_send_wevents(struct work_struct *work)
-{
- struct orinoco_private *priv =
- container_of(work, struct orinoco_private, wevent_work);
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return;
-
- orinoco_send_assocreqie_wevent(priv);
- orinoco_send_assocrespie_wevent(priv);
- orinoco_send_bssid_wevent(priv);
-
- orinoco_unlock(priv, &flags);
-}
-
-static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
- unsigned long scan_age)
-{
- if (priv->has_ext_scan) {
- struct xbss_element *bss;
- struct xbss_element *tmp_bss;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
- if (!scan_age ||
- time_after(jiffies, bss->last_scanned + scan_age)) {
- list_move_tail(&bss->list,
- &priv->bss_free_list);
- /* Don't blow away ->list, just BSS data */
- memset(&bss->bss, 0, sizeof(bss->bss));
- bss->last_scanned = 0;
- }
- }
- } else {
- struct bss_element *bss;
- struct bss_element *tmp_bss;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
- if (!scan_age ||
- time_after(jiffies, bss->last_scanned + scan_age)) {
- list_move_tail(&bss->list,
- &priv->bss_free_list);
- /* Don't blow away ->list, just BSS data */
- memset(&bss->bss, 0, sizeof(bss->bss));
- bss->last_scanned = 0;
- }
- }
- }
-}
-
-static void orinoco_add_ext_scan_result(struct orinoco_private *priv,
- struct agere_ext_scan_info *atom)
-{
- struct xbss_element *bss = NULL;
- int found = 0;
-
- /* Try to update an existing bss first */
- list_for_each_entry(bss, &priv->bss_list, list) {
- if (compare_ether_addr(bss->bss.bssid, atom->bssid))
- continue;
- /* ESSID lengths */
- if (bss->bss.data[1] != atom->data[1])
- continue;
- if (memcmp(&bss->bss.data[2], &atom->data[2],
- atom->data[1]))
- continue;
- found = 1;
- break;
- }
-
- /* Grab a bss off the free list */
- if (!found && !list_empty(&priv->bss_free_list)) {
- bss = list_entry(priv->bss_free_list.next,
- struct xbss_element, list);
- list_del(priv->bss_free_list.next);
-
- list_add_tail(&bss->list, &priv->bss_list);
- }
-
- if (bss) {
- /* Always update the BSS to get latest beacon info */
- memcpy(&bss->bss, atom, sizeof(bss->bss));
- bss->last_scanned = jiffies;
- }
-}
-
-static int orinoco_process_scan_results(struct net_device *dev,
- unsigned char *buf,
- int len)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int offset; /* In the scan data */
- union hermes_scan_info *atom;
- int atom_len;
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- atom_len = sizeof(struct agere_scan_apinfo);
- offset = 0;
- break;
- case FIRMWARE_TYPE_SYMBOL:
- /* Lack of documentation necessitates this hack.
- * Different firmwares have 68 or 76 byte long atoms.
- * We try modulo first. If the length divides by both,
- * we check what would be the channel in the second
- * frame for a 68-byte atom. 76-byte atoms have 0 there.
- * Valid channel cannot be 0. */
- if (len % 76)
- atom_len = 68;
- else if (len % 68)
- atom_len = 76;
- else if (len >= 1292 && buf[68] == 0)
- atom_len = 76;
- else
- atom_len = 68;
- offset = 0;
- break;
- case FIRMWARE_TYPE_INTERSIL:
- offset = 4;
- if (priv->has_hostscan) {
- atom_len = le16_to_cpup((__le16 *)buf);
- /* Sanity check for atom_len */
- if (atom_len < sizeof(struct prism2_scan_apinfo)) {
- printk(KERN_ERR "%s: Invalid atom_len in scan "
- "data: %d\n", dev->name, atom_len);
- return -EIO;
- }
- } else
- atom_len = offsetof(struct prism2_scan_apinfo, atim);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- /* Check that we got an whole number of atoms */
- if ((len - offset) % atom_len) {
- printk(KERN_ERR "%s: Unexpected scan data length %d, "
- "atom_len %d, offset %d\n", dev->name, len,
- atom_len, offset);
- return -EIO;
- }
-
- orinoco_clear_scan_results(priv, msecs_to_jiffies(15000));
-
- /* Read the entries one by one */
- for (; offset + atom_len <= len; offset += atom_len) {
- int found = 0;
- struct bss_element *bss = NULL;
-
- /* Get next atom */
- atom = (union hermes_scan_info *) (buf + offset);
-
- /* Try to update an existing bss first */
- list_for_each_entry(bss, &priv->bss_list, list) {
- if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid))
- continue;
- if (le16_to_cpu(bss->bss.a.essid_len) !=
- le16_to_cpu(atom->a.essid_len))
- continue;
- if (memcmp(bss->bss.a.essid, atom->a.essid,
- le16_to_cpu(atom->a.essid_len)))
- continue;
- found = 1;
- break;
- }
-
- /* Grab a bss off the free list */
- if (!found && !list_empty(&priv->bss_free_list)) {
- bss = list_entry(priv->bss_free_list.next,
- struct bss_element, list);
- list_del(priv->bss_free_list.next);
-
- list_add_tail(&bss->list, &priv->bss_list);
- }
-
- if (bss) {
- /* Always update the BSS to get latest beacon info */
- memcpy(&bss->bss, atom, sizeof(bss->bss));
- bss->last_scanned = jiffies;
- }
- }
-
- return 0;
-}
-
-static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- u16 infofid;
- struct {
- __le16 len;
- __le16 type;
- } __attribute__ ((packed)) info;
- int len, type;
- int err;
-
- /* This is an answer to an INQUIRE command that we did earlier,
- * or an information "event" generated by the card
- * The controller return to us a pseudo frame containing
- * the information in question - Jean II */
- infofid = hermes_read_regn(hw, INFOFID);
-
- /* Read the info frame header - don't try too hard */
- err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
- infofid, 0);
- if (err) {
- printk(KERN_ERR "%s: error %d reading info frame. "
- "Frame dropped.\n", dev->name, err);
- return;
- }
-
- len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
- type = le16_to_cpu(info.type);
-
- switch (type) {
- case HERMES_INQ_TALLIES: {
- struct hermes_tallies_frame tallies;
- struct iw_statistics *wstats = &priv->wstats;
-
- if (len > sizeof(tallies)) {
- printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
- dev->name, len);
- len = sizeof(tallies);
- }
-
- err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
- infofid, sizeof(info));
- if (err)
- break;
-
- /* Increment our various counters */
- /* wstats->discard.nwid - no wrong BSSID stuff */
- wstats->discard.code +=
- le16_to_cpu(tallies.RxWEPUndecryptable);
- if (len == sizeof(tallies))
- wstats->discard.code +=
- le16_to_cpu(tallies.RxDiscards_WEPICVError) +
- le16_to_cpu(tallies.RxDiscards_WEPExcluded);
- wstats->discard.misc +=
- le16_to_cpu(tallies.TxDiscardsWrongSA);
- wstats->discard.fragment +=
- le16_to_cpu(tallies.RxMsgInBadMsgFragments);
- wstats->discard.retries +=
- le16_to_cpu(tallies.TxRetryLimitExceeded);
- /* wstats->miss.beacon - no match */
- }
- break;
- case HERMES_INQ_LINKSTATUS: {
- struct hermes_linkstatus linkstatus;
- u16 newstatus;
- int connected;
-
- if (priv->iw_mode == IW_MODE_MONITOR)
- break;
-
- if (len != sizeof(linkstatus)) {
- printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
- dev->name, len);
- break;
- }
-
- err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
- infofid, sizeof(info));
- if (err)
- break;
- newstatus = le16_to_cpu(linkstatus.linkstatus);
-
- /* Symbol firmware uses "out of range" to signal that
- * the hostscan frame can be requested. */
- if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
- priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
- priv->has_hostscan && priv->scan_inprogress) {
- hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
- break;
- }
-
- connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
- || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
- || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
-
- if (connected)
- netif_carrier_on(dev);
- else if (!ignore_disconnect)
- netif_carrier_off(dev);
-
- if (newstatus != priv->last_linkstatus) {
- priv->last_linkstatus = newstatus;
- print_linkstatus(dev, newstatus);
- /* The info frame contains only one word which is the
- * status (see hermes.h). The status is pretty boring
- * in itself, that's why we export the new BSSID...
- * Jean II */
- schedule_work(&priv->wevent_work);
- }
- }
- break;
- case HERMES_INQ_SCAN:
- if (!priv->scan_inprogress && priv->bssid_fixed &&
- priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
- schedule_work(&priv->join_work);
- break;
- }
- /* fall through */
- case HERMES_INQ_HOSTSCAN:
- case HERMES_INQ_HOSTSCAN_SYMBOL: {
- /* Result of a scanning. Contains information about
- * cells in the vicinity - Jean II */
- union iwreq_data wrqu;
- unsigned char *buf;
-
- /* Scan is no longer in progress */
- priv->scan_inprogress = 0;
-
- /* Sanity check */
- if (len > 4096) {
- printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
- dev->name, len);
- break;
- }
-
- /* Allocate buffer for results */
- buf = kmalloc(len, GFP_ATOMIC);
- if (buf == NULL)
- /* No memory, so can't printk()... */
- break;
-
- /* Read scan data */
- err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
- infofid, sizeof(info));
- if (err) {
- kfree(buf);
- break;
- }
-
-#ifdef ORINOCO_DEBUG
- {
- int i;
- printk(KERN_DEBUG "Scan result [%02X", buf[0]);
- for(i = 1; i < (len * 2); i++)
- printk(":%02X", buf[i]);
- printk("]\n");
- }
-#endif /* ORINOCO_DEBUG */
-
- if (orinoco_process_scan_results(dev, buf, len) == 0) {
- /* Send an empty event to user space.
- * We don't send the received data on the event because
- * it would require us to do complex transcoding, and
- * we want to minimise the work done in the irq handler
- * Use a request to extract the data - Jean II */
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
- }
- kfree(buf);
- }
- break;
- case HERMES_INQ_CHANNELINFO:
- {
- struct agere_ext_scan_info *bss;
-
- if (!priv->scan_inprogress) {
- printk(KERN_DEBUG "%s: Got chaninfo without scan, "
- "len=%d\n", dev->name, len);
- break;
- }
-
- /* An empty result indicates that the scan is complete */
- if (len == 0) {
- union iwreq_data wrqu;
-
- /* Scan is no longer in progress */
- priv->scan_inprogress = 0;
-
- wrqu.data.length = 0;
- wrqu.data.flags = 0;
- wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
- break;
- }
-
- /* Sanity check */
- else if (len > sizeof(*bss)) {
- printk(KERN_WARNING
- "%s: Ext scan results too large (%d bytes). "
- "Truncating results to %zd bytes.\n",
- dev->name, len, sizeof(*bss));
- len = sizeof(*bss);
- } else if (len < (offsetof(struct agere_ext_scan_info,
- data) + 2)) {
- /* Drop this result now so we don't have to
- * keep checking later */
- printk(KERN_WARNING
- "%s: Ext scan results too short (%d bytes)\n",
- dev->name, len);
- break;
- }
-
- bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
- if (bss == NULL)
- break;
-
- /* Read scan data */
- err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
- infofid, sizeof(info));
- if (err) {
- kfree(bss);
- break;
- }
-
- orinoco_add_ext_scan_result(priv, bss);
-
- kfree(bss);
- break;
- }
- case HERMES_INQ_SEC_STAT_AGERE:
- /* Security status (Agere specific) */
- /* Ignore this frame for now */
- if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
- break;
- /* fall through */
- default:
- printk(KERN_DEBUG "%s: Unknown information frame received: "
- "type 0x%04x, length %d\n", dev->name, type, len);
- /* We don't actually do anything about it */
- break;
- }
-}
-
-static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
-{
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
-}
-
-/********************************************************************/
-/* Internal hardware control routines */
-/********************************************************************/
-
-int __orinoco_up(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct hermes *hw = &priv->hw;
- int err;
-
- netif_carrier_off(dev); /* just to make sure */
-
- err = __orinoco_program_rids(dev);
- if (err) {
- printk(KERN_ERR "%s: Error %d configuring card\n",
- dev->name, err);
- return err;
- }
-
- /* Fire things up again */
- hermes_set_irqmask(hw, ORINOCO_INTEN);
- err = hermes_enable_port(hw, 0);
- if (err) {
- printk(KERN_ERR "%s: Error %d enabling MAC port\n",
- dev->name, err);
- return err;
- }
-
- netif_start_queue(dev);
-
- return 0;
-}
-
-int __orinoco_down(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct hermes *hw = &priv->hw;
- int err;
-
- netif_stop_queue(dev);
-
- if (! priv->hw_unavailable) {
- if (! priv->broken_disableport) {
- err = hermes_disable_port(hw, 0);
- if (err) {
- /* Some firmwares (e.g. Intersil 1.3.x) seem
- * to have problems disabling the port, oh
- * well, too bad. */
- printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
- dev->name, err);
- priv->broken_disableport = 1;
- }
- }
- hermes_set_irqmask(hw, 0);
- hermes_write_regn(hw, EVACK, 0xffff);
- }
-
- /* firmware will have to reassociate */
- netif_carrier_off(dev);
- priv->last_linkstatus = 0xffff;
-
- return 0;
-}
-
-static int orinoco_allocate_fid(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct hermes *hw = &priv->hw;
- int err;
-
- err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
- if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
- /* Try workaround for old Symbol firmware bug */
- printk(KERN_WARNING "%s: firmware ALLOC bug detected "
- "(old Symbol firmware?). Trying to work around... ",
- dev->name);
-
- priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
- err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
- if (err)
- printk("failed!\n");
- else
- printk("ok.\n");
- }
-
- return err;
-}
-
-int orinoco_reinit_firmware(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct hermes *hw = &priv->hw;
- int err;
-
- err = hermes_init(hw);
- if (!err)
- err = orinoco_allocate_fid(dev);
-
- return err;
-}
-
-static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
-{
- hermes_t *hw = &priv->hw;
- int err = 0;
-
- if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
- printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
- priv->ndev->name, priv->bitratemode);
- return -EINVAL;
- }
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFTXRATECONTROL,
- bitrate_table[priv->bitratemode].agere_txratectrl);
- break;
- case FIRMWARE_TYPE_INTERSIL:
- case FIRMWARE_TYPE_SYMBOL:
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFTXRATECONTROL,
- bitrate_table[priv->bitratemode].intersil_txratectrl);
- break;
- default:
- BUG();
- }
-
- return err;
-}
-
-/* Set fixed AP address */
-static int __orinoco_hw_set_wap(struct orinoco_private *priv)
-{
- int roaming_flag;
- int err = 0;
- hermes_t *hw = &priv->hw;
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- /* not supported */
- break;
- case FIRMWARE_TYPE_INTERSIL:
- if (priv->bssid_fixed)
- roaming_flag = 2;
- else
- roaming_flag = 1;
-
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFROAMINGMODE,
- roaming_flag);
- break;
- case FIRMWARE_TYPE_SYMBOL:
- err = HERMES_WRITE_RECORD(hw, USER_BAP,
- HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
- &priv->desired_bssid);
- break;
- }
- return err;
-}
-
-/* Change the WEP keys and/or the current keys. Can be called
- * either from __orinoco_hw_setup_enc() or directly from
- * orinoco_ioctl_setiwencode(). In the later case the association
- * with the AP is not broken (if the firmware can handle it),
- * which is needed for 802.1x implementations. */
-static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
-{
- hermes_t *hw = &priv->hw;
- int err = 0;
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- err = HERMES_WRITE_RECORD(hw, USER_BAP,
- HERMES_RID_CNFWEPKEYS_AGERE,
- &priv->keys);
- if (err)
- return err;
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFTXKEY_AGERE,
- priv->tx_key);
- if (err)
- return err;
- break;
- case FIRMWARE_TYPE_INTERSIL:
- case FIRMWARE_TYPE_SYMBOL:
- {
- int keylen;
- int i;
-
- /* Force uniform key length to work around firmware bugs */
- keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
-
- if (keylen > LARGE_KEY_SIZE) {
- printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
- priv->ndev->name, priv->tx_key, keylen);
- return -E2BIG;
- }
-
- /* Write all 4 keys */
- for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
- err = hermes_write_ltv(hw, USER_BAP,
- HERMES_RID_CNFDEFAULTKEY0 + i,
- HERMES_BYTES_TO_RECLEN(keylen),
- priv->keys[i].data);
- if (err)
- return err;
- }
-
- /* Write the index of the key used in transmission */
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFWEPDEFAULTKEYID,
- priv->tx_key);
- if (err)
- return err;
- }
- break;
- }
-
- return 0;
-}
-
-static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
-{
- hermes_t *hw = &priv->hw;
- int err = 0;
- int master_wep_flag;
- int auth_flag;
- int enc_flag;
-
- /* Setup WEP keys for WEP and WPA */
- if (priv->encode_alg)
- __orinoco_hw_setup_wepkeys(priv);
-
- if (priv->wep_restrict)
- auth_flag = HERMES_AUTH_SHARED_KEY;
- else
- auth_flag = HERMES_AUTH_OPEN;
-
- if (priv->wpa_enabled)
- enc_flag = 2;
- else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
- enc_flag = 1;
- else
- enc_flag = 0;
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
- if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
- /* Enable the shared-key authentication. */
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFAUTHENTICATION_AGERE,
- auth_flag);
- }
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFWEPENABLED_AGERE,
- enc_flag);
- if (err)
- return err;
-
- if (priv->has_wpa) {
- /* Set WPA key management */
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
- priv->key_mgmt);
- if (err)
- return err;
- }
-
- break;
-
- case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
- case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
- if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
- if (priv->wep_restrict ||
- (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
- master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
- HERMES_WEP_EXCL_UNENCRYPTED;
- else
- master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
-
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFAUTHENTICATION,
- auth_flag);
- if (err)
- return err;
- } else
- master_wep_flag = 0;
-
- if (priv->iw_mode == IW_MODE_MONITOR)
- master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
-
- /* Master WEP setting : on/off */
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFWEPFLAGS_INTERSIL,
- master_wep_flag);
- if (err)
- return err;
-
- break;
- }
-
- return 0;
-}
-
-/* key must be 32 bytes, including the tx and rx MIC keys.
- * rsc must be 8 bytes
- * tsc must be 8 bytes or NULL
- */
-static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
- u8 *key, u8 *rsc, u8 *tsc)
-{
- struct {
- __le16 idx;
- u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
- u8 key[TKIP_KEYLEN];
- u8 tx_mic[MIC_KEYLEN];
- u8 rx_mic[MIC_KEYLEN];
- u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
- } __attribute__ ((packed)) buf;
- int ret;
- int err;
- int k;
- u16 xmitting;
-
- key_idx &= 0x3;
-
- if (set_tx)
- key_idx |= 0x8000;
-
- buf.idx = cpu_to_le16(key_idx);
- memcpy(buf.key, key,
- sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
-
- if (rsc == NULL)
- memset(buf.rsc, 0, sizeof(buf.rsc));
- else
- memcpy(buf.rsc, rsc, sizeof(buf.rsc));
-
- if (tsc == NULL) {
- memset(buf.tsc, 0, sizeof(buf.tsc));
- buf.tsc[4] = 0x10;
- } else {
- memcpy(buf.tsc, tsc, sizeof(buf.tsc));
- }
-
- /* Wait upto 100ms for tx queue to empty */
- k = 100;
- do {
- k--;
- udelay(1000);
- ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
- &xmitting);
- if (ret)
- break;
- } while ((k > 0) && xmitting);
-
- if (k == 0)
- ret = -ETIMEDOUT;
-
- err = HERMES_WRITE_RECORD(hw, USER_BAP,
- HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
- &buf);
-
- return ret ? ret : err;
-}
-
-static int orinoco_clear_tkip_key(struct orinoco_private *priv,
- int key_idx)
-{
- hermes_t *hw = &priv->hw;
- int err;
-
- memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
- key_idx);
- if (err)
- printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
- priv->ndev->name, err, key_idx);
- return err;
-}
-
-static int __orinoco_program_rids(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err;
- struct hermes_idstring idbuf;
-
- /* Set the MAC address */
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
- HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting MAC address\n",
- dev->name, err);
- return err;
- }
-
- /* Set up the link mode */
- err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
- priv->port_type);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting port type\n",
- dev->name, err);
- return err;
- }
- /* Set the channel/frequency */
- if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFOWNCHANNEL,
- priv->channel);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting channel %d\n",
- dev->name, err, priv->channel);
- return err;
- }
- }
-
- if (priv->has_ibss) {
- u16 createibss;
-
- if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
- printk(KERN_WARNING "%s: This firmware requires an "
- "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
- /* With wvlan_cs, in this case, we would crash.
- * hopefully, this driver will behave better...
- * Jean II */
- createibss = 0;
- } else {
- createibss = priv->createibss;
- }
-
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFCREATEIBSS,
- createibss);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
- dev->name, err);
- return err;
- }
- }
-
- /* Set the desired BSSID */
- err = __orinoco_hw_set_wap(priv);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting AP address\n",
- dev->name, err);
- return err;
- }
- /* Set the desired ESSID */
- idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
- memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
- /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
- HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
- &idbuf);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
- dev->name, err);
- return err;
- }
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
- HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
- &idbuf);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
- dev->name, err);
- return err;
- }
-
- /* Set the station name */
- idbuf.len = cpu_to_le16(strlen(priv->nick));
- memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
- HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
- &idbuf);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting nickname\n",
- dev->name, err);
- return err;
- }
-
- /* Set AP density */
- if (priv->has_sensitivity) {
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFSYSTEMSCALE,
- priv->ap_density);
- if (err) {
- printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
- "Disabling sensitivity control\n",
- dev->name, err);
-
- priv->has_sensitivity = 0;
- }
- }
-
- /* Set RTS threshold */
- err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
- priv->rts_thresh);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
- dev->name, err);
- return err;
- }
-
- /* Set fragmentation threshold or MWO robustness */
- if (priv->has_mwo)
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMWOROBUST_AGERE,
- priv->mwo_robust);
- else
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
- priv->frag_thresh);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting fragmentation\n",
- dev->name, err);
- return err;
- }
-
- /* Set bitrate */
- err = __orinoco_hw_set_bitrate(priv);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting bitrate\n",
- dev->name, err);
- return err;
- }
-
- /* Set power management */
- if (priv->has_pm) {
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPMENABLED,
- priv->pm_on);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting up PM\n",
- dev->name, err);
- return err;
- }
-
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMULTICASTRECEIVE,
- priv->pm_mcast);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting up PM\n",
- dev->name, err);
- return err;
- }
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMAXSLEEPDURATION,
- priv->pm_period);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting up PM\n",
- dev->name, err);
- return err;
- }
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPMHOLDOVERDURATION,
- priv->pm_timeout);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting up PM\n",
- dev->name, err);
- return err;
- }
- }
-
- /* Set preamble - only for Symbol so far... */
- if (priv->has_preamble) {
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPREAMBLE_SYMBOL,
- priv->preamble);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting preamble\n",
- dev->name, err);
- return err;
- }
- }
-
- /* Set up encryption */
- if (priv->has_wep || priv->has_wpa) {
- err = __orinoco_hw_setup_enc(priv);
- if (err) {
- printk(KERN_ERR "%s: Error %d activating encryption\n",
- dev->name, err);
- return err;
- }
- }
-
- if (priv->iw_mode == IW_MODE_MONITOR) {
- /* Enable monitor mode */
- dev->type = ARPHRD_IEEE80211;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
- HERMES_TEST_MONITOR, 0, NULL);
- } else {
- /* Disable monitor mode */
- dev->type = ARPHRD_ETHER;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
- HERMES_TEST_STOP, 0, NULL);
- }
- if (err)
- return err;
-
- /* Set promiscuity / multicast*/
- priv->promiscuous = 0;
- priv->mc_count = 0;
-
- /* FIXME: what about netif_tx_lock */
- __orinoco_set_multicast_list(dev);
-
- return 0;
-}
-
-/* FIXME: return int? */
-static void
-__orinoco_set_multicast_list(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- int promisc, mc_count;
-
- /* The Hermes doesn't seem to have an allmulti mode, so we go
- * into promiscuous mode and let the upper levels deal. */
- if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
- (dev->mc_count > MAX_MULTICAST(priv)) ) {
- promisc = 1;
- mc_count = 0;
- } else {
- promisc = 0;
- mc_count = dev->mc_count;
- }
-
- if (promisc != priv->promiscuous) {
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPROMISCUOUSMODE,
- promisc);
- if (err) {
- printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
- dev->name, err);
- } else
- priv->promiscuous = promisc;
- }
-
- /* If we're not in promiscuous mode, then we need to set the
- * group address if either we want to multicast, or if we were
- * multicasting and want to stop */
- if (! promisc && (mc_count || priv->mc_count) ) {
- struct dev_mc_list *p = dev->mc_list;
- struct hermes_multicast mclist;
- int i;
-
- for (i = 0; i < mc_count; i++) {
- /* paranoia: is list shorter than mc_count? */
- BUG_ON(! p);
- /* paranoia: bad address size in list? */
- BUG_ON(p->dmi_addrlen != ETH_ALEN);
-
- memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
- p = p->next;
- }
-
- if (p)
- printk(KERN_WARNING "%s: Multicast list is "
- "longer than mc_count\n", dev->name);
-
- err = hermes_write_ltv(hw, USER_BAP,
- HERMES_RID_CNFGROUPADDRESSES,
- HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
- &mclist);
- if (err)
- printk(KERN_ERR "%s: Error %d setting multicast list.\n",
- dev->name, err);
- else
- priv->mc_count = mc_count;
- }
-}
-
-/* This must be called from user context, without locks held - use
- * schedule_work() */
-static void orinoco_reset(struct work_struct *work)
-{
- struct orinoco_private *priv =
- container_of(work, struct orinoco_private, reset_work);
- struct net_device *dev = priv->ndev;
- struct hermes *hw = &priv->hw;
- int err;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- /* When the hardware becomes available again, whatever
- * detects that is responsible for re-initializing
- * it. So no need for anything further */
- return;
-
- netif_stop_queue(dev);
-
- /* Shut off interrupts. Depending on what state the hardware
- * is in, this might not work, but we'll try anyway */
- hermes_set_irqmask(hw, 0);
- hermes_write_regn(hw, EVACK, 0xffff);
-
- priv->hw_unavailable++;
- priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
- netif_carrier_off(dev);
-
- orinoco_unlock(priv, &flags);
-
- /* Scanning support: Cleanup of driver struct */
- orinoco_clear_scan_results(priv, 0);
- priv->scan_inprogress = 0;
-
- if (priv->hard_reset) {
- err = (*priv->hard_reset)(priv);
- if (err) {
- printk(KERN_ERR "%s: orinoco_reset: Error %d "
- "performing hard reset\n", dev->name, err);
- goto disable;
- }
- }
-
- if (priv->do_fw_download) {
- err = orinoco_download(priv);
- if (err)
- priv->do_fw_download = 0;
- }
-
- err = orinoco_reinit_firmware(dev);
- if (err) {
- printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
- dev->name, err);
- goto disable;
- }
-
- spin_lock_irq(&priv->lock); /* This has to be called from user context */
-
- priv->hw_unavailable--;
-
- /* priv->open or priv->hw_unavailable might have changed while
- * we dropped the lock */
- if (priv->open && (! priv->hw_unavailable)) {
- err = __orinoco_up(dev);
- if (err) {
- printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
- dev->name, err);
- } else
- dev->trans_start = jiffies;
- }
-
- spin_unlock_irq(&priv->lock);
-
- return;
- disable:
- hermes_set_irqmask(hw, 0);
- netif_device_detach(dev);
- printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
-}
-
-/********************************************************************/
-/* Interrupt handler */
-/********************************************************************/
-
-static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
-{
- printk(KERN_DEBUG "%s: TICK\n", dev->name);
-}
-
-static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
-{
- /* This seems to happen a fair bit under load, but ignoring it
- seems to work fine...*/
- printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
- dev->name);
-}
-
-irqreturn_t orinoco_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int count = MAX_IRQLOOPS_PER_IRQ;
- u16 evstat, events;
- /* These are used to detect a runaway interrupt situation */
- /* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
- * we panic and shut down the hardware */
- static int last_irq_jiffy = 0; /* jiffies value the last time
- * we were called */
- static int loops_this_jiffy = 0;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0) {
- /* If hw is unavailable - we don't know if the irq was
- * for us or not */
- return IRQ_HANDLED;
- }
-
- evstat = hermes_read_regn(hw, EVSTAT);
- events = evstat & hw->inten;
- if (! events) {
- orinoco_unlock(priv, &flags);
- return IRQ_NONE;
- }
-
- if (jiffies != last_irq_jiffy)
- loops_this_jiffy = 0;
- last_irq_jiffy = jiffies;
-
- while (events && count--) {
- if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
- printk(KERN_WARNING "%s: IRQ handler is looping too "
- "much! Resetting.\n", dev->name);
- /* Disable interrupts for now */
- hermes_set_irqmask(hw, 0);
- schedule_work(&priv->reset_work);
- break;
- }
-
- /* Check the card hasn't been removed */
- if (! hermes_present(hw)) {
- DEBUG(0, "orinoco_interrupt(): card removed\n");
- break;
- }
-
- if (events & HERMES_EV_TICK)
- __orinoco_ev_tick(dev, hw);
- if (events & HERMES_EV_WTERR)
- __orinoco_ev_wterr(dev, hw);
- if (events & HERMES_EV_INFDROP)
- __orinoco_ev_infdrop(dev, hw);
- if (events & HERMES_EV_INFO)
- __orinoco_ev_info(dev, hw);
- if (events & HERMES_EV_RX)
- __orinoco_ev_rx(dev, hw);
- if (events & HERMES_EV_TXEXC)
- __orinoco_ev_txexc(dev, hw);
- if (events & HERMES_EV_TX)
- __orinoco_ev_tx(dev, hw);
- if (events & HERMES_EV_ALLOC)
- __orinoco_ev_alloc(dev, hw);
-
- hermes_write_regn(hw, EVACK, evstat);
-
- evstat = hermes_read_regn(hw, EVSTAT);
- events = evstat & hw->inten;
- };
-
- orinoco_unlock(priv, &flags);
- return IRQ_HANDLED;
-}
-
-/********************************************************************/
-/* Initialization */
-/********************************************************************/
-
-struct comp_id {
- u16 id, variant, major, minor;
-} __attribute__ ((packed));
-
-static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
-{
- if (nic_id->id < 0x8000)
- return FIRMWARE_TYPE_AGERE;
- else if (nic_id->id == 0x8000 && nic_id->major == 0)
- return FIRMWARE_TYPE_SYMBOL;
- else
- return FIRMWARE_TYPE_INTERSIL;
-}
-
-/* Set priv->firmware type, determine firmware properties */
-static int determine_firmware(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err;
- struct comp_id nic_id, sta_id;
- unsigned int firmver;
- char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
-
- /* Get the hardware version */
- err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
- if (err) {
- printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
- dev->name, err);
- return err;
- }
-
- le16_to_cpus(&nic_id.id);
- le16_to_cpus(&nic_id.variant);
- le16_to_cpus(&nic_id.major);
- le16_to_cpus(&nic_id.minor);
- printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
- dev->name, nic_id.id, nic_id.variant,
- nic_id.major, nic_id.minor);
-
- priv->firmware_type = determine_firmware_type(&nic_id);
-
- /* Get the firmware version */
- err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
- if (err) {
- printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
- dev->name, err);
- return err;
- }
-
- le16_to_cpus(&sta_id.id);
- le16_to_cpus(&sta_id.variant);
- le16_to_cpus(&sta_id.major);
- le16_to_cpus(&sta_id.minor);
- printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
- dev->name, sta_id.id, sta_id.variant,
- sta_id.major, sta_id.minor);
-
- switch (sta_id.id) {
- case 0x15:
- printk(KERN_ERR "%s: Primary firmware is active\n",
- dev->name);
- return -ENODEV;
- case 0x14b:
- printk(KERN_ERR "%s: Tertiary firmware is active\n",
- dev->name);
- return -ENODEV;
- case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
- case 0x21: /* Symbol Spectrum24 Trilogy */
- break;
- default:
- printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
- dev->name);
- break;
- }
-
- /* Default capabilities */
- priv->has_sensitivity = 1;
- priv->has_mwo = 0;
- priv->has_preamble = 0;
- priv->has_port3 = 1;
- priv->has_ibss = 1;
- priv->has_wep = 0;
- priv->has_big_wep = 0;
- priv->has_alt_txcntl = 0;
- priv->has_ext_scan = 0;
- priv->has_wpa = 0;
- priv->do_fw_download = 0;
-
- /* Determine capabilities from the firmware version */
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE:
- /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
- ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
-
- firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
-
- priv->has_ibss = (firmver >= 0x60006);
- priv->has_wep = (firmver >= 0x40020);
- priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
- Gold cards from the others? */
- priv->has_mwo = (firmver >= 0x60000);
- priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
- priv->ibss_port = 1;
- priv->has_hostscan = (firmver >= 0x8000a);
- priv->do_fw_download = 1;
- priv->broken_monitor = (firmver >= 0x80000);
- priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
- priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
- priv->has_wpa = (firmver >= 0x9002a);
- /* Tested with Agere firmware :
- * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
- * Tested CableTron firmware : 4.32 => Anton */
- break;
- case FIRMWARE_TYPE_SYMBOL:
- /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
- /* Intel MAC : 00:02:B3:* */
- /* 3Com MAC : 00:50:DA:* */
- memset(tmp, 0, sizeof(tmp));
- /* Get the Symbol firmware version */
- err = hermes_read_ltv(hw, USER_BAP,
- HERMES_RID_SECONDARYVERSION_SYMBOL,
- SYMBOL_MAX_VER_LEN, NULL, &tmp);
- if (err) {
- printk(KERN_WARNING
- "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n",
- dev->name, err);
- firmver = 0;
- tmp[0] = '\0';
- } else {
- /* The firmware revision is a string, the format is
- * something like : "V2.20-01".
- * Quick and dirty parsing... - Jean II
- */
- firmver = ((tmp[1] - '0') << 16) | ((tmp[3] - '0') << 12)
- | ((tmp[4] - '0') << 8) | ((tmp[6] - '0') << 4)
- | (tmp[7] - '0');
-
- tmp[SYMBOL_MAX_VER_LEN] = '\0';
- }
-
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Symbol %s", tmp);
-
- priv->has_ibss = (firmver >= 0x20000);
- priv->has_wep = (firmver >= 0x15012);
- priv->has_big_wep = (firmver >= 0x20000);
- priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
- (firmver >= 0x29000 && firmver < 0x30000) ||
- firmver >= 0x31000;
- priv->has_preamble = (firmver >= 0x20000);
- priv->ibss_port = 4;
-
- /* Symbol firmware is found on various cards, but
- * there has been no attempt to check firmware
- * download on non-spectrum_cs based cards.
- *
- * Given that the Agere firmware download works
- * differently, we should avoid doing a firmware
- * download with the Symbol algorithm on non-spectrum
- * cards.
- *
- * For now we can identify a spectrum_cs based card
- * because it has a firmware reset function.
- */
- priv->do_fw_download = (priv->stop_fw != NULL);
-
- priv->broken_disableport = (firmver == 0x25013) ||
- (firmver >= 0x30000 && firmver <= 0x31000);
- priv->has_hostscan = (firmver >= 0x31001) ||
- (firmver >= 0x29057 && firmver < 0x30000);
- /* Tested with Intel firmware : 0x20015 => Jean II */
- /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
- break;
- case FIRMWARE_TYPE_INTERSIL:
- /* D-Link, Linksys, Adtron, ZoomAir, and many others...
- * Samsung, Compaq 100/200 and Proxim are slightly
- * different and less well tested */
- /* D-Link MAC : 00:40:05:* */
- /* Addtron MAC : 00:90:D1:* */
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
- sta_id.variant);
-
- firmver = ((unsigned long)sta_id.major << 16) |
- ((unsigned long)sta_id.minor << 8) | sta_id.variant;
-
- priv->has_ibss = (firmver >= 0x000700); /* FIXME */
- priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
- priv->has_pm = (firmver >= 0x000700);
- priv->has_hostscan = (firmver >= 0x010301);
-
- if (firmver >= 0x000800)
- priv->ibss_port = 0;
- else {
- printk(KERN_NOTICE "%s: Intersil firmware earlier "
- "than v0.8.x - several features not supported\n",
- dev->name);
- priv->ibss_port = 1;
- }
- break;
- }
- printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
- priv->fw_name);
-
- return 0;
-}
-
-static int orinoco_init(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- struct hermes_idstring nickbuf;
- u16 reclen;
- int len;
- DECLARE_MAC_BUF(mac);
-
- /* No need to lock, the hw_unavailable flag is already set in
- * alloc_orinocodev() */
- priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
-
- /* Initialize the firmware */
- err = hermes_init(hw);
- if (err != 0) {
- printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
- dev->name, err);
- goto out;
- }
-
- err = determine_firmware(dev);
- if (err != 0) {
- printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
- dev->name);
- goto out;
- }
-
- if (priv->do_fw_download) {
- err = orinoco_download(priv);
- if (err)
- priv->do_fw_download = 0;
-
- /* Check firmware version again */
- err = determine_firmware(dev);
- if (err != 0) {
- printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
- dev->name);
- goto out;
- }
- }
-
- if (priv->has_port3)
- printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
- if (priv->has_ibss)
- printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
- dev->name);
- if (priv->has_wep) {
- printk(KERN_DEBUG "%s: WEP supported, ", dev->name);
- if (priv->has_big_wep)
- printk("104-bit key\n");
- else
- printk("40-bit key\n");
- }
- if (priv->has_wpa) {
- printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
- if (orinoco_mic_init(priv)) {
- printk(KERN_ERR "%s: Failed to setup MIC crypto "
- "algorithm. Disabling WPA support\n", dev->name);
- priv->has_wpa = 0;
- }
- }
-
- /* Now we have the firmware capabilities, allocate appropiate
- * sized scan buffers */
- if (orinoco_bss_data_allocate(priv))
- goto out;
- orinoco_bss_data_init(priv);
-
- /* Get the MAC address */
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
- ETH_ALEN, NULL, dev->dev_addr);
- if (err) {
- printk(KERN_WARNING "%s: failed to read MAC address!\n",
- dev->name);
- goto out;
- }
-
- printk(KERN_DEBUG "%s: MAC address %s\n",
- dev->name, print_mac(mac, dev->dev_addr));
-
- /* Get the station name */
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
- sizeof(nickbuf), &reclen, &nickbuf);
- if (err) {
- printk(KERN_ERR "%s: failed to read station name\n",
- dev->name);
- goto out;
- }
- if (nickbuf.len)
- len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
- else
- len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
- memcpy(priv->nick, &nickbuf.val, len);
- priv->nick[len] = '\0';
-
- printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
-
- err = orinoco_allocate_fid(dev);
- if (err) {
- printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
- dev->name);
- goto out;
- }
-
- /* Get allowed channels */
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
- &priv->channel_mask);
- if (err) {
- printk(KERN_ERR "%s: failed to read channel list!\n",
- dev->name);
- goto out;
- }
-
- /* Get initial AP density */
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
- &priv->ap_density);
- if (err || priv->ap_density < 1 || priv->ap_density > 3) {
- priv->has_sensitivity = 0;
- }
-
- /* Get initial RTS threshold */
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
- &priv->rts_thresh);
- if (err) {
- printk(KERN_ERR "%s: failed to read RTS threshold!\n",
- dev->name);
- goto out;
- }
-
- /* Get initial fragmentation settings */
- if (priv->has_mwo)
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMWOROBUST_AGERE,
- &priv->mwo_robust);
- else
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
- &priv->frag_thresh);
- if (err) {
- printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
- dev->name);
- goto out;
- }
-
- /* Power management setup */
- if (priv->has_pm) {
- priv->pm_on = 0;
- priv->pm_mcast = 1;
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMAXSLEEPDURATION,
- &priv->pm_period);
- if (err) {
- printk(KERN_ERR "%s: failed to read power management period!\n",
- dev->name);
- goto out;
- }
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPMHOLDOVERDURATION,
- &priv->pm_timeout);
- if (err) {
- printk(KERN_ERR "%s: failed to read power management timeout!\n",
- dev->name);
- goto out;
- }
- }
-
- /* Preamble setup */
- if (priv->has_preamble) {
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFPREAMBLE_SYMBOL,
- &priv->preamble);
- if (err)
- goto out;
- }
-
- /* Set up the default configuration */
- priv->iw_mode = IW_MODE_INFRA;
- /* By default use IEEE/IBSS ad-hoc mode if we have it */
- priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss);
- set_port_type(priv);
- priv->channel = 0; /* use firmware default */
-
- priv->promiscuous = 0;
- priv->encode_alg = IW_ENCODE_ALG_NONE;
- priv->tx_key = 0;
- priv->wpa_enabled = 0;
- priv->tkip_cm_active = 0;
- priv->key_mgmt = 0;
- priv->wpa_ie_len = 0;
- priv->wpa_ie = NULL;
-
- /* Make the hardware available, as long as it hasn't been
- * removed elsewhere (e.g. by PCMCIA hot unplug) */
- spin_lock_irq(&priv->lock);
- priv->hw_unavailable--;
- spin_unlock_irq(&priv->lock);
-
- printk(KERN_DEBUG "%s: ready\n", dev->name);
-
- out:
- return err;
-}
-
-struct net_device
-*alloc_orinocodev(int sizeof_card,
- struct device *device,
- int (*hard_reset)(struct orinoco_private *),
- int (*stop_fw)(struct orinoco_private *, int))
-{
- struct net_device *dev;
- struct orinoco_private *priv;
-
- dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
- if (! dev)
- return NULL;
- priv = netdev_priv(dev);
- priv->ndev = dev;
- if (sizeof_card)
- priv->card = (void *)((unsigned long)priv
- + sizeof(struct orinoco_private));
- else
- priv->card = NULL;
- priv->dev = device;
-
- /* Setup / override net_device fields */
- dev->init = orinoco_init;
- dev->hard_start_xmit = orinoco_xmit;
- dev->tx_timeout = orinoco_tx_timeout;
- dev->watchdog_timeo = HZ; /* 1 second timeout */
- dev->get_stats = orinoco_get_stats;
- dev->ethtool_ops = &orinoco_ethtool_ops;
- dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def;
-#ifdef WIRELESS_SPY
- priv->wireless_data.spy_data = &priv->spy_data;
- dev->wireless_data = &priv->wireless_data;
-#endif
- dev->change_mtu = orinoco_change_mtu;
- dev->set_multicast_list = orinoco_set_multicast_list;
- /* we use the default eth_mac_addr for setting the MAC addr */
-
- /* Reserve space in skb for the SNAP header */
- dev->hard_header_len += ENCAPS_OVERHEAD;
-
- /* Set up default callbacks */
- dev->open = orinoco_open;
- dev->stop = orinoco_stop;
- priv->hard_reset = hard_reset;
- priv->stop_fw = stop_fw;
-
- spin_lock_init(&priv->lock);
- priv->open = 0;
- priv->hw_unavailable = 1; /* orinoco_init() must clear this
- * before anything else touches the
- * hardware */
- INIT_WORK(&priv->reset_work, orinoco_reset);
- INIT_WORK(&priv->join_work, orinoco_join_ap);
- INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
-
- INIT_LIST_HEAD(&priv->rx_list);
- tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
- (unsigned long) dev);
-
- netif_carrier_off(dev);
- priv->last_linkstatus = 0xffff;
-
- return dev;
-}
-
-void free_orinocodev(struct net_device *dev)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- /* No need to empty priv->rx_list: if the tasklet is scheduled
- * when we call tasklet_kill it will run one final time,
- * emptying the list */
- tasklet_kill(&priv->rx_tasklet);
- priv->wpa_ie_len = 0;
- kfree(priv->wpa_ie);
- orinoco_mic_free(priv);
- orinoco_bss_data_free(priv);
- free_netdev(dev);
-}
-
-/********************************************************************/
-/* Wireless extensions */
-/********************************************************************/
-
-/* Return : < 0 -> error code ; >= 0 -> length */
-static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
- char buf[IW_ESSID_MAX_SIZE+1])
-{
- hermes_t *hw = &priv->hw;
- int err = 0;
- struct hermes_idstring essidbuf;
- char *p = (char *)(&essidbuf.val);
- int len;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (strlen(priv->desired_essid) > 0) {
- /* We read the desired SSID from the hardware rather
- than from priv->desired_essid, just in case the
- firmware is allowed to change it on us. I'm not
- sure about this */
- /* My guess is that the OWNSSID should always be whatever
- * we set to the card, whereas CURRENT_SSID is the one that
- * may change... - Jean II */
- u16 rid;
-
- *active = 1;
-
- rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
- HERMES_RID_CNFDESIREDSSID;
-
- err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
- NULL, &essidbuf);
- if (err)
- goto fail_unlock;
- } else {
- *active = 0;
-
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
- sizeof(essidbuf), NULL, &essidbuf);
- if (err)
- goto fail_unlock;
- }
-
- len = le16_to_cpu(essidbuf.len);
- BUG_ON(len > IW_ESSID_MAX_SIZE);
-
- memset(buf, 0, IW_ESSID_MAX_SIZE);
- memcpy(buf, p, len);
- err = len;
-
- fail_unlock:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static long orinoco_hw_get_freq(struct orinoco_private *priv)
-{
-
- hermes_t *hw = &priv->hw;
- int err = 0;
- u16 channel;
- long freq = 0;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
- if (err)
- goto out;
-
- /* Intersil firmware 1.3.5 returns 0 when the interface is down */
- if (channel == 0) {
- err = -EBUSY;
- goto out;
- }
-
- if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
- printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
- priv->ndev->name, channel);
- err = -EBUSY;
- goto out;
-
- }
- freq = channel_frequency[channel-1] * 100000;
-
- out:
- orinoco_unlock(priv, &flags);
-
- if (err > 0)
- err = -EBUSY;
- return err ? err : freq;
-}
-
-static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
- int *numrates, s32 *rates, int max)
-{
- hermes_t *hw = &priv->hw;
- struct hermes_idstring list;
- unsigned char *p = (unsigned char *)&list.val;
- int err = 0;
- int num;
- int i;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
- sizeof(list), NULL, &list);
- orinoco_unlock(priv, &flags);
-
- if (err)
- return err;
-
- num = le16_to_cpu(list.len);
- *numrates = num;
- num = min(num, max);
-
- for (i = 0; i < num; i++) {
- rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
- }
-
- return 0;
-}
-
-static int orinoco_ioctl_getname(struct net_device *dev,
- struct iw_request_info *info,
- char *name,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int numrates;
- int err;
-
- err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
-
- if (!err && (numrates > 2))
- strcpy(name, "IEEE 802.11b");
- else
- strcpy(name, "IEEE 802.11-DS");
-
- return 0;
-}
-
-static int orinoco_ioctl_setwap(struct net_device *dev,
- struct iw_request_info *info,
- struct sockaddr *ap_addr,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
- static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- /* Enable automatic roaming - no sanity checks are needed */
- if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
- memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
- priv->bssid_fixed = 0;
- memset(priv->desired_bssid, 0, ETH_ALEN);
-
- /* "off" means keep existing connection */
- if (ap_addr->sa_data[0] == 0) {
- __orinoco_hw_set_wap(priv);
- err = 0;
- }
- goto out;
- }
-
- if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
- printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
- "support manual roaming\n",
- dev->name);
- err = -EOPNOTSUPP;
- goto out;
- }
-
- if (priv->iw_mode != IW_MODE_INFRA) {
- printk(KERN_WARNING "%s: Manual roaming supported only in "
- "managed mode\n", dev->name);
- err = -EOPNOTSUPP;
- goto out;
- }
-
- /* Intersil firmware hangs without Desired ESSID */
- if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
- strlen(priv->desired_essid) == 0) {
- printk(KERN_WARNING "%s: Desired ESSID must be set for "
- "manual roaming\n", dev->name);
- err = -EOPNOTSUPP;
- goto out;
- }
-
- /* Finally, enable manual roaming */
- priv->bssid_fixed = 1;
- memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
-
- out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-static int orinoco_ioctl_getwap(struct net_device *dev,
- struct iw_request_info *info,
- struct sockaddr *ap_addr,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- hermes_t *hw = &priv->hw;
- int err = 0;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- ap_addr->sa_family = ARPHRD_ETHER;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
- ETH_ALEN, NULL, ap_addr->sa_data);
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_setmode(struct net_device *dev,
- struct iw_request_info *info,
- u32 *mode,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
-
- if (priv->iw_mode == *mode)
- return 0;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- switch (*mode) {
- case IW_MODE_ADHOC:
- if (!priv->has_ibss && !priv->has_port3)
- err = -EOPNOTSUPP;
- break;
-
- case IW_MODE_INFRA:
- break;
-
- case IW_MODE_MONITOR:
- if (priv->broken_monitor && !force_monitor) {
- printk(KERN_WARNING "%s: Monitor mode support is "
- "buggy in this firmware, not enabling\n",
- dev->name);
- err = -EOPNOTSUPP;
- }
- break;
-
- default:
- err = -EOPNOTSUPP;
- break;
- }
-
- if (err == -EINPROGRESS) {
- priv->iw_mode = *mode;
- set_port_type(priv);
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getmode(struct net_device *dev,
- struct iw_request_info *info,
- u32 *mode,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- *mode = priv->iw_mode;
- return 0;
-}
-
-static int orinoco_ioctl_getiwrange(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = 0;
- struct iw_range *range = (struct iw_range *) extra;
- int numrates;
- int i, k;
-
- rrq->length = sizeof(struct iw_range);
- memset(range, 0, sizeof(struct iw_range));
-
- range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 22;
-
- /* Set available channels/frequencies */
- range->num_channels = NUM_CHANNELS;
- k = 0;
- for (i = 0; i < NUM_CHANNELS; i++) {
- if (priv->channel_mask & (1 << i)) {
- range->freq[k].i = i + 1;
- range->freq[k].m = channel_frequency[i] * 100000;
- range->freq[k].e = 1;
- k++;
- }
-
- if (k >= IW_MAX_FREQUENCIES)
- break;
- }
- range->num_frequency = k;
- range->sensitivity = 3;
-
- if (priv->has_wep) {
- range->max_encoding_tokens = ORINOCO_MAX_KEYS;
- range->encoding_size[0] = SMALL_KEY_SIZE;
- range->num_encoding_sizes = 1;
-
- if (priv->has_big_wep) {
- range->encoding_size[1] = LARGE_KEY_SIZE;
- range->num_encoding_sizes = 2;
- }
- }
-
- if (priv->has_wpa)
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
-
- if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){
- /* Quality stats meaningless in ad-hoc mode */
- } else {
- range->max_qual.qual = 0x8b - 0x2f;
- range->max_qual.level = 0x2f - 0x95 - 1;
- range->max_qual.noise = 0x2f - 0x95 - 1;
- /* Need to get better values */
- range->avg_qual.qual = 0x24;
- range->avg_qual.level = 0xC2;
- range->avg_qual.noise = 0x9E;
- }
-
- err = orinoco_hw_get_bitratelist(priv, &numrates,
- range->bitrate, IW_MAX_BITRATES);
- if (err)
- return err;
- range->num_bitrates = numrates;
-
- /* Set an indication of the max TCP throughput in bit/s that we can
- * expect using this interface. May be use for QoS stuff...
- * Jean II */
- if (numrates > 2)
- range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
- else
- range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
-
- range->min_rts = 0;
- range->max_rts = 2347;
- range->min_frag = 256;
- range->max_frag = 2346;
-
- range->min_pmp = 0;
- range->max_pmp = 65535000;
- range->min_pmt = 0;
- range->max_pmt = 65535 * 1000; /* ??? */
- range->pmp_flags = IW_POWER_PERIOD;
- range->pmt_flags = IW_POWER_TIMEOUT;
- range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
-
- range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
- range->retry_flags = IW_RETRY_LIMIT;
- range->r_time_flags = IW_RETRY_LIFETIME;
- range->min_retry = 0;
- range->max_retry = 65535; /* ??? */
- range->min_r_time = 0;
- range->max_r_time = 65535 * 1000; /* ??? */
-
- if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
- range->scan_capa = IW_SCAN_CAPA_ESSID;
- else
- range->scan_capa = IW_SCAN_CAPA_NONE;
-
- /* Event capability (kernel) */
- IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
- /* Event capability (driver) */
- IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
- IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
- IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
- IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
-
- return 0;
-}
-
-static int orinoco_ioctl_setiwencode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq,
- char *keybuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int index = (erq->flags & IW_ENCODE_INDEX) - 1;
- int setindex = priv->tx_key;
- int encode_alg = priv->encode_alg;
- int restricted = priv->wep_restrict;
- u16 xlen = 0;
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
-
- if (! priv->has_wep)
- return -EOPNOTSUPP;
-
- if (erq->pointer) {
- /* We actually have a key to set - check its length */
- if (erq->length > LARGE_KEY_SIZE)
- return -E2BIG;
-
- if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep )
- return -E2BIG;
- }
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- /* Clear any TKIP key we have */
- if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
- (void) orinoco_clear_tkip_key(priv, setindex);
-
- if (erq->length > 0) {
- if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
- index = priv->tx_key;
-
- /* Adjust key length to a supported value */
- if (erq->length > SMALL_KEY_SIZE) {
- xlen = LARGE_KEY_SIZE;
- } else if (erq->length > 0) {
- xlen = SMALL_KEY_SIZE;
- } else
- xlen = 0;
-
- /* Switch on WEP if off */
- if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
- setindex = index;
- encode_alg = IW_ENCODE_ALG_WEP;
- }
- } else {
- /* Important note : if the user do "iwconfig eth0 enc off",
- * we will arrive there with an index of -1. This is valid
- * but need to be taken care off... Jean II */
- if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
- if((index != -1) || (erq->flags == 0)) {
- err = -EINVAL;
- goto out;
- }
- } else {
- /* Set the index : Check that the key is valid */
- if(priv->keys[index].len == 0) {
- err = -EINVAL;
- goto out;
- }
- setindex = index;
- }
- }
-
- if (erq->flags & IW_ENCODE_DISABLED)
- encode_alg = IW_ENCODE_ALG_NONE;
- if (erq->flags & IW_ENCODE_OPEN)
- restricted = 0;
- if (erq->flags & IW_ENCODE_RESTRICTED)
- restricted = 1;
-
- if (erq->pointer && erq->length > 0) {
- priv->keys[index].len = cpu_to_le16(xlen);
- memset(priv->keys[index].data, 0,
- sizeof(priv->keys[index].data));
- memcpy(priv->keys[index].data, keybuf, erq->length);
- }
- priv->tx_key = setindex;
-
- /* Try fast key change if connected and only keys are changed */
- if ((priv->encode_alg == encode_alg) &&
- (priv->wep_restrict == restricted) &&
- netif_carrier_ok(dev)) {
- err = __orinoco_hw_setup_wepkeys(priv);
- /* No need to commit if successful */
- goto out;
- }
-
- priv->encode_alg = encode_alg;
- priv->wep_restrict = restricted;
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getiwencode(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq,
- char *keybuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int index = (erq->flags & IW_ENCODE_INDEX) - 1;
- u16 xlen = 0;
- unsigned long flags;
-
- if (! priv->has_wep)
- return -EOPNOTSUPP;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
- index = priv->tx_key;
-
- erq->flags = 0;
- if (!priv->encode_alg)
- erq->flags |= IW_ENCODE_DISABLED;
- erq->flags |= index + 1;
-
- if (priv->wep_restrict)
- erq->flags |= IW_ENCODE_RESTRICTED;
- else
- erq->flags |= IW_ENCODE_OPEN;
-
- xlen = le16_to_cpu(priv->keys[index].len);
-
- erq->length = xlen;
-
- memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
-
- orinoco_unlock(priv, &flags);
- return 0;
-}
-
-static int orinoco_ioctl_setessid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq,
- char *essidbuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
- * anyway... - Jean II */
-
- /* Hum... Should not use Wireless Extension constant (may change),
- * should use our own... - Jean II */
- if (erq->length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
- memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
-
- /* If not ANY, get the new ESSID */
- if (erq->flags) {
- memcpy(priv->desired_essid, essidbuf, erq->length);
- }
-
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getessid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *erq,
- char *essidbuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int active;
- int err = 0;
- unsigned long flags;
-
- if (netif_running(dev)) {
- err = orinoco_hw_get_essid(priv, &active, essidbuf);
- if (err < 0)
- return err;
- erq->length = err;
- } else {
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
- memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
- erq->length = strlen(priv->desired_essid);
- orinoco_unlock(priv, &flags);
- }
-
- erq->flags = 1;
-
- return 0;
-}
-
-static int orinoco_ioctl_setnick(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *nrq,
- char *nickbuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- if (nrq->length > IW_ESSID_MAX_SIZE)
- return -E2BIG;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- memset(priv->nick, 0, sizeof(priv->nick));
- memcpy(priv->nick, nickbuf, nrq->length);
-
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getnick(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *nrq,
- char *nickbuf)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE);
- orinoco_unlock(priv, &flags);
-
- nrq->length = strlen(priv->nick);
-
- return 0;
-}
-
-static int orinoco_ioctl_setfreq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *frq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int chan = -1;
- unsigned long flags;
- int err = -EINPROGRESS; /* Call commit handler */
-
- /* In infrastructure mode the AP sets the channel */
- if (priv->iw_mode == IW_MODE_INFRA)
- return -EBUSY;
-
- if ( (frq->e == 0) && (frq->m <= 1000) ) {
- /* Setting by channel number */
- chan = frq->m;
- } else {
- /* Setting by frequency - search the table */
- int mult = 1;
- int i;
-
- for (i = 0; i < (6 - frq->e); i++)
- mult *= 10;
-
- for (i = 0; i < NUM_CHANNELS; i++)
- if (frq->m == (channel_frequency[i] * mult))
- chan = i+1;
- }
-
- if ( (chan < 1) || (chan > NUM_CHANNELS) ||
- ! (priv->channel_mask & (1 << (chan-1)) ) )
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- priv->channel = chan;
- if (priv->iw_mode == IW_MODE_MONITOR) {
- /* Fast channel change - no commit if successful */
- hermes_t *hw = &priv->hw;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
- HERMES_TEST_SET_CHANNEL,
- chan, NULL);
- }
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getfreq(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_freq *frq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int tmp;
-
- /* Locking done in there */
- tmp = orinoco_hw_get_freq(priv);
- if (tmp < 0) {
- return tmp;
- }
-
- frq->m = tmp;
- frq->e = 1;
-
- return 0;
-}
-
-static int orinoco_ioctl_getsens(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *srq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- u16 val;
- int err;
- unsigned long flags;
-
- if (!priv->has_sensitivity)
- return -EOPNOTSUPP;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFSYSTEMSCALE, &val);
- orinoco_unlock(priv, &flags);
-
- if (err)
- return err;
-
- srq->value = val;
- srq->fixed = 0; /* auto */
-
- return 0;
-}
-
-static int orinoco_ioctl_setsens(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *srq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int val = srq->value;
- unsigned long flags;
-
- if (!priv->has_sensitivity)
- return -EOPNOTSUPP;
-
- if ((val < 1) || (val > 3))
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
- priv->ap_density = val;
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_setrts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int val = rrq->value;
- unsigned long flags;
-
- if (rrq->disabled)
- val = 2347;
-
- if ( (val < 0) || (val > 2347) )
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- priv->rts_thresh = val;
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getrts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- rrq->value = priv->rts_thresh;
- rrq->disabled = (rrq->value == 2347);
- rrq->fixed = 1;
-
- return 0;
-}
-
-static int orinoco_ioctl_setfrag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *frq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (priv->has_mwo) {
- if (frq->disabled)
- priv->mwo_robust = 0;
- else {
- if (frq->fixed)
- printk(KERN_WARNING "%s: Fixed fragmentation is "
- "not supported on this firmware. "
- "Using MWO robust instead.\n", dev->name);
- priv->mwo_robust = 1;
- }
- } else {
- if (frq->disabled)
- priv->frag_thresh = 2346;
- else {
- if ( (frq->value < 256) || (frq->value > 2346) )
- err = -EINVAL;
- else
- priv->frag_thresh = frq->value & ~0x1; /* must be even */
- }
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getfrag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *frq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err;
- u16 val;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (priv->has_mwo) {
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMWOROBUST_AGERE,
- &val);
- if (err)
- val = 0;
-
- frq->value = val ? 2347 : 0;
- frq->disabled = ! val;
- frq->fixed = 0;
- } else {
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
- &val);
- if (err)
- val = 0;
-
- frq->value = val;
- frq->disabled = (val >= 2346);
- frq->fixed = 1;
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_setrate(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int ratemode = -1;
- int bitrate; /* 100s of kilobits */
- int i;
- unsigned long flags;
-
- /* As the user space doesn't know our highest rate, it uses -1
- * to ask us to set the highest rate. Test it using "iwconfig
- * ethX rate auto" - Jean II */
- if (rrq->value == -1)
- bitrate = 110;
- else {
- if (rrq->value % 100000)
- return -EINVAL;
- bitrate = rrq->value / 100000;
- }
-
- if ( (bitrate != 10) && (bitrate != 20) &&
- (bitrate != 55) && (bitrate != 110) )
- return -EINVAL;
-
- for (i = 0; i < BITRATE_TABLE_SIZE; i++)
- if ( (bitrate_table[i].bitrate == bitrate) &&
- (bitrate_table[i].automatic == ! rrq->fixed) ) {
- ratemode = i;
- break;
- }
-
- if (ratemode == -1)
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
- priv->bitratemode = ratemode;
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS;
-}
-
-static int orinoco_ioctl_getrate(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- int ratemode;
- int i;
- u16 val;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- ratemode = priv->bitratemode;
-
- BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
-
- rrq->value = bitrate_table[ratemode].bitrate * 100000;
- rrq->fixed = ! bitrate_table[ratemode].automatic;
- rrq->disabled = 0;
-
- /* If the interface is running we try to find more about the
- current mode */
- if (netif_running(dev)) {
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CURRENTTXRATE, &val);
- if (err)
- goto out;
-
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
- /* Note : in Lucent firmware, the return value of
- * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
- * and therefore is totally different from the
- * encoding of HERMES_RID_CNFTXRATECONTROL.
- * Don't forget that 6Mb/s is really 5.5Mb/s */
- if (val == 6)
- rrq->value = 5500000;
- else
- rrq->value = val * 1000000;
- break;
- case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
- case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
- for (i = 0; i < BITRATE_TABLE_SIZE; i++)
- if (bitrate_table[i].intersil_txratectrl == val) {
- ratemode = i;
- break;
- }
- if (i >= BITRATE_TABLE_SIZE)
- printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
- dev->name, val);
-
- rrq->value = bitrate_table[ratemode].bitrate * 100000;
- break;
- default:
- BUG();
- }
- }
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_setpower(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *prq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (prq->disabled) {
- priv->pm_on = 0;
- } else {
- switch (prq->flags & IW_POWER_MODE) {
- case IW_POWER_UNICAST_R:
- priv->pm_mcast = 0;
- priv->pm_on = 1;
- break;
- case IW_POWER_ALL_R:
- priv->pm_mcast = 1;
- priv->pm_on = 1;
- break;
- case IW_POWER_ON:
- /* No flags : but we may have a value - Jean II */
- break;
- default:
- err = -EINVAL;
- goto out;
- }
-
- if (prq->flags & IW_POWER_TIMEOUT) {
- priv->pm_on = 1;
- priv->pm_timeout = prq->value / 1000;
- }
- if (prq->flags & IW_POWER_PERIOD) {
- priv->pm_on = 1;
- priv->pm_period = prq->value / 1000;
- }
- /* It's valid to not have a value if we are just toggling
- * the flags... Jean II */
- if(!priv->pm_on) {
- err = -EINVAL;
- goto out;
- }
- }
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getpower(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *prq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- u16 enable, period, timeout, mcast;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMAXSLEEPDURATION, &period);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
- if (err)
- goto out;
-
- prq->disabled = !enable;
- /* Note : by default, display the period */
- if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
- prq->flags = IW_POWER_TIMEOUT;
- prq->value = timeout * 1000;
- } else {
- prq->flags = IW_POWER_PERIOD;
- prq->value = period * 1000;
- }
- if (mcast)
- prq->flags |= IW_POWER_ALL_R;
- else
- prq->flags |= IW_POWER_UNICAST_R;
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_set_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, alg = ext->alg, set_key = 1;
- unsigned long flags;
- int err = -EINVAL;
- u16 key_len;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- /* Determine and validate the key index */
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if ((idx < 1) || (idx > WEP_KEYS))
- goto out;
- idx--;
- } else
- idx = priv->tx_key;
-
- if (encoding->flags & IW_ENCODE_DISABLED)
- alg = IW_ENCODE_ALG_NONE;
-
- if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
- /* Clear any TKIP TX key we had */
- (void) orinoco_clear_tkip_key(priv, priv->tx_key);
- }
-
- if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
- priv->tx_key = idx;
- set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
- (ext->key_len > 0)) ? 1 : 0;
- }
-
- if (set_key) {
- /* Set the requested key first */
- switch (alg) {
- case IW_ENCODE_ALG_NONE:
- priv->encode_alg = alg;
- priv->keys[idx].len = 0;
- break;
-
- case IW_ENCODE_ALG_WEP:
- if (ext->key_len > SMALL_KEY_SIZE)
- key_len = LARGE_KEY_SIZE;
- else if (ext->key_len > 0)
- key_len = SMALL_KEY_SIZE;
- else
- goto out;
-
- priv->encode_alg = alg;
- priv->keys[idx].len = cpu_to_le16(key_len);
-
- key_len = min(ext->key_len, key_len);
-
- memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
- memcpy(priv->keys[idx].data, ext->key, key_len);
- break;
-
- case IW_ENCODE_ALG_TKIP:
- {
- hermes_t *hw = &priv->hw;
- u8 *tkip_iv = NULL;
-
- if (!priv->has_wpa ||
- (ext->key_len > sizeof(priv->tkip_key[0])))
- goto out;
-
- priv->encode_alg = alg;
- memset(&priv->tkip_key[idx], 0,
- sizeof(priv->tkip_key[idx]));
- memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
-
- if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
- tkip_iv = &ext->rx_seq[0];
-
- err = __orinoco_hw_set_tkip_key(hw, idx,
- ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
- (u8 *) &priv->tkip_key[idx],
- tkip_iv, NULL);
- if (err)
- printk(KERN_ERR "%s: Error %d setting TKIP key"
- "\n", dev->name, err);
-
- goto out;
- }
- default:
- goto out;
- }
- }
- err = -EINPROGRESS;
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_get_encodeext(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int idx, max_key_len;
- unsigned long flags;
- int err;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = -EINVAL;
- max_key_len = encoding->length - sizeof(*ext);
- if (max_key_len < 0)
- goto out;
-
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if ((idx < 1) || (idx > WEP_KEYS))
- goto out;
- idx--;
- } else
- idx = priv->tx_key;
-
- encoding->flags = idx + 1;
- memset(ext, 0, sizeof(*ext));
-
- ext->alg = priv->encode_alg;
- switch (priv->encode_alg) {
- case IW_ENCODE_ALG_NONE:
- ext->key_len = 0;
- encoding->flags |= IW_ENCODE_DISABLED;
- break;
- case IW_ENCODE_ALG_WEP:
- ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len),
- max_key_len);
- memcpy(ext->key, priv->keys[idx].data, ext->key_len);
- encoding->flags |= IW_ENCODE_ENABLED;
- break;
- case IW_ENCODE_ALG_TKIP:
- ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key),
- max_key_len);
- memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
- encoding->flags |= IW_ENCODE_ENABLED;
- break;
- }
-
- err = 0;
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_set_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- struct iw_param *param = &wrqu->param;
- unsigned long flags;
- int ret = -EINPROGRESS;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_WPA_VERSION:
- case IW_AUTH_CIPHER_PAIRWISE:
- case IW_AUTH_CIPHER_GROUP:
- case IW_AUTH_RX_UNENCRYPTED_EAPOL:
- case IW_AUTH_PRIVACY_INVOKED:
- case IW_AUTH_DROP_UNENCRYPTED:
- /*
- * orinoco does not use these parameters
- */
- break;
-
- case IW_AUTH_KEY_MGMT:
- /* wl_lkm implies value 2 == PSK for Hermes I
- * which ties in with WEXT
- * no other hints tho :(
- */
- priv->key_mgmt = param->value;
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- /* When countermeasures are enabled, shut down the
- * card; when disabled, re-enable the card. This must
- * take effect immediately.
- *
- * TODO: Make sure that the EAPOL message is getting
- * out before card disabled
- */
- if (param->value) {
- priv->tkip_cm_active = 1;
- ret = hermes_enable_port(hw, 0);
- } else {
- priv->tkip_cm_active = 0;
- ret = hermes_disable_port(hw, 0);
- }
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- if (param->value & IW_AUTH_ALG_SHARED_KEY)
- priv->wep_restrict = 1;
- else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
- priv->wep_restrict = 0;
- else
- ret = -EINVAL;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- if (priv->has_wpa) {
- priv->wpa_enabled = param->value ? 1 : 0;
- } else {
- if (param->value)
- ret = -EOPNOTSUPP;
- /* else silently accept disable of WPA */
- priv->wpa_enabled = 0;
- }
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- orinoco_unlock(priv, &flags);
- return ret;
-}
-
-static int orinoco_ioctl_get_auth(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct iw_param *param = &wrqu->param;
- unsigned long flags;
- int ret = 0;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- switch (param->flags & IW_AUTH_INDEX) {
- case IW_AUTH_KEY_MGMT:
- param->value = priv->key_mgmt;
- break;
-
- case IW_AUTH_TKIP_COUNTERMEASURES:
- param->value = priv->tkip_cm_active;
- break;
-
- case IW_AUTH_80211_AUTH_ALG:
- if (priv->wep_restrict)
- param->value = IW_AUTH_ALG_SHARED_KEY;
- else
- param->value = IW_AUTH_ALG_OPEN_SYSTEM;
- break;
-
- case IW_AUTH_WPA_ENABLED:
- param->value = priv->wpa_enabled;
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- orinoco_unlock(priv, &flags);
- return ret;
-}
-
-static int orinoco_ioctl_set_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- u8 *buf;
- unsigned long flags;
- int err = 0;
-
- if ((wrqu->data.length > MAX_WPA_IE_LEN) ||
- (wrqu->data.length && (extra == NULL)))
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (wrqu->data.length) {
- buf = kmalloc(wrqu->data.length, GFP_KERNEL);
- if (buf == NULL) {
- err = -ENOMEM;
- goto out;
- }
-
- memcpy(buf, extra, wrqu->data.length);
- kfree(priv->wpa_ie);
- priv->wpa_ie = buf;
- priv->wpa_ie_len = wrqu->data.length;
- } else {
- kfree(priv->wpa_ie);
- priv->wpa_ie = NULL;
- priv->wpa_ie_len = 0;
- }
-
- if (priv->wpa_ie) {
- /* Looks like wl_lkm wants to check the auth alg, and
- * somehow pass it to the firmware.
- * Instead it just calls the key mgmt rid
- * - we do this in set auth.
- */
- }
-
-out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-static int orinoco_ioctl_get_genie(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err = 0;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
- wrqu->data.length = 0;
- goto out;
- }
-
- if (wrqu->data.length < priv->wpa_ie_len) {
- err = -E2BIG;
- goto out;
- }
-
- wrqu->data.length = priv->wpa_ie_len;
- memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
-
-out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-static int orinoco_ioctl_set_mlme(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- struct iw_mlme *mlme = (struct iw_mlme *)extra;
- unsigned long flags;
- int ret = 0;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- switch (mlme->cmd) {
- case IW_MLME_DEAUTH:
- /* silently ignore */
- break;
-
- case IW_MLME_DISASSOC:
- {
- struct {
- u8 addr[ETH_ALEN];
- __le16 reason_code;
- } __attribute__ ((packed)) buf;
-
- memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
- buf.reason_code = cpu_to_le16(mlme->reason_code);
- ret = HERMES_WRITE_RECORD(hw, USER_BAP,
- HERMES_RID_CNFDISASSOCIATE,
- &buf);
- break;
- }
- default:
- ret = -EOPNOTSUPP;
- }
-
- orinoco_unlock(priv, &flags);
- return ret;
-}
-
-static int orinoco_ioctl_getretry(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- u16 short_limit, long_limit, lifetime;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
- &short_limit);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
- &long_limit);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
- &lifetime);
- if (err)
- goto out;
-
- rrq->disabled = 0; /* Can't be disabled */
-
- /* Note : by default, display the retry number */
- if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
- rrq->flags = IW_RETRY_LIFETIME;
- rrq->value = lifetime * 1000; /* ??? */
- } else {
- /* By default, display the min number */
- if ((rrq->flags & IW_RETRY_LONG)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- rrq->value = long_limit;
- } else {
- rrq->flags = IW_RETRY_LIMIT;
- rrq->value = short_limit;
- if(short_limit != long_limit)
- rrq->flags |= IW_RETRY_SHORT;
- }
- }
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_reset(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- if (! capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
- printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
-
- /* Firmware reset */
- orinoco_reset(&priv->reset_work);
- } else {
- printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
-
- schedule_work(&priv->reset_work);
- }
-
- return 0;
-}
-
-static int orinoco_ioctl_setibssport(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int val = *( (int *) extra );
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- priv->ibss_port = val ;
-
- /* Actually update the mode we are using */
- set_port_type(priv);
-
- orinoco_unlock(priv, &flags);
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getibssport(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int *val = (int *) extra;
-
- *val = priv->ibss_port;
- return 0;
-}
-
-static int orinoco_ioctl_setport3(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int val = *( (int *) extra );
- int err = 0;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- switch (val) {
- case 0: /* Try to do IEEE ad-hoc mode */
- if (! priv->has_ibss) {
- err = -EINVAL;
- break;
- }
- priv->prefer_port3 = 0;
-
- break;
-
- case 1: /* Try to do Lucent proprietary ad-hoc mode */
- if (! priv->has_port3) {
- err = -EINVAL;
- break;
- }
- priv->prefer_port3 = 1;
- break;
-
- default:
- err = -EINVAL;
- }
-
- if (! err) {
- /* Actually update the mode we are using */
- set_port_type(priv);
- err = -EINPROGRESS;
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getport3(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int *val = (int *) extra;
-
- *val = priv->prefer_port3;
- return 0;
-}
-
-static int orinoco_ioctl_setpreamble(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int val;
-
- if (! priv->has_preamble)
- return -EOPNOTSUPP;
-
- /* 802.11b has recently defined some short preamble.
- * Basically, the Phy header has been reduced in size.
- * This increase performance, especially at high rates
- * (the preamble is transmitted at 1Mb/s), unfortunately
- * this give compatibility troubles... - Jean II */
- val = *( (int *) extra );
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (val)
- priv->preamble = 1;
- else
- priv->preamble = 0;
-
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getpreamble(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int *val = (int *) extra;
-
- if (! priv->has_preamble)
- return -EOPNOTSUPP;
-
- *val = priv->preamble;
- return 0;
-}
-
-/* ioctl interface to hermes_read_ltv()
- * To use with iwpriv, pass the RID as the token argument, e.g.
- * iwpriv get_rid [0xfc00]
- * At least Wireless Tools 25 is required to use iwpriv.
- * For Wireless Tools 25 and 26 append "dummy" are the end. */
-static int orinoco_ioctl_getrid(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *data,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- int rid = data->flags;
- u16 length;
- int err;
- unsigned long flags;
-
- /* It's a "get" function, but we don't want users to access the
- * WEP key and other raw firmware data */
- if (! capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if (rid < 0xfc00 || rid > 0xffff)
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
- extra);
- if (err)
- goto out;
-
- data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
- MAX_RID_LEN);
-
- out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-/* Trigger a scan (look for other cells in the vicinity) */
-static int orinoco_ioctl_setscan(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *srq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- hermes_t *hw = &priv->hw;
- struct iw_scan_req *si = (struct iw_scan_req *) extra;
- int err = 0;
- unsigned long flags;
-
- /* Note : you may have realised that, as this is a SET operation,
- * this is privileged and therefore a normal user can't
- * perform scanning.
- * This is not an error, while the device perform scanning,
- * traffic doesn't flow, so it's a perfect DoS...
- * Jean II */
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- /* Scanning with port 0 disabled would fail */
- if (!netif_running(dev)) {
- err = -ENETDOWN;
- goto out;
- }
-
- /* In monitor mode, the scan results are always empty.
- * Probe responses are passed to the driver as received
- * frames and could be processed in software. */
- if (priv->iw_mode == IW_MODE_MONITOR) {
- err = -EOPNOTSUPP;
- goto out;
- }
-
- /* Note : because we don't lock out the irq handler, the way
- * we access scan variables in priv is critical.
- * o scan_inprogress : not touched by irq handler
- * o scan_mode : not touched by irq handler
- * Before modifying anything on those variables, please think hard !
- * Jean II */
-
- /* Save flags */
- priv->scan_mode = srq->flags;
-
- /* Always trigger scanning, even if it's in progress.
- * This way, if the info frame get lost, we will recover somewhat
- * gracefully - Jean II */
-
- if (priv->has_hostscan) {
- switch (priv->firmware_type) {
- case FIRMWARE_TYPE_SYMBOL:
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFHOSTSCAN_SYMBOL,
- HERMES_HOSTSCAN_SYMBOL_ONCE |
- HERMES_HOSTSCAN_SYMBOL_BCAST);
- break;
- case FIRMWARE_TYPE_INTERSIL: {
- __le16 req[3];
-
- req[0] = cpu_to_le16(0x3fff); /* All channels */
- req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
- req[2] = 0; /* Any ESSID */
- err = HERMES_WRITE_RECORD(hw, USER_BAP,
- HERMES_RID_CNFHOSTSCAN, &req);
- }
- break;
- case FIRMWARE_TYPE_AGERE:
- if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
- struct hermes_idstring idbuf;
- size_t len = min(sizeof(idbuf.val),
- (size_t) si->essid_len);
- idbuf.len = cpu_to_le16(len);
- memcpy(idbuf.val, si->essid, len);
-
- err = hermes_write_ltv(hw, USER_BAP,
- HERMES_RID_CNFSCANSSID_AGERE,
- HERMES_BYTES_TO_RECLEN(len + 2),
- &idbuf);
- } else
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFSCANSSID_AGERE,
- 0); /* Any ESSID */
- if (err)
- break;
-
- if (priv->has_ext_scan) {
- /* Clear scan results at the start of
- * an extended scan */
- orinoco_clear_scan_results(priv,
- msecs_to_jiffies(15000));
-
- /* TODO: Is this available on older firmware?
- * Can we use it to scan specific channels
- * for IW_SCAN_THIS_FREQ? */
- err = hermes_write_wordrec(hw, USER_BAP,
- HERMES_RID_CNFSCANCHANNELS2GHZ,
- 0x7FFF);
- if (err)
- goto out;
-
- err = hermes_inquire(hw,
- HERMES_INQ_CHANNELINFO);
- } else
- err = hermes_inquire(hw, HERMES_INQ_SCAN);
- break;
- }
- } else
- err = hermes_inquire(hw, HERMES_INQ_SCAN);
-
- /* One more client */
- if (! err)
- priv->scan_inprogress = 1;
-
- out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-#define MAX_CUSTOM_LEN 64
-
-/* Translate scan data returned from the card to a card independant
- * format that the Wireless Tools will understand - Jean II */
-static inline char *orinoco_translate_scan(struct net_device *dev,
- struct iw_request_info *info,
- char *current_ev,
- char *end_buf,
- union hermes_scan_info *bss,
- unsigned int last_scanned)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- u16 capabilities;
- u16 channel;
- struct iw_event iwe; /* Temporary buffer */
- char custom[MAX_CUSTOM_LEN];
-
- memset(&iwe, 0, sizeof(iwe));
-
- /* First entry *MUST* be the AP MAC address */
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN);
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_ADDR_LEN);
-
- /* Other entries will be displayed in the order we give them */
-
- /* Add the ESSID */
- iwe.u.data.length = le16_to_cpu(bss->a.essid_len);
- if (iwe.u.data.length > 32)
- iwe.u.data.length = 32;
- iwe.cmd = SIOCGIWESSID;
- iwe.u.data.flags = 1;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, bss->a.essid);
-
- /* Add mode */
- iwe.cmd = SIOCGIWMODE;
- capabilities = le16_to_cpu(bss->a.capabilities);
- if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
- if (capabilities & WLAN_CAPABILITY_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else
- iwe.u.mode = IW_MODE_ADHOC;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_UINT_LEN);
- }
-
- channel = bss->s.channel;
- if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
- /* Add channel and frequency */
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = channel;
- iwe.u.freq.e = 0;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
-
- iwe.u.freq.m = channel_frequency[channel-1] * 100000;
- iwe.u.freq.e = 1;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
- }
-
- /* Add quality statistics. level and noise in dB. No link quality */
- iwe.cmd = IWEVQUAL;
- iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
- iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
- iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
- /* Wireless tools prior to 27.pre22 will show link quality
- * anyway, so we provide a reasonable value. */
- if (iwe.u.qual.level > iwe.u.qual.noise)
- iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
- else
- iwe.u.qual.qual = 0;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_QUAL_LEN);
-
- /* Add encryption capability */
- iwe.cmd = SIOCGIWENCODE;
- if (capabilities & WLAN_CAPABILITY_PRIVACY)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, NULL);
-
- /* Bit rate is not available in Lucent/Agere firmwares */
- if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
- char *current_val = current_ev + iwe_stream_lcp_len(info);
- int i;
- int step;
-
- if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
- step = 2;
- else
- step = 1;
-
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
- /* Max 10 values */
- for (i = 0; i < 10; i += step) {
- /* NULL terminated */
- if (bss->p.rates[i] == 0x0)
- break;
- /* Bit rate given in 500 kb/s units (+ 0x80) */
- iwe.u.bitrate.value =
- ((bss->p.rates[i] & 0x7f) * 500000);
- current_val = iwe_stream_add_value(info, current_ev,
- current_val,
- end_buf, &iwe,
- IW_EV_PARAM_LEN);
- }
- /* Check if we added any event */
- if ((current_val - current_ev) > iwe_stream_lcp_len(info))
- current_ev = current_val;
- }
-
- /* Beacon interval */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- "bcn_int=%d",
- le16_to_cpu(bss->a.beacon_interv));
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- /* Capabilites */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- "capab=0x%04x",
- capabilities);
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- /* Add EXTRA: Age to display seconds since last beacon/probe response
- * for given network. */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- " Last beacon: %dms ago",
- jiffies_to_msecs(jiffies - last_scanned));
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- return current_ev;
-}
-
-static inline char *orinoco_translate_ext_scan(struct net_device *dev,
- struct iw_request_info *info,
- char *current_ev,
- char *end_buf,
- struct agere_ext_scan_info *bss,
- unsigned int last_scanned)
-{
- u16 capabilities;
- u16 channel;
- struct iw_event iwe; /* Temporary buffer */
- char custom[MAX_CUSTOM_LEN];
- u8 *ie;
-
- memset(&iwe, 0, sizeof(iwe));
-
- /* First entry *MUST* be the AP MAC address */
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_ADDR_LEN);
-
- /* Other entries will be displayed in the order we give them */
-
- /* Add the ESSID */
- ie = bss->data;
- iwe.u.data.length = ie[1];
- if (iwe.u.data.length) {
- if (iwe.u.data.length > 32)
- iwe.u.data.length = 32;
- iwe.cmd = SIOCGIWESSID;
- iwe.u.data.flags = 1;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, &ie[2]);
- }
-
- /* Add mode */
- capabilities = le16_to_cpu(bss->capabilities);
- if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
- iwe.cmd = SIOCGIWMODE;
- if (capabilities & WLAN_CAPABILITY_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else
- iwe.u.mode = IW_MODE_ADHOC;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_UINT_LEN);
- }
-
- ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET);
- channel = ie ? ie[2] : 0;
- if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
- /* Add channel and frequency */
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = channel;
- iwe.u.freq.e = 0;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
-
- iwe.u.freq.m = channel_frequency[channel-1] * 100000;
- iwe.u.freq.e = 1;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_FREQ_LEN);
- }
-
- /* Add quality statistics. level and noise in dB. No link quality */
- iwe.cmd = IWEVQUAL;
- iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
- iwe.u.qual.level = bss->level - 0x95;
- iwe.u.qual.noise = bss->noise - 0x95;
- /* Wireless tools prior to 27.pre22 will show link quality
- * anyway, so we provide a reasonable value. */
- if (iwe.u.qual.level > iwe.u.qual.noise)
- iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
- else
- iwe.u.qual.qual = 0;
- current_ev = iwe_stream_add_event(info, current_ev, end_buf,
- &iwe, IW_EV_QUAL_LEN);
-
- /* Add encryption capability */
- iwe.cmd = SIOCGIWENCODE;
- if (capabilities & WLAN_CAPABILITY_PRIVACY)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, NULL);
-
- /* WPA IE */
- ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
- if (ie) {
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = ie[1] + 2;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, ie);
- }
-
- /* RSN IE */
- ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN);
- if (ie) {
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = ie[1] + 2;
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, ie);
- }
-
- ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES);
- if (ie) {
- char *p = current_ev + iwe_stream_lcp_len(info);
- int i;
-
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-
- for (i = 2; i < (ie[1] + 2); i++) {
- iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
- p = iwe_stream_add_value(info, current_ev, p, end_buf,
- &iwe, IW_EV_PARAM_LEN);
- }
- /* Check if we added any event */
- if (p > (current_ev + iwe_stream_lcp_len(info)))
- current_ev = p;
- }
-
- /* Timestamp */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length =
- snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx",
- (unsigned long long) le64_to_cpu(bss->timestamp));
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- /* Beacon interval */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- "bcn_int=%d",
- le16_to_cpu(bss->beacon_interval));
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- /* Capabilites */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- "capab=0x%04x",
- capabilities);
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- /* Add EXTRA: Age to display seconds since last beacon/probe response
- * for given network. */
- iwe.cmd = IWEVCUSTOM;
- iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
- " Last beacon: %dms ago",
- jiffies_to_msecs(jiffies - last_scanned));
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
-
- return current_ev;
-}
-
-/* Return results of a scan */
-static int orinoco_ioctl_getscan(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *srq,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- int err = 0;
- unsigned long flags;
- char *current_ev = extra;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (priv->scan_inprogress) {
- /* Important note : we don't want to block the caller
- * until results are ready for various reasons.
- * First, managing wait queues is complex and racy.
- * Second, we grab some rtnetlink lock before comming
- * here (in dev_ioctl()).
- * Third, we generate an Wireless Event, so the
- * caller can wait itself on that - Jean II */
- err = -EAGAIN;
- goto out;
- }
-
- if (priv->has_ext_scan) {
- struct xbss_element *bss;
-
- list_for_each_entry(bss, &priv->bss_list, list) {
- /* Translate this entry to WE format */
- current_ev =
- orinoco_translate_ext_scan(dev, info,
- current_ev,
- extra + srq->length,
- &bss->bss,
- bss->last_scanned);
-
- /* Check if there is space for one more entry */
- if ((extra + srq->length - current_ev)
- <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a
- * bigger buffer */
- err = -E2BIG;
- goto out;
- }
- }
-
- } else {
- struct bss_element *bss;
-
- list_for_each_entry(bss, &priv->bss_list, list) {
- /* Translate this entry to WE format */
- current_ev = orinoco_translate_scan(dev, info,
- current_ev,
- extra + srq->length,
- &bss->bss,
- bss->last_scanned);
-
- /* Check if there is space for one more entry */
- if ((extra + srq->length - current_ev)
- <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a
- * bigger buffer */
- err = -E2BIG;
- goto out;
- }
- }
- }
-
- srq->length = (current_ev - extra);
- srq->flags = (__u16) priv->scan_mode;
-
-out:
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-/* Commit handler, called after set operations */
-static int orinoco_ioctl_commit(struct net_device *dev,
- struct iw_request_info *info,
- void *wrqu,
- char *extra)
-{
- struct orinoco_private *priv = netdev_priv(dev);
- struct hermes *hw = &priv->hw;
- unsigned long flags;
- int err = 0;
-
- if (!priv->open)
- return 0;
-
- if (priv->broken_disableport) {
- orinoco_reset(&priv->reset_work);
- return 0;
- }
-
- if (orinoco_lock(priv, &flags) != 0)
- return err;
-
- err = hermes_disable_port(hw, 0);
- if (err) {
- printk(KERN_WARNING "%s: Unable to disable port "
- "while reconfiguring card\n", dev->name);
- priv->broken_disableport = 1;
- goto out;
- }
-
- err = __orinoco_program_rids(dev);
- if (err) {
- printk(KERN_WARNING "%s: Unable to reconfigure card\n",
- dev->name);
- goto out;
- }
-
- err = hermes_enable_port(hw, 0);
- if (err) {
- printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
- dev->name);
- goto out;
- }
-
- out:
- if (err) {
- printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
- schedule_work(&priv->reset_work);
- err = 0;
- }
-
- orinoco_unlock(priv, &flags);
- return err;
-}
-
-static const struct iw_priv_args orinoco_privtab[] = {
- { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
- { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
- { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- 0, "set_port3" },
- { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "get_port3" },
- { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- 0, "set_preamble" },
- { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "get_preamble" },
- { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- 0, "set_ibssport" },
- { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
- "get_ibssport" },
- { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
- "get_rid" },
-};
-
-
-/*
- * Structures to export the Wireless Handlers
- */
-
-#define STD_IW_HANDLER(id, func) \
- [IW_IOCTL_IDX(id)] = (iw_handler) func
-static const iw_handler orinoco_handler[] = {
- STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
- STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
- STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
- STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
- STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
- STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
- STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
- STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
- STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
- STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
- STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
- STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
- STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
- STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
- STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
- STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
- STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
- STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
- STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
- STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
- STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
- STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
- STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
- STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
- STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
- STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
- STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
- STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
- STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
- STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
- STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
- STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
- STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
- STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
- STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
- STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
- STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
- STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
- STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
-};
-
-
-/*
- Added typecasting since we no longer use iwreq_data -- Moustafa
- */
-static const iw_handler orinoco_private_handler[] = {
- [0] = (iw_handler) orinoco_ioctl_reset,
- [1] = (iw_handler) orinoco_ioctl_reset,
- [2] = (iw_handler) orinoco_ioctl_setport3,
- [3] = (iw_handler) orinoco_ioctl_getport3,
- [4] = (iw_handler) orinoco_ioctl_setpreamble,
- [5] = (iw_handler) orinoco_ioctl_getpreamble,
- [6] = (iw_handler) orinoco_ioctl_setibssport,
- [7] = (iw_handler) orinoco_ioctl_getibssport,
- [9] = (iw_handler) orinoco_ioctl_getrid,
-};
-
-static const struct iw_handler_def orinoco_handler_def = {
- .num_standard = ARRAY_SIZE(orinoco_handler),
- .num_private = ARRAY_SIZE(orinoco_private_handler),
- .num_private_args = ARRAY_SIZE(orinoco_privtab),
- .standard = orinoco_handler,
- .private = orinoco_private_handler,
- .private_args = orinoco_privtab,
- .get_wireless_stats = orinoco_get_wireless_stats,
-};
-
-static void orinoco_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct orinoco_private *priv = netdev_priv(dev);
-
- strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
- strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
- strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
- if (dev->dev.parent)
- strncpy(info->bus_info, dev->dev.parent->bus_id,
- sizeof(info->bus_info) - 1);
- else
- snprintf(info->bus_info, sizeof(info->bus_info) - 1,
- "PCMCIA %p", priv->hw.iobase);
-}
-
-static const struct ethtool_ops orinoco_ethtool_ops = {
- .get_drvinfo = orinoco_get_drvinfo,
- .get_link = ethtool_op_get_link,
-};
-
-/********************************************************************/
-/* Module initialization */
-/********************************************************************/
-
-EXPORT_SYMBOL(alloc_orinocodev);
-EXPORT_SYMBOL(free_orinocodev);
-
-EXPORT_SYMBOL(__orinoco_up);
-EXPORT_SYMBOL(__orinoco_down);
-EXPORT_SYMBOL(orinoco_reinit_firmware);
-
-EXPORT_SYMBOL(orinoco_interrupt);
-
-/* Can't be declared "const" or the whole __initdata section will
- * become const */
-static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
- " (David Gibson <hermes@gibson.dropbear.id.au>, "
- "Pavel Roskin <proski@gnu.org>, et al)";
-
-static int __init init_orinoco(void)
-{
- printk(KERN_DEBUG "%s\n", version);
- return 0;
-}
-
-static void __exit exit_orinoco(void)
-{
-}
-
-module_init(init_orinoco);
-module_exit(exit_orinoco);
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
deleted file mode 100644
index 981570bd3b9d..000000000000
--- a/drivers/net/wireless/orinoco.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/* orinoco.h
- *
- * Common definitions to all pieces of the various orinoco
- * drivers
- */
-
-#ifndef _ORINOCO_H
-#define _ORINOCO_H
-
-#define DRIVER_VERSION "0.15"
-
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/wireless.h>
-#include <net/iw_handler.h>
-
-#include "hermes.h"
-
-/* To enable debug messages */
-//#define ORINOCO_DEBUG 3
-
-#define WIRELESS_SPY // enable iwspy support
-
-#define MAX_SCAN_LEN 4096
-
-#define ORINOCO_MAX_KEY_SIZE 14
-#define ORINOCO_MAX_KEYS 4
-
-struct orinoco_key {
- __le16 len; /* always stored as little-endian */
- char data[ORINOCO_MAX_KEY_SIZE];
-} __attribute__ ((packed));
-
-#define TKIP_KEYLEN 16
-#define MIC_KEYLEN 8
-
-struct orinoco_tkip_key {
- u8 tkip[TKIP_KEYLEN];
- u8 tx_mic[MIC_KEYLEN];
- u8 rx_mic[MIC_KEYLEN];
-};
-
-typedef enum {
- FIRMWARE_TYPE_AGERE,
- FIRMWARE_TYPE_INTERSIL,
- FIRMWARE_TYPE_SYMBOL
-} fwtype_t;
-
-struct bss_element {
- union hermes_scan_info bss;
- unsigned long last_scanned;
- struct list_head list;
-};
-
-struct xbss_element {
- struct agere_ext_scan_info bss;
- unsigned long last_scanned;
- struct list_head list;
-};
-
-struct hermes_rx_descriptor;
-
-struct orinoco_rx_data {
- struct hermes_rx_descriptor *desc;
- struct sk_buff *skb;
- struct list_head list;
-};
-
-struct orinoco_private {
- void *card; /* Pointer to card dependent structure */
- struct device *dev;
- int (*hard_reset)(struct orinoco_private *);
- int (*stop_fw)(struct orinoco_private *, int);
-
- /* Synchronisation stuff */
- spinlock_t lock;
- int hw_unavailable;
- struct work_struct reset_work;
-
- /* Interrupt tasklets */
- struct tasklet_struct rx_tasklet;
- struct list_head rx_list;
- struct orinoco_rx_data *rx_data;
-
- /* driver state */
- int open;
- u16 last_linkstatus;
- struct work_struct join_work;
- struct work_struct wevent_work;
-
- /* Net device stuff */
- struct net_device *ndev;
- struct net_device_stats stats;
- struct iw_statistics wstats;
-
- /* Hardware control variables */
- hermes_t hw;
- u16 txfid;
-
- /* Capabilities of the hardware/firmware */
- fwtype_t firmware_type;
- char fw_name[32];
- int ibss_port;
- int nicbuf_size;
- u16 channel_mask;
-
- /* Boolean capabilities */
- unsigned int has_ibss:1;
- unsigned int has_port3:1;
- unsigned int has_wep:1;
- unsigned int has_big_wep:1;
- unsigned int has_mwo:1;
- unsigned int has_pm:1;
- unsigned int has_preamble:1;
- unsigned int has_sensitivity:1;
- unsigned int has_hostscan:1;
- unsigned int has_alt_txcntl:1;
- unsigned int has_ext_scan:1;
- unsigned int has_wpa:1;
- unsigned int do_fw_download:1;
- unsigned int broken_disableport:1;
- unsigned int broken_monitor:1;
-
- /* Configuration paramaters */
- u32 iw_mode;
- int prefer_port3;
- u16 encode_alg, wep_restrict, tx_key;
- struct orinoco_key keys[ORINOCO_MAX_KEYS];
- int bitratemode;
- char nick[IW_ESSID_MAX_SIZE+1];
- char desired_essid[IW_ESSID_MAX_SIZE+1];
- char desired_bssid[ETH_ALEN];
- int bssid_fixed;
- u16 frag_thresh, mwo_robust;
- u16 channel;
- u16 ap_density, rts_thresh;
- u16 pm_on, pm_mcast, pm_period, pm_timeout;
- u16 preamble;
-#ifdef WIRELESS_SPY
- struct iw_spy_data spy_data; /* iwspy support */
- struct iw_public_data wireless_data;
-#endif
-
- /* Configuration dependent variables */
- int port_type, createibss;
- int promiscuous, mc_count;
-
- /* Scanning support */
- struct list_head bss_list;
- struct list_head bss_free_list;
- void *bss_xbss_data;
-
- int scan_inprogress; /* Scan pending... */
- u32 scan_mode; /* Type of scan done */
-
- /* WPA support */
- u8 *wpa_ie;
- int wpa_ie_len;
-
- struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
- struct crypto_hash *rx_tfm_mic;
- struct crypto_hash *tx_tfm_mic;
-
- unsigned int wpa_enabled:1;
- unsigned int tkip_cm_active:1;
- unsigned int key_mgmt:3;
-};
-
-#ifdef ORINOCO_DEBUG
-extern int orinoco_debug;
-#define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
-#else
-#define DEBUG(n, args...) do { } while (0)
-#endif /* ORINOCO_DEBUG */
-
-/********************************************************************/
-/* Exported prototypes */
-/********************************************************************/
-
-extern struct net_device *alloc_orinocodev(
- int sizeof_card, struct device *device,
- int (*hard_reset)(struct orinoco_private *),
- int (*stop_fw)(struct orinoco_private *, int));
-extern void free_orinocodev(struct net_device *dev);
-extern int __orinoco_up(struct net_device *dev);
-extern int __orinoco_down(struct net_device *dev);
-extern int orinoco_reinit_firmware(struct net_device *dev);
-extern irqreturn_t orinoco_interrupt(int irq, void * dev_id);
-
-/********************************************************************/
-/* Locking and synchronization functions */
-/********************************************************************/
-
-static inline int orinoco_lock(struct orinoco_private *priv,
- unsigned long *flags)
-{
- spin_lock_irqsave(&priv->lock, *flags);
- if (priv->hw_unavailable) {
- DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n",
- priv->ndev);
- spin_unlock_irqrestore(&priv->lock, *flags);
- return -EBUSY;
- }
- return 0;
-}
-
-static inline void orinoco_unlock(struct orinoco_private *priv,
- unsigned long *flags)
-{
- spin_unlock_irqrestore(&priv->lock, *flags);
-}
-
-#endif /* _ORINOCO_H */
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile
new file mode 100644
index 000000000000..791366e08c50
--- /dev/null
+++ b/drivers/net/wireless/orinoco/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the orinoco wireless device drivers.
+#
+
+obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o
+obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
+obj-$(CONFIG_APPLE_AIRPORT) += airport.o
+obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
+obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
+obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
+obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
+obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
new file mode 100644
index 000000000000..28f1cae48439
--- /dev/null
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -0,0 +1,286 @@
+/* airport.c
+ *
+ * A driver for "Hermes" chipset based Apple Airport wireless
+ * card.
+ *
+ * Copyright notice & release notes in file orinoco.c
+ *
+ * Note specific to airport stub:
+ *
+ * 0.05 : first version of the new split driver
+ * 0.06 : fix possible hang on powerup, add sleep support
+ */
+
+#define DRIVER_NAME "airport"
+#define PFX DRIVER_NAME ": "
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/pmac_feature.h>
+
+#include "orinoco.h"
+
+#define AIRPORT_IO_LEN (0x1000) /* one page */
+
+struct airport {
+ struct macio_dev *mdev;
+ void __iomem *vaddr;
+ int irq_requested;
+ int ndev_registered;
+};
+
+static int
+airport_suspend(struct macio_dev *mdev, pm_message_t state)
+{
+ struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name);
+
+ err = orinoco_lock(priv, &flags);
+ if (err) {
+ printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n",
+ dev->name);
+ return 0;
+ }
+
+ err = __orinoco_down(dev);
+ if (err)
+ printk(KERN_WARNING "%s: PBOOK_SLEEP_NOW: Error %d downing interface\n",
+ dev->name, err);
+
+ netif_device_detach(dev);
+
+ priv->hw_unavailable++;
+
+ orinoco_unlock(priv, &flags);
+
+ disable_irq(dev->irq);
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0);
+
+ return 0;
+}
+
+static int
+airport_resume(struct macio_dev *mdev)
+{
+ struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ printk(KERN_DEBUG "%s: Airport waking up\n", dev->name);
+
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1);
+ msleep(200);
+
+ enable_irq(dev->irq);
+
+ err = orinoco_reinit_firmware(dev);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d re-initializing firmware on PBOOK_WAKE\n",
+ dev->name, err);
+ return 0;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ netif_device_attach(dev);
+
+ priv->hw_unavailable--;
+
+ if (priv->open && (! priv->hw_unavailable)) {
+ err = __orinoco_up(dev);
+ if (err)
+ printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n",
+ dev->name, err);
+ }
+
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int
+airport_detach(struct macio_dev *mdev)
+{
+ struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev);
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct airport *card = priv->card;
+
+ if (card->ndev_registered)
+ unregister_netdev(dev);
+ card->ndev_registered = 0;
+
+ if (card->irq_requested)
+ free_irq(dev->irq, dev);
+ card->irq_requested = 0;
+
+ if (card->vaddr)
+ iounmap(card->vaddr);
+ card->vaddr = NULL;
+
+ macio_release_resource(mdev, 0);
+
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0);
+ ssleep(1);
+
+ macio_set_drvdata(mdev, NULL);
+ free_orinocodev(dev);
+
+ return 0;
+}
+
+static int airport_hard_reset(struct orinoco_private *priv)
+{
+ /* It would be nice to power cycle the Airport for a real hard
+ * reset, but for some reason although it appears to
+ * re-initialize properly, it falls in a screaming heap
+ * shortly afterwards. */
+#if 0
+ struct net_device *dev = priv->ndev;
+ struct airport *card = priv->card;
+
+ /* Vitally important. If we don't do this it seems we get an
+ * interrupt somewhere during the power cycle, since
+ * hw_unavailable is already set it doesn't get ACKed, we get
+ * into an interrupt loop and the PMU decides to turn us
+ * off. */
+ disable_irq(dev->irq);
+
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0);
+ ssleep(1);
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 1);
+ ssleep(1);
+
+ enable_irq(dev->irq);
+ ssleep(1);
+#endif
+
+ return 0;
+}
+
+static int
+airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
+{
+ struct orinoco_private *priv;
+ struct net_device *dev;
+ struct airport *card;
+ unsigned long phys_addr;
+ hermes_t *hw;
+
+ if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) {
+ printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n");
+ return -ENODEV;
+ }
+
+ /* Allocate space for private device-specific data */
+ dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
+ airport_hard_reset, NULL);
+ if (! dev) {
+ printk(KERN_ERR PFX "Cannot allocate network device\n");
+ return -ENODEV;
+ }
+ priv = netdev_priv(dev);
+ card = priv->card;
+
+ hw = &priv->hw;
+ card->mdev = mdev;
+
+ if (macio_request_resource(mdev, 0, "airport")) {
+ printk(KERN_ERR PFX "can't request IO resource !\n");
+ free_orinocodev(dev);
+ return -EBUSY;
+ }
+
+ SET_NETDEV_DEV(dev, &mdev->ofdev.dev);
+
+ macio_set_drvdata(mdev, dev);
+
+ /* Setup interrupts & base address */
+ dev->irq = macio_irq(mdev, 0);
+ phys_addr = macio_resource_start(mdev, 0); /* Physical address */
+ printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr);
+ dev->base_addr = phys_addr;
+ card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN);
+ if (!card->vaddr) {
+ printk(KERN_ERR PFX "ioremap() failed\n");
+ goto failed;
+ }
+
+ hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING);
+
+ /* Power up card */
+ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1);
+ ssleep(1);
+
+ /* Reset it before we get the interrupt */
+ hermes_init(hw);
+
+ if (request_irq(dev->irq, orinoco_interrupt, 0, dev->name, dev)) {
+ printk(KERN_ERR PFX "Couldn't get IRQ %d\n", dev->irq);
+ goto failed;
+ }
+ card->irq_requested = 1;
+
+ /* Tell the stack we exist */
+ if (register_netdev(dev) != 0) {
+ printk(KERN_ERR PFX "register_netdev() failed\n");
+ goto failed;
+ }
+ printk(KERN_DEBUG PFX "Card registered for interface %s\n", dev->name);
+ card->ndev_registered = 1;
+ return 0;
+ failed:
+ airport_detach(mdev);
+ return -ENODEV;
+} /* airport_attach */
+
+
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+ " (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
+MODULE_LICENSE("Dual MPL/GPL");
+
+static struct of_device_id airport_match[] =
+{
+ {
+ .name = "radio",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE (of, airport_match);
+
+static struct macio_driver airport_driver =
+{
+ .name = DRIVER_NAME,
+ .match_table = airport_match,
+ .probe = airport_attach,
+ .remove = airport_detach,
+ .suspend = airport_suspend,
+ .resume = airport_resume,
+};
+
+static int __init
+init_airport(void)
+{
+ printk(KERN_DEBUG "%s\n", version);
+
+ return macio_register_driver(&airport_driver);
+}
+
+static void __exit
+exit_airport(void)
+{
+ macio_unregister_driver(&airport_driver);
+}
+
+module_init(init_airport);
+module_exit(exit_airport);
diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/orinoco/hermes.c
index bfa375369df3..bfa375369df3 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/orinoco/hermes.c
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index 8b13c8fef3dc..8b13c8fef3dc 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c
index d8c626e61a3a..d8c626e61a3a 100644
--- a/drivers/net/wireless/hermes_dld.c
+++ b/drivers/net/wireless/orinoco/hermes_dld.c
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/orinoco/hermes_dld.h
index 6fcb26277999..6fcb26277999 100644
--- a/drivers/net/wireless/hermes_dld.h
+++ b/drivers/net/wireless/orinoco/hermes_dld.h
diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/orinoco/hermes_rid.h
index 42eb67dea1df..42eb67dea1df 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/orinoco/hermes_rid.h
diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c
new file mode 100644
index 000000000000..bc84e2792f8a
--- /dev/null
+++ b/drivers/net/wireless/orinoco/orinoco.c
@@ -0,0 +1,6130 @@
+/* orinoco.c - (formerly known as dldwd_cs.c and orinoco_cs.c)
+ *
+ * A driver for Hermes or Prism 2 chipset based PCMCIA wireless
+ * adaptors, with Lucent/Agere, Intersil or Symbol firmware.
+ *
+ * Current maintainers (as of 29 September 2003) are:
+ * Pavel Roskin <proski AT gnu.org>
+ * and David Gibson <hermes AT gibson.dropbear.id.au>
+ *
+ * (C) Copyright David Gibson, IBM Corporation 2001-2003.
+ * Copyright (C) 2000 David Gibson, Linuxcare Australia.
+ * With some help from :
+ * Copyright (C) 2001 Jean Tourrilhes, HP Labs
+ * Copyright (C) 2001 Benjamin Herrenschmidt
+ *
+ * Based on dummy_cs.c 1.27 2000/06/12 21:27:25
+ *
+ * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy
+ * AT fasta.fh-dortmund.de>
+ * http://www.stud.fh-dortmund.de/~andy/wvlan/
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds AT users.sourceforge.net>. Portions created by David
+ * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights
+ * Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL. */
+
+/*
+ * TODO
+ * o Handle de-encapsulation within network layer, provide 802.11
+ * headers (patch from Thomas 'Dent' Mirlacher)
+ * o Fix possible races in SPY handling.
+ * o Disconnect wireless extensions from fundamental configuration.
+ * o (maybe) Software WEP support (patch from Stano Meduna).
+ * o (maybe) Use multiple Tx buffers - driver handling queue
+ * rather than firmware.
+ */
+
+/* Locking and synchronization:
+ *
+ * The basic principle is that everything is serialized through a
+ * single spinlock, priv->lock. The lock is used in user, bh and irq
+ * context, so when taken outside hardirq context it should always be
+ * taken with interrupts disabled. The lock protects both the
+ * hardware and the struct orinoco_private.
+ *
+ * Another flag, priv->hw_unavailable indicates that the hardware is
+ * unavailable for an extended period of time (e.g. suspended, or in
+ * the middle of a hard reset). This flag is protected by the
+ * spinlock. All code which touches the hardware should check the
+ * flag after taking the lock, and if it is set, give up on whatever
+ * they are doing and drop the lock again. The orinoco_lock()
+ * function handles this (it unlocks and returns -EBUSY if
+ * hw_unavailable is non-zero).
+ */
+
+#define DRIVER_NAME "orinoco"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/firmware.h>
+#include <linux/suspend.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+
+#include "hermes_rid.h"
+#include "hermes_dld.h"
+#include "orinoco.h"
+
+/********************************************************************/
+/* Module information */
+/********************************************************************/
+
+MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & David Gibson <hermes@gibson.dropbear.id.au>");
+MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/* Level of debugging. Used in the macros in orinoco.h */
+#ifdef ORINOCO_DEBUG
+int orinoco_debug = ORINOCO_DEBUG;
+module_param(orinoco_debug, int, 0644);
+MODULE_PARM_DESC(orinoco_debug, "Debug level");
+EXPORT_SYMBOL(orinoco_debug);
+#endif
+
+static int suppress_linkstatus; /* = 0 */
+module_param(suppress_linkstatus, bool, 0644);
+MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes");
+static int ignore_disconnect; /* = 0 */
+module_param(ignore_disconnect, int, 0644);
+MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer");
+
+static int force_monitor; /* = 0 */
+module_param(force_monitor, int, 0644);
+MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
+
+/********************************************************************/
+/* Compile time configuration and compatibility stuff */
+/********************************************************************/
+
+/* We do this this way to avoid ifdefs in the actual code */
+#ifdef WIRELESS_SPY
+#define SPY_NUMBER(priv) (priv->spy_data.spy_number)
+#else
+#define SPY_NUMBER(priv) 0
+#endif /* WIRELESS_SPY */
+
+/********************************************************************/
+/* Internal constants */
+/********************************************************************/
+
+/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
+static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
+
+#define ORINOCO_MIN_MTU 256
+#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD)
+
+#define SYMBOL_MAX_VER_LEN (14)
+#define USER_BAP 0
+#define IRQ_BAP 1
+#define MAX_IRQLOOPS_PER_IRQ 10
+#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
+ * how many events the
+ * device could
+ * legitimately generate */
+#define SMALL_KEY_SIZE 5
+#define LARGE_KEY_SIZE 13
+#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
+
+#define DUMMY_FID 0xFFFF
+
+/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
+ HERMES_MAX_MULTICAST : 0)*/
+#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
+
+#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \
+ | HERMES_EV_TX | HERMES_EV_TXEXC \
+ | HERMES_EV_WTERR | HERMES_EV_INFO \
+ | HERMES_EV_INFDROP )
+
+#define MAX_RID_LEN 1024
+
+static const struct iw_handler_def orinoco_handler_def;
+static const struct ethtool_ops orinoco_ethtool_ops;
+
+/********************************************************************/
+/* Data tables */
+/********************************************************************/
+
+/* The frequency of each channel in MHz */
+static const long channel_frequency[] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484
+};
+#define NUM_CHANNELS ARRAY_SIZE(channel_frequency)
+
+/* This tables gives the actual meanings of the bitrate IDs returned
+ * by the firmware. */
+static struct {
+ int bitrate; /* in 100s of kilobits */
+ int automatic;
+ u16 agere_txratectrl;
+ u16 intersil_txratectrl;
+} bitrate_table[] = {
+ {110, 1, 3, 15}, /* Entry 0 is the default */
+ {10, 0, 1, 1},
+ {10, 1, 1, 1},
+ {20, 0, 2, 2},
+ {20, 1, 6, 3},
+ {55, 0, 4, 4},
+ {55, 1, 7, 7},
+ {110, 0, 5, 8},
+};
+#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table)
+
+/********************************************************************/
+/* Data types */
+/********************************************************************/
+
+/* Beginning of the Tx descriptor, used in TxExc handling */
+struct hermes_txexc_data {
+ struct hermes_tx_descriptor desc;
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+} __attribute__ ((packed));
+
+/* Rx frame header except compatibility 802.3 header */
+struct hermes_rx_descriptor {
+ /* Control */
+ __le16 status;
+ __le32 time;
+ u8 silence;
+ u8 signal;
+ u8 rate;
+ u8 rxflow;
+ __le32 reserved;
+
+ /* 802.11 header */
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+
+ /* Data length */
+ __le16 data_len;
+} __attribute__ ((packed));
+
+/********************************************************************/
+/* Function prototypes */
+/********************************************************************/
+
+static int __orinoco_program_rids(struct net_device *dev);
+static void __orinoco_set_multicast_list(struct net_device *dev);
+
+/********************************************************************/
+/* Michael MIC crypto setup */
+/********************************************************************/
+#define MICHAEL_MIC_LEN 8
+static int orinoco_mic_init(struct orinoco_private *priv)
+{
+ priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
+ if (IS_ERR(priv->tx_tfm_mic)) {
+ printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_mic = NULL;
+ return -ENOMEM;
+ }
+
+ priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
+ if (IS_ERR(priv->rx_tfm_mic)) {
+ printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_mic = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void orinoco_mic_free(struct orinoco_private *priv)
+{
+ if (priv->tx_tfm_mic)
+ crypto_free_hash(priv->tx_tfm_mic);
+ if (priv->rx_tfm_mic)
+ crypto_free_hash(priv->rx_tfm_mic);
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 *key,
+ u8 *da, u8 *sa, u8 priority,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+ u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+
+ /* Copy header into buffer. We need the padding on the end zeroed */
+ memcpy(&hdr[0], da, ETH_ALEN);
+ memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
+ hdr[ETH_ALEN*2] = priority;
+ hdr[ETH_ALEN*2+1] = 0;
+ hdr[ETH_ALEN*2+2] = 0;
+ hdr[ETH_ALEN*2+3] = 0;
+
+ /* Use scatter gather to MIC header and data in one go */
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, sizeof(hdr));
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr),
+ mic);
+}
+
+/********************************************************************/
+/* Internal helper functions */
+/********************************************************************/
+
+static inline void set_port_type(struct orinoco_private *priv)
+{
+ switch (priv->iw_mode) {
+ case IW_MODE_INFRA:
+ priv->port_type = 1;
+ priv->createibss = 0;
+ break;
+ case IW_MODE_ADHOC:
+ if (priv->prefer_port3) {
+ priv->port_type = 3;
+ priv->createibss = 0;
+ } else {
+ priv->port_type = priv->ibss_port;
+ priv->createibss = 1;
+ }
+ break;
+ case IW_MODE_MONITOR:
+ priv->port_type = 3;
+ priv->createibss = 0;
+ break;
+ default:
+ printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
+ priv->ndev->name);
+ }
+}
+
+#define ORINOCO_MAX_BSS_COUNT 64
+static int orinoco_bss_data_allocate(struct orinoco_private *priv)
+{
+ if (priv->bss_xbss_data)
+ return 0;
+
+ if (priv->has_ext_scan)
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct xbss_element),
+ GFP_KERNEL);
+ else
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct bss_element),
+ GFP_KERNEL);
+
+ if (!priv->bss_xbss_data) {
+ printk(KERN_WARNING "Out of memory allocating beacons");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void orinoco_bss_data_free(struct orinoco_private *priv)
+{
+ kfree(priv->bss_xbss_data);
+ priv->bss_xbss_data = NULL;
+}
+
+#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
+#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
+static void orinoco_bss_data_init(struct orinoco_private *priv)
+{
+ int i;
+
+ INIT_LIST_HEAD(&priv->bss_free_list);
+ INIT_LIST_HEAD(&priv->bss_list);
+ if (priv->has_ext_scan)
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_XBSS[i].list),
+ &priv->bss_free_list);
+ else
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_BSS[i].list),
+ &priv->bss_free_list);
+
+}
+
+static inline u8 *orinoco_get_ie(u8 *data, size_t len,
+ enum ieee80211_eid eid)
+{
+ u8 *p = data;
+ while ((p + 2) < (data + len)) {
+ if (p[0] == eid)
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
+#define WPA_SELECTOR_LEN 4
+static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
+{
+ u8 *p = data;
+ while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
+ if ((p[0] == WLAN_EID_GENERIC) &&
+ (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+
+/********************************************************************/
+/* Download functionality */
+/********************************************************************/
+
+struct fw_info {
+ char *pri_fw;
+ char *sta_fw;
+ char *ap_fw;
+ u32 pda_addr;
+ u16 pda_size;
+};
+
+const static struct fw_info orinoco_fw[] = {
+ { NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
+ { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
+ { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 }
+};
+
+/* Structure used to access fields in FW
+ * Make sure LE decoding macros are used
+ */
+struct orinoco_fw_header {
+ char hdr_vers[6]; /* ASCII string for header version */
+ __le16 headersize; /* Total length of header */
+ __le32 entry_point; /* NIC entry point */
+ __le32 blocks; /* Number of blocks to program */
+ __le32 block_offset; /* Offset of block data from eof header */
+ __le32 pdr_offset; /* Offset to PDR data from eof header */
+ __le32 pri_offset; /* Offset to primary plug data */
+ __le32 compat_offset; /* Offset to compatibility data*/
+ char signature[0]; /* FW signature length headersize-20 */
+} __attribute__ ((packed));
+
+/* Download either STA or AP firmware into the card. */
+static int
+orinoco_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw,
+ int ap)
+{
+ /* Plug Data Area (PDA) */
+ __le16 *pda;
+
+ hermes_t *hw = &priv->hw;
+ const struct firmware *fw_entry;
+ const struct orinoco_fw_header *hdr;
+ const unsigned char *first_block;
+ const unsigned char *end;
+ const char *firmware;
+ struct net_device *dev = priv->ndev;
+ int err = 0;
+
+ pda = kzalloc(fw->pda_size, GFP_KERNEL);
+ if (!pda)
+ return -ENOMEM;
+
+ if (ap)
+ firmware = fw->ap_fw;
+ else
+ firmware = fw->sta_fw;
+
+ printk(KERN_DEBUG "%s: Attempting to download firmware %s\n",
+ dev->name, firmware);
+
+ /* Read current plug data */
+ err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
+ printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
+ if (err)
+ goto free;
+
+ if (!priv->cached_fw) {
+ err = request_firmware(&fw_entry, firmware, priv->dev);
+
+ if (err) {
+ printk(KERN_ERR "%s: Cannot find firmware %s\n",
+ dev->name, firmware);
+ err = -ENOENT;
+ goto free;
+ }
+ } else
+ fw_entry = priv->cached_fw;
+
+ hdr = (const struct orinoco_fw_header *) fw_entry->data;
+
+ /* Enable aux port to allow programming */
+ err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
+ printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Program data */
+ first_block = (fw_entry->data +
+ le16_to_cpu(hdr->headersize) +
+ le32_to_cpu(hdr->block_offset));
+ end = fw_entry->data + fw_entry->size;
+
+ err = hermes_program(hw, first_block, end);
+ printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Update production data */
+ first_block = (fw_entry->data +
+ le16_to_cpu(hdr->headersize) +
+ le32_to_cpu(hdr->pdr_offset));
+
+ err = hermes_apply_pda_with_defaults(hw, first_block, pda);
+ printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err);
+ if (err)
+ goto abort;
+
+ /* Tell card we've finished */
+ err = hermesi_program_end(hw);
+ printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Check if we're running */
+ printk(KERN_DEBUG "%s: hermes_present returned %d\n",
+ dev->name, hermes_present(hw));
+
+abort:
+ /* If we requested the firmware, release it. */
+ if (!priv->cached_fw)
+ release_firmware(fw_entry);
+
+free:
+ kfree(pda);
+ return err;
+}
+
+/* End markers */
+#define TEXT_END 0x1A /* End of text header */
+
+/*
+ * Process a firmware image - stop the card, load the firmware, reset
+ * the card and make sure it responds. For the secondary firmware take
+ * care of the PDA - read it and then write it on top of the firmware.
+ */
+static int
+symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
+ const unsigned char *image, const unsigned char *end,
+ int secondary)
+{
+ hermes_t *hw = &priv->hw;
+ int ret = 0;
+ const unsigned char *ptr;
+ const unsigned char *first_block;
+
+ /* Plug Data Area (PDA) */
+ __le16 *pda = NULL;
+
+ /* Binary block begins after the 0x1A marker */
+ ptr = image;
+ while (*ptr++ != TEXT_END);
+ first_block = ptr;
+
+ /* Read the PDA from EEPROM */
+ if (secondary) {
+ pda = kzalloc(fw->pda_size, GFP_KERNEL);
+ if (!pda)
+ return -ENOMEM;
+
+ ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
+ if (ret)
+ goto free;
+ }
+
+ /* Stop the firmware, so that it can be safely rewritten */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 1);
+ if (ret)
+ goto free;
+ }
+
+ /* Program the adapter with new firmware */
+ ret = hermes_program(hw, first_block, end);
+ if (ret)
+ goto free;
+
+ /* Write the PDA to the adapter */
+ if (secondary) {
+ size_t len = hermes_blocks_length(first_block);
+ ptr = first_block + len;
+ ret = hermes_apply_pda(hw, ptr, pda);
+ kfree(pda);
+ if (ret)
+ return ret;
+ }
+
+ /* Run the firmware */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Reset hermes chip and make sure it responds */
+ ret = hermes_init(hw);
+
+ /* hermes_reset() should return 0 with the secondary firmware */
+ if (secondary && ret != 0)
+ return -ENODEV;
+
+ /* And this should work with any firmware */
+ if (!hermes_present(hw))
+ return -ENODEV;
+
+ return 0;
+
+free:
+ kfree(pda);
+ return ret;
+}
+
+
+/*
+ * Download the firmware into the card, this also does a PCMCIA soft
+ * reset on the card, to make sure it's in a sane state.
+ */
+static int
+symbol_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw)
+{
+ struct net_device *dev = priv->ndev;
+ int ret;
+ const struct firmware *fw_entry;
+
+ if (!priv->cached_pri_fw) {
+ if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->pri_fw);
+ return -ENOENT;
+ }
+ } else
+ fw_entry = priv->cached_pri_fw;
+
+ /* Load primary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 0);
+
+ if (!priv->cached_pri_fw)
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Primary firmware download failed\n",
+ dev->name);
+ return ret;
+ }
+
+ if (!priv->cached_fw) {
+ if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->sta_fw);
+ return -ENOENT;
+ }
+ } else
+ fw_entry = priv->cached_fw;
+
+ /* Load secondary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 1);
+ if (!priv->cached_fw)
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Secondary firmware download failed\n",
+ dev->name);
+ }
+
+ return ret;
+}
+
+static int orinoco_download(struct orinoco_private *priv)
+{
+ int err = 0;
+ /* Reload firmware */
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ /* case FIRMWARE_TYPE_INTERSIL: */
+ err = orinoco_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type], 0);
+ break;
+
+ case FIRMWARE_TYPE_SYMBOL:
+ err = symbol_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type]);
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ break;
+ }
+ /* TODO: if we fail we probably need to reinitialise
+ * the driver */
+
+ return err;
+}
+
+#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
+static void orinoco_cache_fw(struct orinoco_private *priv, int ap)
+{
+ const struct firmware *fw_entry = NULL;
+ const char *pri_fw;
+ const char *fw;
+
+ pri_fw = orinoco_fw[priv->firmware_type].pri_fw;
+ if (ap)
+ fw = orinoco_fw[priv->firmware_type].ap_fw;
+ else
+ fw = orinoco_fw[priv->firmware_type].sta_fw;
+
+ if (pri_fw) {
+ if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0)
+ priv->cached_pri_fw = fw_entry;
+ }
+
+ if (fw) {
+ if (request_firmware(&fw_entry, fw, priv->dev) == 0)
+ priv->cached_fw = fw_entry;
+ }
+}
+
+static void orinoco_uncache_fw(struct orinoco_private *priv)
+{
+ if (priv->cached_pri_fw)
+ release_firmware(priv->cached_pri_fw);
+ if (priv->cached_fw)
+ release_firmware(priv->cached_fw);
+
+ priv->cached_pri_fw = NULL;
+ priv->cached_fw = NULL;
+}
+#else
+#define orinoco_cache_fw(priv, ap)
+#define orinoco_uncache_fw(priv)
+#endif
+
+/********************************************************************/
+/* Device methods */
+/********************************************************************/
+
+static int orinoco_open(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = __orinoco_up(dev);
+
+ if (! err)
+ priv->open = 1;
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_stop(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = 0;
+
+ /* We mustn't use orinoco_lock() here, because we need to be
+ able to close the interface even if hw_unavailable is set
+ (e.g. as we're released after a PC Card removal) */
+ spin_lock_irq(&priv->lock);
+
+ priv->open = 0;
+
+ err = __orinoco_down(dev);
+
+ spin_unlock_irq(&priv->lock);
+
+ return err;
+}
+
+static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ return &priv->stats;
+}
+
+static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_statistics *wstats = &priv->wstats;
+ int err;
+ unsigned long flags;
+
+ if (! netif_device_present(dev)) {
+ printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
+ dev->name);
+ return NULL; /* FIXME: Can we do better than this? */
+ }
+
+ /* If busy, return the old stats. Returning NULL may cause
+ * the interface to disappear from /proc/net/wireless */
+ if (orinoco_lock(priv, &flags) != 0)
+ return wstats;
+
+ /* We can't really wait for the tallies inquiry command to
+ * complete, so we just use the previous results and trigger
+ * a new tallies inquiry command for next time - Jean II */
+ /* FIXME: Really we should wait for the inquiry to come back -
+ * as it is the stats we give don't make a whole lot of sense.
+ * Unfortunately, it's not clear how to do that within the
+ * wireless extensions framework: I think we're in user
+ * context, but a lock seems to be held by the time we get in
+ * here so we're not safe to sleep here. */
+ hermes_inquire(hw, HERMES_INQ_TALLIES);
+
+ if (priv->iw_mode == IW_MODE_ADHOC) {
+ memset(&wstats->qual, 0, sizeof(wstats->qual));
+ /* If a spy address is defined, we report stats of the
+ * first spy address - Jean II */
+ if (SPY_NUMBER(priv)) {
+ wstats->qual.qual = priv->spy_data.spy_stat[0].qual;
+ wstats->qual.level = priv->spy_data.spy_stat[0].level;
+ wstats->qual.noise = priv->spy_data.spy_stat[0].noise;
+ wstats->qual.updated = priv->spy_data.spy_stat[0].updated;
+ }
+ } else {
+ struct {
+ __le16 qual, signal, noise, unused;
+ } __attribute__ ((packed)) cq;
+
+ err = HERMES_READ_RECORD(hw, USER_BAP,
+ HERMES_RID_COMMSQUALITY, &cq);
+
+ if (!err) {
+ wstats->qual.qual = (int)le16_to_cpu(cq.qual);
+ wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
+ wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
+ wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ }
+ }
+
+ orinoco_unlock(priv, &flags);
+ return wstats;
+}
+
+static void orinoco_set_multicast_list(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0) {
+ printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
+ "called when hw_unavailable\n", dev->name);
+ return;
+ }
+
+ __orinoco_set_multicast_list(dev);
+ orinoco_unlock(priv, &flags);
+}
+
+static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
+ return -EINVAL;
+
+ /* MTU + encapsulation + header length */
+ if ( (new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) >
+ (priv->nicbuf_size - ETH_HLEN) )
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+/********************************************************************/
+/* Tx path */
+/********************************************************************/
+
+static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u16 txfid = priv->txfid;
+ struct ethhdr *eh;
+ int tx_control;
+ unsigned long flags;
+
+ if (! netif_running(dev)) {
+ printk(KERN_ERR "%s: Tx on stopped device!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (netif_queue_stopped(dev)) {
+ printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (orinoco_lock(priv, &flags) != 0) {
+ printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
+ /* Oops, the firmware hasn't established a connection,
+ silently drop the packet (this seems to be the
+ safest approach). */
+ goto drop;
+ }
+
+ /* Check packet length */
+ if (skb->len < ETH_HLEN)
+ goto drop;
+
+ tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
+
+ if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
+ tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
+ HERMES_TXCTRL_MIC;
+
+ if (priv->has_alt_txcntl) {
+ /* WPA enabled firmwares have tx_cntl at the end of
+ * the 802.11 header. So write zeroed descriptor and
+ * 802.11 header at the same time
+ */
+ char desc[HERMES_802_3_OFFSET];
+ __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
+
+ memset(&desc, 0, sizeof(desc));
+
+ *txcntl = cpu_to_le16(tx_control);
+ err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d writing Tx "
+ "descriptor to BAP\n", dev->name, err);
+ goto busy;
+ }
+ } else {
+ struct hermes_tx_descriptor desc;
+
+ memset(&desc, 0, sizeof(desc));
+
+ desc.tx_control = cpu_to_le16(tx_control);
+ err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d writing Tx "
+ "descriptor to BAP\n", dev->name, err);
+ goto busy;
+ }
+
+ /* Clear the 802.11 header and data length fields - some
+ * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
+ * if this isn't done. */
+ hermes_clear_words(hw, HERMES_DATA0,
+ HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
+ }
+
+ eh = (struct ethhdr *)skb->data;
+
+ /* Encapsulate Ethernet-II frames */
+ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
+ struct header_struct {
+ struct ethhdr eth; /* 802.3 header */
+ u8 encap[6]; /* 802.2 header */
+ } __attribute__ ((packed)) hdr;
+
+ /* Strip destination and source from the data */
+ skb_pull(skb, 2 * ETH_ALEN);
+
+ /* And move them to a separate header */
+ memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
+ hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
+ memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));
+
+ /* Insert the SNAP header */
+ if (skb_headroom(skb) < sizeof(hdr)) {
+ printk(KERN_ERR
+ "%s: Not enough headroom for 802.2 headers %d\n",
+ dev->name, skb_headroom(skb));
+ goto drop;
+ }
+ eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
+ memcpy(eh, &hdr, sizeof(hdr));
+ }
+
+ err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
+ txfid, HERMES_802_3_OFFSET);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
+ dev->name, err);
+ goto busy;
+ }
+
+ /* Calculate Michael MIC */
+ if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
+ u8 mic_buf[MICHAEL_MIC_LEN + 1];
+ u8 *mic;
+ size_t offset;
+ size_t len;
+
+ if (skb->len % 2) {
+ /* MIC start is on an odd boundary */
+ mic_buf[0] = skb->data[skb->len - 1];
+ mic = &mic_buf[1];
+ offset = skb->len - 1;
+ len = MICHAEL_MIC_LEN + 1;
+ } else {
+ mic = &mic_buf[0];
+ offset = skb->len;
+ len = MICHAEL_MIC_LEN;
+ }
+
+ michael_mic(priv->tx_tfm_mic,
+ priv->tkip_key[priv->tx_key].tx_mic,
+ eh->h_dest, eh->h_source, 0 /* priority */,
+ skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
+
+ /* Write the MIC */
+ err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
+ txfid, HERMES_802_3_OFFSET + offset);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
+ dev->name, err);
+ goto busy;
+ }
+ }
+
+ /* Finally, we actually initiate the send */
+ netif_stop_queue(dev);
+
+ err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
+ txfid, NULL);
+ if (err) {
+ netif_start_queue(dev);
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d transmitting packet\n",
+ dev->name, err);
+ goto busy;
+ }
+
+ dev->trans_start = jiffies;
+ stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
+ goto ok;
+
+ drop:
+ stats->tx_errors++;
+ stats->tx_dropped++;
+
+ ok:
+ orinoco_unlock(priv, &flags);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+
+ busy:
+ if (err == -EIO)
+ schedule_work(&priv->reset_work);
+ orinoco_unlock(priv, &flags);
+ return NETDEV_TX_BUSY;
+}
+
+static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u16 fid = hermes_read_regn(hw, ALLOCFID);
+
+ if (fid != priv->txfid) {
+ if (fid != DUMMY_FID)
+ printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
+ dev->name, fid);
+ return;
+ }
+
+ hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
+}
+
+static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+
+ stats->tx_packets++;
+
+ netif_wake_queue(dev);
+
+ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
+}
+
+static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ u16 fid = hermes_read_regn(hw, TXCOMPLFID);
+ u16 status;
+ struct hermes_txexc_data hdr;
+ int err = 0;
+
+ if (fid == DUMMY_FID)
+ return; /* Nothing's really happened */
+
+ /* Read part of the frame header - we need status and addr1 */
+ err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
+ sizeof(struct hermes_txexc_data),
+ fid, 0);
+
+ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
+ stats->tx_errors++;
+
+ if (err) {
+ printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
+ "(FID=%04X error %d)\n",
+ dev->name, fid, err);
+ return;
+ }
+
+ DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
+ err, fid);
+
+ /* We produce a TXDROP event only for retry or lifetime
+ * exceeded, because that's the only status that really mean
+ * that this particular node went away.
+ * Other errors means that *we* screwed up. - Jean II */
+ status = le16_to_cpu(hdr.desc.status);
+ if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
+ union iwreq_data wrqu;
+
+ /* Copy 802.11 dest address.
+ * We use the 802.11 header because the frame may
+ * not be 802.3 or may be mangled...
+ * In Ad-Hoc mode, it will be the node address.
+ * In managed mode, it will be most likely the AP addr
+ * User space will figure out how to convert it to
+ * whatever it needs (IP address or else).
+ * - Jean II */
+ memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
+ }
+
+ netif_wake_queue(dev);
+}
+
+static void orinoco_tx_timeout(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ struct hermes *hw = &priv->hw;
+
+ printk(KERN_WARNING "%s: Tx timeout! "
+ "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
+ dev->name, hermes_read_regn(hw, ALLOCFID),
+ hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
+
+ stats->tx_errors++;
+
+ schedule_work(&priv->reset_work);
+}
+
+/********************************************************************/
+/* Rx path (data frames) */
+/********************************************************************/
+
+/* Does the frame have a SNAP header indicating it should be
+ * de-encapsulated to Ethernet-II? */
+static inline int is_ethersnap(void *_hdr)
+{
+ u8 *hdr = _hdr;
+
+ /* We de-encapsulate all packets which, a) have SNAP headers
+ * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
+ * and where b) the OUI of the SNAP header is 00:00:00 or
+ * 00:00:f8 - we need both because different APs appear to use
+ * different OUIs for some reason */
+ return (memcmp(hdr, &encaps_hdr, 5) == 0)
+ && ( (hdr[5] == 0x00) || (hdr[5] == 0xf8) );
+}
+
+static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
+ int level, int noise)
+{
+ struct iw_quality wstats;
+ wstats.level = level - 0x95;
+ wstats.noise = noise - 0x95;
+ wstats.qual = (level > noise) ? (level - noise) : 0;
+ wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ /* Update spy records */
+ wireless_spy_update(dev, mac, &wstats);
+}
+
+static void orinoco_stat_gather(struct net_device *dev,
+ struct sk_buff *skb,
+ struct hermes_rx_descriptor *desc)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ /* Using spy support with lots of Rx packets, like in an
+ * infrastructure (AP), will really slow down everything, because
+ * the MAC address must be compared to each entry of the spy list.
+ * If the user really asks for it (set some address in the
+ * spy list), we do it, but he will pay the price.
+ * Note that to get here, you need both WIRELESS_SPY
+ * compiled in AND some addresses in the list !!!
+ */
+ /* Note : gcc will optimise the whole section away if
+ * WIRELESS_SPY is not defined... - Jean II */
+ if (SPY_NUMBER(priv)) {
+ orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN,
+ desc->signal, desc->silence);
+ }
+}
+
+/*
+ * orinoco_rx_monitor - handle received monitor frames.
+ *
+ * Arguments:
+ * dev network device
+ * rxfid received FID
+ * desc rx descriptor of the frame
+ *
+ * Call context: interrupt
+ */
+static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
+ struct hermes_rx_descriptor *desc)
+{
+ u32 hdrlen = 30; /* return full header by default */
+ u32 datalen = 0;
+ u16 fc;
+ int err;
+ int len;
+ struct sk_buff *skb;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ hermes_t *hw = &priv->hw;
+
+ len = le16_to_cpu(desc->data_len);
+
+ /* Determine the size of the header and the data */
+ fc = le16_to_cpu(desc->frame_ctl);
+ switch (fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_DATA:
+ if ((fc & IEEE80211_FCTL_TODS)
+ && (fc & IEEE80211_FCTL_FROMDS))
+ hdrlen = 30;
+ else
+ hdrlen = 24;
+ datalen = len;
+ break;
+ case IEEE80211_FTYPE_MGMT:
+ hdrlen = 24;
+ datalen = len;
+ break;
+ case IEEE80211_FTYPE_CTL:
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PSPOLL:
+ case IEEE80211_STYPE_RTS:
+ case IEEE80211_STYPE_CFEND:
+ case IEEE80211_STYPE_CFENDACK:
+ hdrlen = 16;
+ break;
+ case IEEE80211_STYPE_CTS:
+ case IEEE80211_STYPE_ACK:
+ hdrlen = 10;
+ break;
+ }
+ break;
+ default:
+ /* Unknown frame type */
+ break;
+ }
+
+ /* sanity check the length */
+ if (datalen > IEEE80211_MAX_DATA_LEN + 12) {
+ printk(KERN_DEBUG "%s: oversized monitor frame, "
+ "data length = %d\n", dev->name, datalen);
+ stats->rx_length_errors++;
+ goto update_stats;
+ }
+
+ skb = dev_alloc_skb(hdrlen + datalen);
+ if (!skb) {
+ printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
+ dev->name);
+ goto update_stats;
+ }
+
+ /* Copy the 802.11 header to the skb */
+ memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
+ skb_reset_mac_header(skb);
+
+ /* If any, copy the data from the card to the skb */
+ if (datalen > 0) {
+ err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
+ ALIGN(datalen, 2), rxfid,
+ HERMES_802_2_OFFSET);
+ if (err) {
+ printk(KERN_ERR "%s: error %d reading monitor frame\n",
+ dev->name, err);
+ goto drop;
+ }
+ }
+
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_802_2);
+
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
+ netif_rx(skb);
+ return;
+
+ drop:
+ dev_kfree_skb_irq(skb);
+ update_stats:
+ stats->rx_errors++;
+ stats->rx_dropped++;
+}
+
+/* Get tsc from the firmware */
+static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
+ u8 *tsc)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
+
+ if ((key < 0) || (key > 4))
+ return -EINVAL;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
+ sizeof(tsc_arr), NULL, &tsc_arr);
+ if (!err)
+ memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
+
+ return err;
+}
+
+static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ struct iw_statistics *wstats = &priv->wstats;
+ struct sk_buff *skb = NULL;
+ u16 rxfid, status;
+ int length;
+ struct hermes_rx_descriptor *desc;
+ struct orinoco_rx_data *rx_data;
+ int err;
+
+ desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+ if (!desc) {
+ printk(KERN_WARNING
+ "%s: Can't allocate space for RX descriptor\n",
+ dev->name);
+ goto update_stats;
+ }
+
+ rxfid = hermes_read_regn(hw, RXFID);
+
+ err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
+ rxfid, 0);
+ if (err) {
+ printk(KERN_ERR "%s: error %d reading Rx descriptor. "
+ "Frame dropped.\n", dev->name, err);
+ goto update_stats;
+ }
+
+ status = le16_to_cpu(desc->status);
+
+ if (status & HERMES_RXSTAT_BADCRC) {
+ DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
+ dev->name);
+ stats->rx_crc_errors++;
+ goto update_stats;
+ }
+
+ /* Handle frames in monitor mode */
+ if (priv->iw_mode == IW_MODE_MONITOR) {
+ orinoco_rx_monitor(dev, rxfid, desc);
+ goto out;
+ }
+
+ if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
+ DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
+ dev->name);
+ wstats->discard.code++;
+ goto update_stats;
+ }
+
+ length = le16_to_cpu(desc->data_len);
+
+ /* Sanity checks */
+ if (length < 3) { /* No for even an 802.2 LLC header */
+ /* At least on Symbol firmware with PCF we get quite a
+ lot of these legitimately - Poll frames with no
+ data. */
+ goto out;
+ }
+ if (length > IEEE80211_MAX_DATA_LEN) {
+ printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
+ dev->name, length);
+ stats->rx_length_errors++;
+ goto update_stats;
+ }
+
+ /* Payload size does not include Michael MIC. Increase payload
+ * size to read it together with the data. */
+ if (status & HERMES_RXSTAT_MIC)
+ length += MICHAEL_MIC_LEN;
+
+ /* We need space for the packet data itself, plus an ethernet
+ header, plus 2 bytes so we can align the IP header on a
+ 32bit boundary, plus 1 byte so we can read in odd length
+ packets from the card, which has an IO granularity of 16
+ bits */
+ skb = dev_alloc_skb(length+ETH_HLEN+2+1);
+ if (!skb) {
+ printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
+ dev->name);
+ goto update_stats;
+ }
+
+ /* We'll prepend the header, so reserve space for it. The worst
+ case is no decapsulation, when 802.3 header is prepended and
+ nothing is removed. 2 is for aligning the IP header. */
+ skb_reserve(skb, ETH_HLEN + 2);
+
+ err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
+ ALIGN(length, 2), rxfid,
+ HERMES_802_2_OFFSET);
+ if (err) {
+ printk(KERN_ERR "%s: error %d reading frame. "
+ "Frame dropped.\n", dev->name, err);
+ goto drop;
+ }
+
+ /* Add desc and skb to rx queue */
+ rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
+ if (!rx_data) {
+ printk(KERN_WARNING "%s: Can't allocate RX packet\n",
+ dev->name);
+ goto drop;
+ }
+ rx_data->desc = desc;
+ rx_data->skb = skb;
+ list_add_tail(&rx_data->list, &priv->rx_list);
+ tasklet_schedule(&priv->rx_tasklet);
+
+ return;
+
+drop:
+ dev_kfree_skb_irq(skb);
+update_stats:
+ stats->rx_errors++;
+ stats->rx_dropped++;
+out:
+ kfree(desc);
+}
+
+static void orinoco_rx(struct net_device *dev,
+ struct hermes_rx_descriptor *desc,
+ struct sk_buff *skb)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ u16 status, fc;
+ int length;
+ struct ethhdr *hdr;
+
+ status = le16_to_cpu(desc->status);
+ length = le16_to_cpu(desc->data_len);
+ fc = le16_to_cpu(desc->frame_ctl);
+
+ /* Calculate and check MIC */
+ if (status & HERMES_RXSTAT_MIC) {
+ int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
+ HERMES_MIC_KEY_ID_SHIFT);
+ u8 mic[MICHAEL_MIC_LEN];
+ u8 *rxmic;
+ u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
+ desc->addr3 : desc->addr2;
+
+ /* Extract Michael MIC from payload */
+ rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
+
+ skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
+ length -= MICHAEL_MIC_LEN;
+
+ michael_mic(priv->rx_tfm_mic,
+ priv->tkip_key[key_id].rx_mic,
+ desc->addr1,
+ src,
+ 0, /* priority or QoS? */
+ skb->data,
+ skb->len,
+ &mic[0]);
+
+ if (memcmp(mic, rxmic,
+ MICHAEL_MIC_LEN)) {
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure wxmic;
+
+ printk(KERN_WARNING "%s: "
+ "Invalid Michael MIC in data frame from %pM, "
+ "using key %i\n",
+ dev->name, src, key_id);
+
+ /* TODO: update stats */
+
+ /* Notify userspace */
+ memset(&wxmic, 0, sizeof(wxmic));
+ wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
+ wxmic.flags |= (desc->addr1[0] & 1) ?
+ IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
+ wxmic.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
+
+ (void) orinoco_hw_get_tkip_iv(priv, key_id,
+ &wxmic.tsc[0]);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(wxmic);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
+ (char *) &wxmic);
+
+ goto drop;
+ }
+ }
+
+ /* Handle decapsulation
+ * In most cases, the firmware tell us about SNAP frames.
+ * For some reason, the SNAP frames sent by LinkSys APs
+ * are not properly recognised by most firmwares.
+ * So, check ourselves */
+ if (length >= ENCAPS_OVERHEAD &&
+ (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
+ ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
+ is_ethersnap(skb->data))) {
+ /* These indicate a SNAP within 802.2 LLC within
+ 802.11 frame which we'll need to de-encapsulate to
+ the original EthernetII frame. */
+ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD);
+ } else {
+ /* 802.3 frame - prepend 802.3 header as is */
+ hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+ hdr->h_proto = htons(length);
+ }
+ memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
+ if (fc & IEEE80211_FCTL_FROMDS)
+ memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
+ else
+ memcpy(hdr->h_source, desc->addr2, ETH_ALEN);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ if (fc & IEEE80211_FCTL_TODS)
+ skb->pkt_type = PACKET_OTHERHOST;
+
+ /* Process the wireless stats if needed */
+ orinoco_stat_gather(dev, skb, desc);
+
+ /* Pass the packet to the networking stack */
+ netif_rx(skb);
+ stats->rx_packets++;
+ stats->rx_bytes += length;
+
+ return;
+
+ drop:
+ dev_kfree_skb(skb);
+ stats->rx_errors++;
+ stats->rx_dropped++;
+}
+
+static void orinoco_rx_isr_tasklet(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *) data;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_rx_data *rx_data, *temp;
+ struct hermes_rx_descriptor *desc;
+ struct sk_buff *skb;
+
+ /* extract desc and skb from queue */
+ list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
+ desc = rx_data->desc;
+ skb = rx_data->skb;
+ list_del(&rx_data->list);
+ kfree(rx_data);
+
+ orinoco_rx(dev, desc, skb);
+
+ kfree(desc);
+ }
+}
+
+/********************************************************************/
+/* Rx path (info frames) */
+/********************************************************************/
+
+static void print_linkstatus(struct net_device *dev, u16 status)
+{
+ char * s;
+
+ if (suppress_linkstatus)
+ return;
+
+ switch (status) {
+ case HERMES_LINKSTATUS_NOT_CONNECTED:
+ s = "Not Connected";
+ break;
+ case HERMES_LINKSTATUS_CONNECTED:
+ s = "Connected";
+ break;
+ case HERMES_LINKSTATUS_DISCONNECTED:
+ s = "Disconnected";
+ break;
+ case HERMES_LINKSTATUS_AP_CHANGE:
+ s = "AP Changed";
+ break;
+ case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
+ s = "AP Out of Range";
+ break;
+ case HERMES_LINKSTATUS_AP_IN_RANGE:
+ s = "AP In Range";
+ break;
+ case HERMES_LINKSTATUS_ASSOC_FAILED:
+ s = "Association Failed";
+ break;
+ default:
+ s = "UNKNOWN";
+ }
+
+ printk(KERN_INFO "%s: New link status: %s (%04x)\n",
+ dev->name, s, status);
+}
+
+/* Search scan results for requested BSSID, join it if found */
+static void orinoco_join_ap(struct work_struct *work)
+{
+ struct orinoco_private *priv =
+ container_of(work, struct orinoco_private, join_work);
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ int err;
+ unsigned long flags;
+ struct join_req {
+ u8 bssid[ETH_ALEN];
+ __le16 channel;
+ } __attribute__ ((packed)) req;
+ const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
+ struct prism2_scan_apinfo *atom = NULL;
+ int offset = 4;
+ int found = 0;
+ u8 *buf;
+ u16 len;
+
+ /* Allocate buffer for scan results */
+ buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
+ if (! buf)
+ return;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ goto fail_lock;
+
+ /* Sanity checks in case user changed something in the meantime */
+ if (! priv->bssid_fixed)
+ goto out;
+
+ if (strlen(priv->desired_essid) == 0)
+ goto out;
+
+ /* Read scan results from the firmware */
+ err = hermes_read_ltv(hw, USER_BAP,
+ HERMES_RID_SCANRESULTSTABLE,
+ MAX_SCAN_LEN, &len, buf);
+ if (err) {
+ printk(KERN_ERR "%s: Cannot read scan results\n",
+ dev->name);
+ goto out;
+ }
+
+ len = HERMES_RECLEN_TO_BYTES(len);
+
+ /* Go through the scan results looking for the channel of the AP
+ * we were requested to join */
+ for (; offset + atom_len <= len; offset += atom_len) {
+ atom = (struct prism2_scan_apinfo *) (buf + offset);
+ if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (! found) {
+ DEBUG(1, "%s: Requested AP not found in scan results\n",
+ dev->name);
+ goto out;
+ }
+
+ memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
+ req.channel = atom->channel; /* both are little-endian */
+ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
+ &req);
+ if (err)
+ printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ fail_lock:
+ kfree(buf);
+}
+
+/* Send new BSSID to userspace */
+static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+ ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
+ if (err != 0)
+ return;
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ /* Send event to user space */
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88];
+ u8 *ie;
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ ie = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (ie) {
+ int rem = sizeof(buf) - (ie - &buf[0]);
+ wrqu.data.length = ie[1] + 2;
+ if (wrqu.data.length > rem)
+ wrqu.data.length = rem;
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
+ }
+}
+
+static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
+ u8 *ie;
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ ie = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (ie) {
+ int rem = sizeof(buf) - (ie - &buf[0]);
+ wrqu.data.length = ie[1] + 2;
+ if (wrqu.data.length > rem)
+ wrqu.data.length = rem;
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+ }
+}
+
+static void orinoco_send_wevents(struct work_struct *work)
+{
+ struct orinoco_private *priv =
+ container_of(work, struct orinoco_private, wevent_work);
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return;
+
+ orinoco_send_assocreqie_wevent(priv);
+ orinoco_send_assocrespie_wevent(priv);
+ orinoco_send_bssid_wevent(priv);
+
+ orinoco_unlock(priv, &flags);
+}
+
+static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
+ unsigned long scan_age)
+{
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+ struct xbss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
+ }
+ } else {
+ struct bss_element *bss;
+ struct bss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
+ }
+ }
+}
+
+static void orinoco_add_ext_scan_result(struct orinoco_private *priv,
+ struct agere_ext_scan_info *atom)
+{
+ struct xbss_element *bss = NULL;
+ int found = 0;
+
+ /* Try to update an existing bss first */
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ if (compare_ether_addr(bss->bss.bssid, atom->bssid))
+ continue;
+ /* ESSID lengths */
+ if (bss->bss.data[1] != atom->data[1])
+ continue;
+ if (memcmp(&bss->bss.data[2], &atom->data[2],
+ atom->data[1]))
+ continue;
+ found = 1;
+ break;
+ }
+
+ /* Grab a bss off the free list */
+ if (!found && !list_empty(&priv->bss_free_list)) {
+ bss = list_entry(priv->bss_free_list.next,
+ struct xbss_element, list);
+ list_del(priv->bss_free_list.next);
+
+ list_add_tail(&bss->list, &priv->bss_list);
+ }
+
+ if (bss) {
+ /* Always update the BSS to get latest beacon info */
+ memcpy(&bss->bss, atom, sizeof(bss->bss));
+ bss->last_scanned = jiffies;
+ }
+}
+
+static int orinoco_process_scan_results(struct net_device *dev,
+ unsigned char *buf,
+ int len)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int offset; /* In the scan data */
+ union hermes_scan_info *atom;
+ int atom_len;
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ atom_len = sizeof(struct agere_scan_apinfo);
+ offset = 0;
+ break;
+ case FIRMWARE_TYPE_SYMBOL:
+ /* Lack of documentation necessitates this hack.
+ * Different firmwares have 68 or 76 byte long atoms.
+ * We try modulo first. If the length divides by both,
+ * we check what would be the channel in the second
+ * frame for a 68-byte atom. 76-byte atoms have 0 there.
+ * Valid channel cannot be 0. */
+ if (len % 76)
+ atom_len = 68;
+ else if (len % 68)
+ atom_len = 76;
+ else if (len >= 1292 && buf[68] == 0)
+ atom_len = 76;
+ else
+ atom_len = 68;
+ offset = 0;
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ offset = 4;
+ if (priv->has_hostscan) {
+ atom_len = le16_to_cpup((__le16 *)buf);
+ /* Sanity check for atom_len */
+ if (atom_len < sizeof(struct prism2_scan_apinfo)) {
+ printk(KERN_ERR "%s: Invalid atom_len in scan "
+ "data: %d\n", dev->name, atom_len);
+ return -EIO;
+ }
+ } else
+ atom_len = offsetof(struct prism2_scan_apinfo, atim);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Check that we got an whole number of atoms */
+ if ((len - offset) % atom_len) {
+ printk(KERN_ERR "%s: Unexpected scan data length %d, "
+ "atom_len %d, offset %d\n", dev->name, len,
+ atom_len, offset);
+ return -EIO;
+ }
+
+ orinoco_clear_scan_results(priv, msecs_to_jiffies(15000));
+
+ /* Read the entries one by one */
+ for (; offset + atom_len <= len; offset += atom_len) {
+ int found = 0;
+ struct bss_element *bss = NULL;
+
+ /* Get next atom */
+ atom = (union hermes_scan_info *) (buf + offset);
+
+ /* Try to update an existing bss first */
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid))
+ continue;
+ if (le16_to_cpu(bss->bss.a.essid_len) !=
+ le16_to_cpu(atom->a.essid_len))
+ continue;
+ if (memcmp(bss->bss.a.essid, atom->a.essid,
+ le16_to_cpu(atom->a.essid_len)))
+ continue;
+ found = 1;
+ break;
+ }
+
+ /* Grab a bss off the free list */
+ if (!found && !list_empty(&priv->bss_free_list)) {
+ bss = list_entry(priv->bss_free_list.next,
+ struct bss_element, list);
+ list_del(priv->bss_free_list.next);
+
+ list_add_tail(&bss->list, &priv->bss_list);
+ }
+
+ if (bss) {
+ /* Always update the BSS to get latest beacon info */
+ memcpy(&bss->bss, atom, sizeof(bss->bss));
+ bss->last_scanned = jiffies;
+ }
+ }
+
+ return 0;
+}
+
+static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u16 infofid;
+ struct {
+ __le16 len;
+ __le16 type;
+ } __attribute__ ((packed)) info;
+ int len, type;
+ int err;
+
+ /* This is an answer to an INQUIRE command that we did earlier,
+ * or an information "event" generated by the card
+ * The controller return to us a pseudo frame containing
+ * the information in question - Jean II */
+ infofid = hermes_read_regn(hw, INFOFID);
+
+ /* Read the info frame header - don't try too hard */
+ err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
+ infofid, 0);
+ if (err) {
+ printk(KERN_ERR "%s: error %d reading info frame. "
+ "Frame dropped.\n", dev->name, err);
+ return;
+ }
+
+ len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
+ type = le16_to_cpu(info.type);
+
+ switch (type) {
+ case HERMES_INQ_TALLIES: {
+ struct hermes_tallies_frame tallies;
+ struct iw_statistics *wstats = &priv->wstats;
+
+ if (len > sizeof(tallies)) {
+ printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
+ dev->name, len);
+ len = sizeof(tallies);
+ }
+
+ err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
+ infofid, sizeof(info));
+ if (err)
+ break;
+
+ /* Increment our various counters */
+ /* wstats->discard.nwid - no wrong BSSID stuff */
+ wstats->discard.code +=
+ le16_to_cpu(tallies.RxWEPUndecryptable);
+ if (len == sizeof(tallies))
+ wstats->discard.code +=
+ le16_to_cpu(tallies.RxDiscards_WEPICVError) +
+ le16_to_cpu(tallies.RxDiscards_WEPExcluded);
+ wstats->discard.misc +=
+ le16_to_cpu(tallies.TxDiscardsWrongSA);
+ wstats->discard.fragment +=
+ le16_to_cpu(tallies.RxMsgInBadMsgFragments);
+ wstats->discard.retries +=
+ le16_to_cpu(tallies.TxRetryLimitExceeded);
+ /* wstats->miss.beacon - no match */
+ }
+ break;
+ case HERMES_INQ_LINKSTATUS: {
+ struct hermes_linkstatus linkstatus;
+ u16 newstatus;
+ int connected;
+
+ if (priv->iw_mode == IW_MODE_MONITOR)
+ break;
+
+ if (len != sizeof(linkstatus)) {
+ printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
+ infofid, sizeof(info));
+ if (err)
+ break;
+ newstatus = le16_to_cpu(linkstatus.linkstatus);
+
+ /* Symbol firmware uses "out of range" to signal that
+ * the hostscan frame can be requested. */
+ if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
+ priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
+ priv->has_hostscan && priv->scan_inprogress) {
+ hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
+ break;
+ }
+
+ connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
+ || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
+ || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
+
+ if (connected)
+ netif_carrier_on(dev);
+ else if (!ignore_disconnect)
+ netif_carrier_off(dev);
+
+ if (newstatus != priv->last_linkstatus) {
+ priv->last_linkstatus = newstatus;
+ print_linkstatus(dev, newstatus);
+ /* The info frame contains only one word which is the
+ * status (see hermes.h). The status is pretty boring
+ * in itself, that's why we export the new BSSID...
+ * Jean II */
+ schedule_work(&priv->wevent_work);
+ }
+ }
+ break;
+ case HERMES_INQ_SCAN:
+ if (!priv->scan_inprogress && priv->bssid_fixed &&
+ priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
+ schedule_work(&priv->join_work);
+ break;
+ }
+ /* fall through */
+ case HERMES_INQ_HOSTSCAN:
+ case HERMES_INQ_HOSTSCAN_SYMBOL: {
+ /* Result of a scanning. Contains information about
+ * cells in the vicinity - Jean II */
+ union iwreq_data wrqu;
+ unsigned char *buf;
+
+ /* Scan is no longer in progress */
+ priv->scan_inprogress = 0;
+
+ /* Sanity check */
+ if (len > 4096) {
+ printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ /* Allocate buffer for results */
+ buf = kmalloc(len, GFP_ATOMIC);
+ if (buf == NULL)
+ /* No memory, so can't printk()... */
+ break;
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
+ infofid, sizeof(info));
+ if (err) {
+ kfree(buf);
+ break;
+ }
+
+#ifdef ORINOCO_DEBUG
+ {
+ int i;
+ printk(KERN_DEBUG "Scan result [%02X", buf[0]);
+ for(i = 1; i < (len * 2); i++)
+ printk(":%02X", buf[i]);
+ printk("]\n");
+ }
+#endif /* ORINOCO_DEBUG */
+
+ if (orinoco_process_scan_results(dev, buf, len) == 0) {
+ /* Send an empty event to user space.
+ * We don't send the received data on the event because
+ * it would require us to do complex transcoding, and
+ * we want to minimise the work done in the irq handler
+ * Use a request to extract the data - Jean II */
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ }
+ kfree(buf);
+ }
+ break;
+ case HERMES_INQ_CHANNELINFO:
+ {
+ struct agere_ext_scan_info *bss;
+
+ if (!priv->scan_inprogress) {
+ printk(KERN_DEBUG "%s: Got chaninfo without scan, "
+ "len=%d\n", dev->name, len);
+ break;
+ }
+
+ /* An empty result indicates that the scan is complete */
+ if (len == 0) {
+ union iwreq_data wrqu;
+
+ /* Scan is no longer in progress */
+ priv->scan_inprogress = 0;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ break;
+ }
+
+ /* Sanity check */
+ else if (len > sizeof(*bss)) {
+ printk(KERN_WARNING
+ "%s: Ext scan results too large (%d bytes). "
+ "Truncating results to %zd bytes.\n",
+ dev->name, len, sizeof(*bss));
+ len = sizeof(*bss);
+ } else if (len < (offsetof(struct agere_ext_scan_info,
+ data) + 2)) {
+ /* Drop this result now so we don't have to
+ * keep checking later */
+ printk(KERN_WARNING
+ "%s: Ext scan results too short (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ break;
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
+ infofid, sizeof(info));
+ if (err) {
+ kfree(bss);
+ break;
+ }
+
+ orinoco_add_ext_scan_result(priv, bss);
+
+ kfree(bss);
+ break;
+ }
+ case HERMES_INQ_SEC_STAT_AGERE:
+ /* Security status (Agere specific) */
+ /* Ignore this frame for now */
+ if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
+ break;
+ /* fall through */
+ default:
+ printk(KERN_DEBUG "%s: Unknown information frame received: "
+ "type 0x%04x, length %d\n", dev->name, type, len);
+ /* We don't actually do anything about it */
+ break;
+ }
+}
+
+static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
+{
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);
+}
+
+/********************************************************************/
+/* Internal hardware control routines */
+/********************************************************************/
+
+int __orinoco_up(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ int err;
+
+ netif_carrier_off(dev); /* just to make sure */
+
+ err = __orinoco_program_rids(dev);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d configuring card\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Fire things up again */
+ hermes_set_irqmask(hw, ORINOCO_INTEN);
+ err = hermes_enable_port(hw, 0);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d enabling MAC port\n",
+ dev->name, err);
+ return err;
+ }
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+int __orinoco_down(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ int err;
+
+ netif_stop_queue(dev);
+
+ if (! priv->hw_unavailable) {
+ if (! priv->broken_disableport) {
+ err = hermes_disable_port(hw, 0);
+ if (err) {
+ /* Some firmwares (e.g. Intersil 1.3.x) seem
+ * to have problems disabling the port, oh
+ * well, too bad. */
+ printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
+ dev->name, err);
+ priv->broken_disableport = 1;
+ }
+ }
+ hermes_set_irqmask(hw, 0);
+ hermes_write_regn(hw, EVACK, 0xffff);
+ }
+
+ /* firmware will have to reassociate */
+ netif_carrier_off(dev);
+ priv->last_linkstatus = 0xffff;
+
+ return 0;
+}
+
+static int orinoco_allocate_fid(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ int err;
+
+ err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+ if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
+ /* Try workaround for old Symbol firmware bug */
+ printk(KERN_WARNING "%s: firmware ALLOC bug detected "
+ "(old Symbol firmware?). Trying to work around... ",
+ dev->name);
+
+ priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
+ err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+ if (err)
+ printk("failed!\n");
+ else
+ printk("ok.\n");
+ }
+
+ return err;
+}
+
+int orinoco_reinit_firmware(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ int err;
+
+ err = hermes_init(hw);
+ if (priv->do_fw_download && !err) {
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+ }
+ if (!err)
+ err = orinoco_allocate_fid(dev);
+
+ return err;
+}
+
+static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+
+ if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
+ printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
+ priv->ndev->name, priv->bitratemode);
+ return -EINVAL;
+ }
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFTXRATECONTROL,
+ bitrate_table[priv->bitratemode].agere_txratectrl);
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ case FIRMWARE_TYPE_SYMBOL:
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFTXRATECONTROL,
+ bitrate_table[priv->bitratemode].intersil_txratectrl);
+ break;
+ default:
+ BUG();
+ }
+
+ return err;
+}
+
+/* Set fixed AP address */
+static int __orinoco_hw_set_wap(struct orinoco_private *priv)
+{
+ int roaming_flag;
+ int err = 0;
+ hermes_t *hw = &priv->hw;
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ /* not supported */
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ if (priv->bssid_fixed)
+ roaming_flag = 2;
+ else
+ roaming_flag = 1;
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFROAMINGMODE,
+ roaming_flag);
+ break;
+ case FIRMWARE_TYPE_SYMBOL:
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
+ &priv->desired_bssid);
+ break;
+ }
+ return err;
+}
+
+/* Change the WEP keys and/or the current keys. Can be called
+ * either from __orinoco_hw_setup_enc() or directly from
+ * orinoco_ioctl_setiwencode(). In the later case the association
+ * with the AP is not broken (if the firmware can handle it),
+ * which is needed for 802.1x implementations. */
+static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFWEPKEYS_AGERE,
+ &priv->keys);
+ if (err)
+ return err;
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFTXKEY_AGERE,
+ priv->tx_key);
+ if (err)
+ return err;
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ case FIRMWARE_TYPE_SYMBOL:
+ {
+ int keylen;
+ int i;
+
+ /* Force uniform key length to work around firmware bugs */
+ keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
+
+ if (keylen > LARGE_KEY_SIZE) {
+ printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
+ priv->ndev->name, priv->tx_key, keylen);
+ return -E2BIG;
+ }
+
+ /* Write all 4 keys */
+ for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
+ err = hermes_write_ltv(hw, USER_BAP,
+ HERMES_RID_CNFDEFAULTKEY0 + i,
+ HERMES_BYTES_TO_RECLEN(keylen),
+ priv->keys[i].data);
+ if (err)
+ return err;
+ }
+
+ /* Write the index of the key used in transmission */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFWEPDEFAULTKEYID,
+ priv->tx_key);
+ if (err)
+ return err;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ int master_wep_flag;
+ int auth_flag;
+ int enc_flag;
+
+ /* Setup WEP keys for WEP and WPA */
+ if (priv->encode_alg)
+ __orinoco_hw_setup_wepkeys(priv);
+
+ if (priv->wep_restrict)
+ auth_flag = HERMES_AUTH_SHARED_KEY;
+ else
+ auth_flag = HERMES_AUTH_OPEN;
+
+ if (priv->wpa_enabled)
+ enc_flag = 2;
+ else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
+ enc_flag = 1;
+ else
+ enc_flag = 0;
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
+ /* Enable the shared-key authentication. */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFAUTHENTICATION_AGERE,
+ auth_flag);
+ }
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFWEPENABLED_AGERE,
+ enc_flag);
+ if (err)
+ return err;
+
+ if (priv->has_wpa) {
+ /* Set WPA key management */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
+ priv->key_mgmt);
+ if (err)
+ return err;
+ }
+
+ break;
+
+ case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
+ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
+ if (priv->wep_restrict ||
+ (priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
+ master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
+ HERMES_WEP_EXCL_UNENCRYPTED;
+ else
+ master_wep_flag = HERMES_WEP_PRIVACY_INVOKED;
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFAUTHENTICATION,
+ auth_flag);
+ if (err)
+ return err;
+ } else
+ master_wep_flag = 0;
+
+ if (priv->iw_mode == IW_MODE_MONITOR)
+ master_wep_flag |= HERMES_WEP_HOST_DECRYPT;
+
+ /* Master WEP setting : on/off */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFWEPFLAGS_INTERSIL,
+ master_wep_flag);
+ if (err)
+ return err;
+
+ break;
+ }
+
+ return 0;
+}
+
+/* key must be 32 bytes, including the tx and rx MIC keys.
+ * rsc must be 8 bytes
+ * tsc must be 8 bytes or NULL
+ */
+static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
+ u8 *key, u8 *rsc, u8 *tsc)
+{
+ struct {
+ __le16 idx;
+ u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
+ u8 key[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+ u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
+ } __attribute__ ((packed)) buf;
+ int ret;
+ int err;
+ int k;
+ u16 xmitting;
+
+ key_idx &= 0x3;
+
+ if (set_tx)
+ key_idx |= 0x8000;
+
+ buf.idx = cpu_to_le16(key_idx);
+ memcpy(buf.key, key,
+ sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
+
+ if (rsc == NULL)
+ memset(buf.rsc, 0, sizeof(buf.rsc));
+ else
+ memcpy(buf.rsc, rsc, sizeof(buf.rsc));
+
+ if (tsc == NULL) {
+ memset(buf.tsc, 0, sizeof(buf.tsc));
+ buf.tsc[4] = 0x10;
+ } else {
+ memcpy(buf.tsc, tsc, sizeof(buf.tsc));
+ }
+
+ /* Wait upto 100ms for tx queue to empty */
+ k = 100;
+ do {
+ k--;
+ udelay(1000);
+ ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
+ &xmitting);
+ if (ret)
+ break;
+ } while ((k > 0) && xmitting);
+
+ if (k == 0)
+ ret = -ETIMEDOUT;
+
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
+ &buf);
+
+ return ret ? ret : err;
+}
+
+static int orinoco_clear_tkip_key(struct orinoco_private *priv,
+ int key_idx)
+{
+ hermes_t *hw = &priv->hw;
+ int err;
+
+ memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
+ key_idx);
+ if (err)
+ printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
+ priv->ndev->name, err, key_idx);
+ return err;
+}
+
+static int __orinoco_program_rids(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err;
+ struct hermes_idstring idbuf;
+
+ /* Set the MAC address */
+ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+ HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting MAC address\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set up the link mode */
+ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE,
+ priv->port_type);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting port type\n",
+ dev->name, err);
+ return err;
+ }
+ /* Set the channel/frequency */
+ if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) {
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFOWNCHANNEL,
+ priv->channel);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting channel %d\n",
+ dev->name, err, priv->channel);
+ return err;
+ }
+ }
+
+ if (priv->has_ibss) {
+ u16 createibss;
+
+ if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) {
+ printk(KERN_WARNING "%s: This firmware requires an "
+ "ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
+ /* With wvlan_cs, in this case, we would crash.
+ * hopefully, this driver will behave better...
+ * Jean II */
+ createibss = 0;
+ } else {
+ createibss = priv->createibss;
+ }
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFCREATEIBSS,
+ createibss);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n",
+ dev->name, err);
+ return err;
+ }
+ }
+
+ /* Set the desired BSSID */
+ err = __orinoco_hw_set_wap(priv);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting AP address\n",
+ dev->name, err);
+ return err;
+ }
+ /* Set the desired ESSID */
+ idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
+ memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
+ /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
+ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
+ HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+ &idbuf);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting OWNSSID\n",
+ dev->name, err);
+ return err;
+ }
+ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
+ HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
+ &idbuf);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set the station name */
+ idbuf.len = cpu_to_le16(strlen(priv->nick));
+ memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
+ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+ HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
+ &idbuf);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting nickname\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set AP density */
+ if (priv->has_sensitivity) {
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSYSTEMSCALE,
+ priv->ap_density);
+ if (err) {
+ printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
+ "Disabling sensitivity control\n",
+ dev->name, err);
+
+ priv->has_sensitivity = 0;
+ }
+ }
+
+ /* Set RTS threshold */
+ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
+ priv->rts_thresh);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting RTS threshold\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set fragmentation threshold or MWO robustness */
+ if (priv->has_mwo)
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMWOROBUST_AGERE,
+ priv->mwo_robust);
+ else
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+ priv->frag_thresh);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting fragmentation\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set bitrate */
+ err = __orinoco_hw_set_bitrate(priv);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting bitrate\n",
+ dev->name, err);
+ return err;
+ }
+
+ /* Set power management */
+ if (priv->has_pm) {
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPMENABLED,
+ priv->pm_on);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting up PM\n",
+ dev->name, err);
+ return err;
+ }
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMULTICASTRECEIVE,
+ priv->pm_mcast);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting up PM\n",
+ dev->name, err);
+ return err;
+ }
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMAXSLEEPDURATION,
+ priv->pm_period);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting up PM\n",
+ dev->name, err);
+ return err;
+ }
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPMHOLDOVERDURATION,
+ priv->pm_timeout);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting up PM\n",
+ dev->name, err);
+ return err;
+ }
+ }
+
+ /* Set preamble - only for Symbol so far... */
+ if (priv->has_preamble) {
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPREAMBLE_SYMBOL,
+ priv->preamble);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting preamble\n",
+ dev->name, err);
+ return err;
+ }
+ }
+
+ /* Set up encryption */
+ if (priv->has_wep || priv->has_wpa) {
+ err = __orinoco_hw_setup_enc(priv);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d activating encryption\n",
+ dev->name, err);
+ return err;
+ }
+ }
+
+ if (priv->iw_mode == IW_MODE_MONITOR) {
+ /* Enable monitor mode */
+ dev->type = ARPHRD_IEEE80211;
+ err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ HERMES_TEST_MONITOR, 0, NULL);
+ } else {
+ /* Disable monitor mode */
+ dev->type = ARPHRD_ETHER;
+ err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ HERMES_TEST_STOP, 0, NULL);
+ }
+ if (err)
+ return err;
+
+ /* Set promiscuity / multicast*/
+ priv->promiscuous = 0;
+ priv->mc_count = 0;
+
+ /* FIXME: what about netif_tx_lock */
+ __orinoco_set_multicast_list(dev);
+
+ return 0;
+}
+
+/* FIXME: return int? */
+static void
+__orinoco_set_multicast_list(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ int promisc, mc_count;
+
+ /* The Hermes doesn't seem to have an allmulti mode, so we go
+ * into promiscuous mode and let the upper levels deal. */
+ if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+ (dev->mc_count > MAX_MULTICAST(priv)) ) {
+ promisc = 1;
+ mc_count = 0;
+ } else {
+ promisc = 0;
+ mc_count = dev->mc_count;
+ }
+
+ if (promisc != priv->promiscuous) {
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPROMISCUOUSMODE,
+ promisc);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
+ dev->name, err);
+ } else
+ priv->promiscuous = promisc;
+ }
+
+ /* If we're not in promiscuous mode, then we need to set the
+ * group address if either we want to multicast, or if we were
+ * multicasting and want to stop */
+ if (! promisc && (mc_count || priv->mc_count) ) {
+ struct dev_mc_list *p = dev->mc_list;
+ struct hermes_multicast mclist;
+ int i;
+
+ for (i = 0; i < mc_count; i++) {
+ /* paranoia: is list shorter than mc_count? */
+ BUG_ON(! p);
+ /* paranoia: bad address size in list? */
+ BUG_ON(p->dmi_addrlen != ETH_ALEN);
+
+ memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
+ p = p->next;
+ }
+
+ if (p)
+ printk(KERN_WARNING "%s: Multicast list is "
+ "longer than mc_count\n", dev->name);
+
+ err = hermes_write_ltv(hw, USER_BAP,
+ HERMES_RID_CNFGROUPADDRESSES,
+ HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
+ &mclist);
+ if (err)
+ printk(KERN_ERR "%s: Error %d setting multicast list.\n",
+ dev->name, err);
+ else
+ priv->mc_count = mc_count;
+ }
+}
+
+/* This must be called from user context, without locks held - use
+ * schedule_work() */
+static void orinoco_reset(struct work_struct *work)
+{
+ struct orinoco_private *priv =
+ container_of(work, struct orinoco_private, reset_work);
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ int err;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ /* When the hardware becomes available again, whatever
+ * detects that is responsible for re-initializing
+ * it. So no need for anything further */
+ return;
+
+ netif_stop_queue(dev);
+
+ /* Shut off interrupts. Depending on what state the hardware
+ * is in, this might not work, but we'll try anyway */
+ hermes_set_irqmask(hw, 0);
+ hermes_write_regn(hw, EVACK, 0xffff);
+
+ priv->hw_unavailable++;
+ priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
+ netif_carrier_off(dev);
+
+ orinoco_unlock(priv, &flags);
+
+ /* Scanning support: Cleanup of driver struct */
+ orinoco_clear_scan_results(priv, 0);
+ priv->scan_inprogress = 0;
+
+ if (priv->hard_reset) {
+ err = (*priv->hard_reset)(priv);
+ if (err) {
+ printk(KERN_ERR "%s: orinoco_reset: Error %d "
+ "performing hard reset\n", dev->name, err);
+ goto disable;
+ }
+ }
+
+ err = orinoco_reinit_firmware(dev);
+ if (err) {
+ printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
+ dev->name, err);
+ goto disable;
+ }
+
+ spin_lock_irq(&priv->lock); /* This has to be called from user context */
+
+ priv->hw_unavailable--;
+
+ /* priv->open or priv->hw_unavailable might have changed while
+ * we dropped the lock */
+ if (priv->open && (! priv->hw_unavailable)) {
+ err = __orinoco_up(dev);
+ if (err) {
+ printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
+ dev->name, err);
+ } else
+ dev->trans_start = jiffies;
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ return;
+ disable:
+ hermes_set_irqmask(hw, 0);
+ netif_device_detach(dev);
+ printk(KERN_ERR "%s: Device has been disabled!\n", dev->name);
+}
+
+/********************************************************************/
+/* Interrupt handler */
+/********************************************************************/
+
+static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
+{
+ printk(KERN_DEBUG "%s: TICK\n", dev->name);
+}
+
+static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
+{
+ /* This seems to happen a fair bit under load, but ignoring it
+ seems to work fine...*/
+ printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
+ dev->name);
+}
+
+irqreturn_t orinoco_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int count = MAX_IRQLOOPS_PER_IRQ;
+ u16 evstat, events;
+ /* These are used to detect a runaway interrupt situation */
+ /* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
+ * we panic and shut down the hardware */
+ static int last_irq_jiffy = 0; /* jiffies value the last time
+ * we were called */
+ static int loops_this_jiffy = 0;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0) {
+ /* If hw is unavailable - we don't know if the irq was
+ * for us or not */
+ return IRQ_HANDLED;
+ }
+
+ evstat = hermes_read_regn(hw, EVSTAT);
+ events = evstat & hw->inten;
+ if (! events) {
+ orinoco_unlock(priv, &flags);
+ return IRQ_NONE;
+ }
+
+ if (jiffies != last_irq_jiffy)
+ loops_this_jiffy = 0;
+ last_irq_jiffy = jiffies;
+
+ while (events && count--) {
+ if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
+ printk(KERN_WARNING "%s: IRQ handler is looping too "
+ "much! Resetting.\n", dev->name);
+ /* Disable interrupts for now */
+ hermes_set_irqmask(hw, 0);
+ schedule_work(&priv->reset_work);
+ break;
+ }
+
+ /* Check the card hasn't been removed */
+ if (! hermes_present(hw)) {
+ DEBUG(0, "orinoco_interrupt(): card removed\n");
+ break;
+ }
+
+ if (events & HERMES_EV_TICK)
+ __orinoco_ev_tick(dev, hw);
+ if (events & HERMES_EV_WTERR)
+ __orinoco_ev_wterr(dev, hw);
+ if (events & HERMES_EV_INFDROP)
+ __orinoco_ev_infdrop(dev, hw);
+ if (events & HERMES_EV_INFO)
+ __orinoco_ev_info(dev, hw);
+ if (events & HERMES_EV_RX)
+ __orinoco_ev_rx(dev, hw);
+ if (events & HERMES_EV_TXEXC)
+ __orinoco_ev_txexc(dev, hw);
+ if (events & HERMES_EV_TX)
+ __orinoco_ev_tx(dev, hw);
+ if (events & HERMES_EV_ALLOC)
+ __orinoco_ev_alloc(dev, hw);
+
+ hermes_write_regn(hw, EVACK, evstat);
+
+ evstat = hermes_read_regn(hw, EVSTAT);
+ events = evstat & hw->inten;
+ };
+
+ orinoco_unlock(priv, &flags);
+ return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* Power management */
+/********************************************************************/
+#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT)
+static int orinoco_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct orinoco_private *priv = container_of(notifier,
+ struct orinoco_private,
+ pm_notifier);
+
+ /* All we need to do is cache the firmware before suspend, and
+ * release it when we come out.
+ *
+ * Only need to do this if we're downloading firmware. */
+ if (!priv->do_fw_download)
+ return NOTIFY_DONE;
+
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ orinoco_cache_fw(priv, 0);
+ break;
+
+ case PM_POST_RESTORE:
+ /* Restore from hibernation failed. We need to clean
+ * up in exactly the same way, so fall through. */
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ orinoco_uncache_fw(priv);
+ break;
+
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */
+#define orinoco_pm_notifier NULL
+#endif
+
+/********************************************************************/
+/* Initialization */
+/********************************************************************/
+
+struct comp_id {
+ u16 id, variant, major, minor;
+} __attribute__ ((packed));
+
+static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
+{
+ if (nic_id->id < 0x8000)
+ return FIRMWARE_TYPE_AGERE;
+ else if (nic_id->id == 0x8000 && nic_id->major == 0)
+ return FIRMWARE_TYPE_SYMBOL;
+ else
+ return FIRMWARE_TYPE_INTERSIL;
+}
+
+/* Set priv->firmware type, determine firmware properties */
+static int determine_firmware(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err;
+ struct comp_id nic_id, sta_id;
+ unsigned int firmver;
+ char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2)));
+
+ /* Get the hardware version */
+ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id);
+ if (err) {
+ printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n",
+ dev->name, err);
+ return err;
+ }
+
+ le16_to_cpus(&nic_id.id);
+ le16_to_cpus(&nic_id.variant);
+ le16_to_cpus(&nic_id.major);
+ le16_to_cpus(&nic_id.minor);
+ printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n",
+ dev->name, nic_id.id, nic_id.variant,
+ nic_id.major, nic_id.minor);
+
+ priv->firmware_type = determine_firmware_type(&nic_id);
+
+ /* Get the firmware version */
+ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
+ if (err) {
+ printk(KERN_ERR "%s: Cannot read station identity: error %d\n",
+ dev->name, err);
+ return err;
+ }
+
+ le16_to_cpus(&sta_id.id);
+ le16_to_cpus(&sta_id.variant);
+ le16_to_cpus(&sta_id.major);
+ le16_to_cpus(&sta_id.minor);
+ printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n",
+ dev->name, sta_id.id, sta_id.variant,
+ sta_id.major, sta_id.minor);
+
+ switch (sta_id.id) {
+ case 0x15:
+ printk(KERN_ERR "%s: Primary firmware is active\n",
+ dev->name);
+ return -ENODEV;
+ case 0x14b:
+ printk(KERN_ERR "%s: Tertiary firmware is active\n",
+ dev->name);
+ return -ENODEV;
+ case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */
+ case 0x21: /* Symbol Spectrum24 Trilogy */
+ break;
+ default:
+ printk(KERN_NOTICE "%s: Unknown station ID, please report\n",
+ dev->name);
+ break;
+ }
+
+ /* Default capabilities */
+ priv->has_sensitivity = 1;
+ priv->has_mwo = 0;
+ priv->has_preamble = 0;
+ priv->has_port3 = 1;
+ priv->has_ibss = 1;
+ priv->has_wep = 0;
+ priv->has_big_wep = 0;
+ priv->has_alt_txcntl = 0;
+ priv->has_ext_scan = 0;
+ priv->has_wpa = 0;
+ priv->do_fw_download = 0;
+
+ /* Determine capabilities from the firmware version */
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
+ ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
+ snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
+ "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
+
+ firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
+
+ priv->has_ibss = (firmver >= 0x60006);
+ priv->has_wep = (firmver >= 0x40020);
+ priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell
+ Gold cards from the others? */
+ priv->has_mwo = (firmver >= 0x60000);
+ priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
+ priv->ibss_port = 1;
+ priv->has_hostscan = (firmver >= 0x8000a);
+ priv->do_fw_download = 1;
+ priv->broken_monitor = (firmver >= 0x80000);
+ priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
+ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
+ priv->has_wpa = (firmver >= 0x9002a);
+ /* Tested with Agere firmware :
+ * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
+ * Tested CableTron firmware : 4.32 => Anton */
+ break;
+ case FIRMWARE_TYPE_SYMBOL:
+ /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */
+ /* Intel MAC : 00:02:B3:* */
+ /* 3Com MAC : 00:50:DA:* */
+ memset(tmp, 0, sizeof(tmp));
+ /* Get the Symbol firmware version */
+ err = hermes_read_ltv(hw, USER_BAP,
+ HERMES_RID_SECONDARYVERSION_SYMBOL,
+ SYMBOL_MAX_VER_LEN, NULL, &tmp);
+ if (err) {
+ printk(KERN_WARNING
+ "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n",
+ dev->name, err);
+ firmver = 0;
+ tmp[0] = '\0';
+ } else {
+ /* The firmware revision is a string, the format is
+ * something like : "V2.20-01".
+ * Quick and dirty parsing... - Jean II
+ */
+ firmver = ((tmp[1] - '0') << 16) | ((tmp[3] - '0') << 12)
+ | ((tmp[4] - '0') << 8) | ((tmp[6] - '0') << 4)
+ | (tmp[7] - '0');
+
+ tmp[SYMBOL_MAX_VER_LEN] = '\0';
+ }
+
+ snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
+ "Symbol %s", tmp);
+
+ priv->has_ibss = (firmver >= 0x20000);
+ priv->has_wep = (firmver >= 0x15012);
+ priv->has_big_wep = (firmver >= 0x20000);
+ priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) ||
+ (firmver >= 0x29000 && firmver < 0x30000) ||
+ firmver >= 0x31000;
+ priv->has_preamble = (firmver >= 0x20000);
+ priv->ibss_port = 4;
+
+ /* Symbol firmware is found on various cards, but
+ * there has been no attempt to check firmware
+ * download on non-spectrum_cs based cards.
+ *
+ * Given that the Agere firmware download works
+ * differently, we should avoid doing a firmware
+ * download with the Symbol algorithm on non-spectrum
+ * cards.
+ *
+ * For now we can identify a spectrum_cs based card
+ * because it has a firmware reset function.
+ */
+ priv->do_fw_download = (priv->stop_fw != NULL);
+
+ priv->broken_disableport = (firmver == 0x25013) ||
+ (firmver >= 0x30000 && firmver <= 0x31000);
+ priv->has_hostscan = (firmver >= 0x31001) ||
+ (firmver >= 0x29057 && firmver < 0x30000);
+ /* Tested with Intel firmware : 0x20015 => Jean II */
+ /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ /* D-Link, Linksys, Adtron, ZoomAir, and many others...
+ * Samsung, Compaq 100/200 and Proxim are slightly
+ * different and less well tested */
+ /* D-Link MAC : 00:40:05:* */
+ /* Addtron MAC : 00:90:D1:* */
+ snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
+ "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
+ sta_id.variant);
+
+ firmver = ((unsigned long)sta_id.major << 16) |
+ ((unsigned long)sta_id.minor << 8) | sta_id.variant;
+
+ priv->has_ibss = (firmver >= 0x000700); /* FIXME */
+ priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
+ priv->has_pm = (firmver >= 0x000700);
+ priv->has_hostscan = (firmver >= 0x010301);
+
+ if (firmver >= 0x000800)
+ priv->ibss_port = 0;
+ else {
+ printk(KERN_NOTICE "%s: Intersil firmware earlier "
+ "than v0.8.x - several features not supported\n",
+ dev->name);
+ priv->ibss_port = 1;
+ }
+ break;
+ }
+ printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name,
+ priv->fw_name);
+
+ return 0;
+}
+
+static int orinoco_init(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ struct hermes_idstring nickbuf;
+ u16 reclen;
+ int len;
+
+ /* No need to lock, the hw_unavailable flag is already set in
+ * alloc_orinocodev() */
+ priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
+
+ /* Initialize the firmware */
+ err = hermes_init(hw);
+ if (err != 0) {
+ printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n",
+ dev->name, err);
+ goto out;
+ }
+
+ err = determine_firmware(dev);
+ if (err != 0) {
+ printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
+ dev->name);
+ goto out;
+ }
+
+ if (priv->do_fw_download) {
+#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT
+ orinoco_cache_fw(priv, 0);
+#endif
+
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+
+ /* Check firmware version again */
+ err = determine_firmware(dev);
+ if (err != 0) {
+ printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
+ dev->name);
+ goto out;
+ }
+ }
+
+ if (priv->has_port3)
+ printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
+ if (priv->has_ibss)
+ printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
+ dev->name);
+ if (priv->has_wep) {
+ printk(KERN_DEBUG "%s: WEP supported, ", dev->name);
+ if (priv->has_big_wep)
+ printk("104-bit key\n");
+ else
+ printk("40-bit key\n");
+ }
+ if (priv->has_wpa) {
+ printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
+ if (orinoco_mic_init(priv)) {
+ printk(KERN_ERR "%s: Failed to setup MIC crypto "
+ "algorithm. Disabling WPA support\n", dev->name);
+ priv->has_wpa = 0;
+ }
+ }
+
+ /* Now we have the firmware capabilities, allocate appropiate
+ * sized scan buffers */
+ if (orinoco_bss_data_allocate(priv))
+ goto out;
+ orinoco_bss_data_init(priv);
+
+ /* Get the MAC address */
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+ ETH_ALEN, NULL, dev->dev_addr);
+ if (err) {
+ printk(KERN_WARNING "%s: failed to read MAC address!\n",
+ dev->name);
+ goto out;
+ }
+
+ printk(KERN_DEBUG "%s: MAC address %pM\n",
+ dev->name, dev->dev_addr);
+
+ /* Get the station name */
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+ sizeof(nickbuf), &reclen, &nickbuf);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read station name\n",
+ dev->name);
+ goto out;
+ }
+ if (nickbuf.len)
+ len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
+ else
+ len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
+ memcpy(priv->nick, &nickbuf.val, len);
+ priv->nick[len] = '\0';
+
+ printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
+
+ err = orinoco_allocate_fid(dev);
+ if (err) {
+ printk(KERN_ERR "%s: failed to allocate NIC buffer!\n",
+ dev->name);
+ goto out;
+ }
+
+ /* Get allowed channels */
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
+ &priv->channel_mask);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read channel list!\n",
+ dev->name);
+ goto out;
+ }
+
+ /* Get initial AP density */
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
+ &priv->ap_density);
+ if (err || priv->ap_density < 1 || priv->ap_density > 3) {
+ priv->has_sensitivity = 0;
+ }
+
+ /* Get initial RTS threshold */
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
+ &priv->rts_thresh);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read RTS threshold!\n",
+ dev->name);
+ goto out;
+ }
+
+ /* Get initial fragmentation settings */
+ if (priv->has_mwo)
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMWOROBUST_AGERE,
+ &priv->mwo_robust);
+ else
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+ &priv->frag_thresh);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read fragmentation settings!\n",
+ dev->name);
+ goto out;
+ }
+
+ /* Power management setup */
+ if (priv->has_pm) {
+ priv->pm_on = 0;
+ priv->pm_mcast = 1;
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMAXSLEEPDURATION,
+ &priv->pm_period);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read power management period!\n",
+ dev->name);
+ goto out;
+ }
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPMHOLDOVERDURATION,
+ &priv->pm_timeout);
+ if (err) {
+ printk(KERN_ERR "%s: failed to read power management timeout!\n",
+ dev->name);
+ goto out;
+ }
+ }
+
+ /* Preamble setup */
+ if (priv->has_preamble) {
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFPREAMBLE_SYMBOL,
+ &priv->preamble);
+ if (err)
+ goto out;
+ }
+
+ /* Set up the default configuration */
+ priv->iw_mode = IW_MODE_INFRA;
+ /* By default use IEEE/IBSS ad-hoc mode if we have it */
+ priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss);
+ set_port_type(priv);
+ priv->channel = 0; /* use firmware default */
+
+ priv->promiscuous = 0;
+ priv->encode_alg = IW_ENCODE_ALG_NONE;
+ priv->tx_key = 0;
+ priv->wpa_enabled = 0;
+ priv->tkip_cm_active = 0;
+ priv->key_mgmt = 0;
+ priv->wpa_ie_len = 0;
+ priv->wpa_ie = NULL;
+
+ /* Make the hardware available, as long as it hasn't been
+ * removed elsewhere (e.g. by PCMCIA hot unplug) */
+ spin_lock_irq(&priv->lock);
+ priv->hw_unavailable--;
+ spin_unlock_irq(&priv->lock);
+
+ printk(KERN_DEBUG "%s: ready\n", dev->name);
+
+ out:
+ return err;
+}
+
+struct net_device
+*alloc_orinocodev(int sizeof_card,
+ struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int))
+{
+ struct net_device *dev;
+ struct orinoco_private *priv;
+
+ dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
+ if (! dev)
+ return NULL;
+ priv = netdev_priv(dev);
+ priv->ndev = dev;
+ if (sizeof_card)
+ priv->card = (void *)((unsigned long)priv
+ + sizeof(struct orinoco_private));
+ else
+ priv->card = NULL;
+ priv->dev = device;
+
+ /* Setup / override net_device fields */
+ dev->init = orinoco_init;
+ dev->hard_start_xmit = orinoco_xmit;
+ dev->tx_timeout = orinoco_tx_timeout;
+ dev->watchdog_timeo = HZ; /* 1 second timeout */
+ dev->get_stats = orinoco_get_stats;
+ dev->ethtool_ops = &orinoco_ethtool_ops;
+ dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def;
+#ifdef WIRELESS_SPY
+ priv->wireless_data.spy_data = &priv->spy_data;
+ dev->wireless_data = &priv->wireless_data;
+#endif
+ dev->change_mtu = orinoco_change_mtu;
+ dev->set_multicast_list = orinoco_set_multicast_list;
+ /* we use the default eth_mac_addr for setting the MAC addr */
+
+ /* Reserve space in skb for the SNAP header */
+ dev->hard_header_len += ENCAPS_OVERHEAD;
+
+ /* Set up default callbacks */
+ dev->open = orinoco_open;
+ dev->stop = orinoco_stop;
+ priv->hard_reset = hard_reset;
+ priv->stop_fw = stop_fw;
+
+ spin_lock_init(&priv->lock);
+ priv->open = 0;
+ priv->hw_unavailable = 1; /* orinoco_init() must clear this
+ * before anything else touches the
+ * hardware */
+ INIT_WORK(&priv->reset_work, orinoco_reset);
+ INIT_WORK(&priv->join_work, orinoco_join_ap);
+ INIT_WORK(&priv->wevent_work, orinoco_send_wevents);
+
+ INIT_LIST_HEAD(&priv->rx_list);
+ tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
+ (unsigned long) dev);
+
+ netif_carrier_off(dev);
+ priv->last_linkstatus = 0xffff;
+
+ priv->cached_pri_fw = NULL;
+ priv->cached_fw = NULL;
+
+ /* Register PM notifiers */
+ priv->pm_notifier.notifier_call = orinoco_pm_notifier;
+ register_pm_notifier(&priv->pm_notifier);
+
+ return dev;
+}
+
+void free_orinocodev(struct net_device *dev)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ /* No need to empty priv->rx_list: if the tasklet is scheduled
+ * when we call tasklet_kill it will run one final time,
+ * emptying the list */
+ tasklet_kill(&priv->rx_tasklet);
+
+ unregister_pm_notifier(&priv->pm_notifier);
+ orinoco_uncache_fw(priv);
+
+ priv->wpa_ie_len = 0;
+ kfree(priv->wpa_ie);
+ orinoco_mic_free(priv);
+ orinoco_bss_data_free(priv);
+ free_netdev(dev);
+}
+
+/********************************************************************/
+/* Wireless extensions */
+/********************************************************************/
+
+/* Return : < 0 -> error code ; >= 0 -> length */
+static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
+ char buf[IW_ESSID_MAX_SIZE+1])
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ struct hermes_idstring essidbuf;
+ char *p = (char *)(&essidbuf.val);
+ int len;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (strlen(priv->desired_essid) > 0) {
+ /* We read the desired SSID from the hardware rather
+ than from priv->desired_essid, just in case the
+ firmware is allowed to change it on us. I'm not
+ sure about this */
+ /* My guess is that the OWNSSID should always be whatever
+ * we set to the card, whereas CURRENT_SSID is the one that
+ * may change... - Jean II */
+ u16 rid;
+
+ *active = 1;
+
+ rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
+ HERMES_RID_CNFDESIREDSSID;
+
+ err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
+ NULL, &essidbuf);
+ if (err)
+ goto fail_unlock;
+ } else {
+ *active = 0;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
+ sizeof(essidbuf), NULL, &essidbuf);
+ if (err)
+ goto fail_unlock;
+ }
+
+ len = le16_to_cpu(essidbuf.len);
+ BUG_ON(len > IW_ESSID_MAX_SIZE);
+
+ memset(buf, 0, IW_ESSID_MAX_SIZE);
+ memcpy(buf, p, len);
+ err = len;
+
+ fail_unlock:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static long orinoco_hw_get_freq(struct orinoco_private *priv)
+{
+
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u16 channel;
+ long freq = 0;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
+ if (err)
+ goto out;
+
+ /* Intersil firmware 1.3.5 returns 0 when the interface is down */
+ if (channel == 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
+ printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
+ priv->ndev->name, channel);
+ err = -EBUSY;
+ goto out;
+
+ }
+ freq = channel_frequency[channel-1] * 100000;
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ if (err > 0)
+ err = -EBUSY;
+ return err ? err : freq;
+}
+
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
+ int *numrates, s32 *rates, int max)
+{
+ hermes_t *hw = &priv->hw;
+ struct hermes_idstring list;
+ unsigned char *p = (unsigned char *)&list.val;
+ int err = 0;
+ int num;
+ int i;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
+ sizeof(list), NULL, &list);
+ orinoco_unlock(priv, &flags);
+
+ if (err)
+ return err;
+
+ num = le16_to_cpu(list.len);
+ *numrates = num;
+ num = min(num, max);
+
+ for (i = 0; i < num; i++) {
+ rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
+ }
+
+ return 0;
+}
+
+static int orinoco_ioctl_getname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int numrates;
+ int err;
+
+ err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
+
+ if (!err && (numrates > 2))
+ strcpy(name, "IEEE 802.11b");
+ else
+ strcpy(name, "IEEE 802.11-DS");
+
+ return 0;
+}
+
+static int orinoco_ioctl_setwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = -EINPROGRESS; /* Call commit handler */
+ unsigned long flags;
+ static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Enable automatic roaming - no sanity checks are needed */
+ if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
+ memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
+ priv->bssid_fixed = 0;
+ memset(priv->desired_bssid, 0, ETH_ALEN);
+
+ /* "off" means keep existing connection */
+ if (ap_addr->sa_data[0] == 0) {
+ __orinoco_hw_set_wap(priv);
+ err = 0;
+ }
+ goto out;
+ }
+
+ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
+ printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
+ "support manual roaming\n",
+ dev->name);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (priv->iw_mode != IW_MODE_INFRA) {
+ printk(KERN_WARNING "%s: Manual roaming supported only in "
+ "managed mode\n", dev->name);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Intersil firmware hangs without Desired ESSID */
+ if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
+ strlen(priv->desired_essid) == 0) {
+ printk(KERN_WARNING "%s: Desired ESSID must be set for "
+ "manual roaming\n", dev->name);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Finally, enable manual roaming */
+ priv->bssid_fixed = 1;
+ memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
+
+ out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static int orinoco_ioctl_getwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ ap_addr->sa_family = ARPHRD_ETHER;
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+ ETH_ALEN, NULL, ap_addr->sa_data);
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_setmode(struct net_device *dev,
+ struct iw_request_info *info,
+ u32 *mode,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = -EINPROGRESS; /* Call commit handler */
+ unsigned long flags;
+
+ if (priv->iw_mode == *mode)
+ return 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (*mode) {
+ case IW_MODE_ADHOC:
+ if (!priv->has_ibss && !priv->has_port3)
+ err = -EOPNOTSUPP;
+ break;
+
+ case IW_MODE_INFRA:
+ break;
+
+ case IW_MODE_MONITOR:
+ if (priv->broken_monitor && !force_monitor) {
+ printk(KERN_WARNING "%s: Monitor mode support is "
+ "buggy in this firmware, not enabling\n",
+ dev->name);
+ err = -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ if (err == -EINPROGRESS) {
+ priv->iw_mode = *mode;
+ set_port_type(priv);
+ }
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getmode(struct net_device *dev,
+ struct iw_request_info *info,
+ u32 *mode,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ *mode = priv->iw_mode;
+ return 0;
+}
+
+static int orinoco_ioctl_getiwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = 0;
+ struct iw_range *range = (struct iw_range *) extra;
+ int numrates;
+ int i, k;
+
+ rrq->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 22;
+
+ /* Set available channels/frequencies */
+ range->num_channels = NUM_CHANNELS;
+ k = 0;
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (priv->channel_mask & (1 << i)) {
+ range->freq[k].i = i + 1;
+ range->freq[k].m = channel_frequency[i] * 100000;
+ range->freq[k].e = 1;
+ k++;
+ }
+
+ if (k >= IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = k;
+ range->sensitivity = 3;
+
+ if (priv->has_wep) {
+ range->max_encoding_tokens = ORINOCO_MAX_KEYS;
+ range->encoding_size[0] = SMALL_KEY_SIZE;
+ range->num_encoding_sizes = 1;
+
+ if (priv->has_big_wep) {
+ range->encoding_size[1] = LARGE_KEY_SIZE;
+ range->num_encoding_sizes = 2;
+ }
+ }
+
+ if (priv->has_wpa)
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
+
+ if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){
+ /* Quality stats meaningless in ad-hoc mode */
+ } else {
+ range->max_qual.qual = 0x8b - 0x2f;
+ range->max_qual.level = 0x2f - 0x95 - 1;
+ range->max_qual.noise = 0x2f - 0x95 - 1;
+ /* Need to get better values */
+ range->avg_qual.qual = 0x24;
+ range->avg_qual.level = 0xC2;
+ range->avg_qual.noise = 0x9E;
+ }
+
+ err = orinoco_hw_get_bitratelist(priv, &numrates,
+ range->bitrate, IW_MAX_BITRATES);
+ if (err)
+ return err;
+ range->num_bitrates = numrates;
+
+ /* Set an indication of the max TCP throughput in bit/s that we can
+ * expect using this interface. May be use for QoS stuff...
+ * Jean II */
+ if (numrates > 2)
+ range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
+ else
+ range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->min_pmp = 0;
+ range->max_pmp = 65535000;
+ range->min_pmt = 0;
+ range->max_pmt = 65535 * 1000; /* ??? */
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
+
+ range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = IW_RETRY_LIFETIME;
+ range->min_retry = 0;
+ range->max_retry = 65535; /* ??? */
+ range->min_r_time = 0;
+ range->max_r_time = 65535 * 1000; /* ??? */
+
+ if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
+ range->scan_capa = IW_SCAN_CAPA_ESSID;
+ else
+ range->scan_capa = IW_SCAN_CAPA_NONE;
+
+ /* Event capability (kernel) */
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+ /* Event capability (driver) */
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+ IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
+
+ return 0;
+}
+
+static int orinoco_ioctl_setiwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq,
+ char *keybuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int index = (erq->flags & IW_ENCODE_INDEX) - 1;
+ int setindex = priv->tx_key;
+ int encode_alg = priv->encode_alg;
+ int restricted = priv->wep_restrict;
+ u16 xlen = 0;
+ int err = -EINPROGRESS; /* Call commit handler */
+ unsigned long flags;
+
+ if (! priv->has_wep)
+ return -EOPNOTSUPP;
+
+ if (erq->pointer) {
+ /* We actually have a key to set - check its length */
+ if (erq->length > LARGE_KEY_SIZE)
+ return -E2BIG;
+
+ if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep )
+ return -E2BIG;
+ }
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Clear any TKIP key we have */
+ if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
+ (void) orinoco_clear_tkip_key(priv, setindex);
+
+ if (erq->length > 0) {
+ if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
+ index = priv->tx_key;
+
+ /* Adjust key length to a supported value */
+ if (erq->length > SMALL_KEY_SIZE) {
+ xlen = LARGE_KEY_SIZE;
+ } else if (erq->length > 0) {
+ xlen = SMALL_KEY_SIZE;
+ } else
+ xlen = 0;
+
+ /* Switch on WEP if off */
+ if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
+ setindex = index;
+ encode_alg = IW_ENCODE_ALG_WEP;
+ }
+ } else {
+ /* Important note : if the user do "iwconfig eth0 enc off",
+ * we will arrive there with an index of -1. This is valid
+ * but need to be taken care off... Jean II */
+ if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
+ if((index != -1) || (erq->flags == 0)) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ /* Set the index : Check that the key is valid */
+ if(priv->keys[index].len == 0) {
+ err = -EINVAL;
+ goto out;
+ }
+ setindex = index;
+ }
+ }
+
+ if (erq->flags & IW_ENCODE_DISABLED)
+ encode_alg = IW_ENCODE_ALG_NONE;
+ if (erq->flags & IW_ENCODE_OPEN)
+ restricted = 0;
+ if (erq->flags & IW_ENCODE_RESTRICTED)
+ restricted = 1;
+
+ if (erq->pointer && erq->length > 0) {
+ priv->keys[index].len = cpu_to_le16(xlen);
+ memset(priv->keys[index].data, 0,
+ sizeof(priv->keys[index].data));
+ memcpy(priv->keys[index].data, keybuf, erq->length);
+ }
+ priv->tx_key = setindex;
+
+ /* Try fast key change if connected and only keys are changed */
+ if ((priv->encode_alg == encode_alg) &&
+ (priv->wep_restrict == restricted) &&
+ netif_carrier_ok(dev)) {
+ err = __orinoco_hw_setup_wepkeys(priv);
+ /* No need to commit if successful */
+ goto out;
+ }
+
+ priv->encode_alg = encode_alg;
+ priv->wep_restrict = restricted;
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getiwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq,
+ char *keybuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int index = (erq->flags & IW_ENCODE_INDEX) - 1;
+ u16 xlen = 0;
+ unsigned long flags;
+
+ if (! priv->has_wep)
+ return -EOPNOTSUPP;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
+ index = priv->tx_key;
+
+ erq->flags = 0;
+ if (!priv->encode_alg)
+ erq->flags |= IW_ENCODE_DISABLED;
+ erq->flags |= index + 1;
+
+ if (priv->wep_restrict)
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ else
+ erq->flags |= IW_ENCODE_OPEN;
+
+ xlen = le16_to_cpu(priv->keys[index].len);
+
+ erq->length = xlen;
+
+ memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
+
+ orinoco_unlock(priv, &flags);
+ return 0;
+}
+
+static int orinoco_ioctl_setessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq,
+ char *essidbuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
+ * anyway... - Jean II */
+
+ /* Hum... Should not use Wireless Extension constant (may change),
+ * should use our own... - Jean II */
+ if (erq->length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
+ memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
+
+ /* If not ANY, get the new ESSID */
+ if (erq->flags) {
+ memcpy(priv->desired_essid, essidbuf, erq->length);
+ }
+
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_getessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq,
+ char *essidbuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int active;
+ int err = 0;
+ unsigned long flags;
+
+ if (netif_running(dev)) {
+ err = orinoco_hw_get_essid(priv, &active, essidbuf);
+ if (err < 0)
+ return err;
+ erq->length = err;
+ } else {
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+ memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE);
+ erq->length = strlen(priv->desired_essid);
+ orinoco_unlock(priv, &flags);
+ }
+
+ erq->flags = 1;
+
+ return 0;
+}
+
+static int orinoco_ioctl_setnick(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *nrq,
+ char *nickbuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ if (nrq->length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, nickbuf, nrq->length);
+
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_getnick(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *nrq,
+ char *nickbuf)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE);
+ orinoco_unlock(priv, &flags);
+
+ nrq->length = strlen(priv->nick);
+
+ return 0;
+}
+
+static int orinoco_ioctl_setfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *frq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int chan = -1;
+ unsigned long flags;
+ int err = -EINPROGRESS; /* Call commit handler */
+
+ /* In infrastructure mode the AP sets the channel */
+ if (priv->iw_mode == IW_MODE_INFRA)
+ return -EBUSY;
+
+ if ( (frq->e == 0) && (frq->m <= 1000) ) {
+ /* Setting by channel number */
+ chan = frq->m;
+ } else {
+ /* Setting by frequency - search the table */
+ int mult = 1;
+ int i;
+
+ for (i = 0; i < (6 - frq->e); i++)
+ mult *= 10;
+
+ for (i = 0; i < NUM_CHANNELS; i++)
+ if (frq->m == (channel_frequency[i] * mult))
+ chan = i+1;
+ }
+
+ if ( (chan < 1) || (chan > NUM_CHANNELS) ||
+ ! (priv->channel_mask & (1 << (chan-1)) ) )
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ priv->channel = chan;
+ if (priv->iw_mode == IW_MODE_MONITOR) {
+ /* Fast channel change - no commit if successful */
+ hermes_t *hw = &priv->hw;
+ err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ HERMES_TEST_SET_CHANNEL,
+ chan, NULL);
+ }
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *frq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int tmp;
+
+ /* Locking done in there */
+ tmp = orinoco_hw_get_freq(priv);
+ if (tmp < 0) {
+ return tmp;
+ }
+
+ frq->m = tmp;
+ frq->e = 1;
+
+ return 0;
+}
+
+static int orinoco_ioctl_getsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *srq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ u16 val;
+ int err;
+ unsigned long flags;
+
+ if (!priv->has_sensitivity)
+ return -EOPNOTSUPP;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSYSTEMSCALE, &val);
+ orinoco_unlock(priv, &flags);
+
+ if (err)
+ return err;
+
+ srq->value = val;
+ srq->fixed = 0; /* auto */
+
+ return 0;
+}
+
+static int orinoco_ioctl_setsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *srq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int val = srq->value;
+ unsigned long flags;
+
+ if (!priv->has_sensitivity)
+ return -EOPNOTSUPP;
+
+ if ((val < 1) || (val > 3))
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+ priv->ap_density = val;
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_setrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int val = rrq->value;
+ unsigned long flags;
+
+ if (rrq->disabled)
+ val = 2347;
+
+ if ( (val < 0) || (val > 2347) )
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ priv->rts_thresh = val;
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_getrts(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ rrq->value = priv->rts_thresh;
+ rrq->disabled = (rrq->value == 2347);
+ rrq->fixed = 1;
+
+ return 0;
+}
+
+static int orinoco_ioctl_setfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = -EINPROGRESS; /* Call commit handler */
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (priv->has_mwo) {
+ if (frq->disabled)
+ priv->mwo_robust = 0;
+ else {
+ if (frq->fixed)
+ printk(KERN_WARNING "%s: Fixed fragmentation is "
+ "not supported on this firmware. "
+ "Using MWO robust instead.\n", dev->name);
+ priv->mwo_robust = 1;
+ }
+ } else {
+ if (frq->disabled)
+ priv->frag_thresh = 2346;
+ else {
+ if ( (frq->value < 256) || (frq->value > 2346) )
+ err = -EINVAL;
+ else
+ priv->frag_thresh = frq->value & ~0x1; /* must be even */
+ }
+ }
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getfrag(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *frq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err;
+ u16 val;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (priv->has_mwo) {
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMWOROBUST_AGERE,
+ &val);
+ if (err)
+ val = 0;
+
+ frq->value = val ? 2347 : 0;
+ frq->disabled = ! val;
+ frq->fixed = 0;
+ } else {
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+ &val);
+ if (err)
+ val = 0;
+
+ frq->value = val;
+ frq->disabled = (val >= 2346);
+ frq->fixed = 1;
+ }
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_setrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int ratemode = -1;
+ int bitrate; /* 100s of kilobits */
+ int i;
+ unsigned long flags;
+
+ /* As the user space doesn't know our highest rate, it uses -1
+ * to ask us to set the highest rate. Test it using "iwconfig
+ * ethX rate auto" - Jean II */
+ if (rrq->value == -1)
+ bitrate = 110;
+ else {
+ if (rrq->value % 100000)
+ return -EINVAL;
+ bitrate = rrq->value / 100000;
+ }
+
+ if ( (bitrate != 10) && (bitrate != 20) &&
+ (bitrate != 55) && (bitrate != 110) )
+ return -EINVAL;
+
+ for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+ if ( (bitrate_table[i].bitrate == bitrate) &&
+ (bitrate_table[i].automatic == ! rrq->fixed) ) {
+ ratemode = i;
+ break;
+ }
+
+ if (ratemode == -1)
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+ priv->bitratemode = ratemode;
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS;
+}
+
+static int orinoco_ioctl_getrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ int ratemode;
+ int i;
+ u16 val;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ ratemode = priv->bitratemode;
+
+ BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE));
+
+ rrq->value = bitrate_table[ratemode].bitrate * 100000;
+ rrq->fixed = ! bitrate_table[ratemode].automatic;
+ rrq->disabled = 0;
+
+ /* If the interface is running we try to find more about the
+ current mode */
+ if (netif_running(dev)) {
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CURRENTTXRATE, &val);
+ if (err)
+ goto out;
+
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
+ /* Note : in Lucent firmware, the return value of
+ * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
+ * and therefore is totally different from the
+ * encoding of HERMES_RID_CNFTXRATECONTROL.
+ * Don't forget that 6Mb/s is really 5.5Mb/s */
+ if (val == 6)
+ rrq->value = 5500000;
+ else
+ rrq->value = val * 1000000;
+ break;
+ case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
+ case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
+ for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+ if (bitrate_table[i].intersil_txratectrl == val) {
+ ratemode = i;
+ break;
+ }
+ if (i >= BITRATE_TABLE_SIZE)
+ printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
+ dev->name, val);
+
+ rrq->value = bitrate_table[ratemode].bitrate * 100000;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_setpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *prq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = -EINPROGRESS; /* Call commit handler */
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (prq->disabled) {
+ priv->pm_on = 0;
+ } else {
+ switch (prq->flags & IW_POWER_MODE) {
+ case IW_POWER_UNICAST_R:
+ priv->pm_mcast = 0;
+ priv->pm_on = 1;
+ break;
+ case IW_POWER_ALL_R:
+ priv->pm_mcast = 1;
+ priv->pm_on = 1;
+ break;
+ case IW_POWER_ON:
+ /* No flags : but we may have a value - Jean II */
+ break;
+ default:
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (prq->flags & IW_POWER_TIMEOUT) {
+ priv->pm_on = 1;
+ priv->pm_timeout = prq->value / 1000;
+ }
+ if (prq->flags & IW_POWER_PERIOD) {
+ priv->pm_on = 1;
+ priv->pm_period = prq->value / 1000;
+ }
+ /* It's valid to not have a value if we are just toggling
+ * the flags... Jean II */
+ if(!priv->pm_on) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getpower(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *prq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u16 enable, period, timeout, mcast;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable);
+ if (err)
+ goto out;
+
+ err = hermes_read_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFMAXSLEEPDURATION, &period);
+ if (err)
+ goto out;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
+ if (err)
+ goto out;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
+ if (err)
+ goto out;
+
+ prq->disabled = !enable;
+ /* Note : by default, display the period */
+ if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ prq->flags = IW_POWER_TIMEOUT;
+ prq->value = timeout * 1000;
+ } else {
+ prq->flags = IW_POWER_PERIOD;
+ prq->value = period * 1000;
+ }
+ if (mcast)
+ prq->flags |= IW_POWER_ALL_R;
+ else
+ prq->flags |= IW_POWER_UNICAST_R;
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, alg = ext->alg, set_key = 1;
+ unsigned long flags;
+ int err = -EINVAL;
+ u16 key_len;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Determine and validate the key index */
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if ((idx < 1) || (idx > 4))
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ alg = IW_ENCODE_ALG_NONE;
+
+ if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
+ /* Clear any TKIP TX key we had */
+ (void) orinoco_clear_tkip_key(priv, priv->tx_key);
+ }
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ priv->tx_key = idx;
+ set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
+ (ext->key_len > 0)) ? 1 : 0;
+ }
+
+ if (set_key) {
+ /* Set the requested key first */
+ switch (alg) {
+ case IW_ENCODE_ALG_NONE:
+ priv->encode_alg = alg;
+ priv->keys[idx].len = 0;
+ break;
+
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len > SMALL_KEY_SIZE)
+ key_len = LARGE_KEY_SIZE;
+ else if (ext->key_len > 0)
+ key_len = SMALL_KEY_SIZE;
+ else
+ goto out;
+
+ priv->encode_alg = alg;
+ priv->keys[idx].len = cpu_to_le16(key_len);
+
+ key_len = min(ext->key_len, key_len);
+
+ memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
+ memcpy(priv->keys[idx].data, ext->key, key_len);
+ break;
+
+ case IW_ENCODE_ALG_TKIP:
+ {
+ hermes_t *hw = &priv->hw;
+ u8 *tkip_iv = NULL;
+
+ if (!priv->has_wpa ||
+ (ext->key_len > sizeof(priv->tkip_key[0])))
+ goto out;
+
+ priv->encode_alg = alg;
+ memset(&priv->tkip_key[idx], 0,
+ sizeof(priv->tkip_key[idx]));
+ memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
+
+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+ tkip_iv = &ext->rx_seq[0];
+
+ err = __orinoco_hw_set_tkip_key(hw, idx,
+ ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
+ (u8 *) &priv->tkip_key[idx],
+ tkip_iv, NULL);
+ if (err)
+ printk(KERN_ERR "%s: Error %d setting TKIP key"
+ "\n", dev->name, err);
+
+ goto out;
+ }
+ default:
+ goto out;
+ }
+ }
+ err = -EINPROGRESS;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, max_key_len;
+ unsigned long flags;
+ int err;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = -EINVAL;
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ goto out;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if ((idx < 1) || (idx > 4))
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ ext->alg = priv->encode_alg;
+ switch (priv->encode_alg) {
+ case IW_ENCODE_ALG_NONE:
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len),
+ max_key_len);
+ memcpy(ext->key, priv->keys[idx].data, ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key),
+ max_key_len);
+ memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ }
+
+ err = 0;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_param *param = &wrqu->param;
+ unsigned long flags;
+ int ret = -EINPROGRESS;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ case IW_AUTH_DROP_UNENCRYPTED:
+ /*
+ * orinoco does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ /* wl_lkm implies value 2 == PSK for Hermes I
+ * which ties in with WEXT
+ * no other hints tho :(
+ */
+ priv->key_mgmt = param->value;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ /* When countermeasures are enabled, shut down the
+ * card; when disabled, re-enable the card. This must
+ * take effect immediately.
+ *
+ * TODO: Make sure that the EAPOL message is getting
+ * out before card disabled
+ */
+ if (param->value) {
+ priv->tkip_cm_active = 1;
+ ret = hermes_enable_port(hw, 0);
+ } else {
+ priv->tkip_cm_active = 0;
+ ret = hermes_disable_port(hw, 0);
+ }
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (param->value & IW_AUTH_ALG_SHARED_KEY)
+ priv->wep_restrict = 1;
+ else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
+ priv->wep_restrict = 0;
+ else
+ ret = -EINVAL;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ if (priv->has_wpa) {
+ priv->wpa_enabled = param->value ? 1 : 0;
+ } else {
+ if (param->value)
+ ret = -EOPNOTSUPP;
+ /* else silently accept disable of WPA */
+ priv->wpa_enabled = 0;
+ }
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
+static int orinoco_ioctl_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_param *param = &wrqu->param;
+ unsigned long flags;
+ int ret = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_KEY_MGMT:
+ param->value = priv->key_mgmt;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ param->value = priv->tkip_cm_active;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (priv->wep_restrict)
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ else
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = priv->wpa_enabled;
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
+static int orinoco_ioctl_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u8 *buf;
+ unsigned long flags;
+ int err = 0;
+
+ /* cut off at IEEE80211_MAX_DATA_LEN */
+ if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) ||
+ (wrqu->data.length && (extra == NULL)))
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(buf, extra, wrqu->data.length);
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = buf;
+ priv->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = NULL;
+ priv->wpa_ie_len = 0;
+ }
+
+ if (priv->wpa_ie) {
+ /* Looks like wl_lkm wants to check the auth alg, and
+ * somehow pass it to the firmware.
+ * Instead it just calls the key mgmt rid
+ * - we do this in set auth.
+ */
+ }
+
+out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static int orinoco_ioctl_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
+ wrqu->data.length = 0;
+ goto out;
+ }
+
+ if (wrqu->data.length < priv->wpa_ie_len) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ wrqu->data.length = priv->wpa_ie_len;
+ memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static int orinoco_ioctl_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ unsigned long flags;
+ int ret = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ /* silently ignore */
+ break;
+
+ case IW_MLME_DISASSOC:
+ {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 reason_code;
+ } __attribute__ ((packed)) buf;
+
+ memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
+ buf.reason_code = cpu_to_le16(mlme->reason_code);
+ ret = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFDISASSOCIATE,
+ &buf);
+ break;
+ }
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
+static int orinoco_ioctl_getretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u16 short_limit, long_limit, lifetime;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
+ &short_limit);
+ if (err)
+ goto out;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
+ &long_limit);
+ if (err)
+ goto out;
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
+ &lifetime);
+ if (err)
+ goto out;
+
+ rrq->disabled = 0; /* Can't be disabled */
+
+ /* Note : by default, display the retry number */
+ if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ rrq->flags = IW_RETRY_LIFETIME;
+ rrq->value = lifetime * 1000; /* ??? */
+ } else {
+ /* By default, display the min number */
+ if ((rrq->flags & IW_RETRY_LONG)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
+ rrq->value = long_limit;
+ } else {
+ rrq->flags = IW_RETRY_LIMIT;
+ rrq->value = short_limit;
+ if(short_limit != long_limit)
+ rrq->flags |= IW_RETRY_SHORT;
+ }
+ }
+
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ if (! capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
+ printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
+
+ /* Firmware reset */
+ orinoco_reset(&priv->reset_work);
+ } else {
+ printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
+
+ schedule_work(&priv->reset_work);
+ }
+
+ return 0;
+}
+
+static int orinoco_ioctl_setibssport(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int val = *( (int *) extra );
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ priv->ibss_port = val ;
+
+ /* Actually update the mode we are using */
+ set_port_type(priv);
+
+ orinoco_unlock(priv, &flags);
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_getibssport(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int *val = (int *) extra;
+
+ *val = priv->ibss_port;
+ return 0;
+}
+
+static int orinoco_ioctl_setport3(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int val = *( (int *) extra );
+ int err = 0;
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (val) {
+ case 0: /* Try to do IEEE ad-hoc mode */
+ if (! priv->has_ibss) {
+ err = -EINVAL;
+ break;
+ }
+ priv->prefer_port3 = 0;
+
+ break;
+
+ case 1: /* Try to do Lucent proprietary ad-hoc mode */
+ if (! priv->has_port3) {
+ err = -EINVAL;
+ break;
+ }
+ priv->prefer_port3 = 1;
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
+ if (! err) {
+ /* Actually update the mode we are using */
+ set_port_type(priv);
+ err = -EINPROGRESS;
+ }
+
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_getport3(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int *val = (int *) extra;
+
+ *val = priv->prefer_port3;
+ return 0;
+}
+
+static int orinoco_ioctl_setpreamble(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int val;
+
+ if (! priv->has_preamble)
+ return -EOPNOTSUPP;
+
+ /* 802.11b has recently defined some short preamble.
+ * Basically, the Phy header has been reduced in size.
+ * This increase performance, especially at high rates
+ * (the preamble is transmitted at 1Mb/s), unfortunately
+ * this give compatibility troubles... - Jean II */
+ val = *( (int *) extra );
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (val)
+ priv->preamble = 1;
+ else
+ priv->preamble = 0;
+
+ orinoco_unlock(priv, &flags);
+
+ return -EINPROGRESS; /* Call commit handler */
+}
+
+static int orinoco_ioctl_getpreamble(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int *val = (int *) extra;
+
+ if (! priv->has_preamble)
+ return -EOPNOTSUPP;
+
+ *val = priv->preamble;
+ return 0;
+}
+
+/* ioctl interface to hermes_read_ltv()
+ * To use with iwpriv, pass the RID as the token argument, e.g.
+ * iwpriv get_rid [0xfc00]
+ * At least Wireless Tools 25 is required to use iwpriv.
+ * For Wireless Tools 25 and 26 append "dummy" are the end. */
+static int orinoco_ioctl_getrid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ int rid = data->flags;
+ u16 length;
+ int err;
+ unsigned long flags;
+
+ /* It's a "get" function, but we don't want users to access the
+ * WEP key and other raw firmware data */
+ if (! capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (rid < 0xfc00 || rid > 0xffff)
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
+ extra);
+ if (err)
+ goto out;
+
+ data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
+ MAX_RID_LEN);
+
+ out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+/* Trigger a scan (look for other cells in the vicinity) */
+static int orinoco_ioctl_setscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *srq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_scan_req *si = (struct iw_scan_req *) extra;
+ int err = 0;
+ unsigned long flags;
+
+ /* Note : you may have realised that, as this is a SET operation,
+ * this is privileged and therefore a normal user can't
+ * perform scanning.
+ * This is not an error, while the device perform scanning,
+ * traffic doesn't flow, so it's a perfect DoS...
+ * Jean II */
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Scanning with port 0 disabled would fail */
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ /* In monitor mode, the scan results are always empty.
+ * Probe responses are passed to the driver as received
+ * frames and could be processed in software. */
+ if (priv->iw_mode == IW_MODE_MONITOR) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Note : because we don't lock out the irq handler, the way
+ * we access scan variables in priv is critical.
+ * o scan_inprogress : not touched by irq handler
+ * o scan_mode : not touched by irq handler
+ * Before modifying anything on those variables, please think hard !
+ * Jean II */
+
+ /* Save flags */
+ priv->scan_mode = srq->flags;
+
+ /* Always trigger scanning, even if it's in progress.
+ * This way, if the info frame get lost, we will recover somewhat
+ * gracefully - Jean II */
+
+ if (priv->has_hostscan) {
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_SYMBOL:
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFHOSTSCAN_SYMBOL,
+ HERMES_HOSTSCAN_SYMBOL_ONCE |
+ HERMES_HOSTSCAN_SYMBOL_BCAST);
+ break;
+ case FIRMWARE_TYPE_INTERSIL: {
+ __le16 req[3];
+
+ req[0] = cpu_to_le16(0x3fff); /* All channels */
+ req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
+ req[2] = 0; /* Any ESSID */
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFHOSTSCAN, &req);
+ }
+ break;
+ case FIRMWARE_TYPE_AGERE:
+ if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
+ struct hermes_idstring idbuf;
+ size_t len = min(sizeof(idbuf.val),
+ (size_t) si->essid_len);
+ idbuf.len = cpu_to_le16(len);
+ memcpy(idbuf.val, si->essid, len);
+
+ err = hermes_write_ltv(hw, USER_BAP,
+ HERMES_RID_CNFSCANSSID_AGERE,
+ HERMES_BYTES_TO_RECLEN(len + 2),
+ &idbuf);
+ } else
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSCANSSID_AGERE,
+ 0); /* Any ESSID */
+ if (err)
+ break;
+
+ if (priv->has_ext_scan) {
+ /* Clear scan results at the start of
+ * an extended scan */
+ orinoco_clear_scan_results(priv,
+ msecs_to_jiffies(15000));
+
+ /* TODO: Is this available on older firmware?
+ * Can we use it to scan specific channels
+ * for IW_SCAN_THIS_FREQ? */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSCANCHANNELS2GHZ,
+ 0x7FFF);
+ if (err)
+ goto out;
+
+ err = hermes_inquire(hw,
+ HERMES_INQ_CHANNELINFO);
+ } else
+ err = hermes_inquire(hw, HERMES_INQ_SCAN);
+ break;
+ }
+ } else
+ err = hermes_inquire(hw, HERMES_INQ_SCAN);
+
+ /* One more client */
+ if (! err)
+ priv->scan_inprogress = 1;
+
+ out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+#define MAX_CUSTOM_LEN 64
+
+/* Translate scan data returned from the card to a card independant
+ * format that the Wireless Tools will understand - Jean II */
+static inline char *orinoco_translate_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ union hermes_scan_info *bss,
+ unsigned long last_scanned)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u16 capabilities;
+ u16 channel;
+ struct iw_event iwe; /* Temporary buffer */
+ char custom[MAX_CUSTOM_LEN];
+
+ memset(&iwe, 0, sizeof(iwe));
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ iwe.u.data.length = le16_to_cpu(bss->a.essid_len);
+ if (iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, bss->a.essid);
+
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ capabilities = le16_to_cpu(bss->a.capabilities);
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ channel = bss->s.channel;
+ if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
+ /* Add channel and frequency */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = channel;
+ iwe.u.freq.e = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
+ iwe.u.freq.m = channel_frequency[channel-1] * 100000;
+ iwe.u.freq.e = 1;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+ }
+
+ /* Add quality statistics. level and noise in dB. No link quality */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
+ iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
+ /* Wireless tools prior to 27.pre22 will show link quality
+ * anyway, so we provide a reasonable value. */
+ if (iwe.u.qual.level > iwe.u.qual.noise)
+ iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+ else
+ iwe.u.qual.qual = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (capabilities & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, NULL);
+
+ /* Bit rate is not available in Lucent/Agere firmwares */
+ if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
+ char *current_val = current_ev + iwe_stream_lcp_len(info);
+ int i;
+ int step;
+
+ if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
+ step = 2;
+ else
+ step = 1;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ /* Max 10 values */
+ for (i = 0; i < 10; i += step) {
+ /* NULL terminated */
+ if (bss->p.rates[i] == 0x0)
+ break;
+ /* Bit rate given in 500 kb/s units (+ 0x80) */
+ iwe.u.bitrate.value =
+ ((bss->p.rates[i] & 0x7f) * 500000);
+ current_val = iwe_stream_add_value(info, current_ev,
+ current_val,
+ end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if ((current_val - current_ev) > iwe_stream_lcp_len(info))
+ current_ev = current_val;
+ }
+
+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->a.beacon_interv));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Capabilites */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "capab=0x%04x",
+ capabilities);
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network. */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ " Last beacon: %dms ago",
+ jiffies_to_msecs(jiffies - last_scanned));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ return current_ev;
+}
+
+static inline char *orinoco_translate_ext_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ struct agere_ext_scan_info *bss,
+ unsigned long last_scanned)
+{
+ u16 capabilities;
+ u16 channel;
+ struct iw_event iwe; /* Temporary buffer */
+ char custom[MAX_CUSTOM_LEN];
+ u8 *ie;
+
+ memset(&iwe, 0, sizeof(iwe));
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ ie = bss->data;
+ iwe.u.data.length = ie[1];
+ if (iwe.u.data.length) {
+ if (iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, &ie[2]);
+ }
+
+ /* Add mode */
+ capabilities = le16_to_cpu(bss->capabilities);
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS);
+ channel = ie ? ie[2] : 0;
+ if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
+ /* Add channel and frequency */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = channel;
+ iwe.u.freq.e = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
+ iwe.u.freq.m = channel_frequency[channel-1] * 100000;
+ iwe.u.freq.e = 1;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+ }
+
+ /* Add quality statistics. level and noise in dB. No link quality */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.level = bss->level - 0x95;
+ iwe.u.qual.noise = bss->noise - 0x95;
+ /* Wireless tools prior to 27.pre22 will show link quality
+ * anyway, so we provide a reasonable value. */
+ if (iwe.u.qual.level > iwe.u.qual.noise)
+ iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+ else
+ iwe.u.qual.qual = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (capabilities & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, NULL);
+
+ /* WPA IE */
+ ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ /* RSN IE */
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN);
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES);
+ if (ie) {
+ char *p = current_ev + iwe_stream_lcp_len(info);
+ int i;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 2; i < (ie[1] + 2); i++) {
+ iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
+ p = iwe_stream_add_value(info, current_ev, p, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if (p > (current_ev + iwe_stream_lcp_len(info)))
+ current_ev = p;
+ }
+
+ /* Timestamp */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length =
+ snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx",
+ (unsigned long long) le64_to_cpu(bss->timestamp));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->beacon_interval));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Capabilites */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "capab=0x%04x",
+ capabilities);
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network. */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ " Last beacon: %dms ago",
+ jiffies_to_msecs(jiffies - last_scanned));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ return current_ev;
+}
+
+/* Return results of a scan */
+static int orinoco_ioctl_getscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *srq,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = 0;
+ unsigned long flags;
+ char *current_ev = extra;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (priv->scan_inprogress) {
+ /* Important note : we don't want to block the caller
+ * until results are ready for various reasons.
+ * First, managing wait queues is complex and racy.
+ * Second, we grab some rtnetlink lock before comming
+ * here (in dev_ioctl()).
+ * Third, we generate an Wireless Event, so the
+ * caller can wait itself on that - Jean II */
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev =
+ orinoco_translate_ext_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+
+ } else {
+ struct bss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev = orinoco_translate_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+ }
+
+ srq->length = (current_ev - extra);
+ srq->flags = (__u16) priv->scan_mode;
+
+out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+/* Commit handler, called after set operations */
+static int orinoco_ioctl_commit(struct net_device *dev,
+ struct iw_request_info *info,
+ void *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct hermes *hw = &priv->hw;
+ unsigned long flags;
+ int err = 0;
+
+ if (!priv->open)
+ return 0;
+
+ if (priv->broken_disableport) {
+ orinoco_reset(&priv->reset_work);
+ return 0;
+ }
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return err;
+
+ err = hermes_disable_port(hw, 0);
+ if (err) {
+ printk(KERN_WARNING "%s: Unable to disable port "
+ "while reconfiguring card\n", dev->name);
+ priv->broken_disableport = 1;
+ goto out;
+ }
+
+ err = __orinoco_program_rids(dev);
+ if (err) {
+ printk(KERN_WARNING "%s: Unable to reconfigure card\n",
+ dev->name);
+ goto out;
+ }
+
+ err = hermes_enable_port(hw, 0);
+ if (err) {
+ printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
+ dev->name);
+ goto out;
+ }
+
+ out:
+ if (err) {
+ printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
+ schedule_work(&priv->reset_work);
+ err = 0;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static const struct iw_priv_args orinoco_privtab[] = {
+ { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
+ { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
+ { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0, "set_port3" },
+ { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "get_port3" },
+ { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0, "set_preamble" },
+ { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "get_preamble" },
+ { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 0, "set_ibssport" },
+ { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ "get_ibssport" },
+ { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
+ "get_rid" },
+};
+
+
+/*
+ * Structures to export the Wireless Handlers
+ */
+
+#define STD_IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = (iw_handler) func
+static const iw_handler orinoco_handler[] = {
+ STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
+ STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
+ STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
+ STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
+ STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
+ STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
+ STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
+ STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
+ STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
+ STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+ STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+ STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+ STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
+ STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
+ STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
+ STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
+ STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
+ STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
+ STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
+ STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
+ STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
+ STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
+ STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
+ STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
+ STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
+ STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
+ STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
+ STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
+ STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
+ STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
+ STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
+ STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
+ STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
+ STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
+ STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
+ STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
+ STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
+ STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
+ STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
+};
+
+
+/*
+ Added typecasting since we no longer use iwreq_data -- Moustafa
+ */
+static const iw_handler orinoco_private_handler[] = {
+ [0] = (iw_handler) orinoco_ioctl_reset,
+ [1] = (iw_handler) orinoco_ioctl_reset,
+ [2] = (iw_handler) orinoco_ioctl_setport3,
+ [3] = (iw_handler) orinoco_ioctl_getport3,
+ [4] = (iw_handler) orinoco_ioctl_setpreamble,
+ [5] = (iw_handler) orinoco_ioctl_getpreamble,
+ [6] = (iw_handler) orinoco_ioctl_setibssport,
+ [7] = (iw_handler) orinoco_ioctl_getibssport,
+ [9] = (iw_handler) orinoco_ioctl_getrid,
+};
+
+static const struct iw_handler_def orinoco_handler_def = {
+ .num_standard = ARRAY_SIZE(orinoco_handler),
+ .num_private = ARRAY_SIZE(orinoco_private_handler),
+ .num_private_args = ARRAY_SIZE(orinoco_privtab),
+ .standard = orinoco_handler,
+ .private = orinoco_private_handler,
+ .private_args = orinoco_privtab,
+ .get_wireless_stats = orinoco_get_wireless_stats,
+};
+
+static void orinoco_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+
+ strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
+ strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
+ strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
+ if (dev->dev.parent)
+ strncpy(info->bus_info, dev_name(dev->dev.parent),
+ sizeof(info->bus_info) - 1);
+ else
+ snprintf(info->bus_info, sizeof(info->bus_info) - 1,
+ "PCMCIA %p", priv->hw.iobase);
+}
+
+static const struct ethtool_ops orinoco_ethtool_ops = {
+ .get_drvinfo = orinoco_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+/********************************************************************/
+/* Module initialization */
+/********************************************************************/
+
+EXPORT_SYMBOL(alloc_orinocodev);
+EXPORT_SYMBOL(free_orinocodev);
+
+EXPORT_SYMBOL(__orinoco_up);
+EXPORT_SYMBOL(__orinoco_down);
+EXPORT_SYMBOL(orinoco_reinit_firmware);
+
+EXPORT_SYMBOL(orinoco_interrupt);
+
+/* Can't be declared "const" or the whole __initdata section will
+ * become const */
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+ " (David Gibson <hermes@gibson.dropbear.id.au>, "
+ "Pavel Roskin <proski@gnu.org>, et al)";
+
+static int __init init_orinoco(void)
+{
+ printk(KERN_DEBUG "%s\n", version);
+ return 0;
+}
+
+static void __exit exit_orinoco(void)
+{
+}
+
+module_init(init_orinoco);
+module_exit(exit_orinoco);
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
new file mode 100644
index 000000000000..00750c8ba7db
--- /dev/null
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -0,0 +1,222 @@
+/* orinoco.h
+ *
+ * Common definitions to all pieces of the various orinoco
+ * drivers
+ */
+
+#ifndef _ORINOCO_H
+#define _ORINOCO_H
+
+#define DRIVER_VERSION "0.15"
+
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+#include "hermes.h"
+
+/* To enable debug messages */
+//#define ORINOCO_DEBUG 3
+
+#define WIRELESS_SPY // enable iwspy support
+
+#define MAX_SCAN_LEN 4096
+
+#define ORINOCO_MAX_KEY_SIZE 14
+#define ORINOCO_MAX_KEYS 4
+
+struct orinoco_key {
+ __le16 len; /* always stored as little-endian */
+ char data[ORINOCO_MAX_KEY_SIZE];
+} __attribute__ ((packed));
+
+#define TKIP_KEYLEN 16
+#define MIC_KEYLEN 8
+
+struct orinoco_tkip_key {
+ u8 tkip[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+};
+
+typedef enum {
+ FIRMWARE_TYPE_AGERE,
+ FIRMWARE_TYPE_INTERSIL,
+ FIRMWARE_TYPE_SYMBOL
+} fwtype_t;
+
+struct bss_element {
+ union hermes_scan_info bss;
+ unsigned long last_scanned;
+ struct list_head list;
+};
+
+struct xbss_element {
+ struct agere_ext_scan_info bss;
+ unsigned long last_scanned;
+ struct list_head list;
+};
+
+struct hermes_rx_descriptor;
+
+struct orinoco_rx_data {
+ struct hermes_rx_descriptor *desc;
+ struct sk_buff *skb;
+ struct list_head list;
+};
+
+struct firmware;
+
+struct orinoco_private {
+ void *card; /* Pointer to card dependent structure */
+ struct device *dev;
+ int (*hard_reset)(struct orinoco_private *);
+ int (*stop_fw)(struct orinoco_private *, int);
+
+ /* Synchronisation stuff */
+ spinlock_t lock;
+ int hw_unavailable;
+ struct work_struct reset_work;
+
+ /* Interrupt tasklets */
+ struct tasklet_struct rx_tasklet;
+ struct list_head rx_list;
+ struct orinoco_rx_data *rx_data;
+
+ /* driver state */
+ int open;
+ u16 last_linkstatus;
+ struct work_struct join_work;
+ struct work_struct wevent_work;
+
+ /* Net device stuff */
+ struct net_device *ndev;
+ struct net_device_stats stats;
+ struct iw_statistics wstats;
+
+ /* Hardware control variables */
+ hermes_t hw;
+ u16 txfid;
+
+ /* Capabilities of the hardware/firmware */
+ fwtype_t firmware_type;
+ char fw_name[32];
+ int ibss_port;
+ int nicbuf_size;
+ u16 channel_mask;
+
+ /* Boolean capabilities */
+ unsigned int has_ibss:1;
+ unsigned int has_port3:1;
+ unsigned int has_wep:1;
+ unsigned int has_big_wep:1;
+ unsigned int has_mwo:1;
+ unsigned int has_pm:1;
+ unsigned int has_preamble:1;
+ unsigned int has_sensitivity:1;
+ unsigned int has_hostscan:1;
+ unsigned int has_alt_txcntl:1;
+ unsigned int has_ext_scan:1;
+ unsigned int has_wpa:1;
+ unsigned int do_fw_download:1;
+ unsigned int broken_disableport:1;
+ unsigned int broken_monitor:1;
+
+ /* Configuration paramaters */
+ u32 iw_mode;
+ int prefer_port3;
+ u16 encode_alg, wep_restrict, tx_key;
+ struct orinoco_key keys[ORINOCO_MAX_KEYS];
+ int bitratemode;
+ char nick[IW_ESSID_MAX_SIZE+1];
+ char desired_essid[IW_ESSID_MAX_SIZE+1];
+ char desired_bssid[ETH_ALEN];
+ int bssid_fixed;
+ u16 frag_thresh, mwo_robust;
+ u16 channel;
+ u16 ap_density, rts_thresh;
+ u16 pm_on, pm_mcast, pm_period, pm_timeout;
+ u16 preamble;
+#ifdef WIRELESS_SPY
+ struct iw_spy_data spy_data; /* iwspy support */
+ struct iw_public_data wireless_data;
+#endif
+
+ /* Configuration dependent variables */
+ int port_type, createibss;
+ int promiscuous, mc_count;
+
+ /* Scanning support */
+ struct list_head bss_list;
+ struct list_head bss_free_list;
+ void *bss_xbss_data;
+
+ int scan_inprogress; /* Scan pending... */
+ u32 scan_mode; /* Type of scan done */
+
+ /* WPA support */
+ u8 *wpa_ie;
+ int wpa_ie_len;
+
+ struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
+ struct crypto_hash *rx_tfm_mic;
+ struct crypto_hash *tx_tfm_mic;
+
+ unsigned int wpa_enabled:1;
+ unsigned int tkip_cm_active:1;
+ unsigned int key_mgmt:3;
+
+ /* Cached in memory firmware to use during ->resume. */
+ const struct firmware *cached_pri_fw;
+ const struct firmware *cached_fw;
+
+ struct notifier_block pm_notifier;
+};
+
+#ifdef ORINOCO_DEBUG
+extern int orinoco_debug;
+#define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
+#else
+#define DEBUG(n, args...) do { } while (0)
+#endif /* ORINOCO_DEBUG */
+
+/********************************************************************/
+/* Exported prototypes */
+/********************************************************************/
+
+extern struct net_device *alloc_orinocodev(
+ int sizeof_card, struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int));
+extern void free_orinocodev(struct net_device *dev);
+extern int __orinoco_up(struct net_device *dev);
+extern int __orinoco_down(struct net_device *dev);
+extern int orinoco_reinit_firmware(struct net_device *dev);
+extern irqreturn_t orinoco_interrupt(int irq, void * dev_id);
+
+/********************************************************************/
+/* Locking and synchronization functions */
+/********************************************************************/
+
+static inline int orinoco_lock(struct orinoco_private *priv,
+ unsigned long *flags)
+{
+ spin_lock_irqsave(&priv->lock, *flags);
+ if (priv->hw_unavailable) {
+ DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n",
+ priv->ndev);
+ spin_unlock_irqrestore(&priv->lock, *flags);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static inline void orinoco_unlock(struct orinoco_private *priv,
+ unsigned long *flags)
+{
+ spin_unlock_irqrestore(&priv->lock, *flags);
+}
+
+#endif /* _ORINOCO_H */
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
new file mode 100644
index 000000000000..f127602670ec
--- /dev/null
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -0,0 +1,537 @@
+/* orinoco_cs.c (formerly known as dldwd_cs.c)
+ *
+ * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
+ * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
+ * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others).
+ * It should also be usable on various Prism II based cards such as the
+ * Linksys, D-Link and Farallon Skyline. It should also work on Symbol
+ * cards such as the 3Com AirConnect and Ericsson WLAN.
+ *
+ * Copyright notice & release notes in file orinoco.c
+ */
+
+#define DRIVER_NAME "orinoco_cs"
+#define PFX DRIVER_NAME ": "
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include "orinoco.h"
+
+/********************************************************************/
+/* Module stuff */
+/********************************************************************/
+
+MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
+MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/* Module parameters */
+
+/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
+ * don't have any CIS entry for it. This workaround it... */
+static int ignore_cis_vcc; /* = 0 */
+module_param(ignore_cis_vcc, int, 0);
+MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
+
+/********************************************************************/
+/* Data structures */
+/********************************************************************/
+
+/* PCMCIA specific device information (goes in the card field of
+ * struct orinoco_private */
+struct orinoco_pccard {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+
+ /* Used to handle hard reset */
+ /* yuck, we need this hack to work around the insanity of the
+ * PCMCIA layer */
+ unsigned long hard_reset_in_progress;
+};
+
+
+/********************************************************************/
+/* Function prototypes */
+/********************************************************************/
+
+static int orinoco_cs_config(struct pcmcia_device *link);
+static void orinoco_cs_release(struct pcmcia_device *link);
+static void orinoco_cs_detach(struct pcmcia_device *p_dev);
+
+/********************************************************************/
+/* Device methods */
+/********************************************************************/
+
+static int
+orinoco_cs_hard_reset(struct orinoco_private *priv)
+{
+ struct orinoco_pccard *card = priv->card;
+ struct pcmcia_device *link = card->p_dev;
+ int err;
+
+ /* We need atomic ops here, because we're not holding the lock */
+ set_bit(0, &card->hard_reset_in_progress);
+
+ err = pcmcia_reset_card(link->socket);
+ if (err)
+ return err;
+
+ msleep(100);
+ clear_bit(0, &card->hard_reset_in_progress);
+
+ return 0;
+}
+
+/********************************************************************/
+/* PCMCIA stuff */
+/********************************************************************/
+
+/*
+ * This creates an "instance" of the driver, allocating local data
+ * structures for one device. The device is registered with Card
+ * Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a card
+ * insertion event. */
+static int
+orinoco_cs_probe(struct pcmcia_device *link)
+{
+ struct net_device *dev;
+ struct orinoco_private *priv;
+ struct orinoco_pccard *card;
+
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ orinoco_cs_hard_reset, NULL);
+ if (! dev)
+ return -ENOMEM;
+ priv = netdev_priv(dev);
+ card = priv->card;
+
+ /* Link both structures together */
+ card->p_dev = link;
+ link->priv = dev;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = orinoco_interrupt;
+ link->irq.Instance = dev;
+
+ /* General socket configuration defaults can go here. In this
+ * client, we assume very little, and rely on the CIS for
+ * almost everything. In most clients, many details (i.e.,
+ * number, sizes, and attributes of IO windows) are fixed by
+ * the nature of the device, and can be hard-wired here. */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return orinoco_cs_config(link);
+} /* orinoco_cs_attach */
+
+/*
+ * This deletes a driver "instance". The device is de-registered with
+ * Card Services. If it has been released, all local data structures
+ * are freed. Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void orinoco_cs_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->dev_node)
+ unregister_netdev(dev);
+
+ orinoco_cs_release(link);
+
+ free_orinocodev(dev);
+} /* orinoco_cs_detach */
+
+/*
+ * orinoco_cs_config() is scheduled to run after a CARD_INSERTION
+ * event is received, to configure the PCMCIA socket, and to make the
+ * device available to the system.
+ */
+
+#define CS_CHECK(fn, ret) do { \
+ last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \
+ } while (0)
+
+static int orinoco_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ goto next_entry;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n",
+ __func__, vcc,
+ cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n",
+ __func__, vcc,
+ dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+ return 0;
+
+next_entry:
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+};
+
+static int
+orinoco_cs_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_pccard *card = priv->card;
+ hermes_t *hw = &priv->hw;
+ int last_fn, last_ret;
+ void __iomem *mem;
+
+ /*
+ * In this loop, we scan the CIS for configuration table
+ * entries, each of which describes a valid card
+ * configuration, including voltage, IO window, memory window,
+ * and interrupt settings.
+ *
+ * We make no assumptions about the card to be configured: we
+ * use just the information available in the CIS. In an ideal
+ * world, this would work for any PCMCIA card, but it requires
+ * a complete and accurate CIS. In practice, a driver usually
+ * "knows" most of these things without consulting the CIS,
+ * and most client drivers will only use the CIS to fill in
+ * implementation-defined details.
+ */
+ last_ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
+ printk(KERN_ERR PFX "GetNextTuple(): No matching "
+ "CIS configuration. Maybe you need the "
+ "ignore_cis_vcc=1 parameter.\n");
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
+ }
+
+ /*
+ * Allocate an interrupt line. Note that this does not assign
+ * a handler to the interrupt, unless the 'Handler' member of
+ * the irq structure is initialized.
+ */
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+
+ /* We initialize the hermes structure before completing PCMCIA
+ * configuration just in case the interrupt handler gets
+ * called. */
+ mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
+ if (!mem)
+ goto cs_failed;
+
+ hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping, and putting the
+ * card and host interface into "Memory and IO" mode.
+ */
+ CS_CHECK(RequestConfiguration,
+ pcmcia_request_configuration(link, &link->conf));
+
+ /* Ok, we have the configuration, prepare to register the netdev */
+ dev->base_addr = link->io.BasePort1;
+ dev->irq = link->irq.AssignedIRQ;
+ card->node.major = card->node.minor = 0;
+
+ SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ /* Tell the stack we exist */
+ if (register_netdev(dev) != 0) {
+ printk(KERN_ERR PFX "register_netdev() failed\n");
+ goto failed;
+ }
+
+ /* At this point, the dev_node_t structure(s) needs to be
+ * initialized and arranged in a linked list at link->dev_node. */
+ strcpy(card->node.dev_name, dev->name);
+ link->dev_node = &card->node; /* link->dev_node being non-NULL is also
+ used to indicate that the
+ net_device has been registered */
+
+ /* Finally, report what we've done */
+ printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io "
+ "0x%04x-0x%04x\n", dev->name, dev_name(dev->dev.parent),
+ link->irq.AssignedIRQ, link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+ return 0;
+
+ cs_failed:
+ cs_error(link, last_fn, last_ret);
+
+ failed:
+ orinoco_cs_release(link);
+ return -ENODEV;
+} /* orinoco_cs_config */
+
+/*
+ * After a card is removed, orinoco_cs_release() will unregister the
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+orinoco_cs_release(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* We're committed to taking the device away now, so mark the
+ * hardware as unavailable */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->hw_unavailable++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ pcmcia_disable_device(link);
+ if (priv->hw.iobase)
+ ioport_unmap(priv->hw.iobase);
+} /* orinoco_cs_release */
+
+static int orinoco_cs_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_pccard *card = priv->card;
+ int err = 0;
+ unsigned long flags;
+
+ /* This is probably racy, but I can't think of
+ a better way, short of rewriting the PCMCIA
+ layer to not suck :-( */
+ if (! test_bit(0, &card->hard_reset_in_progress)) {
+ spin_lock_irqsave(&priv->lock, flags);
+
+ err = __orinoco_down(dev);
+ if (err)
+ printk(KERN_WARNING "%s: Error %d downing interface\n",
+ dev->name, err);
+
+ netif_device_detach(dev);
+ priv->hw_unavailable++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+ return 0;
+}
+
+static int orinoco_cs_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_pccard *card = priv->card;
+ int err = 0;
+ unsigned long flags;
+
+ if (! test_bit(0, &card->hard_reset_in_progress)) {
+ err = orinoco_reinit_firmware(dev);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
+ dev->name, err);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ netif_device_attach(dev);
+ priv->hw_unavailable--;
+
+ if (priv->open && ! priv->hw_unavailable) {
+ err = __orinoco_up(dev);
+ if (err)
+ printk(KERN_ERR "%s: Error %d restarting card\n",
+ dev->name, err);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+ return err;
+}
+
+
+/********************************************************************/
+/* Module initialization */
+/********************************************************************/
+
+/* Can't be declared "const" or the whole __initdata section will
+ * become const */
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+ " (David Gibson <hermes@gibson.dropbear.id.au>, "
+ "Pavel Roskin <proski@gnu.org>, et al)";
+
+static struct pcmcia_device_id orinoco_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */
+ PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */
+ PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */
+ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */
+ PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */
+ PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */
+ PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), /* Lucent Orinoco and old Intersil */
+ PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */
+ PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */
+ PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */
+ PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */
+ PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */
+ PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */
+ PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */
+ PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */
+ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */
+ PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */
+ PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */
+ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */
+ PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */
+ PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */
+ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */
+ PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */
+ PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */
+ PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */
+ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */
+ PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9),
+ PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3),
+ PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5),
+ PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2),
+ PCMCIA_DEVICE_PROD_ID123("AIRVAST", "IEEE 802.11b Wireless PCMCIA Card", "HFA3863", 0xea569531, 0x4bcb9645, 0x355cb092),
+ PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f),
+ PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842),
+ PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e),
+ PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169),
+ PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb),
+ PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3),
+ PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18),
+ PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90),
+ PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b),
+ PCMCIA_DEVICE_PROD_ID123("corega", "WL PCCL-11", "ISL37300P", 0x0a21501a, 0x59868926, 0xc9049a39),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584),
+ PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9),
+ PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae),
+ PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac),
+ PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab),
+ PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916),
+ PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146),
+ PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3),
+ PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c),
+ PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0),
+ PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077),
+ PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18),
+ PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77),
+ PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf),
+ PCMCIA_DEVICE_PROD_ID123("Intersil", "PRISM Freedom PCMCIA Adapter", "ISL37100P", 0x4b801a17, 0xf222ec2d, 0x630d52b2),
+ PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92),
+ PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395),
+ PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410),
+ PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3),
+ PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01),
+ PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1),
+ PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1),
+ PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767),
+ PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6),
+ PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed),
+ PCMCIA_DEVICE_PROD_ID123("PCMCIA", "11M WLAN Card v2.5", "ISL37300P", 0x281f1c5d, 0x6e440487, 0xc9049a39),
+ PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264),
+ PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178),
+ PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9),
+ PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26),
+ PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b),
+ PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757),
+ PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a),
+ PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e),
+ PCMCIA_DEVICE_PROD_ID123("The Linksys Group, Inc.", "Instant Wireless Network PC Card", "ISL37300P", 0xa5f472c2, 0x590eb502, 0xc9049a39),
+ PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids);
+
+static struct pcmcia_driver orinoco_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = DRIVER_NAME,
+ },
+ .probe = orinoco_cs_probe,
+ .remove = orinoco_cs_detach,
+ .id_table = orinoco_cs_ids,
+ .suspend = orinoco_cs_suspend,
+ .resume = orinoco_cs_resume,
+};
+
+static int __init
+init_orinoco_cs(void)
+{
+ printk(KERN_DEBUG "%s\n", version);
+
+ return pcmcia_register_driver(&orinoco_driver);
+}
+
+static void __exit
+exit_orinoco_cs(void)
+{
+ pcmcia_unregister_driver(&orinoco_driver);
+}
+
+module_init(init_orinoco_cs);
+module_exit(exit_orinoco_cs);
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c
index 2fc86596302e..2fc86596302e 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco/orinoco_nortel.c
diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c
index 4ebd638a073e..4ebd638a073e 100644
--- a/drivers/net/wireless/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco/orinoco_pci.c
diff --git a/drivers/net/wireless/orinoco_pci.h b/drivers/net/wireless/orinoco/orinoco_pci.h
index f4e5e06760c1..f4e5e06760c1 100644
--- a/drivers/net/wireless/orinoco_pci.h
+++ b/drivers/net/wireless/orinoco/orinoco_pci.h
diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c
index ef761857bb38..ef761857bb38 100644
--- a/drivers/net/wireless/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco/orinoco_plx.c
diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c
index ede24ec309c0..ede24ec309c0 100644
--- a/drivers/net/wireless/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco/orinoco_tmd.c
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
new file mode 100644
index 000000000000..b2ca2e39c2cb
--- /dev/null
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -0,0 +1,530 @@
+/*
+ * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
+ * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
+ * Communications and Intel PRO/Wireless 2011B.
+ *
+ * The driver implements Symbol firmware download. The rest is handled
+ * in hermes.c and orinoco.c.
+ *
+ * Utilities for downloading the Symbol firmware are available at
+ * http://sourceforge.net/projects/orinoco/
+ *
+ * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
+ * Portions based on orinoco_cs.c:
+ * Copyright (C) David Gibson, Linuxcare Australia
+ * Portions based on Spectrum24tDnld.c from original spectrum24 driver:
+ * Copyright (C) Symbol Technologies.
+ *
+ * See copyright notice in file orinoco.c.
+ */
+
+#define DRIVER_NAME "spectrum_cs"
+#define PFX DRIVER_NAME ": "
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include "orinoco.h"
+
+/********************************************************************/
+/* Module stuff */
+/********************************************************************/
+
+MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
+MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/* Module parameters */
+
+/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
+ * don't have any CIS entry for it. This workaround it... */
+static int ignore_cis_vcc; /* = 0 */
+module_param(ignore_cis_vcc, int, 0);
+MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
+
+/********************************************************************/
+/* Data structures */
+/********************************************************************/
+
+/* PCMCIA specific device information (goes in the card field of
+ * struct orinoco_private */
+struct orinoco_pccard {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+};
+
+/********************************************************************/
+/* Function prototypes */
+/********************************************************************/
+
+static int spectrum_cs_config(struct pcmcia_device *link);
+static void spectrum_cs_release(struct pcmcia_device *link);
+
+/* Constants for the CISREG_CCSR register */
+#define HCR_RUN 0x07 /* run firmware after reset */
+#define HCR_IDLE 0x0E /* don't run firmware after reset */
+#define HCR_MEM16 0x10 /* memory width bit, should be preserved */
+
+
+#define CS_CHECK(fn, ret) \
+ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+/*
+ * Reset the card using configuration registers COR and CCSR.
+ * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
+ */
+static int
+spectrum_reset(struct pcmcia_device *link, int idle)
+{
+ int last_ret, last_fn;
+ conf_reg_t reg;
+ u_int save_cor;
+
+ /* Doing it if hardware is gone is guaranteed crash */
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+
+ /* Save original COR value */
+ reg.Function = 0;
+ reg.Action = CS_READ;
+ reg.Offset = CISREG_COR;
+ CS_CHECK(AccessConfigurationRegister,
+ pcmcia_access_configuration_register(link, &reg));
+ save_cor = reg.Value;
+
+ /* Soft-Reset card */
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ reg.Value = (save_cor | COR_SOFT_RESET);
+ CS_CHECK(AccessConfigurationRegister,
+ pcmcia_access_configuration_register(link, &reg));
+ udelay(1000);
+
+ /* Read CCSR */
+ reg.Action = CS_READ;
+ reg.Offset = CISREG_CCSR;
+ CS_CHECK(AccessConfigurationRegister,
+ pcmcia_access_configuration_register(link, &reg));
+
+ /*
+ * Start or stop the firmware. Memory width bit should be
+ * preserved from the value we've just read.
+ */
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_CCSR;
+ reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16);
+ CS_CHECK(AccessConfigurationRegister,
+ pcmcia_access_configuration_register(link, &reg));
+ udelay(1000);
+
+ /* Restore original COR configuration index */
+ reg.Action = CS_WRITE;
+ reg.Offset = CISREG_COR;
+ reg.Value = (save_cor & ~COR_SOFT_RESET);
+ CS_CHECK(AccessConfigurationRegister,
+ pcmcia_access_configuration_register(link, &reg));
+ udelay(1000);
+ return 0;
+
+ cs_failed:
+ cs_error(link, last_fn, last_ret);
+ return -ENODEV;
+}
+
+/********************************************************************/
+/* Device methods */
+/********************************************************************/
+
+static int
+spectrum_cs_hard_reset(struct orinoco_private *priv)
+{
+ struct orinoco_pccard *card = priv->card;
+ struct pcmcia_device *link = card->p_dev;
+
+ /* Soft reset using COR and HCR */
+ spectrum_reset(link, 0);
+
+ return 0;
+}
+
+static int
+spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
+{
+ struct orinoco_pccard *card = priv->card;
+ struct pcmcia_device *link = card->p_dev;
+
+ return spectrum_reset(link, idle);
+}
+
+/********************************************************************/
+/* PCMCIA stuff */
+/********************************************************************/
+
+/*
+ * This creates an "instance" of the driver, allocating local data
+ * structures for one device. The device is registered with Card
+ * Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a card
+ * insertion event. */
+static int
+spectrum_cs_probe(struct pcmcia_device *link)
+{
+ struct net_device *dev;
+ struct orinoco_private *priv;
+ struct orinoco_pccard *card;
+
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ spectrum_cs_hard_reset,
+ spectrum_cs_stop_firmware);
+ if (! dev)
+ return -ENOMEM;
+ priv = netdev_priv(dev);
+ card = priv->card;
+
+ /* Link both structures together */
+ card->p_dev = link;
+ link->priv = dev;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = orinoco_interrupt;
+ link->irq.Instance = dev;
+
+ /* General socket configuration defaults can go here. In this
+ * client, we assume very little, and rely on the CIS for
+ * almost everything. In most clients, many details (i.e.,
+ * number, sizes, and attributes of IO windows) are fixed by
+ * the nature of the device, and can be hard-wired here. */
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return spectrum_cs_config(link);
+} /* spectrum_cs_attach */
+
+/*
+ * This deletes a driver "instance". The device is de-registered with
+ * Card Services. If it has been released, all local data structures
+ * are freed. Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void spectrum_cs_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->dev_node)
+ unregister_netdev(dev);
+
+ spectrum_cs_release(link);
+
+ free_orinocodev(dev);
+} /* spectrum_cs_detach */
+
+/*
+ * spectrum_cs_config() is scheduled to run after a CARD_INSERTION
+ * event is received, to configure the PCMCIA socket, and to make the
+ * device available to the system.
+ */
+
+static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ goto next_entry;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n",
+ __func__, vcc,
+ cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "%s: Vcc mismatch (vcc = %d, CIS = %d)\n",
+ __func__, vcc,
+ dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+ return 0;
+
+next_entry:
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+};
+
+static int
+spectrum_cs_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_pccard *card = priv->card;
+ hermes_t *hw = &priv->hw;
+ int last_fn, last_ret;
+ void __iomem *mem;
+
+ /*
+ * In this loop, we scan the CIS for configuration table
+ * entries, each of which describes a valid card
+ * configuration, including voltage, IO window, memory window,
+ * and interrupt settings.
+ *
+ * We make no assumptions about the card to be configured: we
+ * use just the information available in the CIS. In an ideal
+ * world, this would work for any PCMCIA card, but it requires
+ * a complete and accurate CIS. In practice, a driver usually
+ * "knows" most of these things without consulting the CIS,
+ * and most client drivers will only use the CIS to fill in
+ * implementation-defined details.
+ */
+ last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
+ printk(KERN_ERR PFX "GetNextTuple(): No matching "
+ "CIS configuration. Maybe you need the "
+ "ignore_cis_vcc=1 parameter.\n");
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
+ }
+
+ /*
+ * Allocate an interrupt line. Note that this does not assign
+ * a handler to the interrupt, unless the 'Handler' member of
+ * the irq structure is initialized.
+ */
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+
+ /* We initialize the hermes structure before completing PCMCIA
+ * configuration just in case the interrupt handler gets
+ * called. */
+ mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
+ if (!mem)
+ goto cs_failed;
+
+ hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping, and putting the
+ * card and host interface into "Memory and IO" mode.
+ */
+ CS_CHECK(RequestConfiguration,
+ pcmcia_request_configuration(link, &link->conf));
+
+ /* Ok, we have the configuration, prepare to register the netdev */
+ dev->base_addr = link->io.BasePort1;
+ dev->irq = link->irq.AssignedIRQ;
+ card->node.major = card->node.minor = 0;
+
+ /* Reset card */
+ if (spectrum_cs_hard_reset(priv) != 0) {
+ goto failed;
+ }
+
+ SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ /* Tell the stack we exist */
+ if (register_netdev(dev) != 0) {
+ printk(KERN_ERR PFX "register_netdev() failed\n");
+ goto failed;
+ }
+
+ /* At this point, the dev_node_t structure(s) needs to be
+ * initialized and arranged in a linked list at link->dev_node. */
+ strcpy(card->node.dev_name, dev->name);
+ link->dev_node = &card->node; /* link->dev_node being non-NULL is also
+ used to indicate that the
+ net_device has been registered */
+
+ /* Finally, report what we've done */
+ printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io "
+ "0x%04x-0x%04x\n", dev->name, dev_name(dev->dev.parent),
+ link->irq.AssignedIRQ, link->io.BasePort1,
+ link->io.BasePort1 + link->io.NumPorts1 - 1);
+
+ return 0;
+
+ cs_failed:
+ cs_error(link, last_fn, last_ret);
+
+ failed:
+ spectrum_cs_release(link);
+ return -ENODEV;
+} /* spectrum_cs_config */
+
+/*
+ * After a card is removed, spectrum_cs_release() will unregister the
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+spectrum_cs_release(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* We're committed to taking the device away now, so mark the
+ * hardware as unavailable */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->hw_unavailable++;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ pcmcia_disable_device(link);
+ if (priv->hw.iobase)
+ ioport_unmap(priv->hw.iobase);
+} /* spectrum_cs_release */
+
+
+static int
+spectrum_cs_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ /* Mark the device as stopped, to block IO until later */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ err = __orinoco_down(dev);
+ if (err)
+ printk(KERN_WARNING "%s: Error %d downing interface\n",
+ dev->name, err);
+
+ netif_device_detach(dev);
+ priv->hw_unavailable++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return err;
+}
+
+static int
+spectrum_cs_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err;
+
+ err = orinoco_reinit_firmware(dev);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
+ dev->name, err);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ netif_device_attach(dev);
+ priv->hw_unavailable--;
+
+ if (priv->open && !priv->hw_unavailable) {
+ err = __orinoco_up(dev);
+ if (err)
+ printk(KERN_ERR "%s: Error %d restarting card\n",
+ dev->name, err);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+
+/********************************************************************/
+/* Module initialization */
+/********************************************************************/
+
+/* Can't be declared "const" or the whole __initdata section will
+ * become const */
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+ " (Pavel Roskin <proski@gnu.org>,"
+ " David Gibson <hermes@gibson.dropbear.id.au>, et al)";
+
+static struct pcmcia_device_id spectrum_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
+ PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
+ PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
+
+static struct pcmcia_driver orinoco_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = DRIVER_NAME,
+ },
+ .probe = spectrum_cs_probe,
+ .remove = spectrum_cs_detach,
+ .suspend = spectrum_cs_suspend,
+ .resume = spectrum_cs_resume,
+ .id_table = spectrum_cs_ids,
+};
+
+static int __init
+init_spectrum_cs(void)
+{
+ printk(KERN_DEBUG "%s\n", version);
+
+ return pcmcia_register_driver(&orinoco_driver);
+}
+
+static void __exit
+exit_spectrum_cs(void)
+{
+ pcmcia_unregister_driver(&orinoco_driver);
+}
+
+module_init(init_spectrum_cs);
+module_exit(exit_spectrum_cs);
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
deleted file mode 100644
index 6fcf2bda7cdf..000000000000
--- a/drivers/net/wireless/orinoco_cs.c
+++ /dev/null
@@ -1,533 +0,0 @@
-/* orinoco_cs.c (formerly known as dldwd_cs.c)
- *
- * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
- * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
- * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others).
- * It should also be usable on various Prism II based cards such as the
- * Linksys, D-Link and Farallon Skyline. It should also work on Symbol
- * cards such as the 3Com AirConnect and Ericsson WLAN.
- *
- * Copyright notice & release notes in file orinoco.c
- */
-
-#define DRIVER_NAME "orinoco_cs"
-#define PFX DRIVER_NAME ": "
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-
-#include "orinoco.h"
-
-/********************************************************************/
-/* Module stuff */
-/********************************************************************/
-
-MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
-MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
-MODULE_LICENSE("Dual MPL/GPL");
-
-/* Module parameters */
-
-/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
- * don't have any CIS entry for it. This workaround it... */
-static int ignore_cis_vcc; /* = 0 */
-module_param(ignore_cis_vcc, int, 0);
-MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
-
-/********************************************************************/
-/* Data structures */
-/********************************************************************/
-
-/* PCMCIA specific device information (goes in the card field of
- * struct orinoco_private */
-struct orinoco_pccard {
- struct pcmcia_device *p_dev;
- dev_node_t node;
-
- /* Used to handle hard reset */
- /* yuck, we need this hack to work around the insanity of the
- * PCMCIA layer */
- unsigned long hard_reset_in_progress;
-};
-
-
-/********************************************************************/
-/* Function prototypes */
-/********************************************************************/
-
-static int orinoco_cs_config(struct pcmcia_device *link);
-static void orinoco_cs_release(struct pcmcia_device *link);
-static void orinoco_cs_detach(struct pcmcia_device *p_dev);
-
-/********************************************************************/
-/* Device methods */
-/********************************************************************/
-
-static int
-orinoco_cs_hard_reset(struct orinoco_private *priv)
-{
- struct orinoco_pccard *card = priv->card;
- struct pcmcia_device *link = card->p_dev;
- int err;
-
- /* We need atomic ops here, because we're not holding the lock */
- set_bit(0, &card->hard_reset_in_progress);
-
- err = pcmcia_reset_card(link->socket);
- if (err)
- return err;
-
- msleep(100);
- clear_bit(0, &card->hard_reset_in_progress);
-
- return 0;
-}
-
-/********************************************************************/
-/* PCMCIA stuff */
-/********************************************************************/
-
-/*
- * This creates an "instance" of the driver, allocating local data
- * structures for one device. The device is registered with Card
- * Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a card
- * insertion event. */
-static int
-orinoco_cs_probe(struct pcmcia_device *link)
-{
- struct net_device *dev;
- struct orinoco_private *priv;
- struct orinoco_pccard *card;
-
- dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
- orinoco_cs_hard_reset, NULL);
- if (! dev)
- return -ENOMEM;
- priv = netdev_priv(dev);
- card = priv->card;
-
- /* Link both structures together */
- card->p_dev = link;
- link->priv = dev;
-
- /* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
- link->irq.Handler = orinoco_interrupt;
- link->irq.Instance = dev;
-
- /* General socket configuration defaults can go here. In this
- * client, we assume very little, and rely on the CIS for
- * almost everything. In most clients, many details (i.e.,
- * number, sizes, and attributes of IO windows) are fixed by
- * the nature of the device, and can be hard-wired here. */
- link->conf.Attributes = 0;
- link->conf.IntType = INT_MEMORY_AND_IO;
-
- return orinoco_cs_config(link);
-} /* orinoco_cs_attach */
-
-/*
- * This deletes a driver "instance". The device is de-registered with
- * Card Services. If it has been released, all local data structures
- * are freed. Otherwise, the structures will be freed when the device
- * is released.
- */
-static void orinoco_cs_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->dev_node)
- unregister_netdev(dev);
-
- orinoco_cs_release(link);
-
- free_orinocodev(dev);
-} /* orinoco_cs_detach */
-
-/*
- * orinoco_cs_config() is scheduled to run after a CARD_INSERTION
- * event is received, to configure the PCMCIA socket, and to make the
- * device available to the system.
- */
-
-#define CS_CHECK(fn, ret) do { \
- last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \
- } while (0)
-
-static int orinoco_cs_config_check(struct pcmcia_device *p_dev,
- cistpl_cftable_entry_t *cfg,
- cistpl_cftable_entry_t *dflt,
- unsigned int vcc,
- void *priv_data)
-{
- if (cfg->index == 0)
- goto next_entry;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- p_dev->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
- p_dev->conf.Vpp =
- dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- p_dev->io.BasePort1 = io->win[0].base;
- p_dev->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- p_dev->io.Attributes2 = p_dev->io.Attributes1;
- p_dev->io.BasePort2 = io->win[1].base;
- p_dev->io.NumPorts2 = io->win[1].len;
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
- goto next_entry;
- }
- return 0;
-
-next_entry:
- pcmcia_disable_device(p_dev);
- return -ENODEV;
-};
-
-static int
-orinoco_cs_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_pccard *card = priv->card;
- hermes_t *hw = &priv->hw;
- int last_fn, last_ret;
- void __iomem *mem;
-
- /*
- * In this loop, we scan the CIS for configuration table
- * entries, each of which describes a valid card
- * configuration, including voltage, IO window, memory window,
- * and interrupt settings.
- *
- * We make no assumptions about the card to be configured: we
- * use just the information available in the CIS. In an ideal
- * world, this would work for any PCMCIA card, but it requires
- * a complete and accurate CIS. In practice, a driver usually
- * "knows" most of these things without consulting the CIS,
- * and most client drivers will only use the CIS to fill in
- * implementation-defined details.
- */
- last_ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
- if (last_ret) {
- if (!ignore_cis_vcc)
- printk(KERN_ERR PFX "GetNextTuple(): No matching "
- "CIS configuration. Maybe you need the "
- "ignore_cis_vcc=1 parameter.\n");
- cs_error(link, RequestIO, last_ret);
- goto failed;
- }
-
- /*
- * Allocate an interrupt line. Note that this does not assign
- * a handler to the interrupt, unless the 'Handler' member of
- * the irq structure is initialized.
- */
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
-
- /* We initialize the hermes structure before completing PCMCIA
- * configuration just in case the interrupt handler gets
- * called. */
- mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
- if (!mem)
- goto cs_failed;
-
- hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
-
- /*
- * This actually configures the PCMCIA socket -- setting up
- * the I/O windows and the interrupt mapping, and putting the
- * card and host interface into "Memory and IO" mode.
- */
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
-
- /* Ok, we have the configuration, prepare to register the netdev */
- dev->base_addr = link->io.BasePort1;
- dev->irq = link->irq.AssignedIRQ;
- card->node.major = card->node.minor = 0;
-
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
- /* Tell the stack we exist */
- if (register_netdev(dev) != 0) {
- printk(KERN_ERR PFX "register_netdev() failed\n");
- goto failed;
- }
-
- /* At this point, the dev_node_t structure(s) needs to be
- * initialized and arranged in a linked list at link->dev_node. */
- strcpy(card->node.dev_name, dev->name);
- link->dev_node = &card->node; /* link->dev_node being non-NULL is also
- used to indicate that the
- net_device has been registered */
-
- /* Finally, report what we've done */
- printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io "
- "0x%04x-0x%04x\n", dev->name, dev->dev.parent->bus_id,
- link->irq.AssignedIRQ, link->io.BasePort1,
- link->io.BasePort1 + link->io.NumPorts1 - 1);
- return 0;
-
- cs_failed:
- cs_error(link, last_fn, last_ret);
-
- failed:
- orinoco_cs_release(link);
- return -ENODEV;
-} /* orinoco_cs_config */
-
-/*
- * After a card is removed, orinoco_cs_release() will unregister the
- * device, and release the PCMCIA configuration. If the device is
- * still open, this will be postponed until it is closed.
- */
-static void
-orinoco_cs_release(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- /* We're committed to taking the device away now, so mark the
- * hardware as unavailable */
- spin_lock_irqsave(&priv->lock, flags);
- priv->hw_unavailable++;
- spin_unlock_irqrestore(&priv->lock, flags);
-
- pcmcia_disable_device(link);
- if (priv->hw.iobase)
- ioport_unmap(priv->hw.iobase);
-} /* orinoco_cs_release */
-
-static int orinoco_cs_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_pccard *card = priv->card;
- int err = 0;
- unsigned long flags;
-
- /* This is probably racy, but I can't think of
- a better way, short of rewriting the PCMCIA
- layer to not suck :-( */
- if (! test_bit(0, &card->hard_reset_in_progress)) {
- spin_lock_irqsave(&priv->lock, flags);
-
- err = __orinoco_down(dev);
- if (err)
- printk(KERN_WARNING "%s: Error %d downing interface\n",
- dev->name, err);
-
- netif_device_detach(dev);
- priv->hw_unavailable++;
-
- spin_unlock_irqrestore(&priv->lock, flags);
- }
-
- return 0;
-}
-
-static int orinoco_cs_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_pccard *card = priv->card;
- int err = 0;
- unsigned long flags;
-
- if (! test_bit(0, &card->hard_reset_in_progress)) {
- err = orinoco_reinit_firmware(dev);
- if (err) {
- printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
- dev->name, err);
- return -EIO;
- }
-
- spin_lock_irqsave(&priv->lock, flags);
-
- netif_device_attach(dev);
- priv->hw_unavailable--;
-
- if (priv->open && ! priv->hw_unavailable) {
- err = __orinoco_up(dev);
- if (err)
- printk(KERN_ERR "%s: Error %d restarting card\n",
- dev->name, err);
- }
-
- spin_unlock_irqrestore(&priv->lock, flags);
- }
-
- return err;
-}
-
-
-/********************************************************************/
-/* Module initialization */
-/********************************************************************/
-
-/* Can't be declared "const" or the whole __initdata section will
- * become const */
-static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
- " (David Gibson <hermes@gibson.dropbear.id.au>, "
- "Pavel Roskin <proski@gnu.org>, et al)";
-
-static struct pcmcia_device_id orinoco_cs_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */
- PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */
- PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */
- PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */
- PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */
- PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */
- PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), /* Lucent Orinoco and old Intersil */
- PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */
- PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */
- PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */
- PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */
- PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */
- PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */
- PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */
- PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */
- PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */
- PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */
- PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */
- PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */
- PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */
- PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */
- PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */
- PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */
- PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */
- PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */
- PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9),
- PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3),
- PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5),
- PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2),
- PCMCIA_DEVICE_PROD_ID123("AIRVAST", "IEEE 802.11b Wireless PCMCIA Card", "HFA3863", 0xea569531, 0x4bcb9645, 0x355cb092),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f),
- PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842),
- PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e),
- PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169),
- PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb),
- PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3),
- PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18),
- PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90),
- PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b),
- PCMCIA_DEVICE_PROD_ID123("corega", "WL PCCL-11", "ISL37300P", 0x0a21501a, 0x59868926, 0xc9049a39),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9),
- PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae),
- PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac),
- PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab),
- PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916),
- PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146),
- PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3),
- PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c),
- PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0),
- PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077),
- PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18),
- PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77),
- PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf),
- PCMCIA_DEVICE_PROD_ID123("Intersil", "PRISM Freedom PCMCIA Adapter", "ISL37100P", 0x4b801a17, 0xf222ec2d, 0x630d52b2),
- PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395),
- PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3),
- PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01),
- PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1),
- PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767),
- PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6),
- PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed),
- PCMCIA_DEVICE_PROD_ID123("PCMCIA", "11M WLAN Card v2.5", "ISL37300P", 0x281f1c5d, 0x6e440487, 0xc9049a39),
- PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264),
- PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178),
- PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9),
- PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26),
- PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b),
- PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757),
- PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a),
- PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e),
- PCMCIA_DEVICE_PROD_ID123("The Linksys Group, Inc.", "Instant Wireless Network PC Card", "ISL37300P", 0xa5f472c2, 0x590eb502, 0xc9049a39),
- PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids);
-
-static struct pcmcia_driver orinoco_driver = {
- .owner = THIS_MODULE,
- .drv = {
- .name = DRIVER_NAME,
- },
- .probe = orinoco_cs_probe,
- .remove = orinoco_cs_detach,
- .id_table = orinoco_cs_ids,
- .suspend = orinoco_cs_suspend,
- .resume = orinoco_cs_resume,
-};
-
-static int __init
-init_orinoco_cs(void)
-{
- printk(KERN_DEBUG "%s\n", version);
-
- return pcmcia_register_driver(&orinoco_driver);
-}
-
-static void __exit
-exit_orinoco_cs(void)
-{
- pcmcia_unregister_driver(&orinoco_driver);
-}
-
-module_init(init_orinoco_cs);
-module_exit(exit_orinoco_cs);
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index 1d0704fe146f..ab79e32f0b27 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -14,17 +14,17 @@
* published by the Free Software Foundation.
*/
-enum control_frame_types {
- P54_CONTROL_TYPE_FILTER_SET = 0,
- P54_CONTROL_TYPE_CHANNEL_CHANGE,
- P54_CONTROL_TYPE_FREQDONE,
+enum p54_control_frame_types {
+ P54_CONTROL_TYPE_SETUP = 0,
+ P54_CONTROL_TYPE_SCAN,
+ P54_CONTROL_TYPE_TRAP,
P54_CONTROL_TYPE_DCFINIT,
- P54_CONTROL_TYPE_ENCRYPTION,
+ P54_CONTROL_TYPE_RX_KEYCACHE,
P54_CONTROL_TYPE_TIM,
- P54_CONTROL_TYPE_POWERMGT,
- P54_CONTROL_TYPE_FREEQUEUE,
+ P54_CONTROL_TYPE_PSM,
+ P54_CONTROL_TYPE_TXCANCEL,
P54_CONTROL_TYPE_TXDONE,
- P54_CONTROL_TYPE_PING,
+ P54_CONTROL_TYPE_BURST,
P54_CONTROL_TYPE_STAT_READBACK,
P54_CONTROL_TYPE_BBP,
P54_CONTROL_TYPE_EEPROM_READBACK,
@@ -37,18 +37,44 @@ enum control_frame_types {
P54_CONTROL_TYPE_XBOW_SYNTH_CFG,
P54_CONTROL_TYPE_CCE_QUIET,
P54_CONTROL_TYPE_PSM_STA_UNLOCK,
+ P54_CONTROL_TYPE_PCS,
+ P54_CONTROL_TYPE_BT_BALANCER = 28,
+ P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30,
+ P54_CONTROL_TYPE_ARPTABLE = 31,
+ P54_CONTROL_TYPE_BT_OPTIONS = 35
};
-struct p54_control_hdr {
- __le16 magic1;
+#define P54_HDR_FLAG_CONTROL BIT(15)
+#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0))
+
+struct p54_hdr {
+ __le16 flags;
__le16 len;
__le32 req_id;
- __le16 type; /* enum control_frame_types */
- u8 retry1;
- u8 retry2;
+ __le16 type; /* enum p54_control_frame_types */
+ u8 rts_tries;
+ u8 tries;
u8 data[0];
} __attribute__ ((packed));
+#define FREE_AFTER_TX(skb) \
+ ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
+ flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET))
+
+struct p54_edcf_queue_param {
+ __le16 aifs;
+ __le16 cwmin;
+ __le16 cwmax;
+ __le16 txop;
+} __attribute__ ((packed));
+
+struct p54_rssi_linear_approximation {
+ s16 mul;
+ s16 add;
+ s16 longbow_unkn;
+ s16 longbow_unk2;
+};
+
#define EEPROM_READBACK_LEN 0x3fc
#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000
@@ -59,49 +85,53 @@ struct p54_control_hdr {
#define FW_LM20 0x4c4d3230
struct p54_common {
+ struct ieee80211_hw *hw;
u32 rx_start;
u32 rx_end;
struct sk_buff_head tx_queue;
- void (*tx)(struct ieee80211_hw *dev, struct p54_control_hdr *data,
- size_t len, int free_on_tx);
+ void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
int (*open)(struct ieee80211_hw *dev);
void (*stop)(struct ieee80211_hw *dev);
int mode;
- u16 seqno;
u16 rx_mtu;
u8 headroom;
u8 tailroom;
struct mutex conf_mutex;
u8 mac_addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
- __le16 filter_type;
struct pda_iq_autocal_entry *iq_autocal;
unsigned int iq_autocal_len;
struct pda_channel_output_limit *output_limit;
unsigned int output_limit_len;
struct pda_pa_curve_data *curve_data;
+ struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS];
unsigned int filter_flags;
+ bool use_short_slot;
u16 rxhw;
u8 version;
- u8 rx_antenna;
unsigned int tx_hdr_len;
- void *cached_vdcf;
unsigned int fw_var;
unsigned int fw_interface;
unsigned int output_power;
u32 tsf_low32;
u32 tsf_high32;
+ u64 basic_rate_mask;
+ u16 wakeup_timer;
+ u16 aid;
struct ieee80211_tx_queue_stats tx_stats[8];
+ struct p54_edcf_queue_param qos_params[8];
struct ieee80211_low_level_stats stats;
- struct timer_list stats_timer;
- struct completion stats_comp;
- void *cached_stats;
+ struct delayed_work work;
+ struct sk_buff *cached_beacon;
int noise;
void *eeprom;
struct completion eeprom_comp;
+ u8 privacy_caps;
+ u8 rx_keycache_size;
};
int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);
+void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb);
int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw);
int p54_read_eeprom(struct ieee80211_hw *dev);
struct ieee80211_hw *p54_init_common(size_t priv_data_len);
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
index 827ca0384a4c..82354b974a04 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -1,12 +1,15 @@
-
/*
* Common code for mac80211 Prism54 drivers
*
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -22,6 +25,9 @@
#include "p54.h"
#include "p54common.h"
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Softmac Prism54 common code");
MODULE_LICENSE("GPL");
@@ -152,21 +158,21 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
priv->fw_interface = be32_to_cpup((__be32 *)
bootrec->data);
switch (priv->fw_interface) {
- case FW_FMAC:
- printk(KERN_INFO "p54: FreeMAC firmware\n");
- break;
- case FW_LM20:
- printk(KERN_INFO "p54: LM20 firmware\n");
- break;
case FW_LM86:
- printk(KERN_INFO "p54: LM86 firmware\n");
- break;
- case FW_LM87:
- printk(KERN_INFO "p54: LM87 firmware\n");
+ case FW_LM20:
+ case FW_LM87: {
+ char *iftype = (char *)bootrec->data;
+ printk(KERN_INFO "%s: p54 detected a LM%c%c "
+ "firmware\n",
+ wiphy_name(dev->wiphy),
+ iftype[2], iftype[3]);
break;
+ }
+ case FW_FMAC:
default:
- printk(KERN_INFO "p54: unknown firmware\n");
- break;
+ printk(KERN_ERR "%s: unsupported firmware\n",
+ wiphy_name(dev->wiphy));
+ return -ENODEV;
}
break;
case BR_CODE_COMPONENT_VERSION:
@@ -182,8 +188,10 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
priv->headroom = desc->headroom;
priv->tailroom = desc->tailroom;
+ priv->privacy_caps = desc->privacy_caps;
+ priv->rx_keycache_size = desc->rx_keycache_size;
if (le32_to_cpu(bootrec->len) == 11)
- priv->rx_mtu = le16_to_cpu(bootrec->rx_mtu);
+ priv->rx_mtu = le16_to_cpu(desc->rx_mtu);
else
priv->rx_mtu = (size_t)
0x620 - priv->tx_hdr_len;
@@ -208,18 +216,35 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
}
if (fw_version)
- printk(KERN_INFO "p54: FW rev %s - Softmac protocol %x.%x\n",
- fw_version, priv->fw_var >> 8, priv->fw_var & 0xff);
+ printk(KERN_INFO "%s: FW rev %s - Softmac protocol %x.%x\n",
+ wiphy_name(dev->wiphy), fw_version,
+ priv->fw_var >> 8, priv->fw_var & 0xff);
+
+ if (priv->fw_var < 0x500)
+ printk(KERN_INFO "%s: you are using an obsolete firmware. "
+ "visit http://wireless.kernel.org/en/users/Drivers/p54 "
+ "and grab one for \"kernel >= 2.6.28\"!\n",
+ wiphy_name(dev->wiphy));
if (priv->fw_var >= 0x300) {
/* Firmware supports QoS, use it! */
- priv->tx_stats[4].limit = 3;
- priv->tx_stats[5].limit = 4;
- priv->tx_stats[6].limit = 3;
- priv->tx_stats[7].limit = 1;
+ priv->tx_stats[4].limit = 3; /* AC_VO */
+ priv->tx_stats[5].limit = 4; /* AC_VI */
+ priv->tx_stats[6].limit = 3; /* AC_BE */
+ priv->tx_stats[7].limit = 2; /* AC_BK */
dev->queues = 4;
}
+ if (!modparam_nohwcrypt)
+ printk(KERN_INFO "%s: cryptographic accelerator "
+ "WEP:%s, TKIP:%s, CCMP:%s\n",
+ wiphy_name(dev->wiphy),
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" :
+ "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP |
+ BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no",
+ (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ?
+ "YES" : "no");
+
return 0;
}
EXPORT_SYMBOL_GPL(p54_parse_firmware);
@@ -310,6 +335,36 @@ static const char *p54_rf_chips[] = { "NULL", "Duette3", "Duette2",
"Frisbee", "Xbow", "Longbow", "NULL", "NULL" };
static int p54_init_xbow_synth(struct ieee80211_hw *dev);
+static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len,
+ u16 type)
+{
+ struct p54_common *priv = dev->priv;
+ int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0;
+ int entry_size = sizeof(struct pda_rssi_cal_entry) + offset;
+ int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2;
+ int i;
+
+ if (len != (entry_size * num_entries)) {
+ printk(KERN_ERR "%s: unknown rssi calibration data packing "
+ " type:(%x) len:%d.\n",
+ wiphy_name(dev->wiphy), type, len);
+
+ print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE,
+ data, len);
+
+ printk(KERN_ERR "%s: please report this issue.\n",
+ wiphy_name(dev->wiphy));
+ return;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ struct pda_rssi_cal_entry *cal = data +
+ (offset + i * entry_size);
+ priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul);
+ priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add);
+ }
+}
+
static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
{
struct p54_common *priv = dev->priv;
@@ -320,7 +375,6 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
int err;
u8 *end = (u8 *)eeprom + len;
u16 synth = 0;
- DECLARE_MAC_BUF(mac);
wrap = (struct eeprom_pda_wrap *) eeprom;
entry = (void *)wrap->data + le16_to_cpu(wrap->len);
@@ -377,8 +431,9 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
err = p54_convert_rev1(dev, curve_data);
break;
default:
- printk(KERN_ERR "p54: unknown curve data "
+ printk(KERN_ERR "%s: unknown curve data "
"revision %d\n",
+ wiphy_name(dev->wiphy),
curve_data->cal_method_rev);
err = -ENODEV;
break;
@@ -409,12 +464,40 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
case PDR_HARDWARE_PLATFORM_COMPONENT_ID:
priv->version = *(u8 *)(entry->data + 1);
break;
+ case PDR_RSSI_LINEAR_APPROXIMATION:
+ case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND:
+ case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED:
+ p54_parse_rssical(dev, entry->data, data_len,
+ le16_to_cpu(entry->code));
+ break;
case PDR_END:
/* make it overrun */
entry_len = len;
break;
+ case PDR_MANUFACTURING_PART_NUMBER:
+ case PDR_PDA_VERSION:
+ case PDR_NIC_SERIAL_NUMBER:
+ case PDR_REGULATORY_DOMAIN_LIST:
+ case PDR_TEMPERATURE_TYPE:
+ case PDR_PRISM_PCI_IDENTIFIER:
+ case PDR_COUNTRY_INFORMATION:
+ case PDR_OEM_NAME:
+ case PDR_PRODUCT_NAME:
+ case PDR_UTF8_OEM_NAME:
+ case PDR_UTF8_PRODUCT_NAME:
+ case PDR_COUNTRY_LIST:
+ case PDR_DEFAULT_COUNTRY:
+ case PDR_ANTENNA_GAIN:
+ case PDR_PRISM_INDIGO_PA_CALIBRATION_DATA:
+ case PDR_REGULATORY_POWER_LIMITS:
+ case PDR_RADIATED_TRANSMISSION_CORRECTION:
+ case PDR_PRISM_TX_IQ_CALIBRATION:
+ case PDR_BASEBAND_REGISTERS:
+ case PDR_PER_CHANNEL_BASEBAND_REGISTERS:
+ break;
default:
- printk(KERN_INFO "p54: unknown eeprom code : 0x%x\n",
+ printk(KERN_INFO "%s: unknown eeprom code : 0x%x\n",
+ wiphy_name(dev->wiphy),
le16_to_cpu(entry->code));
break;
}
@@ -424,17 +507,18 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
if (!synth || !priv->iq_autocal || !priv->output_limit ||
!priv->curve_data) {
- printk(KERN_ERR "p54: not all required entries found in eeprom!\n");
+ printk(KERN_ERR "%s: not all required entries found in eeprom!\n",
+ wiphy_name(dev->wiphy));
err = -EINVAL;
goto err;
}
- priv->rxhw = synth & 0x07;
+ priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK;
if (priv->rxhw == 4)
p54_init_xbow_synth(dev);
- if (!(synth & 0x40))
+ if (!(synth & PDR_SYNTH_24_GHZ_DISABLED))
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz;
- if (!(synth & 0x80))
+ if (!(synth & PDR_SYNTH_5_GHZ_DISABLED))
dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz;
if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
@@ -446,9 +530,9 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
SET_IEEE80211_PERM_ADDR(dev, perm_addr);
}
- printk(KERN_INFO "%s: hwaddr %s, MAC:isl38%02x RF:%s\n",
+ printk(KERN_INFO "%s: hwaddr %pM, MAC:isl38%02x RF:%s\n",
wiphy_name(dev->wiphy),
- print_mac(mac, dev->wiphy->perm_addr),
+ dev->wiphy->perm_addr,
priv->version, p54_rf_chips[priv->rxhw]);
return 0;
@@ -469,36 +553,56 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
priv->curve_data = NULL;
}
- printk(KERN_ERR "p54: eeprom parse failed!\n");
+ printk(KERN_ERR "%s: eeprom parse failed!\n",
+ wiphy_name(dev->wiphy));
return err;
}
static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi)
{
- /* TODO: get the rssi_add & rssi_mul data from the eeprom */
- return ((rssi * 0x83) / 64 - 400) / 4;
+ struct p54_common *priv = dev->priv;
+ int band = dev->conf.channel->band;
+
+ return ((rssi * priv->rssical_db[band].mul) / 64 +
+ priv->rssical_db[band].add) / 4;
}
static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
- struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data;
+ struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
struct ieee80211_rx_status rx_status = {0};
u16 freq = le16_to_cpu(hdr->freq);
size_t header_len = sizeof(*hdr);
u32 tsf32;
- if (!(hdr->magic & cpu_to_le16(0x0001))) {
+ /*
+ * If the device is in a unspecified state we have to
+ * ignore all data frames. Else we could end up with a
+ * nasty crash.
+ */
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return 0;
+
+ if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) {
if (priv->filter_flags & FIF_FCSFAIL)
rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
else
return 0;
}
+ if (hdr->decrypt_status == P54_DECRYPT_OK)
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+ if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) ||
+ (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP))
+ rx_status.flag |= RX_FLAG_MMIC_ERROR;
+
rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
rx_status.noise = priv->noise;
/* XX correct? */
rx_status.qual = (100 * hdr->rssi) / 127;
+ if (hdr->rate & 0x10)
+ rx_status.flag |= RX_FLAG_SHORTPRE;
rx_status.rate_idx = (dev->conf.channel->band == IEEE80211_BAND_2GHZ ?
hdr->rate : (hdr->rate - 4)) & 0xf;
rx_status.freq = freq;
@@ -513,7 +617,7 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
rx_status.flag |= RX_FLAG_TSFT;
- if (hdr->magic & cpu_to_le16(0x4000))
+ if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
header_len += hdr->align[0];
skb_pull(skb, header_len);
@@ -521,6 +625,9 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
ieee80211_rx_irqsafe(dev, skb, &rx_status);
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_STATISTICS_UPDATE));
+
return -1;
}
@@ -529,88 +636,197 @@ static void inline p54_wake_free_queues(struct ieee80211_hw *dev)
struct p54_common *priv = dev->priv;
int i;
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return ;
+
for (i = 0; i < dev->queues; i++)
if (priv->tx_stats[i + 4].len < priv->tx_stats[i + 4].limit)
ieee80211_wake_queue(dev, i);
}
+void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54_common *priv = dev->priv;
+ struct ieee80211_tx_info *info;
+ struct memrecord *range;
+ unsigned long flags;
+ u32 freed = 0, last_addr = priv->rx_start;
+
+ if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue)))
+ return;
+
+ /*
+ * don't try to free an already unlinked skb
+ */
+ if (unlikely((!skb->next) || (!skb->prev)))
+ return;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ info = IEEE80211_SKB_CB(skb);
+ range = (void *)info->rate_driver_data;
+ if (skb->prev != (struct sk_buff *)&priv->tx_queue) {
+ struct ieee80211_tx_info *ni;
+ struct memrecord *mr;
+
+ ni = IEEE80211_SKB_CB(skb->prev);
+ mr = (struct memrecord *)ni->rate_driver_data;
+ last_addr = mr->end_addr;
+ }
+ if (skb->next != (struct sk_buff *)&priv->tx_queue) {
+ struct ieee80211_tx_info *ni;
+ struct memrecord *mr;
+
+ ni = IEEE80211_SKB_CB(skb->next);
+ mr = (struct memrecord *)ni->rate_driver_data;
+ freed = mr->start_addr - last_addr;
+ } else
+ freed = priv->rx_end - last_addr;
+ __skb_unlink(skb, &priv->tx_queue);
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ dev_kfree_skb_any(skb);
+
+ if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 +
+ IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
+ p54_wake_free_queues(dev);
+}
+EXPORT_SYMBOL_GPL(p54_free_skb);
+
+static struct sk_buff *p54_find_tx_entry(struct ieee80211_hw *dev,
+ __le32 req_id)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *entry = priv->tx_queue.next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_queue.lock, flags);
+ while (entry != (struct sk_buff *)&priv->tx_queue) {
+ struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
+
+ if (hdr->req_id == req_id) {
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return entry;
+ }
+ entry = entry->next;
+ }
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return NULL;
+}
+
static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
- struct p54_frame_sent_hdr *payload = (struct p54_frame_sent_hdr *) hdr->data;
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data;
struct sk_buff *entry = (struct sk_buff *) priv->tx_queue.next;
u32 addr = le32_to_cpu(hdr->req_id) - priv->headroom;
struct memrecord *range = NULL;
u32 freed = 0;
u32 last_addr = priv->rx_start;
unsigned long flags;
+ int count, idx;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
while (entry != (struct sk_buff *)&priv->tx_queue) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(entry);
- range = (void *)info->driver_data;
- if (range->start_addr == addr) {
- struct p54_control_hdr *entry_hdr;
- struct p54_tx_control_allocdata *entry_data;
- int pad = 0;
-
- if (entry->next != (struct sk_buff *)&priv->tx_queue) {
- struct ieee80211_tx_info *ni;
- struct memrecord *mr;
-
- ni = IEEE80211_SKB_CB(entry->next);
- mr = (struct memrecord *)ni->driver_data;
- freed = mr->start_addr - last_addr;
- } else
- freed = priv->rx_end - last_addr;
+ struct p54_hdr *entry_hdr;
+ struct p54_tx_data *entry_data;
+ int pad = 0;
+ range = (void *)info->rate_driver_data;
+ if (range->start_addr != addr) {
last_addr = range->end_addr;
- __skb_unlink(entry, &priv->tx_queue);
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ entry = entry->next;
+ continue;
+ }
- memset(&info->status, 0, sizeof(info->status));
- entry_hdr = (struct p54_control_hdr *) entry->data;
- entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data;
- if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0)
- pad = entry_data->align[0];
-
- priv->tx_stats[entry_data->hw_queue].len--;
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
- if (!(payload->status & 0x01))
- info->flags |= IEEE80211_TX_STAT_ACK;
- else
- info->status.excessive_retries = 1;
- }
- info->status.retry_count = payload->retries - 1;
- info->status.ack_signal = p54_rssi_to_dbm(dev,
- le16_to_cpu(payload->ack_rssi));
- skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
- ieee80211_tx_status_irqsafe(dev, entry);
- goto out;
+ if (entry->next != (struct sk_buff *)&priv->tx_queue) {
+ struct ieee80211_tx_info *ni;
+ struct memrecord *mr;
+
+ ni = IEEE80211_SKB_CB(entry->next);
+ mr = (struct memrecord *)ni->rate_driver_data;
+ freed = mr->start_addr - last_addr;
} else
- last_addr = range->end_addr;
- entry = entry->next;
+ freed = priv->rx_end - last_addr;
+
+ last_addr = range->end_addr;
+ __skb_unlink(entry, &priv->tx_queue);
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+
+ entry_hdr = (struct p54_hdr *) entry->data;
+ entry_data = (struct p54_tx_data *) entry_hdr->data;
+ priv->tx_stats[entry_data->hw_queue].len--;
+ priv->stats.dot11ACKFailureCount += payload->tries - 1;
+
+ if (unlikely(entry == priv->cached_beacon)) {
+ kfree_skb(entry);
+ priv->cached_beacon = NULL;
+ goto out;
+ }
+
+ /*
+ * Clear manually, ieee80211_tx_info_clear_status would
+ * clear the counts too and we need them.
+ */
+ memset(&info->status.ampdu_ack_len, 0,
+ sizeof(struct ieee80211_tx_info) -
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info,
+ status.ampdu_ack_len) != 23);
+
+ if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
+ pad = entry_data->align[0];
+
+ /* walk through the rates array and adjust the counts */
+ count = payload->tries;
+ for (idx = 0; idx < 4; idx++) {
+ if (count >= info->status.rates[idx].count) {
+ count -= info->status.rates[idx].count;
+ } else if (count > 0) {
+ info->status.rates[idx].count = count;
+ count = 0;
+ } else {
+ info->status.rates[idx].idx = -1;
+ info->status.rates[idx].count = 0;
+ }
+ }
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (!payload->status))
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ if (payload->status & P54_TX_PSM_CANCELLED)
+ info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+ info->status.ack_signal = p54_rssi_to_dbm(dev,
+ (int)payload->ack_rssi);
+ skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
+ ieee80211_tx_status_irqsafe(dev, entry);
+ goto out;
}
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
out:
- if (freed >= IEEE80211_MAX_RTS_THRESHOLD + 0x170 +
- sizeof(struct p54_control_hdr))
+ if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 +
+ IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
p54_wake_free_queues(dev);
}
static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
struct sk_buff *skb)
{
- struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data;
struct p54_common *priv = dev->priv;
if (!priv->eeprom)
return ;
- memcpy(priv->eeprom, eeprom->data, le16_to_cpu(eeprom->len));
+ if (priv->fw_var >= 0x509) {
+ memcpy(priv->eeprom, eeprom->v2.data,
+ le16_to_cpu(eeprom->v2.len));
+ } else {
+ memcpy(priv->eeprom, eeprom->v1.data,
+ le16_to_cpu(eeprom->v1.len));
+ }
complete(&priv->eeprom_comp);
}
@@ -618,10 +834,14 @@ static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
- u32 tsf32 = le32_to_cpu(stats->tsf32);
+ u32 tsf32;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
+ tsf32 = le32_to_cpu(stats->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
priv->tsf_low32 = tsf32;
@@ -631,19 +851,50 @@ static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
priv->noise = p54_rssi_to_dbm(dev, le32_to_cpu(stats->noise));
- complete(&priv->stats_comp);
- mod_timer(&priv->stats_timer, jiffies + 5 * HZ);
+ p54_free_skb(dev, p54_find_tx_entry(dev, hdr->req_id));
+}
+
+static void p54_rx_trap(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
+ struct p54_trap *trap = (struct p54_trap *) hdr->data;
+ u16 event = le16_to_cpu(trap->event);
+ u16 freq = le16_to_cpu(trap->frequency);
+
+ switch (event) {
+ case P54_TRAP_BEACON_TX:
+ break;
+ case P54_TRAP_RADAR:
+ printk(KERN_INFO "%s: radar (freq:%d MHz)\n",
+ wiphy_name(dev->wiphy), freq);
+ break;
+ case P54_TRAP_NO_BEACON:
+ break;
+ case P54_TRAP_SCAN:
+ break;
+ case P54_TRAP_TBTT:
+ break;
+ case P54_TRAP_TIMER:
+ break;
+ default:
+ printk(KERN_INFO "%s: received event:%x freq:%d\n",
+ wiphy_name(dev->wiphy), event, freq);
+ break;
+ }
}
static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
{
- struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data;
+ struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
switch (le16_to_cpu(hdr->type)) {
case P54_CONTROL_TYPE_TXDONE:
p54_rx_frame_sent(dev, skb);
break;
+ case P54_CONTROL_TYPE_TRAP:
+ p54_rx_trap(dev, skb);
+ break;
case P54_CONTROL_TYPE_BBP:
break;
case P54_CONTROL_TYPE_STAT_READBACK:
@@ -664,9 +915,9 @@ static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb)
/* returns zero if skb can be reused */
int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
- u8 type = le16_to_cpu(*((__le16 *)skb->data)) >> 8;
+ u16 type = le16_to_cpu(*((__le16 *)skb->data));
- if (type == 0x80)
+ if (type & P54_HDR_FLAG_CONTROL)
return p54_rx_control(dev, skb);
else
return p54_rx_data(dev, skb);
@@ -682,12 +933,14 @@ EXPORT_SYMBOL_GPL(p54_rx);
* marks allocated areas as reserved if necessary. p54_rx_frame_sent frees
* allocated areas.
*/
-static void p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
- struct p54_control_hdr *data, u32 len)
+static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
+ struct p54_hdr *data, u32 len)
{
struct p54_common *priv = dev->priv;
struct sk_buff *entry = priv->tx_queue.next;
struct sk_buff *target_skb = NULL;
+ struct ieee80211_tx_info *info;
+ struct memrecord *range;
u32 last_addr = priv->rx_start;
u32 largest_hole = 0;
u32 target_addr = priv->rx_start;
@@ -695,12 +948,37 @@ static void p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
unsigned int left;
len = (len + priv->headroom + priv->tailroom + 3) & ~0x3;
+ if (!skb)
+ return -EINVAL;
+
spin_lock_irqsave(&priv->tx_queue.lock, flags);
+
left = skb_queue_len(&priv->tx_queue);
+ if (unlikely(left >= 28)) {
+ /*
+ * The tx_queue is nearly full!
+ * We have throttle normal data traffic, because we must
+ * have a few spare slots for control frames left.
+ */
+ ieee80211_stop_queues(dev);
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_TX_TIMEOUT));
+
+ if (unlikely(left == 32)) {
+ /*
+ * The tx_queue is now really full.
+ *
+ * TODO: check if the device has crashed and reset it.
+ */
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ return -ENOSPC;
+ }
+ }
+
while (left--) {
u32 hole_size;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(entry);
- struct memrecord *range = (void *)info->driver_data;
+ info = IEEE80211_SKB_CB(entry);
+ range = (void *)info->rate_driver_data;
hole_size = range->start_addr - last_addr;
if (!target_skb && hole_size >= len) {
target_skb = entry->prev;
@@ -715,64 +993,102 @@ static void p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
target_skb = priv->tx_queue.prev;
largest_hole = max(largest_hole, priv->rx_end - last_addr - len);
if (!skb_queue_empty(&priv->tx_queue)) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(target_skb);
- struct memrecord *range = (void *)info->driver_data;
+ info = IEEE80211_SKB_CB(target_skb);
+ range = (void *)info->rate_driver_data;
target_addr = range->end_addr;
}
} else
largest_hole = max(largest_hole, priv->rx_end - last_addr);
- if (skb) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct memrecord *range = (void *)info->driver_data;
- range->start_addr = target_addr;
- range->end_addr = target_addr + len;
- __skb_queue_after(&priv->tx_queue, target_skb, skb);
- if (largest_hole < priv->rx_mtu + priv->headroom +
- priv->tailroom +
- sizeof(struct p54_control_hdr))
- ieee80211_stop_queues(dev);
+ if (!target_skb) {
+ spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ ieee80211_stop_queues(dev);
+ return -ENOSPC;
}
+
+ info = IEEE80211_SKB_CB(skb);
+ range = (void *)info->rate_driver_data;
+ range->start_addr = target_addr;
+ range->end_addr = target_addr + len;
+ __skb_queue_after(&priv->tx_queue, target_skb, skb);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
+ if (largest_hole < priv->headroom + sizeof(struct p54_hdr) +
+ 48 + IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
+ ieee80211_stop_queues(dev);
+
data->req_id = cpu_to_le32(target_addr + priv->headroom);
+ return 0;
+}
+
+static struct sk_buff *p54_alloc_skb(struct ieee80211_hw *dev,
+ u16 hdr_flags, u16 len, u16 type, gfp_t memflags)
+{
+ struct p54_common *priv = dev->priv;
+ struct p54_hdr *hdr;
+ struct sk_buff *skb;
+
+ skb = __dev_alloc_skb(len + priv->tx_hdr_len, memflags);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, priv->tx_hdr_len);
+
+ hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr));
+ hdr->flags = cpu_to_le16(hdr_flags);
+ hdr->len = cpu_to_le16(len - sizeof(*hdr));
+ hdr->type = cpu_to_le16(type);
+ hdr->tries = hdr->rts_tries = 0;
+
+ if (unlikely(p54_assign_address(dev, skb, hdr, len))) {
+ kfree_skb(skb);
+ return NULL;
+ }
+ return skb;
}
int p54_read_eeprom(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr = NULL;
+ struct p54_hdr *hdr = NULL;
struct p54_eeprom_lm86 *eeprom_hdr;
- size_t eeprom_size = 0x2020, offset = 0, blocksize;
+ struct sk_buff *skb;
+ size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize;
int ret = -ENOMEM;
void *eeprom = NULL;
- hdr = (struct p54_control_hdr *)kzalloc(sizeof(*hdr) +
- sizeof(*eeprom_hdr) + EEPROM_READBACK_LEN, GFP_KERNEL);
- if (!hdr)
- goto free;
+ maxblocksize = EEPROM_READBACK_LEN;
+ if (priv->fw_var >= 0x509)
+ maxblocksize -= 0xc;
+ else
+ maxblocksize -= 0x4;
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL, sizeof(*hdr) +
+ sizeof(*eeprom_hdr) + maxblocksize,
+ P54_CONTROL_TYPE_EEPROM_READBACK, GFP_KERNEL);
+ if (!skb)
+ goto free;
priv->eeprom = kzalloc(EEPROM_READBACK_LEN, GFP_KERNEL);
if (!priv->eeprom)
goto free;
-
eeprom = kzalloc(eeprom_size, GFP_KERNEL);
if (!eeprom)
goto free;
- hdr->magic1 = cpu_to_le16(0x8000);
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_EEPROM_READBACK);
- hdr->retry1 = hdr->retry2 = 0;
- eeprom_hdr = (struct p54_eeprom_lm86 *) hdr->data;
+ eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb,
+ sizeof(*eeprom_hdr) + maxblocksize);
while (eeprom_size) {
- blocksize = min(eeprom_size, (size_t)EEPROM_READBACK_LEN);
- hdr->len = cpu_to_le16(blocksize + sizeof(*eeprom_hdr));
- eeprom_hdr->offset = cpu_to_le16(offset);
- eeprom_hdr->len = cpu_to_le16(blocksize);
- p54_assign_address(dev, NULL, hdr, le16_to_cpu(hdr->len) +
- sizeof(*hdr));
- priv->tx(dev, hdr, le16_to_cpu(hdr->len) + sizeof(*hdr), 0);
+ blocksize = min(eeprom_size, maxblocksize);
+ if (priv->fw_var < 0x509) {
+ eeprom_hdr->v1.offset = cpu_to_le16(offset);
+ eeprom_hdr->v1.len = cpu_to_le16(blocksize);
+ } else {
+ eeprom_hdr->v2.offset = cpu_to_le32(offset);
+ eeprom_hdr->v2.len = cpu_to_le16(blocksize);
+ eeprom_hdr->v2.magic2 = 0xf;
+ memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4);
+ }
+ priv->tx(dev, skb);
if (!wait_for_completion_interruptible_timeout(&priv->eeprom_comp, HZ)) {
printk(KERN_ERR "%s: device does not respond!\n",
@@ -790,166 +1106,422 @@ int p54_read_eeprom(struct ieee80211_hw *dev)
free:
kfree(priv->eeprom);
priv->eeprom = NULL;
- kfree(hdr);
+ p54_free_skb(dev, skb);
kfree(eeprom);
return ret;
}
EXPORT_SYMBOL_GPL(p54_read_eeprom);
+static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *skb;
+ struct p54_tim *tim;
+
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET,
+ sizeof(struct p54_hdr) + sizeof(*tim),
+ P54_CONTROL_TYPE_TIM, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ tim = (struct p54_tim *) skb_put(skb, sizeof(*tim));
+ tim->count = 1;
+ tim->entry[0] = cpu_to_le16(set ? (sta->aid | 0x8000) : sta->aid);
+ priv->tx(dev, skb);
+ return 0;
+}
+
+static int p54_sta_unlock(struct ieee80211_hw *dev, u8 *addr)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *skb;
+ struct p54_sta_unlock *sta;
+
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET,
+ sizeof(struct p54_hdr) + sizeof(*sta),
+ P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta));
+ memcpy(sta->addr, addr, ETH_ALEN);
+ priv->tx(dev, skb);
+ return 0;
+}
+
+static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ switch (notify_cmd) {
+ case STA_NOTIFY_ADD:
+ case STA_NOTIFY_REMOVE:
+ /*
+ * Notify the firmware that we don't want or we don't
+ * need to buffer frames for this station anymore.
+ */
+
+ p54_sta_unlock(dev, sta->addr);
+ break;
+ case STA_NOTIFY_AWAKE:
+ /* update the firmware's filter table */
+ p54_sta_unlock(dev, sta->addr);
+ break;
+ default:
+ break;
+ }
+}
+
+static int p54_tx_cancel(struct ieee80211_hw *dev, struct sk_buff *entry)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *skb;
+ struct p54_hdr *hdr;
+ struct p54_txcancel *cancel;
+
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET,
+ sizeof(struct p54_hdr) + sizeof(*cancel),
+ P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (void *)entry->data;
+ cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel));
+ cancel->req_id = hdr->req_id;
+ priv->tx(dev, skb);
+ return 0;
+}
+
+static int p54_tx_fill(struct ieee80211_hw *dev, struct sk_buff *skb,
+ struct ieee80211_tx_info *info, u8 *queue, size_t *extra_len,
+ u16 *flags, u16 *aid)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct p54_common *priv = dev->priv;
+ int ret = 0;
+
+ if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) {
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ *aid = 0;
+ *queue = 0;
+ *extra_len = IEEE80211_MAX_TIM_LEN;
+ *flags = P54_HDR_FLAG_DATA_OUT_TIMESTAMP;
+ return 0;
+ } else if (ieee80211_is_probe_resp(hdr->frame_control)) {
+ *aid = 0;
+ *queue = 2;
+ *flags = P54_HDR_FLAG_DATA_OUT_TIMESTAMP |
+ P54_HDR_FLAG_DATA_OUT_NOCANCEL;
+ return 0;
+ } else {
+ *queue = 2;
+ ret = 0;
+ }
+ } else {
+ *queue += 4;
+ ret = 1;
+ }
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ *aid = 1;
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+ *aid = 0;
+ *queue = 3;
+ return 0;
+ }
+ if (info->control.sta)
+ *aid = info->control.sta->aid;
+ else
+ *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
+ }
+ return ret;
+}
+
+static u8 p54_convert_algo(enum ieee80211_key_alg alg)
+{
+ switch (alg) {
+ case ALG_WEP:
+ return P54_CRYPTO_WEP;
+ case ALG_TKIP:
+ return P54_CRYPTO_TKIPMICHAEL;
+ case ALG_CCMP:
+ return P54_CRYPTO_AESCCMP;
+ default:
+ return 0;
+ }
+}
+
static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_queue_stats *current_queue;
+ struct ieee80211_tx_queue_stats *current_queue = NULL;
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
- struct p54_tx_control_allocdata *txhdr;
- size_t padding, len;
- u8 rate;
+ struct p54_hdr *hdr;
+ struct p54_tx_data *txhdr;
+ size_t padding, len, tim_len = 0;
+ int i, j, ridx, ret;
+ u16 hdr_flags = 0, aid = 0;
+ u8 rate, queue, crypt_offset = 0;
u8 cts_rate = 0x20;
+ u8 rc_flags;
+ u8 calculated_tries[4];
+ u8 nrates = 0, nremaining = 8;
- current_queue = &priv->tx_stats[skb_get_queue_mapping(skb) + 4];
- if (unlikely(current_queue->len > current_queue->limit))
+ queue = skb_get_queue_mapping(skb);
+
+ ret = p54_tx_fill(dev, skb, info, &queue, &tim_len, &hdr_flags, &aid);
+ current_queue = &priv->tx_stats[queue];
+ if (unlikely((current_queue->len > current_queue->limit) && ret))
return NETDEV_TX_BUSY;
current_queue->len++;
current_queue->count++;
- if (current_queue->len == current_queue->limit)
+ if ((current_queue->len == current_queue->limit) && ret)
ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
len = skb->len;
- txhdr = (struct p54_tx_control_allocdata *)
- skb_push(skb, sizeof(*txhdr) + padding);
- hdr = (struct p54_control_hdr *) skb_push(skb, sizeof(*hdr));
+ if (info->control.hw_key) {
+ crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
+ if (info->control.hw_key->alg == ALG_TKIP) {
+ u8 *iv = (u8 *)(skb->data + crypt_offset);
+ /*
+ * The firmware excepts that the IV has to have
+ * this special format
+ */
+ iv[1] = iv[0];
+ iv[0] = iv[2];
+ iv[2] = 0;
+ }
+ }
+
+ txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding);
+ hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr));
if (padding)
- hdr->magic1 = cpu_to_le16(0x4010);
- else
- hdr->magic1 = cpu_to_le16(0x0010);
- hdr->len = cpu_to_le16(len);
- hdr->type = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 0 : cpu_to_le16(1);
- hdr->retry1 = hdr->retry2 = info->control.retry_limit;
-
- /* TODO: add support for alternate retry TX rates */
- rate = ieee80211_get_tx_rate(dev, info)->hw_value;
- if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE) {
- rate |= 0x10;
- cts_rate |= 0x10;
+ hdr_flags |= P54_HDR_FLAG_DATA_ALIGN;
+ hdr->type = cpu_to_le16(aid);
+ hdr->rts_tries = info->control.rates[0].count;
+
+ /*
+ * we register the rates in perfect order, and
+ * RTS/CTS won't happen on 5 GHz
+ */
+ cts_rate = info->control.rts_cts_rate_idx;
+
+ memset(&txhdr->rateset, 0, sizeof(txhdr->rateset));
+
+ /* see how many rates got used */
+ for (i = 0; i < 4; i++) {
+ if (info->control.rates[i].idx < 0)
+ break;
+ nrates++;
+ }
+
+ /* limit tries to 8/nrates per rate */
+ for (i = 0; i < nrates; i++) {
+ /*
+ * The magic expression here is equivalent to 8/nrates for
+ * all values that matter, but avoids division and jumps.
+ * Note that nrates can only take the values 1 through 4.
+ */
+ calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1,
+ info->control.rates[i].count);
+ nremaining -= calculated_tries[i];
}
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
- rate |= 0x40;
- cts_rate |= ieee80211_get_rts_cts_rate(dev, info)->hw_value;
- } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
- rate |= 0x20;
- cts_rate |= ieee80211_get_rts_cts_rate(dev, info)->hw_value;
+
+ /* if there are tries left, distribute from back to front */
+ for (i = nrates - 1; nremaining > 0 && i >= 0; i--) {
+ int tmp = info->control.rates[i].count - calculated_tries[i];
+
+ if (tmp <= 0)
+ continue;
+ /* RC requested more tries at this rate */
+
+ tmp = min_t(int, tmp, nremaining);
+ calculated_tries[i] += tmp;
+ nremaining -= tmp;
+ }
+
+ ridx = 0;
+ for (i = 0; i < nrates && ridx < 8; i++) {
+ /* we register the rates in perfect order */
+ rate = info->control.rates[i].idx;
+ if (info->band == IEEE80211_BAND_5GHZ)
+ rate += 4;
+
+ /* store the count we actually calculated for TX status */
+ info->control.rates[i].count = calculated_tries[i];
+
+ rc_flags = info->control.rates[i].flags;
+ if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) {
+ rate |= 0x10;
+ cts_rate |= 0x10;
+ }
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ rate |= 0x40;
+ else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ rate |= 0x20;
+ for (j = 0; j < calculated_tries[i] && ridx < 8; j++) {
+ txhdr->rateset[ridx] = rate;
+ ridx++;
+ }
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+ hdr_flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
+
+ /* TODO: enable bursting */
+ hdr->flags = cpu_to_le16(hdr_flags);
+ hdr->tries = ridx;
+ txhdr->rts_rate_idx = 0;
+ if (info->control.hw_key) {
+ crypt_offset += info->control.hw_key->iv_len;
+ txhdr->key_type = p54_convert_algo(info->control.hw_key->alg);
+ txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
+ memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
+ if (info->control.hw_key->alg == ALG_TKIP) {
+ if (unlikely(skb_tailroom(skb) < 12))
+ goto err;
+ /* reserve space for the MIC key */
+ len += 8;
+ memcpy(skb_put(skb, 8), &(info->control.hw_key->key
+ [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8);
+ }
+ /* reserve some space for ICV */
+ len += info->control.hw_key->icv_len;
+ } else {
+ txhdr->key_type = 0;
+ txhdr->key_len = 0;
}
- memset(txhdr->rateset, rate, 8);
- txhdr->key_type = 0;
- txhdr->key_len = 0;
- txhdr->hw_queue = skb_get_queue_mapping(skb) + 4;
+ txhdr->crypt_offset = crypt_offset;
+ txhdr->hw_queue = queue;
+ if (current_queue)
+ txhdr->backlog = current_queue->len;
+ else
+ txhdr->backlog = 0;
+ memset(txhdr->durations, 0, sizeof(txhdr->durations));
txhdr->tx_antenna = (info->antenna_sel_tx == 0) ?
2 : info->antenna_sel_tx - 1;
txhdr->output_power = priv->output_power;
- txhdr->cts_rate = (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
- 0 : cts_rate;
+ txhdr->cts_rate = cts_rate;
if (padding)
txhdr->align[0] = padding;
- /* FIXME: The sequence that follows is needed for this driver to
- * work with mac80211 since "mac80211: fix TX sequence numbers".
- * As with the temporary code in rt2x00, changes will be needed
- * to get proper sequence numbers on beacons. In addition, this
- * patch places the sequence number in the hardware state, which
- * limits us to a single virtual state.
- */
- if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
- if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
- priv->seqno += 0x10;
- ieee80211hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- ieee80211hdr->seq_ctrl |= cpu_to_le16(priv->seqno);
- }
+ hdr->len = cpu_to_le16(len);
/* modifies skb->cb and with it info, so must be last! */
- p54_assign_address(dev, skb, hdr, skb->len);
+ if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len)))
+ goto err;
+ priv->tx(dev, skb);
+
+ queue_delayed_work(dev->workqueue, &priv->work,
+ msecs_to_jiffies(P54_TX_FRAME_LIFETIME));
- priv->tx(dev, hdr, skb->len, 0);
return 0;
+
+ err:
+ skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding);
+ if (current_queue) {
+ current_queue->len--;
+ current_queue->count--;
+ }
+ return NETDEV_TX_BUSY;
}
-static int p54_set_filter(struct ieee80211_hw *dev, u16 filter_type,
- const u8 *bssid)
+static int p54_setup_mac(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_filter *filter;
- size_t data_len;
+ struct sk_buff *skb;
+ struct p54_setup_mac *setup;
+ u16 mode;
- hdr = kzalloc(sizeof(*hdr) + sizeof(*filter) +
- priv->tx_hdr_len, GFP_ATOMIC);
- if (!hdr)
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_SETUP,
+ GFP_ATOMIC);
+ if (!skb)
return -ENOMEM;
- hdr = (void *)hdr + priv->tx_hdr_len;
-
- filter = (struct p54_tx_control_filter *) hdr->data;
- hdr->magic1 = cpu_to_le16(0x8001);
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_FILTER_SET);
-
- priv->filter_type = filter->filter_type = cpu_to_le16(filter_type);
- memcpy(filter->mac_addr, priv->mac_addr, ETH_ALEN);
- if (!bssid)
- memset(filter->bssid, ~0, ETH_ALEN);
- else
- memcpy(filter->bssid, bssid, ETH_ALEN);
-
- filter->rx_antenna = priv->rx_antenna;
+ setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup));
+ if (dev->conf.radio_enabled) {
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ mode = P54_FILTER_TYPE_STATION;
+ break;
+ case NL80211_IFTYPE_AP:
+ mode = P54_FILTER_TYPE_AP;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ mode = P54_FILTER_TYPE_IBSS;
+ break;
+ default:
+ mode = P54_FILTER_TYPE_NONE;
+ break;
+ }
+ if (priv->filter_flags & FIF_PROMISC_IN_BSS)
+ mode |= P54_FILTER_TYPE_TRANSPARENT;
+ } else
+ mode = P54_FILTER_TYPE_RX_DISABLED;
+ setup->mac_mode = cpu_to_le16(mode);
+ memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN);
+ memcpy(setup->bssid, priv->bssid, ETH_ALEN);
+ setup->rx_antenna = 2; /* automatic */
+ setup->rx_align = 0;
if (priv->fw_var < 0x500) {
- data_len = P54_TX_CONTROL_FILTER_V1_LEN;
- filter->v1.basic_rate_mask = cpu_to_le32(0x15F);
- filter->v1.rx_addr = cpu_to_le32(priv->rx_end);
- filter->v1.max_rx = cpu_to_le16(priv->rx_mtu);
- filter->v1.rxhw = cpu_to_le16(priv->rxhw);
- filter->v1.wakeup_timer = cpu_to_le16(500);
+ setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ memset(setup->v1.rts_rates, 0, 8);
+ setup->v1.rx_addr = cpu_to_le32(priv->rx_end);
+ setup->v1.max_rx = cpu_to_le16(priv->rx_mtu);
+ setup->v1.rxhw = cpu_to_le16(priv->rxhw);
+ setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer);
+ setup->v1.unalloc0 = cpu_to_le16(0);
} else {
- data_len = P54_TX_CONTROL_FILTER_V2_LEN;
- filter->v2.rx_addr = cpu_to_le32(priv->rx_end);
- filter->v2.max_rx = cpu_to_le16(priv->rx_mtu);
- filter->v2.rxhw = cpu_to_le16(priv->rxhw);
- filter->v2.timer = cpu_to_le16(1000);
+ setup->v2.rx_addr = cpu_to_le32(priv->rx_end);
+ setup->v2.max_rx = cpu_to_le16(priv->rx_mtu);
+ setup->v2.rxhw = cpu_to_le16(priv->rxhw);
+ setup->v2.timer = cpu_to_le16(priv->wakeup_timer);
+ setup->v2.truncate = cpu_to_le16(48896);
+ setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ setup->v2.sbss_offset = 0;
+ setup->v2.mcast_window = 0;
+ setup->v2.rx_rssi_threshold = 0;
+ setup->v2.rx_ed_threshold = 0;
+ setup->v2.ref_clock = cpu_to_le32(644245094);
+ setup->v2.lpf_bandwidth = cpu_to_le16(65535);
+ setup->v2.osc_start_delay = cpu_to_le16(65535);
}
-
- hdr->len = cpu_to_le16(data_len);
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + data_len);
- priv->tx(dev, hdr, sizeof(*hdr) + data_len, 1);
+ priv->tx(dev, skb);
return 0;
}
-static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
+static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_channel *chan;
+ struct sk_buff *skb;
+ struct p54_scan *chan;
unsigned int i;
- size_t data_len;
void *entry;
+ __le16 freq = cpu_to_le16(dev->conf.channel->center_freq);
+ int band = dev->conf.channel->band;
- hdr = kzalloc(sizeof(*hdr) + sizeof(*chan) +
- priv->tx_hdr_len, GFP_KERNEL);
- if (!hdr)
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*chan) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_SCAN,
+ GFP_ATOMIC);
+ if (!skb)
return -ENOMEM;
- hdr = (void *)hdr + priv->tx_hdr_len;
-
- chan = (struct p54_tx_control_channel *) hdr->data;
-
- hdr->magic1 = cpu_to_le16(0x8001);
-
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE);
-
- chan->flags = cpu_to_le16(0x1);
- chan->dwell = cpu_to_le16(0x0);
+ chan = (struct p54_scan *) skb_put(skb, sizeof(*chan));
+ memset(chan->padding1, 0, sizeof(chan->padding1));
+ chan->mode = cpu_to_le16(mode);
+ chan->dwell = cpu_to_le16(dwell);
for (i = 0; i < priv->iq_autocal_len; i++) {
if (priv->iq_autocal[i].freq != freq)
@@ -990,61 +1562,50 @@ static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq)
}
entry += sizeof(__le16);
- chan->pa_points_per_curve =
- min(priv->curve_data->points_per_channel, (u8) 8);
-
- memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) *
- chan->pa_points_per_curve);
+ chan->pa_points_per_curve = 8;
+ memset(chan->curve_data, 0, sizeof(*chan->curve_data));
+ memcpy(chan->curve_data, entry,
+ sizeof(struct p54_pa_curve_data_sample) *
+ min((u8)8, priv->curve_data->points_per_channel));
break;
}
if (priv->fw_var < 0x500) {
- data_len = P54_TX_CONTROL_CHANNEL_V1_LEN;
- chan->v1.rssical_mul = cpu_to_le16(130);
- chan->v1.rssical_add = cpu_to_le16(0xfe70);
+ chan->v1_rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
+ chan->v1_rssi.add = cpu_to_le16(priv->rssical_db[band].add);
} else {
- data_len = P54_TX_CONTROL_CHANNEL_V2_LEN;
- chan->v2.rssical_mul = cpu_to_le16(130);
- chan->v2.rssical_add = cpu_to_le16(0xfe70);
- chan->v2.basic_rate_mask = cpu_to_le32(0x15f);
+ chan->v2.rssi.mul = cpu_to_le16(priv->rssical_db[band].mul);
+ chan->v2.rssi.add = cpu_to_le16(priv->rssical_db[band].add);
+ chan->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask);
+ memset(chan->v2.rts_rates, 0, 8);
}
-
- hdr->len = cpu_to_le16(data_len);
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + data_len);
- priv->tx(dev, hdr, sizeof(*hdr) + data_len, 1);
+ priv->tx(dev, skb);
return 0;
err:
printk(KERN_ERR "%s: frequency change failed\n", wiphy_name(dev->wiphy));
- kfree(hdr);
+ kfree_skb(skb);
return -EINVAL;
}
static int p54_set_leds(struct ieee80211_hw *dev, int mode, int link, int act)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_led *led;
+ struct sk_buff *skb;
+ struct p54_led *led;
- hdr = kzalloc(sizeof(*hdr) + sizeof(*led) +
- priv->tx_hdr_len, GFP_KERNEL);
- if (!hdr)
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_LED,
+ GFP_ATOMIC);
+ if (!skb)
return -ENOMEM;
- hdr = (void *)hdr + priv->tx_hdr_len;
- hdr->magic1 = cpu_to_le16(0x8001);
- hdr->len = cpu_to_le16(sizeof(*led));
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_LED);
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*led));
-
- led = (struct p54_tx_control_led *) hdr->data;
+ led = (struct p54_led *)skb_put(skb, sizeof(*led));
led->mode = cpu_to_le16(mode);
led->led_permanent = cpu_to_le16(link);
led->led_temporary = cpu_to_le16(act);
led->duration = cpu_to_le16(1000);
-
- priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*led), 1);
-
+ priv->tx(dev, skb);
return 0;
}
@@ -1056,88 +1617,144 @@ do { \
queue.txop = cpu_to_le16(_txop); \
} while(0)
-static void p54_init_vdcf(struct ieee80211_hw *dev)
+static int p54_set_edcf(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_vdcf *vdcf;
-
- /* all USB V1 adapters need a extra headroom */
- hdr = (void *)priv->cached_vdcf + priv->tx_hdr_len;
- hdr->magic1 = cpu_to_le16(0x8001);
- hdr->len = cpu_to_le16(sizeof(*vdcf));
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_DCFINIT);
- hdr->req_id = cpu_to_le32(priv->rx_start);
-
- vdcf = (struct p54_tx_control_vdcf *) hdr->data;
-
- P54_SET_QUEUE(vdcf->queue[0], 0x0002, 0x0003, 0x0007, 47);
- P54_SET_QUEUE(vdcf->queue[1], 0x0002, 0x0007, 0x000f, 94);
- P54_SET_QUEUE(vdcf->queue[2], 0x0003, 0x000f, 0x03ff, 0);
- P54_SET_QUEUE(vdcf->queue[3], 0x0007, 0x000f, 0x03ff, 0);
+ struct sk_buff *skb;
+ struct p54_edcf *edcf;
+
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_DCFINIT,
+ GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf));
+ if (priv->use_short_slot) {
+ edcf->slottime = 9;
+ edcf->sifs = 0x10;
+ edcf->eofpad = 0x00;
+ } else {
+ edcf->slottime = 20;
+ edcf->sifs = 0x0a;
+ edcf->eofpad = 0x06;
+ }
+ /* (see prism54/isl_oid.h for further details) */
+ edcf->frameburst = cpu_to_le16(0);
+ edcf->round_trip_delay = cpu_to_le16(0);
+ edcf->flags = 0;
+ memset(edcf->mapping, 0, sizeof(edcf->mapping));
+ memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue));
+ priv->tx(dev, skb);
+ return 0;
}
-static void p54_set_vdcf(struct ieee80211_hw *dev)
+static int p54_beacon_tim(struct sk_buff *skb)
{
- struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_vdcf *vdcf;
+ /*
+ * the good excuse for this mess is ... the firmware.
+ * The dummy TIM MUST be at the end of the beacon frame,
+ * because it'll be overwritten!
+ */
- hdr = (void *)priv->cached_vdcf + priv->tx_hdr_len;
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ u8 *pos, *end;
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*vdcf));
+ if (skb->len <= sizeof(mgmt))
+ return -EINVAL;
- vdcf = (struct p54_tx_control_vdcf *) hdr->data;
+ pos = (u8 *)mgmt->u.beacon.variable;
+ end = skb->data + skb->len;
+ while (pos < end) {
+ if (pos + 2 + pos[1] > end)
+ return -EINVAL;
- if (dev->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
- vdcf->slottime = 9;
- vdcf->magic1 = 0x10;
- vdcf->magic2 = 0x00;
- } else {
- vdcf->slottime = 20;
- vdcf->magic1 = 0x0a;
- vdcf->magic2 = 0x06;
- }
+ if (pos[0] == WLAN_EID_TIM) {
+ u8 dtim_len = pos[1];
+ u8 dtim_period = pos[3];
+ u8 *next = pos + 2 + dtim_len;
- /* (see prism54/isl_oid.h for further details) */
- vdcf->frameburst = cpu_to_le16(0);
+ if (dtim_len < 3)
+ return -EINVAL;
+
+ memmove(pos, next, end - next);
- priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*vdcf), 0);
+ if (dtim_len > 3)
+ skb_trim(skb, skb->len - (dtim_len - 3));
+
+ pos = end - (dtim_len + 2);
+
+ /* add the dummy at the end */
+ pos[0] = WLAN_EID_TIM;
+ pos[1] = 3;
+ pos[2] = 0;
+ pos[3] = dtim_period;
+ pos[4] = 0;
+ return 0;
+ }
+ pos += 2 + pos[1];
+ }
+ return 0;
}
-static int p54_start(struct ieee80211_hw *dev)
+static int p54_beacon_update(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
{
struct p54_common *priv = dev->priv;
- int err;
-
- if (!priv->cached_vdcf) {
- priv->cached_vdcf = kzalloc(sizeof(struct p54_tx_control_vdcf)+
- priv->tx_hdr_len + sizeof(struct p54_control_hdr),
- GFP_KERNEL);
+ struct sk_buff *beacon;
+ int ret;
- if (!priv->cached_vdcf)
- return -ENOMEM;
+ if (priv->cached_beacon) {
+ p54_tx_cancel(dev, priv->cached_beacon);
+ /* wait for the last beacon the be freed */
+ msleep(10);
}
- if (!priv->cached_stats) {
- priv->cached_stats = kzalloc(sizeof(struct p54_statistics) +
- priv->tx_hdr_len + sizeof(struct p54_control_hdr),
- GFP_KERNEL);
+ beacon = ieee80211_beacon_get(dev, vif);
+ if (!beacon)
+ return -ENOMEM;
+ ret = p54_beacon_tim(beacon);
+ if (ret)
+ return ret;
+ ret = p54_tx(dev, beacon);
+ if (ret)
+ return ret;
+ priv->cached_beacon = beacon;
+ priv->tsf_high32 = 0;
+ priv->tsf_low32 = 0;
- if (!priv->cached_stats) {
- kfree(priv->cached_vdcf);
- priv->cached_vdcf = NULL;
- return -ENOMEM;
- }
- }
+ return 0;
+}
+static int p54_start(struct ieee80211_hw *dev)
+{
+ struct p54_common *priv = dev->priv;
+ int err;
+
+ mutex_lock(&priv->conf_mutex);
err = priv->open(dev);
- if (!err)
- priv->mode = NL80211_IFTYPE_MONITOR;
+ if (err)
+ goto out;
+ P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47);
+ P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94);
+ P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0);
+ P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0);
+ err = p54_set_edcf(dev);
+ if (err)
+ goto out;
+
+ memset(priv->bssid, ~0, ETH_ALEN);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ err = p54_setup_mac(dev);
+ if (err) {
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ goto out;
+ }
- p54_init_vdcf(dev);
+ queue_delayed_work(dev->workqueue, &priv->work, 0);
- mod_timer(&priv->stats_timer, jiffies + HZ);
+out:
+ mutex_unlock(&priv->conf_mutex);
return err;
}
@@ -1146,12 +1763,18 @@ static void p54_stop(struct ieee80211_hw *dev)
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
- del_timer(&priv->stats_timer);
+ mutex_lock(&priv->conf_mutex);
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ cancel_delayed_work_sync(&priv->work);
+ if (priv->cached_beacon)
+ p54_tx_cancel(dev, priv->cached_beacon);
+
+ priv->stop(dev);
while ((skb = skb_dequeue(&priv->tx_queue)))
kfree_skb(skb);
- priv->stop(dev);
+ priv->cached_beacon = NULL;
priv->tsf_high32 = priv->tsf_low32 = 0;
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ mutex_unlock(&priv->conf_mutex);
}
static int p54_add_interface(struct ieee80211_hw *dev,
@@ -1159,32 +1782,28 @@ static int p54_add_interface(struct ieee80211_hw *dev,
{
struct p54_common *priv = dev->priv;
- if (priv->mode != NL80211_IFTYPE_MONITOR)
+ mutex_lock(&priv->conf_mutex);
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
return -EOPNOTSUPP;
+ }
switch (conf->type) {
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
priv->mode = conf->type;
break;
default:
+ mutex_unlock(&priv->conf_mutex);
return -EOPNOTSUPP;
}
memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
-
- p54_set_filter(dev, 0, NULL);
-
- switch (conf->type) {
- case NL80211_IFTYPE_STATION:
- p54_set_filter(dev, 1, NULL);
- break;
- default:
- BUG(); /* impossible */
- break;
- }
-
+ p54_setup_mac(dev);
p54_set_leds(dev, 1, 0, 0);
-
+ mutex_unlock(&priv->conf_mutex);
return 0;
}
@@ -1192,22 +1811,38 @@ static void p54_remove_interface(struct ieee80211_hw *dev,
struct ieee80211_if_init_conf *conf)
{
struct p54_common *priv = dev->priv;
+
+ mutex_lock(&priv->conf_mutex);
+ if (priv->cached_beacon)
+ p54_tx_cancel(dev, priv->cached_beacon);
priv->mode = NL80211_IFTYPE_MONITOR;
memset(priv->mac_addr, 0, ETH_ALEN);
- p54_set_filter(dev, 0, NULL);
+ memset(priv->bssid, 0, ETH_ALEN);
+ p54_setup_mac(dev);
+ mutex_unlock(&priv->conf_mutex);
}
-static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
+static int p54_config(struct ieee80211_hw *dev, u32 changed)
{
int ret;
struct p54_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
mutex_lock(&priv->conf_mutex);
- priv->rx_antenna = (conf->antenna_sel_rx == 0) ?
- 2 : conf->antenna_sel_tx - 1;
- priv->output_power = conf->power_level << 2;
- ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq));
- p54_set_vdcf(dev);
+ if (changed & IEEE80211_CONF_CHANGE_POWER)
+ priv->output_power = conf->power_level << 2;
+ if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) {
+ ret = p54_setup_mac(dev);
+ if (ret)
+ goto out;
+ }
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ ret = p54_scan(dev, P54_SCAN_EXIT, 0);
+ if (ret)
+ goto out;
+ }
+
+out:
mutex_unlock(&priv->conf_mutex);
return ret;
}
@@ -1217,13 +1852,36 @@ static int p54_config_interface(struct ieee80211_hw *dev,
struct ieee80211_if_conf *conf)
{
struct p54_common *priv = dev->priv;
+ int ret = 0;
mutex_lock(&priv->conf_mutex);
- p54_set_filter(dev, 0, conf->bssid);
- p54_set_leds(dev, 1, !is_multicast_ether_addr(conf->bssid), 0);
- memcpy(priv->bssid, conf->bssid, ETH_ALEN);
+ if (conf->changed & IEEE80211_IFCC_BSSID) {
+ memcpy(priv->bssid, conf->bssid, ETH_ALEN);
+ ret = p54_setup_mac(dev);
+ if (ret)
+ goto out;
+ }
+
+ if (conf->changed & IEEE80211_IFCC_BEACON) {
+ ret = p54_scan(dev, P54_SCAN_EXIT, 0);
+ if (ret)
+ goto out;
+ ret = p54_setup_mac(dev);
+ if (ret)
+ goto out;
+ ret = p54_beacon_update(dev, vif);
+ if (ret)
+ goto out;
+ ret = p54_set_edcf(dev);
+ if (ret)
+ goto out;
+ }
+
+ ret = p54_set_leds(dev, 1, !is_multicast_ether_addr(priv->bssid), 0);
+
+out:
mutex_unlock(&priv->conf_mutex);
- return 0;
+ return ret;
}
static void p54_configure_filter(struct ieee80211_hw *dev,
@@ -1233,94 +1891,78 @@ static void p54_configure_filter(struct ieee80211_hw *dev,
{
struct p54_common *priv = dev->priv;
- *total_flags &= FIF_BCN_PRBRESP_PROMISC |
- FIF_PROMISC_IN_BSS |
- FIF_FCSFAIL;
+ *total_flags &= FIF_PROMISC_IN_BSS |
+ (*total_flags & FIF_PROMISC_IN_BSS) ?
+ FIF_FCSFAIL : 0;
priv->filter_flags = *total_flags;
- if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
- if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
- p54_set_filter(dev, le16_to_cpu(priv->filter_type),
- NULL);
- else
- p54_set_filter(dev, le16_to_cpu(priv->filter_type),
- priv->bssid);
- }
-
- if (changed_flags & FIF_PROMISC_IN_BSS) {
- if (*total_flags & FIF_PROMISC_IN_BSS)
- p54_set_filter(dev, le16_to_cpu(priv->filter_type) |
- 0x8, NULL);
- else
- p54_set_filter(dev, le16_to_cpu(priv->filter_type) &
- ~0x8, priv->bssid);
- }
+ if (changed_flags & FIF_PROMISC_IN_BSS)
+ p54_setup_mac(dev);
}
static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct p54_common *priv = dev->priv;
- struct p54_tx_control_vdcf *vdcf;
-
- vdcf = (struct p54_tx_control_vdcf *)(((struct p54_control_hdr *)
- ((void *)priv->cached_vdcf + priv->tx_hdr_len))->data);
+ int ret;
+ mutex_lock(&priv->conf_mutex);
if ((params) && !(queue > 4)) {
- P54_SET_QUEUE(vdcf->queue[queue], params->aifs,
+ P54_SET_QUEUE(priv->qos_params[queue], params->aifs,
params->cw_min, params->cw_max, params->txop);
+ ret = p54_set_edcf(dev);
} else
- return -EINVAL;
-
- p54_set_vdcf(dev);
-
- return 0;
+ ret = -EINVAL;
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
}
static int p54_init_xbow_synth(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_tx_control_xbow_synth *xbow;
+ struct sk_buff *skb;
+ struct p54_xbow_synth *xbow;
- hdr = kzalloc(sizeof(*hdr) + sizeof(*xbow) +
- priv->tx_hdr_len, GFP_KERNEL);
- if (!hdr)
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow) +
+ sizeof(struct p54_hdr),
+ P54_CONTROL_TYPE_XBOW_SYNTH_CFG,
+ GFP_KERNEL);
+ if (!skb)
return -ENOMEM;
- hdr = (void *)hdr + priv->tx_hdr_len;
- hdr->magic1 = cpu_to_le16(0x8001);
- hdr->len = cpu_to_le16(sizeof(*xbow));
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_XBOW_SYNTH_CFG);
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*xbow));
-
- xbow = (struct p54_tx_control_xbow_synth *) hdr->data;
+ xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow));
xbow->magic1 = cpu_to_le16(0x1);
xbow->magic2 = cpu_to_le16(0x2);
xbow->freq = cpu_to_le16(5390);
-
- priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*xbow), 1);
-
+ memset(xbow->padding, 0, sizeof(xbow->padding));
+ priv->tx(dev, skb);
return 0;
}
-static void p54_statistics_timer(unsigned long data)
+static void p54_work(struct work_struct *work)
{
- struct ieee80211_hw *dev = (struct ieee80211_hw *) data;
- struct p54_common *priv = dev->priv;
- struct p54_control_hdr *hdr;
- struct p54_statistics *stats;
+ struct p54_common *priv = container_of(work, struct p54_common,
+ work.work);
+ struct ieee80211_hw *dev = priv->hw;
+ struct sk_buff *skb;
- BUG_ON(!priv->cached_stats);
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+ return ;
- hdr = (void *)priv->cached_stats + priv->tx_hdr_len;
- hdr->magic1 = cpu_to_le16(0x8000);
- hdr->len = cpu_to_le16(sizeof(*stats));
- hdr->type = cpu_to_le16(P54_CONTROL_TYPE_STAT_READBACK);
- p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*stats));
+ /*
+ * TODO: walk through tx_queue and do the following tasks
+ * 1. initiate bursts.
+ * 2. cancel stuck frames / reset the device if necessary.
+ */
+
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL, sizeof(struct p54_hdr) +
+ sizeof(struct p54_statistics),
+ P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL);
+ if (!skb)
+ return ;
- priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*stats), 0);
+ priv->tx(dev, skb);
}
static int p54_get_stats(struct ieee80211_hw *dev,
@@ -1328,17 +1970,7 @@ static int p54_get_stats(struct ieee80211_hw *dev,
{
struct p54_common *priv = dev->priv;
- del_timer(&priv->stats_timer);
- p54_statistics_timer((unsigned long)dev);
-
- if (!wait_for_completion_interruptible_timeout(&priv->stats_comp, HZ)) {
- printk(KERN_ERR "%s: device does not respond!\n",
- wiphy_name(dev->wiphy));
- return -EBUSY;
- }
-
memcpy(stats, &priv->stats, sizeof(*stats));
-
return 0;
}
@@ -1352,14 +1984,133 @@ static int p54_get_tx_stats(struct ieee80211_hw *dev,
return 0;
}
+static void p54_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct p54_common *priv = dev->priv;
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ priv->use_short_slot = info->use_short_slot;
+ p54_set_edcf(dev);
+ }
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ if (dev->conf.channel->band == IEEE80211_BAND_5GHZ)
+ priv->basic_rate_mask = (info->basic_rates << 4);
+ else
+ priv->basic_rate_mask = info->basic_rates;
+ p54_setup_mac(dev);
+ if (priv->fw_var >= 0x500)
+ p54_scan(dev, P54_SCAN_EXIT, 0);
+ }
+ if (changed & BSS_CHANGED_ASSOC) {
+ if (info->assoc) {
+ priv->aid = info->aid;
+ priv->wakeup_timer = info->beacon_int *
+ info->dtim_period * 5;
+ p54_setup_mac(dev);
+ }
+ }
+
+}
+
+static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key)
+{
+ struct p54_common *priv = dev->priv;
+ struct sk_buff *skb;
+ struct p54_keycache *rxkey;
+ u8 algo = 0;
+
+ if (modparam_nohwcrypt)
+ return -EOPNOTSUPP;
+
+ if (cmd == DISABLE_KEY)
+ algo = 0;
+ else {
+ switch (key->alg) {
+ case ALG_TKIP:
+ if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
+ BR_DESC_PRIV_CAP_TKIP)))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_TKIPMICHAEL;
+ break;
+ case ALG_WEP:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_WEP;
+ break;
+ case ALG_CCMP:
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP))
+ return -EOPNOTSUPP;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ algo = P54_CRYPTO_AESCCMP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (key->keyidx > priv->rx_keycache_size) {
+ /*
+ * The device supports the choosen algorithm, but the firmware
+ * does not provide enough key slots to store all of them.
+ * So, incoming frames have to be decoded by the mac80211 stack,
+ * but we can still offload encryption for outgoing frames.
+ */
+
+ return 0;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+ skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey) +
+ sizeof(struct p54_hdr), P54_CONTROL_TYPE_RX_KEYCACHE,
+ GFP_ATOMIC);
+ if (!skb) {
+ mutex_unlock(&priv->conf_mutex);
+ return -ENOMEM;
+ }
+
+ /* TODO: some devices have 4 more free slots for rx keys */
+ rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
+ rxkey->entry = key->keyidx;
+ rxkey->key_id = key->keyidx;
+ rxkey->key_type = algo;
+ if (address)
+ memcpy(rxkey->mac, address, ETH_ALEN);
+ else
+ memset(rxkey->mac, ~0, ETH_ALEN);
+ if (key->alg != ALG_TKIP) {
+ rxkey->key_len = min((u8)16, key->keylen);
+ memcpy(rxkey->key, key->key, rxkey->key_len);
+ } else {
+ rxkey->key_len = 24;
+ memcpy(rxkey->key, key->key, 16);
+ memcpy(&(rxkey->key[16]), &(key->key
+ [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8);
+ }
+
+ priv->tx(dev, skb);
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+}
+
static const struct ieee80211_ops p54_ops = {
.tx = p54_tx,
.start = p54_start,
.stop = p54_stop,
.add_interface = p54_add_interface,
.remove_interface = p54_remove_interface,
+ .set_tim = p54_set_tim,
+ .sta_notify = p54_sta_notify,
+ .set_key = p54_set_key,
.config = p54_config,
.config_interface = p54_config_interface,
+ .bss_info_changed = p54_bss_info_changed,
.configure_filter = p54_configure_filter,
.conf_tx = p54_conf_tx,
.get_stats = p54_get_stats,
@@ -1376,32 +2127,43 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
return NULL;
priv = dev->priv;
+ priv->hw = dev;
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->basic_rate_mask = 0x15f;
skb_queue_head_init(&priv->tx_queue);
- dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */
- IEEE80211_HW_RX_INCLUDES_FCS |
+ dev->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM;
- dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
dev->channel_change_time = 1000; /* TODO: find actual value */
-
- priv->tx_stats[0].limit = 1;
- priv->tx_stats[1].limit = 1;
- priv->tx_stats[2].limit = 1;
- priv->tx_stats[3].limit = 1;
- priv->tx_stats[4].limit = 5;
+ priv->tx_stats[0].limit = 1; /* Beacon queue */
+ priv->tx_stats[1].limit = 1; /* Probe queue for HW scan */
+ priv->tx_stats[2].limit = 3; /* queue for MLMEs */
+ priv->tx_stats[3].limit = 3; /* Broadcast / MC queue */
+ priv->tx_stats[4].limit = 5; /* Data */
dev->queues = 1;
priv->noise = -94;
- dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 +
- sizeof(struct p54_tx_control_allocdata);
+ /*
+ * We support at most 8 tries no matter which rate they're at,
+ * we cannot support max_rates * max_rate_tries as we set it
+ * here, but setting it correctly to 4/2 or so would limit us
+ * artificially if the RC algorithm wants just two rates, so
+ * let's say 4/7, we'll redistribute it at TX time, see the
+ * comments there.
+ */
+ dev->max_rates = 4;
+ dev->max_rate_tries = 7;
+ dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 +
+ sizeof(struct p54_tx_data);
mutex_init(&priv->conf_mutex);
init_completion(&priv->eeprom_comp);
- init_completion(&priv->stats_comp);
- setup_timer(&priv->stats_timer, p54_statistics_timer,
- (unsigned long)dev);
+ INIT_DELAYED_WORK(&priv->work, p54_work);
return dev;
}
@@ -1410,11 +2172,9 @@ EXPORT_SYMBOL_GPL(p54_init_common);
void p54_free_common(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- kfree(priv->cached_stats);
kfree(priv->iq_autocal);
kfree(priv->output_limit);
kfree(priv->curve_data);
- kfree(priv->cached_vdcf);
}
EXPORT_SYMBOL_GPL(p54_free_common);
diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h
index 2fa994cfcfed..f5729de83fe1 100644
--- a/drivers/net/wireless/p54/p54common.h
+++ b/drivers/net/wireless/p54/p54common.h
@@ -7,8 +7,12 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007, Christian Lamparter <chunkeey@web.de>
*
- * Based on the islsm (softmac prism54) driver, which is:
- * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * - LMAC API interface header file for STLC4560 (lmac_longbow.h)
+ * Copyright (C) 2007 Conexant Systems, 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
@@ -19,9 +23,24 @@ struct bootrec {
__le32 code;
__le32 len;
u32 data[10];
- __le16 rx_mtu;
} __attribute__((packed));
+#define PDR_SYNTH_FRONTEND_MASK 0x0007
+#define PDR_SYNTH_IQ_CAL_MASK 0x0018
+#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000
+#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008
+#define PDR_SYNTH_IQ_CAL_ZIF 0x0010
+#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020
+#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0001
+#define PDR_SYNTH_24_GHZ_MASK 0x0040
+#define PDR_SYNTH_24_GHZ_DISABLED 0x0040
+#define PDR_SYNTH_5_GHZ_MASK 0x0080
+#define PDR_SYNTH_5_GHZ_DISABLED 0x0080
+#define PDR_SYNTH_RX_DIV_MASK 0x0100
+#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100
+#define PDR_SYNTH_TX_DIV_MASK 0x0200
+#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200
+
struct bootrec_exp_if {
__le16 role;
__le16 if_id;
@@ -30,6 +49,13 @@ struct bootrec_exp_if {
__le16 top_compat;
} __attribute__((packed));
+#define BR_DESC_PRIV_CAP_WEP BIT(0)
+#define BR_DESC_PRIV_CAP_TKIP BIT(1)
+#define BR_DESC_PRIV_CAP_MICHAEL BIT(2)
+#define BR_DESC_PRIV_CAP_CCX_CP BIT(3)
+#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4)
+#define BR_DESC_PRIV_CAP_AESCCMP BIT(5)
+
struct bootrec_desc {
__le16 modes;
__le16 flags;
@@ -37,8 +63,15 @@ struct bootrec_desc {
__le32 rx_end;
u8 headroom;
u8 tailroom;
- u8 unimportant[6];
+ u8 tx_queues;
+ u8 tx_depth;
+ u8 privacy_caps;
+ u8 rx_keycache_size;
+ u8 time_size;
+ u8 padding;
u8 rates[16];
+ u8 padding2[4];
+ __le16 rx_mtu;
} __attribute__((packed));
#define BR_CODE_MIN 0x80000000
@@ -51,6 +84,31 @@ struct bootrec_desc {
#define BR_CODE_END_OF_BRA 0xFF0000FF
#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF
+#define P54_HDR_FLAG_DATA_ALIGN BIT(14)
+#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0)
+#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1)
+#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2)
+#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3)
+#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4)
+#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5)
+#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6)
+#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7)
+#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8)
+#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9)
+#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10)
+#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11)
+
+#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0)
+#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1)
+#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2)
+#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3)
+#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4)
+#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5)
+#define P54_HDR_FLAG_DATA_IN_DATA BIT(6)
+#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7)
+#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8)
+#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9)
+
/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */
struct pda_entry {
@@ -117,6 +175,11 @@ struct pda_pa_curve_data {
u8 data[0];
} __attribute__ ((packed));
+struct pda_rssi_cal_entry {
+ __le16 mul;
+ __le16 add;
+} __attribute__ ((packed));
+
/*
* this defines the PDR codes used to build PDAs as defined in document
* number 553155. The current implementation mirrors version 1.1 of the
@@ -165,6 +228,19 @@ struct pda_pa_curve_data {
#define PDR_BASEBAND_REGISTERS 0x8000
#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001
+/* PDR definitions for default country & country list */
+#define PDR_COUNTRY_CERT_CODE 0x80
+#define PDR_COUNTRY_CERT_CODE_REAL 0x00
+#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80
+#define PDR_COUNTRY_CERT_BAND 0x40
+#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00
+#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40
+#define PDR_COUNTRY_CERT_IODOOR 0x30
+#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00
+#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20
+#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30
+#define PDR_COUNTRY_CERT_INDEX 0x0F
+
/* stored in skb->cb */
struct memrecord {
u32 start_addr;
@@ -172,41 +248,108 @@ struct memrecord {
};
struct p54_eeprom_lm86 {
- __le16 offset;
- __le16 len;
- u8 data[0];
+ union {
+ struct {
+ __le16 offset;
+ __le16 len;
+ u8 data[0];
+ } v1;
+ struct {
+ __le32 offset;
+ __le16 len;
+ u8 magic2;
+ u8 pad;
+ u8 magic[4];
+ u8 data[0];
+ } v2;
+ } __attribute__ ((packed));
} __attribute__ ((packed));
-struct p54_rx_hdr {
- __le16 magic;
+enum p54_rx_decrypt_status {
+ P54_DECRYPT_NONE = 0,
+ P54_DECRYPT_OK,
+ P54_DECRYPT_NOKEY,
+ P54_DECRYPT_NOMICHAEL,
+ P54_DECRYPT_NOCKIPMIC,
+ P54_DECRYPT_FAIL_WEP,
+ P54_DECRYPT_FAIL_TKIP,
+ P54_DECRYPT_FAIL_MICHAEL,
+ P54_DECRYPT_FAIL_CKIPKP,
+ P54_DECRYPT_FAIL_CKIPMIC,
+ P54_DECRYPT_FAIL_AESCCMP
+};
+
+struct p54_rx_data {
+ __le16 flags;
__le16 len;
__le16 freq;
u8 antenna;
u8 rate;
u8 rssi;
u8 quality;
- u16 unknown2;
+ u8 decrypt_status;
+ u8 rssi_raw;
__le32 tsf32;
__le32 unalloc0;
u8 align[0];
} __attribute__ ((packed));
-struct p54_frame_sent_hdr {
+enum p54_trap_type {
+ P54_TRAP_SCAN = 0,
+ P54_TRAP_TIMER,
+ P54_TRAP_BEACON_TX,
+ P54_TRAP_FAA_RADIO_ON,
+ P54_TRAP_FAA_RADIO_OFF,
+ P54_TRAP_RADAR,
+ P54_TRAP_NO_BEACON,
+ P54_TRAP_TBTT,
+ P54_TRAP_SCO_ENTER,
+ P54_TRAP_SCO_EXIT
+};
+
+struct p54_trap {
+ __le16 event;
+ __le16 frequency;
+} __attribute__ ((packed));
+
+enum p54_frame_sent_status {
+ P54_TX_OK = 0,
+ P54_TX_FAILED,
+ P54_TX_PSM,
+ P54_TX_PSM_CANCELLED = 4
+};
+
+struct p54_frame_sent {
u8 status;
- u8 retries;
- __le16 ack_rssi;
+ u8 tries;
+ u8 ack_rssi;
+ u8 quality;
__le16 seq;
- u16 rate;
+ u8 antenna;
+ u8 padding;
} __attribute__ ((packed));
-struct p54_tx_control_allocdata {
+enum p54_tx_data_crypt {
+ P54_CRYPTO_NONE = 0,
+ P54_CRYPTO_WEP,
+ P54_CRYPTO_TKIP,
+ P54_CRYPTO_TKIPMICHAEL,
+ P54_CRYPTO_CCX_WEPMIC,
+ P54_CRYPTO_CCX_KPMIC,
+ P54_CRYPTO_CCX_KP,
+ P54_CRYPTO_AESCCMP
+};
+
+struct p54_tx_data {
u8 rateset[8];
- u8 unalloc0[2];
+ u8 rts_rate_idx;
+ u8 crypt_offset;
u8 key_type;
u8 key_len;
u8 key[16];
u8 hw_queue;
- u8 unalloc1[9];
+ u8 backlog;
+ __le16 durations[4];
u8 tx_antenna;
u8 output_power;
u8 cts_rate;
@@ -214,8 +357,23 @@ struct p54_tx_control_allocdata {
u8 align[0];
} __attribute__ ((packed));
-struct p54_tx_control_filter {
- __le16 filter_type;
+/* unit is ms */
+#define P54_TX_FRAME_LIFETIME 2000
+#define P54_TX_TIMEOUT 4000
+#define P54_STATISTICS_UPDATE 5000
+
+#define P54_FILTER_TYPE_NONE 0
+#define P54_FILTER_TYPE_STATION BIT(0)
+#define P54_FILTER_TYPE_IBSS BIT(1)
+#define P54_FILTER_TYPE_AP BIT(2)
+#define P54_FILTER_TYPE_TRANSPARENT BIT(3)
+#define P54_FILTER_TYPE_PROMISCUOUS BIT(4)
+#define P54_FILTER_TYPE_HIBERNATE BIT(5)
+#define P54_FILTER_TYPE_NOACK BIT(6)
+#define P54_FILTER_TYPE_RX_DISABLED BIT(7)
+
+struct p54_setup_mac {
+ __le16 mac_mode;
u8 mac_addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
u8 rx_antenna;
@@ -235,17 +393,29 @@ struct p54_tx_control_filter {
__le16 max_rx;
__le16 rxhw;
__le16 timer;
- __le16 unalloc0;
- __le32 unalloc1;
+ __le16 truncate;
+ __le32 basic_rate_mask;
+ u8 sbss_offset;
+ u8 mcast_window;
+ u8 rx_rssi_threshold;
+ u8 rx_ed_threshold;
+ __le32 ref_clock;
+ __le16 lpf_bandwidth;
+ __le16 osc_start_delay;
} v2 __attribute__ ((packed));
} __attribute__ ((packed));
} __attribute__ ((packed));
-#define P54_TX_CONTROL_FILTER_V1_LEN (sizeof(struct p54_tx_control_filter))
-#define P54_TX_CONTROL_FILTER_V2_LEN (sizeof(struct p54_tx_control_filter)-8)
+#define P54_SETUP_V1_LEN 40
+#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac))
-struct p54_tx_control_channel {
- __le16 flags;
+#define P54_SCAN_EXIT BIT(0)
+#define P54_SCAN_TRAP BIT(1)
+#define P54_SCAN_ACTIVE BIT(2)
+#define P54_SCAN_FILTER BIT(3)
+
+struct p54_scan {
+ __le16 mode;
__le16 dwell;
u8 padding1[20];
struct pda_iq_autocal_entry iq_autocal;
@@ -261,45 +431,35 @@ struct p54_tx_control_channel {
u8 dup_16qam;
u8 dup_64qam;
union {
- struct {
- __le16 rssical_mul;
- __le16 rssical_add;
- } v1 __attribute__ ((packed));
+ struct pda_rssi_cal_entry v1_rssi;
struct {
__le32 basic_rate_mask;
- u8 rts_rates[8];
- __le16 rssical_mul;
- __le16 rssical_add;
+ u8 rts_rates[8];
+ struct pda_rssi_cal_entry rssi;
} v2 __attribute__ ((packed));
} __attribute__ ((packed));
} __attribute__ ((packed));
-#define P54_TX_CONTROL_CHANNEL_V1_LEN (sizeof(struct p54_tx_control_channel)-12)
-#define P54_TX_CONTROL_CHANNEL_V2_LEN (sizeof(struct p54_tx_control_channel))
+#define P54_SCAN_V1_LEN 0x70
+#define P54_SCAN_V2_LEN 0x7c
-struct p54_tx_control_led {
+struct p54_led {
__le16 mode;
__le16 led_temporary;
__le16 led_permanent;
__le16 duration;
} __attribute__ ((packed));
-struct p54_tx_vdcf_queues {
- __le16 aifs;
- __le16 cwmin;
- __le16 cwmax;
- __le16 txop;
-} __attribute__ ((packed));
-
-struct p54_tx_control_vdcf {
- u8 padding;
+struct p54_edcf {
+ u8 flags;
u8 slottime;
- u8 magic1;
- u8 magic2;
- struct p54_tx_vdcf_queues queue[8];
- u8 pad2[4];
+ u8 sifs;
+ u8 eofpad;
+ struct p54_edcf_queue_param queue[8];
+ u8 mapping[4];
__le16 frameburst;
+ __le16 round_trip_delay;
} __attribute__ ((packed));
struct p54_statistics {
@@ -312,14 +472,103 @@ struct p54_statistics {
__le32 tsf32;
__le32 airtime;
__le32 noise;
- __le32 unkn[10]; /* CCE / CCA / RADAR */
+ __le32 sample_noise[8];
+ __le32 sample_cca;
+ __le32 sample_tx;
} __attribute__ ((packed));
-struct p54_tx_control_xbow_synth {
+struct p54_xbow_synth {
__le16 magic1;
__le16 magic2;
__le16 freq;
u32 padding[5];
} __attribute__ ((packed));
+struct p54_timer {
+ __le32 interval;
+} __attribute__ ((packed));
+
+struct p54_keycache {
+ u8 entry;
+ u8 key_id;
+ u8 mac[ETH_ALEN];
+ u8 padding[2];
+ u8 key_type;
+ u8 key_len;
+ u8 key[24];
+} __attribute__ ((packed));
+
+struct p54_burst {
+ u8 flags;
+ u8 queue;
+ u8 backlog;
+ u8 pad;
+ __le16 durations[32];
+} __attribute__ ((packed));
+
+struct p54_psm_interval {
+ __le16 interval;
+ __le16 periods;
+} __attribute__ ((packed));
+
+#define P54_PSM BIT(0)
+#define P54_PSM_DTIM BIT(1)
+#define P54_PSM_MCBC BIT(2)
+#define P54_PSM_CHECKSUM BIT(3)
+#define P54_PSM_SKIP_MORE_DATA BIT(4)
+#define P54_PSM_BEACON_TIMEOUT BIT(5)
+#define P54_PSM_HFOSLEEP BIT(6)
+#define P54_PSM_AUTOSWITCH_SLEEP BIT(7)
+#define P54_PSM_LPIT BIT(8)
+#define P54_PSM_BF_UCAST_SKIP BIT(9)
+#define P54_PSM_BF_MCAST_SKIP BIT(10)
+
+struct p54_psm {
+ __le16 mode;
+ __le16 aid;
+ struct p54_psm_interval intervals[4];
+ u8 beacon_rssi_skip_max;
+ u8 rssi_delta_threshold;
+ u8 nr;
+ u8 exclude[1];
+} __attribute__ ((packed));
+
+#define MC_FILTER_ADDRESS_NUM 4
+
+struct p54_group_address_table {
+ __le16 filter_enable;
+ __le16 num_address;
+ u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN];
+} __attribute__ ((packed));
+
+struct p54_txcancel {
+ __le32 req_id;
+} __attribute__ ((packed));
+
+struct p54_sta_unlock {
+ u8 addr[ETH_ALEN];
+ u16 padding;
+} __attribute__ ((packed));
+
+#define P54_TIM_CLEAR BIT(15)
+struct p54_tim {
+ u8 count;
+ u8 padding[3];
+ __le16 entry[8];
+} __attribute__ ((packed));
+
+struct p54_cce_quiet {
+ __le32 period;
+} __attribute__ ((packed));
+
+struct p54_bt_balancer {
+ __le16 prio_thresh;
+ __le16 acl_thresh;
+} __attribute__ ((packed));
+
+struct p54_arp_table {
+ __le16 filter_enable;
+ u8 ipv4_addr[4];
+} __attribute__ ((packed));
+
#endif /* P54COMMON_H */
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index 88b3cad8b65e..aa367a0ddc49 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -28,6 +28,7 @@ MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Prism54 PCI wireless driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("prism54pci");
+MODULE_FIRMWARE("isl3886pci");
static struct pci_device_id p54p_table[] __devinitdata = {
/* Intersil PRISM Duette/Prism GT Wireless LAN adapter */
@@ -46,7 +47,6 @@ MODULE_DEVICE_TABLE(pci, p54p_table);
static int p54p_upload_firmware(struct ieee80211_hw *dev)
{
struct p54p_priv *priv = dev->priv;
- const struct firmware *fw_entry = NULL;
__le32 reg;
int err;
__le32 *data;
@@ -72,21 +72,15 @@ static int p54p_upload_firmware(struct ieee80211_hw *dev)
P54P_WRITE(ctrl_stat, reg);
wmb();
- err = request_firmware(&fw_entry, "isl3886", &priv->pdev->dev);
- if (err) {
- printk(KERN_ERR "%s (p54pci): cannot find firmware "
- "(isl3886)\n", pci_name(priv->pdev));
- return err;
- }
+ /* wait for the firmware to reset properly */
+ mdelay(10);
- err = p54_parse_firmware(dev, fw_entry);
- if (err) {
- release_firmware(fw_entry);
+ err = p54_parse_firmware(dev, priv->firmware);
+ if (err)
return err;
- }
- data = (__le32 *) fw_entry->data;
- remains = fw_entry->size;
+ data = (__le32 *) priv->firmware->data;
+ remains = priv->firmware->size;
device_addr = ISL38XX_DEV_FIRMWARE_ADDR;
while (remains) {
u32 i = 0;
@@ -104,8 +98,6 @@ static int p54p_upload_firmware(struct ieee80211_hw *dev)
P54P_READ(int_enable);
}
- release_firmware(fw_entry);
-
reg = P54P_READ(ctrl_stat);
reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN);
reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET);
@@ -235,7 +227,9 @@ static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index,
while (i != idx) {
desc = &ring[i];
- kfree(tx_buf[i]);
+ if (tx_buf[i])
+ if (FREE_AFTER_TX((struct sk_buff *) tx_buf[i]))
+ p54_free_skb(dev, tx_buf[i]);
tx_buf[i] = NULL;
pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr),
@@ -306,8 +300,7 @@ static irqreturn_t p54p_interrupt(int irq, void *dev_id)
return reg ? IRQ_HANDLED : IRQ_NONE;
}
-static void p54p_tx(struct ieee80211_hw *dev, struct p54_control_hdr *data,
- size_t len, int free_on_tx)
+static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54p_priv *priv = dev->priv;
struct p54p_ring_control *ring_control = priv->ring_control;
@@ -322,28 +315,21 @@ static void p54p_tx(struct ieee80211_hw *dev, struct p54_control_hdr *data,
idx = le32_to_cpu(ring_control->host_idx[1]);
i = idx % ARRAY_SIZE(ring_control->tx_data);
- mapping = pci_map_single(priv->pdev, data, len, PCI_DMA_TODEVICE);
+ priv->tx_buf_data[i] = skb;
+ mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
desc = &ring_control->tx_data[i];
desc->host_addr = cpu_to_le32(mapping);
- desc->device_addr = data->req_id;
- desc->len = cpu_to_le16(len);
+ desc->device_addr = ((struct p54_hdr *)skb->data)->req_id;
+ desc->len = cpu_to_le16(skb->len);
desc->flags = 0;
wmb();
ring_control->host_idx[1] = cpu_to_le32(idx + 1);
-
- if (free_on_tx)
- priv->tx_buf_data[i] = data;
-
spin_unlock_irqrestore(&priv->lock, flags);
P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE));
P54P_READ(dev_int);
-
- /* FIXME: unlikely to happen because the device usually runs out of
- memory before we fill the ring up, but we can make it impossible */
- if (idx - device_idx > ARRAY_SIZE(ring_control->tx_data) - 2)
- printk(KERN_INFO "%s: tx overflow.\n", wiphy_name(dev->wiphy));
}
static void p54p_stop(struct ieee80211_hw *dev)
@@ -393,7 +379,7 @@ static void p54p_stop(struct ieee80211_hw *dev)
le16_to_cpu(desc->len),
PCI_DMA_TODEVICE);
- kfree(priv->tx_buf_data[i]);
+ p54_free_skb(dev, priv->tx_buf_data[i]);
priv->tx_buf_data[i] = NULL;
}
@@ -405,7 +391,7 @@ static void p54p_stop(struct ieee80211_hw *dev)
le16_to_cpu(desc->len),
PCI_DMA_TODEVICE);
- kfree(priv->tx_buf_mgmt[i]);
+ p54_free_skb(dev, priv->tx_buf_mgmt[i]);
priv->tx_buf_mgmt[i] = NULL;
}
@@ -481,7 +467,6 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
struct ieee80211_hw *dev;
unsigned long mem_addr, mem_len;
int err;
- DECLARE_MAC_BUF(mac);
err = pci_enable_device(pdev);
if (err) {
@@ -495,15 +480,14 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
if (mem_len < sizeof(struct p54p_csr)) {
printk(KERN_ERR "%s (p54pci): Too short PCI resources\n",
pci_name(pdev));
- pci_disable_device(pdev);
- return err;
+ goto err_disable_dev;
}
err = pci_request_regions(pdev, "p54pci");
if (err) {
printk(KERN_ERR "%s (p54pci): Cannot obtain PCI resources\n",
pci_name(pdev));
- return err;
+ goto err_disable_dev;
}
if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) ||
@@ -556,6 +540,17 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
spin_lock_init(&priv->lock);
tasklet_init(&priv->rx_tasklet, p54p_rx_tasklet, (unsigned long)dev);
+ err = request_firmware(&priv->firmware, "isl3886pci",
+ &priv->pdev->dev);
+ if (err) {
+ printk(KERN_ERR "%s (p54pci): cannot find firmware "
+ "(isl3886pci)\n", pci_name(priv->pdev));
+ err = request_firmware(&priv->firmware, "isl3886",
+ &priv->pdev->dev);
+ if (err)
+ goto err_free_common;
+ }
+
err = p54p_open(dev);
if (err)
goto err_free_common;
@@ -574,6 +569,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
return 0;
err_free_common:
+ release_firmware(priv->firmware);
p54_free_common(dev);
pci_free_consistent(pdev, sizeof(*priv->ring_control),
priv->ring_control, priv->ring_control_dma);
@@ -587,6 +583,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev,
err_free_reg:
pci_release_regions(pdev);
+ err_disable_dev:
pci_disable_device(pdev);
return err;
}
@@ -601,6 +598,7 @@ static void __devexit p54p_remove(struct pci_dev *pdev)
ieee80211_unregister_hw(dev);
priv = dev->priv;
+ release_firmware(priv->firmware);
pci_free_consistent(pdev, sizeof(*priv->ring_control),
priv->ring_control, priv->ring_control_dma);
p54_free_common(dev);
diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h
index 4a6778070afc..fbb683953fb2 100644
--- a/drivers/net/wireless/p54/p54pci.h
+++ b/drivers/net/wireless/p54/p54pci.h
@@ -93,7 +93,7 @@ struct p54p_priv {
struct pci_dev *pdev;
struct p54p_csr __iomem *map;
struct tasklet_struct rx_tasklet;
-
+ const struct firmware *firmware;
spinlock_t lock;
struct p54p_ring_control *ring_control;
dma_addr_t ring_control_dma;
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 75d749bccb0d..c44a200059d2 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -28,6 +28,8 @@ MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
MODULE_DESCRIPTION("Prism54 USB wireless driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("prism54usb");
+MODULE_FIRMWARE("isl3886usb");
+MODULE_FIRMWARE("isl3887usb");
static struct usb_device_id p54u_table[] __devinitdata = {
/* Version 1 devices (pci chip + net2280) */
@@ -84,13 +86,13 @@ static void p54u_rx_cb(struct urb *urb)
struct ieee80211_hw *dev = info->dev;
struct p54u_priv *priv = dev->priv;
+ skb_unlink(skb, &priv->rx_queue);
+
if (unlikely(urb->status)) {
- info->urb = NULL;
- usb_free_urb(urb);
+ dev_kfree_skb_irq(skb);
return;
}
- skb_unlink(skb, &priv->rx_queue);
skb_put(skb, urb->actual_length);
if (priv->hw_type == P54U_NET2280)
@@ -103,7 +105,6 @@ static void p54u_rx_cb(struct urb *urb)
if (p54_rx(dev, skb)) {
skb = dev_alloc_skb(priv->common.rx_mtu + 32);
if (unlikely(!skb)) {
- usb_free_urb(urb);
/* TODO check rx queue length and refill *somewhere* */
return;
}
@@ -113,7 +114,6 @@ static void p54u_rx_cb(struct urb *urb)
info->dev = dev;
urb->transfer_buffer = skb_tail_pointer(skb);
urb->context = skb;
- skb_queue_tail(&priv->rx_queue, skb);
} else {
if (priv->hw_type == P54U_NET2280)
skb_push(skb, priv->common.tx_hdr_len);
@@ -128,40 +128,56 @@ static void p54u_rx_cb(struct urb *urb)
WARN_ON(1);
urb->transfer_buffer = skb_tail_pointer(skb);
}
-
- skb_queue_tail(&priv->rx_queue, skb);
}
-
- usb_submit_urb(urb, GFP_ATOMIC);
+ skb_queue_tail(&priv->rx_queue, skb);
+ usb_anchor_urb(urb, &priv->submitted);
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ skb_unlink(skb, &priv->rx_queue);
+ usb_unanchor_urb(urb);
+ dev_kfree_skb_irq(skb);
+ }
}
static void p54u_tx_cb(struct urb *urb)
{
- usb_free_urb(urb);
+ struct sk_buff *skb = urb->context;
+ struct ieee80211_hw *dev = (struct ieee80211_hw *)
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+ struct p54u_priv *priv = dev->priv;
+
+ skb_pull(skb, priv->common.tx_hdr_len);
+ if (FREE_AFTER_TX(skb))
+ p54_free_skb(dev, skb);
}
-static void p54u_tx_free_cb(struct urb *urb)
+static void p54u_tx_dummy_cb(struct urb *urb) { }
+
+static void p54u_free_urbs(struct ieee80211_hw *dev)
{
- kfree(urb->transfer_buffer);
- usb_free_urb(urb);
+ struct p54u_priv *priv = dev->priv;
+ usb_kill_anchored_urbs(&priv->submitted);
}
static int p54u_init_urbs(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
- struct urb *entry;
+ struct urb *entry = NULL;
struct sk_buff *skb;
struct p54u_rx_info *info;
+ int ret = 0;
while (skb_queue_len(&priv->rx_queue) < 32) {
skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL);
- if (!skb)
- break;
+ if (!skb) {
+ ret = -ENOMEM;
+ goto err;
+ }
entry = usb_alloc_urb(0, GFP_KERNEL);
if (!entry) {
- kfree_skb(skb);
- break;
+ ret = -ENOMEM;
+ goto err;
}
+
usb_fill_bulk_urb(entry, priv->udev,
usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA),
skb_tail_pointer(skb),
@@ -170,33 +186,32 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
info->urb = entry;
info->dev = dev;
skb_queue_tail(&priv->rx_queue, skb);
- usb_submit_urb(entry, GFP_KERNEL);
+
+ usb_anchor_urb(entry, &priv->submitted);
+ ret = usb_submit_urb(entry, GFP_KERNEL);
+ if (ret) {
+ skb_unlink(skb, &priv->rx_queue);
+ usb_unanchor_urb(entry);
+ goto err;
+ }
+ usb_free_urb(entry);
+ entry = NULL;
}
return 0;
-}
-
-static void p54u_free_urbs(struct ieee80211_hw *dev)
-{
- struct p54u_priv *priv = dev->priv;
- struct p54u_rx_info *info;
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&priv->rx_queue))) {
- info = (struct p54u_rx_info *) skb->cb;
- if (!info->urb)
- continue;
-
- usb_kill_urb(info->urb);
- kfree_skb(skb);
- }
+ err:
+ usb_free_urb(entry);
+ kfree_skb(skb);
+ p54u_free_urbs(dev);
+ return ret;
}
-static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data,
- size_t len, int free_on_tx)
+static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *addr_urb, *data_urb;
+ int err = 0;
addr_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!addr_urb)
@@ -209,59 +224,85 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data,
}
usb_fill_bulk_urb(addr_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &data->req_id,
- sizeof(data->req_id), p54u_tx_cb, dev);
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ &((struct p54_hdr *)skb->data)->req_id, 4,
+ p54u_tx_dummy_cb, dev);
usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), data, len,
- free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev);
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ skb->data, skb->len, p54u_tx_cb, skb);
+
+ usb_anchor_urb(addr_urb, &priv->submitted);
+ err = usb_submit_urb(addr_urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(addr_urb);
+ goto out;
+ }
- usb_submit_urb(addr_urb, GFP_ATOMIC);
- usb_submit_urb(data_urb, GFP_ATOMIC);
+ usb_anchor_urb(addr_urb, &priv->submitted);
+ err = usb_submit_urb(data_urb, GFP_ATOMIC);
+ if (err)
+ usb_unanchor_urb(data_urb);
+
+ out:
+ usb_free_urb(addr_urb);
+ usb_free_urb(data_urb);
+
+ if (err)
+ p54_free_skb(dev, skb);
}
-static __le32 p54u_lm87_chksum(const u32 *data, size_t length)
+static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
{
u32 chk = 0;
length >>= 2;
while (length--) {
- chk ^= *data++;
+ chk ^= le32_to_cpu(*data++);
chk = (chk >> 5) ^ (chk << 3);
}
return cpu_to_le32(chk);
}
-static void p54u_tx_lm87(struct ieee80211_hw *dev,
- struct p54_control_hdr *data,
- size_t len, int free_on_tx)
+static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *data_urb;
- struct lm87_tx_hdr *hdr = (void *)data - sizeof(*hdr);
+ struct lm87_tx_hdr *hdr;
+ __le32 checksum;
+ __le32 addr = ((struct p54_hdr *)skb->data)->req_id;
data_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!data_urb)
return;
- hdr->chksum = p54u_lm87_chksum((u32 *)data, len);
- hdr->device_addr = data->req_id;
+ checksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len);
+ hdr = (struct lm87_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ hdr->chksum = checksum;
+ hdr->device_addr = addr;
usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr,
- len + sizeof(*hdr), free_on_tx ? p54u_tx_free_cb : p54u_tx_cb,
- dev);
-
- usb_submit_urb(data_urb, GFP_ATOMIC);
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ skb->data, skb->len, p54u_tx_cb, skb);
+
+ usb_anchor_urb(data_urb, &priv->submitted);
+ if (usb_submit_urb(data_urb, GFP_ATOMIC)) {
+ usb_unanchor_urb(data_urb);
+ skb_pull(skb, sizeof(*hdr));
+ p54_free_skb(dev, skb);
+ }
+ usb_free_urb(data_urb);
}
-static void p54u_tx_net2280(struct ieee80211_hw *dev, struct p54_control_hdr *data,
- size_t len, int free_on_tx)
+static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54u_priv *priv = dev->priv;
struct urb *int_urb, *data_urb;
struct net2280_tx_hdr *hdr;
struct net2280_reg_write *reg;
+ int err = 0;
+ __le32 addr = ((struct p54_hdr *) skb->data)->req_id;
+ __le16 len = cpu_to_le16(skb->len);
reg = kmalloc(sizeof(*reg), GFP_ATOMIC);
if (!reg)
@@ -284,21 +325,47 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct p54_control_hdr *da
reg->addr = cpu_to_le32(P54U_DEV_BASE);
reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA);
- len += sizeof(*data);
- hdr = (void *)data - sizeof(*hdr);
+ hdr = (void *)skb_push(skb, sizeof(*hdr));
memset(hdr, 0, sizeof(*hdr));
- hdr->device_addr = data->req_id;
- hdr->len = cpu_to_le16(len);
+ hdr->len = len;
+ hdr->device_addr = addr;
usb_fill_bulk_urb(int_urb, priv->udev,
usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg),
- p54u_tx_free_cb, dev);
- usb_submit_urb(int_urb, GFP_ATOMIC);
+ p54u_tx_dummy_cb, dev);
+
+ /*
+ * This flag triggers a code path in the USB subsystem that will
+ * free what's inside the transfer_buffer after the callback routine
+ * has completed.
+ */
+ int_urb->transfer_flags |= URB_FREE_BUFFER;
usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, len + sizeof(*hdr),
- free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev);
- usb_submit_urb(data_urb, GFP_ATOMIC);
+ usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
+ skb->data, skb->len, p54u_tx_cb, skb);
+
+ usb_anchor_urb(int_urb, &priv->submitted);
+ err = usb_submit_urb(int_urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(int_urb);
+ goto out;
+ }
+
+ usb_anchor_urb(data_urb, &priv->submitted);
+ err = usb_submit_urb(data_urb, GFP_ATOMIC);
+ if (err) {
+ usb_unanchor_urb(data_urb);
+ goto out;
+ }
+ out:
+ usb_free_urb(int_urb);
+ usb_free_urb(data_urb);
+
+ if (err) {
+ skb_pull(skb, sizeof(*hdr));
+ p54_free_skb(dev, skb);
+ }
}
static int p54u_write(struct p54u_priv *priv,
@@ -375,7 +442,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
if (!buf) {
- printk(KERN_ERR "p54usb: cannot allocate firmware upload buffer!\n");
+ dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
+ "upload buffer!\n");
err = -ENOMEM;
goto err_bufalloc;
}
@@ -383,14 +451,18 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
memcpy(buf, start_string, 4);
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 4);
if (err) {
- printk(KERN_ERR "p54usb: reset failed! (%d)\n", err);
+ dev_err(&priv->udev->dev, "(p54usb) reset failed! (%d)\n", err);
goto err_reset;
}
- err = request_firmware(&fw_entry, "isl3887usb_bare", &priv->udev->dev);
+ err = request_firmware(&fw_entry, "isl3887usb", &priv->udev->dev);
if (err) {
- printk(KERN_ERR "p54usb: cannot find firmware (isl3887usb_bare)!\n");
- goto err_req_fw_failed;
+ dev_err(&priv->udev->dev, "p54usb: cannot find firmware "
+ "(isl3887usb)\n");
+ err = request_firmware(&fw_entry, "isl3887usb_bare",
+ &priv->udev->dev);
+ if (err)
+ goto err_req_fw_failed;
}
err = p54_parse_firmware(dev, fw_entry);
@@ -441,7 +513,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size);
if (err) {
- printk(KERN_ERR "p54usb: firmware upload failed!\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware "
+ "upload failed!\n");
goto err_upload_failed;
}
@@ -452,10 +525,9 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
*((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size));
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
if (err) {
- printk(KERN_ERR "p54usb: firmware upload failed!\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
goto err_upload_failed;
}
-
timeout = jiffies + msecs_to_jiffies(1000);
while (!(err = usb_bulk_msg(priv->udev,
usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) {
@@ -463,25 +535,27 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
break;
if (alen > 5 && !memcmp(buf, "ERROR", 5)) {
- printk(KERN_INFO "p54usb: firmware upload failed!\n");
err = -EINVAL;
break;
}
if (time_after(jiffies, timeout)) {
- printk(KERN_ERR "p54usb: firmware boot timed out!\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware boot "
+ "timed out!\n");
err = -ETIMEDOUT;
break;
}
}
- if (err)
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
goto err_upload_failed;
+ }
buf[0] = 'g';
buf[1] = '\r';
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2);
if (err) {
- printk(KERN_ERR "p54usb: firmware boot failed!\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n");
goto err_upload_failed;
}
@@ -521,15 +595,21 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
buf = kmalloc(512, GFP_KERNEL);
if (!buf) {
- printk(KERN_ERR "p54usb: firmware buffer alloc failed!\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware buffer "
+ "alloc failed!\n");
return -ENOMEM;
}
- err = request_firmware(&fw_entry, "isl3890usb", &priv->udev->dev);
+ err = request_firmware(&fw_entry, "isl3886usb", &priv->udev->dev);
if (err) {
- printk(KERN_ERR "p54usb: cannot find firmware (isl3890usb)!\n");
- kfree(buf);
- return err;
+ dev_err(&priv->udev->dev, "(p54usb) cannot find firmware "
+ "(isl3886usb)\n");
+ err = request_firmware(&fw_entry, "isl3890usb",
+ &priv->udev->dev);
+ if (err) {
+ kfree(buf);
+ return err;
+ }
}
err = p54_parse_firmware(dev, fw_entry);
@@ -648,8 +728,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len);
if (err) {
- printk(KERN_ERR "p54usb: firmware block upload "
- "failed\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware block "
+ "upload failed\n");
goto fail;
}
@@ -682,8 +762,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
0x002C | (unsigned long)&devreg->direct_mem_win);
if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) ||
!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) {
- printk(KERN_ERR "p54usb: firmware DMA transfer "
- "failed\n");
+ dev_err(&priv->udev->dev, "(p54usb) firmware DMA "
+ "transfer failed\n");
goto fail;
}
@@ -786,11 +866,11 @@ static int __devinit p54u_probe(struct usb_interface *intf,
struct p54u_priv *priv;
int err;
unsigned int i, recognized_pipes;
- DECLARE_MAC_BUF(mac);
dev = p54_init_common(sizeof(*priv));
+
if (!dev) {
- printk(KERN_ERR "p54usb: ieee80211 alloc failed\n");
+ dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n");
return -ENOMEM;
}
@@ -842,6 +922,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
goto err_free_dev;
skb_queue_head_init(&priv->rx_queue);
+ init_usb_anchor(&priv->submitted);
p54u_open(dev);
err = p54_read_eeprom(dev);
@@ -851,7 +932,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
err = ieee80211_register_hw(dev);
if (err) {
- printk(KERN_ERR "p54usb: Cannot register netdevice\n");
+ dev_err(&udev->dev, "(p54usb) Cannot register netdevice\n");
goto err_free_dev;
}
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index 5b8fe91379c3..54ee738bf2af 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -133,6 +133,7 @@ struct p54u_priv {
spinlock_t lock;
struct sk_buff_head rx_queue;
+ struct usb_anchor submitted;
};
#endif /* P54USB_H */
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index 16e68f4b654a..57a150a22de5 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -2028,12 +2028,11 @@ static void
format_event(islpci_private *priv, char *dest, const char *str,
const struct obj_mlme *mlme, u16 *length, int error)
{
- DECLARE_MAC_BUF(mac);
int n = snprintf(dest, IW_CUSTOM_MAX,
- "%s %s %s %s (%2.2X)",
+ "%s %s %pM %s (%2.2X)",
str,
((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"),
- print_mac(mac, mlme->address),
+ mlme->address,
(error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
: ""), mlme->code);
BUG_ON(n > IW_CUSTOM_MAX);
@@ -2113,7 +2112,6 @@ prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
{
struct list_head *ptr;
struct islpci_bss_wpa_ie *bss = NULL;
- DECLARE_MAC_BUF(mac);
if (wpa_ie_len > MAX_WPA_IE_LEN)
wpa_ie_len = MAX_WPA_IE_LEN;
@@ -2154,7 +2152,7 @@ prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid,
bss->last_update = jiffies;
} else {
printk(KERN_DEBUG "Failed to add BSS WPA entry for "
- "%s\n", print_mac(mac, bssid));
+ "%pM\n", bssid);
}
/* expire old entries from WPA list */
@@ -2219,7 +2217,6 @@ prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
{
struct ieee80211_beacon_phdr *hdr;
u8 *pos, *end;
- DECLARE_MAC_BUF(mac);
if (!priv->wpa)
return;
@@ -2230,7 +2227,7 @@ prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
while (pos < end) {
if (pos + 2 + pos[1] > end) {
printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
- "for %s\n", print_mac(mac, addr));
+ "for %pM\n", addr);
return;
}
if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
@@ -2269,7 +2266,6 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
size_t len = 0; /* u16, better? */
u8 *payload = NULL, *pos = NULL;
int ret;
- DECLARE_MAC_BUF(mac);
/* I think all trapable objects are listed here.
* Some oids have a EX version. The difference is that they are emitted
@@ -2358,8 +2354,8 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
break;
memcpy(&confirm->address, mlmeex->address, ETH_ALEN);
- printk(KERN_DEBUG "Authenticate from: address:\t%s\n",
- print_mac(mac, mlmeex->address));
+ printk(KERN_DEBUG "Authenticate from: address:\t%pM\n",
+ mlmeex->address);
confirm->id = -1; /* or mlmeex->id ? */
confirm->state = 0; /* not used */
confirm->code = 0;
@@ -2404,8 +2400,8 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
if (!wpa_ie_len) {
- printk(KERN_DEBUG "No WPA IE found from address:\t%s\n",
- print_mac(mac, mlmeex->address));
+ printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
+ mlmeex->address);
kfree(confirm);
break;
}
@@ -2441,8 +2437,8 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie);
if (!wpa_ie_len) {
- printk(KERN_DEBUG "No WPA IE found from address:\t%s\n",
- print_mac(mac, mlmeex->address));
+ printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n",
+ mlmeex->address);
kfree(confirm);
break;
}
diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c
index af2e4f2405f2..9a72b1e3e163 100644
--- a/drivers/net/wireless/prism54/islpci_hotplug.c
+++ b/drivers/net/wireless/prism54/islpci_hotplug.c
@@ -93,7 +93,7 @@ static struct pci_driver prism54_driver = {
Module initialization functions
******************************************************************************/
-int
+static int
prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct net_device *ndev;
@@ -216,7 +216,7 @@ prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static volatile int __in_cleanup_module = 0;
/* this one removes one(!!) instance only */
-void
+static void
prism54_remove(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
@@ -259,7 +259,7 @@ prism54_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
-int
+static int
prism54_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *ndev = pci_get_drvdata(pdev);
@@ -282,7 +282,7 @@ prism54_suspend(struct pci_dev *pdev, pm_message_t state)
return 0;
}
-int
+static int
prism54_resume(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 1404a5717520..99ec7d622518 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -414,7 +414,6 @@ static int ray_config(struct pcmcia_device *link)
memreq_t mem;
struct net_device *dev = (struct net_device *)link->priv;
ray_dev_t *local = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
DEBUG(1, "ray_config(0x%p)\n", link);
@@ -485,8 +484,8 @@ static int ray_config(struct pcmcia_device *link)
strcpy(local->node.dev_name, dev->name);
link->dev_node = &local->node;
- printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %s\n",
- dev->name, dev->irq, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: RayLink, irq %d, hw_addr %pM\n",
+ dev->name, dev->irq, dev->dev_addr);
return 0;
@@ -829,7 +828,7 @@ static int ray_resume(struct pcmcia_device *link)
}
/*===========================================================================*/
-int ray_dev_init(struct net_device *dev)
+static int ray_dev_init(struct net_device *dev)
{
#ifdef RAY_IMMEDIATE_INIT
int i;
@@ -2285,7 +2284,6 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, unsigned i
skb->protocol = eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
local->stats.rx_packets++;
local->stats.rx_bytes += total_len;
@@ -2595,7 +2593,6 @@ static int ray_cs_proc_show(struct seq_file *m, void *v)
UCHAR *p;
struct freq_hop_element *pfh;
UCHAR c[33];
- DECLARE_MAC_BUF(mac);
link = this_device;
if (!link)
@@ -2623,8 +2620,7 @@ static int ray_cs_proc_show(struct seq_file *m, void *v)
nettype[local->sparm.b5.a_network_type], c);
p = local->bss_id;
- seq_printf(m, "BSSID = %s\n",
- print_mac(mac, p));
+ seq_printf(m, "BSSID = %pM\n", p);
seq_printf(m, "Country code = %d\n",
local->sparm.b5.a_curr_country_code);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 2b414899dfa0..607ce9f61b54 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -37,11 +37,11 @@
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/wireless.h>
+#include <linux/ieee80211.h>
#include <linux/if_arp.h>
#include <linux/ctype.h>
#include <linux/spinlock.h>
#include <net/iw_handler.h>
-#include <net/ieee80211.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/rndis_host.h>
@@ -1104,7 +1104,7 @@ static int rndis_iw_get_range(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct iw_range *range = (struct iw_range *)extra;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int len, ret, i, j, num, has_80211g_rates;
u8 rates[8];
@@ -1210,7 +1210,7 @@ static int rndis_iw_get_range(struct net_device *dev,
static int rndis_iw_get_name(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
strcpy(wrqu->name, priv->name);
@@ -1223,7 +1223,7 @@ static int rndis_iw_set_essid(struct net_device *dev,
{
struct ndis_80211_ssid ssid;
int length = wrqu->essid.length;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'",
wrqu->essid.flags, wrqu->essid.length, essid);
@@ -1250,7 +1250,7 @@ static int rndis_iw_get_essid(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
{
struct ndis_80211_ssid ssid;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
int ret;
ret = get_essid(usbdev, &ssid);
@@ -1273,15 +1273,14 @@ static int rndis_iw_get_essid(struct net_device *dev,
static int rndis_iw_get_bssid(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
unsigned char bssid[ETH_ALEN];
int ret;
- DECLARE_MAC_BUF(mac);
ret = get_bssid(usbdev, bssid);
if (ret == 0)
- devdbg(usbdev, "SIOCGIWAP: %s", print_mac(mac, bssid));
+ devdbg(usbdev, "SIOCGIWAP: %pM", bssid);
else
devdbg(usbdev, "SIOCGIWAP: <not associated>");
@@ -1295,12 +1294,11 @@ static int rndis_iw_get_bssid(struct net_device *dev,
static int rndis_iw_set_bssid(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
u8 *bssid = (u8 *)wrqu->ap_addr.sa_data;
- DECLARE_MAC_BUF(mac);
int ret;
- devdbg(usbdev, "SIOCSIWAP: %s", print_mac(mac, bssid));
+ devdbg(usbdev, "SIOCSIWAP: %pM", bssid);
ret = rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN);
@@ -1318,7 +1316,7 @@ static int rndis_iw_set_auth(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct iw_param *p = &wrqu->param;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int ret = -ENOTSUPP;
@@ -1399,7 +1397,7 @@ static int rndis_iw_get_auth(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct iw_param *p = &wrqu->param;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
switch (p->flags & IW_AUTH_INDEX) {
@@ -1431,7 +1429,7 @@ static int rndis_iw_get_mode(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
switch (priv->infra_mode) {
@@ -1454,7 +1452,7 @@ static int rndis_iw_get_mode(struct net_device *dev,
static int rndis_iw_set_mode(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
int mode;
devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode);
@@ -1479,7 +1477,7 @@ static int rndis_iw_set_mode(struct net_device *dev,
static int rndis_iw_set_encode(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int ret, index, key_len;
u8 *key;
@@ -1542,7 +1540,7 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct ndis_80211_key ndis_key;
int keyidx, ret;
@@ -1627,7 +1625,7 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
static int rndis_iw_set_scan(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
union iwreq_data evt;
int ret = -EINVAL;
__le32 tmp;
@@ -1652,19 +1650,18 @@ static char *rndis_translate_scan(struct net_device *dev,
struct ndis_80211_bssid_ex *bssid)
{
#ifdef DEBUG
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
#endif
- struct ieee80211_info_element *ie;
+ u8 *ie;
char *current_val;
int bssid_len, ie_len, i;
u32 beacon, atim;
struct iw_event iwe;
unsigned char sbuf[32];
- DECLARE_MAC_BUF(mac);
bssid_len = le32_to_cpu(bssid->length);
- devdbg(usbdev, "BSSID %s", print_mac(mac, bssid->mac));
+ devdbg(usbdev, "BSSID %pM", bssid->mac);
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN);
@@ -1753,20 +1750,20 @@ static char *rndis_translate_scan(struct net_device *dev,
ie_len = min(bssid_len - (int)sizeof(*bssid),
(int)le32_to_cpu(bssid->ie_length));
ie_len -= sizeof(struct ndis_80211_fixed_ies);
- while (ie_len >= sizeof(*ie) && sizeof(*ie) + ie->len <= ie_len) {
- if ((ie->id == MFIE_TYPE_GENERIC && ie->len >= 4 &&
- memcmp(ie->data, "\x00\x50\xf2\x01", 4) == 0) ||
- ie->id == MFIE_TYPE_RSN) {
+ while (ie_len >= 2 && 2 + ie[1] <= ie_len) {
+ if ((ie[0] == WLAN_EID_GENERIC && ie[1] >= 4 &&
+ memcmp(ie + 2, "\x00\x50\xf2\x01", 4) == 0) ||
+ ie[0] == WLAN_EID_RSN) {
devdbg(usbdev, "IE: WPA%d",
- (ie->id == MFIE_TYPE_RSN) ? 2 : 1);
+ (ie[0] == WLAN_EID_RSN) ? 2 : 1);
iwe.cmd = IWEVGENIE;
- iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN);
- cev = iwe_stream_add_point(info, cev, end_buf, &iwe,
- (u8 *)ie);
+ /* arbitrary cut-off at 64 */
+ iwe.u.data.length = min(ie[1] + 2, 64);
+ cev = iwe_stream_add_point(info, cev, end_buf, &iwe, ie);
}
- ie_len -= sizeof(*ie) + ie->len;
- ie = (struct ieee80211_info_element *)&ie->data[ie->len];
+ ie_len -= 2 + ie[1];
+ ie += 2 + ie[1];
}
return cev;
@@ -1776,7 +1773,7 @@ static char *rndis_translate_scan(struct net_device *dev,
static int rndis_iw_get_scan(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
void *buf = NULL;
char *cev = extra;
struct ndis_80211_bssid_list_ex *bssid_list;
@@ -1822,7 +1819,7 @@ out:
static int rndis_iw_set_genie(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int ret = 0;
@@ -1856,7 +1853,7 @@ static int rndis_iw_set_genie(struct net_device *dev,
static int rndis_iw_get_genie(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
devdbg(usbdev, "SIOCGIWGENIE");
@@ -1879,7 +1876,7 @@ static int rndis_iw_get_genie(struct net_device *dev,
static int rndis_iw_set_rts(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
__le32 tmp;
devdbg(usbdev, "SIOCSIWRTS");
@@ -1892,7 +1889,7 @@ static int rndis_iw_set_rts(struct net_device *dev,
static int rndis_iw_get_rts(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
__le32 tmp;
int len, ret;
@@ -1913,7 +1910,7 @@ static int rndis_iw_get_rts(struct net_device *dev,
static int rndis_iw_set_frag(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
__le32 tmp;
devdbg(usbdev, "SIOCSIWFRAG");
@@ -1927,7 +1924,7 @@ static int rndis_iw_set_frag(struct net_device *dev,
static int rndis_iw_get_frag(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
__le32 tmp;
int len, ret;
@@ -1947,7 +1944,7 @@ static int rndis_iw_get_frag(struct net_device *dev,
static int rndis_iw_set_nick(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
devdbg(usbdev, "SIOCSIWNICK");
@@ -1964,7 +1961,7 @@ static int rndis_iw_set_nick(struct net_device *dev,
static int rndis_iw_get_nick(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
wrqu->data.flags = 1;
@@ -1980,7 +1977,7 @@ static int rndis_iw_get_nick(struct net_device *dev,
static int rndis_iw_set_freq(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct ndis_80211_conf config;
unsigned int dsconfig;
int len, ret;
@@ -2011,7 +2008,7 @@ static int rndis_iw_set_freq(struct net_device *dev,
static int rndis_iw_get_freq(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct ndis_80211_conf config;
int len, ret;
@@ -2028,7 +2025,7 @@ static int rndis_iw_get_freq(struct net_device *dev,
static int rndis_iw_get_txpower(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tx_power;
int ret = 0, len;
@@ -2062,7 +2059,7 @@ static int rndis_iw_get_txpower(struct net_device *dev,
static int rndis_iw_set_txpower(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
__le32 tx_power = 0;
int ret = 0;
@@ -2114,7 +2111,7 @@ static int rndis_iw_set_txpower(struct net_device *dev,
static int rndis_iw_get_rate(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
__le32 tmp;
int ret, len;
@@ -2132,7 +2129,7 @@ static int rndis_iw_get_rate(struct net_device *dev,
static int rndis_iw_set_mlme(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct iw_mlme *mlme = (struct iw_mlme *)extra;
unsigned char bssid[ETH_ALEN];
@@ -2157,7 +2154,7 @@ static int rndis_iw_set_mlme(struct net_device *dev,
static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
unsigned long flags;
@@ -2287,7 +2284,7 @@ get_bssid:
static void rndis_wext_set_multicast_list(struct net_device *dev)
{
- struct usbnet *usbdev = dev->priv;
+ struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index 95511ac22470..178b313293b4 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -57,6 +57,7 @@ config RT2500USB
tristate "Ralink rt2500 (USB) support"
depends on USB
select RT2X00_LIB_USB
+ select RT2X00_LIB_CRYPTO
---help---
This adds support for rt2500 wireless chipset family.
Supported chips: RT2571 & RT2572.
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 08cb9eec16a6..6a977679124d 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -49,45 +49,33 @@
* the access attempt is considered to have failed,
* and we will print an error.
*/
-static u32 rt2400pci_bbp_check(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- unsigned int i;
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, BBPCSR, &reg);
- if (!rt2x00_get_field32(reg, BBPCSR_BUSY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- return reg;
-}
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg))
static void rt2400pci_bbp_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u8 value)
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2400pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Write failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the data into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- reg = 0;
- rt2x00_set_field32(&reg, BBPCSR_VALUE, value);
- rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
- rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
- rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 1);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBPCSR_VALUE, value);
+ rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
+ rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
+ rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 1);
+
+ rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ }
- rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -95,66 +83,58 @@ static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2400pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the request into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
*/
- reg = 0;
- rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
- rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
- rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 0);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
+ rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
+ rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 0);
- rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2400pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n");
- *value = 0xff;
- return;
+ WAIT_FOR_BBP(rt2x00dev, &reg);
}
*value = rt2x00_get_field32(reg, BBPCSR_VALUE);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2400pci_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u32 value)
{
u32 reg;
- unsigned int i;
if (!word)
return;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, RFCSR, &reg);
- if (!rt2x00_get_field32(reg, RFCSR_BUSY))
- goto rf_write;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- ERROR(rt2x00dev, "RFCSR register busy. Write failed.\n");
- return;
+ mutex_lock(&rt2x00dev->csr_mutex);
-rf_write:
- reg = 0;
- rt2x00_set_field32(&reg, RFCSR_VALUE, value);
- rt2x00_set_field32(&reg, RFCSR_NUMBER_OF_BITS, 20);
- rt2x00_set_field32(&reg, RFCSR_IF_SELECT, 0);
- rt2x00_set_field32(&reg, RFCSR_BUSY, 1);
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, RFCSR_VALUE, value);
+ rt2x00_set_field32(&reg, RFCSR_NUMBER_OF_BITS, 20);
+ rt2x00_set_field32(&reg, RFCSR_IF_SELECT, 0);
+ rt2x00_set_field32(&reg, RFCSR_BUSY, 1);
+
+ rt2x00pci_register_write(rt2x00dev, RFCSR, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
- rt2x00pci_register_write(rt2x00dev, RFCSR, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
@@ -188,43 +168,34 @@ static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
}
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) )
-
-static void rt2400pci_read_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 *data)
-{
- rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data);
-}
-
-static void rt2400pci_write_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 data)
-{
- rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data);
-}
-
static const struct rt2x00debug rt2400pci_rt2x00debug = {
.owner = THIS_MODULE,
.csr = {
- .read = rt2400pci_read_csr,
- .write = rt2400pci_write_csr,
+ .read = rt2x00pci_register_read,
+ .write = rt2x00pci_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
.word_size = sizeof(u32),
.word_count = CSR_REG_SIZE / sizeof(u32),
},
.eeprom = {
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
.word_size = sizeof(u16),
.word_count = EEPROM_SIZE / sizeof(u16),
},
.bbp = {
.read = rt2400pci_bbp_read,
.write = rt2400pci_bbp_write,
+ .word_base = BBP_BASE,
.word_size = sizeof(u8),
.word_count = BBP_SIZE / sizeof(u8),
},
.rf = {
.read = rt2x00_rf_read,
.write = rt2400pci_rf_write,
+ .word_base = RF_BASE,
.word_size = sizeof(u32),
.word_count = RF_SIZE / sizeof(u32),
},
@@ -331,7 +302,7 @@ static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev,
/*
* Enable beacon config
*/
- bcn_preload = PREAMBLE + get_duration(IEEE80211_HEADER, 20);
+ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20);
rt2x00pci_register_read(rt2x00dev, BCNCSR1, &reg);
rt2x00_set_field32(&reg, BCNCSR1_PRELOAD, bcn_preload);
rt2x00pci_register_write(rt2x00dev, BCNCSR1, reg);
@@ -376,32 +347,94 @@ static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 10));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 20));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 55));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 110));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+
+ rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+
+ rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+ rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+ rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+
+ rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+ rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+ rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+ rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+
+ rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+ rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+ rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+ rt2x00pci_register_write(rt2x00dev, CSR19, reg);
}
-static void rt2400pci_config_phymode(struct rt2x00_dev *rt2x00dev,
- const int basic_rate_mask)
+static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
{
- rt2x00pci_register_write(rt2x00dev, ARCSR1, basic_rate_mask);
+ u8 r1;
+ u8 r4;
+
+ /*
+ * We should never come here because rt2x00lib is supposed
+ * to catch this and send us the correct antenna explicitely.
+ */
+ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
+ ant->tx == ANTENNA_SW_DIVERSITY);
+
+ rt2400pci_bbp_read(rt2x00dev, 4, &r4);
+ rt2400pci_bbp_read(rt2x00dev, 1, &r1);
+
+ /*
+ * Configure the TX antenna.
+ */
+ switch (ant->tx) {
+ case ANTENNA_HW_DIVERSITY:
+ rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1);
+ break;
+ case ANTENNA_A:
+ rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2);
+ break;
+ }
+
+ /*
+ * Configure the RX antenna.
+ */
+ switch (ant->rx) {
+ case ANTENNA_HW_DIVERSITY:
+ rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1);
+ break;
+ case ANTENNA_A:
+ rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2);
+ break;
+ }
+
+ rt2400pci_bbp_write(rt2x00dev, 4, r4);
+ rt2400pci_bbp_write(rt2x00dev, 1, r1);
}
static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev,
@@ -460,56 +493,17 @@ static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower)
rt2400pci_bbp_write(rt2x00dev, 3, TXPOWER_TO_DEV(txpower));
}
-static void rt2400pci_config_antenna(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
+static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
{
- u8 r1;
- u8 r4;
-
- /*
- * We should never come here because rt2x00lib is supposed
- * to catch this and send us the correct antenna explicitely.
- */
- BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
- ant->tx == ANTENNA_SW_DIVERSITY);
-
- rt2400pci_bbp_read(rt2x00dev, 4, &r4);
- rt2400pci_bbp_read(rt2x00dev, 1, &r1);
-
- /*
- * Configure the TX antenna.
- */
- switch (ant->tx) {
- case ANTENNA_HW_DIVERSITY:
- rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1);
- break;
- case ANTENNA_A:
- rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2);
- break;
- }
-
- /*
- * Configure the RX antenna.
- */
- switch (ant->rx) {
- case ANTENNA_HW_DIVERSITY:
- rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1);
- break;
- case ANTENNA_A:
- rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2);
- break;
- }
+ u32 reg;
- rt2400pci_bbp_write(rt2x00dev, 4, r4);
- rt2400pci_bbp_write(rt2x00dev, 1, r1);
+ rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+ rt2x00_set_field32(&reg, CSR11_LONG_RETRY,
+ libconf->conf->long_frame_max_tx_count);
+ rt2x00_set_field32(&reg, CSR11_SHORT_RETRY,
+ libconf->conf->short_frame_max_tx_count);
+ rt2x00pci_register_write(rt2x00dev, CSR11, reg);
}
static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev,
@@ -517,20 +511,6 @@ static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
- rt2x00_set_field32(&reg, CSR11_SLOT_TIME, libconf->slot_time);
- rt2x00pci_register_write(rt2x00dev, CSR11, reg);
-
- rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
- rt2x00_set_field32(&reg, CSR18_SIFS, libconf->sifs);
- rt2x00_set_field32(&reg, CSR18_PIFS, libconf->pifs);
- rt2x00pci_register_write(rt2x00dev, CSR18, reg);
-
- rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
- rt2x00_set_field32(&reg, CSR19_DIFS, libconf->difs);
- rt2x00_set_field32(&reg, CSR19_EIFS, libconf->eifs);
- rt2x00pci_register_write(rt2x00dev, CSR19, reg);
-
rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
@@ -548,16 +528,14 @@ static void rt2400pci_config(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf,
const unsigned int flags)
{
- if (flags & CONFIG_UPDATE_PHYMODE)
- rt2400pci_config_phymode(rt2x00dev, libconf->basic_rates);
- if (flags & CONFIG_UPDATE_CHANNEL)
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt2400pci_config_channel(rt2x00dev, &libconf->rf);
- if (flags & CONFIG_UPDATE_TXPOWER)
+ if (flags & IEEE80211_CONF_CHANGE_POWER)
rt2400pci_config_txpower(rt2x00dev,
libconf->conf->power_level);
- if (flags & CONFIG_UPDATE_ANTENNA)
- rt2400pci_config_antenna(rt2x00dev, &libconf->ant);
- if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT))
+ if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ rt2400pci_config_retry_limit(rt2x00dev, libconf);
+ if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
rt2400pci_config_duration(rt2x00dev, libconf);
}
@@ -628,36 +606,47 @@ static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev)
/*
* Initialization functions.
*/
-static void rt2400pci_init_rxentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static bool rt2400pci_get_entry_state(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 2, &word);
- rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len);
- rt2x00_desc_write(entry_priv->desc, 2, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_desc_read(entry_priv->desc, 1, &word);
- rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
- rt2x00_desc_write(entry_priv->desc, 1, word);
+ return rt2x00_get_field32(word, RXD_W0_OWNER_NIC);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) ||
+ rt2x00_get_field32(word, TXD_W0_VALID));
+ }
}
-static void rt2400pci_init_txentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static void rt2400pci_clear_entry(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, TXD_W0_VALID, 0);
- rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 2, &word);
+ rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len);
+ rt2x00_desc_write(entry_priv->desc, 2, word);
+
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+ rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
+ rt2x00_desc_write(entry_priv->desc, 1, word);
+
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, TXD_W0_VALID, 0);
+ rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ }
}
static int rt2400pci_init_queues(struct rt2x00_dev *rt2x00dev)
@@ -1313,10 +1302,8 @@ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
*/
mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
- DECLARE_MAC_BUF(macbuf);
-
random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac));
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
}
rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
@@ -1504,20 +1491,6 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt2400pci_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retry)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u32 reg;
-
- rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
- rt2x00_set_field32(&reg, CSR11_LONG_RETRY, long_retry);
- rt2x00_set_field32(&reg, CSR11_SHORT_RETRY, short_retry);
- rt2x00pci_register_write(rt2x00dev, CSR11, reg);
-
- return 0;
-}
-
static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
@@ -1576,7 +1549,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
.config_interface = rt2x00mac_config_interface,
.configure_filter = rt2x00mac_configure_filter,
.get_stats = rt2x00mac_get_stats,
- .set_retry_limit = rt2400pci_set_retry_limit,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2400pci_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
@@ -1589,8 +1561,8 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
.probe_hw = rt2400pci_probe_hw,
.initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize,
- .init_rxentry = rt2400pci_init_rxentry,
- .init_txentry = rt2400pci_init_txentry,
+ .get_entry_state = rt2400pci_get_entry_state,
+ .clear_entry = rt2400pci_clear_entry,
.set_device_state = rt2400pci_set_device_state,
.rfkill_poll = rt2400pci_rfkill_poll,
.link_stats = rt2400pci_link_stats,
@@ -1604,6 +1576,7 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
.config_filter = rt2400pci_config_filter,
.config_intf = rt2400pci_config_intf,
.config_erp = rt2400pci_config_erp,
+ .config_ant = rt2400pci_config_ant,
.config = rt2400pci_config,
};
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h
index bbff381ce396..9aefda4ab3c2 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/rt2x00/rt2400pci.h
@@ -46,7 +46,9 @@
#define CSR_REG_SIZE 0x014c
#define EEPROM_BASE 0x0000
#define EEPROM_SIZE 0x0100
+#define BBP_BASE 0x0000
#define BBP_SIZE 0x0020
+#define RF_BASE 0x0000
#define RF_SIZE 0x0010
/*
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index ef42cc04a2d7..d3bc218ec85c 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -49,45 +49,33 @@
* the access attempt is considered to have failed,
* and we will print an error.
*/
-static u32 rt2500pci_bbp_check(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- unsigned int i;
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, BBPCSR, &reg);
- if (!rt2x00_get_field32(reg, BBPCSR_BUSY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- return reg;
-}
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg))
static void rt2500pci_bbp_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u8 value)
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2500pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Write failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the data into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- reg = 0;
- rt2x00_set_field32(&reg, BBPCSR_VALUE, value);
- rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
- rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
- rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 1);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBPCSR_VALUE, value);
+ rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
+ rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
+ rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 1);
+
+ rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ }
- rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -95,66 +83,58 @@ static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2500pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the request into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
*/
- reg = 0;
- rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
- rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
- rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 0);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBPCSR_REGNUM, word);
+ rt2x00_set_field32(&reg, BBPCSR_BUSY, 1);
+ rt2x00_set_field32(&reg, BBPCSR_WRITE_CONTROL, 0);
- rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
+ rt2x00pci_register_write(rt2x00dev, BBPCSR, reg);
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2500pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, BBPCSR_BUSY)) {
- ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n");
- *value = 0xff;
- return;
+ WAIT_FOR_BBP(rt2x00dev, &reg);
}
*value = rt2x00_get_field32(reg, BBPCSR_VALUE);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2500pci_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u32 value)
{
u32 reg;
- unsigned int i;
if (!word)
return;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, RFCSR, &reg);
- if (!rt2x00_get_field32(reg, RFCSR_BUSY))
- goto rf_write;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- ERROR(rt2x00dev, "RFCSR register busy. Write failed.\n");
- return;
+ mutex_lock(&rt2x00dev->csr_mutex);
-rf_write:
- reg = 0;
- rt2x00_set_field32(&reg, RFCSR_VALUE, value);
- rt2x00_set_field32(&reg, RFCSR_NUMBER_OF_BITS, 20);
- rt2x00_set_field32(&reg, RFCSR_IF_SELECT, 0);
- rt2x00_set_field32(&reg, RFCSR_BUSY, 1);
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, RFCSR_VALUE, value);
+ rt2x00_set_field32(&reg, RFCSR_NUMBER_OF_BITS, 20);
+ rt2x00_set_field32(&reg, RFCSR_IF_SELECT, 0);
+ rt2x00_set_field32(&reg, RFCSR_BUSY, 1);
+
+ rt2x00pci_register_write(rt2x00dev, RFCSR, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
- rt2x00pci_register_write(rt2x00dev, RFCSR, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
@@ -188,43 +168,34 @@ static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
}
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) )
-
-static void rt2500pci_read_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 *data)
-{
- rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data);
-}
-
-static void rt2500pci_write_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 data)
-{
- rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data);
-}
-
static const struct rt2x00debug rt2500pci_rt2x00debug = {
.owner = THIS_MODULE,
.csr = {
- .read = rt2500pci_read_csr,
- .write = rt2500pci_write_csr,
+ .read = rt2x00pci_register_read,
+ .write = rt2x00pci_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
.word_size = sizeof(u32),
.word_count = CSR_REG_SIZE / sizeof(u32),
},
.eeprom = {
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
.word_size = sizeof(u16),
.word_count = EEPROM_SIZE / sizeof(u16),
},
.bbp = {
.read = rt2500pci_bbp_read,
.write = rt2500pci_bbp_write,
+ .word_base = BBP_BASE,
.word_size = sizeof(u8),
.word_count = BBP_SIZE / sizeof(u8),
},
.rf = {
.read = rt2x00_rf_read,
.write = rt2500pci_rf_write,
+ .word_base = RF_BASE,
.word_size = sizeof(u32),
.word_count = RF_SIZE / sizeof(u32),
},
@@ -336,7 +307,7 @@ static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev,
/*
* Enable beacon config
*/
- bcn_preload = PREAMBLE + get_duration(IEEE80211_HEADER, 20);
+ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20);
rt2x00pci_register_read(rt2x00dev, BCNCSR1, &reg);
rt2x00_set_field32(&reg, BCNCSR1_PRELOAD, bcn_preload);
rt2x00_set_field32(&reg, BCNCSR1_BEACON_CWMIN, queue->cw_min);
@@ -382,32 +353,114 @@ static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
rt2x00pci_register_read(rt2x00dev, ARCSR2, &reg);
rt2x00_set_field32(&reg, ARCSR2_SIGNAL, 0x00);
rt2x00_set_field32(&reg, ARCSR2_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 10));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 10));
rt2x00pci_register_write(rt2x00dev, ARCSR2, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR3, &reg);
rt2x00_set_field32(&reg, ARCSR3_SIGNAL, 0x01 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR3_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 20));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 20));
rt2x00pci_register_write(rt2x00dev, ARCSR3, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR4, &reg);
rt2x00_set_field32(&reg, ARCSR4_SIGNAL, 0x02 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR4_SERVICE, 0x04);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 55));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 55));
rt2x00pci_register_write(rt2x00dev, ARCSR4, reg);
rt2x00pci_register_read(rt2x00dev, ARCSR5, &reg);
rt2x00_set_field32(&reg, ARCSR5_SIGNAL, 0x03 | preamble_mask);
rt2x00_set_field32(&reg, ARCSR5_SERVICE, 0x84);
- rt2x00_set_field32(&reg, ARCSR2_LENGTH, get_duration(ACK_SIZE, 110));
+ rt2x00_set_field32(&reg, ARCSR2_LENGTH, GET_DURATION(ACK_SIZE, 110));
rt2x00pci_register_write(rt2x00dev, ARCSR5, reg);
+
+ rt2x00pci_register_write(rt2x00dev, ARCSR1, erp->basic_rates);
+
+ rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+ rt2x00_set_field32(&reg, CSR11_SLOT_TIME, erp->slot_time);
+ rt2x00pci_register_write(rt2x00dev, CSR11, reg);
+
+ rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
+ rt2x00_set_field32(&reg, CSR18_SIFS, erp->sifs);
+ rt2x00_set_field32(&reg, CSR18_PIFS, erp->pifs);
+ rt2x00pci_register_write(rt2x00dev, CSR18, reg);
+
+ rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
+ rt2x00_set_field32(&reg, CSR19_DIFS, erp->difs);
+ rt2x00_set_field32(&reg, CSR19_EIFS, erp->eifs);
+ rt2x00pci_register_write(rt2x00dev, CSR19, reg);
}
-static void rt2500pci_config_phymode(struct rt2x00_dev *rt2x00dev,
- const int basic_rate_mask)
+static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
{
- rt2x00pci_register_write(rt2x00dev, ARCSR1, basic_rate_mask);
+ u32 reg;
+ u8 r14;
+ u8 r2;
+
+ /*
+ * We should never come here because rt2x00lib is supposed
+ * to catch this and send us the correct antenna explicitely.
+ */
+ BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
+ ant->tx == ANTENNA_SW_DIVERSITY);
+
+ rt2x00pci_register_read(rt2x00dev, BBPCSR1, &reg);
+ rt2500pci_bbp_read(rt2x00dev, 14, &r14);
+ rt2500pci_bbp_read(rt2x00dev, 2, &r2);
+
+ /*
+ * Configure the TX antenna.
+ */
+ switch (ant->tx) {
+ case ANTENNA_A:
+ rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0);
+ rt2x00_set_field32(&reg, BBPCSR1_CCK, 0);
+ rt2x00_set_field32(&reg, BBPCSR1_OFDM, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2);
+ rt2x00_set_field32(&reg, BBPCSR1_CCK, 2);
+ rt2x00_set_field32(&reg, BBPCSR1_OFDM, 2);
+ break;
+ }
+
+ /*
+ * Configure the RX antenna.
+ */
+ switch (ant->rx) {
+ case ANTENNA_A:
+ rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0);
+ break;
+ case ANTENNA_B:
+ default:
+ rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2);
+ break;
+ }
+
+ /*
+ * RT2525E and RT5222 need to flip TX I/Q
+ */
+ if (rt2x00_rf(&rt2x00dev->chip, RF2525E) ||
+ rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+ rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
+ rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 1);
+ rt2x00_set_field32(&reg, BBPCSR1_OFDM_FLIP, 1);
+
+ /*
+ * RT2525E does not need RX I/Q Flip.
+ */
+ if (rt2x00_rf(&rt2x00dev->chip, RF2525E))
+ rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
+ } else {
+ rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 0);
+ rt2x00_set_field32(&reg, BBPCSR1_OFDM_FLIP, 0);
+ }
+
+ rt2x00pci_register_write(rt2x00dev, BBPCSR1, reg);
+ rt2500pci_bbp_write(rt2x00dev, 14, r14);
+ rt2500pci_bbp_write(rt2x00dev, 2, r2);
}
static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev,
@@ -489,76 +542,17 @@ static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev,
rt2500pci_rf_write(rt2x00dev, 3, rf3);
}
-static void rt2500pci_config_antenna(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
+static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
{
u32 reg;
- u8 r14;
- u8 r2;
-
- /*
- * We should never come here because rt2x00lib is supposed
- * to catch this and send us the correct antenna explicitely.
- */
- BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY ||
- ant->tx == ANTENNA_SW_DIVERSITY);
-
- rt2x00pci_register_read(rt2x00dev, BBPCSR1, &reg);
- rt2500pci_bbp_read(rt2x00dev, 14, &r14);
- rt2500pci_bbp_read(rt2x00dev, 2, &r2);
-
- /*
- * Configure the TX antenna.
- */
- switch (ant->tx) {
- case ANTENNA_A:
- rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0);
- rt2x00_set_field32(&reg, BBPCSR1_CCK, 0);
- rt2x00_set_field32(&reg, BBPCSR1_OFDM, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2);
- rt2x00_set_field32(&reg, BBPCSR1_CCK, 2);
- rt2x00_set_field32(&reg, BBPCSR1_OFDM, 2);
- break;
- }
-
- /*
- * Configure the RX antenna.
- */
- switch (ant->rx) {
- case ANTENNA_A:
- rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0);
- break;
- case ANTENNA_B:
- default:
- rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2);
- break;
- }
-
- /*
- * RT2525E and RT5222 need to flip TX I/Q
- */
- if (rt2x00_rf(&rt2x00dev->chip, RF2525E) ||
- rt2x00_rf(&rt2x00dev->chip, RF5222)) {
- rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
- rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 1);
- rt2x00_set_field32(&reg, BBPCSR1_OFDM_FLIP, 1);
- /*
- * RT2525E does not need RX I/Q Flip.
- */
- if (rt2x00_rf(&rt2x00dev->chip, RF2525E))
- rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
- } else {
- rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 0);
- rt2x00_set_field32(&reg, BBPCSR1_OFDM_FLIP, 0);
- }
-
- rt2x00pci_register_write(rt2x00dev, BBPCSR1, reg);
- rt2500pci_bbp_write(rt2x00dev, 14, r14);
- rt2500pci_bbp_write(rt2x00dev, 2, r2);
+ rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
+ rt2x00_set_field32(&reg, CSR11_LONG_RETRY,
+ libconf->conf->long_frame_max_tx_count);
+ rt2x00_set_field32(&reg, CSR11_SHORT_RETRY,
+ libconf->conf->short_frame_max_tx_count);
+ rt2x00pci_register_write(rt2x00dev, CSR11, reg);
}
static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev,
@@ -566,20 +560,6 @@ static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
- rt2x00_set_field32(&reg, CSR11_SLOT_TIME, libconf->slot_time);
- rt2x00pci_register_write(rt2x00dev, CSR11, reg);
-
- rt2x00pci_register_read(rt2x00dev, CSR18, &reg);
- rt2x00_set_field32(&reg, CSR18_SIFS, libconf->sifs);
- rt2x00_set_field32(&reg, CSR18_PIFS, libconf->pifs);
- rt2x00pci_register_write(rt2x00dev, CSR18, reg);
-
- rt2x00pci_register_read(rt2x00dev, CSR19, &reg);
- rt2x00_set_field32(&reg, CSR19_DIFS, libconf->difs);
- rt2x00_set_field32(&reg, CSR19_EIFS, libconf->eifs);
- rt2x00pci_register_write(rt2x00dev, CSR19, reg);
-
rt2x00pci_register_read(rt2x00dev, TXCSR1, &reg);
rt2x00_set_field32(&reg, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
rt2x00_set_field32(&reg, TXCSR1_AUTORESPONDER, 1);
@@ -597,17 +577,16 @@ static void rt2500pci_config(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf,
const unsigned int flags)
{
- if (flags & CONFIG_UPDATE_PHYMODE)
- rt2500pci_config_phymode(rt2x00dev, libconf->basic_rates);
- if (flags & CONFIG_UPDATE_CHANNEL)
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt2500pci_config_channel(rt2x00dev, &libconf->rf,
libconf->conf->power_level);
- if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL))
+ if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
+ !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
rt2500pci_config_txpower(rt2x00dev,
libconf->conf->power_level);
- if (flags & CONFIG_UPDATE_ANTENNA)
- rt2500pci_config_antenna(rt2x00dev, &libconf->ant);
- if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT))
+ if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ rt2500pci_config_retry_limit(rt2x00dev, libconf);
+ if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
rt2500pci_config_duration(rt2x00dev, libconf);
}
@@ -723,32 +702,43 @@ dynamic_cca_tune:
/*
* Initialization functions.
*/
-static void rt2500pci_init_rxentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static bool rt2500pci_get_entry_state(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 1, &word);
- rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
- rt2x00_desc_write(entry_priv->desc, 1, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+
+ return rt2x00_get_field32(word, RXD_W0_OWNER_NIC);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) ||
+ rt2x00_get_field32(word, TXD_W0_VALID));
+ }
}
-static void rt2500pci_init_txentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static void rt2500pci_clear_entry(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, TXD_W0_VALID, 0);
- rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+ rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma);
+ rt2x00_desc_write(entry_priv->desc, 1, word);
+
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, TXD_W0_VALID, 0);
+ rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ }
}
static int rt2500pci_init_queues(struct rt2x00_dev *rt2x00dev)
@@ -1451,11 +1441,8 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
*/
mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
- DECLARE_MAC_BUF(macbuf);
-
random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %s\n",
- print_mac(macbuf, mac));
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
}
rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
@@ -1830,20 +1817,6 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt2500pci_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retry)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u32 reg;
-
- rt2x00pci_register_read(rt2x00dev, CSR11, &reg);
- rt2x00_set_field32(&reg, CSR11_LONG_RETRY, long_retry);
- rt2x00_set_field32(&reg, CSR11_SHORT_RETRY, short_retry);
- rt2x00pci_register_write(rt2x00dev, CSR11, reg);
-
- return 0;
-}
-
static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
@@ -1877,7 +1850,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
.config_interface = rt2x00mac_config_interface,
.configure_filter = rt2x00mac_configure_filter,
.get_stats = rt2x00mac_get_stats,
- .set_retry_limit = rt2500pci_set_retry_limit,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
@@ -1890,8 +1862,8 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
.probe_hw = rt2500pci_probe_hw,
.initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize,
- .init_rxentry = rt2500pci_init_rxentry,
- .init_txentry = rt2500pci_init_txentry,
+ .get_entry_state = rt2500pci_get_entry_state,
+ .clear_entry = rt2500pci_clear_entry,
.set_device_state = rt2500pci_set_device_state,
.rfkill_poll = rt2500pci_rfkill_poll,
.link_stats = rt2500pci_link_stats,
@@ -1905,6 +1877,7 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
.config_filter = rt2500pci_config_filter,
.config_intf = rt2500pci_config_intf,
.config_erp = rt2500pci_config_erp,
+ .config_ant = rt2500pci_config_ant,
.config = rt2500pci_config,
};
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h
index 8c26bef6cf49..e135247f7f89 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.h
+++ b/drivers/net/wireless/rt2x00/rt2500pci.h
@@ -57,7 +57,9 @@
#define CSR_REG_SIZE 0x0174
#define EEPROM_BASE 0x0000
#define EEPROM_SIZE 0x0200
+#define BBP_BASE 0x0000
#define BBP_SIZE 0x0040
+#define RF_BASE 0x0000
#define RF_SIZE 0x0014
/*
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index d3bf7bba611a..30028e2422fc 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -36,6 +36,13 @@
#include "rt2500usb.h"
/*
+ * Allow hardware encryption to be disabled.
+ */
+static int modparam_nohwcrypt = 1;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+/*
* Register access.
* All access to the CSR registers will go through the methods
* rt2500usb_register_read and rt2500usb_register_write.
@@ -47,7 +54,7 @@
* between each attampt. When the busy bit is still set at that time,
* the access attempt is considered to have failed,
* and we will print an error.
- * If the usb_cache_mutex is already held then the _lock variants must
+ * If the csr_mutex is already held then the _lock variants must
* be used instead.
*/
static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
@@ -57,7 +64,7 @@ static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
__le16 reg;
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(u16), REGISTER_TIMEOUT);
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
*value = le16_to_cpu(reg);
}
@@ -68,7 +75,7 @@ static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev,
__le16 reg;
rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(u16), REGISTER_TIMEOUT);
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
*value = le16_to_cpu(reg);
}
@@ -89,7 +96,7 @@ static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
__le16 reg = cpu_to_le16(value);
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(u16), REGISTER_TIMEOUT);
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
}
static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
@@ -99,7 +106,7 @@ static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
__le16 reg = cpu_to_le16(value);
rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(u16), REGISTER_TIMEOUT);
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
}
static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
@@ -112,53 +119,53 @@ static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
REGISTER_TIMEOUT16(length));
}
-static u16 rt2500usb_bbp_check(struct rt2x00_dev *rt2x00dev)
+static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ struct rt2x00_field16 field,
+ u16 *reg)
{
- u16 reg;
unsigned int i;
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2500usb_register_read_lock(rt2x00dev, PHY_CSR8, &reg);
- if (!rt2x00_get_field16(reg, PHY_CSR8_BUSY))
- break;
+ rt2500usb_register_read_lock(rt2x00dev, offset, reg);
+ if (!rt2x00_get_field16(*reg, field))
+ return 1;
udelay(REGISTER_BUSY_DELAY);
}
- return reg;
+ ERROR(rt2x00dev, "Indirect register access failed: "
+ "offset=0x%.08x, value=0x%.08x\n", offset, *reg);
+ *reg = ~0;
+
+ return 0;
}
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg))
+
static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u8 value)
{
u16 reg;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Wait until the BBP becomes ready.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
- goto exit_fail;
-
- /*
- * Write the data into the BBP.
- */
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR7_DATA, value);
- rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
- rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 0);
-
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
-
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-
- return;
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR7_DATA, value);
+ rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
+ rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 0);
-exit_fail:
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
+ }
- ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n");
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -166,122 +173,107 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev,
{
u16 reg;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Wait until the BBP becomes ready.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
*/
- reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
- goto exit_fail;
-
- /*
- * Write the request into the BBP.
- */
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
- rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 1);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR7_REG_ID, word);
+ rt2x00_set_field16(&reg, PHY_CSR7_READ_CONTROL, 1);
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg);
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt2500usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field16(reg, PHY_CSR8_BUSY))
- goto exit_fail;
+ if (WAIT_FOR_BBP(rt2x00dev, &reg))
+ rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, &reg);
+ }
- rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, &reg);
*value = rt2x00_get_field16(reg, PHY_CSR7_DATA);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-
- return;
-
-exit_fail:
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-
- ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n");
- *value = 0xff;
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u32 value)
{
u16 reg;
- unsigned int i;
if (!word)
return;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2500usb_register_read_lock(rt2x00dev, PHY_CSR10, &reg);
- if (!rt2x00_get_field16(reg, PHY_CSR10_RF_BUSY))
- goto rf_write;
- udelay(REGISTER_BUSY_DELAY);
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- ERROR(rt2x00dev, "PHY_CSR10 register busy. Write failed.\n");
- return;
-
-rf_write:
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR9_RF_VALUE, value);
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg);
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR9_RF_VALUE, value);
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg);
- reg = 0;
- rt2x00_set_field16(&reg, PHY_CSR10_RF_VALUE, value >> 16);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_NUMBER_OF_BITS, 20);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_IF_SELECT, 0);
- rt2x00_set_field16(&reg, PHY_CSR10_RF_BUSY, 1);
+ reg = 0;
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_VALUE, value >> 16);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_NUMBER_OF_BITS, 20);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_IF_SELECT, 0);
+ rt2x00_set_field16(&reg, PHY_CSR10_RF_BUSY, 1);
- rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
+ rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u16)) )
-
-static void rt2500usb_read_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 *data)
+static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
{
- rt2500usb_register_read(rt2x00dev, CSR_OFFSET(word), (u16 *) data);
+ rt2500usb_register_read(rt2x00dev, offset, (u16 *)value);
}
-static void rt2500usb_write_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 data)
+static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
{
- rt2500usb_register_write(rt2x00dev, CSR_OFFSET(word), data);
+ rt2500usb_register_write(rt2x00dev, offset, value);
}
static const struct rt2x00debug rt2500usb_rt2x00debug = {
.owner = THIS_MODULE,
.csr = {
- .read = rt2500usb_read_csr,
- .write = rt2500usb_write_csr,
+ .read = _rt2500usb_register_read,
+ .write = _rt2500usb_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
.word_size = sizeof(u16),
.word_count = CSR_REG_SIZE / sizeof(u16),
},
.eeprom = {
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
.word_size = sizeof(u16),
.word_count = EEPROM_SIZE / sizeof(u16),
},
.bbp = {
.read = rt2500usb_bbp_read,
.write = rt2500usb_bbp_write,
+ .word_base = BBP_BASE,
.word_size = sizeof(u8),
.word_count = BBP_SIZE / sizeof(u8),
},
.rf = {
.read = rt2x00_rf_read,
.write = rt2500usb_rf_write,
+ .word_base = RF_BASE,
.word_size = sizeof(u32),
.word_count = RF_SIZE / sizeof(u32),
},
@@ -338,6 +330,82 @@ static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev,
/*
* Configuration handlers.
*/
+
+/*
+ * rt2500usb does not differentiate between shared and pairwise
+ * keys, so we should use the same function for both key types.
+ */
+static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
+ int timeout;
+ u32 mask;
+ u16 reg;
+
+ if (crypto->cmd == SET_KEY) {
+ /*
+ * Pairwise key will always be entry 0, but this
+ * could collide with a shared key on the same
+ * position...
+ */
+ mask = TXRX_CSR0_KEY_ID.bit_mask;
+
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ reg &= mask;
+
+ if (reg && reg == mask)
+ return -ENOSPC;
+
+ reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
+
+ key->hw_key_idx += reg ? ffz(reg) : 0;
+
+ /*
+ * The encryption key doesn't fit within the CSR cache,
+ * this means we should allocate it seperately and use
+ * rt2x00usb_vendor_request() to send the key to the hardware.
+ */
+ reg = KEY_ENTRY(key->hw_key_idx);
+ timeout = REGISTER_TIMEOUT32(sizeof(crypto->key));
+ rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, reg,
+ crypto->key,
+ sizeof(crypto->key),
+ timeout);
+
+ /*
+ * The driver does not support the IV/EIV generation
+ * in hardware. However it doesn't support the IV/EIV
+ * inside the ieee80211 frame either, but requires it
+ * to be provided seperately for the descriptor.
+ * rt2x00lib will cut the IV/EIV data out of all frames
+ * given to us by mac80211, but we must tell mac80211
+ * to generate the IV/EIV data.
+ */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ }
+
+ /*
+ * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate
+ * a particular key is valid.
+ */
+ rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00_set_field16(&reg, TXRX_CSR0_ALGORITHM, crypto->cipher);
+ rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
+
+ mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID);
+ if (crypto->cmd == SET_KEY)
+ mask |= 1 << key->hw_key_idx;
+ else if (crypto->cmd == DISABLE_KEY)
+ mask &= ~(1 << key->hw_key_idx);
+ rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, mask);
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+
+ return 0;
+}
+
static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev,
const unsigned int filter_flags)
{
@@ -380,7 +448,7 @@ static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,
/*
* Enable beacon config
*/
- bcn_preload = PREAMBLE + get_duration(IEEE80211_HEADER, 20);
+ bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20);
rt2500usb_register_read(rt2x00dev, TXRX_CSR20, &reg);
rt2x00_set_field16(&reg, TXRX_CSR20_OFFSET, bcn_preload >> 6);
rt2x00_set_field16(&reg, TXRX_CSR20_BCN_EXPECT_WINDOW,
@@ -423,57 +491,16 @@ static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field16(&reg, TXRX_CSR10_AUTORESPOND_PREAMBLE,
!!erp->short_preamble);
rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg);
-}
-
-static void rt2500usb_config_phymode(struct rt2x00_dev *rt2x00dev,
- const int basic_rate_mask)
-{
- rt2500usb_register_write(rt2x00dev, TXRX_CSR11, basic_rate_mask);
-}
-
-static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev,
- struct rf_channel *rf, const int txpower)
-{
- /*
- * Set TXpower.
- */
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
-
- /*
- * For RT2525E we should first set the channel to half band higher.
- */
- if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) {
- static const u32 vals[] = {
- 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2,
- 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba,
- 0x000008ba, 0x000008be, 0x000008b7, 0x00000902,
- 0x00000902, 0x00000906
- };
-
- rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]);
- if (rf->rf4)
- rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
- }
-
- rt2500usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt2500usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt2500usb_rf_write(rt2x00dev, 3, rf->rf3);
- if (rf->rf4)
- rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
-}
-static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev,
- const int txpower)
-{
- u32 rf3;
+ rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates);
- rt2x00_rf_read(rt2x00dev, 3, &rf3);
- rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
- rt2500usb_rf_write(rt2x00dev, 3, rf3);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
}
-static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
+static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
{
u8 r2;
u8 r14;
@@ -555,15 +582,52 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev,
rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6);
}
+static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev,
+ struct rf_channel *rf, const int txpower)
+{
+ /*
+ * Set TXpower.
+ */
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+
+ /*
+ * For RT2525E we should first set the channel to half band higher.
+ */
+ if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) {
+ static const u32 vals[] = {
+ 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2,
+ 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba,
+ 0x000008ba, 0x000008be, 0x000008b7, 0x00000902,
+ 0x00000902, 0x00000906
+ };
+
+ rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]);
+ if (rf->rf4)
+ rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
+ }
+
+ rt2500usb_rf_write(rt2x00dev, 1, rf->rf1);
+ rt2500usb_rf_write(rt2x00dev, 2, rf->rf2);
+ rt2500usb_rf_write(rt2x00dev, 3, rf->rf3);
+ if (rf->rf4)
+ rt2500usb_rf_write(rt2x00dev, 4, rf->rf4);
+}
+
+static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev,
+ const int txpower)
+{
+ u32 rf3;
+
+ rt2x00_rf_read(rt2x00dev, 3, &rf3);
+ rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+ rt2500usb_rf_write(rt2x00dev, 3, rf3);
+}
+
static void rt2500usb_config_duration(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf)
{
u16 reg;
- rt2500usb_register_write(rt2x00dev, MAC_CSR10, libconf->slot_time);
- rt2500usb_register_write(rt2x00dev, MAC_CSR11, libconf->sifs);
- rt2500usb_register_write(rt2x00dev, MAC_CSR12, libconf->eifs);
-
rt2500usb_register_read(rt2x00dev, TXRX_CSR18, &reg);
rt2x00_set_field16(&reg, TXRX_CSR18_INTERVAL,
libconf->conf->beacon_int * 4);
@@ -574,17 +638,14 @@ static void rt2500usb_config(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf,
const unsigned int flags)
{
- if (flags & CONFIG_UPDATE_PHYMODE)
- rt2500usb_config_phymode(rt2x00dev, libconf->basic_rates);
- if (flags & CONFIG_UPDATE_CHANNEL)
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt2500usb_config_channel(rt2x00dev, &libconf->rf,
libconf->conf->power_level);
- if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL))
+ if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
+ !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
rt2500usb_config_txpower(rt2x00dev,
libconf->conf->power_level);
- if (flags & CONFIG_UPDATE_ANTENNA)
- rt2500usb_config_antenna(rt2x00dev, &libconf->ant);
- if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT))
+ if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
rt2500usb_config_duration(rt2x00dev, libconf);
}
@@ -866,7 +927,7 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev)
rt2500usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field16(&reg, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER);
- rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, 0xff);
+ rt2x00_set_field16(&reg, TXRX_CSR0_KEY_ID, 0);
rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg);
rt2500usb_register_read(rt2x00dev, MAC_CSR18, &reg);
@@ -1088,7 +1149,7 @@ static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
* Start writing the descriptor words.
*/
rt2x00_desc_read(txd, 1, &word);
- rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER);
+ rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset);
rt2x00_set_field32(&word, TXD_W1_AIFS, txdesc->aifs);
rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min);
rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max);
@@ -1101,6 +1162,11 @@ static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, txdesc->length_high);
rt2x00_desc_write(txd, 2, word);
+ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) {
+ _rt2x00_desc_write(txd, 3, skbdesc->iv[0]);
+ _rt2x00_desc_write(txd, 4, skbdesc->iv[1]);
+ }
+
rt2x00_desc_read(txd, 0, &word);
rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit);
rt2x00_set_field32(&word, TXD_W0_MORE_FRAG,
@@ -1115,7 +1181,8 @@ static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags));
rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs);
rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len);
- rt2x00_set_field32(&word, TXD_W0_CIPHER, CIPHER_NONE);
+ rt2x00_set_field32(&word, TXD_W0_CIPHER, txdesc->cipher);
+ rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx);
rt2x00_desc_write(txd, 0, word);
}
@@ -1130,7 +1197,7 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
- int pipe = usb_sndbulkpipe(usb_dev, 1);
+ int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint);
int length;
u16 reg;
@@ -1156,7 +1223,7 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
* length of the data to usb_fill_bulk_urb. Pass the skb
* to the driver to determine what the length should be.
*/
- length = rt2x00dev->ops->lib->get_tx_data_len(rt2x00dev, entry->skb);
+ length = rt2x00dev->ops->lib->get_tx_data_len(entry);
usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe,
entry->skb->data, length, rt2500usb_beacondone,
@@ -1178,8 +1245,7 @@ static void rt2500usb_write_beacon(struct queue_entry *entry)
usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC);
}
-static int rt2500usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb)
+static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
{
int length;
@@ -1187,8 +1253,8 @@ static int rt2500usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev,
* The length _must_ be a multiple of 2,
* but it must _not_ be a multiple of the USB packet size.
*/
- length = roundup(skb->len, 2);
- length += (2 * !(length % rt2x00dev->usb_maxpacket));
+ length = roundup(entry->skb->len, 2);
+ length += (2 * !(length % entry->queue->usb_maxpacket));
return length;
}
@@ -1227,6 +1293,7 @@ static void rt2500usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
static void rt2500usb_fill_rxdone(struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc)
{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct queue_entry_priv_usb *entry_priv = entry->priv_data;
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
__le32 *rxd =
@@ -1254,6 +1321,33 @@ static void rt2500usb_fill_rxdone(struct queue_entry *entry,
if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR))
rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC;
+ if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
+ rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER);
+ if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR))
+ rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY;
+ }
+
+ if (rxdesc->cipher != CIPHER_NONE) {
+ _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
+ _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_IV;
+
+ /* ICV is located at the end of frame */
+
+ /*
+ * Hardware has stripped IV/EIV data from 802.11 frame during
+ * decryption. It has provided the data seperately but rt2x00lib
+ * should decide if it should be reinserted.
+ */
+ rxdesc->flags |= RX_FLAG_IV_STRIPPED;
+ if (rxdesc->cipher != CIPHER_TKIP)
+ rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
+ if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
+ rxdesc->flags |= RX_FLAG_DECRYPTED;
+ else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
+ rxdesc->flags |= RX_FLAG_MMIC_ERROR;
+ }
+
/*
* Obtain the status about this packet.
* When frame was received with an OFDM bitrate,
@@ -1261,8 +1355,8 @@ static void rt2500usb_fill_rxdone(struct queue_entry *entry,
* a CCK bitrate the signal is the rate in 100kbit/s.
*/
rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL);
- rxdesc->rssi = rt2x00_get_field32(word1, RXD_W1_RSSI) -
- entry->queue->rt2x00dev->rssi_offset;
+ rxdesc->rssi =
+ rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset;
rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT);
if (rt2x00_get_field32(word0, RXD_W0_OFDM))
@@ -1319,10 +1413,8 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
*/
mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
- DECLARE_MAC_BUF(macbuf);
-
random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac));
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
}
rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
@@ -1752,6 +1844,10 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_ATIM_QUEUE, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags);
+ if (!modparam_nohwcrypt) {
+ __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
+ __set_bit(CONFIG_CRYPTO_COPY_IV, &rt2x00dev->flags);
+ }
__set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags);
/*
@@ -1771,6 +1867,7 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
.config = rt2x00mac_config,
.config_interface = rt2x00mac_config_interface,
.configure_filter = rt2x00mac_configure_filter,
+ .set_key = rt2x00mac_set_key,
.get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx,
@@ -1781,8 +1878,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.probe_hw = rt2500usb_probe_hw,
.initialize = rt2x00usb_initialize,
.uninitialize = rt2x00usb_uninitialize,
- .init_rxentry = rt2x00usb_init_rxentry,
- .init_txentry = rt2x00usb_init_txentry,
+ .clear_entry = rt2x00usb_clear_entry,
.set_device_state = rt2500usb_set_device_state,
.link_stats = rt2500usb_link_stats,
.reset_tuner = rt2500usb_reset_tuner,
@@ -1793,9 +1889,12 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.get_tx_data_len = rt2500usb_get_tx_data_len,
.kick_tx_queue = rt2500usb_kick_tx_queue,
.fill_rxdone = rt2500usb_fill_rxdone,
+ .config_shared_key = rt2500usb_config_key,
+ .config_pairwise_key = rt2500usb_config_key,
.config_filter = rt2500usb_config_filter,
.config_intf = rt2500usb_config_intf,
.config_erp = rt2500usb_config_erp,
+ .config_ant = rt2500usb_config_ant,
.config = rt2500usb_config,
};
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h
index 89e5ed24e4f7..4347dfdabcd4 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/rt2x00/rt2500usb.h
@@ -57,7 +57,9 @@
#define CSR_REG_SIZE 0x0100
#define EEPROM_BASE 0x0000
#define EEPROM_SIZE 0x006a
+#define BBP_BASE 0x0000
#define BBP_SIZE 0x0060
+#define RF_BASE 0x0000
#define RF_SIZE 0x0014
/*
@@ -445,6 +447,9 @@
#define SEC_CSR30 0x04bc
#define SEC_CSR31 0x04be
+#define KEY_ENTRY(__idx) \
+ ( SEC_CSR0 + ((__idx) * 16) )
+
/*
* PHY control registers.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 1359a3768404..39ecf3b82ca1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -44,7 +44,7 @@
/*
* Module information.
*/
-#define DRV_VERSION "2.2.1"
+#define DRV_VERSION "2.2.3"
#define DRV_PROJECT "http://rt2x00.serialmonkey.com"
/*
@@ -92,6 +92,16 @@
DEBUG_PRINTK(__dev, KERN_DEBUG, "EEPROM recovery", __msg, ##__args)
/*
+ * Duration calculations
+ * The rate variable passed is: 100kbs.
+ * To convert from bytes to bits we multiply size with 8,
+ * then the size is multiplied with 10 to make the
+ * real rate -> rate argument correction.
+ */
+#define GET_DURATION(__size, __rate) (((__size) * 8 * 10) / (__rate))
+#define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate))
+
+/*
* Standard timing and size defines.
* These values should follow the ieee80211 specifications.
*/
@@ -109,9 +119,9 @@
#define DIFS ( PIFS + SLOT_TIME )
#define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME )
#define EIFS ( SIFS + DIFS + \
- (8 * (IEEE80211_HEADER + ACK_SIZE)) )
+ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) )
#define SHORT_EIFS ( SIFS + SHORT_DIFS + \
- (8 * (IEEE80211_HEADER + ACK_SIZE)) )
+ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) )
/*
* Chipset identification
@@ -348,13 +358,6 @@ struct rt2x00_intf {
spinlock_t lock;
/*
- * BSS configuration. Copied from the structure
- * passed to us through the bss_info_changed()
- * callback funtion.
- */
- struct ieee80211_bss_conf conf;
-
- /*
* MAC of the device.
*/
u8 mac[ETH_ALEN];
@@ -433,18 +436,6 @@ struct rt2x00lib_conf {
struct rf_channel rf;
struct channel_info channel;
-
- struct antenna_setup ant;
-
- enum ieee80211_band band;
-
- u32 basic_rates;
- u32 slot_time;
-
- short sifs;
- short pifs;
- short difs;
- short eifs;
};
/*
@@ -456,6 +447,15 @@ struct rt2x00lib_erp {
int ack_timeout;
int ack_consume_time;
+
+ u64 basic_rates;
+
+ int slot_time;
+
+ short sifs;
+ short pifs;
+ short difs;
+ short eifs;
};
/*
@@ -533,10 +533,8 @@ struct rt2x00lib_ops {
/*
* queue initialization handlers
*/
- void (*init_rxentry) (struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry);
- void (*init_txentry) (struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry);
+ bool (*get_entry_state) (struct queue_entry *entry);
+ void (*clear_entry) (struct queue_entry *entry);
/*
* Radio control handlers.
@@ -557,8 +555,7 @@ struct rt2x00lib_ops {
struct txentry_desc *txdesc);
int (*write_tx_data) (struct queue_entry *entry);
void (*write_beacon) (struct queue_entry *entry);
- int (*get_tx_data_len) (struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb);
+ int (*get_tx_data_len) (struct queue_entry *entry);
void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev,
const enum data_queue_qid queue);
@@ -589,16 +586,11 @@ struct rt2x00lib_ops {
void (*config_erp) (struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_erp *erp);
+ void (*config_ant) (struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant);
void (*config) (struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf,
- const unsigned int flags);
-#define CONFIG_UPDATE_PHYMODE ( 1 << 1 )
-#define CONFIG_UPDATE_CHANNEL ( 1 << 2 )
-#define CONFIG_UPDATE_TXPOWER ( 1 << 3 )
-#define CONFIG_UPDATE_ANTENNA ( 1 << 4 )
-#define CONFIG_UPDATE_SLOT_TIME ( 1 << 5 )
-#define CONFIG_UPDATE_BEACON_INT ( 1 << 6 )
-#define CONFIG_UPDATE_ALL 0xffff
+ const unsigned int changed_flags);
};
/*
@@ -661,6 +653,7 @@ enum rt2x00_flags {
CONFIG_EXTERNAL_LNA_BG,
CONFIG_DOUBLE_ANTENNA,
CONFIG_DISABLE_LINK_TUNING,
+ CONFIG_CRYPTO_COPY_IV,
};
/*
@@ -738,8 +731,7 @@ struct rt2x00_dev {
/*
* This is the default TX/RX antenna setup as indicated
- * by the device's EEPROM. When mac80211 sets its
- * antenna value to 0 we should be using these values.
+ * by the device's EEPROM.
*/
struct antenna_setup default_ant;
@@ -754,16 +746,15 @@ struct rt2x00_dev {
} csr;
/*
- * Mutex to protect register accesses on USB devices.
- * There are 2 reasons this is needed, one is to ensure
- * use of the csr_cache (for USB devices) by one thread
- * isn't corrupted by another thread trying to access it.
- * The other is that access to BBP and RF registers
- * require multiple BUS transactions and if another thread
- * attempted to access one of those registers at the same
- * time one of the writes could silently fail.
+ * Mutex to protect register accesses.
+ * For PCI and USB devices it protects against concurrent indirect
+ * register access (BBP, RF, MCU) since accessing those
+ * registers require multiple calls to the CSR registers.
+ * For USB devices it also protects the csr_cache since that
+ * field is used for normal CSR access and it cannot support
+ * multiple callers simultaneously.
*/
- struct mutex usb_cache_mutex;
+ struct mutex csr_mutex;
/*
* Current packet filter configuration for the device.
@@ -808,14 +799,15 @@ struct rt2x00_dev {
short lna_gain;
/*
- * USB Max frame size (for rt2500usb & rt73usb).
+ * Current TX power value.
*/
- u16 usb_maxpacket;
+ u16 tx_power;
/*
- * Current TX power value.
+ * Current retry values.
*/
- u16 tx_power;
+ u8 short_retry;
+ u8 long_retry;
/*
* Rssi <-> Dbm offset
@@ -938,23 +930,6 @@ static inline u16 rt2x00_check_rev(const struct rt2x00_chip *chipset,
!!(chipset->rev & 0x0000f));
}
-/*
- * Duration calculations
- * The rate variable passed is: 100kbs.
- * To convert from bytes to bits we multiply size with 8,
- * then the size is multiplied with 10 to make the
- * real rate -> rate argument correction.
- */
-static inline u16 get_duration(const unsigned int size, const u8 rate)
-{
- return ((size * 8 * 10) / rate);
-}
-
-static inline u16 get_duration_res(const unsigned int size, const u8 rate)
-{
- return ((size * 8 * 10) % rate);
-}
-
/**
* rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
* @rt2x00dev: Pointer to &struct rt2x00_dev.
@@ -997,7 +972,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
-int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed);
int rt2x00mac_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf);
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 4d5e87b015a0..e66fb316cd61 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -86,13 +86,14 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
erp.short_preamble = bss_conf->use_short_preamble;
erp.cts_protection = bss_conf->use_cts_prot;
- erp.ack_timeout = PLCP + get_duration(ACK_SIZE, 10);
- erp.ack_consume_time = SIFS + PLCP + get_duration(ACK_SIZE, 10);
+ erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME;
+ erp.sifs = SIFS;
+ erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS;
+ erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS;
+ erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS;
- if (rt2x00dev->hw->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME)
- erp.ack_timeout += SHORT_DIFS;
- else
- erp.ack_timeout += DIFS;
+ erp.ack_timeout = PLCP + erp.difs + GET_DURATION(ACK_SIZE, 10);
+ erp.ack_consume_time = SIFS + PLCP + GET_DURATION(ACK_SIZE, 10);
if (bss_conf->use_short_preamble) {
erp.ack_timeout += SHORT_PREAMBLE;
@@ -102,19 +103,39 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
erp.ack_consume_time += PREAMBLE;
}
+ erp.basic_rates = bss_conf->basic_rates;
+
rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp);
}
+static inline
+enum antenna rt2x00lib_config_antenna_check(enum antenna current_ant,
+ enum antenna default_ant)
+{
+ if (current_ant != ANTENNA_SW_DIVERSITY)
+ return current_ant;
+ return (default_ant != ANTENNA_SW_DIVERSITY) ? default_ant : ANTENNA_B;
+}
+
void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
- enum antenna rx, enum antenna tx)
+ struct antenna_setup *ant)
{
- struct rt2x00lib_conf libconf;
+ struct antenna_setup *def = &rt2x00dev->default_ant;
+ struct antenna_setup *active = &rt2x00dev->link.ant.active;
- libconf.ant.rx = rx;
- libconf.ant.tx = tx;
+ /*
+ * Failsafe: Make sure we are not sending the
+ * ANTENNA_SW_DIVERSITY state to the driver.
+ * If that happes fallback to hardware default,
+ * or our own default.
+ * The calls to rt2x00lib_config_antenna_check()
+ * might have caused that we restore back to the already
+ * active setting. If that has happened we can quit.
+ */
+ ant->rx = rt2x00lib_config_antenna_check(ant->rx, def->rx);
+ ant->tx = rt2x00lib_config_antenna_check(ant->tx, def->tx);
- if (rx == rt2x00dev->link.ant.active.rx &&
- tx == rt2x00dev->link.ant.active.tx)
+ if (ant->rx == active->rx && ant->tx == active->tx)
return;
/*
@@ -129,119 +150,28 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
* The latter is required since we need to recalibrate the
* noise-sensitivity ratio for the new setup.
*/
- rt2x00dev->ops->lib->config(rt2x00dev, &libconf, CONFIG_UPDATE_ANTENNA);
+ rt2x00dev->ops->lib->config_ant(rt2x00dev, ant);
+
rt2x00lib_reset_link_tuner(rt2x00dev);
rt2x00_reset_link_ant_rssi(&rt2x00dev->link);
- rt2x00dev->link.ant.active.rx = libconf.ant.rx;
- rt2x00dev->link.ant.active.tx = libconf.ant.tx;
+ memcpy(active, ant, sizeof(*ant));
if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON_LINK);
}
-static u32 rt2x00lib_get_basic_rates(struct ieee80211_supported_band *band)
-{
- const struct rt2x00_rate *rate;
- unsigned int i;
- u32 mask = 0;
-
- for (i = 0; i < band->n_bitrates; i++) {
- rate = rt2x00_get_rate(band->bitrates[i].hw_value);
- if (rate->flags & DEV_RATE_BASIC)
- mask |= rate->ratemask;
- }
-
- return mask;
-}
-
void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_conf *conf, const int force_config)
+ struct ieee80211_conf *conf,
+ unsigned int ieee80211_flags)
{
struct rt2x00lib_conf libconf;
- struct ieee80211_supported_band *band;
- struct antenna_setup *default_ant = &rt2x00dev->default_ant;
- struct antenna_setup *active_ant = &rt2x00dev->link.ant.active;
- int flags = 0;
- int short_slot_time;
-
- /*
- * In some situations we want to force all configurations
- * to be reloaded (When resuming for instance).
- */
- if (force_config) {
- flags = CONFIG_UPDATE_ALL;
- goto config;
- }
- /*
- * Check which configuration options have been
- * updated and should be send to the device.
- */
- if (rt2x00dev->rx_status.band != conf->channel->band)
- flags |= CONFIG_UPDATE_PHYMODE;
- if (rt2x00dev->rx_status.freq != conf->channel->center_freq)
- flags |= CONFIG_UPDATE_CHANNEL;
- if (rt2x00dev->tx_power != conf->power_level)
- flags |= CONFIG_UPDATE_TXPOWER;
-
- /*
- * Determining changes in the antenna setups request several checks:
- * antenna_sel_{r,t}x = 0
- * -> Does active_{r,t}x match default_{r,t}x
- * -> Is default_{r,t}x SW_DIVERSITY
- * antenna_sel_{r,t}x = 1/2
- * -> Does active_{r,t}x match antenna_sel_{r,t}x
- * The reason for not updating the antenna while SW diversity
- * should be used is simple: Software diversity means that
- * we should switch between the antenna's based on the
- * quality. This means that the current antenna is good enough
- * to work with untill the link tuner decides that an antenna
- * switch should be performed.
- */
- if (!conf->antenna_sel_rx &&
- default_ant->rx != ANTENNA_SW_DIVERSITY &&
- default_ant->rx != active_ant->rx)
- flags |= CONFIG_UPDATE_ANTENNA;
- else if (conf->antenna_sel_rx &&
- conf->antenna_sel_rx != active_ant->rx)
- flags |= CONFIG_UPDATE_ANTENNA;
- else if (active_ant->rx == ANTENNA_SW_DIVERSITY)
- flags |= CONFIG_UPDATE_ANTENNA;
-
- if (!conf->antenna_sel_tx &&
- default_ant->tx != ANTENNA_SW_DIVERSITY &&
- default_ant->tx != active_ant->tx)
- flags |= CONFIG_UPDATE_ANTENNA;
- else if (conf->antenna_sel_tx &&
- conf->antenna_sel_tx != active_ant->tx)
- flags |= CONFIG_UPDATE_ANTENNA;
- else if (active_ant->tx == ANTENNA_SW_DIVERSITY)
- flags |= CONFIG_UPDATE_ANTENNA;
-
- /*
- * The following configuration options are never
- * stored anywhere and will always be updated.
- */
- flags |= CONFIG_UPDATE_SLOT_TIME;
- flags |= CONFIG_UPDATE_BEACON_INT;
-
- /*
- * We have determined what options should be updated,
- * now precalculate device configuration values depending
- * on what configuration options need to be updated.
- */
-config:
memset(&libconf, 0, sizeof(libconf));
- if (flags & CONFIG_UPDATE_PHYMODE) {
- band = &rt2x00dev->bands[conf->channel->band];
-
- libconf.band = conf->channel->band;
- libconf.basic_rates = rt2x00lib_get_basic_rates(band);
- }
+ libconf.conf = conf;
- if (flags & CONFIG_UPDATE_CHANNEL) {
+ if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
memcpy(&libconf.rf,
&rt2x00dev->spec.channels[conf->channel->hw_value],
sizeof(libconf.rf));
@@ -251,61 +181,23 @@ config:
sizeof(libconf.channel));
}
- if (flags & CONFIG_UPDATE_ANTENNA) {
- if (conf->antenna_sel_rx)
- libconf.ant.rx = conf->antenna_sel_rx;
- else if (default_ant->rx != ANTENNA_SW_DIVERSITY)
- libconf.ant.rx = default_ant->rx;
- else if (active_ant->rx == ANTENNA_SW_DIVERSITY)
- libconf.ant.rx = ANTENNA_B;
- else
- libconf.ant.rx = active_ant->rx;
-
- if (conf->antenna_sel_tx)
- libconf.ant.tx = conf->antenna_sel_tx;
- else if (default_ant->tx != ANTENNA_SW_DIVERSITY)
- libconf.ant.tx = default_ant->tx;
- else if (active_ant->tx == ANTENNA_SW_DIVERSITY)
- libconf.ant.tx = ANTENNA_B;
- else
- libconf.ant.tx = active_ant->tx;
- }
-
- if (flags & CONFIG_UPDATE_SLOT_TIME) {
- short_slot_time = conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME;
-
- libconf.slot_time =
- short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME;
- libconf.sifs = SIFS;
- libconf.pifs = short_slot_time ? SHORT_PIFS : PIFS;
- libconf.difs = short_slot_time ? SHORT_DIFS : DIFS;
- libconf.eifs = short_slot_time ? SHORT_EIFS : EIFS;
- }
-
- libconf.conf = conf;
-
/*
* Start configuration.
*/
- rt2x00dev->ops->lib->config(rt2x00dev, &libconf, flags);
+ rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags);
/*
* Some configuration changes affect the link quality
* which means we need to reset the link tuner.
*/
- if (flags & (CONFIG_UPDATE_CHANNEL | CONFIG_UPDATE_ANTENNA))
+ if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt2x00lib_reset_link_tuner(rt2x00dev);
- if (flags & CONFIG_UPDATE_PHYMODE) {
- rt2x00dev->curr_band = conf->channel->band;
- rt2x00dev->rx_status.band = conf->channel->band;
- }
-
- rt2x00dev->rx_status.freq = conf->channel->center_freq;
+ rt2x00dev->curr_band = conf->channel->band;
rt2x00dev->tx_power = conf->power_level;
+ rt2x00dev->short_retry = conf->short_frame_max_tx_count;
+ rt2x00dev->long_retry = conf->long_frame_max_tx_count;
- if (flags & CONFIG_UPDATE_ANTENNA) {
- rt2x00dev->link.ant.active.rx = libconf.ant.rx;
- rt2x00dev->link.ant.active.tx = libconf.ant.tx;
- }
+ rt2x00dev->rx_status.band = conf->channel->band;
+ rt2x00dev->rx_status.freq = conf->channel->center_freq;
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index 5a858e5106c4..37ad0d2fb64c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -46,6 +46,29 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
}
}
+void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+ struct txentry_desc *txdesc)
+{
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+ struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
+
+ __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags);
+
+ txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key);
+
+ if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+ __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags);
+
+ txdesc->key_idx = hw_key->hw_key_idx;
+ txdesc->iv_offset = ieee80211_get_hdrlen_from_skb(entry->skb);
+
+ if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV))
+ __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags);
+
+ if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC))
+ __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags);
+}
+
unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info)
{
struct ieee80211_key_conf *key = tx_info->control.hw_key;
@@ -69,6 +92,18 @@ unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info)
return overhead;
}
+void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, unsigned int iv_len)
+{
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
+ unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb);
+
+ if (unlikely(!iv_len))
+ return;
+
+ /* Copy IV/EIV data */
+ memcpy(skbdesc->iv, skb->data + header_length, iv_len);
+}
+
void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len)
{
struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
@@ -78,10 +113,7 @@ void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len)
return;
/* Copy IV/EIV data */
- if (iv_len >= 4)
- memcpy(&skbdesc->iv, skb->data + header_length, 4);
- if (iv_len >= 8)
- memcpy(&skbdesc->eiv, skb->data + header_length + 4, 4);
+ memcpy(skbdesc->iv, skb->data + header_length, iv_len);
/* Move ieee80211 header */
memmove(skb->data + iv_len, skb->data, header_length);
@@ -98,7 +130,7 @@ void rt2x00crypto_tx_insert_iv(struct sk_buff *skb)
struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
unsigned int header_length = ieee80211_get_hdrlen_from_skb(skb);
const unsigned int iv_len =
- ((!!(skbdesc->iv)) * 4) + ((!!(skbdesc->eiv)) * 4);
+ ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4);
if (!(skbdesc->flags & FRAME_DESC_IV_STRIPPED))
return;
@@ -109,10 +141,7 @@ void rt2x00crypto_tx_insert_iv(struct sk_buff *skb)
memmove(skb->data, skb->data + iv_len, header_length);
/* Copy IV/EIV data */
- if (iv_len >= 4)
- memcpy(skb->data + header_length, &skbdesc->iv, 4);
- if (iv_len >= 8)
- memcpy(skb->data + header_length + 4, &skbdesc->eiv, 4);
+ memcpy(skb->data + header_length, skbdesc->iv, iv_len);
/* IV/EIV data has returned into the frame */
skbdesc->flags &= ~FRAME_DESC_IV_STRIPPED;
@@ -172,17 +201,9 @@ void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align,
header_length);
transfer += header_length;
- /* Copy IV data */
- if (iv_len >= 4) {
- memcpy(skb->data + transfer, &rxdesc->iv, 4);
- transfer += 4;
- }
-
- /* Copy EIV data */
- if (iv_len >= 8) {
- memcpy(skb->data + transfer, &rxdesc->eiv, 4);
- transfer += 4;
- }
+ /* Copy IV/EIV data */
+ memcpy(skb->data + transfer, rxdesc->iv, iv_len);
+ transfer += iv_len;
/* Move payload */
if (align) {
@@ -198,16 +219,14 @@ void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align,
*/
transfer += payload_len;
- /* Copy ICV data */
- if (icv_len >= 4) {
- memcpy(skb->data + transfer, &rxdesc->icv, 4);
- /*
- * AES appends 8 bytes, we can't fill the upper
- * 4 bytes, but mac80211 doesn't care about what
- * we provide here anyway and strips it immediately.
- */
- transfer += icv_len;
- }
+ /*
+ * Copy ICV data
+ * AES appends 8 bytes, we can't fill the upper
+ * 4 bytes, but mac80211 doesn't care about what
+ * we provide here anyway and strips it immediately.
+ */
+ memcpy(skb->data + transfer, &rxdesc->icv, 4);
+ transfer += icv_len;
/* IV/EIV/ICV has been inserted into frame */
rxdesc->size = transfer;
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 5cf4c859e39d..54dd10060bf1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -285,7 +285,7 @@ exit:
}
static unsigned int rt2x00debug_poll_queue_dump(struct file *file,
- poll_table *wait)
+ poll_table *wait)
{
struct rt2x00debug_intf *intf = file->private_data;
@@ -377,7 +377,7 @@ static ssize_t rt2x00debug_read_crypto_stats(struct file *file,
if (*offset)
return 0;
- data = kzalloc((1 + CIPHER_MAX)* MAX_LINE_LENGTH, GFP_KERNEL);
+ data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -424,16 +424,21 @@ static ssize_t rt2x00debug_read_##__name(struct file *file, \
const struct rt2x00debug *debug = intf->debug; \
char line[16]; \
size_t size; \
+ unsigned int index = intf->offset_##__name; \
__type value; \
\
if (*offset) \
return 0; \
\
- if (intf->offset_##__name >= debug->__name.word_count) \
+ if (index >= debug->__name.word_count) \
return -EINVAL; \
\
- debug->__name.read(intf->rt2x00dev, \
- intf->offset_##__name, &value); \
+ if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \
+ index *= debug->__name.word_size; \
+ \
+ index += debug->__name.word_base; \
+ \
+ debug->__name.read(intf->rt2x00dev, index, &value); \
\
size = sprintf(line, __format, value); \
\
@@ -454,12 +459,13 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \
const struct rt2x00debug *debug = intf->debug; \
char line[16]; \
size_t size; \
+ unsigned int index = intf->offset_##__name; \
__type value; \
\
if (*offset) \
return 0; \
\
- if (intf->offset_##__name >= debug->__name.word_count) \
+ if (index >= debug->__name.word_count) \
return -EINVAL; \
\
if (copy_from_user(line, buf, length)) \
@@ -468,8 +474,12 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \
size = strlen(line); \
value = simple_strtoul(line, NULL, 0); \
\
- debug->__name.write(intf->rt2x00dev, \
- intf->offset_##__name, value); \
+ if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \
+ index *= debug->__name.word_size; \
+ \
+ index += debug->__name.word_base; \
+ \
+ debug->__name.write(intf->rt2x00dev, index, value); \
\
*offset += size; \
return size; \
@@ -587,29 +597,29 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
intf->driver_folder =
debugfs_create_dir(intf->rt2x00dev->ops->name,
rt2x00dev->hw->wiphy->debugfsdir);
- if (IS_ERR(intf->driver_folder))
+ if (IS_ERR(intf->driver_folder) || !intf->driver_folder)
goto exit;
intf->driver_entry =
rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob);
- if (IS_ERR(intf->driver_entry))
+ if (IS_ERR(intf->driver_entry) || !intf->driver_entry)
goto exit;
intf->chipset_entry =
rt2x00debug_create_file_chipset("chipset",
intf, &intf->chipset_blob);
- if (IS_ERR(intf->chipset_entry))
+ if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry)
goto exit;
intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR,
intf->driver_folder, intf,
&rt2x00debug_fop_dev_flags);
- if (IS_ERR(intf->dev_flags))
+ if (IS_ERR(intf->dev_flags) || !intf->dev_flags)
goto exit;
intf->register_folder =
debugfs_create_dir("register", intf->driver_folder);
- if (IS_ERR(intf->register_folder))
+ if (IS_ERR(intf->register_folder) || !intf->register_folder)
goto exit;
#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \
@@ -619,7 +629,8 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
S_IRUSR | S_IWUSR, \
(__intf)->register_folder, \
&(__intf)->offset_##__name); \
- if (IS_ERR((__intf)->__name##_off_entry)) \
+ if (IS_ERR((__intf)->__name##_off_entry) \
+ || !(__intf)->__name##_off_entry) \
goto exit; \
\
(__intf)->__name##_val_entry = \
@@ -627,7 +638,8 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
S_IRUSR | S_IWUSR, \
(__intf)->register_folder, \
(__intf), &rt2x00debug_fop_##__name);\
- if (IS_ERR((__intf)->__name##_val_entry)) \
+ if (IS_ERR((__intf)->__name##_val_entry) \
+ || !(__intf)->__name##_val_entry) \
goto exit; \
})
@@ -640,13 +652,14 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
intf->queue_folder =
debugfs_create_dir("queue", intf->driver_folder);
- if (IS_ERR(intf->queue_folder))
+ if (IS_ERR(intf->queue_folder) || !intf->queue_folder)
goto exit;
intf->queue_frame_dump_entry =
debugfs_create_file("dump", S_IRUSR, intf->queue_folder,
intf, &rt2x00debug_fop_queue_dump);
- if (IS_ERR(intf->queue_frame_dump_entry))
+ if (IS_ERR(intf->queue_frame_dump_entry)
+ || !intf->queue_frame_dump_entry)
goto exit;
skb_queue_head_init(&intf->frame_dump_skbqueue);
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h
index c4ce895aa1c7..a92104dfee9a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.h
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.h
@@ -28,6 +28,16 @@
struct rt2x00_dev;
+/**
+ * enum rt2x00debugfs_entry_flags: Flags for debugfs registry entry
+ *
+ * @RT2X00DEBUGFS_OFFSET: rt2x00lib should pass the register offset
+ * as argument when using the callback function read()/write()
+ */
+enum rt2x00debugfs_entry_flags {
+ RT2X00DEBUGFS_OFFSET = (1 << 0),
+};
+
#define RT2X00DEBUGFS_REGISTER_ENTRY(__name, __type) \
struct reg##__name { \
void (*read)(struct rt2x00_dev *rt2x00dev, \
@@ -35,6 +45,9 @@ struct reg##__name { \
void (*write)(struct rt2x00_dev *rt2x00dev, \
const unsigned int word, __type data); \
\
+ unsigned int flags; \
+ \
+ unsigned int word_base; \
unsigned int word_size; \
unsigned int word_count; \
} __name
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 86840e3585e8..6d92542fcf0d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -101,8 +101,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
/*
* Initialize all data queues.
*/
- rt2x00queue_init_rx(rt2x00dev);
- rt2x00queue_init_tx(rt2x00dev);
+ rt2x00queue_init_queues(rt2x00dev);
/*
* Enable radio.
@@ -176,13 +175,14 @@ void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state)
static void rt2x00lib_evaluate_antenna_sample(struct rt2x00_dev *rt2x00dev)
{
- enum antenna rx = rt2x00dev->link.ant.active.rx;
- enum antenna tx = rt2x00dev->link.ant.active.tx;
+ struct antenna_setup ant;
int sample_a =
rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_A);
int sample_b =
rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_B);
+ memcpy(&ant, &rt2x00dev->link.ant.active, sizeof(ant));
+
/*
* We are done sampling. Now we should evaluate the results.
*/
@@ -200,21 +200,22 @@ static void rt2x00lib_evaluate_antenna_sample(struct rt2x00_dev *rt2x00dev)
return;
if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY)
- rx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B;
+ ant.rx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B;
if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY)
- tx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B;
+ ant.tx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B;
- rt2x00lib_config_antenna(rt2x00dev, rx, tx);
+ rt2x00lib_config_antenna(rt2x00dev, &ant);
}
static void rt2x00lib_evaluate_antenna_eval(struct rt2x00_dev *rt2x00dev)
{
- enum antenna rx = rt2x00dev->link.ant.active.rx;
- enum antenna tx = rt2x00dev->link.ant.active.tx;
+ struct antenna_setup ant;
int rssi_curr = rt2x00_get_link_ant_rssi(&rt2x00dev->link);
int rssi_old = rt2x00_update_ant_rssi(&rt2x00dev->link, rssi_curr);
+ memcpy(&ant, &rt2x00dev->link.ant.active, sizeof(ant));
+
/*
* Legacy driver indicates that we should swap antenna's
* when the difference in RSSI is greater that 5. This
@@ -230,12 +231,12 @@ static void rt2x00lib_evaluate_antenna_eval(struct rt2x00_dev *rt2x00dev)
rt2x00dev->link.ant.flags |= ANTENNA_MODE_SAMPLE;
if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY)
- rx = (rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
+ ant.rx = (ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY)
- tx = (tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
+ ant.tx = (ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A;
- rt2x00lib_config_antenna(rt2x00dev, rx, tx);
+ rt2x00lib_config_antenna(rt2x00dev, &ant);
}
static void rt2x00lib_evaluate_antenna(struct rt2x00_dev *rt2x00dev)
@@ -249,11 +250,9 @@ static void rt2x00lib_evaluate_antenna(struct rt2x00_dev *rt2x00dev)
rt2x00dev->link.ant.flags &= ~ANTENNA_RX_DIVERSITY;
rt2x00dev->link.ant.flags &= ~ANTENNA_TX_DIVERSITY;
- if (rt2x00dev->hw->conf.antenna_sel_rx == 0 &&
- rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
+ if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY)
rt2x00dev->link.ant.flags |= ANTENNA_RX_DIVERSITY;
- if (rt2x00dev->hw->conf.antenna_sel_tx == 0 &&
- rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
+ if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY)
rt2x00dev->link.ant.flags |= ANTENNA_TX_DIVERSITY;
if (!(rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) &&
@@ -419,7 +418,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
*/
spin_lock(&intf->lock);
- memcpy(&conf, &intf->conf, sizeof(conf));
+ memcpy(&conf, &vif->bss_conf, sizeof(conf));
delayed_flags = intf->delayed_flags;
intf->delayed_flags = 0;
@@ -500,7 +499,9 @@ void rt2x00lib_txdone(struct queue_entry *entry,
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb);
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
enum data_queue_qid qid = skb_get_queue_mapping(entry->skb);
+ u8 rate_idx, rate_flags;
/*
* Unmap the skb.
@@ -530,14 +531,18 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00dev->link.qual.tx_failed +=
test_bit(TXDONE_FAILURE, &txdesc->flags);
+ rate_idx = skbdesc->tx_rate_idx;
+ rate_flags = skbdesc->tx_rate_flags;
+
/*
* Initialize TX status
*/
memset(&tx_info->status, 0, sizeof(tx_info->status));
tx_info->status.ack_signal = 0;
- tx_info->status.excessive_retries =
- test_bit(TXDONE_EXCESSIVE_RETRY, &txdesc->flags);
- tx_info->status.retry_count = txdesc->retry;
+ tx_info->status.rates[0].idx = rate_idx;
+ tx_info->status.rates[0].flags = rate_flags;
+ tx_info->status.rates[0].count = txdesc->retry + 1;
+ tx_info->status.rates[1].idx = -1; /* terminate */
if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
@@ -546,7 +551,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00dev->low_level_stats.dot11ACKFailureCount++;
}
- if (tx_info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
+ if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
rt2x00dev->low_level_stats.dot11RTSSuccessCount++;
else if (test_bit(TXDONE_FAILURE, &txdesc->flags))
@@ -570,7 +575,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
entry->skb = NULL;
entry->flags = 0;
- rt2x00dev->ops->lib->init_txentry(rt2x00dev, entry);
+ rt2x00dev->ops->lib->clear_entry(entry);
clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
@@ -631,7 +636,8 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
* provided seperately (through hardware descriptor)
* in which case we should reinsert the data into the frame.
*/
- if ((rxdesc.flags & RX_FLAG_IV_STRIPPED)) {
+ if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) &&
+ (rxdesc.flags & RX_FLAG_IV_STRIPPED)) {
rt2x00crypto_rx_insert_iv(entry->skb, align,
header_length, &rxdesc);
} else if (align) {
@@ -702,7 +708,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
entry->skb = skb;
entry->flags = 0;
- rt2x00dev->ops->lib->init_rxentry(rt2x00dev, entry);
+ rt2x00dev->ops->lib->clear_entry(entry);
rt2x00queue_index_inc(entry->queue, Q_INDEX);
}
@@ -713,31 +719,31 @@ EXPORT_SYMBOL_GPL(rt2x00lib_rxdone);
*/
const struct rt2x00_rate rt2x00_supported_rates[12] = {
{
- .flags = DEV_RATE_CCK | DEV_RATE_BASIC,
+ .flags = DEV_RATE_CCK,
.bitrate = 10,
.ratemask = BIT(0),
.plcp = 0x00,
},
{
- .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE | DEV_RATE_BASIC,
+ .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE,
.bitrate = 20,
.ratemask = BIT(1),
.plcp = 0x01,
},
{
- .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE | DEV_RATE_BASIC,
+ .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE,
.bitrate = 55,
.ratemask = BIT(2),
.plcp = 0x02,
},
{
- .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE | DEV_RATE_BASIC,
+ .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE,
.bitrate = 110,
.ratemask = BIT(3),
.plcp = 0x03,
},
{
- .flags = DEV_RATE_OFDM | DEV_RATE_BASIC,
+ .flags = DEV_RATE_OFDM,
.bitrate = 60,
.ratemask = BIT(4),
.plcp = 0x0b,
@@ -749,7 +755,7 @@ const struct rt2x00_rate rt2x00_supported_rates[12] = {
.plcp = 0x0f,
},
{
- .flags = DEV_RATE_OFDM | DEV_RATE_BASIC,
+ .flags = DEV_RATE_OFDM,
.bitrate = 120,
.ratemask = BIT(6),
.plcp = 0x0a,
@@ -761,7 +767,7 @@ const struct rt2x00_rate rt2x00_supported_rates[12] = {
.plcp = 0x0e,
},
{
- .flags = DEV_RATE_OFDM | DEV_RATE_BASIC,
+ .flags = DEV_RATE_OFDM,
.bitrate = 240,
.ratemask = BIT(8),
.plcp = 0x09,
@@ -1046,16 +1052,24 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
{
int retval = -ENOMEM;
+ mutex_init(&rt2x00dev->csr_mutex);
+
/*
* Make room for rt2x00_intf inside the per-interface
* structure ieee80211_vif.
*/
rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf);
- rt2x00dev->hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
+ /*
+ * Determine which operating modes are supported, all modes
+ * which require beaconing, depend on the availability of
+ * beacon entries.
+ */
+ rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ if (rt2x00dev->ops->bcn->entry_num > 0)
+ rt2x00dev->hw->wiphy->interface_modes |=
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
/*
* Let the driver probe the device to detect the capabilities.
@@ -1247,7 +1261,7 @@ int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev)
/*
* Reconfigure device.
*/
- retval = rt2x00mac_config(rt2x00dev->hw, &rt2x00dev->hw->conf);
+ retval = rt2x00mac_config(rt2x00dev->hw, ~0);
if (retval)
goto exit;
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c
index b362a1cf3f8d..68f4e0fc35b9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.c
+++ b/drivers/net/wireless/rt2x00/rt2x00leds.c
@@ -72,49 +72,33 @@ void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi)
}
}
-void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled)
+static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled)
{
- struct rt2x00_led *led = &rt2x00dev->led_qual;
- unsigned int brightness;
+ unsigned int brightness = enabled ? LED_FULL : LED_OFF;
- if ((led->type != LED_TYPE_ACTIVITY) || !(led->flags & LED_REGISTERED))
+ if (!(led->flags & LED_REGISTERED))
return;
- brightness = enabled ? LED_FULL : LED_OFF;
- if (brightness != led->led_dev.brightness) {
- led->led_dev.brightness_set(&led->led_dev, brightness);
- led->led_dev.brightness = brightness;
- }
+ led->led_dev.brightness_set(&led->led_dev, brightness);
+ led->led_dev.brightness = brightness;
}
-void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled)
+void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled)
{
- struct rt2x00_led *led = &rt2x00dev->led_assoc;
- unsigned int brightness;
-
- if ((led->type != LED_TYPE_ASSOC) || !(led->flags & LED_REGISTERED))
- return;
+ if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY)
+ rt2x00led_led_simple(&rt2x00dev->led_qual, enabled);
+}
- brightness = enabled ? LED_FULL : LED_OFF;
- if (brightness != led->led_dev.brightness) {
- led->led_dev.brightness_set(&led->led_dev, brightness);
- led->led_dev.brightness = brightness;
- }
+void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled)
+{
+ if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC)
+ rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled);
}
void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled)
{
- struct rt2x00_led *led = &rt2x00dev->led_radio;
- unsigned int brightness;
-
- if ((led->type != LED_TYPE_RADIO) || !(led->flags & LED_REGISTERED))
- return;
-
- brightness = enabled ? LED_FULL : LED_OFF;
- if (brightness != led->led_dev.brightness) {
- led->led_dev.brightness_set(&led->led_dev, brightness);
- led->led_dev.brightness = brightness;
- }
+ if (rt2x00dev->led_radio.type == LED_TYPE_ASSOC)
+ rt2x00led_led_simple(&rt2x00dev->led_radio, enabled);
}
static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
@@ -125,6 +109,7 @@ static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
int retval;
led->led_dev.name = name;
+ led->led_dev.brightness = LED_OFF;
retval = led_classdev_register(device, &led->led_dev);
if (retval) {
@@ -199,7 +184,16 @@ exit_fail:
static void rt2x00leds_unregister_led(struct rt2x00_led *led)
{
led_classdev_unregister(&led->led_dev);
- led->led_dev.brightness_set(&led->led_dev, LED_OFF);
+
+ /*
+ * This might look weird, but when we are unregistering while
+ * suspended the led is already off, and since we haven't
+ * fully resumed yet, access to the device might not be
+ * possible yet.
+ */
+ if (!(led->led_dev.flags & LED_SUSPENDED))
+ led->led_dev.brightness_set(&led->led_dev, LED_OFF);
+
led->flags &= ~LED_REGISTERED;
}
@@ -213,22 +207,40 @@ void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev)
rt2x00leds_unregister_led(&rt2x00dev->led_radio);
}
+static inline void rt2x00leds_suspend_led(struct rt2x00_led *led)
+{
+ led_classdev_suspend(&led->led_dev);
+
+ /* This shouldn't be needed, but just to be safe */
+ led->led_dev.brightness_set(&led->led_dev, LED_OFF);
+ led->led_dev.brightness = LED_OFF;
+}
+
void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev)
{
if (rt2x00dev->led_qual.flags & LED_REGISTERED)
- led_classdev_suspend(&rt2x00dev->led_qual.led_dev);
+ rt2x00leds_suspend_led(&rt2x00dev->led_qual);
if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
- led_classdev_suspend(&rt2x00dev->led_assoc.led_dev);
+ rt2x00leds_suspend_led(&rt2x00dev->led_assoc);
if (rt2x00dev->led_radio.flags & LED_REGISTERED)
- led_classdev_suspend(&rt2x00dev->led_radio.led_dev);
+ rt2x00leds_suspend_led(&rt2x00dev->led_radio);
+}
+
+static inline void rt2x00leds_resume_led(struct rt2x00_led *led)
+{
+ led_classdev_resume(&led->led_dev);
+
+ /* Device might have enabled the LEDS during resume */
+ led->led_dev.brightness_set(&led->led_dev, LED_OFF);
+ led->led_dev.brightness = LED_OFF;
}
void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev)
{
if (rt2x00dev->led_radio.flags & LED_REGISTERED)
- led_classdev_resume(&rt2x00dev->led_radio.led_dev);
+ rt2x00leds_resume_led(&rt2x00dev->led_radio);
if (rt2x00dev->led_assoc.flags & LED_REGISTERED)
- led_classdev_resume(&rt2x00dev->led_assoc.led_dev);
+ rt2x00leds_resume_led(&rt2x00dev->led_assoc);
if (rt2x00dev->led_qual.flags & LED_REGISTERED)
- led_classdev_resume(&rt2x00dev->led_qual.led_dev);
+ rt2x00leds_resume_led(&rt2x00dev->led_qual);
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 797eb619aa0a..03024327767b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -43,7 +43,6 @@ struct rt2x00_rate {
#define DEV_RATE_CCK 0x0001
#define DEV_RATE_OFDM 0x0002
#define DEV_RATE_SHORT_PREAMBLE 0x0004
-#define DEV_RATE_BASIC 0x0008
unsigned short bitrate; /* In 100kbit/s */
unsigned short ratemask;
@@ -94,9 +93,10 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
struct rt2x00_intf *intf,
struct ieee80211_bss_conf *conf);
void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
- enum antenna rx, enum antenna tx);
+ struct antenna_setup *ant);
void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_conf *conf, const int force_config);
+ struct ieee80211_conf *conf,
+ const unsigned int changed_flags);
/**
* DOC: Queue handlers
@@ -150,8 +150,16 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
*/
void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index);
-void rt2x00queue_init_rx(struct rt2x00_dev *rt2x00dev);
-void rt2x00queue_init_tx(struct rt2x00_dev *rt2x00dev);
+/**
+ * rt2x00queue_init_queues - Initialize all data queues
+ * @rt2x00dev: Pointer to &struct rt2x00_dev.
+ *
+ * This function will loop through all available queues to clear all
+ * index numbers and set the queue entry to the correct initialization
+ * state.
+ */
+void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev);
+
int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev);
void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev);
int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev);
@@ -210,7 +218,10 @@ static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev,
*/
#ifdef CONFIG_RT2X00_LIB_CRYPTO
enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key);
+void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+ struct txentry_desc *txdesc);
unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info);
+void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, unsigned int iv_len);
void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, unsigned int iv_len);
void rt2x00crypto_tx_insert_iv(struct sk_buff *skb);
void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int align,
@@ -222,11 +233,21 @@ static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *
return CIPHER_NONE;
}
+static inline void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+ struct txentry_desc *txdesc)
+{
+}
+
static inline unsigned int rt2x00crypto_tx_overhead(struct ieee80211_tx_info *tx_info)
{
return 0;
}
+static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb,
+ unsigned int iv_len)
+{
+}
+
static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb,
unsigned int iv_len)
{
@@ -242,7 +263,7 @@ static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb,
struct rxdone_entry_desc *rxdesc)
{
}
-#endif
+#endif /* CONFIG_RT2X00_LIB_CRYPTO */
/*
* RFkill handlers.
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 2c6cc5c374ff..38edee5fe168 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -39,7 +39,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
unsigned int data_length;
int retval = 0;
- if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
data_length = sizeof(struct ieee80211_cts);
else
data_length = sizeof(struct ieee80211_rts);
@@ -64,11 +64,11 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
*/
memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb));
rts_info = IEEE80211_SKB_CB(skb);
- rts_info->flags &= ~IEEE80211_TX_CTL_USE_RTS_CTS;
- rts_info->flags &= ~IEEE80211_TX_CTL_USE_CTS_PROTECT;
+ rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS;
+ rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT;
rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS;
- if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
rts_info->flags |= IEEE80211_TX_CTL_NO_ACK;
else
rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK;
@@ -79,12 +79,10 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
* RTS/CTS frame should use the length of the frame plus any
* encryption overhead that will be added by the hardware.
*/
-#ifdef CONFIG_RT2X00_LIB_CRYPTO
if (!frag_skb->do_not_encrypt)
data_length += rt2x00crypto_tx_overhead(tx_info);
-#endif /* CONFIG_RT2X00_LIB_CRYPTO */
- if (tx_info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif,
frag_skb->data, data_length, tx_info,
(struct ieee80211_cts *)(skb->data));
@@ -132,8 +130,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
ERROR(rt2x00dev,
"Attempt to send packet over invalid queue %d.\n"
"Please file bug report to %s.\n", qid, DRV_PROJECT);
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
+ goto exit_fail;
}
/*
@@ -146,8 +143,8 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* inside the hardware.
*/
frame_control = le16_to_cpu(ieee80211hdr->frame_control);
- if ((tx_info->flags & (IEEE80211_TX_CTL_USE_RTS_CTS |
- IEEE80211_TX_CTL_USE_CTS_PROTECT)) &&
+ if ((tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS |
+ IEEE80211_TX_RC_USE_CTS_PROTECT)) &&
!rt2x00dev->ops->hw->set_rts_threshold) {
if (rt2x00queue_available(queue) <= 1)
goto exit_fail;
@@ -335,10 +332,10 @@ void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface);
-int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
- int radio_on;
+ struct ieee80211_conf *conf = &hw->conf;
int status;
/*
@@ -355,7 +352,6 @@ int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
* some configuration parameters (e.g. channel and antenna values) can
* only be set when the radio is enabled.
*/
- radio_on = test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags);
if (conf->radio_enabled) {
/* For programming the values, we have to turn RX off */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF);
@@ -369,7 +365,18 @@ int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
* When we've just turned on the radio, we want to reprogram
* everything to ensure a consistent state
*/
- rt2x00lib_config(rt2x00dev, conf, !radio_on);
+ rt2x00lib_config(rt2x00dev, conf, changed);
+
+ /*
+ * The radio was enabled, configure the antenna to the
+ * default settings, the link tuner will later start
+ * continue configuring the antenna based on the software
+ * diversity. But for non-diversity configurations, we need
+ * to have configured the correct state now.
+ */
+ if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED)
+ rt2x00lib_config_antenna(rt2x00dev,
+ &rt2x00dev->default_ant);
/* Turn RX back on */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON);
@@ -480,12 +487,15 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_key_conf *key)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct ieee80211_sta *sta;
int (*set_key) (struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_crypto *crypto,
struct ieee80211_key_conf *key);
struct rt2x00lib_crypto crypto;
- if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags))
+ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+ return 0;
+ else if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags))
return -EOPNOTSUPP;
else if (key->keylen > 32)
return -ENOSPC;
@@ -528,6 +538,17 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
memcpy(&crypto.key, &key->key[0], key->keylen);
/*
+ * Discover the Association ID from mac80211.
+ * Some drivers need this information when updating the
+ * hardware key (either adding or removing).
+ */
+ rcu_read_lock();
+ sta = ieee80211_find_sta(hw, address);
+ if (sta)
+ crypto.aid = sta->aid;
+ rcu_read_unlock();
+
+ /*
* Each BSS has a maximum of 4 shared keys.
* Shared key index values:
* 0) BSS0 key0
@@ -625,7 +646,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* When the erp information has changed, we should perform
* additional configuration steps. For all other changes we are done.
*/
- if (changes & (BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_ERP_CTS_PROT)) {
+ if (changes & ~(BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags))
rt2x00lib_config_erp(rt2x00dev, intf, bss_conf);
else
@@ -633,7 +654,6 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
}
spin_lock(&intf->lock);
- memcpy(&intf->conf, bss_conf, sizeof(*bss_conf));
if (delayed) {
intf->delayed_flags |= delayed;
schedule_work(&rt2x00dev->intf_work);
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
index adf2876ed8ab..d52b22b82d1f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.c
@@ -32,24 +32,46 @@
#include "rt2x00pci.h"
/*
+ * Register access.
+ */
+int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const struct rt2x00_field32 field,
+ u32 *reg)
+{
+ unsigned int i;
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2x00pci_register_read(rt2x00dev, offset, reg);
+ if (!rt2x00_get_field32(*reg, field))
+ return 1;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ ERROR(rt2x00dev, "Indirect register access failed: "
+ "offset=0x%.08x, value=0x%.08x\n", offset, *reg);
+ *reg = ~0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2x00pci_regbusy_read);
+
+/*
* TX data handlers.
*/
int rt2x00pci_write_tx_data(struct queue_entry *entry)
{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
struct skb_frame_desc *skbdesc;
- u32 word;
-
- rt2x00_desc_read(entry_priv->desc, 0, &word);
/*
* This should not happen, we already checked the entry
* was ours. When the hardware disagrees there has been
* a queue corruption!
*/
- if (unlikely(rt2x00_get_field32(word, TXD_ENTRY_OWNER_NIC) ||
- rt2x00_get_field32(word, TXD_ENTRY_VALID))) {
- ERROR(entry->queue->rt2x00dev,
+ if (unlikely(rt2x00dev->ops->lib->get_entry_state(entry))) {
+ ERROR(rt2x00dev,
"Corrupt queue %d, accessing entry which is not ours.\n"
"Please file bug report to %s.\n",
entry->queue->qid, DRV_PROJECT);
@@ -76,14 +98,12 @@ void rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev)
struct queue_entry *entry;
struct queue_entry_priv_pci *entry_priv;
struct skb_frame_desc *skbdesc;
- u32 word;
while (1) {
entry = rt2x00queue_get_entry(queue, Q_INDEX);
entry_priv = entry->priv_data;
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- if (rt2x00_get_field32(word, RXD_ENTRY_OWNER_NIC))
+ if (rt2x00dev->ops->lib->get_entry_state(entry))
break;
/*
@@ -222,8 +242,7 @@ static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev)
{
struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev);
- rt2x00dev->csr.base = ioremap(pci_resource_start(pci_dev, 0),
- pci_resource_len(pci_dev, 0));
+ rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0);
if (!rt2x00dev->csr.base)
goto exit;
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h
index 80bf97c03e2d..9c0a4d77bc1b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.h
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.h
@@ -44,21 +44,10 @@
#define REGISTER_BUSY_DELAY 100
/*
- * Descriptor availability flags.
- * All PCI device descriptors have these 2 flags
- * with the exact same definition.
- * By storing them here we can use them inside rt2x00pci
- * for some simple entry availability checking.
- */
-#define TXD_ENTRY_OWNER_NIC FIELD32(0x00000001)
-#define TXD_ENTRY_VALID FIELD32(0x00000002)
-#define RXD_ENTRY_OWNER_NIC FIELD32(0x00000001)
-
-/*
* Register access.
*/
static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev,
- const unsigned long offset,
+ const unsigned int offset,
u32 *value)
{
*value = readl(rt2x00dev->csr.base + offset);
@@ -66,14 +55,14 @@ static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev,
static inline void
rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev,
- const unsigned long offset,
+ const unsigned int offset,
void *value, const u16 length)
{
memcpy_fromio(value, rt2x00dev->csr.base + offset, length);
}
static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev,
- const unsigned long offset,
+ const unsigned int offset,
u32 value)
{
writel(value, rt2x00dev->csr.base + offset);
@@ -81,13 +70,31 @@ static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev,
static inline void
rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev,
- const unsigned long offset,
+ const unsigned int offset,
const void *value, const u16 length)
{
memcpy_toio(rt2x00dev->csr.base + offset, value, length);
}
/**
+ * rt2x00pci_regbusy_read - Read from register with busy check
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @field: Field to check if register is busy
+ * @reg: Pointer to where register contents should be stored
+ *
+ * This function will read the given register, and checks if the
+ * register is busy. If it is, it will sleep for a couple of
+ * microseconds before reading the register again. If the register
+ * is not read after a certain timeout, this function will return
+ * FALSE.
+ */
+int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const struct rt2x00_field32 field,
+ u32 *reg);
+
+/**
* rt2x00pci_write_tx_data - Initialize data for TX operation
* @entry: The entry where the frame is located
*
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 451d410ecdae..eaec6bd93ed5 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -55,14 +55,12 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct rt2x00_dev *rt2x00dev,
/*
* For IV/EIV/ICV assembly we must make sure there is
* at least 8 bytes bytes available in headroom for IV/EIV
- * and 4 bytes for ICV data as tailroon.
+ * and 8 bytes for ICV data as tailroon.
*/
-#ifdef CONFIG_RT2X00_LIB_CRYPTO
if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
head_size += 8;
- tail_size += 4;
+ tail_size += 8;
}
-#endif /* CONFIG_RT2X00_LIB_CRYPTO */
/*
* Allocate skbuffer.
@@ -174,7 +172,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
txdesc->cw_max = entry->queue->cw_max;
txdesc->aifs = entry->queue->aifs;
- /* Data length + CRC + IV/EIV/ICV/MMIC (when using encryption) */
+ /* Data length + CRC */
data_length = entry->skb->len + 4;
/*
@@ -183,34 +181,17 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK))
__set_bit(ENTRY_TXD_ACK, &txdesc->flags);
-#ifdef CONFIG_RT2X00_LIB_CRYPTO
if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) &&
!entry->skb->do_not_encrypt) {
- struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
-
- __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags);
-
- txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key);
-
- if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
- __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags);
-
- txdesc->key_idx = hw_key->hw_key_idx;
- txdesc->iv_offset = ieee80211_get_hdrlen_from_skb(entry->skb);
+ /* Apply crypto specific descriptor information */
+ rt2x00crypto_create_tx_descriptor(entry, txdesc);
/*
* Extend frame length to include all encryption overhead
* that will be added by the hardware.
*/
data_length += rt2x00crypto_tx_overhead(tx_info);
-
- if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV))
- __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags);
-
- if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC))
- __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags);
}
-#endif /* CONFIG_RT2X00_LIB_CRYPTO */
/*
* Check if this is a RTS/CTS frame
@@ -230,8 +211,8 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
/*
* Determine retry information.
*/
- txdesc->retry_limit = tx_info->control.retry_limit;
- if (tx_info->flags & IEEE80211_TX_CTL_LONG_RETRY_LIMIT)
+ txdesc->retry_limit = tx_info->control.rates[0].count - 1;
+ if (txdesc->retry_limit >= rt2x00dev->long_retry)
__set_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags);
/*
@@ -312,8 +293,8 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
/*
* Convert length to microseconds.
*/
- residual = get_duration_res(data_length, hwrate->bitrate);
- duration = get_duration(data_length, hwrate->bitrate);
+ residual = GET_DURATION_RES(data_length, hwrate->bitrate);
+ duration = GET_DURATION(data_length, hwrate->bitrate);
if (residual != 0) {
duration++;
@@ -371,13 +352,15 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry,
int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
{
+ struct ieee80211_tx_info *tx_info;
struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
struct txentry_desc txdesc;
struct skb_frame_desc *skbdesc;
unsigned int iv_len = 0;
+ u8 rate_idx, rate_flags;
if (unlikely(rt2x00queue_full(queue)))
- return -EINVAL;
+ return -ENOBUFS;
if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) {
ERROR(queue->rt2x00dev,
@@ -399,13 +382,18 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
iv_len = IEEE80211_SKB_CB(skb)->control.hw_key->iv_len;
/*
- * All information is retreived from the skb->cb array,
+ * All information is retrieved from the skb->cb array,
* now we should claim ownership of the driver part of that
- * array.
+ * array, preserving the bitrate index and flags.
*/
- skbdesc = get_skb_frame_desc(entry->skb);
+ tx_info = IEEE80211_SKB_CB(skb);
+ rate_idx = tx_info->control.rates[0].idx;
+ rate_flags = tx_info->control.rates[0].flags;
+ skbdesc = get_skb_frame_desc(skb);
memset(skbdesc, 0, sizeof(*skbdesc));
skbdesc->entry = entry;
+ skbdesc->tx_rate_idx = rate_idx;
+ skbdesc->tx_rate_flags = rate_flags;
/*
* When hardware encryption is supported, and this frame
@@ -414,19 +402,21 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
*/
if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) &&
!test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) {
- rt2x00crypto_tx_remove_iv(skb, iv_len);
+ if (test_bit(CONFIG_CRYPTO_COPY_IV, &queue->rt2x00dev->flags))
+ rt2x00crypto_tx_copy_iv(skb, iv_len);
+ else
+ rt2x00crypto_tx_remove_iv(skb, iv_len);
}
/*
* It could be possible that the queue was corrupted and this
- * call failed. Just drop the frame, we cannot rollback and pass
- * the frame to mac80211 because the skb->cb has now been tainted.
+ * call failed. Since we always return NETDEV_TX_OK to mac80211,
+ * this frame will simply be dropped.
*/
if (unlikely(queue->rt2x00dev->ops->lib->write_tx_data(entry))) {
clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
- dev_kfree_skb_any(entry->skb);
entry->skb = NULL;
- return 0;
+ return -EIO;
}
if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags))
@@ -556,7 +546,7 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
queue->length++;
} else if (index == Q_INDEX_DONE) {
queue->length--;
- queue->count ++;
+ queue->count++;
}
spin_unlock_irqrestore(&queue->lock, irqflags);
@@ -575,40 +565,18 @@ static void rt2x00queue_reset(struct data_queue *queue)
spin_unlock_irqrestore(&queue->lock, irqflags);
}
-void rt2x00queue_init_rx(struct rt2x00_dev *rt2x00dev)
-{
- struct data_queue *queue = rt2x00dev->rx;
- unsigned int i;
-
- rt2x00queue_reset(queue);
-
- if (!rt2x00dev->ops->lib->init_rxentry)
- return;
-
- for (i = 0; i < queue->limit; i++) {
- queue->entries[i].flags = 0;
-
- rt2x00dev->ops->lib->init_rxentry(rt2x00dev,
- &queue->entries[i]);
- }
-}
-
-void rt2x00queue_init_tx(struct rt2x00_dev *rt2x00dev)
+void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
unsigned int i;
- txall_queue_for_each(rt2x00dev, queue) {
+ queue_for_each(rt2x00dev, queue) {
rt2x00queue_reset(queue);
- if (!rt2x00dev->ops->lib->init_txentry)
- continue;
-
for (i = 0; i < queue->limit; i++) {
queue->entries[i].flags = 0;
- rt2x00dev->ops->lib->init_txentry(rt2x00dev,
- &queue->entries[i]);
+ rt2x00dev->ops->lib->clear_entry(&queue->entries[i]);
}
}
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 9dbf04f0f04c..282937153408 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -104,22 +104,25 @@ enum skb_frame_desc_flags {
*
* @flags: Frame flags, see &enum skb_frame_desc_flags.
* @desc_len: Length of the frame descriptor.
+ * @tx_rate_idx: the index of the TX rate, used for TX status reporting
+ * @tx_rate_flags: the TX rate flags, used for TX status reporting
* @desc: Pointer to descriptor part of the frame.
* Note that this pointer could point to something outside
* of the scope of the skb->data pointer.
- * @iv: IV data used during encryption/decryption.
- * @eiv: EIV data used during encryption/decryption.
+ * @iv: IV/EIV data used during encryption/decryption.
* @skb_dma: (PCI-only) the DMA address associated with the sk buffer.
* @entry: The entry to which this sk buffer belongs.
*/
struct skb_frame_desc {
- unsigned int flags;
+ u8 flags;
+
+ u8 desc_len;
+ u8 tx_rate_idx;
+ u8 tx_rate_flags;
- unsigned int desc_len;
void *desc;
- __le32 iv;
- __le32 eiv;
+ __le32 iv[2];
dma_addr_t skb_dma;
@@ -143,11 +146,15 @@ static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb)
* @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value.
* @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value.
* @RXDONE_MY_BSS: Does this frame originate from device's BSS.
+ * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data.
+ * @RXDONE_CRYPTO_ICV: Driver provided ICV data.
*/
enum rxdone_entry_desc_flags {
RXDONE_SIGNAL_PLCP = 1 << 0,
RXDONE_SIGNAL_BITRATE = 1 << 1,
RXDONE_MY_BSS = 1 << 2,
+ RXDONE_CRYPTO_IV = 1 << 3,
+ RXDONE_CRYPTO_ICV = 1 << 4,
};
/**
@@ -163,8 +170,7 @@ enum rxdone_entry_desc_flags {
* @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags).
* @cipher: Cipher type used during decryption.
* @cipher_status: Decryption status.
- * @iv: IV data used during decryption.
- * @eiv: EIV data used during decryption.
+ * @iv: IV/EIV data used during decryption.
* @icv: ICV data used during decryption.
*/
struct rxdone_entry_desc {
@@ -177,8 +183,7 @@ struct rxdone_entry_desc {
u8 cipher;
u8 cipher_status;
- __le32 iv;
- __le32 eiv;
+ __le32 iv[2];
__le32 icv;
};
@@ -375,6 +380,8 @@ enum queue_index {
* @cw_max: The cw max value for outgoing frames (field ignored in RX queue).
* @data_size: Maximum data size for the frames in this queue.
* @desc_size: Hardware descriptor size for the data in this queue.
+ * @usb_endpoint: Device endpoint used for communication (USB only)
+ * @usb_maxpacket: Max packet size for given endpoint (USB only)
*/
struct data_queue {
struct rt2x00_dev *rt2x00dev;
@@ -396,6 +403,9 @@ struct data_queue {
unsigned short data_size;
unsigned short desc_size;
+
+ unsigned short usb_endpoint;
+ unsigned short usb_maxpacket;
};
/**
@@ -439,6 +449,19 @@ struct data_queue_desc {
&(__dev)->tx[(__dev)->ops->tx_queues]
/**
+ * queue_next - Return pointer to next queue in list (HELPER MACRO).
+ * @__queue: Current queue for which we need the next queue
+ *
+ * Using the current queue address we take the address directly
+ * after the queue to take the next queue. Note that this macro
+ * should be used carefully since it does not protect against
+ * moving past the end of the list. (See macros &queue_end and
+ * &tx_queue_end for determining the end of the queue).
+ */
+#define queue_next(__queue) \
+ &(__queue)[1]
+
+/**
* queue_loop - Loop through the queues within a specific range (HELPER MACRO).
* @__entry: Pointer where the current queue entry will be stored in.
* @__start: Start queue pointer.
@@ -448,8 +471,8 @@ struct data_queue_desc {
*/
#define queue_loop(__entry, __start, __end) \
for ((__entry) = (__start); \
- prefetch(&(__entry)[1]), (__entry) != (__end); \
- (__entry) = &(__entry)[1])
+ prefetch(queue_next(__entry)), (__entry) != (__end);\
+ (__entry) = queue_next(__entry))
/**
* queue_for_each - Loop through all queues
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index b73a7e0aeed4..83df312ac56f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -79,7 +79,7 @@ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev,
{
int status;
- BUG_ON(!mutex_is_locked(&rt2x00dev->usb_cache_mutex));
+ BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex));
/*
* Check for Cache availability.
@@ -110,13 +110,13 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev,
{
int status;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
+ mutex_lock(&rt2x00dev->csr_mutex);
status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request,
requesttype, offset, buffer,
buffer_length, timeout);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
+ mutex_unlock(&rt2x00dev->csr_mutex);
return status;
}
@@ -132,7 +132,7 @@ int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev,
unsigned char *tb;
u16 off, len, bsize;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
+ mutex_lock(&rt2x00dev->csr_mutex);
tb = (char *)buffer;
off = offset;
@@ -148,12 +148,34 @@ int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev,
off += bsize;
}
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
+ mutex_unlock(&rt2x00dev->csr_mutex);
return status;
}
EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_large_buff);
+int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ struct rt2x00_field32 field,
+ u32 *reg)
+{
+ unsigned int i;
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2x00usb_register_read_lock(rt2x00dev, offset, reg);
+ if (!rt2x00_get_field32(*reg, field))
+ return 1;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ ERROR(rt2x00dev, "Indirect register access failed: "
+ "offset=0x%.08x, value=0x%.08x\n", offset, *reg);
+ *reg = ~0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read);
+
/*
* TX data handlers.
*/
@@ -212,10 +234,10 @@ int rt2x00usb_write_tx_data(struct queue_entry *entry)
* length of the data to usb_fill_bulk_urb. Pass the skb
* to the driver to determine what the length should be.
*/
- length = rt2x00dev->ops->lib->get_tx_data_len(rt2x00dev, entry->skb);
+ length = rt2x00dev->ops->lib->get_tx_data_len(entry);
usb_fill_bulk_urb(entry_priv->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, 1),
+ usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint),
entry->skb->data, length,
rt2x00usb_interrupt_txdone, entry);
@@ -351,28 +373,96 @@ EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio);
/*
* Device initialization handlers.
*/
-void rt2x00usb_init_rxentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+void rt2x00usb_clear_entry(struct queue_entry *entry)
{
- struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
+ struct usb_device *usb_dev =
+ to_usb_device_intf(entry->queue->rt2x00dev->dev);
struct queue_entry_priv_usb *entry_priv = entry->priv_data;
+ int pipe;
- usb_fill_bulk_urb(entry_priv->urb, usb_dev,
- usb_rcvbulkpipe(usb_dev, 1),
- entry->skb->data, entry->skb->len,
- rt2x00usb_interrupt_rxdone, entry);
+ if (entry->queue->qid == QID_RX) {
+ pipe = usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint);
+ usb_fill_bulk_urb(entry_priv->urb, usb_dev, pipe,
+ entry->skb->data, entry->skb->len,
+ rt2x00usb_interrupt_rxdone, entry);
- set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
- usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
+ set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
+ usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
+ } else {
+ entry->flags = 0;
+ }
}
-EXPORT_SYMBOL_GPL(rt2x00usb_init_rxentry);
+EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry);
-void rt2x00usb_init_txentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static void rt2x00usb_assign_endpoint(struct data_queue *queue,
+ struct usb_endpoint_descriptor *ep_desc)
{
- entry->flags = 0;
+ struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev);
+ int pipe;
+
+ queue->usb_endpoint = usb_endpoint_num(ep_desc);
+
+ if (queue->qid == QID_RX) {
+ pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint);
+ queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0);
+ } else {
+ pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint);
+ queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1);
+ }
+
+ if (!queue->usb_maxpacket)
+ queue->usb_maxpacket = 1;
+}
+
+static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev)
+{
+ struct usb_interface *intf = to_usb_interface(rt2x00dev->dev);
+ struct usb_host_interface *intf_desc = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *ep_desc;
+ struct data_queue *queue = rt2x00dev->tx;
+ struct usb_endpoint_descriptor *tx_ep_desc = NULL;
+ unsigned int i;
+
+ /*
+ * Walk through all available endpoints to search for "bulk in"
+ * and "bulk out" endpoints. When we find such endpoints collect
+ * the information we need from the descriptor and assign it
+ * to the queue.
+ */
+ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+ ep_desc = &intf_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(ep_desc)) {
+ rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc);
+ } else if (usb_endpoint_is_bulk_out(ep_desc)) {
+ rt2x00usb_assign_endpoint(queue, ep_desc);
+
+ if (queue != queue_end(rt2x00dev))
+ queue = queue_next(queue);
+ tx_ep_desc = ep_desc;
+ }
+ }
+
+ /*
+ * At least 1 endpoint for RX and 1 endpoint for TX must be available.
+ */
+ if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) {
+ ERROR(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n");
+ return -EPIPE;
+ }
+
+ /*
+ * It might be possible not all queues have a dedicated endpoint.
+ * Loop through all TX queues and copy the endpoint information
+ * which we have gathered from already assigned endpoints.
+ */
+ txall_queue_for_each(rt2x00dev, queue) {
+ if (!queue->usb_endpoint)
+ rt2x00usb_assign_endpoint(queue, tx_ep_desc);
+ }
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(rt2x00usb_init_txentry);
static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev,
struct data_queue *queue)
@@ -445,6 +535,13 @@ int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev)
int status;
/*
+ * Find endpoints for each queue
+ */
+ status = rt2x00usb_find_endpoints(rt2x00dev);
+ if (status)
+ goto exit;
+
+ /*
* Allocate DMA
*/
queue_for_each(rt2x00dev, queue) {
@@ -534,12 +631,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
rt2x00dev->dev = &usb_intf->dev;
rt2x00dev->ops = ops;
rt2x00dev->hw = hw;
- mutex_init(&rt2x00dev->usb_cache_mutex);
-
- rt2x00dev->usb_maxpacket =
- usb_maxpacket(usb_dev, usb_sndbulkpipe(usb_dev, 1), 1);
- if (!rt2x00dev->usb_maxpacket)
- rt2x00dev->usb_maxpacket = 1;
retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval)
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index 3b4a67417f95..2bd4ac855f52 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -231,6 +231,142 @@ static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev,
REGISTER_TIMEOUT16(length));
}
+/**
+ * rt2x00usb_regbusy_read - Read 32bit register word
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Pointer to where register contents should be stored
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_request_buff().
+ */
+static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
+{
+ __le32 reg;
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+ *value = le32_to_cpu(reg);
+}
+
+/**
+ * rt2x00usb_register_read_lock - Read 32bit register word
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Pointer to where register contents should be stored
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_req_buff_lock().
+ */
+static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
+{
+ __le32 reg;
+ rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+ *value = le32_to_cpu(reg);
+}
+
+/**
+ * rt2x00usb_register_multiread - Read 32bit register words
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Pointer to where register contents should be stored
+ * @length: Length of the data
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_request_buff().
+ */
+static inline void rt2x00usb_register_multiread(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u32 length)
+{
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
+ USB_VENDOR_REQUEST_IN, offset,
+ value, length,
+ REGISTER_TIMEOUT32(length));
+}
+
+/**
+ * rt2x00usb_register_write - Write 32bit register word
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Data which should be written
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_request_buff().
+ */
+static inline void rt2x00usb_register_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
+{
+ __le32 reg = cpu_to_le32(value);
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+}
+
+/**
+ * rt2x00usb_register_write_lock - Write 32bit register word
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Data which should be written
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_req_buff_lock().
+ */
+static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
+{
+ __le32 reg = cpu_to_le32(value);
+ rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ &reg, sizeof(reg), REGISTER_TIMEOUT);
+}
+
+/**
+ * rt2x00usb_register_multiwrite - Write 32bit register words
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @value: Data which should be written
+ * @length: Length of the data
+ *
+ * This function is a simple wrapper for 32bit register access
+ * through rt2x00usb_vendor_request_buff().
+ */
+static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u32 length)
+{
+ rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
+ USB_VENDOR_REQUEST_OUT, offset,
+ value, length,
+ REGISTER_TIMEOUT32(length));
+}
+
+/**
+ * rt2x00usb_regbusy_read - Read from register with busy check
+ * @rt2x00dev: Device pointer, see &struct rt2x00_dev.
+ * @offset: Register offset
+ * @field: Field to check if register is busy
+ * @reg: Pointer to where register contents should be stored
+ *
+ * This function will read the given register, and checks if the
+ * register is busy. If it is, it will sleep for a couple of
+ * microseconds before reading the register again. If the register
+ * is not read after a certain timeout, this function will return
+ * FALSE.
+ */
+int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ struct rt2x00_field32 field,
+ u32 *reg);
+
/*
* Radio handlers
*/
@@ -286,10 +422,7 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
/*
* Device initialization handlers.
*/
-void rt2x00usb_init_rxentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry);
-void rt2x00usb_init_txentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry);
+void rt2x00usb_clear_entry(struct queue_entry *entry);
int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev);
void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index a461620b489f..987e89009f74 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -55,45 +55,36 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
* the access attempt is considered to have failed,
* and we will print an error.
*/
-static u32 rt61pci_bbp_check(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- unsigned int i;
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, PHY_CSR3, &reg);
- if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- return reg;
-}
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg))
+#define WAIT_FOR_MCU(__dev, __reg) \
+ rt2x00pci_regbusy_read((__dev), H2M_MAILBOX_CSR, \
+ H2M_MAILBOX_CSR_OWNER, (__reg))
static void rt61pci_bbp_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u8 value)
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt61pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the data into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR3_VALUE, value);
- rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
- rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
- rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 0);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR3_VALUE, value);
+ rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
+ rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
+ rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 0);
+
+ rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg);
+ }
- rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -101,66 +92,58 @@ static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt61pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
- return;
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the request into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
*/
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
- rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
- rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 1);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
+ rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
+ rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 1);
- rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg);
+ rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg);
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt61pci_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) {
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
- *value = 0xff;
- return;
+ WAIT_FOR_BBP(rt2x00dev, &reg);
}
*value = rt2x00_get_field32(reg, PHY_CSR3_VALUE);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt61pci_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u32 value)
{
u32 reg;
- unsigned int i;
if (!word)
return;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00pci_register_read(rt2x00dev, PHY_CSR4, &reg);
- if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY))
- goto rf_write;
- udelay(REGISTER_BUSY_DELAY);
- }
+ mutex_lock(&rt2x00dev->csr_mutex);
- ERROR(rt2x00dev, "PHY_CSR4 register busy. Write failed.\n");
- return;
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR4_VALUE, value);
+ rt2x00_set_field32(&reg, PHY_CSR4_NUMBER_OF_BITS, 21);
+ rt2x00_set_field32(&reg, PHY_CSR4_IF_SELECT, 0);
+ rt2x00_set_field32(&reg, PHY_CSR4_BUSY, 1);
-rf_write:
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR4_VALUE, value);
- rt2x00_set_field32(&reg, PHY_CSR4_NUMBER_OF_BITS, 21);
- rt2x00_set_field32(&reg, PHY_CSR4_IF_SELECT, 0);
- rt2x00_set_field32(&reg, PHY_CSR4_BUSY, 1);
+ rt2x00pci_register_write(rt2x00dev, PHY_CSR4, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
- rt2x00pci_register_write(rt2x00dev, PHY_CSR4, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
#ifdef CONFIG_RT2X00_LIB_LEDS
@@ -175,25 +158,27 @@ static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt2x00pci_register_read(rt2x00dev, H2M_MAILBOX_CSR, &reg);
+ mutex_lock(&rt2x00dev->csr_mutex);
- if (rt2x00_get_field32(reg, H2M_MAILBOX_CSR_OWNER)) {
- ERROR(rt2x00dev, "mcu request error. "
- "Request 0x%02x failed for token 0x%02x.\n",
- command, token);
- return;
+ /*
+ * Wait until the MCU becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_MCU(rt2x00dev, &reg)) {
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_OWNER, 1);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_CMD_TOKEN, token);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG0, arg0);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG1, arg1);
+ rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg);
+
+ rt2x00pci_register_read(rt2x00dev, HOST_CMD_CSR, &reg);
+ rt2x00_set_field32(&reg, HOST_CMD_CSR_HOST_COMMAND, command);
+ rt2x00_set_field32(&reg, HOST_CMD_CSR_INTERRUPT_MCU, 1);
+ rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg);
}
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_OWNER, 1);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_CMD_TOKEN, token);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG0, arg0);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG1, arg1);
- rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg);
+ mutex_unlock(&rt2x00dev->csr_mutex);
- rt2x00pci_register_read(rt2x00dev, HOST_CMD_CSR, &reg);
- rt2x00_set_field32(&reg, HOST_CMD_CSR_HOST_COMMAND, command);
- rt2x00_set_field32(&reg, HOST_CMD_CSR_INTERRUPT_MCU, 1);
- rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg);
}
#endif /* CONFIG_RT2X00_LIB_LEDS */
@@ -228,43 +213,34 @@ static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
}
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) )
-
-static void rt61pci_read_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 *data)
-{
- rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data);
-}
-
-static void rt61pci_write_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 data)
-{
- rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data);
-}
-
static const struct rt2x00debug rt61pci_rt2x00debug = {
.owner = THIS_MODULE,
.csr = {
- .read = rt61pci_read_csr,
- .write = rt61pci_write_csr,
+ .read = rt2x00pci_register_read,
+ .write = rt2x00pci_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
.word_size = sizeof(u32),
.word_count = CSR_REG_SIZE / sizeof(u32),
},
.eeprom = {
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
.word_size = sizeof(u16),
.word_count = EEPROM_SIZE / sizeof(u16),
},
.bbp = {
.read = rt61pci_bbp_read,
.write = rt61pci_bbp_write,
+ .word_base = BBP_BASE,
.word_size = sizeof(u8),
.word_count = BBP_SIZE / sizeof(u8),
},
.rf = {
.read = rt2x00_rf_read,
.write = rt61pci_rf_write,
+ .word_base = RF_BASE,
.word_size = sizeof(u32),
.word_count = RF_SIZE / sizeof(u32),
},
@@ -643,95 +619,18 @@ static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
!!erp->short_preamble);
rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
-}
-
-
-static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- u16 eeprom;
- short lna_gain = 0;
-
- if (libconf->band == IEEE80211_BAND_2GHZ) {
- if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags))
- lna_gain += 14;
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
- lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
- } else {
- if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags))
- lna_gain += 14;
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
- lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1);
- }
-
- rt2x00dev->lna_gain = lna_gain;
-}
-
-static void rt61pci_config_phymode(struct rt2x00_dev *rt2x00dev,
- const int basic_rate_mask)
-{
- rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, basic_rate_mask);
-}
-
-static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev,
- struct rf_channel *rf, const int txpower)
-{
- u8 r3;
- u8 r94;
- u8 smart;
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
- rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
-
- smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
- rt2x00_rf(&rt2x00dev->chip, RF2527));
-
- rt61pci_bbp_read(rt2x00dev, 3, &r3);
- rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
- rt61pci_bbp_write(rt2x00dev, 3, r3);
-
- r94 = 6;
- if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94))
- r94 += txpower - MAX_TXPOWER;
- else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94))
- r94 += txpower;
- rt61pci_bbp_write(rt2x00dev, 94, r94);
+ rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
- rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
- rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
- rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
-
- udelay(200);
-
- rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
- rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
- rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
- rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
-
- udelay(200);
-
- rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
- rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
- rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
-
- msleep(1);
-}
-
-static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev,
- const int txpower)
-{
- struct rf_channel rf;
-
- rt2x00_rf_read(rt2x00dev, 1, &rf.rf1);
- rt2x00_rf_read(rt2x00dev, 2, &rf.rf2);
- rt2x00_rf_read(rt2x00dev, 3, &rf.rf3);
- rt2x00_rf_read(rt2x00dev, 4, &rf.rf4);
+ rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+ rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
- rt61pci_config_channel(rt2x00dev, &rf, txpower);
+ rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+ rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+ rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+ rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
}
static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
@@ -906,8 +805,8 @@ static const struct antenna_sel antenna_sel_bg[] = {
{ 98, { 0x48, 0x48 } },
};
-static void rt61pci_config_antenna(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
+static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
{
const struct antenna_sel *sel;
unsigned int lna;
@@ -954,20 +853,105 @@ static void rt61pci_config_antenna(struct rt2x00_dev *rt2x00dev,
}
}
-static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev,
+static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ u16 eeprom;
+ short lna_gain = 0;
+
+ if (libconf->conf->channel->band == IEEE80211_BAND_2GHZ) {
+ if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags))
+ lna_gain += 14;
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
+ lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
+ } else {
+ if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags))
+ lna_gain += 14;
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
+ lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1);
+ }
+
+ rt2x00dev->lna_gain = lna_gain;
+}
+
+static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev,
+ struct rf_channel *rf, const int txpower)
+{
+ u8 r3;
+ u8 r94;
+ u8 smart;
+
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+ rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
+
+ smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
+ rt2x00_rf(&rt2x00dev->chip, RF2527));
+
+ rt61pci_bbp_read(rt2x00dev, 3, &r3);
+ rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
+ rt61pci_bbp_write(rt2x00dev, 3, r3);
+
+ r94 = 6;
+ if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94))
+ r94 += txpower - MAX_TXPOWER;
+ else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94))
+ r94 += txpower;
+ rt61pci_bbp_write(rt2x00dev, 94, r94);
+
+ rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
+ rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
+ rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
+
+ udelay(200);
+
+ rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
+ rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
+ rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
+ rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
+
+ udelay(200);
+
+ rt61pci_rf_write(rt2x00dev, 1, rf->rf1);
+ rt61pci_rf_write(rt2x00dev, 2, rf->rf2);
+ rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt61pci_rf_write(rt2x00dev, 4, rf->rf4);
+
+ msleep(1);
+}
+
+static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev,
+ const int txpower)
+{
+ struct rf_channel rf;
+
+ rt2x00_rf_read(rt2x00dev, 1, &rf.rf1);
+ rt2x00_rf_read(rt2x00dev, 2, &rf.rf2);
+ rt2x00_rf_read(rt2x00dev, 3, &rf.rf3);
+ rt2x00_rf_read(rt2x00dev, 4, &rf.rf4);
+
+ rt61pci_config_channel(rt2x00dev, &rf, txpower);
+}
+
+static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf)
{
u32 reg;
- rt2x00pci_register_read(rt2x00dev, MAC_CSR9, &reg);
- rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, libconf->slot_time);
- rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
+ rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
+ rt2x00_set_field32(&reg, TXRX_CSR4_LONG_RETRY_LIMIT,
+ libconf->conf->long_frame_max_tx_count);
+ rt2x00_set_field32(&reg, TXRX_CSR4_SHORT_RETRY_LIMIT,
+ libconf->conf->short_frame_max_tx_count);
+ rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
+}
- rt2x00pci_register_read(rt2x00dev, MAC_CSR8, &reg);
- rt2x00_set_field32(&reg, MAC_CSR8_SIFS, libconf->sifs);
- rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
- rt2x00_set_field32(&reg, MAC_CSR8_EIFS, libconf->eifs);
- rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg);
+static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ u32 reg;
rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
@@ -990,16 +974,15 @@ static void rt61pci_config(struct rt2x00_dev *rt2x00dev,
/* Always recalculate LNA gain before changing configuration */
rt61pci_config_lna_gain(rt2x00dev, libconf);
- if (flags & CONFIG_UPDATE_PHYMODE)
- rt61pci_config_phymode(rt2x00dev, libconf->basic_rates);
- if (flags & CONFIG_UPDATE_CHANNEL)
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt61pci_config_channel(rt2x00dev, &libconf->rf,
libconf->conf->power_level);
- if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL))
+ if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
+ !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level);
- if (flags & CONFIG_UPDATE_ANTENNA)
- rt61pci_config_antenna(rt2x00dev, &libconf->ant);
- if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT))
+ if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ rt61pci_config_retry_limit(rt2x00dev, libconf);
+ if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
rt61pci_config_duration(rt2x00dev, libconf);
}
@@ -1263,33 +1246,44 @@ static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data,
/*
* Initialization functions.
*/
-static void rt61pci_init_rxentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static bool rt61pci_get_entry_state(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 5, &word);
- rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS,
- skbdesc->skb_dma);
- rt2x00_desc_write(entry_priv->desc, 5, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+
+ return rt2x00_get_field32(word, RXD_W0_OWNER_NIC);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) ||
+ rt2x00_get_field32(word, TXD_W0_VALID));
+ }
}
-static void rt61pci_init_txentry(struct rt2x00_dev *rt2x00dev,
- struct queue_entry *entry)
+static void rt61pci_clear_entry(struct queue_entry *entry)
{
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
u32 word;
- rt2x00_desc_read(entry_priv->desc, 0, &word);
- rt2x00_set_field32(&word, TXD_W0_VALID, 0);
- rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
- rt2x00_desc_write(entry_priv->desc, 0, word);
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 5, &word);
+ rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS,
+ skbdesc->skb_dma);
+ rt2x00_desc_write(entry_priv->desc, 5, word);
+
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, TXD_W0_VALID, 0);
+ rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+ }
}
static int rt61pci_init_queues(struct rt2x00_dev *rt2x00dev)
@@ -1784,8 +1778,8 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_desc_write(txd, 2, word);
if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) {
- _rt2x00_desc_write(txd, 3, skbdesc->iv);
- _rt2x00_desc_write(txd, 4, skbdesc->eiv);
+ _rt2x00_desc_write(txd, 3, skbdesc->iv[0]);
+ _rt2x00_desc_write(txd, 4, skbdesc->iv[1]);
}
rt2x00_desc_read(txd, 5, &word);
@@ -1934,7 +1928,7 @@ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
}
static void rt61pci_fill_rxdone(struct queue_entry *entry,
- struct rxdone_entry_desc *rxdesc)
+ struct rxdone_entry_desc *rxdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct queue_entry_priv_pci *entry_priv = entry->priv_data;
@@ -1955,9 +1949,12 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry,
}
if (rxdesc->cipher != CIPHER_NONE) {
- _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv);
- _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->eiv);
+ _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]);
+ _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->iv[1]);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_IV;
+
_rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_ICV;
/*
* Hardware has stripped IV/EIV data from 802.11 frame during
@@ -2175,10 +2172,8 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
*/
mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
- DECLARE_MAC_BUF(macbuf);
-
random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac));
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
}
rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
@@ -2630,20 +2625,6 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt61pci_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retry)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u32 reg;
-
- rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, &reg);
- rt2x00_set_field32(&reg, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry);
- rt2x00_set_field32(&reg, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry);
- rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
-
- return 0;
-}
-
static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
@@ -2726,7 +2707,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
.configure_filter = rt2x00mac_configure_filter,
.set_key = rt2x00mac_set_key,
.get_stats = rt2x00mac_get_stats,
- .set_retry_limit = rt61pci_set_retry_limit,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt61pci_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
@@ -2741,8 +2721,8 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
.load_firmware = rt61pci_load_firmware,
.initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize,
- .init_rxentry = rt61pci_init_rxentry,
- .init_txentry = rt61pci_init_txentry,
+ .get_entry_state = rt61pci_get_entry_state,
+ .clear_entry = rt61pci_clear_entry,
.set_device_state = rt61pci_set_device_state,
.rfkill_poll = rt61pci_rfkill_poll,
.link_stats = rt61pci_link_stats,
@@ -2758,6 +2738,7 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
.config_filter = rt61pci_config_filter,
.config_intf = rt61pci_config_intf,
.config_erp = rt61pci_config_erp,
+ .config_ant = rt61pci_config_ant,
.config = rt61pci_config,
};
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h
index 8ec1451308cc..65fe3332364a 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/rt2x00/rt61pci.h
@@ -48,7 +48,9 @@
#define CSR_REG_SIZE 0x04b0
#define EEPROM_BASE 0x0000
#define EEPROM_SIZE 0x0100
+#define BBP_BASE 0x0000
#define BBP_SIZE 0x0080
+#define RF_BASE 0x0000
#define RF_SIZE 0x0014
/*
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 934f8e03c5aa..d638a8a59370 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -46,7 +46,7 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/*
* Register access.
* All access to the CSR registers will go through the methods
- * rt73usb_register_read and rt73usb_register_write.
+ * rt2x00usb_register_read and rt2x00usb_register_write.
* BBP and RF register require indirect register access,
* and use the CSR registers BBPCSR and RFCSR to achieve this.
* These indirect registers work with busy bits,
@@ -55,113 +55,35 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
* between each attampt. When the busy bit is still set at that time,
* the access attempt is considered to have failed,
* and we will print an error.
- * The _lock versions must be used if you already hold the usb_cache_mutex
+ * The _lock versions must be used if you already hold the csr_mutex
*/
-static inline void rt73usb_register_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset, u32 *value)
-{
- __le32 reg;
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(u32), REGISTER_TIMEOUT);
- *value = le32_to_cpu(reg);
-}
-
-static inline void rt73usb_register_read_lock(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset, u32 *value)
-{
- __le32 reg;
- rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- &reg, sizeof(u32), REGISTER_TIMEOUT);
- *value = le32_to_cpu(reg);
-}
-
-static inline void rt73usb_register_multiread(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u32 length)
-{
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
- USB_VENDOR_REQUEST_IN, offset,
- value, length,
- REGISTER_TIMEOUT32(length));
-}
-
-static inline void rt73usb_register_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset, u32 value)
-{
- __le32 reg = cpu_to_le32(value);
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(u32), REGISTER_TIMEOUT);
-}
-
-static inline void rt73usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset, u32 value)
-{
- __le32 reg = cpu_to_le32(value);
- rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- &reg, sizeof(u32), REGISTER_TIMEOUT);
-}
-
-static inline void rt73usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u32 length)
-{
- rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT, offset,
- value, length,
- REGISTER_TIMEOUT32(length));
-}
-
-static u32 rt73usb_bbp_check(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- unsigned int i;
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt73usb_register_read_lock(rt2x00dev, PHY_CSR3, &reg);
- if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- return reg;
-}
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2x00usb_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2x00usb_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg))
static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u8 value)
{
u32 reg;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Wait until the BBP becomes ready.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
- goto exit_fail;
-
- /*
- * Write the data into the BBP.
- */
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR3_VALUE, value);
- rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
- rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
- rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 0);
-
- rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-
- return;
-
-exit_fail:
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR3_VALUE, value);
+ rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
+ rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
+ rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 0);
+
+ rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg);
+ }
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n");
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev,
@@ -169,123 +91,95 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
-
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
- goto exit_fail;
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * Write the request into the BBP.
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
*/
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
- rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
- rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 1);
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR3_REGNUM, word);
+ rt2x00_set_field32(&reg, PHY_CSR3_BUSY, 1);
+ rt2x00_set_field32(&reg, PHY_CSR3_READ_CONTROL, 1);
- rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg);
+ rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg);
- /*
- * Wait until the BBP becomes ready.
- */
- reg = rt73usb_bbp_check(rt2x00dev);
- if (rt2x00_get_field32(reg, PHY_CSR3_BUSY))
- goto exit_fail;
+ WAIT_FOR_BBP(rt2x00dev, &reg);
+ }
*value = rt2x00_get_field32(reg, PHY_CSR3_VALUE);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-
- return;
-
-exit_fail:
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n");
- *value = 0xff;
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev,
const unsigned int word, const u32 value)
{
u32 reg;
- unsigned int i;
if (!word)
return;
- mutex_lock(&rt2x00dev->usb_cache_mutex);
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt73usb_register_read_lock(rt2x00dev, PHY_CSR4, &reg);
- if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY))
- goto rf_write;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
- ERROR(rt2x00dev, "PHY_CSR4 register busy. Write failed.\n");
- return;
-
-rf_write:
- reg = 0;
- rt2x00_set_field32(&reg, PHY_CSR4_VALUE, value);
+ mutex_lock(&rt2x00dev->csr_mutex);
/*
- * RF5225 and RF2527 contain 21 bits per RF register value,
- * all others contain 20 bits.
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
*/
- rt2x00_set_field32(&reg, PHY_CSR4_NUMBER_OF_BITS,
- 20 + (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
- rt2x00_rf(&rt2x00dev->chip, RF2527)));
- rt2x00_set_field32(&reg, PHY_CSR4_IF_SELECT, 0);
- rt2x00_set_field32(&reg, PHY_CSR4_BUSY, 1);
-
- rt73usb_register_write_lock(rt2x00dev, PHY_CSR4, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
- mutex_unlock(&rt2x00dev->usb_cache_mutex);
-}
-
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) )
-
-static void rt73usb_read_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 *data)
-{
- rt73usb_register_read(rt2x00dev, CSR_OFFSET(word), data);
-}
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, PHY_CSR4_VALUE, value);
+ /*
+ * RF5225 and RF2527 contain 21 bits per RF register value,
+ * all others contain 20 bits.
+ */
+ rt2x00_set_field32(&reg, PHY_CSR4_NUMBER_OF_BITS,
+ 20 + (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
+ rt2x00_rf(&rt2x00dev->chip, RF2527)));
+ rt2x00_set_field32(&reg, PHY_CSR4_IF_SELECT, 0);
+ rt2x00_set_field32(&reg, PHY_CSR4_BUSY, 1);
+
+ rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR4, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
-static void rt73usb_write_csr(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u32 data)
-{
- rt73usb_register_write(rt2x00dev, CSR_OFFSET(word), data);
+ mutex_unlock(&rt2x00dev->csr_mutex);
}
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
static const struct rt2x00debug rt73usb_rt2x00debug = {
.owner = THIS_MODULE,
.csr = {
- .read = rt73usb_read_csr,
- .write = rt73usb_write_csr,
+ .read = rt2x00usb_register_read,
+ .write = rt2x00usb_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
.word_size = sizeof(u32),
.word_count = CSR_REG_SIZE / sizeof(u32),
},
.eeprom = {
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
.word_size = sizeof(u16),
.word_count = EEPROM_SIZE / sizeof(u16),
},
.bbp = {
.read = rt73usb_bbp_read,
.write = rt73usb_bbp_write,
+ .word_base = BBP_BASE,
.word_size = sizeof(u8),
.word_count = BBP_SIZE / sizeof(u8),
},
.rf = {
.read = rt2x00_rf_read,
.write = rt73usb_rf_write,
+ .word_base = RF_BASE,
.word_size = sizeof(u32),
.word_count = RF_SIZE / sizeof(u32),
},
@@ -341,10 +235,10 @@ static int rt73usb_blink_set(struct led_classdev *led_cdev,
container_of(led_cdev, struct rt2x00_led, led_dev);
u32 reg;
- rt73usb_register_read(led->rt2x00dev, MAC_CSR14, &reg);
+ rt2x00usb_register_read(led->rt2x00dev, MAC_CSR14, &reg);
rt2x00_set_field32(&reg, MAC_CSR14_ON_PERIOD, *delay_on);
rt2x00_set_field32(&reg, MAC_CSR14_OFF_PERIOD, *delay_off);
- rt73usb_register_write(led->rt2x00dev, MAC_CSR14, reg);
+ rt2x00usb_register_write(led->rt2x00dev, MAC_CSR14, reg);
return 0;
}
@@ -387,7 +281,7 @@ static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev,
*/
mask = (0xf << crypto->bssidx);
- rt73usb_register_read(rt2x00dev, SEC_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR0, &reg);
reg &= mask;
if (reg && reg == mask)
@@ -424,16 +318,16 @@ static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev,
field.bit_offset = (3 * key->hw_key_idx);
field.bit_mask = 0x7 << field.bit_offset;
- rt73usb_register_read(rt2x00dev, SEC_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR1, &reg);
rt2x00_set_field32(&reg, field, crypto->cipher);
- rt73usb_register_write(rt2x00dev, SEC_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR1, reg);
} else {
field.bit_offset = (3 * (key->hw_key_idx - 8));
field.bit_mask = 0x7 << field.bit_offset;
- rt73usb_register_read(rt2x00dev, SEC_CSR5, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR5, &reg);
rt2x00_set_field32(&reg, field, crypto->cipher);
- rt73usb_register_write(rt2x00dev, SEC_CSR5, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR5, reg);
}
/*
@@ -456,12 +350,12 @@ static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev,
*/
mask = 1 << key->hw_key_idx;
- rt73usb_register_read(rt2x00dev, SEC_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR0, &reg);
if (crypto->cmd == SET_KEY)
reg |= mask;
else if (crypto->cmd == DISABLE_KEY)
reg &= ~mask;
- rt73usb_register_write(rt2x00dev, SEC_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR0, reg);
return 0;
}
@@ -486,10 +380,10 @@ static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
* When both registers are full, we drop the key,
* otherwise we use the first invalid entry.
*/
- rt73usb_register_read(rt2x00dev, SEC_CSR2, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR2, &reg);
if (reg && reg == ~0) {
key->hw_key_idx = 32;
- rt73usb_register_read(rt2x00dev, SEC_CSR3, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR3, &reg);
if (reg && reg == ~0)
return -ENOSPC;
}
@@ -517,14 +411,14 @@ static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
/*
* Send the address and cipher type to the hardware register.
* This data fits within the CSR cache size, so we can use
- * rt73usb_register_multiwrite() directly.
+ * rt2x00usb_register_multiwrite() directly.
*/
memset(&addr_entry, 0, sizeof(addr_entry));
memcpy(&addr_entry, crypto->address, ETH_ALEN);
addr_entry.cipher = crypto->cipher;
reg = PAIRWISE_TA_ENTRY(key->hw_key_idx);
- rt73usb_register_multiwrite(rt2x00dev, reg,
+ rt2x00usb_register_multiwrite(rt2x00dev, reg,
&addr_entry, sizeof(addr_entry));
/*
@@ -532,9 +426,9 @@ static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
* without this received frames will not be decrypted
* by the hardware.
*/
- rt73usb_register_read(rt2x00dev, SEC_CSR4, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR4, &reg);
reg |= (1 << crypto->bssidx);
- rt73usb_register_write(rt2x00dev, SEC_CSR4, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR4, reg);
/*
* The driver does not support the IV/EIV generation
@@ -557,21 +451,21 @@ static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
if (key->hw_key_idx < 32) {
mask = 1 << key->hw_key_idx;
- rt73usb_register_read(rt2x00dev, SEC_CSR2, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR2, &reg);
if (crypto->cmd == SET_KEY)
reg |= mask;
else if (crypto->cmd == DISABLE_KEY)
reg &= ~mask;
- rt73usb_register_write(rt2x00dev, SEC_CSR2, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR2, reg);
} else {
mask = 1 << (key->hw_key_idx - 32);
- rt73usb_register_read(rt2x00dev, SEC_CSR3, &reg);
+ rt2x00usb_register_read(rt2x00dev, SEC_CSR3, &reg);
if (crypto->cmd == SET_KEY)
reg |= mask;
else if (crypto->cmd == DISABLE_KEY)
reg &= ~mask;
- rt73usb_register_write(rt2x00dev, SEC_CSR3, reg);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR3, reg);
}
return 0;
@@ -588,7 +482,7 @@ static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev,
* and broadcast frames will always be accepted since
* there is no filter for it at this time.
*/
- rt73usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_CRC,
!(filter_flags & FIF_FCSFAIL));
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_PHYSICAL,
@@ -606,7 +500,7 @@ static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_BROADCAST, 0);
rt2x00_set_field32(&reg, TXRX_CSR0_DROP_ACK_CTS,
!(filter_flags & FIF_CONTROL));
- rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
}
static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev,
@@ -625,16 +519,16 @@ static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev,
* bits which (when set to 0) will invalidate the entire beacon.
*/
beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx);
- rt73usb_register_write(rt2x00dev, beacon_base, 0);
+ rt2x00usb_register_write(rt2x00dev, beacon_base, 0);
/*
* Enable synchronisation.
*/
- rt73usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_SYNC, conf->sync);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
}
if (flags & CONFIG_UPDATE_MAC) {
@@ -642,7 +536,7 @@ static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff);
conf->mac[1] = cpu_to_le32(reg);
- rt73usb_register_multiwrite(rt2x00dev, MAC_CSR2,
+ rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR2,
conf->mac, sizeof(conf->mac));
}
@@ -651,7 +545,7 @@ static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, MAC_CSR5_BSS_ID_MASK, 3);
conf->bssid[1] = cpu_to_le32(reg);
- rt73usb_register_multiwrite(rt2x00dev, MAC_CSR4,
+ rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR4,
conf->bssid, sizeof(conf->bssid));
}
}
@@ -661,95 +555,26 @@ static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt73usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_RX_ACK_TIMEOUT, erp->ack_timeout);
- rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_PREAMBLE,
!!erp->short_preamble);
- rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg);
-}
-
-static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- u16 eeprom;
- short lna_gain = 0;
-
- if (libconf->band == IEEE80211_BAND_2GHZ) {
- if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags))
- lna_gain += 14;
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
- lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
- } else {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
- lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1);
- }
-
- rt2x00dev->lna_gain = lna_gain;
-}
-
-static void rt73usb_config_phymode(struct rt2x00_dev *rt2x00dev,
- const int basic_rate_mask)
-{
- rt73usb_register_write(rt2x00dev, TXRX_CSR5, basic_rate_mask);
-}
-
-static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev,
- struct rf_channel *rf, const int txpower)
-{
- u8 r3;
- u8 r94;
- u8 smart;
-
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
- rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
-
- smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
- rt2x00_rf(&rt2x00dev->chip, RF2527));
-
- rt73usb_bbp_read(rt2x00dev, 3, &r3);
- rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
- rt73usb_bbp_write(rt2x00dev, 3, r3);
-
- r94 = 6;
- if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94))
- r94 += txpower - MAX_TXPOWER;
- else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94))
- r94 += txpower;
- rt73usb_bbp_write(rt2x00dev, 94, r94);
-
- rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
- rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
- rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
- rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, erp->slot_time);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
- udelay(10);
-}
-
-static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev,
- const int txpower)
-{
- struct rf_channel rf;
-
- rt2x00_rf_read(rt2x00dev, 1, &rf.rf1);
- rt2x00_rf_read(rt2x00dev, 2, &rf.rf2);
- rt2x00_rf_read(rt2x00dev, 3, &rf.rf3);
- rt2x00_rf_read(rt2x00dev, 4, &rf.rf4);
-
- rt73usb_config_channel(rt2x00dev, &rf, txpower);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR8, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR8_SIFS, erp->sifs);
+ rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
+ rt2x00_set_field32(&reg, MAC_CSR8_EIFS, erp->eifs);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg);
}
static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
@@ -869,8 +694,8 @@ static const struct antenna_sel antenna_sel_bg[] = {
{ 98, { 0x48, 0x48 } },
};
-static void rt73usb_config_antenna(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
+static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
+ struct antenna_setup *ant)
{
const struct antenna_sel *sel;
unsigned int lna;
@@ -895,14 +720,14 @@ static void rt73usb_config_antenna(struct rt2x00_dev *rt2x00dev,
for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
rt73usb_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]);
- rt73usb_register_read(rt2x00dev, PHY_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, PHY_CSR0, &reg);
rt2x00_set_field32(&reg, PHY_CSR0_PA_PE_BG,
(rt2x00dev->curr_band == IEEE80211_BAND_2GHZ));
rt2x00_set_field32(&reg, PHY_CSR0_PA_PE_A,
(rt2x00dev->curr_band == IEEE80211_BAND_5GHZ));
- rt73usb_register_write(rt2x00dev, PHY_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg);
if (rt2x00_rf(&rt2x00dev->chip, RF5226) ||
rt2x00_rf(&rt2x00dev->chip, RF5225))
@@ -912,33 +737,111 @@ static void rt73usb_config_antenna(struct rt2x00_dev *rt2x00dev,
rt73usb_config_antenna_2x(rt2x00dev, ant);
}
-static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev,
+static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
struct rt2x00lib_conf *libconf)
{
+ u16 eeprom;
+ short lna_gain = 0;
+
+ if (libconf->conf->channel->band == IEEE80211_BAND_2GHZ) {
+ if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags))
+ lna_gain += 14;
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
+ lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
+ } else {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
+ lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1);
+ }
+
+ rt2x00dev->lna_gain = lna_gain;
+}
+
+static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev,
+ struct rf_channel *rf, const int txpower)
+{
+ u8 r3;
+ u8 r94;
+ u8 smart;
+
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
+ rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
+
+ smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
+ rt2x00_rf(&rt2x00dev->chip, RF2527));
+
+ rt73usb_bbp_read(rt2x00dev, 3, &r3);
+ rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
+ rt73usb_bbp_write(rt2x00dev, 3, r3);
+
+ r94 = 6;
+ if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94))
+ r94 += txpower - MAX_TXPOWER;
+ else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94))
+ r94 += txpower;
+ rt73usb_bbp_write(rt2x00dev, 94, r94);
+
+ rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
+ rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
+ rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+
+ rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
+ rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
+ rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
+ rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+
+ rt73usb_rf_write(rt2x00dev, 1, rf->rf1);
+ rt73usb_rf_write(rt2x00dev, 2, rf->rf2);
+ rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt73usb_rf_write(rt2x00dev, 4, rf->rf4);
+
+ udelay(10);
+}
+
+static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev,
+ const int txpower)
+{
+ struct rf_channel rf;
+
+ rt2x00_rf_read(rt2x00dev, 1, &rf.rf1);
+ rt2x00_rf_read(rt2x00dev, 2, &rf.rf2);
+ rt2x00_rf_read(rt2x00dev, 3, &rf.rf3);
+ rt2x00_rf_read(rt2x00dev, 4, &rf.rf4);
+
+ rt73usb_config_channel(rt2x00dev, &rf, txpower);
+}
+
+static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
u32 reg;
- rt73usb_register_read(rt2x00dev, MAC_CSR9, &reg);
- rt2x00_set_field32(&reg, MAC_CSR9_SLOT_TIME, libconf->slot_time);
- rt73usb_register_write(rt2x00dev, MAC_CSR9, reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+ rt2x00_set_field32(&reg, TXRX_CSR4_LONG_RETRY_LIMIT,
+ libconf->conf->long_frame_max_tx_count);
+ rt2x00_set_field32(&reg, TXRX_CSR4_SHORT_RETRY_LIMIT,
+ libconf->conf->short_frame_max_tx_count);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+}
- rt73usb_register_read(rt2x00dev, MAC_CSR8, &reg);
- rt2x00_set_field32(&reg, MAC_CSR8_SIFS, libconf->sifs);
- rt2x00_set_field32(&reg, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3);
- rt2x00_set_field32(&reg, MAC_CSR8_EIFS, libconf->eifs);
- rt73usb_register_write(rt2x00dev, MAC_CSR8, reg);
+static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ u32 reg;
- rt73usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
- rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
rt2x00_set_field32(&reg, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL,
libconf->conf->beacon_int * 16);
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
}
static void rt73usb_config(struct rt2x00_dev *rt2x00dev,
@@ -948,16 +851,15 @@ static void rt73usb_config(struct rt2x00_dev *rt2x00dev,
/* Always recalculate LNA gain before changing configuration */
rt73usb_config_lna_gain(rt2x00dev, libconf);
- if (flags & CONFIG_UPDATE_PHYMODE)
- rt73usb_config_phymode(rt2x00dev, libconf->basic_rates);
- if (flags & CONFIG_UPDATE_CHANNEL)
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
rt73usb_config_channel(rt2x00dev, &libconf->rf,
libconf->conf->power_level);
- if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL))
+ if ((flags & IEEE80211_CONF_CHANGE_POWER) &&
+ !(flags & IEEE80211_CONF_CHANGE_CHANNEL))
rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level);
- if (flags & CONFIG_UPDATE_ANTENNA)
- rt73usb_config_antenna(rt2x00dev, &libconf->ant);
- if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT))
+ if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ rt73usb_config_retry_limit(rt2x00dev, libconf);
+ if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
rt73usb_config_duration(rt2x00dev, libconf);
}
@@ -972,13 +874,13 @@ static void rt73usb_link_stats(struct rt2x00_dev *rt2x00dev,
/*
* Update FCS error count from register.
*/
- rt73usb_register_read(rt2x00dev, STA_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, STA_CSR0, &reg);
qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR);
/*
* Update False CCA count from register.
*/
- rt73usb_register_read(rt2x00dev, STA_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, STA_CSR1, &reg);
qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR);
}
@@ -1138,7 +1040,7 @@ static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data,
* Wait for stable hardware.
*/
for (i = 0; i < 100; i++) {
- rt73usb_register_read(rt2x00dev, MAC_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg)
break;
msleep(1);
@@ -1180,13 +1082,13 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
- rt73usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_AUTO_TX_SEQ, 1);
rt2x00_set_field32(&reg, TXRX_CSR0_DISABLE_RX, 0);
rt2x00_set_field32(&reg, TXRX_CSR0_TX_WITHOUT_WAITING, 0);
- rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR1, &reg);
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID0_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID1, 30); /* Rssi */
@@ -1195,12 +1097,12 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev)
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID2_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID3, 30); /* Rssi */
rt2x00_set_field32(&reg, TXRX_CSR1_BBP_ID3_VALID, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR1, reg);
/*
* CCK TXD BBP registers
*/
- rt73usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR2, &reg);
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID0, 13);
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID0_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID1, 12);
@@ -1209,77 +1111,77 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev)
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID2_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID3, 10);
rt2x00_set_field32(&reg, TXRX_CSR2_BBP_ID3_VALID, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR2, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR2, reg);
/*
* OFDM TXD BBP registers
*/
- rt73usb_register_read(rt2x00dev, TXRX_CSR3, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR3, &reg);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID0, 7);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID0_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID1, 6);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID1_VALID, 1);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID2, 5);
rt2x00_set_field32(&reg, TXRX_CSR3_BBP_ID2_VALID, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR3, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR3, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR7, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR7, &reg);
rt2x00_set_field32(&reg, TXRX_CSR7_ACK_CTS_6MBS, 59);
rt2x00_set_field32(&reg, TXRX_CSR7_ACK_CTS_9MBS, 53);
rt2x00_set_field32(&reg, TXRX_CSR7_ACK_CTS_12MBS, 49);
rt2x00_set_field32(&reg, TXRX_CSR7_ACK_CTS_18MBS, 46);
- rt73usb_register_write(rt2x00dev, TXRX_CSR7, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR7, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR8, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR8, &reg);
rt2x00_set_field32(&reg, TXRX_CSR8_ACK_CTS_24MBS, 44);
rt2x00_set_field32(&reg, TXRX_CSR8_ACK_CTS_36MBS, 42);
rt2x00_set_field32(&reg, TXRX_CSR8_ACK_CTS_48MBS, 42);
rt2x00_set_field32(&reg, TXRX_CSR8_ACK_CTS_54MBS, 42);
- rt73usb_register_write(rt2x00dev, TXRX_CSR8, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR8, reg);
- rt73usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_INTERVAL, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_SYNC, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0);
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
- rt73usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f);
- rt73usb_register_read(rt2x00dev, MAC_CSR6, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR6, &reg);
rt2x00_set_field32(&reg, MAC_CSR6_MAX_FRAME_UNIT, 0xfff);
- rt73usb_register_write(rt2x00dev, MAC_CSR6, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR6, reg);
- rt73usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718);
if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE))
return -EBUSY;
- rt73usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00);
/*
* Invalidate all Shared Keys (SEC_CSR0),
* and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5)
*/
- rt73usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000);
- rt73usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000);
- rt73usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000);
+ rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000);
reg = 0x000023b0;
if (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
rt2x00_rf(&rt2x00dev->chip, RF2527))
rt2x00_set_field32(&reg, PHY_CSR1_RF_RPI, 1);
- rt73usb_register_write(rt2x00dev, PHY_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg);
- rt73usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06);
- rt73usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606);
- rt73usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408);
+ rt2x00usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06);
+ rt2x00usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606);
+ rt2x00usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408);
- rt73usb_register_read(rt2x00dev, MAC_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR9, &reg);
rt2x00_set_field32(&reg, MAC_CSR9_CW_SELECT, 0);
- rt73usb_register_write(rt2x00dev, MAC_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
/*
* Clear all beacons
@@ -1287,36 +1189,36 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev)
* the first byte since that byte contains the VALID and OWNER
* bits which (when set to 0) will invalidate the entire beacon.
*/
- rt73usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0);
- rt73usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0);
- rt73usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0);
- rt73usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0);
+ rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0);
+ rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0);
+ rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0);
+ rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0);
/*
* We must clear the error counters.
* These registers are cleared on read,
* so we may pass a useless variable to store the value.
*/
- rt73usb_register_read(rt2x00dev, STA_CSR0, &reg);
- rt73usb_register_read(rt2x00dev, STA_CSR1, &reg);
- rt73usb_register_read(rt2x00dev, STA_CSR2, &reg);
+ rt2x00usb_register_read(rt2x00dev, STA_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, STA_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, STA_CSR2, &reg);
/*
* Reset MAC and BBP registers.
*/
- rt73usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR1, &reg);
rt2x00_set_field32(&reg, MAC_CSR1_SOFT_RESET, 1);
rt2x00_set_field32(&reg, MAC_CSR1_BBP_RESET, 1);
- rt73usb_register_write(rt2x00dev, MAC_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg);
- rt73usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR1, &reg);
rt2x00_set_field32(&reg, MAC_CSR1_SOFT_RESET, 0);
rt2x00_set_field32(&reg, MAC_CSR1_BBP_RESET, 0);
- rt73usb_register_write(rt2x00dev, MAC_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg);
- rt73usb_register_read(rt2x00dev, MAC_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR1, &reg);
rt2x00_set_field32(&reg, MAC_CSR1_HOST_READY, 1);
- rt73usb_register_write(rt2x00dev, MAC_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg);
return 0;
}
@@ -1394,11 +1296,11 @@ static void rt73usb_toggle_rx(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt73usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, &reg);
rt2x00_set_field32(&reg, TXRX_CSR0_DISABLE_RX,
(state == STATE_RADIO_RX_OFF) ||
(state == STATE_RADIO_RX_OFF_LINK));
- rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
}
static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev)
@@ -1415,12 +1317,12 @@ static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev)
static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev)
{
- rt73usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818);
/*
* Disable synchronisation.
*/
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, 0);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, 0);
rt2x00usb_disable_radio(rt2x00dev);
}
@@ -1433,10 +1335,10 @@ static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state)
put_to_sleep = (state != STATE_AWAKE);
- rt73usb_register_read(rt2x00dev, MAC_CSR12, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR12, &reg);
rt2x00_set_field32(&reg, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep);
rt2x00_set_field32(&reg, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep);
- rt73usb_register_write(rt2x00dev, MAC_CSR12, reg);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg);
/*
* Device is not guaranteed to be in the requested state yet.
@@ -1444,7 +1346,7 @@ static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state)
* device has entered the correct state.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt73usb_register_read(rt2x00dev, MAC_CSR12, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR12, &reg);
state = rt2x00_get_field32(reg, MAC_CSR12_BBP_CURRENT_STATE);
if (state == !put_to_sleep)
return 0;
@@ -1526,8 +1428,8 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_desc_write(txd, 2, word);
if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) {
- _rt2x00_desc_write(txd, 3, skbdesc->iv);
- _rt2x00_desc_write(txd, 4, skbdesc->eiv);
+ _rt2x00_desc_write(txd, 3, skbdesc->iv[0]);
+ _rt2x00_desc_write(txd, 4, skbdesc->iv[1]);
}
rt2x00_desc_read(txd, 5, &word);
@@ -1584,11 +1486,11 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
* Disable beaconing while we are reloading the beacon data,
* otherwise we might be sending out invalid data.
*/
- rt73usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
/*
* Write entire beacon with descriptor to register.
@@ -1606,8 +1508,7 @@ static void rt73usb_write_beacon(struct queue_entry *entry)
entry->skb = NULL;
}
-static int rt73usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb)
+static int rt73usb_get_tx_data_len(struct queue_entry *entry)
{
int length;
@@ -1615,8 +1516,8 @@ static int rt73usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev,
* The length _must_ be a multiple of 4,
* but it must _not_ be a multiple of the USB packet size.
*/
- length = roundup(skb->len, 4);
- length += (4 * !(length % rt2x00dev->usb_maxpacket));
+ length = roundup(entry->skb->len, 4);
+ length += (4 * !(length % entry->queue->usb_maxpacket));
return length;
}
@@ -1635,14 +1536,14 @@ static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
* For Wi-Fi faily generated beacons between participating stations.
* Set TBTT phase adaptive adjustment step to 8us (default 16us)
*/
- rt73usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008);
- rt73usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &reg);
if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) {
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 1);
- rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg);
+ rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
}
}
@@ -1685,7 +1586,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
}
static void rt73usb_fill_rxdone(struct queue_entry *entry,
- struct rxdone_entry_desc *rxdesc)
+ struct rxdone_entry_desc *rxdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
@@ -1717,9 +1618,12 @@ static void rt73usb_fill_rxdone(struct queue_entry *entry,
}
if (rxdesc->cipher != CIPHER_NONE) {
- _rt2x00_desc_read(rxd, 2, &rxdesc->iv);
- _rt2x00_desc_read(rxd, 3, &rxdesc->eiv);
+ _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]);
+ _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_IV;
+
_rt2x00_desc_read(rxd, 4, &rxdesc->icv);
+ rxdesc->dev_flags |= RXDONE_CRYPTO_ICV;
/*
* Hardware has stripped IV/EIV data from 802.11 frame during
@@ -1781,10 +1685,8 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
*/
mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
- DECLARE_MAC_BUF(macbuf);
-
random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac));
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
}
rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
@@ -1883,7 +1785,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
* Identify RF chipset.
*/
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
- rt73usb_register_read(rt2x00dev, MAC_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
rt2x00_set_chip(rt2x00dev, RT2571, value, reg);
if (!rt2x00_check_rev(&rt2x00dev->chip, 0x25730)) {
@@ -2211,20 +2113,6 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
/*
* IEEE80211 stack callback functions.
*/
-static int rt73usb_set_retry_limit(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retry)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u32 reg;
-
- rt73usb_register_read(rt2x00dev, TXRX_CSR4, &reg);
- rt2x00_set_field32(&reg, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry);
- rt2x00_set_field32(&reg, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry);
- rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg);
-
- return 0;
-}
-
static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
const struct ieee80211_tx_queue_params *params)
{
@@ -2251,33 +2139,33 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
field.bit_offset = queue_idx * 16;
field.bit_mask = 0xffff << field.bit_offset;
- rt73usb_register_read(rt2x00dev, AC_TXOP_CSR0, &reg);
+ rt2x00usb_register_read(rt2x00dev, AC_TXOP_CSR0, &reg);
rt2x00_set_field32(&reg, field, queue->txop);
- rt73usb_register_write(rt2x00dev, AC_TXOP_CSR0, reg);
+ rt2x00usb_register_write(rt2x00dev, AC_TXOP_CSR0, reg);
} else if (queue_idx < 4) {
field.bit_offset = (queue_idx - 2) * 16;
field.bit_mask = 0xffff << field.bit_offset;
- rt73usb_register_read(rt2x00dev, AC_TXOP_CSR1, &reg);
+ rt2x00usb_register_read(rt2x00dev, AC_TXOP_CSR1, &reg);
rt2x00_set_field32(&reg, field, queue->txop);
- rt73usb_register_write(rt2x00dev, AC_TXOP_CSR1, reg);
+ rt2x00usb_register_write(rt2x00dev, AC_TXOP_CSR1, reg);
}
/* Update WMM registers */
field.bit_offset = queue_idx * 4;
field.bit_mask = 0xf << field.bit_offset;
- rt73usb_register_read(rt2x00dev, AIFSN_CSR, &reg);
+ rt2x00usb_register_read(rt2x00dev, AIFSN_CSR, &reg);
rt2x00_set_field32(&reg, field, queue->aifs);
- rt73usb_register_write(rt2x00dev, AIFSN_CSR, reg);
+ rt2x00usb_register_write(rt2x00dev, AIFSN_CSR, reg);
- rt73usb_register_read(rt2x00dev, CWMIN_CSR, &reg);
+ rt2x00usb_register_read(rt2x00dev, CWMIN_CSR, &reg);
rt2x00_set_field32(&reg, field, queue->cw_min);
- rt73usb_register_write(rt2x00dev, CWMIN_CSR, reg);
+ rt2x00usb_register_write(rt2x00dev, CWMIN_CSR, reg);
- rt73usb_register_read(rt2x00dev, CWMAX_CSR, &reg);
+ rt2x00usb_register_read(rt2x00dev, CWMAX_CSR, &reg);
rt2x00_set_field32(&reg, field, queue->cw_max);
- rt73usb_register_write(rt2x00dev, CWMAX_CSR, reg);
+ rt2x00usb_register_write(rt2x00dev, CWMAX_CSR, reg);
return 0;
}
@@ -2295,9 +2183,9 @@ static u64 rt73usb_get_tsf(struct ieee80211_hw *hw)
u64 tsf;
u32 reg;
- rt73usb_register_read(rt2x00dev, TXRX_CSR13, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR13, &reg);
tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32;
- rt73usb_register_read(rt2x00dev, TXRX_CSR12, &reg);
+ rt2x00usb_register_read(rt2x00dev, TXRX_CSR12, &reg);
tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER);
return tsf;
@@ -2317,7 +2205,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
.configure_filter = rt2x00mac_configure_filter,
.set_key = rt2x00mac_set_key,
.get_stats = rt2x00mac_get_stats,
- .set_retry_limit = rt73usb_set_retry_limit,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt73usb_conf_tx,
.get_tx_stats = rt2x00mac_get_tx_stats,
@@ -2331,8 +2218,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
.load_firmware = rt73usb_load_firmware,
.initialize = rt2x00usb_initialize,
.uninitialize = rt2x00usb_uninitialize,
- .init_rxentry = rt2x00usb_init_rxentry,
- .init_txentry = rt2x00usb_init_txentry,
+ .clear_entry = rt2x00usb_clear_entry,
.set_device_state = rt73usb_set_device_state,
.link_stats = rt73usb_link_stats,
.reset_tuner = rt73usb_reset_tuner,
@@ -2348,6 +2234,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
.config_filter = rt73usb_config_filter,
.config_intf = rt73usb_config_intf,
.config_erp = rt73usb_config_erp,
+ .config_ant = rt73usb_config_ant,
.config = rt73usb_config,
};
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h
index 868386c457f6..46e1405eb0e2 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/rt2x00/rt73usb.h
@@ -48,7 +48,9 @@
#define CSR_REG_SIZE 0x04b0
#define EEPROM_BASE 0x0000
#define EEPROM_SIZE 0x0100
+#define BBP_BASE 0x0000
#define BBP_SIZE 0x0080
+#define RF_BASE 0x0000
#define RF_SIZE 0x0014
/*
diff --git a/drivers/net/wireless/rtl8180_dev.c b/drivers/net/wireless/rtl8180_dev.c
deleted file mode 100644
index df7e78ee8a88..000000000000
--- a/drivers/net/wireless/rtl8180_dev.c
+++ /dev/null
@@ -1,1081 +0,0 @@
-
-/*
- * Linux device driver for RTL8180 / RTL8185
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * Thanks to Realtek for their support!
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/eeprom_93cx6.h>
-#include <net/mac80211.h>
-
-#include "rtl8180.h"
-#include "rtl8180_rtl8225.h"
-#include "rtl8180_sa2400.h"
-#include "rtl8180_max2820.h"
-#include "rtl8180_grf5101.h"
-
-MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
-MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver");
-MODULE_LICENSE("GPL");
-
-static struct pci_device_id rtl8180_table[] __devinitdata = {
- /* rtl8185 */
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) },
- { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) },
- { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) },
-
- /* rtl8180 */
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) },
- { PCI_DEVICE(0x1799, 0x6001) },
- { PCI_DEVICE(0x1799, 0x6020) },
- { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) },
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, rtl8180_table);
-
-static const struct ieee80211_rate rtl818x_rates[] = {
- { .bitrate = 10, .hw_value = 0, },
- { .bitrate = 20, .hw_value = 1, },
- { .bitrate = 55, .hw_value = 2, },
- { .bitrate = 110, .hw_value = 3, },
- { .bitrate = 60, .hw_value = 4, },
- { .bitrate = 90, .hw_value = 5, },
- { .bitrate = 120, .hw_value = 6, },
- { .bitrate = 180, .hw_value = 7, },
- { .bitrate = 240, .hw_value = 8, },
- { .bitrate = 360, .hw_value = 9, },
- { .bitrate = 480, .hw_value = 10, },
- { .bitrate = 540, .hw_value = 11, },
-};
-
-static const struct ieee80211_channel rtl818x_channels[] = {
- { .center_freq = 2412 },
- { .center_freq = 2417 },
- { .center_freq = 2422 },
- { .center_freq = 2427 },
- { .center_freq = 2432 },
- { .center_freq = 2437 },
- { .center_freq = 2442 },
- { .center_freq = 2447 },
- { .center_freq = 2452 },
- { .center_freq = 2457 },
- { .center_freq = 2462 },
- { .center_freq = 2467 },
- { .center_freq = 2472 },
- { .center_freq = 2484 },
-};
-
-
-
-
-void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
-{
- struct rtl8180_priv *priv = dev->priv;
- int i = 10;
- u32 buf;
-
- buf = (data << 8) | addr;
-
- rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80);
- while (i--) {
- rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf);
- if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF))
- return;
- }
-}
-
-static void rtl8180_handle_rx(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- unsigned int count = 32;
-
- while (count--) {
- struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
- struct sk_buff *skb = priv->rx_buf[priv->rx_idx];
- u32 flags = le32_to_cpu(entry->flags);
-
- if (flags & RTL818X_RX_DESC_FLAG_OWN)
- return;
-
- if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL |
- RTL818X_RX_DESC_FLAG_FOF |
- RTL818X_RX_DESC_FLAG_RX_ERR)))
- goto done;
- else {
- u32 flags2 = le32_to_cpu(entry->flags2);
- struct ieee80211_rx_status rx_status = {0};
- struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE);
-
- if (unlikely(!new_skb))
- goto done;
-
- pci_unmap_single(priv->pdev,
- *((dma_addr_t *)skb->cb),
- MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
- skb_put(skb, flags & 0xFFF);
-
- rx_status.antenna = (flags2 >> 15) & 1;
- /* TODO: improve signal/rssi reporting */
- rx_status.qual = flags2 & 0xFF;
- rx_status.signal = (flags2 >> 8) & 0x7F;
- /* XXX: is this correct? */
- rx_status.rate_idx = (flags >> 20) & 0xF;
- rx_status.freq = dev->conf.channel->center_freq;
- rx_status.band = dev->conf.channel->band;
- rx_status.mactime = le64_to_cpu(entry->tsft);
- rx_status.flag |= RX_FLAG_TSFT;
- if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
- rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
-
- ieee80211_rx_irqsafe(dev, skb, &rx_status);
-
- skb = new_skb;
- priv->rx_buf[priv->rx_idx] = skb;
- *((dma_addr_t *) skb->cb) =
- pci_map_single(priv->pdev, skb_tail_pointer(skb),
- MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
- }
-
- done:
- entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb));
- entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
- MAX_RX_SIZE);
- if (priv->rx_idx == 31)
- entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
- priv->rx_idx = (priv->rx_idx + 1) % 32;
- }
-}
-
-static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio)
-{
- struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_tx_ring *ring = &priv->tx_ring[prio];
-
- while (skb_queue_len(&ring->queue)) {
- struct rtl8180_tx_desc *entry = &ring->desc[ring->idx];
- struct sk_buff *skb;
- struct ieee80211_tx_info *info;
- u32 flags = le32_to_cpu(entry->flags);
-
- if (flags & RTL818X_TX_DESC_FLAG_OWN)
- return;
-
- ring->idx = (ring->idx + 1) % ring->entries;
- skb = __skb_dequeue(&ring->queue);
- pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf),
- skb->len, PCI_DMA_TODEVICE);
-
- info = IEEE80211_SKB_CB(skb);
- memset(&info->status, 0, sizeof(info->status));
-
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
- if (flags & RTL818X_TX_DESC_FLAG_TX_OK)
- info->flags |= IEEE80211_TX_STAT_ACK;
- else
- info->status.excessive_retries = 1;
- }
- info->status.retry_count = flags & 0xFF;
-
- ieee80211_tx_status_irqsafe(dev, skb);
- if (ring->entries - skb_queue_len(&ring->queue) == 2)
- ieee80211_wake_queue(dev, prio);
- }
-}
-
-static irqreturn_t rtl8180_interrupt(int irq, void *dev_id)
-{
- struct ieee80211_hw *dev = dev_id;
- struct rtl8180_priv *priv = dev->priv;
- u16 reg;
-
- spin_lock(&priv->lock);
- reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS);
- if (unlikely(reg == 0xFFFF)) {
- spin_unlock(&priv->lock);
- return IRQ_HANDLED;
- }
-
- rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg);
-
- if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR))
- rtl8180_handle_tx(dev, 3);
-
- if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR))
- rtl8180_handle_tx(dev, 2);
-
- if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR))
- rtl8180_handle_tx(dev, 1);
-
- if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR))
- rtl8180_handle_tx(dev, 0);
-
- if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR))
- rtl8180_handle_rx(dev);
-
- spin_unlock(&priv->lock);
-
- return IRQ_HANDLED;
-}
-
-static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_tx_ring *ring;
- struct rtl8180_tx_desc *entry;
- unsigned long flags;
- unsigned int idx, prio;
- dma_addr_t mapping;
- u32 tx_flags;
- u16 plcp_len = 0;
- __le16 rts_duration = 0;
-
- prio = skb_get_queue_mapping(skb);
- ring = &priv->tx_ring[prio];
-
- mapping = pci_map_single(priv->pdev, skb->data,
- skb->len, PCI_DMA_TODEVICE);
-
- tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
- RTL818X_TX_DESC_FLAG_LS |
- (ieee80211_get_tx_rate(dev, info)->hw_value << 24) |
- skb->len;
-
- if (priv->r8185)
- tx_flags |= RTL818X_TX_DESC_FLAG_DMA |
- RTL818X_TX_DESC_FLAG_NO_ENC;
-
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
- tx_flags |= RTL818X_TX_DESC_FLAG_RTS;
- tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
- } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
- tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
- tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
- }
-
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
- rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len,
- info);
-
- if (!priv->r8185) {
- unsigned int remainder;
-
- plcp_len = DIV_ROUND_UP(16 * (skb->len + 4),
- (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10);
- remainder = (16 * (skb->len + 4)) %
- ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10);
- if (remainder > 0 && remainder <= 6)
- plcp_len |= 1 << 15;
- }
-
- spin_lock_irqsave(&priv->lock, flags);
- idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
- entry = &ring->desc[idx];
-
- entry->rts_duration = rts_duration;
- entry->plcp_len = cpu_to_le16(plcp_len);
- entry->tx_buf = cpu_to_le32(mapping);
- entry->frame_len = cpu_to_le32(skb->len);
- entry->flags2 = info->control.retries[0].rate_idx >= 0 ?
- ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0;
- entry->retry_limit = info->control.retry_limit;
- entry->flags = cpu_to_le32(tx_flags);
- __skb_queue_tail(&ring->queue, skb);
- if (ring->entries - skb_queue_len(&ring->queue) < 2)
- ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
- spin_unlock_irqrestore(&priv->lock, flags);
-
- rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
-
- return 0;
-}
-
-void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam)
-{
- u8 reg;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-}
-
-static int rtl8180_init_hw(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- u16 reg;
-
- rtl818x_iowrite8(priv, &priv->map->CMD, 0);
- rtl818x_ioread8(priv, &priv->map->CMD);
- msleep(10);
-
- /* reset */
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
- rtl818x_ioread8(priv, &priv->map->CMD);
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg &= (1 << 1);
- reg |= RTL818X_CMD_RESET;
- rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET);
- rtl818x_ioread8(priv, &priv->map->CMD);
- msleep(200);
-
- /* check success of reset */
- if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) {
- printk(KERN_ERR "%s: reset timeout!\n", wiphy_name(dev->wiphy));
- return -ETIMEDOUT;
- }
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD);
- rtl818x_ioread8(priv, &priv->map->CMD);
- msleep(200);
-
- if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) {
- /* For cardbus */
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- reg |= 1 << 1;
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
- reg = rtl818x_ioread16(priv, &priv->map->FEMR);
- reg |= (1 << 15) | (1 << 14) | (1 << 4);
- rtl818x_iowrite16(priv, &priv->map->FEMR, reg);
- }
-
- rtl818x_iowrite8(priv, &priv->map->MSR, 0);
-
- if (!priv->r8185)
- rtl8180_set_anaparam(priv, priv->anaparam);
-
- rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
- rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
- rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
- rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
- rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
-
- /* TODO: necessary? specs indicate not */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
- rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3));
- if (priv->r8185) {
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
- rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4));
- }
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */
-
- /* TODO: turn off hw wep on rtl8180 */
-
- rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
-
- if (priv->r8185) {
- rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
- rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
-
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
-
- /* TODO: set ClkRun enable? necessary? */
- reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE);
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6));
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
- } else {
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1);
- rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
-
- rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6);
- rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C);
- }
-
- priv->rf->init(dev);
- if (priv->r8185)
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
- return 0;
-}
-
-static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_rx_desc *entry;
- int i;
-
- priv->rx_ring = pci_alloc_consistent(priv->pdev,
- sizeof(*priv->rx_ring) * 32,
- &priv->rx_ring_dma);
-
- if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) {
- printk(KERN_ERR "%s: Cannot allocate RX ring\n",
- wiphy_name(dev->wiphy));
- return -ENOMEM;
- }
-
- memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32);
- priv->rx_idx = 0;
-
- for (i = 0; i < 32; i++) {
- struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE);
- dma_addr_t *mapping;
- entry = &priv->rx_ring[i];
- if (!skb)
- return 0;
-
- priv->rx_buf[i] = skb;
- mapping = (dma_addr_t *)skb->cb;
- *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb),
- MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
- entry->rx_buf = cpu_to_le32(*mapping);
- entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
- MAX_RX_SIZE);
- }
- entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
- return 0;
-}
-
-static void rtl8180_free_rx_ring(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- int i;
-
- for (i = 0; i < 32; i++) {
- struct sk_buff *skb = priv->rx_buf[i];
- if (!skb)
- continue;
-
- pci_unmap_single(priv->pdev,
- *((dma_addr_t *)skb->cb),
- MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
- kfree_skb(skb);
- }
-
- pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32,
- priv->rx_ring, priv->rx_ring_dma);
- priv->rx_ring = NULL;
-}
-
-static int rtl8180_init_tx_ring(struct ieee80211_hw *dev,
- unsigned int prio, unsigned int entries)
-{
- struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_tx_desc *ring;
- dma_addr_t dma;
- int i;
-
- ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma);
- if (!ring || (unsigned long)ring & 0xFF) {
- printk(KERN_ERR "%s: Cannot allocate TX ring (prio = %d)\n",
- wiphy_name(dev->wiphy), prio);
- return -ENOMEM;
- }
-
- memset(ring, 0, sizeof(*ring)*entries);
- priv->tx_ring[prio].desc = ring;
- priv->tx_ring[prio].dma = dma;
- priv->tx_ring[prio].idx = 0;
- priv->tx_ring[prio].entries = entries;
- skb_queue_head_init(&priv->tx_ring[prio].queue);
-
- for (i = 0; i < entries; i++)
- ring[i].next_tx_desc =
- cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring));
-
- return 0;
-}
-
-static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio)
-{
- struct rtl8180_priv *priv = dev->priv;
- struct rtl8180_tx_ring *ring = &priv->tx_ring[prio];
-
- while (skb_queue_len(&ring->queue)) {
- struct rtl8180_tx_desc *entry = &ring->desc[ring->idx];
- struct sk_buff *skb = __skb_dequeue(&ring->queue);
-
- pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf),
- skb->len, PCI_DMA_TODEVICE);
- kfree_skb(skb);
- ring->idx = (ring->idx + 1) % ring->entries;
- }
-
- pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries,
- ring->desc, ring->dma);
- ring->desc = NULL;
-}
-
-static int rtl8180_start(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- int ret, i;
- u32 reg;
-
- ret = rtl8180_init_rx_ring(dev);
- if (ret)
- return ret;
-
- for (i = 0; i < 4; i++)
- if ((ret = rtl8180_init_tx_ring(dev, i, 16)))
- goto err_free_rings;
-
- ret = rtl8180_init_hw(dev);
- if (ret)
- goto err_free_rings;
-
- rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
- rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
- rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
- rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
- rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
-
- ret = request_irq(priv->pdev->irq, &rtl8180_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, dev);
- if (ret) {
- printk(KERN_ERR "%s: failed to register IRQ handler\n",
- wiphy_name(dev->wiphy));
- goto err_free_rings;
- }
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
-
- rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
- rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
-
- reg = RTL818X_RX_CONF_ONLYERLPKT |
- RTL818X_RX_CONF_RX_AUTORESETPHY |
- RTL818X_RX_CONF_MGMT |
- RTL818X_RX_CONF_DATA |
- (7 << 8 /* MAX RX DMA */) |
- RTL818X_RX_CONF_BROADCAST |
- RTL818X_RX_CONF_NICMAC;
-
- if (priv->r8185)
- reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2;
- else {
- reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1)
- ? RTL818X_RX_CONF_CSDM1 : 0;
- reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2)
- ? RTL818X_RX_CONF_CSDM2 : 0;
- }
-
- priv->rx_conf = reg;
- rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
-
- if (priv->r8185) {
- reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
- rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
-
- reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
- reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
- rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
-
- /* disable early TX */
- rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f);
- }
-
- reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
- reg |= (6 << 21 /* MAX TX DMA */) |
- RTL818X_TX_CONF_NO_ICV;
-
- if (priv->r8185)
- reg &= ~RTL818X_TX_CONF_PROBE_DTS;
- else
- reg &= ~RTL818X_TX_CONF_HW_SEQNUM;
-
- /* different meaning, same value on both rtl8185 and rtl8180 */
- reg &= ~RTL818X_TX_CONF_SAT_HWPLCP;
-
- rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg |= RTL818X_CMD_RX_ENABLE;
- reg |= RTL818X_CMD_TX_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
-
- priv->mode = NL80211_IFTYPE_MONITOR;
- return 0;
-
- err_free_rings:
- rtl8180_free_rx_ring(dev);
- for (i = 0; i < 4; i++)
- if (priv->tx_ring[i].desc)
- rtl8180_free_tx_ring(dev, i);
-
- return ret;
-}
-
-static void rtl8180_stop(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- u8 reg;
- int i;
-
- priv->mode = NL80211_IFTYPE_UNSPECIFIED;
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg &= ~RTL818X_CMD_TX_ENABLE;
- reg &= ~RTL818X_CMD_RX_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
-
- priv->rf->stop(dev);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG4);
- rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- free_irq(priv->pdev->irq, dev);
-
- rtl8180_free_rx_ring(dev);
- for (i = 0; i < 4; i++)
- rtl8180_free_tx_ring(dev, i);
-}
-
-static int rtl8180_add_interface(struct ieee80211_hw *dev,
- struct ieee80211_if_init_conf *conf)
-{
- struct rtl8180_priv *priv = dev->priv;
-
- if (priv->mode != NL80211_IFTYPE_MONITOR)
- return -EOPNOTSUPP;
-
- switch (conf->type) {
- case NL80211_IFTYPE_STATION:
- priv->mode = conf->type;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- priv->vif = conf->vif;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0],
- le32_to_cpu(*(__le32 *)conf->mac_addr));
- rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4],
- le16_to_cpu(*(__le16 *)(conf->mac_addr + 4)));
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- return 0;
-}
-
-static void rtl8180_remove_interface(struct ieee80211_hw *dev,
- struct ieee80211_if_init_conf *conf)
-{
- struct rtl8180_priv *priv = dev->priv;
- priv->mode = NL80211_IFTYPE_MONITOR;
- priv->vif = NULL;
-}
-
-static int rtl8180_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
-{
- struct rtl8180_priv *priv = dev->priv;
-
- priv->rf->set_chan(dev, conf);
-
- return 0;
-}
-
-static int rtl8180_config_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_if_conf *conf)
-{
- struct rtl8180_priv *priv = dev->priv;
- int i;
-
- for (i = 0; i < ETH_ALEN; i++)
- rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);
-
- if (is_valid_ether_addr(conf->bssid))
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA);
- else
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK);
-
- return 0;
-}
-
-static void rtl8180_configure_filter(struct ieee80211_hw *dev,
- unsigned int changed_flags,
- unsigned int *total_flags,
- int mc_count, struct dev_addr_list *mclist)
-{
- struct rtl8180_priv *priv = dev->priv;
-
- if (changed_flags & FIF_FCSFAIL)
- priv->rx_conf ^= RTL818X_RX_CONF_FCS;
- if (changed_flags & FIF_CONTROL)
- priv->rx_conf ^= RTL818X_RX_CONF_CTRL;
- if (changed_flags & FIF_OTHER_BSS)
- priv->rx_conf ^= RTL818X_RX_CONF_MONITOR;
- if (*total_flags & FIF_ALLMULTI || mc_count > 0)
- priv->rx_conf |= RTL818X_RX_CONF_MULTICAST;
- else
- priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST;
-
- *total_flags = 0;
-
- if (priv->rx_conf & RTL818X_RX_CONF_FCS)
- *total_flags |= FIF_FCSFAIL;
- if (priv->rx_conf & RTL818X_RX_CONF_CTRL)
- *total_flags |= FIF_CONTROL;
- if (priv->rx_conf & RTL818X_RX_CONF_MONITOR)
- *total_flags |= FIF_OTHER_BSS;
- if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST)
- *total_flags |= FIF_ALLMULTI;
-
- rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf);
-}
-
-static const struct ieee80211_ops rtl8180_ops = {
- .tx = rtl8180_tx,
- .start = rtl8180_start,
- .stop = rtl8180_stop,
- .add_interface = rtl8180_add_interface,
- .remove_interface = rtl8180_remove_interface,
- .config = rtl8180_config,
- .config_interface = rtl8180_config_interface,
- .configure_filter = rtl8180_configure_filter,
-};
-
-static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom)
-{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8180_priv *priv = dev->priv;
- u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
-
- eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE;
- eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ;
- eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK;
- eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS;
-}
-
-static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom)
-{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8180_priv *priv = dev->priv;
- u8 reg = 2 << 6;
-
- if (eeprom->reg_data_in)
- reg |= RTL818X_EEPROM_CMD_WRITE;
- if (eeprom->reg_data_out)
- reg |= RTL818X_EEPROM_CMD_READ;
- if (eeprom->reg_data_clock)
- reg |= RTL818X_EEPROM_CMD_CK;
- if (eeprom->reg_chip_select)
- reg |= RTL818X_EEPROM_CMD_CS;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
-}
-
-static int __devinit rtl8180_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct ieee80211_hw *dev;
- struct rtl8180_priv *priv;
- unsigned long mem_addr, mem_len;
- unsigned int io_addr, io_len;
- int err, i;
- struct eeprom_93cx6 eeprom;
- const char *chip_name, *rf_name = NULL;
- u32 reg;
- u16 eeprom_val;
- DECLARE_MAC_BUF(mac);
-
- err = pci_enable_device(pdev);
- if (err) {
- printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n",
- pci_name(pdev));
- return err;
- }
-
- err = pci_request_regions(pdev, KBUILD_MODNAME);
- if (err) {
- printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n",
- pci_name(pdev));
- return err;
- }
-
- io_addr = pci_resource_start(pdev, 0);
- io_len = pci_resource_len(pdev, 0);
- mem_addr = pci_resource_start(pdev, 1);
- mem_len = pci_resource_len(pdev, 1);
-
- if (mem_len < sizeof(struct rtl818x_csr) ||
- io_len < sizeof(struct rtl818x_csr)) {
- printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n",
- pci_name(pdev));
- err = -ENOMEM;
- goto err_free_reg;
- }
-
- if ((err = pci_set_dma_mask(pdev, 0xFFFFFF00ULL)) ||
- (err = pci_set_consistent_dma_mask(pdev, 0xFFFFFF00ULL))) {
- printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n",
- pci_name(pdev));
- goto err_free_reg;
- }
-
- pci_set_master(pdev);
-
- dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops);
- if (!dev) {
- printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n",
- pci_name(pdev));
- err = -ENOMEM;
- goto err_free_reg;
- }
-
- priv = dev->priv;
- priv->pdev = pdev;
-
- dev->max_altrates = 1;
- SET_IEEE80211_DEV(dev, &pdev->dev);
- pci_set_drvdata(pdev, dev);
-
- priv->map = pci_iomap(pdev, 1, mem_len);
- if (!priv->map)
- priv->map = pci_iomap(pdev, 0, io_len);
-
- if (!priv->map) {
- printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n",
- pci_name(pdev));
- goto err_free_dev;
- }
-
- BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels));
- BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates));
-
- memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels));
- memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates));
-
- priv->band.band = IEEE80211_BAND_2GHZ;
- priv->band.channels = priv->channels;
- priv->band.n_channels = ARRAY_SIZE(rtl818x_channels);
- priv->band.bitrates = priv->rates;
- priv->band.n_bitrates = 4;
- dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
-
- dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
- IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_SIGNAL_UNSPEC;
- dev->queues = 1;
- dev->max_signal = 65;
-
- reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
- reg &= RTL818X_TX_CONF_HWVER_MASK;
- switch (reg) {
- case RTL818X_TX_CONF_R8180_ABCD:
- chip_name = "RTL8180";
- break;
- case RTL818X_TX_CONF_R8180_F:
- chip_name = "RTL8180vF";
- break;
- case RTL818X_TX_CONF_R8185_ABC:
- chip_name = "RTL8185";
- break;
- case RTL818X_TX_CONF_R8185_D:
- chip_name = "RTL8185vD";
- break;
- default:
- printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n",
- pci_name(pdev), reg >> 25);
- goto err_iounmap;
- }
-
- priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC;
- if (priv->r8185) {
- priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates);
- pci_try_set_mwi(pdev);
- }
-
- eeprom.data = dev;
- eeprom.register_read = rtl8180_eeprom_register_read;
- eeprom.register_write = rtl8180_eeprom_register_write;
- if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
- eeprom.width = PCI_EEPROM_WIDTH_93C66;
- else
- eeprom.width = PCI_EEPROM_WIDTH_93C46;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
-
- eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val);
- eeprom_val &= 0xFF;
- switch (eeprom_val) {
- case 1: rf_name = "Intersil";
- break;
- case 2: rf_name = "RFMD";
- break;
- case 3: priv->rf = &sa2400_rf_ops;
- break;
- case 4: priv->rf = &max2820_rf_ops;
- break;
- case 5: priv->rf = &grf5101_rf_ops;
- break;
- case 9: priv->rf = rtl8180_detect_rf(dev);
- break;
- case 10:
- rf_name = "RTL8255";
- break;
- default:
- printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n",
- pci_name(pdev), eeprom_val);
- goto err_iounmap;
- }
-
- if (!priv->rf) {
- printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n",
- pci_name(pdev), rf_name);
- goto err_iounmap;
- }
-
- eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val);
- priv->csthreshold = eeprom_val >> 8;
- if (!priv->r8185) {
- __le32 anaparam;
- eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2);
- priv->anaparam = le32_to_cpu(anaparam);
- eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam);
- }
-
- eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)dev->wiphy->perm_addr, 3);
- if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
- printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using"
- " randomly generated MAC addr\n", pci_name(pdev));
- random_ether_addr(dev->wiphy->perm_addr);
- }
-
- /* CCK TX power */
- for (i = 0; i < 14; i += 2) {
- u16 txpwr;
- eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr);
- priv->channels[i].hw_value = txpwr & 0xFF;
- priv->channels[i + 1].hw_value = txpwr >> 8;
- }
-
- /* OFDM TX power */
- if (priv->r8185) {
- for (i = 0; i < 14; i += 2) {
- u16 txpwr;
- eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr);
- priv->channels[i].hw_value |= (txpwr & 0xFF) << 8;
- priv->channels[i + 1].hw_value |= txpwr & 0xFF00;
- }
- }
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- spin_lock_init(&priv->lock);
-
- err = ieee80211_register_hw(dev);
- if (err) {
- printk(KERN_ERR "%s (rtl8180): Cannot register device\n",
- pci_name(pdev));
- goto err_iounmap;
- }
-
- printk(KERN_INFO "%s: hwaddr %s, %s + %s\n",
- wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr),
- chip_name, priv->rf->name);
-
- return 0;
-
- err_iounmap:
- iounmap(priv->map);
-
- err_free_dev:
- pci_set_drvdata(pdev, NULL);
- ieee80211_free_hw(dev);
-
- err_free_reg:
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- return err;
-}
-
-static void __devexit rtl8180_remove(struct pci_dev *pdev)
-{
- struct ieee80211_hw *dev = pci_get_drvdata(pdev);
- struct rtl8180_priv *priv;
-
- if (!dev)
- return;
-
- ieee80211_unregister_hw(dev);
-
- priv = dev->priv;
-
- pci_iounmap(pdev, priv->map);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- ieee80211_free_hw(dev);
-}
-
-#ifdef CONFIG_PM
-static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- pci_save_state(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
- return 0;
-}
-
-static int rtl8180_resume(struct pci_dev *pdev)
-{
- pci_set_power_state(pdev, PCI_D0);
- pci_restore_state(pdev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-static struct pci_driver rtl8180_driver = {
- .name = KBUILD_MODNAME,
- .id_table = rtl8180_table,
- .probe = rtl8180_probe,
- .remove = __devexit_p(rtl8180_remove),
-#ifdef CONFIG_PM
- .suspend = rtl8180_suspend,
- .resume = rtl8180_resume,
-#endif /* CONFIG_PM */
-};
-
-static int __init rtl8180_init(void)
-{
- return pci_register_driver(&rtl8180_driver);
-}
-
-static void __exit rtl8180_exit(void)
-{
- pci_unregister_driver(&rtl8180_driver);
-}
-
-module_init(rtl8180_init);
-module_exit(rtl8180_exit);
diff --git a/drivers/net/wireless/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c
deleted file mode 100644
index cd22781728a9..000000000000
--- a/drivers/net/wireless/rtl8180_rtl8225.c
+++ /dev/null
@@ -1,780 +0,0 @@
-
-/*
- * Radio tuning for RTL8225 on RTL8180
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8180 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * Thanks to Realtek for their support!
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <net/mac80211.h>
-
-#include "rtl8180.h"
-#include "rtl8180_rtl8225.h"
-
-static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
-{
- struct rtl8180_priv *priv = dev->priv;
- u16 reg80, reg84, reg82;
- u32 bangdata;
- int i;
-
- bangdata = (data << 4) | (addr & 0xf);
-
- reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
- reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
-
- reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
-
- for (i = 15; i >= 0; i--) {
- u16 reg = reg80 | !!(bangdata & (1 << i));
-
- if (i & 1)
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
-
- if (!(i & 1))
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-}
-
-static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
-{
- struct rtl8180_priv *priv = dev->priv;
- u16 reg80, reg82, reg84, out;
- int i;
-
- reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
- reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
- reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400;
-
- reg80 &= ~0xF;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(4);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(5);
-
- for (i = 4; i >= 0; i--) {
- u16 reg = reg80 | ((addr >> i) & 1);
-
- if (!(i & 1)) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(1);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
-
- if (i & 1) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(1);
- }
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
-
- out = 0;
- for (i = 11; i >= 0; i--) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(1);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
-
- if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
- out |= 1 << i;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 2));
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- udelay(2);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
-
- return out;
-}
-
-static const u16 rtl8225bcd_rxgain[] = {
- 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
- 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
- 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
- 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
- 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
- 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
- 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
- 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
- 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
- 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
- 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
- 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
-};
-
-static const u8 rtl8225_agc[] = {
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
- 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
- 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
- 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
- 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
- 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
- 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
- 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
- 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
- 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
- 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
- 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
-};
-
-static const u8 rtl8225_gain[] = {
- 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
- 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
- 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
- 0x33, 0x80, 0x79, 0xc5, /* -78dbm */
- 0x43, 0x78, 0x76, 0xc5, /* -74dbm */
- 0x53, 0x60, 0x73, 0xc5, /* -70dbm */
- 0x63, 0x58, 0x70, 0xc5, /* -66dbm */
-};
-
-static const u8 rtl8225_threshold[] = {
- 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
-};
-
-static const u8 rtl8225_tx_gain_cck_ofdm[] = {
- 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
-};
-
-static const u8 rtl8225_tx_power_cck[] = {
- 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
- 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
- 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
- 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
- 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
- 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
-};
-
-static const u8 rtl8225_tx_power_cck_ch14[] = {
- 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
- 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
- 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
- 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
- 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
-};
-
-static const u8 rtl8225_tx_power_ofdm[] = {
- 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
-};
-
-static const u32 rtl8225_chan[] = {
- 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
- 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
-};
-
-static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
-{
- struct rtl8180_priv *priv = dev->priv;
- u8 cck_power, ofdm_power;
- const u8 *tmp;
- u32 reg;
- int i;
-
- cck_power = priv->channels[channel - 1].hw_value & 0xFF;
- ofdm_power = priv->channels[channel - 1].hw_value >> 8;
-
- cck_power = min(cck_power, (u8)35);
- ofdm_power = min(ofdm_power, (u8)35);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
- rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
-
- if (channel == 14)
- tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
- else
- tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
-
- for (i = 0; i < 8; i++)
- rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
-
- msleep(1); /* FIXME: optional? */
-
- /* anaparam2 on */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
- rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1);
-
- tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
-
- rtl8225_write_phy_ofdm(dev, 5, *tmp);
- rtl8225_write_phy_ofdm(dev, 7, *tmp);
-
- msleep(1);
-}
-
-static void rtl8225_rf_init(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- int i;
-
- rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
-
- /* host_pci_init */
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- msleep(200); /* FIXME: ehh?? */
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
-
- rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
-
- /* TODO: check if we need really to change BRSR to do RF config */
- rtl818x_ioread16(priv, &priv->map->BRSR);
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
- rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl8225_write(dev, 0x0, 0x067);
- rtl8225_write(dev, 0x1, 0xFE0);
- rtl8225_write(dev, 0x2, 0x44D);
- rtl8225_write(dev, 0x3, 0x441);
- rtl8225_write(dev, 0x4, 0x8BE);
- rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */
- rtl8225_write(dev, 0x6, 0xAE6);
- rtl8225_write(dev, 0x7, rtl8225_chan[0]);
- rtl8225_write(dev, 0x8, 0x01F);
- rtl8225_write(dev, 0x9, 0x334);
- rtl8225_write(dev, 0xA, 0xFD4);
- rtl8225_write(dev, 0xB, 0x391);
- rtl8225_write(dev, 0xC, 0x050);
- rtl8225_write(dev, 0xD, 0x6DB);
- rtl8225_write(dev, 0xE, 0x029);
- rtl8225_write(dev, 0xF, 0x914); msleep(1);
-
- rtl8225_write(dev, 0x2, 0xC4D); msleep(100);
-
- rtl8225_write(dev, 0x0, 0x127);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
- rtl8225_write(dev, 0x1, i + 1);
- rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
- }
-
- rtl8225_write(dev, 0x0, 0x027);
- rtl8225_write(dev, 0x0, 0x22F);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
- rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
- msleep(1);
- rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
- msleep(1);
- }
-
- msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
-
- rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
- rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
- rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
- rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
- rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
- rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
- rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
- rtl8225_write_phy_cck(dev, 0x13, 0xd0);
- rtl8225_write_phy_cck(dev, 0x19, 0x00);
- rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
- rtl8225_write_phy_cck(dev, 0x1b, 0x08);
- rtl8225_write_phy_cck(dev, 0x40, 0x86);
- rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1);
- rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
- rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1);
- rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1);
- rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1);
- rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1);
-
- rtl8225_rf_set_tx_power(dev, 1);
-
- /* RX antenna default to A */
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
-
- rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
- msleep(1);
- rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-
- rtl8225_write(dev, 0x0c, 0x50);
- /* set OFDM initial gain */
- rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]);
- rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]);
- rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]);
- rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]);
- /* set CCK threshold */
- rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]);
-}
-
-static const u8 rtl8225z2_tx_power_cck_ch14[] = {
- 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
-};
-
-static const u8 rtl8225z2_tx_power_cck_B[] = {
- 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04
-};
-
-static const u8 rtl8225z2_tx_power_cck_A[] = {
- 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04
-};
-
-static const u8 rtl8225z2_tx_power_cck[] = {
- 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
-};
-
-static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
-{
- struct rtl8180_priv *priv = dev->priv;
- u8 cck_power, ofdm_power;
- const u8 *tmp;
- int i;
-
- cck_power = priv->channels[channel - 1].hw_value & 0xFF;
- ofdm_power = priv->channels[channel - 1].hw_value >> 8;
-
- if (channel == 14)
- tmp = rtl8225z2_tx_power_cck_ch14;
- else if (cck_power == 12)
- tmp = rtl8225z2_tx_power_cck_B;
- else if (cck_power == 13)
- tmp = rtl8225z2_tx_power_cck_A;
- else
- tmp = rtl8225z2_tx_power_cck;
-
- for (i = 0; i < 8; i++)
- rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
-
- cck_power = min(cck_power, (u8)35);
- if (cck_power == 13 || cck_power == 14)
- cck_power = 12;
- if (cck_power >= 15)
- cck_power -= 2;
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power);
- rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK);
- msleep(1);
-
- ofdm_power = min(ofdm_power, (u8)35);
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power);
-
- rtl8225_write_phy_ofdm(dev, 2, 0x62);
- rtl8225_write_phy_ofdm(dev, 5, 0x00);
- rtl8225_write_phy_ofdm(dev, 6, 0x40);
- rtl8225_write_phy_ofdm(dev, 7, 0x00);
- rtl8225_write_phy_ofdm(dev, 8, 0x40);
-
- msleep(1);
-}
-
-static const u16 rtl8225z2_rxgain[] = {
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
- 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
- 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
- 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
- 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
- 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
- 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
- 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
- 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
- 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
- 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
- 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
-};
-
-static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- int i;
-
- rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
-
- /* host_pci_init */
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- msleep(200); /* FIXME: ehh?? */
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
-
- rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008);
-
- /* TODO: check if we need really to change BRSR to do RF config */
- rtl818x_ioread16(priv, &priv->map->BRSR);
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
- rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-
- rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
- rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
- rtl8225_write(dev, 0x2, 0x44D); msleep(1);
- rtl8225_write(dev, 0x3, 0x441); msleep(1);
- rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
- rtl8225_write(dev, 0x5, 0xC72); msleep(1);
- rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
- rtl8225_write(dev, 0x7, 0x82A); msleep(1);
- rtl8225_write(dev, 0x8, 0x03F); msleep(1);
- rtl8225_write(dev, 0x9, 0x335); msleep(1);
- rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
- rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
- rtl8225_write(dev, 0xc, 0x850); msleep(1);
- rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
- rtl8225_write(dev, 0xe, 0x02B); msleep(1);
- rtl8225_write(dev, 0xf, 0x114); msleep(100);
-
- if (!(rtl8225_read(dev, 6) & (1 << 7))) {
- rtl8225_write(dev, 0x02, 0x0C4D);
- msleep(200);
- rtl8225_write(dev, 0x02, 0x044D);
- msleep(100);
- /* TODO: readd calibration failure message when the calibration
- check works */
- }
-
- rtl8225_write(dev, 0x0, 0x1B7);
- rtl8225_write(dev, 0x3, 0x002);
- rtl8225_write(dev, 0x5, 0x004);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
- rtl8225_write(dev, 0x1, i + 1);
- rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
- }
-
- rtl8225_write(dev, 0x0, 0x0B7); msleep(100);
- rtl8225_write(dev, 0x2, 0xC4D);
-
- msleep(200);
- rtl8225_write(dev, 0x2, 0x44D);
- msleep(100);
-
- rtl8225_write(dev, 0x00, 0x2BF);
- rtl8225_write(dev, 0xFF, 0xFFFF);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
- rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
- msleep(1);
- rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
- msleep(1);
- }
-
- msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
- rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */
- rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
-
- rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
- rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
- rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
- rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
- rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
- rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
- rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
- rtl8225_write_phy_cck(dev, 0x13, 0xd0);
- rtl8225_write_phy_cck(dev, 0x19, 0x00);
- rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
- rtl8225_write_phy_cck(dev, 0x1b, 0x08);
- rtl8225_write_phy_cck(dev, 0x40, 0x86);
- rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1);
- rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
- rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1);
- rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1);
- rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1);
- rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1);
- rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
-
- rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1);
-
- rtl8225z2_rf_set_tx_power(dev, 1);
-
- /* RX antenna default to A */
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
-
- rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
- msleep(1);
- rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
-}
-
-static void rtl8225_rf_stop(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- u8 reg;
-
- rtl8225_write(dev, 0x4, 0x1f); msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-}
-
-static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
- struct ieee80211_conf *conf)
-{
- struct rtl8180_priv *priv = dev->priv;
- int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
- if (priv->rf->init == rtl8225_rf_init)
- rtl8225_rf_set_tx_power(dev, chan);
- else
- rtl8225z2_rf_set_tx_power(dev, chan);
-
- rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
- msleep(10);
-
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
- } else {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
- }
-}
-
-static const struct rtl818x_rf_ops rtl8225_ops = {
- .name = "rtl8225",
- .init = rtl8225_rf_init,
- .stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
-};
-
-static const struct rtl818x_rf_ops rtl8225z2_ops = {
- .name = "rtl8225z2",
- .init = rtl8225z2_rf_init,
- .stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
-};
-
-const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev)
-{
- struct rtl8180_priv *priv = dev->priv;
- u16 reg8, reg9;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
- rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
- msleep(100);
-
- rtl8225_write(dev, 0, 0x1B7);
-
- reg8 = rtl8225_read(dev, 8);
- reg9 = rtl8225_read(dev, 9);
-
- rtl8225_write(dev, 0, 0x0B7);
-
- if (reg8 != 0x588 || reg9 != 0x700)
- return &rtl8225_ops;
-
- return &rtl8225z2_ops;
-}
diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h
deleted file mode 100644
index e82bb4d289e8..000000000000
--- a/drivers/net/wireless/rtl8187.h
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Definitions for RTL8187 hardware
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * This program is free software; you can 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 RTL8187_H
-#define RTL8187_H
-
-#include "rtl818x.h"
-
-#define RTL8187_EEPROM_TXPWR_BASE 0x05
-#define RTL8187_EEPROM_MAC_ADDR 0x07
-#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */
-#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */
-#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */
-
-#define RTL8187_REQT_READ 0xC0
-#define RTL8187_REQT_WRITE 0x40
-#define RTL8187_REQ_GET_REG 0x05
-#define RTL8187_REQ_SET_REG 0x05
-
-#define RTL8187_MAX_RX 0x9C4
-
-struct rtl8187_rx_info {
- struct urb *urb;
- struct ieee80211_hw *dev;
-};
-
-struct rtl8187_rx_hdr {
- __le32 flags;
- u8 noise;
- u8 signal;
- u8 agc;
- u8 reserved;
- __le64 mac_time;
-} __attribute__((packed));
-
-struct rtl8187b_rx_hdr {
- __le32 flags;
- __le64 mac_time;
- u8 sq;
- u8 rssi;
- u8 agc;
- u8 flags2;
- __le16 snr_long2end;
- s8 pwdb_g12;
- u8 fot;
-} __attribute__((packed));
-
-/* {rtl8187,rtl8187b}_tx_info is in skb */
-
-struct rtl8187_tx_hdr {
- __le32 flags;
- __le16 rts_duration;
- __le16 len;
- __le32 retry;
-} __attribute__((packed));
-
-struct rtl8187b_tx_hdr {
- __le32 flags;
- __le16 rts_duration;
- __le16 len;
- __le32 unused_1;
- __le16 unused_2;
- __le16 tx_duration;
- __le32 unused_3;
- __le32 retry;
- __le32 unused_4[2];
-} __attribute__((packed));
-
-enum {
- DEVICE_RTL8187,
- DEVICE_RTL8187B
-};
-
-struct rtl8187_priv {
- /* common between rtl818x drivers */
- struct rtl818x_csr *map;
- const struct rtl818x_rf_ops *rf;
- struct ieee80211_vif *vif;
- int mode;
- /* The mutex protects the TX loopback state.
- * Any attempt to set channels concurrently locks the device.
- */
- struct mutex conf_mutex;
-
- /* rtl8187 specific */
- struct ieee80211_channel channels[14];
- struct ieee80211_rate rates[12];
- struct ieee80211_supported_band band;
- struct usb_device *udev;
- u32 rx_conf;
- u16 txpwr_base;
- u16 seqno;
- u8 asic_rev;
- u8 is_rtl8187b;
- enum {
- RTL8187BvB,
- RTL8187BvD,
- RTL8187BvE
- } hw_rev;
- struct sk_buff_head rx_queue;
- u8 signal;
- u8 quality;
- u8 noise;
-};
-
-void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);
-
-static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv,
- u8 *addr, u8 idx)
-{
- u8 val;
-
- usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
- RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
-
- return val;
-}
-
-static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
-{
- return rtl818x_ioread8_idx(priv, addr, 0);
-}
-
-static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv,
- __le16 *addr, u8 idx)
-{
- __le16 val;
-
- usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
- RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
-
- return le16_to_cpu(val);
-}
-
-static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
-{
- return rtl818x_ioread16_idx(priv, addr, 0);
-}
-
-static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv,
- __le32 *addr, u8 idx)
-{
- __le32 val;
-
- usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
- RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
-
- return le32_to_cpu(val);
-}
-
-static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr)
-{
- return rtl818x_ioread32_idx(priv, addr, 0);
-}
-
-static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv,
- u8 *addr, u8 val, u8 idx)
-{
- usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
- RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &val,
- sizeof(val), HZ / 2);
-}
-
-static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val)
-{
- rtl818x_iowrite8_idx(priv, addr, val, 0);
-}
-
-static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv,
- __le16 *addr, u16 val, u8 idx)
-{
- __le16 buf = cpu_to_le16(val);
-
- usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
- RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
- HZ / 2);
-}
-
-static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr,
- u16 val)
-{
- rtl818x_iowrite16_idx(priv, addr, val, 0);
-}
-
-static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv,
- __le32 *addr, u32 val, u8 idx)
-{
- __le32 buf = cpu_to_le32(val);
-
- usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
- RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
- HZ / 2);
-}
-
-static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr,
- u32 val)
-{
- rtl818x_iowrite32_idx(priv, addr, val, 0);
-}
-
-#endif /* RTL8187_H */
diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c
deleted file mode 100644
index 69eb0132593b..000000000000
--- a/drivers/net/wireless/rtl8187_dev.c
+++ /dev/null
@@ -1,1261 +0,0 @@
-/*
- * Linux device driver for RTL8187
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * Magic delays and register offsets below are taken from the original
- * r8187 driver sources. Thanks to Realtek for their support!
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/eeprom_93cx6.h>
-#include <net/mac80211.h>
-
-#include "rtl8187.h"
-#include "rtl8187_rtl8225.h"
-
-MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
-MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
-MODULE_LICENSE("GPL");
-
-static struct usb_device_id rtl8187_table[] __devinitdata = {
- /* Asus */
- {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187},
- /* Belkin */
- {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B},
- /* Realtek */
- {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},
- {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B},
- {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B},
- {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B},
- /* Netgear */
- {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187},
- {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187},
- {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B},
- /* HP */
- {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187},
- /* Sitecom */
- {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
- {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B},
- /* Abocom */
- {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, rtl8187_table);
-
-static const struct ieee80211_rate rtl818x_rates[] = {
- { .bitrate = 10, .hw_value = 0, },
- { .bitrate = 20, .hw_value = 1, },
- { .bitrate = 55, .hw_value = 2, },
- { .bitrate = 110, .hw_value = 3, },
- { .bitrate = 60, .hw_value = 4, },
- { .bitrate = 90, .hw_value = 5, },
- { .bitrate = 120, .hw_value = 6, },
- { .bitrate = 180, .hw_value = 7, },
- { .bitrate = 240, .hw_value = 8, },
- { .bitrate = 360, .hw_value = 9, },
- { .bitrate = 480, .hw_value = 10, },
- { .bitrate = 540, .hw_value = 11, },
-};
-
-static const struct ieee80211_channel rtl818x_channels[] = {
- { .center_freq = 2412 },
- { .center_freq = 2417 },
- { .center_freq = 2422 },
- { .center_freq = 2427 },
- { .center_freq = 2432 },
- { .center_freq = 2437 },
- { .center_freq = 2442 },
- { .center_freq = 2447 },
- { .center_freq = 2452 },
- { .center_freq = 2457 },
- { .center_freq = 2462 },
- { .center_freq = 2467 },
- { .center_freq = 2472 },
- { .center_freq = 2484 },
-};
-
-static void rtl8187_iowrite_async_cb(struct urb *urb)
-{
- kfree(urb->context);
- usb_free_urb(urb);
-}
-
-static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
- void *data, u16 len)
-{
- struct usb_ctrlrequest *dr;
- struct urb *urb;
- struct rtl8187_async_write_data {
- u8 data[4];
- struct usb_ctrlrequest dr;
- } *buf;
- int rc;
-
- buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
- if (!buf)
- return;
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- kfree(buf);
- return;
- }
-
- dr = &buf->dr;
-
- dr->bRequestType = RTL8187_REQT_WRITE;
- dr->bRequest = RTL8187_REQ_SET_REG;
- dr->wValue = addr;
- dr->wIndex = 0;
- dr->wLength = cpu_to_le16(len);
-
- memcpy(buf, data, len);
-
- usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0),
- (unsigned char *)dr, buf, len,
- rtl8187_iowrite_async_cb, buf);
- rc = usb_submit_urb(urb, GFP_ATOMIC);
- if (rc < 0) {
- kfree(buf);
- usb_free_urb(urb);
- }
-}
-
-static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv,
- __le32 *addr, u32 val)
-{
- __le32 buf = cpu_to_le32(val);
-
- rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr),
- &buf, sizeof(buf));
-}
-
-void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
-{
- struct rtl8187_priv *priv = dev->priv;
-
- data <<= 8;
- data |= addr | 0x80;
-
- rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF);
- rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF);
- rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF);
- rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF);
-
- msleep(1);
-}
-
-static void rtl8187_tx_cb(struct urb *urb)
-{
- struct sk_buff *skb = (struct sk_buff *)urb->context;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hw *hw = info->driver_data[0];
- struct rtl8187_priv *priv = hw->priv;
-
- usb_free_urb(info->driver_data[1]);
- skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
- sizeof(struct rtl8187_tx_hdr));
- memset(&info->status, 0, sizeof(info->status));
- info->flags |= IEEE80211_TX_STAT_ACK;
- ieee80211_tx_status_irqsafe(hw, skb);
-}
-
-static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct rtl8187_priv *priv = dev->priv;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
- unsigned int ep;
- void *buf;
- struct urb *urb;
- __le16 rts_dur = 0;
- u32 flags;
- int rc;
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- kfree_skb(skb);
- return 0;
- }
-
- flags = skb->len;
- flags |= RTL818X_TX_DESC_FLAG_NO_ENC;
-
- flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
- if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control))
- flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) {
- flags |= RTL818X_TX_DESC_FLAG_RTS;
- flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
- rts_dur = ieee80211_rts_duration(dev, priv->vif,
- skb->len, info);
- } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
- flags |= RTL818X_TX_DESC_FLAG_CTS;
- flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
- }
-
- if (!priv->is_rtl8187b) {
- struct rtl8187_tx_hdr *hdr =
- (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
- hdr->flags = cpu_to_le32(flags);
- hdr->len = 0;
- hdr->rts_duration = rts_dur;
- hdr->retry = cpu_to_le32(info->control.retry_limit << 8);
- buf = hdr;
-
- ep = 2;
- } else {
- /* fc needs to be calculated before skb_push() */
- unsigned int epmap[4] = { 6, 7, 5, 4 };
- struct ieee80211_hdr *tx_hdr =
- (struct ieee80211_hdr *)(skb->data);
- u16 fc = le16_to_cpu(tx_hdr->frame_control);
-
- struct rtl8187b_tx_hdr *hdr =
- (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr));
- struct ieee80211_rate *txrate =
- ieee80211_get_tx_rate(dev, info);
- memset(hdr, 0, sizeof(*hdr));
- hdr->flags = cpu_to_le32(flags);
- hdr->rts_duration = rts_dur;
- hdr->retry = cpu_to_le32(info->control.retry_limit << 8);
- hdr->tx_duration =
- ieee80211_generic_frame_duration(dev, priv->vif,
- skb->len, txrate);
- buf = hdr;
-
- if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
- ep = 12;
- else
- ep = epmap[skb_get_queue_mapping(skb)];
- }
-
- /* FIXME: The sequence that follows is needed for this driver to
- * work with mac80211 since "mac80211: fix TX sequence numbers".
- * As with the temporary code in rt2x00, changes will be needed
- * to get proper sequence numbers on beacons. In addition, this
- * patch places the sequence number in the hardware state, which
- * limits us to a single virtual state.
- */
- if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
- if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
- priv->seqno += 0x10;
- ieee80211hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
- ieee80211hdr->seq_ctrl |= cpu_to_le16(priv->seqno);
- }
-
- info->driver_data[0] = dev;
- info->driver_data[1] = urb;
-
- usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep),
- buf, skb->len, rtl8187_tx_cb, skb);
- rc = usb_submit_urb(urb, GFP_ATOMIC);
- if (rc < 0) {
- usb_free_urb(urb);
- kfree_skb(skb);
- }
-
- return 0;
-}
-
-static void rtl8187_rx_cb(struct urb *urb)
-{
- struct sk_buff *skb = (struct sk_buff *)urb->context;
- struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb;
- struct ieee80211_hw *dev = info->dev;
- struct rtl8187_priv *priv = dev->priv;
- struct ieee80211_rx_status rx_status = { 0 };
- int rate, signal;
- u32 flags;
- u32 quality;
-
- spin_lock(&priv->rx_queue.lock);
- if (skb->next)
- __skb_unlink(skb, &priv->rx_queue);
- else {
- spin_unlock(&priv->rx_queue.lock);
- return;
- }
- spin_unlock(&priv->rx_queue.lock);
-
- if (unlikely(urb->status)) {
- usb_free_urb(urb);
- dev_kfree_skb_irq(skb);
- return;
- }
-
- skb_put(skb, urb->actual_length);
- if (!priv->is_rtl8187b) {
- struct rtl8187_rx_hdr *hdr =
- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
- flags = le32_to_cpu(hdr->flags);
- signal = hdr->signal & 0x7f;
- rx_status.antenna = (hdr->signal >> 7) & 1;
- rx_status.noise = hdr->noise;
- rx_status.mactime = le64_to_cpu(hdr->mac_time);
- priv->quality = signal;
- rx_status.qual = priv->quality;
- priv->noise = hdr->noise;
- rate = (flags >> 20) & 0xF;
- if (rate > 3) { /* OFDM rate */
- if (signal > 90)
- signal = 90;
- else if (signal < 25)
- signal = 25;
- signal = 90 - signal;
- } else { /* CCK rate */
- if (signal > 95)
- signal = 95;
- else if (signal < 30)
- signal = 30;
- signal = 95 - signal;
- }
- rx_status.signal = signal;
- priv->signal = signal;
- } else {
- struct rtl8187b_rx_hdr *hdr =
- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
- /* The Realtek datasheet for the RTL8187B shows that the RX
- * header contains the following quantities: signal quality,
- * RSSI, AGC, the received power in dB, and the measured SNR.
- * In testing, none of these quantities show qualitative
- * agreement with AP signal strength, except for the AGC,
- * which is inversely proportional to the strength of the
- * signal. In the following, the quality and signal strength
- * are derived from the AGC. The arbitrary scaling constants
- * are chosen to make the results close to the values obtained
- * for a BCM4312 using b43 as the driver. The noise is ignored
- * for now.
- */
- flags = le32_to_cpu(hdr->flags);
- quality = 170 - hdr->agc;
- if (quality > 100)
- quality = 100;
- signal = 14 - hdr->agc / 2;
- rx_status.qual = quality;
- priv->quality = quality;
- rx_status.signal = signal;
- priv->signal = signal;
- rx_status.antenna = (hdr->rssi >> 7) & 1;
- rx_status.mactime = le64_to_cpu(hdr->mac_time);
- rate = (flags >> 20) & 0xF;
- }
-
- skb_trim(skb, flags & 0x0FFF);
- rx_status.rate_idx = rate;
- rx_status.freq = dev->conf.channel->center_freq;
- rx_status.band = dev->conf.channel->band;
- rx_status.flag |= RX_FLAG_TSFT;
- if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
- rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
- ieee80211_rx_irqsafe(dev, skb, &rx_status);
-
- skb = dev_alloc_skb(RTL8187_MAX_RX);
- if (unlikely(!skb)) {
- usb_free_urb(urb);
- /* TODO check rx queue length and refill *somewhere* */
- return;
- }
-
- info = (struct rtl8187_rx_info *)skb->cb;
- info->urb = urb;
- info->dev = dev;
- urb->transfer_buffer = skb_tail_pointer(skb);
- urb->context = skb;
- skb_queue_tail(&priv->rx_queue, skb);
-
- usb_submit_urb(urb, GFP_ATOMIC);
-}
-
-static int rtl8187_init_urbs(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- struct urb *entry;
- struct sk_buff *skb;
- struct rtl8187_rx_info *info;
-
- while (skb_queue_len(&priv->rx_queue) < 8) {
- skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL);
- if (!skb)
- break;
- entry = usb_alloc_urb(0, GFP_KERNEL);
- if (!entry) {
- kfree_skb(skb);
- break;
- }
- usb_fill_bulk_urb(entry, priv->udev,
- usb_rcvbulkpipe(priv->udev,
- priv->is_rtl8187b ? 3 : 1),
- skb_tail_pointer(skb),
- RTL8187_MAX_RX, rtl8187_rx_cb, skb);
- info = (struct rtl8187_rx_info *)skb->cb;
- info->urb = entry;
- info->dev = dev;
- skb_queue_tail(&priv->rx_queue, skb);
- usb_submit_urb(entry, GFP_KERNEL);
- }
-
- return 0;
-}
-
-static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- u8 reg;
- int i;
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg &= (1 << 1);
- reg |= RTL818X_CMD_RESET;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
-
- i = 10;
- do {
- msleep(2);
- if (!(rtl818x_ioread8(priv, &priv->map->CMD) &
- RTL818X_CMD_RESET))
- break;
- } while (--i);
-
- if (!i) {
- printk(KERN_ERR "%s: Reset timeout!\n", wiphy_name(dev->wiphy));
- return -ETIMEDOUT;
- }
-
- /* reload registers from eeprom */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD);
-
- i = 10;
- do {
- msleep(4);
- if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) &
- RTL818X_EEPROM_CMD_CONFIG))
- break;
- } while (--i);
-
- if (!i) {
- printk(KERN_ERR "%s: eeprom reset timeout!\n",
- wiphy_name(dev->wiphy));
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int rtl8187_init_hw(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- u8 reg;
- int res;
-
- /* reset */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg |
- RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
- RTL8187_RTL8225_ANAPARAM_ON);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187_RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg &
- ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
-
- msleep(200);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
- msleep(200);
-
- res = rtl8187_cmd_reset(dev);
- if (res)
- return res;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
- RTL8187_RTL8225_ANAPARAM_ON);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187_RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- /* setup card */
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0);
- rtl818x_iowrite8(priv, &priv->map->GPIO, 0);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8));
- rtl818x_iowrite8(priv, &priv->map->GPIO, 1);
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
-
- rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
- reg &= 0x3F;
- reg |= 0x80;
- rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
- rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
-
- // TODO: set RESP_RATE and BRSR properly
- rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
-
- /* host_usb_init */
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0);
- rtl818x_iowrite8(priv, &priv->map->GPIO, 0);
- reg = rtl818x_ioread8(priv, (u8 *)0xFE53);
- rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7));
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8));
- rtl818x_iowrite8(priv, &priv->map->GPIO, 0x20);
- rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80);
- msleep(100);
-
- rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
- rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_CONFIG);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_NORMAL);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7);
- msleep(100);
-
- priv->rf->init(dev);
-
- rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
- reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1;
- rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
- rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10);
- rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80);
- rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60);
- rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
-
- return 0;
-}
-
-static const u8 rtl8187b_reg_table[][3] = {
- {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0},
- {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0},
- {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0},
- {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0},
-
- {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1},
- {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1},
- {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xE0, 0xFF, 1}, {0xE1, 0x0F, 1},
- {0xE2, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, {0xF2, 0x02, 1},
- {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, {0xF6, 0x06, 1},
- {0xF7, 0x07, 1}, {0xF8, 0x08, 1},
-
- {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2},
- {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2},
- {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2},
- {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2},
- {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2},
- {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2},
- {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, {0x72, 0x56, 2},
- {0x73, 0x9A, 2},
-
- {0x34, 0xF0, 0}, {0x35, 0x0F, 0}, {0x5B, 0x40, 0}, {0x84, 0x88, 0},
- {0x85, 0x24, 0}, {0x88, 0x54, 0}, {0x8B, 0xB8, 0}, {0x8C, 0x07, 0},
- {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, {0x95, 0x12, 0}, {0x96, 0x00, 0},
- {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, {0x9F, 0x10, 0}, {0xB4, 0x22, 0},
- {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, {0xEE, 0x00, 0}, {0x91, 0x03, 0},
-
- {0x4C, 0x00, 2}, {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0},
- {0x8E, 0x08, 0}, {0x8F, 0x00, 0}
-};
-
-static int rtl8187b_init_hw(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- int res, i;
- u8 reg;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_CONFIG);
-
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- reg |= RTL818X_CONFIG3_ANAPARAM_WRITE | RTL818X_CONFIG3_GNT_SELECT;
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187B_RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
- RTL8187B_RTL8225_ANAPARAM_ON);
- rtl818x_iowrite8(priv, &priv->map->ANAPARAM3,
- RTL8187B_RTL8225_ANAPARAM3_ON);
-
- rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10);
- reg = rtl818x_ioread8(priv, (u8 *)0xFF62);
- rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5));
- rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5));
-
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE;
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_NORMAL);
-
- res = rtl8187_cmd_reset(dev);
- if (res)
- return res;
-
- rtl818x_iowrite16(priv, (__le16 *)0xFF2D, 0x0FFF);
- reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
- rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
- reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg |= RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT |
- RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
- rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
-
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1);
- reg = rtl818x_ioread8(priv, &priv->map->RATE_FALLBACK);
- reg |= RTL818X_RATE_FALLBACK_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, reg);
-
- rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100);
- rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
- rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
- for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) {
- rtl818x_iowrite8_idx(priv,
- (u8 *)(uintptr_t)
- (rtl8187b_reg_table[i][0] | 0xFF00),
- rtl8187b_reg_table[i][1],
- rtl8187b_reg_table[i][2]);
- }
-
- rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50);
- rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0);
-
- rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1);
- rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1);
- rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1);
-
- rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001);
-
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- reg |= RTL818X_CONFIG3_ANAPARAM_WRITE;
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
- RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488);
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
- msleep(1100);
-
- priv->rf->init(dev);
-
- reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
-
- rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4);
- rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00);
- rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
- rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
- rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F);
- rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
- rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
-
- reg = rtl818x_ioread8(priv, (u8 *)0xFFDB);
- rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2));
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3);
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3);
- rtl818x_iowrite8(priv, (u8 *)0xFF61, 0);
- rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1);
- rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1);
- rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10);
- rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2);
-
- rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B);
-
- rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1);
-
- return 0;
-}
-
-static int rtl8187_start(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- u32 reg;
- int ret;
-
- ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) :
- rtl8187b_init_hw(dev);
- if (ret)
- return ret;
-
- mutex_lock(&priv->conf_mutex);
- if (priv->is_rtl8187b) {
- reg = RTL818X_RX_CONF_MGMT |
- RTL818X_RX_CONF_DATA |
- RTL818X_RX_CONF_BROADCAST |
- RTL818X_RX_CONF_NICMAC |
- RTL818X_RX_CONF_BSSID |
- (7 << 13 /* RX FIFO threshold NONE */) |
- (7 << 10 /* MAX RX DMA */) |
- RTL818X_RX_CONF_RX_AUTORESETPHY |
- RTL818X_RX_CONF_ONLYERLPKT |
- RTL818X_RX_CONF_MULTICAST;
- priv->rx_conf = reg;
- rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
-
- rtl818x_iowrite32(priv, &priv->map->TX_CONF,
- RTL818X_TX_CONF_HW_SEQNUM |
- RTL818X_TX_CONF_DISREQQSIZE |
- (7 << 8 /* short retry limit */) |
- (7 << 0 /* long retry limit */) |
- (7 << 21 /* MAX TX DMA */));
- rtl8187_init_urbs(dev);
- mutex_unlock(&priv->conf_mutex);
- return 0;
- }
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
-
- rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
- rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
-
- rtl8187_init_urbs(dev);
-
- reg = RTL818X_RX_CONF_ONLYERLPKT |
- RTL818X_RX_CONF_RX_AUTORESETPHY |
- RTL818X_RX_CONF_BSSID |
- RTL818X_RX_CONF_MGMT |
- RTL818X_RX_CONF_DATA |
- (7 << 13 /* RX FIFO threshold NONE */) |
- (7 << 10 /* MAX RX DMA */) |
- RTL818X_RX_CONF_BROADCAST |
- RTL818X_RX_CONF_NICMAC;
-
- priv->rx_conf = reg;
- rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
-
- reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
- reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
- reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
- rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
-
- reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
- reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
- rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
-
- reg = RTL818X_TX_CONF_CW_MIN |
- (7 << 21 /* MAX TX DMA */) |
- RTL818X_TX_CONF_NO_ICV;
- rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg |= RTL818X_CMD_TX_ENABLE;
- reg |= RTL818X_CMD_RX_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
- mutex_unlock(&priv->conf_mutex);
-
- return 0;
-}
-
-static void rtl8187_stop(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- struct rtl8187_rx_info *info;
- struct sk_buff *skb;
- u32 reg;
-
- mutex_lock(&priv->conf_mutex);
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
-
- reg = rtl818x_ioread8(priv, &priv->map->CMD);
- reg &= ~RTL818X_CMD_TX_ENABLE;
- reg &= ~RTL818X_CMD_RX_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->CMD, reg);
-
- priv->rf->stop(dev);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG4);
- rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- while ((skb = skb_dequeue(&priv->rx_queue))) {
- info = (struct rtl8187_rx_info *)skb->cb;
- usb_kill_urb(info->urb);
- kfree_skb(skb);
- }
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int rtl8187_add_interface(struct ieee80211_hw *dev,
- struct ieee80211_if_init_conf *conf)
-{
- struct rtl8187_priv *priv = dev->priv;
- int i;
-
- if (priv->mode != NL80211_IFTYPE_MONITOR)
- return -EOPNOTSUPP;
-
- switch (conf->type) {
- case NL80211_IFTYPE_STATION:
- priv->mode = conf->type;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- mutex_lock(&priv->conf_mutex);
- priv->vif = conf->vif;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- for (i = 0; i < ETH_ALEN; i++)
- rtl818x_iowrite8(priv, &priv->map->MAC[i],
- ((u8 *)conf->mac_addr)[i]);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- mutex_unlock(&priv->conf_mutex);
- return 0;
-}
-
-static void rtl8187_remove_interface(struct ieee80211_hw *dev,
- struct ieee80211_if_init_conf *conf)
-{
- struct rtl8187_priv *priv = dev->priv;
- mutex_lock(&priv->conf_mutex);
- priv->mode = NL80211_IFTYPE_MONITOR;
- priv->vif = NULL;
- mutex_unlock(&priv->conf_mutex);
-}
-
-static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
-{
- struct rtl8187_priv *priv = dev->priv;
- u32 reg;
-
- mutex_lock(&priv->conf_mutex);
- reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
- /* Enable TX loopback on MAC level to avoid TX during channel
- * changes, as this has be seen to causes problems and the
- * card will stop work until next reset
- */
- rtl818x_iowrite32(priv, &priv->map->TX_CONF,
- reg | RTL818X_TX_CONF_LOOPBACK_MAC);
- msleep(10);
- priv->rf->set_chan(dev, conf);
- msleep(10);
- rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
-
- if (!priv->is_rtl8187b) {
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
-
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
- } else {
- rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
- rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
- rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24);
- rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
- }
- }
-
- rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
- rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100);
- rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100);
- rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100);
- mutex_unlock(&priv->conf_mutex);
- return 0;
-}
-
-static int rtl8187_config_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_if_conf *conf)
-{
- struct rtl8187_priv *priv = dev->priv;
- int i;
- u8 reg;
-
- mutex_lock(&priv->conf_mutex);
- for (i = 0; i < ETH_ALEN; i++)
- rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);
-
- if (is_valid_ether_addr(conf->bssid)) {
- reg = RTL818X_MSR_INFRA;
- if (priv->is_rtl8187b)
- reg |= RTL818X_MSR_ENEDCA;
- rtl818x_iowrite8(priv, &priv->map->MSR, reg);
- } else {
- reg = RTL818X_MSR_NO_LINK;
- rtl818x_iowrite8(priv, &priv->map->MSR, reg);
- }
-
- mutex_unlock(&priv->conf_mutex);
- return 0;
-}
-
-static void rtl8187_configure_filter(struct ieee80211_hw *dev,
- unsigned int changed_flags,
- unsigned int *total_flags,
- int mc_count, struct dev_addr_list *mclist)
-{
- struct rtl8187_priv *priv = dev->priv;
-
- if (changed_flags & FIF_FCSFAIL)
- priv->rx_conf ^= RTL818X_RX_CONF_FCS;
- if (changed_flags & FIF_CONTROL)
- priv->rx_conf ^= RTL818X_RX_CONF_CTRL;
- if (changed_flags & FIF_OTHER_BSS)
- priv->rx_conf ^= RTL818X_RX_CONF_MONITOR;
- if (*total_flags & FIF_ALLMULTI || mc_count > 0)
- priv->rx_conf |= RTL818X_RX_CONF_MULTICAST;
- else
- priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST;
-
- *total_flags = 0;
-
- if (priv->rx_conf & RTL818X_RX_CONF_FCS)
- *total_flags |= FIF_FCSFAIL;
- if (priv->rx_conf & RTL818X_RX_CONF_CTRL)
- *total_flags |= FIF_CONTROL;
- if (priv->rx_conf & RTL818X_RX_CONF_MONITOR)
- *total_flags |= FIF_OTHER_BSS;
- if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST)
- *total_flags |= FIF_ALLMULTI;
-
- rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf);
-}
-
-static const struct ieee80211_ops rtl8187_ops = {
- .tx = rtl8187_tx,
- .start = rtl8187_start,
- .stop = rtl8187_stop,
- .add_interface = rtl8187_add_interface,
- .remove_interface = rtl8187_remove_interface,
- .config = rtl8187_config,
- .config_interface = rtl8187_config_interface,
- .configure_filter = rtl8187_configure_filter,
-};
-
-static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom)
-{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8187_priv *priv = dev->priv;
- u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
-
- eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE;
- eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ;
- eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK;
- eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS;
-}
-
-static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom)
-{
- struct ieee80211_hw *dev = eeprom->data;
- struct rtl8187_priv *priv = dev->priv;
- u8 reg = RTL818X_EEPROM_CMD_PROGRAM;
-
- if (eeprom->reg_data_in)
- reg |= RTL818X_EEPROM_CMD_WRITE;
- if (eeprom->reg_data_out)
- reg |= RTL818X_EEPROM_CMD_READ;
- if (eeprom->reg_data_clock)
- reg |= RTL818X_EEPROM_CMD_CK;
- if (eeprom->reg_chip_select)
- reg |= RTL818X_EEPROM_CMD_CS;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg);
- udelay(10);
-}
-
-static int __devinit rtl8187_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct ieee80211_hw *dev;
- struct rtl8187_priv *priv;
- struct eeprom_93cx6 eeprom;
- struct ieee80211_channel *channel;
- const char *chip_name;
- u16 txpwr, reg;
- int err, i;
- DECLARE_MAC_BUF(mac);
-
- dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops);
- if (!dev) {
- printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n");
- return -ENOMEM;
- }
-
- priv = dev->priv;
- priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);
-
- SET_IEEE80211_DEV(dev, &intf->dev);
- usb_set_intfdata(intf, dev);
- priv->udev = udev;
-
- usb_get_dev(udev);
-
- skb_queue_head_init(&priv->rx_queue);
-
- BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels));
- BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates));
-
- memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels));
- memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates));
- priv->map = (struct rtl818x_csr *)0xFF00;
-
- priv->band.band = IEEE80211_BAND_2GHZ;
- priv->band.channels = priv->channels;
- priv->band.n_channels = ARRAY_SIZE(rtl818x_channels);
- priv->band.bitrates = priv->rates;
- priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates);
- dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
-
-
- priv->mode = NL80211_IFTYPE_MONITOR;
- dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
- IEEE80211_HW_RX_INCLUDES_FCS;
-
- eeprom.data = dev;
- eeprom.register_read = rtl8187_eeprom_register_read;
- eeprom.register_write = rtl8187_eeprom_register_write;
- if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
- eeprom.width = PCI_EEPROM_WIDTH_93C66;
- else
- eeprom.width = PCI_EEPROM_WIDTH_93C46;
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- udelay(10);
-
- eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR,
- (__le16 __force *)dev->wiphy->perm_addr, 3);
- if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
- printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly "
- "generated MAC address\n");
- random_ether_addr(dev->wiphy->perm_addr);
- }
-
- channel = priv->channels;
- for (i = 0; i < 3; i++) {
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i,
- &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
- (*channel++).hw_value = txpwr >> 8;
- }
- for (i = 0; i < 2; i++) {
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i,
- &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
- (*channel++).hw_value = txpwr >> 8;
- }
-
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE,
- &priv->txpwr_base);
-
- reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1;
- rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
- /* 0 means asic B-cut, we should use SW 3 wire
- * bit-by-bit banging for radio. 1 means we can use
- * USB specific request to write radio registers */
- priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3;
- rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- if (!priv->is_rtl8187b) {
- u32 reg32;
- reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF);
- reg32 &= RTL818X_TX_CONF_HWVER_MASK;
- switch (reg32) {
- case RTL818X_TX_CONF_R8187vD_B:
- /* Some RTL8187B devices have a USB ID of 0x8187
- * detect them here */
- chip_name = "RTL8187BvB(early)";
- priv->is_rtl8187b = 1;
- priv->hw_rev = RTL8187BvB;
- break;
- case RTL818X_TX_CONF_R8187vD:
- chip_name = "RTL8187vD";
- break;
- default:
- chip_name = "RTL8187vB (default)";
- }
- } else {
- /*
- * Force USB request to write radio registers for 8187B, Realtek
- * only uses it in their sources
- */
- /*if (priv->asic_rev == 0) {
- printk(KERN_WARNING "rtl8187: Forcing use of USB "
- "requests to write to radio registers\n");
- priv->asic_rev = 1;
- }*/
- switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) {
- case RTL818X_R8187B_B:
- chip_name = "RTL8187BvB";
- priv->hw_rev = RTL8187BvB;
- break;
- case RTL818X_R8187B_D:
- chip_name = "RTL8187BvD";
- priv->hw_rev = RTL8187BvD;
- break;
- case RTL818X_R8187B_E:
- chip_name = "RTL8187BvE";
- priv->hw_rev = RTL8187BvE;
- break;
- default:
- chip_name = "RTL8187BvB (default)";
- priv->hw_rev = RTL8187BvB;
- }
- }
-
- if (!priv->is_rtl8187b) {
- for (i = 0; i < 2; i++) {
- eeprom_93cx6_read(&eeprom,
- RTL8187_EEPROM_TXPWR_CHAN_6 + i,
- &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
- (*channel++).hw_value = txpwr >> 8;
- }
- } else {
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6,
- &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
-
- eeprom_93cx6_read(&eeprom, 0x0A, &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
-
- eeprom_93cx6_read(&eeprom, 0x1C, &txpwr);
- (*channel++).hw_value = txpwr & 0xFF;
- (*channel++).hw_value = txpwr >> 8;
- }
-
- if (priv->is_rtl8187b) {
- printk(KERN_WARNING "rtl8187: 8187B chip detected. Support "
- "is EXPERIMENTAL, and could damage your\n"
- " hardware, use at your own risk\n");
- dev->flags |= IEEE80211_HW_SIGNAL_DBM;
- } else {
- dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC;
- dev->max_signal = 65;
- }
-
- dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
-
- if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b)
- printk(KERN_INFO "rtl8187: inconsistency between id with OEM"
- " info!\n");
-
- priv->rf = rtl8187_detect_rf(dev);
- dev->extra_tx_headroom = (!priv->is_rtl8187b) ?
- sizeof(struct rtl8187_tx_hdr) :
- sizeof(struct rtl8187b_tx_hdr);
- if (!priv->is_rtl8187b)
- dev->queues = 1;
- else
- dev->queues = 4;
-
- err = ieee80211_register_hw(dev);
- if (err) {
- printk(KERN_ERR "rtl8187: Cannot register device\n");
- goto err_free_dev;
- }
- mutex_init(&priv->conf_mutex);
-
- printk(KERN_INFO "%s: hwaddr %s, %s V%d + %s\n",
- wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr),
- chip_name, priv->asic_rev, priv->rf->name);
-
- return 0;
-
- err_free_dev:
- ieee80211_free_hw(dev);
- usb_set_intfdata(intf, NULL);
- usb_put_dev(udev);
- return err;
-}
-
-static void __devexit rtl8187_disconnect(struct usb_interface *intf)
-{
- struct ieee80211_hw *dev = usb_get_intfdata(intf);
- struct rtl8187_priv *priv;
-
- if (!dev)
- return;
-
- ieee80211_unregister_hw(dev);
-
- priv = dev->priv;
- usb_put_dev(interface_to_usbdev(intf));
- ieee80211_free_hw(dev);
-}
-
-static struct usb_driver rtl8187_driver = {
- .name = KBUILD_MODNAME,
- .id_table = rtl8187_table,
- .probe = rtl8187_probe,
- .disconnect = __devexit_p(rtl8187_disconnect),
-};
-
-static int __init rtl8187_init(void)
-{
- return usb_register(&rtl8187_driver);
-}
-
-static void __exit rtl8187_exit(void)
-{
- usb_deregister(&rtl8187_driver);
-}
-
-module_init(rtl8187_init);
-module_exit(rtl8187_exit);
diff --git a/drivers/net/wireless/rtl8187_rtl8225.c b/drivers/net/wireless/rtl8187_rtl8225.c
deleted file mode 100644
index 1bae89903410..000000000000
--- a/drivers/net/wireless/rtl8187_rtl8225.c
+++ /dev/null
@@ -1,992 +0,0 @@
-/*
- * Radio tuning for RTL8225 on RTL8187
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * Magic delays, register offsets, and phy value tables below are
- * taken from the original r8187 driver sources. Thanks to Realtek
- * for their support!
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/init.h>
-#include <linux/usb.h>
-#include <net/mac80211.h>
-
-#include "rtl8187.h"
-#include "rtl8187_rtl8225.h"
-
-static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data)
-{
- struct rtl8187_priv *priv = dev->priv;
- u16 reg80, reg84, reg82;
- u32 bangdata;
- int i;
-
- bangdata = (data << 4) | (addr & 0xf);
-
- reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
- reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
-
- reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7);
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
- udelay(10);
-
- for (i = 15; i >= 0; i--) {
- u16 reg = reg80 | (bangdata & (1 << i)) >> i;
-
- if (i & 1)
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
-
- if (!(i & 1))
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
- msleep(2);
-}
-
-static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data)
-{
- struct rtl8187_priv *priv = dev->priv;
- u16 reg80, reg82, reg84;
-
- reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
- reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
- reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
-
- reg80 &= ~(0x3 << 2);
- reg84 &= ~0xF;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007);
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- udelay(2);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
- udelay(10);
-
- usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
- RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
- addr, 0x8225, &data, sizeof(data), HZ / 2);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- udelay(10);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
- msleep(2);
-}
-
-static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
-{
- struct rtl8187_priv *priv = dev->priv;
-
- if (priv->asic_rev)
- rtl8225_write_8051(dev, addr, cpu_to_le16(data));
- else
- rtl8225_write_bitbang(dev, addr, data);
-}
-
-static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
-{
- struct rtl8187_priv *priv = dev->priv;
- u16 reg80, reg82, reg84, out;
- int i;
-
- reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
- reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
- reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
-
- reg80 &= ~0xF;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
- udelay(4);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
- udelay(5);
-
- for (i = 4; i >= 0; i--) {
- u16 reg = reg80 | ((addr >> i) & 1);
-
- if (!(i & 1)) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- udelay(1);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg | (1 << 1));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg | (1 << 1));
- udelay(2);
-
- if (i & 1) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
- udelay(1);
- }
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- udelay(2);
-
- out = 0;
- for (i = 11; i >= 0; i--) {
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- udelay(1);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- udelay(2);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 1));
- udelay(2);
-
- if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
- out |= 1 << i;
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3));
- udelay(2);
- }
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
- reg80 | (1 << 3) | (1 << 2));
- udelay(2);
-
- rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
- rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
- rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
-
- return out;
-}
-
-static const u16 rtl8225bcd_rxgain[] = {
- 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
- 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
- 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
- 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
- 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
- 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
- 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
- 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
- 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
- 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
- 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
- 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
-};
-
-static const u8 rtl8225_agc[] = {
- 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
- 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
- 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
- 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
- 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
- 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
- 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
- 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
- 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
- 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
- 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
- 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
- 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
-};
-
-static const u8 rtl8225_gain[] = {
- 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */
- 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */
- 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */
- 0x33, 0x80, 0x79, 0xc5, /* -78dBm */
- 0x43, 0x78, 0x76, 0xc5, /* -74dBm */
- 0x53, 0x60, 0x73, 0xc5, /* -70dBm */
- 0x63, 0x58, 0x70, 0xc5, /* -66dBm */
-};
-
-static const u8 rtl8225_threshold[] = {
- 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
-};
-
-static const u8 rtl8225_tx_gain_cck_ofdm[] = {
- 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
-};
-
-static const u8 rtl8225_tx_power_cck[] = {
- 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
- 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
- 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
- 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
- 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
- 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
-};
-
-static const u8 rtl8225_tx_power_cck_ch14[] = {
- 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
- 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
- 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
- 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
- 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
-};
-
-static const u8 rtl8225_tx_power_ofdm[] = {
- 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
-};
-
-static const u32 rtl8225_chan[] = {
- 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
- 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
-};
-
-static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
-{
- struct rtl8187_priv *priv = dev->priv;
- u8 cck_power, ofdm_power;
- const u8 *tmp;
- u32 reg;
- int i;
-
- cck_power = priv->channels[channel - 1].hw_value & 0xF;
- ofdm_power = priv->channels[channel - 1].hw_value >> 4;
-
- cck_power = min(cck_power, (u8)11);
- ofdm_power = min(ofdm_power, (u8)35);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
- rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
-
- if (channel == 14)
- tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
- else
- tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
-
- for (i = 0; i < 8; i++)
- rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
-
- msleep(1); // FIXME: optional?
-
- /* anaparam2 on */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187_RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl8225_write_phy_ofdm(dev, 2, 0x42);
- rtl8225_write_phy_ofdm(dev, 6, 0x00);
- rtl8225_write_phy_ofdm(dev, 8, 0x00);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
- rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1);
-
- tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
-
- rtl8225_write_phy_ofdm(dev, 5, *tmp);
- rtl8225_write_phy_ofdm(dev, 7, *tmp);
-
- msleep(1);
-}
-
-static void rtl8225_rf_init(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- int i;
-
- rtl8225_write(dev, 0x0, 0x067); msleep(1);
- rtl8225_write(dev, 0x1, 0xFE0); msleep(1);
- rtl8225_write(dev, 0x2, 0x44D); msleep(1);
- rtl8225_write(dev, 0x3, 0x441); msleep(1);
- rtl8225_write(dev, 0x4, 0x486); msleep(1);
- rtl8225_write(dev, 0x5, 0xBC0); msleep(1);
- rtl8225_write(dev, 0x6, 0xAE6); msleep(1);
- rtl8225_write(dev, 0x7, 0x82A); msleep(1);
- rtl8225_write(dev, 0x8, 0x01F); msleep(1);
- rtl8225_write(dev, 0x9, 0x334); msleep(1);
- rtl8225_write(dev, 0xA, 0xFD4); msleep(1);
- rtl8225_write(dev, 0xB, 0x391); msleep(1);
- rtl8225_write(dev, 0xC, 0x050); msleep(1);
- rtl8225_write(dev, 0xD, 0x6DB); msleep(1);
- rtl8225_write(dev, 0xE, 0x029); msleep(1);
- rtl8225_write(dev, 0xF, 0x914); msleep(100);
-
- rtl8225_write(dev, 0x2, 0xC4D); msleep(200);
- rtl8225_write(dev, 0x2, 0x44D); msleep(200);
-
- if (!(rtl8225_read(dev, 6) & (1 << 7))) {
- rtl8225_write(dev, 0x02, 0x0c4d);
- msleep(200);
- rtl8225_write(dev, 0x02, 0x044d);
- msleep(100);
- if (!(rtl8225_read(dev, 6) & (1 << 7)))
- printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
- wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
- }
-
- rtl8225_write(dev, 0x0, 0x127);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
- rtl8225_write(dev, 0x1, i + 1);
- rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
- }
-
- rtl8225_write(dev, 0x0, 0x027);
- rtl8225_write(dev, 0x0, 0x22F);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
- rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
- msleep(1);
- rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
- msleep(1);
- }
-
- msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x02, 0x42); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
- rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
- rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
- rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
-
- rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
- rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
- rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
- rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
- rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1);
- rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
- rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
- rtl8225_write_phy_cck(dev, 0x13, 0xd0);
- rtl8225_write_phy_cck(dev, 0x19, 0x00);
- rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
- rtl8225_write_phy_cck(dev, 0x1b, 0x08);
- rtl8225_write_phy_cck(dev, 0x40, 0x86);
- rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1);
- rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
- rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1);
- rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1);
- rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1);
- rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D);
-
- rtl8225_rf_set_tx_power(dev, 1);
-
- /* RX antenna default to A */
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
-
- rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
- msleep(1);
- rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
-
- /* set sensitivity */
- rtl8225_write(dev, 0x0c, 0x50);
- rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
- rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
- rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
- rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
- rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]);
-}
-
-static const u8 rtl8225z2_agc[] = {
- 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f,
- 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37,
- 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f,
- 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07,
- 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
- 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28,
- 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d,
- 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
-};
-static const u8 rtl8225z2_ofdm[] = {
- 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60,
- 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00,
- 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26,
- 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3,
- 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f,
- 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00,
- 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e,
- 0x6d, 0x3c, 0xfb, 0x07
-};
-
-static const u8 rtl8225z2_tx_power_cck_ch14[] = {
- 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
- 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
-};
-
-static const u8 rtl8225z2_tx_power_cck[] = {
- 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
- 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
- 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
- 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
-};
-
-static const u8 rtl8225z2_tx_power_ofdm[] = {
- 0x42, 0x00, 0x40, 0x00, 0x40
-};
-
-static const u8 rtl8225z2_tx_gain_cck_ofdm[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
- 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
- 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
-};
-
-static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
-{
- struct rtl8187_priv *priv = dev->priv;
- u8 cck_power, ofdm_power;
- const u8 *tmp;
- u32 reg;
- int i;
-
- cck_power = priv->channels[channel - 1].hw_value & 0xF;
- ofdm_power = priv->channels[channel - 1].hw_value >> 4;
-
- cck_power = min(cck_power, (u8)15);
- cck_power += priv->txpwr_base & 0xF;
- cck_power = min(cck_power, (u8)35);
-
- ofdm_power = min(ofdm_power, (u8)15);
- ofdm_power += priv->txpwr_base >> 4;
- ofdm_power = min(ofdm_power, (u8)35);
-
- if (channel == 14)
- tmp = rtl8225z2_tx_power_cck_ch14;
- else
- tmp = rtl8225z2_tx_power_cck;
-
- for (i = 0; i < 8; i++)
- rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
- rtl8225z2_tx_gain_cck_ofdm[cck_power]);
- msleep(1);
-
- /* anaparam2 on */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187_RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3,
- reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl8225_write_phy_ofdm(dev, 2, 0x42);
- rtl8225_write_phy_ofdm(dev, 5, 0x00);
- rtl8225_write_phy_ofdm(dev, 6, 0x40);
- rtl8225_write_phy_ofdm(dev, 7, 0x00);
- rtl8225_write_phy_ofdm(dev, 8, 0x40);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
- rtl8225z2_tx_gain_cck_ofdm[ofdm_power]);
- msleep(1);
-}
-
-static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
-{
- struct rtl8187_priv *priv = dev->priv;
- u8 cck_power, ofdm_power;
- const u8 *tmp;
- int i;
-
- cck_power = priv->channels[channel - 1].hw_value & 0xF;
- ofdm_power = priv->channels[channel - 1].hw_value >> 4;
-
- if (cck_power > 15)
- cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22;
- else
- cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7;
- cck_power += priv->txpwr_base & 0xF;
- cck_power = min(cck_power, (u8)35);
-
- if (ofdm_power > 15)
- ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25;
- else
- ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10;
- ofdm_power += (priv->txpwr_base >> 4) & 0xF;
- ofdm_power = min(ofdm_power, (u8)35);
-
- if (channel == 14)
- tmp = rtl8225z2_tx_power_cck_ch14;
- else
- tmp = rtl8225z2_tx_power_cck;
-
- if (priv->hw_rev == RTL8187BvB) {
- if (cck_power <= 6)
- ; /* do nothing */
- else if (cck_power <= 11)
- tmp += 8;
- else
- tmp += 16;
- } else {
- if (cck_power <= 5)
- ; /* do nothing */
- else if (cck_power <= 11)
- tmp += 8;
- else if (cck_power <= 17)
- tmp += 16;
- else
- tmp += 24;
- }
-
- for (i = 0; i < 8; i++)
- rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
- rtl8225z2_tx_gain_cck_ofdm[cck_power]);
- msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
- rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1);
- if (priv->hw_rev == RTL8187BvB) {
- if (ofdm_power <= 11) {
- rtl8225_write_phy_ofdm(dev, 0x87, 0x60);
- rtl8225_write_phy_ofdm(dev, 0x89, 0x60);
- } else {
- rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
- rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
- }
- } else {
- if (ofdm_power <= 11) {
- rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
- rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
- } else if (ofdm_power <= 17) {
- rtl8225_write_phy_ofdm(dev, 0x87, 0x54);
- rtl8225_write_phy_ofdm(dev, 0x89, 0x54);
- } else {
- rtl8225_write_phy_ofdm(dev, 0x87, 0x50);
- rtl8225_write_phy_ofdm(dev, 0x89, 0x50);
- }
- }
- msleep(1);
-}
-
-static const u16 rtl8225z2_rxgain[] = {
- 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
- 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
- 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
- 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
- 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
- 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
- 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
- 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
- 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
- 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
- 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
- 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
-};
-
-static const u8 rtl8225z2_gain_bg[] = {
- 0x23, 0x15, 0xa5, /* -82-1dBm */
- 0x23, 0x15, 0xb5, /* -82-2dBm */
- 0x23, 0x15, 0xc5, /* -82-3dBm */
- 0x33, 0x15, 0xc5, /* -78dBm */
- 0x43, 0x15, 0xc5, /* -74dBm */
- 0x53, 0x15, 0xc5, /* -70dBm */
- 0x63, 0x15, 0xc5 /* -66dBm */
-};
-
-static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- int i;
-
- rtl8225_write(dev, 0x0, 0x2BF); msleep(1);
- rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
- rtl8225_write(dev, 0x2, 0x44D); msleep(1);
- rtl8225_write(dev, 0x3, 0x441); msleep(1);
- rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
- rtl8225_write(dev, 0x5, 0xC72); msleep(1);
- rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
- rtl8225_write(dev, 0x7, 0x82A); msleep(1);
- rtl8225_write(dev, 0x8, 0x03F); msleep(1);
- rtl8225_write(dev, 0x9, 0x335); msleep(1);
- rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
- rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
- rtl8225_write(dev, 0xc, 0x850); msleep(1);
- rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
- rtl8225_write(dev, 0xe, 0x02B); msleep(1);
- rtl8225_write(dev, 0xf, 0x114); msleep(100);
-
- rtl8225_write(dev, 0x0, 0x1B7);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
- rtl8225_write(dev, 0x1, i + 1);
- rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
- }
-
- rtl8225_write(dev, 0x3, 0x080);
- rtl8225_write(dev, 0x5, 0x004);
- rtl8225_write(dev, 0x0, 0x0B7);
- rtl8225_write(dev, 0x2, 0xc4D);
-
- msleep(200);
- rtl8225_write(dev, 0x2, 0x44D);
- msleep(100);
-
- if (!(rtl8225_read(dev, 6) & (1 << 7))) {
- rtl8225_write(dev, 0x02, 0x0C4D);
- msleep(200);
- rtl8225_write(dev, 0x02, 0x044D);
- msleep(100);
- if (!(rtl8225_read(dev, 6) & (1 << 7)))
- printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
- wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
- }
-
- msleep(200);
-
- rtl8225_write(dev, 0x0, 0x2BF);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
- rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
- msleep(1);
- rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
- msleep(1);
- }
-
- msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x02, 0x42); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
- rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x11, 0x07); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x21, 0x17); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); //FIXME: not needed?
- rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x25, 0x00); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
-
- rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]);
- rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]);
- rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]);
- rtl8225_write_phy_ofdm(dev, 0x21, 0x37);
-
- rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
- rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
- rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
- rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
- rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1);
- rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
- rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
- rtl8225_write_phy_cck(dev, 0x13, 0xd0);
- rtl8225_write_phy_cck(dev, 0x19, 0x00);
- rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
- rtl8225_write_phy_cck(dev, 0x1b, 0x08);
- rtl8225_write_phy_cck(dev, 0x40, 0x86);
- rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1);
- rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
- rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
- rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1);
- rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1);
- rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1);
- rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1);
- rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1);
- rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1);
- rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
-
- rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1);
-
- rtl8225z2_rf_set_tx_power(dev, 1);
-
- /* RX antenna default to A */
- rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
- rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
-
- rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
- msleep(1);
- rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
-}
-
-static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev)
-{
- struct rtl8187_priv *priv = dev->priv;
- int i;
-
- rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
- rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
- rtl8225_write(dev, 0x2, 0x44D); msleep(1);
- rtl8225_write(dev, 0x3, 0x441); msleep(1);
- rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
- rtl8225_write(dev, 0x5, 0xC72); msleep(1);
- rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
- rtl8225_write(dev, 0x7, 0x82A); msleep(1);
- rtl8225_write(dev, 0x8, 0x03F); msleep(1);
- rtl8225_write(dev, 0x9, 0x335); msleep(1);
- rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
- rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
- rtl8225_write(dev, 0xc, 0x850); msleep(1);
- rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
- rtl8225_write(dev, 0xe, 0x02B); msleep(1);
- rtl8225_write(dev, 0xf, 0x114); msleep(1);
-
- rtl8225_write(dev, 0x0, 0x1B7); msleep(1);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
- rtl8225_write(dev, 0x1, i + 1); msleep(1);
- rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); msleep(1);
- }
-
- rtl8225_write(dev, 0x3, 0x080); msleep(1);
- rtl8225_write(dev, 0x5, 0x004); msleep(1);
- rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
- msleep(3000);
-
- rtl8225_write(dev, 0x2, 0xC4D); msleep(1);
- msleep(2000);
-
- rtl8225_write(dev, 0x2, 0x44D); msleep(1);
- rtl8225_write(dev, 0x0, 0x2BF); msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03);
- rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07);
- rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
-
- rtl8225_write_phy_ofdm(dev, 0x80, 0x12);
- for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) {
- rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]);
- rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i);
- rtl8225_write_phy_ofdm(dev, 0xE, 0);
- }
- rtl8225_write_phy_ofdm(dev, 0x80, 0x10);
-
- for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++)
- rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]);
-
- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
- rtl818x_iowrite8(priv, &priv->map->SLOT, 9);
- rtl818x_iowrite8(priv, (u8 *)0xFFF0, 28);
- rtl818x_iowrite8(priv, (u8 *)0xFFF4, 28);
- rtl818x_iowrite8(priv, (u8 *)0xFFF8, 28);
- rtl818x_iowrite8(priv, (u8 *)0xFFFC, 28);
- rtl818x_iowrite8(priv, (u8 *)0xFF2D, 0x5B);
- rtl818x_iowrite8(priv, (u8 *)0xFF79, 0x5B);
- rtl818x_iowrite32(priv, (__le32 *)0xFFF0, (7 << 12) | (3 << 8) | 28);
- rtl818x_iowrite32(priv, (__le32 *)0xFFF4, (7 << 12) | (3 << 8) | 28);
- rtl818x_iowrite32(priv, (__le32 *)0xFFF8, (7 << 12) | (3 << 8) | 28);
- rtl818x_iowrite32(priv, (__le32 *)0xFFFC, (7 << 12) | (3 << 8) | 28);
- rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0);
-
- rtl8225_write_phy_ofdm(dev, 0x97, 0x46); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); msleep(1);
- rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); msleep(1);
- rtl8225_write_phy_cck(dev, 0xc1, 0x88); msleep(1);
-}
-
-static void rtl8225_rf_stop(struct ieee80211_hw *dev)
-{
- u8 reg;
- struct rtl8187_priv *priv = dev->priv;
-
- rtl8225_write(dev, 0x4, 0x1f); msleep(1);
-
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- if (!priv->is_rtl8187b) {
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187_RTL8225_ANAPARAM2_OFF);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
- RTL8187_RTL8225_ANAPARAM_OFF);
- } else {
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
- RTL8187B_RTL8225_ANAPARAM2_OFF);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
- RTL8187B_RTL8225_ANAPARAM_OFF);
- rtl818x_iowrite8(priv, &priv->map->ANAPARAM3,
- RTL8187B_RTL8225_ANAPARAM3_OFF);
- }
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-}
-
-static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
- struct ieee80211_conf *conf)
-{
- struct rtl8187_priv *priv = dev->priv;
- int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
- if (priv->rf->init == rtl8225_rf_init)
- rtl8225_rf_set_tx_power(dev, chan);
- else if (priv->rf->init == rtl8225z2_rf_init)
- rtl8225z2_rf_set_tx_power(dev, chan);
- else
- rtl8225z2_b_rf_set_tx_power(dev, chan);
-
- rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
- msleep(10);
-}
-
-static const struct rtl818x_rf_ops rtl8225_ops = {
- .name = "rtl8225",
- .init = rtl8225_rf_init,
- .stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
-};
-
-static const struct rtl818x_rf_ops rtl8225z2_ops = {
- .name = "rtl8225z2",
- .init = rtl8225z2_rf_init,
- .stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
-};
-
-static const struct rtl818x_rf_ops rtl8225z2_b_ops = {
- .name = "rtl8225z2",
- .init = rtl8225z2_b_rf_init,
- .stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
-};
-
-const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev)
-{
- u16 reg8, reg9;
- struct rtl8187_priv *priv = dev->priv;
-
- if (!priv->is_rtl8187b) {
- rtl8225_write(dev, 0, 0x1B7);
-
- reg8 = rtl8225_read(dev, 8);
- reg9 = rtl8225_read(dev, 9);
-
- rtl8225_write(dev, 0, 0x0B7);
-
- if (reg8 != 0x588 || reg9 != 0x700)
- return &rtl8225_ops;
-
- return &rtl8225z2_ops;
- } else
- return &rtl8225z2_b_ops;
-}
diff --git a/drivers/net/wireless/rtl818x.h b/drivers/net/wireless/rtl818x.h
deleted file mode 100644
index 3538b15211b1..000000000000
--- a/drivers/net/wireless/rtl818x.h
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Definitions for RTL818x hardware
- *
- * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
- *
- * Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
- *
- * This program is free software; you can 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 RTL818X_H
-#define RTL818X_H
-
-struct rtl818x_csr {
- u8 MAC[6];
- u8 reserved_0[2];
- __le32 MAR[2];
- u8 RX_FIFO_COUNT;
- u8 reserved_1;
- u8 TX_FIFO_COUNT;
- u8 BQREQ;
- u8 reserved_2[4];
- __le32 TSFT[2];
- __le32 TLPDA;
- __le32 TNPDA;
- __le32 THPDA;
- __le16 BRSR;
- u8 BSSID[6];
- u8 RESP_RATE;
- u8 EIFS;
- u8 reserved_3[1];
- u8 CMD;
-#define RTL818X_CMD_TX_ENABLE (1 << 2)
-#define RTL818X_CMD_RX_ENABLE (1 << 3)
-#define RTL818X_CMD_RESET (1 << 4)
- u8 reserved_4[4];
- __le16 INT_MASK;
- __le16 INT_STATUS;
-#define RTL818X_INT_RX_OK (1 << 0)
-#define RTL818X_INT_RX_ERR (1 << 1)
-#define RTL818X_INT_TXL_OK (1 << 2)
-#define RTL818X_INT_TXL_ERR (1 << 3)
-#define RTL818X_INT_RX_DU (1 << 4)
-#define RTL818X_INT_RX_FO (1 << 5)
-#define RTL818X_INT_TXN_OK (1 << 6)
-#define RTL818X_INT_TXN_ERR (1 << 7)
-#define RTL818X_INT_TXH_OK (1 << 8)
-#define RTL818X_INT_TXH_ERR (1 << 9)
-#define RTL818X_INT_TXB_OK (1 << 10)
-#define RTL818X_INT_TXB_ERR (1 << 11)
-#define RTL818X_INT_ATIM (1 << 12)
-#define RTL818X_INT_BEACON (1 << 13)
-#define RTL818X_INT_TIME_OUT (1 << 14)
-#define RTL818X_INT_TX_FO (1 << 15)
- __le32 TX_CONF;
-#define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17)
-#define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17)
-#define RTL818X_TX_CONF_NO_ICV (1 << 19)
-#define RTL818X_TX_CONF_DISCW (1 << 20)
-#define RTL818X_TX_CONF_SAT_HWPLCP (1 << 24)
-#define RTL818X_TX_CONF_R8180_ABCD (2 << 25)
-#define RTL818X_TX_CONF_R8180_F (3 << 25)
-#define RTL818X_TX_CONF_R8185_ABC (4 << 25)
-#define RTL818X_TX_CONF_R8185_D (5 << 25)
-#define RTL818X_TX_CONF_R8187vD (5 << 25)
-#define RTL818X_TX_CONF_R8187vD_B (6 << 25)
-#define RTL818X_TX_CONF_HWVER_MASK (7 << 25)
-#define RTL818X_TX_CONF_DISREQQSIZE (1 << 28)
-#define RTL818X_TX_CONF_PROBE_DTS (1 << 29)
-#define RTL818X_TX_CONF_HW_SEQNUM (1 << 30)
-#define RTL818X_TX_CONF_CW_MIN (1 << 31)
- __le32 RX_CONF;
-#define RTL818X_RX_CONF_MONITOR (1 << 0)
-#define RTL818X_RX_CONF_NICMAC (1 << 1)
-#define RTL818X_RX_CONF_MULTICAST (1 << 2)
-#define RTL818X_RX_CONF_BROADCAST (1 << 3)
-#define RTL818X_RX_CONF_FCS (1 << 5)
-#define RTL818X_RX_CONF_DATA (1 << 18)
-#define RTL818X_RX_CONF_CTRL (1 << 19)
-#define RTL818X_RX_CONF_MGMT (1 << 20)
-#define RTL818X_RX_CONF_ADDR3 (1 << 21)
-#define RTL818X_RX_CONF_PM (1 << 22)
-#define RTL818X_RX_CONF_BSSID (1 << 23)
-#define RTL818X_RX_CONF_RX_AUTORESETPHY (1 << 28)
-#define RTL818X_RX_CONF_CSDM1 (1 << 29)
-#define RTL818X_RX_CONF_CSDM2 (1 << 30)
-#define RTL818X_RX_CONF_ONLYERLPKT (1 << 31)
- __le32 INT_TIMEOUT;
- __le32 TBDA;
- u8 EEPROM_CMD;
-#define RTL818X_EEPROM_CMD_READ (1 << 0)
-#define RTL818X_EEPROM_CMD_WRITE (1 << 1)
-#define RTL818X_EEPROM_CMD_CK (1 << 2)
-#define RTL818X_EEPROM_CMD_CS (1 << 3)
-#define RTL818X_EEPROM_CMD_NORMAL (0 << 6)
-#define RTL818X_EEPROM_CMD_LOAD (1 << 6)
-#define RTL818X_EEPROM_CMD_PROGRAM (2 << 6)
-#define RTL818X_EEPROM_CMD_CONFIG (3 << 6)
- u8 CONFIG0;
- u8 CONFIG1;
- u8 CONFIG2;
-#define RTL818X_CONFIG2_ANTENNA_DIV (1 << 6)
- __le32 ANAPARAM;
- u8 MSR;
-#define RTL818X_MSR_NO_LINK (0 << 2)
-#define RTL818X_MSR_ADHOC (1 << 2)
-#define RTL818X_MSR_INFRA (2 << 2)
-#define RTL818X_MSR_MASTER (3 << 2)
-#define RTL818X_MSR_ENEDCA (4 << 2)
- u8 CONFIG3;
-#define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6)
-#define RTL818X_CONFIG3_GNT_SELECT (1 << 7)
- u8 CONFIG4;
-#define RTL818X_CONFIG4_POWEROFF (1 << 6)
-#define RTL818X_CONFIG4_VCOOFF (1 << 7)
- u8 TESTR;
- u8 reserved_9[2];
- u8 PGSELECT;
- u8 SECURITY;
- __le32 ANAPARAM2;
- u8 reserved_10[12];
- __le16 BEACON_INTERVAL;
- __le16 ATIM_WND;
- __le16 BEACON_INTERVAL_TIME;
- __le16 ATIMTR_INTERVAL;
- u8 PHY_DELAY;
- u8 CARRIER_SENSE_COUNTER;
- u8 reserved_11[2];
- u8 PHY[4];
- __le16 RFPinsOutput;
- __le16 RFPinsEnable;
- __le16 RFPinsSelect;
- __le16 RFPinsInput;
- __le32 RF_PARA;
- __le32 RF_TIMING;
- u8 GP_ENABLE;
- u8 GPIO;
- u8 reserved_12[2];
- __le32 HSSI_PARA;
- u8 reserved_13[4];
- u8 TX_AGC_CTL;
-#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0)
-#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1)
-#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2)
- u8 TX_GAIN_CCK;
- u8 TX_GAIN_OFDM;
- u8 TX_ANTENNA;
- u8 reserved_14[16];
- u8 WPA_CONF;
- u8 reserved_15[3];
- u8 SIFS;
- u8 DIFS;
- u8 SLOT;
- u8 reserved_16[5];
- u8 CW_CONF;
-#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0)
-#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1)
- u8 CW_VAL;
- u8 RATE_FALLBACK;
-#define RTL818X_RATE_FALLBACK_ENABLE (1 << 7)
- u8 ACM_CONTROL;
- u8 reserved_17[24];
- u8 CONFIG5;
- u8 TX_DMA_POLLING;
- u8 reserved_18[2];
- __le16 CWR;
- u8 RETRY_CTR;
- u8 reserved_19[3];
- __le16 INT_MIG;
-/* RTL818X_R8187B_*: magic numbers from ioregisters */
-#define RTL818X_R8187B_B 0
-#define RTL818X_R8187B_D 1
-#define RTL818X_R8187B_E 2
- __le32 RDSAR;
- __le16 TID_AC_MAP;
- u8 reserved_20[4];
- u8 ANAPARAM3;
- u8 reserved_21[5];
- __le16 FEMR;
- u8 reserved_22[4];
- __le16 TALLY_CNT;
- u8 TALLY_SEL;
-} __attribute__((packed));
-
-struct rtl818x_rf_ops {
- char *name;
- void (*init)(struct ieee80211_hw *);
- void (*stop)(struct ieee80211_hw *);
- void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *);
-};
-
-/* Tx/Rx flags are common between RTL818X chips */
-
-enum rtl818x_tx_desc_flags {
- RTL818X_TX_DESC_FLAG_NO_ENC = (1 << 15),
- RTL818X_TX_DESC_FLAG_TX_OK = (1 << 15),
- RTL818X_TX_DESC_FLAG_SPLCP = (1 << 16),
- RTL818X_TX_DESC_FLAG_RX_UNDER = (1 << 16),
- RTL818X_TX_DESC_FLAG_MOREFRAG = (1 << 17),
- RTL818X_TX_DESC_FLAG_CTS = (1 << 18),
- RTL818X_TX_DESC_FLAG_RTS = (1 << 23),
- RTL818X_TX_DESC_FLAG_LS = (1 << 28),
- RTL818X_TX_DESC_FLAG_FS = (1 << 29),
- RTL818X_TX_DESC_FLAG_DMA = (1 << 30),
- RTL818X_TX_DESC_FLAG_OWN = (1 << 31)
-};
-
-enum rtl818x_rx_desc_flags {
- RTL818X_RX_DESC_FLAG_ICV_ERR = (1 << 12),
- RTL818X_RX_DESC_FLAG_CRC32_ERR = (1 << 13),
- RTL818X_RX_DESC_FLAG_PM = (1 << 14),
- RTL818X_RX_DESC_FLAG_RX_ERR = (1 << 15),
- RTL818X_RX_DESC_FLAG_BCAST = (1 << 16),
- RTL818X_RX_DESC_FLAG_PAM = (1 << 17),
- RTL818X_RX_DESC_FLAG_MCAST = (1 << 18),
- RTL818X_RX_DESC_FLAG_QOS = (1 << 19), /* RTL8187(B) only */
- RTL818X_RX_DESC_FLAG_TRSW = (1 << 24), /* RTL8187(B) only */
- RTL818X_RX_DESC_FLAG_SPLCP = (1 << 25),
- RTL818X_RX_DESC_FLAG_FOF = (1 << 26),
- RTL818X_RX_DESC_FLAG_DMA_FAIL = (1 << 27),
- RTL818X_RX_DESC_FLAG_LS = (1 << 28),
- RTL818X_RX_DESC_FLAG_FS = (1 << 29),
- RTL818X_RX_DESC_FLAG_EOR = (1 << 30),
- RTL818X_RX_DESC_FLAG_OWN = (1 << 31)
-};
-
-#endif /* RTL818X_H */
diff --git a/drivers/net/wireless/rtl818x/Makefile b/drivers/net/wireless/rtl818x/Makefile
new file mode 100644
index 000000000000..c113b3e69046
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/Makefile
@@ -0,0 +1,7 @@
+rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o
+rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o
+
+obj-$(CONFIG_RTL8180) += rtl8180.o
+obj-$(CONFIG_RTL8187) += rtl8187.o
+
+
diff --git a/drivers/net/wireless/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180.h
index 8721282a8185..8721282a8185 100644
--- a/drivers/net/wireless/rtl8180.h
+++ b/drivers/net/wireless/rtl818x/rtl8180.h
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
new file mode 100644
index 000000000000..5f887fb137a9
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -0,0 +1,1093 @@
+
+/*
+ * Linux device driver for RTL8180 / RTL8185
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * Thanks to Realtek for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/eeprom_93cx6.h>
+#include <net/mac80211.h>
+
+#include "rtl8180.h"
+#include "rtl8180_rtl8225.h"
+#include "rtl8180_sa2400.h"
+#include "rtl8180_max2820.h"
+#include "rtl8180_grf5101.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver");
+MODULE_LICENSE("GPL");
+
+static struct pci_device_id rtl8180_table[] __devinitdata = {
+ /* rtl8185 */
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) },
+
+ /* rtl8180 */
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) },
+ { PCI_DEVICE(0x1799, 0x6001) },
+ { PCI_DEVICE(0x1799, 0x6020) },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, rtl8180_table);
+
+static const struct ieee80211_rate rtl818x_rates[] = {
+ { .bitrate = 10, .hw_value = 0, },
+ { .bitrate = 20, .hw_value = 1, },
+ { .bitrate = 55, .hw_value = 2, },
+ { .bitrate = 110, .hw_value = 3, },
+ { .bitrate = 60, .hw_value = 4, },
+ { .bitrate = 90, .hw_value = 5, },
+ { .bitrate = 120, .hw_value = 6, },
+ { .bitrate = 180, .hw_value = 7, },
+ { .bitrate = 240, .hw_value = 8, },
+ { .bitrate = 360, .hw_value = 9, },
+ { .bitrate = 480, .hw_value = 10, },
+ { .bitrate = 540, .hw_value = 11, },
+};
+
+static const struct ieee80211_channel rtl818x_channels[] = {
+ { .center_freq = 2412 },
+ { .center_freq = 2417 },
+ { .center_freq = 2422 },
+ { .center_freq = 2427 },
+ { .center_freq = 2432 },
+ { .center_freq = 2437 },
+ { .center_freq = 2442 },
+ { .center_freq = 2447 },
+ { .center_freq = 2452 },
+ { .center_freq = 2457 },
+ { .center_freq = 2462 },
+ { .center_freq = 2467 },
+ { .center_freq = 2472 },
+ { .center_freq = 2484 },
+};
+
+
+
+
+void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i = 10;
+ u32 buf;
+
+ buf = (data << 8) | addr;
+
+ rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80);
+ while (i--) {
+ rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf);
+ if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF))
+ return;
+ }
+}
+
+static void rtl8180_handle_rx(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ unsigned int count = 32;
+
+ while (count--) {
+ struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
+ struct sk_buff *skb = priv->rx_buf[priv->rx_idx];
+ u32 flags = le32_to_cpu(entry->flags);
+
+ if (flags & RTL818X_RX_DESC_FLAG_OWN)
+ return;
+
+ if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL |
+ RTL818X_RX_DESC_FLAG_FOF |
+ RTL818X_RX_DESC_FLAG_RX_ERR)))
+ goto done;
+ else {
+ u32 flags2 = le32_to_cpu(entry->flags2);
+ struct ieee80211_rx_status rx_status = {0};
+ struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE);
+
+ if (unlikely(!new_skb))
+ goto done;
+
+ pci_unmap_single(priv->pdev,
+ *((dma_addr_t *)skb->cb),
+ MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+ skb_put(skb, flags & 0xFFF);
+
+ rx_status.antenna = (flags2 >> 15) & 1;
+ /* TODO: improve signal/rssi reporting */
+ rx_status.qual = flags2 & 0xFF;
+ rx_status.signal = (flags2 >> 8) & 0x7F;
+ /* XXX: is this correct? */
+ rx_status.rate_idx = (flags >> 20) & 0xF;
+ rx_status.freq = dev->conf.channel->center_freq;
+ rx_status.band = dev->conf.channel->band;
+ rx_status.mactime = le64_to_cpu(entry->tsft);
+ rx_status.flag |= RX_FLAG_TSFT;
+ if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
+ rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ ieee80211_rx_irqsafe(dev, skb, &rx_status);
+
+ skb = new_skb;
+ priv->rx_buf[priv->rx_idx] = skb;
+ *((dma_addr_t *) skb->cb) =
+ pci_map_single(priv->pdev, skb_tail_pointer(skb),
+ MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+ }
+
+ done:
+ entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb));
+ entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
+ MAX_RX_SIZE);
+ if (priv->rx_idx == 31)
+ entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
+ priv->rx_idx = (priv->rx_idx + 1) % 32;
+ }
+}
+
+static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_tx_ring *ring = &priv->tx_ring[prio];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct rtl8180_tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ u32 flags = le32_to_cpu(entry->flags);
+
+ if (flags & RTL818X_TX_DESC_FLAG_OWN)
+ return;
+
+ ring->idx = (ring->idx + 1) % ring->entries;
+ skb = __skb_dequeue(&ring->queue);
+ pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf),
+ skb->len, PCI_DMA_TODEVICE);
+
+ info = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(info);
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (flags & RTL818X_TX_DESC_FLAG_TX_OK))
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ info->status.rates[0].count = (flags & 0xFF) + 1;
+
+ ieee80211_tx_status_irqsafe(dev, skb);
+ if (ring->entries - skb_queue_len(&ring->queue) == 2)
+ ieee80211_wake_queue(dev, prio);
+ }
+}
+
+static irqreturn_t rtl8180_interrupt(int irq, void *dev_id)
+{
+ struct ieee80211_hw *dev = dev_id;
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg;
+
+ spin_lock(&priv->lock);
+ reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS);
+ if (unlikely(reg == 0xFFFF)) {
+ spin_unlock(&priv->lock);
+ return IRQ_HANDLED;
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg);
+
+ if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR))
+ rtl8180_handle_tx(dev, 3);
+
+ if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR))
+ rtl8180_handle_tx(dev, 2);
+
+ if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR))
+ rtl8180_handle_tx(dev, 1);
+
+ if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR))
+ rtl8180_handle_tx(dev, 0);
+
+ if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR))
+ rtl8180_handle_rx(dev);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_tx_ring *ring;
+ struct rtl8180_tx_desc *entry;
+ unsigned long flags;
+ unsigned int idx, prio;
+ dma_addr_t mapping;
+ u32 tx_flags;
+ u8 rc_flags;
+ u16 plcp_len = 0;
+ __le16 rts_duration = 0;
+
+ prio = skb_get_queue_mapping(skb);
+ ring = &priv->tx_ring[prio];
+
+ mapping = pci_map_single(priv->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+
+ tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
+ RTL818X_TX_DESC_FLAG_LS |
+ (ieee80211_get_tx_rate(dev, info)->hw_value << 24) |
+ skb->len;
+
+ if (priv->r8185)
+ tx_flags |= RTL818X_TX_DESC_FLAG_DMA |
+ RTL818X_TX_DESC_FLAG_NO_ENC;
+
+ rc_flags = info->control.rates[0].flags;
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ tx_flags |= RTL818X_TX_DESC_FLAG_RTS;
+ tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+ } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ tx_flags |= RTL818X_TX_DESC_FLAG_CTS;
+ tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+ }
+
+ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len,
+ info);
+
+ if (!priv->r8185) {
+ unsigned int remainder;
+
+ plcp_len = DIV_ROUND_UP(16 * (skb->len + 4),
+ (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10);
+ remainder = (16 * (skb->len + 4)) %
+ ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10);
+ if (remainder > 0 && remainder <= 6)
+ plcp_len |= 1 << 15;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
+ entry = &ring->desc[idx];
+
+ entry->rts_duration = rts_duration;
+ entry->plcp_len = cpu_to_le16(plcp_len);
+ entry->tx_buf = cpu_to_le32(mapping);
+ entry->frame_len = cpu_to_le32(skb->len);
+ entry->flags2 = info->control.rates[1].idx >= 0 ?
+ ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0;
+ entry->retry_limit = info->control.rates[0].count;
+ entry->flags = cpu_to_le32(tx_flags);
+ __skb_queue_tail(&ring->queue, skb);
+ if (ring->entries - skb_queue_len(&ring->queue) < 2)
+ ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
+
+ return 0;
+}
+
+void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam)
+{
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static int rtl8180_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->CMD, 0);
+ rtl818x_ioread8(priv, &priv->map->CMD);
+ msleep(10);
+
+ /* reset */
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+ rtl818x_ioread8(priv, &priv->map->CMD);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg &= (1 << 1);
+ reg |= RTL818X_CMD_RESET;
+ rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET);
+ rtl818x_ioread8(priv, &priv->map->CMD);
+ msleep(200);
+
+ /* check success of reset */
+ if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) {
+ printk(KERN_ERR "%s: reset timeout!\n", wiphy_name(dev->wiphy));
+ return -ETIMEDOUT;
+ }
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD);
+ rtl818x_ioread8(priv, &priv->map->CMD);
+ msleep(200);
+
+ if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) {
+ /* For cardbus */
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= 1 << 1;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ reg = rtl818x_ioread16(priv, &priv->map->FEMR);
+ reg |= (1 << 15) | (1 << 14) | (1 << 4);
+ rtl818x_iowrite16(priv, &priv->map->FEMR, reg);
+ }
+
+ rtl818x_iowrite8(priv, &priv->map->MSR, 0);
+
+ if (!priv->r8185)
+ rtl8180_set_anaparam(priv, priv->anaparam);
+
+ rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
+ rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
+ rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
+ rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
+ rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
+
+ /* TODO: necessary? specs indicate not */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3));
+ if (priv->r8185) {
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG2);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4));
+ }
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */
+
+ /* TODO: turn off hw wep on rtl8180 */
+
+ rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
+
+ if (priv->r8185) {
+ rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+ rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+ rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
+
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+
+ /* TODO: set ClkRun enable? necessary? */
+ reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE);
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6));
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+ } else {
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1);
+ rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
+
+ rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6);
+ rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C);
+ }
+
+ priv->rf->init(dev);
+ if (priv->r8185)
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+ return 0;
+}
+
+static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_rx_desc *entry;
+ int i;
+
+ priv->rx_ring = pci_alloc_consistent(priv->pdev,
+ sizeof(*priv->rx_ring) * 32,
+ &priv->rx_ring_dma);
+
+ if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) {
+ printk(KERN_ERR "%s: Cannot allocate RX ring\n",
+ wiphy_name(dev->wiphy));
+ return -ENOMEM;
+ }
+
+ memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32);
+ priv->rx_idx = 0;
+
+ for (i = 0; i < 32; i++) {
+ struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE);
+ dma_addr_t *mapping;
+ entry = &priv->rx_ring[i];
+ if (!skb)
+ return 0;
+
+ priv->rx_buf[i] = skb;
+ mapping = (dma_addr_t *)skb->cb;
+ *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb),
+ MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+ entry->rx_buf = cpu_to_le32(*mapping);
+ entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN |
+ MAX_RX_SIZE);
+ }
+ entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR);
+ return 0;
+}
+
+static void rtl8180_free_rx_ring(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ struct sk_buff *skb = priv->rx_buf[i];
+ if (!skb)
+ continue;
+
+ pci_unmap_single(priv->pdev,
+ *((dma_addr_t *)skb->cb),
+ MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+ kfree_skb(skb);
+ }
+
+ pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32,
+ priv->rx_ring, priv->rx_ring_dma);
+ priv->rx_ring = NULL;
+}
+
+static int rtl8180_init_tx_ring(struct ieee80211_hw *dev,
+ unsigned int prio, unsigned int entries)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_tx_desc *ring;
+ dma_addr_t dma;
+ int i;
+
+ ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma);
+ if (!ring || (unsigned long)ring & 0xFF) {
+ printk(KERN_ERR "%s: Cannot allocate TX ring (prio = %d)\n",
+ wiphy_name(dev->wiphy), prio);
+ return -ENOMEM;
+ }
+
+ memset(ring, 0, sizeof(*ring)*entries);
+ priv->tx_ring[prio].desc = ring;
+ priv->tx_ring[prio].dma = dma;
+ priv->tx_ring[prio].idx = 0;
+ priv->tx_ring[prio].entries = entries;
+ skb_queue_head_init(&priv->tx_ring[prio].queue);
+
+ for (i = 0; i < entries; i++)
+ ring[i].next_tx_desc =
+ cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring));
+
+ return 0;
+}
+
+static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ struct rtl8180_tx_ring *ring = &priv->tx_ring[prio];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct rtl8180_tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb = __skb_dequeue(&ring->queue);
+
+ pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf),
+ skb->len, PCI_DMA_TODEVICE);
+ kfree_skb(skb);
+ ring->idx = (ring->idx + 1) % ring->entries;
+ }
+
+ pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries,
+ ring->desc, ring->dma);
+ ring->desc = NULL;
+}
+
+static int rtl8180_start(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int ret, i;
+ u32 reg;
+
+ ret = rtl8180_init_rx_ring(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 4; i++)
+ if ((ret = rtl8180_init_tx_ring(dev, i, 16)))
+ goto err_free_rings;
+
+ ret = rtl8180_init_hw(dev);
+ if (ret)
+ goto err_free_rings;
+
+ rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma);
+ rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma);
+ rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma);
+ rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
+ rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
+
+ ret = request_irq(priv->pdev->irq, &rtl8180_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register IRQ handler\n",
+ wiphy_name(dev->wiphy));
+ goto err_free_rings;
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+
+ rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
+ rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
+
+ reg = RTL818X_RX_CONF_ONLYERLPKT |
+ RTL818X_RX_CONF_RX_AUTORESETPHY |
+ RTL818X_RX_CONF_MGMT |
+ RTL818X_RX_CONF_DATA |
+ (7 << 8 /* MAX RX DMA */) |
+ RTL818X_RX_CONF_BROADCAST |
+ RTL818X_RX_CONF_NICMAC;
+
+ if (priv->r8185)
+ reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2;
+ else {
+ reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1)
+ ? RTL818X_RX_CONF_CSDM1 : 0;
+ reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2)
+ ? RTL818X_RX_CONF_CSDM2 : 0;
+ }
+
+ priv->rx_conf = reg;
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+ if (priv->r8185) {
+ reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+ reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+
+ reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
+ rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
+
+ /* disable early TX */
+ rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f);
+ }
+
+ reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ reg |= (6 << 21 /* MAX TX DMA */) |
+ RTL818X_TX_CONF_NO_ICV;
+
+ if (priv->r8185)
+ reg &= ~RTL818X_TX_CONF_PROBE_DTS;
+ else
+ reg &= ~RTL818X_TX_CONF_HW_SEQNUM;
+
+ /* different meaning, same value on both rtl8185 and rtl8180 */
+ reg &= ~RTL818X_TX_CONF_SAT_HWPLCP;
+
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg |= RTL818X_CMD_RX_ENABLE;
+ reg |= RTL818X_CMD_TX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ return 0;
+
+ err_free_rings:
+ rtl8180_free_rx_ring(dev);
+ for (i = 0; i < 4; i++)
+ if (priv->tx_ring[i].desc)
+ rtl8180_free_tx_ring(dev, i);
+
+ return ret;
+}
+
+static void rtl8180_stop(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 reg;
+ int i;
+
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg &= ~RTL818X_CMD_TX_ENABLE;
+ reg &= ~RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+ priv->rf->stop(dev);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG4);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ free_irq(priv->pdev->irq, dev);
+
+ rtl8180_free_rx_ring(dev);
+ for (i = 0; i < 4; i++)
+ rtl8180_free_tx_ring(dev, i);
+}
+
+static int rtl8180_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR)
+ return -EOPNOTSUPP;
+
+ switch (conf->type) {
+ case NL80211_IFTYPE_STATION:
+ priv->mode = conf->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = conf->vif;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0],
+ le32_to_cpu(*(__le32 *)conf->mac_addr));
+ rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4],
+ le16_to_cpu(*(__le16 *)(conf->mac_addr + 4)));
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ return 0;
+}
+
+static void rtl8180_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->vif = NULL;
+}
+
+static int rtl8180_config(struct ieee80211_hw *dev, u32 changed)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ priv->rf->set_chan(dev, conf);
+
+ return 0;
+}
+
+static int rtl8180_config_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);
+
+ if (is_valid_ether_addr(conf->bssid))
+ rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA);
+ else
+ rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK);
+
+ return 0;
+}
+
+static void rtl8180_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp)
+ priv->rf->conf_erp(dev, info);
+}
+
+static void rtl8180_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ int mc_count, struct dev_addr_list *mclist)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (changed_flags & FIF_FCSFAIL)
+ priv->rx_conf ^= RTL818X_RX_CONF_FCS;
+ if (changed_flags & FIF_CONTROL)
+ priv->rx_conf ^= RTL818X_RX_CONF_CTRL;
+ if (changed_flags & FIF_OTHER_BSS)
+ priv->rx_conf ^= RTL818X_RX_CONF_MONITOR;
+ if (*total_flags & FIF_ALLMULTI || mc_count > 0)
+ priv->rx_conf |= RTL818X_RX_CONF_MULTICAST;
+ else
+ priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST;
+
+ *total_flags = 0;
+
+ if (priv->rx_conf & RTL818X_RX_CONF_FCS)
+ *total_flags |= FIF_FCSFAIL;
+ if (priv->rx_conf & RTL818X_RX_CONF_CTRL)
+ *total_flags |= FIF_CONTROL;
+ if (priv->rx_conf & RTL818X_RX_CONF_MONITOR)
+ *total_flags |= FIF_OTHER_BSS;
+ if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST)
+ *total_flags |= FIF_ALLMULTI;
+
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf);
+}
+
+static const struct ieee80211_ops rtl8180_ops = {
+ .tx = rtl8180_tx,
+ .start = rtl8180_start,
+ .stop = rtl8180_stop,
+ .add_interface = rtl8180_add_interface,
+ .remove_interface = rtl8180_remove_interface,
+ .config = rtl8180_config,
+ .config_interface = rtl8180_config_interface,
+ .bss_info_changed = rtl8180_bss_info_changed,
+ .configure_filter = rtl8180_configure_filter,
+};
+
+static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+ struct ieee80211_hw *dev = eeprom->data;
+ struct rtl8180_priv *priv = dev->priv;
+ u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+
+ eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE;
+ eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ;
+ eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK;
+ eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS;
+}
+
+static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+ struct ieee80211_hw *dev = eeprom->data;
+ struct rtl8180_priv *priv = dev->priv;
+ u8 reg = 2 << 6;
+
+ if (eeprom->reg_data_in)
+ reg |= RTL818X_EEPROM_CMD_WRITE;
+ if (eeprom->reg_data_out)
+ reg |= RTL818X_EEPROM_CMD_READ;
+ if (eeprom->reg_data_clock)
+ reg |= RTL818X_EEPROM_CMD_CK;
+ if (eeprom->reg_chip_select)
+ reg |= RTL818X_EEPROM_CMD_CS;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+}
+
+static int __devinit rtl8180_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct ieee80211_hw *dev;
+ struct rtl8180_priv *priv;
+ unsigned long mem_addr, mem_len;
+ unsigned int io_addr, io_len;
+ int err, i;
+ struct eeprom_93cx6 eeprom;
+ const char *chip_name, *rf_name = NULL;
+ u32 reg;
+ u16 eeprom_val;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n",
+ pci_name(pdev));
+ return err;
+ }
+
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (err) {
+ printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n",
+ pci_name(pdev));
+ return err;
+ }
+
+ io_addr = pci_resource_start(pdev, 0);
+ io_len = pci_resource_len(pdev, 0);
+ mem_addr = pci_resource_start(pdev, 1);
+ mem_len = pci_resource_len(pdev, 1);
+
+ if (mem_len < sizeof(struct rtl818x_csr) ||
+ io_len < sizeof(struct rtl818x_csr)) {
+ printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto err_free_reg;
+ }
+
+ if ((err = pci_set_dma_mask(pdev, 0xFFFFFF00ULL)) ||
+ (err = pci_set_consistent_dma_mask(pdev, 0xFFFFFF00ULL))) {
+ printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n",
+ pci_name(pdev));
+ goto err_free_reg;
+ }
+
+ pci_set_master(pdev);
+
+ dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops);
+ if (!dev) {
+ printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto err_free_reg;
+ }
+
+ priv = dev->priv;
+ priv->pdev = pdev;
+
+ dev->max_rates = 2;
+ SET_IEEE80211_DEV(dev, &pdev->dev);
+ pci_set_drvdata(pdev, dev);
+
+ priv->map = pci_iomap(pdev, 1, mem_len);
+ if (!priv->map)
+ priv->map = pci_iomap(pdev, 0, io_len);
+
+ if (!priv->map) {
+ printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n",
+ pci_name(pdev));
+ goto err_free_dev;
+ }
+
+ BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels));
+ BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates));
+
+ memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels));
+ memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates));
+
+ priv->band.band = IEEE80211_BAND_2GHZ;
+ priv->band.channels = priv->channels;
+ priv->band.n_channels = ARRAY_SIZE(rtl818x_channels);
+ priv->band.bitrates = priv->rates;
+ priv->band.n_bitrates = 4;
+ dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+
+ dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_SIGNAL_UNSPEC;
+ dev->queues = 1;
+ dev->max_signal = 65;
+
+ reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ reg &= RTL818X_TX_CONF_HWVER_MASK;
+ switch (reg) {
+ case RTL818X_TX_CONF_R8180_ABCD:
+ chip_name = "RTL8180";
+ break;
+ case RTL818X_TX_CONF_R8180_F:
+ chip_name = "RTL8180vF";
+ break;
+ case RTL818X_TX_CONF_R8185_ABC:
+ chip_name = "RTL8185";
+ break;
+ case RTL818X_TX_CONF_R8185_D:
+ chip_name = "RTL8185vD";
+ break;
+ default:
+ printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n",
+ pci_name(pdev), reg >> 25);
+ goto err_iounmap;
+ }
+
+ priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC;
+ if (priv->r8185) {
+ priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates);
+ pci_try_set_mwi(pdev);
+ }
+
+ eeprom.data = dev;
+ eeprom.register_read = rtl8180_eeprom_register_read;
+ eeprom.register_write = rtl8180_eeprom_register_write;
+ if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
+ eeprom.width = PCI_EEPROM_WIDTH_93C66;
+ else
+ eeprom.width = PCI_EEPROM_WIDTH_93C46;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+
+ eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val);
+ eeprom_val &= 0xFF;
+ switch (eeprom_val) {
+ case 1: rf_name = "Intersil";
+ break;
+ case 2: rf_name = "RFMD";
+ break;
+ case 3: priv->rf = &sa2400_rf_ops;
+ break;
+ case 4: priv->rf = &max2820_rf_ops;
+ break;
+ case 5: priv->rf = &grf5101_rf_ops;
+ break;
+ case 9: priv->rf = rtl8180_detect_rf(dev);
+ break;
+ case 10:
+ rf_name = "RTL8255";
+ break;
+ default:
+ printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n",
+ pci_name(pdev), eeprom_val);
+ goto err_iounmap;
+ }
+
+ if (!priv->rf) {
+ printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n",
+ pci_name(pdev), rf_name);
+ goto err_iounmap;
+ }
+
+ eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val);
+ priv->csthreshold = eeprom_val >> 8;
+ if (!priv->r8185) {
+ __le32 anaparam;
+ eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2);
+ priv->anaparam = le32_to_cpu(anaparam);
+ eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam);
+ }
+
+ eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)dev->wiphy->perm_addr, 3);
+ if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
+ printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using"
+ " randomly generated MAC addr\n", pci_name(pdev));
+ random_ether_addr(dev->wiphy->perm_addr);
+ }
+
+ /* CCK TX power */
+ for (i = 0; i < 14; i += 2) {
+ u16 txpwr;
+ eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr);
+ priv->channels[i].hw_value = txpwr & 0xFF;
+ priv->channels[i + 1].hw_value = txpwr >> 8;
+ }
+
+ /* OFDM TX power */
+ if (priv->r8185) {
+ for (i = 0; i < 14; i += 2) {
+ u16 txpwr;
+ eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr);
+ priv->channels[i].hw_value |= (txpwr & 0xFF) << 8;
+ priv->channels[i + 1].hw_value |= txpwr & 0xFF00;
+ }
+ }
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ spin_lock_init(&priv->lock);
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ printk(KERN_ERR "%s (rtl8180): Cannot register device\n",
+ pci_name(pdev));
+ goto err_iounmap;
+ }
+
+ printk(KERN_INFO "%s: hwaddr %pM, %s + %s\n",
+ wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
+ chip_name, priv->rf->name);
+
+ return 0;
+
+ err_iounmap:
+ iounmap(priv->map);
+
+ err_free_dev:
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(dev);
+
+ err_free_reg:
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ return err;
+}
+
+static void __devexit rtl8180_remove(struct pci_dev *pdev)
+{
+ struct ieee80211_hw *dev = pci_get_drvdata(pdev);
+ struct rtl8180_priv *priv;
+
+ if (!dev)
+ return;
+
+ ieee80211_unregister_hw(dev);
+
+ priv = dev->priv;
+
+ pci_iounmap(pdev, priv->map);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ ieee80211_free_hw(dev);
+}
+
+#ifdef CONFIG_PM
+static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int rtl8180_resume(struct pci_dev *pdev)
+{
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver rtl8180_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtl8180_table,
+ .probe = rtl8180_probe,
+ .remove = __devexit_p(rtl8180_remove),
+#ifdef CONFIG_PM
+ .suspend = rtl8180_suspend,
+ .resume = rtl8180_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init rtl8180_init(void)
+{
+ return pci_register_driver(&rtl8180_driver);
+}
+
+static void __exit rtl8180_exit(void)
+{
+ pci_unregister_driver(&rtl8180_driver);
+}
+
+module_init(rtl8180_init);
+module_exit(rtl8180_exit);
diff --git a/drivers/net/wireless/rtl8180_grf5101.c b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c
index 947ee55f18b2..947ee55f18b2 100644
--- a/drivers/net/wireless/rtl8180_grf5101.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c
diff --git a/drivers/net/wireless/rtl8180_grf5101.h b/drivers/net/wireless/rtl818x/rtl8180_grf5101.h
index 76647111bcff..76647111bcff 100644
--- a/drivers/net/wireless/rtl8180_grf5101.h
+++ b/drivers/net/wireless/rtl818x/rtl8180_grf5101.h
diff --git a/drivers/net/wireless/rtl8180_max2820.c b/drivers/net/wireless/rtl818x/rtl8180_max2820.c
index 6c825fd7f3b6..6c825fd7f3b6 100644
--- a/drivers/net/wireless/rtl8180_max2820.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_max2820.c
diff --git a/drivers/net/wireless/rtl8180_max2820.h b/drivers/net/wireless/rtl818x/rtl8180_max2820.h
index 61cf6d1e7d57..61cf6d1e7d57 100644
--- a/drivers/net/wireless/rtl8180_max2820.h
+++ b/drivers/net/wireless/rtl818x/rtl8180_max2820.h
diff --git a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c
new file mode 100644
index 000000000000..4d2be0d9672b
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c
@@ -0,0 +1,788 @@
+
+/*
+ * Radio tuning for RTL8225 on RTL8180
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * Thanks to Realtek for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <net/mac80211.h>
+
+#include "rtl8180.h"
+#include "rtl8180_rtl8225.h"
+
+static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg80, reg84, reg82;
+ u32 bangdata;
+ int i;
+
+ bangdata = (data << 4) | (addr & 0xf);
+
+ reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
+ reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
+
+ reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+
+ for (i = 15; i >= 0; i--) {
+ u16 reg = reg80 | !!(bangdata & (1 << i));
+
+ if (i & 1)
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+
+ if (!(i & 1))
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+}
+
+static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg80, reg82, reg84, out;
+ int i;
+
+ reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
+ reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+ reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400;
+
+ reg80 &= ~0xF;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(4);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(5);
+
+ for (i = 4; i >= 0; i--) {
+ u16 reg = reg80 | ((addr >> i) & 1);
+
+ if (!(i & 1)) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(1);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+
+ if (i & 1) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(1);
+ }
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+
+ out = 0;
+ for (i = 11; i >= 0; i--) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(1);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+
+ if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
+ out |= 1 << i;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 2));
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ udelay(2);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
+
+ return out;
+}
+
+static const u16 rtl8225bcd_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
+ 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
+};
+
+static const u8 rtl8225_agc[] = {
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
+ 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
+ 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
+ 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
+ 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
+ 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
+ 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
+ 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
+ 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
+ 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
+ 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+static const u8 rtl8225_gain[] = {
+ 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
+ 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
+ 0x33, 0x80, 0x79, 0xc5, /* -78dbm */
+ 0x43, 0x78, 0x76, 0xc5, /* -74dbm */
+ 0x53, 0x60, 0x73, 0xc5, /* -70dbm */
+ 0x63, 0x58, 0x70, 0xc5, /* -66dbm */
+};
+
+static const u8 rtl8225_threshold[] = {
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
+};
+
+static const u8 rtl8225_tx_gain_cck_ofdm[] = {
+ 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static const u8 rtl8225_tx_power_cck[] = {
+ 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+ 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+ 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+ 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static const u8 rtl8225_tx_power_cck_ch14[] = {
+ 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225_tx_power_ofdm[] = {
+ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static const u32 rtl8225_chan[] = {
+ 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
+ 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
+};
+
+static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ u32 reg;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xFF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 8;
+
+ cck_power = min(cck_power, (u8)35);
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
+
+ if (channel == 14)
+ tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
+ else
+ tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ msleep(1); /* FIXME: optional? */
+
+ /* anaparam2 on */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1);
+
+ tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
+
+ rtl8225_write_phy_ofdm(dev, 5, *tmp);
+ rtl8225_write_phy_ofdm(dev, 7, *tmp);
+
+ msleep(1);
+}
+
+static void rtl8225_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i;
+
+ rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
+
+ /* host_pci_init */
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ msleep(200); /* FIXME: ehh?? */
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
+
+ /* TODO: check if we need really to change BRSR to do RF config */
+ rtl818x_ioread16(priv, &priv->map->BRSR);
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
+ rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl8225_write(dev, 0x0, 0x067);
+ rtl8225_write(dev, 0x1, 0xFE0);
+ rtl8225_write(dev, 0x2, 0x44D);
+ rtl8225_write(dev, 0x3, 0x441);
+ rtl8225_write(dev, 0x4, 0x8BE);
+ rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */
+ rtl8225_write(dev, 0x6, 0xAE6);
+ rtl8225_write(dev, 0x7, rtl8225_chan[0]);
+ rtl8225_write(dev, 0x8, 0x01F);
+ rtl8225_write(dev, 0x9, 0x334);
+ rtl8225_write(dev, 0xA, 0xFD4);
+ rtl8225_write(dev, 0xB, 0x391);
+ rtl8225_write(dev, 0xC, 0x050);
+ rtl8225_write(dev, 0xD, 0x6DB);
+ rtl8225_write(dev, 0xE, 0x029);
+ rtl8225_write(dev, 0xF, 0x914); msleep(1);
+
+ rtl8225_write(dev, 0x2, 0xC4D); msleep(100);
+
+ rtl8225_write(dev, 0x0, 0x127);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1);
+ rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
+ }
+
+ rtl8225_write(dev, 0x0, 0x027);
+ rtl8225_write(dev, 0x0, 0x22F);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+ msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+ msleep(1);
+ }
+
+ msleep(1);
+
+ rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
+
+ rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+ rtl8225_write_phy_cck(dev, 0x19, 0x00);
+ rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+ rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+ rtl8225_write_phy_cck(dev, 0x40, 0x86);
+ rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1);
+
+ rtl8225_rf_set_tx_power(dev, 1);
+
+ /* RX antenna default to A */
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
+
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
+ msleep(1);
+ rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+ rtl8225_write(dev, 0x0c, 0x50);
+ /* set OFDM initial gain */
+ rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]);
+ rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]);
+ rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]);
+ rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]);
+ /* set CCK threshold */
+ rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]);
+}
+
+static const u8 rtl8225z2_tx_power_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225z2_tx_power_cck_B[] = {
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04
+};
+
+static const u8 rtl8225z2_tx_power_cck_A[] = {
+ 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04
+};
+
+static const u8 rtl8225z2_tx_power_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+};
+
+static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xFF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 8;
+
+ if (channel == 14)
+ tmp = rtl8225z2_tx_power_cck_ch14;
+ else if (cck_power == 12)
+ tmp = rtl8225z2_tx_power_cck_B;
+ else if (cck_power == 13)
+ tmp = rtl8225z2_tx_power_cck_A;
+ else
+ tmp = rtl8225z2_tx_power_cck;
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ cck_power = min(cck_power, (u8)35);
+ if (cck_power == 13 || cck_power == 14)
+ cck_power = 12;
+ if (cck_power >= 15)
+ cck_power -= 2;
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power);
+ rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK);
+ msleep(1);
+
+ ofdm_power = min(ofdm_power, (u8)35);
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power);
+
+ rtl8225_write_phy_ofdm(dev, 2, 0x62);
+ rtl8225_write_phy_ofdm(dev, 5, 0x00);
+ rtl8225_write_phy_ofdm(dev, 6, 0x40);
+ rtl8225_write_phy_ofdm(dev, 7, 0x00);
+ rtl8225_write_phy_ofdm(dev, 8, 0x40);
+
+ msleep(1);
+}
+
+static const u16 rtl8225z2_rxgain[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
+ 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
+ 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
+ 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
+ 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
+ 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
+ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
+ 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
+ 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
+ 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
+ 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int i;
+
+ rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
+
+ /* host_pci_init */
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ msleep(200); /* FIXME: ehh?? */
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008);
+
+ /* TODO: check if we need really to change BRSR to do RF config */
+ rtl818x_ioread16(priv, &priv->map->BRSR);
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
+ rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
+ rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
+ rtl8225_write(dev, 0x2, 0x44D); msleep(1);
+ rtl8225_write(dev, 0x3, 0x441); msleep(1);
+ rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
+ rtl8225_write(dev, 0x5, 0xC72); msleep(1);
+ rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
+ rtl8225_write(dev, 0x7, 0x82A); msleep(1);
+ rtl8225_write(dev, 0x8, 0x03F); msleep(1);
+ rtl8225_write(dev, 0x9, 0x335); msleep(1);
+ rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
+ rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
+ rtl8225_write(dev, 0xc, 0x850); msleep(1);
+ rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
+ rtl8225_write(dev, 0xe, 0x02B); msleep(1);
+ rtl8225_write(dev, 0xf, 0x114); msleep(100);
+
+ if (!(rtl8225_read(dev, 6) & (1 << 7))) {
+ rtl8225_write(dev, 0x02, 0x0C4D);
+ msleep(200);
+ rtl8225_write(dev, 0x02, 0x044D);
+ msleep(100);
+ /* TODO: readd calibration failure message when the calibration
+ check works */
+ }
+
+ rtl8225_write(dev, 0x0, 0x1B7);
+ rtl8225_write(dev, 0x3, 0x002);
+ rtl8225_write(dev, 0x5, 0x004);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1);
+ rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
+ }
+
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(100);
+ rtl8225_write(dev, 0x2, 0xC4D);
+
+ msleep(200);
+ rtl8225_write(dev, 0x2, 0x44D);
+ msleep(100);
+
+ rtl8225_write(dev, 0x00, 0x2BF);
+ rtl8225_write(dev, 0xFF, 0xFFFF);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+ msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+ msleep(1);
+ }
+
+ msleep(1);
+
+ rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
+ rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */
+ rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
+
+ rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+ rtl8225_write_phy_cck(dev, 0x19, 0x00);
+ rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+ rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+ rtl8225_write_phy_cck(dev, 0x40, 0x86);
+ rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1);
+ rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
+
+ rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1);
+
+ rtl8225z2_rf_set_tx_power(dev, 1);
+
+ /* RX antenna default to A */
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
+
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
+ msleep(1);
+ rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+}
+
+static void rtl8225_rf_stop(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u8 reg;
+
+ rtl8225_write(dev, 0x4, 0x1f); msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
+ struct ieee80211_conf *conf)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
+
+ if (priv->rf->init == rtl8225_rf_init)
+ rtl8225_rf_set_tx_power(dev, chan);
+ else
+ rtl8225z2_rf_set_tx_power(dev, chan);
+
+ rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
+ msleep(10);
+}
+
+static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev,
+ struct ieee80211_bss_conf *info)
+{
+ struct rtl8180_priv *priv = dev->priv;
+
+ if (info->use_short_slot) {
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
+ rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
+ rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
+ } else {
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
+ rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
+ rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
+ }
+}
+
+static const struct rtl818x_rf_ops rtl8225_ops = {
+ .name = "rtl8225",
+ .init = rtl8225_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel,
+ .conf_erp = rtl8225_rf_conf_erp,
+};
+
+static const struct rtl818x_rf_ops rtl8225z2_ops = {
+ .name = "rtl8225z2",
+ .init = rtl8225z2_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel,
+ .conf_erp = rtl8225_rf_conf_erp,
+};
+
+const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev)
+{
+ struct rtl8180_priv *priv = dev->priv;
+ u16 reg8, reg9;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+ msleep(100);
+
+ rtl8225_write(dev, 0, 0x1B7);
+
+ reg8 = rtl8225_read(dev, 8);
+ reg9 = rtl8225_read(dev, 9);
+
+ rtl8225_write(dev, 0, 0x0B7);
+
+ if (reg8 != 0x588 || reg9 != 0x700)
+ return &rtl8225_ops;
+
+ return &rtl8225z2_ops;
+}
diff --git a/drivers/net/wireless/rtl8180_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h
index 310013a2d726..310013a2d726 100644
--- a/drivers/net/wireless/rtl8180_rtl8225.h
+++ b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h
diff --git a/drivers/net/wireless/rtl8180_sa2400.c b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c
index cea4e0ccb92d..cea4e0ccb92d 100644
--- a/drivers/net/wireless/rtl8180_sa2400.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c
diff --git a/drivers/net/wireless/rtl8180_sa2400.h b/drivers/net/wireless/rtl818x/rtl8180_sa2400.h
index a4aaa0d413f1..a4aaa0d413f1 100644
--- a/drivers/net/wireless/rtl8180_sa2400.h
+++ b/drivers/net/wireless/rtl818x/rtl8180_sa2400.h
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h
new file mode 100644
index 000000000000..3b1e1c2aad26
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8187.h
@@ -0,0 +1,227 @@
+/*
+ * Definitions for RTL8187 hardware
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * This program is free software; you can 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 RTL8187_H
+#define RTL8187_H
+
+#include "rtl818x.h"
+
+#define RTL8187_EEPROM_TXPWR_BASE 0x05
+#define RTL8187_EEPROM_MAC_ADDR 0x07
+#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */
+#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */
+#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */
+
+#define RTL8187_REQT_READ 0xC0
+#define RTL8187_REQT_WRITE 0x40
+#define RTL8187_REQ_GET_REG 0x05
+#define RTL8187_REQ_SET_REG 0x05
+
+#define RTL8187_MAX_RX 0x9C4
+
+struct rtl8187_rx_info {
+ struct urb *urb;
+ struct ieee80211_hw *dev;
+};
+
+struct rtl8187_rx_hdr {
+ __le32 flags;
+ u8 noise;
+ u8 signal;
+ u8 agc;
+ u8 reserved;
+ __le64 mac_time;
+} __attribute__((packed));
+
+struct rtl8187b_rx_hdr {
+ __le32 flags;
+ __le64 mac_time;
+ u8 sq;
+ u8 rssi;
+ u8 agc;
+ u8 flags2;
+ __le16 snr_long2end;
+ s8 pwdb_g12;
+ u8 fot;
+} __attribute__((packed));
+
+/* {rtl8187,rtl8187b}_tx_info is in skb */
+
+struct rtl8187_tx_hdr {
+ __le32 flags;
+ __le16 rts_duration;
+ __le16 len;
+ __le32 retry;
+} __attribute__((packed));
+
+struct rtl8187b_tx_hdr {
+ __le32 flags;
+ __le16 rts_duration;
+ __le16 len;
+ __le32 unused_1;
+ __le16 unused_2;
+ __le16 tx_duration;
+ __le32 unused_3;
+ __le32 retry;
+ __le32 unused_4[2];
+} __attribute__((packed));
+
+enum {
+ DEVICE_RTL8187,
+ DEVICE_RTL8187B
+};
+
+struct rtl8187_priv {
+ /* common between rtl818x drivers */
+ struct rtl818x_csr *map;
+ const struct rtl818x_rf_ops *rf;
+ struct ieee80211_vif *vif;
+ int mode;
+ /* The mutex protects the TX loopback state.
+ * Any attempt to set channels concurrently locks the device.
+ */
+ struct mutex conf_mutex;
+
+ /* rtl8187 specific */
+ struct ieee80211_channel channels[14];
+ struct ieee80211_rate rates[12];
+ struct ieee80211_supported_band band;
+ struct usb_device *udev;
+ u32 rx_conf;
+ struct usb_anchor anchored;
+ u16 txpwr_base;
+ u8 asic_rev;
+ u8 is_rtl8187b;
+ enum {
+ RTL8187BvB,
+ RTL8187BvD,
+ RTL8187BvE
+ } hw_rev;
+ struct sk_buff_head rx_queue;
+ u8 signal;
+ u8 quality;
+ u8 noise;
+ u8 slot_time;
+ u8 aifsn[4];
+ struct {
+ __le64 buf;
+ struct sk_buff_head queue;
+ } b_tx_status;
+};
+
+void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);
+
+static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv,
+ u8 *addr, u8 idx)
+{
+ u8 val;
+
+ usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
+ RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);
+
+ return val;
+}
+
+static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
+{
+ return rtl818x_ioread8_idx(priv, addr, 0);
+}
+
+static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv,
+ __le16 *addr, u8 idx)
+{
+ __le16 val;
+
+ usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
+ RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);
+
+ return le16_to_cpu(val);
+}
+
+static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
+{
+ return rtl818x_ioread16_idx(priv, addr, 0);
+}
+
+static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv,
+ __le32 *addr, u8 idx)
+{
+ __le32 val;
+
+ usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0),
+ RTL8187_REQ_GET_REG, RTL8187_REQT_READ,
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);
+
+ return le32_to_cpu(val);
+}
+
+static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr)
+{
+ return rtl818x_ioread32_idx(priv, addr, 0);
+}
+
+static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv,
+ u8 *addr, u8 val, u8 idx)
+{
+ usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
+ RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);
+}
+
+static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val)
+{
+ rtl818x_iowrite8_idx(priv, addr, val, 0);
+}
+
+static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv,
+ __le16 *addr, u16 val, u8 idx)
+{
+ __le16 buf = cpu_to_le16(val);
+
+ usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
+ RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
+ (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
+ HZ / 2);
+}
+
+static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr,
+ u16 val)
+{
+ rtl818x_iowrite16_idx(priv, addr, val, 0);
+}
+
+static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv,
+ __le32 *addr, u32 val, u8 idx)
+{
+ __le32 buf = cpu_to_le32(val);
+
+ usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
+ RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
+ (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
+ HZ / 2);
+}
+
+static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr,
+ u32 val)
+{
+ rtl818x_iowrite32_idx(priv, addr, val, 0);
+}
+
+#endif /* RTL8187_H */
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
new file mode 100644
index 000000000000..00ce3ef39abe
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -0,0 +1,1496 @@
+/*
+ * Linux device driver for RTL8187
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * The driver was extended to the RTL8187B in 2008 by:
+ * Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ * Hin-Tak Leung <htl10@users.sourceforge.net>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ * Magic delays and register offsets below are taken from the original
+ * r8187 driver sources. Thanks to Realtek for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/eeprom_93cx6.h>
+#include <net/mac80211.h>
+
+#include "rtl8187.h"
+#include "rtl8187_rtl8225.h"
+
+MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
+MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Herton Ronaldo Krzesinski <herton@mandriva.com.br>");
+MODULE_AUTHOR("Hin-Tak Leung <htl10@users.sourceforge.net>");
+MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
+MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
+MODULE_LICENSE("GPL");
+
+static struct usb_device_id rtl8187_table[] __devinitdata = {
+ /* Asus */
+ {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187},
+ /* Belkin */
+ {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B},
+ /* Realtek */
+ {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B},
+ {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B},
+ {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B},
+ /* Netgear */
+ {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B},
+ /* HP */
+ {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187},
+ /* Sitecom */
+ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B},
+ /* Abocom */
+ {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, rtl8187_table);
+
+static const struct ieee80211_rate rtl818x_rates[] = {
+ { .bitrate = 10, .hw_value = 0, },
+ { .bitrate = 20, .hw_value = 1, },
+ { .bitrate = 55, .hw_value = 2, },
+ { .bitrate = 110, .hw_value = 3, },
+ { .bitrate = 60, .hw_value = 4, },
+ { .bitrate = 90, .hw_value = 5, },
+ { .bitrate = 120, .hw_value = 6, },
+ { .bitrate = 180, .hw_value = 7, },
+ { .bitrate = 240, .hw_value = 8, },
+ { .bitrate = 360, .hw_value = 9, },
+ { .bitrate = 480, .hw_value = 10, },
+ { .bitrate = 540, .hw_value = 11, },
+};
+
+static const struct ieee80211_channel rtl818x_channels[] = {
+ { .center_freq = 2412 },
+ { .center_freq = 2417 },
+ { .center_freq = 2422 },
+ { .center_freq = 2427 },
+ { .center_freq = 2432 },
+ { .center_freq = 2437 },
+ { .center_freq = 2442 },
+ { .center_freq = 2447 },
+ { .center_freq = 2452 },
+ { .center_freq = 2457 },
+ { .center_freq = 2462 },
+ { .center_freq = 2467 },
+ { .center_freq = 2472 },
+ { .center_freq = 2484 },
+};
+
+static void rtl8187_iowrite_async_cb(struct urb *urb)
+{
+ kfree(urb->context);
+}
+
+static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
+ void *data, u16 len)
+{
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ struct rtl8187_async_write_data {
+ u8 data[4];
+ struct usb_ctrlrequest dr;
+ } *buf;
+ int rc;
+
+ buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ kfree(buf);
+ return;
+ }
+
+ dr = &buf->dr;
+
+ dr->bRequestType = RTL8187_REQT_WRITE;
+ dr->bRequest = RTL8187_REQ_SET_REG;
+ dr->wValue = addr;
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(len);
+
+ memcpy(buf, data, len);
+
+ usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0),
+ (unsigned char *)dr, buf, len,
+ rtl8187_iowrite_async_cb, buf);
+ usb_anchor_urb(urb, &priv->anchored);
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc < 0) {
+ kfree(buf);
+ usb_unanchor_urb(urb);
+ }
+ usb_free_urb(urb);
+}
+
+static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv,
+ __le32 *addr, u32 val)
+{
+ __le32 buf = cpu_to_le32(val);
+
+ rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr),
+ &buf, sizeof(buf));
+}
+
+void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
+{
+ struct rtl8187_priv *priv = dev->priv;
+
+ data <<= 8;
+ data |= addr | 0x80;
+
+ rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF);
+ rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF);
+ rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF);
+ rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF);
+}
+
+static void rtl8187_tx_cb(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *)urb->context;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hw *hw = info->rate_driver_data[0];
+ struct rtl8187_priv *priv = hw->priv;
+
+ skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
+ sizeof(struct rtl8187_tx_hdr));
+ ieee80211_tx_info_clear_status(info);
+
+ if (!urb->status &&
+ !(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ priv->is_rtl8187b) {
+ skb_queue_tail(&priv->b_tx_status.queue, skb);
+
+ /* queue is "full", discard last items */
+ while (skb_queue_len(&priv->b_tx_status.queue) > 5) {
+ struct sk_buff *old_skb;
+
+ dev_dbg(&priv->udev->dev,
+ "transmit status queue full\n");
+
+ old_skb = skb_dequeue(&priv->b_tx_status.queue);
+ ieee80211_tx_status_irqsafe(hw, old_skb);
+ }
+ } else {
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !urb->status)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ ieee80211_tx_status_irqsafe(hw, skb);
+ }
+}
+
+static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ unsigned int ep;
+ void *buf;
+ struct urb *urb;
+ __le16 rts_dur = 0;
+ u32 flags;
+ int rc;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ flags = skb->len;
+ flags |= RTL818X_TX_DESC_FLAG_NO_ENC;
+
+ flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
+ if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control))
+ flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ flags |= RTL818X_TX_DESC_FLAG_RTS;
+ flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+ rts_dur = ieee80211_rts_duration(dev, priv->vif,
+ skb->len, info);
+ } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ flags |= RTL818X_TX_DESC_FLAG_CTS;
+ flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19;
+ }
+
+ if (!priv->is_rtl8187b) {
+ struct rtl8187_tx_hdr *hdr =
+ (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ hdr->flags = cpu_to_le32(flags);
+ hdr->len = 0;
+ hdr->rts_duration = rts_dur;
+ hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8);
+ buf = hdr;
+
+ ep = 2;
+ } else {
+ /* fc needs to be calculated before skb_push() */
+ unsigned int epmap[4] = { 6, 7, 5, 4 };
+ struct ieee80211_hdr *tx_hdr =
+ (struct ieee80211_hdr *)(skb->data);
+ u16 fc = le16_to_cpu(tx_hdr->frame_control);
+
+ struct rtl8187b_tx_hdr *hdr =
+ (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ struct ieee80211_rate *txrate =
+ ieee80211_get_tx_rate(dev, info);
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->flags = cpu_to_le32(flags);
+ hdr->rts_duration = rts_dur;
+ hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8);
+ hdr->tx_duration =
+ ieee80211_generic_frame_duration(dev, priv->vif,
+ skb->len, txrate);
+ buf = hdr;
+
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)
+ ep = 12;
+ else
+ ep = epmap[skb_get_queue_mapping(skb)];
+ }
+
+ info->rate_driver_data[0] = dev;
+ info->rate_driver_data[1] = urb;
+
+ usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep),
+ buf, skb->len, rtl8187_tx_cb, skb);
+ usb_anchor_urb(urb, &priv->anchored);
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc < 0) {
+ usb_unanchor_urb(urb);
+ kfree_skb(skb);
+ }
+ usb_free_urb(urb);
+
+ return rc;
+}
+
+static void rtl8187_rx_cb(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *)urb->context;
+ struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb;
+ struct ieee80211_hw *dev = info->dev;
+ struct rtl8187_priv *priv = dev->priv;
+ struct ieee80211_rx_status rx_status = { 0 };
+ int rate, signal;
+ u32 flags;
+ u32 quality;
+ unsigned long f;
+
+ spin_lock_irqsave(&priv->rx_queue.lock, f);
+ if (skb->next)
+ __skb_unlink(skb, &priv->rx_queue);
+ else {
+ spin_unlock_irqrestore(&priv->rx_queue.lock, f);
+ return;
+ }
+ spin_unlock_irqrestore(&priv->rx_queue.lock, f);
+ skb_put(skb, urb->actual_length);
+
+ if (unlikely(urb->status)) {
+ dev_kfree_skb_irq(skb);
+ return;
+ }
+
+ if (!priv->is_rtl8187b) {
+ struct rtl8187_rx_hdr *hdr =
+ (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
+ flags = le32_to_cpu(hdr->flags);
+ /* As with the RTL8187B below, the AGC is used to calculate
+ * signal strength and quality. In this case, the scaling
+ * constants are derived from the output of p54usb.
+ */
+ quality = 130 - ((41 * hdr->agc) >> 6);
+ signal = -4 - ((27 * hdr->agc) >> 6);
+ rx_status.antenna = (hdr->signal >> 7) & 1;
+ rx_status.mactime = le64_to_cpu(hdr->mac_time);
+ } else {
+ struct rtl8187b_rx_hdr *hdr =
+ (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
+ /* The Realtek datasheet for the RTL8187B shows that the RX
+ * header contains the following quantities: signal quality,
+ * RSSI, AGC, the received power in dB, and the measured SNR.
+ * In testing, none of these quantities show qualitative
+ * agreement with AP signal strength, except for the AGC,
+ * which is inversely proportional to the strength of the
+ * signal. In the following, the quality and signal strength
+ * are derived from the AGC. The arbitrary scaling constants
+ * are chosen to make the results close to the values obtained
+ * for a BCM4312 using b43 as the driver. The noise is ignored
+ * for now.
+ */
+ flags = le32_to_cpu(hdr->flags);
+ quality = 170 - hdr->agc;
+ signal = 14 - hdr->agc / 2;
+ rx_status.antenna = (hdr->rssi >> 7) & 1;
+ rx_status.mactime = le64_to_cpu(hdr->mac_time);
+ }
+
+ if (quality > 100)
+ quality = 100;
+ rx_status.qual = quality;
+ priv->quality = quality;
+ rx_status.signal = signal;
+ priv->signal = signal;
+ rate = (flags >> 20) & 0xF;
+ skb_trim(skb, flags & 0x0FFF);
+ rx_status.rate_idx = rate;
+ rx_status.freq = dev->conf.channel->center_freq;
+ rx_status.band = dev->conf.channel->band;
+ rx_status.flag |= RX_FLAG_TSFT;
+ if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR)
+ rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+ ieee80211_rx_irqsafe(dev, skb, &rx_status);
+
+ skb = dev_alloc_skb(RTL8187_MAX_RX);
+ if (unlikely(!skb)) {
+ /* TODO check rx queue length and refill *somewhere* */
+ return;
+ }
+
+ info = (struct rtl8187_rx_info *)skb->cb;
+ info->urb = urb;
+ info->dev = dev;
+ urb->transfer_buffer = skb_tail_pointer(skb);
+ urb->context = skb;
+ skb_queue_tail(&priv->rx_queue, skb);
+
+ usb_anchor_urb(urb, &priv->anchored);
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ usb_unanchor_urb(urb);
+ skb_unlink(skb, &priv->rx_queue);
+ dev_kfree_skb_irq(skb);
+ }
+}
+
+static int rtl8187_init_urbs(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ struct urb *entry = NULL;
+ struct sk_buff *skb;
+ struct rtl8187_rx_info *info;
+ int ret = 0;
+
+ while (skb_queue_len(&priv->rx_queue) < 8) {
+ skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ entry = usb_alloc_urb(0, GFP_KERNEL);
+ if (!entry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ usb_fill_bulk_urb(entry, priv->udev,
+ usb_rcvbulkpipe(priv->udev,
+ priv->is_rtl8187b ? 3 : 1),
+ skb_tail_pointer(skb),
+ RTL8187_MAX_RX, rtl8187_rx_cb, skb);
+ info = (struct rtl8187_rx_info *)skb->cb;
+ info->urb = entry;
+ info->dev = dev;
+ skb_queue_tail(&priv->rx_queue, skb);
+ usb_anchor_urb(entry, &priv->anchored);
+ ret = usb_submit_urb(entry, GFP_KERNEL);
+ if (ret) {
+ skb_unlink(skb, &priv->rx_queue);
+ usb_unanchor_urb(entry);
+ goto err;
+ }
+ usb_free_urb(entry);
+ }
+ return ret;
+
+err:
+ usb_free_urb(entry);
+ kfree_skb(skb);
+ usb_kill_anchored_urbs(&priv->anchored);
+ return ret;
+}
+
+static void rtl8187b_status_cb(struct urb *urb)
+{
+ struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context;
+ struct rtl8187_priv *priv = hw->priv;
+ u64 val;
+ unsigned int cmd_type;
+
+ if (unlikely(urb->status))
+ return;
+
+ /*
+ * Read from status buffer:
+ *
+ * bits [30:31] = cmd type:
+ * - 0 indicates tx beacon interrupt
+ * - 1 indicates tx close descriptor
+ *
+ * In the case of tx beacon interrupt:
+ * [0:9] = Last Beacon CW
+ * [10:29] = reserved
+ * [30:31] = 00b
+ * [32:63] = Last Beacon TSF
+ *
+ * If it's tx close descriptor:
+ * [0:7] = Packet Retry Count
+ * [8:14] = RTS Retry Count
+ * [15] = TOK
+ * [16:27] = Sequence No
+ * [28] = LS
+ * [29] = FS
+ * [30:31] = 01b
+ * [32:47] = unused (reserved?)
+ * [48:63] = MAC Used Time
+ */
+ val = le64_to_cpu(priv->b_tx_status.buf);
+
+ cmd_type = (val >> 30) & 0x3;
+ if (cmd_type == 1) {
+ unsigned int pkt_rc, seq_no;
+ bool tok;
+ struct sk_buff *skb;
+ struct ieee80211_hdr *ieee80211hdr;
+ unsigned long flags;
+
+ pkt_rc = val & 0xFF;
+ tok = val & (1 << 15);
+ seq_no = (val >> 16) & 0xFFF;
+
+ spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags);
+ skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) {
+ ieee80211hdr = (struct ieee80211_hdr *)skb->data;
+
+ /*
+ * While testing, it was discovered that the seq_no
+ * doesn't actually contains the sequence number.
+ * Instead of returning just the 12 bits of sequence
+ * number, hardware is returning entire sequence control
+ * (fragment number plus sequence number) in a 12 bit
+ * only field overflowing after some time. As a
+ * workaround, just consider the lower bits, and expect
+ * it's unlikely we wrongly ack some sent data
+ */
+ if ((le16_to_cpu(ieee80211hdr->seq_ctrl)
+ & 0xFFF) == seq_no)
+ break;
+ }
+ if (skb != (struct sk_buff *) &priv->b_tx_status.queue) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ __skb_unlink(skb, &priv->b_tx_status.queue);
+ if (tok)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.rates[0].count = pkt_rc + 1;
+
+ ieee80211_tx_status_irqsafe(hw, skb);
+ }
+ spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags);
+ }
+
+ usb_anchor_urb(urb, &priv->anchored);
+ if (usb_submit_urb(urb, GFP_ATOMIC))
+ usb_unanchor_urb(urb);
+}
+
+static int rtl8187b_init_status_urb(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ struct urb *entry;
+ int ret = 0;
+
+ entry = usb_alloc_urb(0, GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9),
+ &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf),
+ rtl8187b_status_cb, dev);
+
+ usb_anchor_urb(entry, &priv->anchored);
+ ret = usb_submit_urb(entry, GFP_KERNEL);
+ if (ret)
+ usb_unanchor_urb(entry);
+ usb_free_urb(entry);
+
+ return ret;
+}
+
+static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg;
+ int i;
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg &= (1 << 1);
+ reg |= RTL818X_CMD_RESET;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+ i = 10;
+ do {
+ msleep(2);
+ if (!(rtl818x_ioread8(priv, &priv->map->CMD) &
+ RTL818X_CMD_RESET))
+ break;
+ } while (--i);
+
+ if (!i) {
+ printk(KERN_ERR "%s: Reset timeout!\n", wiphy_name(dev->wiphy));
+ return -ETIMEDOUT;
+ }
+
+ /* reload registers from eeprom */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD);
+
+ i = 10;
+ do {
+ msleep(4);
+ if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) &
+ RTL818X_EEPROM_CMD_CONFIG))
+ break;
+ } while (--i);
+
+ if (!i) {
+ printk(KERN_ERR "%s: eeprom reset timeout!\n",
+ wiphy_name(dev->wiphy));
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rtl8187_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg;
+ int res;
+
+ /* reset */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg |
+ RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
+ RTL8187_RTL8225_ANAPARAM_ON);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187_RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg &
+ ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ msleep(200);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
+ msleep(200);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
+ RTL8187_RTL8225_ANAPARAM_ON);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187_RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ /* setup card */
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0);
+ rtl818x_iowrite8(priv, &priv->map->GPIO, 0);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8));
+ rtl818x_iowrite8(priv, &priv->map->GPIO, 1);
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+
+ rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
+ reg &= 0x3F;
+ reg |= 0x80;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
+ rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+ rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+
+ // TODO: set RESP_RATE and BRSR properly
+ rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+
+ /* host_usb_init */
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0);
+ rtl818x_iowrite8(priv, &priv->map->GPIO, 0);
+ reg = rtl818x_ioread8(priv, (u8 *)0xFE53);
+ rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8));
+ rtl818x_iowrite8(priv, &priv->map->GPIO, 0x20);
+ rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80);
+ msleep(100);
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
+ rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7);
+ msleep(100);
+
+ priv->rf->init(dev);
+
+ rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3);
+ reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1;
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
+ rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10);
+ rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80);
+ rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60);
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
+
+ return 0;
+}
+
+static const u8 rtl8187b_reg_table[][3] = {
+ {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0},
+ {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0},
+ {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0},
+ {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0},
+
+ {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1},
+ {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1},
+ {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xE0, 0xFF, 1}, {0xE1, 0x0F, 1},
+ {0xE2, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, {0xF2, 0x02, 1},
+ {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, {0xF6, 0x06, 1},
+ {0xF7, 0x07, 1}, {0xF8, 0x08, 1},
+
+ {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2},
+ {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2},
+ {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2},
+ {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2},
+ {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2},
+ {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2},
+ {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, {0x72, 0x56, 2},
+ {0x73, 0x9A, 2},
+
+ {0x34, 0xF0, 0}, {0x35, 0x0F, 0}, {0x5B, 0x40, 0}, {0x84, 0x88, 0},
+ {0x85, 0x24, 0}, {0x88, 0x54, 0}, {0x8B, 0xB8, 0}, {0x8C, 0x07, 0},
+ {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, {0x95, 0x12, 0}, {0x96, 0x00, 0},
+ {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, {0x9F, 0x10, 0}, {0xB4, 0x22, 0},
+ {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, {0xEE, 0x00, 0}, {0x91, 0x03, 0},
+
+ {0x4C, 0x00, 2}, {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0},
+ {0x8E, 0x08, 0}, {0x8F, 0x00, 0}
+};
+
+static int rtl8187b_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int res, i;
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE | RTL818X_CONFIG3_GNT_SELECT;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187B_RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
+ RTL8187B_RTL8225_ANAPARAM_ON);
+ rtl818x_iowrite8(priv, &priv->map->ANAPARAM3,
+ RTL8187B_RTL8225_ANAPARAM3_ON);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10);
+ reg = rtl818x_ioread8(priv, (u8 *)0xFF62);
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5));
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5));
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
+ rtl818x_iowrite16(priv, (__le16 *)0xFF2D, 0x0FFF);
+ reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+ reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+ reg |= RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT |
+ RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1);
+ reg = rtl818x_ioread8(priv, &priv->map->RATE_FALLBACK);
+ reg |= RTL818X_RATE_FALLBACK_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, reg);
+
+ rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100);
+ rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+ for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) {
+ rtl818x_iowrite8_idx(priv,
+ (u8 *)(uintptr_t)
+ (rtl8187b_reg_table[i][0] | 0xFF00),
+ rtl8187b_reg_table[i][1],
+ rtl8187b_reg_table[i][2]);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50);
+ rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0);
+
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1);
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1);
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD,
+ RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ msleep(100);
+
+ priv->rf->init(dev);
+
+ reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+
+ reg = rtl818x_ioread8(priv, (u8 *)0xFFDB);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2));
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3);
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2);
+
+ rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1);
+
+ priv->slot_time = 0x9;
+ priv->aifsn[0] = 2; /* AIFSN[AC_VO] */
+ priv->aifsn[1] = 2; /* AIFSN[AC_VI] */
+ priv->aifsn[2] = 7; /* AIFSN[AC_BK] */
+ priv->aifsn[3] = 3; /* AIFSN[AC_BE] */
+ rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0);
+
+ return 0;
+}
+
+static int rtl8187_start(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u32 reg;
+ int ret;
+
+ ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) :
+ rtl8187b_init_hw(dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->conf_mutex);
+
+ init_usb_anchor(&priv->anchored);
+
+ if (priv->is_rtl8187b) {
+ reg = RTL818X_RX_CONF_MGMT |
+ RTL818X_RX_CONF_DATA |
+ RTL818X_RX_CONF_BROADCAST |
+ RTL818X_RX_CONF_NICMAC |
+ RTL818X_RX_CONF_BSSID |
+ (7 << 13 /* RX FIFO threshold NONE */) |
+ (7 << 10 /* MAX RX DMA */) |
+ RTL818X_RX_CONF_RX_AUTORESETPHY |
+ RTL818X_RX_CONF_ONLYERLPKT |
+ RTL818X_RX_CONF_MULTICAST;
+ priv->rx_conf = reg;
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF,
+ RTL818X_TX_CONF_HW_SEQNUM |
+ RTL818X_TX_CONF_DISREQQSIZE |
+ (7 << 8 /* short retry limit */) |
+ (7 << 0 /* long retry limit */) |
+ (7 << 21 /* MAX TX DMA */));
+ rtl8187_init_urbs(dev);
+ rtl8187b_init_status_urb(dev);
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+
+ rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
+ rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0);
+
+ rtl8187_init_urbs(dev);
+
+ reg = RTL818X_RX_CONF_ONLYERLPKT |
+ RTL818X_RX_CONF_RX_AUTORESETPHY |
+ RTL818X_RX_CONF_BSSID |
+ RTL818X_RX_CONF_MGMT |
+ RTL818X_RX_CONF_DATA |
+ (7 << 13 /* RX FIFO threshold NONE */) |
+ (7 << 10 /* MAX RX DMA */) |
+ RTL818X_RX_CONF_BROADCAST |
+ RTL818X_RX_CONF_NICMAC;
+
+ priv->rx_conf = reg;
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+ reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT;
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+
+ reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT;
+ reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT;
+ rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
+
+ reg = RTL818X_TX_CONF_CW_MIN |
+ (7 << 21 /* MAX TX DMA */) |
+ RTL818X_TX_CONF_NO_ICV;
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg |= RTL818X_CMD_TX_ENABLE;
+ reg |= RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+ mutex_unlock(&priv->conf_mutex);
+
+ return 0;
+}
+
+static void rtl8187_stop(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ struct sk_buff *skb;
+ u32 reg;
+
+ mutex_lock(&priv->conf_mutex);
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CMD);
+ reg &= ~RTL818X_CMD_TX_ENABLE;
+ reg &= ~RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+
+ priv->rf->stop(dev);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG4);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ while ((skb = skb_dequeue(&priv->b_tx_status.queue)))
+ dev_kfree_skb_any(skb);
+
+ usb_kill_anchored_urbs(&priv->anchored);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int rtl8187_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR)
+ return -EOPNOTSUPP;
+
+ switch (conf->type) {
+ case NL80211_IFTYPE_STATION:
+ priv->mode = conf->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+ priv->vif = conf->vif;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ for (i = 0; i < ETH_ALEN; i++)
+ rtl818x_iowrite8(priv, &priv->map->MAC[i],
+ ((u8 *)conf->mac_addr)[i]);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+}
+
+static void rtl8187_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ mutex_lock(&priv->conf_mutex);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->vif = NULL;
+ mutex_unlock(&priv->conf_mutex);
+}
+
+static int rtl8187_config(struct ieee80211_hw *dev, u32 changed)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+ u32 reg;
+
+ mutex_lock(&priv->conf_mutex);
+ reg = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ /* Enable TX loopback on MAC level to avoid TX during channel
+ * changes, as this has be seen to causes problems and the
+ * card will stop work until next reset
+ */
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF,
+ reg | RTL818X_TX_CONF_LOOPBACK_MAC);
+ priv->rf->set_chan(dev, conf);
+ msleep(10);
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);
+
+ rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
+ rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100);
+ rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100);
+ rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100);
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+}
+
+static int rtl8187_config_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+ u8 reg;
+
+ mutex_lock(&priv->conf_mutex);
+ for (i = 0; i < ETH_ALEN; i++)
+ rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);
+
+ if (is_valid_ether_addr(conf->bssid)) {
+ reg = RTL818X_MSR_INFRA;
+ if (priv->is_rtl8187b)
+ reg |= RTL818X_MSR_ENEDCA;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ } else {
+ reg = RTL818X_MSR_NO_LINK;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ }
+
+ mutex_unlock(&priv->conf_mutex);
+ return 0;
+}
+
+/*
+ * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for
+ * example. Thus we have to use raw values for AC_*_PARAM register addresses.
+ */
+static __le32 *rtl8187b_ac_addr[4] = {
+ (__le32 *) 0xFFF0, /* AC_VO */
+ (__le32 *) 0xFFF4, /* AC_VI */
+ (__le32 *) 0xFFFC, /* AC_BK */
+ (__le32 *) 0xFFF8, /* AC_BE */
+};
+
+#define SIFS_TIME 0xa
+
+static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot,
+ bool use_short_preamble)
+{
+ if (priv->is_rtl8187b) {
+ u8 difs, eifs;
+ u16 ack_timeout;
+ int queue;
+
+ if (use_short_slot) {
+ priv->slot_time = 0x9;
+ difs = 0x1c;
+ eifs = 0x53;
+ } else {
+ priv->slot_time = 0x14;
+ difs = 0x32;
+ eifs = 0x5b;
+ }
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+ rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, difs);
+
+ /*
+ * BRSR+1 on 8187B is in fact EIFS register
+ * Value in units of 4 us
+ */
+ rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs);
+
+ /*
+ * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout
+ * register. In units of 4 us like eifs register
+ * ack_timeout = ack duration + plcp + difs + preamble
+ */
+ ack_timeout = 112 + 48 + difs;
+ if (use_short_preamble)
+ ack_timeout += 72;
+ else
+ ack_timeout += 144;
+ rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER,
+ DIV_ROUND_UP(ack_timeout, 4));
+
+ for (queue = 0; queue < 4; queue++)
+ rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue],
+ priv->aifsn[queue] * priv->slot_time +
+ SIFS_TIME);
+ } else {
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+ if (use_short_slot) {
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
+ rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14);
+ } else {
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
+ rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
+ rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24);
+ }
+ }
+}
+
+static void rtl8187_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct rtl8187_priv *priv = dev->priv;
+
+ if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE))
+ rtl8187_conf_erp(priv, info->use_short_slot,
+ info->use_short_preamble);
+}
+
+static void rtl8187_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ int mc_count, struct dev_addr_list *mclist)
+{
+ struct rtl8187_priv *priv = dev->priv;
+
+ if (changed_flags & FIF_FCSFAIL)
+ priv->rx_conf ^= RTL818X_RX_CONF_FCS;
+ if (changed_flags & FIF_CONTROL)
+ priv->rx_conf ^= RTL818X_RX_CONF_CTRL;
+ if (changed_flags & FIF_OTHER_BSS)
+ priv->rx_conf ^= RTL818X_RX_CONF_MONITOR;
+ if (*total_flags & FIF_ALLMULTI || mc_count > 0)
+ priv->rx_conf |= RTL818X_RX_CONF_MULTICAST;
+ else
+ priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST;
+
+ *total_flags = 0;
+
+ if (priv->rx_conf & RTL818X_RX_CONF_FCS)
+ *total_flags |= FIF_FCSFAIL;
+ if (priv->rx_conf & RTL818X_RX_CONF_CTRL)
+ *total_flags |= FIF_CONTROL;
+ if (priv->rx_conf & RTL818X_RX_CONF_MONITOR)
+ *total_flags |= FIF_OTHER_BSS;
+ if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST)
+ *total_flags |= FIF_ALLMULTI;
+
+ rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf);
+}
+
+static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cw_min, cw_max;
+
+ if (queue > 3)
+ return -EINVAL;
+
+ cw_min = fls(params->cw_min);
+ cw_max = fls(params->cw_max);
+
+ if (priv->is_rtl8187b) {
+ priv->aifsn[queue] = params->aifs;
+
+ /*
+ * This is the structure of AC_*_PARAM registers in 8187B:
+ * - TXOP limit field, bit offset = 16
+ * - ECWmax, bit offset = 12
+ * - ECWmin, bit offset = 8
+ * - AIFS, bit offset = 0
+ */
+ rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue],
+ (params->txop << 16) | (cw_max << 12) |
+ (cw_min << 8) | (params->aifs *
+ priv->slot_time + SIFS_TIME));
+ } else {
+ if (queue != 0)
+ return -EINVAL;
+
+ rtl818x_iowrite8(priv, &priv->map->CW_VAL,
+ cw_min | (cw_max << 4));
+ }
+ return 0;
+}
+
+static const struct ieee80211_ops rtl8187_ops = {
+ .tx = rtl8187_tx,
+ .start = rtl8187_start,
+ .stop = rtl8187_stop,
+ .add_interface = rtl8187_add_interface,
+ .remove_interface = rtl8187_remove_interface,
+ .config = rtl8187_config,
+ .config_interface = rtl8187_config_interface,
+ .bss_info_changed = rtl8187_bss_info_changed,
+ .configure_filter = rtl8187_configure_filter,
+ .conf_tx = rtl8187_conf_tx
+};
+
+static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+ struct ieee80211_hw *dev = eeprom->data;
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
+
+ eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE;
+ eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ;
+ eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK;
+ eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS;
+}
+
+static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+ struct ieee80211_hw *dev = eeprom->data;
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg = RTL818X_EEPROM_CMD_PROGRAM;
+
+ if (eeprom->reg_data_in)
+ reg |= RTL818X_EEPROM_CMD_WRITE;
+ if (eeprom->reg_data_out)
+ reg |= RTL818X_EEPROM_CMD_READ;
+ if (eeprom->reg_data_clock)
+ reg |= RTL818X_EEPROM_CMD_CK;
+ if (eeprom->reg_chip_select)
+ reg |= RTL818X_EEPROM_CMD_CS;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg);
+ udelay(10);
+}
+
+static int __devinit rtl8187_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct ieee80211_hw *dev;
+ struct rtl8187_priv *priv;
+ struct eeprom_93cx6 eeprom;
+ struct ieee80211_channel *channel;
+ const char *chip_name;
+ u16 txpwr, reg;
+ int err, i;
+
+ dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops);
+ if (!dev) {
+ printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n");
+ return -ENOMEM;
+ }
+
+ priv = dev->priv;
+ priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);
+
+ SET_IEEE80211_DEV(dev, &intf->dev);
+ usb_set_intfdata(intf, dev);
+ priv->udev = udev;
+
+ usb_get_dev(udev);
+
+ skb_queue_head_init(&priv->rx_queue);
+
+ BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels));
+ BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates));
+
+ memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels));
+ memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates));
+ priv->map = (struct rtl818x_csr *)0xFF00;
+
+ priv->band.band = IEEE80211_BAND_2GHZ;
+ priv->band.channels = priv->channels;
+ priv->band.n_channels = ARRAY_SIZE(rtl818x_channels);
+ priv->band.bitrates = priv->rates;
+ priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates);
+ dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+
+
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_RX_INCLUDES_FCS;
+
+ eeprom.data = dev;
+ eeprom.register_read = rtl8187_eeprom_register_read;
+ eeprom.register_write = rtl8187_eeprom_register_write;
+ if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6))
+ eeprom.width = PCI_EEPROM_WIDTH_93C66;
+ else
+ eeprom.width = PCI_EEPROM_WIDTH_93C46;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ udelay(10);
+
+ eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR,
+ (__le16 __force *)dev->wiphy->perm_addr, 3);
+ if (!is_valid_ether_addr(dev->wiphy->perm_addr)) {
+ printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly "
+ "generated MAC address\n");
+ random_ether_addr(dev->wiphy->perm_addr);
+ }
+
+ channel = priv->channels;
+ for (i = 0; i < 3; i++) {
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+ for (i = 0; i < 2; i++) {
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE,
+ &priv->txpwr_base);
+
+ reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1;
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
+ /* 0 means asic B-cut, we should use SW 3 wire
+ * bit-by-bit banging for radio. 1 means we can use
+ * USB specific request to write radio registers */
+ priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3;
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ if (!priv->is_rtl8187b) {
+ u32 reg32;
+ reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ reg32 &= RTL818X_TX_CONF_HWVER_MASK;
+ switch (reg32) {
+ case RTL818X_TX_CONF_R8187vD_B:
+ /* Some RTL8187B devices have a USB ID of 0x8187
+ * detect them here */
+ chip_name = "RTL8187BvB(early)";
+ priv->is_rtl8187b = 1;
+ priv->hw_rev = RTL8187BvB;
+ break;
+ case RTL818X_TX_CONF_R8187vD:
+ chip_name = "RTL8187vD";
+ break;
+ default:
+ chip_name = "RTL8187vB (default)";
+ }
+ } else {
+ /*
+ * Force USB request to write radio registers for 8187B, Realtek
+ * only uses it in their sources
+ */
+ /*if (priv->asic_rev == 0) {
+ printk(KERN_WARNING "rtl8187: Forcing use of USB "
+ "requests to write to radio registers\n");
+ priv->asic_rev = 1;
+ }*/
+ switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) {
+ case RTL818X_R8187B_B:
+ chip_name = "RTL8187BvB";
+ priv->hw_rev = RTL8187BvB;
+ break;
+ case RTL818X_R8187B_D:
+ chip_name = "RTL8187BvD";
+ priv->hw_rev = RTL8187BvD;
+ break;
+ case RTL818X_R8187B_E:
+ chip_name = "RTL8187BvE";
+ priv->hw_rev = RTL8187BvE;
+ break;
+ default:
+ chip_name = "RTL8187BvB (default)";
+ priv->hw_rev = RTL8187BvB;
+ }
+ }
+
+ if (!priv->is_rtl8187b) {
+ for (i = 0; i < 2; i++) {
+ eeprom_93cx6_read(&eeprom,
+ RTL8187_EEPROM_TXPWR_CHAN_6 + i,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+ } else {
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x0A, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x1C, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+
+ if (priv->is_rtl8187b)
+ printk(KERN_WARNING "rtl8187: 8187B chip detected.\n");
+
+ /*
+ * XXX: Once this driver supports anything that requires
+ * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ.
+ */
+ dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+ if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b)
+ printk(KERN_INFO "rtl8187: inconsistency between id with OEM"
+ " info!\n");
+
+ priv->rf = rtl8187_detect_rf(dev);
+ dev->extra_tx_headroom = (!priv->is_rtl8187b) ?
+ sizeof(struct rtl8187_tx_hdr) :
+ sizeof(struct rtl8187b_tx_hdr);
+ if (!priv->is_rtl8187b)
+ dev->queues = 1;
+ else
+ dev->queues = 4;
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ printk(KERN_ERR "rtl8187: Cannot register device\n");
+ goto err_free_dev;
+ }
+ mutex_init(&priv->conf_mutex);
+ skb_queue_head_init(&priv->b_tx_status.queue);
+
+ printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n",
+ wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
+ chip_name, priv->asic_rev, priv->rf->name);
+
+ return 0;
+
+ err_free_dev:
+ ieee80211_free_hw(dev);
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(udev);
+ return err;
+}
+
+static void __devexit rtl8187_disconnect(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct rtl8187_priv *priv;
+
+ if (!dev)
+ return;
+
+ ieee80211_unregister_hw(dev);
+
+ priv = dev->priv;
+ usb_put_dev(interface_to_usbdev(intf));
+ ieee80211_free_hw(dev);
+}
+
+static struct usb_driver rtl8187_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtl8187_table,
+ .probe = rtl8187_probe,
+ .disconnect = __devexit_p(rtl8187_disconnect),
+};
+
+static int __init rtl8187_init(void)
+{
+ return usb_register(&rtl8187_driver);
+}
+
+static void __exit rtl8187_exit(void)
+{
+ usb_deregister(&rtl8187_driver);
+}
+
+module_init(rtl8187_init);
+module_exit(rtl8187_exit);
diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c
new file mode 100644
index 000000000000..4e75e8e7fa90
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c
@@ -0,0 +1,971 @@
+/*
+ * Radio tuning for RTL8225 on RTL8187
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * Magic delays, register offsets, and phy value tables below are
+ * taken from the original r8187 driver sources. Thanks to Realtek
+ * for their support!
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <net/mac80211.h>
+
+#include "rtl8187.h"
+#include "rtl8187_rtl8225.h"
+
+static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u16 reg80, reg84, reg82;
+ u32 bangdata;
+ int i;
+
+ bangdata = (data << 4) | (addr & 0xf);
+
+ reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
+ reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
+
+ reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7);
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+ udelay(10);
+
+ for (i = 15; i >= 0; i--) {
+ u16 reg = reg80 | (bangdata & (1 << i)) >> i;
+
+ if (i & 1)
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
+
+ if (!(i & 1))
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
+}
+
+static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u16 reg80, reg82, reg84;
+
+ reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
+ reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+ reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
+
+ reg80 &= ~(0x3 << 2);
+ reg84 &= ~0xF;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007);
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ udelay(2);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+ udelay(10);
+
+ usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0),
+ RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE,
+ addr, 0x8225, &data, sizeof(data), HZ / 2);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ udelay(10);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
+}
+
+static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
+{
+ struct rtl8187_priv *priv = dev->priv;
+
+ if (priv->asic_rev)
+ rtl8225_write_8051(dev, addr, cpu_to_le16(data));
+ else
+ rtl8225_write_bitbang(dev, addr, data);
+}
+
+static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u16 reg80, reg82, reg84, out;
+ int i;
+
+ reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
+ reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
+ reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
+
+ reg80 &= ~0xF;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
+ udelay(4);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
+ udelay(5);
+
+ for (i = 4; i >= 0; i--) {
+ u16 reg = reg80 | ((addr >> i) & 1);
+
+ if (!(i & 1)) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ udelay(1);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg | (1 << 1));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg | (1 << 1));
+ udelay(2);
+
+ if (i & 1) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
+ udelay(1);
+ }
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ udelay(2);
+
+ out = 0;
+ for (i = 11; i >= 0; i--) {
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ udelay(1);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ udelay(2);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 1));
+ udelay(2);
+
+ if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
+ out |= 1 << i;
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3));
+ udelay(2);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
+ reg80 | (1 << 3) | (1 << 2));
+ udelay(2);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
+
+ return out;
+}
+
+static const u16 rtl8225bcd_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
+ 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
+};
+
+static const u8 rtl8225_agc[] = {
+ 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
+ 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
+ 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
+ 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
+ 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
+ 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
+ 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
+ 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
+ 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
+ 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
+ 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+static const u8 rtl8225_gain[] = {
+ 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */
+ 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */
+ 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */
+ 0x33, 0x80, 0x79, 0xc5, /* -78dBm */
+ 0x43, 0x78, 0x76, 0xc5, /* -74dBm */
+ 0x53, 0x60, 0x73, 0xc5, /* -70dBm */
+ 0x63, 0x58, 0x70, 0xc5, /* -66dBm */
+};
+
+static const u8 rtl8225_threshold[] = {
+ 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
+};
+
+static const u8 rtl8225_tx_gain_cck_ofdm[] = {
+ 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
+};
+
+static const u8 rtl8225_tx_power_cck[] = {
+ 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
+ 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
+ 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
+ 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
+};
+
+static const u8 rtl8225_tx_power_cck_ch14[] = {
+ 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225_tx_power_ofdm[] = {
+ 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
+};
+
+static const u32 rtl8225_chan[] = {
+ 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
+ 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
+};
+
+static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ u32 reg;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 4;
+
+ cck_power = min(cck_power, (u8)11);
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
+
+ if (channel == 14)
+ tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
+ else
+ tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ msleep(1); // FIXME: optional?
+
+ /* anaparam2 on */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187_RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl8225_write_phy_ofdm(dev, 2, 0x42);
+ rtl8225_write_phy_ofdm(dev, 6, 0x00);
+ rtl8225_write_phy_ofdm(dev, 8, 0x00);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1);
+
+ tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
+
+ rtl8225_write_phy_ofdm(dev, 5, *tmp);
+ rtl8225_write_phy_ofdm(dev, 7, *tmp);
+
+ msleep(1);
+}
+
+static void rtl8225_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ rtl8225_write(dev, 0x0, 0x067);
+ rtl8225_write(dev, 0x1, 0xFE0);
+ rtl8225_write(dev, 0x2, 0x44D);
+ rtl8225_write(dev, 0x3, 0x441);
+ rtl8225_write(dev, 0x4, 0x486);
+ rtl8225_write(dev, 0x5, 0xBC0);
+ rtl8225_write(dev, 0x6, 0xAE6);
+ rtl8225_write(dev, 0x7, 0x82A);
+ rtl8225_write(dev, 0x8, 0x01F);
+ rtl8225_write(dev, 0x9, 0x334);
+ rtl8225_write(dev, 0xA, 0xFD4);
+ rtl8225_write(dev, 0xB, 0x391);
+ rtl8225_write(dev, 0xC, 0x050);
+ rtl8225_write(dev, 0xD, 0x6DB);
+ rtl8225_write(dev, 0xE, 0x029);
+ rtl8225_write(dev, 0xF, 0x914); msleep(100);
+
+ rtl8225_write(dev, 0x2, 0xC4D); msleep(200);
+ rtl8225_write(dev, 0x2, 0x44D); msleep(200);
+
+ if (!(rtl8225_read(dev, 6) & (1 << 7))) {
+ rtl8225_write(dev, 0x02, 0x0c4d);
+ msleep(200);
+ rtl8225_write(dev, 0x02, 0x044d);
+ msleep(100);
+ if (!(rtl8225_read(dev, 6) & (1 << 7)))
+ printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
+ wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
+ }
+
+ rtl8225_write(dev, 0x0, 0x127);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1);
+ rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
+ }
+
+ rtl8225_write(dev, 0x0, 0x027);
+ rtl8225_write(dev, 0x0, 0x22F);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+ rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+ }
+
+ msleep(1);
+
+ rtl8225_write_phy_ofdm(dev, 0x00, 0x01);
+ rtl8225_write_phy_ofdm(dev, 0x01, 0x02);
+ rtl8225_write_phy_ofdm(dev, 0x02, 0x42);
+ rtl8225_write_phy_ofdm(dev, 0x03, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x04, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x05, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x06, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x07, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x08, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x09, 0xfe);
+ rtl8225_write_phy_ofdm(dev, 0x0a, 0x09);
+ rtl8225_write_phy_ofdm(dev, 0x0b, 0x80);
+ rtl8225_write_phy_ofdm(dev, 0x0c, 0x01);
+ rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3);
+ rtl8225_write_phy_ofdm(dev, 0x0f, 0x38);
+ rtl8225_write_phy_ofdm(dev, 0x10, 0x84);
+ rtl8225_write_phy_ofdm(dev, 0x11, 0x06);
+ rtl8225_write_phy_ofdm(dev, 0x12, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x13, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x14, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x15, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x16, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x17, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xef);
+ rtl8225_write_phy_ofdm(dev, 0x19, 0x19);
+ rtl8225_write_phy_ofdm(dev, 0x1a, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x1b, 0x76);
+ rtl8225_write_phy_ofdm(dev, 0x1c, 0x04);
+ rtl8225_write_phy_ofdm(dev, 0x1e, 0x95);
+ rtl8225_write_phy_ofdm(dev, 0x1f, 0x75);
+ rtl8225_write_phy_ofdm(dev, 0x20, 0x1f);
+ rtl8225_write_phy_ofdm(dev, 0x21, 0x27);
+ rtl8225_write_phy_ofdm(dev, 0x22, 0x16);
+ rtl8225_write_phy_ofdm(dev, 0x24, 0x46);
+ rtl8225_write_phy_ofdm(dev, 0x25, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90);
+ rtl8225_write_phy_ofdm(dev, 0x27, 0x88);
+
+ rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
+ rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
+ rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
+ rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
+
+ rtl8225_write_phy_cck(dev, 0x00, 0x98);
+ rtl8225_write_phy_cck(dev, 0x03, 0x20);
+ rtl8225_write_phy_cck(dev, 0x04, 0x7e);
+ rtl8225_write_phy_cck(dev, 0x05, 0x12);
+ rtl8225_write_phy_cck(dev, 0x06, 0xfc);
+ rtl8225_write_phy_cck(dev, 0x07, 0x78);
+ rtl8225_write_phy_cck(dev, 0x08, 0x2e);
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b);
+ rtl8225_write_phy_cck(dev, 0x11, 0x88);
+ rtl8225_write_phy_cck(dev, 0x12, 0x47);
+ rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+ rtl8225_write_phy_cck(dev, 0x19, 0x00);
+ rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+ rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+ rtl8225_write_phy_cck(dev, 0x40, 0x86);
+ rtl8225_write_phy_cck(dev, 0x41, 0x8d);
+ rtl8225_write_phy_cck(dev, 0x42, 0x15);
+ rtl8225_write_phy_cck(dev, 0x43, 0x18);
+ rtl8225_write_phy_cck(dev, 0x44, 0x1f);
+ rtl8225_write_phy_cck(dev, 0x45, 0x1e);
+ rtl8225_write_phy_cck(dev, 0x46, 0x1a);
+ rtl8225_write_phy_cck(dev, 0x47, 0x15);
+ rtl8225_write_phy_cck(dev, 0x48, 0x10);
+ rtl8225_write_phy_cck(dev, 0x49, 0x0a);
+ rtl8225_write_phy_cck(dev, 0x4a, 0x05);
+ rtl8225_write_phy_cck(dev, 0x4b, 0x02);
+ rtl8225_write_phy_cck(dev, 0x4c, 0x05);
+
+ rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D);
+
+ rtl8225_rf_set_tx_power(dev, 1);
+
+ /* RX antenna default to A */
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */
+
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
+ msleep(1);
+ rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
+
+ /* set sensitivity */
+ rtl8225_write(dev, 0x0c, 0x50);
+ rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]);
+ rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]);
+ rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]);
+ rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]);
+ rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]);
+}
+
+static const u8 rtl8225z2_agc[] = {
+ 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f,
+ 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37,
+ 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f,
+ 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07,
+ 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28,
+ 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
+};
+static const u8 rtl8225z2_ofdm[] = {
+ 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00,
+ 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26,
+ 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3,
+ 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f,
+ 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00,
+ 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e,
+ 0x6d, 0x3c, 0xfb, 0x07
+};
+
+static const u8 rtl8225z2_tx_power_cck_ch14[] = {
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
+};
+
+static const u8 rtl8225z2_tx_power_cck[] = {
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
+};
+
+static const u8 rtl8225z2_tx_power_ofdm[] = {
+ 0x42, 0x00, 0x40, 0x00, 0x40
+};
+
+static const u8 rtl8225z2_tx_gain_cck_ofdm[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
+};
+
+static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ u32 reg;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 4;
+
+ cck_power = min(cck_power, (u8)15);
+ cck_power += priv->txpwr_base & 0xF;
+ cck_power = min(cck_power, (u8)35);
+
+ ofdm_power = min(ofdm_power, (u8)15);
+ ofdm_power += priv->txpwr_base >> 4;
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ if (channel == 14)
+ tmp = rtl8225z2_tx_power_cck_ch14;
+ else
+ tmp = rtl8225z2_tx_power_cck;
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225z2_tx_gain_cck_ofdm[cck_power]);
+ msleep(1);
+
+ /* anaparam2 on */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187_RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3,
+ reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl8225_write_phy_ofdm(dev, 2, 0x42);
+ rtl8225_write_phy_ofdm(dev, 5, 0x00);
+ rtl8225_write_phy_ofdm(dev, 6, 0x40);
+ rtl8225_write_phy_ofdm(dev, 7, 0x00);
+ rtl8225_write_phy_ofdm(dev, 8, 0x40);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225z2_tx_gain_cck_ofdm[ofdm_power]);
+ msleep(1);
+}
+
+static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 4;
+
+ if (cck_power > 15)
+ cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22;
+ else
+ cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7;
+ cck_power += priv->txpwr_base & 0xF;
+ cck_power = min(cck_power, (u8)35);
+
+ if (ofdm_power > 15)
+ ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25;
+ else
+ ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10;
+ ofdm_power += (priv->txpwr_base >> 4) & 0xF;
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ if (channel == 14)
+ tmp = rtl8225z2_tx_power_cck_ch14;
+ else
+ tmp = rtl8225z2_tx_power_cck;
+
+ if (priv->hw_rev == RTL8187BvB) {
+ if (cck_power <= 6)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else
+ tmp += 16;
+ } else {
+ if (cck_power <= 5)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else if (cck_power <= 17)
+ tmp += 16;
+ else
+ tmp += 24;
+ }
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1);
+ msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1);
+ if (priv->hw_rev == RTL8187BvB) {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x60);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x60);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ }
+ } else {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ } else if (ofdm_power <= 17) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x54);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x54);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x50);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x50);
+ }
+ }
+ msleep(1);
+}
+
+static const u16 rtl8225z2_rxgain[] = {
+ 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
+ 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
+ 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
+ 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
+ 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
+ 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
+ 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
+ 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
+ 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
+ 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
+ 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
+};
+
+static const u8 rtl8225z2_gain_bg[] = {
+ 0x23, 0x15, 0xa5, /* -82-1dBm */
+ 0x23, 0x15, 0xb5, /* -82-2dBm */
+ 0x23, 0x15, 0xc5, /* -82-3dBm */
+ 0x33, 0x15, 0xc5, /* -78dBm */
+ 0x43, 0x15, 0xc5, /* -74dBm */
+ 0x53, 0x15, 0xc5, /* -70dBm */
+ 0x63, 0x15, 0xc5 /* -66dBm */
+};
+
+static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ rtl8225_write(dev, 0x0, 0x2BF);
+ rtl8225_write(dev, 0x1, 0xEE0);
+ rtl8225_write(dev, 0x2, 0x44D);
+ rtl8225_write(dev, 0x3, 0x441);
+ rtl8225_write(dev, 0x4, 0x8C3);
+ rtl8225_write(dev, 0x5, 0xC72);
+ rtl8225_write(dev, 0x6, 0x0E6);
+ rtl8225_write(dev, 0x7, 0x82A);
+ rtl8225_write(dev, 0x8, 0x03F);
+ rtl8225_write(dev, 0x9, 0x335);
+ rtl8225_write(dev, 0xa, 0x9D4);
+ rtl8225_write(dev, 0xb, 0x7BB);
+ rtl8225_write(dev, 0xc, 0x850);
+ rtl8225_write(dev, 0xd, 0xCDF);
+ rtl8225_write(dev, 0xe, 0x02B);
+ rtl8225_write(dev, 0xf, 0x114);
+ msleep(100);
+
+ rtl8225_write(dev, 0x0, 0x1B7);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1);
+ rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
+ }
+
+ rtl8225_write(dev, 0x3, 0x080);
+ rtl8225_write(dev, 0x5, 0x004);
+ rtl8225_write(dev, 0x0, 0x0B7);
+ rtl8225_write(dev, 0x2, 0xc4D);
+
+ msleep(200);
+ rtl8225_write(dev, 0x2, 0x44D);
+ msleep(100);
+
+ if (!(rtl8225_read(dev, 6) & (1 << 7))) {
+ rtl8225_write(dev, 0x02, 0x0C4D);
+ msleep(200);
+ rtl8225_write(dev, 0x02, 0x044D);
+ msleep(100);
+ if (!(rtl8225_read(dev, 6) & (1 << 7)))
+ printk(KERN_WARNING "%s: RF Calibration Failed! %x\n",
+ wiphy_name(dev->wiphy), rtl8225_read(dev, 6));
+ }
+
+ msleep(200);
+
+ rtl8225_write(dev, 0x0, 0x2BF);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
+ rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
+ }
+
+ msleep(1);
+
+ rtl8225_write_phy_ofdm(dev, 0x00, 0x01);
+ rtl8225_write_phy_ofdm(dev, 0x01, 0x02);
+ rtl8225_write_phy_ofdm(dev, 0x02, 0x42);
+ rtl8225_write_phy_ofdm(dev, 0x03, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x04, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x05, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x06, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x07, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x08, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x09, 0xfe);
+ rtl8225_write_phy_ofdm(dev, 0x0a, 0x08);
+ rtl8225_write_phy_ofdm(dev, 0x0b, 0x80);
+ rtl8225_write_phy_ofdm(dev, 0x0c, 0x01);
+ rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
+ rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3);
+ rtl8225_write_phy_ofdm(dev, 0x0f, 0x38);
+ rtl8225_write_phy_ofdm(dev, 0x10, 0x84);
+ rtl8225_write_phy_ofdm(dev, 0x11, 0x07);
+ rtl8225_write_phy_ofdm(dev, 0x12, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x13, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x14, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x15, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x16, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x17, 0x40);
+ rtl8225_write_phy_ofdm(dev, 0x18, 0xef);
+ rtl8225_write_phy_ofdm(dev, 0x19, 0x19);
+ rtl8225_write_phy_ofdm(dev, 0x1a, 0x20);
+ rtl8225_write_phy_ofdm(dev, 0x1b, 0x15);
+ rtl8225_write_phy_ofdm(dev, 0x1c, 0x04);
+ rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5);
+ rtl8225_write_phy_ofdm(dev, 0x1e, 0x95);
+ rtl8225_write_phy_ofdm(dev, 0x1f, 0x75);
+ rtl8225_write_phy_ofdm(dev, 0x20, 0x1f);
+ rtl8225_write_phy_ofdm(dev, 0x21, 0x17);
+ rtl8225_write_phy_ofdm(dev, 0x22, 0x16);
+ rtl8225_write_phy_ofdm(dev, 0x23, 0x80);
+ rtl8225_write_phy_ofdm(dev, 0x24, 0x46);
+ rtl8225_write_phy_ofdm(dev, 0x25, 0x00);
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90);
+ rtl8225_write_phy_ofdm(dev, 0x27, 0x88);
+
+ rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]);
+ rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]);
+ rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]);
+ rtl8225_write_phy_ofdm(dev, 0x21, 0x37);
+
+ rtl8225_write_phy_cck(dev, 0x00, 0x98);
+ rtl8225_write_phy_cck(dev, 0x03, 0x20);
+ rtl8225_write_phy_cck(dev, 0x04, 0x7e);
+ rtl8225_write_phy_cck(dev, 0x05, 0x12);
+ rtl8225_write_phy_cck(dev, 0x06, 0xfc);
+ rtl8225_write_phy_cck(dev, 0x07, 0x78);
+ rtl8225_write_phy_cck(dev, 0x08, 0x2e);
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b);
+ rtl8225_write_phy_cck(dev, 0x11, 0x88);
+ rtl8225_write_phy_cck(dev, 0x12, 0x47);
+ rtl8225_write_phy_cck(dev, 0x13, 0xd0);
+ rtl8225_write_phy_cck(dev, 0x19, 0x00);
+ rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
+ rtl8225_write_phy_cck(dev, 0x1b, 0x08);
+ rtl8225_write_phy_cck(dev, 0x40, 0x86);
+ rtl8225_write_phy_cck(dev, 0x41, 0x8d);
+ rtl8225_write_phy_cck(dev, 0x42, 0x15);
+ rtl8225_write_phy_cck(dev, 0x43, 0x18);
+ rtl8225_write_phy_cck(dev, 0x44, 0x36);
+ rtl8225_write_phy_cck(dev, 0x45, 0x35);
+ rtl8225_write_phy_cck(dev, 0x46, 0x2e);
+ rtl8225_write_phy_cck(dev, 0x47, 0x25);
+ rtl8225_write_phy_cck(dev, 0x48, 0x1c);
+ rtl8225_write_phy_cck(dev, 0x49, 0x12);
+ rtl8225_write_phy_cck(dev, 0x4a, 0x09);
+ rtl8225_write_phy_cck(dev, 0x4b, 0x04);
+ rtl8225_write_phy_cck(dev, 0x4c, 0x05);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1);
+
+ rtl8225z2_rf_set_tx_power(dev, 1);
+
+ /* RX antenna default to A */
+ rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */
+ rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */
+
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
+ msleep(1);
+ rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
+}
+
+static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ rtl8225_write(dev, 0x0, 0x0B7);
+ rtl8225_write(dev, 0x1, 0xEE0);
+ rtl8225_write(dev, 0x2, 0x44D);
+ rtl8225_write(dev, 0x3, 0x441);
+ rtl8225_write(dev, 0x4, 0x8C3);
+ rtl8225_write(dev, 0x5, 0xC72);
+ rtl8225_write(dev, 0x6, 0x0E6);
+ rtl8225_write(dev, 0x7, 0x82A);
+ rtl8225_write(dev, 0x8, 0x03F);
+ rtl8225_write(dev, 0x9, 0x335);
+ rtl8225_write(dev, 0xa, 0x9D4);
+ rtl8225_write(dev, 0xb, 0x7BB);
+ rtl8225_write(dev, 0xc, 0x850);
+ rtl8225_write(dev, 0xd, 0xCDF);
+ rtl8225_write(dev, 0xe, 0x02B);
+ rtl8225_write(dev, 0xf, 0x114);
+
+ rtl8225_write(dev, 0x0, 0x1B7);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1);
+ rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
+ }
+
+ rtl8225_write(dev, 0x3, 0x080);
+ rtl8225_write(dev, 0x5, 0x004);
+ rtl8225_write(dev, 0x0, 0x0B7);
+
+ rtl8225_write(dev, 0x2, 0xC4D);
+
+ rtl8225_write(dev, 0x2, 0x44D);
+ rtl8225_write(dev, 0x0, 0x2BF);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03);
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07);
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x12);
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0);
+ }
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x10);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++)
+ rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]);
+
+ rtl8225_write_phy_ofdm(dev, 0x97, 0x46);
+ rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6);
+ rtl8225_write_phy_ofdm(dev, 0x85, 0xfc);
+ rtl8225_write_phy_cck(dev, 0xc1, 0x88);
+}
+
+static void rtl8225_rf_stop(struct ieee80211_hw *dev)
+{
+ u8 reg;
+ struct rtl8187_priv *priv = dev->priv;
+
+ rtl8225_write(dev, 0x4, 0x1f);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ if (!priv->is_rtl8187b) {
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187_RTL8225_ANAPARAM2_OFF);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
+ RTL8187_RTL8225_ANAPARAM_OFF);
+ } else {
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2,
+ RTL8187B_RTL8225_ANAPARAM2_OFF);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM,
+ RTL8187B_RTL8225_ANAPARAM_OFF);
+ rtl818x_iowrite8(priv, &priv->map->ANAPARAM3,
+ RTL8187B_RTL8225_ANAPARAM3_OFF);
+ }
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+}
+
+static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
+ struct ieee80211_conf *conf)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
+
+ if (priv->rf->init == rtl8225_rf_init)
+ rtl8225_rf_set_tx_power(dev, chan);
+ else if (priv->rf->init == rtl8225z2_rf_init)
+ rtl8225z2_rf_set_tx_power(dev, chan);
+ else
+ rtl8225z2_b_rf_set_tx_power(dev, chan);
+
+ rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
+ msleep(10);
+}
+
+static const struct rtl818x_rf_ops rtl8225_ops = {
+ .name = "rtl8225",
+ .init = rtl8225_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel
+};
+
+static const struct rtl818x_rf_ops rtl8225z2_ops = {
+ .name = "rtl8225z2",
+ .init = rtl8225z2_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel
+};
+
+static const struct rtl818x_rf_ops rtl8225z2_b_ops = {
+ .name = "rtl8225z2",
+ .init = rtl8225z2_b_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel
+};
+
+const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev)
+{
+ u16 reg8, reg9;
+ struct rtl8187_priv *priv = dev->priv;
+
+ if (!priv->is_rtl8187b) {
+ rtl8225_write(dev, 0, 0x1B7);
+
+ reg8 = rtl8225_read(dev, 8);
+ reg9 = rtl8225_read(dev, 9);
+
+ rtl8225_write(dev, 0, 0x0B7);
+
+ if (reg8 != 0x588 || reg9 != 0x700)
+ return &rtl8225_ops;
+
+ return &rtl8225z2_ops;
+ } else
+ return &rtl8225z2_b_ops;
+}
diff --git a/drivers/net/wireless/rtl8187_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h
index 20c5b6ead0f6..20c5b6ead0f6 100644
--- a/drivers/net/wireless/rtl8187_rtl8225.h
+++ b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h
diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h
new file mode 100644
index 000000000000..34a5555cc19c
--- /dev/null
+++ b/drivers/net/wireless/rtl818x/rtl818x.h
@@ -0,0 +1,232 @@
+/*
+ * Definitions for RTL818x hardware
+ *
+ * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ *
+ * This program is free software; you can 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 RTL818X_H
+#define RTL818X_H
+
+struct rtl818x_csr {
+ u8 MAC[6];
+ u8 reserved_0[2];
+ __le32 MAR[2];
+ u8 RX_FIFO_COUNT;
+ u8 reserved_1;
+ u8 TX_FIFO_COUNT;
+ u8 BQREQ;
+ u8 reserved_2[4];
+ __le32 TSFT[2];
+ __le32 TLPDA;
+ __le32 TNPDA;
+ __le32 THPDA;
+ __le16 BRSR;
+ u8 BSSID[6];
+ u8 RESP_RATE;
+ u8 EIFS;
+ u8 reserved_3[1];
+ u8 CMD;
+#define RTL818X_CMD_TX_ENABLE (1 << 2)
+#define RTL818X_CMD_RX_ENABLE (1 << 3)
+#define RTL818X_CMD_RESET (1 << 4)
+ u8 reserved_4[4];
+ __le16 INT_MASK;
+ __le16 INT_STATUS;
+#define RTL818X_INT_RX_OK (1 << 0)
+#define RTL818X_INT_RX_ERR (1 << 1)
+#define RTL818X_INT_TXL_OK (1 << 2)
+#define RTL818X_INT_TXL_ERR (1 << 3)
+#define RTL818X_INT_RX_DU (1 << 4)
+#define RTL818X_INT_RX_FO (1 << 5)
+#define RTL818X_INT_TXN_OK (1 << 6)
+#define RTL818X_INT_TXN_ERR (1 << 7)
+#define RTL818X_INT_TXH_OK (1 << 8)
+#define RTL818X_INT_TXH_ERR (1 << 9)
+#define RTL818X_INT_TXB_OK (1 << 10)
+#define RTL818X_INT_TXB_ERR (1 << 11)
+#define RTL818X_INT_ATIM (1 << 12)
+#define RTL818X_INT_BEACON (1 << 13)
+#define RTL818X_INT_TIME_OUT (1 << 14)
+#define RTL818X_INT_TX_FO (1 << 15)
+ __le32 TX_CONF;
+#define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17)
+#define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17)
+#define RTL818X_TX_CONF_NO_ICV (1 << 19)
+#define RTL818X_TX_CONF_DISCW (1 << 20)
+#define RTL818X_TX_CONF_SAT_HWPLCP (1 << 24)
+#define RTL818X_TX_CONF_R8180_ABCD (2 << 25)
+#define RTL818X_TX_CONF_R8180_F (3 << 25)
+#define RTL818X_TX_CONF_R8185_ABC (4 << 25)
+#define RTL818X_TX_CONF_R8185_D (5 << 25)
+#define RTL818X_TX_CONF_R8187vD (5 << 25)
+#define RTL818X_TX_CONF_R8187vD_B (6 << 25)
+#define RTL818X_TX_CONF_HWVER_MASK (7 << 25)
+#define RTL818X_TX_CONF_DISREQQSIZE (1 << 28)
+#define RTL818X_TX_CONF_PROBE_DTS (1 << 29)
+#define RTL818X_TX_CONF_HW_SEQNUM (1 << 30)
+#define RTL818X_TX_CONF_CW_MIN (1 << 31)
+ __le32 RX_CONF;
+#define RTL818X_RX_CONF_MONITOR (1 << 0)
+#define RTL818X_RX_CONF_NICMAC (1 << 1)
+#define RTL818X_RX_CONF_MULTICAST (1 << 2)
+#define RTL818X_RX_CONF_BROADCAST (1 << 3)
+#define RTL818X_RX_CONF_FCS (1 << 5)
+#define RTL818X_RX_CONF_DATA (1 << 18)
+#define RTL818X_RX_CONF_CTRL (1 << 19)
+#define RTL818X_RX_CONF_MGMT (1 << 20)
+#define RTL818X_RX_CONF_ADDR3 (1 << 21)
+#define RTL818X_RX_CONF_PM (1 << 22)
+#define RTL818X_RX_CONF_BSSID (1 << 23)
+#define RTL818X_RX_CONF_RX_AUTORESETPHY (1 << 28)
+#define RTL818X_RX_CONF_CSDM1 (1 << 29)
+#define RTL818X_RX_CONF_CSDM2 (1 << 30)
+#define RTL818X_RX_CONF_ONLYERLPKT (1 << 31)
+ __le32 INT_TIMEOUT;
+ __le32 TBDA;
+ u8 EEPROM_CMD;
+#define RTL818X_EEPROM_CMD_READ (1 << 0)
+#define RTL818X_EEPROM_CMD_WRITE (1 << 1)
+#define RTL818X_EEPROM_CMD_CK (1 << 2)
+#define RTL818X_EEPROM_CMD_CS (1 << 3)
+#define RTL818X_EEPROM_CMD_NORMAL (0 << 6)
+#define RTL818X_EEPROM_CMD_LOAD (1 << 6)
+#define RTL818X_EEPROM_CMD_PROGRAM (2 << 6)
+#define RTL818X_EEPROM_CMD_CONFIG (3 << 6)
+ u8 CONFIG0;
+ u8 CONFIG1;
+ u8 CONFIG2;
+#define RTL818X_CONFIG2_ANTENNA_DIV (1 << 6)
+ __le32 ANAPARAM;
+ u8 MSR;
+#define RTL818X_MSR_NO_LINK (0 << 2)
+#define RTL818X_MSR_ADHOC (1 << 2)
+#define RTL818X_MSR_INFRA (2 << 2)
+#define RTL818X_MSR_MASTER (3 << 2)
+#define RTL818X_MSR_ENEDCA (4 << 2)
+ u8 CONFIG3;
+#define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6)
+#define RTL818X_CONFIG3_GNT_SELECT (1 << 7)
+ u8 CONFIG4;
+#define RTL818X_CONFIG4_POWEROFF (1 << 6)
+#define RTL818X_CONFIG4_VCOOFF (1 << 7)
+ u8 TESTR;
+ u8 reserved_9[2];
+ u8 PGSELECT;
+ u8 SECURITY;
+ __le32 ANAPARAM2;
+ u8 reserved_10[12];
+ __le16 BEACON_INTERVAL;
+ __le16 ATIM_WND;
+ __le16 BEACON_INTERVAL_TIME;
+ __le16 ATIMTR_INTERVAL;
+ u8 PHY_DELAY;
+ u8 CARRIER_SENSE_COUNTER;
+ u8 reserved_11[2];
+ u8 PHY[4];
+ __le16 RFPinsOutput;
+ __le16 RFPinsEnable;
+ __le16 RFPinsSelect;
+ __le16 RFPinsInput;
+ __le32 RF_PARA;
+ __le32 RF_TIMING;
+ u8 GP_ENABLE;
+ u8 GPIO;
+ u8 reserved_12[2];
+ __le32 HSSI_PARA;
+ u8 reserved_13[4];
+ u8 TX_AGC_CTL;
+#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0)
+#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1)
+#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2)
+ u8 TX_GAIN_CCK;
+ u8 TX_GAIN_OFDM;
+ u8 TX_ANTENNA;
+ u8 reserved_14[16];
+ u8 WPA_CONF;
+ u8 reserved_15[3];
+ u8 SIFS;
+ u8 DIFS;
+ u8 SLOT;
+ u8 reserved_16[5];
+ u8 CW_CONF;
+#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0)
+#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1)
+ u8 CW_VAL;
+ u8 RATE_FALLBACK;
+#define RTL818X_RATE_FALLBACK_ENABLE (1 << 7)
+ u8 ACM_CONTROL;
+ u8 reserved_17[24];
+ u8 CONFIG5;
+ u8 TX_DMA_POLLING;
+ u8 reserved_18[2];
+ __le16 CWR;
+ u8 RETRY_CTR;
+ u8 reserved_19[3];
+ __le16 INT_MIG;
+/* RTL818X_R8187B_*: magic numbers from ioregisters */
+#define RTL818X_R8187B_B 0
+#define RTL818X_R8187B_D 1
+#define RTL818X_R8187B_E 2
+ __le32 RDSAR;
+ __le16 TID_AC_MAP;
+ u8 reserved_20[4];
+ u8 ANAPARAM3;
+ u8 reserved_21[5];
+ __le16 FEMR;
+ u8 reserved_22[4];
+ __le16 TALLY_CNT;
+ u8 TALLY_SEL;
+} __attribute__((packed));
+
+struct rtl818x_rf_ops {
+ char *name;
+ void (*init)(struct ieee80211_hw *);
+ void (*stop)(struct ieee80211_hw *);
+ void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *);
+ void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *);
+};
+
+/* Tx/Rx flags are common between RTL818X chips */
+
+enum rtl818x_tx_desc_flags {
+ RTL818X_TX_DESC_FLAG_NO_ENC = (1 << 15),
+ RTL818X_TX_DESC_FLAG_TX_OK = (1 << 15),
+ RTL818X_TX_DESC_FLAG_SPLCP = (1 << 16),
+ RTL818X_TX_DESC_FLAG_RX_UNDER = (1 << 16),
+ RTL818X_TX_DESC_FLAG_MOREFRAG = (1 << 17),
+ RTL818X_TX_DESC_FLAG_CTS = (1 << 18),
+ RTL818X_TX_DESC_FLAG_RTS = (1 << 23),
+ RTL818X_TX_DESC_FLAG_LS = (1 << 28),
+ RTL818X_TX_DESC_FLAG_FS = (1 << 29),
+ RTL818X_TX_DESC_FLAG_DMA = (1 << 30),
+ RTL818X_TX_DESC_FLAG_OWN = (1 << 31)
+};
+
+enum rtl818x_rx_desc_flags {
+ RTL818X_RX_DESC_FLAG_ICV_ERR = (1 << 12),
+ RTL818X_RX_DESC_FLAG_CRC32_ERR = (1 << 13),
+ RTL818X_RX_DESC_FLAG_PM = (1 << 14),
+ RTL818X_RX_DESC_FLAG_RX_ERR = (1 << 15),
+ RTL818X_RX_DESC_FLAG_BCAST = (1 << 16),
+ RTL818X_RX_DESC_FLAG_PAM = (1 << 17),
+ RTL818X_RX_DESC_FLAG_MCAST = (1 << 18),
+ RTL818X_RX_DESC_FLAG_QOS = (1 << 19), /* RTL8187(B) only */
+ RTL818X_RX_DESC_FLAG_TRSW = (1 << 24), /* RTL8187(B) only */
+ RTL818X_RX_DESC_FLAG_SPLCP = (1 << 25),
+ RTL818X_RX_DESC_FLAG_FOF = (1 << 26),
+ RTL818X_RX_DESC_FLAG_DMA_FAIL = (1 << 27),
+ RTL818X_RX_DESC_FLAG_LS = (1 << 28),
+ RTL818X_RX_DESC_FLAG_FS = (1 << 29),
+ RTL818X_RX_DESC_FLAG_EOR = (1 << 30),
+ RTL818X_RX_DESC_FLAG_OWN = (1 << 31)
+};
+
+#endif /* RTL818X_H */
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
deleted file mode 100644
index 852789ad34b3..000000000000
--- a/drivers/net/wireless/spectrum_cs.c
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
- * Symbol Wireless Networker LA4137, CompactFlash cards by Socket
- * Communications and Intel PRO/Wireless 2011B.
- *
- * The driver implements Symbol firmware download. The rest is handled
- * in hermes.c and orinoco.c.
- *
- * Utilities for downloading the Symbol firmware are available at
- * http://sourceforge.net/projects/orinoco/
- *
- * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
- * Portions based on orinoco_cs.c:
- * Copyright (C) David Gibson, Linuxcare Australia
- * Portions based on Spectrum24tDnld.c from original spectrum24 driver:
- * Copyright (C) Symbol Technologies.
- *
- * See copyright notice in file orinoco.c.
- */
-
-#define DRIVER_NAME "spectrum_cs"
-#define PFX DRIVER_NAME ": "
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-
-#include "orinoco.h"
-
-/********************************************************************/
-/* Module stuff */
-/********************************************************************/
-
-MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
-MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
-MODULE_LICENSE("Dual MPL/GPL");
-
-/* Module parameters */
-
-/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
- * don't have any CIS entry for it. This workaround it... */
-static int ignore_cis_vcc; /* = 0 */
-module_param(ignore_cis_vcc, int, 0);
-MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
-
-/********************************************************************/
-/* Data structures */
-/********************************************************************/
-
-/* PCMCIA specific device information (goes in the card field of
- * struct orinoco_private */
-struct orinoco_pccard {
- struct pcmcia_device *p_dev;
- dev_node_t node;
-};
-
-/********************************************************************/
-/* Function prototypes */
-/********************************************************************/
-
-static int spectrum_cs_config(struct pcmcia_device *link);
-static void spectrum_cs_release(struct pcmcia_device *link);
-
-/* Constants for the CISREG_CCSR register */
-#define HCR_RUN 0x07 /* run firmware after reset */
-#define HCR_IDLE 0x0E /* don't run firmware after reset */
-#define HCR_MEM16 0x10 /* memory width bit, should be preserved */
-
-
-#define CS_CHECK(fn, ret) \
- do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
-/*
- * Reset the card using configuration registers COR and CCSR.
- * If IDLE is 1, stop the firmware, so that it can be safely rewritten.
- */
-static int
-spectrum_reset(struct pcmcia_device *link, int idle)
-{
- int last_ret, last_fn;
- conf_reg_t reg;
- u_int save_cor;
-
- /* Doing it if hardware is gone is guaranteed crash */
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- /* Save original COR value */
- reg.Function = 0;
- reg.Action = CS_READ;
- reg.Offset = CISREG_COR;
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
- save_cor = reg.Value;
-
- /* Soft-Reset card */
- reg.Action = CS_WRITE;
- reg.Offset = CISREG_COR;
- reg.Value = (save_cor | COR_SOFT_RESET);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
- udelay(1000);
-
- /* Read CCSR */
- reg.Action = CS_READ;
- reg.Offset = CISREG_CCSR;
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
-
- /*
- * Start or stop the firmware. Memory width bit should be
- * preserved from the value we've just read.
- */
- reg.Action = CS_WRITE;
- reg.Offset = CISREG_CCSR;
- reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
- udelay(1000);
-
- /* Restore original COR configuration index */
- reg.Action = CS_WRITE;
- reg.Offset = CISREG_COR;
- reg.Value = (save_cor & ~COR_SOFT_RESET);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
- udelay(1000);
- return 0;
-
- cs_failed:
- cs_error(link, last_fn, last_ret);
- return -ENODEV;
-}
-
-/********************************************************************/
-/* Device methods */
-/********************************************************************/
-
-static int
-spectrum_cs_hard_reset(struct orinoco_private *priv)
-{
- struct orinoco_pccard *card = priv->card;
- struct pcmcia_device *link = card->p_dev;
-
- /* Soft reset using COR and HCR */
- spectrum_reset(link, 0);
-
- return 0;
-}
-
-static int
-spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
-{
- struct orinoco_pccard *card = priv->card;
- struct pcmcia_device *link = card->p_dev;
-
- return spectrum_reset(link, idle);
-}
-
-/********************************************************************/
-/* PCMCIA stuff */
-/********************************************************************/
-
-/*
- * This creates an "instance" of the driver, allocating local data
- * structures for one device. The device is registered with Card
- * Services.
- *
- * The dev_link structure is initialized, but we don't actually
- * configure the card at this point -- we wait until we receive a card
- * insertion event. */
-static int
-spectrum_cs_probe(struct pcmcia_device *link)
-{
- struct net_device *dev;
- struct orinoco_private *priv;
- struct orinoco_pccard *card;
-
- dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
- spectrum_cs_hard_reset,
- spectrum_cs_stop_firmware);
- if (! dev)
- return -ENOMEM;
- priv = netdev_priv(dev);
- card = priv->card;
-
- /* Link both structures together */
- card->p_dev = link;
- link->priv = dev;
-
- /* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
- link->irq.Handler = orinoco_interrupt;
- link->irq.Instance = dev;
-
- /* General socket configuration defaults can go here. In this
- * client, we assume very little, and rely on the CIS for
- * almost everything. In most clients, many details (i.e.,
- * number, sizes, and attributes of IO windows) are fixed by
- * the nature of the device, and can be hard-wired here. */
- link->conf.Attributes = 0;
- link->conf.IntType = INT_MEMORY_AND_IO;
-
- return spectrum_cs_config(link);
-} /* spectrum_cs_attach */
-
-/*
- * This deletes a driver "instance". The device is de-registered with
- * Card Services. If it has been released, all local data structures
- * are freed. Otherwise, the structures will be freed when the device
- * is released.
- */
-static void spectrum_cs_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->dev_node)
- unregister_netdev(dev);
-
- spectrum_cs_release(link);
-
- free_orinocodev(dev);
-} /* spectrum_cs_detach */
-
-/*
- * spectrum_cs_config() is scheduled to run after a CARD_INSERTION
- * event is received, to configure the PCMCIA socket, and to make the
- * device available to the system.
- */
-
-static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
- cistpl_cftable_entry_t *cfg,
- cistpl_cftable_entry_t *dflt,
- unsigned int vcc,
- void *priv_data)
-{
- if (cfg->index == 0)
- goto next_entry;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- p_dev->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
- p_dev->conf.Vpp =
- dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- p_dev->io.BasePort1 = io->win[0].base;
- p_dev->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- p_dev->io.Attributes2 = p_dev->io.Attributes1;
- p_dev->io.BasePort2 = io->win[1].base;
- p_dev->io.NumPorts2 = io->win[1].len;
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
- goto next_entry;
- }
- return 0;
-
-next_entry:
- pcmcia_disable_device(p_dev);
- return -ENODEV;
-};
-
-static int
-spectrum_cs_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- struct orinoco_pccard *card = priv->card;
- hermes_t *hw = &priv->hw;
- int last_fn, last_ret;
- void __iomem *mem;
-
- /*
- * In this loop, we scan the CIS for configuration table
- * entries, each of which describes a valid card
- * configuration, including voltage, IO window, memory window,
- * and interrupt settings.
- *
- * We make no assumptions about the card to be configured: we
- * use just the information available in the CIS. In an ideal
- * world, this would work for any PCMCIA card, but it requires
- * a complete and accurate CIS. In practice, a driver usually
- * "knows" most of these things without consulting the CIS,
- * and most client drivers will only use the CIS to fill in
- * implementation-defined details.
- */
- last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
- if (last_ret) {
- if (!ignore_cis_vcc)
- printk(KERN_ERR PFX "GetNextTuple(): No matching "
- "CIS configuration. Maybe you need the "
- "ignore_cis_vcc=1 parameter.\n");
- cs_error(link, RequestIO, last_ret);
- goto failed;
- }
-
- /*
- * Allocate an interrupt line. Note that this does not assign
- * a handler to the interrupt, unless the 'Handler' member of
- * the irq structure is initialized.
- */
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
-
- /* We initialize the hermes structure before completing PCMCIA
- * configuration just in case the interrupt handler gets
- * called. */
- mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
- if (!mem)
- goto cs_failed;
-
- hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
-
- /*
- * This actually configures the PCMCIA socket -- setting up
- * the I/O windows and the interrupt mapping, and putting the
- * card and host interface into "Memory and IO" mode.
- */
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
-
- /* Ok, we have the configuration, prepare to register the netdev */
- dev->base_addr = link->io.BasePort1;
- dev->irq = link->irq.AssignedIRQ;
- card->node.major = card->node.minor = 0;
-
- /* Reset card */
- if (spectrum_cs_hard_reset(priv) != 0) {
- goto failed;
- }
-
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
- /* Tell the stack we exist */
- if (register_netdev(dev) != 0) {
- printk(KERN_ERR PFX "register_netdev() failed\n");
- goto failed;
- }
-
- /* At this point, the dev_node_t structure(s) needs to be
- * initialized and arranged in a linked list at link->dev_node. */
- strcpy(card->node.dev_name, dev->name);
- link->dev_node = &card->node; /* link->dev_node being non-NULL is also
- used to indicate that the
- net_device has been registered */
-
- /* Finally, report what we've done */
- printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io "
- "0x%04x-0x%04x\n", dev->name, dev->dev.parent->bus_id,
- link->irq.AssignedIRQ, link->io.BasePort1,
- link->io.BasePort1 + link->io.NumPorts1 - 1);
-
- return 0;
-
- cs_failed:
- cs_error(link, last_fn, last_ret);
-
- failed:
- spectrum_cs_release(link);
- return -ENODEV;
-} /* spectrum_cs_config */
-
-/*
- * After a card is removed, spectrum_cs_release() will unregister the
- * device, and release the PCMCIA configuration. If the device is
- * still open, this will be postponed until it is closed.
- */
-static void
-spectrum_cs_release(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- /* We're committed to taking the device away now, so mark the
- * hardware as unavailable */
- spin_lock_irqsave(&priv->lock, flags);
- priv->hw_unavailable++;
- spin_unlock_irqrestore(&priv->lock, flags);
-
- pcmcia_disable_device(link);
- if (priv->hw.iobase)
- ioport_unmap(priv->hw.iobase);
-} /* spectrum_cs_release */
-
-
-static int
-spectrum_cs_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
- unsigned long flags;
- int err = 0;
-
- /* Mark the device as stopped, to block IO until later */
- spin_lock_irqsave(&priv->lock, flags);
-
- err = __orinoco_down(dev);
- if (err)
- printk(KERN_WARNING "%s: Error %d downing interface\n",
- dev->name, err);
-
- netif_device_detach(dev);
- priv->hw_unavailable++;
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return err;
-}
-
-static int
-spectrum_cs_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct orinoco_private *priv = netdev_priv(dev);
-
- netif_device_attach(dev);
- priv->hw_unavailable--;
- schedule_work(&priv->reset_work);
-
- return 0;
-}
-
-
-/********************************************************************/
-/* Module initialization */
-/********************************************************************/
-
-/* Can't be declared "const" or the whole __initdata section will
- * become const */
-static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
- " (Pavel Roskin <proski@gnu.org>,"
- " David Gibson <hermes@gibson.dropbear.id.au>, et al)";
-
-static struct pcmcia_device_id spectrum_cs_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
- PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
-
-static struct pcmcia_driver orinoco_driver = {
- .owner = THIS_MODULE,
- .drv = {
- .name = DRIVER_NAME,
- },
- .probe = spectrum_cs_probe,
- .remove = spectrum_cs_detach,
- .suspend = spectrum_cs_suspend,
- .resume = spectrum_cs_resume,
- .id_table = spectrum_cs_ids,
-};
-
-static int __init
-init_spectrum_cs(void)
-{
- printk(KERN_DEBUG "%s\n", version);
-
- return pcmcia_register_driver(&orinoco_driver);
-}
-
-static void __exit
-exit_spectrum_cs(void)
-{
- pcmcia_unregister_driver(&orinoco_driver);
-}
-
-module_init(init_spectrum_cs);
-module_exit(exit_spectrum_cs);
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index 417e9e675fac..dd0de3a9ed4e 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -1234,7 +1234,7 @@ static void ResetRadio(struct strip *strip_info)
static void strip_write_some_more(struct tty_struct *tty)
{
- struct strip *strip_info = (struct strip *) tty->disc_data;
+ struct strip *strip_info = tty->disc_data;
/* First make sure we're connected. */
if (!strip_info || strip_info->magic != STRIP_MAGIC ||
@@ -1252,7 +1252,7 @@ static void strip_write_some_more(struct tty_struct *tty)
#endif
} else { /* Else start transmission of another packet */
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
strip_unlock(strip_info);
}
}
@@ -1455,8 +1455,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
*/
strip_info->tx_head = strip_info->tx_buff;
strip_info->tx_left = ptr - strip_info->tx_buff;
- strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-
+ set_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags);
/*
* 4. Debugging check to make sure we're not overflowing the buffer.
*/
@@ -1997,7 +1996,6 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header * header,
#ifdef EXT_COUNTERS
strip_info->rx_bytes += packetlen;
#endif
- skb->dev->last_rx = jiffies;
netif_rx(skb);
}
}
@@ -2261,7 +2259,7 @@ static void process_message(struct strip *strip_info)
static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
- struct strip *strip_info = (struct strip *) tty->disc_data;
+ struct strip *strip_info = tty->disc_data;
const unsigned char *end = cp + count;
if (!strip_info || strip_info->magic != STRIP_MAGIC
@@ -2455,8 +2453,7 @@ static int strip_close_low(struct net_device *dev)
if (strip_info->tty == NULL)
return -EBUSY;
- strip_info->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
+ clear_bit(TTY_DO_WRITE_WAKEUP, &strip_info->tty->flags);
netif_stop_queue(dev);
/*
@@ -2490,7 +2487,6 @@ static void strip_dev_setup(struct net_device *dev)
*/
dev->trans_start = 0;
- dev->last_rx = 0;
dev->tx_queue_len = 30; /* Drop after 30 frames queued */
dev->flags = 0;
@@ -2498,7 +2494,7 @@ static void strip_dev_setup(struct net_device *dev)
dev->type = ARPHRD_METRICOM; /* dtang */
dev->hard_header_len = sizeof(STRIP_Header);
/*
- * dev->priv Already holds a pointer to our struct strip
+ * netdev_priv(dev) Already holds a pointer to our struct strip
*/
*(MetricomAddress *) & dev->broadcast = broadcast_address;
@@ -2598,7 +2594,7 @@ static struct strip *strip_alloc(void)
static int strip_open(struct tty_struct *tty)
{
- struct strip *strip_info = (struct strip *) tty->disc_data;
+ struct strip *strip_info = tty->disc_data;
/*
* First make sure we're not already connected.
@@ -2669,7 +2665,7 @@ static int strip_open(struct tty_struct *tty)
static void strip_close(struct tty_struct *tty)
{
- struct strip *strip_info = (struct strip *) tty->disc_data;
+ struct strip *strip_info = tty->disc_data;
/*
* First make sure we're connected.
@@ -2695,7 +2691,7 @@ static void strip_close(struct tty_struct *tty)
static int strip_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct strip *strip_info = (struct strip *) tty->disc_data;
+ struct strip *strip_info = tty->disc_data;
/*
* First make sure we're connected.
diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
index e939a73ff794..832679396b6c 100644
--- a/drivers/net/wireless/wavelan.c
+++ b/drivers/net/wireless/wavelan.c
@@ -134,7 +134,7 @@ static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
*/
static inline void wv_ints_off(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
lp->hacr &= ~HACR_INTRON;
@@ -148,7 +148,7 @@ static inline void wv_ints_off(struct net_device * dev)
*/
static inline void wv_ints_on(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
lp->hacr |= HACR_INTRON;
@@ -526,7 +526,7 @@ static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
*/
static void wv_ack(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u16 scb_cs;
int i;
@@ -568,7 +568,7 @@ static void wv_ack(struct net_device * dev)
*/
static int wv_synchronous_cmd(struct net_device * dev, const char *str)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u16 scb_cmd;
ach_t cb;
@@ -824,7 +824,7 @@ if (lp->tx_n_in_use > 0)
*/
static void wv_82586_reconfig(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long flags;
/* Arm the flag, will be cleard in wv_82586_config() */
@@ -859,8 +859,6 @@ static void wv_82586_reconfig(struct net_device * dev)
*/
static void wv_psa_show(psa_t * p)
{
- DECLARE_MAC_BUF(mac);
-
printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
p->psa_io_base_addr_1,
@@ -872,13 +870,10 @@ static void wv_psa_show(psa_t * p)
printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
printk("psa_int_req_no: %d\n", p->psa_int_req_no);
#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "psa_unused0[]: %s\n",
- print_mac(mac, p->psa_unused0));
+ printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0);
#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "psa_univ_mac_addr[]: %s\n",
- print_mac(mac, p->psa_univ_mac_addr));
- printk(KERN_DEBUG "psa_local_mac_addr[]: %s\n",
- print_mac(mac, p->psa_local_mac_addr));
+ printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr);
+ printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr);
printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
p->psa_univ_local_sel);
printk("psa_comp_number: %d, ", p->psa_comp_number);
@@ -927,7 +922,7 @@ static void wv_psa_show(psa_t * p)
static void wv_mmc_show(struct net_device * dev)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
mmr_t m;
/* Basic check */
@@ -1107,8 +1102,6 @@ static void wv_scb_show(unsigned long ioaddr)
*/
static void wv_ru_show(struct net_device * dev)
{
- /* net_local *lp = (net_local *) dev->priv; */
-
printk(KERN_DEBUG
"##### WaveLAN i82586 receiver unit status: #####\n");
printk(KERN_DEBUG "ru:");
@@ -1153,7 +1146,7 @@ static void wv_cu_show_one(struct net_device * dev, net_local * lp, int i, u16 p
*/
static void wv_cu_show(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned int i;
u16 p;
@@ -1195,7 +1188,7 @@ static void wv_local_show(struct net_device * dev)
{
net_local *lp;
- lp = (net_local *) dev->priv;
+ lp = netdev_priv(dev);
printk(KERN_DEBUG "local:");
printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
@@ -1220,14 +1213,13 @@ static inline void wv_packet_info(u8 * p, /* Packet to dump */
{ /* Name of the function */
int i;
int maxi;
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG
- "%s: %s(): dest %s, length %d\n",
- msg1, msg2, print_mac(mac, p), length);
+ "%s: %s(): dest %pM, length %d\n",
+ msg1, msg2, p, length);
printk(KERN_DEBUG
- "%s: %s(): src %s, type 0x%02X%02X\n",
- msg1, msg2, print_mac(mac, &p[6]), p[12], p[13]);
+ "%s: %s(): src %pM, type 0x%02X%02X\n",
+ msg1, msg2, &p[6], p[12], p[13]);
#ifdef DEBUG_PACKET_DUMP
@@ -1256,11 +1248,8 @@ static inline void wv_packet_info(u8 * p, /* Packet to dump */
static void wv_init_info(struct net_device * dev)
{
short ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
psa_t psa;
-#ifdef DEBUG_BASIC_SHOW
- DECLARE_MAC_BUF(mac);
-#endif
/* Read the parameter storage area */
psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
@@ -1277,8 +1266,8 @@ static void wv_init_info(struct net_device * dev)
#ifdef DEBUG_BASIC_SHOW
/* Now, let's go for the basic stuff. */
- printk(KERN_NOTICE "%s: WaveLAN at %#x, %s, IRQ %d",
- dev->name, ioaddr, print_mac(mac, dev->dev_addr), dev->irq);
+ printk(KERN_NOTICE "%s: WaveLAN at %#x, %pM, IRQ %d",
+ dev->name, ioaddr, dev->dev_addr, dev->irq);
/* Print current network ID. */
if (psa.psa_nwid_select)
@@ -1369,7 +1358,7 @@ static en_stats *wavelan_get_stats(struct net_device * dev)
printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
#endif
- return (&((net_local *) dev->priv)->stats);
+ return &((net_local *)netdev_priv(dev))->stats;
}
/*------------------------------------------------------------------*/
@@ -1382,7 +1371,7 @@ static en_stats *wavelan_get_stats(struct net_device * dev)
*/
static void wavelan_set_multicast_list(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
#ifdef DEBUG_IOCTL_TRACE
printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n",
@@ -1716,7 +1705,7 @@ static inline void wl_spy_gather(struct net_device * dev,
*/
static inline void wl_his_gather(struct net_device * dev, u8 * stats)
{ /* Statistics to gather */
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
u8 level = stats[0] & MMR_SIGNAL_LVL;
int i;
@@ -1753,7 +1742,7 @@ static int wavelan_set_nwid(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
mm_t m;
unsigned long flags;
@@ -1812,7 +1801,7 @@ static int wavelan_get_nwid(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
int ret = 0;
@@ -1844,7 +1833,7 @@ static int wavelan_set_freq(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
unsigned long flags;
int ret;
@@ -1874,7 +1863,7 @@ static int wavelan_get_freq(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
int ret = 0;
@@ -1920,7 +1909,7 @@ static int wavelan_set_sens(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
int ret = 0;
@@ -1956,7 +1945,7 @@ static int wavelan_get_sens(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
int ret = 0;
@@ -1987,7 +1976,7 @@ static int wavelan_set_encode(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
unsigned long flags;
psa_t psa;
int ret = 0;
@@ -2057,7 +2046,7 @@ static int wavelan_get_encode(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
int ret = 0;
@@ -2104,7 +2093,7 @@ static int wavelan_get_range(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
struct iw_range *range = (struct iw_range *) extra;
unsigned long flags;
int ret = 0;
@@ -2179,7 +2168,7 @@ static int wavelan_set_qthr(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
@@ -2211,7 +2200,7 @@ static int wavelan_get_qthr(struct net_device *dev,
char *extra)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
psa_t psa;
unsigned long flags;
@@ -2239,7 +2228,7 @@ static int wavelan_set_histo(struct net_device *dev,
union iwreq_data *wrqu,
char *extra)
{
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
/* Check the number of intervals. */
if (wrqu->data.length > 16) {
@@ -2282,7 +2271,7 @@ static int wavelan_get_histo(struct net_device *dev,
union iwreq_data *wrqu,
char *extra)
{
- net_local *lp = (net_local *) dev->priv; /* lp is not unused */
+ net_local *lp = netdev_priv(dev); /* lp is not unused */
/* Set the number of intervals. */
wrqu->data.length = lp->his_number;
@@ -2386,7 +2375,7 @@ static const struct iw_handler_def wavelan_handler_def =
static iw_stats *wavelan_get_wireless_stats(struct net_device * dev)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
mmr_t m;
iw_stats *wstats;
unsigned long flags;
@@ -2461,7 +2450,7 @@ static iw_stats *wavelan_get_wireless_stats(struct net_device * dev)
static void
wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
struct sk_buff *skb;
@@ -2537,7 +2526,6 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
netif_rx(skb);
/* Keep statistics up to date */
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += sksize;
@@ -2556,7 +2544,7 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize)
static void wv_receive(struct net_device * dev)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
fd_t fd;
rbd_t rbd;
int nreaped = 0;
@@ -2738,7 +2726,7 @@ static void wv_receive(struct net_device * dev)
*/
static int wv_packet_write(struct net_device * dev, void *buf, short length)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
unsigned short txblock;
unsigned short txpred;
@@ -2869,7 +2857,7 @@ static int wv_packet_write(struct net_device * dev, void *buf, short length)
*/
static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long flags;
char data[ETH_ZLEN];
@@ -2937,7 +2925,7 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev)
static int wv_mmc_init(struct net_device * dev)
{
unsigned long ioaddr = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
psa_t psa;
mmw_t m;
int configured;
@@ -3108,7 +3096,7 @@ static int wv_mmc_init(struct net_device * dev)
*/
static int wv_ru_start(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u16 scb_cs;
fd_t fd;
@@ -3200,7 +3188,7 @@ static int wv_ru_start(struct net_device * dev)
*/
static int wv_cu_start(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
int i;
u16 txblock;
@@ -3301,7 +3289,7 @@ static int wv_cu_start(struct net_device * dev)
*/
static int wv_82586_start(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
scp_t scp; /* system configuration pointer */
iscp_t iscp; /* intermediate scp */
@@ -3433,7 +3421,7 @@ static int wv_82586_start(struct net_device * dev)
*/
static void wv_82586_config(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
unsigned short txblock;
unsigned short txpred;
@@ -3565,15 +3553,11 @@ static void wv_82586_config(struct net_device * dev)
WAVELAN_ADDR_SIZE >> 1);
#ifdef DEBUG_CONFIG_INFO
- {
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG
"%s: wv_82586_config(): set %d multicast addresses:\n",
dev->name, lp->mc_count);
for (dmi = dev->mc_list; dmi; dmi = dmi->next)
- printk(KERN_DEBUG " %s\n",
- print_mac(mac, dmi->dmi_addr));
- }
+ printk(KERN_DEBUG " %pM\n", dmi->dmi_addr);
#endif
}
@@ -3613,7 +3597,7 @@ static void wv_82586_config(struct net_device * dev)
*/
static void wv_82586_stop(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
u16 scb_cmd;
@@ -3650,7 +3634,7 @@ static void wv_82586_stop(struct net_device * dev)
*/
static int wv_hw_reset(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long ioaddr = dev->base_addr;
#ifdef DEBUG_CONFIG_TRACE
@@ -3751,7 +3735,7 @@ static irqreturn_t wavelan_interrupt(int irq, void *dev_id)
printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
#endif
- lp = (net_local *) dev->priv;
+ lp = netdev_priv(dev);
ioaddr = dev->base_addr;
#ifdef DEBUG_INTERRUPT_INFO
@@ -3894,7 +3878,7 @@ static irqreturn_t wavelan_interrupt(int irq, void *dev_id)
*/
static void wavelan_watchdog(struct net_device * dev)
{
- net_local * lp = (net_local *)dev->priv;
+ net_local *lp = netdev_priv(dev);
u_long ioaddr = dev->base_addr;
unsigned long flags;
unsigned int nreaped;
@@ -3974,7 +3958,7 @@ static void wavelan_watchdog(struct net_device * dev)
*/
static int wavelan_open(struct net_device * dev)
{
- net_local * lp = (net_local *)dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long flags;
#ifdef DEBUG_CALLBACK_TRACE
@@ -4029,7 +4013,7 @@ static int wavelan_open(struct net_device * dev)
*/
static int wavelan_close(struct net_device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local *lp = netdev_priv(dev);
unsigned long flags;
#ifdef DEBUG_CALLBACK_TRACE
@@ -4128,8 +4112,8 @@ static int __init wavelan_config(struct net_device *dev, unsigned short ioaddr)
dev->if_port = 0;
/* Initialize device structures */
- memset(dev->priv, 0, sizeof(net_local));
- lp = (net_local *) dev->priv;
+ memset(netdev_priv(dev), 0, sizeof(net_local));
+ lp = netdev_priv(dev);
/* Back link to the device structure. */
lp->dev = dev;
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index e124b1d6267a..de717f8ffd61 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -1020,7 +1020,6 @@ wv_82593_reconfig(struct net_device * dev)
static void
wv_psa_show(psa_t * p)
{
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
p->psa_io_base_addr_1,
@@ -1034,13 +1033,10 @@ wv_psa_show(psa_t * p)
printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
printk("psa_int_req_no: %d\n", p->psa_int_req_no);
#ifdef DEBUG_SHOW_UNUSED
- printk(KERN_DEBUG "psa_unused0[]: %s\n",
- print_mac(mac, p->psa_unused0));
+ printk(KERN_DEBUG "psa_unused0[]: %pM\n", p->psa_unused0);
#endif /* DEBUG_SHOW_UNUSED */
- printk(KERN_DEBUG "psa_univ_mac_addr[]: %s\n",
- print_mac(mac, p->psa_univ_mac_addr));
- printk(KERN_DEBUG "psa_local_mac_addr[]: %s\n",
- print_mac(mac, p->psa_local_mac_addr));
+ printk(KERN_DEBUG "psa_univ_mac_addr[]: %pM\n", p->psa_univ_mac_addr);
+ printk(KERN_DEBUG "psa_local_mac_addr[]: %pM\n", p->psa_local_mac_addr);
printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
printk("psa_comp_number: %d, ", p->psa_comp_number);
printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
@@ -1238,12 +1234,11 @@ wv_packet_info(u_char * p, /* Packet to dump */
{
int i;
int maxi;
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "%s: %s(): dest %s, length %d\n",
- msg1, msg2, print_mac(mac, p), length);
- printk(KERN_DEBUG "%s: %s(): src %s, type 0x%02X%02X\n",
- msg1, msg2, print_mac(mac, &p[6]), p[12], p[13]);
+ printk(KERN_DEBUG "%s: %s(): dest %pM, length %d\n",
+ msg1, msg2, p, length);
+ printk(KERN_DEBUG "%s: %s(): src %pM, type 0x%02X%02X\n",
+ msg1, msg2, &p[6], p[12], p[13]);
#ifdef DEBUG_PACKET_DUMP
@@ -1274,7 +1269,6 @@ wv_init_info(struct net_device * dev)
{
unsigned int base = dev->base_addr;
psa_t psa;
- DECLARE_MAC_BUF(mac);
/* Read the parameter storage area */
psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
@@ -1291,10 +1285,8 @@ wv_init_info(struct net_device * dev)
#ifdef DEBUG_BASIC_SHOW
/* Now, let's go for the basic stuff */
- printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, "
- "hw_addr %s",
- dev->name, base, dev->irq,
- print_mac(mac, dev->dev_addr));
+ printk(KERN_NOTICE "%s: WaveLAN: port %#x, irq %d, hw_addr %pM",
+ dev->name, base, dev->irq, dev->dev_addr);
/* Print current network id */
if(psa.psa_nwid_select)
@@ -2243,13 +2235,7 @@ static int wavelan_set_wap(struct net_device *dev,
char *extra)
{
#ifdef DEBUG_IOCTL_INFO
- printk(KERN_DEBUG "Set AP to : %02X:%02X:%02X:%02X:%02X:%02X\n",
- wrqu->ap_addr.sa_data[0],
- wrqu->ap_addr.sa_data[1],
- wrqu->ap_addr.sa_data[2],
- wrqu->ap_addr.sa_data[3],
- wrqu->ap_addr.sa_data[4],
- wrqu->ap_addr.sa_data[5]);
+ printk(KERN_DEBUG "Set AP to : %pM\n", wrqu->ap_addr.sa_data);
#endif /* DEBUG_IOCTL_INFO */
return -EOPNOTSUPP;
@@ -2892,7 +2878,6 @@ wv_packet_read(struct net_device * dev,
netif_rx(skb);
/* Keep stats up to date */
- dev->last_rx = jiffies;
lp->stats.rx_packets++;
lp->stats.rx_bytes += sksize;
@@ -3647,12 +3632,10 @@ wv_82593_config(struct net_device * dev)
int addrs_len = WAVELAN_ADDR_SIZE * lp->mc_count;
#ifdef DEBUG_CONFIG_INFO
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: wv_hw_config(): set %d multicast addresses:\n",
dev->name, lp->mc_count);
for(dmi=dev->mc_list; dmi; dmi=dmi->next)
- printk(KERN_DEBUG " %s\n",
- print_mac(mac, dmi->dmi_addr));
+ printk(KERN_DEBUG " %pM\n", dmi->dmi_addr);
#endif
/* Initialize adapter's ethernet multicast addresses */
diff --git a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h
index 65ceb088f700..59bb3a55ab48 100644
--- a/drivers/net/wireless/wl3501.h
+++ b/drivers/net/wireless/wl3501.h
@@ -2,7 +2,7 @@
#define __WL3501_H__
#include <linux/spinlock.h>
-#include <net/ieee80211.h>
+#include <linux/ieee80211.h>
/* define for WLA 2.0 */
#define WL3501_BLKSZ 256
@@ -548,7 +548,7 @@ struct wl3501_80211_tx_plcp_hdr {
struct wl3501_80211_tx_hdr {
struct wl3501_80211_tx_plcp_hdr pclp_hdr;
- struct ieee80211_hdr_4addr mac_hdr;
+ struct ieee80211_hdr mac_hdr;
} __attribute__ ((packed));
/*
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index 68789c6e1ce9..c99a1b6b948f 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -860,10 +860,9 @@ static int wl3501_esbq_confirm(struct wl3501_card *this)
static void wl3501_online(struct net_device *dev)
{
struct wl3501_card *this = netdev_priv(dev);
- DECLARE_MAC_BUF(mac);
- printk(KERN_INFO "%s: Wireless LAN online. BSSID: %s\n",
- dev->name, print_mac(mac, this->bssid));
+ printk(KERN_INFO "%s: Wireless LAN online. BSSID: %pM\n",
+ dev->name, this->bssid);
netif_wake_queue(dev);
}
@@ -1014,7 +1013,6 @@ static inline void wl3501_md_ind_interrupt(struct net_device *dev,
wl3501_receive(this, skb->data, pkt_len);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, dev);
- dev->last_rx = jiffies;
this->stats.rx_packets++;
this->stats.rx_bytes += skb->len;
netif_rx(skb);
@@ -1965,7 +1963,6 @@ static int wl3501_config(struct pcmcia_device *link)
struct net_device *dev = link->priv;
int i = 0, j, last_fn, last_ret;
struct wl3501_card *this;
- DECLARE_MAC_BUF(mac);
/* Try allocating IO ports. This tries a few fixed addresses. If you
* want, you can also read the card's config table to pick addresses --
@@ -2024,9 +2021,9 @@ static int wl3501_config(struct pcmcia_device *link)
/* print probe information */
printk(KERN_INFO "%s: wl3501 @ 0x%3.3x, IRQ %d, "
- "MAC addr in flash ROM:%s\n",
+ "MAC addr in flash ROM:%pM\n",
dev->name, this->base_addr, (int)dev->irq,
- print_mac(mac, dev->dev_addr));
+ dev->dev_addr);
/*
* Initialize card parameters - added by jss
*/
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index b16ec6e5f0e3..b45c27d42fd8 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -17,11 +17,11 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
+#include <linux/ieee80211.h>
#include <net/iw_handler.h>
#include <linux/string.h>
#include <linux/if_arp.h>
#include <linux/firmware.h>
-#include <net/ieee80211.h>
#include "zd1201.h"
static struct usb_device_id zd1201_table[] = {
@@ -328,7 +328,6 @@ static void zd1201_usbrx(struct urb *urb)
memcpy(skb_put(skb, 2), &data[datalen-24], 2);
memcpy(skb_put(skb, len), data, len);
skb->protocol = eth_type_trans(skb, zd->dev);
- skb->dev->last_rx = jiffies;
zd->stats.rx_packets++;
zd->stats.rx_bytes += skb->len;
netif_rx(skb);
@@ -346,7 +345,7 @@ static void zd1201_usbrx(struct urb *urb)
frag = kmalloc(sizeof(*frag), GFP_ATOMIC);
if (!frag)
goto resubmit;
- skb = dev_alloc_skb(IEEE80211_DATA_LEN +14+2);
+ skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2);
if (!skb) {
kfree(frag);
goto resubmit;
@@ -385,7 +384,6 @@ static void zd1201_usbrx(struct urb *urb)
memcpy(skb_put(skb, len), data+8, len);
}
skb->protocol = eth_type_trans(skb, zd->dev);
- skb->dev->last_rx = jiffies;
zd->stats.rx_packets++;
zd->stats.rx_bytes += skb->len;
netif_rx(skb);
@@ -745,7 +743,7 @@ static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
static int zd1201_net_open(struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
/* Start MAC with wildcard if no essid set */
if (!zd->mac_enabled)
@@ -783,7 +781,7 @@ static int zd1201_net_stop(struct net_device *dev)
*/
static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
unsigned char *txbuf = zd->txdata;
int txbuflen, pad = 0, err;
struct urb *urb = zd->tx_urb;
@@ -833,7 +831,7 @@ static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
static void zd1201_tx_timeout(struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
if (!zd)
return;
@@ -848,7 +846,7 @@ static void zd1201_tx_timeout(struct net_device *dev)
static int zd1201_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
int err;
if (!zd)
@@ -865,21 +863,21 @@ static int zd1201_set_mac_address(struct net_device *dev, void *p)
static struct net_device_stats *zd1201_get_stats(struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
return &zd->stats;
}
static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
return &zd->iwstats;
}
static void zd1201_set_multicast(struct net_device *dev)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
struct dev_mc_list *mc = dev->mc_list;
unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
int i;
@@ -899,7 +897,7 @@ static void zd1201_set_multicast(struct net_device *dev)
static int zd1201_config_commit(struct net_device *dev,
struct iw_request_info *info, struct iw_point *data, char *essid)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
return zd1201_mac_reset(zd);
}
@@ -914,7 +912,7 @@ static int zd1201_get_name(struct net_device *dev,
static int zd1201_set_freq(struct net_device *dev,
struct iw_request_info *info, struct iw_freq *freq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short channel = 0;
int err;
@@ -939,7 +937,7 @@ static int zd1201_set_freq(struct net_device *dev,
static int zd1201_get_freq(struct net_device *dev,
struct iw_request_info *info, struct iw_freq *freq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short channel;
int err;
@@ -955,7 +953,7 @@ static int zd1201_get_freq(struct net_device *dev,
static int zd1201_set_mode(struct net_device *dev,
struct iw_request_info *info, __u32 *mode, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short porttype, monitor = 0;
unsigned char buffer[IW_ESSID_MAX_SIZE+2];
int err;
@@ -1017,7 +1015,7 @@ static int zd1201_set_mode(struct net_device *dev,
static int zd1201_get_mode(struct net_device *dev,
struct iw_request_info *info, __u32 *mode, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short porttype;
int err;
@@ -1093,7 +1091,7 @@ static int zd1201_get_range(struct net_device *dev,
static int zd1201_get_wap(struct net_device *dev,
struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
unsigned char buffer[6];
if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
@@ -1121,7 +1119,7 @@ static int zd1201_set_scan(struct net_device *dev,
static int zd1201_get_scan(struct net_device *dev,
struct iw_request_info *info, struct iw_point *srq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
int err, i, j, enabled_save;
struct iw_event iwe;
char *cev = extra;
@@ -1213,7 +1211,7 @@ static int zd1201_get_scan(struct net_device *dev,
static int zd1201_set_essid(struct net_device *dev,
struct iw_request_info *info, struct iw_point *data, char *essid)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
if (data->length > IW_ESSID_MAX_SIZE)
return -EINVAL;
@@ -1228,7 +1226,7 @@ static int zd1201_set_essid(struct net_device *dev,
static int zd1201_get_essid(struct net_device *dev,
struct iw_request_info *info, struct iw_point *data, char *essid)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
memcpy(essid, zd->essid, zd->essidlen);
data->flags = 1;
@@ -1249,7 +1247,7 @@ static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
static int zd1201_set_rate(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short rate;
int err;
@@ -1282,7 +1280,7 @@ static int zd1201_set_rate(struct net_device *dev,
static int zd1201_get_rate(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short rate;
int err;
@@ -1315,7 +1313,7 @@ static int zd1201_get_rate(struct net_device *dev,
static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
int err;
short val = rts->value;
@@ -1335,7 +1333,7 @@ static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short rtst;
int err;
@@ -1352,7 +1350,7 @@ static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
int err;
short val = frag->value;
@@ -1373,7 +1371,7 @@ static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short fragt;
int err;
@@ -1402,7 +1400,7 @@ static int zd1201_get_retry(struct net_device *dev,
static int zd1201_set_encode(struct net_device *dev,
struct iw_request_info *info, struct iw_point *erq, char *key)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short i;
int err, rid;
@@ -1459,7 +1457,7 @@ static int zd1201_set_encode(struct net_device *dev,
static int zd1201_get_encode(struct net_device *dev,
struct iw_request_info *info, struct iw_point *erq, char *key)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short i;
int err;
@@ -1492,7 +1490,7 @@ static int zd1201_get_encode(struct net_device *dev,
static int zd1201_set_power(struct net_device *dev,
struct iw_request_info *info, struct iw_param *vwrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short enabled, duration, level;
int err;
@@ -1531,7 +1529,7 @@ out:
static int zd1201_get_power(struct net_device *dev,
struct iw_request_info *info, struct iw_param *vwrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short enabled, level, duration;
int err;
@@ -1618,7 +1616,7 @@ static const iw_handler zd1201_iw_handler[] =
static int zd1201_set_hostauth(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
if (!zd->ap)
return -EOPNOTSUPP;
@@ -1629,7 +1627,7 @@ static int zd1201_set_hostauth(struct net_device *dev,
static int zd1201_get_hostauth(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short hostauth;
int err;
@@ -1648,7 +1646,7 @@ static int zd1201_get_hostauth(struct net_device *dev,
static int zd1201_auth_sta(struct net_device *dev,
struct iw_request_info *info, struct sockaddr *sta, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
unsigned char buffer[10];
if (!zd->ap)
@@ -1664,7 +1662,7 @@ static int zd1201_auth_sta(struct net_device *dev,
static int zd1201_set_maxassoc(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
int err;
if (!zd->ap)
@@ -1679,7 +1677,7 @@ static int zd1201_set_maxassoc(struct net_device *dev,
static int zd1201_get_maxassoc(struct net_device *dev,
struct iw_request_info *info, struct iw_param *rrq, char *extra)
{
- struct zd1201 *zd = (struct zd1201 *)dev->priv;
+ struct zd1201 *zd = netdev_priv(dev);
short maxassoc;
int err;
@@ -1731,6 +1729,7 @@ static int zd1201_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct zd1201 *zd;
+ struct net_device *dev;
struct usb_device *usb;
int err;
short porttype;
@@ -1738,9 +1737,12 @@ static int zd1201_probe(struct usb_interface *interface,
usb = interface_to_usbdev(interface);
- zd = kzalloc(sizeof(struct zd1201), GFP_KERNEL);
- if (!zd)
+ dev = alloc_etherdev(sizeof(*zd));
+ if (!dev)
return -ENOMEM;
+ zd = netdev_priv(dev);
+ zd->dev = dev;
+
zd->ap = ap;
zd->usb = usb;
zd->removed = 0;
@@ -1775,34 +1777,29 @@ static int zd1201_probe(struct usb_interface *interface,
if (err)
goto err_start;
- zd->dev = alloc_etherdev(0);
- if (!zd->dev)
- goto err_start;
-
- zd->dev->priv = zd;
- zd->dev->open = zd1201_net_open;
- zd->dev->stop = zd1201_net_stop;
- zd->dev->get_stats = zd1201_get_stats;
- zd->dev->wireless_handlers =
+ dev->open = zd1201_net_open;
+ dev->stop = zd1201_net_stop;
+ dev->get_stats = zd1201_get_stats;
+ dev->wireless_handlers =
(struct iw_handler_def *)&zd1201_iw_handlers;
- zd->dev->hard_start_xmit = zd1201_hard_start_xmit;
- zd->dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
- zd->dev->tx_timeout = zd1201_tx_timeout;
- zd->dev->set_multicast_list = zd1201_set_multicast;
- zd->dev->set_mac_address = zd1201_set_mac_address;
- strcpy(zd->dev->name, "wlan%d");
+ dev->hard_start_xmit = zd1201_hard_start_xmit;
+ dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
+ dev->tx_timeout = zd1201_tx_timeout;
+ dev->set_multicast_list = zd1201_set_multicast;
+ dev->set_mac_address = zd1201_set_mac_address;
+ strcpy(dev->name, "wlan%d");
err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR,
- zd->dev->dev_addr, zd->dev->addr_len);
+ dev->dev_addr, dev->addr_len);
if (err)
- goto err_net;
+ goto err_start;
/* Set wildcard essid to match zd->essid */
*(__le16 *)buf = cpu_to_le16(0);
err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
IW_ESSID_MAX_SIZE+2, 1);
if (err)
- goto err_net;
+ goto err_start;
if (zd->ap)
porttype = ZD1201_PORTTYPE_AP;
@@ -1810,30 +1807,28 @@ static int zd1201_probe(struct usb_interface *interface,
porttype = ZD1201_PORTTYPE_BSS;
err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
if (err)
- goto err_net;
+ goto err_start;
- SET_NETDEV_DEV(zd->dev, &usb->dev);
+ SET_NETDEV_DEV(dev, &usb->dev);
- err = register_netdev(zd->dev);
+ err = register_netdev(dev);
if (err)
- goto err_net;
+ goto err_start;
dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
- zd->dev->name);
+ dev->name);
usb_set_intfdata(interface, zd);
zd1201_enable(zd); /* zd1201 likes to startup enabled, */
zd1201_disable(zd); /* interfering with all the wifis in range */
return 0;
-err_net:
- free_netdev(zd->dev);
err_start:
/* Leave the device in reset state */
zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
err_zd:
usb_free_urb(zd->tx_urb);
usb_free_urb(zd->rx_urb);
- kfree(zd);
+ free_netdev(dev);
return err;
}
@@ -1846,10 +1841,6 @@ static void zd1201_disconnect(struct usb_interface *interface)
if (!zd)
return;
usb_set_intfdata(interface, NULL);
- if (zd->dev) {
- unregister_netdev(zd->dev);
- free_netdev(zd->dev);
- }
hlist_for_each_entry_safe(frag, node, node2, &zd->fraglist, fnode) {
hlist_del_init(&frag->fnode);
@@ -1865,7 +1856,11 @@ static void zd1201_disconnect(struct usb_interface *interface)
usb_kill_urb(zd->rx_urb);
usb_free_urb(zd->rx_urb);
}
- kfree(zd);
+
+ if (zd->dev) {
+ unregister_netdev(zd->dev);
+ free_netdev(zd->dev);
+ }
}
#ifdef CONFIG_PM
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index e0ac58b8ff1f..f1519143f8a6 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -378,7 +378,6 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
[0] = { .addr = CR_MAC_ADDR_P1 },
[1] = { .addr = CR_MAC_ADDR_P2 },
};
- DECLARE_MAC_BUF(mac);
if (mac_addr) {
reqs[0].value = (mac_addr[3] << 24)
@@ -387,8 +386,7 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
| mac_addr[0];
reqs[1].value = (mac_addr[5] << 8)
| mac_addr[4];
- dev_dbg_f(zd_chip_dev(chip),
- "mac addr %s\n", print_mac(mac, mac_addr));
+ dev_dbg_f(zd_chip_dev(chip), "mac addr %pM\n", mac_addr);
} else {
dev_dbg_f(zd_chip_dev(chip), "set NULL mac\n");
}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index cac732f4047f..9caa96a13586 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -171,7 +171,7 @@ int zd_mac_init_hw(struct ieee80211_hw *hw)
r = zd_reg2alpha2(mac->regdomain, alpha2);
if (!r)
- regulatory_hint(hw->wiphy, alpha2, NULL);
+ regulatory_hint(hw->wiphy, alpha2);
r = 0;
disable_int:
@@ -296,15 +296,14 @@ static void zd_op_stop(struct ieee80211_hw *hw)
* If no status information has been requested, the skb is freed.
*/
static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
- u32 flags, int ackssi, bool success)
+ int ackssi, bool success)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- memset(&info->status, 0, sizeof(info->status));
+ ieee80211_tx_info_clear_status(info);
- if (!success)
- info->status.excessive_retries = 1;
- info->flags |= flags;
+ if (success)
+ info->flags |= IEEE80211_TX_STAT_ACK;
info->status.ack_signal = ackssi;
ieee80211_tx_status_irqsafe(hw, skb);
}
@@ -326,7 +325,7 @@ void zd_mac_tx_failed(struct ieee80211_hw *hw)
if (skb == NULL)
return;
- tx_status(hw, skb, 0, 0, 0);
+ tx_status(hw, skb, 0, 0);
}
/**
@@ -342,12 +341,12 @@ void zd_mac_tx_failed(struct ieee80211_hw *hw)
void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hw *hw = info->driver_data[0];
+ struct ieee80211_hw *hw = info->rate_driver_data[0];
skb_pull(skb, sizeof(struct zd_ctrlset));
if (unlikely(error ||
(info->flags & IEEE80211_TX_CTL_NO_ACK))) {
- tx_status(hw, skb, 0, 0, !error);
+ tx_status(hw, skb, 0, !error);
} else {
struct sk_buff_head *q =
&zd_hw_mac(hw)->ack_wait_queue;
@@ -406,7 +405,8 @@ static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length)
}
static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
- struct ieee80211_hdr *header, u32 flags)
+ struct ieee80211_hdr *header,
+ struct ieee80211_tx_info *info)
{
/*
* CONTROL TODO:
@@ -417,7 +417,7 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
cs->control = 0;
/* First fragment */
- if (flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
/* Multicast */
@@ -428,10 +428,10 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
if (ieee80211_is_pspoll(header->frame_control))
cs->control |= ZD_CS_PS_POLL_FRAME;
- if (flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
cs->control |= ZD_CS_RTS;
- if (flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
cs->control |= ZD_CS_SELF_CTS;
/* FIXME: Management frame? */
@@ -517,12 +517,12 @@ static int fill_ctrlset(struct zd_mac *mac,
txrate = ieee80211_get_tx_rate(mac->hw, info);
cs->modulation = txrate->hw_value;
- if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
cs->modulation = txrate->hw_value_short;
cs->tx_length = cpu_to_le16(frag_len);
- cs_set_control(mac, cs, hdr, info->flags);
+ cs_set_control(mac, cs, hdr, info);
packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
ZD_ASSERT(packet_length <= 0xffff);
@@ -577,7 +577,7 @@ static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (r)
return r;
- info->driver_data[0] = hw;
+ info->rate_driver_data[0] = hw;
r = zd_usb_tx(&mac->chip.usb, skb);
if (r)
@@ -618,7 +618,7 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN)))
{
__skb_unlink(skb, q);
- tx_status(hw, skb, IEEE80211_TX_STAT_ACK, stats->signal, 1);
+ tx_status(hw, skb, stats->signal, 1);
goto out;
}
}
@@ -743,9 +743,11 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,
zd_write_mac_addr(&mac->chip, NULL);
}
-static int zd_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
+static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct zd_mac *mac = zd_hw_mac(hw);
+ struct ieee80211_conf *conf = &hw->conf;
+
return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
}
@@ -852,14 +854,12 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) {
zd_mc_add_all(&hash);
} else {
- DECLARE_MAC_BUF(macbuf);
-
zd_mc_clear(&hash);
for (i = 0; i < mc_count; i++) {
if (!mclist)
break;
- dev_dbg_f(zd_mac_dev(mac), "mc addr %s\n",
- print_mac(macbuf, mclist->dmi_addr));
+ dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n",
+ mclist->dmi_addr);
zd_mc_add_addr(&hash, mclist->dmi_addr);
mclist = mclist->next;
}
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index a3ccd8c1c716..b5db57d2fcf5 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -909,7 +909,7 @@ free_urb:
* it might be freed by zd_mac_tx_to_dev or mac80211)
*/
info = IEEE80211_SKB_CB(skb);
- usb = &zd_hw_mac(info->driver_data[0])->chip.usb;
+ usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
zd_mac_tx_to_dev(skb, urb->status);
free_tx_urb(usb, urb);
tx_dec_submitted_urbs(usb);
@@ -1065,8 +1065,7 @@ static int eject_installer(struct usb_interface *intf)
/* Find bulk out endpoint */
endpoint = &iface_desc->endpoint[1].desc;
if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT &&
- (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
- USB_ENDPOINT_XFER_BULK) {
+ usb_endpoint_xfer_bulk(endpoint)) {
bulk_out_ep = endpoint->bEndpointAddress;
} else {
dev_err(&udev->dev,
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 6d017adc914a..761635be9104 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -196,7 +196,7 @@ static void rx_refill_timeout(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
struct netfront_info *np = netdev_priv(dev);
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
}
static int netfront_tx_slot_available(struct netfront_info *np)
@@ -328,7 +328,7 @@ static int xennet_open(struct net_device *dev)
xennet_alloc_rx_buffers(dev);
np->rx.sring->rsp_event = np->rx.rsp_cons + 1;
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
}
spin_unlock_bh(&np->rx_lock);
@@ -841,7 +841,6 @@ static int handle_incoming_queue(struct net_device *dev,
/* Pass it up. */
netif_receive_skb(skb);
- dev->last_rx = jiffies;
}
return packets_dropped;
@@ -980,7 +979,7 @@ err:
RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do);
if (!more_to_do)
- __netif_rx_complete(dev, napi);
+ __netif_rx_complete(napi);
local_irq_restore(flags);
}
@@ -1311,7 +1310,7 @@ static irqreturn_t xennet_interrupt(int irq, void *dev_id)
xennet_tx_buf_gc(dev);
/* Under tx_lock: protects access to rx shared-ring indexes. */
if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx))
- netif_rx_schedule(dev, &np->napi);
+ netif_rx_schedule(&np->napi);
}
spin_unlock_irqrestore(&np->tx_lock, flags);
diff --git a/drivers/net/xtsonic.c b/drivers/net/xtsonic.c
index da42aa06a3ba..03a3f34e9039 100644
--- a/drivers/net/xtsonic.c
+++ b/drivers/net/xtsonic.c
@@ -239,8 +239,6 @@ int __init xtsonic_probe(struct platform_device *pdev)
struct resource *resmem, *resirq;
int err = 0;
- DECLARE_MAC_BUF(mac);
-
if ((resmem = platform_get_resource(pdev, IORESOURCE_MEM, 0)) == NULL)
return -ENODEV;
@@ -263,8 +261,8 @@ int __init xtsonic_probe(struct platform_device *pdev)
if ((err = register_netdev(dev)))
goto out1;
- printk("%s: SONIC ethernet @%08lx, MAC %s, IRQ %d\n", dev->name,
- dev->base_addr, print_mac(mac, dev->dev_addr), dev->irq);
+ printk("%s: SONIC ethernet @%08lx, MAC %pM, IRQ %d\n", dev->name,
+ dev->base_addr, dev->dev_addr, dev->irq);
return 0;
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 57e1f495b9fc..cf9712922778 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -355,6 +355,16 @@ static int yellowfin_close(struct net_device *dev);
static void set_rx_mode(struct net_device *dev);
static const struct ethtool_ops ethtool_ops;
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = yellowfin_open,
+ .ndo_stop = yellowfin_close,
+ .ndo_start_xmit = yellowfin_start_xmit,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_tx_timeout = yellowfin_tx_timeout,
+};
static int __devinit yellowfin_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
@@ -374,7 +384,6 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev,
#else
int bar = 1;
#endif
- DECLARE_MAC_BUF(mac);
/* when built into the kernel, we only print version if device is found */
#ifndef MODULE
@@ -465,13 +474,8 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev,
np->duplex_lock = 1;
/* The Yellowfin-specific entries in the device structure. */
- dev->open = &yellowfin_open;
- dev->hard_start_xmit = &yellowfin_start_xmit;
- dev->stop = &yellowfin_close;
- dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &netdev_ioctl;
+ dev->netdev_ops = &netdev_ops;
SET_ETHTOOL_OPS(dev, &ethtool_ops);
- dev->tx_timeout = yellowfin_tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
if (mtu)
@@ -481,10 +485,10 @@ static int __devinit yellowfin_init_one(struct pci_dev *pdev,
if (i)
goto err_out_unmap_status;
- printk(KERN_INFO "%s: %s type %8x at %p, %s, IRQ %d.\n",
+ printk(KERN_INFO "%s: %s type %8x at %p, %pM, IRQ %d.\n",
dev->name, pci_id_tbl[chip_idx].name,
ioread32(ioaddr + ChipRev), ioaddr,
- print_mac(mac, dev->dev_addr), irq);
+ dev->dev_addr, irq);
if (np->drv_flags & HasMII) {
int phy, phy_idx = 0;
@@ -1100,11 +1104,9 @@ static int yellowfin_rx(struct net_device *dev)
memcmp(le32_to_cpu(yp->rx_ring_dma +
entry*sizeof(struct yellowfin_desc)),
"\377\377\377\377\377\377", 6) != 0) {
- if (bogus_rx++ == 0) {
- DECLARE_MAC_BUF(mac);
- printk(KERN_WARNING "%s: Bad frame to %s\n",
- dev->name, print_mac(mac, buf_addr));
- }
+ if (bogus_rx++ == 0)
+ printk(KERN_WARNING "%s: Bad frame to %pM\n",
+ dev->name, buf_addr);
#endif
} else {
struct sk_buff *skb;
@@ -1141,7 +1143,6 @@ static int yellowfin_rx(struct net_device *dev)
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -1423,14 +1424,3 @@ static void __exit yellowfin_cleanup (void)
module_init(yellowfin_init);
module_exit(yellowfin_cleanup);
-
-/*
- * Local variables:
- * compile-command: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c yellowfin.c"
- * compile-command-alphaLX: "gcc -DMODULE -Wall -Wstrict-prototypes -O2 -c yellowfin.c -fomit-frame-pointer -fno-strength-reduce -mno-fp-regs -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED"
- * simple-compile-command: "gcc -DMODULE -O6 -c yellowfin.c"
- * c-indent-level: 4
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index a86c022d6a94..f0b15c9347d0 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -167,7 +167,7 @@ static void znet_tx_timeout (struct net_device *dev);
/* Request needed resources */
static int znet_request_resources (struct net_device *dev)
{
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
unsigned long flags;
if (request_irq (dev->irq, &znet_interrupt, 0, "ZNet", dev))
@@ -201,7 +201,7 @@ static int znet_request_resources (struct net_device *dev)
static void znet_release_resources (struct net_device *dev)
{
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
unsigned long flags;
release_region (znet->sia_base, znet->sia_size);
@@ -216,7 +216,7 @@ static void znet_release_resources (struct net_device *dev)
/* Keep the magical SIA stuff in a single function... */
static void znet_transceiver_power (struct net_device *dev, int on)
{
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
unsigned char v;
/* Turn on/off the 82501 SIA, using zenith-specific magic. */
@@ -235,7 +235,7 @@ static void znet_transceiver_power (struct net_device *dev, int on)
Also used from hardware_init. */
static void znet_set_multicast_list (struct net_device *dev)
{
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
short ioaddr = dev->base_addr;
struct i82593_conf_block *cfblk = &znet->i593_init;
@@ -370,7 +370,6 @@ static int __init znet_probe (void)
struct net_device *dev;
char *p;
int err = -ENOMEM;
- DECLARE_MAC_BUF(mac);
/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
@@ -387,7 +386,7 @@ static int __init znet_probe (void)
if (!dev)
return -ENOMEM;
- znet = dev->priv;
+ znet = netdev_priv(dev);
netinfo = (struct netidblk *)p;
dev->base_addr = netinfo->iobase1;
@@ -397,9 +396,9 @@ static int __init znet_probe (void)
for (i = 0; i < 6; i++)
dev->dev_addr[i] = netinfo->netid[i];
- printk(KERN_INFO "%s: ZNET at %#3lx, %s"
+ printk(KERN_INFO "%s: ZNET at %#3lx, %pM"
", using IRQ %d DMA %d and %d.\n",
- dev->name, dev->base_addr, print_mac(mac, dev->dev_addr),
+ dev->name, dev->base_addr, dev->dev_addr,
dev->irq, netinfo->dma1, netinfo->dma2);
if (znet_debug > 1) {
@@ -531,7 +530,7 @@ static void znet_tx_timeout (struct net_device *dev)
static int znet_send_packet(struct sk_buff *skb, struct net_device *dev)
{
int ioaddr = dev->base_addr;
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
unsigned long flags;
short length = skb->len;
@@ -601,7 +600,7 @@ static int znet_send_packet(struct sk_buff *skb, struct net_device *dev)
static irqreturn_t znet_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
int ioaddr;
int boguscnt = 20;
int handled = 0;
@@ -679,7 +678,7 @@ static irqreturn_t znet_interrupt(int irq, void *dev_id)
static void znet_rx(struct net_device *dev)
{
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
int ioaddr = dev->base_addr;
int boguscount = 1;
short next_frame_end_offset = 0; /* Offset of next frame start. */
@@ -786,7 +785,6 @@ static void znet_rx(struct net_device *dev)
}
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
- dev->last_rx = jiffies;
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
}
@@ -829,7 +827,7 @@ static void show_dma(struct net_device *dev)
{
short ioaddr = dev->base_addr;
unsigned char stat = inb (ioaddr);
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
unsigned long flags;
short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE;
unsigned addr = inb(dma_port);
@@ -852,7 +850,7 @@ static void hardware_init(struct net_device *dev)
{
unsigned long flags;
short ioaddr = dev->base_addr;
- struct znet_private *znet = dev->priv;
+ struct znet_private *znet = netdev_priv(dev);
znet->rx_cur = znet->rx_start;
znet->tx_cur = znet->tx_start;
@@ -914,7 +912,7 @@ static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
static __exit void znet_cleanup (void)
{
if (znet_dev) {
- struct znet_private *znet = znet_dev->priv;
+ struct znet_private *znet = netdev_priv(znet_dev);
unregister_netdev (znet_dev);
kfree (znet->rx_start);
diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c
index 3926b2aa9cca..affd904deafc 100644
--- a/drivers/net/zorro8390.c
+++ b/drivers/net/zorro8390.c
@@ -122,7 +122,7 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z,
break;
board = z->resource.start;
ioaddr = board+cards[i].offset;
- dev = ____alloc_ei_netdev(0);
+ dev = alloc_ei_netdev();
if (!dev)
return -ENOMEM;
if (!request_mem_region(ioaddr, NE_IO_EXTENT*2, DRV_NAME)) {
@@ -139,6 +139,20 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z,
return 0;
}
+static const struct net_device_ops zorro8390_netdev_ops = {
+ .ndo_open = zorro8390_open,
+ .ndo_stop = zorro8390_close,
+ .ndo_start_xmit = ei_start_xmit,
+ .ndo_tx_timeout = ei_tx_timeout,
+ .ndo_get_stats = ei_get_stats,
+ .ndo_set_multicast_list = ei_set_multicast_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ei_poll,
+#endif
+};
+
static int __devinit zorro8390_init(struct net_device *dev,
unsigned long board, const char *name,
unsigned long ioaddr)
@@ -151,7 +165,6 @@ static int __devinit zorro8390_init(struct net_device *dev,
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
};
- DECLARE_MAC_BUF(mac);
/* Reset card. Who knows what dain-bramaged state it was left in. */
{
@@ -216,7 +229,7 @@ static int __devinit zorro8390_init(struct net_device *dev,
dev->dev_addr[i] = SA_prom[i];
#ifdef DEBUG
- printk("%s", print_mac(mac, dev->dev_addr));
+ printk("%pM", dev->dev_addr);
#endif
ei_status.name = name;
@@ -231,12 +244,8 @@ static int __devinit zorro8390_init(struct net_device *dev,
ei_status.block_output = &zorro8390_block_output;
ei_status.get_8390_hdr = &zorro8390_get_8390_hdr;
ei_status.reg_offset = zorro8390_offsets;
- dev->open = &zorro8390_open;
- dev->stop = &zorro8390_close;
-#ifdef CONFIG_NET_POLL_CONTROLLER
- dev->poll_controller = __ei_poll;
-#endif
+ dev->netdev_ops = &zorro8390_netdev_ops;
__NS8390_init(dev, 0);
err = register_netdev(dev);
if (err) {
@@ -244,8 +253,8 @@ static int __devinit zorro8390_init(struct net_device *dev,
return err;
}
- printk(KERN_INFO "%s: %s at 0x%08lx, Ethernet Address %s\n",
- dev->name, name, board, print_mac(mac, dev->dev_addr));
+ printk(KERN_INFO "%s: %s at 0x%08lx, Ethernet Address %pM\n",
+ dev->name, name, board, dev->dev_addr);
return 0;
}
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 7c79e94a35ea..cd17092b82bd 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -329,6 +329,41 @@ struct device_node *of_find_compatible_node(struct device_node *from,
EXPORT_SYMBOL(of_find_compatible_node);
/**
+ * of_find_node_with_property - Find a node which has a property with
+ * the given name.
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned. of_node_put() will be called on it
+ * @prop_name: The name of the property to look for.
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_node_with_property(struct device_node *from,
+ const char *prop_name)
+{
+ struct device_node *np;
+ struct property *pp;
+
+ read_lock(&devtree_lock);
+ np = from ? from->allnext : allnodes;
+ for (; np; np = np->allnext) {
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ if (of_prop_cmp(pp->name, prop_name) == 0) {
+ of_node_get(np);
+ goto out;
+ }
+ }
+ }
+out:
+ of_node_put(from);
+ read_unlock(&devtree_lock);
+ return np;
+}
+EXPORT_SYMBOL(of_find_node_with_property);
+
+/**
* of_match_node - Tell if an device_node has a matching of_match structure
* @matches: array of of device match structures to search in
* @node: the of device structure to match against
@@ -464,8 +499,8 @@ EXPORT_SYMBOL_GPL(of_modalias_node);
* @list_name: property name that contains a list
* @cells_name: property name that specifies phandles' arguments count
* @index: index of a phandle to parse out
- * @out_node: pointer to device_node struct pointer (will be filled)
- * @out_args: pointer to arguments pointer (will be filled)
+ * @out_node: optional pointer to device_node struct pointer (will be filled)
+ * @out_args: optional pointer to arguments pointer (will be filled)
*
* This function is useful to parse lists of phandles and their arguments.
* Returns 0 on success and fills out_node and out_args, on error returns
@@ -499,7 +534,7 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
int size;
int cur_index = 0;
struct device_node *node = NULL;
- const void *args;
+ const void *args = NULL;
list = of_get_property(np, list_name, &size);
if (!list) {
@@ -512,14 +547,12 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
const u32 *cells;
const phandle *phandle;
- phandle = list;
- args = list + 1;
+ phandle = list++;
+ args = list;
/* one cell hole in the list = <>; */
- if (!*phandle) {
- list++;
+ if (!*phandle)
goto next;
- }
node = of_find_node_by_phandle(*phandle);
if (!node) {
@@ -535,8 +568,7 @@ int of_parse_phandles_with_args(struct device_node *np, const char *list_name,
goto err1;
}
- /* Next phandle is at offset of one phandle cell + #cells */
- list += 1 + *cells;
+ list += *cells;
if (list > list_end) {
pr_debug("%s: insufficient arguments length\n",
np->full_name);
@@ -548,16 +580,26 @@ next:
of_node_put(node);
node = NULL;
+ args = NULL;
cur_index++;
}
if (!node) {
- ret = -ENOENT;
+ /*
+ * args w/o node indicates that the loop above has stopped at
+ * the 'hole' cell. Report this differently.
+ */
+ if (args)
+ ret = -EEXIST;
+ else
+ ret = -ENOENT;
goto err0;
}
- *out_node = node;
- *out_args = args;
+ if (out_node)
+ *out_node = node;
+ if (out_args)
+ *out_args = args;
return 0;
err1:
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 7cd7301b5839..6eea601a9204 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -19,14 +19,17 @@
#include <asm/prom.h>
/**
- * of_get_gpio - Get a GPIO number from the device tree to use with GPIO API
+ * of_get_gpio_flags - Get a GPIO number and flags to use with GPIO API
* @np: device node to get GPIO from
* @index: index of the GPIO
+ * @flags: a flags pointer to fill in
*
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
- * value on the error condition.
+ * value on the error condition. If @flags is not NULL the function also fills
+ * in flags for the GPIO.
*/
-int of_get_gpio(struct device_node *np, int index)
+int of_get_gpio_flags(struct device_node *np, int index,
+ enum of_gpio_flags *flags)
{
int ret;
struct device_node *gc;
@@ -59,7 +62,11 @@ int of_get_gpio(struct device_node *np, int index)
goto err1;
}
- ret = of_gc->xlate(of_gc, np, gpio_spec);
+ /* .xlate might decide to not fill in the flags, so clear it. */
+ if (flags)
+ *flags = 0;
+
+ ret = of_gc->xlate(of_gc, np, gpio_spec, flags);
if (ret < 0)
goto err1;
@@ -70,26 +77,75 @@ err0:
pr_debug("%s exited with status %d\n", __func__, ret);
return ret;
}
-EXPORT_SYMBOL(of_get_gpio);
+EXPORT_SYMBOL(of_get_gpio_flags);
/**
- * of_gpio_simple_xlate - translate gpio_spec to the GPIO number
+ * of_gpio_count - Count GPIOs for a device
+ * @np: device node to count GPIOs for
+ *
+ * The function returns the count of GPIOs specified for a node.
+ *
+ * Note that the empty GPIO specifiers counts too. For example,
+ *
+ * gpios = <0
+ * &pio1 1 2
+ * 0
+ * &pio2 3 4>;
+ *
+ * defines four GPIOs (so this function will return 4), two of which
+ * are not specified.
+ */
+unsigned int of_gpio_count(struct device_node *np)
+{
+ unsigned int cnt = 0;
+
+ do {
+ int ret;
+
+ ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells",
+ cnt, NULL, NULL);
+ /* A hole in the gpios = <> counts anyway. */
+ if (ret < 0 && ret != -EEXIST)
+ break;
+ } while (++cnt);
+
+ return cnt;
+}
+EXPORT_SYMBOL(of_gpio_count);
+
+/**
+ * of_gpio_simple_xlate - translate gpio_spec to the GPIO number and flags
* @of_gc: pointer to the of_gpio_chip structure
* @np: device node of the GPIO chip
* @gpio_spec: gpio specifier as found in the device tree
+ * @flags: a flags pointer to fill in
*
* This is simple translation function, suitable for the most 1:1 mapped
* gpio chips. This function performs only one sanity check: whether gpio
* is less than ngpios (that is specified in the gpio_chip).
*/
int of_gpio_simple_xlate(struct of_gpio_chip *of_gc, struct device_node *np,
- const void *gpio_spec)
+ const void *gpio_spec, enum of_gpio_flags *flags)
{
const u32 *gpio = gpio_spec;
+ /*
+ * We're discouraging gpio_cells < 2, since that way you'll have to
+ * write your own xlate function (that will have to retrive the GPIO
+ * number and the flags from a single gpio cell -- this is possible,
+ * but not recommended).
+ */
+ if (of_gc->gpio_cells < 2) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
if (*gpio > of_gc->gc.ngpio)
return -EINVAL;
+ if (flags)
+ *flags = gpio[1];
+
return *gpio;
}
EXPORT_SYMBOL(of_gpio_simple_xlate);
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 24bbef777c19..e1b0ad6e918f 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -24,6 +24,7 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
for_each_child_of_node(adap_node, node) {
struct i2c_board_info info = {};
+ struct dev_archdata dev_ad = {};
const u32 *addr;
int len;
@@ -41,6 +42,9 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
info.addr = *addr;
+ dev_archdata_set_node(&dev_ad, node);
+ info.archdata = &dev_ad;
+
request_module("%s", info.type);
result = i2c_new_device(adap, &info);
@@ -51,6 +55,13 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
irq_dispose_mapping(info.irq);
continue;
}
+
+ /*
+ * Get the node to not lose the dev_archdata->of_node.
+ * Currently there is no way to put it back, as well as no
+ * of_unregister_i2c_devices() call.
+ */
+ of_node_get(node);
}
}
EXPORT_SYMBOL(of_register_i2c_devices);
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c
index b55cd23ffdef..737bd9484822 100644
--- a/drivers/oprofile/buffer_sync.c
+++ b/drivers/oprofile/buffer_sync.c
@@ -268,18 +268,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
return cookie;
}
-static void increment_tail(struct oprofile_cpu_buffer *b)
-{
- unsigned long new_tail = b->tail_pos + 1;
-
- rmb(); /* be sure fifo pointers are synchromized */
-
- if (new_tail < b->buffer_size)
- b->tail_pos = new_tail;
- else
- b->tail_pos = 0;
-}
-
static unsigned long last_cookie = INVALID_COOKIE;
static void add_cpu_switch(int i)
@@ -331,28 +319,25 @@ static void add_trace_begin(void)
#define IBS_FETCH_CODE_SIZE 2
#define IBS_OP_CODE_SIZE 5
-#define IBS_EIP(offset) \
- (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip)
-#define IBS_EVENT(offset) \
- (((struct op_sample *)&cpu_buf->buffer[(offset)])->event)
/*
* Add IBS fetch and op entries to event buffer
*/
-static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
- struct mm_struct *mm)
+static void add_ibs_begin(int cpu, int code, struct mm_struct *mm)
{
unsigned long rip;
int i, count;
unsigned long ibs_cookie = 0;
off_t offset;
+ struct op_sample *sample;
- increment_tail(cpu_buf); /* move to RIP entry */
-
- rip = IBS_EIP(cpu_buf->tail_pos);
+ sample = cpu_buffer_read_entry(cpu);
+ if (!sample)
+ goto Error;
+ rip = sample->eip;
#ifdef __LP64__
- rip += IBS_EVENT(cpu_buf->tail_pos) << 32;
+ rip += sample->event << 32;
#endif
if (mm) {
@@ -376,8 +361,8 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
add_event_entry(offset); /* Offset from Dcookie */
/* we send the Dcookie offset, but send the raw Linear Add also*/
- add_event_entry(IBS_EIP(cpu_buf->tail_pos));
- add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+ add_event_entry(sample->eip);
+ add_event_entry(sample->event);
if (code == IBS_FETCH_CODE)
count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/
@@ -385,10 +370,17 @@ static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/
for (i = 0; i < count; i++) {
- increment_tail(cpu_buf);
- add_event_entry(IBS_EIP(cpu_buf->tail_pos));
- add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+ sample = cpu_buffer_read_entry(cpu);
+ if (!sample)
+ goto Error;
+ add_event_entry(sample->eip);
+ add_event_entry(sample->event);
}
+
+ return;
+
+Error:
+ return;
}
#endif
@@ -466,33 +458,6 @@ static inline int is_code(unsigned long val)
}
-/* "acquire" as many cpu buffer slots as we can */
-static unsigned long get_slots(struct oprofile_cpu_buffer *b)
-{
- unsigned long head = b->head_pos;
- unsigned long tail = b->tail_pos;
-
- /*
- * Subtle. This resets the persistent last_task
- * and in_kernel values used for switching notes.
- * BUT, there is a small window between reading
- * head_pos, and this call, that means samples
- * can appear at the new head position, but not
- * be prefixed with the notes for switching
- * kernel mode or a task switch. This small hole
- * can lead to mis-attribution or samples where
- * we don't know if it's in the kernel or not,
- * at the start of an event buffer.
- */
- cpu_buffer_reset(b);
-
- if (head >= tail)
- return head - tail;
-
- return head + (b->buffer_size - tail);
-}
-
-
/* Move tasks along towards death. Any tasks on dead_tasks
* will definitely have no remaining references in any
* CPU buffers at this point, because we use two lists,
@@ -559,61 +524,61 @@ typedef enum {
*/
void sync_buffer(int cpu)
{
- struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
struct mm_struct *mm = NULL;
+ struct mm_struct *oldmm;
struct task_struct *new;
unsigned long cookie = 0;
int in_kernel = 1;
sync_buffer_state state = sb_buffer_start;
-#ifndef CONFIG_OPROFILE_IBS
unsigned int i;
unsigned long available;
-#endif
mutex_lock(&buffer_mutex);
add_cpu_switch(cpu);
- /* Remember, only we can modify tail_pos */
-
-#ifndef CONFIG_OPROFILE_IBS
- available = get_slots(cpu_buf);
+ cpu_buffer_reset(cpu);
+ available = cpu_buffer_entries(cpu);
for (i = 0; i < available; ++i) {
-#else
- while (get_slots(cpu_buf)) {
-#endif
- struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos];
+ struct op_sample *s = cpu_buffer_read_entry(cpu);
+ if (!s)
+ break;
if (is_code(s->eip)) {
- if (s->event <= CPU_IS_KERNEL) {
+ switch (s->event) {
+ case 0:
+ case CPU_IS_KERNEL:
/* kernel/userspace switch */
in_kernel = s->event;
if (state == sb_buffer_start)
state = sb_sample_start;
add_kernel_ctx_switch(s->event);
- } else if (s->event == CPU_TRACE_BEGIN) {
+ break;
+ case CPU_TRACE_BEGIN:
state = sb_bt_start;
add_trace_begin();
+ break;
#ifdef CONFIG_OPROFILE_IBS
- } else if (s->event == IBS_FETCH_BEGIN) {
+ case IBS_FETCH_BEGIN:
state = sb_bt_start;
- add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm);
- } else if (s->event == IBS_OP_BEGIN) {
+ add_ibs_begin(cpu, IBS_FETCH_CODE, mm);
+ break;
+ case IBS_OP_BEGIN:
state = sb_bt_start;
- add_ibs_begin(cpu_buf, IBS_OP_CODE, mm);
+ add_ibs_begin(cpu, IBS_OP_CODE, mm);
+ break;
#endif
- } else {
- struct mm_struct *oldmm = mm;
-
+ default:
/* userspace context switch */
+ oldmm = mm;
new = (struct task_struct *)s->event;
-
release_mm(oldmm);
mm = take_tasks_mm(new);
if (mm != oldmm)
cookie = get_exec_dcookie(mm);
add_user_ctx_switch(new, cookie);
+ break;
}
} else if (state >= sb_bt_start &&
!add_sample(mm, s, in_kernel)) {
@@ -622,8 +587,6 @@ void sync_buffer(int cpu)
atomic_inc(&oprofile_stats.bt_lost_no_mapping);
}
}
-
- increment_tail(cpu_buf);
}
release_mm(mm);
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index 01d38e78cde1..61090969158f 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -28,6 +28,25 @@
#include "buffer_sync.h"
#include "oprof.h"
+#define OP_BUFFER_FLAGS 0
+
+/*
+ * Read and write access is using spin locking. Thus, writing to the
+ * buffer by NMI handler (x86) could occur also during critical
+ * sections when reading the buffer. To avoid this, there are 2
+ * buffers for independent read and write access. Read access is in
+ * process context only, write access only in the NMI handler. If the
+ * read buffer runs empty, both buffers are swapped atomically. There
+ * is potentially a small window during swapping where the buffers are
+ * disabled and samples could be lost.
+ *
+ * Using 2 buffers is a little bit overhead, but the solution is clear
+ * and does not require changes in the ring buffer implementation. It
+ * can be changed to a single buffer solution when the ring buffer
+ * access is implemented as non-locking atomic code.
+ */
+struct ring_buffer *op_ring_buffer_read;
+struct ring_buffer *op_ring_buffer_write;
DEFINE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
static void wq_sync_buffer(struct work_struct *work);
@@ -37,12 +56,12 @@ static int work_enabled;
void free_cpu_buffers(void)
{
- int i;
-
- for_each_possible_cpu(i) {
- vfree(per_cpu(cpu_buffer, i).buffer);
- per_cpu(cpu_buffer, i).buffer = NULL;
- }
+ if (op_ring_buffer_read)
+ ring_buffer_free(op_ring_buffer_read);
+ op_ring_buffer_read = NULL;
+ if (op_ring_buffer_write)
+ ring_buffer_free(op_ring_buffer_write);
+ op_ring_buffer_write = NULL;
}
unsigned long oprofile_get_cpu_buffer_size(void)
@@ -64,14 +83,16 @@ int alloc_cpu_buffers(void)
unsigned long buffer_size = fs_cpu_buffer_size;
+ op_ring_buffer_read = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ if (!op_ring_buffer_read)
+ goto fail;
+ op_ring_buffer_write = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS);
+ if (!op_ring_buffer_write)
+ goto fail;
+
for_each_possible_cpu(i) {
struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
- b->buffer = vmalloc_node(sizeof(struct op_sample) * buffer_size,
- cpu_to_node(i));
- if (!b->buffer)
- goto fail;
-
b->last_task = NULL;
b->last_is_kernel = -1;
b->tracing = 0;
@@ -124,57 +145,31 @@ void end_cpu_work(void)
flush_scheduled_work();
}
-/* Resets the cpu buffer to a sane state. */
-void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf)
-{
- /* reset these to invalid values; the next sample
- * collected will populate the buffer with proper
- * values to initialize the buffer
- */
- cpu_buf->last_is_kernel = -1;
- cpu_buf->last_task = NULL;
-}
-
-/* compute number of available slots in cpu_buffer queue */
-static unsigned long nr_available_slots(struct oprofile_cpu_buffer const *b)
+static inline int
+add_sample(struct oprofile_cpu_buffer *cpu_buf,
+ unsigned long pc, unsigned long event)
{
- unsigned long head = b->head_pos;
- unsigned long tail = b->tail_pos;
+ struct op_entry entry;
+ int ret;
- if (tail > head)
- return (tail - head) - 1;
+ ret = cpu_buffer_write_entry(&entry);
+ if (ret)
+ return ret;
- return tail + (b->buffer_size - head) - 1;
-}
+ entry.sample->eip = pc;
+ entry.sample->event = event;
-static void increment_head(struct oprofile_cpu_buffer *b)
-{
- unsigned long new_head = b->head_pos + 1;
-
- /* Ensure anything written to the slot before we
- * increment is visible */
- wmb();
-
- if (new_head < b->buffer_size)
- b->head_pos = new_head;
- else
- b->head_pos = 0;
-}
+ ret = cpu_buffer_write_commit(&entry);
+ if (ret)
+ return ret;
-static inline void
-add_sample(struct oprofile_cpu_buffer *cpu_buf,
- unsigned long pc, unsigned long event)
-{
- struct op_sample *entry = &cpu_buf->buffer[cpu_buf->head_pos];
- entry->eip = pc;
- entry->event = event;
- increment_head(cpu_buf);
+ return 0;
}
-static inline void
+static inline int
add_code(struct oprofile_cpu_buffer *buffer, unsigned long value)
{
- add_sample(buffer, ESCAPE_CODE, value);
+ return add_sample(buffer, ESCAPE_CODE, value);
}
/* This must be safe from any context. It's safe writing here
@@ -198,11 +193,6 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
return 0;
}
- if (nr_available_slots(cpu_buf) < 3) {
- cpu_buf->sample_lost_overflow++;
- return 0;
- }
-
is_kernel = !!is_kernel;
task = current;
@@ -210,26 +200,29 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
/* notice a switch from user->kernel or vice versa */
if (cpu_buf->last_is_kernel != is_kernel) {
cpu_buf->last_is_kernel = is_kernel;
- add_code(cpu_buf, is_kernel);
+ if (add_code(cpu_buf, is_kernel))
+ goto fail;
}
/* notice a task switch */
if (cpu_buf->last_task != task) {
cpu_buf->last_task = task;
- add_code(cpu_buf, (unsigned long)task);
+ if (add_code(cpu_buf, (unsigned long)task))
+ goto fail;
}
- add_sample(cpu_buf, pc, event);
+ if (add_sample(cpu_buf, pc, event))
+ goto fail;
+
return 1;
+
+fail:
+ cpu_buf->sample_lost_overflow++;
+ return 0;
}
static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf)
{
- if (nr_available_slots(cpu_buf) < 4) {
- cpu_buf->sample_lost_overflow++;
- return 0;
- }
-
add_code(cpu_buf, CPU_TRACE_BEGIN);
cpu_buf->tracing = 1;
return 1;
@@ -253,8 +246,10 @@ void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
if (!oprofile_begin_trace(cpu_buf))
return;
- /* if log_sample() fail we can't backtrace since we lost the source
- * of this event */
+ /*
+ * if log_sample() fail we can't backtrace since we lost the
+ * source of this event
+ */
if (log_sample(cpu_buf, pc, is_kernel, event))
oprofile_ops.backtrace(regs, backtrace_depth);
oprofile_end_trace(cpu_buf);
@@ -272,49 +267,55 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event)
#define MAX_IBS_SAMPLE_SIZE 14
-void oprofile_add_ibs_sample(struct pt_regs *const regs,
- unsigned int *const ibs_sample, int ibs_code)
+void oprofile_add_ibs_sample(struct pt_regs * const regs,
+ unsigned int * const ibs_sample, int ibs_code)
{
int is_kernel = !user_mode(regs);
struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
struct task_struct *task;
+ int fail = 0;
cpu_buf->sample_received++;
- if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) {
- /* we can't backtrace since we lost the source of this event */
- cpu_buf->sample_lost_overflow++;
- return;
- }
-
/* notice a switch from user->kernel or vice versa */
if (cpu_buf->last_is_kernel != is_kernel) {
+ if (add_code(cpu_buf, is_kernel))
+ goto fail;
cpu_buf->last_is_kernel = is_kernel;
- add_code(cpu_buf, is_kernel);
}
/* notice a task switch */
if (!is_kernel) {
task = current;
if (cpu_buf->last_task != task) {
+ if (add_code(cpu_buf, (unsigned long)task))
+ goto fail;
cpu_buf->last_task = task;
- add_code(cpu_buf, (unsigned long)task);
}
}
- add_code(cpu_buf, ibs_code);
- add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
- add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
- add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
+ fail = fail || add_code(cpu_buf, ibs_code);
+ fail = fail || add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
if (ibs_code == IBS_OP_BEGIN) {
- add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
- add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
- add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
+ fail = fail || add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
}
+ if (fail)
+ goto fail;
+
if (backtrace_depth)
oprofile_ops.backtrace(regs, backtrace_depth);
+
+ return;
+
+fail:
+ cpu_buf->sample_lost_overflow++;
+ return;
}
#endif
@@ -332,21 +333,21 @@ void oprofile_add_trace(unsigned long pc)
if (!cpu_buf->tracing)
return;
- if (nr_available_slots(cpu_buf) < 1) {
- cpu_buf->tracing = 0;
- cpu_buf->sample_lost_overflow++;
- return;
- }
+ /*
+ * broken frame can give an eip with the same value as an
+ * escape code, abort the trace if we get it
+ */
+ if (pc == ESCAPE_CODE)
+ goto fail;
- /* broken frame can give an eip with the same value as an escape code,
- * abort the trace if we get it */
- if (pc == ESCAPE_CODE) {
- cpu_buf->tracing = 0;
- cpu_buf->backtrace_aborted++;
- return;
- }
+ if (add_sample(cpu_buf, pc, 0))
+ goto fail;
- add_sample(cpu_buf, pc, 0);
+ return;
+fail:
+ cpu_buf->tracing = 0;
+ cpu_buf->backtrace_aborted++;
+ return;
}
/*
diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h
index d3cc26264db5..aacb0f0bc566 100644
--- a/drivers/oprofile/cpu_buffer.h
+++ b/drivers/oprofile/cpu_buffer.h
@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/cache.h>
#include <linux/sched.h>
+#include <linux/ring_buffer.h>
struct task_struct;
@@ -32,6 +33,12 @@ struct op_sample {
unsigned long event;
};
+struct op_entry {
+ struct ring_buffer_event *event;
+ struct op_sample *sample;
+ unsigned long irq_flags;
+};
+
struct oprofile_cpu_buffer {
volatile unsigned long head_pos;
volatile unsigned long tail_pos;
@@ -39,7 +46,6 @@ struct oprofile_cpu_buffer {
struct task_struct *last_task;
int last_is_kernel;
int tracing;
- struct op_sample *buffer;
unsigned long sample_received;
unsigned long sample_lost_overflow;
unsigned long backtrace_aborted;
@@ -48,9 +54,68 @@ struct oprofile_cpu_buffer {
struct delayed_work work;
};
+extern struct ring_buffer *op_ring_buffer_read;
+extern struct ring_buffer *op_ring_buffer_write;
DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
-void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf);
+/*
+ * Resets the cpu buffer to a sane state.
+ *
+ * reset these to invalid values; the next sample collected will
+ * populate the buffer with proper values to initialize the buffer
+ */
+static inline void cpu_buffer_reset(int cpu)
+{
+ struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
+
+ cpu_buf->last_is_kernel = -1;
+ cpu_buf->last_task = NULL;
+}
+
+static inline int cpu_buffer_write_entry(struct op_entry *entry)
+{
+ entry->event = ring_buffer_lock_reserve(op_ring_buffer_write,
+ sizeof(struct op_sample),
+ &entry->irq_flags);
+ if (entry->event)
+ entry->sample = ring_buffer_event_data(entry->event);
+ else
+ entry->sample = NULL;
+
+ if (!entry->sample)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static inline int cpu_buffer_write_commit(struct op_entry *entry)
+{
+ return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event,
+ entry->irq_flags);
+}
+
+static inline struct op_sample *cpu_buffer_read_entry(int cpu)
+{
+ struct ring_buffer_event *e;
+ e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL);
+ if (e)
+ return ring_buffer_event_data(e);
+ if (ring_buffer_swap_cpu(op_ring_buffer_read,
+ op_ring_buffer_write,
+ cpu))
+ return NULL;
+ e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL);
+ if (e)
+ return ring_buffer_event_data(e);
+ return NULL;
+}
+
+/* "acquire" as many cpu buffer slots as we can */
+static inline unsigned long cpu_buffer_entries(int cpu)
+{
+ return ring_buffer_entries_cpu(op_ring_buffer_read, cpu)
+ + ring_buffer_entries_cpu(op_ring_buffer_write, cpu);
+}
/* transient events for the CPU buffer -> event buffer */
#define CPU_IS_KERNEL 1
diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c
index cc106d503ace..d8201998b0b7 100644
--- a/drivers/oprofile/oprofile_files.c
+++ b/drivers/oprofile/oprofile_files.c
@@ -14,9 +14,13 @@
#include "oprofile_stats.h"
#include "oprof.h"
-unsigned long fs_buffer_size = 131072;
-unsigned long fs_cpu_buffer_size = 8192;
-unsigned long fs_buffer_watershed = 32768; /* FIXME: tune */
+#define FS_BUFFER_SIZE_DEFAULT 131072
+#define FS_CPU_BUFFER_SIZE_DEFAULT 8192
+#define FS_BUFFER_WATERSHED_DEFAULT 32768 /* FIXME: tune */
+
+unsigned long fs_buffer_size;
+unsigned long fs_cpu_buffer_size;
+unsigned long fs_buffer_watershed;
static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
@@ -120,6 +124,11 @@ static const struct file_operations dump_fops = {
void oprofile_create_files(struct super_block *sb, struct dentry *root)
{
+ /* reinitialize default values */
+ fs_buffer_size = FS_BUFFER_SIZE_DEFAULT;
+ fs_cpu_buffer_size = FS_CPU_BUFFER_SIZE_DEFAULT;
+ fs_buffer_watershed = FS_BUFFER_WATERSHED_DEFAULT;
+
oprofilefs_create_file(sb, root, "enable", &enable_fops);
oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666);
oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops);
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index f9b12664f9fb..454b6532e409 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -360,13 +360,13 @@ static __inline__ int led_get_net_activity(void)
read_lock(&dev_base_lock);
rcu_read_lock();
for_each_netdev(&init_net, dev) {
- struct net_device_stats *stats;
+ const struct net_device_stats *stats;
struct in_device *in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !in_dev->ifa_list)
continue;
if (ipv4_is_loopback(in_dev->ifa_list->ifa_local))
continue;
- stats = dev->get_stats(dev);
+ stats = dev_get_stats(dev);
rx_total += stats->rx_packets;
tx_total += stats->tx_packets;
}
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index f9e244da30ae..9bcb6cbd5aa9 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -113,7 +113,7 @@ struct acpiphp_slot {
u8 device; /* pci device# */
- u32 sun; /* ACPI _SUN (slot unique number) */
+ unsigned long long sun; /* ACPI _SUN (slot unique number) */
u32 flags; /* see below */
};
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 95b536a23d25..43c10bd261b4 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -337,7 +337,7 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
acpiphp_slot->slot = slot;
- snprintf(name, SLOT_NAME_SIZE, "%u", slot->acpi_slot->sun);
+ snprintf(name, SLOT_NAME_SIZE, "%llu", slot->acpi_slot->sun);
retval = pci_hp_register(slot->hotplug_slot,
acpiphp_slot->bridge->pci_bus,
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 955aae4071f7..3affc6472e65 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -255,13 +255,13 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
bridge->nr_slots++;
- dbg("found ACPI PCI Hotplug slot %d at PCI %04x:%02x:%02x\n",
+ dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
slot->sun, pci_domain_nr(bridge->pci_bus),
bridge->pci_bus->number, slot->device);
retval = acpiphp_register_hotplug_slot(slot);
if (retval) {
if (retval == -EBUSY)
- warn("Slot %d already registered by another "
+ warn("Slot %llu already registered by another "
"hotplug driver\n", slot->sun);
else
warn("acpiphp_register_hotplug_slot failed "
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index c892daae74d6..633e743442ac 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -1402,10 +1402,6 @@ static int __init ibmphp_init(void)
goto error;
}
- /* lock ourselves into memory with a module
- * count of -1 so that no one can unload us. */
- module_put(THIS_MODULE);
-
exit:
return rc;
@@ -1423,4 +1419,3 @@ static void __exit ibmphp_exit(void)
}
module_init(ibmphp_init);
-module_exit(ibmphp_exit);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 4b23bc39b11e..39cf248d24e3 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -432,18 +432,19 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
goto err_out_release_ctlr;
}
+ /* Check if slot is occupied */
t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
-
- t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
- if (value && pciehp_force) {
- rc = pciehp_enable_slot(t_slot);
- if (rc) /* -ENODEV: shouldn't happen, but deal with it */
- value = 0;
- }
- if ((POWER_CTRL(ctrl)) && !value) {
- rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
- if (rc)
- goto err_out_free_ctrl_slot;
+ t_slot->hpc_ops->get_adapter_status(t_slot, &value);
+ if (value) {
+ if (pciehp_force)
+ pciehp_enable_slot(t_slot);
+ } else {
+ /* Power off slot if not occupied */
+ if (POWER_CTRL(ctrl)) {
+ rc = t_slot->hpc_ops->power_off_slot(t_slot);
+ if (rc)
+ goto err_out_free_ctrl_slot;
+ }
}
return 0;
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index 9c2a22fed18b..4e3e0382c16e 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -14,6 +14,9 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
+
+#undef DEBUG
+
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/string.h>
@@ -151,20 +154,20 @@ static void dlpar_pci_add_bus(struct device_node *dn)
return;
}
+ /* Scan below the new bridge */
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
of_scan_pci_bridge(dn, dev);
- pcibios_fixup_new_pci_devices(dev->subordinate);
-
- /* Claim new bus resources */
- pcibios_claim_one_bus(dev->bus);
-
/* Map IO space for child bus, which may or may not succeed */
pcibios_map_io_space(dev->subordinate);
- /* Add new devices to global lists. Register in proc, sysfs. */
- pci_bus_add_devices(phb->bus);
+ /* Finish adding it : resource allocation, adding devices, etc...
+ * Note that we need to perform the finish pass on the -parent-
+ * bus of the EADS bridge so the bridge device itself gets
+ * properly added
+ */
+ pcibios_finish_adding_to_bus(phb->bus);
}
static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
@@ -203,27 +206,6 @@ static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
return 0;
}
-static int dlpar_remove_root_bus(struct pci_controller *phb)
-{
- struct pci_bus *phb_bus;
- int rc;
-
- phb_bus = phb->bus;
- if (!(list_empty(&phb_bus->children) &&
- list_empty(&phb_bus->devices))) {
- return -EBUSY;
- }
-
- rc = pcibios_remove_root_bus(phb);
- if (rc)
- return -EIO;
-
- device_unregister(phb_bus->bridge);
- pci_remove_bus(phb_bus);
-
- return 0;
-}
-
static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
{
struct slot *slot;
@@ -235,18 +217,15 @@ static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
/* If pci slot is hotplugable, use hotplug to remove it */
slot = find_php_slot(dn);
- if (slot) {
- if (rpaphp_deregister_slot(slot)) {
- printk(KERN_ERR
- "%s: unable to remove hotplug slot %s\n",
- __func__, drc_name);
- return -EIO;
- }
+ if (slot && rpaphp_deregister_slot(slot)) {
+ printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
+ __func__, drc_name);
+ return -EIO;
}
pdn = dn->data;
BUG_ON(!pdn || !pdn->phb);
- rc = dlpar_remove_root_bus(pdn->phb);
+ rc = remove_phb_dynamic(pdn->phb);
if (rc < 0)
return rc;
@@ -378,26 +357,38 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
if (!bus)
return -EINVAL;
- /* If pci slot is hotplugable, use hotplug to remove it */
+ pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
+ bus->self ? pci_name(bus->self) : "<!PHB!>");
+
slot = find_php_slot(dn);
if (slot) {
+ pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
+ pci_domain_nr(bus), bus->number);
+
if (rpaphp_deregister_slot(slot)) {
printk(KERN_ERR
"%s: unable to remove hotplug slot %s\n",
__func__, drc_name);
return -EIO;
}
- } else
- pcibios_remove_pci_devices(bus);
+ }
+
+ /* Remove all devices below slot */
+ pcibios_remove_pci_devices(bus);
+ /* Unmap PCI IO space */
if (pcibios_unmap_io_space(bus)) {
printk(KERN_ERR "%s: failed to unmap bus range\n",
__func__);
return -ERANGE;
}
+ /* Remove the EADS bridge device itself */
BUG_ON(!bus->self);
+ pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
+ eeh_remove_bus_device(bus->self);
pci_remove_bus_device(bus->self);
+
return 0;
}
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index dfc63d01f20a..aac7006949f1 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -252,7 +252,7 @@ static void report_resume(struct pci_dev *dev, void *data)
if (!dev->driver ||
!dev->driver->err_handler ||
- !dev->driver->err_handler->slot_reset)
+ !dev->driver->err_handler->resume)
return;
err_handler = dev->driver->err_handler;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 5f4f85f56cb7..ce0985615133 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -606,27 +606,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev)
sis_apic_bug = 1;
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw);
-
-#define AMD8131_revA0 0x01
-#define AMD8131_revB0 0x11
-#define AMD8131_MISC 0x40
-#define AMD8131_NIOAMODE_BIT 0
-static void quirk_amd_8131_ioapic(struct pci_dev *dev)
-{
- unsigned char tmp;
-
- if (nr_ioapics == 0)
- return;
-
- if (dev->revision == AMD8131_revA0 || dev->revision == AMD8131_revB0) {
- dev_info(&dev->dev, "Fixing up AMD8131 IOAPIC mode\n");
- pci_read_config_byte( dev, AMD8131_MISC, &tmp);
- tmp &= ~(1 << AMD8131_NIOAMODE_BIT);
- pci_write_config_byte( dev, AMD8131_MISC, tmp);
- }
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
#endif /* CONFIG_X86_IO_APIC */
/*
@@ -1423,6 +1402,155 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);
+#ifdef CONFIG_X86_IO_APIC
+/*
+ * Boot interrupts on some chipsets cannot be turned off. For these chipsets,
+ * remap the original interrupt in the linux kernel to the boot interrupt, so
+ * that a PCI device's interrupt handler is installed on the boot interrupt
+ * line instead.
+ */
+static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev)
+{
+ if (noioapicquirk || noioapicreroute)
+ return;
+
+ dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT;
+
+ printk(KERN_INFO "PCI quirk: reroute interrupts for 0x%04x:0x%04x\n",
+ dev->vendor, dev->device);
+ return;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXH_1, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PXHV, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_0, quirk_reroute_to_boot_interrupts_intel);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80332_1, quirk_reroute_to_boot_interrupts_intel);
+
+/*
+ * On some chipsets we can disable the generation of legacy INTx boot
+ * interrupts.
+ */
+
+/*
+ * IO-APIC1 on 6300ESB generates boot interrupts, see intel order no
+ * 300641-004US, section 5.7.3.
+ */
+#define INTEL_6300_IOAPIC_ABAR 0x40
+#define INTEL_6300_DISABLE_BOOT_IRQ (1<<14)
+
+static void quirk_disable_intel_boot_interrupt(struct pci_dev *dev)
+{
+ u16 pci_config_word;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_word(dev, INTEL_6300_IOAPIC_ABAR, &pci_config_word);
+ pci_config_word |= INTEL_6300_DISABLE_BOOT_IRQ;
+ pci_write_config_word(dev, INTEL_6300_IOAPIC_ABAR, pci_config_word);
+
+ printk(KERN_INFO "disabled boot interrupt on device 0x%04x:0x%04x\n",
+ dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt);
+
+/*
+ * disable boot interrupts on HT-1000
+ */
+#define BC_HT1000_FEATURE_REG 0x64
+#define BC_HT1000_PIC_REGS_ENABLE (1<<0)
+#define BC_HT1000_MAP_IDX 0xC00
+#define BC_HT1000_MAP_DATA 0xC01
+
+static void quirk_disable_broadcom_boot_interrupt(struct pci_dev *dev)
+{
+ u32 pci_config_dword;
+ u8 irq;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_dword(dev, BC_HT1000_FEATURE_REG, &pci_config_dword);
+ pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword |
+ BC_HT1000_PIC_REGS_ENABLE);
+
+ for (irq = 0x10; irq < 0x10 + 32; irq++) {
+ outb(irq, BC_HT1000_MAP_IDX);
+ outb(0x00, BC_HT1000_MAP_DATA);
+ }
+
+ pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword);
+
+ printk(KERN_INFO "disabled boot interrupts on PCI device"
+ "0x%04x:0x%04x\n", dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt);
+
+/*
+ * disable boot interrupts on AMD and ATI chipsets
+ */
+/*
+ * NOIOAMODE needs to be disabled to disable "boot interrupts". For AMD 8131
+ * rev. A0 and B0, NOIOAMODE needs to be disabled anyway to fix IO-APIC mode
+ * (due to an erratum).
+ */
+#define AMD_813X_MISC 0x40
+#define AMD_813X_NOIOAMODE (1<<0)
+
+static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev)
+{
+ u32 pci_config_dword;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword);
+ pci_config_dword &= ~AMD_813X_NOIOAMODE;
+ pci_write_config_dword(dev, AMD_813X_MISC, pci_config_dword);
+
+ printk(KERN_INFO "disabled boot interrupts on PCI device "
+ "0x%04x:0x%04x\n", dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, quirk_disable_amd_813x_boot_interrupt);
+
+#define AMD_8111_PCI_IRQ_ROUTING 0x56
+
+static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev)
+{
+ u16 pci_config_word;
+
+ if (noioapicquirk)
+ return;
+
+ pci_read_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, &pci_config_word);
+ if (!pci_config_word) {
+ printk(KERN_INFO "boot interrupts on PCI device 0x%04x:0x%04x "
+ "already disabled\n",
+ dev->vendor, dev->device);
+ return;
+ }
+ pci_write_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, 0);
+ printk(KERN_INFO "disabled boot interrupts on PCI device "
+ "0x%04x:0x%04x\n", dev->vendor, dev->device);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt);
+#endif /* CONFIG_X86_IO_APIC */
+
/*
* Toshiba TC86C001 IDE controller reports the standard 8-byte BAR0 size
* but the PIO transfers won't work if BAR0 falls at the odd 8 bytes.
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 222904411a13..276473543982 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -217,7 +217,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER)
+ || ARCH_VIPER || ARCH_PXA_ESERIES)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 238629ad7f7c..bbac46327227 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -72,5 +72,6 @@ pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o
pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps4.o
pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
+pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/pxa2xx_e740.c b/drivers/pcmcia/pxa2xx_e740.c
new file mode 100644
index 000000000000..f663a011bf4a
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_e740.c
@@ -0,0 +1,176 @@
+/*
+ * Toshiba e740 PCMCIA specific routines.
+ *
+ * (c) 2004 Ian Molton <spyro@f2s.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/eseries-gpio.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include "soc_common.h"
+
+static struct pcmcia_irqs cd_irqs[] = {
+ {
+ .sock = 0,
+ .irq = IRQ_GPIO(GPIO_E740_PCMCIA_CD0),
+ .str = "CF card detect"
+ },
+ {
+ .sock = 1,
+ .irq = IRQ_GPIO(GPIO_E740_PCMCIA_CD1),
+ .str = "Wifi switch"
+ },
+};
+
+static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ skt->irq = skt->nr == 0 ? IRQ_GPIO(GPIO_E740_PCMCIA_RDY0) :
+ IRQ_GPIO(GPIO_E740_PCMCIA_RDY1);
+
+ return soc_pcmcia_request_irqs(skt, &cd_irqs[skt->nr], 1);
+}
+
+/*
+ * Release all resources.
+ */
+static void e740_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, &cd_irqs[skt->nr], 1);
+}
+
+static void e740_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ if (skt->nr == 0) {
+ state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD0) ? 0 : 1;
+ state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY0) ? 1 : 0;
+ } else {
+ state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD1) ? 0 : 1;
+ state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY1) ? 1 : 0;
+ }
+
+ state->vs_3v = 1;
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->wrprot = 0;
+ state->vs_Xv = 0;
+}
+
+static int e740_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ if (state->flags & SS_RESET) {
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_RST0, 1);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_RST1, 1);
+ } else {
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_RST0, 0);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_RST1, 0);
+ }
+
+ switch (state->Vcc) {
+ case 0: /* Socket off */
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_PWR0, 0);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_PWR1, 1);
+ break;
+ case 50:
+ case 33: /* socket on */
+ if (skt->nr == 0)
+ gpio_set_value(GPIO_E740_PCMCIA_PWR0, 1);
+ else
+ gpio_set_value(GPIO_E740_PCMCIA_PWR1, 0);
+ break;
+ default:
+ printk(KERN_ERR "e740_cs: Unsupported Vcc: %d\n", state->Vcc);
+ }
+
+ return 0;
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation. This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+static void e740_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_enable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
+}
+
+/*
+ * Disable card status IRQs on suspend.
+ */
+static void e740_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_disable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
+}
+
+static struct pcmcia_low_level e740_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = e740_pcmcia_hw_init,
+ .hw_shutdown = e740_pcmcia_hw_shutdown,
+ .socket_state = e740_pcmcia_socket_state,
+ .configure_socket = e740_pcmcia_configure_socket,
+ .socket_init = e740_pcmcia_socket_init,
+ .socket_suspend = e740_pcmcia_socket_suspend,
+ .nr = 2,
+};
+
+static struct platform_device *e740_pcmcia_device;
+
+static int __init e740_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_e740())
+ return -ENODEV;
+
+ e740_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!e740_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(e740_pcmcia_device, &e740_pcmcia_ops,
+ sizeof(e740_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(e740_pcmcia_device);
+
+ if (ret)
+ platform_device_put(e740_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit e740_pcmcia_exit(void)
+{
+ platform_device_unregister(e740_pcmcia_device);
+}
+
+module_init(e740_pcmcia_init);
+module_exit(e740_pcmcia_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_DESCRIPTION("e740 PCMCIA platform support");
diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c
index 7ff824496b39..7e6b5a3b3281 100644
--- a/drivers/pnp/pnpbios/bioscalls.c
+++ b/drivers/pnp/pnpbios/bioscalls.c
@@ -481,7 +481,7 @@ void pnpbios_calls_init(union pnp_bios_install_struct *header)
set_base(bad_bios_desc, __va((unsigned long)0x40 << 4));
_set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4));
- for (i = 0; i < NR_CPUS; i++) {
+ for_each_possible_cpu(i) {
struct desc_struct *gdt = get_cpu_gdt_table(i);
if (!gdt)
continue;
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
index 06848b254d57..5324978b73fb 100644
--- a/drivers/ps3/ps3av.c
+++ b/drivers/ps3/ps3av.c
@@ -59,8 +59,6 @@ static struct ps3av {
struct ps3av_reply_hdr reply_hdr;
u8 raw[PS3AV_BUF_SIZE];
} recv_buf;
- void (*flip_ctl)(int on, void *data);
- void *flip_data;
} *ps3av;
/* color space */
@@ -939,24 +937,6 @@ int ps3av_audio_mute(int mute)
EXPORT_SYMBOL_GPL(ps3av_audio_mute);
-void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
- void *flip_data)
-{
- mutex_lock(&ps3av->mutex);
- ps3av->flip_ctl = flip_ctl;
- ps3av->flip_data = flip_data;
- mutex_unlock(&ps3av->mutex);
-}
-EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl);
-
-void ps3av_flip_ctl(int on)
-{
- mutex_lock(&ps3av->mutex);
- if (ps3av->flip_ctl)
- ps3av->flip_ctl(on, ps3av->flip_data);
- mutex_unlock(&ps3av->mutex);
-}
-
static int ps3av_probe(struct ps3_system_bus_device *dev)
{
int res;
diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c
index 11eb50318fec..716596e8e5b0 100644
--- a/drivers/ps3/ps3av_cmd.c
+++ b/drivers/ps3/ps3av_cmd.c
@@ -864,7 +864,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len)
{
int res;
- ps3av_flip_ctl(0); /* flip off */
+ mutex_lock(&ps3_gpu_mutex);
/* avb packet */
res = ps3av_do_pkt(PS3AV_CID_AVB_PARAM, send_len, sizeof(*avb),
@@ -878,7 +878,7 @@ int ps3av_cmd_avb_param(struct ps3av_pkt_avb_param *avb, u32 send_len)
res);
out:
- ps3av_flip_ctl(1); /* flip on */
+ mutex_unlock(&ps3_gpu_mutex);
return res;
}
diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c
index 643a6b98462b..5c13f61bfb1b 100644
--- a/drivers/rapidio/rio-scan.c
+++ b/drivers/rapidio/rio-scan.c
@@ -365,15 +365,15 @@ static struct rio_dev *rio_setup_device(struct rio_net *net,
rdid++)
rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
rdev->rswitch = rswitch;
- sprintf(rio_name(rdev), "%02x:s:%04x", rdev->net->id,
- rdev->rswitch->switchid);
+ dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id,
+ rdev->rswitch->switchid);
rio_route_set_ops(rdev);
list_add_tail(&rswitch->node, &rio_switches);
} else
- sprintf(rio_name(rdev), "%02x:e:%04x", rdev->net->id,
- rdev->destid);
+ dev_set_name(&rdev->dev, "%02x:e:%04x", rdev->net->id,
+ rdev->destid);
rdev->dev.bus = &rio_bus_type;
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 2133f37906f2..d5e4e637ddec 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -21,6 +21,7 @@
#include <mach/board.h>
#include <mach/at91_rtt.h>
+#include <mach/cpu.h>
/*
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 2cd77ab8fc66..054e05294af8 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -328,6 +328,13 @@ isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
int sr;
u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
+ /* The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec);
regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min);
regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL;
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index f59277bbedaa..7a568beba3f0 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -26,7 +26,7 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/plat-s3c/regs-rtc.h>
+#include <plat/regs-rtc.h>
/* I have yet to find an S3C implementation with more than one
* of these rtc blocks in */
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index 66a9bb85bbe8..d26a5f82aaba 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -38,11 +38,11 @@
#include <mach/pxa-regs.h>
#endif
-#define TIMER_FREQ CLOCK_TICK_RATE
#define RTC_DEF_DIVIDER 32768 - 1
#define RTC_DEF_TRIM 0
static unsigned long rtc_freq = 1024;
+static unsigned long timer_freq;
static struct rtc_time rtc_alarm;
static DEFINE_SPINLOCK(sa1100_rtc_lock);
@@ -157,7 +157,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id)
rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF);
if (rtc_timer1_count == 1)
- rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2)));
+ rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2)));
return IRQ_HANDLED;
}
@@ -166,7 +166,7 @@ static int sa1100_rtc_read_callback(struct device *dev, int data)
{
if (data & RTC_PF) {
/* interpolate missed periods and set match for the next */
- unsigned long period = TIMER_FREQ/rtc_freq;
+ unsigned long period = timer_freq / rtc_freq;
unsigned long oscr = OSCR;
unsigned long osmr1 = OSMR1;
unsigned long missed = (oscr - osmr1)/period;
@@ -263,7 +263,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
return 0;
case RTC_PIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
- OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
+ OSMR1 = timer_freq / rtc_freq + OSCR;
OIER |= OIER_E1;
rtc_timer1_count = 1;
spin_unlock_irq(&sa1100_rtc_lock);
@@ -271,7 +271,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
case RTC_IRQP_READ:
return put_user(rtc_freq, (unsigned long *)arg);
case RTC_IRQP_SET:
- if (arg < 1 || arg > TIMER_FREQ)
+ if (arg < 1 || arg > timer_freq)
return -EINVAL;
rtc_freq = arg;
return 0;
@@ -352,6 +352,8 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
+ timer_freq = get_clock_tick_rate();
+
/*
* According to the manual we should be able to let RTTR be zero
* and then a default diviser for a 32.768KHz clock is used.
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 363bd1303d21..570ae59c1d5e 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1898,15 +1898,19 @@ restart_cb:
wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED));
/* Process finished ERP request. */
if (cqr->refers) {
+ spin_lock_bh(&block->queue_lock);
__dasd_block_process_erp(block, cqr);
+ spin_unlock_bh(&block->queue_lock);
/* restart list_for_xx loop since dasd_process_erp
* might remove multiple elements */
goto restart_cb;
}
/* call the callback function */
+ spin_lock_irq(&block->request_queue_lock);
cqr->endclk = get_clock();
list_del_init(&cqr->blocklist);
__dasd_cleanup_cqr(cqr);
+ spin_unlock_irq(&block->request_queue_lock);
}
return rc;
}
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 921443b01d16..2ef25731d197 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -23,6 +23,7 @@
/* This is ugly... */
#define PRINTK_HEADER "dasd_devmap:"
+#define DASD_BUS_ID_SIZE 20
#include "dasd_int.h"
@@ -41,7 +42,7 @@ EXPORT_SYMBOL_GPL(dasd_page_cache);
*/
struct dasd_devmap {
struct list_head list;
- char bus_id[BUS_ID_SIZE];
+ char bus_id[DASD_BUS_ID_SIZE];
unsigned int devindex;
unsigned short features;
struct dasd_device *device;
@@ -94,7 +95,7 @@ dasd_hash_busid(const char *bus_id)
int hash, i;
hash = 0;
- for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
+ for (i = 0; (i < DASD_BUS_ID_SIZE) && *bus_id; i++, bus_id++)
hash += *bus_id;
return hash & 0xff;
}
@@ -301,7 +302,7 @@ dasd_parse_range( char *parsestring ) {
int from, from_id0, from_id1;
int to, to_id0, to_id1;
int features, rc;
- char bus_id[BUS_ID_SIZE+1], *str;
+ char bus_id[DASD_BUS_ID_SIZE+1], *str;
str = parsestring;
rc = dasd_busid(&str, &from_id0, &from_id1, &from);
@@ -407,14 +408,14 @@ dasd_add_busid(const char *bus_id, int features)
devmap = NULL;
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list)
- if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+ if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
devmap = tmp;
break;
}
if (!devmap) {
/* This bus_id is new. */
new->devindex = dasd_max_devindex++;
- strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
+ strncpy(new->bus_id, bus_id, DASD_BUS_ID_SIZE);
new->features = features;
new->device = NULL;
list_add(&new->list, &dasd_hashlists[hash]);
@@ -439,7 +440,7 @@ dasd_find_busid(const char *bus_id)
devmap = ERR_PTR(-ENODEV);
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
- if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+ if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
devmap = tmp;
break;
}
@@ -561,7 +562,7 @@ dasd_create_device(struct ccw_device *cdev)
}
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
- cdev->dev.driver_data = device;
+ dev_set_drvdata(&cdev->dev, device);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return device;
@@ -597,7 +598,7 @@ dasd_delete_device(struct dasd_device *device)
/* Disconnect dasd_device structure from ccw_device structure. */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->cdev->dev.driver_data = NULL;
+ dev_set_drvdata(&device->cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
/*
@@ -638,7 +639,7 @@ dasd_put_device_wake(struct dasd_device *device)
struct dasd_device *
dasd_device_from_cdev_locked(struct ccw_device *cdev)
{
- struct dasd_device *device = cdev->dev.driver_data;
+ struct dasd_device *device = dev_get_drvdata(&cdev->dev);
if (!device)
return ERR_PTR(-ENODEV);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 2e60d5f968c8..bd2c52e20762 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1496,7 +1496,7 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
/* service information message SIM */
- if (irb->esw.esw0.erw.cons && (irb->ecw[27] & DASD_SENSE_BIT_0) &&
+ if (irb->esw.esw0.erw.cons && !(irb->ecw[27] & DASD_SENSE_BIT_0) &&
((irb->ecw[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
dasd_3990_erp_handle_sim(device, irb->ecw);
dasd_schedule_device_bh(device);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 9088de84b45d..bf6fd348f20e 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -180,12 +180,12 @@ dasd_calc_metrics(char *page, char **start, off_t off,
#ifdef CONFIG_DASD_PROFILE
static char *
-dasd_statistics_array(char *str, unsigned int *array, int shift)
+dasd_statistics_array(char *str, unsigned int *array, int factor)
{
int i;
for (i = 0; i < 32; i++) {
- str += sprintf(str, "%7d ", array[i] >> shift);
+ str += sprintf(str, "%7d ", array[i] / factor);
if (i == 15)
str += sprintf(str, "\n");
}
@@ -202,7 +202,7 @@ dasd_statistics_read(char *page, char **start, off_t off,
#ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t *prof;
char *str;
- int shift;
+ int factor;
/* check for active profiling */
if (dasd_profile_level == DASD_PROFILE_OFF) {
@@ -214,12 +214,14 @@ dasd_statistics_read(char *page, char **start, off_t off,
prof = &dasd_global_profile;
/* prevent couter 'overflow' on output */
- for (shift = 0; (prof->dasd_io_reqs >> shift) > 9999999; shift++);
+ for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
+ factor *= 10);
str = page;
str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs);
- str += sprintf(str, "with %d sectors(512B each)\n",
+ str += sprintf(str, "with %u sectors(512B each)\n",
prof->dasd_io_sects);
+ str += sprintf(str, "Scale Factor is %d\n", factor);
str += sprintf(str,
" __<4 ___8 __16 __32 __64 _128 "
" _256 _512 __1k __2k __4k __8k "
@@ -230,22 +232,22 @@ dasd_statistics_read(char *page, char **start, off_t off,
" __1G __2G __4G " " _>4G\n");
str += sprintf(str, "Histogram of sizes (512B secs)\n");
- str = dasd_statistics_array(str, prof->dasd_io_secs, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_secs, factor);
str += sprintf(str, "Histogram of I/O times (microseconds)\n");
- str = dasd_statistics_array(str, prof->dasd_io_times, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_times, factor);
str += sprintf(str, "Histogram of I/O times per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_timps, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_timps, factor);
str += sprintf(str, "Histogram of I/O time till ssch\n");
- str = dasd_statistics_array(str, prof->dasd_io_time1, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time1, factor);
str += sprintf(str, "Histogram of I/O time between ssch and irq\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time2, factor);
str += sprintf(str, "Histogram of I/O time between ssch "
"and irq per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2ps, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time2ps, factor);
str += sprintf(str, "Histogram of I/O time between irq and end\n");
- str = dasd_statistics_array(str, prof->dasd_io_time3, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time3, factor);
str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n");
- str = dasd_statistics_array(str, prof->dasd_io_nr_req, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_nr_req, factor);
len = str - page;
#else
len = sprintf(page, "Statistics are not activated in this kernel\n");
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 63f26a135fe5..26ffc6ab441d 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -4,6 +4,9 @@
* Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer
*/
+#define KMSG_COMPONENT "dcssblk"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h>
@@ -17,19 +20,10 @@
#include <linux/interrupt.h>
#include <asm/s390_rdev.h>
-//#define DCSSBLK_DEBUG /* Debug messages on/off */
#define DCSSBLK_NAME "dcssblk"
#define DCSSBLK_MINORS_PER_DISK 1
#define DCSSBLK_PARM_LEN 400
-
-#ifdef DCSSBLK_DEBUG
-#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)
-#else
-#define PRINT_DEBUG(x...) do {} while (0)
-#endif
-#define PRINT_INFO(x...) printk(KERN_INFO DCSSBLK_NAME " info: " x)
-#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
-#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
+#define DCSS_BUS_ID_SIZE 20
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
static int dcssblk_release(struct gendisk *disk, fmode_t mode);
@@ -50,7 +44,7 @@ static struct block_device_operations dcssblk_devops = {
struct dcssblk_dev_info {
struct list_head lh;
struct device dev;
- char segment_name[BUS_ID_SIZE];
+ char segment_name[DCSS_BUS_ID_SIZE];
atomic_t use_count;
struct gendisk *gd;
unsigned long start;
@@ -65,7 +59,7 @@ struct dcssblk_dev_info {
struct segment_info {
struct list_head lh;
- char segment_name[BUS_ID_SIZE];
+ char segment_name[DCSS_BUS_ID_SIZE];
unsigned long start;
unsigned long end;
int segment_type;
@@ -261,10 +255,9 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
/* check continuity */
for (i = 0; i < dev_info->num_of_segments - 1; i++) {
if ((sort_list[i].end + 1) != sort_list[i+1].start) {
- PRINT_ERR("Segment %s is not contiguous with "
- "segment %s\n",
- sort_list[i].segment_name,
- sort_list[i+1].segment_name);
+ pr_err("Adjacent DCSSs %s and %s are not "
+ "contiguous\n", sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
rc = -EINVAL;
goto out;
}
@@ -275,10 +268,10 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
!(sort_list[i+1].segment_type &
SEGMENT_EXCLUSIVE) ||
(sort_list[i+1].segment_type == SEG_TYPE_ER)) {
- PRINT_ERR("Segment %s has different type from "
- "segment %s\n",
- sort_list[i].segment_name,
- sort_list[i+1].segment_name);
+ pr_err("DCSS %s and DCSS %s have "
+ "incompatible types\n",
+ sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
rc = -EINVAL;
goto out;
}
@@ -380,8 +373,9 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
} else if (inbuf[0] == '0') {
/* reload segments in exclusive mode */
if (dev_info->segment_type == SEG_TYPE_SC) {
- PRINT_ERR("Segment type SC (%s) cannot be loaded in "
- "non-shared mode\n", dev_info->segment_name);
+ pr_err("DCSS %s is of type SC and cannot be "
+ "loaded as exclusive-writable\n",
+ dev_info->segment_name);
rc = -EINVAL;
goto out;
}
@@ -404,9 +398,8 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
goto out;
removeseg:
- PRINT_ERR("Could not reload segment(s) of the device %s, removing "
- "segment(s) now!\n",
- dev_info->segment_name);
+ pr_err("DCSS device %s is removed after a failed access mode "
+ "change\n", dev_info->segment_name);
temp = entry;
list_for_each_entry(entry, &dev_info->seg_list, lh) {
if (entry != temp)
@@ -454,17 +447,17 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
if (inbuf[0] == '1') {
if (atomic_read(&dev_info->use_count) == 0) {
// device is idle => we save immediately
- PRINT_INFO("Saving segment(s) of the device %s\n",
- dev_info->segment_name);
+ pr_info("All DCSSs that map to device %s are "
+ "saved\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) {
segment_save(entry->segment_name);
}
} else {
// device is busy => we save it when it becomes
// idle in dcssblk_release
- PRINT_INFO("Device %s is currently busy, segment(s) "
- "will be saved when it becomes idle...\n",
- dev_info->segment_name);
+ pr_info("Device %s is in use, its DCSSs will be "
+ "saved when it becomes idle\n",
+ dev_info->segment_name);
dev_info->save_pending = 1;
}
} else if (inbuf[0] == '0') {
@@ -472,9 +465,9 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
// device is busy & the user wants to undo his save
// request
dev_info->save_pending = 0;
- PRINT_INFO("Pending save for segment(s) of the device "
- "%s deactivated\n",
- dev_info->segment_name);
+ pr_info("A pending save request for device %s "
+ "has been canceled\n",
+ dev_info->segment_name);
}
} else {
up_write(&dcssblk_devices_sem);
@@ -614,9 +607,8 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
- PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
- "capacity = %lu (512 Byte) sectors\n", local_buf,
- seg_byte_size, seg_byte_size >> 9);
+ pr_info("Loaded %s with total size %lu bytes and capacity %lu "
+ "sectors\n", local_buf, seg_byte_size, seg_byte_size >> 9);
dev_info->save_pending = 0;
dev_info->is_shared = 1;
@@ -744,13 +736,15 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
dev_info = dcssblk_get_device_by_name(local_buf);
if (dev_info == NULL) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Device %s is not loaded!\n", local_buf);
+ pr_warning("Device %s cannot be removed because it is not a "
+ "known device\n", local_buf);
rc = -ENODEV;
goto out_buf;
}
if (atomic_read(&dev_info->use_count) != 0) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Device %s is in use!\n", local_buf);
+ pr_warning("Device %s cannot be removed while it is in "
+ "use\n", local_buf);
rc = -EBUSY;
goto out_buf;
}
@@ -807,8 +801,8 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
down_write(&dcssblk_devices_sem);
if (atomic_dec_and_test(&dev_info->use_count)
&& (dev_info->save_pending)) {
- PRINT_INFO("Device %s became idle and is being saved now\n",
- dev_info->segment_name);
+ pr_info("Device %s has become idle and is being saved "
+ "now\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) {
segment_save(entry->segment_name);
}
@@ -851,7 +845,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
case SEG_TYPE_SC:
/* cannot write to these segments */
if (bio_data_dir(bio) == WRITE) {
- PRINT_WARN("rejecting write to ro device %s\n",
+ pr_warning("Writing to %s failed because it "
+ "is a read-only device\n",
dev_name(&dev_info->dev));
goto fail;
}
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 03916989ed2d..76814f3e898a 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -25,6 +25,9 @@
* generic hard disk support to replace ad-hoc partitioning
*/
+#define KMSG_COMPONENT "xpram"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h> /* isdigit, isxdigit */
@@ -42,12 +45,6 @@
#define XPRAM_DEVS 1 /* one partition */
#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */
-#define PRINT_DEBUG(x...) printk(KERN_DEBUG XPRAM_NAME " debug:" x)
-#define PRINT_INFO(x...) printk(KERN_INFO XPRAM_NAME " info:" x)
-#define PRINT_WARN(x...) printk(KERN_WARNING XPRAM_NAME " warning:" x)
-#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x)
-
-
typedef struct {
unsigned int size; /* size of xpram segment in pages */
unsigned int offset; /* start page of xpram segment */
@@ -264,7 +261,7 @@ static int __init xpram_setup_sizes(unsigned long pages)
/* Check number of devices. */
if (devs <= 0 || devs > XPRAM_MAX_DEVS) {
- PRINT_ERR("invalid number %d of devices\n",devs);
+ pr_err("%d is not a valid number of XPRAM devices\n",devs);
return -EINVAL;
}
xpram_devs = devs;
@@ -295,22 +292,22 @@ static int __init xpram_setup_sizes(unsigned long pages)
mem_auto_no++;
}
- PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs);
+ pr_info(" number of devices (partitions): %d \n", xpram_devs);
for (i = 0; i < xpram_devs; i++) {
if (xpram_sizes[i])
- PRINT_INFO(" size of partition %d: %u kB\n",
- i, xpram_sizes[i]);
+ pr_info(" size of partition %d: %u kB\n",
+ i, xpram_sizes[i]);
else
- PRINT_INFO(" size of partition %d to be set "
- "automatically\n",i);
+ pr_info(" size of partition %d to be set "
+ "automatically\n",i);
}
- PRINT_DEBUG(" memory needed (for sized partitions): %lu kB\n",
- mem_needed);
- PRINT_DEBUG(" partitions to be sized automatically: %d\n",
- mem_auto_no);
+ pr_info(" memory needed (for sized partitions): %lu kB\n",
+ mem_needed);
+ pr_info(" partitions to be sized automatically: %d\n",
+ mem_auto_no);
if (mem_needed > pages * 4) {
- PRINT_ERR("Not enough expanded memory available\n");
+ pr_err("Not enough expanded memory available\n");
return -EINVAL;
}
@@ -322,8 +319,8 @@ static int __init xpram_setup_sizes(unsigned long pages)
*/
if (mem_auto_no) {
mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4;
- PRINT_INFO(" automatically determined "
- "partition size: %lu kB\n", mem_auto);
+ pr_info(" automatically determined "
+ "partition size: %lu kB\n", mem_auto);
for (i = 0; i < xpram_devs; i++)
if (xpram_sizes[i] == 0)
xpram_sizes[i] = mem_auto;
@@ -405,12 +402,12 @@ static int __init xpram_init(void)
/* Find out size of expanded memory. */
if (xpram_present() != 0) {
- PRINT_WARN("No expanded memory available\n");
+ pr_err("No expanded memory available\n");
return -ENODEV;
}
xpram_pages = xpram_highest_page_index() + 1;
- PRINT_INFO(" %u pages expanded memory found (%lu KB).\n",
- xpram_pages, (unsigned long) xpram_pages*4);
+ pr_info(" %u pages expanded memory found (%lu KB).\n",
+ xpram_pages, (unsigned long) xpram_pages*4);
rc = xpram_setup_sizes(xpram_pages);
if (rc)
return rc;
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 35fd8dfcaaa6..97e63cf46944 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -7,6 +7,9 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
+#define KMSG_COMPONENT "monreader"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -24,19 +27,6 @@
#include <asm/ebcdic.h>
#include <asm/extmem.h>
-//#define MON_DEBUG /* Debug messages on/off */
-
-#define MON_NAME "monreader"
-
-#define P_INFO(x...) printk(KERN_INFO MON_NAME " info: " x)
-#define P_ERROR(x...) printk(KERN_ERR MON_NAME " error: " x)
-#define P_WARNING(x...) printk(KERN_WARNING MON_NAME " warning: " x)
-
-#ifdef MON_DEBUG
-#define P_DEBUG(x...) printk(KERN_DEBUG MON_NAME " debug: " x)
-#else
-#define P_DEBUG(x...) do {} while (0)
-#endif
#define MON_COLLECT_SAMPLE 0x80
#define MON_COLLECT_EVENT 0x40
@@ -172,7 +162,7 @@ static int mon_send_reply(struct mon_msg *monmsg,
} else
monmsg->replied_msglim = 1;
if (rc) {
- P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc);
+ pr_err("Reading monitor data failed with rc=%i\n", rc);
return -EIO;
}
return 0;
@@ -251,7 +241,8 @@ static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
{
struct mon_private *monpriv = path->private;
- P_ERROR("IUCV connection severed with rc = 0x%X\n", ipuser[0]);
+ pr_err("z/VM *MONITOR system service disconnected with rc=%i\n",
+ ipuser[0]);
iucv_path_sever(path, NULL);
atomic_set(&monpriv->iucv_severed, 1);
wake_up(&mon_conn_wait_queue);
@@ -266,8 +257,7 @@ static void mon_iucv_message_pending(struct iucv_path *path,
memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
msg, sizeof(*msg));
if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
- P_WARNING("IUCV message pending, message limit (%i) reached\n",
- MON_MSGLIM);
+ pr_warning("The read queue for monitor data is full\n");
monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
}
monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
@@ -311,8 +301,8 @@ static int mon_open(struct inode *inode, struct file *filp)
rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
MON_SERVICE, NULL, user_data_connect, monpriv);
if (rc) {
- P_ERROR("iucv connection to *MONITOR failed with "
- "IPUSER SEVER code = %i\n", rc);
+ pr_err("Connecting to the z/VM *MONITOR system service "
+ "failed with rc=%i\n", rc);
rc = -EIO;
goto out_path;
}
@@ -353,7 +343,8 @@ static int mon_close(struct inode *inode, struct file *filp)
*/
rc = iucv_path_sever(monpriv->path, user_data_sever);
if (rc)
- P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
+ pr_warning("Disconnecting the z/VM *MONITOR system service "
+ "failed with rc=%i\n", rc);
atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0);
@@ -469,7 +460,8 @@ static int __init mon_init(void)
int rc;
if (!MACHINE_IS_VM) {
- P_ERROR("not running under z/VM, driver not loaded\n");
+ pr_err("The z/VM *MONITOR record device driver cannot be "
+ "loaded without z/VM\n");
return -ENODEV;
}
@@ -478,7 +470,8 @@ static int __init mon_init(void)
*/
rc = iucv_register(&monreader_iucv_handler, 1);
if (rc) {
- P_ERROR("failed to register with iucv driver\n");
+ pr_err("The z/VM *MONITOR record device driver failed to "
+ "register with IUCV\n");
return rc;
}
@@ -488,8 +481,8 @@ static int __init mon_init(void)
goto out_iucv;
}
if (rc != SEG_TYPE_SC) {
- P_ERROR("segment %s has unsupported type, should be SC\n",
- mon_dcss_name);
+ pr_err("The specified *MONITOR DCSS %s does not have the "
+ "required type SC\n", mon_dcss_name);
rc = -EINVAL;
goto out_iucv;
}
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 4d71aa8c1a79..c7d7483bab9a 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -8,6 +8,9 @@
* Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
*/
+#define KMSG_COMPONENT "monwriter"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -64,9 +67,9 @@ static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
if (rc <= 0)
return rc;
+ pr_err("Writing monitor data failed with rc=%i\n", rc);
if (rc == 5)
return -EPERM;
- printk("DIAG X'DC' error with return code: %i\n", rc);
return -EINVAL;
}
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index ec9c0bcf66ee..506390496416 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -6,6 +6,9 @@
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_cmd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -16,9 +19,8 @@
#include <linux/memory.h>
#include <asm/chpid.h>
#include <asm/sclp.h>
-#include "sclp.h"
-#define TAG "sclp_cmd: "
+#include "sclp.h"
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
@@ -169,8 +171,8 @@ static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
/* Check response. */
if (request->status != SCLP_REQ_DONE) {
- printk(KERN_WARNING TAG "sync request failed "
- "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status);
+ pr_warning("sync request failed (cmd=0x%08x, "
+ "status=0x%02x)\n", cmd, request->status);
rc = -EIO;
}
out:
@@ -224,8 +226,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
if (rc)
goto out;
if (sccb->header.response_code != 0x0010) {
- printk(KERN_WARNING TAG "readcpuinfo failed "
- "(response=0x%04x)\n", sccb->header.response_code);
+ pr_warning("readcpuinfo failed (response=0x%04x)\n",
+ sccb->header.response_code);
rc = -EIO;
goto out;
}
@@ -262,8 +264,9 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
case 0x0120:
break;
default:
- printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, "
- "response=0x%04x)\n", cmd, sccb->header.response_code);
+ pr_warning("configure cpu failed (cmd=0x%08x, "
+ "response=0x%04x)\n", cmd,
+ sccb->header.response_code);
rc = -EIO;
break;
}
@@ -626,9 +629,9 @@ static int do_chp_configure(sclp_cmdw_t cmd)
case 0x0450:
break;
default:
- printk(KERN_WARNING TAG "configure channel-path failed "
- "(cmd=0x%08x, response=0x%04x)\n", cmd,
- sccb->header.response_code);
+ pr_warning("configure channel-path failed "
+ "(cmd=0x%08x, response=0x%04x)\n", cmd,
+ sccb->header.response_code);
rc = -EIO;
break;
}
@@ -695,8 +698,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
if (rc)
goto out;
if (sccb->header.response_code != 0x0010) {
- printk(KERN_WARNING TAG "read channel-path info failed "
- "(response=0x%04x)\n", sccb->header.response_code);
+ pr_warning("read channel-path info failed "
+ "(response=0x%04x)\n", sccb->header.response_code);
rc = -EIO;
goto out;
}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index 4cebd6ee6d27..b497afe061cc 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -5,15 +5,17 @@
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_config"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/cpu.h>
#include <linux/sysdev.h>
#include <linux/workqueue.h>
#include <asm/smp.h>
-#include "sclp.h"
-#define TAG "sclp_config: "
+#include "sclp.h"
struct conf_mgm_data {
u8 reserved;
@@ -31,7 +33,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work)
int cpu;
struct sys_device *sysdev;
- printk(KERN_WARNING TAG "cpu capability changed.\n");
+ pr_warning("cpu capability changed.\n");
get_online_cpus();
for_each_online_cpu(cpu) {
sysdev = get_cpu_sysdev(cpu);
@@ -78,7 +80,7 @@ static int __init sclp_conf_init(void)
return rc;
if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
- printk(KERN_WARNING TAG "no configuration management.\n");
+ pr_warning("no configuration management.\n");
sclp_unregister(&sclp_conf_register);
rc = -ENOSYS;
}
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c
index d887bd261d28..62c2647f37f4 100644
--- a/drivers/s390/char/sclp_cpi_sys.c
+++ b/drivers/s390/char/sclp_cpi_sys.c
@@ -7,6 +7,9 @@
* Michael Ernst <mernst@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_cpi"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
@@ -20,6 +23,7 @@
#include <linux/completion.h>
#include <asm/ebcdic.h>
#include <asm/sclp.h>
+
#include "sclp.h"
#include "sclp_rw.h"
#include "sclp_cpi_sys.h"
@@ -150,16 +154,16 @@ static int cpi_req(void)
wait_for_completion(&completion);
if (req->status != SCLP_REQ_DONE) {
- printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n",
- req->status);
+ pr_warning("request failed (status=0x%02x)\n",
+ req->status);
rc = -EIO;
goto out_free_req;
}
response = ((struct cpi_sccb *) req->sccb)->header.response_code;
if (response != 0x0020) {
- printk(KERN_WARNING "cpi: failed with "
- "response code 0x%x\n", response);
+ pr_warning("request failed with response code 0x%x\n",
+ response);
rc = -EIO;
}
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 8b854857ba07..6a1c58dc61a7 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -5,15 +5,18 @@
* Author(s): Michael Holzheu
*/
+#define KMSG_COMPONENT "sclp_sdias"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/sched.h>
#include <asm/sclp.h>
#include <asm/debug.h>
#include <asm/ipl.h>
+
#include "sclp.h"
#include "sclp_rw.h"
#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
-#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
#define SDIAS_RETRIES 300
#define SDIAS_SLEEP_TICKS 50
@@ -131,7 +134,7 @@ int sclp_sdias_blk_count(void)
rc = sdias_sclp_send(&request);
if (rc) {
- ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+ pr_err("sclp_send failed for get_nr_blocks\n");
goto out;
}
if (sccb.hdr.response_code != 0x0020) {
@@ -145,7 +148,8 @@ int sclp_sdias_blk_count(void)
rc = sccb.evbuf.blk_cnt;
break;
default:
- ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+ pr_err("SCLP error: %x\n",
+ sccb.evbuf.event_status);
rc = -EIO;
goto out;
}
@@ -201,7 +205,7 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
rc = sdias_sclp_send(&request);
if (rc) {
- ERROR_MSG("sclp_send failed: %x\n", rc);
+ pr_err("sclp_send failed: %x\n", rc);
goto out;
}
if (sccb.hdr.response_code != 0x0020) {
@@ -219,9 +223,9 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
case EVSTATE_NO_DATA:
TRACE("no data\n");
default:
- ERROR_MSG("Error from SCLP while copying hsa. "
- "Event status = %x\n",
- sccb.evbuf.event_status);
+ pr_err("Error from SCLP while copying hsa. "
+ "Event status = %x\n",
+ sccb.evbuf.event_status);
rc = -EIO;
}
out:
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 9854f19f5e62..a839aa531d7c 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -583,23 +583,6 @@ sclp_vt220_chars_in_buffer(struct tty_struct *tty)
return count;
}
-static void
-__sclp_vt220_flush_buffer(void)
-{
- unsigned long flags;
-
- sclp_vt220_emit_current();
- spin_lock_irqsave(&sclp_vt220_lock, flags);
- if (timer_pending(&sclp_vt220_timer))
- del_timer(&sclp_vt220_timer);
- while (sclp_vt220_outqueue_count > 0) {
- spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- sclp_sync_wait();
- spin_lock_irqsave(&sclp_vt220_lock, flags);
- }
- spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-}
-
/*
* Pass on all buffers to the hardware. Return only when there are no more
* buffers pending.
@@ -745,6 +728,22 @@ sclp_vt220_con_device(struct console *c, int *index)
return sclp_vt220_driver;
}
+static void __sclp_vt220_flush_buffer(void)
+{
+ unsigned long flags;
+
+ sclp_vt220_emit_current();
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ if (timer_pending(&sclp_vt220_timer))
+ del_timer(&sclp_vt220_timer);
+ while (sclp_vt220_outqueue_count > 0) {
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+ sclp_sync_wait();
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ }
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+}
+
static int
sclp_vt220_notify(struct notifier_block *self,
unsigned long event, void *data)
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 09e7d9bf438b..a6087cec55b4 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -11,12 +11,14 @@
* The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS
*/
+#define KMSG_COMPONENT "vmcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
-#include <linux/smp_lock.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/uaccess.h>
@@ -26,8 +28,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Borntraeger <borntraeger@de.ibm.com>");
MODULE_DESCRIPTION("z/VM CP interface");
-#define PRINTK_HEADER "vmcp: "
-
static debug_info_t *vmcp_debug;
static int vmcp_open(struct inode *inode, struct file *file)
@@ -41,13 +41,11 @@ static int vmcp_open(struct inode *inode, struct file *file)
if (!session)
return -ENOMEM;
- lock_kernel();
session->bufsize = PAGE_SIZE;
session->response = NULL;
session->resp_size = 0;
mutex_init(&session->mutex);
file->private_data = session;
- unlock_kernel();
return nonseekable_open(inode, file);
}
@@ -193,7 +191,8 @@ static int __init vmcp_init(void)
int ret;
if (!MACHINE_IS_VM) {
- PRINT_WARN("z/VM CP interface is only available under z/VM\n");
+ pr_warning("The z/VM CP interface device driver cannot be "
+ "loaded without z/VM\n");
return -ENODEV;
}
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 24762727bc27..aabbeb909cc6 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -10,6 +10,10 @@
* Stefan Weinhuber <wein@de.ibm.com>
*
*/
+
+#define KMSG_COMPONENT "vmlogrdr"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -28,8 +32,6 @@
#include <linux/smp_lock.h>
#include <linux/string.h>
-
-
MODULE_AUTHOR
("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
" Stefan Weinhuber (wein@de.ibm.com)");
@@ -174,8 +176,7 @@ static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
struct vmlogrdr_priv_t * logptr = path->private;
u8 reason = (u8) ipuser[8];
- printk (KERN_ERR "vmlogrdr: connection severed with"
- " reason %i\n", reason);
+ pr_err("vmlogrdr: connection severed with reason %i\n", reason);
iucv_path_sever(path, NULL);
kfree(path);
@@ -333,8 +334,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
if (ret)
- printk (KERN_WARNING "vmlogrdr: failed to start "
- "recording automatically\n");
+ pr_warning("vmlogrdr: failed to start "
+ "recording automatically\n");
}
/* create connection to the system service */
@@ -345,9 +346,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
logptr->system_service, NULL, NULL,
logptr);
if (connect_rc) {
- printk (KERN_ERR "vmlogrdr: iucv connection to %s "
- "failed with rc %i \n", logptr->system_service,
- connect_rc);
+ pr_err("vmlogrdr: iucv connection to %s "
+ "failed with rc %i \n",
+ logptr->system_service, connect_rc);
goto out_path;
}
@@ -388,8 +389,8 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp)
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
if (ret)
- printk (KERN_WARNING "vmlogrdr: failed to stop "
- "recording automatically\n");
+ pr_warning("vmlogrdr: failed to stop "
+ "recording automatically\n");
}
logptr->dev_in_use = 0;
@@ -823,8 +824,7 @@ static int __init vmlogrdr_init(void)
dev_t dev;
if (! MACHINE_IS_VM) {
- printk (KERN_ERR "vmlogrdr: not running under VM, "
- "driver not loaded.\n");
+ pr_err("not running under VM, driver not loaded.\n");
return -ENODEV;
}
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 9020eba620ee..5dcef81fc9d9 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -8,6 +8,9 @@
* Frank Munzert <munzert@de.ibm.com>
*/
+#define KMSG_COMPONENT "vmur"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/cdev.h>
#include <linux/smp_lock.h>
@@ -40,8 +43,6 @@ MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver");
MODULE_LICENSE("GPL");
-#define PRINTK_HEADER "vmur: "
-
static dev_t ur_first_dev_maj_min;
static struct class *vmur_class;
static struct debug_info *vmur_dbf;
@@ -987,7 +988,8 @@ static int __init ur_init(void)
dev_t dev;
if (!MACHINE_IS_VM) {
- PRINT_ERR("%s is only available under z/VM.\n", ur_banner);
+ pr_err("The %s cannot be loaded without z/VM\n",
+ ur_banner);
return -ENODEV;
}
@@ -1006,7 +1008,8 @@ static int __init ur_init(void)
rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
if (rc) {
- PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc);
+ pr_err("Kernel function alloc_chrdev_region failed with "
+ "error code %d\n", rc);
goto fail_unregister_driver;
}
ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
@@ -1016,7 +1019,7 @@ static int __init ur_init(void)
rc = PTR_ERR(vmur_class);
goto fail_unregister_region;
}
- PRINT_INFO("%s loaded.\n", ur_banner);
+ pr_info("%s loaded.\n", ur_banner);
return 0;
fail_unregister_region:
@@ -1034,7 +1037,7 @@ static void __exit ur_exit(void)
unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
ccw_driver_unregister(&ur_driver);
debug_unregister(vmur_dbf);
- PRINT_INFO("%s unloaded.\n", ur_banner);
+ pr_info("%s unloaded.\n", ur_banner);
}
module_init(ur_init);
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 7fd84be11931..eefc6611412e 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -9,6 +9,9 @@
* Author(s): Michael Holzheu
*/
+#define KMSG_COMPONENT "zdump"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/utsname.h>
@@ -24,8 +27,6 @@
#include "sclp.h"
#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
-#define MSG(x...) printk( KERN_ALERT x )
-#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
#define TO_USER 0
#define TO_KERNEL 1
@@ -563,19 +564,19 @@ static int __init sys_info_init(enum arch_id arch)
switch (arch) {
case ARCH_S390X:
- MSG("DETECTED 'S390X (64 bit) OS'\n");
+ pr_alert("DETECTED 'S390X (64 bit) OS'\n");
sys_info.sa_base = SAVE_AREA_BASE_S390X;
sys_info.sa_size = sizeof(struct save_area_s390x);
set_s390x_lc_mask(&sys_info.lc_mask);
break;
case ARCH_S390:
- MSG("DETECTED 'S390 (32 bit) OS'\n");
+ pr_alert("DETECTED 'S390 (32 bit) OS'\n");
sys_info.sa_base = SAVE_AREA_BASE_S390;
sys_info.sa_size = sizeof(struct save_area_s390);
set_s390_lc_mask(&sys_info.lc_mask);
break;
default:
- ERROR_MSG("unknown architecture 0x%x.\n",arch);
+ pr_alert("0x%x is an unknown architecture.\n",arch);
return -EINVAL;
}
sys_info.arch = arch;
@@ -674,7 +675,8 @@ static int __init zcore_init(void)
#ifndef __s390x__
if (arch == ARCH_S390X) {
- ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+ pr_alert("The 32-bit dump tool cannot be used for a "
+ "64-bit system\n");
rc = -EINVAL;
goto fail;
}
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 2f547b840ef0..fe00be3675cd 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -9,6 +9,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -50,9 +53,10 @@ static int blacklist_range(range_action action, unsigned int from_ssid,
{
if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
if (msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore range "
- "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
- to_ssid, to);
+ pr_warning("0.%x.%04x to 0.%x.%04x is not a valid "
+ "range for cio_ignore\n", from_ssid, from,
+ to_ssid, to);
+
return 1;
}
@@ -140,8 +144,8 @@ static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
rc = 0;
out:
if (rc && msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
- str);
+ pr_warning("%s is not a valid device for the cio_ignore "
+ "kernel parameter\n", str);
return rc;
}
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 3ac2c2019f5e..918e6fce2573 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -19,6 +19,8 @@
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
+#define CCW_BUS_ID_SIZE 20
+
/* In Linux 2.4, we had a channel device layer called "chandev"
* that did all sorts of obscure stuff for networking devices.
* This is another driver that serves as a replacement for just
@@ -89,15 +91,23 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
gdev = to_ccwgroupdev(dev);
- if (gdev->state != CCWGROUP_OFFLINE)
- return -EINVAL;
-
+ /* Prevent concurrent online/offline processing and ungrouping. */
+ if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
+ return -EAGAIN;
+ if (gdev->state != CCWGROUP_OFFLINE) {
+ rc = -EINVAL;
+ goto out;
+ }
/* Note that we cannot unregister the device from one of its
* attribute methods, so we have to use this roundabout approach.
*/
rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
- if (rc)
- count = rc;
+out:
+ if (rc) {
+ /* Release onoff "lock" when ungrouping failed. */
+ atomic_set(&gdev->onoff, 0);
+ return rc;
+ }
return count;
}
@@ -172,7 +182,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
len = end - start + 1;
end++;
}
- if (len < BUS_ID_SIZE) {
+ if (len < CCW_BUS_ID_SIZE) {
strlcpy(bus_id, start, len);
rc = 0;
} else
@@ -181,7 +191,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
return rc;
}
-static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
+static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
{
int cssid, ssid, devno;
@@ -213,7 +223,7 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
{
struct ccwgroup_device *gdev;
int rc, i;
- char tmp_bus_id[BUS_ID_SIZE];
+ char tmp_bus_id[CCW_BUS_ID_SIZE];
const char *curr_buf;
gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 29826fdd47b8..ebab6ea4659b 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -8,6 +8,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -333,6 +336,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
struct chp_config_data *data;
struct chp_id chpid;
int num;
+ char *events[3] = {"configure", "deconfigure", "cancel deconfigure"};
CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
if (sei_area->rs != 0)
@@ -343,8 +347,8 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
if (!chp_test_bit(data->map, num))
continue;
chpid.id = num;
- printk(KERN_WARNING "cio: processing configure event %d for "
- "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+ pr_notice("Processing %s for channel path %x.%02x\n",
+ events[data->op], chpid.cssid, chpid.id);
switch (data->op) {
case 0:
chp_cfg_schedule(chpid, 1);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index f49f0e502b8d..0a2f2edafc03 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
}
private->request = NULL;
memcpy(&request->irb, irb, sizeof(*irb));
- stsch(sch->schid, &sch->schib);
+ cio_update_schib(sch);
complete(&request->completion);
put_device(&sch->dev);
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 3db2c386546f..8a8df7552969 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -9,6 +9,9 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -104,44 +107,6 @@ cio_get_options (struct subchannel *sch)
return flags;
}
-/*
- * Use tpi to get a pending interrupt, call the interrupt handler and
- * return a pointer to the subchannel structure.
- */
-static int
-cio_tpi(void)
-{
- struct tpi_info *tpi_info;
- struct subchannel *sch;
- struct irb *irb;
- int irq_context;
-
- tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
- if (tpi (NULL) != 1)
- return 0;
- irb = (struct irb *) __LC_IRB;
- /* Store interrupt response block to lowcore. */
- if (tsch (tpi_info->schid, irb) != 0)
- /* Not status pending or not operational. */
- return 1;
- sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
- if (!sch)
- return 1;
- irq_context = in_interrupt();
- if (!irq_context)
- local_bh_disable();
- irq_enter ();
- spin_lock(sch->lock);
- memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
- if (sch->driver && sch->driver->irq)
- sch->driver->irq(sch);
- spin_unlock(sch->lock);
- irq_exit ();
- if (!irq_context)
- _local_bh_enable();
- return 1;
-}
-
static int
cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
{
@@ -152,11 +117,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
else
sch->lpm = 0;
- stsch (sch->schid, &sch->schib);
-
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
+
+ if (cio_update_schib(sch))
+ return -ENODEV;
+
sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -354,7 +321,8 @@ cio_cancel (struct subchannel *sch)
switch (ccode) {
case 0: /* success */
/* Update information in scsw. */
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ return -ENODEV;
return 0;
case 1: /* status pending */
return -EBUSY;
@@ -365,30 +333,70 @@ cio_cancel (struct subchannel *sch)
}
}
+
+static void cio_apply_config(struct subchannel *sch, struct schib *schib)
+{
+ schib->pmcw.intparm = sch->config.intparm;
+ schib->pmcw.mbi = sch->config.mbi;
+ schib->pmcw.isc = sch->config.isc;
+ schib->pmcw.ena = sch->config.ena;
+ schib->pmcw.mme = sch->config.mme;
+ schib->pmcw.mp = sch->config.mp;
+ schib->pmcw.csense = sch->config.csense;
+ schib->pmcw.mbfc = sch->config.mbfc;
+ if (sch->config.mbfc)
+ schib->mba = sch->config.mba;
+}
+
+static int cio_check_config(struct subchannel *sch, struct schib *schib)
+{
+ return (schib->pmcw.intparm == sch->config.intparm) &&
+ (schib->pmcw.mbi == sch->config.mbi) &&
+ (schib->pmcw.isc == sch->config.isc) &&
+ (schib->pmcw.ena == sch->config.ena) &&
+ (schib->pmcw.mme == sch->config.mme) &&
+ (schib->pmcw.mp == sch->config.mp) &&
+ (schib->pmcw.csense == sch->config.csense) &&
+ (schib->pmcw.mbfc == sch->config.mbfc) &&
+ (!sch->config.mbfc || (schib->mba == sch->config.mba));
+}
+
/*
- * Function: cio_modify
- * Issues a "Modify Subchannel" on the specified subchannel
+ * cio_commit_config - apply configuration to the subchannel
*/
-int
-cio_modify (struct subchannel *sch)
+int cio_commit_config(struct subchannel *sch)
{
- int ccode, retry, ret;
+ struct schib schib;
+ int ccode, retry, ret = 0;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
- ret = 0;
for (retry = 0; retry < 5; retry++) {
- ccode = msch_err (sch->schid, &sch->schib);
- if (ccode < 0) /* -EIO if msch gets a program check. */
+ /* copy desired changes to local schib */
+ cio_apply_config(sch, &schib);
+ ccode = msch_err(sch->schid, &schib);
+ if (ccode < 0) /* -EIO if msch gets a program check. */
return ccode;
switch (ccode) {
case 0: /* successfull */
- return 0;
- case 1: /* status pending */
+ if (stsch(sch->schid, &schib) ||
+ !css_sch_is_valid(&schib))
+ return -ENODEV;
+ if (cio_check_config(sch, &schib)) {
+ /* commit changes from local schib */
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+ }
+ ret = -EAGAIN;
+ break;
+ case 1: /* status pending */
return -EBUSY;
- case 2: /* busy */
- udelay (100); /* allow for recovery */
+ case 2: /* busy */
+ udelay(100); /* allow for recovery */
ret = -EBUSY;
break;
- case 3: /* not operational */
+ case 3: /* not operational */
return -ENODEV;
}
}
@@ -396,6 +404,23 @@ cio_modify (struct subchannel *sch)
}
/**
+ * cio_update_schib - Perform stsch and update schib if subchannel is valid.
+ * @sch: subchannel on which to perform stsch
+ * Return zero on success, -ENODEV otherwise.
+ */
+int cio_update_schib(struct subchannel *sch)
+{
+ struct schib schib;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
+
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cio_update_schib);
+
+/**
* cio_enable_subchannel - enable a subchannel.
* @sch: subchannel to be enabled
* @intparm: interruption parameter to set
@@ -403,7 +428,6 @@ cio_modify (struct subchannel *sch)
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
char dbf_txt[15];
- int ccode;
int retry;
int ret;
@@ -412,33 +436,27 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
if (sch_is_pseudo_sch(sch))
return -EINVAL;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode)
+ if (cio_update_schib(sch))
return -ENODEV;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 1;
- sch->schib.pmcw.isc = sch->isc;
- sch->schib.pmcw.intparm = intparm;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EIO)
+ sch->config.ena = 1;
+ sch->config.isc = sch->isc;
+ sch->config.intparm = intparm;
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = cio_commit_config(sch);
+ if (ret == -EIO) {
/*
- * Got a program check in cio_modify. Try without
+ * Got a program check in msch. Try without
* the concurrent sense bit the next time.
*/
- sch->schib.pmcw.csense = 0;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (sch->schib.pmcw.ena)
- break;
- }
- if (ret == -EBUSY) {
+ sch->config.csense = 0;
+ } else if (ret == -EBUSY) {
struct irb irb;
if (tsch(sch->schid, &irb) != 0)
break;
- }
+ } else
+ break;
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
@@ -453,8 +471,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
- int ccode;
- int retry;
int ret;
CIO_TRACE_EVENT (2, "dissch");
@@ -462,8 +478,7 @@ int cio_disable_subchannel(struct subchannel *sch)
if (sch_is_pseudo_sch(sch))
return 0;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode == 3) /* Not operational. */
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
@@ -473,24 +488,9 @@ int cio_disable_subchannel(struct subchannel *sch)
*/
return -EBUSY;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 0;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EBUSY)
- /*
- * The subchannel is busy or status pending.
- * We'll disable when the next interrupt was delivered
- * via the state machine.
- */
- break;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (!sch->schib.pmcw.ena)
- break;
- }
- }
+ sch->config.ena = 0;
+ ret = cio_commit_config(sch);
+
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
@@ -687,6 +687,43 @@ static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
+/*
+ * Use tpi to get a pending interrupt, call the interrupt handler and
+ * return a pointer to the subchannel structure.
+ */
+static int cio_tpi(void)
+{
+ struct tpi_info *tpi_info;
+ struct subchannel *sch;
+ struct irb *irb;
+ int irq_context;
+
+ tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
+ if (tpi(NULL) != 1)
+ return 0;
+ irb = (struct irb *) __LC_IRB;
+ /* Store interrupt response block to lowcore. */
+ if (tsch(tpi_info->schid, irb) != 0)
+ /* Not status pending or not operational. */
+ return 1;
+ sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
+ if (!sch)
+ return 1;
+ irq_context = in_interrupt();
+ if (!irq_context)
+ local_bh_disable();
+ irq_enter();
+ spin_lock(sch->lock);
+ memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
+ if (sch->driver && sch->driver->irq)
+ sch->driver->irq(sch);
+ spin_unlock(sch->lock);
+ irq_exit();
+ if (!irq_context)
+ _local_bh_enable();
+ return 1;
+}
+
void *cio_get_console_priv(void)
{
return &console_priv;
@@ -780,7 +817,7 @@ cio_probe_console(void)
sch_no = cio_get_console_sch_no();
if (sch_no == -1) {
console_subchannel_in_use = 0;
- printk(KERN_WARNING "cio: No ccw console found!\n");
+ pr_warning("No CCW console was found\n");
return ERR_PTR(-ENODEV);
}
memset(&console_subchannel, 0, sizeof(struct subchannel));
@@ -796,10 +833,9 @@ cio_probe_console(void)
* enable console I/O-interrupt subclass
*/
isc_register(CONSOLE_ISC);
- console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
- console_subchannel.schib.pmcw.intparm =
- (u32)(addr_t)&console_subchannel;
- ret = cio_modify(&console_subchannel);
+ console_subchannel.config.isc = CONSOLE_ISC;
+ console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
+ ret = cio_commit_config(&console_subchannel);
if (ret) {
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
@@ -811,8 +847,8 @@ cio_probe_console(void)
void
cio_release_console(void)
{
- console_subchannel.schib.pmcw.intparm = 0;
- cio_modify(&console_subchannel);
+ console_subchannel.config.intparm = 0;
+ cio_commit_config(&console_subchannel);
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
}
@@ -852,7 +888,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
cc = msch(schid, schib);
if (cc)
return (cc==3?-ENODEV:-EBUSY);
- stsch(schid, schib);
+ if (stsch(schid, schib) || !css_sch_is_valid(schib))
+ return -ENODEV;
if (!schib->pmcw.ena)
return 0;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 0fb24784e925..5150fba742ac 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -45,6 +45,19 @@ struct pmcw {
/* ... in an operand exception. */
} __attribute__ ((packed));
+/* Target SCHIB configuration. */
+struct schib_config {
+ u64 mba;
+ u32 intparm;
+ u16 mbi;
+ u32 isc:3;
+ u32 ena:1;
+ u32 mme:2;
+ u32 mp:1;
+ u32 csense:1;
+ u32 mbfc:1;
+} __attribute__ ((packed));
+
/*
* subchannel information block
*/
@@ -82,6 +95,8 @@ struct subchannel {
struct device dev; /* entry in device tree */
struct css_driver *driver;
void *private; /* private per subchannel type data */
+ struct work_struct work;
+ struct schib_config config;
} __attribute__ ((aligned(8)));
#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
@@ -100,7 +115,8 @@ extern int cio_start_key (struct subchannel *, struct ccw1 *, __u8, __u8);
extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
-extern int cio_modify (struct subchannel *);
+extern int cio_update_schib(struct subchannel *sch);
+extern int cio_commit_config(struct subchannel *sch);
int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index a90b28c0be57..dc98b2c63862 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -25,6 +25,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/bootmem.h>
#include <linux/device.h>
#include <linux/init.h>
@@ -185,56 +188,19 @@ static inline void cmf_activate(void *area, unsigned int onoff)
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
- int ret;
- int retry;
struct subchannel *sch;
- struct schib *schib;
sch = to_subchannel(cdev->dev.parent);
- schib = &sch->schib;
- /* msch can silently fail, so do it again if necessary */
- for (retry = 0; retry < 3; retry++) {
- /* prepare schib */
- stsch(sch->schid, schib);
- schib->pmcw.mme = mme;
- schib->pmcw.mbfc = mbfc;
- /* address can be either a block address or a block index */
- if (mbfc)
- schib->mba = address;
- else
- schib->pmcw.mbi = address;
-
- /* try to submit it */
- switch(ret = msch_err(sch->schid, schib)) {
- case 0:
- break;
- case 1:
- case 2: /* in I/O or status pending */
- ret = -EBUSY;
- break;
- case 3: /* subchannel is no longer valid */
- ret = -ENODEV;
- break;
- default: /* msch caught an exception */
- ret = -EINVAL;
- break;
- }
- stsch(sch->schid, schib); /* restore the schib */
-
- if (ret)
- break;
- /* check if it worked */
- if (schib->pmcw.mme == mme &&
- schib->pmcw.mbfc == mbfc &&
- (mbfc ? (schib->mba == address)
- : (schib->pmcw.mbi == address)))
- return 0;
+ sch->config.mme = mme;
+ sch->config.mbfc = mbfc;
+ /* address can be either a block address or a block index */
+ if (mbfc)
+ sch->config.mba = address;
+ else
+ sch->config.mbi = address;
- ret = -EINVAL;
- }
-
- return ret;
+ return cio_commit_config(sch);
}
struct set_schib_struct {
@@ -338,7 +304,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
@@ -1359,9 +1325,8 @@ static int __init init_cmf(void)
default:
return 1;
}
-
- printk(KERN_INFO "cio: Channel measurement facility using %s "
- "format (%s)\n", format_string, detect_string);
+ pr_info("Channel measurement facility initialized using format "
+ "%s (mode %s)\n", format_string, detect_string);
return 0;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 76bbb1e74c29..8019288bc6de 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -6,6 +6,10 @@
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
+
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -128,8 +132,8 @@ css_free_subchannel(struct subchannel *sch)
{
if (sch) {
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
kfree(sch->lock);
kfree(sch);
}
@@ -844,8 +848,8 @@ out:
s390_unregister_crw_handler(CRW_RSC_CSS);
chsc_free_sei_area();
kfree(slow_subchannel_set);
- printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
- ret);
+ pr_alert("The CSS device driver initialization failed with "
+ "errno=%d\n", ret);
return ret;
}
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 4e4008325e28..23d5752349b5 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -376,19 +376,23 @@ int ccw_device_set_offline(struct ccw_device *cdev)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
}
spin_unlock_irq(cdev->ccwlock);
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
return ret;
}
spin_unlock_irq(cdev->ccwlock);
- if (ret == 0)
+ if (ret == 0) {
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
- else {
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
+ } else {
CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
cdev->online = 1;
}
- return ret;
+ return ret;
}
/**
@@ -411,6 +415,9 @@ int ccw_device_set_online(struct ccw_device *cdev)
return -ENODEV;
if (cdev->online || !cdev->drv)
return -EINVAL;
+ /* Hold on to an extra reference while device is online. */
+ if (!get_device(&cdev->dev))
+ return -ENODEV;
spin_lock_irq(cdev->ccwlock);
ret = ccw_device_online(cdev);
@@ -422,10 +429,15 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return ret;
}
- if (cdev->private->state != DEV_STATE_ONLINE)
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return -ENODEV;
+ }
if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
cdev->online = 1;
return 0;
@@ -440,6 +452,8 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return (ret == 0) ? -ENODEV : ret;
}
@@ -704,6 +718,8 @@ ccw_device_release(struct device *dev)
struct ccw_device *cdev;
cdev = to_ccwdev(dev);
+ /* Release reference of parent subchannel. */
+ put_device(cdev->dev.parent);
kfree(cdev->private);
kfree(cdev);
}
@@ -735,8 +751,8 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
+ /* Release reference from device_initialize(). */
+ put_device(&cdev->dev);
return -ENODEV;
}
return 0;
@@ -778,37 +794,55 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
struct subchannel *other_sch;
int ret;
- other_sch = to_subchannel(get_device(cdev->dev.parent));
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ other_sch = to_subchannel(cdev->dev.parent);
+ /* Note: device_move() changes cdev->dev.parent */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&other_sch->dev);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_set_cdev(other_sch, NULL);
/* No need to keep a subchannel without ccw device around. */
css_sch_device_unregister(other_sch);
- put_device(&other_sch->dev);
sch_attach_device(sch, cdev);
+ /* Put reference for old parent. */
+ put_device(&other_sch->dev);
}
static void sch_attach_orphaned_device(struct subchannel *sch,
struct ccw_device *cdev)
{
int ret;
+ struct subchannel *pseudo_sch;
- /* Try to move the ccw device to its new subchannel. */
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ pseudo_sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
"failed (ret=%d)!\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_attach_device(sch, cdev);
+ /* Put reference on pseudo subchannel. */
+ put_device(&pseudo_sch->dev);
}
static void sch_create_and_recog_new_device(struct subchannel *sch)
@@ -830,9 +864,11 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
spin_lock_irq(sch->lock);
sch_set_cdev(sch, NULL);
spin_unlock_irq(sch->lock);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
css_sch_device_unregister(sch);
+ /* Put reference from io_subchannel_create_ccwdev(). */
+ put_device(&sch->dev);
+ /* Give up initial reference. */
+ put_device(&cdev->dev);
}
}
@@ -854,15 +890,20 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ /* Increase refcount for pseudo subchannel. */
+ get_device(&css->pseudo_subchannel->dev);
/*
* Move the orphaned ccw device to the orphanage so the replacing
* ccw device can take its place on the subchannel.
+ * Note: device_move() changes cdev->dev.parent
*/
ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Decrease refcount for pseudo subchannel again. */
+ put_device(&css->pseudo_subchannel->dev);
return;
}
cdev->ccwlock = css->pseudo_subchannel->lock;
@@ -875,17 +916,23 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
if (replacing_cdev) {
sch_attach_disconnected_device(sch, replacing_cdev);
/* Release reference from get_disc_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
if (replacing_cdev) {
sch_attach_orphaned_device(sch, replacing_cdev);
/* Release reference from get_orphaned_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
sch_create_and_recog_new_device(sch);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
}
/*
@@ -903,6 +950,14 @@ io_subchannel_register(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Check if subchannel is still registered. It may have become
+ * unregistered if a machine check hit us after finishing
+ * device recognition but before the register work could be
+ * queued.
+ */
+ if (!device_is_registered(&sch->dev))
+ goto out_err;
css_update_ssd_info(sch);
/*
* io_subchannel_register() will also be called after device
@@ -910,7 +965,7 @@ io_subchannel_register(struct work_struct *work)
* be registered). We need to reprobe since we may now have sense id
* information.
*/
- if (klist_node_attached(&cdev->dev.knode_parent)) {
+ if (device_is_registered(&cdev->dev)) {
if (!cdev->drv) {
ret = device_reprobe(&cdev->dev);
if (ret)
@@ -934,22 +989,19 @@ io_subchannel_register(struct work_struct *work)
CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&cdev->dev);
spin_lock_irqsave(sch->lock, flags);
sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
- kfree (cdev->private);
- kfree (cdev);
- put_device(&sch->dev);
- if (atomic_dec_and_test(&ccw_device_init_count))
- wake_up(&ccw_device_init_wq);
- return;
+ /* Release initial device reference. */
+ put_device(&cdev->dev);
+ goto out_err;
}
- put_device(&cdev->dev);
out:
cdev->private->flags.recog_done = 1;
- put_device(&sch->dev);
wake_up(&cdev->private->wait_q);
+out_err:
+ /* Release reference for workqueue processing. */
+ put_device(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
@@ -968,8 +1020,8 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
/* Release cdev reference for workqueue processing.*/
put_device(&cdev->dev);
/* Release subchannel reference for local processing. */
@@ -998,8 +1050,6 @@ io_subchannel_recog_done(struct ccw_device *cdev)
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister);
queue_work(slow_path_wq, &cdev->private->kick_work);
- /* Release subchannel reference for asynchronous recognition. */
- put_device(&sch->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
@@ -1070,10 +1120,15 @@ static void ccw_device_move_to_sch(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
sch = priv->sch;
cdev = priv->cdev;
- former_parent = ccw_device_is_orphan(cdev) ?
- NULL : to_subchannel(get_device(cdev->dev.parent));
+ former_parent = to_subchannel(cdev->dev.parent);
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
mutex_lock(&sch->reg_mutex);
- /* Try to move the ccw device to its new subchannel. */
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
rc = device_move(&cdev->dev, &sch->dev);
mutex_unlock(&sch->reg_mutex);
if (rc) {
@@ -1083,21 +1138,23 @@ static void ccw_device_move_to_sch(struct work_struct *work)
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schid.sch_no, rc);
css_sch_device_unregister(sch);
+ /* Put reference for new parent again. */
+ put_device(&sch->dev);
goto out;
}
- if (former_parent) {
+ if (!sch_is_pseudo_sch(former_parent)) {
spin_lock_irq(former_parent->lock);
sch_set_cdev(former_parent, NULL);
spin_unlock_irq(former_parent->lock);
css_sch_device_unregister(former_parent);
/* Reset intparm to zeroes. */
- former_parent->schib.pmcw.intparm = 0;
- cio_modify(former_parent);
+ former_parent->config.intparm = 0;
+ cio_commit_config(former_parent);
}
sch_attach_device(sch, cdev);
out:
- if (former_parent)
- put_device(&former_parent->dev);
+ /* Put reference for old parent. */
+ put_device(&former_parent->dev);
put_device(&cdev->dev);
}
@@ -1113,6 +1170,15 @@ static void io_subchannel_irq(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
+void io_subchannel_init_config(struct subchannel *sch)
+{
+ memset(&sch->config, 0, sizeof(sch->config));
+ sch->config.csense = 1;
+ /* Use subchannel mp mode when there is more than 1 installed CHPID. */
+ if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
+ sch->config.mp = 1;
+}
+
static void io_subchannel_init_fields(struct subchannel *sch)
{
if (cio_is_console(sch->schid))
@@ -1127,18 +1193,34 @@ static void io_subchannel_init_fields(struct subchannel *sch)
sch->schib.pmcw.dev, sch->schid.ssid,
sch->schid.sch_no, sch->schib.pmcw.pim,
sch->schib.pmcw.pam, sch->schib.pmcw.pom);
- /* Initially set up some fields in the pmcw. */
- sch->schib.pmcw.ena = 0;
- sch->schib.pmcw.csense = 1; /* concurrent sense */
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1; /* multipath mode */
- /* clean up possible residual cmf stuff */
- sch->schib.pmcw.mme = 0;
- sch->schib.pmcw.mbfc = 0;
- sch->schib.pmcw.mbi = 0;
- sch->schib.mba = 0;
+
+ io_subchannel_init_config(sch);
}
+static void io_subchannel_do_unreg(struct work_struct *work)
+{
+ struct subchannel *sch;
+
+ sch = container_of(work, struct subchannel, work);
+ css_sch_device_unregister(sch);
+ /* Reset intparm to zeroes. */
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
+ put_device(&sch->dev);
+}
+
+/* Schedule unregister if we have no cdev. */
+static void io_subchannel_schedule_removal(struct subchannel *sch)
+{
+ get_device(&sch->dev);
+ INIT_WORK(&sch->work, io_subchannel_do_unreg);
+ queue_work(slow_path_wq, &sch->work);
+}
+
+/*
+ * Note: We always return 0 so that we bind to the device even on error.
+ * This is needed so that our remove function is called on unregister.
+ */
static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
@@ -1168,9 +1250,8 @@ static int io_subchannel_probe(struct subchannel *sch)
ccw_device_register(cdev);
/*
* Check if the device is already online. If it is
- * the reference count needs to be corrected
- * (see ccw_device_online and css_init_done for the
- * ugly details).
+ * the reference count needs to be corrected since we
+ * didn't obtain a reference in ccw_device_set_online.
*/
if (cdev->private->state != DEV_STATE_NOT_OPER &&
cdev->private->state != DEV_STATE_OFFLINE &&
@@ -1179,23 +1260,24 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
io_subchannel_init_fields(sch);
- /*
- * First check if a fitting device may be found amongst the
- * disconnected devices or in the orphanage.
- */
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
+ rc = cio_commit_config(sch);
+ if (rc)
+ goto out_schedule;
rc = sysfs_create_group(&sch->dev.kobj,
&io_subchannel_attr_group);
if (rc)
- return rc;
+ goto out_schedule;
/* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
- if (!sch->private) {
- rc = -ENOMEM;
+ if (!sch->private)
goto out_err;
- }
+ /*
+ * First check if a fitting device may be found amongst the
+ * disconnected devices or in the orphanage.
+ */
+ dev_id.devno = sch->schib.pmcw.dev;
+ dev_id.ssid = sch->schid.ssid;
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1213,24 +1295,21 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
cdev = io_subchannel_create_ccwdev(sch);
- if (IS_ERR(cdev)) {
- rc = PTR_ERR(cdev);
+ if (IS_ERR(cdev))
goto out_err;
- }
rc = io_subchannel_recog(cdev, sch);
if (rc) {
spin_lock_irqsave(sch->lock, flags);
- sch_set_cdev(sch, NULL);
+ io_subchannel_recog_done(cdev);
spin_unlock_irqrestore(sch->lock, flags);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
- goto out_err;
}
return 0;
out_err:
kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
- return rc;
+out_schedule:
+ io_subchannel_schedule_removal(sch);
+ return 0;
}
static int
@@ -1275,10 +1354,7 @@ static void io_subchannel_verify(struct subchannel *sch)
static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
+ if (cio_update_schib(sch))
return 0;
if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
return 1;
@@ -1347,15 +1423,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
io_subchannel_verify(sch);
break;
case CHP_OFFLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
- if (!css_sch_is_valid(&sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_ONLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
+ if (cio_update_schib(sch))
+ return -ENODEV;
sch->lpm |= mask & sch->opm;
io_subchannel_verify(sch);
break;
@@ -1610,8 +1684,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
spin_lock_irqsave(sch->lock, flags);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
break;
case REPROBE:
ccw_device_trigger_reprobe(cdev);
@@ -1652,6 +1726,9 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
io_subchannel_init_fields(sch);
+ rc = cio_commit_config(sch);
+ if (rc)
+ return rc;
sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
@@ -1723,7 +1800,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
+ return (strcmp(bus_id, dev_name(dev)) == 0);
}
@@ -1806,6 +1883,8 @@ ccw_device_remove (struct device *dev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up reference obtained in ccw_device_set_online(). */
+ put_device(&cdev->dev);
}
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 104ed669db43..0f2e63ea48de 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -76,6 +76,7 @@ extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
void io_subchannel_recog_done(struct ccw_device *cdev);
+void io_subchannel_init_config(struct subchannel *sch);
int ccw_device_cancel_halt_clear(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 10bc03940fb3..8df5eaafc5ab 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
int ret;
sch = to_subchannel(cdev->dev.parent);
- ret = stsch(sch->schid, &sch->schib);
- if (ret || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (!sch->schib.pmcw.ena)
/* Not operational -> done. */
@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
* through ssch() and the path information is up to date.
*/
old_lpm = sch->lpm;
- stsch(sch->schid, &sch->schib);
- sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
/* Check since device may again have become not operational. */
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
state = DEV_STATE_NOT_OPER;
+ else
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
/* Force reprobe on all chpids. */
old_lpm = 0;
@@ -399,9 +400,6 @@ ccw_device_done(struct ccw_device *cdev, int state)
ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);
-
- if (css_init_done && state != DEV_STATE_ONLINE)
- put_device (&cdev->dev);
}
static int cmp_pgid(struct pgid *p1, struct pgid *p2)
@@ -552,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
/* Update schib - pom may have changed. */
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ return;
+ }
/* Update lpm with verified path mask. */
sch->lpm = sch->vpm;
/* Repeat path verification? */
@@ -611,8 +613,6 @@ ccw_device_online(struct ccw_device *cdev)
(cdev->private->state != DEV_STATE_BOXED))
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- if (css_init_done && !get_device(&cdev->dev))
- return -ENODEV;
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
if (ret != 0) {
/* Couldn't enable the subchannel for i/o. Sick device. */
@@ -672,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev)
return 0;
}
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY;
@@ -750,7 +750,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
* Since we might not just be coming from an interrupt from the
* subchannel we have to update the schib.
*/
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ ccw_device_verify_done(cdev, -ENODEV);
+ return;
+ }
if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
@@ -1016,20 +1019,21 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
/* Update some values. */
- if (stsch(sch->schid, &sch->schib))
- return;
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return;
/*
* The pim, pam, pom values may not be accurate, but they are the best
* we have before performing device selection :/
*/
sch->lpm = sch->schib.pmcw.pam & sch->opm;
- /* Re-set some bits in the pmcw that were lost. */
- sch->schib.pmcw.csense = 1;
- sch->schib.pmcw.ena = 0;
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1;
+ /*
+ * Use the initial configuration since we can't be shure that the old
+ * paths are valid.
+ */
+ io_subchannel_init_config(sch);
+ if (cio_commit_config(sch))
+ return;
+
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 86bc94eb607f..fc5ca1dd52b3 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev)
sch->vpm = 0;
/* Get current pam. */
- if (stsch(sch->schid, &sch->schib)) {
+ if (cio_update_schib(sch)) {
ccw_device_verify_done(cdev, -ENODEV);
return;
}
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 1b03c5423be2..5814dbee2410 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ goto doverify;
CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
"not operational \n", __func__,
@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
+doverify:
cdev->private->flags.doverify = 1;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index e3ea1d5f2810..42f2b09631b6 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -10,10 +10,10 @@
#include <asm/page.h>
#include <asm/schid.h>
+#include <asm/debug.h>
#include "chsc.h"
#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
-#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
/*
@@ -111,12 +111,12 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue,
}
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count)
+ int *start, int *count, int ack)
{
register unsigned long _ccq asm ("0") = *count;
register unsigned long _token asm ("1") = token;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- unsigned long _state = 0;
+ unsigned long _state = (unsigned long)ack << 63;
asm volatile(
" .insn rrf,0xB99c0000,%1,%2,0,0"
@@ -133,7 +133,7 @@ static inline int do_eqbs(u64 token, unsigned char *state, int queue,
static inline int do_sqbs(u64 token, unsigned char state, int queue,
int *start, int *count) { return 0; }
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count) { return 0; }
+ int *start, int *count, int ack) { return 0; }
#endif /* CONFIG_64BIT */
struct qdio_irq;
@@ -186,20 +186,14 @@ struct qdio_input_q {
/* input buffer acknowledgement flag */
int polling;
+ /* how much sbals are acknowledged with qebsm */
+ int ack_count;
+
/* last time of noticing incoming data */
u64 timestamp;
-
- /* lock for clearing the acknowledgement */
- spinlock_t lock;
};
struct qdio_output_q {
- /* failed siga-w attempts*/
- atomic_t busy_siga_counter;
-
- /* start time of busy condition */
- u64 timestamp;
-
/* PCIs are enabled for the queue */
int pci_out_enabled;
@@ -250,6 +244,7 @@ struct qdio_q {
struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet;
+ spinlock_t lock;
/* error condition during a data transfer */
unsigned int qdio_error;
@@ -300,11 +295,13 @@ struct qdio_irq {
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
+ debug_info_t *debug_area;
struct mutex setup_mutex;
};
/* helper functions */
#define queue_type(q) q->irq_ptr->qib.qfmt
+#define SCH_NO(q) (q->irq_ptr->schid.sch_no)
#define is_thinint_irq(irq) \
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
@@ -348,10 +345,13 @@ static inline unsigned long long get_usecs(void)
((bufnr + 1) & QDIO_MAX_BUFFERS_MASK)
#define add_buf(bufnr, inc) \
((bufnr + inc) & QDIO_MAX_BUFFERS_MASK)
+#define sub_buf(bufnr, dec) \
+ ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK)
/* prototypes for thin interrupt */
void qdio_sync_after_thinint(struct qdio_q *q);
-int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state);
+int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state,
+ int auto_ack);
void qdio_check_outbound_after_thinint(struct qdio_q *q);
int qdio_inbound_q_moved(struct qdio_q *q);
void qdio_kick_inbound_handler(struct qdio_q *q);
@@ -378,10 +378,15 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs,
int nr_output_qs);
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr);
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data);
int qdio_setup_irq(struct qdio_initialize *init_data);
void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_release_memory(struct qdio_irq *irq_ptr);
+int qdio_setup_create_sysfs(struct ccw_device *cdev);
+void qdio_setup_destroy_sysfs(struct ccw_device *cdev);
int qdio_setup_init(void);
void qdio_setup_exit(void);
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f05590355be8..f8a3b6967f69 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -14,7 +14,7 @@
#include "qdio.h"
debug_info_t *qdio_dbf_setup;
-debug_info_t *qdio_dbf_trace;
+debug_info_t *qdio_dbf_error;
static struct dentry *debugfs_root;
#define MAX_DEBUGFS_QUEUES 32
@@ -22,59 +22,33 @@ static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL };
static DEFINE_MUTEX(debugfs_mutex);
#define QDIO_DEBUGFS_NAME_LEN 40
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data)
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr)
{
- char dbf_text[20];
-
- sprintf(dbf_text, "qfmt:%x", init_data->q_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8);
- sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *));
- sprintf(dbf_text, "niq:%4x", init_data->no_input_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "noq:%4x", init_data->no_output_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *));
-}
-
-static void qdio_unregister_dbf_views(void)
-{
- if (qdio_dbf_setup)
- debug_unregister(qdio_dbf_setup);
- if (qdio_dbf_trace)
- debug_unregister(qdio_dbf_trace);
-}
-
-static int qdio_register_dbf_views(void)
-{
- qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES,
- QDIO_DBF_SETUP_NR_AREAS,
- QDIO_DBF_SETUP_LEN);
- if (!qdio_dbf_setup)
- goto oom;
- debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL);
-
- qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES,
- QDIO_DBF_TRACE_NR_AREAS,
- QDIO_DBF_TRACE_LEN);
- if (!qdio_dbf_trace)
- goto oom;
- debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL);
- return 0;
-oom:
- qdio_unregister_dbf_views();
- return -ENOMEM;
+ char text[20];
+
+ DBF_EVENT("qfmt:%1d", init_data->q_format);
+ DBF_HEX(init_data->adapter_name, 8);
+ DBF_EVENT("qpff%4x", init_data->qib_param_field_format);
+ DBF_HEX(&init_data->qib_param_field, sizeof(void *));
+ DBF_HEX(&init_data->input_slib_elements, sizeof(void *));
+ DBF_HEX(&init_data->output_slib_elements, sizeof(void *));
+ DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs,
+ init_data->no_output_qs);
+ DBF_HEX(&init_data->input_handler, sizeof(void *));
+ DBF_HEX(&init_data->output_handler, sizeof(void *));
+ DBF_HEX(&init_data->int_parm, sizeof(long));
+ DBF_HEX(&init_data->flags, sizeof(long));
+ DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
+ DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
+ DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
+
+ /* allocate trace view for the interface */
+ snprintf(text, 20, "qdio_%s", dev_name(&init_data->cdev->dev));
+ irq_ptr->debug_area = debug_register(text, 2, 1, 16);
+ debug_register_view(irq_ptr->debug_area, &debug_hex_ascii_view);
+ debug_set_level(irq_ptr->debug_area, DBF_WARN);
+ DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created");
}
static int qstat_show(struct seq_file *m, void *v)
@@ -86,16 +60,18 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q)
return 0;
- seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci);
+ seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci);
seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used));
seq_printf(m, "ftc: %d\n", q->first_to_check);
seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc);
seq_printf(m, "polling: %d\n", q->u.in.polling);
+ seq_printf(m, "ack count: %d\n", q->u.in.ack_count);
seq_printf(m, "slsb buffer states:\n");
+ seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n");
qdio_siga_sync_q(q);
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- get_buf_state(q, i, &state);
+ get_buf_state(q, i, &state, 0);
switch (state) {
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_OUTPUT_NOT_INIT:
@@ -127,6 +103,7 @@ static int qstat_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
}
seq_printf(m, "\n");
+ seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n");
return 0;
}
@@ -223,11 +200,24 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd
int __init qdio_debug_init(void)
{
debugfs_root = debugfs_create_dir("qdio_queues", NULL);
- return qdio_register_dbf_views();
+
+ qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
+ debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_setup, DBF_INFO);
+ DBF_EVENT("dbf created\n");
+
+ qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
+ debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_error, DBF_INFO);
+ DBF_ERROR("dbf created\n");
+ return 0;
}
void qdio_debug_exit(void)
{
debugfs_remove(debugfs_root);
- qdio_unregister_dbf_views();
+ if (qdio_dbf_setup)
+ debug_unregister(qdio_dbf_setup);
+ if (qdio_dbf_error)
+ debug_unregister(qdio_dbf_error);
}
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index 5a4d85b829ad..5d70bd162ae9 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -12,80 +12,72 @@
#include <asm/qdio.h>
#include "qdio.h"
-#define QDIO_DBF_HEX(ex, name, level, addr, len) \
+/* that gives us 15 characters in the text event views */
+#define QDIO_DBF_LEN 16
+
+extern debug_info_t *qdio_dbf_setup;
+extern debug_info_t *qdio_dbf_error;
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int qdio_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 */
+
+#undef DBF_EVENT
+#undef DBF_ERROR
+#undef DBF_DEV_EVENT
+
+#define DBF_EVENT(text...) \
do { \
- if (ex) \
- debug_exception(qdio_dbf_##name, level, (void *)(addr), len); \
- else \
- debug_event(qdio_dbf_##name, level, (void *)(addr), len); \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
} while (0)
-#define QDIO_DBF_TEXT(ex, name, level, text) \
+
+#define DBF_HEX(addr, len) \
do { \
- if (ex) \
- debug_text_exception(qdio_dbf_##name, level, text); \
- else \
- debug_text_event(qdio_dbf_##name, level, text); \
+ debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
} while (0)
-#define QDIO_DBF_HEX0(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 0, addr, len)
-#define QDIO_DBF_HEX1(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 1, addr, len)
-#define QDIO_DBF_HEX2(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 2, addr, len)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_HEX3(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 3, addr, len)
-#define QDIO_DBF_HEX4(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 4, addr, len)
-#define QDIO_DBF_HEX5(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 5, addr, len)
-#define QDIO_DBF_HEX6(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 6, addr, len)
-#else
-#define QDIO_DBF_HEX3(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX4(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX5(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX6(ex, name, addr, len) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_TEXT0(ex, name, text) QDIO_DBF_TEXT(ex, name, 0, text)
-#define QDIO_DBF_TEXT1(ex, name, text) QDIO_DBF_TEXT(ex, name, 1, text)
-#define QDIO_DBF_TEXT2(ex, name, text) QDIO_DBF_TEXT(ex, name, 2, text)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TEXT3(ex, name, text) QDIO_DBF_TEXT(ex, name, 3, text)
-#define QDIO_DBF_TEXT4(ex, name, text) QDIO_DBF_TEXT(ex, name, 4, text)
-#define QDIO_DBF_TEXT5(ex, name, text) QDIO_DBF_TEXT(ex, name, 5, text)
-#define QDIO_DBF_TEXT6(ex, name, text) QDIO_DBF_TEXT(ex, name, 6, text)
-#else
-#define QDIO_DBF_TEXT3(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT4(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT5(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT6(ex, name, text) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_ERROR(text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
+ } while (0)
-/* s390dbf views */
-#define QDIO_DBF_SETUP_LEN 8
-#define QDIO_DBF_SETUP_PAGES 8
-#define QDIO_DBF_SETUP_NR_AREAS 1
+#define DBF_ERROR_HEX(addr, len) \
+ do { \
+ debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
+ } while (0)
-#define QDIO_DBF_TRACE_LEN 8
-#define QDIO_DBF_TRACE_NR_AREAS 2
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TRACE_PAGES 32
-#define QDIO_DBF_SETUP_LEVEL 6
-#define QDIO_DBF_TRACE_LEVEL 4
-#else /* !CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_TRACE_PAGES 8
-#define QDIO_DBF_SETUP_LEVEL 2
-#define QDIO_DBF_TRACE_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_DEV_EVENT(level, device, text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ if (qdio_dbf_passes(device->debug_area, level)) { \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(device->debug_area, level, debug_buffer); \
+ } \
+ } while (0)
-extern debug_info_t *qdio_dbf_setup;
-extern debug_info_t *qdio_dbf_trace;
+#define DBF_DEV_HEX(level, device, addr, len) \
+ do { \
+ debug_event(device->debug_area, level, (void*)(addr), len); \
+ } while (0)
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data);
-void debug_print_bstat(struct qdio_q *q);
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr);
void qdio_setup_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
int qdio_debug_init(void);
void qdio_debug_exit(void);
+
#endif
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 7c8659151993..744f928a59ea 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -74,7 +74,7 @@ static inline int do_siga_input(struct subchannel_id schid, unsigned int mask)
* Note: For IQDC unicast queues only the highest priority queue is processed.
*/
static inline int do_siga_output(unsigned long schid, unsigned long mask,
- u32 *bb, unsigned int fc)
+ unsigned int *bb, unsigned int fc)
{
register unsigned long __fc asm("0") = fc;
register unsigned long __schid asm("1") = schid;
@@ -95,8 +95,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
{
- char dbf_text[15];
-
/* all done or next buffer state different */
if (ccq == 0 || ccq == 32)
return 0;
@@ -104,8 +102,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
if (ccq == 96 || ccq == 97)
return 1;
/* notify devices immediately */
- sprintf(dbf_text, "%d", ccq);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
return -EIO;
}
@@ -115,41 +112,45 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
* @state: state of the extracted buffers
* @start: buffer number to start at
* @count: count of buffers to examine
+ * @auto_ack: automatically acknowledge buffers
*
* Returns the number of successfull extracted equal buffer states.
* Stops processing if a state is different from the last buffers state.
*/
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
- int start, int count)
+ int start, int count, int auto_ack)
{
unsigned int ccq = 0;
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
again:
- ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
+ ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
+ auto_ack);
rc = qdio_check_ccq(q, ccq);
/* At least one buffer was processed, return and extract the remaining
* buffers later.
*/
- if ((ccq == 96) && (count != tmp_count))
+ if ((ccq == 96) && (count != tmp_count)) {
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete);
return (count - tmp_count);
+ }
+
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "eqAGAIN");
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT2(1, trace, "eqberr");
- sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -176,9 +177,12 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
+
+ if (!count)
+ return 0;
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
@@ -186,16 +190,13 @@ again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
rc = qdio_check_ccq(q, ccq);
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "sqAGAIN");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT3(1, trace, "sqberr");
- sprintf(dbf_text, "%2x,%2x", count, tmp_count);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- sprintf(dbf_text, "%d,%d", ccq, nr);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
-
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -207,7 +208,8 @@ again:
/* returns number of examined buffers and their common state in *state */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state, unsigned int count)
+ unsigned char *state, unsigned int count,
+ int auto_ack)
{
unsigned char __state = 0;
int i;
@@ -216,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
if (is_qebsm(q))
- return qdio_do_eqbs(q, state, bufnr, count);
+ return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
for (i = 0; i < count; i++) {
if (!__state)
@@ -230,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
}
inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state)
+ unsigned char *state, int auto_ack)
{
- return get_buf_states(q, bufnr, state, 1);
+ return get_buf_states(q, bufnr, state, 1, auto_ack);
}
/* wrap-around safe setting of slsb states, returns number of changed buffers */
@@ -282,14 +284,12 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
if (!need_siga_sync(q))
return 0;
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_sync);
cc = do_siga_sync(q->irq_ptr->schid, output, input);
- if (cc) {
- QDIO_DBF_TEXT4(0, trace, "sigasync");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
- }
+ if (cc)
+ DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc);
return cc;
}
@@ -311,50 +311,37 @@ static inline int qdio_siga_sync_all(struct qdio_q *q)
return qdio_siga_sync(q, ~0U, ~0U);
}
-static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
+static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit)
{
- unsigned int fc = 0;
unsigned long schid;
+ unsigned int fc = 0;
+ u64 start_time = 0;
+ int cc;
- if (q->u.out.use_enh_siga) {
+ if (q->u.out.use_enh_siga)
fc = 3;
- }
- if (!is_qebsm(q))
- schid = *((u32 *)&q->irq_ptr->schid);
- else {
+
+ if (is_qebsm(q)) {
schid = q->irq_ptr->sch_token;
fc |= 0x80;
}
- return do_siga_output(schid, q->mask, busy_bit, fc);
-}
-
-static int qdio_siga_output(struct qdio_q *q)
-{
- int cc;
- u32 busy_bit;
- u64 start_time = 0;
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "sigaout");
- QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
+ else
+ schid = *((u32 *)&q->irq_ptr->schid);
- qdio_perf_stat_inc(&perf_stats.siga_out);
again:
- cc = qdio_do_siga_output(q, &busy_bit);
- if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) {
- sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
+ cc = do_siga_output(schid, q->mask, busy_bit, fc);
- if (!start_time)
+ /* hipersocket busy condition */
+ if (*busy_bit) {
+ WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
+
+ if (!start_time) {
start_time = get_usecs();
- else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+ goto again;
+ }
+ if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}
-
- if (cc == 2 && busy_bit)
- cc |= QDIO_ERROR_SIGA_BUSY;
- if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
return cc;
}
@@ -362,14 +349,12 @@ static inline int qdio_siga_input(struct qdio_q *q)
{
int cc;
- QDIO_DBF_TEXT4(0, trace, "sigain");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_in);
cc = do_siga_input(q->irq_ptr->schid, q->mask);
if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc);
return cc;
}
@@ -387,35 +372,91 @@ void qdio_sync_after_thinint(struct qdio_q *q)
inline void qdio_stop_polling(struct qdio_q *q)
{
- spin_lock_bh(&q->u.in.lock);
- if (!q->u.in.polling) {
- spin_unlock_bh(&q->u.in.lock);
+ if (!q->u.in.polling)
return;
- }
+
q->u.in.polling = 0;
qdio_perf_stat_inc(&perf_stats.debug_stop_polling);
/* show the card that we are not polling anymore */
- set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
- spin_unlock_bh(&q->u.in.lock);
+ if (is_qebsm(q)) {
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = 0;
+ } else
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
}
-static void announce_buffer_error(struct qdio_q *q)
+static void announce_buffer_error(struct qdio_q *q, int count)
{
- char dbf_text[15];
+ q->qdio_error |= QDIO_ERROR_SLSB_STATE;
+
+ /* special handling for no target buffer empty */
+ if ((!q->is_input_q &&
+ (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) {
+ qdio_perf_stat_inc(&perf_stats.outbound_target_full);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d",
+ q->first_to_check);
+ return;
+ }
- if (q->is_input_q)
- QDIO_DBF_TEXT3(1, trace, "inperr");
- else
- QDIO_DBF_TEXT3(0, trace, "outperr");
+ DBF_ERROR("%4x BUF ERROR", SCH_NO(q));
+ DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr);
+ DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count);
+ DBF_ERROR("F14:%2x F15:%2x",
+ q->sbal[q->first_to_check]->element[14].flags & 0xff,
+ q->sbal[q->first_to_check]->element[15].flags & 0xff);
+}
+
+static inline void inbound_primed(struct qdio_q *q, int count)
+{
+ int new;
+
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count);
+
+ /* for QEBSM the ACK was already set by EQBS */
+ if (is_qebsm(q)) {
+ if (!q->u.in.polling) {
+ q->u.in.polling = 1;
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /* delete the previous ACK's */
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /*
+ * ACK the newest buffer. The ACK will be removed in qdio_stop_polling
+ * or by the next inbound run.
+ */
+ new = add_buf(q->first_to_check, count - 1);
+ if (q->u.in.polling) {
+ /* reset the previous ACK but first set the new one */
+ set_buf_state(q, new, SLSB_P_INPUT_ACK);
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
+ }
+ else {
+ q->u.in.polling = 1;
+ set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK);
+ }
- sprintf(dbf_text, "%x-%x-%x", q->first_to_check,
- q->sbal[q->first_to_check]->element[14].flags,
- q->sbal[q->first_to_check]->element[15].flags);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256);
+ q->last_move_ftc = new;
+ count--;
+ if (!count)
+ return;
- q->qdio_error = QDIO_ERROR_SLSB_STATE;
+ /*
+ * Need to change all PRIMED buffers to NOT_INIT, otherwise
+ * we're loosing initiative in the thinint code.
+ */
+ set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT,
+ count);
}
static int get_inbound_buffer_frontier(struct qdio_q *q)
@@ -424,13 +465,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
unsigned char state;
/*
- * If we still poll don't update last_move_ftc, keep the
- * previously ACK buffer there.
- */
- if (!q->u.in.polling)
- q->last_move_ftc = q->first_to_check;
-
- /*
* Don't check 128 buffers, as otherwise qdio_inbound_q_moved
* would return 0.
*/
@@ -450,34 +484,13 @@ check_next:
if (q->first_to_check == stop)
goto out;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 1);
if (!count)
goto out;
switch (state) {
case SLSB_P_INPUT_PRIMED:
- QDIO_DBF_TEXT5(0, trace, "inptprim");
-
- /*
- * Only ACK the first buffer. The ACK will be removed in
- * qdio_stop_polling.
- */
- if (q->u.in.polling)
- state = SLSB_P_INPUT_NOT_INIT;
- else {
- q->u.in.polling = 1;
- state = SLSB_P_INPUT_ACK;
- }
- set_buf_state(q, q->first_to_check, state);
-
- /*
- * Need to change all PRIMED buffers to NOT_INIT, otherwise
- * we're loosing initiative in the thinint code.
- */
- if (count > 1)
- set_buf_states(q, next_buf(q->first_to_check),
- SLSB_P_INPUT_NOT_INIT, count - 1);
-
+ inbound_primed(q, count);
/*
* No siga-sync needed for non-qebsm here, as the inbound queue
* will be synced on the next siga-r, resp.
@@ -487,7 +500,7 @@ check_next:
atomic_sub(count, &q->nr_buf_used);
goto check_next;
case SLSB_P_INPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
@@ -495,13 +508,12 @@ check_next:
case SLSB_CU_INPUT_EMPTY:
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_INPUT_ACK:
- QDIO_DBF_TEXT5(0, trace, "inpnipro");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
break;
default:
BUG();
}
out:
- QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int));
return q->first_to_check;
}
@@ -515,8 +527,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
if (!need_siga_sync(q) && !pci_out_supported(q))
q->u.in.timestamp = get_usecs();
- QDIO_DBF_TEXT4(0, trace, "inhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved");
return 1;
} else
return 0;
@@ -524,10 +535,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
static int qdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
@@ -538,7 +546,7 @@ static int qdio_inbound_q_done(struct qdio_q *q)
*/
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* we got something to do */
return 0;
@@ -552,20 +560,12 @@ static int qdio_inbound_q_done(struct qdio_q *q)
* has (probably) not moved (see qdio_inbound_processing).
*/
if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisdon");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d",
+ q->first_to_check);
return 1;
} else {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisntd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d",
+ q->first_to_check);
return 0;
}
}
@@ -573,9 +573,6 @@ static int qdio_inbound_q_done(struct qdio_q *q)
void qdio_kick_inbound_handler(struct qdio_q *q)
{
int count, start, end;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
qdio_perf_stat_inc(&perf_stats.inbound_handler);
@@ -586,10 +583,7 @@ void qdio_kick_inbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -655,14 +649,14 @@ check_next:
if (q->first_to_check == stop)
return q->first_to_check;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 0);
if (!count)
return q->first_to_check;
switch (state) {
case SLSB_P_OUTPUT_EMPTY:
/* the adapter got it */
- QDIO_DBF_TEXT5(0, trace, "outpempt");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count);
atomic_sub(count, &q->nr_buf_used);
q->first_to_check = add_buf(q->first_to_check, count);
@@ -674,14 +668,14 @@ check_next:
break;
goto check_next;
case SLSB_P_OUTPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
break;
case SLSB_CU_OUTPUT_PRIMED:
/* the adapter has not fetched the output yet */
- QDIO_DBF_TEXT5(0, trace, "outpprim");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr);
break;
case SLSB_P_OUTPUT_NOT_INIT:
case SLSB_P_OUTPUT_HALTED:
@@ -706,99 +700,48 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
if ((bufnr != q->last_move_ftc) || q->qdio_error) {
q->last_move_ftc = bufnr;
- QDIO_DBF_TEXT4(0, trace, "oqhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
return 1;
} else
return 0;
}
-/*
- * VM could present us cc=2 and busy bit set on SIGA-write
- * during reconfiguration of their Guest LAN (only in iqdio mode,
- * otherwise qdio is asynchronous and cc=2 and busy bit there will take
- * the queues down immediately).
- *
- * Therefore qdio_siga_output will try for a short time constantly,
- * if such a condition occurs. If it doesn't change, it will
- * increase the busy_siga_counter and save the timestamp, and
- * schedule the queue for later processing. qdio_outbound_processing
- * will check out the counter. If non-zero, it will call qdio_kick_outbound_q
- * as often as the value of the counter. This will attempt further SIGA
- * instructions. For each successful SIGA, the counter is
- * decreased, for failing SIGAs the counter remains the same, after
- * all. After some time of no movement, qdio_kick_outbound_q will
- * finally fail and reflect corresponding error codes to call
- * the upper layer module and have it take the queues down.
- *
- * Note that this is a change from the original HiperSockets design
- * (saying cc=2 and busy bit means take the queues down), but in
- * these days Guest LAN didn't exist... excessive cc=2 with busy bit
- * conditions will still take the queues down, but the threshold is
- * higher due to the Guest LAN environment.
- *
- * Called from outbound tasklet and do_QDIO handler.
- */
static void qdio_kick_outbound_q(struct qdio_q *q)
{
- int rc;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "kickoutq");
- QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
-#endif /* CONFIG_QDIO_DEBUG */
+ unsigned int busy_bit;
+ int cc;
if (!need_siga_out(q))
return;
- rc = qdio_siga_output(q);
- switch (rc) {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
+ qdio_perf_stat_inc(&perf_stats.siga_out);
+
+ cc = qdio_siga_output(q, &busy_bit);
+ switch (cc) {
case 0:
- /* TODO: improve error handling for CC=0 case */
-#ifdef CONFIG_QDIO_DEBUG
- if (q->u.out.timestamp) {
- QDIO_DBF_TEXT3(0, trace, "cc2reslv");
- sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no,
- q->nr,
- atomic_read(&q->u.out.busy_siga_counter));
- QDIO_DBF_TEXT3(0, trace, dbf_text);
- }
-#endif /* CONFIG_QDIO_DEBUG */
- /* went smooth this time, reset timestamp */
- q->u.out.timestamp = 0;
break;
- /* cc=2 and busy bit */
- case (2 | QDIO_ERROR_SIGA_BUSY):
- atomic_inc(&q->u.out.busy_siga_counter);
-
- /* if the last siga was successful, save timestamp here */
- if (!q->u.out.timestamp)
- q->u.out.timestamp = get_usecs();
-
- /* if we're in time, don't touch qdio_error */
- if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) {
- tasklet_schedule(&q->tasklet);
- break;
+ case 2:
+ if (busy_bit) {
+ DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
+ q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
+ } else {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
+ q->nr);
+ q->qdio_error = cc;
}
- QDIO_DBF_TEXT2(0, trace, "cc2REPRT");
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr,
- atomic_read(&q->u.out.busy_siga_counter));
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- default:
- /* for plain cc=1, 2 or 3 */
- q->qdio_error = rc;
+ break;
+ case 1:
+ case 3:
+ DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
+ q->qdio_error = cc;
+ break;
}
}
static void qdio_kick_outbound_handler(struct qdio_q *q)
{
int start, end, count;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
start = q->first_to_kick;
end = q->last_move_ftc;
@@ -807,13 +750,8 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "kickouth");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -828,22 +766,18 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
static void __qdio_outbound_processing(struct qdio_q *q)
{
- int siga_attempts;
+ unsigned long flags;
qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
-
- /* see comment in qdio_kick_outbound_q */
- siga_attempts = atomic_read(&q->u.out.busy_siga_counter);
- while (siga_attempts--) {
- atomic_dec(&q->u.out.busy_siga_counter);
- qdio_kick_outbound_q(q);
- }
+ spin_lock_irqsave(&q->lock, flags);
BUG_ON(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q))
qdio_kick_outbound_handler(q);
+ spin_unlock_irqrestore(&q->lock, flags);
+
if (queue_type(q) == QDIO_ZFCP_QFMT) {
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
tasklet_schedule(&q->tasklet);
@@ -908,27 +842,18 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q)
static inline void qdio_set_state(struct qdio_irq *irq_ptr,
enum qdio_irq_states state)
{
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "newstate");
- sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state);
- QDIO_DBF_TEXT5(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "newstate: %1d", state);
irq_ptr->state = state;
mb();
}
-static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
+static void qdio_irq_check_sense(struct qdio_irq *irq_ptr, struct irb *irb)
{
- char dbf_text[15];
-
if (irb->esw.esw0.erw.cons) {
- sprintf(dbf_text, "sens%4x", schid.sch_no);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
- QDIO_DBF_HEX0(0, trace, irb, 64);
- QDIO_DBF_HEX0(0, trace, irb->ecw, 64);
+ DBF_ERROR("%4x sense:", irq_ptr->schid.sch_no);
+ DBF_ERROR_HEX(irb, 64);
+ DBF_ERROR_HEX(irb->ecw, 64);
}
}
@@ -962,14 +887,10 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
struct qdio_q *q;
- char dbf_text[15];
- QDIO_DBF_TEXT2(1, trace, "ick2");
- sprintf(dbf_text, "%s", dev_name(&cdev->dev));
- QDIO_DBF_TEXT2(1, trace, dbf_text);
- QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
+ DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no);
+ DBF_ERROR("intp :%lx", intparm);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
if (irq_ptr->nr_input_qs) {
q = irq_ptr->input_qs[0];
@@ -1022,28 +943,29 @@ static void qdio_int_error(struct ccw_device *cdev)
}
static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
- int dstat)
+ int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
- QDIO_DBF_TEXT2(1, setup, "eq:ckcon");
+ DBF_ERROR("EQ:ck con");
goto error;
}
if (!(dstat & DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:no de");
+ DBF_ERROR("EQ:no dev");
goto error;
}
if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:badio");
+ DBF_ERROR("EQ: bad io");
goto error;
}
return 0;
error:
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
+ DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
+
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
@@ -1052,12 +974,8 @@ static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- char dbf_text[15];
-
- sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
if (!qdio_establish_check_errors(cdev, cstat, dstat))
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
}
@@ -1068,25 +986,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int cstat, dstat;
- char dbf_text[15];
qdio_perf_stat_inc(&perf_stats.qdio_int);
if (!intparm || !irq_ptr) {
- sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
return;
}
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
- sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
return;
case -ETIMEDOUT:
- sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no);
qdio_int_error(cdev);
return;
default:
@@ -1094,7 +1008,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
}
- qdio_irq_check_sense(irq_ptr->schid, irb);
+ qdio_irq_check_sense(irq_ptr, irb);
cstat = irb->scsw.cmd.cstat;
dstat = irb->scsw.cmd.dstat;
@@ -1129,23 +1043,20 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
/**
* qdio_get_ssqd_desc - get qdio subchannel description
* @cdev: ccw device to get description for
+ * @data: where to store the ssqd
*
- * Returns a pointer to the saved qdio subchannel description,
- * or NULL for not setup qdio devices.
+ * Returns 0 or an error code. The results of the chsc are stored in the
+ * specified structure.
*/
-struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev)
+int qdio_get_ssqd_desc(struct ccw_device *cdev,
+ struct qdio_ssqd_desc *data)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return NULL;
+ if (!cdev || !cdev->private)
+ return -EINVAL;
- return &irq_ptr->ssqd_desc;
+ DBF_EVENT("get ssqd:%4x", cdev->private->schid.sch_no);
+ return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data);
}
EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
@@ -1159,14 +1070,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
*/
int qdio_cleanup(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int rc;
- sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
@@ -1199,18 +1105,15 @@ static void qdio_shutdown_queues(struct ccw_device *cdev)
*/
int qdio_shutdown(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int rc;
unsigned long flags;
- char dbf_text[15];
- sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
+ DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no);
+
mutex_lock(&irq_ptr->setup_mutex);
/*
* Subchannel was already shot down. We cannot prevent being called
@@ -1234,10 +1137,8 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
/* default behaviour is halt */
rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
if (rc) {
- sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "rc=%d", rc);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_ERROR("%4x SHUTD ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4d", rc);
goto no_cleanup;
}
@@ -1271,17 +1172,18 @@ EXPORT_SYMBOL_GPL(qdio_shutdown);
*/
int qdio_free(struct ccw_device *cdev)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
+ DBF_EVENT("qfree:%4x", cdev->private->schid.sch_no);
mutex_lock(&irq_ptr->setup_mutex);
+
+ if (irq_ptr->debug_area != NULL) {
+ debug_unregister(irq_ptr->debug_area);
+ irq_ptr->debug_area = NULL;
+ }
cdev->private->qdio_data = NULL;
mutex_unlock(&irq_ptr->setup_mutex);
@@ -1300,10 +1202,6 @@ EXPORT_SYMBOL_GPL(qdio_free);
int qdio_initialize(struct qdio_initialize *init_data)
{
int rc;
- char dbf_text[15];
-
- sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
rc = qdio_allocate(init_data);
if (rc)
@@ -1323,10 +1221,8 @@ EXPORT_SYMBOL_GPL(qdio_initialize);
int qdio_allocate(struct qdio_initialize *init_data)
{
struct qdio_irq *irq_ptr;
- char dbf_text[15];
- sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qallocate:%4x", init_data->cdev->private->schid.sch_no);
if ((init_data->no_input_qs && !init_data->input_handler) ||
(init_data->no_output_qs && !init_data->output_handler))
@@ -1340,16 +1236,13 @@ int qdio_allocate(struct qdio_initialize *init_data)
(!init_data->output_sbal_addr_array))
return -EINVAL;
- qdio_allocate_do_dbf(init_data);
-
/* irq_ptr must be in GFP_DMA since it contains ccw1.cda */
irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!irq_ptr)
goto out_err;
- QDIO_DBF_TEXT0(0, setup, "irq_ptr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *));
mutex_init(&irq_ptr->setup_mutex);
+ qdio_allocate_dbf(init_data, irq_ptr);
/*
* Allocate a page for the chsc calls in qdio_establish.
@@ -1367,9 +1260,6 @@ int qdio_allocate(struct qdio_initialize *init_data)
goto out_rel;
WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
- QDIO_DBF_TEXT0(0, setup, "qdr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *));
-
if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs,
init_data->no_output_qs))
goto out_rel;
@@ -1390,14 +1280,12 @@ EXPORT_SYMBOL_GPL(qdio_allocate);
*/
int qdio_establish(struct qdio_initialize *init_data)
{
- char dbf_text[20];
struct qdio_irq *irq_ptr;
struct ccw_device *cdev = init_data->cdev;
unsigned long saveflags;
int rc;
- sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qestablish:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1427,10 +1315,8 @@ int qdio_establish(struct qdio_initialize *init_data)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0);
if (rc) {
- sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "eq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x est IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1451,10 +1337,8 @@ int qdio_establish(struct qdio_initialize *init_data)
}
qdio_setup_ssqd_info(irq_ptr);
- sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc);
+ DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac);
/* qebsm is now setup if available, initialize buffer states */
qdio_init_buf_states(irq_ptr);
@@ -1475,10 +1359,8 @@ int qdio_activate(struct ccw_device *cdev)
struct qdio_irq *irq_ptr;
int rc;
unsigned long saveflags;
- char dbf_text[20];
- sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qactivate:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1504,10 +1386,8 @@ int qdio_activate(struct ccw_device *cdev)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE,
0, DOIO_DENY_PREFETCH);
if (rc) {
- sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "aq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1565,23 +1445,38 @@ static inline int buf_in_between(int bufnr, int start, int count)
static void handle_inbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count)
{
- unsigned long flags;
- int used, rc;
+ int used, cc, diff;
- /*
- * do_QDIO could run in parallel with the queue tasklet so the
- * upper-layer programm could empty the ACK'ed buffer here.
- * If that happens we must clear the polling flag, otherwise
- * qdio_stop_polling() could set the buffer to NOT_INIT after
- * it was set to EMPTY which would kill us.
- */
- spin_lock_irqsave(&q->u.in.lock, flags);
- if (q->u.in.polling)
- if (buf_in_between(q->last_move_ftc, bufnr, count))
+ if (!q->u.in.polling)
+ goto set;
+
+ /* protect against stop polling setting an ACK for an emptied slsb */
+ if (count == QDIO_MAX_BUFFERS_PER_Q) {
+ /* overwriting everything, just delete polling status */
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ goto set;
+ } else if (buf_in_between(q->last_move_ftc, bufnr, count)) {
+ if (is_qebsm(q)) {
+ /* partial overwrite, just update last_move_ftc */
+ diff = add_buf(bufnr, count);
+ diff = sub_buf(diff, q->last_move_ftc);
+ q->u.in.ack_count -= diff;
+ if (q->u.in.ack_count <= 0) {
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ /* TODO: must we set last_move_ftc to something meaningful? */
+ goto set;
+ }
+ q->last_move_ftc = add_buf(q->last_move_ftc, diff);
+ }
+ else
+ /* the only ACK will be deleted, so stop polling */
q->u.in.polling = 0;
+ }
+set:
count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);
- spin_unlock_irqrestore(&q->u.in.lock, flags);
used = atomic_add_return(count, &q->nr_buf_used) - count;
BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q);
@@ -1591,9 +1486,9 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
return;
if (need_siga_in(q)) {
- rc = qdio_siga_input(q);
- if (rc)
- q->qdio_error = rc;
+ cc = qdio_siga_input(q);
+ if (cc)
+ q->qdio_error = cc;
}
}
@@ -1640,6 +1535,10 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
while (count--)
qdio_kick_outbound_q(q);
}
+
+ /* report CC=2 conditions synchronously */
+ if (q->qdio_error)
+ __qdio_outbound_processing(q);
goto out;
}
@@ -1649,11 +1548,11 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
}
/* try to fast requeue buffers */
- get_buf_state(q, prev_buf(bufnr), &state);
+ get_buf_state(q, prev_buf(bufnr), &state, 0);
if (state != SLSB_CU_OUTPUT_PRIMED)
qdio_kick_outbound_q(q);
else {
- QDIO_DBF_TEXT5(0, trace, "fast-req");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
qdio_perf_stat_inc(&perf_stats.fast_requeue);
}
out:
@@ -1673,12 +1572,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
int q_nr, int bufnr, int count)
{
struct qdio_irq *irq_ptr;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[20];
-
- sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
(count > QDIO_MAX_BUFFERS_PER_Q) ||
@@ -1692,33 +1585,24 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
if (!irq_ptr)
return -ENODEV;
-#ifdef CONFIG_QDIO_DEBUG
if (callflags & QDIO_FLAG_SYNC_INPUT)
- QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr],
- sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input");
else
- QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr],
- sizeof(void *));
-
- sprintf(dbf_text, "flag%04x", callflags);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "qi%02xct%02x", bufnr, count);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output");
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count);
if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
return -EBUSY;
if (callflags & QDIO_FLAG_SYNC_INPUT)
- handle_inbound(irq_ptr->input_qs[q_nr],
- callflags, bufnr, count);
+ handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
+ count);
else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
- handle_outbound(irq_ptr->output_qs[q_nr],
- callflags, bufnr, count);
- else {
- QDIO_DBF_TEXT3(1, trace, "doQD:inv");
+ handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr,
+ count);
+ else
return -EINVAL;
- }
return 0;
}
EXPORT_SYMBOL_GPL(do_QDIO);
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
index ec5c4a414235..136d0f0b1e93 100644
--- a/drivers/s390/cio/qdio_perf.c
+++ b/drivers/s390/cio/qdio_perf.c
@@ -74,12 +74,20 @@ static int qdio_perf_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n",
(long)atomic_long_read(&perf_stats.fast_requeue));
+ seq_printf(m, "Number of outbound target full condition\t: %li\n",
+ (long)atomic_long_read(&perf_stats.outbound_target_full));
seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_tl_out_timer));
seq_printf(m, "Number of stop polling calls\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_stop_polling));
seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n",
(long)atomic_long_read(&perf_stats.thinint_inbound_loop2));
+ seq_printf(m, "QEBSM EQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_eqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_eqbs_incomplete));
+ seq_printf(m, "QEBSM SQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_sqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_sqbs_incomplete));
seq_printf(m, "\n");
return 0;
}
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h
index 5c406a8b7387..7821ac4fa517 100644
--- a/drivers/s390/cio/qdio_perf.h
+++ b/drivers/s390/cio/qdio_perf.h
@@ -36,10 +36,15 @@ struct qdio_perf_stats {
atomic_long_t inbound_handler;
atomic_long_t outbound_handler;
atomic_long_t fast_requeue;
+ atomic_long_t outbound_target_full;
/* for debugging */
atomic_long_t debug_tl_out_timer;
atomic_long_t debug_stop_polling;
+ atomic_long_t debug_eqbs_all;
+ atomic_long_t debug_eqbs_incomplete;
+ atomic_long_t debug_sqbs_all;
+ atomic_long_t debug_sqbs_incomplete;
};
extern struct qdio_perf_stats perf_stats;
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index a0b6b46e7466..c08356b95bf5 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -117,17 +117,16 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i);
q->nr = i;
q->handler = handler;
+ spin_lock_init(&q->lock);
}
static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
- void **sbals_array, char *dbf_text, int i)
+ void **sbals_array, int i)
{
struct qdio_q *prev;
int j;
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &q, sizeof(void *));
-
+ DBF_HEX(&q, sizeof(void *));
q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
/* fill in sbal */
@@ -150,31 +149,26 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
q->sl->element[j].sbal = (unsigned long)q->sbal[j];
- QDIO_DBF_TEXT2(0, setup, "sl-sb-b0");
- QDIO_DBF_HEX2(0, setup, q->sl, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, &q->slsb, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, q->sbal, sizeof(void *));
+ DBF_EVENT("sl-slsb-sbal");
+ DBF_HEX(q->sl, sizeof(void *));
+ DBF_HEX(&q->slsb, sizeof(void *));
+ DBF_HEX(q->sbal, sizeof(void *));
}
static void setup_queues(struct qdio_irq *irq_ptr,
struct qdio_initialize *qdio_init)
{
- char dbf_text[20];
struct qdio_q *q;
void **input_sbal_array = qdio_init->input_sbal_addr_array;
void **output_sbal_array = qdio_init->output_sbal_addr_array;
int i;
- sprintf(dbf_text, "qset%4x", qdio_init->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
for_each_input_queue(irq_ptr, q, i) {
- sprintf(dbf_text, "in-q%4x", i);
+ DBF_EVENT("in-q:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i);
q->is_input_q = 1;
- spin_lock_init(&q->u.in.lock);
- setup_storage_lists(q, irq_ptr, input_sbal_array, dbf_text, i);
+ setup_storage_lists(q, irq_ptr, input_sbal_array, i);
input_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
if (is_thinint_irq(irq_ptr))
@@ -186,12 +180,11 @@ static void setup_queues(struct qdio_irq *irq_ptr,
}
for_each_output_queue(irq_ptr, q, i) {
- sprintf(dbf_text, "outq%4x", i);
+ DBF_EVENT("outq:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i);
q->is_input_q = 0;
- setup_storage_lists(q, irq_ptr, output_sbal_array,
- dbf_text, i);
+ setup_storage_lists(q, irq_ptr, output_sbal_array, i);
output_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
tasklet_init(&q->tasklet, qdio_outbound_processing,
@@ -222,8 +215,6 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac)
static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
unsigned char qdioac, unsigned long token)
{
- char dbf_text[15];
-
if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM))
goto no_qebsm;
if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) ||
@@ -232,33 +223,41 @@ static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
irq_ptr->sch_token = token;
- QDIO_DBF_TEXT0(0, setup, "V=V:1");
- sprintf(dbf_text, "%8lx", irq_ptr->sch_token);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("V=V:1");
+ DBF_EVENT("%8lx", irq_ptr->sch_token);
return;
no_qebsm:
irq_ptr->sch_token = 0;
irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
- QDIO_DBF_TEXT0(0, setup, "noV=V");
+ DBF_EVENT("noV=V");
}
-static int __get_ssqd_info(struct qdio_irq *irq_ptr)
+/*
+ * If there is a qdio_irq we use the chsc_page and store the information
+ * in the qdio_irq, otherwise we copy it to the specified structure.
+ */
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data)
{
struct chsc_ssqd_area *ssqd;
int rc;
- QDIO_DBF_TEXT0(0, setup, "getssqd");
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ DBF_EVENT("getssqd:%4x", schid->sch_no);
+ if (irq_ptr != NULL)
+ ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ else
+ ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
memset(ssqd, 0, PAGE_SIZE);
ssqd->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0024,
};
- ssqd->first_sch = irq_ptr->schid.sch_no;
- ssqd->last_sch = irq_ptr->schid.sch_no;
- ssqd->ssid = irq_ptr->schid.ssid;
+ ssqd->first_sch = schid->sch_no;
+ ssqd->last_sch = schid->sch_no;
+ ssqd->ssid = schid->ssid;
if (chsc(ssqd))
return -EIO;
@@ -268,27 +267,29 @@ static int __get_ssqd_info(struct qdio_irq *irq_ptr)
if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
- (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no))
+ (ssqd->qdio_ssqd.sch != schid->sch_no))
return -EINVAL;
- memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
- sizeof(struct qdio_ssqd_desc));
+ if (irq_ptr != NULL)
+ memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ else {
+ memcpy(data, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ free_page((unsigned long)ssqd);
+ }
return 0;
}
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
{
unsigned char qdioac;
- char dbf_text[15];
int rc;
- rc = __get_ssqd_info(irq_ptr);
+ rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL);
if (rc) {
- QDIO_DBF_TEXT2(0, setup, "ssqdasig");
- sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- sprintf(dbf_text, "rc:%d", rc);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%x", rc);
/* all flags set, worst case */
qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED |
AC1_SIGA_SYNC_NEEDED;
@@ -297,9 +298,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
process_ac_flags(irq_ptr, qdioac);
-
- sprintf(dbf_text, "qdioac%2x", qdioac);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_EVENT("qdioac:%4x", qdioac);
}
void qdio_release_memory(struct qdio_irq *irq_ptr)
@@ -419,7 +418,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
/* get qdio commands */
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no eq");
+ DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -427,7 +426,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no aq");
+ DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -447,56 +446,38 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
{
char s[80];
- sprintf(s, "qdio: %s ", dev_name(&cdev->dev));
- switch (irq_ptr->qib.qfmt) {
- case QDIO_QETH_QFMT:
- sprintf(s + strlen(s), "OSA ");
- break;
- case QDIO_ZFCP_QFMT:
- sprintf(s + strlen(s), "ZFCP ");
- break;
- case QDIO_IQDIO_QFMT:
- sprintf(s + strlen(s), "HS ");
- break;
- }
- sprintf(s + strlen(s), "on SC %x using ", irq_ptr->schid.sch_no);
- sprintf(s + strlen(s), "AI:%d ", is_thinint_irq(irq_ptr));
- sprintf(s + strlen(s), "QEBSM:%d ", (irq_ptr->sch_token) ? 1 : 0);
- sprintf(s + strlen(s), "PCI:%d ",
- (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0);
- sprintf(s + strlen(s), "TDD:%d ", css_general_characteristics.aif_tdd);
- sprintf(s + strlen(s), "SIGA:");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.input) ? "R" : " ");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.output) ? "W" : " ");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.sync) ? "S" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
- sprintf(s + strlen(s), "\n");
+ snprintf(s, 80, "qdio: %s %s on SC %x using "
+ "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n",
+ dev_name(&cdev->dev),
+ (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" :
+ ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"),
+ irq_ptr->schid.sch_no,
+ is_thinint_irq(irq_ptr),
+ (irq_ptr->sch_token) ? 1 : 0,
+ (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0,
+ css_general_characteristics.aif_tdd,
+ (irq_ptr->siga_flag.input) ? "R" : " ",
+ (irq_ptr->siga_flag.output) ? "W" : " ",
+ (irq_ptr->siga_flag.sync) ? "S" : " ",
+ (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
printk(KERN_INFO "%s", s);
}
int __init qdio_setup_init(void)
{
- char dbf_text[15];
-
qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
256, 0, NULL);
if (!qdio_q_cache)
return -ENOMEM;
/* Check for OSA/FCP thin interrupts (bit 67). */
- sprintf(dbf_text, "thini%1x",
- (css_general_characteristics.aif_osa) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("thinint:%1d",
+ (css_general_characteristics.aif_osa) ? 1 : 0);
/* Check for QEBSM support in general (bit 58). */
- sprintf(dbf_text, "cssQBS:%1x",
- (qebsm_possible()) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("cssQEBSM:%1d", (qebsm_possible()) ? 1 : 0);
return 0;
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index ea7f61400267..8e90e147b746 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -125,13 +125,13 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
static inline int tiqdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* more work coming */
@@ -258,8 +258,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
struct scssc_area *scssc_area;
- char dbf_text[15];
- void *ptr;
int rc;
scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
@@ -294,19 +292,15 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
rc = chsc_error_from_response(scssc_area->response.code);
if (rc) {
- sprintf(dbf_text, "sidR%4x", scssc_area->response.code);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT1(0, setup, dbf_text);
- ptr = &scssc_area->response;
- QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN);
+ DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
+ scssc_area->response.code);
+ DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
return rc;
}
- QDIO_DBF_TEXT2(0, setup, "setscind");
- QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr,
- sizeof(unsigned long));
- QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr,
- sizeof(unsigned long));
+ DBF_EVENT("setscind");
+ DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
+ DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long));
return 0;
}
@@ -327,14 +321,11 @@ void tiqdio_free_memory(void)
int __init tiqdio_register_thinints(void)
{
- char dbf_text[20];
-
isc_register(QDIO_AIRQ_ISC);
tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
NULL, QDIO_AIRQ_ISC);
if (IS_ERR(tiqdio_alsi)) {
- sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi));
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
tiqdio_alsi = NULL;
isc_unregister(QDIO_AIRQ_ISC);
return -ENOMEM;
@@ -360,7 +351,7 @@ void qdio_setup_thinint(struct qdio_irq *irq_ptr)
if (!is_thinint_irq(irq_ptr))
return;
irq_ptr->dsci = get_indicator();
- QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *));
+ DBF_HEX(&irq_ptr->dsci, sizeof(void *));
}
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index e3fe6838293a..1f5f5d2d87d9 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus.
*
@@ -23,6 +24,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "ap"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -34,6 +38,10 @@
#include <linux/mutex.h>
#include <asm/s390_rdev.h>
#include <asm/reset.h>
+#include <asm/airq.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/isc.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
@@ -46,6 +54,7 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
static int ap_poll_thread_start(void);
static void ap_poll_thread_stop(void);
static void ap_request_timeout(unsigned long);
+static inline void ap_schedule_poll_timer(void);
/*
* Module description.
@@ -68,7 +77,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 DEFINE_SPINLOCK(ap_device_lock);
+static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
/*
@@ -80,19 +89,29 @@ static int ap_config_time = AP_CONFIG_TIME;
static DECLARE_WORK(ap_config_work, ap_scan_bus);
/*
- * Tasklet & timer for AP request polling.
+ * Tasklet & timer for AP request polling and interrupts
*/
static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
static DEFINE_MUTEX(ap_poll_thread_mutex);
+static void *ap_interrupt_indicator;
static struct hrtimer ap_poll_timer;
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
* If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
static unsigned long long poll_timeout = 250000;
/**
+ * ap_using_interrupts() - Returns non-zero if interrupt support is
+ * available.
+ */
+static inline int ap_using_interrupts(void)
+{
+ return ap_interrupt_indicator != NULL;
+}
+
+/**
* ap_intructions_available() - Test if AP instructions are available.
*
* Returns 0 if the AP instructions are installed.
@@ -113,6 +132,23 @@ static inline int ap_instructions_available(void)
}
/**
+ * ap_interrupts_available(): Test if AP interrupts are available.
+ *
+ * Returns 1 if AP interrupts are available.
+ */
+static int ap_interrupts_available(void)
+{
+ unsigned long long facility_bits[2];
+
+ if (stfle(facility_bits, 2) <= 1)
+ return 0;
+ if (!(facility_bits[0] & (1ULL << 61)) ||
+ !(facility_bits[1] & (1ULL << 62)))
+ return 0;
+ return 1;
+}
+
+/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
@@ -152,6 +188,80 @@ static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid)
return reg1;
}
+#ifdef CONFIG_64BIT
+/**
+ * ap_queue_interruption_control(): Enable interruption for a specific AP.
+ * @qid: The AP queue number
+ * @ind: The notification indicator byte
+ *
+ * Returns AP queue status.
+ */
+static inline struct ap_queue_status
+ap_queue_interruption_control(ap_qid_t qid, void *ind)
+{
+ register unsigned long reg0 asm ("0") = qid | 0x03000000UL;
+ register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC;
+ register struct ap_queue_status reg1_out asm ("1");
+ register void *reg2 asm ("2") = ind;
+ asm volatile(
+ ".long 0xb2af0000" /* PQAP(RAPQ) */
+ : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)
+ :
+ : "cc" );
+ return reg1_out;
+}
+#endif
+
+/**
+ * ap_queue_enable_interruption(): Enable interruption on an AP.
+ * @qid: The AP queue number
+ * @ind: the notification indicator byte
+ *
+ * Enables interruption on AP queue via ap_queue_interruption_control(). Based
+ * on the return value it waits a while and tests the AP queue if interrupts
+ * have been switched on using ap_test_queue().
+ */
+static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
+{
+#ifdef CONFIG_64BIT
+ struct ap_queue_status status;
+ int t_depth, t_device_type, rc, i;
+
+ rc = -EBUSY;
+ status = ap_queue_interruption_control(qid, ind);
+
+ for (i = 0; i < AP_MAX_RESET; i++) {
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (status.int_enabled)
+ return 0;
+ break;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_BUSY:
+ break;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ case AP_RESPONSE_INVALID_ADDRESS:
+ return -ENODEV;
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ if (status.int_enabled)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ if (i < AP_MAX_RESET - 1) {
+ udelay(5);
+ status = ap_test_queue(qid, &t_depth, &t_device_type);
+ }
+ }
+ return rc;
+#else
+ return -EINVAL;
+#endif
+}
+
/**
* __ap_send(): Send message to adjunct processor queue.
* @qid: The AP queue number
@@ -295,6 +405,11 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
case AP_RESPONSE_CHECKSTOPPED:
rc = -ENODEV;
break;
+ case AP_RESPONSE_INVALID_ADDRESS:
+ rc = -ENODEV;
+ break;
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ break;
case AP_RESPONSE_BUSY:
break;
default:
@@ -345,6 +460,15 @@ static int ap_init_queue(ap_qid_t qid)
status = ap_test_queue(qid, &dummy, &dummy);
}
}
+ if (rc == 0 && ap_using_interrupts()) {
+ rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator);
+ /* If interruption mode is supported by the machine,
+ * but an AP can not be enabled for interruption then
+ * the AP will be discarded. */
+ if (rc)
+ pr_err("Registering adapter interrupts for "
+ "AP %d failed\n", AP_QID_DEVICE(qid));
+ }
return rc;
}
@@ -397,16 +521,16 @@ static ssize_t ap_hwtype_show(struct device *dev,
struct ap_device *ap_dev = to_ap_dev(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type);
}
-static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
+static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ap_device *ap_dev = to_ap_dev(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth);
}
-static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
+static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
static ssize_t ap_request_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -509,9 +633,9 @@ static int ap_device_probe(struct device *dev)
ap_dev->drv = ap_drv;
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
if (!rc) {
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_add(&ap_dev->list, &ap_device_list);
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
}
return rc;
}
@@ -553,9 +677,9 @@ static int ap_device_remove(struct device *dev)
ap_flush_queue(ap_dev);
del_timer_sync(&ap_dev->timeout);
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
if (ap_drv->remove)
ap_drv->remove(ap_dev);
spin_lock_bh(&ap_dev->lock);
@@ -599,6 +723,14 @@ static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
}
+static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ap_using_interrupts() ? 1 : 0);
+}
+
+static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL);
+
static ssize_t ap_config_time_store(struct bus_type *bus,
const char *buf, size_t count)
{
@@ -653,7 +785,8 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
ktime_t hr_time;
/* 120 seconds = maximum poll interval */
- if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000)
+ if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 ||
+ time > 120000000000ULL)
return -EINVAL;
poll_timeout = time;
hr_time = ktime_set(0, poll_timeout);
@@ -672,6 +805,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
&bus_attr_ap_domain,
&bus_attr_config_time,
&bus_attr_poll_thread,
+ &bus_attr_ap_interrupts,
&bus_attr_poll_timeout,
NULL,
};
@@ -814,6 +948,11 @@ out:
return rc;
}
+static void ap_interrupt_handler(void *unused1, void *unused2)
+{
+ tasklet_schedule(&ap_tasklet);
+}
+
/**
* __ap_scan_bus(): Scan the AP bus.
* @dev: Pointer to device
@@ -928,6 +1067,8 @@ ap_config_timeout(unsigned long ptr)
*/
static inline void ap_schedule_poll_timer(void)
{
+ if (ap_using_interrupts())
+ return;
if (hrtimer_is_queued(&ap_poll_timer))
return;
hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout),
@@ -1181,7 +1322,7 @@ static void ap_reset(struct ap_device *ap_dev)
ap_dev->unregistered = 1;
}
-static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags)
+static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
{
spin_lock(&ap_dev->lock);
if (!ap_dev->unregistered) {
@@ -1207,13 +1348,19 @@ static void ap_poll_all(unsigned long dummy)
unsigned long flags;
struct ap_device *ap_dev;
+ /* Reset the indicator if interrupts are used. Thus new interrupts can
+ * be received. Doing it in the beginning of the tasklet is therefor
+ * important that no requests on any AP get lost.
+ */
+ if (ap_using_interrupts())
+ xchg((u8 *)ap_interrupt_indicator, 0);
do {
flags = 0;
- spin_lock(&ap_device_lock);
+ spin_lock(&ap_device_list_lock);
list_for_each_entry(ap_dev, &ap_device_list, list) {
- __ap_poll_all(ap_dev, &flags);
+ __ap_poll_device(ap_dev, &flags);
}
- spin_unlock(&ap_device_lock);
+ spin_unlock(&ap_device_list_lock);
} while (flags & 1);
if (flags & 2)
ap_schedule_poll_timer();
@@ -1253,11 +1400,11 @@ static int ap_poll_thread(void *data)
remove_wait_queue(&ap_poll_wait, &wait);
flags = 0;
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_for_each_entry(ap_dev, &ap_device_list, list) {
- __ap_poll_all(ap_dev, &flags);
+ __ap_poll_device(ap_dev, &flags);
}
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&ap_poll_wait, &wait);
@@ -1268,6 +1415,8 @@ static int ap_poll_thread_start(void)
{
int rc;
+ if (ap_using_interrupts())
+ return 0;
mutex_lock(&ap_poll_thread_mutex);
if (!ap_poll_kthread) {
ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
@@ -1301,8 +1450,12 @@ static void ap_request_timeout(unsigned long data)
{
struct ap_device *ap_dev = (struct ap_device *) data;
- if (ap_dev->reset == AP_RESET_ARMED)
+ if (ap_dev->reset == AP_RESET_ARMED) {
ap_dev->reset = AP_RESET_DO;
+
+ if (ap_using_interrupts())
+ tasklet_schedule(&ap_tasklet);
+ }
}
static void ap_reset_domain(void)
@@ -1337,14 +1490,25 @@ int __init ap_module_init(void)
int rc, i;
if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
- printk(KERN_WARNING "Invalid param: domain = %d. "
- " Not loading.\n", ap_domain_index);
+ pr_warning("%d is not a valid cryptographic domain\n",
+ ap_domain_index);
return -EINVAL;
}
if (ap_instructions_available() != 0) {
- printk(KERN_WARNING "AP instructions not installed.\n");
+ pr_warning("The hardware system does not support "
+ "AP instructions\n");
return -ENODEV;
}
+ if (ap_interrupts_available()) {
+ isc_register(AP_ISC);
+ ap_interrupt_indicator = s390_register_adapter_interrupt(
+ &ap_interrupt_handler, NULL, AP_ISC);
+ if (IS_ERR(ap_interrupt_indicator)) {
+ ap_interrupt_indicator = NULL;
+ isc_unregister(AP_ISC);
+ }
+ }
+
register_reset_call(&ap_reset_call);
/* Create /sys/bus/ap. */
@@ -1408,6 +1572,10 @@ out_bus:
bus_unregister(&ap_bus_type);
out:
unregister_reset_call(&ap_reset_call);
+ if (ap_using_interrupts()) {
+ s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
+ isc_unregister(AP_ISC);
+ }
return rc;
}
@@ -1443,6 +1611,10 @@ void ap_module_exit(void)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
bus_unregister(&ap_bus_type);
unregister_reset_call(&ap_reset_call);
+ if (ap_using_interrupts()) {
+ s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
+ isc_unregister(AP_ISC);
+ }
}
#ifndef CONFIG_ZCRYPT_MONOLITHIC
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 446378b308fc..a35362241805 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus header file.
*
@@ -67,7 +68,8 @@ struct ap_queue_status {
unsigned int queue_empty : 1;
unsigned int replies_waiting : 1;
unsigned int queue_full : 1;
- unsigned int pad1 : 5;
+ unsigned int pad1 : 4;
+ unsigned int int_enabled : 1;
unsigned int response_code : 8;
unsigned int pad2 : 16;
};
@@ -78,6 +80,8 @@ struct ap_queue_status {
#define AP_RESPONSE_DECONFIGURED 0x03
#define AP_RESPONSE_CHECKSTOPPED 0x04
#define AP_RESPONSE_BUSY 0x05
+#define AP_RESPONSE_INVALID_ADDRESS 0x06
+#define AP_RESPONSE_OTHERWISE_CHANGED 0x07
#define AP_RESPONSE_Q_FULL 0x10
#define AP_RESPONSE_NO_PENDING_REPLY 0x10
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 54f4cbc3be9e..326ea08f67c9 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -264,17 +264,21 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type80_hdr *t80h = reply->message;
+ struct type80_hdr *t80h;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t80h->type == TYPE80_RSP_CODE) {
+ goto out;
+ }
+ t80h = reply->message;
+ if (t80h->type == TYPE80_RSP_CODE) {
length = min(CEX2A_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);
}
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 12da4815ba8e..17ba81b58c78 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -247,17 +247,21 @@ static void zcrypt_pcica_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type84_hdr *t84h = reply->message;
+ struct type84_hdr *t84h;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t84h->code == TYPE84_RSP_CODE) {
+ goto out;
+ }
+ t84h = reply->message;
+ if (t84h->code == TYPE84_RSP_CODE) {
length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len);
memcpy(msg->message, reply->message, length);
} else
memcpy(msg->message, reply->message, sizeof error_reply);
+out:
complete((struct completion *) msg->private);
}
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index 779952cb19fc..f4b0c4795434 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -447,19 +447,23 @@ static void zcrypt_pcicc_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type86_reply *t86r = reply->message;
+ struct type86_reply *t86r;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ goto out;
+ }
+ t86r = reply->message;
+ if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprb.cprb_ver_id == 0x01) {
length = sizeof(struct type86_reply) + t86r->length - 2;
length = min(PCICC_MAX_RESPONSE_SIZE, length);
memcpy(msg->message, reply->message, length);
} else
memcpy(msg->message, reply->message, sizeof error_reply);
+out:
complete((struct completion *) msg->private);
}
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index d8ad36f81540..e7a1e22e77ac 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -635,13 +635,16 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
};
struct response_type *resp_type =
(struct response_type *) msg->private;
- struct type86x_reply *t86r = reply->message;
+ struct type86x_reply *t86r;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ 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:
@@ -660,6 +663,7 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
}
} else
memcpy(msg->message, reply->message, sizeof error_reply);
+out:
complete(&(resp_type->work));
}
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 3d442444c618..28c90b89f2b4 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -188,11 +188,13 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
config = kvm_vq_config(kdev->desc)+index;
err = vmem_add_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
if (err)
goto out;
- vq = vring_new_virtqueue(config->num, vdev, (void *) config->address,
+ vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN,
+ vdev, (void *) config->address,
kvm_notify, callback);
if (!vq) {
err = -ENOMEM;
@@ -209,7 +211,8 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev,
return vq;
unmap:
vmem_remove_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
out:
return ERR_PTR(err);
}
@@ -220,7 +223,8 @@ static void kvm_del_vq(struct virtqueue *vq)
vring_del_virtqueue(vq);
vmem_remove_mapping(config->address,
- vring_size(config->num, PAGE_SIZE));
+ vring_size(config->num,
+ KVM_S390_VIRTIO_RING_ALIGN));
}
/*
@@ -295,13 +299,29 @@ static void scan_devices(void)
*/
static void kvm_extint_handler(u16 code)
{
- void *data = (void *) *(long *) __LC_PFAULT_INTPARM;
- u16 subcode = S390_lowcore.cpu_addr;
+ struct virtqueue *vq;
+ u16 subcode;
+ int config_changed;
+ subcode = S390_lowcore.cpu_addr;
if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
- vring_interrupt(0, data);
+ /* The LSB might be overloaded, we have to mask it */
+ vq = (struct virtqueue *) ((*(long *) __LC_PFAULT_INTPARM) & ~1UL);
+
+ /* We use the LSB of extparam, to decide, if this interrupt is a config
+ * change or a "standard" interrupt */
+ config_changed = (*(int *) __LC_EXT_PARAMS & 1);
+
+ if (config_changed) {
+ struct virtio_driver *drv;
+ drv = container_of(vq->vdev->dev.driver,
+ struct virtio_driver, driver);
+ if (drv->config_changed)
+ drv->config_changed(vq->vdev);
+ } else
+ vring_interrupt(0, vq);
}
/*
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 42776550acfd..f29c7086fc19 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -13,6 +13,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -190,21 +193,22 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
{
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
- "%s(%s): %s: %04x\n",
- CTCM_FUNTAIL, ch->id, msg, rc);
+ "%s(%s): %s: %04x\n",
+ CTCM_FUNTAIL, ch->id, msg, rc);
switch (rc) {
case -EBUSY:
- ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg);
+ pr_info("%s: The communication peer is busy\n",
+ ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
break;
case -ENODEV:
- ctcm_pr_emerg("%s (%s): Invalid device called for IO\n",
- ch->id, msg);
+ pr_err("%s: The specified target device is not valid\n",
+ ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
break;
default:
- ctcm_pr_emerg("%s (%s): Unknown error in do_IO %04x\n",
- ch->id, msg, rc);
+ pr_err("An I/O operation resulted in error %04x\n",
+ rc);
fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
}
}
@@ -886,8 +890,15 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CTC_STATE_RXERR);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
}
- } else
- ctcm_pr_warn("%s: Error during RX init handshake\n", dev->name);
+ } else {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
+ ctc_ch_event_names[event], fsm_getstate_str(fi));
+
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
+ }
}
/**
@@ -969,7 +980,9 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
ctc_ch_event_names[event], fsm_getstate_str(fi));
- ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name);
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
}
}
@@ -2101,14 +2114,11 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg)
CTCMY_DBF_DEV_NAME(TRACE, dev, "");
if (IS_MPC(priv)) {
- ctcm_pr_info("ctcm: %s Restarting Device and "
- "MPC Group in 5 seconds\n",
- dev->name);
restart_timer = CTCM_TIME_1_SEC;
} else {
- ctcm_pr_info("%s: Restarting\n", dev->name);
restart_timer = CTCM_TIME_5_SEC;
}
+ dev_info(&dev->dev, "Restarting device\n");
dev_action_stop(fi, event, arg);
fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
@@ -2150,16 +2160,16 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg)
case DEV_STATE_STARTWAIT_RX:
if (event == DEV_EVENT_RXUP) {
fsm_newstate(fi, DEV_STATE_RUNNING);
- ctcm_pr_info("%s: connected with remote side\n",
- dev->name);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
ctcm_clear_busy(dev);
}
break;
case DEV_STATE_STARTWAIT_TX:
if (event == DEV_EVENT_TXUP) {
fsm_newstate(fi, DEV_STATE_RUNNING);
- ctcm_pr_info("%s: connected with remote side\n",
- dev->name);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
ctcm_clear_busy(dev);
}
break;
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index a4e29836a2aa..2678573becec 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -21,6 +21,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -281,14 +284,16 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- ctcm_pr_warn("i/o-error on device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An I/O-error occurred on the CTCM device\n");
break;
case -ETIMEDOUT:
- ctcm_pr_warn("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An adapter hardware operation timed out\n");
break;
default:
- ctcm_pr_warn("unknown error %ld on device %s\n",
- PTR_ERR(irb), dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An error occurred on the adapter hardware\n");
}
return PTR_ERR(irb);
}
@@ -309,15 +314,17 @@ static inline void ccw_unit_check(struct channel *ch, __u8 sense)
if (sense & SNS0_INTERVENTION_REQ) {
if (sense & 0x01) {
if (ch->sense_rc != 0x01) {
- ctcm_pr_debug("%s: Interface disc. or Sel. "
- "reset (remote)\n", ch->id);
+ pr_notice(
+ "%s: The communication peer has "
+ "disconnected\n", ch->id);
ch->sense_rc = 0x01;
}
fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
} else {
if (ch->sense_rc != SNS0_INTERVENTION_REQ) {
- ctcm_pr_debug("%s: System reset (remote)\n",
- ch->id);
+ pr_notice(
+ "%s: The remote operating system is "
+ "not available\n", ch->id);
ch->sense_rc = SNS0_INTERVENTION_REQ;
}
fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
@@ -1194,8 +1201,11 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
/* Check for unsolicited interrupts. */
if (cgdev == NULL) {
- ctcm_pr_warn("ctcm: Got unsolicited irq: c-%02x d-%02x\n",
- cstat, dstat);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR,
+ "%s(%s) unsolicited irq: c-%02x d-%02x\n",
+ CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat);
+ dev_warn(&cdev->dev,
+ "The adapter received a non-specific IRQ\n");
return;
}
@@ -1207,31 +1217,34 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
else if (priv->channel[WRITE]->cdev == cdev)
ch = priv->channel[WRITE];
else {
- ctcm_pr_err("ctcm: Can't determine channel for interrupt, "
- "device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "%s: Internal error: Can't determine channel for "
+ "interrupt device %s\n",
+ __func__, dev_name(&cdev->dev));
+ /* Explain: inconsistent internal structures */
return;
}
dev = ch->netdev;
if (dev == NULL) {
- ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n",
- __func__, dev_name(&cdev->dev), ch);
+ dev_err(&cdev->dev,
+ "%s Internal error: net_device is NULL, ch = 0x%p\n",
+ __func__, ch);
+ /* Explain: inconsistent internal structures */
return;
}
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
- "%s(%s): int. for %s: cstat=%02x dstat=%02x",
- CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
-
/* Copy interruption response block. */
memcpy(ch->irb, irb, sizeof(struct irb));
+ /* Issue error message and return on subchannel error code */
if (irb->scsw.cmd.cstat) {
- /* Check for good subchannel return code, otherwise error message */
fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
- ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
- dev->name, ch->id, irb->scsw.cmd.cstat,
- irb->scsw.cmd.dstat);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): sub-ch check %s: cs=%02x ds=%02x",
+ CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
+ dev_warn(&cdev->dev,
+ "A check occurred on the subchannel\n");
return;
}
@@ -1239,7 +1252,7 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
if ((irb->ecw[0] & ch->sense_rc) == 0)
/* print it only once */
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
"%s(%s): sense=%02x, ds=%02x",
CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat);
ccw_unit_check(ch, irb->ecw[0]);
@@ -1574,6 +1587,11 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
+ dev_info(&dev->dev,
+ "setup OK : r/w = %s/%s, protocol : %d\n",
+ priv->channel[READ]->id,
+ priv->channel[WRITE]->id, priv->protocol);
+
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
priv->channel[READ]->id,
@@ -1687,7 +1705,7 @@ static void __exit ctcm_exit(void)
{
unregister_cu3088_discipline(&ctcm_group_driver);
ctcm_unregister_dbf_views();
- ctcm_pr_info("CTCM driver unloaded\n");
+ pr_info("CTCM driver unloaded\n");
}
/*
@@ -1695,7 +1713,7 @@ static void __exit ctcm_exit(void)
*/
static void print_banner(void)
{
- printk(KERN_INFO "CTCM driver initialized\n");
+ pr_info("CTCM driver initialized\n");
}
/**
@@ -1717,8 +1735,8 @@ static int __init ctcm_init(void)
ret = register_cu3088_discipline(&ctcm_group_driver);
if (ret) {
ctcm_unregister_dbf_views();
- ctcm_pr_crit("ctcm_init failed with register_cu3088_discipline "
- "(rc = %d)\n", ret);
+ pr_err("%s / register_cu3088_discipline failed, ret = %d\n",
+ __func__, ret);
return ret;
}
print_banner();
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index d77cce3fe4d4..d925e732b7d8 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -41,12 +41,6 @@
#define LOG_FLAG_NOMEM 8
#define ctcm_pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
-#define ctcm_pr_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
-#define ctcm_pr_notice(fmt, arg...) printk(KERN_NOTICE fmt, ##arg)
-#define ctcm_pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
-#define ctcm_pr_emerg(fmt, arg...) printk(KERN_EMERG fmt, ##arg)
-#define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg)
-#define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg)
#define CTCM_PR_DEBUG(fmt, arg...) \
do { \
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 19f5d5ed85e0..3db5f846bbf6 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -19,6 +19,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -386,7 +389,7 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
if (grp->allocchan_callback_retries < 4) {
if (grp->allochanfunc)
grp->allochanfunc(grp->port_num,
- grp->group_max_buflen);
+ grp->group_max_buflen);
} else {
/* there are problems...bail out */
/* there may be a state mismatch so restart */
@@ -1232,8 +1235,9 @@ done:
dev_kfree_skb_any(pskb);
if (sendrc == NET_RX_DROP) {
- printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED"
- " - PACKET DROPPED\n", dev->name, __func__);
+ dev_warn(&dev->dev,
+ "The network backlog for %s is exceeded, "
+ "package dropped\n", __func__);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
@@ -1670,10 +1674,11 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
CTCM_FUNTAIL, ch->id);
}
}
-
done:
if (rc) {
- ctcm_pr_info("ctcmpc : %s() failed\n", __func__);
+ dev_warn(&dev->dev,
+ "The XID used in the MPC protocol is not valid, "
+ "rc = %d\n", rc);
priv->xid->xid2_flag2 = 0x40;
grp->saved_xid2->xid2_flag2 = 0x40;
}
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
index bb2d13721d34..8452bb052d68 100644
--- a/drivers/s390/net/ctcm_sysfs.c
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -10,6 +10,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/sysfs.h>
#include "ctcm_main.h"
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 0825be87e5a0..acca6678cb2b 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -26,6 +26,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "lcs"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
@@ -54,8 +57,6 @@
#error Cannot compile lcs.c without some net devices switched on.
#endif
-#define PRINTK_HEADER " lcs: "
-
/**
* initialization string for output
*/
@@ -96,7 +97,7 @@ lcs_register_debug_facility(void)
lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8);
lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8);
if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
- PRINT_ERR("Not enough memory for debug facility.\n");
+ pr_err("Not enough memory for debug facility.\n");
lcs_unregister_debug_facility();
return -ENOMEM;
}
@@ -503,7 +504,9 @@ lcs_start_channel(struct lcs_channel *channel)
if (rc) {
LCS_DBF_TEXT_(4,trace,"essh%s",
dev_name(&channel->ccwdev->dev));
- PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
+ dev_err(&channel->ccwdev->dev,
+ "Starting an LCS device resulted in an error,"
+ " rc=%d!\n", rc);
}
return rc;
}
@@ -640,7 +643,9 @@ __lcs_resume_channel(struct lcs_channel *channel)
if (rc) {
LCS_DBF_TEXT_(4, trace, "ersc%s",
dev_name(&channel->ccwdev->dev));
- PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
+ dev_err(&channel->ccwdev->dev,
+ "Sending data from the LCS device to the LAN failed"
+ " with rc=%d\n",rc);
} else
channel->state = LCS_CH_STATE_RUNNING;
return rc;
@@ -1086,7 +1091,7 @@ lcs_check_multicast_support(struct lcs_card *card)
cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
if (rc != 0) {
- PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
+ pr_err("Query IPAssist failed. Assuming unsupported!\n");
return -EOPNOTSUPP;
}
if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
@@ -1119,8 +1124,8 @@ list_modified:
rc = lcs_send_setipm(card, ipm);
spin_lock_irqsave(&card->ipm_lock, flags);
if (rc) {
- PRINT_INFO("Adding multicast address failed. "
- "Table possibly full!\n");
+ pr_info("Adding multicast address failed."
+ " Table possibly full!\n");
/* store ipm in failed list -> will be added
* to ipm_list again, so a retry will be done
* during the next call of this function */
@@ -1231,8 +1236,8 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
ipm = (struct lcs_ipm_list *)
kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
if (ipm == NULL) {
- PRINT_INFO("Not enough memory to add "
- "new multicast entry!\n");
+ pr_info("Not enough memory to add"
+ " new multicast entry!\n");
break;
}
memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
@@ -1290,7 +1295,7 @@ lcs_set_multicast_list(struct net_device *dev)
struct lcs_card *card;
LCS_DBF_TEXT(4, trace, "setmulti");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD))
schedule_work(&card->kernel_thread_starter);
@@ -1306,18 +1311,21 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "An I/O-error occurred on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "A command timed out on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT);
break;
default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "An error occurred on the LCS device, rc=%ld\n",
+ PTR_ERR(irb));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT(2, trace, " rc???");
}
@@ -1403,8 +1411,10 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
/* Check for channel and device errors presented */
rc = lcs_get_problem(cdev, irb);
if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
- PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
- dev_name(&cdev->dev), dstat, cstat);
+ dev_warn(&cdev->dev,
+ "The LCS device stopped because of an error,"
+ " dstat=0x%X, cstat=0x%X \n",
+ dstat, cstat);
if (rc) {
channel->state = LCS_CH_STATE_ERROR;
}
@@ -1607,7 +1617,7 @@ lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
int rc;
LCS_DBF_TEXT(5, trace, "pktxmit");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
rc = __lcs_start_xmit(card, skb, dev);
return rc;
}
@@ -1761,8 +1771,8 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
lcs_schedule_recovery(card);
break;
case LCS_CMD_STOPLAN:
- PRINT_WARN("Stoplan for %s initiated by LGW.\n",
- card->dev->name);
+ pr_warning("Stoplan for %s initiated by LGW.\n",
+ card->dev->name);
if (card->dev)
netif_carrier_off(card->dev);
break;
@@ -1790,7 +1800,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
skb = dev_alloc_skb(skb_len);
if (skb == NULL) {
- PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
+ dev_err(&card->dev->dev,
+ " Allocating a socket buffer to interface %s failed\n",
card->dev->name);
card->stats.rx_dropped++;
return;
@@ -1863,7 +1874,7 @@ lcs_getstats(struct net_device *dev)
struct lcs_card *card;
LCS_DBF_TEXT(4, trace, "netstats");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
return &card->stats;
}
@@ -1878,7 +1889,7 @@ lcs_stop_device(struct net_device *dev)
int rc;
LCS_DBF_TEXT(2, trace, "stopdev");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
netif_carrier_off(dev);
netif_tx_disable(dev);
dev->flags &= ~IFF_UP;
@@ -1886,7 +1897,8 @@ lcs_stop_device(struct net_device *dev)
(card->write.state != LCS_CH_STATE_RUNNING));
rc = lcs_stopcard(card);
if (rc)
- PRINT_ERR("Try it again!\n ");
+ dev_err(&card->dev->dev,
+ " Shutting down the LCS device failed\n ");
return rc;
}
@@ -1901,11 +1913,11 @@ lcs_open_device(struct net_device *dev)
int rc;
LCS_DBF_TEXT(2, trace, "opendev");
- card = (struct lcs_card *) dev->priv;
+ card = (struct lcs_card *) dev->ml_priv;
/* initialize statistics */
rc = lcs_detect(card);
if (rc) {
- PRINT_ERR("LCS:Error in opening device!\n");
+ pr_err("Error in opening device!\n");
} else {
dev->flags |= IFF_UP;
@@ -2113,8 +2125,9 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
rc = lcs_detect(card);
if (rc) {
LCS_DBF_TEXT(2, setup, "dtctfail");
- PRINT_WARN("Detection of LCS card failed with return code "
- "%d (0x%x)\n", rc, rc);
+ dev_err(&card->dev->dev,
+ "Detecting a network adapter for LCS devices"
+ " failed with rc=%d (0x%x)\n", rc, rc);
lcs_stopcard(card);
goto out;
}
@@ -2144,13 +2157,13 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
#endif
default:
LCS_DBF_TEXT(3, setup, "errinit");
- PRINT_ERR("LCS: Initialization failed\n");
+ pr_err(" Initialization failed\n");
goto out;
}
if (!dev)
goto out;
card->dev = dev;
- card->dev->priv = card;
+ card->dev->ml_priv = card;
card->dev->open = lcs_open_device;
card->dev->stop = lcs_stop_device;
card->dev->hard_start_xmit = lcs_start_xmit;
@@ -2176,13 +2189,13 @@ netdev_out:
goto out;
/* Print out supported assists: IPv6 */
- PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s IPv6 support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
+ "with" : "without");
/* Print out supported assist: Multicast */
- PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s Multicast support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
+ "with" : "without");
return 0;
out:
@@ -2248,15 +2261,16 @@ lcs_recovery(void *ptr)
return 0;
LCS_DBF_TEXT(4, trace, "recover2");
gdev = card->gdev;
- PRINT_WARN("Recovery of device %s started...\n", dev_name(&gdev->dev));
+ dev_warn(&gdev->dev,
+ "A recovery process has been started for the LCS device\n");
rc = __lcs_shutdown_device(gdev, 1);
rc = lcs_new_device(gdev);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- card->dev->name);
+ pr_info("Device %s successfully recovered!\n",
+ card->dev->name);
else
- PRINT_INFO("Device %s could not be recovered!\n",
- card->dev->name);
+ pr_info("Device %s could not be recovered!\n",
+ card->dev->name);
lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD);
return 0;
}
@@ -2308,17 +2322,17 @@ __init lcs_init_module(void)
{
int rc;
- PRINT_INFO("Loading %s\n",version);
+ pr_info("Loading %s\n", version);
rc = lcs_register_debug_facility();
LCS_DBF_TEXT(0, setup, "lcsinit");
if (rc) {
- PRINT_ERR("Initialization failed\n");
+ pr_err("Initialization failed\n");
return rc;
}
rc = register_cu3088_discipline(&lcs_group_driver);
if (rc) {
- PRINT_ERR("Initialization failed\n");
+ pr_err("Initialization failed\n");
return rc;
}
return 0;
@@ -2331,7 +2345,7 @@ __init lcs_init_module(void)
static void
__exit lcs_cleanup_module(void)
{
- PRINT_INFO("Terminating lcs module.\n");
+ pr_info("Terminating lcs module.\n");
LCS_DBF_TEXT(0, trace, "cleanup");
unregister_cu3088_discipline(&lcs_group_driver);
lcs_unregister_debug_facility();
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 0fea51e34b57..930e2fc2a011 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -31,6 +31,9 @@
*
*/
+#define KMSG_COMPONENT "netiucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#undef DEBUG
#include <linux/module.h>
@@ -846,7 +849,8 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&conn->timer);
iucv_path_sever(conn->path, NULL);
- PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
+ dev_info(privptr->dev, "The peer interface of the IUCV device"
+ " has closed the connection\n");
IUCV_DBF_TEXT(data, 2,
"conn_action_connsever: Remote dropped connection\n");
fsm_newstate(fi, CONN_STATE_STARTWAIT);
@@ -856,13 +860,15 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
static void conn_action_start(fsm_instance *fi, int event, void *arg)
{
struct iucv_connection *conn = arg;
+ struct net_device *netdev = conn->netdev;
+ struct netiucv_priv *privptr = netdev_priv(netdev);
int rc;
IUCV_DBF_TEXT(trace, 3, __func__);
fsm_newstate(fi, CONN_STATE_STARTWAIT);
IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
- conn->netdev->name, conn->userid);
+ netdev->name, conn->userid);
/*
* We must set the state before calling iucv_connect because the
@@ -876,41 +882,45 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
NULL, iucvMagic, conn);
switch (rc) {
case 0:
- conn->netdev->tx_queue_len = conn->path->msglim;
+ netdev->tx_queue_len = conn->path->msglim;
fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
CONN_EVENT_TIMER, conn);
return;
case 11:
- PRINT_INFO("%s: User %s is currently not available.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to z/VM guest %s\n",
+ netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 12:
- PRINT_INFO("%s: User %s is currently not ready.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to the peer on z/VM"
+ " guest %s\n", netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 13:
- PRINT_WARN("%s: Too many IUCV connections.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device would exceed the maximum"
+ " number of IUCV connections\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 14:
- PRINT_WARN("%s: User %s has too many IUCV connections.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_err(privptr->dev,
+ "z/VM guest %s has too many IUCV connections"
+ " to connect with the IUCV device\n",
+ netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 15:
- PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "The IUCV device cannot connect to a z/VM guest with no"
+ " IUCV authorization\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
default:
- PRINT_WARN("%s: iucv_connect returned error %d\n",
- conn->netdev->name, rc);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device failed with error %d\n",
+ rc);
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
}
@@ -1059,8 +1069,9 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
switch (fsm_getstate(fi)) {
case DEV_STATE_STARTWAIT:
fsm_newstate(fi, DEV_STATE_RUNNING);
- PRINT_INFO("%s: connected with remote side %s\n",
- dev->name, privptr->conn->userid);
+ dev_info(privptr->dev,
+ "The IUCV device has been connected"
+ " successfully to %s\n", privptr->conn->userid);
IUCV_DBF_TEXT(setup, 3,
"connection is up and running\n");
break;
@@ -1982,6 +1993,8 @@ static ssize_t conn_write(struct device_driver *drv,
if (rc)
goto out_unreg;
+ dev_info(priv->dev, "The IUCV interface to %s has been"
+ " established successfully\n", netiucv_printname(username));
return count;
@@ -2027,10 +2040,9 @@ static ssize_t remove_write (struct device_driver *drv,
continue;
read_unlock_bh(&iucv_connection_rwlock);
if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
- PRINT_WARN("netiucv: net device %s active with peer "
- "%s\n", ndev->name, priv->conn->userid);
- PRINT_WARN("netiucv: %s cannot be removed\n",
- ndev->name);
+ dev_warn(dev, "The IUCV device is connected"
+ " to %s and cannot be removed\n",
+ priv->conn->userid);
IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
return -EPERM;
}
@@ -2062,7 +2074,7 @@ static struct attribute_group *netiucv_drv_attr_groups[] = {
static void netiucv_banner(void)
{
- PRINT_INFO("NETIUCV driver initialized\n");
+ pr_info("driver initialized\n");
}
static void __exit netiucv_exit(void)
@@ -2088,7 +2100,7 @@ static void __exit netiucv_exit(void)
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
- PRINT_INFO("NETIUCV driver unloaded\n");
+ pr_info("driver unloaded\n");
return;
}
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index af6d60458513..d5ccce1643e4 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -31,11 +31,10 @@
#include <asm/qdio.h>
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
+#include <asm/sysinfo.h>
#include "qeth_core_mpc.h"
-#define KMSG_COMPONENT "qeth"
-
/**
* Debug Facility stuff
*/
@@ -74,11 +73,6 @@ struct qeth_dbf_info {
#define QETH_DBF_TEXT_(name, level, text...) \
qeth_dbf_longtext(QETH_DBF_##name, level, text)
-/**
- * some more debug stuff
- */
-#define PRINTK_HEADER "qeth: "
-
#define SENSE_COMMAND_REJECT_BYTE 0
#define SENSE_COMMAND_REJECT_FLAG 0x80
#define SENSE_RESETTING_EVENT_BYTE 1
@@ -733,6 +727,7 @@ struct qeth_card {
struct qeth_osn_info osn_info;
struct qeth_discipline discipline;
atomic_t force_alloc_skb;
+ struct service_level qeth_service_level;
};
struct qeth_card_list_struct {
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 52d26592c72c..e783644a2105 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -319,7 +322,10 @@ static int qeth_issue_next_read(struct qeth_card *card)
return -EIO;
iob = qeth_get_buffer(&card->read);
if (!iob) {
- PRINT_WARN("issue_next_read failed: no iob available!\n");
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob "
+ "available\n", dev_name(&card->gdev->dev));
return -ENOMEM;
}
qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE);
@@ -327,7 +333,8 @@ static int qeth_issue_next_read(struct qeth_card *card)
rc = ccw_device_start(card->read.ccwdev, &card->read.ccw,
(addr_t) iob, 0, 0);
if (rc) {
- PRINT_ERR("Error in starting next read ccw! rc=%i\n", rc);
+ QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! "
+ "rc=%i\n", dev_name(&card->gdev->dev), rc);
atomic_set(&card->read.irq_pending, 0);
qeth_schedule_recovery(card);
wake_up(&card->wait_q);
@@ -393,10 +400,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
} else {
switch (cmd->hdr.command) {
case IPA_CMD_STOPLAN:
- PRINT_WARN("Link failure on %s (CHPID 0x%X) - "
- "there is a network problem or "
- "someone pulled the cable or "
- "disabled the port.\n",
+ dev_warn(&card->gdev->dev,
+ "The link for interface %s on CHPID"
+ " 0x%X failed\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
card->lan_online = 0;
@@ -404,9 +410,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
netif_carrier_off(card->dev);
return NULL;
case IPA_CMD_STARTLAN:
- PRINT_INFO("Link reestablished on %s "
- "(CHPID 0x%X). Scheduling "
- "IP address reset.\n",
+ dev_info(&card->gdev->dev,
+ "The link for %s on CHPID 0x%X has"
+ " been restored\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
netif_carrier_on(card->dev);
@@ -458,7 +464,7 @@ static int qeth_check_idx_response(unsigned char *buffer)
QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN);
if ((buffer[2] & 0xc0) == 0xc0) {
- PRINT_WARN("received an IDX TERMINATE "
+ QETH_DBF_MESSAGE(2, "received an IDX TERMINATE "
"with cause code 0x%02x%s\n",
buffer[4],
((buffer[4] == 0x22) ?
@@ -744,8 +750,10 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
QETH_DBF_TEXT(TRACE, 2, "CGENCHK");
- PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
- dev_name(&cdev->dev), dstat, cstat);
+ dev_warn(&cdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x ",
+ dev_name(&cdev->dev), dstat, cstat);
print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET,
16, 1, irb, 64, 1);
return 1;
@@ -784,12 +792,14 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
+ QETH_DBF_MESSAGE(2, "%s i/o-error on device\n",
+ dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev, "A hardware operation timed out"
+ " on the device\n");
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -ETIMEDOUT);
if (intparm == QETH_RCD_PARM) {
@@ -802,8 +812,8 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
}
break;
default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- dev_name(&cdev->dev));
+ QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n",
+ dev_name(&cdev->dev), PTR_ERR(irb));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT(TRACE, 2, " rc???");
}
@@ -869,10 +879,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
(dstat & DEV_STAT_UNIT_CHECK) ||
(cstat)) {
if (irb->esw.esw0.erw.cons) {
- /* TODO: we should make this s390dbf */
- PRINT_WARN("sense data available on channel %s.\n",
- CHANNEL_ID(channel));
- PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat);
+ dev_warn(&channel->ccwdev->dev,
+ "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s sense data available. cstat "
+ "0x%X dstat 0x%X\n",
+ dev_name(&channel->ccwdev->dev), cstat, dstat);
print_hex_dump(KERN_WARNING, "qeth: irb ",
DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1);
print_hex_dump(KERN_WARNING, "qeth: sense data ",
@@ -1138,6 +1150,14 @@ static int qeth_setup_card(struct qeth_card *card)
return 0;
}
+static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr)
+{
+ struct qeth_card *card = container_of(slr, struct qeth_card,
+ qeth_service_level);
+ seq_printf(m, "qeth: %s firmware level %s\n", CARD_BUS_ID(card),
+ card->info.mcl_level);
+}
+
static struct qeth_card *qeth_alloc_card(void)
{
struct qeth_card *card;
@@ -1157,6 +1177,8 @@ static struct qeth_card *qeth_alloc_card(void)
return NULL;
}
card->options.layer2 = -1;
+ card->qeth_service_level.seq_print = qeth_core_sl_print;
+ register_service_level(&card->qeth_service_level);
return card;
}
@@ -1175,8 +1197,8 @@ static int qeth_determine_card_type(struct qeth_card *card)
card->qdio.no_out_queues = known_devices[i][8];
card->info.is_multicast_different = known_devices[i][9];
if (qeth_is_1920_device(card)) {
- PRINT_INFO("Priority Queueing not able "
- "due to hardware limitations!\n");
+ dev_info(&card->gdev->dev,
+ "Priority Queueing not supported\n");
card->qdio.no_out_queues = 1;
card->qdio.default_out_queue = 0;
}
@@ -1185,7 +1207,8 @@ static int qeth_determine_card_type(struct qeth_card *card)
i++;
}
card->info.type = QETH_CARD_TYPE_UNKNOWN;
- PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card));
+ dev_err(&card->gdev->dev, "The adapter hardware is of an "
+ "unknown type\n");
return -ENOENT;
}
@@ -1368,8 +1391,8 @@ static int qeth_get_unitaddr(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "getunit");
rc = qeth_read_conf_data(card, (void **) &prcd, &length);
if (rc) {
- PRINT_ERR("qeth_read_conf_data for device %s returned %i\n",
- CARD_DDEV_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n",
+ dev_name(&card->gdev->dev), rc);
return rc;
}
card->info.chpid = prcd[30];
@@ -1519,7 +1542,10 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
if (rc == -ERESTARTSYS)
return rc;
if (channel->state != CH_STATE_ACTIVATING) {
- PRINT_WARN("IDX activate timed out!\n");
+ dev_warn(&channel->ccwdev->dev, "The qeth device driver"
+ " failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n",
+ dev_name(&channel->ccwdev->dev));
QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME);
qeth_clear_cmd_buffers(channel);
return -ETIME;
@@ -1552,20 +1578,21 @@ static void qeth_idx_write_cb(struct qeth_channel *channel,
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_WDEV_ID(card));
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
else
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: "
- "negative reply\n", CARD_WDEV_ID(card));
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:"
+ " negative reply\n",
+ dev_name(&card->write.ccwdev->dev));
goto out;
}
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) {
- PRINT_WARN("IDX_ACTIVATE on write channel device %s: "
- "function level mismatch "
- "(sent: 0x%x, received: 0x%x)\n",
- CARD_WDEV_ID(card), card->info.func_level, temp);
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: "
+ "function level mismatch (sent: 0x%x, received: "
+ "0x%x)\n", dev_name(&card->write.ccwdev->dev),
+ card->info.func_level, temp);
goto out;
}
channel->state = CH_STATE_UP;
@@ -1591,12 +1618,13 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_RDEV_ID(card));
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
else
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: "
- "negative reply\n", CARD_RDEV_ID(card));
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:"
+ " negative reply\n",
+ dev_name(&card->read.ccwdev->dev));
goto out;
}
@@ -1610,9 +1638,10 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (temp != qeth_peer_func_level(card->info.func_level)) {
- PRINT_WARN("IDX_ACTIVATE on read channel device %s: function "
- "level mismatch (sent: 0x%x, received: 0x%x)\n",
- CARD_RDEV_ID(card), card->info.func_level, temp);
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
+ "level mismatch (sent: 0x%x, received: 0x%x)\n",
+ dev_name(&card->read.ccwdev->dev),
+ card->info.func_level, temp);
goto out;
}
memcpy(&card->token.issuer_rm_r,
@@ -1686,8 +1715,9 @@ int qeth_send_control_data(struct qeth_card *card, int len,
(addr_t) iob, 0, 0);
spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
if (rc) {
- PRINT_WARN("qeth_send_control_data: "
- "ccw_device_start rc = %i\n", rc);
+ QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: "
+ "ccw_device_start rc = %i\n",
+ dev_name(&card->write.ccwdev->dev), rc);
QETH_DBF_TEXT_(TRACE, 2, " err%d", rc);
spin_lock_irqsave(&card->lock, flags);
list_del_init(&reply->list);
@@ -2170,11 +2200,8 @@ static void qeth_print_status_with_portname(struct qeth_card *card)
dbf_text[i] =
(char) _ebcasc[(__u8) dbf_text[i]];
dbf_text[8] = 0;
- PRINT_INFO("Device %s/%s/%s is a%s card%s%s%s\n"
+ dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
"with link type %s (portname: %s)\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
qeth_get_cardname(card),
(card->info.mcl_level[0]) ? " (level: " : "",
(card->info.mcl_level[0]) ? card->info.mcl_level : "",
@@ -2187,23 +2214,17 @@ static void qeth_print_status_with_portname(struct qeth_card *card)
static void qeth_print_status_no_portname(struct qeth_card *card)
{
if (card->info.portname[0])
- PRINT_INFO("Device %s/%s/%s is a%s "
+ dev_info(&card->gdev->dev, "Device is a%s "
"card%s%s%s\nwith link type %s "
"(no portname needed by interface).\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
qeth_get_cardname(card),
(card->info.mcl_level[0]) ? " (level: " : "",
(card->info.mcl_level[0]) ? card->info.mcl_level : "",
(card->info.mcl_level[0]) ? ")" : "",
qeth_get_cardname_short(card));
else
- PRINT_INFO("Device %s/%s/%s is a%s "
+ dev_info(&card->gdev->dev, "Device is a%s "
"card%s%s%s\nwith link type %s.\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
qeth_get_cardname(card),
(card->info.mcl_level[0]) ? " (level: " : "",
(card->info.mcl_level[0]) ? card->info.mcl_level : "",
@@ -2325,7 +2346,6 @@ static int qeth_init_input_buffer(struct qeth_card *card,
* the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off
* buffers
*/
- BUG_ON(!pool_entry);
buf->pool_entry = pool_entry;
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
@@ -2630,9 +2650,8 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
qeth_get_micros() -
card->perf_stats.inbound_do_qdio_start_time;
if (rc) {
- PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
- "return %i (device %s).\n",
- rc, CARD_DDEV_ID(card));
+ dev_warn(&card->gdev->dev,
+ "QDIO reported an error, rc=%i\n", rc);
QETH_DBF_TEXT(TRACE, 2, "qinberr");
QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card));
}
@@ -3730,6 +3749,7 @@ static void qeth_core_free_card(struct qeth_card *card)
free_netdev(card->dev);
kfree(card->ip_tbd_list);
qeth_free_qdio_buffers(card);
+ unregister_service_level(&card->qeth_service_level);
kfree(card);
}
@@ -3757,7 +3777,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
int qeth_core_hardsetup_card(struct qeth_card *card)
{
- struct qdio_ssqd_desc *qdio_ssqd;
+ struct qdio_ssqd_desc *ssqd;
int retries = 3;
int mpno = 0;
int rc;
@@ -3766,7 +3786,8 @@ int qeth_core_hardsetup_card(struct qeth_card *card)
atomic_set(&card->force_alloc_skb, 0);
retry:
if (retries < 3) {
- PRINT_WARN("Retrying to do IDX activates.\n");
+ QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
+ dev_name(&card->gdev->dev));
ccw_device_set_offline(CARD_DDEV(card));
ccw_device_set_offline(CARD_WDEV(card));
ccw_device_set_offline(CARD_RDEV(card));
@@ -3792,9 +3813,16 @@ retry:
return rc;
}
- qdio_ssqd = qdio_get_ssqd_desc(CARD_DDEV(card));
- if (qdio_ssqd)
- mpno = qdio_ssqd->pcnt;
+ ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL);
+ if (!ssqd) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd);
+ if (rc == 0)
+ mpno = ssqd->pcnt;
+ kfree(ssqd);
+
if (mpno)
mpno = min(mpno - 1, QETH_MAX_PORTNO);
if (card->info.portno > mpno) {
@@ -3834,7 +3862,10 @@ retry:
}
return 0;
out:
- PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
+ dev_warn(&card->gdev->dev, "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n",
+ dev_name(&card->gdev->dev), rc);
return rc;
}
EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
@@ -4054,8 +4085,8 @@ int qeth_core_load_discipline(struct qeth_card *card,
break;
}
if (!card->discipline.ccwgdriver) {
- PRINT_ERR("Support for discipline %d not present\n",
- discipline);
+ dev_err(&card->gdev->dev, "There is no kernel module to "
+ "support discipline %d\n", discipline);
rc = -EINVAL;
}
return rc;
@@ -4448,7 +4479,7 @@ static int __init qeth_core_init(void)
{
int rc;
- PRINT_INFO("loading core functions\n");
+ pr_info("loading core functions\n");
INIT_LIST_HEAD(&qeth_core_card_list.list);
rwlock_init(&qeth_core_card_list.rwlock);
@@ -4488,9 +4519,10 @@ driver_err:
ccwgroup_err:
ccw_driver_unregister(&qeth_ccw_driver);
ccw_err:
+ QETH_DBF_MESSAGE(2, "Initialization failed with code %d\n", rc);
qeth_unregister_dbf_views();
out_err:
- PRINT_ERR("Initialization failed with code %d\n", rc);
+ pr_err("Initializing the qeth device driver failed\n");
return rc;
}
@@ -4503,7 +4535,7 @@ static void __exit qeth_core_exit(void)
ccw_driver_unregister(&qeth_ccw_driver);
kmem_cache_destroy(qeth_core_header_cache);
qeth_unregister_dbf_views();
- PRINT_INFO("core functions removed\n");
+ pr_info("core functions removed\n");
}
module_init(qeth_core_init);
diff --git a/drivers/s390/net/qeth_core_offl.c b/drivers/s390/net/qeth_core_offl.c
index 452874e89740..4080126ca48c 100644
--- a/drivers/s390/net/qeth_core_offl.c
+++ b/drivers/s390/net/qeth_core_offl.c
@@ -350,7 +350,7 @@ static __wsum qeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp,
phcsum = csum_tcpudp_nofold(eddp->nh.ip4.h.saddr, eddp->nh.ip4.h.daddr,
eddp->thl + data_len, IPPROTO_TCP, 0);
/* compute checksum of tcp header */
- return csum_partial((u8 *)&eddp->th, eddp->thl, phcsum);
+ return csum_partial(&eddp->th, eddp->thl, phcsum);
}
static __wsum qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp,
@@ -362,12 +362,12 @@ static __wsum qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp,
QETH_DBF_TEXT(TRACE, 5, "eddpckt6");
eddp->th.tcp.h.check = 0;
/* compute pseudo header checksum */
- phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.saddr,
+ phcsum = csum_partial(&eddp->nh.ip6.h.saddr,
sizeof(struct in6_addr), 0);
- phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.daddr,
+ phcsum = csum_partial(&eddp->nh.ip6.h.daddr,
sizeof(struct in6_addr), phcsum);
proto = htonl(IPPROTO_TCP);
- phcsum = csum_partial((u8 *)&proto, sizeof(u32), phcsum);
+ phcsum = csum_partial(&proto, sizeof(u32), phcsum);
return phcsum;
}
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 1b1e80336d2c..2c48591ced44 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -131,17 +134,13 @@ static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
mac = &cmd->data.setdelmac.mac[0];
/* MAC already registered, needed in couple/uncouple case */
if (cmd->hdr.return_code == 0x2005) {
- QETH_DBF_MESSAGE(2, "Group MAC %02x:%02x:%02x:%02x:%02x:%02x "
- "already existing on %s \n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card));
+ QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s \n",
+ mac, QETH_CARD_IFNAME(card));
cmd->hdr.return_code = 0;
}
if (cmd->hdr.return_code)
- QETH_DBF_MESSAGE(2, "Could not set group MAC "
- "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ QETH_DBF_MESSAGE(2, "Could not set group MAC %pM on %s: %x\n",
+ mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
return 0;
}
@@ -163,10 +162,8 @@ static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
cmd = (struct qeth_ipa_cmd *) data;
mac = &cmd->data.setdelmac.mac[0];
if (cmd->hdr.return_code)
- QETH_DBF_MESSAGE(2, "Could not delete group MAC "
- "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
- QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+ QETH_DBF_MESSAGE(2, "Could not delete group MAC %pM on %s: %x\n",
+ mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code);
return 0;
}
@@ -503,12 +500,13 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac,
OSA_ADDR_LEN);
- PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
- "successfully registered on device %s\n",
- card->dev->dev_addr[0], card->dev->dev_addr[1],
- card->dev->dev_addr[2], card->dev->dev_addr[3],
- card->dev->dev_addr[4], card->dev->dev_addr[5],
- card->dev->name);
+ dev_info(&card->gdev->dev,
+ "MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ "successfully registered on device %s\n",
+ card->dev->dev_addr[0], card->dev->dev_addr[1],
+ card->dev->dev_addr[2], card->dev->dev_addr[3],
+ card->dev->dev_addr[4], card->dev->dev_addr[5],
+ card->dev->name);
}
return 0;
}
@@ -1015,9 +1013,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
if (rc == 0xe080) {
- PRINT_WARN("LAN on card %s if offline! "
- "Waiting for STARTLAN from card.\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
card->lan_online = 0;
}
return rc;
@@ -1117,8 +1114,8 @@ static int qeth_l2_recover(void *ptr)
if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
return 0;
QETH_DBF_TEXT(TRACE, 2, "recover2");
- PRINT_WARN("Recovery of device %s started ...\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
card->use_hard_stop = 1;
__qeth_l2_set_offline(card->gdev, 1);
rc = __qeth_l2_set_online(card->gdev, 1);
@@ -1126,27 +1123,27 @@ static int qeth_l2_recover(void *ptr)
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
else {
rtnl_lock();
dev_close(card->dev);
rtnl_unlock();
- PRINT_INFO("Device %s could not be recovered!\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
}
return 0;
}
static int __init qeth_l2_init(void)
{
- PRINT_INFO("register layer 2 discipline\n");
+ pr_info("register layer 2 discipline\n");
return 0;
}
static void __exit qeth_l2_exit(void)
{
- PRINT_INFO("unregister layer 2 discipline\n");
+ pr_info("unregister layer 2 discipline\n");
}
static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index ed59fedd5922..c0b30b25a5f1 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -917,8 +920,8 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card,
if (rc) {
QETH_DBF_TEXT(TRACE, 2, "FAILED");
qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
- PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n",
- buf, rc, rc);
+ dev_warn(&card->gdev->dev,
+ "Registering IP address %s failed\n", buf);
}
return rc;
}
@@ -1029,24 +1032,22 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "setadprm");
if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
- PRINT_WARN("set adapter parameters not supported "
- "on device %s.\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "set adapter parameters not supported.\n");
QETH_DBF_TEXT(SETUP, 2, " notsupp");
return 0;
}
rc = qeth_query_setadapterparms(card);
if (rc) {
- PRINT_WARN("couldn't set adapter parameters on device %s: "
- "x%x\n", CARD_BUS_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "%s couldn't set adapter parameters: "
+ "0x%x\n", card->gdev->dev.bus_id, rc);
return rc;
}
if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
rc = qeth_setadpparms_change_macaddr(card);
if (rc)
- PRINT_WARN("couldn't get MAC address on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), rc);
+ dev_warn(&card->gdev->dev, "Reading the adapter MAC"
+ " address failed\n", rc);
}
if ((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
@@ -1160,16 +1161,17 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "ipaarp");
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "ARP processing not supported on %s!\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start ARP processing "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting ARP processing support for %s failed\n",
+ QETH_CARD_IFNAME(card));
}
return rc;
}
@@ -1181,19 +1183,21 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "ipaipfrg");
if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
- PRINT_INFO("Hardware IP fragmentation not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start Hardware IP fragmentation "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting IP fragmentation support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else
- PRINT_INFO("Hardware IP fragmentation enabled \n");
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation enabled \n");
return rc;
}
@@ -1207,17 +1211,18 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
- PRINT_INFO("Inbound source address not "
- "supported on %s\n", QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Inbound source address not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
IPA_CMD_ASS_START, 0);
if (rc)
- PRINT_WARN("Could not start inbound source "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting proxy ARP support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
@@ -1228,19 +1233,19 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtvlan");
if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
- PRINT_WARN("VLAN not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start vlan "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting VLAN support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else {
- PRINT_INFO("VLAN enabled \n");
+ dev_info(&card->gdev->dev, "VLAN enabled\n");
}
return rc;
}
@@ -1252,19 +1257,20 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "stmcast");
if (!qeth_is_supported(card, IPA_MULTICASTING)) {
- PRINT_WARN("Multicast not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Multicast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start multicast "
- "assist on %s: rc=%i\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting multicast support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else {
- PRINT_INFO("Multicast enabled\n");
+ dev_info(&card->gdev->dev, "Multicast enabled\n");
card->dev->flags |= IFF_MULTICAST;
}
return rc;
@@ -1315,36 +1321,37 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
rc = qeth_l3_query_ipassists(card, QETH_PROT_IPV6);
if (rc) {
- PRINT_ERR("IPv6 query ipassist failed on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
IPA_CMD_ASS_START, 3);
if (rc) {
- PRINT_WARN("IPv6 start assist (version 4) failed "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6,
IPA_CMD_ASS_START);
if (rc) {
- PRINT_WARN("IPV6 start assist (version 6) failed "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
IPA_CMD_ASS_START);
if (rc) {
- PRINT_WARN("Could not enable passthrough "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Enabling the passthrough mode for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
out:
- PRINT_INFO("IPV6 enabled \n");
+ dev_info(&card->gdev->dev, "IPV6 enabled\n");
return 0;
}
#endif
@@ -1356,8 +1363,8 @@ static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtipv6");
if (!qeth_is_supported(card, IPA_IPV6)) {
- PRINT_WARN("IPv6 not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "IPv6 not supported on %s\n", QETH_CARD_IFNAME(card));
return 0;
}
#ifdef CONFIG_QETH_IPV6
@@ -1373,34 +1380,35 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "stbrdcst");
card->info.broadcast_capable = 0;
if (!qeth_is_supported(card, IPA_FILTERING)) {
- PRINT_WARN("Broadcast not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Broadcast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
goto out;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not enable broadcasting filtering "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
+ "%s failed\n", QETH_CARD_IFNAME(card));
goto out;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_CONFIGURE, 1);
if (rc) {
- PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Setting up broadcast filtering for %s failed\n",
+ QETH_CARD_IFNAME(card));
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
- PRINT_INFO("Broadcast enabled \n");
+ dev_info(&card->gdev->dev, "Broadcast enabled\n");
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_ENABLE, 1);
if (rc) {
- PRINT_WARN("Could not set up broadcast echo filtering on "
- "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Setting up broadcast echo "
+ "filtering for %s failed\n", QETH_CARD_IFNAME(card));
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
@@ -1419,18 +1427,18 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card)
rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Starting Inbound HW Checksumming failed on %s: "
- "0x%x,\ncontinuing using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
IPA_CMD_ASS_ENABLE,
card->info.csum_mask);
if (rc) {
- PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: "
- "0x%x,\ncontinuing using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
return 0;
@@ -1443,26 +1451,30 @@ static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtcsum");
if (card->options.checksum_type == NO_CHECKSUMMING) {
- PRINT_WARN("Using no checksumming on %s.\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Using no checksumming on %s.\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
if (card->options.checksum_type == SW_CHECKSUMMING) {
- PRINT_WARN("Using SW checksumming on %s.\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Using SW checksumming on %s.\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
- PRINT_WARN("Inbound HW Checksumming not "
- "supported on %s,\ncontinuing "
- "using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Inbound HW Checksumming not "
+ "supported on %s,\ncontinuing "
+ "using Inbound SW Checksumming\n",
+ QETH_CARD_IFNAME(card));
card->options.checksum_type = SW_CHECKSUMMING;
return 0;
}
rc = qeth_l3_send_checksum_command(card);
if (!rc)
- PRINT_INFO("HW Checksumming (inbound) enabled \n");
+ dev_info(&card->gdev->dev,
+ "HW Checksumming (inbound) enabled\n");
return rc;
}
@@ -1474,18 +1486,20 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "sttso");
if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
- PRINT_WARN("Outbound TSO not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Outbound TSO not supported on %s\n",
+ QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
} else {
rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
IPA_CMD_ASS_START, 0);
if (rc)
- PRINT_WARN("Could not start outbound TSO "
- "assist on %s: rc=%i\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Starting outbound TCP "
+ "segmentation offload for %s failed\n",
+ QETH_CARD_IFNAME(card));
else
- PRINT_INFO("Outbound TSO enabled\n");
+ dev_info(&card->gdev->dev,
+ "Outbound TSO enabled\n");
}
if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)) {
card->options.large_send = QETH_LARGE_SEND_NO;
@@ -1578,12 +1592,8 @@ static int qeth_l3_get_unique_id_cb(struct qeth_card *card,
else {
card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD;
- PRINT_WARN("couldn't get a unique id from the card on device "
- "%s (result=x%x), using default id. ipv6 "
- "autoconfig on other lpars may lead to duplicate "
- "ip addresses. please use manually "
- "configured ones.\n",
- CARD_BUS_ID(card), cmd->hdr.return_code);
+ dev_warn(&card->gdev->dev, "The network adapter failed to "
+ "generate a unique ID\n");
}
return 0;
}
@@ -3086,9 +3096,8 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
if (rc == 0xe080) {
- PRINT_WARN("LAN on card %s if offline! "
- "Waiting for STARTLAN from card.\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
card->lan_online = 0;
}
return rc;
@@ -3194,8 +3203,8 @@ static int qeth_l3_recover(void *ptr)
if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
return 0;
QETH_DBF_TEXT(TRACE, 2, "recover2");
- PRINT_WARN("Recovery of device %s started ...\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
card->use_hard_stop = 1;
__qeth_l3_set_offline(card->gdev, 1);
rc = __qeth_l3_set_online(card->gdev, 1);
@@ -3203,14 +3212,14 @@ static int qeth_l3_recover(void *ptr)
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
else {
rtnl_lock();
dev_close(card->dev);
rtnl_unlock();
- PRINT_INFO("Device %s could not be recovered!\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
}
return 0;
}
@@ -3344,7 +3353,7 @@ static int qeth_l3_register_notifiers(void)
return rc;
}
#else
- PRINT_WARN("layer 3 discipline no IPv6 support\n");
+ pr_warning("There is no IPv6 support for the layer 3 discipline\n");
#endif
return 0;
}
@@ -3363,7 +3372,7 @@ static int __init qeth_l3_init(void)
{
int rc = 0;
- PRINT_INFO("register layer 3 discipline\n");
+ pr_info("register layer 3 discipline\n");
rc = qeth_l3_register_notifiers();
return rc;
}
@@ -3371,7 +3380,7 @@ static int __init qeth_l3_init(void)
static void __exit qeth_l3_exit(void)
{
qeth_l3_unregister_notifiers();
- PRINT_INFO("unregister layer 3 discipline\n");
+ pr_info("unregister layer 3 discipline\n");
}
module_init(qeth_l3_init);
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 3d4e3e3f3fc0..8af7dfbe022c 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -25,16 +25,21 @@
* Sven Schuetz
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/miscdevice.h>
+#include <linux/seq_file.h>
#include "zfcp_ext.h"
-static char *device;
+#define ZFCP_BUS_ID_SIZE 20
MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
MODULE_DESCRIPTION("FCP HBA driver");
MODULE_LICENSE("GPL");
-module_param(device, charp, 0400);
+static char *init_device;
+module_param_named(device, init_device, charp, 0400);
MODULE_PARM_DESC(device, "specify initial device");
static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
@@ -67,46 +72,7 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
return 1;
}
-static int __init zfcp_device_setup(char *devstr)
-{
- char *token;
- char *str;
-
- if (!devstr)
- return 0;
-
- /* duplicate devstr and keep the original for sysfs presentation*/
- str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
- if (!str)
- return 0;
-
- strcpy(str, devstr);
-
- token = strsep(&str, ",");
- if (!token || strlen(token) >= BUS_ID_SIZE)
- goto err_out;
- strncpy(zfcp_data.init_busid, token, BUS_ID_SIZE);
-
- token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0,
- (unsigned long long *) &zfcp_data.init_wwpn))
- goto err_out;
-
- token = strsep(&str, ",");
- if (!token || strict_strtoull(token, 0,
- (unsigned long long *) &zfcp_data.init_fcp_lun))
- goto err_out;
-
- kfree(str);
- return 1;
-
- err_out:
- kfree(str);
- pr_err("zfcp: %s is not a valid SCSI device\n", devstr);
- return 0;
-}
-
-static void __init zfcp_init_device_configure(void)
+static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
@@ -114,17 +80,17 @@ static void __init zfcp_init_device_configure(void)
down(&zfcp_data.config_sema);
read_lock_irq(&zfcp_data.config_lock);
- adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid);
+ adapter = zfcp_get_adapter_by_busid(busid);
if (adapter)
zfcp_adapter_get(adapter);
read_unlock_irq(&zfcp_data.config_lock);
if (!adapter)
goto out_adapter;
- port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0);
+ port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
if (IS_ERR(port))
goto out_port;
- unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun);
+ unit = zfcp_unit_enqueue(port, lun);
if (IS_ERR(unit))
goto out_unit;
up(&zfcp_data.config_sema);
@@ -154,6 +120,42 @@ static struct kmem_cache *zfcp_cache_create(int size, char *name)
return kmem_cache_create(name , size, align, 0, NULL);
}
+static void __init zfcp_init_device_setup(char *devstr)
+{
+ char *token;
+ char *str;
+ char busid[ZFCP_BUS_ID_SIZE];
+ u64 wwpn, lun;
+
+ /* duplicate devstr and keep the original for sysfs presentation*/
+ str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
+ if (!str)
+ return;
+
+ strcpy(str, devstr);
+
+ token = strsep(&str, ",");
+ if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE)
+ goto err_out;
+ strncpy(busid, token, ZFCP_BUS_ID_SIZE);
+
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0, (unsigned long long *) &wwpn))
+ goto err_out;
+
+ token = strsep(&str, ",");
+ if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun))
+ goto err_out;
+
+ kfree(str);
+ zfcp_init_device_configure(busid, wwpn, lun);
+ return;
+
+ err_out:
+ kfree(str);
+ pr_err("%s is not a valid SCSI device\n", devstr);
+}
+
static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
@@ -175,7 +177,6 @@ static int __init zfcp_module_init(void)
zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq");
- INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
sema_init(&zfcp_data.config_sema, 1);
rwlock_init(&zfcp_data.config_lock);
@@ -186,21 +187,20 @@ static int __init zfcp_module_init(void)
retval = misc_register(&zfcp_cfdc_misc);
if (retval) {
- pr_err("zfcp: Registering the misc device zfcp_cfdc failed\n");
+ pr_err("Registering the misc device zfcp_cfdc failed\n");
goto out_misc;
}
retval = zfcp_ccw_register();
if (retval) {
- pr_err("zfcp: The zfcp device driver could not register with "
+ pr_err("The zfcp device driver could not register with "
"the common I/O layer\n");
goto out_ccw_register;
}
- if (zfcp_device_setup(device))
- zfcp_init_device_configure();
-
- goto out;
+ if (init_device)
+ zfcp_init_device_setup(init_device);
+ return 0;
out_ccw_register:
misc_deregister(&zfcp_cfdc_misc);
@@ -436,6 +436,16 @@ static void _zfcp_status_read_scheduler(struct work_struct *work)
stat_work));
}
+static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
+{
+ struct zfcp_adapter *adapter =
+ container_of(sl, struct zfcp_adapter, service_level);
+
+ seq_printf(m, "zfcp: %s microcode level %x\n",
+ dev_name(&adapter->ccw_device->dev),
+ adapter->fsf_lic_version);
+}
+
/**
* zfcp_adapter_enqueue - enqueue a new adapter to the list
* @ccw_device: pointer to the struct cc_device
@@ -500,6 +510,8 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later);
+ adapter->service_level.seq_print = zfcp_print_sl;
+
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
@@ -509,14 +521,11 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
&zfcp_sysfs_adapter_attrs))
goto sysfs_failed;
- write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
- list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
- write_unlock_irq(&zfcp_data.config_lock);
-
zfcp_fc_nameserver_init(adapter);
- return 0;
+ if (!zfcp_adapter_scsi_register(adapter))
+ return 0;
sysfs_failed:
zfcp_adapter_debug_unregister(adapter);
@@ -555,14 +564,7 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
return;
zfcp_adapter_debug_unregister(adapter);
-
- /* remove specified adapter data structure from list */
- write_lock_irq(&zfcp_data.config_lock);
- list_del(&adapter->list);
- write_unlock_irq(&zfcp_data.config_lock);
-
zfcp_qdio_free(adapter);
-
zfcp_free_low_mem_buffers(adapter);
kfree(adapter->req_list);
kfree(adapter->fc_stats);
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 951a8d409d1d..285881f07648 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
/**
@@ -103,10 +106,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
if (retval)
goto out;
- retval = zfcp_adapter_scsi_register(adapter);
- if (retval)
- goto out_scsi_register;
-
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
adapter->req_no = 0;
@@ -120,8 +119,6 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
flush_work(&adapter->scan_work);
return 0;
- out_scsi_register:
- zfcp_erp_thread_kill(adapter);
out:
up(&zfcp_data.config_sema);
return retval;
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index ec2abceca6dc..10cbfd172a28 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -7,6 +7,9 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <asm/ccwdev.h>
@@ -82,20 +85,9 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
{
- struct zfcp_adapter *adapter = NULL, *cur_adapter;
- struct ccw_dev_id dev_id;
-
- read_lock_irq(&zfcp_data.config_lock);
- list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
- ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
- if (dev_id.devno == devno) {
- adapter = cur_adapter;
- zfcp_adapter_get(adapter);
- break;
- }
- }
- read_unlock_irq(&zfcp_data.config_lock);
- return adapter;
+ char busid[9];
+ snprintf(busid, sizeof(busid), "0.0.%04x", devno);
+ return zfcp_get_adapter_by_busid(busid);
}
static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 31012d58cfb7..cb6df609953e 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/ctype.h>
#include <asm/debug.h>
#include "zfcp_ext.h"
@@ -519,7 +522,7 @@ static const char *zfcp_rec_dbf_ids[] = {
[29] = "link down",
[30] = "link up status read",
[31] = "open port failed",
- [32] = "open port failed",
+ [32] = "",
[33] = "close port",
[34] = "open unit failed",
[35] = "exclusive open unit failed",
@@ -933,6 +936,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
rct->reason_code = hdr->reason_code;
rct->expl = hdr->reason_code_expl;
rct->vendor_unique = hdr->vendor_unique;
+ rct->max_res_size = hdr->max_res_size;
rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(adapter->san_dbf, level, r, sizeof(*r));
@@ -1040,6 +1044,7 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code);
zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl);
zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique);
+ zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size);
} else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
@@ -1246,7 +1251,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
char dbf_name[DEBUG_MAX_NAME_LEN];
/* debug feature area which records recovery activity */
- sprintf(dbf_name, "zfcp_%s_rec", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev));
adapter->rec_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_rec_dbf_record));
if (!adapter->rec_dbf)
@@ -1256,7 +1261,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->rec_dbf, 3);
/* debug feature area which records HBA (FSF and QDIO) conditions */
- sprintf(dbf_name, "zfcp_%s_hba", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev));
adapter->hba_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_hba_dbf_record));
if (!adapter->hba_dbf)
@@ -1266,7 +1271,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->hba_dbf, 3);
/* debug feature area which records SAN command failures and recovery */
- sprintf(dbf_name, "zfcp_%s_san", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev));
adapter->san_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_san_dbf_record));
if (!adapter->san_dbf)
@@ -1276,7 +1281,7 @@ int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
debug_set_level(adapter->san_dbf, 6);
/* debug feature area which records SCSI command failures and recovery */
- sprintf(dbf_name, "zfcp_%s_scsi", zfcp_get_busid_by_adapter(adapter));
+ sprintf(dbf_name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev));
adapter->scsi_dbf = debug_register(dbf_name, dbfsize, 1,
sizeof(struct zfcp_scsi_dbf_record));
if (!adapter->scsi_dbf)
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 5d6b2dff855b..74998ff88e57 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -171,6 +171,7 @@ struct zfcp_san_dbf_record_ct_response {
u8 reason_code;
u8 expl;
u8 vendor_unique;
+ u16 max_res_size;
u32 len;
} __attribute__ ((packed));
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 9ce4c75bd190..510662783a6f 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -33,6 +33,7 @@
#include <asm/qdio.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
+#include <asm/sysinfo.h>
#include "zfcp_dbf.h"
#include "zfcp_fsf.h"
@@ -158,20 +159,6 @@ struct fcp_rscn_element {
u32 nport_did:24;
} __attribute__((packed));
-#define ZFCP_PORT_ADDRESS 0x0
-#define ZFCP_AREA_ADDRESS 0x1
-#define ZFCP_DOMAIN_ADDRESS 0x2
-#define ZFCP_FABRIC_ADDRESS 0x3
-
-#define ZFCP_PORTS_RANGE_PORT 0xFFFFFF
-#define ZFCP_PORTS_RANGE_AREA 0xFFFF00
-#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
-#define ZFCP_PORTS_RANGE_FABRIC 0x000000
-
-#define ZFCP_NO_PORTS_PER_AREA 0x100
-#define ZFCP_NO_PORTS_PER_DOMAIN 0x10000
-#define ZFCP_NO_PORTS_PER_FABRIC 0x1000000
-
/* see fc-ph */
struct fcp_logo {
u32 command;
@@ -210,7 +197,6 @@ struct zfcp_ls_adisc {
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_GPN_FT 0x0172
-#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@@ -257,7 +243,6 @@ struct zfcp_ls_adisc {
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
-#define ZFCP_STATUS_PORT_DID_DID 0x00000002
#define ZFCP_STATUS_PORT_PHYS_CLOSING 0x00000004
#define ZFCP_STATUS_PORT_NO_WWPN 0x00000008
#define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020
@@ -339,8 +324,6 @@ struct ct_iu_gid_pn_resp {
* @wka_port: port where the request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @timeout: FSF timeout for this request
@@ -351,8 +334,6 @@ struct zfcp_send_ct {
struct zfcp_wka_port *wka_port;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
int timeout;
@@ -377,8 +358,6 @@ struct zfcp_gid_pn_data {
* @d_id: destiniation id of port where request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @completion: completion for synchronization purposes
@@ -391,8 +370,6 @@ struct zfcp_send_els {
u32 d_id;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
struct completion *completion;
@@ -450,7 +427,6 @@ struct zfcp_latencies {
};
struct zfcp_adapter {
- struct list_head list; /* list of adapters */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
@@ -515,6 +491,7 @@ struct zfcp_adapter {
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
struct work_struct scan_work;
+ struct service_level service_level;
atomic_t qdio_outb_full; /* queue full incidents */
};
@@ -591,16 +568,11 @@ struct zfcp_fsf_req {
struct zfcp_data {
struct scsi_host_template scsi_host_template;
struct scsi_transport_template *scsi_transport_template;
- struct list_head adapter_list_head; /* head of adapter list */
rwlock_t config_lock; /* serialises changes
to adapter/port/unit
lists */
struct semaphore config_sema; /* serialises configuration
changes */
- atomic_t loglevel; /* current loglevel */
- char init_busid[20];
- u64 init_wwpn;
- u64 init_fcp_lun;
struct kmem_cache *fsf_req_qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
@@ -621,8 +593,6 @@ struct zfcp_fsf_req_qtcb {
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
-#define zfcp_get_busid_by_adapter(adapter) (dev_name(&adapter->ccw_device->dev))
-
/*
* Helper functions for request ID management.
*/
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index c557ba34e1aa..387a3af528ac 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#define ZFCP_MAX_ERPS 3
@@ -837,7 +840,6 @@ static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
return ZFCP_ERP_FAILED;
}
port->d_id = adapter->peer_d_id;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
return zfcp_erp_port_strategy_open_port(act);
}
@@ -868,12 +870,12 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
case ZFCP_ERP_STEP_PORT_CLOSING:
if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
return zfcp_erp_open_ptp_port(act);
- if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ if (!port->d_id) {
queue_work(zfcp_data.work_queue, &port->gid_pn_work);
return ZFCP_ERP_CONTINUES;
}
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
- if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+ if (!port->d_id) {
if (p_status & (ZFCP_STATUS_PORT_INVALID_WWPN)) {
zfcp_erp_port_failed(port, 26, NULL);
return ZFCP_ERP_EXIT;
@@ -885,7 +887,7 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
case ZFCP_ERP_STEP_PORT_OPENING:
/* D_ID might have changed during open */
if (p_status & ZFCP_STATUS_COMMON_OPEN) {
- if (p_status & ZFCP_STATUS_PORT_DID_DID)
+ if (port->d_id)
return ZFCP_ERP_SUCCEEDED;
else {
act->step = ZFCP_ERP_STEP_PORT_CLOSING;
@@ -1281,10 +1283,13 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (result != ZFCP_ERP_SUCCEEDED)
+ if (result != ZFCP_ERP_SUCCEEDED) {
+ unregister_service_level(&adapter->service_level);
zfcp_erp_rports_del(adapter);
- else
+ } else {
+ register_service_level(&adapter->service_level);
schedule_work(&adapter->scan_work);
+ }
zfcp_adapter_put(adapter);
break;
}
@@ -1379,6 +1384,7 @@ static int zfcp_erp_thread(void *data)
struct list_head *next;
struct zfcp_erp_action *act;
unsigned long flags;
+ int ignore;
daemonize("zfcperp%s", dev_name(&adapter->ccw_device->dev));
/* Block all signals */
@@ -1401,7 +1407,7 @@ static int zfcp_erp_thread(void *data)
}
zfcp_rec_dbf_event_thread_lock(4, adapter);
- down_interruptible(&adapter->erp_ready_sem);
+ ignore = down_interruptible(&adapter->erp_ready_sem);
zfcp_rec_dbf_event_thread_lock(5, adapter);
}
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 8aab3091a7b1..eabdfe24456e 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -6,8 +6,25 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
+enum rscn_address_format {
+ RSCN_PORT_ADDRESS = 0x0,
+ RSCN_AREA_ADDRESS = 0x1,
+ RSCN_DOMAIN_ADDRESS = 0x2,
+ RSCN_FABRIC_ADDRESS = 0x3,
+};
+
+static u32 rscn_range_mask[] = {
+ [RSCN_PORT_ADDRESS] = 0xFFFFFF,
+ [RSCN_AREA_ADDRESS] = 0xFFFF00,
+ [RSCN_DOMAIN_ADDRESS] = 0xFF0000,
+ [RSCN_FABRIC_ADDRESS] = 0x000000,
+};
+
struct ct_iu_gpn_ft_req {
struct ct_hdr header;
u8 flags;
@@ -23,9 +40,12 @@ struct gpn_ft_resp_acc {
u64 wwpn;
} __attribute__ ((packed));
-#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
- / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr))
+#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \
+ / sizeof(struct gpn_ft_resp_acc))
#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
+ - sizeof(struct ct_hdr))
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
struct ct_iu_gpn_ft_resp {
@@ -157,22 +177,7 @@ static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
for (i = 1; i < no_entries; i++) {
/* skip head and start with 1st element */
fcp_rscn_element++;
- switch (fcp_rscn_element->addr_format) {
- case ZFCP_PORT_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_PORT;
- break;
- case ZFCP_AREA_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_AREA;
- break;
- case ZFCP_DOMAIN_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_DOMAIN;
- break;
- case ZFCP_FABRIC_ADDRESS:
- range_mask = ZFCP_PORTS_RANGE_FABRIC;
- break;
- default:
- continue;
- }
+ range_mask = rscn_range_mask[fcp_rscn_element->addr_format];
_zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
}
schedule_work(&fsf_req->adapter->scan_work);
@@ -263,7 +268,6 @@ static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
return;
/* looks like a valid d_id */
port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
- atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
}
int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
@@ -281,8 +285,6 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
- gid_pn->ct.req_count = 1;
- gid_pn->ct.resp_count = 1;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
sizeof(struct ct_iu_gid_pn_req));
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
@@ -294,7 +296,7 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
- gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+ gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
init_completion(&compl_rec.done);
@@ -404,8 +406,6 @@ static int zfcp_fc_adisc(struct zfcp_port *port)
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
sizeof(struct zfcp_ls_adisc));
- adisc->els.req_count = 1;
- adisc->els.resp_count = 1;
adisc->els.adapter = adapter;
adisc->els.port = port;
adisc->els.d_id = port->d_id;
@@ -445,17 +445,17 @@ void zfcp_test_link(struct zfcp_port *port)
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
}
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
kfree(sg_virt(sg)); /* free request buffer */
- zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+ zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
}
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
struct zfcp_gpn_ft *gpn_ft;
struct ct_iu_gpn_ft_req *req;
@@ -472,8 +472,8 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
- if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
- zfcp_free_sg_env(gpn_ft);
+ if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) {
+ zfcp_free_sg_env(gpn_ft, buf_num);
gpn_ft = NULL;
}
out:
@@ -482,7 +482,8 @@ out:
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
- struct zfcp_adapter *adapter)
+ struct zfcp_adapter *adapter,
+ int max_bytes)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
@@ -495,8 +496,7 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
req->header.options = ZFCP_CT_SYNCHRONOUS;
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
- req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
- (ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+ req->header.max_res_size = max_bytes / 4;
req->flags = 0;
req->domain_id_scope = 0;
req->area_id_scope = 0;
@@ -509,8 +509,6 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
- ct->req_count = 1;
- ct->resp_count = ZFCP_GPN_FT_BUFFERS;
init_completion(&compl_rec.done);
compl_rec.handler = NULL;
@@ -537,7 +535,7 @@ static void zfcp_validate_port(struct zfcp_port *port)
zfcp_port_dequeue(port);
}
-static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
@@ -557,13 +555,17 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
return -EIO;
}
- if (hdr->max_res_size)
+ if (hdr->max_res_size) {
+ dev_warn(&adapter->ccw_device->dev,
+ "The name server reported %d words residual data\n",
+ hdr->max_res_size);
return -E2BIG;
+ }
down(&zfcp_data.config_sema);
/* first entry is the header */
- for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES && !last; x++) {
+ for (x = 1; x < max_entries && !last; x++) {
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
acc++;
else
@@ -586,7 +588,6 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
}
port = zfcp_port_enqueue(adapter, acc->wwpn,
- ZFCP_STATUS_PORT_DID_DID |
ZFCP_STATUS_COMMON_NOESC, d_id);
if (IS_ERR(port))
ret = PTR_ERR(port);
@@ -609,6 +610,12 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
+ int chain, max_entries, buf_num, max_bytes;
+
+ chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
+ buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
+ max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
+ max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
return 0;
@@ -617,23 +624,23 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
if (ret)
return ret;
- gpn_ft = zfcp_alloc_sg_env();
+ gpn_ft = zfcp_alloc_sg_env(buf_num);
if (!gpn_ft) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < 3; i++) {
- ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+ ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+ ret = zfcp_scan_eval_gpn_ft(gpn_ft, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
- zfcp_free_sg_env(gpn_ft);
+ zfcp_free_sg_env(gpn_ft, buf_num);
out:
zfcp_wka_port_put(&adapter->nsp);
return ret;
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index dc0367690405..e6416f8541b0 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/blktrace_api.h>
#include "zfcp_ext.h"
@@ -641,38 +644,38 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
}
}
-static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter)
+static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
{
- struct zfcp_qdio_queue *req_q = &adapter->req_q;
-
- spin_lock_bh(&adapter->req_q_lock);
- if (atomic_read(&req_q->count))
+ if (atomic_read(&adapter->req_q.count) > 0)
return 1;
- spin_unlock_bh(&adapter->req_q_lock);
+ atomic_inc(&adapter->qdio_outb_full);
return 0;
}
-static int zfcp_fsf_sbal_available(struct zfcp_adapter *adapter)
-{
- unsigned int count = atomic_read(&adapter->req_q.count);
- if (!count)
- atomic_inc(&adapter->qdio_outb_full);
- return count > 0;
-}
-
static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+ __releases(&adapter->req_q_lock)
+ __acquires(&adapter->req_q_lock)
{
+ struct zfcp_qdio_queue *req_q = &adapter->req_q;
long ret;
+ if (atomic_read(&req_q->count) <= -REQUEST_LIST_SIZE)
+ return -EIO;
+ if (atomic_read(&req_q->count) > 0)
+ return 0;
+
+ atomic_dec(&req_q->count);
spin_unlock_bh(&adapter->req_q_lock);
ret = wait_event_interruptible_timeout(adapter->request_wq,
- zfcp_fsf_sbal_check(adapter), 5 * HZ);
+ atomic_read(&req_q->count) >= 0,
+ 5 * HZ);
+ spin_lock_bh(&adapter->req_q_lock);
+ atomic_inc(&req_q->count);
+
if (ret > 0)
return 0;
if (!ret)
atomic_inc(&adapter->qdio_outb_full);
-
- spin_lock_bh(&adapter->req_q_lock);
return -EIO;
}
@@ -1010,12 +1013,29 @@ skip_fsfstatus:
send_ct->handler(send_ct->handler_data);
}
-static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
- struct scatterlist *sg_req,
- struct scatterlist *sg_resp, int max_sbals)
+static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
+ struct scatterlist *sg_req,
+ struct scatterlist *sg_resp,
+ int max_sbals)
{
+ struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(req);
+ u32 feat = req->adapter->adapter_features;
int bytes;
+ if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
+ if (sg_req->length > PAGE_SIZE || sg_resp->length > PAGE_SIZE ||
+ !sg_is_last(sg_req) || !sg_is_last(sg_resp))
+ return -EOPNOTSUPP;
+
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = sg_virt(sg_req);
+ sbale[2].length = sg_req->length;
+ sbale[3].addr = sg_virt(sg_resp);
+ sbale[3].length = sg_resp->length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ return 0;
+ }
+
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
@@ -1057,8 +1077,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
- FSF_MAX_SBALS_PER_REQ);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp,
+ FSF_MAX_SBALS_PER_REQ);
if (ret)
goto failed_send;
@@ -1168,7 +1188,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, els->req, els->resp, 2);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2);
if (ret)
goto failed_send;
@@ -1403,13 +1423,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_NO_RETRY_POSSIBLE:
- dev_warn(&req->adapter->ccw_device->dev,
- "Remote port 0x%016Lx could not be opened\n",
- (unsigned long long)port->wwpn);
- zfcp_erp_port_failed(port, 32, req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
@@ -1437,10 +1451,10 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
- if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+ if (req->qtcb->bottom.support.els1_length >=
+ FSF_PLOGI_MIN_LEN) {
if (plogi->serv_param.wwpn != port->wwpn)
- atomic_clear_mask(ZFCP_STATUS_PORT_DID_DID,
- &port->status);
+ port->d_id = 0;
else {
port->wwnn = plogi->serv_param.wwnn;
zfcp_fc_plogi_evaluate(port, plogi);
@@ -1904,7 +1918,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
dev_err(&adapter->ccw_device->dev,
"Shared read-write access not "
"supported (unit 0x%016Lx, port "
- "0x%016Lx\n)",
+ "0x%016Lx)\n",
(unsigned long long)unit->fcp_lun,
(unsigned long long)unit->port->wwpn);
zfcp_erp_unit_failed(unit, 36, req);
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index fa2a31780611..8bb200252347 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -164,6 +164,7 @@
#define FSF_FEATURE_LUN_SHARING 0x00000004
#define FSF_FEATURE_NOTIFICATION_LOST 0x00000008
#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
#define FSF_FEATURE_UPDATE_ALERT 0x00000100
#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200
@@ -322,6 +323,7 @@ struct fsf_nport_serv_param {
u8 vendor_version_level[16];
} __attribute__ ((packed));
+#define FSF_PLOGI_MIN_LEN 112
struct fsf_plogi {
u32 code;
struct fsf_nport_serv_param serv_param;
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 664752f90b20..33e0a206a0a4 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
@@ -109,7 +112,7 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
* corruption and must stop the machine immediatly.
*/
panic("error: unknown request id (%lx) on adapter %s.\n",
- req_id, zfcp_get_busid_by_adapter(adapter));
+ req_id, dev_name(&adapter->ccw_device->dev));
zfcp_reqlist_remove(adapter, fsf_req);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
@@ -389,7 +392,7 @@ int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
init_data->cdev = adapter->ccw_device;
init_data->q_format = QDIO_ZFCP_QFMT;
- memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
+ memcpy(init_data->adapter_name, dev_name(&adapter->ccw_device->dev), 8);
ASCEBC(init_data->adapter_name, 8);
init_data->qib_param_field_format = 0;
init_data->qib_param_field = NULL;
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 468c880f8b6d..9dc42a68fbdd 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#include <asm/atomic.h>
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index ca9293ba1766..899af2b45b1e 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index c3e4ab07b9cc..0eea90781385 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -1,17 +1,21 @@
/*
* drivers/s390/sysinfo.c
*
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ * Copyright IBM Corp. 2001, 2008
+ * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <asm/ebcdic.h>
#include <asm/sysinfo.h>
+#include <asm/cpcmd.h>
/* Sigh, math-emu. Don't ask. */
#include <asm/sfp-util.h>
@@ -271,6 +275,125 @@ static __init int create_proc_sysinfo(void)
__initcall(create_proc_sysinfo);
+/*
+ * Service levels interface.
+ */
+
+static DECLARE_RWSEM(service_level_sem);
+static LIST_HEAD(service_level_list);
+
+int register_service_level(struct service_level *slr)
+{
+ struct service_level *ptr;
+
+ down_write(&service_level_sem);
+ list_for_each_entry(ptr, &service_level_list, list)
+ if (ptr == slr) {
+ up_write(&service_level_sem);
+ return -EEXIST;
+ }
+ list_add_tail(&slr->list, &service_level_list);
+ up_write(&service_level_sem);
+ return 0;
+}
+EXPORT_SYMBOL(register_service_level);
+
+int unregister_service_level(struct service_level *slr)
+{
+ struct service_level *ptr, *next;
+ int rc = -ENOENT;
+
+ down_write(&service_level_sem);
+ list_for_each_entry_safe(ptr, next, &service_level_list, list) {
+ if (ptr != slr)
+ continue;
+ list_del(&ptr->list);
+ rc = 0;
+ break;
+ }
+ up_write(&service_level_sem);
+ return rc;
+}
+EXPORT_SYMBOL(unregister_service_level);
+
+static void *service_level_start(struct seq_file *m, loff_t *pos)
+{
+ down_read(&service_level_sem);
+ return seq_list_start(&service_level_list, *pos);
+}
+
+static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ return seq_list_next(p, &service_level_list, pos);
+}
+
+static void service_level_stop(struct seq_file *m, void *p)
+{
+ up_read(&service_level_sem);
+}
+
+static int service_level_show(struct seq_file *m, void *p)
+{
+ struct service_level *slr;
+
+ slr = list_entry(p, struct service_level, list);
+ slr->seq_print(m, slr);
+ return 0;
+}
+
+static const struct seq_operations service_level_seq_ops = {
+ .start = service_level_start,
+ .next = service_level_next,
+ .stop = service_level_stop,
+ .show = service_level_show
+};
+
+static int service_level_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &service_level_seq_ops);
+}
+
+static const struct file_operations service_level_ops = {
+ .open = service_level_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+static void service_level_vm_print(struct seq_file *m,
+ struct service_level *slr)
+{
+ char *query_buffer, *str;
+
+ query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
+ if (!query_buffer)
+ return;
+ cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
+ str = strchr(query_buffer, '\n');
+ if (str)
+ *str = 0;
+ seq_printf(m, "VM: %s\n", query_buffer);
+ kfree(query_buffer);
+}
+
+static struct service_level service_level_vm = {
+ .seq_print = service_level_vm_print
+};
+
+static __init int create_proc_service_level(void)
+{
+ proc_create("service_levels", 0, NULL, &service_level_ops);
+ if (MACHINE_IS_VM)
+ register_service_level(&service_level_vm);
+ return 0;
+}
+
+subsys_initcall(create_proc_service_level);
+
+/*
+ * Bogomips calculation based on cpu capability.
+ */
+
int get_cpu_capability(unsigned int *capability)
{
struct sysinfo_1_2_2 *info;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 403ecad48d4b..152d4aa9354f 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -352,6 +352,8 @@ config ISCSI_TCP
http://open-iscsi.org
+source "drivers/scsi/cxgb3i/Kconfig"
+
config SGIWD93_SCSI
tristate "SGI WD93C93 SCSI Driver"
depends on SGI_HAS_WD93 && SCSI
@@ -603,6 +605,19 @@ config SCSI_FLASHPOINT
substantial, so users of MultiMaster Host Adapters may not
wish to include it.
+config LIBFC
+ tristate "LibFC module"
+ select SCSI_FC_ATTRS
+ ---help---
+ Fibre Channel library module
+
+config FCOE
+ tristate "FCoE module"
+ depends on PCI
+ select LIBFC
+ ---help---
+ Fibre Channel over Ethernet module
+
config SCSI_DMX3191D
tristate "DMX3191D SCSI support"
depends on PCI && SCSI
@@ -1357,6 +1372,13 @@ config SCSI_LPFC
This lpfc driver supports the Emulex LightPulse
Family of Fibre Channel PCI host adapters.
+config SCSI_LPFC_DEBUG_FS
+ bool "Emulex LightPulse Fibre Channel debugfs Support"
+ depends on SCSI_LPFC && DEBUG_FS
+ help
+ This makes debugging infomation from the lpfc driver
+ available via the debugfs filesystem.
+
config SCSI_SIM710
tristate "Simple 53c710 SCSI support (Compaq, NCR machines)"
depends on (EISA || MCA) && SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 72fd5043cfa1..1410697257cb 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -36,7 +36,9 @@ obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o
obj-$(CONFIG_SCSI_DH) += device_handler/
-obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
+obj-$(CONFIG_LIBFC) += libfc/
+obj-$(CONFIG_FCOE) += fcoe/
+obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o
obj-$(CONFIG_SCSI_ZORRO7XX) += 53c700.o zorro7xx.o
@@ -124,6 +126,7 @@ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o
obj-$(CONFIG_SCSI_STEX) += stex.o
obj-$(CONFIG_SCSI_MVSAS) += mvsas.o
obj-$(CONFIG_PS3_ROM) += ps3rom.o
+obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index eeddbd19eba5..f92da9fd5f20 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -30,7 +30,7 @@
* $Log: NCR5380.c,v $
* Revision 1.10 1998/9/2 Alan Cox
- * (alan@redhat.com)
+ * (alan@lxorguk.ukuu.org.uk)
* Fixed up the timer lockups reported so far. Things still suck. Looking
* forward to 2.3 and per device request queues. Then it'll be possible to
* SMP thread this beast and improve life no end.
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
index 84bb61628372..3c298c7253ee 100644
--- a/drivers/scsi/a100u2w.c
+++ b/drivers/scsi/a100u2w.c
@@ -54,7 +54,7 @@
* 9/28/04 Christoph Hellwig <hch@lst.de>
* - merge the two source files
* - remove internal queueing code
- * 14/06/07 Alan Cox <alan@redhat.com>
+ * 14/06/07 Alan Cox <alan@lxorguk.ukuu.org.uk>
* - Grand cleanup and Linuxisation
*/
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 8abfd06b5a72..90d1d0878cb8 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index a7355260cfcf..0391d759dfdb 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
@@ -90,14 +90,24 @@ static int ioctl_send_fib(struct aac_dev * dev, void __user *arg)
if (size < le16_to_cpu(kfib->header.SenderSize))
size = le16_to_cpu(kfib->header.SenderSize);
if (size > dev->max_fib_size) {
+ dma_addr_t daddr;
+
if (size > 2048) {
retval = -EINVAL;
goto cleanup;
}
+
+ kfib = pci_alloc_consistent(dev->pdev, size, &daddr);
+ if (!kfib) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
/* Highjack the hw_fib */
hw_fib = fibptr->hw_fib_va;
hw_fib_pa = fibptr->hw_fib_pa;
- fibptr->hw_fib_va = kfib = pci_alloc_consistent(dev->pdev, size, &fibptr->hw_fib_pa);
+ fibptr->hw_fib_va = kfib;
+ fibptr->hw_fib_pa = daddr;
memset(((char *)kfib) + dev->max_fib_size, 0, size - dev->max_fib_size);
memcpy(kfib, hw_fib, dev->max_fib_size);
}
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index cbac06355107..16310443b55a 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 289304aab690..d24c2670040b 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
index 933f208eedba..abc9ef5d1b10 100644
--- a/drivers/scsi/aacraid/dpcsup.c
+++ b/drivers/scsi/aacraid/dpcsup.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 162cd927d94b..36d8aab97efe 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
@@ -175,8 +175,8 @@ static struct aac_driver_ident aac_drivers[] = {
{ aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* PERC 3/Di (Boxster/PERC3DiB) */
{ aac_rx_init, "aacraid", "ADAPTEC ", "catapult ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* catapult */
{ aac_rx_init, "aacraid", "ADAPTEC ", "tomcat ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* tomcat */
- { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Adaptec 2120S (Crusader) */
- { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Adaptec 2200S (Vulcan) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec 2120S (Crusader) */
+ { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Adaptec 2200S (Vulcan) */
{ aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Adaptec 2200S (Vulcan-2m) */
{ aac_rx_init, "aacraid", "Legend ", "Legend S220 ", 1, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Legend S220 (Legend Crusader) */
{ aac_rx_init, "aacraid", "Legend ", "Legend S230 ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Legend S230 (Legend Vulcan) */
diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c
index 8cd6588a83e3..16d8db550027 100644
--- a/drivers/scsi/aacraid/rkt.c
+++ b/drivers/scsi/aacraid/rkt.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index 073208b0f622..f70d9f8e79e5 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
index fc1a55796a89..b6a3c5c187b6 100644
--- a/drivers/scsi/aacraid/sa.c
+++ b/drivers/scsi/aacraid/sa.c
@@ -1,6 +1,6 @@
/*
* Adaptec AAC series RAID controller driver
- * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com>
+ * (c) Copyright 2001 Red Hat Inc.
*
* based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux.
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 399fe559e4de..2f602720193e 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -13425,8 +13425,7 @@ static int __devinit advansys_board_found(struct Scsi_Host *shost,
}
boardp->asc_n_io_port = pci_resource_len(pdev, 1);
- boardp->ioremap_addr = ioremap(pci_resource_start(pdev, 1),
- boardp->asc_n_io_port);
+ boardp->ioremap_addr = pci_ioremap_bar(pdev, 1);
if (!boardp->ioremap_addr) {
shost_printk(KERN_ERR, shost, "ioremap(%lx, %d) "
"returned NULL\n",
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 7c45d88a205b..ed0e3e55652a 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -22,7 +22,7 @@
* aha1740_makecode may still need even more work
* if it doesn't work for your devices, take a look.
*
- * Reworked for new_eh and new locking by Alan Cox <alan@redhat.com>
+ * Reworked for new_eh and new locking by Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Converted to EISA and generic DMA APIs by Marc Zyngier
* <maz@wild-wind.fr.eu.org>, 4/2003.
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index f91f79c8007d..106c04d2d793 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -235,7 +235,7 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
uint32_t intmask_org;
int i, j;
- acb->pmuA = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ acb->pmuA = pci_ioremap_bar(pdev, 0);
if (!acb->pmuA) {
printk(KERN_NOTICE "arcmsr%d: memory mapping region fail \n",
acb->host->host_no);
@@ -329,13 +329,11 @@ static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
reg = (struct MessageUnit_B *)(dma_coherent +
ARCMSR_MAX_FREECCB_NUM * sizeof(struct CommandControlBlock));
acb->pmuB = reg;
- mem_base0 = ioremap(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ mem_base0 = pci_ioremap_bar(pdev, 0);
if (!mem_base0)
goto out;
- mem_base1 = ioremap(pci_resource_start(pdev, 2),
- pci_resource_len(pdev, 2));
+ mem_base1 = pci_ioremap_bar(pdev, 2);
if (!mem_base1) {
iounmap(mem_base0);
goto out;
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 7d311541c76c..20ca0a6374b5 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -1,8 +1,8 @@
/*
* Copyright (C) 1997 Wu Ching Chen
* 2.1.x update (C) 1998 Krzysztof G. Baranowski
- * 2.5.x update (C) 2002 Red Hat <alan@redhat.com>
- * 2.6.x update (C) 2004 Red Hat <alan@redhat.com>
+ * 2.5.x update (C) 2002 Red Hat
+ * 2.6.x update (C) 2004 Red Hat
*
* Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes
*
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 88ecf94ad979..af9725409f43 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -190,7 +190,7 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
result = scsi_execute_req(ch->device, cmd, direction, buffer,
buflength, &sshdr, timeout * HZ,
- MAX_RETRIES);
+ MAX_RETRIES, NULL);
dprintk("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
diff --git a/drivers/scsi/cxgb3i/Kbuild b/drivers/scsi/cxgb3i/Kbuild
new file mode 100644
index 000000000000..ee7d6d2f9c3b
--- /dev/null
+++ b/drivers/scsi/cxgb3i/Kbuild
@@ -0,0 +1,4 @@
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/cxgb3
+
+cxgb3i-y := cxgb3i_init.o cxgb3i_iscsi.o cxgb3i_pdu.o cxgb3i_offload.o
+obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i_ddp.o cxgb3i.o
diff --git a/drivers/scsi/cxgb3i/Kconfig b/drivers/scsi/cxgb3i/Kconfig
new file mode 100644
index 000000000000..bfdcaf5c9c57
--- /dev/null
+++ b/drivers/scsi/cxgb3i/Kconfig
@@ -0,0 +1,7 @@
+config SCSI_CXGB3_ISCSI
+ tristate "Chelsio S3xx iSCSI support"
+ depends on CHELSIO_T3_DEPENDS
+ select CHELSIO_T3
+ select SCSI_ISCSI_ATTRS
+ ---help---
+ This driver supports iSCSI offload for the Chelsio S3 series devices.
diff --git a/drivers/scsi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgb3i/cxgb3i.h
new file mode 100644
index 000000000000..fde6e4c634e7
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i.h
@@ -0,0 +1,139 @@
+/*
+ * cxgb3i.h: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_H__
+#define __CXGB3I_H__
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/scatterlist.h>
+#include <scsi/libiscsi_tcp.h>
+
+/* from cxgb3 LLD */
+#include "common.h"
+#include "t3_cpl.h"
+#include "t3cdev.h"
+#include "cxgb3_ctl_defs.h"
+#include "cxgb3_offload.h"
+#include "firmware_exports.h"
+
+#include "cxgb3i_offload.h"
+#include "cxgb3i_ddp.h"
+
+#define CXGB3I_SCSI_QDEPTH_DFLT 128
+#define CXGB3I_MAX_TARGET CXGB3I_MAX_CONN
+#define CXGB3I_MAX_LUN 512
+#define ISCSI_PDU_NONPAYLOAD_MAX \
+ (sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE + 2*ISCSI_DIGEST_SIZE)
+
+struct cxgb3i_adapter;
+struct cxgb3i_hba;
+struct cxgb3i_endpoint;
+
+/**
+ * struct cxgb3i_hba - cxgb3i iscsi structure (per port)
+ *
+ * @snic: cxgb3i adapter containing this port
+ * @ndev: pointer to netdev structure
+ * @shost: pointer to scsi host structure
+ */
+struct cxgb3i_hba {
+ struct cxgb3i_adapter *snic;
+ struct net_device *ndev;
+ struct Scsi_Host *shost;
+};
+
+/**
+ * struct cxgb3i_adapter - cxgb3i adapter structure (per pci)
+ *
+ * @listhead: list head to link elements
+ * @lock: lock for this structure
+ * @tdev: pointer to t3cdev used by cxgb3 driver
+ * @pdev: pointer to pci dev
+ * @hba_cnt: # of hbas (the same as # of ports)
+ * @hba: all the hbas on this adapter
+ * @tx_max_size: max. tx packet size supported
+ * @rx_max_size: max. rx packet size supported
+ * @tag_format: ddp tag format settings
+ */
+struct cxgb3i_adapter {
+ struct list_head list_head;
+ spinlock_t lock;
+ struct t3cdev *tdev;
+ struct pci_dev *pdev;
+ unsigned char hba_cnt;
+ struct cxgb3i_hba *hba[MAX_NPORTS];
+
+ unsigned int tx_max_size;
+ unsigned int rx_max_size;
+
+ struct cxgb3i_tag_format tag_format;
+};
+
+/**
+ * struct cxgb3i_conn - cxgb3i iscsi connection
+ *
+ * @listhead: list head to link elements
+ * @cep: pointer to iscsi_endpoint structure
+ * @conn: pointer to iscsi_conn structure
+ * @hba: pointer to the hba this conn. is going through
+ * @task_idx_bits: # of bits needed for session->cmds_max
+ */
+struct cxgb3i_conn {
+ struct list_head list_head;
+ struct cxgb3i_endpoint *cep;
+ struct iscsi_conn *conn;
+ struct cxgb3i_hba *hba;
+ unsigned int task_idx_bits;
+};
+
+/**
+ * struct cxgb3i_endpoint - iscsi tcp endpoint
+ *
+ * @c3cn: the h/w tcp connection representation
+ * @hba: pointer to the hba this conn. is going through
+ * @cconn: pointer to the associated cxgb3i iscsi connection
+ */
+struct cxgb3i_endpoint {
+ struct s3_conn *c3cn;
+ struct cxgb3i_hba *hba;
+ struct cxgb3i_conn *cconn;
+};
+
+int cxgb3i_iscsi_init(void);
+void cxgb3i_iscsi_cleanup(void);
+
+struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *);
+void cxgb3i_adapter_remove(struct t3cdev *);
+int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *);
+void cxgb3i_adapter_ulp_cleanup(struct cxgb3i_adapter *);
+
+struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *);
+struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *,
+ struct net_device *);
+void cxgb3i_hba_host_remove(struct cxgb3i_hba *);
+
+int cxgb3i_pdu_init(void);
+void cxgb3i_pdu_cleanup(void);
+void cxgb3i_conn_cleanup_task(struct iscsi_task *);
+int cxgb3i_conn_alloc_pdu(struct iscsi_task *, u8);
+int cxgb3i_conn_init_pdu(struct iscsi_task *, unsigned int, unsigned int);
+int cxgb3i_conn_xmit_pdu(struct iscsi_task *);
+
+void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt);
+int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt);
+
+#endif
diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
new file mode 100644
index 000000000000..1a41f04264f7
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c
@@ -0,0 +1,770 @@
+/*
+ * cxgb3i_ddp.c: Chelsio S3xx iSCSI DDP Manager.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/skbuff.h>
+
+/* from cxgb3 LLD */
+#include "common.h"
+#include "t3_cpl.h"
+#include "t3cdev.h"
+#include "cxgb3_ctl_defs.h"
+#include "cxgb3_offload.h"
+#include "firmware_exports.h"
+
+#include "cxgb3i_ddp.h"
+
+#define DRV_MODULE_NAME "cxgb3i_ddp"
+#define DRV_MODULE_VERSION "1.0.0"
+#define DRV_MODULE_RELDATE "Dec. 1, 2008"
+
+static char version[] =
+ "Chelsio S3xx iSCSI DDP " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Karen Xie <kxie@chelsio.com>");
+MODULE_DESCRIPTION("cxgb3i ddp pagepod manager");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+#define ddp_log_error(fmt...) printk(KERN_ERR "cxgb3i_ddp: ERR! " fmt)
+#define ddp_log_warn(fmt...) printk(KERN_WARNING "cxgb3i_ddp: WARN! " fmt)
+#define ddp_log_info(fmt...) printk(KERN_INFO "cxgb3i_ddp: " fmt)
+
+#ifdef __DEBUG_CXGB3I_DDP__
+#define ddp_log_debug(fmt, args...) \
+ printk(KERN_INFO "cxgb3i_ddp: %s - " fmt, __func__ , ## args)
+#else
+#define ddp_log_debug(fmt...)
+#endif
+
+/*
+ * iSCSI Direct Data Placement
+ *
+ * T3 h/w can directly place the iSCSI Data-In or Data-Out PDU's payload into
+ * pre-posted final destination host-memory buffers based on the Initiator
+ * Task Tag (ITT) in Data-In or Target Task Tag (TTT) in Data-Out PDUs.
+ *
+ * The host memory address is programmed into h/w in the format of pagepod
+ * entries.
+ * The location of the pagepod entry is encoded into ddp tag which is used or
+ * is the base for ITT/TTT.
+ */
+
+#define DDP_PGIDX_MAX 4
+#define DDP_THRESHOLD 2048
+static unsigned char ddp_page_order[DDP_PGIDX_MAX] = {0, 1, 2, 4};
+static unsigned char ddp_page_shift[DDP_PGIDX_MAX] = {12, 13, 14, 16};
+static unsigned char page_idx = DDP_PGIDX_MAX;
+
+static LIST_HEAD(cxgb3i_ddp_list);
+static DEFINE_RWLOCK(cxgb3i_ddp_rwlock);
+
+/*
+ * functions to program the pagepod in h/w
+ */
+static inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
+{
+ struct ulp_mem_io *req = (struct ulp_mem_io *)skb->head;
+
+ req->wr.wr_lo = 0;
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS));
+ req->cmd_lock_addr = htonl(V_ULP_MEMIO_ADDR(addr >> 5) |
+ V_ULPTX_CMD(ULP_MEM_WRITE));
+ req->len = htonl(V_ULP_MEMIO_DATA_LEN(PPOD_SIZE >> 5) |
+ V_ULPTX_NFLITS((PPOD_SIZE >> 3) + 1));
+}
+
+static int set_ddp_map(struct cxgb3i_ddp_info *ddp, struct pagepod_hdr *hdr,
+ unsigned int idx, unsigned int npods,
+ struct cxgb3i_gather_list *gl)
+{
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
+ int i;
+
+ for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ struct sk_buff *skb = ddp->gl_skb[idx];
+ struct pagepod *ppod;
+ int j, pidx;
+
+ /* hold on to the skb until we clear the ddp mapping */
+ skb_get(skb);
+
+ ulp_mem_io_set_hdr(skb, pm_addr);
+ ppod = (struct pagepod *)
+ (skb->head + sizeof(struct ulp_mem_io));
+ memcpy(&(ppod->hdr), hdr, sizeof(struct pagepod));
+ for (pidx = 4 * i, j = 0; j < 5; ++j, ++pidx)
+ ppod->addr[j] = pidx < gl->nelem ?
+ cpu_to_be64(gl->phys_addr[pidx]) : 0UL;
+
+ skb->priority = CPL_PRIORITY_CONTROL;
+ cxgb3_ofld_send(ddp->tdev, skb);
+ }
+ return 0;
+}
+
+static int clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int idx,
+ unsigned int npods)
+{
+ unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
+ int i;
+
+ for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+ struct sk_buff *skb = ddp->gl_skb[idx];
+
+ ddp->gl_skb[idx] = NULL;
+ memset((skb->head + sizeof(struct ulp_mem_io)), 0, PPOD_SIZE);
+ ulp_mem_io_set_hdr(skb, pm_addr);
+ skb->priority = CPL_PRIORITY_CONTROL;
+ cxgb3_ofld_send(ddp->tdev, skb);
+ }
+ return 0;
+}
+
+static inline int ddp_find_unused_entries(struct cxgb3i_ddp_info *ddp,
+ int start, int max, int count,
+ struct cxgb3i_gather_list *gl)
+{
+ unsigned int i, j;
+
+ spin_lock(&ddp->map_lock);
+ for (i = start; i <= max;) {
+ for (j = 0; j < count; j++) {
+ if (ddp->gl_map[i + j])
+ break;
+ }
+ if (j == count) {
+ for (j = 0; j < count; j++)
+ ddp->gl_map[i + j] = gl;
+ spin_unlock(&ddp->map_lock);
+ return i;
+ }
+ i += j + 1;
+ }
+ spin_unlock(&ddp->map_lock);
+ return -EBUSY;
+}
+
+static inline void ddp_unmark_entries(struct cxgb3i_ddp_info *ddp,
+ int start, int count)
+{
+ spin_lock(&ddp->map_lock);
+ memset(&ddp->gl_map[start], 0,
+ count * sizeof(struct cxgb3i_gather_list *));
+ spin_unlock(&ddp->map_lock);
+}
+
+static inline void ddp_free_gl_skb(struct cxgb3i_ddp_info *ddp,
+ int idx, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++, idx++)
+ if (ddp->gl_skb[idx]) {
+ kfree_skb(ddp->gl_skb[idx]);
+ ddp->gl_skb[idx] = NULL;
+ }
+}
+
+static inline int ddp_alloc_gl_skb(struct cxgb3i_ddp_info *ddp, int idx,
+ int count, gfp_t gfp)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct sk_buff *skb = alloc_skb(sizeof(struct ulp_mem_io) +
+ PPOD_SIZE, gfp);
+ if (skb) {
+ ddp->gl_skb[idx + i] = skb;
+ skb_put(skb, sizeof(struct ulp_mem_io) + PPOD_SIZE);
+ } else {
+ ddp_free_gl_skb(ddp, idx, i);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/**
+ * cxgb3i_ddp_find_page_index - return ddp page index for a given page size.
+ * @pgsz: page size
+ * return the ddp page index, if no match is found return DDP_PGIDX_MAX.
+ */
+int cxgb3i_ddp_find_page_index(unsigned long pgsz)
+{
+ int i;
+
+ for (i = 0; i < DDP_PGIDX_MAX; i++) {
+ if (pgsz == (1UL << ddp_page_shift[i]))
+ return i;
+ }
+ ddp_log_debug("ddp page size 0x%lx not supported.\n", pgsz);
+ return DDP_PGIDX_MAX;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_find_page_index);
+
+static inline void ddp_gl_unmap(struct pci_dev *pdev,
+ struct cxgb3i_gather_list *gl)
+{
+ int i;
+
+ for (i = 0; i < gl->nelem; i++)
+ pci_unmap_page(pdev, gl->phys_addr[i], PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+}
+
+static inline int ddp_gl_map(struct pci_dev *pdev,
+ struct cxgb3i_gather_list *gl)
+{
+ int i;
+
+ for (i = 0; i < gl->nelem; i++) {
+ gl->phys_addr[i] = pci_map_page(pdev, gl->pages[i], 0,
+ PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(pci_dma_mapping_error(pdev, gl->phys_addr[i])))
+ goto unmap;
+ }
+
+ return i;
+
+unmap:
+ if (i) {
+ unsigned int nelem = gl->nelem;
+
+ gl->nelem = i;
+ ddp_gl_unmap(pdev, gl);
+ gl->nelem = nelem;
+ }
+ return -ENOMEM;
+}
+
+/**
+ * cxgb3i_ddp_make_gl - build ddp page buffer list
+ * @xferlen: total buffer length
+ * @sgl: page buffer scatter-gather list
+ * @sgcnt: # of page buffers
+ * @pdev: pci_dev, used for pci map
+ * @gfp: allocation mode
+ *
+ * construct a ddp page buffer list from the scsi scattergather list.
+ * coalesce buffers as much as possible, and obtain dma addresses for
+ * each page.
+ *
+ * Return the cxgb3i_gather_list constructed from the page buffers if the
+ * memory can be used for ddp. Return NULL otherwise.
+ */
+struct cxgb3i_gather_list *cxgb3i_ddp_make_gl(unsigned int xferlen,
+ struct scatterlist *sgl,
+ unsigned int sgcnt,
+ struct pci_dev *pdev,
+ gfp_t gfp)
+{
+ struct cxgb3i_gather_list *gl;
+ struct scatterlist *sg = sgl;
+ struct page *sgpage = sg_page(sg);
+ unsigned int sglen = sg->length;
+ unsigned int sgoffset = sg->offset;
+ unsigned int npages = (xferlen + sgoffset + PAGE_SIZE - 1) >>
+ PAGE_SHIFT;
+ int i = 1, j = 0;
+
+ if (xferlen < DDP_THRESHOLD) {
+ ddp_log_debug("xfer %u < threshold %u, no ddp.\n",
+ xferlen, DDP_THRESHOLD);
+ return NULL;
+ }
+
+ gl = kzalloc(sizeof(struct cxgb3i_gather_list) +
+ npages * (sizeof(dma_addr_t) + sizeof(struct page *)),
+ gfp);
+ if (!gl)
+ return NULL;
+
+ gl->pages = (struct page **)&gl->phys_addr[npages];
+ gl->length = xferlen;
+ gl->offset = sgoffset;
+ gl->pages[0] = sgpage;
+
+ sg = sg_next(sg);
+ while (sg) {
+ struct page *page = sg_page(sg);
+
+ if (sgpage == page && sg->offset == sgoffset + sglen)
+ sglen += sg->length;
+ else {
+ /* make sure the sgl is fit for ddp:
+ * each has the same page size, and
+ * all of the middle pages are used completely
+ */
+ if ((j && sgoffset) ||
+ ((i != sgcnt - 1) &&
+ ((sglen + sgoffset) & ~PAGE_MASK)))
+ goto error_out;
+
+ j++;
+ if (j == gl->nelem || sg->offset)
+ goto error_out;
+ gl->pages[j] = page;
+ sglen = sg->length;
+ sgoffset = sg->offset;
+ sgpage = page;
+ }
+ i++;
+ sg = sg_next(sg);
+ }
+ gl->nelem = ++j;
+
+ if (ddp_gl_map(pdev, gl) < 0)
+ goto error_out;
+
+ return gl;
+
+error_out:
+ kfree(gl);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_make_gl);
+
+/**
+ * cxgb3i_ddp_release_gl - release a page buffer list
+ * @gl: a ddp page buffer list
+ * @pdev: pci_dev used for pci_unmap
+ * free a ddp page buffer list resulted from cxgb3i_ddp_make_gl().
+ */
+void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl,
+ struct pci_dev *pdev)
+{
+ ddp_gl_unmap(pdev, gl);
+ kfree(gl);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_release_gl);
+
+/**
+ * cxgb3i_ddp_tag_reserve - set up ddp for a data transfer
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @tformat: tag format
+ * @tagp: the s/w tag, if ddp setup is successful, it will be updated with
+ * ddp/hw tag
+ * @gl: the page momory list
+ * @gfp: allocation mode
+ *
+ * ddp setup for a given page buffer list and construct the ddp tag.
+ * return 0 if success, < 0 otherwise.
+ */
+int cxgb3i_ddp_tag_reserve(struct t3cdev *tdev, unsigned int tid,
+ struct cxgb3i_tag_format *tformat, u32 *tagp,
+ struct cxgb3i_gather_list *gl, gfp_t gfp)
+{
+ struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi;
+ struct pagepod_hdr hdr;
+ unsigned int npods;
+ int idx = -1, idx_max;
+ int err = -ENOMEM;
+ u32 sw_tag = *tagp;
+ u32 tag;
+
+ if (page_idx >= DDP_PGIDX_MAX || !ddp || !gl || !gl->nelem ||
+ gl->length < DDP_THRESHOLD) {
+ ddp_log_debug("pgidx %u, xfer %u/%u, NO ddp.\n",
+ page_idx, gl->length, DDP_THRESHOLD);
+ return -EINVAL;
+ }
+
+ npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
+ idx_max = ddp->nppods - npods + 1;
+
+ if (ddp->idx_last == ddp->nppods)
+ idx = ddp_find_unused_entries(ddp, 0, idx_max, npods, gl);
+ else {
+ idx = ddp_find_unused_entries(ddp, ddp->idx_last + 1,
+ idx_max, npods, gl);
+ if (idx < 0 && ddp->idx_last >= npods)
+ idx = ddp_find_unused_entries(ddp, 0,
+ ddp->idx_last - npods + 1,
+ npods, gl);
+ }
+ if (idx < 0) {
+ ddp_log_debug("xferlen %u, gl %u, npods %u NO DDP.\n",
+ gl->length, gl->nelem, npods);
+ return idx;
+ }
+
+ err = ddp_alloc_gl_skb(ddp, idx, npods, gfp);
+ if (err < 0)
+ goto unmark_entries;
+
+ tag = cxgb3i_ddp_tag_base(tformat, sw_tag);
+ tag |= idx << PPOD_IDX_SHIFT;
+
+ hdr.rsvd = 0;
+ hdr.vld_tid = htonl(F_PPOD_VALID | V_PPOD_TID(tid));
+ hdr.pgsz_tag_clr = htonl(tag & ddp->rsvd_tag_mask);
+ hdr.maxoffset = htonl(gl->length);
+ hdr.pgoffset = htonl(gl->offset);
+
+ err = set_ddp_map(ddp, &hdr, idx, npods, gl);
+ if (err < 0)
+ goto free_gl_skb;
+
+ ddp->idx_last = idx;
+ ddp_log_debug("xfer %u, gl %u,%u, tid 0x%x, 0x%x -> 0x%x(%u,%u).\n",
+ gl->length, gl->nelem, gl->offset, tid, sw_tag, tag,
+ idx, npods);
+ *tagp = tag;
+ return 0;
+
+free_gl_skb:
+ ddp_free_gl_skb(ddp, idx, npods);
+unmark_entries:
+ ddp_unmark_entries(ddp, idx, npods);
+ return err;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_reserve);
+
+/**
+ * cxgb3i_ddp_tag_release - release a ddp tag
+ * @tdev: t3cdev adapter
+ * @tag: ddp tag
+ * ddp cleanup for a given ddp tag and release all the resources held
+ */
+void cxgb3i_ddp_tag_release(struct t3cdev *tdev, u32 tag)
+{
+ struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi;
+ u32 idx;
+
+ if (!ddp) {
+ ddp_log_error("release ddp tag 0x%x, ddp NULL.\n", tag);
+ return;
+ }
+
+ idx = (tag >> PPOD_IDX_SHIFT) & ddp->idx_mask;
+ if (idx < ddp->nppods) {
+ struct cxgb3i_gather_list *gl = ddp->gl_map[idx];
+ unsigned int npods;
+
+ if (!gl) {
+ ddp_log_error("release ddp 0x%x, idx 0x%x, gl NULL.\n",
+ tag, idx);
+ return;
+ }
+ npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
+ ddp_log_debug("ddp tag 0x%x, release idx 0x%x, npods %u.\n",
+ tag, idx, npods);
+ clear_ddp_map(ddp, idx, npods);
+ ddp_unmark_entries(ddp, idx, npods);
+ cxgb3i_ddp_release_gl(gl, ddp->pdev);
+ } else
+ ddp_log_error("ddp tag 0x%x, idx 0x%x > max 0x%x.\n",
+ tag, idx, ddp->nppods);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_release);
+
+static int setup_conn_pgidx(struct t3cdev *tdev, unsigned int tid, int pg_idx,
+ int reply)
+{
+ struct sk_buff *skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
+ GFP_KERNEL);
+ struct cpl_set_tcb_field *req;
+ u64 val = pg_idx < DDP_PGIDX_MAX ? pg_idx : 0;
+
+ if (!skb)
+ return -ENOMEM;
+
+ /* set up ulp submode and page size */
+ req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
+ req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->cpu_idx = 0;
+ req->word = htons(31);
+ req->mask = cpu_to_be64(0xF0000000);
+ req->val = cpu_to_be64(val << 28);
+ skb->priority = CPL_PRIORITY_CONTROL;
+
+ cxgb3_ofld_send(tdev, skb);
+ return 0;
+}
+
+/**
+ * cxgb3i_setup_conn_host_pagesize - setup the conn.'s ddp page size
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @reply: request reply from h/w
+ * set up the ddp page size based on the host PAGE_SIZE for a connection
+ * identified by tid
+ */
+int cxgb3i_setup_conn_host_pagesize(struct t3cdev *tdev, unsigned int tid,
+ int reply)
+{
+ return setup_conn_pgidx(tdev, tid, page_idx, reply);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_host_pagesize);
+
+/**
+ * cxgb3i_setup_conn_pagesize - setup the conn.'s ddp page size
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @reply: request reply from h/w
+ * @pgsz: ddp page size
+ * set up the ddp page size for a connection identified by tid
+ */
+int cxgb3i_setup_conn_pagesize(struct t3cdev *tdev, unsigned int tid,
+ int reply, unsigned long pgsz)
+{
+ int pgidx = cxgb3i_ddp_find_page_index(pgsz);
+
+ return setup_conn_pgidx(tdev, tid, pgidx, reply);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_pagesize);
+
+/**
+ * cxgb3i_setup_conn_digest - setup conn. digest setting
+ * @tdev: t3cdev adapter
+ * @tid: connection id
+ * @hcrc: header digest enabled
+ * @dcrc: data digest enabled
+ * @reply: request reply from h/w
+ * set up the iscsi digest settings for a connection identified by tid
+ */
+int cxgb3i_setup_conn_digest(struct t3cdev *tdev, unsigned int tid,
+ int hcrc, int dcrc, int reply)
+{
+ struct sk_buff *skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
+ GFP_KERNEL);
+ struct cpl_set_tcb_field *req;
+ u64 val = (hcrc ? 1 : 0) | (dcrc ? 2 : 0);
+
+ if (!skb)
+ return -ENOMEM;
+
+ /* set up ulp submode and page size */
+ req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
+ req->reply = V_NO_REPLY(reply ? 0 : 1);
+ req->cpu_idx = 0;
+ req->word = htons(31);
+ req->mask = cpu_to_be64(0x0F000000);
+ req->val = cpu_to_be64(val << 24);
+ skb->priority = CPL_PRIORITY_CONTROL;
+
+ cxgb3_ofld_send(tdev, skb);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_digest);
+
+static int ddp_init(struct t3cdev *tdev)
+{
+ struct cxgb3i_ddp_info *ddp;
+ struct ulp_iscsi_info uinfo;
+ unsigned int ppmax, bits;
+ int i, err;
+ static int vers_printed;
+
+ if (!vers_printed) {
+ printk(KERN_INFO "%s", version);
+ vers_printed = 1;
+ }
+
+ err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo);
+ if (err < 0) {
+ ddp_log_error("%s, failed to get iscsi param err=%d.\n",
+ tdev->name, err);
+ return err;
+ }
+
+ ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT;
+ bits = __ilog2_u32(ppmax) + 1;
+ if (bits > PPOD_IDX_MAX_SIZE)
+ bits = PPOD_IDX_MAX_SIZE;
+ ppmax = (1 << (bits - 1)) - 1;
+
+ ddp = cxgb3i_alloc_big_mem(sizeof(struct cxgb3i_ddp_info) +
+ ppmax *
+ (sizeof(struct cxgb3i_gather_list *) +
+ sizeof(struct sk_buff *)),
+ GFP_KERNEL);
+ if (!ddp) {
+ ddp_log_warn("%s unable to alloc ddp 0x%d, ddp disabled.\n",
+ tdev->name, ppmax);
+ return 0;
+ }
+ ddp->gl_map = (struct cxgb3i_gather_list **)(ddp + 1);
+ ddp->gl_skb = (struct sk_buff **)(((char *)ddp->gl_map) +
+ ppmax *
+ sizeof(struct cxgb3i_gather_list *));
+ spin_lock_init(&ddp->map_lock);
+
+ ddp->tdev = tdev;
+ ddp->pdev = uinfo.pdev;
+ ddp->max_txsz = min_t(unsigned int, uinfo.max_txsz, ULP2_MAX_PKT_SIZE);
+ ddp->max_rxsz = min_t(unsigned int, uinfo.max_rxsz, ULP2_MAX_PKT_SIZE);
+ ddp->llimit = uinfo.llimit;
+ ddp->ulimit = uinfo.ulimit;
+ ddp->nppods = ppmax;
+ ddp->idx_last = ppmax;
+ ddp->idx_bits = bits;
+ ddp->idx_mask = (1 << bits) - 1;
+ ddp->rsvd_tag_mask = (1 << (bits + PPOD_IDX_SHIFT)) - 1;
+
+ uinfo.tagmask = ddp->idx_mask << PPOD_IDX_SHIFT;
+ for (i = 0; i < DDP_PGIDX_MAX; i++)
+ uinfo.pgsz_factor[i] = ddp_page_order[i];
+ uinfo.ulimit = uinfo.llimit + (ppmax << PPOD_SIZE_SHIFT);
+
+ err = tdev->ctl(tdev, ULP_ISCSI_SET_PARAMS, &uinfo);
+ if (err < 0) {
+ ddp_log_warn("%s unable to set iscsi param err=%d, "
+ "ddp disabled.\n", tdev->name, err);
+ goto free_ddp_map;
+ }
+
+ tdev->ulp_iscsi = ddp;
+
+ /* add to the list */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_add_tail(&ddp->list, &cxgb3i_ddp_list);
+ write_unlock(&cxgb3i_ddp_rwlock);
+
+ ddp_log_info("nppods %u (0x%x ~ 0x%x), bits %u, mask 0x%x,0x%x "
+ "pkt %u,%u.\n",
+ ppmax, ddp->llimit, ddp->ulimit, ddp->idx_bits,
+ ddp->idx_mask, ddp->rsvd_tag_mask,
+ ddp->max_txsz, ddp->max_rxsz);
+ return 0;
+
+free_ddp_map:
+ cxgb3i_free_big_mem(ddp);
+ return err;
+}
+
+/**
+ * cxgb3i_adapter_ddp_init - initialize the adapter's ddp resource
+ * @tdev: t3cdev adapter
+ * @tformat: tag format
+ * @txsz: max tx pkt size, filled in by this func.
+ * @rxsz: max rx pkt size, filled in by this func.
+ * initialize the ddp pagepod manager for a given adapter if needed and
+ * setup the tag format for a given iscsi entity
+ */
+int cxgb3i_adapter_ddp_init(struct t3cdev *tdev,
+ struct cxgb3i_tag_format *tformat,
+ unsigned int *txsz, unsigned int *rxsz)
+{
+ struct cxgb3i_ddp_info *ddp;
+ unsigned char idx_bits;
+
+ if (!tformat)
+ return -EINVAL;
+
+ if (!tdev->ulp_iscsi) {
+ int err = ddp_init(tdev);
+ if (err < 0)
+ return err;
+ }
+ ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi;
+
+ idx_bits = 32 - tformat->sw_bits;
+ tformat->rsvd_bits = ddp->idx_bits;
+ tformat->rsvd_shift = PPOD_IDX_SHIFT;
+ tformat->rsvd_mask = (1 << tformat->rsvd_bits) - 1;
+
+ ddp_log_info("tag format: sw %u, rsvd %u,%u, mask 0x%x.\n",
+ tformat->sw_bits, tformat->rsvd_bits,
+ tformat->rsvd_shift, tformat->rsvd_mask);
+
+ *txsz = ddp->max_txsz;
+ *rxsz = ddp->max_rxsz;
+ ddp_log_info("ddp max pkt size: %u, %u.\n",
+ ddp->max_txsz, ddp->max_rxsz);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_init);
+
+static void ddp_release(struct cxgb3i_ddp_info *ddp)
+{
+ int i = 0;
+ struct t3cdev *tdev = ddp->tdev;
+
+ tdev->ulp_iscsi = NULL;
+ while (i < ddp->nppods) {
+ struct cxgb3i_gather_list *gl = ddp->gl_map[i];
+ if (gl) {
+ int npods = (gl->nelem + PPOD_PAGES_MAX - 1)
+ >> PPOD_PAGES_SHIFT;
+
+ kfree(gl);
+ ddp_free_gl_skb(ddp, i, npods);
+ } else
+ i++;
+ }
+ cxgb3i_free_big_mem(ddp);
+}
+
+/**
+ * cxgb3i_adapter_ddp_cleanup - release the adapter's ddp resource
+ * @tdev: t3cdev adapter
+ * release all the resource held by the ddp pagepod manager for a given
+ * adapter if needed
+ */
+void cxgb3i_adapter_ddp_cleanup(struct t3cdev *tdev)
+{
+ struct cxgb3i_ddp_info *ddp;
+
+ /* remove from the list */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_for_each_entry(ddp, &cxgb3i_ddp_list, list) {
+ if (ddp->tdev == tdev) {
+ list_del(&ddp->list);
+ break;
+ }
+ }
+ write_unlock(&cxgb3i_ddp_rwlock);
+
+ if (ddp)
+ ddp_release(ddp);
+}
+EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_cleanup);
+
+/**
+ * cxgb3i_ddp_init_module - module init entry point
+ * initialize any driver wide global data structures
+ */
+static int __init cxgb3i_ddp_init_module(void)
+{
+ page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE);
+ ddp_log_info("system PAGE_SIZE %lu, ddp idx %u.\n",
+ PAGE_SIZE, page_idx);
+ return 0;
+}
+
+/**
+ * cxgb3i_ddp_exit_module - module cleanup/exit entry point
+ * go through the ddp list and release any resource held.
+ */
+static void __exit cxgb3i_ddp_exit_module(void)
+{
+ struct cxgb3i_ddp_info *ddp;
+
+ /* release all ddp manager if there is any */
+ write_lock(&cxgb3i_ddp_rwlock);
+ list_for_each_entry(ddp, &cxgb3i_ddp_list, list) {
+ list_del(&ddp->list);
+ ddp_release(ddp);
+ }
+ write_unlock(&cxgb3i_ddp_rwlock);
+}
+
+module_init(cxgb3i_ddp_init_module);
+module_exit(cxgb3i_ddp_exit_module);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.h b/drivers/scsi/cxgb3i/cxgb3i_ddp.h
new file mode 100644
index 000000000000..5c7c4d95c493
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.h
@@ -0,0 +1,306 @@
+/*
+ * cxgb3i_ddp.h: Chelsio S3xx iSCSI DDP Manager.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_ULP2_DDP_H__
+#define __CXGB3I_ULP2_DDP_H__
+
+/**
+ * struct cxgb3i_tag_format - cxgb3i ulp tag format for an iscsi entity
+ *
+ * @sw_bits: # of bits used by iscsi software layer
+ * @rsvd_bits: # of bits used by h/w
+ * @rsvd_shift: h/w bits shift left
+ * @rsvd_mask: reserved bit mask
+ */
+struct cxgb3i_tag_format {
+ unsigned char sw_bits;
+ unsigned char rsvd_bits;
+ unsigned char rsvd_shift;
+ unsigned char filler[1];
+ u32 rsvd_mask;
+};
+
+/**
+ * struct cxgb3i_gather_list - cxgb3i direct data placement memory
+ *
+ * @tag: ddp tag
+ * @length: total data buffer length
+ * @offset: initial offset to the 1st page
+ * @nelem: # of pages
+ * @pages: page pointers
+ * @phys_addr: physical address
+ */
+struct cxgb3i_gather_list {
+ u32 tag;
+ unsigned int length;
+ unsigned int offset;
+ unsigned int nelem;
+ struct page **pages;
+ dma_addr_t phys_addr[0];
+};
+
+/**
+ * struct cxgb3i_ddp_info - cxgb3i direct data placement for pdu payload
+ *
+ * @list: list head to link elements
+ * @tdev: pointer to t3cdev used by cxgb3 driver
+ * @max_txsz: max tx packet size for ddp
+ * @max_rxsz: max rx packet size for ddp
+ * @llimit: lower bound of the page pod memory
+ * @ulimit: upper bound of the page pod memory
+ * @nppods: # of page pod entries
+ * @idx_last: page pod entry last used
+ * @idx_bits: # of bits the pagepod index would take
+ * @idx_mask: pagepod index mask
+ * @rsvd_tag_mask: tag mask
+ * @map_lock: lock to synchonize access to the page pod map
+ * @gl_map: ddp memory gather list
+ * @gl_skb: skb used to program the pagepod
+ */
+struct cxgb3i_ddp_info {
+ struct list_head list;
+ struct t3cdev *tdev;
+ struct pci_dev *pdev;
+ unsigned int max_txsz;
+ unsigned int max_rxsz;
+ unsigned int llimit;
+ unsigned int ulimit;
+ unsigned int nppods;
+ unsigned int idx_last;
+ unsigned char idx_bits;
+ unsigned char filler[3];
+ u32 idx_mask;
+ u32 rsvd_tag_mask;
+ spinlock_t map_lock;
+ struct cxgb3i_gather_list **gl_map;
+ struct sk_buff **gl_skb;
+};
+
+#define ULP2_MAX_PKT_SIZE 16224
+#define ULP2_MAX_PDU_PAYLOAD (ULP2_MAX_PKT_SIZE - ISCSI_PDU_NONPAYLOAD_MAX)
+#define PPOD_PAGES_MAX 4
+#define PPOD_PAGES_SHIFT 2 /* 4 pages per pod */
+
+/*
+ * struct pagepod_hdr, pagepod - pagepod format
+ */
+struct pagepod_hdr {
+ u32 vld_tid;
+ u32 pgsz_tag_clr;
+ u32 maxoffset;
+ u32 pgoffset;
+ u64 rsvd;
+};
+
+struct pagepod {
+ struct pagepod_hdr hdr;
+ u64 addr[PPOD_PAGES_MAX + 1];
+};
+
+#define PPOD_SIZE sizeof(struct pagepod) /* 64 */
+#define PPOD_SIZE_SHIFT 6
+
+#define PPOD_COLOR_SHIFT 0
+#define PPOD_COLOR_SIZE 6
+#define PPOD_COLOR_MASK ((1 << PPOD_COLOR_SIZE) - 1)
+
+#define PPOD_IDX_SHIFT PPOD_COLOR_SIZE
+#define PPOD_IDX_MAX_SIZE 24
+
+#define S_PPOD_TID 0
+#define M_PPOD_TID 0xFFFFFF
+#define V_PPOD_TID(x) ((x) << S_PPOD_TID)
+
+#define S_PPOD_VALID 24
+#define V_PPOD_VALID(x) ((x) << S_PPOD_VALID)
+#define F_PPOD_VALID V_PPOD_VALID(1U)
+
+#define S_PPOD_COLOR 0
+#define M_PPOD_COLOR 0x3F
+#define V_PPOD_COLOR(x) ((x) << S_PPOD_COLOR)
+
+#define S_PPOD_TAG 6
+#define M_PPOD_TAG 0xFFFFFF
+#define V_PPOD_TAG(x) ((x) << S_PPOD_TAG)
+
+#define S_PPOD_PGSZ 30
+#define M_PPOD_PGSZ 0x3
+#define V_PPOD_PGSZ(x) ((x) << S_PPOD_PGSZ)
+
+/*
+ * large memory chunk allocation/release
+ * use vmalloc() if kmalloc() fails
+ */
+static inline void *cxgb3i_alloc_big_mem(unsigned int size,
+ gfp_t gfp)
+{
+ void *p = kmalloc(size, gfp);
+ if (!p)
+ p = vmalloc(size);
+ if (p)
+ memset(p, 0, size);
+ return p;
+}
+
+static inline void cxgb3i_free_big_mem(void *addr)
+{
+ if (is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+
+/*
+ * cxgb3i ddp tag are 32 bits, it consists of reserved bits used by h/w and
+ * non-reserved bits that can be used by the iscsi s/w.
+ * The reserved bits are identified by the rsvd_bits and rsvd_shift fields
+ * in struct cxgb3i_tag_format.
+ *
+ * The upper most reserved bit can be used to check if a tag is ddp tag or not:
+ * if the bit is 0, the tag is a valid ddp tag
+ */
+
+/**
+ * cxgb3i_is_ddp_tag - check if a given tag is a hw/ddp tag
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return true if the tag is a ddp tag, false otherwise.
+ */
+static inline int cxgb3i_is_ddp_tag(struct cxgb3i_tag_format *tformat, u32 tag)
+{
+ return !(tag & (1 << (tformat->rsvd_bits + tformat->rsvd_shift - 1)));
+}
+
+/**
+ * cxgb3i_sw_tag_usable - check if a given s/w tag has enough bits left for
+ * the reserved/hw bits
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ *
+ * return true if the tag is a ddp tag, false otherwise.
+ */
+static inline int cxgb3i_sw_tag_usable(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ sw_tag >>= (32 - tformat->rsvd_bits);
+ return !sw_tag;
+}
+
+/**
+ * cxgb3i_set_non_ddp_tag - mark a given s/w tag as an invalid ddp tag
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ *
+ * insert 1 at the upper most reserved bit to mark it as an invalid ddp tag.
+ */
+static inline u32 cxgb3i_set_non_ddp_tag(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
+ u32 mask = (1 << shift) - 1;
+
+ if (sw_tag && (sw_tag & ~mask)) {
+ u32 v1 = sw_tag & ((1 << shift) - 1);
+ u32 v2 = (sw_tag >> (shift - 1)) << shift;
+
+ return v2 | v1 | 1 << shift;
+ }
+ return sw_tag | 1 << shift;
+}
+
+/**
+ * cxgb3i_ddp_tag_base - shift the s/w tag bits so that reserved bits are not
+ * used.
+ * @tformat: tag format information
+ * @sw_tag: s/w tag to be checked
+ */
+static inline u32 cxgb3i_ddp_tag_base(struct cxgb3i_tag_format *tformat,
+ u32 sw_tag)
+{
+ u32 mask = (1 << tformat->rsvd_shift) - 1;
+
+ if (sw_tag && (sw_tag & ~mask)) {
+ u32 v1 = sw_tag & mask;
+ u32 v2 = sw_tag >> tformat->rsvd_shift;
+
+ v2 <<= tformat->rsvd_shift + tformat->rsvd_bits;
+ return v2 | v1;
+ }
+ return sw_tag;
+}
+
+/**
+ * cxgb3i_tag_rsvd_bits - get the reserved bits used by the h/w
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return the reserved bits in the tag
+ */
+static inline u32 cxgb3i_tag_rsvd_bits(struct cxgb3i_tag_format *tformat,
+ u32 tag)
+{
+ if (cxgb3i_is_ddp_tag(tformat, tag))
+ return (tag >> tformat->rsvd_shift) & tformat->rsvd_mask;
+ return 0;
+}
+
+/**
+ * cxgb3i_tag_nonrsvd_bits - get the non-reserved bits used by the s/w
+ * @tformat: tag format information
+ * @tag: tag to be checked
+ *
+ * return the non-reserved bits in the tag.
+ */
+static inline u32 cxgb3i_tag_nonrsvd_bits(struct cxgb3i_tag_format *tformat,
+ u32 tag)
+{
+ unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
+ u32 v1, v2;
+
+ if (cxgb3i_is_ddp_tag(tformat, tag)) {
+ v1 = tag & ((1 << tformat->rsvd_shift) - 1);
+ v2 = (tag >> (shift + 1)) << tformat->rsvd_shift;
+ } else {
+ u32 mask = (1 << shift) - 1;
+
+ tag &= ~(1 << shift);
+ v1 = tag & mask;
+ v2 = (tag >> 1) & ~mask;
+ }
+ return v1 | v2;
+}
+
+int cxgb3i_ddp_tag_reserve(struct t3cdev *, unsigned int tid,
+ struct cxgb3i_tag_format *, u32 *tag,
+ struct cxgb3i_gather_list *, gfp_t gfp);
+void cxgb3i_ddp_tag_release(struct t3cdev *, u32 tag);
+
+struct cxgb3i_gather_list *cxgb3i_ddp_make_gl(unsigned int xferlen,
+ struct scatterlist *sgl,
+ unsigned int sgcnt,
+ struct pci_dev *pdev,
+ gfp_t gfp);
+void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl,
+ struct pci_dev *pdev);
+
+int cxgb3i_setup_conn_host_pagesize(struct t3cdev *, unsigned int tid,
+ int reply);
+int cxgb3i_setup_conn_pagesize(struct t3cdev *, unsigned int tid, int reply,
+ unsigned long pgsz);
+int cxgb3i_setup_conn_digest(struct t3cdev *, unsigned int tid,
+ int hcrc, int dcrc, int reply);
+int cxgb3i_ddp_find_page_index(unsigned long pgsz);
+int cxgb3i_adapter_ddp_init(struct t3cdev *, struct cxgb3i_tag_format *,
+ unsigned int *txsz, unsigned int *rxsz);
+void cxgb3i_adapter_ddp_cleanup(struct t3cdev *);
+#endif
diff --git a/drivers/scsi/cxgb3i/cxgb3i_init.c b/drivers/scsi/cxgb3i/cxgb3i_init.c
new file mode 100644
index 000000000000..091ecb4d9f3d
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_init.c
@@ -0,0 +1,107 @@
+/* cxgb3i_init.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include "cxgb3i.h"
+
+#define DRV_MODULE_NAME "cxgb3i"
+#define DRV_MODULE_VERSION "1.0.0"
+#define DRV_MODULE_RELDATE "Jun. 1, 2008"
+
+static char version[] =
+ "Chelsio S3xx iSCSI Driver " DRV_MODULE_NAME
+ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Karen Xie <kxie@chelsio.com>");
+MODULE_DESCRIPTION("Chelsio S3xx iSCSI Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static void open_s3_dev(struct t3cdev *);
+static void close_s3_dev(struct t3cdev *);
+
+static cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS];
+static struct cxgb3_client t3c_client = {
+ .name = "iscsi_cxgb3",
+ .handlers = cxgb3i_cpl_handlers,
+ .add = open_s3_dev,
+ .remove = close_s3_dev,
+};
+
+/**
+ * open_s3_dev - register with cxgb3 LLD
+ * @t3dev: cxgb3 adapter instance
+ */
+static void open_s3_dev(struct t3cdev *t3dev)
+{
+ static int vers_printed;
+
+ if (!vers_printed) {
+ printk(KERN_INFO "%s", version);
+ vers_printed = 1;
+ }
+
+ cxgb3i_sdev_add(t3dev, &t3c_client);
+ cxgb3i_adapter_add(t3dev);
+}
+
+/**
+ * close_s3_dev - de-register with cxgb3 LLD
+ * @t3dev: cxgb3 adapter instance
+ */
+static void close_s3_dev(struct t3cdev *t3dev)
+{
+ cxgb3i_adapter_remove(t3dev);
+ cxgb3i_sdev_remove(t3dev);
+}
+
+/**
+ * cxgb3i_init_module - module init entry point
+ *
+ * initialize any driver wide global data structures and register itself
+ * with the cxgb3 module
+ */
+static int __init cxgb3i_init_module(void)
+{
+ int err;
+
+ err = cxgb3i_sdev_init(cxgb3i_cpl_handlers);
+ if (err < 0)
+ return err;
+
+ err = cxgb3i_iscsi_init();
+ if (err < 0)
+ return err;
+
+ err = cxgb3i_pdu_init();
+ if (err < 0)
+ return err;
+
+ cxgb3_register_client(&t3c_client);
+
+ return 0;
+}
+
+/**
+ * cxgb3i_exit_module - module cleanup/exit entry point
+ *
+ * go through the driver hba list and for each hba, release any resource held.
+ * and unregisters iscsi transport and the cxgb3 module
+ */
+static void __exit cxgb3i_exit_module(void)
+{
+ cxgb3_unregister_client(&t3c_client);
+ cxgb3i_pdu_cleanup();
+ cxgb3i_iscsi_cleanup();
+ cxgb3i_sdev_cleanup();
+}
+
+module_init(cxgb3i_init_module);
+module_exit(cxgb3i_exit_module);
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
new file mode 100644
index 000000000000..d83464b9b3f9
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -0,0 +1,951 @@
+/* cxgb3i_iscsi.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2008 Red Hat, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/inet.h>
+#include <linux/crypto.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/iscsi_proto.h>
+#include <scsi/libiscsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "cxgb3i.h"
+#include "cxgb3i_pdu.h"
+
+#ifdef __DEBUG_CXGB3I_TAG__
+#define cxgb3i_tag_debug cxgb3i_log_debug
+#else
+#define cxgb3i_tag_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_CXGB3I_API__
+#define cxgb3i_api_debug cxgb3i_log_debug
+#else
+#define cxgb3i_api_debug(fmt...)
+#endif
+
+/*
+ * align pdu size to multiple of 512 for better performance
+ */
+#define align_pdu_size(n) do { n = (n) & (~511); } while (0)
+
+static struct scsi_transport_template *cxgb3i_scsi_transport;
+static struct scsi_host_template cxgb3i_host_template;
+static struct iscsi_transport cxgb3i_iscsi_transport;
+static unsigned char sw_tag_idx_bits;
+static unsigned char sw_tag_age_bits;
+
+static LIST_HEAD(cxgb3i_snic_list);
+static DEFINE_RWLOCK(cxgb3i_snic_rwlock);
+
+/**
+ * cxgb3i_adapter_add - init a s3 adapter structure and any h/w settings
+ * @t3dev: t3cdev adapter
+ * return the resulting cxgb3i_adapter struct
+ */
+struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *t3dev)
+{
+ struct cxgb3i_adapter *snic;
+ struct adapter *adapter = tdev2adap(t3dev);
+ int i;
+
+ snic = kzalloc(sizeof(*snic), GFP_KERNEL);
+ if (!snic) {
+ cxgb3i_api_debug("cxgb3 %s, OOM.\n", t3dev->name);
+ return NULL;
+ }
+ spin_lock_init(&snic->lock);
+
+ snic->tdev = t3dev;
+ snic->pdev = adapter->pdev;
+ snic->tag_format.sw_bits = sw_tag_idx_bits + sw_tag_age_bits;
+
+ if (cxgb3i_adapter_ddp_init(t3dev, &snic->tag_format,
+ &snic->tx_max_size,
+ &snic->rx_max_size) < 0)
+ goto free_snic;
+
+ for_each_port(adapter, i) {
+ snic->hba[i] = cxgb3i_hba_host_add(snic, adapter->port[i]);
+ if (!snic->hba[i])
+ goto ulp_cleanup;
+ }
+ snic->hba_cnt = adapter->params.nports;
+
+ /* add to the list */
+ write_lock(&cxgb3i_snic_rwlock);
+ list_add_tail(&snic->list_head, &cxgb3i_snic_list);
+ write_unlock(&cxgb3i_snic_rwlock);
+
+ return snic;
+
+ulp_cleanup:
+ cxgb3i_adapter_ddp_cleanup(t3dev);
+free_snic:
+ kfree(snic);
+ return NULL;
+}
+
+/**
+ * cxgb3i_adapter_remove - release all the resources held and cleanup any
+ * h/w settings
+ * @t3dev: t3cdev adapter
+ */
+void cxgb3i_adapter_remove(struct t3cdev *t3dev)
+{
+ int i;
+ struct cxgb3i_adapter *snic;
+
+ /* remove from the list */
+ write_lock(&cxgb3i_snic_rwlock);
+ list_for_each_entry(snic, &cxgb3i_snic_list, list_head) {
+ if (snic->tdev == t3dev) {
+ list_del(&snic->list_head);
+ break;
+ }
+ }
+ write_unlock(&cxgb3i_snic_rwlock);
+
+ if (snic) {
+ for (i = 0; i < snic->hba_cnt; i++) {
+ if (snic->hba[i]) {
+ cxgb3i_hba_host_remove(snic->hba[i]);
+ snic->hba[i] = NULL;
+ }
+ }
+
+ /* release ddp resources */
+ cxgb3i_adapter_ddp_cleanup(snic->tdev);
+ kfree(snic);
+ }
+}
+
+/**
+ * cxgb3i_hba_find_by_netdev - find the cxgb3i_hba structure with a given
+ * net_device
+ * @t3dev: t3cdev adapter
+ */
+struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *ndev)
+{
+ struct cxgb3i_adapter *snic;
+ int i;
+
+ read_lock(&cxgb3i_snic_rwlock);
+ list_for_each_entry(snic, &cxgb3i_snic_list, list_head) {
+ for (i = 0; i < snic->hba_cnt; i++) {
+ if (snic->hba[i]->ndev == ndev) {
+ read_unlock(&cxgb3i_snic_rwlock);
+ return snic->hba[i];
+ }
+ }
+ }
+ read_unlock(&cxgb3i_snic_rwlock);
+ return NULL;
+}
+
+/**
+ * cxgb3i_hba_host_add - register a new host with scsi/iscsi
+ * @snic: the cxgb3i adapter
+ * @ndev: associated net_device
+ */
+struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic,
+ struct net_device *ndev)
+{
+ struct cxgb3i_hba *hba;
+ struct Scsi_Host *shost;
+ int err;
+
+ shost = iscsi_host_alloc(&cxgb3i_host_template,
+ sizeof(struct cxgb3i_hba),
+ CXGB3I_SCSI_QDEPTH_DFLT);
+ if (!shost) {
+ cxgb3i_log_info("iscsi_host_alloc failed.\n");
+ return NULL;
+ }
+
+ shost->transportt = cxgb3i_scsi_transport;
+ shost->max_lun = CXGB3I_MAX_LUN;
+ shost->max_id = CXGB3I_MAX_TARGET;
+ shost->max_channel = 0;
+ shost->max_cmd_len = 16;
+
+ hba = iscsi_host_priv(shost);
+ hba->snic = snic;
+ hba->ndev = ndev;
+ hba->shost = shost;
+
+ pci_dev_get(snic->pdev);
+ err = iscsi_host_add(shost, &snic->pdev->dev);
+ if (err) {
+ cxgb3i_log_info("iscsi_host_add failed.\n");
+ goto pci_dev_put;
+ }
+
+ cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n",
+ shost, hba, shost->host_no);
+
+ return hba;
+
+pci_dev_put:
+ pci_dev_put(snic->pdev);
+ scsi_host_put(shost);
+ return NULL;
+}
+
+/**
+ * cxgb3i_hba_host_remove - de-register the host with scsi/iscsi
+ * @hba: the cxgb3i hba
+ */
+void cxgb3i_hba_host_remove(struct cxgb3i_hba *hba)
+{
+ cxgb3i_api_debug("shost 0x%p, hba 0x%p, no %u.\n",
+ hba->shost, hba, hba->shost->host_no);
+ iscsi_host_remove(hba->shost);
+ pci_dev_put(hba->snic->pdev);
+ iscsi_host_free(hba->shost);
+}
+
+/**
+ * cxgb3i_ep_connect - establish TCP connection to target portal
+ * @dst_addr: target IP address
+ * @non_blocking: blocking or non-blocking call
+ *
+ * Initiates a TCP/IP connection to the dst_addr
+ */
+static struct iscsi_endpoint *cxgb3i_ep_connect(struct sockaddr *dst_addr,
+ int non_blocking)
+{
+ struct iscsi_endpoint *ep;
+ struct cxgb3i_endpoint *cep;
+ struct cxgb3i_hba *hba;
+ struct s3_conn *c3cn = NULL;
+ int err = 0;
+
+ c3cn = cxgb3i_c3cn_create();
+ if (!c3cn) {
+ cxgb3i_log_info("ep connect OOM.\n");
+ err = -ENOMEM;
+ goto release_conn;
+ }
+
+ err = cxgb3i_c3cn_connect(c3cn, (struct sockaddr_in *)dst_addr);
+ if (err < 0) {
+ cxgb3i_log_info("ep connect failed.\n");
+ goto release_conn;
+ }
+ hba = cxgb3i_hba_find_by_netdev(c3cn->dst_cache->dev);
+ if (!hba) {
+ err = -ENOSPC;
+ cxgb3i_log_info("NOT going through cxgbi device.\n");
+ goto release_conn;
+ }
+ if (c3cn_is_closing(c3cn)) {
+ err = -ENOSPC;
+ cxgb3i_log_info("ep connect unable to connect.\n");
+ goto release_conn;
+ }
+
+ ep = iscsi_create_endpoint(sizeof(*cep));
+ if (!ep) {
+ err = -ENOMEM;
+ cxgb3i_log_info("iscsi alloc ep, OOM.\n");
+ goto release_conn;
+ }
+ cep = ep->dd_data;
+ cep->c3cn = c3cn;
+ cep->hba = hba;
+
+ cxgb3i_api_debug("ep 0x%p, 0x%p, c3cn 0x%p, hba 0x%p.\n",
+ ep, cep, c3cn, hba);
+ return ep;
+
+release_conn:
+ cxgb3i_api_debug("conn 0x%p failed, release.\n", c3cn);
+ if (c3cn)
+ cxgb3i_c3cn_release(c3cn);
+ return ERR_PTR(err);
+}
+
+/**
+ * cxgb3i_ep_poll - polls for TCP connection establishement
+ * @ep: TCP connection (endpoint) handle
+ * @timeout_ms: timeout value in milli secs
+ *
+ * polls for TCP connect request to complete
+ */
+static int cxgb3i_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
+{
+ struct cxgb3i_endpoint *cep = ep->dd_data;
+ struct s3_conn *c3cn = cep->c3cn;
+
+ if (!c3cn_is_established(c3cn))
+ return 0;
+ cxgb3i_api_debug("ep 0x%p, c3cn 0x%p established.\n", ep, c3cn);
+ return 1;
+}
+
+/**
+ * cxgb3i_ep_disconnect - teardown TCP connection
+ * @ep: TCP connection (endpoint) handle
+ *
+ * teardown TCP connection
+ */
+static void cxgb3i_ep_disconnect(struct iscsi_endpoint *ep)
+{
+ struct cxgb3i_endpoint *cep = ep->dd_data;
+ struct cxgb3i_conn *cconn = cep->cconn;
+
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p.\n", ep, cep);
+
+ if (cconn && cconn->conn) {
+ /*
+ * stop the xmit path so the xmit_pdu function is
+ * not being called
+ */
+ iscsi_suspend_tx(cconn->conn);
+
+ write_lock_bh(&cep->c3cn->callback_lock);
+ cep->c3cn->user_data = NULL;
+ cconn->cep = NULL;
+ write_unlock_bh(&cep->c3cn->callback_lock);
+ }
+
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p, release c3cn 0x%p.\n",
+ ep, cep, cep->c3cn);
+ cxgb3i_c3cn_release(cep->c3cn);
+ iscsi_destroy_endpoint(ep);
+}
+
+/**
+ * cxgb3i_session_create - create a new iscsi session
+ * @cmds_max: max # of commands
+ * @qdepth: scsi queue depth
+ * @initial_cmdsn: initial iscsi CMDSN for this session
+ * @host_no: pointer to return host no
+ *
+ * Creates a new iSCSI session
+ */
+static struct iscsi_cls_session *
+cxgb3i_session_create(struct iscsi_endpoint *ep, u16 cmds_max, u16 qdepth,
+ u32 initial_cmdsn, u32 *host_no)
+{
+ struct cxgb3i_endpoint *cep;
+ struct cxgb3i_hba *hba;
+ struct Scsi_Host *shost;
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+
+ if (!ep) {
+ cxgb3i_log_error("%s, missing endpoint.\n", __func__);
+ return NULL;
+ }
+
+ cep = ep->dd_data;
+ hba = cep->hba;
+ shost = hba->shost;
+ cxgb3i_api_debug("ep 0x%p, cep 0x%p, hba 0x%p.\n", ep, cep, hba);
+ BUG_ON(hba != iscsi_host_priv(shost));
+
+ *host_no = shost->host_no;
+
+ cls_session = iscsi_session_setup(&cxgb3i_iscsi_transport, shost,
+ cmds_max,
+ sizeof(struct iscsi_tcp_task),
+ initial_cmdsn, ISCSI_MAX_TARGET);
+ if (!cls_session)
+ return NULL;
+ session = cls_session->dd_data;
+ if (iscsi_tcp_r2tpool_alloc(session))
+ goto remove_session;
+
+ return cls_session;
+
+remove_session:
+ iscsi_session_teardown(cls_session);
+ return NULL;
+}
+
+/**
+ * cxgb3i_session_destroy - destroys iscsi session
+ * @cls_session: pointer to iscsi cls session
+ *
+ * Destroys an iSCSI session instance and releases its all resources held
+ */
+static void cxgb3i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ cxgb3i_api_debug("sess 0x%p.\n", cls_session);
+ iscsi_tcp_r2tpool_free(cls_session->dd_data);
+ iscsi_session_teardown(cls_session);
+}
+
+/**
+ * cxgb3i_conn_max_xmit_dlength -- check the max. xmit pdu segment size,
+ * reduce it to be within the hardware limit if needed
+ * @conn: iscsi connection
+ */
+static inline int cxgb3i_conn_max_xmit_dlength(struct iscsi_conn *conn)
+
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ cconn->hba->snic->tx_max_size -
+ ISCSI_PDU_NONPAYLOAD_MAX);
+
+ if (conn->max_xmit_dlength)
+ conn->max_xmit_dlength = min_t(unsigned int,
+ conn->max_xmit_dlength, max);
+ else
+ conn->max_xmit_dlength = max;
+ align_pdu_size(conn->max_xmit_dlength);
+ cxgb3i_log_info("conn 0x%p, max xmit %u.\n",
+ conn, conn->max_xmit_dlength);
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_max_recv_dlength -- check the max. recv pdu segment size against
+ * the hardware limit
+ * @conn: iscsi connection
+ * return 0 if the value is valid, < 0 otherwise.
+ */
+static inline int cxgb3i_conn_max_recv_dlength(struct iscsi_conn *conn)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int max = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD,
+ cconn->hba->snic->rx_max_size -
+ ISCSI_PDU_NONPAYLOAD_MAX);
+
+ align_pdu_size(max);
+ if (conn->max_recv_dlength) {
+ if (conn->max_recv_dlength > max) {
+ cxgb3i_log_error("MaxRecvDataSegmentLength %u too big."
+ " Need to be <= %u.\n",
+ conn->max_recv_dlength, max);
+ return -EINVAL;
+ }
+ conn->max_recv_dlength = min_t(unsigned int,
+ conn->max_recv_dlength, max);
+ align_pdu_size(conn->max_recv_dlength);
+ } else
+ conn->max_recv_dlength = max;
+ cxgb3i_api_debug("conn 0x%p, max recv %u.\n",
+ conn, conn->max_recv_dlength);
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_create - create iscsi connection instance
+ * @cls_session: pointer to iscsi cls session
+ * @cid: iscsi cid
+ *
+ * Creates a new iSCSI connection instance for a given session
+ */
+static struct iscsi_cls_conn *cxgb3i_conn_create(struct iscsi_cls_session
+ *cls_session, u32 cid)
+{
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_conn *conn;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct cxgb3i_conn *cconn;
+
+ cxgb3i_api_debug("sess 0x%p, cid %u.\n", cls_session, cid);
+
+ cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*cconn), cid);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ tcp_conn = conn->dd_data;
+ cconn = tcp_conn->dd_data;
+
+ cconn->conn = conn;
+ return cls_conn;
+}
+
+/**
+ * cxgb3i_conn_bind - binds iscsi sess, conn and endpoint together
+ * @cls_session: pointer to iscsi cls session
+ * @cls_conn: pointer to iscsi cls conn
+ * @transport_eph: 64-bit EP handle
+ * @is_leading: leading connection on this session?
+ *
+ * Binds together an iSCSI session, an iSCSI connection and a
+ * TCP connection. This routine returns error code if the TCP
+ * connection does not belong on the device iSCSI sess/conn is bound
+ */
+
+static int cxgb3i_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn,
+ u64 transport_eph, int is_leading)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic;
+ struct iscsi_endpoint *ep;
+ struct cxgb3i_endpoint *cep;
+ struct s3_conn *c3cn;
+ int err;
+
+ ep = iscsi_lookup_endpoint(transport_eph);
+ if (!ep)
+ return -EINVAL;
+
+ /* setup ddp pagesize */
+ cep = ep->dd_data;
+ c3cn = cep->c3cn;
+ snic = cep->hba->snic;
+ err = cxgb3i_setup_conn_host_pagesize(snic->tdev, c3cn->tid, 0);
+ if (err < 0)
+ return err;
+
+ cxgb3i_api_debug("ep 0x%p, cls sess 0x%p, cls conn 0x%p.\n",
+ ep, cls_session, cls_conn);
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+ return -EINVAL;
+
+ /* calculate the tag idx bits needed for this conn based on cmds_max */
+ cconn->task_idx_bits = (__ilog2_u32(conn->session->cmds_max - 1)) + 1;
+ cxgb3i_api_debug("session cmds_max 0x%x, bits %u.\n",
+ conn->session->cmds_max, cconn->task_idx_bits);
+
+ read_lock(&c3cn->callback_lock);
+ c3cn->user_data = conn;
+ cconn->hba = cep->hba;
+ cconn->cep = cep;
+ cep->cconn = cconn;
+ read_unlock(&c3cn->callback_lock);
+
+ cxgb3i_conn_max_xmit_dlength(conn);
+ cxgb3i_conn_max_recv_dlength(conn);
+
+ spin_lock_bh(&conn->session->lock);
+ sprintf(conn->portal_address, NIPQUAD_FMT,
+ NIPQUAD(c3cn->daddr.sin_addr.s_addr));
+ conn->portal_port = ntohs(c3cn->daddr.sin_port);
+ spin_unlock_bh(&conn->session->lock);
+
+ /* init recv engine */
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+
+ return 0;
+}
+
+/**
+ * cxgb3i_conn_get_param - return iscsi connection parameter to caller
+ * @cls_conn: pointer to iscsi cls conn
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ *
+ * returns iSCSI connection parameters
+ */
+static int cxgb3i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ int len;
+
+ cxgb3i_api_debug("cls_conn 0x%p, param %d.\n", cls_conn, param);
+
+ switch (param) {
+ case ISCSI_PARAM_CONN_PORT:
+ spin_lock_bh(&conn->session->lock);
+ len = sprintf(buf, "%hu\n", conn->portal_port);
+ spin_unlock_bh(&conn->session->lock);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ spin_lock_bh(&conn->session->lock);
+ len = sprintf(buf, "%s\n", conn->portal_address);
+ spin_unlock_bh(&conn->session->lock);
+ break;
+ default:
+ return iscsi_conn_get_param(cls_conn, param, buf);
+ }
+
+ return len;
+}
+
+/**
+ * cxgb3i_conn_set_param - set iscsi connection parameter
+ * @cls_conn: pointer to iscsi cls conn
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ * @buflen: buffer length
+ *
+ * set iSCSI connection parameters
+ */
+static int cxgb3i_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct s3_conn *c3cn = cconn->cep->c3cn;
+ int value, err = 0;
+
+ switch (param) {
+ case ISCSI_PARAM_HDRDGST_EN:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && conn->hdrdgst_en)
+ err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid,
+ conn->hdrdgst_en,
+ conn->datadgst_en, 0);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && conn->datadgst_en)
+ err = cxgb3i_setup_conn_digest(snic->tdev, c3cn->tid,
+ conn->hdrdgst_en,
+ conn->datadgst_en, 0);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+ if (value <= 0 || !is_power_of_2(value))
+ return -EINVAL;
+ if (session->max_r2t == value)
+ break;
+ iscsi_tcp_r2tpool_free(session);
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err && iscsi_tcp_r2tpool_alloc(session))
+ return -ENOMEM;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err)
+ err = cxgb3i_conn_max_recv_dlength(conn);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ err = iscsi_set_param(cls_conn, param, buf, buflen);
+ if (!err)
+ err = cxgb3i_conn_max_xmit_dlength(conn);
+ break;
+ default:
+ return iscsi_set_param(cls_conn, param, buf, buflen);
+ }
+ return err;
+}
+
+/**
+ * cxgb3i_host_set_param - configure host (adapter) related parameters
+ * @shost: scsi host pointer
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ */
+static int cxgb3i_host_set_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param,
+ char *buf, int buflen)
+{
+ struct cxgb3i_hba *hba = iscsi_host_priv(shost);
+
+ cxgb3i_api_debug("param %d, buf %s.\n", param, buf);
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ {
+ __be32 addr = in_aton(buf);
+ cxgb3i_set_private_ipv4addr(hba->ndev, addr);
+ return 0;
+ }
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ /* ignore */
+ return 0;
+ default:
+ return iscsi_host_set_param(shost, param, buf, buflen);
+ }
+}
+
+/**
+ * cxgb3i_host_get_param - returns host (adapter) related parameters
+ * @shost: scsi host pointer
+ * @param: parameter type identifier
+ * @buf: buffer pointer
+ */
+static int cxgb3i_host_get_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param, char *buf)
+{
+ struct cxgb3i_hba *hba = iscsi_host_priv(shost);
+ int len = 0;
+
+ cxgb3i_api_debug("hba %s, param %d.\n", hba->ndev->name, param);
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_HWADDRESS:
+ len = sysfs_format_mac(buf, hba->ndev->dev_addr, 6);
+ break;
+ case ISCSI_HOST_PARAM_NETDEV_NAME:
+ len = sprintf(buf, "%s\n", hba->ndev->name);
+ break;
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ {
+ __be32 addr;
+
+ addr = cxgb3i_get_private_ipv4addr(hba->ndev);
+ len = sprintf(buf, NIPQUAD_FMT, NIPQUAD(addr));
+ break;
+ }
+ default:
+ return iscsi_host_get_param(shost, param, buf);
+ }
+ return len;
+}
+
+/**
+ * cxgb3i_conn_get_stats - returns iSCSI stats
+ * @cls_conn: pointer to iscsi cls conn
+ * @stats: pointer to iscsi statistic struct
+ */
+static void cxgb3i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+ stats->digest_err = 0;
+ stats->timeout_err = 0;
+ stats->custom_length = 1;
+ strcpy(stats->custom[0].desc, "eh_abort_cnt");
+ stats->custom[0].value = conn->eh_abort_cnt;
+}
+
+/**
+ * cxgb3i_parse_itt - get the idx and age bits from a given tag
+ * @conn: iscsi connection
+ * @itt: itt tag
+ * @idx: task index, filled in by this function
+ * @age: session age, filled in by this function
+ */
+static void cxgb3i_parse_itt(struct iscsi_conn *conn, itt_t itt,
+ int *idx, int *age)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ u32 tag = ntohl((__force u32) itt);
+ u32 sw_bits;
+
+ sw_bits = cxgb3i_tag_nonrsvd_bits(&snic->tag_format, tag);
+ if (idx)
+ *idx = sw_bits & ((1 << cconn->task_idx_bits) - 1);
+ if (age)
+ *age = (sw_bits >> cconn->task_idx_bits) & ISCSI_AGE_MASK;
+
+ cxgb3i_tag_debug("parse tag 0x%x/0x%x, sw 0x%x, itt 0x%x, age 0x%x.\n",
+ tag, itt, sw_bits, idx ? *idx : 0xFFFFF,
+ age ? *age : 0xFF);
+}
+
+/**
+ * cxgb3i_reserve_itt - generate tag for a give task
+ * Try to set up ddp for a scsi read task.
+ * @task: iscsi task
+ * @hdr_itt: tag, filled in by this function
+ */
+int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
+{
+ struct scsi_cmnd *sc = task->sc;
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *sess = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct cxgb3i_tag_format *tformat = &snic->tag_format;
+ u32 sw_tag = (sess->age << cconn->task_idx_bits) | task->itt;
+ u32 tag;
+ int err = -EINVAL;
+
+ if (sc &&
+ (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
+ cxgb3i_sw_tag_usable(tformat, sw_tag)) {
+ struct s3_conn *c3cn = cconn->cep->c3cn;
+ struct cxgb3i_gather_list *gl;
+
+ gl = cxgb3i_ddp_make_gl(scsi_in(sc)->length,
+ scsi_in(sc)->table.sgl,
+ scsi_in(sc)->table.nents,
+ snic->pdev,
+ GFP_ATOMIC);
+ if (gl) {
+ tag = sw_tag;
+ err = cxgb3i_ddp_tag_reserve(snic->tdev, c3cn->tid,
+ tformat, &tag,
+ gl, GFP_ATOMIC);
+ if (err < 0)
+ cxgb3i_ddp_release_gl(gl, snic->pdev);
+ }
+ }
+
+ if (err < 0)
+ tag = cxgb3i_set_non_ddp_tag(tformat, sw_tag);
+ /* the itt need to sent in big-endian order */
+ *hdr_itt = (__force itt_t)htonl(tag);
+
+ cxgb3i_tag_debug("new tag 0x%x/0x%x (itt 0x%x, age 0x%x).\n",
+ tag, *hdr_itt, task->itt, sess->age);
+ return 0;
+}
+
+/**
+ * cxgb3i_release_itt - release the tag for a given task
+ * if the tag is a ddp tag, release the ddp setup
+ * @task: iscsi task
+ * @hdr_itt: tag
+ */
+void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt)
+{
+ struct scsi_cmnd *sc = task->sc;
+ struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ struct cxgb3i_adapter *snic = cconn->hba->snic;
+ struct cxgb3i_tag_format *tformat = &snic->tag_format;
+ u32 tag = ntohl((__force u32)hdr_itt);
+
+ cxgb3i_tag_debug("release tag 0x%x.\n", tag);
+
+ if (sc &&
+ (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_FROM_DEVICE) &&
+ cxgb3i_is_ddp_tag(tformat, tag))
+ cxgb3i_ddp_tag_release(snic->tdev, tag);
+}
+
+/**
+ * cxgb3i_host_template -- Scsi_Host_Template structure
+ * used when registering with the scsi mid layer
+ */
+static struct scsi_host_template cxgb3i_host_template = {
+ .module = THIS_MODULE,
+ .name = "Chelsio S3xx iSCSI Initiator",
+ .proc_name = "cxgb3i",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = 128 * (ISCSI_DEF_XMIT_CMDS_MAX - 1),
+ .sg_tablesize = SG_ALL,
+ .max_sectors = 0xFFFF,
+ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_device_reset_handler = iscsi_eh_device_reset,
+ .eh_target_reset_handler = iscsi_eh_target_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .this_id = -1,
+};
+
+static struct iscsi_transport cxgb3i_iscsi_transport = {
+ .owner = THIS_MODULE,
+ .name = "cxgb3i",
+ .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
+ | CAP_DATADGST | CAP_DIGEST_OFFLOAD |
+ CAP_PADDING_OFFLOAD,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME | ISCSI_TPGT |
+ ISCSI_USERNAME | ISCSI_PASSWORD |
+ ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_LU_RESET_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO |
+ ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
+ .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
+ ISCSI_HOST_INITIATOR_NAME | ISCSI_HOST_NETDEV_NAME,
+ .get_host_param = cxgb3i_host_get_param,
+ .set_host_param = cxgb3i_host_set_param,
+ /* session management */
+ .create_session = cxgb3i_session_create,
+ .destroy_session = cxgb3i_session_destroy,
+ .get_session_param = iscsi_session_get_param,
+ /* connection management */
+ .create_conn = cxgb3i_conn_create,
+ .bind_conn = cxgb3i_conn_bind,
+ .destroy_conn = iscsi_tcp_conn_teardown,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_conn_stop,
+ .get_conn_param = cxgb3i_conn_get_param,
+ .set_param = cxgb3i_conn_set_param,
+ .get_stats = cxgb3i_conn_get_stats,
+ /* pdu xmit req. from user space */
+ .send_pdu = iscsi_conn_send_pdu,
+ /* task */
+ .init_task = iscsi_tcp_task_init,
+ .xmit_task = iscsi_tcp_task_xmit,
+ .cleanup_task = cxgb3i_conn_cleanup_task,
+
+ /* pdu */
+ .alloc_pdu = cxgb3i_conn_alloc_pdu,
+ .init_pdu = cxgb3i_conn_init_pdu,
+ .xmit_pdu = cxgb3i_conn_xmit_pdu,
+ .parse_pdu_itt = cxgb3i_parse_itt,
+
+ /* TCP connect/disconnect */
+ .ep_connect = cxgb3i_ep_connect,
+ .ep_poll = cxgb3i_ep_poll,
+ .ep_disconnect = cxgb3i_ep_disconnect,
+ /* Error recovery timeout call */
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
+};
+
+int cxgb3i_iscsi_init(void)
+{
+ sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
+ sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
+ cxgb3i_log_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n",
+ ISCSI_ITT_MASK, sw_tag_idx_bits,
+ ISCSI_AGE_MASK, sw_tag_age_bits);
+
+ cxgb3i_scsi_transport =
+ iscsi_register_transport(&cxgb3i_iscsi_transport);
+ if (!cxgb3i_scsi_transport) {
+ cxgb3i_log_error("Could not register cxgb3i transport.\n");
+ return -ENODEV;
+ }
+ cxgb3i_api_debug("cxgb3i transport 0x%p.\n", cxgb3i_scsi_transport);
+ return 0;
+}
+
+void cxgb3i_iscsi_cleanup(void)
+{
+ if (cxgb3i_scsi_transport) {
+ cxgb3i_api_debug("cxgb3i transport 0x%p.\n",
+ cxgb3i_scsi_transport);
+ iscsi_unregister_transport(&cxgb3i_iscsi_transport);
+ }
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c
new file mode 100644
index 000000000000..a865f1fefe8b
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.c
@@ -0,0 +1,1810 @@
+/*
+ * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
+ *
+ * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
+ * release for licensing terms and conditions.
+ *
+ * Written by: Dimitris Michailidis (dm@chelsio.com)
+ * Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/if_vlan.h>
+#include <linux/version.h>
+
+#include "cxgb3_defs.h"
+#include "cxgb3_ctl_defs.h"
+#include "firmware_exports.h"
+#include "cxgb3i_offload.h"
+#include "cxgb3i_pdu.h"
+#include "cxgb3i_ddp.h"
+
+#ifdef __DEBUG_C3CN_CONN__
+#define c3cn_conn_debug cxgb3i_log_info
+#else
+#define c3cn_conn_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_C3CN_TX__
+#define c3cn_tx_debug cxgb3i_log_debug
+#else
+#define c3cn_tx_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_C3CN_RX__
+#define c3cn_rx_debug cxgb3i_log_debug
+#else
+#define c3cn_rx_debug(fmt...)
+#endif
+
+/*
+ * module parameters releated to offloaded iscsi connection
+ */
+static int cxgb3_rcv_win = 256 * 1024;
+module_param(cxgb3_rcv_win, int, 0644);
+MODULE_PARM_DESC(cxgb3_rcv_win, "TCP receive window in bytes (default=256KB)");
+
+static int cxgb3_snd_win = 64 * 1024;
+module_param(cxgb3_snd_win, int, 0644);
+MODULE_PARM_DESC(cxgb3_snd_win, "TCP send window in bytes (default=64KB)");
+
+static int cxgb3_rx_credit_thres = 10 * 1024;
+module_param(cxgb3_rx_credit_thres, int, 0644);
+MODULE_PARM_DESC(rx_credit_thres,
+ "RX credits return threshold in bytes (default=10KB)");
+
+static unsigned int cxgb3_max_connect = 8 * 1024;
+module_param(cxgb3_max_connect, uint, 0644);
+MODULE_PARM_DESC(cxgb3_max_connect, "Max. # of connections (default=8092)");
+
+static unsigned int cxgb3_sport_base = 20000;
+module_param(cxgb3_sport_base, uint, 0644);
+MODULE_PARM_DESC(cxgb3_sport_base, "starting port number (default=20000)");
+
+/*
+ * cxgb3i tcp connection data(per adapter) list
+ */
+static LIST_HEAD(cdata_list);
+static DEFINE_RWLOCK(cdata_rwlock);
+
+static int c3cn_push_tx_frames(struct s3_conn *c3cn, int req_completion);
+static void c3cn_release_offload_resources(struct s3_conn *c3cn);
+
+/*
+ * iscsi source port management
+ *
+ * Find a free source port in the port allocation map. We use a very simple
+ * rotor scheme to look for the next free port.
+ *
+ * If a source port has been specified make sure that it doesn't collide with
+ * our normal source port allocation map. If it's outside the range of our
+ * allocation/deallocation scheme just let them use it.
+ *
+ * If the source port is outside our allocation range, the caller is
+ * responsible for keeping track of their port usage.
+ */
+static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata)
+{
+ unsigned int start;
+ int idx;
+
+ if (!cdata)
+ goto error_out;
+
+ if (c3cn->saddr.sin_port != 0) {
+ idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
+ if (idx < 0 || idx >= cxgb3_max_connect)
+ return 0;
+ if (!test_and_set_bit(idx, cdata->sport_map))
+ return -EADDRINUSE;
+ }
+
+ /* the sport_map_next may not be accurate but that is okay, sport_map
+ should be */
+ start = idx = cdata->sport_map_next;
+ do {
+ if (++idx >= cxgb3_max_connect)
+ idx = 0;
+ if (!(test_and_set_bit(idx, cdata->sport_map))) {
+ c3cn->saddr.sin_port = htons(cxgb3_sport_base + idx);
+ cdata->sport_map_next = idx;
+ c3cn_conn_debug("%s reserve port %u.\n",
+ cdata->cdev->name,
+ cxgb3_sport_base + idx);
+ return 0;
+ }
+ } while (idx != start);
+
+error_out:
+ return -EADDRNOTAVAIL;
+}
+
+static void c3cn_put_port(struct s3_conn *c3cn)
+{
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev);
+
+ if (c3cn->saddr.sin_port) {
+ int idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base;
+
+ c3cn->saddr.sin_port = 0;
+ if (idx < 0 || idx >= cxgb3_max_connect)
+ return;
+ clear_bit(idx, cdata->sport_map);
+ c3cn_conn_debug("%s, release port %u.\n",
+ cdata->cdev->name, cxgb3_sport_base + idx);
+ }
+}
+
+static inline void c3cn_set_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ __set_bit(flag, &c3cn->flags);
+ c3cn_conn_debug("c3cn 0x%p, set %d, s %u, f 0x%lx.\n",
+ c3cn, flag, c3cn->state, c3cn->flags);
+}
+
+static inline void c3cn_clear_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ __clear_bit(flag, &c3cn->flags);
+ c3cn_conn_debug("c3cn 0x%p, clear %d, s %u, f 0x%lx.\n",
+ c3cn, flag, c3cn->state, c3cn->flags);
+}
+
+static inline int c3cn_flag(struct s3_conn *c3cn, enum c3cn_flags flag)
+{
+ if (c3cn == NULL)
+ return 0;
+ return test_bit(flag, &c3cn->flags);
+}
+
+static void c3cn_set_state(struct s3_conn *c3cn, int state)
+{
+ c3cn_conn_debug("c3cn 0x%p state -> %u.\n", c3cn, state);
+ c3cn->state = state;
+}
+
+static inline void c3cn_hold(struct s3_conn *c3cn)
+{
+ atomic_inc(&c3cn->refcnt);
+}
+
+static inline void c3cn_put(struct s3_conn *c3cn)
+{
+ if (atomic_dec_and_test(&c3cn->refcnt)) {
+ c3cn_conn_debug("free c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ kfree(c3cn);
+ }
+}
+
+static void c3cn_closed(struct s3_conn *c3cn)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn_put_port(c3cn);
+ c3cn_release_offload_resources(c3cn);
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
+ cxgb3i_conn_closing(c3cn);
+}
+
+/*
+ * CPL (Chelsio Protocol Language) defines a message passing interface between
+ * the host driver and T3 asic.
+ * The section below implments CPLs that related to iscsi tcp connection
+ * open/close/abort and data send/receive.
+ */
+
+/*
+ * CPL connection active open request: host ->
+ */
+static unsigned int find_best_mtu(const struct t3c_data *d, unsigned short mtu)
+{
+ int i = 0;
+
+ while (i < d->nmtus - 1 && d->mtus[i + 1] <= mtu)
+ ++i;
+ return i;
+}
+
+static unsigned int select_mss(struct s3_conn *c3cn, unsigned int pmtu)
+{
+ unsigned int idx;
+ struct dst_entry *dst = c3cn->dst_cache;
+ struct t3cdev *cdev = c3cn->cdev;
+ const struct t3c_data *td = T3C_DATA(cdev);
+ u16 advmss = dst_metric(dst, RTAX_ADVMSS);
+
+ if (advmss > pmtu - 40)
+ advmss = pmtu - 40;
+ if (advmss < td->mtus[0] - 40)
+ advmss = td->mtus[0] - 40;
+ idx = find_best_mtu(td, advmss + 40);
+ return idx;
+}
+
+static inline int compute_wscale(int win)
+{
+ int wscale = 0;
+ while (wscale < 14 && (65535<<wscale) < win)
+ wscale++;
+ return wscale;
+}
+
+static inline unsigned int calc_opt0h(struct s3_conn *c3cn)
+{
+ int wscale = compute_wscale(cxgb3_rcv_win);
+ return V_KEEP_ALIVE(1) |
+ F_TCAM_BYPASS |
+ V_WND_SCALE(wscale) |
+ V_MSS_IDX(c3cn->mss_idx);
+}
+
+static inline unsigned int calc_opt0l(struct s3_conn *c3cn)
+{
+ return V_ULP_MODE(ULP_MODE_ISCSI) |
+ V_RCV_BUFSIZ(cxgb3_rcv_win>>10);
+}
+
+static void make_act_open_req(struct s3_conn *c3cn, struct sk_buff *skb,
+ unsigned int atid, const struct l2t_entry *e)
+{
+ struct cpl_act_open_req *req;
+
+ c3cn_conn_debug("c3cn 0x%p, atid 0x%x.\n", c3cn, atid);
+
+ skb->priority = CPL_PRIORITY_SETUP;
+ req = (struct cpl_act_open_req *)__skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, atid));
+ req->local_port = c3cn->saddr.sin_port;
+ req->peer_port = c3cn->daddr.sin_port;
+ req->local_ip = c3cn->saddr.sin_addr.s_addr;
+ req->peer_ip = c3cn->daddr.sin_addr.s_addr;
+ req->opt0h = htonl(calc_opt0h(c3cn) | V_L2T_IDX(e->idx) |
+ V_TX_CHANNEL(e->smt_idx));
+ req->opt0l = htonl(calc_opt0l(c3cn));
+ req->params = 0;
+}
+
+static void fail_act_open(struct s3_conn *c3cn, int errno)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ c3cn->err = errno;
+ c3cn_closed(c3cn);
+}
+
+static void act_open_req_arp_failure(struct t3cdev *dev, struct sk_buff *skb)
+{
+ struct s3_conn *c3cn = (struct s3_conn *)skb->sk;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ c3cn_hold(c3cn);
+ spin_lock_bh(&c3cn->lock);
+ if (c3cn->state == C3CN_STATE_CONNECTING)
+ fail_act_open(c3cn, EHOSTUNREACH);
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+ __kfree_skb(skb);
+}
+
+/*
+ * CPL connection close request: host ->
+ *
+ * Close a connection by sending a CPL_CLOSE_CON_REQ message and queue it to
+ * the write queue (i.e., after any unsent txt data).
+ */
+static void skb_entail(struct s3_conn *c3cn, struct sk_buff *skb,
+ int flags)
+{
+ CXGB3_SKB_CB(skb)->seq = c3cn->write_seq;
+ CXGB3_SKB_CB(skb)->flags = flags;
+ __skb_queue_tail(&c3cn->write_queue, skb);
+}
+
+static void send_close_req(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->cpl_close;
+ struct cpl_close_con_req *req = (struct cpl_close_con_req *)skb->head;
+ unsigned int tid = c3cn->tid;
+
+ c3cn_conn_debug("c3cn 0x%p, state 0x%x, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->cpl_close = NULL;
+
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_CLOSE_CON));
+ req->wr.wr_lo = htonl(V_WR_TID(tid));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_CON_REQ, tid));
+ req->rsvd = htonl(c3cn->write_seq);
+
+ skb_entail(c3cn, skb, C3CB_FLAG_NO_APPEND);
+ if (c3cn->state != C3CN_STATE_CONNECTING)
+ c3cn_push_tx_frames(c3cn, 1);
+}
+
+/*
+ * CPL connection abort request: host ->
+ *
+ * Send an ABORT_REQ message. Makes sure we do not send multiple ABORT_REQs
+ * for the same connection and also that we do not try to send a message
+ * after the connection has closed.
+ */
+static void abort_arp_failure(struct t3cdev *cdev, struct sk_buff *skb)
+{
+ struct cpl_abort_req *req = cplhdr(skb);
+
+ c3cn_conn_debug("tdev 0x%p.\n", cdev);
+
+ req->cmd = CPL_ABORT_NO_RST;
+ cxgb3_ofld_send(cdev, skb);
+}
+
+static inline void c3cn_purge_write_queue(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&c3cn->write_queue)))
+ __kfree_skb(skb);
+}
+
+static void send_abort_req(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->cpl_abort_req;
+ struct cpl_abort_req *req;
+ unsigned int tid = c3cn->tid;
+
+ if (unlikely(c3cn->state == C3CN_STATE_ABORTING) || !skb ||
+ !c3cn->cdev)
+ return;
+
+ c3cn_set_state(c3cn, C3CN_STATE_ABORTING);
+
+ c3cn_conn_debug("c3cn 0x%p, flag ABORT_RPL + ABORT_SHUT.\n", c3cn);
+
+ c3cn_set_flag(c3cn, C3CN_ABORT_RPL_PENDING);
+
+ /* Purge the send queue so we don't send anything after an abort. */
+ c3cn_purge_write_queue(c3cn);
+
+ c3cn->cpl_abort_req = NULL;
+ req = (struct cpl_abort_req *)skb->head;
+
+ skb->priority = CPL_PRIORITY_DATA;
+ set_arp_failure_handler(skb, abort_arp_failure);
+
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_REQ));
+ req->wr.wr_lo = htonl(V_WR_TID(tid));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ABORT_REQ, tid));
+ req->rsvd0 = htonl(c3cn->snd_nxt);
+ req->rsvd1 = !c3cn_flag(c3cn, C3CN_TX_DATA_SENT);
+ req->cmd = CPL_ABORT_SEND_RST;
+
+ l2t_send(c3cn->cdev, skb, c3cn->l2t);
+}
+
+/*
+ * CPL connection abort reply: host ->
+ *
+ * Send an ABORT_RPL message in response of the ABORT_REQ received.
+ */
+static void send_abort_rpl(struct s3_conn *c3cn, int rst_status)
+{
+ struct sk_buff *skb = c3cn->cpl_abort_rpl;
+ struct cpl_abort_rpl *rpl = (struct cpl_abort_rpl *)skb->head;
+
+ c3cn->cpl_abort_rpl = NULL;
+
+ skb->priority = CPL_PRIORITY_DATA;
+ rpl->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL));
+ rpl->wr.wr_lo = htonl(V_WR_TID(c3cn->tid));
+ OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, c3cn->tid));
+ rpl->cmd = rst_status;
+
+ cxgb3_ofld_send(c3cn->cdev, skb);
+}
+
+/*
+ * CPL connection rx data ack: host ->
+ * Send RX credits through an RX_DATA_ACK CPL message. Returns the number of
+ * credits sent.
+ */
+static u32 send_rx_credits(struct s3_conn *c3cn, u32 credits, u32 dack)
+{
+ struct sk_buff *skb;
+ struct cpl_rx_data_ack *req;
+
+ skb = alloc_skb(sizeof(*req), GFP_ATOMIC);
+ if (!skb)
+ return 0;
+
+ req = (struct cpl_rx_data_ack *)__skb_put(skb, sizeof(*req));
+ req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
+ OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RX_DATA_ACK, c3cn->tid));
+ req->credit_dack = htonl(dack | V_RX_CREDITS(credits));
+ skb->priority = CPL_PRIORITY_ACK;
+ cxgb3_ofld_send(c3cn->cdev, skb);
+ return credits;
+}
+
+/*
+ * CPL connection tx data: host ->
+ *
+ * Send iscsi PDU via TX_DATA CPL message. Returns the number of
+ * credits sent.
+ * Each TX_DATA consumes work request credit (wrs), so we need to keep track of
+ * how many we've used so far and how many are pending (i.e., yet ack'ed by T3).
+ */
+
+/*
+ * For ULP connections HW may inserts digest bytes into the pdu. Those digest
+ * bytes are not sent by the host but are part of the TCP payload and therefore
+ * consume TCP sequence space.
+ */
+static const unsigned int cxgb3_ulp_extra_len[] = { 0, 4, 4, 8 };
+static inline unsigned int ulp_extra_len(const struct sk_buff *skb)
+{
+ return cxgb3_ulp_extra_len[skb_ulp_mode(skb) & 3];
+}
+
+static unsigned int wrlen __read_mostly;
+
+/*
+ * The number of WRs needed for an skb depends on the number of fragments
+ * in the skb and whether it has any payload in its main body. This maps the
+ * length of the gather list represented by an skb into the # of necessary WRs.
+ *
+ * The max. length of an skb is controlled by the max pdu size which is ~16K.
+ * Also, assume the min. fragment length is the sector size (512), then add
+ * extra fragment counts for iscsi bhs and payload padding.
+ */
+#define SKB_WR_LIST_SIZE (16384/512 + 3)
+static unsigned int skb_wrs[SKB_WR_LIST_SIZE] __read_mostly;
+
+static void s3_init_wr_tab(unsigned int wr_len)
+{
+ int i;
+
+ if (skb_wrs[1]) /* already initialized */
+ return;
+
+ for (i = 1; i < SKB_WR_LIST_SIZE; i++) {
+ int sgl_len = (3 * i) / 2 + (i & 1);
+
+ sgl_len += 3;
+ skb_wrs[i] = (sgl_len <= wr_len
+ ? 1 : 1 + (sgl_len - 2) / (wr_len - 1));
+ }
+
+ wrlen = wr_len * 8;
+}
+
+static inline void reset_wr_list(struct s3_conn *c3cn)
+{
+ c3cn->wr_pending_head = NULL;
+}
+
+/*
+ * Add a WR to a connections's list of pending WRs. This is a singly-linked
+ * list of sk_buffs operating as a FIFO. The head is kept in wr_pending_head
+ * and the tail in wr_pending_tail.
+ */
+static inline void enqueue_wr(struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ skb_wr_data(skb) = NULL;
+
+ /*
+ * We want to take an extra reference since both us and the driver
+ * need to free the packet before it's really freed. We know there's
+ * just one user currently so we use atomic_set rather than skb_get
+ * to avoid the atomic op.
+ */
+ atomic_set(&skb->users, 2);
+
+ if (!c3cn->wr_pending_head)
+ c3cn->wr_pending_head = skb;
+ else
+ skb_wr_data(skb) = skb;
+ c3cn->wr_pending_tail = skb;
+}
+
+static inline struct sk_buff *peek_wr(const struct s3_conn *c3cn)
+{
+ return c3cn->wr_pending_head;
+}
+
+static inline void free_wr_skb(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+static inline struct sk_buff *dequeue_wr(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb = c3cn->wr_pending_head;
+
+ if (likely(skb)) {
+ /* Don't bother clearing the tail */
+ c3cn->wr_pending_head = skb_wr_data(skb);
+ skb_wr_data(skb) = NULL;
+ }
+ return skb;
+}
+
+static void purge_wr_queue(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+ while ((skb = dequeue_wr(c3cn)) != NULL)
+ free_wr_skb(skb);
+}
+
+static inline void make_tx_data_wr(struct s3_conn *c3cn, struct sk_buff *skb,
+ int len)
+{
+ struct tx_data_wr *req;
+
+ skb_reset_transport_header(skb);
+ req = (struct tx_data_wr *)__skb_push(skb, sizeof(*req));
+ req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA));
+ req->wr_lo = htonl(V_WR_TID(c3cn->tid));
+ req->sndseq = htonl(c3cn->snd_nxt);
+ /* len includes the length of any HW ULP additions */
+ req->len = htonl(len);
+ req->param = htonl(V_TX_PORT(c3cn->l2t->smt_idx));
+ /* V_TX_ULP_SUBMODE sets both the mode and submode */
+ req->flags = htonl(V_TX_ULP_SUBMODE(skb_ulp_mode(skb)) |
+ V_TX_SHOVE((skb_peek(&c3cn->write_queue) ? 0 : 1)));
+
+ if (!c3cn_flag(c3cn, C3CN_TX_DATA_SENT)) {
+ req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
+ V_TX_CPU_IDX(c3cn->qset));
+ /* Sendbuffer is in units of 32KB. */
+ req->param |= htonl(V_TX_SNDBUF(cxgb3_snd_win >> 15));
+ c3cn_set_flag(c3cn, C3CN_TX_DATA_SENT);
+ }
+}
+
+/**
+ * c3cn_push_tx_frames -- start transmit
+ * @c3cn: the offloaded connection
+ * @req_completion: request wr_ack or not
+ *
+ * Prepends TX_DATA_WR or CPL_CLOSE_CON_REQ headers to buffers waiting in a
+ * connection's send queue and sends them on to T3. Must be called with the
+ * connection's lock held. Returns the amount of send buffer space that was
+ * freed as a result of sending queued data to T3.
+ */
+static void arp_failure_discard(struct t3cdev *cdev, struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+static int c3cn_push_tx_frames(struct s3_conn *c3cn, int req_completion)
+{
+ int total_size = 0;
+ struct sk_buff *skb;
+ struct t3cdev *cdev;
+ struct cxgb3i_sdev_data *cdata;
+
+ if (unlikely(c3cn->state == C3CN_STATE_CONNECTING ||
+ c3cn->state == C3CN_STATE_CLOSE_WAIT_1 ||
+ c3cn->state == C3CN_STATE_ABORTING)) {
+ c3cn_tx_debug("c3cn 0x%p, in closing state %u.\n",
+ c3cn, c3cn->state);
+ return 0;
+ }
+
+ cdev = c3cn->cdev;
+ cdata = CXGB3_SDEV_DATA(cdev);
+
+ while (c3cn->wr_avail
+ && (skb = skb_peek(&c3cn->write_queue)) != NULL) {
+ int len = skb->len; /* length before skb_push */
+ int frags = skb_shinfo(skb)->nr_frags + (len != skb->data_len);
+ int wrs_needed = skb_wrs[frags];
+
+ if (wrs_needed > 1 && len + sizeof(struct tx_data_wr) <= wrlen)
+ wrs_needed = 1;
+
+ WARN_ON(frags >= SKB_WR_LIST_SIZE || wrs_needed < 1);
+
+ if (c3cn->wr_avail < wrs_needed) {
+ c3cn_tx_debug("c3cn 0x%p, skb len %u/%u, frag %u, "
+ "wr %d < %u.\n",
+ c3cn, skb->len, skb->datalen, frags,
+ wrs_needed, c3cn->wr_avail);
+ break;
+ }
+
+ __skb_unlink(skb, &c3cn->write_queue);
+ skb->priority = CPL_PRIORITY_DATA;
+ skb->csum = wrs_needed; /* remember this until the WR_ACK */
+ c3cn->wr_avail -= wrs_needed;
+ c3cn->wr_unacked += wrs_needed;
+ enqueue_wr(c3cn, skb);
+
+ if (likely(CXGB3_SKB_CB(skb)->flags & C3CB_FLAG_NEED_HDR)) {
+ len += ulp_extra_len(skb);
+ make_tx_data_wr(c3cn, skb, len);
+ c3cn->snd_nxt += len;
+ if ((req_completion
+ && c3cn->wr_unacked == wrs_needed)
+ || (CXGB3_SKB_CB(skb)->flags & C3CB_FLAG_COMPL)
+ || c3cn->wr_unacked >= c3cn->wr_max / 2) {
+ struct work_request_hdr *wr = cplhdr(skb);
+
+ wr->wr_hi |= htonl(F_WR_COMPL);
+ c3cn->wr_unacked = 0;
+ }
+ CXGB3_SKB_CB(skb)->flags &= ~C3CB_FLAG_NEED_HDR;
+ }
+
+ total_size += skb->truesize;
+ set_arp_failure_handler(skb, arp_failure_discard);
+ l2t_send(cdev, skb, c3cn->l2t);
+ }
+ return total_size;
+}
+
+/*
+ * process_cpl_msg: -> host
+ * Top-level CPL message processing used by most CPL messages that
+ * pertain to connections.
+ */
+static inline void process_cpl_msg(void (*fn)(struct s3_conn *,
+ struct sk_buff *),
+ struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ spin_lock_bh(&c3cn->lock);
+ fn(c3cn, skb);
+ spin_unlock_bh(&c3cn->lock);
+}
+
+/*
+ * process_cpl_msg_ref: -> host
+ * Similar to process_cpl_msg() but takes an extra connection reference around
+ * the call to the handler. Should be used if the handler may drop a
+ * connection reference.
+ */
+static inline void process_cpl_msg_ref(void (*fn) (struct s3_conn *,
+ struct sk_buff *),
+ struct s3_conn *c3cn,
+ struct sk_buff *skb)
+{
+ c3cn_hold(c3cn);
+ process_cpl_msg(fn, c3cn, skb);
+ c3cn_put(c3cn);
+}
+
+/*
+ * Process a CPL_ACT_ESTABLISH message: -> host
+ * Updates connection state from an active establish CPL message. Runs with
+ * the connection lock held.
+ */
+
+static inline void s3_free_atid(struct t3cdev *cdev, unsigned int tid)
+{
+ struct s3_conn *c3cn = cxgb3_free_atid(cdev, tid);
+ if (c3cn)
+ c3cn_put(c3cn);
+}
+
+static void c3cn_established(struct s3_conn *c3cn, u32 snd_isn,
+ unsigned int opt)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ c3cn->write_seq = c3cn->snd_nxt = c3cn->snd_una = snd_isn;
+
+ /*
+ * Causes the first RX_DATA_ACK to supply any Rx credits we couldn't
+ * pass through opt0.
+ */
+ if (cxgb3_rcv_win > (M_RCV_BUFSIZ << 10))
+ c3cn->rcv_wup -= cxgb3_rcv_win - (M_RCV_BUFSIZ << 10);
+
+ dst_confirm(c3cn->dst_cache);
+
+ smp_mb();
+
+ c3cn_set_state(c3cn, C3CN_STATE_ESTABLISHED);
+}
+
+static void process_act_establish(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_act_establish *req = cplhdr(skb);
+ u32 rcv_isn = ntohl(req->rcv_isn); /* real RCV_ISN + 1 */
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (unlikely(c3cn->state != C3CN_STATE_CONNECTING))
+ cxgb3i_log_error("TID %u expected SYN_SENT, got EST., s %u\n",
+ c3cn->tid, c3cn->state);
+
+ c3cn->copied_seq = c3cn->rcv_wup = c3cn->rcv_nxt = rcv_isn;
+ c3cn_established(c3cn, ntohl(req->snd_isn), ntohs(req->tcp_opt));
+
+ __kfree_skb(skb);
+
+ if (unlikely(c3cn_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED)))
+ /* upper layer has requested closing */
+ send_abort_req(c3cn);
+ else if (c3cn_push_tx_frames(c3cn, 1))
+ cxgb3i_conn_tx_open(c3cn);
+}
+
+static int do_act_establish(struct t3cdev *cdev, struct sk_buff *skb,
+ void *ctx)
+{
+ struct cpl_act_establish *req = cplhdr(skb);
+ unsigned int tid = GET_TID(req);
+ unsigned int atid = G_PASS_OPEN_TID(ntohl(req->tos_tid));
+ struct s3_conn *c3cn = ctx;
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev);
+
+ c3cn_conn_debug("rcv, tid 0x%x, c3cn 0x%p, s %u, f 0x%lx.\n",
+ tid, c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->tid = tid;
+ c3cn_hold(c3cn);
+ cxgb3_insert_tid(cdata->cdev, cdata->client, c3cn, tid);
+ s3_free_atid(cdev, atid);
+
+ c3cn->qset = G_QNUM(ntohl(skb->csum));
+
+ process_cpl_msg(process_act_establish, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process a CPL_ACT_OPEN_RPL message: -> host
+ * Handle active open failures.
+ */
+static int act_open_rpl_status_to_errno(int status)
+{
+ switch (status) {
+ case CPL_ERR_CONN_RESET:
+ return ECONNREFUSED;
+ case CPL_ERR_ARP_MISS:
+ return EHOSTUNREACH;
+ case CPL_ERR_CONN_TIMEDOUT:
+ return ETIMEDOUT;
+ case CPL_ERR_TCAM_FULL:
+ return ENOMEM;
+ case CPL_ERR_CONN_EXIST:
+ cxgb3i_log_error("ACTIVE_OPEN_RPL: 4-tuple in use\n");
+ return EADDRINUSE;
+ default:
+ return EIO;
+ }
+}
+
+static void act_open_retry_timer(unsigned long data)
+{
+ struct sk_buff *skb;
+ struct s3_conn *c3cn = (struct s3_conn *)data;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u.\n", c3cn, c3cn->state);
+
+ spin_lock_bh(&c3cn->lock);
+ skb = alloc_skb(sizeof(struct cpl_act_open_req), GFP_ATOMIC);
+ if (!skb)
+ fail_act_open(c3cn, ENOMEM);
+ else {
+ skb->sk = (struct sock *)c3cn;
+ set_arp_failure_handler(skb, act_open_req_arp_failure);
+ make_act_open_req(c3cn, skb, c3cn->tid, c3cn->l2t);
+ l2t_send(c3cn->cdev, skb, c3cn->l2t);
+ }
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+}
+
+static void process_act_open_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_act_open_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (rpl->status == CPL_ERR_CONN_EXIST &&
+ c3cn->retry_timer.function != act_open_retry_timer) {
+ c3cn->retry_timer.function = act_open_retry_timer;
+ if (!mod_timer(&c3cn->retry_timer, jiffies + HZ / 2))
+ c3cn_hold(c3cn);
+ } else
+ fail_act_open(c3cn, act_open_rpl_status_to_errno(rpl->status));
+ __kfree_skb(skb);
+}
+
+static int do_act_open_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+ struct cpl_act_open_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("rcv, status 0x%x, c3cn 0x%p, s %u, f 0x%lx.\n",
+ rpl->status, c3cn, c3cn->state, c3cn->flags);
+
+ if (rpl->status != CPL_ERR_TCAM_FULL &&
+ rpl->status != CPL_ERR_CONN_EXIST &&
+ rpl->status != CPL_ERR_ARP_MISS)
+ cxgb3_queue_tid_release(cdev, GET_TID(rpl));
+
+ process_cpl_msg_ref(process_act_open_rpl, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process PEER_CLOSE CPL messages: -> host
+ * Handle peer FIN.
+ */
+static void process_peer_close(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING))
+ goto out;
+
+ switch (c3cn->state) {
+ case C3CN_STATE_ESTABLISHED:
+ c3cn_set_state(c3cn, C3CN_STATE_PASSIVE_CLOSE);
+ break;
+ case C3CN_STATE_ACTIVE_CLOSE:
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_2);
+ break;
+ case C3CN_STATE_CLOSE_WAIT_1:
+ c3cn_closed(c3cn);
+ break;
+ case C3CN_STATE_ABORTING:
+ break;
+ default:
+ cxgb3i_log_error("%s: peer close, TID %u in bad state %u\n",
+ c3cn->cdev->name, c3cn->tid, c3cn->state);
+ }
+
+ cxgb3i_conn_closing(c3cn);
+out:
+ __kfree_skb(skb);
+}
+
+static int do_peer_close(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ process_cpl_msg_ref(process_peer_close, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process CLOSE_CONN_RPL CPL message: -> host
+ * Process a peer ACK to our FIN.
+ */
+static void process_close_con_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_close_con_rpl *rpl = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ c3cn->snd_una = ntohl(rpl->snd_nxt) - 1; /* exclude FIN */
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING))
+ goto out;
+
+ switch (c3cn->state) {
+ case C3CN_STATE_ACTIVE_CLOSE:
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_1);
+ break;
+ case C3CN_STATE_CLOSE_WAIT_1:
+ case C3CN_STATE_CLOSE_WAIT_2:
+ c3cn_closed(c3cn);
+ break;
+ case C3CN_STATE_ABORTING:
+ break;
+ default:
+ cxgb3i_log_error("%s: close_rpl, TID %u in bad state %u\n",
+ c3cn->cdev->name, c3cn->tid, c3cn->state);
+ }
+
+out:
+ kfree_skb(skb);
+}
+
+static int do_close_con_rpl(struct t3cdev *cdev, struct sk_buff *skb,
+ void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ process_cpl_msg_ref(process_close_con_rpl, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process ABORT_REQ_RSS CPL message: -> host
+ * Process abort requests. If we are waiting for an ABORT_RPL we ignore this
+ * request except that we need to reply to it.
+ */
+
+static int abort_status_to_errno(struct s3_conn *c3cn, int abort_reason,
+ int *need_rst)
+{
+ switch (abort_reason) {
+ case CPL_ERR_BAD_SYN: /* fall through */
+ case CPL_ERR_CONN_RESET:
+ return c3cn->state > C3CN_STATE_ESTABLISHED ?
+ EPIPE : ECONNRESET;
+ case CPL_ERR_XMIT_TIMEDOUT:
+ case CPL_ERR_PERSIST_TIMEDOUT:
+ case CPL_ERR_FINWAIT2_TIMEDOUT:
+ case CPL_ERR_KEEPALIVE_TIMEDOUT:
+ return ETIMEDOUT;
+ default:
+ return EIO;
+ }
+}
+
+static void process_abort_req(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ int rst_status = CPL_ABORT_NO_RST;
+ const struct cpl_abort_req_rss *req = cplhdr(skb);
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (!c3cn_flag(c3cn, C3CN_ABORT_REQ_RCVD)) {
+ c3cn_set_flag(c3cn, C3CN_ABORT_REQ_RCVD);
+ c3cn_set_state(c3cn, C3CN_STATE_ABORTING);
+ __kfree_skb(skb);
+ return;
+ }
+
+ c3cn_clear_flag(c3cn, C3CN_ABORT_REQ_RCVD);
+ send_abort_rpl(c3cn, rst_status);
+
+ if (!c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING)) {
+ c3cn->err =
+ abort_status_to_errno(c3cn, req->status, &rst_status);
+ c3cn_closed(c3cn);
+ }
+}
+
+static int do_abort_req(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ const struct cpl_abort_req_rss *req = cplhdr(skb);
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, c3cn 0x%p, s 0x%x, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (req->status == CPL_ERR_RTX_NEG_ADVICE ||
+ req->status == CPL_ERR_PERSIST_NEG_ADVICE) {
+ __kfree_skb(skb);
+ return 0;
+ }
+
+ process_cpl_msg_ref(process_abort_req, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process ABORT_RPL_RSS CPL message: -> host
+ * Process abort replies. We only process these messages if we anticipate
+ * them as the coordination between SW and HW in this area is somewhat lacking
+ * and sometimes we get ABORT_RPLs after we are done with the connection that
+ * originated the ABORT_REQ.
+ */
+static void process_abort_rpl(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ if (c3cn_flag(c3cn, C3CN_ABORT_RPL_PENDING)) {
+ if (!c3cn_flag(c3cn, C3CN_ABORT_RPL_RCVD))
+ c3cn_set_flag(c3cn, C3CN_ABORT_RPL_RCVD);
+ else {
+ c3cn_clear_flag(c3cn, C3CN_ABORT_RPL_RCVD);
+ c3cn_clear_flag(c3cn, C3CN_ABORT_RPL_PENDING);
+ if (c3cn_flag(c3cn, C3CN_ABORT_REQ_RCVD))
+ cxgb3i_log_error("%s tid %u, ABORT_RPL_RSS\n",
+ c3cn->cdev->name, c3cn->tid);
+ c3cn_closed(c3cn);
+ }
+ }
+ __kfree_skb(skb);
+}
+
+static int do_abort_rpl(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct cpl_abort_rpl_rss *rpl = cplhdr(skb);
+ struct s3_conn *c3cn = ctx;
+
+ c3cn_conn_debug("rcv, status 0x%x, c3cn 0x%p, s %u, 0x%lx.\n",
+ rpl->status, c3cn, c3cn ? c3cn->state : 0,
+ c3cn ? c3cn->flags : 0UL);
+
+ /*
+ * Ignore replies to post-close aborts indicating that the abort was
+ * requested too late. These connections are terminated when we get
+ * PEER_CLOSE or CLOSE_CON_RPL and by the time the abort_rpl_rss
+ * arrives the TID is either no longer used or it has been recycled.
+ */
+ if (rpl->status == CPL_ERR_ABORT_FAILED)
+ goto discard;
+
+ /*
+ * Sometimes we've already closed the connection, e.g., a post-close
+ * abort races with ABORT_REQ_RSS, the latter frees the connection
+ * expecting the ABORT_REQ will fail with CPL_ERR_ABORT_FAILED,
+ * but FW turns the ABORT_REQ into a regular one and so we get
+ * ABORT_RPL_RSS with status 0 and no connection.
+ */
+ if (!c3cn)
+ goto discard;
+
+ process_cpl_msg_ref(process_abort_rpl, c3cn, skb);
+ return 0;
+
+discard:
+ __kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * Process RX_ISCSI_HDR CPL message: -> host
+ * Handle received PDUs, the payload could be DDP'ed. If not, the payload
+ * follow after the bhs.
+ */
+static void process_rx_iscsi_hdr(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_iscsi_hdr *hdr_cpl = cplhdr(skb);
+ struct cpl_iscsi_hdr_norss data_cpl;
+ struct cpl_rx_data_ddp_norss ddp_cpl;
+ unsigned int hdr_len, data_len, status;
+ unsigned int len;
+ int err;
+
+ if (unlikely(c3cn->state >= C3CN_STATE_PASSIVE_CLOSE)) {
+ if (c3cn->state != C3CN_STATE_ABORTING)
+ send_abort_req(c3cn);
+ __kfree_skb(skb);
+ return;
+ }
+
+ CXGB3_SKB_CB(skb)->seq = ntohl(hdr_cpl->seq);
+ CXGB3_SKB_CB(skb)->flags = 0;
+
+ skb_reset_transport_header(skb);
+ __skb_pull(skb, sizeof(struct cpl_iscsi_hdr));
+
+ len = hdr_len = ntohs(hdr_cpl->len);
+ /* msg coalesce is off or not enough data received */
+ if (skb->len <= hdr_len) {
+ cxgb3i_log_error("%s: TID %u, ISCSI_HDR, skb len %u < %u.\n",
+ c3cn->cdev->name, c3cn->tid,
+ skb->len, hdr_len);
+ goto abort_conn;
+ }
+
+ err = skb_copy_bits(skb, skb->len - sizeof(ddp_cpl), &ddp_cpl,
+ sizeof(ddp_cpl));
+ if (err < 0)
+ goto abort_conn;
+
+ skb_ulp_mode(skb) = ULP2_FLAG_DATA_READY;
+ skb_ulp_pdulen(skb) = ntohs(ddp_cpl.len);
+ skb_ulp_ddigest(skb) = ntohl(ddp_cpl.ulp_crc);
+ status = ntohl(ddp_cpl.ddp_status);
+
+ c3cn_rx_debug("rx skb 0x%p, len %u, pdulen %u, ddp status 0x%x.\n",
+ skb, skb->len, skb_ulp_pdulen(skb), status);
+
+ if (status & (1 << RX_DDP_STATUS_HCRC_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_HCRC_ERROR;
+ if (status & (1 << RX_DDP_STATUS_DCRC_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_DCRC_ERROR;
+ if (status & (1 << RX_DDP_STATUS_PAD_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_PAD_ERROR;
+
+ if (skb->len > (hdr_len + sizeof(ddp_cpl))) {
+ err = skb_copy_bits(skb, hdr_len, &data_cpl, sizeof(data_cpl));
+ if (err < 0)
+ goto abort_conn;
+ data_len = ntohs(data_cpl.len);
+ len += sizeof(data_cpl) + data_len;
+ } else if (status & (1 << RX_DDP_STATUS_DDP_SHIFT))
+ skb_ulp_mode(skb) |= ULP2_FLAG_DATA_DDPED;
+
+ c3cn->rcv_nxt = ntohl(ddp_cpl.seq) + skb_ulp_pdulen(skb);
+ __pskb_trim(skb, len);
+ __skb_queue_tail(&c3cn->receive_queue, skb);
+ cxgb3i_conn_pdu_ready(c3cn);
+
+ return;
+
+abort_conn:
+ send_abort_req(c3cn);
+ __kfree_skb(skb);
+}
+
+static int do_iscsi_hdr(struct t3cdev *t3dev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ process_cpl_msg(process_rx_iscsi_hdr, c3cn, skb);
+ return 0;
+}
+
+/*
+ * Process TX_DATA_ACK CPL messages: -> host
+ * Process an acknowledgment of WR completion. Advance snd_una and send the
+ * next batch of work requests from the write queue.
+ */
+static void process_wr_ack(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct cpl_wr_ack *hdr = cplhdr(skb);
+ unsigned int credits = ntohs(hdr->credits);
+ u32 snd_una = ntohl(hdr->snd_una);
+
+ c3cn->wr_avail += credits;
+ if (c3cn->wr_unacked > c3cn->wr_max - c3cn->wr_avail)
+ c3cn->wr_unacked = c3cn->wr_max - c3cn->wr_avail;
+
+ while (credits) {
+ struct sk_buff *p = peek_wr(c3cn);
+
+ if (unlikely(!p)) {
+ cxgb3i_log_error("%u WR_ACK credits for TID %u with "
+ "nothing pending, state %u\n",
+ credits, c3cn->tid, c3cn->state);
+ break;
+ }
+ if (unlikely(credits < p->csum)) {
+ p->csum -= credits;
+ break;
+ } else {
+ dequeue_wr(c3cn);
+ credits -= p->csum;
+ free_wr_skb(p);
+ }
+ }
+
+ if (unlikely(before(snd_una, c3cn->snd_una)))
+ goto out_free;
+
+ if (c3cn->snd_una != snd_una) {
+ c3cn->snd_una = snd_una;
+ dst_confirm(c3cn->dst_cache);
+ }
+
+ if (skb_queue_len(&c3cn->write_queue) && c3cn_push_tx_frames(c3cn, 0))
+ cxgb3i_conn_tx_open(c3cn);
+out_free:
+ __kfree_skb(skb);
+}
+
+static int do_wr_ack(struct t3cdev *cdev, struct sk_buff *skb, void *ctx)
+{
+ struct s3_conn *c3cn = ctx;
+
+ process_cpl_msg(process_wr_ack, c3cn, skb);
+ return 0;
+}
+
+/*
+ * for each connection, pre-allocate skbs needed for close/abort requests. So
+ * that we can service the request right away.
+ */
+static void c3cn_free_cpl_skbs(struct s3_conn *c3cn)
+{
+ if (c3cn->cpl_close)
+ kfree_skb(c3cn->cpl_close);
+ if (c3cn->cpl_abort_req)
+ kfree_skb(c3cn->cpl_abort_req);
+ if (c3cn->cpl_abort_rpl)
+ kfree_skb(c3cn->cpl_abort_rpl);
+}
+
+static int c3cn_alloc_cpl_skbs(struct s3_conn *c3cn)
+{
+ c3cn->cpl_close = alloc_skb(sizeof(struct cpl_close_con_req),
+ GFP_KERNEL);
+ if (!c3cn->cpl_close)
+ return -ENOMEM;
+ skb_put(c3cn->cpl_close, sizeof(struct cpl_close_con_req));
+
+ c3cn->cpl_abort_req = alloc_skb(sizeof(struct cpl_abort_req),
+ GFP_KERNEL);
+ if (!c3cn->cpl_abort_req)
+ goto free_cpl_skbs;
+ skb_put(c3cn->cpl_abort_req, sizeof(struct cpl_abort_req));
+
+ c3cn->cpl_abort_rpl = alloc_skb(sizeof(struct cpl_abort_rpl),
+ GFP_KERNEL);
+ if (!c3cn->cpl_abort_rpl)
+ goto free_cpl_skbs;
+ skb_put(c3cn->cpl_abort_rpl, sizeof(struct cpl_abort_rpl));
+
+ return 0;
+
+free_cpl_skbs:
+ c3cn_free_cpl_skbs(c3cn);
+ return -ENOMEM;
+}
+
+/**
+ * c3cn_release_offload_resources - release offload resource
+ * @c3cn: the offloaded iscsi tcp connection.
+ * Release resources held by an offload connection (TID, L2T entry, etc.)
+ */
+static void c3cn_release_offload_resources(struct s3_conn *c3cn)
+{
+ struct t3cdev *cdev = c3cn->cdev;
+ unsigned int tid = c3cn->tid;
+
+ if (!cdev)
+ return;
+
+ c3cn->qset = 0;
+
+ c3cn_free_cpl_skbs(c3cn);
+
+ if (c3cn->wr_avail != c3cn->wr_max) {
+ purge_wr_queue(c3cn);
+ reset_wr_list(c3cn);
+ }
+
+ if (c3cn->l2t) {
+ l2t_release(L2DATA(cdev), c3cn->l2t);
+ c3cn->l2t = NULL;
+ }
+
+ if (c3cn->state == C3CN_STATE_CONNECTING) /* we have ATID */
+ s3_free_atid(cdev, tid);
+ else { /* we have TID */
+ cxgb3_remove_tid(cdev, (void *)c3cn, tid);
+ c3cn_put(c3cn);
+ }
+
+ c3cn->cdev = NULL;
+}
+
+/**
+ * cxgb3i_c3cn_create - allocate and initialize an s3_conn structure
+ * returns the s3_conn structure allocated.
+ */
+struct s3_conn *cxgb3i_c3cn_create(void)
+{
+ struct s3_conn *c3cn;
+
+ c3cn = kzalloc(sizeof(*c3cn), GFP_KERNEL);
+ if (!c3cn)
+ return NULL;
+
+ /* pre-allocate close/abort cpl, so we don't need to wait for memory
+ when close/abort is requested. */
+ if (c3cn_alloc_cpl_skbs(c3cn) < 0)
+ goto free_c3cn;
+
+ c3cn_conn_debug("alloc c3cn 0x%p.\n", c3cn);
+
+ c3cn->flags = 0;
+ spin_lock_init(&c3cn->lock);
+ atomic_set(&c3cn->refcnt, 1);
+ skb_queue_head_init(&c3cn->receive_queue);
+ skb_queue_head_init(&c3cn->write_queue);
+ setup_timer(&c3cn->retry_timer, NULL, (unsigned long)c3cn);
+ rwlock_init(&c3cn->callback_lock);
+
+ return c3cn;
+
+free_c3cn:
+ kfree(c3cn);
+ return NULL;
+}
+
+static void c3cn_active_close(struct s3_conn *c3cn)
+{
+ int data_lost;
+ int close_req = 0;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+
+ dst_confirm(c3cn->dst_cache);
+
+ c3cn_hold(c3cn);
+ spin_lock_bh(&c3cn->lock);
+
+ data_lost = skb_queue_len(&c3cn->receive_queue);
+ __skb_queue_purge(&c3cn->receive_queue);
+
+ switch (c3cn->state) {
+ case C3CN_STATE_CLOSED:
+ case C3CN_STATE_ACTIVE_CLOSE:
+ case C3CN_STATE_CLOSE_WAIT_1:
+ case C3CN_STATE_CLOSE_WAIT_2:
+ case C3CN_STATE_ABORTING:
+ /* nothing need to be done */
+ break;
+ case C3CN_STATE_CONNECTING:
+ /* defer until cpl_act_open_rpl or cpl_act_establish */
+ c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
+ break;
+ case C3CN_STATE_ESTABLISHED:
+ close_req = 1;
+ c3cn_set_state(c3cn, C3CN_STATE_ACTIVE_CLOSE);
+ break;
+ case C3CN_STATE_PASSIVE_CLOSE:
+ close_req = 1;
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSE_WAIT_2);
+ break;
+ }
+
+ if (close_req) {
+ if (data_lost)
+ /* Unread data was tossed, zap the connection. */
+ send_abort_req(c3cn);
+ else
+ send_close_req(c3cn);
+ }
+
+ spin_unlock_bh(&c3cn->lock);
+ c3cn_put(c3cn);
+}
+
+/**
+ * cxgb3i_c3cn_release - close and release an iscsi tcp connection and any
+ * resource held
+ * @c3cn: the iscsi tcp connection
+ */
+void cxgb3i_c3cn_release(struct s3_conn *c3cn)
+{
+ c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ if (likely(c3cn->state != C3CN_STATE_CONNECTING))
+ c3cn_active_close(c3cn);
+ else
+ c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED);
+ c3cn_put(c3cn);
+}
+
+static int is_cxgb3_dev(struct net_device *dev)
+{
+ struct cxgb3i_sdev_data *cdata;
+
+ write_lock(&cdata_rwlock);
+ list_for_each_entry(cdata, &cdata_list, list) {
+ struct adap_ports *ports = &cdata->ports;
+ int i;
+
+ for (i = 0; i < ports->nports; i++)
+ if (dev == ports->lldevs[i]) {
+ write_unlock(&cdata_rwlock);
+ return 1;
+ }
+ }
+ write_unlock(&cdata_rwlock);
+ return 0;
+}
+
+/**
+ * cxgb3_egress_dev - return the cxgb3 egress device
+ * @root_dev: the root device anchoring the search
+ * @c3cn: the connection used to determine egress port in bonding mode
+ * @context: in bonding mode, indicates a connection set up or failover
+ *
+ * Return egress device or NULL if the egress device isn't one of our ports.
+ */
+static struct net_device *cxgb3_egress_dev(struct net_device *root_dev,
+ struct s3_conn *c3cn,
+ int context)
+{
+ while (root_dev) {
+ if (root_dev->priv_flags & IFF_802_1Q_VLAN)
+ root_dev = vlan_dev_real_dev(root_dev);
+ else if (is_cxgb3_dev(root_dev))
+ return root_dev;
+ else
+ return NULL;
+ }
+ return NULL;
+}
+
+static struct rtable *find_route(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport)
+{
+ struct rtable *rt;
+ struct flowi fl = {
+ .oif = 0,
+ .nl_u = {
+ .ip4_u = {
+ .daddr = daddr,
+ .saddr = saddr,
+ .tos = 0 } },
+ .proto = IPPROTO_TCP,
+ .uli_u = {
+ .ports = {
+ .sport = sport,
+ .dport = dport } } };
+
+ if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0))
+ return NULL;
+ return rt;
+}
+
+/*
+ * Assign offload parameters to some connection fields.
+ */
+static void init_offload_conn(struct s3_conn *c3cn,
+ struct t3cdev *cdev,
+ struct dst_entry *dst)
+{
+ BUG_ON(c3cn->cdev != cdev);
+ c3cn->wr_max = c3cn->wr_avail = T3C_DATA(cdev)->max_wrs;
+ c3cn->wr_unacked = 0;
+ c3cn->mss_idx = select_mss(c3cn, dst_mtu(dst));
+
+ reset_wr_list(c3cn);
+}
+
+static int initiate_act_open(struct s3_conn *c3cn, struct net_device *dev)
+{
+ struct cxgb3i_sdev_data *cdata = NDEV2CDATA(dev);
+ struct t3cdev *cdev = cdata->cdev;
+ struct dst_entry *dst = c3cn->dst_cache;
+ struct sk_buff *skb;
+
+ c3cn_conn_debug("c3cn 0x%p, state %u, flag 0x%lx.\n",
+ c3cn, c3cn->state, c3cn->flags);
+ /*
+ * Initialize connection data. Note that the flags and ULP mode are
+ * initialized higher up ...
+ */
+ c3cn->dev = dev;
+ c3cn->cdev = cdev;
+ c3cn->tid = cxgb3_alloc_atid(cdev, cdata->client, c3cn);
+ if (c3cn->tid < 0)
+ goto out_err;
+
+ c3cn->qset = 0;
+ c3cn->l2t = t3_l2t_get(cdev, dst->neighbour, dev);
+ if (!c3cn->l2t)
+ goto free_tid;
+
+ skb = alloc_skb(sizeof(struct cpl_act_open_req), GFP_KERNEL);
+ if (!skb)
+ goto free_l2t;
+
+ skb->sk = (struct sock *)c3cn;
+ set_arp_failure_handler(skb, act_open_req_arp_failure);
+
+ c3cn_hold(c3cn);
+
+ init_offload_conn(c3cn, cdev, dst);
+ c3cn->err = 0;
+
+ make_act_open_req(c3cn, skb, c3cn->tid, c3cn->l2t);
+ l2t_send(cdev, skb, c3cn->l2t);
+ return 0;
+
+free_l2t:
+ l2t_release(L2DATA(cdev), c3cn->l2t);
+free_tid:
+ s3_free_atid(cdev, c3cn->tid);
+ c3cn->tid = 0;
+out_err:
+ return -1;
+}
+
+
+/**
+ * cxgb3i_c3cn_connect - initiates an iscsi tcp connection to a given address
+ * @c3cn: the iscsi tcp connection
+ * @usin: destination address
+ *
+ * return 0 if active open request is sent, < 0 otherwise.
+ */
+int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin)
+{
+ struct rtable *rt;
+ struct net_device *dev;
+ struct cxgb3i_sdev_data *cdata;
+ struct t3cdev *cdev;
+ __be32 sipv4;
+ int err;
+
+ if (usin->sin_family != AF_INET)
+ return -EAFNOSUPPORT;
+
+ c3cn->daddr.sin_port = usin->sin_port;
+ c3cn->daddr.sin_addr.s_addr = usin->sin_addr.s_addr;
+
+ rt = find_route(c3cn->saddr.sin_addr.s_addr,
+ c3cn->daddr.sin_addr.s_addr,
+ c3cn->saddr.sin_port,
+ c3cn->daddr.sin_port);
+ if (rt == NULL) {
+ c3cn_conn_debug("NO route to 0x%x, port %u.\n",
+ c3cn->daddr.sin_addr.s_addr,
+ ntohs(c3cn->daddr.sin_port));
+ return -ENETUNREACH;
+ }
+
+ if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
+ c3cn_conn_debug("multi-cast route to 0x%x, port %u.\n",
+ c3cn->daddr.sin_addr.s_addr,
+ ntohs(c3cn->daddr.sin_port));
+ ip_rt_put(rt);
+ return -ENETUNREACH;
+ }
+
+ if (!c3cn->saddr.sin_addr.s_addr)
+ c3cn->saddr.sin_addr.s_addr = rt->rt_src;
+
+ /* now commit destination to connection */
+ c3cn->dst_cache = &rt->u.dst;
+
+ /* try to establish an offloaded connection */
+ dev = cxgb3_egress_dev(c3cn->dst_cache->dev, c3cn, 0);
+ if (dev == NULL) {
+ c3cn_conn_debug("c3cn 0x%p, egress dev NULL.\n", c3cn);
+ return -ENETUNREACH;
+ }
+ cdata = NDEV2CDATA(dev);
+ cdev = cdata->cdev;
+
+ /* get a source port if one hasn't been provided */
+ err = c3cn_get_port(c3cn, cdata);
+ if (err)
+ return err;
+
+ c3cn_conn_debug("c3cn 0x%p get port %u.\n",
+ c3cn, ntohs(c3cn->saddr.sin_port));
+
+ sipv4 = cxgb3i_get_private_ipv4addr(dev);
+ if (!sipv4) {
+ c3cn_conn_debug("c3cn 0x%p, iscsi ip not configured.\n", c3cn);
+ sipv4 = c3cn->saddr.sin_addr.s_addr;
+ cxgb3i_set_private_ipv4addr(dev, sipv4);
+ } else
+ c3cn->saddr.sin_addr.s_addr = sipv4;
+
+ c3cn_conn_debug("c3cn 0x%p, %u.%u.%u.%u,%u-%u.%u.%u.%u,%u SYN_SENT.\n",
+ c3cn, NIPQUAD(c3cn->saddr.sin_addr.s_addr),
+ ntohs(c3cn->saddr.sin_port),
+ NIPQUAD(c3cn->daddr.sin_addr.s_addr),
+ ntohs(c3cn->daddr.sin_port));
+
+ c3cn_set_state(c3cn, C3CN_STATE_CONNECTING);
+ if (!initiate_act_open(c3cn, dev))
+ return 0;
+
+ /*
+ * If we get here, we don't have an offload connection so simply
+ * return a failure.
+ */
+ err = -ENOTSUPP;
+
+ /*
+ * This trashes the connection and releases the local port,
+ * if necessary.
+ */
+ c3cn_conn_debug("c3cn 0x%p -> CLOSED.\n", c3cn);
+ c3cn_set_state(c3cn, C3CN_STATE_CLOSED);
+ ip_rt_put(rt);
+ c3cn_put_port(c3cn);
+ c3cn->daddr.sin_port = 0;
+ return err;
+}
+
+/**
+ * cxgb3i_c3cn_rx_credits - ack received tcp data.
+ * @c3cn: iscsi tcp connection
+ * @copied: # of bytes processed
+ *
+ * Called after some received data has been read. It returns RX credits
+ * to the HW for the amount of data processed.
+ */
+void cxgb3i_c3cn_rx_credits(struct s3_conn *c3cn, int copied)
+{
+ struct t3cdev *cdev;
+ int must_send;
+ u32 credits, dack = 0;
+
+ if (c3cn->state != C3CN_STATE_ESTABLISHED)
+ return;
+
+ credits = c3cn->copied_seq - c3cn->rcv_wup;
+ if (unlikely(!credits))
+ return;
+
+ cdev = c3cn->cdev;
+
+ if (unlikely(cxgb3_rx_credit_thres == 0))
+ return;
+
+ dack = F_RX_DACK_CHANGE | V_RX_DACK_MODE(1);
+
+ /*
+ * For coalescing to work effectively ensure the receive window has
+ * at least 16KB left.
+ */
+ must_send = credits + 16384 >= cxgb3_rcv_win;
+
+ if (must_send || credits >= cxgb3_rx_credit_thres)
+ c3cn->rcv_wup += send_rx_credits(c3cn, credits, dack);
+}
+
+/**
+ * cxgb3i_c3cn_send_pdus - send the skbs containing iscsi pdus
+ * @c3cn: iscsi tcp connection
+ * @skb: skb contains the iscsi pdu
+ *
+ * Add a list of skbs to a connection send queue. The skbs must comply with
+ * the max size limit of the device and have a headroom of at least
+ * TX_HEADER_LEN bytes.
+ * Return # of bytes queued.
+ */
+int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb)
+{
+ struct sk_buff *next;
+ int err, copied = 0;
+
+ spin_lock_bh(&c3cn->lock);
+
+ if (c3cn->state != C3CN_STATE_ESTABLISHED) {
+ c3cn_tx_debug("c3cn 0x%p, not in est. state %u.\n",
+ c3cn, c3cn->state);
+ err = -EAGAIN;
+ goto out_err;
+ }
+
+ err = -EPIPE;
+ if (c3cn->err) {
+ c3cn_tx_debug("c3cn 0x%p, err %d.\n", c3cn, c3cn->err);
+ goto out_err;
+ }
+
+ while (skb) {
+ int frags = skb_shinfo(skb)->nr_frags +
+ (skb->len != skb->data_len);
+
+ if (unlikely(skb_headroom(skb) < TX_HEADER_LEN)) {
+ c3cn_tx_debug("c3cn 0x%p, skb head.\n", c3cn);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (frags >= SKB_WR_LIST_SIZE) {
+ cxgb3i_log_error("c3cn 0x%p, tx frags %d, len %u,%u.\n",
+ c3cn, skb_shinfo(skb)->nr_frags,
+ skb->len, skb->data_len);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ next = skb->next;
+ skb->next = NULL;
+ skb_entail(c3cn, skb, C3CB_FLAG_NO_APPEND | C3CB_FLAG_NEED_HDR);
+ copied += skb->len;
+ c3cn->write_seq += skb->len + ulp_extra_len(skb);
+ skb = next;
+ }
+done:
+ if (likely(skb_queue_len(&c3cn->write_queue)))
+ c3cn_push_tx_frames(c3cn, 1);
+ spin_unlock_bh(&c3cn->lock);
+ return copied;
+
+out_err:
+ if (copied == 0 && err == -EPIPE)
+ copied = c3cn->err ? c3cn->err : -EPIPE;
+ goto done;
+}
+
+static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata)
+{
+ struct adap_ports *ports = &cdata->ports;
+ int i;
+
+ for (i = 0; i < ports->nports; i++)
+ NDEV2CDATA(ports->lldevs[i]) = NULL;
+ cxgb3i_free_big_mem(cdata);
+}
+
+void cxgb3i_sdev_cleanup(void)
+{
+ struct cxgb3i_sdev_data *cdata;
+
+ write_lock(&cdata_rwlock);
+ list_for_each_entry(cdata, &cdata_list, list) {
+ list_del(&cdata->list);
+ sdev_data_cleanup(cdata);
+ }
+ write_unlock(&cdata_rwlock);
+}
+
+int cxgb3i_sdev_init(cxgb3_cpl_handler_func *cpl_handlers)
+{
+ cpl_handlers[CPL_ACT_ESTABLISH] = do_act_establish;
+ cpl_handlers[CPL_ACT_OPEN_RPL] = do_act_open_rpl;
+ cpl_handlers[CPL_PEER_CLOSE] = do_peer_close;
+ cpl_handlers[CPL_ABORT_REQ_RSS] = do_abort_req;
+ cpl_handlers[CPL_ABORT_RPL_RSS] = do_abort_rpl;
+ cpl_handlers[CPL_CLOSE_CON_RPL] = do_close_con_rpl;
+ cpl_handlers[CPL_TX_DMA_ACK] = do_wr_ack;
+ cpl_handlers[CPL_ISCSI_HDR] = do_iscsi_hdr;
+
+ if (cxgb3_max_connect > CXGB3I_MAX_CONN)
+ cxgb3_max_connect = CXGB3I_MAX_CONN;
+ return 0;
+}
+
+/**
+ * cxgb3i_sdev_add - allocate and initialize resources for each adapter found
+ * @cdev: t3cdev adapter
+ * @client: cxgb3 driver client
+ */
+void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client)
+{
+ struct cxgb3i_sdev_data *cdata;
+ struct ofld_page_info rx_page_info;
+ unsigned int wr_len;
+ int mapsize = DIV_ROUND_UP(cxgb3_max_connect,
+ 8 * sizeof(unsigned long));
+ int i;
+
+ cdata = cxgb3i_alloc_big_mem(sizeof(*cdata) + mapsize, GFP_KERNEL);
+ if (!cdata)
+ return;
+
+ if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 ||
+ cdev->ctl(cdev, GET_PORTS, &cdata->ports) < 0 ||
+ cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0)
+ goto free_cdata;
+
+ s3_init_wr_tab(wr_len);
+
+ INIT_LIST_HEAD(&cdata->list);
+ cdata->cdev = cdev;
+ cdata->client = client;
+
+ for (i = 0; i < cdata->ports.nports; i++)
+ NDEV2CDATA(cdata->ports.lldevs[i]) = cdata;
+
+ write_lock(&cdata_rwlock);
+ list_add_tail(&cdata->list, &cdata_list);
+ write_unlock(&cdata_rwlock);
+
+ return;
+
+free_cdata:
+ cxgb3i_free_big_mem(cdata);
+}
+
+/**
+ * cxgb3i_sdev_remove - free the allocated resources for the adapter
+ * @cdev: t3cdev adapter
+ */
+void cxgb3i_sdev_remove(struct t3cdev *cdev)
+{
+ struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev);
+
+ write_lock(&cdata_rwlock);
+ list_del(&cdata->list);
+ write_unlock(&cdata_rwlock);
+
+ sdev_data_cleanup(cdata);
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.h b/drivers/scsi/cxgb3i/cxgb3i_offload.h
new file mode 100644
index 000000000000..d23156907ffd
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_offload.h
@@ -0,0 +1,231 @@
+/*
+ * cxgb3i_offload.h: Chelsio S3xx iscsi offloaded tcp connection management
+ *
+ * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
+ * release for licensing terms and conditions.
+ *
+ * Written by: Dimitris Michailidis (dm@chelsio.com)
+ * Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef _CXGB3I_OFFLOAD_H
+#define _CXGB3I_OFFLOAD_H
+
+#include <linux/skbuff.h>
+#include <net/tcp.h>
+
+#include "common.h"
+#include "adapter.h"
+#include "t3cdev.h"
+#include "cxgb3_offload.h"
+
+#define cxgb3i_log_error(fmt...) printk(KERN_ERR "cxgb3i: ERR! " fmt)
+#define cxgb3i_log_warn(fmt...) printk(KERN_WARNING "cxgb3i: WARN! " fmt)
+#define cxgb3i_log_info(fmt...) printk(KERN_INFO "cxgb3i: " fmt)
+#define cxgb3i_log_debug(fmt, args...) \
+ printk(KERN_INFO "cxgb3i: %s - " fmt, __func__ , ## args)
+
+/**
+ * struct s3_conn - an iscsi tcp connection structure
+ *
+ * @dev: net device of with connection
+ * @cdev: adapter t3cdev for net device
+ * @flags: see c3cn_flags below
+ * @tid: connection id assigned by the h/w
+ * @qset: queue set used by connection
+ * @mss_idx: Maximum Segment Size table index
+ * @l2t: ARP resolution entry for offload packets
+ * @wr_max: maximum in-flight writes
+ * @wr_avail: number of writes available
+ * @wr_unacked: writes since last request for completion notification
+ * @wr_pending_head: head of pending write queue
+ * @wr_pending_tail: tail of pending write queue
+ * @cpl_close: skb for cpl_close_req
+ * @cpl_abort_req: skb for cpl_abort_req
+ * @cpl_abort_rpl: skb for cpl_abort_rpl
+ * @lock: connection status lock
+ * @refcnt: reference count on connection
+ * @state: connection state
+ * @saddr: source ip/port address
+ * @daddr: destination ip/port address
+ * @dst_cache: reference to destination route
+ * @receive_queue: received PDUs
+ * @write_queue: un-pushed pending writes
+ * @retry_timer: retry timer for various operations
+ * @err: connection error status
+ * @callback_lock: lock for opaque user context
+ * @user_data: opaque user context
+ * @rcv_nxt: next receive seq. #
+ * @copied_seq: head of yet unread data
+ * @rcv_wup: rcv_nxt on last window update sent
+ * @snd_nxt: next sequence we send
+ * @snd_una: first byte we want an ack for
+ * @write_seq: tail+1 of data held in send buffer
+ */
+struct s3_conn {
+ struct net_device *dev;
+ struct t3cdev *cdev;
+ unsigned long flags;
+ int tid;
+ int qset;
+ int mss_idx;
+ struct l2t_entry *l2t;
+ int wr_max;
+ int wr_avail;
+ int wr_unacked;
+ struct sk_buff *wr_pending_head;
+ struct sk_buff *wr_pending_tail;
+ struct sk_buff *cpl_close;
+ struct sk_buff *cpl_abort_req;
+ struct sk_buff *cpl_abort_rpl;
+ spinlock_t lock;
+ atomic_t refcnt;
+ volatile unsigned int state;
+ struct sockaddr_in saddr;
+ struct sockaddr_in daddr;
+ struct dst_entry *dst_cache;
+ struct sk_buff_head receive_queue;
+ struct sk_buff_head write_queue;
+ struct timer_list retry_timer;
+ int err;
+ rwlock_t callback_lock;
+ void *user_data;
+
+ u32 rcv_nxt;
+ u32 copied_seq;
+ u32 rcv_wup;
+ u32 snd_nxt;
+ u32 snd_una;
+ u32 write_seq;
+};
+
+/*
+ * connection state
+ */
+enum conn_states {
+ C3CN_STATE_CONNECTING = 1,
+ C3CN_STATE_ESTABLISHED,
+ C3CN_STATE_ACTIVE_CLOSE,
+ C3CN_STATE_PASSIVE_CLOSE,
+ C3CN_STATE_CLOSE_WAIT_1,
+ C3CN_STATE_CLOSE_WAIT_2,
+ C3CN_STATE_ABORTING,
+ C3CN_STATE_CLOSED,
+};
+
+static inline unsigned int c3cn_is_closing(const struct s3_conn *c3cn)
+{
+ return c3cn->state >= C3CN_STATE_ACTIVE_CLOSE;
+}
+static inline unsigned int c3cn_is_established(const struct s3_conn *c3cn)
+{
+ return c3cn->state == C3CN_STATE_ESTABLISHED;
+}
+
+/*
+ * Connection flags -- many to track some close related events.
+ */
+enum c3cn_flags {
+ C3CN_ABORT_RPL_RCVD, /* received one ABORT_RPL_RSS message */
+ C3CN_ABORT_REQ_RCVD, /* received one ABORT_REQ_RSS message */
+ C3CN_ABORT_RPL_PENDING, /* expecting an abort reply */
+ C3CN_TX_DATA_SENT, /* already sent a TX_DATA WR */
+ C3CN_ACTIVE_CLOSE_NEEDED, /* need to be closed */
+};
+
+/**
+ * cxgb3i_sdev_data - Per adapter data.
+ * Linked off of each Ethernet device port on the adapter.
+ * Also available via the t3cdev structure since we have pointers to our port
+ * net_device's there ...
+ *
+ * @list: list head to link elements
+ * @cdev: t3cdev adapter
+ * @client: CPL client pointer
+ * @ports: array of adapter ports
+ * @sport_map_next: next index into the port map
+ * @sport_map: source port map
+ */
+struct cxgb3i_sdev_data {
+ struct list_head list;
+ struct t3cdev *cdev;
+ struct cxgb3_client *client;
+ struct adap_ports ports;
+ unsigned int sport_map_next;
+ unsigned long sport_map[0];
+};
+#define NDEV2CDATA(ndev) (*(struct cxgb3i_sdev_data **)&(ndev)->ec_ptr)
+#define CXGB3_SDEV_DATA(cdev) NDEV2CDATA((cdev)->lldev)
+
+void cxgb3i_sdev_cleanup(void);
+int cxgb3i_sdev_init(cxgb3_cpl_handler_func *);
+void cxgb3i_sdev_add(struct t3cdev *, struct cxgb3_client *);
+void cxgb3i_sdev_remove(struct t3cdev *);
+
+struct s3_conn *cxgb3i_c3cn_create(void);
+int cxgb3i_c3cn_connect(struct s3_conn *, struct sockaddr_in *);
+void cxgb3i_c3cn_rx_credits(struct s3_conn *, int);
+int cxgb3i_c3cn_send_pdus(struct s3_conn *, struct sk_buff *);
+void cxgb3i_c3cn_release(struct s3_conn *);
+
+/**
+ * cxgb3_skb_cb - control block for received pdu state and ULP mode management.
+ *
+ * @flag: see C3CB_FLAG_* below
+ * @ulp_mode: ULP mode/submode of sk_buff
+ * @seq: tcp sequence number
+ * @ddigest: pdu data digest
+ * @pdulen: recovered pdu length
+ * @wr_data: scratch area for tx wr
+ */
+struct cxgb3_skb_cb {
+ __u8 flags;
+ __u8 ulp_mode;
+ __u32 seq;
+ __u32 ddigest;
+ __u32 pdulen;
+ struct sk_buff *wr_data;
+};
+
+#define CXGB3_SKB_CB(skb) ((struct cxgb3_skb_cb *)&((skb)->cb[0]))
+
+#define skb_ulp_mode(skb) (CXGB3_SKB_CB(skb)->ulp_mode)
+#define skb_ulp_ddigest(skb) (CXGB3_SKB_CB(skb)->ddigest)
+#define skb_ulp_pdulen(skb) (CXGB3_SKB_CB(skb)->pdulen)
+#define skb_wr_data(skb) (CXGB3_SKB_CB(skb)->wr_data)
+
+enum c3cb_flags {
+ C3CB_FLAG_NEED_HDR = 1 << 0, /* packet needs a TX_DATA_WR header */
+ C3CB_FLAG_NO_APPEND = 1 << 1, /* don't grow this skb */
+ C3CB_FLAG_COMPL = 1 << 2, /* request WR completion */
+};
+
+/**
+ * sge_opaque_hdr -
+ * Opaque version of structure the SGE stores at skb->head of TX_DATA packets
+ * and for which we must reserve space.
+ */
+struct sge_opaque_hdr {
+ void *dev;
+ dma_addr_t addr[MAX_SKB_FRAGS + 1];
+};
+
+/* for TX: a skb must have a headroom of at least TX_HEADER_LEN bytes */
+#define TX_HEADER_LEN \
+ (sizeof(struct tx_data_wr) + sizeof(struct sge_opaque_hdr))
+
+/*
+ * get and set private ip for iscsi traffic
+ */
+#define cxgb3i_get_private_ipv4addr(ndev) \
+ (((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr)
+#define cxgb3i_set_private_ipv4addr(ndev, addr) \
+ (((struct port_info *)(netdev_priv(ndev)))->iscsi_ipv4addr) = addr
+
+/* max. connections per adapter */
+#define CXGB3I_MAX_CONN 16384
+#endif /* _CXGB3_OFFLOAD_H */
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
new file mode 100644
index 000000000000..ce7ce8c6094c
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
@@ -0,0 +1,402 @@
+/*
+ * cxgb3i_pdu.c: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008 Mike Christie
+ * Copyright (c) 2008 Red Hat, 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.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#include <linux/skbuff.h>
+#include <linux/crypto.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+
+#include "cxgb3i.h"
+#include "cxgb3i_pdu.h"
+
+#ifdef __DEBUG_CXGB3I_RX__
+#define cxgb3i_rx_debug cxgb3i_log_debug
+#else
+#define cxgb3i_rx_debug(fmt...)
+#endif
+
+#ifdef __DEBUG_CXGB3I_TX__
+#define cxgb3i_tx_debug cxgb3i_log_debug
+#else
+#define cxgb3i_tx_debug(fmt...)
+#endif
+
+static struct page *pad_page;
+
+/*
+ * pdu receive, interact with libiscsi_tcp
+ */
+static inline int read_pdu_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+ unsigned int offset, int offloaded)
+{
+ int status = 0;
+ int bytes_read;
+
+ bytes_read = iscsi_tcp_recv_skb(conn, skb, offset, offloaded, &status);
+ switch (status) {
+ case ISCSI_TCP_CONN_ERR:
+ return -EIO;
+ case ISCSI_TCP_SUSPENDED:
+ /* no transfer - just have caller flush queue */
+ return bytes_read;
+ case ISCSI_TCP_SKB_DONE:
+ /*
+ * pdus should always fit in the skb and we should get
+ * segment done notifcation.
+ */
+ iscsi_conn_printk(KERN_ERR, conn, "Invalid pdu or skb.");
+ return -EFAULT;
+ case ISCSI_TCP_SEGMENT_DONE:
+ return bytes_read;
+ default:
+ iscsi_conn_printk(KERN_ERR, conn, "Invalid iscsi_tcp_recv_skb "
+ "status %d\n", status);
+ return -EINVAL;
+ }
+}
+
+static int cxgb3i_conn_read_pdu_skb(struct iscsi_conn *conn,
+ struct sk_buff *skb)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ bool offloaded = 0;
+ unsigned int offset;
+ int rc;
+
+ cxgb3i_rx_debug("conn 0x%p, skb 0x%p, len %u, flag 0x%x.\n",
+ conn, skb, skb->len, skb_ulp_mode(skb));
+
+ if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_PROTO);
+ return -EIO;
+ }
+
+ if (conn->hdrdgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_HCRC_ERROR)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
+ return -EIO;
+ }
+
+ if (conn->datadgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) {
+ iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
+ return -EIO;
+ }
+
+ /* iscsi hdr */
+ rc = read_pdu_skb(conn, skb, 0, 0);
+ if (rc <= 0)
+ return rc;
+
+ if (iscsi_tcp_recv_segment_is_hdr(tcp_conn))
+ return 0;
+
+ offset = rc;
+ if (conn->hdrdgst_en)
+ offset += ISCSI_DIGEST_SIZE;
+
+ /* iscsi data */
+ if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) {
+ cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, ddp'ed, "
+ "itt 0x%x.\n",
+ skb,
+ tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
+ tcp_conn->in.datalen,
+ ntohl(tcp_conn->in.hdr->itt));
+ offloaded = 1;
+ } else {
+ cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, NOT ddp'ed, "
+ "itt 0x%x.\n",
+ skb,
+ tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
+ tcp_conn->in.datalen,
+ ntohl(tcp_conn->in.hdr->itt));
+ offset += sizeof(struct cpl_iscsi_hdr_norss);
+ }
+
+ rc = read_pdu_skb(conn, skb, offset, offloaded);
+ if (rc < 0)
+ return rc;
+ else
+ return 0;
+}
+
+/*
+ * pdu transmit, interact with libiscsi_tcp
+ */
+static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
+{
+ u8 submode = 0;
+
+ if (hcrc)
+ submode |= 1;
+ if (dcrc)
+ submode |= 2;
+ skb_ulp_mode(skb) = (ULP_MODE_ISCSI << 4) | submode;
+}
+
+void cxgb3i_conn_cleanup_task(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ /* never reached the xmit task callout */
+ if (tcp_task->dd_data)
+ kfree_skb(tcp_task->dd_data);
+ tcp_task->dd_data = NULL;
+
+ /* MNC - Do we need a check in case this is called but
+ * cxgb3i_conn_alloc_pdu has never been called on the task */
+ cxgb3i_release_itt(task, task->hdr_itt);
+ iscsi_tcp_cleanup_task(task);
+}
+
+/*
+ * We do not support ahs yet
+ */
+int cxgb3i_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb;
+
+ task->hdr = NULL;
+ /* always allocate rooms for AHS */
+ skb = alloc_skb(sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE +
+ TX_HEADER_LEN, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ cxgb3i_tx_debug("task 0x%p, opcode 0x%x, skb 0x%p.\n",
+ task, opcode, skb);
+
+ tcp_task->dd_data = skb;
+ skb_reserve(skb, TX_HEADER_LEN);
+ task->hdr = (struct iscsi_hdr *)skb->data;
+ task->hdr_max = sizeof(struct iscsi_hdr);
+
+ /* data_out uses scsi_cmd's itt */
+ if (opcode != ISCSI_OP_SCSI_DATA_OUT)
+ cxgb3i_reserve_itt(task, &task->hdr->itt);
+
+ return 0;
+}
+
+int cxgb3i_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
+ unsigned int count)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb = tcp_task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct page *pg;
+ unsigned int datalen = count;
+ int i, padlen = iscsi_padding(count);
+ skb_frag_t *frag;
+
+ cxgb3i_tx_debug("task 0x%p,0x%p, offset %u, count %u, skb 0x%p.\n",
+ task, task->sc, offset, count, skb);
+
+ skb_put(skb, task->hdr_len);
+ tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0);
+ if (!count)
+ return 0;
+
+ if (task->sc) {
+ struct scatterlist *sg;
+ struct scsi_data_buffer *sdb;
+ unsigned int sgoffset = offset;
+ struct page *sgpg;
+ unsigned int sglen;
+
+ sdb = scsi_out(task->sc);
+ sg = sdb->table.sgl;
+
+ for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
+ cxgb3i_tx_debug("sg %d, page 0x%p, len %u offset %u\n",
+ i, sg_page(sg), sg->length, sg->offset);
+
+ if (sgoffset < sg->length)
+ break;
+ sgoffset -= sg->length;
+ }
+ sgpg = sg_page(sg);
+ sglen = sg->length - sgoffset;
+
+ do {
+ int j = skb_shinfo(skb)->nr_frags;
+ unsigned int copy;
+
+ if (!sglen) {
+ sg = sg_next(sg);
+ sgpg = sg_page(sg);
+ sgoffset = 0;
+ sglen = sg->length;
+ ++i;
+ }
+ copy = min(sglen, datalen);
+ if (j && skb_can_coalesce(skb, j, sgpg,
+ sg->offset + sgoffset)) {
+ skb_shinfo(skb)->frags[j - 1].size += copy;
+ } else {
+ get_page(sgpg);
+ skb_fill_page_desc(skb, j, sgpg,
+ sg->offset + sgoffset, copy);
+ }
+ sgoffset += copy;
+ sglen -= copy;
+ datalen -= copy;
+ } while (datalen);
+ } else {
+ pg = virt_to_page(task->data);
+
+ while (datalen) {
+ i = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[i];
+
+ get_page(pg);
+ frag->page = pg;
+ frag->page_offset = 0;
+ frag->size = min((unsigned int)PAGE_SIZE, datalen);
+
+ skb_shinfo(skb)->nr_frags++;
+ datalen -= frag->size;
+ pg++;
+ }
+ }
+
+ if (padlen) {
+ i = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[i];
+ frag->page = pad_page;
+ frag->page_offset = 0;
+ frag->size = padlen;
+ skb_shinfo(skb)->nr_frags++;
+ }
+
+ datalen = count + padlen;
+ skb->data_len += datalen;
+ skb->truesize += datalen;
+ skb->len += datalen;
+ return 0;
+}
+
+int cxgb3i_conn_xmit_pdu(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct sk_buff *skb = tcp_task->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
+ struct cxgb3i_conn *cconn = tcp_conn->dd_data;
+ unsigned int datalen;
+ int err;
+
+ if (!skb)
+ return 0;
+
+ datalen = skb->data_len;
+ tcp_task->dd_data = NULL;
+ err = cxgb3i_c3cn_send_pdus(cconn->cep->c3cn, skb);
+ cxgb3i_tx_debug("task 0x%p, skb 0x%p, len %u/%u, rv %d.\n",
+ task, skb, skb->len, skb->data_len, err);
+ if (err > 0) {
+ int pdulen = err;
+
+ if (task->conn->hdrdgst_en)
+ pdulen += ISCSI_DIGEST_SIZE;
+ if (datalen && task->conn->datadgst_en)
+ pdulen += ISCSI_DIGEST_SIZE;
+
+ task->conn->txdata_octets += pdulen;
+ return 0;
+ }
+
+ if (err < 0 && err != -EAGAIN) {
+ kfree_skb(skb);
+ cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
+ task->itt, skb, skb->len, skb->data_len, err);
+ iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
+ iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
+ return err;
+ }
+ /* reset skb to send when we are called again */
+ tcp_task->dd_data = skb;
+ return -EAGAIN;
+}
+
+int cxgb3i_pdu_init(void)
+{
+ pad_page = alloc_page(GFP_KERNEL);
+ if (!pad_page)
+ return -ENOMEM;
+ memset(page_address(pad_page), 0, PAGE_SIZE);
+ return 0;
+}
+
+void cxgb3i_pdu_cleanup(void)
+{
+ if (pad_page) {
+ __free_page(pad_page);
+ pad_page = NULL;
+ }
+}
+
+void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn)
+{
+ struct sk_buff *skb;
+ unsigned int read = 0;
+ struct iscsi_conn *conn = c3cn->user_data;
+ int err = 0;
+
+ cxgb3i_rx_debug("cn 0x%p.\n", c3cn);
+
+ read_lock(&c3cn->callback_lock);
+ if (unlikely(!conn || conn->suspend_rx)) {
+ cxgb3i_rx_debug("conn 0x%p, id %d, suspend_rx %lu!\n",
+ conn, conn ? conn->id : 0xFF,
+ conn ? conn->suspend_rx : 0xFF);
+ read_unlock(&c3cn->callback_lock);
+ return;
+ }
+ skb = skb_peek(&c3cn->receive_queue);
+ while (!err && skb) {
+ __skb_unlink(skb, &c3cn->receive_queue);
+ read += skb_ulp_pdulen(skb);
+ err = cxgb3i_conn_read_pdu_skb(conn, skb);
+ __kfree_skb(skb);
+ skb = skb_peek(&c3cn->receive_queue);
+ }
+ read_unlock(&c3cn->callback_lock);
+ if (c3cn) {
+ c3cn->copied_seq += read;
+ cxgb3i_c3cn_rx_credits(c3cn, read);
+ }
+ conn->rxdata_octets += read;
+}
+
+void cxgb3i_conn_tx_open(struct s3_conn *c3cn)
+{
+ struct iscsi_conn *conn = c3cn->user_data;
+
+ cxgb3i_tx_debug("cn 0x%p.\n", c3cn);
+ if (conn) {
+ cxgb3i_tx_debug("cn 0x%p, cid %d.\n", c3cn, conn->id);
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
+ }
+}
+
+void cxgb3i_conn_closing(struct s3_conn *c3cn)
+{
+ struct iscsi_conn *conn;
+
+ read_lock(&c3cn->callback_lock);
+ conn = c3cn->user_data;
+ if (conn && c3cn->state != C3CN_STATE_ESTABLISHED)
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ read_unlock(&c3cn->callback_lock);
+}
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.h b/drivers/scsi/cxgb3i/cxgb3i_pdu.h
new file mode 100644
index 000000000000..a3f685cc2362
--- /dev/null
+++ b/drivers/scsi/cxgb3i/cxgb3i_pdu.h
@@ -0,0 +1,59 @@
+/*
+ * cxgb3i_ulp2.h: Chelsio S3xx iSCSI driver.
+ *
+ * Copyright (c) 2008 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef __CXGB3I_ULP2_PDU_H__
+#define __CXGB3I_ULP2_PDU_H__
+
+struct cpl_iscsi_hdr_norss {
+ union opcode_tid ot;
+ u16 pdu_len_ddp;
+ u16 len;
+ u32 seq;
+ u16 urg;
+ u8 rsvd;
+ u8 status;
+};
+
+struct cpl_rx_data_ddp_norss {
+ union opcode_tid ot;
+ u16 urg;
+ u16 len;
+ u32 seq;
+ u32 nxt_seq;
+ u32 ulp_crc;
+ u32 ddp_status;
+};
+
+#define RX_DDP_STATUS_IPP_SHIFT 27 /* invalid pagepod */
+#define RX_DDP_STATUS_TID_SHIFT 26 /* tid mismatch */
+#define RX_DDP_STATUS_COLOR_SHIFT 25 /* color mismatch */
+#define RX_DDP_STATUS_OFFSET_SHIFT 24 /* offset mismatch */
+#define RX_DDP_STATUS_ULIMIT_SHIFT 23 /* ulimit error */
+#define RX_DDP_STATUS_TAG_SHIFT 22 /* tag mismatch */
+#define RX_DDP_STATUS_DCRC_SHIFT 21 /* dcrc error */
+#define RX_DDP_STATUS_HCRC_SHIFT 20 /* hcrc error */
+#define RX_DDP_STATUS_PAD_SHIFT 19 /* pad error */
+#define RX_DDP_STATUS_PPP_SHIFT 18 /* pagepod parity error */
+#define RX_DDP_STATUS_LLIMIT_SHIFT 17 /* llimit error */
+#define RX_DDP_STATUS_DDP_SHIFT 16 /* ddp'able */
+#define RX_DDP_STATUS_PMM_SHIFT 15 /* pagepod mismatch */
+
+#define ULP2_FLAG_DATA_READY 0x1
+#define ULP2_FLAG_DATA_DDPED 0x2
+#define ULP2_FLAG_HCRC_ERROR 0x10
+#define ULP2_FLAG_DCRC_ERROR 0x20
+#define ULP2_FLAG_PAD_ERROR 0x40
+
+void cxgb3i_conn_closing(struct s3_conn *);
+void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn);
+void cxgb3i_conn_tx_open(struct s3_conn *c3cn);
+#endif
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 3d50cabca7ee..53664765570a 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -24,6 +24,7 @@
#include <scsi/scsi_dh.h>
#define RDAC_NAME "rdac"
+#define RDAC_RETRY_COUNT 5
/*
* LSI mode page stuff
@@ -386,6 +387,7 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
struct c9_inquiry *inqp;
h->lun_state = RDAC_LUN_UNOWNED;
+ h->state = RDAC_STATE_ACTIVE;
err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);
if (err == SCSI_DH_OK) {
inqp = &h->inq.c9;
@@ -477,21 +479,27 @@ static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
{
struct request *rq;
struct request_queue *q = sdev->request_queue;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+ int err, retry_cnt = RDAC_RETRY_COUNT;
+retry:
+ err = SCSI_DH_RES_TEMP_UNAVAIL;
rq = rdac_failover_get(sdev, h);
if (!rq)
goto done;
- sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
+ sdev_printk(KERN_INFO, sdev, "%s MODE_SELECT command.\n",
+ (retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
err = blk_execute_rq(q, NULL, rq, 1);
- if (err != SCSI_DH_OK)
+ blk_put_request(rq);
+ if (err != SCSI_DH_OK) {
err = mode_select_handle_sense(sdev, h->sense);
+ if (err == SCSI_DH_RETRY && retry_cnt--)
+ goto retry;
+ }
if (err == SCSI_DH_OK)
h->state = RDAC_STATE_ACTIVE;
- blk_put_request(rq);
done:
return err;
}
@@ -594,6 +602,8 @@ static const struct scsi_dh_devlist rdac_dev_list[] = {
{"SUN", "LCSM100_F"},
{"DELL", "MD3000"},
{"DELL", "MD3000i"},
+ {"LSI", "INF-01-00"},
+ {"ENGENIO", "INF-01-00"},
{NULL, NULL},
};
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index a73a6bbb1b2b..976cdd5c94ef 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1626,8 +1626,15 @@ static void map_dma(unsigned int i, struct hostdata *ha)
cpp->sense_len = SCSI_SENSE_BUFFERSIZE;
- count = scsi_dma_map(SCpnt);
- BUG_ON(count < 0);
+ if (!scsi_sg_count(SCpnt)) {
+ cpp->data_len = 0;
+ return;
+ }
+
+ count = pci_map_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
+ pci_dir);
+ BUG_ON(!count);
+
scsi_for_each_sg(SCpnt, sg, count, k) {
cpp->sglist[k].address = H2DEV(sg_dma_address(sg));
cpp->sglist[k].num_bytes = H2DEV(sg_dma_len(sg));
@@ -1655,7 +1662,9 @@ static void unmap_dma(unsigned int i, struct hostdata *ha)
pci_unmap_single(ha->pdev, DEV2H(cpp->sense_addr),
DEV2H(cpp->sense_len), PCI_DMA_FROMDEVICE);
- scsi_dma_unmap(SCpnt);
+ if (scsi_sg_count(SCpnt))
+ pci_unmap_sg(ha->pdev, scsi_sglist(SCpnt), scsi_sg_count(SCpnt),
+ pci_dir);
if (!DEV2H(cpp->data_len))
pci_dir = PCI_DMA_BIDIRECTIONAL;
diff --git a/drivers/scsi/eata_pio.c b/drivers/scsi/eata_pio.c
index 952505c006df..152dd15db276 100644
--- a/drivers/scsi/eata_pio.c
+++ b/drivers/scsi/eata_pio.c
@@ -14,8 +14,8 @@
* neuffer@goofy.zdv.uni-mainz.de *
* a.arnold@kfa-juelich.de *
* *
- * Updated 2002 by Alan Cox <alan@redhat.com> for Linux *
- * 2.5.x and the newer locking and error handling *
+ * Updated 2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> for *
+ * Linux 2.5.x and the newer locking and error handling *
* *
* This program is free software; you can redistribute it *
* and/or modify it under the terms of the GNU General *
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 62a4618530d0..a680e18b5f3b 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -1453,7 +1453,7 @@ static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
offset = 0;
if (offset) {
- int rounded_up, one_clock;
+ int one_clock;
if (period > esp->max_period) {
period = offset = 0;
@@ -1463,9 +1463,7 @@ static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp)
goto do_reject;
one_clock = esp->ccycle / 1000;
- rounded_up = (period << 2);
- rounded_up = (rounded_up + one_clock - 1) / one_clock;
- stp = rounded_up;
+ stp = DIV_ROUND_UP(period << 2, one_clock);
if (stp && esp->rev >= FAS236) {
if (stp >= 50)
stp--;
diff --git a/drivers/scsi/fcoe/Makefile b/drivers/scsi/fcoe/Makefile
new file mode 100644
index 000000000000..b78da06d7c0e
--- /dev/null
+++ b/drivers/scsi/fcoe/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile
+
+obj-$(CONFIG_FCOE) += fcoe.o
+
+fcoe-y := \
+ libfcoe.o \
+ fcoe_sw.o \
+ fc_transport_fcoe.o
diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
new file mode 100644
index 000000000000..bf7fe6fc0820
--- /dev/null
+++ b/drivers/scsi/fcoe/fc_transport_fcoe.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/pci.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+/* internal fcoe transport */
+struct fcoe_transport_internal {
+ struct fcoe_transport *t;
+ struct net_device *netdev;
+ struct list_head list;
+};
+
+/* fcoe transports list and its lock */
+static LIST_HEAD(fcoe_transports);
+static DEFINE_MUTEX(fcoe_transports_lock);
+
+/**
+ * fcoe_transport_default - returns ptr to the default transport fcoe_sw
+ **/
+struct fcoe_transport *fcoe_transport_default(void)
+{
+ return &fcoe_sw_transport;
+}
+
+/**
+ * fcoe_transport_to_pcidev - get the pci dev from a netdev
+ * @netdev: the netdev that pci dev will be retrived from
+ *
+ * Returns: NULL or the corrsponding pci_dev
+ **/
+struct pci_dev *fcoe_transport_pcidev(const struct net_device *netdev)
+{
+ if (!netdev->dev.parent)
+ return NULL;
+ return to_pci_dev(netdev->dev.parent);
+}
+
+/**
+ * fcoe_transport_device_lookup - find out netdev is managed by the
+ * transport
+ * assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static struct fcoe_transport_internal *fcoe_transport_device_lookup(
+ struct fcoe_transport *t, struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ /* assign the transpor to this device */
+ mutex_lock(&t->devlock);
+ list_for_each_entry(ti, &t->devlist, list) {
+ if (ti->netdev == netdev) {
+ mutex_unlock(&t->devlock);
+ return ti;
+ }
+ }
+ mutex_unlock(&t->devlock);
+ return NULL;
+}
+/**
+ * fcoe_transport_device_add - assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_add(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ ti = fcoe_transport_device_lookup(t, netdev);
+ if (ti) {
+ printk(KERN_DEBUG "fcoe_transport_device_add:"
+ "device %s is already added to transport %s\n",
+ netdev->name, t->name);
+ return -EEXIST;
+ }
+ /* allocate an internal struct to host the netdev and the list */
+ ti = kzalloc(sizeof(*ti), GFP_KERNEL);
+ if (!ti)
+ return -ENOMEM;
+
+ ti->t = t;
+ ti->netdev = netdev;
+ INIT_LIST_HEAD(&ti->list);
+ dev_hold(ti->netdev);
+
+ mutex_lock(&t->devlock);
+ list_add(&ti->list, &t->devlist);
+ mutex_unlock(&t->devlock);
+
+ printk(KERN_DEBUG "fcoe_transport_device_add:"
+ "device %s added to transport %s\n",
+ netdev->name, t->name);
+
+ return 0;
+}
+
+/**
+ * fcoe_transport_device_remove - remove a device from its transport
+ * @netdev: the netdev the transport to be attached to
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_remove(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ struct fcoe_transport_internal *ti;
+
+ ti = fcoe_transport_device_lookup(t, netdev);
+ if (!ti) {
+ printk(KERN_DEBUG "fcoe_transport_device_remove:"
+ "device %s is not managed by transport %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ mutex_lock(&t->devlock);
+ list_del(&ti->list);
+ mutex_unlock(&t->devlock);
+ printk(KERN_DEBUG "fcoe_transport_device_remove:"
+ "device %s removed from transport %s\n",
+ netdev->name, t->name);
+ dev_put(ti->netdev);
+ kfree(ti);
+ return 0;
+}
+
+/**
+ * fcoe_transport_device_remove_all - remove all from transport devlist
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
+{
+ struct fcoe_transport_internal *ti, *tmp;
+
+ mutex_lock(&t->devlock);
+ list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
+ list_del(&ti->list);
+ kfree(ti);
+ }
+ mutex_unlock(&t->devlock);
+}
+
+/**
+ * fcoe_transport_match - use the bus device match function to match the hw
+ * @t: the fcoe transport
+ * @netdev:
+ *
+ * This function is used to check if the givne transport wants to manage the
+ * input netdev. if the transports implements the match function, it will be
+ * called, o.w. we just compare the pci vendor and device id.
+ *
+ * Returns: true for match up
+ **/
+static bool fcoe_transport_match(struct fcoe_transport *t,
+ struct net_device *netdev)
+{
+ /* match transport by vendor and device id */
+ struct pci_dev *pci;
+
+ pci = fcoe_transport_pcidev(netdev);
+
+ if (pci) {
+ printk(KERN_DEBUG "fcoe_transport_match:"
+ "%s:%x:%x -- %s:%x:%x\n",
+ t->name, t->vendor, t->device,
+ netdev->name, pci->vendor, pci->device);
+
+ /* if transport supports match */
+ if (t->match)
+ return t->match(netdev);
+
+ /* else just compare the vendor and device id: pci only */
+ return (t->vendor == pci->vendor) && (t->device == pci->device);
+ }
+ return false;
+}
+
+/**
+ * fcoe_transport_lookup - check if the transport is already registered
+ * @t: the transport to be looked up
+ *
+ * This compares the parent device (pci) vendor and device id
+ *
+ * Returns: NULL if not found
+ *
+ * TODO - return default sw transport if no other transport is found
+ **/
+static struct fcoe_transport *fcoe_transport_lookup(
+ struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry(t, &fcoe_transports, list) {
+ if (fcoe_transport_match(t, netdev)) {
+ mutex_unlock(&fcoe_transports_lock);
+ return t;
+ }
+ }
+ mutex_unlock(&fcoe_transports_lock);
+
+ printk(KERN_DEBUG "fcoe_transport_lookup:"
+ "use default transport for %s\n", netdev->name);
+ return fcoe_transport_default();
+}
+
+/**
+ * fcoe_transport_register - adds a fcoe transport to the fcoe transports list
+ * @t: ptr to the fcoe transport to be added
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_register(struct fcoe_transport *t)
+{
+ struct fcoe_transport *tt;
+
+ /* TODO - add fcoe_transport specific initialization here */
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry(tt, &fcoe_transports, list) {
+ if (tt == t) {
+ mutex_unlock(&fcoe_transports_lock);
+ return -EEXIST;
+ }
+ }
+ list_add_tail(&t->list, &fcoe_transports);
+ mutex_unlock(&fcoe_transports_lock);
+
+ mutex_init(&t->devlock);
+ INIT_LIST_HEAD(&t->devlist);
+
+ printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_register);
+
+/**
+ * fcoe_transport_unregister - remove the tranport fro the fcoe transports list
+ * @t: ptr to the fcoe transport to be removed
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_unregister(struct fcoe_transport *t)
+{
+ struct fcoe_transport *tt, *tmp;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
+ if (tt == t) {
+ list_del(&t->list);
+ mutex_unlock(&fcoe_transports_lock);
+ fcoe_transport_device_remove_all(t);
+ printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
+ t->name);
+ return 0;
+ }
+ }
+ mutex_unlock(&fcoe_transports_lock);
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
+
+/*
+ * fcoe_load_transport_driver - load an offload driver by alias name
+ * @netdev: the target net device
+ *
+ * Requests for an offload driver module as the fcoe transport, if fails, it
+ * falls back to use the SW HBA (fcoe_sw) as its transport
+ *
+ * TODO -
+ * 1. supports only PCI device
+ * 2. needs fix for VLAn and bonding
+ * 3. pure hw fcoe hba may not have netdev
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_load_transport_driver(struct net_device *netdev)
+{
+ struct pci_dev *pci;
+ struct device *dev = netdev->dev.parent;
+
+ if (fcoe_transport_lookup(netdev)) {
+ /* load default transport */
+ printk(KERN_DEBUG "fcoe: already loaded transport for %s\n",
+ netdev->name);
+ return -EEXIST;
+ }
+
+ pci = to_pci_dev(dev);
+ if (dev->bus != &pci_bus_type) {
+ printk(KERN_DEBUG "fcoe: support noly PCI device\n");
+ return -ENODEV;
+ }
+ printk(KERN_DEBUG "fcoe: loading driver fcoe-pci-0x%04x-0x%04x\n",
+ pci->vendor, pci->device);
+
+ return request_module("fcoe-pci-0x%04x-0x%04x",
+ pci->vendor, pci->device);
+
+}
+EXPORT_SYMBOL_GPL(fcoe_load_transport_driver);
+
+/**
+ * fcoe_transport_attach - load transport to fcoe
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_attach(struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ /* find the corresponding transport */
+ t = fcoe_transport_lookup(netdev);
+ if (!t) {
+ printk(KERN_DEBUG "fcoe_transport_attach"
+ ":no transport for %s:use %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ /* add to the transport */
+ if (fcoe_transport_device_add(t, netdev)) {
+ printk(KERN_DEBUG "fcoe_transport_attach"
+ ":failed to add %s to tramsport %s\n",
+ netdev->name, t->name);
+ return -EIO;
+ }
+ /* transport create function */
+ if (t->create)
+ t->create(netdev);
+
+ printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
+ t->name, netdev->name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_attach);
+
+/**
+ * fcoe_transport_release - unload transport from fcoe
+ * @netdev: the net device on which fcoe is to be released
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_release(struct net_device *netdev)
+{
+ struct fcoe_transport *t;
+
+ /* find the corresponding transport */
+ t = fcoe_transport_lookup(netdev);
+ if (!t) {
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "no transport for %s:use %s\n",
+ netdev->name, t->name);
+ return -ENODEV;
+ }
+ /* remove the device from the transport */
+ if (fcoe_transport_device_remove(t, netdev)) {
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "failed to add %s to tramsport %s\n",
+ netdev->name, t->name);
+ return -EIO;
+ }
+ /* transport destroy function */
+ if (t->destroy)
+ t->destroy(netdev);
+
+ printk(KERN_DEBUG "fcoe_transport_release:"
+ "device %s dettached from transport %s\n",
+ netdev->name, t->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_release);
+
+/**
+ * fcoe_transport_init - initializes fcoe transport layer
+ *
+ * This prepares for the fcoe transport layer
+ *
+ * Returns: none
+ **/
+int __init fcoe_transport_init(void)
+{
+ INIT_LIST_HEAD(&fcoe_transports);
+ mutex_init(&fcoe_transports_lock);
+ return 0;
+}
+
+/**
+ * fcoe_transport_exit - cleans up the fcoe transport layer
+ * This cleans up the fcoe transport layer. removing any transport on the list,
+ * note that the transport destroy func is not called here.
+ *
+ * Returns: none
+ **/
+int __exit fcoe_transport_exit(void)
+{
+ struct fcoe_transport *t, *tmp;
+
+ mutex_lock(&fcoe_transports_lock);
+ list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
+ list_del(&t->list);
+ mutex_unlock(&fcoe_transports_lock);
+ fcoe_transport_device_remove_all(t);
+ mutex_lock(&fcoe_transports_lock);
+ }
+ mutex_unlock(&fcoe_transports_lock);
+ return 0;
+}
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
new file mode 100644
index 000000000000..dc4cd5e25760
--- /dev/null
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_encaps.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+#define FCOE_SW_VERSION "0.1"
+#define FCOE_SW_NAME "fcoesw"
+#define FCOE_SW_VENDOR "Open-FCoE.org"
+
+#define FCOE_MAX_LUN 255
+#define FCOE_MAX_FCP_TARGET 256
+
+#define FCOE_MAX_OUTSTANDING_COMMANDS 1024
+
+#define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */
+
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
+
+struct fc_function_template fcoe_sw_transport_function = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = fc_get_host_stats,
+ .issue_fc_host_lip = fcoe_reset,
+
+ .terminate_rport_io = fc_rport_terminate_io,
+};
+
+static struct scsi_host_template fcoe_sw_shost_template = {
+ .module = THIS_MODULE,
+ .name = "FCoE Driver",
+ .proc_name = FCOE_SW_NAME,
+ .queuecommand = fc_queuecommand,
+ .eh_abort_handler = fc_eh_abort,
+ .eh_device_reset_handler = fc_eh_device_reset,
+ .eh_host_reset_handler = fc_eh_host_reset,
+ .slave_alloc = fc_slave_alloc,
+ .change_queue_depth = fc_change_queue_depth,
+ .change_queue_type = fc_change_queue_type,
+ .this_id = -1,
+ .cmd_per_lun = 32,
+ .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = 0xffff,
+};
+
+/*
+ * fcoe_sw_lport_config - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
+ *
+ */
+static int fcoe_sw_lport_config(struct fc_lport *lp)
+{
+ int i = 0;
+
+ lp->link_status = 0;
+ lp->max_retry_count = 3;
+ lp->e_d_tov = 2 * 1000; /* FC-FS default */
+ lp->r_a_tov = 2 * 2 * 1000;
+ lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+
+ /*
+ * allocate per cpu stats block
+ */
+ for_each_online_cpu(i)
+ lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats),
+ GFP_KERNEL);
+
+ /* lport fc_lport related configuration */
+ fc_lport_config(lp);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_netdev_config - sets up fcoe_softc for lport and network
+ * related properties
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_sw_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+{
+ u32 mfs;
+ u64 wwnn, wwpn;
+ struct fcoe_softc *fc;
+ u8 flogi_maddr[ETH_ALEN];
+
+ /* Setup lport private data to point to fcoe softc */
+ fc = lport_priv(lp);
+ fc->lp = lp;
+ fc->real_dev = netdev;
+ fc->phys_dev = netdev;
+
+ /* Require support for get_pauseparam ethtool op. */
+ if (netdev->priv_flags & IFF_802_1Q_VLAN)
+ fc->phys_dev = vlan_dev_real_dev(netdev);
+
+ /* Do not support for bonding device */
+ if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
+ (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
+ (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * Determine max frame size based on underlying device and optional
+ * user-configured limit. If the MFS is too low, fcoe_link_ok()
+ * will return 0, so do this first.
+ */
+ mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
+ if (fc_set_mfs(lp, mfs))
+ return -EINVAL;
+
+ lp->link_status = ~FC_PAUSE & ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ lp->link_status |= FC_LINK_UP;
+
+ /* offload features support */
+ if (fc->real_dev->features & NETIF_F_SG)
+ lp->sg_supp = 1;
+
+
+ skb_queue_head_init(&fc->fcoe_pending_queue);
+
+ /* setup Source Mac Address */
+ memcpy(fc->ctl_src_addr, fc->real_dev->dev_addr,
+ fc->real_dev->addr_len);
+
+ wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+ fc_set_wwnn(lp, wwnn);
+ /* XXX - 3rd arg needs to be vlan id */
+ wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+ fc_set_wwpn(lp, wwpn);
+
+ /*
+ * Add FCoE MAC address as second unicast MAC address
+ * or enter promiscuous mode if not capable of listening
+ * for multiple unicast MACs.
+ */
+ rtnl_lock();
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_add(fc->real_dev, flogi_maddr, ETH_ALEN);
+ rtnl_unlock();
+
+ /*
+ * setup the receive function from ethernet driver
+ * on the ethertype for the given device
+ */
+ fc->fcoe_packet_type.func = fcoe_rcv;
+ fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+ fc->fcoe_packet_type.dev = fc->real_dev;
+ dev_add_pack(&fc->fcoe_packet_type);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_shost_config - sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ * @dev : device associated to scsi host
+ *
+ * Must be called after fcoe_sw_lport_config) and fcoe_sw_netdev_config()
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
+ struct device *dev)
+{
+ int rc = 0;
+
+ /* lport scsi host config */
+ lp->host = shost;
+
+ lp->host->max_lun = FCOE_MAX_LUN;
+ lp->host->max_id = FCOE_MAX_FCP_TARGET;
+ lp->host->max_channel = 0;
+ lp->host->transportt = scsi_transport_fcoe_sw;
+
+ /* add the new host to the SCSI-ml */
+ rc = scsi_add_host(lp->host, dev);
+ if (rc) {
+ FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
+ return rc;
+ }
+ sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+ FCOE_SW_NAME, FCOE_SW_VERSION,
+ fcoe_netdev(lp)->name);
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_em_config - allocates em for this lport
+ * @lp: the port that em is to allocated for
+ *
+ * Returns : 0 on success
+ */
+static inline int fcoe_sw_em_config(struct fc_lport *lp)
+{
+ BUG_ON(lp->emp);
+
+ lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+ FCOE_MIN_XID, FCOE_MAX_XID);
+ if (!lp->emp)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * fcoe_sw_destroy - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_sw_destroy(struct net_device *netdev)
+{
+ int cpu;
+ struct fc_lport *lp = NULL;
+ struct fcoe_softc *fc;
+ u8 flogi_maddr[ETH_ALEN];
+
+ BUG_ON(!netdev);
+
+ printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
+ netdev->name);
+
+ lp = fcoe_hostlist_lookup(netdev);
+ if (!lp)
+ return -ENODEV;
+
+ fc = fcoe_softc(lp);
+
+ /* Logout of the fabric */
+ fc_fabric_logoff(lp);
+
+ /* Remove the instance from fcoe's list */
+ fcoe_hostlist_remove(lp);
+
+ /* Don't listen for Ethernet packets anymore */
+ dev_remove_pack(&fc->fcoe_packet_type);
+
+ /* Cleanup the fc_lport */
+ fc_lport_destroy(lp);
+ fc_fcp_destroy(lp);
+
+ /* Detach from the scsi-ml */
+ fc_remove_host(lp->host);
+ scsi_remove_host(lp->host);
+
+ /* There are no more rports or I/O, free the EM */
+ if (lp->emp)
+ fc_exch_mgr_free(lp->emp);
+
+ /* Delete secondary MAC addresses */
+ rtnl_lock();
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_delete(fc->real_dev, flogi_maddr, ETH_ALEN);
+ if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+ dev_unicast_delete(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+ rtnl_unlock();
+
+ /* Free the per-CPU revieve threads */
+ fcoe_percpu_clean(lp);
+
+ /* Free existing skbs */
+ fcoe_clean_pending_queue(lp);
+
+ /* Free memory used by statistical counters */
+ for_each_online_cpu(cpu)
+ kfree(lp->dev_stats[cpu]);
+
+ /* Release the net_device and Scsi_Host */
+ dev_put(fc->real_dev);
+ scsi_host_put(lp->host);
+
+ return 0;
+}
+
+static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
+ .frame_send = fcoe_xmit,
+};
+
+/*
+ * fcoe_sw_create - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
+ */
+static int fcoe_sw_create(struct net_device *netdev)
+{
+ int rc;
+ struct fc_lport *lp = NULL;
+ struct fcoe_softc *fc;
+ struct Scsi_Host *shost;
+
+ BUG_ON(!netdev);
+
+ printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
+ netdev->name);
+
+ lp = fcoe_hostlist_lookup(netdev);
+ if (lp)
+ return -EEXIST;
+
+ shost = fcoe_host_alloc(&fcoe_sw_shost_template,
+ sizeof(struct fcoe_softc));
+ if (!shost) {
+ FC_DBG("Could not allocate host structure\n");
+ return -ENOMEM;
+ }
+ lp = shost_priv(shost);
+ fc = lport_priv(lp);
+
+ /* configure fc_lport, e.g., em */
+ rc = fcoe_sw_lport_config(lp);
+ if (rc) {
+ FC_DBG("Could not configure lport\n");
+ goto out_host_put;
+ }
+
+ /* configure lport network properties */
+ rc = fcoe_sw_netdev_config(lp, netdev);
+ if (rc) {
+ FC_DBG("Could not configure netdev for lport\n");
+ goto out_host_put;
+ }
+
+ /* configure lport scsi host properties */
+ rc = fcoe_sw_shost_config(lp, shost, &netdev->dev);
+ if (rc) {
+ FC_DBG("Could not configure shost for lport\n");
+ goto out_host_put;
+ }
+
+ /* lport exch manager allocation */
+ rc = fcoe_sw_em_config(lp);
+ if (rc) {
+ FC_DBG("Could not configure em for lport\n");
+ goto out_host_put;
+ }
+
+ /* Initialize the library */
+ rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
+ if (rc) {
+ FC_DBG("Could not configure libfc for lport!\n");
+ goto out_lp_destroy;
+ }
+
+ /* add to lports list */
+ fcoe_hostlist_add(lp);
+
+ lp->boot_time = jiffies;
+
+ fc_fabric_login(lp);
+
+ dev_hold(netdev);
+
+ return rc;
+
+out_lp_destroy:
+ fc_exch_mgr_free(lp->emp); /* Free the EM */
+out_host_put:
+ scsi_host_put(lp->host);
+ return rc;
+}
+
+/*
+ * fcoe_sw_match - the fcoe sw transport match function
+ *
+ * Returns : false always
+ */
+static bool fcoe_sw_match(struct net_device *netdev)
+{
+ /* FIXME - for sw transport, always return false */
+ return false;
+}
+
+/* the sw hba fcoe transport */
+struct fcoe_transport fcoe_sw_transport = {
+ .name = "fcoesw",
+ .create = fcoe_sw_create,
+ .destroy = fcoe_sw_destroy,
+ .match = fcoe_sw_match,
+ .vendor = 0x0,
+ .device = 0xffff,
+};
+
+/*
+ * fcoe_sw_init - registers fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
+int __init fcoe_sw_init(void)
+{
+ /* attach to scsi transport */
+ scsi_transport_fcoe_sw =
+ fc_attach_transport(&fcoe_sw_transport_function);
+ if (!scsi_transport_fcoe_sw) {
+ printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
+ return -ENODEV;
+ }
+ /* register sw transport */
+ fcoe_transport_register(&fcoe_sw_transport);
+ return 0;
+}
+
+/*
+ * fcoe_sw_exit - unregisters fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_sw_exit(void)
+{
+ /* dettach the transport */
+ fc_release_transport(scsi_transport_fcoe_sw);
+ fcoe_transport_unregister(&fcoe_sw_transport);
+ return 0;
+}
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
new file mode 100644
index 000000000000..e419f486cdb3
--- /dev/null
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -0,0 +1,1510 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/kthread.h>
+#include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+#include <net/rtnetlink.h>
+
+#include <scsi/fc/fc_encaps.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
+
+static int debug_fcoe;
+
+#define FCOE_MAX_QUEUE_DEPTH 256
+
+/* destination address mode */
+#define FCOE_GW_ADDR_MODE 0x00
+#define FCOE_FCOUI_ADDR_MODE 0x01
+
+#define FCOE_WORD_TO_BYTE 4
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+struct fcoe_percpu_s *fcoe_percpu[NR_CPUS];
+
+
+/* Function Prototyes */
+static int fcoe_check_wait_queue(struct fc_lport *);
+static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *);
+static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *);
+static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+#ifdef CONFIG_HOTPLUG_CPU
+static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
+#endif /* CONFIG_HOTPLUG_CPU */
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+ .notifier_call = fcoe_device_notification,
+};
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+static struct notifier_block fcoe_cpu_notifier = {
+ .notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_create_percpu_data - creates the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will be created
+ *
+ * create percpu stats block, from cpu add notifier
+ *
+ * Returns: none
+ **/
+static void fcoe_create_percpu_data(int cpu)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ if (lp->dev_stats[cpu] == NULL)
+ lp->dev_stats[cpu] =
+ kzalloc(sizeof(struct fcoe_dev_stats),
+ GFP_KERNEL);
+ }
+ write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_destroy_percpu_data - destroys the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will destroyed
+ *
+ * destroy percpu stats block called by cpu add/remove notifier
+ *
+ * Retuns: none
+ **/
+static void fcoe_destroy_percpu_data(int cpu)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ kfree(lp->dev_stats[cpu]);
+ lp->dev_stats[cpu] = NULL;
+ }
+ write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_cpu_callback - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * this creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ **/
+static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
+ void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ fcoe_create_percpu_data(cpu);
+ break;
+ case CPU_DEAD:
+ fcoe_destroy_percpu_data(cpu);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/**
+ * fcoe_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *ptype, struct net_device *olddev)
+{
+ struct fc_lport *lp;
+ struct fcoe_rcv_info *fr;
+ struct fcoe_softc *fc;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ unsigned short oxid;
+ int cpu_idx;
+ struct fcoe_percpu_s *fps;
+
+ fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
+ lp = fc->lp;
+ if (unlikely(lp == NULL)) {
+ FC_DBG("cannot find hba structure");
+ goto err2;
+ }
+
+ if (unlikely(debug_fcoe)) {
+ FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p "
+ "end:%p sum:%d dev:%s", skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+
+ }
+
+ /* check for FCOE packet type */
+ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
+ FC_DBG("wrong FC type frame");
+ goto err;
+ }
+
+ /*
+ * Check for minimum frame length, and make sure required FCoE
+ * and FC headers are pulled into the linear data area.
+ */
+ if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+ !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+ goto err;
+
+ skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ oxid = ntohs(fh->fh_ox_id);
+
+ fr = fcoe_dev_from_skb(skb);
+ fr->fr_dev = lp;
+ fr->ptype = ptype;
+ cpu_idx = 0;
+#ifdef CONFIG_SMP
+ /*
+ * The incoming frame exchange id(oxid) is ANDed with num of online
+ * cpu bits to get cpu_idx and then this cpu_idx is used for selecting
+ * a per cpu kernel thread from fcoe_percpu. In case the cpu is
+ * offline or no kernel thread for derived cpu_idx then cpu_idx is
+ * initialize to first online cpu index.
+ */
+ cpu_idx = oxid & (num_online_cpus() - 1);
+ if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx))
+ cpu_idx = first_cpu(cpu_online_map);
+#endif
+ fps = fcoe_percpu[cpu_idx];
+
+ spin_lock_bh(&fps->fcoe_rx_list.lock);
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->fcoe_rx_list.qlen == 1)
+ wake_up_process(fps->thread);
+
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+
+ return 0;
+err:
+#ifdef CONFIG_SMP
+ stats = lp->dev_stats[smp_processor_id()];
+#else
+ stats = lp->dev_stats[0];
+#endif
+ if (stats)
+ stats->ErrorFrames++;
+
+err2:
+ kfree_skb(skb);
+ return -1;
+}
+EXPORT_SYMBOL_GPL(fcoe_rcv);
+
+/**
+ * fcoe_start_io - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ **/
+static inline int fcoe_start_io(struct sk_buff *skb)
+{
+ int rc;
+
+ skb_get(skb);
+ rc = dev_queue_xmit(skb);
+ if (rc != 0)
+ return rc;
+ kfree_skb(skb);
+ return 0;
+}
+
+/**
+ * fcoe_get_paged_crc_eof - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+{
+ struct fcoe_percpu_s *fps;
+ struct page *page;
+ int cpu_idx;
+
+ cpu_idx = get_cpu();
+ fps = fcoe_percpu[cpu_idx];
+ page = fps->crc_eof_page;
+ if (!page) {
+ page = alloc_page(GFP_ATOMIC);
+ if (!page) {
+ put_cpu();
+ return -ENOMEM;
+ }
+ fps->crc_eof_page = page;
+ WARN_ON(fps->crc_eof_offset != 0);
+ }
+
+ get_page(page);
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
+ fps->crc_eof_offset, tlen);
+ skb->len += tlen;
+ skb->data_len += tlen;
+ skb->truesize += tlen;
+ fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
+
+ if (fps->crc_eof_offset >= PAGE_SIZE) {
+ fps->crc_eof_page = NULL;
+ fps->crc_eof_offset = 0;
+ put_page(page);
+ }
+ put_cpu();
+ return 0;
+}
+
+/**
+ * fcoe_fc_crc - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return : 32 bit crc
+ *
+ **/
+u32 fcoe_fc_crc(struct fc_frame *fp)
+{
+ struct sk_buff *skb = fp_skb(fp);
+ struct skb_frag_struct *frag;
+ unsigned char *data;
+ unsigned long off, len, clen;
+ u32 crc;
+ unsigned i;
+
+ crc = crc32(~0, skb->data, skb_headlen(skb));
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ off = frag->page_offset;
+ len = frag->size;
+ while (len > 0) {
+ clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
+ data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
+ KM_SKB_DATA_SOFTIRQ);
+ crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
+ kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
+ off += clen;
+ len -= clen;
+ }
+ }
+ return crc;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit - FCoE frame transmit function
+ * @lp: the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return : 0 for success
+ *
+ **/
+int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+{
+ int wlen, rc = 0;
+ u32 crc;
+ struct ethhdr *eh;
+ struct fcoe_crc_eof *cp;
+ struct sk_buff *skb;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ unsigned int hlen; /* header length implies the version */
+ unsigned int tlen; /* trailer length */
+ unsigned int elen; /* eth header, may include vlan */
+ int flogi_in_progress = 0;
+ struct fcoe_softc *fc;
+ u8 sof, eof;
+ struct fcoe_hdr *hp;
+
+ WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
+
+ fc = fcoe_softc(lp);
+ /*
+ * if it is a flogi then we need to learn gw-addr
+ * and my own fcid
+ */
+ fh = fc_frame_header_get(fp);
+ if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
+ if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+ fc->flogi_oxid = ntohs(fh->fh_ox_id);
+ fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+ fc->flogi_progress = 1;
+ flogi_in_progress = 1;
+ } else if (fc->flogi_progress && ntoh24(fh->fh_s_id) != 0) {
+ /*
+ * Here we must've gotten an SID by accepting an FLOGI
+ * from a point-to-point connection. Switch to using
+ * the source mac based on the SID. The destination
+ * MAC in this case would have been set by receving the
+ * FLOGI.
+ */
+ fc_fcoe_set_mac(fc->data_src_addr, fh->fh_s_id);
+ fc->flogi_progress = 0;
+ }
+ }
+
+ skb = fp_skb(fp);
+ sof = fr_sof(fp);
+ eof = fr_eof(fp);
+
+ elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+ sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
+ hlen = sizeof(struct fcoe_hdr);
+ tlen = sizeof(struct fcoe_crc_eof);
+ wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
+
+ /* crc offload */
+ if (likely(lp->crc_offload)) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->csum_start = skb_headroom(skb);
+ skb->csum_offset = skb->len;
+ crc = 0;
+ } else {
+ skb->ip_summed = CHECKSUM_NONE;
+ crc = fcoe_fc_crc(fp);
+ }
+
+ /* copy fc crc and eof to the skb buff */
+ if (skb_is_nonlinear(skb)) {
+ skb_frag_t *frag;
+ if (fcoe_get_paged_crc_eof(skb, tlen)) {
+ kfree(skb);
+ return -ENOMEM;
+ }
+ frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
+ cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+ + frag->page_offset;
+ } else {
+ cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+ }
+
+ memset(cp, 0, sizeof(*cp));
+ cp->fcoe_eof = eof;
+ cp->fcoe_crc32 = cpu_to_le32(~crc);
+
+ if (skb_is_nonlinear(skb)) {
+ kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
+ cp = NULL;
+ }
+
+ /* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+ skb_push(skb, elen + hlen);
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb->mac_len = elen;
+ skb->protocol = htons(ETH_P_802_3);
+ skb->dev = fc->real_dev;
+
+ /* fill up mac and fcoe headers */
+ eh = eth_hdr(skb);
+ eh->h_proto = htons(ETH_P_FCOE);
+ if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
+ fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
+ else
+ /* insert GW address */
+ memcpy(eh->h_dest, fc->dest_addr, ETH_ALEN);
+
+ if (unlikely(flogi_in_progress))
+ memcpy(eh->h_source, fc->ctl_src_addr, ETH_ALEN);
+ else
+ memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
+
+ hp = (struct fcoe_hdr *)(eh + 1);
+ memset(hp, 0, sizeof(*hp));
+ if (FC_FCOE_VER)
+ FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+ hp->fcoe_sof = sof;
+
+ /* update tx stats: regardless if LLD fails */
+ stats = lp->dev_stats[smp_processor_id()];
+ if (stats) {
+ stats->TxFrames++;
+ stats->TxWords += wlen;
+ }
+
+ /* send down to lld */
+ fr_dev(fp) = lp;
+ if (fc->fcoe_pending_queue.qlen)
+ rc = fcoe_check_wait_queue(lp);
+
+ if (rc == 0)
+ rc = fcoe_start_io(skb);
+
+ if (rc) {
+ fcoe_insert_wait_queue(lp, skb);
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ fc_pause(lp);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_xmit);
+
+/*
+ * fcoe_percpu_receive_thread - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ *
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+ struct fcoe_percpu_s *p = arg;
+ u32 fr_len;
+ struct fc_lport *lp;
+ struct fcoe_rcv_info *fr;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ struct sk_buff *skb;
+ struct fcoe_crc_eof crc_eof;
+ struct fc_frame *fp;
+ u8 *mac = NULL;
+ struct fcoe_softc *fc;
+ struct fcoe_hdr *hp;
+
+ set_user_nice(current, 19);
+
+ while (!kthread_should_stop()) {
+
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) == NULL) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ if (kthread_should_stop())
+ return 0;
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ }
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ fr = fcoe_dev_from_skb(skb);
+ lp = fr->fr_dev;
+ if (unlikely(lp == NULL)) {
+ FC_DBG("invalid HBA Structure");
+ kfree_skb(skb);
+ continue;
+ }
+
+ stats = lp->dev_stats[smp_processor_id()];
+
+ if (unlikely(debug_fcoe)) {
+ FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p "
+ "tail:%p end:%p sum:%d dev:%s",
+ skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+ }
+
+ /*
+ * Save source MAC address before discarding header.
+ */
+ fc = lport_priv(lp);
+ if (unlikely(fc->flogi_progress))
+ mac = eth_hdr(skb)->h_source;
+
+ if (skb_is_nonlinear(skb))
+ skb_linearize(skb); /* not ideal */
+
+ /*
+ * Frame length checks and setting up the header pointers
+ * was done in fcoe_rcv already.
+ */
+ hp = (struct fcoe_hdr *) skb_network_header(skb);
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+ if (stats) {
+ if (stats->ErrorFrames < 5)
+ FC_DBG("unknown FCoE version %x",
+ FC_FCOE_DECAPS_VER(hp));
+ stats->ErrorFrames++;
+ }
+ kfree_skb(skb);
+ continue;
+ }
+
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+ if (stats) {
+ stats->RxFrames++;
+ stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+ }
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_dev(fp) = lp;
+ fr_sof(fp) = hp->fcoe_sof;
+
+ /* Copy out the CRC and EOF trailer for access */
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ kfree_skb(skb);
+ continue;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ /*
+ * We only check CRC if no offload is available and if it is
+ * it's solicited data, in which case, the FCP layer would
+ * check it during the copy.
+ */
+ if (lp->crc_offload)
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ else
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+ fh->fh_type == FC_TYPE_FCP) {
+ fc_exch_recv(lp, lp->emp, fp);
+ continue;
+ }
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ if (le32_to_cpu(fr_crc(fp)) !=
+ ~crc32(~0, skb->data, fr_len)) {
+ if (debug_fcoe || stats->InvalidCRCCount < 5)
+ printk(KERN_WARNING "fcoe: dropping "
+ "frame with CRC error\n");
+ stats->InvalidCRCCount++;
+ stats->ErrorFrames++;
+ fc_frame_free(fp);
+ continue;
+ }
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ }
+ /* non flogi and non data exchanges are handled here */
+ if (unlikely(fc->flogi_progress))
+ fcoe_recv_flogi(fc, fp, mac);
+ fc_exch_recv(lp, lp->emp, fp);
+ }
+ return 0;
+}
+
+/**
+ * fcoe_recv_flogi - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ **/
+static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
+{
+ struct fc_frame_header *fh;
+ u8 op;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_type != FC_TYPE_ELS)
+ return;
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
+ fc->flogi_oxid == ntohs(fh->fh_ox_id)) {
+ /*
+ * FLOGI accepted.
+ * If the src mac addr is FC_OUI-based, then we mark the
+ * address_mode flag to use FC_OUI-based Ethernet DA.
+ * Otherwise we use the FCoE gateway addr
+ */
+ if (!compare_ether_addr(sa, (u8[6]) FC_FCOE_FLOGI_MAC)) {
+ fc->address_mode = FCOE_FCOUI_ADDR_MODE;
+ } else {
+ memcpy(fc->dest_addr, sa, ETH_ALEN);
+ fc->address_mode = FCOE_GW_ADDR_MODE;
+ }
+
+ /*
+ * Remove any previously-set unicast MAC filter.
+ * Add secondary FCoE MAC address filter for our OUI.
+ */
+ rtnl_lock();
+ if (compare_ether_addr(fc->data_src_addr, (u8[6]) { 0 }))
+ dev_unicast_delete(fc->real_dev, fc->data_src_addr,
+ ETH_ALEN);
+ fc_fcoe_set_mac(fc->data_src_addr, fh->fh_d_id);
+ dev_unicast_add(fc->real_dev, fc->data_src_addr, ETH_ALEN);
+ rtnl_unlock();
+
+ fc->flogi_progress = 0;
+ } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
+ /*
+ * Save source MAC for point-to-point responses.
+ */
+ memcpy(fc->dest_addr, sa, ETH_ALEN);
+ fc->address_mode = FCOE_GW_ADDR_MODE;
+ }
+}
+
+/**
+ * fcoe_watchdog - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and put fcoe to be paused state
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ **/
+void fcoe_watchdog(ulong vp)
+{
+ struct fc_lport *lp;
+ struct fcoe_softc *fc;
+ int paused = 0;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ lp = fc->lp;
+ if (lp) {
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ paused = 1;
+ if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) {
+ if (paused)
+ fc_unpause(lp);
+ }
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+
+ fcoe_timer.expires = jiffies + (1 * HZ);
+ add_timer(&fcoe_timer);
+}
+
+
+/**
+ * fcoe_check_wait_queue - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return 0 if a error occurs, then restore wait_queue and
+ * try again later.
+ *
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_check_wait_queue(struct fc_lport *lp)
+{
+ int rc, unpause = 0;
+ int paused = 0;
+ struct sk_buff *skb;
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+
+ /*
+ * is this interface paused?
+ */
+ if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ paused = 1;
+ if (fc->fcoe_pending_queue.qlen) {
+ while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ rc = fcoe_start_io(skb);
+ if (rc) {
+ fcoe_insert_wait_queue_head(lp, skb);
+ return rc;
+ }
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ }
+ if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH)
+ unpause = 1;
+ }
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ if ((unpause) && (paused))
+ fc_unpause(lp);
+ return fc->fcoe_pending_queue.qlen;
+}
+
+/**
+ * fcoe_insert_wait_queue_head - puts skb to fcoe pending queue head
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
+static void fcoe_insert_wait_queue_head(struct fc_lport *lp,
+ struct sk_buff *skb)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ __skb_queue_head(&fc->fcoe_pending_queue, skb);
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+
+/**
+ * fcoe_insert_wait_queue - put the skb into fcoe pending queue tail
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
+static void fcoe_insert_wait_queue(struct fc_lport *lp,
+ struct sk_buff *skb)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_softc(lp);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+
+/**
+ * fcoe_dev_setup - setup link change notification interface
+ *
+ **/
+static void fcoe_dev_setup(void)
+{
+ /*
+ * here setup a interface specific wd time to
+ * monitor the link state
+ */
+ register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup - cleanup link change notification interface
+ **/
+static void fcoe_dev_cleanup(void)
+{
+ unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_device_notification(struct notifier_block *notifier,
+ ulong event, void *ptr)
+{
+ struct fc_lport *lp = NULL;
+ struct net_device *real_dev = ptr;
+ struct fcoe_softc *fc;
+ struct fcoe_dev_stats *stats;
+ u16 new_status;
+ u32 mfs;
+ int rc = NOTIFY_OK;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ if (fc->real_dev == real_dev) {
+ lp = fc->lp;
+ break;
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+ if (lp == NULL) {
+ rc = NOTIFY_DONE;
+ goto out;
+ }
+
+ new_status = lp->link_status;
+ switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_GOING_DOWN:
+ new_status &= ~FC_LINK_UP;
+ break;
+ case NETDEV_UP:
+ case NETDEV_CHANGE:
+ new_status &= ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ new_status |= FC_LINK_UP;
+ break;
+ case NETDEV_CHANGEMTU:
+ mfs = fc->real_dev->mtu -
+ (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
+ if (mfs >= FC_MIN_MAX_FRAME)
+ fc_set_mfs(lp, mfs);
+ new_status &= ~FC_LINK_UP;
+ if (!fcoe_link_ok(lp))
+ new_status |= FC_LINK_UP;
+ break;
+ case NETDEV_REGISTER:
+ break;
+ default:
+ FC_DBG("unknown event %ld call", event);
+ }
+ if (lp->link_status != new_status) {
+ if ((new_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_linkup(lp);
+ else {
+ stats = lp->dev_stats[smp_processor_id()];
+ if (stats)
+ stats->LinkFailureCount++;
+ fc_linkdown(lp);
+ fcoe_clean_pending_queue(lp);
+ }
+ }
+out:
+ return rc;
+}
+
+/**
+ * fcoe_if_to_netdev - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ **/
+static struct net_device *fcoe_if_to_netdev(const char *buffer)
+{
+ char *cp;
+ char ifname[IFNAMSIZ + 2];
+
+ if (buffer) {
+ strlcpy(ifname, buffer, IFNAMSIZ);
+ cp = ifname + strlen(ifname);
+ while (--cp >= ifname && *cp == '\n')
+ *cp = '\0';
+ return dev_get_by_name(&init_net, ifname);
+ }
+ return NULL;
+}
+
+/**
+ * fcoe_netdev_to_module_owner - finds out the nic drive moddule of the netdev
+ * @netdev: the target netdev
+ *
+ * Returns: ptr to the struct module, NULL for failure
+ **/
+static struct module *fcoe_netdev_to_module_owner(
+ const struct net_device *netdev)
+{
+ struct device *dev;
+
+ if (!netdev)
+ return NULL;
+
+ dev = netdev->dev.parent;
+ if (!dev)
+ return NULL;
+
+ if (!dev->driver)
+ return NULL;
+
+ return dev->driver->owner;
+}
+
+/**
+ * fcoe_ethdrv_get - holds the nic driver module by try_module_get() for
+ * the corresponding netdev.
+ * @netdev: the target netdev
+ *
+ * Returns: 0 for succsss
+ **/
+static int fcoe_ethdrv_get(const struct net_device *netdev)
+{
+ struct module *owner;
+
+ owner = fcoe_netdev_to_module_owner(netdev);
+ if (owner) {
+ printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+ module_name(owner), netdev->name);
+ return try_module_get(owner);
+ }
+ return -ENODEV;
+}
+
+/**
+ * fcoe_ethdrv_get - releases the nic driver module by module_put for
+ * the corresponding netdev.
+ * @netdev: the target netdev
+ *
+ * Returns: 0 for succsss
+ **/
+static int fcoe_ethdrv_put(const struct net_device *netdev)
+{
+ struct module *owner;
+
+ owner = fcoe_netdev_to_module_owner(netdev);
+ if (owner) {
+ printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+ module_name(owner), netdev->name);
+ module_put(owner);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/**
+ * fcoe_destroy- handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+ int rc;
+ struct net_device *netdev;
+
+ netdev = fcoe_if_to_netdev(buffer);
+ if (!netdev) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+ /* look for existing lport */
+ if (!fcoe_hostlist_lookup(netdev)) {
+ rc = -ENODEV;
+ goto out_putdev;
+ }
+ /* pass to transport */
+ rc = fcoe_transport_release(netdev);
+ if (rc) {
+ printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+ netdev->name);
+ rc = -EIO;
+ goto out_putdev;
+ }
+ fcoe_ethdrv_put(netdev);
+ rc = 0;
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ return rc;
+}
+
+/**
+ * fcoe_create - handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+ int rc;
+ struct net_device *netdev;
+
+ netdev = fcoe_if_to_netdev(buffer);
+ if (!netdev) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+ /* look for existing lport */
+ if (fcoe_hostlist_lookup(netdev)) {
+ rc = -EEXIST;
+ goto out_putdev;
+ }
+ fcoe_ethdrv_get(netdev);
+
+ /* pass to transport */
+ rc = fcoe_transport_attach(netdev);
+ if (rc) {
+ printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+ netdev->name);
+ fcoe_ethdrv_put(netdev);
+ rc = -EIO;
+ goto out_putdev;
+ }
+ rc = 0;
+out_putdev:
+ dev_put(netdev);
+out_nodev:
+ return rc;
+}
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/*
+ * fcoe_link_ok - check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+ struct fcoe_softc *fc = fcoe_softc(lp);
+ struct net_device *dev = fc->real_dev;
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ int rc = 0;
+
+ if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
+ dev = fc->phys_dev;
+ if (dev->ethtool_ops->get_settings) {
+ dev->ethtool_ops->get_settings(dev, &ecmd);
+ lp->link_supported_speeds &=
+ ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+ if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full))
+ lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+ if (ecmd.supported & SUPPORTED_10000baseT_Full)
+ lp->link_supported_speeds |=
+ FC_PORTSPEED_10GBIT;
+ if (ecmd.speed == SPEED_1000)
+ lp->link_speed = FC_PORTSPEED_1GBIT;
+ if (ecmd.speed == SPEED_10000)
+ lp->link_speed = FC_PORTSPEED_10GBIT;
+ }
+ } else
+ rc = -1;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/*
+ * fcoe_percpu_clean - frees skb of the corresponding lport from the per
+ * cpu queue.
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+ int idx;
+ struct fcoe_percpu_s *pp;
+ struct fcoe_rcv_info *fr;
+ struct sk_buff_head *list;
+ struct sk_buff *skb, *next;
+ struct sk_buff *head;
+
+ for (idx = 0; idx < NR_CPUS; idx++) {
+ if (fcoe_percpu[idx]) {
+ pp = fcoe_percpu[idx];
+ spin_lock_bh(&pp->fcoe_rx_list.lock);
+ list = &pp->fcoe_rx_list;
+ head = list->next;
+ for (skb = head; skb != (struct sk_buff *)list;
+ skb = next) {
+ next = skb->next;
+ fr = fcoe_dev_from_skb(skb);
+ if (fr->fr_dev == lp) {
+ __skb_unlink(skb, list);
+ kfree_skb(skb);
+ }
+ }
+ spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue - dequeue skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ **/
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+ struct fcoe_softc *fc = lport_priv(lp);
+ struct sk_buff *skb;
+
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ kfree_skb(skb);
+ spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ }
+ spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc - allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO - to libfc?
+ */
+static inline struct Scsi_Host *libfc_host_alloc(
+ struct scsi_host_template *sht, int priv_size)
+{
+ return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc - allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+ return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/*
+ * fcoe_reset - resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+ fc_lport_reset(lport);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/*
+ * fcoe_wwn_from_mac - converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+ unsigned int scheme, unsigned int port)
+{
+ u64 wwn;
+ u64 host_mac;
+
+ /* The MAC is in NO, so flip only the low 48 bits */
+ host_mac = ((u64) mac[0] << 40) |
+ ((u64) mac[1] << 32) |
+ ((u64) mac[2] << 24) |
+ ((u64) mac[3] << 16) |
+ ((u64) mac[4] << 8) |
+ (u64) mac[5];
+
+ WARN_ON(host_mac >= (1ULL << 48));
+ wwn = host_mac | ((u64) scheme << 60);
+ switch (scheme) {
+ case 1:
+ WARN_ON(port != 0);
+ break;
+ case 2:
+ WARN_ON(port >= 0xfff);
+ wwn |= (u64) port << 48;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+/*
+ * fcoe_hostlist_lookup_softc - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *fcoe_hostlist_lookup_softc(
+ const struct net_device *dev)
+{
+ struct fcoe_softc *fc;
+
+ read_lock(&fcoe_hostlist_lock);
+ list_for_each_entry(fc, &fcoe_hostlist, list) {
+ if (fc->real_dev == dev) {
+ read_unlock(&fcoe_hostlist_lock);
+ return fc;
+ }
+ }
+ read_unlock(&fcoe_hostlist_lock);
+ return NULL;
+}
+
+/*
+ * fcoe_hostlist_lookup - find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(netdev);
+
+ return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/*
+ * fcoe_hostlist_add - add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+ if (!fc) {
+ fc = fcoe_softc(lp);
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_add_tail(&fc->list, &fcoe_hostlist);
+ write_unlock_bh(&fcoe_hostlist_lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/*
+ * fcoe_hostlist_remove - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+ struct fcoe_softc *fc;
+
+ fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+ BUG_ON(!fc);
+ write_lock_bh(&fcoe_hostlist_lock);
+ list_del(&fc->list);
+ write_unlock_bh(&fcoe_hostlist_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ **/
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+ /* Set the function pointers set by the LLDD */
+ memcpy(&lp->tt, tt, sizeof(*tt));
+ if (fc_fcp_init(lp))
+ return -ENOMEM;
+ fc_exch_init(lp);
+ fc_elsct_init(lp);
+ fc_lport_init(lp);
+ fc_rport_init(lp);
+ fc_disc_init(lp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init - fcoe module loading initialization
+ *
+ * Initialization routine
+ * 1. Will create fc transport software structure
+ * 2. initialize the link list of port information structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int __init fcoe_init(void)
+{
+ int cpu;
+ struct fcoe_percpu_s *p;
+
+
+ INIT_LIST_HEAD(&fcoe_hostlist);
+ rwlock_init(&fcoe_hostlist_lock);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ register_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+ /*
+ * initialize per CPU interrupt thread
+ */
+ for_each_online_cpu(cpu) {
+ p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL);
+ if (p) {
+ p->thread = kthread_create(fcoe_percpu_receive_thread,
+ (void *)p,
+ "fcoethread/%d", cpu);
+
+ /*
+ * if there is no error then bind the thread to the cpu
+ * initialize the semaphore and skb queue head
+ */
+ if (likely(!IS_ERR(p->thread))) {
+ p->cpu = cpu;
+ fcoe_percpu[cpu] = p;
+ skb_queue_head_init(&p->fcoe_rx_list);
+ kthread_bind(p->thread, cpu);
+ wake_up_process(p->thread);
+ } else {
+ fcoe_percpu[cpu] = NULL;
+ kfree(p);
+
+ }
+ }
+ }
+
+ /*
+ * setup link change notification
+ */
+ fcoe_dev_setup();
+
+ init_timer(&fcoe_timer);
+ fcoe_timer.data = 0;
+ fcoe_timer.function = fcoe_watchdog;
+ fcoe_timer.expires = (jiffies + (10 * HZ));
+ add_timer(&fcoe_timer);
+
+ /* initiatlize the fcoe transport */
+ fcoe_transport_init();
+
+ fcoe_sw_init();
+
+ return 0;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static void __exit fcoe_exit(void)
+{
+ u32 idx;
+ struct fcoe_softc *fc, *tmp;
+ struct fcoe_percpu_s *p;
+ struct sk_buff *skb;
+
+ /*
+ * Stop all call back interfaces
+ */
+#ifdef CONFIG_HOTPLUG_CPU
+ unregister_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+ fcoe_dev_cleanup();
+
+ /*
+ * stop timer
+ */
+ del_timer_sync(&fcoe_timer);
+
+ /* releases the assocaited fcoe transport for each lport */
+ list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+ fcoe_transport_release(fc->real_dev);
+
+ for (idx = 0; idx < NR_CPUS; idx++) {
+ if (fcoe_percpu[idx]) {
+ kthread_stop(fcoe_percpu[idx]->thread);
+ p = fcoe_percpu[idx];
+ spin_lock_bh(&p->fcoe_rx_list.lock);
+ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+ kfree_skb(skb);
+ spin_unlock_bh(&p->fcoe_rx_list.lock);
+ if (fcoe_percpu[idx]->crc_eof_page)
+ put_page(fcoe_percpu[idx]->crc_eof_page);
+ kfree(fcoe_percpu[idx]);
+ }
+ }
+
+ /* remove sw trasnport */
+ fcoe_sw_exit();
+
+ /* detach the transport */
+ fcoe_transport_exit();
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 56f4e6bffc21..32eef66114c7 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -3,7 +3,7 @@
* Revised: Mon Dec 28 21:59:02 1998 by faith@acm.org
* Author: Rickard E. Faith, faith@cs.unc.edu
* Copyright 1992-1996, 1998 Rickard E. Faith (faith@acm.org)
- * Shared IRQ supported added 7/7/2001 Alan Cox <alan@redhat.com>
+ * Shared IRQ supported added 7/7/2001 Alan Cox <alan@lxorguk.ukuu.org.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
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 4d15a62914e9..9c1e6a5b5af0 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -10,7 +10,7 @@
See the WWW-page: http://www.uni-mainz.de/~langm000/linux.html for latest
updates, info and ADF-files for adapters supported by this driver.
- Alan Cox <alan@redhat.com>
+ Alan Cox <alan@lxorguk.ukuu.org.uk>
Updated for Linux 2.5.45 to use the new error handler, cleaned up the
lock macros and did a few unavoidable locking tweaks, plus one locking
fix in the irq and completion path.
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 7650707a40de..44f202f33101 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -121,6 +121,7 @@ static const struct {
{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ABORT, 0, 1, "transaction cancelled" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ABORT, 0, 1, "transaction cancelled implicit" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
+ { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
{ IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
@@ -278,13 +279,6 @@ static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
rsp->data.info.rsp_code))
return DID_ERROR << 16;
- if (!vfc_cmd->status) {
- if (rsp->flags & FCP_RESID_OVER)
- return rsp->scsi_status | (DID_ERROR << 16);
- else
- return rsp->scsi_status | (DID_OK << 16);
- }
-
err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
if (err >= 0)
return rsp->scsi_status | (cmd_status[err].result << 16);
@@ -503,6 +497,7 @@ static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
case IBMVFC_HOST_ACTION_INIT:
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_QUERY_TGTS:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_NONE:
default:
@@ -566,7 +561,7 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
struct ibmvfc_target *tgt;
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
dev_err(vhost->dev,
"Host initialization retries exceeded. Taking adapter offline\n");
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
@@ -765,6 +760,9 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
cmnd->scsi_done(cmnd);
}
+ if (evt->eh_comp)
+ complete(evt->eh_comp);
+
ibmvfc_free_event(evt);
}
@@ -847,11 +845,12 @@ static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
{
if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
- if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ vhost->delay_init = 1;
+ if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
dev_err(vhost->dev,
"Host initialization retries exceeded. Taking adapter offline\n");
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
- } else if (vhost->init_retries == IBMVFC_MAX_INIT_RETRIES)
+ } else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
__ibmvfc_reset_host(vhost);
else
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -1252,6 +1251,7 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
evt->sync_iu = NULL;
evt->crq.format = format;
evt->done = done;
+ evt->eh_comp = NULL;
}
/**
@@ -1381,6 +1381,8 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
add_timer(&evt->timer);
}
+ mb();
+
if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
list_del(&evt->queue);
del_timer(&evt->timer);
@@ -1477,6 +1479,11 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
+ if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
+ ibmvfc_reinit_host(evt->vhost);
+
+ if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
+ cmnd->result = (DID_ERROR << 16);
ibmvfc_log_error(evt);
}
@@ -1489,6 +1496,9 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
cmnd->scsi_done(cmnd);
}
+ if (evt->eh_comp)
+ complete(evt->eh_comp);
+
ibmvfc_free_event(evt);
}
@@ -1627,7 +1637,7 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
struct ibmvfc_cmd *tmf;
- struct ibmvfc_event *evt;
+ struct ibmvfc_event *evt = NULL;
union ibmvfc_iu rsp_iu;
struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
int rsp_rc = -EBUSY;
@@ -1789,7 +1799,8 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
{
struct ibmvfc_host *vhost = shost_priv(sdev->host);
- struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ struct scsi_target *starget = scsi_target(sdev);
+ struct fc_rport *rport = starget_to_rport(starget);
struct ibmvfc_tmf *tmf;
struct ibmvfc_event *evt, *found_evt;
union ibmvfc_iu rsp;
@@ -1827,7 +1838,7 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
int_to_scsilun(sdev->lun, &tmf->lun);
tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
tmf->cancel_key = (unsigned long)sdev->hostdata;
- tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata);
+ tmf->my_cancel_key = (unsigned long)starget->hostdata;
evt->sync_iu = &rsp;
init_completion(&evt->comp);
@@ -1859,6 +1870,91 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
}
/**
+ * ibmvfc_match_target - Match function for specified target
+ * @evt: ibmvfc event struct
+ * @device: device to match (starget)
+ *
+ * Returns:
+ * 1 if event matches starget / 0 if event does not match starget
+ **/
+static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
+ return 1;
+ return 0;
+}
+
+/**
+ * ibmvfc_match_lun - Match function for specified LUN
+ * @evt: ibmvfc event struct
+ * @device: device to match (sdev)
+ *
+ * Returns:
+ * 1 if event matches sdev / 0 if event does not match sdev
+ **/
+static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
+{
+ if (evt->cmnd && evt->cmnd->device == device)
+ return 1;
+ return 0;
+}
+
+/**
+ * ibmvfc_wait_for_ops - Wait for ops to complete
+ * @vhost: ibmvfc host struct
+ * @device: device to match (starget or sdev)
+ * @match: match function
+ *
+ * Returns:
+ * SUCCESS / FAILED
+ **/
+static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
+ int (*match) (struct ibmvfc_event *, void *))
+{
+ struct ibmvfc_event *evt;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ int wait;
+ unsigned long flags;
+ signed long timeout = init_timeout * HZ;
+
+ ENTER;
+ do {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = &comp;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (wait) {
+ timeout = wait_for_completion_timeout(&comp, timeout);
+
+ if (!timeout) {
+ wait = 0;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(evt, &vhost->sent, queue) {
+ if (match(evt, device)) {
+ evt->eh_comp = NULL;
+ wait++;
+ }
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (wait)
+ dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
+ LEAVE;
+ return wait ? FAILED : SUCCESS;
+ }
+ }
+ } while (wait);
+
+ LEAVE;
+ return SUCCESS;
+}
+
+/**
* ibmvfc_eh_abort_handler - Abort a command
* @cmd: scsi command to abort
*
@@ -1867,29 +1963,21 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
**/
static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
int cancel_rc, abort_rc;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET);
- abort_rc = ibmvfc_abort_task_set(cmd->device);
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+ abort_rc = ibmvfc_abort_task_set(sdev);
- if (!cancel_rc && !abort_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == cmd->device)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !abort_rc)
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -1901,29 +1989,21 @@ static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
**/
static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
int cancel_rc, reset_rc;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
ibmvfc_wait_while_resetting(vhost);
- cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET);
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN");
+ cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
- if (!cancel_rc && !reset_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && evt->cmnd->device == cmd->device)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !reset_rc)
+ rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -1959,31 +2039,23 @@ static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
**/
static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
{
- struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
- struct scsi_target *starget = scsi_target(cmd->device);
- struct ibmvfc_event *evt, *pos;
+ struct scsi_device *sdev = cmd->device;
+ struct ibmvfc_host *vhost = shost_priv(sdev->host);
+ struct scsi_target *starget = scsi_target(sdev);
int reset_rc;
+ int rc = FAILED;
unsigned long cancel_rc = 0;
- unsigned long flags;
ENTER;
ibmvfc_wait_while_resetting(vhost);
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
- reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target");
+ reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
- if (!cancel_rc && !reset_rc) {
- spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- LEAVE;
- return SUCCESS;
- }
+ if (!cancel_rc && !reset_rc)
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
LEAVE;
- return FAILED;
+ return rc;
}
/**
@@ -2013,23 +2085,18 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
struct scsi_target *starget = to_scsi_target(&rport->dev);
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
struct ibmvfc_host *vhost = shost_priv(shost);
- struct ibmvfc_event *evt, *pos;
unsigned long cancel_rc = 0;
unsigned long abort_rc = 0;
- unsigned long flags;
+ int rc = FAILED;
ENTER;
starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
- if (!cancel_rc && !abort_rc) {
- spin_lock_irqsave(shost->host_lock, flags);
- list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
- if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
- ibmvfc_fail_request(evt, DID_ABORT);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
- } else
+ if (!cancel_rc && !abort_rc)
+ rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
+
+ if (rc == FAILED)
ibmvfc_issue_fc_host_lip(shost);
LEAVE;
}
@@ -2089,15 +2156,17 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_LINK_UP:
case IBMVFC_AE_RESUME:
vhost->events_to_log |= IBMVFC_AE_LINKUP;
- ibmvfc_init_host(vhost, 1);
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
break;
case IBMVFC_AE_SCN_FABRIC:
+ case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
- ibmvfc_init_host(vhost, 1);
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
break;
case IBMVFC_AE_SCN_NPORT:
case IBMVFC_AE_SCN_GROUP:
- case IBMVFC_AE_SCN_DOMAIN:
vhost->events_to_log |= IBMVFC_AE_RSCN;
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
@@ -2263,6 +2332,28 @@ static int ibmvfc_slave_alloc(struct scsi_device *sdev)
}
/**
+ * ibmvfc_target_alloc - Setup the target's task set value
+ * @starget: struct scsi_target
+ *
+ * Set the target's task set value so that error handling works as
+ * expected.
+ *
+ * Returns:
+ * 0 on success / -ENXIO if device does not exist
+ **/
+static int ibmvfc_target_alloc(struct scsi_target *starget)
+{
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct ibmvfc_host *vhost = shost_priv(shost);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->hostdata = (void *)(unsigned long)vhost->task_set++;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ return 0;
+}
+
+/**
* ibmvfc_slave_configure - Configure the device
* @sdev: struct scsi_device device to configure
*
@@ -2541,6 +2632,7 @@ static struct scsi_host_template driver_template = {
.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
.slave_alloc = ibmvfc_slave_alloc,
.slave_configure = ibmvfc_slave_configure,
+ .target_alloc = ibmvfc_target_alloc,
.scan_finished = ibmvfc_scan_finished,
.change_queue_depth = ibmvfc_change_queue_depth,
.change_queue_type = ibmvfc_change_queue_type,
@@ -2637,7 +2729,7 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
} else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_async(async, vhost);
- crq->valid = 0;
+ async->valid = 0;
} else
done = 1;
}
@@ -2669,7 +2761,7 @@ static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
void (*job_step) (struct ibmvfc_target *))
{
- if (++tgt->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+ if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
wake_up(&tgt->vhost->work_wait_q);
} else
@@ -2708,6 +2800,8 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
rsp->status, rsp->error, status);
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -2802,6 +2896,8 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -3093,6 +3189,8 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+ else
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
break;
};
@@ -3423,6 +3521,7 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_ALLOC_TGTS:
case IBMVFC_HOST_ACTION_TGT_ADD:
case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
case IBMVFC_HOST_ACTION_QUERY:
default:
break;
@@ -3519,7 +3618,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
case IBMVFC_HOST_ACTION_INIT:
BUG_ON(vhost->state != IBMVFC_INITIALIZING);
- vhost->job_step(vhost);
+ if (vhost->delay_init) {
+ vhost->delay_init = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ssleep(15);
+ return;
+ } else
+ vhost->job_step(vhost);
break;
case IBMVFC_HOST_ACTION_QUERY:
list_for_each_entry(tgt, &vhost->targets, queue)
@@ -3538,6 +3643,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
break;
case IBMVFC_HOST_ACTION_TGT_DEL:
+ case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
list_for_each_entry(tgt, &vhost->targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
@@ -3553,8 +3659,17 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
if (vhost->state == IBMVFC_INITIALIZING) {
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
- vhost->job_step = ibmvfc_discover_targets;
+ if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
+ ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
+ vhost->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ scsi_unblock_requests(vhost->host);
+ return;
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+ vhost->job_step = ibmvfc_discover_targets;
+ }
} else {
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -3577,14 +3692,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
}
- if (!ibmvfc_dev_init_to_do(vhost)) {
- ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
- vhost->init_retries = 0;
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- scsi_unblock_requests(vhost->host);
- return;
- }
+ if (!ibmvfc_dev_init_to_do(vhost))
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
break;
case IBMVFC_HOST_ACTION_TGT_ADD:
list_for_each_entry(tgt, &vhost->targets, queue) {
@@ -3592,16 +3701,6 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_tgt_add_rport(tgt);
return;
- } else if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
- tgt_dbg(tgt, "Deleting rport\n");
- rport = tgt->rport;
- tgt->rport = NULL;
- list_del(&tgt->queue);
- spin_unlock_irqrestore(vhost->host->host_lock, flags);
- if (rport)
- fc_remote_port_delete(rport);
- kref_put(&tgt->kref, ibmvfc_release_tgt);
- return;
}
}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index fb3177ab6691..babdf3db59df 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,11 +29,11 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.2"
-#define IBMVFC_DRIVER_DATE "(August 14, 2008)"
+#define IBMVFC_DRIVER_VERSION "1.0.4"
+#define IBMVFC_DRIVER_DATE "(November 14, 2008)"
#define IBMVFC_DEFAULT_TIMEOUT 15
-#define IBMVFC_INIT_TIMEOUT 30
+#define IBMVFC_INIT_TIMEOUT 120
#define IBMVFC_MAX_REQUESTS_DEFAULT 100
#define IBMVFC_DEBUG 0
@@ -43,7 +43,8 @@
#define IBMVFC_MAX_DISC_THREADS 4
#define IBMVFC_TGT_MEMPOOL_SZ 64
#define IBMVFC_MAX_CMDS_PER_LUN 64
-#define IBMVFC_MAX_INIT_RETRIES 3
+#define IBMVFC_MAX_HOST_INIT_RETRIES 6
+#define IBMVFC_MAX_TGT_INIT_RETRIES 3
#define IBMVFC_DEV_LOSS_TMO (5 * 60)
#define IBMVFC_DEFAULT_LOG_LEVEL 2
#define IBMVFC_MAX_CDB_LEN 16
@@ -109,6 +110,7 @@ enum ibmvfc_vios_errors {
IBMVFC_TRANS_CANCELLED = 0x0006,
IBMVFC_TRANS_CANCELLED_IMPLICIT = 0x0007,
IBMVFC_INSUFFICIENT_RESOURCE = 0x0008,
+ IBMVFC_PLOGI_REQUIRED = 0x0010,
IBMVFC_COMMAND_FAILED = 0x8000,
};
@@ -337,7 +339,6 @@ struct ibmvfc_tmf {
#define IBMVFC_TMF_LUA_VALID 0x40
u32 cancel_key;
u32 my_cancel_key;
-#define IBMVFC_TMF_CANCEL_KEY 0x80000000
u32 pad;
u64 reserved[2];
}__attribute__((packed, aligned (8)));
@@ -524,10 +525,10 @@ enum ibmvfc_async_event {
};
struct ibmvfc_crq {
- u8 valid;
- u8 format;
+ volatile u8 valid;
+ volatile u8 format;
u8 reserved[6];
- u64 ioba;
+ volatile u64 ioba;
}__attribute__((packed, aligned (8)));
struct ibmvfc_crq_queue {
@@ -537,13 +538,13 @@ struct ibmvfc_crq_queue {
};
struct ibmvfc_async_crq {
- u8 valid;
+ volatile u8 valid;
u8 pad[3];
u32 pad2;
- u64 event;
- u64 scsi_id;
- u64 wwpn;
- u64 node_name;
+ volatile u64 event;
+ volatile u64 scsi_id;
+ volatile u64 wwpn;
+ volatile u64 node_name;
u64 reserved;
}__attribute__((packed, aligned (8)));
@@ -606,6 +607,7 @@ struct ibmvfc_event {
struct srp_direct_buf *ext_list;
dma_addr_t ext_list_token;
struct completion comp;
+ struct completion *eh_comp;
struct timer_list timer;
};
@@ -626,6 +628,7 @@ enum ibmvfc_host_action {
IBMVFC_HOST_ACTION_TGT_DEL,
IBMVFC_HOST_ACTION_ALLOC_TGTS,
IBMVFC_HOST_ACTION_TGT_INIT,
+ IBMVFC_HOST_ACTION_TGT_DEL_FAILED,
IBMVFC_HOST_ACTION_TGT_ADD,
};
@@ -671,6 +674,7 @@ struct ibmvfc_host {
int discovery_threads;
int client_migrated;
int reinit;
+ int delay_init;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
@@ -700,7 +704,7 @@ struct ibmvfc_host {
#define ibmvfc_log(vhost, level, ...) \
do { \
- if (level >= (vhost)->log_level) \
+ if ((vhost)->log_level >= level) \
dev_err((vhost)->dev, ##__VA_ARGS__); \
} while (0)
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 6cad1758243a..868d35ea01bb 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -107,7 +107,7 @@ module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_channel, "Largest channel value");
module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds");
-module_param_named(max_requests, max_requests, int, S_IRUGO | S_IWUSR);
+module_param_named(max_requests, max_requests, int, S_IRUGO);
MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter");
/* ------------------------------------------------------------
@@ -1657,7 +1657,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vdev->dev.driver_data = NULL;
- driver_template.can_queue = max_requests;
+ driver_template.can_queue = max_requests - 2;
host = scsi_host_alloc(&driver_template, sizeof(*hostdata));
if (!host) {
dev_err(&vdev->dev, "couldn't allocate host data\n");
diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c
index 2a5b29d12172..e2dd6a45924a 100644
--- a/drivers/scsi/ibmvscsi/ibmvstgt.c
+++ b/drivers/scsi/ibmvscsi/ibmvstgt.c
@@ -864,21 +864,23 @@ static int ibmvstgt_probe(struct vio_dev *dev, const struct vio_device_id *id)
INIT_WORK(&vport->crq_work, handle_crq);
- err = crq_queue_create(&vport->crq_queue, target);
+ err = scsi_add_host(shost, target->dev);
if (err)
goto free_srp_target;
- err = scsi_add_host(shost, target->dev);
+ err = scsi_tgt_alloc_queue(shost);
if (err)
- goto destroy_queue;
+ goto remove_host;
- err = scsi_tgt_alloc_queue(shost);
+ err = crq_queue_create(&vport->crq_queue, target);
if (err)
- goto destroy_queue;
+ goto free_queue;
return 0;
-destroy_queue:
- crq_queue_destroy(target);
+free_queue:
+ scsi_tgt_free_queue(shost);
+remove_host:
+ scsi_remove_host(shost);
free_srp_target:
srp_target_free(target);
put_host:
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 2370fd82ebfe..c24140aff8e7 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -578,6 +578,8 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
{
idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
ide_drive_t *drive = scsi->drive;
+ ide_hwif_t *hwif;
+ ide_hwgroup_t *hwgroup;
int busy;
int ret = FAILED;
@@ -594,13 +596,16 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
goto no_drive;
}
- /* First give it some more time, how much is "right" is hard to say :-( */
+ hwif = drive->hwif;
+ hwgroup = hwif->hwgroup;
- busy = ide_wait_not_busy(HWIF(drive), 100); /* FIXME - uses mdelay which causes latency? */
+ /* First give it some more time, how much is "right" is hard to say :-(
+ FIXME - uses mdelay which causes latency? */
+ busy = ide_wait_not_busy(hwif, 100);
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
printk (KERN_WARNING "ide-scsi: drive did%s become ready\n", busy?" not":"");
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&hwgroup->lock);
/* If there is no pc running we're done (our interrupt took care of it) */
pc = drive->pc;
@@ -629,7 +634,7 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
}
ide_unlock:
- spin_unlock_irq(&ide_lock);
+ spin_unlock_irq(&hwgroup->lock);
no_drive:
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
printk (KERN_WARNING "ide-scsi: abort returns %s\n", ret == SUCCESS?"success":"failed");
@@ -642,6 +647,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
struct request *req;
idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host);
ide_drive_t *drive = scsi->drive;
+ ide_hwgroup_t *hwgroup;
int ready = 0;
int ret = SUCCESS;
@@ -658,14 +664,18 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
return FAILED;
}
+ hwgroup = drive->hwif->hwgroup;
+
spin_lock_irq(cmd->device->host->host_lock);
- spin_lock(&ide_lock);
+ spin_lock(&hwgroup->lock);
pc = drive->pc;
+ if (pc)
+ req = pc->rq;
- if (pc == NULL || (req = pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) {
+ if (pc == NULL || req != hwgroup->rq || hwgroup->handler == NULL) {
printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n");
- spin_unlock(&ide_lock);
+ spin_unlock(&hwgroup->lock);
spin_unlock_irq(cmd->device->host->host_lock);
return FAILED;
}
@@ -685,10 +695,10 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
BUG();
}
- HWGROUP(drive)->rq = NULL;
- HWGROUP(drive)->handler = NULL;
- HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */
- spin_unlock(&ide_lock);
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ hwgroup->busy = 1; /* will set this to zero when ide reset finished */
+ spin_unlock(&hwgroup->lock);
ide_do_reset(drive);
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 8053b1e86ccb..52bdc6df6b92 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -107,7 +107,7 @@
* this thing into as good a shape as possible, and I'm positive
* there are lots of lurking bugs and "Stupid Places".
*
- * Updated for Linux 2.5 by Alan Cox <alan@redhat.com>
+ * Updated for Linux 2.5 by Alan Cox <alan@lxorguk.ukuu.org.uk>
* - Using new_eh handler
* - Hopefully got all the locking right again
* See "FIXME" notes for items that could do with more work
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index e3f739776bad..5529518ff2fa 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -4,7 +4,7 @@
* Copyright (c) 1994-1998 Initio Corporation
* Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
* Copyright (c) 2004 Christoph Hellwig <hch@lst.de>
- * Copyright (c) 2007 Red Hat <alan@redhat.com>
+ * Copyright (c) 2007 Red Hat
*
* This 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/scsi/initio.h b/drivers/scsi/initio.h
index cb48efa81fe2..e58af9e95506 100644
--- a/drivers/scsi/initio.h
+++ b/drivers/scsi/initio.h
@@ -4,7 +4,7 @@
* Copyright (c) 1994-1998 Initio Corporation
* All rights reserved.
*
- * Cleanups (c) Copyright 2007 Red Hat <alan@redhat.com>
+ * Cleanups (c) Copyright 2007 Red Hat <alan@lxorguk.ukuu.org.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
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index ded854a6dd35..0edfb1fa63ce 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -5389,9 +5389,9 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd)
list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
wake_up_all(&ioa_cfg->reset_wait_q);
- spin_unlock_irq(ioa_cfg->host->host_lock);
+ spin_unlock(ioa_cfg->host->host_lock);
scsi_unblock_requests(ioa_cfg->host);
- spin_lock_irq(ioa_cfg->host->host_lock);
+ spin_lock(ioa_cfg->host->host_lock);
if (!ioa_cfg->allow_cmds)
scsi_block_requests(ioa_cfg->host);
@@ -7473,7 +7473,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev,
goto out_scsi_host_put;
}
- ipr_regs = ioremap(ipr_regs_pci, pci_resource_len(pdev, 0));
+ ipr_regs = pci_ioremap_bar(pdev, 0);
if (!ipr_regs) {
dev_err(&pdev->dev,
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 4871dd1f2582..59459141b437 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -19,7 +19,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * Alan Cox <alan@redhat.com> - Removed several careless u32/dma_addr_t errors
+ * Alan Cox <alan@lxorguk.ukuu.org.uk> - Removed several careless u32/dma_addr_t errors
* that broke 64bit platforms.
*/
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index ed6c54cae7b1..23808dfe22ba 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -27,7 +27,6 @@
*/
#include <linux/types.h>
-#include <linux/list.h>
#include <linux/inet.h>
#include <linux/file.h>
#include <linux/blkdev.h>
@@ -44,12 +43,12 @@
#include "iscsi_tcp.h"
-MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
+ "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
"Alex Aizman <itn780@yahoo.com>");
MODULE_DESCRIPTION("iSCSI/TCP data-path");
MODULE_LICENSE("GPL");
#undef DEBUG_TCP
-#define DEBUG_ASSERT
#ifdef DEBUG_TCP
#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
@@ -57,934 +56,41 @@ MODULE_LICENSE("GPL");
#define debug_tcp(fmt...)
#endif
-#ifndef DEBUG_ASSERT
-#ifdef BUG_ON
-#undef BUG_ON
-#endif
-#define BUG_ON(expr)
-#endif
-
-static struct scsi_transport_template *iscsi_tcp_scsi_transport;
-static struct scsi_host_template iscsi_sht;
-static struct iscsi_transport iscsi_tcp_transport;
+static struct scsi_transport_template *iscsi_sw_tcp_scsi_transport;
+static struct scsi_host_template iscsi_sw_tcp_sht;
+static struct iscsi_transport iscsi_sw_tcp_transport;
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
-static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment);
-
-/*
- * Scatterlist handling: inside the iscsi_segment, we
- * remember an index into the scatterlist, and set data/size
- * to the current scatterlist entry. For highmem pages, we
- * kmap as needed.
- *
- * Note that the page is unmapped when we return from
- * TCP's data_ready handler, so we may end up mapping and
- * unmapping the same page repeatedly. The whole reason
- * for this is that we shouldn't keep the page mapped
- * outside the softirq.
- */
-
-/**
- * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
- * @segment: the buffer object
- * @sg: scatterlist
- * @offset: byte offset into that sg entry
- *
- * This function sets up the segment so that subsequent
- * data is copied to the indicated sg entry, at the given
- * offset.
- */
-static inline void
-iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
- struct scatterlist *sg, unsigned int offset)
-{
- segment->sg = sg;
- segment->sg_offset = offset;
- segment->size = min(sg->length - offset,
- segment->total_size - segment->total_copied);
- segment->data = NULL;
-}
-
-/**
- * iscsi_tcp_segment_map - map the current S/G page
- * @segment: iscsi_segment
- * @recv: 1 if called from recv path
- *
- * We only need to possibly kmap data if scatter lists are being used,
- * because the iscsi passthrough and internal IO paths will never use high
- * mem pages.
- */
-static inline void
-iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
-{
- struct scatterlist *sg;
-
- if (segment->data != NULL || !segment->sg)
- return;
-
- sg = segment->sg;
- BUG_ON(segment->sg_mapped);
- BUG_ON(sg->length == 0);
-
- /*
- * If the page count is greater than one it is ok to send
- * to the network layer's zero copy send path. If not we
- * have to go the slow sendmsg path. We always map for the
- * recv path.
- */
- if (page_count(sg_page(sg)) >= 1 && !recv)
- return;
-
- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
- segment);
- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
- segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
-}
-
-static inline void
-iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
-{
- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
-
- if (segment->sg_mapped) {
- debug_tcp("iscsi_tcp_segment_unmap valid\n");
- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
- segment->sg_mapped = NULL;
- segment->data = NULL;
- }
-}
-
-/*
- * Splice the digest buffer into the buffer
- */
-static inline void
-iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
-{
- segment->data = digest;
- segment->digest_len = ISCSI_DIGEST_SIZE;
- segment->total_size += ISCSI_DIGEST_SIZE;
- segment->size = ISCSI_DIGEST_SIZE;
- segment->copied = 0;
- segment->sg = NULL;
- segment->hash = NULL;
-}
-
-/**
- * iscsi_tcp_segment_done - check whether the segment is complete
- * @segment: iscsi segment to check
- * @recv: set to one of this is called from the recv path
- * @copied: number of bytes copied
- *
- * Check if we're done receiving this segment. If the receive
- * buffer is full but we expect more data, move on to the
- * next entry in the scatterlist.
- *
- * If the amount of data we received isn't a multiple of 4,
- * we will transparently receive the pad bytes, too.
- *
- * This function must be re-entrant.
- */
-static inline int
-iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied)
-{
- static unsigned char padbuf[ISCSI_PAD_LEN];
- struct scatterlist sg;
- unsigned int pad;
-
- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
- segment->size, recv ? "recv" : "xmit");
- if (segment->hash && copied) {
- /*
- * If a segment is kmapd we must unmap it before sending
- * to the crypto layer since that will try to kmap it again.
- */
- iscsi_tcp_segment_unmap(segment);
-
- if (!segment->data) {
- sg_init_table(&sg, 1);
- sg_set_page(&sg, sg_page(segment->sg), copied,
- segment->copied + segment->sg_offset +
- segment->sg->offset);
- } else
- sg_init_one(&sg, segment->data + segment->copied,
- copied);
- crypto_hash_update(segment->hash, &sg, copied);
- }
-
- segment->copied += copied;
- if (segment->copied < segment->size) {
- iscsi_tcp_segment_map(segment, recv);
- return 0;
- }
-
- segment->total_copied += segment->copied;
- segment->copied = 0;
- segment->size = 0;
-
- /* Unmap the current scatterlist page, if there is one. */
- iscsi_tcp_segment_unmap(segment);
-
- /* Do we have more scatterlist entries? */
- debug_tcp("total copied %u total size %u\n", segment->total_copied,
- segment->total_size);
- if (segment->total_copied < segment->total_size) {
- /* Proceed to the next entry in the scatterlist. */
- iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
- 0);
- iscsi_tcp_segment_map(segment, recv);
- BUG_ON(segment->size == 0);
- return 0;
- }
-
- /* Do we need to handle padding? */
- pad = iscsi_padding(segment->total_copied);
- if (pad != 0) {
- debug_tcp("consume %d pad bytes\n", pad);
- segment->total_size += pad;
- segment->size = pad;
- segment->data = padbuf;
- return 0;
- }
-
- /*
- * Set us up for transferring the data digest. hdr digest
- * is completely handled in hdr done function.
- */
- if (segment->hash) {
- crypto_hash_final(segment->hash, segment->digest);
- iscsi_tcp_segment_splice_digest(segment,
- recv ? segment->recv_digest : segment->digest);
- return 0;
- }
-
- return 1;
-}
-
-/**
- * iscsi_tcp_xmit_segment - transmit segment
- * @tcp_conn: the iSCSI TCP connection
- * @segment: the buffer to transmnit
- *
- * This function transmits as much of the buffer as
- * the network layer will accept, and returns the number of
- * bytes transmitted.
- *
- * If CRC hashing is enabled, the function will compute the
- * hash as it goes. When the entire segment has been transmitted,
- * it will retrieve the hash value and send it as well.
- */
-static int
-iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct socket *sk = tcp_conn->sock;
- unsigned int copied = 0;
- int r = 0;
-
- while (!iscsi_tcp_segment_done(segment, 0, r)) {
- struct scatterlist *sg;
- unsigned int offset, copy;
- int flags = 0;
-
- r = 0;
- offset = segment->copied;
- copy = segment->size - offset;
-
- if (segment->total_copied + segment->size < segment->total_size)
- flags |= MSG_MORE;
-
- /* Use sendpage if we can; else fall back to sendmsg */
- if (!segment->data) {
- sg = segment->sg;
- offset += segment->sg_offset + sg->offset;
- r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy,
- flags);
- } else {
- struct msghdr msg = { .msg_flags = flags };
- struct kvec iov = {
- .iov_base = segment->data + offset,
- .iov_len = copy
- };
-
- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
- }
-
- if (r < 0) {
- iscsi_tcp_segment_unmap(segment);
- if (copied || r == -EAGAIN)
- break;
- return r;
- }
- copied += r;
- }
- return copied;
-}
-
-/**
- * iscsi_tcp_segment_recv - copy data to segment
- * @tcp_conn: the iSCSI TCP connection
- * @segment: the buffer to copy to
- * @ptr: data pointer
- * @len: amount of data available
- *
- * This function copies up to @len bytes to the
- * given buffer, and returns the number of bytes
- * consumed, which can actually be less than @len.
- *
- * If hash digest is enabled, the function will update the
- * hash while copying.
- * Combining these two operations doesn't buy us a lot (yet),
- * but in the future we could implement combined copy+crc,
- * just way we do for network layer checksums.
- */
-static int
-iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment, const void *ptr,
- unsigned int len)
-{
- unsigned int copy = 0, copied = 0;
-
- while (!iscsi_tcp_segment_done(segment, 1, copy)) {
- if (copied == len) {
- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
- len);
- break;
- }
-
- copy = min(len - copied, segment->size - segment->copied);
- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
- memcpy(segment->data + segment->copied, ptr + copied, copy);
- copied += copy;
- }
- return copied;
-}
-
-static inline void
-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
- unsigned char digest[ISCSI_DIGEST_SIZE])
-{
- struct scatterlist sg;
-
- sg_init_one(&sg, hdr, hdrlen);
- crypto_hash_digest(hash, &sg, hdrlen, digest);
-}
-
-static inline int
-iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- if (!segment->digest_len)
- return 1;
-
- if (memcmp(segment->recv_digest, segment->digest,
- segment->digest_len)) {
- debug_scsi("digest mismatch\n");
- return 0;
- }
-
- return 1;
-}
-
-/*
- * Helper function to set up segment buffer
- */
-static inline void
-__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
-{
- memset(segment, 0, sizeof(*segment));
- segment->total_size = size;
- segment->done = done;
-
- if (hash) {
- segment->hash = hash;
- crypto_hash_init(hash);
- }
-}
-
-static inline void
-iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
- size_t size, iscsi_segment_done_fn_t *done,
- struct hash_desc *hash)
-{
- __iscsi_segment_init(segment, size, done, hash);
- segment->data = data;
- segment->size = size;
-}
-
-static inline int
-iscsi_segment_seek_sg(struct iscsi_segment *segment,
- struct scatterlist *sg_list, unsigned int sg_count,
- unsigned int offset, size_t size,
- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
-{
- struct scatterlist *sg;
- unsigned int i;
-
- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
- offset, size);
- __iscsi_segment_init(segment, size, done, hash);
- for_each_sg(sg_list, sg, sg_count, i) {
- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
- sg->offset);
- if (offset < sg->length) {
- iscsi_tcp_segment_init_sg(segment, sg, offset);
- return 0;
- }
- offset -= sg->length;
- }
-
- return ISCSI_ERR_DATA_OFFSET;
-}
-
-/**
- * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
- * @tcp_conn: iscsi connection to prep for
- *
- * This function always passes NULL for the hash argument, because when this
- * function is called we do not yet know the final size of the header and want
- * to delay the digest processing until we know that.
- */
-static void
-iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
-{
- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
- iscsi_segment_init_linear(&tcp_conn->in.segment,
- tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
- iscsi_tcp_hdr_recv_done, NULL);
-}
-
-/*
- * Handle incoming reply to any other type of command
- */
-static int
-iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- int rc = 0;
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_DATA_DGST;
-
- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
- conn->data, tcp_conn->in.datalen);
- if (rc)
- return rc;
-
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
-}
-
-static void
-iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct hash_desc *rx_hash = NULL;
-
- if (conn->datadgst_en)
- rx_hash = &tcp_conn->rx_hash;
-
- iscsi_segment_init_linear(&tcp_conn->in.segment,
- conn->data, tcp_conn->in.datalen,
- iscsi_tcp_data_recv_done, rx_hash);
-}
-
-/*
- * must be called with session lock
- */
-static void
-iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_r2t_info *r2t;
-
- /* nothing to do for mgmt tasks */
- if (!task->sc)
- return;
-
- /* flush task's r2t queues */
- while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
- }
-
- r2t = tcp_task->r2t;
- if (r2t != NULL) {
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- tcp_task->r2t = NULL;
- }
-}
-
-/**
- * iscsi_data_in - SCSI Data-In Response processing
- * @conn: iscsi connection
- * @task: scsi command task
- **/
-static int
-iscsi_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
- int datasn = be32_to_cpu(rhdr->datasn);
- unsigned total_in_length = scsi_in(task->sc)->length;
-
- iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
- if (tcp_conn->in.datalen == 0)
- return 0;
-
- if (tcp_task->exp_datasn != datasn) {
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
- __func__, tcp_task->exp_datasn, datasn);
- return ISCSI_ERR_DATASN;
- }
-
- tcp_task->exp_datasn++;
-
- tcp_task->data_offset = be32_to_cpu(rhdr->offset);
- if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
- __func__, tcp_task->data_offset,
- tcp_conn->in.datalen, total_in_length);
- return ISCSI_ERR_DATA_OFFSET;
- }
-
- conn->datain_pdus_cnt++;
- return 0;
-}
-
-/**
- * iscsi_solicit_data_init - initialize first Data-Out
- * @conn: iscsi connection
- * @task: scsi command task
- * @r2t: R2T info
- *
- * Notes:
- * Initialize first Data-Out within this R2T sequence and finds
- * proper data_offset within this SCSI command.
- *
- * This function is called with connection lock taken.
- **/
-static void
-iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task,
- struct iscsi_r2t_info *r2t)
-{
- struct iscsi_data *hdr;
-
- hdr = &r2t->dtask.hdr;
- memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = r2t->ttt;
- hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
- r2t->solicit_datasn++;
- hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = r2t->exp_statsn;
- hdr->offset = cpu_to_be32(r2t->data_offset);
- if (r2t->data_length > conn->max_xmit_dlength) {
- hton24(hdr->dlength, conn->max_xmit_dlength);
- r2t->data_count = conn->max_xmit_dlength;
- hdr->flags = 0;
- } else {
- hton24(hdr->dlength, r2t->data_length);
- r2t->data_count = r2t->data_length;
- hdr->flags = ISCSI_FLAG_CMD_FINAL;
- }
- conn->dataout_pdus_cnt++;
-
- r2t->sent = 0;
-}
-
-/**
- * iscsi_r2t_rsp - iSCSI R2T Response processing
- * @conn: iscsi connection
- * @task: scsi command task
- **/
-static int
-iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
-{
- struct iscsi_r2t_info *r2t;
- struct iscsi_session *session = conn->session;
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
- int r2tsn = be32_to_cpu(rhdr->r2tsn);
- int rc;
-
- if (tcp_conn->in.datalen) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2t with datalen %d\n",
- tcp_conn->in.datalen);
- return ISCSI_ERR_DATALEN;
- }
-
- if (tcp_task->exp_datasn != r2tsn){
- debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
- __func__, tcp_task->exp_datasn, r2tsn);
- return ISCSI_ERR_R2TSN;
- }
-
- /* fill-in new R2T associated with the task */
- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
-
- if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
- iscsi_conn_printk(KERN_INFO, conn,
- "dropping R2T itt %d in recovery.\n",
- task->itt);
- return 0;
- }
-
- rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
- BUG_ON(!rc);
-
- r2t->exp_statsn = rhdr->statsn;
- r2t->data_length = be32_to_cpu(rhdr->data_length);
- if (r2t->data_length == 0) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2T with zero data len\n");
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
- }
-
- if (r2t->data_length > session->max_burst)
- debug_scsi("invalid R2T with data len %u and max burst %u."
- "Attempting to execute request.\n",
- r2t->data_length, session->max_burst);
-
- r2t->data_offset = be32_to_cpu(rhdr->data_offset);
- if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
- iscsi_conn_printk(KERN_ERR, conn,
- "invalid R2T with data len %u at offset %u "
- "and total length %d\n", r2t->data_length,
- r2t->data_offset, scsi_out(task->sc)->length);
- __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- return ISCSI_ERR_DATALEN;
- }
-
- r2t->ttt = rhdr->ttt; /* no flip */
- r2t->solicit_datasn = 0;
-
- iscsi_solicit_data_init(conn, task, r2t);
-
- tcp_task->exp_datasn = r2tsn + 1;
- __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
- conn->r2t_pdus_cnt++;
-
- iscsi_requeue_task(task);
- return 0;
-}
-
-/*
- * Handle incoming reply to DataIn command
- */
-static int
-iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct iscsi_hdr *hdr = tcp_conn->in.hdr;
- int rc;
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_DATA_DGST;
-
- /* check for non-exceptional status */
- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
- if (rc)
- return rc;
- }
-
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- return 0;
-}
-
-/**
- * iscsi_tcp_hdr_dissect - process PDU header
- * @conn: iSCSI connection
- * @hdr: PDU header
- *
- * This function analyzes the header of the PDU received,
- * and performs several sanity checks. If the PDU is accompanied
- * by data, the receive buffer is set up to copy the incoming data
- * to the correct location.
- */
-static int
-iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
-{
- int rc = 0, opcode, ahslen;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_task *task;
-
- /* verify PDU length */
- tcp_conn->in.datalen = ntoh24(hdr->dlength);
- if (tcp_conn->in.datalen > conn->max_recv_dlength) {
- iscsi_conn_printk(KERN_ERR, conn,
- "iscsi_tcp: datalen %d > %d\n",
- tcp_conn->in.datalen, conn->max_recv_dlength);
- return ISCSI_ERR_DATALEN;
- }
-
- /* Additional header segments. So far, we don't
- * process additional headers.
- */
- ahslen = hdr->hlength << 2;
-
- opcode = hdr->opcode & ISCSI_OPCODE_MASK;
- /* verify itt (itt encoding: age+cid+itt) */
- rc = iscsi_verify_itt(conn, hdr->itt);
- if (rc)
- return rc;
-
- debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
- opcode, ahslen, tcp_conn->in.datalen);
-
- switch(opcode) {
- case ISCSI_OP_SCSI_DATA_IN:
- spin_lock(&conn->session->lock);
- task = iscsi_itt_to_ctask(conn, hdr->itt);
- if (!task)
- rc = ISCSI_ERR_BAD_ITT;
- else
- rc = iscsi_data_in(conn, task);
- if (rc) {
- spin_unlock(&conn->session->lock);
- break;
- }
-
- if (tcp_conn->in.datalen) {
- struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct hash_desc *rx_hash = NULL;
- struct scsi_data_buffer *sdb = scsi_in(task->sc);
-
- /*
- * Setup copy of Data-In into the Scsi_Cmnd
- * Scatterlist case:
- * We set up the iscsi_segment to point to the next
- * scatterlist entry to copy to. As we go along,
- * we move on to the next scatterlist entry and
- * update the digest per-entry.
- */
- if (conn->datadgst_en)
- rx_hash = &tcp_conn->rx_hash;
-
- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
- "datalen=%d)\n", tcp_conn,
- tcp_task->data_offset,
- tcp_conn->in.datalen);
- rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
- sdb->table.sgl,
- sdb->table.nents,
- tcp_task->data_offset,
- tcp_conn->in.datalen,
- iscsi_tcp_process_data_in,
- rx_hash);
- spin_unlock(&conn->session->lock);
- return rc;
- }
- rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
- spin_unlock(&conn->session->lock);
- break;
- case ISCSI_OP_SCSI_CMD_RSP:
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
- case ISCSI_OP_R2T:
- spin_lock(&conn->session->lock);
- task = iscsi_itt_to_ctask(conn, hdr->itt);
- if (!task)
- rc = ISCSI_ERR_BAD_ITT;
- else if (ahslen)
- rc = ISCSI_ERR_AHSLEN;
- else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
- rc = iscsi_r2t_rsp(conn, task);
- else
- rc = ISCSI_ERR_PROTO;
- spin_unlock(&conn->session->lock);
- break;
- case ISCSI_OP_LOGIN_RSP:
- case ISCSI_OP_TEXT_RSP:
- case ISCSI_OP_REJECT:
- case ISCSI_OP_ASYNC_EVENT:
- /*
- * It is possible that we could get a PDU with a buffer larger
- * than 8K, but there are no targets that currently do this.
- * For now we fail until we find a vendor that needs it
- */
- if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) {
- iscsi_conn_printk(KERN_ERR, conn,
- "iscsi_tcp: received buffer of "
- "len %u but conn buffer is only %u "
- "(opcode %0x)\n",
- tcp_conn->in.datalen,
- ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);
- rc = ISCSI_ERR_PROTO;
- break;
- }
-
- /* If there's data coming in with the response,
- * receive it to the connection's buffer.
- */
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- /* fall through */
- case ISCSI_OP_LOGOUT_RSP:
- case ISCSI_OP_NOOP_IN:
- case ISCSI_OP_SCSI_TMFUNC_RSP:
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
- default:
- rc = ISCSI_ERR_BAD_OPCODE;
- break;
- }
-
- if (rc == 0) {
- /* Anything that comes with data should have
- * been handled above. */
- if (tcp_conn->in.datalen)
- return ISCSI_ERR_PROTO;
- iscsi_tcp_hdr_recv_prep(tcp_conn);
- }
-
- return rc;
-}
-
/**
- * iscsi_tcp_hdr_recv_done - process PDU header
- *
- * This is the callback invoked when the PDU header has
- * been received. If the header is followed by additional
- * header segments, we go back for more data.
- */
-static int
-iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
-{
- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
- struct iscsi_hdr *hdr;
-
- /* Check if there are additional header segments
- * *prior* to computing the digest, because we
- * may need to go back to the caller for more.
- */
- hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
- if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
- /* Bump the header length - the caller will
- * just loop around and get the AHS for us, and
- * call again. */
- unsigned int ahslen = hdr->hlength << 2;
-
- /* Make sure we don't overflow */
- if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
- return ISCSI_ERR_AHSLEN;
-
- segment->total_size += ahslen;
- segment->size += ahslen;
- return 0;
- }
-
- /* We're done processing the header. See if we're doing
- * header digests; if so, set up the recv_digest buffer
- * and go back for more. */
- if (conn->hdrdgst_en) {
- if (segment->digest_len == 0) {
- iscsi_tcp_segment_splice_digest(segment,
- segment->recv_digest);
- return 0;
- }
- iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
- segment->total_copied - ISCSI_DIGEST_SIZE,
- segment->digest);
-
- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
- return ISCSI_ERR_HDR_DGST;
- }
-
- tcp_conn->in.hdr = hdr;
- return iscsi_tcp_hdr_dissect(conn, hdr);
-}
-
-/**
- * iscsi_tcp_recv - TCP receive in sendfile fashion
+ * iscsi_sw_tcp_recv - TCP receive in sendfile fashion
* @rd_desc: read descriptor
* @skb: socket buffer
* @offset: offset in skb
* @len: skb->len - offset
- **/
-static int
-iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
- unsigned int offset, size_t len)
+ */
+static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+ unsigned int offset, size_t len)
{
struct iscsi_conn *conn = rd_desc->arg.data;
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->in.segment;
- struct skb_seq_state seq;
- unsigned int consumed = 0;
- int rc = 0;
+ unsigned int consumed, total_consumed = 0;
+ int status;
debug_tcp("in %d bytes\n", skb->len - offset);
- if (unlikely(conn->suspend_rx)) {
- debug_tcp("conn %d Rx suspended!\n", conn->id);
- return 0;
- }
+ do {
+ status = 0;
+ consumed = iscsi_tcp_recv_skb(conn, skb, offset, 0, &status);
+ offset += consumed;
+ total_consumed += consumed;
+ } while (consumed != 0 && status != ISCSI_TCP_SKB_DONE);
- skb_prepare_seq_read(skb, offset, skb->len, &seq);
- while (1) {
- unsigned int avail;
- const u8 *ptr;
-
- avail = skb_seq_read(consumed, &ptr, &seq);
- if (avail == 0) {
- debug_tcp("no more data avail. Consumed %d\n",
- consumed);
- break;
- }
- BUG_ON(segment->copied >= segment->size);
-
- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
- rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
- BUG_ON(rc == 0);
- consumed += rc;
-
- if (segment->total_copied >= segment->total_size) {
- debug_tcp("segment done\n");
- rc = segment->done(tcp_conn, segment);
- if (rc != 0) {
- skb_abort_seq_read(&seq);
- goto error;
- }
-
- /* The done() functions sets up the
- * next segment. */
- }
- }
- skb_abort_seq_read(&seq);
- conn->rxdata_octets += consumed;
- return consumed;
-
-error:
- debug_tcp("Error receiving PDU, errno=%d\n", rc);
- iscsi_conn_failure(conn, rc);
- return 0;
+ debug_tcp("read %d bytes status %d\n", skb->len - offset, status);
+ return total_consumed;
}
-static void
-iscsi_tcp_data_ready(struct sock *sk, int flag)
+static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
{
struct iscsi_conn *conn = sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
@@ -1000,7 +106,7 @@ iscsi_tcp_data_ready(struct sock *sk, int flag)
*/
rd_desc.arg.data = conn;
rd_desc.count = 1;
- tcp_read_sock(sk, &rd_desc, iscsi_tcp_recv);
+ tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
read_unlock(&sk->sk_callback_lock);
@@ -1009,10 +115,10 @@ iscsi_tcp_data_ready(struct sock *sk, int flag)
iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
}
-static void
-iscsi_tcp_state_change(struct sock *sk)
+static void iscsi_sw_tcp_state_change(struct sock *sk)
{
struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
struct iscsi_conn *conn;
struct iscsi_session *session;
void (*old_state_change)(struct sock *);
@@ -1030,7 +136,8 @@ iscsi_tcp_state_change(struct sock *sk)
}
tcp_conn = conn->dd_data;
- old_state_change = tcp_conn->old_state_change;
+ tcp_sw_conn = tcp_conn->dd_data;
+ old_state_change = tcp_sw_conn->old_state_change;
read_unlock(&sk->sk_callback_lock);
@@ -1041,63 +148,123 @@ iscsi_tcp_state_change(struct sock *sk)
* iscsi_write_space - Called when more output buffer space is available
* @sk: socket space is available for
**/
-static void
-iscsi_write_space(struct sock *sk)
+static void iscsi_sw_tcp_write_space(struct sock *sk)
{
struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- tcp_conn->old_write_space(sk);
+ tcp_sw_conn->old_write_space(sk);
debug_tcp("iscsi_write_space: cid %d\n", conn->id);
scsi_queue_work(conn->session->host, &conn->xmitwork);
}
-static void
-iscsi_conn_set_callbacks(struct iscsi_conn *conn)
+static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct sock *sk = tcp_conn->sock->sk;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct sock *sk = tcp_sw_conn->sock->sk;
/* assign new callbacks */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = conn;
- tcp_conn->old_data_ready = sk->sk_data_ready;
- tcp_conn->old_state_change = sk->sk_state_change;
- tcp_conn->old_write_space = sk->sk_write_space;
- sk->sk_data_ready = iscsi_tcp_data_ready;
- sk->sk_state_change = iscsi_tcp_state_change;
- sk->sk_write_space = iscsi_write_space;
+ tcp_sw_conn->old_data_ready = sk->sk_data_ready;
+ tcp_sw_conn->old_state_change = sk->sk_state_change;
+ tcp_sw_conn->old_write_space = sk->sk_write_space;
+ sk->sk_data_ready = iscsi_sw_tcp_data_ready;
+ sk->sk_state_change = iscsi_sw_tcp_state_change;
+ sk->sk_write_space = iscsi_sw_tcp_write_space;
write_unlock_bh(&sk->sk_callback_lock);
}
static void
-iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_sw_tcp_conn *tcp_sw_conn)
{
- struct sock *sk = tcp_conn->sock->sk;
+ struct sock *sk = tcp_sw_conn->sock->sk;
/* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
write_lock_bh(&sk->sk_callback_lock);
sk->sk_user_data = NULL;
- sk->sk_data_ready = tcp_conn->old_data_ready;
- sk->sk_state_change = tcp_conn->old_state_change;
- sk->sk_write_space = tcp_conn->old_write_space;
+ sk->sk_data_ready = tcp_sw_conn->old_data_ready;
+ sk->sk_state_change = tcp_sw_conn->old_state_change;
+ sk->sk_write_space = tcp_sw_conn->old_write_space;
sk->sk_no_check = 0;
write_unlock_bh(&sk->sk_callback_lock);
}
/**
- * iscsi_xmit - TCP transmit
+ * iscsi_sw_tcp_xmit_segment - transmit segment
+ * @tcp_conn: the iSCSI TCP connection
+ * @segment: the buffer to transmnit
+ *
+ * This function transmits as much of the buffer as
+ * the network layer will accept, and returns the number of
+ * bytes transmitted.
+ *
+ * If CRC hashing is enabled, the function will compute the
+ * hash as it goes. When the entire segment has been transmitted,
+ * it will retrieve the hash value and send it as well.
+ */
+static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct socket *sk = tcp_sw_conn->sock;
+ unsigned int copied = 0;
+ int r = 0;
+
+ while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) {
+ struct scatterlist *sg;
+ unsigned int offset, copy;
+ int flags = 0;
+
+ r = 0;
+ offset = segment->copied;
+ copy = segment->size - offset;
+
+ if (segment->total_copied + segment->size < segment->total_size)
+ flags |= MSG_MORE;
+
+ /* Use sendpage if we can; else fall back to sendmsg */
+ if (!segment->data) {
+ sg = segment->sg;
+ offset += segment->sg_offset + sg->offset;
+ r = tcp_sw_conn->sendpage(sk, sg_page(sg), offset,
+ copy, flags);
+ } else {
+ struct msghdr msg = { .msg_flags = flags };
+ struct kvec iov = {
+ .iov_base = segment->data + offset,
+ .iov_len = copy
+ };
+
+ r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+ }
+
+ if (r < 0) {
+ iscsi_tcp_segment_unmap(segment);
+ if (copied || r == -EAGAIN)
+ break;
+ return r;
+ }
+ copied += r;
+ }
+ return copied;
+}
+
+/**
+ * iscsi_sw_tcp_xmit - TCP transmit
**/
-static int
-iscsi_xmit(struct iscsi_conn *conn)
+static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->out.segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
unsigned int consumed = 0;
int rc = 0;
while (1) {
- rc = iscsi_tcp_xmit_segment(tcp_conn, segment);
+ rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment);
if (rc < 0) {
rc = ISCSI_ERR_XMIT_FAILED;
goto error;
@@ -1132,22 +299,22 @@ error:
/**
* iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
*/
-static inline int
-iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
+static inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_segment *segment = &tcp_conn->out.segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
return segment->total_copied - segment->total_size;
}
-static inline int
-iscsi_tcp_flush(struct iscsi_conn *conn)
+static int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task)
{
+ struct iscsi_conn *conn = task->conn;
int rc;
- while (iscsi_tcp_xmit_qlen(conn)) {
- rc = iscsi_xmit(conn);
+ while (iscsi_sw_tcp_xmit_qlen(conn)) {
+ rc = iscsi_sw_tcp_xmit(conn);
if (rc == 0)
return -EAGAIN;
if (rc < 0)
@@ -1161,27 +328,31 @@ iscsi_tcp_flush(struct iscsi_conn *conn)
* This is called when we're done sending the header.
* Simply copy the data_segment to the send segment, and return.
*/
-static int
-iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
- struct iscsi_segment *segment)
+static int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
{
- tcp_conn->out.segment = tcp_conn->out.data_segment;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+
+ tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment;
debug_tcp("Header done. Next segment size %u total_size %u\n",
- tcp_conn->out.segment.size, tcp_conn->out.segment.total_size);
+ tcp_sw_conn->out.segment.size,
+ tcp_sw_conn->out.segment.total_size);
return 0;
}
-static void
-iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
+static void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr,
+ size_t hdrlen)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
conn->hdrdgst_en? ", digest enabled" : "");
/* Clear the data segment - needs to be filled in by the
* caller using iscsi_tcp_send_data_prep() */
- memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment));
+ memset(&tcp_sw_conn->out.data_segment, 0,
+ sizeof(struct iscsi_segment));
/* If header digest is enabled, compute the CRC and
* place the digest into the same buffer. We make
@@ -1189,7 +360,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
* sufficient room.
*/
if (conn->hdrdgst_en) {
- iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
+ iscsi_tcp_dgst_header(&tcp_sw_conn->tx_hash, hdr, hdrlen,
hdr + hdrlen);
hdrlen += ISCSI_DIGEST_SIZE;
}
@@ -1197,10 +368,10 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
/* Remember header pointer for later, when we need
* to decide whether there's a payload to go along
* with the header. */
- tcp_conn->out.hdr = hdr;
+ tcp_sw_conn->out.hdr = hdr;
- iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen,
- iscsi_tcp_send_hdr_done, NULL);
+ iscsi_segment_init_linear(&tcp_sw_conn->out.segment, hdr, hdrlen,
+ iscsi_sw_tcp_send_hdr_done, NULL);
}
/*
@@ -1209,11 +380,12 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
* of by the iscsi_segment routines.
*/
static int
-iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
- unsigned int count, unsigned int offset,
- unsigned int len)
+iscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
+ unsigned int count, unsigned int offset,
+ unsigned int len)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
@@ -1223,22 +395,23 @@ iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
/* Make sure the datalen matches what the caller
said he would send. */
- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_conn->tx_hash;
+ tx_hash = &tcp_sw_conn->tx_hash;
- return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
- sg, count, offset, len,
- NULL, tx_hash);
+ return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment,
+ sg, count, offset, len,
+ NULL, tx_hash);
}
static void
-iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+iscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
size_t len)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
@@ -1247,341 +420,160 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
/* Make sure the datalen matches what the caller
said he would send. */
- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+ hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
if (conn->datadgst_en)
- tx_hash = &tcp_conn->tx_hash;
+ tx_hash = &tcp_sw_conn->tx_hash;
- iscsi_segment_init_linear(&tcp_conn->out.data_segment,
+ iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment,
data, len, NULL, tx_hash);
}
-/**
- * iscsi_solicit_data_cont - initialize next Data-Out
- * @conn: iscsi connection
- * @task: scsi command task
- * @r2t: R2T info
- * @left: bytes left to transfer
- *
- * Notes:
- * Initialize next Data-Out within this R2T sequence and continue
- * to process next Scatter-Gather element(if any) of this SCSI command.
- *
- * Called under connection lock.
- **/
-static int
-iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
- struct iscsi_r2t_info *r2t)
+static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
+ unsigned int offset, unsigned int count)
{
- struct iscsi_data *hdr;
- int new_offset, left;
-
- BUG_ON(r2t->data_length - r2t->sent < 0);
- left = r2t->data_length - r2t->sent;
- if (left == 0)
- return 0;
-
- hdr = &r2t->dtask.hdr;
- memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = r2t->ttt;
- hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
- r2t->solicit_datasn++;
- hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = r2t->exp_statsn;
- new_offset = r2t->data_offset + r2t->sent;
- hdr->offset = cpu_to_be32(new_offset);
- if (left > conn->max_xmit_dlength) {
- hton24(hdr->dlength, conn->max_xmit_dlength);
- r2t->data_count = conn->max_xmit_dlength;
- } else {
- hton24(hdr->dlength, left);
- r2t->data_count = left;
- hdr->flags = ISCSI_FLAG_CMD_FINAL;
- }
-
- conn->dataout_pdus_cnt++;
- return 1;
-}
-
-/**
- * iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
- * @conn: iscsi connection
- * @task: scsi command task
- * @sc: scsi command
- **/
-static int
-iscsi_tcp_task_init(struct iscsi_task *task)
-{
- struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_conn *conn = task->conn;
- struct scsi_cmnd *sc = task->sc;
- int err;
+ int err = 0;
- if (!sc) {
- /*
- * mgmt tasks do not have a scatterlist since they come
- * in from the iscsi interface.
- */
- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
- task->itt);
-
- /* Prepare PDU, optionally w/ immediate data */
- iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr));
-
- /* If we have immediate data, attach a payload */
- if (task->data_count)
- iscsi_tcp_send_linear_data_prepare(conn, task->data,
- task->data_count);
- return 0;
- }
+ iscsi_sw_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
- BUG_ON(__kfifo_len(tcp_task->r2tqueue));
- tcp_task->sent = 0;
- tcp_task->exp_datasn = 0;
+ if (!count)
+ return 0;
- /* Prepare PDU, optionally w/ immediate data */
- debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
- conn->id, task->itt, task->imm_count,
- task->unsol_count);
- iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
+ if (!task->sc)
+ iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count);
+ else {
+ struct scsi_data_buffer *sdb = scsi_out(task->sc);
- if (!task->imm_count)
- return 0;
+ err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl,
+ sdb->table.nents, offset,
+ count);
+ }
- /* If we have immediate data, attach a payload */
- err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
- scsi_out(sc)->table.nents,
- 0, task->imm_count);
- if (err)
- return err;
- tcp_task->sent += task->imm_count;
- task->imm_count = 0;
+ if (err) {
+ iscsi_conn_failure(conn, err);
+ return -EIO;
+ }
return 0;
}
-/*
- * iscsi_tcp_task_xmit - xmit normal PDU task
- * @task: iscsi command task
- *
- * We're expected to return 0 when everything was transmitted succesfully,
- * -EAGAIN if there's still data in the queue, or != 0 for any other kind
- * of error.
- */
-static int
-iscsi_tcp_task_xmit(struct iscsi_task *task)
+static int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
{
- struct iscsi_conn *conn = task->conn;
struct iscsi_tcp_task *tcp_task = task->dd_data;
- struct scsi_cmnd *sc = task->sc;
- struct scsi_data_buffer *sdb;
- int rc = 0;
-
-flush:
- /* Flush any pending data first. */
- rc = iscsi_tcp_flush(conn);
- if (rc < 0)
- return rc;
-
- /* mgmt command */
- if (!sc) {
- if (task->hdr->itt == RESERVED_ITT)
- iscsi_put_task(task);
- return 0;
- }
-
- /* Are we done already? */
- if (sc->sc_data_direction != DMA_TO_DEVICE)
- return 0;
- sdb = scsi_out(sc);
- if (task->unsol_count != 0) {
- struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr;
-
- /* Prepare a header for the unsolicited PDU.
- * The amount of data we want to send will be
- * in task->data_count.
- * FIXME: return the data count instead.
- */
- iscsi_prep_unsolicit_data_pdu(task, hdr);
-
- debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
- task->itt, tcp_task->sent, task->data_count);
-
- iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
- sdb->table.nents, tcp_task->sent,
- task->data_count);
- if (rc)
- goto fail;
- tcp_task->sent += task->data_count;
- task->unsol_count -= task->data_count;
- goto flush;
- } else {
- struct iscsi_session *session = conn->session;
- struct iscsi_r2t_info *r2t;
-
- /* All unsolicited PDUs sent. Check for solicited PDUs.
- */
- spin_lock_bh(&session->lock);
- r2t = tcp_task->r2t;
- if (r2t != NULL) {
- /* Continue with this R2T? */
- if (!iscsi_solicit_data_cont(conn, task, r2t)) {
- debug_scsi(" done with r2t %p\n", r2t);
-
- __kfifo_put(tcp_task->r2tpool.queue,
- (void*)&r2t, sizeof(void*));
- tcp_task->r2t = r2t = NULL;
- }
- }
-
- if (r2t == NULL) {
- __kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t,
- sizeof(void*));
- r2t = tcp_task->r2t;
- }
- spin_unlock_bh(&session->lock);
-
- /* Waiting for more R2Ts to arrive. */
- if (r2t == NULL) {
- debug_tcp("no R2Ts yet\n");
- return 0;
- }
-
- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
- r2t, r2t->solicit_datasn - 1, task->itt,
- r2t->data_offset + r2t->sent, r2t->data_count);
-
- iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
- sizeof(struct iscsi_hdr));
-
- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
- sdb->table.nents,
- r2t->data_offset + r2t->sent,
- r2t->data_count);
- if (rc)
- goto fail;
- tcp_task->sent += r2t->data_count;
- r2t->sent += r2t->data_count;
- goto flush;
- }
+ task->hdr = task->dd_data + sizeof(*tcp_task);
+ task->hdr_max = sizeof(struct iscsi_sw_tcp_hdrbuf) - ISCSI_DIGEST_SIZE;
return 0;
-fail:
- iscsi_conn_failure(conn, rc);
- return -EIO;
}
static struct iscsi_cls_conn *
-iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session,
+ uint32_t conn_idx)
{
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
- cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
+ cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*tcp_sw_conn),
+ conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
- /*
- * due to strange issues with iser these are not set
- * in iscsi_conn_setup
- */
- conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
-
tcp_conn = conn->dd_data;
- tcp_conn->iscsi_conn = conn;
+ tcp_sw_conn = tcp_conn->dd_data;
- tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_conn->tx_hash.flags = 0;
- if (IS_ERR(tcp_conn->tx_hash.tfm))
+ tcp_sw_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_sw_conn->tx_hash.flags = 0;
+ if (IS_ERR(tcp_sw_conn->tx_hash.tfm))
goto free_conn;
- tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
- CRYPTO_ALG_ASYNC);
- tcp_conn->rx_hash.flags = 0;
- if (IS_ERR(tcp_conn->rx_hash.tfm))
+ tcp_sw_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
+ CRYPTO_ALG_ASYNC);
+ tcp_sw_conn->rx_hash.flags = 0;
+ if (IS_ERR(tcp_sw_conn->rx_hash.tfm))
goto free_tx_tfm;
+ tcp_conn->rx_hash = &tcp_sw_conn->rx_hash;
return cls_conn;
free_tx_tfm:
- crypto_free_hash(tcp_conn->tx_hash.tfm);
+ crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
free_conn:
iscsi_conn_printk(KERN_ERR, conn,
"Could not create connection due to crc32c "
"loading error. Make sure the crc32c "
"module is built as a module or into the "
"kernel\n");
- iscsi_conn_teardown(cls_conn);
+ iscsi_tcp_conn_teardown(cls_conn);
return NULL;
}
-static void
-iscsi_tcp_release_conn(struct iscsi_conn *conn)
+static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct socket *sock = tcp_conn->sock;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
+ struct socket *sock = tcp_sw_conn->sock;
if (!sock)
return;
sock_hold(sock->sk);
- iscsi_conn_restore_callbacks(tcp_conn);
+ iscsi_sw_tcp_conn_restore_callbacks(tcp_sw_conn);
sock_put(sock->sk);
spin_lock_bh(&session->lock);
- tcp_conn->sock = NULL;
+ tcp_sw_conn->sock = NULL;
spin_unlock_bh(&session->lock);
sockfd_put(sock);
}
-static void
-iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
+static void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- iscsi_tcp_release_conn(conn);
+ iscsi_sw_tcp_release_conn(conn);
- if (tcp_conn->tx_hash.tfm)
- crypto_free_hash(tcp_conn->tx_hash.tfm);
- if (tcp_conn->rx_hash.tfm)
- crypto_free_hash(tcp_conn->rx_hash.tfm);
+ if (tcp_sw_conn->tx_hash.tfm)
+ crypto_free_hash(tcp_sw_conn->tx_hash.tfm);
+ if (tcp_sw_conn->rx_hash.tfm)
+ crypto_free_hash(tcp_sw_conn->rx_hash.tfm);
- iscsi_conn_teardown(cls_conn);
+ iscsi_tcp_conn_teardown(cls_conn);
}
-static void
-iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
/* userspace may have goofed up and not bound us */
- if (!tcp_conn->sock)
+ if (!tcp_sw_conn->sock)
return;
/*
* Make sure our recv side is stopped.
* Older tools called conn stop before ep_disconnect
* so IO could still be coming in.
*/
- write_lock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+ write_lock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- write_unlock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+ write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock);
iscsi_conn_stop(cls_conn, flag);
- iscsi_tcp_release_conn(conn);
+ iscsi_sw_tcp_release_conn(conn);
}
-static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
- char *buf, int *port,
- int (*getname)(struct socket *, struct sockaddr *,
- int *addrlen))
+static int iscsi_sw_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
+ char *buf, int *port,
+ int (*getname)(struct socket *,
+ struct sockaddr *,
+ int *addrlen))
{
struct sockaddr_storage *addr;
struct sockaddr_in6 *sin6;
@@ -1601,14 +593,14 @@ static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
case AF_INET:
sin = (struct sockaddr_in *)addr;
spin_lock_bh(&conn->session->lock);
- sprintf(buf, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+ sprintf(buf, "%pI4", &sin->sin_addr.s_addr);
*port = be16_to_cpu(sin->sin_port);
spin_unlock_bh(&conn->session->lock);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)addr;
spin_lock_bh(&conn->session->lock);
- sprintf(buf, NIP6_FMT, NIP6(sin6->sin6_addr));
+ sprintf(buf, "%pI6", &sin6->sin6_addr);
*port = be16_to_cpu(sin6->sin6_port);
spin_unlock_bh(&conn->session->lock);
break;
@@ -1619,14 +611,15 @@ free_addr:
}
static int
-iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
- struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
- int is_leading)
+iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
+ int is_leading)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
struct sock *sk;
struct socket *sock;
int err;
@@ -1643,13 +636,13 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
* userspace may still want to query the values since we will
* be using them for the reconnect
*/
- err = iscsi_tcp_get_addr(conn, sock, conn->portal_address,
- &conn->portal_port, kernel_getpeername);
+ err = iscsi_sw_tcp_get_addr(conn, sock, conn->portal_address,
+ &conn->portal_port, kernel_getpeername);
if (err)
goto free_socket;
- err = iscsi_tcp_get_addr(conn, sock, ihost->local_address,
- &ihost->local_port, kernel_getsockname);
+ err = iscsi_sw_tcp_get_addr(conn, sock, ihost->local_address,
+ &ihost->local_port, kernel_getsockname);
if (err)
goto free_socket;
@@ -1658,7 +651,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
goto free_socket;
/* bind iSCSI connection and socket */
- tcp_conn->sock = sock;
+ tcp_sw_conn->sock = sock;
/* setup Socket parameters */
sk = sock->sk;
@@ -1666,8 +659,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
- iscsi_conn_set_callbacks(conn);
- tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
+ iscsi_sw_tcp_conn_set_callbacks(conn);
+ tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage;
/*
* set receive state machine into initial state
*/
@@ -1679,74 +672,14 @@ free_socket:
return err;
}
-static int
-iscsi_r2tpool_alloc(struct iscsi_session *session)
-{
- int i;
- int cmd_i;
-
- /*
- * initialize per-task: R2T pool and xmit queue
- */
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_task *task = session->cmds[cmd_i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- /*
- * pre-allocated x4 as much r2ts to handle race when
- * target acks DataOut faster than we data_xmit() queues
- * could replenish r2tqueue.
- */
-
- /* R2T pool */
- if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL,
- sizeof(struct iscsi_r2t_info))) {
- goto r2t_alloc_fail;
- }
-
- /* R2T xmit queue */
- tcp_task->r2tqueue = kfifo_alloc(
- session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
- if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
- iscsi_pool_free(&tcp_task->r2tpool);
- goto r2t_alloc_fail;
- }
- }
-
- return 0;
-
-r2t_alloc_fail:
- for (i = 0; i < cmd_i; i++) {
- struct iscsi_task *task = session->cmds[i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- kfifo_free(tcp_task->r2tqueue);
- iscsi_pool_free(&tcp_task->r2tpool);
- }
- return -ENOMEM;
-}
-
-static void
-iscsi_r2tpool_free(struct iscsi_session *session)
-{
- int i;
-
- for (i = 0; i < session->cmds_max; i++) {
- struct iscsi_task *task = session->cmds[i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- kfifo_free(tcp_task->r2tqueue);
- iscsi_pool_free(&tcp_task->r2tpool);
- }
-}
-
-static int
-iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
- char *buf, int buflen)
+static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf,
+ int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
int value;
switch(param) {
@@ -1755,8 +688,8 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
break;
case ISCSI_PARAM_DATADGST_EN:
iscsi_set_param(cls_conn, param, buf, buflen);
- tcp_conn->sendpage = conn->datadgst_en ?
- sock_no_sendpage : tcp_conn->sock->ops->sendpage;
+ tcp_sw_conn->sendpage = conn->datadgst_en ?
+ sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage;
break;
case ISCSI_PARAM_MAX_R2T:
sscanf(buf, "%d", &value);
@@ -1764,9 +697,9 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
return -EINVAL;
if (session->max_r2t == value)
break;
- iscsi_r2tpool_free(session);
+ iscsi_tcp_r2tpool_free(session);
iscsi_set_param(cls_conn, param, buf, buflen);
- if (iscsi_r2tpool_alloc(session))
+ if (iscsi_tcp_r2tpool_alloc(session))
return -ENOMEM;
break;
default:
@@ -1776,9 +709,8 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
return 0;
}
-static int
-iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
- enum iscsi_param param, char *buf)
+static int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
{
struct iscsi_conn *conn = cls_conn->dd_data;
int len;
@@ -1802,48 +734,42 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
}
static void
-iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+iscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- stats->txdata_octets = conn->txdata_octets;
- stats->rxdata_octets = conn->rxdata_octets;
- stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
- stats->dataout_pdus = conn->dataout_pdus_cnt;
- stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
- stats->datain_pdus = conn->datain_pdus_cnt;
- stats->r2t_pdus = conn->r2t_pdus_cnt;
- stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
- stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
stats->custom_length = 3;
strcpy(stats->custom[0].desc, "tx_sendpage_failures");
- stats->custom[0].value = tcp_conn->sendpage_failures_cnt;
+ stats->custom[0].value = tcp_sw_conn->sendpage_failures_cnt;
strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
- stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt;
+ stats->custom[1].value = tcp_sw_conn->discontiguous_hdr_cnt;
strcpy(stats->custom[2].desc, "eh_abort_cnt");
stats->custom[2].value = conn->eh_abort_cnt;
+
+ iscsi_tcp_conn_get_stats(cls_conn, stats);
}
static struct iscsi_cls_session *
-iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
- uint16_t qdepth, uint32_t initial_cmdsn,
- uint32_t *hostno)
+iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
+ uint16_t qdepth, uint32_t initial_cmdsn,
+ uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
struct Scsi_Host *shost;
- int cmd_i;
if (ep) {
printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
return NULL;
}
- shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth);
+ shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 0, qdepth);
if (!shost)
return NULL;
- shost->transportt = iscsi_tcp_scsi_transport;
+ shost->transportt = iscsi_sw_tcp_scsi_transport;
shost->max_lun = iscsi_max_lun;
shost->max_id = 0;
shost->max_channel = 0;
@@ -1853,23 +779,17 @@ iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
goto free_host;
*hostno = shost->host_no;
- cls_session = iscsi_session_setup(&iscsi_tcp_transport, shost, cmds_max,
- sizeof(struct iscsi_tcp_task),
+ cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost,
+ cmds_max,
+ sizeof(struct iscsi_tcp_task) +
+ sizeof(struct iscsi_sw_tcp_hdrbuf),
initial_cmdsn, 0);
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
shost->can_queue = session->scsi_cmds_max;
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_task *task = session->cmds[cmd_i];
- struct iscsi_tcp_task *tcp_task = task->dd_data;
-
- task->hdr = &tcp_task->hdr.cmd_hdr;
- task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE;
- }
-
- if (iscsi_r2tpool_alloc(session))
+ if (iscsi_tcp_r2tpool_alloc(session))
goto remove_session;
return cls_session;
@@ -1882,25 +802,25 @@ free_host:
return NULL;
}
-static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- iscsi_r2tpool_free(cls_session->dd_data);
+ iscsi_tcp_r2tpool_free(cls_session->dd_data);
iscsi_session_teardown(cls_session);
iscsi_host_remove(shost);
iscsi_host_free(shost);
}
-static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
+static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
{
blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
blk_queue_dma_alignment(sdev->request_queue, 0);
return 0;
}
-static struct scsi_host_template iscsi_sht = {
+static struct scsi_host_template iscsi_sw_tcp_sht = {
.module = THIS_MODULE,
.name = "iSCSI Initiator over TCP/IP",
.queuecommand = iscsi_queuecommand,
@@ -1913,12 +833,12 @@ static struct scsi_host_template iscsi_sht = {
.eh_device_reset_handler= iscsi_eh_device_reset,
.eh_target_reset_handler= iscsi_eh_target_reset,
.use_clustering = DISABLE_CLUSTERING,
- .slave_configure = iscsi_tcp_slave_configure,
+ .slave_configure = iscsi_sw_tcp_slave_configure,
.proc_name = "iscsi_tcp",
.this_id = -1,
};
-static struct iscsi_transport iscsi_tcp_transport = {
+static struct iscsi_transport iscsi_sw_tcp_transport = {
.owner = THIS_MODULE,
.name = "tcp",
.caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
@@ -1951,32 +871,36 @@ static struct iscsi_transport iscsi_tcp_transport = {
ISCSI_HOST_INITIATOR_NAME |
ISCSI_HOST_NETDEV_NAME,
/* session management */
- .create_session = iscsi_tcp_session_create,
- .destroy_session = iscsi_tcp_session_destroy,
+ .create_session = iscsi_sw_tcp_session_create,
+ .destroy_session = iscsi_sw_tcp_session_destroy,
/* connection management */
- .create_conn = iscsi_tcp_conn_create,
- .bind_conn = iscsi_tcp_conn_bind,
- .destroy_conn = iscsi_tcp_conn_destroy,
- .set_param = iscsi_conn_set_param,
- .get_conn_param = iscsi_tcp_conn_get_param,
+ .create_conn = iscsi_sw_tcp_conn_create,
+ .bind_conn = iscsi_sw_tcp_conn_bind,
+ .destroy_conn = iscsi_sw_tcp_conn_destroy,
+ .set_param = iscsi_sw_tcp_conn_set_param,
+ .get_conn_param = iscsi_sw_tcp_conn_get_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_conn_start,
- .stop_conn = iscsi_tcp_conn_stop,
+ .stop_conn = iscsi_sw_tcp_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
- .get_stats = iscsi_conn_get_stats,
+ .get_stats = iscsi_sw_tcp_conn_get_stats,
+ /* iscsi task/cmd helpers */
.init_task = iscsi_tcp_task_init,
.xmit_task = iscsi_tcp_task_xmit,
.cleanup_task = iscsi_tcp_cleanup_task,
+ /* low level pdu helpers */
+ .xmit_pdu = iscsi_sw_tcp_pdu_xmit,
+ .init_pdu = iscsi_sw_tcp_pdu_init,
+ .alloc_pdu = iscsi_sw_tcp_pdu_alloc,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
-static int __init
-iscsi_tcp_init(void)
+static int __init iscsi_sw_tcp_init(void)
{
if (iscsi_max_lun < 1) {
printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n",
@@ -1984,19 +908,18 @@ iscsi_tcp_init(void)
return -EINVAL;
}
- iscsi_tcp_scsi_transport = iscsi_register_transport(
- &iscsi_tcp_transport);
- if (!iscsi_tcp_scsi_transport)
+ iscsi_sw_tcp_scsi_transport = iscsi_register_transport(
+ &iscsi_sw_tcp_transport);
+ if (!iscsi_sw_tcp_scsi_transport)
return -ENODEV;
return 0;
}
-static void __exit
-iscsi_tcp_exit(void)
+static void __exit iscsi_sw_tcp_exit(void)
{
- iscsi_unregister_transport(&iscsi_tcp_transport);
+ iscsi_unregister_transport(&iscsi_sw_tcp_transport);
}
-module_init(iscsi_tcp_init);
-module_exit(iscsi_tcp_exit);
+module_init(iscsi_sw_tcp_init);
+module_exit(iscsi_sw_tcp_exit);
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index 498d8ca39848..ca6b7bc64de0 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -19,67 +19,27 @@
* See the file COPYING included with this distribution for more details.
*/
-#ifndef ISCSI_TCP_H
-#define ISCSI_TCP_H
+#ifndef ISCSI_SW_TCP_H
+#define ISCSI_SW_TCP_H
#include <scsi/libiscsi.h>
+#include <scsi/libiscsi_tcp.h>
-struct crypto_hash;
struct socket;
struct iscsi_tcp_conn;
-struct iscsi_segment;
-
-typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
- struct iscsi_segment *);
-
-struct iscsi_segment {
- unsigned char *data;
- unsigned int size;
- unsigned int copied;
- unsigned int total_size;
- unsigned int total_copied;
-
- struct hash_desc *hash;
- unsigned char recv_digest[ISCSI_DIGEST_SIZE];
- unsigned char digest[ISCSI_DIGEST_SIZE];
- unsigned int digest_len;
-
- struct scatterlist *sg;
- void *sg_mapped;
- unsigned int sg_offset;
-
- iscsi_segment_done_fn_t *done;
-};
-
-/* Socket connection recieve helper */
-struct iscsi_tcp_recv {
- struct iscsi_hdr *hdr;
- struct iscsi_segment segment;
-
- /* Allocate buffer for BHS + AHS */
- uint32_t hdr_buf[64];
-
- /* copied and flipped values */
- int datalen;
-};
/* Socket connection send helper */
-struct iscsi_tcp_send {
+struct iscsi_sw_tcp_send {
struct iscsi_hdr *hdr;
struct iscsi_segment segment;
struct iscsi_segment data_segment;
};
-struct iscsi_tcp_conn {
+struct iscsi_sw_tcp_conn {
struct iscsi_conn *iscsi_conn;
struct socket *sock;
- int stop_stage; /* conn_stop() flag: *
- * stop to recover, *
- * stop to terminate */
- /* control data */
- struct iscsi_tcp_recv in; /* TCP receive context */
- struct iscsi_tcp_send out; /* TCP send context */
+ struct iscsi_sw_tcp_send out;
/* old values for socket callbacks */
void (*old_data_ready)(struct sock *, int);
void (*old_state_change)(struct sock *);
@@ -93,41 +53,13 @@ struct iscsi_tcp_conn {
uint32_t sendpage_failures_cnt;
uint32_t discontiguous_hdr_cnt;
- int error;
-
ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
};
-struct iscsi_data_task {
- struct iscsi_data hdr; /* PDU */
- char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
-};
-
-struct iscsi_r2t_info {
- __be32 ttt; /* copied from R2T */
- __be32 exp_statsn; /* copied from R2T */
- uint32_t data_length; /* copied from R2T */
- uint32_t data_offset; /* copied from R2T */
- int sent; /* R2T sequence progress */
- int data_count; /* DATA-Out payload progress */
- int solicit_datasn;
- struct iscsi_data_task dtask; /* Data-Out header buf */
-};
-
-struct iscsi_tcp_task {
- struct iscsi_hdr_buff {
- struct iscsi_cmd cmd_hdr;
- char hdrextbuf[ISCSI_MAX_AHS_SIZE +
+struct iscsi_sw_tcp_hdrbuf {
+ struct iscsi_hdr hdrbuf;
+ char hdrextbuf[ISCSI_MAX_AHS_SIZE +
ISCSI_DIGEST_SIZE];
- } hdr;
-
- int sent;
- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
- int data_offset;
- struct iscsi_r2t_info *r2t; /* in progress R2T */
- struct iscsi_pool r2tpool;
- struct kfifo *r2tqueue;
- struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
};
-#endif /* ISCSI_H */
+#endif /* ISCSI_SW_TCP_H */
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
new file mode 100644
index 000000000000..55f982de3a9a
--- /dev/null
+++ b/drivers/scsi/libfc/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile
+
+obj-$(CONFIG_LIBFC) += libfc.o
+
+libfc-objs := \
+ fc_disc.o \
+ fc_exch.o \
+ fc_elsct.o \
+ fc_frame.o \
+ fc_lport.o \
+ fc_rport.o \
+ fc_fcp.o
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
new file mode 100644
index 000000000000..dd1564c9e04a
--- /dev/null
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -0,0 +1,845 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Target Discovery
+ *
+ * This block discovers all FC-4 remote ports, including FCP initiators. It
+ * also handles RSCN events and re-discovery if necessary.
+ */
+
+/*
+ * DISC LOCKING
+ *
+ * The disc mutex is can be locked when acquiring rport locks, but may not
+ * be held when acquiring the lport lock. Refer to fc_lport.c for more
+ * details.
+ */
+
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <asm/unaligned.h>
+
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/libfc.h>
+
+#define FC_DISC_RETRY_LIMIT 3 /* max retries */
+#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
+
+#define FC_DISC_DELAY 3
+
+static int fc_disc_debug;
+
+#define FC_DEBUG_DISC(fmt...) \
+ do { \
+ if (fc_disc_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static void fc_disc_gpn_ft_req(struct fc_disc *);
+static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
+static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
+ struct fc_rport_identifiers *);
+static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
+static void fc_disc_done(struct fc_disc *);
+static void fc_disc_timeout(struct work_struct *);
+static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
+static void fc_disc_restart(struct fc_disc *);
+
+/**
+ * fc_disc_lookup_rport - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
+ u32 port_id)
+{
+ const struct fc_disc *disc = &lport->disc;
+ struct fc_rport *rport, *found = NULL;
+ struct fc_rport_libfc_priv *rdata;
+ int disc_found = 0;
+
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ if (rport->port_id == port_id) {
+ disc_found = 1;
+ found = rport;
+ break;
+ }
+ }
+
+ if (!disc_found)
+ found = NULL;
+
+ return found;
+}
+
+/**
+ * fc_disc_stop_rports - delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop rports on
+ *
+ * Locking Note: This function expects that the lport mutex is locked before
+ * calling it.
+ */
+void fc_disc_stop_rports(struct fc_disc *disc)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata, *next;
+
+ lport = disc->lport;
+
+ mutex_lock(&disc->disc_mutex);
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+
+ mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fc_disc_rport_callback - Event handler for rport events
+ * @lport: The lport which is receiving the event
+ * @rport: The rport which the event has occured on
+ * @event: The event that occured
+ *
+ * Locking Note: The rport lock should not be held when calling
+ * this function.
+ */
+static void fc_disc_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_disc *disc = &lport->disc;
+ int found = 0;
+
+ FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
+ rport->port_id);
+
+ if (event == RPORT_EV_CREATED) {
+ if (disc) {
+ found = 1;
+ mutex_lock(&disc->disc_mutex);
+ list_add_tail(&rdata->peers, &disc->rports);
+ mutex_unlock(&disc->disc_mutex);
+ }
+ }
+
+ if (!found)
+ FC_DEBUG_DISC("The rport (%6x) is not maintained "
+ "by the discovery layer\n", rport->port_id);
+}
+
+/**
+ * fc_disc_recv_rscn_req - Handle Registered State Change Notification (RSCN)
+ * @sp: Current sequence of the RSCN exchange
+ * @fp: RSCN Frame
+ * @lport: Fibre Channel host port instance
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_disc *disc)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ struct fc_els_rscn *rp;
+ struct fc_els_rscn_page *pp;
+ struct fc_seq_els_data rjt_data;
+ unsigned int len;
+ int redisc = 0;
+ enum fc_els_rscn_ev_qual ev_qual;
+ enum fc_els_rscn_addr_fmt fmt;
+ LIST_HEAD(disc_ports);
+ struct fc_disc_port *dp, *next;
+
+ lport = disc->lport;
+
+ FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ /* make sure the frame contains an RSCN message */
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ if (!rp)
+ goto reject;
+ /* make sure the page length is as expected (4 bytes) */
+ if (rp->rscn_page_len != sizeof(*pp))
+ goto reject;
+ /* get the RSCN payload length */
+ len = ntohs(rp->rscn_plen);
+ if (len < sizeof(*rp))
+ goto reject;
+ /* make sure the frame contains the expected payload */
+ rp = fc_frame_payload_get(fp, len);
+ if (!rp)
+ goto reject;
+ /* payload must be a multiple of the RSCN page size */
+ len -= sizeof(*rp);
+ if (len % sizeof(*pp))
+ goto reject;
+
+ for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
+ ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
+ ev_qual &= ELS_RSCN_EV_QUAL_MASK;
+ fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
+ fmt &= ELS_RSCN_ADDR_FMT_MASK;
+ /*
+ * if we get an address format other than port
+ * (area, domain, fabric), then do a full discovery
+ */
+ switch (fmt) {
+ case ELS_ADDR_FMT_PORT:
+ FC_DEBUG_DISC("Port address format for port (%6x)\n",
+ ntoh24(pp->rscn_fid));
+ dp = kzalloc(sizeof(*dp), GFP_KERNEL);
+ if (!dp) {
+ redisc = 1;
+ break;
+ }
+ dp->lp = lport;
+ dp->ids.port_id = ntoh24(pp->rscn_fid);
+ dp->ids.port_name = -1;
+ dp->ids.node_name = -1;
+ dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ list_add_tail(&dp->peers, &disc_ports);
+ break;
+ case ELS_ADDR_FMT_AREA:
+ case ELS_ADDR_FMT_DOM:
+ case ELS_ADDR_FMT_FAB:
+ default:
+ FC_DEBUG_DISC("Address format is (%d)\n", fmt);
+ redisc = 1;
+ break;
+ }
+ }
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ if (redisc) {
+ FC_DEBUG_DISC("RSCN received: rediscovering\n");
+ fc_disc_restart(disc);
+ } else {
+ FC_DEBUG_DISC("RSCN received: not rediscovering. "
+ "redisc %d state %d in_prog %d\n",
+ redisc, lport->state, disc->pending);
+ list_for_each_entry_safe(dp, next, &disc_ports, peers) {
+ list_del(&dp->peers);
+ rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+ if (rport) {
+ rdata = RPORT_TO_PRIV(rport);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+ fc_disc_single(disc, dp);
+ }
+ }
+ fc_frame_free(fp);
+ return;
+reject:
+ FC_DEBUG_DISC("Received a bad RSCN frame\n");
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_disc_recv_req - Handle incoming requests
+ * @sp: Current sequence of the request exchange
+ * @fp: The frame
+ * @lport: The FC local port
+ *
+ * Locking Note: This function is called from the EM and will lock
+ * the disc_mutex before calling the handler for the
+ * request.
+ */
+static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ u8 op;
+ struct fc_disc *disc = &lport->disc;
+
+ op = fc_frame_payload_op(fp);
+ switch (op) {
+ case ELS_RSCN:
+ mutex_lock(&disc->disc_mutex);
+ fc_disc_recv_rscn_req(sp, fp, disc);
+ mutex_unlock(&disc->disc_mutex);
+ break;
+ default:
+ FC_DBG("Received an unsupported request. opcode (%x)\n", op);
+ break;
+ }
+}
+
+/**
+ * fc_disc_restart - Restart discovery
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc mutex
+ * is already locked.
+ */
+static void fc_disc_restart(struct fc_disc *disc)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata, *next;
+ struct fc_lport *lport = disc->lport;
+
+ FC_DEBUG_DISC("Restarting discovery for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+ }
+
+ disc->requested = 1;
+ if (!disc->pending)
+ fc_disc_gpn_ft_req(disc);
+}
+
+/**
+ * fc_disc_start - Fibre Channel Target discovery
+ * @lport: FC local port
+ *
+ * Returns non-zero if discovery cannot be started.
+ */
+static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
+ enum fc_disc_event),
+ struct fc_lport *lport)
+{
+ struct fc_rport *rport;
+ struct fc_rport_identifiers ids;
+ struct fc_disc *disc = &lport->disc;
+
+ /*
+ * At this point we may have a new disc job or an existing
+ * one. Either way, let's lock when we make changes to it
+ * and send the GPN_FT request.
+ */
+ mutex_lock(&disc->disc_mutex);
+
+ disc->disc_callback = disc_callback;
+
+ /*
+ * If not ready, or already running discovery, just set request flag.
+ */
+ disc->requested = 1;
+
+ if (disc->pending) {
+ mutex_unlock(&disc->disc_mutex);
+ return;
+ }
+
+ /*
+ * Handle point-to-point mode as a simple discovery
+ * of the remote port. Yucky, yucky, yuck, yuck!
+ */
+ rport = disc->lport->ptp_rp;
+ if (rport) {
+ ids.port_id = rport->port_id;
+ ids.port_name = rport->port_name;
+ ids.node_name = rport->node_name;
+ ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ get_device(&rport->dev);
+
+ if (!fc_disc_new_target(disc, rport, &ids)) {
+ disc->event = DISC_EV_SUCCESS;
+ fc_disc_done(disc);
+ }
+ put_device(&rport->dev);
+ } else {
+ fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
+ }
+
+ mutex_unlock(&disc->disc_mutex);
+}
+
+static struct fc_rport_operations fc_disc_rport_ops = {
+ .event_callback = fc_disc_rport_callback,
+};
+
+/**
+ * fc_disc_new_target - Handle new target found by discovery
+ * @lport: FC local port
+ * @rport: The previous FC remote port (NULL if new remote port)
+ * @ids: Identifiers for the new FC remote port
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static int fc_disc_new_target(struct fc_disc *disc,
+ struct fc_rport *rport,
+ struct fc_rport_identifiers *ids)
+{
+ struct fc_lport *lport = disc->lport;
+ struct fc_rport_libfc_priv *rp;
+ int error = 0;
+
+ if (rport && ids->port_name) {
+ if (rport->port_name == -1) {
+ /*
+ * Set WWN and fall through to notify of create.
+ */
+ fc_rport_set_name(rport, ids->port_name,
+ rport->node_name);
+ } else if (rport->port_name != ids->port_name) {
+ /*
+ * This is a new port with the same FCID as
+ * a previously-discovered port. Presumably the old
+ * port logged out and a new port logged in and was
+ * assigned the same FCID. This should be rare.
+ * Delete the old one and fall thru to re-create.
+ */
+ fc_disc_del_target(disc, rport);
+ rport = NULL;
+ }
+ }
+ if (((ids->port_name != -1) || (ids->port_id != -1)) &&
+ ids->port_id != fc_host_port_id(lport->host) &&
+ ids->port_name != lport->wwpn) {
+ if (!rport) {
+ rport = lport->tt.rport_lookup(lport, ids->port_id);
+ if (!rport) {
+ struct fc_disc_port dp;
+ dp.lp = lport;
+ dp.ids.port_id = ids->port_id;
+ dp.ids.port_name = ids->port_name;
+ dp.ids.node_name = ids->node_name;
+ dp.ids.roles = ids->roles;
+ rport = fc_rport_rogue_create(&dp);
+ }
+ if (!rport)
+ error = -ENOMEM;
+ }
+ if (rport) {
+ rp = rport->dd_data;
+ rp->ops = &fc_disc_rport_ops;
+ rp->rp_state = RPORT_ST_INIT;
+ lport->tt.rport_login(rport);
+ }
+ }
+ return error;
+}
+
+/**
+ * fc_disc_del_target - Delete a target
+ * @disc: FC discovery context
+ * @rport: The remote port to be removed
+ */
+static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
+{
+ struct fc_lport *lport = disc->lport;
+ struct fc_rport_libfc_priv *rdata = RPORT_TO_PRIV(rport);
+ list_del(&rdata->peers);
+ lport->tt.rport_logoff(rport);
+}
+
+/**
+ * fc_disc_done - Discovery has been completed
+ * @disc: FC discovery context
+ */
+static void fc_disc_done(struct fc_disc *disc)
+{
+ struct fc_lport *lport = disc->lport;
+
+ FC_DEBUG_DISC("Discovery complete for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ disc->disc_callback(lport, disc->event);
+ disc->event = DISC_EV_NONE;
+
+ if (disc->requested)
+ fc_disc_gpn_ft_req(disc);
+ else
+ disc->pending = 0;
+}
+
+/**
+ * fc_disc_error - Handle error on dNS request
+ * @disc: FC discovery context
+ * @fp: The frame pointer
+ */
+static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
+{
+ struct fc_lport *lport = disc->lport;
+ unsigned long delay = 0;
+ if (fc_disc_debug)
+ FC_DBG("Error %ld, retries %d/%d\n",
+ PTR_ERR(fp), disc->retry_count,
+ FC_DISC_RETRY_LIMIT);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out,
+ * retry after delay.
+ */
+ if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
+ /* go ahead and retry */
+ if (!fp)
+ delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
+ else {
+ delay = msecs_to_jiffies(lport->e_d_tov);
+
+ /* timeout faster first time */
+ if (!disc->retry_count)
+ delay /= 4;
+ }
+ disc->retry_count++;
+ schedule_delayed_work(&disc->disc_work, delay);
+ } else {
+ /* exceeded retries */
+ disc->event = DISC_EV_FAILED;
+ fc_disc_done(disc);
+ }
+ }
+}
+
+/**
+ * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_gpn_ft_req(struct fc_disc *disc)
+{
+ struct fc_frame *fp;
+ struct fc_lport *lport = disc->lport;
+
+ WARN_ON(!fc_lport_test_ready(lport));
+
+ disc->pending = 1;
+ disc->requested = 0;
+
+ disc->buf_len = 0;
+ disc->seq_count = 0;
+ fp = fc_frame_alloc(lport,
+ sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_gid_ft));
+ if (!fp)
+ goto err;
+
+ if (lport->tt.elsct_send(lport, NULL, fp,
+ FC_NS_GPN_FT,
+ fc_disc_gpn_ft_resp,
+ disc, lport->e_d_tov))
+ return;
+err:
+ fc_disc_error(disc, fp);
+}
+
+/**
+ * fc_disc_gpn_ft_parse - Parse the list of IDs and names resulting from a request
+ * @lport: Fibre Channel host port instance
+ * @buf: GPN_FT response buffer
+ * @len: size of response buffer
+ */
+static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
+{
+ struct fc_lport *lport;
+ struct fc_gpn_ft_resp *np;
+ char *bp;
+ size_t plen;
+ size_t tlen;
+ int error = 0;
+ struct fc_disc_port dp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+
+ lport = disc->lport;
+
+ /*
+ * Handle partial name record left over from previous call.
+ */
+ bp = buf;
+ plen = len;
+ np = (struct fc_gpn_ft_resp *)bp;
+ tlen = disc->buf_len;
+ if (tlen) {
+ WARN_ON(tlen >= sizeof(*np));
+ plen = sizeof(*np) - tlen;
+ WARN_ON(plen <= 0);
+ WARN_ON(plen >= sizeof(*np));
+ if (plen > len)
+ plen = len;
+ np = &disc->partial_buf;
+ memcpy((char *)np + tlen, bp, plen);
+
+ /*
+ * Set bp so that the loop below will advance it to the
+ * first valid full name element.
+ */
+ bp -= tlen;
+ len += tlen;
+ plen += tlen;
+ disc->buf_len = (unsigned char) plen;
+ if (plen == sizeof(*np))
+ disc->buf_len = 0;
+ }
+
+ /*
+ * Handle full name records, including the one filled from above.
+ * Normally, np == bp and plen == len, but from the partial case above,
+ * bp, len describe the overall buffer, and np, plen describe the
+ * partial buffer, which if would usually be full now.
+ * After the first time through the loop, things return to "normal".
+ */
+ while (plen >= sizeof(*np)) {
+ dp.lp = lport;
+ dp.ids.port_id = ntoh24(np->fp_fid);
+ dp.ids.port_name = ntohll(np->fp_wwpn);
+ dp.ids.node_name = -1;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if ((dp.ids.port_id != fc_host_port_id(lport->host)) &&
+ (dp.ids.port_name != lport->wwpn)) {
+ rport = fc_rport_rogue_create(&dp);
+ if (rport) {
+ rdata = rport->dd_data;
+ rdata->ops = &fc_disc_rport_ops;
+ rdata->local_port = lport;
+ lport->tt.rport_login(rport);
+ } else
+ FC_DBG("Failed to allocate memory for "
+ "the newly discovered port (%6x)\n",
+ dp.ids.port_id);
+ }
+
+ if (np->fp_flags & FC_NS_FID_LAST) {
+ disc->event = DISC_EV_SUCCESS;
+ fc_disc_done(disc);
+ len = 0;
+ break;
+ }
+ len -= sizeof(*np);
+ bp += sizeof(*np);
+ np = (struct fc_gpn_ft_resp *)bp;
+ plen = len;
+ }
+
+ /*
+ * Save any partial record at the end of the buffer for next time.
+ */
+ if (error == 0 && len > 0 && len < sizeof(*np)) {
+ if (np != &disc->partial_buf) {
+ FC_DEBUG_DISC("Partial buffer remains "
+ "for discovery by (%6x)\n",
+ fc_host_port_id(lport->host));
+ memcpy(&disc->partial_buf, np, len);
+ }
+ disc->buf_len = (unsigned char) len;
+ } else {
+ disc->buf_len = 0;
+ }
+ return error;
+}
+
+/*
+ * Handle retry of memory allocation for remote ports.
+ */
+static void fc_disc_timeout(struct work_struct *work)
+{
+ struct fc_disc *disc = container_of(work,
+ struct fc_disc,
+ disc_work.work);
+ mutex_lock(&disc->disc_mutex);
+ if (disc->requested && !disc->pending)
+ fc_disc_gpn_ft_req(disc);
+ mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fc_disc_gpn_ft_resp - Handle a response frame from Get Port Names (GPN_FT)
+ * @sp: Current sequence of GPN_FT exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *disc_arg)
+{
+ struct fc_disc *disc = disc_arg;
+ struct fc_ct_hdr *cp;
+ struct fc_frame_header *fh;
+ unsigned int seq_cnt;
+ void *buf = NULL;
+ unsigned int len;
+ int error;
+
+ FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n",
+ fc_host_port_id(disc->lport->host));
+
+ if (IS_ERR(fp)) {
+ fc_disc_error(disc, fp);
+ return;
+ }
+
+ WARN_ON(!fc_frame_is_linear(fp)); /* buffer must be contiguous */
+ fh = fc_frame_header_get(fp);
+ len = fr_len(fp) - sizeof(*fh);
+ seq_cnt = ntohs(fh->fh_seq_cnt);
+ if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
+ disc->seq_count == 0) {
+ cp = fc_frame_payload_get(fp, sizeof(*cp));
+ if (!cp) {
+ FC_DBG("GPN_FT response too short, len %d\n",
+ fr_len(fp));
+ } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
+
+ /*
+ * Accepted. Parse response.
+ */
+ buf = cp + 1;
+ len -= sizeof(*cp);
+ } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
+ FC_DBG("GPN_FT rejected reason %x exp %x "
+ "(check zoning)\n", cp->ct_reason,
+ cp->ct_explan);
+ disc->event = DISC_EV_FAILED;
+ fc_disc_done(disc);
+ } else {
+ FC_DBG("GPN_FT unexpected response code %x\n",
+ ntohs(cp->ct_cmd));
+ }
+ } else if (fr_sof(fp) == FC_SOF_N3 &&
+ seq_cnt == disc->seq_count) {
+ buf = fh + 1;
+ } else {
+ FC_DBG("GPN_FT unexpected frame - out of sequence? "
+ "seq_cnt %x expected %x sof %x eof %x\n",
+ seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
+ }
+ if (buf) {
+ error = fc_disc_gpn_ft_parse(disc, buf, len);
+ if (error)
+ fc_disc_error(disc, fp);
+ else
+ disc->seq_count++;
+ }
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_disc_single - Discover the directory information for a single target
+ * @lport: FC local port
+ * @dp: The port to rediscover
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
+{
+ struct fc_lport *lport;
+ struct fc_rport *rport;
+ struct fc_rport *new_rport;
+ struct fc_rport_libfc_priv *rdata;
+
+ lport = disc->lport;
+
+ if (dp->ids.port_id == fc_host_port_id(lport->host))
+ goto out;
+
+ rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+ if (rport)
+ fc_disc_del_target(disc, rport);
+
+ new_rport = fc_rport_rogue_create(dp);
+ if (new_rport) {
+ rdata = new_rport->dd_data;
+ rdata->ops = &fc_disc_rport_ops;
+ kfree(dp);
+ lport->tt.rport_login(new_rport);
+ }
+ return;
+out:
+ kfree(dp);
+}
+
+/**
+ * fc_disc_stop - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ */
+void fc_disc_stop(struct fc_lport *lport)
+{
+ struct fc_disc *disc = &lport->disc;
+
+ if (disc) {
+ cancel_delayed_work_sync(&disc->disc_work);
+ fc_disc_stop_rports(disc);
+ }
+}
+
+/**
+ * fc_disc_stop_final - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ *
+ * This function will block until discovery has been
+ * completely stopped and all rports have been deleted.
+ */
+void fc_disc_stop_final(struct fc_lport *lport)
+{
+ fc_disc_stop(lport);
+ lport->tt.rport_flush_queue();
+}
+
+/**
+ * fc_disc_init - Initialize the discovery block
+ * @lport: FC local port
+ */
+int fc_disc_init(struct fc_lport *lport)
+{
+ struct fc_disc *disc;
+
+ if (!lport->tt.disc_start)
+ lport->tt.disc_start = fc_disc_start;
+
+ if (!lport->tt.disc_stop)
+ lport->tt.disc_stop = fc_disc_stop;
+
+ if (!lport->tt.disc_stop_final)
+ lport->tt.disc_stop_final = fc_disc_stop_final;
+
+ if (!lport->tt.disc_recv_req)
+ lport->tt.disc_recv_req = fc_disc_recv_req;
+
+ if (!lport->tt.rport_lookup)
+ lport->tt.rport_lookup = fc_disc_lookup_rport;
+
+ disc = &lport->disc;
+ INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
+ mutex_init(&disc->disc_mutex);
+ INIT_LIST_HEAD(&disc->rports);
+
+ disc->lport = lport;
+ disc->delay = FC_DISC_DELAY;
+ disc->event = DISC_EV_NONE;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_disc_init);
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
new file mode 100644
index 000000000000..dd47fe619d1e
--- /dev/null
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Provide interface to send ELS/CT FC frames
+ */
+
+#include <asm/unaligned.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+/*
+ * fc_elsct_send - sends ELS/CT frame
+ */
+static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
+ struct fc_rport *rport,
+ struct fc_frame *fp,
+ unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void *arg, u32 timer_msec)
+{
+ enum fc_rctl r_ctl;
+ u32 did;
+ enum fc_fh_type fh_type;
+ int rc;
+
+ /* ELS requests */
+ if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
+ rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type);
+ else
+ /* CT requests */
+ rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type);
+
+ if (rc)
+ return NULL;
+
+ fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
+}
+
+int fc_elsct_init(struct fc_lport *lport)
+{
+ if (!lport->tt.elsct_send)
+ lport->tt.elsct_send = fc_elsct_send;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_elsct_init);
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
new file mode 100644
index 000000000000..66db08a5f27f
--- /dev/null
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -0,0 +1,1970 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright(c) 2008 Mike Christie
+ *
+ * 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.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Fibre Channel exchange and sequence handling.
+ */
+
+#include <linux/timer.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+
+#include <scsi/fc/fc_fc2.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */
+
+/*
+ * fc_exch_debug can be set in debugger or at compile time to get more logs.
+ */
+static int fc_exch_debug;
+
+#define FC_DEBUG_EXCH(fmt...) \
+ do { \
+ if (fc_exch_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+
+/*
+ * Structure and function definitions for managing Fibre Channel Exchanges
+ * and Sequences.
+ *
+ * The three primary structures used here are fc_exch_mgr, fc_exch, and fc_seq.
+ *
+ * fc_exch_mgr holds the exchange state for an N port
+ *
+ * fc_exch holds state for one exchange and links to its active sequence.
+ *
+ * fc_seq holds the state for an individual sequence.
+ */
+
+/*
+ * Exchange manager.
+ *
+ * This structure is the center for creating exchanges and sequences.
+ * It manages the allocation of exchange IDs.
+ */
+struct fc_exch_mgr {
+ enum fc_class class; /* default class for sequences */
+ spinlock_t em_lock; /* exchange manager lock,
+ must be taken before ex_lock */
+ u16 last_xid; /* last allocated exchange ID */
+ u16 min_xid; /* min exchange ID */
+ u16 max_xid; /* max exchange ID */
+ u16 max_read; /* max exchange ID for read */
+ u16 last_read; /* last xid allocated for read */
+ u32 total_exches; /* total allocated exchanges */
+ struct list_head ex_list; /* allocated exchanges list */
+ struct fc_lport *lp; /* fc device instance */
+ mempool_t *ep_pool; /* reserve ep's */
+
+ /*
+ * currently exchange mgr stats are updated but not used.
+ * either stats can be expose via sysfs or remove them
+ * all together if not used XXX
+ */
+ struct {
+ atomic_t no_free_exch;
+ atomic_t no_free_exch_xid;
+ atomic_t xid_not_found;
+ atomic_t xid_busy;
+ atomic_t seq_not_found;
+ atomic_t non_bls_resp;
+ } stats;
+ struct fc_exch **exches; /* for exch pointers indexed by xid */
+};
+#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+
+static void fc_exch_rrq(struct fc_exch *);
+static void fc_seq_ls_acc(struct fc_seq *);
+static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
+ enum fc_els_rjt_explan);
+static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
+static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
+
+/*
+ * Internal implementation notes.
+ *
+ * The exchange manager is one by default in libfc but LLD may choose
+ * to have one per CPU. The sequence manager is one per exchange manager
+ * and currently never separated.
+ *
+ * Section 9.8 in FC-FS-2 specifies: "The SEQ_ID is a one-byte field
+ * assigned by the Sequence Initiator that shall be unique for a specific
+ * D_ID and S_ID pair while the Sequence is open." Note that it isn't
+ * qualified by exchange ID, which one might think it would be.
+ * In practice this limits the number of open sequences and exchanges to 256
+ * per session. For most targets we could treat this limit as per exchange.
+ *
+ * The exchange and its sequence are freed when the last sequence is received.
+ * It's possible for the remote port to leave an exchange open without
+ * sending any sequences.
+ *
+ * Notes on reference counts:
+ *
+ * Exchanges are reference counted and exchange gets freed when the reference
+ * count becomes zero.
+ *
+ * Timeouts:
+ * Sequences are timed out for E_D_TOV and R_A_TOV.
+ *
+ * Sequence event handling:
+ *
+ * The following events may occur on initiator sequences:
+ *
+ * Send.
+ * For now, the whole thing is sent.
+ * Receive ACK
+ * This applies only to class F.
+ * The sequence is marked complete.
+ * ULP completion.
+ * The upper layer calls fc_exch_done() when done
+ * with exchange and sequence tuple.
+ * RX-inferred completion.
+ * When we receive the next sequence on the same exchange, we can
+ * retire the previous sequence ID. (XXX not implemented).
+ * Timeout.
+ * R_A_TOV frees the sequence ID. If we're waiting for ACK,
+ * E_D_TOV causes abort and calls upper layer response handler
+ * with FC_EX_TIMEOUT error.
+ * Receive RJT
+ * XXX defer.
+ * Send ABTS
+ * On timeout.
+ *
+ * The following events may occur on recipient sequences:
+ *
+ * Receive
+ * Allocate sequence for first frame received.
+ * Hold during receive handler.
+ * Release when final frame received.
+ * Keep status of last N of these for the ELS RES command. XXX TBD.
+ * Receive ABTS
+ * Deallocate sequence
+ * Send RJT
+ * Deallocate
+ *
+ * For now, we neglect conditions where only part of a sequence was
+ * received or transmitted, or where out-of-order receipt is detected.
+ */
+
+/*
+ * Locking notes:
+ *
+ * The EM code run in a per-CPU worker thread.
+ *
+ * To protect against concurrency between a worker thread code and timers,
+ * sequence allocation and deallocation must be locked.
+ * - exchange refcnt can be done atomicly without locks.
+ * - sequence allocation must be locked by exch lock.
+ * - If the em_lock and ex_lock must be taken at the same time, then the
+ * em_lock must be taken before the ex_lock.
+ */
+
+/*
+ * opcode names for debugging.
+ */
+static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
+
+#define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
+ unsigned int max_index)
+{
+ const char *name = NULL;
+
+ if (op < max_index)
+ name = table[op];
+ if (!name)
+ name = "unknown";
+ return name;
+}
+
+static const char *fc_exch_rctl_name(unsigned int op)
+{
+ return fc_exch_name_lookup(op, fc_exch_rctl_names,
+ FC_TABLE_SIZE(fc_exch_rctl_names));
+}
+
+/*
+ * Hold an exchange - keep it from being freed.
+ */
+static void fc_exch_hold(struct fc_exch *ep)
+{
+ atomic_inc(&ep->ex_refcnt);
+}
+
+/*
+ * setup fc hdr by initializing few more FC header fields and sof/eof.
+ * Initialized fields by this func:
+ * - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
+ * - sof and eof
+ */
+static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
+ u32 f_ctl)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ u16 fill;
+
+ fr_sof(fp) = ep->class;
+ if (ep->seq.cnt)
+ fr_sof(fp) = fc_sof_normal(ep->class);
+
+ if (f_ctl & FC_FC_END_SEQ) {
+ fr_eof(fp) = FC_EOF_T;
+ if (fc_sof_needs_ack(ep->class))
+ fr_eof(fp) = FC_EOF_N;
+ /*
+ * Form f_ctl.
+ * The number of fill bytes to make the length a 4-byte
+ * multiple is the low order 2-bits of the f_ctl.
+ * The fill itself will have been cleared by the frame
+ * allocation.
+ * After this, the length will be even, as expected by
+ * the transport.
+ */
+ fill = fr_len(fp) & 3;
+ if (fill) {
+ fill = 4 - fill;
+ /* TODO, this may be a problem with fragmented skb */
+ skb_put(fp_skb(fp), fill);
+ hton24(fh->fh_f_ctl, f_ctl | fill);
+ }
+ } else {
+ WARN_ON(fr_len(fp) % 4 != 0); /* no pad to non last frame */
+ fr_eof(fp) = FC_EOF_N;
+ }
+
+ /*
+ * Initialize remainig fh fields
+ * from fc_fill_fc_hdr
+ */
+ fh->fh_ox_id = htons(ep->oxid);
+ fh->fh_rx_id = htons(ep->rxid);
+ fh->fh_seq_id = ep->seq.id;
+ fh->fh_seq_cnt = htons(ep->seq.cnt);
+}
+
+
+/*
+ * Release a reference to an exchange.
+ * If the refcnt goes to zero and the exchange is complete, it is freed.
+ */
+static void fc_exch_release(struct fc_exch *ep)
+{
+ struct fc_exch_mgr *mp;
+
+ if (atomic_dec_and_test(&ep->ex_refcnt)) {
+ mp = ep->em;
+ if (ep->destructor)
+ ep->destructor(&ep->seq, ep->arg);
+ if (ep->lp->tt.exch_put)
+ ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
+ WARN_ON(!ep->esb_stat & ESB_ST_COMPLETE);
+ mempool_free(ep, mp->ep_pool);
+ }
+}
+
+static int fc_exch_done_locked(struct fc_exch *ep)
+{
+ int rc = 1;
+
+ /*
+ * We must check for completion in case there are two threads
+ * tyring to complete this. But the rrq code will reuse the
+ * ep, and in that case we only clear the resp and set it as
+ * complete, so it can be reused by the timer to send the rrq.
+ */
+ ep->resp = NULL;
+ if (ep->state & FC_EX_DONE)
+ return rc;
+ ep->esb_stat |= ESB_ST_COMPLETE;
+
+ if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {
+ ep->state |= FC_EX_DONE;
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ rc = 0;
+ }
+ return rc;
+}
+
+static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
+{
+ struct fc_exch_mgr *mp;
+
+ mp = ep->em;
+ spin_lock_bh(&mp->em_lock);
+ WARN_ON(mp->total_exches <= 0);
+ mp->total_exches--;
+ mp->exches[ep->xid - mp->min_xid] = NULL;
+ list_del(&ep->ex_list);
+ spin_unlock_bh(&mp->em_lock);
+ fc_exch_release(ep); /* drop hold for exch in mp */
+}
+
+/*
+ * Internal version of fc_exch_timer_set - used with lock held.
+ */
+static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
+ unsigned int timer_msec)
+{
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
+ return;
+
+ FC_DEBUG_EXCH("Exchange (%4x) timed out, notifying the upper layer\n",
+ ep->xid);
+ if (schedule_delayed_work(&ep->timeout_work,
+ msecs_to_jiffies(timer_msec)))
+ fc_exch_hold(ep); /* hold for timer */
+}
+
+/*
+ * Set timer for an exchange.
+ * The time is a minimum delay in milliseconds until the timer fires.
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
+ * Returns non-zero if a timer couldn't be allocated.
+ */
+static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
+{
+ spin_lock_bh(&ep->ex_lock);
+ fc_exch_timer_set_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
+}
+
+int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+{
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ struct fc_frame *fp;
+ int error;
+
+ ep = fc_seq_exch(req_sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL) ||
+ ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP)) {
+ spin_unlock_bh(&ep->ex_lock);
+ return -ENXIO;
+ }
+
+ /*
+ * Send the abort on a new sequence if possible.
+ */
+ sp = fc_seq_start_next_locked(&ep->seq);
+ if (!sp) {
+ spin_unlock_bh(&ep->ex_lock);
+ return -ENOMEM;
+ }
+
+ ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;
+ if (timer_msec)
+ fc_exch_timer_set_locked(ep, timer_msec);
+ spin_unlock_bh(&ep->ex_lock);
+
+ /*
+ * If not logged into the fabric, don't send ABTS but leave
+ * sequence active until next timeout.
+ */
+ if (!ep->sid)
+ return 0;
+
+ /*
+ * Send an abort for the sequence that timed out.
+ */
+ fp = fc_frame_alloc(ep->lp, 0);
+ if (fp) {
+ fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
+ FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+ error = fc_seq_send(ep->lp, sp, fp);
+ } else
+ error = -ENOBUFS;
+ return error;
+}
+EXPORT_SYMBOL(fc_seq_exch_abort);
+
+/*
+ * Exchange timeout - handle exchange timer expiration.
+ * The timer will have been cancelled before this is called.
+ */
+static void fc_exch_timeout(struct work_struct *work)
+{
+ struct fc_exch *ep = container_of(work, struct fc_exch,
+ timeout_work.work);
+ struct fc_seq *sp = &ep->seq;
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *arg;
+ u32 e_stat;
+ int rc = 1;
+
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
+ goto unlock;
+
+ e_stat = ep->esb_stat;
+ if (e_stat & ESB_ST_COMPLETE) {
+ ep->esb_stat = e_stat & ~ESB_ST_REC_QUAL;
+ if (e_stat & ESB_ST_REC_QUAL)
+ fc_exch_rrq(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ goto done;
+ } else {
+ resp = ep->resp;
+ arg = ep->arg;
+ ep->resp = NULL;
+ if (e_stat & ESB_ST_ABNORMAL)
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ if (resp)
+ resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
+ fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
+ goto done;
+ }
+unlock:
+ spin_unlock_bh(&ep->ex_lock);
+done:
+ /*
+ * This release matches the hold taken when the timer was set.
+ */
+ fc_exch_release(ep);
+}
+
+/*
+ * Allocate a sequence.
+ *
+ * We don't support multiple originated sequences on the same exchange.
+ * By implication, any previously originated sequence on this exchange
+ * is complete, and we reallocate the same sequence.
+ */
+static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
+{
+ struct fc_seq *sp;
+
+ sp = &ep->seq;
+ sp->ssb_stat = 0;
+ sp->cnt = 0;
+ sp->id = seq_id;
+ return sp;
+}
+
+/*
+ * fc_em_alloc_xid - returns an xid based on request type
+ * @lp : ptr to associated lport
+ * @fp : ptr to the assocated frame
+ *
+ * check the associated fc_fsp_pkt to get scsi command type and
+ * command direction to decide from which range this exch id
+ * will be allocated from.
+ *
+ * Returns : 0 or an valid xid
+ */
+static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
+{
+ u16 xid, min, max;
+ u16 *plast;
+ struct fc_exch *ep = NULL;
+
+ if (mp->max_read) {
+ if (fc_frame_is_read(fp)) {
+ min = mp->min_xid;
+ max = mp->max_read;
+ plast = &mp->last_read;
+ } else {
+ min = mp->max_read + 1;
+ max = mp->max_xid;
+ plast = &mp->last_xid;
+ }
+ } else {
+ min = mp->min_xid;
+ max = mp->max_xid;
+ plast = &mp->last_xid;
+ }
+ xid = *plast;
+ do {
+ xid = (xid == max) ? min : xid + 1;
+ ep = mp->exches[xid - mp->min_xid];
+ } while ((ep != NULL) && (xid != *plast));
+
+ if (unlikely(ep))
+ xid = 0;
+ else
+ *plast = xid;
+
+ return xid;
+}
+
+/*
+ * fc_exch_alloc - allocate an exchange.
+ * @mp : ptr to the exchange manager
+ * @xid: input xid
+ *
+ * if xid is supplied zero then assign next free exchange ID
+ * from exchange manager, otherwise use supplied xid.
+ * Returns with exch lock held.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
+ struct fc_frame *fp, u16 xid)
+{
+ struct fc_exch *ep;
+
+ /* allocate memory for exchange */
+ ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
+ if (!ep) {
+ atomic_inc(&mp->stats.no_free_exch);
+ goto out;
+ }
+ memset(ep, 0, sizeof(*ep));
+
+ spin_lock_bh(&mp->em_lock);
+ /* alloc xid if input xid 0 */
+ if (!xid) {
+ /* alloc a new xid */
+ xid = fc_em_alloc_xid(mp, fp);
+ if (!xid) {
+ printk(KERN_ERR "fc_em_alloc_xid() failed\n");
+ goto err;
+ }
+ }
+
+ fc_exch_hold(ep); /* hold for exch in mp */
+ spin_lock_init(&ep->ex_lock);
+ /*
+ * Hold exch lock for caller to prevent fc_exch_reset()
+ * from releasing exch while fc_exch_alloc() caller is
+ * still working on exch.
+ */
+ spin_lock_bh(&ep->ex_lock);
+
+ mp->exches[xid - mp->min_xid] = ep;
+ list_add_tail(&ep->ex_list, &mp->ex_list);
+ fc_seq_alloc(ep, ep->seq_id++);
+ mp->total_exches++;
+ spin_unlock_bh(&mp->em_lock);
+
+ /*
+ * update exchange
+ */
+ ep->oxid = ep->xid = xid;
+ ep->em = mp;
+ ep->lp = mp->lp;
+ ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
+ ep->rxid = FC_XID_UNKNOWN;
+ ep->class = mp->class;
+ INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);
+out:
+ return ep;
+err:
+ spin_unlock_bh(&mp->em_lock);
+ atomic_inc(&mp->stats.no_free_exch_xid);
+ mempool_free(ep, mp->ep_pool);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_alloc);
+
+/*
+ * Lookup and hold an exchange.
+ */
+static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
+{
+ struct fc_exch *ep = NULL;
+
+ if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
+ spin_lock_bh(&mp->em_lock);
+ ep = mp->exches[xid - mp->min_xid];
+ if (ep) {
+ fc_exch_hold(ep);
+ WARN_ON(ep->xid != xid);
+ }
+ spin_unlock_bh(&mp->em_lock);
+ }
+ return ep;
+}
+
+void fc_exch_done(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+ int rc;
+
+ spin_lock_bh(&ep->ex_lock);
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+}
+EXPORT_SYMBOL(fc_exch_done);
+
+/*
+ * Allocate a new exchange as responder.
+ * Sets the responder ID in the frame header.
+ */
+static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ u16 rxid;
+
+ ep = mp->lp->tt.exch_get(mp->lp, fp);
+ if (ep) {
+ ep->class = fc_frame_class(fp);
+
+ /*
+ * Set EX_CTX indicating we're responding on this exchange.
+ */
+ ep->f_ctl |= FC_FC_EX_CTX; /* we're responding */
+ ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not new */
+ fh = fc_frame_header_get(fp);
+ ep->sid = ntoh24(fh->fh_d_id);
+ ep->did = ntoh24(fh->fh_s_id);
+ ep->oid = ep->did;
+
+ /*
+ * Allocated exchange has placed the XID in the
+ * originator field. Move it to the responder field,
+ * and set the originator XID from the frame.
+ */
+ ep->rxid = ep->xid;
+ ep->oxid = ntohs(fh->fh_ox_id);
+ ep->esb_stat |= ESB_ST_RESP | ESB_ST_SEQ_INIT;
+ if ((ntoh24(fh->fh_f_ctl) & FC_FC_SEQ_INIT) == 0)
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+
+ /*
+ * Set the responder ID in the frame header.
+ * The old one should've been 0xffff.
+ * If it isn't, don't assign one.
+ * Incoming basic link service frames may specify
+ * a referenced RX_ID.
+ */
+ if (fh->fh_type != FC_TYPE_BLS) {
+ rxid = ntohs(fh->fh_rx_id);
+ WARN_ON(rxid != FC_XID_UNKNOWN);
+ fh->fh_rx_id = htons(ep->rxid);
+ }
+ fc_exch_hold(ep); /* hold for caller */
+ spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */
+ }
+ return ep;
+}
+
+/*
+ * Find a sequence for receive where the other end is originating the sequence.
+ * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
+ * on the ep that should be released by the caller.
+ */
+static enum fc_pf_rjt_reason
+fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_exch *ep = NULL;
+ struct fc_seq *sp = NULL;
+ enum fc_pf_rjt_reason reject = FC_RJT_NONE;
+ u32 f_ctl;
+ u16 xid;
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ WARN_ON((f_ctl & FC_FC_SEQ_CTX) != 0);
+
+ /*
+ * Lookup or create the exchange if we will be creating the sequence.
+ */
+ if (f_ctl & FC_FC_EX_CTX) {
+ xid = ntohs(fh->fh_ox_id); /* we originated exch */
+ ep = fc_exch_find(mp, xid);
+ if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ reject = FC_RJT_OX_ID;
+ goto out;
+ }
+ if (ep->rxid == FC_XID_UNKNOWN)
+ ep->rxid = ntohs(fh->fh_rx_id);
+ else if (ep->rxid != ntohs(fh->fh_rx_id)) {
+ reject = FC_RJT_OX_ID;
+ goto rel;
+ }
+ } else {
+ xid = ntohs(fh->fh_rx_id); /* we are the responder */
+
+ /*
+ * Special case for MDS issuing an ELS TEST with a
+ * bad rxid of 0.
+ * XXX take this out once we do the proper reject.
+ */
+ if (xid == 0 && fh->fh_r_ctl == FC_RCTL_ELS_REQ &&
+ fc_frame_payload_op(fp) == ELS_TEST) {
+ fh->fh_rx_id = htons(FC_XID_UNKNOWN);
+ xid = FC_XID_UNKNOWN;
+ }
+
+ /*
+ * new sequence - find the exchange
+ */
+ ep = fc_exch_find(mp, xid);
+ if ((f_ctl & FC_FC_FIRST_SEQ) && fc_sof_is_init(fr_sof(fp))) {
+ if (ep) {
+ atomic_inc(&mp->stats.xid_busy);
+ reject = FC_RJT_RX_ID;
+ goto rel;
+ }
+ ep = fc_exch_resp(mp, fp);
+ if (!ep) {
+ reject = FC_RJT_EXCH_EST; /* XXX */
+ goto out;
+ }
+ xid = ep->xid; /* get our XID */
+ } else if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ reject = FC_RJT_RX_ID; /* XID not found */
+ goto out;
+ }
+ }
+
+ /*
+ * At this point, we have the exchange held.
+ * Find or create the sequence.
+ */
+ if (fc_sof_is_init(fr_sof(fp))) {
+ sp = fc_seq_start_next(&ep->seq);
+ if (!sp) {
+ reject = FC_RJT_SEQ_XS; /* exchange shortage */
+ goto rel;
+ }
+ sp->id = fh->fh_seq_id;
+ sp->ssb_stat |= SSB_ST_RESP;
+ } else {
+ sp = &ep->seq;
+ if (sp->id != fh->fh_seq_id) {
+ atomic_inc(&mp->stats.seq_not_found);
+ reject = FC_RJT_SEQ_ID; /* sequence/exch should exist */
+ goto rel;
+ }
+ }
+ WARN_ON(ep != fc_seq_exch(sp));
+
+ if (f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+
+ fr_seq(fp) = sp;
+out:
+ return reject;
+rel:
+ fc_exch_done(&ep->seq);
+ fc_exch_release(ep); /* hold from fc_exch_find/fc_exch_resp */
+ return reject;
+}
+
+/*
+ * Find the sequence for a frame being received.
+ * We originated the sequence, so it should be found.
+ * We may or may not have originated the exchange.
+ * Does not hold the sequence for the caller.
+ */
+static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_exch *ep;
+ struct fc_seq *sp = NULL;
+ u32 f_ctl;
+ u16 xid;
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ WARN_ON((f_ctl & FC_FC_SEQ_CTX) != FC_FC_SEQ_CTX);
+ xid = ntohs((f_ctl & FC_FC_EX_CTX) ? fh->fh_ox_id : fh->fh_rx_id);
+ ep = fc_exch_find(mp, xid);
+ if (!ep)
+ return NULL;
+ if (ep->seq.id == fh->fh_seq_id) {
+ /*
+ * Save the RX_ID if we didn't previously know it.
+ */
+ sp = &ep->seq;
+ if ((f_ctl & FC_FC_EX_CTX) != 0 &&
+ ep->rxid == FC_XID_UNKNOWN) {
+ ep->rxid = ntohs(fh->fh_rx_id);
+ }
+ }
+ fc_exch_release(ep);
+ return sp;
+}
+
+/*
+ * Set addresses for an exchange.
+ * Note this must be done before the first sequence of the exchange is sent.
+ */
+static void fc_exch_set_addr(struct fc_exch *ep,
+ u32 orig_id, u32 resp_id)
+{
+ ep->oid = orig_id;
+ if (ep->esb_stat & ESB_ST_RESP) {
+ ep->sid = resp_id;
+ ep->did = orig_id;
+ } else {
+ ep->sid = orig_id;
+ ep->did = resp_id;
+ }
+}
+
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ sp = fc_seq_alloc(ep, ep->seq_id++);
+ FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x\n",
+ ep->xid, ep->f_ctl, sp->id);
+ return sp;
+}
+/*
+ * Allocate a new sequence on the same exchange as the supplied sequence.
+ * This will never return NULL.
+ */
+struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0);
+ sp = fc_seq_start_next_locked(sp);
+ spin_unlock_bh(&ep->ex_lock);
+
+ return sp;
+}
+EXPORT_SYMBOL(fc_seq_start_next);
+
+int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ int error;
+ u32 f_ctl;
+
+ ep = fc_seq_exch(sp);
+ WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, f_ctl);
+
+ /*
+ * update sequence count if this frame is carrying
+ * multiple FC frames when sequence offload is enabled
+ * by LLD.
+ */
+ if (fr_max_payload(fp))
+ sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
+ fr_max_payload(fp));
+ else
+ sp->cnt++;
+
+ /*
+ * Send the frame.
+ */
+ error = lp->tt.frame_send(lp, fp);
+
+ /*
+ * Update the exchange and sequence flags,
+ * assuming all frames for the sequence have been sent.
+ * We can only be called to send once for each sequence.
+ */
+ spin_lock_bh(&ep->ex_lock);
+ ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
+ if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return error;
+}
+EXPORT_SYMBOL(fc_seq_send);
+
+void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data)
+{
+ switch (els_cmd) {
+ case ELS_LS_RJT:
+ fc_seq_ls_rjt(sp, els_data->reason, els_data->explan);
+ break;
+ case ELS_LS_ACC:
+ fc_seq_ls_acc(sp);
+ break;
+ case ELS_RRQ:
+ fc_exch_els_rrq(sp, els_data->fp);
+ break;
+ case ELS_REC:
+ fc_exch_els_rec(sp, els_data->fp);
+ break;
+ default:
+ FC_DBG("Invalid ELS CMD:%x\n", els_cmd);
+ }
+}
+EXPORT_SYMBOL(fc_seq_els_rsp_send);
+
+/*
+ * Send a sequence, which is also the last sequence in the exchange.
+ */
+static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
+ enum fc_rctl rctl, enum fc_fh_type fh_type)
+{
+ u32 f_ctl;
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ f_ctl |= ep->f_ctl;
+ fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0);
+ fc_seq_send(ep->lp, sp, fp);
+}
+
+/*
+ * Send ACK_1 (or equiv.) indicating we received something.
+ * The frame we're acking is supplied.
+ */
+static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *rx_fh;
+ struct fc_frame_header *fh;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_lport *lp = ep->lp;
+ unsigned int f_ctl;
+
+ /*
+ * Don't send ACKs for class 3.
+ */
+ if (fc_sof_needs_ack(fr_sof(rx_fp))) {
+ fp = fc_frame_alloc(lp, 0);
+ if (!fp)
+ return;
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_ACK_1;
+ fh->fh_type = FC_TYPE_BLS;
+
+ /*
+ * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22).
+ * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT.
+ * Bits 9-8 are meaningful (retransmitted or unidirectional).
+ * Last ACK uses bits 7-6 (continue sequence),
+ * bits 5-4 are meaningful (what kind of ACK to use).
+ */
+ rx_fh = fc_frame_header_get(rx_fp);
+ f_ctl = ntoh24(rx_fh->fh_f_ctl);
+ f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX |
+ FC_FC_FIRST_SEQ | FC_FC_LAST_SEQ |
+ FC_FC_END_SEQ | FC_FC_END_CONN | FC_FC_SEQ_INIT |
+ FC_FC_RETX_SEQ | FC_FC_UNI_TX;
+ f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX;
+ hton24(fh->fh_f_ctl, f_ctl);
+
+ fc_exch_setup_hdr(ep, fp, f_ctl);
+ fh->fh_seq_id = rx_fh->fh_seq_id;
+ fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
+ fh->fh_parm_offset = htonl(1); /* ack single frame */
+
+ fr_sof(fp) = fr_sof(rx_fp);
+ if (f_ctl & FC_FC_END_SEQ)
+ fr_eof(fp) = FC_EOF_T;
+ else
+ fr_eof(fp) = FC_EOF_N;
+
+ (void) lp->tt.frame_send(lp, fp);
+ }
+}
+
+/*
+ * Send BLS Reject.
+ * This is for rejecting BA_ABTS only.
+ */
+static void
+fc_exch_send_ba_rjt(struct fc_frame *rx_fp, enum fc_ba_rjt_reason reason,
+ enum fc_ba_rjt_explan explan)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *rx_fh;
+ struct fc_frame_header *fh;
+ struct fc_ba_rjt *rp;
+ struct fc_lport *lp;
+ unsigned int f_ctl;
+
+ lp = fr_dev(rx_fp);
+ fp = fc_frame_alloc(lp, sizeof(*rp));
+ if (!fp)
+ return;
+ fh = fc_frame_header_get(fp);
+ rx_fh = fc_frame_header_get(rx_fp);
+
+ memset(fh, 0, sizeof(*fh) + sizeof(*rp));
+
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ rp->br_reason = reason;
+ rp->br_explan = explan;
+
+ /*
+ * seq_id, cs_ctl, df_ctl and param/offset are zero.
+ */
+ memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3);
+ memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3);
+ fh->fh_ox_id = rx_fh->fh_rx_id;
+ fh->fh_rx_id = rx_fh->fh_ox_id;
+ fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
+ fh->fh_r_ctl = FC_RCTL_BA_RJT;
+ fh->fh_type = FC_TYPE_BLS;
+
+ /*
+ * Form f_ctl by inverting EX_CTX and SEQ_CTX (bits 23, 22).
+ * Echo FIRST_SEQ, LAST_SEQ, END_SEQ, END_CONN, SEQ_INIT.
+ * Bits 9-8 are meaningful (retransmitted or unidirectional).
+ * Last ACK uses bits 7-6 (continue sequence),
+ * bits 5-4 are meaningful (what kind of ACK to use).
+ * Always set LAST_SEQ, END_SEQ.
+ */
+ f_ctl = ntoh24(rx_fh->fh_f_ctl);
+ f_ctl &= FC_FC_EX_CTX | FC_FC_SEQ_CTX |
+ FC_FC_END_CONN | FC_FC_SEQ_INIT |
+ FC_FC_RETX_SEQ | FC_FC_UNI_TX;
+ f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX;
+ f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ f_ctl &= ~FC_FC_FIRST_SEQ;
+ hton24(fh->fh_f_ctl, f_ctl);
+
+ fr_sof(fp) = fc_sof_class(fr_sof(rx_fp));
+ fr_eof(fp) = FC_EOF_T;
+ if (fc_sof_needs_ack(fr_sof(fp)))
+ fr_eof(fp) = FC_EOF_N;
+
+ (void) lp->tt.frame_send(lp, fp);
+}
+
+/*
+ * Handle an incoming ABTS. This would be for target mode usually,
+ * but could be due to lost FCP transfer ready, confirm or RRQ.
+ * We always handle this as an exchange abort, ignoring the parameter.
+ */
+static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
+{
+ struct fc_frame *fp;
+ struct fc_ba_acc *ap;
+ struct fc_frame_header *fh;
+ struct fc_seq *sp;
+
+ if (!ep)
+ goto reject;
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->esb_stat & ESB_ST_COMPLETE) {
+ spin_unlock_bh(&ep->ex_lock);
+ goto reject;
+ }
+ if (!(ep->esb_stat & ESB_ST_REC_QUAL))
+ fc_exch_hold(ep); /* hold for REC_QUAL */
+ ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL;
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
+
+ fp = fc_frame_alloc(ep->lp, sizeof(*ap));
+ if (!fp) {
+ spin_unlock_bh(&ep->ex_lock);
+ goto free;
+ }
+ fh = fc_frame_header_get(fp);
+ ap = fc_frame_payload_get(fp, sizeof(*ap));
+ memset(ap, 0, sizeof(*ap));
+ sp = &ep->seq;
+ ap->ba_high_seq_cnt = htons(0xffff);
+ if (sp->ssb_stat & SSB_ST_RESP) {
+ ap->ba_seq_id = sp->id;
+ ap->ba_seq_id_val = FC_BA_SEQ_ID_VAL;
+ ap->ba_high_seq_cnt = fh->fh_seq_cnt;
+ ap->ba_low_seq_cnt = htons(sp->cnt);
+ }
+ sp = fc_seq_start_next(sp);
+ spin_unlock_bh(&ep->ex_lock);
+ fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS);
+ fc_frame_free(rx_fp);
+ return;
+
+reject:
+ fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID);
+free:
+ fc_frame_free(rx_fp);
+}
+
+/*
+ * Handle receive where the other end is originating the sequence.
+ */
+static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_seq *sp = NULL;
+ struct fc_exch *ep = NULL;
+ enum fc_sof sof;
+ enum fc_eof eof;
+ u32 f_ctl;
+ enum fc_pf_rjt_reason reject;
+
+ fr_seq(fp) = NULL;
+ reject = fc_seq_lookup_recip(mp, fp);
+ if (reject == FC_RJT_NONE) {
+ sp = fr_seq(fp); /* sequence will be held */
+ ep = fc_seq_exch(sp);
+ sof = fr_sof(fp);
+ eof = fr_eof(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_seq_send_ack(sp, fp);
+
+ /*
+ * Call the receive function.
+ *
+ * The receive function may allocate a new sequence
+ * over the old one, so we shouldn't change the
+ * sequence after this.
+ *
+ * The frame will be freed by the receive function.
+ * If new exch resp handler is valid then call that
+ * first.
+ */
+ if (ep->resp)
+ ep->resp(sp, fp, ep->arg);
+ else
+ lp->tt.lport_recv(lp, sp, fp);
+ fc_exch_release(ep); /* release from lookup */
+ } else {
+ FC_DEBUG_EXCH("exch/seq lookup failed: reject %x\n", reject);
+ fc_frame_free(fp);
+ }
+}
+
+/*
+ * Handle receive where the other end is originating the sequence in
+ * response to our exchange.
+ */
+static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ enum fc_sof sof;
+ u32 f_ctl;
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *ex_resp_arg;
+ int rc;
+
+ ep = fc_exch_find(mp, ntohs(fh->fh_ox_id));
+ if (!ep) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto out;
+ }
+ if (ep->rxid == FC_XID_UNKNOWN)
+ ep->rxid = ntohs(fh->fh_rx_id);
+ if (ep->sid != 0 && ep->sid != ntoh24(fh->fh_d_id)) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto rel;
+ }
+ if (ep->did != ntoh24(fh->fh_s_id) &&
+ ep->did != FC_FID_FLOGI) {
+ atomic_inc(&mp->stats.xid_not_found);
+ goto rel;
+ }
+ sof = fr_sof(fp);
+ if (fc_sof_is_init(sof)) {
+ sp = fc_seq_start_next(&ep->seq);
+ sp->id = fh->fh_seq_id;
+ sp->ssb_stat |= SSB_ST_RESP;
+ } else {
+ sp = &ep->seq;
+ if (sp->id != fh->fh_seq_id) {
+ atomic_inc(&mp->stats.seq_not_found);
+ goto rel;
+ }
+ }
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fr_seq(fp) = sp;
+ if (f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+
+ if (fc_sof_needs_ack(sof))
+ fc_seq_send_ack(sp, fp);
+ resp = ep->resp;
+ ex_resp_arg = ep->arg;
+
+ if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&
+ (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+ (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+ spin_lock_bh(&ep->ex_lock);
+ rc = fc_exch_done_locked(ep);
+ WARN_ON(fc_seq_exch(sp) != ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ }
+
+ /*
+ * Call the receive function.
+ * The sequence is held (has a refcnt) for us,
+ * but not for the receive function.
+ *
+ * The receive function may allocate a new sequence
+ * over the old one, so we shouldn't change the
+ * sequence after this.
+ *
+ * The frame will be freed by the receive function.
+ * If new exch resp handler is valid then call that
+ * first.
+ */
+ if (resp)
+ resp(sp, fp, ex_resp_arg);
+ else
+ fc_frame_free(fp);
+ fc_exch_release(ep);
+ return;
+rel:
+ fc_exch_release(ep);
+out:
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle receive for a sequence where other end is responding to our sequence.
+ */
+static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_seq *sp;
+
+ sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */
+ if (!sp) {
+ atomic_inc(&mp->stats.xid_not_found);
+ FC_DEBUG_EXCH("seq lookup failed\n");
+ } else {
+ atomic_inc(&mp->stats.non_bls_resp);
+ FC_DEBUG_EXCH("non-BLS response to sequence");
+ }
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle the response to an ABTS for exchange or sequence.
+ * This can be BA_ACC or BA_RJT.
+ */
+static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
+{
+ void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg);
+ void *ex_resp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ba_acc *ap;
+ struct fc_seq *sp;
+ u16 low;
+ u16 high;
+ int rc = 1, has_rec = 0;
+
+ fh = fc_frame_header_get(fp);
+ FC_DEBUG_EXCH("exch: BLS rctl %x - %s\n",
+ fh->fh_r_ctl, fc_exch_rctl_name(fh->fh_r_ctl));
+
+ if (cancel_delayed_work_sync(&ep->timeout_work))
+ fc_exch_release(ep); /* release from pending timer hold */
+
+ spin_lock_bh(&ep->ex_lock);
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_ACC:
+ ap = fc_frame_payload_get(fp, sizeof(*ap));
+ if (!ap)
+ break;
+
+ /*
+ * Decide whether to establish a Recovery Qualifier.
+ * We do this if there is a non-empty SEQ_CNT range and
+ * SEQ_ID is the same as the one we aborted.
+ */
+ low = ntohs(ap->ba_low_seq_cnt);
+ high = ntohs(ap->ba_high_seq_cnt);
+ if ((ep->esb_stat & ESB_ST_REC_QUAL) == 0 &&
+ (ap->ba_seq_id_val != FC_BA_SEQ_ID_VAL ||
+ ap->ba_seq_id == ep->seq_id) && low != high) {
+ ep->esb_stat |= ESB_ST_REC_QUAL;
+ fc_exch_hold(ep); /* hold for recovery qualifier */
+ has_rec = 1;
+ }
+ break;
+ case FC_RCTL_BA_RJT:
+ break;
+ default:
+ break;
+ }
+
+ resp = ep->resp;
+ ex_resp_arg = ep->arg;
+
+ /* do we need to do some other checks here. Can we reuse more of
+ * fc_exch_recv_seq_resp
+ */
+ sp = &ep->seq;
+ /*
+ * do we want to check END_SEQ as well as LAST_SEQ here?
+ */
+ if (ep->fh_type != FC_TYPE_FCP &&
+ ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+
+ if (resp)
+ resp(sp, fp, ex_resp_arg);
+ else
+ fc_frame_free(fp);
+
+ if (has_rec)
+ fc_exch_timer_set(ep, ep->r_a_tov);
+
+}
+
+/*
+ * Receive BLS sequence.
+ * This is always a sequence initiated by the remote side.
+ * We may be either the originator or recipient of the exchange.
+ */
+static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fc_exch *ep;
+ u32 f_ctl;
+
+ fh = fc_frame_header_get(fp);
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fr_seq(fp) = NULL;
+
+ ep = fc_exch_find(mp, (f_ctl & FC_FC_EX_CTX) ?
+ ntohs(fh->fh_ox_id) : ntohs(fh->fh_rx_id));
+ if (ep && (f_ctl & FC_FC_SEQ_INIT)) {
+ spin_lock_bh(&ep->ex_lock);
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ }
+ if (f_ctl & FC_FC_SEQ_CTX) {
+ /*
+ * A response to a sequence we initiated.
+ * This should only be ACKs for class 2 or F.
+ */
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_ACK_1:
+ case FC_RCTL_ACK_0:
+ break;
+ default:
+ FC_DEBUG_EXCH("BLS rctl %x - %s received",
+ fh->fh_r_ctl,
+ fc_exch_rctl_name(fh->fh_r_ctl));
+ break;
+ }
+ fc_frame_free(fp);
+ } else {
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_RJT:
+ case FC_RCTL_BA_ACC:
+ if (ep)
+ fc_exch_abts_resp(ep, fp);
+ else
+ fc_frame_free(fp);
+ break;
+ case FC_RCTL_BA_ABTS:
+ fc_exch_recv_abts(ep, fp);
+ break;
+ default: /* ignore junk */
+ fc_frame_free(fp);
+ break;
+ }
+ }
+ if (ep)
+ fc_exch_release(ep); /* release hold taken by fc_exch_find */
+}
+
+/*
+ * Accept sequence with LS_ACC.
+ * If this fails due to allocation or transmit congestion, assume the
+ * originator will repeat the sequence.
+ */
+static void fc_seq_ls_acc(struct fc_seq *req_sp)
+{
+ struct fc_seq *sp;
+ struct fc_els_ls_acc *acc;
+ struct fc_frame *fp;
+
+ sp = fc_seq_start_next(req_sp);
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
+ if (fp) {
+ acc = fc_frame_payload_get(fp, sizeof(*acc));
+ memset(acc, 0, sizeof(*acc));
+ acc->la_cmd = ELS_LS_ACC;
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+ }
+}
+
+/*
+ * Reject sequence with ELS LS_RJT.
+ * If this fails due to allocation or transmit congestion, assume the
+ * originator will repeat the sequence.
+ */
+static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
+ enum fc_els_rjt_explan explan)
+{
+ struct fc_seq *sp;
+ struct fc_els_ls_rjt *rjt;
+ struct fc_frame *fp;
+
+ sp = fc_seq_start_next(req_sp);
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt));
+ if (fp) {
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ memset(rjt, 0, sizeof(*rjt));
+ rjt->er_cmd = ELS_LS_RJT;
+ rjt->er_reason = reason;
+ rjt->er_explan = explan;
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+ }
+}
+
+static void fc_exch_reset(struct fc_exch *ep)
+{
+ struct fc_seq *sp;
+ void (*resp)(struct fc_seq *, struct fc_frame *, void *);
+ void *arg;
+ int rc = 1;
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->state |= FC_EX_RST_CLEANUP;
+ /*
+ * we really want to call del_timer_sync, but cannot due
+ * to the lport calling with the lport lock held (some resp
+ * functions can also grab the lport lock which could cause
+ * a deadlock).
+ */
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
+ resp = ep->resp;
+ ep->resp = NULL;
+ if (ep->esb_stat & ESB_ST_REC_QUAL)
+ atomic_dec(&ep->ex_refcnt); /* drop hold for rec_qual */
+ ep->esb_stat &= ~ESB_ST_REC_QUAL;
+ arg = ep->arg;
+ sp = &ep->seq;
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+
+ if (resp)
+ resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
+}
+
+/*
+ * Reset an exchange manager, releasing all sequences and exchanges.
+ * If sid is non-zero, reset only exchanges we source from that FID.
+ * If did is non-zero, reset only exchanges destined to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_exch_mgr *mp, u32 sid, u32 did)
+{
+ struct fc_exch *ep;
+ struct fc_exch *next;
+
+ spin_lock_bh(&mp->em_lock);
+restart:
+ list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) {
+ if ((sid == 0 || sid == ep->sid) &&
+ (did == 0 || did == ep->did)) {
+ fc_exch_hold(ep);
+ spin_unlock_bh(&mp->em_lock);
+
+ fc_exch_reset(ep);
+
+ fc_exch_release(ep);
+ spin_lock_bh(&mp->em_lock);
+
+ /*
+ * must restart loop incase while lock was down
+ * multiple eps were released.
+ */
+ goto restart;
+ }
+ }
+ spin_unlock_bh(&mp->em_lock);
+}
+EXPORT_SYMBOL(fc_exch_mgr_reset);
+
+/*
+ * Handle incoming ELS REC - Read Exchange Concise.
+ * Note that the requesting port may be different than the S_ID in the request.
+ */
+static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep;
+ struct fc_exch_mgr *em;
+ struct fc_els_rec *rp;
+ struct fc_els_rec_acc *acc;
+ enum fc_els_rjt_reason reason = ELS_RJT_LOGIC;
+ enum fc_els_rjt_explan explan;
+ u32 sid;
+ u16 rxid;
+ u16 oxid;
+
+ rp = fc_frame_payload_get(rfp, sizeof(*rp));
+ explan = ELS_EXPL_INV_LEN;
+ if (!rp)
+ goto reject;
+ sid = ntoh24(rp->rec_s_id);
+ rxid = ntohs(rp->rec_rx_id);
+ oxid = ntohs(rp->rec_ox_id);
+
+ /*
+ * Currently it's hard to find the local S_ID from the exchange
+ * manager. This will eventually be fixed, but for now it's easier
+ * to lookup the subject exchange twice, once as if we were
+ * the initiator, and then again if we weren't.
+ */
+ em = fc_seq_exch(sp)->em;
+ ep = fc_exch_find(em, oxid);
+ explan = ELS_EXPL_OXID_RXID;
+ if (ep && ep->oid == sid) {
+ if (ep->rxid != FC_XID_UNKNOWN &&
+ rxid != FC_XID_UNKNOWN &&
+ ep->rxid != rxid)
+ goto rel;
+ } else {
+ if (ep)
+ fc_exch_release(ep);
+ ep = NULL;
+ if (rxid != FC_XID_UNKNOWN)
+ ep = fc_exch_find(em, rxid);
+ if (!ep)
+ goto reject;
+ }
+
+ fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
+ if (!fp) {
+ fc_exch_done(sp);
+ goto out;
+ }
+ sp = fc_seq_start_next(sp);
+ acc = fc_frame_payload_get(fp, sizeof(*acc));
+ memset(acc, 0, sizeof(*acc));
+ acc->reca_cmd = ELS_LS_ACC;
+ acc->reca_ox_id = rp->rec_ox_id;
+ memcpy(acc->reca_ofid, rp->rec_s_id, 3);
+ acc->reca_rx_id = htons(ep->rxid);
+ if (ep->sid == ep->oid)
+ hton24(acc->reca_rfid, ep->did);
+ else
+ hton24(acc->reca_rfid, ep->sid);
+ acc->reca_fc4value = htonl(ep->seq.rec_data);
+ acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP |
+ ESB_ST_SEQ_INIT |
+ ESB_ST_COMPLETE));
+ sp = fc_seq_start_next(sp);
+ fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+out:
+ fc_exch_release(ep);
+ fc_frame_free(rfp);
+ return;
+
+rel:
+ fc_exch_release(ep);
+reject:
+ fc_seq_ls_rjt(sp, reason, explan);
+ fc_frame_free(rfp);
+}
+
+/*
+ * Handle response from RRQ.
+ * Not much to do here, really.
+ * Should report errors.
+ *
+ * TODO: fix error handler.
+ */
+static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+{
+ struct fc_exch *aborted_ep = arg;
+ unsigned int op;
+
+ if (IS_ERR(fp)) {
+ int err = PTR_ERR(fp);
+
+ if (err == -FC_EX_CLOSED)
+ goto cleanup;
+ FC_DBG("Cannot process RRQ, because of frame error %d\n", err);
+ return;
+ }
+
+ op = fc_frame_payload_op(fp);
+ fc_frame_free(fp);
+
+ switch (op) {
+ case ELS_LS_RJT:
+ FC_DBG("LS_RJT for RRQ");
+ /* fall through */
+ case ELS_LS_ACC:
+ goto cleanup;
+ default:
+ FC_DBG("unexpected response op %x for RRQ", op);
+ return;
+ }
+
+cleanup:
+ fc_exch_done(&aborted_ep->seq);
+ /* drop hold for rec qual */
+ fc_exch_release(aborted_ep);
+}
+
+/*
+ * Send ELS RRQ - Reinstate Recovery Qualifier.
+ * This tells the remote port to stop blocking the use of
+ * the exchange and the seq_cnt range.
+ */
+static void fc_exch_rrq(struct fc_exch *ep)
+{
+ struct fc_lport *lp;
+ struct fc_els_rrq *rrq;
+ struct fc_frame *fp;
+ struct fc_seq *rrq_sp;
+ u32 did;
+
+ lp = ep->lp;
+
+ fp = fc_frame_alloc(lp, sizeof(*rrq));
+ if (!fp)
+ return;
+ rrq = fc_frame_payload_get(fp, sizeof(*rrq));
+ memset(rrq, 0, sizeof(*rrq));
+ rrq->rrq_cmd = ELS_RRQ;
+ hton24(rrq->rrq_s_id, ep->sid);
+ rrq->rrq_ox_id = htons(ep->oxid);
+ rrq->rrq_rx_id = htons(ep->rxid);
+
+ did = ep->did;
+ if (ep->esb_stat & ESB_ST_RESP)
+ did = ep->sid;
+
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
+ fc_host_port_id(lp->host), FC_TYPE_ELS,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep,
+ lp->e_d_tov);
+ if (!rrq_sp) {
+ ep->esb_stat |= ESB_ST_REC_QUAL;
+ fc_exch_timer_set_locked(ep, ep->r_a_tov);
+ return;
+ }
+}
+
+
+/*
+ * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+ */
+static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_exch *ep; /* request or subject exchange */
+ struct fc_els_rrq *rp;
+ u32 sid;
+ u16 xid;
+ enum fc_els_rjt_explan explan;
+
+ rp = fc_frame_payload_get(fp, sizeof(*rp));
+ explan = ELS_EXPL_INV_LEN;
+ if (!rp)
+ goto reject;
+
+ /*
+ * lookup subject exchange.
+ */
+ ep = fc_seq_exch(sp);
+ sid = ntoh24(rp->rrq_s_id); /* subject source */
+ xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
+ ep = fc_exch_find(ep->em, xid);
+
+ explan = ELS_EXPL_OXID_RXID;
+ if (!ep)
+ goto reject;
+ spin_lock_bh(&ep->ex_lock);
+ if (ep->oxid != ntohs(rp->rrq_ox_id))
+ goto unlock_reject;
+ if (ep->rxid != ntohs(rp->rrq_rx_id) &&
+ ep->rxid != FC_XID_UNKNOWN)
+ goto unlock_reject;
+ explan = ELS_EXPL_SID;
+ if (ep->sid != sid)
+ goto unlock_reject;
+
+ /*
+ * Clear Recovery Qualifier state, and cancel timer if complete.
+ */
+ if (ep->esb_stat & ESB_ST_REC_QUAL) {
+ ep->esb_stat &= ~ESB_ST_REC_QUAL;
+ atomic_dec(&ep->ex_refcnt); /* drop hold for rec qual */
+ }
+ if (ep->esb_stat & ESB_ST_COMPLETE) {
+ if (cancel_delayed_work(&ep->timeout_work))
+ atomic_dec(&ep->ex_refcnt); /* drop timer hold */
+ }
+
+ spin_unlock_bh(&ep->ex_lock);
+
+ /*
+ * Send LS_ACC.
+ */
+ fc_seq_ls_acc(sp);
+ fc_frame_free(fp);
+ return;
+
+unlock_reject:
+ spin_unlock_bh(&ep->ex_lock);
+ fc_exch_release(ep); /* drop hold from fc_exch_find */
+reject:
+ fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+ fc_frame_free(fp);
+}
+
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+ enum fc_class class,
+ u16 min_xid, u16 max_xid)
+{
+ struct fc_exch_mgr *mp;
+ size_t len;
+
+ if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) {
+ FC_DBG("Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+ min_xid, max_xid);
+ return NULL;
+ }
+
+ /*
+ * Memory need for EM
+ */
+#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
+ len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
+ len += sizeof(struct fc_exch_mgr);
+
+ mp = kzalloc(len, GFP_ATOMIC);
+ if (!mp)
+ return NULL;
+
+ mp->class = class;
+ mp->total_exches = 0;
+ mp->exches = (struct fc_exch **)(mp + 1);
+ mp->lp = lp;
+ /* adjust em exch xid range for offload */
+ mp->min_xid = min_xid;
+ mp->max_xid = max_xid;
+ mp->last_xid = min_xid - 1;
+ mp->max_read = 0;
+ mp->last_read = 0;
+ if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
+ mp->max_read = lp->lro_xid;
+ mp->last_read = min_xid - 1;
+ mp->last_xid = mp->max_read;
+ } else {
+ /* disable lro if no xid control over read */
+ lp->lro_enabled = 0;
+ }
+
+ INIT_LIST_HEAD(&mp->ex_list);
+ spin_lock_init(&mp->em_lock);
+
+ mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
+ if (!mp->ep_pool)
+ goto free_mp;
+
+ return mp;
+
+free_mp:
+ kfree(mp);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_mgr_alloc);
+
+void fc_exch_mgr_free(struct fc_exch_mgr *mp)
+{
+ WARN_ON(!mp);
+ /*
+ * The total exch count must be zero
+ * before freeing exchange manager.
+ */
+ WARN_ON(mp->total_exches != 0);
+ mempool_destroy(mp->ep_pool);
+ kfree(mp);
+}
+EXPORT_SYMBOL(fc_exch_mgr_free);
+
+struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
+{
+ if (!lp || !lp->emp)
+ return NULL;
+
+ return fc_exch_alloc(lp->emp, fp, 0);
+}
+EXPORT_SYMBOL(fc_exch_get);
+
+struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *, void *),
+ void *arg, u32 timer_msec)
+{
+ struct fc_exch *ep;
+ struct fc_seq *sp = NULL;
+ struct fc_frame_header *fh;
+ int rc = 1;
+
+ ep = lp->tt.exch_get(lp, fp);
+ if (!ep) {
+ fc_frame_free(fp);
+ return NULL;
+ }
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ fh = fc_frame_header_get(fp);
+ fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
+ ep->resp = resp;
+ ep->destructor = destructor;
+ ep->arg = arg;
+ ep->r_a_tov = FC_DEF_R_A_TOV;
+ ep->lp = lp;
+ sp = &ep->seq;
+
+ ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+ ep->f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+ sp->cnt++;
+
+ if (unlikely(lp->tt.frame_send(lp, fp)))
+ goto err;
+
+ if (timer_msec)
+ fc_exch_timer_set_locked(ep, timer_msec);
+ ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */
+
+ if (ep->f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return sp;
+err:
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_mgr_delete_ep(ep);
+ return NULL;
+}
+EXPORT_SYMBOL(fc_exch_seq_send);
+
+/*
+ * Receive a frame
+ */
+void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ u32 f_ctl;
+
+ /* lport lock ? */
+ if (!lp || !mp || (lp->state == LPORT_ST_NONE)) {
+ FC_DBG("fc_lport or EM is not allocated and configured");
+ fc_frame_free(fp);
+ return;
+ }
+
+ /*
+ * If frame is marked invalid, just drop it.
+ */
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ switch (fr_eof(fp)) {
+ case FC_EOF_T:
+ if (f_ctl & FC_FC_END_SEQ)
+ skb_trim(fp_skb(fp), fr_len(fp) - FC_FC_FILL(f_ctl));
+ /* fall through */
+ case FC_EOF_N:
+ if (fh->fh_type == FC_TYPE_BLS)
+ fc_exch_recv_bls(mp, fp);
+ else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) ==
+ FC_FC_EX_CTX)
+ fc_exch_recv_seq_resp(mp, fp);
+ else if (f_ctl & FC_FC_SEQ_CTX)
+ fc_exch_recv_resp(mp, fp);
+ else
+ fc_exch_recv_req(lp, mp, fp);
+ break;
+ default:
+ FC_DBG("dropping invalid frame (eof %x)", fr_eof(fp));
+ fc_frame_free(fp);
+ break;
+ }
+}
+EXPORT_SYMBOL(fc_exch_recv);
+
+int fc_exch_init(struct fc_lport *lp)
+{
+ if (!lp->tt.exch_get) {
+ /*
+ * exch_put() should be NULL if
+ * exch_get() is NULL
+ */
+ WARN_ON(lp->tt.exch_put);
+ lp->tt.exch_get = fc_exch_get;
+ }
+
+ if (!lp->tt.seq_start_next)
+ lp->tt.seq_start_next = fc_seq_start_next;
+
+ if (!lp->tt.exch_seq_send)
+ lp->tt.exch_seq_send = fc_exch_seq_send;
+
+ if (!lp->tt.seq_send)
+ lp->tt.seq_send = fc_seq_send;
+
+ if (!lp->tt.seq_els_rsp_send)
+ lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
+
+ if (!lp->tt.exch_done)
+ lp->tt.exch_done = fc_exch_done;
+
+ if (!lp->tt.exch_mgr_reset)
+ lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
+
+ if (!lp->tt.seq_exch_abort)
+ lp->tt.seq_exch_abort = fc_seq_exch_abort;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_exch_init);
+
+int fc_setup_exch_mgr(void)
+{
+ fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fc_em_cachep)
+ return -ENOMEM;
+ return 0;
+}
+
+void fc_destroy_exch_mgr(void)
+{
+ kmem_cache_destroy(fc_em_cachep);
+}
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
new file mode 100644
index 000000000000..404e63ff46b8
--- /dev/null
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -0,0 +1,2131 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 Red Hat, Inc. All rights reserved.
+ * Copyright(c) 2008 Mike Christie
+ *
+ * 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.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include <linux/crc32.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <scsi/fc/fc_fc2.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("libfc");
+MODULE_LICENSE("GPL");
+
+static int fc_fcp_debug;
+
+#define FC_DEBUG_FCP(fmt...) \
+ do { \
+ if (fc_fcp_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static struct kmem_cache *scsi_pkt_cachep;
+
+/* SRB state definitions */
+#define FC_SRB_FREE 0 /* cmd is free */
+#define FC_SRB_CMD_SENT (1 << 0) /* cmd has been sent */
+#define FC_SRB_RCV_STATUS (1 << 1) /* response has arrived */
+#define FC_SRB_ABORT_PENDING (1 << 2) /* cmd abort sent to device */
+#define FC_SRB_ABORTED (1 << 3) /* abort acknowleged */
+#define FC_SRB_DISCONTIG (1 << 4) /* non-sequential data recvd */
+#define FC_SRB_COMPL (1 << 5) /* fc_io_compl has been run */
+#define FC_SRB_FCP_PROCESSING_TMO (1 << 6) /* timer function processing */
+#define FC_SRB_NOMEM (1 << 7) /* dropped to out of mem */
+
+#define FC_SRB_READ (1 << 1)
+#define FC_SRB_WRITE (1 << 0)
+
+/*
+ * The SCp.ptr should be tested and set under the host lock. NULL indicates
+ * that the command has been retruned to the scsi layer.
+ */
+#define CMD_SP(Cmnd) ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr)
+#define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in)
+#define CMD_COMPL_STATUS(Cmnd) ((Cmnd)->SCp.this_residual)
+#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
+#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+
+struct fc_fcp_internal {
+ mempool_t *scsi_pkt_pool;
+ struct list_head scsi_pkt_queue;
+ u8 throttled;
+};
+
+#define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv)
+
+/*
+ * function prototypes
+ * FC scsi I/O related functions
+ */
+static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
+static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
+static void fc_timeout_error(struct fc_fcp_pkt *);
+static void fc_fcp_timeout(unsigned long data);
+static void fc_fcp_rec(struct fc_fcp_pkt *);
+static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
+static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_io_compl(struct fc_fcp_pkt *);
+
+static void fc_fcp_srr(struct fc_fcp_pkt *, enum fc_rctl, u32);
+static void fc_fcp_srr_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
+
+/*
+ * command status codes
+ */
+#define FC_COMPLETE 0
+#define FC_CMD_ABORTED 1
+#define FC_CMD_RESET 2
+#define FC_CMD_PLOGO 3
+#define FC_SNS_RCV 4
+#define FC_TRANS_ERR 5
+#define FC_DATA_OVRRUN 6
+#define FC_DATA_UNDRUN 7
+#define FC_ERROR 8
+#define FC_HRD_ERROR 9
+#define FC_CMD_TIME_OUT 10
+
+/*
+ * Error recovery timeout values.
+ */
+#define FC_SCSI_ER_TIMEOUT (10 * HZ)
+#define FC_SCSI_TM_TOV (10 * HZ)
+#define FC_SCSI_REC_TOV (2 * HZ)
+#define FC_HOST_RESET_TIMEOUT (30 * HZ)
+
+#define FC_MAX_ERROR_CNT 5
+#define FC_MAX_RECOV_RETRY 3
+
+#define FC_FCP_DFLT_QUEUE_DEPTH 32
+
+/**
+ * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet
+ * @lp: fc lport struct
+ * @gfp: gfp flags for allocation
+ *
+ * This is used by upper layer scsi driver.
+ * Return Value : scsi_pkt structure or null on allocation failure.
+ * Context : call from process context. no locking required.
+ */
+static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_pkt *fsp;
+
+ fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
+ if (fsp) {
+ memset(fsp, 0, sizeof(*fsp));
+ fsp->lp = lp;
+ atomic_set(&fsp->ref_cnt, 1);
+ init_timer(&fsp->timer);
+ INIT_LIST_HEAD(&fsp->list);
+ spin_lock_init(&fsp->scsi_pkt_lock);
+ }
+ return fsp;
+}
+
+/**
+ * fc_fcp_pkt_release - release hold on scsi_pkt packet
+ * @fsp: fcp packet struct
+ *
+ * This is used by upper layer scsi driver.
+ * Context : call from process and interrupt context.
+ * no locking required
+ */
+static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
+{
+ if (atomic_dec_and_test(&fsp->ref_cnt)) {
+ struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp);
+
+ mempool_free(fsp, si->scsi_pkt_pool);
+ }
+}
+
+static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
+{
+ atomic_inc(&fsp->ref_cnt);
+}
+
+/**
+ * fc_fcp_pkt_destory - release hold on scsi_pkt packet
+ *
+ * @seq: exchange sequence
+ * @fsp: fcp packet struct
+ *
+ * Release hold on scsi_pkt packet set to keep scsi_pkt
+ * till EM layer exch resource is not freed.
+ * Context : called from from EM layer.
+ * no locking required
+ */
+static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
+{
+ fc_fcp_pkt_release(fsp);
+}
+
+/**
+ * fc_fcp_lock_pkt - lock a packet and get a ref to it.
+ * @fsp: fcp packet
+ *
+ * We should only return error if we return a command to scsi-ml before
+ * getting a response. This can happen in cases where we send a abort, but
+ * do not wait for the response and the abort and command can be passing
+ * each other on the wire/network-layer.
+ *
+ * Note: this function locks the packet and gets a reference to allow
+ * callers to call the completion function while the lock is held and
+ * not have to worry about the packets refcount.
+ *
+ * TODO: Maybe we should just have callers grab/release the lock and
+ * have a function that they call to verify the fsp and grab a ref if
+ * needed.
+ */
+static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
+{
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ if (fsp->state & FC_SRB_COMPL) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ return -EPERM;
+ }
+
+ fc_fcp_pkt_hold(fsp);
+ return 0;
+}
+
+static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
+{
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ fc_fcp_pkt_release(fsp);
+}
+
+static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
+{
+ if (!(fsp->state & FC_SRB_COMPL))
+ mod_timer(&fsp->timer, jiffies + delay);
+}
+
+static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
+{
+ if (!fsp->seq_ptr)
+ return -EINVAL;
+
+ fsp->state |= FC_SRB_ABORT_PENDING;
+ return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
+}
+
+/*
+ * Retry command.
+ * An abort isn't needed.
+ */
+static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
+{
+ if (fsp->seq_ptr) {
+ fsp->lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ fsp->status_code = FC_ERROR;
+ fc_fcp_complete_locked(fsp);
+}
+
+/*
+ * Receive SCSI data from target.
+ * Called after receiving solicited data.
+ */
+static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ struct scsi_cmnd *sc = fsp->cmd;
+ struct fc_lport *lp = fsp->lp;
+ struct fcoe_dev_stats *stats;
+ struct fc_frame_header *fh;
+ size_t start_offset;
+ size_t offset;
+ u32 crc;
+ u32 copy_len = 0;
+ size_t len;
+ void *buf;
+ struct scatterlist *sg;
+ size_t remaining;
+
+ fh = fc_frame_header_get(fp);
+ offset = ntohl(fh->fh_parm_offset);
+ start_offset = offset;
+ len = fr_len(fp) - sizeof(*fh);
+ buf = fc_frame_payload_get(fp, 0);
+
+ if (offset + len > fsp->data_len) {
+ /*
+ * this should never happen
+ */
+ if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
+ fc_frame_crc_check(fp))
+ goto crc_err;
+ FC_DEBUG_FCP("data received past end. len %zx offset %zx "
+ "data_len %x\n", len, offset, fsp->data_len);
+ fc_fcp_retry_cmd(fsp);
+ return;
+ }
+ if (offset != fsp->xfer_len)
+ fsp->state |= FC_SRB_DISCONTIG;
+
+ crc = 0;
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
+ crc = crc32(~0, (u8 *) fh, sizeof(*fh));
+
+ sg = scsi_sglist(sc);
+ remaining = len;
+
+ while (remaining > 0 && sg) {
+ size_t off;
+ void *page_addr;
+ size_t sg_bytes;
+
+ if (offset >= sg->length) {
+ offset -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+ sg_bytes = min(remaining, sg->length - offset);
+
+ /*
+ * The scatterlist item may be bigger than PAGE_SIZE,
+ * but we are limited to mapping PAGE_SIZE at a time.
+ */
+ off = offset + sg->offset;
+ sg_bytes = min(sg_bytes, (size_t)
+ (PAGE_SIZE - (off & ~PAGE_MASK)));
+ page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ if (!page_addr)
+ break; /* XXX panic? */
+
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
+ crc = crc32(crc, buf, sg_bytes);
+ memcpy((char *)page_addr + (off & ~PAGE_MASK), buf,
+ sg_bytes);
+
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ buf += sg_bytes;
+ offset += sg_bytes;
+ remaining -= sg_bytes;
+ copy_len += sg_bytes;
+ }
+
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ buf = fc_frame_payload_get(fp, 0);
+ if (len % 4) {
+ crc = crc32(crc, buf + len, 4 - (len % 4));
+ len += 4 - (len % 4);
+ }
+
+ if (~crc != le32_to_cpu(fr_crc(fp))) {
+crc_err:
+ stats = lp->dev_stats[smp_processor_id()];
+ stats->ErrorFrames++;
+ if (stats->InvalidCRCCount++ < 5)
+ FC_DBG("CRC error on data frame\n");
+ /*
+ * Assume the frame is total garbage.
+ * We may have copied it over the good part
+ * of the buffer.
+ * If so, we need to retry the entire operation.
+ * Otherwise, ignore it.
+ */
+ if (fsp->state & FC_SRB_DISCONTIG)
+ fc_fcp_retry_cmd(fsp);
+ return;
+ }
+ }
+
+ if (fsp->xfer_contig_end == start_offset)
+ fsp->xfer_contig_end += copy_len;
+ fsp->xfer_len += copy_len;
+
+ /*
+ * In the very rare event that this data arrived after the response
+ * and completes the transfer, call the completion handler.
+ */
+ if (unlikely(fsp->state & FC_SRB_RCV_STATUS) &&
+ fsp->xfer_len == fsp->data_len - fsp->scsi_resid)
+ fc_fcp_complete_locked(fsp);
+}
+
+/*
+ * fc_fcp_send_data - Send SCSI data to target.
+ * @fsp: ptr to fc_fcp_pkt
+ * @sp: ptr to this sequence
+ * @offset: starting offset for this data request
+ * @seq_blen: the burst length for this data request
+ *
+ * Called after receiving a Transfer Ready data descriptor.
+ * if LLD is capable of seq offload then send down seq_blen
+ * size of data in single frame, otherwise send multiple FC
+ * frames of max FC frame payload supported by target port.
+ *
+ * Returns : 0 for success.
+ */
+static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
+ size_t offset, size_t seq_blen)
+{
+ struct fc_exch *ep;
+ struct scsi_cmnd *sc;
+ struct scatterlist *sg;
+ struct fc_frame *fp = NULL;
+ struct fc_lport *lp = fsp->lp;
+ size_t remaining;
+ size_t t_blen;
+ size_t tlen;
+ size_t sg_bytes;
+ size_t frame_offset, fh_parm_offset;
+ int error;
+ void *data = NULL;
+ void *page_addr;
+ int using_sg = lp->sg_supp;
+ u32 f_ctl;
+
+ WARN_ON(seq_blen <= 0);
+ if (unlikely(offset + seq_blen > fsp->data_len)) {
+ /* this should never happen */
+ FC_DEBUG_FCP("xfer-ready past end. seq_blen %zx offset %zx\n",
+ seq_blen, offset);
+ fc_fcp_send_abort(fsp);
+ return 0;
+ } else if (offset != fsp->xfer_len) {
+ /* Out of Order Data Request - no problem, but unexpected. */
+ FC_DEBUG_FCP("xfer-ready non-contiguous. "
+ "seq_blen %zx offset %zx\n", seq_blen, offset);
+ }
+
+ /*
+ * if LLD is capable of seq_offload then set transport
+ * burst length (t_blen) to seq_blen, otherwise set t_blen
+ * to max FC frame payload previously set in fsp->max_payload.
+ */
+ t_blen = lp->seq_offload ? seq_blen : fsp->max_payload;
+ WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
+ if (t_blen > 512)
+ t_blen &= ~(512 - 1); /* round down to block size */
+ WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD); /* won't go below 256 */
+ sc = fsp->cmd;
+
+ remaining = seq_blen;
+ fh_parm_offset = frame_offset = offset;
+ tlen = 0;
+ seq = lp->tt.seq_start_next(seq);
+ f_ctl = FC_FC_REL_OFF;
+ WARN_ON(!seq);
+
+ /*
+ * If a get_page()/put_page() will fail, don't use sg lists
+ * in the fc_frame structure.
+ *
+ * The put_page() may be long after the I/O has completed
+ * in the case of FCoE, since the network driver does it
+ * via free_skb(). See the test in free_pages_check().
+ *
+ * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'.
+ */
+ if (using_sg) {
+ for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) {
+ if (page_count(sg_page(sg)) == 0 ||
+ (sg_page(sg)->flags & (1 << PG_lru |
+ 1 << PG_private |
+ 1 << PG_locked |
+ 1 << PG_active |
+ 1 << PG_slab |
+ 1 << PG_swapcache |
+ 1 << PG_writeback |
+ 1 << PG_reserved |
+ 1 << PG_buddy))) {
+ using_sg = 0;
+ break;
+ }
+ }
+ }
+ sg = scsi_sglist(sc);
+
+ while (remaining > 0 && sg) {
+ if (offset >= sg->length) {
+ offset -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+ if (!fp) {
+ tlen = min(t_blen, remaining);
+
+ /*
+ * TODO. Temporary workaround. fc_seq_send() can't
+ * handle odd lengths in non-linear skbs.
+ * This will be the final fragment only.
+ */
+ if (tlen % 4)
+ using_sg = 0;
+ if (using_sg) {
+ fp = _fc_frame_alloc(lp, 0);
+ if (!fp)
+ return -ENOMEM;
+ } else {
+ fp = fc_frame_alloc(lp, tlen);
+ if (!fp)
+ return -ENOMEM;
+
+ data = (void *)(fr_hdr(fp)) +
+ sizeof(struct fc_frame_header);
+ }
+ fh_parm_offset = frame_offset;
+ fr_max_payload(fp) = fsp->max_payload;
+ }
+ sg_bytes = min(tlen, sg->length - offset);
+ if (using_sg) {
+ WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags >
+ FC_FRAME_SG_LEN);
+ get_page(sg_page(sg));
+ skb_fill_page_desc(fp_skb(fp),
+ skb_shinfo(fp_skb(fp))->nr_frags,
+ sg_page(sg), sg->offset + offset,
+ sg_bytes);
+ fp_skb(fp)->data_len += sg_bytes;
+ fr_len(fp) += sg_bytes;
+ fp_skb(fp)->truesize += PAGE_SIZE;
+ } else {
+ size_t off = offset + sg->offset;
+
+ /*
+ * The scatterlist item may be bigger than PAGE_SIZE,
+ * but we must not cross pages inside the kmap.
+ */
+ sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE -
+ (off & ~PAGE_MASK)));
+ page_addr = kmap_atomic(sg_page(sg) +
+ (off >> PAGE_SHIFT),
+ KM_SOFTIRQ0);
+ memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
+ sg_bytes);
+ kunmap_atomic(page_addr, KM_SOFTIRQ0);
+ data += sg_bytes;
+ }
+ offset += sg_bytes;
+ frame_offset += sg_bytes;
+ tlen -= sg_bytes;
+ remaining -= sg_bytes;
+
+ if (tlen)
+ continue;
+
+ /*
+ * Send sequence with transfer sequence initiative in case
+ * this is last FCP frame of the sequence.
+ */
+ if (remaining == 0)
+ f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ;
+
+ ep = fc_seq_exch(seq);
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, fh_parm_offset);
+
+ /*
+ * send fragment using for a sequence.
+ */
+ error = lp->tt.seq_send(lp, seq, fp);
+ if (error) {
+ WARN_ON(1); /* send error should be rare */
+ fc_fcp_retry_cmd(fsp);
+ return 0;
+ }
+ fp = NULL;
+ }
+ fsp->xfer_len += seq_blen; /* premature count? */
+ return 0;
+}
+
+static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int ba_done = 1;
+ struct fc_ba_rjt *brp;
+ struct fc_frame_header *fh;
+
+ fh = fc_frame_header_get(fp);
+ switch (fh->fh_r_ctl) {
+ case FC_RCTL_BA_ACC:
+ break;
+ case FC_RCTL_BA_RJT:
+ brp = fc_frame_payload_get(fp, sizeof(*brp));
+ if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR)
+ break;
+ /* fall thru */
+ default:
+ /*
+ * we will let the command timeout
+ * and scsi-ml recover in this case,
+ * therefore cleared the ba_done flag.
+ */
+ ba_done = 0;
+ }
+
+ if (ba_done) {
+ fsp->state |= FC_SRB_ABORTED;
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+
+ if (fsp->wait_for_comp)
+ complete(&fsp->tm_done);
+ else
+ fc_fcp_complete_locked(fsp);
+ }
+}
+
+/*
+ * fc_fcp_reduce_can_queue - drop can_queue
+ * @lp: lport to drop queueing for
+ *
+ * If we are getting memory allocation failures, then we may
+ * be trying to execute too many commands. We let the running
+ * commands complete or timeout, then try again with a reduced
+ * can_queue. Eventually we will hit the point where we run
+ * on all reserved structs.
+ */
+static void fc_fcp_reduce_can_queue(struct fc_lport *lp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ unsigned long flags;
+ int can_queue;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ if (si->throttled)
+ goto done;
+ si->throttled = 1;
+
+ can_queue = lp->host->can_queue;
+ can_queue >>= 1;
+ if (!can_queue)
+ can_queue = 1;
+ lp->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lp->host, "Could not allocate frame.\n"
+ "Reducing can_queue to %d.\n", can_queue);
+done:
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+}
+
+/*
+ * exch mgr calls this routine to process scsi
+ * exchanges.
+ *
+ * Return : None
+ * Context : called from Soft IRQ context
+ * can not called holding list lock
+ */
+static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
+ struct fc_lport *lp;
+ struct fc_frame_header *fh;
+ struct fcp_txrdy *dd;
+ u8 r_ctl;
+ int rc = 0;
+
+ if (IS_ERR(fp))
+ goto errout;
+
+ fh = fc_frame_header_get(fp);
+ r_ctl = fh->fh_r_ctl;
+ lp = fsp->lp;
+
+ if (!(lp->state & LPORT_ST_READY))
+ goto out;
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+ fsp->last_pkt_time = jiffies;
+
+ if (fh->fh_type == FC_TYPE_BLS) {
+ fc_fcp_abts_resp(fsp, fp);
+ goto unlock;
+ }
+
+ if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING))
+ goto unlock;
+
+ if (r_ctl == FC_RCTL_DD_DATA_DESC) {
+ /*
+ * received XFER RDY from the target
+ * need to send data to the target
+ */
+ WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
+ dd = fc_frame_payload_get(fp, sizeof(*dd));
+ WARN_ON(!dd);
+
+ rc = fc_fcp_send_data(fsp, seq,
+ (size_t) ntohl(dd->ft_data_ro),
+ (size_t) ntohl(dd->ft_burst_len));
+ if (!rc)
+ seq->rec_data = fsp->xfer_len;
+ else if (rc == -ENOMEM)
+ fsp->state |= FC_SRB_NOMEM;
+ } else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
+ /*
+ * received a DATA frame
+ * next we will copy the data to the system buffer
+ */
+ WARN_ON(fr_len(fp) < sizeof(*fh)); /* len may be 0 */
+ fc_fcp_recv_data(fsp, fp);
+ seq->rec_data = fsp->xfer_contig_end;
+ } else if (r_ctl == FC_RCTL_DD_CMD_STATUS) {
+ WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
+
+ fc_fcp_resp(fsp, fp);
+ } else {
+ FC_DBG("unexpected frame. r_ctl %x\n", r_ctl);
+ }
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_frame_free(fp);
+errout:
+ if (IS_ERR(fp))
+ fc_fcp_error(fsp, fp);
+ else if (rc == -ENOMEM)
+ fc_fcp_reduce_can_queue(lp);
+}
+
+static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fcp_resp *fc_rp;
+ struct fcp_resp_ext *rp_ex;
+ struct fcp_resp_rsp_info *fc_rp_info;
+ u32 plen;
+ u32 expected_len;
+ u32 respl = 0;
+ u32 snsl = 0;
+ u8 flags = 0;
+
+ plen = fr_len(fp);
+ fh = (struct fc_frame_header *)fr_hdr(fp);
+ if (unlikely(plen < sizeof(*fh) + sizeof(*fc_rp)))
+ goto len_err;
+ plen -= sizeof(*fh);
+ fc_rp = (struct fcp_resp *)(fh + 1);
+ fsp->cdb_status = fc_rp->fr_status;
+ flags = fc_rp->fr_flags;
+ fsp->scsi_comp_flags = flags;
+ expected_len = fsp->data_len;
+
+ if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) {
+ rp_ex = (void *)(fc_rp + 1);
+ if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) {
+ if (plen < sizeof(*fc_rp) + sizeof(*rp_ex))
+ goto len_err;
+ fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1);
+ if (flags & FCP_RSP_LEN_VAL) {
+ respl = ntohl(rp_ex->fr_rsp_len);
+ if (respl != sizeof(*fc_rp_info))
+ goto len_err;
+ if (fsp->wait_for_comp) {
+ /* Abuse cdb_status for rsp code */
+ fsp->cdb_status = fc_rp_info->rsp_code;
+ complete(&fsp->tm_done);
+ /*
+ * tmfs will not have any scsi cmd so
+ * exit here
+ */
+ return;
+ } else
+ goto err;
+ }
+ if (flags & FCP_SNS_LEN_VAL) {
+ snsl = ntohl(rp_ex->fr_sns_len);
+ if (snsl > SCSI_SENSE_BUFFERSIZE)
+ snsl = SCSI_SENSE_BUFFERSIZE;
+ memcpy(fsp->cmd->sense_buffer,
+ (char *)fc_rp_info + respl, snsl);
+ }
+ }
+ if (flags & (FCP_RESID_UNDER | FCP_RESID_OVER)) {
+ if (plen < sizeof(*fc_rp) + sizeof(rp_ex->fr_resid))
+ goto len_err;
+ if (flags & FCP_RESID_UNDER) {
+ fsp->scsi_resid = ntohl(rp_ex->fr_resid);
+ /*
+ * The cmnd->underflow is the minimum number of
+ * bytes that must be transfered for this
+ * command. Provided a sense condition is not
+ * present, make sure the actual amount
+ * transferred is at least the underflow value
+ * or fail.
+ */
+ if (!(flags & FCP_SNS_LEN_VAL) &&
+ (fc_rp->fr_status == 0) &&
+ (scsi_bufflen(fsp->cmd) -
+ fsp->scsi_resid) < fsp->cmd->underflow)
+ goto err;
+ expected_len -= fsp->scsi_resid;
+ } else {
+ fsp->status_code = FC_ERROR;
+ }
+ }
+ }
+ fsp->state |= FC_SRB_RCV_STATUS;
+
+ /*
+ * Check for missing or extra data frames.
+ */
+ if (unlikely(fsp->xfer_len != expected_len)) {
+ if (fsp->xfer_len < expected_len) {
+ /*
+ * Some data may be queued locally,
+ * Wait a at least one jiffy to see if it is delivered.
+ * If this expires without data, we may do SRR.
+ */
+ fc_fcp_timer_set(fsp, 2);
+ return;
+ }
+ fsp->status_code = FC_DATA_OVRRUN;
+ FC_DBG("tgt %6x xfer len %zx greater than expected len %x. "
+ "data len %x\n",
+ fsp->rport->port_id,
+ fsp->xfer_len, expected_len, fsp->data_len);
+ }
+ fc_fcp_complete_locked(fsp);
+ return;
+
+len_err:
+ FC_DBG("short FCP response. flags 0x%x len %u respl %u snsl %u\n",
+ flags, fr_len(fp), respl, snsl);
+err:
+ fsp->status_code = FC_ERROR;
+ fc_fcp_complete_locked(fsp);
+}
+
+/**
+ * fc_fcp_complete_locked - complete processing of a fcp packet
+ * @fsp: fcp packet
+ *
+ * This function may sleep if a timer is pending. The packet lock must be
+ * held, and the host lock must not be held.
+ */
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
+{
+ struct fc_lport *lp = fsp->lp;
+ struct fc_seq *seq;
+ struct fc_exch *ep;
+ u32 f_ctl;
+
+ if (fsp->state & FC_SRB_ABORT_PENDING)
+ return;
+
+ if (fsp->state & FC_SRB_ABORTED) {
+ if (!fsp->status_code)
+ fsp->status_code = FC_CMD_ABORTED;
+ } else {
+ /*
+ * Test for transport underrun, independent of response
+ * underrun status.
+ */
+ if (fsp->xfer_len < fsp->data_len && !fsp->io_status &&
+ (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) ||
+ fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) {
+ fsp->status_code = FC_DATA_UNDRUN;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ }
+ }
+
+ seq = fsp->seq_ptr;
+ if (seq) {
+ fsp->seq_ptr = NULL;
+ if (unlikely(fsp->scsi_comp_flags & FCP_CONF_REQ)) {
+ struct fc_frame *conf_frame;
+ struct fc_seq *csp;
+
+ csp = lp->tt.seq_start_next(seq);
+ conf_frame = fc_frame_alloc(fsp->lp, 0);
+ if (conf_frame) {
+ f_ctl = FC_FC_SEQ_INIT;
+ f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ ep = fc_seq_exch(seq);
+ fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
+ ep->did, ep->sid,
+ FC_TYPE_FCP, f_ctl, 0);
+ lp->tt.seq_send(lp, csp, conf_frame);
+ }
+ }
+ lp->tt.exch_done(seq);
+ }
+ fc_io_compl(fsp);
+}
+
+static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
+{
+ struct fc_lport *lp = fsp->lp;
+
+ if (fsp->seq_ptr) {
+ lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+ fsp->status_code = error;
+}
+
+/**
+ * fc_fcp_cleanup_each_cmd - run fn on each active command
+ * @lp: logical port
+ * @id: target id
+ * @lun: lun
+ * @error: fsp status code
+ *
+ * If lun or id is -1, they are ignored.
+ */
+static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
+ unsigned int lun, int error)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_pkt *fsp;
+ struct scsi_cmnd *sc_cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+restart:
+ list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
+ sc_cmd = fsp->cmd;
+ if (id != -1 && scmd_id(sc_cmd) != id)
+ continue;
+
+ if (lun != -1 && sc_cmd->device->lun != lun)
+ continue;
+
+ fc_fcp_pkt_hold(fsp);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ if (!fc_fcp_lock_pkt(fsp)) {
+ fc_fcp_cleanup_cmd(fsp, error);
+ fc_io_compl(fsp);
+ fc_fcp_unlock_pkt(fsp);
+ }
+
+ fc_fcp_pkt_release(fsp);
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ /*
+ * while we dropped the lock multiple pkts could
+ * have been released, so we have to start over.
+ */
+ goto restart;
+ }
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+}
+
+static void fc_fcp_abort_io(struct fc_lport *lp)
+{
+ fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
+}
+
+/**
+ * fc_fcp_pkt_send - send a fcp packet to the lower level.
+ * @lp: fc lport
+ * @fsp: fc packet.
+ *
+ * This is called by upper layer protocol.
+ * Return : zero for success and -1 for failure
+ * Context : called from queuecommand which can be called from process
+ * or scsi soft irq.
+ * Locks : called with the host lock and irqs disabled.
+ */
+static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ int rc;
+
+ fsp->cmd->SCp.ptr = (char *)fsp;
+ fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
+ fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK;
+
+ int_to_scsilun(fsp->cmd->device->lun,
+ (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
+ list_add_tail(&fsp->list, &si->scsi_pkt_queue);
+
+ spin_unlock_irq(lp->host->host_lock);
+ rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
+ spin_lock_irq(lp->host->host_lock);
+ if (rc)
+ list_del(&fsp->list);
+
+ return rc;
+}
+
+static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg))
+{
+ struct fc_frame *fp;
+ struct fc_seq *seq;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+ const size_t len = sizeof(fsp->cdb_cmd);
+ int rc = 0;
+
+ if (fc_fcp_lock_pkt(fsp))
+ return 0;
+
+ fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
+ if (!fp) {
+ rc = -1;
+ goto unlock;
+ }
+
+ memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
+ fr_cmd(fp) = fsp->cmd;
+ rport = fsp->rport;
+ fsp->max_payload = rport->maxframe_size;
+ rp = rport->dd_data;
+
+ fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
+ if (!seq) {
+ fc_frame_free(fp);
+ rc = -1;
+ goto unlock;
+ }
+ fsp->last_pkt_time = jiffies;
+ fsp->seq_ptr = seq;
+ fc_fcp_pkt_hold(fsp); /* hold for fc_fcp_pkt_destroy */
+
+ setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
+ fc_fcp_timer_set(fsp,
+ (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
+ FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+ return rc;
+}
+
+/*
+ * transport error handler
+ */
+static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int error = PTR_ERR(fp);
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ switch (error) {
+ case -FC_EX_CLOSED:
+ fc_fcp_retry_cmd(fsp);
+ goto unlock;
+ default:
+ FC_DBG("unknown error %ld\n", PTR_ERR(fp));
+ }
+ /*
+ * clear abort pending, because the lower layer
+ * decided to force completion.
+ */
+ fsp->state &= ~FC_SRB_ABORT_PENDING;
+ fsp->status_code = FC_CMD_PLOGO;
+ fc_fcp_complete_locked(fsp);
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+}
+
+/*
+ * Scsi abort handler- calls to send an abort
+ * and then wait for abort completion
+ */
+static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+{
+ int rc = FAILED;
+
+ if (fc_fcp_send_abort(fsp))
+ return FAILED;
+
+ init_completion(&fsp->tm_done);
+ fsp->wait_for_comp = 1;
+
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ fsp->wait_for_comp = 0;
+
+ if (!rc) {
+ FC_DBG("target abort cmd failed\n");
+ rc = FAILED;
+ } else if (fsp->state & FC_SRB_ABORTED) {
+ FC_DBG("target abort cmd passed\n");
+ rc = SUCCESS;
+ fc_fcp_complete_locked(fsp);
+ }
+
+ return rc;
+}
+
+/*
+ * Retry LUN reset after resource allocation failed.
+ */
+static void fc_lun_reset_send(unsigned long data)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_lport *lp = fsp->lp;
+ if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
+ if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
+ return;
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+ setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ fc_fcp_unlock_pkt(fsp);
+ }
+}
+
+/*
+ * Scsi device reset handler- send a LUN RESET to the device
+ * and wait for reset reply
+ */
+static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+ unsigned int id, unsigned int lun)
+{
+ int rc;
+
+ fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
+ fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET;
+ int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+
+ fsp->wait_for_comp = 1;
+ init_completion(&fsp->tm_done);
+
+ fc_lun_reset_send((unsigned long)fsp);
+
+ /*
+ * wait for completion of reset
+ * after that make sure all commands are terminated
+ */
+ rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV);
+
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ fsp->state |= FC_SRB_COMPL;
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+
+ del_timer_sync(&fsp->timer);
+
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ if (fsp->seq_ptr) {
+ lp->tt.exch_done(fsp->seq_ptr);
+ fsp->seq_ptr = NULL;
+ }
+ fsp->wait_for_comp = 0;
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+
+ if (!rc) {
+ FC_DBG("lun reset failed\n");
+ return FAILED;
+ }
+
+ /* cdb_status holds the tmf's rsp code */
+ if (fsp->cdb_status != FCP_TMF_CMPL)
+ return FAILED;
+
+ FC_DBG("lun reset to lun %u completed\n", lun);
+ fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
+ return SUCCESS;
+}
+
+/*
+ * Task Managment response handler
+ */
+static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = arg;
+ struct fc_frame_header *fh;
+
+ if (IS_ERR(fp)) {
+ /*
+ * If there is an error just let it timeout or wait
+ * for TMF to be aborted if it timedout.
+ *
+ * scsi-eh will escalate for when either happens.
+ */
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ /*
+ * raced with eh timeout handler.
+ */
+ if (!fsp->seq_ptr || !fsp->wait_for_comp) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ return;
+ }
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_type != FC_TYPE_BLS)
+ fc_fcp_resp(fsp, fp);
+ fsp->seq_ptr = NULL;
+ fsp->lp->tt.exch_done(seq);
+ fc_frame_free(fp);
+ fc_fcp_unlock_pkt(fsp);
+}
+
+static void fc_fcp_cleanup(struct fc_lport *lp)
+{
+ fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
+}
+
+/*
+ * fc_fcp_timeout: called by OS timer function.
+ *
+ * The timer has been inactivated and must be reactivated if desired
+ * using fc_fcp_timer_set().
+ *
+ * Algorithm:
+ *
+ * If REC is supported, just issue it, and return. The REC exchange will
+ * complete or time out, and recovery can continue at that point.
+ *
+ * Otherwise, if the response has been received without all the data,
+ * it has been ER_TIMEOUT since the response was received.
+ *
+ * If the response has not been received,
+ * we see if data was received recently. If it has been, we continue waiting,
+ * otherwise, we abort the command.
+ */
+static void fc_fcp_timeout(unsigned long data)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
+ struct fc_rport *rport = fsp->rport;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ if (fsp->cdb_cmd.fc_tm_flags)
+ goto unlock;
+
+ fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
+
+ if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
+ fc_fcp_rec(fsp);
+ else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
+ jiffies))
+ fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
+ else if (fsp->state & FC_SRB_RCV_STATUS)
+ fc_fcp_complete_locked(fsp);
+ else
+ fc_timeout_error(fsp);
+ fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;
+unlock:
+ fc_fcp_unlock_pkt(fsp);
+}
+
+/*
+ * Send a REC ELS request
+ */
+static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
+{
+ struct fc_lport *lp;
+ struct fc_frame *fp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+
+ lp = fsp->lp;
+ rport = fsp->rport;
+ rp = rport->dd_data;
+ if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
+ fsp->status_code = FC_HRD_ERROR;
+ fsp->io_status = SUGGEST_RETRY << 24;
+ fc_fcp_complete_locked(fsp);
+ return;
+ }
+ fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
+ if (!fp)
+ goto retry;
+
+ fr_seq(fp) = fsp->seq_ptr;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+ if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+ fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
+ return;
+ }
+ fc_frame_free(fp);
+retry:
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ else
+ fc_timeout_error(fsp);
+}
+
+/*
+ * Receive handler for REC ELS frame
+ * if it is a reject then let the scsi layer to handle
+ * the timeout. if it is a LS_ACC then if the io was not completed
+ * then set the timeout and return otherwise complete the exchange
+ * and tell the scsi layer to restart the I/O.
+ */
+static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
+ struct fc_els_rec_acc *recp;
+ struct fc_els_ls_rjt *rjt;
+ u32 e_stat;
+ u8 opcode;
+ u32 offset;
+ enum dma_data_direction data_dir;
+ enum fc_rctl r_ctl;
+ struct fc_rport_libfc_priv *rp;
+
+ if (IS_ERR(fp)) {
+ fc_fcp_rec_error(fsp, fp);
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ fsp->recov_retry = 0;
+ opcode = fc_frame_payload_op(fp);
+ if (opcode == ELS_LS_RJT) {
+ rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+ switch (rjt->er_reason) {
+ default:
+ FC_DEBUG_FCP("device %x unexpected REC reject "
+ "reason %d expl %d\n",
+ fsp->rport->port_id, rjt->er_reason,
+ rjt->er_explan);
+ /* fall through */
+ case ELS_RJT_UNSUP:
+ FC_DEBUG_FCP("device does not support REC\n");
+ rp = fsp->rport->dd_data;
+ /*
+ * if we do not spport RECs or got some bogus
+ * reason then resetup timer so we check for
+ * making progress.
+ */
+ rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
+ fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
+ break;
+ case ELS_RJT_LOGIC:
+ case ELS_RJT_UNAB:
+ /*
+ * If no data transfer, the command frame got dropped
+ * so we just retry. If data was transferred, we
+ * lost the response but the target has no record,
+ * so we abort and retry.
+ */
+ if (rjt->er_explan == ELS_EXPL_OXID_RXID &&
+ fsp->xfer_len == 0) {
+ fc_fcp_retry_cmd(fsp);
+ break;
+ }
+ fc_timeout_error(fsp);
+ break;
+ }
+ } else if (opcode == ELS_LS_ACC) {
+ if (fsp->state & FC_SRB_ABORTED)
+ goto unlock_out;
+
+ data_dir = fsp->cmd->sc_data_direction;
+ recp = fc_frame_payload_get(fp, sizeof(*recp));
+ offset = ntohl(recp->reca_fc4value);
+ e_stat = ntohl(recp->reca_e_stat);
+
+ if (e_stat & ESB_ST_COMPLETE) {
+
+ /*
+ * The exchange is complete.
+ *
+ * For output, we must've lost the response.
+ * For input, all data must've been sent.
+ * We lost may have lost the response
+ * (and a confirmation was requested) and maybe
+ * some data.
+ *
+ * If all data received, send SRR
+ * asking for response. If partial data received,
+ * or gaps, SRR requests data at start of gap.
+ * Recovery via SRR relies on in-order-delivery.
+ */
+ if (data_dir == DMA_TO_DEVICE) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else if (fsp->xfer_contig_end == offset) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else {
+ offset = fsp->xfer_contig_end;
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ }
+ fc_fcp_srr(fsp, r_ctl, offset);
+ } else if (e_stat & ESB_ST_SEQ_INIT) {
+
+ /*
+ * The remote port has the initiative, so just
+ * keep waiting for it to complete.
+ */
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ } else {
+
+ /*
+ * The exchange is incomplete, we have seq. initiative.
+ * Lost response with requested confirmation,
+ * lost confirmation, lost transfer ready or
+ * lost write data.
+ *
+ * For output, if not all data was received, ask
+ * for transfer ready to be repeated.
+ *
+ * If we received or sent all the data, send SRR to
+ * request response.
+ *
+ * If we lost a response, we may have lost some read
+ * data as well.
+ */
+ r_ctl = FC_RCTL_DD_SOL_DATA;
+ if (data_dir == DMA_TO_DEVICE) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ if (offset < fsp->data_len)
+ r_ctl = FC_RCTL_DD_DATA_DESC;
+ } else if (offset == fsp->xfer_contig_end) {
+ r_ctl = FC_RCTL_DD_CMD_STATUS;
+ } else if (fsp->xfer_contig_end < offset) {
+ offset = fsp->xfer_contig_end;
+ }
+ fc_fcp_srr(fsp, r_ctl, offset);
+ }
+ }
+unlock_out:
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */
+ fc_frame_free(fp);
+}
+
+/*
+ * Handle error response or timeout for REC exchange.
+ */
+static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ int error = PTR_ERR(fp);
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ switch (error) {
+ case -FC_EX_CLOSED:
+ fc_fcp_retry_cmd(fsp);
+ break;
+
+ default:
+ FC_DBG("REC %p fid %x error unexpected error %d\n",
+ fsp, fsp->rport->port_id, error);
+ fsp->status_code = FC_CMD_PLOGO;
+ /* fall through */
+
+ case -FC_EX_TIMEOUT:
+ /*
+ * Assume REC or LS_ACC was lost.
+ * The exchange manager will have aborted REC, so retry.
+ */
+ FC_DBG("REC fid %x error error %d retry %d/%d\n",
+ fsp->rport->port_id, error, fsp->recov_retry,
+ FC_MAX_RECOV_RETRY);
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_rec(fsp);
+ else
+ fc_timeout_error(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */
+}
+
+/*
+ * Time out error routine:
+ * abort's the I/O close the exchange and
+ * send completion notification to scsi layer
+ */
+static void fc_timeout_error(struct fc_fcp_pkt *fsp)
+{
+ fsp->status_code = FC_CMD_TIME_OUT;
+ fsp->cdb_status = 0;
+ fsp->io_status = 0;
+ /*
+ * if this fails then we let the scsi command timer fire and
+ * scsi-ml escalate.
+ */
+ fc_fcp_send_abort(fsp);
+}
+
+/*
+ * Sequence retransmission request.
+ * This is called after receiving status but insufficient data, or
+ * when expecting status but the request has timed out.
+ */
+static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
+{
+ struct fc_lport *lp = fsp->lp;
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rp;
+ struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
+ struct fc_seq *seq;
+ struct fcp_srr *srr;
+ struct fc_frame *fp;
+ u8 cdb_op;
+
+ rport = fsp->rport;
+ rp = rport->dd_data;
+ cdb_op = fsp->cdb_cmd.fc_cdb[0];
+
+ if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
+ goto retry; /* shouldn't happen */
+ fp = fc_frame_alloc(lp, sizeof(*srr));
+ if (!fp)
+ goto retry;
+
+ srr = fc_frame_payload_get(fp, sizeof(*srr));
+ memset(srr, 0, sizeof(*srr));
+ srr->srr_op = ELS_SRR;
+ srr->srr_ox_id = htons(ep->oxid);
+ srr->srr_rx_id = htons(ep->rxid);
+ srr->srr_r_ctl = r_ctl;
+ srr->srr_rel_off = htonl(offset);
+
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
+ fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+ seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+ if (!seq) {
+ fc_frame_free(fp);
+ goto retry;
+ }
+ fsp->recov_seq = seq;
+ fsp->xfer_len = offset;
+ fsp->xfer_contig_end = offset;
+ fsp->state &= ~FC_SRB_RCV_STATUS;
+ fc_fcp_pkt_hold(fsp); /* hold for outstanding SRR */
+ return;
+retry:
+ fc_fcp_retry_cmd(fsp);
+}
+
+/*
+ * Handle response from SRR.
+ */
+static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_fcp_pkt *fsp = arg;
+ struct fc_frame_header *fh;
+
+ if (IS_ERR(fp)) {
+ fc_fcp_srr_error(fsp, fp);
+ return;
+ }
+
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+
+ fh = fc_frame_header_get(fp);
+ /*
+ * BUG? fc_fcp_srr_error calls exch_done which would release
+ * the ep. But if fc_fcp_srr_error had got -FC_EX_TIMEOUT,
+ * then fc_exch_timeout would be sending an abort. The exch_done
+ * call by fc_fcp_srr_error would prevent fc_exch.c from seeing
+ * an abort response though.
+ */
+ if (fh->fh_type == FC_TYPE_BLS) {
+ fc_fcp_unlock_pkt(fsp);
+ return;
+ }
+
+ fsp->recov_seq = NULL;
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ fsp->recov_retry = 0;
+ fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+ break;
+ case ELS_LS_RJT:
+ default:
+ fc_timeout_error(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+ fsp->lp->tt.exch_done(seq);
+out:
+ fc_frame_free(fp);
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
+}
+
+static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
+{
+ if (fc_fcp_lock_pkt(fsp))
+ goto out;
+ fsp->lp->tt.exch_done(fsp->recov_seq);
+ fsp->recov_seq = NULL;
+ switch (PTR_ERR(fp)) {
+ case -FC_EX_TIMEOUT:
+ if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
+ fc_fcp_rec(fsp);
+ else
+ fc_timeout_error(fsp);
+ break;
+ case -FC_EX_CLOSED: /* e.g., link failure */
+ /* fall through */
+ default:
+ fc_fcp_retry_cmd(fsp);
+ break;
+ }
+ fc_fcp_unlock_pkt(fsp);
+out:
+ fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
+}
+
+static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
+{
+ /* lock ? */
+ return (lp->state == LPORT_ST_READY) && (lp->link_status & FC_LINK_UP);
+}
+
+/**
+ * fc_queuecommand - The queuecommand function of the scsi template
+ * @cmd: struct scsi_cmnd to be executed
+ * @done: Callback function to be called when cmd is completed
+ *
+ * this is the i/o strategy routine, called by the scsi layer
+ * this routine is called with holding the host_lock.
+ */
+int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct fc_lport *lp;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ struct fc_fcp_pkt *fsp;
+ struct fc_rport_libfc_priv *rp;
+ int rval;
+ int rc = 0;
+ struct fcoe_dev_stats *stats;
+
+ lp = shost_priv(sc_cmd->device->host);
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval) {
+ sc_cmd->result = rval;
+ done(sc_cmd);
+ goto out;
+ }
+
+ if (!*(struct fc_remote_port **)rport->dd_data) {
+ /*
+ * rport is transitioning from blocked/deleted to
+ * online
+ */
+ sc_cmd->result = DID_IMM_RETRY << 16;
+ done(sc_cmd);
+ goto out;
+ }
+
+ rp = rport->dd_data;
+
+ if (!fc_fcp_lport_queue_ready(lp)) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+ if (fsp == NULL) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ /*
+ * build the libfc request pkt
+ */
+ fsp->cmd = sc_cmd; /* save the cmd */
+ fsp->lp = lp; /* save the softc ptr */
+ fsp->rport = rport; /* set the remote port ptr */
+ sc_cmd->scsi_done = done;
+
+ /*
+ * set up the transfer length
+ */
+ fsp->data_len = scsi_bufflen(sc_cmd);
+ fsp->xfer_len = 0;
+
+ /*
+ * setup the data direction
+ */
+ stats = lp->dev_stats[smp_processor_id()];
+ if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ fsp->req_flags = FC_SRB_READ;
+ stats->InputRequests++;
+ stats->InputMegabytes = fsp->data_len;
+ } else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
+ fsp->req_flags = FC_SRB_WRITE;
+ stats->OutputRequests++;
+ stats->OutputMegabytes = fsp->data_len;
+ } else {
+ fsp->req_flags = 0;
+ stats->ControlRequests++;
+ }
+
+ fsp->tgt_flags = rp->flags;
+
+ init_timer(&fsp->timer);
+ fsp->timer.data = (unsigned long)fsp;
+
+ /*
+ * send it to the lower layer
+ * if we get -1 return then put the request in the pending
+ * queue.
+ */
+ rval = fc_fcp_pkt_send(lp, fsp);
+ if (rval != 0) {
+ fsp->state = FC_SRB_FREE;
+ fc_fcp_pkt_release(fsp);
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+out:
+ return rc;
+}
+EXPORT_SYMBOL(fc_queuecommand);
+
+/**
+ * fc_io_compl - Handle responses for completed commands
+ * @fsp: scsi packet
+ *
+ * Translates a error to a Linux SCSI error.
+ *
+ * The fcp packet lock must be held when calling.
+ */
+static void fc_io_compl(struct fc_fcp_pkt *fsp)
+{
+ struct fc_fcp_internal *si;
+ struct scsi_cmnd *sc_cmd;
+ struct fc_lport *lp;
+ unsigned long flags;
+
+ fsp->state |= FC_SRB_COMPL;
+ if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
+ spin_unlock_bh(&fsp->scsi_pkt_lock);
+ del_timer_sync(&fsp->timer);
+ spin_lock_bh(&fsp->scsi_pkt_lock);
+ }
+
+ lp = fsp->lp;
+ si = fc_get_scsi_internal(lp);
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ if (!fsp->cmd) {
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return;
+ }
+
+ /*
+ * if a command timed out while we had to try and throttle IO
+ * and it is now getting cleaned up, then we are about to
+ * try again so clear the throttled flag incase we get more
+ * time outs.
+ */
+ if (si->throttled && fsp->state & FC_SRB_NOMEM)
+ si->throttled = 0;
+
+ sc_cmd = fsp->cmd;
+ fsp->cmd = NULL;
+
+ if (!sc_cmd->SCp.ptr) {
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return;
+ }
+
+ CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status;
+ switch (fsp->status_code) {
+ case FC_COMPLETE:
+ if (fsp->cdb_status == 0) {
+ /*
+ * good I/O status
+ */
+ sc_cmd->result = DID_OK << 16;
+ if (fsp->scsi_resid)
+ CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ } else if (fsp->cdb_status == QUEUE_FULL) {
+ struct scsi_device *tmp_sdev;
+ struct scsi_device *sdev = sc_cmd->device;
+
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->id != sdev->id)
+ continue;
+
+ if (tmp_sdev->queue_depth > 1) {
+ scsi_track_queue_full(tmp_sdev,
+ tmp_sdev->
+ queue_depth - 1);
+ }
+ }
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ } else {
+ /*
+ * transport level I/O was ok but scsi
+ * has non zero status
+ */
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ }
+ break;
+ case FC_ERROR:
+ sc_cmd->result = DID_ERROR << 16;
+ break;
+ case FC_DATA_UNDRUN:
+ if (fsp->cdb_status == 0) {
+ /*
+ * scsi status is good but transport level
+ * underrun. for read it should be an error??
+ */
+ sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
+ } else {
+ /*
+ * scsi got underrun, this is an error
+ */
+ CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+ sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
+ }
+ break;
+ case FC_DATA_OVRRUN:
+ /*
+ * overrun is an error
+ */
+ sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
+ break;
+ case FC_CMD_ABORTED:
+ sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
+ break;
+ case FC_CMD_TIME_OUT:
+ sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
+ break;
+ case FC_CMD_RESET:
+ sc_cmd->result = (DID_RESET << 16);
+ break;
+ case FC_HRD_ERROR:
+ sc_cmd->result = (DID_NO_CONNECT << 16);
+ break;
+ default:
+ sc_cmd->result = (DID_ERROR << 16);
+ break;
+ }
+
+ list_del(&fsp->list);
+ sc_cmd->SCp.ptr = NULL;
+ sc_cmd->scsi_done(sc_cmd);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ /* release ref from initial allocation in queue command */
+ fc_fcp_pkt_release(fsp);
+}
+
+/**
+ * fc_fcp_complete - complete processing of a fcp packet
+ * @fsp: fcp packet
+ *
+ * This function may sleep if a fsp timer is pending.
+ * The host lock must not be held by caller.
+ */
+void fc_fcp_complete(struct fc_fcp_pkt *fsp)
+{
+ if (fc_fcp_lock_pkt(fsp))
+ return;
+
+ fc_fcp_complete_locked(fsp);
+ fc_fcp_unlock_pkt(fsp);
+}
+EXPORT_SYMBOL(fc_fcp_complete);
+
+/**
+ * fc_eh_abort - Abort a command...from scsi host template
+ * @sc_cmd: scsi command to abort
+ *
+ * send ABTS to the target device and wait for the response
+ * sc_cmd is the pointer to the command to be aborted.
+ */
+int fc_eh_abort(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_fcp_pkt *fsp;
+ struct fc_lport *lp;
+ int rc = FAILED;
+ unsigned long flags;
+
+ lp = shost_priv(sc_cmd->device->host);
+ if (lp->state != LPORT_ST_READY)
+ return rc;
+ else if (!(lp->link_status & FC_LINK_UP))
+ return rc;
+
+ spin_lock_irqsave(lp->host->host_lock, flags);
+ fsp = CMD_SP(sc_cmd);
+ if (!fsp) {
+ /* command completed while scsi eh was setting up */
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+ return SUCCESS;
+ }
+ /* grab a ref so the fsp and sc_cmd cannot be relased from under us */
+ fc_fcp_pkt_hold(fsp);
+ spin_unlock_irqrestore(lp->host->host_lock, flags);
+
+ if (fc_fcp_lock_pkt(fsp)) {
+ /* completed while we were waiting for timer to be deleted */
+ rc = SUCCESS;
+ goto release_pkt;
+ }
+
+ rc = fc_fcp_pkt_abort(lp, fsp);
+ fc_fcp_unlock_pkt(fsp);
+
+release_pkt:
+ fc_fcp_pkt_release(fsp);
+ return rc;
+}
+EXPORT_SYMBOL(fc_eh_abort);
+
+/**
+ * fc_eh_device_reset: Reset a single LUN
+ * @sc_cmd: scsi command
+ *
+ * Set from scsi host template to send tm cmd to the target and wait for the
+ * response.
+ */
+int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct fc_lport *lp;
+ struct fc_fcp_pkt *fsp;
+ struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
+ int rc = FAILED;
+ struct fc_rport_libfc_priv *rp;
+ int rval;
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval)
+ goto out;
+
+ rp = rport->dd_data;
+ lp = shost_priv(sc_cmd->device->host);
+
+ if (lp->state != LPORT_ST_READY)
+ return rc;
+
+ fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+ if (fsp == NULL) {
+ FC_DBG("could not allocate scsi_pkt\n");
+ sc_cmd->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+
+ /*
+ * Build the libfc request pkt. Do not set the scsi cmnd, because
+ * the sc passed in is not setup for execution like when sent
+ * through the queuecommand callout.
+ */
+ fsp->lp = lp; /* save the softc ptr */
+ fsp->rport = rport; /* set the remote port ptr */
+
+ /*
+ * flush outstanding commands
+ */
+ rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
+ fsp->state = FC_SRB_FREE;
+ fc_fcp_pkt_release(fsp);
+
+out:
+ return rc;
+}
+EXPORT_SYMBOL(fc_eh_device_reset);
+
+/**
+ * fc_eh_host_reset - The reset function will reset the ports on the host.
+ * @sc_cmd: scsi command
+ */
+int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
+{
+ struct Scsi_Host *shost = sc_cmd->device->host;
+ struct fc_lport *lp = shost_priv(shost);
+ unsigned long wait_tmo;
+
+ lp->tt.lport_reset(lp);
+ wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
+ while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
+ msleep(1000);
+
+ if (fc_fcp_lport_queue_ready(lp)) {
+ shost_printk(KERN_INFO, shost, "Host reset succeeded.\n");
+ return SUCCESS;
+ } else {
+ shost_printk(KERN_INFO, shost, "Host reset failed. "
+ "lport not ready.\n");
+ return FAILED;
+ }
+}
+EXPORT_SYMBOL(fc_eh_host_reset);
+
+/**
+ * fc_slave_alloc - configure queue depth
+ * @sdev: scsi device
+ *
+ * Configures queue depth based on host's cmd_per_len. If not set
+ * then we use the libfc default.
+ */
+int fc_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+ int queue_depth;
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ if (sdev->tagged_supported) {
+ if (sdev->host->hostt->cmd_per_lun)
+ queue_depth = sdev->host->hostt->cmd_per_lun;
+ else
+ queue_depth = FC_FCP_DFLT_QUEUE_DEPTH;
+ scsi_activate_tcq(sdev, queue_depth);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fc_slave_alloc);
+
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ return sdev->queue_depth;
+}
+EXPORT_SYMBOL(fc_change_queue_depth);
+
+int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag_type);
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+
+ return tag_type;
+}
+EXPORT_SYMBOL(fc_change_queue_type);
+
+void fc_fcp_destroy(struct fc_lport *lp)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+
+ if (!list_empty(&si->scsi_pkt_queue))
+ printk(KERN_ERR "Leaked scsi packets.\n");
+
+ mempool_destroy(si->scsi_pkt_pool);
+ kfree(si);
+ lp->scsi_priv = NULL;
+}
+EXPORT_SYMBOL(fc_fcp_destroy);
+
+int fc_fcp_init(struct fc_lport *lp)
+{
+ int rc;
+ struct fc_fcp_internal *si;
+
+ if (!lp->tt.fcp_cmd_send)
+ lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+
+ if (!lp->tt.fcp_cleanup)
+ lp->tt.fcp_cleanup = fc_fcp_cleanup;
+
+ if (!lp->tt.fcp_abort_io)
+ lp->tt.fcp_abort_io = fc_fcp_abort_io;
+
+ si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
+ if (!si)
+ return -ENOMEM;
+ lp->scsi_priv = si;
+ INIT_LIST_HEAD(&si->scsi_pkt_queue);
+
+ si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
+ if (!si->scsi_pkt_pool) {
+ rc = -ENOMEM;
+ goto free_internal;
+ }
+ return 0;
+
+free_internal:
+ kfree(si);
+ return rc;
+}
+EXPORT_SYMBOL(fc_fcp_init);
+
+static int __init libfc_init(void)
+{
+ int rc;
+
+ scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
+ sizeof(struct fc_fcp_pkt),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (scsi_pkt_cachep == NULL) {
+ FC_DBG("Unable to allocate SRB cache...module load failed!");
+ return -ENOMEM;
+ }
+
+ rc = fc_setup_exch_mgr();
+ if (rc)
+ goto destroy_pkt_cache;
+
+ rc = fc_setup_rport();
+ if (rc)
+ goto destroy_em;
+
+ return rc;
+destroy_em:
+ fc_destroy_exch_mgr();
+destroy_pkt_cache:
+ kmem_cache_destroy(scsi_pkt_cachep);
+ return rc;
+}
+
+static void __exit libfc_exit(void)
+{
+ kmem_cache_destroy(scsi_pkt_cachep);
+ fc_destroy_exch_mgr();
+ fc_destroy_rport();
+}
+
+module_init(libfc_init);
+module_exit(libfc_exit);
diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c
new file mode 100644
index 000000000000..63fe00cfe667
--- /dev/null
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Frame allocation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/crc32.h>
+
+#include <scsi/fc_frame.h>
+
+/*
+ * Check the CRC in a frame.
+ */
+u32 fc_frame_crc_check(struct fc_frame *fp)
+{
+ u32 crc;
+ u32 error;
+ const u8 *bp;
+ unsigned int len;
+
+ WARN_ON(!fc_frame_is_linear(fp));
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ len = (fr_len(fp) + 3) & ~3; /* round up length to include fill */
+ bp = (const u8 *) fr_hdr(fp);
+ crc = ~crc32(~0, bp, len);
+ error = crc ^ fr_crc(fp);
+ return error;
+}
+EXPORT_SYMBOL(fc_frame_crc_check);
+
+/*
+ * Allocate a frame intended to be sent via fcoe_xmit.
+ * Get an sk_buff for the frame and set the length.
+ */
+struct fc_frame *__fc_frame_alloc(size_t len)
+{
+ struct fc_frame *fp;
+ struct sk_buff *skb;
+
+ WARN_ON((len % sizeof(u32)) != 0);
+ len += sizeof(struct fc_frame_header);
+ skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM);
+ if (!skb)
+ return NULL;
+ fp = (struct fc_frame *) skb;
+ fc_frame_init(fp);
+ skb_reserve(skb, FC_FRAME_HEADROOM);
+ skb_put(skb, len);
+ return fp;
+}
+EXPORT_SYMBOL(__fc_frame_alloc);
+
+
+struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
+{
+ struct fc_frame *fp;
+ size_t fill;
+
+ fill = payload_len % 4;
+ if (fill != 0)
+ fill = 4 - fill;
+ fp = __fc_frame_alloc(payload_len + fill);
+ if (fp) {
+ memset((char *) fr_hdr(fp) + payload_len, 0, fill);
+ /* trim is OK, we just allocated it so there are no fragments */
+ skb_trim(fp_skb(fp),
+ payload_len + sizeof(struct fc_frame_header));
+ }
+ return fp;
+}
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
new file mode 100644
index 000000000000..0b9bdb1fb807
--- /dev/null
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -0,0 +1,1604 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * PORT LOCKING NOTES
+ *
+ * These comments only apply to the 'port code' which consists of the lport,
+ * disc and rport blocks.
+ *
+ * MOTIVATION
+ *
+ * The lport, disc and rport blocks all have mutexes that are used to protect
+ * those objects. The main motivation for these locks is to prevent from
+ * having an lport reset just before we send a frame. In that scenario the
+ * lport's FID would get set to zero and then we'd send a frame with an
+ * invalid SID. We also need to ensure that states don't change unexpectedly
+ * while processing another state.
+ *
+ * HEIRARCHY
+ *
+ * The following heirarchy defines the locking rules. A greater lock
+ * may be held before acquiring a lesser lock, but a lesser lock should never
+ * be held while attempting to acquire a greater lock. Here is the heirarchy-
+ *
+ * lport > disc, lport > rport, disc > rport
+ *
+ * CALLBACKS
+ *
+ * The callbacks cause complications with this scheme. There is a callback
+ * from the rport (to either lport or disc) and a callback from disc
+ * (to the lport).
+ *
+ * As rports exit the rport state machine a callback is made to the owner of
+ * the rport to notify success or failure. Since the callback is likely to
+ * cause the lport or disc to grab its lock we cannot hold the rport lock
+ * while making the callback. To ensure that the rport is not free'd while
+ * processing the callback the rport callbacks are serialized through a
+ * single-threaded workqueue. An rport would never be free'd while in a
+ * callback handler becuase no other rport work in this queue can be executed
+ * at the same time.
+ *
+ * When discovery succeeds or fails a callback is made to the lport as
+ * notification. Currently, succesful discovery causes the lport to take no
+ * action. A failure will cause the lport to reset. There is likely a circular
+ * locking problem with this implementation.
+ */
+
+/*
+ * LPORT LOCKING
+ *
+ * The critical sections protected by the lport's mutex are quite broad and
+ * may be improved upon in the future. The lport code and its locking doesn't
+ * influence the I/O path, so excessive locking doesn't penalize I/O
+ * performance.
+ *
+ * The strategy is to lock whenever processing a request or response. Note
+ * that every _enter_* function corresponds to a state change. They generally
+ * change the lports state and then send a request out on the wire. We lock
+ * before calling any of these functions to protect that state change. This
+ * means that the entry points into the lport block manage the locks while
+ * the state machine can transition between states (i.e. _enter_* functions)
+ * while always staying protected.
+ *
+ * When handling responses we also hold the lport mutex broadly. When the
+ * lport receives the response frame it locks the mutex and then calls the
+ * appropriate handler for the particuar response. Generally a response will
+ * trigger a state change and so the lock must already be held.
+ *
+ * Retries also have to consider the locking. The retries occur from a work
+ * context and the work function will lock the lport and then retry the state
+ * (i.e. _enter_* function).
+ */
+
+#include <linux/timer.h>
+#include <asm/unaligned.h>
+
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+/* Fabric IDs to use for point-to-point mode, chosen on whims. */
+#define FC_LOCAL_PTP_FID_LO 0x010101
+#define FC_LOCAL_PTP_FID_HI 0x010102
+
+#define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/
+
+static int fc_lport_debug;
+
+#define FC_DEBUG_LPORT(fmt...) \
+ do { \
+ if (fc_lport_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+static void fc_lport_error(struct fc_lport *, struct fc_frame *);
+
+static void fc_lport_enter_reset(struct fc_lport *);
+static void fc_lport_enter_flogi(struct fc_lport *);
+static void fc_lport_enter_dns(struct fc_lport *);
+static void fc_lport_enter_rpn_id(struct fc_lport *);
+static void fc_lport_enter_rft_id(struct fc_lport *);
+static void fc_lport_enter_scr(struct fc_lport *);
+static void fc_lport_enter_ready(struct fc_lport *);
+static void fc_lport_enter_logo(struct fc_lport *);
+
+static const char *fc_lport_state_names[] = {
+ [LPORT_ST_NONE] = "none",
+ [LPORT_ST_FLOGI] = "FLOGI",
+ [LPORT_ST_DNS] = "dNS",
+ [LPORT_ST_RPN_ID] = "RPN_ID",
+ [LPORT_ST_RFT_ID] = "RFT_ID",
+ [LPORT_ST_SCR] = "SCR",
+ [LPORT_ST_READY] = "Ready",
+ [LPORT_ST_LOGO] = "LOGO",
+ [LPORT_ST_RESET] = "reset",
+};
+
+static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
+{
+ fc_frame_free(fp);
+ return 0;
+}
+
+/**
+ * fc_lport_rport_callback - Event handler for rport events
+ * @lport: The lport which is receiving the event
+ * @rport: The rport which the event has occured on
+ * @event: The event that occured
+ *
+ * Locking Note: The rport lock should not be held when calling
+ * this function.
+ */
+static void fc_lport_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
+{
+ FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event,
+ rport->port_id);
+
+ switch (event) {
+ case RPORT_EV_CREATED:
+ if (rport->port_id == FC_FID_DIR_SERV) {
+ mutex_lock(&lport->lp_mutex);
+ if (lport->state == LPORT_ST_DNS) {
+ lport->dns_rp = rport;
+ fc_lport_enter_rpn_id(lport);
+ } else {
+ FC_DEBUG_LPORT("Received an CREATED event on "
+ "port (%6x) for the directory "
+ "server, but the lport is not "
+ "in the DNS state, it's in the "
+ "%d state", rport->port_id,
+ lport->state);
+ lport->tt.rport_logoff(rport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+ } else
+ FC_DEBUG_LPORT("Received an event for port (%6x) "
+ "which is not the directory server\n",
+ rport->port_id);
+ break;
+ case RPORT_EV_LOGO:
+ case RPORT_EV_FAILED:
+ case RPORT_EV_STOP:
+ if (rport->port_id == FC_FID_DIR_SERV) {
+ mutex_lock(&lport->lp_mutex);
+ lport->dns_rp = NULL;
+ mutex_unlock(&lport->lp_mutex);
+
+ } else
+ FC_DEBUG_LPORT("Received an event for port (%6x) "
+ "which is not the directory server\n",
+ rport->port_id);
+ break;
+ case RPORT_EV_NONE:
+ break;
+ }
+}
+
+/**
+ * fc_lport_state - Return a string which represents the lport's state
+ * @lport: The lport whose state is to converted to a string
+ */
+static const char *fc_lport_state(struct fc_lport *lport)
+{
+ const char *cp;
+
+ cp = fc_lport_state_names[lport->state];
+ if (!cp)
+ cp = "unknown";
+ return cp;
+}
+
+/**
+ * fc_lport_ptp_setup - Create an rport for point-to-point mode
+ * @lport: The lport to attach the ptp rport to
+ * @fid: The FID of the ptp rport
+ * @remote_wwpn: The WWPN of the ptp rport
+ * @remote_wwnn: The WWNN of the ptp rport
+ */
+static void fc_lport_ptp_setup(struct fc_lport *lport,
+ u32 remote_fid, u64 remote_wwpn,
+ u64 remote_wwnn)
+{
+ struct fc_disc_port dp;
+
+ dp.lp = lport;
+ dp.ids.port_id = remote_fid;
+ dp.ids.port_name = remote_wwpn;
+ dp.ids.node_name = remote_wwnn;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+ if (lport->ptp_rp) {
+ lport->tt.rport_logoff(lport->ptp_rp);
+ lport->ptp_rp = NULL;
+ }
+
+ lport->ptp_rp = fc_rport_rogue_create(&dp);
+
+ lport->tt.rport_login(lport->ptp_rp);
+
+ fc_lport_enter_ready(lport);
+}
+
+void fc_get_host_port_type(struct Scsi_Host *shost)
+{
+ /* TODO - currently just NPORT */
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+}
+EXPORT_SYMBOL(fc_get_host_port_type);
+
+void fc_get_host_port_state(struct Scsi_Host *shost)
+{
+ struct fc_lport *lp = shost_priv(shost);
+
+ if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+}
+EXPORT_SYMBOL(fc_get_host_port_state);
+
+void fc_get_host_speed(struct Scsi_Host *shost)
+{
+ struct fc_lport *lport = shost_priv(shost);
+
+ fc_host_speed(shost) = lport->link_speed;
+}
+EXPORT_SYMBOL(fc_get_host_speed);
+
+struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
+{
+ int i;
+ struct fc_host_statistics *fcoe_stats;
+ struct fc_lport *lp = shost_priv(shost);
+ struct timespec v0, v1;
+
+ fcoe_stats = &lp->host_stats;
+ memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
+
+ jiffies_to_timespec(jiffies, &v0);
+ jiffies_to_timespec(lp->boot_time, &v1);
+ fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
+
+ for_each_online_cpu(i) {
+ struct fcoe_dev_stats *stats = lp->dev_stats[i];
+ if (stats == NULL)
+ continue;
+ fcoe_stats->tx_frames += stats->TxFrames;
+ fcoe_stats->tx_words += stats->TxWords;
+ fcoe_stats->rx_frames += stats->RxFrames;
+ fcoe_stats->rx_words += stats->RxWords;
+ fcoe_stats->error_frames += stats->ErrorFrames;
+ fcoe_stats->invalid_crc_count += stats->InvalidCRCCount;
+ fcoe_stats->fcp_input_requests += stats->InputRequests;
+ fcoe_stats->fcp_output_requests += stats->OutputRequests;
+ fcoe_stats->fcp_control_requests += stats->ControlRequests;
+ fcoe_stats->fcp_input_megabytes += stats->InputMegabytes;
+ fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes;
+ fcoe_stats->link_failure_count += stats->LinkFailureCount;
+ }
+ fcoe_stats->lip_count = -1;
+ fcoe_stats->nos_count = -1;
+ fcoe_stats->loss_of_sync_count = -1;
+ fcoe_stats->loss_of_signal_count = -1;
+ fcoe_stats->prim_seq_protocol_err_count = -1;
+ fcoe_stats->dumped_frames = -1;
+ return fcoe_stats;
+}
+EXPORT_SYMBOL(fc_get_host_stats);
+
+/*
+ * Fill in FLOGI command for request.
+ */
+static void
+fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
+ unsigned int op)
+{
+ struct fc_els_csp *sp;
+ struct fc_els_cssp *cp;
+
+ memset(flogi, 0, sizeof(*flogi));
+ flogi->fl_cmd = (u8) op;
+ put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+ sp = &flogi->fl_csp;
+ sp->sp_hi_ver = 0x20;
+ sp->sp_lo_ver = 0x20;
+ sp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ sp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+ if (op != ELS_FLOGI) {
+ sp->sp_features = htons(FC_SP_FT_CIRO);
+ sp->sp_tot_seq = htons(255); /* seq. we accept */
+ sp->sp_rel_off = htons(0x1f);
+ sp->sp_e_d_tov = htonl(lport->e_d_tov);
+
+ cp->cp_rdfs = htons((u16) lport->mfs);
+ cp->cp_con_seq = htons(255);
+ cp->cp_open_seq = 1;
+ }
+}
+
+/*
+ * Add a supported FC-4 type.
+ */
+static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
+{
+ __be32 *mp;
+
+ mp = &lport->fcts.ff_type_map[type / FC_NS_BPW];
+ *mp = htonl(ntohl(*mp) | 1UL << (type % FC_NS_BPW));
+}
+
+/**
+ * fc_lport_recv_rlir_req - Handle received Registered Link Incident Report.
+ * @lport: Fibre Channel local port recieving the RLIR
+ * @sp: current sequence in the RLIR exchange
+ * @fp: RLIR request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Received RLIR request while in state %s\n",
+ fc_lport_state(lport));
+
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_lport_recv_echo_req - Handle received ECHO request
+ * @lport: Fibre Channel local port recieving the ECHO
+ * @sp: current sequence in the ECHO exchange
+ * @fp: ECHO request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ unsigned int len;
+ void *pp;
+ void *dp;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received RLIR request while in state %s\n",
+ fc_lport_state(lport));
+
+ len = fr_len(in_fp) - sizeof(struct fc_frame_header);
+ pp = fc_frame_payload_get(in_fp, len);
+
+ if (len < sizeof(__be32))
+ len = sizeof(__be32);
+
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ dp = fc_frame_payload_get(fp, len);
+ memcpy(dp, pp, len);
+ *((u32 *)dp) = htonl(ELS_LS_ACC << 24);
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_echo_req - Handle received Request Node ID data request
+ * @lport: Fibre Channel local port recieving the RNID
+ * @sp: current sequence in the RNID exchange
+ * @fp: RNID request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_rnid *req;
+ struct {
+ struct fc_els_rnid_resp rnid;
+ struct fc_els_rnid_cid cid;
+ struct fc_els_rnid_gen gen;
+ } *rp;
+ struct fc_seq_els_data rjt_data;
+ u8 fmt;
+ size_t len;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received RNID request while in state %s\n",
+ fc_lport_state(lport));
+
+ req = fc_frame_payload_get(in_fp, sizeof(*req));
+ if (!req) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ fmt = req->rnid_fmt;
+ len = sizeof(*rp);
+ if (fmt != ELS_RNIDF_GEN ||
+ ntohl(lport->rnid_gen.rnid_atype) == 0) {
+ fmt = ELS_RNIDF_NONE; /* nothing to provide */
+ len -= sizeof(rp->gen);
+ }
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ rp = fc_frame_payload_get(fp, len);
+ memset(rp, 0, len);
+ rp->rnid.rnid_cmd = ELS_LS_ACC;
+ rp->rnid.rnid_fmt = fmt;
+ rp->rnid.rnid_cid_len = sizeof(rp->cid);
+ rp->cid.rnid_wwpn = htonll(lport->wwpn);
+ rp->cid.rnid_wwnn = htonll(lport->wwnn);
+ if (fmt == ELS_RNIDF_GEN) {
+ rp->rnid.rnid_sid_len = sizeof(rp->gen);
+ memcpy(&rp->gen, &lport->rnid_gen,
+ sizeof(rp->gen));
+ }
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_adisc_req - Handle received Address Discovery Request
+ * @lport: Fibre Channel local port recieving the ADISC
+ * @sp: current sequence in the ADISC exchange
+ * @fp: ADISC request frame
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_adisc *req, *rp;
+ struct fc_seq_els_data rjt_data;
+ size_t len;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received ADISC request while in state %s\n",
+ fc_lport_state(lport));
+
+ req = fc_frame_payload_get(in_fp, sizeof(*req));
+ if (!req) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_LOGIC;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ len = sizeof(*rp);
+ fp = fc_frame_alloc(lport, len);
+ if (fp) {
+ rp = fc_frame_payload_get(fp, len);
+ memset(rp, 0, len);
+ rp->adisc_cmd = ELS_LS_ACC;
+ rp->adisc_wwpn = htonll(lport->wwpn);
+ rp->adisc_wwnn = htonll(lport->wwnn);
+ hton24(rp->adisc_port_id,
+ fc_host_port_id(lport->host));
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ }
+ }
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_logo_req - Handle received fabric LOGO request
+ * @lport: Fibre Channel local port recieving the LOGO
+ * @sp: current sequence in the LOGO exchange
+ * @fp: LOGO request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_lport_enter_reset(lport);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_fabric_login - Start the lport state machine
+ * @lport: The lport that should log into the fabric
+ *
+ * Locking Note: This function should not be called
+ * with the lport lock held.
+ */
+int fc_fabric_login(struct fc_lport *lport)
+{
+ int rc = -1;
+
+ mutex_lock(&lport->lp_mutex);
+ if (lport->state == LPORT_ST_NONE) {
+ fc_lport_enter_reset(lport);
+ rc = 0;
+ }
+ mutex_unlock(&lport->lp_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(fc_fabric_login);
+
+/**
+ * fc_linkup - Handler for transport linkup events
+ * @lport: The lport whose link is up
+ */
+void fc_linkup(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Link is up for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ if ((lport->link_status & FC_LINK_UP) != FC_LINK_UP) {
+ lport->link_status |= FC_LINK_UP;
+
+ if (lport->state == LPORT_ST_RESET)
+ fc_lport_enter_flogi(lport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_linkup);
+
+/**
+ * fc_linkdown - Handler for transport linkdown events
+ * @lport: The lport whose link is down
+ */
+void fc_linkdown(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ FC_DEBUG_LPORT("Link is down for port (%6x)\n",
+ fc_host_port_id(lport->host));
+
+ if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) {
+ lport->link_status &= ~(FC_LINK_UP);
+ fc_lport_enter_reset(lport);
+ lport->tt.fcp_cleanup(lport);
+ }
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_linkdown);
+
+/**
+ * fc_pause - Pause the flow of frames
+ * @lport: The lport to be paused
+ */
+void fc_pause(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ lport->link_status |= FC_PAUSE;
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_pause);
+
+/**
+ * fc_unpause - Unpause the flow of frames
+ * @lport: The lport to be unpaused
+ */
+void fc_unpause(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ lport->link_status &= ~(FC_PAUSE);
+ mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_unpause);
+
+/**
+ * fc_fabric_logoff - Logout of the fabric
+ * @lport: fc_lport pointer to logoff the fabric
+ *
+ * Return value:
+ * 0 for success, -1 for failure
+ **/
+int fc_fabric_logoff(struct fc_lport *lport)
+{
+ lport->tt.disc_stop_final(lport);
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_logo(lport);
+ mutex_unlock(&lport->lp_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(fc_fabric_logoff);
+
+/**
+ * fc_lport_destroy - unregister a fc_lport
+ * @lport: fc_lport pointer to unregister
+ *
+ * Return value:
+ * None
+ * Note:
+ * exit routine for fc_lport instance
+ * clean-up all the allocated memory
+ * and free up other system resources.
+ *
+ **/
+int fc_lport_destroy(struct fc_lport *lport)
+{
+ lport->tt.frame_send = fc_frame_drop;
+ lport->tt.fcp_abort_io(lport);
+ lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_destroy);
+
+/**
+ * fc_set_mfs - sets up the mfs for the corresponding fc_lport
+ * @lport: fc_lport pointer to unregister
+ * @mfs: the new mfs for fc_lport
+ *
+ * Set mfs for the given fc_lport to the new mfs.
+ *
+ * Return: 0 for success
+ *
+ **/
+int fc_set_mfs(struct fc_lport *lport, u32 mfs)
+{
+ unsigned int old_mfs;
+ int rc = -EINVAL;
+
+ mutex_lock(&lport->lp_mutex);
+
+ old_mfs = lport->mfs;
+
+ if (mfs >= FC_MIN_MAX_FRAME) {
+ mfs &= ~3;
+ if (mfs > FC_MAX_FRAME)
+ mfs = FC_MAX_FRAME;
+ mfs -= sizeof(struct fc_frame_header);
+ lport->mfs = mfs;
+ rc = 0;
+ }
+
+ if (!rc && mfs < old_mfs)
+ fc_lport_enter_reset(lport);
+
+ mutex_unlock(&lport->lp_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(fc_set_mfs);
+
+/**
+ * fc_lport_disc_callback - Callback for discovery events
+ * @lport: FC local port
+ * @event: The discovery event
+ */
+void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
+{
+ switch (event) {
+ case DISC_EV_SUCCESS:
+ FC_DEBUG_LPORT("Got a SUCCESS event for port (%6x)\n",
+ fc_host_port_id(lport->host));
+ break;
+ case DISC_EV_FAILED:
+ FC_DEBUG_LPORT("Got a FAILED event for port (%6x)\n",
+ fc_host_port_id(lport->host));
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_reset(lport);
+ mutex_unlock(&lport->lp_mutex);
+ break;
+ case DISC_EV_NONE:
+ WARN_ON(1);
+ break;
+ }
+}
+
+/**
+ * fc_rport_enter_ready - Enter the ready state and start discovery
+ * @lport: Fibre Channel local port that is ready
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_ready(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Port (%6x) entered Ready from state %s\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_READY);
+
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
+}
+
+/**
+ * fc_lport_recv_flogi_req - Receive a FLOGI request
+ * @sp_in: The sequence the FLOGI is on
+ * @rx_fp: The frame the FLOGI is in
+ * @lport: The lport that recieved the request
+ *
+ * A received FLOGI request indicates a point-to-point connection.
+ * Accept it with the common service parameters indicating our N port.
+ * Set up to do a PLOGI if we have the higher-number WWPN.
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
+ struct fc_frame *rx_fp,
+ struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct fc_seq *sp;
+ struct fc_exch *ep;
+ struct fc_els_flogi *flp;
+ struct fc_els_flogi *new_flp;
+ u64 remote_wwpn;
+ u32 remote_fid;
+ u32 local_fid;
+ u32 f_ctl;
+
+ FC_DEBUG_LPORT("Received FLOGI request while in state %s\n",
+ fc_lport_state(lport));
+
+ fh = fc_frame_header_get(rx_fp);
+ remote_fid = ntoh24(fh->fh_s_id);
+ flp = fc_frame_payload_get(rx_fp, sizeof(*flp));
+ if (!flp)
+ goto out;
+ remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
+ if (remote_wwpn == lport->wwpn) {
+ FC_DBG("FLOGI from port with same WWPN %llx "
+ "possible configuration error\n", remote_wwpn);
+ goto out;
+ }
+ FC_DBG("FLOGI from port WWPN %llx\n", remote_wwpn);
+
+ /*
+ * XXX what is the right thing to do for FIDs?
+ * The originator might expect our S_ID to be 0xfffffe.
+ * But if so, both of us could end up with the same FID.
+ */
+ local_fid = FC_LOCAL_PTP_FID_LO;
+ if (remote_wwpn < lport->wwpn) {
+ local_fid = FC_LOCAL_PTP_FID_HI;
+ if (!remote_fid || remote_fid == local_fid)
+ remote_fid = FC_LOCAL_PTP_FID_LO;
+ } else if (!remote_fid) {
+ remote_fid = FC_LOCAL_PTP_FID_HI;
+ }
+
+ fc_host_port_id(lport->host) = local_fid;
+
+ fp = fc_frame_alloc(lport, sizeof(*flp));
+ if (fp) {
+ sp = lport->tt.seq_start_next(fr_seq(rx_fp));
+ new_flp = fc_frame_payload_get(fp, sizeof(*flp));
+ fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI);
+ new_flp->fl_cmd = (u8) ELS_LS_ACC;
+
+ /*
+ * Send the response. If this fails, the originator should
+ * repeat the sequence.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+
+ } else {
+ fc_lport_error(lport, fp);
+ }
+ fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
+ get_unaligned_be64(&flp->fl_wwnn));
+
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
+
+out:
+ sp = fr_seq(rx_fp);
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_lport_recv_req - The generic lport request handler
+ * @lport: The lport that received the request
+ * @sp: The sequence the request is on
+ * @fp: The frame the request is in
+ *
+ * This function will see if the lport handles the request or
+ * if an rport should handle the request.
+ *
+ * Locking Note: This function should not be called with the lport
+ * lock held becuase it will grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
+ struct fc_rport *rport;
+ u32 s_id;
+ u32 d_id;
+ struct fc_seq_els_data rjt_data;
+
+ mutex_lock(&lport->lp_mutex);
+
+ /*
+ * Handle special ELS cases like FLOGI, LOGO, and
+ * RSCN here. These don't require a session.
+ * Even if we had a session, it might not be ready.
+ */
+ if (fh->fh_type == FC_TYPE_ELS && fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ /*
+ * Check opcode.
+ */
+ recv = NULL;
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_FLOGI:
+ recv = fc_lport_recv_flogi_req;
+ break;
+ case ELS_LOGO:
+ fh = fc_frame_header_get(fp);
+ if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI)
+ recv = fc_lport_recv_logo_req;
+ break;
+ case ELS_RSCN:
+ recv = lport->tt.disc_recv_req;
+ break;
+ case ELS_ECHO:
+ recv = fc_lport_recv_echo_req;
+ break;
+ case ELS_RLIR:
+ recv = fc_lport_recv_rlir_req;
+ break;
+ case ELS_RNID:
+ recv = fc_lport_recv_rnid_req;
+ break;
+ case ELS_ADISC:
+ recv = fc_lport_recv_adisc_req;
+ break;
+ }
+
+ if (recv)
+ recv(sp, fp, lport);
+ else {
+ /*
+ * Find session.
+ * If this is a new incoming PLOGI, we won't find it.
+ */
+ s_id = ntoh24(fh->fh_s_id);
+ d_id = ntoh24(fh->fh_d_id);
+
+ rport = lport->tt.rport_lookup(lport, s_id);
+ if (rport)
+ lport->tt.rport_recv_req(sp, fp, rport);
+ else {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp,
+ ELS_LS_RJT,
+ &rjt_data);
+ fc_frame_free(fp);
+ }
+ }
+ } else {
+ FC_DBG("dropping invalid frame (eof %x)\n", fr_eof(fp));
+ fc_frame_free(fp);
+ }
+ mutex_unlock(&lport->lp_mutex);
+
+ /*
+ * The common exch_done for all request may not be good
+ * if any request requires longer hold on exhange. XXX
+ */
+ lport->tt.exch_done(sp);
+}
+
+/**
+ * fc_lport_reset - Reset an lport
+ * @lport: The lport which should be reset
+ *
+ * Locking Note: This functions should not be called with the
+ * lport lock held.
+ */
+int fc_lport_reset(struct fc_lport *lport)
+{
+ mutex_lock(&lport->lp_mutex);
+ fc_lport_enter_reset(lport);
+ mutex_unlock(&lport->lp_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_reset);
+
+/**
+ * fc_rport_enter_reset - Reset the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_reset(struct fc_lport *lport)
+{
+ FC_DEBUG_LPORT("Port (%6x) entered RESET state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RESET);
+
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+
+ if (lport->ptp_rp) {
+ lport->tt.rport_logoff(lport->ptp_rp);
+ lport->ptp_rp = NULL;
+ }
+
+ lport->tt.disc_stop(lport);
+
+ lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ fc_host_fabric_name(lport->host) = 0;
+ fc_host_port_id(lport->host) = 0;
+
+ if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP)
+ fc_lport_enter_flogi(lport);
+}
+
+/**
+ * fc_lport_error - Handler for any errors
+ * @lport: The fc_lport object
+ * @fp: The frame pointer
+ *
+ * If the error was caused by a resource allocation failure
+ * then wait for half a second and retry, otherwise retry
+ * after the e_d_tov time.
+ */
+static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
+{
+ unsigned long delay = 0;
+ FC_DEBUG_LPORT("Error %ld in state %s, retries %d\n",
+ PTR_ERR(fp), fc_lport_state(lport),
+ lport->retry_count);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out.
+ * Retry after delay
+ */
+ if (lport->retry_count < lport->max_retry_count) {
+ lport->retry_count++;
+ if (!fp)
+ delay = msecs_to_jiffies(500);
+ else
+ delay = msecs_to_jiffies(lport->e_d_tov);
+
+ schedule_delayed_work(&lport->retry_work, delay);
+ } else {
+ switch (lport->state) {
+ case LPORT_ST_NONE:
+ case LPORT_ST_READY:
+ case LPORT_ST_RESET:
+ case LPORT_ST_RPN_ID:
+ case LPORT_ST_RFT_ID:
+ case LPORT_ST_SCR:
+ case LPORT_ST_DNS:
+ case LPORT_ST_FLOGI:
+ case LPORT_ST_LOGO:
+ fc_lport_enter_reset(lport);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * fc_lport_rft_id_resp - Handle response to Register Fibre
+ * Channel Types by ID (RPN_ID) request
+ * @sp: current sequence in RPN_ID exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a RFT_ID response\n");
+
+ if (lport->state != LPORT_ST_RFT_ID) {
+ FC_DBG("Received a RFT_ID response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_DIR &&
+ ct->ct_fs_subtype == FC_NS_SUBTYPE &&
+ ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_scr(lport);
+ else
+ fc_lport_error(lport, fp);
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_rpn_id_resp - Handle response to Register Port
+ * Name by ID (RPN_ID) request
+ * @sp: current sequence in RPN_ID exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a RPN_ID response\n");
+
+ if (lport->state != LPORT_ST_RPN_ID) {
+ FC_DBG("Received a RPN_ID response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_DIR &&
+ ct->ct_fs_subtype == FC_NS_SUBTYPE &&
+ ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_rft_id(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_scr_resp - Handle response to State Change Register (SCR) request
+ * @sp: current sequence in SCR exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the registration request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ u8 op;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a SCR response\n");
+
+ if (lport->state != LPORT_ST_SCR) {
+ FC_DBG("Received a SCR response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC)
+ fc_lport_enter_ready(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_enter_scr - Send a State Change Register (SCR) request
+ * @lport: Fibre Channel local port to register for state changes
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_scr(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Port (%6x) entered SCR state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_SCR);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_scr));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR,
+ fc_lport_scr_resp, lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_lport_enter_rft_id - Register FC4-types with the name server
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_rft_id(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_ns_fts *lps;
+ int i;
+
+ FC_DEBUG_LPORT("Port (%6x) entered RFT_ID state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RFT_ID);
+
+ lps = &lport->fcts;
+ i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]);
+ while (--i >= 0)
+ if (ntohl(lps->ff_type_map[i]) != 0)
+ break;
+ if (i < 0) {
+ /* nothing to register, move on to SCR */
+ fc_lport_enter_scr(lport);
+ return;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rft));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID,
+ fc_lport_rft_id_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_rport_enter_rft_id - Register port name with the name server
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_rpn_id(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Port (%6x) entered RPN_ID state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RPN_ID);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rn_id));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID,
+ fc_lport_rpn_id_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+static struct fc_rport_operations fc_lport_rport_ops = {
+ .event_callback = fc_lport_rport_callback,
+};
+
+/**
+ * fc_rport_enter_dns - Create a rport to the name server
+ * @lport: Fibre Channel local port requesting a rport for the name server
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_dns(struct fc_lport *lport)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ struct fc_disc_port dp;
+
+ dp.ids.port_id = FC_FID_DIR_SERV;
+ dp.ids.port_name = -1;
+ dp.ids.node_name = -1;
+ dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ dp.lp = lport;
+
+ FC_DEBUG_LPORT("Port (%6x) entered DNS state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_DNS);
+
+ rport = fc_rport_rogue_create(&dp);
+ if (!rport)
+ goto err;
+
+ rdata = rport->dd_data;
+ rdata->ops = &fc_lport_rport_ops;
+ lport->tt.rport_login(rport);
+ return;
+
+err:
+ fc_lport_error(lport, NULL);
+}
+
+/**
+ * fc_lport_timeout - Handler for the retry_work timer.
+ * @work: The work struct of the fc_lport
+ */
+static void fc_lport_timeout(struct work_struct *work)
+{
+ struct fc_lport *lport =
+ container_of(work, struct fc_lport,
+ retry_work.work);
+
+ mutex_lock(&lport->lp_mutex);
+
+ switch (lport->state) {
+ case LPORT_ST_NONE:
+ case LPORT_ST_READY:
+ case LPORT_ST_RESET:
+ WARN_ON(1);
+ break;
+ case LPORT_ST_FLOGI:
+ fc_lport_enter_flogi(lport);
+ break;
+ case LPORT_ST_DNS:
+ fc_lport_enter_dns(lport);
+ break;
+ case LPORT_ST_RPN_ID:
+ fc_lport_enter_rpn_id(lport);
+ break;
+ case LPORT_ST_RFT_ID:
+ fc_lport_enter_rft_id(lport);
+ break;
+ case LPORT_ST_SCR:
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_LOGO:
+ fc_lport_enter_logo(lport);
+ break;
+ }
+
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_logo_resp - Handle response to LOGO request
+ * @sp: current sequence in LOGO exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the LOGO request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ u8 op;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a LOGO response\n");
+
+ if (lport->state != LPORT_ST_LOGO) {
+ FC_DBG("Received a LOGO response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC)
+ fc_lport_enter_reset(lport);
+ else
+ fc_lport_error(lport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_rport_enter_logo - Logout of the fabric
+ * @lport: Fibre Channel local port to be logged out
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_logo(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+ struct fc_els_logo *logo;
+
+ FC_DEBUG_LPORT("Port (%6x) entered LOGO state from %s state\n",
+ fc_host_port_id(lport->host), fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_LOGO);
+
+ /* DNS session should be closed so we can release it here */
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+
+ fp = fc_frame_alloc(lport, sizeof(*logo));
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp,
+ lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_lport_flogi_resp - Handle response to FLOGI request
+ * @sp: current sequence in FLOGI exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error
+ * and then unlock the lport.
+ */
+static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_els_flogi *flp;
+ u32 did;
+ u16 csp_flags;
+ unsigned int r_a_tov;
+ unsigned int e_d_tov;
+ u16 mfs;
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ FC_DEBUG_LPORT("Received a FLOGI response\n");
+
+ if (lport->state != LPORT_ST_FLOGI) {
+ FC_DBG("Received a FLOGI response, but in state %s\n",
+ fc_lport_state(lport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ did = ntoh24(fh->fh_d_id);
+ if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
+
+ FC_DEBUG_LPORT("Assigned fid %x\n", did);
+ fc_host_port_id(lport->host) = did;
+
+ flp = fc_frame_payload_get(fp, sizeof(*flp));
+ if (flp) {
+ mfs = ntohs(flp->fl_csp.sp_bb_data) &
+ FC_SP_BB_DATA_MASK;
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
+ mfs < lport->mfs)
+ lport->mfs = mfs;
+ csp_flags = ntohs(flp->fl_csp.sp_features);
+ r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
+ e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
+ if (csp_flags & FC_SP_FT_EDTR)
+ e_d_tov /= 1000000;
+ if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+ if (e_d_tov > lport->e_d_tov)
+ lport->e_d_tov = e_d_tov;
+ lport->r_a_tov = 2 * e_d_tov;
+ FC_DBG("Point-to-Point mode\n");
+ fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
+ get_unaligned_be64(
+ &flp->fl_wwpn),
+ get_unaligned_be64(
+ &flp->fl_wwnn));
+ } else {
+ lport->e_d_tov = e_d_tov;
+ lport->r_a_tov = r_a_tov;
+ fc_host_fabric_name(lport->host) =
+ get_unaligned_be64(&flp->fl_wwnn);
+ fc_lport_enter_dns(lport);
+ }
+ }
+
+ if (flp) {
+ csp_flags = ntohs(flp->fl_csp.sp_features);
+ if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+ lport->tt.disc_start(fc_lport_disc_callback,
+ lport);
+ }
+ }
+ } else {
+ FC_DBG("bad FLOGI response\n");
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_rport_enter_flogi - Send a FLOGI request to the fabric manager
+ * @lport: Fibre Channel local port to be logged in to the fabric
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+void fc_lport_enter_flogi(struct fc_lport *lport)
+{
+ struct fc_frame *fp;
+
+ FC_DEBUG_LPORT("Processing FLOGI state\n");
+
+ fc_lport_state_enter(lport, LPORT_ST_FLOGI);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+ if (!fp)
+ return fc_lport_error(lport, fp);
+
+ if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI,
+ fc_lport_flogi_resp, lport, lport->e_d_tov))
+ fc_lport_error(lport, fp);
+}
+
+/* Configure a fc_lport */
+int fc_lport_config(struct fc_lport *lport)
+{
+ INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
+ mutex_init(&lport->lp_mutex);
+
+ fc_lport_state_enter(lport, LPORT_ST_NONE);
+
+ fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
+ fc_lport_add_fc4_type(lport, FC_TYPE_CT);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_config);
+
+int fc_lport_init(struct fc_lport *lport)
+{
+ if (!lport->tt.lport_recv)
+ lport->tt.lport_recv = fc_lport_recv_req;
+
+ if (!lport->tt.lport_reset)
+ lport->tt.lport_reset = fc_lport_reset;
+
+ fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
+ fc_host_node_name(lport->host) = lport->wwnn;
+ fc_host_port_name(lport->host) = lport->wwpn;
+ fc_host_supported_classes(lport->host) = FC_COS_CLASS3;
+ memset(fc_host_supported_fc4s(lport->host), 0,
+ sizeof(fc_host_supported_fc4s(lport->host)));
+ fc_host_supported_fc4s(lport->host)[2] = 1;
+ fc_host_supported_fc4s(lport->host)[7] = 1;
+
+ /* This value is also unchanging */
+ memset(fc_host_active_fc4s(lport->host), 0,
+ sizeof(fc_host_active_fc4s(lport->host)));
+ fc_host_active_fc4s(lport->host)[2] = 1;
+ fc_host_active_fc4s(lport->host)[7] = 1;
+ fc_host_maxframe_size(lport->host) = lport->mfs;
+ fc_host_supported_speeds(lport->host) = 0;
+ if (lport->link_supported_speeds & FC_PORTSPEED_1GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
+ if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
+ fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_lport_init);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
new file mode 100644
index 000000000000..e780d8caf70e
--- /dev/null
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -0,0 +1,1291 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * RPORT GENERAL INFO
+ *
+ * This file contains all processing regarding fc_rports. It contains the
+ * rport state machine and does all rport interaction with the transport class.
+ * There should be no other places in libfc that interact directly with the
+ * transport class in regards to adding and deleting rports.
+ *
+ * fc_rport's represent N_Port's within the fabric.
+ */
+
+/*
+ * RPORT LOCKING
+ *
+ * The rport should never hold the rport mutex and then attempt to acquire
+ * either the lport or disc mutexes. The rport's mutex is considered lesser
+ * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for
+ * more comments on the heirarchy.
+ *
+ * The locking strategy is similar to the lport's strategy. The lock protects
+ * the rport's states and is held and released by the entry points to the rport
+ * block. All _enter_* functions correspond to rport states and expect the rport
+ * mutex to be locked before calling them. This means that rports only handle
+ * one request or response at a time, since they're not critical for the I/O
+ * path this potential over-use of the mutex is acceptable.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/rcupdate.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <asm/unaligned.h>
+
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+
+static int fc_rport_debug;
+
+#define FC_DEBUG_RPORT(fmt...) \
+ do { \
+ if (fc_rport_debug) \
+ FC_DBG(fmt); \
+ } while (0)
+
+struct workqueue_struct *rport_event_queue;
+
+static void fc_rport_enter_plogi(struct fc_rport *);
+static void fc_rport_enter_prli(struct fc_rport *);
+static void fc_rport_enter_rtv(struct fc_rport *);
+static void fc_rport_enter_ready(struct fc_rport *);
+static void fc_rport_enter_logo(struct fc_rport *);
+
+static void fc_rport_recv_plogi_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_prli_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_prlo_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_logo_req(struct fc_rport *,
+ struct fc_seq *, struct fc_frame *);
+static void fc_rport_timeout(struct work_struct *);
+static void fc_rport_error(struct fc_rport *, struct fc_frame *);
+static void fc_rport_work(struct work_struct *);
+
+static const char *fc_rport_state_names[] = {
+ [RPORT_ST_NONE] = "None",
+ [RPORT_ST_INIT] = "Init",
+ [RPORT_ST_PLOGI] = "PLOGI",
+ [RPORT_ST_PRLI] = "PRLI",
+ [RPORT_ST_RTV] = "RTV",
+ [RPORT_ST_READY] = "Ready",
+ [RPORT_ST_LOGO] = "LOGO",
+};
+
+static void fc_rport_rogue_destroy(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_rport(dev);
+ FC_DEBUG_RPORT("Destroying rogue rport (%6x)\n", rport->port_id);
+ kfree(rport);
+}
+
+struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
+{
+ struct fc_rport *rport;
+ struct fc_rport_libfc_priv *rdata;
+ rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL);
+
+ if (!rport)
+ return NULL;
+
+ rdata = RPORT_TO_PRIV(rport);
+
+ rport->dd_data = rdata;
+ rport->port_id = dp->ids.port_id;
+ rport->port_name = dp->ids.port_name;
+ rport->node_name = dp->ids.node_name;
+ rport->roles = dp->ids.roles;
+ rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ /*
+ * Note: all this libfc rogue rport code will be removed for
+ * upstream so it fine that this is really ugly and hacky right now.
+ */
+ device_initialize(&rport->dev);
+ rport->dev.release = fc_rport_rogue_destroy;
+
+ mutex_init(&rdata->rp_mutex);
+ rdata->local_port = dp->lp;
+ rdata->trans_state = FC_PORTSTATE_ROGUE;
+ rdata->rp_state = RPORT_ST_INIT;
+ rdata->event = RPORT_EV_NONE;
+ rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
+ rdata->ops = NULL;
+ rdata->e_d_tov = dp->lp->e_d_tov;
+ rdata->r_a_tov = dp->lp->r_a_tov;
+ INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
+ INIT_WORK(&rdata->event_work, fc_rport_work);
+ /*
+ * For good measure, but not necessary as we should only
+ * add REAL rport to the lport list.
+ */
+ INIT_LIST_HEAD(&rdata->peers);
+
+ return rport;
+}
+
+/**
+ * fc_rport_state - return a string for the state the rport is in
+ * @rport: The rport whose state we want to get a string for
+ */
+static const char *fc_rport_state(struct fc_rport *rport)
+{
+ const char *cp;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ cp = fc_rport_state_names[rdata->rp_state];
+ if (!cp)
+ cp = "Unknown";
+ return cp;
+}
+
+/**
+ * fc_set_rport_loss_tmo - Set the remote port loss timeout in seconds.
+ * @rport: Pointer to Fibre Channel remote port structure
+ * @timeout: timeout in seconds
+ */
+void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
+{
+ if (timeout)
+ rport->dev_loss_tmo = timeout + 5;
+ else
+ rport->dev_loss_tmo = 30;
+}
+EXPORT_SYMBOL(fc_set_rport_loss_tmo);
+
+/**
+ * fc_plogi_get_maxframe - Get max payload from the common service parameters
+ * @flp: FLOGI payload structure
+ * @maxval: upper limit, may be less than what is in the service parameters
+ */
+static unsigned int
+fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval)
+{
+ unsigned int mfs;
+
+ /*
+ * Get max payload from the common service parameters and the
+ * class 3 receive data field size.
+ */
+ mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK;
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
+ maxval = mfs;
+ mfs = ntohs(flp->fl_cssp[3 - 1].cp_rdfs);
+ if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
+ maxval = mfs;
+ return maxval;
+}
+
+/**
+ * fc_rport_state_enter - Change the rport's state
+ * @rport: The rport whose state should change
+ * @new: The new state of the rport
+ *
+ * Locking Note: Called with the rport lock held
+ */
+static void fc_rport_state_enter(struct fc_rport *rport,
+ enum fc_rport_state new)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ if (rdata->rp_state != new)
+ rdata->retries = 0;
+ rdata->rp_state = new;
+}
+
+static void fc_rport_work(struct work_struct *work)
+{
+ struct fc_rport_libfc_priv *rdata =
+ container_of(work, struct fc_rport_libfc_priv, event_work);
+ enum fc_rport_event event;
+ enum fc_rport_trans_state trans_state;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_rport_operations *rport_ops;
+ struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+
+ mutex_lock(&rdata->rp_mutex);
+ event = rdata->event;
+ rport_ops = rdata->ops;
+
+ if (event == RPORT_EV_CREATED) {
+ struct fc_rport *new_rport;
+ struct fc_rport_libfc_priv *new_rdata;
+ struct fc_rport_identifiers ids;
+
+ ids.port_id = rport->port_id;
+ ids.roles = rport->roles;
+ ids.port_name = rport->port_name;
+ ids.node_name = rport->node_name;
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ new_rport = fc_remote_port_add(lport->host, 0, &ids);
+ if (new_rport) {
+ /*
+ * Switch from the rogue rport to the rport
+ * returned by the FC class.
+ */
+ new_rport->maxframe_size = rport->maxframe_size;
+
+ new_rdata = new_rport->dd_data;
+ new_rdata->e_d_tov = rdata->e_d_tov;
+ new_rdata->r_a_tov = rdata->r_a_tov;
+ new_rdata->ops = rdata->ops;
+ new_rdata->local_port = rdata->local_port;
+ new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
+ new_rdata->trans_state = FC_PORTSTATE_REAL;
+ mutex_init(&new_rdata->rp_mutex);
+ INIT_DELAYED_WORK(&new_rdata->retry_work,
+ fc_rport_timeout);
+ INIT_LIST_HEAD(&new_rdata->peers);
+ INIT_WORK(&new_rdata->event_work, fc_rport_work);
+
+ fc_rport_state_enter(new_rport, RPORT_ST_READY);
+ } else {
+ FC_DBG("Failed to create the rport for port "
+ "(%6x).\n", ids.port_id);
+ event = RPORT_EV_FAILED;
+ }
+ put_device(&rport->dev);
+ rport = new_rport;
+ rdata = new_rport->dd_data;
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
+ } else if ((event == RPORT_EV_FAILED) ||
+ (event == RPORT_EV_LOGO) ||
+ (event == RPORT_EV_STOP)) {
+ trans_state = rdata->trans_state;
+ mutex_unlock(&rdata->rp_mutex);
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
+ if (trans_state == FC_PORTSTATE_ROGUE)
+ put_device(&rport->dev);
+ else
+ fc_remote_port_delete(rport);
+ } else
+ mutex_unlock(&rdata->rp_mutex);
+}
+
+/**
+ * fc_rport_login - Start the remote port login state machine
+ * @rport: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+int fc_rport_login(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Login to port (%6x)\n", rport->port_id);
+
+ fc_rport_enter_plogi(rport);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ return 0;
+}
+
+/**
+ * fc_rport_logoff - Logoff and remove an rport
+ * @rport: Fibre Channel remote port to be removed
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+int fc_rport_logoff(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
+
+ fc_rport_enter_logo(rport);
+
+ /*
+ * Change the state to NONE so that we discard
+ * the response.
+ */
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ cancel_delayed_work_sync(&rdata->retry_work);
+
+ mutex_lock(&rdata->rp_mutex);
+
+ rdata->event = RPORT_EV_STOP;
+ queue_work(rport_event_queue, &rdata->event_work);
+
+ mutex_unlock(&rdata->rp_mutex);
+
+ return 0;
+}
+
+/**
+ * fc_rport_enter_ready - The rport is ready
+ * @rport: Fibre Channel remote port that is ready
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_ready(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+
+ fc_rport_state_enter(rport, RPORT_ST_READY);
+
+ FC_DEBUG_RPORT("Port (%6x) is Ready\n", rport->port_id);
+
+ rdata->event = RPORT_EV_CREATED;
+ queue_work(rport_event_queue, &rdata->event_work);
+}
+
+/**
+ * fc_rport_timeout - Handler for the retry_work timer.
+ * @work: The work struct of the fc_rport_libfc_priv
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+static void fc_rport_timeout(struct work_struct *work)
+{
+ struct fc_rport_libfc_priv *rdata =
+ container_of(work, struct fc_rport_libfc_priv, retry_work.work);
+ struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+
+ mutex_lock(&rdata->rp_mutex);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PLOGI:
+ fc_rport_enter_plogi(rport);
+ break;
+ case RPORT_ST_PRLI:
+ fc_rport_enter_prli(rport);
+ break;
+ case RPORT_ST_RTV:
+ fc_rport_enter_rtv(rport);
+ break;
+ case RPORT_ST_LOGO:
+ fc_rport_enter_logo(rport);
+ break;
+ case RPORT_ST_READY:
+ case RPORT_ST_INIT:
+ case RPORT_ST_NONE:
+ break;
+ }
+
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_error - Handler for any errors
+ * @rport: The fc_rport object
+ * @fp: The frame pointer
+ *
+ * If the error was caused by a resource allocation failure
+ * then wait for half a second and retry, otherwise retry
+ * immediately.
+ *
+ * Locking Note: The rport lock is expected to be held before
+ * calling this routine
+ */
+static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ unsigned long delay = 0;
+
+ FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n",
+ PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
+
+ if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+ /*
+ * Memory allocation failure, or the exchange timed out.
+ * Retry after delay
+ */
+ if (rdata->retries < rdata->local_port->max_retry_count) {
+ rdata->retries++;
+ if (!fp)
+ delay = msecs_to_jiffies(500);
+ get_device(&rport->dev);
+ schedule_delayed_work(&rdata->retry_work, delay);
+ } else {
+ switch (rdata->rp_state) {
+ case RPORT_ST_PLOGI:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_LOGO:
+ rdata->event = RPORT_EV_FAILED;
+ queue_work(rport_event_queue,
+ &rdata->event_work);
+ break;
+ case RPORT_ST_RTV:
+ fc_rport_enter_ready(rport);
+ break;
+ case RPORT_ST_NONE:
+ case RPORT_ST_READY:
+ case RPORT_ST_INIT:
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_els_flogi *plp;
+ unsigned int tov;
+ u16 csp_seq;
+ u16 cssp_seq;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_PLOGI) {
+ FC_DBG("Received a PLOGI response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC &&
+ (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
+ rport->port_name = get_unaligned_be64(&plp->fl_wwpn);
+ rport->node_name = get_unaligned_be64(&plp->fl_wwnn);
+
+ tov = ntohl(plp->fl_csp.sp_e_d_tov);
+ if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
+ tov /= 1000;
+ if (tov > rdata->e_d_tov)
+ rdata->e_d_tov = tov;
+ csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
+ cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
+ if (cssp_seq < csp_seq)
+ csp_seq = cssp_seq;
+ rdata->max_seq = csp_seq;
+ rport->maxframe_size =
+ fc_plogi_get_maxframe(plp, lport->mfs);
+
+ /*
+ * If the rport is one of the well known addresses
+ * we skip PRLI and RTV and go straight to READY.
+ */
+ if (rport->port_id >= FC_FID_DOM_MGR)
+ fc_rport_enter_ready(rport);
+ else
+ fc_rport_enter_prli(rport);
+ } else
+ fc_rport_error(rport, fp);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer
+ * @rport: Fibre Channel remote port to send PLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_plogi(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered PLOGI state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+
+ rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+ rdata->e_d_tov = lport->e_d_tov;
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_prli_resp - Process Login (PRLI) response handler
+ * @sp: current sequence in the PRLI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ u32 roles = FC_RPORT_ROLE_UNKNOWN;
+ u32 fcp_parm = 0;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_PRLI) {
+ FC_DBG("Received a PRLI response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ pp = fc_frame_payload_get(fp, sizeof(*pp));
+ if (pp && pp->prli.prli_spp_len >= sizeof(pp->spp)) {
+ fcp_parm = ntohl(pp->spp.spp_params);
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ }
+
+ rport->supported_classes = FC_COS_CLASS3;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+
+ rport->roles = roles;
+ fc_rport_enter_rtv(rport);
+
+ } else {
+ FC_DBG("Bad ELS response\n");
+ rdata->event = RPORT_EV_FAILED;
+ queue_work(rport_event_queue, &rdata->event_work);
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_logo_resp - Logout (LOGO) response handler
+ * @sp: current sequence in the LOGO exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n",
+ rport->port_id);
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ if (rdata->rp_state != RPORT_ST_LOGO) {
+ FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ fc_rport_enter_rtv(rport);
+ } else {
+ FC_DBG("Bad ELS response\n");
+ rdata->event = RPORT_EV_LOGO;
+ queue_work(rport_event_queue, &rdata->event_work);
+ }
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_prli - Send Process Login (PRLI) request to peer
+ * @rport: Fibre Channel remote port to send PRLI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_prli(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered PRLI state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_PRLI);
+
+ fp = fc_frame_alloc(lport, sizeof(*pp));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
+ fc_rport_prli_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_els_rtv_resp - Request Timeout Value response handler
+ * @sp: current sequence in the RTV exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Many targets don't seem to support this.
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
+ */
+static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fc_rport *rport = rp_arg;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
+ rport->port_id);
+
+ if (rdata->rp_state != RPORT_ST_RTV) {
+ FC_DBG("Received a RTV response, but in state %s\n",
+ fc_rport_state(rport));
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
+ op = fc_frame_payload_op(fp);
+ if (op == ELS_LS_ACC) {
+ struct fc_els_rtv_acc *rtv;
+ u32 toq;
+ u32 tov;
+
+ rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+ if (rtv) {
+ toq = ntohl(rtv->rtv_toq);
+ tov = ntohl(rtv->rtv_r_a_tov);
+ if (tov == 0)
+ tov = 1;
+ rdata->r_a_tov = tov;
+ tov = ntohl(rtv->rtv_e_d_tov);
+ if (toq & FC_ELS_RTV_EDRES)
+ tov /= 1000000;
+ if (tov == 0)
+ tov = 1;
+ rdata->e_d_tov = tov;
+ }
+ }
+
+ fc_rport_enter_ready(rport);
+
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_rtv - Send Request Timeout Value (RTV) request to peer
+ * @rport: Fibre Channel remote port to send RTV to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_rtv(struct fc_rport *rport)
+{
+ struct fc_frame *fp;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ FC_DEBUG_RPORT("Port (%6x) entered RTV state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_RTV);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
+ fc_rport_rtv_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+/**
+ * fc_rport_enter_logo - Send Logout (LOGO) request to peer
+ * @rport: Fibre Channel remote port to send LOGO to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_logo(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_DEBUG_RPORT("Port (%6x) entered LOGO state from %s state\n",
+ rport->port_id, fc_rport_state(rport));
+
+ fc_rport_state_enter(rport, RPORT_ST_LOGO);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
+ if (!fp) {
+ fc_rport_error(rport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
+ fc_rport_logo_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
+}
+
+
+/**
+ * fc_rport_recv_req - Receive a request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
+ */
+void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ struct fc_frame_header *fh;
+ struct fc_seq_els_data els_data;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ els_data.fp = NULL;
+ els_data.explan = ELS_EXPL_NONE;
+ els_data.reason = ELS_RJT_NONE;
+
+ fh = fc_frame_header_get(fp);
+
+ if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) {
+ op = fc_frame_payload_op(fp);
+ switch (op) {
+ case ELS_PLOGI:
+ fc_rport_recv_plogi_req(rport, sp, fp);
+ break;
+ case ELS_PRLI:
+ fc_rport_recv_prli_req(rport, sp, fp);
+ break;
+ case ELS_PRLO:
+ fc_rport_recv_prlo_req(rport, sp, fp);
+ break;
+ case ELS_LOGO:
+ fc_rport_recv_logo_req(rport, sp, fp);
+ break;
+ case ELS_RRQ:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+ break;
+ case ELS_REC:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+ break;
+ default:
+ els_data.reason = ELS_RJT_UNSUP;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ break;
+ }
+ }
+
+ mutex_unlock(&rdata->rp_mutex);
+}
+
+/**
+ * fc_rport_recv_plogi_req - Handle incoming Port Login (PLOGI) request
+ * @rport: Fibre Channel remote port that initiated PLOGI
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: PLOGI request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp = rx_fp;
+ struct fc_exch *ep;
+ struct fc_frame_header *fh;
+ struct fc_els_flogi *pl;
+ struct fc_seq_els_data rjt_data;
+ u32 sid;
+ u64 wwpn;
+ u64 wwnn;
+ enum fc_els_rjt_reason reject = 0;
+ u32 f_ctl;
+ rjt_data.fp = NULL;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received PLOGI request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ sid = ntoh24(fh->fh_s_id);
+ pl = fc_frame_payload_get(fp, sizeof(*pl));
+ if (!pl) {
+ FC_DBG("incoming PLOGI from %x too short\n", sid);
+ WARN_ON(1);
+ /* XXX TBD: send reject? */
+ fc_frame_free(fp);
+ return;
+ }
+ wwpn = get_unaligned_be64(&pl->fl_wwpn);
+ wwnn = get_unaligned_be64(&pl->fl_wwnn);
+
+ /*
+ * If the session was just created, possibly due to the incoming PLOGI,
+ * set the state appropriately and accept the PLOGI.
+ *
+ * If we had also sent a PLOGI, and if the received PLOGI is from a
+ * higher WWPN, we accept it, otherwise an LS_RJT is sent with reason
+ * "command already in progress".
+ *
+ * XXX TBD: If the session was ready before, the PLOGI should result in
+ * all outstanding exchanges being reset.
+ */
+ switch (rdata->rp_state) {
+ case RPORT_ST_INIT:
+ FC_DEBUG_RPORT("incoming PLOGI from %6x wwpn %llx state INIT "
+ "- reject\n", sid, wwpn);
+ reject = ELS_RJT_UNSUP;
+ break;
+ case RPORT_ST_PLOGI:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in PLOGI state %d\n",
+ sid, rdata->rp_state);
+ if (wwpn < lport->wwpn)
+ reject = ELS_RJT_INPROG;
+ break;
+ case RPORT_ST_PRLI:
+ case RPORT_ST_READY:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in logged-in state %d "
+ "- ignored for now\n", sid, rdata->rp_state);
+ /* XXX TBD - should reset */
+ break;
+ case RPORT_ST_NONE:
+ default:
+ FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected "
+ "state %d\n", sid, rdata->rp_state);
+ break;
+ }
+
+ if (reject) {
+ rjt_data.reason = reject;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+ } else {
+ fp = fc_frame_alloc(lport, sizeof(*pl));
+ if (fp == NULL) {
+ fp = rx_fp;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+ } else {
+ sp = lport->tt.seq_start_next(sp);
+ WARN_ON(!sp);
+ fc_rport_set_name(rport, wwpn, wwnn);
+
+ /*
+ * Get session payload size from incoming PLOGI.
+ */
+ rport->maxframe_size =
+ fc_plogi_get_maxframe(pl, lport->mfs);
+ fc_frame_free(rx_fp);
+ fc_plogi_fill(lport, fp, ELS_LS_ACC);
+
+ /*
+ * Send LS_ACC. If this fails,
+ * the originator should retry.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ if (rdata->rp_state == RPORT_ST_PLOGI)
+ fc_rport_enter_prli(rport);
+ }
+ }
+}
+
+/**
+ * fc_rport_recv_prli_req - Handle incoming Process Login (PRLI) request
+ * @rport: Fibre Channel remote port that initiated PRLI
+ * @sp: current sequence in the PRLI exchange
+ * @fp: PRLI request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_prli_req(struct fc_rport *rport,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_exch *ep;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+ struct fc_els_spp *rspp; /* request service param page */
+ struct fc_els_spp *spp; /* response spp */
+ unsigned int len;
+ unsigned int plen;
+ enum fc_els_rjt_reason reason = ELS_RJT_UNAB;
+ enum fc_els_rjt_explan explan = ELS_EXPL_NONE;
+ enum fc_els_spp_resp resp;
+ struct fc_seq_els_data rjt_data;
+ u32 f_ctl;
+ u32 fcp_parm;
+ u32 roles = FC_RPORT_ROLE_UNKNOWN;
+ rjt_data.fp = NULL;
+
+ fh = fc_frame_header_get(rx_fp);
+
+ FC_DEBUG_RPORT("Received PRLI request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ case RPORT_ST_READY:
+ reason = ELS_RJT_NONE;
+ break;
+ default:
+ break;
+ }
+ len = fr_len(rx_fp) - sizeof(*fh);
+ pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
+ if (pp == NULL) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ } else {
+ plen = ntohs(pp->prli.prli_len);
+ if ((plen % 4) != 0 || plen > len) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ } else if (plen < len) {
+ len = plen;
+ }
+ plen = pp->prli.prli_spp_len;
+ if ((plen % 4) != 0 || plen < sizeof(*spp) ||
+ plen > len || len < sizeof(*pp)) {
+ reason = ELS_RJT_PROT;
+ explan = ELS_EXPL_INV_LEN;
+ }
+ rspp = &pp->spp;
+ }
+ if (reason != ELS_RJT_NONE ||
+ (fp = fc_frame_alloc(lport, len)) == NULL) {
+ rjt_data.reason = reason;
+ rjt_data.explan = explan;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ } else {
+ sp = lport->tt.seq_start_next(sp);
+ WARN_ON(!sp);
+ pp = fc_frame_payload_get(fp, len);
+ WARN_ON(!pp);
+ memset(pp, 0, len);
+ pp->prli.prli_cmd = ELS_LS_ACC;
+ pp->prli.prli_spp_len = plen;
+ pp->prli.prli_len = htons(len);
+ len -= sizeof(struct fc_els_prli);
+
+ /*
+ * Go through all the service parameter pages and build
+ * response. If plen indicates longer SPP than standard,
+ * use that. The entire response has been pre-cleared above.
+ */
+ spp = &pp->spp;
+ while (len >= plen) {
+ spp->spp_type = rspp->spp_type;
+ spp->spp_type_ext = rspp->spp_type_ext;
+ spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+ resp = FC_SPP_RESP_ACK;
+ if (rspp->spp_flags & FC_SPP_RPA_VAL)
+ resp = FC_SPP_RESP_NO_PA;
+ switch (rspp->spp_type) {
+ case 0: /* common to all FC-4 types */
+ break;
+ case FC_TYPE_FCP:
+ fcp_parm = ntohl(rspp->spp_params);
+ if (fcp_parm * FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rport->supported_classes = FC_COS_CLASS3;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+ rport->roles = roles;
+
+ spp->spp_params =
+ htonl(lport->service_params);
+ break;
+ default:
+ resp = FC_SPP_RESP_INVL;
+ break;
+ }
+ spp->spp_flags |= resp;
+ len -= plen;
+ rspp = (struct fc_els_spp *)((char *)rspp + plen);
+ spp = (struct fc_els_spp *)((char *)spp + plen);
+ }
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+
+ /*
+ * Get lock and re-check state.
+ */
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ fc_rport_enter_ready(rport);
+ break;
+ case RPORT_ST_READY:
+ break;
+ default:
+ break;
+ }
+ }
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_recv_prlo_req - Handle incoming Process Logout (PRLO) request
+ * @rport: Fibre Channel remote port that initiated PRLO
+ * @sp: current sequence in the PRLO exchange
+ * @fp: PRLO request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ struct fc_frame_header *fh;
+ struct fc_seq_els_data rjt_data;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received PRLO request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_recv_logo_req - Handle incoming Logout (LOGO) request
+ * @rport: Fibre Channel remote port that initiated LOGO
+ * @sp: current sequence in the LOGO exchange
+ * @fp: LOGO request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh;
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ fh = fc_frame_header_get(fp);
+
+ FC_DEBUG_RPORT("Received LOGO request from port (%6x) "
+ "while in state %s\n", ntoh24(fh->fh_s_id),
+ fc_rport_state(rport));
+
+ rdata->event = RPORT_EV_LOGO;
+ queue_work(rport_event_queue, &rdata->event_work);
+
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ fc_frame_free(fp);
+}
+
+static void fc_rport_flush_queue(void)
+{
+ flush_workqueue(rport_event_queue);
+}
+
+
+int fc_rport_init(struct fc_lport *lport)
+{
+ if (!lport->tt.rport_login)
+ lport->tt.rport_login = fc_rport_login;
+
+ if (!lport->tt.rport_logoff)
+ lport->tt.rport_logoff = fc_rport_logoff;
+
+ if (!lport->tt.rport_recv_req)
+ lport->tt.rport_recv_req = fc_rport_recv_req;
+
+ if (!lport->tt.rport_flush_queue)
+ lport->tt.rport_flush_queue = fc_rport_flush_queue;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_rport_init);
+
+int fc_setup_rport()
+{
+ rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
+ if (!rport_event_queue)
+ return -ENOMEM;
+ return 0;
+}
+EXPORT_SYMBOL(fc_setup_rport);
+
+void fc_destroy_rport()
+{
+ destroy_workqueue(rport_event_queue);
+}
+EXPORT_SYMBOL(fc_destroy_rport);
+
+void fc_rport_terminate_io(struct fc_rport *rport)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
+
+ lport->tt.exch_mgr_reset(lport->emp, 0, rport->port_id);
+ lport->tt.exch_mgr_reset(lport->emp, rport->port_id, 0);
+}
+EXPORT_SYMBOL(fc_rport_terminate_io);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 801c7cf54d2e..7225b6e2029e 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -88,34 +88,47 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
-void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *task,
- struct iscsi_data *hdr)
+/**
+ * iscsi_prep_data_out_pdu - initialize Data-Out
+ * @task: scsi command task
+ * @r2t: R2T info
+ * @hdr: iscsi data in pdu
+ *
+ * Notes:
+ * Initialize Data-Out within this R2T sequence and finds
+ * proper data_offset within this SCSI command.
+ *
+ * This function is called with connection lock taken.
+ **/
+void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t,
+ struct iscsi_data *hdr)
{
struct iscsi_conn *conn = task->conn;
+ unsigned int left = r2t->data_length - r2t->sent;
+
+ task->hdr_len = sizeof(struct iscsi_data);
memset(hdr, 0, sizeof(struct iscsi_data));
- hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
- hdr->datasn = cpu_to_be32(task->unsol_datasn);
- task->unsol_datasn++;
+ hdr->ttt = r2t->ttt;
+ hdr->datasn = cpu_to_be32(r2t->datasn);
+ r2t->datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
-
- hdr->itt = task->hdr->itt;
- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
- hdr->offset = cpu_to_be32(task->unsol_offset);
-
- if (task->unsol_count > conn->max_xmit_dlength) {
+ memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->itt = task->hdr_itt;
+ hdr->exp_statsn = r2t->exp_statsn;
+ hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent);
+ if (left > conn->max_xmit_dlength) {
hton24(hdr->dlength, conn->max_xmit_dlength);
- task->data_count = conn->max_xmit_dlength;
- task->unsol_offset += task->data_count;
+ r2t->data_count = conn->max_xmit_dlength;
hdr->flags = 0;
} else {
- hton24(hdr->dlength, task->unsol_count);
- task->data_count = task->unsol_count;
+ hton24(hdr->dlength, left);
+ r2t->data_count = left;
hdr->flags = ISCSI_FLAG_CMD_FINAL;
}
+ conn->dataout_pdus_cnt++;
}
-EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+EXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu);
static int iscsi_add_hdr(struct iscsi_task *task, unsigned len)
{
@@ -206,11 +219,24 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
- struct iscsi_cmd *hdr = task->hdr;
struct scsi_cmnd *sc = task->sc;
+ struct iscsi_cmd *hdr;
unsigned hdrlength, cmd_len;
+ itt_t itt;
int rc;
+ rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
+ if (rc)
+ return rc;
+ hdr = (struct iscsi_cmd *) task->hdr;
+ itt = hdr->itt;
+ memset(hdr, 0, sizeof(*hdr));
+
+ if (session->tt->parse_pdu_itt)
+ hdr->itt = task->hdr_itt = itt;
+ else
+ hdr->itt = task->hdr_itt = build_itt(task->itt,
+ task->conn->session->age);
task->hdr_len = 0;
rc = iscsi_add_hdr(task, sizeof(*hdr));
if (rc)
@@ -218,8 +244,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
hdr->opcode = ISCSI_OP_SCSI_CMD;
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- hdr->itt = build_itt(task->itt, session->age);
- hdr->cmdsn = cpu_to_be32(session->cmdsn);
+ memcpy(task->lun, hdr->lun, sizeof(task->lun));
+ hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
cmd_len = sc->cmd_len;
@@ -242,6 +268,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
}
if (sc->sc_data_direction == DMA_TO_DEVICE) {
unsigned out_len = scsi_out(sc)->length;
+ struct iscsi_r2t_info *r2t = &task->unsol_r2t;
+
hdr->data_length = cpu_to_be32(out_len);
hdr->flags |= ISCSI_FLAG_CMD_WRITE;
/*
@@ -254,13 +282,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
* without R2T ack right after
* immediate data
*
- * r2t_data_count bytes to be sent via R2T ack's
+ * r2t data_length bytes to be sent via R2T ack's
*
* pad_count bytes to be sent as zero-padding
*/
- task->unsol_count = 0;
- task->unsol_offset = 0;
- task->unsol_datasn = 0;
+ memset(r2t, 0, sizeof(*r2t));
if (session->imm_data_en) {
if (out_len >= session->first_burst)
@@ -274,12 +300,14 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
zero_data(hdr->dlength);
if (!session->initial_r2t_en) {
- task->unsol_count = min(session->first_burst, out_len)
- - task->imm_count;
- task->unsol_offset = task->imm_count;
+ r2t->data_length = min(session->first_burst, out_len) -
+ task->imm_count;
+ r2t->data_offset = task->imm_count;
+ r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+ r2t->exp_statsn = cpu_to_be32(conn->exp_statsn);
}
- if (!task->unsol_count)
+ if (!task->unsol_r2t.data_length)
/* No unsolicit Data-Out's */
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
} else {
@@ -300,8 +328,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
WARN_ON(hdrlength >= 256);
hdr->hlength = hdrlength & 0xFF;
- if (conn->session->tt->init_task &&
- conn->session->tt->init_task(task))
+ if (session->tt->init_task && session->tt->init_task(task))
return -EIO;
task->state = ISCSI_TASK_RUNNING;
@@ -332,6 +359,7 @@ static void iscsi_complete_command(struct iscsi_task *task)
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;
+ session->tt->cleanup_task(task);
list_del_init(&task->running);
task->state = ISCSI_TASK_COMPLETED;
task->sc = NULL;
@@ -402,8 +430,6 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
* the cmd in the sequencing
*/
conn->session->queued_cmdsn--;
- else
- conn->session->tt->cleanup_task(conn, task);
sc->result = err;
if (!scsi_bidi_cmnd(sc))
@@ -423,7 +449,7 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
struct iscsi_task *task)
{
struct iscsi_session *session = conn->session;
- struct iscsi_hdr *hdr = (struct iscsi_hdr *)task->hdr;
+ struct iscsi_hdr *hdr = task->hdr;
struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
@@ -437,7 +463,6 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
*/
nop->cmdsn = cpu_to_be32(session->cmdsn);
if (hdr->itt != RESERVED_ITT) {
- hdr->itt = build_itt(task->itt, session->age);
/*
* TODO: We always use immediate, so we never hit this.
* If we start to send tmfs or nops as non-immediate then
@@ -450,12 +475,13 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
}
}
- if (session->tt->init_task)
- session->tt->init_task(task);
+ if (session->tt->init_task && session->tt->init_task(task))
+ return -EIO;
if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
session->state = ISCSI_STATE_LOGGING_OUT;
+ task->state = ISCSI_TASK_RUNNING;
list_move_tail(&task->running, &conn->mgmt_run_list);
debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
@@ -469,6 +495,7 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_session *session = conn->session;
struct iscsi_task *task;
+ itt_t itt;
if (session->state == ISCSI_STATE_TERMINATE)
return NULL;
@@ -489,12 +516,6 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (!__kfifo_get(session->cmdpool.queue,
(void*)&task, sizeof(void*)))
return NULL;
-
- if ((hdr->opcode == (ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE)) &&
- hdr->ttt == RESERVED_ITT) {
- conn->ping_task = task;
- conn->last_ping = jiffies;
- }
}
/*
* released in complete pdu for task we expect a response for, and
@@ -511,23 +532,47 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
} else
task->data_count = 0;
+ if (conn->session->tt->alloc_pdu(task, hdr->opcode)) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate "
+ "pdu for mgmt task.\n");
+ goto requeue_task;
+ }
+ itt = task->hdr->itt;
+ task->hdr_len = sizeof(struct iscsi_hdr);
memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
+
+ if (hdr->itt != RESERVED_ITT) {
+ if (session->tt->parse_pdu_itt)
+ task->hdr->itt = itt;
+ else
+ task->hdr->itt = build_itt(task->itt,
+ task->conn->session->age);
+ }
+
INIT_LIST_HEAD(&task->running);
list_add_tail(&task->running, &conn->mgmtqueue);
if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
- if (iscsi_prep_mgmt_task(conn, task)) {
- __iscsi_put_task(task);
- return NULL;
- }
+ if (iscsi_prep_mgmt_task(conn, task))
+ goto free_task;
if (session->tt->xmit_task(task))
- task = NULL;
+ goto free_task;
} else
scsi_queue_work(conn->session->host, &conn->xmitwork);
return task;
+
+free_task:
+ __iscsi_put_task(task);
+ return NULL;
+
+requeue_task:
+ if (task != conn->login_task)
+ __kfifo_put(session->cmdpool.queue, (void*)&task,
+ sizeof(void*));
+ return NULL;
}
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -703,6 +748,11 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
if (!task)
iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
+ else if (!rhdr) {
+ /* only track our nops */
+ conn->ping_task = task;
+ conn->last_ping = jiffies;
+ }
}
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -710,7 +760,6 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
struct iscsi_hdr rejected_pdu;
- uint32_t itt;
conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
@@ -720,10 +769,9 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
- itt = get_itt(rejected_pdu.itt);
iscsi_conn_printk(KERN_ERR, conn,
- "itt 0x%x had pdu (op 0x%x) rejected "
- "due to DataDigest error.\n", itt,
+ "pdu (op 0x%x) rejected "
+ "due to DataDigest error.\n",
rejected_pdu.opcode);
}
}
@@ -743,12 +791,15 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- uint32_t i;
+ int i;
if (itt == RESERVED_ITT)
return NULL;
- i = get_itt(itt);
+ if (session->tt->parse_pdu_itt)
+ session->tt->parse_pdu_itt(conn, itt, &i, NULL);
+ else
+ i = get_itt(itt);
if (i >= session->cmds_max)
return NULL;
@@ -923,20 +974,25 @@ EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- uint32_t i;
+ int age = 0, i = 0;
if (itt == RESERVED_ITT)
return 0;
- if (((__force u32)itt & ISCSI_AGE_MASK) !=
- (session->age << ISCSI_AGE_SHIFT)) {
+ if (session->tt->parse_pdu_itt)
+ session->tt->parse_pdu_itt(conn, itt, &i, &age);
+ else {
+ i = get_itt(itt);
+ age = ((__force u32)itt >> ISCSI_AGE_SHIFT) & ISCSI_AGE_MASK;
+ }
+
+ if (age != session->age) {
iscsi_conn_printk(KERN_ERR, conn,
"received itt %x expected session age (%x)\n",
(__force u32)itt, session->age);
return ISCSI_ERR_BAD_ITT;
}
- i = get_itt(itt);
if (i >= session->cmds_max) {
iscsi_conn_printk(KERN_ERR, conn,
"received invalid itt index %u (max cmds "
@@ -1137,8 +1193,13 @@ check_mgmt:
fail_command(conn, conn->task, DID_IMM_RETRY << 16);
continue;
}
- if (iscsi_prep_scsi_cmd_pdu(conn->task)) {
- fail_command(conn, conn->task, DID_ABORT << 16);
+ rc = iscsi_prep_scsi_cmd_pdu(conn->task);
+ if (rc) {
+ if (rc == -ENOMEM) {
+ conn->task = NULL;
+ goto again;
+ } else
+ fail_command(conn, conn->task, DID_ABORT << 16);
continue;
}
rc = iscsi_xmit_task(conn);
@@ -1196,6 +1257,26 @@ static void iscsi_xmitworker(struct work_struct *work)
} while (rc >= 0 || rc == -EAGAIN);
}
+static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn,
+ struct scsi_cmnd *sc)
+{
+ struct iscsi_task *task;
+
+ if (!__kfifo_get(conn->session->cmdpool.queue,
+ (void *) &task, sizeof(void *)))
+ return NULL;
+
+ sc->SCp.phase = conn->session->age;
+ sc->SCp.ptr = (char *) task;
+
+ atomic_set(&task->refcount, 1);
+ task->state = ISCSI_TASK_PENDING;
+ task->conn = conn;
+ task->sc = sc;
+ INIT_LIST_HEAD(&task->running);
+ return task;
+}
+
enum {
FAILURE_BAD_HOST = 1,
FAILURE_SESSION_FAILED,
@@ -1282,33 +1363,27 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto reject;
}
- if (!__kfifo_get(session->cmdpool.queue, (void*)&task,
- sizeof(void*))) {
+ task = iscsi_alloc_task(conn, sc);
+ if (!task) {
reason = FAILURE_OOM;
goto reject;
}
- sc->SCp.phase = session->age;
- sc->SCp.ptr = (char *)task;
-
- atomic_set(&task->refcount, 1);
- task->state = ISCSI_TASK_PENDING;
- task->conn = conn;
- task->sc = sc;
- INIT_LIST_HEAD(&task->running);
list_add_tail(&task->running, &conn->xmitqueue);
if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
- if (iscsi_prep_scsi_cmd_pdu(task)) {
- sc->result = DID_ABORT << 16;
- sc->scsi_done = NULL;
- iscsi_complete_command(task);
- goto fault;
+ reason = iscsi_prep_scsi_cmd_pdu(task);
+ if (reason) {
+ if (reason == -ENOMEM) {
+ reason = FAILURE_OOM;
+ goto prepd_reject;
+ } else {
+ sc->result = DID_ABORT << 16;
+ goto prepd_fault;
+ }
}
if (session->tt->xmit_task(task)) {
- sc->scsi_done = NULL;
- iscsi_complete_command(task);
reason = FAILURE_SESSION_NOT_READY;
- goto reject;
+ goto prepd_reject;
}
} else
scsi_queue_work(session->host, &conn->xmitwork);
@@ -1318,12 +1393,18 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
spin_lock(host->host_lock);
return 0;
+prepd_reject:
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
reject:
spin_unlock(&session->lock);
debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
spin_lock(host->host_lock);
return SCSI_MLQUEUE_TARGET_BUSY;
+prepd_fault:
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
fault:
spin_unlock(&session->lock);
debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
@@ -1635,9 +1716,9 @@ static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->rtt = task->hdr->itt;
- hdr->refcmdsn = task->hdr->cmdsn;
+ memcpy(hdr->lun, task->lun, sizeof(hdr->lun));
+ hdr->rtt = task->hdr_itt;
+ hdr->refcmdsn = task->cmdsn;
}
int iscsi_eh_abort(struct scsi_cmnd *sc)
@@ -2224,7 +2305,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
}
spin_unlock_bh(&session->lock);
- data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
+ data = (char *) __get_free_pages(GFP_KERNEL,
+ get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
if (!data)
goto login_task_data_alloc_fail;
conn->login_task->data = conn->data = data;
@@ -2295,7 +2377,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
iscsi_suspend_tx(conn);
spin_lock_bh(&session->lock);
- kfree(conn->data);
+ free_pages((unsigned long) conn->data,
+ get_order(ISCSI_DEF_MAX_RECV_SEG_LEN));
kfree(conn->persistent_address);
__kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
new file mode 100644
index 000000000000..a745f91d2928
--- /dev/null
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -0,0 +1,1163 @@
+/*
+ * iSCSI over TCP/IP Data-Path lib
+ *
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 - 2006 Mike Christie
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@googlegroups.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.
+ *
+ * See the file COPYING included with this distribution for more details.
+ *
+ * Credits:
+ * Christoph Hellwig
+ * FUJITA Tomonori
+ * Arne Redlich
+ * Zhenyu Wang
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/inet.h>
+#include <linux/file.h>
+#include <linux/blkdev.h>
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <net/tcp.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "iscsi_tcp.h"
+
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
+ "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
+ "Alex Aizman <itn780@yahoo.com>");
+MODULE_DESCRIPTION("iSCSI/TCP data-path");
+MODULE_LICENSE("GPL");
+#undef DEBUG_TCP
+
+#ifdef DEBUG_TCP
+#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt)
+#else
+#define debug_tcp(fmt...)
+#endif
+
+static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment);
+
+/*
+ * Scatterlist handling: inside the iscsi_segment, we
+ * remember an index into the scatterlist, and set data/size
+ * to the current scatterlist entry. For highmem pages, we
+ * kmap as needed.
+ *
+ * Note that the page is unmapped when we return from
+ * TCP's data_ready handler, so we may end up mapping and
+ * unmapping the same page repeatedly. The whole reason
+ * for this is that we shouldn't keep the page mapped
+ * outside the softirq.
+ */
+
+/**
+ * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
+ * @segment: the buffer object
+ * @sg: scatterlist
+ * @offset: byte offset into that sg entry
+ *
+ * This function sets up the segment so that subsequent
+ * data is copied to the indicated sg entry, at the given
+ * offset.
+ */
+static inline void
+iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
+ struct scatterlist *sg, unsigned int offset)
+{
+ segment->sg = sg;
+ segment->sg_offset = offset;
+ segment->size = min(sg->length - offset,
+ segment->total_size - segment->total_copied);
+ segment->data = NULL;
+}
+
+/**
+ * iscsi_tcp_segment_map - map the current S/G page
+ * @segment: iscsi_segment
+ * @recv: 1 if called from recv path
+ *
+ * We only need to possibly kmap data if scatter lists are being used,
+ * because the iscsi passthrough and internal IO paths will never use high
+ * mem pages.
+ */
+static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
+{
+ struct scatterlist *sg;
+
+ if (segment->data != NULL || !segment->sg)
+ return;
+
+ sg = segment->sg;
+ BUG_ON(segment->sg_mapped);
+ BUG_ON(sg->length == 0);
+
+ /*
+ * If the page count is greater than one it is ok to send
+ * to the network layer's zero copy send path. If not we
+ * have to go the slow sendmsg path. We always map for the
+ * recv path.
+ */
+ if (page_count(sg_page(sg)) >= 1 && !recv)
+ return;
+
+ debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
+ segment);
+ segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+ segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
+}
+
+void iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
+{
+ debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
+
+ if (segment->sg_mapped) {
+ debug_tcp("iscsi_tcp_segment_unmap valid\n");
+ kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+ segment->sg_mapped = NULL;
+ segment->data = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_unmap);
+
+/*
+ * Splice the digest buffer into the buffer
+ */
+static inline void
+iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
+{
+ segment->data = digest;
+ segment->digest_len = ISCSI_DIGEST_SIZE;
+ segment->total_size += ISCSI_DIGEST_SIZE;
+ segment->size = ISCSI_DIGEST_SIZE;
+ segment->copied = 0;
+ segment->sg = NULL;
+ segment->hash = NULL;
+}
+
+/**
+ * iscsi_tcp_segment_done - check whether the segment is complete
+ * @tcp_conn: iscsi tcp connection
+ * @segment: iscsi segment to check
+ * @recv: set to one of this is called from the recv path
+ * @copied: number of bytes copied
+ *
+ * Check if we're done receiving this segment. If the receive
+ * buffer is full but we expect more data, move on to the
+ * next entry in the scatterlist.
+ *
+ * If the amount of data we received isn't a multiple of 4,
+ * we will transparently receive the pad bytes, too.
+ *
+ * This function must be re-entrant.
+ */
+int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment, int recv,
+ unsigned copied)
+{
+ static unsigned char padbuf[ISCSI_PAD_LEN];
+ struct scatterlist sg;
+ unsigned int pad;
+
+ debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
+ segment->size, recv ? "recv" : "xmit");
+ if (segment->hash && copied) {
+ /*
+ * If a segment is kmapd we must unmap it before sending
+ * to the crypto layer since that will try to kmap it again.
+ */
+ iscsi_tcp_segment_unmap(segment);
+
+ if (!segment->data) {
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, sg_page(segment->sg), copied,
+ segment->copied + segment->sg_offset +
+ segment->sg->offset);
+ } else
+ sg_init_one(&sg, segment->data + segment->copied,
+ copied);
+ crypto_hash_update(segment->hash, &sg, copied);
+ }
+
+ segment->copied += copied;
+ if (segment->copied < segment->size) {
+ iscsi_tcp_segment_map(segment, recv);
+ return 0;
+ }
+
+ segment->total_copied += segment->copied;
+ segment->copied = 0;
+ segment->size = 0;
+
+ /* Unmap the current scatterlist page, if there is one. */
+ iscsi_tcp_segment_unmap(segment);
+
+ /* Do we have more scatterlist entries? */
+ debug_tcp("total copied %u total size %u\n", segment->total_copied,
+ segment->total_size);
+ if (segment->total_copied < segment->total_size) {
+ /* Proceed to the next entry in the scatterlist. */
+ iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
+ 0);
+ iscsi_tcp_segment_map(segment, recv);
+ BUG_ON(segment->size == 0);
+ return 0;
+ }
+
+ /* Do we need to handle padding? */
+ if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) {
+ pad = iscsi_padding(segment->total_copied);
+ if (pad != 0) {
+ debug_tcp("consume %d pad bytes\n", pad);
+ segment->total_size += pad;
+ segment->size = pad;
+ segment->data = padbuf;
+ return 0;
+ }
+ }
+
+ /*
+ * Set us up for transferring the data digest. hdr digest
+ * is completely handled in hdr done function.
+ */
+ if (segment->hash) {
+ crypto_hash_final(segment->hash, segment->digest);
+ iscsi_tcp_segment_splice_digest(segment,
+ recv ? segment->recv_digest : segment->digest);
+ return 0;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_done);
+
+/**
+ * iscsi_tcp_segment_recv - copy data to segment
+ * @tcp_conn: the iSCSI TCP connection
+ * @segment: the buffer to copy to
+ * @ptr: data pointer
+ * @len: amount of data available
+ *
+ * This function copies up to @len bytes to the
+ * given buffer, and returns the number of bytes
+ * consumed, which can actually be less than @len.
+ *
+ * If hash digest is enabled, the function will update the
+ * hash while copying.
+ * Combining these two operations doesn't buy us a lot (yet),
+ * but in the future we could implement combined copy+crc,
+ * just way we do for network layer checksums.
+ */
+static int
+iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment, const void *ptr,
+ unsigned int len)
+{
+ unsigned int copy = 0, copied = 0;
+
+ while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) {
+ if (copied == len) {
+ debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
+ len);
+ break;
+ }
+
+ copy = min(len - copied, segment->size - segment->copied);
+ debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+ memcpy(segment->data + segment->copied, ptr + copied, copy);
+ copied += copy;
+ }
+ return copied;
+}
+
+inline void
+iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+ unsigned char digest[ISCSI_DIGEST_SIZE])
+{
+ struct scatterlist sg;
+
+ sg_init_one(&sg, hdr, hdrlen);
+ crypto_hash_digest(hash, &sg, hdrlen, digest);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_dgst_header);
+
+static inline int
+iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ if (!segment->digest_len)
+ return 1;
+
+ if (memcmp(segment->recv_digest, segment->digest,
+ segment->digest_len)) {
+ debug_scsi("digest mismatch\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Helper function to set up segment buffer
+ */
+static inline void
+__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
+ iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+{
+ memset(segment, 0, sizeof(*segment));
+ segment->total_size = size;
+ segment->done = done;
+
+ if (hash) {
+ segment->hash = hash;
+ crypto_hash_init(hash);
+ }
+}
+
+inline void
+iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
+ size_t size, iscsi_segment_done_fn_t *done,
+ struct hash_desc *hash)
+{
+ __iscsi_segment_init(segment, size, done, hash);
+ segment->data = data;
+ segment->size = size;
+}
+EXPORT_SYMBOL_GPL(iscsi_segment_init_linear);
+
+inline int
+iscsi_segment_seek_sg(struct iscsi_segment *segment,
+ struct scatterlist *sg_list, unsigned int sg_count,
+ unsigned int offset, size_t size,
+ iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+{
+ struct scatterlist *sg;
+ unsigned int i;
+
+ debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
+ offset, size);
+ __iscsi_segment_init(segment, size, done, hash);
+ for_each_sg(sg_list, sg, sg_count, i) {
+ debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
+ sg->offset);
+ if (offset < sg->length) {
+ iscsi_tcp_segment_init_sg(segment, sg, offset);
+ return 0;
+ }
+ offset -= sg->length;
+ }
+
+ return ISCSI_ERR_DATA_OFFSET;
+}
+EXPORT_SYMBOL_GPL(iscsi_segment_seek_sg);
+
+/**
+ * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
+ * @tcp_conn: iscsi connection to prep for
+ *
+ * This function always passes NULL for the hash argument, because when this
+ * function is called we do not yet know the final size of the header and want
+ * to delay the digest processing until we know that.
+ */
+void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+{
+ debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
+ tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+ iscsi_segment_init_linear(&tcp_conn->in.segment,
+ tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
+ iscsi_tcp_hdr_recv_done, NULL);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_recv_prep);
+
+/*
+ * Handle incoming reply to any other type of command
+ */
+static int
+iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ int rc = 0;
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_DATA_DGST;
+
+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
+ conn->data, tcp_conn->in.datalen);
+ if (rc)
+ return rc;
+
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+}
+
+static void
+iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct hash_desc *rx_hash = NULL;
+
+ if (conn->datadgst_en &
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
+ rx_hash = tcp_conn->rx_hash;
+
+ iscsi_segment_init_linear(&tcp_conn->in.segment,
+ conn->data, tcp_conn->in.datalen,
+ iscsi_tcp_data_recv_done, rx_hash);
+}
+
+/**
+ * iscsi_tcp_cleanup_task - free tcp_task resources
+ * @task: iscsi task
+ *
+ * must be called with session lock
+ */
+void iscsi_tcp_cleanup_task(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_r2t_info *r2t;
+
+ /* nothing to do for mgmt or pending tasks */
+ if (!task->sc || task->state == ISCSI_TASK_PENDING)
+ return;
+
+ /* flush task's r2t queues */
+ while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
+ }
+
+ r2t = tcp_task->r2t;
+ if (r2t != NULL) {
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ tcp_task->r2t = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task);
+
+/**
+ * iscsi_tcp_data_in - SCSI Data-In Response processing
+ * @conn: iscsi connection
+ * @task: scsi command task
+ */
+static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
+ int datasn = be32_to_cpu(rhdr->datasn);
+ unsigned total_in_length = scsi_in(task->sc)->length;
+
+ iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr);
+ if (tcp_conn->in.datalen == 0)
+ return 0;
+
+ if (tcp_task->exp_datasn != datasn) {
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
+ __func__, tcp_task->exp_datasn, datasn);
+ return ISCSI_ERR_DATASN;
+ }
+
+ tcp_task->exp_datasn++;
+
+ tcp_task->data_offset = be32_to_cpu(rhdr->offset);
+ if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
+ debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
+ __func__, tcp_task->data_offset,
+ tcp_conn->in.datalen, total_in_length);
+ return ISCSI_ERR_DATA_OFFSET;
+ }
+
+ conn->datain_pdus_cnt++;
+ return 0;
+}
+
+/**
+ * iscsi_tcp_r2t_rsp - iSCSI R2T Response processing
+ * @conn: iscsi connection
+ * @task: scsi command task
+ */
+static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
+ struct iscsi_r2t_info *r2t;
+ int r2tsn = be32_to_cpu(rhdr->r2tsn);
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2t with datalen %d\n",
+ tcp_conn->in.datalen);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (tcp_task->exp_datasn != r2tsn){
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+ __func__, tcp_task->exp_datasn, r2tsn);
+ return ISCSI_ERR_R2TSN;
+ }
+
+ /* fill-in new R2T associated with the task */
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
+
+ if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "dropping R2T itt %d in recovery.\n",
+ task->itt);
+ return 0;
+ }
+
+ rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
+ if (!rc) {
+ iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. "
+ "Target has sent more R2Ts than it "
+ "negotiated for or driver has has leaked.\n");
+ return ISCSI_ERR_PROTO;
+ }
+
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2T with zero data len\n");
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ return ISCSI_ERR_DATALEN;
+ }
+
+ if (r2t->data_length > session->max_burst)
+ debug_scsi("invalid R2T with data len %u and max burst %u."
+ "Attempting to execute request.\n",
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+ if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "invalid R2T with data len %u at offset %u "
+ "and total length %d\n", r2t->data_length,
+ r2t->data_offset, scsi_out(task->sc)->length);
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
+ sizeof(void*));
+ return ISCSI_ERR_DATALEN;
+ }
+
+ r2t->ttt = rhdr->ttt; /* no flip */
+ r2t->datasn = 0;
+ r2t->sent = 0;
+
+ tcp_task->exp_datasn = r2tsn + 1;
+ __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
+ conn->r2t_pdus_cnt++;
+
+ iscsi_requeue_task(task);
+ return 0;
+}
+
+/*
+ * Handle incoming reply to DataIn command
+ */
+static int
+iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+ int rc;
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_DATA_DGST;
+
+ /* check for non-exceptional status */
+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+ if (rc)
+ return rc;
+ }
+
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+}
+
+/**
+ * iscsi_tcp_hdr_dissect - process PDU header
+ * @conn: iSCSI connection
+ * @hdr: PDU header
+ *
+ * This function analyzes the header of the PDU received,
+ * and performs several sanity checks. If the PDU is accompanied
+ * by data, the receive buffer is set up to copy the incoming data
+ * to the correct location.
+ */
+static int
+iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+{
+ int rc = 0, opcode, ahslen;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_task *task;
+
+ /* verify PDU length */
+ tcp_conn->in.datalen = ntoh24(hdr->dlength);
+ if (tcp_conn->in.datalen > conn->max_recv_dlength) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "iscsi_tcp: datalen %d > %d\n",
+ tcp_conn->in.datalen, conn->max_recv_dlength);
+ return ISCSI_ERR_DATALEN;
+ }
+
+ /* Additional header segments. So far, we don't
+ * process additional headers.
+ */
+ ahslen = hdr->hlength << 2;
+
+ opcode = hdr->opcode & ISCSI_OPCODE_MASK;
+ /* verify itt (itt encoding: age+cid+itt) */
+ rc = iscsi_verify_itt(conn, hdr->itt);
+ if (rc)
+ return rc;
+
+ debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
+ opcode, ahslen, tcp_conn->in.datalen);
+
+ switch(opcode) {
+ case ISCSI_OP_SCSI_DATA_IN:
+ spin_lock(&conn->session->lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else
+ rc = iscsi_tcp_data_in(conn, task);
+ if (rc) {
+ spin_unlock(&conn->session->lock);
+ break;
+ }
+
+ if (tcp_conn->in.datalen) {
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct hash_desc *rx_hash = NULL;
+ struct scsi_data_buffer *sdb = scsi_in(task->sc);
+
+ /*
+ * Setup copy of Data-In into the Scsi_Cmnd
+ * Scatterlist case:
+ * We set up the iscsi_segment to point to the next
+ * scatterlist entry to copy to. As we go along,
+ * we move on to the next scatterlist entry and
+ * update the digest per-entry.
+ */
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
+ rx_hash = tcp_conn->rx_hash;
+
+ debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
+ "datalen=%d)\n", tcp_conn,
+ tcp_task->data_offset,
+ tcp_conn->in.datalen);
+ rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
+ sdb->table.sgl,
+ sdb->table.nents,
+ tcp_task->data_offset,
+ tcp_conn->in.datalen,
+ iscsi_tcp_process_data_in,
+ rx_hash);
+ spin_unlock(&conn->session->lock);
+ return rc;
+ }
+ rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+ spin_unlock(&conn->session->lock);
+ break;
+ case ISCSI_OP_SCSI_CMD_RSP:
+ if (tcp_conn->in.datalen) {
+ iscsi_tcp_data_recv_prep(tcp_conn);
+ return 0;
+ }
+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+ break;
+ case ISCSI_OP_R2T:
+ spin_lock(&conn->session->lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+ else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
+ rc = iscsi_tcp_r2t_rsp(conn, task);
+ else
+ rc = ISCSI_ERR_PROTO;
+ spin_unlock(&conn->session->lock);
+ break;
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_REJECT:
+ case ISCSI_OP_ASYNC_EVENT:
+ /*
+ * It is possible that we could get a PDU with a buffer larger
+ * than 8K, but there are no targets that currently do this.
+ * For now we fail until we find a vendor that needs it
+ */
+ if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "iscsi_tcp: received buffer of "
+ "len %u but conn buffer is only %u "
+ "(opcode %0x)\n",
+ tcp_conn->in.datalen,
+ ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ /* If there's data coming in with the response,
+ * receive it to the connection's buffer.
+ */
+ if (tcp_conn->in.datalen) {
+ iscsi_tcp_data_recv_prep(tcp_conn);
+ return 0;
+ }
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+
+ if (rc == 0) {
+ /* Anything that comes with data should have
+ * been handled above. */
+ if (tcp_conn->in.datalen)
+ return ISCSI_ERR_PROTO;
+ iscsi_tcp_hdr_recv_prep(tcp_conn);
+ }
+
+ return rc;
+}
+
+/**
+ * iscsi_tcp_hdr_recv_done - process PDU header
+ *
+ * This is the callback invoked when the PDU header has
+ * been received. If the header is followed by additional
+ * header segments, we go back for more data.
+ */
+static int
+iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment)
+{
+ struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+ struct iscsi_hdr *hdr;
+
+ /* Check if there are additional header segments
+ * *prior* to computing the digest, because we
+ * may need to go back to the caller for more.
+ */
+ hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+ if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
+ /* Bump the header length - the caller will
+ * just loop around and get the AHS for us, and
+ * call again. */
+ unsigned int ahslen = hdr->hlength << 2;
+
+ /* Make sure we don't overflow */
+ if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
+ return ISCSI_ERR_AHSLEN;
+
+ segment->total_size += ahslen;
+ segment->size += ahslen;
+ return 0;
+ }
+
+ /* We're done processing the header. See if we're doing
+ * header digests; if so, set up the recv_digest buffer
+ * and go back for more. */
+ if (conn->hdrdgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) {
+ if (segment->digest_len == 0) {
+ /*
+ * Even if we offload the digest processing we
+ * splice it in so we can increment the skb/segment
+ * counters in preparation for the data segment.
+ */
+ iscsi_tcp_segment_splice_digest(segment,
+ segment->recv_digest);
+ return 0;
+ }
+
+ iscsi_tcp_dgst_header(tcp_conn->rx_hash, hdr,
+ segment->total_copied - ISCSI_DIGEST_SIZE,
+ segment->digest);
+
+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+ return ISCSI_ERR_HDR_DGST;
+ }
+
+ tcp_conn->in.hdr = hdr;
+ return iscsi_tcp_hdr_dissect(conn, hdr);
+}
+
+/**
+ * iscsi_tcp_recv_segment_is_hdr - tests if we are reading in a header
+ * @tcp_conn: iscsi tcp conn
+ *
+ * returns non zero if we are currently processing or setup to process
+ * a header.
+ */
+inline int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn)
+{
+ return tcp_conn->in.segment.done == iscsi_tcp_hdr_recv_done;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_recv_segment_is_hdr);
+
+/**
+ * iscsi_tcp_recv_skb - Process skb
+ * @conn: iscsi connection
+ * @skb: network buffer with header and/or data segment
+ * @offset: offset in skb
+ * @offload: bool indicating if transfer was offloaded
+ *
+ * Will return status of transfer in status. And will return
+ * number of bytes copied.
+ */
+int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+ unsigned int offset, bool offloaded, int *status)
+{
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ struct iscsi_segment *segment = &tcp_conn->in.segment;
+ struct skb_seq_state seq;
+ unsigned int consumed = 0;
+ int rc = 0;
+
+ debug_tcp("in %d bytes\n", skb->len - offset);
+
+ if (unlikely(conn->suspend_rx)) {
+ debug_tcp("conn %d Rx suspended!\n", conn->id);
+ *status = ISCSI_TCP_SUSPENDED;
+ return 0;
+ }
+
+ if (offloaded) {
+ segment->total_copied = segment->total_size;
+ goto segment_done;
+ }
+
+ skb_prepare_seq_read(skb, offset, skb->len, &seq);
+ while (1) {
+ unsigned int avail;
+ const u8 *ptr;
+
+ avail = skb_seq_read(consumed, &ptr, &seq);
+ if (avail == 0) {
+ debug_tcp("no more data avail. Consumed %d\n",
+ consumed);
+ *status = ISCSI_TCP_SKB_DONE;
+ skb_abort_seq_read(&seq);
+ goto skb_done;
+ }
+ BUG_ON(segment->copied >= segment->size);
+
+ debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+ rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+ BUG_ON(rc == 0);
+ consumed += rc;
+
+ if (segment->total_copied >= segment->total_size) {
+ skb_abort_seq_read(&seq);
+ goto segment_done;
+ }
+ }
+
+segment_done:
+ *status = ISCSI_TCP_SEGMENT_DONE;
+ debug_tcp("segment done\n");
+ rc = segment->done(tcp_conn, segment);
+ if (rc != 0) {
+ *status = ISCSI_TCP_CONN_ERR;
+ debug_tcp("Error receiving PDU, errno=%d\n", rc);
+ iscsi_conn_failure(conn, rc);
+ return 0;
+ }
+ /* The done() functions sets up the next segment. */
+
+skb_done:
+ conn->rxdata_octets += consumed;
+ return consumed;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb);
+
+/**
+ * iscsi_tcp_task_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @task: scsi command task
+ * @sc: scsi command
+ */
+int iscsi_tcp_task_init(struct iscsi_task *task)
+{
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct scsi_cmnd *sc = task->sc;
+ int err;
+
+ if (!sc) {
+ /*
+ * mgmt tasks do not have a scatterlist since they come
+ * in from the iscsi interface.
+ */
+ debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
+ task->itt);
+
+ return conn->session->tt->init_pdu(task, 0, task->data_count);
+ }
+
+ BUG_ON(__kfifo_len(tcp_task->r2tqueue));
+ tcp_task->exp_datasn = 0;
+
+ /* Prepare PDU, optionally w/ immediate data */
+ debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
+ conn->id, task->itt, task->imm_count,
+ task->unsol_r2t.data_length);
+
+ err = conn->session->tt->init_pdu(task, 0, task->imm_count);
+ if (err)
+ return err;
+ task->imm_count = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_init);
+
+static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
+{
+ struct iscsi_session *session = task->conn->session;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_r2t_info *r2t = NULL;
+
+ if (iscsi_task_has_unsol_data(task))
+ r2t = &task->unsol_r2t;
+ else {
+ spin_lock_bh(&session->lock);
+ if (tcp_task->r2t) {
+ r2t = tcp_task->r2t;
+ /* Continue with this R2T? */
+ if (r2t->data_length <= r2t->sent) {
+ debug_scsi(" done with r2t %p\n", r2t);
+ __kfifo_put(tcp_task->r2tpool.queue,
+ (void *)&tcp_task->r2t,
+ sizeof(void *));
+ tcp_task->r2t = r2t = NULL;
+ }
+ }
+
+ if (r2t == NULL) {
+ __kfifo_get(tcp_task->r2tqueue,
+ (void *)&tcp_task->r2t, sizeof(void *));
+ r2t = tcp_task->r2t;
+ }
+ spin_unlock_bh(&session->lock);
+ }
+
+ return r2t;
+}
+
+/**
+ * iscsi_tcp_task_xmit - xmit normal PDU task
+ * @task: iscsi command task
+ *
+ * We're expected to return 0 when everything was transmitted succesfully,
+ * -EAGAIN if there's still data in the queue, or != 0 for any other kind
+ * of error.
+ */
+int iscsi_tcp_task_xmit(struct iscsi_task *task)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_r2t_info *r2t;
+ int rc = 0;
+
+flush:
+ /* Flush any pending data first. */
+ rc = session->tt->xmit_pdu(task);
+ if (rc < 0)
+ return rc;
+
+ /* mgmt command */
+ if (!task->sc) {
+ if (task->hdr->itt == RESERVED_ITT)
+ iscsi_put_task(task);
+ return 0;
+ }
+
+ /* Are we done already? */
+ if (task->sc->sc_data_direction != DMA_TO_DEVICE)
+ return 0;
+
+ r2t = iscsi_tcp_get_curr_r2t(task);
+ if (r2t == NULL) {
+ /* Waiting for more R2Ts to arrive. */
+ debug_tcp("no R2Ts yet\n");
+ return 0;
+ }
+
+ rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_DATA_OUT);
+ if (rc)
+ return rc;
+ iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr);
+
+ debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+ r2t, r2t->datasn - 1, task->hdr->itt,
+ r2t->data_offset + r2t->sent, r2t->data_count);
+
+ rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent,
+ r2t->data_count);
+ if (rc)
+ return rc;
+ r2t->sent += r2t->data_count;
+ goto flush;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_xmit);
+
+struct iscsi_cls_conn *
+iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size,
+ uint32_t conn_idx)
+
+{
+ struct iscsi_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_tcp_conn *tcp_conn;
+
+ cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+ /*
+ * due to strange issues with iser these are not set
+ * in iscsi_conn_setup
+ */
+ conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
+
+ tcp_conn = conn->dd_data;
+ tcp_conn->iscsi_conn = conn;
+
+ tcp_conn->dd_data = kzalloc(dd_data_size, GFP_KERNEL);
+ if (!tcp_conn->dd_data) {
+ iscsi_conn_teardown(cls_conn);
+ return NULL;
+ }
+ return cls_conn;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_setup);
+
+void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ kfree(tcp_conn->dd_data);
+ iscsi_conn_teardown(cls_conn);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown);
+
+int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session)
+{
+ int i;
+ int cmd_i;
+
+ /*
+ * initialize per-task: R2T pool and xmit queue
+ */
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_task *task = session->cmds[cmd_i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ /*
+ * pre-allocated x2 as much r2ts to handle race when
+ * target acks DataOut faster than we data_xmit() queues
+ * could replenish r2tqueue.
+ */
+
+ /* R2T pool */
+ if (iscsi_pool_init(&tcp_task->r2tpool,
+ session->max_r2t * 2, NULL,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+
+ /* R2T xmit queue */
+ tcp_task->r2tqueue = kfifo_alloc(
+ session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
+ if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
+ iscsi_pool_free(&tcp_task->r2tpool);
+ goto r2t_alloc_fail;
+ }
+ }
+
+ return 0;
+
+r2t_alloc_fail:
+ for (i = 0; i < cmd_i; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
+ }
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_alloc);
+
+void iscsi_tcp_r2tpool_free(struct iscsi_session *session)
+{
+ int i;
+
+ for (i = 0; i < session->cmds_max; i++) {
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
+ }
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free);
+
+void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt;
+ stats->r2t_pdus = conn->r2t_pdus_cnt;
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_conn_get_stats);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 60a9e6e9384b..dcba267db711 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -29,8 +29,10 @@ struct lpfc_sli2_slim;
#define LPFC_MAX_NS_RETRY 3 /* Number of retry attempts to contact
the NameServer before giving up. */
#define LPFC_CMD_PER_LUN 3 /* max outstanding cmds per lun */
-#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
-#define LPFC_MAX_SG_SEG_CNT 256 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */
+#define LPFC_DEFAULT_PROT_SG_SEG_CNT 4096 /* sg protection elements count */
+#define LPFC_MAX_SG_SEG_CNT 4096 /* sg element count per scsi cmnd */
+#define LPFC_MAX_PROT_SG_SEG_CNT 4096 /* prot sg element count per scsi cmd*/
#define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */
#define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */
#define LPFC_VNAME_LEN 100 /* vport symbolic name length */
@@ -354,8 +356,6 @@ struct lpfc_vport {
uint8_t load_flag;
#define FC_LOADING 0x1 /* HBA in process of loading drvr */
#define FC_UNLOADING 0x2 /* HBA in process of unloading drvr */
- char *vname; /* Application assigned name */
-
/* Vport Config Parameters */
uint32_t cfg_scan_down;
uint32_t cfg_lun_queue_depth;
@@ -376,7 +376,7 @@ struct lpfc_vport {
struct fc_vport *fc_vport;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *debug_disc_trc;
struct dentry *debug_nodelist;
struct dentry *vport_debugfs_root;
@@ -428,6 +428,7 @@ struct lpfc_hba {
#define LPFC_SLI3_VPORT_TEARDOWN 0x04
#define LPFC_SLI3_CRP_ENABLED 0x08
#define LPFC_SLI3_INB_ENABLED 0x10
+#define LPFC_SLI3_BG_ENABLED 0x20
uint32_t iocb_cmd_size;
uint32_t iocb_rsp_size;
@@ -501,12 +502,14 @@ struct lpfc_hba {
uint32_t cfg_poll_tmo;
uint32_t cfg_use_msi;
uint32_t cfg_sg_seg_cnt;
+ uint32_t cfg_prot_sg_seg_cnt;
uint32_t cfg_sg_dma_buf_size;
uint64_t cfg_soft_wwnn;
uint64_t cfg_soft_wwpn;
uint32_t cfg_hba_queue_depth;
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
+ uint32_t cfg_enable_bg;
lpfc_vpd_t vpd; /* vital product data */
@@ -572,6 +575,9 @@ struct lpfc_hba {
uint64_t fc4InputRequests;
uint64_t fc4OutputRequests;
uint64_t fc4ControlRequests;
+ uint64_t bg_guard_err_cnt;
+ uint64_t bg_apptag_err_cnt;
+ uint64_t bg_reftag_err_cnt;
struct lpfc_sysfs_mbox sysfs_mbox;
@@ -594,6 +600,8 @@ struct lpfc_hba {
struct fc_host_statistics link_stats;
enum intr_type_t intr_type;
+ uint32_t intr_mode;
+#define LPFC_INTR_ERROR 0xFFFFFFFF
struct msix_entry msix_entries[LPFC_MSIX_VECTORS];
struct list_head port_list;
@@ -613,12 +621,14 @@ struct lpfc_hba {
unsigned long last_rsrc_error_time;
unsigned long last_ramp_down_time;
unsigned long last_ramp_up_time;
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct dentry *hba_debugfs_root;
atomic_t debugfs_vport_count;
struct dentry *debug_hbqinfo;
struct dentry *debug_dumpHostSlim;
struct dentry *debug_dumpHBASlim;
+ struct dentry *debug_dumpData; /* BlockGuard BPL*/
+ struct dentry *debug_dumpDif; /* BlockGuard BPL*/
struct dentry *debug_slow_ring_trc;
struct lpfc_debugfs_trc *slow_ring_trc;
atomic_t slow_ring_trc_cnt;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index aa3d6277581d..40cf0f4f327f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -96,6 +96,61 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
+static ssize_t
+lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ if (phba->cfg_enable_bg)
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED)
+ return snprintf(buf, PAGE_SIZE, "BlockGuard Enabled\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Not Supported\n");
+ else
+ return snprintf(buf, PAGE_SIZE,
+ "BlockGuard Disabled\n");
+}
+
+static ssize_t
+lpfc_bg_guard_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_guard_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_apptag_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_apptag_err_cnt);
+}
+
+static ssize_t
+lpfc_bg_reftag_err_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ return snprintf(buf, PAGE_SIZE, "%llu\n",
+ (unsigned long long)phba->bg_reftag_err_cnt);
+}
+
/**
* lpfc_info_show: Return some pci info about the host in ascii.
* @dev: class converted to a Scsi_host structure.
@@ -1485,6 +1540,10 @@ lpfc_vport_param_store(name)\
static DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
lpfc_##name##_show, lpfc_##name##_store)
+static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
+static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
+static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
+static DEVICE_ATTR(bg_reftag_err, S_IRUGO, lpfc_bg_reftag_err_show, NULL);
static DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL);
static DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL);
static DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL);
@@ -1970,6 +2029,7 @@ static DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
# LOG_LINK_EVENT 0x10 Link events
# LOG_FCP 0x40 FCP traffic history
# LOG_NODE 0x80 Node table events
+# LOG_BG 0x200 BlockBuard events
# LOG_MISC 0x400 Miscellaneous events
# LOG_SLI 0x800 SLI events
# LOG_FCP_ERROR 0x1000 Only log FCP errors
@@ -2769,6 +2829,42 @@ LPFC_ATTR_R(enable_hba_reset, 1, 0, 1, "Enable HBA resets from the driver.");
LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
/*
+# lpfc_enable_bg: Enable BlockGuard (Emulex's Implementation of T10-DIF)
+# 0 = BlockGuard disabled (default)
+# 1 = BlockGuard enabled
+# Value range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
+
+
+/*
+# lpfc_prot_mask: i
+# - Bit mask of host protection capabilities used to register with the
+# SCSI mid-layer
+# - Only meaningful if BG is turned on (lpfc_enable_bg=1).
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all profiles.
+#
+*/
+unsigned int lpfc_prot_mask = SHOST_DIX_TYPE0_PROTECTION;
+
+module_param(lpfc_prot_mask, uint, 0);
+MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask");
+
+/*
+# lpfc_prot_guard: i
+# - Bit mask of protection guard types to register with the SCSI mid-layer
+# - Guard types are currently either 1) IP checksum 2) T10-DIF CRC
+# - Allows you to ultimately specify which profiles to use
+# - Default will result in registering capabilities for all guard types
+#
+*/
+unsigned char lpfc_prot_guard = SHOST_DIX_GUARD_IP;
+module_param(lpfc_prot_guard, byte, 0);
+MODULE_PARM_DESC(lpfc_prot_guard, "host protection guard type");
+
+
+/*
* lpfc_sg_seg_cnt: Initial Maximum DMA Segment Count
* This value can be set to values between 64 and 256. The default value is
* 64, but may be increased to allow for larger Max I/O sizes. The scsi layer
@@ -2777,7 +2873,15 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(sg_seg_cnt, LPFC_DEFAULT_SG_SEG_CNT, LPFC_DEFAULT_SG_SEG_CNT,
LPFC_MAX_SG_SEG_CNT, "Max Scatter Gather Segment Count");
+LPFC_ATTR_R(prot_sg_seg_cnt, LPFC_DEFAULT_PROT_SG_SEG_CNT,
+ LPFC_DEFAULT_PROT_SG_SEG_CNT, LPFC_MAX_PROT_SG_SEG_CNT,
+ "Max Protection Scatter Gather Segment Count");
+
struct device_attribute *lpfc_hba_attrs[] = {
+ &dev_attr_bg_info,
+ &dev_attr_bg_guard_err,
+ &dev_attr_bg_apptag_err,
+ &dev_attr_bg_reftag_err,
&dev_attr_info,
&dev_attr_serialnum,
&dev_attr_modeldesc,
@@ -2825,6 +2929,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_poll,
&dev_attr_lpfc_poll_tmo,
&dev_attr_lpfc_use_msi,
+ &dev_attr_lpfc_enable_bg,
&dev_attr_lpfc_soft_wwnn,
&dev_attr_lpfc_soft_wwpn,
&dev_attr_lpfc_soft_wwn_enable,
@@ -2833,6 +2938,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_sg_seg_cnt,
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
+ &dev_attr_lpfc_prot_sg_seg_cnt,
NULL,
};
@@ -3282,25 +3388,28 @@ lpfc_alloc_sysfs_attr(struct lpfc_vport *vport)
int error;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_ctlreg_attr);
- if (error)
+ &sysfs_drvr_stat_data_attr);
+
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (error || vport->port_type == LPFC_NPIV_PORT)
goto out;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_mbox_attr);
+ &sysfs_ctlreg_attr);
if (error)
- goto out_remove_ctlreg_attr;
+ goto out_remove_stat_attr;
error = sysfs_create_bin_file(&shost->shost_dev.kobj,
- &sysfs_drvr_stat_data_attr);
+ &sysfs_mbox_attr);
if (error)
- goto out_remove_mbox_attr;
+ goto out_remove_ctlreg_attr;
return 0;
-out_remove_mbox_attr:
- sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
out_remove_ctlreg_attr:
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
+out_remove_stat_attr:
+ sysfs_remove_bin_file(&shost->shost_dev.kobj,
+ &sysfs_drvr_stat_data_attr);
out:
return error;
}
@@ -3315,6 +3424,9 @@ lpfc_free_sysfs_attr(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
sysfs_remove_bin_file(&shost->shost_dev.kobj,
&sysfs_drvr_stat_data_attr);
+ /* Virtual ports do not need ctrl_reg and mbox */
+ if (vport->port_type == LPFC_NPIV_PORT)
+ return;
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_mbox_attr);
sysfs_remove_bin_file(&shost->shost_dev.kobj, &sysfs_ctlreg_attr);
}
@@ -3792,6 +3904,23 @@ lpfc_show_rport_##field (struct device *dev, \
lpfc_rport_show_function(field, format_string, sz, ) \
static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
+/**
+ * lpfc_set_vport_symbolic_name: Set the vport's symbolic name.
+ * @fc_vport: The fc_vport who's symbolic name has been changed.
+ *
+ * Description:
+ * This function is called by the transport after the @fc_vport's symbolic name
+ * has been changed. This function re-registers the symbolic name with the
+ * switch to propogate the change into the fabric if the vport is active.
+ **/
+static void
+lpfc_set_vport_symbolic_name(struct fc_vport *fc_vport)
+{
+ struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
+
+ if (vport->port_state == LPFC_VPORT_READY)
+ lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
+}
struct fc_function_template lpfc_transport_functions = {
/* fixed attributes the driver supports */
@@ -3801,6 +3930,7 @@ struct fc_function_template lpfc_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3850,6 +3980,10 @@ struct fc_function_template lpfc_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.dd_fcvport_size = sizeof(struct lpfc_vport *),
+
+ .vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
struct fc_function_template lpfc_vport_transport_functions = {
@@ -3860,6 +3994,7 @@ struct fc_function_template lpfc_vport_transport_functions = {
.show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
+ .show_host_symbolic_name = 1,
/* dynamic attributes the driver supports */
.get_host_port_id = lpfc_get_host_port_id,
@@ -3908,6 +4043,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
.terminate_rport_io = lpfc_terminate_rport_io,
.vport_disable = lpfc_vport_disable,
+
+ .set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
};
/**
@@ -3930,13 +4067,12 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
+ lpfc_enable_bg_init(phba, lpfc_enable_bg);
phba->cfg_poll = lpfc_poll;
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
- /* Also reinitialize the host templates with new values. */
- lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
- lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
/*
* Since the sg_tablesize is module parameter, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be dynamically calculated.
@@ -3945,6 +4081,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
sizeof(struct fcp_rsp) +
((phba->cfg_sg_seg_cnt + 2) * sizeof(struct ulp_bde64));
+
+ if (phba->cfg_enable_bg) {
+ phba->cfg_sg_seg_cnt = LPFC_MAX_SG_SEG_CNT;
+ phba->cfg_sg_dma_buf_size +=
+ phba->cfg_prot_sg_seg_cnt * sizeof(struct ulp_bde64);
+ }
+
+ /* Also reinitialize the host templates with new values. */
+ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+ lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt;
+
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 044ef4057d28..07f4976319a5 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -22,6 +22,7 @@ typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -284,12 +285,24 @@ extern void lpfc_debugfs_slow_ring_trc(struct lpfc_hba *, char *, uint32_t,
uint32_t, uint32_t);
extern struct lpfc_hbq_init *lpfc_hbq_defs[];
+/* externs BlockGuard */
+extern char *_dump_buf_data;
+extern unsigned long _dump_buf_data_order;
+extern char *_dump_buf_dif;
+extern unsigned long _dump_buf_dif_order;
+extern spinlock_t _dump_buf_lock;
+extern int _dump_buf_done;
+extern spinlock_t pgcnt_lock;
+extern unsigned int pgcnt;
+extern unsigned int lpfc_prot_mask;
+extern unsigned char lpfc_prot_guard;
+
/* Interface exported by fabric iocb scheduler */
void lpfc_fabric_abort_nport(struct lpfc_nodelist *);
void lpfc_fabric_abort_hba(struct lpfc_hba *);
void lpfc_fabric_block_timeout(unsigned long);
void lpfc_unblock_fabric_iocbs(struct lpfc_hba *);
-void lpfc_adjust_queue_depth(struct lpfc_hba *);
+void lpfc_rampdown_queue_depth(struct lpfc_hba *);
void lpfc_ramp_down_queue_handler(struct lpfc_hba *);
void lpfc_ramp_up_queue_handler(struct lpfc_hba *);
void lpfc_scsi_dev_block(struct lpfc_hba *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 26dae8bae2d1..896c7b0351e5 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -560,18 +560,25 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4], vport->fc_ns_retry);
/* Don't bother processing response if vport is being torn down. */
- if (vport->load_flag & FC_UNLOADING)
+ if (vport->load_flag & FC_UNLOADING) {
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
+ }
if (lpfc_els_chk_latt(vport)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0216 Link event during NS query\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto out;
}
if (lpfc_error_lost_link(irsp)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0226 NS query failed due to link event\n");
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
goto out;
}
if (irsp->ulpStatus) {
@@ -587,6 +594,8 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
if (rc == 0)
goto out;
}
+ if (vport->fc_flag & FC_RSCN_MODE)
+ lpfc_els_flush_rscn(vport);
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0257 GID_FT Query error: 0x%x 0x%x\n",
@@ -1008,8 +1017,10 @@ lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
if (n < size)
n += snprintf(symbol + n, size - n, " VPort-%d", vport->vpi);
- if (n < size && vport->vname)
- n += snprintf(symbol + n, size - n, " VName-%s", vport->vname);
+ if (n < size &&
+ strlen(vport->fc_vport->symbolic_name))
+ n += snprintf(symbol + n, size - n, " VName-%s",
+ vport->fc_vport->symbolic_name);
return n;
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 771920bdde44..b615eda361d5 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -46,7 +46,7 @@
#include "lpfc_compat.h"
#include "lpfc_debugfs.h"
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* debugfs interface
*
@@ -618,7 +618,7 @@ inline void
lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -659,7 +659,7 @@ inline void
lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
uint32_t data1, uint32_t data2, uint32_t data3)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc *dtp;
int index;
@@ -680,7 +680,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
return;
}
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
/**
* lpfc_debugfs_disc_trc_open - Open the discovery trace log.
* @inode: The inode pointer that contains a vport pointer.
@@ -907,6 +907,91 @@ out:
return rc;
}
+static int
+lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_data)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundry */
+ printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+ __func__, _dump_buf_data);
+ debug->buffer = _dump_buf_data;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_data_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static int
+lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug;
+ int rc = -ENOMEM;
+
+ if (!_dump_buf_dif)
+ return -EBUSY;
+
+ debug = kmalloc(sizeof(*debug), GFP_KERNEL);
+ if (!debug)
+ goto out;
+
+ /* Round to page boundry */
+ printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__,
+ _dump_buf_dif, file->f_dentry->d_name.name);
+ debug->buffer = _dump_buf_dif;
+ if (!debug->buffer) {
+ kfree(debug);
+ goto out;
+ }
+
+ debug->len = (1 << _dump_buf_dif_order) << PAGE_SHIFT;
+ file->private_data = debug;
+
+ rc = 0;
+out:
+ return rc;
+}
+
+static ssize_t
+lpfc_debugfs_dumpDataDif_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ /*
+ * The Data/DIF buffers only save one failing IO
+ * The write op is used as a reset mechanism after an IO has
+ * already been saved to the next one can be saved
+ */
+ spin_lock(&_dump_buf_lock);
+
+ memset((void *)_dump_buf_data, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_data_order));
+ memset((void *)_dump_buf_dif, 0,
+ ((1 << PAGE_SHIFT) << _dump_buf_dif_order));
+
+ _dump_buf_done = 0;
+
+ spin_unlock(&_dump_buf_lock);
+
+ return nbytes;
+}
+
+
+
/**
* lpfc_debugfs_nodelist_open - Open the nodelist debugfs file.
* @inode: The inode pointer that contains a vport pointer.
@@ -1035,6 +1120,17 @@ lpfc_debugfs_release(struct inode *inode, struct file *file)
return 0;
}
+static int
+lpfc_debugfs_dumpDataDif_release(struct inode *inode, struct file *file)
+{
+ struct lpfc_debug *debug = file->private_data;
+
+ debug->buffer = NULL;
+ kfree(debug);
+
+ return 0;
+}
+
#undef lpfc_debugfs_op_disc_trc
static struct file_operations lpfc_debugfs_op_disc_trc = {
.owner = THIS_MODULE,
@@ -1080,6 +1176,26 @@ static struct file_operations lpfc_debugfs_op_dumpHostSlim = {
.release = lpfc_debugfs_release,
};
+#undef lpfc_debugfs_op_dumpData
+static struct file_operations lpfc_debugfs_op_dumpData = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpData_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
+#undef lpfc_debugfs_op_dumpDif
+static struct file_operations lpfc_debugfs_op_dumpDif = {
+ .owner = THIS_MODULE,
+ .open = lpfc_debugfs_dumpDif_open,
+ .llseek = lpfc_debugfs_lseek,
+ .read = lpfc_debugfs_read,
+ .write = lpfc_debugfs_dumpDataDif_write,
+ .release = lpfc_debugfs_dumpDataDif_release,
+};
+
#undef lpfc_debugfs_op_slow_ring_trc
static struct file_operations lpfc_debugfs_op_slow_ring_trc = {
.owner = THIS_MODULE,
@@ -1106,7 +1222,7 @@ static atomic_t lpfc_debugfs_hba_count;
inline void
lpfc_debugfs_initialize(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
char name[64];
uint32_t num, i;
@@ -1176,6 +1292,32 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
goto debug_failed;
}
+ /* Setup dumpData */
+ snprintf(name, sizeof(name), "dumpData");
+ phba->debug_dumpData =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpData);
+ if (!phba->debug_dumpData) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0800 Cannot create debugfs dumpData\n");
+ goto debug_failed;
+ }
+
+ /* Setup dumpDif */
+ snprintf(name, sizeof(name), "dumpDif");
+ phba->debug_dumpDif =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dumpDif);
+ if (!phba->debug_dumpDif) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0801 Cannot create debugfs dumpDif\n");
+ goto debug_failed;
+ }
+
+
+
/* Setup slow ring trace */
if (lpfc_debugfs_max_slow_ring_trc) {
num = lpfc_debugfs_max_slow_ring_trc - 1;
@@ -1305,7 +1447,7 @@ debug_failed:
inline void
lpfc_debugfs_terminate(struct lpfc_vport *vport)
{
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_hba *phba = vport->phba;
if (vport->disc_trc) {
@@ -1340,6 +1482,16 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(phba->debug_dumpHostSlim); /* HostSlim */
phba->debug_dumpHostSlim = NULL;
}
+ if (phba->debug_dumpData) {
+ debugfs_remove(phba->debug_dumpData); /* dumpData */
+ phba->debug_dumpData = NULL;
+ }
+
+ if (phba->debug_dumpDif) {
+ debugfs_remove(phba->debug_dumpDif); /* dumpDif */
+ phba->debug_dumpDif = NULL;
+ }
+
if (phba->slow_ring_trc) {
kfree(phba->slow_ring_trc);
phba->slow_ring_trc = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.h b/drivers/scsi/lpfc/lpfc_debugfs.h
index 31e86a55391d..03c7313a1012 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.h
+++ b/drivers/scsi/lpfc/lpfc_debugfs.h
@@ -21,7 +21,7 @@
#ifndef _H_LPFC_DEBUG_FS
#define _H_LPFC_DEBUG_FS
-#ifdef CONFIG_LPFC_DEBUG_FS
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
struct lpfc_debugfs_trc {
char *fmt;
uint32_t data1;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 630bd28fb997..a8f30bdaff69 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -221,7 +221,11 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
/* For ELS_REQUEST64_CR, use the VPI by default */
icmd->ulpContext = vport->vpi;
icmd->ulpCt_h = 0;
- icmd->ulpCt_l = 1;
+ /* The CT field must be 0=INVALID_RPI for the ECHO cmd */
+ if (elscmd == ELS_CMD_ECHO)
+ icmd->ulpCt_l = 0; /* context = invalid RPI */
+ else
+ icmd->ulpCt_l = 1; /* context = VPI */
}
bpl = (struct ulp_bde64 *) pbuflist->virt;
@@ -271,7 +275,8 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
return elsiocb;
els_iocb_free_pbuf_exit:
- lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+ if (expectRsp)
+ lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
kfree(pbuflist);
els_iocb_free_prsp_exit:
@@ -2468,6 +2473,15 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
case IOSTAT_LOCAL_REJECT:
switch ((irsp->un.ulpWord[4] & 0xff)) {
case IOERR_LOOP_OPEN_FAILURE:
+ if (cmd == ELS_CMD_FLOGI) {
+ if (PCI_DEVICE_ID_HORNET ==
+ phba->pcidev->device) {
+ phba->fc_topology = TOPOLOGY_LOOP;
+ phba->pport->fc_myDID = 0;
+ phba->alpa_map[0] = 0;
+ phba->alpa_map[1] = 0;
+ }
+ }
if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0)
delay = 1000;
retry = 1;
@@ -3823,27 +3837,21 @@ lpfc_rscn_payload_check(struct lpfc_vport *vport, uint32_t did)
while (payload_len) {
rscn_did.un.word = be32_to_cpu(*lp++);
payload_len -= sizeof(uint32_t);
- switch (rscn_did.un.b.resv) {
- case 0: /* Single N_Port ID effected */
+ switch (rscn_did.un.b.resv & RSCN_ADDRESS_FORMAT_MASK) {
+ case RSCN_ADDRESS_FORMAT_PORT:
if (ns_did.un.word == rscn_did.un.word)
goto return_did_out;
break;
- case 1: /* Whole N_Port Area effected */
+ case RSCN_ADDRESS_FORMAT_AREA:
if ((ns_did.un.b.domain == rscn_did.un.b.domain)
&& (ns_did.un.b.area == rscn_did.un.b.area))
goto return_did_out;
break;
- case 2: /* Whole N_Port Domain effected */
+ case RSCN_ADDRESS_FORMAT_DOMAIN:
if (ns_did.un.b.domain == rscn_did.un.b.domain)
goto return_did_out;
break;
- default:
- /* Unknown Identifier in RSCN node */
- lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY,
- "0217 Unknown Identifier in "
- "RSCN payload Data: x%x\n",
- rscn_did.un.word);
- case 3: /* Whole Fabric effected */
+ case RSCN_ADDRESS_FORMAT_FABRIC:
goto return_did_out;
}
}
@@ -3887,6 +3895,49 @@ lpfc_rscn_recovery_check(struct lpfc_vport *vport)
}
/**
+ * lpfc_send_rscn_event: Send an RSCN event to management application.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ *
+ * lpfc_send_rscn_event sends an RSCN netlink event to management
+ * applications.
+ */
+static void
+lpfc_send_rscn_event(struct lpfc_vport *vport,
+ struct lpfc_iocbq *cmdiocb)
+{
+ struct lpfc_dmabuf *pcmd;
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ uint32_t *payload_ptr;
+ uint32_t payload_len;
+ struct lpfc_rscn_event_header *rscn_event_data;
+
+ pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+ payload_ptr = (uint32_t *) pcmd->virt;
+ payload_len = be32_to_cpu(*payload_ptr & ~ELS_CMD_MASK);
+
+ rscn_event_data = kmalloc(sizeof(struct lpfc_rscn_event_header) +
+ payload_len, GFP_KERNEL);
+ if (!rscn_event_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0147 Failed to allocate memory for RSCN event\n");
+ return;
+ }
+ rscn_event_data->event_type = FC_REG_RSCN_EVENT;
+ rscn_event_data->payload_length = payload_len;
+ memcpy(rscn_event_data->rscn_payload, payload_ptr,
+ payload_len);
+
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header) + payload_len,
+ (char *)rscn_event_data,
+ LPFC_NL_VENDOR_ID);
+
+ kfree(rscn_event_data);
+}
+
+/**
* lpfc_els_rcv_rscn: Process an unsolicited rscn iocb.
* @vport: pointer to a host virtual N_Port data structure.
* @cmdiocb: pointer to lpfc command iocb data structure.
@@ -3933,6 +3984,10 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
"0214 RSCN received Data: x%x x%x x%x x%x\n",
vport->fc_flag, payload_len, *lp,
vport->fc_rscn_id_cnt);
+
+ /* Send an RSCN event to the management application */
+ lpfc_send_rscn_event(vport, cmdiocb);
+
for (i = 0; i < payload_len/sizeof(uint32_t); i++)
fc_host_post_event(shost, fc_get_event_number(),
FCH_EVT_RSCN, lp[i]);
@@ -4884,10 +4939,6 @@ lpfc_els_timeout_handler(struct lpfc_vport *vport)
uint32_t timeout;
uint32_t remote_ID = 0xffffffff;
- /* If the timer is already canceled do nothing */
- if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
- return;
- }
spin_lock_irq(&phba->hbalock);
timeout = (uint32_t)(phba->fc_ratov << 1);
@@ -5128,7 +5179,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(lsrjt_event),
(char *)&lsrjt_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
if ((rspiocbp->iocb.ulpStatus == IOSTAT_NPORT_BSY) ||
@@ -5146,7 +5197,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
fc_get_event_number(),
sizeof(fabric_event),
(char *)&fabric_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
return;
}
@@ -5164,32 +5215,68 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba,
static void
lpfc_send_els_event(struct lpfc_vport *vport,
struct lpfc_nodelist *ndlp,
- uint32_t cmd)
+ uint32_t *payload)
{
- struct lpfc_els_event_header els_data;
+ struct lpfc_els_event_header *els_data = NULL;
+ struct lpfc_logo_event *logo_data = NULL;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
- els_data.event_type = FC_REG_ELS_EVENT;
- switch (cmd) {
+ if (*payload == ELS_CMD_LOGO) {
+ logo_data = kmalloc(sizeof(struct lpfc_logo_event), GFP_KERNEL);
+ if (!logo_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0148 Failed to allocate memory "
+ "for LOGO event\n");
+ return;
+ }
+ els_data = &logo_data->header;
+ } else {
+ els_data = kmalloc(sizeof(struct lpfc_els_event_header),
+ GFP_KERNEL);
+ if (!els_data) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0149 Failed to allocate memory "
+ "for ELS event\n");
+ return;
+ }
+ }
+ els_data->event_type = FC_REG_ELS_EVENT;
+ switch (*payload) {
case ELS_CMD_PLOGI:
- els_data.subcategory = LPFC_EVENT_PLOGI_RCV;
+ els_data->subcategory = LPFC_EVENT_PLOGI_RCV;
break;
case ELS_CMD_PRLO:
- els_data.subcategory = LPFC_EVENT_PRLO_RCV;
+ els_data->subcategory = LPFC_EVENT_PRLO_RCV;
break;
case ELS_CMD_ADISC:
- els_data.subcategory = LPFC_EVENT_ADISC_RCV;
+ els_data->subcategory = LPFC_EVENT_ADISC_RCV;
+ break;
+ case ELS_CMD_LOGO:
+ els_data->subcategory = LPFC_EVENT_LOGO_RCV;
+ /* Copy the WWPN in the LOGO payload */
+ memcpy(logo_data->logo_wwpn, &payload[2],
+ sizeof(struct lpfc_name));
break;
default:
return;
}
- memcpy(els_data.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
- memcpy(els_data.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
- fc_host_post_vendor_event(shost,
- fc_get_event_number(),
- sizeof(els_data),
- (char *)&els_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ memcpy(els_data->wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name));
+ memcpy(els_data->wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name));
+ if (*payload == ELS_CMD_LOGO) {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_logo_event),
+ (char *)logo_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(logo_data);
+ } else {
+ fc_host_post_vendor_event(shost,
+ fc_get_event_number(),
+ sizeof(struct lpfc_els_event_header),
+ (char *)els_data,
+ LPFC_NL_VENDOR_ID);
+ kfree(els_data);
+ }
return;
}
@@ -5296,7 +5383,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
phba->fc_stat.elsRcvPLOGI++;
ndlp = lpfc_plogi_confirm_nport(phba, payload, ndlp);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -5334,6 +5421,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvLOGO++;
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5346,7 +5434,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
did, vport->port_state, ndlp->nlp_flag);
phba->fc_stat.elsRcvPRLO++;
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
break;
@@ -5364,7 +5452,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
"RCV ADISC: did:x%x/ste:x%x flg:x%x",
did, vport->port_state, ndlp->nlp_flag);
- lpfc_send_els_event(vport, ndlp, cmd);
+ lpfc_send_els_event(vport, ndlp, payload);
phba->fc_stat.elsRcvADISC++;
if (vport->port_state < LPFC_DISC_AUTH) {
rjt_err = LSRJT_UNABLE_TPC;
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index a1a70d9ffc2a..8c64494444bf 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -350,7 +350,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
evt_data_size = sizeof(fast_evt_data->un.
read_check_error);
} else if ((evt_sub_category == LPFC_EVENT_FABRIC_BUSY) ||
- (evt_sub_category == IOSTAT_NPORT_BSY)) {
+ (evt_sub_category == LPFC_EVENT_PORT_BUSY)) {
evt_data = (char *) &fast_evt_data->un.fabric_evt;
evt_data_size = sizeof(fast_evt_data->un.fabric_evt);
} else {
@@ -387,7 +387,7 @@ lpfc_send_fastpath_evt(struct lpfc_hba *phba,
fc_get_event_number(),
evt_data_size,
evt_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_free_fast_evt(phba, fast_evt_data);
return;
@@ -585,20 +585,25 @@ lpfc_do_work(void *p)
set_user_nice(current, -20);
phba->data_flags = 0;
- while (1) {
+ while (!kthread_should_stop()) {
/* wait and check worker queue activities */
rc = wait_event_interruptible(phba->work_waitq,
(test_and_clear_bit(LPFC_DATA_READY,
&phba->data_flags)
|| kthread_should_stop()));
- BUG_ON(rc);
-
- if (kthread_should_stop())
+ /* Signal wakeup shall terminate the worker thread */
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+ "0433 Wakeup on signal: rc=x%x\n", rc);
break;
+ }
/* Attend pending lpfc data processing */
lpfc_work_done(phba);
}
+ phba->worker_thread = NULL;
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "0432 Worker thread stopped.\n");
return 0;
}
@@ -1852,6 +1857,32 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
NLP_STE_UNUSED_NODE);
}
+/**
+ * lpfc_initialize_node: Initialize all fields of node object.
+ * @vport: Pointer to Virtual Port object.
+ * @ndlp: Pointer to FC node object.
+ * @did: FC_ID of the node.
+ * This function is always called when node object need to
+ * be initialized. It initializes all the fields of the node
+ * object.
+ **/
+static inline void
+lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ uint32_t did)
+{
+ INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
+ INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
+ init_timer(&ndlp->nlp_delayfunc);
+ ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+ ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+ ndlp->nlp_DID = did;
+ ndlp->vport = vport;
+ ndlp->nlp_sid = NLP_NO_SID;
+ kref_init(&ndlp->kref);
+ NLP_INT_NODE_ACT(ndlp);
+ atomic_set(&ndlp->cmd_pending, 0);
+ ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+}
struct lpfc_nodelist *
lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
@@ -1892,17 +1923,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* re-initialize ndlp except of ndlp linked list pointer */
memset((((char *)ndlp) + sizeof (struct list_head)), 0,
sizeof (struct lpfc_nodelist) - sizeof (struct list_head));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
- /* ndlp management re-initialize */
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
+ lpfc_initialize_node(vport, ndlp, did);
spin_unlock_irqrestore(&phba->ndlp_lock, flags);
@@ -3116,19 +3137,9 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t did)
{
memset(ndlp, 0, sizeof (struct lpfc_nodelist));
- INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
- INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp);
- init_timer(&ndlp->nlp_delayfunc);
- ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
- ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
- ndlp->nlp_DID = did;
- ndlp->vport = vport;
- ndlp->nlp_sid = NLP_NO_SID;
+
+ lpfc_initialize_node(vport, ndlp, did);
INIT_LIST_HEAD(&ndlp->nlp_listp);
- kref_init(&ndlp->kref);
- NLP_INT_NODE_ACT(ndlp);
- atomic_set(&ndlp->cmd_pending, 0);
- ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE,
"node init: did:x%x",
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 5de5dabbbee6..4168c7b498b8 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -65,6 +65,9 @@
#define SLI3_IOCB_RSP_SIZE 64
+/* vendor ID used in SCSI netlink calls */
+#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
+
/* Common Transport structures and definitions */
union CtRevisionId {
@@ -866,6 +869,12 @@ typedef struct _D_ID { /* Structure is in Big Endian format */
} un;
} D_ID;
+#define RSCN_ADDRESS_FORMAT_PORT 0x0
+#define RSCN_ADDRESS_FORMAT_AREA 0x1
+#define RSCN_ADDRESS_FORMAT_DOMAIN 0x2
+#define RSCN_ADDRESS_FORMAT_FABRIC 0x3
+#define RSCN_ADDRESS_FORMAT_MASK 0x3
+
/*
* Structure to define all ELS Payload types
*/
@@ -1535,6 +1544,108 @@ typedef struct ULP_BDL { /* SLI-2 */
uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */
} ULP_BDL;
+/*
+ * BlockGuard Definitions
+ */
+
+enum lpfc_protgrp_type {
+ LPFC_PG_TYPE_INVALID = 0, /* used to indicate errors */
+ LPFC_PG_TYPE_NO_DIF, /* no DIF data pointed to by prot grp */
+ LPFC_PG_TYPE_EMBD_DIF, /* DIF is embedded (inline) with data */
+ LPFC_PG_TYPE_DIF_BUF /* DIF has its own scatter/gather list */
+};
+
+/* PDE Descriptors */
+#define LPFC_PDE1_DESCRIPTOR 0x81
+#define LPFC_PDE2_DESCRIPTOR 0x82
+#define LPFC_PDE3_DESCRIPTOR 0x83
+
+/* BlockGuard Profiles */
+enum lpfc_bg_prof_codes {
+ LPFC_PROF_INVALID,
+ LPFC_PROF_A1 = 128, /* Full Protection */
+ LPFC_PROF_A2, /* Disabled Protection Checks:A2~A4 */
+ LPFC_PROF_A3,
+ LPFC_PROF_A4,
+ LPFC_PROF_B1, /* Embedded DIFs: B1~B3 */
+ LPFC_PROF_B2,
+ LPFC_PROF_B3,
+ LPFC_PROF_C1, /* Separate DIFs: C1~C3 */
+ LPFC_PROF_C2,
+ LPFC_PROF_C3,
+ LPFC_PROF_D1, /* Full Protection */
+ LPFC_PROF_D2, /* Partial Protection & Check Disabling */
+ LPFC_PROF_D3,
+ LPFC_PROF_E1, /* E1~E4:out - check-only, in - update apptag */
+ LPFC_PROF_E2,
+ LPFC_PROF_E3,
+ LPFC_PROF_E4,
+ LPFC_PROF_F1, /* Full Translation - F1 Prot Descriptor */
+ /* F1 Translation BDE */
+ LPFC_PROF_ANT1, /* TCP checksum, DIF inline with data buffers */
+ LPFC_PROF_AST1, /* TCP checksum, DIF split from data buffer */
+ LPFC_PROF_ANT2,
+ LPFC_PROF_AST2
+};
+
+/* BlockGuard error-control defines */
+#define BG_EC_STOP_ERR 0x00
+#define BG_EC_CONT_ERR 0x01
+#define BG_EC_IGN_UNINIT_STOP_ERR 0x10
+#define BG_EC_IGN_UNINIT_CONT_ERR 0x11
+
+/* PDE (Protection Descriptor Entry) word 0 bit masks and shifts */
+#define PDE_DESC_TYPE_MASK 0xff000000
+#define PDE_DESC_TYPE_SHIFT 24
+#define PDE_BG_PROFILE_MASK 0x00ff0000
+#define PDE_BG_PROFILE_SHIFT 16
+#define PDE_BLOCK_LEN_MASK 0x0000fffc
+#define PDE_BLOCK_LEN_SHIFT 2
+#define PDE_ERR_CTRL_MASK 0x00000003
+#define PDE_ERR_CTRL_SHIFT 0
+/* PDE word 1 bit masks and shifts */
+#define PDE_APPTAG_MASK_MASK 0xffff0000
+#define PDE_APPTAG_MASK_SHIFT 16
+#define PDE_APPTAG_VAL_MASK 0x0000ffff
+#define PDE_APPTAG_VAL_SHIFT 0
+struct lpfc_pde {
+ uint32_t parms; /* bitfields of descriptor, prof, len, and ec */
+ uint32_t apptag; /* bitfields of app tag maskand app tag value */
+ uint32_t reftag; /* reference tag occupying all 32 bits */
+};
+
+/* inline function to set fields in parms of PDE */
+static inline void
+lpfc_pde_set_bg_parms(struct lpfc_pde *p, u8 desc, u8 prof, u16 len, u8 ec)
+{
+ uint32_t *wp = &p->parms;
+
+ /* spec indicates that adapter appends two 0's to length field */
+ len = len >> 2;
+
+ *wp &= 0;
+ *wp |= ((desc << PDE_DESC_TYPE_SHIFT) & PDE_DESC_TYPE_MASK);
+ *wp |= ((prof << PDE_BG_PROFILE_SHIFT) & PDE_BG_PROFILE_MASK);
+ *wp |= ((len << PDE_BLOCK_LEN_SHIFT) & PDE_BLOCK_LEN_MASK);
+ *wp |= ((ec << PDE_ERR_CTRL_SHIFT) & PDE_ERR_CTRL_MASK);
+ *wp = le32_to_cpu(*wp);
+}
+
+/* inline function to set apptag and reftag fields of PDE */
+static inline void
+lpfc_pde_set_dif_parms(struct lpfc_pde *p, u16 apptagmask, u16 apptagval,
+ u32 reftag)
+{
+ uint32_t *wp = &p->apptag;
+ *wp &= 0;
+ *wp |= ((apptagmask << PDE_APPTAG_MASK_SHIFT) & PDE_APPTAG_MASK_MASK);
+ *wp |= ((apptagval << PDE_APPTAG_VAL_SHIFT) & PDE_APPTAG_VAL_MASK);
+ *wp = le32_to_cpu(*wp);
+ wp = &p->reftag;
+ *wp = le32_to_cpu(reftag);
+}
+
+
/* Structure for MB Command LOAD_SM and DOWN_LOAD */
typedef struct {
@@ -2359,6 +2470,30 @@ typedef struct {
#define DMP_RSP_OFFSET 0x14 /* word 5 contains first word of rsp */
#define DMP_RSP_SIZE 0x6C /* maximum of 27 words of rsp data */
+#define WAKE_UP_PARMS_REGION_ID 4
+#define WAKE_UP_PARMS_WORD_SIZE 15
+
+/* Option rom version structure */
+struct prog_id {
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint8_t type;
+ uint8_t id;
+ uint32_t ver:4; /* Major Version */
+ uint32_t rev:4; /* Revision */
+ uint32_t lev:2; /* Level */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t num:4; /* number after dist type */
+#else /* __LITTLE_ENDIAN_BITFIELD */
+ uint32_t num:4; /* number after dist type */
+ uint32_t dist:2; /* Dist Type */
+ uint32_t lev:2; /* Level */
+ uint32_t rev:4; /* Revision */
+ uint32_t ver:4; /* Major Version */
+ uint8_t id;
+ uint8_t type;
+#endif
+};
+
/* Structure for MB Command UPDATE_CFG (0x1B) */
struct update_cfg_var {
@@ -2552,11 +2687,19 @@ typedef struct {
uint32_t pcbLow; /* bit 31:0 of memory based port config block */
uint32_t pcbHigh; /* bit 63:32 of memory based port config block */
- uint32_t hbainit[6];
+ uint32_t hbainit[5];
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+#else /* __LITTLE_ENDIAN */
+ uint32_t rsvd : 31; /* least significant 31 bits of word 9 */
+ uint32_t hps : 1; /* bit 31 word9 Host Pointer in slim */
+#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd : 24; /* Reserved */
- uint32_t cmv : 1; /* Configure Max VPIs */
+ uint32_t rsvd1 : 23; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t cmv : 1; /* Configure Max VPIs */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t chbs : 1; /* Cofigure Host Backing store */
@@ -2573,10 +2716,12 @@ typedef struct {
uint32_t csah : 1; /* Configure Synchronous Abort Handling */
uint32_t ccrp : 1; /* Config Command Ring Polling */
uint32_t cmv : 1; /* Configure Max VPIs */
- uint32_t rsvd : 24; /* Reserved */
+ uint32_t cbg : 1; /* Configure BlockGuard */
+ uint32_t rsvd1 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t rsvd2 : 23; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
uint32_t gmv : 1; /* Grant Max VPIs */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
@@ -2594,7 +2739,8 @@ typedef struct {
uint32_t gsah : 1; /* Grant Synchronous Abort Handling */
uint32_t gcrp : 1; /* Grant Command Ring Polling */
uint32_t gmv : 1; /* Grant Max VPIs */
- uint32_t rsvd2 : 24; /* Reserved */
+ uint32_t gbg : 1; /* Grant BlockGuard */
+ uint32_t rsvd2 : 23; /* Reserved */
#endif
#ifdef __BIG_ENDIAN_BITFIELD
@@ -3214,6 +3360,94 @@ struct que_xri64cx_ext_fields {
struct lpfc_hbq_entry buff[5];
};
+struct sli3_bg_fields {
+ uint32_t filler[6]; /* word 8-13 in IOCB */
+ uint32_t bghm; /* word 14 - BlockGuard High Water Mark */
+/* Bitfields for bgstat (BlockGuard Status - word 15 of IOCB) */
+#define BGS_BIDIR_BG_PROF_MASK 0xff000000
+#define BGS_BIDIR_BG_PROF_SHIFT 24
+#define BGS_BIDIR_ERR_COND_FLAGS_MASK 0x003f0000
+#define BGS_BIDIR_ERR_COND_SHIFT 16
+#define BGS_BG_PROFILE_MASK 0x0000ff00
+#define BGS_BG_PROFILE_SHIFT 8
+#define BGS_INVALID_PROF_MASK 0x00000020
+#define BGS_INVALID_PROF_SHIFT 5
+#define BGS_UNINIT_DIF_BLOCK_MASK 0x00000010
+#define BGS_UNINIT_DIF_BLOCK_SHIFT 4
+#define BGS_HI_WATER_MARK_PRESENT_MASK 0x00000008
+#define BGS_HI_WATER_MARK_PRESENT_SHIFT 3
+#define BGS_REFTAG_ERR_MASK 0x00000004
+#define BGS_REFTAG_ERR_SHIFT 2
+#define BGS_APPTAG_ERR_MASK 0x00000002
+#define BGS_APPTAG_ERR_SHIFT 1
+#define BGS_GUARD_ERR_MASK 0x00000001
+#define BGS_GUARD_ERR_SHIFT 0
+ uint32_t bgstat; /* word 15 - BlockGuard Status */
+};
+
+static inline uint32_t
+lpfc_bgs_get_bidir_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_BG_PROF_MASK) >>
+ BGS_BIDIR_BG_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bidir_err_cond(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BIDIR_ERR_COND_FLAGS_MASK) >>
+ BGS_BIDIR_ERR_COND_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_bg_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_BG_PROFILE_MASK) >>
+ BGS_BG_PROFILE_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_invalid_prof(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_INVALID_PROF_MASK) >>
+ BGS_INVALID_PROF_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_uninit_dif_block(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_UNINIT_DIF_BLOCK_MASK) >>
+ BGS_UNINIT_DIF_BLOCK_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_hi_water_mark_present(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_HI_WATER_MARK_PRESENT_MASK) >>
+ BGS_HI_WATER_MARK_PRESENT_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_reftag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_REFTAG_ERR_MASK) >>
+ BGS_REFTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_apptag_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_APPTAG_ERR_MASK) >>
+ BGS_APPTAG_ERR_SHIFT;
+}
+
+static inline uint32_t
+lpfc_bgs_get_guard_err(uint32_t bgstat)
+{
+ return (le32_to_cpu(bgstat) & BGS_GUARD_ERR_MASK) >>
+ BGS_GUARD_ERR_SHIFT;
+}
+
#define LPFC_EXT_DATA_BDE_COUNT 3
struct fcp_irw_ext {
uint32_t io_tag64_low;
@@ -3322,6 +3556,9 @@ typedef struct _IOCB { /* IOCB structure */
struct que_xri64cx_ext_fields que_xri64cx_ext_words;
struct fcp_irw_ext fcp_ext;
uint32_t sli3Words[24]; /* 96 extra bytes for SLI-3 */
+
+ /* words 8-15 for BlockGuard */
+ struct sli3_bg_fields sli3_bg;
} unsli3;
#define ulpCt_h ulpXS
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 909be3301bba..4c77038c8f1c 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -45,6 +45,12 @@
#include "lpfc_vport.h"
#include "lpfc_version.h"
+char *_dump_buf_data;
+unsigned long _dump_buf_data_order;
+char *_dump_buf_dif;
+unsigned long _dump_buf_dif_order;
+spinlock_t _dump_buf_lock;
+
static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
static int lpfc_post_rcv_buf(struct lpfc_hba *);
@@ -236,6 +242,51 @@ lpfc_config_async_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
}
/**
+ * lpfc_dump_wakeup_param_cmpl: Completion handler for dump memory mailbox
+ * command used for getting wake up parameters.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmboxq: pointer to the driver internal queue element for mailbox command.
+ *
+ * This is the completion handler for dump mailbox command for getting
+ * wake up parameters. When this command complete, the response contain
+ * Option rom version of the HBA. This function translate the version number
+ * into a human readable string and store it in OptionROMVersion.
+ **/
+static void
+lpfc_dump_wakeup_param_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq)
+{
+ struct prog_id *prg;
+ uint32_t prog_id_word;
+ char dist = ' ';
+ /* character array used for decoding dist type. */
+ char dist_char[] = "nabx";
+
+ if (pmboxq->mb.mbxStatus != MBX_SUCCESS) {
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+ }
+
+ prg = (struct prog_id *) &prog_id_word;
+
+ /* word 7 contain option rom version */
+ prog_id_word = pmboxq->mb.un.varWords[7];
+
+ /* Decode the Option rom version word to a readable string */
+ if (prg->dist < 4)
+ dist = dist_char[prg->dist];
+
+ if ((prg->dist == 3) && (prg->num == 0))
+ sprintf(phba->OptionROMVersion, "%d.%d%d",
+ prg->ver, prg->rev, prg->lev);
+ else
+ sprintf(phba->OptionROMVersion, "%d.%d%d%c%d",
+ prg->ver, prg->rev, prg->lev,
+ dist, prg->num);
+ mempool_free(pmboxq, phba->mbox_mem_pool);
+ return;
+}
+
+/**
* lpfc_config_port_post: Perform lpfc initialization after config port.
* @phba: pointer to lpfc hba data structure.
*
@@ -482,6 +533,20 @@ lpfc_config_port_post(struct lpfc_hba *phba)
rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
+
+ /* Get Option rom version */
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ lpfc_dump_wakeup_param(phba, pmb);
+ pmb->mbox_cmpl = lpfc_dump_wakeup_param_cmpl;
+ pmb->vport = phba->pport;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+
+ if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
+ "to get Option ROM version status x%x\n.", rc);
+ mempool_free(pmb, phba->mbox_mem_pool);
+ }
+
return 0;
}
@@ -686,11 +751,6 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
return;
spin_lock_irq(&phba->pport->work_port_lock);
- /* If the timer is already canceled do nothing */
- if (!(phba->pport->work_port_events & WORKER_HB_TMO)) {
- spin_unlock_irq(&phba->pport->work_port_lock);
- return;
- }
if (time_after(phba->last_completion_time + LPFC_HB_MBOX_INTERVAL * HZ,
jiffies)) {
@@ -833,8 +893,7 @@ lpfc_handle_eratt(struct lpfc_hba *phba)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(board_event),
(char *) &board_event,
- SCSI_NL_VID_TYPE_PCI
- | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (phba->work_hs & HS_FFER6) {
/* Re-establishing Link */
@@ -1984,6 +2043,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
shost->max_lun = vport->cfg_max_luns;
shost->this_id = -1;
shost->max_cmd_len = 16;
+
/*
* Set initial can_queue value since 0 is no longer supported and
* scsi_add_host will fail. This will be adjusted later based on the
@@ -2042,8 +2102,6 @@ destroy_port(struct lpfc_vport *vport)
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
struct lpfc_hba *phba = vport->phba;
- kfree(vport->vname);
-
lpfc_debugfs_terminate(vport);
fc_remove_host(shost);
scsi_remove_host(shost);
@@ -2226,8 +2284,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
ARRAY_SIZE(phba->msix_entries));
if (rc) {
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0420 Enable MSI-X failed (%d), continuing "
- "with MSI\n", rc);
+ "0420 PCI enable MSI-X failed (%d)\n", rc);
goto msi_fail_out;
} else
for (i = 0; i < LPFC_MSIX_VECTORS; i++)
@@ -2244,9 +2301,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
rc = request_irq(phba->msix_entries[0].vector, &lpfc_sp_intr_handler,
IRQF_SHARED, LPFC_SP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0421 MSI-X slow-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto msi_fail_out;
}
@@ -2255,9 +2312,9 @@ lpfc_enable_msix(struct lpfc_hba *phba)
IRQF_SHARED, LPFC_FP_DRIVER_HANDLER_NAME, phba);
if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0429 MSI-X fast-path request_irq failed "
- "(%d), continuing with MSI\n", rc);
+ "(%d)\n", rc);
goto irq_fail_out;
}
@@ -2278,7 +2335,7 @@ lpfc_enable_msix(struct lpfc_hba *phba)
goto mbx_fail_out;
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"0351 Config MSI mailbox command failed, "
"mbxCmd x%x, mbxStatus x%x\n",
pmb->mb.mbxCommand, pmb->mb.mbxStatus);
@@ -2327,6 +2384,195 @@ lpfc_disable_msix(struct lpfc_hba *phba)
}
/**
+ * lpfc_enable_msi: Enable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable the MSI interrupt mode. The kernel
+ * function pci_enable_msi() is called to enable the MSI vector. The
+ * device driver is responsible for calling the request_irq() to register
+ * MSI vector with a interrupt the handler, which is done in this function.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ */
+static int
+lpfc_enable_msi(struct lpfc_hba *phba)
+{
+ int rc;
+
+ rc = pci_enable_msi(phba->pcidev);
+ if (!rc)
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0462 PCI enable MSI mode success.\n");
+ else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0471 PCI enable MSI mode failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (rc) {
+ pci_disable_msi(phba->pcidev);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
+ "0478 MSI request_irq failed (%d)\n", rc);
+ }
+ return rc;
+}
+
+/**
+ * lpfc_disable_msi: Disable MSI interrupt mode.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable the MSI interrupt mode. The driver
+ * calls free_irq() on MSI vector it has done request_irq() on before
+ * calling pci_disable_msi(). Failure to do so results in a BUG_ON() and
+ * a device will be left with MSI enabled and leaks its vector.
+ */
+
+static void
+lpfc_disable_msi(struct lpfc_hba *phba)
+{
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msi(phba->pcidev);
+ return;
+}
+
+/**
+ * lpfc_log_intr_mode: Log the active interrupt mode
+ * @phba: pointer to lpfc hba data structure.
+ * @intr_mode: active interrupt mode adopted.
+ *
+ * This routine it invoked to log the currently used active interrupt mode
+ * to the device.
+ */
+static void
+lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
+{
+ switch (intr_mode) {
+ case 0:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0470 Enable INTx interrupt mode.\n");
+ break;
+ case 1:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0481 Enabled MSI interrupt mode.\n");
+ break;
+ case 2:
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0480 Enabled MSI-X interrupt mode.\n");
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0482 Illegal interrupt mode.\n");
+ break;
+ }
+ return;
+}
+
+static void
+lpfc_stop_port(struct lpfc_hba *phba)
+{
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
+
+ /* Reset some HBA SLI setup states */
+ lpfc_stop_phba_timers(phba);
+ phba->pport->work_port_events = 0;
+
+ return;
+}
+
+/**
+ * lpfc_enable_intr: Enable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to enable device interrupt and associate driver's
+ * interrupt handler(s) to interrupt vector(s). Depends on the interrupt
+ * mode configured to the driver, the driver will try to fallback from the
+ * configured interrupt mode to an interrupt mode which is supported by the
+ * platform, kernel, and device in the order of: MSI-X -> MSI -> IRQ.
+ *
+ * Return codes
+ * 0 - sucessful
+ * other values - error
+ **/
+static uint32_t
+lpfc_enable_intr(struct lpfc_hba *phba, uint32_t cfg_mode)
+{
+ uint32_t intr_mode = LPFC_INTR_ERROR;
+ int retval;
+
+ if (cfg_mode == 2) {
+ /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
+ retval = lpfc_sli_config_port(phba, 3);
+ if (!retval) {
+ /* Now, try to enable MSI-X interrupt mode */
+ retval = lpfc_enable_msix(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI-X mode */
+ phba->intr_type = MSIX;
+ intr_mode = 2;
+ }
+ }
+ }
+
+ /* Fallback to MSI if MSI-X initialization failed */
+ if (cfg_mode >= 1 && phba->intr_type == NONE) {
+ retval = lpfc_enable_msi(phba);
+ if (!retval) {
+ /* Indicate initialization to MSI mode */
+ phba->intr_type = MSI;
+ intr_mode = 1;
+ }
+ }
+
+ /* Fallback to INTx if both MSI-X/MSI initalization failed */
+ if (phba->intr_type == NONE) {
+ retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
+ IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+ if (!retval) {
+ /* Indicate initialization to INTx mode */
+ phba->intr_type = INTx;
+ intr_mode = 0;
+ }
+ }
+ return intr_mode;
+}
+
+/**
+ * lpfc_disable_intr: Disable device interrupt.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to disable device interrupt and disassociate the
+ * driver's interrupt handler(s) from interrupt vector(s). Depending on the
+ * interrupt mode, the driver will release the interrupt vector(s) for the
+ * message signaled interrupt.
+ **/
+static void
+lpfc_disable_intr(struct lpfc_hba *phba)
+{
+ /* Disable the currently initialized interrupt mode */
+ if (phba->intr_type == MSIX)
+ lpfc_disable_msix(phba);
+ else if (phba->intr_type == MSI)
+ lpfc_disable_msi(phba);
+ else if (phba->intr_type == INTx)
+ free_irq(phba->pcidev->irq, phba);
+
+ /* Reset interrupt management states */
+ phba->intr_type = NONE;
+ phba->sli.slistat.sli_intr = 0;
+
+ return;
+}
+
+/**
* lpfc_pci_probe_one: lpfc PCI probe func to register device to PCI subsystem.
* @pdev: pointer to PCI device
* @pid: pointer to PCI device identifier
@@ -2356,6 +2602,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
int error = -ENODEV, retval;
int i, hbq_count;
uint16_t iotag;
+ uint32_t cfg_mode, intr_mode;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
struct lpfc_adapter_event_header adapter_event;
@@ -2409,6 +2656,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->eratt_poll.data = (unsigned long) phba;
pci_set_master(pdev);
+ pci_save_state(pdev);
pci_try_set_mwi(pdev);
if (pci_set_dma_mask(phba->pcidev, DMA_64BIT_MASK) != 0)
@@ -2557,7 +2805,6 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
lpfc_debugfs_initialize(vport);
pci_set_drvdata(pdev, shost);
- phba->intr_type = NONE;
phba->MBslimaddr = phba->slim_memmap_p;
phba->HAregaddr = phba->ctrl_regs_memmap_p + HA_REG_OFFSET;
@@ -2565,63 +2812,58 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
phba->HSregaddr = phba->ctrl_regs_memmap_p + HS_REG_OFFSET;
phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET;
- /* Configure and enable interrupt */
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0427 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0426 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0430 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0473 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0452 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0451 Enable "
- "interrupt handler failed\n");
- error = retval;
- goto out_disable_msi;
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
-
+ /* Configure sysfs attributes */
if (lpfc_alloc_sysfs_attr(vport)) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"1476 Failed to allocate sysfs attr\n");
error = -ENOMEM;
- goto out_free_irq;
+ goto out_destroy_port;
}
- if (lpfc_sli_hba_setup(phba)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1477 Failed to set up hba\n");
- error = -ENODEV;
- goto out_remove_device;
+ cfg_mode = phba->cfg_use_msi;
+ while (true) {
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, cfg_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0426 Failed to enable interrupt.\n");
+ goto out_free_sysfs_attr;
+ }
+ /* HBA SLI setup */
+ if (lpfc_sli_hba_setup(phba)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1477 Failed to set up hba\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+
+ /* Wait 50ms for the interrupts of previous mailbox commands */
+ msleep(50);
+ /* Check active interrupts received */
+ if (phba->sli.slistat.sli_intr > LPFC_MSIX_VECTORS) {
+ /* Log the current active interrupt mode */
+ phba->intr_mode = intr_mode;
+ lpfc_log_intr_mode(phba, intr_mode);
+ break;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0451 Configure interrupt mode (%d) "
+ "failed active interrupt test.\n",
+ intr_mode);
+ if (intr_mode == 0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0479 Failed to enable "
+ "interrupt.\n");
+ error = -ENODEV;
+ goto out_remove_device;
+ }
+ /* Stop HBA SLI setups */
+ lpfc_stop_port(phba);
+ /* Disable the current interrupt mode */
+ lpfc_disable_intr(phba);
+ /* Try next level of interrupt mode */
+ cfg_mode = --intr_mode;
+ }
}
/*
@@ -2629,6 +2871,75 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
* the value of can_queue.
*/
shost->can_queue = phba->cfg_hba_queue_depth - 10;
+ if (phba->sli3_options & LPFC_SLI3_BG_ENABLED) {
+
+ if (lpfc_prot_mask && lpfc_prot_guard) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "1478 Registering BlockGuard with the "
+ "SCSI layer\n");
+
+ scsi_host_set_prot(shost, lpfc_prot_mask);
+ scsi_host_set_guard(shost, lpfc_prot_guard);
+ }
+ }
+
+ if (!_dump_buf_data) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ spin_lock_init(&_dump_buf_lock);
+ _dump_buf_data =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_data at 0x%p\n",
+ (1 << pagecnt), _dump_buf_data);
+ _dump_buf_data_order = pagecnt;
+ memset(_dump_buf_data, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_data_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ "\n", _dump_buf_data);
+ }
+
+
+ if (!_dump_buf_dif) {
+ int pagecnt = 10;
+ while (pagecnt) {
+ _dump_buf_dif =
+ (char *) __get_free_pages(GFP_KERNEL, pagecnt);
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD allocated %d pages for "
+ "_dump_buf_dif at 0x%p\n",
+ (1 << pagecnt), _dump_buf_dif);
+ _dump_buf_dif_order = pagecnt;
+ memset(_dump_buf_dif, 0, ((1 << PAGE_SHIFT)
+ << pagecnt));
+ break;
+ } else {
+ --pagecnt;
+ }
+
+ }
+
+ if (!_dump_buf_dif_order)
+ printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ "memory for hexdump\n");
+
+ } else {
+ printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ _dump_buf_dif);
+ }
lpfc_host_attrib_init(shost);
@@ -2646,29 +2957,22 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(adapter_event),
(char *) &adapter_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
-
- scsi_scan_host(shost);
+ LPFC_NL_VENDOR_ID);
return 0;
out_remove_device:
- lpfc_free_sysfs_attr(vport);
spin_lock_irq(shost->host_lock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(shost->host_lock);
-out_free_irq:
lpfc_stop_phba_timers(phba);
phba->pport->work_port_events = 0;
-
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else
- free_irq(phba->pcidev->irq, phba);
-
-out_disable_msi:
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
+ lpfc_disable_intr(phba);
+ lpfc_sli_hba_down(phba);
+ lpfc_sli_brdrestart(phba);
+out_free_sysfs_attr:
+ lpfc_free_sysfs_attr(vport);
+out_destroy_port:
destroy_port(vport);
out_kthread_stop:
kthread_stop(phba->worker_thread);
@@ -2709,7 +3013,7 @@ out:
* @pdev: pointer to PCI device
*
* This routine is to be registered to the kernel's PCI subsystem. When an
- * Emulex HBA is removed from PCI bus. It perform all the necessary cleanup
+ * Emulex HBA is removed from PCI bus, it performs all the necessary cleanup
* for the HBA device to be removed from the PCI subsystem properly.
**/
static void __devexit
@@ -2717,18 +3021,27 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_vport **vports;
struct lpfc_hba *phba = vport->phba;
+ int i;
int bars = pci_select_bars(pdev, IORESOURCE_MEM);
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
lpfc_free_sysfs_attr(vport);
kthread_stop(phba->worker_thread);
+ /* Release all the vports against this physical port */
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 1; i <= phba->max_vpi && vports[i] != NULL; i++)
+ fc_vport_terminate(vports[i]->fc_vport);
+ lpfc_destroy_vport_work_array(phba, vports);
+
+ /* Remove FC host and then SCSI host with the physical port */
fc_remove_host(shost);
scsi_remove_host(shost);
lpfc_cleanup(vport);
@@ -2748,13 +3061,8 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
lpfc_debugfs_terminate(vport);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
pci_set_drvdata(pdev, NULL);
scsi_host_put(shost);
@@ -2786,6 +3094,115 @@ lpfc_pci_remove_one(struct pci_dev *pdev)
}
/**
+ * lpfc_pci_suspend_one: lpfc PCI func to suspend device for power management.
+ * @pdev: pointer to PCI device
+ * @msg: power management message
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it quiesces the
+ * device by stopping the driver's worker thread for the device, turning off
+ * device's interrupt and DMA, and bring the device offline. Note that as the
+ * driver implements the minimum PM requirements to a power-aware driver's PM
+ * support for suspend/resume -- all the possible PM messages (SUSPEND,
+ * HIBERNATE, FREEZE) to the suspend() method call will be treated as SUSPEND
+ * and the driver will fully reinitialize its device during resume() method
+ * call, the driver will set device to PCI_D3hot state in PCI config space
+ * instead of setting it according to the @msg provided by the PM.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_suspend_one(struct pci_dev *pdev, pm_message_t msg)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0473 PCI device Power Management suspend.\n");
+
+ /* Bring down the device */
+ lpfc_offline_prep(phba);
+ lpfc_offline(phba);
+ kthread_stop(phba->worker_thread);
+
+ /* Disable interrupt from device */
+ lpfc_disable_intr(phba);
+
+ /* Save device state to PCI config space */
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+/**
+ * lpfc_pci_resume_one: lpfc PCI func to resume device for power management.
+ * @pdev: pointer to PCI device
+ *
+ * This routine is to be registered to the kernel's PCI subsystem to support
+ * system Power Management (PM). When PM invokes this method, it restores
+ * the device's PCI config space state and fully reinitializes the device
+ * and brings it online. Note that as the driver implements the minimum PM
+ * requirements to a power-aware driver's PM for suspend/resume -- all
+ * the possible PM messages (SUSPEND, HIBERNATE, FREEZE) to the suspend()
+ * method call will be treated as SUSPEND and the driver will fully
+ * reinitialize its device during resume() method call, the device will be
+ * set to PCI_D0 directly in PCI config space before restoring the state.
+ *
+ * Return code
+ * 0 - driver suspended the device
+ * Error otherwise
+ **/
+static int
+lpfc_pci_resume_one(struct pci_dev *pdev)
+{
+ struct Scsi_Host *shost = pci_get_drvdata(pdev);
+ struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ uint32_t intr_mode;
+ int error;
+
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "0452 PCI device Power Management resume.\n");
+
+ /* Restore device state from PCI config space */
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
+
+ /* Startup the kernel thread for this host adapter. */
+ phba->worker_thread = kthread_run(lpfc_do_work, phba,
+ "lpfc_worker_%d", phba->brd_no);
+ if (IS_ERR(phba->worker_thread)) {
+ error = PTR_ERR(phba->worker_thread);
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0434 PM resume failed to start worker "
+ "thread: error=x%x.\n", error);
+ return error;
+ }
+
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0430 PM resume Failed to enable interrupt\n");
+ return -EIO;
+ } else
+ phba->intr_mode = intr_mode;
+
+ /* Restart HBA and bring it online */
+ lpfc_sli_brdrestart(phba);
+ lpfc_online(phba);
+
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
+ return 0;
+}
+
+/**
* lpfc_io_error_detected: Driver method for handling PCI I/O error detected.
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
@@ -2828,13 +3245,8 @@ static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev,
pring = &psli->ring[psli->fcp_ring];
lpfc_sli_abort_iocb_ring(phba, pring);
- if (phba->intr_type == MSIX)
- lpfc_disable_msix(phba);
- else {
- free_irq(phba->pcidev->irq, phba);
- if (phba->intr_type == MSI)
- pci_disable_msi(phba->pcidev);
- }
+ /* Disable interrupt */
+ lpfc_disable_intr(phba);
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
@@ -2862,7 +3274,7 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
struct lpfc_sli *psli = &phba->sli;
- int error, retval;
+ uint32_t intr_mode;
dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n");
if (pci_enable_device_mem(pdev)) {
@@ -2871,61 +3283,31 @@ static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev)
return PCI_ERS_RESULT_DISCONNECT;
}
- pci_set_master(pdev);
+ pci_restore_state(pdev);
+ if (pdev->is_busmaster)
+ pci_set_master(pdev);
spin_lock_irq(&phba->hbalock);
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
spin_unlock_irq(&phba->hbalock);
- /* Enable configured interrupt method */
- phba->intr_type = NONE;
- if (phba->cfg_use_msi == 2) {
- /* Need to issue conf_port mbox cmd before conf_msi mbox cmd */
- error = lpfc_sli_config_port(phba, 3);
- if (error)
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0478 Firmware not capable of SLI 3 mode.\n");
- else {
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0479 Firmware capable of SLI 3 mode.\n");
- /* Now, try to enable MSI-X interrupt mode */
- error = lpfc_enable_msix(phba);
- if (!error) {
- phba->intr_type = MSIX;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0480 enable MSI-X mode.\n");
- }
- }
- }
-
- /* Fallback to MSI if MSI-X initialization failed */
- if (phba->cfg_use_msi >= 1 && phba->intr_type == NONE) {
- retval = pci_enable_msi(phba->pcidev);
- if (!retval) {
- phba->intr_type = MSI;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0481 enable MSI mode.\n");
- } else
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "0470 enable IRQ mode.\n");
- }
-
- /* MSI-X is the only case the doesn't need to call request_irq */
- if (phba->intr_type != MSIX) {
- retval = request_irq(phba->pcidev->irq, lpfc_intr_handler,
- IRQF_SHARED, LPFC_DRIVER_NAME, phba);
- if (retval) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0471 Enable interrupt handler "
- "failed\n");
- } else if (phba->intr_type != MSI)
- phba->intr_type = INTx;
- }
+ /* Configure and enable interrupt */
+ intr_mode = lpfc_enable_intr(phba, phba->intr_mode);
+ if (intr_mode == LPFC_INTR_ERROR) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0427 Cannot re-enable interrupt after "
+ "slot reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ } else
+ phba->intr_mode = intr_mode;
/* Take device offline; this will perform cleanup */
lpfc_offline(phba);
lpfc_sli_brdrestart(phba);
+ /* Log the current active interrupt mode */
+ lpfc_log_intr_mode(phba, phba->intr_mode);
+
return PCI_ERS_RESULT_RECOVERED;
}
@@ -3037,6 +3419,8 @@ static struct pci_driver lpfc_driver = {
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
.remove = __devexit_p(lpfc_pci_remove_one),
+ .suspend = lpfc_pci_suspend_one,
+ .resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
};
@@ -3100,6 +3484,19 @@ lpfc_exit(void)
fc_release_transport(lpfc_transport_template);
if (lpfc_enable_npiv)
fc_release_transport(lpfc_vport_transport_template);
+ if (_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data "
+ "at 0x%p\n",
+ (1L << _dump_buf_data_order), _dump_buf_data);
+ free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
+ }
+
+ if (_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif "
+ "at 0x%p\n",
+ (1L << _dump_buf_dif_order), _dump_buf_dif);
+ free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
+ }
}
module_init(lpfc_init);
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
index 39fd2b843bec..a85b7c196bbc 100644
--- a/drivers/scsi/lpfc/lpfc_logmsg.h
+++ b/drivers/scsi/lpfc/lpfc_logmsg.h
@@ -27,6 +27,7 @@
#define LOG_FCP 0x40 /* FCP traffic history */
#define LOG_NODE 0x80 /* Node table events */
#define LOG_TEMP 0x100 /* Temperature sensor events */
+#define LOG_BG 0x200 /* BlockBuard events */
#define LOG_MISC 0x400 /* Miscellaneous events */
#define LOG_SLI 0x800 /* SLI events */
#define LOG_FCP_ERROR 0x1000 /* log errors, not underruns */
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 7465fe746fe9..34eeb086a667 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -77,6 +77,38 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
}
/**
+ * lpfc_dump_mem: Prepare a mailbox command for retrieving wakeup params.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ * This function create a dump memory mailbox command to dump wake up
+ * parameters.
+ */
+void
+lpfc_dump_wakeup_param(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ void *ctx;
+
+ mb = &pmb->mb;
+ /* Save context so that we can restore after memset */
+ ctx = pmb->context2;
+
+ /* Setup to dump VPD region */
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+ mb->mbxCommand = MBX_DUMP_MEMORY;
+ mb->mbxOwner = OWN_HOST;
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.type = DMP_NV_PARAMS;
+ mb->un.varDmp.entry_index = 0;
+ mb->un.varDmp.region_id = WAKE_UP_PARMS_REGION_ID;
+ mb->un.varDmp.word_cnt = WAKE_UP_PARMS_WORD_SIZE;
+ mb->un.varDmp.co = 0;
+ mb->un.varDmp.resp_offset = 0;
+ pmb->context2 = ctx;
+ return;
+}
+
+/**
* lpfc_read_nv: Prepare a mailbox command for reading HBA's NVRAM param.
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -1061,9 +1093,14 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
+ /* Always Host Group Pointer is in SLIM */
+ mb->un.varCfgPort.hps = 1;
+
/* If HBA supports SLI=3 ask for it */
if (phba->sli_rev == 3 && phba->vpd.sli3Feat.cerbm) {
+ if (phba->cfg_enable_bg)
+ mb->un.varCfgPort.cbg = 1; /* configure BlockGuard */
mb->un.varCfgPort.cerbm = 1; /* Request HBQs */
mb->un.varCfgPort.ccrp = 1; /* Command Ring Polling */
mb->un.varCfgPort.cinb = 1; /* Interrupt Notification Block */
@@ -1163,16 +1200,11 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
sizeof(*phba->host_gp));
}
- /* Setup Port Group ring pointer */
- if (phba->sli3_options & LPFC_SLI3_INB_ENABLED) {
- pgp_offset = offsetof(struct lpfc_sli2_slim,
- mbx.us.s3_inb_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
- } else if (phba->sli_rev == 3) {
+ /* Setup Port Group offset */
+ if (phba->sli_rev == 3)
pgp_offset = offsetof(struct lpfc_sli2_slim,
mbx.us.s3_pgp.port);
- phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
- } else
+ else
pgp_offset = offsetof(struct lpfc_sli2_slim, mbx.us.s2.port);
pdma_addr = phba->slim2p.phys + pgp_offset;
phba->pcb->pgpAddrHigh = putPaddrHigh(pdma_addr);
@@ -1285,10 +1317,12 @@ lpfc_mbox_get(struct lpfc_hba * phba)
void
lpfc_mbox_cmpl_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
{
+ unsigned long iflag;
+
/* This function expects to be called from interrupt context */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
list_add_tail(&mbq->list, &phba->sli.mboxq_cmpl);
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index 1accb5a9f4e6..27d1a88a98fe 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -22,18 +22,20 @@
#define FC_REG_LINK_EVENT 0x0001 /* link up / down events */
#define FC_REG_RSCN_EVENT 0x0002 /* RSCN events */
#define FC_REG_CT_EVENT 0x0004 /* CT request events */
-#define FC_REG_DUMP_EVENT 0x0008 /* Dump events */
-#define FC_REG_TEMPERATURE_EVENT 0x0010 /* temperature events */
-#define FC_REG_ELS_EVENT 0x0020 /* lpfc els events */
-#define FC_REG_FABRIC_EVENT 0x0040 /* lpfc fabric events */
-#define FC_REG_SCSI_EVENT 0x0080 /* lpfc scsi events */
-#define FC_REG_BOARD_EVENT 0x0100 /* lpfc board events */
-#define FC_REG_ADAPTER_EVENT 0x0200 /* lpfc adapter events */
+#define FC_REG_DUMP_EVENT 0x0010 /* Dump events */
+#define FC_REG_TEMPERATURE_EVENT 0x0020 /* temperature events */
+#define FC_REG_VPORTRSCN_EVENT 0x0040 /* Vport RSCN events */
+#define FC_REG_ELS_EVENT 0x0080 /* lpfc els events */
+#define FC_REG_FABRIC_EVENT 0x0100 /* lpfc fabric events */
+#define FC_REG_SCSI_EVENT 0x0200 /* lpfc scsi events */
+#define FC_REG_BOARD_EVENT 0x0400 /* lpfc board events */
+#define FC_REG_ADAPTER_EVENT 0x0800 /* lpfc adapter events */
#define FC_REG_EVENT_MASK (FC_REG_LINK_EVENT | \
FC_REG_RSCN_EVENT | \
FC_REG_CT_EVENT | \
FC_REG_DUMP_EVENT | \
FC_REG_TEMPERATURE_EVENT | \
+ FC_REG_VPORTRSCN_EVENT | \
FC_REG_ELS_EVENT | \
FC_REG_FABRIC_EVENT | \
FC_REG_SCSI_EVENT | \
@@ -52,6 +54,13 @@
* The payload sent via the fc transport is one-way driver->application.
*/
+/* RSCN event header */
+struct lpfc_rscn_event_header {
+ uint32_t event_type;
+ uint32_t payload_length; /* RSCN data length in bytes */
+ uint32_t rscn_payload[];
+};
+
/* els event header */
struct lpfc_els_event_header {
uint32_t event_type;
@@ -65,6 +74,7 @@ struct lpfc_els_event_header {
#define LPFC_EVENT_PRLO_RCV 0x02
#define LPFC_EVENT_ADISC_RCV 0x04
#define LPFC_EVENT_LSRJT_RCV 0x08
+#define LPFC_EVENT_LOGO_RCV 0x10
/* special els lsrjt event */
struct lpfc_lsrjt_event {
@@ -74,6 +84,11 @@ struct lpfc_lsrjt_event {
uint32_t explanation;
};
+/* special els logo event */
+struct lpfc_logo_event {
+ struct lpfc_els_event_header header;
+ uint8_t logo_wwpn[8];
+};
/* fabric event header */
struct lpfc_fabric_event_header {
@@ -125,6 +140,7 @@ struct lpfc_scsi_varqueuedepth_event {
/* special case scsi check condition event */
struct lpfc_scsi_check_condition_event {
struct lpfc_scsi_event_header scsi_event;
+ uint8_t opcode;
uint8_t sense_key;
uint8_t asc;
uint8_t ascq;
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 0c25d97acb42..8f548adae9cc 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1929,10 +1929,10 @@ lpfc_device_recov_npr_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (vport->fc_flag & FC_RSCN_DEFERRED)
return ndlp->nlp_state;
+ lpfc_cancel_retry_delay_tmo(vport, ndlp);
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
spin_unlock_irq(shost->host_lock);
- lpfc_cancel_retry_delay_tmo(vport, ndlp);
return ndlp->nlp_state;
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index bd1867411821..b103b6ed4970 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -18,13 +18,14 @@
* more details, a copy of which can be found in the file COPYING *
* included with this package. *
*******************************************************************/
-
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
@@ -43,6 +44,73 @@
#define LPFC_RESET_WAIT 2
#define LPFC_ABORT_WAIT 2
+int _dump_buf_done;
+
+static char *dif_op_str[] = {
+ "SCSI_PROT_NORMAL",
+ "SCSI_PROT_READ_INSERT",
+ "SCSI_PROT_WRITE_STRIP",
+ "SCSI_PROT_READ_STRIP",
+ "SCSI_PROT_WRITE_INSERT",
+ "SCSI_PROT_READ_PASS",
+ "SCSI_PROT_WRITE_PASS",
+ "SCSI_PROT_READ_CONVERT",
+ "SCSI_PROT_WRITE_CONVERT"
+};
+
+static void
+lpfc_debug_save_data(struct scsi_cmnd *cmnd)
+{
+ void *src, *dst;
+ struct scatterlist *sgde = scsi_sglist(cmnd);
+
+ if (!_dump_buf_data) {
+ printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ __func__);
+ return;
+ }
+
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+ return;
+ }
+
+ dst = (void *) _dump_buf_data;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
+static void
+lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
+{
+ void *src, *dst;
+ struct scatterlist *sgde = scsi_prot_sglist(cmnd);
+
+ if (!_dump_buf_dif) {
+ printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!sgde) {
+ printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+ return;
+ }
+
+ dst = _dump_buf_dif;
+ while (sgde) {
+ src = sg_virt(sgde);
+ memcpy(dst, src, sgde->length);
+ dst += sgde->length;
+ sgde = sg_next(sgde);
+ }
+}
+
/**
* lpfc_update_stats: Update statistical data for the command completion.
* @phba: Pointer to HBA object.
@@ -66,6 +134,8 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
if (cmd->result)
return;
+ latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time);
+
spin_lock_irqsave(shost->host_lock, flags);
if (!vport->stat_data_enabled ||
vport->stat_data_blocked ||
@@ -74,13 +144,15 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
return;
}
- latency = jiffies_to_msecs(jiffies - lpfc_cmd->start_time);
if (phba->bucket_type == LPFC_LINEAR_BUCKET) {
i = (latency + phba->bucket_step - 1 - phba->bucket_base)/
phba->bucket_step;
- if (i >= LPFC_MAX_BUCKET_COUNT)
- i = LPFC_MAX_BUCKET_COUNT;
+ /* check array subscript bounds */
+ if (i < 0)
+ i = 0;
+ else if (i >= LPFC_MAX_BUCKET_COUNT)
+ i = LPFC_MAX_BUCKET_COUNT - 1;
} else {
for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++)
if (latency <= (phba->bucket_base +
@@ -92,7 +164,6 @@ lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
spin_unlock_irqrestore(shost->host_lock, flags);
}
-
/**
* lpfc_send_sdev_queuedepth_change_event: Posts a queuedepth change
* event.
@@ -148,12 +219,19 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
return;
}
-/*
- * This function is called with no lock held when there is a resource
- * error in driver or in firmware.
- */
+/**
+ * lpfc_rampdown_queue_depth: Post RAMP_DOWN_QUEUE event to worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called when there is resource error in driver or firmware.
+ * This routine posts WORKER_RAMP_DOWN_QUEUE event for @phba. This routine
+ * posts at most 1 event each second. This routine wakes up worker thread of
+ * @phba to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
void
-lpfc_adjust_queue_depth(struct lpfc_hba *phba)
+lpfc_rampdown_queue_depth(struct lpfc_hba *phba)
{
unsigned long flags;
uint32_t evt_posted;
@@ -182,10 +260,17 @@ lpfc_adjust_queue_depth(struct lpfc_hba *phba)
return;
}
-/*
- * This function is called with no lock held when there is a successful
- * SCSI command completion.
- */
+/**
+ * lpfc_rampup_queue_depth: Post RAMP_UP_QUEUE event for worker thread.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine
+ * post at most 1 event every 5 minute after last_ramp_up_time or
+ * last_rsrc_error_time. This routine wakes up worker thread of @phba
+ * to process WORKER_RAM_DOWN_EVENT event.
+ *
+ * This routine should be called with no lock held.
+ **/
static inline void
lpfc_rampup_queue_depth(struct lpfc_vport *vport,
struct scsi_device *sdev)
@@ -217,6 +302,14 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
return;
}
+/**
+ * lpfc_ramp_down_queue_handler: WORKER_RAMP_DOWN_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_DOWN_QUEUE event for worker
+ * thread.This routine reduces queue depth for all scsi device on each vport
+ * associated with @phba.
+ **/
void
lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
{
@@ -267,6 +360,15 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
atomic_set(&phba->num_cmd_success, 0);
}
+/**
+ * lpfc_ramp_up_queue_handler: WORKER_RAMP_UP_QUEUE event handler.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker
+ * thread.This routine increases queue depth for all scsi device on each vport
+ * associated with @phba by 1. This routine also sets @phba num_rsrc_err and
+ * num_cmd_success to zero.
+ **/
void
lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
{
@@ -336,14 +438,21 @@ lpfc_scsi_dev_block(struct lpfc_hba *phba)
lpfc_destroy_vport_work_array(phba, vports);
}
-/*
+/**
+ * lpfc_new_scsi_buf: Scsi buffer allocator.
+ * @vport: The virtual port for which this call being executed.
+ *
* This routine allocates a scsi buffer, which contains all the necessary
* information needed to initiate a SCSI I/O. The non-DMAable buffer region
* contains information to build the IOCB. The DMAable region contains
- * memory for the FCP CMND, FCP RSP, and the inital BPL. In addition to
- * allocating memeory, the FCP CMND and FCP RSP BDEs are setup in the BPL
+ * memory for the FCP CMND, FCP RSP, and the initial BPL. In addition to
+ * allocating memory, the FCP CMND and FCP RSP BDEs are setup in the BPL
* and the BPL BDE is setup in the IOCB.
- */
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf data structure - Success
+ **/
static struct lpfc_scsi_buf *
lpfc_new_scsi_buf(struct lpfc_vport *vport)
{
@@ -407,14 +516,14 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd));
bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd);
bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[0].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w);
/* Setup the physical region for the FCP RSP */
bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp));
bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp));
bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp);
bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64;
- bpl[1].tus.w = le32_to_cpu(bpl->tus.w);
+ bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w);
/*
* Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
@@ -422,7 +531,8 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
*/
iocb = &psb->cur_iocbq.iocb;
iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
- if (phba->sli_rev == 3) {
+ if ((phba->sli_rev == 3) &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
/* fill in immediate fcp command BDE */
iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED;
iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd);
@@ -452,6 +562,17 @@ lpfc_new_scsi_buf(struct lpfc_vport *vport)
return psb;
}
+/**
+ * lpfc_get_scsi_buf: Get a scsi buffer from lpfc_scsi_buf_list list of Hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list
+ * and returns to caller.
+ *
+ * Return codes:
+ * NULL - Error
+ * Pointer to lpfc_scsi_buf - Success
+ **/
static struct lpfc_scsi_buf*
lpfc_get_scsi_buf(struct lpfc_hba * phba)
{
@@ -464,11 +585,20 @@ lpfc_get_scsi_buf(struct lpfc_hba * phba)
if (lpfc_cmd) {
lpfc_cmd->seg_cnt = 0;
lpfc_cmd->nonsg_phys = 0;
+ lpfc_cmd->prot_seg_cnt = 0;
}
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
return lpfc_cmd;
}
+/**
+ * lpfc_release_scsi_buf: Return a scsi buffer back to hba lpfc_scsi_buf_list list.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is being released.
+ *
+ * This routine releases @psb scsi buffer by adding it to tail of @phba
+ * lpfc_scsi_buf_list list.
+ **/
static void
lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
{
@@ -480,6 +610,20 @@ lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb)
spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag);
}
+/**
+ * lpfc_scsi_prep_dma_buf: Routine to do DMA mapping for scsi buffer.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine does the pci dma mapping for scatter-gather list of scsi cmnd
+ * field of @lpfc_cmd. This routine scans through sg elements and format the
+ * bdea. This routine also initializes all IOCB fields which are dependent on
+ * scsi command request buffer.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
static int
lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
{
@@ -516,7 +660,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d",
+ "dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
@@ -535,6 +679,7 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) {
physaddr = sg_dma_address(sgel);
if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
nseg <= LPFC_EXT_DATA_BDE_COUNT) {
data_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
data_bde->tus.f.bdeSize = sg_dma_len(sgel);
@@ -560,7 +705,8 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* explicitly reinitialized and for SLI-3 the extended bde count is
* explicitly reinitialized since all iocb memory resources are reused.
*/
- if (phba->sli_rev == 3) {
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) {
if (num_bde > LPFC_EXT_DATA_BDE_COUNT) {
/*
* The extended IOCB format can only fit 3 BDE or a BPL.
@@ -587,7 +733,683 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
((num_bde + 2) * sizeof(struct ulp_bde64));
}
fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd));
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = le32_to_cpu(scsi_bufflen(scsi_cmnd));
+ return 0;
+}
+
+/*
+ * Given a scsi cmnd, determine the BlockGuard profile to be used
+ * with the cmd
+ */
+static int
+lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+{
+ uint8_t guard_type = scsi_host_get_guard(sc->device->host);
+ uint8_t ret_prof = LPFC_PROF_INVALID;
+
+ if (guard_type == SHOST_DIX_GUARD_IP) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ ret_prof = LPFC_PROF_AST2;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ ret_prof = LPFC_PROF_AST1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+
+ }
+ } else if (guard_type == SHOST_DIX_GUARD_CRC) {
+ switch (scsi_get_prot_op(sc)) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret_prof = LPFC_PROF_A1;
+ break;
+
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ ret_prof = LPFC_PROF_C1;
+ break;
+
+ case SCSI_PROT_READ_CONVERT:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_NORMAL:
+ default:
+ printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ scsi_get_prot_op(sc), guard_type);
+ break;
+ }
+ } else {
+ /* unsupported format */
+ BUG();
+ }
+
+ return ret_prof;
+}
+
+struct scsi_dif_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+static inline unsigned
+lpfc_cmd_blksize(struct scsi_cmnd *sc)
+{
+ return sc->device->sector_size;
+}
+
+/**
+ * lpfc_get_cmd_dif_parms - Extract DIF parameters from SCSI command
+ * @sc: in: SCSI command
+ * @apptagmask out: app tag mask
+ * @apptagval out: app tag value
+ * @reftag out: ref tag (reference tag)
+ *
+ * Description:
+ * Extract DIF paramters from the command if possible. Otherwise,
+ * use default paratmers.
+ *
+ **/
+static inline void
+lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask,
+ uint16_t *apptagval, uint32_t *reftag)
+{
+ struct scsi_dif_tuple *spt;
+ unsigned char op = scsi_get_prot_op(sc);
+ unsigned int protcnt = scsi_prot_sg_count(sc);
+ static int cnt;
+
+ if (protcnt && (op == SCSI_PROT_WRITE_STRIP ||
+ op == SCSI_PROT_WRITE_PASS ||
+ op == SCSI_PROT_WRITE_CONVERT)) {
+
+ cnt++;
+ spt = page_address(sg_page(scsi_prot_sglist(sc))) +
+ scsi_prot_sglist(sc)[0].offset;
+ *apptagmask = 0;
+ *apptagval = 0;
+ *reftag = cpu_to_be32(spt->ref_tag);
+
+ } else {
+ /* SBC defines ref tag to be lower 32bits of LBA */
+ *reftag = (uint32_t) (0xffffffff & scsi_get_lba(sc));
+ *apptagmask = 0;
+ *apptagval = 0;
+ }
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ * +-------------------------+
+ * start of prot group --> | PDE_1 |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * Note: Data s/g buffers have been dma mapped
+ */
+static int
+lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datasegcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct lpfc_pde *pde1 = NULL;
+ dma_addr_t physaddr;
+ int i = 0, num_bde = 0;
+ int datadir = sc->sc_data_direction;
+ int prof = LPFC_PROF_INVALID;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+
+ pde1 = (struct lpfc_pde *) bpl;
+ prof = lpfc_sc_to_sli_prof(sc);
+
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ /* setup PDE1 with what we have */
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* assumption: caller has already run dma_map_sg on command data */
+ scsi_for_each_sg(sc, sgde, datasegcnt, i) {
+ physaddr = sg_dma_address(sgde);
+ bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+ bpl->tus.f.bdeSize = sg_dma_len(sgde);
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+ bpl++;
+ num_bde++;
+ }
+
+out:
+ return num_bde;
+}
+
+/*
+ * This function sets up buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF_BUF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream. For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ * +-------------------------+
+ * start of first prot group --> | PDE_1 |
+ * +-------------------------+
+ * | PDE_3 (Prot BDE) |
+ * +-------------------------+
+ * | Data BDE |
+ * +-------------------------+
+ * |more Data BDE's ... (opt)|
+ * +-------------------------+
+ * start of new prot group --> | PDE_1 |
+ * +-------------------------+
+ * | ... |
+ * +-------------------------+
+ *
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ * mapped for DMA
+ */
+static int
+lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct ulp_bde64 *bpl, int datacnt, int protcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct scatterlist *sgpe = NULL; /* s/g prot entry */
+ struct lpfc_pde *pde1 = NULL;
+ struct ulp_bde64 *prot_bde = NULL;
+ dma_addr_t dataphysaddr, protphysaddr;
+ unsigned short curr_data = 0, curr_prot = 0;
+ unsigned int split_offset, protgroup_len;
+ unsigned int protgrp_blks, protgrp_bytes;
+ unsigned int remainder, subtotal;
+ int prof = LPFC_PROF_INVALID;
+ int datadir = sc->sc_data_direction;
+ unsigned char pgdone = 0, alldone = 0;
+ unsigned blksize;
+ uint32_t reftag;
+ uint16_t apptagmask, apptagval;
+ int num_bde = 0;
+
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
+ if (!sgpe || !sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9020 Invalid s/g entry: data=0x%p prot=0x%p\n",
+ sgpe, sgde);
+ return 0;
+ }
+
+ prof = lpfc_sc_to_sli_prof(sc);
+ if (prof == LPFC_PROF_INVALID)
+ goto out;
+
+ /* extract some info from the scsi command for PDE1*/
+ blksize = lpfc_cmd_blksize(sc);
+ lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag);
+
+ split_offset = 0;
+ do {
+ /* setup the first PDE_1 */
+ pde1 = (struct lpfc_pde *) bpl;
+
+ lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize,
+ BG_EC_STOP_ERR);
+ lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag);
+
+ num_bde++;
+ bpl++;
+
+ /* setup the first BDE that points to protection buffer */
+ prot_bde = (struct ulp_bde64 *) bpl;
+ protphysaddr = sg_dma_address(sgpe);
+ prot_bde->addrLow = le32_to_cpu(putPaddrLow(protphysaddr));
+ prot_bde->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr));
+ protgroup_len = sg_dma_len(sgpe);
+
+
+ /* must be integer multiple of the DIF block length */
+ BUG_ON(protgroup_len % 8);
+
+ protgrp_blks = protgroup_len / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+
+ prot_bde->tus.f.bdeSize = protgroup_len;
+ if (datadir == DMA_TO_DEVICE)
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ prot_bde->tus.w = le32_to_cpu(bpl->tus.w);
+
+ curr_prot++;
+ num_bde++;
+
+ /* setup BDE's for data blocks associated with DIF data */
+ pgdone = 0;
+ subtotal = 0; /* total bytes processed for current prot grp */
+ while (!pgdone) {
+ if (!sgde) {
+ printk(KERN_ERR "%s Invalid data segment\n",
+ __func__);
+ return 0;
+ }
+ bpl++;
+ dataphysaddr = sg_dma_address(sgde) + split_offset;
+ bpl->addrLow = le32_to_cpu(putPaddrLow(dataphysaddr));
+ bpl->addrHigh = le32_to_cpu(putPaddrHigh(dataphysaddr));
+
+ remainder = sg_dma_len(sgde) - split_offset;
+
+ if ((subtotal + remainder) <= protgrp_bytes) {
+ /* we can use this whole buffer */
+ bpl->tus.f.bdeSize = remainder;
+ split_offset = 0;
+
+ if ((subtotal + remainder) == protgrp_bytes)
+ pgdone = 1;
+ } else {
+ /* must split this buffer with next prot grp */
+ bpl->tus.f.bdeSize = protgrp_bytes - subtotal;
+ split_offset += bpl->tus.f.bdeSize;
+ }
+
+ subtotal += bpl->tus.f.bdeSize;
+
+ if (datadir == DMA_TO_DEVICE)
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ else
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+ num_bde++;
+ curr_data++;
+
+ if (split_offset)
+ break;
+
+ /* Move to the next s/g segment if possible */
+ sgde = sg_next(sgde);
+ }
+
+ /* are we done ? */
+ if (curr_prot == protcnt) {
+ alldone = 1;
+ } else if (curr_prot < protcnt) {
+ /* advance to next prot buffer */
+ sgpe = sg_next(sgpe);
+ bpl++;
+
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ } else {
+ /* if we're here, we have a bug */
+ printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+ }
+
+ } while (!alldone);
+
+out:
+
+
+ return num_bde;
+}
+/*
+ * Given a SCSI command that supports DIF, determine composition of protection
+ * groups involved in setting up buffer lists
+ *
+ * Returns:
+ * for DIF (for both read and write)
+ * */
+static int
+lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
+{
+ int ret = LPFC_PG_TYPE_INVALID;
+ unsigned char op = scsi_get_prot_op(sc);
+
+ switch (op) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ ret = LPFC_PG_TYPE_NO_DIF;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ case SCSI_PROT_WRITE_CONVERT:
+ case SCSI_PROT_READ_CONVERT:
+ ret = LPFC_PG_TYPE_DIF_BUF;
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9021 Unsupported protection op:%d\n", op);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ */
+static int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ uint32_t num_bde = 0;
+ int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+ int prot_group_type = 0;
+ int diflen, fcpdl;
+ unsigned blksize;
+
+ /*
+ * Start the lpfc command prep by bumping the bpl beyond fcp_cmnd
+ * fcp_rsp regions to the first data bde entry
+ */
+ bpl += 2;
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ datasegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_sglist(scsi_cmnd),
+ scsi_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!datasegcnt))
+ return 1;
+
+ lpfc_cmd->seg_cnt = datasegcnt;
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+ switch (prot_group_type) {
+ case LPFC_PG_TYPE_NO_DIF:
+ num_bde = lpfc_bg_setup_bpl(phba, scsi_cmnd, bpl,
+ datasegcnt);
+ /* we shoud have 2 or more entries in buffer list */
+ if (num_bde < 2)
+ goto err;
+ break;
+ case LPFC_PG_TYPE_DIF_BUF:{
+ /*
+ * This type indicates that protection buffers are
+ * passed to the driver, so that needs to be prepared
+ * for DMA
+ */
+ protsegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!protsegcnt)) {
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ lpfc_cmd->prot_seg_cnt = protsegcnt;
+ if (lpfc_cmd->prot_seg_cnt
+ > phba->cfg_prot_sg_seg_cnt) {
+ printk(KERN_ERR "%s: Too many prot sg segments "
+ "from dma_map_sg. Config %d,"
+ "prot_seg_cnt %d\n", __func__,
+ phba->cfg_prot_sg_seg_cnt,
+ lpfc_cmd->prot_seg_cnt);
+ dma_unmap_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ datadir);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ num_bde = lpfc_bg_setup_bpl_prot(phba, scsi_cmnd, bpl,
+ datasegcnt, protsegcnt);
+ /* we shoud have 3 or more entries in buffer list */
+ if (num_bde < 3)
+ goto err;
+ break;
+ }
+ case LPFC_PG_TYPE_INVALID:
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9022 Unexpected protection group %i\n",
+ prot_group_type);
+ return 1;
+ }
+ }
+
+ /*
+ * Finish initializing those IOCB fields that are dependent on the
+ * scsi_cmnd request_buffer. Note that the bdeSize is explicitly
+ * reinitialized since all iocb memory resources are used many times
+ * for transmit, receive, and continuation bpl's.
+ */
+ iocb_cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64));
+ iocb_cmd->un.fcpi64.bdl.bdeSize += (num_bde * sizeof(struct ulp_bde64));
+ iocb_cmd->ulpBdeCount = 1;
+ iocb_cmd->ulpLe = 1;
+
+ fcpdl = scsi_bufflen(scsi_cmnd);
+
+ if (scsi_get_prot_type(scsi_cmnd) == SCSI_PROT_DIF_TYPE1) {
+ /*
+ * We are in DIF Type 1 mode
+ * Every data block has a 8 byte DIF (trailer)
+ * attached to it. Must ajust FCP data length
+ */
+ blksize = lpfc_cmd_blksize(scsi_cmnd);
+ diflen = (fcpdl / blksize) * 8;
+ fcpdl += diflen;
+ }
+ fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+
return 0;
+err:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9023 Could not setup all needed BDE's"
+ "prot_group_type=%d, num_bde=%d\n",
+ prot_group_type, num_bde);
+ return 1;
+}
+
+/*
+ * This function checks for BlockGuard errors detected by
+ * the HBA. In case of errors, the ASC/ASCQ fields in the
+ * sense buffer will be set accordingly, paired with
+ * ILLEGAL_REQUEST to signal to the kernel that the HBA
+ * detected corruption.
+ *
+ * Returns:
+ * 0 - No error found
+ * 1 - BlockGuard error found
+ * -1 - Internal error (bad profile, ...etc)
+ */
+static int
+lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
+ struct lpfc_iocbq *pIocbOut)
+{
+ struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+ struct sli3_bg_fields *bgf = &pIocbOut->iocb.unsli3.sli3_bg;
+ int ret = 0;
+ uint32_t bghm = bgf->bghm;
+ uint32_t bgstat = bgf->bgstat;
+ uint64_t failing_sector = 0;
+
+ printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%lx "
+ "bgstat=0x%x bghm=0x%x\n",
+ cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
+ cmd->request->nr_sectors, bgstat, bghm);
+
+ spin_lock(&_dump_buf_lock);
+ if (!_dump_buf_done) {
+ printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_data(cmd);
+
+ /* If we have a prot sgl, save the DIF buffer */
+ if (lpfc_prot_group_type(phba, cmd) ==
+ LPFC_PG_TYPE_DIF_BUF) {
+ printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_dif(cmd);
+ }
+
+ _dump_buf_done = 1;
+ }
+ spin_unlock(&_dump_buf_lock);
+
+ if (lpfc_bgs_get_invalid_prof(bgstat)) {
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n",
+ bgstat);
+ ret = (-1);
+ goto out;
+ }
+
+ if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
+ bgstat);
+ ret = (-1);
+ goto out;
+ }
+
+ if (lpfc_bgs_get_guard_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x1);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+ phba->bg_guard_err_cnt++;
+ printk(KERN_ERR "BLKGRD: guard_tag error\n");
+ }
+
+ if (lpfc_bgs_get_reftag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x3);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_reftag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: ref_tag error\n");
+ }
+
+ if (lpfc_bgs_get_apptag_err(bgstat)) {
+ ret = 1;
+
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x2);
+ cmd->result = (DRIVER_SENSE|SUGGEST_DIE) << 24
+ | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
+
+ phba->bg_apptag_err_cnt++;
+ printk(KERN_ERR "BLKGRD: app_tag error\n");
+ }
+
+ if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
+ /*
+ * setup sense data descriptor 0 per SPC-4 as an information
+ * field, and put the failing LBA in it
+ */
+ cmd->sense_buffer[8] = 0; /* Information */
+ cmd->sense_buffer[9] = 0xa; /* Add. length */
+ do_div(bghm, cmd->device->sector_size);
+
+ failing_sector = scsi_get_lba(cmd);
+ failing_sector += bghm;
+
+ put_unaligned_be64(failing_sector, &cmd->sense_buffer[10]);
+ }
+
+ if (!ret) {
+ /* No error was reported - problem in FW? */
+ cmd->result = ScsiResult(DID_ERROR, 0);
+ printk(KERN_ERR "BLKGRD: no errors reported!\n");
+ }
+
+out:
+ return ret;
}
/**
@@ -681,6 +1503,15 @@ lpfc_send_scsi_error_event(struct lpfc_hba *phba, struct lpfc_vport *vport,
lpfc_worker_wake_up(phba);
return;
}
+
+/**
+ * lpfc_scsi_unprep_dma_buf: Routine to un-map DMA mapping of scatter gather.
+ * @phba: The Hba for which this call is being executed.
+ * @psb: The scsi buffer which is going to be un-mapped.
+ *
+ * This routine does DMA un-mapping of scatter gather list of scsi command
+ * field of @lpfc_cmd.
+ **/
static void
lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
{
@@ -692,8 +1523,22 @@ lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb)
*/
if (psb->seg_cnt > 0)
scsi_dma_unmap(psb->pCmd);
+ if (psb->prot_seg_cnt > 0)
+ dma_unmap_sg(&phba->pcidev->dev, scsi_prot_sglist(psb->pCmd),
+ scsi_prot_sg_count(psb->pCmd),
+ psb->pCmd->sc_data_direction);
}
+/**
+ * lpfc_handler_fcp_err: FCP response handler.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @rsp_iocb: The response IOCB which contains FCP error.
+ *
+ * This routine is called to process response IOCB with status field
+ * IOSTAT_FCP_RSP_ERROR. This routine sets result field of scsi command
+ * based upon SCSI and FCP error.
+ **/
static void
lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_iocbq *rsp_iocb)
@@ -735,7 +1580,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
logit = LOG_FCP;
lpfc_printf_vlog(vport, KERN_WARNING, logit,
- "0730 FCP command x%x failed: x%x SNS x%x x%x "
+ "9024 FCP command x%x failed: x%x SNS x%x x%x "
"Data: x%x x%x x%x x%x x%x\n",
cmnd->cmnd[0], scsi_status,
be32_to_cpu(*lp), be32_to_cpu(*(lp + 3)), resp_info,
@@ -758,7 +1603,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0716 FCP Read Underrun, expected %d, "
+ "9025 FCP Read Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
@@ -774,7 +1619,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_get_resid(cmnd) != fcpi_parm)) {
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_FCP | LOG_FCP_ERROR,
- "0735 FCP Read Check Error "
+ "9026 FCP Read Check Error "
"and Underrun Data: x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
scsi_get_resid(cmnd), fcpi_parm,
@@ -793,7 +1638,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
(scsi_bufflen(cmnd) - scsi_get_resid(cmnd)
< cmnd->underflow)) {
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
- "0717 FCP command x%x residual "
+ "9027 FCP command x%x residual "
"underrun converted to error "
"Data: x%x x%x x%x\n",
cmnd->cmnd[0], scsi_bufflen(cmnd),
@@ -802,7 +1647,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
}
} else if (resp_info & RESID_OVER) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0720 FCP command x%x residual overrun error. "
+ "9028 FCP command x%x residual overrun error. "
"Data: x%x x%x \n", cmnd->cmnd[0],
scsi_bufflen(cmnd), scsi_get_resid(cmnd));
host_status = DID_ERROR;
@@ -814,7 +1659,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm &&
(cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR,
- "0734 FCP Read Check Error Data: "
+ "9029 FCP Read Check Error Data: "
"x%x x%x x%x x%x\n",
be32_to_cpu(fcpcmd->fcpDl),
be32_to_cpu(fcprsp->rspResId),
@@ -828,6 +1673,16 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
}
+/**
+ * lpfc_scsi_cmd_iocb_cmpl: Scsi cmnd IOCB completion routine.
+ * @phba: The Hba for which this call is being executed.
+ * @pIocbIn: The command IOCBQ for the scsi cmnd.
+ * @pIocbOut: The response IOCBQ for the scsi cmnd .
+ *
+ * This routine assigns scsi command result by looking into response IOCB
+ * status field appropriately. This routine handles QUEUE FULL condition as
+ * well by ramping down device queue depth.
+ **/
static void
lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut)
@@ -846,7 +1701,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
- atomic_dec(&pnode->cmd_pending);
+ if (pnode && NLP_CHK_NODE_ACT(pnode))
+ atomic_dec(&pnode->cmd_pending);
if (lpfc_cmd->status) {
if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
@@ -856,7 +1712,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->status = IOSTAT_DEFAULT;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0729 FCP cmd x%x failed <%d/%d> "
+ "9030 FCP cmd x%x failed <%d/%d> "
"status: x%x result: x%x Data: x%x x%x\n",
cmd->cmnd[0],
cmd->device ? cmd->device->id : 0xffff,
@@ -904,7 +1760,28 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_cmd->result == IOERR_ABORT_REQUESTED) {
cmd->result = ScsiResult(DID_REQUEUE, 0);
break;
- } /* else: fall through */
+ }
+
+ if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
+ lpfc_cmd->result == IOERR_TX_DMA_FAILED) &&
+ pIocbOut->iocb.unsli3.sli3_bg.bgstat) {
+ if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ /*
+ * This is a response for a BG enabled
+ * cmd. Parse BG error
+ */
+ lpfc_parse_bg_err(phba, lpfc_cmd,
+ pIocbOut);
+ break;
+ } else {
+ lpfc_printf_vlog(vport, KERN_WARNING,
+ LOG_BG,
+ "9031 non-zero BGSTAT "
+ "on unprotected cmd");
+ }
+ }
+
+ /* else: fall through */
default:
cmd->result = ScsiResult(DID_ERROR, 0);
break;
@@ -936,23 +1813,31 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
time_after(jiffies, lpfc_cmd->start_time +
msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) {
spin_lock_irqsave(sdev->host->host_lock, flags);
- if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) &&
- (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) &&
- ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10))))
- pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending);
-
- pnode->last_change_time = jiffies;
+ if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if (pnode->cmd_qdepth >
+ atomic_read(&pnode->cmd_pending) &&
+ (atomic_read(&pnode->cmd_pending) >
+ LPFC_MIN_TGT_QDEPTH) &&
+ ((cmd->cmnd[0] == READ_10) ||
+ (cmd->cmnd[0] == WRITE_10)))
+ pnode->cmd_qdepth =
+ atomic_read(&pnode->cmd_pending);
+
+ pnode->last_change_time = jiffies;
+ }
spin_unlock_irqrestore(sdev->host->host_lock, flags);
- } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
+ } else if (pnode && NLP_CHK_NODE_ACT(pnode)) {
+ if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) &&
time_after(jiffies, pnode->last_change_time +
- msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
- spin_lock_irqsave(sdev->host->host_lock, flags);
- pnode->cmd_qdepth += pnode->cmd_qdepth *
- LPFC_TGTQ_RAMPUP_PCENT / 100;
- if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
- pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
- pnode->last_change_time = jiffies;
- spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) {
+ spin_lock_irqsave(sdev->host->host_lock, flags);
+ pnode->cmd_qdepth += pnode->cmd_qdepth *
+ LPFC_TGTQ_RAMPUP_PCENT / 100;
+ if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH)
+ pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH;
+ pnode->last_change_time = jiffies;
+ spin_unlock_irqrestore(sdev->host->host_lock, flags);
+ }
}
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
@@ -1067,6 +1952,15 @@ lpfc_fcpcmd_to_iocb(uint8_t *data, struct fcp_cmnd *fcp_cmnd)
}
}
+/**
+ * lpfc_scsi_prep_cmnd: Routine to convert scsi cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: The scsi command which needs to send.
+ * @pnode: Pointer to lpfc_nodelist.
+ *
+ * This routine initializes fcp_cmnd and iocb data structure from scsi command
+ * to transfer.
+ **/
static void
lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_nodelist *pnode)
@@ -1122,7 +2016,6 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else {
iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
iocb_cmd->ulpPU = PARM_READ_CHECK;
- iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd);
fcp_cmnd->fcpCntl3 = READ_DATA;
phba->fc4InputRequests++;
}
@@ -1133,7 +2026,8 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcp_cmnd->fcpCntl3 = 0;
phba->fc4ControlRequests++;
}
- if (phba->sli_rev == 3)
+ if (phba->sli_rev == 3 &&
+ !(phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(iocb_cmd->unsli3.fcp_ext.icd, fcp_cmnd);
/*
* Finish initializing those IOCB fields that are independent
@@ -1152,6 +2046,19 @@ lpfc_scsi_prep_cmnd(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
piocbq->vport = vport;
}
+/**
+ * lpfc_scsi_prep_task_mgmt_cmnd: Convert scsi TM cmnd to FCP information unit.
+ * @vport: The virtual port for which this call is being executed.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure.
+ * @lun: Logical unit number.
+ * @task_mgmt_cmd: SCSI task management command.
+ *
+ * This routine creates FCP information unit corresponding to @task_mgmt_cmd.
+ *
+ * Return codes:
+ * 0 - Error
+ * 1 - Success
+ **/
static int
lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
struct lpfc_scsi_buf *lpfc_cmd,
@@ -1178,7 +2085,8 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
int_to_scsilun(lun, &fcp_cmnd->fcp_lun);
fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
- if (vport->phba->sli_rev == 3)
+ if (vport->phba->sli_rev == 3 &&
+ !(vport->phba->sli3_options & LPFC_SLI3_BG_ENABLED))
lpfc_fcpcmd_to_iocb(piocb->unsli3.fcp_ext.icd, fcp_cmnd);
piocb->ulpCommand = CMD_FCP_ICMND64_CR;
piocb->ulpContext = ndlp->nlp_rpi;
@@ -1201,6 +2109,15 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_vport *vport,
return 1;
}
+/**
+ * lpc_taskmgmt_def_cmpl: IOCB completion routine for task management command.
+ * @phba: The Hba for which this call is being executed.
+ * @cmdiocbq: Pointer to lpfc_iocbq data structure.
+ * @rspiocbq: Pointer to lpfc_iocbq data structure.
+ *
+ * This routine is IOCB completion routine for device reset and target reset
+ * routine. This routine release scsi buffer associated with lpfc_cmd.
+ **/
static void
lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
struct lpfc_iocbq *cmdiocbq,
@@ -1213,6 +2130,20 @@ lpfc_tskmgmt_def_cmpl(struct lpfc_hba *phba,
return;
}
+/**
+ * lpfc_scsi_tgt_reset: Target reset handler.
+ * @lpfc_cmd: Pointer to lpfc_scsi_buf data structure
+ * @vport: The virtual port for which this call is being executed.
+ * @tgt_id: Target ID.
+ * @lun: Lun number.
+ * @rdata: Pointer to lpfc_rport_data.
+ *
+ * This routine issues a TARGET RESET iocb to reset a target with @tgt_id ID.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success.
+ **/
static int
lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
unsigned tgt_id, unsigned int lun,
@@ -1266,6 +2197,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport,
return ret;
}
+/**
+ * lpfc_info: Info entry point of scsi_host_template data structure.
+ * @host: The scsi host for which this call is being executed.
+ *
+ * This routine provides module information about hba.
+ *
+ * Reutrn code:
+ * Pointer to char - Success.
+ **/
const char *
lpfc_info(struct Scsi_Host *host)
{
@@ -1295,6 +2235,13 @@ lpfc_info(struct Scsi_Host *host)
return lpfcinfobuf;
}
+/**
+ * lpfc_poll_rearm_time: Routine to modify fcp_poll timer of hba.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine modifies fcp_poll_timer field of @phba by cfg_poll_tmo.
+ * The default value of cfg_poll_tmo is 10 milliseconds.
+ **/
static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
{
unsigned long poll_tmo_expires =
@@ -1305,11 +2252,25 @@ static __inline__ void lpfc_poll_rearm_timer(struct lpfc_hba * phba)
poll_tmo_expires);
}
+/**
+ * lpfc_poll_start_timer: Routine to start fcp_poll_timer of HBA.
+ * @phba: The Hba for which this call is being executed.
+ *
+ * This routine starts the fcp_poll_timer of @phba.
+ **/
void lpfc_poll_start_timer(struct lpfc_hba * phba)
{
lpfc_poll_rearm_timer(phba);
}
+/**
+ * lpfc_poll_timeout: Restart polling timer.
+ * @ptr: Map to lpfc_hba data structure pointer.
+ *
+ * This routine restarts fcp_poll timer, when FCP ring polling is enable
+ * and FCP Ring interrupt is disable.
+ **/
+
void lpfc_poll_timeout(unsigned long ptr)
{
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
@@ -1321,6 +2282,20 @@ void lpfc_poll_timeout(unsigned long ptr)
}
}
+/**
+ * lpfc_queuecommand: Queuecommand entry point of Scsi Host Templater data
+ * structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ * @done: Pointer to done routine.
+ *
+ * Driver registers this routine to scsi midlayer to submit a @cmd to process.
+ * This routine prepares an IOCB from scsi command and provides to firmware.
+ * The @done callback is invoked after driver finished processing the command.
+ *
+ * Return value :
+ * 0 - Success
+ * SCSI_MLQUEUE_HOST_BUSY - Block all devices served by this host temporarily.
+ **/
static int
lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
{
@@ -1340,6 +2315,17 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_fail_command;
}
+ if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
+ scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+
+ printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x "
+ "str=%s without registering for BlockGuard - "
+ "Rejecting command\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ goto out_fail_command;
+ }
+
/*
* Catch race where our node has transitioned, but the
* transport is still transitioning.
@@ -1348,12 +2334,13 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0);
goto out_fail_command;
}
- if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)
+ if (vport->cfg_max_scsicmpl_time &&
+ (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth))
goto out_host_busy;
lpfc_cmd = lpfc_get_scsi_buf(phba);
if (lpfc_cmd == NULL) {
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
"0707 driver's buffer pool is empty, "
@@ -1361,7 +2348,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_host_busy;
}
- lpfc_cmd->start_time = jiffies;
/*
* Store the midlayer's command structure for the completion phase
* and complete the command initialization.
@@ -1373,7 +2359,65 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->host_scribble = (unsigned char *)lpfc_cmd;
cmnd->scsi_done = done;
- err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
+ "str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x \n",
+ cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
+ cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
+ cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9035 BLKGRD: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9036 BLKGRD: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors,
+ cmnd);
+
+ err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
+ } else {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x"
+ " str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x \n",
+ cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
+ cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
+ cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 dbg: READ @ sector %llu, "
+ "count %lu\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors);
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9041 dbg: WRITE @ sector %llu, "
+ "count %lu cmd=%p\n",
+ (unsigned long long)scsi_get_lba(cmnd),
+ cmnd->request->nr_sectors, cmnd);
+ else
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9042 dbg: parser not implemented\n");
+ err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+ }
+
if (err)
goto out_host_busy_free_buf;
@@ -1382,9 +2426,10 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
atomic_inc(&ndlp->cmd_pending);
err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
&lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
- if (err)
+ if (err) {
+ atomic_dec(&ndlp->cmd_pending);
goto out_host_busy_free_buf;
-
+ }
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_poll_fcp_ring(phba);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
@@ -1394,7 +2439,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
out_host_busy_free_buf:
- atomic_dec(&ndlp->cmd_pending);
lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
lpfc_release_scsi_buf(phba, lpfc_cmd);
out_host_busy:
@@ -1405,6 +2449,12 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
return 0;
}
+/**
+ * lpfc_block_error_handler: Routine to block error handler.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
+ **/
static void
lpfc_block_error_handler(struct scsi_cmnd *cmnd)
{
@@ -1421,6 +2471,17 @@ lpfc_block_error_handler(struct scsi_cmnd *cmnd)
return;
}
+/**
+ * lpfc_abort_handler: Eh_abort_handler entry point of Scsi Host Template data
+ *structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine aborts @cmnd pending in base driver.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_abort_handler(struct scsi_cmnd *cmnd)
{
@@ -1516,6 +2577,18 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_device_reset_handler: eh_device_reset entry point of Scsi Host Template
+ *data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does a device reset by sending a TARGET_RESET task management
+ * command.
+ *
+ * Return code :
+ * 0x2003 - Error
+ * 0ex2002 - Success
+ **/
static int
lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1560,7 +2633,7 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
@@ -1633,6 +2706,17 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_bus_reset_handler: eh_bus_reset_handler entry point of Scsi Host
+ * Template data structure.
+ * @cmnd: Pointer to scsi_cmnd data structure.
+ *
+ * This routine does target reset to all target on @cmnd->device->host.
+ *
+ * Return Code:
+ * 0x2003 - Error
+ * 0x2002 - Success
+ **/
static int
lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
{
@@ -1657,7 +2741,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
fc_get_event_number(),
sizeof(scsi_event),
(char *)&scsi_event,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
lpfc_block_error_handler(cmnd);
/*
@@ -1723,6 +2807,20 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
return ret;
}
+/**
+ * lpfc_slave_alloc: slave_alloc entry point of Scsi Host Template data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine populates the cmds_per_lun count + 2 scsi_bufs into this host's
+ * globally available list of scsi buffers. This routine also makes sure scsi
+ * buffer is not allocated more than HBA limit conveyed to midlayer. This list
+ * of scsi buffer exists for the lifetime of the driver.
+ *
+ * Return codes:
+ * non-0 - Error
+ * 0 - Success
+ **/
static int
lpfc_slave_alloc(struct scsi_device *sdev)
{
@@ -1784,6 +2882,19 @@ lpfc_slave_alloc(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_configure: slave_configure entry point of Scsi Host Templater data
+ * structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine configures following items
+ * - Tag command queuing support for @sdev if supported.
+ * - Dev loss time out value of fc_rport.
+ * - Enable SLI polling for fcp ring if ENABLE_FCP_RING_POLLING flag is set.
+ *
+ * Return codes:
+ * 0 - Success
+ **/
static int
lpfc_slave_configure(struct scsi_device *sdev)
{
@@ -1813,6 +2924,12 @@ lpfc_slave_configure(struct scsi_device *sdev)
return 0;
}
+/**
+ * lpfc_slave_destroy: slave_destroy entry point of SHT data structure.
+ * @sdev: Pointer to scsi_device.
+ *
+ * This routine sets @sdev hostatdata filed to null.
+ **/
static void
lpfc_slave_destroy(struct scsi_device *sdev)
{
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
index 437f182e2322..c7c440d5fa29 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.h
+++ b/drivers/scsi/lpfc/lpfc_scsi.h
@@ -124,6 +124,8 @@ struct lpfc_scsi_buf {
uint32_t seg_cnt; /* Number of scatter-gather segments returned by
* dma_map_sg. The driver needs this for calls
* to dma_unmap_sg. */
+ uint32_t prot_seg_cnt; /* seg_cnt's counterpart for protection data */
+
dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */
/*
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 8ab5babdeebc..01dfdc8696f8 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -542,6 +542,7 @@ lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
*/
nextiocb->iocb.ulpIoTag = (nextiocb->iocb_cmpl) ? nextiocb->iotag : 0;
+
if (pring->ringno == LPFC_ELS_RING) {
lpfc_debugfs_slow_ring_trc(phba,
"IOCB cmd ring: wd4:x%08x wd6:x%08x wd7:x%08x",
@@ -1259,68 +1260,6 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
}
/**
- * lpfc_sli_replace_hbqbuff: Replace the HBQ buffer with a new buffer.
- * @phba: Pointer to HBA context object.
- * @tag: Tag for the HBQ buffer.
- *
- * This function is called from unsolicited event handler code path to get the
- * HBQ buffer associated with an unsolicited iocb. This function is called with
- * no lock held. It returns the buffer associated with the given tag and posts
- * another buffer to the firmware. Note that the new buffer must be allocated
- * before taking the hbalock and that the hba lock must be held until it is
- * finished with the hbq entry swap.
- **/
-static struct lpfc_dmabuf *
-lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
-{
- struct hbq_dmabuf *hbq_entry, *new_hbq_entry;
- uint32_t hbqno;
- void *virt; /* virtual address ptr */
- dma_addr_t phys; /* mapped address */
- unsigned long flags;
-
- hbqno = tag >> 16;
- new_hbq_entry = (phba->hbqs[hbqno].hbq_alloc_buffer)(phba);
- /* Check whether HBQ is still in use */
- spin_lock_irqsave(&phba->hbalock, flags);
- if (!phba->hbq_in_use) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
-
- hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
- if (hbq_entry == NULL) {
- if (new_hbq_entry)
- (phba->hbqs[hbqno].hbq_free_buffer)(phba,
- new_hbq_entry);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return NULL;
- }
- list_del(&hbq_entry->dbuf.list);
-
- if (new_hbq_entry == NULL) {
- list_add_tail(&hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- return &hbq_entry->dbuf;
- }
- new_hbq_entry->tag = -1;
- phys = new_hbq_entry->dbuf.phys;
- virt = new_hbq_entry->dbuf.virt;
- new_hbq_entry->dbuf.phys = hbq_entry->dbuf.phys;
- new_hbq_entry->dbuf.virt = hbq_entry->dbuf.virt;
- hbq_entry->dbuf.phys = phys;
- hbq_entry->dbuf.virt = virt;
- lpfc_sli_free_hbq(phba, hbq_entry);
- list_add_tail(&new_hbq_entry->dbuf.list, &phba->hbqbuf_in_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
-
- return &new_hbq_entry->dbuf;
-}
-
-/**
* lpfc_sli_get_buff: Get the buffer associated with the buffer tag.
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -1334,13 +1273,17 @@ lpfc_sli_replace_hbqbuff(struct lpfc_hba *phba, uint32_t tag)
**/
static struct lpfc_dmabuf *
lpfc_sli_get_buff(struct lpfc_hba *phba,
- struct lpfc_sli_ring *pring,
- uint32_t tag)
+ struct lpfc_sli_ring *pring,
+ uint32_t tag)
{
+ struct hbq_dmabuf *hbq_entry;
+
if (tag & QUE_BUFTAG_BIT)
return lpfc_sli_ring_taggedbuf_get(phba, pring, tag);
- else
- return lpfc_sli_replace_hbqbuff(phba, tag);
+ hbq_entry = lpfc_sli_hbqbuf_find(phba, tag);
+ if (!hbq_entry)
+ return NULL;
+ return &hbq_entry->dbuf;
}
@@ -1372,8 +1315,6 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
match = 0;
irsp = &(saveq->iocb);
- if (irsp->ulpStatus == IOSTAT_NEED_BUFFER)
- return 1;
if (irsp->ulpCommand == CMD_ASYNC_STATUS) {
if (pring->lpfc_sli_rcv_async_status)
pring->lpfc_sli_rcv_async_status(phba, pring, saveq);
@@ -1982,7 +1923,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2225,7 +2166,7 @@ lpfc_sli_handle_slow_ring_event(struct lpfc_hba *phba,
if ((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
(irsp->un.ulpWord[4] == IOERR_NO_RESOURCES)) {
spin_unlock_irqrestore(&phba->hbalock, iflag);
- lpfc_adjust_queue_depth(phba);
+ lpfc_rampdown_queue_depth(phba);
spin_lock_irqsave(&phba->hbalock, iflag);
}
@@ -2790,7 +2731,6 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
{
MAILBOX_t *mb;
struct lpfc_sli *psli;
- uint16_t skip_post;
volatile uint32_t word0;
void __iomem *to_slim;
@@ -2815,13 +2755,10 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
readl(to_slim); /* flush */
/* Only skip post after fc_ffinit is completed */
- if (phba->pport->port_state) {
- skip_post = 1;
+ if (phba->pport->port_state)
word0 = 1; /* This is really setting up word1 */
- } else {
- skip_post = 0;
+ else
word0 = 0; /* This is really setting up word1 */
- }
to_slim = phba->MBslimaddr + sizeof (uint32_t);
writel(*(uint32_t *) mb, to_slim);
readl(to_slim); /* flush */
@@ -2835,10 +2772,8 @@ lpfc_sli_brdrestart(struct lpfc_hba *phba)
memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets));
psli->stats_start = get_seconds();
- if (skip_post)
- mdelay(100);
- else
- mdelay(2000);
+ /* Give the INITFF and Post time to settle. */
+ mdelay(100);
lpfc_hba_down_post(phba);
@@ -3084,7 +3019,6 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
spin_unlock_irq(&phba->hbalock);
phba->pport->port_state = LPFC_VPORT_UNKNOWN;
lpfc_sli_brdrestart(phba);
- msleep(2500);
rc = lpfc_sli_chipset_init(phba);
if (rc)
break;
@@ -3111,7 +3045,8 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options &= ~(LPFC_SLI3_NPIV_ENABLED |
LPFC_SLI3_HBQ_ENABLED |
LPFC_SLI3_CRP_ENABLED |
- LPFC_SLI3_INB_ENABLED);
+ LPFC_SLI3_INB_ENABLED |
+ LPFC_SLI3_BG_ENABLED);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0442 Adapter failed to init, mbxCmd x%x "
@@ -3144,17 +3079,29 @@ lpfc_sli_config_port(struct lpfc_hba *phba, int sli_mode)
phba->sli3_options |= LPFC_SLI3_CRP_ENABLED;
if (pmb->mb.un.varCfgPort.ginb) {
phba->sli3_options |= LPFC_SLI3_INB_ENABLED;
+ phba->hbq_get = phba->mbox->us.s3_inb_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_inb_pgp.port;
phba->inb_ha_copy = &phba->mbox->us.s3_inb_pgp.ha_copy;
phba->inb_counter = &phba->mbox->us.s3_inb_pgp.counter;
phba->inb_last_counter =
phba->mbox->us.s3_inb_pgp.counter;
} else {
+ phba->hbq_get = phba->mbox->us.s3_pgp.hbq_get;
phba->port_gp = phba->mbox->us.s3_pgp.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
}
+
+ if (phba->cfg_enable_bg) {
+ if (pmb->mb.un.varCfgPort.gbg)
+ phba->sli3_options |= LPFC_SLI3_BG_ENABLED;
+ else
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0443 Adapter did not grant "
+ "BlockGuard\n");
+ }
} else {
+ phba->hbq_get = NULL;
phba->port_gp = phba->mbox->us.s2.port;
phba->inb_ha_copy = NULL;
phba->inb_counter = NULL;
@@ -3305,10 +3252,6 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba)
struct lpfc_sli *psli = &phba->sli;
struct lpfc_sli_ring *pring;
- if (!(phba->pport->work_port_events & WORKER_MBOX_TMO)) {
- return;
- }
-
/* Mbox cmd <mbxCommand> timeout */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
@@ -4005,7 +3948,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
shost = lpfc_shost_from_vport(phba->pport);
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(temp_event_data), (char *) &temp_event_data,
- SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
+ LPFC_NL_VENDOR_ID);
}
@@ -5184,6 +5127,10 @@ lpfc_sli_check_eratt(struct lpfc_hba *phba)
{
uint32_t ha_copy;
+ /* If PCI channel is offline, don't process it */
+ if (unlikely(pci_channel_offline(phba->pcidev)))
+ return 0;
+
/* If somebody is waiting to handle an eratt, don't process it
* here. The brdkill function will do this.
*/
@@ -5242,6 +5189,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
uint32_t ha_copy;
uint32_t work_ha_copy;
unsigned long status;
+ unsigned long iflag;
uint32_t control;
MAILBOX_t *mbox, *pmbox;
@@ -5274,7 +5222,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
if (unlikely(phba->link_state < LPFC_LINK_DOWN))
return IRQ_NONE;
/* Need to read HA REG for slow-path events */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
ha_copy = readl(phba->HAregaddr);
/* If somebody is waiting to handle an eratt don't process it
* here. The brdkill function will do this.
@@ -5294,7 +5242,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
@@ -5307,13 +5255,13 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
* Turn off Link Attention interrupts
* until CLEAR_LA done
*/
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->sli.sli_flag &= ~LPFC_PROCESS_LA;
control = readl(phba->HCregaddr);
control &= ~HC_LAINT_ENA;
writel(control, phba->HCregaddr);
readl(phba->HCregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
else
work_ha_copy &= ~HA_LATT;
@@ -5328,7 +5276,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
if (status & HA_RXMASK) {
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
control = readl(phba->HCregaddr);
lpfc_debugfs_slow_ring_trc(phba,
@@ -5357,10 +5305,10 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
(uint32_t)((unsigned long)
&phba->work_waitq));
}
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
}
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
if (work_ha_copy & HA_ERATT)
lpfc_sli_read_hs(phba);
if ((work_ha_copy & HA_MBATT) && (phba->sli.mbox_active)) {
@@ -5372,7 +5320,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
/* First check out the status word */
lpfc_sli_pcimem_bcopy(mbox, pmbox, sizeof(uint32_t));
if (pmbox->mbxOwner != OWN_HOST) {
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
/*
* Stray Mailbox Interrupt, mbxCommand <cmd>
* mbxStatus <status>
@@ -5389,7 +5337,7 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
work_ha_copy &= ~HA_MBATT;
} else {
phba->sli.mbox_active = NULL;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
phba->last_completion_time = jiffies;
del_timer(&phba->sli.mbox_tmo);
if (pmb->mbox_cmpl) {
@@ -5438,14 +5386,18 @@ lpfc_sp_intr_handler(int irq, void *dev_id)
goto send_current_mbox;
}
}
- spin_lock(&phba->pport->work_port_lock);
+ spin_lock_irqsave(
+ &phba->pport->work_port_lock,
+ iflag);
phba->pport->work_port_events &=
~WORKER_MBOX_TMO;
- spin_unlock(&phba->pport->work_port_lock);
+ spin_unlock_irqrestore(
+ &phba->pport->work_port_lock,
+ iflag);
lpfc_mbox_cmpl_put(phba, pmb);
}
} else
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
if ((work_ha_copy & HA_MBATT) &&
(phba->sli.mbox_active == NULL)) {
@@ -5461,9 +5413,9 @@ send_current_mbox:
"MBX_SUCCESS");
}
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
phba->work_ha |= work_ha_copy;
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
lpfc_worker_wake_up(phba);
}
return IRQ_HANDLED;
@@ -5495,6 +5447,7 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
uint32_t ha_copy;
unsigned long status;
+ unsigned long iflag;
/* Get the driver's phba structure from the dev_id and
* assume the HBA is not interrupting.
@@ -5520,11 +5473,11 @@ lpfc_fp_intr_handler(int irq, void *dev_id)
/* Need to read HA REG for FCP ring and other ring events */
ha_copy = readl(phba->HAregaddr);
/* Clear up only attention source related to fast-path */
- spin_lock(&phba->hbalock);
+ spin_lock_irqsave(&phba->hbalock, iflag);
writel((ha_copy & (HA_R0_CLR_MSK | HA_R1_CLR_MSK)),
phba->HAregaddr);
readl(phba->HAregaddr); /* flush */
- spin_unlock(&phba->hbalock);
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
ha_copy = phba->ha_copy;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index cc43e9de22cc..7e32e95c5392 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.2.8"
+#define LPFC_DRIVER_VERSION "8.3.0"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index a7de1cc02b40..63b54c66756c 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -288,10 +288,8 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
int vpi;
int rc = VPORT_ERROR;
int status;
- int size;
- if ((phba->sli_rev < 3) ||
- !(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
+ if ((phba->sli_rev < 3) || !(phba->cfg_enable_npiv)) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
"1808 Create VPORT failed: "
"NPIV is not enabled: SLImode:%d\n",
@@ -351,20 +349,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
- size = strnlen(fc_vport->symbolic_name, LPFC_VNAME_LEN);
- if (size) {
- vport->vname = kzalloc(size+1, GFP_KERNEL);
- if (!vport->vname) {
- lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
- "1814 Create VPORT failed. "
- "vname allocation failed.\n");
- rc = VPORT_ERROR;
- lpfc_free_vpi(phba, vpi);
- destroy_port(vport);
- goto error_out;
- }
- memcpy(vport->vname, fc_vport->symbolic_name, size+1);
- }
if (fc_vport->node_name != 0)
u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
if (fc_vport->port_name != 0)
@@ -394,6 +378,9 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
+ /* Create binary sysfs attribute for vport */
+ lpfc_alloc_sysfs_attr(vport);
+
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
@@ -405,6 +392,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
}
if (disable) {
+ lpfc_vport_set_state(vport, FC_VPORT_DISABLED);
rc = VPORT_OK;
goto out;
}
@@ -587,8 +575,12 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
spin_lock_irq(&phba->hbalock);
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(&phba->hbalock);
- kfree(vport->vname);
+
+ lpfc_free_sysfs_attr(vport);
+
lpfc_debugfs_terminate(vport);
+
+ /* Remove FC host and then SCSI host with the vport */
fc_remove_host(lpfc_shost_from_vport(vport));
scsi_remove_host(lpfc_shost_from_vport(vport));
diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c
index 887682a24e36..c24e86f07804 100644
--- a/drivers/scsi/mac_esp.c
+++ b/drivers/scsi/mac_esp.c
@@ -53,7 +53,8 @@ struct mac_esp_priv {
void __iomem *pdma_io;
int error;
};
-static struct platform_device *internal_esp, *external_esp;
+static struct platform_device *internal_pdev, *external_pdev;
+static struct esp *esp_chips[2];
#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
platform_get_drvdata((struct platform_device *) \
@@ -170,7 +171,7 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
#define MAC_ESP_PDMA_LOOP(operands) \
asm volatile ( \
- " tstw %2 \n" \
+ " tstw %1 \n" \
" jbeq 20f \n" \
"1: movew " operands " \n" \
"2: movew " operands " \n" \
@@ -188,14 +189,14 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
"14: movew " operands " \n" \
"15: movew " operands " \n" \
"16: movew " operands " \n" \
- " subqw #1,%2 \n" \
+ " subqw #1,%1 \n" \
" jbne 1b \n" \
- "20: tstw %3 \n" \
+ "20: tstw %2 \n" \
" jbeq 30f \n" \
"21: movew " operands " \n" \
- " subqw #1,%3 \n" \
+ " subqw #1,%2 \n" \
" jbne 21b \n" \
- "30: tstw %4 \n" \
+ "30: tstw %3 \n" \
" jbeq 40f \n" \
"31: moveb " operands " \n" \
"32: nop \n" \
@@ -223,8 +224,8 @@ static inline int mac_esp_wait_for_dreq(struct esp *esp)
" .long 31b,40b \n" \
" .long 32b,40b \n" \
" .previous \n" \
- : "+a" (addr) \
- : "a" (mep->pdma_io), "r" (count32), "r" (count2), "g" (esp_count))
+ : "+a" (addr), "+r" (count32), "+r" (count2) \
+ : "g" (count1), "a" (mep->pdma_io))
static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
u32 dma_count, int write, u8 cmd)
@@ -247,19 +248,20 @@ static void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
do {
unsigned int count32 = esp_count >> 5;
unsigned int count2 = (esp_count & 0x1F) >> 1;
+ unsigned int count1 = esp_count & 1;
unsigned int start_addr = addr;
if (mac_esp_wait_for_dreq(esp))
break;
if (write) {
- MAC_ESP_PDMA_LOOP("%1@,%0@+");
+ MAC_ESP_PDMA_LOOP("%4@,%0@+");
esp_count -= addr - start_addr;
} else {
unsigned int n;
- MAC_ESP_PDMA_LOOP("%0@+,%1@");
+ MAC_ESP_PDMA_LOOP("%0@+,%4@");
if (mac_esp_wait_for_empty_fifo(esp))
break;
@@ -442,6 +444,32 @@ static u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
return dma_len > 0xFFFF ? 0xFFFF : dma_len;
}
+static irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
+{
+ int got_intr;
+
+ /*
+ * This is an edge triggered IRQ, so we have to be careful to
+ * avoid missing a transition when it is shared by two ESP devices.
+ */
+
+ do {
+ got_intr = 0;
+ if (esp_chips[0] &&
+ (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) {
+ (void)scsi_esp_intr(irq, esp_chips[0]);
+ got_intr = 1;
+ }
+ if (esp_chips[1] &&
+ (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) {
+ (void)scsi_esp_intr(irq, esp_chips[1]);
+ got_intr = 1;
+ }
+ } while (got_intr);
+
+ return IRQ_HANDLED;
+}
+
static struct esp_driver_ops mac_esp_ops = {
.esp_write8 = mac_esp_write8,
.esp_read8 = mac_esp_read8,
@@ -556,10 +584,16 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
}
host->irq = IRQ_MAC_SCSI;
- err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "Mac ESP",
- esp);
- if (err < 0)
- goto fail_free_priv;
+ esp_chips[dev->id] = esp;
+ mb();
+ if (esp_chips[!dev->id] == NULL) {
+ err = request_irq(host->irq, mac_scsi_esp_intr, 0,
+ "Mac ESP", NULL);
+ if (err < 0) {
+ esp_chips[dev->id] = NULL;
+ goto fail_free_priv;
+ }
+ }
err = scsi_esp_register(esp, &dev->dev);
if (err)
@@ -568,7 +602,8 @@ static int __devinit esp_mac_probe(struct platform_device *dev)
return 0;
fail_free_irq:
- free_irq(host->irq, esp);
+ if (esp_chips[!dev->id] == NULL)
+ free_irq(host->irq, esp);
fail_free_priv:
kfree(mep);
fail_free_command_block:
@@ -587,7 +622,9 @@ static int __devexit esp_mac_remove(struct platform_device *dev)
scsi_esp_unregister(esp);
- free_irq(irq, esp);
+ esp_chips[dev->id] = NULL;
+ if (!(esp_chips[0] || esp_chips[1]))
+ free_irq(irq, NULL);
kfree(mep);
@@ -614,19 +651,18 @@ static int __init mac_esp_init(void)
if (err)
return err;
- internal_esp = platform_device_alloc(DRV_MODULE_NAME, 0);
- if (internal_esp && platform_device_add(internal_esp)) {
- platform_device_put(internal_esp);
- internal_esp = NULL;
+ internal_pdev = platform_device_alloc(DRV_MODULE_NAME, 0);
+ if (internal_pdev && platform_device_add(internal_pdev)) {
+ platform_device_put(internal_pdev);
+ internal_pdev = NULL;
}
-
- external_esp = platform_device_alloc(DRV_MODULE_NAME, 1);
- if (external_esp && platform_device_add(external_esp)) {
- platform_device_put(external_esp);
- external_esp = NULL;
+ external_pdev = platform_device_alloc(DRV_MODULE_NAME, 1);
+ if (external_pdev && platform_device_add(external_pdev)) {
+ platform_device_put(external_pdev);
+ external_pdev = NULL;
}
- if (internal_esp || external_esp) {
+ if (internal_pdev || external_pdev) {
return 0;
} else {
platform_driver_unregister(&esp_mac_driver);
@@ -638,13 +674,13 @@ static void __exit mac_esp_exit(void)
{
platform_driver_unregister(&esp_mac_driver);
- if (internal_esp) {
- platform_device_unregister(internal_esp);
- internal_esp = NULL;
+ if (internal_pdev) {
+ platform_device_unregister(internal_pdev);
+ internal_pdev = NULL;
}
- if (external_esp) {
- platform_device_unregister(external_esp);
- external_esp = NULL;
+ if (external_pdev) {
+ platform_device_unregister(external_pdev);
+ external_pdev = NULL;
}
}
diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c
index 0248919bc2df..bf2a1c516293 100644
--- a/drivers/scsi/mac_scsi.c
+++ b/drivers/scsi/mac_scsi.c
@@ -47,7 +47,6 @@
#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/machw.h>
#include <asm/mac_via.h>
#include "scsi.h"
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index 22052bb7becb..d06ec5aa6924 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -3401,8 +3401,7 @@ static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_i
data->IrqNumber = pdev->irq;
data->BaseAddress = pci_resource_start(pdev, 0);
data->NumAddress = pci_resource_len (pdev, 0);
- data->MmioAddress = ioremap_nocache(pci_resource_start(pdev, 1),
- pci_resource_len (pdev, 1));
+ data->MmioAddress = pci_ioremap_bar(pdev, 1);
data->MmioLength = pci_resource_len (pdev, 1);
pci_set_master(pdev);
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index b6cd12b2e996..8cb9240596ab 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -4294,8 +4294,7 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
error = -ENODEV;
#if MEMORY_MAPPED_IO
- ha->mmpbase = ioremap(pci_resource_start(ha->pdev, 1),
- pci_resource_len(ha->pdev, 1));
+ ha->mmpbase = pci_ioremap_bar(ha->pdev, 1);
if (!ha->mmpbase) {
printk(KERN_INFO "qla1280: Unable to map I/O memory\n");
goto error_free_response_ring;
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index ed731968f15f..cd53627cc761 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -19,8 +19,9 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->fw_dump_reading == 0)
return 0;
@@ -34,8 +35,9 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
int reading;
if (off != 0)
@@ -48,7 +50,7 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
break;
qla_printk(KERN_INFO, ha,
- "Firmware dump cleared on (%ld).\n", ha->host_no);
+ "Firmware dump cleared on (%ld).\n", vha->host_no);
ha->fw_dump_reading = 0;
ha->fw_dumped = 0;
@@ -59,14 +61,14 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj,
qla_printk(KERN_INFO, ha,
"Raw firmware dump ready for read on (%ld).\n",
- ha->host_no);
+ vha->host_no);
}
break;
case 2:
- qla2x00_alloc_fw_dump(ha);
+ qla2x00_alloc_fw_dump(vha);
break;
case 3:
- qla2x00_system_error(ha);
+ qla2x00_system_error(vha);
break;
}
return (count);
@@ -87,8 +89,9 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN))
return 0;
@@ -103,8 +106,9 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
uint16_t cnt;
if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size)
@@ -134,11 +138,11 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj,
}
/* Write NVRAM. */
- ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count);
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->nvram, ha->nvram_base,
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->nvram_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->nvram, ha->nvram_base,
count);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
return (count);
}
@@ -158,8 +162,9 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->optrom_state != QLA_SREADING)
return 0;
@@ -173,8 +178,9 @@ qla2x00_sysfs_write_optrom(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (ha->optrom_state != QLA_SWRITING)
return -EINVAL;
@@ -203,8 +209,10 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
uint32_t start = 0;
uint32_t size = ha->optrom_size;
int val, valid;
@@ -262,7 +270,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
ha->optrom_region_start, ha->optrom_region_size));
memset(ha->optrom_buffer, 0, ha->optrom_region_size);
- ha->isp_ops->read_optrom(ha, ha->optrom_buffer,
+ ha->isp_ops->read_optrom(vha, ha->optrom_buffer,
ha->optrom_region_start, ha->optrom_region_size);
break;
case 2:
@@ -333,7 +341,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj,
"Writing flash region -- 0x%x/0x%x.\n",
ha->optrom_region_start, ha->optrom_region_size));
- ha->isp_ops->write_optrom(ha, ha->optrom_buffer,
+ ha->isp_ops->write_optrom(vha, ha->optrom_buffer,
ha->optrom_region_start, ha->optrom_region_size);
break;
default:
@@ -356,8 +364,9 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN))
return 0;
@@ -371,15 +380,16 @@ qla2x00_sysfs_write_vpd(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size)
return 0;
/* Write NVRAM. */
- ha->isp_ops->write_nvram(ha, (uint8_t *)buf, ha->vpd_base, count);
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd, ha->vpd_base, count);
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->vpd_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, ha->vpd_base, count);
return count;
}
@@ -399,8 +409,9 @@ qla2x00_sysfs_read_sfp(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj,
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
uint16_t iter, addr, offset;
int rval;
@@ -429,7 +440,7 @@ do_read:
offset = 0;
}
- rval = qla2x00_read_sfp(ha, ha->sfp_data_dma, addr, offset,
+ rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, addr, offset,
SFP_BLOCK_SIZE);
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
@@ -469,30 +480,31 @@ static struct sysfs_entry {
};
void
-qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha)
+qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
{
- struct Scsi_Host *host = ha->host;
+ struct Scsi_Host *host = vha->host;
struct sysfs_entry *iter;
int ret;
for (iter = bin_file_entries; iter->name; iter++) {
- if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha))
+ if (iter->is4GBp_only && !IS_FWI2_CAPABLE(vha->hw))
continue;
ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
iter->attr);
if (ret)
- qla_printk(KERN_INFO, ha,
+ qla_printk(KERN_INFO, vha->hw,
"Unable to create sysfs %s binary attribute "
"(%d).\n", iter->name, ret);
}
}
void
-qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
+qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
{
- struct Scsi_Host *host = ha->host;
+ struct Scsi_Host *host = vha->host;
struct sysfs_entry *iter;
+ struct qla_hw_data *ha = vha->hw;
for (iter = bin_file_entries; iter->name; iter++) {
if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha))
@@ -503,7 +515,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
}
if (ha->beacon_blink_led == 1)
- ha->isp_ops->beacon_off(ha);
+ ha->isp_ops->beacon_off(vha);
}
/* Scsi_Host attributes. */
@@ -519,22 +531,24 @@ static ssize_t
qla2x00_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- char fw_str[30];
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ char fw_str[128];
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->isp_ops->fw_version_str(ha, fw_str));
+ ha->isp_ops->fw_version_str(vha, fw_str));
}
static ssize_t
qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
uint32_t sn;
if (IS_FWI2_CAPABLE(ha)) {
- qla2xxx_get_vpd_field(ha, "SN", buf, PAGE_SIZE);
+ qla2xxx_get_vpd_field(vha, "SN", buf, PAGE_SIZE);
return snprintf(buf, PAGE_SIZE, "%s\n", buf);
}
@@ -547,15 +561,16 @@ static ssize_t
qla2x00_isp_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "ISP%04X\n", ha->pdev->device);
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "ISP%04X\n", vha->hw->pdev->device);
}
static ssize_t
qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n",
ha->product_id[0], ha->product_id[1], ha->product_id[2],
ha->product_id[3]);
@@ -565,43 +580,44 @@ static ssize_t
qla2x00_model_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "%s\n", ha->model_number);
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_number);
}
static ssize_t
qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->model_desc ? ha->model_desc: "");
+ vha->hw->model_desc ? vha->hw->model_desc : "");
}
static ssize_t
qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
char pci_info[30];
return snprintf(buf, PAGE_SIZE, "%s\n",
- ha->isp_ops->pci_info_str(ha, pci_info));
+ vha->hw->isp_ops->pci_info_str(vha, pci_info));
}
static ssize_t
qla2x00_link_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int len = 0;
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD)
+ if (atomic_read(&vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&vha->loop_state) == LOOP_DEAD)
len = snprintf(buf, PAGE_SIZE, "Link Down\n");
- else if (atomic_read(&ha->loop_state) != LOOP_READY ||
- test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) ||
- test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags))
+ else if (atomic_read(&vha->loop_state) != LOOP_READY ||
+ test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) ||
+ test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n");
else {
len = snprintf(buf, PAGE_SIZE, "Link Up - ");
@@ -632,10 +648,10 @@ static ssize_t
qla2x00_zio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int len = 0;
- switch (ha->zio_mode) {
+ switch (vha->hw->zio_mode) {
case QLA_ZIO_MODE_6:
len += snprintf(buf + len, PAGE_SIZE-len, "Mode 6\n");
break;
@@ -650,7 +666,8 @@ static ssize_t
qla2x00_zio_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int val = 0;
uint16_t zio_mode;
@@ -668,7 +685,7 @@ qla2x00_zio_store(struct device *dev, struct device_attribute *attr,
/* Update per-hba values and queue a reset. */
if (zio_mode != QLA_ZIO_DISABLED || ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = zio_mode;
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
return strlen(buf);
}
@@ -677,16 +694,16 @@ static ssize_t
qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
- return snprintf(buf, PAGE_SIZE, "%d us\n", ha->zio_timer * 100);
+ return snprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100);
}
static ssize_t
qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int val = 0;
uint16_t zio_timer;
@@ -696,7 +713,7 @@ qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr,
return -ERANGE;
zio_timer = (uint16_t)(val / 100);
- ha->zio_timer = zio_timer;
+ vha->hw->zio_timer = zio_timer;
return strlen(buf);
}
@@ -705,10 +722,10 @@ static ssize_t
qla2x00_beacon_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
int len = 0;
- if (ha->beacon_blink_led)
+ if (vha->hw->beacon_blink_led)
len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n");
else
len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n");
@@ -719,14 +736,15 @@ static ssize_t
qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
int val = 0;
int rval;
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return -EPERM;
- if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) {
+ if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) {
qla_printk(KERN_WARNING, ha,
"Abort ISP active -- ignoring beacon request.\n");
return -EBUSY;
@@ -736,9 +754,9 @@ qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
return -EINVAL;
if (val)
- rval = ha->isp_ops->beacon_on(ha);
+ rval = ha->isp_ops->beacon_on(vha);
else
- rval = ha->isp_ops->beacon_off(ha);
+ rval = ha->isp_ops->beacon_off(vha);
if (rval != QLA_SUCCESS)
count = 0;
@@ -750,8 +768,8 @@ static ssize_t
qla2x00_optrom_bios_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1],
ha->bios_revision[0]);
}
@@ -760,8 +778,8 @@ static ssize_t
qla2x00_optrom_efi_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1],
ha->efi_revision[0]);
}
@@ -770,8 +788,8 @@ static ssize_t
qla2x00_optrom_fcode_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1],
ha->fcode_revision[0]);
}
@@ -780,8 +798,8 @@ static ssize_t
qla2x00_optrom_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n",
ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2],
ha->fw_revision[3]);
@@ -791,8 +809,8 @@ static ssize_t
qla2x00_total_isp_aborts_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- scsi_qla_host_t *ha = shost_priv(class_to_shost(dev));
-
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
return snprintf(buf, PAGE_SIZE, "%d\n",
ha->qla_stats.total_isp_aborts);
}
@@ -848,16 +866,17 @@ struct device_attribute *qla2x00_host_attrs[] = {
static void
qla2x00_get_host_port_id(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- fc_host_port_id(shost) = ha->d_id.b.domain << 16 |
- ha->d_id.b.area << 8 | ha->d_id.b.al_pa;
+ fc_host_port_id(shost) = vha->d_id.b.domain << 16 |
+ vha->d_id.b.area << 8 | vha->d_id.b.al_pa;
}
static void
qla2x00_get_host_speed(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ struct qla_hw_data *ha = ((struct scsi_qla_host *)
+ (shost_priv(shost)))->hw;
u32 speed = FC_PORTSPEED_UNKNOWN;
switch (ha->link_data_rate) {
@@ -880,14 +899,14 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
static void
qla2x00_get_host_port_type(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
uint32_t port_type = FC_PORTTYPE_UNKNOWN;
- if (ha->parent) {
+ if (vha->vp_idx) {
fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
return;
}
- switch (ha->current_topology) {
+ switch (vha->hw->current_topology) {
case ISP_CFG_NL:
port_type = FC_PORTTYPE_LPORT;
break;
@@ -908,11 +927,11 @@ static void
qla2x00_get_starget_node_name(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
u64 node_name = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
node_name = wwn_to_u64(fcport->node_name);
@@ -927,11 +946,11 @@ static void
qla2x00_get_starget_port_name(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
u64 port_name = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
port_name = wwn_to_u64(fcport->port_name);
@@ -946,11 +965,11 @@ static void
qla2x00_get_starget_port_id(struct scsi_target *starget)
{
struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
- scsi_qla_host_t *ha = shost_priv(host);
+ scsi_qla_host_t *vha = shost_priv(host);
fc_port_t *fcport;
uint32_t port_id = ~0U;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->rport &&
starget->id == fcport->rport->scsi_target_id) {
port_id = fcport->d_id.b.domain << 16 |
@@ -999,9 +1018,9 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
* final cleanup of firmware resources (PCBs and XCBs).
*/
if (fcport->loop_id != FC_NO_LOOP_ID) {
- fcport->ha->isp_ops->fabric_logout(fcport->ha, fcport->loop_id,
- fcport->d_id.b.domain, fcport->d_id.b.area,
- fcport->d_id.b.al_pa);
+ fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
}
@@ -1011,16 +1030,18 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
static int
qla2x00_issue_lip(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- qla2x00_loop_reset(ha);
+ qla2x00_loop_reset(vha);
return 0;
}
static struct fc_host_statistics *
qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
int rval;
struct link_statistics *stats;
dma_addr_t stats_dma;
@@ -1032,21 +1053,21 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
if (stats == NULL) {
DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
goto done;
}
memset(stats, 0, DMA_POOL_SIZE);
rval = QLA_FUNCTION_FAILED;
if (IS_FWI2_CAPABLE(ha)) {
- rval = qla24xx_get_isp_stats(ha, stats, stats_dma);
- } else if (atomic_read(&ha->loop_state) == LOOP_READY &&
- !test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) &&
- !test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) &&
+ rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma);
+ } else if (atomic_read(&base_vha->loop_state) == LOOP_READY &&
+ !test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) &&
+ !test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) &&
!ha->dpc_active) {
/* Must be in a 'READY' state for statistics retrieval. */
- rval = qla2x00_get_link_status(ha, ha->loop_id, stats,
- stats_dma);
+ rval = qla2x00_get_link_status(base_vha, base_vha->loop_id,
+ stats, stats_dma);
}
if (rval != QLA_SUCCESS)
@@ -1077,29 +1098,29 @@ done:
static void
qla2x00_get_host_symbolic_name(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- qla2x00_get_sym_node_name(ha, fc_host_symbolic_name(shost));
+ qla2x00_get_sym_node_name(vha, fc_host_symbolic_name(shost));
}
static void
qla2x00_set_host_system_hostname(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
}
static void
qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
u64 node_name;
- if (ha->device_flags & SWITCH_FOUND)
- node_name = wwn_to_u64(ha->fabric_node_name);
+ if (vha->device_flags & SWITCH_FOUND)
+ node_name = wwn_to_u64(vha->fabric_node_name);
else
- node_name = wwn_to_u64(ha->node_name);
+ node_name = wwn_to_u64(vha->node_name);
fc_host_fabric_name(shost) = node_name;
}
@@ -1107,11 +1128,12 @@ qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
static void
qla2x00_get_host_port_state(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = to_qla_parent(shost_priv(shost));
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct scsi_qla_host *base_vha = pci_get_drvdata(vha->hw->pdev);
- if (!ha->flags.online)
+ if (!base_vha->flags.online)
fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
- else if (atomic_read(&ha->loop_state) == LOOP_TIMEOUT)
+ else if (atomic_read(&base_vha->loop_state) == LOOP_TIMEOUT)
fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
else
fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
@@ -1121,8 +1143,11 @@ static int
qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
{
int ret = 0;
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
- scsi_qla_host_t *vha;
+ int cnt = 0;
+ uint8_t qos = QLA_DEFAULT_QUE_QOS;
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *vha = NULL;
+ struct qla_hw_data *ha = base_vha->hw;
ret = qla24xx_vport_create_req_sanity_check(fc_vport);
if (ret) {
@@ -1144,18 +1169,19 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
atomic_set(&vha->vp_state, VP_FAILED);
/* ready to create vport */
- qla_printk(KERN_INFO, vha, "VP entry id %d assigned.\n", vha->vp_idx);
+ qla_printk(KERN_INFO, vha->hw, "VP entry id %d assigned.\n",
+ vha->vp_idx);
/* initialized vport states */
atomic_set(&vha->loop_state, LOOP_DOWN);
vha->vp_err_state= VP_ERR_PORTDWN;
vha->vp_prev_err_state= VP_ERR_UNKWN;
/* Check if physical ha port is Up */
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
/* Don't retry or attempt login of this virtual port */
DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n",
- vha->host_no));
+ base_vha->host_no));
atomic_set(&vha->loop_state, LOOP_DEAD);
if (!disable)
fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
@@ -1171,18 +1197,32 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
fc_host_supported_classes(vha->host) =
- fc_host_supported_classes(ha->host);
+ fc_host_supported_classes(base_vha->host);
fc_host_supported_speeds(vha->host) =
- fc_host_supported_speeds(ha->host);
+ fc_host_supported_speeds(base_vha->host);
qla24xx_vport_disable(fc_vport, disable);
+ /* Create a queue pair for the vport */
+ if (ha->mqenable) {
+ if (ha->npiv_info) {
+ for (; cnt < ha->nvram_npiv_size; cnt++) {
+ if (ha->npiv_info[cnt].port_name ==
+ vha->port_name &&
+ ha->npiv_info[cnt].node_name ==
+ vha->node_name) {
+ qos = ha->npiv_info[cnt].q_qos;
+ break;
+ }
+ }
+ }
+ qla25xx_create_queues(vha, qos);
+ }
+
return 0;
vport_create_failed_2:
qla24xx_disable_vp(vha);
qla24xx_deallocate_vp_id(vha);
- kfree(vha->port_name);
- kfree(vha->node_name);
scsi_host_put(vha->host);
return FC_VPORT_FAILED;
}
@@ -1191,17 +1231,34 @@ static int
qla24xx_vport_delete(struct fc_vport *fc_vport)
{
scsi_qla_host_t *vha = fc_vport->dd_data;
- scsi_qla_host_t *pha = to_qla_parent(vha);
+ fc_port_t *fcport, *tfcport;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t id = vha->vp_idx;
while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) ||
- test_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags))
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags))
msleep(1000);
+ if (ha->mqenable) {
+ if (qla25xx_delete_queues(vha, 0) != QLA_SUCCESS)
+ qla_printk(KERN_WARNING, ha,
+ "Queue delete failed.\n");
+ vha->req_ques[0] = ha->req_q_map[0]->id;
+ }
+
qla24xx_disable_vp(vha);
- qla24xx_deallocate_vp_id(vha);
- kfree(vha->node_name);
- kfree(vha->port_name);
+ fc_remove_host(vha->host);
+
+ scsi_remove_host(vha->host);
+
+ list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ fcport = NULL;
+ }
+
+ qla24xx_deallocate_vp_id(vha);
if (vha->timer_active) {
qla2x00_vp_stop_timer(vha);
@@ -1210,12 +1267,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
vha->host_no, vha->vp_idx, vha));
}
- fc_remove_host(vha->host);
-
- scsi_remove_host(vha->host);
-
scsi_host_put(vha->host);
-
+ qla_printk(KERN_INFO, ha, "vport %d deleted\n", id);
return 0;
}
@@ -1318,15 +1371,16 @@ struct fc_function_template qla2xxx_transport_vport_functions = {
};
void
-qla2x00_init_host_attr(scsi_qla_host_t *ha)
+qla2x00_init_host_attr(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
u32 speed = FC_PORTSPEED_UNKNOWN;
- fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name);
- fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name);
- fc_host_supported_classes(ha->host) = FC_COS_CLASS3;
- fc_host_max_npiv_vports(ha->host) = ha->max_npiv_vports;;
- fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count;
+ fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
+ fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
+ fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+ fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
+ fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
if (IS_QLA25XX(ha))
speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
@@ -1338,5 +1392,5 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha)
speed = FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
else
speed = FC_PORTSPEED_1GBIT;
- fc_host_supported_speeds(ha->host) = speed;
+ fc_host_supported_speeds(vha->host) = speed;
}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 510ba64bc286..1cf77772623b 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
static inline void
-qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump)
+qla2xxx_prep_dump(struct qla_hw_data *ha, struct qla2xxx_fw_dump *fw_dump)
{
fw_dump->fw_major_version = htonl(ha->fw_major_version);
fw_dump->fw_minor_version = htonl(ha->fw_minor_version);
@@ -23,22 +23,24 @@ qla2xxx_prep_dump(scsi_qla_host_t *ha, struct qla2xxx_fw_dump *fw_dump)
}
static inline void *
-qla2xxx_copy_queues(scsi_qla_host_t *ha, void *ptr)
+qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr)
{
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* Request queue. */
- memcpy(ptr, ha->request_ring, ha->request_q_length *
+ memcpy(ptr, req->ring, req->length *
sizeof(request_t));
/* Response queue. */
- ptr += ha->request_q_length * sizeof(request_t);
- memcpy(ptr, ha->response_ring, ha->response_q_length *
+ ptr += req->length * sizeof(request_t);
+ memcpy(ptr, rsp->ring, rsp->length *
sizeof(response_t));
- return ptr + (ha->response_q_length * sizeof(response_t));
+ return ptr + (rsp->length * sizeof(response_t));
}
static int
-qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram,
+qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
uint32_t ram_dwords, void **nxt)
{
int rval;
@@ -112,7 +114,7 @@ qla24xx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint32_t *ram,
}
static int
-qla24xx_dump_memory(scsi_qla_host_t *ha, uint32_t *code_ram,
+qla24xx_dump_memory(struct qla_hw_data *ha, uint32_t *code_ram,
uint32_t cram_size, void **nxt)
{
int rval;
@@ -163,7 +165,7 @@ qla24xx_pause_risc(struct device_reg_24xx __iomem *reg)
}
static int
-qla24xx_soft_reset(scsi_qla_host_t *ha)
+qla24xx_soft_reset(struct qla_hw_data *ha)
{
int rval = QLA_SUCCESS;
uint32_t cnt;
@@ -215,8 +217,8 @@ qla24xx_soft_reset(scsi_qla_host_t *ha)
}
static int
-qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram,
- uint32_t ram_words, void **nxt)
+qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram,
+ uint16_t ram_words, void **nxt)
{
int rval;
uint32_t cnt, stat, timer, words, idx;
@@ -314,16 +316,17 @@ qla2xxx_read_window(struct device_reg_2xxx __iomem *reg, uint32_t count,
* @hardware_locked: Called with the hardware_lock
*/
void
-qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint16_t __iomem *dmp_reg;
unsigned long flags;
struct qla2300_fw_dump *fw;
void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
flags = 0;
@@ -468,7 +471,7 @@ qla2300_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -483,16 +486,18 @@ qla2300_fw_dump_failed:
* @hardware_locked: Called with the hardware_lock
*/
void
-qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt, timer;
uint16_t risc_address;
uint16_t mb0, mb2;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint16_t __iomem *dmp_reg;
unsigned long flags;
struct qla2100_fw_dump *fw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = 0;
mb0 = mb2 = 0;
@@ -673,7 +678,7 @@ qla2100_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -683,12 +688,12 @@ qla2100_fw_dump_failed:
}
void
-qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
uint32_t risc_address;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
uint32_t __iomem *dmp_reg;
uint32_t *iter_reg;
@@ -697,6 +702,7 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
struct qla24xx_fw_dump *fw;
uint32_t ext_mem_cnt;
void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = ext_mem_cnt = 0;
flags = 0;
@@ -919,7 +925,7 @@ qla24xx_fw_dump_failed_0:
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -929,13 +935,14 @@ qla24xx_fw_dump_failed:
}
void
-qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
+qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
{
int rval;
uint32_t cnt;
uint32_t risc_address;
-
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct device_reg_25xxmq __iomem *reg25;
uint32_t __iomem *dmp_reg;
uint32_t *iter_reg;
uint16_t __iomem *mbx_reg;
@@ -944,6 +951,11 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
uint32_t ext_mem_cnt;
void *nxt;
struct qla2xxx_fce_chain *fcec;
+ struct qla2xxx_mq_chain *mq = NULL;
+ uint32_t qreg_size;
+ uint8_t req_cnt, rsp_cnt, que_cnt;
+ uint32_t que_idx;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
risc_address = ext_mem_cnt = 0;
flags = 0;
@@ -988,6 +1000,29 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Multi queue registers */
+ if (ha->mqenable) {
+ qreg_size = sizeof(struct qla2xxx_mq_chain);
+ mq = kzalloc(qreg_size, GFP_KERNEL);
+ if (!mq)
+ goto qla25xx_fw_dump_failed_0;
+ req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
+ rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
+ que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt;
+ mq->count = htonl(que_cnt);
+ mq->chain_size = htonl(qreg_size);
+ mq->type = __constant_htonl(DUMP_CHAIN_MQ);
+ for (cnt = 0; cnt < que_cnt; cnt++) {
+ reg25 = (struct device_reg_25xxmq *) ((void *)
+ ha->mqiobase + cnt * QLA_QUE_PAGE);
+ que_idx = cnt * 4;
+ mq->qregs[que_idx] = htonl(reg25->req_q_in);
+ mq->qregs[que_idx+1] = htonl(reg25->req_q_out);
+ mq->qregs[que_idx+2] = htonl(reg25->rsp_q_in);
+ mq->qregs[que_idx+3] = htonl(reg25->rsp_q_out);
+ }
+ }
WRT_REG_DWORD(&reg->iobase_window, 0x00);
RD_REG_DWORD(&reg->iobase_window);
@@ -1225,7 +1260,14 @@ qla25xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked)
ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
- fcec = nxt + ntohl(ha->fw_dump->eft_size);
+ if (ha->mqenable) {
+ nxt = nxt + ntohl(ha->fw_dump->eft_size);
+ memcpy(nxt, mq, qreg_size);
+ kfree(mq);
+ fcec = nxt + qreg_size;
+ } else {
+ fcec = nxt + ntohl(ha->fw_dump->eft_size);
+ }
fcec->type = __constant_htonl(DUMP_CHAIN_FCE | DUMP_CHAIN_LAST);
fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) +
fce_calc_size(ha->fce_bufs));
@@ -1248,7 +1290,7 @@ qla25xx_fw_dump_failed_0:
} else {
qla_printk(KERN_INFO, ha,
"Firmware dump saved to temp buffer (%ld/%p).\n",
- ha->host_no, ha->fw_dump);
+ base_vha->host_no, ha->fw_dump);
ha->fw_dumped = 1;
}
@@ -1256,15 +1298,15 @@ qla25xx_fw_dump_failed:
if (!hardware_locked)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
-
/****************************************************************************/
/* Driver Debug Functions. */
/****************************************************************************/
void
-qla2x00_dump_regs(scsi_qla_host_t *ha)
+qla2x00_dump_regs(scsi_qla_host_t *vha)
{
int i;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
uint16_t __iomem *mbx_reg;
@@ -1274,7 +1316,7 @@ qla2x00_dump_regs(scsi_qla_host_t *ha)
printk("Mailbox registers:\n");
for (i = 0; i < 6; i++)
- printk("scsi(%ld): mbox %d 0x%04x \n", ha->host_no, i,
+ printk("scsi(%ld): mbox %d 0x%04x \n", vha->host_no, i,
RD_REG_WORD(mbx_reg++));
}
@@ -1302,3 +1344,5 @@ qla2x00_dump_buffer(uint8_t * b, uint32_t size)
if (cnt % 16)
printk("\n");
}
+
+
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
index 2e9c0c097f5e..c1794a70a45f 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.h
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -4,6 +4,9 @@
*
* See LICENSE.qla2xxx for copyright and licensing details.
*/
+
+#include "qla_def.h"
+
/*
* Driver debug definitions.
*/
@@ -23,6 +26,7 @@
/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */
/* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */
+/* #define QL_DEBUG_LEVEL_17 */ /* Output MULTI-Q trace messages */
/*
* Macros use for debugging the driver.
@@ -43,6 +47,7 @@
#define DEBUG2_11(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#define DEBUG2_13(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#define DEBUG2_16(x) do { if (ql2xextended_error_logging) { x; } } while (0)
+#define DEBUG2_17(x) do { if (ql2xextended_error_logging) { x; } } while (0)
#if defined(QL_DEBUG_LEVEL_3)
#define DEBUG3(x) do {x;} while (0)
@@ -127,7 +132,6 @@
#else
#define DEBUG16(x) do {} while (0)
#endif
-
/*
* Firmware Dump structure definition
*/
@@ -266,8 +270,17 @@ struct qla2xxx_fce_chain {
uint32_t eregs[8];
};
+struct qla2xxx_mq_chain {
+ uint32_t type;
+ uint32_t chain_size;
+
+ uint32_t count;
+ uint32_t qregs[4 * QLA_MQ_SIZE];
+};
+
#define DUMP_CHAIN_VARIANT 0x80000000
#define DUMP_CHAIN_FCE 0x7FFFFAF0
+#define DUMP_CHAIN_MQ 0x7FFFFAF1
#define DUMP_CHAIN_LAST 0x80000000
struct qla2xxx_fw_dump {
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index b97194096d8e..a29c95204975 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -181,11 +181,14 @@
#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
+struct req_que;
+
/*
* SCSI Request Block
*/
typedef struct srb {
- struct scsi_qla_host *ha; /* HA the SP is queued on */
+ struct scsi_qla_host *vha; /* HA the SP is queued on */
+ struct req_que *que;
struct fc_port *fcport;
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
@@ -369,9 +372,17 @@ struct device_reg_2xxx {
} u_end;
};
+struct device_reg_25xxmq {
+ volatile uint32_t req_q_in;
+ volatile uint32_t req_q_out;
+ volatile uint32_t rsp_q_in;
+ volatile uint32_t rsp_q_out;
+};
+
typedef union {
struct device_reg_2xxx isp;
struct device_reg_24xx isp24;
+ struct device_reg_25xxmq isp25mq;
} device_reg_t;
#define ISP_REQ_Q_IN(ha, reg) \
@@ -1524,7 +1535,7 @@ typedef struct {
*/
typedef struct fc_port {
struct list_head list;
- struct scsi_qla_host *ha;
+ struct scsi_qla_host *vha;
uint8_t node_name[WWN_SIZE];
uint8_t port_name[WWN_SIZE];
@@ -1550,7 +1561,6 @@ typedef struct fc_port {
unsigned long last_queue_full;
unsigned long last_ramp_up;
- struct list_head vp_fcport;
uint16_t vp_idx;
} fc_port_t;
@@ -2037,6 +2047,8 @@ typedef struct vport_params {
#define VP_RET_CODE_NO_MEM 5
#define VP_RET_CODE_NOT_FOUND 6
+struct qla_hw_data;
+
/*
* ISP operations
*/
@@ -2055,10 +2067,11 @@ struct isp_operations {
char * (*fw_version_str) (struct scsi_qla_host *, char *);
irq_handler_t intr_handler;
- void (*enable_intrs) (struct scsi_qla_host *);
- void (*disable_intrs) (struct scsi_qla_host *);
+ void (*enable_intrs) (struct qla_hw_data *);
+ void (*disable_intrs) (struct qla_hw_data *);
- int (*abort_command) (struct scsi_qla_host *, srb_t *);
+ int (*abort_command) (struct scsi_qla_host *, srb_t *,
+ struct req_que *);
int (*target_reset) (struct fc_port *, unsigned int);
int (*lun_reset) (struct fc_port *, unsigned int);
int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t,
@@ -2089,6 +2102,10 @@ struct isp_operations {
uint32_t);
int (*get_flash_version) (struct scsi_qla_host *, void *);
+ int (*start_scsi) (srb_t *);
+ void (*wrt_req_reg) (struct qla_hw_data *, uint16_t, uint16_t);
+ void (*wrt_rsp_reg) (struct qla_hw_data *, uint16_t, uint16_t);
+ uint16_t (*rd_req_reg) (struct qla_hw_data *, uint16_t);
};
/* MSI-X Support *************************************************************/
@@ -2100,16 +2117,18 @@ struct isp_operations {
#define QLA_MSIX_DEFAULT 0x00
#define QLA_MSIX_RSP_Q 0x01
-#define QLA_MSIX_ENTRIES 2
#define QLA_MIDX_DEFAULT 0
#define QLA_MIDX_RSP_Q 1
+#define QLA_PCI_MSIX_CONTROL 0xa2
struct scsi_qla_host;
+struct rsp_que;
struct qla_msix_entry {
int have_irq;
- uint32_t msix_vector;
- uint16_t msix_entry;
+ uint32_t vector;
+ uint16_t entry;
+ struct rsp_que *rsp;
};
#define WATCH_INTERVAL 1 /* number of seconds */
@@ -2160,208 +2179,137 @@ struct qla_statistics {
uint64_t output_bytes;
};
-/*
- * Linux Host Adapter structure
- */
-typedef struct scsi_qla_host {
- struct list_head list;
+/* Multi queue support */
+#define MBC_INITIALIZE_MULTIQ 0x1f
+#define QLA_QUE_PAGE 0X1000
+#define QLA_MQ_SIZE 32
+#define QLA_MAX_HOST_QUES 16
+#define QLA_MAX_QUEUES 256
+#define ISP_QUE_REG(ha, id) \
+ ((ha->mqenable) ? \
+ ((void *)(ha->mqiobase) +\
+ (QLA_QUE_PAGE * id)) :\
+ ((void *)(ha->iobase)))
+#define QLA_REQ_QUE_ID(tag) \
+ ((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0)
+#define QLA_DEFAULT_QUE_QOS 5
+#define QLA_PRECONFIG_VPORTS 32
+#define QLA_MAX_VPORTS_QLA24XX 128
+#define QLA_MAX_VPORTS_QLA25XX 256
+/* Response queue data structure */
+struct rsp_que {
+ dma_addr_t dma;
+ response_t *ring;
+ response_t *ring_ptr;
+ uint16_t ring_index;
+ uint16_t out_ptr;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t vp_idx;
+ struct qla_hw_data *hw;
+ struct qla_msix_entry *msix;
+ struct req_que *req;
+};
- /* Commonly used flags and state information. */
- struct Scsi_Host *host;
- struct pci_dev *pdev;
+/* Request queue data structure */
+struct req_que {
+ dma_addr_t dma;
+ request_t *ring;
+ request_t *ring_ptr;
+ uint16_t ring_index;
+ uint16_t in_ptr;
+ uint16_t cnt;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t qos;
+ uint16_t vp_idx;
+ struct rsp_que *rsp;
+ srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
+ uint32_t current_outstanding_cmd;
+ int max_q_depth;
+};
- unsigned long host_no;
+/*
+ * Qlogic host adapter specific data structure.
+*/
+struct qla_hw_data {
+ struct pci_dev *pdev;
+ /* SRB cache. */
+#define SRB_MIN_REQ 128
+ mempool_t *srb_mempool;
volatile struct {
- uint32_t init_done :1;
- uint32_t online :1;
uint32_t mbox_int :1;
uint32_t mbox_busy :1;
- uint32_t rscn_queue_overflow :1;
- uint32_t reset_active :1;
-
- uint32_t management_server_logged_in :1;
- uint32_t process_response_queue :1;
uint32_t disable_risc_code_load :1;
uint32_t enable_64bit_addressing :1;
uint32_t enable_lip_reset :1;
- uint32_t enable_lip_full_login :1;
uint32_t enable_target_reset :1;
+ uint32_t enable_lip_full_login :1;
uint32_t enable_led_scheme :1;
uint32_t inta_enabled :1;
uint32_t msi_enabled :1;
uint32_t msix_enabled :1;
uint32_t disable_serdes :1;
uint32_t gpsc_supported :1;
- uint32_t vsan_enabled :1;
+ uint32_t vsan_enabled :1;
uint32_t npiv_supported :1;
uint32_t fce_enabled :1;
- uint32_t hw_event_marker_found :1;
+ uint32_t hw_event_marker_found:1;
} flags;
- atomic_t loop_state;
-#define LOOP_TIMEOUT 1
-#define LOOP_DOWN 2
-#define LOOP_UP 3
-#define LOOP_UPDATE 4
-#define LOOP_READY 5
-#define LOOP_DEAD 6
-
- unsigned long dpc_flags;
-#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
-#define RESET_ACTIVE 1
-#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
-#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
-#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
-#define LOOP_RESYNC_ACTIVE 5
-#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
-#define RSCN_UPDATE 7 /* Perform an RSCN update. */
-#define MAILBOX_RETRY 8
-#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
-#define FAILOVER_EVENT_NEEDED 10
-#define FAILOVER_EVENT 11
-#define FAILOVER_NEEDED 12
-#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
-#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
-#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
-#define ABORT_QUEUES_NEEDED 16
-#define RELOGIN_NEEDED 17
-#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
-#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
-#define ISP_ABORT_RETRY 20 /* ISP aborted. */
-#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
-#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
-#define IOCTL_ERROR_RECOVERY 23
-#define LOOP_RESET_NEEDED 24
-#define BEACON_BLINK_NEEDED 25
-#define REGISTER_FDMI_NEEDED 26
-#define FCPORT_UPDATE_NEEDED 27
-#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */
-#define UNLOADING 29
-#define NPIV_CONFIG_NEEDED 30
-
- uint32_t device_flags;
-#define DFLG_LOCAL_DEVICES BIT_0
-#define DFLG_RETRY_LOCAL_DEVICES BIT_1
-#define DFLG_FABRIC_DEVICES BIT_2
-#define SWITCH_FOUND BIT_3
-#define DFLG_NO_CABLE BIT_4
-
-#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
-#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
- uint32_t device_type;
-#define DT_ISP2100 BIT_0
-#define DT_ISP2200 BIT_1
-#define DT_ISP2300 BIT_2
-#define DT_ISP2312 BIT_3
-#define DT_ISP2322 BIT_4
-#define DT_ISP6312 BIT_5
-#define DT_ISP6322 BIT_6
-#define DT_ISP2422 BIT_7
-#define DT_ISP2432 BIT_8
-#define DT_ISP5422 BIT_9
-#define DT_ISP5432 BIT_10
-#define DT_ISP2532 BIT_11
-#define DT_ISP8432 BIT_12
-#define DT_ISP_LAST (DT_ISP8432 << 1)
-
-#define DT_IIDMA BIT_26
-#define DT_FWI2 BIT_27
-#define DT_ZIO_SUPPORTED BIT_28
-#define DT_OEM_001 BIT_29
-#define DT_ISP2200A BIT_30
-#define DT_EXTENDED_IDS BIT_31
-
-#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1))
-#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100)
-#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200)
-#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300)
-#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312)
-#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322)
-#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312)
-#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322)
-#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422)
-#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432)
-#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422)
-#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432)
-#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
-#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
-
-#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
- IS_QLA6312(ha) || IS_QLA6322(ha))
-#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
-#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
-#define IS_QLA25XX(ha) (IS_QLA2532(ha))
-#define IS_QLA84XX(ha) (IS_QLA8432(ha))
-#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
- IS_QLA84XX(ha))
-
-#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
-#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
-#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
-#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
-#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
-
- /* SRB cache. */
-#define SRB_MIN_REQ 128
- mempool_t *srb_mempool;
-
/* This spinlock is used to protect "io transactions", you must
- * acquire it before doing any IO to the card, eg with RD_REG*() and
- * WRT_REG*() for the duration of your entire commandtransaction.
- *
- * This spinlock is of lower priority than the io request lock.
- */
-
- spinlock_t hardware_lock ____cacheline_aligned;
+ * acquire it before doing any IO to the card, eg with RD_REG*() and
+ * WRT_REG*() for the duration of your entire commandtransaction.
+ *
+ * This spinlock is of lower priority than the io request lock.
+ */
+ spinlock_t hardware_lock ____cacheline_aligned;
int bars;
int mem_only;
- device_reg_t __iomem *iobase; /* Base I/O address */
+ device_reg_t __iomem *iobase; /* Base I/O address */
resource_size_t pio_address;
-#define MIN_IOBASE_LEN 0x100
-
- /* ISP ring lock, rings, and indexes */
- dma_addr_t request_dma; /* Physical address. */
- request_t *request_ring; /* Base virtual address */
- request_t *request_ring_ptr; /* Current address. */
- uint16_t req_ring_index; /* Current index. */
- uint16_t req_q_cnt; /* Number of available entries. */
- uint16_t request_q_length;
-
- dma_addr_t response_dma; /* Physical address. */
- response_t *response_ring; /* Base virtual address */
- response_t *response_ring_ptr; /* Current address. */
- uint16_t rsp_ring_index; /* Current index. */
- uint16_t response_q_length;
-
- struct isp_operations *isp_ops;
- /* Outstandings ISP commands. */
- srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
- uint32_t current_outstanding_cmd;
- srb_t *status_srb; /* Status continuation entry. */
+#define MIN_IOBASE_LEN 0x100
+/* Multi queue data structs */
+ device_reg_t *mqiobase;
+ uint16_t msix_count;
+ uint8_t mqenable;
+ struct req_que **req_q_map;
+ struct rsp_que **rsp_q_map;
+ unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ uint16_t max_queues;
+ struct qla_npiv_entry *npiv_info;
+ uint16_t nvram_npiv_size;
+
+ uint16_t switch_cap;
+#define FLOGI_SEQ_DEL BIT_8
+#define FLOGI_MID_SUPPORT BIT_10
+#define FLOGI_VSAN_SUPPORT BIT_12
+#define FLOGI_SP_SUPPORT BIT_13
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+ uint16_t max_loop_id;
- /* ISP configuration data. */
- uint16_t loop_id; /* Host adapter loop id */
- uint16_t switch_cap;
-#define FLOGI_SEQ_DEL BIT_8
-#define FLOGI_MID_SUPPORT BIT_10
-#define FLOGI_VSAN_SUPPORT BIT_12
-#define FLOGI_SP_SUPPORT BIT_13
uint16_t fb_rev;
-
- port_id_t d_id; /* Host adapter port id */
uint16_t max_public_loop_ids;
- uint16_t min_external_loopid; /* First external loop Id */
+ uint16_t min_external_loopid; /* First external loop Id */
#define PORT_SPEED_UNKNOWN 0xFFFF
-#define PORT_SPEED_1GB 0x00
-#define PORT_SPEED_2GB 0x01
-#define PORT_SPEED_4GB 0x03
-#define PORT_SPEED_8GB 0x04
- uint16_t link_data_rate; /* F/W operating speed */
+#define PORT_SPEED_1GB 0x00
+#define PORT_SPEED_2GB 0x01
+#define PORT_SPEED_4GB 0x03
+#define PORT_SPEED_8GB 0x04
+ uint16_t link_data_rate; /* F/W operating speed */
uint8_t current_topology;
uint8_t prev_topology;
@@ -2370,15 +2318,69 @@ typedef struct scsi_qla_host {
#define ISP_CFG_FL 4
#define ISP_CFG_F 8
- uint8_t operating_mode; /* F/W operating mode */
+ uint8_t operating_mode; /* F/W operating mode */
#define LOOP 0
#define P2P 1
#define LOOP_P2P 2
#define P2P_LOOP 3
-
- uint8_t marker_needed;
-
uint8_t interrupts_on;
+ uint32_t isp_abort_cnt;
+
+#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
+#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
+ uint32_t device_type;
+#define DT_ISP2100 BIT_0
+#define DT_ISP2200 BIT_1
+#define DT_ISP2300 BIT_2
+#define DT_ISP2312 BIT_3
+#define DT_ISP2322 BIT_4
+#define DT_ISP6312 BIT_5
+#define DT_ISP6322 BIT_6
+#define DT_ISP2422 BIT_7
+#define DT_ISP2432 BIT_8
+#define DT_ISP5422 BIT_9
+#define DT_ISP5432 BIT_10
+#define DT_ISP2532 BIT_11
+#define DT_ISP8432 BIT_12
+#define DT_ISP_LAST (DT_ISP8432 << 1)
+
+#define DT_IIDMA BIT_26
+#define DT_FWI2 BIT_27
+#define DT_ZIO_SUPPORTED BIT_28
+#define DT_OEM_001 BIT_29
+#define DT_ISP2200A BIT_30
+#define DT_EXTENDED_IDS BIT_31
+#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1))
+#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100)
+#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200)
+#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300)
+#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312)
+#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322)
+#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312)
+#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322)
+#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422)
+#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432)
+#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422)
+#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432)
+#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
+#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
+
+#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
+ IS_QLA6312(ha) || IS_QLA6322(ha))
+#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
+#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
+#define IS_QLA25XX(ha) (IS_QLA2532(ha))
+#define IS_QLA84XX(ha) (IS_QLA8432(ha))
+#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
+ IS_QLA84XX(ha))
+#define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \
+ IS_QLA25XX(ha))
+
+#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
+#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
+#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
+#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
+#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
/* HBA serial number */
uint8_t serial0;
@@ -2386,8 +2388,8 @@ typedef struct scsi_qla_host {
uint8_t serial2;
/* NVRAM configuration data */
-#define MAX_NVRAM_SIZE 4096
-#define VPD_OFFSET MAX_NVRAM_SIZE / 2
+#define MAX_NVRAM_SIZE 4096
+#define VPD_OFFSET MAX_NVRAM_SIZE / 2
uint16_t nvram_size;
uint16_t nvram_base;
void *nvram;
@@ -2401,22 +2403,8 @@ typedef struct scsi_qla_host {
uint16_t r_a_tov;
int port_down_retry_count;
uint8_t mbx_count;
- uint16_t last_loop_id;
- uint16_t mgmt_svr_loop_id;
-
- uint32_t login_retry_count;
- int max_q_depth;
-
- struct list_head work_list;
-
- /* Fibre Channel Device List. */
- struct list_head fcports;
-
- /* RSCN queue. */
- uint32_t rscn_queue[MAX_RSCN_COUNT];
- uint8_t rscn_in_ptr;
- uint8_t rscn_out_ptr;
+ uint32_t login_retry_count;
/* SNS command interfaces. */
ms_iocb_entry_t *ms_iocb;
dma_addr_t ms_iocb_dma;
@@ -2426,28 +2414,20 @@ typedef struct scsi_qla_host {
struct sns_cmd_pkt *sns_cmd;
dma_addr_t sns_cmd_dma;
-#define SFP_DEV_SIZE 256
-#define SFP_BLOCK_SIZE 64
- void *sfp_data;
- dma_addr_t sfp_data_dma;
+#define SFP_DEV_SIZE 256
+#define SFP_BLOCK_SIZE 64
+ void *sfp_data;
+ dma_addr_t sfp_data_dma;
struct task_struct *dpc_thread;
uint8_t dpc_active; /* DPC routine is active */
- /* Timeout timers. */
- uint8_t loop_down_abort_time; /* port down timer */
- atomic_t loop_down_timer; /* loop down timer */
- uint8_t link_down_timeout; /* link down timeout */
-
- uint32_t timer_active;
- struct timer_list timer;
-
dma_addr_t gid_list_dma;
struct gid_list_info *gid_list;
int gid_list_info_size;
/* Small DMA pool allocations -- maximum 256 bytes in length. */
-#define DMA_POOL_SIZE 256
+#define DMA_POOL_SIZE 256
struct dma_pool *s_dma_pool;
dma_addr_t init_cb_dma;
@@ -2459,17 +2439,17 @@ typedef struct scsi_qla_host {
mbx_cmd_t *mcp;
unsigned long mbx_cmd_flags;
-#define MBX_INTERRUPT 1
-#define MBX_INTR_WAIT 2
+#define MBX_INTERRUPT 1
+#define MBX_INTR_WAIT 2
#define MBX_UPDATE_FLASH_ACTIVE 3
- struct mutex vport_lock; /* Virtual port synchronization */
- struct completion mbx_cmd_comp; /* Serialize mbx access */
+ struct mutex vport_lock; /* Virtual port synchronization */
+ struct completion mbx_cmd_comp; /* Serialize mbx access */
struct completion mbx_intr_comp; /* Used for completion notification */
uint32_t mbx_flags;
#define MBX_IN_PROGRESS BIT_0
-#define MBX_BUSY BIT_1 /* Got the Access */
+#define MBX_BUSY BIT_1 /* Got the Access */
#define MBX_SLEEPING_ON_SEM BIT_2
#define MBX_POLLING_FOR_COMP BIT_3
#define MBX_COMPLETED BIT_4
@@ -2488,7 +2468,7 @@ typedef struct scsi_qla_host {
#define RISC_START_ADDRESS_2300 0x800
#define RISC_START_ADDRESS_2400 0x100000
- uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
+ uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
uint8_t fw_seriallink_options[4];
uint16_t fw_seriallink_options24[4];
@@ -2509,10 +2489,10 @@ typedef struct scsi_qla_host {
uint64_t fce_wr, fce_rd;
struct mutex fce_mutex;
+ uint32_t hw_event_start;
uint32_t hw_event_ptr;
uint32_t hw_event_pause_errors;
- uint8_t host_str[16];
uint32_t pci_attr;
uint16_t chip_revision;
@@ -2523,11 +2503,6 @@ typedef struct scsi_qla_host {
char model_desc[80];
uint8_t adapter_id[16+1];
- uint8_t *node_name;
- uint8_t *port_name;
- uint8_t fabric_node_name[WWN_SIZE];
- uint32_t isp_abort_cnt;
-
/* Option ROM information. */
char *optrom_buffer;
uint32_t optrom_size;
@@ -2538,13 +2513,13 @@ typedef struct scsi_qla_host {
uint32_t optrom_region_start;
uint32_t optrom_region_size;
- /* PCI expansion ROM image information. */
+/* PCI expansion ROM image information. */
#define ROM_CODE_TYPE_BIOS 0
#define ROM_CODE_TYPE_FCODE 1
#define ROM_CODE_TYPE_EFI 3
- uint8_t bios_revision[2];
- uint8_t efi_revision[2];
- uint8_t fcode_revision[16];
+ uint8_t bios_revision[2];
+ uint8_t efi_revision[2];
+ uint8_t fcode_revision[16];
uint32_t fw_revision[4];
uint32_t fdt_wrt_disable;
@@ -2553,39 +2528,144 @@ typedef struct scsi_qla_host {
uint32_t fdt_unprotect_sec_cmd;
uint32_t fdt_protect_sec_cmd;
- uint32_t flt_region_flt;
- uint32_t flt_region_fdt;
- uint32_t flt_region_boot;
- uint32_t flt_region_fw;
- uint32_t flt_region_vpd_nvram;
- uint32_t flt_region_hw_event;
- uint32_t flt_region_npiv_conf;
+ uint32_t flt_region_flt;
+ uint32_t flt_region_fdt;
+ uint32_t flt_region_boot;
+ uint32_t flt_region_fw;
+ uint32_t flt_region_vpd_nvram;
+ uint32_t flt_region_hw_event;
+ uint32_t flt_region_npiv_conf;
/* Needed for BEACON */
- uint16_t beacon_blink_led;
- uint8_t beacon_color_state;
+ uint16_t beacon_blink_led;
+ uint8_t beacon_color_state;
#define QLA_LED_GRN_ON 0x01
#define QLA_LED_YLW_ON 0x02
#define QLA_LED_ABR_ON 0x04
#define QLA_LED_ALL_ON 0x07 /* yellow, green, amber. */
/* ISP2322: red, green, amber. */
-
- uint16_t zio_mode;
- uint16_t zio_timer;
+ uint16_t zio_mode;
+ uint16_t zio_timer;
struct fc_host_statistics fc_host_stat;
- struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES];
+ struct qla_msix_entry *msix_entries;
+
+ struct list_head vp_list; /* list of VP */
+ unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) /
+ sizeof(unsigned long)];
+ uint16_t num_vhosts; /* number of vports created */
+ uint16_t num_vsans; /* number of vsan created */
+ uint16_t max_npiv_vports; /* 63 or 125 per topoloty */
+ int cur_vport_count;
+
+ struct qla_chip_state_84xx *cs84xx;
+ struct qla_statistics qla_stats;
+ struct isp_operations *isp_ops;
+};
+
+/*
+ * Qlogic scsi host structure
+ */
+typedef struct scsi_qla_host {
+ struct list_head list;
+ struct list_head vp_fcports; /* list of fcports */
+ struct list_head work_list;
+ /* Commonly used flags and state information. */
+ struct Scsi_Host *host;
+ unsigned long host_no;
+ uint8_t host_str[16];
+
+ volatile struct {
+ uint32_t init_done :1;
+ uint32_t online :1;
+ uint32_t rscn_queue_overflow :1;
+ uint32_t reset_active :1;
+
+ uint32_t management_server_logged_in :1;
+ uint32_t process_response_queue :1;
+ } flags;
+
+ atomic_t loop_state;
+#define LOOP_TIMEOUT 1
+#define LOOP_DOWN 2
+#define LOOP_UP 3
+#define LOOP_UPDATE 4
+#define LOOP_READY 5
+#define LOOP_DEAD 6
+
+ unsigned long dpc_flags;
+#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
+#define RESET_ACTIVE 1
+#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
+#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
+#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
+#define LOOP_RESYNC_ACTIVE 5
+#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
+#define RSCN_UPDATE 7 /* Perform an RSCN update. */
+#define MAILBOX_RETRY 8
+#define ISP_RESET_NEEDED 9 /* Initiate a ISP reset. */
+#define FAILOVER_EVENT_NEEDED 10
+#define FAILOVER_EVENT 11
+#define FAILOVER_NEEDED 12
+#define SCSI_RESTART_NEEDED 13 /* Processes SCSI retry queue. */
+#define PORT_RESTART_NEEDED 14 /* Processes Retry queue. */
+#define RESTART_QUEUES_NEEDED 15 /* Restarts the Lun queue. */
+#define ABORT_QUEUES_NEEDED 16
+#define RELOGIN_NEEDED 17
+#define LOGIN_RETRY_NEEDED 18 /* Initiate required fabric logins. */
+#define REGISTER_FC4_NEEDED 19 /* SNS FC4 registration required. */
+#define ISP_ABORT_RETRY 20 /* ISP aborted. */
+#define FCPORT_RESCAN_NEEDED 21 /* IO descriptor processing needed */
+#define IODESC_PROCESS_NEEDED 22 /* IO descriptor processing needed */
+#define IOCTL_ERROR_RECOVERY 23
+#define LOOP_RESET_NEEDED 24
+#define BEACON_BLINK_NEEDED 25
+#define REGISTER_FDMI_NEEDED 26
+#define FCPORT_UPDATE_NEEDED 27
+#define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */
+#define UNLOADING 29
+#define NPIV_CONFIG_NEEDED 30
+
+ uint32_t device_flags;
+#define DFLG_LOCAL_DEVICES BIT_0
+#define DFLG_RETRY_LOCAL_DEVICES BIT_1
+#define DFLG_FABRIC_DEVICES BIT_2
+#define SWITCH_FOUND BIT_3
+#define DFLG_NO_CABLE BIT_4
+
+ srb_t *status_srb; /* Status continuation entry. */
+
+ /* ISP configuration data. */
+ uint16_t loop_id; /* Host adapter loop id */
+
+ port_id_t d_id; /* Host adapter port id */
+ uint8_t marker_needed;
+ uint16_t mgmt_svr_loop_id;
+
+
+
+ /* RSCN queue. */
+ uint32_t rscn_queue[MAX_RSCN_COUNT];
+ uint8_t rscn_in_ptr;
+ uint8_t rscn_out_ptr;
+
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+
+ uint32_t timer_active;
+ struct timer_list timer;
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint8_t fabric_node_name[WWN_SIZE];
+ uint32_t vp_abort_cnt;
- struct list_head vp_list; /* list of VP */
struct fc_vport *fc_vport; /* holds fc_vport * for each vport */
- unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) / sizeof(unsigned long)];
- uint16_t num_vhosts; /* number of vports created */
- uint16_t num_vsans; /* number of vsan created */
uint16_t vp_idx; /* vport ID */
- struct scsi_qla_host *parent; /* holds pport */
unsigned long vp_flags;
- struct list_head vp_fcports; /* list of fcports */
#define VP_IDX_ACQUIRED 0 /* bit no 0 */
#define VP_CREATE_NEEDED 1
#define VP_BIND_NEEDED 2
@@ -2604,14 +2684,10 @@ typedef struct scsi_qla_host {
#define VP_ERR_FAB_NORESOURCES 3
#define VP_ERR_FAB_LOGOUT 4
#define VP_ERR_ADAP_NORESOURCES 5
- uint16_t max_npiv_vports; /* 63 or 125 per topoloty */
- int cur_vport_count;
-
- struct qla_chip_state_84xx *cs84xx;
- struct qla_statistics qla_stats;
+ struct qla_hw_data *hw;
+ int req_ques[QLA_MAX_HOST_QUES];
} scsi_qla_host_t;
-
/*
* Macros to help code, maintain, etc.
*/
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 561a4411719d..0e366a1b44b3 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -15,10 +15,11 @@ static atomic_t qla2x00_dfs_root_count;
static int
qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
{
- scsi_qla_host_t *ha = s->private;
+ scsi_qla_host_t *vha = s->private;
uint32_t cnt;
uint32_t *fce;
uint64_t fce_start;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->fce_mutex);
@@ -51,7 +52,8 @@ qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
static int
qla2x00_dfs_fce_open(struct inode *inode, struct file *file)
{
- scsi_qla_host_t *ha = inode->i_private;
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
int rval;
if (!ha->flags.fce_enabled)
@@ -60,7 +62,7 @@ qla2x00_dfs_fce_open(struct inode *inode, struct file *file)
mutex_lock(&ha->fce_mutex);
/* Pause tracing to flush FCE buffers. */
- rval = qla2x00_disable_fce_trace(ha, &ha->fce_wr, &ha->fce_rd);
+ rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd);
if (rval)
qla_printk(KERN_WARNING, ha,
"DebugFS: Unable to disable FCE (%d).\n", rval);
@@ -75,7 +77,8 @@ out:
static int
qla2x00_dfs_fce_release(struct inode *inode, struct file *file)
{
- scsi_qla_host_t *ha = inode->i_private;
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
int rval;
if (ha->flags.fce_enabled)
@@ -86,7 +89,7 @@ qla2x00_dfs_fce_release(struct inode *inode, struct file *file)
/* Re-enable FCE tracing. */
ha->flags.fce_enabled = 1;
memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));
- rval = qla2x00_enable_fce_trace(ha, ha->fce_dma, ha->fce_bufs,
+ rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs,
ha->fce_mb, &ha->fce_bufs);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -107,8 +110,9 @@ static const struct file_operations dfs_fce_ops = {
};
int
-qla2x00_dfs_setup(scsi_qla_host_t *ha)
+qla2x00_dfs_setup(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA25XX(ha))
goto out;
if (!ha->fce)
@@ -130,7 +134,7 @@ create_dir:
goto create_nodes;
mutex_init(&ha->fce_mutex);
- ha->dfs_dir = debugfs_create_dir(ha->host_str, qla2x00_dfs_root);
+ ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root);
if (!ha->dfs_dir) {
qla_printk(KERN_NOTICE, ha,
"DebugFS: Unable to create ha directory.\n");
@@ -152,8 +156,9 @@ out:
}
int
-qla2x00_dfs_remove(scsi_qla_host_t *ha)
+qla2x00_dfs_remove(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (ha->dfs_fce) {
debugfs_remove(ha->dfs_fce);
ha->dfs_fce = NULL;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index d1d14202575a..ee1f1e794c2d 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -299,7 +299,8 @@ struct init_cb_24xx {
uint32_t response_q_address[2];
uint32_t prio_request_q_address[2];
- uint8_t reserved_2[8];
+ uint16_t msix;
+ uint8_t reserved_2[6];
uint16_t atio_q_inpointer;
uint16_t atio_q_length;
@@ -372,8 +373,9 @@ struct init_cb_24xx {
* BIT 17-31 = Reserved
*/
uint32_t firmware_options_3;
-
- uint8_t reserved_3[24];
+ uint16_t qos;
+ uint16_t rid;
+ uint8_t reserved_3[20];
};
/*
@@ -754,7 +756,8 @@ struct abort_entry_24xx {
uint32_t handle_to_abort; /* System handle to abort. */
- uint8_t reserved_1[32];
+ uint16_t req_que_no;
+ uint8_t reserved_1[30];
uint8_t port_id[3]; /* PortID of destination port. */
uint8_t vp_index;
@@ -1258,7 +1261,8 @@ struct qla_npiv_header {
struct qla_npiv_entry {
uint16_t flags;
uint16_t vf_id;
- uint16_t qos;
+ uint8_t q_qos;
+ uint8_t f_qos;
uint16_t unused1;
uint8_t port_name[WWN_SIZE];
uint8_t node_name[WWN_SIZE];
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 753dbe6cce6e..0011e31205db 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -63,6 +63,7 @@ extern int ql2xallocfwdump;
extern int ql2xextended_error_logging;
extern int ql2xqfullrampup;
extern int ql2xiidmaenable;
+extern int ql2xmaxqueues;
extern int qla2x00_loop_reset(scsi_qla_host_t *);
extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -72,7 +73,10 @@ extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t,
uint16_t, uint16_t);
extern void qla2x00_abort_fcport_cmds(fc_port_t *);
-
+extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *,
+ struct qla_hw_data *);
+extern void qla2x00_free_host(struct scsi_qla_host *);
+extern void qla2x00_relogin(struct scsi_qla_host *);
/*
* Global Functions in qla_mid.c source file.
*/
@@ -94,7 +98,7 @@ extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *);
-extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *);
+extern void qla2x00_sp_compl(struct qla_hw_data *, srb_t *);
extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
@@ -105,10 +109,11 @@ extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *);
-extern void qla2xxx_wake_dpc(scsi_qla_host_t *);
-extern void qla2x00_alert_all_vps(scsi_qla_host_t *, uint16_t *);
-extern void qla2x00_async_event(scsi_qla_host_t *, uint16_t *);
-extern void qla2x00_vp_abort_isp(scsi_qla_host_t *);
+extern void qla2xxx_wake_dpc(struct scsi_qla_host *);
+extern void qla2x00_alert_all_vps(struct rsp_que *, uint16_t *);
+extern void qla2x00_async_event(scsi_qla_host_t *, struct rsp_que *,
+ uint16_t *);
+extern int qla2x00_vp_abort_isp(scsi_qla_host_t *);
/*
* Global Function Prototypes in qla_iocb.c source file.
@@ -119,8 +124,10 @@ extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
extern int qla2x00_start_scsi(srb_t *sp);
extern int qla24xx_start_scsi(srb_t *sp);
-int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
-int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
+int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
+ uint16_t, uint16_t, uint8_t);
+int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
+ uint16_t, uint16_t, uint8_t);
/*
* Global Function Prototypes in qla_mbx.c source file.
@@ -154,7 +161,7 @@ extern int
qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
extern int
-qla2x00_abort_command(scsi_qla_host_t *, srb_t *);
+qla2x00_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
extern int
qla2x00_abort_target(struct fc_port *, unsigned int);
@@ -225,7 +232,7 @@ extern int
qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *,
dma_addr_t);
-extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *);
+extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *, struct req_que *);
extern int qla24xx_abort_target(struct fc_port *, unsigned int);
extern int qla24xx_lun_reset(struct fc_port *, unsigned int);
@@ -264,10 +271,10 @@ extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *);
extern irqreturn_t qla2100_intr_handler(int, void *);
extern irqreturn_t qla2300_intr_handler(int, void *);
extern irqreturn_t qla24xx_intr_handler(int, void *);
-extern void qla2x00_process_response_queue(struct scsi_qla_host *);
-extern void qla24xx_process_response_queue(struct scsi_qla_host *);
+extern void qla2x00_process_response_queue(struct rsp_que *);
+extern void qla24xx_process_response_queue(struct rsp_que *);
-extern int qla2x00_request_irqs(scsi_qla_host_t *);
+extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
extern void qla2x00_free_irqs(scsi_qla_host_t *);
/*
@@ -367,4 +374,27 @@ extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
*/
extern int qla2x00_dfs_setup(scsi_qla_host_t *);
extern int qla2x00_dfs_remove(scsi_qla_host_t *);
+
+/* Globa function prototypes for multi-q */
+extern int qla25xx_request_irq(struct rsp_que *);
+extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *,
+ uint8_t);
+extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *,
+ uint8_t);
+extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t, uint8_t, uint8_t);
+extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t);
+extern int qla25xx_update_req_que(struct scsi_qla_host *, uint8_t, uint8_t);
+extern void qla2x00_init_response_q_entries(struct rsp_que *);
+extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
+extern int qla25xx_delete_rsp_que(struct scsi_qla_host *, struct rsp_que *);
+extern int qla25xx_create_queues(struct scsi_qla_host *, uint8_t);
+extern int qla25xx_delete_queues(struct scsi_qla_host *, uint8_t);
+extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index c2a4bfbcb05b..0a6f72973996 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -22,8 +22,9 @@ static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
+qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
{
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
ms_pkt = ha->ms_iocb;
@@ -59,8 +60,9 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
+qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
{
+ struct qla_hw_data *ha = vha->hw;
struct ct_entry_24xx *ct_pkt;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
@@ -82,7 +84,7 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *ha, uint32_t req_size, uint32_t rsp_size)
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return (ct_pkt);
}
@@ -110,16 +112,17 @@ qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
}
static int
-qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
+qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
struct ct_sns_rsp *ct_rsp, const char *routine)
{
int rval;
uint16_t comp_status;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_FUNCTION_FAILED;
if (ms_pkt->entry_status != 0) {
DEBUG2_3(printk("scsi(%ld): %s failed, error status (%x).\n",
- ha->host_no, routine, ms_pkt->entry_status));
+ vha->host_no, routine, ms_pkt->entry_status));
} else {
if (IS_FWI2_CAPABLE(ha))
comp_status = le16_to_cpu(
@@ -133,7 +136,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
if (ct_rsp->header.response !=
__constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
DEBUG2_3(printk("scsi(%ld): %s failed, "
- "rejected request:\n", ha->host_no,
+ "rejected request:\n", vha->host_no,
routine));
DEBUG2_3(qla2x00_dump_buffer(
(uint8_t *)&ct_rsp->header,
@@ -144,7 +147,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
break;
default:
DEBUG2_3(printk("scsi(%ld): %s failed, completion "
- "status (%x).\n", ha->host_no, routine,
+ "status (%x).\n", vha->host_no, routine,
comp_status));
break;
}
@@ -160,21 +163,21 @@ qla2x00_chk_ms_status(scsi_qla_host_t *ha, ms_iocb_entry_t *ms_pkt,
* Returns 0 on success.
*/
int
-qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_ga_nxt(ha, fcport));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_ga_nxt(vha, fcport);
/* Issue GA_NXT */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GA_NXT_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE,
GA_NXT_RSP_SIZE);
/* Prepare CT request */
@@ -188,13 +191,13 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GA_NXT") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GA_NXT") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -216,7 +219,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
fcport->node_name[0], fcport->node_name[1],
fcport->node_name[2], fcport->node_name[3],
fcport->node_name[4], fcport->node_name[5],
@@ -242,7 +245,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
* Returns 0 on success.
*/
int
-qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
@@ -252,16 +255,16 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
struct ct_sns_rsp *ct_rsp;
struct ct_sns_gid_pt_data *gid_data;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gid_pt(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gid_pt(vha, list);
gid_data = NULL;
/* Issue GID_PT */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GID_PT_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE,
GID_PT_RSP_SIZE);
/* Prepare CT request */
@@ -273,13 +276,13 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "GID_PT") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GID_PT") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -320,7 +323,7 @@ qla2x00_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
@@ -328,15 +331,15 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gpn_id(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gpn_id(vha, list);
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GPN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE,
GPN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -350,13 +353,13 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed "
- "(%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "(%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -381,23 +384,22 @@ qla2x00_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_gnn_id(ha, list));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gnn_id(vha, list);
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GNN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GNN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE,
GNN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -411,13 +413,13 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed "
- "(%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "(%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GNN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -429,7 +431,7 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
list[i].node_name[0], list[i].node_name[1],
list[i].node_name[2], list[i].node_name[3],
list[i].node_name[4], list[i].node_name[5],
@@ -457,21 +459,20 @@ qla2x00_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
int
-qla2x00_rft_id(scsi_qla_host_t *ha)
+qla2x00_rft_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_rft_id(ha));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rft_id(vha);
/* Issue RFT_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFT_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE,
RFT_ID_RSP_SIZE);
/* Prepare CT request */
@@ -480,25 +481,25 @@ qla2x00_rft_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 types */
- ct_req->req.rft_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rft_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rft_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rft_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa;
ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFT_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -511,23 +512,23 @@ qla2x00_rft_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_rff_id(scsi_qla_host_t *ha)
+qla2x00_rff_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
+ "ISP2100/ISP2200.\n", vha->host_no));
return (QLA_SUCCESS);
}
/* Issue RFF_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RFF_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE,
RFF_ID_RSP_SIZE);
/* Prepare CT request */
@@ -536,26 +537,26 @@ qla2x00_rff_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
- ct_req->req.rff_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rff_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rff_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
ct_req->req.rff_id.fc4_feature = BIT_1;
ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RFF_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -568,21 +569,20 @@ qla2x00_rff_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_rnn_id(scsi_qla_host_t *ha)
+qla2x00_rnn_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- return (qla2x00_sns_rnn_id(ha));
- }
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rnn_id(vha);
/* Issue RNN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, RNN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE,
RNN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -591,33 +591,34 @@ qla2x00_rnn_id(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_id, node_name */
- ct_req->req.rnn_id.port_id[0] = ha->d_id.b.domain;
- ct_req->req.rnn_id.port_id[1] = ha->d_id.b.area;
- ct_req->req.rnn_id.port_id[2] = ha->d_id.b.al_pa;
+ ct_req->req.rnn_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rnn_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rnn_id.port_id[2] = vha->d_id.b.al_pa;
- memcpy(ct_req->req.rnn_id.node_name, ha->node_name, WWN_SIZE);
+ memcpy(ct_req->req.rnn_id.node_name, vha->node_name, WWN_SIZE);
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RNN_ID") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
}
void
-qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn)
+qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn)
{
+ struct qla_hw_data *ha = vha->hw;
sprintf(snn, "%s FW:v%d.%02d.%02d DVR:v%s",ha->model_number,
ha->fw_major_version, ha->fw_minor_version,
ha->fw_subminor_version, qla2x00_version_str);
@@ -630,23 +631,24 @@ qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn)
* Returns 0 on success.
*/
int
-qla2x00_rsnn_nn(scsi_qla_host_t *ha)
+qla2x00_rsnn_nn(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
+ "ISP2100/ISP2200.\n", vha->host_no));
return (QLA_SUCCESS);
}
/* Issue RSNN_NN */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, 0, RSNN_NN_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
@@ -654,10 +656,10 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- node_name, symbolic node_name, size */
- memcpy(ct_req->req.rsnn_nn.node_name, ha->node_name, WWN_SIZE);
+ memcpy(ct_req->req.rsnn_nn.node_name, vha->node_name, WWN_SIZE);
/* Prepare the Symbolic Node Name */
- qla2x00_get_sym_node_name(ha, ct_req->req.rsnn_nn.sym_node_name);
+ qla2x00_get_sym_node_name(vha, ct_req->req.rsnn_nn.sym_node_name);
/* Calculate SNN length */
ct_req->req.rsnn_nn.name_len =
@@ -669,18 +671,18 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RSNN_NN") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -696,11 +698,12 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha)
* Returns a pointer to the @ha's sns_cmd.
*/
static inline struct sns_cmd_pkt *
-qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len,
+qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len,
uint16_t data_size)
{
uint16_t wc;
struct sns_cmd_pkt *sns_cmd;
+ struct qla_hw_data *ha = vha->hw;
sns_cmd = ha->sns_cmd;
memset(sns_cmd, 0, sizeof(struct sns_cmd_pkt));
@@ -726,15 +729,15 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *ha, uint16_t cmd, uint16_t scmd_len,
* Returns 0 on success.
*/
static int
-qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue GA_NXT. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
GA_NXT_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -743,16 +746,16 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
sns_cmd->p.cmd.param[2] = fcport->d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.gan_data[8] != 0x80 ||
sns_cmd->p.gan_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, "
- "ga_nxt_rsp:\n", ha->host_no));
+ "ga_nxt_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -772,7 +775,7 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
fcport->node_name[0], fcport->node_name[1],
fcport->node_name[2], fcport->node_name[3],
fcport->node_name[4], fcport->node_name[5],
@@ -800,33 +803,33 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *ha, fc_port_t *fcport)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
uint8_t *entry;
struct sns_cmd_pkt *sns_cmd;
/* Issue GID_PT. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
GID_PT_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_type. */
sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.gid_data[8] != 0x80 ||
sns_cmd->p.gid_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, "
- "gid_rsp:\n", ha->host_no));
+ "gid_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -867,17 +870,17 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GPN_ID */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GPN_ID_CMD,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GPN_ID_CMD,
GPN_ID_SNS_SCMD_LEN, GPN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -886,16 +889,16 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed "
- "(%d).\n", ha->host_no, rval));
+ "(%d).\n", vha->host_no, rval));
} else if (sns_cmd->p.gpn_data[8] != 0x80 ||
sns_cmd->p.gpn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected "
- "request, gpn_rsp:\n", ha->host_no));
+ "request, gpn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -922,17 +925,17 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GNN_ID */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, GNN_ID_CMD,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GNN_ID_CMD,
GNN_ID_SNS_SCMD_LEN, GNN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id. */
@@ -941,16 +944,16 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed "
- "(%d).\n", ha->host_no, rval));
+ "(%d).\n", vha->host_no, rval));
} else if (sns_cmd->p.gnn_data[8] != 0x80 ||
sns_cmd->p.gnn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected "
- "request, gnn_rsp:\n", ha->host_no));
+ "request, gnn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
@@ -962,7 +965,7 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
"nn %02x%02x%02x%02x%02x%02x%02x%02x "
"pn %02x%02x%02x%02x%02x%02x%02x%02x "
"portid=%02x%02x%02x.\n",
- ha->host_no,
+ vha->host_no,
list[i].node_name[0], list[i].node_name[1],
list[i].node_name[2], list[i].node_name[3],
list[i].node_name[4], list[i].node_name[5],
@@ -992,40 +995,40 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *ha, sw_info_t *list)
* Returns 0 on success.
*/
static int
-qla2x00_sns_rft_id(scsi_qla_host_t *ha)
+qla2x00_sns_rft_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue RFT_ID. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
RFT_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id, FC-4 types */
- sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
- sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
- sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
sns_cmd->p.cmd.param[5] = 0x01; /* FCP-3 */
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.rft_data[8] != 0x80 ||
sns_cmd->p.rft_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, "
- "rft_rsp:\n", ha->host_no));
+ "rft_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1041,47 +1044,47 @@ qla2x00_sns_rft_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_sns_rnn_id(scsi_qla_host_t *ha)
+qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
/* Issue RNN_ID. */
/* Prepare SNS command request. */
- sns_cmd = qla2x00_prep_sns_cmd(ha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
RNN_ID_SNS_DATA_SIZE);
/* Prepare SNS command arguments -- port_id, nodename. */
- sns_cmd->p.cmd.param[0] = ha->d_id.b.al_pa;
- sns_cmd->p.cmd.param[1] = ha->d_id.b.area;
- sns_cmd->p.cmd.param[2] = ha->d_id.b.domain;
-
- sns_cmd->p.cmd.param[4] = ha->node_name[7];
- sns_cmd->p.cmd.param[5] = ha->node_name[6];
- sns_cmd->p.cmd.param[6] = ha->node_name[5];
- sns_cmd->p.cmd.param[7] = ha->node_name[4];
- sns_cmd->p.cmd.param[8] = ha->node_name[3];
- sns_cmd->p.cmd.param[9] = ha->node_name[2];
- sns_cmd->p.cmd.param[10] = ha->node_name[1];
- sns_cmd->p.cmd.param[11] = ha->node_name[0];
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[4] = vha->node_name[7];
+ sns_cmd->p.cmd.param[5] = vha->node_name[6];
+ sns_cmd->p.cmd.param[6] = vha->node_name[5];
+ sns_cmd->p.cmd.param[7] = vha->node_name[4];
+ sns_cmd->p.cmd.param[8] = vha->node_name[3];
+ sns_cmd->p.cmd.param[9] = vha->node_name[2];
+ sns_cmd->p.cmd.param[10] = vha->node_name[1];
+ sns_cmd->p.cmd.param[11] = vha->node_name[0];
/* Execute SNS command. */
- rval = qla2x00_send_sns(ha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
sizeof(struct sns_cmd_pkt));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else if (sns_cmd->p.rnn_data[8] != 0x80 ||
sns_cmd->p.rnn_data[9] != 0x02) {
DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, "
- "rnn_rsp:\n", ha->host_no));
+ "rnn_rsp:\n", vha->host_no));
DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16));
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1094,25 +1097,25 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_mgmt_svr_login(scsi_qla_host_t *ha)
+qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
{
int ret;
uint16_t mb[MAILBOX_REGISTER_COUNT];
-
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
- if (ha->flags.management_server_logged_in)
+ if (vha->flags.management_server_logged_in)
return ret;
- ha->isp_ops->fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
+ ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
mb, BIT_1);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: "
"loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n",
- __func__, ha->host_no, ha->mgmt_svr_loop_id, mb[0], mb[1],
+ __func__, vha->host_no, vha->mgmt_svr_loop_id, mb[0], mb[1],
mb[2], mb[6], mb[7]));
ret = QLA_FUNCTION_FAILED;
} else
- ha->flags.management_server_logged_in = 1;
+ vha->flags.management_server_logged_in = 1;
return ret;
}
@@ -1126,17 +1129,17 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *ha)
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
ms_iocb_entry_t *ms_pkt;
-
+ struct qla_hw_data *ha = vha->hw;
ms_pkt = ha->ms_iocb;
memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
ms_pkt->entry_type = MS_IOCB_TYPE;
ms_pkt->entry_count = 1;
- SET_TARGET_ID(ha, ms_pkt->loop_id, ha->mgmt_svr_loop_id);
+ SET_TARGET_ID(ha, ms_pkt->loop_id, vha->mgmt_svr_loop_id);
ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
@@ -1164,17 +1167,18 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
* Returns a pointer to the @ha's ms_iocb.
*/
void *
-qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
struct ct_entry_24xx *ct_pkt;
+ struct qla_hw_data *ha = vha->hw;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id);
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
@@ -1188,14 +1192,15 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size,
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return ct_pkt;
}
static inline ms_iocb_entry_t *
-qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size)
+qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
{
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
@@ -1240,7 +1245,7 @@ qla2x00_prep_ct_fdmi_req(struct ct_sns_req *ct_req, uint16_t cmd,
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
+qla2x00_fdmi_rhba(scsi_qla_host_t *vha)
{
int rval, alen;
uint32_t size, sn;
@@ -1250,11 +1255,12 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
struct ct_sns_rsp *ct_rsp;
uint8_t *entries;
struct ct_fdmi_hba_attr *eiter;
+ struct qla_hw_data *ha = vha->hw;
/* Issue RHBA */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RHBA_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD,
@@ -1262,9 +1268,9 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
- memcpy(ct_req->req.rhba.hba_identifier, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rhba.hba_identifier, vha->port_name, WWN_SIZE);
ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1);
- memcpy(ct_req->req.rhba.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rhba.port_name, vha->port_name, WWN_SIZE);
size = 2 * WWN_SIZE + 4 + 4;
/* Attributes */
@@ -1276,11 +1282,11 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter = (struct ct_fdmi_hba_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME);
eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE);
- memcpy(eiter->a.node_name, ha->node_name, WWN_SIZE);
+ memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE);
size += 4 + WWN_SIZE;
DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
eiter->a.node_name[0], eiter->a.node_name[1], eiter->a.node_name[2],
eiter->a.node_name[3], eiter->a.node_name[4], eiter->a.node_name[5],
eiter->a.node_name[6], eiter->a.node_name[7]));
@@ -1294,7 +1300,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, vha->host_no,
eiter->a.manufacturer));
/* Serial number. */
@@ -1307,7 +1313,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, vha->host_no,
eiter->a.serial_num));
/* Model name. */
@@ -1319,7 +1325,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, vha->host_no,
eiter->a.model));
/* Model description. */
@@ -1332,7 +1338,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, vha->host_no,
eiter->a.model_desc));
/* Hardware version. */
@@ -1344,7 +1350,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, vha->host_no,
eiter->a.hw_version));
/* Driver version. */
@@ -1356,7 +1362,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, vha->host_no,
eiter->a.driver_version));
/* Option ROM version. */
@@ -1368,27 +1374,27 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, vha->host_no,
eiter->a.orom_version));
/* Firmware version */
eiter = (struct ct_fdmi_hba_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION);
- ha->isp_ops->fw_version_str(ha, eiter->a.fw_version);
+ ha->isp_ops->fw_version_str(vha, eiter->a.fw_version);
alen = strlen(eiter->a.fw_version);
alen += (alen & 3) ? (4 - (alen & 3)) : 4;
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, vha->host_no,
eiter->a.fw_version));
/* Update MS request size. */
- qla2x00_update_ms_fdmi_iocb(ha, size + 16);
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
DEBUG13(printk("%s(%ld): RHBA identifier="
"%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
- ha->host_no, ct_req->req.rhba.hba_identifier[0],
+ vha->host_no, ct_req->req.rhba.hba_identifier[0],
ct_req->req.rhba.hba_identifier[1],
ct_req->req.rhba.hba_identifier[2],
ct_req->req.rhba.hba_identifier[3],
@@ -1399,25 +1405,25 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
DEBUG13(qla2x00_dump_buffer(entries, size));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RHBA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RHBA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RHBA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM &&
ct_rsp->header.explanation_code ==
CT_EXPL_ALREADY_REGISTERED) {
DEBUG2_13(printk("%s(%ld): HBA already registered.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
rval = QLA_ALREADY_REGISTERED;
}
} else {
DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1430,17 +1436,17 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
+qla2x00_fdmi_dhba(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
/* Issue RPA */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, DHBA_REQ_SIZE,
DHBA_RSP_SIZE);
/* Prepare CT request */
@@ -1449,28 +1455,28 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- portname. */
- memcpy(ct_req->req.dhba.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE);
DEBUG13(printk("%s(%ld): DHBA portname="
- "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, ha->host_no,
+ "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, vha->host_no,
ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1],
ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3],
ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5],
ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): DHBA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "DHBA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "DHBA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1483,11 +1489,11 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
+qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
{
int rval, alen;
uint32_t size, max_frame_size;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1498,7 +1504,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
/* Issue RPA */
/* Prepare common MS IOCB */
/* Request size adjusted after CT preparation */
- ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE);
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RPA_RSP_SIZE);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD,
@@ -1506,7 +1512,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare FDMI command arguments -- attribute block, attributes. */
- memcpy(ct_req->req.rpa.port_name, ha->port_name, WWN_SIZE);
+ memcpy(ct_req->req.rpa.port_name, vha->port_name, WWN_SIZE);
size = WWN_SIZE + 4;
/* Attributes */
@@ -1521,8 +1527,9 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->a.fc4_types[2] = 0x01;
size += 4 + 32;
- DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__, ha->host_no,
- eiter->a.fc4_types[2], eiter->a.fc4_types[1]));
+ DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__,
+ vha->host_no, eiter->a.fc4_types[2],
+ eiter->a.fc4_types[1]));
/* Supported speed. */
eiter = (struct ct_fdmi_port_attr *) (entries + size);
@@ -1544,7 +1551,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
FDMI_PORT_SPEED_1GB);
size += 4 + 4;
- DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, vha->host_no,
eiter->a.sup_speed));
/* Current speed. */
@@ -1575,7 +1582,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
}
size += 4 + 4;
- DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, vha->host_no,
eiter->a.cur_speed));
/* Max frame size. */
@@ -1588,7 +1595,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->a.max_frame_size = cpu_to_be32(max_frame_size);
size += 4 + 4;
- DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, vha->host_no,
eiter->a.max_frame_size));
/* OS device name. */
@@ -1600,32 +1607,32 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
- DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, ha->host_no,
+ DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, vha->host_no,
eiter->a.os_dev_name));
/* Hostname. */
- if (strlen(fc_host_system_hostname(ha->host))) {
+ if (strlen(fc_host_system_hostname(vha->host))) {
ct_req->req.rpa.attrs.count =
__constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT);
eiter = (struct ct_fdmi_port_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_PORT_HOST_NAME);
snprintf(eiter->a.host_name, sizeof(eiter->a.host_name),
- "%s", fc_host_system_hostname(ha->host));
+ "%s", fc_host_system_hostname(vha->host));
alen = strlen(eiter->a.host_name);
alen += (alen & 3) ? (4 - (alen & 3)) : 4;
eiter->len = cpu_to_be16(4 + alen);
size += 4 + alen;
DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__,
- ha->host_no, eiter->a.host_name));
+ vha->host_no, eiter->a.host_name));
}
/* Update MS request size. */
- qla2x00_update_ms_fdmi_iocb(ha, size + 16);
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
DEBUG13(printk("%s(%ld): RPA portname="
"%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__,
- ha->host_no, ct_req->req.rpa.port_name[0],
+ vha->host_no, ct_req->req.rpa.port_name[0],
ct_req->req.rpa.port_name[1], ct_req->req.rpa.port_name[2],
ct_req->req.rpa.port_name[3], ct_req->req.rpa.port_name[4],
ct_req->req.rpa.port_name[5], ct_req->req.rpa.port_name[6],
@@ -1633,18 +1640,18 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
DEBUG13(qla2x00_dump_buffer(entries, size));
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): RPA issue IOCB failed (%d).\n",
- ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RPA") !=
+ vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RPA") !=
QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
DEBUG2(printk("scsi(%ld): RPA exiting normally.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1657,34 +1664,28 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_fdmi_register(scsi_qla_host_t *ha)
+qla2x00_fdmi_register(scsi_qla_host_t *vha)
{
int rval;
- if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- DEBUG2(printk("scsi(%ld): FDMI unsupported on "
- "ISP2100/ISP2200.\n", ha->host_no));
- return QLA_SUCCESS;
- }
-
- rval = qla2x00_mgmt_svr_login(ha);
+ rval = qla2x00_mgmt_svr_login(vha);
if (rval)
return rval;
- rval = qla2x00_fdmi_rhba(ha);
+ rval = qla2x00_fdmi_rhba(vha);
if (rval) {
if (rval != QLA_ALREADY_REGISTERED)
return rval;
- rval = qla2x00_fdmi_dhba(ha);
+ rval = qla2x00_fdmi_dhba(vha);
if (rval)
return rval;
- rval = qla2x00_fdmi_rhba(ha);
+ rval = qla2x00_fdmi_rhba(vha);
if (rval)
return rval;
}
- rval = qla2x00_fdmi_rpa(ha);
+ rval = qla2x00_fdmi_rpa(vha);
return rval;
}
@@ -1697,11 +1698,11 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1712,7 +1713,7 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = ha->isp_ops->prep_ms_iocb(ha, GFPN_ID_REQ_SIZE,
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE,
GFPN_ID_RSP_SIZE);
/* Prepare CT request */
@@ -1726,13 +1727,13 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB "
- "failed (%d).\n", ha->host_no, rval));
- } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "failed (%d).\n", vha->host_no, rval));
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GFPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
} else {
@@ -1750,17 +1751,17 @@ qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list)
}
static inline void *
-qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size,
+qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size,
uint32_t rsp_size)
{
struct ct_entry_24xx *ct_pkt;
-
+ struct qla_hw_data *ha = vha->hw;
ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
ct_pkt->entry_type = CT_IOCB_TYPE;
ct_pkt->entry_count = 1;
- ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id);
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
@@ -1774,7 +1775,7 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size,
ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
- ct_pkt->vp_index = ha->vp_idx;
+ ct_pkt->vp_index = vha->vp_idx;
return ct_pkt;
}
@@ -1803,11 +1804,11 @@ qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd,
* Returns 0 on success.
*/
int
-qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
+qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
{
int rval;
uint16_t i;
-
+ struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
struct ct_sns_req *ct_req;
struct ct_sns_rsp *ct_rsp;
@@ -1817,14 +1818,14 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
if (!ha->flags.gpsc_supported)
return QLA_FUNCTION_FAILED;
- rval = qla2x00_mgmt_svr_login(ha);
+ rval = qla2x00_mgmt_svr_login(vha);
if (rval)
return rval;
for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
- ms_pkt = qla24xx_prep_ms_fm_iocb(ha, GPSC_REQ_SIZE,
+ ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE,
GPSC_RSP_SIZE);
/* Prepare CT request */
@@ -1837,13 +1838,13 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
WWN_SIZE);
/* Execute MS IOCB */
- rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma,
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
sizeof(ms_iocb_entry_t));
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB "
- "failed (%d).\n", ha->host_no, rval));
- } else if ((rval = qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp,
+ "failed (%d).\n", vha->host_no, rval));
+ } else if ((rval = qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GPSC")) != QLA_SUCCESS) {
/* FM command unsupported? */
if (rval == QLA_INVALID_COMMAND &&
@@ -1853,7 +1854,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
CT_REASON_COMMAND_UNSUPPORTED)) {
DEBUG2(printk("scsi(%ld): GPSC command "
"unsupported, disabling query...\n",
- ha->host_no));
+ vha->host_no));
ha->flags.gpsc_supported = 0;
rval = QLA_FUNCTION_FAILED;
break;
@@ -1878,7 +1879,7 @@ qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list)
DEBUG2_3(printk("scsi(%ld): GPSC ext entry - "
"fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x "
- "speed=%04x.\n", ha->host_no,
+ "speed=%04x.\n", vha->host_no,
list[i].fabric_port_name[0],
list[i].fabric_port_name[1],
list[i].fabric_port_name[2],
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 4218f20f5ed5..52ed56ecf195 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -5,6 +5,7 @@
* See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
+#include "qla_gbl.h"
#include <linux/delay.h>
#include <linux/vmalloc.h>
@@ -21,7 +22,6 @@
static int qla2x00_isp_firmware(scsi_qla_host_t *);
static void qla2x00_resize_request_q(scsi_qla_host_t *);
static int qla2x00_setup_chip(scsi_qla_host_t *);
-static void qla2x00_init_response_q_entries(scsi_qla_host_t *);
static int qla2x00_init_rings(scsi_qla_host_t *);
static int qla2x00_fw_ready(scsi_qla_host_t *);
static int qla2x00_configure_hba(scsi_qla_host_t *);
@@ -35,10 +35,11 @@ static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
static int qla2x00_restart_isp(scsi_qla_host_t *);
-static int qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev);
+static int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
static int qla84xx_init_chip(scsi_qla_host_t *);
+static int qla25xx_init_queues(struct qla_hw_data *);
/****************************************************************************/
/* QLogic ISP2x00 Hardware Support Functions. */
@@ -55,77 +56,81 @@ static int qla84xx_init_chip(scsi_qla_host_t *);
* 0 = success
*/
int
-qla2x00_initialize_adapter(scsi_qla_host_t *ha)
+qla2x00_initialize_adapter(scsi_qla_host_t *vha)
{
int rval;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Clear adapter flags. */
- ha->flags.online = 0;
- ha->flags.reset_active = 0;
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- atomic_set(&ha->loop_state, LOOP_DOWN);
- ha->device_flags = DFLG_NO_CABLE;
- ha->dpc_flags = 0;
- ha->flags.management_server_logged_in = 0;
- ha->marker_needed = 0;
+ vha->flags.online = 0;
+ vha->flags.reset_active = 0;
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ vha->device_flags = DFLG_NO_CABLE;
+ vha->dpc_flags = 0;
+ vha->flags.management_server_logged_in = 0;
+ vha->marker_needed = 0;
ha->mbx_flags = 0;
ha->isp_abort_cnt = 0;
ha->beacon_blink_led = 0;
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+
+ set_bit(0, ha->req_qid_map);
+ set_bit(0, ha->rsp_qid_map);
qla_printk(KERN_INFO, ha, "Configuring PCI space...\n");
- rval = ha->isp_ops->pci_config(ha);
+ rval = ha->isp_ops->pci_config(vha);
if (rval) {
DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
- ha->isp_ops->reset_chip(ha);
+ ha->isp_ops->reset_chip(vha);
- rval = qla2xxx_get_flash_info(ha);
+ rval = qla2xxx_get_flash_info(vha);
if (rval) {
DEBUG2(printk("scsi(%ld): Unable to validate FLASH data.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
- ha->isp_ops->get_flash_version(ha, ha->request_ring);
+ ha->isp_ops->get_flash_version(vha, req->ring);
qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n");
- ha->isp_ops->nvram_config(ha);
+ ha->isp_ops->nvram_config(vha);
if (ha->flags.disable_serdes) {
/* Mask HBA via NVRAM settings? */
qla_printk(KERN_INFO, ha, "Masking HBA WWPN "
"%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n",
- ha->port_name[0], ha->port_name[1],
- ha->port_name[2], ha->port_name[3],
- ha->port_name[4], ha->port_name[5],
- ha->port_name[6], ha->port_name[7]);
+ vha->port_name[0], vha->port_name[1],
+ vha->port_name[2], vha->port_name[3],
+ vha->port_name[4], vha->port_name[5],
+ vha->port_name[6], vha->port_name[7]);
return QLA_FUNCTION_FAILED;
}
qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n");
- if (qla2x00_isp_firmware(ha) != QLA_SUCCESS) {
- rval = ha->isp_ops->chip_diag(ha);
+ if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) {
+ rval = ha->isp_ops->chip_diag(vha);
if (rval)
return (rval);
- rval = qla2x00_setup_chip(ha);
+ rval = qla2x00_setup_chip(vha);
if (rval)
return (rval);
}
if (IS_QLA84XX(ha)) {
- ha->cs84xx = qla84xx_get_chip(ha);
+ ha->cs84xx = qla84xx_get_chip(vha);
if (!ha->cs84xx) {
qla_printk(KERN_ERR, ha,
"Unable to configure ISP84XX.\n");
return QLA_FUNCTION_FAILED;
}
}
- rval = qla2x00_init_rings(ha);
+ rval = qla2x00_init_rings(vha);
return (rval);
}
@@ -137,10 +142,11 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2100_pci_config(scsi_qla_host_t *ha)
+qla2100_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
pci_set_master(ha->pdev);
@@ -167,11 +173,12 @@ qla2100_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2300_pci_config(scsi_qla_host_t *ha)
+qla2300_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags = 0;
uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
pci_set_master(ha->pdev);
@@ -248,10 +255,11 @@ qla2300_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla24xx_pci_config(scsi_qla_host_t *ha)
+qla24xx_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
pci_set_master(ha->pdev);
@@ -291,9 +299,10 @@ qla24xx_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla25xx_pci_config(scsi_qla_host_t *ha)
+qla25xx_pci_config(scsi_qla_host_t *vha)
{
uint16_t w;
+ struct qla_hw_data *ha = vha->hw;
pci_set_master(ha->pdev);
pci_try_set_mwi(ha->pdev);
@@ -321,32 +330,33 @@ qla25xx_pci_config(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_isp_firmware(scsi_qla_host_t *ha)
+qla2x00_isp_firmware(scsi_qla_host_t *vha)
{
int rval;
uint16_t loop_id, topo, sw_cap;
uint8_t domain, area, al_pa;
+ struct qla_hw_data *ha = vha->hw;
/* Assume loading risc code */
rval = QLA_FUNCTION_FAILED;
if (ha->flags.disable_risc_code_load) {
DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n");
/* Verify checksum of loaded RISC code. */
- rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address);
+ rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address);
if (rval == QLA_SUCCESS) {
/* And, verify we are not in ROM code. */
- rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa,
+ rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa,
&area, &domain, &topo, &sw_cap);
}
}
if (rval) {
DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -359,9 +369,10 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla2x00_reset_chip(scsi_qla_host_t *ha)
+qla2x00_reset_chip(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t cnt;
uint16_t cmd;
@@ -499,10 +510,11 @@ qla2x00_reset_chip(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static inline void
-qla24xx_reset_risc(scsi_qla_host_t *ha)
+qla24xx_reset_risc(scsi_qla_host_t *vha)
{
int hw_evt = 0;
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
uint32_t cnt, d2;
uint16_t wd;
@@ -541,7 +553,7 @@ qla24xx_reset_risc(scsi_qla_host_t *ha)
barrier();
}
if (cnt == 0 || hw_evt)
- qla2xxx_hw_event_log(ha, HW_EVENT_RESET_ERR,
+ qla2xxx_hw_event_log(vha, HW_EVENT_RESET_ERR,
RD_REG_WORD(&reg->mailbox1), RD_REG_WORD(&reg->mailbox2),
RD_REG_WORD(&reg->mailbox3));
@@ -571,12 +583,13 @@ qla24xx_reset_risc(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla24xx_reset_chip(scsi_qla_host_t *ha)
+qla24xx_reset_chip(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
ha->isp_ops->disable_intrs(ha);
/* Perform RISC reset. */
- qla24xx_reset_risc(ha);
+ qla24xx_reset_risc(vha);
}
/**
@@ -586,20 +599,22 @@ qla24xx_reset_chip(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
int
-qla2x00_chip_diag(scsi_qla_host_t *ha)
+qla2x00_chip_diag(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
unsigned long flags = 0;
uint16_t data;
uint32_t cnt;
uint16_t mb[5];
+ struct req_que *req = ha->req_q_map[0];
/* Assume a failed state */
rval = QLA_FUNCTION_FAILED;
DEBUG3(printk("scsi(%ld): Testing device at %lx.\n",
- ha->host_no, (u_long)&reg->flash_address));
+ vha->host_no, (u_long)&reg->flash_address));
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -662,17 +677,17 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
ha->product_id[3] = mb[4];
/* Adjust fw RISC transfer size */
- if (ha->request_q_length > 1024)
+ if (req->length > 1024)
ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024;
else
ha->fw_transfer_size = REQUEST_ENTRY_SIZE *
- ha->request_q_length;
+ req->length;
if (IS_QLA2200(ha) &&
RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) {
/* Limit firmware transfer size with a 2200A */
DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n",
- ha->host_no));
+ vha->host_no));
ha->device_type |= DT_ISP2200A;
ha->fw_transfer_size = 128;
@@ -681,11 +696,11 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
/* Wrap Incoming Mailboxes Test. */
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", ha->host_no));
- rval = qla2x00_mbx_reg_test(ha);
+ DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", vha->host_no));
+ rval = qla2x00_mbx_reg_test(vha);
if (rval) {
DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Failed mailbox send register test\n");
}
@@ -698,7 +713,7 @@ qla2x00_chip_diag(scsi_qla_host_t *ha)
chip_diag_failed:
if (rval)
DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED "
- "****\n", ha->host_no));
+ "****\n", vha->host_no));
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -712,19 +727,21 @@ chip_diag_failed:
* Returns 0 on success.
*/
int
-qla24xx_chip_diag(scsi_qla_host_t *ha)
+qla24xx_chip_diag(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Perform RISC reset. */
- qla24xx_reset_risc(ha);
+ qla24xx_reset_risc(vha);
- ha->fw_transfer_size = REQUEST_ENTRY_SIZE * ha->request_q_length;
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length;
- rval = qla2x00_mbx_reg_test(ha);
+ rval = qla2x00_mbx_reg_test(vha);
if (rval) {
DEBUG(printk("scsi(%ld): Failed mailbox send register test\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Failed mailbox send register test\n");
} else {
@@ -736,13 +753,16 @@ qla24xx_chip_diag(scsi_qla_host_t *ha)
}
void
-qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
+qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
{
int rval;
uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size,
- eft_size, fce_size;
+ eft_size, fce_size, mq_size;
dma_addr_t tc_dma;
void *tc;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
if (ha->fw_dump) {
qla_printk(KERN_WARNING, ha,
@@ -751,7 +771,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
}
ha->fw_dumped = 0;
- fixed_size = mem_size = eft_size = fce_size = 0;
+ fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
fixed_size = sizeof(struct qla2100_fw_dump);
} else if (IS_QLA23XX(ha)) {
@@ -760,10 +780,12 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
sizeof(uint16_t);
} else if (IS_FWI2_CAPABLE(ha)) {
fixed_size = IS_QLA25XX(ha) ?
- offsetof(struct qla25xx_fw_dump, ext_mem):
- offsetof(struct qla24xx_fw_dump, ext_mem);
+ offsetof(struct qla25xx_fw_dump, ext_mem) :
+ offsetof(struct qla24xx_fw_dump, ext_mem);
mem_size = (ha->fw_memory_size - 0x100000 + 1) *
sizeof(uint32_t);
+ if (ha->mqenable)
+ mq_size = sizeof(struct qla2xxx_mq_chain);
/* Allocate memory for Fibre Channel Event Buffer. */
if (!IS_QLA25XX(ha))
@@ -778,7 +800,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha)
}
memset(tc, 0, FCE_SIZE);
- rval = qla2x00_enable_fce_trace(ha, tc_dma, FCE_NUM_BUFFERS,
+ rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS,
ha->fce_mb, &ha->fce_bufs);
if (rval) {
qla_printk(KERN_WARNING, ha, "Unable to initialize "
@@ -807,7 +829,7 @@ try_eft:
}
memset(tc, 0, EFT_SIZE);
- rval = qla2x00_enable_eft_trace(ha, tc_dma, EFT_NUM_BUFFERS);
+ rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
if (rval) {
qla_printk(KERN_WARNING, ha, "Unable to initialize "
"EFT (%d).\n", rval);
@@ -824,12 +846,12 @@ try_eft:
ha->eft = tc;
}
cont_alloc:
- req_q_size = ha->request_q_length * sizeof(request_t);
- rsp_q_size = ha->response_q_length * sizeof(response_t);
+ req_q_size = req->length * sizeof(request_t);
+ rsp_q_size = rsp->length * sizeof(response_t);
dump_size = offsetof(struct qla2xxx_fw_dump, isp);
dump_size += fixed_size + mem_size + req_q_size + rsp_q_size +
- eft_size + fce_size;
+ mq_size + eft_size + fce_size;
ha->fw_dump = vmalloc(dump_size);
if (!ha->fw_dump) {
@@ -844,7 +866,6 @@ cont_alloc:
}
return;
}
-
qla_printk(KERN_INFO, ha, "Allocated (%d KB) for firmware dump...\n",
dump_size / 1024);
@@ -875,27 +896,29 @@ cont_alloc:
* Returns 0 on success.
*/
static void
-qla2x00_resize_request_q(scsi_qla_host_t *ha)
+qla2x00_resize_request_q(scsi_qla_host_t *vha)
{
int rval;
uint16_t fw_iocb_cnt = 0;
uint16_t request_q_length = REQUEST_ENTRY_CNT_2XXX_EXT_MEM;
dma_addr_t request_dma;
request_t *request_ring;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Valid only on recent ISPs. */
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return;
/* Retrieve IOCB counts available to the firmware. */
- rval = qla2x00_get_resource_cnts(ha, NULL, NULL, NULL, &fw_iocb_cnt,
- &ha->max_npiv_vports);
+ rval = qla2x00_get_resource_cnts(vha, NULL, NULL, NULL, &fw_iocb_cnt,
+ &ha->max_npiv_vports);
if (rval)
return;
/* No point in continuing if current settings are sufficient. */
if (fw_iocb_cnt < 1024)
return;
- if (ha->request_q_length >= request_q_length)
+ if (req->length >= request_q_length)
return;
/* Attempt to claim larger area for request queue. */
@@ -909,17 +932,17 @@ qla2x00_resize_request_q(scsi_qla_host_t *ha)
qla_printk(KERN_INFO, ha, "Extended memory detected (%d KB)...\n",
(ha->fw_memory_size + 1) / 1024);
qla_printk(KERN_INFO, ha, "Resizing request queue depth "
- "(%d -> %d)...\n", ha->request_q_length, request_q_length);
+ "(%d -> %d)...\n", req->length, request_q_length);
/* Clear old allocations. */
dma_free_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t), ha->request_ring,
- ha->request_dma);
+ (req->length + 1) * sizeof(request_t), req->ring,
+ req->dma);
/* Begin using larger queue. */
- ha->request_q_length = request_q_length;
- ha->request_ring = request_ring;
- ha->request_dma = request_dma;
+ req->length = request_q_length;
+ req->ring = request_ring;
+ req->dma = request_dma;
}
/**
@@ -929,10 +952,11 @@ qla2x00_resize_request_q(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_setup_chip(scsi_qla_host_t *ha)
+qla2x00_setup_chip(scsi_qla_host_t *vha)
{
int rval;
uint32_t srisc_address = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
unsigned long flags;
@@ -945,28 +969,27 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
}
/* Load firmware sequences */
- rval = ha->isp_ops->load_risc(ha, &srisc_address);
+ rval = ha->isp_ops->load_risc(vha, &srisc_address);
if (rval == QLA_SUCCESS) {
DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC "
- "code.\n", ha->host_no));
+ "code.\n", vha->host_no));
- rval = qla2x00_verify_checksum(ha, srisc_address);
+ rval = qla2x00_verify_checksum(vha, srisc_address);
if (rval == QLA_SUCCESS) {
/* Start firmware execution. */
DEBUG(printk("scsi(%ld): Checksum OK, start "
- "firmware.\n", ha->host_no));
+ "firmware.\n", vha->host_no));
- rval = qla2x00_execute_fw(ha, srisc_address);
+ rval = qla2x00_execute_fw(vha, srisc_address);
/* Retrieve firmware information. */
if (rval == QLA_SUCCESS && ha->fw_major_version == 0) {
- qla2x00_get_fw_version(ha,
+ qla2x00_get_fw_version(vha,
&ha->fw_major_version,
&ha->fw_minor_version,
&ha->fw_subminor_version,
&ha->fw_attributes, &ha->fw_memory_size);
ha->flags.npiv_supported = 0;
- if ((IS_QLA24XX(ha) || IS_QLA25XX(ha) ||
- IS_QLA84XX(ha)) &&
+ if (IS_QLA2XXX_MIDTYPE(ha) &&
(ha->fw_attributes & BIT_2)) {
ha->flags.npiv_supported = 1;
if ((!ha->max_npiv_vports) ||
@@ -975,15 +998,15 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
ha->max_npiv_vports =
MIN_MULTI_ID_FABRIC - 1;
}
- qla2x00_resize_request_q(ha);
+ qla2x00_resize_request_q(vha);
if (ql2xallocfwdump)
- qla2x00_alloc_fw_dump(ha);
+ qla2x00_alloc_fw_dump(vha);
}
} else {
DEBUG2(printk(KERN_INFO
"scsi(%ld): ISP Firmware failed checksum.\n",
- ha->host_no));
+ vha->host_no));
}
}
@@ -1002,7 +1025,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
if (rval) {
DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1017,14 +1040,14 @@ qla2x00_setup_chip(scsi_qla_host_t *ha)
*
* Returns 0 on success.
*/
-static void
-qla2x00_init_response_q_entries(scsi_qla_host_t *ha)
+void
+qla2x00_init_response_q_entries(struct rsp_que *rsp)
{
uint16_t cnt;
response_t *pkt;
- pkt = ha->response_ring_ptr;
- for (cnt = 0; cnt < ha->response_q_length; cnt++) {
+ pkt = rsp->ring_ptr;
+ for (cnt = 0; cnt < rsp->length; cnt++) {
pkt->signature = RESPONSE_PROCESSED;
pkt++;
}
@@ -1038,19 +1061,20 @@ qla2x00_init_response_q_entries(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
void
-qla2x00_update_fw_options(scsi_qla_host_t *ha)
+qla2x00_update_fw_options(scsi_qla_host_t *vha)
{
uint16_t swing, emphasis, tx_sens, rx_sens;
+ struct qla_hw_data *ha = vha->hw;
memset(ha->fw_options, 0, sizeof(ha->fw_options));
- qla2x00_get_fw_options(ha, ha->fw_options);
+ qla2x00_get_fw_options(vha, ha->fw_options);
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return;
/* Serial Link options. */
DEBUG3(printk("scsi(%ld): Serial link options:\n",
- ha->host_no));
+ vha->host_no));
DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options,
sizeof(ha->fw_seriallink_options)));
@@ -1108,19 +1132,20 @@ qla2x00_update_fw_options(scsi_qla_host_t *ha)
ha->fw_options[2] |= BIT_13;
/* Update firmware options. */
- qla2x00_set_fw_options(ha, ha->fw_options);
+ qla2x00_set_fw_options(vha, ha->fw_options);
}
void
-qla24xx_update_fw_options(scsi_qla_host_t *ha)
+qla24xx_update_fw_options(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
/* Update Serial Link options. */
if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0)
return;
- rval = qla2x00_set_serdes_params(ha,
+ rval = qla2x00_set_serdes_params(vha,
le16_to_cpu(ha->fw_seriallink_options24[1]),
le16_to_cpu(ha->fw_seriallink_options24[2]),
le16_to_cpu(ha->fw_seriallink_options24[3]));
@@ -1131,19 +1156,22 @@ qla24xx_update_fw_options(scsi_qla_host_t *ha)
}
void
-qla2x00_config_rings(struct scsi_qla_host *ha)
+qla2x00_config_rings(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* Setup ring parameters in initialization control block. */
ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0);
ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0);
- ha->init_cb->request_q_length = cpu_to_le16(ha->request_q_length);
- ha->init_cb->response_q_length = cpu_to_le16(ha->response_q_length);
- ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma));
- ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma));
- ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma));
- ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma));
+ ha->init_cb->request_q_length = cpu_to_le16(req->length);
+ ha->init_cb->response_q_length = cpu_to_le16(rsp->length);
+ ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), 0);
WRT_REG_WORD(ISP_REQ_Q_OUT(ha, reg), 0);
@@ -1153,27 +1181,62 @@ qla2x00_config_rings(struct scsi_qla_host *ha)
}
void
-qla24xx_config_rings(struct scsi_qla_host *ha)
+qla24xx_config_rings(struct scsi_qla_host *vha)
{
- struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, 0);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+ struct qla_msix_entry *msix;
struct init_cb_24xx *icb;
+ uint16_t rid = 0;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
- /* Setup ring parameters in initialization control block. */
+/* Setup ring parameters in initialization control block. */
icb = (struct init_cb_24xx *)ha->init_cb;
icb->request_q_outpointer = __constant_cpu_to_le16(0);
icb->response_q_inpointer = __constant_cpu_to_le16(0);
- icb->request_q_length = cpu_to_le16(ha->request_q_length);
- icb->response_q_length = cpu_to_le16(ha->response_q_length);
- icb->request_q_address[0] = cpu_to_le32(LSD(ha->request_dma));
- icb->request_q_address[1] = cpu_to_le32(MSD(ha->request_dma));
- icb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma));
- icb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma));
-
- WRT_REG_DWORD(&reg->req_q_in, 0);
- WRT_REG_DWORD(&reg->req_q_out, 0);
- WRT_REG_DWORD(&reg->rsp_q_in, 0);
- WRT_REG_DWORD(&reg->rsp_q_out, 0);
- RD_REG_DWORD(&reg->rsp_q_out);
+ icb->request_q_length = cpu_to_le16(req->length);
+ icb->response_q_length = cpu_to_le16(rsp->length);
+ icb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ icb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
+
+ if (ha->mqenable) {
+ icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
+ icb->rid = __constant_cpu_to_le16(rid);
+ if (ha->flags.msix_enabled) {
+ msix = &ha->msix_entries[1];
+ DEBUG2_17(printk(KERN_INFO
+ "Reistering vector 0x%x for base que\n", msix->entry));
+ icb->msix = cpu_to_le16(msix->entry);
+ }
+ /* Use alternate PCI bus number */
+ if (MSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_19);
+ /* Use alternate PCI devfn */
+ if (LSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_18);
+
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22);
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23);
+ ha->rsp_q_map[0]->options = icb->firmware_options_2;
+
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_out, 0);
+ } else {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
+ }
+ /* PCI posting */
+ RD_REG_DWORD(&ioreg->hccr);
}
/**
@@ -1186,11 +1249,14 @@ qla24xx_config_rings(struct scsi_qla_host *ha)
* Returns 0 on success.
*/
static int
-qla2x00_init_rings(scsi_qla_host_t *ha)
+qla2x00_init_rings(scsi_qla_host_t *vha)
{
int rval;
unsigned long flags = 0;
int cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
struct mid_init_cb_24xx *mid_init_cb =
(struct mid_init_cb_24xx *) ha->init_cb;
@@ -1198,45 +1264,45 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
/* Clear outstanding commands array. */
for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
- ha->outstanding_cmds[cnt] = NULL;
+ req->outstanding_cmds[cnt] = NULL;
- ha->current_outstanding_cmd = 0;
+ req->current_outstanding_cmd = 0;
/* Clear RSCN queue. */
- ha->rscn_in_ptr = 0;
- ha->rscn_out_ptr = 0;
+ vha->rscn_in_ptr = 0;
+ vha->rscn_out_ptr = 0;
/* Initialize firmware. */
- ha->request_ring_ptr = ha->request_ring;
- ha->req_ring_index = 0;
- ha->req_q_cnt = ha->request_q_length;
- ha->response_ring_ptr = ha->response_ring;
- ha->rsp_ring_index = 0;
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
/* Initialize response queue entries */
- qla2x00_init_response_q_entries(ha);
+ qla2x00_init_response_q_entries(rsp);
- ha->isp_ops->config_rings(ha);
+ ha->isp_ops->config_rings(vha);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Update any ISP specific firmware options before initialization. */
- ha->isp_ops->update_fw_options(ha);
+ ha->isp_ops->update_fw_options(vha);
- DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): Issue init firmware.\n", vha->host_no));
if (ha->flags.npiv_supported)
mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports);
mid_init_cb->options = __constant_cpu_to_le16(BIT_1);
- rval = qla2x00_init_firmware(ha, ha->init_cb_size);
+ rval = qla2x00_init_firmware(vha, ha->init_cb_size);
if (rval) {
DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
} else {
DEBUG3(printk("scsi(%ld): Init firmware -- success.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1249,13 +1315,14 @@ qla2x00_init_rings(scsi_qla_host_t *ha)
* Returns 0 on success.
*/
static int
-qla2x00_fw_ready(scsi_qla_host_t *ha)
+qla2x00_fw_ready(scsi_qla_host_t *vha)
{
int rval;
unsigned long wtime, mtime, cs84xx_time;
uint16_t min_wait; /* Minimum wait time if loop is down */
uint16_t wait_time; /* Wait time if loop is coming ready */
uint16_t state[3];
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
@@ -1277,29 +1344,29 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
wtime = jiffies + (wait_time * HZ);
/* Wait for ISP to finish LIP */
- if (!ha->flags.init_done)
+ if (!vha->flags.init_done)
qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n");
DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n",
- ha->host_no));
+ vha->host_no));
do {
- rval = qla2x00_get_firmware_state(ha, state);
+ rval = qla2x00_get_firmware_state(vha, state);
if (rval == QLA_SUCCESS) {
if (state[0] < FSTATE_LOSS_OF_SYNC) {
- ha->device_flags &= ~DFLG_NO_CABLE;
+ vha->device_flags &= ~DFLG_NO_CABLE;
}
if (IS_QLA84XX(ha) && state[0] != FSTATE_READY) {
DEBUG16(printk("scsi(%ld): fw_state=%x "
- "84xx=%x.\n", ha->host_no, state[0],
+ "84xx=%x.\n", vha->host_no, state[0],
state[2]));
if ((state[2] & FSTATE_LOGGED_IN) &&
(state[2] & FSTATE_WAITING_FOR_VERIFY)) {
DEBUG16(printk("scsi(%ld): Sending "
- "verify iocb.\n", ha->host_no));
+ "verify iocb.\n", vha->host_no));
cs84xx_time = jiffies;
- rval = qla84xx_init_chip(ha);
+ rval = qla84xx_init_chip(vha);
if (rval != QLA_SUCCESS)
break;
@@ -1309,13 +1376,13 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
mtime += cs84xx_time;
DEBUG16(printk("scsi(%ld): Increasing "
"wait time by %ld. New time %ld\n",
- ha->host_no, cs84xx_time, wtime));
+ vha->host_no, cs84xx_time, wtime));
}
} else if (state[0] == FSTATE_READY) {
DEBUG(printk("scsi(%ld): F/W Ready - OK \n",
- ha->host_no));
+ vha->host_no));
- qla2x00_get_retry_cnt(ha, &ha->retry_count,
+ qla2x00_get_retry_cnt(vha, &ha->retry_count,
&ha->login_timeout, &ha->r_a_tov);
rval = QLA_SUCCESS;
@@ -1324,7 +1391,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
rval = QLA_FUNCTION_FAILED;
- if (atomic_read(&ha->loop_down_timer) &&
+ if (atomic_read(&vha->loop_down_timer) &&
state[0] != FSTATE_READY) {
/* Loop down. Timeout on min_wait for states
* other than Wait for Login.
@@ -1333,7 +1400,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
qla_printk(KERN_INFO, ha,
"Cable is unplugged...\n");
- ha->device_flags |= DFLG_NO_CABLE;
+ vha->device_flags |= DFLG_NO_CABLE;
break;
}
}
@@ -1350,15 +1417,15 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
msleep(500);
DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
- ha->host_no, state[0], jiffies));
+ vha->host_no, state[0], jiffies));
} while (1);
DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n",
- ha->host_no, state[0], jiffies));
+ vha->host_no, state[0], jiffies));
if (rval) {
DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n",
- ha->host_no));
+ vha->host_no));
}
return (rval);
@@ -1378,7 +1445,7 @@ qla2x00_fw_ready(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_configure_hba(scsi_qla_host_t *ha)
+qla2x00_configure_hba(scsi_qla_host_t *vha)
{
int rval;
uint16_t loop_id;
@@ -1388,19 +1455,20 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
uint8_t area;
uint8_t domain;
char connect_type[22];
+ struct qla_hw_data *ha = vha->hw;
/* Get host addresses. */
- rval = qla2x00_get_adapter_id(ha,
+ rval = qla2x00_get_adapter_id(vha,
&loop_id, &al_pa, &area, &domain, &topo, &sw_cap);
if (rval != QLA_SUCCESS) {
- if (LOOP_TRANSITION(ha) || atomic_read(&ha->loop_down_timer) ||
+ if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) ||
(rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
DEBUG2(printk("%s(%ld) Loop is in a transition state\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
} else {
qla_printk(KERN_WARNING, ha,
"ERROR -- Unable to get host loop ID.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
return (rval);
}
@@ -1411,7 +1479,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
return (QLA_FUNCTION_FAILED);
}
- ha->loop_id = loop_id;
+ vha->loop_id = loop_id;
/* initialize */
ha->min_external_loopid = SNS_FIRST_LOOP_ID;
@@ -1421,14 +1489,14 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
switch (topo) {
case 0:
DEBUG3(printk("scsi(%ld): HBA in NL topology.\n",
- ha->host_no));
+ vha->host_no));
ha->current_topology = ISP_CFG_NL;
strcpy(connect_type, "(Loop)");
break;
case 1:
DEBUG3(printk("scsi(%ld): HBA in FL topology.\n",
- ha->host_no));
+ vha->host_no));
ha->switch_cap = sw_cap;
ha->current_topology = ISP_CFG_FL;
strcpy(connect_type, "(FL_Port)");
@@ -1436,7 +1504,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
case 2:
DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n",
- ha->host_no));
+ vha->host_no));
ha->operating_mode = P2P;
ha->current_topology = ISP_CFG_N;
strcpy(connect_type, "(N_Port-to-N_Port)");
@@ -1444,7 +1512,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
case 3:
DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n",
- ha->host_no));
+ vha->host_no));
ha->switch_cap = sw_cap;
ha->operating_mode = P2P;
ha->current_topology = ISP_CFG_F;
@@ -1454,7 +1522,7 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
default:
DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. "
"Using NL.\n",
- ha->host_no, topo));
+ vha->host_no, topo));
ha->current_topology = ISP_CFG_NL;
strcpy(connect_type, "(Loop)");
break;
@@ -1462,29 +1530,31 @@ qla2x00_configure_hba(scsi_qla_host_t *ha)
/* Save Host port and loop ID. */
/* byte order - Big Endian */
- ha->d_id.b.domain = domain;
- ha->d_id.b.area = area;
- ha->d_id.b.al_pa = al_pa;
+ vha->d_id.b.domain = domain;
+ vha->d_id.b.area = area;
+ vha->d_id.b.al_pa = al_pa;
- if (!ha->flags.init_done)
+ if (!vha->flags.init_done)
qla_printk(KERN_INFO, ha,
"Topology - %s, Host Loop address 0x%x\n",
- connect_type, ha->loop_id);
+ connect_type, vha->loop_id);
if (rval) {
- DEBUG2_3(printk("scsi(%ld): FAILED.\n", ha->host_no));
+ DEBUG2_3(printk("scsi(%ld): FAILED.\n", vha->host_no));
} else {
- DEBUG3(printk("scsi(%ld): exiting normally.\n", ha->host_no));
+ DEBUG3(printk("scsi(%ld): exiting normally.\n", vha->host_no));
}
return(rval);
}
static inline void
-qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *def)
+qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
+ char *def)
{
char *st, *en;
uint16_t index;
+ struct qla_hw_data *ha = vha->hw;
if (memcmp(model, BINZERO, len) != 0) {
strncpy(ha->model_number, model, len);
@@ -1516,16 +1586,17 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de
}
}
if (IS_FWI2_CAPABLE(ha))
- qla2xxx_get_vpd_field(ha, "\x82", ha->model_desc,
+ qla2xxx_get_vpd_field(vha, "\x82", ha->model_desc,
sizeof(ha->model_desc));
}
/* On sparc systems, obtain port and node WWN from firmware
* properties.
*/
-static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv)
+static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *vha, nvram_t *nv)
{
#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
struct pci_dev *pdev = ha->pdev;
struct device_node *dp = pci_device_to_OF_node(pdev);
const u8 *val;
@@ -1555,12 +1626,13 @@ static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, nvram_t *nv)
* 0 = success.
*/
int
-qla2x00_nvram_config(scsi_qla_host_t *ha)
+qla2x00_nvram_config(scsi_qla_host_t *vha)
{
int rval;
uint8_t chksum = 0;
uint16_t cnt;
uint8_t *dptr1, *dptr2;
+ struct qla_hw_data *ha = vha->hw;
init_cb_t *icb = ha->init_cb;
nvram_t *nv = ha->nvram;
uint8_t *ptr = ha->nvram;
@@ -1576,11 +1648,11 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
ha->nvram_base = 0x80;
/* Get NVRAM data and calculate checksum. */
- ha->isp_ops->read_nvram(ha, ptr, ha->nvram_base, ha->nvram_size);
+ ha->isp_ops->read_nvram(vha, ptr, ha->nvram_base, ha->nvram_size);
for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++)
chksum += *ptr++;
- DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no));
DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size));
/* Bad NVRAM data, set defaults parameters. */
@@ -1594,7 +1666,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
"invalid -- WWPN) defaults.\n");
if (chksum)
- qla2xxx_hw_event_log(ha, HW_EVENT_NVRAM_CHKSUM_ERR, 0,
+ qla2xxx_hw_event_log(vha, HW_EVENT_NVRAM_CHKSUM_ERR, 0,
MSW(chksum), LSW(chksum));
/*
@@ -1631,7 +1703,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
nv->port_name[3] = 224;
nv->port_name[4] = 139;
- qla2xxx_nvram_wwn_from_ofw(ha, nv);
+ qla2xxx_nvram_wwn_from_ofw(vha, nv);
nv->login_timeout = 4;
@@ -1684,7 +1756,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
strcpy(ha->model_number, "QLA2300");
}
} else {
- qla2x00_set_model_info(ha, nv->model_number,
+ qla2x00_set_model_info(vha, nv->model_number,
sizeof(nv->model_number), "QLA23xx");
}
} else if (IS_QLA2200(ha)) {
@@ -1760,8 +1832,8 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
ha->serial0 = icb->port_name[5];
ha->serial1 = icb->port_name[6];
ha->serial2 = icb->port_name[7];
- ha->node_name = icb->node_name;
- ha->port_name = icb->port_name;
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
@@ -1829,10 +1901,10 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
icb->response_accumulation_timer = 3;
icb->interrupt_delay_timer = 5;
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
} else {
/* Enable ZIO. */
- if (!ha->flags.init_done) {
+ if (!vha->flags.init_done) {
ha->zio_mode = icb->add_firmware_options[0] &
(BIT_3 | BIT_2 | BIT_1 | BIT_0);
ha->zio_timer = icb->interrupt_delay_timer ?
@@ -1840,12 +1912,12 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
}
icb->add_firmware_options[0] &=
~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
- ha->flags.process_response_queue = 0;
+ vha->flags.process_response_queue = 0;
if (ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = QLA_ZIO_MODE_6;
DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer "
- "delay (%d us).\n", ha->host_no, ha->zio_mode,
+ "delay (%d us).\n", vha->host_no, ha->zio_mode,
ha->zio_timer * 100));
qla_printk(KERN_INFO, ha,
"ZIO mode %d enabled; timer delay (%d us).\n",
@@ -1853,13 +1925,13 @@ qla2x00_nvram_config(scsi_qla_host_t *ha)
icb->add_firmware_options[0] |= (uint8_t)ha->zio_mode;
icb->interrupt_delay_timer = (uint8_t)ha->zio_timer;
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
}
}
if (rval) {
DEBUG2_3(printk(KERN_WARNING
- "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
}
return (rval);
}
@@ -1870,10 +1942,10 @@ qla2x00_rport_del(void *data)
fc_port_t *fcport = data;
struct fc_rport *rport;
- spin_lock_irq(fcport->ha->host->host_lock);
+ spin_lock_irq(fcport->vha->host->host_lock);
rport = fcport->drport;
fcport->drport = NULL;
- spin_unlock_irq(fcport->ha->host->host_lock);
+ spin_unlock_irq(fcport->vha->host->host_lock);
if (rport)
fc_remote_port_delete(rport);
}
@@ -1886,7 +1958,7 @@ qla2x00_rport_del(void *data)
* Returns a pointer to the allocated fcport, or NULL, if none available.
*/
static fc_port_t *
-qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
+qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
{
fc_port_t *fcport;
@@ -1895,8 +1967,8 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
return NULL;
/* Setup fcport template structure. */
- fcport->ha = ha;
- fcport->vp_idx = ha->vp_idx;
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
fcport->port_type = FCT_UNKNOWN;
fcport->loop_id = FC_NO_LOOP_ID;
atomic_set(&fcport->state, FCS_UNCONFIGURED);
@@ -1919,101 +1991,97 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags)
* 2 = database was full and device was not configured.
*/
static int
-qla2x00_configure_loop(scsi_qla_host_t *ha)
+qla2x00_configure_loop(scsi_qla_host_t *vha)
{
int rval;
unsigned long flags, save_flags;
-
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
/* Get Initiator ID */
- if (test_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags)) {
- rval = qla2x00_configure_hba(ha);
+ if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) {
+ rval = qla2x00_configure_hba(vha);
if (rval != QLA_SUCCESS) {
DEBUG(printk("scsi(%ld): Unable to configure HBA.\n",
- ha->host_no));
+ vha->host_no));
return (rval);
}
}
- save_flags = flags = ha->dpc_flags;
+ save_flags = flags = vha->dpc_flags;
DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n",
- ha->host_no, flags));
+ vha->host_no, flags));
/*
* If we have both an RSCN and PORT UPDATE pending then handle them
* both at the same time.
*/
- clear_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- clear_bit(RSCN_UPDATE, &ha->dpc_flags);
+ clear_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ clear_bit(RSCN_UPDATE, &vha->dpc_flags);
/* Determine what we need to do */
if (ha->current_topology == ISP_CFG_FL &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
} else if (ha->current_topology == ISP_CFG_F &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
clear_bit(LOCAL_LOOP_UPDATE, &flags);
} else if (ha->current_topology == ISP_CFG_N) {
clear_bit(RSCN_UPDATE, &flags);
- } else if (!ha->flags.online ||
+ } else if (!vha->flags.online ||
(test_bit(ABORT_ISP_ACTIVE, &flags))) {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
set_bit(LOCAL_LOOP_UPDATE, &flags);
}
if (test_bit(LOCAL_LOOP_UPDATE, &flags)) {
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
rval = QLA_FUNCTION_FAILED;
- } else {
- rval = qla2x00_configure_local_loop(ha);
- }
+ else
+ rval = qla2x00_configure_local_loop(vha);
}
if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) {
- if (LOOP_TRANSITION(ha)) {
+ if (LOOP_TRANSITION(vha))
rval = QLA_FUNCTION_FAILED;
- } else {
- rval = qla2x00_configure_fabric(ha);
- }
+ else
+ rval = qla2x00_configure_fabric(vha);
}
if (rval == QLA_SUCCESS) {
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
rval = QLA_FUNCTION_FAILED;
} else {
- atomic_set(&ha->loop_state, LOOP_READY);
+ atomic_set(&vha->loop_state, LOOP_READY);
- DEBUG(printk("scsi(%ld): LOOP READY\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): LOOP READY\n", vha->host_no));
}
}
if (rval) {
DEBUG2_3(printk("%s(%ld): *** FAILED ***\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
} else {
DEBUG3(printk("%s: exiting normally\n", __func__));
}
/* Restore state if a resync event occurred during processing */
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- if (test_bit(RSCN_UPDATE, &save_flags)) {
- ha->flags.rscn_queue_overflow = 1;
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- }
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ if (test_bit(RSCN_UPDATE, &save_flags))
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
}
return (rval);
@@ -2032,7 +2100,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha)
* 0 = success.
*/
static int
-qla2x00_configure_local_loop(scsi_qla_host_t *ha)
+qla2x00_configure_local_loop(scsi_qla_host_t *vha)
{
int rval, rval2;
int found_devs;
@@ -2044,18 +2112,18 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
char *id_iter;
uint16_t loop_id;
uint8_t domain, area, al_pa;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
found_devs = 0;
new_fcport = NULL;
entries = MAX_FIBRE_DEVICES;
- DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", ha->host_no));
- DEBUG3(qla2x00_get_fcal_position_map(ha, NULL));
+ DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", vha->host_no));
+ DEBUG3(qla2x00_get_fcal_position_map(vha, NULL));
/* Get list of logged in devices. */
memset(ha->gid_list, 0, GID_LIST_SIZE);
- rval = qla2x00_get_id_list(ha, ha->gid_list, ha->gid_list_dma,
+ rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma,
&entries);
if (rval != QLA_SUCCESS)
goto cleanup_allocation;
@@ -2066,7 +2134,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
entries * sizeof(struct gid_list_info)));
/* Allocate temporary fcport for any new fcports discovered. */
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
rval = QLA_MEMORY_ALLOC_FAILED;
goto cleanup_allocation;
@@ -2076,17 +2144,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/*
* Mark local devices that were present with FCF_DEVICE_LOST for now.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (atomic_read(&fcport->state) == FCS_ONLINE &&
fcport->port_type != FCT_BROADCAST &&
(fcport->flags & FCF_FABRIC_DEVICE) == 0) {
DEBUG(printk("scsi(%ld): Marking port lost, "
"loop_id=0x%04x\n",
- ha->host_no, fcport->loop_id));
+ vha->host_no, fcport->loop_id));
atomic_set(&fcport->state, FCS_DEVICE_LOST);
fcport->flags &= ~FCF_FARP_DONE;
@@ -2113,7 +2178,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/* Bypass if not same domain and area of adapter. */
if (area && domain &&
- (area != ha->d_id.b.area || domain != ha->d_id.b.domain))
+ (area != vha->d_id.b.area || domain != vha->d_id.b.domain))
continue;
/* Bypass invalid local loop ID. */
@@ -2125,26 +2190,23 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
new_fcport->d_id.b.area = area;
new_fcport->d_id.b.al_pa = al_pa;
new_fcport->loop_id = loop_id;
- new_fcport->vp_idx = ha->vp_idx;
- rval2 = qla2x00_get_port_database(ha, new_fcport, 0);
+ new_fcport->vp_idx = vha->vp_idx;
+ rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
if (rval2 != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Failed to retrieve fcport "
"information -- get_port_database=%x, "
"loop_id=0x%04x\n",
- ha->host_no, rval2, new_fcport->loop_id));
+ vha->host_no, rval2, new_fcport->loop_id));
DEBUG2(printk("scsi(%ld): Scheduling resync...\n",
- ha->host_no));
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ vha->host_no));
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
continue;
}
/* Check for matching device in port list. */
found = 0;
fcport = NULL;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (memcmp(new_fcport->port_name, fcport->port_name,
WWN_SIZE))
continue;
@@ -2164,17 +2226,15 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
if (!found) {
/* New device, add to fcports list. */
new_fcport->flags &= ~FCF_PERSISTENT_BOUND;
- if (ha->parent) {
- new_fcport->ha = ha;
- new_fcport->vp_idx = ha->vp_idx;
- list_add_tail(&new_fcport->vp_fcport,
- &ha->vp_fcports);
+ if (vha->vp_idx) {
+ new_fcport->vha = vha;
+ new_fcport->vp_idx = vha->vp_idx;
}
- list_add_tail(&new_fcport->list, &pha->fcports);
+ list_add_tail(&new_fcport->list, &vha->vp_fcports);
/* Allocate a new replacement fcport. */
fcport = new_fcport;
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
rval = QLA_MEMORY_ALLOC_FAILED;
goto cleanup_allocation;
@@ -2185,7 +2245,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha)
/* Base iIDMA settings on HBA port speed. */
fcport->fp_speed = ha->link_data_rate;
- qla2x00_update_fcport(ha, fcport);
+ qla2x00_update_fcport(vha, fcport);
found_devs++;
}
@@ -2195,24 +2255,25 @@ cleanup_allocation:
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Configure local loop error exit: "
- "rval=%x\n", ha->host_no, rval));
+ "rval=%x\n", vha->host_no, rval));
}
if (found_devs) {
- ha->device_flags |= DFLG_LOCAL_DEVICES;
- ha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES;
+ vha->device_flags |= DFLG_LOCAL_DEVICES;
+ vha->device_flags &= ~DFLG_RETRY_LOCAL_DEVICES;
}
return (rval);
}
static void
-qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
#define LS_UNKNOWN 2
static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
int rval;
uint16_t mb[6];
+ struct qla_hw_data *ha = vha->hw;
if (!IS_IIDMA_CAPABLE(ha))
return;
@@ -2221,12 +2282,12 @@ qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
fcport->fp_speed > ha->link_data_rate)
return;
- rval = qla2x00_set_idma_speed(ha, fcport->loop_id, fcport->fp_speed,
+ rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed,
mb);
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA "
"%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n",
- ha->host_no, fcport->port_name[0], fcport->port_name[1],
+ vha->host_no, fcport->port_name[0], fcport->port_name[1],
fcport->port_name[2], fcport->port_name[3],
fcport->port_name[4], fcport->port_name[5],
fcport->port_name[6], fcport->port_name[7], rval,
@@ -2244,10 +2305,11 @@ qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
}
static void
-qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
{
struct fc_rport_identifiers rport_ids;
struct fc_rport *rport;
+ struct qla_hw_data *ha = vha->hw;
if (fcport->drport)
qla2x00_rport_del(fcport);
@@ -2257,15 +2319,15 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
rport_ids.port_id = fcport->d_id.b.domain << 16 |
fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
- fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids);
+ fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids);
if (!rport) {
qla_printk(KERN_WARNING, ha,
"Unable to allocate fc remote port!\n");
return;
}
- spin_lock_irq(fcport->ha->host->host_lock);
+ spin_lock_irq(fcport->vha->host->host_lock);
*((fc_port_t **)rport->dd_data) = fcport;
- spin_unlock_irq(fcport->ha->host->host_lock);
+ spin_unlock_irq(fcport->vha->host->host_lock);
rport->supported_classes = fcport->supported_classes;
@@ -2293,23 +2355,23 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport)
* Kernel context.
*/
void
-qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
{
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- fcport->ha = ha;
+ fcport->vha = vha;
fcport->login_retry = 0;
- fcport->port_login_retry_count = pha->port_down_retry_count *
+ fcport->port_login_retry_count = ha->port_down_retry_count *
PORT_RETRY_TIME;
- atomic_set(&fcport->port_down_timer, pha->port_down_retry_count *
+ atomic_set(&fcport->port_down_timer, ha->port_down_retry_count *
PORT_RETRY_TIME);
fcport->flags &= ~FCF_LOGIN_NEEDED;
- qla2x00_iidma_fcport(ha, fcport);
+ qla2x00_iidma_fcport(vha, fcport);
atomic_set(&fcport->state, FCS_ONLINE);
- qla2x00_reg_remote_port(ha, fcport);
+ qla2x00_reg_remote_port(vha, fcport);
}
/*
@@ -2324,7 +2386,7 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport)
* BIT_0 = error
*/
static int
-qla2x00_configure_fabric(scsi_qla_host_t *ha)
+qla2x00_configure_fabric(scsi_qla_host_t *vha)
{
int rval, rval2;
fc_port_t *fcport, *fcptemp;
@@ -2332,25 +2394,26 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
uint16_t mb[MAILBOX_REGISTER_COUNT];
uint16_t loop_id;
LIST_HEAD(new_fcports);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
/* If FL port exists, then SNS is present */
if (IS_FWI2_CAPABLE(ha))
loop_id = NPH_F_PORT;
else
loop_id = SNS_FL_PORT;
- rval = qla2x00_get_port_name(ha, loop_id, ha->fabric_node_name, 1);
+ rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1);
if (rval != QLA_SUCCESS) {
DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL "
- "Port\n", ha->host_no));
+ "Port\n", vha->host_no));
- ha->device_flags &= ~SWITCH_FOUND;
+ vha->device_flags &= ~SWITCH_FOUND;
return (QLA_SUCCESS);
}
- ha->device_flags |= SWITCH_FOUND;
+ vha->device_flags |= SWITCH_FOUND;
/* Mark devices that need re-synchronization. */
- rval2 = qla2x00_device_resync(ha);
+ rval2 = qla2x00_device_resync(vha);
if (rval2 == QLA_RSCNS_HANDLED) {
/* No point doing the scan, just continue. */
return (QLA_SUCCESS);
@@ -2358,15 +2421,15 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
do {
/* FDMI support. */
if (ql2xfdmienable &&
- test_and_clear_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags))
- qla2x00_fdmi_register(ha);
+ test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags))
+ qla2x00_fdmi_register(vha);
/* Ensure we are logged into the SNS. */
if (IS_FWI2_CAPABLE(ha))
loop_id = NPH_SNS;
else
loop_id = SIMPLE_NAME_SERVER;
- ha->isp_ops->fabric_login(ha, loop_id, 0xff, 0xff,
+ ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
0xfc, mb, BIT_1 | BIT_0);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2(qla_printk(KERN_INFO, ha,
@@ -2376,29 +2439,29 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
return (QLA_SUCCESS);
}
- if (test_and_clear_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags)) {
- if (qla2x00_rft_id(ha)) {
+ if (test_and_clear_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags)) {
+ if (qla2x00_rft_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register FC-4 "
- "TYPE failed.\n", ha->host_no));
+ "TYPE failed.\n", vha->host_no));
}
- if (qla2x00_rff_id(ha)) {
+ if (qla2x00_rff_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register FC-4 "
- "Features failed.\n", ha->host_no));
+ "Features failed.\n", vha->host_no));
}
- if (qla2x00_rnn_id(ha)) {
+ if (qla2x00_rnn_id(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register Node Name "
- "failed.\n", ha->host_no));
- } else if (qla2x00_rsnn_nn(ha)) {
+ "failed.\n", vha->host_no));
+ } else if (qla2x00_rsnn_nn(vha)) {
/* EMPTY */
DEBUG2(printk("scsi(%ld): Register Symbolic "
- "Node Name failed.\n", ha->host_no));
+ "Node Name failed.\n", vha->host_no));
}
}
- rval = qla2x00_find_all_fabric_devs(ha, &new_fcports);
+ rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
if (rval != QLA_SUCCESS)
break;
@@ -2406,24 +2469,21 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Logout all previous fabric devices marked lost, except
* tape devices.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx !=ha->vp_idx)
- continue;
-
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue;
if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
- qla2x00_mark_device_lost(ha, fcport,
+ qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_TAPE_PRESENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
- ha->isp_ops->fabric_logout(ha,
+ ha->isp_ops->fabric_logout(vha,
fcport->loop_id,
fcport->d_id.b.domain,
fcport->d_id.b.area,
@@ -2434,18 +2494,15 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
}
/* Starting free loop ID. */
- next_loopid = pha->min_external_loopid;
+ next_loopid = ha->min_external_loopid;
/*
* Scan through our port list and login entries that need to be
* logged in.
*/
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
@@ -2455,14 +2512,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
if (fcport->loop_id == FC_NO_LOOP_ID) {
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(
- to_qla_parent(ha), fcport);
+ base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
}
/* Login and update database */
- qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
}
/* Exit if out of loop IDs. */
@@ -2474,31 +2531,26 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Login and add the new devices to our port list.
*/
list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
- if (atomic_read(&ha->loop_down_timer) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
/* Find a new loop ID to use. */
fcport->loop_id = next_loopid;
- rval = qla2x00_find_new_loop_id(to_qla_parent(ha),
- fcport);
+ rval = qla2x00_find_new_loop_id(base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
/* Login and update database */
- qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
-
- if (ha->parent) {
- fcport->ha = ha;
- fcport->vp_idx = ha->vp_idx;
- list_add_tail(&fcport->vp_fcport,
- &ha->vp_fcports);
- list_move_tail(&fcport->list,
- &ha->parent->fcports);
- } else
- list_move_tail(&fcport->list, &ha->fcports);
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
+
+ if (vha->vp_idx) {
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ }
+ list_move_tail(&fcport->list, &vha->vp_fcports);
}
} while (0);
@@ -2510,7 +2562,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
if (rval) {
DEBUG2(printk("scsi(%ld): Configure fabric error exit: "
- "rval=%d\n", ha->host_no, rval));
+ "rval=%d\n", vha->host_no, rval));
}
return (rval);
@@ -2531,7 +2583,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
+qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
+ struct list_head *new_fcports)
{
int rval;
uint16_t loop_id;
@@ -2542,11 +2595,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
int swl_idx;
int first_dev, last_dev;
port_id_t wrap, nxt_d_id;
- int vp_index;
- int empty_vp_index;
- int found_vp;
- scsi_qla_host_t *vha;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev);
rval = QLA_SUCCESS;
@@ -2555,43 +2605,42 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
if (!swl) {
/*EMPTY*/
DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback "
- "on GA_NXT\n", ha->host_no));
+ "on GA_NXT\n", vha->host_no));
} else {
- if (qla2x00_gid_pt(ha, swl) != QLA_SUCCESS) {
+ if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
- } else if (qla2x00_gpn_id(ha, swl) != QLA_SUCCESS) {
+ } else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
- } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) {
+ } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) {
kfree(swl);
swl = NULL;
} else if (ql2xiidmaenable &&
- qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) {
- qla2x00_gpsc(ha, swl);
+ qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) {
+ qla2x00_gpsc(vha, swl);
}
}
swl_idx = 0;
/* Allocate temporary fcport for any new fcports discovered. */
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
- new_fcport->vp_idx = ha->vp_idx;
/* Set start port ID scan at adapter ID. */
first_dev = 1;
last_dev = 0;
/* Starting free loop ID. */
- loop_id = pha->min_external_loopid;
- for (; loop_id <= ha->last_loop_id; loop_id++) {
- if (qla2x00_is_reserved_id(ha, loop_id))
+ loop_id = ha->min_external_loopid;
+ for (; loop_id <= ha->max_loop_id; loop_id++) {
+ if (qla2x00_is_reserved_id(vha, loop_id))
continue;
- if (atomic_read(&ha->loop_down_timer) || LOOP_TRANSITION(ha))
+ if (atomic_read(&vha->loop_down_timer) || LOOP_TRANSITION(vha))
break;
if (swl != NULL) {
@@ -2614,7 +2663,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
}
} else {
/* Send GA_NXT to the switch */
- rval = qla2x00_ga_nxt(ha, new_fcport);
+ rval = qla2x00_ga_nxt(vha, new_fcport);
if (rval != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"SNS scan failed -- assuming zero-entry "
@@ -2635,44 +2684,31 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
first_dev = 0;
} else if (new_fcport->d_id.b24 == wrap.b24) {
DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n",
- ha->host_no, new_fcport->d_id.b.domain,
+ vha->host_no, new_fcport->d_id.b.domain,
new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa));
break;
}
/* Bypass if same physical adapter. */
- if (new_fcport->d_id.b24 == pha->d_id.b24)
+ if (new_fcport->d_id.b24 == base_vha->d_id.b24)
continue;
/* Bypass virtual ports of the same host. */
- if (pha->num_vhosts) {
- for_each_mapped_vp_idx(pha, vp_index) {
- empty_vp_index = 1;
- found_vp = 0;
- list_for_each_entry(vha, &pha->vp_list,
- vp_list) {
- if (vp_index == vha->vp_idx) {
- empty_vp_index = 0;
- found_vp = 1;
- break;
- }
- }
-
- if (empty_vp_index)
- continue;
-
- if (found_vp &&
- new_fcport->d_id.b24 == vha->d_id.b24)
+ found = 0;
+ if (ha->num_vhosts) {
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (new_fcport->d_id.b24 == vp->d_id.b24) {
+ found = 1;
break;
+ }
}
-
- if (vp_index <= pha->max_npiv_vports)
+ if (found)
continue;
}
/* Bypass if same domain and area of adapter. */
if (((new_fcport->d_id.b24 & 0xffff00) ==
- (ha->d_id.b24 & 0xffff00)) && ha->current_topology ==
+ (vha->d_id.b24 & 0xffff00)) && ha->current_topology ==
ISP_CFG_FL)
continue;
@@ -2682,9 +2718,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
/* Locate matching device in database. */
found = 0;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (new_fcport->vp_idx != fcport->vp_idx)
- continue;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (memcmp(new_fcport->port_name, fcport->port_name,
WWN_SIZE))
continue;
@@ -2728,7 +2762,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
(fcport->flags & FCF_TAPE_PRESENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
@@ -2739,27 +2773,25 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
if (found)
continue;
-
/* If device was not in our fcports list, then add it. */
list_add_tail(&new_fcport->list, new_fcports);
/* Allocate a new replacement fcport. */
nxt_d_id.b24 = new_fcport->d_id.b24;
- new_fcport = qla2x00_alloc_fcport(ha, GFP_KERNEL);
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
if (new_fcport == NULL) {
kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
new_fcport->d_id.b24 = nxt_d_id.b24;
- new_fcport->vp_idx = ha->vp_idx;
}
kfree(swl);
kfree(new_fcport);
if (!list_empty(new_fcports))
- ha->device_flags |= DFLG_FABRIC_DEVICES;
+ vha->device_flags |= DFLG_FABRIC_DEVICES;
return (rval);
}
@@ -2779,13 +2811,14 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports)
* Kernel context.
*/
static int
-qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
+qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
{
int rval;
int found;
fc_port_t *fcport;
uint16_t first_loop_id;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
rval = QLA_SUCCESS;
@@ -2794,17 +2827,15 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
for (;;) {
/* Skip loop ID if already used by adapter. */
- if (dev->loop_id == ha->loop_id) {
+ if (dev->loop_id == vha->loop_id)
dev->loop_id++;
- }
/* Skip reserved loop IDs. */
- while (qla2x00_is_reserved_id(ha, dev->loop_id)) {
+ while (qla2x00_is_reserved_id(vha, dev->loop_id))
dev->loop_id++;
- }
/* Reset loop ID if passed the end. */
- if (dev->loop_id > ha->last_loop_id) {
+ if (dev->loop_id > ha->max_loop_id) {
/* first loop ID. */
dev->loop_id = ha->min_external_loopid;
}
@@ -2812,12 +2843,17 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
/* Check for loop ID being already in use. */
found = 0;
fcport = NULL;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->loop_id == dev->loop_id && fcport != dev) {
- /* ID possibly in use */
- found++;
- break;
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ list_for_each_entry(fcport, &vp->vp_fcports, list) {
+ if (fcport->loop_id == dev->loop_id &&
+ fcport != dev) {
+ /* ID possibly in use */
+ found++;
+ break;
+ }
}
+ if (found)
+ break;
}
/* If not in use then it is free to use. */
@@ -2850,7 +2886,7 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *ha, fc_port_t *dev)
* Kernel context.
*/
static int
-qla2x00_device_resync(scsi_qla_host_t *ha)
+qla2x00_device_resync(scsi_qla_host_t *vha)
{
int rval;
uint32_t mask;
@@ -2859,14 +2895,13 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
uint8_t rscn_out_iter;
uint8_t format;
port_id_t d_id;
- scsi_qla_host_t *pha = to_qla_parent(ha);
rval = QLA_RSCNS_HANDLED;
- while (ha->rscn_out_ptr != ha->rscn_in_ptr ||
- ha->flags.rscn_queue_overflow) {
+ while (vha->rscn_out_ptr != vha->rscn_in_ptr ||
+ vha->flags.rscn_queue_overflow) {
- rscn_entry = ha->rscn_queue[ha->rscn_out_ptr];
+ rscn_entry = vha->rscn_queue[vha->rscn_out_ptr];
format = MSB(MSW(rscn_entry));
d_id.b.domain = LSB(MSW(rscn_entry));
d_id.b.area = MSB(LSW(rscn_entry));
@@ -2874,37 +2909,37 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = "
"[%02x/%02x%02x%02x].\n",
- ha->host_no, ha->rscn_out_ptr, format, d_id.b.domain,
+ vha->host_no, vha->rscn_out_ptr, format, d_id.b.domain,
d_id.b.area, d_id.b.al_pa));
- ha->rscn_out_ptr++;
- if (ha->rscn_out_ptr == MAX_RSCN_COUNT)
- ha->rscn_out_ptr = 0;
+ vha->rscn_out_ptr++;
+ if (vha->rscn_out_ptr == MAX_RSCN_COUNT)
+ vha->rscn_out_ptr = 0;
/* Skip duplicate entries. */
- for (rscn_out_iter = ha->rscn_out_ptr;
- !ha->flags.rscn_queue_overflow &&
- rscn_out_iter != ha->rscn_in_ptr;
+ for (rscn_out_iter = vha->rscn_out_ptr;
+ !vha->flags.rscn_queue_overflow &&
+ rscn_out_iter != vha->rscn_in_ptr;
rscn_out_iter = (rscn_out_iter ==
(MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) {
- if (rscn_entry != ha->rscn_queue[rscn_out_iter])
+ if (rscn_entry != vha->rscn_queue[rscn_out_iter])
break;
DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue "
- "entry found at [%d].\n", ha->host_no,
+ "entry found at [%d].\n", vha->host_no,
rscn_out_iter));
- ha->rscn_out_ptr = rscn_out_iter;
+ vha->rscn_out_ptr = rscn_out_iter;
}
/* Queue overflow, set switch default case. */
- if (ha->flags.rscn_queue_overflow) {
+ if (vha->flags.rscn_queue_overflow) {
DEBUG(printk("scsi(%ld): device_resync: rscn "
- "overflow.\n", ha->host_no));
+ "overflow.\n", vha->host_no));
format = 3;
- ha->flags.rscn_queue_overflow = 0;
+ vha->flags.rscn_queue_overflow = 0;
}
switch (format) {
@@ -2920,16 +2955,13 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
default:
mask = 0x0;
d_id.b24 = 0;
- ha->rscn_out_ptr = ha->rscn_in_ptr;
+ vha->rscn_out_ptr = vha->rscn_in_ptr;
break;
}
rval = QLA_SUCCESS;
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != ha->vp_idx)
- continue;
-
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
(fcport->d_id.b24 & mask) != d_id.b24 ||
fcport->port_type == FCT_BROADCAST)
@@ -2938,7 +2970,7 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
if (atomic_read(&fcport->state) == FCS_ONLINE) {
if (format != 3 ||
fcport->port_type != FCT_INITIATOR) {
- qla2x00_mark_device_lost(ha, fcport,
+ qla2x00_mark_device_lost(vha, fcport,
0, 0);
}
}
@@ -2965,30 +2997,31 @@ qla2x00_device_resync(scsi_qla_host_t *ha)
* Kernel context.
*/
static int
-qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *next_loopid)
{
int rval;
int retry;
uint8_t opts;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
retry = 0;
- rval = qla2x00_fabric_login(ha, fcport, next_loopid);
+ rval = qla2x00_fabric_login(vha, fcport, next_loopid);
if (rval == QLA_SUCCESS) {
/* Send an ADISC to tape devices.*/
opts = 0;
if (fcport->flags & FCF_TAPE_PRESENT)
opts |= BIT_1;
- rval = qla2x00_get_port_database(ha, fcport, opts);
+ rval = qla2x00_get_port_database(vha, fcport, opts);
if (rval != QLA_SUCCESS) {
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
- qla2x00_mark_device_lost(ha, fcport, 1, 0);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
} else {
- qla2x00_update_fcport(ha, fcport);
+ qla2x00_update_fcport(vha, fcport);
}
}
@@ -3010,13 +3043,14 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* 3 - Fatal error
*/
int
-qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *next_loopid)
{
int rval;
int retry;
uint16_t tmp_loopid;
uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
retry = 0;
tmp_loopid = 0;
@@ -3024,11 +3058,11 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
for (;;) {
DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x "
"for port %02x%02x%02x.\n",
- ha->host_no, fcport->loop_id, fcport->d_id.b.domain,
+ vha->host_no, fcport->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa));
/* Login fcport on switch. */
- ha->isp_ops->fabric_login(ha, fcport->loop_id,
+ ha->isp_ops->fabric_login(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, mb, BIT_0);
if (mb[0] == MBS_PORT_ID_USED) {
@@ -3084,7 +3118,7 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* Loop ID already used, try next loop ID.
*/
fcport->loop_id++;
- rval = qla2x00_find_new_loop_id(ha, fcport);
+ rval = qla2x00_find_new_loop_id(vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of loop IDs to use */
break;
@@ -3096,10 +3130,10 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* dead.
*/
*next_loopid = fcport->loop_id;
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
- qla2x00_mark_device_lost(ha, fcport, 1, 0);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
rval = 1;
break;
@@ -3109,12 +3143,12 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
*/
DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x "
"loop_id=%x jiffies=%lx.\n",
- __func__, ha->host_no, mb[0],
+ __func__, vha->host_no, mb[0],
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, fcport->loop_id, jiffies));
*next_loopid = fcport->loop_id;
- ha->isp_ops->fabric_logout(ha, fcport->loop_id,
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
fcport->loop_id = FC_NO_LOOP_ID;
@@ -3142,13 +3176,13 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport,
* 3 - Fatal error
*/
int
-qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport)
+qla2x00_local_device_login(scsi_qla_host_t *vha, fc_port_t *fcport)
{
int rval;
uint16_t mb[MAILBOX_REGISTER_COUNT];
memset(mb, 0, sizeof(mb));
- rval = qla2x00_login_local_device(ha, fcport, mb, BIT_0);
+ rval = qla2x00_login_local_device(vha, fcport, mb, BIT_0);
if (rval == QLA_SUCCESS) {
/* Interrogate mailbox registers for any errors */
if (mb[0] == MBS_COMMAND_ERROR)
@@ -3172,57 +3206,57 @@ qla2x00_local_device_login(scsi_qla_host_t *ha, fc_port_t *fcport)
* 0 = success
*/
int
-qla2x00_loop_resync(scsi_qla_host_t *ha)
+qla2x00_loop_resync(scsi_qla_host_t *vha)
{
- int rval;
+ int rval = QLA_SUCCESS;
uint32_t wait_time;
-
- rval = QLA_SUCCESS;
-
- atomic_set(&ha->loop_state, LOOP_UPDATE);
- clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
- if (ha->flags.online) {
- if (!(rval = qla2x00_fw_ready(ha))) {
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (vha->flags.online) {
+ if (!(rval = qla2x00_fw_ready(vha))) {
/* Wait at most MAX_TARGET RSCNs for a stable link. */
wait_time = 256;
do {
- atomic_set(&ha->loop_state, LOOP_UPDATE);
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
- ha->marker_needed = 0;
+ qla2x00_marker(vha, req, rsp, 0, 0,
+ MK_SYNC_ALL);
+ vha->marker_needed = 0;
/* Remap devices on Loop. */
- clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
- qla2x00_configure_loop(ha);
+ qla2x00_configure_loop(vha);
wait_time--;
- } while (!atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
- wait_time &&
- (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
}
}
- if (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+ if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
return (QLA_FUNCTION_FAILED);
- }
- if (rval) {
+ if (rval)
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
- }
return (rval);
}
void
-qla2x00_update_fcports(scsi_qla_host_t *ha)
+qla2x00_update_fcports(scsi_qla_host_t *vha)
{
fc_port_t *fcport;
/* Go with deferred removal of rport references. */
- list_for_each_entry(fcport, &ha->fcports, list)
- if (fcport->drport &&
+ list_for_each_entry(fcport, &vha->vp_fcports, list)
+ if (fcport && fcport->drport &&
atomic_read(&fcport->state) != FCS_UNCONFIGURED)
qla2x00_rport_del(fcport);
}
@@ -3238,63 +3272,65 @@ qla2x00_update_fcports(scsi_qla_host_t *ha)
* 0 = success
*/
int
-qla2x00_abort_isp(scsi_qla_host_t *ha)
+qla2x00_abort_isp(scsi_qla_host_t *vha)
{
int rval;
uint8_t status = 0;
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+ struct req_que *req = ha->req_q_map[0];
- if (ha->flags.online) {
- ha->flags.online = 0;
- clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ if (vha->flags.online) {
+ vha->flags.online = 0;
+ clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
ha->qla_stats.total_isp_aborts++;
qla_printk(KERN_INFO, ha,
"Performing ISP error recovery - ha= %p.\n", ha);
- ha->isp_ops->reset_chip(ha);
-
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- qla2x00_mark_all_devices_lost(ha, 0);
- list_for_each_entry(vha, &ha->vp_list, vp_list)
- qla2x00_mark_all_devices_lost(vha, 0);
+ ha->isp_ops->reset_chip(vha);
+
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ list_for_each_entry(vp, &ha->vp_list, list)
+ qla2x00_mark_all_devices_lost(vp, 0);
} else {
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
}
/* Requeue all commands in outstanding command list. */
- qla2x00_abort_all_cmds(ha, DID_RESET << 16);
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
- ha->isp_ops->get_flash_version(ha, ha->request_ring);
+ ha->isp_ops->get_flash_version(vha, req->ring);
- ha->isp_ops->nvram_config(ha);
+ ha->isp_ops->nvram_config(vha);
- if (!qla2x00_restart_isp(ha)) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ if (!qla2x00_restart_isp(vha)) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
- if (!atomic_read(&ha->loop_down_timer)) {
+ if (!atomic_read(&vha->loop_down_timer)) {
/*
* Issue marker command only when we are going
* to start the I/O .
*/
- ha->marker_needed = 1;
+ vha->marker_needed = 1;
}
- ha->flags.online = 1;
+ vha->flags.online = 1;
ha->isp_ops->enable_intrs(ha);
ha->isp_abort_cnt = 0;
- clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
fce_calc_size(ha->fce_bufs));
- rval = qla2x00_enable_fce_trace(ha,
+ rval = qla2x00_enable_fce_trace(vha,
ha->fce_dma, ha->fce_bufs, ha->fce_mb,
&ha->fce_bufs);
if (rval) {
@@ -3307,7 +3343,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
if (ha->eft) {
memset(ha->eft, 0, EFT_SIZE);
- rval = qla2x00_enable_eft_trace(ha,
+ rval = qla2x00_enable_eft_trace(vha,
ha->eft_dma, EFT_NUM_BUFFERS);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -3316,8 +3352,8 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
}
}
} else { /* failed the ISP abort */
- ha->flags.online = 1;
- if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) {
+ vha->flags.online = 1;
+ if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
if (ha->isp_abort_cnt == 0) {
qla_printk(KERN_WARNING, ha,
"ISP error recovery failed - "
@@ -3326,37 +3362,41 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
* The next call disables the board
* completely.
*/
- ha->isp_ops->reset_adapter(ha);
- ha->flags.online = 0;
+ ha->isp_ops->reset_adapter(vha);
+ vha->flags.online = 0;
clear_bit(ISP_ABORT_RETRY,
- &ha->dpc_flags);
+ &vha->dpc_flags);
status = 0;
} else { /* schedule another ISP abort */
ha->isp_abort_cnt--;
DEBUG(printk("qla%ld: ISP abort - "
"retry remaining %d\n",
- ha->host_no, ha->isp_abort_cnt));
+ vha->host_no, ha->isp_abort_cnt));
status = 1;
}
} else {
ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
DEBUG(printk("qla2x00(%ld): ISP error recovery "
"- retrying (%d) more times\n",
- ha->host_no, ha->isp_abort_cnt));
- set_bit(ISP_ABORT_RETRY, &ha->dpc_flags);
+ vha->host_no, ha->isp_abort_cnt));
+ set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
status = 1;
}
}
}
- if (status) {
+ if (!status) {
+ DEBUG(printk(KERN_INFO
+ "qla2x00_abort_isp(%ld): succeeded.\n",
+ vha->host_no));
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx)
+ qla2x00_vp_abort_isp(vp);
+ }
+ } else {
qla_printk(KERN_INFO, ha,
"qla2x00_abort_isp: **** FAILED ****\n");
- } else {
- DEBUG(printk(KERN_INFO
- "qla2x00_abort_isp(%ld): exiting.\n",
- ha->host_no));
}
return(status);
@@ -3373,42 +3413,50 @@ qla2x00_abort_isp(scsi_qla_host_t *ha)
* 0 = success
*/
static int
-qla2x00_restart_isp(scsi_qla_host_t *ha)
+qla2x00_restart_isp(scsi_qla_host_t *vha)
{
uint8_t status = 0;
uint32_t wait_time;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
/* If firmware needs to be loaded */
- if (qla2x00_isp_firmware(ha)) {
- ha->flags.online = 0;
- if (!(status = ha->isp_ops->chip_diag(ha)))
- status = qla2x00_setup_chip(ha);
+ if (qla2x00_isp_firmware(vha)) {
+ vha->flags.online = 0;
+ status = ha->isp_ops->chip_diag(vha);
+ if (!status)
+ status = qla2x00_setup_chip(vha);
}
- if (!status && !(status = qla2x00_init_rings(ha))) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- if (!(status = qla2x00_fw_ready(ha))) {
+ if (!status && !(status = qla2x00_init_rings(vha))) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ /* Initialize the queues in use */
+ qla25xx_init_queues(ha);
+
+ status = qla2x00_fw_ready(vha);
+ if (!status) {
DEBUG(printk("%s(): Start configure loop, "
"status = %d\n", __func__, status));
/* Issue a marker after FW becomes ready. */
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
- ha->flags.online = 1;
+ vha->flags.online = 1;
/* Wait at most MAX_TARGET RSCNs for a stable link. */
wait_time = 256;
do {
- clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- qla2x00_configure_loop(ha);
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ qla2x00_configure_loop(vha);
wait_time--;
- } while (!atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) &&
- wait_time &&
- (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)));
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
}
/* if no cable then assume it's good */
- if ((ha->device_flags & DFLG_NO_CABLE))
+ if ((vha->device_flags & DFLG_NO_CABLE))
status = 0;
DEBUG(printk("%s(): Configure loop done, status = 0x%x\n",
@@ -3418,6 +3466,46 @@ qla2x00_restart_isp(scsi_qla_host_t *ha)
return (status);
}
+static int
+qla25xx_init_queues(struct qla_hw_data *ha)
+{
+ struct rsp_que *rsp = NULL;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ int ret = -1;
+ int i;
+
+ for (i = 1; i < ha->max_queues; i++) {
+ rsp = ha->rsp_q_map[i];
+ if (rsp) {
+ rsp->options &= ~BIT_0;
+ ret = qla25xx_init_rsp_que(base_vha, rsp, rsp->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Rsp que:%d init failed\n", __func__,
+ rsp->id));
+ else
+ DEBUG2_17(printk(KERN_INFO
+ "%s Rsp que:%d inited\n", __func__,
+ rsp->id));
+ }
+ req = ha->req_q_map[i];
+ if (req) {
+ req->options &= ~BIT_0;
+ ret = qla25xx_init_req_que(base_vha, req, req->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Req que:%d init failed\n", __func__,
+ req->id));
+ else
+ DEBUG2_17(printk(KERN_WARNING
+ "%s Rsp que:%d inited\n", __func__,
+ req->id));
+ }
+ }
+ return ret;
+}
+
/*
* qla2x00_reset_adapter
* Reset adapter.
@@ -3426,12 +3514,13 @@ qla2x00_restart_isp(scsi_qla_host_t *ha)
* ha = adapter block pointer.
*/
void
-qla2x00_reset_adapter(scsi_qla_host_t *ha)
+qla2x00_reset_adapter(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
- ha->flags.online = 0;
+ vha->flags.online = 0;
ha->isp_ops->disable_intrs(ha);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -3443,12 +3532,13 @@ qla2x00_reset_adapter(scsi_qla_host_t *ha)
}
void
-qla24xx_reset_adapter(scsi_qla_host_t *ha)
+qla24xx_reset_adapter(scsi_qla_host_t *vha)
{
unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
- ha->flags.online = 0;
+ vha->flags.online = 0;
ha->isp_ops->disable_intrs(ha);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -3462,9 +3552,11 @@ qla24xx_reset_adapter(scsi_qla_host_t *ha)
/* On sparc systems, obtain port and node WWN from firmware
* properties.
*/
-static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *nv)
+static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *vha,
+ struct nvram_24xx *nv)
{
#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
struct pci_dev *pdev = ha->pdev;
struct device_node *dp = pci_device_to_OF_node(pdev);
const u8 *val;
@@ -3481,7 +3573,7 @@ static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *ha, struct nvram_24xx *n
}
int
-qla24xx_nvram_config(scsi_qla_host_t *ha)
+qla24xx_nvram_config(scsi_qla_host_t *vha)
{
int rval;
struct init_cb_24xx *icb;
@@ -3490,6 +3582,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
uint8_t *dptr1, *dptr2;
uint32_t chksum;
uint16_t cnt;
+ struct qla_hw_data *ha = vha->hw;
rval = QLA_SUCCESS;
icb = (struct init_cb_24xx *)ha->init_cb;
@@ -3507,12 +3600,12 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
/* Get VPD data into cache */
ha->vpd = ha->nvram + VPD_OFFSET;
- ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd,
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd,
ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4);
/* Get NVRAM data into cache and calculate checksum. */
dptr = (uint32_t *)nv;
- ha->isp_ops->read_nvram(ha, (uint8_t *)dptr, ha->nvram_base,
+ ha->isp_ops->read_nvram(vha, (uint8_t *)dptr, ha->nvram_base,
ha->nvram_size);
for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
chksum += le32_to_cpu(*dptr++);
@@ -3557,7 +3650,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
nv->node_name[5] = 0x1c;
nv->node_name[6] = 0x55;
nv->node_name[7] = 0x86;
- qla24xx_nvram_wwn_from_ofw(ha, nv);
+ qla24xx_nvram_wwn_from_ofw(vha, nv);
nv->login_retry_count = __constant_cpu_to_le16(8);
nv->interrupt_delay_timer = __constant_cpu_to_le16(0);
nv->login_timeout = __constant_cpu_to_le16(0);
@@ -3577,7 +3670,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
}
/* Reset Initialization control block */
- memset(icb, 0, sizeof(struct init_cb_24xx));
+ memset(icb, 0, ha->init_cb_size);
/* Copy 1st segment. */
dptr1 = (uint8_t *)icb;
@@ -3600,7 +3693,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
/*
* Setup driver NVRAM options.
*/
- qla2x00_set_model_info(ha, nv->model_name, sizeof(nv->model_name),
+ qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
"QLA2462");
/* Use alternate WWN? */
@@ -3639,8 +3732,8 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
ha->serial0 = icb->port_name[5];
ha->serial1 = icb->port_name[6];
ha->serial2 = icb->port_name[7];
- ha->node_name = icb->node_name;
- ha->port_name = icb->port_name;
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
@@ -3695,7 +3788,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
ha->login_retry_count = ql2xloginretrycount;
/* Enable ZIO. */
- if (!ha->flags.init_done) {
+ if (!vha->flags.init_done) {
ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
(BIT_3 | BIT_2 | BIT_1 | BIT_0);
ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ?
@@ -3703,12 +3796,12 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
}
icb->firmware_options_2 &= __constant_cpu_to_le32(
~(BIT_3 | BIT_2 | BIT_1 | BIT_0));
- ha->flags.process_response_queue = 0;
+ vha->flags.process_response_queue = 0;
if (ha->zio_mode != QLA_ZIO_DISABLED) {
ha->zio_mode = QLA_ZIO_MODE_6;
DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay "
- "(%d us).\n", ha->host_no, ha->zio_mode,
+ "(%d us).\n", vha->host_no, ha->zio_mode,
ha->zio_timer * 100));
qla_printk(KERN_INFO, ha,
"ZIO mode %d enabled; timer delay (%d us).\n",
@@ -3717,36 +3810,37 @@ qla24xx_nvram_config(scsi_qla_host_t *ha)
icb->firmware_options_2 |= cpu_to_le32(
(uint32_t)ha->zio_mode);
icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer);
- ha->flags.process_response_queue = 1;
+ vha->flags.process_response_queue = 1;
}
if (rval) {
DEBUG2_3(printk(KERN_WARNING
- "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ "scsi(%ld): NVRAM configuration failed!\n", vha->host_no));
}
return (rval);
}
static int
-qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
- int rval;
+ int rval = QLA_SUCCESS;
int segments, fragment;
uint32_t faddr;
uint32_t *dcode, dlen;
uint32_t risc_addr;
uint32_t risc_size;
uint32_t i;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
rval = QLA_SUCCESS;
segments = FA_RISC_CODE_SEGMENTS;
faddr = ha->flt_region_fw;
- dcode = (uint32_t *)ha->request_ring;
+ dcode = (uint32_t *)req->ring;
*srisc_addr = 0;
/* Validate firmware image by checking version. */
- qla24xx_read_flash_data(ha, dcode, faddr + 4, 4);
+ qla24xx_read_flash_data(vha, dcode, faddr + 4, 4);
for (i = 0; i < 4; i++)
dcode[i] = be32_to_cpu(dcode[i]);
if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
@@ -3764,7 +3858,7 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
while (segments && rval == QLA_SUCCESS) {
/* Read segment's load information. */
- qla24xx_read_flash_data(ha, dcode, faddr, 4);
+ qla24xx_read_flash_data(vha, dcode, faddr, 4);
risc_addr = be32_to_cpu(dcode[2]);
*srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
@@ -3778,17 +3872,17 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
"addr %x, number of dwords 0x%x, offset 0x%x.\n",
- ha->host_no, risc_addr, dlen, faddr));
+ vha->host_no, risc_addr, dlen, faddr));
- qla24xx_read_flash_data(ha, dcode, faddr, dlen);
+ qla24xx_read_flash_data(vha, dcode, faddr, dlen);
for (i = 0; i < dlen; i++)
dcode[i] = swab32(dcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
dlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -3812,16 +3906,18 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
#define QLA_FW_URL "ftp://ftp.qlogic.com/outgoing/linux/firmware/"
int
-qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
int i, fragment;
uint16_t *wcode, *fwcode;
uint32_t risc_addr, risc_size, fwclen, wlen, *seg;
struct fw_blob *blob;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Load firmware blob. */
- blob = qla2x00_request_firmware(ha);
+ blob = qla2x00_request_firmware(vha);
if (!blob) {
qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
@@ -3831,7 +3927,7 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
rval = QLA_SUCCESS;
- wcode = (uint16_t *)ha->request_ring;
+ wcode = (uint16_t *)req->ring;
*srisc_addr = 0;
fwcode = (uint16_t *)blob->fw->data;
fwclen = 0;
@@ -3878,17 +3974,17 @@ qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
wlen = risc_size;
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
- "addr %x, number of words 0x%x.\n", ha->host_no,
+ "addr %x, number of words 0x%x.\n", vha->host_no,
risc_addr, wlen));
for (i = 0; i < wlen; i++)
wcode[i] = swab16(fwcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
wlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -3912,7 +4008,7 @@ fail_fw_integrity:
}
int
-qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
{
int rval;
int segments, fragment;
@@ -3922,9 +4018,11 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
uint32_t i;
struct fw_blob *blob;
uint32_t *fwcode, fwclen;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/* Load firmware blob. */
- blob = qla2x00_request_firmware(ha);
+ blob = qla2x00_request_firmware(vha);
if (!blob) {
qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
@@ -3933,13 +4031,13 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
/* Try to load RISC code from flash. */
qla_printk(KERN_ERR, ha, "Attempting to load (potentially "
"outdated) firmware from flash.\n");
- return qla24xx_load_risc_flash(ha, srisc_addr);
+ return qla24xx_load_risc_flash(vha, srisc_addr);
}
rval = QLA_SUCCESS;
segments = FA_RISC_CODE_SEGMENTS;
- dcode = (uint32_t *)ha->request_ring;
+ dcode = (uint32_t *)req->ring;
*srisc_addr = 0;
fwcode = (uint32_t *)blob->fw->data;
fwclen = 0;
@@ -3987,17 +4085,17 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
dlen = risc_size;
DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
- "addr %x, number of dwords 0x%x.\n", ha->host_no,
+ "addr %x, number of dwords 0x%x.\n", vha->host_no,
risc_addr, dlen));
for (i = 0; i < dlen; i++)
dcode[i] = swab32(fwcode[i]);
- rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
dlen);
if (rval) {
DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
- "segment %d of firmware\n", ha->host_no,
+ "segment %d of firmware\n", vha->host_no,
fragment));
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to load segment %d of "
@@ -4021,49 +4119,53 @@ fail_fw_integrity:
}
void
-qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha)
+qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
{
int ret, retries;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_FWI2_CAPABLE(ha))
return;
if (!ha->fw_major_version)
return;
- ret = qla2x00_stop_firmware(ha);
+ ret = qla2x00_stop_firmware(vha);
for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
retries ; retries--) {
- ha->isp_ops->reset_chip(ha);
- if (ha->isp_ops->chip_diag(ha) != QLA_SUCCESS)
+ ha->isp_ops->reset_chip(vha);
+ if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS)
continue;
- if (qla2x00_setup_chip(ha) != QLA_SUCCESS)
+ if (qla2x00_setup_chip(vha) != QLA_SUCCESS)
continue;
qla_printk(KERN_INFO, ha,
"Attempting retry of stop-firmware command...\n");
- ret = qla2x00_stop_firmware(ha);
+ ret = qla2x00_stop_firmware(vha);
}
}
int
-qla24xx_configure_vhba(scsi_qla_host_t *ha)
+qla24xx_configure_vhba(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
- if (!ha->parent)
+ if (!vha->vp_idx)
return -EINVAL;
- rval = qla2x00_fw_ready(ha->parent);
+ rval = qla2x00_fw_ready(base_vha);
if (rval == QLA_SUCCESS) {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- qla2x00_marker(ha, 0, 0, MK_SYNC_ALL);
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
}
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
/* Login to SNS first */
- qla24xx_login_fabric(ha->parent, NPH_SNS, 0xff, 0xff, 0xfc,
- mb, BIT_1);
+ ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, BIT_1);
if (mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG15(qla_printk(KERN_INFO, ha,
"Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
@@ -4072,11 +4174,11 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha)
return (QLA_FUNCTION_FAILED);
}
- atomic_set(&ha->loop_down_timer, 0);
- atomic_set(&ha->loop_state, LOOP_UP);
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- rval = qla2x00_loop_resync(ha->parent);
+ atomic_set(&vha->loop_down_timer, 0);
+ atomic_set(&vha->loop_state, LOOP_UP);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ rval = qla2x00_loop_resync(base_vha);
return rval;
}
@@ -4087,9 +4189,10 @@ static LIST_HEAD(qla_cs84xx_list);
static DEFINE_MUTEX(qla_cs84xx_mutex);
static struct qla_chip_state_84xx *
-qla84xx_get_chip(struct scsi_qla_host *ha)
+qla84xx_get_chip(struct scsi_qla_host *vha)
{
struct qla_chip_state_84xx *cs84xx;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&qla_cs84xx_mutex);
@@ -4129,21 +4232,23 @@ __qla84xx_chip_release(struct kref *kref)
}
void
-qla84xx_put_chip(struct scsi_qla_host *ha)
+qla84xx_put_chip(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
if (ha->cs84xx)
kref_put(&ha->cs84xx->kref, __qla84xx_chip_release);
}
static int
-qla84xx_init_chip(scsi_qla_host_t *ha)
+qla84xx_init_chip(scsi_qla_host_t *vha)
{
int rval;
uint16_t status[2];
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->cs84xx->fw_update_mutex);
- rval = qla84xx_verify_chip(ha, status);
+ rval = qla84xx_verify_chip(vha, status);
mutex_unlock(&ha->cs84xx->fw_update_mutex);
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index e90afad120ee..5e0a7095c9f2 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -32,47 +32,15 @@ qla2x00_debounce_register(volatile uint16_t __iomem *addr)
}
static inline void
-qla2x00_poll(scsi_qla_host_t *ha)
+qla2x00_poll(struct rsp_que *rsp)
{
unsigned long flags;
-
+ struct qla_hw_data *ha = rsp->hw;
local_irq_save(flags);
- ha->isp_ops->intr_handler(0, ha);
+ ha->isp_ops->intr_handler(0, rsp);
local_irq_restore(flags);
}
-static __inline__ scsi_qla_host_t *
-to_qla_parent(scsi_qla_host_t *ha)
-{
- return ha->parent ? ha->parent : ha;
-}
-
-/**
- * qla2x00_issue_marker() - Issue a Marker IOCB if necessary.
- * @ha: HA context
- * @ha_locked: is function called with the hardware lock
- *
- * Returns non-zero if a failure occurred, else zero.
- */
-static inline int
-qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked)
-{
- /* Send marker if required */
- if (ha->marker_needed != 0) {
- if (ha_locked) {
- if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
- QLA_SUCCESS)
- return (QLA_FUNCTION_FAILED);
- } else {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
- QLA_SUCCESS)
- return (QLA_FUNCTION_FAILED);
- }
- ha->marker_needed = 0;
- }
- return (QLA_SUCCESS);
-}
-
static inline uint8_t *
host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
{
@@ -87,11 +55,12 @@ host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
}
static inline int
-qla2x00_is_reserved_id(scsi_qla_host_t *ha, uint16_t loop_id)
+qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id)
{
+ struct qla_hw_data *ha = vha->hw;
if (IS_FWI2_CAPABLE(ha))
return (loop_id > NPH_LAST_HANDLE);
- return ((loop_id > ha->last_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
+ return ((loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST);
-};
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 85bc0a48598b..5bedc9d05942 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -11,8 +11,9 @@
#include <scsi/scsi_tcq.h>
-static request_t *qla2x00_req_pkt(scsi_qla_host_t *ha);
-static void qla2x00_isp_cmd(scsi_qla_host_t *ha);
+static request_t *qla2x00_req_pkt(struct scsi_qla_host *, struct req_que *,
+ struct rsp_que *rsp);
+static void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *);
/**
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
@@ -30,11 +31,11 @@ qla2x00_get_cmd_direction(srb_t *sp)
/* Set transfer direction */
if (sp->cmd->sc_data_direction == DMA_TO_DEVICE) {
cflags = CF_WRITE;
- sp->fcport->ha->qla_stats.output_bytes +=
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
scsi_bufflen(sp->cmd);
} else if (sp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
cflags = CF_READ;
- sp->fcport->ha->qla_stats.input_bytes +=
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
scsi_bufflen(sp->cmd);
}
return (cflags);
@@ -91,20 +92,19 @@ qla2x00_calc_iocbs_64(uint16_t dsds)
* Returns a pointer to the Continuation Type 0 IOCB packet.
*/
static inline cont_entry_t *
-qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
+qla2x00_prep_cont_type0_iocb(struct req_que *req, struct scsi_qla_host *vha)
{
cont_entry_t *cont_pkt;
-
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else {
- ha->request_ring_ptr++;
+ req->ring_ptr++;
}
- cont_pkt = (cont_entry_t *)ha->request_ring_ptr;
+ cont_pkt = (cont_entry_t *)req->ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
@@ -120,20 +120,20 @@ qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
* Returns a pointer to the continuation type 1 IOCB packet.
*/
static inline cont_a64_entry_t *
-qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha)
+qla2x00_prep_cont_type1_iocb(struct req_que *req, scsi_qla_host_t *vha)
{
cont_a64_entry_t *cont_pkt;
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else {
- ha->request_ring_ptr++;
+ req->ring_ptr++;
}
- cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr;
+ cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
/* Load packet defaults. */
*((uint32_t *)(&cont_pkt->entry_type)) =
@@ -155,10 +155,11 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -172,7 +173,8 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
@@ -190,7 +192,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
* Seven DSDs are available in the Continuation
* Type 0 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type0_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type0_iocb(req, vha);
cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
avail_dsds = 7;
}
@@ -214,10 +216,11 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -231,7 +234,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
@@ -250,7 +254,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -274,7 +278,7 @@ qla2x00_start_scsi(srb_t *sp)
{
int ret, nseg;
unsigned long flags;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
uint32_t *clr_ptr;
uint32_t index;
@@ -284,33 +288,39 @@ qla2x00_start_scsi(srb_t *sp)
uint16_t req_cnt;
uint16_t tot_dsds;
struct device_reg_2xxx __iomem *reg;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
/* Setup device pointers. */
ret = 0;
- ha = sp->ha;
+ vha = sp->vha;
+ ha = vha->hw;
reg = &ha->iobase->isp;
cmd = sp->cmd;
+ req = ha->req_q_map[0];
+ rsp = ha->rsp_q_map[0];
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
/* Send marker if required */
- if (ha->marker_needed != 0) {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
+ != QLA_SUCCESS)
return (QLA_FUNCTION_FAILED);
- }
- ha->marker_needed = 0;
+ vha->marker_needed = 0;
}
/* Acquire ring specific lock */
spin_lock_irqsave(&ha->hardware_lock, flags);
/* Check for room in outstanding command list. */
- handle = ha->current_outstanding_cmd;
+ handle = req->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
handle++;
if (handle == MAX_OUTSTANDING_COMMANDS)
handle = 1;
- if (!ha->outstanding_cmds[handle])
+ if (!req->outstanding_cmds[handle])
break;
}
if (index == MAX_OUTSTANDING_COMMANDS)
@@ -329,25 +339,26 @@ qla2x00_start_scsi(srb_t *sp)
/* Calculate the number of request entries needed. */
req_cnt = ha->isp_ops->calc_req_entries(tot_dsds);
- if (ha->req_q_cnt < (req_cnt + 2)) {
+ if (req->cnt < (req_cnt + 2)) {
cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg));
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
- if (ha->req_q_cnt < (req_cnt + 2))
+ if (req->cnt < (req_cnt + 2))
goto queuing_error;
/* Build command packet */
- ha->current_outstanding_cmd = handle;
- ha->outstanding_cmds[handle] = sp;
- sp->ha = ha;
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->vha = vha;
+ sp->que = req;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
- ha->req_q_cnt -= req_cnt;
+ req->cnt -= req_cnt;
- cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr;
+ cmd_pkt = (cmd_entry_t *)req->ring_ptr;
cmd_pkt->handle = handle;
/* Zero out remaining portion of packet. */
clr_ptr = (uint32_t *)cmd_pkt + 2;
@@ -373,23 +384,23 @@ qla2x00_start_scsi(srb_t *sp)
wmb();
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
sp->flags |= SRB_DMA_VALID;
/* Set chip new ring index. */
- WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), req->ring_index);
RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
/* Manage unprocessed RIO/ZIO commands in response queue. */
- if (ha->flags.process_response_queue &&
- ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
- qla2x00_process_response_queue(ha);
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla2x00_process_response_queue(rsp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return (QLA_SUCCESS);
@@ -415,18 +426,20 @@ queuing_error:
* Returns non-zero if a failure occurred, else zero.
*/
int
-__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
- uint8_t type)
+__qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id,
+ uint16_t lun, uint8_t type)
{
mrk_entry_t *mrk;
struct mrk_entry_24xx *mrk24;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
mrk24 = NULL;
- mrk = (mrk_entry_t *)qla2x00_req_pkt(pha);
+ mrk = (mrk_entry_t *)qla2x00_req_pkt(vha, req, rsp);
if (mrk == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
return (QLA_FUNCTION_FAILED);
}
@@ -440,7 +453,7 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
mrk24->lun[1] = LSB(lun);
mrk24->lun[2] = MSB(lun);
host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
- mrk24->vp_index = ha->vp_idx;
+ mrk24->vp_index = vha->vp_idx;
} else {
SET_TARGET_ID(ha, mrk->target, loop_id);
mrk->lun = cpu_to_le16(lun);
@@ -448,22 +461,22 @@ __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
}
wmb();
- qla2x00_isp_cmd(pha);
+ qla2x00_isp_cmd(vha, req);
return (QLA_SUCCESS);
}
int
-qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
- uint8_t type)
+qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id, uint16_t lun,
+ uint8_t type)
{
int ret;
unsigned long flags = 0;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- spin_lock_irqsave(&pha->hardware_lock, flags);
- ret = __qla2x00_marker(ha, loop_id, lun, type);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+ ret = __qla2x00_marker(vha, req, rsp, loop_id, lun, type);
+ spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
return (ret);
}
@@ -477,9 +490,11 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
* Returns NULL if function failed, else, a pointer to the request packet.
*/
static request_t *
-qla2x00_req_pkt(scsi_qla_host_t *ha)
+qla2x00_req_pkt(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp)
{
- device_reg_t __iomem *reg = ha->iobase;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
request_t *pkt = NULL;
uint16_t cnt;
uint32_t *dword_ptr;
@@ -488,24 +503,29 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
/* Wait 1 second for slot. */
for (timer = HZ; timer; timer--) {
- if ((req_cnt + 2) >= ha->req_q_cnt) {
+ if ((req_cnt + 2) >= req->cnt) {
/* Calculate number of free request entries. */
- if (IS_FWI2_CAPABLE(ha))
- cnt = (uint16_t)RD_REG_DWORD(
- &reg->isp24.req_q_out);
- else
- cnt = qla2x00_debounce_register(
- ISP_REQ_Q_OUT(ha, &reg->isp));
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (ha->mqenable)
+ cnt = (uint16_t)
+ RD_REG_DWORD(&reg->isp25mq.req_q_out);
+ else {
+ if (IS_FWI2_CAPABLE(ha))
+ cnt = (uint16_t)RD_REG_DWORD(
+ &reg->isp24.req_q_out);
+ else
+ cnt = qla2x00_debounce_register(
+ ISP_REQ_Q_OUT(ha, &reg->isp));
+ }
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
/* If room for request in request ring. */
- if ((req_cnt + 2) < ha->req_q_cnt) {
- ha->req_q_cnt--;
- pkt = ha->request_ring_ptr;
+ if ((req_cnt + 2) < req->cnt) {
+ req->cnt--;
+ pkt = req->ring_ptr;
/* Zero out packet. */
dword_ptr = (uint32_t *)pkt;
@@ -513,7 +533,7 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
*dword_ptr++ = 0;
/* Set system defined field. */
- pkt->sys_define = (uint8_t)ha->req_ring_index;
+ pkt->sys_define = (uint8_t)req->ring_index;
/* Set entry count. */
pkt->entry_count = 1;
@@ -522,15 +542,14 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
}
/* Release ring specific lock */
- spin_unlock(&ha->hardware_lock);
+ spin_unlock_irq(&ha->hardware_lock);
udelay(2); /* 2 us */
/* Check for pending interrupts. */
/* During init we issue marker directly */
- if (!ha->marker_needed && !ha->flags.init_done)
- qla2x00_poll(ha);
-
+ if (!vha->marker_needed && !vha->flags.init_done)
+ qla2x00_poll(rsp);
spin_lock_irq(&ha->hardware_lock);
}
if (!pkt) {
@@ -547,29 +566,38 @@ qla2x00_req_pkt(scsi_qla_host_t *ha)
* Note: The caller must hold the hardware lock before calling this routine.
*/
static void
-qla2x00_isp_cmd(scsi_qla_host_t *ha)
+qla2x00_isp_cmd(struct scsi_qla_host *vha, struct req_que *req)
{
- device_reg_t __iomem *reg = ha->iobase;
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
DEBUG5(printk("%s(): IOCB data:\n", __func__));
DEBUG5(qla2x00_dump_buffer(
- (uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
+ (uint8_t *)req->ring_ptr, REQUEST_ENTRY_SIZE));
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
/* Set chip new ring index. */
- if (IS_FWI2_CAPABLE(ha)) {
- WRT_REG_DWORD(&reg->isp24.req_q_in, ha->req_ring_index);
- RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
- } else {
- WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp), ha->req_ring_index);
- RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ if (ha->mqenable) {
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, req->ring_index);
+ RD_REG_DWORD(&ioreg->hccr);
+ }
+ else {
+ if (IS_FWI2_CAPABLE(ha)) {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+ } else {
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp),
+ req->ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ }
}
}
@@ -610,10 +638,11 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
{
uint16_t avail_dsds;
uint32_t *cur_dsd;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct scsi_cmnd *cmd;
struct scatterlist *sg;
int i;
+ struct req_que *req;
cmd = sp->cmd;
@@ -627,18 +656,19 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
return;
}
- ha = sp->ha;
+ vha = sp->vha;
+ req = sp->que;
/* Set transfer direction */
if (cmd->sc_data_direction == DMA_TO_DEVICE) {
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_WRITE_DATA);
- sp->fcport->ha->qla_stats.output_bytes +=
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
scsi_bufflen(sp->cmd);
} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_READ_DATA);
- sp->fcport->ha->qla_stats.input_bytes +=
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
scsi_bufflen(sp->cmd);
}
@@ -658,7 +688,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
* Five DSDs are available in the Continuation
* Type 1 IOCB.
*/
- cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
+ cont_pkt = qla2x00_prep_cont_type1_iocb(req, vha);
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
avail_dsds = 5;
}
@@ -683,8 +713,6 @@ qla24xx_start_scsi(srb_t *sp)
{
int ret, nseg;
unsigned long flags;
- scsi_qla_host_t *ha, *pha;
- struct scsi_cmnd *cmd;
uint32_t *clr_ptr;
uint32_t index;
uint32_t handle;
@@ -692,35 +720,45 @@ qla24xx_start_scsi(srb_t *sp)
uint16_t cnt;
uint16_t req_cnt;
uint16_t tot_dsds;
- struct device_reg_24xx __iomem *reg;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_qla_host *vha = sp->vha;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id;
/* Setup device pointers. */
ret = 0;
- ha = sp->ha;
- pha = to_qla_parent(ha);
- reg = &ha->iobase->isp24;
- cmd = sp->cmd;
+ que_id = vha->req_ques[0];
+
+ req = ha->req_q_map[que_id];
+ sp->que = req;
+
+ if (req->rsp)
+ rsp = req->rsp;
+ else
+ rsp = ha->rsp_q_map[que_id];
/* So we know we haven't pci_map'ed anything yet */
tot_dsds = 0;
/* Send marker if required */
- if (ha->marker_needed != 0) {
- if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL)
+ != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
- }
- ha->marker_needed = 0;
+ vha->marker_needed = 0;
}
/* Acquire ring specific lock */
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
/* Check for room in outstanding command list. */
- handle = ha->current_outstanding_cmd;
+ handle = req->current_outstanding_cmd;
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
handle++;
if (handle == MAX_OUTSTANDING_COMMANDS)
handle = 1;
- if (!ha->outstanding_cmds[handle])
+ if (!req->outstanding_cmds[handle])
break;
}
if (index == MAX_OUTSTANDING_COMMANDS)
@@ -738,25 +776,26 @@ qla24xx_start_scsi(srb_t *sp)
tot_dsds = nseg;
req_cnt = qla24xx_calc_iocbs(tot_dsds);
- if (ha->req_q_cnt < (req_cnt + 2)) {
- cnt = (uint16_t)RD_REG_DWORD_RELAXED(&reg->req_q_out);
- if (ha->req_ring_index < cnt)
- ha->req_q_cnt = cnt - ha->req_ring_index;
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = ha->isp_ops->rd_req_reg(ha, req->id);
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
else
- ha->req_q_cnt = ha->request_q_length -
- (ha->req_ring_index - cnt);
+ req->cnt = req->length -
+ (req->ring_index - cnt);
}
- if (ha->req_q_cnt < (req_cnt + 2))
+ if (req->cnt < (req_cnt + 2))
goto queuing_error;
/* Build command packet. */
- ha->current_outstanding_cmd = handle;
- ha->outstanding_cmds[handle] = sp;
- sp->ha = ha;
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->vha = vha;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
- ha->req_q_cnt -= req_cnt;
+ req->cnt -= req_cnt;
- cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr;
+ cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
cmd_pkt->handle = handle;
/* Zero out remaining portion of packet. */
@@ -789,32 +828,63 @@ qla24xx_start_scsi(srb_t *sp)
wmb();
/* Adjust ring index. */
- ha->req_ring_index++;
- if (ha->req_ring_index == ha->request_q_length) {
- ha->req_ring_index = 0;
- ha->request_ring_ptr = ha->request_ring;
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
} else
- ha->request_ring_ptr++;
+ req->ring_ptr++;
sp->flags |= SRB_DMA_VALID;
/* Set chip new ring index. */
- WRT_REG_DWORD(&reg->req_q_in, ha->req_ring_index);
- RD_REG_DWORD_RELAXED(&reg->req_q_in); /* PCI Posting. */
+ ha->isp_ops->wrt_req_reg(ha, req->id, req->ring_index);
/* Manage unprocessed RIO/ZIO commands in response queue. */
- if (ha->flags.process_response_queue &&
- ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
- qla24xx_process_response_queue(ha);
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla24xx_process_response_queue(rsp);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
queuing_error:
if (tot_dsds)
scsi_dma_unmap(cmd);
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_FUNCTION_FAILED;
}
+
+uint16_t
+qla24xx_rd_req_reg(struct qla_hw_data *ha, uint16_t id)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ return RD_REG_DWORD_RELAXED(&reg->isp24.req_q_out);
+}
+
+uint16_t
+qla25xx_rd_req_reg(struct qla_hw_data *ha, uint16_t id)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ return RD_REG_DWORD_RELAXED(&reg->isp25mq.req_q_out);
+}
+
+void
+qla24xx_wrt_req_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ WRT_REG_DWORD(&reg->isp24.req_q_in, index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+}
+
+void
+qla25xx_wrt_req_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, index);
+ RD_REG_DWORD(&ioreg->hccr); /* PCI posting */
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index a76efd99d007..d5fb79a88001 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -10,10 +10,13 @@
#include <scsi/scsi_tcq.h>
static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
-static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t);
-static void qla2x00_status_entry(scsi_qla_host_t *, void *);
+static void qla2x00_process_completed_request(struct scsi_qla_host *,
+ struct req_que *, uint32_t);
+static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
static void qla2x00_status_cont_entry(scsi_qla_host_t *, sts_cont_entry_t *);
-static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
+static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
+ sts_entry_t *);
+static struct scsi_qla_host *qla2x00_get_rsp_host(struct rsp_que *);
/**
* qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
@@ -27,24 +30,28 @@ static void qla2x00_error_entry(scsi_qla_host_t *, sts_entry_t *);
irqreturn_t
qla2100_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
struct device_reg_2xxx __iomem *reg;
int status;
unsigned long iter;
uint16_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return (IRQ_NONE);
}
+ ha = rsp->hw;
reg = &ha->iobase->isp;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
hccr = RD_REG_WORD(&reg->hccr);
if (hccr & HCCR_RISC_PAUSE) {
@@ -59,8 +66,8 @@ qla2100_intr_handler(int irq, void *dev_id)
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) == 0)
break;
@@ -72,24 +79,24 @@ qla2100_intr_handler(int irq, void *dev_id)
/* Get mailbox data. */
mb[0] = RD_MAILBOX_REG(ha, reg, 0);
if (mb[0] > 0x3fff && mb[0] < 0x8000) {
- qla2x00_mbx_completion(ha, mb[0]);
+ qla2x00_mbx_completion(vha, mb[0]);
status |= MBX_INTERRUPT;
} else if (mb[0] > 0x7fff && mb[0] < 0xc000) {
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
mb[3] = RD_MAILBOX_REG(ha, reg, 3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
} else {
/*EMPTY*/
DEBUG2(printk("scsi(%ld): Unrecognized "
"interrupt type (%d).\n",
- ha->host_no, mb[0]));
+ vha->host_no, mb[0]));
}
/* Release mailbox registers. */
WRT_REG_WORD(&reg->semaphore, 0);
RD_REG_WORD(&reg->semaphore);
} else {
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
RD_REG_WORD(&reg->hccr);
@@ -118,25 +125,29 @@ qla2100_intr_handler(int irq, void *dev_id)
irqreturn_t
qla2300_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
struct device_reg_2xxx __iomem *reg;
int status;
unsigned long iter;
uint32_t stat;
uint16_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
+ struct qla_hw_data *ha;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return (IRQ_NONE);
}
+ ha = rsp->hw;
reg = &ha->iobase->isp;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
if (stat & HSR_RISC_PAUSED) {
@@ -159,8 +170,8 @@ qla2300_intr_handler(int irq, void *dev_id)
WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
RD_REG_WORD(&reg->hccr);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSR_RISC_INT) == 0)
break;
@@ -170,7 +181,7 @@ qla2300_intr_handler(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla2x00_mbx_completion(ha, MSW(stat));
+ qla2x00_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
/* Release mailbox registers. */
@@ -181,26 +192,26 @@ qla2300_intr_handler(int irq, void *dev_id)
mb[1] = RD_MAILBOX_REG(ha, reg, 1);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
mb[3] = RD_MAILBOX_REG(ha, reg, 3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
break;
case 0x15:
mb[0] = MBA_CMPLT_1_16BIT;
mb[1] = MSW(stat);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x16:
mb[0] = MBA_SCSI_COMPLETION;
mb[1] = MSW(stat);
mb[2] = RD_MAILBOX_REG(ha, reg, 2);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
@@ -223,10 +234,11 @@ qla2300_intr_handler(int irq, void *dev_id)
* @mb0: Mailbox0 register
*/
static void
-qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
+qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
uint16_t cnt;
uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Load return mailbox registers. */
@@ -247,10 +259,10 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
if (ha->mcp) {
DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
- __func__, ha->host_no, ha->mcp->mb[0]));
+ __func__, vha->host_no, ha->mcp->mb[0]));
} else {
DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
}
}
@@ -260,7 +272,7 @@ qla2x00_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
* @mb: Mailbox registers (0 - 3)
*/
void
-qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
+qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
{
#define LS_UNKNOWN 2
static char *link_speeds[5] = { "1", "2", "?", "4", "8" };
@@ -268,6 +280,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
uint16_t handle_cnt;
uint16_t cnt;
uint32_t handles[5];
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
@@ -329,17 +342,19 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
switch (mb[0]) {
case MBA_SCSI_COMPLETION: /* Fast Post */
- if (!ha->flags.online)
+ if (!vha->flags.online)
break;
for (cnt = 0; cnt < handle_cnt; cnt++)
- qla2x00_process_completed_request(ha, handles[cnt]);
+ qla2x00_process_completed_request(vha, rsp->req,
+ handles[cnt]);
break;
case MBA_RESET: /* Reset */
- DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no));
+ DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n",
+ vha->host_no));
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
break;
case MBA_SYSTEM_ERR: /* System Error */
@@ -347,70 +362,70 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
"ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
mb[1], mb[2], mb[3]);
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- ha->isp_ops->fw_dump(ha, 1);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ ha->isp_ops->fw_dump(vha, 1);
if (IS_FWI2_CAPABLE(ha)) {
if (mb[1] == 0 && mb[2] == 0) {
qla_printk(KERN_ERR, ha,
"Unrecoverable Hardware Error: adapter "
"marked OFFLINE!\n");
- ha->flags.online = 0;
+ vha->flags.online = 0;
} else
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
} else if (mb[1] == 0) {
qla_printk(KERN_INFO, ha,
"Unrecoverable Hardware Error: adapter marked "
"OFFLINE!\n");
- ha->flags.online = 0;
+ vha->flags.online = 0;
} else
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n");
- qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n",
- ha->host_no));
+ vha->host_no));
break;
case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
- DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no,
+ DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", vha->host_no,
mb[1]));
qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LIP, mb[1]);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIP, mb[1]);
break;
case MBA_LOOP_UP: /* Loop Up Event */
@@ -425,59 +440,59 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
}
DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n",
- ha->host_no, link_speed));
+ vha->host_no, link_speed));
qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n",
link_speed);
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LINKUP, ha->link_data_rate);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
- "(%x %x %x).\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
mb[1], mb[2], mb[3]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- ha->device_flags |= DFLG_NO_CABLE;
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ vha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
ha->link_data_rate = PORT_SPEED_UNKNOWN;
- qla2x00_post_aen_work(ha, FCH_EVT_LINKDOWN, 0);
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKDOWN, 0);
break;
case MBA_LIP_RESET: /* LIP reset occurred */
DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n",
- ha->host_no, mb[1]));
+ vha->host_no, mb[1]));
qla_printk(KERN_INFO, ha,
"LIP reset occurred (%x).\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
ha->operating_mode = LOOP;
- ha->flags.management_server_logged_in = 0;
- qla2x00_post_aen_work(ha, FCH_EVT_LIPRESET, mb[1]);
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]);
break;
case MBA_POINT_TO_POINT: /* Point-to-Point */
@@ -485,33 +500,33 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
break;
DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n",
- ha->host_no));
+ vha->host_no));
/*
* Until there's a transition from loop down to loop up, treat
* this as loop down only.
*/
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- if (!(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
- set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
- }
- set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
- set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags);
+ if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)))
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
ha->flags.gpsc_supported = 1;
- ha->flags.management_server_logged_in = 0;
+ vha->flags.management_server_logged_in = 0;
break;
case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
@@ -520,134 +535,137 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection "
"received.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_INFO, ha,
"Configuration change detected: value=%x.\n", mb[1]);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN) {
- atomic_set(&ha->loop_state, LOOP_DOWN);
- if (!atomic_read(&ha->loop_down_timer))
- atomic_set(&ha->loop_down_timer,
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
}
- if (ha->parent) {
- atomic_set(&ha->vp_state, VP_FAILED);
- fc_vport_set_state(ha->fc_vport, FC_VPORT_FAILED);
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
}
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
case MBA_PORT_UPDATE: /* Port database update */
+ /* Only handle SCNs for our Vport index. */
+ if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
+ break;
+
/*
* If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET
* event etc. earlier indicating loop is down) then process
* it. Otherwise ignore it and Wait for RSCN to come in.
*/
- atomic_set(&ha->loop_down_timer, 0);
- if (atomic_read(&ha->loop_state) != LOOP_DOWN &&
- atomic_read(&ha->loop_state) != LOOP_DEAD) {
+ atomic_set(&vha->loop_down_timer, 0);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
+ atomic_read(&vha->loop_state) != LOOP_DEAD) {
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE "
- "ignored %04x/%04x/%04x.\n", ha->host_no, mb[1],
+ "ignored %04x/%04x/%04x.\n", vha->host_no, mb[1],
mb[2], mb[3]));
break;
}
DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): Port database changed %04x %04x %04x.\n",
- ha->host_no, mb[1], mb[2], mb[3]));
+ vha->host_no, mb[1], mb[2], mb[3]));
/*
* Mark all devices as missing so we will login again.
*/
- atomic_set(&ha->loop_state, LOOP_UP);
+ atomic_set(&vha->loop_state, LOOP_UP);
- qla2x00_mark_all_devices_lost(ha, 1);
+ qla2x00_mark_all_devices_lost(vha, 1);
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
case MBA_RSCN_UPDATE: /* State Change Registration */
/* Check if the Vport has issued a SCR */
- if (ha->parent && test_bit(VP_SCR_NEEDED, &ha->vp_flags))
+ if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags))
break;
/* Only handle SCNs for our Vport index. */
- if (ha->parent && ha->vp_idx != (mb[3] & 0xff))
+ if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
break;
-
DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): RSCN database changed -- %04x %04x %04x.\n",
- ha->host_no, mb[1], mb[2], mb[3]));
+ vha->host_no, mb[1], mb[2], mb[3]));
rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
- host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) |
- ha->d_id.b.al_pa;
+ host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
+ | vha->d_id.b.al_pa;
if (rscn_entry == host_pid) {
DEBUG(printk(KERN_INFO
"scsi(%ld): Ignoring RSCN update to local host "
"port ID (%06x)\n",
- ha->host_no, host_pid));
+ vha->host_no, host_pid));
break;
}
/* Ignore reserved bits from RSCN-payload. */
rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2];
- rscn_queue_index = ha->rscn_in_ptr + 1;
+ rscn_queue_index = vha->rscn_in_ptr + 1;
if (rscn_queue_index == MAX_RSCN_COUNT)
rscn_queue_index = 0;
- if (rscn_queue_index != ha->rscn_out_ptr) {
- ha->rscn_queue[ha->rscn_in_ptr] = rscn_entry;
- ha->rscn_in_ptr = rscn_queue_index;
+ if (rscn_queue_index != vha->rscn_out_ptr) {
+ vha->rscn_queue[vha->rscn_in_ptr] = rscn_entry;
+ vha->rscn_in_ptr = rscn_queue_index;
} else {
- ha->flags.rscn_queue_overflow = 1;
+ vha->flags.rscn_queue_overflow = 1;
}
- atomic_set(&ha->loop_state, LOOP_UPDATE);
- atomic_set(&ha->loop_down_timer, 0);
- ha->flags.management_server_logged_in = 0;
+ atomic_set(&vha->loop_state, LOOP_UPDATE);
+ atomic_set(&vha->loop_down_timer, 0);
+ vha->flags.management_server_logged_in = 0;
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- qla2x00_post_aen_work(ha, FCH_EVT_RSCN, rscn_entry);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry);
break;
/* case MBA_RIO_RESPONSE: */
case MBA_ZIO_RESPONSE:
DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n",
- ha->host_no));
+ vha->host_no));
DEBUG(printk(KERN_INFO
"scsi(%ld): [R|Z]IO update completion.\n",
- ha->host_no));
+ vha->host_no));
if (IS_FWI2_CAPABLE(ha))
- qla24xx_process_response_queue(ha);
+ qla24xx_process_response_queue(rsp);
else
- qla2x00_process_response_queue(ha);
+ qla2x00_process_response_queue(rsp);
break;
case MBA_DISCARD_RND_FRAME:
DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x "
- "%04x.\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "%04x.\n", vha->host_no, mb[1], mb[2], mb[3]));
break;
case MBA_TRACE_NOTIFICATION:
DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n",
- ha->host_no, mb[1], mb[2]));
+ vha->host_no, mb[1], mb[2]));
break;
case MBA_ISP84XX_ALERT:
DEBUG2(printk("scsi(%ld): ISP84XX Alert Notification -- "
- "%04x %04x %04x\n", ha->host_no, mb[1], mb[2], mb[3]));
+ "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3]));
spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
switch (mb[1]) {
@@ -682,16 +700,22 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb)
break;
}
- if (!ha->parent && ha->num_vhosts)
- qla2x00_alert_all_vps(ha, mb);
+ if (!vha->vp_idx && ha->num_vhosts)
+ qla2x00_alert_all_vps(rsp, mb);
}
static void
qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
{
fc_port_t *fcport = data;
+ struct scsi_qla_host *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = NULL;
- if (fcport->ha->max_q_depth <= sdev->queue_depth)
+ req = ha->req_q_map[vha->req_ques[0]];
+ if (!req)
+ return;
+ if (req->max_q_depth <= sdev->queue_depth)
return;
if (sdev->ordered_tags)
@@ -703,9 +727,9 @@ qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, void *data)
fcport->last_ramp_up = jiffies;
- DEBUG2(qla_printk(KERN_INFO, fcport->ha,
+ DEBUG2(qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n",
- fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
+ fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
sdev->queue_depth));
}
@@ -717,20 +741,21 @@ qla2x00_adjust_sdev_qdepth_down(struct scsi_device *sdev, void *data)
if (!scsi_track_queue_full(sdev, sdev->queue_depth - 1))
return;
- DEBUG2(qla_printk(KERN_INFO, fcport->ha,
+ DEBUG2(qla_printk(KERN_INFO, fcport->vha->hw,
"scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n",
- fcport->ha->host_no, sdev->channel, sdev->id, sdev->lun,
+ fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun,
sdev->queue_depth));
}
static inline void
-qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_ramp_up_queue_depth(scsi_qla_host_t *vha, struct req_que *req,
+ srb_t *sp)
{
fc_port_t *fcport;
struct scsi_device *sdev;
sdev = sp->cmd->device;
- if (sdev->queue_depth >= ha->max_q_depth)
+ if (sdev->queue_depth >= req->max_q_depth)
return;
fcport = sp->fcport;
@@ -751,25 +776,27 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp)
* @index: SRB index
*/
static void
-qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
+qla2x00_process_completed_request(struct scsi_qla_host *vha,
+ struct req_que *req, uint32_t index)
{
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
/* Validate handle. */
if (index >= MAX_OUTSTANDING_COMMANDS) {
DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n",
- ha->host_no, index));
+ vha->host_no, index));
qla_printk(KERN_WARNING, ha,
"Invalid SCSI completion handle %d.\n", index);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
return;
}
- sp = ha->outstanding_cmds[index];
+ sp = req->outstanding_cmds[index];
if (sp) {
/* Free outstanding command slot. */
- ha->outstanding_cmds[index] = NULL;
+ req->outstanding_cmds[index] = NULL;
CMD_COMPL_STATUS(sp->cmd) = 0L;
CMD_SCSI_STATUS(sp->cmd) = 0L;
@@ -777,15 +804,15 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
/* Save ISP completion status */
sp->cmd->result = DID_OK << 16;
- qla2x00_ramp_up_queue_depth(ha, sp);
+ qla2x00_ramp_up_queue_depth(vha, req, sp);
qla2x00_sp_compl(ha, sp);
} else {
DEBUG2(printk("scsi(%ld): Invalid ISP SCSI completion handle\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Invalid ISP SCSI completion handle\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
}
@@ -794,32 +821,36 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index)
* @ha: SCSI driver HA context
*/
void
-qla2x00_process_response_queue(struct scsi_qla_host *ha)
+qla2x00_process_response_queue(struct rsp_que *rsp)
{
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = rsp->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
sts_entry_t *pkt;
uint16_t handle_cnt;
uint16_t cnt;
- if (!ha->flags.online)
+ vha = qla2x00_get_rsp_host(rsp);
+
+ if (!vha->flags.online)
return;
- while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
- pkt = (sts_entry_t *)ha->response_ring_ptr;
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (sts_entry_t *)rsp->ring_ptr;
- ha->rsp_ring_index++;
- if (ha->rsp_ring_index == ha->response_q_length) {
- ha->rsp_ring_index = 0;
- ha->response_ring_ptr = ha->response_ring;
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
} else {
- ha->response_ring_ptr++;
+ rsp->ring_ptr++;
}
if (pkt->entry_status != 0) {
DEBUG3(printk(KERN_INFO
- "scsi(%ld): Process error entry.\n", ha->host_no));
+ "scsi(%ld): Process error entry.\n", vha->host_no));
- qla2x00_error_entry(ha, pkt);
+ qla2x00_error_entry(vha, rsp, pkt);
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
continue;
@@ -827,31 +858,31 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha)
switch (pkt->entry_type) {
case STATUS_TYPE:
- qla2x00_status_entry(ha, pkt);
+ qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_TYPE_21:
handle_cnt = ((sts21_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
- qla2x00_process_completed_request(ha,
+ qla2x00_process_completed_request(vha, rsp->req,
((sts21_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_TYPE_22:
handle_cnt = ((sts22_entry_t *)pkt)->handle_count;
for (cnt = 0; cnt < handle_cnt; cnt++) {
- qla2x00_process_completed_request(ha,
+ qla2x00_process_completed_request(vha, rsp->req,
((sts22_entry_t *)pkt)->handle[cnt]);
}
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
break;
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown response pkt type %x "
"entry status=%x.\n",
- ha->host_no, pkt->entry_type, pkt->entry_status));
+ vha->host_no, pkt->entry_type, pkt->entry_status));
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -859,7 +890,7 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha)
}
/* Adjust ring index */
- WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), ha->rsp_ring_index);
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), rsp->ring_index);
}
static inline void
@@ -881,10 +912,10 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
sp->request_sense_ptr += sense_len;
sp->request_sense_length -= sense_len;
if (sp->request_sense_length != 0)
- sp->fcport->ha->status_srb = sp;
+ sp->fcport->vha->status_srb = sp;
DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) "
- "cmd=%p pid=%ld\n", __func__, sp->fcport->ha->host_no,
+ "cmd=%p pid=%ld\n", __func__, sp->fcport->vha->host_no,
cp->device->channel, cp->device->id, cp->device->lun, cp,
cp->serial_number));
if (sense_len)
@@ -898,7 +929,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len)
* @pkt: Entry pointer
*/
static void
-qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
+qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
{
srb_t *sp;
fc_port_t *fcport;
@@ -911,6 +942,8 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
int32_t resid;
uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len;
uint8_t *rsp_info, *sense_data;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = rsp->req;
sts = (sts_entry_t *) pkt;
sts24 = (struct sts_entry_24xx *) pkt;
@@ -924,31 +957,31 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
/* Fast path completion. */
if (comp_status == CS_COMPLETE && scsi_status == 0) {
- qla2x00_process_completed_request(ha, sts->handle);
+ qla2x00_process_completed_request(vha, req, sts->handle);
return;
}
/* Validate handle. */
if (sts->handle < MAX_OUTSTANDING_COMMANDS) {
- sp = ha->outstanding_cmds[sts->handle];
- ha->outstanding_cmds[sts->handle] = NULL;
+ sp = req->outstanding_cmds[sts->handle];
+ req->outstanding_cmds[sts->handle] = NULL;
} else
sp = NULL;
if (sp == NULL) {
DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
return;
}
cp = sp->cmd;
if (cp == NULL) {
DEBUG2(printk("scsi(%ld): Command already returned back to OS "
- "pkt->handle=%d sp=%p.\n", ha->host_no, sts->handle, sp));
+ "pkt->handle=%d sp=%p.\n", vha->host_no, sts->handle, sp));
qla_printk(KERN_WARNING, ha,
"Command is NULL: already returned to OS (sp=%p)\n", sp);
@@ -987,7 +1020,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (rsp_info_len > 3 && rsp_info[3]) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol "
"failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..."
- "retrying command\n", ha->host_no,
+ "retrying command\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, rsp_info_len, rsp_info[0],
rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4],
@@ -1025,7 +1058,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
- "error status.\n", ha->host_no,
+ "error status.\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
scsi_bufflen(cp));
@@ -1039,7 +1072,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
- "0x%x-0x%x.\n", ha->host_no, comp_status,
+ "0x%x-0x%x.\n", vha->host_no, comp_status,
scsi_status));
/* Adjust queue depth for all luns on the port. */
@@ -1078,7 +1111,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d) UNDERRUN status detected "
"0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
- "os_underflow=0x%x\n", ha->host_no,
+ "os_underflow=0x%x\n", vha->host_no,
cp->device->id, cp->device->lun, comp_status,
scsi_status, resid_len, resid, cp->cmnd[0],
cp->underflow));
@@ -1095,7 +1128,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
- "0x%x-0x%x.\n", ha->host_no, comp_status,
+ "0x%x-0x%x.\n", vha->host_no, comp_status,
scsi_status));
/*
@@ -1125,10 +1158,10 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (!(scsi_status & SS_RESIDUAL_UNDER)) {
DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
"frame(s) detected (%x of %x bytes)..."
- "retrying command.\n", ha->host_no,
- cp->device->channel, cp->device->id,
- cp->device->lun, resid,
- scsi_bufflen(cp)));
+ "retrying command.\n",
+ vha->host_no, cp->device->channel,
+ cp->device->id, cp->device->lun, resid,
+ scsi_bufflen(cp)));
cp->result = DID_BUS_BUSY << 16;
break;
@@ -1140,7 +1173,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d:%d): Mid-layer underflow "
"detected (%x of %x bytes)...returning "
- "error status.\n", ha->host_no,
+ "error status.\n", vha->host_no,
cp->device->channel, cp->device->id,
cp->device->lun, resid,
scsi_bufflen(cp));
@@ -1157,7 +1190,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
case CS_DATA_OVERRUN:
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n",
- ha->host_no, cp->device->id, cp->device->lun, comp_status,
+ vha->host_no, cp->device->id, cp->device->lun, comp_status,
scsi_status));
DEBUG2(printk(KERN_INFO
"CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
@@ -1183,7 +1216,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down "
"pid=%ld, compl status=0x%x, port state=0x%x\n",
- ha->host_no, cp->device->id, cp->device->lun,
+ vha->host_no, cp->device->id, cp->device->lun,
cp->serial_number, comp_status,
atomic_read(&fcport->state)));
@@ -1194,13 +1227,13 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
cp->result = DID_TRANSPORT_DISRUPTED << 16;
if (atomic_read(&fcport->state) == FCS_ONLINE)
- qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1);
+ qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
break;
case CS_RESET:
DEBUG2(printk(KERN_INFO
"scsi(%ld): RESET status detected 0x%x-0x%x.\n",
- ha->host_no, comp_status, scsi_status));
+ vha->host_no, comp_status, scsi_status));
cp->result = DID_RESET << 16;
break;
@@ -1213,7 +1246,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
*/
DEBUG2(printk(KERN_INFO
"scsi(%ld): ABORT status detected 0x%x-0x%x.\n",
- ha->host_no, comp_status, scsi_status));
+ vha->host_no, comp_status, scsi_status));
cp->result = DID_RESET << 16;
break;
@@ -1229,25 +1262,25 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
if (IS_FWI2_CAPABLE(ha)) {
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected "
- "0x%x-0x%x\n", ha->host_no, cp->device->channel,
+ "0x%x-0x%x\n", vha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status,
scsi_status));
break;
}
DEBUG2(printk(KERN_INFO
"scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x "
- "sflags=%x.\n", ha->host_no, cp->device->channel,
+ "sflags=%x.\n", vha->host_no, cp->device->channel,
cp->device->id, cp->device->lun, comp_status, scsi_status,
le16_to_cpu(sts->status_flags)));
/* Check to see if logout occurred. */
if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT))
- qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1);
+ qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
break;
default:
DEBUG3(printk("scsi(%ld): Error detected (unknown status) "
- "0x%x-0x%x.\n", ha->host_no, comp_status, scsi_status));
+ "0x%x-0x%x.\n", vha->host_no, comp_status, scsi_status));
qla_printk(KERN_INFO, ha,
"Unknown status detected 0x%x-0x%x.\n",
comp_status, scsi_status);
@@ -1257,7 +1290,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
}
/* Place command on done queue. */
- if (ha->status_srb == NULL)
+ if (vha->status_srb == NULL)
qla2x00_sp_compl(ha, sp);
}
@@ -1269,10 +1302,11 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt)
* Extended sense data.
*/
static void
-qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
+qla2x00_status_cont_entry(scsi_qla_host_t *vha, sts_cont_entry_t *pkt)
{
uint8_t sense_sz = 0;
- srb_t *sp = ha->status_srb;
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp = vha->status_srb;
struct scsi_cmnd *cp;
if (sp != NULL && sp->request_sense_length != 0) {
@@ -1284,7 +1318,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
"cmd is NULL: already returned to OS (sp=%p)\n",
sp);
- ha->status_srb = NULL;
+ vha->status_srb = NULL;
return;
}
@@ -1305,7 +1339,7 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
/* Place command on done queue. */
if (sp->request_sense_length == 0) {
- ha->status_srb = NULL;
+ vha->status_srb = NULL;
qla2x00_sp_compl(ha, sp);
}
}
@@ -1317,10 +1351,11 @@ qla2x00_status_cont_entry(scsi_qla_host_t *ha, sts_cont_entry_t *pkt)
* @pkt: Entry pointer
*/
static void
-qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
+qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
{
srb_t *sp;
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = rsp->req;
#if defined(QL_DEBUG_LEVEL_2)
if (pkt->entry_status & RF_INV_E_ORDER)
qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__);
@@ -1339,13 +1374,13 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
/* Validate handle. */
if (pkt->handle < MAX_OUTSTANDING_COMMANDS)
- sp = ha->outstanding_cmds[pkt->handle];
+ sp = req->outstanding_cmds[pkt->handle];
else
sp = NULL;
if (sp) {
/* Free outstanding command slot. */
- ha->outstanding_cmds[pkt->handle] = NULL;
+ req->outstanding_cmds[pkt->handle] = NULL;
/* Bad payload or header */
if (pkt->entry_status &
@@ -1362,12 +1397,12 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
} else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type ==
COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7) {
DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Error entry - invalid handle\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
}
}
@@ -1377,10 +1412,11 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt)
* @mb0: Mailbox0 register
*/
static void
-qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
+qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
{
uint16_t cnt;
uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Load return mailbox registers. */
@@ -1395,10 +1431,10 @@ qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
if (ha->mcp) {
DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n",
- __func__, ha->host_no, ha->mcp->mb[0]));
+ __func__, vha->host_no, ha->mcp->mb[0]));
} else {
DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
}
}
@@ -1407,30 +1443,33 @@ qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0)
* @ha: SCSI driver HA context
*/
void
-qla24xx_process_response_queue(struct scsi_qla_host *ha)
+qla24xx_process_response_queue(struct rsp_que *rsp)
{
- struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ struct qla_hw_data *ha = rsp->hw;
struct sts_entry_24xx *pkt;
+ struct scsi_qla_host *vha;
+
+ vha = qla2x00_get_rsp_host(rsp);
- if (!ha->flags.online)
+ if (!vha->flags.online)
return;
- while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) {
- pkt = (struct sts_entry_24xx *)ha->response_ring_ptr;
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
- ha->rsp_ring_index++;
- if (ha->rsp_ring_index == ha->response_q_length) {
- ha->rsp_ring_index = 0;
- ha->response_ring_ptr = ha->response_ring;
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
} else {
- ha->response_ring_ptr++;
+ rsp->ring_ptr++;
}
if (pkt->entry_status != 0) {
DEBUG3(printk(KERN_INFO
- "scsi(%ld): Process error entry.\n", ha->host_no));
+ "scsi(%ld): Process error entry.\n", vha->host_no));
- qla2x00_error_entry(ha, (sts_entry_t *) pkt);
+ qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
wmb();
continue;
@@ -1438,13 +1477,13 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
switch (pkt->entry_type) {
case STATUS_TYPE:
- qla2x00_status_entry(ha, pkt);
+ qla2x00_status_entry(vha, rsp, pkt);
break;
case STATUS_CONT_TYPE:
- qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt);
+ qla2x00_status_cont_entry(vha, (sts_cont_entry_t *)pkt);
break;
case VP_RPT_ID_IOCB_TYPE:
- qla24xx_report_id_acquisition(ha,
+ qla24xx_report_id_acquisition(vha,
(struct vp_rpt_id_entry_24xx *)pkt);
break;
default:
@@ -1452,7 +1491,7 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
DEBUG4(printk(KERN_WARNING
"scsi(%ld): Received unknown response pkt type %x "
"entry status=%x.\n",
- ha->host_no, pkt->entry_type, pkt->entry_status));
+ vha->host_no, pkt->entry_type, pkt->entry_status));
break;
}
((response_t *)pkt)->signature = RESPONSE_PROCESSED;
@@ -1460,14 +1499,15 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha)
}
/* Adjust ring index */
- WRT_REG_DWORD(&reg->rsp_q_out, ha->rsp_ring_index);
+ ha->isp_ops->wrt_rsp_reg(ha, rsp->id, rsp->ring_index);
}
static void
-qla2xxx_check_risc_status(scsi_qla_host_t *ha)
+qla2xxx_check_risc_status(scsi_qla_host_t *vha)
{
int rval;
uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
if (!IS_QLA25XX(ha))
@@ -1521,25 +1561,29 @@ done:
irqreturn_t
qla24xx_intr_handler(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
struct device_reg_24xx __iomem *reg;
int status;
unsigned long iter;
uint32_t stat;
uint32_t hccr;
uint16_t mb[4];
+ struct rsp_que *rsp;
- ha = (scsi_qla_host_t *) dev_id;
- if (!ha) {
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
printk(KERN_INFO
- "%s(): NULL host pointer\n", __func__);
+ "%s(): NULL response queue pointer\n", __func__);
return IRQ_NONE;
}
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
status = 0;
spin_lock(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
for (iter = 50; iter--; ) {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1547,7 +1591,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
break;
if (ha->hw_event_pause_errors == 0)
- qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR,
+ qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR,
0, MSW(stat), LSW(stat));
else if (ha->hw_event_pause_errors < 0xffffffff)
ha->hw_event_pause_errors++;
@@ -1557,10 +1601,10 @@ qla24xx_intr_handler(int irq, void *dev_id)
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
"Dumping firmware!\n", hccr);
- qla2xxx_check_risc_status(ha);
+ qla2xxx_check_risc_status(vha);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSRX_RISC_INT) == 0)
break;
@@ -1570,7 +1614,7 @@ qla24xx_intr_handler(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla24xx_mbx_completion(ha, MSW(stat));
+ qla24xx_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
break;
@@ -1579,15 +1623,16 @@ qla24xx_intr_handler(int irq, void *dev_id)
mb[1] = RD_REG_WORD(&reg->mailbox1);
mb[2] = RD_REG_WORD(&reg->mailbox2);
mb[3] = RD_REG_WORD(&reg->mailbox3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla24xx_process_response_queue(ha);
+ case 0x14:
+ qla24xx_process_response_queue(rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -1607,15 +1652,22 @@ qla24xx_intr_handler(int irq, void *dev_id)
static irqreturn_t
qla24xx_msix_rsp_q(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
struct device_reg_24xx __iomem *reg;
- ha = dev_id;
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
spin_lock_irq(&ha->hardware_lock);
- qla24xx_process_response_queue(ha);
+ qla24xx_process_response_queue(rsp);
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
spin_unlock_irq(&ha->hardware_lock);
@@ -1624,20 +1676,64 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
}
static irqreturn_t
+qla25xx_msix_rsp_q(int irq, void *dev_id)
+{
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_24xx __iomem *reg;
+ uint16_t msix_disabled_hccr = 0;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+ reg = &ha->iobase->isp24;
+
+ spin_lock_irq(&ha->hardware_lock);
+
+ msix_disabled_hccr = rsp->options;
+ if (!rsp->id)
+ msix_disabled_hccr &= __constant_cpu_to_le32(BIT_22);
+ else
+ msix_disabled_hccr &= __constant_cpu_to_le32(BIT_6);
+
+ qla24xx_process_response_queue(rsp);
+
+ if (!msix_disabled_hccr)
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+
+ spin_unlock_irq(&ha->hardware_lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
qla24xx_msix_default(int irq, void *dev_id)
{
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
struct device_reg_24xx __iomem *reg;
int status;
uint32_t stat;
uint32_t hccr;
uint16_t mb[4];
- ha = dev_id;
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ DEBUG(printk(
+ "%s(): NULL response queue pointer\n", __func__));
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
reg = &ha->iobase->isp24;
status = 0;
spin_lock_irq(&ha->hardware_lock);
+ vha = qla2x00_get_rsp_host(rsp);
do {
stat = RD_REG_DWORD(&reg->host_status);
if (stat & HSRX_RISC_PAUSED) {
@@ -1645,7 +1741,7 @@ qla24xx_msix_default(int irq, void *dev_id)
break;
if (ha->hw_event_pause_errors == 0)
- qla2x00_post_hwe_work(ha, HW_EVENT_PARITY_ERR,
+ qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR,
0, MSW(stat), LSW(stat));
else if (ha->hw_event_pause_errors < 0xffffffff)
ha->hw_event_pause_errors++;
@@ -1655,10 +1751,10 @@ qla24xx_msix_default(int irq, void *dev_id)
qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, "
"Dumping firmware!\n", hccr);
- qla2xxx_check_risc_status(ha);
+ qla2xxx_check_risc_status(vha);
- ha->isp_ops->fw_dump(ha, 1);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
} else if ((stat & HSRX_RISC_INT) == 0)
break;
@@ -1668,7 +1764,7 @@ qla24xx_msix_default(int irq, void *dev_id)
case 0x2:
case 0x10:
case 0x11:
- qla24xx_mbx_completion(ha, MSW(stat));
+ qla24xx_mbx_completion(vha, MSW(stat));
status |= MBX_INTERRUPT;
break;
@@ -1677,15 +1773,16 @@ qla24xx_msix_default(int irq, void *dev_id)
mb[1] = RD_REG_WORD(&reg->mailbox1);
mb[2] = RD_REG_WORD(&reg->mailbox2);
mb[3] = RD_REG_WORD(&reg->mailbox3);
- qla2x00_async_event(ha, mb);
+ qla2x00_async_event(vha, rsp, mb);
break;
case 0x13:
- qla24xx_process_response_queue(ha);
+ case 0x14:
+ qla24xx_process_response_queue(rsp);
break;
default:
DEBUG2(printk("scsi(%ld): Unrecognized interrupt type "
"(%d).\n",
- ha->host_no, stat & 0xff));
+ vha->host_no, stat & 0xff));
break;
}
WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
@@ -1710,70 +1807,138 @@ struct qla_init_msix_entry {
irq_handler_t handler;
};
-static struct qla_init_msix_entry imsix_entries[QLA_MSIX_ENTRIES] = {
- { QLA_MSIX_DEFAULT, QLA_MIDX_DEFAULT,
- "qla2xxx (default)", qla24xx_msix_default },
+static struct qla_init_msix_entry base_queue = {
+ .entry = 0,
+ .index = 0,
+ .name = "qla2xxx (default)",
+ .handler = qla24xx_msix_default,
+};
+
+static struct qla_init_msix_entry base_rsp_queue = {
+ .entry = 1,
+ .index = 1,
+ .name = "qla2xxx (rsp_q)",
+ .handler = qla24xx_msix_rsp_q,
+};
- { QLA_MSIX_RSP_Q, QLA_MIDX_RSP_Q,
- "qla2xxx (rsp_q)", qla24xx_msix_rsp_q },
+static struct qla_init_msix_entry multi_rsp_queue = {
+ .entry = 1,
+ .index = 1,
+ .name = "qla2xxx (multi_q)",
+ .handler = qla25xx_msix_rsp_q,
};
static void
-qla24xx_disable_msix(scsi_qla_host_t *ha)
+qla24xx_disable_msix(struct qla_hw_data *ha)
{
int i;
struct qla_msix_entry *qentry;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++) {
- qentry = &ha->msix_entries[imsix_entries[i].index];
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
if (qentry->have_irq)
- free_irq(qentry->msix_vector, ha);
+ free_irq(qentry->vector, qentry->rsp);
}
pci_disable_msix(ha->pdev);
+ kfree(ha->msix_entries);
+ ha->msix_entries = NULL;
+ ha->flags.msix_enabled = 0;
}
static int
-qla24xx_enable_msix(scsi_qla_host_t *ha)
+qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
{
int i, ret;
- struct msix_entry entries[QLA_MSIX_ENTRIES];
+ struct msix_entry *entries;
struct qla_msix_entry *qentry;
+ struct qla_init_msix_entry *msix_queue;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++)
- entries[i].entry = imsix_entries[i].entry;
+ entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
+ GFP_KERNEL);
+ if (!entries)
+ return -ENOMEM;
- ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries));
+ for (i = 0; i < ha->msix_count; i++)
+ entries[i].entry = i;
+
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
if (ret) {
qla_printk(KERN_WARNING, ha,
- "MSI-X: Failed to enable support -- %d/%d\n",
- QLA_MSIX_ENTRIES, ret);
+ "MSI-X: Failed to enable support -- %d/%d\n"
+ " Retry with %d vectors\n", ha->msix_count, ret, ret);
+ ha->msix_count = ret;
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable"
+ " support, giving up -- %d/%d\n",
+ ha->msix_count, ret);
+ goto msix_out;
+ }
+ ha->max_queues = ha->msix_count - 1;
+ }
+ ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
+ ha->msix_count, GFP_KERNEL);
+ if (!ha->msix_entries) {
+ ret = -ENOMEM;
goto msix_out;
}
ha->flags.msix_enabled = 1;
- for (i = 0; i < QLA_MSIX_ENTRIES; i++) {
- qentry = &ha->msix_entries[imsix_entries[i].index];
- qentry->msix_vector = entries[i].vector;
- qentry->msix_entry = entries[i].entry;
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
+ qentry->vector = entries[i].vector;
+ qentry->entry = entries[i].entry;
qentry->have_irq = 0;
- ret = request_irq(qentry->msix_vector,
- imsix_entries[i].handler, 0, imsix_entries[i].name, ha);
- if (ret) {
- qla_printk(KERN_WARNING, ha,
- "MSI-X: Unable to register handler -- %x/%d.\n",
- imsix_entries[i].index, ret);
- qla24xx_disable_msix(ha);
- goto msix_out;
- }
- qentry->have_irq = 1;
+ qentry->rsp = NULL;
+ }
+
+ /* Enable MSI-X for AENs for queue 0 */
+ qentry = &ha->msix_entries[0];
+ ret = request_irq(qentry->vector, base_queue.handler, 0,
+ base_queue.name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ qentry->vector, ret);
+ qla24xx_disable_msix(ha);
+ goto msix_out;
}
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
+
+ /* Enable MSI-X vector for response queue update for queue 0 */
+ if (ha->max_queues > 1 && ha->mqiobase) {
+ ha->mqenable = 1;
+ msix_queue = &multi_rsp_queue;
+ qla_printk(KERN_INFO, ha,
+ "MQ enabled, Number of Queue Resources: %d \n",
+ ha->max_queues);
+ } else {
+ ha->mqenable = 0;
+ msix_queue = &base_rsp_queue;
+ }
+
+ qentry = &ha->msix_entries[1];
+ ret = request_irq(qentry->vector, msix_queue->handler, 0,
+ msix_queue->name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ qentry->vector, ret);
+ qla24xx_disable_msix(ha);
+ ha->mqenable = 0;
+ goto msix_out;
+ }
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
msix_out:
+ kfree(entries);
return ret;
}
int
-qla2x00_request_irqs(scsi_qla_host_t *ha)
+qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
{
int ret;
device_reg_t __iomem *reg = ha->iobase;
@@ -1782,11 +1947,11 @@ qla2x00_request_irqs(scsi_qla_host_t *ha)
if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha))
goto skip_msix;
- if (IS_QLA2432(ha) && (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX ||
- !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) {
+ if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX ||
+ !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) {
DEBUG2(qla_printk(KERN_WARNING, ha,
- "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n",
- ha->chip_revision, ha->fw_attributes));
+ "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n",
+ ha->pdev->revision, ha->fw_attributes));
goto skip_msix;
}
@@ -1803,7 +1968,7 @@ qla2x00_request_irqs(scsi_qla_host_t *ha)
goto skip_msi;
}
- ret = qla24xx_enable_msix(ha);
+ ret = qla24xx_enable_msix(ha, rsp);
if (!ret) {
DEBUG2(qla_printk(KERN_INFO, ha,
"MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision,
@@ -1825,7 +1990,7 @@ skip_msix:
skip_msi:
ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler,
- IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha);
+ IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, rsp);
if (ret) {
qla_printk(KERN_WARNING, ha,
"Failed to reserve interrupt %d already in use.\n",
@@ -1833,10 +1998,8 @@ skip_msi:
goto fail;
}
ha->flags.inta_enabled = 1;
- ha->host->irq = ha->pdev->irq;
clear_risc_ints:
- ha->isp_ops->disable_intrs(ha);
spin_lock_irq(&ha->hardware_lock);
if (IS_FWI2_CAPABLE(ha)) {
WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_HOST_INT);
@@ -1853,13 +2016,74 @@ fail:
}
void
-qla2x00_free_irqs(scsi_qla_host_t *ha)
+qla2x00_free_irqs(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
+ struct rsp_que *rsp = ha->rsp_q_map[0];
if (ha->flags.msix_enabled)
qla24xx_disable_msix(ha);
else if (ha->flags.inta_enabled) {
- free_irq(ha->host->irq, ha);
+ free_irq(ha->pdev->irq, rsp);
pci_disable_msi(ha->pdev);
}
}
+
+static struct scsi_qla_host *
+qla2x00_get_rsp_host(struct rsp_que *rsp)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = rsp->hw;
+ struct scsi_qla_host *vha = NULL;
+ struct sts_entry_24xx *pkt;
+ struct req_que *req;
+
+ if (rsp->id) {
+ pkt = (struct sts_entry_24xx *) rsp->ring_ptr;
+ req = rsp->req;
+ if (pkt && pkt->handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = req->outstanding_cmds[pkt->handle];
+ if (sp)
+ vha = sp->vha;
+ }
+ }
+ if (!vha)
+ /* handle it in base queue */
+ vha = pci_get_drvdata(ha->pdev);
+
+ return vha;
+}
+
+int qla25xx_request_irq(struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = rsp->hw;
+ struct qla_init_msix_entry *intr = &multi_rsp_queue;
+ struct qla_msix_entry *msix = rsp->msix;
+ int ret;
+
+ ret = request_irq(msix->vector, intr->handler, 0, intr->name, rsp);
+ if (ret) {
+ qla_printk(KERN_WARNING, ha,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ msix->vector, ret);
+ return ret;
+ }
+ msix->have_irq = 1;
+ msix->rsp = rsp;
+ return ret;
+}
+
+void
+qla25xx_wrt_rsp_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->mqiobase + QLA_QUE_PAGE * id;
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_out, index);
+}
+
+void
+qla24xx_wrt_rsp_reg(struct qla_hw_data *ha, uint16_t id, uint16_t index)
+{
+ device_reg_t __iomem *reg = (void *) ha->iobase;
+ WRT_REG_DWORD(&reg->isp24.rsp_q_out, index);
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 3402746ec128..a99976f5fabd 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -29,7 +29,7 @@
* Kernel context.
*/
static int
-qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
+qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
{
int rval;
unsigned long flags = 0;
@@ -42,15 +42,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
uint32_t cnt;
uint32_t mboxes;
unsigned long wait_time;
- scsi_qla_host_t *ha = to_qla_parent(pvha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
reg = ha->iobase;
- io_lock_on = ha->flags.init_done;
+ io_lock_on = base_vha->flags.init_done;
rval = QLA_SUCCESS;
- abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
- DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, base_vha->host_no));
/*
* Wait for active mailbox commands to finish by waiting at most tov
@@ -62,7 +63,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
mcp->tov * HZ)) {
/* Timeout occurred. Return error. */
DEBUG2_3_11(printk("%s(%ld): cmd access timeout. "
- "Exiting.\n", __func__, ha->host_no));
+ "Exiting.\n", __func__, base_vha->host_no));
return QLA_FUNCTION_TIMEOUT;
}
}
@@ -72,7 +73,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
ha->mcp = mcp;
DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n",
- ha->host_no, mcp->mb[0]));
+ base_vha->host_no, mcp->mb[0]));
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -100,15 +101,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
#if defined(QL_DEBUG_LEVEL_1)
printk("%s(%ld): Loaded MBX registers (displayed in bytes) = \n",
- __func__, ha->host_no);
+ __func__, base_vha->host_no);
qla2x00_dump_buffer((uint8_t *)mcp->mb, 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16);
printk("\n");
qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8);
printk("\n");
- printk("%s(%ld): I/O address = %p.\n", __func__, ha->host_no, optr);
- qla2x00_dump_regs(ha);
+ printk("%s(%ld): I/O address = %p.\n", __func__, base_vha->host_no,
+ optr);
+ qla2x00_dump_regs(base_vha);
#endif
/* Issue set host interrupt command to send cmd out. */
@@ -117,7 +119,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
/* Unlock mbx registers and wait for interrupt */
DEBUG11(printk("%s(%ld): going to unlock irq & waiting for interrupt. "
- "jiffies=%lx.\n", __func__, ha->host_no, jiffies));
+ "jiffies=%lx.\n", __func__, base_vha->host_no, jiffies));
/* Wait for mbx cmd completion until timeout */
@@ -137,7 +139,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
} else {
DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__,
- ha->host_no, command));
+ base_vha->host_no, command));
if (IS_FWI2_CAPABLE(ha))
WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_SET_HOST_INT);
@@ -151,7 +153,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
break;
/* Check for pending interrupts. */
- qla2x00_poll(ha);
+ qla2x00_poll(ha->rsp_q_map[0]);
if (command != MBC_LOAD_RISC_RAM_EXTENDED &&
!ha->flags.mbox_int)
@@ -164,7 +166,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
uint16_t *iptr2;
DEBUG3_11(printk("%s(%ld): cmd %x completed.\n", __func__,
- ha->host_no, command));
+ base_vha->host_no, command));
/* Got interrupt. Clear the flag. */
ha->flags.mbox_int = 0;
@@ -200,12 +202,12 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
ictrl = RD_REG_WORD(&reg->isp.ictrl);
}
printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n",
- __func__, ha->host_no, command);
+ __func__, base_vha->host_no, command);
printk("%s(%ld): icontrol=%x jiffies=%lx\n", __func__,
- ha->host_no, ictrl, jiffies);
+ base_vha->host_no, ictrl, jiffies);
printk("%s(%ld): *** mailbox[0] = 0x%x ***\n", __func__,
- ha->host_no, mb0);
- qla2x00_dump_regs(ha);
+ base_vha->host_no, mb0);
+ qla2x00_dump_regs(base_vha);
#endif
rval = QLA_FUNCTION_TIMEOUT;
@@ -218,10 +220,10 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (abort_active || !io_lock_on) {
DEBUG11(printk("%s(%ld): checking for additional resp "
- "interrupt.\n", __func__, ha->host_no));
+ "interrupt.\n", __func__, base_vha->host_no));
/* polling mode for non isp_abort commands. */
- qla2x00_poll(ha);
+ qla2x00_poll(ha->rsp_q_map[0]);
}
if (rval == QLA_FUNCTION_TIMEOUT &&
@@ -229,35 +231,37 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (!io_lock_on || (mcp->flags & IOCTL_CMD)) {
/* not in dpc. schedule it for dpc to take over. */
DEBUG(printk("%s(%ld): timeout schedule "
- "isp_abort_needed.\n", __func__, ha->host_no));
+ "isp_abort_needed.\n", __func__,
+ base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): timeout schedule "
- "isp_abort_needed.\n", __func__, ha->host_no));
+ "isp_abort_needed.\n", __func__,
+ base_vha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occurred. Scheduling ISP "
"abort.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
} else if (!abort_active) {
/* call abort directly since we are in the DPC thread */
DEBUG(printk("%s(%ld): timeout calling abort_isp\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): timeout calling "
- "abort_isp\n", __func__, ha->host_no));
+ "abort_isp\n", __func__, base_vha->host_no));
qla_printk(KERN_WARNING, ha,
"Mailbox command timeout occurred. Issuing ISP "
"abort.\n");
- set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- if (qla2x00_abort_isp(ha)) {
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha)) {
/* Failed. retry later. */
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
}
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
DEBUG(printk("%s(%ld): finished abort_isp\n", __func__,
- ha->host_no));
+ base_vha->host_no));
DEBUG2_3_11(printk("%s(%ld): finished abort_isp\n",
- __func__, ha->host_no));
+ __func__, base_vha->host_no));
}
}
@@ -267,24 +271,26 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp)
if (rval) {
DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, "
- "mbx2=%x, cmd=%x ****\n", __func__, ha->host_no,
+ "mbx2=%x, cmd=%x ****\n", __func__, base_vha->host_no,
mcp->mb[0], mcp->mb[1], mcp->mb[2], command));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__,
+ base_vha->host_no));
}
return rval;
}
int
-qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
+qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
uint32_t risc_code_size)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
@@ -312,13 +318,13 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -340,13 +346,14 @@ qla2x00_load_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t risc_addr,
* Kernel context.
*/
int
-qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
+qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
mcp->out_mb = MBX_0;
@@ -369,18 +376,18 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
if (IS_FWI2_CAPABLE(ha)) {
DEBUG11(printk("%s(%ld): done exchanges=%x.\n",
- __func__, ha->host_no, mcp->mb[1]));
+ __func__, vha->host_no, mcp->mb[1]));
} else {
DEBUG11(printk("%s(%ld): done.\n", __func__,
- ha->host_no));
+ vha->host_no));
}
}
@@ -404,28 +411,28 @@ qla2x00_execute_fw(scsi_qla_host_t *ha, uint32_t risc_addr)
* Kernel context.
*/
void
-qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
+qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
uint16_t *subminor, uint16_t *attributes, uint32_t *memory)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->flags = 0;
mcp->tov = MBX_TOV_SECONDS;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox data. */
*major = mcp->mb[1];
*minor = mcp->mb[2];
*subminor = mcp->mb[3];
*attributes = mcp->mb[6];
- if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ if (IS_QLA2100(vha->hw) || IS_QLA2200(vha->hw))
*memory = 0x1FFFF; /* Defaults to 128KB. */
else
*memory = (mcp->mb[5] << 16) | mcp->mb[4];
@@ -433,10 +440,10 @@ qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
}
@@ -455,32 +462,32 @@ qla2x00_get_fw_version(scsi_qla_host_t *ha, uint16_t *major, uint16_t *minor,
* Kernel context.
*/
int
-qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
fwopts[0] = mcp->mb[0];
fwopts[1] = mcp->mb[1];
fwopts[2] = mcp->mb[2];
fwopts[3] = mcp->mb[3];
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -502,13 +509,13 @@ qla2x00_get_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
* Kernel context.
*/
int
-qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
+qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
mcp->mb[1] = fwopts[1];
@@ -516,7 +523,7 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
mcp->mb[3] = fwopts[3];
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->in_mb |= MBX_1;
} else {
mcp->mb[10] = fwopts[10];
@@ -526,17 +533,17 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
}
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
fwopts[0] = mcp->mb[0];
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x/%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -558,13 +565,14 @@ qla2x00_set_fw_options(scsi_qla_host_t *ha, uint16_t *fwopts)
* Kernel context.
*/
int
-qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
+qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", vha->host_no));
mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
mcp->mb[1] = 0xAAAA;
@@ -578,7 +586,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 ||
@@ -591,7 +599,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
struct device_reg_24xx __iomem *reg =
&ha->iobase->isp24;
- qla2xxx_hw_event_log(ha, HW_EVENT_ISP_ERR, 0,
+ qla2xxx_hw_event_log(vha, HW_EVENT_ISP_ERR, 0,
LSW(RD_REG_DWORD(&reg->hccr)),
LSW(RD_REG_DWORD(&reg->istatus)));
}
@@ -600,11 +608,11 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -626,18 +634,18 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
+qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_VERIFY_CHECKSUM;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[1] = MSW(risc_addr);
mcp->mb[2] = LSW(risc_addr);
mcp->out_mb |= MBX_2|MBX_1;
@@ -650,14 +658,14 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__,
- ha->host_no, rval, IS_FWI2_CAPABLE(ha) ?
+ vha->host_no, rval, IS_FWI2_CAPABLE(vha->hw) ?
(mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -682,7 +690,7 @@ qla2x00_verify_checksum(scsi_qla_host_t *ha, uint32_t risc_addr)
* Kernel context.
*/
static int
-qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer,
+qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
dma_addr_t phys_addr, size_t size, uint32_t tov)
{
int rval;
@@ -699,30 +707,28 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *ha, void *buffer,
mcp->in_mb = MBX_2|MBX_0;
mcp->tov = tov;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n",
- ha->host_no, rval));
- DEBUG2(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
sts_entry_t *sts_entry = (sts_entry_t *) buffer;
/* Mask reserved bits. */
sts_entry->entry_status &=
- IS_FWI2_CAPABLE(ha) ? RF_MASK_24XX :RF_MASK;
+ IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
}
return rval;
}
int
-qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr,
+qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr,
size_t size)
{
- return qla2x00_issue_iocb_timeout(ha, buffer, phys_addr, size,
+ return qla2x00_issue_iocb_timeout(vha, buffer, phys_addr, size,
MBX_TOV_SECONDS);
}
@@ -741,22 +747,23 @@ qla2x00_issue_iocb(scsi_qla_host_t *ha, void *buffer, dma_addr_t phys_addr,
* Kernel context.
*/
int
-qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
{
unsigned long flags = 0;
fc_port_t *fcport;
int rval;
- uint32_t handle;
+ uint32_t handle = 0;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no));
fcport = sp->fcport;
spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
- if (ha->outstanding_cmds[handle] == sp)
+ if (req->outstanding_cmds[handle] == sp)
break;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -778,14 +785,14 @@ qla2x00_abort_command(scsi_qla_host_t *ha, srb_t *sp)
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
DEBUG11(printk("qla2x00_abort_command(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -797,40 +804,45 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l)
int rval, rval2;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
l = l;
- ha = fcport->ha;
+ vha = fcport->vha;
+ req = vha->hw->req_q_map[0];
+ rsp = vha->hw->rsp_q_map[0];
mcp->mb[0] = MBC_ABORT_TARGET;
mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = fcport->loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = fcport->loop_id << 8;
}
- mcp->mb[2] = ha->loop_reset_delay;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[2] = vha->hw->loop_reset_delay;
+ mcp->mb[9] = vha->vp_idx;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, 0, MK_SYNC_ID);
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0,
+ MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -842,37 +854,42 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
int rval, rval2;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
- ha = fcport->ha;
+ vha = fcport->vha;
+ req = vha->hw->req_q_map[0];
+ rsp = vha->hw->rsp_q_map[0];
mcp->mb[0] = MBC_LUN_RESET;
mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha))
+ if (HAS_EXTENDED_IDS(vha->hw))
mcp->mb[1] = fcport->loop_id;
else
mcp->mb[1] = fcport->loop_id << 8;
mcp->mb[2] = l;
mcp->mb[3] = 0;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, l, MK_SYNC_ID_LUN);
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ MK_SYNC_ID_LUN);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -899,7 +916,7 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l)
* Kernel context.
*/
int
-qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
+qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap)
{
int rval;
@@ -907,15 +924,15 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_0;
mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (mcp->mb[0] == MBS_COMMAND_ERROR)
rval = QLA_COMMAND_ERROR;
else if (mcp->mb[0] == MBS_INVALID_COMMAND)
@@ -932,11 +949,11 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -958,7 +975,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa,
* Kernel context.
*/
int
-qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
+qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
uint16_t *r_a_tov)
{
int rval;
@@ -967,19 +984,19 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_RETRY_COUNT;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n",
- ha->host_no, mcp->mb[0]));
+ vha->host_no, mcp->mb[0]));
} else {
/* Convert returned data and check our values. */
*r_a_tov = mcp->mb[3] / 2;
@@ -991,7 +1008,7 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
}
DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d "
- "ratov=%d.\n", ha->host_no, mcp->mb[3], ratov));
+ "ratov=%d.\n", vha->host_no, mcp->mb[3], ratov));
}
return rval;
@@ -1015,14 +1032,15 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *ha, uint8_t *retry_cnt, uint8_t *tov,
* Kernel context.
*/
int
-qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
+qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
if (ha->flags.npiv_supported)
mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE;
@@ -1040,17 +1058,17 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
mcp->buf_size = size;
mcp->flags = MBX_DMA_OUT;
mcp->tov = MBX_TOV_SECONDS;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x "
"mb0=%x.\n",
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1073,7 +1091,7 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size)
* Kernel context.
*/
int
-qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
+qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
@@ -1081,14 +1099,15 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
port_database_t *pd;
struct port_database_24xx *pd24;
dma_addr_t pd_dma;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
pd24 = NULL;
pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
if (pd == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Port Database "
- "structure.\n", __func__, ha->host_no));
+ "structure.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE));
@@ -1100,7 +1119,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
mcp->mb[3] = LSW(pd_dma);
mcp->mb[6] = MSW(MSD(pd_dma));
mcp->mb[7] = LSW(MSD(pd_dma));
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_0;
if (IS_FWI2_CAPABLE(ha)) {
@@ -1120,7 +1139,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS)
goto gpd_error_out;
@@ -1132,7 +1151,7 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt)
pd24->last_login_state != PDS_PRLI_COMPLETE) {
DEBUG2(printk("%s(%ld): Unable to verify "
"login-state (%x/%x) for loop_id %x\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
pd24->current_login_state,
pd24->last_login_state, fcport->loop_id));
rval = QLA_FUNCTION_FAILED;
@@ -1192,9 +1211,9 @@ gpd_error_out:
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -1217,21 +1236,21 @@ gpd_error_out:
* Kernel context.
*/
int
-qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
+qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return firmware states. */
states[0] = mcp->mb[1];
@@ -1241,11 +1260,11 @@ qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): "
- "failed=%x.\n", ha->host_no, rval));
+ "failed=%x.\n", vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1270,7 +1289,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *ha, uint16_t *states)
* Kernel context.
*/
int
-qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
+qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
uint8_t opt)
{
int rval;
@@ -1278,12 +1297,12 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_GET_PORT_NAME;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = opt;
mcp->out_mb |= MBX_10;
@@ -1294,12 +1313,12 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
if (name != NULL) {
/* This function returns name in big endian. */
@@ -1314,7 +1333,7 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
}
DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1336,45 +1355,45 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name,
* Kernel context.
*/
int
-qla2x00_lip_reset(scsi_qla_host_t *ha)
+qla2x00_lip_reset(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
mcp->mb[1] = BIT_6;
mcp->mb[2] = 0;
- mcp->mb[3] = ha->loop_reset_delay;
+ mcp->mb[3] = vha->hw->loop_reset_delay;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
} else {
mcp->mb[0] = MBC_LIP_RESET;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = 0x00ff;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
} else {
mcp->mb[1] = 0xff00;
}
- mcp->mb[2] = ha->loop_reset_delay;
+ mcp->mb[2] = vha->hw->loop_reset_delay;
mcp->mb[3] = 0;
}
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n",
- __func__, ha->host_no, rval));
+ __func__, vha->host_no, rval));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -1399,7 +1418,7 @@ qla2x00_lip_reset(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
+qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
uint16_t cmd_size, size_t buf_size)
{
int rval;
@@ -1407,10 +1426,11 @@ qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total "
- "tov=%d.\n", ha->retry_count, ha->login_timeout, mcp->tov));
+ "tov=%d.\n", vha->hw->retry_count, vha->hw->login_timeout,
+ mcp->tov));
mcp->mb[0] = MBC_SEND_SNS_COMMAND;
mcp->mb[1] = cmd_size;
@@ -1422,25 +1442,25 @@ qla2x00_send_sns(scsi_qla_host_t *ha, dma_addr_t sns_phys_address,
mcp->in_mb = MBX_0|MBX_1;
mcp->buf_size = buf_size;
mcp->flags = MBX_DMA_OUT|MBX_DMA_IN;
- mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ mcp->tov = (vha->hw->login_timeout * 2) + (vha->hw->login_timeout / 2);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
- "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x "
- "mb[1]=%x.\n", ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
/*EMPTY*/
- DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", vha->host_no));
}
return rval;
}
int
-qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
@@ -1448,13 +1468,14 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
uint32_t iop[2];
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
if (lg == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1470,14 +1491,14 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = ha->vp_idx;
- rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
+ lg->vp_index = vha->vp_idx;
+ rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB "
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, vha->host_no, rval));
} else if (lg->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
lg->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
@@ -1486,7 +1507,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x) ioparam=%x/%x.\n", __func__,
- ha->host_no, le16_to_cpu(lg->comp_status), iop[0],
+ vha->host_no, le16_to_cpu(lg->comp_status), iop[0],
iop[1]));
switch (iop[0]) {
@@ -1515,7 +1536,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
break;
}
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
iop[0] = le32_to_cpu(lg->io_parameter[0]);
@@ -1562,14 +1583,15 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", ha->host_no));
+ DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", vha->host_no));
mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
@@ -1586,7 +1608,7 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb != NULL) {
@@ -1613,12 +1635,12 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x "
- "mb[0]=%x mb[1]=%x mb[2]=%x.\n", ha->host_no, rval,
+ "mb[0]=%x mb[1]=%x mb[2]=%x.\n", vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[2]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1641,19 +1663,20 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
*
*/
int
-qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
uint16_t *mb_ret, uint8_t opt)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
if (IS_FWI2_CAPABLE(ha))
- return qla24xx_login_fabric(ha, fcport->loop_id,
+ return qla24xx_login_fabric(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, mb_ret, opt);
- DEBUG3(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG3(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
if (HAS_EXTENDED_IDS(ha))
@@ -1665,7 +1688,7 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb_ret != NULL) {
@@ -1686,33 +1709,34 @@ qla2x00_login_local_device(scsi_qla_host_t *ha, fc_port_t *fcport,
rval = QLA_SUCCESS;
DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
- "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x "
- "mb[6]=%x mb[7]=%x.\n", __func__, ha->host_no, rval,
+ "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval,
mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]));
} else {
/*EMPTY*/
- DEBUG3(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG3(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return (rval);
}
int
-qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa)
{
int rval;
struct logio_entry_24xx *lg;
dma_addr_t lg_dma;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
if (lg == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Logout IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(lg, 0, sizeof(struct logio_entry_24xx));
@@ -1725,25 +1749,26 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
lg->port_id[0] = al_pa;
lg->port_id[1] = area;
lg->port_id[2] = domain;
- lg->vp_index = ha->vp_idx;
- rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0);
+ lg->vp_index = vha->vp_idx;
+
+ rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB "
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, vha->host_no, rval));
} else if (lg->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
lg->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
- DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ DEBUG2_3_11(printk("%s(%ld %d): failed to complete IOCB "
"-- completion status (%x) ioparam=%x/%x.\n", __func__,
- ha->host_no, le16_to_cpu(lg->comp_status),
+ vha->host_no, vha->vp_idx, le16_to_cpu(lg->comp_status),
le32_to_cpu(lg->io_parameter[0]),
le32_to_cpu(lg->io_parameter[1])));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
dma_pool_free(ha->s_dma_pool, lg, lg_dma);
@@ -1768,7 +1793,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
+qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
uint8_t area, uint8_t al_pa)
{
int rval;
@@ -1776,11 +1801,11 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
mcp->out_mb = MBX_1|MBX_0;
- if (HAS_EXTENDED_IDS(ha)) {
+ if (HAS_EXTENDED_IDS(vha->hw)) {
mcp->mb[1] = loop_id;
mcp->mb[10] = 0;
mcp->out_mb |= MBX_10;
@@ -1791,16 +1816,16 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x "
- "mbx1=%x.\n", ha->host_no, rval, mcp->mb[1]));
+ "mbx1=%x.\n", vha->host_no, rval, mcp->mb[1]));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1822,33 +1847,33 @@ qla2x00_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain,
* Kernel context.
*/
int
-qla2x00_full_login_lip(scsi_qla_host_t *ha)
+qla2x00_full_login_lip(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
- mcp->mb[1] = IS_FWI2_CAPABLE(ha) ? BIT_3: 0;
+ mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
mcp->mb[2] = 0;
mcp->mb[3] = 0;
mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
/*EMPTY*/
DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1867,7 +1892,7 @@ qla2x00_full_login_lip(scsi_qla_host_t *ha)
* Kernel context.
*/
int
-qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
+qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
uint16_t *entries)
{
int rval;
@@ -1875,20 +1900,20 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
mbx_cmd_t *mcp = &mc;
DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n",
- ha->host_no));
+ vha->host_no));
if (id_list == NULL)
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_ID_LIST;
mcp->out_mb = MBX_0;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[2] = MSW(id_list_dma);
mcp->mb[3] = LSW(id_list_dma);
mcp->mb[6] = MSW(MSD(id_list_dma));
mcp->mb[7] = LSW(MSD(id_list_dma));
mcp->mb[8] = 0;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2;
} else {
mcp->mb[1] = MSW(id_list_dma);
@@ -1900,16 +1925,16 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n",
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
*entries = mcp->mb[1];
DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n",
- ha->host_no));
+ vha->host_no));
}
return rval;
@@ -1929,7 +1954,7 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma,
* Kernel context.
*/
int
-qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
+qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
{
@@ -1937,22 +1962,22 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__,
- ha->host_no, mcp->mb[0]));
+ vha->host_no, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
- "mb7=%x mb10=%x mb11=%x.\n", __func__, ha->host_no,
+ "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
mcp->mb[10], mcp->mb[11]));
@@ -1964,7 +1989,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
*cur_iocb_cnt = mcp->mb[7];
if (orig_iocb_cnt)
*orig_iocb_cnt = mcp->mb[10];
- if (ha->flags.npiv_supported && max_npiv_vports)
+ if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
}
@@ -1987,18 +2012,19 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt,
* Kernel context.
*/
int
-qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
+qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
char *pmap;
dma_addr_t pmap_dma;
+ struct qla_hw_data *ha = vha->hw;
pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
if (pmap == NULL) {
DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(pmap, 0, FCAL_MAP_SIZE);
@@ -2013,11 +2039,11 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
mcp->buf_size = FCAL_MAP_SIZE;
mcp->flags = MBX_DMA_IN;
mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map "
- "size (%x)\n", __func__, ha->host_no, mcp->mb[0],
+ "size (%x)\n", __func__, vha->host_no, mcp->mb[0],
mcp->mb[1], (unsigned)pmap[0]));
DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1));
@@ -2028,9 +2054,9 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2051,15 +2077,16 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map)
* BIT_1 = mailbox error.
*/
int
-qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
+qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
struct link_statistics *stats, dma_addr_t stats_dma)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
uint32_t *siter, *diter, dwords;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_LINK_STATUS;
mcp->mb[2] = MSW(stats_dma);
@@ -2084,12 +2111,12 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
}
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = IOCTL_CMD;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
- __func__, ha->host_no, mcp->mb[0]));
+ __func__, vha->host_no, mcp->mb[0]));
rval = QLA_FUNCTION_FAILED;
} else {
/* Copy over data -- firmware data is LE. */
@@ -2101,14 +2128,14 @@ qla2x00_get_link_status(scsi_qla_host_t *ha, uint16_t loop_id,
} else {
/* Failed. */
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
return rval;
}
int
-qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
+qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
dma_addr_t stats_dma)
{
int rval;
@@ -2116,7 +2143,7 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
mbx_cmd_t *mcp = &mc;
uint32_t *siter, *diter, dwords;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
mcp->mb[2] = MSW(stats_dma);
@@ -2124,18 +2151,18 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
mcp->mb[6] = MSW(MSD(stats_dma));
mcp->mb[7] = LSW(MSD(stats_dma));
mcp->mb[8] = sizeof(struct link_statistics) / 4;
- mcp->mb[9] = ha->vp_idx;
+ mcp->mb[9] = vha->vp_idx;
mcp->mb[10] = 0;
mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = IOCTL_CMD;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n",
- __func__, ha->host_no, mcp->mb[0]));
+ __func__, vha->host_no, mcp->mb[0]));
rval = QLA_FUNCTION_FAILED;
} else {
/* Copy over data -- firmware data is LE. */
@@ -2147,14 +2174,14 @@ qla24xx_get_isp_stats(scsi_qla_host_t *ha, struct link_statistics *stats,
} else {
/* Failed. */
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
}
return rval;
}
int
-qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
+qla24xx_abort_command(scsi_qla_host_t *vha, srb_t *sp, struct req_que *req)
{
int rval;
fc_port_t *fcport;
@@ -2163,18 +2190,18 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
struct abort_entry_24xx *abt;
dma_addr_t abt_dma;
uint32_t handle;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
fcport = sp->fcport;
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
- if (pha->outstanding_cmds[handle] == sp)
+ if (req->outstanding_cmds[handle] == sp)
break;
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (handle == MAX_OUTSTANDING_COMMANDS) {
/* Command not found. */
return QLA_FUNCTION_FAILED;
@@ -2183,7 +2210,7 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
if (abt == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Abort IOCB.\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(abt, 0, sizeof(struct abort_entry_24xx));
@@ -2196,22 +2223,25 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp)
abt->port_id[1] = fcport->d_id.b.area;
abt->port_id[2] = fcport->d_id.b.domain;
abt->vp_index = fcport->vp_idx;
- rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0);
+
+ abt->req_que_no = cpu_to_le16(req->id);
+
+ rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n",
- __func__, ha->host_no, rval));
+ __func__, vha->host_no, rval));
} else if (abt->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
abt->entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (abt->nport_handle != __constant_cpu_to_le16(0)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, vha->host_no,
le16_to_cpu(abt->nport_handle)));
rval = QLA_FUNCTION_FAILED;
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
dma_pool_free(ha->s_dma_pool, abt, abt_dma);
@@ -2233,16 +2263,21 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
dma_addr_t tsk_dma;
- scsi_qla_host_t *ha, *pha;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no));
- ha = fcport->ha;
- pha = to_qla_parent(ha);
- tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma);
+ vha = fcport->vha;
+ ha = vha->hw;
+ req = ha->req_q_map[0];
+ rsp = ha->rsp_q_map[0];
+ tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
if (tsk == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Task Management "
- "IOCB.\n", __func__, ha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(tsk, 0, sizeof(struct tsk_mgmt_cmd));
@@ -2262,34 +2297,34 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
sizeof(tsk->p.tsk.lun));
}
- rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0);
+ rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
- "(%x).\n", __func__, ha->host_no, name, rval));
+ "(%x).\n", __func__, vha->host_no, name, rval));
} else if (tsk->p.sts.entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, vha->host_no,
tsk->p.sts.entry_status));
rval = QLA_FUNCTION_FAILED;
} else if (tsk->p.sts.comp_status !=
__constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x).\n", __func__,
- ha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+ vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
rval = QLA_FUNCTION_FAILED;
}
/* Issue marker IOCB. */
- rval2 = qla2x00_marker(ha, fcport->loop_id, l,
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID);
if (rval2 != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB "
- "(%x).\n", __func__, ha->host_no, rval2));
+ "(%x).\n", __func__, vha->host_no, rval2));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
- dma_pool_free(pha->s_dma_pool, tsk, tsk_dma);
+ dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
return rval;
}
@@ -2307,29 +2342,30 @@ qla24xx_lun_reset(struct fc_port *fcport, unsigned int l)
}
int
-qla2x00_system_error(scsi_qla_host_t *ha)
+qla2x00_system_error(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 5;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2342,14 +2378,14 @@ qla2x00_system_error(scsi_qla_host_t *ha)
* Returns
*/
int
-qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g,
+qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
uint16_t sw_em_2g, uint16_t sw_em_4g)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_SERDES_PARAMS;
mcp->mb[1] = BIT_0;
@@ -2360,61 +2396,61 @@ qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
/*EMPTY*/
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
/*EMPTY*/
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_stop_firmware(scsi_qla_host_t *ha)
+qla2x00_stop_firmware(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_STOP_FIRMWARE;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = 5;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma,
+qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
uint16_t buffers)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_EFT_ENABLE;
@@ -2428,28 +2464,28 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *ha, dma_addr_t eft_dma,
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_disable_eft_trace(scsi_qla_host_t *ha)
+qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_EFT_DISABLE;
@@ -2457,29 +2493,29 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *ha)
mcp->in_mb = MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
+qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
uint16_t buffers, uint16_t *mb, uint32_t *dwords)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_QLA25XX(ha))
+ if (!IS_QLA25XX(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_FCE_ENABLE;
@@ -2497,12 +2533,12 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
if (mb)
memcpy(mb, mcp->mb, 8 * sizeof(*mb));
@@ -2514,16 +2550,16 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *ha, dma_addr_t fce_dma,
}
int
-qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
+qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_TRACE_CONTROL;
mcp->mb[1] = TC_FCE_DISABLE;
@@ -2533,12 +2569,12 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n",
- __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1]));
+ __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
if (wr)
*wr = (uint64_t) mcp->mb[5] << 48 |
@@ -2556,17 +2592,17 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *ha, uint64_t *wr, uint64_t *rd)
}
int
-qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr,
+qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint16_t addr,
uint16_t off, uint16_t count)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_FWI2_CAPABLE(ha))
+ if (!IS_FWI2_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_READ_SFP;
mcp->mb[1] = addr;
@@ -2581,30 +2617,30 @@ qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
int
-qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
+qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
uint16_t port_speed, uint16_t *mb)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_IIDMA_CAPABLE(ha))
+ if (!IS_IIDMA_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mcp->mb[0] = MBC_PORT_PARAMS;
mcp->mb[1] = loop_id;
@@ -2615,7 +2651,7 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
/* Return mailbox statuses. */
if (mb != NULL) {
@@ -2628,28 +2664,29 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id,
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
}
void
-qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
struct vp_rpt_id_entry_24xx *rptid_entry)
{
uint8_t vp_idx;
uint16_t stat = le16_to_cpu(rptid_entry->vp_idx);
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
if (rptid_entry->entry_status != 0)
return;
if (rptid_entry->format == 0) {
DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d,"
- " number of VPs acquired %d\n", __func__, ha->host_no,
+ " number of VPs acquired %d\n", __func__, vha->host_no,
MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count)));
DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__,
rptid_entry->port_id[2], rptid_entry->port_id[1],
@@ -2658,7 +2695,7 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
vp_idx = LSB(stat);
DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled "
"- status %d - "
- "with port id %02x%02x%02x\n",__func__,ha->host_no,
+ "with port id %02x%02x%02x\n", __func__, vha->host_no,
vp_idx, MSB(stat),
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
@@ -2668,25 +2705,24 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *ha,
if (MSB(stat) == 1)
return;
- list_for_each_entry(vha, &ha->vp_list, vp_list)
- if (vp_idx == vha->vp_idx)
+ list_for_each_entry(vp, &ha->vp_list, list)
+ if (vp_idx == vp->vp_idx)
break;
-
- if (!vha)
+ if (!vp)
return;
- vha->d_id.b.domain = rptid_entry->port_id[2];
- vha->d_id.b.area = rptid_entry->port_id[1];
- vha->d_id.b.al_pa = rptid_entry->port_id[0];
+ vp->d_id.b.domain = rptid_entry->port_id[2];
+ vp->d_id.b.area = rptid_entry->port_id[1];
+ vp->d_id.b.al_pa = rptid_entry->port_id[0];
/*
* Cannot configure here as we are still sitting on the
* response queue. Handle it in dpc context.
*/
- set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
- set_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+ set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
- qla2xxx_wake_dpc(ha);
+ qla2xxx_wake_dpc(vha);
}
}
@@ -2709,15 +2745,15 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
int rval;
struct vp_config_entry_24xx *vpmod;
dma_addr_t vpmod_dma;
- scsi_qla_host_t *pha;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
/* This can be called by the parent */
- pha = to_qla_parent(vha);
- vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
+ vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
if (!vpmod) {
DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP "
- "IOCB.\n", __func__, pha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
@@ -2732,26 +2768,27 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha)
memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
vpmod->entry_count = 1;
- rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0);
+ rval = qla2x00_issue_iocb(base_vha, vpmod, vpmod_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB"
- "(%x).\n", __func__, pha->host_no, rval));
+ "(%x).\n", __func__, base_vha->host_no, rval));
} else if (vpmod->comp_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, pha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vpmod->comp_status));
rval = QLA_FUNCTION_FAILED;
} else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, pha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vpmod->comp_status)));
rval = QLA_FUNCTION_FAILED;
} else {
/* EMPTY */
- DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__,
+ base_vha->host_no));
fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
}
- dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma);
+ dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
return rval;
}
@@ -2778,11 +2815,12 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
int map, pos;
struct vp_ctrl_entry_24xx *vce;
dma_addr_t vce_dma;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
int vp_index = vha->vp_idx;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__,
- ha->host_no, vp_index));
+ vha->host_no, vp_index));
if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
return QLA_PARAMETER_ERROR;
@@ -2791,7 +2829,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
if (!vce) {
DEBUG2_3(printk("%s(%ld): "
"failed to allocate VP Control IOCB.\n", __func__,
- ha->host_no));
+ base_vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx));
@@ -2810,30 +2848,30 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
vce->vp_idx_map[map] |= 1 << pos;
mutex_unlock(&ha->vport_lock);
- rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0);
+ rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB"
- "(%x).\n", __func__, ha->host_no, rval));
+ "(%x).\n", __func__, base_vha->host_no, rval));
printk("%s(%ld): failed to issue VP control IOCB"
- "(%x).\n", __func__, ha->host_no, rval);
+ "(%x).\n", __func__, base_vha->host_no, rval);
} else if (vce->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vce->entry_status));
printk("%s(%ld): failed to complete IOCB "
- "-- error status (%x).\n", __func__, ha->host_no,
+ "-- error status (%x).\n", __func__, base_vha->host_no,
vce->entry_status);
rval = QLA_FUNCTION_FAILED;
} else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vce->comp_status)));
printk("%s(%ld): failed to complete IOCB "
- "-- completion status (%x).\n", __func__, ha->host_no,
+ "-- completion status (%x).\n", __func__, base_vha->host_no,
le16_to_cpu(vce->comp_status));
rval = QLA_FUNCTION_FAILED;
} else {
- DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG2(printk("%s(%ld): done.\n", __func__, base_vha->host_no));
}
dma_pool_free(ha->s_dma_pool, vce, vce_dma);
@@ -2863,7 +2901,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
*/
int
-qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
+qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format,
uint16_t vp_idx)
{
int rval;
@@ -2884,7 +2922,7 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
mcp->in_mb = MBX_0|MBX_1;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval == QLA_SUCCESS) {
if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
@@ -2897,16 +2935,16 @@ qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format,
}
int
-qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
+qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
uint32_t size)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- if (MSW(addr) || IS_FWI2_CAPABLE(ha)) {
+ if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
mcp->mb[8] = MSW(addr);
mcp->out_mb = MBX_8|MBX_0;
@@ -2920,7 +2958,7 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
mcp->mb[6] = MSW(MSD(req_dma));
mcp->mb[7] = LSW(MSD(req_dma));
mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1;
- if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_FWI2_CAPABLE(vha->hw)) {
mcp->mb[4] = MSW(size);
mcp->mb[5] = LSW(size);
mcp->out_mb |= MBX_5|MBX_4;
@@ -2932,13 +2970,13 @@ qla2x00_dump_ram(scsi_qla_host_t *ha, dma_addr_t req_dma, uint32_t addr,
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
- rval = qla2x00_mailbox_command(ha, mcp);
+ rval = qla2x00_mailbox_command(vha, mcp);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__,
- ha->host_no, rval, mcp->mb[0]));
+ vha->host_no, rval, mcp->mb[0]));
} else {
- DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no));
}
return rval;
@@ -2954,20 +2992,21 @@ struct cs84xx_mgmt_cmd {
};
int
-qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
+qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
{
int rval, retry;
struct cs84xx_mgmt_cmd *mn;
dma_addr_t mn_dma;
uint16_t options;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
- DEBUG16(printk("%s(%ld): entered.\n", __func__, ha->host_no));
+ DEBUG16(printk("%s(%ld): entered.\n", __func__, vha->host_no));
mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
if (mn == NULL) {
DEBUG2_3(printk("%s(%ld): failed to allocate Verify ISP84XX "
- "IOCB.\n", __func__, ha->host_no));
+ "IOCB.\n", __func__, vha->host_no));
return QLA_MEMORY_ALLOC_FAILED;
}
@@ -2986,19 +3025,19 @@ qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
mn->p.req.options = cpu_to_le16(options);
DEBUG16(printk("%s(%ld): Dump of Verify Request.\n", __func__,
- ha->host_no));
+ vha->host_no));
DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
sizeof(*mn)));
- rval = qla2x00_issue_iocb_timeout(ha, mn, mn_dma, 0, 120);
+ rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
if (rval != QLA_SUCCESS) {
DEBUG2_16(printk("%s(%ld): failed to issue Verify "
- "IOCB (%x).\n", __func__, ha->host_no, rval));
+ "IOCB (%x).\n", __func__, vha->host_no, rval));
goto verify_done;
}
DEBUG16(printk("%s(%ld): Dump of Verify Response.\n", __func__,
- ha->host_no));
+ vha->host_no));
DEBUG16(qla2x00_dump_buffer((uint8_t *)mn,
sizeof(*mn)));
@@ -3006,21 +3045,21 @@ qla84xx_verify_chip(struct scsi_qla_host *ha, uint16_t *status)
status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
le16_to_cpu(mn->p.rsp.failure_code) : 0;
DEBUG2_16(printk("%s(%ld): cs=%x fc=%x\n", __func__,
- ha->host_no, status[0], status[1]));
+ vha->host_no, status[0], status[1]));
if (status[0] != CS_COMPLETE) {
rval = QLA_FUNCTION_FAILED;
if (!(options & VCO_DONT_UPDATE_FW)) {
DEBUG2_16(printk("%s(%ld): Firmware update "
"failed. Retrying without update "
- "firmware.\n", __func__, ha->host_no));
+ "firmware.\n", __func__, vha->host_no));
options |= VCO_DONT_UPDATE_FW;
options &= ~VCO_FORCE_UPDATE;
retry = 1;
}
} else {
DEBUG2_16(printk("%s(%ld): firmware updated to %x.\n",
- __func__, ha->host_no,
+ __func__, vha->host_no,
le32_to_cpu(mn->p.rsp.fw_ver)));
/* NOTE: we only update OP firmware. */
@@ -3037,10 +3076,115 @@ verify_done:
if (rval != QLA_SUCCESS) {
DEBUG2_16(printk("%s(%ld): failed=%x.\n", __func__,
- ha->host_no, rval));
+ vha->host_no, rval));
} else {
- DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no));
+ DEBUG16(printk("%s(%ld): done.\n", __func__, vha->host_no));
+ }
+
+ return rval;
+}
+
+int
+qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req,
+ uint8_t options)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = options;
+ mcp->mb[2] = MSW(LSD(req->dma));
+ mcp->mb[3] = LSW(LSD(req->dma));
+ mcp->mb[6] = MSW(MSD(req->dma));
+ mcp->mb[7] = LSW(MSD(req->dma));
+ mcp->mb[5] = req->length;
+ if (req->rsp)
+ mcp->mb[10] = req->rsp->id;
+ mcp->mb[12] = req->qos;
+ mcp->mb[11] = req->vp_idx;
+ mcp->mb[13] = req->rid;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * req->id);
+
+ mcp->mb[4] = req->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7|
+ MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = 60;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(options & BIT_0)) {
+ WRT_REG_DWORD(&reg->req_q_in, 0);
+ WRT_REG_DWORD(&reg->req_q_out, 0);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x mb0=%x.\n",
+ __func__, vha->host_no, rval, mcp->mb[0]));
+ return rval;
+}
+
+int
+qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp,
+ uint8_t options)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = options;
+ mcp->mb[2] = MSW(LSD(rsp->dma));
+ mcp->mb[3] = LSW(LSD(rsp->dma));
+ mcp->mb[6] = MSW(MSD(rsp->dma));
+ mcp->mb[7] = LSW(MSD(rsp->dma));
+ mcp->mb[5] = rsp->length;
+ mcp->mb[11] = rsp->vp_idx;
+ mcp->mb[14] = rsp->msix->vector;
+ mcp->mb[13] = rsp->rid;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * rsp->id);
+
+ mcp->mb[4] = rsp->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7
+ |MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = 60;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(options & BIT_0)) {
+ WRT_REG_DWORD(&reg->rsp_q_out, 0);
+ WRT_REG_DWORD(&reg->rsp_q_in, 0);
}
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x "
+ "mb0=%x.\n", __func__,
+ vha->host_no, rval, mcp->mb[0]));
return rval;
}
+
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 93560cd72784..386ffeae5b5a 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -5,6 +5,7 @@
* See LICENSE.qla2xxx for copyright and licensing details.
*/
#include "qla_def.h"
+#include "qla_gbl.h"
#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
@@ -18,7 +19,7 @@
void
qla2x00_vp_stop_timer(scsi_qla_host_t *vha)
{
- if (vha->parent && vha->timer_active) {
+ if (vha->vp_idx && vha->timer_active) {
del_timer_sync(&vha->timer);
vha->timer_active = 0;
}
@@ -28,7 +29,7 @@ static uint32_t
qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
{
uint32_t vp_id;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
/* Find an empty slot and assign an vp_id */
mutex_lock(&ha->vport_lock);
@@ -44,7 +45,7 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
ha->num_vhosts++;
ha->cur_vport_count++;
vha->vp_idx = vp_id;
- list_add_tail(&vha->vp_list, &ha->vp_list);
+ list_add_tail(&vha->list, &ha->vp_list);
mutex_unlock(&ha->vport_lock);
return vp_id;
}
@@ -53,24 +54,24 @@ void
qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
{
uint16_t vp_id;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
mutex_lock(&ha->vport_lock);
vp_id = vha->vp_idx;
ha->num_vhosts--;
ha->cur_vport_count--;
clear_bit(vp_id, ha->vp_idx_map);
- list_del(&vha->vp_list);
+ list_del(&vha->list);
mutex_unlock(&ha->vport_lock);
}
static scsi_qla_host_t *
-qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name)
+qla24xx_find_vhost_by_name(struct qla_hw_data *ha, uint8_t *port_name)
{
scsi_qla_host_t *vha;
/* Locate matching device in database. */
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
+ list_for_each_entry(vha, &ha->vp_list, list) {
if (!memcmp(port_name, vha->port_name, WWN_SIZE))
return vha;
}
@@ -94,16 +95,13 @@ static void
qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
{
fc_port_t *fcport;
- scsi_qla_host_t *pha = to_qla_parent(vha);
-
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (fcport->vp_idx != vha->vp_idx)
- continue;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
DEBUG15(printk("scsi(%ld): Marking port dead, "
"loop_id=0x%04x :%x\n",
vha->host_no, fcport->loop_id, fcport->vp_idx));
+ atomic_set(&fcport->state, FCS_DEVICE_DEAD);
qla2x00_mark_device_lost(vha, fcport, 0, 0);
atomic_set(&fcport->state, FCS_UNCONFIGURED);
}
@@ -118,7 +116,6 @@ qla24xx_disable_vp(scsi_qla_host_t *vha)
atomic_set(&vha->loop_state, LOOP_DOWN);
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
- /* Delete all vp's fcports from parent's list */
qla2x00_mark_vp_devices_dead(vha);
atomic_set(&vha->vp_state, VP_FAILED);
vha->flags.management_server_logged_in = 0;
@@ -135,11 +132,12 @@ int
qla24xx_enable_vp(scsi_qla_host_t *vha)
{
int ret;
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
/* Check if physical ha port is Up */
- if (atomic_read(&ha->loop_state) == LOOP_DOWN ||
- atomic_read(&ha->loop_state) == LOOP_DEAD ) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
vha->vp_err_state = VP_ERR_PORTDWN;
fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN);
goto enable_failed;
@@ -177,8 +175,8 @@ qla24xx_configure_vp(scsi_qla_host_t *vha)
vha->host_no, __func__));
ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx);
if (ret != QLA_SUCCESS) {
- DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving"
- " of RSCN requests: 0x%x\n", ret));
+ DEBUG15(qla_printk(KERN_ERR, vha->hw, "Failed to enable "
+ "receiving of RSCN requests: 0x%x\n", ret));
return;
} else {
/* Corresponds to SCR enabled */
@@ -194,25 +192,14 @@ qla24xx_configure_vp(scsi_qla_host_t *vha)
}
void
-qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb)
+qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb)
{
- int i, vp_idx_matched;
scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = rsp->hw;
+ int i = 0;
- if (ha->parent)
- return;
-
- for_each_mapped_vp_idx(ha, i) {
- vp_idx_matched = 0;
-
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
- if (i == vha->vp_idx) {
- vp_idx_matched = 1;
- break;
- }
- }
-
- if (vp_idx_matched) {
+ list_for_each_entry(vha, &ha->vp_list, list) {
+ if (vha->vp_idx) {
switch (mb[0]) {
case MBA_LIP_OCCURRED:
case MBA_LOOP_UP:
@@ -223,16 +210,17 @@ qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb)
case MBA_PORT_UPDATE:
case MBA_RSCN_UPDATE:
DEBUG15(printk("scsi(%ld)%s: Async_event for"
- " VP[%d], mb = 0x%x, vha=%p\n",
- vha->host_no, __func__,i, *mb, vha));
- qla2x00_async_event(vha, mb);
+ " VP[%d], mb = 0x%x, vha=%p\n",
+ vha->host_no, __func__, i, *mb, vha));
+ qla2x00_async_event(vha, rsp, mb);
break;
}
}
+ i++;
}
}
-void
+int
qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
{
/*
@@ -247,38 +235,56 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
}
+ /* To exclusively reset vport, we need to log it out first.*/
+ if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
+
DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n",
vha->host_no, vha->vp_idx));
- qla24xx_enable_vp(vha);
+ return qla24xx_enable_vp(vha);
}
static int
qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
{
- scsi_qla_host_t *ha = vha->parent;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
/* VP acquired. complete port configuration */
- if (atomic_read(&ha->loop_state) == LOOP_READY) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_READY) {
qla24xx_configure_vp(vha);
} else {
set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
- set_bit(VP_DPC_NEEDED, &ha->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &base_vha->dpc_flags);
}
return 0;
}
- if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
- qla2x00_vp_abort_isp(vha);
+ if (test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) {
+ qla2x00_update_fcports(vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+ }
+
+ if ((test_and_clear_bit(RELOGIN_NEEDED, &vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) &&
+ atomic_read(&vha->loop_state) != LOOP_DOWN) {
+
+ DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
+ vha->host_no));
+ qla2x00_relogin(vha);
+
+ DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
+ vha->host_no));
+ }
if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) &&
(!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) {
clear_bit(RESET_ACTIVE, &vha->dpc_flags);
}
- if (atomic_read(&vha->vp_state) == VP_ACTIVE &&
- test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
qla2x00_loop_resync(vha);
clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
@@ -289,38 +295,30 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
}
void
-qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha)
+qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha)
{
int ret;
- int i, vp_idx_matched;
- scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
- if (ha->parent)
+ if (vha->vp_idx)
return;
if (list_empty(&ha->vp_list))
return;
- clear_bit(VP_DPC_NEEDED, &ha->dpc_flags);
-
- for_each_mapped_vp_idx(ha, i) {
- vp_idx_matched = 0;
-
- list_for_each_entry(vha, &ha->vp_list, vp_list) {
- if (i == vha->vp_idx) {
- vp_idx_matched = 1;
- break;
- }
- }
+ clear_bit(VP_DPC_NEEDED, &vha->dpc_flags);
- if (vp_idx_matched)
- ret = qla2x00_do_dpc_vp(vha);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx)
+ ret = qla2x00_do_dpc_vp(vp);
}
}
int
qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
{
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
scsi_qla_host_t *vha;
uint8_t port_name[WWN_SIZE];
@@ -337,7 +335,7 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
/* Check up unique WWPN */
u64_to_wwn(fc_vport->port_name, port_name);
- if (!memcmp(port_name, ha->port_name, WWN_SIZE))
+ if (!memcmp(port_name, base_vha->port_name, WWN_SIZE))
return VPCERR_BAD_WWN;
vha = qla24xx_find_vhost_by_name(ha, port_name);
if (vha)
@@ -346,7 +344,7 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
/* Check up max-npiv-supports */
if (ha->num_vhosts > ha->max_npiv_vports) {
DEBUG15(printk("scsi(%ld): num_vhosts %ud is bigger than "
- "max_npv_vports %ud.\n", ha->host_no,
+ "max_npv_vports %ud.\n", base_vha->host_no,
ha->num_vhosts, ha->max_npiv_vports));
return VPCERR_UNSUPPORTED;
}
@@ -356,59 +354,34 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
scsi_qla_host_t *
qla24xx_create_vhost(struct fc_vport *fc_vport)
{
- scsi_qla_host_t *ha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
scsi_qla_host_t *vha;
+ struct scsi_host_template *sht = &qla24xx_driver_template;
struct Scsi_Host *host;
- host = scsi_host_alloc(&qla24xx_driver_template,
- sizeof(scsi_qla_host_t));
- if (!host) {
- printk(KERN_WARNING
- "qla2xxx: scsi_host_alloc() failed for vport\n");
+ vha = qla2x00_create_host(sht, ha);
+ if (!vha) {
+ DEBUG(printk("qla2xxx: scsi_host_alloc() failed for vport\n"));
return(NULL);
}
- vha = shost_priv(host);
-
- /* clone the parent hba */
- memcpy(vha, ha, sizeof (scsi_qla_host_t));
-
+ host = vha->host;
fc_vport->dd_data = vha;
-
- vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
- if (!vha->node_name)
- goto create_vhost_failed_1;
-
- vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL);
- if (!vha->port_name)
- goto create_vhost_failed_2;
-
/* New host info */
u64_to_wwn(fc_vport->node_name, vha->node_name);
u64_to_wwn(fc_vport->port_name, vha->port_name);
- vha->host = host;
- vha->host_no = host->host_no;
- vha->parent = ha;
vha->fc_vport = fc_vport;
vha->device_flags = 0;
vha->vp_idx = qla24xx_allocate_vp_id(vha);
if (vha->vp_idx > ha->max_npiv_vports) {
DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n",
vha->host_no));
- goto create_vhost_failed_3;
+ goto create_vhost_failed;
}
vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
- init_completion(&vha->mbx_cmd_comp);
- complete(&vha->mbx_cmd_comp);
- init_completion(&vha->mbx_intr_comp);
-
- INIT_LIST_HEAD(&vha->list);
- INIT_LIST_HEAD(&vha->fcports);
- INIT_LIST_HEAD(&vha->vp_fcports);
- INIT_LIST_HEAD(&vha->work_list);
-
vha->dpc_flags = 0L;
set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
@@ -423,7 +396,9 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
- host->can_queue = vha->request_q_length + 128;
+ memset(vha->req_ques, 0, sizeof(vha->req_ques) * QLA_MAX_HOST_QUES);
+ vha->req_ques[0] = ha->req_q_map[0]->id;
+ host->can_queue = ha->req_q_map[0]->length + 128;
host->this_id = 255;
host->cmd_per_lun = 3;
host->max_cmd_len = MAX_CMDSZ;
@@ -440,12 +415,341 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
return vha;
-create_vhost_failed_3:
- kfree(vha->port_name);
+create_vhost_failed:
+ return NULL;
+}
-create_vhost_failed_2:
- kfree(vha->node_name);
+static void
+qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = req->id;
+
+ dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
+ sizeof(request_t), req->ring, req->dma);
+ req->ring = NULL;
+ req->dma = 0;
+ if (que_id) {
+ ha->req_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(req);
+ req = NULL;
+}
-create_vhost_failed_1:
- return NULL;
+static void
+qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = rsp->id;
+
+ if (rsp->msix && rsp->msix->have_irq) {
+ free_irq(rsp->msix->vector, rsp);
+ rsp->msix->have_irq = 0;
+ rsp->msix->rsp = NULL;
+ }
+ dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
+ sizeof(response_t), rsp->ring, rsp->dma);
+ rsp->ring = NULL;
+ rsp->dma = 0;
+ if (que_id) {
+ ha->rsp_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(rsp);
+ rsp = NULL;
+}
+
+int
+qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ int ret = -1;
+
+ if (req) {
+ req->options |= BIT_0;
+ ret = qla25xx_init_req_que(vha, req, req->options);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_req_que(vha, req);
+
+ return ret;
+}
+
+int
+qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ int ret = -1;
+
+ if (rsp) {
+ rsp->options |= BIT_0;
+ ret = qla25xx_init_rsp_que(vha, rsp, rsp->options);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_rsp_que(vha, rsp);
+
+ return ret;
+}
+
+int qla25xx_update_req_que(struct scsi_qla_host *vha, uint8_t que, uint8_t qos)
+{
+ int ret = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[que];
+
+ req->options |= BIT_3;
+ req->qos = qos;
+ ret = qla25xx_init_req_que(vha, req, req->options);
+ if (ret != QLA_SUCCESS)
+ DEBUG2_17(printk(KERN_WARNING "%s failed\n", __func__));
+ /* restore options bit */
+ req->options &= ~BIT_3;
+ return ret;
+}
+
+
+/* Delete all queues for a given vhost */
+int
+qla25xx_delete_queues(struct scsi_qla_host *vha, uint8_t que_no)
+{
+ int cnt, ret = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (que_no) {
+ /* Delete request queue */
+ req = ha->req_q_map[que_no];
+ if (req) {
+ rsp = req->rsp;
+ ret = qla25xx_delete_req_que(vha, req);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete req que %d\n", req->id);
+ return ret;
+ }
+ /* Delete associated response queue */
+ if (rsp) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete rsp que %d\n",
+ rsp->id);
+ return ret;
+ }
+ }
+ }
+ } else { /* delete all queues of this host */
+ for (cnt = 0; cnt < QLA_MAX_HOST_QUES; cnt++) {
+ /* Delete request queues */
+ req = ha->req_q_map[vha->req_ques[cnt]];
+ if (req && req->id) {
+ rsp = req->rsp;
+ ret = qla25xx_delete_req_que(vha, req);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete req que %d\n",
+ vha->req_ques[cnt]);
+ return ret;
+ }
+ vha->req_ques[cnt] = ha->req_q_map[0]->id;
+ /* Delete associated response queue */
+ if (rsp && rsp->id) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Couldn't delete rsp que %d\n",
+ rsp->id);
+ return ret;
+ }
+ }
+ }
+ }
+ }
+ qla_printk(KERN_INFO, ha, "Queues deleted for vport:%d\n",
+ vha->vp_idx);
+ return ret;
+}
+
+int
+qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid, uint8_t rsp_que, uint8_t qos)
+{
+ int ret = 0;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;
+
+ req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (req == NULL) {
+ qla_printk(KERN_WARNING, ha, "could not allocate memory"
+ "for request que\n");
+ goto que_failed;
+ }
+
+ req->length = REQUEST_ENTRY_CNT_24XX;
+ req->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ &req->dma, GFP_KERNEL);
+ if (req->ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - request_ring\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->req_qid_map, ha->max_queues);
+ if (que_id >= ha->max_queues) {
+ mutex_unlock(&ha->vport_lock);
+ qla_printk(KERN_INFO, ha, "No resources to create "
+ "additional request queue\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->req_qid_map);
+ ha->req_q_map[que_id] = req;
+ req->rid = rid;
+ req->vp_idx = vp_idx;
+ req->qos = qos;
+
+ if (ha->rsp_q_map[rsp_que])
+ req->rsp = ha->rsp_q_map[rsp_que];
+ /* Use alternate PCI bus number */
+ if (MSB(req->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(req->rid))
+ options |= BIT_5;
+ req->options = options;
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ req->id = que_id;
+ mutex_unlock(&ha->vport_lock);
+
+ ret = qla25xx_init_req_que(base_vha, req, options);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+
+ return req->id;
+
+que_failed:
+ qla25xx_free_req_que(base_vha, req);
+ return 0;
+}
+
+/* create response queue */
+int
+qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid)
+{
+ int ret = 0;
+ struct rsp_que *rsp = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;;
+
+ rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (rsp == NULL) {
+ qla_printk(KERN_WARNING, ha, "could not allocate memory for"
+ " response que\n");
+ goto que_failed;
+ }
+
+ rsp->length = RESPONSE_ENTRY_CNT_2300;
+ rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ &rsp->dma, GFP_KERNEL);
+ if (rsp->ring == NULL) {
+ qla_printk(KERN_WARNING, ha,
+ "Memory Allocation failed - response_ring\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues);
+ if (que_id >= ha->max_queues) {
+ mutex_unlock(&ha->vport_lock);
+ qla_printk(KERN_INFO, ha, "No resources to create "
+ "additional response queue\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->rsp_qid_map);
+
+ if (ha->flags.msix_enabled)
+ rsp->msix = &ha->msix_entries[que_id + 1];
+ else
+ qla_printk(KERN_WARNING, ha, "msix not enabled\n");
+
+ ha->rsp_q_map[que_id] = rsp;
+ rsp->rid = rid;
+ rsp->vp_idx = vp_idx;
+ rsp->hw = ha;
+ /* Use alternate PCI bus number */
+ if (MSB(rsp->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(rsp->rid))
+ options |= BIT_5;
+ rsp->options = options;
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
+ rsp->id = que_id;
+ mutex_unlock(&ha->vport_lock);
+
+ ret = qla25xx_request_irq(rsp);
+ if (ret)
+ goto que_failed;
+
+ ret = qla25xx_init_rsp_que(base_vha, rsp, options);
+ if (ret != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha, "%s failed\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+
+ qla2x00_init_response_q_entries(rsp);
+
+ return rsp->id;
+
+que_failed:
+ qla25xx_free_rsp_que(base_vha, rsp);
+ return 0;
+}
+
+int
+qla25xx_create_queues(struct scsi_qla_host *vha, uint8_t qos)
+{
+ uint16_t options = 0;
+ uint8_t ret = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ options |= BIT_1;
+ ret = qla25xx_create_rsp_que(ha, options, vha->vp_idx, 0);
+ if (!ret) {
+ qla_printk(KERN_WARNING, ha, "Response Que create failed\n");
+ return ret;
+ } else
+ qla_printk(KERN_INFO, ha, "Response Que:%d created.\n", ret);
+
+ options = 0;
+ if (qos & BIT_7)
+ options |= BIT_8;
+ ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, ret,
+ qos & ~BIT_7);
+ if (ret) {
+ vha->req_ques[0] = ret;
+ qla_printk(KERN_INFO, ha, "Request Que:%d created.\n", ret);
+ } else
+ qla_printk(KERN_WARNING, ha, "Request Que create failed\n");
+
+ return ret;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 35567203ef61..8ea927788b3f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -92,7 +92,12 @@ MODULE_PARM_DESC(ql2xiidmaenable,
"Enables iIDMA settings "
"Default is 1 - perform iIDMA. 0 - no iIDMA.");
-
+int ql2xmaxqueues = 1;
+module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
+MODULE_PARM_DESC(ql2xmaxqueues,
+ "Enables MQ settings "
+ "Default is 1 for single queue. Set it to number \
+ of queues in MQ mode.");
/*
* SCSI host template entry points
*/
@@ -183,42 +188,108 @@ struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
*/
__inline__ void
-qla2x00_start_timer(scsi_qla_host_t *ha, void *func, unsigned long interval)
+qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval)
{
- init_timer(&ha->timer);
- ha->timer.expires = jiffies + interval * HZ;
- ha->timer.data = (unsigned long)ha;
- ha->timer.function = (void (*)(unsigned long))func;
- add_timer(&ha->timer);
- ha->timer_active = 1;
+ init_timer(&vha->timer);
+ vha->timer.expires = jiffies + interval * HZ;
+ vha->timer.data = (unsigned long)vha;
+ vha->timer.function = (void (*)(unsigned long))func;
+ add_timer(&vha->timer);
+ vha->timer_active = 1;
}
static inline void
-qla2x00_restart_timer(scsi_qla_host_t *ha, unsigned long interval)
+qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
{
- mod_timer(&ha->timer, jiffies + interval * HZ);
+ mod_timer(&vha->timer, jiffies + interval * HZ);
}
static __inline__ void
-qla2x00_stop_timer(scsi_qla_host_t *ha)
+qla2x00_stop_timer(scsi_qla_host_t *vha)
{
- del_timer_sync(&ha->timer);
- ha->timer_active = 0;
+ del_timer_sync(&vha->timer);
+ vha->timer_active = 0;
}
static int qla2x00_do_dpc(void *data);
static void qla2x00_rst_aen(scsi_qla_host_t *);
-static int qla2x00_mem_alloc(scsi_qla_host_t *);
-static void qla2x00_mem_free(scsi_qla_host_t *ha);
-static void qla2x00_sp_free_dma(scsi_qla_host_t *, srb_t *);
+static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
+ struct req_que **, struct rsp_que **);
+static void qla2x00_mem_free(struct qla_hw_data *);
+static void qla2x00_sp_free_dma(srb_t *);
/* -------------------------------------------------------------------------- */
+static int qla2x00_alloc_queues(struct qla_hw_data *ha)
+{
+ ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_queues,
+ GFP_KERNEL);
+ if (!ha->req_q_map) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for request queue ptrs\n");
+ goto fail_req_map;
+ }
+
+ ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_queues,
+ GFP_KERNEL);
+ if (!ha->rsp_q_map) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for response queue ptrs\n");
+ goto fail_rsp_map;
+ }
+ set_bit(0, ha->rsp_qid_map);
+ set_bit(0, ha->req_qid_map);
+ return 1;
+
+fail_rsp_map:
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+fail_req_map:
+ return -ENOMEM;
+}
+
+static void qla2x00_free_que(struct qla_hw_data *ha, struct req_que *req,
+ struct rsp_que *rsp)
+{
+ if (rsp && rsp->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ rsp->ring, rsp->dma);
+
+ kfree(rsp);
+ rsp = NULL;
+ if (req && req->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ req->ring, req->dma);
+
+ kfree(req);
+ req = NULL;
+}
+
+static void qla2x00_free_queues(struct qla_hw_data *ha)
+{
+ struct req_que *req;
+ struct rsp_que *rsp;
+ int cnt;
+
+ for (cnt = 0; cnt < ha->max_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ req = ha->req_q_map[cnt];
+ qla2x00_free_que(ha, req, rsp);
+ }
+ kfree(ha->rsp_q_map);
+ ha->rsp_q_map = NULL;
+
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+}
static char *
-qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str)
+qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str)
{
+ struct qla_hw_data *ha = vha->hw;
static char *pci_bus_modes[] = {
"33", "66", "100", "133",
};
@@ -240,9 +311,10 @@ qla2x00_pci_info_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
+qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
{
static char *pci_bus_modes[] = { "33", "66", "100", "133", };
+ struct qla_hw_data *ha = vha->hw;
uint32_t pci_bus;
int pcie_reg;
@@ -290,9 +362,10 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
+qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str)
{
char un_str[10];
+ struct qla_hw_data *ha = vha->hw;
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
ha->fw_minor_version,
@@ -328,8 +401,9 @@ qla2x00_fw_version_str(struct scsi_qla_host *ha, char *str)
}
static char *
-qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
+qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
{
+ struct qla_hw_data *ha = vha->hw;
sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
ha->fw_minor_version,
ha->fw_subminor_version);
@@ -354,18 +428,20 @@ qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
}
static inline srb_t *
-qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
+qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
if (!sp)
return sp;
- sp->ha = ha;
+ sp->vha = vha;
sp->fcport = fcport;
sp->cmd = cmd;
+ sp->que = ha->req_q_map[0];
sp->flags = 0;
CMD_SP(cmd) = (void *)sp;
cmd->scsi_done = done;
@@ -376,9 +452,10 @@ qla2x00_get_new_sp(scsi_qla_host_t *ha, fc_port_t *fcport,
static int
qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+ struct qla_hw_data *ha = vha->hw;
srb_t *sp;
int rval;
@@ -399,33 +476,33 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
if (atomic_read(&fcport->state) != FCS_ONLINE) {
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
- atomic_read(&ha->loop_state) == LOOP_DEAD) {
+ atomic_read(&vha->loop_state) == LOOP_DEAD) {
cmd->result = DID_NO_CONNECT << 16;
goto qc_fail_command;
}
goto qc_target_busy;
}
- spin_unlock_irq(ha->host->host_lock);
+ spin_unlock_irq(vha->host->host_lock);
- sp = qla2x00_get_new_sp(ha, fcport, cmd, done);
+ sp = qla2x00_get_new_sp(vha, fcport, cmd, done);
if (!sp)
goto qc_host_busy_lock;
- rval = qla2x00_start_scsi(sp);
+ rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS)
goto qc_host_busy_free_sp;
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return 0;
qc_host_busy_free_sp:
- qla2x00_sp_free_dma(ha, sp);
+ qla2x00_sp_free_dma(sp);
mempool_free(sp, ha->srb_mempool);
qc_host_busy_lock:
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return SCSI_MLQUEUE_HOST_BUSY;
qc_target_busy:
@@ -441,14 +518,15 @@ qc_fail_command:
static int
qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
srb_t *sp;
int rval;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- if (unlikely(pci_channel_offline(pha->pdev))) {
+ if (unlikely(pci_channel_offline(ha->pdev))) {
cmd->result = DID_REQUEUE << 16;
goto qc24_fail_command;
}
@@ -465,33 +543,33 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
if (atomic_read(&fcport->state) != FCS_ONLINE) {
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
- atomic_read(&pha->loop_state) == LOOP_DEAD) {
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
cmd->result = DID_NO_CONNECT << 16;
goto qc24_fail_command;
}
goto qc24_target_busy;
}
- spin_unlock_irq(ha->host->host_lock);
+ spin_unlock_irq(vha->host->host_lock);
- sp = qla2x00_get_new_sp(pha, fcport, cmd, done);
+ sp = qla2x00_get_new_sp(base_vha, fcport, cmd, done);
if (!sp)
goto qc24_host_busy_lock;
- rval = qla24xx_start_scsi(sp);
+ rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS)
goto qc24_host_busy_free_sp;
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return 0;
qc24_host_busy_free_sp:
- qla2x00_sp_free_dma(pha, sp);
- mempool_free(sp, pha->srb_mempool);
+ qla2x00_sp_free_dma(sp);
+ mempool_free(sp, ha->srb_mempool);
qc24_host_busy_lock:
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
return SCSI_MLQUEUE_HOST_BUSY;
qc24_target_busy:
@@ -510,17 +588,14 @@ qc24_fail_command:
* max time.
*
* Input:
- * ha = actual ha whose done queue will contain the command
- * returned by firmware.
* cmd = Scsi Command to wait on.
- * flag = Abort/Reset(Bus or Device Reset)
*
* Return:
* Not Found : 0
* Found : 1
*/
static int
-qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
+qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
{
#define ABORT_POLLING_PERIOD 1000
#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD))
@@ -557,21 +632,22 @@ qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd)
* Failed (Adapter is offline/disabled) : 1
*/
int
-qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
+qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
{
int return_status;
unsigned long wait_online;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
- while (((test_bit(ISP_ABORT_NEEDED, &pha->dpc_flags)) ||
- test_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags) ||
- test_bit(ISP_ABORT_RETRY, &pha->dpc_flags) ||
- pha->dpc_active) && time_before(jiffies, wait_online)) {
+ while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ ha->dpc_active) && time_before(jiffies, wait_online)) {
msleep(1000);
}
- if (pha->flags.online)
+ if (base_vha->flags.online)
return_status = QLA_SUCCESS;
else
return_status = QLA_FUNCTION_FAILED;
@@ -596,19 +672,20 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
* Failed (LOOP_NOT_READY) : 1
*/
static inline int
-qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
+qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha)
{
int return_status = QLA_SUCCESS;
unsigned long loop_timeout ;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
/* wait for 5 min at the max for loop to be ready */
loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ);
- while ((!atomic_read(&pha->loop_down_timer) &&
- atomic_read(&pha->loop_state) == LOOP_DOWN) ||
- atomic_read(&pha->loop_state) != LOOP_READY) {
- if (atomic_read(&pha->loop_state) == LOOP_DEAD) {
+ while ((!atomic_read(&base_vha->loop_down_timer) &&
+ atomic_read(&base_vha->loop_state) == LOOP_DOWN) ||
+ atomic_read(&base_vha->loop_state) != LOOP_READY) {
+ if (atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
return_status = QLA_FUNCTION_FAILED;
break;
}
@@ -624,35 +701,42 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha)
void
qla2x00_abort_fcport_cmds(fc_port_t *fcport)
{
- int cnt;
+ int cnt, que, id;
unsigned long flags;
srb_t *sp;
- scsi_qla_host_t *ha = fcport->ha;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ scsi_qla_host_t *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
- spin_lock_irqsave(&pha->hardware_lock, flags);
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = pha->outstanding_cmds[cnt];
- if (!sp)
- continue;
- if (sp->fcport != fcport)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
+ id = vha->req_ques[que];
+ req = ha->req_q_map[id];
+ if (!req)
continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (!sp)
+ continue;
+ if (sp->fcport != fcport)
+ continue;
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(ha, sp)) {
- DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed -- %lx\n", sp->cmd->serial_number));
- } else {
- if (qla2x00_eh_wait_on_command(ha, sp->cmd) !=
- QLA_SUCCESS)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(vha, sp, req)) {
DEBUG2(qla_printk(KERN_WARNING, ha,
- "Abort failed while waiting -- %lx\n",
- sp->cmd->serial_number));
-
+ "Abort failed -- %lx\n",
+ sp->cmd->serial_number));
+ } else {
+ if (qla2x00_eh_wait_on_command(sp->cmd) !=
+ QLA_SUCCESS)
+ DEBUG2(qla_printk(KERN_WARNING, ha,
+ "Abort failed while waiting -- %lx\n",
+ sp->cmd->serial_number));
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
}
- spin_lock_irqsave(&pha->hardware_lock, flags);
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
static void
@@ -690,14 +774,16 @@ qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
static int
qla2xxx_eh_abort(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
srb_t *sp;
int ret, i;
unsigned int id, lun;
unsigned long serial;
unsigned long flags;
int wait = 0;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ srb_t *spt;
qla2x00_block_error_handler(cmd);
@@ -709,11 +795,15 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
id = cmd->device->id;
lun = cmd->device->lun;
serial = cmd->serial_number;
+ spt = (srb_t *) CMD_SP(cmd);
+ if (!spt)
+ return SUCCESS;
+ req = spt->que;
/* Check active list for command command. */
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
for (i = 1; i < MAX_OUTSTANDING_COMMANDS; i++) {
- sp = pha->outstanding_cmds[i];
+ sp = req->outstanding_cmds[i];
if (sp == NULL)
continue;
@@ -721,38 +811,36 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
if (sp->cmd != cmd)
continue;
- DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld.\n",
- __func__, ha->host_no, sp, serial));
+ DEBUG2(printk("%s(%ld): aborting sp %p from RISC."
+ " pid=%ld.\n", __func__, vha->host_no, sp, serial));
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- if (ha->isp_ops->abort_command(ha, sp)) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(vha, sp, req)) {
DEBUG2(printk("%s(%ld): abort_command "
- "mbx failed.\n", __func__, ha->host_no));
- ret = FAILED;
+ "mbx failed.\n", __func__, vha->host_no));
} else {
DEBUG3(printk("%s(%ld): abort_command "
- "mbx success.\n", __func__, ha->host_no));
+ "mbx success.\n", __func__, vha->host_no));
wait = 1;
}
- spin_lock_irqsave(&pha->hardware_lock, flags);
-
+ spin_lock_irqsave(&ha->hardware_lock, flags);
break;
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Wait for the command to be returned. */
if (wait) {
- if (qla2x00_eh_wait_on_command(ha, cmd) != QLA_SUCCESS) {
+ if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
"scsi(%ld:%d:%d): Abort handler timed out -- %lx "
- "%x.\n", ha->host_no, id, lun, serial, ret);
+ "%x.\n", vha->host_no, id, lun, serial, ret);
ret = FAILED;
}
}
qla_printk(KERN_INFO, ha,
"scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n",
- ha->host_no, id, lun, wait, serial, ret);
+ vha->host_no, id, lun, wait, serial, ret);
return ret;
}
@@ -764,23 +852,27 @@ enum nexus_wait_type {
};
static int
-qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
- unsigned int l, enum nexus_wait_type type)
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
+ unsigned int l, srb_t *sp, enum nexus_wait_type type)
{
int cnt, match, status;
- srb_t *sp;
unsigned long flags;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
status = QLA_SUCCESS;
- spin_lock_irqsave(&pha->hardware_lock, flags);
- for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS;
- cnt++) {
- sp = pha->outstanding_cmds[cnt];
+ if (!sp)
+ return status;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ req = sp->que;
+ for (cnt = 1; status == QLA_SUCCESS &&
+ cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
if (!sp)
continue;
- if (ha->vp_idx != sp->fcport->ha->vp_idx)
+ if (vha->vp_idx != sp->fcport->vha->vp_idx)
continue;
match = 0;
switch (type) {
@@ -792,17 +884,17 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
break;
case WAIT_LUN:
match = (sp->cmd->device->id == t &&
- sp->cmd->device->lun == l);
+ sp->cmd->device->lun == l);
break;
}
if (!match)
continue;
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
- status = qla2x00_eh_wait_on_command(ha, sp->cmd);
- spin_lock_irqsave(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ status = qla2x00_eh_wait_on_command(sp->cmd);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
}
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return status;
}
@@ -818,7 +910,7 @@ static int
__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int err;
@@ -827,31 +919,31 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
if (!fcport)
return FAILED;
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name);
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
+ vha->host_no, cmd->device->id, cmd->device->lun, name);
err = 0;
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
goto eh_reset_failed;
err = 1;
- if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS)
goto eh_reset_failed;
err = 2;
if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
goto eh_reset_failed;
err = 3;
- if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id,
- cmd->device->lun, type) != QLA_SUCCESS)
+ if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
+ cmd->device->lun, (srb_t *) CMD_SP(cmd), type) != QLA_SUCCESS)
goto eh_reset_failed;
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name);
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
+ vha->host_no, cmd->device->id, cmd->device->lun, name);
return SUCCESS;
eh_reset_failed:
- qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
- ha->host_no, cmd->device->id, cmd->device->lun, name,
+ qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n"
+ , vha->host_no, cmd->device->id, cmd->device->lun, name,
reset_errors[err]);
return FAILED;
}
@@ -859,7 +951,8 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
static int
qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
ha->isp_ops->lun_reset);
@@ -868,7 +961,8 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
static int
qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
ha->isp_ops->target_reset);
@@ -892,12 +986,12 @@ qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
static int
qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int ret = FAILED;
unsigned int id, lun;
unsigned long serial;
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
qla2x00_block_error_handler(cmd);
@@ -908,28 +1002,28 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
if (!fcport)
return ret;
- qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d): LOOP RESET ISSUED.\n", ha->host_no, id, lun);
+ qla_printk(KERN_INFO, vha->hw,
+ "scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun);
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS) {
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
DEBUG2(printk("%s failed:board disabled\n",__func__));
goto eh_bus_reset_done;
}
- if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
- if (qla2x00_loop_reset(ha) == QLA_SUCCESS)
+ if (qla2x00_wait_for_loop_ready(vha) == QLA_SUCCESS) {
+ if (qla2x00_loop_reset(vha) == QLA_SUCCESS)
ret = SUCCESS;
}
if (ret == FAILED)
goto eh_bus_reset_done;
/* Flush outstanding commands. */
- if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) !=
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) !=
QLA_SUCCESS)
ret = FAILED;
eh_bus_reset_done:
- qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
+ qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__,
(ret == FAILED) ? "failed" : "succeded");
return ret;
@@ -953,12 +1047,14 @@ eh_bus_reset_done:
static int
qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
{
- scsi_qla_host_t *ha = shost_priv(cmd->device->host);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+ struct qla_hw_data *ha = vha->hw;
int ret = FAILED;
unsigned int id, lun;
unsigned long serial;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ srb_t *sp = (srb_t *) CMD_SP(cmd);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
qla2x00_block_error_handler(cmd);
@@ -970,9 +1066,9 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
return ret;
qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no, id, lun);
+ "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun);
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
goto eh_host_reset_lock;
/*
@@ -983,26 +1079,28 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
* devices as lost kicking of the port_down_timer
* while dpc is stuck for the mailbox to complete.
*/
- qla2x00_wait_for_loop_ready(ha);
- set_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- if (qla2x00_abort_isp(pha)) {
- clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- /* failed. schedule dpc to try */
- set_bit(ISP_ABORT_NEEDED, &pha->dpc_flags);
-
- if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
+ qla2x00_wait_for_loop_ready(vha);
+ if (vha != base_vha) {
+ if (qla2x00_vp_abort_isp(vha))
goto eh_host_reset_lock;
+ } else {
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha)) {
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ /* failed. schedule dpc to try */
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
+ goto eh_host_reset_lock;
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
}
- clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
- /* Waiting for our command in done_queue to be returned to OS.*/
- if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) ==
- QLA_SUCCESS)
+ /* Waiting for command to be returned to OS.*/
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, sp, WAIT_HOST) ==
+ QLA_SUCCESS)
ret = SUCCESS;
- if (ha->parent)
- qla2x00_vp_abort_isp(ha);
-
eh_host_reset_lock:
qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__,
(ret == FAILED) ? "failed" : "succeded");
@@ -1021,35 +1119,36 @@ eh_host_reset_lock:
* 0 = success
*/
int
-qla2x00_loop_reset(scsi_qla_host_t *ha)
+qla2x00_loop_reset(scsi_qla_host_t *vha)
{
int ret;
struct fc_port *fcport;
+ struct qla_hw_data *ha = vha->hw;
- if (ha->flags.enable_lip_full_login) {
- ret = qla2x00_full_login_lip(ha);
+ if (ha->flags.enable_lip_full_login && !vha->vp_idx) {
+ ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
- DEBUG2_3(printk("%s(%ld): bus_reset failed: "
- "full_login_lip=%d.\n", __func__, ha->host_no,
+ DEBUG2_3(printk("%s(%ld): failed: "
+ "full_login_lip=%d.\n", __func__, vha->host_no,
ret));
}
- atomic_set(&ha->loop_state, LOOP_DOWN);
- atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
- qla2x00_mark_all_devices_lost(ha, 0);
- qla2x00_wait_for_loop_ready(ha);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ qla2x00_wait_for_loop_ready(vha);
}
- if (ha->flags.enable_lip_reset) {
- ret = qla2x00_lip_reset(ha);
+ if (ha->flags.enable_lip_reset && !vha->vp_idx) {
+ ret = qla2x00_lip_reset(vha);
if (ret != QLA_SUCCESS) {
- DEBUG2_3(printk("%s(%ld): bus_reset failed: "
- "lip_reset=%d.\n", __func__, ha->host_no, ret));
- }
- qla2x00_wait_for_loop_ready(ha);
+ DEBUG2_3(printk("%s(%ld): failed: "
+ "lip_reset=%d.\n", __func__, vha->host_no, ret));
+ } else
+ qla2x00_wait_for_loop_ready(vha);
}
if (ha->flags.enable_target_reset) {
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->port_type != FCT_TARGET)
continue;
@@ -1057,31 +1156,37 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): bus_reset failed: "
"target_reset=%d d_id=%x.\n", __func__,
- ha->host_no, ret, fcport->d_id.b24));
+ vha->host_no, ret, fcport->d_id.b24));
}
}
}
-
/* Issue marker command only when we are going to start the I/O */
- ha->marker_needed = 1;
+ vha->marker_needed = 1;
return QLA_SUCCESS;
}
void
-qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res)
+qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
{
- int cnt;
+ int que, cnt;
unsigned long flags;
srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
spin_lock_irqsave(&ha->hardware_lock, flags);
- for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
- sp = ha->outstanding_cmds[cnt];
- if (sp) {
- ha->outstanding_cmds[cnt] = NULL;
- sp->cmd->result = res;
- qla2x00_sp_compl(ha, sp);
+ for (que = 0; que < QLA_MAX_HOST_QUES; que++) {
+ req = ha->req_q_map[vha->req_ques[que]];
+ if (!req)
+ continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp && sp->vha == vha) {
+ req->outstanding_cmds[cnt] = NULL;
+ sp->cmd->result = res;
+ qla2x00_sp_compl(ha, sp);
+ }
}
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -1103,13 +1208,15 @@ qla2xxx_slave_alloc(struct scsi_device *sdev)
static int
qla2xxx_slave_configure(struct scsi_device *sdev)
{
- scsi_qla_host_t *ha = shost_priv(sdev->host);
+ scsi_qla_host_t *vha = shost_priv(sdev->host);
+ struct qla_hw_data *ha = vha->hw;
struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+ struct req_que *req = ha->req_q_map[0];
if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, ha->max_q_depth);
+ scsi_activate_tcq(sdev, req->max_q_depth);
else
- scsi_deactivate_tcq(sdev, ha->max_q_depth);
+ scsi_deactivate_tcq(sdev, req->max_q_depth);
rport->dev_loss_tmo = ha->port_down_retry_count;
@@ -1152,8 +1259,9 @@ qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
* supported addressing method.
*/
static void
-qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
+qla2x00_config_dma_addressing(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
/* Assume a 32bit DMA mask. */
ha->flags.enable_64bit_addressing = 0;
@@ -1174,7 +1282,7 @@ qla2x00_config_dma_addressing(scsi_qla_host_t *ha)
}
static void
-qla2x00_enable_intrs(scsi_qla_host_t *ha)
+qla2x00_enable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1189,7 +1297,7 @@ qla2x00_enable_intrs(scsi_qla_host_t *ha)
}
static void
-qla2x00_disable_intrs(scsi_qla_host_t *ha)
+qla2x00_disable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1203,7 +1311,7 @@ qla2x00_disable_intrs(scsi_qla_host_t *ha)
}
static void
-qla24xx_enable_intrs(scsi_qla_host_t *ha)
+qla24xx_enable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -1216,7 +1324,7 @@ qla24xx_enable_intrs(scsi_qla_host_t *ha)
}
static void
-qla24xx_disable_intrs(scsi_qla_host_t *ha)
+qla24xx_disable_intrs(struct qla_hw_data *ha)
{
unsigned long flags = 0;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -1260,6 +1368,10 @@ static struct isp_operations qla2100_isp_ops = {
.read_optrom = qla2x00_read_optrom_data,
.write_optrom = qla2x00_write_optrom_data,
.get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .wrt_req_reg = NULL,
+ .wrt_rsp_reg = NULL,
+ .rd_req_reg = NULL,
};
static struct isp_operations qla2300_isp_ops = {
@@ -1294,6 +1406,10 @@ static struct isp_operations qla2300_isp_ops = {
.read_optrom = qla2x00_read_optrom_data,
.write_optrom = qla2x00_write_optrom_data,
.get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .wrt_req_reg = NULL,
+ .wrt_rsp_reg = NULL,
+ .rd_req_reg = NULL,
};
static struct isp_operations qla24xx_isp_ops = {
@@ -1328,6 +1444,10 @@ static struct isp_operations qla24xx_isp_ops = {
.read_optrom = qla24xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_start_scsi,
+ .wrt_req_reg = qla24xx_wrt_req_reg,
+ .wrt_rsp_reg = qla24xx_wrt_rsp_reg,
+ .rd_req_reg = qla24xx_rd_req_reg,
};
static struct isp_operations qla25xx_isp_ops = {
@@ -1362,10 +1482,14 @@ static struct isp_operations qla25xx_isp_ops = {
.read_optrom = qla25xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_start_scsi,
+ .wrt_req_reg = qla24xx_wrt_req_reg,
+ .wrt_rsp_reg = qla24xx_wrt_rsp_reg,
+ .rd_req_reg = qla24xx_rd_req_reg,
};
static inline void
-qla2x00_set_isp_flags(scsi_qla_host_t *ha)
+qla2x00_set_isp_flags(struct qla_hw_data *ha)
{
ha->device_type = DT_EXTENDED_IDS;
switch (ha->pdev->device) {
@@ -1447,9 +1571,10 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha)
}
static int
-qla2x00_iospace_config(scsi_qla_host_t *ha)
+qla2x00_iospace_config(struct qla_hw_data *ha)
{
resource_size_t pio;
+ uint16_t msix;
if (pci_request_selected_regions(ha->pdev, ha->bars,
QLA2XXX_DRIVER_NAME)) {
@@ -1502,6 +1627,30 @@ skip_pio:
goto iospace_error_exit;
}
+ /* Determine queue resources */
+ ha->max_queues = 1;
+ if (ql2xmaxqueues <= 1 || !IS_QLA25XX(ha))
+ goto mqiobase_exit;
+ ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
+ pci_resource_len(ha->pdev, 3));
+ if (ha->mqiobase) {
+ /* Read MSIX vector size of the board */
+ pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
+ ha->msix_count = msix;
+ /* Max queues are bounded by available msix vectors */
+ /* queue 0 uses two msix vectors */
+ if (ha->msix_count - 1 < ql2xmaxqueues)
+ ha->max_queues = ha->msix_count - 1;
+ else if (ql2xmaxqueues > QLA_MQ_SIZE)
+ ha->max_queues = QLA_MQ_SIZE;
+ else
+ ha->max_queues = ql2xmaxqueues;
+ qla_printk(KERN_INFO, ha,
+ "MSI-X vector count: %d\n", msix);
+ }
+
+mqiobase_exit:
+ ha->msix_count = ha->max_queues + 1;
return (0);
iospace_error_exit:
@@ -1511,25 +1660,25 @@ iospace_error_exit:
static void
qla2xxx_scan_start(struct Scsi_Host *shost)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
- set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
- set_bit(RSCN_UPDATE, &ha->dpc_flags);
- set_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags);
}
static int
qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- scsi_qla_host_t *ha = shost_priv(shost);
+ scsi_qla_host_t *vha = shost_priv(shost);
- if (!ha->host)
+ if (!vha->host)
return 1;
- if (time > ha->loop_reset_delay * HZ)
+ if (time > vha->hw->loop_reset_delay * HZ)
return 1;
- return atomic_read(&ha->loop_state) == LOOP_READY;
+ return atomic_read(&vha->loop_state) == LOOP_READY;
}
/*
@@ -1540,11 +1689,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret = -ENODEV;
struct Scsi_Host *host;
- scsi_qla_host_t *ha;
+ scsi_qla_host_t *base_vha = NULL;
+ struct qla_hw_data *ha;
char pci_info[30];
char fw_str[30];
struct scsi_host_template *sht;
- int bars, mem_only = 0;
+ int bars, max_id, mem_only = 0;
+ uint16_t req_length = 0, rsp_length = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
sht = &qla2x00_driver_template;
@@ -1570,33 +1723,24 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
/* This may fail but that's ok */
pci_enable_pcie_error_reporting(pdev);
- host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
- if (host == NULL) {
- printk(KERN_WARNING
- "qla2xxx: Couldn't allocate host from scsi layer!\n");
- goto probe_disable_device;
+ ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
+ if (!ha) {
+ DEBUG(printk("Unable to allocate memory for ha\n"));
+ goto probe_out;
}
+ ha->pdev = pdev;
/* Clear our data area */
- ha = shost_priv(host);
- memset(ha, 0, sizeof(scsi_qla_host_t));
-
- ha->pdev = pdev;
- ha->host = host;
- ha->host_no = host->host_no;
- sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no);
- ha->parent = NULL;
ha->bars = bars;
ha->mem_only = mem_only;
spin_lock_init(&ha->hardware_lock);
/* Set ISP-type information. */
qla2x00_set_isp_flags(ha);
-
/* Configure PCI I/O space */
ret = qla2x00_iospace_config(ha);
if (ret)
- goto probe_failed;
+ goto probe_hw_failed;
qla_printk(KERN_INFO, ha,
"Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq,
@@ -1604,95 +1748,137 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->prev_topology = 0;
ha->init_cb_size = sizeof(init_cb_t);
- ha->mgmt_svr_loop_id = MANAGEMENT_SERVER + ha->vp_idx;
ha->link_data_rate = PORT_SPEED_UNKNOWN;
ha->optrom_size = OPTROM_SIZE_2300;
- ha->max_q_depth = MAX_Q_DEPTH;
- if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
- ha->max_q_depth = ql2xmaxqdepth;
-
/* Assign ISP specific operations. */
+ max_id = MAX_TARGETS_2200;
if (IS_QLA2100(ha)) {
- host->max_id = MAX_TARGETS_2100;
+ max_id = MAX_TARGETS_2100;
ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
- ha->request_q_length = REQUEST_ENTRY_CNT_2100;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
- host->sg_tablesize = 32;
+ req_length = REQUEST_ENTRY_CNT_2100;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
ha->gid_list_info_size = 4;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA2200(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_2200;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2100;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2100;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
ha->gid_list_info_size = 4;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA23XX(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_2200;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->gid_list_info_size = 6;
if (IS_QLA2322(ha) || IS_QLA6322(ha))
ha->optrom_size = OPTROM_SIZE_2322;
ha->isp_ops = &qla2300_isp_ops;
} else if (IS_QLA24XX_TYPE(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
- ha->mgmt_svr_loop_id = 10 + ha->vp_idx;
ha->gid_list_info_size = 8;
ha->optrom_size = OPTROM_SIZE_24XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX;
ha->isp_ops = &qla24xx_isp_ops;
} else if (IS_QLA25XX(ha)) {
- host->max_id = MAX_TARGETS_2200;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
- ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
- ha->response_q_length = RESPONSE_ENTRY_CNT_2300;
- ha->last_loop_id = SNS_LAST_LOOP_ID_2300;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
- ha->mgmt_svr_loop_id = 10 + ha->vp_idx;
ha->gid_list_info_size = 8;
ha->optrom_size = OPTROM_SIZE_25XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
ha->isp_ops = &qla25xx_isp_ops;
}
- host->can_queue = ha->request_q_length + 128;
mutex_init(&ha->vport_lock);
init_completion(&ha->mbx_cmd_comp);
complete(&ha->mbx_cmd_comp);
init_completion(&ha->mbx_intr_comp);
- INIT_LIST_HEAD(&ha->list);
- INIT_LIST_HEAD(&ha->fcports);
- INIT_LIST_HEAD(&ha->vp_list);
- INIT_LIST_HEAD(&ha->work_list);
-
set_bit(0, (unsigned long *) ha->vp_idx_map);
- qla2x00_config_dma_addressing(ha);
- if (qla2x00_mem_alloc(ha)) {
+ ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
+ if (!ret) {
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to allocate memory for adapter\n");
+ goto probe_hw_failed;
+ }
+
+ req->max_q_depth = MAX_Q_DEPTH;
+ if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
+ req->max_q_depth = ql2xmaxqdepth;
+
+
+ base_vha = qla2x00_create_host(sht, ha);
+ if (!base_vha) {
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for scsi_host\n");
+
ret = -ENOMEM;
+ goto probe_hw_failed;
+ }
+
+ pci_set_drvdata(pdev, base_vha);
+
+ qla2x00_config_dma_addressing(base_vha);
+
+ host = base_vha->host;
+ base_vha->req_ques[0] = req->id;
+ host->can_queue = req->length + 128;
+ if (IS_QLA2XXX_MIDTYPE(ha))
+ base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
+ else
+ base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
+ base_vha->vp_idx;
+ if (IS_QLA2100(ha))
+ host->sg_tablesize = 32;
+ host->max_id = max_id;
+ host->this_id = 255;
+ host->cmd_per_lun = 3;
+ host->unique_id = host->host_no;
+ host->max_cmd_len = MAX_CMDSZ;
+ host->max_channel = MAX_BUSES - 1;
+ host->max_lun = MAX_LUNS;
+ host->transportt = qla2xxx_transport_template;
+
+ /* Set up the irqs */
+ ret = qla2x00_request_irqs(ha, rsp);
+ if (ret)
+ goto probe_failed;
+
+ /* Alloc arrays of request and response ring ptrs */
+ if (!qla2x00_alloc_queues(ha)) {
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to allocate memory for queue"
+ " pointers\n");
goto probe_failed;
}
+ ha->rsp_q_map[0] = rsp;
+ ha->req_q_map[0] = req;
- if (qla2x00_initialize_adapter(ha)) {
+ if (ha->mqenable) {
+ ha->isp_ops->wrt_req_reg = qla25xx_wrt_req_reg;
+ ha->isp_ops->wrt_rsp_reg = qla25xx_wrt_rsp_reg;
+ ha->isp_ops->rd_req_reg = qla25xx_rd_req_reg;
+ }
+
+ if (qla2x00_initialize_adapter(base_vha)) {
qla_printk(KERN_WARNING, ha,
"Failed to initialize adapter\n");
DEBUG2(printk("scsi(%ld): Failed to initialize adapter - "
"Adapter flags %x.\n",
- ha->host_no, ha->device_flags));
+ base_vha->host_no, base_vha->device_flags));
ret = -ENODEV;
goto probe_failed;
@@ -1702,7 +1888,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* Startup the kernel thread for this host adapter
*/
ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
- "%s_dpc", ha->host_str);
+ "%s_dpc", base_vha->host_str);
if (IS_ERR(ha->dpc_thread)) {
qla_printk(KERN_WARNING, ha,
"Unable to start DPC thread!\n");
@@ -1710,28 +1896,17 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
}
- host->this_id = 255;
- host->cmd_per_lun = 3;
- host->unique_id = host->host_no;
- host->max_cmd_len = MAX_CMDSZ;
- host->max_channel = MAX_BUSES - 1;
- host->max_lun = MAX_LUNS;
- host->transportt = qla2xxx_transport_template;
-
- ret = qla2x00_request_irqs(ha);
- if (ret)
- goto probe_failed;
+ list_add_tail(&base_vha->list, &ha->vp_list);
+ base_vha->host->irq = ha->pdev->irq;
/* Initialized the timer */
- qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL);
+ qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n",
- ha->host_no, ha));
+ base_vha->host_no, ha));
- pci_set_drvdata(pdev, ha);
-
- ha->flags.init_done = 1;
- ha->flags.online = 1;
+ base_vha->flags.init_done = 1;
+ base_vha->flags.online = 1;
ret = scsi_add_host(host, &pdev->dev);
if (ret)
@@ -1741,76 +1916,98 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
scsi_scan_host(host);
- qla2x00_alloc_sysfs_attr(ha);
+ qla2x00_alloc_sysfs_attr(base_vha);
- qla2x00_init_host_attr(ha);
+ qla2x00_init_host_attr(base_vha);
- qla2x00_dfs_setup(ha);
+ qla2x00_dfs_setup(base_vha);
qla_printk(KERN_INFO, ha, "\n"
" QLogic Fibre Channel HBA Driver: %s\n"
" QLogic %s - %s\n"
" ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n",
qla2x00_version_str, ha->model_number,
- ha->model_desc ? ha->model_desc: "", pdev->device,
- ha->isp_ops->pci_info_str(ha, pci_info), pci_name(pdev),
- ha->flags.enable_64bit_addressing ? '+': '-', ha->host_no,
- ha->isp_ops->fw_version_str(ha, fw_str));
+ ha->model_desc ? ha->model_desc : "", pdev->device,
+ ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev),
+ ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no,
+ ha->isp_ops->fw_version_str(base_vha, fw_str));
return 0;
probe_failed:
- qla2x00_free_device(ha);
+ qla2x00_free_que(ha, req, rsp);
+ qla2x00_free_device(base_vha);
- scsi_host_put(host);
+ scsi_host_put(base_vha->host);
-probe_disable_device:
- pci_disable_device(pdev);
+probe_hw_failed:
+ if (ha->iobase)
+ iounmap(ha->iobase);
+
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
probe_out:
+ pci_disable_device(pdev);
return ret;
}
static void
qla2x00_remove_one(struct pci_dev *pdev)
{
- scsi_qla_host_t *ha, *vha, *temp;
+ scsi_qla_host_t *base_vha, *vha, *temp;
+ struct qla_hw_data *ha;
+
+ base_vha = pci_get_drvdata(pdev);
+ ha = base_vha->hw;
+
+ list_for_each_entry_safe(vha, temp, &ha->vp_list, list) {
+ if (vha && vha->fc_vport)
+ fc_vport_terminate(vha->fc_vport);
+ }
- ha = pci_get_drvdata(pdev);
+ set_bit(UNLOADING, &base_vha->dpc_flags);
- list_for_each_entry_safe(vha, temp, &ha->vp_list, vp_list)
- fc_vport_terminate(vha->fc_vport);
+ qla2x00_dfs_remove(base_vha);
- set_bit(UNLOADING, &ha->dpc_flags);
+ qla84xx_put_chip(base_vha);
- qla2x00_dfs_remove(ha);
+ qla2x00_free_sysfs_attr(base_vha);
- qla84xx_put_chip(ha);
+ fc_remove_host(base_vha->host);
- qla2x00_free_sysfs_attr(ha);
+ scsi_remove_host(base_vha->host);
- fc_remove_host(ha->host);
+ qla2x00_free_device(base_vha);
- scsi_remove_host(ha->host);
+ scsi_host_put(base_vha->host);
- qla2x00_free_device(ha);
+ if (ha->iobase)
+ iounmap(ha->iobase);
- scsi_host_put(ha->host);
+ if (ha->mqiobase)
+ iounmap(ha->mqiobase);
+
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static void
-qla2x00_free_device(scsi_qla_host_t *ha)
+qla2x00_free_device(scsi_qla_host_t *vha)
{
- qla2x00_abort_all_cmds(ha, DID_NO_CONNECT << 16);
+ struct qla_hw_data *ha = vha->hw;
+ qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
/* Disable timer */
- if (ha->timer_active)
- qla2x00_stop_timer(ha);
+ if (vha->timer_active)
+ qla2x00_stop_timer(vha);
- ha->flags.online = 0;
+ vha->flags.online = 0;
/* Kill the kernel thread for this host */
if (ha->dpc_thread) {
@@ -1825,45 +2022,41 @@ qla2x00_free_device(scsi_qla_host_t *ha)
}
if (ha->flags.fce_enabled)
- qla2x00_disable_fce_trace(ha, NULL, NULL);
+ qla2x00_disable_fce_trace(vha, NULL, NULL);
if (ha->eft)
- qla2x00_disable_eft_trace(ha);
+ qla2x00_disable_eft_trace(vha);
/* Stop currently executing firmware. */
- qla2x00_try_to_stop_firmware(ha);
+ qla2x00_try_to_stop_firmware(vha);
/* turn-off interrupts on the card */
if (ha->interrupts_on)
ha->isp_ops->disable_intrs(ha);
- qla2x00_mem_free(ha);
+ qla2x00_free_irqs(vha);
- qla2x00_free_irqs(ha);
+ qla2x00_mem_free(ha);
- /* release io space registers */
- if (ha->iobase)
- iounmap(ha->iobase);
- pci_release_selected_regions(ha->pdev, ha->bars);
+ qla2x00_free_queues(ha);
}
static inline void
-qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
+qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
int defer)
{
struct fc_rport *rport;
- scsi_qla_host_t *pha = to_qla_parent(ha);
if (!fcport->rport)
return;
rport = fcport->rport;
if (defer) {
- spin_lock_irq(ha->host->host_lock);
+ spin_lock_irq(vha->host->host_lock);
fcport->drport = rport;
- spin_unlock_irq(ha->host->host_lock);
- set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags);
- qla2xxx_wake_dpc(pha);
+ spin_unlock_irq(vha->host->host_lock);
+ set_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
} else
fc_remote_port_delete(rport);
}
@@ -1877,13 +2070,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport,
*
* Context:
*/
-void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
+void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
int do_login, int defer)
{
if (atomic_read(&fcport->state) == FCS_ONLINE &&
- ha->vp_idx == fcport->vp_idx)
- qla2x00_schedule_rport_del(ha, fcport, defer);
-
+ vha->vp_idx == fcport->vp_idx) {
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ }
/*
* We may need to retry the login, so don't change the state of the
* port but do the retries.
@@ -1895,13 +2089,13 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
return;
if (fcport->login_retry == 0) {
- fcport->login_retry = ha->login_retry_count;
- set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
+ fcport->login_retry = vha->hw->login_retry_count;
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
DEBUG(printk("scsi(%ld): Port login retry: "
"%02x%02x%02x%02x%02x%02x%02x%02x, "
"id = 0x%04x retry cnt=%d\n",
- ha->host_no,
+ vha->host_no,
fcport->port_name[0],
fcport->port_name[1],
fcport->port_name[2],
@@ -1929,13 +2123,12 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *ha, fc_port_t *fcport,
* Context:
*/
void
-qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
{
fc_port_t *fcport;
- scsi_qla_host_t *pha = to_qla_parent(ha);
- list_for_each_entry(fcport, &pha->fcports, list) {
- if (ha->vp_idx != fcport->vp_idx)
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (vha->vp_idx != fcport->vp_idx)
continue;
/*
* No point in marking the device as lost, if the device is
@@ -1943,9 +2136,11 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
*/
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
continue;
- if (atomic_read(&fcport->state) == FCS_ONLINE)
- qla2x00_schedule_rport_del(ha, fcport, defer);
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ if (atomic_read(&fcport->state) == FCS_ONLINE) {
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ } else
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
}
}
@@ -1958,105 +2153,153 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer)
* !0 = failure.
*/
static int
-qla2x00_mem_alloc(scsi_qla_host_t *ha)
+qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
+ struct req_que **req, struct rsp_que **rsp)
{
char name[16];
- ha->request_ring = dma_alloc_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t), &ha->request_dma,
- GFP_KERNEL);
- if (!ha->request_ring)
- goto fail;
-
- ha->response_ring = dma_alloc_coherent(&ha->pdev->dev,
- (ha->response_q_length + 1) * sizeof(response_t),
- &ha->response_dma, GFP_KERNEL);
- if (!ha->response_ring)
- goto fail_free_request_ring;
-
- ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
- &ha->gid_list_dma, GFP_KERNEL);
- if (!ha->gid_list)
- goto fail_free_response_ring;
+ ha->init_cb_size = sizeof(init_cb_t);
+ if (IS_QLA2XXX_MIDTYPE(ha))
+ ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
- &ha->init_cb_dma, GFP_KERNEL);
+ &ha->init_cb_dma, GFP_KERNEL);
if (!ha->init_cb)
- goto fail_free_gid_list;
+ goto fail;
- snprintf(name, sizeof(name), "%s_%ld", QLA2XXX_DRIVER_NAME,
- ha->host_no);
- ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
- DMA_POOL_SIZE, 8, 0);
- if (!ha->s_dma_pool)
+ ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+ &ha->gid_list_dma, GFP_KERNEL);
+ if (!ha->gid_list)
goto fail_free_init_cb;
ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
if (!ha->srb_mempool)
- goto fail_free_s_dma_pool;
+ goto fail_free_gid_list;
/* Get memory for cached NVRAM */
ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
if (!ha->nvram)
goto fail_free_srb_mempool;
+ snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME,
+ ha->pdev->device);
+ ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ DMA_POOL_SIZE, 8, 0);
+ if (!ha->s_dma_pool)
+ goto fail_free_nvram;
+
/* Allocate memory for SNS commands */
if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
- /* Get consistent memory allocated for SNS commands */
+ /* Get consistent memory allocated for SNS commands */
ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
- sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
+ sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
if (!ha->sns_cmd)
- goto fail_free_nvram;
+ goto fail_dma_pool;
} else {
- /* Get consistent memory allocated for MS IOCB */
+ /* Get consistent memory allocated for MS IOCB */
ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
- &ha->ms_iocb_dma);
+ &ha->ms_iocb_dma);
if (!ha->ms_iocb)
- goto fail_free_nvram;
-
- /* Get consistent memory allocated for CT SNS commands */
+ goto fail_dma_pool;
+ /* Get consistent memory allocated for CT SNS commands */
ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
- sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
+ sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
if (!ha->ct_sns)
goto fail_free_ms_iocb;
}
- return 0;
+ /* Allocate memory for request ring */
+ *req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (!*req) {
+ DEBUG(printk("Unable to allocate memory for req\n"));
+ goto fail_req;
+ }
+ (*req)->length = req_len;
+ (*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*req)->length + 1) * sizeof(request_t),
+ &(*req)->dma, GFP_KERNEL);
+ if (!(*req)->ring) {
+ DEBUG(printk("Unable to allocate memory for req_ring\n"));
+ goto fail_req_ring;
+ }
+ /* Allocate memory for response ring */
+ *rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (!*rsp) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for rsp\n");
+ goto fail_rsp;
+ }
+ (*rsp)->hw = ha;
+ (*rsp)->length = rsp_len;
+ (*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*rsp)->length + 1) * sizeof(response_t),
+ &(*rsp)->dma, GFP_KERNEL);
+ if (!(*rsp)->ring) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for rsp_ring\n");
+ goto fail_rsp_ring;
+ }
+ (*req)->rsp = *rsp;
+ (*rsp)->req = *req;
+ /* Allocate memory for NVRAM data for vports */
+ if (ha->nvram_npiv_size) {
+ ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
+ ha->nvram_npiv_size, GFP_KERNEL);
+ if (!ha->npiv_info) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to allocate memory for npiv info\n");
+ goto fail_npiv_info;
+ }
+ } else
+ ha->npiv_info = NULL;
+ INIT_LIST_HEAD(&ha->vp_list);
+ return 1;
+
+fail_npiv_info:
+ dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
+ sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
+ (*rsp)->ring = NULL;
+ (*rsp)->dma = 0;
+fail_rsp_ring:
+ kfree(*rsp);
+fail_rsp:
+ dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
+ sizeof(request_t), (*req)->ring, (*req)->dma);
+ (*req)->ring = NULL;
+ (*req)->dma = 0;
+fail_req_ring:
+ kfree(*req);
+fail_req:
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+ ha->ct_sns, ha->ct_sns_dma);
+ ha->ct_sns = NULL;
+ ha->ct_sns_dma = 0;
fail_free_ms_iocb:
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
ha->ms_iocb_dma = 0;
+fail_dma_pool:
+ dma_pool_destroy(ha->s_dma_pool);
+ ha->s_dma_pool = NULL;
fail_free_nvram:
kfree(ha->nvram);
ha->nvram = NULL;
fail_free_srb_mempool:
mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
-fail_free_s_dma_pool:
- dma_pool_destroy(ha->s_dma_pool);
- ha->s_dma_pool = NULL;
-fail_free_init_cb:
- dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
- ha->init_cb_dma);
- ha->init_cb = NULL;
- ha->init_cb_dma = 0;
fail_free_gid_list:
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
- ha->gid_list_dma);
+ ha->gid_list_dma);
ha->gid_list = NULL;
ha->gid_list_dma = 0;
-fail_free_response_ring:
- dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) *
- sizeof(response_t), ha->response_ring, ha->response_dma);
- ha->response_ring = NULL;
- ha->response_dma = 0;
-fail_free_request_ring:
- dma_free_coherent(&ha->pdev->dev, (ha->request_q_length + 1) *
- sizeof(request_t), ha->request_ring, ha->request_dma);
- ha->request_ring = NULL;
- ha->request_dma = 0;
+fail_free_init_cb:
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
+ ha->init_cb_dma);
+ ha->init_cb = NULL;
+ ha->init_cb_dma = 0;
fail:
+ DEBUG(printk("%s: Memory allocation failure\n", __func__));
return -ENOMEM;
}
@@ -2068,32 +2311,29 @@ fail:
* ha = adapter block pointer.
*/
static void
-qla2x00_mem_free(scsi_qla_host_t *ha)
+qla2x00_mem_free(struct qla_hw_data *ha)
{
- struct list_head *fcpl, *fcptemp;
- fc_port_t *fcport;
-
if (ha->srb_mempool)
mempool_destroy(ha->srb_mempool);
if (ha->fce)
dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
- ha->fce_dma);
+ ha->fce_dma);
if (ha->fw_dump) {
if (ha->eft)
dma_free_coherent(&ha->pdev->dev,
- ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
+ ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
vfree(ha->fw_dump);
}
if (ha->sns_cmd)
dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
- ha->sns_cmd, ha->sns_cmd_dma);
+ ha->sns_cmd, ha->sns_cmd_dma);
if (ha->ct_sns)
dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
- ha->ct_sns, ha->ct_sns_dma);
+ ha->ct_sns, ha->ct_sns_dma);
if (ha->sfp_data)
dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
@@ -2104,23 +2344,18 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
if (ha->s_dma_pool)
dma_pool_destroy(ha->s_dma_pool);
- if (ha->init_cb)
- dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
- ha->init_cb, ha->init_cb_dma);
if (ha->gid_list)
dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
- ha->gid_list_dma);
+ ha->gid_list_dma);
- if (ha->response_ring)
- dma_free_coherent(&ha->pdev->dev,
- (ha->response_q_length + 1) * sizeof(response_t),
- ha->response_ring, ha->response_dma);
- if (ha->request_ring)
- dma_free_coherent(&ha->pdev->dev,
- (ha->request_q_length + 1) * sizeof(request_t),
- ha->request_ring, ha->request_dma);
+ if (ha->init_cb)
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
+ ha->init_cb, ha->init_cb_dma);
+ vfree(ha->optrom_buffer);
+ kfree(ha->nvram);
+ kfree(ha->npiv_info);
ha->srb_mempool = NULL;
ha->eft = NULL;
@@ -2139,30 +2374,45 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
ha->gid_list = NULL;
ha->gid_list_dma = 0;
- ha->response_ring = NULL;
- ha->response_dma = 0;
- ha->request_ring = NULL;
- ha->request_dma = 0;
+ ha->fw_dump = NULL;
+ ha->fw_dumped = 0;
+ ha->fw_dump_reading = 0;
+}
- list_for_each_safe(fcpl, fcptemp, &ha->fcports) {
- fcport = list_entry(fcpl, fc_port_t, list);
+struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
+ struct qla_hw_data *ha)
+{
+ struct Scsi_Host *host;
+ struct scsi_qla_host *vha = NULL;
- /* fc ports */
- list_del_init(&fcport->list);
- kfree(fcport);
+ host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
+ if (host == NULL) {
+ printk(KERN_WARNING
+ "qla2xxx: Couldn't allocate host from scsi layer!\n");
+ goto fail;
}
- INIT_LIST_HEAD(&ha->fcports);
- ha->fw_dump = NULL;
- ha->fw_dumped = 0;
- ha->fw_dump_reading = 0;
+ /* Clear our data area */
+ vha = shost_priv(host);
+ memset(vha, 0, sizeof(scsi_qla_host_t));
- vfree(ha->optrom_buffer);
- kfree(ha->nvram);
+ vha->host = host;
+ vha->host_no = host->host_no;
+ vha->hw = ha;
+
+ INIT_LIST_HEAD(&vha->vp_fcports);
+ INIT_LIST_HEAD(&vha->work_list);
+ INIT_LIST_HEAD(&vha->list);
+
+ sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
+ return vha;
+
+fail:
+ return vha;
}
static struct qla_work_evt *
-qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
+qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type,
int locked)
{
struct qla_work_evt *e;
@@ -2179,42 +2429,42 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
}
static int
-qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
+qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e, int locked)
{
unsigned long uninitialized_var(flags);
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
if (!locked)
- spin_lock_irqsave(&pha->hardware_lock, flags);
- list_add_tail(&e->list, &ha->work_list);
- qla2xxx_wake_dpc(ha);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ list_add_tail(&e->list, &vha->work_list);
+ qla2xxx_wake_dpc(vha);
if (!locked)
- spin_unlock_irqrestore(&pha->hardware_lock, flags);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
}
int
-qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code,
+qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
u32 data)
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_AEN, 1);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.aen.code = code;
e->u.aen.data = data;
- return qla2x00_post_work(ha, e, 1);
+ return qla2x00_post_work(vha, e, 1);
}
int
-qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
+qla2x00_post_hwe_work(struct scsi_qla_host *vha, uint16_t code, uint16_t d1,
uint16_t d2, uint16_t d3)
{
struct qla_work_evt *e;
- e = qla2x00_alloc_work(ha, QLA_EVT_HWE_LOG, 1);
+ e = qla2x00_alloc_work(vha, QLA_EVT_HWE_LOG, 1);
if (!e)
return QLA_FUNCTION_FAILED;
@@ -2222,36 +2472,95 @@ qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
e->u.hwe.d1 = d1;
e->u.hwe.d2 = d2;
e->u.hwe.d3 = d3;
- return qla2x00_post_work(ha, e, 1);
+ return qla2x00_post_work(vha, e, 1);
}
static void
-qla2x00_do_work(struct scsi_qla_host *ha)
+qla2x00_do_work(struct scsi_qla_host *vha)
{
struct qla_work_evt *e;
- scsi_qla_host_t *pha = to_qla_parent(ha);
+ struct qla_hw_data *ha = vha->hw;
- spin_lock_irq(&pha->hardware_lock);
- while (!list_empty(&ha->work_list)) {
- e = list_entry(ha->work_list.next, struct qla_work_evt, list);
+ spin_lock_irq(&ha->hardware_lock);
+ while (!list_empty(&vha->work_list)) {
+ e = list_entry(vha->work_list.next, struct qla_work_evt, list);
list_del_init(&e->list);
- spin_unlock_irq(&pha->hardware_lock);
+ spin_unlock_irq(&ha->hardware_lock);
switch (e->type) {
case QLA_EVT_AEN:
- fc_host_post_event(ha->host, fc_get_event_number(),
+ fc_host_post_event(vha->host, fc_get_event_number(),
e->u.aen.code, e->u.aen.data);
break;
case QLA_EVT_HWE_LOG:
- qla2xxx_hw_event_log(ha, e->u.hwe.code, e->u.hwe.d1,
+ qla2xxx_hw_event_log(vha, e->u.hwe.code, e->u.hwe.d1,
e->u.hwe.d2, e->u.hwe.d3);
break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
- spin_lock_irq(&pha->hardware_lock);
+ spin_lock_irq(&ha->hardware_lock);
+ }
+ spin_unlock_irq(&ha->hardware_lock);
+}
+/* Relogins all the fcports of a vport
+ * Context: dpc thread
+ */
+void qla2x00_relogin(struct scsi_qla_host *vha)
+{
+ fc_port_t *fcport;
+ uint8_t status;
+ uint16_t next_loopid = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ /*
+ * If the port is not ONLINE then try to login
+ * to it if we haven't run out of retries.
+ */
+ if (atomic_read(&fcport->state) !=
+ FCS_ONLINE && fcport->login_retry) {
+
+ if (fcport->flags & FCF_FABRIC_DEVICE) {
+ if (fcport->flags & FCF_TAPE_PRESENT)
+ ha->isp_ops->fabric_logout(vha,
+ fcport->loop_id,
+ fcport->d_id.b.domain,
+ fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+
+ status = qla2x00_fabric_login(vha, fcport,
+ &next_loopid);
+ } else
+ status = qla2x00_local_device_login(vha,
+ fcport);
+
+ fcport->login_retry--;
+ if (status == QLA_SUCCESS) {
+ fcport->old_loop_id = fcport->loop_id;
+
+ DEBUG(printk("scsi(%ld): port login OK: logged "
+ "in ID 0x%x\n", vha->host_no, fcport->loop_id));
+
+ qla2x00_update_fcport(vha, fcport);
+
+ } else if (status == 1) {
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ /* retry the login again */
+ DEBUG(printk("scsi(%ld): Retrying"
+ " %d login again loop_id 0x%x\n",
+ vha->host_no, fcport->login_retry,
+ fcport->loop_id));
+ } else {
+ fcport->login_retry = 0;
+ }
+
+ if (fcport->login_retry == 0 && status != QLA_SUCCESS)
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
}
- spin_unlock_irq(&pha->hardware_lock);
}
/**************************************************************************
@@ -2271,15 +2580,11 @@ static int
qla2x00_do_dpc(void *data)
{
int rval;
- scsi_qla_host_t *ha;
- fc_port_t *fcport;
- uint8_t status;
- uint16_t next_loopid;
- struct scsi_qla_host *vha;
- int i;
+ scsi_qla_host_t *base_vha;
+ struct qla_hw_data *ha;
-
- ha = (scsi_qla_host_t *)data;
+ ha = (struct qla_hw_data *)data;
+ base_vha = pci_get_drvdata(ha->pdev);
set_user_nice(current, -20);
@@ -2293,10 +2598,10 @@ qla2x00_do_dpc(void *data)
DEBUG3(printk("qla2x00: DPC handler waking up\n"));
/* Initialization not yet finished. Don't do anything yet. */
- if (!ha->flags.init_done)
+ if (!base_vha->flags.init_done)
continue;
- DEBUG3(printk("scsi(%ld): DPC handler\n", ha->host_no));
+ DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no));
ha->dpc_active = 1;
@@ -2305,149 +2610,98 @@ qla2x00_do_dpc(void *data)
continue;
}
- qla2x00_do_work(ha);
+ qla2x00_do_work(base_vha);
- if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
+ if (test_and_clear_bit(ISP_ABORT_NEEDED,
+ &base_vha->dpc_flags)) {
DEBUG(printk("scsi(%ld): dpc: sched "
"qla2x00_abort_isp ha = %p\n",
- ha->host_no, ha));
+ base_vha->host_no, ha));
if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
- &ha->dpc_flags))) {
+ &base_vha->dpc_flags))) {
- if (qla2x00_abort_isp(ha)) {
+ if (qla2x00_abort_isp(base_vha)) {
/* failed. retry later */
set_bit(ISP_ABORT_NEEDED,
- &ha->dpc_flags);
- }
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- }
-
- for_each_mapped_vp_idx(ha, i) {
- list_for_each_entry(vha, &ha->vp_list,
- vp_list) {
- if (i == vha->vp_idx) {
- set_bit(ISP_ABORT_NEEDED,
- &vha->dpc_flags);
- break;
- }
+ &base_vha->dpc_flags);
}
+ clear_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags);
}
DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) {
- qla2x00_update_fcports(ha);
- clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags);
+ if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
+ qla2x00_update_fcports(base_vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
}
- if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
- (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
+ if (test_and_clear_bit(RESET_MARKER_NEEDED,
+ &base_vha->dpc_flags) &&
+ (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n",
- ha->host_no));
+ base_vha->host_no));
- qla2x00_rst_aen(ha);
- clear_bit(RESET_ACTIVE, &ha->dpc_flags);
+ qla2x00_rst_aen(base_vha);
+ clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
}
/* Retry each device up to login retry count */
- if ((test_and_clear_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
- !test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) &&
- atomic_read(&ha->loop_state) != LOOP_DOWN) {
+ if ((test_and_clear_bit(RELOGIN_NEEDED,
+ &base_vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
DEBUG(printk("scsi(%ld): qla2x00_port_login()\n",
- ha->host_no));
-
- next_loopid = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
- /*
- * If the port is not ONLINE then try to login
- * to it if we haven't run out of retries.
- */
- if (atomic_read(&fcport->state) != FCS_ONLINE &&
- fcport->login_retry) {
-
- if (fcport->flags & FCF_FABRIC_DEVICE) {
- if (fcport->flags &
- FCF_TAPE_PRESENT)
- ha->isp_ops->fabric_logout(
- ha, fcport->loop_id,
- fcport->d_id.b.domain,
- fcport->d_id.b.area,
- fcport->d_id.b.al_pa);
- status = qla2x00_fabric_login(
- ha, fcport, &next_loopid);
- } else
- status =
- qla2x00_local_device_login(
- ha, fcport);
-
- fcport->login_retry--;
- if (status == QLA_SUCCESS) {
- fcport->old_loop_id = fcport->loop_id;
-
- DEBUG(printk("scsi(%ld): port login OK: logged in ID 0x%x\n",
- ha->host_no, fcport->loop_id));
-
- qla2x00_update_fcport(ha,
- fcport);
- } else if (status == 1) {
- set_bit(RELOGIN_NEEDED, &ha->dpc_flags);
- /* retry the login again */
- DEBUG(printk("scsi(%ld): Retrying %d login again loop_id 0x%x\n",
- ha->host_no,
- fcport->login_retry, fcport->loop_id));
- } else {
- fcport->login_retry = 0;
- }
- if (fcport->login_retry == 0 && status != QLA_SUCCESS)
- fcport->loop_id = FC_NO_LOOP_ID;
- }
- if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
- break;
- }
+ base_vha->host_no));
+ qla2x00_relogin(base_vha);
+
DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
+ &base_vha->dpc_flags)) {
DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
- ha->host_no));
+ base_vha->host_no));
if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
- &ha->dpc_flags))) {
+ &base_vha->dpc_flags))) {
- rval = qla2x00_loop_resync(ha);
+ rval = qla2x00_loop_resync(base_vha);
- clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
+ clear_bit(LOOP_RESYNC_ACTIVE,
+ &base_vha->dpc_flags);
}
DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n",
- ha->host_no));
+ base_vha->host_no));
}
- if (test_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags) &&
- atomic_read(&ha->loop_state) == LOOP_READY) {
- clear_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags);
- qla2xxx_flash_npiv_conf(ha);
+ if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) == LOOP_READY) {
+ clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_flash_npiv_conf(base_vha);
}
if (!ha->interrupts_on)
ha->isp_ops->enable_intrs(ha);
- if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags))
- ha->isp_ops->beacon_blink(ha);
+ if (test_and_clear_bit(BEACON_BLINK_NEEDED,
+ &base_vha->dpc_flags))
+ ha->isp_ops->beacon_blink(base_vha);
- qla2x00_do_dpc_all_vps(ha);
+ qla2x00_do_dpc_all_vps(base_vha);
ha->dpc_active = 0;
} /* End of while(1) */
- DEBUG(printk("scsi(%ld): DPC handler exiting\n", ha->host_no));
+ DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no));
/*
* Make sure that nobody tries to wake us up again.
@@ -2458,11 +2712,12 @@ qla2x00_do_dpc(void *data)
}
void
-qla2xxx_wake_dpc(scsi_qla_host_t *ha)
+qla2xxx_wake_dpc(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct task_struct *t = ha->dpc_thread;
- if (!test_bit(UNLOADING, &ha->dpc_flags) && t)
+ if (!test_bit(UNLOADING, &vha->dpc_flags) && t)
wake_up_process(t);
}
@@ -2474,26 +2729,26 @@ qla2xxx_wake_dpc(scsi_qla_host_t *ha)
* ha = adapter block pointer.
*/
static void
-qla2x00_rst_aen(scsi_qla_host_t *ha)
+qla2x00_rst_aen(scsi_qla_host_t *vha)
{
- if (ha->flags.online && !ha->flags.reset_active &&
- !atomic_read(&ha->loop_down_timer) &&
- !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags))) {
+ if (vha->flags.online && !vha->flags.reset_active &&
+ !atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) {
do {
- clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags);
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
/*
* Issue marker command only when we are going to start
* the I/O.
*/
- ha->marker_needed = 1;
- } while (!atomic_read(&ha->loop_down_timer) &&
- (test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags)));
+ vha->marker_needed = 1;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ (test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)));
}
}
static void
-qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_sp_free_dma(srb_t *sp)
{
struct scsi_cmnd *cmd = sp->cmd;
@@ -2505,11 +2760,11 @@ qla2x00_sp_free_dma(scsi_qla_host_t *ha, srb_t *sp)
}
void
-qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
+qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp)
{
struct scsi_cmnd *cmd = sp->cmd;
- qla2x00_sp_free_dma(ha, sp);
+ qla2x00_sp_free_dma(sp);
mempool_free(sp, ha->srb_mempool);
@@ -2525,7 +2780,7 @@ qla2x00_sp_compl(scsi_qla_host_t *ha, srb_t *sp)
* Context: Interrupt
***************************************************************************/
void
-qla2x00_timer(scsi_qla_host_t *ha)
+qla2x00_timer(scsi_qla_host_t *vha)
{
unsigned long cpu_flags = 0;
fc_port_t *fcport;
@@ -2533,8 +2788,8 @@ qla2x00_timer(scsi_qla_host_t *ha)
int index;
srb_t *sp;
int t;
- scsi_qla_host_t *pha = to_qla_parent(ha);
-
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
/*
* Ports - Port down timer.
*
@@ -2543,7 +2798,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
* the port it marked DEAD.
*/
t = 0;
- list_for_each_entry(fcport, &ha->fcports, list) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
if (fcport->port_type != FCT_TARGET)
continue;
@@ -2557,7 +2812,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
DEBUG(printk("scsi(%ld): fcport-%d - port retry count: "
"%d remaining\n",
- ha->host_no,
+ vha->host_no,
t, atomic_read(&fcport->port_down_timer)));
}
t++;
@@ -2565,30 +2820,32 @@ qla2x00_timer(scsi_qla_host_t *ha)
/* Loop down handler. */
- if (atomic_read(&ha->loop_down_timer) > 0 &&
- !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) {
+ if (atomic_read(&vha->loop_down_timer) > 0 &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ && vha->flags.online) {
- if (atomic_read(&ha->loop_down_timer) ==
- ha->loop_down_abort_time) {
+ if (atomic_read(&vha->loop_down_timer) ==
+ vha->loop_down_abort_time) {
DEBUG(printk("scsi(%ld): Loop Down - aborting the "
"queues before time expire\n",
- ha->host_no));
+ vha->host_no));
- if (!IS_QLA2100(ha) && ha->link_down_timeout)
- atomic_set(&ha->loop_state, LOOP_DEAD);
+ if (!IS_QLA2100(ha) && vha->link_down_timeout)
+ atomic_set(&vha->loop_state, LOOP_DEAD);
/* Schedule an ISP abort to return any tape commands. */
/* NPIV - scan physical port only */
- if (!ha->parent) {
+ if (!vha->vp_idx) {
spin_lock_irqsave(&ha->hardware_lock,
cpu_flags);
+ req = ha->req_q_map[0];
for (index = 1;
index < MAX_OUTSTANDING_COMMANDS;
index++) {
fc_port_t *sfcp;
- sp = ha->outstanding_cmds[index];
+ sp = req->outstanding_cmds[index];
if (!sp)
continue;
sfcp = sp->fcport;
@@ -2596,63 +2853,63 @@ qla2x00_timer(scsi_qla_host_t *ha)
continue;
set_bit(ISP_ABORT_NEEDED,
- &ha->dpc_flags);
+ &vha->dpc_flags);
break;
}
spin_unlock_irqrestore(&ha->hardware_lock,
- cpu_flags);
+ cpu_flags);
}
- set_bit(ABORT_QUEUES_NEEDED, &ha->dpc_flags);
+ set_bit(ABORT_QUEUES_NEEDED, &vha->dpc_flags);
start_dpc++;
}
/* if the loop has been down for 4 minutes, reinit adapter */
- if (atomic_dec_and_test(&ha->loop_down_timer) != 0) {
+ if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
DEBUG(printk("scsi(%ld): Loop down exceed 4 mins - "
"restarting queues.\n",
- ha->host_no));
+ vha->host_no));
- set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
+ set_bit(RESTART_QUEUES_NEEDED, &vha->dpc_flags);
start_dpc++;
- if (!(ha->device_flags & DFLG_NO_CABLE) &&
- !ha->parent) {
+ if (!(vha->device_flags & DFLG_NO_CABLE) &&
+ !vha->vp_idx) {
DEBUG(printk("scsi(%ld): Loop down - "
"aborting ISP.\n",
- ha->host_no));
+ vha->host_no));
qla_printk(KERN_WARNING, ha,
"Loop down - aborting ISP.\n");
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
}
}
DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n",
- ha->host_no,
- atomic_read(&ha->loop_down_timer)));
+ vha->host_no,
+ atomic_read(&vha->loop_down_timer)));
}
/* Check if beacon LED needs to be blinked */
if (ha->beacon_blink_led == 1) {
- set_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags);
+ set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
start_dpc++;
}
/* Process any deferred work. */
- if (!list_empty(&ha->work_list))
+ if (!list_empty(&vha->work_list))
start_dpc++;
/* Schedule the DPC routine if needed */
- if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
- test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
- test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) ||
+ if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) ||
start_dpc ||
- test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
- test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
- test_bit(VP_DPC_NEEDED, &ha->dpc_flags) ||
- test_bit(RELOGIN_NEEDED, &ha->dpc_flags)))
- qla2xxx_wake_dpc(pha);
+ test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) ||
+ test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) ||
+ test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
+ test_bit(RELOGIN_NEEDED, &vha->dpc_flags)))
+ qla2xxx_wake_dpc(vha);
- qla2x00_restart_timer(ha, WATCH_INTERVAL);
+ qla2x00_restart_timer(vha, WATCH_INTERVAL);
}
/* Firmware interface routines. */
@@ -2684,8 +2941,9 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
};
struct fw_blob *
-qla2x00_request_firmware(scsi_qla_host_t *ha)
+qla2x00_request_firmware(scsi_qla_host_t *vha)
{
+ struct qla_hw_data *ha = vha->hw;
struct fw_blob *blob;
blob = NULL;
@@ -2709,7 +2967,7 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
DEBUG2(printk("scsi(%ld): Failed to load firmware image "
- "(%s).\n", ha->host_no, blob->name));
+ "(%s).\n", vha->host_no, blob->name));
blob->fw = NULL;
blob = NULL;
goto out;
@@ -2754,7 +3012,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
int risc_paused = 0;
uint32_t stat;
unsigned long flags;
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
@@ -2777,7 +3036,7 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
if (risc_paused) {
qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, "
"Dumping firmware!\n");
- ha->isp_ops->fw_dump(ha, 0);
+ ha->isp_ops->fw_dump(base_vha, 0);
return PCI_ERS_RESULT_NEED_RESET;
} else
@@ -2788,7 +3047,8 @@ static pci_ers_result_t
qla2xxx_pci_slot_reset(struct pci_dev *pdev)
{
pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
int rc;
if (ha->mem_only)
@@ -2804,13 +3064,13 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
}
pci_set_master(pdev);
- if (ha->isp_ops->pci_config(ha))
+ if (ha->isp_ops->pci_config(base_vha))
return ret;
- set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
- if (qla2x00_abort_isp(ha)== QLA_SUCCESS)
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS)
ret = PCI_ERS_RESULT_RECOVERED;
- clear_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags);
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
return ret;
}
@@ -2818,10 +3078,11 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
static void
qla2xxx_pci_resume(struct pci_dev *pdev)
{
- scsi_qla_host_t *ha = pci_get_drvdata(pdev);
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
int ret;
- ret = qla2x00_wait_for_hba_online(ha);
+ ret = qla2x00_wait_for_hba_online(base_vha);
if (ret != QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
"the device failed to resume I/O "
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index e4af678eb2d6..c538ee1b1a31 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -10,10 +10,6 @@
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
-static uint16_t qla2x00_nvram_request(scsi_qla_host_t *, uint32_t);
-static void qla2x00_nv_deselect(scsi_qla_host_t *);
-static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
-
/*
* NVRAM support routines
*/
@@ -23,7 +19,7 @@ static void qla2x00_nv_write(scsi_qla_host_t *, uint16_t);
* @ha: HA context
*/
static void
-qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
+qla2x00_lock_nvram_access(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -56,7 +52,7 @@ qla2x00_lock_nvram_access(scsi_qla_host_t *ha)
* @ha: HA context
*/
static void
-qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
+qla2x00_unlock_nvram_access(struct qla_hw_data *ha)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -67,6 +63,84 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
}
/**
+ * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
+ * @ha: HA context
+ * @data: Serial interface selector
+ */
+static void
+qla2x00_nv_write(struct qla_hw_data *ha, uint16_t data)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_CLOCK |
+ NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
+ * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
+ * NVRAM.
+ * @ha: HA context
+ * @nv_cmd: NVRAM command
+ *
+ * Bit definitions for NVRAM command:
+ *
+ * Bit 26 = start bit
+ * Bit 25, 24 = opcode
+ * Bit 23-16 = address
+ * Bit 15-0 = write data
+ *
+ * Returns the word read from nvram @addr.
+ */
+static uint16_t
+qla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd)
+{
+ uint8_t cnt;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint16_t data = 0;
+ uint16_t reg_data;
+
+ /* Send command to NVRAM. */
+ nv_cmd <<= 5;
+ for (cnt = 0; cnt < 11; cnt++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+ nv_cmd <<= 1;
+ }
+
+ /* Read data from NVRAM. */
+ for (cnt = 0; cnt < 16; cnt++) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ data <<= 1;
+ reg_data = RD_REG_WORD(&reg->nvram);
+ if (reg_data & NVR_DATA_IN)
+ data |= BIT_0;
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ }
+
+ /* Deselect chip. */
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+
+ return data;
+}
+
+
+/**
* qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
* request routine to get the word from NVRAM.
* @ha: HA context
@@ -75,7 +149,7 @@ qla2x00_unlock_nvram_access(scsi_qla_host_t *ha)
* Returns the word read from nvram @addr.
*/
static uint16_t
-qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
+qla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr)
{
uint16_t data;
uint32_t nv_cmd;
@@ -88,13 +162,27 @@ qla2x00_get_nvram_word(scsi_qla_host_t *ha, uint32_t addr)
}
/**
+ * qla2x00_nv_deselect() - Deselect NVRAM operations.
+ * @ha: HA context
+ */
+static void
+qla2x00_nv_deselect(struct qla_hw_data *ha)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
* qla2x00_write_nvram_word() - Write NVRAM data.
* @ha: HA context
* @addr: Address in NVRAM to write
* @data: word to program
*/
static void
-qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
+qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data)
{
int count;
uint16_t word;
@@ -132,7 +220,7 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
do {
if (!--wait_cnt) {
DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
- __func__, ha->host_no));
+ __func__, vha->host_no));
break;
}
NVRAM_DELAY();
@@ -150,8 +238,8 @@ qla2x00_write_nvram_word(scsi_qla_host_t *ha, uint32_t addr, uint16_t data)
}
static int
-qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data,
- uint32_t tmo)
+qla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr,
+ uint16_t data, uint32_t tmo)
{
int ret, count;
uint16_t word;
@@ -209,102 +297,11 @@ qla2x00_write_nvram_word_tmo(scsi_qla_host_t *ha, uint32_t addr, uint16_t data,
}
/**
- * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
- * NVRAM.
- * @ha: HA context
- * @nv_cmd: NVRAM command
- *
- * Bit definitions for NVRAM command:
- *
- * Bit 26 = start bit
- * Bit 25, 24 = opcode
- * Bit 23-16 = address
- * Bit 15-0 = write data
- *
- * Returns the word read from nvram @addr.
- */
-static uint16_t
-qla2x00_nvram_request(scsi_qla_host_t *ha, uint32_t nv_cmd)
-{
- uint8_t cnt;
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
- uint16_t data = 0;
- uint16_t reg_data;
-
- /* Send command to NVRAM. */
- nv_cmd <<= 5;
- for (cnt = 0; cnt < 11; cnt++) {
- if (nv_cmd & BIT_31)
- qla2x00_nv_write(ha, NVR_DATA_OUT);
- else
- qla2x00_nv_write(ha, 0);
- nv_cmd <<= 1;
- }
-
- /* Read data from NVRAM. */
- for (cnt = 0; cnt < 16; cnt++) {
- WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- data <<= 1;
- reg_data = RD_REG_WORD(&reg->nvram);
- if (reg_data & NVR_DATA_IN)
- data |= BIT_0;
- WRT_REG_WORD(&reg->nvram, NVR_SELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- }
-
- /* Deselect chip. */
- WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-
- return (data);
-}
-
-/**
- * qla2x00_nv_write() - Clean NVRAM operations.
- * @ha: HA context
- */
-static void
-qla2x00_nv_deselect(scsi_qla_host_t *ha)
-{
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
-
- WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-}
-
-/**
- * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
- * @ha: HA context
- * @data: Serial interface selector
- */
-static void
-qla2x00_nv_write(scsi_qla_host_t *ha, uint16_t data)
-{
- struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
-
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT| NVR_CLOCK |
- NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
- WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
- RD_REG_WORD(&reg->nvram); /* PCI Posting. */
- NVRAM_DELAY();
-}
-
-/**
* qla2x00_clear_nvram_protection() -
* @ha: HA context
*/
static int
-qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
+qla2x00_clear_nvram_protection(struct qla_hw_data *ha)
{
int ret, stat;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -352,9 +349,8 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(printk("%s(%ld): NVRAM didn't go "
- "ready...\n", __func__,
- ha->host_no));
+ DEBUG9_10(qla_printk(
+ "NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -370,7 +366,7 @@ qla2x00_clear_nvram_protection(scsi_qla_host_t *ha)
}
static void
-qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
+qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t word, wait_cnt;
@@ -412,8 +408,7 @@ qla2x00_set_nvram_protection(scsi_qla_host_t *ha, int stat)
wait_cnt = NVR_WAIT_CNT;
do {
if (!--wait_cnt) {
- DEBUG9_10(printk("%s(%ld): NVRAM didn't go ready...\n",
- __func__, ha->host_no));
+ DEBUG9_10(qla_printk("NVRAM didn't go ready...\n"));
break;
}
NVRAM_DELAY();
@@ -454,7 +449,7 @@ nvram_data_to_access_addr(uint32_t naddr)
}
static uint32_t
-qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr)
+qla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr)
{
int rval;
uint32_t cnt, data;
@@ -482,21 +477,20 @@ qla24xx_read_flash_dword(scsi_qla_host_t *ha, uint32_t addr)
}
uint32_t *
-qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
+qla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
uint32_t dwords)
{
uint32_t i;
-
/* Dword reads to flash. */
for (i = 0; i < dwords; i++, faddr++)
- dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw,
flash_data_to_access_addr(faddr)));
return dwptr;
}
static int
-qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data)
+qla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data)
{
int rval;
uint32_t cnt;
@@ -519,7 +513,7 @@ qla24xx_write_flash_dword(scsi_qla_host_t *ha, uint32_t addr, uint32_t data)
}
static void
-qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
+qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
uint8_t *flash_id)
{
uint32_t ids;
@@ -544,7 +538,7 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
}
static int
-qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
+qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
{
const char *loc, *locations[] = { "DEF", "PCI" };
uint32_t pcihdr, pcids;
@@ -552,6 +546,8 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
uint8_t *buf, *bcode, last_image;
uint16_t cnt, chksum, *wptr;
struct qla_flt_location *fltl;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
/*
* FLT-location structure resides after the last PCI region.
@@ -563,20 +559,20 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
FA_FLASH_LAYOUT_ADDR;
/* Begin with first PCI expansion ROM header. */
- buf = (uint8_t *)ha->request_ring;
- dcode = (uint32_t *)ha->request_ring;
+ buf = (uint8_t *)req->ring;
+ dcode = (uint32_t *)req->ring;
pcihdr = 0;
last_image = 1;
do {
/* Verify PCI expansion ROM header. */
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
bcode = buf + (pcihdr % 4);
if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa)
goto end;
/* Locate PCI data structure. */
pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
- qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
bcode = buf + (pcihdr % 4);
/* Validate signature of PCI data structure. */
@@ -591,14 +587,14 @@ qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start)
} while (!last_image);
/* Now verify FLT-location structure. */
- fltl = (struct qla_flt_location *)ha->request_ring;
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2,
+ fltl = (struct qla_flt_location *)req->ring;
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2,
sizeof(struct qla_flt_location) >> 2);
if (fltl->sig[0] != 'Q' || fltl->sig[1] != 'F' ||
fltl->sig[2] != 'L' || fltl->sig[3] != 'T')
goto end;
- wptr = (uint16_t *)ha->request_ring;
+ wptr = (uint16_t *)req->ring;
cnt = sizeof(struct qla_flt_location) >> 1;
for (chksum = 0; cnt; cnt--)
chksum += le16_to_cpu(*wptr++);
@@ -619,7 +615,7 @@ end:
}
static void
-qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr)
+qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
{
const char *loc, *locations[] = { "DEF", "FLT" };
uint16_t *wptr;
@@ -627,12 +623,14 @@ qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr)
uint32_t start;
struct qla_flt_header *flt;
struct qla_flt_region *region;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
ha->flt_region_flt = flt_addr;
- wptr = (uint16_t *)ha->request_ring;
- flt = (struct qla_flt_header *)ha->request_ring;
+ wptr = (uint16_t *)req->ring;
+ flt = (struct qla_flt_header *)req->ring;
region = (struct qla_flt_region *)&flt[1];
- ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
flt_addr << 2, OPTROM_BURST_SIZE);
if (*wptr == __constant_cpu_to_le16(0xffff))
goto no_flash_data;
@@ -720,7 +718,7 @@ done:
}
static void
-qla2xxx_get_fdt_info(scsi_qla_host_t *ha)
+qla2xxx_get_fdt_info(scsi_qla_host_t *vha)
{
#define FLASH_BLK_SIZE_4K 0x1000
#define FLASH_BLK_SIZE_32K 0x8000
@@ -731,10 +729,12 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *ha)
struct qla_fdt_layout *fdt;
uint8_t man_id, flash_id;
uint16_t mid, fid;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
- wptr = (uint16_t *)ha->request_ring;
- fdt = (struct qla_fdt_layout *)ha->request_ring;
- ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring,
+ wptr = (uint16_t *)req->ring;
+ fdt = (struct qla_fdt_layout *)req->ring;
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
ha->flt_region_fdt << 2, OPTROM_BURST_SIZE);
if (*wptr == __constant_cpu_to_le16(0xffff))
goto no_flash_data;
@@ -807,38 +807,41 @@ done:
}
int
-qla2xxx_get_flash_info(scsi_qla_host_t *ha)
+qla2xxx_get_flash_info(scsi_qla_host_t *vha)
{
int ret;
uint32_t flt_addr;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
return QLA_SUCCESS;
- ret = qla2xxx_find_flt_start(ha, &flt_addr);
+ ret = qla2xxx_find_flt_start(vha, &flt_addr);
if (ret != QLA_SUCCESS)
return ret;
- qla2xxx_get_flt_info(ha, flt_addr);
- qla2xxx_get_fdt_info(ha);
+ qla2xxx_get_flt_info(vha, flt_addr);
+ qla2xxx_get_fdt_info(vha);
return QLA_SUCCESS;
}
void
-qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
+qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
{
#define NPIV_CONFIG_SIZE (16*1024)
void *data;
uint16_t *wptr;
uint16_t cnt, chksum;
+ int i;
struct qla_npiv_header hdr;
struct qla_npiv_entry *entry;
+ struct qla_hw_data *ha = vha->hw;
if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
return;
- ha->isp_ops->read_optrom(ha, (uint8_t *)&hdr,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr,
ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header));
if (hdr.version == __constant_cpu_to_le16(0xffff))
return;
@@ -857,7 +860,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
return;
}
- ha->isp_ops->read_optrom(ha, (uint8_t *)data,
+ ha->isp_ops->read_optrom(vha, (uint8_t *)data,
ha->flt_region_npiv_conf << 2, NPIV_CONFIG_SIZE);
cnt = (sizeof(struct qla_npiv_header) + le16_to_cpu(hdr.entries) *
@@ -874,7 +877,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
entry = data + sizeof(struct qla_npiv_header);
cnt = le16_to_cpu(hdr.entries);
- for ( ; cnt; cnt--, entry++) {
+ for (i = 0; cnt; cnt--, entry++, i++) {
uint16_t flags;
struct fc_vport_identifiers vid;
struct fc_vport *vport;
@@ -892,25 +895,29 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha)
vid.port_name = wwn_to_u64(entry->port_name);
vid.node_name = wwn_to_u64(entry->node_name);
+ memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
+
DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx "
- "wwnn=%llx vf_id=0x%x qos=0x%x.\n", cnt,
- (unsigned long long)vid.port_name,
- (unsigned long long)vid.node_name,
- le16_to_cpu(entry->vf_id), le16_to_cpu(entry->qos)));
-
- vport = fc_vport_create(ha->host, 0, &vid);
- if (!vport)
- qla_printk(KERN_INFO, ha, "NPIV-Config: Failed to "
- "create vport [%02x]: wwpn=%llx wwnn=%llx.\n", cnt,
- (unsigned long long)vid.port_name,
- (unsigned long long)vid.node_name);
+ "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
+ vid.port_name, vid.node_name, le16_to_cpu(entry->vf_id),
+ entry->q_qos, entry->f_qos));
+
+ if (i < QLA_PRECONFIG_VPORTS) {
+ vport = fc_vport_create(vha->host, 0, &vid);
+ if (!vport)
+ qla_printk(KERN_INFO, ha,
+ "NPIV-Config: Failed to create vport [%02x]: "
+ "wwpn=%llx wwnn=%llx.\n", cnt,
+ vid.port_name, vid.node_name);
+ }
}
done:
kfree(data);
+ ha->npiv_info = NULL;
}
static void
-qla24xx_unprotect_flash(scsi_qla_host_t *ha)
+qla24xx_unprotect_flash(struct qla_hw_data *ha)
{
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -929,7 +936,7 @@ qla24xx_unprotect_flash(scsi_qla_host_t *ha)
}
static void
-qla24xx_protect_flash(scsi_qla_host_t *ha)
+qla24xx_protect_flash(struct qla_hw_data *ha)
{
uint32_t cnt;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
@@ -955,7 +962,7 @@ skip_wrt_protect:
}
static int
-qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
+qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
uint32_t dwords)
{
int ret;
@@ -965,6 +972,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
dma_addr_t optrom_dma;
void *optrom = NULL;
uint32_t *s, *d;
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
@@ -1002,9 +1010,8 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
(fdata & 0xff00) |((fdata << 16) &
0xff0000) | ((fdata >> 16) & 0xff));
if (ret != QLA_SUCCESS) {
- DEBUG9(printk("%s(%ld) Unable to flash "
- "sector: address=%x.\n", __func__,
- ha->host_no, faddr));
+ DEBUG9(qla_printk("Unable to flash sector: "
+ "address=%x.\n", faddr));
break;
}
}
@@ -1016,7 +1023,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
miter < OPTROM_BURST_DWORDS; miter++, s++, d++)
*s = cpu_to_le32(*d);
- ret = qla2x00_load_ram(ha, optrom_dma,
+ ret = qla2x00_load_ram(vha, optrom_dma,
flash_data_to_access_addr(faddr),
OPTROM_BURST_DWORDS);
if (ret != QLA_SUCCESS) {
@@ -1044,7 +1051,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
if (ret != QLA_SUCCESS) {
DEBUG9(printk("%s(%ld) Unable to program flash "
"address=%x data=%x.\n", __func__,
- ha->host_no, faddr, *dwptr));
+ vha->host_no, faddr, *dwptr));
break;
}
@@ -1067,11 +1074,12 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr,
}
uint8_t *
-qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla2x00_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
uint16_t *wptr;
+ struct qla_hw_data *ha = vha->hw;
/* Word reads to NVRAM via registers. */
wptr = (uint16_t *)buf;
@@ -1085,7 +1093,7 @@ qla2x00_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
uint8_t *
-qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
@@ -1094,20 +1102,21 @@ qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
/* Dword reads to flash. */
dwptr = (uint32_t *)buf;
for (i = 0; i < bytes >> 2; i++, naddr++)
- dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw,
nvram_data_to_access_addr(naddr)));
return buf;
}
int
-qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla2x00_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
int ret, stat;
uint32_t i;
uint16_t *wptr;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
@@ -1134,12 +1143,13 @@ qla2x00_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
int
-qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
int ret;
uint32_t i;
uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
ret = QLA_SUCCESS;
@@ -1162,9 +1172,8 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
nvram_data_to_access_addr(naddr),
cpu_to_le32(*dwptr));
if (ret != QLA_SUCCESS) {
- DEBUG9(printk("%s(%ld) Unable to program "
- "nvram address=%x data=%x.\n", __func__,
- ha->host_no, naddr, *dwptr));
+ DEBUG9(qla_printk("Unable to program nvram address=%x "
+ "data=%x.\n", naddr, *dwptr));
break;
}
}
@@ -1182,11 +1191,12 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
uint8_t *
-qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla25xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
uint32_t i;
uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
/* Dword reads to flash. */
dwptr = (uint32_t *)buf;
@@ -1199,19 +1209,20 @@ qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
int
-qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
+qla25xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
uint32_t bytes)
{
+ struct qla_hw_data *ha = vha->hw;
#define RMW_BUFFER_SIZE (64 * 1024)
uint8_t *dbuf;
dbuf = vmalloc(RMW_BUFFER_SIZE);
if (!dbuf)
return QLA_MEMORY_ALLOC_FAILED;
- ha->isp_ops->read_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2,
+ ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
RMW_BUFFER_SIZE);
memcpy(dbuf + (naddr << 2), buf, bytes);
- ha->isp_ops->write_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2,
+ ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
RMW_BUFFER_SIZE);
vfree(dbuf);
@@ -1219,7 +1230,7 @@ qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
}
static inline void
-qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+qla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
{
if (IS_QLA2322(ha)) {
/* Flip all colors. */
@@ -1249,12 +1260,13 @@ qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
#define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r))
void
-qla2x00_beacon_blink(struct scsi_qla_host *ha)
+qla2x00_beacon_blink(struct scsi_qla_host *vha)
{
uint16_t gpio_enable;
uint16_t gpio_data;
uint16_t led_color = 0;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -1298,17 +1310,18 @@ qla2x00_beacon_blink(struct scsi_qla_host *ha)
}
int
-qla2x00_beacon_on(struct scsi_qla_host *ha)
+qla2x00_beacon_on(struct scsi_qla_host *vha)
{
uint16_t gpio_enable;
uint16_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon on).\n");
return QLA_FUNCTION_FAILED;
@@ -1354,9 +1367,10 @@ qla2x00_beacon_on(struct scsi_qla_host *ha)
}
int
-qla2x00_beacon_off(struct scsi_qla_host *ha)
+qla2x00_beacon_off(struct scsi_qla_host *vha)
{
int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
ha->beacon_blink_led = 0;
@@ -1366,12 +1380,12 @@ qla2x00_beacon_off(struct scsi_qla_host *ha)
else
ha->beacon_color_state = QLA_LED_GRN_ON;
- ha->isp_ops->beacon_blink(ha); /* This turns green LED off */
+ ha->isp_ops->beacon_blink(vha); /* This turns green LED off */
ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
- rval = qla2x00_set_fw_options(ha, ha->fw_options);
+ rval = qla2x00_set_fw_options(vha, ha->fw_options);
if (rval != QLA_SUCCESS)
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon off).\n");
@@ -1380,7 +1394,7 @@ qla2x00_beacon_off(struct scsi_qla_host *ha)
static inline void
-qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+qla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
{
/* Flip all colors. */
if (ha->beacon_color_state == QLA_LED_ALL_ON) {
@@ -1395,11 +1409,12 @@ qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
}
void
-qla24xx_beacon_blink(struct scsi_qla_host *ha)
+qla24xx_beacon_blink(struct scsi_qla_host *vha)
{
uint16_t led_color = 0;
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
/* Save the Original GPIOD. */
@@ -1428,20 +1443,21 @@ qla24xx_beacon_blink(struct scsi_qla_host *ha)
}
int
-qla24xx_beacon_on(struct scsi_qla_host *ha)
+qla24xx_beacon_on(struct scsi_qla_host *vha)
{
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
if (ha->beacon_blink_led == 0) {
/* Enable firmware for update */
ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS)
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS)
return QLA_FUNCTION_FAILED;
- if (qla2x00_get_fw_options(ha, ha->fw_options) !=
+ if (qla2x00_get_fw_options(vha, ha->fw_options) !=
QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon on).\n");
@@ -1469,16 +1485,17 @@ qla24xx_beacon_on(struct scsi_qla_host *ha)
}
int
-qla24xx_beacon_off(struct scsi_qla_host *ha)
+qla24xx_beacon_off(struct scsi_qla_host *vha)
{
uint32_t gpio_data;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
ha->beacon_blink_led = 0;
ha->beacon_color_state = QLA_LED_ALL_ON;
- ha->isp_ops->beacon_blink(ha); /* Will flip to all off. */
+ ha->isp_ops->beacon_blink(vha); /* Will flip to all off. */
/* Give control back to firmware. */
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -1492,13 +1509,13 @@ qla24xx_beacon_off(struct scsi_qla_host *ha)
ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
- if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to update fw options (beacon off).\n");
return QLA_FUNCTION_FAILED;
}
- if (qla2x00_get_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+ if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
qla_printk(KERN_WARNING, ha,
"Unable to get fw options (beacon off).\n");
return QLA_FUNCTION_FAILED;
@@ -1517,7 +1534,7 @@ qla24xx_beacon_off(struct scsi_qla_host *ha)
* @ha: HA context
*/
static void
-qla2x00_flash_enable(scsi_qla_host_t *ha)
+qla2x00_flash_enable(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1533,7 +1550,7 @@ qla2x00_flash_enable(scsi_qla_host_t *ha)
* @ha: HA context
*/
static void
-qla2x00_flash_disable(scsi_qla_host_t *ha)
+qla2x00_flash_disable(struct qla_hw_data *ha)
{
uint16_t data;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1554,7 +1571,7 @@ qla2x00_flash_disable(scsi_qla_host_t *ha)
* Returns the byte read from flash @addr.
*/
static uint8_t
-qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr)
+qla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr)
{
uint16_t data;
uint16_t bank_select;
@@ -1615,7 +1632,7 @@ qla2x00_read_flash_byte(scsi_qla_host_t *ha, uint32_t addr)
* @data: Data to write
*/
static void
-qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data)
+qla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data)
{
uint16_t bank_select;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
@@ -1678,7 +1695,7 @@ qla2x00_write_flash_byte(scsi_qla_host_t *ha, uint32_t addr, uint8_t data)
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data,
+qla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data,
uint8_t man_id, uint8_t flash_id)
{
int status;
@@ -1718,8 +1735,8 @@ qla2x00_poll_flash(scsi_qla_host_t *ha, uint32_t addr, uint8_t poll_data,
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data,
- uint8_t man_id, uint8_t flash_id)
+qla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr,
+ uint8_t data, uint8_t man_id, uint8_t flash_id)
{
/* Write Program Command Sequence. */
if (IS_OEM_001(ha)) {
@@ -1755,7 +1772,7 @@ qla2x00_program_flash_address(scsi_qla_host_t *ha, uint32_t addr, uint8_t data,
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id)
+qla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id)
{
/* Individual Sector Erase Command Sequence */
if (IS_OEM_001(ha)) {
@@ -1791,7 +1808,7 @@ qla2x00_erase_flash(scsi_qla_host_t *ha, uint8_t man_id, uint8_t flash_id)
* Returns 0 on success, else non-zero.
*/
static int
-qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr,
+qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
uint32_t sec_mask, uint8_t man_id, uint8_t flash_id)
{
/* Individual Sector Erase Command Sequence */
@@ -1817,7 +1834,7 @@ qla2x00_erase_flash_sector(scsi_qla_host_t *ha, uint32_t addr,
* @flash_id: Flash ID
*/
static void
-qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
+qla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
uint8_t *flash_id)
{
qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
@@ -1831,8 +1848,8 @@ qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id,
}
static void
-qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr,
- uint32_t length)
+qla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf,
+ uint32_t saddr, uint32_t length)
{
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
uint32_t midpoint, ilength;
@@ -1856,14 +1873,15 @@ qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr,
}
static inline void
-qla2x00_suspend_hba(struct scsi_qla_host *ha)
+qla2x00_suspend_hba(struct scsi_qla_host *vha)
{
int cnt;
unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
ha->isp_ops->disable_intrs(ha);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
@@ -1884,26 +1902,29 @@ qla2x00_suspend_hba(struct scsi_qla_host *ha)
}
static inline void
-qla2x00_resume_hba(struct scsi_qla_host *ha)
+qla2x00_resume_hba(struct scsi_qla_host *vha)
{
+ struct qla_hw_data *ha = vha->hw;
+
/* Resume HBA. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
- qla2x00_wait_for_hba_online(ha);
- scsi_unblock_requests(ha->host);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_hba_online(vha);
+ scsi_unblock_requests(vha->host);
}
uint8_t *
-qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla2x00_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
uint32_t addr, midpoint;
uint8_t *data;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- qla2x00_suspend_hba(ha);
+ qla2x00_suspend_hba(vha);
/* Go with read. */
midpoint = ha->optrom_size / 2;
@@ -1922,13 +1943,13 @@ qla2x00_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
qla2x00_flash_disable(ha);
/* Resume HBA. */
- qla2x00_resume_hba(ha);
+ qla2x00_resume_hba(vha);
return buf;
}
int
-qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla2x00_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
@@ -1936,10 +1957,11 @@ qla2x00_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
uint8_t man_id, flash_id, sec_number, data;
uint16_t wd;
uint32_t addr, liter, sec_mask, rest_addr;
+ struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
/* Suspend HBA. */
- qla2x00_suspend_hba(ha);
+ qla2x00_suspend_hba(vha);
rval = QLA_SUCCESS;
sec_number = 0;
@@ -2139,55 +2161,58 @@ update_flash:
qla2x00_flash_disable(ha);
/* Resume HBA. */
- qla2x00_resume_hba(ha);
+ qla2x00_resume_hba(vha);
return rval;
}
uint8_t *
-qla24xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla24xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
+ struct qla_hw_data *ha = vha->hw;
+
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with read. */
- qla24xx_read_flash_data(ha, (uint32_t *)buf, offset >> 2, length >> 2);
+ qla24xx_read_flash_data(vha, (uint32_t *)buf, offset >> 2, length >> 2);
/* Resume HBA. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- scsi_unblock_requests(ha->host);
+ scsi_unblock_requests(vha->host);
return buf;
}
int
-qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla24xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
/* Suspend HBA. */
- scsi_block_requests(ha->host);
+ scsi_block_requests(vha->host);
set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
/* Go with write. */
- rval = qla24xx_write_flash_data(ha, (uint32_t *)buf, offset >> 2,
+ rval = qla24xx_write_flash_data(vha, (uint32_t *)buf, offset >> 2,
length >> 2);
/* Resume HBA -- RISC reset needed. */
clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
- set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags);
- qla2xxx_wake_dpc(ha);
- qla2x00_wait_for_hba_online(ha);
- scsi_unblock_requests(ha->host);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_hba_online(vha);
+ scsi_unblock_requests(vha->host);
return rval;
}
uint8_t *
-qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
+qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
uint32_t offset, uint32_t length)
{
int rval;
@@ -2195,6 +2220,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
void *optrom;
uint8_t *pbuf;
uint32_t faddr, left, burst;
+ struct qla_hw_data *ha = vha->hw;
if (offset & 0xfff)
goto slow_read;
@@ -2219,7 +2245,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
if (burst > left)
burst = left;
- rval = qla2x00_dump_ram(ha, optrom_dma,
+ rval = qla2x00_dump_ram(vha, optrom_dma,
flash_data_to_access_addr(faddr), burst);
if (rval) {
qla_printk(KERN_WARNING, ha,
@@ -2248,7 +2274,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *ha, uint8_t *buf,
return buf;
slow_read:
- return qla24xx_read_optrom_data(ha, buf, offset, length);
+ return qla24xx_read_optrom_data(vha, buf, offset, length);
}
/**
@@ -2270,7 +2296,7 @@ slow_read:
* Returns QLA_SUCCESS on successful retrieval of version.
*/
static void
-qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids)
+qla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids)
{
int ret = QLA_FUNCTION_FAILED;
uint32_t istart, iend, iter, vend;
@@ -2344,13 +2370,14 @@ qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids)
}
int
-qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
+qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
{
int ret = QLA_SUCCESS;
uint8_t code_type, last_image;
uint32_t pcihdr, pcids;
uint8_t *dbyte;
uint16_t *dcode;
+ struct qla_hw_data *ha = vha->hw;
if (!ha->pio_address || !mbuf)
return QLA_FUNCTION_FAILED;
@@ -2370,8 +2397,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
/* No signature */
- DEBUG2(printk("scsi(%ld): No matching ROM "
- "signature.\n", ha->host_no));
+ DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
+ "signature.\n"));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2387,8 +2414,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
/* Incorrect header. */
- DEBUG2(printk("%s(): PCI data struct not found "
- "pcir_adr=%x.\n", __func__, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
+ "found pcir_adr=%x.\n", pcids));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2402,7 +2429,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x12);
ha->bios_revision[1] =
qla2x00_read_flash_byte(ha, pcids + 0x13);
- DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
ha->bios_revision[1], ha->bios_revision[0]));
break;
case ROM_CODE_TYPE_FCODE:
@@ -2416,12 +2443,12 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_byte(ha, pcids + 0x12);
ha->efi_revision[1] =
qla2x00_read_flash_byte(ha, pcids + 0x13);
- DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
ha->efi_revision[1], ha->efi_revision[0]));
break;
default:
- DEBUG2(printk("%s(): Unrecognized code type %x at "
- "pcids %x.\n", __func__, code_type, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
+ "type %x at pcids %x.\n", code_type, pcids));
break;
}
@@ -2441,16 +2468,16 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
8);
- DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n",
- __func__, ha->host_no));
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "dumping fw ver from "
+ "flash:\n"));
DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8));
if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
dcode[2] == 0xffff && dcode[3] == 0xffff) ||
(dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
dcode[3] == 0)) {
- DEBUG2(printk("%s(): Unrecognized fw revision at "
- "%x.\n", __func__, ha->flt_region_fw * 4));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
+ "revision at %x.\n", ha->flt_region_fw * 4));
} else {
/* values are in big endian */
ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
@@ -2465,7 +2492,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
}
int
-qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
+qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
{
int ret = QLA_SUCCESS;
uint32_t pcihdr, pcids;
@@ -2473,6 +2500,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
uint8_t *bcode;
uint8_t code_type, last_image;
int i;
+ struct qla_hw_data *ha = vha->hw;
if (!mbuf)
return QLA_FUNCTION_FAILED;
@@ -2489,12 +2517,12 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
last_image = 1;
do {
/* Verify PCI expansion ROM header. */
- qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
bcode = mbuf + (pcihdr % 4);
if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) {
/* No signature */
- DEBUG2(printk("scsi(%ld): No matching ROM "
- "signature.\n", ha->host_no));
+ DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM "
+ "signature.\n"));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2502,15 +2530,15 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
/* Locate PCI data structure. */
pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
- qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20);
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
bcode = mbuf + (pcihdr % 4);
/* Validate signature of PCI data structure. */
if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
bcode[0x2] != 'I' || bcode[0x3] != 'R') {
/* Incorrect header. */
- DEBUG2(printk("%s(): PCI data struct not found "
- "pcir_adr=%x.\n", __func__, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not "
+ "found pcir_adr=%x.\n", pcids));
ret = QLA_FUNCTION_FAILED;
break;
}
@@ -2522,26 +2550,26 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
/* Intel x86, PC-AT compatible. */
ha->bios_revision[0] = bcode[0x12];
ha->bios_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n",
ha->bios_revision[1], ha->bios_revision[0]));
break;
case ROM_CODE_TYPE_FCODE:
/* Open Firmware standard for PCI (FCode). */
ha->fcode_revision[0] = bcode[0x12];
ha->fcode_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read FCODE %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read FCODE %d.%d.\n",
ha->fcode_revision[1], ha->fcode_revision[0]));
break;
case ROM_CODE_TYPE_EFI:
/* Extensible Firmware Interface (EFI). */
ha->efi_revision[0] = bcode[0x12];
ha->efi_revision[1] = bcode[0x13];
- DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__,
+ DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n",
ha->efi_revision[1], ha->efi_revision[0]));
break;
default:
- DEBUG2(printk("%s(): Unrecognized code type %x at "
- "pcids %x.\n", __func__, code_type, pcids));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code "
+ "type %x at pcids %x.\n", code_type, pcids));
break;
}
@@ -2555,7 +2583,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
dcode = mbuf;
- qla24xx_read_flash_data(ha, dcode, ha->flt_region_fw + 4, 4);
+ qla24xx_read_flash_data(vha, dcode, ha->flt_region_fw + 4, 4);
for (i = 0; i < 4; i++)
dcode[i] = be32_to_cpu(dcode[i]);
@@ -2563,8 +2591,8 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf)
dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
(dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
dcode[3] == 0)) {
- DEBUG2(printk("%s(): Unrecognized fw version at %x.\n",
- __func__, ha->flt_region_fw));
+ DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw "
+ "revision at %x.\n", ha->flt_region_fw * 4));
} else {
ha->fw_revision[0] = dcode[0];
ha->fw_revision[1] = dcode[1];
@@ -2593,8 +2621,9 @@ qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
}
int
-qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size)
+qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size)
{
+ struct qla_hw_data *ha = vha->hw;
uint8_t *pos = ha->vpd;
uint8_t *end = pos + ha->vpd_size;
int len = 0;
@@ -2621,9 +2650,10 @@ qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size)
}
static int
-qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
+qla2xxx_hw_event_store(scsi_qla_host_t *vha, uint32_t *fdata)
{
uint32_t d[2], faddr;
+ struct qla_hw_data *ha = vha->hw;
/* Locate first empty entry. */
for (;;) {
@@ -2634,7 +2664,7 @@ qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
return QLA_MEMORY_ALLOC_FAILED;
}
- qla24xx_read_flash_data(ha, d, ha->hw_event_ptr, 2);
+ qla24xx_read_flash_data(vha, d, ha->hw_event_ptr, 2);
faddr = flash_data_to_access_addr(ha->hw_event_ptr);
ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE;
if (d[0] == __constant_cpu_to_le32(0xffffffff) &&
@@ -2655,12 +2685,12 @@ qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata)
}
int
-qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
+qla2xxx_hw_event_log(scsi_qla_host_t *vha, uint16_t code, uint16_t d1,
uint16_t d2, uint16_t d3)
{
#define QMARK(a, b, c, d) \
cpu_to_le32(LSB(a) << 24 | LSB(b) << 16 | LSB(c) << 8 | LSB(d))
-
+ struct qla_hw_data *ha = vha->hw;
int rval;
uint32_t marker[2], fdata[4];
@@ -2681,7 +2711,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
/* Locate marker. */
ha->hw_event_ptr = ha->flt_region_hw_event;
for (;;) {
- qla24xx_read_flash_data(ha, fdata, ha->hw_event_ptr,
+ qla24xx_read_flash_data(vha, fdata, ha->hw_event_ptr,
4);
if (fdata[0] == __constant_cpu_to_le32(0xffffffff) &&
fdata[1] == __constant_cpu_to_le32(0xffffffff))
@@ -2700,7 +2730,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
}
/* No marker, write it. */
if (!ha->flags.hw_event_marker_found) {
- rval = qla2xxx_hw_event_store(ha, marker);
+ rval = qla2xxx_hw_event_store(vha, marker);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_WARNING, ha,
"HW event -- Failed marker write=%x.!\n",
@@ -2714,7 +2744,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1,
/* Store error. */
fdata[0] = cpu_to_le32(code << 16 | d1);
fdata[1] = cpu_to_le32(d2 << 16 | d3);
- rval = qla2xxx_hw_event_store(ha, fdata);
+ rval = qla2xxx_hw_event_store(vha, fdata);
if (rval != QLA_SUCCESS) {
DEBUG2(qla_printk(KERN_WARNING, ha,
"HW event -- Failed error write=%x.!\n",
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index eea6720adf16..be22f3a09f8d 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,9 +7,9 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.02.01-k9"
+#define QLA2XXX_VERSION "8.02.03-k1"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 2
-#define QLA_DRIVER_PATCH_VER 1
+#define QLA_DRIVER_PATCH_VER 3
#define QLA_DRIVER_BETA_VER 0
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index db7ea3bb4e83..eb3a414b189a 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -206,8 +206,7 @@ static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
break;
case ISCSI_PARAM_CONN_ADDRESS:
/* TODO: what are the ipv6 bits */
- len = sprintf(buf, "%u.%u.%u.%u\n",
- NIPQUAD(ddb_entry->ip_addr));
+ len = sprintf(buf, "%pI4\n", &ddb_entry->ip_addr);
break;
default:
return -ENOSYS;
diff --git a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
index de7b3bc2cbc9..1ad51552d6b1 100644
--- a/drivers/scsi/qlogicfas408.c
+++ b/drivers/scsi/qlogicfas408.c
@@ -23,7 +23,7 @@
Functions as standalone, loadable, and PCMCIA driver, the latter from
Dave Hinds' PCMCIA package.
- Cleaned up 26/10/2002 by Alan Cox <alan@redhat.com> as part of the 2.5
+ Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
SCSI driver cleanup and audit. This driver still needs work on the
following
- Non terminating hardware waits
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index edfaf241c5ba..381838ebd460 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -136,7 +136,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
else
eh_timed_out = NULL;
- if (eh_timed_out)
+ if (eh_timed_out) {
rtn = eh_timed_out(scmd);
switch (rtn) {
case BLK_EH_NOT_HANDLED:
@@ -144,6 +144,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
default:
return rtn;
}
+ }
if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
scmd->result |= DID_TIME_OUT << 16;
@@ -1405,8 +1406,9 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
return ADD_TO_MLQUEUE;
case GOOD:
case COMMAND_TERMINATED:
- case TASK_ABORTED:
return SUCCESS;
+ case TASK_ABORTED:
+ goto maybe_retry;
case CHECK_CONDITION:
rtn = scsi_check_sense(scmd);
if (rtn == NEEDS_RETRY)
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index dc1cfb2fd76b..2ae4f8fc5831 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -94,7 +94,7 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd,
SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd));
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0,
- &sshdr, timeout, retries);
+ &sshdr, timeout, retries, NULL);
SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", result));
@@ -270,11 +270,11 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
EXPORT_SYMBOL(scsi_ioctl);
/**
- * scsi_nonblock_ioctl() - Handle SG_SCSI_RESET
+ * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
* @sdev: scsi device receiving ioctl
* @cmd: Must be SC_SCSI_RESET
* @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST}
- * @filp: either NULL or a &struct file which must have the O_NONBLOCK flag.
+ * @ndelay: file mode O_NDELAY flag
*/
int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
void __user *arg, int ndelay)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index fa45a1a66867..f2f51e0333eb 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -183,13 +183,15 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason)
* @timeout: request timeout in seconds
* @retries: number of times to retry request
* @flags: or into request flags;
+ * @resid: optional residual length
*
* returns the req->errors value which is the scsi_cmnd result
* field.
*/
int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- unsigned char *sense, int timeout, int retries, int flags)
+ unsigned char *sense, int timeout, int retries, int flags,
+ int *resid)
{
struct request *req;
int write = (data_direction == DMA_TO_DEVICE);
@@ -224,6 +226,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
if (unlikely(req->data_len > 0 && req->data_len <= bufflen))
memset(buffer + (bufflen - req->data_len), 0, req->data_len);
+ if (resid)
+ *resid = req->data_len;
ret = req->errors;
out:
blk_put_request(req);
@@ -235,7 +239,8 @@ EXPORT_SYMBOL(scsi_execute);
int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- struct scsi_sense_hdr *sshdr, int timeout, int retries)
+ struct scsi_sense_hdr *sshdr, int timeout, int retries,
+ int *resid)
{
char *sense = NULL;
int result;
@@ -246,7 +251,7 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
return DRIVER_ERROR << 24;
}
result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen,
- sense, timeout, retries, 0);
+ sense, timeout, retries, 0, resid);
if (sshdr)
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr);
@@ -648,8 +653,8 @@ static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd)
struct request *req = cmd->request;
unsigned long flags;
- scsi_unprep_request(req);
spin_lock_irqsave(q->queue_lock, flags);
+ scsi_unprep_request(req);
blk_requeue_request(q, req);
spin_unlock_irqrestore(q->queue_lock, flags);
@@ -875,16 +880,24 @@ static void scsi_end_bidi_request(struct scsi_cmnd *cmd)
* (the normal case for most drivers), we don't need
* the logic to deal with cleaning up afterwards.
*
- * We must do one of several things here:
+ * We must call scsi_end_request(). This will finish off
+ * the specified number of sectors. If we are done, the
+ * command block will be released and the queue function
+ * will be goosed. If we are not done then we have to
+ * figure out what to do next:
+ *
+ * a) We can call scsi_requeue_command(). The request
+ * will be unprepared and put back on the queue. Then
+ * a new command will be created for it. This should
+ * be used if we made forward progress, or if we want
+ * to switch from READ(10) to READ(6) for example.
*
- * a) Call scsi_end_request. This will finish off the
- * specified number of sectors. If we are done, the
- * command block will be released, and the queue
- * function will be goosed. If we are not done, then
- * scsi_end_request will directly goose the queue.
+ * b) We can call scsi_queue_insert(). The request will
+ * be put back on the queue and retried using the same
+ * command as before, possibly after a delay.
*
- * b) We can just use scsi_requeue_command() here. This would
- * be used if we just wanted to retry, for example.
+ * c) We can call blk_end_request() with -EIO to fail
+ * the remainder of the request.
*/
void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
{
@@ -896,6 +909,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
int sense_deferred = 0;
+ enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
+ ACTION_DELAYED_RETRY} action;
+ char *description = NULL;
if (result) {
sense_valid = scsi_command_normalize_sense(cmd, &sshdr);
@@ -947,10 +963,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
return;
this_count = blk_rq_bytes(req);
- /* good_bytes = 0, or (inclusive) there were leftovers and
- * result = 0, so scsi_end_request couldn't retry.
- */
- if (sense_valid && !sense_deferred) {
+ if (host_byte(result) == DID_RESET) {
+ /* Third party bus reset or reset for error recovery
+ * reasons. Just retry the command and see what
+ * happens.
+ */
+ action = ACTION_RETRY;
+ } else if (sense_valid && !sense_deferred) {
switch (sshdr.sense_key) {
case UNIT_ATTENTION:
if (cmd->device->removable) {
@@ -958,16 +977,15 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
* and quietly refuse further access.
*/
cmd->device->changed = 1;
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ description = "Media Changed";
+ action = ACTION_FAIL;
} else {
/* Must have been a power glitch, or a
* bus reset. Could not have been a
* media change, so we just retry the
- * request and see what happens.
+ * command and see what happens.
*/
- scsi_requeue_command(q, cmd);
- return;
+ action = ACTION_RETRY;
}
break;
case ILLEGAL_REQUEST:
@@ -983,21 +1001,18 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
(cmd->cmnd[0] == READ_10 ||
cmd->cmnd[0] == WRITE_10)) {
+ /* This will issue a new 6-byte command. */
cmd->device->use_10_for_rw = 0;
- /* This will cause a retry with a
- * 6-byte command.
- */
- scsi_requeue_command(q, cmd);
- } else if (sshdr.asc == 0x10) /* DIX */
- scsi_end_request(cmd, -EIO, this_count, 0);
- else
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ action = ACTION_REPREP;
+ } else
+ action = ACTION_FAIL;
+ break;
case ABORTED_COMMAND:
if (sshdr.asc == 0x10) { /* DIF */
- scsi_end_request(cmd, -EIO, this_count, 0);
- return;
- }
+ action = ACTION_FAIL;
+ description = "Data Integrity Failure";
+ } else
+ action = ACTION_RETRY;
break;
case NOT_READY:
/* If the device is in the process of becoming
@@ -1012,49 +1027,57 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
case 0x07: /* operation in progress */
case 0x08: /* Long write in progress */
case 0x09: /* self test in progress */
- scsi_requeue_command(q, cmd);
- return;
- default:
+ action = ACTION_DELAYED_RETRY;
break;
}
+ } else {
+ description = "Device not ready";
+ action = ACTION_FAIL;
}
- if (!(req->cmd_flags & REQ_QUIET))
- scsi_cmd_print_sense_hdr(cmd,
- "Device not ready",
- &sshdr);
-
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ break;
case VOLUME_OVERFLOW:
- if (!(req->cmd_flags & REQ_QUIET)) {
- scmd_printk(KERN_INFO, cmd,
- "Volume overflow, CDB: ");
- __scsi_print_command(cmd->cmnd);
- scsi_print_sense("", cmd);
- }
/* See SSC3rXX or current. */
- scsi_end_request(cmd, -EIO, this_count, 1);
- return;
+ action = ACTION_FAIL;
+ break;
default:
+ description = "Unhandled sense code";
+ action = ACTION_FAIL;
break;
}
+ } else {
+ description = "Unhandled error code";
+ action = ACTION_FAIL;
}
- if (host_byte(result) == DID_RESET) {
- /* Third party bus reset or reset for error recovery
- * reasons. Just retry the request and see what
- * happens.
- */
- scsi_requeue_command(q, cmd);
- return;
- }
- if (result) {
+
+ switch (action) {
+ case ACTION_FAIL:
+ /* Give up and fail the remainder of the request */
if (!(req->cmd_flags & REQ_QUIET)) {
+ if (description)
+ scmd_printk(KERN_INFO, cmd, "%s",
+ description);
scsi_print_result(cmd);
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
}
+ blk_end_request(req, -EIO, blk_rq_bytes(req));
+ scsi_next_command(cmd);
+ break;
+ case ACTION_REPREP:
+ /* Unprep the request and put it back at the head of the queue.
+ * A new command will be prepared and issued.
+ */
+ scsi_requeue_command(q, cmd);
+ break;
+ case ACTION_RETRY:
+ /* Retry the same command immediately */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
+ break;
+ case ACTION_DELAYED_RETRY:
+ /* Retry the same command after a delay */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+ break;
}
- scsi_end_request(cmd, -EIO, this_count, !result);
}
static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
@@ -1998,7 +2021,7 @@ scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
}
ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len,
- sshdr, timeout, retries);
+ sshdr, timeout, retries, NULL);
kfree(real_buffer);
return ret;
}
@@ -2063,7 +2086,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
memset(buffer, 0, len);
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len,
- sshdr, timeout, retries);
+ sshdr, timeout, retries, NULL);
/* This code looks awful: what it's doing is making sure an
* ILLEGAL REQUEST sense return identifies the actual command
@@ -2145,7 +2168,7 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
/* try to eat the UNIT_ATTENTION if there are enough retries */
do {
result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
- timeout, retries);
+ timeout, retries, NULL);
if (sdev->removable && scsi_sense_valid(sshdr) &&
sshdr->sense_key == UNIT_ATTENTION)
sdev->changed = 1;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index b14dc02c3ded..18486b51668d 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -216,7 +216,7 @@ static void scsi_unlock_floptical(struct scsi_device *sdev,
scsi_cmd[4] = 0x2a; /* size */
scsi_cmd[5] = 0;
scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL,
- SCSI_TIMEOUT, 3);
+ SCSI_TIMEOUT, 3, NULL);
}
/**
@@ -573,6 +573,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
/* Each pass gets up to three chances to ignore Unit Attention */
for (count = 0; count < 3; ++count) {
+ int resid;
+
memset(scsi_cmd, 0, 6);
scsi_cmd[0] = INQUIRY;
scsi_cmd[4] = (unsigned char) try_inquiry_len;
@@ -581,7 +583,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
inq_result, try_inquiry_len, &sshdr,
- HZ / 2 + HZ * scsi_inq_timeout, 3);
+ HZ / 2 + HZ * scsi_inq_timeout, 3,
+ &resid);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
"with code 0x%x\n",
@@ -602,6 +605,14 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
(sshdr.ascq == 0))
continue;
}
+ } else {
+ /*
+ * if nothing was transferred, we try
+ * again. It's a workaround for some USB
+ * devices.
+ */
+ if (resid == try_inquiry_len)
+ continue;
}
break;
}
@@ -1390,7 +1401,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
lun_data, length, &sshdr,
- SCSI_TIMEOUT + 4 * HZ, 3);
+ SCSI_TIMEOUT + 4 * HZ, 3, NULL);
SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"
" %s (try %d) result 0x%x\n", result
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 1e71abf0607a..062304de4854 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -3012,6 +3012,16 @@ fc_timeout_deleted_rport(struct work_struct *work)
rport->port_state = FC_PORTSTATE_NOTPRESENT;
rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
+ /*
+ * Pre-emptively kill I/O rather than waiting for the work queue
+ * item to teardown the starget. (FCOE libFC folks prefer this
+ * and to have the rport_port_id still set when it's done).
+ */
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ fc_terminate_rport_io(rport);
+
+ BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+
/* remove the identifiers that aren't used in the consisting binding */
switch (fc_host->tgtid_bind_type) {
case FC_TGTID_BIND_BY_WWPN:
@@ -3035,9 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
* went away and didn't come back - we'll remove
* all attached scsi devices.
*/
- spin_unlock_irqrestore(shost->host_lock, flags);
-
- scsi_target_unblock(&rport->dev);
fc_queue_work(shost, &rport->stgt_delete_work);
}
diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c
index 7c2d28924d2a..f49f55c6bfc8 100644
--- a/drivers/scsi/scsi_transport_spi.c
+++ b/drivers/scsi/scsi_transport_spi.c
@@ -111,8 +111,9 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
sense, DV_TIMEOUT, /* retries */ 1,
REQ_FAILFAST_DEV |
REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER);
- if (result & DRIVER_SENSE) {
+ REQ_FAILFAST_DRIVER,
+ NULL);
+ if (driver_byte(result) & DRIVER_SENSE) {
struct scsi_sense_hdr sshdr_tmp;
if (!sshdr)
sshdr = &sshdr_tmp;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5081b3981d3c..62b28d58e65e 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -884,7 +884,7 @@ static int sd_sync_cache(struct scsi_disk *sdkp)
* flush everything.
*/
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res == 0)
break;
}
@@ -1134,7 +1134,7 @@ sd_spinup_disk(struct scsi_disk *sdkp)
the_result = scsi_execute_req(sdkp->device, cmd,
DMA_NONE, NULL, 0,
&sshdr, SD_TIMEOUT,
- SD_MAX_RETRIES);
+ SD_MAX_RETRIES, NULL);
/*
* If the drive has indicated to us that it
@@ -1192,7 +1192,8 @@ sd_spinup_disk(struct scsi_disk *sdkp)
cmd[4] |= 1 << 4;
scsi_execute_req(sdkp->device, cmd, DMA_NONE,
NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES,
+ NULL);
spintime_expire = jiffies + 100 * HZ;
spintime = 1;
}
@@ -1306,7 +1307,7 @@ repeat:
the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
buffer, longrc ? 13 : 8, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (media_not_present(sdkp, &sshdr))
return;
@@ -1986,7 +1987,7 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
return -ENODEV;
res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
- SD_TIMEOUT, SD_MAX_RETRIES);
+ SD_TIMEOUT, SD_MAX_RETRIES, NULL);
if (res) {
sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
sd_print_result(sdkp, res);
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 1bcf3c33d7ff..7f0df29f3a64 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -77,7 +77,7 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code,
};
return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
- NULL, SES_TIMEOUT, SES_RETRIES);
+ NULL, SES_TIMEOUT, SES_RETRIES, NULL);
}
static int ses_send_diag(struct scsi_device *sdev, int page_code,
@@ -95,7 +95,7 @@ static int ses_send_diag(struct scsi_device *sdev, int page_code,
};
result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen,
- NULL, SES_TIMEOUT, SES_RETRIES);
+ NULL, SES_TIMEOUT, SES_RETRIES, NULL);
if (result)
sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
result);
@@ -369,7 +369,8 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
return;
if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
- VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES))
+ VPD_INQUIRY_SIZE, NULL, SES_TIMEOUT, SES_RETRIES,
+ NULL))
goto free;
vpd_len = (buf[2] << 8) + buf[3];
@@ -380,7 +381,7 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
cmd[3] = vpd_len >> 8;
cmd[4] = vpd_len & 0xff;
if (scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf,
- vpd_len, NULL, SES_TIMEOUT, SES_RETRIES))
+ vpd_len, NULL, SES_TIMEOUT, SES_RETRIES, NULL))
goto free;
desc = buf + 4;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 45b66b98a516..e7fa3caead79 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -177,7 +177,7 @@ int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr)
do {
the_result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL,
0, sshdr, SR_TIMEOUT,
- retries--);
+ retries--, NULL);
if (scsi_sense_valid(sshdr) &&
sshdr->sense_key == UNIT_ATTENTION)
sdev->changed = 1;
@@ -681,7 +681,7 @@ static void get_sectorsize(struct scsi_cd *cd)
/* Do the command and wait.. */
the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
buffer, sizeof(buffer), NULL,
- SR_TIMEOUT, MAX_RETRIES);
+ SR_TIMEOUT, MAX_RETRIES, NULL);
retries--;
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index ae87d08df588..d92ff512d213 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -207,7 +207,7 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc)
memset(sense, 0, sizeof(*sense));
result = scsi_execute(SDev, cgc->cmd, cgc->data_direction,
cgc->buffer, cgc->buflen, (char *)sense,
- cgc->timeout, IOCTL_RETRIES, 0);
+ cgc->timeout, IOCTL_RETRIES, 0, NULL);
scsi_normalize_sense((char *)sense, sizeof(*sense), &sshdr);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index c959bdc55f4f..7f3f317ee6ca 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -451,9 +451,23 @@ static void st_sleep_done(void *data, char *sense, int result, int resid)
complete(SRpnt->waiting);
}
-static struct st_request *st_allocate_request(void)
+static struct st_request *st_allocate_request(struct scsi_tape *stp)
{
- return kzalloc(sizeof(struct st_request), GFP_KERNEL);
+ struct st_request *streq;
+
+ streq = kzalloc(sizeof(*streq), GFP_KERNEL);
+ if (streq)
+ streq->stp = stp;
+ else {
+ DEBC(printk(KERN_ERR "%s: Can't get SCSI request.\n",
+ tape_name(stp)););
+ if (signal_pending(current))
+ stp->buffer->syscall_result = -EINTR;
+ else
+ stp->buffer->syscall_result = -EBUSY;
+ }
+
+ return streq;
}
static void st_release_request(struct st_request *streq)
@@ -481,18 +495,10 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd
return NULL;
}
- if (SRpnt == NULL) {
- SRpnt = st_allocate_request();
- if (SRpnt == NULL) {
- DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n",
- tape_name(STp)); );
- if (signal_pending(current))
- (STp->buffer)->syscall_result = (-EINTR);
- else
- (STp->buffer)->syscall_result = (-EBUSY);
+ if (!SRpnt) {
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
return NULL;
- }
- SRpnt->stp = STp;
}
/* If async IO, set last_SRpnt. This ptr tells write_behind_check
@@ -527,6 +533,28 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd
return SRpnt;
}
+static int st_scsi_kern_execute(struct st_request *streq,
+ const unsigned char *cmd, int data_direction,
+ void *buffer, unsigned bufflen, int timeout,
+ int retries)
+{
+ struct scsi_tape *stp = streq->stp;
+ int ret, resid;
+
+ stp->buffer->cmdstat.have_sense = 0;
+ memcpy(streq->cmd, cmd, sizeof(streq->cmd));
+
+ ret = scsi_execute(stp->device, cmd, data_direction, buffer, bufflen,
+ streq->sense, timeout, retries, 0, &resid);
+ if (driver_byte(ret) & DRIVER_ERROR)
+ return -EBUSY;
+
+ stp->buffer->cmdstat.midlevel_result = streq->result = ret;
+ stp->buffer->cmdstat.residual = resid;
+ stp->buffer->syscall_result = st_chk_result(stp, streq);
+
+ return 0;
+}
/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
write has been correct but EOM early warning reached, -EIO if write ended in
@@ -599,6 +627,7 @@ static int cross_eof(struct scsi_tape * STp, int forward)
{
struct st_request *SRpnt;
unsigned char cmd[MAX_COMMAND_SIZE];
+ int ret;
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
@@ -612,19 +641,26 @@ static int cross_eof(struct scsi_tape * STp, int forward)
DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n",
tape_name(STp), forward ? "forward" : "backward"));
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- STp->device->timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
- st_release_request(SRpnt);
- SRpnt = NULL;
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES);
+ if (ret)
+ goto out;
+
+ ret = STp->buffer->syscall_result;
if ((STp->buffer)->cmdstat.midlevel_result != 0)
printk(KERN_ERR "%s: Stepping over filemark %s failed.\n",
tape_name(STp), forward ? "forward" : "backward");
- return (STp->buffer)->syscall_result;
+out:
+ st_release_request(SRpnt);
+
+ return ret;
}
@@ -657,7 +693,8 @@ static int st_flush_write_buffer(struct scsi_tape * STp)
cmd[4] = blks;
SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
- STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES, 1);
if (!SRpnt)
return (STp->buffer)->syscall_result;
@@ -844,21 +881,24 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
int attentions, waits, max_wait, scode;
int retval = CHKRES_READY, new_session = 0;
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return STp->buffer->syscall_result;
+
max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
for (attentions=waits=0; ; ) {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
- STp->long_timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->long_timeout,
+ MAX_READY_RETRIES);
+ if (retval)
break;
- }
if (cmdstatp->have_sense) {
@@ -902,8 +942,8 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
break;
}
- if (SRpnt != NULL)
- st_release_request(SRpnt);
+ st_release_request(SRpnt);
+
return retval;
}
@@ -980,16 +1020,24 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
}
}
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt) {
+ retval = STp->buffer->syscall_result;
+ goto err_out;
+ }
+
if (STp->omit_blklims)
STp->min_block = STp->max_block = (-1);
else {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = READ_BLOCK_LIMITS;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 6,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (retval) {
+ st_release_request(SRpnt);
goto err_out;
}
@@ -1013,10 +1061,12 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
cmd[0] = MODE_SENSE;
cmd[4] = 12;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 12,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (retval) {
+ st_release_request(SRpnt);
goto err_out;
}
@@ -1246,10 +1296,17 @@ static int st_flush(struct file *filp, fl_owner_t id)
cmd[0] = WRITE_FILEMARKS;
cmd[4] = 1 + STp->two_fm;
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- STp->device->timeout, MAX_WRITE_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt) {
- result = (STp->buffer)->syscall_result;
+ result = STp->buffer->syscall_result;
+ goto out;
+ }
+
+ result = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0,
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES);
+ if (result) {
+ st_release_request(SRpnt);
goto out;
}
@@ -1634,7 +1691,8 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
cmd[4] = blks;
SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
- STp->device->timeout, MAX_WRITE_RETRIES, !async_write);
+ STp->device->request_queue->rq_timeout,
+ MAX_WRITE_RETRIES, !async_write);
if (!SRpnt) {
retval = STbp->syscall_result;
goto out;
@@ -1804,7 +1862,8 @@ static long read_tape(struct scsi_tape *STp, long count,
SRpnt = *aSRpnt;
SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_RETRIES, 1);
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES, 1);
release_buffering(STp, 1);
*aSRpnt = SRpnt;
if (!SRpnt)
@@ -2213,7 +2272,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
DEBC( printk(KERN_INFO "%s: Long timeout set to %d seconds.\n", name,
(value & ~MT_ST_SET_LONG_TIMEOUT)));
} else {
- STp->device->timeout = value * HZ;
+ blk_queue_rq_timeout(STp->device->request_queue,
+ value * HZ);
DEBC( printk(KERN_INFO "%s: Normal timeout set to %d seconds.\n",
name, value) );
}
@@ -2311,7 +2371,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
{
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
+ int ret;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SENSE;
@@ -2320,14 +2381,17 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
cmd[2] = page;
cmd[4] = 255;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE,
- STp->device->timeout, 0, 1);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return STp->buffer->syscall_result;
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, cmd[4],
+ STp->device->request_queue->rq_timeout,
+ MAX_RETRIES);
st_release_request(SRpnt);
- return (STp->buffer)->syscall_result;
+ return ret ? : STp->buffer->syscall_result;
}
@@ -2335,9 +2399,9 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
static int write_mode_page(struct scsi_tape *STp, int page, int slow)
{
- int pgo;
+ int pgo, timeout, ret = 0;
unsigned char cmd[MAX_COMMAND_SIZE];
- struct st_request *SRpnt = NULL;
+ struct st_request *SRpnt;
memset(cmd, 0, MAX_COMMAND_SIZE);
cmd[0] = MODE_SELECT;
@@ -2351,14 +2415,21 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow)
(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE,
- (slow ? STp->long_timeout : STp->device->timeout), 0, 1);
- if (SRpnt == NULL)
- return (STp->buffer)->syscall_result;
+ SRpnt = st_allocate_request(STp);
+ if (!SRpnt)
+ return ret;
+
+ timeout = slow ? STp->long_timeout :
+ STp->device->request_queue->rq_timeout;
+
+ ret = st_scsi_kern_execute(SRpnt, cmd, DMA_TO_DEVICE,
+ STp->buffer->b_data, cmd[4], timeout, 0);
+ if (!ret)
+ ret = STp->buffer->syscall_result;
st_release_request(SRpnt);
- return (STp->buffer)->syscall_result;
+ return ret;
}
@@ -2464,7 +2535,7 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
}
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
else
timeout = STp->long_timeout;
@@ -2476,13 +2547,16 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
printk(ST_DEB_MSG "%s: Loading tape.\n", name);
);
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
- timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, timeout,
+ MAX_RETRIES);
+ if (retval)
+ goto out;
retval = (STp->buffer)->syscall_result;
- st_release_request(SRpnt);
if (!retval) { /* SCSI command successful */
@@ -2501,6 +2575,8 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
STps = &(STp->ps[STp->partition]);
STps->drv_file = STps->drv_block = (-1);
}
+out:
+ st_release_request(SRpnt);
return retval;
}
@@ -2638,7 +2714,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[2] = (arg >> 16);
cmd[3] = (arg >> 8);
cmd[4] = arg;
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
DEBC(
if (cmd_in == MTWEOF)
printk(ST_DEB_MSG "%s: Writing %d filemarks.\n", name,
@@ -2656,7 +2732,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[0] = REZERO_UNIT;
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
DEBC(printk(ST_DEB_MSG "%s: Rewinding tape.\n", name));
fileno = blkno = at_sm = 0;
@@ -2669,7 +2745,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[0] = START_STOP;
if (STp->immediate) {
cmd[1] = 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
cmd[4] = 3;
DEBC(printk(ST_DEB_MSG "%s: Retensioning tape.\n", name));
@@ -2702,7 +2778,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
cmd[1] = (arg ? 1 : 0); /* Long erase with non-zero argument */
if (STp->immediate) {
cmd[1] |= 2; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
else
timeout = STp->long_timeout * 8;
@@ -2754,7 +2830,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
(STp->buffer)->b_data[9] = (ltmp >> 16);
(STp->buffer)->b_data[10] = (ltmp >> 8);
(STp->buffer)->b_data[11] = ltmp;
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
DEBC(
if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
printk(ST_DEB_MSG
@@ -2776,12 +2852,15 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
return (-ENOSYS);
}
- SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
- timeout, MAX_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
return (STp->buffer)->syscall_result;
- ioctl_result = (STp->buffer)->syscall_result;
+ ioctl_result = st_scsi_kern_execute(SRpnt, cmd, direction,
+ STp->buffer->b_data, datalen,
+ timeout, MAX_RETRIES);
+ if (!ioctl_result)
+ ioctl_result = (STp->buffer)->syscall_result;
if (!ioctl_result) { /* SCSI command successful */
st_release_request(SRpnt);
@@ -2943,10 +3022,17 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti
if (!logical && !STp->scsi2_logical)
scmd[1] = 1;
}
- SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
- STp->device->timeout, MAX_READY_RETRIES, 1);
+
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ result = st_scsi_kern_execute(SRpnt, scmd, DMA_FROM_DEVICE,
+ STp->buffer->b_data, 20,
+ STp->device->request_queue->rq_timeout,
+ MAX_READY_RETRIES);
+ if (result)
+ goto out;
if ((STp->buffer)->syscall_result != 0 ||
(STp->device->scsi_level >= SCSI_2 &&
@@ -2974,6 +3060,7 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti
DEBC(printk(ST_DEB_MSG "%s: Got tape pos. blk %d part %d.\n", name,
*block, *partition));
}
+out:
st_release_request(SRpnt);
SRpnt = NULL;
@@ -3045,13 +3132,17 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
}
if (STp->immediate) {
scmd[1] |= 1; /* Don't wait for completion */
- timeout = STp->device->timeout;
+ timeout = STp->device->request_queue->rq_timeout;
}
- SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
- timeout, MAX_READY_RETRIES, 1);
+ SRpnt = st_allocate_request(STp);
if (!SRpnt)
- return (STp->buffer)->syscall_result;
+ return STp->buffer->syscall_result;
+
+ result = st_scsi_kern_execute(SRpnt, scmd, DMA_NONE, NULL, 0,
+ timeout, MAX_READY_RETRIES);
+ if (result)
+ goto out;
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
@@ -3076,7 +3167,7 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
STps->drv_block = STps->drv_file = 0;
result = 0;
}
-
+out:
st_release_request(SRpnt);
SRpnt = NULL;
@@ -4029,7 +4120,7 @@ static int st_probe(struct device *dev)
tpnt->partition = 0;
tpnt->new_partition = 0;
tpnt->nbr_partitions = 0;
- tpnt->device->timeout = ST_TIMEOUT;
+ blk_queue_rq_timeout(tpnt->device->request_queue, ST_TIMEOUT);
tpnt->long_timeout = ST_LONG_TIMEOUT;
tpnt->try_dio = try_direct_io && !SDp->host->unchecked_isa_dma;
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 2fa830c0be27..a3a18ad73125 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -1108,8 +1108,7 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_scsi_host_put;
}
- hba->mmio_base = ioremap_nocache(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ hba->mmio_base = pci_ioremap_bar(pdev, 0);
if ( !hba->mmio_base) {
printk(KERN_ERR DRV_NAME "(%s): memory map failed\n",
pci_name(pdev));
diff --git a/drivers/scsi/sym53c416.c b/drivers/scsi/sym53c416.c
index f7d279542fa5..e5c369bb568f 100644
--- a/drivers/scsi/sym53c416.c
+++ b/drivers/scsi/sym53c416.c
@@ -6,7 +6,7 @@
* Changes :
*
* Marcelo Tosatti <marcelo@conectiva.com.br> : Added io_request_lock locking
- * Alan Cox <alan@redhat.com> : Cleaned up code formatting
+ * Alan Cox <alan@lxorguk.ukuu.org.uk> : Cleaned up code formatting
* Fixed an irq locking bug
* Added ISAPnP support
* Bjoern A. Zeeb <bzeeb@zabbadoz.net> : Initial irq locking updates
diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c
index 69ac6e590f1d..9a4273445c0d 100644
--- a/drivers/scsi/tmscsim.c
+++ b/drivers/scsi/tmscsim.c
@@ -2572,9 +2572,10 @@ static struct pci_driver dc390_driver = {
static int __init dc390_module_init(void)
{
- if (!disable_clustering)
+ if (!disable_clustering) {
printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n");
printk(KERN_INFO " with \"disable_clustering=1\" and report to maintainers\n");
+ }
if (tmscsim[0] == -1 || tmscsim[0] > 15) {
tmscsim[0] = 7;
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 329eb8780e74..601e95141cbe 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1111,7 +1111,8 @@ static int u14_34f_detect(struct scsi_host_template *tpnt) {
static void map_dma(unsigned int i, unsigned int j) {
unsigned int data_len = 0;
- unsigned int k, count, pci_dir;
+ unsigned int k, pci_dir;
+ int count;
struct scatterlist *sg;
struct mscp *cpp;
struct scsi_cmnd *SCpnt;
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index d4c13561f4a6..093610bcfcce 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -146,13 +146,13 @@
*
* use host->host_lock, not io_request_lock, cleanups
*
- * 2002/10/04 - Alan Cox <alan@redhat.com>
+ * 2002/10/04 - Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Use dev_id for interrupts, kill __func__ pasting
* Add a lock for the scb pool, clean up all other cli/sti usage stuff
* Use the adapter lock for the other places we had the cli's
*
- * 2002/10/06 - Alan Cox <alan@redhat.com>
+ * 2002/10/06 - Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Switch to new style error handling
* Clean up delay to udelay, and yielding sleeps
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 579d63a81aa2..b695ab3142d8 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -447,7 +447,7 @@ config SERIAL_CLPS711X_CONSOLE
config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
- depends on ARM && PLAT_S3C24XX
+ depends on ARM && PLAT_S3C
select SERIAL_CORE
help
Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
@@ -455,6 +455,16 @@ config SERIAL_SAMSUNG
provide all of these ports, depending on how the serial port
pins are configured.
+config SERIAL_SAMSUNG_UARTS
+ int
+ depends on SERIAL_SAMSUNG
+ default 2 if ARCH_S3C2400
+ default 4 if ARCH_S3C64XX || CPU_S3C2443
+ default 3
+ help
+ Select the number of available UART ports for the Samsung S3C
+ serial driver
+
config SERIAL_SAMSUNG_DEBUG
bool "Samsung SoC serial debug"
depends on SERIAL_SAMSUNG && DEBUG_LL
@@ -508,7 +518,20 @@ config SERIAL_S3C2440
help
Serial port support for the Samsung S3C2440 and S3C2442 SoC
+config SERIAL_S3C24A0
+ tristate "Samsung S3C24A0 Serial port support"
+ depends on SERIAL_SAMSUNG && CPU_S3C24A0
+ default y if CPU_S3C24A0
+ help
+ Serial port support for the Samsung S3C24A0 SoC
+config SERIAL_S3C6400
+ tristate "Samsung S3C6400/S3C6410 Serial port support"
+ depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410)
+ default y
+ help
+ Serial port support for the Samsung S3C6400 and S3C6410
+ SoCs
config SERIAL_DZ
bool "DECstation DZ serial driver"
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 0c17c8ddb19d..dfe775ac45b2 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
+obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
+obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c
index 71562689116f..e3a5ad5ef1d6 100644
--- a/drivers/serial/amba-pl010.c
+++ b/drivers/serial/amba-pl010.c
@@ -692,7 +692,7 @@ static int pl010_probe(struct amba_device *dev, void *id)
goto free;
}
- uap->clk = clk_get(&dev->dev, "UARTCLK");
+ uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
goto unmap;
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index b7180046f8db..8b2b9700f3e4 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -756,7 +756,7 @@ static int pl011_probe(struct amba_device *dev, void *id)
goto free;
}
- uap->clk = clk_get(&dev->dev, "UARTCLK");
+ uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
goto unmap;
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 3f90f1bbbbcd..a50954612b60 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -66,7 +66,7 @@
#define ONEMS 0xb0 /* One Millisecond register */
#define UTS 0xb4 /* UART Test Register */
#endif
-#ifdef CONFIG_ARCH_IMX
+#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
#define BIPR1 0xb0 /* Incremental Preset Register 1 */
#define BIPR2 0xb4 /* Incremental Preset Register 2 */
#define BIPR3 0xb8 /* Incremental Preset Register 3 */
@@ -96,7 +96,7 @@
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#ifdef CONFIG_ARCH_IMX
+#if defined(CONFIG_ARCH_IMX) || defined(CONFIG_ARCH_MX1)
#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
#endif
#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
@@ -187,11 +187,11 @@
#define MAX_INTERNAL_IRQ IMX_IRQS
#endif
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
+#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
-#define MAX_INTERNAL_IRQ MXC_MAX_INT_LINES
+#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
#endif
/*
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 28c00c3d58f5..0c3a2ab1612c 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -429,14 +429,24 @@ mpc52xx_uart_tx_empty(struct uart_port *port)
static void
mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- /* Not implemented */
+ if (mctrl & TIOCM_RTS)
+ out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
+ else
+ out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
}
static unsigned int
mpc52xx_uart_get_mctrl(struct uart_port *port)
{
- /* Not implemented */
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+ unsigned int ret = TIOCM_DSR;
+ u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+
+ if (!(status & MPC52xx_PSC_CTS))
+ ret |= TIOCM_CTS;
+ if (!(status & MPC52xx_PSC_DCD))
+ ret |= TIOCM_CAR;
+
+ return ret;
}
static void
@@ -479,7 +489,15 @@ mpc52xx_uart_stop_rx(struct uart_port *port)
static void
mpc52xx_uart_enable_ms(struct uart_port *port)
{
- /* Not implemented */
+ struct mpc52xx_psc __iomem *psc = PSC(port);
+
+ /* clear D_*-bits by reading them */
+ in_8(&psc->mpc52xx_psc_ipcr);
+ /* enable CTS and DCD as IPC interrupts */
+ out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
}
static void
@@ -580,6 +598,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
MPC52xx_PSC_MODE_ONE_STOP_5_BITS :
MPC52xx_PSC_MODE_ONE_STOP;
+ if (new->c_cflag & CRTSCTS) {
+ mr1 |= MPC52xx_PSC_MODE_RXRTS;
+ mr2 |= MPC52xx_PSC_MODE_TXCTS;
+ }
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
@@ -617,6 +639,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
out_8(&psc->ctur, ctr >> 8);
out_8(&psc->ctlr, ctr & 0xff);
+ if (UART_ENABLE_MS(port, new->c_cflag))
+ mpc52xx_uart_enable_ms(port);
+
/* Reenable TX & RX */
out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
@@ -752,10 +777,15 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
if (status & MPC52xx_PSC_SR_RB) {
flag = TTY_BREAK;
uart_handle_break(port);
- } else if (status & MPC52xx_PSC_SR_PE)
+ port->icount.brk++;
+ } else if (status & MPC52xx_PSC_SR_PE) {
flag = TTY_PARITY;
- else if (status & MPC52xx_PSC_SR_FE)
+ port->icount.parity++;
+ }
+ else if (status & MPC52xx_PSC_SR_FE) {
flag = TTY_FRAME;
+ port->icount.frame++;
+ }
/* Clear error condition */
out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT);
@@ -769,6 +799,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
* affect the current character
*/
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ port->icount.overrun++;
}
}
@@ -826,6 +857,7 @@ mpc52xx_uart_int(int irq, void *dev_id)
struct uart_port *port = dev_id;
unsigned long pass = ISR_PASS_LIMIT;
unsigned int keepgoing;
+ u8 status;
spin_lock(&port->lock);
@@ -842,6 +874,13 @@ mpc52xx_uart_int(int irq, void *dev_id)
if (psc_ops->tx_rdy(port))
keepgoing |= mpc52xx_uart_int_tx_chars(port);
+ status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+ if (status & MPC52xx_PSC_D_DCD)
+ uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
+
+ if (status & MPC52xx_PSC_D_CTS)
+ uart_handle_cts_change(port, !(status & MPC52xx_PSC_CTS));
+
/* Limit number of iteration */
if (!(--pass))
keepgoing = 0;
@@ -1109,22 +1148,29 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match)
return ret;
port->mapbase = res.start;
+ if (!port->mapbase) {
+ dev_dbg(&op->dev, "Could not allocate resources for PSC\n");
+ return -EINVAL;
+ }
+
port->irq = irq_of_parse_and_map(op->node, 0);
+ if (port->irq == NO_IRQ) {
+ dev_dbg(&op->dev, "Could not get irq\n");
+ return -EINVAL;
+ }
dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n",
(void *)port->mapbase, port->irq, port->uartclk);
- if ((port->irq == NO_IRQ) || !port->mapbase) {
- printk(KERN_ERR "Could not allocate resources for PSC\n");
- return -EINVAL;
- }
-
/* Add the port to the uart sub-system */
ret = uart_add_one_port(&mpc52xx_uart_driver, port);
- if (!ret)
- dev_set_drvdata(&op->dev, (void *)port);
+ if (ret) {
+ irq_dispose_mapping(port->irq);
+ return ret;
+ }
- return ret;
+ dev_set_drvdata(&op->dev, (void *)port);
+ return 0;
}
static int
diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c
index 317b061f7641..ad3488504010 100644
--- a/drivers/serial/pmac_zilog.c
+++ b/drivers/serial/pmac_zilog.c
@@ -1383,6 +1383,29 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser)
return -EINVAL;
}
+#ifdef CONFIG_CONSOLE_POLL
+
+static int pmz_poll_get_char(struct uart_port *port)
+{
+ struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+
+ while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0)
+ udelay(5);
+ return read_zsdata(uap);
+}
+
+static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
+{
+ struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+
+ /* Wait for the transmit buffer to empty. */
+ while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0)
+ udelay(5);
+ write_zsdata(uap, c);
+}
+
+#endif
+
static struct uart_ops pmz_pops = {
.tx_empty = pmz_tx_empty,
.set_mctrl = pmz_set_mctrl,
@@ -1400,6 +1423,10 @@ static struct uart_ops pmz_pops = {
.request_port = pmz_request_port,
.config_port = pmz_config_port,
.verify_port = pmz_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = pmz_poll_get_char,
+ .poll_put_char = pmz_poll_put_char,
+#endif
};
/*
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
index abc00be55433..f6e3b86bb0be 100644
--- a/drivers/serial/pxa.c
+++ b/drivers/serial/pxa.c
@@ -48,6 +48,7 @@
#include <mach/hardware.h>
#include <asm/irq.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-uart.h>
struct uart_pxa_port {
@@ -766,7 +767,7 @@ static int serial_pxa_probe(struct platform_device *dev)
if (!sport)
return -ENOMEM;
- sport->clk = clk_get(&dev->dev, "UARTCLK");
+ sport->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
goto err_free;
diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c
new file mode 100644
index 000000000000..ebf2fd3c8f7d
--- /dev/null
+++ b/drivers/serial/s3c24a0.c
@@ -0,0 +1,118 @@
+/* linux/drivers/serial/s3c24a0.c
+ *
+ * Driver for Samsung S3C24A0 SoC onboard UARTs.
+ *
+ * Based on drivers/serial/s3c2410.c
+ *
+ * Author: Sandeep Patil <sandeep.patil@azingo.com>
+ *
+ * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-gpio.h>
+
+#include "samsung.h"
+
+static int s3c24a0_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ if (strcmp(clk->name, "uclk") == 0)
+ ucon |= S3C2410_UCON_UCLK;
+ else
+ ucon &= ~S3C2410_UCON_UCLK;
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+static int s3c24a0_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ clk->divisor = 1;
+ clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk";
+
+ return 0;
+}
+
+static int s3c24a0_serial_resetport(struct uart_port *port,
+ struct s3c2410_uartcfg *cfg)
+{
+ dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n",
+ port, port->mapbase, cfg);
+
+ wr_regl(port, S3C2410_UCON, cfg->ucon);
+ wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+ /* reset both fifos */
+
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+ return 0;
+}
+
+static struct s3c24xx_uart_info s3c24a0_uart_inf = {
+ .name = "Samsung S3C24A0 UART",
+ .type = PORT_S3C2410,
+ .fifosize = 16,
+ .rx_fifomask = S3C24A0_UFSTAT_RXMASK,
+ .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT,
+ .rx_fifofull = S3C24A0_UFSTAT_RXFULL,
+ .tx_fifofull = S3C24A0_UFSTAT_TXFULL,
+ .tx_fifomask = S3C24A0_UFSTAT_TXMASK,
+ .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT,
+ .get_clksrc = s3c24a0_serial_getsource,
+ .set_clksrc = s3c24a0_serial_setsource,
+ .reset_port = s3c24a0_serial_resetport,
+};
+
+static int s3c24a0_serial_probe(struct platform_device *dev)
+{
+ return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf);
+}
+
+static struct platform_driver s3c24a0_serial_drv = {
+ .probe = s3c24a0_serial_probe,
+ .remove = s3c24xx_serial_remove,
+ .driver = {
+ .name = "s3c24a0-uart",
+ .owner = THIS_MODULE,
+ },
+};
+
+s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+
+static int __init s3c24a0_serial_init(void)
+{
+ return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf);
+}
+
+static void __exit s3c24a0_serial_exit(void)
+{
+ platform_driver_unregister(&s3c24a0_serial_drv);
+}
+
+module_init(s3c24a0_serial_init);
+module_exit(s3c24a0_serial_exit);
+
diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c
new file mode 100644
index 000000000000..06936d13393f
--- /dev/null
+++ b/drivers/serial/s3c6400.c
@@ -0,0 +1,151 @@
+/* linux/drivers/serial/s3c6400.c
+ *
+ * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs.
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+
+#include "samsung.h"
+
+static int s3c6400_serial_setsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ if (strcmp(clk->name, "uclk0") == 0) {
+ ucon &= ~S3C6400_UCON_CLKMASK;
+ ucon |= S3C6400_UCON_UCLK0;
+ } else if (strcmp(clk->name, "uclk1") == 0)
+ ucon |= S3C6400_UCON_UCLK1;
+ else if (strcmp(clk->name, "pclk") == 0) {
+ /* See notes about transitioning from UCLK to PCLK */
+ ucon &= ~S3C6400_UCON_UCLK0;
+ } else {
+ printk(KERN_ERR "unknown clock source %s\n", clk->name);
+ return -EINVAL;
+ }
+
+ wr_regl(port, S3C2410_UCON, ucon);
+ return 0;
+}
+
+
+static int s3c6400_serial_getsource(struct uart_port *port,
+ struct s3c24xx_uart_clksrc *clk)
+{
+ u32 ucon = rd_regl(port, S3C2410_UCON);
+
+ clk->divisor = 1;
+
+ switch (ucon & S3C6400_UCON_CLKMASK) {
+ case S3C6400_UCON_UCLK0:
+ clk->name = "uclk0";
+ break;
+
+ case S3C6400_UCON_UCLK1:
+ clk->name = "uclk1";
+ break;
+
+ case S3C6400_UCON_PCLK:
+ case S3C6400_UCON_PCLK2:
+ clk->name = "pclk";
+ break;
+ }
+
+ return 0;
+}
+
+static int s3c6400_serial_resetport(struct uart_port *port,
+ struct s3c2410_uartcfg *cfg)
+{
+ unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+ dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n",
+ port, port->mapbase, cfg);
+
+ /* ensure we don't change the clock settings... */
+
+ ucon &= S3C6400_UCON_CLKMASK;
+
+ wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
+ wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+ /* reset both fifos */
+
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+ wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+ return 0;
+}
+
+static struct s3c24xx_uart_info s3c6400_uart_inf = {
+ .name = "Samsung S3C6400 UART",
+ .type = PORT_S3C6400,
+ .fifosize = 64,
+ .rx_fifomask = S3C2440_UFSTAT_RXMASK,
+ .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
+ .rx_fifofull = S3C2440_UFSTAT_RXFULL,
+ .tx_fifofull = S3C2440_UFSTAT_TXFULL,
+ .tx_fifomask = S3C2440_UFSTAT_TXMASK,
+ .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
+ .get_clksrc = s3c6400_serial_getsource,
+ .set_clksrc = s3c6400_serial_setsource,
+ .reset_port = s3c6400_serial_resetport,
+};
+
+/* device management */
+
+static int s3c6400_serial_probe(struct platform_device *dev)
+{
+ dbg("s3c6400_serial_probe: dev=%p\n", dev);
+ return s3c24xx_serial_probe(dev, &s3c6400_uart_inf);
+}
+
+static struct platform_driver s3c6400_serial_drv = {
+ .probe = s3c6400_serial_probe,
+ .remove = s3c24xx_serial_remove,
+ .driver = {
+ .name = "s3c6400-uart",
+ .owner = THIS_MODULE,
+ },
+};
+
+s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+
+static int __init s3c6400_serial_init(void)
+{
+ return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
+}
+
+static void __exit s3c6400_serial_exit(void)
+{
+ platform_driver_unregister(&s3c6400_serial_drv);
+}
+
+module_init(s3c6400_serial_init);
+module_exit(s3c6400_serial_exit);
+
+MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c6400-uart");
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index 1e219d3d0352..41ac94872b8d 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -42,13 +42,14 @@
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <asm/irq.h>
#include <mach/hardware.h>
+#include <mach/map.h>
#include <plat/regs-serial.h>
-#include <mach/regs-gpio.h>
#include "samsung.h"
@@ -58,19 +59,6 @@
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
-/* we can support 3 uarts, but not always use them */
-
-#ifdef CONFIG_CPU_S3C2400
-#define NR_PORTS (2)
-#else
-#define NR_PORTS (3)
-#endif
-
-/* port irq numbers */
-
-#define TX_IRQ(port) ((port)->irq + 1)
-#define RX_IRQ(port) ((port)->irq)
-
/* macros to change one thing to another */
#define tx_enabled(port) ((port)->unused[0])
@@ -136,8 +124,10 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port)
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (tx_enabled(port)) {
- disable_irq(TX_IRQ(port));
+ disable_irq(ourport->tx_irq);
tx_enabled(port) = 0;
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_enable(port);
@@ -146,11 +136,13 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
static void s3c24xx_serial_start_tx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
- enable_irq(TX_IRQ(port));
+ enable_irq(ourport->tx_irq);
tx_enabled(port) = 1;
}
}
@@ -158,9 +150,11 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+
if (rx_enabled(port)) {
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
- disable_irq(RX_IRQ(port));
+ disable_irq(ourport->rx_irq);
rx_enabled(port) = 0;
}
}
@@ -384,13 +378,13 @@ static void s3c24xx_serial_shutdown(struct uart_port *port)
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (ourport->tx_claimed) {
- free_irq(TX_IRQ(port), ourport);
+ free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0;
ourport->tx_claimed = 0;
}
if (ourport->rx_claimed) {
- free_irq(RX_IRQ(port), ourport);
+ free_irq(ourport->rx_irq, ourport);
ourport->rx_claimed = 0;
rx_enabled(port) = 0;
}
@@ -407,12 +401,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
rx_enabled(port) = 1;
- ret = request_irq(RX_IRQ(port),
- s3c24xx_serial_rx_chars, 0,
+ ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret != 0) {
- printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
+ printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}
@@ -422,12 +415,11 @@ static int s3c24xx_serial_startup(struct uart_port *port)
tx_enabled(port) = 1;
- ret = request_irq(TX_IRQ(port),
- s3c24xx_serial_tx_chars, 0,
+ ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret) {
- printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
+ printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}
@@ -452,6 +444,8 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ ourport->pm_level = level;
+
switch (level) {
case 3:
if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
@@ -661,6 +655,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
ourport->clksrc = clksrc;
ourport->baudclk = clk;
+ ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
switch (termios->c_cflag & CSIZE) {
@@ -752,6 +747,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port)
return "S3C2440";
case PORT_S3C2412:
return "S3C2412";
+ case PORT_S3C6400:
+ return "S3C6400/10";
default:
return NULL;
}
@@ -827,14 +824,14 @@ static struct uart_ops s3c24xx_serial_ops = {
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
- .nr = 3,
+ .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
-static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
+static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
@@ -859,7 +856,7 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
.line = 1,
}
},
-#if NR_PORTS > 2
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
.port = {
@@ -872,6 +869,20 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
+ },
+#endif
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
+ [3] = {
+ .port = {
+ .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
+ .iotype = UPIO_MEM,
+ .irq = IRQ_S3CUART_RX3,
+ .uartclk = 0,
+ .fifosize = 16,
+ .ops = &s3c24xx_serial_ops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 3,
+ }
}
#endif
};
@@ -890,6 +901,89 @@ static inline int s3c24xx_serial_resetport(struct uart_port *port,
return (info->reset_port)(port, cfg);
}
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct s3c24xx_uart_port *port;
+ struct uart_port *uport;
+
+ port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
+ uport = &port->port;
+
+ /* check to see if port is enabled */
+
+ if (port->pm_level != 0)
+ return 0;
+
+ /* try and work out if the baudrate is changing, we can detect
+ * a change in rate, but we do not have support for detecting
+ * a disturbance in the clock-rate over the change.
+ */
+
+ if (IS_ERR(port->clk))
+ goto exit;
+
+ if (port->baudclk_rate == clk_get_rate(port->clk))
+ goto exit;
+
+ if (val == CPUFREQ_PRECHANGE) {
+ /* we should really shut the port down whilst the
+ * frequency change is in progress. */
+
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ struct ktermios *termios;
+ struct tty_struct *tty;
+
+ if (uport->info == NULL)
+ goto exit;
+
+ tty = uport->info->port.tty;
+
+ if (tty == NULL)
+ goto exit;
+
+ termios = tty->termios;
+
+ if (termios == NULL) {
+ printk(KERN_WARNING "%s: no termios?\n", __func__);
+ goto exit;
+ }
+
+ s3c24xx_serial_set_termios(uport, termios, NULL);
+ }
+
+ exit:
+ return 0;
+}
+
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
+
+ return cpufreq_register_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+ cpufreq_unregister_notifier(&port->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+ return 0;
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+}
+#endif
+
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
@@ -914,8 +1008,11 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
if (port->mapbase != 0)
return 0;
- if (cfg->hwport > 3)
- return -EINVAL;
+ if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
+ printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
+ cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
+ return -ERANGE;
+ }
/* setup info for port */
port->dev = &platdev->dev;
@@ -943,18 +1040,26 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
- port->mapbase = res->start;
- port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
+ port->mapbase = res->start;
+ port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
- else
+ else {
port->irq = ret;
+ ourport->rx_irq = ret;
+ ourport->tx_irq = ret + 1;
+ }
+
+ ret = platform_get_irq(platdev, 1);
+ if (ret > 0)
+ ourport->tx_irq = ret;
ourport->clk = clk_get(&platdev->dev, "uart");
- dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
- port->mapbase, port->membase, port->irq, port->uartclk);
+ dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
+ port->mapbase, port->membase, port->irq,
+ ourport->rx_irq, ourport->tx_irq, port->uartclk);
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);
@@ -1002,6 +1107,10 @@ int s3c24xx_serial_probe(struct platform_device *dev,
if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
+ ret = s3c24xx_serial_cpufreq_register(ourport);
+ if (ret < 0)
+ dev_err(&dev->dev, "failed to add cpufreq notifier\n");
+
return 0;
probe_err:
@@ -1015,6 +1124,7 @@ int s3c24xx_serial_remove(struct platform_device *dev)
struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
if (port) {
+ s3c24xx_serial_cpufreq_deregister(to_ourport(port));
device_remove_file(&dev->dev, &dev_attr_clock_source);
uart_remove_one_port(&s3c24xx_uart_drv, port);
}
@@ -1219,7 +1329,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
platdev_ptr = s3c24xx_uart_devs;
- for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) {
+ for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
}
@@ -1240,7 +1350,7 @@ s3c24xx_serial_console_setup(struct console *co, char *options)
/* is this a valid port */
- if (co->index == -1 || co->index >= NR_PORTS)
+ if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
co->index = 0;
port = &s3c24xx_serial_ports[co->index].port;
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h
index 5c92ebbe7d9e..571d6b90d206 100644
--- a/drivers/serial/samsung.h
+++ b/drivers/serial/samsung.h
@@ -33,12 +33,21 @@ struct s3c24xx_uart_info {
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
+ unsigned int pm_level;
+ unsigned long baudclk_rate;
+
+ unsigned int rx_irq;
+ unsigned int tx_irq;
struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
+
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
};
/* conversion functions */
diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c
index 61dc8b3daa26..a7bf024a8286 100644
--- a/drivers/serial/serial_lh7a40x.c
+++ b/drivers/serial/serial_lh7a40x.c
@@ -41,9 +41,10 @@
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/irq.h>
+#include <mach/hardware.h>
#define DEV_MAJOR 204
#define DEV_MINOR 16
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 165fc010978c..557b54ab2f25 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -51,7 +51,6 @@
#ifdef CONFIG_SUPERH
#include <asm/clock.h>
#include <asm/sh_bios.h>
-#include <asm/kgdb.h>
#endif
#include "sh-sci.h"
@@ -65,10 +64,6 @@ struct sci_port {
/* Port IRQs: ERI, RXI, TXI, BRI (optional) */
unsigned int irqs[SCIx_NR_IRQS];
- /* Port pin configuration */
- void (*init_pins)(struct uart_port *port,
- unsigned int cflag);
-
/* Port enable callback */
void (*enable)(struct uart_port *port);
@@ -85,10 +80,6 @@ struct sci_port {
#endif
};
-#ifdef CONFIG_SH_KGDB
-static struct sci_port *kgdb_sci_port;
-#endif
-
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static struct sci_port *serial_console_port;
#endif
@@ -101,21 +92,26 @@ static void sci_stop_tx(struct uart_port *port);
static struct sci_port sci_ports[SCI_NPORTS];
static struct uart_driver sci_uart_driver;
-#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \
- defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static inline struct sci_port *
+to_sci_port(struct uart_port *uart)
+{
+ return container_of(uart, struct sci_port, port);
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+
+#ifdef CONFIG_CONSOLE_POLL
static inline void handle_error(struct uart_port *port)
{
/* Clear error flags */
sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
}
-static int get_char(struct uart_port *port)
+static int sci_poll_get_char(struct uart_port *port)
{
- unsigned long flags;
unsigned short status;
int c;
- spin_lock_irqsave(&port->lock, flags);
do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
@@ -123,23 +119,21 @@ static int get_char(struct uart_port *port)
continue;
}
} while (!(status & SCxSR_RDxF(port)));
+
c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
+
+ /* Dummy read */
+ sci_in(port, SCxSR);
sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- spin_unlock_irqrestore(&port->lock, flags);
return c;
}
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
+#endif
-#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || defined(CONFIG_SH_KGDB)
-static void put_char(struct uart_port *port, char c)
+static void sci_poll_put_char(struct uart_port *port, unsigned char c)
{
- unsigned long flags;
unsigned short status;
- spin_lock_irqsave(&port->lock, flags);
-
do {
status = sci_in(port, SCxSR);
} while (!(status & SCxSR_TDxE(port)));
@@ -147,96 +141,22 @@ static void put_char(struct uart_port *port, char c)
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
sci_out(port, SCxTDR, c);
-
- spin_unlock_irqrestore(&port->lock, flags);
}
-#endif
-
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
-static void put_string(struct sci_port *sci_port, const char *buffer, int count)
-{
- struct uart_port *port = &sci_port->port;
- const unsigned char *p = buffer;
- int i;
-
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
- int checksum;
- int usegdb=0;
-
-#ifdef CONFIG_SH_STANDARD_BIOS
- /* This call only does a trap the first time it is
- * called, and so is safe to do here unconditionally
- */
- usegdb |= sh_bios_in_gdb_mode();
-#endif
-#ifdef CONFIG_SH_KGDB
- usegdb |= (kgdb_in_gdb_mode && (sci_port == kgdb_sci_port));
-#endif
-
- if (usegdb) {
- /* $<packet info>#<checksum>. */
- do {
- unsigned char c;
- put_char(port, '$');
- put_char(port, 'O'); /* 'O'utput to console */
- checksum = 'O';
-
- for (i=0; i<count; i++) { /* Don't use run length encoding */
- int h, l;
-
- c = *p++;
- h = hex_asc_hi(c);
- l = hex_asc_lo(c);
- put_char(port, h);
- put_char(port, l);
- checksum += h + l;
- }
- put_char(port, '#');
- put_char(port, hex_asc_hi(checksum));
- put_char(port, hex_asc_lo(checksum));
- } while (get_char(port) != '+');
- } else
-#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
- for (i=0; i<count; i++) {
- if (*p == 10)
- put_char(port, '\r');
- put_char(port, *p++);
- }
-}
-#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-
-#ifdef CONFIG_SH_KGDB
-static int kgdb_sci_getchar(void)
-{
- int c;
-
- /* Keep trying to read a character, this could be neater */
- while ((c = get_char(&kgdb_sci_port->port)) < 0)
- cpu_relax();
-
- return c;
-}
-
-static inline void kgdb_sci_putchar(int c)
-{
- put_char(&kgdb_sci_port->port, c);
-}
-#endif /* CONFIG_SH_KGDB */
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
#if defined(__H8300S__)
enum { sci_disable, sci_enable };
-static void h8300_sci_config(struct uart_port* port, unsigned int ctrl)
+static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
{
- volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL;
+ volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL;
int ch = (port->mapbase - SMR0) >> 3;
unsigned char mask = 1 << (ch+1);
- if (ctrl == sci_disable) {
+ if (ctrl == sci_disable)
*mstpcrl |= mask;
- } else {
+ else
*mstpcrl &= ~mask;
- }
}
static inline void h8300_sci_enable(struct uart_port *port)
@@ -251,7 +171,7 @@ static inline void h8300_sci_disable(struct uart_port *port)
#endif
#if defined(__H8300H__) || defined(__H8300S__)
-static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
+static void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
int ch = (port->mapbase - SMR0) >> 3;
@@ -266,141 +186,99 @@ static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
/* tx mark output*/
H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
}
-#else
-#define sci_init_pins_sci NULL
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
-static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
- unsigned int fcr_val = 0;
-
- if (cflag & CRTSCTS)
- fcr_val |= SCFCR_MCE;
-
- sci_out(port, SCFCR, fcr_val);
-}
-#else
-#define sci_init_pins_irda NULL
-#endif
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
-static void sci_init_pins_scif(struct uart_port* port, unsigned int cflag)
-{
- unsigned int fcr_val = 0;
-
- set_sh771x_scif_pfc(port);
- if (cflag & CRTSCTS) {
- fcr_val |= SCFCR_MCE;
- }
- sci_out(port, SCFCR, fcr_val);
+ if (port->mapbase == 0xA4400000) {
+ __raw_writew(__raw_readw(PACR) & 0xffc0, PACR);
+ __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR);
+ } else if (port->mapbase == 0xA4410000)
+ __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR);
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
- unsigned int fcr_val = 0;
unsigned short data;
if (cflag & CRTSCTS) {
/* enable RTS/CTS */
if (port->mapbase == 0xa4430000) { /* SCIF0 */
/* Clear PTCR bit 9-2; enable all scif pins but sck */
- data = ctrl_inw(PORT_PTCR);
- ctrl_outw((data & 0xfc03), PORT_PTCR);
+ data = __raw_readw(PORT_PTCR);
+ __raw_writew((data & 0xfc03), PORT_PTCR);
} else if (port->mapbase == 0xa4438000) { /* SCIF1 */
/* Clear PVCR bit 9-2 */
- data = ctrl_inw(PORT_PVCR);
- ctrl_outw((data & 0xfc03), PORT_PVCR);
+ data = __raw_readw(PORT_PVCR);
+ __raw_writew((data & 0xfc03), PORT_PVCR);
}
- fcr_val |= SCFCR_MCE;
} else {
if (port->mapbase == 0xa4430000) { /* SCIF0 */
/* Clear PTCR bit 5-2; enable only tx and rx */
- data = ctrl_inw(PORT_PTCR);
- ctrl_outw((data & 0xffc3), PORT_PTCR);
+ data = __raw_readw(PORT_PTCR);
+ __raw_writew((data & 0xffc3), PORT_PTCR);
} else if (port->mapbase == 0xa4438000) { /* SCIF1 */
/* Clear PVCR bit 5-2 */
- data = ctrl_inw(PORT_PVCR);
- ctrl_outw((data & 0xffc3), PORT_PVCR);
+ data = __raw_readw(PORT_PVCR);
+ __raw_writew((data & 0xffc3), PORT_PVCR);
}
}
- sci_out(port, SCFCR, fcr_val);
}
#elif defined(CONFIG_CPU_SH3)
/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
- unsigned int fcr_val = 0;
unsigned short data;
/* We need to set SCPCR to enable RTS/CTS */
- data = ctrl_inw(SCPCR);
+ data = __raw_readw(SCPCR);
/* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/
- ctrl_outw(data & 0x0fcf, SCPCR);
+ __raw_writew(data & 0x0fcf, SCPCR);
- if (cflag & CRTSCTS)
- fcr_val |= SCFCR_MCE;
- else {
+ if (!(cflag & CRTSCTS)) {
/* We need to set SCPCR to enable RTS/CTS */
- data = ctrl_inw(SCPCR);
+ data = __raw_readw(SCPCR);
/* Clear out SCP7MD1,0, SCP4MD1,0,
Set SCP6MD1,0 = {01} (output) */
- ctrl_outw((data & 0x0fcf) | 0x1000, SCPCR);
+ __raw_writew((data & 0x0fcf) | 0x1000, SCPCR);
data = ctrl_inb(SCPDR);
/* Set /RTS2 (bit6) = 0 */
ctrl_outb(data & 0xbf, SCPDR);
}
-
- sci_out(port, SCFCR, fcr_val);
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7722)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
- unsigned int fcr_val = 0;
unsigned short data;
if (port->mapbase == 0xffe00000) {
- data = ctrl_inw(PSCR);
+ data = __raw_readw(PSCR);
data &= ~0x03cf;
- if (cflag & CRTSCTS)
- fcr_val |= SCFCR_MCE;
- else
+ if (!(cflag & CRTSCTS))
data |= 0x0340;
- ctrl_outw(data, PSCR);
+ __raw_writew(data, PSCR);
}
- /* SCIF1 and SCIF2 should be setup by board code */
-
- sci_out(port, SCFCR, fcr_val);
-}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7723)
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
-{
- /* Nothing to do here.. */
- sci_out(port, SCFCR, 0);
}
-#else
-/* For SH7750 */
-static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
-{
- unsigned int fcr_val = 0;
-
- if (cflag & CRTSCTS) {
- fcr_val |= SCFCR_MCE;
- } else {
-#if defined(CONFIG_CPU_SUBTYPE_SH7343) || defined(CONFIG_CPU_SUBTYPE_SH7366)
- /* Nothing */
#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785) || \
defined(CONFIG_CPU_SUBTYPE_SHX3)
- ctrl_outw(0x0080, SCSPTR0); /* Set RTS = 1 */
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ if (!(cflag & CRTSCTS))
+ __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */
+}
+#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A)
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ if (!(cflag & CRTSCTS))
+ __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */
+}
#else
- ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */
-#endif
- }
- sci_out(port, SCFCR, fcr_val);
+static inline void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+ /* Nothing to do */
}
#endif
@@ -419,18 +297,26 @@ static inline int scif_rxroom(struct uart_port *port)
#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
static inline int scif_txroom(struct uart_port *port)
{
- if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+ if ((port->mapbase == 0xffe00000) ||
+ (port->mapbase == 0xffe08000)) {
+ /* SCIF0/1*/
return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
- else /* SCIF2 */
+ } else {
+ /* SCIF2 */
return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+ }
}
static inline int scif_rxroom(struct uart_port *port)
{
- if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+ if ((port->mapbase == 0xffe00000) ||
+ (port->mapbase == 0xffe08000)) {
+ /* SCIF0/1*/
return sci_in(port, SCRFDR) & 0xff;
- else /* SCIF2 */
+ } else {
+ /* SCIF2 */
return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
+ }
}
#else
static inline int scif_txroom(struct uart_port *port)
@@ -446,12 +332,12 @@ static inline int scif_rxroom(struct uart_port *port)
static inline int sci_txroom(struct uart_port *port)
{
- return ((sci_in(port, SCxSR) & SCI_TDRE) != 0);
+ return (sci_in(port, SCxSR) & SCI_TDRE) != 0;
}
static inline int sci_rxroom(struct uart_port *port)
{
- return ((sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0);
+ return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
}
/* ********************************************************************** *
@@ -469,11 +355,10 @@ static void sci_transmit_chars(struct uart_port *port)
status = sci_in(port, SCxSR);
if (!(status & SCxSR_TDxE(port))) {
ctrl = sci_in(port, SCSCR);
- if (uart_circ_empty(xmit)) {
+ if (uart_circ_empty(xmit))
ctrl &= ~SCI_CTRL_FLAGS_TIE;
- } else {
+ else
ctrl |= SCI_CTRL_FLAGS_TIE;
- }
sci_out(port, SCSCR, ctrl);
return;
}
@@ -521,11 +406,11 @@ static void sci_transmit_chars(struct uart_port *port)
}
/* On SH3, SCIF may read end-of-break as a space->mark char */
-#define STEPFN(c) ({int __c=(c); (((__c-1)|(__c)) == -1); })
+#define STEPFN(c) ({int __c = (c); (((__c-1)|(__c)) == -1); })
static inline void sci_receive_chars(struct uart_port *port)
{
- struct sci_port *sci_port = (struct sci_port *)port;
+ struct sci_port *sci_port = to_sci_port(port);
struct tty_struct *tty = port->info->port.tty;
int i, count, copied = 0;
unsigned short status;
@@ -550,13 +435,13 @@ static inline void sci_receive_chars(struct uart_port *port)
if (port->type == PORT_SCI) {
char c = sci_in(port, SCxRDR);
- if (uart_handle_sysrq_char(port, c) || sci_port->break_flag)
+ if (uart_handle_sysrq_char(port, c) ||
+ sci_port->break_flag)
count = 0;
- else {
+ else
tty_insert_flip_char(tty, c, TTY_NORMAL);
- }
} else {
- for (i=0; i<count; i++) {
+ for (i = 0; i < count; i++) {
char c = sci_in(port, SCxRDR);
status = sci_in(port, SCxSR);
#if defined(CONFIG_CPU_SH3)
@@ -569,7 +454,7 @@ static inline void sci_receive_chars(struct uart_port *port)
}
/* Nonzero => end-of-break */
- pr_debug("scif: debounce<%02x>\n", c);
+ dev_dbg(port->dev, "debounce<%02x>\n", c);
sci_port->break_flag = 0;
if (STEPFN(c)) {
@@ -586,12 +471,13 @@ static inline void sci_receive_chars(struct uart_port *port)
/* Store data and status */
if (status&SCxSR_FER(port)) {
flag = TTY_FRAME;
- pr_debug("sci: frame error\n");
+ dev_notice(port->dev, "frame error\n");
} else if (status&SCxSR_PER(port)) {
flag = TTY_PARITY;
- pr_debug("sci: parity error\n");
+ dev_notice(port->dev, "parity error\n");
} else
flag = TTY_NORMAL;
+
tty_insert_flip_char(tty, c, flag);
}
}
@@ -651,13 +537,14 @@ static inline int sci_handle_errors(struct uart_port *port)
/* overrun error */
if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
copied++;
- pr_debug("sci: overrun error\n");
+
+ dev_notice(port->dev, "overrun error");
}
if (status & SCxSR_FER(port)) {
if (sci_rxd_in(port) == 0) {
/* Notify of BREAK */
- struct sci_port *sci_port = (struct sci_port *)port;
+ struct sci_port *sci_port = to_sci_port(port);
if (!sci_port->break_flag) {
sci_port->break_flag = 1;
@@ -666,15 +553,19 @@ static inline int sci_handle_errors(struct uart_port *port)
/* Do sysrq handling. */
if (uart_handle_break(port))
return 0;
- pr_debug("sci: BREAK detected\n");
+
+ dev_dbg(port->dev, "BREAK detected\n");
+
if (tty_insert_flip_char(tty, 0, TTY_BREAK))
- copied++;
- }
+ copied++;
+ }
+
} else {
/* frame error */
if (tty_insert_flip_char(tty, 0, TTY_FRAME))
copied++;
- pr_debug("sci: frame error\n");
+
+ dev_notice(port->dev, "frame error\n");
}
}
@@ -682,7 +573,8 @@ static inline int sci_handle_errors(struct uart_port *port)
/* parity error */
if (tty_insert_flip_char(tty, 0, TTY_PARITY))
copied++;
- pr_debug("sci: parity error\n");
+
+ dev_notice(port->dev, "parity error");
}
if (copied)
@@ -691,6 +583,27 @@ static inline int sci_handle_errors(struct uart_port *port)
return copied;
}
+static inline int sci_handle_fifo_overrun(struct uart_port *port)
+{
+ struct tty_struct *tty = port->info->port.tty;
+ int copied = 0;
+
+ if (port->type != PORT_SCIF)
+ return 0;
+
+ if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) {
+ sci_out(port, SCLSR, 0);
+
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_flip_buffer_push(tty);
+
+ dev_notice(port->dev, "overrun error\n");
+ copied++;
+ }
+
+ return copied;
+}
+
static inline int sci_handle_breaks(struct uart_port *port)
{
int copied = 0;
@@ -709,23 +622,15 @@ static inline int sci_handle_breaks(struct uart_port *port)
/* Notify of BREAK */
if (tty_insert_flip_char(tty, 0, TTY_BREAK))
copied++;
- pr_debug("sci: BREAK detected\n");
- }
-#if defined(SCIF_ORER)
- /* XXX: Handle SCIF overrun error */
- if (port->type != PORT_SCI && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
- sci_out(port, SCLSR, 0);
- if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
- copied++;
- pr_debug("sci: overrun error\n");
- }
+ dev_dbg(port->dev, "BREAK detected\n");
}
-#endif
if (copied)
tty_flip_buffer_push(tty);
+ copied += sci_handle_fifo_overrun(port);
+
return copied;
}
@@ -763,16 +668,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
}
} else {
-#if defined(SCIF_ORER)
- if((sci_in(port, SCLSR) & SCIF_ORER) != 0) {
- struct tty_struct *tty = port->info->port.tty;
-
- sci_out(port, SCLSR, 0);
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
- tty_flip_buffer_push(tty);
- pr_debug("scif: overrun error\n");
- }
-#endif
+ sci_handle_fifo_overrun(port);
sci_rx_interrupt(irq, ptr);
}
@@ -801,8 +697,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
struct uart_port *port = ptr;
irqreturn_t ret = IRQ_NONE;
- ssr_status = sci_in(port,SCxSR);
- scr_status = sci_in(port,SCSCR);
+ ssr_status = sci_in(port, SCxSR);
+ scr_status = sci_in(port, SCSCR);
/* Tx Interrupt */
if ((ssr_status & 0x0020) && (scr_status & SCI_CTRL_FLAGS_TIE))
@@ -820,7 +716,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
return ret;
}
-#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
/*
* Here we define a transistion notifier so that we can update all of our
* ports' baud rate when the peripheral clock changes.
@@ -828,41 +724,20 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
static int sci_notifier(struct notifier_block *self,
unsigned long phase, void *p)
{
- struct cpufreq_freqs *freqs = p;
int i;
if ((phase == CPUFREQ_POSTCHANGE) ||
- (phase == CPUFREQ_RESUMECHANGE)){
+ (phase == CPUFREQ_RESUMECHANGE))
for (i = 0; i < SCI_NPORTS; i++) {
- struct uart_port *port = &sci_ports[i].port;
- struct clk *clk;
-
- /*
- * Update the uartclk per-port if frequency has
- * changed, since it will no longer necessarily be
- * consistent with the old frequency.
- *
- * Really we want to be able to do something like
- * uart_change_speed() or something along those lines
- * here to implicitly reset the per-port baud rate..
- *
- * Clean this up later..
- */
- clk = clk_get(NULL, "module_clk");
- port->uartclk = clk_get_rate(clk);
- clk_put(clk);
+ struct sci_port *s = &sci_ports[i];
+ s->port.uartclk = clk_get_rate(s->clk);
}
- printk(KERN_INFO "%s: got a postchange notification "
- "for cpu %d (old %d, new %d)\n",
- __func__, freqs->cpu, freqs->old, freqs->new);
- }
-
return NOTIFY_OK;
}
static struct notifier_block sci_nb = { &sci_notifier, NULL, 0 };
-#endif /* CONFIG_CPU_FREQ && CONFIG_HAVE_CLK */
+#endif
static int sci_request_irq(struct sci_port *port)
{
@@ -875,23 +750,22 @@ static int sci_request_irq(struct sci_port *port)
"SCI Transmit Data Empty", "SCI Break" };
if (port->irqs[0] == port->irqs[1]) {
- if (!port->irqs[0]) {
- printk(KERN_ERR "sci: Cannot allocate irq.(IRQ=0)\n");
+ if (unlikely(!port->irqs[0]))
return -ENODEV;
- }
if (request_irq(port->irqs[0], sci_mpxed_interrupt,
IRQF_DISABLED, "sci", port)) {
- printk(KERN_ERR "sci: Cannot allocate irq.\n");
+ dev_err(port->port.dev, "Can't allocate IRQ\n");
return -ENODEV;
}
} else {
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
- if (!port->irqs[i])
+ if (unlikely(!port->irqs[i]))
continue;
+
if (request_irq(port->irqs[i], handlers[i],
IRQF_DISABLED, desc[i], port)) {
- printk(KERN_ERR "sci: Cannot allocate irq.\n");
+ dev_err(port->port.dev, "Can't allocate IRQ\n");
return -ENODEV;
}
}
@@ -904,12 +778,9 @@ static void sci_free_irq(struct sci_port *port)
{
int i;
- if (port->irqs[0] == port->irqs[1]) {
- if (!port->irqs[0])
- printk("sci: sci_free_irq error\n");
- else
- free_irq(port->irqs[0], port);
- } else {
+ if (port->irqs[0] == port->irqs[1])
+ free_irq(port->irqs[0], port);
+ else {
for (i = 0; i < ARRAY_SIZE(port->irqs); i++) {
if (!port->irqs[i])
continue;
@@ -1028,7 +899,6 @@ static void sci_shutdown(struct uart_port *port)
static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
- struct sci_port *s = &sci_ports[port->line];
unsigned int status, baud, smr_val;
int t = -1;
@@ -1060,32 +930,36 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
sci_out(port, SCSMR, smr_val);
if (t > 0) {
- if(t >= 256) {
+ if (t >= 256) {
sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
t >>= 2;
- } else {
+ } else
sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3);
- }
+
sci_out(port, SCBRR, t);
udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
}
- if (likely(s->init_pins))
- s->init_pins(port, termios->c_cflag);
+ sci_init_pins(port, termios->c_cflag);
+ sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0);
sci_out(port, SCSCR, SCSCR_INIT(port));
if ((termios->c_cflag & CREAD) != 0)
- sci_start_rx(port,0);
+ sci_start_rx(port, 0);
}
static const char *sci_type(struct uart_port *port)
{
switch (port->type) {
- case PORT_SCI: return "sci";
- case PORT_SCIF: return "scif";
- case PORT_IRDA: return "irda";
- case PORT_SCIFA: return "scifa";
+ case PORT_IRDA:
+ return "irda";
+ case PORT_SCI:
+ return "sci";
+ case PORT_SCIF:
+ return "scif";
+ case PORT_SCIFA:
+ return "scifa";
}
return NULL;
@@ -1108,19 +982,6 @@ static void sci_config_port(struct uart_port *port, int flags)
port->type = s->type;
- switch (port->type) {
- case PORT_SCI:
- s->init_pins = sci_init_pins_sci;
- break;
- case PORT_SCIF:
- case PORT_SCIFA:
- s->init_pins = sci_init_pins_scif;
- break;
- case PORT_IRDA:
- s->init_pins = sci_init_pins_irda;
- break;
- }
-
if (port->flags & UPF_IOREMAP && !port->membase) {
#if defined(CONFIG_SUPERH64)
port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF");
@@ -1129,7 +990,7 @@ static void sci_config_port(struct uart_port *port, int flags)
port->membase = ioremap_nocache(port->mapbase, 0x40);
#endif
- printk(KERN_ERR "sci: can't remap port#%d\n", port->line);
+ dev_err(port->dev, "can't remap port#%d\n", port->line);
}
}
@@ -1163,6 +1024,10 @@ static struct uart_ops sci_uart_ops = {
.request_port = sci_request_port,
.config_port = sci_config_port,
.verify_port = sci_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = sci_poll_get_char,
+ .poll_put_char = sci_poll_put_char,
+#endif
};
static void __init sci_init_ports(void)
@@ -1229,7 +1094,15 @@ int __init early_sci_setup(struct uart_port *port)
static void serial_console_write(struct console *co, const char *s,
unsigned count)
{
- put_string(serial_console_port, s, count);
+ struct uart_port *port = &serial_console_port->port;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (*s == 10)
+ sci_poll_put_char(port, '\r');
+
+ sci_poll_put_char(port, *s++);
+ }
}
static int __init serial_console_setup(struct console *co, char *options)
@@ -1307,89 +1180,8 @@ static int __init sci_console_init(void)
console_initcall(sci_console_init);
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-#ifdef CONFIG_SH_KGDB_CONSOLE
-/*
- * FIXME: Most of this can go away.. at the moment, we rely on
- * arch/sh/kernel/setup.c to do the command line parsing for kgdb, though
- * most of that can easily be done here instead.
- *
- * For the time being, just accept the values that were parsed earlier..
- */
-static void __init kgdb_console_get_options(struct uart_port *port, int *baud,
- int *parity, int *bits)
-{
- *baud = kgdb_baud;
- *parity = tolower(kgdb_parity);
- *bits = kgdb_bits - '0';
-}
-
-/*
- * The naming here is somewhat misleading, since kgdb_console_setup() takes
- * care of the early-on initialization for kgdb, regardless of whether we
- * actually use kgdb as a console or not.
- *
- * On the plus side, this lets us kill off the old kgdb_sci_setup() nonsense.
- */
-int __init kgdb_console_setup(struct console *co, char *options)
-{
- struct uart_port *port = &sci_ports[kgdb_portnum].port;
- int baud = 38400;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- if (co->index != kgdb_portnum)
- co->index = kgdb_portnum;
-
- kgdb_sci_port = &sci_ports[co->index];
- port = &kgdb_sci_port->port;
-
- /*
- * Also need to check port->type, we don't actually have any
- * UPIO_PORT ports, but uart_report_port() handily misreports
- * it anyways if we don't have a port available by the time this is
- * called.
- */
- if (!port->type)
- return -ENODEV;
- if (!port->membase || !port->mapbase)
- return -ENODEV;
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- else
- kgdb_console_get_options(port, &baud, &parity, &bits);
-
- kgdb_getchar = kgdb_sci_getchar;
- kgdb_putchar = kgdb_sci_putchar;
-
- return uart_set_options(port, co, baud, parity, bits, flow);
-}
-
-static struct console kgdb_console = {
- .name = "ttySC",
- .device = uart_console_device,
- .write = kgdb_console_write,
- .setup = kgdb_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &sci_uart_driver,
-};
-
-/* Register the KGDB console so we get messages (d'oh!) */
-static int __init kgdb_console_init(void)
-{
- sci_init_ports();
- register_console(&kgdb_console);
- return 0;
-}
-console_initcall(kgdb_console_init);
-#endif /* CONFIG_SH_KGDB_CONSOLE */
-
-#if defined(CONFIG_SH_KGDB_CONSOLE)
-#define SCI_CONSOLE &kgdb_console
-#elif defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
-#define SCI_CONSOLE &serial_console
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+#define SCI_CONSOLE (&serial_console)
#else
#define SCI_CONSOLE 0
#endif
@@ -1463,15 +1255,8 @@ static int __devinit sci_probe(struct platform_device *dev)
uart_add_one_port(&sci_uart_driver, &sciport->port);
}
-#if defined(CONFIG_SH_KGDB) && !defined(CONFIG_SH_KGDB_CONSOLE)
- kgdb_sci_port = &sci_ports[kgdb_portnum];
- kgdb_getchar = kgdb_sci_getchar;
- kgdb_putchar = kgdb_sci_putchar;
-#endif
-
-#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_HAVE_CLK)
+#ifdef CONFIG_HAVE_CLK
cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
- dev_info(&dev->dev, "CPU frequency notifier registered\n");
#endif
#ifdef CONFIG_SH_STANDARD_BIOS
@@ -1491,6 +1276,10 @@ static int __devexit sci_remove(struct platform_device *dev)
{
int i;
+#ifdef CONFIG_HAVE_CLK
+ cpufreq_unregister_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
+#endif
+
for (i = 0; i < SCI_NPORTS; i++)
uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 9f33b064172e..38c600c0dbbf 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -133,13 +133,20 @@
# define SCSPTR5 0xffef0024 /* 16 bit SCIF */
# define SCIF_OPER 0x0001 /* Overrun error bit */
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
-#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \
+#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7203) || \
defined(CONFIG_CPU_SUBTYPE_SH7206) || \
defined(CONFIG_CPU_SUBTYPE_SH7263)
# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */
# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */
# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */
# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */
+# if defined(CONFIG_CPU_SUBTYPE_SH7201)
+# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */
+# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */
+# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */
+# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */
+# endif
# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
# define SCSPTR0 0xf8400020 /* 16 bit SCIF */
@@ -225,6 +232,10 @@
# define SCIF_TXROOM_MAX 16
#endif
+#ifndef SCIF_ORER
+#define SCIF_ORER 0x0000
+#endif
+
#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS)
#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF)
@@ -232,12 +243,7 @@
#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER)
#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER)
#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK)
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7705)
-# define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER)
-#else
-# define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : 0x0000)
-#endif
+#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER)
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
@@ -501,18 +507,6 @@ static inline int sci_rxd_in(struct uart_port *port)
{
return sci_in(port,SCxSR)&0x0010 ? 1 : 0;
}
-static inline void set_sh771x_scif_pfc(struct uart_port *port)
-{
- if (port->mapbase == 0xA4400000){
- ctrl_outw(ctrl_inw(PACR)&0xffc0,PACR);
- ctrl_outw(ctrl_inw(PBCR)&0x0fff,PBCR);
- return;
- }
- if (port->mapbase == 0xA4410000){
- ctrl_outw(ctrl_inw(PBCR)&0xf003,PBCR);
- return;
- }
-}
#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721)
static inline int sci_rxd_in(struct uart_port *port)
@@ -664,7 +658,8 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR5) & 0x0001 ? 1 : 0; /* SCIF */
return 1;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7203) || \
+#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7203) || \
defined(CONFIG_CPU_SUBTYPE_SH7206) || \
defined(CONFIG_CPU_SUBTYPE_SH7263)
static inline int sci_rxd_in(struct uart_port *port)
@@ -677,6 +672,16 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xfffe9800)
return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+#if defined(CONFIG_CPU_SUBTYPE_SH7201)
+ if (port->mapbase == 0xfffeA000)
+ return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xfffeA800)
+ return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xfffeB000)
+ return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xfffeB800)
+ return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+#endif
return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index cf12f2d84be2..6104f461a3cd 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -32,8 +32,8 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#include <mach/regs-ssp.h>
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index c252cbac00f1..256d18395a23 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -28,7 +28,7 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#include <mach/spi.h>
struct s3c24xx_spi {
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 0ffabf5c0b60..65a1ed951a1d 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -226,7 +226,7 @@ int ssb_devices_freeze(struct ssb_bus *bus)
err = drv->suspend(dev, state);
if (err) {
ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",
- dev->dev->bus_id);
+ dev_name(dev->dev));
goto err_unwind;
}
}
@@ -269,7 +269,7 @@ int ssb_devices_thaw(struct ssb_bus *bus)
err = drv->resume(dev);
if (err) {
ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
- dev->dev->bus_id);
+ dev_name(dev->dev));
}
}
@@ -454,8 +454,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
dev->release = ssb_release_dev;
dev->bus = &ssb_bustype;
- snprintf(dev->bus_id, sizeof(dev->bus_id),
- "ssb%u:%d", bus->busnumber, dev_idx);
+ dev_set_name(dev, "ssb%u:%d", bus->busnumber, dev_idx);
switch (bus->bustype) {
case SSB_BUSTYPE_PCI:
@@ -480,7 +479,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
if (err) {
ssb_printk(KERN_ERR PFX
"Could not register %s\n",
- dev->bus_id);
+ dev_name(dev));
/* Set dev to NULL to not unregister
* dev on error unwinding. */
sdev->dev = NULL;
@@ -796,7 +795,7 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus,
err = ssb_bus_register(bus, ssb_pci_get_invariants, 0);
if (!err) {
ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
- "PCI device %s\n", host_pci->dev.bus_id);
+ "PCI device %s\n", dev_name(&host_pci->dev));
}
return err;
diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c
index e82db4aaa050..26737a010c6d 100644
--- a/drivers/ssb/pcihost_wrapper.c
+++ b/drivers/ssb/pcihost_wrapper.c
@@ -65,7 +65,7 @@ static int ssb_pcihost_probe(struct pci_dev *dev,
err = pci_enable_device(dev);
if (err)
goto err_kfree_ssb;
- name = dev->dev.bus_id;
+ name = dev_name(&dev->dev);
if (dev->driver && dev->driver->name)
name = dev->driver->name;
err = pci_request_regions(dev, name);
diff --git a/drivers/staging/slicoss/slicoss.c b/drivers/staging/slicoss/slicoss.c
index 8fa9490b3e2c..00390362f10f 100644
--- a/drivers/staging/slicoss/slicoss.c
+++ b/drivers/staging/slicoss/slicoss.c
@@ -765,8 +765,7 @@ static int slic_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
#ifdef SLIC_USER_REQUEST_DUMP_ENABLED
case SIOCSLICDUMPCARD:
{
- struct adapter *adapter = (struct adapter *)
- dev->priv;
+ struct adapter *adapter = netdev_priv(dev);
struct sliccard *card;
ASSERT(adapter);
@@ -1685,7 +1684,7 @@ static void slic_timer_ping(ulong dev)
struct sliccard *card;
ASSERT(dev);
- adapter = (struct adapter *)((struct net_device *) dev)->priv;
+ adapter = netdev_priv((struct net_device *)dev);
ASSERT(adapter);
card = adapter->card;
ASSERT(card);
@@ -3136,7 +3135,7 @@ static void slic_timer_get_stats(ulong dev)
struct slic_shmem *pshmem;
ASSERT(dev);
- adapter = (struct adapter *)((struct net_device *)dev)->priv;
+ adapter = netdev_priv((struct net_device *)dev);
ASSERT(adapter);
card = adapter->card;
ASSERT(card);
diff --git a/drivers/staging/winbond/linux/wbusb.c b/drivers/staging/winbond/linux/wbusb.c
index f4a7875f2389..39ca9b9878f8 100644
--- a/drivers/staging/winbond/linux/wbusb.c
+++ b/drivers/staging/winbond/linux/wbusb.c
@@ -336,7 +336,11 @@ WbUsb_destroy(phw_data_t pHwData)
int wb35_open(struct net_device *netdev)
{
- PADAPTER Adapter = (PADAPTER)netdev->priv;
+ /* netdev_priv() or netdev->ml_priv should reference to the address of
+ * private data(PADAPTER). It depends on whether private data memory is
+ * allocated when alloc_netdev().
+ */
+ PADAPTER Adapter = (PADAPTER)netdev_priv(netdev);
phw_data_t pHwData = &Adapter->sHwData;
netif_start_queue(netdev);
diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c
index 11f84a829e14..2b705eaa50e8 100644
--- a/drivers/staging/wlan-ng/p80211netdev.c
+++ b/drivers/staging/wlan-ng/p80211netdev.c
@@ -244,7 +244,7 @@ static int p80211knetdev_init( netdevice_t *netdev)
static struct net_device_stats*
p80211knetdev_get_stats(netdevice_t *netdev)
{
- wlandevice_t *wlandev = (wlandevice_t*)netdev->priv;
+ wlandevice_t *wlandev = netdev->ml_priv;
DBFENTER;
/* TODO: review the MIB stats for items that correspond to
@@ -272,7 +272,7 @@ p80211knetdev_get_stats(netdevice_t *netdev)
static int p80211knetdev_open( netdevice_t *netdev )
{
int result = 0; /* success */
- wlandevice_t *wlandev = (wlandevice_t*)(netdev->priv);
+ wlandevice_t *wlandev = netdev->ml_priv;
DBFENTER;
@@ -315,7 +315,7 @@ static int p80211knetdev_open( netdevice_t *netdev )
static int p80211knetdev_stop( netdevice_t *netdev )
{
int result = 0;
- wlandevice_t *wlandev = (wlandevice_t*)(netdev->priv);
+ wlandevice_t *wlandev = netdev->ml_priv;
DBFENTER;
@@ -460,7 +460,7 @@ static int p80211knetdev_hard_start_xmit( struct sk_buff *skb, netdevice_t *netd
{
int result = 0;
int txresult = -1;
- wlandevice_t *wlandev = (wlandevice_t*)netdev->priv;
+ wlandevice_t *wlandev = netdev->ml_priv;
p80211_hdr_t p80211_hdr;
p80211_metawep_t p80211_wep;
@@ -603,7 +603,7 @@ static int p80211knetdev_hard_start_xmit( struct sk_buff *skb, netdevice_t *netd
----------------------------------------------------------------*/
static void p80211knetdev_set_multicast_list(netdevice_t *dev)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
DBFENTER;
@@ -696,7 +696,7 @@ static int p80211knetdev_do_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd)
{
int result = 0;
p80211ioctl_req_t *req = (p80211ioctl_req_t*)ifr;
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
UINT8 *msgbuf;
DBFENTER;
@@ -812,7 +812,7 @@ static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr)
dot11req.msgcode = DIDmsg_dot11req_mibset;
dot11req.msglen = sizeof(p80211msg_dot11req_mibset_t);
memcpy(dot11req.devname,
- ((wlandevice_t*)(dev->priv))->name,
+ ((wlandevice_t *)dev->ml_priv)->name,
WLAN_DEVNAMELEN_MAX - 1);
/* Set up the mibattribute argument */
@@ -833,7 +833,7 @@ static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr)
resultcode->data = 0;
/* now fire the request */
- result = p80211req_dorequest(dev->priv, (UINT8*)&dot11req);
+ result = p80211req_dorequest(dev->ml_priv, (UINT8 *)&dot11req);
/* If the request wasn't successful, report an error and don't
* change the netdev address
@@ -917,7 +917,7 @@ int wlan_setup(wlandevice_t *wlandev)
memset( dev, 0, sizeof(netdevice_t));
ether_setup(dev);
wlandev->netdev = dev;
- dev->priv = wlandev;
+ dev->ml_priv = wlandev;
dev->hard_start_xmit = p80211knetdev_hard_start_xmit;
dev->get_stats = p80211knetdev_get_stats;
#ifdef HAVE_PRIVATE_IOCTL
@@ -1487,7 +1487,7 @@ void p80211_resume(wlandevice_t *wlandev)
static void p80211knetdev_tx_timeout( netdevice_t *netdev)
{
- wlandevice_t *wlandev = (wlandevice_t*)netdev->priv;
+ wlandevice_t *wlandev = netdev->ml_priv;
DBFENTER;
if (wlandev->tx_timeout) {
diff --git a/drivers/staging/wlan-ng/p80211wext.c b/drivers/staging/wlan-ng/p80211wext.c
index 906ba4392376..b2c9ea25fa42 100644
--- a/drivers/staging/wlan-ng/p80211wext.c
+++ b/drivers/staging/wlan-ng/p80211wext.c
@@ -218,7 +218,7 @@ exit:
struct iw_statistics* p80211wext_get_wireless_stats (netdevice_t *dev)
{
p80211msg_lnxreq_commsquality_t quality;
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct iw_statistics* wstats = &wlandev->wstats;
int retval;
@@ -301,7 +301,7 @@ static int p80211wext_giwfreq(netdevice_t *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -339,7 +339,7 @@ static int p80211wext_siwfreq(netdevice_t *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -380,7 +380,7 @@ static int p80211wext_giwmode(netdevice_t *dev,
struct iw_request_info *info,
__u32 *mode, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
DBFENTER;
@@ -407,7 +407,7 @@ static int p80211wext_siwmode(netdevice_t *dev,
struct iw_request_info *info,
__u32 *mode, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -550,7 +550,7 @@ static int p80211wext_giwap(netdevice_t *dev,
struct sockaddr *ap_addr, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
DBFENTER;
@@ -566,7 +566,7 @@ static int p80211wext_giwencode(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *erq, char *key)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
int err = 0;
int i;
@@ -607,7 +607,7 @@ static int p80211wext_siwencode(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *erq, char *key)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211msg_dot11req_mibset_t msg;
p80211item_pstr32_t pstr;
@@ -736,7 +736,7 @@ static int p80211wext_giwessid(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *data, char *essid)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
DBFENTER;
@@ -762,7 +762,7 @@ static int p80211wext_siwessid(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *data, char *essid)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211msg_lnxreq_autojoin_t msg;
int result;
@@ -816,7 +816,7 @@ static int p80211wext_siwcommit(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *data, char *essid)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
int err = 0;
DBFENTER;
@@ -839,7 +839,7 @@ static int p80211wext_giwrate(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rrq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -893,7 +893,7 @@ static int p80211wext_giwrts(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -927,7 +927,7 @@ static int p80211wext_siwrts(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rts, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -964,7 +964,7 @@ static int p80211wext_giwfrag(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -997,7 +997,7 @@ static int p80211wext_siwfrag(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *frag, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -1047,7 +1047,7 @@ static int p80211wext_giwretry(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rrq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -1126,7 +1126,7 @@ static int p80211wext_siwretry(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rrq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -1198,7 +1198,7 @@ static int p80211wext_siwtxpow(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rrq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -1243,7 +1243,7 @@ static int p80211wext_giwtxpow(netdevice_t *dev,
struct iw_request_info *info,
struct iw_param *rrq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211item_uint32_t mibitem;
p80211msg_dot11req_mibset_t msg;
int result;
@@ -1281,7 +1281,7 @@ static int p80211wext_siwspy(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *srq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct sockaddr address[IW_MAX_SPY];
int number = srq->length;
int i;
@@ -1317,7 +1317,7 @@ static int p80211wext_giwspy(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *srq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct sockaddr address[IW_MAX_SPY];
struct iw_quality spy_stat[IW_MAX_SPY];
@@ -1378,7 +1378,7 @@ static int p80211wext_siwscan(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *srq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211msg_dot11req_scan_t msg;
int result;
int err = 0;
@@ -1501,7 +1501,7 @@ static int p80211wext_giwscan(netdevice_t *dev,
struct iw_request_info *info,
struct iw_point *srq, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
p80211msg_dot11req_scan_results_t msg;
int result = 0;
int err = 0;
@@ -1551,7 +1551,7 @@ static int p80211wext_set_encodeext(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
p80211msg_dot11req_mibset_t msg;
p80211item_pstr32_t *pstr;
@@ -1627,7 +1627,7 @@ static int p80211wext_get_encodeext(struct net_device *dev,
union iwreq_data *wrqu, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
struct iw_point *encoding = &wrqu->encoding;
@@ -1682,7 +1682,7 @@ static int p80211_wext_set_iwauth (struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct iw_param *param = &wrqu->param;
int result =0;
@@ -1734,7 +1734,7 @@ static int p80211_wext_get_iwauth (struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
struct iw_param *param = &wrqu->param;
int result =0;
@@ -1868,7 +1868,7 @@ struct iw_handler_def p80211wext_handler_def = {
/* wireless extensions' ioctls */
int p80211wext_support_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd)
{
- wlandevice_t *wlandev = (wlandevice_t*)dev->priv;
+ wlandevice_t *wlandev = dev->ml_priv;
#if WIRELESS_EXT < 13
struct iwreq *iwr = (struct iwreq*)ifr;
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 9aea43a8c4ad..5ed4ae07bac1 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -286,9 +286,7 @@ static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
struct atm_dev *atm_dev = usbatm_instance->atm_dev;
- return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
- atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],
- atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
+ return snprintf(buf, PAGE_SIZE, "%pM\n", atm_dev->esi);
}
static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index 06dd114910d4..fbea8563df1e 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -770,10 +770,7 @@ static int usbatm_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *pag
return sprintf(page, "%s\n", instance->description);
if (!left--)
- return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
- atm_dev->esi[0], atm_dev->esi[1],
- atm_dev->esi[2], atm_dev->esi[3],
- atm_dev->esi[4], atm_dev->esi[5]);
+ return sprintf(page, "MAC: %pM\n", atm_dev->esi);
if (!left--)
return sprintf(page,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 2bccefebff1b..aa79280df15d 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -574,6 +574,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_device *dev = NULL;
struct dev_state *ps;
+ const struct cred *cred = current_cred();
int ret;
lock_kernel();
@@ -617,8 +618,8 @@ static int usbdev_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ps->wait);
ps->discsignr = 0;
ps->disc_pid = get_pid(task_pid(current));
- ps->disc_uid = current->uid;
- ps->disc_euid = current->euid;
+ ps->disc_uid = cred->uid;
+ ps->disc_euid = cred->euid;
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
@@ -967,6 +968,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct usb_host_endpoint *ep;
struct async *as;
struct usb_ctrlrequest *dr = NULL;
+ const struct cred *cred = current_cred();
unsigned int u, totlen, isofrmlen;
int ret, ifnum = -1;
int is_in;
@@ -1174,8 +1176,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->signr = uurb->signr;
as->ifnum = ifnum;
as->pid = get_pid(task_pid(current));
- as->uid = current->uid;
- as->euid = current->euid;
+ as->uid = cred->uid;
+ as->euid = cred->euid;
security_task_getsecid(current, &as->secid);
if (!is_in) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 94632264dccf..185be760833e 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -277,8 +277,8 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
new file mode 100644
index 000000000000..d8fc9b32fe36
--- /dev/null
+++ b/drivers/usb/gadget/f_phonet.c
@@ -0,0 +1,621 @@
+/*
+ * f_phonet.c -- USB CDC Phonet function
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ *
+ * Author: Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_phonet.h>
+#include <linux/if_arp.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/composite.h>
+
+#include "u_phonet.h"
+
+#define PN_MEDIA_USB 0x1B
+
+/*-------------------------------------------------------------------------*/
+
+struct phonet_port {
+ struct f_phonet *usb;
+ spinlock_t lock;
+};
+
+struct f_phonet {
+ struct usb_function function;
+ struct net_device *dev;
+ struct usb_ep *in_ep, *out_ep;
+
+ struct usb_request *in_req;
+ struct usb_request *out_reqv[0];
+};
+
+static int phonet_rxq_size = 2;
+
+static inline struct f_phonet *func_to_pn(struct usb_function *f)
+{
+ return container_of(f, struct f_phonet, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define USB_CDC_SUBCLASS_PHONET 0xfe
+#define USB_CDC_PHONET_TYPE 0xab
+
+static struct usb_interface_descriptor
+pn_control_intf_desc = {
+ .bLength = sizeof pn_control_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC, */
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET,
+};
+
+static const struct usb_cdc_header_desc
+pn_header_desc = {
+ .bLength = sizeof pn_header_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+};
+
+static const struct usb_cdc_header_desc
+pn_phonet_desc = {
+ .bLength = sizeof pn_phonet_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_PHONET_TYPE,
+ .bcdCDC = __constant_cpu_to_le16(0x1505), /* ??? */
+};
+
+static struct usb_cdc_union_desc
+pn_union_desc = {
+ .bLength = sizeof pn_union_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+
+ /* .bMasterInterface0 = DYNAMIC, */
+ /* .bSlaveInterface0 = DYNAMIC, */
+};
+
+static struct usb_interface_descriptor
+pn_data_nop_intf_desc = {
+ .bLength = sizeof pn_data_nop_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC, */
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+};
+
+static struct usb_interface_descriptor
+pn_data_intf_desc = {
+ .bLength = sizeof pn_data_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC, */
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+pn_fs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor
+pn_hs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *fs_pn_function[] = {
+ (struct usb_descriptor_header *) &pn_control_intf_desc,
+ (struct usb_descriptor_header *) &pn_header_desc,
+ (struct usb_descriptor_header *) &pn_phonet_desc,
+ (struct usb_descriptor_header *) &pn_union_desc,
+ (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+ (struct usb_descriptor_header *) &pn_data_intf_desc,
+ (struct usb_descriptor_header *) &pn_fs_sink_desc,
+ (struct usb_descriptor_header *) &pn_fs_source_desc,
+ NULL,
+};
+
+static struct usb_descriptor_header *hs_pn_function[] = {
+ (struct usb_descriptor_header *) &pn_control_intf_desc,
+ (struct usb_descriptor_header *) &pn_header_desc,
+ (struct usb_descriptor_header *) &pn_phonet_desc,
+ (struct usb_descriptor_header *) &pn_union_desc,
+ (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
+ (struct usb_descriptor_header *) &pn_data_intf_desc,
+ (struct usb_descriptor_header *) &pn_hs_sink_desc,
+ (struct usb_descriptor_header *) &pn_hs_source_desc,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int pn_net_open(struct net_device *dev)
+{
+ if (netif_carrier_ok(dev))
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int pn_net_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_phonet *fp = ep->driver_data;
+ struct net_device *dev = fp->dev;
+ struct sk_buff *skb = req->context;
+
+ switch (req->status) {
+ case 0:
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ break;
+
+ case -ESHUTDOWN: /* disconnected */
+ case -ECONNRESET: /* disabled */
+ dev->stats.tx_aborted_errors++;
+ default:
+ dev->stats.tx_errors++;
+ }
+
+ dev_kfree_skb_any(skb);
+ if (netif_carrier_ok(dev))
+ netif_wake_queue(dev);
+}
+
+static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct phonet_port *port = netdev_priv(dev);
+ struct f_phonet *fp;
+ struct usb_request *req;
+ unsigned long flags;
+
+ if (skb->protocol != htons(ETH_P_PHONET))
+ goto out;
+
+ spin_lock_irqsave(&port->lock, flags);
+ fp = port->usb;
+ if (unlikely(!fp)) /* race with carrier loss */
+ goto out_unlock;
+
+ req = fp->in_req;
+ req->buf = skb->data;
+ req->length = skb->len;
+ req->complete = pn_tx_complete;
+ req->zero = 1;
+ req->context = skb;
+
+ if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
+ goto out_unlock;
+
+ netif_stop_queue(dev);
+ skb = NULL;
+
+out_unlock:
+ spin_unlock_irqrestore(&port->lock, flags);
+out:
+ if (unlikely(skb)) {
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ }
+ return 0;
+}
+
+static int pn_net_mtu(struct net_device *dev, int new_mtu)
+{
+ struct phonet_port *port = netdev_priv(dev);
+ unsigned long flags;
+ int err = -EBUSY;
+
+ if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
+ return -EINVAL;
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (!netif_carrier_ok(dev)) {
+ dev->mtu = new_mtu;
+ err = 0;
+ }
+ spin_unlock_irqrestore(&port->lock, flags);
+ return err;
+}
+
+static void pn_net_setup(struct net_device *dev)
+{
+ dev->features = 0;
+ dev->type = ARPHRD_PHONET;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = PHONET_DEV_MTU;
+ dev->hard_header_len = 1;
+ dev->dev_addr[0] = PN_MEDIA_USB;
+ dev->addr_len = 1;
+ dev->tx_queue_len = 1;
+
+ dev->destructor = free_netdev;
+ dev->header_ops = &phonet_header_ops;
+ dev->open = pn_net_open;
+ dev->stop = pn_net_close;
+ dev->hard_start_xmit = pn_net_xmit; /* mandatory */
+ dev->change_mtu = pn_net_mtu;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Queue buffer for data from the host
+ */
+static int
+pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
+{
+ struct sk_buff *skb;
+ const size_t size = fp->dev->mtu;
+ int err;
+
+ skb = alloc_skb(size, gfp_flags);
+ if (!skb)
+ return -ENOMEM;
+
+ req->buf = skb->data;
+ req->length = size;
+ req->context = skb;
+
+ err = usb_ep_queue(fp->out_ep, req, gfp_flags);
+ if (unlikely(err))
+ dev_kfree_skb_any(skb);
+ return err;
+}
+
+static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_phonet *fp = ep->driver_data;
+ struct net_device *dev = fp->dev;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+
+ switch (status) {
+ case 0:
+ if (unlikely(!netif_running(dev)))
+ break;
+ if (unlikely(req->actual < 1))
+ break;
+ skb_put(skb, req->actual);
+ skb->protocol = htons(ETH_P_PHONET);
+ skb_reset_mac_header(skb);
+ __skb_pull(skb, 1);
+ skb->dev = dev;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ netif_rx(skb);
+ skb = NULL;
+ break;
+
+ /* Do not resubmit in these cases: */
+ case -ESHUTDOWN: /* disconnect */
+ case -ECONNABORTED: /* hw reset */
+ case -ECONNRESET: /* dequeued (unlink or netif down) */
+ req = NULL;
+ break;
+
+ /* Do resubmit in these cases: */
+ case -EOVERFLOW: /* request buffer overflow */
+ dev->stats.rx_over_errors++;
+ default:
+ dev->stats.rx_errors++;
+ break;
+ }
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+ if (req)
+ pn_rx_submit(fp, req, GFP_ATOMIC);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void __pn_reset(struct usb_function *f)
+{
+ struct f_phonet *fp = func_to_pn(f);
+ struct net_device *dev = fp->dev;
+ struct phonet_port *port = netdev_priv(dev);
+
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ port->usb = NULL;
+
+ usb_ep_disable(fp->out_ep);
+ usb_ep_disable(fp->in_ep);
+}
+
+static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_phonet *fp = func_to_pn(f);
+ struct usb_gadget *gadget = fp->function.config->cdev->gadget;
+
+ if (intf == pn_control_intf_desc.bInterfaceNumber)
+ /* control interface, no altsetting */
+ return (alt > 0) ? -EINVAL : 0;
+
+ if (intf == pn_data_intf_desc.bInterfaceNumber) {
+ struct net_device *dev = fp->dev;
+ struct phonet_port *port = netdev_priv(dev);
+
+ /* data intf (0: inactive, 1: active) */
+ if (alt > 1)
+ return -EINVAL;
+
+ spin_lock(&port->lock);
+ __pn_reset(f);
+ if (alt == 1) {
+ struct usb_endpoint_descriptor *out, *in;
+ int i;
+
+ out = ep_choose(gadget,
+ &pn_hs_sink_desc,
+ &pn_fs_sink_desc);
+ in = ep_choose(gadget,
+ &pn_hs_source_desc,
+ &pn_fs_source_desc);
+ usb_ep_enable(fp->out_ep, out);
+ usb_ep_enable(fp->in_ep, in);
+
+ port->usb = fp;
+ fp->out_ep->driver_data = fp;
+ fp->in_ep->driver_data = fp;
+
+ netif_carrier_on(dev);
+ if (netif_running(dev))
+ netif_wake_queue(dev);
+ for (i = 0; i < phonet_rxq_size; i++)
+ pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC);
+ }
+ spin_unlock(&port->lock);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int pn_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_phonet *fp = func_to_pn(f);
+
+ if (intf == pn_control_intf_desc.bInterfaceNumber)
+ return 0;
+
+ if (intf == pn_data_intf_desc.bInterfaceNumber) {
+ struct phonet_port *port = netdev_priv(fp->dev);
+ u8 alt;
+
+ spin_lock(&port->lock);
+ alt = port->usb != NULL;
+ spin_unlock(&port->lock);
+ return alt;
+ }
+
+ return -EINVAL;
+}
+
+static void pn_disconnect(struct usb_function *f)
+{
+ struct f_phonet *fp = func_to_pn(f);
+ struct phonet_port *port = netdev_priv(fp->dev);
+ unsigned long flags;
+
+ /* remain disabled until set_alt */
+ spin_lock_irqsave(&port->lock, flags);
+ __pn_reset(f);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static __init
+int pn_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usb_gadget *gadget = cdev->gadget;
+ struct f_phonet *fp = func_to_pn(f);
+ struct usb_ep *ep;
+ int status, i;
+
+ /* Reserve interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto err;
+ pn_control_intf_desc.bInterfaceNumber = status;
+ pn_union_desc.bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto err;
+ pn_data_nop_intf_desc.bInterfaceNumber = status;
+ pn_data_intf_desc.bInterfaceNumber = status;
+ pn_union_desc.bSlaveInterface0 = status;
+
+ /* Reserve endpoints */
+ status = -ENODEV;
+ ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
+ if (!ep)
+ goto err;
+ fp->out_ep = ep;
+ ep->driver_data = fp; /* Claim */
+
+ ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
+ if (!ep)
+ goto err;
+ fp->in_ep = ep;
+ ep->driver_data = fp; /* Claim */
+
+ pn_hs_sink_desc.bEndpointAddress =
+ pn_fs_sink_desc.bEndpointAddress;
+ pn_hs_source_desc.bEndpointAddress =
+ pn_fs_source_desc.bEndpointAddress;
+
+ /* Do not try to bind Phonet twice... */
+ fp->function.descriptors = fs_pn_function;
+ fp->function.hs_descriptors = hs_pn_function;
+
+ /* Incoming USB requests */
+ status = -ENOMEM;
+ for (i = 0; i < phonet_rxq_size; i++) {
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
+ if (!req)
+ goto err;
+
+ req->complete = pn_rx_complete;
+ fp->out_reqv[i] = req;
+ }
+
+ /* Outgoing USB requests */
+ fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
+ if (!fp->in_req)
+ goto err;
+
+ INFO(cdev, "USB CDC Phonet function\n");
+ INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
+ fp->out_ep->name, fp->in_ep->name);
+ return 0;
+
+err:
+ if (fp->out_ep)
+ fp->out_ep->driver_data = NULL;
+ if (fp->in_ep)
+ fp->in_ep->driver_data = NULL;
+ ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
+ return status;
+}
+
+static void
+pn_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_phonet *fp = func_to_pn(f);
+ int i;
+
+ /* We are already disconnected */
+ if (fp->in_req)
+ usb_ep_free_request(fp->in_ep, fp->in_req);
+ for (i = 0; i < phonet_rxq_size; i++)
+ if (fp->out_reqv[i])
+ usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
+
+ kfree(fp);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct net_device *dev;
+
+int __init phonet_bind_config(struct usb_configuration *c)
+{
+ struct f_phonet *fp;
+ int err;
+
+ fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+ if (!fp)
+ return -ENOMEM;
+
+ fp->dev = dev;
+ fp->function.name = "phonet";
+ fp->function.bind = pn_bind;
+ fp->function.unbind = pn_unbind;
+ fp->function.set_alt = pn_set_alt;
+ fp->function.get_alt = pn_get_alt;
+ fp->function.disable = pn_disconnect;
+
+ err = usb_add_function(c, &fp->function);
+ if (err)
+ kfree(fp);
+ return err;
+}
+
+int __init gphonet_setup(struct usb_gadget *gadget)
+{
+ struct phonet_port *port;
+ int err;
+
+ /* Create net device */
+ BUG_ON(dev);
+ dev = alloc_netdev(sizeof(*port)
+ + (phonet_rxq_size * sizeof(struct usb_request *)),
+ "upnlink%d", pn_net_setup);
+ if (!dev)
+ return -ENOMEM;
+
+ port = netdev_priv(dev);
+ spin_lock_init(&port->lock);
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ SET_NETDEV_DEV(dev, &gadget->dev);
+
+ err = register_netdev(dev);
+ if (err)
+ free_netdev(dev);
+ return err;
+}
+
+void gphonet_cleanup(void)
+{
+ unregister_netdev(dev);
+}
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index 77b44fb48f0a..3a8879ec2061 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -623,7 +623,6 @@ static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
static void init_controller(struct m66592 *m66592)
{
- usbf_start_clock();
m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */
m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
@@ -671,9 +670,7 @@ static void init_controller(struct m66592 *m66592)
static void disable_controller(struct m66592 *m66592)
{
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
- usbf_stop_clock();
-#else
+#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
udelay(1);
m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
@@ -686,9 +683,7 @@ static void disable_controller(struct m66592 *m66592)
static void m66592_start_xclock(struct m66592 *m66592)
{
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
- usbf_start_clock();
-#else
+#if !defined(CONFIG_SUPERH_BUILT_IN_M66592)
u16 tmp;
tmp = m66592_read(m66592, M66592_SYSCFG);
@@ -1539,7 +1534,10 @@ static int __exit m66592_remove(struct platform_device *pdev)
iounmap(m66592->reg);
free_irq(platform_get_irq(pdev, 0), m66592);
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
- usbf_stop_clock();
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+ clk_disable(m66592->clk);
+ clk_put(m66592->clk);
+#endif
kfree(m66592);
return 0;
}
@@ -1556,6 +1554,9 @@ static int __init m66592_probe(struct platform_device *pdev)
int irq;
void __iomem *reg = NULL;
struct m66592 *m66592 = NULL;
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+ char clk_name[8];
+#endif
int ret = 0;
int i;
@@ -1614,6 +1615,16 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+ snprintf(clk_name, sizeof(clk_name), "usbf%d", pdev->id);
+ m66592->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(m66592->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(m66592->clk);
+ goto clean_up2;
+ }
+ clk_enable(m66592->clk);
+#endif
INIT_LIST_HEAD(&m66592->gadget.ep_list);
m66592->gadget.ep0 = &m66592->ep[0].ep;
INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
@@ -1645,7 +1656,7 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
if (m66592->ep0_req == NULL)
- goto clean_up2;
+ goto clean_up3;
m66592->ep0_req->complete = nop_completion;
init_controller(m66592);
@@ -1653,7 +1664,12 @@ static int __init m66592_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
return 0;
+clean_up3:
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+ clk_disable(m66592->clk);
+ clk_put(m66592->clk);
clean_up2:
+#endif
free_irq(irq, m66592);
clean_up:
if (m66592) {
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
index f118f00f1466..286ce07e7960 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -23,6 +23,10 @@
#ifndef __M66592_UDC_H__
#define __M66592_UDC_H__
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+#include <linux/clk.h>
+#endif
+
#define M66592_SYSCFG 0x00
#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */
#define M66592_XTAL48 0x8000 /* 48MHz */
@@ -476,6 +480,9 @@ struct m66592_ep {
struct m66592 {
spinlock_t lock;
void __iomem *reg;
+#if defined(CONFIG_SUPERH_BUILT_IN_M66592) && defined(CONFIG_HAVE_CLK)
+ struct clk *clk;
+#endif
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
@@ -604,26 +611,6 @@ static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
#define m66592_bset(m66592, val, offset) \
m66592_mdfy(m66592, val, 0, offset)
-#if defined(CONFIG_SUPERH_BUILT_IN_M66592)
-#include <asm/io.h>
-#define MSTPCR2 0xA4150038 /* for SH7722 */
-#define MSTPCR2_USB 0x00000800
-
-static inline void usbf_start_clock(void)
-{
- ctrl_outl(ctrl_inl(MSTPCR2) & ~MSTPCR2_USB, MSTPCR2);
-}
-
-static inline void usbf_stop_clock(void)
-{
- ctrl_outl(ctrl_inl(MSTPCR2) | MSTPCR2_USB, MSTPCR2);
-}
-
-#else
-#define usbf_start_clock(x)
-#define usbf_stop_clock(x)
-#endif /* if defined(CONFIG_SUPERH_BUILT_IN_M66592) */
-
#endif /* ifndef __M66592_UDC_H__ */
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 2dbc0db0b46c..8c5026be79d4 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -2145,7 +2145,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
if (irq < 0)
return -ENODEV;
- dev->clk = clk_get(&pdev->dev, "UDCCLK");
+ dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
retval = PTR_ERR(dev->clk);
goto err_clk;
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index caa37c95802c..944e4ff641df 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -2226,7 +2226,7 @@ static int __init pxa_udc_probe(struct platform_device *pdev)
udc->dev = &pdev->dev;
udc->mach = pdev->dev.platform_data;
- udc->clk = clk_get(&pdev->dev, "UDCCLK");
+ udc->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(udc->clk)) {
retval = PTR_ERR(udc->clk);
goto err_clk;
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index 00ba06b44752..8d8d65165983 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -53,8 +53,8 @@
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/regs-udc.h>
-#include <asm/plat-s3c24xx/udc.h>
+#include <plat/regs-udc.h>
+#include <plat/udc.h>
#include "s3c2410_udc.h"
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 66948b72bb9b..d9739d52f8f5 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -146,7 +146,7 @@ static inline int qlen(struct usb_gadget *gadget)
/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
-static int eth_change_mtu(struct net_device *net, int new_mtu)
+static int ueth_change_mtu(struct net_device *net, int new_mtu)
{
struct eth_dev *dev = netdev_priv(net);
unsigned long flags;
@@ -764,7 +764,7 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
if (ethaddr)
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
- net->change_mtu = eth_change_mtu;
+ net->change_mtu = ueth_change_mtu;
net->hard_start_xmit = eth_start_xmit;
net->open = eth_open;
net->stop = eth_stop;
@@ -787,10 +787,8 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
free_netdev(net);
} else {
- DECLARE_MAC_BUF(tmp);
-
- INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr));
- INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac));
+ INFO(dev, "MAC %pM\n", net->dev_addr);
+ INFO(dev, "HOST MAC %pM\n", dev->host_mac);
the_dev = dev;
}
diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h
new file mode 100644
index 000000000000..09a75259b6cd
--- /dev/null
+++ b/drivers/usb/gadget/u_phonet.h
@@ -0,0 +1,21 @@
+/*
+ * u_phonet.h - interface to Phonet
+ *
+ * Copyright (C) 2007-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_PHONET_H
+#define __U_PHONET_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+int gphonet_setup(struct usb_gadget *gadget);
+int phonet_bind_config(struct usb_configuration *c);
+void gphonet_cleanup(void);
+
+#endif /* __U_PHONET_H */
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index 5416cf969005..9d487908012e 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -33,8 +33,9 @@
/*
* Implement Orion USB controller specification guidelines
*/
-static void orion_usb_setup(struct usb_hcd *hcd)
+static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)
{
+ /* The below GLs are according to the Orion Errata document */
/*
* Clear interrupt cause and mask
*/
@@ -258,9 +259,19 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
ehci_orion_conf_mbus_windows(hcd, pd->dram);
/*
- * setup Orion USB controller
+ * setup Orion USB controller.
*/
- orion_usb_setup(hcd);
+ switch (pd->phy_version) {
+ case EHCI_PHY_NA: /* dont change USB phy settings */
+ break;
+ case EHCI_PHY_ORION:
+ orion_usb_phy_v1_setup(hcd);
+ break;
+ case EHCI_PHY_DD:
+ case EHCI_PHY_KW:
+ default:
+ printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n");
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED);
if (err)
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 91697bdb399f..4bbddb73abd9 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -18,6 +18,7 @@
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/io.h>
@@ -25,7 +26,6 @@
#include <mach/mux.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
#include <mach/fpga.h>
#include <mach/usb.h>
@@ -254,8 +254,8 @@ static int ohci_omap_init(struct usb_hcd *hcd)
/* gpio9 for overcurrent detction */
omap_cfg_reg(W8_1610_GPIO9);
- omap_request_gpio(9);
- omap_set_gpio_direction(9, 1 /* IN */);
+ gpio_request(9, "OHCI overcurrent");
+ gpio_direction_input(9);
/* for paranoia's sake: disable USB.PUEN */
omap_cfg_reg(W4_USB_HIGHZ);
@@ -407,7 +407,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
put_device(ohci->transceiver->dev);
}
if (machine_is_omap_osk())
- omap_free_gpio(9);
+ gpio_free(9);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index e294d430733b..e44dc2cbca24 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -296,7 +296,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
return -ENXIO;
}
- usb_clk = clk_get(&pdev->dev, "USBCLK");
+ usb_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(usb_clk))
return PTR_ERR(usb_clk);
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 2376f24f3c83..c21f14e0666a 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -114,6 +114,9 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
int i = 0;
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_HAVE_CLK)
+ clk_enable(r8a66597->clk);
+#endif
do {
r8a66597_write(r8a66597, SCKE, SYSCFG0);
tmp = r8a66597_read(r8a66597, SYSCFG0);
@@ -154,7 +157,11 @@ static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
{
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
udelay(1);
-#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+#if defined(CONFIG_HAVE_CLK)
+ clk_disable(r8a66597->clk);
+#endif
+#else
r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
@@ -2261,6 +2268,9 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
del_timer_sync(&r8a66597->rh_timer);
usb_remove_hcd(hcd);
iounmap((void *)r8a66597->reg);
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+ clk_put(r8a66597->clk);
+#endif
usb_put_hcd(hcd);
return 0;
}
@@ -2268,6 +2278,9 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
#define resource_len(r) (((r)->end - (r)->start) + 1)
static int __init r8a66597_probe(struct platform_device *pdev)
{
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+ char clk_name[8];
+#endif
struct resource *res = NULL, *ires;
int irq = -1;
void __iomem *reg = NULL;
@@ -2320,6 +2333,16 @@ static int __init r8a66597_probe(struct platform_device *pdev)
memset(r8a66597, 0, sizeof(struct r8a66597));
dev_set_drvdata(&pdev->dev, r8a66597);
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+ snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
+ r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(r8a66597->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(r8a66597->clk);
+ goto clean_up2;
+ }
+#endif
+
spin_lock_init(&r8a66597->lock);
init_timer(&r8a66597->rh_timer);
r8a66597->rh_timer.function = r8a66597_timer;
@@ -2365,11 +2388,18 @@ static int __init r8a66597_probe(struct platform_device *pdev)
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to add hcd\n");
- goto clean_up;
+ goto clean_up3;
}
return 0;
+clean_up3:
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+ clk_put(r8a66597->clk);
+clean_up2:
+#endif
+ usb_put_hcd(hcd);
+
clean_up:
if (reg)
iounmap(reg);
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index 84ee01417315..ecacde4d69b0 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -26,6 +26,10 @@
#ifndef __R8A66597_H__
#define __R8A66597_H__
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+#include <linux/clk.h>
+#endif
+
#define SYSCFG0 0x00
#define SYSCFG1 0x02
#define SYSSTS0 0x04
@@ -481,7 +485,9 @@ struct r8a66597_root_hub {
struct r8a66597 {
spinlock_t lock;
unsigned long reg;
-
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
+ struct clk *clk;
+#endif
struct r8a66597_device device0;
struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB];
struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE];
diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c
index cdfe8dfc4340..10985fa233cc 100644
--- a/drivers/uwb/wlp/eda.c
+++ b/drivers/uwb/wlp/eda.c
@@ -313,12 +313,9 @@ int wlp_eda_for_virtual(struct wlp_eda *eda,
list_for_each_entry(itr, &eda->cache, list_node) {
if (!memcmp(itr->virt_addr, virt_addr,
sizeof(itr->virt_addr))) {
- d_printf(6, dev, "EDA: looking for "
- "%02x:%02x:%02x:%02x:%02x:%02x hit %02x:%02x "
+ d_printf(6, dev, "EDA: looking for %pM hit %02x:%02x "
"wss %p tag 0x%02x state %u\n",
- virt_addr[0], virt_addr[1],
- virt_addr[2], virt_addr[3],
- virt_addr[4], virt_addr[5],
+ virt_addr,
itr->dev_addr.data[1],
itr->dev_addr.data[0], itr->wss,
itr->tag, itr->state);
@@ -327,24 +324,13 @@ int wlp_eda_for_virtual(struct wlp_eda *eda,
found = 1;
break;
} else
- d_printf(6, dev, "EDA: looking for "
- "%02x:%02x:%02x:%02x:%02x:%02x "
- "against "
- "%02x:%02x:%02x:%02x:%02x:%02x miss\n",
- virt_addr[0], virt_addr[1],
- virt_addr[2], virt_addr[3],
- virt_addr[4], virt_addr[5],
- itr->virt_addr[0], itr->virt_addr[1],
- itr->virt_addr[2], itr->virt_addr[3],
- itr->virt_addr[4], itr->virt_addr[5]);
+ d_printf(6, dev, "EDA: looking for %pM against %pM miss\n",
+ virt_addr, itr->virt_addr);
}
if (!found) {
if (printk_ratelimit())
- dev_err(dev, "EDA: Eth addr %02x:%02x:%02x"
- ":%02x:%02x:%02x not found.\n",
- virt_addr[0], virt_addr[1],
- virt_addr[2], virt_addr[3],
- virt_addr[4], virt_addr[5]);
+ dev_err(dev, "EDA: Eth addr %pM not found.\n",
+ virt_addr);
result = -ENODEV;
}
spin_unlock_irqrestore(&eda->lock, flags);
@@ -380,19 +366,13 @@ ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
"tag state virt_addr\n");
list_for_each_entry(entry, &eda->cache, list_node) {
result += scnprintf(buf + result, PAGE_SIZE - result,
- "%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x "
- "%p 0x%02x %s "
- "%02x:%02x:%02x:%02x:%02x:%02x\n",
- entry->eth_addr[0], entry->eth_addr[1],
- entry->eth_addr[2], entry->eth_addr[3],
- entry->eth_addr[4], entry->eth_addr[5],
+ "%pM %02x:%02x %p 0x%02x %s %pM\n",
+ entry->eth_addr,
entry->dev_addr.data[1],
entry->dev_addr.data[0], entry->wss,
entry->tag,
wlp_wss_connect_state_str(entry->state),
- entry->virt_addr[0], entry->virt_addr[1],
- entry->virt_addr[2], entry->virt_addr[3],
- entry->virt_addr[4], entry->virt_addr[5]);
+ entry->virt_addr);
if (result >= PAGE_SIZE)
break;
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3f3ce13fef43..6372f8b17b45 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -362,7 +362,7 @@ endchoice
config FB_ACORN
bool "Acorn VIDC support"
- depends on (FB = y) && ARM && (ARCH_ACORN || ARCH_CLPS7500)
+ depends on (FB = y) && ARM && ARCH_ACORN
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -1817,6 +1817,11 @@ config FB_PXA
If unsure, say N.
+config FB_PXA_OVERLAY
+ bool "Support PXA27x/PXA3xx Overlay(s) as framebuffer"
+ default n
+ depends on FB_PXA && (PXA27x || PXA3xx)
+
config FB_PXA_SMARTPANEL
bool "PXA Smartpanel LCD support"
default n
@@ -1889,10 +1894,11 @@ config FB_W100
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && SUPERH
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- default m
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
@@ -2021,17 +2027,19 @@ config FB_COBALT
depends on FB && MIPS_COBALT
config FB_SH7760
- bool "SH7760/SH7763 LCDC support"
- depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763)
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller.
- Supports display resolutions up to 1024x1024 pixel, grayscale and
- color operation, with depths ranging from 1 bpp to 8 bpp monochrome
- and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
- panels <= 320 pixel horizontal resolution.
+ bool "SH7760/SH7763/SH7720/SH7721 LCDC support"
+ depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \
+ || CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Support for the SH7760/SH7763/SH7720/SH7721 integrated
+ (D)STN/TFT LCD Controller.
+ Supports display resolutions up to 1024x1024 pixel, grayscale and
+ color operation, with depths ranging from 1 bpp to 8 bpp monochrome
+ and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
+ panels <= 320 pixel horizontal resolution.
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index a7a1c891bfa2..2ac52fd8cc11 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -343,14 +343,14 @@ static int clcdfb_register(struct clcd_fb *fb)
{
int ret;
- fb->clk = clk_get(&fb->dev->dev, "CLCDCLK");
+ fb->clk = clk_get(&fb->dev->dev, NULL);
if (IS_ERR(fb->clk)) {
ret = PTR_ERR(fb->clk);
goto out;
}
fb->fb.fix.mmio_start = fb->dev->res.start;
- fb->fb.fix.mmio_len = SZ_4K;
+ fb->fb.fix.mmio_len = 4096;
fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
if (!fb->regs) {
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 0b2adefe9e3d..4bcff81b50e0 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -81,9 +81,6 @@
#ifdef CONFIG_ATARI
#include <asm/atariints.h>
#endif
-#ifdef CONFIG_MAC
-#include <asm/macints.h>
-#endif
#if defined(__mc68000__)
#include <asm/machdep.h>
#include <asm/setup.h>
@@ -160,8 +157,6 @@ static int fbcon_set_origin(struct vc_data *);
/* # VBL ints between cursor state changes */
#define ATARI_CURSOR_BLINK_RATE (42)
-#define MAC_CURSOR_BLINK_RATE (32)
-#define DEFAULT_CURSOR_BLINK_RATE (20)
static int vbl_cursor_cnt;
static int fbcon_cursor_noblink;
@@ -210,19 +205,6 @@ static void fbcon_start(void);
static void fbcon_exit(void);
static struct device *fbcon_device;
-#ifdef CONFIG_MAC
-/*
- * On the Macintoy, there may or may not be a working VBL int. We need to probe
- */
-static int vbl_detected;
-
-static irqreturn_t fb_vbl_detect(int irq, void *dummy)
-{
- vbl_detected++;
- return IRQ_HANDLED;
-}
-#endif
-
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
static inline void fbcon_set_rotation(struct fb_info *info)
{
@@ -421,7 +403,7 @@ static void fb_flashcursor(struct work_struct *work)
release_console_sem();
}
-#if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
+#ifdef CONFIG_ATARI
static int cursor_blink_rate;
static irqreturn_t fb_vbl_handler(int irq, void *dev_id)
{
@@ -949,9 +931,7 @@ static const char *fbcon_startup(void)
struct fb_info *info = NULL;
struct fbcon_ops *ops;
int rows, cols;
- int irqres;
- irqres = 1;
/*
* If num_registered_fb is zero, this is a call for the dummy part.
* The frame buffer devices weren't initialized yet.
@@ -1040,56 +1020,11 @@ static const char *fbcon_startup(void)
#ifdef CONFIG_ATARI
if (MACH_IS_ATARI) {
cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
- irqres =
- request_irq(IRQ_AUTO_4, fb_vbl_handler,
+ (void)request_irq(IRQ_AUTO_4, fb_vbl_handler,
IRQ_TYPE_PRIO, "framebuffer vbl",
info);
}
-#endif /* CONFIG_ATARI */
-
-#ifdef CONFIG_MAC
- /*
- * On a Macintoy, the VBL interrupt may or may not be active.
- * As interrupt based cursor is more reliable and race free, we
- * probe for VBL interrupts.
- */
- if (MACH_IS_MAC) {
- int ct = 0;
- /*
- * Probe for VBL: set temp. handler ...
- */
- irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
- "framebuffer vbl", info);
- vbl_detected = 0;
-
- /*
- * ... and spin for 20 ms ...
- */
- while (!vbl_detected && ++ct < 1000)
- udelay(20);
-
- if (ct == 1000)
- printk
- ("fbcon_startup: No VBL detected, using timer based cursor.\n");
-
- free_irq(IRQ_MAC_VBL, fb_vbl_detect);
-
- if (vbl_detected) {
- /*
- * interrupt based cursor ok
- */
- cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
- irqres =
- request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
- "framebuffer vbl", info);
- } else {
- /*
- * VBL not detected: fall through, use timer based cursor
- */
- irqres = 1;
- }
- }
-#endif /* CONFIG_MAC */
+#endif /* CONFIG_ATARI */
fbcon_add_cursor_timer(info);
fbcon_has_exited = 0;
@@ -3520,11 +3455,8 @@ static void fbcon_exit(void)
return;
#ifdef CONFIG_ATARI
- free_irq(IRQ_AUTO_4, fb_vbl_handler);
-#endif
-#ifdef CONFIG_MAC
- if (MACH_IS_MAC && vbl_detected)
- free_irq(IRQ_MAC_VBL, fb_vbl_handler);
+ if (MACH_IS_ATARI)
+ free_irq(IRQ_AUTO_4, fb_vbl_handler);
#endif
kfree((void *)softback_buf);
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 448d209a0bf2..e6210725b9ab 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -112,6 +112,23 @@ static int vga_video_font_height;
static int vga_scan_lines __read_mostly;
static unsigned int vga_rolled_over;
+int vgacon_text_mode_force = 0;
+
+bool vgacon_text_force(void)
+{
+ return vgacon_text_mode_force ? true : false;
+}
+EXPORT_SYMBOL(vgacon_text_force);
+
+static int __init text_mode(char *str)
+{
+ vgacon_text_mode_force = 1;
+ return 1;
+}
+
+/* force text mode - used by kernel modesetting */
+__setup("nomodeset", text_mode);
+
static int __init no_scroll(char *str)
{
/*
diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c
index b0be7eac32d8..49fcbe8f18ac 100644
--- a/drivers/video/controlfb.c
+++ b/drivers/video/controlfb.c
@@ -298,10 +298,10 @@ static int controlfb_mmap(struct fb_info *info,
return -EINVAL;
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK)+info->fix.mmio_len);
- pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
} else {
/* framebuffer */
- pgprot_val(vma->vm_page_prot) |= _PAGE_WRITETHRU;
+ vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot);
}
start &= PAGE_MASK;
if ((vma->vm_end - vma->vm_start + off) > len)
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index 41d62632dcdb..39d5d643a50b 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -1513,7 +1513,7 @@ static int cyberpro_pci_enable_mmio(struct cfb_info *cfb)
iop = ioremap(0x3000000, 0x5000);
if (iop == NULL) {
- prom_printf("iga5000: cannot map I/O\n");
+ printk(KERN_ERR "iga5000: cannot map I/O\n");
return -ENOMEM;
}
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 4835bdc4e9f1..082026546aee 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -24,6 +24,19 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
+{
+ void *screen_base = (void __force *) info->screen_base;
+ struct page *page;
+
+ if (is_vmalloc_addr(screen_base + offs))
+ page = vmalloc_to_page(screen_base + offs);
+ else
+ page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
+
+ return page;
+}
+
/* this is to find and return the vmalloc-ed fb pages */
static int fb_deferred_io_fault(struct vm_area_struct *vma,
struct vm_fault *vmf)
@@ -31,14 +44,12 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
unsigned long offset;
struct page *page;
struct fb_info *info = vma->vm_private_data;
- /* info->screen_base is virtual memory */
- void *screen_base = (void __force *) info->screen_base;
offset = vmf->pgoff << PAGE_SHIFT;
if (offset >= info->fix.smem_len)
return VM_FAULT_SIGBUS;
- page = vmalloc_to_page(screen_base + offset);
+ page = fb_deferred_io_page(info, offset);
if (!page)
return VM_FAULT_SIGBUS;
@@ -60,6 +71,10 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct fb_info *info = file->private_data;
+ /* Skip if deferred io is complied-in but disabled on this fbdev */
+ if (!info->fbdefio)
+ return 0;
+
/* Kill off the delayed work */
cancel_rearming_delayed_work(&info->deferred_work);
@@ -184,7 +199,6 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- void *screen_base = (void __force *) info->screen_base;
struct fb_deferred_io *fbdefio = info->fbdefio;
struct page *page;
int i;
@@ -195,9 +209,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
/* clear out the mapping that we setup */
for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
- page = vmalloc_to_page(screen_base + i);
+ page = fb_deferred_io_page(info, i);
page->mapping = NULL;
}
+
+ info->fbops->fb_mmap = NULL;
+ mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index ccd986140c95..d58c68cd456e 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -1,6 +1,4 @@
/*
- * linux/drivers/video/imxfb.c
- *
* Freescale i.MX Frame Buffer device driver
*
* Copyright (C) 2004 Sascha Hauer, Pengutronix
@@ -16,7 +14,6 @@
* linux-arm-kernel@lists.arm.linux.org.uk
*/
-//#define DEBUG 1
#include <linux/module.h>
#include <linux/kernel.h>
@@ -32,9 +29,8 @@
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
-#include <mach/hardware.h>
-#include <asm/io.h>
#include <mach/imxfb.h>
/*
@@ -42,23 +38,150 @@
*/
#define DEBUG_VAR 1
-#include "imxfb.h"
+#define DRIVER_NAME "imx-fb"
+
+#define LCDC_SSA 0x00
+
+#define LCDC_SIZE 0x04
+#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
+#define SIZE_YMAX(y) ((y) & 0x1ff)
+
+#define LCDC_VPW 0x08
+#define VPW_VPW(x) ((x) & 0x3ff)
+
+#define LCDC_CPOS 0x0C
+#define CPOS_CC1 (1<<31)
+#define CPOS_CC0 (1<<30)
+#define CPOS_OP (1<<28)
+#define CPOS_CXP(x) (((x) & 3ff) << 16)
+#define CPOS_CYP(y) ((y) & 0x1ff)
+
+#define LCDC_LCWHB 0x10
+#define LCWHB_BK_EN (1<<31)
+#define LCWHB_CW(w) (((w) & 0x1f) << 24)
+#define LCWHB_CH(h) (((h) & 0x1f) << 16)
+#define LCWHB_BD(x) ((x) & 0xff)
+
+#define LCDC_LCHCC 0x14
+#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
+#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
+#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
+
+#define LCDC_PCR 0x18
+
+#define LCDC_HCR 0x1C
+#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26)
+#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8)
+#define HCR_H_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_VCR 0x20
+#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26)
+#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8)
+#define VCR_V_WAIT_2(x) ((x) & 0xff)
+
+#define LCDC_POS 0x24
+#define POS_POS(x) ((x) & 1f)
+
+#define LCDC_LSCR1 0x28
+/* bit fields in imxfb.h */
+
+#define LCDC_PWMR 0x2C
+/* bit fields in imxfb.h */
+
+#define LCDC_DMACR 0x30
+/* bit fields in imxfb.h */
+
+#define LCDC_RMCR 0x34
+#define RMCR_LCDC_EN (1<<1)
+#define RMCR_SELF_REF (1<<0)
+
+#define LCDC_LCDICR 0x38
+#define LCDICR_INT_SYN (1<<2)
+#define LCDICR_INT_CON (1)
+
+#define LCDC_LCDISR 0x40
+#define LCDISR_UDR_ERR (1<<3)
+#define LCDISR_ERR_RES (1<<2)
+#define LCDISR_EOF (1<<1)
+#define LCDISR_BOF (1<<0)
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct imxfb_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+struct imxfb_info {
+ struct platform_device *pdev;
+ void __iomem *regs;
-static struct imxfb_rgb def_rgb_16 = {
- .red = { .offset = 8, .length = 4, },
- .green = { .offset = 4, .length = 4, },
- .blue = { .offset = 0, .length = 4, },
- .transp = { .offset = 0, .length = 0, },
+ u_int max_bpp;
+ u_int max_xres;
+ u_int max_yres;
+
+ /*
+ * These are the addresses we mapped
+ * the framebuffer memory region to.
+ */
+ dma_addr_t map_dma;
+ u_char *map_cpu;
+ u_int map_size;
+
+ u_char *screen_cpu;
+ dma_addr_t screen_dma;
+ u_int palette_size;
+
+ dma_addr_t dbar1;
+ dma_addr_t dbar2;
+
+ u_int pcr;
+ u_int pwmr;
+ u_int lscr1;
+ u_int dmacr;
+ u_int cmap_inverse:1,
+ cmap_static:1,
+ unused:30;
+
+ void (*lcd_power)(int);
+ void (*backlight_power)(int);
+};
+
+#define IMX_NAME "IMX"
+
+/*
+ * Minimum X and Y resolutions
+ */
+#define MIN_XRES 64
+#define MIN_YRES 64
+
+static struct imxfb_rgb def_rgb_16_tft = {
+ .red = {.offset = 11, .length = 5,},
+ .green = {.offset = 5, .length = 6,},
+ .blue = {.offset = 0, .length = 5,},
+ .transp = {.offset = 0, .length = 0,},
+};
+
+static struct imxfb_rgb def_rgb_16_stn = {
+ .red = {.offset = 8, .length = 4,},
+ .green = {.offset = 4, .length = 4,},
+ .blue = {.offset = 0, .length = 4,},
+ .transp = {.offset = 0, .length = 0,},
};
static struct imxfb_rgb def_rgb_8 = {
- .red = { .offset = 0, .length = 8, },
- .green = { .offset = 0, .length = 8, },
- .blue = { .offset = 0, .length = 8, },
- .transp = { .offset = 0, .length = 0, },
+ .red = {.offset = 0, .length = 8,},
+ .green = {.offset = 0, .length = 8,},
+ .blue = {.offset = 0, .length = 8,},
+ .transp = {.offset = 0, .length = 0,},
};
-static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int imxfb_activate_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
{
@@ -67,10 +190,8 @@ static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
return chan << bf->offset;
}
-#define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2)
-static int
-imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
- u_int trans, struct fb_info *info)
+static int imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
u_int val, ret = 1;
@@ -81,14 +202,13 @@ imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
(CNVT_TOHW(green,4) << 4) |
CNVT_TOHW(blue, 4);
- LCDC_PALETTE(regno) = val;
+ writel(val, fbi->regs + 0x800 + (regno << 2));
ret = 0;
}
return ret;
}
-static int
-imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int trans, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
@@ -148,11 +268,10 @@ imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
* yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
* bitfields, horizontal timing, vertical timing.
*/
-static int
-imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
- int rgbidx;
+ struct imxfb_rgb *rgb;
if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
@@ -168,23 +287,25 @@ imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
switch (var->bits_per_pixel) {
case 16:
- rgbidx = RGB_16;
+ default:
+ if (readl(fbi->regs + LCDC_PCR) & PCR_TFT)
+ rgb = &def_rgb_16_tft;
+ else
+ rgb = &def_rgb_16_stn;
break;
case 8:
- rgbidx = RGB_8;
+ rgb = &def_rgb_8;
break;
- default:
- rgbidx = RGB_16;
}
/*
* Copy the RGB parameters for this display
* from the machine specific parameters.
*/
- var->red = fbi->rgb[rgbidx]->red;
- var->green = fbi->rgb[rgbidx]->green;
- var->blue = fbi->rgb[rgbidx]->blue;
- var->transp = fbi->rgb[rgbidx]->transp;
+ var->red = rgb->red;
+ var->green = rgb->green;
+ var->blue = rgb->blue;
+ var->transp = rgb->transp;
pr_debug("RGBT length = %d:%d:%d:%d\n",
var->red.length, var->green.length, var->blue.length,
@@ -221,8 +342,7 @@ static int imxfb_set_par(struct fb_info *info)
info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
}
- info->fix.line_length = var->xres_virtual *
- var->bits_per_pixel / 8;
+ info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
imxfb_activate_var(var, info);
@@ -235,22 +355,27 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
pr_debug("Enabling LCD controller\n");
/* initialize LCDC */
- LCDC_RMCR &= ~RMCR_LCDC_EN; /* just to be safe... */
+ writel(readl(fbi->regs + LCDC_RMCR) & ~RMCR_LCDC_EN,
+ fbi->regs + LCDC_RMCR); /* just to be safe... */
+
+ writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
- LCDC_SSA = fbi->screen_dma;
/* physical screen start address */
- LCDC_VPW = VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4);
+ writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4),
+ fbi->regs + LCDC_VPW);
- LCDC_POS = 0x00000000; /* panning offset 0 (0 pixel offset) */
+ /* panning offset 0 (0 pixel offset) */
+ writel(0x00000000, fbi->regs + LCDC_POS);
/* disable hardware cursor */
- LCDC_CPOS &= ~(CPOS_CC0 | CPOS_CC1);
+ writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
+ fbi->regs + LCDC_CPOS);
- LCDC_RMCR = RMCR_LCDC_EN;
+ writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
- if(fbi->backlight_power)
+ if (fbi->backlight_power)
fbi->backlight_power(1);
- if(fbi->lcd_power)
+ if (fbi->lcd_power)
fbi->lcd_power(1);
}
@@ -258,12 +383,12 @@ static void imxfb_disable_controller(struct imxfb_info *fbi)
{
pr_debug("Disabling LCD controller\n");
- if(fbi->backlight_power)
+ if (fbi->backlight_power)
fbi->backlight_power(0);
- if(fbi->lcd_power)
+ if (fbi->lcd_power)
fbi->lcd_power(0);
- LCDC_RMCR = 0;
+ writel(0, fbi->regs + LCDC_RMCR);
}
static int imxfb_blank(int blank, struct fb_info *info)
@@ -340,74 +465,26 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
info->fix.id, var->lower_margin);
#endif
- LCDC_HCR = HCR_H_WIDTH(var->hsync_len) |
- HCR_H_WAIT_1(var->left_margin) |
- HCR_H_WAIT_2(var->right_margin);
+ writel(HCR_H_WIDTH(var->hsync_len) |
+ HCR_H_WAIT_1(var->right_margin) |
+ HCR_H_WAIT_2(var->left_margin),
+ fbi->regs + LCDC_HCR);
- LCDC_VCR = VCR_V_WIDTH(var->vsync_len) |
- VCR_V_WAIT_1(var->upper_margin) |
- VCR_V_WAIT_2(var->lower_margin);
+ writel(VCR_V_WIDTH(var->vsync_len) |
+ VCR_V_WAIT_1(var->lower_margin) |
+ VCR_V_WAIT_2(var->upper_margin),
+ fbi->regs + LCDC_VCR);
- LCDC_SIZE = SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres);
- LCDC_PCR = fbi->pcr;
- LCDC_PWMR = fbi->pwmr;
- LCDC_LSCR1 = fbi->lscr1;
- LCDC_DMACR = fbi->dmacr;
+ writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres),
+ fbi->regs + LCDC_SIZE);
+ writel(fbi->pcr, fbi->regs + LCDC_PCR);
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+ writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
+ writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
return 0;
}
-static void imxfb_setup_gpio(struct imxfb_info *fbi)
-{
- int width;
-
- LCDC_RMCR &= ~(RMCR_LCDC_EN | RMCR_SELF_REF);
-
- if( fbi->pcr & PCR_TFT )
- width = 16;
- else
- width = 1 << ((fbi->pcr >> 28) & 0x3);
-
- switch(width) {
- case 16:
- imx_gpio_mode(PD30_PF_LD15);
- imx_gpio_mode(PD29_PF_LD14);
- imx_gpio_mode(PD28_PF_LD13);
- imx_gpio_mode(PD27_PF_LD12);
- imx_gpio_mode(PD26_PF_LD11);
- imx_gpio_mode(PD25_PF_LD10);
- imx_gpio_mode(PD24_PF_LD9);
- imx_gpio_mode(PD23_PF_LD8);
- case 8:
- imx_gpio_mode(PD22_PF_LD7);
- imx_gpio_mode(PD21_PF_LD6);
- imx_gpio_mode(PD20_PF_LD5);
- imx_gpio_mode(PD19_PF_LD4);
- case 4:
- imx_gpio_mode(PD18_PF_LD3);
- imx_gpio_mode(PD17_PF_LD2);
- case 2:
- imx_gpio_mode(PD16_PF_LD1);
- case 1:
- imx_gpio_mode(PD15_PF_LD0);
- }
-
- /* initialize GPIOs */
- imx_gpio_mode(PD6_PF_LSCLK);
- imx_gpio_mode(PD11_PF_CONTRAST);
- imx_gpio_mode(PD14_PF_FLM_VSYNC);
- imx_gpio_mode(PD13_PF_LP_HSYNC);
- imx_gpio_mode(PD12_PF_ACD_OE);
-
- /* These are only needed for Sharp HR TFT displays */
- if (fbi->pcr & PCR_SHARP) {
- imx_gpio_mode(PD7_PF_REV);
- imx_gpio_mode(PD8_PF_CLS);
- imx_gpio_mode(PD9_PF_PS);
- imx_gpio_mode(PD10_PF_SPL_SPR);
- }
-}
-
#ifdef CONFIG_PM
/*
* Power management hooks. Note that we won't be called from IRQ context,
@@ -416,7 +493,8 @@ static void imxfb_setup_gpio(struct imxfb_info *fbi)
static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
{
struct imxfb_info *fbi = platform_get_drvdata(dev);
- pr_debug("%s\n",__func__);
+
+ pr_debug("%s\n", __func__);
imxfb_disable_controller(fbi);
return 0;
@@ -425,7 +503,8 @@ static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
static int imxfb_resume(struct platform_device *dev)
{
struct imxfb_info *fbi = platform_get_drvdata(dev);
- pr_debug("%s\n",__func__);
+
+ pr_debug("%s\n", __func__);
imxfb_enable_controller(fbi);
return 0;
@@ -435,149 +514,136 @@ static int imxfb_resume(struct platform_device *dev)
#define imxfb_resume NULL
#endif
-static int __init imxfb_init_fbinfo(struct device *dev)
+static int __init imxfb_init_fbinfo(struct platform_device *pdev)
{
- struct imxfb_mach_info *inf = dev->platform_data;
- struct fb_info *info = dev_get_drvdata(dev);
+ struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
+ struct fb_info *info = dev_get_drvdata(&pdev->dev);
struct imxfb_info *fbi = info->par;
pr_debug("%s\n",__func__);
- info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL);
+ info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
if (!info->pseudo_palette)
return -ENOMEM;
memset(fbi, 0, sizeof(struct imxfb_info));
- fbi->dev = dev;
strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
- info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.type_aux = 0;
info->fix.xpanstep = 0;
info->fix.ypanstep = 0;
info->fix.ywrapstep = 0;
- info->fix.accel = FB_ACCEL_NONE;
+ info->fix.accel = FB_ACCEL_NONE;
info->var.nonstd = 0;
info->var.activate = FB_ACTIVATE_NOW;
info->var.height = -1;
info->var.width = -1;
info->var.accel_flags = 0;
- info->var.vmode = FB_VMODE_NONINTERLACED;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
info->fbops = &imxfb_ops;
- info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
-
- fbi->rgb[RGB_16] = &def_rgb_16;
- fbi->rgb[RGB_8] = &def_rgb_8;
-
- fbi->max_xres = inf->xres;
- info->var.xres = inf->xres;
- info->var.xres_virtual = inf->xres;
- fbi->max_yres = inf->yres;
- info->var.yres = inf->yres;
- info->var.yres_virtual = inf->yres;
- fbi->max_bpp = inf->bpp;
- info->var.bits_per_pixel = inf->bpp;
- info->var.nonstd = inf->nonstd;
- info->var.pixclock = inf->pixclock;
- info->var.hsync_len = inf->hsync_len;
- info->var.left_margin = inf->left_margin;
- info->var.right_margin = inf->right_margin;
- info->var.vsync_len = inf->vsync_len;
- info->var.upper_margin = inf->upper_margin;
- info->var.lower_margin = inf->lower_margin;
- info->var.sync = inf->sync;
- info->var.grayscale = inf->cmap_greyscale;
- fbi->cmap_inverse = inf->cmap_inverse;
- fbi->cmap_static = inf->cmap_static;
- fbi->pcr = inf->pcr;
- fbi->lscr1 = inf->lscr1;
- fbi->dmacr = inf->dmacr;
- fbi->pwmr = inf->pwmr;
- fbi->lcd_power = inf->lcd_power;
- fbi->backlight_power = inf->backlight_power;
+ info->flags = FBINFO_FLAG_DEFAULT |
+ FBINFO_READS_FAST;
+
+ fbi->max_xres = pdata->xres;
+ info->var.xres = pdata->xres;
+ info->var.xres_virtual = pdata->xres;
+ fbi->max_yres = pdata->yres;
+ info->var.yres = pdata->yres;
+ info->var.yres_virtual = pdata->yres;
+ fbi->max_bpp = pdata->bpp;
+ info->var.bits_per_pixel = pdata->bpp;
+ info->var.nonstd = pdata->nonstd;
+ info->var.pixclock = pdata->pixclock;
+ info->var.hsync_len = pdata->hsync_len;
+ info->var.left_margin = pdata->left_margin;
+ info->var.right_margin = pdata->right_margin;
+ info->var.vsync_len = pdata->vsync_len;
+ info->var.upper_margin = pdata->upper_margin;
+ info->var.lower_margin = pdata->lower_margin;
+ info->var.sync = pdata->sync;
+ info->var.grayscale = pdata->cmap_greyscale;
+ fbi->cmap_inverse = pdata->cmap_inverse;
+ fbi->cmap_static = pdata->cmap_static;
+ fbi->pcr = pdata->pcr;
+ fbi->lscr1 = pdata->lscr1;
+ fbi->dmacr = pdata->dmacr;
+ fbi->pwmr = pdata->pwmr;
+ fbi->lcd_power = pdata->lcd_power;
+ fbi->backlight_power = pdata->backlight_power;
info->fix.smem_len = fbi->max_xres * fbi->max_yres *
fbi->max_bpp / 8;
return 0;
}
-/*
- * Allocates the DRAM memory for the frame buffer. This buffer is
- * remapped into a non-cached, non-buffered, memory region to
- * allow pixel writes to occur without flushing the cache.
- * Once this area is remapped, all virtual memory access to the
- * video memory should occur at the new region.
- */
-static int __init imxfb_map_video_memory(struct fb_info *info)
-{
- struct imxfb_info *fbi = info->par;
-
- fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
- fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
- &fbi->map_dma,GFP_KERNEL);
-
- if (fbi->map_cpu) {
- info->screen_base = fbi->map_cpu;
- fbi->screen_cpu = fbi->map_cpu;
- fbi->screen_dma = fbi->map_dma;
- info->fix.smem_start = fbi->screen_dma;
- }
-
- return fbi->map_cpu ? 0 : -ENOMEM;
-}
-
static int __init imxfb_probe(struct platform_device *pdev)
{
struct imxfb_info *fbi;
struct fb_info *info;
- struct imxfb_mach_info *inf;
+ struct imx_fb_platform_data *pdata;
struct resource *res;
int ret;
printk("i.MX Framebuffer driver\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if(!res)
+ if (!res)
return -ENODEV;
- inf = pdev->dev.platform_data;
- if(!inf) {
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
dev_err(&pdev->dev,"No platform_data available\n");
return -ENOMEM;
}
info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
- if(!info)
+ if (!info)
return -ENOMEM;
fbi = info->par;
platform_set_drvdata(pdev, info);
- ret = imxfb_init_fbinfo(&pdev->dev);
- if( ret < 0 )
+ ret = imxfb_init_fbinfo(pdev);
+ if (ret < 0)
goto failed_init;
- res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB");
+ res = request_mem_region(res->start, resource_size(res),
+ DRIVER_NAME);
if (!res) {
ret = -EBUSY;
- goto failed_regs;
+ goto failed_req;
+ }
+
+ fbi->regs = ioremap(res->start, resource_size(res));
+ if (fbi->regs == NULL) {
+ printk(KERN_ERR"Cannot map frame buffer registers\n");
+ goto failed_ioremap;
}
- if (!inf->fixed_screen_cpu) {
- ret = imxfb_map_video_memory(info);
- if (ret) {
+ if (!pdata->fixed_screen_cpu) {
+ fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
+ fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
+ fbi->map_size, &fbi->map_dma, GFP_KERNEL);
+
+ if (!fbi->map_cpu) {
dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto failed_map;
}
+
+ info->screen_base = fbi->map_cpu;
+ fbi->screen_cpu = fbi->map_cpu;
+ fbi->screen_dma = fbi->map_dma;
+ info->fix.smem_start = fbi->screen_dma;
} else {
/* Fixed framebuffer mapping enables location of the screen in eSRAM */
- fbi->map_cpu = inf->fixed_screen_cpu;
- fbi->map_dma = inf->fixed_screen_dma;
+ fbi->map_cpu = pdata->fixed_screen_cpu;
+ fbi->map_dma = pdata->fixed_screen_dma;
info->screen_base = fbi->map_cpu;
fbi->screen_cpu = fbi->map_cpu;
fbi->screen_dma = fbi->map_dma;
@@ -590,12 +656,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
*/
imxfb_check_var(&info->var, info);
- ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
+ ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
if (ret < 0)
goto failed_cmap;
- imxfb_setup_gpio(fbi);
-
imxfb_set_par(info);
ret = register_framebuffer(info);
if (ret < 0) {
@@ -610,20 +674,22 @@ static int __init imxfb_probe(struct platform_device *pdev)
failed_register:
fb_dealloc_cmap(&info->cmap);
failed_cmap:
- if (!inf->fixed_screen_cpu)
+ if (!pdata->fixed_screen_cpu)
dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
- fbi->map_dma);
+ fbi->map_dma);
failed_map:
- kfree(info->pseudo_palette);
-failed_regs:
+ iounmap(fbi->regs);
+failed_ioremap:
release_mem_region(res->start, res->end - res->start);
+failed_req:
+ kfree(info->pseudo_palette);
failed_init:
platform_set_drvdata(pdev, NULL);
framebuffer_release(info);
return ret;
}
-static int imxfb_remove(struct platform_device *pdev)
+static int __devexit imxfb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
struct imxfb_info *fbi = info->par;
@@ -639,6 +705,7 @@ static int imxfb_remove(struct platform_device *pdev)
kfree(info->pseudo_palette);
framebuffer_release(info);
+ iounmap(fbi->regs);
release_mem_region(res->start, res->end - res->start + 1);
platform_set_drvdata(pdev, NULL);
@@ -653,19 +720,18 @@ void imxfb_shutdown(struct platform_device * dev)
}
static struct platform_driver imxfb_driver = {
- .probe = imxfb_probe,
.suspend = imxfb_suspend,
.resume = imxfb_resume,
- .remove = imxfb_remove,
+ .remove = __devexit_p(imxfb_remove),
.shutdown = imxfb_shutdown,
.driver = {
- .name = "imx-fb",
+ .name = DRIVER_NAME,
},
};
int __init imxfb_init(void)
{
- return platform_driver_register(&imxfb_driver);
+ return platform_driver_probe(&imxfb_driver, imxfb_probe);
}
static void __exit imxfb_cleanup(void)
diff --git a/drivers/video/imxfb.h b/drivers/video/imxfb.h
deleted file mode 100644
index e837a8b48eb8..000000000000
--- a/drivers/video/imxfb.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * linux/drivers/video/imxfb.h
- *
- * Freescale i.MX Frame Buffer device driver
- *
- * Copyright (C) 2004 S.Hauer, Pengutronix
- *
- * Copyright (C) 1999 Eric A. Thomas
- * Based on acornfb.c Copyright (C) Russell King.
- *
- * 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.
- */
-
-/*
- * These are the bitfields for each
- * display depth that we support.
- */
-struct imxfb_rgb {
- struct fb_bitfield red;
- struct fb_bitfield green;
- struct fb_bitfield blue;
- struct fb_bitfield transp;
-};
-
-#define RGB_16 (0)
-#define RGB_8 (1)
-#define NR_RGB 2
-
-struct imxfb_info {
- struct device *dev;
- struct imxfb_rgb *rgb[NR_RGB];
-
- u_int max_bpp;
- u_int max_xres;
- u_int max_yres;
-
- /*
- * These are the addresses we mapped
- * the framebuffer memory region to.
- */
- dma_addr_t map_dma;
- u_char * map_cpu;
- u_int map_size;
-
- u_char * screen_cpu;
- dma_addr_t screen_dma;
- u_int palette_size;
-
- dma_addr_t dbar1;
- dma_addr_t dbar2;
-
- u_int pcr;
- u_int pwmr;
- u_int lscr1;
- u_int dmacr;
- u_int cmap_inverse:1,
- cmap_static:1,
- unused:30;
-
- void (*lcd_power)(int);
- void (*backlight_power)(int);
-};
-
-#define IMX_NAME "IMX"
-
-/*
- * Minimum X and Y resolutions
- */
-#define MIN_XRES 64
-#define MIN_YRES 64
-
diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c
index ee380d5f3410..d66887e8cbb1 100644
--- a/drivers/video/macfb.c
+++ b/drivers/video/macfb.c
@@ -36,7 +36,6 @@
#include <asm/irq.h>
#include <asm/macintosh.h>
#include <asm/io.h>
-#include <asm/machw.h>
/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */
#define DAC_BASE 0x50f24000
@@ -78,34 +77,34 @@ static int csc_setpalette (unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
struct fb_info *fb_info);
-static volatile struct {
+static struct {
unsigned char addr;
/* Note: word-aligned */
char pad[3];
unsigned char lut;
-} *valkyrie_cmap_regs;
+} __iomem *valkyrie_cmap_regs;
-static volatile struct {
+static struct {
unsigned char addr;
unsigned char lut;
-} *v8_brazil_cmap_regs;
+} __iomem *v8_brazil_cmap_regs;
-static volatile struct {
+static struct {
unsigned char addr;
char pad1[3]; /* word aligned */
unsigned char lut;
char pad2[3]; /* word aligned */
unsigned char cntl; /* a guess as to purpose */
-} *rbv_cmap_regs;
+} __iomem *rbv_cmap_regs;
-static volatile struct {
+static struct {
unsigned long reset;
unsigned long pad1[3];
unsigned char pad2[3];
unsigned char lut;
-} *dafb_cmap_regs;
+} __iomem *dafb_cmap_regs;
-static volatile struct {
+static struct {
unsigned char addr; /* OFFSET: 0x00 */
unsigned char pad1[15];
unsigned char lut; /* OFFSET: 0x10 */
@@ -114,16 +113,16 @@ static volatile struct {
unsigned char pad3[7];
unsigned long vbl_addr; /* OFFSET: 0x28 */
unsigned int status2; /* OFFSET: 0x2C */
-} *civic_cmap_regs;
+} __iomem *civic_cmap_regs;
-static volatile struct {
+static struct {
char pad1[0x40];
unsigned char clut_waddr; /* 0x40 */
char pad2;
unsigned char clut_data; /* 0x42 */
char pad3[0x3];
unsigned char clut_raddr; /* 0x46 */
-} *csc_cmap_regs;
+} __iomem *csc_cmap_regs;
/* We will leave these the way they are for the time being */
struct mdc_cmap_regs {
@@ -507,10 +506,10 @@ static int csc_setpalette (unsigned int regno, unsigned int red,
struct fb_info *info)
{
mdelay(1);
- csc_cmap_regs->clut_waddr = regno;
- csc_cmap_regs->clut_data = red;
- csc_cmap_regs->clut_data = green;
- csc_cmap_regs->clut_data = blue;
+ nubus_writeb(regno, &csc_cmap_regs->clut_waddr);
+ nubus_writeb(red, &csc_cmap_regs->clut_data);
+ nubus_writeb(green, &csc_cmap_regs->clut_data);
+ nubus_writeb(blue, &csc_cmap_regs->clut_data);
return 0;
}
diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index 4b5d80771904..38ac805db97d 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -460,12 +460,16 @@ static void ps3fb_sync_image(struct device *dev, u64 frame_offset,
line_length |= (u64)src_line_length << 32;
src_offset += GPU_FB_START;
+
+ mutex_lock(&ps3_gpu_mutex);
status = lv1_gpu_context_attribute(ps3fb.context_handle,
L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT,
dst_offset, GPU_IOIF + src_offset,
L1GPU_FB_BLIT_WAIT_FOR_COMPLETION |
(width << 16) | height,
line_length);
+ mutex_unlock(&ps3_gpu_mutex);
+
if (status)
dev_err(dev,
"%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n",
@@ -784,15 +788,6 @@ static int ps3fb_wait_for_vsync(u32 crtc)
return 0;
}
-static void ps3fb_flip_ctl(int on, void *data)
-{
- struct ps3fb_priv *priv = data;
- if (on)
- atomic_dec_if_positive(&priv->ext_flip);
- else
- atomic_inc(&priv->ext_flip);
-}
-
/*
* ioctl
@@ -1228,7 +1223,6 @@ static int __devinit ps3fb_probe(struct ps3_system_bus_device *dev)
}
ps3fb.task = task;
- ps3av_register_flip_ctl(ps3fb_flip_ctl, &ps3fb);
return 0;
@@ -1258,10 +1252,9 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
- ps3fb_flip_ctl(0, &ps3fb); /* flip off */
+ atomic_inc(&ps3fb.ext_flip); /* flip off */
ps3fb.dinfo->irq.mask = 0;
- ps3av_register_flip_ctl(NULL, NULL);
if (ps3fb.task) {
struct task_struct *task = ps3fb.task;
ps3fb.task = NULL;
@@ -1296,8 +1289,8 @@ static int ps3fb_shutdown(struct ps3_system_bus_device *dev)
}
static struct ps3_system_bus_driver ps3fb_driver = {
- .match_id = PS3_MATCH_ID_GRAPHICS,
- .match_sub_id = PS3_MATCH_SUB_ID_FB,
+ .match_id = PS3_MATCH_ID_GPU,
+ .match_sub_id = PS3_MATCH_SUB_ID_GPU_FB,
.core.name = DEVICE_NAME,
.core.owner = THIS_MODULE,
.probe = ps3fb_probe,
@@ -1355,4 +1348,4 @@ module_exit(ps3fb_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver");
MODULE_AUTHOR("Sony Computer Entertainment Inc.");
-MODULE_ALIAS(PS3_MODULE_ALIAS_GRAPHICS);
+MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_FB);
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index cc59c52e1103..48ff701d3a72 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -20,6 +20,16 @@
*
* linux-arm-kernel@lists.arm.linux.org.uk
*
+ * Add support for overlay1 and overlay2 based on pxafb_overlay.c:
+ *
+ * Copyright (C) 2004, Intel Corporation
+ *
+ * 2003/08/27: <yu.tang@intel.com>
+ * 2004/03/10: <stanley.cai@intel.com>
+ * 2004/10/28: <yan.yin@intel.com>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * All Rights Reserved
*/
#include <linux/module.h>
@@ -50,7 +60,6 @@
#include <asm/irq.h>
#include <asm/div64.h>
#include <mach/pxa-regs.h>
-#include <mach/pxa2xx-gpio.h>
#include <mach/bitfield.h>
#include <mach/pxafb.h>
@@ -67,14 +76,16 @@
LCCR0_SFM | LCCR0_LDM | LCCR0_ENB)
#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\
- LCCR3_PCD | LCCR3_BPP)
-
-static void (*pxafb_backlight_power)(int);
-static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *);
+ LCCR3_PCD | LCCR3_BPP(0xf))
static int pxafb_activate_var(struct fb_var_screeninfo *var,
struct pxafb_info *);
static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
+static void setup_base_frame(struct pxafb_info *fbi, int branch);
+static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
+ unsigned long offset, size_t size);
+
+static unsigned long video_mem_size = 0;
static inline unsigned long
lcd_readl(struct pxafb_info *fbi, unsigned int off)
@@ -156,6 +167,12 @@ pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
val |= ((blue >> 8) & 0x000000fc);
((u32 *)(fbi->palette_cpu))[regno] = val;
break;
+ case LCCR4_PAL_FOR_3:
+ val = ((red << 8) & 0x00ff0000);
+ val |= ((green >> 0) & 0x0000ff00);
+ val |= ((blue >> 8) & 0x000000ff);
+ ((u32 *)(fbi->palette_cpu))[regno] = val;
+ break;
}
return 0;
@@ -216,37 +233,110 @@ pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
return ret;
}
-/*
- * pxafb_bpp_to_lccr3():
- * Convert a bits per pixel value to the correct bit pattern for LCCR3
- */
-static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var)
+/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */
+static inline int var_to_depth(struct fb_var_screeninfo *var)
{
- int ret = 0;
+ return var->red.length + var->green.length +
+ var->blue.length + var->transp.length;
+}
+
+/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */
+static int pxafb_var_to_bpp(struct fb_var_screeninfo *var)
+{
+ int bpp = -EINVAL;
+
switch (var->bits_per_pixel) {
- case 1: ret = LCCR3_1BPP; break;
- case 2: ret = LCCR3_2BPP; break;
- case 4: ret = LCCR3_4BPP; break;
- case 8: ret = LCCR3_8BPP; break;
- case 16: ret = LCCR3_16BPP; break;
+ case 1: bpp = 0; break;
+ case 2: bpp = 1; break;
+ case 4: bpp = 2; break;
+ case 8: bpp = 3; break;
+ case 16: bpp = 4; break;
case 24:
- switch (var->red.length + var->green.length +
- var->blue.length + var->transp.length) {
- case 18: ret = LCCR3_18BPP_P | LCCR3_PDFOR_3; break;
- case 19: ret = LCCR3_19BPP_P; break;
+ switch (var_to_depth(var)) {
+ case 18: bpp = 6; break; /* 18-bits/pixel packed */
+ case 19: bpp = 8; break; /* 19-bits/pixel packed */
+ case 24: bpp = 9; break;
}
break;
case 32:
- switch (var->red.length + var->green.length +
- var->blue.length + var->transp.length) {
- case 18: ret = LCCR3_18BPP | LCCR3_PDFOR_3; break;
- case 19: ret = LCCR3_19BPP; break;
- case 24: ret = LCCR3_24BPP | LCCR3_PDFOR_3; break;
- case 25: ret = LCCR3_25BPP; break;
+ switch (var_to_depth(var)) {
+ case 18: bpp = 5; break; /* 18-bits/pixel unpacked */
+ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */
+ case 25: bpp = 10; break;
}
break;
}
- return ret;
+ return bpp;
+}
+
+/*
+ * pxafb_var_to_lccr3():
+ * Convert a bits per pixel value to the correct bit pattern for LCCR3
+ *
+ * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an
+ * implication of the acutal use of transparency bit, which we handle it
+ * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel
+ * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP.
+ *
+ * Transparency for palette pixel formats is not supported at the moment.
+ */
+static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var)
+{
+ int bpp = pxafb_var_to_bpp(var);
+ uint32_t lccr3;
+
+ if (bpp < 0)
+ return 0;
+
+ lccr3 = LCCR3_BPP(bpp);
+
+ switch (var_to_depth(var)) {
+ case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break;
+ case 18: lccr3 |= LCCR3_PDFOR_3; break;
+ case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3;
+ break;
+ case 19:
+ case 25: lccr3 |= LCCR3_PDFOR_0; break;
+ }
+ return lccr3;
+}
+
+#define SET_PIXFMT(v, r, g, b, t) \
+({ \
+ (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \
+ (v)->transp.length = (t) ? (t) : 0; \
+ (v)->blue.length = (b); (v)->blue.offset = 0; \
+ (v)->green.length = (g); (v)->green.offset = (b); \
+ (v)->red.length = (r); (v)->red.offset = (b) + (g); \
+})
+
+/* set the RGBT bitfields of fb_var_screeninf according to
+ * var->bits_per_pixel and given depth
+ */
+static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth)
+{
+ if (depth == 0)
+ depth = var->bits_per_pixel;
+
+ if (var->bits_per_pixel < 16) {
+ /* indexed pixel formats */
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 8;
+ }
+
+ switch (depth) {
+ case 16: var->transp.length ?
+ SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */
+ SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */
+ case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */
+ case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */
+ case 24: var->transp.length ?
+ SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */
+ SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */
+ case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */
+ }
}
#ifdef CONFIG_CPU_FREQ
@@ -308,8 +398,49 @@ static void pxafb_setmode(struct fb_var_screeninfo *var,
var->lower_margin = mode->lower_margin;
var->sync = mode->sync;
var->grayscale = mode->cmap_greyscale;
- var->xres_virtual = var->xres;
- var->yres_virtual = var->yres;
+
+ /* set the initial RGBA bitfields */
+ pxafb_set_pixfmt(var, mode->depth);
+}
+
+static int pxafb_adjust_timing(struct pxafb_info *fbi,
+ struct fb_var_screeninfo *var)
+{
+ int line_length;
+
+ var->xres = max_t(int, var->xres, MIN_XRES);
+ var->yres = max_t(int, var->yres, MIN_YRES);
+
+ if (!(fbi->lccr0 & LCCR0_LCDT)) {
+ clamp_val(var->hsync_len, 1, 64);
+ clamp_val(var->vsync_len, 1, 64);
+ clamp_val(var->left_margin, 1, 255);
+ clamp_val(var->right_margin, 1, 255);
+ clamp_val(var->upper_margin, 1, 255);
+ clamp_val(var->lower_margin, 1, 255);
+ }
+
+ /* make sure each line is aligned on word boundary */
+ line_length = var->xres * var->bits_per_pixel / 8;
+ line_length = ALIGN(line_length, 4);
+ var->xres = line_length * 8 / var->bits_per_pixel;
+
+ /* we don't support xpan, force xres_virtual to be equal to xres */
+ var->xres_virtual = var->xres;
+
+ if (var->accel_flags & FB_ACCELF_TEXT)
+ var->yres_virtual = fbi->fb.fix.smem_len / line_length;
+ else
+ var->yres_virtual = max(var->yres_virtual, var->yres);
+
+ /* check for limits */
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+ return -EINVAL;
+
+ if (var->yres > var->yres_virtual)
+ return -EINVAL;
+
+ return 0;
}
/*
@@ -325,11 +456,7 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct pxafb_info *fbi = (struct pxafb_info *)info;
struct pxafb_mach_info *inf = fbi->dev->platform_data;
-
- if (var->xres < MIN_XRES)
- var->xres = MIN_XRES;
- if (var->yres < MIN_YRES)
- var->yres = MIN_YRES;
+ int err;
if (inf->fixed_modes) {
struct pxafb_mode_info *mode;
@@ -338,74 +465,18 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (!mode)
return -EINVAL;
pxafb_setmode(var, mode);
- } else {
- if (var->xres > inf->modes->xres)
- return -EINVAL;
- if (var->yres > inf->modes->yres)
- return -EINVAL;
- if (var->bits_per_pixel > inf->modes->bpp)
- return -EINVAL;
}
- var->xres_virtual =
- max(var->xres_virtual, var->xres);
- var->yres_virtual =
- max(var->yres_virtual, var->yres);
+ /* do a test conversion to BPP fields to check the color formats */
+ err = pxafb_var_to_bpp(var);
+ if (err < 0)
+ return err;
- /*
- * Setup the RGB parameters for this display.
- *
- * The pixel packing format is described on page 7-11 of the
- * PXA2XX Developer's Manual.
- */
- if (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;
- var->transp.offset = var->transp.length = 0;
- } else if (var->bits_per_pixel > 16) {
- struct pxafb_mode_info *mode;
+ pxafb_set_pixfmt(var, var_to_depth(var));
- mode = pxafb_getmode(inf, var);
- if (!mode)
- return -EINVAL;
-
- switch (mode->depth) {
- case 18: /* RGB666 */
- var->transp.offset = var->transp.length = 0;
- var->red.offset = 12; var->red.length = 6;
- var->green.offset = 6; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 6;
- break;
- case 19: /* RGBT666 */
- var->transp.offset = 18; var->transp.length = 1;
- var->red.offset = 12; var->red.length = 6;
- var->green.offset = 6; var->green.length = 6;
- var->blue.offset = 0; var->blue.length = 6;
- break;
- case 24: /* RGB888 */
- var->transp.offset = var->transp.length = 0;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- break;
- case 25: /* RGBT888 */
- var->transp.offset = 24; var->transp.length = 1;
- var->red.offset = 16; var->red.length = 8;
- var->green.offset = 8; var->green.length = 8;
- var->blue.offset = 0; var->blue.length = 8;
- break;
- default:
- return -EINVAL;
- }
- } else {
- var->red.offset = var->green.offset = 0;
- var->blue.offset = var->transp.offset = 0;
- var->red.length = 8;
- var->green.length = 8;
- var->blue.length = 8;
- var->transp.length = 0;
- }
+ err = pxafb_adjust_timing(fbi, var);
+ if (err)
+ return err;
#ifdef CONFIG_CPU_FREQ
pr_debug("pxafb: dma period = %d ps\n",
@@ -415,11 +486,6 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
return 0;
}
-static inline void pxafb_set_truecolor(u_int is_true_color)
-{
- /* do your machine-specific setup if needed */
-}
-
/*
* pxafb_set_par():
* Set the user defined part of the display for the specified console
@@ -452,11 +518,6 @@ static int pxafb_set_par(struct fb_info *info)
fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0];
- /*
- * Set (any) board control register to handle new color depth
- */
- pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
-
if (fbi->fb.var.bits_per_pixel >= 16)
fb_dealloc_cmap(&fbi->fb.cmap);
else
@@ -467,6 +528,24 @@ static int pxafb_set_par(struct fb_info *info)
return 0;
}
+static int pxafb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxafb_info *fbi = (struct pxafb_info *)info;
+ int dma = DMA_MAX + DMA_BASE;
+
+ if (fbi->state != C_ENABLE)
+ return 0;
+
+ setup_base_frame(fbi, 1);
+
+ if (fbi->lccr0 & LCCR0_SDS)
+ lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1);
+
+ lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1);
+ return 0;
+}
+
/*
* pxafb_blank():
* Blank the display by setting all palette values to zero. Note, the
@@ -502,32 +581,342 @@ static int pxafb_blank(int blank, struct fb_info *info)
return 0;
}
-static int pxafb_mmap(struct fb_info *info,
- struct vm_area_struct *vma)
-{
- struct pxafb_info *fbi = (struct pxafb_info *)info;
- unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
-
- if (off < info->fix.smem_len) {
- vma->vm_pgoff += fbi->video_offset / PAGE_SIZE;
- return dma_mmap_writecombine(fbi->dev, vma, fbi->map_cpu,
- fbi->map_dma, fbi->map_size);
- }
- return -EINVAL;
-}
-
static struct fb_ops pxafb_ops = {
.owner = THIS_MODULE,
.fb_check_var = pxafb_check_var,
.fb_set_par = pxafb_set_par,
+ .fb_pan_display = pxafb_pan_display,
.fb_setcolreg = pxafb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = pxafb_blank,
- .fb_mmap = pxafb_mmap,
};
+#ifdef CONFIG_FB_PXA_OVERLAY
+static void overlay1fb_setup(struct pxafb_layer *ofb)
+{
+ int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;
+ unsigned long start = ofb->video_mem_phys;
+ setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size);
+}
+
+/* Depending on the enable status of overlay1/2, the DMA should be
+ * updated from FDADRx (when disabled) or FBRx (when enabled).
+ */
+static void overlay1fb_enable(struct pxafb_layer *ofb)
+{
+ int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN;
+ uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0);
+
+ lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1);
+ lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]);
+ lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN);
+}
+
+static void overlay1fb_disable(struct pxafb_layer *ofb)
+{
+ uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
+
+ lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN);
+
+ lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1));
+ lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1));
+ lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3);
+
+ if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)
+ pr_warning("%s: timeout disabling overlay1\n", __func__);
+
+ lcd_writel(ofb->fbi, LCCR5, lccr5);
+}
+
+static void overlay2fb_setup(struct pxafb_layer *ofb)
+{
+ int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);
+ unsigned long start[3] = { ofb->video_mem_phys, 0, 0 };
+
+ if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) {
+ size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual;
+ setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size);
+ } else {
+ size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual;
+ switch (pfor) {
+ case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break;
+ }
+ start[1] = start[0] + size;
+ start[2] = start[1] + size / div;
+ setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size);
+ setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div);
+ setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div);
+ }
+}
+
+static void overlay2fb_enable(struct pxafb_layer *ofb)
+{
+ int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd);
+ int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN;
+ uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0);
+ uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0);
+ uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0);
+
+ if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED)
+ lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);
+ else {
+ lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2);
+ lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3);
+ lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4);
+ }
+ lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]);
+ lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN);
+}
+
+static void overlay2fb_disable(struct pxafb_layer *ofb)
+{
+ uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5);
+
+ lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN);
+
+ lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2));
+ lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2));
+ lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3);
+ lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3);
+ lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3);
+
+ if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0)
+ pr_warning("%s: timeout disabling overlay2\n", __func__);
+}
+
+static struct pxafb_layer_ops ofb_ops[] = {
+ [0] = {
+ .enable = overlay1fb_enable,
+ .disable = overlay1fb_disable,
+ .setup = overlay1fb_setup,
+ },
+ [1] = {
+ .enable = overlay2fb_enable,
+ .disable = overlay2fb_disable,
+ .setup = overlay2fb_setup,
+ },
+};
+
+static int overlayfb_open(struct fb_info *info, int user)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+
+ /* no support for framebuffer console on overlay */
+ if (user == 0)
+ return -ENODEV;
+
+ /* allow only one user at a time */
+ if (atomic_inc_and_test(&ofb->usage))
+ return -EBUSY;
+
+ /* unblank the base framebuffer */
+ fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
+ return 0;
+}
+
+static int overlayfb_release(struct fb_info *info, int user)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer*) info;
+
+ atomic_dec(&ofb->usage);
+ ofb->ops->disable(ofb);
+
+ free_pages_exact(ofb->video_mem, ofb->video_mem_size);
+ ofb->video_mem = NULL;
+ ofb->video_mem_size = 0;
+ return 0;
+}
+
+static int overlayfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+ struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var;
+ int xpos, ypos, pfor, bpp;
+
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_XPOS(var->nonstd);
+ pfor = NONSTD_TO_PFOR(var->nonstd);
+
+ bpp = pxafb_var_to_bpp(var);
+ if (bpp < 0)
+ return -EINVAL;
+
+ /* no support for YUV format on overlay1 */
+ if (ofb->id == OVERLAY1 && pfor != 0)
+ return -EINVAL;
+
+ /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */
+ switch (pfor) {
+ case OVERLAY_FORMAT_RGB:
+ bpp = pxafb_var_to_bpp(var);
+ if (bpp < 0)
+ return -EINVAL;
+
+ pxafb_set_pixfmt(var, var_to_depth(var));
+ break;
+ case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break;
+ default:
+ return -EINVAL;
+ }
+
+ /* each line must start at a 32-bit word boundary */
+ if ((xpos * bpp) % 32)
+ return -EINVAL;
+
+ /* xres must align on 32-bit word boundary */
+ var->xres = roundup(var->xres * bpp, 32) / bpp;
+
+ if ((xpos + var->xres > base_var->xres) ||
+ (ypos + var->yres > base_var->yres))
+ return -EINVAL;
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = max(var->yres, var->yres_virtual);
+ return 0;
+}
+
+static int overlayfb_map_video_memory(struct pxafb_layer *ofb)
+{
+ struct fb_var_screeninfo *var = &ofb->fb.var;
+ int pfor = NONSTD_TO_PFOR(var->nonstd);
+ int size, bpp = 0;
+
+ switch (pfor) {
+ case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break;
+ case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break;
+ case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break;
+ case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break;
+ }
+
+ ofb->fb.fix.line_length = var->xres_virtual * bpp / 8;
+
+ size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual);
+
+ /* don't re-allocate if the original video memory is enough */
+ if (ofb->video_mem) {
+ if (ofb->video_mem_size >= size)
+ return 0;
+
+ free_pages_exact(ofb->video_mem, ofb->video_mem_size);
+ }
+
+ ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (ofb->video_mem == NULL)
+ return -ENOMEM;
+
+ ofb->video_mem_phys = virt_to_phys(ofb->video_mem);
+ ofb->video_mem_size = size;
+
+ ofb->fb.fix.smem_start = ofb->video_mem_phys;
+ ofb->fb.fix.smem_len = ofb->fb.fix.line_length * var->yres_virtual;
+ ofb->fb.screen_base = ofb->video_mem;
+ return 0;
+}
+
+static int overlayfb_set_par(struct fb_info *info)
+{
+ struct pxafb_layer *ofb = (struct pxafb_layer *)info;
+ struct fb_var_screeninfo *var = &info->var;
+ int xpos, ypos, pfor, bpp, ret;
+
+ ret = overlayfb_map_video_memory(ofb);
+ if (ret)
+ return ret;
+
+ bpp = pxafb_var_to_bpp(var);
+ xpos = NONSTD_TO_XPOS(var->nonstd);
+ ypos = NONSTD_TO_XPOS(var->nonstd);
+ pfor = NONSTD_TO_PFOR(var->nonstd);
+
+ ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) |
+ OVLxC1_BPP(bpp);
+ ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos);
+
+ if (ofb->id == OVERLAY2)
+ ofb->control[1] |= OVL2C2_PFOR(pfor);
+
+ ofb->ops->setup(ofb);
+ ofb->ops->enable(ofb);
+ return 0;
+}
+
+static struct fb_ops overlay_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = overlayfb_open,
+ .fb_release = overlayfb_release,
+ .fb_check_var = overlayfb_check_var,
+ .fb_set_par = overlayfb_set_par,
+};
+
+static void __devinit init_pxafb_overlay(struct pxafb_info *fbi,
+ struct pxafb_layer *ofb, int id)
+{
+ sprintf(ofb->fb.fix.id, "overlay%d", id + 1);
+
+ ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ ofb->fb.fix.xpanstep = 0;
+ ofb->fb.fix.ypanstep = 1;
+
+ ofb->fb.var.activate = FB_ACTIVATE_NOW;
+ ofb->fb.var.height = -1;
+ ofb->fb.var.width = -1;
+ ofb->fb.var.vmode = FB_VMODE_NONINTERLACED;
+
+ ofb->fb.fbops = &overlay_fb_ops;
+ ofb->fb.flags = FBINFO_FLAG_DEFAULT;
+ ofb->fb.node = -1;
+ ofb->fb.pseudo_palette = NULL;
+
+ ofb->id = id;
+ ofb->ops = &ofb_ops[id];
+ atomic_set(&ofb->usage, 0);
+ ofb->fbi = fbi;
+ init_completion(&ofb->branch_done);
+}
+
+static int __devinit pxafb_overlay_init(struct pxafb_info *fbi)
+{
+ int i, ret;
+
+ for (i = 0; i < 2; i++) {
+ init_pxafb_overlay(fbi, &fbi->overlay[i], i);
+ ret = register_framebuffer(&fbi->overlay[i].fb);
+ if (ret) {
+ dev_err(fbi->dev, "failed to register overlay %d\n", i);
+ return ret;
+ }
+ }
+
+ /* mask all IU/BS/EOF/SOF interrupts */
+ lcd_writel(fbi, LCCR5, ~0);
+
+ /* place overlay(s) on top of base */
+ fbi->lccr0 |= LCCR0_OUC;
+ pr_info("PXA Overlay driver loaded successfully!\n");
+ return 0;
+}
+
+static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ unregister_framebuffer(&fbi->overlay[i].fb);
+}
+#else
+static inline void pxafb_overlay_init(struct pxafb_info *fbi) {}
+static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {}
+#endif /* CONFIG_FB_PXA_OVERLAY */
+
/*
* Calculate the PCD value from the clock rate (in picoseconds).
* We take account of the PPCR clock setting.
@@ -607,22 +996,22 @@ unsigned long pxafb_get_hsync_time(struct device *dev)
EXPORT_SYMBOL(pxafb_get_hsync_time);
static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
- unsigned int offset, size_t size)
+ unsigned long start, size_t size)
{
struct pxafb_dma_descriptor *dma_desc, *pal_desc;
unsigned int dma_desc_off, pal_desc_off;
- if (dma < 0 || dma >= DMA_MAX)
+ if (dma < 0 || dma >= DMA_MAX * 2)
return -EINVAL;
dma_desc = &fbi->dma_buff->dma_desc[dma];
dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
- dma_desc->fsadr = fbi->screen_dma + offset;
+ dma_desc->fsadr = start;
dma_desc->fidr = 0;
dma_desc->ldcmd = size;
- if (pal < 0 || pal >= PAL_MAX) {
+ if (pal < 0 || pal >= PAL_MAX * 2) {
dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
} else {
@@ -648,6 +1037,27 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
return 0;
}
+static void setup_base_frame(struct pxafb_info *fbi, int branch)
+{
+ struct fb_var_screeninfo *var = &fbi->fb.var;
+ struct fb_fix_screeninfo *fix = &fbi->fb.fix;
+ int nbytes, dma, pal, bpp = var->bits_per_pixel;
+ unsigned long offset;
+
+ dma = DMA_BASE + (branch ? DMA_MAX : 0);
+ pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0);
+
+ nbytes = fix->line_length * var->yres;
+ offset = fix->line_length * var->yoffset + fbi->video_mem_phys;
+
+ if (fbi->lccr0 & LCCR0_SDS) {
+ nbytes = nbytes / 2;
+ setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes);
+ }
+
+ setup_frame_dma(fbi, dma, pal, offset, nbytes);
+}
+
#ifdef CONFIG_FB_PXA_SMARTPANEL
static int setup_smart_dma(struct pxafb_info *fbi)
{
@@ -701,6 +1111,7 @@ int pxafb_smart_flush(struct fb_info *info)
lcd_writel(fbi, LCCR1, fbi->reg_lccr1);
lcd_writel(fbi, LCCR2, fbi->reg_lccr2);
lcd_writel(fbi, LCCR3, fbi->reg_lccr3);
+ lcd_writel(fbi, LCCR4, fbi->reg_lccr4);
lcd_writel(fbi, FDADR0, fbi->fdadr[0]);
lcd_writel(fbi, FDADR6, fbi->fdadr[6]);
@@ -727,12 +1138,19 @@ int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
int i;
struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
- /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */
- for (i = 0; i < n_cmds; i++) {
+ for (i = 0; i < n_cmds; i++, cmds++) {
+ /* if it is a software delay, flush and delay */
+ if ((*cmds & 0xff00) == SMART_CMD_DELAY) {
+ pxafb_smart_flush(info);
+ mdelay(*cmds & 0xff);
+ continue;
+ }
+
+ /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */
if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8)
pxafb_smart_flush(info);
- fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds++;
+ fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds;
}
return 0;
@@ -764,7 +1182,9 @@ static void setup_smart_timing(struct pxafb_info *fbi,
LCCR1_HorSnchWdth(__smart_timing(t3, lclk));
fbi->reg_lccr2 = LCCR2_DisHght(var->yres);
- fbi->reg_lccr3 = LCCR3_PixClkDiv(__smart_timing(t4, lclk));
+ fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk));
+ fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0;
+ fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0;
/* FIXME: make this configurable */
fbi->reg_cmdcr = 1;
@@ -789,11 +1209,15 @@ static int pxafb_smart_thread(void *arg)
if (try_to_freeze())
continue;
+ mutex_lock(&fbi->ctrlr_lock);
+
if (fbi->state == C_ENABLE) {
inf->smart_update(&fbi->fb);
complete(&fbi->refresh_done);
}
+ mutex_unlock(&fbi->ctrlr_lock);
+
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(30 * HZ / 1000);
}
@@ -804,16 +1228,22 @@ static int pxafb_smart_thread(void *arg)
static int pxafb_smart_init(struct pxafb_info *fbi)
{
- if (!(fbi->lccr0 | LCCR0_LCDT))
+ if (!(fbi->lccr0 & LCCR0_LCDT))
return 0;
+ fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
+ fbi->n_smart_cmds = 0;
+
+ init_completion(&fbi->command_done);
+ init_completion(&fbi->refresh_done);
+
fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi,
"lcd_refresh");
if (IS_ERR(fbi->smart_thread)) {
- printk(KERN_ERR "%s: unable to create kernel thread\n",
- __func__);
+ pr_err("%s: unable to create kernel thread\n", __func__);
return PTR_ERR(fbi->smart_thread);
}
+
return 0;
}
#else
@@ -826,7 +1256,9 @@ int pxafb_smart_flush(struct fb_info *info)
{
return 0;
}
-#endif /* CONFIG_FB_SMART_PANEL */
+
+static inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; }
+#endif /* CONFIG_FB_PXA_SMARTPANEL */
static void setup_parallel_timing(struct pxafb_info *fbi,
struct fb_var_screeninfo *var)
@@ -874,51 +1306,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
struct pxafb_info *fbi)
{
u_long flags;
- size_t nbytes;
-
-#if DEBUG_VAR
- if (!(fbi->lccr0 & LCCR0_LCDT)) {
- if (var->xres < 16 || var->xres > 1024)
- printk(KERN_ERR "%s: invalid xres %d\n",
- fbi->fb.fix.id, var->xres);
- switch (var->bits_per_pixel) {
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- case 24:
- case 32:
- break;
- default:
- printk(KERN_ERR "%s: invalid bit depth %d\n",
- fbi->fb.fix.id, var->bits_per_pixel);
- break;
- }
- if (var->hsync_len < 1 || var->hsync_len > 64)
- printk(KERN_ERR "%s: invalid hsync_len %d\n",
- fbi->fb.fix.id, var->hsync_len);
- if (var->left_margin < 1 || var->left_margin > 255)
- printk(KERN_ERR "%s: invalid left_margin %d\n",
- fbi->fb.fix.id, var->left_margin);
- if (var->right_margin < 1 || var->right_margin > 255)
- printk(KERN_ERR "%s: invalid right_margin %d\n",
- fbi->fb.fix.id, var->right_margin);
- if (var->yres < 1 || var->yres > 1024)
- printk(KERN_ERR "%s: invalid yres %d\n",
- fbi->fb.fix.id, var->yres);
- if (var->vsync_len < 1 || var->vsync_len > 64)
- printk(KERN_ERR "%s: invalid vsync_len %d\n",
- fbi->fb.fix.id, var->vsync_len);
- if (var->upper_margin < 0 || var->upper_margin > 255)
- printk(KERN_ERR "%s: invalid upper_margin %d\n",
- fbi->fb.fix.id, var->upper_margin);
- if (var->lower_margin < 0 || var->lower_margin > 255)
- printk(KERN_ERR "%s: invalid lower_margin %d\n",
- fbi->fb.fix.id, var->lower_margin);
- }
-#endif
/* Update shadow copy atomically */
local_irq_save(flags);
@@ -929,23 +1317,13 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
#endif
setup_parallel_timing(fbi, var);
+ setup_base_frame(fbi, 0);
+
fbi->reg_lccr0 = fbi->lccr0 |
(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
LCCR0_QDM | LCCR0_BM | LCCR0_OUM);
- fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var);
-
- nbytes = var->yres * fbi->fb.fix.line_length;
-
- if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) {
- nbytes = nbytes / 2;
- setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, nbytes, nbytes);
- }
-
- if ((var->bits_per_pixel >= 16) || (fbi->lccr0 & LCCR0_LCDT))
- setup_frame_dma(fbi, DMA_BASE, PAL_NONE, 0, nbytes);
- else
- setup_frame_dma(fbi, DMA_BASE, PAL_BASE, 0, nbytes);
+ fbi->reg_lccr3 |= pxafb_var_to_lccr3(var);
fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK;
fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK);
@@ -959,6 +1337,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
(lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) ||
(lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) ||
(lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||
+ (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||
(lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||
(lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))
pxafb_schedule_work(fbi, C_REENABLE);
@@ -976,67 +1355,16 @@ static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff");
- if (pxafb_backlight_power)
- pxafb_backlight_power(on);
+ if (fbi->backlight_power)
+ fbi->backlight_power(on);
}
static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on)
{
pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff");
- if (pxafb_lcd_power)
- pxafb_lcd_power(on, &fbi->fb.var);
-}
-
-static void pxafb_setup_gpio(struct pxafb_info *fbi)
-{
- int gpio, ldd_bits;
- unsigned int lccr0 = fbi->lccr0;
-
- /*
- * setup is based on type of panel supported
- */
-
- /* 4 bit interface */
- if ((lccr0 & LCCR0_CMS) == LCCR0_Mono &&
- (lccr0 & LCCR0_SDS) == LCCR0_Sngl &&
- (lccr0 & LCCR0_DPD) == LCCR0_4PixMono)
- ldd_bits = 4;
-
- /* 8 bit interface */
- else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono &&
- ((lccr0 & LCCR0_SDS) == LCCR0_Dual ||
- (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) ||
- ((lccr0 & LCCR0_CMS) == LCCR0_Color &&
- (lccr0 & LCCR0_PAS) == LCCR0_Pas &&
- (lccr0 & LCCR0_SDS) == LCCR0_Sngl))
- ldd_bits = 8;
-
- /* 16 bit interface */
- else if ((lccr0 & LCCR0_CMS) == LCCR0_Color &&
- ((lccr0 & LCCR0_SDS) == LCCR0_Dual ||
- (lccr0 & LCCR0_PAS) == LCCR0_Act))
- ldd_bits = 16;
-
- else {
- printk(KERN_ERR "pxafb_setup_gpio: unable to determine "
- "bits per pixel\n");
- return;
- }
-
- for (gpio = 58; ldd_bits; gpio++, ldd_bits--)
- pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT);
- /* 18 bit interface */
- if (fbi->fb.var.bits_per_pixel > 16) {
- pxa_gpio_mode(86 | GPIO_ALT_FN_2_OUT);
- pxa_gpio_mode(87 | GPIO_ALT_FN_2_OUT);
- }
- pxa_gpio_mode(GPIO74_LCD_FCLK_MD);
- pxa_gpio_mode(GPIO75_LCD_LCLK_MD);
- pxa_gpio_mode(GPIO76_LCD_PCLK_MD);
-
- if ((lccr0 & LCCR0_PAS) == 0)
- pxa_gpio_mode(GPIO77_LCD_ACBIAS_MD);
+ if (fbi->lcd_power)
+ fbi->lcd_power(on, &fbi->fb.var);
}
static void pxafb_enable_controller(struct pxafb_info *fbi)
@@ -1056,6 +1384,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
return;
/* Sequence from 11.7.10 */
+ lcd_writel(fbi, LCCR4, fbi->reg_lccr4);
lcd_writel(fbi, LCCR3, fbi->reg_lccr3);
lcd_writel(fbi, LCCR2, fbi->reg_lccr2);
lcd_writel(fbi, LCCR1, fbi->reg_lccr1);
@@ -1097,8 +1426,9 @@ static void pxafb_disable_controller(struct pxafb_info *fbi)
static irqreturn_t pxafb_handle_irq(int irq, void *dev_id)
{
struct pxafb_info *fbi = dev_id;
- unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR);
+ unsigned int lccr0, lcsr, lcsr1;
+ lcsr = lcd_readl(fbi, LCSR);
if (lcsr & LCSR_LDD) {
lccr0 = lcd_readl(fbi, LCCR0);
lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM);
@@ -1109,8 +1439,18 @@ static irqreturn_t pxafb_handle_irq(int irq, void *dev_id)
if (lcsr & LCSR_CMD_INT)
complete(&fbi->command_done);
#endif
-
lcd_writel(fbi, LCSR, lcsr);
+
+#ifdef CONFIG_FB_PXA_OVERLAY
+ lcsr1 = lcd_readl(fbi, LCSR1);
+ if (lcsr1 & LCSR1_BS(1))
+ complete(&fbi->overlay[0].branch_done);
+
+ if (lcsr1 & LCSR1_BS(2))
+ complete(&fbi->overlay[1].branch_done);
+
+ lcd_writel(fbi, LCSR1, lcsr1);
+#endif
return IRQ_HANDLED;
}
@@ -1181,7 +1521,6 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
if (old_state == C_ENABLE) {
__pxafb_lcd_power(fbi, 0);
pxafb_disable_controller(fbi);
- pxafb_setup_gpio(fbi);
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
}
@@ -1204,7 +1543,6 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
*/
if (old_state != C_ENABLE) {
fbi->state = C_ENABLE;
- pxafb_setup_gpio(fbi);
pxafb_enable_controller(fbi);
__pxafb_lcd_power(fbi, 1);
__pxafb_backlight_power(fbi, 1);
@@ -1303,77 +1641,34 @@ static int pxafb_resume(struct platform_device *dev)
#define pxafb_resume NULL
#endif
-/*
- * pxafb_map_video_memory():
- * Allocates the DRAM memory for the frame buffer. This buffer is
- * remapped into a non-cached, non-buffered, memory region to
- * allow palette and pixel writes to occur without flushing the
- * cache. Once this area is remapped, all virtual memory
- * access to the video memory should occur at the new region.
- */
-static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi)
+static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi)
{
- /*
- * We reserve one page for the palette, plus the size
- * of the framebuffer.
- */
- fbi->video_offset = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
- fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + fbi->video_offset);
- fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
- &fbi->map_dma, GFP_KERNEL);
-
- if (fbi->map_cpu) {
- /* prevent initial garbage on screen */
- memset(fbi->map_cpu, 0, fbi->map_size);
- fbi->fb.screen_base = fbi->map_cpu + fbi->video_offset;
- fbi->screen_dma = fbi->map_dma + fbi->video_offset;
-
- /*
- * FIXME: this is actually the wrong thing to place in
- * smem_start. But fbdev suffers from the problem that
- * it needs an API which doesn't exist (in this case,
- * dma_writecombine_mmap)
- */
- fbi->fb.fix.smem_start = fbi->screen_dma;
- fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;
-
- fbi->dma_buff = (void *) fbi->map_cpu;
- fbi->dma_buff_phys = fbi->map_dma;
- fbi->palette_cpu = (u16 *) fbi->dma_buff->palette;
+ int size = PAGE_ALIGN(fbi->video_mem_size);
- pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16));
+ fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (fbi->video_mem == NULL)
+ return -ENOMEM;
-#ifdef CONFIG_FB_PXA_SMARTPANEL
- fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
- fbi->n_smart_cmds = 0;
-#endif
- }
-
- return fbi->map_cpu ? 0 : -ENOMEM;
-}
+ fbi->video_mem_phys = virt_to_phys(fbi->video_mem);
+ fbi->video_mem_size = size;
-static void pxafb_decode_mode_info(struct pxafb_info *fbi,
- struct pxafb_mode_info *modes,
- unsigned int num_modes)
-{
- unsigned int i, smemlen;
-
- pxafb_setmode(&fbi->fb.var, &modes[0]);
+ fbi->fb.fix.smem_start = fbi->video_mem_phys;
+ fbi->fb.fix.smem_len = fbi->video_mem_size;
+ fbi->fb.screen_base = fbi->video_mem;
- for (i = 0; i < num_modes; i++) {
- smemlen = modes[i].xres * modes[i].yres * modes[i].bpp / 8;
- if (smemlen > fbi->fb.fix.smem_len)
- fbi->fb.fix.smem_len = smemlen;
- }
+ return fbi->video_mem ? 0 : -ENOMEM;
}
static void pxafb_decode_mach_info(struct pxafb_info *fbi,
struct pxafb_mach_info *inf)
{
unsigned int lcd_conn = inf->lcd_conn;
+ struct pxafb_mode_info *m;
+ int i;
fbi->cmap_inverse = inf->cmap_inverse;
fbi->cmap_static = inf->cmap_static;
+ fbi->lccr4 = inf->lccr4;
switch (lcd_conn & LCD_TYPE_MASK) {
case LCD_TYPE_MONO_STN:
@@ -1398,7 +1693,6 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi,
/* fall back to backward compatibility way */
fbi->lccr0 = inf->lccr0;
fbi->lccr3 = inf->lccr3;
- fbi->lccr4 = inf->lccr4;
goto decode_mode;
}
@@ -1412,7 +1706,22 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi,
fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0;
decode_mode:
- pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes);
+ pxafb_setmode(&fbi->fb.var, &inf->modes[0]);
+
+ /* decide video memory size as follows:
+ * 1. default to mode of maximum resolution
+ * 2. allow platform to override
+ * 3. allow module parameter to override
+ */
+ for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++)
+ fbi->video_mem_size = max_t(size_t, fbi->video_mem_size,
+ m->xres * m->yres * m->bpp / 8);
+
+ if (inf->video_mem_size > fbi->video_mem_size)
+ fbi->video_mem_size = inf->video_mem_size;
+
+ if (video_mem_size > fbi->video_mem_size)
+ fbi->video_mem_size = video_mem_size;
}
static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
@@ -1429,7 +1738,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
memset(fbi, 0, sizeof(struct pxafb_info));
fbi->dev = dev;
- fbi->clk = clk_get(dev, "LCDCLK");
+ fbi->clk = clk_get(dev, NULL);
if (IS_ERR(fbi->clk)) {
kfree(fbi);
return NULL;
@@ -1440,7 +1749,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fbi->fb.fix.type_aux = 0;
fbi->fb.fix.xpanstep = 0;
- fbi->fb.fix.ypanstep = 0;
+ fbi->fb.fix.ypanstep = 1;
fbi->fb.fix.ywrapstep = 0;
fbi->fb.fix.accel = FB_ACCEL_NONE;
@@ -1448,7 +1757,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
- fbi->fb.var.accel_flags = 0;
+ fbi->fb.var.accel_flags = FB_ACCELF_TEXT;
fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
fbi->fb.fbops = &pxafb_ops;
@@ -1468,10 +1777,6 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
INIT_WORK(&fbi->task, pxafb_task);
mutex_init(&fbi->ctrlr_lock);
init_completion(&fbi->disable_done);
-#ifdef CONFIG_FB_PXA_SMARTPANEL
- init_completion(&fbi->command_done);
- init_completion(&fbi->refresh_done);
-#endif
return fbi;
}
@@ -1544,7 +1849,9 @@ static int __devinit parse_opt(struct device *dev, char *this_opt)
s[0] = '\0';
- if (!strncmp(this_opt, "mode:", 5)) {
+ if (!strncmp(this_opt, "vmem:", 5)) {
+ video_mem_size = memparse(this_opt + 5, NULL);
+ } else if (!strncmp(this_opt, "mode:", 5)) {
return parse_opt_mode(dev, this_opt);
} else if (!strncmp(this_opt, "pixclock:", 9)) {
mode->pixclock = simple_strtoul(this_opt+9, NULL, 0);
@@ -1748,8 +2055,7 @@ static int __devinit pxafb_probe(struct platform_device *dev)
ret = -EINVAL;
goto failed;
}
- pxafb_backlight_power = inf->pxafb_backlight_power;
- pxafb_lcd_power = inf->pxafb_lcd_power;
+
fbi = pxafb_init_fbinfo(&dev->dev);
if (!fbi) {
/* only reason for pxafb_init_fbinfo to fail is kmalloc */
@@ -1758,6 +2064,9 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed;
}
+ fbi->backlight_power = inf->pxafb_backlight_power;
+ fbi->lcd_power = inf->pxafb_lcd_power;
+
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&dev->dev, "no I/O memory resource defined\n");
@@ -1779,12 +2088,20 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_res;
}
- /* Initialize video memory */
- ret = pxafb_map_video_memory(fbi);
+ fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff));
+ fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size,
+ &fbi->dma_buff_phys, GFP_KERNEL);
+ if (fbi->dma_buff == NULL) {
+ dev_err(&dev->dev, "failed to allocate memory for DMA\n");
+ ret = -ENOMEM;
+ goto failed_free_io;
+ }
+
+ ret = pxafb_init_video_memory(fbi);
if (ret) {
dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
- goto failed_free_io;
+ goto failed_free_dma;
}
irq = platform_get_irq(dev, 0);
@@ -1801,13 +2118,12 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_mem;
}
-#ifdef CONFIG_FB_PXA_SMARTPANEL
ret = pxafb_smart_init(fbi);
if (ret) {
dev_err(&dev->dev, "failed to initialize smartpanel\n");
goto failed_free_irq;
}
-#endif
+
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
@@ -1833,6 +2149,8 @@ static int __devinit pxafb_probe(struct platform_device *dev)
goto failed_free_cmap;
}
+ pxafb_overlay_init(fbi);
+
#ifdef CONFIG_CPU_FREQ
fbi->freq_transition.notifier_call = pxafb_freq_transition;
fbi->freq_policy.notifier_call = pxafb_freq_policy;
@@ -1855,8 +2173,10 @@ failed_free_cmap:
failed_free_irq:
free_irq(irq, fbi);
failed_free_mem:
- dma_free_writecombine(&dev->dev, fbi->map_size,
- fbi->map_cpu, fbi->map_dma);
+ free_pages_exact(fbi->video_mem, fbi->video_mem_size);
+failed_free_dma:
+ dma_free_coherent(&dev->dev, fbi->dma_buff_size,
+ fbi->dma_buff, fbi->dma_buff_phys);
failed_free_io:
iounmap(fbi->mmio_base);
failed_free_res:
@@ -1881,6 +2201,7 @@ static int __devexit pxafb_remove(struct platform_device *dev)
info = &fbi->fb;
+ pxafb_overlay_exit(fbi);
unregister_framebuffer(info);
pxafb_disable_controller(fbi);
@@ -1891,8 +2212,10 @@ static int __devexit pxafb_remove(struct platform_device *dev)
irq = platform_get_irq(dev, 0);
free_irq(irq, fbi);
- dma_free_writecombine(&dev->dev, fbi->map_size,
- fbi->map_cpu, fbi->map_dma);
+ free_pages_exact(fbi->video_mem, fbi->video_mem_size);
+
+ dma_free_writecombine(&dev->dev, fbi->dma_buff_size,
+ fbi->dma_buff, fbi->dma_buff_phys);
iounmap(fbi->mmio_base);
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
index 31541b86f13d..2353521c5c8c 100644
--- a/drivers/video/pxafb.h
+++ b/drivers/video/pxafb.h
@@ -54,11 +54,55 @@ enum {
#define PALETTE_SIZE (256 * 4)
#define CMD_BUFF_SIZE (1024 * 50)
+/* NOTE: the palette and frame dma descriptors are doubled to allow
+ * the 2nd set for branch settings (FBRx)
+ */
struct pxafb_dma_buff {
unsigned char palette[PAL_MAX * PALETTE_SIZE];
uint16_t cmd_buff[CMD_BUFF_SIZE];
- struct pxafb_dma_descriptor pal_desc[PAL_MAX];
- struct pxafb_dma_descriptor dma_desc[DMA_MAX];
+ struct pxafb_dma_descriptor pal_desc[PAL_MAX * 2];
+ struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2];
+};
+
+enum {
+ OVERLAY1,
+ OVERLAY2,
+};
+
+enum {
+ OVERLAY_FORMAT_RGB = 0,
+ OVERLAY_FORMAT_YUV444_PACKED,
+ OVERLAY_FORMAT_YUV444_PLANAR,
+ OVERLAY_FORMAT_YUV422_PLANAR,
+ OVERLAY_FORMAT_YUV420_PLANAR,
+};
+
+#define NONSTD_TO_XPOS(x) (((x) >> 0) & 0x3ff)
+#define NONSTD_TO_YPOS(x) (((x) >> 10) & 0x3ff)
+#define NONSTD_TO_PFOR(x) (((x) >> 20) & 0x7)
+
+struct pxafb_layer;
+
+struct pxafb_layer_ops {
+ void (*enable)(struct pxafb_layer *);
+ void (*disable)(struct pxafb_layer *);
+ void (*setup)(struct pxafb_layer *);
+};
+
+struct pxafb_layer {
+ struct fb_info fb;
+ int id;
+ atomic_t usage;
+ uint32_t control[2];
+
+ struct pxafb_layer_ops *ops;
+
+ void __iomem *video_mem;
+ unsigned long video_mem_phys;
+ size_t video_mem_size;
+ struct completion branch_done;
+
+ struct pxafb_info *fbi;
};
struct pxafb_info {
@@ -69,24 +113,15 @@ struct pxafb_info {
void __iomem *mmio_base;
struct pxafb_dma_buff *dma_buff;
+ size_t dma_buff_size;
dma_addr_t dma_buff_phys;
- dma_addr_t fdadr[DMA_MAX];
-
- /*
- * These are the addresses we mapped
- * the framebuffer memory region to.
- */
- /* raw memory addresses */
- dma_addr_t map_dma; /* physical */
- u_char * map_cpu; /* virtual */
- u_int map_size;
-
- /* addresses of pieces placed in raw buffer */
- u_char * screen_cpu; /* virtual address of frame buffer */
- dma_addr_t screen_dma; /* physical address of frame buffer */
+ dma_addr_t fdadr[DMA_MAX * 2];
+
+ void __iomem *video_mem; /* virtual address of frame buffer */
+ unsigned long video_mem_phys; /* physical address of frame buffer */
+ size_t video_mem_size; /* size of the frame buffer */
u16 * palette_cpu; /* virtual address of palette memory */
u_int palette_size;
- ssize_t video_offset;
u_int lccr0;
u_int lccr3;
@@ -120,10 +155,17 @@ struct pxafb_info {
struct task_struct *smart_thread;
#endif
+#ifdef CONFIG_FB_PXA_OVERLAY
+ struct pxafb_layer overlay[2];
+#endif
+
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
struct notifier_block freq_policy;
#endif
+
+ void (*lcd_power)(int, struct fb_var_screeninfo *);
+ void (*backlight_power)(int);
};
#define TO_INF(ptr,member) container_of(ptr,struct pxafb_info,member)
@@ -148,4 +190,10 @@ struct pxafb_info {
#define MIN_XRES 64
#define MIN_YRES 64
+/* maximum X and Y resolutions - note these are limits from the register
+ * bits length instead of the real ones
+ */
+#define MAX_XRES 1024
+#define MAX_YRES 1024
+
#endif /* __PXAFB_H__ */
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index c052bd4c0b06..076f946fa0f5 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -114,7 +114,7 @@
* - convert dma address types to dma_addr_t
* - remove unused 'montype' stuff
* - remove redundant zero inits of init_var after the initial
- * memzero.
+ * memset.
* - remove allow_modeset (acornfb idea does not belong here)
*
* 2001/05/28: <rmk@arm.linux.org.uk>
diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c
index 8d0212da4514..653bdfee3057 100644
--- a/drivers/video/sh7760fb.c
+++ b/drivers/video/sh7760fb.c
@@ -13,6 +13,8 @@
*
* Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
* for his original source and testing!
+ *
+ * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
*/
#include <linux/completion.h>
@@ -53,29 +55,6 @@ static irqreturn_t sh7760fb_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static void sh7760fb_wait_vsync(struct fb_info *info)
-{
- struct sh7760fb_par *par = info->par;
-
- if (par->pd->novsync)
- return;
-
- iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK,
- par->base + LDINTR);
-
- if (par->irq < 0) {
- /* poll for vert. retrace: status bit is sticky */
- while (!(ioread16(par->base + LDINTR) & VINT_CHECK))
- cpu_relax();
- } else {
- /* a "wait_for_irq_event(par->irq)" would be extremely nice */
- init_completion(&par->vsync);
- enable_irq(par->irq);
- wait_for_completion(&par->vsync);
- disable_irq_nosync(par->irq);
- }
-}
-
/* wait_for_lps - wait until power supply has reached a certain state. */
static int wait_for_lps(struct sh7760fb_par *par, int val)
{
@@ -117,55 +96,28 @@ static int sh7760fb_blank(int blank, struct fb_info *info)
return wait_for_lps(par, lps);
}
-/* set color registers */
-static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+static int sh7760_setcolreg (u_int regno,
+ u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
{
- struct sh7760fb_par *par = info->par;
- u32 s = cmap->start;
- u32 l = cmap->len;
- u16 *r = cmap->red;
- u16 *g = cmap->green;
- u16 *b = cmap->blue;
- u32 col, tmo;
- int ret;
+ u32 *palette = info->pseudo_palette;
- ret = 0;
+ if (regno >= 16)
+ return -EINVAL;
- sh7760fb_wait_vsync(info);
+ /* only FB_VISUAL_TRUECOLOR supported */
- /* request palette access */
- iowrite16(LDPALCR_PALEN, par->base + LDPALCR);
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
+ transp >>= 16 - info->var.transp.length;
- /* poll for access grant */
- tmo = 100;
- while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo))
- cpu_relax();
+ palette[regno] = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
- if (!tmo) {
- ret = 1;
- dev_dbg(info->dev, "no palette access!\n");
- goto out;
- }
-
- while (l && (s < 256)) {
- col = ((*r) & 0xff) << 16;
- col |= ((*g) & 0xff) << 8;
- col |= ((*b) & 0xff);
- col &= SH7760FB_PALETTE_MASK;
- iowrite32(col, par->base + LDPR(s));
-
- if (s < 16)
- ((u32 *) (info->pseudo_palette))[s] = s;
-
- s++;
- l--;
- r++;
- g++;
- b++;
- }
-out:
- iowrite16(0, par->base + LDPALCR);
- return ret;
+ return 0;
}
static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info,
@@ -406,7 +358,7 @@ static struct fb_ops sh7760fb_ops = {
.owner = THIS_MODULE,
.fb_blank = sh7760fb_blank,
.fb_check_var = sh7760fb_check_var,
- .fb_setcmap = sh7760fb_setcmap,
+ .fb_setcolreg = sh7760_setcolreg,
.fb_set_par = sh7760fb_set_par,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index efff672fd7b8..0e2b8fd24df1 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -16,7 +16,9 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
#include <video/sh_mobile_lcdc.h>
+#include <asm/atomic.h>
#define PALETTE_NR 16
@@ -30,11 +32,15 @@ struct sh_mobile_lcdc_chan {
u32 pseudo_palette[PALETTE_NR];
struct fb_info info;
dma_addr_t dma_handle;
+ struct fb_deferred_io defio;
};
struct sh_mobile_lcdc_priv {
void __iomem *base;
+ int irq;
#ifdef CONFIG_HAVE_CLK
+ atomic_t clk_usecnt;
+ struct clk *dot_clk;
struct clk *clk;
#endif
unsigned long lddckr;
@@ -56,7 +62,7 @@ struct sh_mobile_lcdc_priv {
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
- LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+ LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
static unsigned long lcdc_offs_mainlcd[] = {
[LDDCKPAT1R] = 0x400,
@@ -66,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
[LDMT3R] = 0x420,
[LDDFR] = 0x424,
[LDSM1R] = 0x428,
+ [LDSM2R] = 0x42c,
[LDSA1R] = 0x430,
[LDMLSR] = 0x438,
[LDHCNR] = 0x448,
@@ -83,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
[LDMT3R] = 0x608,
[LDDFR] = 0x60c,
[LDSM1R] = 0x610,
+ [LDSM2R] = 0x614,
[LDSA1R] = 0x618,
[LDMLSR] = 0x620,
[LDHCNR] = 0x624,
@@ -96,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
#define LCDC_RESET 0x00000100
#define DISPLAY_BEU 0x00000008
#define LCDC_ENABLE 0x00000001
+#define LDINTR_FE 0x00000400
+#define LDINTR_FS 0x00000004
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
int reg_nr, unsigned long data)
@@ -170,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data,
};
+#ifdef CONFIG_HAVE_CLK
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
+{
+ if (atomic_inc_and_test(&priv->clk_usecnt)) {
+ clk_enable(priv->clk);
+ if (priv->dot_clk)
+ clk_enable(priv->dot_clk);
+ }
+}
+
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
+{
+ if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
+ if (priv->dot_clk)
+ clk_disable(priv->dot_clk);
+ clk_disable(priv->clk);
+ }
+}
+#else
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
+#endif
+
+static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+
+ /* enable clocks before accessing hardware */
+ sh_mobile_lcdc_clk_on(ch->lcdc);
+
+ /* trigger panel update */
+ lcdc_write_chan(ch, LDSM2R, 1);
+}
+
+static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
+{
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ if (fbdefio)
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+}
+
+static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
+{
+ struct sh_mobile_lcdc_priv *priv = data;
+ unsigned long tmp;
+
+ /* acknowledge interrupt */
+ tmp = lcdc_read(priv, _LDINTR);
+ tmp &= 0xffffff00; /* mask in high 24 bits */
+ tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
+ lcdc_write(priv, _LDINTR, tmp);
+
+ /* disable clocks */
+ sh_mobile_lcdc_clk_off(priv);
+ return IRQ_HANDLED;
+}
+
static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
int start)
{
@@ -207,6 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
int k, m;
int ret = 0;
+ /* enable clocks before accessing the hardware */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (priv->ch[k].enabled)
+ sh_mobile_lcdc_clk_on(priv);
+
/* reset */
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
@@ -249,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_write(priv, _LDDCKSTPR, 0);
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
- /* interrupts are disabled */
+ /* interrupts are disabled to begin with */
lcdc_write(priv, _LDINTR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -310,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
return ret;
}
- /* --- display_lcdc_data() --- */
- lcdc_write(priv, _LDINTR, 0x00000f00);
-
/* word and long word swap */
lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
@@ -334,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* set line size */
lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
- /* continuous read mode */
- lcdc_write_chan(ch, LDSM1R, 0);
+ /* setup deferred io if SYS bus */
+ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ if (ch->ldmt1r_value & (1 << 12) && tmp) {
+ ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
+ ch->defio.delay = msecs_to_jiffies(tmp);
+ ch->info.fbdefio = &ch->defio;
+ fb_deferred_io_init(&ch->info);
+
+ /* one-shot mode */
+ lcdc_write_chan(ch, LDSM1R, 1);
+
+ /* enable "Frame End Interrupt Enable" bit */
+ lcdc_write(priv, _LDINTR, LDINTR_FE);
+
+ } else {
+ /* continuous read mode */
+ lcdc_write_chan(ch, LDSM1R, 0);
+ }
}
/* display output */
@@ -359,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
struct sh_mobile_lcdc_board_cfg *board_cfg;
+ unsigned long tmp;
int k;
/* tell the board code to disable the panel */
@@ -367,10 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_off)
board_cfg->display_off(board_cfg->board_data);
+
+ /* cleanup deferred io if SYS bus */
+ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ if (ch->ldmt1r_value & (1 << 12) && tmp) {
+ fb_deferred_io_cleanup(&ch->info);
+ ch->info.fbdefio = NULL;
+ }
}
/* stop the lcdc */
sh_mobile_lcdc_start_stop(priv, 0);
+
+ /* stop clocks */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (priv->ch[k].enabled)
+ sh_mobile_lcdc_clk_off(priv);
}
static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
@@ -413,9 +513,13 @@ static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
return -EINVAL;
}
-static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
+static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
+ int clock_source,
struct sh_mobile_lcdc_priv *priv)
{
+#ifdef CONFIG_HAVE_CLK
+ char clk_name[8];
+#endif
char *str;
int icksel;
@@ -430,14 +534,21 @@ static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
priv->lddckr = icksel << 16;
#ifdef CONFIG_HAVE_CLK
+ atomic_set(&priv->clk_usecnt, -1);
+ snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
+ priv->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ return PTR_ERR(priv->clk);
+ }
+
if (str) {
- priv->clk = clk_get(dev, str);
- if (IS_ERR(priv->clk)) {
- dev_err(dev, "cannot get clock %s\n", str);
- return PTR_ERR(priv->clk);
+ priv->dot_clk = clk_get(&pdev->dev, str);
+ if (IS_ERR(priv->dot_clk)) {
+ dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
+ clk_put(priv->clk);
+ return PTR_ERR(priv->dot_clk);
}
-
- clk_enable(priv->clk);
}
#endif
@@ -475,11 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.accel = FB_ACCEL_NONE,
};
+static void sh_mobile_lcdc_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ sys_fillrect(info, rect);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ sys_copyarea(info, area);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ sys_imageblit(info, image);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
static struct fb_ops sh_mobile_lcdc_ops = {
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sh_mobile_lcdc_fillrect,
+ .fb_copyarea = sh_mobile_lcdc_copyarea,
+ .fb_imageblit = sh_mobile_lcdc_imageblit,
};
static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -540,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
+ i = platform_get_irq(pdev, 0);
+ if (!res || i < 0) {
+ dev_err(&pdev->dev, "cannot get platform resources\n");
error = -ENOENT;
goto err0;
}
@@ -553,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err0;
}
+ error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
+ pdev->dev.bus_id, priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to request irq\n");
+ goto err1;
+ }
+
+ priv->irq = i;
platform_set_drvdata(pdev, priv);
pdata = pdev->dev.platform_data;
@@ -587,8 +730,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
- error = sh_mobile_lcdc_setup_clocks(&pdev->dev,
- pdata->clock_source, priv);
+ error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
@@ -637,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
info->fix.smem_start = priv->ch[i].dma_handle;
info->screen_base = buf;
info->device = &pdev->dev;
+ info->par = &priv->ch[i];
}
if (error)
@@ -664,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
(int) priv->ch[i].cfg.lcd_cfg.xres,
(int) priv->ch[i].cfg.lcd_cfg.yres,
priv->ch[i].cfg.bpp);
+
+ /* deferred io mode: disable clock to save power */
+ if (info->fbdefio)
+ sh_mobile_lcdc_clk_off(priv);
}
return 0;
@@ -697,15 +844,16 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_HAVE_CLK
- if (priv->clk) {
- clk_disable(priv->clk);
- clk_put(priv->clk);
- }
+ if (priv->dot_clk)
+ clk_put(priv->dot_clk);
+ clk_put(priv->clk);
#endif
if (priv->base)
iounmap(priv->base);
+ if (priv->irq)
+ free_irq(priv->irq, priv);
kfree(priv);
return 0;
}
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 5b78fd0aff0a..018c070a357f 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -176,7 +176,7 @@ int register_virtio_device(struct virtio_device *dev)
/* Assign a unique device index and hence name. */
dev->index = dev_index++;
- sprintf(dev->dev.bus_id, "virtio%u", dev->index);
+ dev_set_name(&dev->dev, "virtio%u", dev->index);
/* We always start by resetting the device, in case a previous
* driver messed it up. This also tests that code path a little. */
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 62eab43152d2..59268266b79a 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -56,6 +56,15 @@ static struct virtio_device_id id_table[] = {
{ 0 },
};
+static u32 page_to_balloon_pfn(struct page *page)
+{
+ unsigned long pfn = page_to_pfn(page);
+
+ BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT);
+ /* Convert pfn from Linux page size to balloon page size. */
+ return pfn >> (PAGE_SHIFT - VIRTIO_BALLOON_PFN_SHIFT);
+}
+
static void balloon_ack(struct virtqueue *vq)
{
struct virtio_balloon *vb;
@@ -99,7 +108,7 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
msleep(200);
break;
}
- vb->pfns[vb->num_pfns] = page_to_pfn(page);
+ vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page);
totalram_pages--;
vb->num_pages++;
list_add(&page->lru, &vb->pages);
@@ -132,7 +141,7 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num)
for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) {
page = list_first_entry(&vb->pages, struct page, lru);
list_del(&page->lru);
- vb->pfns[vb->num_pfns] = page_to_pfn(page);
+ vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page);
vb->num_pages--;
}
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index c7dc37c7cce9..265fdf2d1276 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -75,7 +75,7 @@ MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
* would make more sense for virtio to not insist on having it's own device. */
static struct device virtio_pci_root = {
.parent = NULL,
- .bus_id = "virtio-pci",
+ .init_name = "virtio-pci",
};
/* Convert a generic virtio device to our structure */
@@ -216,7 +216,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
struct virtio_pci_vq_info *info;
struct virtqueue *vq;
- unsigned long flags;
+ unsigned long flags, size;
u16 num;
int err;
@@ -237,19 +237,20 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
info->queue_index = index;
info->num = num;
- info->queue = kzalloc(PAGE_ALIGN(vring_size(num,PAGE_SIZE)), GFP_KERNEL);
+ size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
+ info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
if (info->queue == NULL) {
err = -ENOMEM;
goto out_info;
}
/* activate the queue */
- iowrite32(virt_to_phys(info->queue) >> PAGE_SHIFT,
+ iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
/* create the vring */
- vq = vring_new_virtqueue(info->num, vdev, info->queue,
- vp_notify, callback);
+ vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
+ vdev, info->queue, vp_notify, callback);
if (!vq) {
err = -ENOMEM;
goto out_activate_queue;
@@ -266,7 +267,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
out_activate_queue:
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kfree(info->queue);
+ free_pages_exact(info->queue, size);
out_info:
kfree(info);
return ERR_PTR(err);
@@ -277,7 +278,7 @@ static void vp_del_vq(struct virtqueue *vq)
{
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
struct virtio_pci_vq_info *info = vq->priv;
- unsigned long flags;
+ unsigned long flags, size;
spin_lock_irqsave(&vp_dev->lock, flags);
list_del(&info->node);
@@ -289,7 +290,8 @@ static void vp_del_vq(struct virtqueue *vq)
iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kfree(info->queue);
+ size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
+ free_pages_exact(info->queue, size);
kfree(info);
}
@@ -305,6 +307,20 @@ static struct virtio_config_ops virtio_pci_config_ops = {
.finalize_features = vp_finalize_features,
};
+static void virtio_pci_release_dev(struct device *_d)
+{
+ struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
+ struct virtio_pci_device *vp_dev = to_vp_device(dev);
+ struct pci_dev *pci_dev = vp_dev->pci_dev;
+
+ free_irq(pci_dev->irq, vp_dev);
+ pci_set_drvdata(pci_dev, NULL);
+ pci_iounmap(pci_dev, vp_dev->ioaddr);
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+ kfree(vp_dev);
+}
+
/* the PCI probing function */
static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
@@ -328,6 +344,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
return -ENOMEM;
vp_dev->vdev.dev.parent = &virtio_pci_root;
+ vp_dev->vdev.dev.release = virtio_pci_release_dev;
vp_dev->vdev.config = &virtio_pci_config_ops;
vp_dev->pci_dev = pci_dev;
INIT_LIST_HEAD(&vp_dev->virtqueues);
@@ -357,7 +374,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
/* register a handler for the queue with the PCI device's interrupt */
err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
- vp_dev->vdev.dev.bus_id, vp_dev);
+ dev_name(&vp_dev->vdev.dev), vp_dev);
if (err)
goto out_set_drvdata;
@@ -387,12 +404,6 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
unregister_virtio_device(&vp_dev->vdev);
- free_irq(pci_dev->irq, vp_dev);
- pci_set_drvdata(pci_dev, NULL);
- pci_iounmap(pci_dev, vp_dev->ioaddr);
- pci_release_regions(pci_dev);
- pci_disable_device(pci_dev);
- kfree(vp_dev);
}
#ifdef CONFIG_PM
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 6eb5303fed11..5777196bf6c9 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -274,6 +274,7 @@ static struct virtqueue_ops vring_vq_ops = {
};
struct virtqueue *vring_new_virtqueue(unsigned int num,
+ unsigned int vring_align,
struct virtio_device *vdev,
void *pages,
void (*notify)(struct virtqueue *),
@@ -292,7 +293,7 @@ struct virtqueue *vring_new_virtqueue(unsigned int num,
if (!vq)
return NULL;
- vring_init(&vq->vring, num, pages, PAGE_SIZE);
+ vring_init(&vq->vring, num, pages, vring_align);
vq->vq.callback = callback;
vq->vq.vdev = vdev;
vq->vq.vq_ops = &vring_vq_ops;
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
index 0d15b0eaf79a..5139c25ca962 100644
--- a/drivers/w1/w1_io.c
+++ b/drivers/w1/w1_io.c
@@ -356,7 +356,9 @@ int w1_reset_select_slave(struct w1_slave *sl)
w1_write_8(sl->master, W1_SKIP_ROM);
else {
u8 match[9] = {W1_MATCH_ROM, };
- memcpy(&match[1], (u8 *)&sl->reg_num, 8);
+ u64 rn = le64_to_cpu(*((u64*)&sl->reg_num));
+
+ memcpy(&match[1], &rn, 8);
w1_write_block(sl->master, match, 9);
}
return 0;
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f7f6ce82a5e2..e31925ee8346 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -42,7 +42,7 @@
#undef S3C_VA_WATCHDOG
#define S3C_VA_WATCHDOG (0)
-#include <asm/plat-s3c/regs-watchdog.h>
+#include <plat/regs-watchdog.h>
#define PFX "s3c2410-wdt: "
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index ed01e4c2beff..e19b45794717 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
+#include <linux/timex.h>
#ifdef CONFIG_ARCH_PXA
#include <mach/pxa-regs.h>
@@ -35,8 +36,7 @@
#include <mach/reset.h>
#include <mach/hardware.h>
-#define OSCR_FREQ CLOCK_TICK_RATE
-
+static unsigned long oscr_freq;
static unsigned long sa1100wdt_users;
static int pre_margin;
static int boot_status;
@@ -123,12 +123,12 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
break;
}
- pre_margin = OSCR_FREQ * time;
+ pre_margin = oscr_freq * time;
OSMR3 = OSCR + pre_margin;
/*fall through*/
case WDIOC_GETTIMEOUT:
- ret = put_user(pre_margin / OSCR_FREQ, p);
+ ret = put_user(pre_margin / oscr_freq, p);
break;
}
return ret;
@@ -155,6 +155,8 @@ static int __init sa1100dog_init(void)
{
int ret;
+ oscr_freq = get_clock_tick_rate();
+
/*
* Read the reset status, and save it for later. If
* we suspend, RCSR will be cleared, and the watchdog
@@ -162,7 +164,7 @@ static int __init sa1100dog_init(void)
*/
boot_status = (reset_status & RESET_STATUS_WATCHDOG) ?
WDIOF_CARDRESET : 0;
- pre_margin = OSCR_FREQ * margin;
+ pre_margin = oscr_freq * margin;
ret = misc_register(&sa1100dog_miscdev);
if (ret == 0)
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 526c191e84ea..8dc7109d61b7 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -44,13 +44,15 @@
#include <linux/list.h>
#include <linux/sysdev.h>
-#include <asm/xen/hypervisor.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/tlb.h>
+#include <asm/xen/hypervisor.h>
+#include <asm/xen/hypercall.h>
+#include <xen/interface/xen.h>
#include <xen/interface/memory.h>
#include <xen/xenbus.h>
#include <xen/features.h>
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 6c8193046e0d..add640ff5c6c 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -233,6 +233,7 @@ static void unmask_evtchn(int port)
static int find_unbound_irq(void)
{
int irq;
+ struct irq_desc *desc;
/* Only allocate from dynirq range */
for (irq = 0; irq < nr_irqs; irq++)
@@ -242,6 +243,10 @@ static int find_unbound_irq(void)
if (irq == nr_irqs)
panic("No available IRQ to bind to: increase nr_irqs!\n");
+ desc = irq_to_desc_alloc_cpu(irq, 0);
+ if (WARN_ON(desc == NULL))
+ return -1;
+
return irq;
}
diff --git a/drivers/xen/features.c b/drivers/xen/features.c
index 0707714e40d6..99eda169c779 100644
--- a/drivers/xen/features.c
+++ b/drivers/xen/features.c
@@ -8,7 +8,11 @@
#include <linux/types.h>
#include <linux/cache.h>
#include <linux/module.h>
-#include <asm/xen/hypervisor.h>
+
+#include <asm/xen/hypercall.h>
+
+#include <xen/interface/xen.h>
+#include <xen/interface/version.h>
#include <xen/features.h>
u8 xen_features[XENFEAT_NR_SUBMAPS * 32] __read_mostly;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 06592b9da83c..7d8f531fb8e8 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -40,6 +40,7 @@
#include <xen/interface/xen.h>
#include <xen/page.h>
#include <xen/grant_table.h>
+#include <asm/xen/hypercall.h>
#include <asm/pgtable.h>
#include <asm/sync_bitops.h>
diff --git a/firmware/Makefile b/firmware/Makefile
index ca8cd305ff93..6968388818be 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -24,6 +24,9 @@ fw-shipped-$(CONFIG_ATARI_DSP56K) += dsp56k/bootstrap.bin
fw-shipped-$(CONFIG_ATM_AMBASSADOR) += atmsar11.fw
fw-shipped-$(CONFIG_CASSINI) += sun/cassini.bin
fw-shipped-$(CONFIG_COMPUTONE) += intelliport2.bin
+fw-shipped-$(CONFIG_CHELSIO_T3) += cxgb3/t3b_psram-1.1.0.bin \
+ cxgb3/t3c_psram-1.1.0.bin \
+ cxgb3/t3fw-7.0.0.bin
fw-shipped-$(CONFIG_DVB_TTUSB_BUDGET) += ttusb-budget/dspbootcode.bin
fw-shipped-$(CONFIG_SMCTR) += tr_smctr.bin
fw-shipped-$(CONFIG_SND_KORG1212) += korg/k1212.dsp
@@ -147,27 +150,15 @@ $(patsubst %,$(obj)/%.gen.o, $(fw-external-y)): $(obj)/%.gen.o: $(fwdir)/%
$(obj)/%: $(obj)/%.ihex | $(objtree)/$(obj)/$$(dir %)
$(call cmd,ihex)
-# Don't depend on ihex2fw if we're installing and it already exists.
-# Putting it after | in the dependencies doesn't seem sufficient when
-# we're installing after a cross-compile, because ihex2fw has dependencies
-# on stuff like /usr/lib/gcc/ppc64-redhat-linux/4.3.0/include/stddef.h and
-# thus wants to be rebuilt. Which it can't be, if the prebuilt kernel tree
-# is exported read-only for someone to run 'make install'.
-ifeq ($(INSTALL):$(wildcard $(obj)/ihex2fw),install:$(obj)/ihex2fw)
-ihex2fw_dep :=
-else
-ihex2fw_dep := $(obj)/ihex2fw
-endif
-
# .HEX is also Intel HEX, but where the offset and length in each record
# is actually meaningful, because the firmware has to be loaded in a certain
# order rather than as a single binary blob. Thus, we convert them into our
# more compact binary representation of ihex records (<linux/ihex.h>)
-$(obj)/%.fw: $(obj)/%.HEX $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.HEX $(obj)/ihex2fw | $(objtree)/$(obj)/$$(dir %)
$(call cmd,ihex2fw)
# .H16 is our own modified form of Intel HEX, with 16-bit length for records.
-$(obj)/%.fw: $(obj)/%.H16 $(ihex2fw_dep) | $(objtree)/$(obj)/$$(dir %)
+$(obj)/%.fw: $(obj)/%.H16 $(obj)/ihex2fw | $(objtree)/$(obj)/$$(dir %)
$(call cmd,h16tofw)
$(firmware-dirs):
diff --git a/firmware/WHENCE b/firmware/WHENCE
index 57002cdecd42..8f06639ba3e7 100644
--- a/firmware/WHENCE
+++ b/firmware/WHENCE
@@ -349,3 +349,14 @@ Licence: Unknown
Found in hex form in kernel source.
--------------------------------------------------------------------------
+
+Driver: cxgb3 - Chelsio Terminator 3 1G/10G Ethernet adapter
+
+File: cxgb3/t3b_psram-1.1.0.bin.ihex
+File: cxgb3/t3c_psram-1.1.0.bin.ihex
+file: cxgb3/t3fw-7.0.0.bin.ihex
+
+License: GPLv2 or OpenIB.org BSD license, no source visible
+
+--------------------------------------------------------------------------
+
diff --git a/firmware/cxgb3/t3b_psram-1.1.0.bin.ihex b/firmware/cxgb3/t3b_psram-1.1.0.bin.ihex
new file mode 100644
index 000000000000..140893005171
--- /dev/null
+++ b/firmware/cxgb3/t3b_psram-1.1.0.bin.ihex
@@ -0,0 +1,162 @@
+:10000000FFFFFFFC000000000000000300000000F4
+:1000100000010100FFFFFFFC0000000000000003E2
+:100020000000000000000000FFFFFFFC00000000D7
+:10003000000000030000000000000000FFFFFFFCC4
+:1000400000000000000000030000000000000000AD
+:10005000FFFFFFFC000000000000000300000000A4
+:1000600000000000FFFFFFFC000000000000000394
+:100070000000000000000000FFFFFFFC0000000087
+:10008000000000030000000000000000FFFFFFFC74
+:10009000000000000000000300000000000000005D
+:1000A000FFFFFFFC00000000000000030000000054
+:1000B00000000000FFFFFFFC000000000000000344
+:1000C0000000000000000000FFFFFFFC0000000037
+:1000D000000000030000000000000000FFFFFFFC24
+:1000E000000000000000000300000000000000000D
+:1000F000FFFFFFFC00000000000000030000000004
+:1001000000000000FFFFFFFC0000000000000003F3
+:100110000000000000000000FFFFFFFBD03403E6FA
+:1001200080262A430000000000000000FFFFFFF8C7
+:10013000007000000000000200000081C604000002
+:10014000FFFFFFFC000000000000000300000000B3
+:1001500000000000FFFFFFFC0000000000000003A3
+:100160000000000000000000FFFFFFFC0000000096
+:10017000000000030000000000000000FFFFFFFC83
+:10018000000000000000000300000000000000006C
+:10019000FFFFFFFC00000000000000030000000063
+:1001A00000000000FFFFFFFC000000000000000353
+:1001B0000000000000000000FFFFFFFBD03403E25E
+:1001C000802829230000000000000000FFFFFFF846
+:1001D0000600023701C5C00013940481C6057000F3
+:1001E000FFFFFFF88200020637030803000000004B
+:1001F00000000000FFFFFFFC000000000000000204
+:10020000208000818DF40000FFFFFFFC0000000053
+:10021000000000030000000000000000FFFFFFFCE2
+:1002200000000000000000030000000000000000CB
+:10023000FFFFFFFC000000000000000300000000C2
+:1002400000000000FFFFFFFC0000000000000003B2
+:100250000000000000000000FFFFFFF9C4310000B3
+:1002600000282C830000000000000000FFFFFFF0CA
+:100270004E70021D00C5C00000000001C118000042
+:10028000FFFFFFFC00000000000000030000000072
+:1002900000000000FFFFFFFC000000000000000362
+:1002A0000000000000000000FFFFFFFC0000000055
+:1002B000000000030000000000000000FFFFFFFC42
+:1002C000000000000000000300000000000000002B
+:1002D000FFFFFFFC00000000000000030000000022
+:1002E00000000000FFFFFFFC000000000000000312
+:1002F0000000000000000000FFFFFFF1C00003E667
+:10030000802828230000000000000000FFFFFFFC01
+:1003100000000000000000021394040000017000BF
+:10032000FFFFFFFC000000000000000300000000D1
+:1003300000000000FFFFFFFC0000000000000003C1
+:100340000000000000000000FFFFFFFC00000000B4
+:10035000000000030000000000000000FFFFFFFCA1
+:10036000000000000000000300000000000000008A
+:10037000FFFFFFFC00000000000000030000000081
+:1003800000000000FFFFFFFC000000000000000371
+:100390000000000000000000FFFFFFFA103400041E
+:1003A000000001030000000000000000FFFFFFF05C
+:1003B0006000000620030802700000F080259A907B
+:1003C000FFFFFFFC00000000000000030000000031
+:1003D00000000000FFFFFFFC000000000000000321
+:1003E0000000000000000000FFFFFFFC0000000014
+:1003F000000000030000000000000000FFFFFFFC01
+:1004000000000000000000030000000000000000E9
+:10041000FFFFFFF1C83102060A000242000000811E
+:1004200080000000FFFFFFF9C83103C60A962A4288
+:100430000000008180000000FFFFFFF00431000495
+:10044000000004030000000000000000FFFFFFF0B8
+:1004500020B000000000000213940401C1197000D4
+:10046000FFFFFFFC00000000000000000000000192
+:1004700000001000FFFFFFFC000000000000000370
+:100480000000000000000000FFFFFFFC0000000073
+:10049000000000030000000000000000FFFFFFFC60
+:1004A0000000000000000003000000000000000049
+:1004B000FFFFFFF00000000400004000680C200176
+:1004C00000001090FFFFFFF9C031C3E600266A402C
+:1004D0000000000100001000FFFFFFFA10F4000010
+:1004E000000002430000000000000000FFFFFFF8D2
+:1004F0006050080000000000700C20F080259A90E9
+:10050000FFFFFFF0060000000100400000000001B6
+:1005100000001000FFFFFFFC0000000000000002D0
+:10052000288C108085C01000FFFFFFFC0000000039
+:10053000000000030000000000000000FFFFFFFCBF
+:1005400000000000000000030000000000000000A8
+:10055000FFFFFFFC0000000000000003000000009F
+:1005600000000000FFFFFFFC00000000000000038F
+:100570000000000000000000FFFFFFF04E00000040
+:10058000000000030000000000000000FFFFFFF17A
+:10059000C00002DE00061A40000000829035C00054
+:1005A000FFFFFFFC0000000000000003000000004F
+:1005B00000000000FFFFFFFC00000000000000033F
+:1005C0000000000000000000FFFFFFFC0000000032
+:1005D000000000030000000000000000FFFFFFFC1F
+:1005E0000000000000000003000000000000000008
+:1005F000FFFFFFFC000000000000000300000000FF
+:1006000000000000FFFFFFF1CA31C3C20A966A432F
+:100610000000000000000000FFFFFFF84E501439FA
+:100620001CC5C0030000000000000000FFFFFFF039
+:100630000000000000000002288C108085C010001F
+:10064000FFFFFFFC000000000000000300000000AE
+:1006500000000000FFFFFFFC00000000000000039E
+:100660000000000000000000FFFFFFFC0000000091
+:10067000000000030000000000000000FFFFFFFC7E
+:100680000000000000000003000000000000000067
+:10069000FFFFFFFC0000000000000003000000005E
+:1006A00000000000FFFFFFF3CA3323D60E966A4313
+:1006B0000000000000000000FFFFFFF8000004063B
+:1006C00020D002430000000000000000FFFFFFF800
+:1006D00000D0000000000000000000839031C00046
+:1006E000FFFFFFFC0000000000000003000000000E
+:1006F00000000000FFFFFFFC0000000000000003FE
+:100700000000000000000000FFFFFFFC00000000F0
+:10071000000000030000000000000000FFFFFFFCDD
+:1007200000000000000000030000000000000000C6
+:10073000FFFFFFFC000000000000000300000000BD
+:1007400000000000FFFFFFF3CA33E3D60E966A43B2
+:100750000000000000000000FFFFFFF000501A1032
+:10076000003002430000000000000000FFFFFFF81F
+:100770000000020620030800700000F990118A9022
+:10078000FFFFFFFC0000000000000003000000006D
+:1007900000000000FFFFFFFC00000000000000035D
+:1007A0000000000000000000FFFFFFFC0000000050
+:1007B000000000030000000000000000FFFFFFFC3D
+:1007C0000000000000000003000000000000000026
+:1007D000FFFFFFFC0000000000000003000000001D
+:1007E00000000000FFFFFFFC00000000000000030D
+:1007F0000000000000000000FFFFFFF9C0501BA632
+:1008000000D202430000000000000000FFFFFFF0E4
+:100810004000020700100002700000E890344A9087
+:10082000FFFFFFFC000000000000000300000000CC
+:1008300000000000FFFFFFFC0000000000000003BC
+:100840000000000000000000FFFFFFFC00000000AF
+:10085000000000030000000000000000FFFFFFFC9C
+:100860000000000000000003000000000000000085
+:10087000FFFFFFFC0000000000000003000000007C
+:1008800000000000FFFFFFFC00000000000000036C
+:100890000000000000000000FFFFFFFA10F4020853
+:1008A00000C002430000000000000000FFFFFFF056
+:1008B0000000000000000000728CC8D893891090DE
+:1008C000FFFFFFF082900000030000030000000023
+:1008D00000000000FFFFFFFC00000000000000031C
+:1008E0000000000000000000FFFFFFFC000000000F
+:1008F000000000030000000000000000FFFFFFFCFC
+:1009000000000000000000030000000000000000E4
+:10091000FFFFFFFC000000000000000300000000DB
+:1009200000000000FFFFFFFC0000000000000003CB
+:100930000000000000000000FFFFFFF000000000CA
+:10094000000000030000000000000000FFFFFFF2B5
+:1009500000000320002612430000000000000000F9
+:10096000FFFFFFF040000203101000030000000032
+:1009700000000000FFFFFFFC00000000000000037B
+:100980000000000000000000FFFFFFFC000000006E
+:10099000000000030000000000000000FFFFFFFC5B
+:1009A0000000000000000003000000000000000044
+:1009B000FFFFFFFC0000000000000003000000003B
+:1009C00000000000FFFFFFFC00000000000000032B
+:1009D0000000000000000000FFFFFFF1D03403E63C
+:1009E00080262A430000000000000000FFFFFFF205
+:1009F0000834023000C005030000000000000000C1
+:040A000070EAA57F74
+:00000001FF
diff --git a/firmware/cxgb3/t3c_psram-1.1.0.bin.ihex b/firmware/cxgb3/t3c_psram-1.1.0.bin.ihex
new file mode 100644
index 000000000000..c6504803852f
--- /dev/null
+++ b/firmware/cxgb3/t3c_psram-1.1.0.bin.ihex
@@ -0,0 +1,162 @@
+:10000000FFFFFFF4000000040000000100000001F9
+:1000100000010100FFFFFFF40000000400000001E8
+:100020000000000100000000FFFFFFF400000004DA
+:10003000000000010000000100000000FFFFFFF4CD
+:1000400000000004000000010000000100000000AA
+:10005000FFFFFFF4000000040000000100000001A9
+:1000600000000000FFFFFFF400000004000000019A
+:100070000000000100000000FFFFFFF4000000048A
+:10008000000000010000000100000000FFFFFFF47D
+:10009000000000040000000100000001000000005A
+:1000A000FFFFFFF400000004000000010000000159
+:1000B00000000000FFFFFFF400000004000000014A
+:1000C0000000000100000000FFFFFFF4000000043A
+:1000D000000000010000000100000000FFFFFFF42D
+:1000E000000000040000000100000001000000000A
+:1000F000FFFFFFF400000004000000010000000109
+:1001000000000000FFFFFFF40000000400000001F9
+:100110000000000100000000FFFFFFF3D03403E205
+:1001200080262A410000000100000000FFFFFFF8C8
+:10013000007000000000000000000080C604000005
+:10014000FFFFFFF4000000040000000100000001B8
+:1001500000000000FFFFFFF40000000400000001A9
+:100160000000000100000000FFFFFFF40000000499
+:10017000000000010000000100000000FFFFFFF48C
+:100180000000000400000001000000010000000069
+:10019000FFFFFFF400000004000000010000000168
+:1001A00000000000FFFFFFF4000000040000000159
+:1001B0000000000100000000FFFFFFFBD03403E25D
+:1001C000802829210000000100000000FFFFFFF847
+:1001D0000600023701C5C00213940480C6057000F2
+:1001E000FFFFFFF88200020637030801000000014C
+:1001F00000000000FFFFFFF400000004000000000A
+:10020000208000808DF40000FFFFFFF40000000458
+:10021000000000010000000100000000FFFFFFF4EB
+:1002200000000004000000010000000100000000C8
+:10023000FFFFFFF4000000040000000100000001C7
+:1002400000000000FFFFFFF40000000400000001B8
+:100250000000000100000000FFFFFFF9C4310000B2
+:1002600000282C810000000100000000FFFFFFF0CB
+:100270004E70021D00C5C00200000000C118000041
+:10028000FFFFFFF400000004000000010000000177
+:1002900000000000FFFFFFF4000000040000000168
+:1002A0000000000100000000FFFFFFF40000000458
+:1002B000000000010000000100000000FFFFFFF44B
+:1002C0000000000400000001000000010000000028
+:1002D000FFFFFFF400000004000000010000000127
+:1002E00000000000FFFFFFF4000000040000000118
+:1002F0000000000100000000FFFFFFF1C00003E666
+:10030000802828210000000100000000FFFFFFF40A
+:1003100000000004000000021394040000017000BB
+:10032000FFFFFFF4000000040000000100000001D6
+:1003300000000000FFFFFFF40000000400000001C7
+:100340000000000100000000FFFFFFF400000004B7
+:10035000000000010000000100000000FFFFFFF4AA
+:100360000000000400000001000000010000000087
+:10037000FFFFFFF400000004000000010000000186
+:1003800000000000FFFFFFF4000000040000000177
+:100390000000000100000000FFFFFFFA103400041D
+:1003A000000001010000000100000000FFFFFFF05D
+:1003B0006000000620030802700000F080259A907B
+:1003C000FFFFFFF400000004000000010000000136
+:1003D00000000000FFFFFFF4000000040000000127
+:1003E0000000000100000000FFFFFFF40000000417
+:1003F000000000010000000100000000FFFFFFF40A
+:1004000000000004000000010000000100000000E6
+:10041000FFFFFFF9C83102020A000242000000811A
+:1004200080000000FFFFFFF1C83103C20A962A4294
+:100430000000008180000000FFFFFFF00431000495
+:10044000000004010000000100000000FFFFFFF8B1
+:1004500020B000040000000013940400C1197000D3
+:10046000FFFFFFF400000004000000020000000095
+:1004700000001000FFFFFFF4000000040000000176
+:100480000000000100000000FFFFFFF40000000476
+:10049000000000010000000100000000FFFFFFF469
+:1004A0000000000400000001000000010000000046
+:1004B000FFFFFFF80000000000004000680C200172
+:1004C00000001090FFFFFFF9C031C3E600266A422A
+:1004D0000000000000001000FFFFFFF210F4000415
+:1004E000000002410000000100000000FFFFFFF0DB
+:1004F0006050080400000002700C20F180259A90E2
+:10050000FFFFFFF8060000040100400200000000A9
+:1005100000001000FFFFFFF40000000400000002D4
+:10052000288C108085C01000FFFFFFF4000000043D
+:10053000000000010000000100000000FFFFFFF4C8
+:1005400000000004000000010000000100000000A5
+:10055000FFFFFFF4000000040000000100000001A4
+:1005600000000000FFFFFFF4000000040000000195
+:100570000000000100000000FFFFFFF04E0000003F
+:10058000000000010000000100000000FFFFFFF973
+:10059000C00002DA00061A42000000839035C00055
+:1005A000FFFFFFF400000004000000010000000154
+:1005B00000000000FFFFFFF4000000040000000145
+:1005C0000000000100000000FFFFFFF40000000435
+:1005D000000000010000000100000000FFFFFFF428
+:1005E0000000000400000001000000010000000005
+:1005F000FFFFFFF400000004000000010000000104
+:1006000000000000FFFFFFF9CA31C3C60A966A4125
+:100610000000000100000000FFFFFFF84E501439F9
+:100620001CC5C0010000000100000000FFFFFFF03A
+:100630000000000000000002288C108085C010001F
+:10064000FFFFFFF4000000040000000100000001B3
+:1006500000000000FFFFFFF40000000400000001A4
+:100660000000000100000000FFFFFFF40000000494
+:10067000000000010000000100000000FFFFFFF487
+:100680000000000400000001000000010000000064
+:10069000FFFFFFF400000004000000010000000163
+:1006A00000000000FFFFFFF3CA3323D60E966A4115
+:1006B0000000000100000000FFFFFFF8000004063A
+:1006C00020D002410000000100000000FFFFFFF801
+:1006D00000D0000000000000000000839031C00046
+:1006E000FFFFFFF400000004000000010000000113
+:1006F00000000000FFFFFFF4000000040000000104
+:100700000000000100000000FFFFFFF400000004F3
+:10071000000000010000000100000000FFFFFFF4E6
+:1007200000000004000000010000000100000000C3
+:10073000FFFFFFF4000000040000000100000001C2
+:1007400000000000FFFFFFFBCA33E3D20E966A41B0
+:100750000000000100000000FFFFFFF000501A1031
+:10076000003002410000000100000000FFFFFFF028
+:100770000000020220030800700000F990118A9026
+:10078000FFFFFFF400000004000000010000000172
+:1007900000000000FFFFFFF4000000040000000163
+:1007A0000000000100000000FFFFFFF40000000453
+:1007B000000000010000000100000000FFFFFFF446
+:1007C0000000000400000001000000010000000023
+:1007D000FFFFFFF400000004000000010000000122
+:1007E00000000000FFFFFFF4000000040000000113
+:1007F0000000000100000000FFFFFFF1C0501BA23D
+:1008000000D202410000000100000000FFFFFFF8DD
+:100810004000020300100002700000E890344A908B
+:10082000FFFFFFF4000000040000000100000001D1
+:1008300000000000FFFFFFF40000000400000001C2
+:100840000000000100000000FFFFFFF400000004B2
+:10085000000000010000000100000000FFFFFFF4A5
+:100860000000000400000001000000010000000082
+:10087000FFFFFFF400000004000000010000000181
+:1008800000000000FFFFFFF4000000040000000172
+:100890000000000100000000FFFFFFFA10F4020852
+:1008A00000C002410000000100000000FFFFFFF057
+:1008B0000000000000000002728CC8D993891090DB
+:1008C000FFFFFFF082900000030000010000000124
+:1008D00000000000FFFFFFF4000000040000000122
+:1008E0000000000100000000FFFFFFF40000000412
+:1008F000000000010000000100000000FFFFFFF405
+:1009000000000004000000010000000100000000E1
+:10091000FFFFFFF4000000040000000100000001E0
+:1009200000000000FFFFFFF40000000400000001D1
+:100930000000000100000000FFFFFFF000000000C9
+:10094000000000010000000100000000FFFFFFF2B6
+:1009500000000320002612410000000100000000FA
+:10096000FFFFFFF040000203101000010000000133
+:1009700000000000FFFFFFF4000000040000000181
+:100980000000000100000000FFFFFFF40000000471
+:10099000000000010000000100000000FFFFFFF464
+:1009A0000000000400000001000000010000000041
+:1009B000FFFFFFF400000004000000010000000140
+:1009C00000000000FFFFFFF4000000040000000131
+:1009D0000000000100000000FFFFFFF9D03403E237
+:1009E00080262A410000000100000000FFFFFFF206
+:1009F0000834023000C005010000000100000000C2
+:040A000070EAA741B0
+:00000001FF
diff --git a/firmware/cxgb3/t3fw-7.0.0.bin.ihex b/firmware/cxgb3/t3fw-7.0.0.bin.ihex
new file mode 100644
index 000000000000..e66117938e88
--- /dev/null
+++ b/firmware/cxgb3/t3fw-7.0.0.bin.ihex
@@ -0,0 +1,1881 @@
+:1000000060007400200380002003700000001000D6
+:1000100000002000E100028400070000E1000288E7
+:1000200000010000E0000000E00000A0010000006E
+:1000300044444440E3000183200200002001E0002A
+:100040002001FF101FFFD0001FFFC000E300043C91
+:1000500002000000200069541FFFC5802000699C39
+:100060001FFFC584200069DC1FFFC58820006A507F
+:100070001FFFC58C200003C0C00000E43100EA313E
+:1000800000A13100A03103020002ED306E2A05000C
+:10009000ED3100020002160012FFDBC03014FFDA5F
+:1000A000D30FD30FD30F03431F244C107249F0D347
+:1000B0000FD30FD30F12FFD5230A00240A00D30F4A
+:1000C000D30FD30F03431F244C107249F0D30FD327
+:1000D0000FD30F14FFCE03421F14FFCB03421F1296
+:1000E000FFCCC0302D37302D37342D37382D373CED
+:1000F000233D017233ED00020012FFC4C0302F37E0
+:10010000002F37102F37202F3730233D017233ED6A
+:1001100000020012FFBEC0302737002737102737F4
+:1001200020273730233D017233ED03020012FFB95F
+:1001300013FFBA0C0200932012FFB913FFB90C028F
+:1001400000932012FFB8C0319320822012FFB71312
+:10015000FFB7932012FFB715FFB316FFB6C030D715
+:100160002005660160001B00000000000000000088
+:10017000043605000200D30FD30F7531140747145E
+:1001800005330C0704437631E60436057539ED0076
+:10019000020012FFA715FFA3C030D72060000600A1
+:1001A00007471405330C070443043E057539F00373
+:1001B000020012FFA1C03014FFA1D30FD30FD30F41
+:1001C0009340B4447249F2D30FD30FD30F14FF9B63
+:1001D000834014FF9B834012FF9B230A0014FF9A65
+:1001E000D30FD30FD30F9340B4447249F2D30FD33C
+:1001F0000FD30F14FF95834012FF95CA20832084EC
+:10020000218522BC22743B108650B4559630B433FD
+:100210007433F463FFE6000000653FE0655FDD12C4
+:10022000FF7CC03028374028374428374828374CCF
+:10023000233D017233ED03020000020012FF7AC079
+:1002400032032E0503020012FF7813FF819320C0B2
+:1002500011014931004831010200C00014FF7E0441
+:10026000D23115FF7D945014FF7D04D33115FF7CEE
+:10027000945014FF7C04D43115FF7C24560014FFE5
+:100280007B04D53115FF7B24560010FF7A03000054
+:10029000000000000000000000000000000000005E
+:1002A000000000000000000000000000000000004E
+:1002B000000000000000000000000000000000003E
+:1002C000000000000000000000000000000000002E
+:1002D000000000000000000000000000000000001E
+:1002E000000000000000000000000000000000000E
+:1002F00000000000000000000000000000000000FE
+:1003000000000000000000000000000000000000ED
+:1003100000000000000000000000000000000000DD
+:1003200000000000000000000000000000000000CD
+:1003300000000000000000000000000000000000BD
+:1003400000000000000000000000000000000000AD
+:10035000000000000000000000000000000000009D
+:10036000000000000000000000000000000000008D
+:10037000000000000000000000000000000000007D
+:10038000000000000000000000000000000000006D
+:10039000000000000000000000000000000000005D
+:1003A000000000000000000000000000000000004D
+:1003B000000000000000000000000000000000003D
+:1003C000000000000000000000000000000000002D
+:1003D000000000000000000000000000000000001D
+:1003E000000000000000000000000000000000000D
+:1003F00000000000000000000000000000000000FD
+:1004000000000000000000000000000000000000EC
+:1004100000000000000000000000000000000000DC
+:1004200063FFFC000000000000000000000000006E
+:100430000000000000000000000000001FFC0000A1
+:100440001FFC0000E30005C81FFC00001FFC0000AB
+:10045000E30005C81FFC00001FFC0000E30005C806
+:100460001FFFC0001FFFC000E30005C81FFFC00042
+:100470001FFFC018E30005C81FFFC0181FFFC018EA
+:10048000E30005E01FFFC0181FFFC288E30005E07E
+:100490001FFFC2881FFFC288E30008501FFFC290E1
+:1004A0001FFFC57CE3000850200000002000016A07
+:1004B000E3000B3C2000018020000180E3000CA839
+:1004C0002000020020000203E3000CA82000021C10
+:1004D00020000220E3000CAC2000022020000226B5
+:1004E000E3000CB02000023C20000240E3000CB806
+:1004F0002000024020000249E3000CBC2000024C16
+:1005000020000250E3000CC82000025020000259D5
+:10051000E3000CCC2000025C20000260E3000CD859
+:100520002000026020000269E3000CDC2000026C65
+:1005300020000270E3000CE8200002702000027925
+:10054000E3000CEC2000028C2000028CE3000CF88D
+:100550002000029020000293E3000CF8200002AC7F
+:10056000200002B0E3000CFC200002D0200002F2C8
+:10057000E3000D00200003B0200003B0E3000D24D1
+:10058000200003B0200003B0E3000D24200003B0DE
+:10059000200003B0E3000D24200003B0200003B0CE
+:1005A000E3000D24200003B020006B74E3000D2451
+:1005B00020006B7420006B74E30074E800000000FE
+:1005C00000000000000000001FFC00001FFC0000F5
+:1005D0001FFFC5801FFFC67020006B7820006B785E
+:1005E000DEFFFE000000080CDEADBEEF1FFFC29074
+:1005F0001FFCFE001FFFC0841FFFC5C030000000AD
+:10060000003FFFFF8040000010000000080FFFFFC8
+:100610001FFFC25D000FFFFF804FFFFF8000000043
+:1006200000000880B000000560500000600000007D
+:1006300040000011350000004100000010000001E2
+:1006400020000000000010007FFFFFFF40000000BE
+:1006500005000000800000190400000000000800F0
+:1006600010000005806000007000000020000009FC
+:10067000001FF8008000001EA0000000F80000002D
+:100680000800000007FFFFFF1800000001008001C4
+:10069000420000001FFFC20D1FFFC0CC0001008000
+:1006A000604000001A0000000C0000000000300054
+:1006B000600008008000001C000100008000001A9B
+:1006C00080000018FC0000008000000100004000D5
+:1006D000800004000300000050000003FFFFBFFF84
+:1006E00000000FFF1FFFC390FFFFF000000016D0B7
+:1006F0000000FFF7A50000001FFFC4A01FFFC451AA
+:100700000001000800000B20202FFF801FFFC445C0
+:1007100000002C00FFFEFFF800FFFFFF1FFFC56871
+:1007200000002000FFFFDFFF0000FFEF01001100CD
+:100730001FFFC4611FFFC3C21FFFC4101FFFC5906E
+:10074000FFFFEFFF0000FFFB1FFFBE90FFFFF7FF63
+:100750001FFFC0540000FFFD0001FBD01FFFC5B00C
+:100760001FFFC6601FFFC591E0FFFE000000800074
+:100770001FFFC52C1FFFC5B41FFFC0581FFFC4D0EB
+:100780001FFCFFD800010081E100060000002710D7
+:100790001FFCFE301FFCFE70E10002001FFFC52899
+:1007A0001FFFC5400003D0901FFFC5542B5063802E
+:1007B0002B5079802B5090802B50A6801FFFC4595E
+:1007C0000100110F202FFE0020300080202FFF009D
+:1007D0000000FFFF0001FFF82B50B2002B50B208C1
+:1007E000000100102B50B1802B50B2802B50BA006A
+:1007F000000100112B50BD282B50BC802B50BDA0F8
+:1008000020300000DFFFFE005000000200C00000AA
+:1008100002000000FFFFF7F41FFFC05C000FF800AC
+:1008200004400000001000000C4000001C400000CC
+:10083000E00000A01FFFC5301FFD00081FFFC544DA
+:100840001FFFC5581FFFC56CE1000690E10006ECD4
+:100850000100000000000000000000000000000097
+:100860002010004020100040201000402014008084
+:10087000200C0000200C0000200C00002010004084
+:10088000201400802014008020140080201800C054
+:10089000201C0100201C0100201C01002020014020
+:1008A000201800C0201800C0201800C0201C010023
+:1008B000201800C0201800C0201800C0201C010013
+:1008C000202001402020014020200140202009401C
+:1008D00020200940202009402020094020240980B0
+:1008E000FFFFFFFFFFFFFFFFFFFFFFFF0000000014
+:1008F00000000000000000000000000000000000F8
+:100900002000525420005124200052542000525400
+:1009100020005060200050602000506020004E9465
+:1009200020004E9420004E8C20004DFC20004CA056
+:1009300020004A88200048880000000000000000D5
+:1009400020005224200050F02000519420005194A7
+:1009500020004F3820004F3820004F3820004F38FB
+:1009600020004F3820004E8420004F3820004BC418
+:1009700020004A3C20004838000000000000000031
+:1009800020000B702000384C200004C02000442CB4
+:1009900020000B6820003F40200003F0200043ECC3
+:1009A0002000481420003C5020003B6C200037C839
+:1009B00020003654200033CC20002EF8200039CC03
+:1009C00020002B5C200027942000648C2000232032
+:1009D0002000200420001FB820001CA4200017B015
+:1009E000200014F020000D8C20000BB4200010BC5F
+:1009F000200012A02000413020003C0420000B7891
+:100A0000200004C000000000000000000000000002
+:100A100000000000000000000000000000000000D6
+:100A200000000000000000000000000000000000C6
+:100A300000000000000000000000000000000000B6
+:100A400000000000000000000000000000000000A6
+:100A50000000000000000000000000000000000096
+:100A60000000000000000000000000000000000086
+:100A70000000000000000000000000000000000076
+:100A8000000000003264000000000000326400003A
+:100A90006400640064006400640064006400640036
+:100AA0000000000000000000000000000000000046
+:100AB0000000000000000000000000000000000036
+:100AC0000000000000000000000000000000000026
+:100AD0000000000000000000000000000000000016
+:100AE00000000000000000000000100000000000F6
+:100AF00000000000000000000000000000000000F6
+:100B000000001000000000000000000000000000D5
+:100B100000000000004323800000000000000000EF
+:100B200000000000000000000000000000000000C5
+:100B3000000000000000000000000000005C9401C4
+:100B40005D94025E94035F94004300000000000087
+:100B50000000000000000000000000000000000095
+:100B60000000000000000000000000000000000085
+:100B7000000000000000000000000000005C900188
+:100B80005D90025E90035F90005300000000000043
+:100B90000000000000000000000000000000000055
+:100BA0000000000000000000000000000000000045
+:100BB000000000000000000000000000009C940005
+:100BC0001D90019D94029E94039F9404089405092E
+:100BD00094060A94070B94004300000000000000F4
+:100BE0000000000000000000000000000000000005
+:100BF000000000000000000000000000009C9001C8
+:100C00009D90029E90071D90039F90047890057917
+:100C100090067A90077B90005300000000000000CF
+:100C200000000000000000000000000000000000C4
+:100C300000000000000000000000000000DC940044
+:100C40001D9001DD9402DE9403DF940404940505F5
+:100C5000940606940707940808940909940A0A94CC
+:100C60000B0B940043000000000000000000000097
+:100C700000000000000000000000000000DC900107
+:100C8000DD9002DE900B1D9003DF9004B49005B55B
+:100C90009006B69007B79008B89009B9900ABA9034
+:100CA0000BBB90005300000063FFFC002000693084
+:100CB00010FFFF0A000000002000695400D231102C
+:100CC000FFFE0A00000000002000699C00D33110E4
+:100CD000FFFE0A0000000000200069DC00D4311093
+:100CE000FFFE0A000000000020006A5000D531100D
+:100CF000FFFE0A000000000063FFFC00E00000A00F
+:100D000012FFF78220028257C82163FFFC12FFF313
+:100D100003E83004EE3005C030932094219522631F
+:100D2000FFFC00001FFFD000000400201FFFC58053
+:100D30001FFFC670200A0011FFFB13FFFB03E63103
+:100D400001020016FFFA17FFFAD30F776B069060C7
+:100D5000B4667763F85415265419D90F140063FF4D
+:100D6000F90000006C1004C020D10F006C1006C008
+:100D7000C71AEF060D4911D830D7201BEF05BC224A
+:100D80008572AB76837105450B957209330C23761A
+:100D900001723B05233D08237601A39D19EEFE7DDC
+:100DA0006326C021C0E0032E380E0E42C8EE29A6ED
+:100DB0007E6D4A0500808800308C8271D10FC0F0B2
+:100DC000082F387FC0EA63FFE49210037F0CABFF6B
+:100DD0000F3D12DB802EDC100E4E36C021C05003BA
+:100DE0002538221200050542CB5029A67E6DEA0562
+:100DF00000B08800308CBC76C050A8F3C081068556
+:100E000038050542CA5129A67E0D480CD30F6D8ABC
+:100E10000500308800208C8271D10F00C061C05065
+:100E200008653875C0C663FFC0C0B0038B387BC08F
+:100E3000D763FFD16C101216EED82A221E2E221D67
+:100E4000C0D07AE11B2CA000D7A028CCE96481484F
+:100E500029CCE8649341C1B97BC12569CC1B6000F2
+:100E6000222CD000D7D028CCE964815429CCE86466
+:100E700093BAC1B97BC10968CC09C020D10F000069
+:100E8000002D25028C32C0900C6F5065F586292408
+:100E900067090847658582B44927200C18EEC00C05
+:100EA0007F11A8FF28F286DB707893026005541941
+:100EB000EEBC09790A2992A36890082822000988C3
+:100EC0000C65853F29F28564953929161A655563A5
+:100ED0007AE104DBA0600001C0B022161B8DB412C1
+:100EE000EEB10D881482240D0D47A82218EEAF092B
+:100EF000DD10082202929018EEAD12EEAE08C80185
+:100F00000D88020242021DEEAA92910D880298926B
+:100F100022B0232DB02204281006DD100242120850
+:100F2000DD0228B0210722100C88100288020D88EB
+:100F30000212EEA18D3302DD0182340D88029893F6
+:100F400092B992948DB582399D9588B68D389896D0
+:100F500088B792999D989897C0D028F28512EE97FD
+:100F600008480BA2722D24CF28F68522121B655447
+:100F7000DE7AE104DBA0600001C0B064BEFB2CB0EF
+:100F80000728B000DA2006880A28824CC0D10B80DE
+:100F900000DBA065AFE763FEE02EA0032B2067D93E
+:100FA000E0D4E065B1AB8B320BFC5064C4B218EEF8
+:100FB000848F2A08B80108FF0C64F2CDC09260014A
+:100FC0008A2AD003292067D4A06594C98B320BFCF0
+:100FD0005064C48C1FEE7B8E2A0FBF017FE9DC8C2E
+:100FE000330CE8506484B4C0B00CA5118F592B1693
+:100FF000128A578E582A1611DBE0AAFA7FAB01B18C
+:10100000EB75CE599E1F8937951CAF98981D798B2B
+:101010000825160C29EC0129160F9A168A1D851F22
+:101020002A16192A0A007BE30A7BE9052B12067FA0
+:10103000BB01C0A165A4698B35C0A0C08078E30462
+:1010400064E3B5C0A165A458891C2916170C4A543D
+:101050002A1616BCAA2A16186000AF0000008837AE
+:10106000893628161429161508F80C09E90C2916D2
+:1010700013981E78FB07281213B088281613891EB0
+:101080009A189F172A1213C0F02916197BE30C7BBC
+:10109000E9078E172B12087EBB01C0F165F406C06C
+:1010A000B02F121988352E12129819281211AAEE93
+:1010B000AF8F78FB022EEC019F138F199F12C0F0A7
+:1010C0007BE30C7EB9078913281202798B01C0F1EA
+:1010D00065F3D22516172912150C4F54C0E12F16AF
+:1010E00016BCFF2F161800F10400EE1A2F1214B0D0
+:1010F000EE0EF8130988010FEE012F1219A8AAAEFF
+:10110000FE7FEB01B1AAD5A02E16192B1219DA50C9
+:101110002C12185818D3C0D0C0902E121907480AA4
+:101120002C1218DAB08F34C0B1AAFF00C1042A1201
+:10113000162F86162C121700BB1AB0BB0EBE019ECE
+:10114000C90BFB1305BB019BC82A7410292467290E
+:1011500070038E75B19F2F7403B0EE0E4E0C65EDCB
+:10116000182820672D25026583132A221E29221D97
+:101170007A9104DBA0600001C0B064BCFC2CB00715
+:1011800028B000DA2006880A28824CC0D10B8000E3
+:10119000DBA065AFE763FCE189AAB199659094890A
+:1011A000341BEE0899AA88331FEE0208485428A47D
+:1011B0002C8E2A8C320FEE020BCB017EB9640C4BC5
+:1011C000516FB25E8B3375B6592CA0130BEE510ED6
+:1011D000CE010E0E410C0C417EC9472FA012B0FF6C
+:1011E00065F2DD8E378CA88B368FA97CB3077BC95F
+:1011F000027EFB01C0D1CED98835DDB00E8E0878D5
+:10120000EB01B1BD89A7DAC00F9B0879BB022ACCDC
+:1012100001DCB0C0B07DA3077AD9027CEB01C0B17C
+:1012200064B163C091292467C020D10F008BDAB16B
+:10123000BB64B0C02C20672D250265C2281DEDDCE3
+:101240008C321FEDE10DCD010FDD0C65D1C10C4FCE
+:10125000516FF2026001B8C0902924670908476500
+:10126000820F7AE104DBA0600001C0B064BC0A2CEC
+:10127000B00728B000DA2006880A28824CC0D10BBB
+:101280008000DBA065AFE763FBEF8C330CE95064B3
+:1012900092090CEF11AFAF2F16178EF885F7DBE030
+:1012A0008FF9251610A5F57F5B022BEC010CA850D9
+:1012B0006580DD8837DAE0AF8929160A789B022A33
+:1012C000EC019514891AD5A02916192A0A007BE386
+:1012D0000A7BE9052B12047FBB01C0A165A1C18B6C
+:1012E00035C0A0C08078E30464E104C0A164AD5CB3
+:1012F0006001AD00008E3419EDB39EDA8C331BED26
+:10130000AC0C4C542CD42C8A2A8C320BAA0209C95E
+:10131000010A990C659F0B0C4F516EF20263FF029C
+:101320008B330BA850648EFA29D0130BEA510A9A1A
+:10133000010A0A410909410A990C659EE52BD01260
+:10134000B0BB65B188C0A08E378CD82B32062FD2A7
+:10135000097CB3077BC9027EFB01C0A165AEC388CF
+:1013600035D3B0AE8E78EB01B1B389D7DAC0AF9B7D
+:1013700079BB01B1CADCB0C0B073A3077A39027C73
+:10138000EB01C0B165BE9BC090292467C020D10F7E
+:10139000008A3688372A16152816140AEA0C08F827
+:1013A0000C981B78FB01B0AA9F158F1B2F16192FC5
+:1013B0000A007BE30A7BE905281205785B01C0F18E
+:1013C00065F0E2AADE8B352F1219291210C050AF3A
+:1013D0009F79FB01B1EE9F11C0F075E30A7E5905BC
+:1013E00028120178BB01C0F164FCEA6000B700007C
+:1013F0007FB30263FEF663FEF17FB30263FC4563D5
+:10140000FC4000006450A4DA205815C8C020D10F59
+:10141000C09163FE43C09163FA73DA20DB70C0D1E0
+:101420002E0A80C09A2924682C70075814B8D2A0BC
+:10143000D10F000019ED6603480B9810088B0209C4
+:1014400029087983022B8DF829121A63FA8B000080
+:101450002A2C74DB40580E442E221D2A221E63FBC8
+:101460000FC09163FCE5022A0258024C0AA2020650
+:101470000000022A025802490AA202060000DB709C
+:10148000DA20C0D12E0A80C0CE2C24682C700758D8
+:10149000149FC020D10FD9A063FCB600C09463FC98
+:1014A000AAC09663FCA5C09663FCA0002A2C74DB3E
+:1014B00030DC405BFE2EC2D02DA4002B200C63FF3D
+:1014C000458F358EA77FEB0263FEBB63FD548935E4
+:1014D00088D7798B0263FEAE63FD47006C1004C0B1
+:1014E00020D10F006C1004C020D10F006C10042B11
+:1014F000221E28221DC0A0C0942924062A25027B72
+:101500008901DBA0C9B913ED24DA2028B0002CB082
+:101510000703880A28824CC0D10B8000DBA065AF8E
+:10152000E7C020D10F0000006C100602260229201F
+:1015300006C0E0689805289CF96581202A61021799
+:10154000ED170A0A4C65A0F02B729E1AED136FB8C6
+:10155000026000F42AA22668A0098B60D30F0ABBA0
+:101560000C65B0E42A729D64A0DE2B600C0CBC11EB
+:1015700007CC082DC2866FD9026000D71DED090D7A
+:10158000BD0A2DD2A368D0078F600DFF0C65F0C394
+:1015900022C285C0F06420BB1DED0E68434D18EDDE
+:1015A0000D8C6B08CC029C208A6008AA110DAA023F
+:1015B0009A21896A9924883298252C610408CC11D3
+:1015C0009C271CECFE0CBA11A7AA29A285ACBC2F43
+:1015D000C4CF299C2829A685C85A2A6C74DB405898
+:1015E0000DE2D2A0D10FD2E0D10F0000289CF96407
+:1015F00080912C60668931B1CC0C0C472C64666FED
+:10160000C669709E661AECF48C30896B0C8C400BAA
+:10161000CC100C99020A9902992088600888110D53
+:10162000880298218C339C238A329A22896A9924D1
+:101630008834982563FF820000CC57DA60DB30DC09
+:10164000405814A7C020D10F00DA60C0B658153733
+:1016500063FFE500DA6058153563FFDC00DA20DB54
+:1016600030DC40DD505815B7D2A0D10F9E102B6151
+:10167000045813C81DECD78E102B600CC0F02F64DB
+:101680006663FF80296123C08879830263FF752E1A
+:1016900016002C60662B61042CCC010C0C472C64CA
+:1016A000665813BC1DECCB8E102B600CC0F02F6461
+:1016B0006663FF506C1004C0B7C0A116ECC815ECEF
+:1016C000BAD720D840B822C040053502967195702F
+:1016D00002A438040442C94B1AECAD19ECAE29A699
+:1016E0007EC140D30F6D4A0500808800208C220AFD
+:1016F00088A272D10FC05008A53875B0E363FFD738
+:101700006C100893149412292006655270C07168F9
+:1017100098052A9CF965A28016ECA12921028A1459
+:1017200009094C6590C78AA00A6A512AACFD65A0D8
+:10173000BCCC5FDB30DA208C12581469C0519A148B
+:10174000C7BF9BA98E142EE20968E0602F629E1D20
+:10175000EC926FF8026000812DD22668D0052F220E
+:10176000007DF9752C629DC79064C06D9C118A1430
+:101770002B200C2AA0200CBD11A6DD0A4F14BFA8F7
+:1017800009880129D286AF88288C09798B551FECEE
+:10179000840FBF0A2FF2A368F0052822007F894337
+:1017A00029D285D49065907760003D00002B200CF5
+:1017B0001FEC7C0CBD11A6DD29D2860FBF0A6E96E8
+:1017C000102FF2A368F00488207F890529D285654F
+:1017D0009155DA205814D5600013DA20C0B6581499
+:1017E000D3600009C09063FFB9DA205814D089147F
+:1017F000899109FE506551CC8C128D14DA20DBD012
+:101800008DD09E100D6D515813419A1464A1F0C7EC
+:101810005F8FA195A9C0510F0F479F1263FEFB0078
+:10182000C091C0F12820062C2066288CF9A7CC0C8A
+:101830000C472C24666FC6088D148DD170DE01C054
+:1018400090DD90648141C9D28A112B210458135133
+:101850008A14C0B02B24668EA92AA020C1701CEC6B
+:101860005B0E281415EC508E148556AC2C9C132E50
+:10187000EC28A855DDE07CE3022DEDF8D3D00A7703
+:1018800036DA40DB50DC305BFF8BD4A02E200CB46A
+:1018900055290A88C0C01BEC490CEF11A6FF28F29D
+:1018A00085ABEE2CE4CFA98828F685290A80881319
+:1018B000A933DD3089147833022D3DF8289020D3E8
+:1018C000D007880CC170080847289420087736652F
+:1018D0007FAE891413EC438990C0D477973D18EC00
+:1018E00041C1BA2721048514099C4006CC118653B6
+:1018F00004771185520C77020B7702C0C098A09D27
+:10190000A18D2B9CA597A496A795A603DD029DA269
+:101910002BF2852CE4CF2BBC202BF6852A2C748B44
+:1019200012580D11D2A0D10F28203DC0E07C87773E
+:101930002E24670E0A4765A07318EC2B8F201CEC31
+:10194000198E148CC48EE408FF1108FF020E8E1449
+:10195000AECC1EEC269F910ECC029C90C0801EEC5B
+:10196000242C21021AEC162FD285AABAB8FF2FD642
+:10197000850ECC0228A4CF2C2502C020D10F8714BD
+:10198000877007074763FD86282123C099798B025A
+:1019900063FEB2DDF063FEAD00DA20DB308C12DDD9
+:1019A000505814E8D2A0D10FC0E163FF828B148C91
+:1019B00012DD50C0AA2E0A802A2468DA2058135358
+:1019C000D2A0D10F007096552B629E6EB8531DEBBE
+:1019D000F22DD22668D0048E207DE9452A629DCB67
+:1019E000AF2B21042C20665812EBC090292466826C
+:1019F0001418EC008F2108FF019F21C020D10F0097
+:101A00008B10C9B88CA00C6C51CCCC8E241FEBEE83
+:101A10008DE19E140FDD029DE18810658FA9C02025
+:101A2000D10FDA20C0B6581441C020D10F000000F9
+:101A30006C1006C0D02A2102941175A70E89347F3C
+:101A400097098B357FBF042D2502DAD00A0C4C652F
+:101A5000C17B16EBD21FEBD028629EC0EA78E3026E
+:101A600060018129F2266890078A2009AA0C65A1E5
+:101A7000732A629DDEA064A1702B200CC0700CBC88
+:101A80001106CC0829C286280A0C79830260014C11
+:101A900019EBC409B90A2992A3689007882009881C
+:101AA0000C65813824C2851CEBC664412F8931093D
+:101AB0008B140CBB016FB11D2C20669E10B1CC0C99
+:101AC0000C472C24666EC60260013409FE5065E1A5
+:101AD0002E8A102AAC188934C0E47F973617EBC7DA
+:101AE000C0821BEBC58C359E419B408B2098459D49
+:101AF0004418EBC307BB029B420C07400F771198B9
+:101B0000439747D7E07FC70B2C2102284A0008CC17
+:101B1000022C25027E97048B362B25227D97048C80
+:101B2000372C25217C973A0AAB022C0A01C0800A87
+:101B3000C8382A3C200808426480821CEB9419EBC8
+:101B40009529C67E00A08800B08C00A08800B08CCB
+:101B500000A08800B08C28629D2DF4A2288C182843
+:101B6000669D89307797351FEB9F8C338832047BD5
+:101B70000B2A2104B47704AA119EB19FB08F2B9D2C
+:101B8000B598B69CB718EB96099C4006CC110CAAE8
+:101B90000208FF029FB2C1CC0CAA029AB4C9772AEC
+:101BA000200C1BEB860CA911A699289285ABAA2DB7
+:101BB000A4CF08780B289685CF58C020D10FC087B6
+:101BC000C0900AC93879880263FF7863FF6CCC57EC
+:101BD000DA20DB308C11581342C020D10FDA2058A4
+:101BE00013D363FFE8C0A063FE89DA20C0B65813A0
+:101BF000CF63FFD92A2C748B11580C5BD2A0D10F64
+:101C00008A102B21045812631FEB64C0D02D246668
+:101C100063FEBD006C1006D62019EB5F1EEB612839
+:101C2000610217EB5E08084C65805F8A300A6A51D2
+:101C300069A3572B729E6EB83F2A922668A0048C27
+:101C4000607AC9342A729D2C4CFECAAB2B600CB64C
+:101C50004F0CBD11A7DD28D2860EBE0A78FB269C4C
+:101C6000112EE2A32C160068E0052F62007EF91504
+:101C700022D285CF2560000D00DA60C0B65813ABC4
+:101C8000C85A60010F00DA605813A8655106DC409D
+:101C9000DB308D30DA600D6D5158121CD3A064A07A
+:101CA000F384A1C05104044763FF6D00C0B02C60F1
+:101CB000668931B1CC0C0C472C64666FC6027096F5
+:101CC0000A2B6104581233C0B02B64666550B42AE5
+:101CD0003C10C0E7DC20C0D1C0F002DF380F0F425B
+:101CE00064F09019EB2A18EB2B28967E8D106DDA94
+:101CF0000500A08800C08CC0A089301DEB3A779702
+:101D00005388328C108F3302CE0BC02492E12261B3
+:101D1000049DE00422118D6B9BE59FE798E61FEB85
+:101D2000300998400688110822020FDD02C18D9DFE
+:101D3000E208220292E4B4C22E600C1FEB200CE8F1
+:101D400011A7882C8285AFEE0C220B2BE4CF2286C4
+:101D500085D2A0D10F28600CD2A08C1119EB180CE1
+:101D60008D11A7DD2ED285A9882B84CF0ECC0B2C0C
+:101D7000D685D10FC0F00ADF387FE80263FF6C63BD
+:101D8000FF6000002A6C74C0B2DC20DD40581211E4
+:101D9000C0B063FF63C020D10F0000006C10042CA2
+:101DA000221D2A221EC049D320293006243468C0AF
+:101DB000407AC105DDA060000200C0D06E9738C037
+:101DC0008F2E0A802B3014C0962934060EBB022EAB
+:101DD00031022B34147E8004243502DE407AC10E99
+:101DE000C8ABDBD0DA302C0A00580A792E31020E4B
+:101DF0000F4CC8FEC020D10F6895F82831020808A2
+:101E00004C658FEF1AEAE61CEAE42BA29EC09A7B8F
+:101E10009B462BC22668B0048D307BD93B29A29DFE
+:101E2000C0E3CB9394901BEAF72D31049B9608DD19
+:101E3000110EDD029D979D9124C4A212EAF32F3169
+:101E40000228A29DC0E52E3406288C3028A69D02CB
+:101E5000FF022F3502C020D10FDA30C0B65813333D
+:101E6000C020D10F6C10062A2006941168A80528FE
+:101E7000ACF965824F29210209094C659206CC5FB5
+:101E8000DB30DA208C11581296C051D3A0C7AF9A1C
+:101E90003AC0E019EAC31DEAC91AEAC28F3A16EA43
+:101EA000BFB1FB64B1352C629E6FC8026001E927A7
+:101EB000DC33277226687007882007880C6581D874
+:101EC00024629DC0766441D02B200C0CBC11A6CCA2
+:101ED00028C286C09E7893026001C819EAB109B988
+:101EE0000A2992A39410689007882009880C6581BC
+:101EF000B224C2856441AC292006299CF96491DF93
+:101F00002C20668931B1CC0C0C472C24666EC6029D
+:101F100060019809F8506581921AEAA38C3619EA93
+:101F2000A10C881489940C0C4709CC10A8990A9923
+:101F30000218EAB62A210499409841882A19EAB47D
+:101F40000C8802098802984228302C2C3013293042
+:101F50001204CC100699100C88109F4408A8020C9B
+:101F6000990209880298438F379F458C389C46898F
+:101F700039C0F1994719EAA788359F4B98480888D6
+:101F800014098802984A8F3018EA9777F732C0749C
+:101F900089328C33984C974D0F9740882B2E4611E1
+:101FA0000677112C461329461204AC1119EA8D0745
+:101FB000CC02C179098802984E07CC022C4610C089
+:101FC0007AADBC0CBA11A6AA29A2852EC4CF097974
+:101FD0000B29A6856550FCC020D10F002B200C0CCE
+:101FE000BC1106CC082FC28609B90A6FF90260013C
+:101FF0001E2992A36890082F220009FF0C65F10F9B
+:102000002FC28564F10928203D082840648084841B
+:102010003504841464407C85A57453778436048425
+:102020001464406F74536C293013C08C798864C079
+:10203000902924670908476580DD882089A48435B4
+:102040001AEA6B048414A4940A440294F014EA6615
+:1020500008881104880298F1843698F3048414A443
+:10206000990A990299F21AEA6229210224C285ADDD
+:10207000B82E84CF244C1024C6850A990229250243
+:10208000C020D10F00CC57DA20DB308C115812144D
+:10209000C020D10FC09163FF97DA20C0B65812A3B9
+:1020A00063FFE100DA205812A163FFD88A102B21C8
+:1020B000045811381DEA422B200CC0E02E24668FF4
+:1020C0003A63FE5400DA20DB30DC40DD5058131D4B
+:1020D000D2A0D10F2A2C748B11580B23D2A0D10F70
+:1020E000292123C08879830263FE2D2A12002C2027
+:1020F000662B21042CCC010C0C472C24665811258E
+:102100001DEA2F2B200CC0E02E24668F3A63FE08B8
+:10211000DA2058128663FF6CDA205BFF20D2A0D150
+:102120000F0000006C100A9516C061C1B0D9402A9A
+:10213000203DC0400BAA010A64382A200629160750
+:1021400068A8052CACF965C33B1DEA16C8442F12DC
+:102150000664F29B2621021EEA12961406064C65BE
+:1021600062DE15EA0E6440CF8A352930039A150ADB
+:10217000990C6490C22C200C8B159C120CCC11A5D0
+:10218000CC9C132CC286B4BB7CB3026002CE8F12EF
+:102190000EFE0A2EE2A368E0082622000E660C65F9
+:1021A00062BA88132882856482B2891564905EDAE7
+:1021B00080D9308C201EEA0C1FEA0D1DE9FA8B1520
+:1021C0008DD4D4B07FB718B88A293C10853608C69C
+:1021D000110E66029681058514A5D50F55029580CE
+:1021E0000418146D8927889608CB110888140EBB33
+:1021F00002A8D8299C200F88029BA198A088929BB6
+:10220000A3088814A8D80F880298A22AAC10C0D0BE
+:102210008A151FE9EA8E1219E9F68B1388142CB27D
+:1022200085098802AFEE0CAA0B2DE4CF2AB68528CB
+:102230002502C020D10F000026529E18E9D76F68F2
+:102240000260020D2882266880098920D30F089930
+:102250000C6591FD2A529DC0FD9A1164A1F32B20BB
+:102260000CC1CA0CB8110588082D82860EBE0A7DE5
+:10227000C30260020A2EE2A368E0082622000E666E
+:102280000C6561FB288285DE806482072920069820
+:1022900010299CF96492042C20668831B1CC0C0C76
+:1022A000472C24666EC6026001BD08FD5065D1B79B
+:1022B00017E9DA1CE9BD19E9C42A21048B2D28305D
+:1022C000102D211D0C88100BDB090A8802098802D9
+:1022D0000CBB0264415589109B909791989284356C
+:1022E000D9E064406FD730DB40D8307F4715273CBA
+:1022F00010BCE92632168C3996E69CE78A37283CD2
+:10230000042AE6080B131464304A2A821686799A46
+:102310009696978C778A7D9C982B82172C7C209A96
+:102320009A2A9C189B99867BB03B298C086DB92111
+:102330008BC996A52692162AAC18B8999BA196A08F
+:102340008BC786CD9BA22B921596A49BA386CB2CE4
+:10235000CC2026A605C0346B4420043B0C0448095D
+:102360000E880A7FB705C0909988BC88C0900B1A68
+:10237000126DAA069988998B288C181CE9A81BE96C
+:10238000A816E99DB1DD2A211C23E6130D0D4F2669
+:10239000E6122D251DC06087207DA907C0D0280A20
+:1023A0000028251D26E6172CE6162BE61505D81164
+:1023B0001AE99328E6180A7A022AE614292006293F
+:1023C0009CF96491062A200CC0901BE97C0CAD118D
+:1023D000A5DD2CD285ABAAC0B029A4CF0CFC0B2C58
+:1023E000D685DA208C172D120658110DD2A0D10FE8
+:1023F0008A356FA546D8308BD56DA90C8A860A8A96
+:1024000014CBA77AB335288C10C080282467080B1A
+:102410004765B11CDA20DB308C17581131D3A0C0CE
+:10242000C1C0D02DA4039C1663FD280086366461CC
+:102430001689109B909791989263FEA1C08163FFCB
+:10244000C98A16CCA7DA20DB308C17581125C0209A
+:10245000D10FDA20C0B65811B563FFE400DA208B43
+:10246000125811B263FFD9009F189E198A112B21AF
+:10247000045810488E198F18C0B02B246663FE2FA5
+:10248000C08063FE01DA20DB308C17DD5058122D3E
+:10249000D2A0D10FDA205811A563FFA42D2123C0AB
+:1024A000C87DC30263FE089F188A112B21042C20CB
+:1024B000669819B1CC0C0C472C24665810368E192E
+:1024C0008F18C0D02D246663FDE50000262123B0BF
+:1024D0006606064F262523656EEA28206A7F870553
+:1024E0000829416490FDC0D01BE93F19E94E262020
+:1024F0000723E61BB166097A022BE61A28200A2D6B
+:10250000E61D2AE61E09880228E61C8826060647DC
+:1025100028E6208B2826E53E2BE6212D24072C20BB
+:10252000062A20642CCCFD64C09DB4FF63FE950098
+:1025300000DB30DA208D16C0CE2E0A802C24688C69
+:1025400017581072D2A0D10F8E102632161FE9151F
+:102550000626148FF62BE61297E127E61328E614D9
+:10256000A6FF0CFF029FE0C1F62EEC4826ECC064EB
+:102570006D6B8435C080644DDBD9E0DC30DBE0DDA1
+:1025800030B18814E92986C98AC8279DFF2CCC1050
+:10259000299C102A76322A76300464011AE9242410
+:1025A0007631AA442476332AD21617E9219AB6073F
+:1025B000660196B784C3BCBB94B58435B4DD74831F
+:1025C000BF2D211D63FD8D0064AF5E1DE8F62C203C
+:1025D000168DD20ACC0C00D10400CC1AACBC9C29BC
+:1025E00063FF46002B21046EB8222C2066B8CC0C69
+:1025F0000C472C2466C9C49F189E198A11580FE5F0
+:102600008E198F18C0348720C0D02D2466C068264C
+:10261000240663FED00000006C1008292006289CC8
+:10262000F86582C3292102C0AA09094C6590E62BEE
+:10263000200C16E8DA0CBC11A6CC2EC286C1D27EC4
+:10264000D30260028E19E8D609B90A2992A32A1684
+:10265000026890078A2009AA0C65A27729C28564BE
+:1026600092712B629E1AE8CC6FB80260026E2AA2A9
+:102670002629160168A0082B22000ABB0C65B25C53
+:1026800029629DC18C6492542A21200A806099108D
+:102690002C203CC7EF000F3E010B3EB1BD0FDB39D4
+:1026A0000BBB098F260DBD112DDC1C0D0D410EDD60
+:1026B000038E27B1DD0D0D410FEE0C0DBB0B2BBCB6
+:1026C0001C0BB7027EC71C2C21257BCB162D1AFCB8
+:1026D0000CBA0C0DA16000093E01073EB1780987D4
+:1026E000390B770A77EB026002062B212328212180
+:1026F000B1BB0B0B4F2B25237B8B29B0BD2D252385
+:10270000C855DA20DB30580FF0292102CC96C0E8FA
+:102710000E9E022E2502CC57DA20DB30DC4058100A
+:1027200070C020D10F2C20668931B1CC0C0C472C05
+:1027300024666EC6026001CF09FD5065D1C92F0A1B
+:10274000012830112922146480112A221B090C440B
+:1027500000C10400FB1A0BAA022A261B2D3010C050
+:10276000A0C0E088301BE88F94139514240A01253B
+:10277000203C2BB022088C14778704C0F10BFA3868
+:10278000C0F2C0840858010F5F010F4E3805354074
+:1027900007EE10C0F0084F3808FF100FEE0228DCDB
+:1027A000FEC0F0084F38842B0BA8100AFF102A2116
+:1027B000200F88020E880208440218E89E8F110834
+:1027C00044022821250A2A140828140488110A889A
+:1027D000022A210494F08E2004D41008EE1104EE95
+:1027E00002C04A04EE029EF1842A08AE110EDE02F7
+:1027F00094F40A54110E44020555100C1E4094F72F
+:1028000007EE100E5502085502C08195F68433C0BC
+:102810005094F3B1948E3295F898F99EF2C080C12D
+:10282000EC24261498FB9EF599FA853895FC843A99
+:1028300094FD8E3B9EFE883998FF853525F61084E1
+:1028400036851424F6118E3784132EF612C0E064F8
+:10285000B04989307797442B301088338C111FE8AA
+:10286000618D322FC614C0F42FC6158F2B2EC619BA
+:102870002DC61A28C61B04AD1109984006881108F8
+:10288000DD020DBB0218E856C1D00DBB0208FF02E5
+:102890002FC6162BC618280A0E2816022B200C88C5
+:1028A000121CE8460CB911A6992A9285ACBB2EB42D
+:1028B000CF0A880B289685C9718B268A2907BB0801
+:1028C0002B26060BAA0C0A0A482A2525655048C063
+:1028D00020D10F00DA2058109563FE3900DA20C0AD
+:1028E000B658109263FE2E00689738C020D10F00B2
+:1028F00000DA20DB7058104FC0C0C0D10ADA390AA4
+:10290000DC3865CDE463FE0D8A102B2104580F21BD
+:10291000C0E02E246663FE25DB402A2C7458091281
+:10292000D2A0D10FDA20580F2663FCF76C1004C038
+:1029300020D10F006C1004270A801CE83F1DE83FDF
+:102940001AE8170C2911AA992A2CFC2B92850DAA9A
+:10295000029CB19AB0C05113E83C28928516E83821
+:1029600014E839A62604240AB888289685234691B7
+:10297000A76625649FD10F006C100AD6302830104E
+:10298000292006288CF964829768980B2A9CF9659F
+:10299000A1B2022A02580F0A89371BE802C89164C3
+:1029A000520E2A21020A0C4C65C2558D3019E7FBE4
+:1029B00074D7052E212365E29A2F929E1AE7F76FAE
+:1029C000F8026002502AA22668A0082C22000ACC35
+:1029D0000C65C2412A929D64A23B9A151FE7F18DB6
+:1029E00067C1E6C8DD2B620618E7EF64B0052880F2
+:1029F000217B8B432B200C18E7E90CBC11A8CC29B8
+:102A0000C28679EB460FBE0A2EE2A368E0052F22AC
+:102A1000007EF9372CC2859C1864C22F2B212F878A
+:102A2000660B7B360B790C6F9D266ED2462C203DB3
+:102A30007BC740CE5560001E2A200CC1B28C2058A6
+:102A4000106F9A1864A2418D6763FFCFC0C063FF07
+:102A5000C5D7B063FFD300C0E06000022E60030E54
+:102A6000DB0C6EB20EDC700CEA11AA6A2AAC20589C
+:102A70000198D7A0DA20DB70C1C82D21205810158D
+:102A80008C268B279A160CBB0C7AB3348F1889636B
+:102A900099F3886298F28E659EF82D60108A189DD1
+:102AA0001768D729C0D09DA92C22182B22139CABC4
+:102AB0009BAA97A58E667E7302600097CF58600030
+:102AC0001FDA208B16580FDB65A13563FFBDC0816F
+:102AD000C0908F18C0A29AF999FB98FA97F563FFF6
+:102AE000D2DB30DA20DC40580F7EC051D6A0C0C007
+:102AF0002BA0102CA4039B172C1208022A02066B91
+:102B000002DF702D60038E179D149E100CDD11C026
+:102B1000E0AD6D2DDC205801178C148B16ACAC2C5D
+:102B200064038A268929ABAA0A990C9A2688660921
+:102B3000094829252507880C98662F2218A7FF2FFA
+:102B4000261863FE96DA20DB30DC40DD5058107D1D
+:102B5000D2A0D10FC0302C20668961B1CC0C0C47BB
+:102B60002C24666EC6026000CEC03009FD5065D0D0
+:102B7000C68E6764E069647066DB608C18DF70DAAB
+:102B8000202D60038E170CDD119E10AD6D2DDC2005
+:102B90001EE7A75800F8232618DA208B16DC402FF2
+:102BA0002213DD50B1FF2F2613580F1DD2A0D10FD5
+:102BB0000028203D084840658DE76F953EDA308D4E
+:102BC000B56D990C8CA80C8C14CACF7CD32D2AAC73
+:102BD00010C090292467090D4764DDC560008E0090
+:102BE0002C1208066B022D6C20077F028E17DA204C
+:102BF0009E101EE78E58007C63FF9A00C09163FF11
+:102C0000D1655080DA20DB60DC40580F35C020C031
+:102C1000F02FA403D10FDA20C0B6580FC463FFE031
+:102C2000006F950263FD70DA20DB30DC40DD50C4BC
+:102C3000E0580EB6D2A0D10F8A152B2104580E559C
+:102C4000232466286010981763FF2500DA20580FA8
+:102C5000B763FFACC858DB30DA20580E9B2A21023C
+:102C600065AF9DC09409A90229250263FF92DB305C
+:102C7000DC40DD50C0A32E0A802A2468DA20580EDA
+:102C8000A3D2A0D10FC020D10FDA202B200C580FD7
+:102C9000C063FF6C6C1004282006C062288CF865A5
+:102CA0008125C050C7DF2B221BC0E12A206B2921C0
+:102CB0002300A104B099292523B1AA00EC1A0BC462
+:102CC000010A0A442A246B04E4390DCC030CBB012D
+:102CD0002B261B64406929200C1BE7300C9A110B32
+:102CE000AA082FA2861BE72E6FF9026000B60B9B85
+:102CF0000A2BB2A368B0082C22000BCC0C65C0A430
+:102D00002BA2851CE75264B09B882B2F21040C88D2
+:102D10000298B08420C08508441108440294B1840C
+:102D20002A08FF1194B48E349FB79EB5C0401DE7AA
+:102D3000232EA2850D9D082EEC282EA68525D4CF06
+:102D400029210209094C68941A689820C9402A214F
+:102D50000265A00B2A221E2B221D7AB10265A079E2
+:102D6000C020D10F2C212365CFDE6000082E212149
+:102D70002D21237EDBD52B221E2F221D2525027B14
+:102D8000F901C0B064BFC413E7042CB00728B00039
+:102D9000DA2003880A28824CC0D10B8000DBA065B2
+:102DA000AFE763FFA62A2C74C0B02C0A02580D8D21
+:102DB0001CE7289CA08B2008BB1106BB029BA189A5
+:102DC0003499A263FF790000262468DA20DB30DC26
+:102DD00040DD50580FDCD2A0D10FDA202B200C5848
+:102DE0000F53C020D10F00006C1006073D14C080A7
+:102DF000DC30DB40DA20C047C02123BC3003283858
+:102E00000808427740022DDC016481571EE6E01974
+:102E1000E6E129E67E6DDA0500508800308CC0E0DE
+:102E2000C02025A03C14E6DFB6D38FC0C0D00F87EA
+:102E3000142440220F8940941077F704C081048243
+:102E400038C0F10B2810C044C02204540104FD38DE
+:102E500002520102FE3808DD10821C07EE100E6ED1
+:102E6000020EDD02242CFEC0E004FE380AEE100E35
+:102E700088020D88028DAB1EE6CF08D8020E8802AC
+:102E800098B0C0E80428100E5E0184A025A1250892
+:102E90004411084402052514045511043402C0816C
+:102EA0000E8E3994B18FAA84109FB475660A26A13C
+:102EB0001FC0F206261460000726A120C0F20626D5
+:102EC000140565020F770107873905E610077810AA
+:102ED00008660206550295B625A1040AE6110858AF
+:102EE0001108280208660296B7C0606440566490D4
+:102EF00053067E11C0F489C288C30B340B964598E3
+:102F000047994618E6B79F410459110E99021FE6EA
+:102F1000B5020E4708D8029F4098420E9902B43875
+:102F2000C1E00E990299442FA00C0CF91114E6A3EC
+:102F30001EE69BA4FFAE992E928526F4CF0E880B39
+:102F4000289685D10F2BA00C0CBE111CE69C1FE609
+:102F500093ACBBAFEE2DE28526B4CF0D3D0B2DE635
+:102F600085D10F00C08005283878480263FEA5632C
+:102F7000FE9900006C1006C0B0C0A6C0C06570F01D
+:102F80008830C0300887140888406580D3C0E0C00E
+:102F900091C0D4C08225203C0B3F109712831CC0E7
+:102FA000700858010D5D01089738C0800B983807EC
+:102FB0007710048810086802087702C0800D9838DE
+:102FC0002D3CFE0888100D9E388D2B0AEE1008EE61
+:102FD0000207EE020CB8100FDD02053B400EDD02C9
+:102FE0009D408920043D100899110D99022D21045E
+:102FF00009A90208DD119941872A05B9100D3D0282
+:103000000ABB110DBB0208770297442821258712BD
+:10301000082814048811071E4007EE100E99027547
+:10302000660926211F0626146000060026212006B8
+:1030300026140868029B47098802984629200CD26A
+:10304000C0C0800C9E111BE65D1FE654AB99AFEE2D
+:103050002DE2852894CF0DAD0B2DE685D10F000014
+:10306000001FE6502FF022C03065FF20C03163FF03
+:103070001BDD408E51CAE00E7836981008770CB2EE
+:10308000AAB1BB8F502DDC1098D99FD889538F528D
+:10309000991199DB9FDA7E830AB1CC255C10C9783F
+:1030A00063FFCF0088108D1108E70C9751AD8DD7C5
+:1030B000F078DB01B1F79D5397528830C030088714
+:1030C00014088840648EC565BFA163FF93000000AB
+:1030D0006C1004D720B03A8820C0308221CAA17475
+:1030E0002B1F2972046D080FC981C9928575B133F0
+:1030F000A2527A3B0C742B0963FFE90000649FEB3A
+:10310000D10FD240D10F00006C1008D630C070959E
+:1031100015DA408E3914E6239A1464E0026451F8FB
+:103120002920062A9CF865A25B2A21020A0B4C651D
+:10313000B21B2C320015E61974C7052D212365D367
+:10314000202E529E1AE6156FE8026002172AA22668
+:1031500068A0082B22000ABB0C65B2082E529D1DE8
+:10316000E61064E1FF8B3864B2299E16C8BC8D69F5
+:103170001EE60D64D0052EE0217BEB492E200C18B5
+:10318000E6070CEF11A8FF29F286C186798B4A1752
+:10319000E60407E70A2772A3687004882077893954
+:1031A00025F28564529E27212E07B73607B90C6F8A
+:1031B0009D01D7B089696E924228203D7B873C8A69
+:1031C00015CDAF600018C1B28C202A200C580E8B90
+:1031D000D5A064A2A88B6863FFCBC05063FFC3C0B7
+:1031E000E06000022E60030E9B0C6EB20EDC700CD1
+:1031F000EA11AA6A2AAC285BFFB6D7A0DA20DB70F6
+:10320000C1C42D211F580E338C268B27D4A00CBB94
+:103210000C7AB3258A63C0909A5388629958985261
+:103220008F659F598E679E5B8D6697559D5A8B68FB
+:103230007B7B748B15CEB360000DDA20DB40580D1C
+:10324000FD65A10963FFCC00DA20DB308C14580D3A
+:10325000A4D6A0C0C0C0D19D152CA403DA20DB6089
+:10326000DF70DC50C0E02560039E101EE5E60C5DBB
+:1032700011AD6D2DDC285BFF3F8E66A5A88F6728FA
+:103280006403AF7F77FB01B1EE9E669F678D268C4E
+:1032900029A4DD0DCC0C9D268B680C0C482C252513
+:1032A00007BB0C9B6863FEC32C20668961B1CC0C04
+:1032B0000C472C24666EC6026000B409FD5065D030
+:1032C000AECBBB8E69CBE7DB60DC50DF70DA201E53
+:1032D000E5E12D6003C08098100CDD11AD6D2DDC93
+:1032E000285BFF248B15C84F8A268929A4AA9A2611
+:1032F0000A990C09094829252565B13BC020D10F41
+:10330000DB602D6C28DF70DA20C0C01EE5D29C1077
+:10331000DC505BFEB563FFCB002D203D0D4D4065BD
+:10332000DDFD6FE522DA308F456DE90C8EAA0E8E39
+:1033300014C9E37EF3112AAC10C090292467090F49
+:103340004764FDDB60014100C09163FFED0088151B
+:1033500065814CDA20DB608C14580D61C020C09070
+:1033600029A403D10FDA20C0B6580DF063FFDE00A8
+:103370008A162B2104580C87C0A02A24668B686308
+:10338000FF3E0000002B9CF965B0C5DA20580C8C7C
+:1033900063FD95002B200C0CBA11A5AA2FA286C1A3
+:1033A000C27FC3026000FC0DB90A2992A36890078E
+:1033B0008C2009CC0C65C0EB26A2856460E52C202E
+:1033C000668931B1CC0C0C472C24666FC60270960E
+:1033D0000ADAE02B2104580C6F272466893077978E
+:1033E0004B18E57F1DE5808A328B33C0F42C210415
+:1033F000099E4006EE1104CC110ECC029F61C1E083
+:103400000ECC029D608F2B9A669B679C6497650823
+:10341000FF029F622F200C18E5690CFE11A5EE2D0E
+:10342000E285A8FF27F4CF2DDC202DE6858F1565DA
+:10343000F091C020D10F00002A2C748B1458064A3A
+:10344000D2A0D10F00DA20DBE0580DB863FEFE00F9
+:1034500000DA20DB308C148D15580E3AD2A0D10F33
+:1034600000008815C888DA20DB30580C972A210222
+:1034700065AEDAC09409A90229250263FECFDA20DD
+:103480002B200C580DC363FEC4272468DA20DB30E0
+:103490002C12042D12052E0A80580C9C63FC80000F
+:1034A000C020D10FDA20580DA18A15CDA1DA200352
+:1034B0003B022C1204580D0A27A403C020D10F0090
+:1034C000C020D10F2A2C748B14580627D2A0D10FFC
+:1034D0006C100E282102941008084C65835E1FE5CD
+:1034E0002F29F29E6F98026003621DE52B29D226D8
+:1034F0006890082A220009AA0C65A3502CF29D644A
+:10350000C34A2B200C0CB611AF66286286C1EC783A
+:10351000E30260034219E52209B90A2992A36890DF
+:10352000078A2009AA0C65A32E246285644328C05B
+:10353000E12A3109C07027246689359A12992A88B0
+:10354000369913982B89379814992C88389915082F
+:1035500058149816982D89392A25042E251D2925B9
+:103560001C283028C09228243C2A30290808479873
+:10357000170989012A243D2A311599180A09410998
+:10358000A90C299CEC29251F7E87192D2A000DA046
+:103590006000083E010A3EB1AD08DA390EAA110AF0
+:1035A000990C29251F27211F18E52C078160010888
+:1035B0003E000D3EB18A0DA8392D9CFC2D2520C161
+:1035C0009009883608DD1C08771C893D8A3C2E2628
+:1035D000132E26142E26152E246B2925222A25216A
+:1035E000282014C0A027252E2D252F0808432D2183
+:1035F0001C282414C07027252427252527252C279F
+:10360000261827261B2724672724682932112725F7
+:103610002399196ED2156D080AB1AA00A10400E918
+:103620001A7D9B0563FFEE00000089191DE4FDC0B3
+:10363000E0C0729B1D8813951B9F1F9C1E961C1C2F
+:10364000E50826203DC0F006054008DB14076601AA
+:103650000D8810C071057E38067F3885120BFF106B
+:1036600016E4E60AEE1096400FEE020D5511B0AFCB
+:1036700009FF110FEE021FE4F90855020FEE02C018
+:10368000F49F418A209F4996489C4B9B4E9D439EA8
+:10369000468D161EE4DA8B15C7CF9C4D9C4C9C457D
+:1036A0009C440BD8140EAE020DBB109E4A9E4208DD
+:1036B0005502954F0D181415E4CD0D88110588029B
+:1036C000984718E4E885262E46122E461A2E4622E2
+:1036D0002646102646182646202F46112F46192F1B
+:1036E00046212846242846262C46142C461595119A
+:1036F0008C1718E4DD0505488F1805551109064893
+:1037000028461389140E66110655020F7F390C3EA8
+:103710004002EE1017E4D60D99110C26400F6611E9
+:103720000B99020C0C408B1D01CC1006FF022746A2
+:103730001B294616C07029311616E4CD0ECC0205A1
+:10374000FF021EE49C15E4CB2F461726461CC0F052
+:10375000861C2F461D2F461F2F46272546230ECC9D
+:1037600002851B2C461E1EE4C41CE48E8F1F8CC7D2
+:103770002E4625ADCC28200629246A2431179C2DFD
+:103780002425238C1ECC81272407C0D77F97188E31
+:10379000110928419E2964808E644098C098094987
+:1037A000362D240660000B00644075C09809493628
+:1037B0002D240601C4042D0AA02E210428628508A8
+:1037C000EE11AD88286685863F843E2D321006486E
+:1037D0001898C300C40406461818E47804C45300BB
+:1037E000661106DD02A8B82784CF9DC409064E964F
+:1037F000C51DE4A305A61106440216E4A09DC0065B
+:10380000440294C2C04304EE029EC114E46326F253
+:103810009D2744A2266C1826F69D655042C020D1F3
+:103820000FC09063FF890000654F70C098C0E82EFC
+:10383000240663FF7D2D2406C09063FF75CC57DA04
+:1038400020DB308C10580C26C020D10F00DA20C0AD
+:10385000B6580CB663FFE500DA20580CB463FFDC01
+:103860002A2C748B10580540D2A0D10F6C1006285A
+:1038700020068A336F8202600161C05013E4472939
+:10388000210216E446699204252502D9502C201500
+:103890009A2814E4448F2627200B0AFE0C0477098B
+:1038A0002B711C64E1398E428D436FBC0260016F45
+:1038B00000E104B0C800881A08A80808D8029827B0
+:1038C0002B200668B32ECE972B221E2C221D011111
+:1038D000027BC901C0B064B0172CB00728B000DA71
+:1038E0002003880A28824CC0D10B8000DBA065AF82
+:1038F000E7C020D10F2D206464DFCA8B29C0F10BF3
+:10390000AB0C66BFC02B200C0CBC11A6CC28C28609
+:103910002E0A0878EB611EE4220EBE0A2EE2A3688E
+:10392000E0052822007E894F29C2851EE42E64907E
+:10393000461FE43C9E90C084989128200A95930FDE
+:10394000880298928E200FEE029E942F20078826E0
+:103950002F950A98969A972E200625240768E34308
+:103960002921021AE4162DC285AABA2DDC202DC603
+:103970008525A4CF63FF4E002B2065CBBDC0C22C94
+:103980002465C9F605E4310002002E62821FE41EA0
+:103990002D41020FEE022E66820DE43129210263D1
+:1039A000FF23000064DFB889422820160091040D2F
+:1039B000880C00881AA8A8982963FFA38C202D32B0
+:1039C00021B1CC9CD02B32212A3223B4BB2B3621FF
+:1039D0007BA9A92D32222D362163FFA0C020D10F53
+:1039E0009F27252415ACB828751C2B2006C0C12E96
+:1039F000BCFE64E0AB68B7772DBCFD65DEC72D204B
+:103A000064C0F064D0868E290EAE0C66E089C0F1E9
+:103A100028205A288CFE08CF3865FEE863FF58003E
+:103A200000E0049310C0810AF30C038339C78F08A8
+:103A3000D80308A80108F80C080819A83303C80C13
+:103A4000A8B828751C030B472B24158310CBB7008F
+:103A5000E104B0BC00CC1AACAC0CDC029C27659E27
+:103A60005EC0B20B990209094F29250263FE50007E
+:103A70002D206A0D2D4165DF7EDA20C0B0580C7212
+:103A800064AF18C0F163FEEF9F2763FFD02E221FA3
+:103A900065EE3263FF79000028221F658E2763FFE1
+:103AA0006E252406252502C09063FE196C1006655C
+:103AB00071332B4C18C0C7293C18C0A1C08009A87D
+:103AC000380808426481101CE3B11AE3B22AC67EAA
+:103AD0002A5CFDD30F6DAA0500B08800908C884049
+:103AE000C0A00889471FE3DB090B47084C50080DAD
+:103AF0005304DD10B4CC04CC100D5D029D310CBB21
+:103B0000029B3088438E2098350FEE029E328D2620
+:103B1000D850A6DD9D268E40C0900E5E5064E09782
+:103B20001CE3C11EE3B0038B0BC0F49FB19EB02D0C
+:103B3000200A99B30CDD029DB28F200CFF029FB4C6
+:103B40008E262D20079EB68C282DB50A9CB72924D9
+:103B5000072F20062B206469F339CBB61DE392238F
+:103B600020168DD20B330C00D10400331AA3C3B43A
+:103B70008D932922200C13E3911FE3880C2E11AFA3
+:103B8000EEA3222924CF2FE285D2A00FDD0B2DE654
+:103B900085D10F00B48C2E200C0CEB111FE3881D77
+:103BA000E37FAFEEADBB22B28529E4CF02C20B2288
+:103BB000B685D2A0D10F00002E200C0CEB111FE314
+:103BC0007F1CE376AFEEACBB22B28529E4CF028244
+:103BD0000B22B685D2A0D10FC0D00BAD387DC80264
+:103BE00063FEEC63FEE08E40272C747BEE12DA70ED
+:103BF000C0B32C3C18DD50580A77C090884063FE53
+:103C0000E3066E02022A02033B02DC40DD5058004C
+:103C1000049A10DB50DA70580454881063FEF600E2
+:103C20006C10082E3C18C0A092161FE3688C40AFA1
+:103C30002F0C8C47C02304CB0BDDB07FB3022DBD0E
+:103C4000F8D9D0C0B075C30260008D9F146D084FC5
+:103C50008D900D6D36ADAA0D660C0EB70B0EBF0A1A
+:103C60009DF0B877B89FD8F000808800708C9711CD
+:103C700087909810971568B12AB22277D32D889132
+:103C8000C0D0CB8F9890279C10007088971200F0BE
+:103C90008C9F139D916460A0C08108BB0375CB38D5
+:103CA00063FFA900B1222EEC1863FFCE85920D7739
+:103CB0000C86939790A6D67D6B01B15595929693FD
+:103CC0008942600017B3CC299C188814DD90789342
+:103CD000022D9DF8D9D063FFBB8942DA9085160C7E
+:103CE0000D472D44021BE36792319B3086437A9146
+:103CF000261BE3581EE36589500E660196350B9925
+:103D000002993288420A880C98428756A7A797568C
+:103D10008F44AFAF9F44D10F1BE34F895096350BB3
+:103D20009902993288420A880C98428756A7A79729
+:103D3000568F44AFAF9F44D10F894263FF9E00006E
+:103D40006C10061FE3529310D6308830C091086380
+:103D5000510808470598389811282102293CFD0888
+:103D6000084C6581576591542A62030A2B5065B14E
+:103D70007E0A68142E0AFF7CA60A2C205ACCC42D79
+:103D80000A022D245A78E0026002088A288926183F
+:103D9000E3400A990C6592032E200B1CE33F08EECA
+:103DA0000B2EED012DE0322EE03308DD110EDD0289
+:103DB0001EE339AFDD0EDD010DCC372D200C8960FF
+:103DC000C1E07B96231AE2F78B622AA0219C127B2A
+:103DD000A316DAD0C1B00B4B37B4BB8C20580B877D
+:103DE0008C12DBA0CEAB6001BF0E4E371BE2EC0C99
+:103DF000DA11ABAA28A286B8EE78EB351EE2E90EFE
+:103E0000DE0A2EE2A368E00488207E89242BA285A6
+:103E100064B0A28762DE700C79369B1388268D27EA
+:103E2000097A0C08DD0C6FAD0E77D3107E7B6960CC
+:103E3000001FC0B063FFD800D79063FFEB9C12DA7D
+:103E400020DB70580AFC8B138C1265A06F8E627E8B
+:103E50007B469C129B13CC5FDA208B10044C0258DB
+:103E60000AA0D6A08B13250A01DE70DA20DC60DD03
+:103E7000405BFF6B8C12D9A02D200CC0E01BE2C769
+:103E800017E2CF0CDA11A7D7ABAA2BA2852E74CFDD
+:103E90000B990B29A68563FF24DA20DC60DD40DE68
+:103EA000708911282007DF50A9882824075BFEFFAE
+:103EB000D2A0D10F0000DBD0DA20580B1C6550F3E4
+:103EC0002A20140A3A4065A0EEDB60DC40DD30DADF
+:103ED0002058098E1FE2EED6A064A0D784A183A04B
+:103EE0000404470305479511036351C05163FE68FD
+:103EF0002C200628CCFD6480A868C704C093292420
+:103F000006C0C18E6419E2A79E269E299E2889922A
+:103F10009E2700910400CC1A009004B0C88D65085B
+:103F2000EE01AECC0D0E5E01EE11AECCB0CC2E0A81
+:103F3000FE0C0C190ECC36C0E20C0C470ECC372C04
+:103F40002416C0B02B24072C20061BE29F0A0E4526
+:103F50000D084228240B2E240AB48929240C7DB88C
+:103F60005A2920160A5D52B09E0EDD362D24642B90
+:103F7000CCFD65BDFB0D0C4764CDF51DE28A88289C
+:103F80008DD20C9B0C00D10400BB1AAB889829631E
+:103F9000FDDE00001CE2B963FE2000001CE2AF63FE
+:103FA000FE188D6563FFA20000DA202B200C580A52
+:103FB000F8645F0BC020D10FC020D10FC093C0E3C5
+:103FC0002E241663FF9D00006C1004C06017E2727F
+:103FD0001DE275C3812931012A300829240A78A1FC
+:103FE00008C3B27BA172D260D10FC0C16550512607
+:103FF00025022AD0202F200B290AFB2B20142E204B
+:104000001526241509BB010DFF0928F11C2B2414CA
+:10401000A8EE2EF51C64A0B52B221E28221D01112E
+:10402000027B8901DB6064B0172CB00728B000DA8E
+:104030002007880A28824CC0D10B8000DBA065AF26
+:10404000E7DB30DC40DD50DA205800D829210209B6
+:104050000B4CCAB2D2A0D10F00CC5A2C30087BC175
+:10406000372ED02064E02D022A02033B02DC40DD23
+:10407000505800CED2A0D10F2B2014B0BB2B24144B
+:104080000B0F4164F0827CB7CAC0C10C9C022C2586
+:1040900002D2A0D10FC020D10F2E200669E2C12F7D
+:1040A00021020F0F4C69F1B82624062625022B2287
+:1040B0001E28221D2A200B2920150DAA092CA11C1F
+:1040C000262415AC9929A51C7B814A600049B0BB08
+:1040D0002B24140B0D41CBD67CB7022C25022B22AE
+:1040E0001E2E221D7BE9022B0A0064B0172CB0079C
+:1040F00028B000DA2007880A28824CC0D10B800043
+:10410000DBA065AFE7C020D10F262406D2A0D10FD7
+:1041100026240663FFC7DB601DE22364BF422CB088
+:104120000728B000DA2007880A28824CC0D10B800B
+:1041300000DBA065AFE71DE21B63FF246C100428C1
+:104140002006C0646F8564CA5B2920147D9726DA37
+:1041500020DB30DC40055D02580019292102090AE4
+:104160004CC8A3C020D10F00C0B10B9B022B25026D
+:10417000C020D10F0000022A02033B022C0A015882
+:1041800000C9C9AADA20DB30DC405809D529A011C2
+:10419000D3A07E97082C0AFD0C9C012CA411C051C1
+:1041A0002D201406DD022D241463FFA2DA20DB305B
+:1041B000DC40DD50C0E0580955D2A0D10F0000000E
+:1041C0006C100616E1F61CE1F665513BC0E117E103
+:1041D000F22821028B2008084C65807C29320009D6
+:1041E00069516993732A629E6EA8482A722668A054
+:1041F000027AB93F2A629DB44FCBA72B200C0CBD8D
+:104200001106DD0828D28678FB150CBF0A2FF2A311
+:1042100068F00488207F89072DD285D30F65D06090
+:104220002A210419E21ED30F7A9B1DDA2058085365
+:10423000600025002C21041BE2197CBB14DA20C08D
+:10424000B658084EC9546000EFDA20580A386000AA
+:104250000700DA20C0B6580A356550DCDC40DB3098
+:104260008D30DA200D6D515808A9D3A064A0C91C67
+:10427000E1CCC05184A18EA00404470E0E4763FF19
+:104280004F2B2104C08C8931C070DF7009F95009AF
+:104290008F386EB8172C2066AECC0C0C472C2466D9
+:1042A0007CFB099D105808BB8D1027246694D11EF5
+:1042B000E1D2B8DC9ED0655056C0D7B83AC0B1C084
+:1042C000F00CBF380F0F42CBF119E1B018E1B22862
+:1042D000967EB04BD30F6DBA0500A08800C08C2C21
+:1042E000200CC0201DE1B60CCF11A6FF2EF285AD2B
+:1042F000CC27C4CF0E4E0B2EF685D10FC0800AB846
+:104300003878D0CD63FFC1008E300E0E4763FEBDFE
+:104310002A2C742B0A01044D025808AE2F200C12CF
+:10432000E1A70CF911A699289285A2FF27F4CFD214
+:10433000A008480B289685D10FC020D10F0000009F
+:104340006C1004C060CB55DB30DC40055D02022AF6
+:10435000025BFF9B29210209084CC882D2A0D10F21
+:104360002B2014B0BB2B24140B0C41CBC57DB7EB19
+:10437000C0C10C9C022C2502D2A0D10F0000022A41
+:1043800002033B02066C02C0D0C7F72E201428316E
+:104390000126250228240A0FEE012E241458010CB0
+:1043A00063FFA300262406D2A0D10F006C100628BC
+:1043B0002102D62008084C6580992B200C12E17749
+:1043C0000CB811A2882A8286B5497A9302600093BC
+:1043D00019E17409B90A2992A36890082A620009B0
+:1043E000AA0C65A07E2882851CE17F6480759C8074
+:1043F000B887B14B9B819B10655072C0A7D97028BC
+:104400000A01C0D0078D380D0D42CBDB1FE1601EC5
+:10441000E1612EF67ED830D30F6D4A05008088000A
+:10442000908CC0802F30082F740029600C1AE16333
+:104430000C9D11A2DD2CD285C020AA992294CF0C0C
+:10444000BC0B2CD685D280D10FC0E0038E387EA065
+:10445000C363FFB7CC582A6C74DB30DC405807E1EB
+:10446000C020D10FDA605809B163FFE70000DD40DA
+:1044700085102A6C74C0B0DC705808562F30082F95
+:10448000740028600CC0F00C8B11A2BB29B28512FD
+:10449000E14B09590BA2822F24CF29B685D2A0D196
+:1044A0000F0000006C1004292014282006B199295F
+:1044B0002414688124C0AF2C0A012B21022C24066D
+:1044C0007BA004C0D02D2502022A02033B02044C2B
+:1044D00002C0D05800BFD2A0D10FC020D10F000021
+:1044E0006C1004293101C2B429240A2A3011C28374
+:1044F00078A16C7BA1696450472C2006C0686FC509
+:1045000062CA572D20147CD722DA20DB30DC40DD54
+:10451000505BFFA6292102090E4CC8E2C020D10F32
+:10452000C0F10F9F022F2502C020D10FDA20DB300F
+:10453000C0C05BFFDC28201406880228241463FF17
+:10454000C72920151BE1182A200BC0C09C240BAAE8
+:10455000092BA11C2C2415AB9929A51C63FF9900DC
+:10456000C020D10FDA20DB30DC40DD50C0E058083D
+:1045700067D2A0D10F0000006C1004CB5513E113DB
+:1045800025221F0D461106550CA32326221E252683
+:104590001F06440B24261E734B1DC852D240D10F58
+:1045A000280A80C04024261FA82828261E28261D49
+:1045B000D240D10FC020D10F244DF824261E63FF16
+:1045C000D80000006C1004282006D6206E850260FA
+:1045D00000DE17E0F21DE0F919E0F2C0C1C0202AA8
+:1045E0008CFC64A1322B6102B44E0B0B4C65B0A85D
+:1045F0002B600C2A62000CB8110788082F828609EC
+:10460000B90A7FE30260009F2992A368900509AA76
+:104610000C65A09328828564808DB8891BE0F7948F
+:10462000819B8065514DC0B7B838C0A1C0E009AECC
+:10463000380E0E4264E0481AE0D51FE0D62FA67E61
+:10464000B04A6DAA0500808800908CC0A02E600C36
+:104650000CE811A7882F8285ADEE0F4F0B2F8685B2
+:104660002B600622E4CF68B12A296015C0B2C99A2E
+:10467000D2A02D61022B64060CDD022D6502D10F44
+:10468000C0E008AE387EB0B763FFAB00226406D24C
+:10469000A0D10F00D2A0D10F00CC57DA60DB30DC04
+:1046A0004058088FC020D10FDA6058092063FFE816
+:1046B0000028221E29221D789902280A00C176C1ED
+:1046C000C1C1D21BE0C2C124AB6B6480437891406E
+:1046D0002A80000CAF0C64F0AE0DAE0C64E0A802B2
+:1046E000AF0C64F0A207AE0C64E09C2FACE864F061
+:1046F000962EACE764E0902FACE664F08A2A80073F
+:1047000008A80B088A027B83022A8DF8D8A065AF1F
+:10471000BBC09060007300002B600C0CB811A78820
+:104720002E82866EE87909BA0A2AA2A368A0048EAE
+:10473000607AE96B2A828564A0651FE0AAC0E32E37
+:1047400064069EA19FA01FE0D62E600A92A30FEEE2
+:10475000029EA28E600FEE029EA42F60147AFF4785
+:1047600022A4172F8285ADBE22E4CF2FFC182F86FE
+:104770008563FE702A6C74C0B1DC90DD40580795EB
+:104780001DE08FC0C163FEC4D9A0DA60DB30DC401D
+:10479000DD50C2F0C1E009FE395807DCD2A0D10FCC
+:1047A000DA605808E263FEF02CA4172982850DBE5A
+:1047B0000822E4CF299C1829868564500C2A6C7441
+:1047C000044B02580169D2A0D10FC020D10F0000C4
+:1047D0006C10062B221E28221D93107B8901C0B06D
+:1047E000C0C9C03BC1F20406401DE078C0E2C074FD
+:1047F0000747010E4E01AD2D9E11C0402E0A1464D4
+:10480000B06E6D084428221D7B81652AB0007EA110
+:104810003B7FA1477B51207CA14968A91768AA1456
+:1048200073A111C09F79A10CC18B78A107C1AE29DA
+:104830000A1E29B4007CA12B2AB0070BAB0BDAB0FF
+:104840007DB3022ABDF8DBA0CAA563FFB428B0106F
+:1048500089116987BB649FB863FFDC00647FB46320
+:10486000FFD50000646FD0C041C1AE2AB40063FF21
+:10487000C62B2102CEBE2A221D2B221E7AB12A8CE3
+:10488000107CB1217AB901C0B0C9B913E043DA2074
+:1048900028B0002CB00703880A28824CC0D10B80B6
+:1048A00000DBA065AFE7D240D10F8910659FD463CC
+:1048B000FFF300006C1008C0706451718F30292123
+:1048C000020F0F4716E036090C4C65C08F8D300D76
+:1048D0006E5168E30260008428629E1AE02F6E88A1
+:1048E000522AA22668A0048B207AB9472A629DB07A
+:1048F0004ECBAF9A102B200C9E110CBC11A6CC29CC
+:10490000C286B748798B4117E02607B70A2772A3FA
+:1049100068700488207789302CC2859C12D7C065C6
+:10492000C0622C21041AE05D7CAB22DA2058069389
+:10493000600029002E21041DE0597EDB18DA20C01A
+:10494000B658068EC958600156C0C063FFCCDA2045
+:10495000580876600006DA20C0B658087465513FE2
+:10496000DC40DB308D30DA200D6D515806E8D3A0E5
+:1049700064A12CC05129210284A18FA00404470FF7
+:104980000F4763FF412B2104C08C8931C0A009F976
+:1049900050098A386EB81E2C2066AFCC0C0C472C00
+:1049A00024667CAB109E142A12005806FA8E14C09E
+:1049B000D02D24668D30C0921BE010C1F87FD60C3C
+:1049C00087129B702976012F7408277C106550B7D9
+:1049D000B83AC0D7DC70C051C080075838080842C8
+:1049E0006480791DDFEA19DFEB29D67E6A420AD39B
+:1049F0000F6DE90500A08800C08CC0A08830C0926F
+:104A00007F863807E80B2F84089B809981B4E82CB7
+:104A1000200CC0901DDFEA0CC311A633223285ADF5
+:104A2000CC02820B29C4CF223685D2A0D10F8A3086
+:104A3000C0F17EAE3229210263FE88002C200C8E4C
+:104A400011C0B01DDFDE0CCF11A6FF22F285ADCC68
+:104A50002BC4CF02EE0B2EF685D2A0D10FC0800A58
+:104A6000583878D08663FF7A9F13DB30DA20C0C1D4
+:104A7000DD705BFF572921028F132A9CFE65AE4330
+:104A8000272502C09063FE3B9E142A2C74C0B1DC23
+:104A900070DD405806D08E141BDFD8C1F863FF5B71
+:104AA000C020D10F6C100628210216DFBC08084C6C
+:104AB00065821529629E6F980260021C19DFB72972
+:104AC00092266890078A2009AA0C65A20B27629D8E
+:104AD000C0CC6472032B21048E31C0A0DDA00EFE79
+:104AE000500ECD386EB8112C20662CCC010C0C4722
+:104AF0002C24667CDB026001EAC0C12930081BDF80
+:104B0000A96490972F0AFFC0D3B09E64E0FD68921D
+:104B10000E6450832A2C74DB40580093D2A0D10F2E
+:104B20002B200C2721040CBC11A6CC29C286280AF4
+:104B3000087983026001B919DF9A09B90A2992A399
+:104B40006890082E220009EE0C65E1A42EC285644F
+:104B5000E19E26200713DFA36E7B0260019A17DF18
+:104B60009A1FDFA319DFD0C0D228200A93E09DE16D
+:104B7000A9690F880298E22F90802A9480B1FF07DC
+:104B8000FF029FE31EDF8E2FC285AEBE0FDF0B2F0D
+:104B9000C6852AE4CF655F7BC020D10F2F30102956
+:104BA00030112E301300993200ED3264F0EE2A30CD
+:104BB000141FDFBD00AA3278EF050F9E092DE47F98
+:104BC0001EDFBB66A0050F98092A8480B4A718DFF2
+:104BD000B8C76F009104AE9EDDE000AF1A00C31AA3
+:104BE0006EE1052DB2000DED0C1EDFB208D81C06DB
+:104BF0003303AE882A848B2EB02E27848C03EE01DB
+:104C00000FEE022EB42E58018F63FF0429310829BC
+:104C100025042830142E3109B0886480A32E240A7C
+:104C2000C0812E30162CB4232E240BB4EF2F240C6D
+:104C30008C378B36292504DEB0DFC00F8F390E8EFE
+:104C4000390FEE0264EEC9089F1101C4048D380CBF
+:104C5000B81800C4040CBE1800EE110EDD02C0E34B
+:104C60000EFF021EDF879F719E701EDF848F2098CB
+:104C7000739D7405FF110BCD53C18098750FDD0234
+:104C80000EDD029D722A24661EDF442F629D2AE4F7
+:104C9000A22FFC182F669D63FE760000002F3012B5
+:104CA0001BDF8600FA3278FF050B980B2A847F669B
+:104CB000D0050B9A0B2DA4802A301100AA3263FF75
+:104CC000442F240A9E2B63FF56CC57DA20DB30DCBE
+:104CD00040580703C020D10F00DA20C0B658079310
+:104CE00063FFE500DA7058062BC0A02A246663FE35
+:104CF00007DA2058078E63FFCFB16928200A862083
+:104D0000090947991129240798107F812693E027E4
+:104D1000E50A9AE388109DE119DF628D11096F029F
+:104D20009FE42DE416098802C0D398E22A24076381
+:104D3000FE5100001FDF2B08691188118D2B93E0B5
+:104D4000098802C09F98E50FDD020478119DE2C03A
+:104D5000F49FE1C0D409880298E463FFCE0000000C
+:104D60006C1004C020D10F006C100485210D381187
+:104D700014DF088622A42408660C962205330B93C0
+:104D800021743B13C862D230D10FC030BC299921A5
+:104D900099209322D230D10F233DF8932163FFE372
+:104DA0006C100AD620941817DEFDD930B83898193F
+:104DB0009914655252C0E1D2E02E61021DDEFA0E56
+:104DC0000E4C65E1638F308E190F6F512FFCFD651E
+:104DD000F1568EE129D0230E8F5077E66B8F181E87
+:104DE000DF37B0FF0FF4110F1F146590CE18DF34BA
+:104DF0008C60A8CCC0B119DEE828600B09CC0B0D83
+:104E0000880929811C28811A2A0A0009880C08BAF5
+:104E1000381BDF2A0CA90A2992947B9B0260008C24
+:104E20002B600C94160CBD11A7DD29D286B84879E9
+:104E300083026000D219DEDA09B80A2882A3981723
+:104E40006880026000A36000A51ADF1E84180AEEC5
+:104E500001CA981BDED18C192BB0008CC06EB31325
+:104E60001DDECE0C1C520DCC0B2DC295C0A17EDBDD
+:104E7000AE6000380C0C5360000900000018DF1011
+:104E80008C60A8CCC0B119DEC428600B09CC0B0D16
+:104E9000880929811C28811A2A0A0009880C08BA65
+:104EA000380CA90A2992947E930263FF72DA60C0DB
+:104EB000BA58071E64507460026600001ADEB78C90
+:104EC000192AA0008CC06EA31A18DEB30C1C52085D
+:104ED000CC0B18DEFA2BC295C0A178B30263FF3F5A
+:104EE00063FFC9000C0C5363FF0989607899182986
+:104EF000D285C9922B729E1DDEA86EB8242DD226B3
+:104F0000991369D00C60000EDA6058070860001829
+:104F1000000088607D890A9A1A29729D9C12991551
+:104F2000CF94DA60C0B65807016551F48D148C181F
+:104F3000DBD08DD0DA600D6D51580574D3A09A1472
+:104F400064A1DD82A085A1B8AF9F190505470202C3
+:104F5000479518C05163FE602B6104C08C8931C035
+:104F6000A009F950098A386EB81F2C6066A2CC0CD3
+:104F70000C472C64667CAB119F119E1B8A1558054B
+:104F8000858E1B8F11C0A02A64669F1164F0E18991
+:104F9000138819DEF06DE9172F810300908DAEFEA6
+:104FA0000080889F9200908C008088B89900908C37
+:104FB00065514E8A10851A8B301FDE8A881229604F
+:104FC0000708580A2C82942D61040ECC0C2C869470
+:104FD0006FDB3C1CDEB4AC9C29C0800B5D50A299F9
+:104FE00009094729C48065D0DA2E600CC0D01FDEC5
+:104FF000730CE811A788228285AFEE02420B2DE4E4
+:10500000CF228685D2A0D10F8E300E0E4763FDA62B
+:10501000A29C0C0C472C64077AB6CD8B602E600ADC
+:10502000280AFF08E80C64810E18DE9D831682139F
+:10503000B33902330B2C34162D350AC02392319F1D
+:1050400030C020923308B20208E80292349832C08D
+:10505000802B600C286407D2A01CDE580CBE11A760
+:10506000EE2DE285ACBB28B4CF0D9D0B2DE685D18E
+:105070000F8B1888138D30B88C0D8F470D4950B4A5
+:10508000990499100D0D5F04DD1009FF029F800D3A
+:10509000BB029B8165508D851AB83AC0F1C0800C67
+:1050A000F83808084264806B1BDE3919DE3A29B6ED
+:1050B0007E8D18B0DD6DDA0500A08800C08CC0A020
+:1050C00063FEF3001DDE4B28600A82138B16C0E0DE
+:1050D0002EC48002B20B0D8802B2BB99239F20C060
+:1050E000D298229D2122600C0C2D11A7DD28D2859B
+:1050F00008BB0B18DE322BD685A8222E24CFD2A0D7
+:10510000D10F9E1B851A2A6C748B185BFF178E1BA0
+:1051100063FEA300C087C0900AF93879809263FFCC
+:1051200086C020D10F9E1B2A6C74C0B18D18580503
+:10513000298E1B851A63FE7E886B8213891608BE32
+:10514000110ECE0202920B9E25B4991EDE259F20E1
+:105150000E88029822C0EF04D8110E88029824C04D
+:10516000E49E21C080D2A02B600C2864071CDE13B3
+:105170000CBE11A7EE2DE285ACBB28B4CF0D9D0B64
+:105180002DE685D10F0000006C1004C020D10F0067
+:105190006C10048633C071C030600001B13300313F
+:1051A0000400741A0462017460F1D10F6C100402DF
+:1051B0002A02033B025BFFF61DDDFB1BDE43C79F9C
+:1051C00088B009A903098A019AB02BD10279801B02
+:1051D0001CDDF3C0E00EE4310002002AC2821DDEB5
+:1051E0003B0DAA022AC6820BE431D10FC1F00FBFDA
+:1051F000020F0F4F2FD5020FE431D10F6C100412A4
+:10520000DDE91ADDE6C0C00CE43100020029A2820B
+:1052100018DE311BDE2F2621020B990108660129B9
+:10522000A68226250206E43114DE2C15DE27236A29
+:10523000902326128550242611252613222C40D196
+:105240000F0000006C100816DE252B0A642C1AB41F
+:1052500017DDD51DDDD118DE220F2E110D2A11B25A
+:10526000E90EEE11A8A80E9911ADAAA799A7EE9E76
+:10527000169911ACAA9A152C80FF2A80FE288D0160
+:1052800029800108AA112880000CAA0208881109A7
+:10529000880208AA1CB88828160058085D297116CB
+:1052A0009A122916038A158B122AA0800BAA288B22
+:1052B00010580857D4708C168B1315DE0A0BAB28C8
+:1052C000235C419B142BC6282C308072C9158C148A
+:1052D0002A50C02B3A200CAA2858084DC0D10ADD0C
+:1052E000372D4540B444B255B2337639DAB2778FB0
+:1052F000118E168815B4EEB18898159E167FE9A414
+:10530000D10F00006C1004C021D10F006C1006C03A
+:10531000701ADDA21EDDAD1DDDB31CDDB51BDD9EEB
+:1053200013DDE1220A0818DD9F14DDEF28802E240A
+:1053300040002816006D2A70A349289080C0F164AF
+:1053400080598510004104C06100661A06550105A8
+:10535000F5390C56110A66082F62966EF74D0B5FF1
+:105360000A2FF22468F00812DDD102420872F93BDC
+:105370002362950C4202CB349232C0F29D309F31B1
+:105380000E8F029F33236295AB522F0A002F948019
+:105390002F24A0233C1023669513DDC2B17712DDC4
+:1053A000D2B144040442242400D10F00D10FD10F04
+:1053B0006C10041ADD792AA00058021B5BFFD3028F
+:1053C0002A02033B025BFFCF1BDD77C0C429B10279
+:1053D000C8AC0C9C020C0C4F2CB5020CE431D10F64
+:1053E0001EDD6FC08008E4310002002DE2821FDD67
+:1053F000800FDD022DE68209E431D10F6C10041517
+:10540000DD6716DD68C02002E4310002002452820C
+:10541000226102734F0602E431C020D10F18DDB4BF
+:1054200019DDB308280109490129568228650208B7
+:10543000E43113DDA9226C7023661DD10F0000003A
+:105440006C1004292006289CF96480A02A9CFD6524
+:10545000A0968A288D262F0A087AD9042B221FC8E5
+:10546000BD2C206464C0812E22090EAE0C66E0784B
+:105470002B200C1EDD4A0CBC11AECC28C28619DDD7
+:105480004878F3026000A909B90A2992A368900834
+:105490002E220009EE0C65E09729C2851FDD5264BB
+:1054A000908E9F90C0E41FDD5F9E9128200AC0E08F
+:1054B0009E930F8802989288200F880298942F203C
+:1054C000079A979D962F950A2E24072820062920B3
+:1054D0006468832F28C28512DD39288C20A2B22E61
+:1054E00024CF28C685C020D10FC020D10F2A206A22
+:1054F0000A2A4165AF55DA20C0B05805D364AFE839
+:10550000C021D10F649FCC1FDD272D20168FF209FB
+:10551000DD0C00F10400DD1AADAD9D2928C2851215
+:10552000DD27288C20A2B22E24CF28C685C020D10A
+:105530000FC021D10F0000006C1004260A001BDDF3
+:105540006D15DD1828206517DD15288CFE64809404
+:105550000C4D11ADBD2CD2F52BD2F42A51027CB1E9
+:10556000422A5102B4BB2ED2F72BD6F47BE9052B8D
+:10557000D2F62BD6F47CB92B29D2F629D6F529D62A
+:10558000F406E4310002002F7282C79F004104C07C
+:105590008100881A09880308FF012F76820AE43106
+:1055A000600000002624652BD2F48E5A2CD2F5B070
+:1055B000EE9E5A7BCB1629D2F62FD2F70CB80C09E7
+:1055C000FF0C08FF0C0F2F14C8F960002F0BCD0C37
+:1055D0000D2D14CED6C0E20EAE020E0E4F2E550289
+:1055E0000EE431D10FDB30DA205BFF951BDD426426
+:1055F000AF5D2A51020C4D11ADBD63FFA906E43128
+:105600000002002E72821FDD000FEE022E76820A4B
+:10561000E431D10F6C100416DCE115DCE2C030037C
+:10562000E43100020024628274472118DD33875A76
+:10563000084801286682CD7319DD310C2A11AA9918
+:105640002292832992847291038220CC292B5102C9
+:105650000BE431C020D10F001FDD2A2E51020FEEC6
+:10566000012E55020EE431B02DB17C9C5A225C60B3
+:1056700008DD112D5619D10F6C100A1ADCC71DDC7C
+:10568000C923A00019DD206F33791CDD07C0281560
+:10569000DD1F1EDD1D2B1C10D4B083E0005086954D
+:1056A0001000408A94186D2A4F0F35110C340924CC
+:1056B00040800A560A2862940D55092F51400F4424
+:1056C000110B440A08970C0F77368F400F7736225C
+:1056D000514107FF0C9F40A8772F62952766940FD2
+:1056E000980C0288368741078836B13308770CAFAB
+:1056F0008F2F66959741030342B13808084298E01E
+:10570000D10F00001CDD0314DD03BFC52442B564C6
+:105710003055C091C0D016DD000488432BC000C0B6
+:10572000406D393E00410400971A7780162FA295EC
+:105730008E50AFEE2EED2006EE369E502DA69560D3
+:10574000001A000077B00983509D5023A695600091
+:105750000223A295223D2006223622A695B144B40A
+:1057600055B8AA28C400D10F04884328C400D10F1B
+:105770006C100415DCEA13DCEAC04004E4310002DA
+:10578000008850CB815BFFBC1CDCE70C2D11ADCC3D
+:105790002BC2822AC28394507BAB142EC28429C2AE
+:1057A000850ABD0C0E990C0D990C09291460000591
+:1057B0000BA90C092914993015DC7B2A51020AE443
+:1057C000312A2CFC5800492B32001EDC742BBCFF04
+:1057D0002B3600CCB5C8A3D2A0D10F00D2A004E4D0
+:1057E000310002002DE2822C51022FBAFF0FDD01A1
+:1057F0002DE6820CE431D10F6C1004D10F000000B3
+:105800006C1004C020D10F006C100413DCC7C0D191
+:1058100003230923318DC0A06F34026000891BDC93
+:1058200061C7CF18DCC00C2911A988268283258284
+:105830008219DC5A7651442752002E8285255C0459
+:1058400025868275E9052582842586827659542627
+:105850008284D5602686822686830AE4310002008F
+:105860002392822FB10200210400D41A0C440304B5
+:1058700033012396820FE43160000200D7A07659ED
+:10588000220AE4310002002E928200210428B10293
+:1058900000DF1A0CFF030FEE012E968208E431D2CE
+:1058A00070D10F00D270D10FC020D10F6C1004DB6B
+:1058B00030862015DC39280A00282502DA2028B095
+:1058C000002CB00705880A28824C2D0A010B8000A5
+:1058D000DBA065AFE61ADC320A4A0A29A2A3C7BFD9
+:1058E000769101D10F2BA6A3D10F00006C1004C03C
+:1058F000D1C7CF1BDC2CC0A018DC280C2911A9882B
+:105900008785858419DC2677517986508E87B45532
+:10591000958475E9038586958477596C8F869F8574
+:105920009F840AE431000200239282B42E00E10435
+:105930002FB10200D41A0C44030433012396820FC2
+:10594000E4310AE43100020023928200D41A0C44AC
+:10595000030433012396820FE431D260D10F00009B
+:105960000AE431000200239282B42800810422B1AB
+:105970000200D41A0C440304330123968202E4315A
+:10598000D2A0D10FD6A07751D6D260D10F0000009F
+:105990006C1004270A801CDC281DDC281ADC000C93
+:1059A0002911AA992A2CFC2B92850DAA029CB19A46
+:1059B000B0C05113DC2528928516DC2114DC22A608
+:1059C0002604240AB888289685234691A76625646C
+:1059D0009FD10F006C100419DC550C2A11A9A9895C
+:1059E00090C484798B761BDC45ABAC2AC2832CC275
+:1059F000847AC1688AA02BBC30D3A064A05E0B2B34
+:105A00000A2CB2A319DC0F68C0071DDC49D30F7D37
+:105A1000C94AA929299D0129901F68913270A603BE
+:105A2000D3A0CA9E689210C7AF2AB6A32A2CFC5BEB
+:105A3000FFAFD230D10F000013DBEF03A3018C3195
+:105A40001DDBE00C8C140DCC012CB6A363FFDC0035
+:105A5000C020D10FDA205BFFCEC020D10FC020D1F3
+:105A60000F0000006C1004DB30C0D019DBCBDA2053
+:105A700028300022300708481209880A28824CDCA6
+:105A8000200B80001BDBC60C4A11ABAA29A284099B
+:105A9000290B29A684D10F006C1004C04118DBBF6C
+:105AA00017DBC10C2611A727277030A86625628650
+:105AB000007104A35500441A75414822628415DB25
+:105AC000E202320BC922882117DBBE088414074486
+:105AD00001754905C834C020D10FD10F1DDC15C098
+:105AE000B28E201FDBAD0E0E43AFEC0FEE0A2BC4BF
+:105AF000A02DE624C0202A62840809470A990B29B0
+:105B00006684D10FC020D10F6C1004DB30C0D018D8
+:105B1000DBA2DA2025300022300708580A28824C00
+:105B2000DC200B80008931709E121BDB9C0C4A111B
+:105B3000ABAA29A28409290B29A684D10F09C9522D
+:105B400068532600910418DB97C0A12F811200AA88
+:105B50001A0AFF022F85121EDB910C4D11AEDD2CAF
+:105B6000D2840C2C0B2CD684D10FC0811FDB8EB8B5
+:105B70009A0A0A4700A1042EF11200881A08EE02C0
+:105B80002EF5121DDB860C4C11ADCC2BC2840B2BD9
+:105B90000B2BC684D10F00006C1004DB30C0D01971
+:105BA000DB7EDA2028300022300709880A28824C60
+:105BB000DC200B80001CDB790C4B11ACBB2AB284BF
+:105BC0000A2A0B2AB684D10F6C1004C04118DB736B
+:105BD00016DB750C2711A626266030A872252286B2
+:105BE000006104A35500441A754108222284023240
+:105BF0000BD10F00C020D10F6C100415DBCE024971
+:105C000014295611245212C0730208430F88110040
+:105C1000810400361AC78F00771A087703074401FA
+:105C2000064402245612D10F6C1006C0B06E230237
+:105C30006000A264209D851013DBAA16DBBEC04065
+:105C4000A6BA2BA2AE0B194164905E68915568927A
+:105C50004A6893372930FF2830FE2AA2AA08881103
+:105C60000A0A4D2AACF20988027589442B3D0129A4
+:105C7000B0002BB0010899110B99027A9932B83310
+:105C80002B2A00B1447249B7600048007FBF051558
+:105C9000DBAA63FFBE253AE863FFB800253AE86354
+:105CA000FFB10000250A6463FFA9C05A63FFA40086
+:105CB00000705F082534FF058C142C34FE70AF0B88
+:105CC0000A8D142E3D012AE4012DE400DA405BFD2B
+:105CD0005D63FFA9D10FD10F6C10041ADB3219DB01
+:105CE0002F1CDB961BDB97C080C07160000D00008D
+:105CF0000022A430B1AA299C107B915F269286795C
+:105D0000C215C0206E62E96D080AB12200210400AC
+:105D1000741A764BDB63FFEE2292850D6311032527
+:105D200014645FCFD650032D436DD9039820B4225D
+:105D30000644146D4922982098219822982398248B
+:105D400098259826982798289829982A982B982C4F
+:105D5000982D982E982F222C4063FF971EDB10273A
+:105D6000E68027E681D10F006C1004C062C04112AA
+:105D7000DB0D13DB7423322D1ADB0819DB6E2AA02E
+:105D8000002992AE6EA30260009128ACFE090D407E
+:105D90002C1AC2C2BD0DCB392B25166480895BFF3E
+:105DA000A215DB691ADB142B3AE80A3A0158059868
+:105DB0002B21160ABB28D3A02B56005805AF8B50B9
+:105DC0000ABB082A0A005805AE15DB602D21022CFB
+:105DD0003AE80C3C2804DD022D25029C505805A60B
+:105DE0008B50AABBC0A15805A61CDB592D21020C63
+:105DF0003C2806DD0213DB572D25029C3058059EFA
+:105E00008B30AABBC0A258059E2A2102C0B40BAA9F
+:105E1000020A0A4F2A25025805B2D10F242423C3AF
+:105E2000CC2C251663FF760018DB4F1CDB4B19DBEF
+:105E30004C1BDB4A17DB1F85202E0AFD1FDB4B2D79
+:105E4000202E24F47A24F47E24F4820EDD0124F43E
+:105E5000862E0AF707552806DD02C0750EDD0105FE
+:105E60000506AB5BA959C0E8AC5C24C4AB0EDD02EF
+:105E700027C4AC2E0ADFA85527B4EC0EDD0124B4EC
+:105E8000EBC2E027942C0EDD0224942B2E0A800D09
+:105E90000D4627546C24546B0EDD022D242E63FE18
+:105EA000FC0000006C1004C3A0C0B35BFF53C04AE9
+:105EB00012DB21C380282616242617C3A1C0B35B9A
+:105EC000FF4EC03CC3A12A261619DAB6299020231A
+:105ED000261764908F2A0A322B0A015BFF47C3A260
+:105EE0002B0A015BFF45C3B22B2616232617C2AF30
+:105EF000C0B15BFF41C2FF2F2616C0EE2E2617C28F
+:105F0000D22D2616C0C82C26172426112A2212C7E5
+:105F1000B30BAA01C0B40BAA022A2612290AA1298E
+:105F20002616C182282617C0B32B26112E22121F37
+:105F3000DACA0FEE022E2612C3D62D26162A0AA280
+:105F4000C1C32C26175BFF2C2C0AA22C2616C1B528
+:105F50002B2617C2AB2A2616C09729261718DB0353
+:105F6000282610D10FC3A2C0B35BFF2363FF6E00CE
+:105F70006C10041CDACE1BDABB18DAFD17DAFE1639
+:105F8000DAFE15DAFEC0E0C0D414DACA1FDA8622BF
+:105F90000A082FF2006D2A36DAC0D9C07C5B020FE6
+:105FA000C90C1CDAC30C9C28A8C3A6C22A36802AB6
+:105FB0002584A4C2A7CC2D248C2B248A2B24872EA5
+:105FC000248BB1BB2E369F2C369E2C369DB1AC1C3B
+:105FD000DAA51BDAEBC0286D2A33DAC0D9C07C5BA6
+:105FE000020FC90C1CDAB30C9C28A8C3A6C22A361F
+:105FF000802B2584A4C2B1BBA7CC2D248C2E248B4E
+:106000002A248A2E369F2C369E2C369DB1ACC07920
+:1060100019DAA31BDADE13DADB1ADADB18DADD149D
+:10602000DAA416DADC04F42812DADC04660C0405BF
+:1060300006A252A858AA5AA3539B3029A500278428
+:106040008AC091C0A52A848C29848B17DAD518DAE6
+:10605000D3A75726361D26361E2E361F16DAD21324
+:10606000DAD2A65504330C2826C82E75002D54AC60
+:106070002E54AB2E54AA2326E62326E52E26E7D15E
+:106080000F0000006C100613DAAF17DAAA24723D75
+:106090002232937F2F0B6D08052832937F8F026386
+:1060A000FFF3C0C4C0B01EDA3FC061D940096939EE
+:1060B00029E4206E4401D6B0C328DFB026E42206CE
+:1060C0002F392FE421C0501FDAB919DAAA16DAAB3A
+:1060D00018DA7994102A72458DE017DAA59D111D02
+:1060E000DAB46DA94BD450255C037A5B18DE507589
+:1060F0006B052E12010E5E0C12DA6E02E2280111FF
+:1061000002AF2222D681D54013DA6A746B052512BC
+:106110000105450C035328B145A83EA932A73322F7
+:10612000369D22369E2436802B369F2BE48B2CE422
+:106130008C14DA8424424DC030041414C84C6D0809
+:1061400006B133041414C84263FFF20016DA13C414
+:1061500040C1580031041ADA13C0B193A200BB1A2F
+:10616000B0BB9BA318DA7829824D23824E28825334
+:106170007A871C2C64008C106FC4481EDA0A043D18
+:106180000C2DE51C2FE11D2DE51A2FE51BD10F006D
+:10619000C07212DA6882207E27DB03034F27640077
+:1061A0008810C0616F8430C0A00319140A54391AD2
+:1061B000D9FD04990C29A51C29A51D29A51A29A5D5
+:1061C0001BD10F001CD9F8053B0C2BC51C2DC11D84
+:1061D0002BC51A2DC51BD10F065439031E1404EE0E
+:1061E0000C2EA51C2EA51D2EA51A2EA51BD10F0009
+:1061F0006C10081AD9EC14DA4F13DA52C72FC050BA
+:1062000016DA6C2566A82566A92566AA2566AB223E
+:1062100036292B424519DA13D8101CDA66C0D49DF2
+:10622000119C100080890B990C99A02816025BFF25
+:10623000952A32E31FD9DC0A5A149AF42932E4B1C0
+:10624000990959140A990C99F52832E508581498B7
+:10625000F62E32CD0E5E142EF6075BFF455BFF1166
+:1062600022463B1CD9D02AC102C1B00BAA021BDABC
+:106270002A0A0A4F2AC5022B463A5804995BFEBAED
+:106280005BFE95C0B0861317D9C525362DC74EC005
+:10629000309414C05014D9CA60004300007F9F0F8F
+:1062A000B155091914659FF4C0500AA9027FA7EFE0
+:1062B00018D9BADA5008580A28822C2B0A000B8009
+:1062C00000005104D2A0C091C7AF00991A0A9903E7
+:1062D000291604CE33642063D3202B2007D6508C9C
+:1062E000142A72827CA85C18D9AC08580A28822C1F
+:1062F000DA500B8000D2A0643FDA8A310A8A140493
+:10630000AA01C82A2B22010B8B1404BB017AB940C5
+:10631000DDA06EA1081DD9A32DD2000DAD0CDB3080
+:10632000DC601AD9E318D99C0ADA2808680A1DDA51
+:106330001F28823CADAA0B8000652F9BD320C0B0E4
+:1063400063FF9B00CA5CB1550050040A091963FF42
+:106350004BDCB06EB1091CD9938CC0D30F0CBC0CB4
+:106360001DD9D41EDA120DCD28AEDD1EDA112DE6B0
+:106370008163FF9B7FA7CE63FF6C00006C10041B42
+:10638000D98727221EC08008E4310002002AB28289
+:1063900019D985003104C06100661A2991020A6A80
+:1063A000022AB68209E43115D9DF0C3811A8532826
+:1063B00032822432842A8CFC7841102921022A3628
+:1063C0008297A0096902292502D10F002B21022CF6
+:1063D00032850B6B022CCCFC2C368297C02B25020D
+:1063E000D10F00006C1006C0C71BD9681AD96A0DFE
+:1063F0004E11D72088208522D98005450B02820CBA
+:106400009572222CF4C8346F2E026000AB1FD96045
+:10641000A9E2AF7D72D334C93DC0212F0A00092FF4
+:10642000380F0F42C9F92AB67E6D4A050030880040
+:10643000908C22720002E20872D1749270D280D1E4
+:106440000FC05003253875C0DF63FFD9097D0CAF3D
+:10645000DD0DEE0C64304ED2300D3F1296112F162A
+:10646000002FFC100F4F36260A01250A0009653857
+:106470000505426450712AB67E6DFA050020880039
+:10648000908CC050A3D28910237C0C09440C290A9B
+:106490000103953805054264505A2AB67E6D4A05B7
+:1064A00000208800308CD280A7EABCAA9A70D10F55
+:1064B000D280BC7B9B70D10F00023F14C1D0D23080
+:1064C0000FDD0C0D4D36298D08C0F1250A0009F5A8
+:1064D00038050542CA582AB67E6DDA0500208800C4
+:1064E000908C897063FF2500C061C05003653875CA
+:1064F000C08663FF80C0D0029D387DC09F63FF9936
+:10650000C05003F53875C0D063FFCA006C1004D6C4
+:106510002068520F695324DA20DB30DC405800F049
+:10652000D2A0D10FDA20DB30DC405800ED9A2424D1
+:10653000240EC02122640FC020D10F00B83BB04C04
+:106540002A2C7489242D200E2E200FA4DDB1EE2ECE
+:10655000240FB0DD2D240E2890072D9003A488B0C1
+:1065600088B1DD2D94032894075BFF9E69511DC0FF
+:10657000E082242A600F18D9902A240329600E8F04
+:106580002029240708FF029F209E64D10FC020D13C
+:106590000F0000006C1004942319D988C0B3083A86
+:1065A000110BAA02992019D8FD9A2128929D16D87C
+:1065B000FAC0502564A2288C1828969DD10F00009F
+:1065C0006C1004282066C038232406B78828246667
+:1065D000D10F00006C1006035A0C0D36110D5C1122
+:1065E000D8208B2282210CBB0C06550F9B820232D5
+:1065F0000B928113D8E7D920A38F6450531CD8E3A2
+:10660000C0D71BD8E4A256C0E1290A0004E9380922
+:10661000094276F34A044302C99E2BC67E6DAA0541
+:1066200000208800308C8981A95909FA0C64A0796E
+:1066300099818A82C8ADD290D10FC06002E63876C7
+:10664000D0DA63FFD4C020BC89998199809282D12D
+:106650000F7F2304292DF8998165BFD963FFE500D9
+:10666000028F0CA3FF0F3312931003AA0CD340CB5D
+:106670009E2BC67E86106D6A0500208800308CBC7B
+:1066800082290A0004F308240A010349380909424F
+:10669000CA982BC67E6DAA0500208800308C0F5941
+:1066A0000CA989BC99998163FF87BC89998163FF93
+:1066B00080C06002E63876D0BA63FFB4C07002478B
+:1066C0003877D0D063FFCA006C100414D8C0C15210
+:1066D000A424C93E28221D738119292102CD932AA1
+:1066E000300075A912DA20DB302C3007C0D25801F7
+:1066F000C4653FDFD10F00002B300703BB0BDAB0BE
+:1067000074B3022ABDF8D3A063FFC6006C1004293D
+:106710002006C0706E9741292102C08F2A2014C024
+:10672000B62B240606AA022A241479800227250201
+:106730002A221E2C221D7AC10EC8ABDA20DB302C97
+:106740000A00033D025BF8226450752D21020D0DF5
+:106750004CC9D3C020D10F00002E9CFB64E0822FD7
+:1067600021020F0F4C65F0911AD88D1CD88B29A2ED
+:106770009EC08A798B5D2BC22668B0048D207BD9A0
+:106780005229A29DC0F364904A97901DD89E2E2155
+:10679000049D9608EE110FEE029E979E9127C4A2CB
+:1067A00018D89A2F21022BA29DC0E52E24062BBCBF
+:1067B0003008FF022BA69D2F2502C020D10F00001C
+:1067C000002F300068F938DA20DB30DC4058004414
+:1067D00063FF7700022A022B0A065800D4220A001F
+:1067E000D10F655010283000688924022A02033B2B
+:1067F00002DC4058003BC020D10FD270D10F000006
+:106800002A2C74033B02044C025BFEF663FF3B0040
+:10681000DB30DC402A2C745BFEF3C020D10F00007B
+:106820006C1004C83F89268829A399992609880CE9
+:10683000080848282525CC52C020D10FDB402A2C3F
+:10684000745BF949D2A0D10F6C1004D820D73082E4
+:10685000220D451105220C928264207407420B130D
+:10686000D84CD420A383732302242DF8858074513F
+:106870004CBC82C0906D081600408800708C77393F
+:1068800003D720C0918680743901D42074610263DB
+:10689000FFE2CA98C097C0411BD8CAC0A00B8B0C9E
+:1068A0000B4A380A0A42C9AA1DD8391CD83A2CD634
+:1068B0007EC140D30F6D4A0500208800308C978040
+:1068C000D270D10FBC8FC0E00F4E387E90E263FFD4
+:1068D000D6BC8292819280C0209282D10F000000AB
+:1068E0006C1006C0D71CD8291BD82B0D4911D720F6
+:1068F0002A221F28221D0A4A0BD28007860C2A76DC
+:106900001F266C80C8346F6E026000D02F0A801A78
+:10691000D82FA29EAA7A7EA33FC93FC0E1C05002F1
+:10692000E538050542CA552BC67EDB20D30F6D4ADC
+:106930000500308800B08C2E721DAE9E0EA50C6432
+:10694000508AD2802E761DC091298403D10FC05069
+:1069500003E53875D0D363FFCD15D81C027E0CA596
+:10696000EE643055DA300E351296129511255C1012
+:10697000054536C0619510C0500265380505426472
+:1069800050892BC67E8510D30F6D5A0500A0880054
+:10699000208CC0A1A3E2C05023FA8003730C03A58E
+:1069A00038AF730505426450722BC67E85110545CC
+:1069B0000C6D5A0500208800308CD280C0A10E9B3F
+:1069C0000CAB7BAFBB2B761D2A8403D10FD280C0CA
+:1069D000C1AF7D2D761D2C8403D10F0000063F141E
+:1069E000C1E0D2300FEE0C0E4E362A8D08C0F125D4
+:1069F0000A000AF538050542CA5C2BC67E6DEA0519
+:106A000000208800A08C22721D63FEFFC061C05070
+:106A100003653875D80263FF6B63FF65C05002A53C
+:106A20003875D08763FF8100C06003F63876D0CC1C
+:106A300063FFC6006C10042A201529201614D7D92C
+:106A40000A990CCB9D2E200B04ED092BD11C09BCFF
+:106A500036ACAA0CBB0C2BD51C0A0A472A2415CB32
+:106A6000A18B438F288942B0A800910400881AA8FE
+:106A7000FF0FBB029B278F260FB80C783B1AC02054
+:106A8000D10F0000292102C0A20A9902292502C0C3
+:106A900021D10F008B2763FFDC2BD11C0CAA0C0A21
+:106AA0000A472A2415ACBB2BD51CC9AE8B438C28B6
+:106AB0008F42B0AD00F10400DD1AADCC0CBB029BDF
+:106AC00027DA20B7EB580019C021D10F9F2763FFA9
+:106AD000EF0000006C100428203C64304705306053
+:106AE00000073E01053EB156076539054928C77FB5
+:106AF000A933030641076603B166060641A6337E45
+:106B0000871E222125291AFC732B1502380C0981B6
+:106B10006000063E01023EB12406423903220AD13A
+:106B20000FD230D10FC05163FFC000006C10041DA4
+:106B3000D79B27221EC08008E4310002002CD2829D
+:106B40001BD799003104C06100661A2BB1020C6C8E
+:106B5000022CD6820BE43119D81B0C3A11AA9328C7
+:106B600032829780253282243284B455253682754C
+:106B7000410A292102096902292502D10F2A21028D
+:106B80002B32830A6A022B36822A2502D10F00009B
+:106B90006C10041DD78219D78C27221EC08009775C
+:106BA0000208E4310002002CD2821BD77E0031049F
+:106BB000C06100661A2BB1020C6C022CD6820BE469
+:106BC0003119D8000C3A11AA9328328297802532C5
+:106BD00082243284B45525368275410B2A21020A5B
+:106BE0006A022A2502D10F002B21022C32830B6B63
+:106BF000022C36822B2502D10F0000006C10041BE2
+:106C0000D7670C2A11ABAA29A286B438798B221B2C
+:106C1000D76419D78B0B2B0A2BB2A309290868B0AC
+:106C20000274B90D299D0129901F6E920822A28538
+:106C3000D10FC020D10FC892C020D10FDA205BEF56
+:106C40003DC020D10F0000006C100414D75428421E
+:106C50009E19D7516F88026000B929922668900763
+:106C60008A2009AA0C65A0AB2A429DC0DC64A0A3BF
+:106C70002B200C19D74B0CBC11A4CC2EC28609B901
+:106C80000A7ED3026000992992A36890078D20099B
+:106C9000DD0C65D08B25C2856450852D2104C03064
+:106CA0006ED80D2C2066B8CC0C0C472C246665C021
+:106CB0007A1AD7521CD75B1DD7481ED74FC084986D
+:106CC000519E5089209D569D54935793559C530A2D
+:106CD00099021CD7BD1AD76499528F26995A985990
+:106CE0009E58935E9C5D935C9A5B0F0D4805DD1189
+:106CF0009D5FC0D81ED7320CB911A499289285AED9
+:106D0000BE23E4CF288C402896859F292D2406C0D9
+:106D100020D10F00CA32DA20C0B65BFF84C72FD162
+:106D20000FC939DA205BFF81C72FD10FDBD05BFEA3
+:106D3000192324662B200C63FF76C72FD10FC72F92
+:106D4000D10F00006C1004C85B29200668941C68F1
+:106D50009607C020D10FC020D10FDA20DB30DC40F5
+:106D6000DD502E0A005BFE69D2A0D10F2E200C1838
+:106D7000D70B0CEF11A8FF29F286C088798B751A02
+:106D8000D7080AEA0A2AA2A368A0048B207AB96469
+:106D900023F28564305E1CD7122A0A802D206829D0
+:106DA00020672821040B991104881109880208DD45
+:106DB00002C094284A1008DD0218D70A9931983089
+:106DC0008B2B9A379D340CBB029B32C0C09C359CE8
+:106DD000362A2C74DB40C0D318D6F929F285A8EEE8
+:106DE000299C2029F6852CE4CF2D2406DD405BFD6F
+:106DF000F9D2A0D10FDA20DBE05BFF4CC020D10F2D
+:106E00006C100AD6302A2006941028ACF86583FF4F
+:106E10002B2122270A022A2124CC572AAC010A0A54
+:106E20004F2A25247ABB026003F72C21022A200C6A
+:106E30000C0C4C65C38E2E22158D32C0F10EDD0C6C
+:106E400065D40488381ED6D56483E18F37C0C8C0A6
+:106E5000960FC9399914B49B9B110D99119913C9B7
+:106E6000FB19D6D02990217F93138B148C205BFFC4
+:106E7000631ED6CADDA064A41C8F676000298C1134
+:106E80000CAD11AEDD28D2860AAB0278CB621AD6E1
+:106E9000C40ABA0A2AA2A368A0052C22007AC95003
+:106EA0002AD285DDA064A3D729212E09F9362A200C
+:106EB0003C09F80C6F8D3ED7F0CB7F28211F08705E
+:106EC00060010B3E00043EB1BC04CB39C74F0BBB85
+:106ED0000A07BB0A0B0C4104CC03B1CC0C0C41AC2F
+:106EE000BBD4B0C0C27CA04C2A21257BAB4660003D
+:106EF0002CC0A063FFACD79063FFBD00C092C74F0A
+:106F00002C7C140C0B4104BB03B1BB0B0B41AB74C9
+:106F1000244C1479A01E2A212574AB18ACBB241A6A
+:106F2000FC0ABC0C04C16000093E01043EB14809E2
+:106F300084390B440A8926882709880C74831DC06C
+:106F40008098D88C649CD98B668A659BDB9ADA978B
+:106F5000D57F730260013ACE5E600016009D15DA9F
+:106F600020DB405BFEB48D151ED68D65A2568F6763
+:106F700063FFCB9D15DA20DB308C105BFE598D153D
+:106F80001ED687C051D6A08FA7C0C08A6897DD9A49
+:106F9000DC8869896A98DE99DF8B6A8A69AB7B77BE
+:106FA000BB022AAC019B6A9A698860C0A0088B1456
+:106FB000778701C0A1C09028203C9417951893169C
+:106FC000C050C031C044048401043938089910C04D
+:106FD00042048401043538083840832B0BA4100781
+:106FE00055102A211F0955020544020B19400A2A8F
+:106FF000140799100585100433020A881114D6F37A
+:10700000095502292104043302089911098802C094
+:107010009209880229212593D00929140499110A7B
+:1070200099020955028A20891408AA110A99021A9C
+:10703000D66F14D6E70A990299D1832A95D698D7A4
+:10704000851804330293D4841783168A658D66AA43
+:10705000CAAD7D77DB01B1AA07FF0C9A659D6688F2
+:10706000268C29A48808CC0C98260C0C482C2525A5
+:107070009F672A200CC0C01BD6510CA911AE99AB3A
+:10708000AB2892852CB4CF8B13AB8828968563FDF3
+:10709000CD00C091C0F0C0B2C0C4886023203C982D
+:1070A000120C3C010B3A010888140B88010A9F3826
+:1070B00007FF10089839C0A00C9A3807881008AA52
+:1070C000100AFF02C0A80A33010393392A210429B8
+:1070D0002125053C1008CC020A331108AA11092900
+:1070E0001403AA020499110BAA022B211F83140B6B
+:1070F0002B140B99020C99028B201CD63C08BB1157
+:1071000003BB020CBB02832A8C2B647084886897B3
+:10711000DD98DC8769886A97DE98DF8812C070770F
+:107120008701C0719BD199D60B78109AD717D6A931
+:1071300008F80208C80207880217D6A598D00737B2
+:107140000297D428200C295CFE2B2124C0F01AD6EB
+:107150001B0C8D11AEDD2CD285AA882F84CF8F1306
+:10716000B0BBAFCC2CD6852A22152B2524B1AA2A58
+:1071700026156490DCC84F8C268B29A4CC9C260C49
+:10718000BB0C0B0B482B25256550E8C020D10F0008
+:107190000000C0709BD199D69AD7881293D4778774
+:1071A0000E18D600921A288022C021082738821A89
+:1071B00018D68A0B731003F30203C3020833029339
+:1071C000D063FF7E00CC57DA20DB608C105BFDC4FF
+:1071D000292102689806689403C020D10F2B221E33
+:1071E000C09028221D2925027B8901C0B064BFE818
+:1071F00013D5EA2CB00728B000DA2003880A2882C9
+:107200004CC0D10B8000DBA065AFE763FFCA000074
+:1072100068A775DA20DB30DC40DD505BFECAD2A007
+:10722000D10FC1FDC19D29252C600003002F252C05
+:107230002F2467272468DA20DB308C10DD502E0ADB
+:10724000805BFD32D2A0D10FC1F8C1A82A252C63E2
+:10725000FFDDC84F8C268B29A4CC9C260CBB0C0BC5
+:107260000B482B25252A2C74DB602C12005BFD7645
+:10727000D2A0D10F2A2C748B105BF6BBD2A0D10FF9
+:10728000DA205BFE2A63FF3C00DA20C0B15BFE6EB1
+:1072900065AF3163FB79DA202B200C5BFE3D63FF89
+:1072A0002300000012D64E8220028257C82163FFBD
+:1072B000FC12D64A03E83004EE3005B13093209436
+:1072C00021952263FFFC000010D6469100920193A5
+:1072D00002940311D61D821001EA30A21101F0318F
+:1072E000C04004E41600020011D63F8210234A0079
+:1072F000032202921011D609C021921004E43184B5
+:107300000383028201810000D230012300000000CB
+:1073100010D636910092019302940311D60C82107C
+:1073200001EA30A21101F131C04004E4160002006C
+:1073300011D62D821013D5B4032202921004E43129
+:10734000840383028201810000D3300133000000F6
+:1073500010D6279100810165104981026510448192
+:1073600003CF1F92019302940311D5FA821001EA10
+:1073700030A21101F231C04004E41600020011D61F
+:1073800019821013D59B032202921004E431840366
+:1073900083028201C010910391029101810000D407
+:1073A0003001430012D5CAC030283740283744285E
+:1073B000374828374C233D017233ED03020063FF49
+:1073C000FC00000010D60B9100920193029403116F
+:1073D000D6098210921011D5BC831003220292109C
+:1073E00011D60612D5CD9210C04004E4160002005A
+:1073F00011D5FD821013D5B5032202921004E43199
+:10740000840383028201810000D530015300000013
+:107410006C10026E322FD620056F04043F04745B9B
+:107420002A05440C00410400331A220A006D490D5C
+:1074300073630403660CB1220F22110313147363E8
+:1074400002222C01D10FC83BD10F000073630CC086
+:1074500021D10F000000000044495630C020D10F58
+:107460006C10020040046B4C07032318020219D170
+:107470000F020319C020D10F6C100202EA30D10FA5
+:107480006C1002CC2503F03160000F006F22050361
+:10749000F1316000056F230503F231000200D10FC6
+:1074A0006C1002CC2502F030D10F00006F220402D4
+:1074B000F130D10F6F230402F230D10FC020D10F71
+:1074C0006C1002220A20230A006D280E283740285B
+:1074D000374428374828374C233D01030200D10F99
+:1074E0006C100202E431D10F0A004368656C7369C5
+:1074F0006F2046572044454255473D30202842756D
+:10750000696C7420547565204175672031322030D4
+:10751000393A34333A303420504454203230303801
+:10752000206F6E2066656C69782E6173696364658F
+:107530007369676E6572732E636F6D3A2F686F6D36
+:10754000652F66656C69782F772F66775F362E30EA
+:10755000292C2056657273696F6E20543378782019
+:107560003030372E30302E3030202D203130303733
+:0C7570003030303010070000CC44A0D6B2
+:00000001FF
diff --git a/fs/9p/fid.c b/fs/9p/fid.c
index 3031e3233dd6..14d944204571 100644
--- a/fs/9p/fid.c
+++ b/fs/9p/fid.c
@@ -45,7 +45,7 @@ int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid)
struct v9fs_dentry *dent;
P9_DPRINTK(P9_DEBUG_VFS, "fid %d dentry %s\n",
- fid->fid, dentry->d_iname);
+ fid->fid, dentry->d_name.name);
dent = dentry->d_fsdata;
if (!dent) {
@@ -79,7 +79,7 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any)
struct p9_fid *fid, *ret;
P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n",
- dentry->d_iname, dentry, uid, any);
+ dentry->d_name.name, dentry, uid, any);
dent = (struct v9fs_dentry *) dentry->d_fsdata;
ret = NULL;
if (dent) {
@@ -120,7 +120,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
switch (access) {
case V9FS_ACCESS_SINGLE:
case V9FS_ACCESS_USER:
- uid = current->fsuid;
+ uid = current_fsuid();
any = 0;
break;
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 24eb01087b6d..332b5ff02fec 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -160,7 +160,7 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses)
v9ses->flags |= V9FS_ACCESS_ANY;
else {
v9ses->flags |= V9FS_ACCESS_SINGLE;
- v9ses->uid = simple_strtol(s, &e, 10);
+ v9ses->uid = simple_strtoul(s, &e, 10);
if (*e != '\0')
v9ses->uid = ~0;
}
diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c
index f9534f18df0a..06dcc7c4f234 100644
--- a/fs/9p/vfs_dentry.c
+++ b/fs/9p/vfs_dentry.c
@@ -52,7 +52,8 @@
static int v9fs_dentry_delete(struct dentry *dentry)
{
- P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
+ P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
+ dentry);
return 1;
}
@@ -69,7 +70,8 @@ static int v9fs_dentry_delete(struct dentry *dentry)
static int v9fs_cached_dentry_delete(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
- P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
+ P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
+ dentry);
if(!inode)
return 1;
@@ -88,7 +90,8 @@ void v9fs_dentry_release(struct dentry *dentry)
struct v9fs_dentry *dent;
struct p9_fid *temp, *current_fid;
- P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
+ P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
+ dentry);
dent = dentry->d_fsdata;
if (dent) {
list_for_each_entry_safe(current_fid, temp, &dent->fidlist,
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 8314d3f43b71..81f8bbf12f9f 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -215,8 +215,8 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_rdev = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -963,7 +963,8 @@ static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer,
if (buflen > PATH_MAX)
buflen = PATH_MAX;
- P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
+ P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_name.name,
+ dentry);
retval = v9fs_readlink(dentry, link, buflen);
@@ -1022,7 +1023,8 @@ v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
char *s = nd_get_link(nd);
- P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name, s);
+ P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name,
+ IS_ERR(s) ? "<error>" : s);
if (!IS_ERR(s))
__putname(s);
}
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
index d6cb1a0ca724..93212e40221a 100644
--- a/fs/9p/vfs_super.c
+++ b/fs/9p/vfs_super.c
@@ -113,8 +113,8 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
struct v9fs_session_info *v9ses = NULL;
struct p9_wstat *st = NULL;
int mode = S_IRWXUGO | S_ISVTX;
- uid_t uid = current->fsuid;
- gid_t gid = current->fsgid;
+ uid_t uid = current_fsuid();
+ gid_t gid = current_fsgid();
struct p9_fid *fid;
int retval = 0;
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index a13b334a3910..415d9c67ac16 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -293,8 +293,8 @@ affs_new_inode(struct inode *dir)
mark_buffer_dirty_inode(bh, inode);
affs_brelse(bh);
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_ino = block;
inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 8989c93193ed..a19d64b582aa 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -163,8 +163,8 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s
/* Fill in defaults */
- *uid = current->uid;
- *gid = current->gid;
+ *uid = current_uid();
+ *gid = current_gid();
*reserved = 2;
*root = -1;
*blocksize = -1;
diff --git a/fs/afs/proc.c b/fs/afs/proc.c
index 9f7d1ae70269..7578c1ab9e0b 100644
--- a/fs/afs/proc.c
+++ b/fs/afs/proc.c
@@ -646,7 +646,7 @@ static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
}
/* display one cell per line on subsequent lines */
- seq_printf(m, "%u.%u.%u.%u\n", NIPQUAD(addr->s_addr));
+ seq_printf(m, "%pI4\n", &addr->s_addr);
return 0;
}
@@ -737,7 +737,7 @@ static int afs_proc_cell_servers_show(struct seq_file *m, void *v)
}
/* display one cell per line on subsequent lines */
- sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(server->addr));
+ sprintf(ipaddr, "%pI4", &server->addr);
seq_printf(m, "%3d %-15.15s %5d\n",
atomic_read(&server->usage), ipaddr, server->fs_state);
diff --git a/fs/afs/server.c b/fs/afs/server.c
index 28f2451419e1..f49099516675 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -105,7 +105,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell,
{
struct afs_server *server, *candidate;
- _enter("%p,"NIPQUAD_FMT, cell, NIPQUAD(addr->s_addr));
+ _enter("%p,%pI4", cell, &addr->s_addr);
/* quick scan of the list to see if we already have the server */
read_lock(&cell->servers_lock);
@@ -168,9 +168,8 @@ found_server:
server_in_two_cells:
write_unlock(&cell->servers_lock);
kfree(candidate);
- printk(KERN_NOTICE "kAFS:"
- " Server "NIPQUAD_FMT" appears to be in two cells\n",
- NIPQUAD(*addr));
+ printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
+ addr);
_leave(" = -EEXIST");
return ERR_PTR(-EEXIST);
}
@@ -184,7 +183,7 @@ struct afs_server *afs_find_server(const struct in_addr *_addr)
struct rb_node *p;
struct in_addr addr = *_addr;
- _enter(NIPQUAD_FMT, NIPQUAD(addr.s_addr));
+ _enter("%pI4", &addr.s_addr);
read_lock(&afs_servers_lock);
diff --git a/fs/aio.c b/fs/aio.c
index f658441d5666..d6f89d3c15e8 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -191,6 +191,20 @@ static int aio_setup_ring(struct kioctx *ctx)
kunmap_atomic((void *)((unsigned long)__event & PAGE_MASK), km); \
} while(0)
+static void ctx_rcu_free(struct rcu_head *head)
+{
+ struct kioctx *ctx = container_of(head, struct kioctx, rcu_head);
+ unsigned nr_events = ctx->max_reqs;
+
+ kmem_cache_free(kioctx_cachep, ctx);
+
+ if (nr_events) {
+ spin_lock(&aio_nr_lock);
+ BUG_ON(aio_nr - nr_events > aio_nr);
+ aio_nr -= nr_events;
+ spin_unlock(&aio_nr_lock);
+ }
+}
/* __put_ioctx
* Called when the last user of an aio context has gone away,
@@ -198,8 +212,6 @@ static int aio_setup_ring(struct kioctx *ctx)
*/
static void __put_ioctx(struct kioctx *ctx)
{
- unsigned nr_events = ctx->max_reqs;
-
BUG_ON(ctx->reqs_active);
cancel_delayed_work(&ctx->wq);
@@ -208,14 +220,7 @@ static void __put_ioctx(struct kioctx *ctx)
mmdrop(ctx->mm);
ctx->mm = NULL;
pr_debug("__put_ioctx: freeing %p\n", ctx);
- kmem_cache_free(kioctx_cachep, ctx);
-
- if (nr_events) {
- spin_lock(&aio_nr_lock);
- BUG_ON(aio_nr - nr_events > aio_nr);
- aio_nr -= nr_events;
- spin_unlock(&aio_nr_lock);
- }
+ call_rcu(&ctx->rcu_head, ctx_rcu_free);
}
#define get_ioctx(kioctx) do { \
@@ -235,6 +240,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
{
struct mm_struct *mm;
struct kioctx *ctx;
+ int did_sync = 0;
/* Prevent overflows */
if ((nr_events > (0x10000000U / sizeof(struct io_event))) ||
@@ -267,21 +273,30 @@ static struct kioctx *ioctx_alloc(unsigned nr_events)
goto out_freectx;
/* limit the number of system wide aios */
- spin_lock(&aio_nr_lock);
- if (aio_nr + ctx->max_reqs > aio_max_nr ||
- aio_nr + ctx->max_reqs < aio_nr)
- ctx->max_reqs = 0;
- else
- aio_nr += ctx->max_reqs;
- spin_unlock(&aio_nr_lock);
+ do {
+ spin_lock_bh(&aio_nr_lock);
+ if (aio_nr + nr_events > aio_max_nr ||
+ aio_nr + nr_events < aio_nr)
+ ctx->max_reqs = 0;
+ else
+ aio_nr += ctx->max_reqs;
+ spin_unlock_bh(&aio_nr_lock);
+ if (ctx->max_reqs || did_sync)
+ break;
+
+ /* wait for rcu callbacks to have completed before giving up */
+ synchronize_rcu();
+ did_sync = 1;
+ ctx->max_reqs = nr_events;
+ } while (1);
+
if (ctx->max_reqs == 0)
goto out_cleanup;
/* now link into global list. */
- write_lock(&mm->ioctx_list_lock);
- ctx->next = mm->ioctx_list;
- mm->ioctx_list = ctx;
- write_unlock(&mm->ioctx_list_lock);
+ spin_lock(&mm->ioctx_lock);
+ hlist_add_head_rcu(&ctx->list, &mm->ioctx_list);
+ spin_unlock(&mm->ioctx_lock);
dprintk("aio: allocated ioctx %p[%ld]: mm=%p mask=0x%x\n",
ctx, ctx->user_id, current->mm, ctx->ring_info.nr);
@@ -375,11 +390,12 @@ ssize_t wait_on_sync_kiocb(struct kiocb *iocb)
*/
void exit_aio(struct mm_struct *mm)
{
- struct kioctx *ctx = mm->ioctx_list;
- mm->ioctx_list = NULL;
- while (ctx) {
- struct kioctx *next = ctx->next;
- ctx->next = NULL;
+ struct kioctx *ctx;
+
+ while (!hlist_empty(&mm->ioctx_list)) {
+ ctx = hlist_entry(mm->ioctx_list.first, struct kioctx, list);
+ hlist_del_rcu(&ctx->list);
+
aio_cancel_all(ctx);
wait_for_all_aios(ctx);
@@ -394,7 +410,6 @@ void exit_aio(struct mm_struct *mm)
atomic_read(&ctx->users), ctx->dead,
ctx->reqs_active);
put_ioctx(ctx);
- ctx = next;
}
}
@@ -555,19 +570,21 @@ int aio_put_req(struct kiocb *req)
static struct kioctx *lookup_ioctx(unsigned long ctx_id)
{
- struct kioctx *ioctx;
- struct mm_struct *mm;
+ struct mm_struct *mm = current->mm;
+ struct kioctx *ctx = NULL;
+ struct hlist_node *n;
- mm = current->mm;
- read_lock(&mm->ioctx_list_lock);
- for (ioctx = mm->ioctx_list; ioctx; ioctx = ioctx->next)
- if (likely(ioctx->user_id == ctx_id && !ioctx->dead)) {
- get_ioctx(ioctx);
+ rcu_read_lock();
+
+ hlist_for_each_entry_rcu(ctx, n, &mm->ioctx_list, list) {
+ if (ctx->user_id == ctx_id && !ctx->dead) {
+ get_ioctx(ctx);
break;
}
- read_unlock(&mm->ioctx_list_lock);
+ }
- return ioctx;
+ rcu_read_unlock();
+ return ctx;
}
/*
@@ -1215,19 +1232,14 @@ out:
static void io_destroy(struct kioctx *ioctx)
{
struct mm_struct *mm = current->mm;
- struct kioctx **tmp;
int was_dead;
/* delete the entry from the list is someone else hasn't already */
- write_lock(&mm->ioctx_list_lock);
+ spin_lock(&mm->ioctx_lock);
was_dead = ioctx->dead;
ioctx->dead = 1;
- for (tmp = &mm->ioctx_list; *tmp && *tmp != ioctx;
- tmp = &(*tmp)->next)
- ;
- if (*tmp)
- *tmp = ioctx->next;
- write_unlock(&mm->ioctx_list_lock);
+ hlist_del_rcu(&ioctx->list);
+ spin_unlock(&mm->ioctx_lock);
dprintk("aio_release(%p)\n", ioctx);
if (likely(!was_dead))
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index 3662dd44896b..c16d9be1b017 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -154,8 +154,8 @@ static struct inode *anon_inode_mkinode(void)
*/
inode->i_state = I_DIRTY;
inode->i_mode = S_IRUSR | S_IWUSR;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
return inode;
}
diff --git a/fs/attr.c b/fs/attr.c
index 7a83819f6ba2..f4360192a938 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -29,13 +29,13 @@ int inode_change_ok(struct inode *inode, struct iattr *attr)
/* Make sure a caller can chown. */
if ((ia_valid & ATTR_UID) &&
- (current->fsuid != inode->i_uid ||
+ (current_fsuid() != inode->i_uid ||
attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
goto error;
/* Make sure caller can chgrp. */
if ((ia_valid & ATTR_GID) &&
- (current->fsuid != inode->i_uid ||
+ (current_fsuid() != inode->i_uid ||
(!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
!capable(CAP_CHOWN))
goto error;
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index b70eea1e8c59..c773680d5c60 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -76,8 +76,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
substring_t args[MAX_OPT_ARGS];
int option;
- *uid = current->uid;
- *gid = current->gid;
+ *uid = current_uid();
+ *gid = current_gid();
*pgrp = task_pgrp_nr(current);
*minproto = *maxproto = AUTOFS_PROTO_VERSION;
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index 33bf8cbfd051..63b7c7afe8df 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -308,7 +308,8 @@ static int autofs_dev_ioctl_open_mountpoint(const char *path, dev_t devid)
goto out;
}
- filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
+ filp = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
+ current_cred());
if (IS_ERR(filp)) {
err = PTR_ERR(filp);
goto out;
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index c7e65bb30ba0..7b19802cfef4 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -235,8 +235,8 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
substring_t args[MAX_OPT_ARGS];
int option;
- *uid = current->uid;
- *gid = current->gid;
+ *uid = current_uid();
+ *gid = current_gid();
*pgrp = task_pgrp_nr(current);
*minproto = AUTOFS_MIN_PROTO_VERSION;
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 4b67c2a2d77c..e02cc8ae5eb3 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -391,8 +391,8 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
memcpy(&wq->name, &qstr, sizeof(struct qstr));
wq->dev = autofs4_get_dev(sbi);
wq->ino = autofs4_get_ino(sbi);
- wq->uid = current->uid;
- wq->gid = current->gid;
+ wq->uid = current_uid();
+ wq->gid = current_gid();
wq->pid = current->pid;
wq->tgid = current->tgid;
wq->status = -EINTR; /* Status return if interrupted */
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
index daae463068e4..4dd1b623f937 100644
--- a/fs/bfs/dir.c
+++ b/fs/bfs/dir.c
@@ -106,8 +106,8 @@ static int bfs_create(struct inode *dir, struct dentry *dentry, int mode,
}
set_bit(ino, info->si_imap);
info->si_freei--;
- inode->i_uid = current->fsuid;
- inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current_fsgid();
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
inode->i_blocks = 0;
inode->i_op = &bfs_file_inops;
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index 204cfd1d7676..f1f3f4192a60 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -320,7 +320,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->mm->free_area_cache = current->mm->mmap_base;
current->mm->cached_hole_size = 0;
- compute_creds(bprm);
+ install_exec_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
#ifdef __sparc__
if (N_MAGIC(ex) == NMAGIC) {
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 8fcfa398d350..c41fa2af7677 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -157,7 +157,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
int items;
elf_addr_t *elf_info;
int ei_index = 0;
- struct task_struct *tsk = current;
+ const struct cred *cred = current_cred();
struct vm_area_struct *vma;
/*
@@ -223,10 +223,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
NEW_AUX_ENT(AT_BASE, interp_load_addr);
NEW_AUX_ENT(AT_FLAGS, 0);
NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
- NEW_AUX_ENT(AT_UID, tsk->uid);
- NEW_AUX_ENT(AT_EUID, tsk->euid);
- NEW_AUX_ENT(AT_GID, tsk->gid);
- NEW_AUX_ENT(AT_EGID, tsk->egid);
+ NEW_AUX_ENT(AT_UID, cred->uid);
+ NEW_AUX_ENT(AT_EUID, cred->euid);
+ NEW_AUX_ENT(AT_GID, cred->gid);
+ NEW_AUX_ENT(AT_EGID, cred->egid);
NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
NEW_AUX_ENT(AT_EXECFN, bprm->exec);
if (k_platform) {
@@ -949,14 +949,14 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
set_binfmt(&elf_format);
#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
- retval = arch_setup_additional_pages(bprm, executable_stack);
+ retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
if (retval < 0) {
send_sig(SIGKILL, current, 0);
goto out;
}
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
- compute_creds(bprm);
+ install_exec_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
retval = create_elf_tables(bprm, &loc->elf_ex,
load_addr, interp_load_addr);
@@ -1361,6 +1361,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
struct mm_struct *mm)
{
+ const struct cred *cred;
unsigned int i, len;
/* first copy the parameters from user space */
@@ -1388,8 +1389,11 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
psinfo->pr_zomb = psinfo->pr_sname == 'Z';
psinfo->pr_nice = task_nice(p);
psinfo->pr_flag = p->flags;
- SET_UID(psinfo->pr_uid, p->uid);
- SET_GID(psinfo->pr_gid, p->gid);
+ rcu_read_lock();
+ cred = __task_cred(p);
+ SET_UID(psinfo->pr_uid, cred->uid);
+ SET_GID(psinfo->pr_gid, cred->gid);
+ rcu_read_unlock();
strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
return 0;
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 5b5424cb3391..aa5b43205e37 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -404,7 +404,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm,
current->mm->start_stack = current->mm->start_brk + stack_size;
#endif
- compute_creds(bprm);
+ install_exec_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
if (create_elf_fdpic_tables(bprm, current->mm,
&exec_params, &interp_params) < 0)
@@ -475,6 +475,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
struct elf_fdpic_params *exec_params,
struct elf_fdpic_params *interp_params)
{
+ const struct cred *cred = current_cred();
unsigned long sp, csp, nitems;
elf_caddr_t __user *argv, *envp;
size_t platform_len = 0, len;
@@ -623,10 +624,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr);
NEW_AUX_ENT(AT_FLAGS, 0);
NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr);
- NEW_AUX_ENT(AT_UID, (elf_addr_t) current->uid);
- NEW_AUX_ENT(AT_EUID, (elf_addr_t) current->euid);
- NEW_AUX_ENT(AT_GID, (elf_addr_t) current->gid);
- NEW_AUX_ENT(AT_EGID, (elf_addr_t) current->egid);
+ NEW_AUX_ENT(AT_UID, (elf_addr_t) cred->uid);
+ NEW_AUX_ENT(AT_EUID, (elf_addr_t) cred->euid);
+ NEW_AUX_ENT(AT_GID, (elf_addr_t) cred->gid);
+ NEW_AUX_ENT(AT_EGID, (elf_addr_t) cred->egid);
NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
NEW_AUX_ENT(AT_EXECFN, bprm->exec);
@@ -1413,6 +1414,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
struct mm_struct *mm)
{
+ const struct cred *cred;
unsigned int i, len;
/* first copy the parameters from user space */
@@ -1440,8 +1442,11 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
psinfo->pr_zomb = psinfo->pr_sname == 'Z';
psinfo->pr_nice = task_nice(p);
psinfo->pr_flag = p->flags;
- SET_UID(psinfo->pr_uid, p->uid);
- SET_GID(psinfo->pr_gid, p->gid);
+ rcu_read_lock();
+ cred = __task_cred(p);
+ SET_UID(psinfo->pr_uid, cred->uid);
+ SET_GID(psinfo->pr_gid, cred->gid);
+ rcu_read_unlock();
strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
return 0;
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
index ccb781a6a804..7bbd5c6b3725 100644
--- a/fs/binfmt_flat.c
+++ b/fs/binfmt_flat.c
@@ -880,7 +880,7 @@ static int load_flat_binary(struct linux_binprm * bprm, struct pt_regs * regs)
(libinfo.lib_list[j].loaded)?
libinfo.lib_list[j].start_data:UNLOADED_LIB;
- compute_creds(bprm);
+ install_exec_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
set_binfmt(&flat_format);
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
index 74e587a52796..08644a61616e 100644
--- a/fs/binfmt_som.c
+++ b/fs/binfmt_som.c
@@ -255,7 +255,7 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
kfree(hpuxhdr);
set_binfmt(&som_format);
- compute_creds(bprm);
+ install_exec_creds(bprm);
setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
create_som_tables(bprm);
diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c
index 19caf7c962ac..77ebc3c263d6 100644
--- a/fs/bio-integrity.c
+++ b/fs/bio-integrity.c
@@ -111,7 +111,7 @@ void bio_integrity_free(struct bio *bio, struct bio_set *bs)
&& bip->bip_buf != NULL)
kfree(bip->bip_buf);
- mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]);
+ bvec_free_bs(bs, bip->bip_vec, bip->bip_pool);
mempool_free(bip, bs->bio_integrity_pool);
bio->bi_integrity = NULL;
diff --git a/fs/bio.c b/fs/bio.c
index df99c882b807..711cee103602 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -31,7 +31,11 @@
DEFINE_TRACE(block_split);
-static struct kmem_cache *bio_slab __read_mostly;
+/*
+ * Test patch to inline a certain number of bi_io_vec's inside the bio
+ * itself, to shrink a bio data allocation from two mempool calls to one
+ */
+#define BIO_INLINE_VECS 4
static mempool_t *bio_split_pool __read_mostly;
@@ -40,9 +44,8 @@ static mempool_t *bio_split_pool __read_mostly;
* break badly! cannot be bigger than what you can fit into an
* unsigned short
*/
-
#define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) }
-static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {
+struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {
BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES),
};
#undef BV
@@ -53,12 +56,121 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {
*/
struct bio_set *fs_bio_set;
+/*
+ * Our slab pool management
+ */
+struct bio_slab {
+ struct kmem_cache *slab;
+ unsigned int slab_ref;
+ unsigned int slab_size;
+ char name[8];
+};
+static DEFINE_MUTEX(bio_slab_lock);
+static struct bio_slab *bio_slabs;
+static unsigned int bio_slab_nr, bio_slab_max;
+
+static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)
+{
+ unsigned int sz = sizeof(struct bio) + extra_size;
+ struct kmem_cache *slab = NULL;
+ struct bio_slab *bslab;
+ unsigned int i, entry = -1;
+
+ mutex_lock(&bio_slab_lock);
+
+ i = 0;
+ while (i < bio_slab_nr) {
+ struct bio_slab *bslab = &bio_slabs[i];
+
+ if (!bslab->slab && entry == -1)
+ entry = i;
+ else if (bslab->slab_size == sz) {
+ slab = bslab->slab;
+ bslab->slab_ref++;
+ break;
+ }
+ i++;
+ }
+
+ if (slab)
+ goto out_unlock;
+
+ if (bio_slab_nr == bio_slab_max && entry == -1) {
+ bio_slab_max <<= 1;
+ bio_slabs = krealloc(bio_slabs,
+ bio_slab_max * sizeof(struct bio_slab),
+ GFP_KERNEL);
+ if (!bio_slabs)
+ goto out_unlock;
+ }
+ if (entry == -1)
+ entry = bio_slab_nr++;
+
+ bslab = &bio_slabs[entry];
+
+ snprintf(bslab->name, sizeof(bslab->name), "bio-%d", entry);
+ slab = kmem_cache_create(bslab->name, sz, 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!slab)
+ goto out_unlock;
+
+ printk("bio: create slab <%s> at %d\n", bslab->name, entry);
+ bslab->slab = slab;
+ bslab->slab_ref = 1;
+ bslab->slab_size = sz;
+out_unlock:
+ mutex_unlock(&bio_slab_lock);
+ return slab;
+}
+
+static void bio_put_slab(struct bio_set *bs)
+{
+ struct bio_slab *bslab = NULL;
+ unsigned int i;
+
+ mutex_lock(&bio_slab_lock);
+
+ for (i = 0; i < bio_slab_nr; i++) {
+ if (bs->bio_slab == bio_slabs[i].slab) {
+ bslab = &bio_slabs[i];
+ break;
+ }
+ }
+
+ if (WARN(!bslab, KERN_ERR "bio: unable to find slab!\n"))
+ goto out;
+
+ WARN_ON(!bslab->slab_ref);
+
+ if (--bslab->slab_ref)
+ goto out;
+
+ kmem_cache_destroy(bslab->slab);
+ bslab->slab = NULL;
+
+out:
+ mutex_unlock(&bio_slab_lock);
+}
+
unsigned int bvec_nr_vecs(unsigned short idx)
{
return bvec_slabs[idx].nr_vecs;
}
-struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs)
+void bvec_free_bs(struct bio_set *bs, struct bio_vec *bv, unsigned int idx)
+{
+ BIO_BUG_ON(idx >= BIOVEC_NR_POOLS);
+
+ if (idx == BIOVEC_MAX_IDX)
+ mempool_free(bv, bs->bvec_pool);
+ else {
+ struct biovec_slab *bvs = bvec_slabs + idx;
+
+ kmem_cache_free(bvs->slab, bv);
+ }
+}
+
+struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx,
+ struct bio_set *bs)
{
struct bio_vec *bvl;
@@ -67,60 +179,85 @@ struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct
* If not, this is a bio_kmalloc() allocation and just do a
* kzalloc() for the exact number of vecs right away.
*/
- if (bs) {
+ if (!bs)
+ bvl = kmalloc(nr * sizeof(struct bio_vec), gfp_mask);
+
+ /*
+ * see comment near bvec_array define!
+ */
+ switch (nr) {
+ case 1:
+ *idx = 0;
+ break;
+ case 2 ... 4:
+ *idx = 1;
+ break;
+ case 5 ... 16:
+ *idx = 2;
+ break;
+ case 17 ... 64:
+ *idx = 3;
+ break;
+ case 65 ... 128:
+ *idx = 4;
+ break;
+ case 129 ... BIO_MAX_PAGES:
+ *idx = 5;
+ break;
+ default:
+ return NULL;
+ }
+
+ /*
+ * idx now points to the pool we want to allocate from. only the
+ * 1-vec entry pool is mempool backed.
+ */
+ if (*idx == BIOVEC_MAX_IDX) {
+fallback:
+ bvl = mempool_alloc(bs->bvec_pool, gfp_mask);
+ } else {
+ struct biovec_slab *bvs = bvec_slabs + *idx;
+ gfp_t __gfp_mask = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
+
/*
- * see comment near bvec_array define!
+ * Make this allocation restricted and don't dump info on
+ * allocation failures, since we'll fallback to the mempool
+ * in case of failure.
*/
- switch (nr) {
- case 1:
- *idx = 0;
- break;
- case 2 ... 4:
- *idx = 1;
- break;
- case 5 ... 16:
- *idx = 2;
- break;
- case 17 ... 64:
- *idx = 3;
- break;
- case 65 ... 128:
- *idx = 4;
- break;
- case 129 ... BIO_MAX_PAGES:
- *idx = 5;
- break;
- default:
- return NULL;
- }
+ __gfp_mask |= __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN;
/*
- * idx now points to the pool we want to allocate from
+ * Try a slab allocation. If this fails and __GFP_WAIT
+ * is set, retry with the 1-entry mempool
*/
- bvl = mempool_alloc(bs->bvec_pools[*idx], gfp_mask);
- if (bvl)
- memset(bvl, 0,
- bvec_nr_vecs(*idx) * sizeof(struct bio_vec));
- } else
- bvl = kzalloc(nr * sizeof(struct bio_vec), gfp_mask);
+ bvl = kmem_cache_alloc(bvs->slab, __gfp_mask);
+ if (unlikely(!bvl && (gfp_mask & __GFP_WAIT))) {
+ *idx = BIOVEC_MAX_IDX;
+ goto fallback;
+ }
+ }
return bvl;
}
-void bio_free(struct bio *bio, struct bio_set *bio_set)
+void bio_free(struct bio *bio, struct bio_set *bs)
{
- if (bio->bi_io_vec) {
- const int pool_idx = BIO_POOL_IDX(bio);
+ void *p;
- BIO_BUG_ON(pool_idx >= BIOVEC_NR_POOLS);
-
- mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]);
- }
+ if (bio_has_allocated_vec(bio))
+ bvec_free_bs(bs, bio->bi_io_vec, BIO_POOL_IDX(bio));
if (bio_integrity(bio))
- bio_integrity_free(bio, bio_set);
+ bio_integrity_free(bio, bs);
+
+ /*
+ * If we have front padding, adjust the bio pointer before freeing
+ */
+ p = bio;
+ if (bs->front_pad)
+ p -= bs->front_pad;
- mempool_free(bio, bio_set->bio_pool);
+ mempool_free(p, bs->bio_pool);
}
/*
@@ -133,7 +270,8 @@ static void bio_fs_destructor(struct bio *bio)
static void bio_kmalloc_destructor(struct bio *bio)
{
- kfree(bio->bi_io_vec);
+ if (bio_has_allocated_vec(bio))
+ kfree(bio->bi_io_vec);
kfree(bio);
}
@@ -157,16 +295,20 @@ void bio_init(struct bio *bio)
* for a &struct bio to become free. If a %NULL @bs is passed in, we will
* fall back to just using @kmalloc to allocate the required memory.
*
- * allocate bio and iovecs from the memory pools specified by the
- * bio_set structure, or @kmalloc if none given.
+ * Note that the caller must set ->bi_destructor on succesful return
+ * of a bio, to do the appropriate freeing of the bio once the reference
+ * count drops to zero.
**/
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
{
- struct bio *bio;
+ struct bio *bio = NULL;
+
+ if (bs) {
+ void *p = mempool_alloc(bs->bio_pool, gfp_mask);
- if (bs)
- bio = mempool_alloc(bs->bio_pool, gfp_mask);
- else
+ if (p)
+ bio = p + bs->front_pad;
+ } else
bio = kmalloc(sizeof(*bio), gfp_mask);
if (likely(bio)) {
@@ -176,7 +318,15 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
if (likely(nr_iovecs)) {
unsigned long uninitialized_var(idx);
- bvl = bvec_alloc_bs(gfp_mask, nr_iovecs, &idx, bs);
+ if (nr_iovecs <= BIO_INLINE_VECS) {
+ idx = 0;
+ bvl = bio->bi_inline_vecs;
+ nr_iovecs = BIO_INLINE_VECS;
+ } else {
+ bvl = bvec_alloc_bs(gfp_mask, nr_iovecs, &idx,
+ bs);
+ nr_iovecs = bvec_nr_vecs(idx);
+ }
if (unlikely(!bvl)) {
if (bs)
mempool_free(bio, bs->bio_pool);
@@ -186,7 +336,7 @@ struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
goto out;
}
bio->bi_flags |= idx << BIO_POOL_OFFSET;
- bio->bi_max_vecs = bvec_nr_vecs(idx);
+ bio->bi_max_vecs = nr_iovecs;
}
bio->bi_io_vec = bvl;
}
@@ -1346,30 +1496,18 @@ EXPORT_SYMBOL(bio_sector_offset);
*/
static int biovec_create_pools(struct bio_set *bs, int pool_entries)
{
- int i;
+ struct biovec_slab *bp = bvec_slabs + BIOVEC_MAX_IDX;
- for (i = 0; i < BIOVEC_NR_POOLS; i++) {
- struct biovec_slab *bp = bvec_slabs + i;
- mempool_t **bvp = bs->bvec_pools + i;
+ bs->bvec_pool = mempool_create_slab_pool(pool_entries, bp->slab);
+ if (!bs->bvec_pool)
+ return -ENOMEM;
- *bvp = mempool_create_slab_pool(pool_entries, bp->slab);
- if (!*bvp)
- return -ENOMEM;
- }
return 0;
}
static void biovec_free_pools(struct bio_set *bs)
{
- int i;
-
- for (i = 0; i < BIOVEC_NR_POOLS; i++) {
- mempool_t *bvp = bs->bvec_pools[i];
-
- if (bvp)
- mempool_destroy(bvp);
- }
-
+ mempool_destroy(bs->bvec_pool);
}
void bioset_free(struct bio_set *bs)
@@ -1379,25 +1517,49 @@ void bioset_free(struct bio_set *bs)
bioset_integrity_free(bs);
biovec_free_pools(bs);
+ bio_put_slab(bs);
kfree(bs);
}
-struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size)
+/**
+ * bioset_create - Create a bio_set
+ * @pool_size: Number of bio and bio_vecs to cache in the mempool
+ * @front_pad: Number of bytes to allocate in front of the returned bio
+ *
+ * Description:
+ * Set up a bio_set to be used with @bio_alloc_bioset. Allows the caller
+ * to ask for a number of bytes to be allocated in front of the bio.
+ * Front pad allocation is useful for embedding the bio inside
+ * another structure, to avoid allocating extra data to go with the bio.
+ * Note that the bio must be embedded at the END of that structure always,
+ * or things will break badly.
+ */
+struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad)
{
- struct bio_set *bs = kzalloc(sizeof(*bs), GFP_KERNEL);
+ unsigned int back_pad = BIO_INLINE_VECS * sizeof(struct bio_vec);
+ struct bio_set *bs;
+ bs = kzalloc(sizeof(*bs), GFP_KERNEL);
if (!bs)
return NULL;
- bs->bio_pool = mempool_create_slab_pool(bio_pool_size, bio_slab);
+ bs->front_pad = front_pad;
+
+ bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);
+ if (!bs->bio_slab) {
+ kfree(bs);
+ return NULL;
+ }
+
+ bs->bio_pool = mempool_create_slab_pool(pool_size, bs->bio_slab);
if (!bs->bio_pool)
goto bad;
- if (bioset_integrity_create(bs, bio_pool_size))
+ if (bioset_integrity_create(bs, pool_size))
goto bad;
- if (!biovec_create_pools(bs, bvec_pool_size))
+ if (!biovec_create_pools(bs, pool_size))
return bs;
bad:
@@ -1421,12 +1583,16 @@ static void __init biovec_init_slabs(void)
static int __init init_bio(void)
{
- bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
+ bio_slab_max = 2;
+ bio_slab_nr = 0;
+ bio_slabs = kzalloc(bio_slab_max * sizeof(struct bio_slab), GFP_KERNEL);
+ if (!bio_slabs)
+ panic("bio: can't allocate bios\n");
bio_integrity_init_slab();
biovec_init_slabs();
- fs_bio_set = bioset_create(BIO_POOL_SIZE, 2);
+ fs_bio_set = bioset_create(BIO_POOL_SIZE, 0);
if (!fs_bio_set)
panic("bio: can't allocate bios\n");
diff --git a/fs/buffer.c b/fs/buffer.c
index 10179cfa1152..776ae091d3b0 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -99,10 +99,18 @@ __clear_page_buffers(struct page *page)
page_cache_release(page);
}
+
+static int quiet_error(struct buffer_head *bh)
+{
+ if (!test_bit(BH_Quiet, &bh->b_state) && printk_ratelimit())
+ return 0;
+ return 1;
+}
+
+
static void buffer_io_error(struct buffer_head *bh)
{
char b[BDEVNAME_SIZE];
-
printk(KERN_ERR "Buffer I/O error on device %s, logical block %Lu\n",
bdevname(bh->b_bdev, b),
(unsigned long long)bh->b_blocknr);
@@ -144,7 +152,7 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate)
if (uptodate) {
set_buffer_uptodate(bh);
} else {
- if (!buffer_eopnotsupp(bh) && printk_ratelimit()) {
+ if (!buffer_eopnotsupp(bh) && !quiet_error(bh)) {
buffer_io_error(bh);
printk(KERN_WARNING "lost page write due to "
"I/O error on %s\n",
@@ -394,7 +402,7 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
set_buffer_uptodate(bh);
} else {
clear_buffer_uptodate(bh);
- if (printk_ratelimit())
+ if (!quiet_error(bh))
buffer_io_error(bh);
SetPageError(page);
}
@@ -455,7 +463,7 @@ static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
if (uptodate) {
set_buffer_uptodate(bh);
} else {
- if (printk_ratelimit()) {
+ if (!quiet_error(bh)) {
buffer_io_error(bh);
printk(KERN_WARNING "lost page write due to "
"I/O error on %s\n",
@@ -2913,6 +2921,9 @@ static void end_bio_bh_io_sync(struct bio *bio, int err)
set_bit(BH_Eopnotsupp, &bh->b_state);
}
+ if (unlikely (test_bit(BIO_QUIET,&bio->bi_flags)))
+ set_bit(BH_Quiet, &bh->b_state);
+
bh->b_end_io(bh, test_bit(BIO_UPTODATE, &bio->bi_flags));
bio_put(bio);
}
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS
index 9c136d7803d9..7f7fa3c302af 100644
--- a/fs/cifs/AUTHORS
+++ b/fs/cifs/AUTHORS
@@ -36,7 +36,9 @@ Miklos Szeredi
Kazeon team for various fixes especially for 2.4 version.
Asser Ferno (Change Notify support)
Shaggy (Dave Kleikamp) for inumerable small fs suggestions and some good cleanup
+Gunter Kukkukk (testing and suggestions for support of old servers)
Igor Mammedov (DFS support)
+Jeff Layton (many, many fixes, as well as great work on the cifs Kerberos code)
Test case and Bug Report contributors
-------------------------------------
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index e078b7aea143..080703a15f44 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,12 @@
+Version 1.56
+------------
+Add "forcemandatorylock" mount option to allow user to use mandatory
+rather than posix (advisory) byte range locks, even though server would
+support posix byte range locks. Fix query of root inode when prefixpath
+specified and user does not have access to query information about the
+top of the share. Fix problem in 2.6.28 resolving DFS paths to
+Samba servers (worked to Windows).
+
Version 1.55
------------
Various fixes to make delete of open files behavior more predictable
diff --git a/fs/cifs/README b/fs/cifs/README
index a439dc1739b3..da4515e3be20 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -463,9 +463,19 @@ A partial list of the supported mount options follows:
with cifs style mandatory byte range locks (and most
cifs servers do not yet support requesting advisory
byte range locks).
+ forcemandatorylock Even if the server supports posix (advisory) byte range
+ locking, send only mandatory lock requests. For some
+ (presumably rare) applications, originally coded for
+ DOS/Windows, which require Windows style mandatory byte range
+ locking, they may be able to take advantage of this option,
+ forcing the cifs client to only send mandatory locks
+ even if the cifs server would support posix advisory locks.
+ "forcemand" is accepted as a shorter form of this mount
+ option.
nodfs Disable DFS (global name space support) even if the
server claims to support it. This can help work around
- a problem with parsing of DFS paths with Samba 3.0.24 server.
+ a problem with parsing of DFS paths with Samba server
+ versions 3.0.24 and 3.0.25.
remount remount the share (often used to change from ro to rw mounts
or vice versa)
cifsacl Report mode bits (e.g. on stat) based on the Windows ACL for
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index e1c18362ba46..85c0a74d034d 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -122,7 +122,7 @@ static char *compose_mount_options(const char *sb_mountdata,
char **devname)
{
int rc;
- char *mountdata;
+ char *mountdata = NULL;
int md_len;
char *tkn_e;
char *srvIP = NULL;
@@ -136,10 +136,9 @@ static char *compose_mount_options(const char *sb_mountdata,
*devname = cifs_get_share_name(ref->node_name);
rc = dns_resolve_server_name_to_ip(*devname, &srvIP);
if (rc != 0) {
- cERROR(1, ("%s: Failed to resolve server part of %s to IP",
- __func__, *devname));
- mountdata = ERR_PTR(rc);
- goto compose_mount_options_out;
+ cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d",
+ __func__, *devname, rc));;
+ goto compose_mount_options_err;
}
/* md_len = strlen(...) + 12 for 'sep+prefixpath='
* assuming that we have 'unc=' and 'ip=' in
@@ -149,8 +148,8 @@ static char *compose_mount_options(const char *sb_mountdata,
strlen(ref->node_name) + 12;
mountdata = kzalloc(md_len+1, GFP_KERNEL);
if (mountdata == NULL) {
- mountdata = ERR_PTR(-ENOMEM);
- goto compose_mount_options_out;
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
}
/* copy all options except of unc,ip,prefixpath */
@@ -197,18 +196,32 @@ static char *compose_mount_options(const char *sb_mountdata,
/* find & copy prefixpath */
tkn_e = strchr(ref->node_name + 2, '\\');
- if (tkn_e == NULL) /* invalid unc, missing share name*/
- goto compose_mount_options_out;
+ if (tkn_e == NULL) {
+ /* invalid unc, missing share name*/
+ rc = -EINVAL;
+ goto compose_mount_options_err;
+ }
+ /*
+ * this function gives us a path with a double backslash prefix. We
+ * require a single backslash for DFS. Temporarily increment fullpath
+ * to put it in the proper form and decrement before freeing it.
+ */
fullpath = build_path_from_dentry(dentry);
+ if (!fullpath) {
+ rc = -ENOMEM;
+ goto compose_mount_options_err;
+ }
+ ++fullpath;
tkn_e = strchr(tkn_e + 1, '\\');
- if (tkn_e || strlen(fullpath) - (ref->path_consumed)) {
+ if (tkn_e || (strlen(fullpath) - ref->path_consumed)) {
strncat(mountdata, &sep, 1);
strcat(mountdata, "prefixpath=");
if (tkn_e)
strcat(mountdata, tkn_e + 1);
- strcat(mountdata, fullpath + (ref->path_consumed));
+ strcat(mountdata, fullpath + ref->path_consumed);
}
+ --fullpath;
kfree(fullpath);
/*cFYI(1,("%s: parent mountdata: %s", __func__,sb_mountdata));*/
@@ -217,6 +230,11 @@ static char *compose_mount_options(const char *sb_mountdata,
compose_mount_options_out:
kfree(srvIP);
return mountdata;
+
+compose_mount_options_err:
+ kfree(mountdata);
+ mountdata = ERR_PTR(rc);
+ goto compose_mount_options_out;
}
@@ -309,13 +327,19 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
goto out_err;
}
+ /*
+ * The MSDFS spec states that paths in DFS referral requests and
+ * responses must be prefixed by a single '\' character instead of
+ * the double backslashes usually used in the UNC. This function
+ * gives us the latter, so we must adjust the result.
+ */
full_path = build_path_from_dentry(dentry);
if (full_path == NULL) {
rc = -ENOMEM;
goto out_err;
}
- rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
+ rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls,
&num_referrals, &referrals,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 877c85409f1f..c4c306f7b06f 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -19,8 +19,8 @@
#define _CIFS_FS_SB_H
#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */
-#define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */
-#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
+#define CIFS_MOUNT_SET_UID 2 /* set current's euid in create etc. */
+#define CIFS_MOUNT_SERVER_INUM 4 /* inode numbers from uniqueid from server */
#define CIFS_MOUNT_DIRECT_IO 8 /* do not write nor read through page cache */
#define CIFS_MOUNT_NO_XATTR 0x10 /* if set - disable xattr support */
#define CIFS_MOUNT_MAP_SPECIAL_CHR 0x20 /* remap illegal chars in filenames */
@@ -30,7 +30,8 @@
#define CIFS_MOUNT_CIFS_ACL 0x200 /* send ACL requests to non-POSIX srv */
#define CIFS_MOUNT_OVERR_UID 0x400 /* override uid returned from server */
#define CIFS_MOUNT_OVERR_GID 0x800 /* override gid returned from server */
-#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+#define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */
+#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */
struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 0ab2fb5afef1..3fd3a9df043a 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -121,11 +121,9 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
/* add the server address */
if (server->addr.sockAddr.sin_family == AF_INET)
- sprintf(dp, "ip4=" NIPQUAD_FMT,
- NIPQUAD(server->addr.sockAddr.sin_addr));
+ sprintf(dp, "ip4=%pI4", &server->addr.sockAddr.sin_addr);
else if (server->addr.sockAddr.sin_family == AF_INET6)
- sprintf(dp, "ip6=" NIP6_SEQFMT,
- NIP6(server->addr.sockAddr6.sin6_addr));
+ sprintf(dp, "ip6=%pi6", &server->addr.sockAddr6.sin6_addr);
else
goto out;
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index bd5f13d38450..d4839cf0cb2c 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -37,7 +37,7 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
extern void E_md4hash(const unsigned char *passwd, unsigned char *p16);
-extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
+extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24);
static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
@@ -280,25 +280,22 @@ int CalcNTLMv2_partial_mac_key(struct cifsSesInfo *ses,
}
#ifdef CONFIG_CIFS_WEAK_PW_HASH
-void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key)
+void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
+ char *lnm_session_key)
{
int i;
char password_with_pad[CIFS_ENCPWD_SIZE];
- if (ses->server == NULL)
- return;
-
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
- if (ses->password)
- strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
-
- if ((ses->server->secMode & SECMODE_PW_ENCRYPT) == 0)
- if (extended_security & CIFSSEC_MAY_PLNTXT) {
- memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);
- memcpy(lnm_session_key, password_with_pad,
- CIFS_ENCPWD_SIZE);
- return;
- }
+ if (password)
+ strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE);
+
+ if (!encrypt && extended_security & CIFSSEC_MAY_PLNTXT) {
+ memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE);
+ memcpy(lnm_session_key, password_with_pad,
+ CIFS_ENCPWD_SIZE);
+ return;
+ }
/* calculate old style session key */
/* calling toupper is less broken than repeatedly
@@ -314,7 +311,8 @@ void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key)
for (i = 0; i < CIFS_ENCPWD_SIZE; i++)
password_with_pad[i] = toupper(password_with_pad[i]);
- SMBencrypt(password_with_pad, ses->server->cryptKey, lnm_session_key);
+ SMBencrypt(password_with_pad, cryptkey, lnm_session_key);
+
/* clear password before we return/free memory */
memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
}
diff --git a/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h
index 152fa2dcfc6c..15d2ec006474 100644
--- a/fs/cifs/cifsencrypt.h
+++ b/fs/cifs/cifsencrypt.h
@@ -26,7 +26,8 @@
extern void mdfour(unsigned char *out, unsigned char *in, int n);
/* smbdes.c */
extern void E_P16(unsigned char *p14, unsigned char *p16);
-extern void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24);
+extern void E_P24(unsigned char *p21, const unsigned char *c8,
+ unsigned char *p24);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index d9cf467309e8..0005a194a75c 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -66,7 +66,9 @@ unsigned int sign_CIFS_PDUs = 1;
extern struct task_struct *oplockThread; /* remove sparse warning */
struct task_struct *oplockThread = NULL;
/* extern struct task_struct * dnotifyThread; remove sparse warning */
+#ifdef CONFIG_CIFS_EXPERIMENTAL
static struct task_struct *dnotifyThread = NULL;
+#endif
static const struct super_operations cifs_super_ops;
unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
module_param(CIFSMaxBufSize, int, 0);
@@ -337,39 +339,58 @@ static int
cifs_show_options(struct seq_file *s, struct vfsmount *m)
{
struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *tcon;
+ struct TCP_Server_Info *server;
cifs_sb = CIFS_SB(m->mnt_sb);
if (cifs_sb) {
- if (cifs_sb->tcon) {
-/* BB add prepath to mount options displayed */
+ tcon = cifs_sb->tcon;
+ if (tcon) {
seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName);
- if (cifs_sb->tcon->ses) {
- if (cifs_sb->tcon->ses->userName)
+ if (tcon->ses) {
+ if (tcon->ses->userName)
seq_printf(s, ",username=%s",
- cifs_sb->tcon->ses->userName);
- if (cifs_sb->tcon->ses->domainName)
+ tcon->ses->userName);
+ if (tcon->ses->domainName)
seq_printf(s, ",domain=%s",
- cifs_sb->tcon->ses->domainName);
+ tcon->ses->domainName);
+ server = tcon->ses->server;
+ if (server) {
+ seq_printf(s, ",addr=");
+ switch (server->addr.sockAddr6.
+ sin6_family) {
+ case AF_INET6:
+ seq_printf(s, "%pI6",
+ &server->addr.sockAddr6.sin6_addr);
+ break;
+ case AF_INET:
+ seq_printf(s, "%pI4",
+ &server->addr.sockAddr.sin_addr.s_addr);
+ break;
+ }
+ }
}
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
- !(cifs_sb->tcon->unix_ext))
+ !(tcon->unix_ext))
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
- !(cifs_sb->tcon->unix_ext))
+ !(tcon->unix_ext))
seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
- if (!cifs_sb->tcon->unix_ext) {
+ if (!tcon->unix_ext) {
seq_printf(s, ",file_mode=0%o,dir_mode=0%o",
cifs_sb->mnt_file_mode,
cifs_sb->mnt_dir_mode);
}
- if (cifs_sb->tcon->seal)
+ if (tcon->seal)
seq_printf(s, ",seal");
- if (cifs_sb->tcon->nocase)
+ if (tcon->nocase)
seq_printf(s, ",nocase");
- if (cifs_sb->tcon->retry)
+ if (tcon->retry)
seq_printf(s, ",hard");
}
+ if (cifs_sb->prepath)
+ seq_printf(s, ",prepath=%s", cifs_sb->prepath);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
seq_printf(s, ",posixpaths");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
@@ -417,9 +438,8 @@ int cifs_xquota_set(struct super_block *sb, int quota_type, qid_t qid,
xid = GetXid();
if (pTcon) {
cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -441,9 +461,8 @@ int cifs_xquota_get(struct super_block *sb, int quota_type, qid_t qid,
xid = GetXid();
if (pTcon) {
cFYI(1, ("set type: 0x%x id: %d", quota_type, qid));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -464,9 +483,8 @@ int cifs_xstate_set(struct super_block *sb, unsigned int flags, int operation)
xid = GetXid();
if (pTcon) {
cFYI(1, ("flags: 0x%x operation: 0x%x", flags, operation));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -479,17 +497,16 @@ int cifs_xstate_get(struct super_block *sb, struct fs_quota_stat *qstats)
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *pTcon;
- if (cifs_sb) {
+ if (cifs_sb)
pTcon = cifs_sb->tcon;
- } else {
+ else
return -EIO;
- }
+
xid = GetXid();
if (pTcon) {
cFYI(1, ("pqstats %p", qstats));
- } else {
+ } else
rc = -EIO;
- }
FreeXid(xid);
return rc;
@@ -1029,6 +1046,7 @@ static int cifs_oplock_thread(void *dummyarg)
return 0;
}
+#ifdef CONFIG_CIFS_EXPERIMENTAL
static int cifs_dnotify_thread(void *dummyarg)
{
struct list_head *tmp;
@@ -1054,6 +1072,7 @@ static int cifs_dnotify_thread(void *dummyarg)
return 0;
}
+#endif
static int __init
init_cifs(void)
@@ -1131,16 +1150,20 @@ init_cifs(void)
goto out_unregister_dfs_key_type;
}
+#ifdef CONFIG_CIFS_EXPERIMENTAL
dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd");
if (IS_ERR(dnotifyThread)) {
rc = PTR_ERR(dnotifyThread);
cERROR(1, ("error %d create dnotify thread", rc));
goto out_stop_oplock_thread;
}
+#endif
return 0;
+#ifdef CONFIG_CIFS_EXPERIMENTAL
out_stop_oplock_thread:
+#endif
kthread_stop(oplockThread);
out_unregister_dfs_key_type:
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -1179,8 +1202,10 @@ exit_cifs(void)
cifs_destroy_inodecache();
cifs_destroy_mids();
cifs_destroy_request_bufs();
- kthread_stop(oplockThread);
+#ifdef CONFIG_CIFS_EXPERIMENTAL
kthread_stop(dnotifyThread);
+#endif
+ kthread_stop(oplockThread);
}
MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 074de0b5064d..2ce04c73d74e 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -101,5 +101,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
-#define CIFS_VERSION "1.55"
+#define CIFS_VERSION "1.56"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c57c0565547f..94c1ca0ec953 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -47,7 +47,11 @@
*/
#define CIFS_MAX_REQ 50
-#define SERVER_NAME_LENGTH 15
+#define RFC1001_NAME_LEN 15
+#define RFC1001_NAME_LEN_WITH_NULL (RFC1001_NAME_LEN + 1)
+
+/* currently length of NIP6_FMT */
+#define SERVER_NAME_LENGTH 40
#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1)
/* used to define string lengths for reversing unicode strings */
@@ -125,8 +129,7 @@ struct TCP_Server_Info {
struct list_head smb_ses_list;
int srv_count; /* reference counter */
/* 15 character server name + 0x20 16th byte indicating type = srv */
- char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
- char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
+ char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
char *hostname; /* hostname portion of UNC string */
struct socket *ssocket;
union {
@@ -151,7 +154,7 @@ struct TCP_Server_Info {
atomic_t num_waiters; /* blocked waiting to get in sendrecv */
#endif
enum statusEnum tcpStatus; /* what we think the status is */
- struct semaphore tcpSem;
+ struct mutex srv_mutex;
struct task_struct *tsk;
char server_GUID[16];
char secMode;
@@ -171,7 +174,7 @@ struct TCP_Server_Info {
__u16 CurrentMid; /* multiplex id - rotating counter */
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
/* 16th byte of RFC1001 workstation name is always null */
- char workstation_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
+ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */
struct mac_key mac_signing_key;
char ntlmv2_hash[16];
@@ -239,6 +242,7 @@ struct cifsTconInfo {
struct cifsSesInfo *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem;
+ char *password; /* for share-level security */
__u16 tid; /* The 2 byte tree id */
__u16 Flags; /* optional support bits */
enum statusEnum tidStatus;
@@ -422,7 +426,6 @@ struct mid_q_entry {
unsigned long when_sent; /* time when smb send finished */
unsigned long when_received; /* when demux complete (taken off wire) */
#endif
- struct cifsSesInfo *ses; /* smb was sent to this server */
struct task_struct *tsk; /* task waiting for response */
struct smb_hdr *resp_buf; /* response buffer */
int midState; /* wish this were enum but can not pass to wait_event */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index d2a073edd1b8..b4e2e9f0ee3d 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1922,7 +1922,7 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
/* DFS server target type */
#define DFS_TYPE_LINK 0x0000 /* also for sysvol targets */
#define DFS_TYPE_ROOT 0x0001
-
+
/* Referral Entry Flags */
#define DFS_NAME_LIST_REF 0x0200
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 6f21ecb85ce5..06f6779988bf 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -39,7 +39,7 @@ extern int smb_send(struct socket *, struct smb_hdr *,
unsigned int /* length */ , struct sockaddr *, bool);
extern unsigned int _GetXid(void);
extern void _FreeXid(unsigned int);
-#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current->fsuid));
+#define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__func__, xid,current_fsuid()));
#define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__func__,curr_xid,(int)rc));}
extern char *build_path_from_dentry(struct dentry *);
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
@@ -330,7 +330,8 @@ extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *);
extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
const struct nls_table *);
#ifdef CONFIG_CIFS_WEAK_PW_HASH
-extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key);
+extern void calc_lanman_hash(const char *password, const char *cryptkey,
+ bool encrypt, char *lnm_session_key);
#endif /* CIFS_WEAK_PW_HASH */
extern int CIFSSMBCopy(int xid,
struct cifsTconInfo *source_tcon,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 6d51696dc762..552642a507c4 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1382,13 +1382,13 @@ openRetry:
if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
*pOplock |= CIFS_CREATE_ACTION;
if (pfile_info) {
- memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
- 36 /* CreationTime to Attributes */);
- /* the file_info buf is endian converted by caller */
- pfile_info->AllocationSize = pSMBr->AllocationSize;
- pfile_info->EndOfFile = pSMBr->EndOfFile;
- pfile_info->NumberOfLinks = cpu_to_le32(1);
- pfile_info->DeletePending = 0;
+ memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
+ 36 /* CreationTime to Attributes */);
+ /* the file_info buf is endian converted by caller */
+ pfile_info->AllocationSize = pSMBr->AllocationSize;
+ pfile_info->EndOfFile = pSMBr->EndOfFile;
+ pfile_info->NumberOfLinks = cpu_to_le32(1);
+ pfile_info->DeletePending = 0;
}
}
@@ -1414,8 +1414,13 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 12;
- else
+ else {
wct = 10; /* old style read */
+ if ((lseek >> 32) > 0) {
+ /* can not handle this big offset for old */
+ return -EIO;
+ }
+ }
*nbytes = 0;
rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
@@ -1431,8 +1436,6 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
if (wct == 12)
pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
- else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
- return -EIO;
pSMB->Remaining = 0;
pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
@@ -1519,8 +1522,13 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 14;
- else
+ else {
wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
(void **) &pSMBr);
@@ -1535,8 +1543,6 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
if (wct == 14)
pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
- else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
- return -EIO;
pSMB->Reserved = 0xFFFFFFFF;
pSMB->WriteMode = 0;
@@ -1558,7 +1564,7 @@ CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
if (buf)
- memcpy(pSMB->Data, buf, bytes_sent);
+ memcpy(pSMB->Data, buf, bytes_sent);
else if (ubuf) {
if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
cifs_buf_release(pSMB);
@@ -1621,10 +1627,15 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
- if (tcon->ses->capabilities & CAP_LARGE_FILES)
+ if (tcon->ses->capabilities & CAP_LARGE_FILES) {
wct = 14;
- else
+ } else {
wct = 12;
+ if ((offset >> 32) > 0) {
+ /* can not handle big offset for old srv */
+ return -EIO;
+ }
+ }
rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
if (rc)
return rc;
@@ -1637,8 +1648,6 @@ CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
if (wct == 14)
pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
- else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
- return -EIO;
pSMB->Reserved = 0xFFFFFFFF;
pSMB->WriteMode = 0;
pSMB->Remaining = 0;
@@ -1862,10 +1871,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
rc = -EIO; /* bad smb */
goto plk_err_exit;
}
- if (pLockData == NULL) {
- rc = -EINVAL;
- goto plk_err_exit;
- }
data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
data_count = le16_to_cpu(pSMBr->t2.DataCount);
if (data_count < sizeof(struct cifs_posix_lock)) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index c7d341714586..e9ea394ee075 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -89,6 +89,7 @@ struct smb_vol {
bool nullauth:1; /* attempt to authenticate with null user */
bool nocase:1; /* request case insensitive filenames */
bool nobrl:1; /* disable sending byte range locks to srv */
+ bool mand_lock:1; /* send mandatory not posix byte range lock reqs */
bool seal:1; /* request transport encryption on share */
bool nodfs:1; /* Do not request DFS, even if available */
bool local_lease:1; /* check leases only on local system, not remote */
@@ -101,25 +102,17 @@ struct smb_vol {
char *prepath;
};
-static int ipv4_connect(struct sockaddr_in *psin_server,
- struct socket **csocket,
- char *netb_name,
- char *server_netb_name,
- bool noblocksnd,
- bool nosndbuf); /* ipv6 never set sndbuf size */
-static int ipv6_connect(struct sockaddr_in6 *psin_server,
- struct socket **csocket, bool noblocksnd);
-
-
- /*
- * cifs tcp session reconnection
- *
- * mark tcp session as reconnecting so temporarily locked
- * mark all smb sessions as reconnecting for tcp session
- * reconnect tcp session
- * wake up waiters on reconnection? - (not needed currently)
- */
+static int ipv4_connect(struct TCP_Server_Info *server);
+static int ipv6_connect(struct TCP_Server_Info *server);
+/*
+ * cifs tcp session reconnection
+ *
+ * mark tcp session as reconnecting so temporarily locked
+ * mark all smb sessions as reconnecting for tcp session
+ * reconnect tcp session
+ * wake up waiters on reconnection? - (not needed currently)
+ */
static int
cifs_reconnect(struct TCP_Server_Info *server)
{
@@ -156,7 +149,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
}
read_unlock(&cifs_tcp_ses_lock);
/* do not want to be sending data on a socket we are freeing */
- down(&server->tcpSem);
+ mutex_lock(&server->srv_mutex);
if (server->ssocket) {
cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
server->ssocket->flags));
@@ -182,21 +175,15 @@ cifs_reconnect(struct TCP_Server_Info *server)
}
}
spin_unlock(&GlobalMid_Lock);
- up(&server->tcpSem);
+ mutex_unlock(&server->srv_mutex);
while ((server->tcpStatus != CifsExiting) &&
(server->tcpStatus != CifsGood)) {
try_to_freeze();
- if (server->addr.sockAddr6.sin6_family == AF_INET6) {
- rc = ipv6_connect(&server->addr.sockAddr6,
- &server->ssocket, server->noautotune);
- } else {
- rc = ipv4_connect(&server->addr.sockAddr,
- &server->ssocket,
- server->workstation_RFC1001_name,
- server->server_RFC1001_name,
- server->noblocksnd, server->noautotune);
- }
+ if (server->addr.sockAddr6.sin6_family == AF_INET6)
+ rc = ipv6_connect(server);
+ else
+ rc = ipv4_connect(server);
if (rc) {
cFYI(1, ("reconnect error %d", rc));
msleep(3000);
@@ -776,7 +763,7 @@ multi_t2_fnd:
set_current_state(TASK_RUNNING);
}
- return 0;
+ module_put_and_exit(0);
}
/* extract the host portion of the UNC string */
@@ -836,8 +823,8 @@ cifs_parse_mount_options(char *options, const char *devname,
/* null target name indicates to use *SMBSERVR default called name
if we end up sending RFC1001 session initialize */
vol->target_rfc1001_name[0] = 0;
- vol->linux_uid = current->uid; /* current->euid instead? */
- vol->linux_gid = current->gid;
+ vol->linux_uid = current_uid(); /* use current_euid() instead? */
+ vol->linux_gid = current_gid();
vol->dir_mode = S_IRWXUGO;
/* 2767 perms indicate mandatory locking support */
vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
@@ -1260,6 +1247,17 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->file_mode ==
(S_IALLUGO & ~(S_ISUID | S_IXGRP)))
vol->file_mode = S_IALLUGO;
+ } else if (strnicmp(data, "forcemandatorylock", 9) == 0) {
+ /* will take the shorter form "forcemand" as well */
+ /* This mount option will force use of mandatory
+ (DOS/Windows style) byte range locks, instead of
+ using posix advisory byte range locks, even if the
+ Unix extensions are available and posix locks would
+ be supported otherwise. If Unix extensions are not
+ negotiated this has no effect since mandatory locks
+ would be used (mandatory locks is all that those
+ those servers support) */
+ vol->mand_lock = 1;
} else if (strnicmp(data, "setuids", 7) == 0) {
vol->setuids = 1;
} else if (strnicmp(data, "nosetuids", 9) == 0) {
@@ -1417,6 +1415,143 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
force_sig(SIGKILL, task);
}
+static struct TCP_Server_Info *
+cifs_get_tcp_session(struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *tcp_ses = NULL;
+ struct sockaddr addr;
+ struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
+ struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
+ int rc;
+
+ memset(&addr, 0, sizeof(struct sockaddr));
+
+ if (volume_info->UNCip && volume_info->UNC) {
+ rc = cifs_inet_pton(AF_INET, volume_info->UNCip,
+ &sin_server->sin_addr.s_addr);
+
+ if (rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6, volume_info->UNCip,
+ &sin_server6->sin6_addr.in6_u);
+ if (rc > 0)
+ addr.sa_family = AF_INET6;
+ } else {
+ addr.sa_family = AF_INET;
+ }
+
+ if (rc <= 0) {
+ /* we failed translating address */
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ cFYI(1, ("UNC: %s ip: %s", volume_info->UNC,
+ volume_info->UNCip));
+ } else if (volume_info->UNCip) {
+ /* BB using ip addr as tcp_ses name to connect to the
+ DFS root below */
+ cERROR(1, ("Connecting to DFS root not implemented yet"));
+ rc = -EINVAL;
+ goto out_err;
+ } else /* which tcp_sess DFS root would we conect to */ {
+ cERROR(1,
+ ("CIFS mount error: No UNC path (e.g. -o "
+ "unc=//192.168.1.100/public) specified"));
+ rc = -EINVAL;
+ goto out_err;
+ }
+
+ /* see if we already have a matching tcp_ses */
+ tcp_ses = cifs_find_tcp_session(&addr);
+ if (tcp_ses)
+ return tcp_ses;
+
+ tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+ if (!tcp_ses) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+
+ tcp_ses->hostname = extract_hostname(volume_info->UNC);
+ if (IS_ERR(tcp_ses->hostname)) {
+ rc = PTR_ERR(tcp_ses->hostname);
+ goto out_err;
+ }
+
+ tcp_ses->noblocksnd = volume_info->noblocksnd;
+ tcp_ses->noautotune = volume_info->noautotune;
+ atomic_set(&tcp_ses->inFlight, 0);
+ init_waitqueue_head(&tcp_ses->response_q);
+ init_waitqueue_head(&tcp_ses->request_q);
+ INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
+ mutex_init(&tcp_ses->srv_mutex);
+ memcpy(tcp_ses->workstation_RFC1001_name,
+ volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ memcpy(tcp_ses->server_RFC1001_name,
+ volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
+ tcp_ses->sequence_number = 0;
+ INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
+ INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
+
+ /*
+ * at this point we are the only ones with the pointer
+ * to the struct since the kernel thread not created yet
+ * no need to spinlock this init of tcpStatus or srv_count
+ */
+ tcp_ses->tcpStatus = CifsNew;
+ ++tcp_ses->srv_count;
+
+ if (addr.sa_family == AF_INET6) {
+ cFYI(1, ("attempting ipv6 connect"));
+ /* BB should we allow ipv6 on port 139? */
+ /* other OS never observed in Wild doing 139 with v6 */
+ memcpy(&tcp_ses->addr.sockAddr6, sin_server6,
+ sizeof(struct sockaddr_in6));
+ sin_server6->sin6_port = htons(volume_info->port);
+ rc = ipv6_connect(tcp_ses);
+ } else {
+ memcpy(&tcp_ses->addr.sockAddr, sin_server,
+ sizeof(struct sockaddr_in));
+ sin_server->sin_port = htons(volume_info->port);
+ rc = ipv4_connect(tcp_ses);
+ }
+ if (rc < 0) {
+ cERROR(1, ("Error connecting to socket. Aborting operation"));
+ goto out_err;
+ }
+
+ /*
+ * since we're in a cifs function already, we know that
+ * this will succeed. No need for try_module_get().
+ */
+ __module_get(THIS_MODULE);
+ tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread,
+ tcp_ses, "cifsd");
+ if (IS_ERR(tcp_ses->tsk)) {
+ rc = PTR_ERR(tcp_ses->tsk);
+ cERROR(1, ("error %d create cifsd thread", rc));
+ module_put(THIS_MODULE);
+ goto out_err;
+ }
+
+ /* thread spawned, put it on the list */
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
+ write_unlock(&cifs_tcp_ses_lock);
+
+ return tcp_ses;
+
+out_err:
+ if (tcp_ses) {
+ kfree(tcp_ses->hostname);
+ if (tcp_ses->ssocket)
+ sock_release(tcp_ses->ssocket);
+ kfree(tcp_ses);
+ }
+ return ERR_PTR(rc);
+}
+
static struct cifsSesInfo *
cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
{
@@ -1593,93 +1728,96 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
static int
-ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
- char *netbios_name, char *target_name,
- bool noblocksnd, bool noautotune)
+ipv4_connect(struct TCP_Server_Info *server)
{
int rc = 0;
- int connected = 0;
+ bool connected = false;
__be16 orig_port = 0;
+ struct socket *socket = server->ssocket;
- if (*csocket == NULL) {
+ if (socket == NULL) {
rc = sock_create_kern(PF_INET, SOCK_STREAM,
- IPPROTO_TCP, csocket);
+ IPPROTO_TCP, &socket);
if (rc < 0) {
cERROR(1, ("Error %d creating socket", rc));
- *csocket = NULL;
return rc;
- } else {
- /* BB other socket options to set KEEPALIVE, NODELAY? */
- cFYI(1, ("Socket created"));
- (*csocket)->sk->sk_allocation = GFP_NOFS;
- cifs_reclassify_socket4(*csocket);
}
+
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("Socket created"));
+ server->ssocket = socket;
+ socket->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket4(socket);
}
- psin_server->sin_family = AF_INET;
- if (psin_server->sin_port) { /* user overrode default port */
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
- sizeof(struct sockaddr_in), 0);
+ /* user overrode default port */
+ if (server->addr.sockAddr.sin_port) {
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr,
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
if (!connected) {
/* save original port so we can retry user specified port
later if fall back ports fail this time */
- orig_port = psin_server->sin_port;
+ orig_port = server->addr.sockAddr.sin_port;
/* do not retry on the same port we just failed on */
- if (psin_server->sin_port != htons(CIFS_PORT)) {
- psin_server->sin_port = htons(CIFS_PORT);
-
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
- sizeof(struct sockaddr_in), 0);
+ if (server->addr.sockAddr.sin_port != htons(CIFS_PORT)) {
+ server->addr.sockAddr.sin_port = htons(CIFS_PORT);
+ rc = socket->ops->connect(socket,
+ (struct sockaddr *)
+ &server->addr.sockAddr,
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
}
if (!connected) {
- psin_server->sin_port = htons(RFC1001_PORT);
- rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
- psin_server,
+ server->addr.sockAddr.sin_port = htons(RFC1001_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr,
sizeof(struct sockaddr_in), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
/* give up here - unless we want to retry on different
protocol families some day */
if (!connected) {
if (orig_port)
- psin_server->sin_port = orig_port;
+ server->addr.sockAddr.sin_port = orig_port;
cFYI(1, ("Error %d connecting to server via ipv4", rc));
- sock_release(*csocket);
- *csocket = NULL;
+ sock_release(socket);
+ server->ssocket = NULL;
return rc;
}
- /* Eventually check for other socket options to change from
- the default. sock_setsockopt not used because it expects
- user space buffer */
- cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
- (*csocket)->sk->sk_sndbuf,
- (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
- (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
- if (!noblocksnd)
- (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+
+
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 3 * HZ;
/* make the bufsizes depend on wsize/rsize and max requests */
- if (noautotune) {
- if ((*csocket)->sk->sk_sndbuf < (200 * 1024))
- (*csocket)->sk->sk_sndbuf = 200 * 1024;
- if ((*csocket)->sk->sk_rcvbuf < (140 * 1024))
- (*csocket)->sk->sk_rcvbuf = 140 * 1024;
+ if (server->noautotune) {
+ if (socket->sk->sk_sndbuf < (200 * 1024))
+ socket->sk->sk_sndbuf = 200 * 1024;
+ if (socket->sk->sk_rcvbuf < (140 * 1024))
+ socket->sk->sk_rcvbuf = 140 * 1024;
}
+ cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
+ socket->sk->sk_sndbuf,
+ socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo));
+
/* send RFC1001 sessinit */
- if (psin_server->sin_port == htons(RFC1001_PORT)) {
+ if (server->addr.sockAddr.sin_port == htons(RFC1001_PORT)) {
/* some servers require RFC1001 sessinit before sending
negprot - BB check reconnection in case where second
sessinit is sent but no second negprot */
@@ -1689,31 +1827,42 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
GFP_KERNEL);
if (ses_init_buf) {
ses_init_buf->trailer.session_req.called_len = 32;
- if (target_name && (target_name[0] != 0)) {
- rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
- target_name, 16);
- } else {
- rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
- DEFAULT_CIFS_CALLED_NAME, 16);
- }
+ if (server->server_RFC1001_name &&
+ server->server_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ server->server_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME,
+ RFC1001_NAME_LEN_WITH_NULL);
ses_init_buf->trailer.session_req.calling_len = 32;
+
/* calling name ends in null (byte 16) from old smb
convention. */
- if (netbios_name && (netbios_name[0] != 0)) {
- rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
- netbios_name, 16);
- } else {
- rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
- "LINUX_CIFS_CLNT", 16);
- }
+ if (server->workstation_RFC1001_name &&
+ server->workstation_RFC1001_name[0] != 0)
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ server->workstation_RFC1001_name,
+ RFC1001_NAME_LEN_WITH_NULL);
+ else
+ rfc1002mangle(ses_init_buf->trailer.
+ session_req.calling_name,
+ "LINUX_CIFS_CLNT",
+ RFC1001_NAME_LEN_WITH_NULL);
+
ses_init_buf->trailer.session_req.scope1 = 0;
ses_init_buf->trailer.session_req.scope2 = 0;
smb_buf = (struct smb_hdr *)ses_init_buf;
/* sizeof RFC1002_SESSION_REQUEST with no scope */
smb_buf->smb_buf_length = 0x81000044;
- rc = smb_send(*csocket, smb_buf, 0x44,
- (struct sockaddr *)psin_server, noblocksnd);
+ rc = smb_send(socket, smb_buf, 0x44,
+ (struct sockaddr *) &server->addr.sockAddr,
+ server->noblocksnd);
kfree(ses_init_buf);
msleep(1); /* RFC1001 layer in at least one server
requires very short break before negprot
@@ -1733,79 +1882,81 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
}
static int
-ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket,
- bool noblocksnd)
+ipv6_connect(struct TCP_Server_Info *server)
{
int rc = 0;
- int connected = 0;
+ bool connected = false;
__be16 orig_port = 0;
+ struct socket *socket = server->ssocket;
- if (*csocket == NULL) {
+ if (socket == NULL) {
rc = sock_create_kern(PF_INET6, SOCK_STREAM,
- IPPROTO_TCP, csocket);
+ IPPROTO_TCP, &socket);
if (rc < 0) {
cERROR(1, ("Error %d creating ipv6 socket", rc));
- *csocket = NULL;
+ socket = NULL;
return rc;
- } else {
- /* BB other socket options to set KEEPALIVE, NODELAY? */
- cFYI(1, ("ipv6 Socket created"));
- (*csocket)->sk->sk_allocation = GFP_NOFS;
- cifs_reclassify_socket6(*csocket);
}
- }
- psin_server->sin6_family = AF_INET6;
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1, ("ipv6 Socket created"));
+ server->ssocket = socket;
+ socket->sk->sk_allocation = GFP_NOFS;
+ cifs_reclassify_socket6(socket);
+ }
- if (psin_server->sin6_port) { /* user overrode default port */
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
+ /* user overrode default port */
+ if (server->addr.sockAddr6.sin6_port) {
+ rc = socket->ops->connect(socket,
+ (struct sockaddr *) &server->addr.sockAddr6,
sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
if (!connected) {
/* save original port so we can retry user specified port
later if fall back ports fail this time */
- orig_port = psin_server->sin6_port;
+ orig_port = server->addr.sockAddr6.sin6_port;
/* do not retry on the same port we just failed on */
- if (psin_server->sin6_port != htons(CIFS_PORT)) {
- psin_server->sin6_port = htons(CIFS_PORT);
-
- rc = (*csocket)->ops->connect(*csocket,
- (struct sockaddr *) psin_server,
+ if (server->addr.sockAddr6.sin6_port != htons(CIFS_PORT)) {
+ server->addr.sockAddr6.sin6_port = htons(CIFS_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr6,
sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
}
if (!connected) {
- psin_server->sin6_port = htons(RFC1001_PORT);
- rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
- psin_server, sizeof(struct sockaddr_in6), 0);
+ server->addr.sockAddr6.sin6_port = htons(RFC1001_PORT);
+ rc = socket->ops->connect(socket, (struct sockaddr *)
+ &server->addr.sockAddr6,
+ sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
- connected = 1;
+ connected = true;
}
/* give up here - unless we want to retry on different
protocol families some day */
if (!connected) {
if (orig_port)
- psin_server->sin6_port = orig_port;
+ server->addr.sockAddr6.sin6_port = orig_port;
cFYI(1, ("Error %d connecting to server via ipv6", rc));
- sock_release(*csocket);
- *csocket = NULL;
+ sock_release(socket);
+ server->ssocket = NULL;
return rc;
}
- /* Eventually check for other socket options to change from
- the default. sock_setsockopt not used because it expects
- user space buffer */
- (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
- if (!noblocksnd)
- (*csocket)->sk->sk_sndtimeo = 3 * HZ;
+ /*
+ * Eventually check for other socket options to change from
+ * the default. sock_setsockopt not used because it expects
+ * user space buffer
+ */
+ socket->sk->sk_rcvtimeo = 7 * HZ;
+ socket->sk->sk_sndtimeo = 3 * HZ;
+ server->ssocket = socket;
return rc;
}
@@ -2011,6 +2162,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
if (pvolume_info->nobrl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+ if (pvolume_info->mand_lock)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL;
if (pvolume_info->cifs_acl)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
if (pvolume_info->override_uid)
@@ -2035,32 +2188,30 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
{
int rc = 0;
int xid;
- struct socket *csocket = NULL;
- struct sockaddr addr;
- struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
- struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
- struct smb_vol volume_info;
+ struct smb_vol *volume_info;
struct cifsSesInfo *pSesInfo = NULL;
struct cifsTconInfo *tcon = NULL;
struct TCP_Server_Info *srvTcp = NULL;
xid = GetXid();
-/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
+ volume_info = kzalloc(sizeof(struct smb_vol), GFP_KERNEL);
+ if (!volume_info) {
+ rc = -ENOMEM;
+ goto out;
+ }
- memset(&addr, 0, sizeof(struct sockaddr));
- memset(&volume_info, 0, sizeof(struct smb_vol));
- if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
+ if (cifs_parse_mount_options(mount_data, devname, volume_info)) {
rc = -EINVAL;
goto out;
}
- if (volume_info.nullauth) {
+ if (volume_info->nullauth) {
cFYI(1, ("null user"));
- volume_info.username = "";
- } else if (volume_info.username) {
+ volume_info->username = "";
+ } else if (volume_info->username) {
/* BB fixme parse for domain name here */
- cFYI(1, ("Username: %s", volume_info.username));
+ cFYI(1, ("Username: %s", volume_info->username));
} else {
cifserror("No username specified");
/* In userspace mount helper we can get user name from alternate
@@ -2069,139 +2220,29 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
goto out;
}
- if (volume_info.UNCip && volume_info.UNC) {
- rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
- &sin_server->sin_addr.s_addr);
-
- if (rc <= 0) {
- /* not ipv4 address, try ipv6 */
- rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
- &sin_server6->sin6_addr.in6_u);
- if (rc > 0)
- addr.sa_family = AF_INET6;
- } else {
- addr.sa_family = AF_INET;
- }
-
- if (rc <= 0) {
- /* we failed translating address */
- rc = -EINVAL;
- goto out;
- }
-
- cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
- /* success */
- rc = 0;
- } else if (volume_info.UNCip) {
- /* BB using ip addr as server name to connect to the
- DFS root below */
- cERROR(1, ("Connecting to DFS root not implemented yet"));
- rc = -EINVAL;
- goto out;
- } else /* which servers DFS root would we conect to */ {
- cERROR(1,
- ("CIFS mount error: No UNC path (e.g. -o "
- "unc=//192.168.1.100/public) specified"));
- rc = -EINVAL;
- goto out;
- }
/* this is needed for ASCII cp to Unicode converts */
- if (volume_info.iocharset == NULL) {
+ if (volume_info->iocharset == NULL) {
cifs_sb->local_nls = load_nls_default();
/* load_nls_default can not return null */
} else {
- cifs_sb->local_nls = load_nls(volume_info.iocharset);
+ cifs_sb->local_nls = load_nls(volume_info->iocharset);
if (cifs_sb->local_nls == NULL) {
cERROR(1, ("CIFS mount error: iocharset %s not found",
- volume_info.iocharset));
+ volume_info->iocharset));
rc = -ELIBACC;
goto out;
}
}
- srvTcp = cifs_find_tcp_session(&addr);
- if (!srvTcp) { /* create socket */
- if (addr.sa_family == AF_INET6) {
- cFYI(1, ("attempting ipv6 connect"));
- /* BB should we allow ipv6 on port 139? */
- /* other OS never observed in Wild doing 139 with v6 */
- sin_server6->sin6_port = htons(volume_info.port);
- rc = ipv6_connect(sin_server6, &csocket,
- volume_info.noblocksnd);
- } else {
- sin_server->sin_port = htons(volume_info.port);
- rc = ipv4_connect(sin_server, &csocket,
- volume_info.source_rfc1001_name,
- volume_info.target_rfc1001_name,
- volume_info.noblocksnd,
- volume_info.noautotune);
- }
- if (rc < 0) {
- cERROR(1, ("Error connecting to socket. "
- "Aborting operation"));
- if (csocket != NULL)
- sock_release(csocket);
- goto out;
- }
-
- srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
- if (!srvTcp) {
- rc = -ENOMEM;
- sock_release(csocket);
- goto out;
- } else {
- srvTcp->noblocksnd = volume_info.noblocksnd;
- srvTcp->noautotune = volume_info.noautotune;
- if (addr.sa_family == AF_INET6)
- memcpy(&srvTcp->addr.sockAddr6, sin_server6,
- sizeof(struct sockaddr_in6));
- else
- memcpy(&srvTcp->addr.sockAddr, sin_server,
- sizeof(struct sockaddr_in));
- atomic_set(&srvTcp->inFlight, 0);
- /* BB Add code for ipv6 case too */
- srvTcp->ssocket = csocket;
- srvTcp->hostname = extract_hostname(volume_info.UNC);
- if (IS_ERR(srvTcp->hostname)) {
- rc = PTR_ERR(srvTcp->hostname);
- sock_release(csocket);
- goto out;
- }
- init_waitqueue_head(&srvTcp->response_q);
- init_waitqueue_head(&srvTcp->request_q);
- INIT_LIST_HEAD(&srvTcp->pending_mid_q);
- /* at this point we are the only ones with the pointer
- to the struct since the kernel thread not created yet
- so no need to spinlock this init of tcpStatus */
- srvTcp->tcpStatus = CifsNew;
- init_MUTEX(&srvTcp->tcpSem);
- srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
- if (IS_ERR(srvTcp->tsk)) {
- rc = PTR_ERR(srvTcp->tsk);
- cERROR(1, ("error %d create cifsd thread", rc));
- srvTcp->tsk = NULL;
- sock_release(csocket);
- kfree(srvTcp->hostname);
- goto out;
- }
- rc = 0;
- memcpy(srvTcp->workstation_RFC1001_name,
- volume_info.source_rfc1001_name, 16);
- memcpy(srvTcp->server_RFC1001_name,
- volume_info.target_rfc1001_name, 16);
- srvTcp->sequence_number = 0;
- INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
- INIT_LIST_HEAD(&srvTcp->smb_ses_list);
- ++srvTcp->srv_count;
- write_lock(&cifs_tcp_ses_lock);
- list_add(&srvTcp->tcp_ses_list,
- &cifs_tcp_ses_list);
- write_unlock(&cifs_tcp_ses_lock);
- }
+ /* get a reference to a tcp session */
+ srvTcp = cifs_get_tcp_session(volume_info);
+ if (IS_ERR(srvTcp)) {
+ rc = PTR_ERR(srvTcp);
+ goto out;
}
- pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
+ pSesInfo = cifs_find_smb_ses(srvTcp, volume_info->username);
if (pSesInfo) {
cFYI(1, ("Existing smb sess found (status=%d)",
pSesInfo->status));
@@ -2228,31 +2269,38 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* new SMB session uses our srvTcp ref */
pSesInfo->server = srvTcp;
- sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
- NIPQUAD(sin_server->sin_addr.s_addr));
+ if (srvTcp->addr.sockAddr6.sin6_family == AF_INET6)
+ sprintf(pSesInfo->serverName, "%pI6",
+ &srvTcp->addr.sockAddr6.sin6_addr);
+ else
+ sprintf(pSesInfo->serverName, "%pI4",
+ &srvTcp->addr.sockAddr.sin_addr.s_addr);
write_lock(&cifs_tcp_ses_lock);
list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
write_unlock(&cifs_tcp_ses_lock);
- /* volume_info.password freed at unmount */
- if (volume_info.password) {
- pSesInfo->password = volume_info.password;
- /* set to NULL to prevent freeing on exit */
- volume_info.password = NULL;
+ /* volume_info->password freed at unmount */
+ if (volume_info->password) {
+ pSesInfo->password = kstrdup(volume_info->password,
+ GFP_KERNEL);
+ if (!pSesInfo->password) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
}
- if (volume_info.username)
- strncpy(pSesInfo->userName, volume_info.username,
+ if (volume_info->username)
+ strncpy(pSesInfo->userName, volume_info->username,
MAX_USERNAME_SIZE);
- if (volume_info.domainname) {
- int len = strlen(volume_info.domainname);
+ if (volume_info->domainname) {
+ int len = strlen(volume_info->domainname);
pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
if (pSesInfo->domainName)
strcpy(pSesInfo->domainName,
- volume_info.domainname);
+ volume_info->domainname);
}
- pSesInfo->linux_uid = volume_info.linux_uid;
- pSesInfo->overrideSecFlg = volume_info.secFlg;
+ pSesInfo->linux_uid = volume_info->linux_uid;
+ pSesInfo->overrideSecFlg = volume_info->secFlg;
down(&pSesInfo->sesSem);
/* BB FIXME need to pass vol->secFlgs BB */
@@ -2263,14 +2311,14 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* search for existing tcon to this server share */
if (!rc) {
- setup_cifs_sb(&volume_info, cifs_sb);
+ setup_cifs_sb(volume_info, cifs_sb);
- tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
+ tcon = cifs_find_tcon(pSesInfo, volume_info->UNC);
if (tcon) {
cFYI(1, ("Found match on UNC path"));
/* existing tcon already has a reference */
cifs_put_smb_ses(pSesInfo);
- if (tcon->seal != volume_info.seal)
+ if (tcon->seal != volume_info->seal)
cERROR(1, ("transport encryption setting "
"conflicts with existing tid"));
} else {
@@ -2279,11 +2327,20 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
rc = -ENOMEM;
goto mount_fail_check;
}
+
tcon->ses = pSesInfo;
+ if (volume_info->password) {
+ tcon->password = kstrdup(volume_info->password,
+ GFP_KERNEL);
+ if (!tcon->password) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
+ }
/* check for null share name ie connect to dfs root */
- if ((strchr(volume_info.UNC + 3, '\\') == NULL)
- && (strchr(volume_info.UNC + 3, '/') == NULL)) {
+ if ((strchr(volume_info->UNC + 3, '\\') == NULL)
+ && (strchr(volume_info->UNC + 3, '/') == NULL)) {
/* rc = connect_to_dfs_path(...) */
cFYI(1, ("DFS root not supported"));
rc = -ENODEV;
@@ -2292,10 +2349,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* BB Do we need to wrap sesSem around
* this TCon call and Unix SetFS as
* we do on SessSetup and reconnect? */
- rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
+ rc = CIFSTCon(xid, pSesInfo, volume_info->UNC,
tcon, cifs_sb->local_nls);
cFYI(1, ("CIFS Tcon rc = %d", rc));
- if (volume_info.nodfs) {
+ if (volume_info->nodfs) {
tcon->Flags &= ~SMB_SHARE_IS_IN_DFS;
cFYI(1, ("DFS disabled (%d)",
tcon->Flags));
@@ -2303,7 +2360,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
}
if (rc)
goto mount_fail_check;
- tcon->seal = volume_info.seal;
+ tcon->seal = volume_info->seal;
write_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
write_unlock(&cifs_tcp_ses_lock);
@@ -2313,9 +2370,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
to a share so for resources mounted more than once
to the same server share the last value passed in
for the retry flag is used */
- tcon->retry = volume_info.retry;
- tcon->nocase = volume_info.nocase;
- tcon->local_lease = volume_info.local_lease;
+ tcon->retry = volume_info->retry;
+ tcon->nocase = volume_info->nocase;
+ tcon->local_lease = volume_info->local_lease;
}
if (pSesInfo) {
if (pSesInfo->capabilities & CAP_LARGE_FILES) {
@@ -2352,7 +2409,7 @@ mount_fail_check:
if (tcon->ses->capabilities & CAP_UNIX)
/* reset of caps checks mount to see if unix extensions
disabled for just this mount */
- reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+ reset_cifs_unix_caps(xid, tcon, sb, volume_info);
else
tcon->unix_ext = 0; /* server does not support them */
@@ -2371,18 +2428,22 @@ mount_fail_check:
cifs_sb->rsize = min(cifs_sb->rsize,
(tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
- /* volume_info.password is freed above when existing session found
+ /* volume_info->password is freed above when existing session found
(in which case it is not needed anymore) but when new sesion is created
the password ptr is put in the new session structure (in which case the
password will be freed at unmount time) */
out:
/* zero out password before freeing */
- if (volume_info.password != NULL) {
- memset(volume_info.password, 0, strlen(volume_info.password));
- kfree(volume_info.password);
+ if (volume_info) {
+ if (volume_info->password != NULL) {
+ memset(volume_info->password, 0,
+ strlen(volume_info->password));
+ kfree(volume_info->password);
+ }
+ kfree(volume_info->UNC);
+ kfree(volume_info->prepath);
+ kfree(volume_info);
}
- kfree(volume_info.UNC);
- kfree(volume_info.prepath);
FreeXid(xid);
return rc;
}
@@ -2533,7 +2594,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
__u16 action = le16_to_cpu(pSMBr->resp.Action);
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
+ cFYI(1, ("Guest login")); /* BB mark SesInfo struct? */
ses->Suid = smb_buffer_response->Uid; /* UID left in wire format
(little endian) */
cFYI(1, ("UID = %d ", ses->Suid));
@@ -2679,13 +2740,11 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
len));
}
} else {
- cERROR(1,
- (" Security Blob Length extends beyond "
+ cERROR(1, ("Security Blob Length extends beyond "
"end of SMB"));
}
} else {
- cERROR(1,
- (" Invalid Word count %d: ",
+ cERROR(1, ("Invalid Word count %d: ",
smb_buffer_response->WordCount));
rc = -EIO;
}
@@ -2843,7 +2902,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login"));
+ cFYI(1, ("Guest login"));
/* Do we want to set anything in SesInfo struct when guest login? */
bcc_ptr = pByteArea(smb_buffer_response);
@@ -2851,8 +2910,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
if (SecurityBlob2->MessageType != NtLmChallenge) {
- cFYI(1,
- ("Unexpected NTLMSSP message type received %d",
+ cFYI(1, ("Unexpected NTLMSSP message type received %d",
SecurityBlob2->MessageType));
} else if (ses) {
ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
@@ -3024,8 +3082,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
cERROR(1, ("No session structure passed in."));
}
} else {
- cERROR(1,
- (" Invalid Word count %d:",
+ cERROR(1, ("Invalid Word count %d:",
smb_buffer_response->WordCount));
rc = -EIO;
}
@@ -3264,7 +3321,7 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
__u16 action = le16_to_cpu(pSMBr->resp.Action);
__u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
- cFYI(1, (" Guest login")); /* BB Should we set anything
+ cFYI(1, ("Guest login")); /* BB Should we set anything
in SesInfo struct ? */
/* if (SecurityBlob2->MessageType != NtLm??) {
cFYI("Unexpected message type on auth response is %d"));
@@ -3487,12 +3544,14 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
NTLMv2 password here) */
#ifdef CONFIG_CIFS_WEAK_PW_HASH
if ((extended_security & CIFSSEC_MAY_LANMAN) &&
- (ses->server->secType == LANMAN))
- calc_lanman_hash(ses, bcc_ptr);
+ (ses->server->secType == LANMAN))
+ calc_lanman_hash(tcon->password, ses->server->cryptKey,
+ ses->server->secMode &
+ SECMODE_PW_ENCRYPT ? true : false,
+ bcc_ptr);
else
#endif /* CIFS_WEAK_PW_HASH */
- SMBNTencrypt(ses->password,
- ses->server->cryptKey,
+ SMBNTencrypt(tcon->password, ses->server->cryptKey,
bcc_ptr);
bcc_ptr += CIFS_SESS_KEY_SIZE;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index e962e75e6f7b..838d9c720a5c 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -235,11 +235,11 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64) current->fsuid;
+ args.uid = (__u64) current_fsuid();
if (inode->i_mode & S_ISGID)
args.gid = (__u64) inode->i_gid;
else
- args.gid = (__u64) current->fsgid;
+ args.gid = (__u64) current_fsgid();
} else {
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
@@ -271,13 +271,13 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if ((oplock & CIFS_CREATE_ACTION) &&
(cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_SET_UID)) {
- newinode->i_uid = current->fsuid;
+ newinode->i_uid = current_fsuid();
if (inode->i_mode & S_ISGID)
newinode->i_gid =
inode->i_gid;
else
newinode->i_gid =
- current->fsgid;
+ current_fsgid();
}
}
}
@@ -375,8 +375,8 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
.device = device_number,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64) current->fsuid;
- args.gid = (__u64) current->fsgid;
+ args.uid = (__u64) current_fsuid();
+ args.gid = (__u64) current_fsgid();
} else {
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
@@ -483,7 +483,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
xid = GetXid();
- cFYI(1, (" parent inode = 0x%p name is: %s and dentry = 0x%p",
+ cFYI(1, ("parent inode = 0x%p name is: %s and dentry = 0x%p",
parent_dir_inode, direntry->d_name.name, direntry));
/* check whether path exists */
@@ -515,12 +515,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
}
if (direntry->d_inode != NULL) {
- cFYI(1, (" non-NULL inode in lookup"));
+ cFYI(1, ("non-NULL inode in lookup"));
} else {
- cFYI(1, (" NULL inode in lookup"));
+ cFYI(1, ("NULL inode in lookup"));
}
- cFYI(1,
- (" Full path: %s inode = 0x%p", full_path, direntry->d_inode));
+ cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
if (pTcon->unix_ext)
rc = cifs_get_inode_info_unix(&newInode, full_path,
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index f0a81e631ae6..b1e1fc6a6e6a 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -644,10 +644,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
__u64 length;
bool wait_flag = false;
struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *pTcon;
+ struct cifsTconInfo *tcon;
__u16 netfid;
__u8 lockType = LOCKING_ANDX_LARGE_FILES;
- bool posix_locking;
+ bool posix_locking = 0;
length = 1 + pfLock->fl_end - pfLock->fl_start;
rc = -EACCES;
@@ -698,7 +698,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
cFYI(1, ("Unknown type of lock"));
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- pTcon = cifs_sb->tcon;
+ tcon = cifs_sb->tcon;
if (file->private_data == NULL) {
FreeXid(xid);
@@ -706,9 +706,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
netfid = ((struct cifsFileInfo *)file->private_data)->netfid;
- posix_locking = (cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(cifs_sb->tcon->fsUnixInfo.Capability));
-
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ posix_locking = 1;
/* BB add code here to normalize offset and length to
account for negative length which we can not accept over the
wire */
@@ -719,7 +720,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
posix_lock_type = CIFS_RDLCK;
else
posix_lock_type = CIFS_WRLCK;
- rc = CIFSSMBPosixLock(xid, pTcon, netfid, 1 /* get */,
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, 1 /* get */,
length, pfLock,
posix_lock_type, wait_flag);
FreeXid(xid);
@@ -727,10 +728,10 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
}
/* BB we could chain these into one lock request BB */
- rc = CIFSSMBLock(xid, pTcon, netfid, length, pfLock->fl_start,
+ rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start,
0, 1, lockType, 0 /* wait flag */ );
if (rc == 0) {
- rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ rc = CIFSSMBLock(xid, tcon, netfid, length,
pfLock->fl_start, 1 /* numUnlock */ ,
0 /* numLock */ , lockType,
0 /* wait flag */ );
@@ -767,7 +768,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if (numUnlock == 1)
posix_lock_type = CIFS_UNLCK;
- rc = CIFSSMBPosixLock(xid, pTcon, netfid, 0 /* set */,
+ rc = CIFSSMBPosixLock(xid, tcon, netfid, 0 /* set */,
length, pfLock,
posix_lock_type, wait_flag);
} else {
@@ -775,7 +776,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
(struct cifsFileInfo *)file->private_data;
if (numLock) {
- rc = CIFSSMBLock(xid, pTcon, netfid, length,
+ rc = CIFSSMBLock(xid, tcon, netfid, length,
pfLock->fl_start,
0, numLock, lockType, wait_flag);
@@ -796,7 +797,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
if (pfLock->fl_start <= li->offset &&
(pfLock->fl_start + length) >=
(li->offset + li->length)) {
- stored_rc = CIFSSMBLock(xid, pTcon,
+ stored_rc = CIFSSMBLock(xid, tcon,
netfid,
li->length, li->offset,
1, 0, li->type, false);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index ff8c68de4a92..f247da9f4edc 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1,7 +1,7 @@
/*
* fs/cifs/inode.c
*
- * Copyright (C) International Business Machines Corp., 2002,2007
+ * Copyright (C) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
@@ -621,6 +621,47 @@ static const struct inode_operations cifs_ipc_inode_ops = {
.lookup = cifs_lookup,
};
+static char *build_path_to_root(struct cifs_sb_info *cifs_sb)
+{
+ int pplen = cifs_sb->prepathlen;
+ int dfsplen;
+ char *full_path = NULL;
+
+ /* if no prefix path, simply set path to the root of share to "" */
+ if (pplen == 0) {
+ full_path = kmalloc(1, GFP_KERNEL);
+ if (full_path)
+ full_path[0] = 0;
+ return full_path;
+ }
+
+ if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
+ dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
+ else
+ dfsplen = 0;
+
+ full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
+ if (full_path == NULL)
+ return full_path;
+
+ if (dfsplen) {
+ strncpy(full_path, cifs_sb->tcon->treeName, dfsplen);
+ /* switch slash direction in prepath depending on whether
+ * windows or posix style path names
+ */
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
+ int i;
+ for (i = 0; i < dfsplen; i++) {
+ if (full_path[i] == '\\')
+ full_path[i] = '/';
+ }
+ }
+ }
+ strncpy(full_path + dfsplen, cifs_sb->prepath, pplen);
+ full_path[dfsplen + pplen] = 0; /* add trailing null */
+ return full_path;
+}
+
/* gets root inode */
struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
{
@@ -628,6 +669,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
struct cifs_sb_info *cifs_sb;
struct inode *inode;
long rc;
+ char *full_path;
inode = iget_locked(sb, ino);
if (!inode)
@@ -636,13 +678,17 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
return inode;
cifs_sb = CIFS_SB(inode->i_sb);
- xid = GetXid();
+ full_path = build_path_to_root(cifs_sb);
+ if (full_path == NULL)
+ return ERR_PTR(-ENOMEM);
+ xid = GetXid();
if (cifs_sb->tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
+ rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb,
+ xid);
else
- rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid,
- NULL);
+ rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb,
+ xid, NULL);
if (rc && cifs_sb->tcon->ipc) {
cFYI(1, ("ipc connection - fake read inode"));
inode->i_mode |= S_IFDIR;
@@ -652,6 +698,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
inode->i_uid = cifs_sb->mnt_uid;
inode->i_gid = cifs_sb->mnt_gid;
} else if (rc) {
+ kfree(full_path);
_FreeXid(xid);
iget_failed(inode);
return ERR_PTR(rc);
@@ -659,6 +706,7 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
unlock_new_inode(inode);
+ kfree(full_path);
/* can not call macro FreeXid here since in a void func
* TODO: This is no longer true
*/
@@ -1143,11 +1191,11 @@ mkdir_get_info:
.device = 0,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64)current->fsuid;
+ args.uid = (__u64)current_fsuid();
if (inode->i_mode & S_ISGID)
args.gid = (__u64)inode->i_gid;
else
- args.gid = (__u64)current->fsgid;
+ args.gid = (__u64)current_fsgid();
} else {
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
@@ -1184,13 +1232,13 @@ mkdir_get_info:
if (cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_SET_UID) {
direntry->d_inode->i_uid =
- current->fsuid;
+ current_fsuid();
if (inode->i_mode & S_ISGID)
direntry->d_inode->i_gid =
inode->i_gid;
else
direntry->d_inode->i_gid =
- current->fsgid;
+ current_fsgid();
}
}
}
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 0088a5b52564..f94650683a00 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -65,7 +65,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
switch (command) {
case CIFS_IOC_CHECKUMOUNT:
cFYI(1, ("User unmount attempted"));
- if (cifs_sb->mnt_uid == current->uid)
+ if (cifs_sb->mnt_uid == current_uid())
rc = 0;
else {
rc = -EACCES;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 9ee3f689c2b0..4c89c572891a 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -97,7 +97,10 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
kfree(buf_to_free->serverOS);
kfree(buf_to_free->serverDomain);
kfree(buf_to_free->serverNOS);
- kfree(buf_to_free->password);
+ if (buf_to_free->password) {
+ memset(buf_to_free->password, 0, strlen(buf_to_free->password));
+ kfree(buf_to_free->password);
+ }
kfree(buf_to_free->domainName);
kfree(buf_to_free);
}
@@ -129,6 +132,10 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
}
atomic_dec(&tconInfoAllocCount);
kfree(buf_to_free->nativeFileSystem);
+ if (buf_to_free->password) {
+ memset(buf_to_free->password, 0, strlen(buf_to_free->password));
+ kfree(buf_to_free->password);
+ }
kfree(buf_to_free);
}
@@ -338,13 +345,13 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
/* BB Add support for establishing new tCon and SMB Session */
/* with userid/password pairs found on the smb session */
/* for other target tcp/ip addresses BB */
- if (current->fsuid != treeCon->ses->linux_uid) {
+ if (current_fsuid() != treeCon->ses->linux_uid) {
cFYI(1, ("Multiuser mode and UID "
"did not match tcon uid"));
read_lock(&cifs_tcp_ses_lock);
list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
- if (ses->linux_uid == current->fsuid) {
+ if (ses->linux_uid == current_fsuid()) {
if (ses->server == treeCon->ses->server) {
cFYI(1, ("found matching uid substitute right smb_uid"));
buffer->Uid = ses->Suid;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 2851d5da0c8c..5f22de7b79a9 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -417,7 +417,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
/* BB calculate hash with password */
/* and copy into bcc */
- calc_lanman_hash(ses, lnm_session_key);
+ calc_lanman_hash(ses->password, ses->server->cryptKey,
+ ses->server->secMode & SECMODE_PW_ENCRYPT ?
+ true : false, lnm_session_key);
+
ses->flags |= CIFS_SES_LANMAN;
memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE;
diff --git a/fs/cifs/smbdes.c b/fs/cifs/smbdes.c
index 04943c976f98..224a1f478966 100644
--- a/fs/cifs/smbdes.c
+++ b/fs/cifs/smbdes.c
@@ -318,7 +318,8 @@ str_to_key(unsigned char *str, unsigned char *key)
}
static void
-smbhash(unsigned char *out, unsigned char *in, unsigned char *key, int forw)
+smbhash(unsigned char *out, const unsigned char *in, unsigned char *key,
+ int forw)
{
int i;
char *outb; /* outb[64] */
@@ -363,7 +364,7 @@ E_P16(unsigned char *p14, unsigned char *p16)
}
void
-E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24)
+E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)
{
smbhash(p24, c8, p21, 1);
smbhash(p24 + 8, c8, p21 + 7, 1);
diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c
index ff3232fa1015..93fb09a99c69 100644
--- a/fs/cifs/smbencrypt.c
+++ b/fs/cifs/smbencrypt.c
@@ -49,9 +49,10 @@
/*The following definitions come from libsmb/smbencrypt.c */
-void SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
+void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
+ unsigned char *p24);
void E_md4hash(const unsigned char *passwd, unsigned char *p16);
-static void SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8,
+static void SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8,
unsigned char p24[24]);
void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
@@ -61,7 +62,7 @@ void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
encrypted password into p24 */
/* Note that password must be uppercased and null terminated */
void
-SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24)
+SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24)
{
unsigned char p14[15], p21[21];
@@ -212,7 +213,7 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n,
/* Does the des encryption from the NT or LM MD4 hash. */
static void
-SMBOWFencrypt(unsigned char passwd[16], unsigned char *c8,
+SMBOWFencrypt(unsigned char passwd[16], const unsigned char *c8,
unsigned char p24[24])
{
unsigned char p21[21];
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index ff8243a8fe3e..7ebe6599ed3a 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -37,15 +37,11 @@ extern mempool_t *cifs_mid_poolp;
extern struct kmem_cache *cifs_oplock_cachep;
static struct mid_q_entry *
-AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
+AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
{
struct mid_q_entry *temp;
- if (ses == NULL) {
- cERROR(1, ("Null session passed in to AllocMidQEntry"));
- return NULL;
- }
- if (ses->server == NULL) {
+ if (server == NULL) {
cERROR(1, ("Null TCP session in AllocMidQEntry"));
return NULL;
}
@@ -62,12 +58,11 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
/* when mid allocated can be before when sent */
temp->when_alloc = jiffies;
- temp->ses = ses;
temp->tsk = current;
}
spin_lock(&GlobalMid_Lock);
- list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
+ list_add_tail(&temp->qhead, &server->pending_mid_q);
atomic_inc(&midCount);
temp->midState = MID_REQUEST_ALLOCATED;
spin_unlock(&GlobalMid_Lock);
@@ -349,37 +344,38 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
if (long_op == CIFS_ASYNC_OP) {
/* oplock breaks must not be held up */
atomic_inc(&ses->server->inFlight);
- } else {
- spin_lock(&GlobalMid_Lock);
- while (1) {
- if (atomic_read(&ses->server->inFlight) >=
- cifs_max_pending){
- spin_unlock(&GlobalMid_Lock);
+ return 0;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ while (1) {
+ if (atomic_read(&ses->server->inFlight) >=
+ cifs_max_pending){
+ spin_unlock(&GlobalMid_Lock);
#ifdef CONFIG_CIFS_STATS2
- atomic_inc(&ses->server->num_waiters);
+ atomic_inc(&ses->server->num_waiters);
#endif
- wait_event(ses->server->request_q,
- atomic_read(&ses->server->inFlight)
- < cifs_max_pending);
+ wait_event(ses->server->request_q,
+ atomic_read(&ses->server->inFlight)
+ < cifs_max_pending);
#ifdef CONFIG_CIFS_STATS2
- atomic_dec(&ses->server->num_waiters);
+ atomic_dec(&ses->server->num_waiters);
#endif
- spin_lock(&GlobalMid_Lock);
- } else {
- if (ses->server->tcpStatus == CifsExiting) {
- spin_unlock(&GlobalMid_Lock);
- return -ENOENT;
- }
-
- /* can not count locking commands against total
- as they are allowed to block on server */
-
- /* update # of requests on the wire to server */
- if (long_op != CIFS_BLOCKING_OP)
- atomic_inc(&ses->server->inFlight);
+ spin_lock(&GlobalMid_Lock);
+ } else {
+ if (ses->server->tcpStatus == CifsExiting) {
spin_unlock(&GlobalMid_Lock);
- break;
+ return -ENOENT;
}
+
+ /* can not count locking commands against total
+ as they are allowed to block on server */
+
+ /* update # of requests on the wire to server */
+ if (long_op != CIFS_BLOCKING_OP)
+ atomic_inc(&ses->server->inFlight);
+ spin_unlock(&GlobalMid_Lock);
+ break;
}
}
return 0;
@@ -390,17 +386,21 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf,
{
if (ses->server->tcpStatus == CifsExiting) {
return -ENOENT;
- } else if (ses->server->tcpStatus == CifsNeedReconnect) {
+ }
+
+ if (ses->server->tcpStatus == CifsNeedReconnect) {
cFYI(1, ("tcp session dead - return to caller to retry"));
return -EAGAIN;
- } else if (ses->status != CifsGood) {
+ }
+
+ if (ses->status != CifsGood) {
/* check if SMB session is bad because we are setting it up */
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE))
return -EAGAIN;
/* else ok - we are setting up session */
}
- *ppmidQ = AllocMidQEntry(in_buf, ses);
+ *ppmidQ = AllocMidQEntry(in_buf, ses->server);
if (*ppmidQ == NULL)
return -ENOMEM;
return 0;
@@ -415,11 +415,8 @@ static int wait_for_response(struct cifsSesInfo *ses,
for (;;) {
curr_timeout = timeout + jiffies;
- wait_event(ses->server->response_q,
- (!(midQ->midState == MID_REQUEST_SUBMITTED)) ||
- time_after(jiffies, curr_timeout) ||
- ((ses->server->tcpStatus != CifsGood) &&
- (ses->server->tcpStatus != CifsNew)));
+ wait_event_timeout(ses->server->response_q,
+ midQ->midState != MID_REQUEST_SUBMITTED, timeout);
if (time_after(jiffies, curr_timeout) &&
(midQ->midState == MID_REQUEST_SUBMITTED) &&
@@ -521,11 +518,11 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf);
/* Update # of requests on wire to server */
atomic_dec(&ses->server->inFlight);
@@ -533,6 +530,11 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ cifs_small_buf_release(in_buf);
+ goto out;
+ }
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
@@ -546,7 +548,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(in_buf);
if (rc < 0)
@@ -581,10 +583,8 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
- if (midQ->resp_buf) {
- spin_unlock(&GlobalMid_Lock);
- receive_len = midQ->resp_buf->smb_buf_length;
- } else {
+
+ if (midQ->resp_buf == NULL) {
cERROR(1, ("No response to cmd %d mid %d",
midQ->command, midQ->mid));
if (midQ->midState == MID_REQUEST_SUBMITTED) {
@@ -612,53 +612,59 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
- if (midQ->resp_buf &&
- (midQ->midState == MID_RESPONSE_RECEIVED)) {
-
- iov[0].iov_base = (char *)midQ->resp_buf;
- if (midQ->largeBuf)
- *pRespBufType = CIFS_LARGE_BUFFER;
- else
- *pRespBufType = CIFS_SMALL_BUFFER;
- iov[0].iov_len = receive_len + 4;
-
- dump_smb(midQ->resp_buf, 80);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(midQ->resp_buf,
+ goto out;
+ }
+
+ /* rcvd frame is ok */
+
+ if (midQ->resp_buf &&
+ (midQ->midState == MID_RESPONSE_RECEIVED)) {
+
+ iov[0].iov_base = (char *)midQ->resp_buf;
+ if (midQ->largeBuf)
+ *pRespBufType = CIFS_LARGE_BUFFER;
+ else
+ *pRespBufType = CIFS_SMALL_BUFFER;
+ iov[0].iov_len = receive_len + 4;
+
+ dump_smb(midQ->resp_buf, 80);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(midQ->resp_buf,
&ses->server->mac_signing_key,
midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
-
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(midQ->resp_buf,
- flags & CIFS_LOG_ERROR);
-
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
- BCC(midQ->resp_buf) =
- le16_to_cpu(BCC_LE(midQ->resp_buf));
- if ((flags & CIFS_NO_RESP) == 0)
- midQ->resp_buf = NULL; /* mark it so buf will
- not be freed by
- DeleteMidQEntry */
- } else {
- rc = -EIO;
- cFYI(1, ("Bad MID state?"));
}
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(midQ->resp_buf,
+ flags & CIFS_LOG_ERROR);
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
+ BCC(midQ->resp_buf) =
+ le16_to_cpu(BCC_LE(midQ->resp_buf));
+ if ((flags & CIFS_NO_RESP) == 0)
+ midQ->resp_buf = NULL; /* mark it so buf will
+ not be freed by
+ DeleteMidQEntry */
+ } else {
+ rc = -EIO;
+ cFYI(1, ("Bad MID state?"));
}
out:
@@ -695,6 +701,12 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
to the same server. We may make this configurable later or
use ses->maxReq */
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ return -EIO;
+ }
+
rc = wait_for_free_request(ses, long_op);
if (rc)
return rc;
@@ -703,29 +715,22 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
/* Update # of requests on wire to server */
atomic_dec(&ses->server->inFlight);
wake_up(&ses->server->request_q);
return rc;
}
- if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- cERROR(1, ("Illegal length, greater than maximum frame, %d",
- in_buf->smb_buf_length));
- DeleteMidQEntry(midQ);
- up(&ses->server->tcpSem);
- /* Update # of requests on wire to server */
- atomic_dec(&ses->server->inFlight);
- wake_up(&ses->server->request_q);
- return -EIO;
- }
-
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
+ mutex_unlock(&ses->server->srv_mutex);
+ goto out;
+ }
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
@@ -738,7 +743,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
if (rc < 0)
goto out;
@@ -772,10 +777,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
wait_for_response(ses, midQ, timeout, 10 * HZ);
spin_lock(&GlobalMid_Lock);
- if (midQ->resp_buf) {
- spin_unlock(&GlobalMid_Lock);
- receive_len = midQ->resp_buf->smb_buf_length;
- } else {
+ if (midQ->resp_buf == NULL) {
cERROR(1, ("No response for cmd %d mid %d",
midQ->command, midQ->mid));
if (midQ->midState == MID_REQUEST_SUBMITTED) {
@@ -803,47 +805,52 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
return rc;
}
+ spin_unlock(&GlobalMid_Lock);
+ receive_len = midQ->resp_buf->smb_buf_length;
+
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
-
- if (midQ->resp_buf && out_buf
- && (midQ->midState == MID_RESPONSE_RECEIVED)) {
- out_buf->smb_buf_length = receive_len;
- memcpy((char *)out_buf + 4,
- (char *)midQ->resp_buf + 4,
- receive_len);
-
- dump_smb(out_buf, 92);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(out_buf,
+ goto out;
+ }
+
+ /* rcvd frame is ok */
+
+ if (midQ->resp_buf && out_buf
+ && (midQ->midState == MID_RESPONSE_RECEIVED)) {
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
&ses->server->mac_signing_key,
midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
+ }
- *pbytes_returned = out_buf->smb_buf_length;
+ *pbytes_returned = out_buf->smb_buf_length;
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * out_buf->WordCount) + 2 /* bcc */ )
- BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
- } else {
- rc = -EIO;
- cERROR(1, ("Bad MID state?"));
- }
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+ } else {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
}
out:
@@ -866,16 +873,16 @@ send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf,
header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0);
in_buf->Mid = mid;
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
(struct sockaddr *) &(ses->server->addr.sockAddr),
ses->server->noblocksnd);
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
@@ -933,6 +940,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
to the same server. We may make this configurable later or
use ses->maxReq */
+ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+ cERROR(1, ("Illegal length, greater than maximum frame, %d",
+ in_buf->smb_buf_length));
+ return -EIO;
+ }
+
rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
if (rc)
return rc;
@@ -941,24 +954,21 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
- down(&ses->server->tcpSem);
+ mutex_lock(&ses->server->srv_mutex);
rc = allocate_mid(ses, in_buf, &midQ);
if (rc) {
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
return rc;
}
- if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
- up(&ses->server->tcpSem);
- cERROR(1, ("Illegal length, greater than maximum frame, %d",
- in_buf->smb_buf_length));
+ rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
+ if (rc) {
DeleteMidQEntry(midQ);
- return -EIO;
+ mutex_unlock(&ses->server->srv_mutex);
+ return rc;
}
- rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
-
midQ->midState = MID_REQUEST_SUBMITTED;
#ifdef CONFIG_CIFS_STATS2
atomic_inc(&ses->server->inSend);
@@ -970,7 +980,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
atomic_dec(&ses->server->inSend);
midQ->when_sent = jiffies;
#endif
- up(&ses->server->tcpSem);
+ mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
DeleteMidQEntry(midQ);
@@ -1052,44 +1062,48 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
cERROR(1, ("Frame too large received. Length: %d Xid: %d",
receive_len, xid));
rc = -EIO;
- } else { /* rcvd frame is ok */
-
- if (midQ->resp_buf && out_buf
- && (midQ->midState == MID_RESPONSE_RECEIVED)) {
- out_buf->smb_buf_length = receive_len;
- memcpy((char *)out_buf + 4,
- (char *)midQ->resp_buf + 4,
- receive_len);
-
- dump_smb(out_buf, 92);
- /* convert the length into a more usable form */
- if ((receive_len > 24) &&
- (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
- SECMODE_SIGN_ENABLED))) {
- rc = cifs_verify_signature(out_buf,
- &ses->server->mac_signing_key,
- midQ->sequence_number+1);
- if (rc) {
- cERROR(1, ("Unexpected SMB signature"));
- /* BB FIXME add code to kill session */
- }
- }
+ goto out;
+ }
- *pbytes_returned = out_buf->smb_buf_length;
+ /* rcvd frame is ok */
- /* BB special case reconnect tid and uid here? */
- rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+ if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) {
+ rc = -EIO;
+ cERROR(1, ("Bad MID state?"));
+ goto out;
+ }
- /* convert ByteCount if necessary */
- if (receive_len >= sizeof(struct smb_hdr) - 4
- /* do not count RFC1001 header */ +
- (2 * out_buf->WordCount) + 2 /* bcc */ )
- BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
- } else {
- rc = -EIO;
- cERROR(1, ("Bad MID state?"));
+ out_buf->smb_buf_length = receive_len;
+ memcpy((char *)out_buf + 4,
+ (char *)midQ->resp_buf + 4,
+ receive_len);
+
+ dump_smb(out_buf, 92);
+ /* convert the length into a more usable form */
+ if ((receive_len > 24) &&
+ (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
+ SECMODE_SIGN_ENABLED))) {
+ rc = cifs_verify_signature(out_buf,
+ &ses->server->mac_signing_key,
+ midQ->sequence_number+1);
+ if (rc) {
+ cERROR(1, ("Unexpected SMB signature"));
+ /* BB FIXME add code to kill session */
}
}
+
+ *pbytes_returned = out_buf->smb_buf_length;
+
+ /* BB special case reconnect tid and uid here? */
+ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ );
+
+ /* convert ByteCount if necessary */
+ if (receive_len >= sizeof(struct smb_hdr) - 4
+ /* do not count RFC1001 header */ +
+ (2 * out_buf->WordCount) + 2 /* bcc */ )
+ BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
+
+out:
DeleteMidQEntry(midQ);
if (rstart && rc == -EACCES)
return -ERESTARTSYS;
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
index 8a2370341c7a..a5bf5771a22a 100644
--- a/fs/coda/cache.c
+++ b/fs/coda/cache.c
@@ -32,8 +32,8 @@ void coda_cache_enter(struct inode *inode, int mask)
struct coda_inode_info *cii = ITOC(inode);
cii->c_cached_epoch = atomic_read(&permission_epoch);
- if (cii->c_uid != current->fsuid) {
- cii->c_uid = current->fsuid;
+ if (cii->c_uid != current_fsuid()) {
+ cii->c_uid = current_fsuid();
cii->c_cached_perm = mask;
} else
cii->c_cached_perm |= mask;
@@ -60,7 +60,7 @@ int coda_cache_check(struct inode *inode, int mask)
int hit;
hit = (mask & cii->c_cached_perm) == mask &&
- cii->c_uid == current->fsuid &&
+ cii->c_uid == current_fsuid() &&
cii->c_cached_epoch == atomic_read(&permission_epoch);
return hit;
diff --git a/fs/coda/file.c b/fs/coda/file.c
index 29137ff3ca67..466303db2df6 100644
--- a/fs/coda/file.c
+++ b/fs/coda/file.c
@@ -13,6 +13,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/stat.h>
+#include <linux/cred.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
@@ -174,7 +175,7 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)
BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
- coda_flags, coda_file->f_uid);
+ coda_flags, coda_file->f_cred->fsuid);
host_inode = cfi->cfi_container->f_path.dentry->d_inode;
cii = ITOC(coda_inode);
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
index ce432bca95d1..c274d949179d 100644
--- a/fs/coda/upcall.c
+++ b/fs/coda/upcall.c
@@ -52,7 +52,7 @@ static void *alloc_upcall(int opcode, int size)
inp->ih.opcode = opcode;
inp->ih.pid = current->pid;
inp->ih.pgid = task_pgrp_nr(current);
- inp->ih.uid = current->fsuid;
+ inp->ih.uid = current_fsuid();
return (void*)inp;
}
diff --git a/fs/compat.c b/fs/compat.c
index e5f49f538502..d1ece79b6411 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1393,10 +1393,20 @@ int compat_do_execve(char * filename,
if (!bprm)
goto out_ret;
+ retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+ if (retval < 0)
+ goto out_free;
+
+ retval = -ENOMEM;
+ bprm->cred = prepare_exec_creds();
+ if (!bprm->cred)
+ goto out_unlock;
+ check_unsafe_exec(bprm);
+
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
- goto out_kfree;
+ goto out_unlock;
sched_exec();
@@ -1410,14 +1420,10 @@ int compat_do_execve(char * filename,
bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
- goto out_mm;
+ goto out;
bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
- goto out_mm;
-
- retval = security_bprm_alloc(bprm);
- if (retval)
goto out;
retval = prepare_binprm(bprm);
@@ -1438,19 +1444,16 @@ int compat_do_execve(char * filename,
goto out;
retval = search_binary_handler(bprm, regs);
- if (retval >= 0) {
- /* execve success */
- security_bprm_free(bprm);
- acct_update_integrals(current);
- free_bprm(bprm);
- return retval;
- }
+ if (retval < 0)
+ goto out;
-out:
- if (bprm->security)
- security_bprm_free(bprm);
+ /* execve succeeded */
+ mutex_unlock(&current->cred_exec_mutex);
+ acct_update_integrals(current);
+ free_bprm(bprm);
+ return retval;
-out_mm:
+out:
if (bprm->mm)
mmput(bprm->mm);
@@ -1460,7 +1463,10 @@ out_file:
fput(bprm->file);
}
-out_kfree:
+out_unlock:
+ mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
free_bprm(bprm);
out_ret:
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 4a714f6c1bed..5d61b7c06e13 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -222,8 +222,8 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
return -ENOMEM;
inode->i_ino = number+2;
- inode->i_uid = config.setuid ? config.uid : current->fsuid;
- inode->i_gid = config.setgid ? config.gid : current->fsgid;
+ inode->i_uid = config.setuid ? config.uid : current_fsuid();
+ inode->i_gid = config.setgid ? config.gid : current_fsgid();
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device);
inode->i_private = tty;
diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c
index 18bda83cc892..aa2a5775a027 100644
--- a/fs/dlm/netlink.c
+++ b/fs/dlm/netlink.c
@@ -127,8 +127,8 @@ static void fill_data(struct dlm_lock_data *data, struct dlm_lkb *lkb)
void dlm_timeout_warn(struct dlm_lkb *lkb)
{
+ struct sk_buff *uninitialized_var(send_skb);
struct dlm_lock_data *data;
- struct sk_buff *send_skb;
size_t size;
int rv;
diff --git a/fs/dquot.c b/fs/dquot.c
index 5e95261005b2..c237ccc8581c 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -874,7 +874,7 @@ static inline int need_print_warning(struct dquot *dquot)
switch (dquot->dq_type) {
case USRQUOTA:
- return current->fsuid == dquot->dq_id;
+ return current_fsuid() == dquot->dq_id;
case GRPQUOTA:
return in_group_p(dquot->dq_id);
}
@@ -981,7 +981,7 @@ static void send_warning(const struct dquot *dquot, const char warntype)
MINOR(dquot->dq_sb->s_dev));
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid);
+ ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
if (ret)
goto attr_err_out;
genlmsg_end(skb, msg_head);
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 3504cf9df358..a75026d35d16 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -691,7 +691,8 @@ int ecryptfs_init_kthread(void);
void ecryptfs_destroy_kthread(void);
int ecryptfs_privileged_open(struct file **lower_file,
struct dentry *lower_dentry,
- struct vfsmount *lower_mnt);
+ struct vfsmount *lower_mnt,
+ const struct cred *cred);
int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry);
#endif /* #ifndef ECRYPTFS_KERNEL_H */
diff --git a/fs/ecryptfs/kthread.c b/fs/ecryptfs/kthread.c
index c440c6b58b2d..c6d7a4d748a0 100644
--- a/fs/ecryptfs/kthread.c
+++ b/fs/ecryptfs/kthread.c
@@ -73,7 +73,7 @@ static int ecryptfs_threadfn(void *ignored)
mntget(req->lower_mnt);
(*req->lower_file) = dentry_open(
req->lower_dentry, req->lower_mnt,
- (O_RDWR | O_LARGEFILE));
+ (O_RDWR | O_LARGEFILE), current_cred());
req->flags |= ECRYPTFS_REQ_PROCESSED;
}
wake_up(&req->wait);
@@ -132,7 +132,8 @@ void ecryptfs_destroy_kthread(void)
*/
int ecryptfs_privileged_open(struct file **lower_file,
struct dentry *lower_dentry,
- struct vfsmount *lower_mnt)
+ struct vfsmount *lower_mnt,
+ const struct cred *cred)
{
struct ecryptfs_open_req *req;
int rc = 0;
@@ -143,7 +144,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
dget(lower_dentry);
mntget(lower_mnt);
(*lower_file) = dentry_open(lower_dentry, lower_mnt,
- (O_RDWR | O_LARGEFILE));
+ (O_RDWR | O_LARGEFILE), cred);
if (!IS_ERR(*lower_file))
goto out;
req = kmem_cache_alloc(ecryptfs_open_req_cache, GFP_KERNEL);
@@ -184,7 +185,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
dget(lower_dentry);
mntget(lower_mnt);
(*lower_file) = dentry_open(lower_dentry, lower_mnt,
- (O_RDONLY | O_LARGEFILE));
+ (O_RDONLY | O_LARGEFILE), cred);
if (IS_ERR(*lower_file)) {
rc = PTR_ERR(*req->lower_file);
(*lower_file) = NULL;
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 64d2ba980df4..fd630713c5c7 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -115,6 +115,7 @@ void __ecryptfs_printk(const char *fmt, ...)
*/
int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
{
+ const struct cred *cred = current_cred();
struct ecryptfs_inode_info *inode_info =
ecryptfs_inode_to_private(ecryptfs_dentry->d_inode);
int rc = 0;
@@ -127,7 +128,7 @@ int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
rc = ecryptfs_privileged_open(&inode_info->lower_file,
- lower_dentry, lower_mnt);
+ lower_dentry, lower_mnt, cred);
if (rc || IS_ERR(inode_info->lower_file)) {
printk(KERN_ERR "Error opening lower persistent file "
"for lower_dentry [0x%p] and lower_mnt [0x%p]; "
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index c6983978a31e..6913f727624d 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -360,7 +360,8 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
struct ecryptfs_msg_ctx *msg_ctx;
size_t msg_size;
struct nsproxy *nsproxy;
- struct user_namespace *current_user_ns;
+ struct user_namespace *tsk_user_ns;
+ uid_t ctx_euid;
int rc;
if (msg->index >= ecryptfs_message_buf_len) {
@@ -384,9 +385,9 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
mutex_unlock(&ecryptfs_daemon_hash_mux);
goto wake_up;
}
- current_user_ns = nsproxy->user_ns;
- rc = ecryptfs_find_daemon_by_euid(&daemon, msg_ctx->task->euid,
- current_user_ns);
+ tsk_user_ns = __task_cred(msg_ctx->task)->user->user_ns;
+ ctx_euid = task_euid(msg_ctx->task);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, ctx_euid, tsk_user_ns);
rcu_read_unlock();
mutex_unlock(&ecryptfs_daemon_hash_mux);
if (rc) {
@@ -394,28 +395,28 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
printk(KERN_WARNING "%s: User [%d] received a "
"message response from process [0x%p] but does "
"not have a registered daemon\n", __func__,
- msg_ctx->task->euid, pid);
+ ctx_euid, pid);
goto wake_up;
}
- if (msg_ctx->task->euid != euid) {
+ if (ctx_euid != euid) {
rc = -EBADMSG;
printk(KERN_WARNING "%s: Received message from user "
"[%d]; expected message from user [%d]\n", __func__,
- euid, msg_ctx->task->euid);
+ euid, ctx_euid);
goto unlock;
}
- if (current_user_ns != user_ns) {
+ if (tsk_user_ns != user_ns) {
rc = -EBADMSG;
printk(KERN_WARNING "%s: Received message from user_ns "
"[0x%p]; expected message from user_ns [0x%p]\n",
- __func__, user_ns, nsproxy->user_ns);
+ __func__, user_ns, tsk_user_ns);
goto unlock;
}
if (daemon->pid != pid) {
rc = -EBADMSG;
printk(KERN_ERR "%s: User [%d] sent a message response "
"from an unrecognized process [0x%p]\n",
- __func__, msg_ctx->task->euid, pid);
+ __func__, ctx_euid, pid);
goto unlock;
}
if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
@@ -464,14 +465,14 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
struct ecryptfs_msg_ctx **msg_ctx)
{
struct ecryptfs_daemon *daemon;
+ uid_t euid = current_euid();
int rc;
- rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
- current->nsproxy->user_ns);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
if (rc || !daemon) {
rc = -ENOTCONN;
printk(KERN_ERR "%s: User [%d] does not have a daemon "
- "registered\n", __func__, current->euid);
+ "registered\n", __func__, euid);
goto out;
}
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c
index b484792a0996..efd95a0ed1ea 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -42,12 +42,12 @@ ecryptfs_miscdev_poll(struct file *file, poll_table *pt)
{
struct ecryptfs_daemon *daemon;
unsigned int mask = 0;
+ uid_t euid = current_euid();
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
/* TODO: Just use file->private_data? */
- rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
- current->nsproxy->user_ns);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
BUG_ON(rc || !daemon);
mutex_lock(&daemon->mux);
mutex_unlock(&ecryptfs_daemon_hash_mux);
@@ -83,6 +83,7 @@ static int
ecryptfs_miscdev_open(struct inode *inode, struct file *file)
{
struct ecryptfs_daemon *daemon = NULL;
+ uid_t euid = current_euid();
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
@@ -93,11 +94,9 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
"count; rc = [%d]\n", __func__, rc);
goto out_unlock_daemon_list;
}
- rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
- current->nsproxy->user_ns);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
if (rc || !daemon) {
- rc = ecryptfs_spawn_daemon(&daemon, current->euid,
- current->nsproxy->user_ns,
+ rc = ecryptfs_spawn_daemon(&daemon, euid, current_user_ns(),
task_pid(current));
if (rc) {
printk(KERN_ERR "%s: Error attempting to spawn daemon; "
@@ -147,11 +146,11 @@ static int
ecryptfs_miscdev_release(struct inode *inode, struct file *file)
{
struct ecryptfs_daemon *daemon = NULL;
+ uid_t euid = current_euid();
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
- rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
- current->nsproxy->user_ns);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
BUG_ON(rc || !daemon);
mutex_lock(&daemon->mux);
BUG_ON(daemon->pid != task_pid(current));
@@ -246,12 +245,12 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
char packet_length[3];
size_t i;
size_t total_length;
+ uid_t euid = current_euid();
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
/* TODO: Just use file->private_data? */
- rc = ecryptfs_find_daemon_by_euid(&daemon, current->euid,
- current->nsproxy->user_ns);
+ rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
BUG_ON(rc || !daemon);
mutex_lock(&daemon->mux);
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
@@ -290,8 +289,8 @@ check_list:
* message from the queue; try again */
goto check_list;
}
- BUG_ON(current->euid != daemon->euid);
- BUG_ON(current->nsproxy->user_ns != daemon->user_ns);
+ BUG_ON(euid != daemon->euid);
+ BUG_ON(current_user_ns() != daemon->user_ns);
BUG_ON(task_pid(current) != daemon->pid);
msg_ctx = list_first_entry(&daemon->msg_ctx_out_queue,
struct ecryptfs_msg_ctx, daemon_out_list);
@@ -414,6 +413,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
size_t packet_size, packet_size_length, i;
ssize_t sz = 0;
char *data;
+ uid_t euid = current_euid();
int rc;
if (count == 0)
@@ -463,8 +463,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
goto out_free;
}
rc = ecryptfs_miscdev_response(&data[i], packet_size,
- current->euid,
- current->nsproxy->user_ns,
+ euid, current_user_ns(),
task_pid(current), seq);
if (rc)
printk(KERN_WARNING "%s: Failed to deliver miscdev "
diff --git a/fs/exec.c b/fs/exec.c
index ec5df9a38313..02d2e120542d 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -55,6 +55,7 @@
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/tlb.h>
+#include "internal.h"
#ifdef __alpha__
/* for /sbin/loader handling in search_binary_handler() */
@@ -772,7 +773,6 @@ static int de_thread(struct task_struct *tsk)
struct signal_struct *sig = tsk->signal;
struct sighand_struct *oldsighand = tsk->sighand;
spinlock_t *lock = &oldsighand->siglock;
- struct task_struct *leader = NULL;
int count;
if (thread_group_empty(tsk))
@@ -810,7 +810,7 @@ static int de_thread(struct task_struct *tsk)
* and to assume its PID:
*/
if (!thread_group_leader(tsk)) {
- leader = tsk->group_leader;
+ struct task_struct *leader = tsk->group_leader;
sig->notify_count = -1; /* for exit_notify() */
for (;;) {
@@ -862,8 +862,9 @@ static int de_thread(struct task_struct *tsk)
BUG_ON(leader->exit_state != EXIT_ZOMBIE);
leader->exit_state = EXIT_DEAD;
-
write_unlock_irq(&tasklist_lock);
+
+ release_task(leader);
}
sig->group_exit_task = NULL;
@@ -872,8 +873,6 @@ static int de_thread(struct task_struct *tsk)
no_thread_group:
exit_itimers(sig);
flush_itimer_signals();
- if (leader)
- release_task(leader);
if (atomic_read(&oldsighand->count) != 1) {
struct sighand_struct *newsighand;
@@ -980,7 +979,7 @@ int flush_old_exec(struct linux_binprm * bprm)
/* This is the point of no return */
current->sas_ss_sp = current->sas_ss_size = 0;
- if (current->euid == current->uid && current->egid == current->gid)
+ if (current_euid() == current_uid() && current_egid() == current_gid())
set_dumpable(current->mm, 1);
else
set_dumpable(current->mm, suid_dumpable);
@@ -1007,16 +1006,17 @@ int flush_old_exec(struct linux_binprm * bprm)
*/
current->mm->task_size = TASK_SIZE;
- if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
- suid_keys(current);
- set_dumpable(current->mm, suid_dumpable);
+ /* install the new credentials */
+ if (bprm->cred->uid != current_euid() ||
+ bprm->cred->gid != current_egid()) {
current->pdeath_signal = 0;
} else if (file_permission(bprm->file, MAY_READ) ||
- (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
- suid_keys(current);
+ bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
set_dumpable(current->mm, suid_dumpable);
}
+ current->personality &= ~bprm->per_clear;
+
/* An exec changes our domain. We are no longer part of the thread
group */
@@ -1033,13 +1033,50 @@ out:
EXPORT_SYMBOL(flush_old_exec);
+/*
+ * install the new credentials for this executable
+ */
+void install_exec_creds(struct linux_binprm *bprm)
+{
+ security_bprm_committing_creds(bprm);
+
+ commit_creds(bprm->cred);
+ bprm->cred = NULL;
+
+ /* cred_exec_mutex must be held at least to this point to prevent
+ * ptrace_attach() from altering our determination of the task's
+ * credentials; any time after this it may be unlocked */
+
+ security_bprm_committed_creds(bprm);
+}
+EXPORT_SYMBOL(install_exec_creds);
+
+/*
+ * determine how safe it is to execute the proposed program
+ * - the caller must hold current->cred_exec_mutex to protect against
+ * PTRACE_ATTACH
+ */
+void check_unsafe_exec(struct linux_binprm *bprm)
+{
+ struct task_struct *p = current;
+
+ bprm->unsafe = tracehook_unsafe_exec(p);
+
+ if (atomic_read(&p->fs->count) > 1 ||
+ atomic_read(&p->files->count) > 1 ||
+ atomic_read(&p->sighand->count) > 1)
+ bprm->unsafe |= LSM_UNSAFE_SHARE;
+}
+
/*
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ *
+ * This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
{
- int mode;
+ umode_t mode;
struct inode * inode = bprm->file->f_path.dentry->d_inode;
int retval;
@@ -1047,14 +1084,15 @@ int prepare_binprm(struct linux_binprm *bprm)
if (bprm->file->f_op == NULL)
return -EACCES;
- bprm->e_uid = current->euid;
- bprm->e_gid = current->egid;
+ /* clear any previous set[ug]id data from a previous binary */
+ bprm->cred->euid = current_euid();
+ bprm->cred->egid = current_egid();
- if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
+ if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
/* Set-uid? */
if (mode & S_ISUID) {
- current->personality &= ~PER_CLEAR_ON_SETID;
- bprm->e_uid = inode->i_uid;
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->euid = inode->i_uid;
}
/* Set-gid? */
@@ -1064,52 +1102,23 @@ int prepare_binprm(struct linux_binprm *bprm)
* executable.
*/
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
- current->personality &= ~PER_CLEAR_ON_SETID;
- bprm->e_gid = inode->i_gid;
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->egid = inode->i_gid;
}
}
/* fill in binprm security blob */
- retval = security_bprm_set(bprm);
+ retval = security_bprm_set_creds(bprm);
if (retval)
return retval;
+ bprm->cred_prepared = 1;
- memset(bprm->buf,0,BINPRM_BUF_SIZE);
- return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+ memset(bprm->buf, 0, BINPRM_BUF_SIZE);
+ return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}
EXPORT_SYMBOL(prepare_binprm);
-static int unsafe_exec(struct task_struct *p)
-{
- int unsafe = tracehook_unsafe_exec(p);
-
- if (atomic_read(&p->fs->count) > 1 ||
- atomic_read(&p->files->count) > 1 ||
- atomic_read(&p->sighand->count) > 1)
- unsafe |= LSM_UNSAFE_SHARE;
-
- return unsafe;
-}
-
-void compute_creds(struct linux_binprm *bprm)
-{
- int unsafe;
-
- if (bprm->e_uid != current->uid) {
- suid_keys(current);
- current->pdeath_signal = 0;
- }
- exec_keys(current);
-
- task_lock(current);
- unsafe = unsafe_exec(current);
- security_bprm_apply_creds(bprm, unsafe);
- task_unlock(current);
- security_bprm_post_apply_creds(bprm);
-}
-EXPORT_SYMBOL(compute_creds);
-
/*
* Arguments are '\0' separated strings found at the location bprm->p
* points to; chop off the first by relocating brpm->p to right after
@@ -1270,6 +1279,8 @@ EXPORT_SYMBOL(search_binary_handler);
void free_bprm(struct linux_binprm *bprm)
{
free_arg_pages(bprm);
+ if (bprm->cred)
+ abort_creds(bprm->cred);
kfree(bprm);
}
@@ -1295,10 +1306,20 @@ int do_execve(char * filename,
if (!bprm)
goto out_files;
+ retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+ if (retval < 0)
+ goto out_free;
+
+ retval = -ENOMEM;
+ bprm->cred = prepare_exec_creds();
+ if (!bprm->cred)
+ goto out_unlock;
+ check_unsafe_exec(bprm);
+
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
- goto out_kfree;
+ goto out_unlock;
sched_exec();
@@ -1312,14 +1333,10 @@ int do_execve(char * filename,
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
- goto out_mm;
+ goto out;
bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
- goto out_mm;
-
- retval = security_bprm_alloc(bprm);
- if (retval)
goto out;
retval = prepare_binprm(bprm);
@@ -1341,21 +1358,18 @@ int do_execve(char * filename,
current->flags &= ~PF_KTHREAD;
retval = search_binary_handler(bprm,regs);
- if (retval >= 0) {
- /* execve success */
- security_bprm_free(bprm);
- acct_update_integrals(current);
- free_bprm(bprm);
- if (displaced)
- put_files_struct(displaced);
- return retval;
- }
+ if (retval < 0)
+ goto out;
-out:
- if (bprm->security)
- security_bprm_free(bprm);
+ /* execve succeeded */
+ mutex_unlock(&current->cred_exec_mutex);
+ acct_update_integrals(current);
+ free_bprm(bprm);
+ if (displaced)
+ put_files_struct(displaced);
+ return retval;
-out_mm:
+out:
if (bprm->mm)
mmput (bprm->mm);
@@ -1364,7 +1378,11 @@ out_file:
allow_write_access(bprm->file);
fput(bprm->file);
}
-out_kfree:
+
+out_unlock:
+ mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
free_bprm(bprm);
out_files:
@@ -1396,6 +1414,7 @@ EXPORT_SYMBOL(set_binfmt);
*/
static int format_corename(char *corename, long signr)
{
+ const struct cred *cred = current_cred();
const char *pat_ptr = core_pattern;
int ispipe = (*pat_ptr == '|');
char *out_ptr = corename;
@@ -1432,7 +1451,7 @@ static int format_corename(char *corename, long signr)
/* uid */
case 'u':
rc = snprintf(out_ptr, out_end - out_ptr,
- "%d", current->uid);
+ "%d", cred->uid);
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
@@ -1440,7 +1459,7 @@ static int format_corename(char *corename, long signr)
/* gid */
case 'g':
rc = snprintf(out_ptr, out_end - out_ptr,
- "%d", current->gid);
+ "%d", cred->gid);
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
@@ -1716,8 +1735,9 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
struct linux_binfmt * binfmt;
struct inode * inode;
struct file * file;
+ const struct cred *old_cred;
+ struct cred *cred;
int retval = 0;
- int fsuid = current->fsuid;
int flag = 0;
int ispipe = 0;
unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
@@ -1730,12 +1750,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
binfmt = current->binfmt;
if (!binfmt || !binfmt->core_dump)
goto fail;
+
+ cred = prepare_creds();
+ if (!cred) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+
down_write(&mm->mmap_sem);
/*
* If another thread got here first, or we are not dumpable, bail out.
*/
if (mm->core_state || !get_dumpable(mm)) {
up_write(&mm->mmap_sem);
+ put_cred(cred);
goto fail;
}
@@ -1746,12 +1774,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
*/
if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
- current->fsuid = 0; /* Dump root private */
+ cred->fsuid = 0; /* Dump root private */
}
retval = coredump_wait(exit_code, &core_state);
- if (retval < 0)
+ if (retval < 0) {
+ put_cred(cred);
goto fail;
+ }
+
+ old_cred = override_creds(cred);
/*
* Clear any false indication of pending signals that might
@@ -1823,7 +1855,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
* Dont allow local users get cute and trick others to coredump
* into their pre-created files:
*/
- if (inode->i_uid != current->fsuid)
+ if (inode->i_uid != current_fsuid())
goto close_fail;
if (!file->f_op)
goto close_fail;
@@ -1842,7 +1874,8 @@ fail_unlock:
if (helper_argv)
argv_free(helper_argv);
- current->fsuid = fsuid;
+ revert_creds(old_cred);
+ put_cred(cred);
coredump_finish(mm);
fail:
return retval;
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
index 890e01828817..197c7db583c7 100644
--- a/fs/exportfs/expfs.c
+++ b/fs/exportfs/expfs.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/namei.h>
+#include <linux/sched.h>
#define dprintk(fmt, args...) do{}while(0)
@@ -249,6 +250,7 @@ static int filldir_one(void * __buf, const char * name, int len,
static int get_name(struct vfsmount *mnt, struct dentry *dentry,
char *name, struct dentry *child)
{
+ const struct cred *cred = current_cred();
struct inode *dir = dentry->d_inode;
int error;
struct file *file;
@@ -263,7 +265,7 @@ static int get_name(struct vfsmount *mnt, struct dentry *dentry,
/*
* Open the directory ...
*/
- file = dentry_open(dget(dentry), mntget(mnt), O_RDONLY);
+ file = dentry_open(dget(dentry), mntget(mnt), O_RDONLY, cred);
error = PTR_ERR(file);
if (IS_ERR(file))
goto out;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 6dac7ba2d22d..4a29d6376081 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -1193,7 +1193,7 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi)
free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
- sbi->s_resuid != current->fsuid &&
+ sbi->s_resuid != current_fsuid() &&
(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
return 0;
}
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index f59741346760..8d0add625870 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -550,7 +550,7 @@ got:
sb->s_dirt = 1;
mark_buffer_dirty(bh2);
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (test_opt (sb, GRPID))
inode->i_gid = dir->i_gid;
else if (dir->i_mode & S_ISGID) {
@@ -558,7 +558,7 @@ got:
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
inode->i_mode = mode;
inode->i_ino = ino;
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index f5b57a2ca35a..0dbf1c048475 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -1422,7 +1422,7 @@ static int ext3_has_free_blocks(struct ext3_sb_info *sbi)
free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
- sbi->s_resuid != current->fsuid &&
+ sbi->s_resuid != current_fsuid() &&
(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
return 0;
}
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c
index 47b678d73e7a..490bd0ed7896 100644
--- a/fs/ext3/ialloc.c
+++ b/fs/ext3/ialloc.c
@@ -539,7 +539,7 @@ got:
percpu_counter_inc(&sbi->s_dirs_counter);
sb->s_dirt = 1;
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (test_opt (sb, GRPID))
inode->i_gid = dir->i_gid;
else if (dir->i_mode & S_ISGID) {
@@ -547,7 +547,7 @@ got:
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
inode->i_mode = mode;
inode->i_ino = ino;
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index db35cfdb3c8b..38b3acf5683b 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -624,7 +624,7 @@ int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks)
return 1;
/* Hm, nope. Are (enough) root reserved blocks available? */
- if (sbi->s_resuid == current->fsuid ||
+ if (sbi->s_resuid == current_fsuid() ||
((sbi->s_resgid != 0) && in_group_p(sbi->s_resgid)) ||
capable(CAP_SYS_RESOURCE)) {
if (free_blocks >= (nblocks + dirty_blocks))
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 2a117e286e54..08cac9fcace2 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -787,7 +787,7 @@ got:
spin_unlock(sb_bgl_lock(sbi, flex_group));
}
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (test_opt(sb, GRPID))
inode->i_gid = dir->i_gid;
else if (dir->i_mode & S_ISGID) {
@@ -795,7 +795,7 @@ got:
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
inode->i_mode = mode;
inode->i_ino = ino + group * EXT4_INODES_PER_GROUP(sb);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e4a241c65dbe..04158ad74dbb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1721,7 +1721,7 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
/* small i_blocks in vfs inode? */
if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) {
/*
- * CONFIG_LSF is not enabled implies the inode
+ * CONFIG_LBD is not enabled implies the inode
* i_block represent total blocks in 512 bytes
* 32 == size of vfs inode i_blocks * 8
*/
@@ -1764,7 +1764,7 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) {
/*
- * !has_huge_files or CONFIG_LSF is not enabled
+ * !has_huge_files or CONFIG_LBD is not enabled
* implies the inode i_block represent total blocks in
* 512 bytes 32 == size of vfs inode i_blocks * 8
*/
@@ -2021,13 +2021,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (has_huge_files) {
/*
* Large file size enabled file system can only be
- * mount if kernel is build with CONFIG_LSF
+ * mount if kernel is build with CONFIG_LBD
*/
if (sizeof(root->i_blocks) < sizeof(u64) &&
!(sb->s_flags & MS_RDONLY)) {
printk(KERN_ERR "EXT4-fs: %s: Filesystem with huge "
"files cannot be mounted read-write "
- "without CONFIG_LSF.\n", sb->s_id);
+ "without CONFIG_LBD.\n", sb->s_id);
goto failed_mount;
}
}
diff --git a/fs/fat/file.c b/fs/fat/file.c
index f06a4e525ece..0a7f4a9918b3 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -304,7 +304,7 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
{
mode_t allow_utime = sbi->options.allow_utime;
- if (current->fsuid != inode->i_uid) {
+ if (current_fsuid() != inode->i_uid) {
if (in_group_p(inode->i_gid))
allow_utime >>= 3;
if (allow_utime & MAY_WRITE)
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index bdd8fb7be2ca..d937aaf77374 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -926,8 +926,8 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
opts->isvfat = is_vfat;
- opts->fs_uid = current->uid;
- opts->fs_gid = current->gid;
+ opts->fs_uid = current_uid();
+ opts->fs_gid = current_gid();
opts->fs_fmask = opts->fs_dmask = current->fs->umask;
opts->allow_utime = -1;
opts->codepage = fat_default_codepage;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 549daf8005fb..cdc141946724 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -212,13 +212,14 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
int force)
{
+ const struct cred *cred = current_cred();
int err;
err = security_file_set_fowner(filp);
if (err)
return err;
- f_modown(filp, pid, type, current->uid, current->euid, force);
+ f_modown(filp, pid, type, cred->uid, cred->euid, force);
return 0;
}
EXPORT_SYMBOL(__f_setown);
@@ -407,10 +408,17 @@ static const long band_table[NSIGPOLL] = {
static inline int sigio_perm(struct task_struct *p,
struct fown_struct *fown, int sig)
{
- return (((fown->euid == 0) ||
- (fown->euid == p->suid) || (fown->euid == p->uid) ||
- (fown->uid == p->suid) || (fown->uid == p->uid)) &&
- !security_file_send_sigiotask(p, fown, sig));
+ const struct cred *cred;
+ int ret;
+
+ rcu_read_lock();
+ cred = __task_cred(p);
+ ret = ((fown->euid == 0 ||
+ fown->euid == cred->suid || fown->euid == cred->uid ||
+ fown->uid == cred->suid || fown->uid == cred->uid) &&
+ !security_file_send_sigiotask(p, fown, sig));
+ rcu_read_unlock();
+ return ret;
}
static void send_sigio_to_task(struct task_struct *p,
diff --git a/fs/file_table.c b/fs/file_table.c
index 5ad0eca6eea2..0fbcacc3ea75 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -36,7 +36,9 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp;
static inline void file_free_rcu(struct rcu_head *head)
{
- struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
+ struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
+
+ put_cred(f->f_cred);
kmem_cache_free(filp_cachep, f);
}
@@ -94,7 +96,7 @@ int proc_nr_files(ctl_table *table, int write, struct file *filp,
*/
struct file *get_empty_filp(void)
{
- struct task_struct *tsk;
+ const struct cred *cred = current_cred();
static int old_max;
struct file * f;
@@ -118,12 +120,10 @@ struct file *get_empty_filp(void)
if (security_file_alloc(f))
goto fail_sec;
- tsk = current;
INIT_LIST_HEAD(&f->f_u.fu_list);
atomic_long_set(&f->f_count, 1);
rwlock_init(&f->f_owner.lock);
- f->f_uid = tsk->fsuid;
- f->f_gid = tsk->fsgid;
+ f->f_cred = get_cred(cred);
eventpoll_init_file(f);
/* f->f_version: 0 */
return f;
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b72361479be2..fba571648a8e 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -87,8 +87,8 @@ static void __fuse_put_request(struct fuse_req *req)
static void fuse_req_init_context(struct fuse_req *req)
{
- req->in.h.uid = current->fsuid;
- req->in.h.gid = current->fsgid;
+ req->in.h.uid = current_fsuid();
+ req->in.h.gid = current_fsgid();
req->in.h.pid = current->pid;
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index fd03330cadeb..95bc22bdd060 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -869,18 +869,25 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
*/
int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
{
+ const struct cred *cred;
+ int ret;
+
if (fc->flags & FUSE_ALLOW_OTHER)
return 1;
- if (task->euid == fc->user_id &&
- task->suid == fc->user_id &&
- task->uid == fc->user_id &&
- task->egid == fc->group_id &&
- task->sgid == fc->group_id &&
- task->gid == fc->group_id)
- return 1;
+ rcu_read_lock();
+ ret = 0;
+ cred = __task_cred(task);
+ if (cred->euid == fc->user_id &&
+ cred->suid == fc->user_id &&
+ cred->uid == fc->user_id &&
+ cred->egid == fc->group_id &&
+ cred->sgid == fc->group_id &&
+ cred->gid == fc->group_id)
+ ret = 1;
+ rcu_read_unlock();
- return 0;
+ return ret;
}
static int fuse_access(struct inode *inode, int mask)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 7cee695fa441..d57616840e89 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -705,18 +705,18 @@ static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode,
(dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) {
if (S_ISDIR(*mode))
*mode |= S_ISUID;
- else if (dip->i_inode.i_uid != current->fsuid)
+ else if (dip->i_inode.i_uid != current_fsuid())
*mode &= ~07111;
*uid = dip->i_inode.i_uid;
} else
- *uid = current->fsuid;
+ *uid = current_fsuid();
if (dip->i_inode.i_mode & S_ISGID) {
if (S_ISDIR(*mode))
*mode |= S_ISGID;
*gid = dip->i_inode.i_gid;
} else
- *gid = current->fsgid;
+ *gid = current_fsgid();
}
static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation)
@@ -1124,8 +1124,8 @@ int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
return -EPERM;
if ((dip->i_inode.i_mode & S_ISVTX) &&
- dip->i_inode.i_uid != current->fsuid &&
- ip->i_inode.i_uid != current->fsuid && !capable(CAP_FOWNER))
+ dip->i_inode.i_uid != current_fsuid() &&
+ ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER))
return -EPERM;
if (IS_APPEND(&dip->i_inode))
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index c69b7ac75bf7..9435dda8f1e0 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -155,8 +155,8 @@ struct inode *hfs_new_inode(struct inode *dir, struct qstr *name, int mode)
hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name);
inode->i_ino = HFS_SB(sb)->next_id++;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
HFS_I(inode)->flags = 0;
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 3c7c7637719c..c8b5acf4b0b7 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -210,8 +210,8 @@ static int parse_options(char *options, struct hfs_sb_info *hsb)
int tmp, token;
/* initialize the sb with defaults */
- hsb->s_uid = current->uid;
- hsb->s_gid = current->gid;
+ hsb->s_uid = current_uid();
+ hsb->s_gid = current_gid();
hsb->s_file_umask = 0133;
hsb->s_dir_umask = 0022;
hsb->s_type = hsb->s_creator = cpu_to_be32(0x3f3f3f3f); /* == '????' */
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index b207f0e6fc22..f105ee9e1cc4 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -296,8 +296,8 @@ struct inode *hfsplus_new_inode(struct super_block *sb, int mode)
inode->i_ino = HFSPLUS_SB(sb).next_cnid++;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c
index 9699c56d323f..bab7f8d1bdfa 100644
--- a/fs/hfsplus/options.c
+++ b/fs/hfsplus/options.c
@@ -49,8 +49,8 @@ void hfsplus_fill_defaults(struct hfsplus_sb_info *opts)
opts->creator = HFSPLUS_DEF_CR_TYPE;
opts->type = HFSPLUS_DEF_CR_TYPE;
opts->umask = current->fs->umask;
- opts->uid = current->uid;
- opts->gid = current->gid;
+ opts->uid = current_uid();
+ opts->gid = current_gid();
opts->part = -1;
opts->session = -1;
}
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index 10783f3d265a..b649232dde97 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -92,11 +92,11 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
inc_nlink(dir);
insert_inode_hash(result);
- if (result->i_uid != current->fsuid ||
- result->i_gid != current->fsgid ||
+ if (result->i_uid != current_fsuid() ||
+ result->i_gid != current_fsgid() ||
result->i_mode != (mode | S_IFDIR)) {
- result->i_uid = current->fsuid;
- result->i_gid = current->fsgid;
+ result->i_uid = current_fsuid();
+ result->i_gid = current_fsgid();
result->i_mode = mode | S_IFDIR;
hpfs_write_inode_nolock(result);
}
@@ -184,11 +184,11 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struc
insert_inode_hash(result);
- if (result->i_uid != current->fsuid ||
- result->i_gid != current->fsgid ||
+ if (result->i_uid != current_fsuid() ||
+ result->i_gid != current_fsgid() ||
result->i_mode != (mode | S_IFREG)) {
- result->i_uid = current->fsuid;
- result->i_gid = current->fsgid;
+ result->i_uid = current_fsuid();
+ result->i_gid = current_fsgid();
result->i_mode = mode | S_IFREG;
hpfs_write_inode_nolock(result);
}
@@ -247,8 +247,8 @@ static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
- result->i_uid = current->fsuid;
- result->i_gid = current->fsgid;
+ result->i_uid = current_fsuid();
+ result->i_gid = current_fsgid();
result->i_nlink = 1;
result->i_size = 0;
result->i_blocks = 1;
@@ -325,8 +325,8 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
result->i_mode = S_IFLNK | 0777;
- result->i_uid = current->fsuid;
- result->i_gid = current->fsgid;
+ result->i_uid = current_fsuid();
+ result->i_gid = current_fsgid();
result->i_blocks = 1;
result->i_nlink = 1;
result->i_size = strlen(symlink);
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index 29ad461d568f..0d049b8919c4 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -475,8 +475,8 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
init_MUTEX(&sbi->hpfs_creation_de);
- uid = current->uid;
- gid = current->gid;
+ uid = current_uid();
+ gid = current_gid();
umask = current->fs->umask;
lowercase = 0;
conv = CONV_BINARY;
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index 2b3d1828db99..b278f7f52024 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -426,6 +426,7 @@ static int file_mode(int fmode)
static int hppfs_open(struct inode *inode, struct file *file)
{
+ const struct cred *cred = file->f_cred;
struct hppfs_private *data;
struct vfsmount *proc_mnt;
struct dentry *proc_dentry;
@@ -446,7 +447,7 @@ static int hppfs_open(struct inode *inode, struct file *file)
/* XXX This isn't closed anywhere */
data->proc_file = dentry_open(dget(proc_dentry), mntget(proc_mnt),
- file_mode(file->f_mode));
+ file_mode(file->f_mode), cred);
err = PTR_ERR(data->proc_file);
if (IS_ERR(data->proc_file))
goto out_free1;
@@ -489,6 +490,7 @@ static int hppfs_open(struct inode *inode, struct file *file)
static int hppfs_dir_open(struct inode *inode, struct file *file)
{
+ const struct cred *cred = file->f_cred;
struct hppfs_private *data;
struct vfsmount *proc_mnt;
struct dentry *proc_dentry;
@@ -502,7 +504,7 @@ static int hppfs_dir_open(struct inode *inode, struct file *file)
proc_dentry = HPPFS_I(inode)->proc_dentry;
proc_mnt = inode->i_sb->s_fs_info;
data->proc_file = dentry_open(dget(proc_dentry), mntget(proc_mnt),
- file_mode(file->f_mode));
+ file_mode(file->f_mode), cred);
err = PTR_ERR(data->proc_file);
if (IS_ERR(data->proc_file))
goto out_free;
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 61edc701b0e6..7d479ce3aceb 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -551,9 +551,9 @@ static int hugetlbfs_mknod(struct inode *dir,
if (S_ISDIR(mode))
mode |= S_ISGID;
} else {
- gid = current->fsgid;
+ gid = current_fsgid();
}
- inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid, gid, mode, dev);
+ inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(), gid, mode, dev);
if (inode) {
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
d_instantiate(dentry, inode);
@@ -586,9 +586,9 @@ static int hugetlbfs_symlink(struct inode *dir,
if (dir->i_mode & S_ISGID)
gid = dir->i_gid;
else
- gid = current->fsgid;
+ gid = current_fsgid();
- inode = hugetlbfs_get_inode(dir->i_sb, current->fsuid,
+ inode = hugetlbfs_get_inode(dir->i_sb, current_fsuid(),
gid, S_IFLNK|S_IRWXUGO, 0);
if (inode) {
int l = strlen(symname)+1;
@@ -854,8 +854,8 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent)
config.nr_blocks = -1; /* No limit on size by default */
config.nr_inodes = -1; /* No limit on number of inodes by default */
- config.uid = current->fsuid;
- config.gid = current->fsgid;
+ config.uid = current_fsuid();
+ config.gid = current_fsgid();
config.mode = 0755;
config.hstate = &default_hstate;
ret = hugetlbfs_parse_options(data, &config);
@@ -951,6 +951,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
struct inode *inode;
struct dentry *dentry, *root;
struct qstr quick_string;
+ struct user_struct *user = current_user();
if (!hugetlbfs_vfsmount)
return ERR_PTR(-ENOENT);
@@ -958,7 +959,7 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
if (!can_do_hugetlb_shm())
return ERR_PTR(-EPERM);
- if (!user_shm_lock(size, current->user))
+ if (!user_shm_lock(size, user))
return ERR_PTR(-ENOMEM);
root = hugetlbfs_vfsmount->mnt_root;
@@ -970,8 +971,8 @@ struct file *hugetlb_file_setup(const char *name, size_t size)
goto out_shm_unlock;
error = -ENOSPC;
- inode = hugetlbfs_get_inode(root->d_sb, current->fsuid,
- current->fsgid, S_IFREG | S_IRWXUGO, 0);
+ inode = hugetlbfs_get_inode(root->d_sb, current_fsuid(),
+ current_fsgid(), S_IFREG | S_IRWXUGO, 0);
if (!inode)
goto out_dentry;
@@ -998,7 +999,7 @@ out_inode:
out_dentry:
dput(dentry);
out_shm_unlock:
- user_shm_unlock(size, current->user);
+ user_shm_unlock(size, user);
return ERR_PTR(error);
}
diff --git a/fs/inode.c b/fs/inode.c
index 0487ddba1397..098a2443196f 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -108,84 +108,100 @@ static void wake_up_inode(struct inode *inode)
wake_up_bit(&inode->i_state, __I_LOCK);
}
-static struct inode *alloc_inode(struct super_block *sb)
+/**
+ * inode_init_always - perform inode structure intialisation
+ * @sb - superblock inode belongs to.
+ * @inode - inode to initialise
+ *
+ * These are initializations that need to be done on every inode
+ * allocation as the fields are not initialised by slab allocation.
+ */
+struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
{
static const struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static const struct file_operations empty_fops;
- struct inode *inode;
-
- if (sb->s_op->alloc_inode)
- inode = sb->s_op->alloc_inode(sb);
- else
- inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);
- if (inode) {
- struct address_space * const mapping = &inode->i_data;
-
- inode->i_sb = sb;
- inode->i_blkbits = sb->s_blocksize_bits;
- inode->i_flags = 0;
- atomic_set(&inode->i_count, 1);
- inode->i_op = &empty_iops;
- inode->i_fop = &empty_fops;
- inode->i_nlink = 1;
- atomic_set(&inode->i_writecount, 0);
- inode->i_size = 0;
- inode->i_blocks = 0;
- inode->i_bytes = 0;
- inode->i_generation = 0;
+ struct address_space * const mapping = &inode->i_data;
+
+ inode->i_sb = sb;
+ inode->i_blkbits = sb->s_blocksize_bits;
+ inode->i_flags = 0;
+ atomic_set(&inode->i_count, 1);
+ inode->i_op = &empty_iops;
+ inode->i_fop = &empty_fops;
+ inode->i_nlink = 1;
+ atomic_set(&inode->i_writecount, 0);
+ inode->i_size = 0;
+ inode->i_blocks = 0;
+ inode->i_bytes = 0;
+ inode->i_generation = 0;
#ifdef CONFIG_QUOTA
- memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
+ memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
#endif
- inode->i_pipe = NULL;
- inode->i_bdev = NULL;
- inode->i_cdev = NULL;
- inode->i_rdev = 0;
- inode->dirtied_when = 0;
- if (security_inode_alloc(inode)) {
- if (inode->i_sb->s_op->destroy_inode)
- inode->i_sb->s_op->destroy_inode(inode);
- else
- kmem_cache_free(inode_cachep, (inode));
- return NULL;
- }
+ inode->i_pipe = NULL;
+ inode->i_bdev = NULL;
+ inode->i_cdev = NULL;
+ inode->i_rdev = 0;
+ inode->dirtied_when = 0;
+ if (security_inode_alloc(inode)) {
+ if (inode->i_sb->s_op->destroy_inode)
+ inode->i_sb->s_op->destroy_inode(inode);
+ else
+ kmem_cache_free(inode_cachep, (inode));
+ return NULL;
+ }
- spin_lock_init(&inode->i_lock);
- lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
+ spin_lock_init(&inode->i_lock);
+ lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
- mutex_init(&inode->i_mutex);
- lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);
+ mutex_init(&inode->i_mutex);
+ lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);
- init_rwsem(&inode->i_alloc_sem);
- lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);
+ init_rwsem(&inode->i_alloc_sem);
+ lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);
- mapping->a_ops = &empty_aops;
- mapping->host = inode;
- mapping->flags = 0;
- mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
- mapping->assoc_mapping = NULL;
- mapping->backing_dev_info = &default_backing_dev_info;
- mapping->writeback_index = 0;
+ mapping->a_ops = &empty_aops;
+ mapping->host = inode;
+ mapping->flags = 0;
+ mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
+ mapping->assoc_mapping = NULL;
+ mapping->backing_dev_info = &default_backing_dev_info;
+ mapping->writeback_index = 0;
- /*
- * If the block_device provides a backing_dev_info for client
- * inodes then use that. Otherwise the inode share the bdev's
- * backing_dev_info.
- */
- if (sb->s_bdev) {
- struct backing_dev_info *bdi;
+ /*
+ * If the block_device provides a backing_dev_info for client
+ * inodes then use that. Otherwise the inode share the bdev's
+ * backing_dev_info.
+ */
+ if (sb->s_bdev) {
+ struct backing_dev_info *bdi;
- bdi = sb->s_bdev->bd_inode_backing_dev_info;
- if (!bdi)
- bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
- mapping->backing_dev_info = bdi;
- }
- inode->i_private = NULL;
- inode->i_mapping = mapping;
+ bdi = sb->s_bdev->bd_inode_backing_dev_info;
+ if (!bdi)
+ bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
+ mapping->backing_dev_info = bdi;
}
+ inode->i_private = NULL;
+ inode->i_mapping = mapping;
+
return inode;
}
+EXPORT_SYMBOL(inode_init_always);
+
+static struct inode *alloc_inode(struct super_block *sb)
+{
+ struct inode *inode;
+
+ if (sb->s_op->alloc_inode)
+ inode = sb->s_op->alloc_inode(sb);
+ else
+ inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);
+
+ if (inode)
+ return inode_init_always(sb, inode);
+ return NULL;
+}
void destroy_inode(struct inode *inode)
{
@@ -196,6 +212,7 @@ void destroy_inode(struct inode *inode)
else
kmem_cache_free(inode_cachep, (inode));
}
+EXPORT_SYMBOL(destroy_inode);
/*
@@ -534,6 +551,49 @@ repeat:
return node ? inode : NULL;
}
+static unsigned long hash(struct super_block *sb, unsigned long hashval)
+{
+ unsigned long tmp;
+
+ tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /
+ L1_CACHE_BYTES;
+ tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> I_HASHBITS);
+ return tmp & I_HASHMASK;
+}
+
+static inline void
+__inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
+ struct inode *inode)
+{
+ inodes_stat.nr_inodes++;
+ list_add(&inode->i_list, &inode_in_use);
+ list_add(&inode->i_sb_list, &sb->s_inodes);
+ if (head)
+ hlist_add_head(&inode->i_hash, head);
+}
+
+/**
+ * inode_add_to_lists - add a new inode to relevant lists
+ * @sb - superblock inode belongs to.
+ * @inode - inode to mark in use
+ *
+ * When an inode is allocated it needs to be accounted for, added to the in use
+ * list, the owning superblock and the inode hash. This needs to be done under
+ * the inode_lock, so export a function to do this rather than the inode lock
+ * itself. We calculate the hash list to add to here so it is all internal
+ * which requires the caller to have already set up the inode number in the
+ * inode to add.
+ */
+void inode_add_to_lists(struct super_block *sb, struct inode *inode)
+{
+ struct hlist_head *head = inode_hashtable + hash(sb, inode->i_ino);
+
+ spin_lock(&inode_lock);
+ __inode_add_to_lists(sb, head, inode);
+ spin_unlock(&inode_lock);
+}
+EXPORT_SYMBOL_GPL(inode_add_to_lists);
+
/**
* new_inode - obtain an inode
* @sb: superblock
@@ -561,9 +621,7 @@ struct inode *new_inode(struct super_block *sb)
inode = alloc_inode(sb);
if (inode) {
spin_lock(&inode_lock);
- inodes_stat.nr_inodes++;
- list_add(&inode->i_list, &inode_in_use);
- list_add(&inode->i_sb_list, &sb->s_inodes);
+ __inode_add_to_lists(sb, NULL, inode);
inode->i_ino = ++last_ino;
inode->i_state = 0;
spin_unlock(&inode_lock);
@@ -622,10 +680,7 @@ static struct inode * get_new_inode(struct super_block *sb, struct hlist_head *h
if (set(inode, data))
goto set_failed;
- inodes_stat.nr_inodes++;
- list_add(&inode->i_list, &inode_in_use);
- list_add(&inode->i_sb_list, &sb->s_inodes);
- hlist_add_head(&inode->i_hash, head);
+ __inode_add_to_lists(sb, head, inode);
inode->i_state = I_LOCK|I_NEW;
spin_unlock(&inode_lock);
@@ -671,10 +726,7 @@ static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_he
old = find_inode_fast(sb, head, ino);
if (!old) {
inode->i_ino = ino;
- inodes_stat.nr_inodes++;
- list_add(&inode->i_list, &inode_in_use);
- list_add(&inode->i_sb_list, &sb->s_inodes);
- hlist_add_head(&inode->i_hash, head);
+ __inode_add_to_lists(sb, head, inode);
inode->i_state = I_LOCK|I_NEW;
spin_unlock(&inode_lock);
@@ -698,16 +750,6 @@ static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_he
return inode;
}
-static unsigned long hash(struct super_block *sb, unsigned long hashval)
-{
- unsigned long tmp;
-
- tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /
- L1_CACHE_BYTES;
- tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> I_HASHBITS);
- return tmp & I_HASHMASK;
-}
-
/**
* iunique - get a unique inode number
* @sb: superblock
@@ -1292,6 +1334,7 @@ int inode_wait(void *word)
schedule();
return 0;
}
+EXPORT_SYMBOL(inode_wait);
/*
* If we try to find an inode in the inode hash while it is being
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
index d367e9b92862..e2425bbd871f 100644
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -601,7 +601,7 @@ asmlinkage long sys_inotify_init1(int flags)
goto out_put_fd;
}
- user = get_uid(current->user);
+ user = get_current_user();
if (unlikely(atomic_read(&user->inotify_devs) >=
inotify_max_user_instances)) {
ret = -EMFILE;
diff --git a/fs/internal.h b/fs/internal.h
index 80aa9a023372..53af885f1732 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -10,6 +10,7 @@
*/
struct super_block;
+struct linux_binprm;
/*
* block_dev.c
@@ -40,6 +41,11 @@ static inline int sb_is_blkdev_sb(struct super_block *sb)
extern void __init chrdev_init(void);
/*
+ * exec.c
+ */
+extern void check_unsafe_exec(struct linux_binprm *);
+
+/*
* namespace.c
*/
extern int copy_mount_options(const void __user *, unsigned long *);
diff --git a/fs/ioprio.c b/fs/ioprio.c
index da3cc460d4df..3569e0ad86a2 100644
--- a/fs/ioprio.c
+++ b/fs/ioprio.c
@@ -31,10 +31,16 @@ static int set_task_ioprio(struct task_struct *task, int ioprio)
{
int err;
struct io_context *ioc;
+ const struct cred *cred = current_cred(), *tcred;
- if (task->uid != current->euid &&
- task->uid != current->uid && !capable(CAP_SYS_NICE))
+ rcu_read_lock();
+ tcred = __task_cred(task);
+ if (tcred->uid != cred->euid &&
+ tcred->uid != cred->uid && !capable(CAP_SYS_NICE)) {
+ rcu_read_unlock();
return -EPERM;
+ }
+ rcu_read_unlock();
err = security_task_setioprio(task, ioprio);
if (err)
@@ -123,7 +129,7 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
break;
case IOPRIO_WHO_USER:
if (!who)
- user = current->user;
+ user = current_user();
else
user = find_user(who);
@@ -131,7 +137,7 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
break;
do_each_thread(g, p) {
- if (p->uid != who)
+ if (__task_cred(p)->uid != who)
continue;
ret = set_task_ioprio(p, ioprio);
if (ret)
@@ -216,7 +222,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
break;
case IOPRIO_WHO_USER:
if (!who)
- user = current->user;
+ user = current_user();
else
user = find_user(who);
@@ -224,7 +230,7 @@ asmlinkage long sys_ioprio_get(int which, int who)
break;
do_each_thread(g, p) {
- if (p->uid != user->uid)
+ if (__task_cred(p)->uid != user->uid)
continue;
tmpio = get_task_ioprio(p);
if (tmpio < 0)
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 210339784b56..b00ee9f05a06 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -59,8 +59,14 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
if (inode->i_size >= IDATASIZE) {
inode->i_op = &page_symlink_inode_operations;
inode->i_mapping->a_ops = &jfs_aops;
- } else
+ } else {
inode->i_op = &jfs_symlink_inode_operations;
+ /*
+ * The inline data should be null-terminated, but
+ * don't let on-disk corruption crash the kernel
+ */
+ JFS_IP(inode)->i_inline[inode->i_size] = '\0';
+ }
} else {
inode->i_op = &jfs_file_inode_operations;
init_special_inode(inode, inode->i_mode, inode->i_rdev);
diff --git a/fs/jfs/jfs_inode.c b/fs/jfs/jfs_inode.c
index ed6574bee51a..70022fd1c539 100644
--- a/fs/jfs/jfs_inode.c
+++ b/fs/jfs/jfs_inode.c
@@ -93,13 +93,13 @@ struct inode *ialloc(struct inode *parent, umode_t mode)
return ERR_PTR(rc);
}
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (parent->i_mode & S_ISGID) {
inode->i_gid = parent->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
/*
* New inodes need to save sane values on disk when
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 8307dd64bf46..1f3b0fc0d351 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -14,6 +14,7 @@
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
#include <linux/smp_lock.h>
+#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
@@ -60,7 +61,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
nlm_init->protocol, nlm_version,
- nlm_init->hostname);
+ nlm_init->hostname, nlm_init->noresvport);
if (host == NULL) {
lockd_down();
return ERR_PTR(-ENOLCK);
@@ -191,11 +192,15 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
void
nlmclnt_recovery(struct nlm_host *host)
{
+ struct task_struct *task;
+
if (!host->h_reclaiming++) {
nlm_get_host(host);
- __module_get(THIS_MODULE);
- if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0)
- module_put(THIS_MODULE);
+ task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
+ if (IS_ERR(task))
+ printk(KERN_ERR "lockd: unable to spawn reclaimer "
+ "thread. Locks for %s won't be reclaimed! "
+ "(%ld)\n", host->h_name, PTR_ERR(task));
}
}
@@ -207,7 +212,6 @@ reclaimer(void *ptr)
struct file_lock *fl, *next;
u32 nsmstate;
- daemonize("%s-reclaim", host->h_name);
allow_signal(SIGKILL);
down_write(&host->h_rwsem);
@@ -233,7 +237,12 @@ restart:
list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
list_del_init(&fl->fl_u.nfs_fl.list);
- /* Why are we leaking memory here? --okir */
+ /*
+ * sending this thread a SIGKILL will result in any unreclaimed
+ * locks being removed from the h_granted list. This means that
+ * the kernel will not attempt to reclaim them again if a new
+ * reclaimer thread is spawned for this host.
+ */
if (signalled())
continue;
if (nlmclnt_reclaim(host, fl) != 0)
@@ -261,5 +270,5 @@ restart:
nlm_release_host(host);
lockd_down();
unlock_kernel();
- module_put_and_exit(0);
+ return 0;
}
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 70fc63a1727b..abdebf76b820 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -48,6 +48,7 @@ struct nlm_lookup_host_info {
const size_t hostname_len; /* it's length */
const struct sockaddr *src_sap; /* our address (optional) */
const size_t src_len; /* it's length */
+ const int noresvport; /* use non-priv port */
};
/*
@@ -115,14 +116,14 @@ static void nlm_display_address(const struct sockaddr *sap,
snprintf(buf, len, "unspecified");
break;
case AF_INET:
- snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+ snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
break;
case AF_INET6:
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
- snprintf(buf, len, NIPQUAD_FMT,
- NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
+ snprintf(buf, len, "%pI4",
+ &sin6->sin6_addr.s6_addr32[3]);
else
- snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
+ snprintf(buf, len, "%pI6", &sin6->sin6_addr);
break;
default:
snprintf(buf, len, "unsupported address family");
@@ -222,6 +223,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
host->h_nsmstate = 0; /* real NSM state */
host->h_nsmhandle = nsm;
host->h_server = ni->server;
+ host->h_noresvport = ni->noresvport;
hlist_add_head(&host->h_hash, chain);
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
@@ -272,6 +274,7 @@ nlm_destroy_host(struct nlm_host *host)
* @protocol: transport protocol to use
* @version: NLM protocol version
* @hostname: '\0'-terminated hostname of server
+ * @noresvport: 1 if non-privileged port should be used
*
* Returns an nlm_host structure that matches the passed-in
* [server address, transport protocol, NLM version, server hostname].
@@ -281,7 +284,9 @@ nlm_destroy_host(struct nlm_host *host)
struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen,
const unsigned short protocol,
- const u32 version, const char *hostname)
+ const u32 version,
+ const char *hostname,
+ int noresvport)
{
const struct sockaddr source = {
.sa_family = AF_UNSPEC,
@@ -296,6 +301,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
.hostname_len = strlen(hostname),
.src_sap = &source,
.src_len = sizeof(source),
+ .noresvport = noresvport,
};
dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
@@ -417,6 +423,8 @@ nlm_bind_host(struct nlm_host *host)
*/
if (!host->h_server)
args.flags |= RPC_CLNT_CREATE_HARDRTRY;
+ if (host->h_noresvport)
+ args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
clnt = rpc_create(&args);
if (!IS_ERR(clnt))
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 4e7e958e8f67..ffd3461f75ef 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -179,7 +179,7 @@ static __be32 *xdr_encode_mon_name(__be32 *p, struct nsm_args *argp)
if (!nsm_use_hostnames) {
snprintf(buffer, XDR_ADDRBUF_LEN,
- NIPQUAD_FMT, NIPQUAD(argp->addr));
+ "%pI4", &argp->addr);
name = buffer;
}
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 56b076736b56..252d80163d02 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -45,7 +45,7 @@
static struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops;
-EXPORT_SYMBOL(nlmsvc_ops);
+EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users;
@@ -300,7 +300,7 @@ out:
mutex_unlock(&nlmsvc_mutex);
return error;
}
-EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL_GPL(lockd_up);
/*
* Decrement the user count and bring down lockd if we're the last.
@@ -329,7 +329,7 @@ lockd_down(void)
out:
mutex_unlock(&nlmsvc_mutex);
}
-EXPORT_SYMBOL(lockd_down);
+EXPORT_SYMBOL_GPL(lockd_down);
#ifdef CONFIG_SYSCTL
diff --git a/fs/locks.c b/fs/locks.c
index 09062e3ff104..46a2e12f7d42 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1349,7 +1349,7 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
struct inode *inode = dentry->d_inode;
int error, rdlease_count = 0, wrlease_count = 0;
- if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+ if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE))
return -EACCES;
if (!S_ISREG(inode->i_mode))
return -EINVAL;
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index 703cc35e04b9..3aebe322271a 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -262,8 +262,8 @@ struct inode * minix_new_inode(const struct inode * dir, int * error)
iput(inode);
return NULL;
}
- inode->i_uid = current->fsuid;
- inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current_fsgid();
inode->i_ino = j;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
inode->i_blocks = 0;
diff --git a/fs/namei.c b/fs/namei.c
index d34e0f9681c6..af3783fff1de 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -186,7 +186,7 @@ int generic_permission(struct inode *inode, int mask,
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
- if (current->fsuid == inode->i_uid)
+ if (current_fsuid() == inode->i_uid)
mode >>= 6;
else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
@@ -441,7 +441,7 @@ static int exec_permission_lite(struct inode *inode)
if (inode->i_op && inode->i_op->permission)
return -EAGAIN;
- if (current->fsuid == inode->i_uid)
+ if (current_fsuid() == inode->i_uid)
mode >>= 6;
else if (in_group_p(inode->i_gid))
mode >>= 3;
@@ -1334,11 +1334,13 @@ static int user_path_parent(int dfd, const char __user *path,
*/
static inline int check_sticky(struct inode *dir, struct inode *inode)
{
+ uid_t fsuid = current_fsuid();
+
if (!(dir->i_mode & S_ISVTX))
return 0;
- if (inode->i_uid == current->fsuid)
+ if (inode->i_uid == fsuid)
return 0;
- if (dir->i_uid == current->fsuid)
+ if (dir->i_uid == fsuid)
return 0;
return !capable(CAP_FOWNER);
}
diff --git a/fs/namespace.c b/fs/namespace.c
index 65b3dc844c87..1c09cab8f7cf 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1176,7 +1176,7 @@ static int mount_is_safe(struct path *path)
if (S_ISLNK(path->dentry->d_inode->i_mode))
return -EPERM;
if (path->dentry->d_inode->i_mode & S_ISVTX) {
- if (current->uid != path->dentry->d_inode->i_uid)
+ if (current_uid() != path->dentry->d_inode->i_uid)
return -EPERM;
}
if (inode_permission(path->dentry->d_inode, MAY_WRITE))
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 3a97c95e1ca2..6d04e050c74e 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -40,10 +40,10 @@ ncp_get_fs_info(struct ncp_server * server, struct file *file,
struct inode *inode = file->f_path.dentry->d_inode;
struct ncp_fs_info info;
- if ((file_permission(file, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (file_permission(file, MAY_WRITE) != 0
+ && current_uid() != server->m.mounted_uid)
return -EACCES;
- }
+
if (copy_from_user(&info, arg, sizeof(info)))
return -EFAULT;
@@ -70,10 +70,10 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct file *file,
struct inode *inode = file->f_path.dentry->d_inode;
struct ncp_fs_info_v2 info2;
- if ((file_permission(file, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (file_permission(file, MAY_WRITE) != 0
+ && current_uid() != server->m.mounted_uid)
return -EACCES;
- }
+
if (copy_from_user(&info2, arg, sizeof(info2)))
return -EFAULT;
@@ -141,10 +141,10 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct file *file,
struct inode *inode = file->f_path.dentry->d_inode;
struct compat_ncp_fs_info_v2 info2;
- if ((file_permission(file, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (file_permission(file, MAY_WRITE) != 0
+ && current_uid() != server->m.mounted_uid)
return -EACCES;
- }
+
if (copy_from_user(&info2, arg, sizeof(info2)))
return -EFAULT;
@@ -270,16 +270,17 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
struct ncp_ioctl_request request;
char* bouncebuffer;
void __user *argp = (void __user *)arg;
+ uid_t uid = current_uid();
switch (cmd) {
#ifdef CONFIG_COMPAT
case NCP_IOC_NCPREQUEST_32:
#endif
case NCP_IOC_NCPREQUEST:
- if ((file_permission(filp, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (file_permission(filp, MAY_WRITE) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
#ifdef CONFIG_COMPAT
if (cmd == NCP_IOC_NCPREQUEST_32) {
struct compat_ncp_ioctl_request request32;
@@ -356,10 +357,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
case NCP_IOC_GETMOUNTUID16:
case NCP_IOC_GETMOUNTUID32:
case NCP_IOC_GETMOUNTUID64:
- if ((file_permission(filp, MAY_READ) != 0)
- && (current->uid != server->m.mounted_uid)) {
+ if (file_permission(filp, MAY_READ) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
if (cmd == NCP_IOC_GETMOUNTUID16) {
u16 uid;
SET_UID(uid, server->m.mounted_uid);
@@ -380,11 +381,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
{
struct ncp_setroot_ioctl sr;
- if ((file_permission(filp, MAY_READ) != 0)
- && (current->uid != server->m.mounted_uid))
- {
+ if (file_permission(filp, MAY_READ) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
if (server->m.mounted_vol[0]) {
struct dentry* dentry = inode->i_sb->s_root;
@@ -408,6 +408,7 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
return -EFAULT;
return 0;
}
+
case NCP_IOC_SETROOT:
{
struct ncp_setroot_ioctl sr;
@@ -455,11 +456,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
#ifdef CONFIG_NCPFS_PACKET_SIGNING
case NCP_IOC_SIGN_INIT:
- if ((file_permission(filp, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid))
- {
+ if (file_permission(filp, MAY_WRITE) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
if (argp) {
if (server->sign_wanted)
{
@@ -478,24 +478,22 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
return 0;
case NCP_IOC_SIGN_WANTED:
- if ((file_permission(filp, MAY_READ) != 0)
- && (current->uid != server->m.mounted_uid))
- {
+ if (file_permission(filp, MAY_READ) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
if (put_user(server->sign_wanted, (int __user *)argp))
return -EFAULT;
return 0;
+
case NCP_IOC_SET_SIGN_WANTED:
{
int newstate;
- if ((file_permission(filp, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid))
- {
+ if (file_permission(filp, MAY_WRITE) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
/* get only low 8 bits... */
if (get_user(newstate, (unsigned char __user *)argp))
return -EFAULT;
@@ -512,11 +510,10 @@ static int __ncp_ioctl(struct inode *inode, struct file *filp,
#ifdef CONFIG_NCPFS_IOCTL_LOCKING
case NCP_IOC_LOCKUNLOCK:
- if ((file_permission(filp, MAY_WRITE) != 0)
- && (current->uid != server->m.mounted_uid))
- {
+ if (file_permission(filp, MAY_WRITE) != 0
+ && uid != server->m.mounted_uid)
return -EACCES;
- }
+
{
struct ncp_lock_ioctl rqdata;
@@ -585,9 +582,8 @@ outrel:
#ifdef CONFIG_COMPAT
case NCP_IOC_GETOBJECTNAME_32:
- if (current->uid != server->m.mounted_uid) {
+ if (uid != server->m.mounted_uid)
return -EACCES;
- }
{
struct compat_ncp_objectname_ioctl user;
size_t outl;
@@ -609,10 +605,10 @@ outrel:
return 0;
}
#endif
+
case NCP_IOC_GETOBJECTNAME:
- if (current->uid != server->m.mounted_uid) {
+ if (uid != server->m.mounted_uid)
return -EACCES;
- }
{
struct ncp_objectname_ioctl user;
size_t outl;
@@ -633,13 +629,13 @@ outrel:
return -EFAULT;
return 0;
}
+
#ifdef CONFIG_COMPAT
case NCP_IOC_SETOBJECTNAME_32:
#endif
case NCP_IOC_SETOBJECTNAME:
- if (current->uid != server->m.mounted_uid) {
+ if (uid != server->m.mounted_uid)
return -EACCES;
- }
{
struct ncp_objectname_ioctl user;
void* newname;
@@ -691,13 +687,13 @@ outrel:
kfree(oldname);
return 0;
}
+
#ifdef CONFIG_COMPAT
case NCP_IOC_GETPRIVATEDATA_32:
#endif
case NCP_IOC_GETPRIVATEDATA:
- if (current->uid != server->m.mounted_uid) {
+ if (uid != server->m.mounted_uid)
return -EACCES;
- }
{
struct ncp_privatedata_ioctl user;
size_t outl;
@@ -736,13 +732,13 @@ outrel:
return 0;
}
+
#ifdef CONFIG_COMPAT
case NCP_IOC_SETPRIVATEDATA_32:
#endif
case NCP_IOC_SETPRIVATEDATA:
- if (current->uid != server->m.mounted_uid) {
+ if (uid != server->m.mounted_uid)
return -EACCES;
- }
{
struct ncp_privatedata_ioctl user;
void* new;
@@ -794,9 +790,10 @@ outrel:
#endif /* CONFIG_NCPFS_NLS */
case NCP_IOC_SETDENTRYTTL:
- if ((file_permission(filp, MAY_WRITE) != 0) &&
- (current->uid != server->m.mounted_uid))
+ if (file_permission(filp, MAY_WRITE) != 0 &&
+ uid != server->m.mounted_uid)
return -EACCES;
+
{
u_int32_t user;
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index c2e9cfd9e5a4..3e634f2a1083 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -16,6 +16,7 @@
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
+#include <linux/sunrpc/svcauth_gss.h>
#include <net/inet_sock.h>
@@ -182,10 +183,34 @@ void nfs_callback_down(void)
mutex_unlock(&nfs_callback_mutex);
}
+static int check_gss_callback_principal(struct nfs_client *clp,
+ struct svc_rqst *rqstp)
+{
+ struct rpc_clnt *r = clp->cl_rpcclient;
+ char *p = svc_gss_principal(rqstp);
+
+ /*
+ * It might just be a normal user principal, in which case
+ * userspace won't bother to tell us the name at all.
+ */
+ if (p == NULL)
+ return SVC_DENIED;
+
+ /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
+
+ if (memcmp(p, "nfs@", 4) != 0)
+ return SVC_DENIED;
+ p += 4;
+ if (strcmp(p, r->cl_server) != 0)
+ return SVC_DENIED;
+ return SVC_OK;
+}
+
static int nfs_callback_authenticate(struct svc_rqst *rqstp)
{
struct nfs_client *clp;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
+ int ret = SVC_OK;
/* Don't talk to strangers */
clp = nfs_find_client(svc_addr(rqstp), 4);
@@ -194,21 +219,22 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
dprintk("%s: %s NFSv4 callback!\n", __func__,
svc_print_addr(rqstp, buf, sizeof(buf)));
- nfs_put_client(clp);
switch (rqstp->rq_authop->flavour) {
case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL)
- return SVC_DENIED;
+ ret = SVC_DENIED;
break;
case RPC_AUTH_UNIX:
break;
case RPC_AUTH_GSS:
- /* FIXME: RPCSEC_GSS handling? */
+ ret = check_gss_callback_principal(clp, rqstp);
+ break;
default:
- return SVC_DENIED;
+ ret = SVC_DENIED;
}
- return SVC_OK;
+ nfs_put_client(clp);
+ return ret;
}
/*
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 7547600b6174..9b728f3565a1 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -143,7 +143,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto;
#ifdef CONFIG_NFS_V4
- init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations);
spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
@@ -224,31 +223,54 @@ void nfs_put_client(struct nfs_client *clp)
}
}
-static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
- const struct sockaddr_in *sa2)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static const struct in6_addr *nfs_map_ipv4_addr(const struct sockaddr *sa, struct in6_addr *addr_mapped)
{
- return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
+ switch (sa->sa_family) {
+ default:
+ return NULL;
+ case AF_INET6:
+ return &((const struct sockaddr_in6 *)sa)->sin6_addr;
+ break;
+ case AF_INET:
+ ipv6_addr_set_v4mapped(((const struct sockaddr_in *)sa)->sin_addr.s_addr,
+ addr_mapped);
+ return addr_mapped;
+ }
}
-static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1,
- const struct sockaddr_in6 *sa2)
+static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
+ const struct sockaddr *sa2)
+{
+ const struct in6_addr *addr1;
+ const struct in6_addr *addr2;
+ struct in6_addr addr1_mapped;
+ struct in6_addr addr2_mapped;
+
+ addr1 = nfs_map_ipv4_addr(sa1, &addr1_mapped);
+ if (likely(addr1 != NULL)) {
+ addr2 = nfs_map_ipv4_addr(sa2, &addr2_mapped);
+ if (likely(addr2 != NULL))
+ return ipv6_addr_equal(addr1, addr2);
+ }
+ return 0;
+}
+#else
+static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
+ const struct sockaddr_in *sa2)
{
- return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr);
+ return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
}
static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
const struct sockaddr *sa2)
{
- switch (sa1->sa_family) {
- case AF_INET:
- return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
- (const struct sockaddr_in *)sa2);
- case AF_INET6:
- return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1,
- (const struct sockaddr_in6 *)sa2);
- }
- BUG();
+ if (unlikely(sa1->sa_family != AF_INET || sa2->sa_family != AF_INET))
+ return 0;
+ return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
+ (const struct sockaddr_in *)sa2);
}
+#endif
/*
* Find a client by IP address and protocol version
@@ -270,8 +292,6 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
if (clp->rpc_ops->version != nfsversion)
continue;
- if (addr->sa_family != clap->sa_family)
- continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
continue;
@@ -305,8 +325,6 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
if (clp->rpc_ops->version != nfsvers)
continue;
- if (sap->sa_family != clap->sa_family)
- continue;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(sap, clap))
continue;
@@ -470,7 +488,7 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
static int nfs_create_rpc_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms,
rpc_authflavor_t flavor,
- int flags)
+ int discrtry, int noresvport)
{
struct rpc_clnt *clnt = NULL;
struct rpc_create_args args = {
@@ -482,9 +500,13 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
.program = &nfs_program,
.version = clp->rpc_ops->version,
.authflavor = flavor,
- .flags = flags,
};
+ if (discrtry)
+ args.flags |= RPC_CLNT_CREATE_DISCRTRY;
+ if (noresvport)
+ args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+
if (!IS_ERR(clp->cl_rpcclient))
return 0;
@@ -522,6 +544,8 @@ static int nfs_start_lockd(struct nfs_server *server)
.protocol = server->flags & NFS_MOUNT_TCP ?
IPPROTO_TCP : IPPROTO_UDP,
.nfs_version = clp->rpc_ops->version,
+ .noresvport = server->flags & NFS_MOUNT_NORESVPORT ?
+ 1 : 0,
};
if (nlm_init.nfs_version > 3)
@@ -623,7 +647,8 @@ static int nfs_init_client(struct nfs_client *clp,
* Create a client RPC handle for doing FSSTAT with UNIX auth only
* - RFC 2623, sec 2.3.2
*/
- error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 0);
+ error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX,
+ 0, data->flags & NFS_MOUNT_NORESVPORT);
if (error < 0)
goto error;
nfs_mark_client_ready(clp, NFS_CS_READY);
@@ -965,7 +990,8 @@ error:
static int nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms,
const char *ip_addr,
- rpc_authflavor_t authflavour)
+ rpc_authflavor_t authflavour,
+ int flags)
{
int error;
@@ -979,7 +1005,7 @@ static int nfs4_init_client(struct nfs_client *clp,
clp->rpc_ops = &nfs_v4_clientops;
error = nfs_create_rpc_client(clp, timeparms, authflavour,
- RPC_CLNT_CREATE_DISCRTRY);
+ 1, flags & NFS_MOUNT_NORESVPORT);
if (error < 0)
goto error;
memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
@@ -1030,7 +1056,8 @@ static int nfs4_set_client(struct nfs_server *server,
error = PTR_ERR(clp);
goto error;
}
- error = nfs4_init_client(clp, timeparms, ip_addr, authflavour);
+ error = nfs4_init_client(clp, timeparms, ip_addr, authflavour,
+ server->flags);
if (error < 0)
goto error_put;
@@ -1059,6 +1086,10 @@ static int nfs4_init_server(struct nfs_server *server,
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans);
+ /* Initialise the client representation from the mount data */
+ server->flags = data->flags;
+ server->caps |= NFS_CAP_ATOMIC_OPEN;
+
/* Get a client record */
error = nfs4_set_client(server,
data->nfs_server.hostname,
@@ -1071,10 +1102,6 @@ static int nfs4_init_server(struct nfs_server *server,
if (error < 0)
goto error;
- /* Initialise the client representation from the mount data */
- server->flags = data->flags;
- server->caps |= NFS_CAP_ATOMIC_OPEN;
-
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
@@ -1177,6 +1204,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
parent_server = NFS_SB(data->sb);
parent_client = parent_server->nfs_client;
+ /* Initialise the client representation from the parent server */
+ nfs_server_copy_userdata(server, parent_server);
+ server->caps |= NFS_CAP_ATOMIC_OPEN;
+
/* Get a client representation.
* Note: NFSv4 always uses TCP, */
error = nfs4_set_client(server, data->hostname,
@@ -1189,10 +1220,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
if (error < 0)
goto error;
- /* Initialise the client representation from the parent server */
- nfs_server_copy_userdata(server, parent_server);
- server->caps |= NFS_CAP_ATOMIC_OPEN;
-
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
if (error < 0)
goto error;
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index cc563cfa6940..968225a88015 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
put_rpccred(cred);
}
+void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
+{
+ set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
+}
+
+int nfs_have_delegation(struct inode *inode, fmode_t flags)
+{
+ struct nfs_delegation *delegation;
+ int ret = 0;
+
+ flags &= FMODE_READ|FMODE_WRITE;
+ rcu_read_lock();
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (delegation != NULL && (delegation->type & flags) == flags) {
+ nfs_mark_delegation_referenced(delegation);
+ ret = 1;
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
{
struct inode *inode = state->inode;
@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
delegation->maxsize = res->maxsize;
oldcred = delegation->cred;
delegation->cred = get_rpccred(cred);
- delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
+ clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
NFS_I(inode)->delegation_state = delegation->type;
smp_wmb();
put_rpccred(oldcred);
@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
return res;
}
+static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
+{
+ struct inode *inode = NULL;
+
+ spin_lock(&delegation->lock);
+ if (delegation->inode != NULL)
+ inode = igrab(delegation->inode);
+ spin_unlock(&delegation->lock);
+ return inode;
+}
+
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
{
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
if (delegation == NULL)
goto nomatch;
+ spin_lock(&delegation->lock);
if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
sizeof(delegation->stateid.data)) != 0)
- goto nomatch;
+ goto nomatch_unlock;
list_del_rcu(&delegation->super_list);
+ delegation->inode = NULL;
nfsi->delegation_state = 0;
rcu_assign_pointer(nfsi->delegation, NULL);
+ spin_unlock(&delegation->lock);
return delegation;
+nomatch_unlock:
+ spin_unlock(&delegation->lock);
nomatch:
return NULL;
}
@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation->change_attr = nfsi->change_attr;
delegation->cred = get_rpccred(cred);
delegation->inode = inode;
+ delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
+ spin_lock_init(&delegation->lock);
spin_lock(&clp->cl_lock);
if (rcu_dereference(nfsi->delegation) != NULL) {
@@ -226,22 +265,47 @@ static void nfs_msync_inode(struct inode *inode)
*/
static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
{
- struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
nfs_msync_inode(inode);
- down_read(&clp->cl_sem);
/* Guard against new delegated open calls */
down_write(&nfsi->rwsem);
nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem);
- up_read(&clp->cl_sem);
nfs_msync_inode(inode);
return nfs_do_return_delegation(inode, delegation, 1);
}
/*
+ * Return all delegations that have been marked for return
+ */
+void nfs_client_return_marked_delegations(struct nfs_client *clp)
+{
+ struct nfs_delegation *delegation;
+ struct inode *inode;
+
+restart:
+ rcu_read_lock();
+ list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+ if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
+ continue;
+ inode = nfs_delegation_grab_inode(delegation);
+ if (inode == NULL)
+ continue;
+ spin_lock(&clp->cl_lock);
+ delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
+ spin_unlock(&clp->cl_lock);
+ rcu_read_unlock();
+ if (delegation != NULL)
+ __nfs_inode_return_delegation(inode, delegation);
+ iput(inode);
+ goto restart;
+ }
+ rcu_read_unlock();
+}
+
+/*
* This function returns the delegation without reclaiming opens
* or protecting against delegation reclaims.
* It is therefore really only safe to be called from
@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode)
return err;
}
+static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
+{
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+}
+
/*
* Return all delegations associated to a super block
*/
-void nfs_return_all_delegations(struct super_block *sb)
+void nfs_super_return_all_delegations(struct super_block *sb)
{
struct nfs_client *clp = NFS_SB(sb)->nfs_client;
struct nfs_delegation *delegation;
- struct inode *inode;
if (clp == NULL)
return;
-restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- if (delegation->inode->i_sb != sb)
- continue;
- inode = igrab(delegation->inode);
- if (inode == NULL)
- continue;
- spin_lock(&clp->cl_lock);
- delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
- spin_unlock(&clp->cl_lock);
- rcu_read_unlock();
- if (delegation != NULL)
- __nfs_inode_return_delegation(inode, delegation);
- iput(inode);
- goto restart;
+ spin_lock(&delegation->lock);
+ if (delegation->inode != NULL && delegation->inode->i_sb == sb)
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ spin_unlock(&delegation->lock);
}
rcu_read_unlock();
+ nfs_client_return_marked_delegations(clp);
}
-static int nfs_do_expire_all_delegations(void *ptr)
+static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
{
- struct nfs_client *clp = ptr;
struct nfs_delegation *delegation;
- struct inode *inode;
- allow_signal(SIGKILL);
-restart:
- if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
- goto out;
- if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
- goto out;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- inode = igrab(delegation->inode);
- if (inode == NULL)
- continue;
- spin_lock(&clp->cl_lock);
- delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
- spin_unlock(&clp->cl_lock);
- rcu_read_unlock();
- if (delegation)
- __nfs_inode_return_delegation(inode, delegation);
- iput(inode);
- goto restart;
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
rcu_read_unlock();
-out:
- nfs_put_client(clp);
- module_put_and_exit(0);
+}
+
+static void nfs_delegation_run_state_manager(struct nfs_client *clp)
+{
+ if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
+ nfs4_schedule_state_manager(clp);
}
void nfs_expire_all_delegations(struct nfs_client *clp)
{
- struct task_struct *task;
-
- __module_get(THIS_MODULE);
- atomic_inc(&clp->cl_count);
- task = kthread_run(nfs_do_expire_all_delegations, clp,
- "%s-delegreturn",
- rpc_peeraddr2str(clp->cl_rpcclient,
- RPC_DISPLAY_ADDR));
- if (!IS_ERR(task))
- return;
- nfs_put_client(clp);
- module_put(THIS_MODULE);
+ nfs_client_mark_return_all_delegations(clp);
+ nfs_delegation_run_state_manager(clp);
}
/*
@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
*/
void nfs_handle_cb_pathdown(struct nfs_client *clp)
{
- struct nfs_delegation *delegation;
- struct inode *inode;
-
if (clp == NULL)
return;
-restart:
+ nfs_client_mark_return_all_delegations(clp);
+}
+
+static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
+{
+ struct nfs_delegation *delegation;
+
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- inode = igrab(delegation->inode);
- if (inode == NULL)
+ if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
continue;
- spin_lock(&clp->cl_lock);
- delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
- spin_unlock(&clp->cl_lock);
- rcu_read_unlock();
- if (delegation != NULL)
- __nfs_inode_return_delegation(inode, delegation);
- iput(inode);
- goto restart;
+ set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
}
rcu_read_unlock();
}
-struct recall_threadargs {
- struct inode *inode;
- struct nfs_client *clp;
- const nfs4_stateid *stateid;
-
- struct completion started;
- int result;
-};
-
-static int recall_thread(void *data)
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
{
- struct recall_threadargs *args = (struct recall_threadargs *)data;
- struct inode *inode = igrab(args->inode);
- struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
- struct nfs_inode *nfsi = NFS_I(inode);
- struct nfs_delegation *delegation;
-
- daemonize("nfsv4-delegreturn");
-
- nfs_msync_inode(inode);
- down_read(&clp->cl_sem);
- down_write(&nfsi->rwsem);
- spin_lock(&clp->cl_lock);
- delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
- if (delegation != NULL)
- args->result = 0;
- else
- args->result = -ENOENT;
- spin_unlock(&clp->cl_lock);
- complete(&args->started);
- nfs_delegation_claim_opens(inode, args->stateid);
- up_write(&nfsi->rwsem);
- up_read(&clp->cl_sem);
- nfs_msync_inode(inode);
-
- if (delegation != NULL)
- nfs_do_return_delegation(inode, delegation, 1);
- iput(inode);
- module_put_and_exit(0);
+ nfs_client_mark_return_unreferenced_delegations(clp);
+ nfs_delegation_run_state_manager(clp);
}
/*
@@ -432,22 +429,20 @@ static int recall_thread(void *data)
*/
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
{
- struct recall_threadargs data = {
- .inode = inode,
- .stateid = stateid,
- };
- int status;
+ struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+ struct nfs_delegation *delegation;
- init_completion(&data.started);
- __module_get(THIS_MODULE);
- status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
- if (status < 0)
- goto out_module_put;
- wait_for_completion(&data.started);
- return data.result;
-out_module_put:
- module_put(THIS_MODULE);
- return status;
+ rcu_read_lock();
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
+ sizeof(delegation->stateid.data)) != 0) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ nfs_mark_return_delegation(clp, delegation);
+ rcu_read_unlock();
+ nfs_delegation_run_state_manager(clp);
+ return 0;
}
/*
@@ -459,10 +454,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
struct inode *res = NULL;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
+ spin_lock(&delegation->lock);
+ if (delegation->inode != NULL &&
+ nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
res = igrab(delegation->inode);
- break;
}
+ spin_unlock(&delegation->lock);
+ if (res != NULL)
+ break;
}
rcu_read_unlock();
return res;
@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
struct nfs_delegation *delegation;
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
- delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
+ set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
rcu_read_unlock();
}
@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
+ struct inode *inode;
restart:
rcu_read_lock();
list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
- if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
+ if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
+ continue;
+ inode = nfs_delegation_grab_inode(delegation);
+ if (inode == NULL)
continue;
spin_lock(&clp->cl_lock);
- delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL);
+ delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
nfs_free_delegation(delegation);
+ iput(inode);
goto restart;
}
rcu_read_unlock();
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index f1c5e2a5d88e..09f383795174 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -17,14 +17,20 @@ struct nfs_delegation {
struct rpc_cred *cred;
struct inode *inode;
nfs4_stateid stateid;
- int type;
-#define NFS_DELEGATION_NEED_RECLAIM 1
- long flags;
+ fmode_t type;
loff_t maxsize;
__u64 change_attr;
+ unsigned long flags;
+ spinlock_t lock;
struct rcu_head rcu;
};
+enum {
+ NFS_DELEGATION_NEED_RECLAIM = 0,
+ NFS_DELEGATION_RETURN,
+ NFS_DELEGATION_REFERENCED,
+};
+
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode);
@@ -32,9 +38,11 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
void nfs_inode_return_delegation_noreclaim(struct inode *inode);
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
-void nfs_return_all_delegations(struct super_block *sb);
+void nfs_super_return_all_delegations(struct super_block *sb);
void nfs_expire_all_delegations(struct nfs_client *clp);
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs_client *clp);
+void nfs_client_return_marked_delegations(struct nfs_client *clp);
void nfs_delegation_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
@@ -45,22 +53,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
-static inline int nfs_have_delegation(struct inode *inode, int flags)
-{
- struct nfs_delegation *delegation;
- int ret = 0;
-
- flags &= FMODE_READ|FMODE_WRITE;
- rcu_read_lock();
- delegation = rcu_dereference(NFS_I(inode)->delegation);
- if (delegation != NULL && (delegation->type & flags) == flags)
- ret = 1;
- rcu_read_unlock();
- return ret;
-}
+void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
+int nfs_have_delegation(struct inode *inode, fmode_t flags);
#else
-static inline int nfs_have_delegation(struct inode *inode, int flags)
+static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
{
return 0;
}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 3e64b98f3a93..e35c8199f82f 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -799,6 +799,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_bad;
}
+ if (nfs_have_delegation(inode, FMODE_READ))
+ goto out_set_verifier;
+
/* Force a full look up iff the parent directory has changed */
if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) {
if (nfs_lookup_verify_inode(inode, nd))
@@ -817,6 +820,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad;
+out_set_verifier:
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_valid:
dput(parent);
@@ -973,7 +977,7 @@ struct dentry_operations nfs4_dentry_operations = {
* Use intent information to determine whether we need to substitute
* the NFSv4-style stateful OPEN for the LOOKUP call
*/
-static int is_atomic_open(struct inode *dir, struct nameidata *nd)
+static int is_atomic_open(struct nameidata *nd)
{
if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0)
return 0;
@@ -996,7 +1000,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
/* Check that we are indeed trying to open this file */
- if (!is_atomic_open(dir, nd))
+ if (!is_atomic_open(nd))
goto no_open;
if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
@@ -1047,10 +1051,10 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
struct inode *dir;
int openflags, ret = 0;
+ if (!is_atomic_open(nd))
+ goto no_open;
parent = dget_parent(dentry);
dir = parent->d_inode;
- if (!is_atomic_open(dir, nd))
- goto no_open;
/* We can't create new files in nfs_open_revalidate(), so we
* optimize away revalidation of negative dentries.
*/
@@ -1062,11 +1066,11 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
/* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode))
- goto no_open;
+ goto no_open_dput;
openflags = nd->intent.open.flags;
/* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
- goto no_open;
+ goto no_open_dput;
/* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC);
@@ -1081,10 +1085,9 @@ out:
if (!ret)
d_drop(dentry);
return ret;
-no_open:
+no_open_dput:
dput(parent);
- if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
- return 1;
+no_open:
return nfs_lookup_revalidate(dentry, nd);
}
#endif /* CONFIG_NFSV4 */
@@ -1794,7 +1797,8 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
cache = nfs_access_search_rbtree(inode, cred);
if (cache == NULL)
goto out;
- if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
+ if (!nfs_have_delegation(inode, FMODE_READ) &&
+ !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
goto out_stale;
res->jiffies = cache->jiffies;
res->cred = cache->cred;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index d22eb383e1cf..0c381686171e 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -592,7 +592,7 @@ static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context
/*
* Given an inode, search for an open context with the desired characteristics
*/
-struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
+struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL;
@@ -712,14 +712,7 @@ int nfs_attribute_timeout(struct inode *inode)
if (nfs_have_delegation(inode, FMODE_READ))
return 0;
- /*
- * Special case: if the attribute timeout is set to 0, then always
- * treat the cache as having expired (unless holding
- * a delegation).
- */
- if (nfsi->attrtimeo == 0)
- return 1;
- return !time_in_range(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
+ return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
}
/**
@@ -1182,7 +1175,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfsi->attrtimeo_timestamp = now;
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
} else {
- if (!time_in_range(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
+ if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index d212ee41caf2..340ede8f608f 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -63,6 +63,20 @@ struct nfs_parsed_mount_data {
struct security_mnt_opts lsm_opts;
};
+/* mount_clnt.c */
+struct nfs_mount_request {
+ struct sockaddr *sap;
+ size_t salen;
+ char *hostname;
+ char *dirpath;
+ u32 version;
+ unsigned short protocol;
+ struct nfs_fh *fh;
+ int noresvport;
+};
+
+extern int nfs_mount(struct nfs_mount_request *info);
+
/* client.c */
extern struct rpc_program nfs_program;
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 086a6830d785..ca905a5bb1ba 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -29,47 +29,43 @@ struct mnt_fhstatus {
/**
* nfs_mount - Obtain an NFS file handle for the given host and path
- * @addr: pointer to server's address
- * @len: size of server's address
- * @hostname: name of server host, or NULL
- * @path: pointer to string containing export path to mount
- * @version: mount version to use for this request
- * @protocol: transport protocol to use for thie request
- * @fh: pointer to location to place returned file handle
+ * @info: pointer to mount request arguments
*
* Uses default timeout parameters specified by underlying transport.
*/
-int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path,
- int version, int protocol, struct nfs_fh *fh)
+int nfs_mount(struct nfs_mount_request *info)
{
struct mnt_fhstatus result = {
- .fh = fh
+ .fh = info->fh
};
struct rpc_message msg = {
- .rpc_argp = path,
+ .rpc_argp = info->dirpath,
.rpc_resp = &result,
};
struct rpc_create_args args = {
- .protocol = protocol,
- .address = addr,
- .addrsize = len,
- .servername = hostname,
+ .protocol = info->protocol,
+ .address = info->sap,
+ .addrsize = info->salen,
+ .servername = info->hostname,
.program = &mnt_program,
- .version = version,
+ .version = info->version,
.authflavor = RPC_AUTH_UNIX,
- .flags = 0,
};
struct rpc_clnt *mnt_clnt;
int status;
dprintk("NFS: sending MNT request for %s:%s\n",
- (hostname ? hostname : "server"), path);
+ (info->hostname ? info->hostname : "server"),
+ info->dirpath);
+
+ if (info->noresvport)
+ args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
mnt_clnt = rpc_create(&args);
if (IS_ERR(mnt_clnt))
goto out_clnt_err;
- if (version == NFS_MNT3_VERSION)
+ if (info->version == NFS_MNT3_VERSION)
msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
else
msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index ea790645fda6..4e4d33204376 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -38,8 +38,12 @@ struct idmap;
((err) != NFSERR_NOFILEHANDLE))
enum nfs4_client_state {
- NFS4CLNT_STATE_RECOVER = 0,
+ NFS4CLNT_MANAGER_RUNNING = 0,
+ NFS4CLNT_CHECK_LEASE,
NFS4CLNT_LEASE_EXPIRED,
+ NFS4CLNT_RECLAIM_REBOOT,
+ NFS4CLNT_RECLAIM_NOGRACE,
+ NFS4CLNT_DELEGRETURN,
};
/*
@@ -90,12 +94,18 @@ struct nfs4_state_owner {
spinlock_t so_lock;
atomic_t so_count;
+ unsigned long so_flags;
struct list_head so_states;
struct list_head so_delegations;
struct nfs_seqid_counter so_seqid;
struct rpc_sequence so_sequence;
};
+enum {
+ NFS_OWNER_RECLAIM_REBOOT,
+ NFS_OWNER_RECLAIM_NOGRACE
+};
+
/*
* struct nfs4_state maintains the client-side state for a given
* (state_owner,inode) tuple (OPEN) or state_owner (LOCK).
@@ -128,6 +138,8 @@ enum {
NFS_O_RDONLY_STATE, /* OPEN stateid has read-only state */
NFS_O_WRONLY_STATE, /* OPEN stateid has write-only state */
NFS_O_RDWR_STATE, /* OPEN stateid has read/write state */
+ NFS_STATE_RECLAIM_REBOOT, /* OPEN stateid server rebooted */
+ NFS_STATE_RECLAIM_NOGRACE, /* OPEN stateid needs to recover state */
};
struct nfs4_state {
@@ -149,7 +161,7 @@ struct nfs4_state {
unsigned int n_rdonly; /* Number of read-only references */
unsigned int n_wronly; /* Number of write-only references */
unsigned int n_rdwr; /* Number of read/write references */
- int state; /* State on the server (R,W, or RW) */
+ fmode_t state; /* State on the server (R,W, or RW) */
atomic_t count;
};
@@ -157,9 +169,12 @@ struct nfs4_state {
struct nfs4_exception {
long timeout;
int retry;
+ struct nfs4_state *state;
};
struct nfs4_state_recovery_ops {
+ int owner_flag_bit;
+ int state_flag_bit;
int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
int (*recover_lock)(struct nfs4_state *, struct file_lock *);
};
@@ -174,7 +189,6 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
/* nfs4proc.c */
-extern int nfs4_map_errors(int err);
extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *);
extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
@@ -187,7 +201,7 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
struct nfs4_fs_locations *fs_locations, struct page *page);
extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
-extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
+extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops;
extern const u32 nfs4_fattr_bitmap[2];
extern const u32 nfs4_statfs_bitmap[2];
@@ -202,16 +216,18 @@ extern void nfs4_kill_renewd(struct nfs_client *);
extern void nfs4_renew_state(struct work_struct *);
/* nfs4state.c */
-struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
+struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
extern void nfs4_put_state_owner(struct nfs4_state_owner *);
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
extern void nfs4_put_open_state(struct nfs4_state *);
-extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t);
-extern void nfs4_close_sync(struct path *, struct nfs4_state *, mode_t);
-extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
+extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
+extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
+extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
extern void nfs4_schedule_state_recovery(struct nfs_client *);
+extern void nfs4_schedule_state_manager(struct nfs_client *);
+extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 83e700a2b0c0..8dde84b988d9 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -62,14 +62,12 @@
struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
-static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
+static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
/* Prevent leaks of NFSv4 errors into userland */
-int nfs4_map_errors(int err)
+static int nfs4_map_errors(int err)
{
if (err < -1000) {
dprintk("%s could not handle NFSv4 error %d\n",
@@ -195,6 +193,83 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
kunmap_atomic(start, KM_USER0);
}
+static int nfs4_wait_bit_killable(void *word)
+{
+ if (fatal_signal_pending(current))
+ return -ERESTARTSYS;
+ schedule();
+ return 0;
+}
+
+static int nfs4_wait_clnt_recover(struct nfs_client *clp)
+{
+ int res;
+
+ might_sleep();
+
+ res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
+ nfs4_wait_bit_killable, TASK_KILLABLE);
+ return res;
+}
+
+static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
+{
+ int res = 0;
+
+ might_sleep();
+
+ if (*timeout <= 0)
+ *timeout = NFS4_POLL_RETRY_MIN;
+ if (*timeout > NFS4_POLL_RETRY_MAX)
+ *timeout = NFS4_POLL_RETRY_MAX;
+ schedule_timeout_killable(*timeout);
+ if (fatal_signal_pending(current))
+ res = -ERESTARTSYS;
+ *timeout <<= 1;
+ return res;
+}
+
+/* This is the error handling routine for processes that are allowed
+ * to sleep.
+ */
+static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+{
+ struct nfs_client *clp = server->nfs_client;
+ struct nfs4_state *state = exception->state;
+ int ret = errorcode;
+
+ exception->retry = 0;
+ switch(errorcode) {
+ case 0:
+ return 0;
+ case -NFS4ERR_ADMIN_REVOKED:
+ case -NFS4ERR_BAD_STATEID:
+ case -NFS4ERR_OPENMODE:
+ if (state == NULL)
+ break;
+ nfs4_state_mark_reclaim_nograce(clp, state);
+ case -NFS4ERR_STALE_CLIENTID:
+ case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_EXPIRED:
+ nfs4_schedule_state_recovery(clp);
+ ret = nfs4_wait_clnt_recover(clp);
+ if (ret == 0)
+ exception->retry = 1;
+ break;
+ case -NFS4ERR_FILE_OPEN:
+ case -NFS4ERR_GRACE:
+ case -NFS4ERR_DELAY:
+ ret = nfs4_delay(server->client, &exception->timeout);
+ if (ret != 0)
+ break;
+ case -NFS4ERR_OLD_STATEID:
+ exception->retry = 1;
+ }
+ /* We failed to handle the error */
+ return nfs4_map_errors(ret);
+}
+
+
static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{
struct nfs_client *clp = server->nfs_client;
@@ -248,7 +323,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
}
static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
- struct nfs4_state_owner *sp, int flags,
+ struct nfs4_state_owner *sp, fmode_t fmode, int flags,
const struct iattr *attrs)
{
struct dentry *parent = dget_parent(path->dentry);
@@ -268,7 +343,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
p->owner = sp;
atomic_inc(&sp->so_count);
p->o_arg.fh = NFS_FH(dir);
- p->o_arg.open_flags = flags,
+ p->o_arg.open_flags = flags;
+ p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
p->o_arg.clientid = server->nfs_client->cl_clientid;
p->o_arg.id = sp->so_owner_id.id;
p->o_arg.name = &p->path.dentry->d_name;
@@ -324,10 +400,13 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
return ret;
}
-static int can_open_cached(struct nfs4_state *state, int mode)
+static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
{
int ret = 0;
- switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
+
+ if (open_mode & O_EXCL)
+ goto out;
+ switch (mode & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ:
ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
break;
@@ -337,21 +416,23 @@ static int can_open_cached(struct nfs4_state *state, int mode)
case FMODE_READ|FMODE_WRITE:
ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
}
+out:
return ret;
}
-static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
+static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
{
- if ((delegation->type & open_flags) != open_flags)
+ if ((delegation->type & fmode) != fmode)
return 0;
- if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM)
+ if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
return 0;
+ nfs_mark_delegation_referenced(delegation);
return 1;
}
-static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
+static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
{
- switch (open_flags) {
+ switch (fmode) {
case FMODE_WRITE:
state->n_wronly++;
break;
@@ -361,15 +442,15 @@ static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
case FMODE_READ|FMODE_WRITE:
state->n_rdwr++;
}
- nfs4_state_set_mode_locked(state, state->state | open_flags);
+ nfs4_state_set_mode_locked(state, state->state | fmode);
}
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
{
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data));
memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data));
- switch (open_flags) {
+ switch (fmode) {
case FMODE_READ:
set_bit(NFS_O_RDONLY_STATE, &state->flags);
break;
@@ -381,16 +462,15 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
}
}
-static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
{
write_seqlock(&state->seqlock);
- nfs_set_open_stateid_locked(state, stateid, open_flags);
+ nfs_set_open_stateid_locked(state, stateid, fmode);
write_sequnlock(&state->seqlock);
}
-static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *deleg_stateid, int open_flags)
+static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
{
- open_flags &= (FMODE_READ|FMODE_WRITE);
/*
* Protect the call to nfs4_state_set_mode_locked and
* serialise the stateid update
@@ -401,20 +481,60 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta
set_bit(NFS_DELEGATED_STATE, &state->flags);
}
if (open_stateid != NULL)
- nfs_set_open_stateid_locked(state, open_stateid, open_flags);
+ nfs_set_open_stateid_locked(state, open_stateid, fmode);
write_sequnlock(&state->seqlock);
spin_lock(&state->owner->so_lock);
- update_open_stateflags(state, open_flags);
+ update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock);
}
-static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags)
+static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
+{
+ struct nfs_inode *nfsi = NFS_I(state->inode);
+ struct nfs_delegation *deleg_cur;
+ int ret = 0;
+
+ fmode &= (FMODE_READ|FMODE_WRITE);
+
+ rcu_read_lock();
+ deleg_cur = rcu_dereference(nfsi->delegation);
+ if (deleg_cur == NULL)
+ goto no_delegation;
+
+ spin_lock(&deleg_cur->lock);
+ if (nfsi->delegation != deleg_cur ||
+ (deleg_cur->type & fmode) != fmode)
+ goto no_delegation_unlock;
+
+ if (delegation == NULL)
+ delegation = &deleg_cur->stateid;
+ else if (memcmp(deleg_cur->stateid.data, delegation->data, NFS4_STATEID_SIZE) != 0)
+ goto no_delegation_unlock;
+
+ nfs_mark_delegation_referenced(deleg_cur);
+ __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
+ ret = 1;
+no_delegation_unlock:
+ spin_unlock(&deleg_cur->lock);
+no_delegation:
+ rcu_read_unlock();
+
+ if (!ret && open_stateid != NULL) {
+ __update_open_stateid(state, open_stateid, NULL, fmode);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+
+static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
{
struct nfs_delegation *delegation;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
- if (delegation == NULL || (delegation->type & open_flags) == open_flags) {
+ if (delegation == NULL || (delegation->type & fmode) == fmode) {
rcu_read_unlock();
return;
}
@@ -427,27 +547,28 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
struct nfs4_state *state = opendata->state;
struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_delegation *delegation;
- int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL);
+ int open_mode = opendata->o_arg.open_flags & O_EXCL;
+ fmode_t fmode = opendata->o_arg.fmode;
nfs4_stateid stateid;
int ret = -EAGAIN;
- rcu_read_lock();
- delegation = rcu_dereference(nfsi->delegation);
for (;;) {
- if (can_open_cached(state, open_mode)) {
+ if (can_open_cached(state, fmode, open_mode)) {
spin_lock(&state->owner->so_lock);
- if (can_open_cached(state, open_mode)) {
- update_open_stateflags(state, open_mode);
+ if (can_open_cached(state, fmode, open_mode)) {
+ update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock);
- rcu_read_unlock();
goto out_return_state;
}
spin_unlock(&state->owner->so_lock);
}
- if (delegation == NULL)
- break;
- if (!can_open_delegated(delegation, open_mode))
+ rcu_read_lock();
+ delegation = rcu_dereference(nfsi->delegation);
+ if (delegation == NULL ||
+ !can_open_delegated(delegation, fmode)) {
+ rcu_read_unlock();
break;
+ }
/* Save the delegation */
memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
rcu_read_unlock();
@@ -455,19 +576,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
if (ret != 0)
goto out;
ret = -EAGAIN;
- rcu_read_lock();
- delegation = rcu_dereference(nfsi->delegation);
- /* If no delegation, try a cached open */
- if (delegation == NULL)
- continue;
- /* Is the delegation still valid? */
- if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0)
- continue;
- rcu_read_unlock();
- update_open_stateid(state, NULL, &stateid, open_mode);
- goto out_return_state;
+
+ /* Try to update the stateid using the delegation */
+ if (update_open_stateid(state, NULL, &stateid, fmode))
+ goto out_return_state;
}
- rcu_read_unlock();
out:
return ERR_PTR(ret);
out_return_state:
@@ -480,7 +593,6 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
struct inode *inode;
struct nfs4_state *state = NULL;
struct nfs_delegation *delegation;
- nfs4_stateid *deleg_stateid = NULL;
int ret;
if (!data->rpc_done) {
@@ -507,7 +619,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
if (delegation)
delegation_flags = delegation->flags;
rcu_read_unlock();
- if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM))
+ if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
nfs_inode_set_delegation(state->inode,
data->owner->so_cred,
&data->o_res);
@@ -516,12 +628,9 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
data->owner->so_cred,
&data->o_res);
}
- rcu_read_lock();
- delegation = rcu_dereference(NFS_I(inode)->delegation);
- if (delegation != NULL)
- deleg_stateid = &delegation->stateid;
- update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags);
- rcu_read_unlock();
+
+ update_open_stateid(state, &data->o_res.stateid, NULL,
+ data->o_arg.fmode);
iput(inode);
out:
return state;
@@ -552,7 +661,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
{
struct nfs4_opendata *opendata;
- opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
+ opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL);
if (opendata == NULL)
return ERR_PTR(-ENOMEM);
opendata->state = state;
@@ -560,12 +669,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
return opendata;
}
-static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res)
+static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
{
struct nfs4_state *newstate;
int ret;
- opendata->o_arg.open_flags = openflags;
+ opendata->o_arg.open_flags = 0;
+ opendata->o_arg.fmode = fmode;
memset(&opendata->o_res, 0, sizeof(opendata->o_res));
memset(&opendata->c_res, 0, sizeof(opendata->c_res));
nfs4_init_opendata_res(opendata);
@@ -575,7 +685,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openf
newstate = nfs4_opendata_to_nfs4_state(opendata);
if (IS_ERR(newstate))
return PTR_ERR(newstate);
- nfs4_close_state(&opendata->path, newstate, openflags);
+ nfs4_close_state(&opendata->path, newstate, fmode);
*res = newstate;
return 0;
}
@@ -631,7 +741,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
{
struct nfs_delegation *delegation;
struct nfs4_opendata *opendata;
- int delegation_type = 0;
+ fmode_t delegation_type = 0;
int status;
opendata = nfs4_open_recoverdata_alloc(ctx, state);
@@ -641,7 +751,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
opendata->o_arg.fh = NFS_FH(state->inode);
rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
- if (delegation != NULL && (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) != 0)
+ if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
delegation_type = delegation->type;
rcu_read_unlock();
opendata->o_arg.u.delegation_type = delegation_type;
@@ -744,7 +854,7 @@ static void nfs4_open_confirm_release(void *calldata)
goto out_free;
state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state))
- nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+ nfs4_close_state(&data->path, state, data->o_arg.fmode);
out_free:
nfs4_opendata_put(data);
}
@@ -808,12 +918,12 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
if (data->state != NULL) {
struct nfs_delegation *delegation;
- if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL)))
+ if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
goto out_no_action;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
if (delegation != NULL &&
- (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) {
+ test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) {
rcu_read_unlock();
goto out_no_action;
}
@@ -877,7 +987,7 @@ static void nfs4_open_release(void *calldata)
goto out_free;
state = nfs4_opendata_to_nfs4_state(data);
if (!IS_ERR(state))
- nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+ nfs4_close_state(&data->path, state, data->o_arg.fmode);
out_free:
nfs4_opendata_put(data);
}
@@ -955,10 +1065,11 @@ static int nfs4_recover_expired_lease(struct nfs_server *server)
int ret;
for (;;) {
- ret = nfs4_wait_clnt_recover(server->client, clp);
+ ret = nfs4_wait_clnt_recover(clp);
if (ret != 0)
return ret;
- if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+ if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
+ !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
break;
nfs4_schedule_state_recovery(clp);
}
@@ -993,8 +1104,9 @@ static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4
do {
err = _nfs4_open_expired(ctx, state);
- if (err == -NFS4ERR_DELAY)
- nfs4_handle_exception(server, err, &exception);
+ if (err != -NFS4ERR_DELAY)
+ break;
+ nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
@@ -1031,12 +1143,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
/*
* Returns a referenced nfs4_state
*/
-static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir);
- struct nfs_client *clp = server->nfs_client;
struct nfs4_opendata *opendata;
int status;
@@ -1050,12 +1161,11 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
if (status != 0)
goto err_put_state_owner;
if (path->dentry->d_inode != NULL)
- nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
- down_read(&clp->cl_sem);
+ nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
status = -ENOMEM;
- opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
+ opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr);
if (opendata == NULL)
- goto err_release_rwsem;
+ goto err_put_state_owner;
if (path->dentry->d_inode != NULL)
opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
@@ -1073,13 +1183,10 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
goto err_opendata_put;
nfs4_opendata_put(opendata);
nfs4_put_state_owner(sp);
- up_read(&clp->cl_sem);
*res = state;
return 0;
err_opendata_put:
nfs4_opendata_put(opendata);
-err_release_rwsem:
- up_read(&clp->cl_sem);
err_put_state_owner:
nfs4_put_state_owner(sp);
out_err:
@@ -1088,14 +1195,14 @@ out_err:
}
-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;
do {
- status = _nfs4_do_open(dir, path, flags, sattr, cred, &res);
+ status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
@@ -1230,10 +1337,13 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
renew_lease(server, calldata->timestamp);
break;
case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_OLD_STATEID:
+ case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_EXPIRED:
- break;
+ if (calldata->arg.fmode == 0)
+ break;
default:
- if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
rpc_restart_call(task);
return;
}
@@ -1272,10 +1382,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
nfs_fattr_init(calldata->res.fattr);
if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
- calldata->arg.open_flags = FMODE_READ;
+ calldata->arg.fmode = FMODE_READ;
} else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
- calldata->arg.open_flags = FMODE_WRITE;
+ calldata->arg.fmode = FMODE_WRITE;
}
calldata->timestamp = jiffies;
rpc_call_start(task);
@@ -1328,6 +1438,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
if (calldata->arg.seqid == NULL)
goto out_free_calldata;
+ calldata->arg.fmode = 0;
calldata->arg.bitmask = server->attr_bitmask;
calldata->res.fattr = &calldata->fattr;
calldata->res.seqid = calldata->arg.seqid;
@@ -1354,13 +1465,13 @@ out:
return status;
}
-static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state)
+static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
{
struct file *filp;
int ret;
/* If the open_intent is for execute, we have an extra check to make */
- if (nd->intent.open.flags & FMODE_EXEC) {
+ if (fmode & FMODE_EXEC) {
ret = nfs_may_open(state->inode,
state->owner->so_cred,
nd->intent.open.flags);
@@ -1376,7 +1487,7 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct
}
ret = PTR_ERR(filp);
out_close:
- nfs4_close_sync(path, state, nd->intent.open.flags);
+ nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
return ret;
}
@@ -1392,6 +1503,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
struct rpc_cred *cred;
struct nfs4_state *state;
struct dentry *res;
+ fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
if (nd->flags & LOOKUP_CREATE) {
attr.ia_mode = nd->intent.open.create_mode;
@@ -1409,7 +1521,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
parent = dentry->d_parent;
/* Protect against concurrent sillydeletes */
nfs_block_sillyrename(parent);
- state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
+ state = nfs4_do_open(dir, &path, fmode, nd->intent.open.flags, &attr, cred);
put_rpccred(cred);
if (IS_ERR(state)) {
if (PTR_ERR(state) == -ENOENT) {
@@ -1424,7 +1536,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
path.dentry = res;
nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
nfs_unblock_sillyrename(parent);
- nfs4_intent_set_file(nd, &path, state);
+ nfs4_intent_set_file(nd, &path, state, fmode);
return res;
}
@@ -1437,11 +1549,12 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
};
struct rpc_cred *cred;
struct nfs4_state *state;
+ fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
cred = rpc_lookup_cred();
if (IS_ERR(cred))
return PTR_ERR(cred);
- state = nfs4_do_open(dir, &path, openflags, NULL, cred);
+ state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
put_rpccred(cred);
if (IS_ERR(state)) {
switch (PTR_ERR(state)) {
@@ -1458,10 +1571,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
}
if (state->inode == dentry->d_inode) {
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
- nfs4_intent_set_file(nd, &path, state);
+ nfs4_intent_set_file(nd, &path, state, fmode);
return 1;
}
- nfs4_close_sync(&path, state, openflags);
+ nfs4_close_sync(&path, state, fmode);
out_drop:
d_drop(dentry);
return 0;
@@ -1887,6 +2000,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
};
struct nfs4_state *state;
struct rpc_cred *cred;
+ fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
int status = 0;
cred = rpc_lookup_cred();
@@ -1894,7 +2008,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
status = PTR_ERR(cred);
goto out;
}
- state = nfs4_do_open(dir, &path, flags, sattr, cred);
+ state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
d_drop(dentry);
if (IS_ERR(state)) {
status = PTR_ERR(state);
@@ -1910,9 +2024,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
nfs_post_op_update_inode(state->inode, &fattr);
}
if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
- status = nfs4_intent_set_file(nd, &path, state);
+ status = nfs4_intent_set_file(nd, &path, state, fmode);
else
- nfs4_close_sync(&path, state, flags);
+ nfs4_close_sync(&path, state, fmode);
out_putcred:
put_rpccred(cred);
out:
@@ -1974,7 +2088,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
{
struct nfs_removeres *res = task->tk_msg.rpc_resp;
- if (nfs4_async_handle_error(task, res->server) == -EAGAIN)
+ if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
return 0;
update_changeattr(dir, &res->cinfo);
nfs_post_op_update_inode(dir, &res->dir_attr);
@@ -2402,7 +2516,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
{
struct nfs_server *server = NFS_SERVER(data->inode);
- if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
rpc_restart_call(task);
return -EAGAIN;
}
@@ -2423,7 +2537,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
{
struct inode *inode = data->inode;
- if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
rpc_restart_call(task);
return -EAGAIN;
}
@@ -2449,7 +2563,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
{
struct inode *inode = data->inode;
- if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
rpc_restart_call(task);
return -EAGAIN;
}
@@ -2742,19 +2856,25 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
}
static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
{
struct nfs_client *clp = server->nfs_client;
if (!clp || task->tk_status >= 0)
return 0;
switch(task->tk_status) {
+ case -NFS4ERR_ADMIN_REVOKED:
+ case -NFS4ERR_BAD_STATEID:
+ case -NFS4ERR_OPENMODE:
+ if (state == NULL)
+ break;
+ nfs4_state_mark_reclaim_nograce(clp, state);
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
nfs4_schedule_state_recovery(clp);
- if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
+ if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
task->tk_status = 0;
return -EAGAIN;
@@ -2772,79 +2892,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
return 0;
}
-static int nfs4_wait_bit_killable(void *word)
-{
- if (fatal_signal_pending(current))
- return -ERESTARTSYS;
- schedule();
- return 0;
-}
-
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
-{
- int res;
-
- might_sleep();
-
- rwsem_acquire(&clp->cl_sem.dep_map, 0, 0, _RET_IP_);
-
- res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
- nfs4_wait_bit_killable, TASK_KILLABLE);
-
- rwsem_release(&clp->cl_sem.dep_map, 1, _RET_IP_);
- return res;
-}
-
-static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
-{
- int res = 0;
-
- might_sleep();
-
- if (*timeout <= 0)
- *timeout = NFS4_POLL_RETRY_MIN;
- if (*timeout > NFS4_POLL_RETRY_MAX)
- *timeout = NFS4_POLL_RETRY_MAX;
- schedule_timeout_killable(*timeout);
- if (fatal_signal_pending(current))
- res = -ERESTARTSYS;
- *timeout <<= 1;
- return res;
-}
-
-/* This is the error handling routine for processes that are allowed
- * to sleep.
- */
-static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
-{
- struct nfs_client *clp = server->nfs_client;
- int ret = errorcode;
-
- exception->retry = 0;
- switch(errorcode) {
- case 0:
- return 0;
- case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_STALE_STATEID:
- case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(clp);
- ret = nfs4_wait_clnt_recover(server->client, clp);
- if (ret == 0)
- exception->retry = 1;
- break;
- case -NFS4ERR_FILE_OPEN:
- case -NFS4ERR_GRACE:
- case -NFS4ERR_DELAY:
- ret = nfs4_delay(server->client, &exception->timeout);
- if (ret != 0)
- break;
- case -NFS4ERR_OLD_STATEID:
- exception->retry = 1;
- }
- /* We failed to handle the error */
- return nfs4_map_errors(ret);
-}
-
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
{
nfs4_verifier sc_verifier;
@@ -2916,7 +2963,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = now;
- clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
spin_unlock(&clp->cl_lock);
}
return status;
@@ -3074,7 +3120,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
struct nfs4_lock_state *lsp;
int status;
- down_read(&clp->cl_sem);
arg.lock_owner.clientid = clp->cl_clientid;
status = nfs4_set_lock_state(state, request);
if (status != 0)
@@ -3091,7 +3136,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
}
request->fl_ops->fl_release_private(request);
out:
- up_read(&clp->cl_sem);
return status;
}
@@ -3181,11 +3225,13 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
sizeof(calldata->lsp->ls_stateid.data));
renew_lease(calldata->server, calldata->timestamp);
break;
+ case -NFS4ERR_BAD_STATEID:
+ case -NFS4ERR_OLD_STATEID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
break;
default:
- if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN)
+ if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
rpc_restart_call(task);
}
}
@@ -3248,6 +3294,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
+ struct nfs_inode *nfsi = NFS_I(state->inode);
struct nfs_seqid *seqid;
struct nfs4_lock_state *lsp;
struct rpc_task *task;
@@ -3257,8 +3304,12 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
status = nfs4_set_lock_state(state, request);
/* Unlock _before_ we do the RPC call */
request->fl_flags |= FL_EXISTS;
- if (do_vfs_lock(request->fl_file, request) == -ENOENT)
+ down_read(&nfsi->rwsem);
+ if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
+ up_read(&nfsi->rwsem);
goto out;
+ }
+ up_read(&nfsi->rwsem);
if (status != 0)
goto out;
/* Is this a delegated lock? */
@@ -3484,7 +3535,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
- struct nfs_client *clp = state->owner->so_client;
+ struct nfs_inode *nfsi = NFS_I(state->inode);
unsigned char fl_flags = request->fl_flags;
int status;
@@ -3496,19 +3547,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
status = do_vfs_lock(request->fl_file, request);
if (status < 0)
goto out;
- down_read(&clp->cl_sem);
+ down_read(&nfsi->rwsem);
if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
- struct nfs_inode *nfsi = NFS_I(state->inode);
/* Yes: cache locks! */
- down_read(&nfsi->rwsem);
/* ...but avoid races with delegation recall... */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
- request->fl_flags = fl_flags & ~FL_SLEEP;
- status = do_vfs_lock(request->fl_file, request);
- up_read(&nfsi->rwsem);
- goto out_unlock;
- }
- up_read(&nfsi->rwsem);
+ request->fl_flags = fl_flags & ~FL_SLEEP;
+ status = do_vfs_lock(request->fl_file, request);
+ goto out_unlock;
}
status = _nfs4_do_setlk(state, cmd, request, 0);
if (status != 0)
@@ -3518,7 +3563,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
if (do_vfs_lock(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
out_unlock:
- up_read(&clp->cl_sem);
+ up_read(&nfsi->rwsem);
out:
request->fl_flags = fl_flags;
return status;
@@ -3664,11 +3709,15 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
}
struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
+ .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
+ .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
.recover_open = nfs4_open_reclaim,
.recover_lock = nfs4_lock_reclaim,
};
-struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = {
+struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = {
+ .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
+ .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
.recover_open = nfs4_open_expired,
.recover_lock = nfs4_lock_expired,
};
diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c
index 3305acbbe2ae..f524e932ff7b 100644
--- a/fs/nfs/nfs4renewd.c
+++ b/fs/nfs/nfs4renewd.c
@@ -65,7 +65,6 @@ nfs4_renew_state(struct work_struct *work)
long lease, timeout;
unsigned long last, now;
- down_read(&clp->cl_sem);
dprintk("%s: start\n", __func__);
/* Are there any active superblocks? */
if (list_empty(&clp->cl_superblocks))
@@ -77,17 +76,19 @@ nfs4_renew_state(struct work_struct *work)
timeout = (2 * lease) / 3 + (long)last - (long)now;
/* Are we close to a lease timeout? */
if (time_after(now, last + lease/3)) {
- cred = nfs4_get_renew_cred(clp);
+ cred = nfs4_get_renew_cred_locked(clp);
+ spin_unlock(&clp->cl_lock);
if (cred == NULL) {
- set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
- spin_unlock(&clp->cl_lock);
+ if (list_empty(&clp->cl_delegations)) {
+ set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+ goto out;
+ }
nfs_expire_all_delegations(clp);
- goto out;
+ } else {
+ /* Queue an asynchronous RENEW. */
+ nfs4_proc_async_renew(clp, cred);
+ put_rpccred(cred);
}
- spin_unlock(&clp->cl_lock);
- /* Queue an asynchronous RENEW. */
- nfs4_proc_async_renew(clp, cred);
- put_rpccred(cred);
timeout = (2 * lease) / 3;
spin_lock(&clp->cl_lock);
} else
@@ -100,12 +101,11 @@ nfs4_renew_state(struct work_struct *work)
cancel_delayed_work(&clp->cl_renewd);
schedule_delayed_work(&clp->cl_renewd, timeout);
spin_unlock(&clp->cl_lock);
+ nfs_expire_unreferenced_delegations(clp);
out:
- up_read(&clp->cl_sem);
dprintk("%s: done\n", __func__);
}
-/* Must be called with clp->cl_sem locked for writes */
void
nfs4_schedule_state_renewal(struct nfs_client *clp)
{
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 401ef8b28f97..2022fe47966f 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -71,14 +71,12 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
return status;
}
-static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp)
+static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
{
struct rpc_cred *cred = NULL;
- spin_lock(&clp->cl_lock);
if (clp->cl_machine_cred != NULL)
cred = get_rpccred(clp->cl_machine_cred);
- spin_unlock(&clp->cl_lock);
return cred;
}
@@ -94,7 +92,7 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp)
put_rpccred(cred);
}
-struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
+struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
@@ -110,13 +108,24 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
return cred;
}
+static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
+{
+ struct rpc_cred *cred;
+
+ spin_lock(&clp->cl_lock);
+ cred = nfs4_get_renew_cred_locked(clp);
+ spin_unlock(&clp->cl_lock);
+ return cred;
+}
+
static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
struct rpc_cred *cred;
- cred = nfs4_get_machine_cred(clp);
+ spin_lock(&clp->cl_lock);
+ cred = nfs4_get_machine_cred_locked(clp);
if (cred != NULL)
goto out;
pos = rb_first(&clp->cl_state_owners);
@@ -125,6 +134,7 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
cred = get_rpccred(sp->so_cred);
}
out:
+ spin_unlock(&clp->cl_lock);
return cred;
}
@@ -295,10 +305,6 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp)
}
}
-/*
- * Note: must be called with clp->cl_sem held in order to prevent races
- * with reboot recovery!
- */
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
{
struct nfs_client *clp = server->nfs_client;
@@ -327,10 +333,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
return sp;
}
-/*
- * Must be called with clp->cl_sem held in order to avoid races
- * with state recovery...
- */
void nfs4_put_state_owner(struct nfs4_state_owner *sp)
{
struct nfs_client *clp = sp->so_client;
@@ -361,18 +363,18 @@ nfs4_alloc_open_state(void)
}
void
-nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode)
+nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
{
- if (state->state == mode)
+ if (state->state == fmode)
return;
/* NB! List reordering - see the reclaim code for why. */
- if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
- if (mode & FMODE_WRITE)
+ if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
+ if (fmode & FMODE_WRITE)
list_move(&state->open_states, &state->owner->so_states);
else
list_move_tail(&state->open_states, &state->owner->so_states);
}
- state->state = mode;
+ state->state = fmode;
}
static struct nfs4_state *
@@ -432,10 +434,6 @@ out:
return state;
}
-/*
- * Beware! Caller must be holding exactly one
- * reference to clp->cl_sem!
- */
void nfs4_put_open_state(struct nfs4_state *state)
{
struct inode *inode = state->inode;
@@ -456,16 +454,16 @@ void nfs4_put_open_state(struct nfs4_state *state)
/*
* Close the current file.
*/
-static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait)
+static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait)
{
struct nfs4_state_owner *owner = state->owner;
int call_close = 0;
- int newstate;
+ fmode_t newstate;
atomic_inc(&owner->so_count);
/* Protect against nfs4_find_state() */
spin_lock(&owner->so_lock);
- switch (mode & (FMODE_READ | FMODE_WRITE)) {
+ switch (fmode & (FMODE_READ | FMODE_WRITE)) {
case FMODE_READ:
state->n_rdonly--;
break;
@@ -500,14 +498,14 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mod
nfs4_do_close(path, state, wait);
}
-void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode)
+void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
{
- __nfs4_close(path, state, mode, 0);
+ __nfs4_close(path, state, fmode, 0);
}
-void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode)
+void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
{
- __nfs4_close(path, state, mode, 1);
+ __nfs4_close(path, state, fmode, 1);
}
/*
@@ -568,7 +566,6 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
*
- * The caller must be holding clp->cl_sem
*/
static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{
@@ -770,32 +767,34 @@ unlock:
return status;
}
-static int reclaimer(void *);
+static int nfs4_run_state_manager(void *);
-static inline void nfs4_clear_recover_bit(struct nfs_client *clp)
+static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
{
smp_mb__before_clear_bit();
- clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state);
+ clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
smp_mb__after_clear_bit();
- wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER);
+ wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
rpc_wake_up(&clp->cl_rpcwaitq);
}
/*
- * State recovery routine
+ * Schedule the nfs_client asynchronous state management routine
*/
-static void nfs4_recover_state(struct nfs_client *clp)
+void nfs4_schedule_state_manager(struct nfs_client *clp)
{
struct task_struct *task;
+ if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
+ return;
__module_get(THIS_MODULE);
atomic_inc(&clp->cl_count);
- task = kthread_run(reclaimer, clp, "%s-reclaim",
+ task = kthread_run(nfs4_run_state_manager, clp, "%s-manager",
rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_ADDR));
if (!IS_ERR(task))
return;
- nfs4_clear_recover_bit(clp);
+ nfs4_clear_state_manager_bit(clp);
nfs_put_client(clp);
module_put(THIS_MODULE);
}
@@ -807,16 +806,42 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp)
{
if (!clp)
return;
- if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
- nfs4_recover_state(clp);
+ if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+ set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+ nfs4_schedule_state_manager(clp);
}
-static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state)
+static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
+{
+
+ set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+ /* Don't recover state that expired before the reboot */
+ if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
+ clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+ return 0;
+ }
+ set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
+ set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
+ return 1;
+}
+
+int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
+{
+ set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
+ clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+ set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
+ set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+ return 1;
+}
+
+static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
{
struct inode *inode = state->inode;
+ struct nfs_inode *nfsi = NFS_I(inode);
struct file_lock *fl;
int status = 0;
+ down_write(&nfsi->rwsem);
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
continue;
@@ -839,12 +864,14 @@ static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_s
goto out_err;
}
}
+ up_write(&nfsi->rwsem);
return 0;
out_err:
+ up_write(&nfsi->rwsem);
return status;
}
-static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp)
+static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
{
struct nfs4_state *state;
struct nfs4_lock_state *lock;
@@ -858,28 +885,34 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
* recovering after a network partition or a reboot from a
* server that doesn't support a grace period.
*/
+restart:
+ spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
+ if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
+ continue;
if (state->state == 0)
continue;
+ atomic_inc(&state->count);
+ spin_unlock(&sp->so_lock);
status = ops->recover_open(sp, state);
if (status >= 0) {
- status = nfs4_reclaim_locks(ops, state);
- if (status < 0)
- goto out_err;
- list_for_each_entry(lock, &state->lock_states, ls_locks) {
- if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
- printk("%s: Lock reclaim failed!\n",
+ status = nfs4_reclaim_locks(state, ops);
+ if (status >= 0) {
+ list_for_each_entry(lock, &state->lock_states, ls_locks) {
+ if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
+ printk("%s: Lock reclaim failed!\n",
__func__);
+ }
+ nfs4_put_open_state(state);
+ goto restart;
}
- continue;
}
switch (status) {
default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__func__, status);
case -ENOENT:
- case -NFS4ERR_RECLAIM_BAD:
- case -NFS4ERR_RECLAIM_CONFLICT:
+ case -ESTALE:
/*
* Open state on this file cannot be recovered
* All we can do is revert to using the zero stateid.
@@ -889,84 +922,176 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
/* Mark the file as being 'closed' */
state->state = 0;
break;
+ case -NFS4ERR_RECLAIM_BAD:
+ case -NFS4ERR_RECLAIM_CONFLICT:
+ nfs4_state_mark_reclaim_nograce(sp->so_client, state);
+ break;
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
+ nfs4_state_mark_reclaim_nograce(sp->so_client, state);
case -NFS4ERR_STALE_CLIENTID:
goto out_err;
}
+ nfs4_put_open_state(state);
+ goto restart;
}
+ spin_unlock(&sp->so_lock);
return 0;
out_err:
+ nfs4_put_open_state(state);
return status;
}
-static void nfs4_state_mark_reclaim(struct nfs_client *clp)
+static void nfs4_clear_open_state(struct nfs4_state *state)
+{
+ struct nfs4_lock_state *lock;
+
+ clear_bit(NFS_DELEGATED_STATE, &state->flags);
+ clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+ clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+ clear_bit(NFS_O_RDWR_STATE, &state->flags);
+ list_for_each_entry(lock, &state->lock_states, ls_locks) {
+ lock->ls_seqid.flags = 0;
+ lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
+ }
+}
+
+static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
{
struct nfs4_state_owner *sp;
struct rb_node *pos;
struct nfs4_state *state;
- struct nfs4_lock_state *lock;
/* Reset all sequence ids to zero */
for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
- sp->so_seqid.counter = 0;
sp->so_seqid.flags = 0;
spin_lock(&sp->so_lock);
list_for_each_entry(state, &sp->so_states, open_states) {
- clear_bit(NFS_DELEGATED_STATE, &state->flags);
- clear_bit(NFS_O_RDONLY_STATE, &state->flags);
- clear_bit(NFS_O_WRONLY_STATE, &state->flags);
- clear_bit(NFS_O_RDWR_STATE, &state->flags);
- list_for_each_entry(lock, &state->lock_states, ls_locks) {
- lock->ls_seqid.counter = 0;
- lock->ls_seqid.flags = 0;
- lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
- }
+ if (mark_reclaim(clp, state))
+ nfs4_clear_open_state(state);
}
spin_unlock(&sp->so_lock);
}
}
-static int reclaimer(void *ptr)
+static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
+{
+ /* Mark all delegations for reclaim */
+ nfs_delegation_mark_reclaim(clp);
+ nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
+}
+
+static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
{
- struct nfs_client *clp = ptr;
struct nfs4_state_owner *sp;
struct rb_node *pos;
- struct nfs4_state_recovery_ops *ops;
- struct rpc_cred *cred;
+ struct nfs4_state *state;
+
+ if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+ return;
+
+ for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
+ sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+ spin_lock(&sp->so_lock);
+ list_for_each_entry(state, &sp->so_states, open_states) {
+ if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
+ continue;
+ nfs4_state_mark_reclaim_nograce(clp, state);
+ }
+ spin_unlock(&sp->so_lock);
+ }
+
+ nfs_delegation_reap_unclaimed(clp);
+}
+
+static void nfs_delegation_clear_all(struct nfs_client *clp)
+{
+ nfs_delegation_mark_reclaim(clp);
+ nfs_delegation_reap_unclaimed(clp);
+}
+
+static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
+{
+ nfs_delegation_clear_all(clp);
+ nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
+}
+
+static void nfs4_state_end_reclaim_nograce(struct nfs_client *clp)
+{
+ clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+}
+
+static void nfs4_recovery_handle_error(struct nfs_client *clp, int error)
+{
+ switch (error) {
+ case -NFS4ERR_CB_PATH_DOWN:
+ nfs_handle_cb_pathdown(clp);
+ break;
+ case -NFS4ERR_STALE_CLIENTID:
+ case -NFS4ERR_LEASE_MOVED:
+ set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+ nfs4_state_start_reclaim_reboot(clp);
+ break;
+ case -NFS4ERR_EXPIRED:
+ set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+ nfs4_state_start_reclaim_nograce(clp);
+ }
+}
+
+static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
+{
+ struct rb_node *pos;
int status = 0;
- allow_signal(SIGKILL);
+restart:
+ spin_lock(&clp->cl_lock);
+ for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
+ struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+ if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags))
+ continue;
+ atomic_inc(&sp->so_count);
+ spin_unlock(&clp->cl_lock);
+ status = nfs4_reclaim_open_state(sp, ops);
+ if (status < 0) {
+ set_bit(ops->owner_flag_bit, &sp->so_flags);
+ nfs4_put_state_owner(sp);
+ nfs4_recovery_handle_error(clp, status);
+ return status;
+ }
+ nfs4_put_state_owner(sp);
+ goto restart;
+ }
+ spin_unlock(&clp->cl_lock);
+ return status;
+}
- /* Ensure exclusive access to NFSv4 state */
- down_write(&clp->cl_sem);
- /* Are there any NFS mounts out there? */
- if (list_empty(&clp->cl_superblocks))
- goto out;
-restart_loop:
- ops = &nfs4_network_partition_recovery_ops;
- /* Are there any open files on this volume? */
+static int nfs4_check_lease(struct nfs_client *clp)
+{
+ struct rpc_cred *cred;
+ int status = -NFS4ERR_EXPIRED;
+
+ /* Is the client already known to have an expired lease? */
+ if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+ return 0;
cred = nfs4_get_renew_cred(clp);
- if (cred != NULL) {
- /* Yes there are: try to renew the old lease */
- status = nfs4_proc_renew(clp, cred);
- put_rpccred(cred);
- switch (status) {
- case 0:
- case -NFS4ERR_CB_PATH_DOWN:
- goto out;
- case -NFS4ERR_STALE_CLIENTID:
- case -NFS4ERR_LEASE_MOVED:
- ops = &nfs4_reboot_recovery_ops;
- }
- } else {
- /* "reboot" to ensure we clear all state on the server */
- clp->cl_boot_time = CURRENT_TIME;
+ if (cred == NULL) {
+ cred = nfs4_get_setclientid_cred(clp);
+ if (cred == NULL)
+ goto out;
}
- /* We're going to have to re-establish a clientid */
- nfs4_state_mark_reclaim(clp);
- status = -ENOENT;
+ status = nfs4_proc_renew(clp, cred);
+ put_rpccred(cred);
+out:
+ nfs4_recovery_handle_error(clp, status);
+ return status;
+}
+
+static int nfs4_reclaim_lease(struct nfs_client *clp)
+{
+ struct rpc_cred *cred;
+ int status = -ENOENT;
+
cred = nfs4_get_setclientid_cred(clp);
if (cred != NULL) {
status = nfs4_init_client(clp, cred);
@@ -974,42 +1099,90 @@ restart_loop:
/* Handle case where the user hasn't set up machine creds */
if (status == -EACCES && cred == clp->cl_machine_cred) {
nfs4_clear_machine_cred(clp);
- goto restart_loop;
+ status = -EAGAIN;
}
}
- if (status)
- goto out_error;
- /* Mark all delegations for reclaim */
- nfs_delegation_mark_reclaim(clp);
- /* Note: list is protected by exclusive lock on cl->cl_sem */
- for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
- sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
- status = nfs4_reclaim_open_state(ops, sp);
- if (status < 0) {
- if (status == -NFS4ERR_NO_GRACE) {
- ops = &nfs4_network_partition_recovery_ops;
- status = nfs4_reclaim_open_state(ops, sp);
+ return status;
+}
+
+static void nfs4_state_manager(struct nfs_client *clp)
+{
+ int status = 0;
+
+ /* Ensure exclusive access to NFSv4 state */
+ for(;;) {
+ if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
+ /* We're going to have to re-establish a clientid */
+ status = nfs4_reclaim_lease(clp);
+ if (status) {
+ set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+ if (status == -EAGAIN)
+ continue;
+ goto out_error;
}
+ clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+ }
+
+ if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
+ status = nfs4_check_lease(clp);
+ if (status != 0)
+ continue;
+ }
+
+ /* First recover reboot state... */
+ if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
+ status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops);
if (status == -NFS4ERR_STALE_CLIENTID)
- goto restart_loop;
- if (status == -NFS4ERR_EXPIRED)
- goto restart_loop;
+ continue;
+ nfs4_state_end_reclaim_reboot(clp);
+ continue;
+ }
+
+ /* Now recover expired state... */
+ if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
+ status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops);
+ if (status < 0) {
+ set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+ if (status == -NFS4ERR_STALE_CLIENTID)
+ continue;
+ if (status == -NFS4ERR_EXPIRED)
+ continue;
+ goto out_error;
+ } else
+ nfs4_state_end_reclaim_nograce(clp);
+ continue;
}
+
+ if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
+ nfs_client_return_marked_delegations(clp);
+ continue;
+ }
+
+ nfs4_clear_state_manager_bit(clp);
+ /* Did we race with an attempt to give us more work? */
+ if (clp->cl_state == 0)
+ break;
+ if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
+ break;
}
- nfs_delegation_reap_unclaimed(clp);
-out:
- up_write(&clp->cl_sem);
- if (status == -NFS4ERR_CB_PATH_DOWN)
- nfs_handle_cb_pathdown(clp);
- nfs4_clear_recover_bit(clp);
+ return;
+out_error:
+ printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
+ " with error %d\n", clp->cl_hostname, -status);
+ if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+ nfs4_state_end_reclaim_reboot(clp);
+ nfs4_clear_state_manager_bit(clp);
+}
+
+static int nfs4_run_state_manager(void *ptr)
+{
+ struct nfs_client *clp = ptr;
+
+ allow_signal(SIGKILL);
+ nfs4_state_manager(clp);
nfs_put_client(clp);
module_put_and_exit(0);
return 0;
-out_error:
- printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s"
- " with error %d\n", clp->cl_hostname, -status);
- set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
- goto out;
}
/*
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index b916297d2334..d1e4c8f8a0a9 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -8,7 +8,7 @@
*
* Kendrick Smith <kmsmith@umich.edu>
* Andy Adamson <andros@umich.edu>
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -67,7 +67,7 @@ static int nfs4_stat_to_errno(int);
#define NFS4_MAXTAGLEN 0
#endif
-/* lock,open owner id:
+/* lock,open owner id:
* we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT >> 2)
*/
#define open_owner_id_maxsz (1 + 4)
@@ -541,6 +541,7 @@ static struct {
struct compound_hdr {
int32_t status;
uint32_t nops;
+ __be32 * nops_p;
uint32_t taglen;
char * tag;
};
@@ -578,7 +579,7 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *
xdr_encode_opaque(p, str, len);
}
-static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
__be32 *p;
@@ -588,8 +589,13 @@ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
WRITE32(hdr->taglen);
WRITEMEM(hdr->tag, hdr->taglen);
WRITE32(NFS4_MINOR_VERSION);
+ hdr->nops_p = p;
WRITE32(hdr->nops);
- return 0;
+}
+
+static void encode_nops(struct compound_hdr *hdr)
+{
+ *hdr->nops_p = htonl(hdr->nops);
}
static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
@@ -601,7 +607,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
}
-static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
{
char owner_name[IDMAP_NAMESZ];
char owner_group[IDMAP_NAMESZ];
@@ -612,7 +618,6 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
int len;
uint32_t bmval0 = 0;
uint32_t bmval1 = 0;
- int status;
/*
* We reserve enough space to write the entire attribute buffer at once.
@@ -709,7 +714,7 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
WRITE32(NFS4_SET_TO_SERVER_TIME);
}
-
+
/*
* Now we backfill the bitmap and the attribute buffer length.
*/
@@ -723,23 +728,20 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
*q++ = htonl(bmval1);
*q++ = htonl(len);
- status = 0;
/* out: */
- return status;
}
-static int encode_access(struct xdr_stream *xdr, u32 access)
+static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(8);
WRITE32(OP_ACCESS);
WRITE32(access);
-
- return 0;
+ hdr->nops++;
}
-static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
+static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
{
__be32 *p;
@@ -747,26 +749,24 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
WRITE32(OP_CLOSE);
WRITE32(arg->seqid->sequence->counter);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
-
- return 0;
+ hdr->nops++;
}
-static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args)
+static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
{
__be32 *p;
-
- RESERVE_SPACE(16);
- WRITE32(OP_COMMIT);
- WRITE64(args->offset);
- WRITE32(args->count);
- return 0;
+ RESERVE_SPACE(16);
+ WRITE32(OP_COMMIT);
+ WRITE64(args->offset);
+ WRITE32(args->count);
+ hdr->nops++;
}
-static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create)
+static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr)
{
__be32 *p;
-
+
RESERVE_SPACE(8);
WRITE32(OP_CREATE);
WRITE32(create->ftype);
@@ -791,64 +791,62 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
RESERVE_SPACE(4 + create->name->len);
WRITE32(create->name->len);
WRITEMEM(create->name->name, create->name->len);
+ hdr->nops++;
- return encode_attrs(xdr, create->attrs, create->server);
+ encode_attrs(xdr, create->attrs, create->server);
}
-static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap)
+static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
{
- __be32 *p;
+ __be32 *p;
- RESERVE_SPACE(12);
- WRITE32(OP_GETATTR);
- WRITE32(1);
- WRITE32(bitmap);
- return 0;
+ RESERVE_SPACE(12);
+ WRITE32(OP_GETATTR);
+ WRITE32(1);
+ WRITE32(bitmap);
+ hdr->nops++;
}
-static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1)
+static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
{
- __be32 *p;
+ __be32 *p;
- RESERVE_SPACE(16);
- WRITE32(OP_GETATTR);
- WRITE32(2);
- WRITE32(bm0);
- WRITE32(bm1);
- return 0;
+ RESERVE_SPACE(16);
+ WRITE32(OP_GETATTR);
+ WRITE32(2);
+ WRITE32(bm0);
+ WRITE32(bm1);
+ hdr->nops++;
}
-static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- return encode_getattr_two(xdr,
- bitmask[0] & nfs4_fattr_bitmap[0],
- bitmask[1] & nfs4_fattr_bitmap[1]);
+ encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
+ bitmask[1] & nfs4_fattr_bitmap[1], hdr);
}
-static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
- bitmask[1] & nfs4_fsinfo_bitmap[1]);
+ encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
+ bitmask[1] & nfs4_fsinfo_bitmap[1], hdr);
}
-static int encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
- return encode_getattr_two(xdr,
- bitmask[0] & nfs4_fs_locations_bitmap[0],
- bitmask[1] & nfs4_fs_locations_bitmap[1]);
+ encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0],
+ bitmask[1] & nfs4_fs_locations_bitmap[1], hdr);
}
-static int encode_getfh(struct xdr_stream *xdr)
+static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(4);
WRITE32(OP_GETFH);
-
- return 0;
+ hdr->nops++;
}
-static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{
__be32 *p;
@@ -856,8 +854,7 @@ static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_LINK);
WRITE32(name->len);
WRITEMEM(name->name, name->len);
-
- return 0;
+ hdr->nops++;
}
static inline int nfs4_lock_type(struct file_lock *fl, int block)
@@ -878,7 +875,7 @@ static inline uint64_t nfs4_lock_length(struct file_lock *fl)
* opcode,type,reclaim,offset,length,new_lock_owner = 32
* open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40
*/
-static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
+static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args, struct compound_hdr *hdr)
{
__be32 *p;
@@ -904,11 +901,10 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE);
WRITE32(args->lock_seqid->sequence->counter);
}
-
- return 0;
+ hdr->nops++;
}
-static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args)
+static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr)
{
__be32 *p;
@@ -921,11 +917,10 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg
WRITE32(16);
WRITEMEM("lock id:", 8);
WRITE64(args->lock_owner.id);
-
- return 0;
+ hdr->nops++;
}
-static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args)
+static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr)
{
__be32 *p;
@@ -936,11 +931,10 @@ static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *arg
WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE);
WRITE64(args->fl->fl_start);
WRITE64(nfs4_lock_length(args->fl));
-
- return 0;
+ hdr->nops++;
}
-static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{
int len = name->len;
__be32 *p;
@@ -949,27 +943,26 @@ static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_LOOKUP);
WRITE32(len);
WRITEMEM(name->name, len);
-
- return 0;
+ hdr->nops++;
}
-static void encode_share_access(struct xdr_stream *xdr, int open_flags)
+static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
{
__be32 *p;
RESERVE_SPACE(8);
- switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
- case FMODE_READ:
- WRITE32(NFS4_SHARE_ACCESS_READ);
- break;
- case FMODE_WRITE:
- WRITE32(NFS4_SHARE_ACCESS_WRITE);
- break;
- case FMODE_READ|FMODE_WRITE:
- WRITE32(NFS4_SHARE_ACCESS_BOTH);
- break;
- default:
- BUG();
+ switch (fmode & (FMODE_READ|FMODE_WRITE)) {
+ case FMODE_READ:
+ WRITE32(NFS4_SHARE_ACCESS_READ);
+ break;
+ case FMODE_WRITE:
+ WRITE32(NFS4_SHARE_ACCESS_WRITE);
+ break;
+ case FMODE_READ|FMODE_WRITE:
+ WRITE32(NFS4_SHARE_ACCESS_BOTH);
+ break;
+ default:
+ WRITE32(0);
}
WRITE32(0); /* for linux, share_deny = 0 always */
}
@@ -984,7 +977,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
RESERVE_SPACE(8);
WRITE32(OP_OPEN);
WRITE32(arg->seqid->sequence->counter);
- encode_share_access(xdr, arg->open_flags);
+ encode_share_access(xdr, arg->fmode);
RESERVE_SPACE(28);
WRITE64(arg->clientid);
WRITE32(16);
@@ -998,13 +991,13 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
RESERVE_SPACE(4);
switch(arg->open_flags & O_EXCL) {
- case 0:
- WRITE32(NFS4_CREATE_UNCHECKED);
- encode_attrs(xdr, arg->u.attrs, arg->server);
- break;
- default:
- WRITE32(NFS4_CREATE_EXCLUSIVE);
- encode_nfs4_verifier(xdr, &arg->u.verifier);
+ case 0:
+ WRITE32(NFS4_CREATE_UNCHECKED);
+ encode_attrs(xdr, arg->u.attrs, arg->server);
+ break;
+ default:
+ WRITE32(NFS4_CREATE_EXCLUSIVE);
+ encode_nfs4_verifier(xdr, &arg->u.verifier);
}
}
@@ -1014,33 +1007,33 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a
RESERVE_SPACE(4);
switch (arg->open_flags & O_CREAT) {
- case 0:
- WRITE32(NFS4_OPEN_NOCREATE);
- break;
- default:
- BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
- WRITE32(NFS4_OPEN_CREATE);
- encode_createmode(xdr, arg);
+ case 0:
+ WRITE32(NFS4_OPEN_NOCREATE);
+ break;
+ default:
+ BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
+ WRITE32(NFS4_OPEN_CREATE);
+ encode_createmode(xdr, arg);
}
}
-static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
+static inline void encode_delegation_type(struct xdr_stream *xdr, fmode_t delegation_type)
{
__be32 *p;
RESERVE_SPACE(4);
switch (delegation_type) {
- case 0:
- WRITE32(NFS4_OPEN_DELEGATE_NONE);
- break;
- case FMODE_READ:
- WRITE32(NFS4_OPEN_DELEGATE_READ);
- break;
- case FMODE_WRITE|FMODE_READ:
- WRITE32(NFS4_OPEN_DELEGATE_WRITE);
- break;
- default:
- BUG();
+ case 0:
+ WRITE32(NFS4_OPEN_DELEGATE_NONE);
+ break;
+ case FMODE_READ:
+ WRITE32(NFS4_OPEN_DELEGATE_READ);
+ break;
+ case FMODE_WRITE|FMODE_READ:
+ WRITE32(NFS4_OPEN_DELEGATE_WRITE);
+ break;
+ default:
+ BUG();
}
}
@@ -1053,7 +1046,7 @@ static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *
encode_string(xdr, name->len, name->name);
}
-static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
+static inline void encode_claim_previous(struct xdr_stream *xdr, fmode_t type)
{
__be32 *p;
@@ -1072,27 +1065,27 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
encode_string(xdr, name->len, name->name);
}
-static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
+static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
{
encode_openhdr(xdr, arg);
encode_opentype(xdr, arg);
switch (arg->claim) {
- case NFS4_OPEN_CLAIM_NULL:
- encode_claim_null(xdr, arg->name);
- break;
- case NFS4_OPEN_CLAIM_PREVIOUS:
- encode_claim_previous(xdr, arg->u.delegation_type);
- break;
- case NFS4_OPEN_CLAIM_DELEGATE_CUR:
- encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
- break;
- default:
- BUG();
+ case NFS4_OPEN_CLAIM_NULL:
+ encode_claim_null(xdr, arg->name);
+ break;
+ case NFS4_OPEN_CLAIM_PREVIOUS:
+ encode_claim_previous(xdr, arg->u.delegation_type);
+ break;
+ case NFS4_OPEN_CLAIM_DELEGATE_CUR:
+ encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
+ break;
+ default:
+ BUG();
}
- return 0;
+ hdr->nops++;
}
-static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
+static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1100,11 +1093,10 @@ static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_con
WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
WRITE32(arg->seqid->sequence->counter);
-
- return 0;
+ hdr->nops++;
}
-static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
+static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1112,12 +1104,12 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
WRITE32(OP_OPEN_DOWNGRADE);
WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
WRITE32(arg->seqid->sequence->counter);
- encode_share_access(xdr, arg->open_flags);
- return 0;
+ encode_share_access(xdr, arg->fmode);
+ hdr->nops++;
}
-static int
-encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+static void
+encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hdr *hdr)
{
int len = fh->size;
__be32 *p;
@@ -1126,18 +1118,16 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh)
WRITE32(OP_PUTFH);
WRITE32(len);
WRITEMEM(fh->data, len);
-
- return 0;
+ hdr->nops++;
}
-static int encode_putrootfh(struct xdr_stream *xdr)
+static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
- __be32 *p;
-
- RESERVE_SPACE(4);
- WRITE32(OP_PUTROOTFH);
+ __be32 *p;
- return 0;
+ RESERVE_SPACE(4);
+ WRITE32(OP_PUTROOTFH);
+ hdr->nops++;
}
static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
@@ -1153,7 +1143,7 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context
WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE);
}
-static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
+static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1165,11 +1155,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
RESERVE_SPACE(12);
WRITE64(args->offset);
WRITE32(args->count);
-
- return 0;
+ hdr->nops++;
}
-static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req)
+static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
{
uint32_t attrs[2] = {
FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
@@ -1191,6 +1180,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
WRITE32(attrs[0] & readdir->bitmask[0]);
WRITE32(attrs[1] & readdir->bitmask[1]);
+ hdr->nops++;
dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
__func__,
(unsigned long long)readdir->cookie,
@@ -1198,21 +1188,18 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
((u32 *)readdir->verifier.data)[1],
attrs[0] & readdir->bitmask[0],
attrs[1] & readdir->bitmask[1]);
-
- return 0;
}
-static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req)
+static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(4);
WRITE32(OP_READLINK);
-
- return 0;
+ hdr->nops++;
}
-static int encode_remove(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1220,11 +1207,10 @@ static int encode_remove(struct xdr_stream *xdr, const struct qstr *name)
WRITE32(OP_REMOVE);
WRITE32(name->len);
WRITEMEM(name->name, name->len);
-
- return 0;
+ hdr->nops++;
}
-static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname)
+static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1232,38 +1218,35 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con
WRITE32(OP_RENAME);
WRITE32(oldname->len);
WRITEMEM(oldname->name, oldname->len);
-
+
RESERVE_SPACE(4 + newname->len);
WRITE32(newname->len);
WRITEMEM(newname->name, newname->len);
-
- return 0;
+ hdr->nops++;
}
-static int encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid)
+static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(12);
WRITE32(OP_RENEW);
WRITE64(client_stateid->cl_clientid);
-
- return 0;
+ hdr->nops++;
}
-static int
-encode_restorefh(struct xdr_stream *xdr)
+static void
+encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(4);
WRITE32(OP_RESTOREFH);
-
- return 0;
+ hdr->nops++;
}
static int
-encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
+encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1278,36 +1261,32 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
RESERVE_SPACE(4);
WRITE32(arg->acl_len);
xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
+ hdr->nops++;
return 0;
}
-static int
-encode_savefh(struct xdr_stream *xdr)
+static void
+encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
__be32 *p;
RESERVE_SPACE(4);
WRITE32(OP_SAVEFH);
-
- return 0;
+ hdr->nops++;
}
-static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server)
+static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr)
{
- int status;
__be32 *p;
-
- RESERVE_SPACE(4+NFS4_STATEID_SIZE);
- WRITE32(OP_SETATTR);
- WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
- if ((status = encode_attrs(xdr, arg->iap, server)))
- return status;
-
- return 0;
+ RESERVE_SPACE(4+NFS4_STATEID_SIZE);
+ WRITE32(OP_SETATTR);
+ WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
+ hdr->nops++;
+ encode_attrs(xdr, arg->iap, server);
}
-static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid)
+static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1322,23 +1301,21 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien
encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
RESERVE_SPACE(4);
WRITE32(setclientid->sc_cb_ident);
-
- return 0;
+ hdr->nops++;
}
-static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state)
+static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr)
{
- __be32 *p;
-
- RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE);
- WRITE32(OP_SETCLIENTID_CONFIRM);
- WRITE64(client_state->cl_clientid);
- WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
+ __be32 *p;
- return 0;
+ RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE);
+ WRITE32(OP_SETCLIENTID_CONFIRM);
+ WRITE64(client_state->cl_clientid);
+ WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
+ hdr->nops++;
}
-static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args)
+static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1353,11 +1330,10 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args
WRITE32(args->count);
xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
-
- return 0;
+ hdr->nops++;
}
-static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid)
+static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr)
{
__be32 *p;
@@ -1365,8 +1341,7 @@ static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *statei
WRITE32(OP_DELEGRETURN);
WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
- return 0;
-
+ hdr->nops++;
}
/*
* END OF "GENERIC" ENCODE ROUTINES.
@@ -1379,21 +1354,16 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status != 0)
- goto out;
- status = encode_access(&xdr, args->access);
- if (status != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_access(&xdr, args->access, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1403,21 +1373,17 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 4,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
- goto out;
- if ((status = encode_lookup(&xdr, args->name)) != 0)
- goto out;
- if ((status = encode_getfh(&xdr)) != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->dir_fh, &hdr);
+ encode_lookup(&xdr, args->name, &hdr);
+ encode_getfh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1427,18 +1393,16 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putrootfh(&xdr)) != 0)
- goto out;
- if ((status = encode_getfh(&xdr)) == 0)
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putrootfh(&xdr, &hdr);
+ encode_getfh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1448,19 +1412,16 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->fh)) != 0)
- goto out;
- if ((status = encode_remove(&xdr, &args->name)) != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_remove(&xdr, &args->name, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1470,27 +1431,20 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 7,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->old_dir)) != 0)
- goto out;
- if ((status = encode_savefh(&xdr)) != 0)
- goto out;
- if ((status = encode_putfh(&xdr, args->new_dir)) != 0)
- goto out;
- if ((status = encode_rename(&xdr, args->old_name, args->new_name)) != 0)
- goto out;
- if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
- goto out;
- if ((status = encode_restorefh(&xdr)) != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->old_dir, &hdr);
+ encode_savefh(&xdr, &hdr);
+ encode_putfh(&xdr, args->new_dir, &hdr);
+ encode_rename(&xdr, args->old_name, args->new_name, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_restorefh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1500,27 +1454,20 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 7,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->fh)) != 0)
- goto out;
- if ((status = encode_savefh(&xdr)) != 0)
- goto out;
- if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
- goto out;
- if ((status = encode_link(&xdr, args->name)) != 0)
- goto out;
- if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
- goto out;
- if ((status = encode_restorefh(&xdr)) != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_savefh(&xdr, &hdr);
+ encode_putfh(&xdr, args->dir_fh, &hdr);
+ encode_link(&xdr, args->name, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_restorefh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1530,27 +1477,20 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 7,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
- goto out;
- if ((status = encode_savefh(&xdr)) != 0)
- goto out;
- if ((status = encode_create(&xdr, args)) != 0)
- goto out;
- if ((status = encode_getfh(&xdr)) != 0)
- goto out;
- if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
- goto out;
- if ((status = encode_restorefh(&xdr)) != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->dir_fh, &hdr);
+ encode_savefh(&xdr, &hdr);
+ encode_create(&xdr, args, &hdr);
+ encode_getfh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_restorefh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1568,15 +1508,15 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->fh)) == 0)
- status = encode_getfattr(&xdr, args->bitmask);
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1584,23 +1524,18 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
*/
static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closeargs *args)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr = {
- .nops = 3,
- };
- int status;
-
- xdr_init_encode(&xdr, &req->rq_snd_buf, p);
- encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_close(&xdr, args);
- if (status != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr = {
+ .nops = 0,
+ };
+
+ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+ encode_compound_hdr(&xdr, &hdr);
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_close(&xdr, args, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1610,33 +1545,20 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 7,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_savefh(&xdr);
- if (status)
- goto out;
- status = encode_open(&xdr, args);
- if (status)
- goto out;
- status = encode_getfh(&xdr);
- if (status)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
- if (status)
- goto out;
- status = encode_restorefh(&xdr);
- if (status)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_savefh(&xdr, &hdr);
+ encode_open(&xdr, args, &hdr);
+ encode_getfh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_restorefh(&xdr, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1646,18 +1568,15 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_open_confirm(&xdr, args);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_open_confirm(&xdr, args, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1667,21 +1586,16 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_open(&xdr, args);
- if (status)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_open(&xdr, args, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1691,21 +1605,16 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_open_downgrade(&xdr, args);
- if (status != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_open_downgrade(&xdr, args, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1715,18 +1624,15 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_lock(&xdr, args);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_lock(&xdr, args, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1736,18 +1642,15 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_lockt(&xdr, args);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_lockt(&xdr, args, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1757,18 +1660,15 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_locku(&xdr, args);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_locku(&xdr, args, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1778,18 +1678,15 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
unsigned int replen;
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_readlink(&xdr, args, req);
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_readlink(&xdr, args, req, &hdr);
/* set up reply kvec
* toplevel_status + taglen + rescount + OP_PUTFH + status
@@ -1798,9 +1695,8 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages,
args->pgbase, args->pglen);
-
-out:
- return status;
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1810,18 +1706,15 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
int replen;
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_readdir(&xdr, args, req);
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_readdir(&xdr, args, req, &hdr);
/* set up reply kvec
* toplevel_status + taglen + rescount + OP_PUTFH + status
@@ -1833,9 +1726,8 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
__func__, replen, args->pages,
args->pgbase, args->count);
-
-out:
- return status;
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1846,18 +1738,14 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int replen, status;
+ int replen;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_read(&xdr, args);
- if (status)
- goto out;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_read(&xdr, args, &hdr);
/* set up reply kvec
* toplevel status + taglen=0 + rescount + OP_PUTFH + status
@@ -1867,33 +1755,27 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
xdr_inline_pages(&req->rq_rcv_buf, replen,
args->pages, args->pgbase, args->count);
req->rq_rcv_buf.flags |= XDRBUF_READ;
-out:
- return status;
+ encode_nops(&hdr);
+ return 0;
}
/*
* Encode an SETATTR request
*/
static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args)
-
{
- struct xdr_stream xdr;
- struct compound_hdr hdr = {
- .nops = 3,
- };
- int status;
-
- xdr_init_encode(&xdr, &req->rq_snd_buf, p);
- encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if(status)
- goto out;
- status = encode_setattr(&xdr, args, args->server);
- if(status)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr = {
+ .nops = 0,
+ };
+
+ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+ encode_compound_hdr(&xdr, &hdr);
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_setattr(&xdr, args, args->server, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1906,22 +1788,21 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
struct xdr_stream xdr;
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int replen, status;
+ int replen;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
+
/* set up reply buffer: */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen,
args->acl_pages, args->acl_pgbase, args->acl_len);
-out:
- return status;
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1931,22 +1812,17 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_write(&xdr, args);
- if (status)
- goto out;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_write(&xdr, args, &hdr);
req->rq_snd_buf.flags |= XDRBUF_WRITE;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1956,21 +1832,16 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_commit(&xdr, args);
- if (status)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_commit(&xdr, args, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1980,16 +1851,15 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (!status)
- status = encode_fsinfo(&xdr, args->bitmask);
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_fsinfo(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -1999,17 +1869,16 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (!status)
- status = encode_getattr_one(&xdr,
- args->bitmask[0] & nfs4_pathconf_bitmap[0]);
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
+ &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2019,18 +1888,16 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status == 0)
- status = encode_getattr_two(&xdr,
- args->bitmask[0] & nfs4_statfs_bitmap[0],
- args->bitmask[1] & nfs4_statfs_bitmap[1]);
- return status;
+ encode_putfh(&xdr, args->fh, &hdr);
+ encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
+ args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2040,19 +1907,18 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struc
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 2,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, fhandle);
- if (status == 0)
- status = encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
- FATTR4_WORD0_LINK_SUPPORT|
- FATTR4_WORD0_SYMLINK_SUPPORT|
- FATTR4_WORD0_ACLSUPPORT);
- return status;
+ encode_putfh(&xdr, fhandle, &hdr);
+ encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
+ FATTR4_WORD0_LINK_SUPPORT|
+ FATTR4_WORD0_SYMLINK_SUPPORT|
+ FATTR4_WORD0_ACLSUPPORT, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2062,12 +1928,14 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 1,
+ .nops = 0,
};
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- return encode_renew(&xdr, clp);
+ encode_renew(&xdr, clp, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2077,12 +1945,14 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 1,
+ .nops = 0,
};
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- return encode_setclientid(&xdr, sc);
+ encode_setclientid(&xdr, sc, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2092,19 +1962,17 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_setclientid_confirm(&xdr, clp);
- if (!status)
- status = encode_putrootfh(&xdr);
- if (!status)
- status = encode_fsinfo(&xdr, lease_bitmap);
- return status;
+ encode_setclientid_confirm(&xdr, clp, &hdr);
+ encode_putrootfh(&xdr, &hdr);
+ encode_fsinfo(&xdr, lease_bitmap, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2114,21 +1982,16 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fhandle);
- if (status != 0)
- goto out;
- status = encode_delegreturn(&xdr, args->stateid);
- if (status != 0)
- goto out;
- status = encode_getfattr(&xdr, args->bitmask);
-out:
- return status;
+ encode_putfh(&xdr, args->fhandle, &hdr);
+ encode_delegreturn(&xdr, args->stateid, &hdr);
+ encode_getfattr(&xdr, args->bitmask, &hdr);
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2138,20 +2001,17 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
- .nops = 3,
+ .nops = 0,
};
struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
int replen;
- int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
- if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
- goto out;
- if ((status = encode_lookup(&xdr, args->name)) != 0)
- goto out;
- if ((status = encode_fs_locations(&xdr, args->bitmask)) != 0)
- goto out;
+ encode_putfh(&xdr, args->dir_fh, &hdr);
+ encode_lookup(&xdr, args->name, &hdr);
+ encode_fs_locations(&xdr, args->bitmask, &hdr);
+
/* set up reply
* toplevel_status + OP_PUTFH + status
* + OP_LOOKUP + status + OP_GETATTR + status = 7
@@ -2159,8 +2019,8 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2;
xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page,
0, PAGE_SIZE);
-out:
- return status;
+ encode_nops(&hdr);
+ return 0;
}
/*
@@ -2217,11 +2077,13 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
READ_BUF(8);
READ32(hdr->status);
READ32(hdr->taglen);
-
+
READ_BUF(hdr->taglen + 4);
hdr->tag = (char *)p;
p += XDR_QUADLEN(hdr->taglen);
READ32(hdr->nops);
+ if (unlikely(hdr->nops < 1))
+ return nfs4_stat_to_errno(hdr->status);
return 0;
}
@@ -3047,8 +2909,7 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
{
__be32 *savep;
- uint32_t attrlen,
- bitmap[2] = {0};
+ uint32_t attrlen, bitmap[2] = {0};
int status;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
@@ -3070,14 +2931,13 @@ xdr_error:
dprintk("%s: xdr returned %d!\n", __func__, -status);
return status;
}
-
+
static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
{
__be32 *savep;
- uint32_t attrlen,
- bitmap[2] = {0};
+ uint32_t attrlen, bitmap[2] = {0};
int status;
-
+
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto xdr_error;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
@@ -3107,10 +2967,9 @@ xdr_error:
static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
{
__be32 *savep;
- uint32_t attrlen,
- bitmap[2] = {0};
+ uint32_t attrlen, bitmap[2] = {0};
int status;
-
+
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto xdr_error;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
@@ -3256,7 +3115,7 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
{
int status;
-
+
status = decode_op_hdr(xdr, OP_LINK);
if (status)
return status;
@@ -3344,27 +3203,27 @@ static int decode_lookup(struct xdr_stream *xdr)
/* This is too sick! */
static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
{
- __be32 *p;
+ __be32 *p;
uint32_t limit_type, nblocks, blocksize;
READ_BUF(12);
READ32(limit_type);
switch (limit_type) {
- case 1:
- READ64(*maxsize);
- break;
- case 2:
- READ32(nblocks);
- READ32(blocksize);
- *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
+ case 1:
+ READ64(*maxsize);
+ break;
+ case 2:
+ READ32(nblocks);
+ READ32(blocksize);
+ *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
}
return 0;
}
static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
{
- __be32 *p;
- uint32_t delegation_type;
+ __be32 *p;
+ uint32_t delegation_type;
READ_BUF(4);
READ32(delegation_type);
@@ -3375,13 +3234,14 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
READ_BUF(NFS4_STATEID_SIZE+4);
COPYMEM(res->delegation.data, NFS4_STATEID_SIZE);
READ32(res->do_recall);
+
switch (delegation_type) {
- case NFS4_OPEN_DELEGATE_READ:
- res->delegation_type = FMODE_READ;
- break;
- case NFS4_OPEN_DELEGATE_WRITE:
- res->delegation_type = FMODE_WRITE|FMODE_READ;
- if (decode_space_limit(xdr, &res->maxsize) < 0)
+ case NFS4_OPEN_DELEGATE_READ:
+ res->delegation_type = FMODE_READ;
+ break;
+ case NFS4_OPEN_DELEGATE_WRITE:
+ res->delegation_type = FMODE_WRITE|FMODE_READ;
+ if (decode_space_limit(xdr, &res->maxsize) < 0)
return -EIO;
}
return decode_ace(xdr, NULL, res->server->nfs_client);
@@ -3389,27 +3249,27 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
{
- __be32 *p;
+ __be32 *p;
uint32_t savewords, bmlen, i;
- int status;
+ int status;
- status = decode_op_hdr(xdr, OP_OPEN);
+ status = decode_op_hdr(xdr, OP_OPEN);
if (status != -EIO)
nfs_increment_open_seqid(status, res->seqid);
- if (status)
- return status;
- READ_BUF(NFS4_STATEID_SIZE);
- COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
+ if (status)
+ return status;
+ READ_BUF(NFS4_STATEID_SIZE);
+ COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
- decode_change_info(xdr, &res->cinfo);
+ decode_change_info(xdr, &res->cinfo);
- READ_BUF(8);
- READ32(res->rflags);
- READ32(bmlen);
- if (bmlen > 10)
- goto xdr_error;
+ READ_BUF(8);
+ READ32(res->rflags);
+ READ32(bmlen);
+ if (bmlen > 10)
+ goto xdr_error;
- READ_BUF(bmlen << 2);
+ READ_BUF(bmlen << 2);
savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
for (i = 0; i < savewords; ++i)
READ32(res->attrset[i]);
@@ -3424,17 +3284,17 @@ xdr_error:
static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
{
- __be32 *p;
+ __be32 *p;
int status;
- status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
+ status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
if (status != -EIO)
nfs_increment_open_seqid(status, res->seqid);
- if (status)
- return status;
- READ_BUF(NFS4_STATEID_SIZE);
- COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
- return 0;
+ if (status)
+ return status;
+ READ_BUF(NFS4_STATEID_SIZE);
+ COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
+ return 0;
}
static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res)
@@ -3562,7 +3422,7 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
dprintk("NFS: readdir reply truncated!\n");
entry[1] = 1;
}
-out:
+out:
kunmap_atomic(kaddr, KM_USER0);
return 0;
short_pkt:
@@ -3718,7 +3578,6 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
uint32_t bmlen;
int status;
-
status = decode_op_hdr(xdr, OP_SETATTR);
if (status)
return status;
@@ -3738,7 +3597,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
READ32(opnum);
if (opnum != OP_SETCLIENTID) {
dprintk("nfs: decode_setclientid: Server returned operation"
- " %d\n", opnum);
+ " %d\n", opnum);
return -EIO;
}
READ32(nfserr);
@@ -3792,34 +3651,34 @@ static int decode_delegreturn(struct xdr_stream *xdr)
}
/*
+ * END OF "GENERIC" DECODE ROUTINES.
+ */
+
+/*
* Decode OPEN_DOWNGRADE response
*/
static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_open_downgrade(&xdr, res);
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_open_downgrade(&xdr, res);
if (status != 0)
goto out;
decode_getfattr(&xdr, res->fattr, res->server);
out:
- return status;
+ return status;
}
/*
- * END OF "GENERIC" DECODE ROUTINES.
- */
-
-/*
* Decode ACCESS response
*/
static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_accessres *res)
@@ -3827,7 +3686,7 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_ac
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3850,7 +3709,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lo
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3873,7 +3732,7 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nf
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3893,7 +3752,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3914,7 +3773,7 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3944,7 +3803,7 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -3977,7 +3836,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_cr
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
@@ -4014,7 +3873,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
-
+
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status)
@@ -4025,7 +3884,6 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
status = decode_getfattr(&xdr, res->fattr, res->server);
out:
return status;
-
}
/*
@@ -4034,21 +3892,20 @@ out:
static int
nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr = {
- .nops = 2,
- };
- int status;
-
- xdr_init_encode(&xdr, &req->rq_snd_buf, p);
- encode_compound_hdr(&xdr, &hdr);
- status = encode_putfh(&xdr, args->fh);
- if (status)
- goto out;
- status = encode_setacl(&xdr, args);
-out:
- return status;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr = {
+ .nops = 0,
+ };
+ int status;
+
+ xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+ encode_compound_hdr(&xdr, &hdr);
+ encode_putfh(&xdr, args->fh, &hdr);
+ status = encode_setacl(&xdr, args, &hdr);
+ encode_nops(&hdr);
+ return status;
}
+
/*
* Decode SETACL response
*/
@@ -4099,18 +3956,18 @@ out:
*/
static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_close(&xdr, res);
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_close(&xdr, res);
if (status != 0)
goto out;
/*
@@ -4121,7 +3978,7 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos
*/
decode_getfattr(&xdr, res->fattr, res->server);
out:
- return status;
+ return status;
}
/*
@@ -4129,23 +3986,23 @@ out:
*/
static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_savefh(&xdr);
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_savefh(&xdr);
+ if (status)
+ goto out;
+ status = decode_open(&xdr, res);
if (status)
goto out;
- status = decode_open(&xdr, res);
- if (status)
- goto out;
if (decode_getfh(&xdr, &res->fh) != 0)
goto out;
if (decode_getfattr(&xdr, res->f_attr, res->server) != 0)
@@ -4154,7 +4011,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr
goto out;
decode_getfattr(&xdr, res->dir_attr, res->server);
out:
- return status;
+ return status;
}
/*
@@ -4162,20 +4019,20 @@ out:
*/
static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, __be32 *p, struct nfs_open_confirmres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_open_confirm(&xdr, res);
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_open_confirm(&xdr, res);
out:
- return status;
+ return status;
}
/*
@@ -4183,23 +4040,23 @@ out:
*/
static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_open(&xdr, res);
- if (status)
- goto out;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_open(&xdr, res);
+ if (status)
+ goto out;
decode_getfattr(&xdr, res->f_attr, res->server);
out:
- return status;
+ return status;
}
/*
@@ -4207,25 +4064,25 @@ out:
*/
static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_setattrres *res)
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_setattr(&xdr, res);
- if (status)
- goto out;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
+ if (status)
+ goto out;
+ status = decode_putfh(&xdr);
+ if (status)
+ goto out;
+ status = decode_setattr(&xdr, res);
+ if (status)
+ goto out;
status = decode_getfattr(&xdr, res->fattr, res->server);
if (status == NFS4ERR_DELAY)
status = 0;
out:
- return status;
+ return status;
}
/*
@@ -4421,8 +4278,6 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf
status = decode_putfh(&xdr);
if (!status)
status = decode_fsinfo(&xdr, fsinfo);
- if (!status)
- status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4511,8 +4366,6 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p,
status = decode_compound_hdr(&xdr, &hdr);
if (!status)
status = decode_setclientid(&xdr, clp);
- if (!status)
- status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4533,8 +4386,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
status = decode_putrootfh(&xdr);
if (!status)
status = decode_fsinfo(&xdr, fsinfo);
- if (!status)
- status = nfs4_stat_to_errno(hdr.status);
return status;
}
@@ -4715,7 +4566,7 @@ nfs4_stat_to_errno(int stat)
.p_replen = NFS4_##restype##_sz, \
.p_statidx = NFSPROC4_CLNT_##proc, \
.p_name = #proc, \
- }
+}
struct rpc_procinfo nfs4_procedures[] = {
PROC(READ, enc_read, dec_read),
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 8478fc25daee..d9ef602fbc5a 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -86,6 +86,8 @@
#include <net/ipconfig.h>
#include <linux/parser.h>
+#include "internal.h"
+
/* Define this to allow debugging output */
#undef NFSROOT_DEBUG
#define NFSDBG_FACILITY NFSDBG_ROOT
@@ -100,7 +102,7 @@ static char nfs_root_name[256] __initdata = "";
static __be32 servaddr __initdata = 0;
/* Name of directory to mount */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
+static char nfs_export_path[NFS_MAXPATHLEN] __initdata = { 0, };
/* NFS-related data */
static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
@@ -312,7 +314,7 @@ static int __init root_nfs_name(char *name)
printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
return -1;
}
- sprintf(nfs_path, buf, cp);
+ sprintf(nfs_export_path, buf, cp);
return 1;
}
@@ -329,7 +331,7 @@ static int __init root_nfs_addr(void)
}
snprintf(nfs_data.hostname, sizeof(nfs_data.hostname),
- "%u.%u.%u.%u", NIPQUAD(servaddr));
+ "%pI4", &servaddr);
return 0;
}
@@ -340,7 +342,7 @@ static int __init root_nfs_addr(void)
static void __init root_nfs_print(void)
{
printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
- nfs_path, nfs_data.hostname);
+ nfs_export_path, nfs_data.hostname);
printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
@@ -421,8 +423,8 @@ static int __init root_nfs_getport(int program, int version, int proto)
{
struct sockaddr_in sin;
- printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
- program, version, NIPQUAD(servaddr));
+ printk(KERN_NOTICE "Looking up port of RPC %d/%d on %pI4\n",
+ program, version, &servaddr);
set_sockaddr(&sin, servaddr, 0);
return rpcb_getport_sync(&sin, program, version, proto);
}
@@ -485,18 +487,23 @@ static int __init root_nfs_get_handle(void)
{
struct nfs_fh fh;
struct sockaddr_in sin;
+ struct nfs_mount_request request = {
+ .sap = (struct sockaddr *)&sin,
+ .salen = sizeof(sin),
+ .dirpath = nfs_export_path,
+ .version = (nfs_data.flags & NFS_MOUNT_VER3) ?
+ NFS_MNT3_VERSION : NFS_MNT_VERSION,
+ .protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
+ XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP,
+ .fh = &fh,
+ };
int status;
- int protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
- XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP;
- int version = (nfs_data.flags & NFS_MOUNT_VER3) ?
- NFS_MNT3_VERSION : NFS_MNT_VERSION;
set_sockaddr(&sin, servaddr, htons(mount_port));
- status = nfs_mount((struct sockaddr *) &sin, sizeof(sin), NULL,
- nfs_path, version, protocol, &fh);
+ status = nfs_mount(&request);
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
- "while mounting %s\n", status, nfs_path);
+ "while mounting %s\n", status, nfs_export_path);
else {
nfs_data.root.size = fh.size;
memcpy(nfs_data.root.data, fh.data, fh.size);
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 40d17987d0e8..f856004bb7fa 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -533,12 +533,6 @@ readpage_async_filler(void *data, struct page *page)
unsigned int len;
int error;
- error = nfs_wb_page(inode, page);
- if (error)
- goto out_unlock;
- if (PageUptodate(page))
- goto out_unlock;
-
len = nfs_page_length(page);
if (len == 0)
return nfs_return_empty_page(page);
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index f48db679a1c6..d6686f4786dc 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -75,6 +75,7 @@ enum {
Opt_acl, Opt_noacl,
Opt_rdirplus, Opt_nordirplus,
Opt_sharecache, Opt_nosharecache,
+ Opt_resvport, Opt_noresvport,
/* Mount options that take integer arguments */
Opt_port,
@@ -129,6 +130,8 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_nordirplus, "nordirplus" },
{ Opt_sharecache, "sharecache" },
{ Opt_nosharecache, "nosharecache" },
+ { Opt_resvport, "resvport" },
+ { Opt_noresvport, "noresvport" },
{ Opt_port, "port=%u" },
{ Opt_rsize, "rsize=%u" },
@@ -462,14 +465,12 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss,
switch (sap->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
- seq_printf(m, ",mountaddr=" NIPQUAD_FMT,
- NIPQUAD(sin->sin_addr.s_addr));
+ seq_printf(m, ",mountaddr=%pI4", &sin->sin_addr.s_addr);
break;
}
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
- seq_printf(m, ",mountaddr=" NIP6_FMT,
- NIP6(sin6->sin6_addr));
+ seq_printf(m, ",mountaddr=%pI6", &sin6->sin6_addr);
break;
}
default:
@@ -514,7 +515,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
- { NFS_MOUNT_UNSHARED, ",nosharecache", ""},
+ { NFS_MOUNT_UNSHARED, ",nosharecache", "" },
+ { NFS_MOUNT_NORESVPORT, ",noresvport", "" },
{ 0, NULL, NULL }
};
const struct proc_nfs_info *nfs_infop;
@@ -1035,6 +1037,12 @@ static int nfs_parse_mount_options(char *raw,
case Opt_nosharecache:
mnt->flags |= NFS_MOUNT_UNSHARED;
break;
+ case Opt_resvport:
+ mnt->flags &= ~NFS_MOUNT_NORESVPORT;
+ break;
+ case Opt_noresvport:
+ mnt->flags |= NFS_MOUNT_NORESVPORT;
+ break;
/*
* options that take numeric values
@@ -1329,8 +1337,14 @@ out_security_failure:
static int nfs_try_mount(struct nfs_parsed_mount_data *args,
struct nfs_fh *root_fh)
{
- struct sockaddr *sap = (struct sockaddr *)&args->mount_server.address;
- char *hostname;
+ struct nfs_mount_request request = {
+ .sap = (struct sockaddr *)
+ &args->mount_server.address,
+ .dirpath = args->nfs_server.export_path,
+ .protocol = args->mount_server.protocol,
+ .fh = root_fh,
+ .noresvport = args->flags & NFS_MOUNT_NORESVPORT,
+ };
int status;
if (args->mount_server.version == 0) {
@@ -1339,42 +1353,38 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
else
args->mount_server.version = NFS_MNT_VERSION;
}
+ request.version = args->mount_server.version;
if (args->mount_server.hostname)
- hostname = args->mount_server.hostname;
+ request.hostname = args->mount_server.hostname;
else
- hostname = args->nfs_server.hostname;
+ request.hostname = args->nfs_server.hostname;
/*
* Construct the mount server's address.
*/
if (args->mount_server.address.ss_family == AF_UNSPEC) {
- memcpy(sap, &args->nfs_server.address,
+ memcpy(request.sap, &args->nfs_server.address,
args->nfs_server.addrlen);
args->mount_server.addrlen = args->nfs_server.addrlen;
}
+ request.salen = args->mount_server.addrlen;
/*
* autobind will be used if mount_server.port == 0
*/
- nfs_set_port(sap, args->mount_server.port);
+ nfs_set_port(request.sap, args->mount_server.port);
/*
* Now ask the mount server to map our export path
* to a file handle.
*/
- status = nfs_mount(sap,
- args->mount_server.addrlen,
- hostname,
- args->nfs_server.export_path,
- args->mount_server.version,
- args->mount_server.protocol,
- root_fh);
+ status = nfs_mount(&request);
if (status == 0)
return 0;
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
- hostname, status);
+ request.hostname, status);
return status;
}
@@ -2421,7 +2431,7 @@ static void nfs4_kill_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
- nfs_return_all_delegations(sb);
+ nfs_super_return_all_delegations(sb);
kill_anon_super(sb);
nfs4_renewd_prepare_shutdown(server);
diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c
index c11f5375d7c1..04133aacb1e5 100644
--- a/fs/nfs_common/nfsacl.c
+++ b/fs/nfs_common/nfsacl.c
@@ -29,8 +29,8 @@
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(nfsacl_encode);
-EXPORT_SYMBOL(nfsacl_decode);
+EXPORT_SYMBOL_GPL(nfsacl_encode);
+EXPORT_SYMBOL_GPL(nfsacl_decode);
struct nfsacl_encode_desc {
struct xdr_array2_desc desc;
diff --git a/fs/nfsctl.c b/fs/nfsctl.c
index aed8145d9087..b1acbd6ab6fb 100644
--- a/fs/nfsctl.c
+++ b/fs/nfsctl.c
@@ -10,6 +10,8 @@
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/syscall.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
#include <linux/linkage.h>
#include <linux/namei.h>
#include <linux/mount.h>
@@ -41,7 +43,8 @@ static struct file *do_open(char *name, int flags)
error = may_open(&nd, MAY_WRITE, FMODE_WRITE);
if (!error)
- return dentry_open(nd.path.dentry, nd.path.mnt, flags);
+ return dentry_open(nd.path.dentry, nd.path.mnt, flags,
+ current_cred());
path_put(&nd.path);
return ERR_PTR(error);
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 294992e9bf69..0184fe9b514c 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -27,53 +27,70 @@ int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
{
- struct svc_cred cred = rqstp->rq_cred;
+ struct group_info *rqgi;
+ struct group_info *gi;
+ struct cred *new;
int i;
int flags = nfsexp_flags(rqstp, exp);
int ret;
+ /* discard any old override before preparing the new set */
+ revert_creds(get_cred(current->real_cred));
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ new->fsuid = rqstp->rq_cred.cr_uid;
+ new->fsgid = rqstp->rq_cred.cr_gid;
+
+ rqgi = rqstp->rq_cred.cr_group_info;
+
if (flags & NFSEXP_ALLSQUASH) {
- cred.cr_uid = exp->ex_anon_uid;
- cred.cr_gid = exp->ex_anon_gid;
- cred.cr_group_info = groups_alloc(0);
+ new->fsuid = exp->ex_anon_uid;
+ new->fsgid = exp->ex_anon_gid;
+ gi = groups_alloc(0);
} else if (flags & NFSEXP_ROOTSQUASH) {
- struct group_info *gi;
- if (!cred.cr_uid)
- cred.cr_uid = exp->ex_anon_uid;
- if (!cred.cr_gid)
- cred.cr_gid = exp->ex_anon_gid;
- gi = groups_alloc(cred.cr_group_info->ngroups);
- if (gi)
- for (i = 0; i < cred.cr_group_info->ngroups; i++) {
- if (!GROUP_AT(cred.cr_group_info, i))
- GROUP_AT(gi, i) = exp->ex_anon_gid;
- else
- GROUP_AT(gi, i) = GROUP_AT(cred.cr_group_info, i);
- }
- cred.cr_group_info = gi;
- } else
- get_group_info(cred.cr_group_info);
-
- if (cred.cr_uid != (uid_t) -1)
- current->fsuid = cred.cr_uid;
- else
- current->fsuid = exp->ex_anon_uid;
- if (cred.cr_gid != (gid_t) -1)
- current->fsgid = cred.cr_gid;
- else
- current->fsgid = exp->ex_anon_gid;
+ if (!new->fsuid)
+ new->fsuid = exp->ex_anon_uid;
+ if (!new->fsgid)
+ new->fsgid = exp->ex_anon_gid;
- if (!cred.cr_group_info)
- return -ENOMEM;
- ret = set_current_groups(cred.cr_group_info);
- put_group_info(cred.cr_group_info);
- if ((cred.cr_uid)) {
- current->cap_effective =
- cap_drop_nfsd_set(current->cap_effective);
+ gi = groups_alloc(rqgi->ngroups);
+ if (!gi)
+ goto oom;
+
+ for (i = 0; i < rqgi->ngroups; i++) {
+ if (!GROUP_AT(rqgi, i))
+ GROUP_AT(gi, i) = exp->ex_anon_gid;
+ else
+ GROUP_AT(gi, i) = GROUP_AT(rqgi, i);
+ }
} else {
- current->cap_effective =
- cap_raise_nfsd_set(current->cap_effective,
- current->cap_permitted);
+ gi = get_group_info(rqgi);
}
+
+ if (new->fsuid == (uid_t) -1)
+ new->fsuid = exp->ex_anon_uid;
+ if (new->fsgid == (gid_t) -1)
+ new->fsgid = exp->ex_anon_gid;
+
+ ret = set_groups(new, gi);
+ put_group_info(gi);
+ if (!ret)
+ goto error;
+
+ if (new->uid)
+ new->cap_effective = cap_drop_nfsd_set(new->cap_effective);
+ else
+ new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
+ new->cap_permitted);
+ put_cred(override_creds(new));
+ return 0;
+
+oom:
+ ret = -ENOMEM;
+error:
+ abort_creds(new);
return ret;
}
+
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 094747a1227c..6d7d8c02c197 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -358,6 +358,7 @@ static struct rpc_program cb_program = {
.nrvers = ARRAY_SIZE(nfs_cb_version),
.version = nfs_cb_version,
.stats = &cb_stats,
+ .pipe_dir_name = "/nfsd4_cb",
};
/* Reference counting, callback cleanup, etc., all look racy as heck.
@@ -382,8 +383,9 @@ static int do_probe_callback(void *data)
.program = &cb_program,
.prognumber = cb->cb_prog,
.version = nfs_cb_version[1]->number,
- .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
+ .authflavor = clp->cl_flavor,
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
+ .client_name = clp->cl_principal,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
@@ -392,6 +394,11 @@ static int do_probe_callback(void *data)
struct rpc_clnt *client;
int status;
+ if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) {
+ status = nfserr_cb_path_down;
+ goto out_err;
+ }
+
/* Initialize address */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index b79ec930d9f1..0f9d6efaa62b 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -54,20 +54,26 @@
static struct path rec_dir;
static int rec_dir_init = 0;
-static void
-nfs4_save_user(uid_t *saveuid, gid_t *savegid)
+static int
+nfs4_save_creds(const struct cred **original_creds)
{
- *saveuid = current->fsuid;
- *savegid = current->fsgid;
- current->fsuid = 0;
- current->fsgid = 0;
+ struct cred *new;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ new->fsuid = 0;
+ new->fsgid = 0;
+ *original_creds = override_creds(new);
+ put_cred(new);
+ return 0;
}
static void
-nfs4_reset_user(uid_t saveuid, gid_t savegid)
+nfs4_reset_creds(const struct cred *original)
{
- current->fsuid = saveuid;
- current->fsgid = savegid;
+ revert_creds(original);
}
static void
@@ -129,10 +135,9 @@ nfsd4_sync_rec_dir(void)
int
nfsd4_create_clid_dir(struct nfs4_client *clp)
{
+ const struct cred *original_cred;
char *dname = clp->cl_recdir;
struct dentry *dentry;
- uid_t uid;
- gid_t gid;
int status;
dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
@@ -140,7 +145,9 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
if (!rec_dir_init || clp->cl_firststate)
return 0;
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ return status;
/* lock the parent */
mutex_lock(&rec_dir.dentry->d_inode->i_mutex);
@@ -168,7 +175,7 @@ out_unlock:
clp->cl_firststate = 1;
nfsd4_sync_rec_dir();
}
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
return status;
}
@@ -211,26 +218,29 @@ nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
static int
nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
{
+ const struct cred *original_cred;
struct file *filp;
struct dentry_list_arg dla = {
.parent = dir,
};
struct list_head *dentries = &dla.dentries;
struct dentry_list *child;
- uid_t uid;
- gid_t gid;
int status;
if (!rec_dir_init)
return 0;
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ return status;
INIT_LIST_HEAD(dentries);
- filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY);
+ filp = dentry_open(dget(dir), mntget(rec_dir.mnt), O_RDONLY,
+ current_cred());
status = PTR_ERR(filp);
if (IS_ERR(filp))
goto out;
+ INIT_LIST_HEAD(dentries);
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
fput(filp);
while (!list_empty(dentries)) {
@@ -249,7 +259,7 @@ out:
dput(child->dentry);
kfree(child);
}
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
return status;
}
@@ -311,8 +321,7 @@ out:
void
nfsd4_remove_clid_dir(struct nfs4_client *clp)
{
- uid_t uid;
- gid_t gid;
+ const struct cred *original_cred;
int status;
if (!rec_dir_init || !clp->cl_firststate)
@@ -322,9 +331,13 @@ nfsd4_remove_clid_dir(struct nfs4_client *clp)
if (status)
goto out;
clp->cl_firststate = 0;
- nfs4_save_user(&uid, &gid);
+
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0)
+ goto out;
+
status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
if (status == 0)
nfsd4_sync_rec_dir();
mnt_drop_write(rec_dir.mnt);
@@ -401,16 +414,21 @@ nfsd4_recdir_load(void) {
void
nfsd4_init_recdir(char *rec_dirname)
{
- uid_t uid = 0;
- gid_t gid = 0;
- int status;
+ const struct cred *original_cred;
+ int status;
printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
rec_dirname);
BUG_ON(rec_dir_init);
- nfs4_save_user(&uid, &gid);
+ status = nfs4_save_creds(&original_cred);
+ if (status < 0) {
+ printk("NFSD: Unable to change credentials to find recovery"
+ " directory: error %d\n",
+ status);
+ return;
+ }
status = kern_path(rec_dirname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&rec_dir);
@@ -420,7 +438,7 @@ nfsd4_init_recdir(char *rec_dirname)
if (!status)
rec_dir_init = 1;
- nfs4_reset_user(uid, gid);
+ nfs4_reset_creds(original_cred);
}
void
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1a052ac2bde9..13e0e074dbb8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -54,6 +54,7 @@
#include <linux/mutex.h>
#include <linux/lockd/bind.h>
#include <linux/module.h>
+#include <linux/sunrpc/svcauth_gss.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -377,6 +378,7 @@ free_client(struct nfs4_client *clp)
shutdown_callback_client(clp);
if (clp->cl_cred.cr_group_info)
put_group_info(clp->cl_cred.cr_group_info);
+ kfree(clp->cl_principal);
kfree(clp->cl_name.data);
kfree(clp);
}
@@ -696,6 +698,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
unsigned int strhashval;
struct nfs4_client *conf, *unconf, *new;
__be32 status;
+ char *princ;
char dname[HEXDIR_LEN];
if (!check_name(clname))
@@ -719,8 +722,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_clid_inuse;
if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)
|| conf->cl_addr != sin->sin_addr.s_addr) {
- dprintk("NFSD: setclientid: string in use by client"
- "at %u.%u.%u.%u\n", NIPQUAD(conf->cl_addr));
+ dprintk("NFSD: setclientid: string in use by clientat %pI4\n",
+ &conf->cl_addr);
goto out;
}
}
@@ -783,6 +786,15 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
copy_verf(new, &clverifier);
new->cl_addr = sin->sin_addr.s_addr;
+ new->cl_flavor = rqstp->rq_flavor;
+ princ = svc_gss_principal(rqstp);
+ if (princ) {
+ new->cl_principal = kstrdup(princ, GFP_KERNEL);
+ if (new->cl_principal == NULL) {
+ free_client(new);
+ goto out;
+ }
+ }
copy_cred(&new->cl_cred, &rqstp->rq_cred);
gen_confirm(new);
gen_callback(new, setclid);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index e3f9783fdcf7..77d7b8c531a6 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -330,7 +330,7 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size)
return -EINVAL;
/* get ipv4 address */
- if (sscanf(fo_path, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) != 4)
+ if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
return -EINVAL;
if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
return -EINVAL;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index cd25d91895a1..f0da7d9c3a92 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -186,9 +186,14 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
* access control settings being in effect, we cannot
* fix that case easily.
*/
- current->cap_effective =
- cap_raise_nfsd_set(current->cap_effective,
- current->cap_permitted);
+ struct cred *new = prepare_creds();
+ if (!new)
+ return nfserrno(-ENOMEM);
+ new->cap_effective =
+ cap_raise_nfsd_set(new->cap_effective,
+ new->cap_permitted);
+ put_cred(override_creds(new));
+ put_cred(new);
} else {
error = nfsd_setuser_and_check_port(rqstp, exp);
if (error)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 4433c8f00163..d1c5f787b365 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -671,6 +671,7 @@ __be32
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
int access, struct file **filp)
{
+ const struct cred *cred = current_cred();
struct dentry *dentry;
struct inode *inode;
int flags = O_RDONLY|O_LARGEFILE;
@@ -725,7 +726,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
DQUOT_INIT(inode);
}
*filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_path.mnt),
- flags);
+ flags, cred);
if (IS_ERR(*filp))
host_err = PTR_ERR(*filp);
out_nfserr:
@@ -1169,7 +1170,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp,
* send along the gid on create when it tries to implement
* setgid directories via NFS:
*/
- if (current->fsuid != 0)
+ if (current_fsuid() != 0)
iap->ia_valid &= ~(ATTR_UID|ATTR_GID);
if (iap->ia_valid)
return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
@@ -2001,7 +2002,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
IS_APPEND(inode)? " append" : "",
__mnt_is_readonly(exp->ex_path.mnt)? " ro" : "");
dprintk(" owner %d/%d user %d/%d\n",
- inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
+ inode->i_uid, inode->i_gid, current_fsuid(), current_fsgid());
#endif
/* Normally we reject any write/sattr etc access on a read-only file
@@ -2044,7 +2045,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
* with NFSv3.
*/
if ((acc & NFSD_MAY_OWNER_OVERRIDE) &&
- inode->i_uid == current->fsuid)
+ inode->i_uid == current_fsuid())
return 0;
/* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
diff --git a/fs/ocfs2/cluster/netdebug.c b/fs/ocfs2/cluster/netdebug.c
index 52276c02f710..f8424874fa07 100644
--- a/fs/ocfs2/cluster/netdebug.c
+++ b/fs/ocfs2/cluster/netdebug.c
@@ -304,8 +304,8 @@ static int sc_seq_show(struct seq_file *seq, void *v)
* use of it here generates a warning with -Wbitwise */
seq_printf(seq, "%p:\n"
" krefs: %d\n"
- " sock: %u.%u.%u.%u:%u -> "
- "%u.%u.%u.%u:%u\n"
+ " sock: %pI4:%u -> "
+ "%pI4:%u\n"
" remote node: %s\n"
" page off: %zu\n"
" handshake ok: %u\n"
@@ -319,8 +319,8 @@ static int sc_seq_show(struct seq_file *seq, void *v)
" func type: %u\n",
sc,
atomic_read(&sc->sc_kref.refcount),
- NIPQUAD(saddr), inet ? ntohs(sport) : 0,
- NIPQUAD(daddr), inet ? ntohs(dport) : 0,
+ &saddr, inet ? ntohs(sport) : 0,
+ &daddr, inet ? ntohs(dport) : 0,
sc->sc_node->nd_name,
sc->sc_page_off,
sc->sc_handshake_ok,
diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c
index 816a3f61330c..70e8fa9e2539 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -250,7 +250,7 @@ static ssize_t o2nm_node_ipv4_port_write(struct o2nm_node *node,
static ssize_t o2nm_node_ipv4_address_read(struct o2nm_node *node, char *page)
{
- return sprintf(page, "%u.%u.%u.%u\n", NIPQUAD(node->nd_ipv4_address));
+ return sprintf(page, "%pI4\n", &node->nd_ipv4_address);
}
static ssize_t o2nm_node_ipv4_address_write(struct o2nm_node *node,
diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c
index 2bcf706d9dd3..9fbe849f6344 100644
--- a/fs/ocfs2/cluster/tcp.c
+++ b/fs/ocfs2/cluster/tcp.c
@@ -1597,8 +1597,8 @@ static void o2net_start_connect(struct work_struct *work)
ret = sock->ops->bind(sock, (struct sockaddr *)&myaddr,
sizeof(myaddr));
if (ret) {
- mlog(ML_ERROR, "bind failed with %d at address %u.%u.%u.%u\n",
- ret, NIPQUAD(mynode->nd_ipv4_address));
+ mlog(ML_ERROR, "bind failed with %d at address %pI4\n",
+ ret, &mynode->nd_ipv4_address);
goto out;
}
@@ -1790,17 +1790,16 @@ static int o2net_accept_one(struct socket *sock)
node = o2nm_get_node_by_ip(sin.sin_addr.s_addr);
if (node == NULL) {
- mlog(ML_NOTICE, "attempt to connect from unknown node at "
- "%u.%u.%u.%u:%d\n", NIPQUAD(sin.sin_addr.s_addr),
- ntohs(sin.sin_port));
+ mlog(ML_NOTICE, "attempt to connect from unknown node at %pI4:%d\n",
+ &sin.sin_addr.s_addr, ntohs(sin.sin_port));
ret = -EINVAL;
goto out;
}
if (o2nm_this_node() > node->nd_num) {
mlog(ML_NOTICE, "unexpected connect attempted from a lower "
- "numbered node '%s' at " "%u.%u.%u.%u:%d with num %u\n",
- node->nd_name, NIPQUAD(sin.sin_addr.s_addr),
+ "numbered node '%s' at " "%pI4:%d with num %u\n",
+ node->nd_name, &sin.sin_addr.s_addr,
ntohs(sin.sin_port), node->nd_num);
ret = -EINVAL;
goto out;
@@ -1810,8 +1809,8 @@ static int o2net_accept_one(struct socket *sock)
* and tries to connect before we see their heartbeat */
if (!o2hb_check_node_heartbeating_from_callback(node->nd_num)) {
mlog(ML_CONN, "attempt to connect from node '%s' at "
- "%u.%u.%u.%u:%d but it isn't heartbeating\n",
- node->nd_name, NIPQUAD(sin.sin_addr.s_addr),
+ "%pI4:%d but it isn't heartbeating\n",
+ node->nd_name, &sin.sin_addr.s_addr,
ntohs(sin.sin_port));
ret = -EINVAL;
goto out;
@@ -1827,8 +1826,8 @@ static int o2net_accept_one(struct socket *sock)
spin_unlock(&nn->nn_lock);
if (ret) {
mlog(ML_NOTICE, "attempt to connect from node '%s' at "
- "%u.%u.%u.%u:%d but it already has an open connection\n",
- node->nd_name, NIPQUAD(sin.sin_addr.s_addr),
+ "%pI4:%d but it already has an open connection\n",
+ node->nd_name, &sin.sin_addr.s_addr,
ntohs(sin.sin_port));
goto out;
}
@@ -1924,15 +1923,15 @@ static int o2net_open_listening_sock(__be32 addr, __be16 port)
sock->sk->sk_reuse = 1;
ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin));
if (ret < 0) {
- mlog(ML_ERROR, "unable to bind socket at %u.%u.%u.%u:%u, "
- "ret=%d\n", NIPQUAD(addr), ntohs(port), ret);
+ mlog(ML_ERROR, "unable to bind socket at %pI4:%u, "
+ "ret=%d\n", &addr, ntohs(port), ret);
goto out;
}
ret = sock->ops->listen(sock, 64);
if (ret < 0) {
- mlog(ML_ERROR, "unable to listen on %u.%u.%u.%u:%u, ret=%d\n",
- NIPQUAD(addr), ntohs(port), ret);
+ mlog(ML_ERROR, "unable to listen on %pI4:%u, ret=%d\n",
+ &addr, ntohs(port), ret);
}
out:
diff --git a/fs/ocfs2/dlm/dlmfs.c b/fs/ocfs2/dlm/dlmfs.c
index ba962d71b34d..6f7a77d54020 100644
--- a/fs/ocfs2/dlm/dlmfs.c
+++ b/fs/ocfs2/dlm/dlmfs.c
@@ -339,8 +339,8 @@ static struct inode *dlmfs_get_root_inode(struct super_block *sb)
ip = DLMFS_I(inode);
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mapping->backing_dev_info = &dlmfs_backing_dev_info;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -365,8 +365,8 @@ static struct inode *dlmfs_get_inode(struct inode *parent,
return NULL;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mapping->backing_dev_info = &dlmfs_backing_dev_info;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index f4967e634ffd..2545e7402efe 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -421,13 +421,13 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb,
fe->i_blkno = cpu_to_le64(fe_blkno);
fe->i_suballoc_bit = cpu_to_le16(suballoc_bit);
fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot);
- fe->i_uid = cpu_to_le32(current->fsuid);
+ fe->i_uid = cpu_to_le32(current_fsuid());
if (dir->i_mode & S_ISGID) {
fe->i_gid = cpu_to_le32(dir->i_gid);
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- fe->i_gid = cpu_to_le32(current->fsgid);
+ fe->i_gid = cpu_to_le32(current_fsgid());
fe->i_mode = cpu_to_le16(mode);
if (S_ISCHR(mode) || S_ISBLK(mode))
fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev));
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
index cbf047a847c5..6afe57c84f84 100644
--- a/fs/omfs/inode.c
+++ b/fs/omfs/inode.c
@@ -37,8 +37,8 @@ struct inode *omfs_new_inode(struct inode *dir, int mode)
inode->i_ino = new_block;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mapping->a_ops = &omfs_aops;
@@ -420,8 +420,8 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_fs_info = sbi;
- sbi->s_uid = current->uid;
- sbi->s_gid = current->gid;
+ sbi->s_uid = current_uid();
+ sbi->s_gid = current_gid();
sbi->s_dmask = sbi->s_fmask = current->fs->umask;
if (!parse_options((char *) data, sbi))
diff --git a/fs/open.c b/fs/open.c
index 83cdb9dee0c1..c0a426d5766c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -425,39 +425,33 @@ out:
*/
asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
{
+ const struct cred *old_cred;
+ struct cred *override_cred;
struct path path;
struct inode *inode;
- int old_fsuid, old_fsgid;
- kernel_cap_t uninitialized_var(old_cap); /* !SECURE_NO_SETUID_FIXUP */
int res;
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
- old_fsuid = current->fsuid;
- old_fsgid = current->fsgid;
+ override_cred = prepare_creds();
+ if (!override_cred)
+ return -ENOMEM;
- current->fsuid = current->uid;
- current->fsgid = current->gid;
+ override_cred->fsuid = override_cred->uid;
+ override_cred->fsgid = override_cred->gid;
if (!issecure(SECURE_NO_SETUID_FIXUP)) {
- /*
- * Clear the capabilities if we switch to a non-root user
- */
-#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
- /*
- * FIXME: There is a race here against sys_capset. The
- * capabilities can change yet we will restore the old
- * value below. We should hold task_capabilities_lock,
- * but we cannot because user_path_at can sleep.
- */
-#endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
- if (current->uid)
- old_cap = cap_set_effective(__cap_empty_set);
+ /* Clear the capabilities if we switch to a non-root user */
+ if (override_cred->uid)
+ cap_clear(override_cred->cap_effective);
else
- old_cap = cap_set_effective(current->cap_permitted);
+ override_cred->cap_effective =
+ override_cred->cap_permitted;
}
+ old_cred = override_creds(override_cred);
+
res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
if (res)
goto out;
@@ -494,12 +488,8 @@ asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
out_path_release:
path_put(&path);
out:
- current->fsuid = old_fsuid;
- current->fsgid = old_fsgid;
-
- if (!issecure(SECURE_NO_SETUID_FIXUP))
- cap_set_effective(old_cap);
-
+ revert_creds(old_cred);
+ put_cred(override_cred);
return res;
}
@@ -792,7 +782,8 @@ static inline int __get_file_write_access(struct inode *inode,
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
int flags, struct file *f,
- int (*open)(struct inode *, struct file *))
+ int (*open)(struct inode *, struct file *),
+ const struct cred *cred)
{
struct inode *inode;
int error;
@@ -816,7 +807,7 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
f->f_op = fops_get(inode->i_fop);
file_move(f, &inode->i_sb->s_files);
- error = security_dentry_open(f);
+ error = security_dentry_open(f, cred);
if (error)
goto cleanup_all;
@@ -891,6 +882,8 @@ cleanup_file:
struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
int (*open)(struct inode *, struct file *))
{
+ const struct cred *cred = current_cred();
+
if (IS_ERR(nd->intent.open.file))
goto out;
if (IS_ERR(dentry))
@@ -898,7 +891,7 @@ struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry
nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt),
nd->intent.open.flags - 1,
nd->intent.open.file,
- open);
+ open, cred);
out:
return nd->intent.open.file;
out_err:
@@ -917,6 +910,7 @@ EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
*/
struct file *nameidata_to_filp(struct nameidata *nd, int flags)
{
+ const struct cred *cred = current_cred();
struct file *filp;
/* Pick up the filp from the open intent */
@@ -924,7 +918,7 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
/* Has the filesystem initialised the file for us? */
if (filp->f_path.dentry == NULL)
filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp,
- NULL);
+ NULL, cred);
else
path_put(&nd->path);
return filp;
@@ -934,7 +928,8 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
* dentry_open() will have done dput(dentry) and mntput(mnt) if it returns an
* error.
*/
-struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
+ const struct cred *cred)
{
int error;
struct file *f;
@@ -959,7 +954,7 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
return ERR_PTR(error);
}
- return __dentry_open(dentry, mnt, flags, f, NULL);
+ return __dentry_open(dentry, mnt, flags, f, NULL, cred);
}
EXPORT_SYMBOL(dentry_open);
diff --git a/fs/pipe.c b/fs/pipe.c
index 7aea8b89baac..aaf797bd57b9 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -899,8 +899,8 @@ static struct inode * get_pipe_inode(void)
*/
inode->i_state = I_DIRTY;
inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
return inode;
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index aec931e09973..39df95a0ec25 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -217,11 +217,11 @@ posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
switch(pa->e_tag) {
case ACL_USER_OBJ:
/* (May have been checked already) */
- if (inode->i_uid == current->fsuid)
+ if (inode->i_uid == current_fsuid())
goto check_perm;
break;
case ACL_USER:
- if (pa->e_id == current->fsuid)
+ if (pa->e_id == current_fsuid())
goto mask;
break;
case ACL_GROUP_OBJ:
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 6af7fba7abb1..7e4877d9dcb5 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -159,6 +159,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
struct group_info *group_info;
int g;
struct fdtable *fdt = NULL;
+ const struct cred *cred;
pid_t ppid, tpid;
rcu_read_lock();
@@ -170,6 +171,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
if (tracer)
tpid = task_pid_nr_ns(tracer, ns);
}
+ cred = get_cred((struct cred *) __task_cred(p));
seq_printf(m,
"State:\t%s\n"
"Tgid:\t%d\n"
@@ -182,8 +184,8 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
task_tgid_nr_ns(p, ns),
pid_nr_ns(pid, ns),
ppid, tpid,
- p->uid, p->euid, p->suid, p->fsuid,
- p->gid, p->egid, p->sgid, p->fsgid);
+ cred->uid, cred->euid, cred->suid, cred->fsuid,
+ cred->gid, cred->egid, cred->sgid, cred->fsgid);
task_lock(p);
if (p->files)
@@ -194,13 +196,12 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
fdt ? fdt->max_fds : 0);
rcu_read_unlock();
- group_info = p->group_info;
- get_group_info(group_info);
+ group_info = cred->group_info;
task_unlock(p);
for (g = 0; g < min(group_info->ngroups, NGROUPS_SMALL); g++)
seq_printf(m, "%d ", GROUP_AT(group_info, g));
- put_group_info(group_info);
+ put_cred(cred);
seq_printf(m, "\n");
}
@@ -262,7 +263,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
blocked = p->blocked;
collect_sigign_sigcatch(p, &ignored, &caught);
num_threads = atomic_read(&p->signal->count);
- qsize = atomic_read(&p->user->sigpending);
+ qsize = atomic_read(&__task_cred(p)->user->sigpending);
qlim = p->signal->rlim[RLIMIT_SIGPENDING].rlim_cur;
unlock_task_sighand(p, &flags);
}
@@ -293,10 +294,21 @@ static void render_cap_t(struct seq_file *m, const char *header,
static inline void task_cap(struct seq_file *m, struct task_struct *p)
{
- render_cap_t(m, "CapInh:\t", &p->cap_inheritable);
- render_cap_t(m, "CapPrm:\t", &p->cap_permitted);
- render_cap_t(m, "CapEff:\t", &p->cap_effective);
- render_cap_t(m, "CapBnd:\t", &p->cap_bset);
+ const struct cred *cred;
+ kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset;
+
+ rcu_read_lock();
+ cred = __task_cred(p);
+ cap_inheritable = cred->cap_inheritable;
+ cap_permitted = cred->cap_permitted;
+ cap_effective = cred->cap_effective;
+ cap_bset = cred->cap_bset;
+ rcu_read_unlock();
+
+ render_cap_t(m, "CapInh:\t", &cap_inheritable);
+ render_cap_t(m, "CapPrm:\t", &cap_permitted);
+ render_cap_t(m, "CapEff:\t", &cap_effective);
+ render_cap_t(m, "CapBnd:\t", &cap_bset);
}
static inline void task_context_switch_counts(struct seq_file *m,
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d4677603c889..cad92c1ac2b3 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -347,8 +347,8 @@ static int proc_pid_wchan(struct task_struct *task, char *buffer)
static int proc_pid_schedstat(struct task_struct *task, char *buffer)
{
return sprintf(buffer, "%llu %llu %lu\n",
- task->sched_info.cpu_time,
- task->sched_info.run_delay,
+ (unsigned long long)task->se.sum_exec_runtime,
+ (unsigned long long)task->sched_info.run_delay,
task->sched_info.pcount);
}
#endif
@@ -1406,6 +1406,7 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st
{
struct inode * inode;
struct proc_inode *ei;
+ const struct cred *cred;
/* We need a new inode */
@@ -1428,8 +1429,11 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st
inode->i_uid = 0;
inode->i_gid = 0;
if (task_dumpable(task)) {
- inode->i_uid = task->euid;
- inode->i_gid = task->egid;
+ rcu_read_lock();
+ cred = __task_cred(task);
+ inode->i_uid = cred->euid;
+ inode->i_gid = cred->egid;
+ rcu_read_unlock();
}
security_task_to_inode(task, inode);
@@ -1445,6 +1449,8 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
{
struct inode *inode = dentry->d_inode;
struct task_struct *task;
+ const struct cred *cred;
+
generic_fillattr(inode, stat);
rcu_read_lock();
@@ -1454,8 +1460,9 @@ static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat
if (task) {
if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
task_dumpable(task)) {
- stat->uid = task->euid;
- stat->gid = task->egid;
+ cred = __task_cred(task);
+ stat->uid = cred->euid;
+ stat->gid = cred->egid;
}
}
rcu_read_unlock();
@@ -1483,11 +1490,16 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
+ const struct cred *cred;
+
if (task) {
if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
task_dumpable(task)) {
- inode->i_uid = task->euid;
- inode->i_gid = task->egid;
+ rcu_read_lock();
+ cred = __task_cred(task);
+ inode->i_uid = cred->euid;
+ inode->i_gid = cred->egid;
+ rcu_read_unlock();
} else {
inode->i_uid = 0;
inode->i_gid = 0;
@@ -1649,6 +1661,7 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
struct task_struct *task = get_proc_task(inode);
int fd = proc_fd(inode);
struct files_struct *files;
+ const struct cred *cred;
if (task) {
files = get_files_struct(task);
@@ -1658,8 +1671,11 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
rcu_read_unlock();
put_files_struct(files);
if (task_dumpable(task)) {
- inode->i_uid = task->euid;
- inode->i_gid = task->egid;
+ rcu_read_lock();
+ cred = __task_cred(task);
+ inode->i_uid = cred->euid;
+ inode->i_gid = cred->egid;
+ rcu_read_unlock();
} else {
inode->i_uid = 0;
inode->i_gid = 0;
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
index d777789b7a89..de2bba5a3440 100644
--- a/fs/proc/proc_devtree.c
+++ b/fs/proc/proc_devtree.c
@@ -218,8 +218,7 @@ void proc_device_tree_add_node(struct device_node *np,
void __init proc_device_tree_init(void)
{
struct device_node *root;
- if ( !have_of )
- return;
+
proc_device_tree = proc_mkdir("device-tree", NULL);
if (proc_device_tree == 0)
return;
diff --git a/fs/quota.c b/fs/quota.c
index 7f4386ebc23a..b7fe44e01618 100644
--- a/fs/quota.c
+++ b/fs/quota.c
@@ -79,7 +79,7 @@ static int generic_quotactl_valid(struct super_block *sb, int type, int cmd, qid
/* Check privileges */
if (cmd == Q_GETQUOTA) {
- if (((type == USRQUOTA && current->euid != id) ||
+ if (((type == USRQUOTA && current_euid() != id) ||
(type == GRPQUOTA && !in_egroup_p(id))) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -130,7 +130,7 @@ static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t i
/* Check privileges */
if (cmd == Q_XGETQUOTA) {
- if (((type == XQM_USRQUOTA && current->euid != id) ||
+ if (((type == XQM_USRQUOTA && current_euid() != id) ||
(type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index f031d1c925f0..a83a3518ae33 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -55,8 +55,8 @@ struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev)
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mapping->a_ops = &ramfs_aops;
inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index f89ebb943f3f..4f322e5ed840 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -573,7 +573,7 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode)
/* the quota init calls have to know who to charge the quota to, so
** we have to set uid and gid here
*/
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
inode->i_mode = mode;
/* Make inode invalid - just in case we are going to drop it before
* the initialization happens */
@@ -584,7 +584,7 @@ static int new_inode_init(struct inode *inode, struct inode *dir, int mode)
if (S_ISDIR(mode))
inode->i_mode |= S_ISGID;
} else {
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
}
DQUOT_INIT(inode);
return 0;
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 16c211558c22..c99358a52176 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -462,7 +462,8 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc)
return -1;
}
-int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits)
+int seq_bitmap(struct seq_file *m, const unsigned long *bits,
+ unsigned int nr_bits)
{
if (m->count < m->size) {
int len = bitmap_scnprintf(m->buf + m->count,
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 48da4fa6b7d4..e7ddd0328ddc 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -667,8 +667,7 @@ smb_make_node(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
attr.ia_mode = mode;
- attr.ia_uid = current->euid;
- attr.ia_gid = current->egid;
+ current_euid_egid(&attr.ia_uid, &attr.ia_gid);
if (!new_valid_dev(dev))
return -EINVAL;
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index 3528f40ffb0f..fc27fbfc5397 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -586,7 +586,7 @@ static int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
if (parse_options(mnt, raw_data))
goto out_bad_option;
}
- mnt->mounted_uid = current->uid;
+ mnt->mounted_uid = current_uid();
smb_setcodepage(server, &mnt->codepage);
/*
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index ee536e8a649a..9468168b9af5 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -864,7 +864,7 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
goto out;
error = -EACCES;
- if (current->uid != server->mnt->mounted_uid &&
+ if (current_uid() != server->mnt->mounted_uid &&
!capable(CAP_SYS_ADMIN))
goto out;
diff --git a/fs/super.c b/fs/super.c
index 400a7608f15e..ddba069d7a99 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -914,7 +914,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
goto out_free_secdata;
BUG_ON(!mnt->mnt_sb);
- error = security_sb_kern_mount(mnt->mnt_sb, secdata);
+ error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
if (error)
goto out_sb;
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
index 115ab0d6f4bc..241e9765cfad 100644
--- a/fs/sysv/ialloc.c
+++ b/fs/sysv/ialloc.c
@@ -165,9 +165,9 @@ struct inode * sysv_new_inode(const struct inode * dir, mode_t mode)
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
inode->i_ino = fs16_to_cpu(sbi, ino);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
inode->i_blocks = 0;
diff --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c
index 1a4973e10664..4a18f084cc42 100644
--- a/fs/ubifs/budget.c
+++ b/fs/ubifs/budget.c
@@ -363,7 +363,7 @@ long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs)
*/
static int can_use_rp(struct ubifs_info *c)
{
- if (current->fsuid == c->rp_uid || capable(CAP_SYS_RESOURCE) ||
+ if (current_fsuid() == c->rp_uid || capable(CAP_SYS_RESOURCE) ||
(c->rp_gid != 0 && in_group_p(c->rp_gid)))
return 1;
return 0;
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 0422c98e1793..f448ab1f9c38 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -104,13 +104,13 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
*/
inode->i_flags |= (S_NOCMTIME);
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
inode->i_mode = mode;
inode->i_mtime = inode->i_atime = inode->i_ctime =
ubifs_current_time(inode);
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
index a4f2b3ce45b0..31fc84297ddb 100644
--- a/fs/udf/ialloc.c
+++ b/fs/udf/ialloc.c
@@ -126,13 +126,13 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
}
mutex_unlock(&sbi->s_alloc_mutex);
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else {
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
}
iinfo->i_location.logicalBlockNum = block;
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 082409cd4b8a..f84bfaa8d941 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -604,7 +604,7 @@ static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
goto out;
iinfo = UDF_I(inode);
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
init_special_inode(inode, mode, rdev);
fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
if (!fi) {
diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c
index ac181f6806a3..6f5dcf006096 100644
--- a/fs/ufs/ialloc.c
+++ b/fs/ufs/ialloc.c
@@ -304,13 +304,13 @@ cg_found:
inode->i_ino = cg * uspi->s_ipg + bit;
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
+ inode->i_uid = current_fsuid();
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
inode->i_mode |= S_ISGID;
} else
- inode->i_gid = current->fsgid;
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_generation = 0;
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 737c9a425361..c3dc491fff89 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -85,13 +85,13 @@ xfs-y += xfs_alloc.o \
xfs_trans_inode.o \
xfs_trans_item.o \
xfs_utils.o \
- xfs_vfsops.o \
xfs_vnodeops.o \
xfs_rw.o \
xfs_dmops.o \
xfs_qmops.o
-xfs-$(CONFIG_XFS_TRACE) += xfs_dir2_trace.o
+xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o \
+ xfs_dir2_trace.o
# Objects in linux/
xfs-y += $(addprefix $(XFS_LINUX)/, \
@@ -106,7 +106,7 @@ xfs-y += $(addprefix $(XFS_LINUX)/, \
xfs_iops.o \
xfs_lrw.o \
xfs_super.o \
- xfs_vnode.o \
+ xfs_sync.o \
xfs_xattr.o)
# Objects in support/
diff --git a/fs/xfs/linux-2.6/sv.h b/fs/xfs/linux-2.6/sv.h
index 351a8f454bd1..4dfc7c370819 100644
--- a/fs/xfs/linux-2.6/sv.h
+++ b/fs/xfs/linux-2.6/sv.h
@@ -32,23 +32,15 @@ typedef struct sv_s {
wait_queue_head_t waiters;
} sv_t;
-#define SV_FIFO 0x0 /* sv_t is FIFO type */
-#define SV_LIFO 0x2 /* sv_t is LIFO type */
-#define SV_PRIO 0x4 /* sv_t is PRIO type */
-#define SV_KEYED 0x6 /* sv_t is KEYED type */
-#define SV_DEFAULT SV_FIFO
-
-
-static inline void _sv_wait(sv_t *sv, spinlock_t *lock, int state,
- unsigned long timeout)
+static inline void _sv_wait(sv_t *sv, spinlock_t *lock)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue_exclusive(&sv->waiters, &wait);
- __set_current_state(state);
+ __set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock(lock);
- schedule_timeout(timeout);
+ schedule();
remove_wait_queue(&sv->waiters, &wait);
}
@@ -58,13 +50,7 @@ static inline void _sv_wait(sv_t *sv, spinlock_t *lock, int state,
#define sv_destroy(sv) \
/*NOTHING*/
#define sv_wait(sv, pri, lock, s) \
- _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
-#define sv_wait_sig(sv, pri, lock, s) \
- _sv_wait(sv, lock, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
-#define sv_timedwait(sv, pri, lock, s, svf, ts, rts) \
- _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, timespec_to_jiffies(ts))
-#define sv_timedwait_sig(sv, pri, lock, s, svf, ts, rts) \
- _sv_wait(sv, lock, TASK_INTERRUPTIBLE, timespec_to_jiffies(ts))
+ _sv_wait(sv, lock)
#define sv_signal(sv) \
wake_up(&(sv)->waiters)
#define sv_broadcast(sv) \
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index a44d68eb50b5..de3a198f771e 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -42,6 +42,40 @@
#include <linux/pagevec.h>
#include <linux/writeback.h>
+
+/*
+ * Prime number of hash buckets since address is used as the key.
+ */
+#define NVSYNC 37
+#define to_ioend_wq(v) (&xfs_ioend_wq[((unsigned long)v) % NVSYNC])
+static wait_queue_head_t xfs_ioend_wq[NVSYNC];
+
+void __init
+xfs_ioend_init(void)
+{
+ int i;
+
+ for (i = 0; i < NVSYNC; i++)
+ init_waitqueue_head(&xfs_ioend_wq[i]);
+}
+
+void
+xfs_ioend_wait(
+ xfs_inode_t *ip)
+{
+ wait_queue_head_t *wq = to_ioend_wq(ip);
+
+ wait_event(*wq, (atomic_read(&ip->i_iocount) == 0));
+}
+
+STATIC void
+xfs_ioend_wake(
+ xfs_inode_t *ip)
+{
+ if (atomic_dec_and_test(&ip->i_iocount))
+ wake_up(to_ioend_wq(ip));
+}
+
STATIC void
xfs_count_page_state(
struct page *page,
@@ -146,16 +180,25 @@ xfs_destroy_ioend(
xfs_ioend_t *ioend)
{
struct buffer_head *bh, *next;
+ struct xfs_inode *ip = XFS_I(ioend->io_inode);
for (bh = ioend->io_buffer_head; bh; bh = next) {
next = bh->b_private;
bh->b_end_io(bh, !ioend->io_error);
}
- if (unlikely(ioend->io_error)) {
- vn_ioerror(XFS_I(ioend->io_inode), ioend->io_error,
- __FILE__,__LINE__);
+
+ /*
+ * Volume managers supporting multiple paths can send back ENODEV
+ * when the final path disappears. In this case continuing to fill
+ * the page cache with dirty data which cannot be written out is
+ * evil, so prevent that.
+ */
+ if (unlikely(ioend->io_error == -ENODEV)) {
+ xfs_do_force_shutdown(ip->i_mount, SHUTDOWN_DEVICE_REQ,
+ __FILE__, __LINE__);
}
- vn_iowake(XFS_I(ioend->io_inode));
+
+ xfs_ioend_wake(ip);
mempool_free(ioend, xfs_ioend_pool);
}
@@ -191,7 +234,7 @@ xfs_setfilesize(
ip->i_d.di_size = isize;
ip->i_update_core = 1;
ip->i_update_size = 1;
- mark_inode_dirty_sync(ioend->io_inode);
+ xfs_mark_inode_dirty_sync(ip);
}
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -317,14 +360,9 @@ xfs_map_blocks(
xfs_iomap_t *mapp,
int flags)
{
- xfs_inode_t *ip = XFS_I(inode);
- int error, nmaps = 1;
-
- error = xfs_iomap(ip, offset, count,
- flags, mapp, &nmaps);
- if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)))
- xfs_iflags_set(ip, XFS_IMODIFIED);
- return -error;
+ int nmaps = 1;
+
+ return -xfs_iomap(XFS_I(inode), offset, count, flags, mapp, &nmaps);
}
STATIC_INLINE int
@@ -512,7 +550,7 @@ xfs_cancel_ioend(
unlock_buffer(bh);
} while ((bh = next_bh) != NULL);
- vn_iowake(XFS_I(ioend->io_inode));
+ xfs_ioend_wake(XFS_I(ioend->io_inode));
mempool_free(ioend, xfs_ioend_pool);
} while ((ioend = next) != NULL);
}
diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h
index 3ba0631a3818..7b26f5ff9692 100644
--- a/fs/xfs/linux-2.6/xfs_aops.h
+++ b/fs/xfs/linux-2.6/xfs_aops.h
@@ -43,4 +43,7 @@ typedef struct xfs_ioend {
extern const struct address_space_operations xfs_address_space_operations;
extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int);
+extern void xfs_ioend_init(void);
+extern void xfs_ioend_wait(struct xfs_inode *);
+
#endif /* __XFS_AOPS_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index 36d5fcd3f593..cb329edc925b 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -630,6 +630,29 @@ xfs_buf_get_flags(
return NULL;
}
+STATIC int
+_xfs_buf_read(
+ xfs_buf_t *bp,
+ xfs_buf_flags_t flags)
+{
+ int status;
+
+ XB_TRACE(bp, "_xfs_buf_read", (unsigned long)flags);
+
+ ASSERT(!(flags & (XBF_DELWRI|XBF_WRITE)));
+ ASSERT(bp->b_bn != XFS_BUF_DADDR_NULL);
+
+ bp->b_flags &= ~(XBF_WRITE | XBF_ASYNC | XBF_DELWRI | \
+ XBF_READ_AHEAD | _XBF_RUN_QUEUES);
+ bp->b_flags |= flags & (XBF_READ | XBF_ASYNC | \
+ XBF_READ_AHEAD | _XBF_RUN_QUEUES);
+
+ status = xfs_buf_iorequest(bp);
+ if (!status && !(flags & XBF_ASYNC))
+ status = xfs_buf_iowait(bp);
+ return status;
+}
+
xfs_buf_t *
xfs_buf_read_flags(
xfs_buftarg_t *target,
@@ -646,7 +669,7 @@ xfs_buf_read_flags(
if (!XFS_BUF_ISDONE(bp)) {
XB_TRACE(bp, "read", (unsigned long)flags);
XFS_STATS_INC(xb_get_read);
- xfs_buf_iostart(bp, flags);
+ _xfs_buf_read(bp, flags);
} else if (flags & XBF_ASYNC) {
XB_TRACE(bp, "read_async", (unsigned long)flags);
/*
@@ -1048,50 +1071,39 @@ xfs_buf_ioerror(
XB_TRACE(bp, "ioerror", (unsigned long)error);
}
-/*
- * Initiate I/O on a buffer, based on the flags supplied.
- * The b_iodone routine in the buffer supplied will only be called
- * when all of the subsidiary I/O requests, if any, have been completed.
- */
int
-xfs_buf_iostart(
- xfs_buf_t *bp,
- xfs_buf_flags_t flags)
+xfs_bawrite(
+ void *mp,
+ struct xfs_buf *bp)
{
- int status = 0;
+ XB_TRACE(bp, "bawrite", 0);
- XB_TRACE(bp, "iostart", (unsigned long)flags);
+ ASSERT(bp->b_bn != XFS_BUF_DADDR_NULL);
- if (flags & XBF_DELWRI) {
- bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_ASYNC);
- bp->b_flags |= flags & (XBF_DELWRI | XBF_ASYNC);
- xfs_buf_delwri_queue(bp, 1);
- return 0;
- }
+ xfs_buf_delwri_dequeue(bp);
- bp->b_flags &= ~(XBF_READ | XBF_WRITE | XBF_ASYNC | XBF_DELWRI | \
- XBF_READ_AHEAD | _XBF_RUN_QUEUES);
- bp->b_flags |= flags & (XBF_READ | XBF_WRITE | XBF_ASYNC | \
- XBF_READ_AHEAD | _XBF_RUN_QUEUES);
+ bp->b_flags &= ~(XBF_READ | XBF_DELWRI | XBF_READ_AHEAD);
+ bp->b_flags |= (XBF_WRITE | XBF_ASYNC | _XBF_RUN_QUEUES);
+
+ bp->b_mount = mp;
+ bp->b_strat = xfs_bdstrat_cb;
+ return xfs_bdstrat_cb(bp);
+}
- BUG_ON(bp->b_bn == XFS_BUF_DADDR_NULL);
+void
+xfs_bdwrite(
+ void *mp,
+ struct xfs_buf *bp)
+{
+ XB_TRACE(bp, "bdwrite", 0);
- /* For writes allow an alternate strategy routine to precede
- * the actual I/O request (which may not be issued at all in
- * a shutdown situation, for example).
- */
- status = (flags & XBF_WRITE) ?
- xfs_buf_iostrategy(bp) : xfs_buf_iorequest(bp);
+ bp->b_strat = xfs_bdstrat_cb;
+ bp->b_mount = mp;
- /* Wait for I/O if we are not an async request.
- * Note: async I/O request completion will release the buffer,
- * and that can already be done by this point. So using the
- * buffer pointer from here on, after async I/O, is invalid.
- */
- if (!status && !(flags & XBF_ASYNC))
- status = xfs_buf_iowait(bp);
+ bp->b_flags &= ~XBF_READ;
+ bp->b_flags |= (XBF_DELWRI | XBF_ASYNC);
- return status;
+ xfs_buf_delwri_queue(bp, 1);
}
STATIC_INLINE void
@@ -1114,8 +1126,7 @@ xfs_buf_bio_end_io(
unsigned int blocksize = bp->b_target->bt_bsize;
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
- if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
- bp->b_error = EIO;
+ xfs_buf_ioerror(bp, -error);
do {
struct page *page = bvec->bv_page;
diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h
index 456519a088c7..288ae7c4c800 100644
--- a/fs/xfs/linux-2.6/xfs_buf.h
+++ b/fs/xfs/linux-2.6/xfs_buf.h
@@ -168,7 +168,7 @@ typedef struct xfs_buf {
struct completion b_iowait; /* queue for I/O waiters */
void *b_fspriv;
void *b_fspriv2;
- void *b_fspriv3;
+ struct xfs_mount *b_mount;
unsigned short b_error; /* error code on I/O */
unsigned int b_page_count; /* size of page array */
unsigned int b_offset; /* page offset in first page */
@@ -214,9 +214,10 @@ extern void xfs_buf_lock(xfs_buf_t *);
extern void xfs_buf_unlock(xfs_buf_t *);
/* Buffer Read and Write Routines */
+extern int xfs_bawrite(void *mp, xfs_buf_t *bp);
+extern void xfs_bdwrite(void *mp, xfs_buf_t *bp);
extern void xfs_buf_ioend(xfs_buf_t *, int);
extern void xfs_buf_ioerror(xfs_buf_t *, int);
-extern int xfs_buf_iostart(xfs_buf_t *, xfs_buf_flags_t);
extern int xfs_buf_iorequest(xfs_buf_t *);
extern int xfs_buf_iowait(xfs_buf_t *);
extern void xfs_buf_iomove(xfs_buf_t *, size_t, size_t, xfs_caddr_t,
@@ -311,10 +312,6 @@ extern void xfs_buf_trace(xfs_buf_t *, char *, void *, void *);
#define XFS_BUF_UNORDERED(bp) ((bp)->b_flags &= ~XBF_ORDERED)
#define XFS_BUF_ISORDERED(bp) ((bp)->b_flags & XBF_ORDERED)
-#define XFS_BUF_SHUT(bp) do { } while (0)
-#define XFS_BUF_UNSHUT(bp) do { } while (0)
-#define XFS_BUF_ISSHUT(bp) (0)
-
#define XFS_BUF_HOLD(bp) xfs_buf_hold(bp)
#define XFS_BUF_READ(bp) ((bp)->b_flags |= XBF_READ)
#define XFS_BUF_UNREAD(bp) ((bp)->b_flags &= ~XBF_READ)
@@ -334,8 +331,6 @@ extern void xfs_buf_trace(xfs_buf_t *, char *, void *, void *);
#define XFS_BUF_SET_FSPRIVATE(bp, val) ((bp)->b_fspriv = (void*)(val))
#define XFS_BUF_FSPRIVATE2(bp, type) ((type)(bp)->b_fspriv2)
#define XFS_BUF_SET_FSPRIVATE2(bp, val) ((bp)->b_fspriv2 = (void*)(val))
-#define XFS_BUF_FSPRIVATE3(bp, type) ((type)(bp)->b_fspriv3)
-#define XFS_BUF_SET_FSPRIVATE3(bp, val) ((bp)->b_fspriv3 = (void*)(val))
#define XFS_BUF_SET_START(bp) do { } while (0)
#define XFS_BUF_SET_BRELSE_FUNC(bp, func) ((bp)->b_relse = (func))
@@ -366,14 +361,6 @@ extern void xfs_buf_trace(xfs_buf_t *, char *, void *, void *);
#define XFS_BUF_TARGET(bp) ((bp)->b_target)
#define XFS_BUFTARG_NAME(target) xfs_buf_target_name(target)
-static inline int xfs_bawrite(void *mp, xfs_buf_t *bp)
-{
- bp->b_fspriv3 = mp;
- bp->b_strat = xfs_bdstrat_cb;
- xfs_buf_delwri_dequeue(bp);
- return xfs_buf_iostart(bp, XBF_WRITE | XBF_ASYNC | _XBF_RUN_QUEUES);
-}
-
static inline void xfs_buf_relse(xfs_buf_t *bp)
{
if (!bp->b_relse)
@@ -414,17 +401,6 @@ static inline int XFS_bwrite(xfs_buf_t *bp)
return error;
}
-/*
- * No error can be returned from xfs_buf_iostart for delwri
- * buffers as they are queued and no I/O is issued.
- */
-static inline void xfs_bdwrite(void *mp, xfs_buf_t *bp)
-{
- bp->b_strat = xfs_bdstrat_cb;
- bp->b_fspriv3 = mp;
- (void)xfs_buf_iostart(bp, XBF_DELWRI | XBF_ASYNC);
-}
-
#define XFS_bdstrat(bp) xfs_buf_iorequest(bp)
#define xfs_iowait(bp) xfs_buf_iowait(bp)
diff --git a/fs/xfs/linux-2.6/xfs_cred.h b/fs/xfs/linux-2.6/xfs_cred.h
index 652721ce0ea5..55bddf3b6091 100644
--- a/fs/xfs/linux-2.6/xfs_cred.h
+++ b/fs/xfs/linux-2.6/xfs_cred.h
@@ -23,16 +23,6 @@
/*
* Credentials
*/
-typedef struct cred {
- /* EMPTY */
-} cred_t;
-
-extern struct cred *sys_cred;
-
-/* this is a hack.. (assumes sys_cred is the only cred_t in the system) */
-static inline int capable_cred(cred_t *cr, int cid)
-{
- return (cr == sys_cred) ? 1 : capable(cid);
-}
+typedef const struct cred cred_t;
#endif /* __XFS_CRED_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c
index 7f7abec25e14..595751f78350 100644
--- a/fs/xfs/linux-2.6/xfs_export.c
+++ b/fs/xfs/linux-2.6/xfs_export.c
@@ -29,7 +29,6 @@
#include "xfs_vnodeops.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
-#include "xfs_vfsops.h"
/*
* Note that we only accept fileids which are long enough rather than allow
diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c
index 3fee790f138b..e14c4e3aea0c 100644
--- a/fs/xfs/linux-2.6/xfs_file.c
+++ b/fs/xfs/linux-2.6/xfs_file.c
@@ -36,89 +36,54 @@
#include "xfs_inode.h"
#include "xfs_error.h"
#include "xfs_rw.h"
-#include "xfs_ioctl32.h"
#include "xfs_vnodeops.h"
+#include "xfs_da_btree.h"
+#include "xfs_ioctl.h"
#include <linux/dcache.h>
#include <linux/smp_lock.h>
static struct vm_operations_struct xfs_file_vm_ops;
-STATIC_INLINE ssize_t
-__xfs_file_read(
+STATIC ssize_t
+xfs_file_aio_read(
struct kiocb *iocb,
const struct iovec *iov,
unsigned long nr_segs,
- int ioflags,
loff_t pos)
{
struct file *file = iocb->ki_filp;
+ int ioflags = IO_ISAIO;
BUG_ON(iocb->ki_pos != pos);
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
+ if (file->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
return xfs_read(XFS_I(file->f_path.dentry->d_inode), iocb, iov,
nr_segs, &iocb->ki_pos, ioflags);
}
STATIC ssize_t
-xfs_file_aio_read(
- struct kiocb *iocb,
- const struct iovec *iov,
- unsigned long nr_segs,
- loff_t pos)
-{
- return __xfs_file_read(iocb, iov, nr_segs, IO_ISAIO, pos);
-}
-
-STATIC ssize_t
-xfs_file_aio_read_invis(
- struct kiocb *iocb,
- const struct iovec *iov,
- unsigned long nr_segs,
- loff_t pos)
-{
- return __xfs_file_read(iocb, iov, nr_segs, IO_ISAIO|IO_INVIS, pos);
-}
-
-STATIC_INLINE ssize_t
-__xfs_file_write(
+xfs_file_aio_write(
struct kiocb *iocb,
const struct iovec *iov,
unsigned long nr_segs,
- int ioflags,
loff_t pos)
{
- struct file *file = iocb->ki_filp;
+ struct file *file = iocb->ki_filp;
+ int ioflags = IO_ISAIO;
BUG_ON(iocb->ki_pos != pos);
if (unlikely(file->f_flags & O_DIRECT))
ioflags |= IO_ISDIRECT;
+ if (file->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
return xfs_write(XFS_I(file->f_mapping->host), iocb, iov, nr_segs,
&iocb->ki_pos, ioflags);
}
STATIC ssize_t
-xfs_file_aio_write(
- struct kiocb *iocb,
- const struct iovec *iov,
- unsigned long nr_segs,
- loff_t pos)
-{
- return __xfs_file_write(iocb, iov, nr_segs, IO_ISAIO, pos);
-}
-
-STATIC ssize_t
-xfs_file_aio_write_invis(
- struct kiocb *iocb,
- const struct iovec *iov,
- unsigned long nr_segs,
- loff_t pos)
-{
- return __xfs_file_write(iocb, iov, nr_segs, IO_ISAIO|IO_INVIS, pos);
-}
-
-STATIC ssize_t
xfs_file_splice_read(
struct file *infilp,
loff_t *ppos,
@@ -126,20 +91,13 @@ xfs_file_splice_read(
size_t len,
unsigned int flags)
{
- return xfs_splice_read(XFS_I(infilp->f_path.dentry->d_inode),
- infilp, ppos, pipe, len, flags, 0);
-}
+ int ioflags = 0;
+
+ if (infilp->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
-STATIC ssize_t
-xfs_file_splice_read_invis(
- struct file *infilp,
- loff_t *ppos,
- struct pipe_inode_info *pipe,
- size_t len,
- unsigned int flags)
-{
return xfs_splice_read(XFS_I(infilp->f_path.dentry->d_inode),
- infilp, ppos, pipe, len, flags, IO_INVIS);
+ infilp, ppos, pipe, len, flags, ioflags);
}
STATIC ssize_t
@@ -150,30 +108,49 @@ xfs_file_splice_write(
size_t len,
unsigned int flags)
{
- return xfs_splice_write(XFS_I(outfilp->f_path.dentry->d_inode),
- pipe, outfilp, ppos, len, flags, 0);
-}
+ int ioflags = 0;
+
+ if (outfilp->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
-STATIC ssize_t
-xfs_file_splice_write_invis(
- struct pipe_inode_info *pipe,
- struct file *outfilp,
- loff_t *ppos,
- size_t len,
- unsigned int flags)
-{
return xfs_splice_write(XFS_I(outfilp->f_path.dentry->d_inode),
- pipe, outfilp, ppos, len, flags, IO_INVIS);
+ pipe, outfilp, ppos, len, flags, ioflags);
}
STATIC int
xfs_file_open(
struct inode *inode,
- struct file *filp)
+ struct file *file)
{
- if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
+ if (!(file->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
return -EFBIG;
- return -xfs_open(XFS_I(inode));
+ if (XFS_FORCED_SHUTDOWN(XFS_M(inode->i_sb)))
+ return -EIO;
+ return 0;
+}
+
+STATIC int
+xfs_dir_open(
+ struct inode *inode,
+ struct file *file)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ int mode;
+ int error;
+
+ error = xfs_file_open(inode, file);
+ if (error)
+ return error;
+
+ /*
+ * If there are any blocks, read-ahead block 0 as we're almost
+ * certain to have the next operation be a read there.
+ */
+ mode = xfs_ilock_map_shared(ip);
+ if (ip->i_d.di_nextents > 0)
+ xfs_da_reada_buf(NULL, ip, 0, XFS_DATA_FORK);
+ xfs_iunlock(ip, mode);
+ return 0;
}
STATIC int
@@ -227,7 +204,7 @@ xfs_file_readdir(
* point we can change the ->readdir prototype to include the
* buffer size.
*/
- bufsize = (size_t)min_t(loff_t, PAGE_SIZE, inode->i_size);
+ bufsize = (size_t)min_t(loff_t, PAGE_SIZE, ip->i_d.di_size);
error = xfs_readdir(ip, dirent, bufsize,
(xfs_off_t *)&filp->f_pos, filldir);
@@ -248,48 +225,6 @@ xfs_file_mmap(
return 0;
}
-STATIC long
-xfs_file_ioctl(
- struct file *filp,
- unsigned int cmd,
- unsigned long p)
-{
- int error;
- struct inode *inode = filp->f_path.dentry->d_inode;
-
- error = xfs_ioctl(XFS_I(inode), filp, 0, cmd, (void __user *)p);
- xfs_iflags_set(XFS_I(inode), XFS_IMODIFIED);
-
- /* NOTE: some of the ioctl's return positive #'s as a
- * byte count indicating success, such as
- * readlink_by_handle. So we don't "sign flip"
- * like most other routines. This means true
- * errors need to be returned as a negative value.
- */
- return error;
-}
-
-STATIC long
-xfs_file_ioctl_invis(
- struct file *filp,
- unsigned int cmd,
- unsigned long p)
-{
- int error;
- struct inode *inode = filp->f_path.dentry->d_inode;
-
- error = xfs_ioctl(XFS_I(inode), filp, IO_INVIS, cmd, (void __user *)p);
- xfs_iflags_set(XFS_I(inode), XFS_IMODIFIED);
-
- /* NOTE: some of the ioctl's return positive #'s as a
- * byte count indicating success, such as
- * readlink_by_handle. So we don't "sign flip"
- * like most other routines. This means true
- * errors need to be returned as a negative value.
- */
- return error;
-}
-
/*
* mmap()d file has taken write protection fault and is being made
* writable. We can set the page state up correctly for a writable
@@ -325,26 +260,8 @@ const struct file_operations xfs_file_operations = {
#endif
};
-const struct file_operations xfs_invis_file_operations = {
- .llseek = generic_file_llseek,
- .read = do_sync_read,
- .write = do_sync_write,
- .aio_read = xfs_file_aio_read_invis,
- .aio_write = xfs_file_aio_write_invis,
- .splice_read = xfs_file_splice_read_invis,
- .splice_write = xfs_file_splice_write_invis,
- .unlocked_ioctl = xfs_file_ioctl_invis,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = xfs_file_compat_invis_ioctl,
-#endif
- .mmap = xfs_file_mmap,
- .open = xfs_file_open,
- .release = xfs_file_release,
- .fsync = xfs_file_fsync,
-};
-
-
const struct file_operations xfs_dir_file_operations = {
+ .open = xfs_dir_open,
.read = generic_read_dir,
.readdir = xfs_file_readdir,
.llseek = generic_file_llseek,
diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c
index 36caa6d957df..5aeb77776961 100644
--- a/fs/xfs/linux-2.6/xfs_fs_subr.c
+++ b/fs/xfs/linux-2.6/xfs_fs_subr.c
@@ -24,6 +24,10 @@ int fs_noerr(void) { return 0; }
int fs_nosys(void) { return ENOSYS; }
void fs_noval(void) { return; }
+/*
+ * note: all filemap functions return negative error codes. These
+ * need to be inverted before returning to the xfs core functions.
+ */
void
xfs_tosspages(
xfs_inode_t *ip,
@@ -53,7 +57,7 @@ xfs_flushinval_pages(
if (!ret)
truncate_inode_pages(mapping, first);
}
- return ret;
+ return -ret;
}
int
@@ -72,10 +76,23 @@ xfs_flush_pages(
xfs_iflags_clear(ip, XFS_ITRUNCATED);
ret = filemap_fdatawrite(mapping);
if (flags & XFS_B_ASYNC)
- return ret;
+ return -ret;
ret2 = filemap_fdatawait(mapping);
if (!ret)
ret = ret2;
}
- return ret;
+ return -ret;
+}
+
+int
+xfs_wait_on_pages(
+ xfs_inode_t *ip,
+ xfs_off_t first,
+ xfs_off_t last)
+{
+ struct address_space *mapping = VFS_I(ip)->i_mapping;
+
+ if (mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK))
+ return -filemap_fdatawait(mapping);
+ return 0;
}
diff --git a/fs/xfs/linux-2.6/xfs_globals.c b/fs/xfs/linux-2.6/xfs_globals.c
index ef90e64641e6..2ae8b1ccb02e 100644
--- a/fs/xfs/linux-2.6/xfs_globals.c
+++ b/fs/xfs/linux-2.6/xfs_globals.c
@@ -26,7 +26,6 @@
*/
xfs_param_t xfs_params = {
/* MIN DFLT MAX */
- .restrict_chown = { 0, 1, 1 },
.sgid_inherit = { 0, 0, 1 },
.symlink_mode = { 0, 0, 1 },
.panic_mask = { 0, 0, 255 },
@@ -43,10 +42,3 @@ xfs_param_t xfs_params = {
.inherit_nodfrg = { 0, 1, 1 },
.fstrm_timer = { 1, 30*100, 3600*100},
};
-
-/*
- * Global system credential structure.
- */
-static cred_t sys_cred_val;
-cred_t *sys_cred = &sys_cred_val;
-
diff --git a/fs/xfs/linux-2.6/xfs_globals.h b/fs/xfs/linux-2.6/xfs_globals.h
index 2770b0085ee8..69f71caf061c 100644
--- a/fs/xfs/linux-2.6/xfs_globals.h
+++ b/fs/xfs/linux-2.6/xfs_globals.h
@@ -19,6 +19,5 @@
#define __XFS_GLOBALS_H__
extern uint64_t xfs_panic_mask; /* set to cause more panics */
-extern struct cred *sys_cred;
#endif /* __XFS_GLOBALS_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c
index d3438c72dcaf..67205f6198ba 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl.c
@@ -68,26 +68,22 @@
* XFS_IOC_PATH_TO_HANDLE
* returns full handle for a path
*/
-STATIC int
+int
xfs_find_handle(
unsigned int cmd,
- void __user *arg)
+ xfs_fsop_handlereq_t *hreq)
{
int hsize;
xfs_handle_t handle;
- xfs_fsop_handlereq_t hreq;
struct inode *inode;
- if (copy_from_user(&hreq, arg, sizeof(hreq)))
- return -XFS_ERROR(EFAULT);
-
memset((char *)&handle, 0, sizeof(handle));
switch (cmd) {
case XFS_IOC_PATH_TO_FSHANDLE:
case XFS_IOC_PATH_TO_HANDLE: {
struct path path;
- int error = user_lpath((const char __user *)hreq.path, &path);
+ int error = user_lpath((const char __user *)hreq->path, &path);
if (error)
return error;
@@ -101,7 +97,7 @@ xfs_find_handle(
case XFS_IOC_FD_TO_HANDLE: {
struct file *file;
- file = fget(hreq.fd);
+ file = fget(hreq->fd);
if (!file)
return -EBADF;
@@ -158,8 +154,8 @@ xfs_find_handle(
}
/* now copy our handle into the user buffer & write out the size */
- if (copy_to_user(hreq.ohandle, &handle, hsize) ||
- copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
+ if (copy_to_user(hreq->ohandle, &handle, hsize) ||
+ copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32))) {
iput(inode);
return -XFS_ERROR(EFAULT);
}
@@ -249,27 +245,25 @@ xfs_vget_fsop_handlereq(
return 0;
}
-STATIC int
+int
xfs_open_by_handle(
xfs_mount_t *mp,
- void __user *arg,
+ xfs_fsop_handlereq_t *hreq,
struct file *parfilp,
struct inode *parinode)
{
+ const struct cred *cred = current_cred();
int error;
int new_fd;
int permflag;
struct file *filp;
struct inode *inode;
struct dentry *dentry;
- xfs_fsop_handlereq_t hreq;
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
- if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
- return -XFS_ERROR(EFAULT);
- error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode);
+ error = xfs_vget_fsop_handlereq(mp, parinode, hreq, &inode);
if (error)
return -error;
@@ -280,10 +274,10 @@ xfs_open_by_handle(
}
#if BITS_PER_LONG != 32
- hreq.oflags |= O_LARGEFILE;
+ hreq->oflags |= O_LARGEFILE;
#endif
/* Put open permission in namei format. */
- permflag = hreq.oflags;
+ permflag = hreq->oflags;
if ((permflag+1) & O_ACCMODE)
permflag++;
if (permflag & O_TRUNC)
@@ -321,15 +315,16 @@ xfs_open_by_handle(
mntget(parfilp->f_path.mnt);
/* Create file pointer. */
- filp = dentry_open(dentry, parfilp->f_path.mnt, hreq.oflags);
+ filp = dentry_open(dentry, parfilp->f_path.mnt, hreq->oflags, cred);
if (IS_ERR(filp)) {
put_unused_fd(new_fd);
return -XFS_ERROR(-PTR_ERR(filp));
}
+
if (inode->i_mode & S_IFREG) {
/* invisible operation should not change atime */
filp->f_flags |= O_NOATIME;
- filp->f_op = &xfs_invis_file_operations;
+ filp->f_mode |= FMODE_NOCMTIME;
}
fd_install(new_fd, filp);
@@ -362,24 +357,21 @@ do_readlink(
}
-STATIC int
+int
xfs_readlink_by_handle(
xfs_mount_t *mp,
- void __user *arg,
+ xfs_fsop_handlereq_t *hreq,
struct inode *parinode)
{
struct inode *inode;
- xfs_fsop_handlereq_t hreq;
__u32 olen;
void *link;
int error;
if (!capable(CAP_SYS_ADMIN))
return -XFS_ERROR(EPERM);
- if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
- return -XFS_ERROR(EFAULT);
- error = xfs_vget_fsop_handlereq(mp, parinode, &hreq, &inode);
+ error = xfs_vget_fsop_handlereq(mp, parinode, hreq, &inode);
if (error)
return -error;
@@ -389,7 +381,7 @@ xfs_readlink_by_handle(
goto out_iput;
}
- if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
+ if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
error = -XFS_ERROR(EFAULT);
goto out_iput;
}
@@ -401,7 +393,7 @@ xfs_readlink_by_handle(
error = -xfs_readlink(XFS_I(inode), link);
if (error)
goto out_kfree;
- error = do_readlink(hreq.ohandle, olen, link);
+ error = do_readlink(hreq->ohandle, olen, link);
if (error)
goto out_kfree;
@@ -500,7 +492,7 @@ xfs_attrlist_by_handle(
return -error;
}
-STATIC int
+int
xfs_attrmulti_attr_get(
struct inode *inode,
char *name,
@@ -529,7 +521,7 @@ xfs_attrmulti_attr_get(
return error;
}
-STATIC int
+int
xfs_attrmulti_attr_set(
struct inode *inode,
char *name,
@@ -559,7 +551,7 @@ xfs_attrmulti_attr_set(
return error;
}
-STATIC int
+int
xfs_attrmulti_attr_remove(
struct inode *inode,
char *name,
@@ -661,19 +653,26 @@ xfs_attrmulti_by_handle(
return -error;
}
-STATIC int
+int
xfs_ioc_space(
struct xfs_inode *ip,
struct inode *inode,
struct file *filp,
int ioflags,
unsigned int cmd,
- void __user *arg)
+ xfs_flock64_t *bf)
{
- xfs_flock64_t bf;
int attr_flags = 0;
int error;
+ /*
+ * Only allow the sys admin to reserve space unless
+ * unwritten extents are enabled.
+ */
+ if (!xfs_sb_version_hasextflgbit(&ip->i_mount->m_sb) &&
+ !capable(CAP_SYS_ADMIN))
+ return -XFS_ERROR(EPERM);
+
if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
return -XFS_ERROR(EPERM);
@@ -683,16 +682,12 @@ xfs_ioc_space(
if (!S_ISREG(inode->i_mode))
return -XFS_ERROR(EINVAL);
- if (copy_from_user(&bf, arg, sizeof(bf)))
- return -XFS_ERROR(EFAULT);
-
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
attr_flags |= XFS_ATTR_NONBLOCK;
if (ioflags & IO_INVIS)
attr_flags |= XFS_ATTR_DMI;
- error = xfs_change_file_space(ip, cmd, &bf, filp->f_pos,
- NULL, attr_flags);
+ error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
return -error;
}
@@ -1007,7 +1002,7 @@ xfs_ioctl_setattr(
* to the file owner ID, except in cases where the
* CAP_FSETID capability is applicable.
*/
- if (current->fsuid != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
+ if (current_fsuid() != ip->i_d.di_uid && !capable(CAP_FOWNER)) {
code = XFS_ERROR(EPERM);
goto error_return;
}
@@ -1104,10 +1099,6 @@ xfs_ioctl_setattr(
/*
* Change file ownership. Must be the owner or privileged.
- * If the system was configured with the "restricted_chown"
- * option, the owner is not permitted to give away the file,
- * and can change the group id only to a group of which he
- * or she is a member.
*/
if (mask & FSX_PROJID) {
/*
@@ -1136,7 +1127,7 @@ xfs_ioctl_setattr(
* the superblock version number since projids didn't
* exist before DINODE_VERSION_2 and SB_VERSION_NLINK.
*/
- if (ip->i_d.di_version == XFS_DINODE_VERSION_1)
+ if (ip->i_d.di_version == 1)
xfs_bump_ino_vers2(tp, ip);
}
@@ -1255,43 +1246,67 @@ xfs_ioc_setxflags(
}
STATIC int
+xfs_getbmap_format(void **ap, struct getbmapx *bmv, int *full)
+{
+ struct getbmap __user *base = *ap;
+
+ /* copy only getbmap portion (not getbmapx) */
+ if (copy_to_user(base, bmv, sizeof(struct getbmap)))
+ return XFS_ERROR(EFAULT);
+
+ *ap += sizeof(struct getbmap);
+ return 0;
+}
+
+STATIC int
xfs_ioc_getbmap(
struct xfs_inode *ip,
int ioflags,
unsigned int cmd,
void __user *arg)
{
- struct getbmap bm;
- int iflags;
+ struct getbmapx bmx;
int error;
- if (copy_from_user(&bm, arg, sizeof(bm)))
+ if (copy_from_user(&bmx, arg, sizeof(struct getbmapx)))
return -XFS_ERROR(EFAULT);
- if (bm.bmv_count < 2)
+ if (bmx.bmv_count < 2)
return -XFS_ERROR(EINVAL);
- iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
+ bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
if (ioflags & IO_INVIS)
- iflags |= BMV_IF_NO_DMAPI_READ;
+ bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
- error = xfs_getbmap(ip, &bm, (struct getbmap __user *)arg+1, iflags);
+ error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
+ (struct getbmap *)arg+1);
if (error)
return -error;
- if (copy_to_user(arg, &bm, sizeof(bm)))
+ /* copy back header - only size of getbmap */
+ if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
return -XFS_ERROR(EFAULT);
return 0;
}
STATIC int
+xfs_getbmapx_format(void **ap, struct getbmapx *bmv, int *full)
+{
+ struct getbmapx __user *base = *ap;
+
+ if (copy_to_user(base, bmv, sizeof(struct getbmapx)))
+ return XFS_ERROR(EFAULT);
+
+ *ap += sizeof(struct getbmapx);
+ return 0;
+}
+
+STATIC int
xfs_ioc_getbmapx(
struct xfs_inode *ip,
void __user *arg)
{
struct getbmapx bmx;
- struct getbmap bm;
- int iflags;
int error;
if (copy_from_user(&bmx, arg, sizeof(bmx)))
@@ -1300,46 +1315,46 @@ xfs_ioc_getbmapx(
if (bmx.bmv_count < 2)
return -XFS_ERROR(EINVAL);
- /*
- * Map input getbmapx structure to a getbmap
- * structure for xfs_getbmap.
- */
- GETBMAP_CONVERT(bmx, bm);
-
- iflags = bmx.bmv_iflags;
-
- if (iflags & (~BMV_IF_VALID))
+ if (bmx.bmv_iflags & (~BMV_IF_VALID))
return -XFS_ERROR(EINVAL);
- iflags |= BMV_IF_EXTENDED;
-
- error = xfs_getbmap(ip, &bm, (struct getbmapx __user *)arg+1, iflags);
+ error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
+ (struct getbmapx *)arg+1);
if (error)
return -error;
- GETBMAP_CONVERT(bm, bmx);
-
- if (copy_to_user(arg, &bmx, sizeof(bmx)))
+ /* copy back header */
+ if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
return -XFS_ERROR(EFAULT);
return 0;
}
-int
-xfs_ioctl(
- xfs_inode_t *ip,
+/*
+ * Note: some of the ioctl's return positive numbers as a
+ * byte count indicating success, such as readlink_by_handle.
+ * So we don't "sign flip" like most other routines. This means
+ * true errors need to be returned as a negative value.
+ */
+long
+xfs_file_ioctl(
struct file *filp,
- int ioflags,
unsigned int cmd,
- void __user *arg)
+ unsigned long p)
{
struct inode *inode = filp->f_path.dentry->d_inode;
- xfs_mount_t *mp = ip->i_mount;
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ void __user *arg = (void __user *)p;
+ int ioflags = 0;
int error;
- xfs_itrace_entry(XFS_I(inode));
- switch (cmd) {
+ if (filp->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
+ xfs_itrace_entry(ip);
+
+ switch (cmd) {
case XFS_IOC_ALLOCSP:
case XFS_IOC_FREESP:
case XFS_IOC_RESVSP:
@@ -1347,17 +1362,13 @@ xfs_ioctl(
case XFS_IOC_ALLOCSP64:
case XFS_IOC_FREESP64:
case XFS_IOC_RESVSP64:
- case XFS_IOC_UNRESVSP64:
- /*
- * Only allow the sys admin to reserve space unless
- * unwritten extents are enabled.
- */
- if (!xfs_sb_version_hasextflgbit(&mp->m_sb) &&
- !capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- return xfs_ioc_space(ip, inode, filp, ioflags, cmd, arg);
+ case XFS_IOC_UNRESVSP64: {
+ xfs_flock64_t bf;
+ if (copy_from_user(&bf, arg, sizeof(bf)))
+ return -XFS_ERROR(EFAULT);
+ return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
+ }
case XFS_IOC_DIOINFO: {
struct dioattr da;
xfs_buftarg_t *target =
@@ -1417,18 +1428,30 @@ xfs_ioctl(
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE:
- case XFS_IOC_PATH_TO_FSHANDLE:
- return xfs_find_handle(cmd, arg);
+ case XFS_IOC_PATH_TO_FSHANDLE: {
+ xfs_fsop_handlereq_t hreq;
- case XFS_IOC_OPEN_BY_HANDLE:
- return xfs_open_by_handle(mp, arg, filp, inode);
+ if (copy_from_user(&hreq, arg, sizeof(hreq)))
+ return -XFS_ERROR(EFAULT);
+ return xfs_find_handle(cmd, &hreq);
+ }
+ case XFS_IOC_OPEN_BY_HANDLE: {
+ xfs_fsop_handlereq_t hreq;
+ if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+ return xfs_open_by_handle(mp, &hreq, filp, inode);
+ }
case XFS_IOC_FSSETDM_BY_HANDLE:
return xfs_fssetdm_by_handle(mp, arg, inode);
- case XFS_IOC_READLINK_BY_HANDLE:
- return xfs_readlink_by_handle(mp, arg, inode);
+ case XFS_IOC_READLINK_BY_HANDLE: {
+ xfs_fsop_handlereq_t hreq;
+ if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+ return xfs_readlink_by_handle(mp, &hreq, inode);
+ }
case XFS_IOC_ATTRLIST_BY_HANDLE:
return xfs_attrlist_by_handle(mp, arg, inode);
@@ -1436,7 +1459,11 @@ xfs_ioctl(
return xfs_attrmulti_by_handle(mp, arg, filp, inode);
case XFS_IOC_SWAPEXT: {
- error = xfs_swapext((struct xfs_swapext __user *)arg);
+ struct xfs_swapext sxp;
+
+ if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
+ return -XFS_ERROR(EFAULT);
+ error = xfs_swapext(&sxp);
return -error;
}
@@ -1492,9 +1519,6 @@ xfs_ioctl(
case XFS_IOC_FSGROWFSDATA: {
xfs_growfs_data_t in;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
@@ -1505,9 +1529,6 @@ xfs_ioctl(
case XFS_IOC_FSGROWFSLOG: {
xfs_growfs_log_t in;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
@@ -1518,9 +1539,6 @@ xfs_ioctl(
case XFS_IOC_FSGROWFSRT: {
xfs_growfs_rt_t in;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.h b/fs/xfs/linux-2.6/xfs_ioctl.h
new file mode 100644
index 000000000000..8c16bf2d7e03
--- /dev/null
+++ b/fs/xfs/linux-2.6/xfs_ioctl.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008 Silicon Graphics, 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.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __XFS_IOCTL_H__
+#define __XFS_IOCTL_H__
+
+extern int
+xfs_ioc_space(
+ struct xfs_inode *ip,
+ struct inode *inode,
+ struct file *filp,
+ int ioflags,
+ unsigned int cmd,
+ xfs_flock64_t *bf);
+
+extern int
+xfs_find_handle(
+ unsigned int cmd,
+ xfs_fsop_handlereq_t *hreq);
+
+extern int
+xfs_open_by_handle(
+ xfs_mount_t *mp,
+ xfs_fsop_handlereq_t *hreq,
+ struct file *parfilp,
+ struct inode *parinode);
+
+extern int
+xfs_readlink_by_handle(
+ xfs_mount_t *mp,
+ xfs_fsop_handlereq_t *hreq,
+ struct inode *parinode);
+
+extern int
+xfs_attrmulti_attr_get(
+ struct inode *inode,
+ char *name,
+ char __user *ubuf,
+ __uint32_t *len,
+ __uint32_t flags);
+
+extern int
+ xfs_attrmulti_attr_set(
+ struct inode *inode,
+ char *name,
+ const char __user *ubuf,
+ __uint32_t len,
+ __uint32_t flags);
+
+extern int
+xfs_attrmulti_attr_remove(
+ struct inode *inode,
+ char *name,
+ __uint32_t flags);
+
+extern long
+xfs_file_ioctl(
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long p);
+
+extern long
+xfs_file_compat_ioctl(
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+
+#endif
diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c
index a4b254eb43b2..0504cece9f66 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl32.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl32.c
@@ -16,11 +16,7 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/compat.h>
-#include <linux/init.h>
#include <linux/ioctl.h>
-#include <linux/syscalls.h>
-#include <linux/types.h>
-#include <linux/fs.h>
#include <asm/uaccess.h>
#include "xfs.h"
#include "xfs_fs.h"
@@ -36,7 +32,6 @@
#include "xfs_bmap_btree.h"
#include "xfs_attr_sf.h"
#include "xfs_dir2_sf.h"
-#include "xfs_vfs.h"
#include "xfs_vnode.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
@@ -44,221 +39,219 @@
#include "xfs_error.h"
#include "xfs_dfrag.h"
#include "xfs_vnodeops.h"
+#include "xfs_fsops.h"
+#include "xfs_alloc.h"
+#include "xfs_rtalloc.h"
+#include "xfs_attr.h"
+#include "xfs_ioctl.h"
#include "xfs_ioctl32.h"
#define _NATIVE_IOC(cmd, type) \
_IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))
-#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
-#define BROKEN_X86_ALIGNMENT
-#define _PACKED __attribute__((packed))
-/* on ia32 l_start is on a 32-bit boundary */
-typedef struct xfs_flock64_32 {
- __s16 l_type;
- __s16 l_whence;
- __s64 l_start __attribute__((packed));
- /* len == 0 means until end of file */
- __s64 l_len __attribute__((packed));
- __s32 l_sysid;
- __u32 l_pid;
- __s32 l_pad[4]; /* reserve area */
-} xfs_flock64_32_t;
-
-#define XFS_IOC_ALLOCSP_32 _IOW ('X', 10, struct xfs_flock64_32)
-#define XFS_IOC_FREESP_32 _IOW ('X', 11, struct xfs_flock64_32)
-#define XFS_IOC_ALLOCSP64_32 _IOW ('X', 36, struct xfs_flock64_32)
-#define XFS_IOC_FREESP64_32 _IOW ('X', 37, struct xfs_flock64_32)
-#define XFS_IOC_RESVSP_32 _IOW ('X', 40, struct xfs_flock64_32)
-#define XFS_IOC_UNRESVSP_32 _IOW ('X', 41, struct xfs_flock64_32)
-#define XFS_IOC_RESVSP64_32 _IOW ('X', 42, struct xfs_flock64_32)
-#define XFS_IOC_UNRESVSP64_32 _IOW ('X', 43, struct xfs_flock64_32)
-
-/* just account for different alignment */
-STATIC unsigned long
-xfs_ioctl32_flock(
- unsigned long arg)
+#ifdef BROKEN_X86_ALIGNMENT
+STATIC int
+xfs_compat_flock64_copyin(
+ xfs_flock64_t *bf,
+ compat_xfs_flock64_t __user *arg32)
{
- xfs_flock64_32_t __user *p32 = (void __user *)arg;
- xfs_flock64_t __user *p = compat_alloc_user_space(sizeof(*p));
-
- if (copy_in_user(&p->l_type, &p32->l_type, sizeof(s16)) ||
- copy_in_user(&p->l_whence, &p32->l_whence, sizeof(s16)) ||
- copy_in_user(&p->l_start, &p32->l_start, sizeof(s64)) ||
- copy_in_user(&p->l_len, &p32->l_len, sizeof(s64)) ||
- copy_in_user(&p->l_sysid, &p32->l_sysid, sizeof(s32)) ||
- copy_in_user(&p->l_pid, &p32->l_pid, sizeof(u32)) ||
- copy_in_user(&p->l_pad, &p32->l_pad, 4*sizeof(u32)))
- return -EFAULT;
-
- return (unsigned long)p;
+ if (get_user(bf->l_type, &arg32->l_type) ||
+ get_user(bf->l_whence, &arg32->l_whence) ||
+ get_user(bf->l_start, &arg32->l_start) ||
+ get_user(bf->l_len, &arg32->l_len) ||
+ get_user(bf->l_sysid, &arg32->l_sysid) ||
+ get_user(bf->l_pid, &arg32->l_pid) ||
+ copy_from_user(bf->l_pad, &arg32->l_pad, 4*sizeof(u32)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
}
-typedef struct compat_xfs_fsop_geom_v1 {
- __u32 blocksize; /* filesystem (data) block size */
- __u32 rtextsize; /* realtime extent size */
- __u32 agblocks; /* fsblocks in an AG */
- __u32 agcount; /* number of allocation groups */
- __u32 logblocks; /* fsblocks in the log */
- __u32 sectsize; /* (data) sector size, bytes */
- __u32 inodesize; /* inode size in bytes */
- __u32 imaxpct; /* max allowed inode space(%) */
- __u64 datablocks; /* fsblocks in data subvolume */
- __u64 rtblocks; /* fsblocks in realtime subvol */
- __u64 rtextents; /* rt extents in realtime subvol*/
- __u64 logstart; /* starting fsblock of the log */
- unsigned char uuid[16]; /* unique id of the filesystem */
- __u32 sunit; /* stripe unit, fsblocks */
- __u32 swidth; /* stripe width, fsblocks */
- __s32 version; /* structure version */
- __u32 flags; /* superblock version flags */
- __u32 logsectsize; /* log sector size, bytes */
- __u32 rtsectsize; /* realtime sector size, bytes */
- __u32 dirblocksize; /* directory block size, bytes */
-} __attribute__((packed)) compat_xfs_fsop_geom_v1_t;
-
-#define XFS_IOC_FSGEOMETRY_V1_32 \
- _IOR ('X', 100, struct compat_xfs_fsop_geom_v1)
-
-STATIC unsigned long xfs_ioctl32_geom_v1(unsigned long arg)
+STATIC int
+xfs_compat_ioc_fsgeometry_v1(
+ struct xfs_mount *mp,
+ compat_xfs_fsop_geom_v1_t __user *arg32)
{
- compat_xfs_fsop_geom_v1_t __user *p32 = (void __user *)arg;
- xfs_fsop_geom_v1_t __user *p = compat_alloc_user_space(sizeof(*p));
+ xfs_fsop_geom_t fsgeo;
+ int error;
- if (copy_in_user(p, p32, sizeof(*p32)))
- return -EFAULT;
- return (unsigned long)p;
+ error = xfs_fs_geometry(mp, &fsgeo, 3);
+ if (error)
+ return -error;
+ /* The 32-bit variant simply has some padding at the end */
+ if (copy_to_user(arg32, &fsgeo, sizeof(struct compat_xfs_fsop_geom_v1)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
}
-typedef struct compat_xfs_inogrp {
- __u64 xi_startino; /* starting inode number */
- __s32 xi_alloccount; /* # bits set in allocmask */
- __u64 xi_allocmask; /* mask of allocated inodes */
-} __attribute__((packed)) compat_xfs_inogrp_t;
-
-STATIC int xfs_inumbers_fmt_compat(
- void __user *ubuffer,
- const xfs_inogrp_t *buffer,
- long count,
- long *written)
+STATIC int
+xfs_compat_growfs_data_copyin(
+ struct xfs_growfs_data *in,
+ compat_xfs_growfs_data_t __user *arg32)
{
- compat_xfs_inogrp_t __user *p32 = ubuffer;
- long i;
+ if (get_user(in->newblocks, &arg32->newblocks) ||
+ get_user(in->imaxpct, &arg32->imaxpct))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
+
+STATIC int
+xfs_compat_growfs_rt_copyin(
+ struct xfs_growfs_rt *in,
+ compat_xfs_growfs_rt_t __user *arg32)
+{
+ if (get_user(in->newblocks, &arg32->newblocks) ||
+ get_user(in->extsize, &arg32->extsize))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
+
+STATIC int
+xfs_inumbers_fmt_compat(
+ void __user *ubuffer,
+ const xfs_inogrp_t *buffer,
+ long count,
+ long *written)
+{
+ compat_xfs_inogrp_t __user *p32 = ubuffer;
+ long i;
for (i = 0; i < count; i++) {
if (put_user(buffer[i].xi_startino, &p32[i].xi_startino) ||
put_user(buffer[i].xi_alloccount, &p32[i].xi_alloccount) ||
put_user(buffer[i].xi_allocmask, &p32[i].xi_allocmask))
- return -EFAULT;
+ return -XFS_ERROR(EFAULT);
}
*written = count * sizeof(*p32);
return 0;
}
#else
-
#define xfs_inumbers_fmt_compat xfs_inumbers_fmt
-#define _PACKED
+#endif /* BROKEN_X86_ALIGNMENT */
-#endif
+STATIC int
+xfs_ioctl32_bstime_copyin(
+ xfs_bstime_t *bstime,
+ compat_xfs_bstime_t __user *bstime32)
+{
+ compat_time_t sec32; /* tv_sec differs on 64 vs. 32 */
-/* XFS_IOC_FSBULKSTAT and friends */
+ if (get_user(sec32, &bstime32->tv_sec) ||
+ get_user(bstime->tv_nsec, &bstime32->tv_nsec))
+ return -XFS_ERROR(EFAULT);
+ bstime->tv_sec = sec32;
+ return 0;
+}
+
+/* xfs_bstat_t has differing alignment on intel, & bstime_t sizes everywhere */
+STATIC int
+xfs_ioctl32_bstat_copyin(
+ xfs_bstat_t *bstat,
+ compat_xfs_bstat_t __user *bstat32)
+{
+ if (get_user(bstat->bs_ino, &bstat32->bs_ino) ||
+ get_user(bstat->bs_mode, &bstat32->bs_mode) ||
+ get_user(bstat->bs_nlink, &bstat32->bs_nlink) ||
+ get_user(bstat->bs_uid, &bstat32->bs_uid) ||
+ get_user(bstat->bs_gid, &bstat32->bs_gid) ||
+ get_user(bstat->bs_rdev, &bstat32->bs_rdev) ||
+ get_user(bstat->bs_blksize, &bstat32->bs_blksize) ||
+ get_user(bstat->bs_size, &bstat32->bs_size) ||
+ xfs_ioctl32_bstime_copyin(&bstat->bs_atime, &bstat32->bs_atime) ||
+ xfs_ioctl32_bstime_copyin(&bstat->bs_mtime, &bstat32->bs_mtime) ||
+ xfs_ioctl32_bstime_copyin(&bstat->bs_ctime, &bstat32->bs_ctime) ||
+ get_user(bstat->bs_blocks, &bstat32->bs_size) ||
+ get_user(bstat->bs_xflags, &bstat32->bs_size) ||
+ get_user(bstat->bs_extsize, &bstat32->bs_extsize) ||
+ get_user(bstat->bs_extents, &bstat32->bs_extents) ||
+ get_user(bstat->bs_gen, &bstat32->bs_gen) ||
+ get_user(bstat->bs_projid, &bstat32->bs_projid) ||
+ get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask) ||
+ get_user(bstat->bs_dmstate, &bstat32->bs_dmstate) ||
+ get_user(bstat->bs_aextents, &bstat32->bs_aextents))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
-typedef struct compat_xfs_bstime {
- __s32 tv_sec; /* seconds */
- __s32 tv_nsec; /* and nanoseconds */
-} compat_xfs_bstime_t;
+/* XFS_IOC_FSBULKSTAT and friends */
-STATIC int xfs_bstime_store_compat(
- compat_xfs_bstime_t __user *p32,
- const xfs_bstime_t *p)
+STATIC int
+xfs_bstime_store_compat(
+ compat_xfs_bstime_t __user *p32,
+ const xfs_bstime_t *p)
{
- __s32 sec32;
+ __s32 sec32;
sec32 = p->tv_sec;
if (put_user(sec32, &p32->tv_sec) ||
put_user(p->tv_nsec, &p32->tv_nsec))
- return -EFAULT;
+ return -XFS_ERROR(EFAULT);
return 0;
}
-typedef struct compat_xfs_bstat {
- __u64 bs_ino; /* inode number */
- __u16 bs_mode; /* type and mode */
- __u16 bs_nlink; /* number of links */
- __u32 bs_uid; /* user id */
- __u32 bs_gid; /* group id */
- __u32 bs_rdev; /* device value */
- __s32 bs_blksize; /* block size */
- __s64 bs_size; /* file size */
- compat_xfs_bstime_t bs_atime; /* access time */
- compat_xfs_bstime_t bs_mtime; /* modify time */
- compat_xfs_bstime_t bs_ctime; /* inode change time */
- int64_t bs_blocks; /* number of blocks */
- __u32 bs_xflags; /* extended flags */
- __s32 bs_extsize; /* extent size */
- __s32 bs_extents; /* number of extents */
- __u32 bs_gen; /* generation count */
- __u16 bs_projid; /* project id */
- unsigned char bs_pad[14]; /* pad space, unused */
- __u32 bs_dmevmask; /* DMIG event mask */
- __u16 bs_dmstate; /* DMIG state info */
- __u16 bs_aextents; /* attribute number of extents */
-} _PACKED compat_xfs_bstat_t;
-
-STATIC int xfs_bulkstat_one_fmt_compat(
+/* Return 0 on success or positive error (to xfs_bulkstat()) */
+STATIC int
+xfs_bulkstat_one_fmt_compat(
void __user *ubuffer,
+ int ubsize,
+ int *ubused,
const xfs_bstat_t *buffer)
{
- compat_xfs_bstat_t __user *p32 = ubuffer;
-
- if (put_user(buffer->bs_ino, &p32->bs_ino) ||
- put_user(buffer->bs_mode, &p32->bs_mode) ||
- put_user(buffer->bs_nlink, &p32->bs_nlink) ||
- put_user(buffer->bs_uid, &p32->bs_uid) ||
- put_user(buffer->bs_gid, &p32->bs_gid) ||
- put_user(buffer->bs_rdev, &p32->bs_rdev) ||
- put_user(buffer->bs_blksize, &p32->bs_blksize) ||
- put_user(buffer->bs_size, &p32->bs_size) ||
+ compat_xfs_bstat_t __user *p32 = ubuffer;
+
+ if (ubsize < sizeof(*p32))
+ return XFS_ERROR(ENOMEM);
+
+ if (put_user(buffer->bs_ino, &p32->bs_ino) ||
+ put_user(buffer->bs_mode, &p32->bs_mode) ||
+ put_user(buffer->bs_nlink, &p32->bs_nlink) ||
+ put_user(buffer->bs_uid, &p32->bs_uid) ||
+ put_user(buffer->bs_gid, &p32->bs_gid) ||
+ put_user(buffer->bs_rdev, &p32->bs_rdev) ||
+ put_user(buffer->bs_blksize, &p32->bs_blksize) ||
+ put_user(buffer->bs_size, &p32->bs_size) ||
xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) ||
xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) ||
xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) ||
- put_user(buffer->bs_blocks, &p32->bs_blocks) ||
- put_user(buffer->bs_xflags, &p32->bs_xflags) ||
- put_user(buffer->bs_extsize, &p32->bs_extsize) ||
- put_user(buffer->bs_extents, &p32->bs_extents) ||
- put_user(buffer->bs_gen, &p32->bs_gen) ||
- put_user(buffer->bs_projid, &p32->bs_projid) ||
- put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) ||
- put_user(buffer->bs_dmstate, &p32->bs_dmstate) ||
+ put_user(buffer->bs_blocks, &p32->bs_blocks) ||
+ put_user(buffer->bs_xflags, &p32->bs_xflags) ||
+ put_user(buffer->bs_extsize, &p32->bs_extsize) ||
+ put_user(buffer->bs_extents, &p32->bs_extents) ||
+ put_user(buffer->bs_gen, &p32->bs_gen) ||
+ put_user(buffer->bs_projid, &p32->bs_projid) ||
+ put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) ||
+ put_user(buffer->bs_dmstate, &p32->bs_dmstate) ||
put_user(buffer->bs_aextents, &p32->bs_aextents))
- return -EFAULT;
- return sizeof(*p32);
+ return XFS_ERROR(EFAULT);
+ if (ubused)
+ *ubused = sizeof(*p32);
+ return 0;
}
-
-
-typedef struct compat_xfs_fsop_bulkreq {
- compat_uptr_t lastip; /* last inode # pointer */
- __s32 icount; /* count of entries in buffer */
- compat_uptr_t ubuffer; /* user buffer for inode desc. */
- compat_uptr_t ocount; /* output count pointer */
-} compat_xfs_fsop_bulkreq_t;
-
-#define XFS_IOC_FSBULKSTAT_32 \
- _IOWR('X', 101, struct compat_xfs_fsop_bulkreq)
-#define XFS_IOC_FSBULKSTAT_SINGLE_32 \
- _IOWR('X', 102, struct compat_xfs_fsop_bulkreq)
-#define XFS_IOC_FSINUMBERS_32 \
- _IOWR('X', 103, struct compat_xfs_fsop_bulkreq)
+STATIC int
+xfs_bulkstat_one_compat(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t ino, /* inode number to get data for */
+ void __user *buffer, /* buffer to place output in */
+ int ubsize, /* size of buffer */
+ void *private_data, /* my private data */
+ xfs_daddr_t bno, /* starting bno of inode cluster */
+ int *ubused, /* bytes used by me */
+ void *dibuff, /* on-disk inode buffer */
+ int *stat) /* BULKSTAT_RV_... */
+{
+ return xfs_bulkstat_one_int(mp, ino, buffer, ubsize,
+ xfs_bulkstat_one_fmt_compat, bno,
+ ubused, dibuff, stat);
+}
/* copied from xfs_ioctl.c */
STATIC int
-xfs_ioc_bulkstat_compat(
- xfs_mount_t *mp,
- unsigned int cmd,
- void __user *arg)
+xfs_compat_ioc_bulkstat(
+ xfs_mount_t *mp,
+ unsigned int cmd,
+ compat_xfs_fsop_bulkreq_t __user *p32)
{
- compat_xfs_fsop_bulkreq_t __user *p32 = (void __user *)arg;
u32 addr;
xfs_fsop_bulkreq_t bulkreq;
int count; /* # of records returned */
@@ -270,20 +263,20 @@ xfs_ioc_bulkstat_compat(
/* should be called again (unused here, but used in dmapi) */
if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ return -XFS_ERROR(EPERM);
if (XFS_FORCED_SHUTDOWN(mp))
return -XFS_ERROR(EIO);
if (get_user(addr, &p32->lastip))
- return -EFAULT;
+ return -XFS_ERROR(EFAULT);
bulkreq.lastip = compat_ptr(addr);
if (get_user(bulkreq.icount, &p32->icount) ||
get_user(addr, &p32->ubuffer))
- return -EFAULT;
+ return -XFS_ERROR(EFAULT);
bulkreq.ubuffer = compat_ptr(addr);
if (get_user(addr, &p32->ocount))
- return -EFAULT;
+ return -XFS_ERROR(EFAULT);
bulkreq.ocount = compat_ptr(addr);
if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64)))
@@ -295,17 +288,22 @@ xfs_ioc_bulkstat_compat(
if (bulkreq.ubuffer == NULL)
return -XFS_ERROR(EINVAL);
- if (cmd == XFS_IOC_FSINUMBERS)
+ if (cmd == XFS_IOC_FSINUMBERS_32) {
error = xfs_inumbers(mp, &inlast, &count,
bulkreq.ubuffer, xfs_inumbers_fmt_compat);
- else {
- /* declare a var to get a warning in case the type changes */
- bulkstat_one_fmt_pf formatter = xfs_bulkstat_one_fmt_compat;
+ } else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {
+ int res;
+
+ error = xfs_bulkstat_one_compat(mp, inlast, bulkreq.ubuffer,
+ sizeof(compat_xfs_bstat_t),
+ NULL, 0, NULL, NULL, &res);
+ } else if (cmd == XFS_IOC_FSBULKSTAT_32) {
error = xfs_bulkstat(mp, &inlast, &count,
- xfs_bulkstat_one, formatter,
+ xfs_bulkstat_one_compat, NULL,
sizeof(compat_xfs_bstat_t), bulkreq.ubuffer,
BULKSTAT_FG_QUICK, &done);
- }
+ } else
+ error = XFS_ERROR(EINVAL);
if (error)
return -error;
@@ -321,63 +319,306 @@ xfs_ioc_bulkstat_compat(
return 0;
}
+STATIC int
+xfs_compat_handlereq_copyin(
+ xfs_fsop_handlereq_t *hreq,
+ compat_xfs_fsop_handlereq_t __user *arg32)
+{
+ compat_xfs_fsop_handlereq_t hreq32;
+
+ if (copy_from_user(&hreq32, arg32, sizeof(compat_xfs_fsop_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+
+ hreq->fd = hreq32.fd;
+ hreq->path = compat_ptr(hreq32.path);
+ hreq->oflags = hreq32.oflags;
+ hreq->ihandle = compat_ptr(hreq32.ihandle);
+ hreq->ihandlen = hreq32.ihandlen;
+ hreq->ohandle = compat_ptr(hreq32.ohandle);
+ hreq->ohandlen = compat_ptr(hreq32.ohandlen);
+ return 0;
+}
-typedef struct compat_xfs_fsop_handlereq {
- __u32 fd; /* fd for FD_TO_HANDLE */
- compat_uptr_t path; /* user pathname */
- __u32 oflags; /* open flags */
- compat_uptr_t ihandle; /* user supplied handle */
- __u32 ihandlen; /* user supplied length */
- compat_uptr_t ohandle; /* user buffer for handle */
- compat_uptr_t ohandlen; /* user buffer length */
-} compat_xfs_fsop_handlereq_t;
-
-#define XFS_IOC_PATH_TO_FSHANDLE_32 \
- _IOWR('X', 104, struct compat_xfs_fsop_handlereq)
-#define XFS_IOC_PATH_TO_HANDLE_32 \
- _IOWR('X', 105, struct compat_xfs_fsop_handlereq)
-#define XFS_IOC_FD_TO_HANDLE_32 \
- _IOWR('X', 106, struct compat_xfs_fsop_handlereq)
-#define XFS_IOC_OPEN_BY_HANDLE_32 \
- _IOWR('X', 107, struct compat_xfs_fsop_handlereq)
-#define XFS_IOC_READLINK_BY_HANDLE_32 \
- _IOWR('X', 108, struct compat_xfs_fsop_handlereq)
-
-STATIC unsigned long xfs_ioctl32_fshandle(unsigned long arg)
+/*
+ * Convert userspace handle data into inode.
+ *
+ * We use the fact that all the fsop_handlereq ioctl calls have a data
+ * structure argument whose first component is always a xfs_fsop_handlereq_t,
+ * so we can pass that sub structure into this handy, shared routine.
+ *
+ * If no error, caller must always iput the returned inode.
+ */
+STATIC int
+xfs_vget_fsop_handlereq_compat(
+ xfs_mount_t *mp,
+ struct inode *parinode, /* parent inode pointer */
+ compat_xfs_fsop_handlereq_t *hreq,
+ struct inode **inode)
{
- compat_xfs_fsop_handlereq_t __user *p32 = (void __user *)arg;
- xfs_fsop_handlereq_t __user *p = compat_alloc_user_space(sizeof(*p));
- u32 addr;
-
- if (copy_in_user(&p->fd, &p32->fd, sizeof(__u32)) ||
- get_user(addr, &p32->path) ||
- put_user(compat_ptr(addr), &p->path) ||
- copy_in_user(&p->oflags, &p32->oflags, sizeof(__u32)) ||
- get_user(addr, &p32->ihandle) ||
- put_user(compat_ptr(addr), &p->ihandle) ||
- copy_in_user(&p->ihandlen, &p32->ihandlen, sizeof(__u32)) ||
- get_user(addr, &p32->ohandle) ||
- put_user(compat_ptr(addr), &p->ohandle) ||
- get_user(addr, &p32->ohandlen) ||
- put_user(compat_ptr(addr), &p->ohandlen))
- return -EFAULT;
-
- return (unsigned long)p;
+ void __user *hanp;
+ size_t hlen;
+ xfs_fid_t *xfid;
+ xfs_handle_t *handlep;
+ xfs_handle_t handle;
+ xfs_inode_t *ip;
+ xfs_ino_t ino;
+ __u32 igen;
+ int error;
+
+ /*
+ * Only allow handle opens under a directory.
+ */
+ if (!S_ISDIR(parinode->i_mode))
+ return XFS_ERROR(ENOTDIR);
+
+ hanp = compat_ptr(hreq->ihandle);
+ hlen = hreq->ihandlen;
+ handlep = &handle;
+
+ if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
+ return XFS_ERROR(EINVAL);
+ if (copy_from_user(handlep, hanp, hlen))
+ return XFS_ERROR(EFAULT);
+ if (hlen < sizeof(*handlep))
+ memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen);
+ if (hlen > sizeof(handlep->ha_fsid)) {
+ if (handlep->ha_fid.fid_len !=
+ (hlen - sizeof(handlep->ha_fsid) -
+ sizeof(handlep->ha_fid.fid_len)) ||
+ handlep->ha_fid.fid_pad)
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Crack the handle, obtain the inode # & generation #
+ */
+ xfid = (struct xfs_fid *)&handlep->ha_fid;
+ if (xfid->fid_len == sizeof(*xfid) - sizeof(xfid->fid_len)) {
+ ino = xfid->fid_ino;
+ igen = xfid->fid_gen;
+ } else {
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Get the XFS inode, building a Linux inode to go with it.
+ */
+ error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
+ if (error)
+ return error;
+ if (ip == NULL)
+ return XFS_ERROR(EIO);
+ if (ip->i_d.di_gen != igen) {
+ xfs_iput_new(ip, XFS_ILOCK_SHARED);
+ return XFS_ERROR(ENOENT);
+ }
+
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ *inode = VFS_I(ip);
+ return 0;
}
+STATIC int
+xfs_compat_attrlist_by_handle(
+ xfs_mount_t *mp,
+ void __user *arg,
+ struct inode *parinode)
+{
+ int error;
+ attrlist_cursor_kern_t *cursor;
+ compat_xfs_fsop_attrlist_handlereq_t al_hreq;
+ struct inode *inode;
+ char *kbuf;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -XFS_ERROR(EPERM);
+ if (copy_from_user(&al_hreq, arg,
+ sizeof(compat_xfs_fsop_attrlist_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+ if (al_hreq.buflen > XATTR_LIST_MAX)
+ return -XFS_ERROR(EINVAL);
+
+ /*
+ * Reject flags, only allow namespaces.
+ */
+ if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
+ return -XFS_ERROR(EINVAL);
+
+ error = xfs_vget_fsop_handlereq_compat(mp, parinode, &al_hreq.hreq,
+ &inode);
+ if (error)
+ goto out;
+
+ kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL);
+ if (!kbuf)
+ goto out_vn_rele;
+
+ cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
+ error = xfs_attr_list(XFS_I(inode), kbuf, al_hreq.buflen,
+ al_hreq.flags, cursor);
+ if (error)
+ goto out_kfree;
+
+ if (copy_to_user(compat_ptr(al_hreq.buffer), kbuf, al_hreq.buflen))
+ error = -EFAULT;
+
+ out_kfree:
+ kfree(kbuf);
+ out_vn_rele:
+ iput(inode);
+ out:
+ return -error;
+}
-STATIC long
-xfs_compat_ioctl(
- int mode,
- struct file *file,
- unsigned cmd,
- unsigned long arg)
+STATIC int
+xfs_compat_attrmulti_by_handle(
+ xfs_mount_t *mp,
+ void __user *arg,
+ struct inode *parinode)
+{
+ int error;
+ compat_xfs_attr_multiop_t *ops;
+ compat_xfs_fsop_attrmulti_handlereq_t am_hreq;
+ struct inode *inode;
+ unsigned int i, size;
+ char *attr_name;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -XFS_ERROR(EPERM);
+ if (copy_from_user(&am_hreq, arg,
+ sizeof(compat_xfs_fsop_attrmulti_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_vget_fsop_handlereq_compat(mp, parinode, &am_hreq.hreq,
+ &inode);
+ if (error)
+ goto out;
+
+ error = E2BIG;
+ size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t);
+ if (!size || size > 16 * PAGE_SIZE)
+ goto out_vn_rele;
+
+ error = ENOMEM;
+ ops = kmalloc(size, GFP_KERNEL);
+ if (!ops)
+ goto out_vn_rele;
+
+ error = EFAULT;
+ if (copy_from_user(ops, compat_ptr(am_hreq.ops), size))
+ goto out_kfree_ops;
+
+ attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
+ if (!attr_name)
+ goto out_kfree_ops;
+
+
+ error = 0;
+ for (i = 0; i < am_hreq.opcount; i++) {
+ ops[i].am_error = strncpy_from_user(attr_name,
+ compat_ptr(ops[i].am_attrname),
+ MAXNAMELEN);
+ if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
+ error = -ERANGE;
+ if (ops[i].am_error < 0)
+ break;
+
+ switch (ops[i].am_opcode) {
+ case ATTR_OP_GET:
+ ops[i].am_error = xfs_attrmulti_attr_get(inode,
+ attr_name,
+ compat_ptr(ops[i].am_attrvalue),
+ &ops[i].am_length, ops[i].am_flags);
+ break;
+ case ATTR_OP_SET:
+ ops[i].am_error = xfs_attrmulti_attr_set(inode,
+ attr_name,
+ compat_ptr(ops[i].am_attrvalue),
+ ops[i].am_length, ops[i].am_flags);
+ break;
+ case ATTR_OP_REMOVE:
+ ops[i].am_error = xfs_attrmulti_attr_remove(inode,
+ attr_name, ops[i].am_flags);
+ break;
+ default:
+ ops[i].am_error = EINVAL;
+ }
+ }
+
+ if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))
+ error = XFS_ERROR(EFAULT);
+
+ kfree(attr_name);
+ out_kfree_ops:
+ kfree(ops);
+ out_vn_rele:
+ iput(inode);
+ out:
+ return -error;
+}
+
+STATIC int
+xfs_compat_fssetdm_by_handle(
+ xfs_mount_t *mp,
+ void __user *arg,
+ struct inode *parinode)
+{
+ int error;
+ struct fsdmidata fsd;
+ compat_xfs_fsop_setdm_handlereq_t dmhreq;
+ struct inode *inode;
+
+ if (!capable(CAP_MKNOD))
+ return -XFS_ERROR(EPERM);
+ if (copy_from_user(&dmhreq, arg,
+ sizeof(compat_xfs_fsop_setdm_handlereq_t)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_vget_fsop_handlereq_compat(mp, parinode, &dmhreq.hreq,
+ &inode);
+ if (error)
+ return -error;
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ error = -XFS_ERROR(EPERM);
+ goto out;
+ }
+
+ if (copy_from_user(&fsd, compat_ptr(dmhreq.data), sizeof(fsd))) {
+ error = -XFS_ERROR(EFAULT);
+ goto out;
+ }
+
+ error = -xfs_set_dmattrs(XFS_I(inode), fsd.fsd_dmevmask,
+ fsd.fsd_dmstate);
+
+out:
+ iput(inode);
+ return error;
+}
+
+long
+xfs_file_compat_ioctl(
+ struct file *filp,
+ unsigned cmd,
+ unsigned long p)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- int error;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_mount *mp = ip->i_mount;
+ void __user *arg = (void __user *)p;
+ int ioflags = 0;
+ int error;
+
+ if (filp->f_mode & FMODE_NOCMTIME)
+ ioflags |= IO_INVIS;
+
+ xfs_itrace_entry(ip);
switch (cmd) {
+ /* No size or alignment issues on any arch */
case XFS_IOC_DIOINFO:
case XFS_IOC_FSGEOMETRY:
case XFS_IOC_FSGETXATTR:
@@ -387,48 +628,18 @@ xfs_compat_ioctl(
case XFS_IOC_GETBMAP:
case XFS_IOC_GETBMAPA:
case XFS_IOC_GETBMAPX:
-/* not handled
- case XFS_IOC_FSSETDM_BY_HANDLE:
- case XFS_IOC_ATTRLIST_BY_HANDLE:
- case XFS_IOC_ATTRMULTI_BY_HANDLE:
-*/
case XFS_IOC_FSCOUNTS:
case XFS_IOC_SET_RESBLKS:
case XFS_IOC_GET_RESBLKS:
- case XFS_IOC_FSGROWFSDATA:
case XFS_IOC_FSGROWFSLOG:
- case XFS_IOC_FSGROWFSRT:
case XFS_IOC_FREEZE:
case XFS_IOC_THAW:
case XFS_IOC_GOINGDOWN:
case XFS_IOC_ERROR_INJECTION:
case XFS_IOC_ERROR_CLEARALL:
- break;
-
- case XFS_IOC32_GETXFLAGS:
- case XFS_IOC32_SETXFLAGS:
- case XFS_IOC32_GETVERSION:
- cmd = _NATIVE_IOC(cmd, long);
- break;
-#ifdef BROKEN_X86_ALIGNMENT
- /* xfs_flock_t has wrong u32 vs u64 alignment */
- case XFS_IOC_ALLOCSP_32:
- case XFS_IOC_FREESP_32:
- case XFS_IOC_ALLOCSP64_32:
- case XFS_IOC_FREESP64_32:
- case XFS_IOC_RESVSP_32:
- case XFS_IOC_UNRESVSP_32:
- case XFS_IOC_RESVSP64_32:
- case XFS_IOC_UNRESVSP64_32:
- arg = xfs_ioctl32_flock(arg);
- cmd = _NATIVE_IOC(cmd, struct xfs_flock64);
- break;
- case XFS_IOC_FSGEOMETRY_V1_32:
- arg = xfs_ioctl32_geom_v1(arg);
- cmd = _NATIVE_IOC(cmd, struct xfs_fsop_geom_v1);
- break;
-
-#else /* These are handled fine if no alignment issues */
+ return xfs_file_ioctl(filp, cmd, p);
+#ifndef BROKEN_X86_ALIGNMENT
+ /* These are handled fine if no alignment issues */
case XFS_IOC_ALLOCSP:
case XFS_IOC_FREESP:
case XFS_IOC_RESVSP:
@@ -438,51 +649,97 @@ xfs_compat_ioctl(
case XFS_IOC_RESVSP64:
case XFS_IOC_UNRESVSP64:
case XFS_IOC_FSGEOMETRY_V1:
- break;
+ case XFS_IOC_FSGROWFSDATA:
+ case XFS_IOC_FSGROWFSRT:
+ return xfs_file_ioctl(filp, cmd, p);
+#else
+ case XFS_IOC_ALLOCSP_32:
+ case XFS_IOC_FREESP_32:
+ case XFS_IOC_ALLOCSP64_32:
+ case XFS_IOC_FREESP64_32:
+ case XFS_IOC_RESVSP_32:
+ case XFS_IOC_UNRESVSP_32:
+ case XFS_IOC_RESVSP64_32:
+ case XFS_IOC_UNRESVSP64_32: {
+ struct xfs_flock64 bf;
- /* xfs_bstat_t still has wrong u32 vs u64 alignment */
- case XFS_IOC_SWAPEXT:
- break;
+ if (xfs_compat_flock64_copyin(&bf, arg))
+ return -XFS_ERROR(EFAULT);
+ cmd = _NATIVE_IOC(cmd, struct xfs_flock64);
+ return xfs_ioc_space(ip, inode, filp, ioflags, cmd, &bf);
+ }
+ case XFS_IOC_FSGEOMETRY_V1_32:
+ return xfs_compat_ioc_fsgeometry_v1(mp, arg);
+ case XFS_IOC_FSGROWFSDATA_32: {
+ struct xfs_growfs_data in;
+
+ if (xfs_compat_growfs_data_copyin(&in, arg))
+ return -XFS_ERROR(EFAULT);
+ error = xfs_growfs_data(mp, &in);
+ return -error;
+ }
+ case XFS_IOC_FSGROWFSRT_32: {
+ struct xfs_growfs_rt in;
+ if (xfs_compat_growfs_rt_copyin(&in, arg))
+ return -XFS_ERROR(EFAULT);
+ error = xfs_growfs_rt(mp, &in);
+ return -error;
+ }
#endif
+ /* long changes size, but xfs only copiese out 32 bits */
+ case XFS_IOC_GETXFLAGS_32:
+ case XFS_IOC_SETXFLAGS_32:
+ case XFS_IOC_GETVERSION_32:
+ cmd = _NATIVE_IOC(cmd, long);
+ return xfs_file_ioctl(filp, cmd, p);
+ case XFS_IOC_SWAPEXT: {
+ struct xfs_swapext sxp;
+ struct compat_xfs_swapext __user *sxu = arg;
+
+ /* Bulk copy in up to the sx_stat field, then copy bstat */
+ if (copy_from_user(&sxp, sxu,
+ offsetof(struct xfs_swapext, sx_stat)) ||
+ xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
+ return -XFS_ERROR(EFAULT);
+ error = xfs_swapext(&sxp);
+ return -error;
+ }
case XFS_IOC_FSBULKSTAT_32:
case XFS_IOC_FSBULKSTAT_SINGLE_32:
case XFS_IOC_FSINUMBERS_32:
- cmd = _NATIVE_IOC(cmd, struct xfs_fsop_bulkreq);
- return xfs_ioc_bulkstat_compat(XFS_I(inode)->i_mount,
- cmd, (void __user*)arg);
+ return xfs_compat_ioc_bulkstat(mp, cmd, arg);
case XFS_IOC_FD_TO_HANDLE_32:
case XFS_IOC_PATH_TO_HANDLE_32:
- case XFS_IOC_PATH_TO_FSHANDLE_32:
- case XFS_IOC_OPEN_BY_HANDLE_32:
- case XFS_IOC_READLINK_BY_HANDLE_32:
- arg = xfs_ioctl32_fshandle(arg);
+ case XFS_IOC_PATH_TO_FSHANDLE_32: {
+ struct xfs_fsop_handlereq hreq;
+
+ if (xfs_compat_handlereq_copyin(&hreq, arg))
+ return -XFS_ERROR(EFAULT);
cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq);
- break;
- default:
- return -ENOIOCTLCMD;
+ return xfs_find_handle(cmd, &hreq);
}
+ case XFS_IOC_OPEN_BY_HANDLE_32: {
+ struct xfs_fsop_handlereq hreq;
- error = xfs_ioctl(XFS_I(inode), file, mode, cmd, (void __user *)arg);
- xfs_iflags_set(XFS_I(inode), XFS_IMODIFIED);
-
- return error;
-}
-
-long
-xfs_file_compat_ioctl(
- struct file *file,
- unsigned cmd,
- unsigned long arg)
-{
- return xfs_compat_ioctl(0, file, cmd, arg);
-}
+ if (xfs_compat_handlereq_copyin(&hreq, arg))
+ return -XFS_ERROR(EFAULT);
+ return xfs_open_by_handle(mp, &hreq, filp, inode);
+ }
+ case XFS_IOC_READLINK_BY_HANDLE_32: {
+ struct xfs_fsop_handlereq hreq;
-long
-xfs_file_compat_invis_ioctl(
- struct file *file,
- unsigned cmd,
- unsigned long arg)
-{
- return xfs_compat_ioctl(IO_INVIS, file, cmd, arg);
+ if (xfs_compat_handlereq_copyin(&hreq, arg))
+ return -XFS_ERROR(EFAULT);
+ return xfs_readlink_by_handle(mp, &hreq, inode);
+ }
+ case XFS_IOC_ATTRLIST_BY_HANDLE_32:
+ return xfs_compat_attrlist_by_handle(mp, arg, inode);
+ case XFS_IOC_ATTRMULTI_BY_HANDLE_32:
+ return xfs_compat_attrmulti_by_handle(mp, arg, inode);
+ case XFS_IOC_FSSETDM_BY_HANDLE_32:
+ return xfs_compat_fssetdm_by_handle(mp, arg, inode);
+ default:
+ return -XFS_ERROR(ENOIOCTLCMD);
+ }
}
diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.h b/fs/xfs/linux-2.6/xfs_ioctl32.h
index 02de6e62ee37..1024c4f8ba0d 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl32.h
+++ b/fs/xfs/linux-2.6/xfs_ioctl32.h
@@ -18,7 +18,217 @@
#ifndef __XFS_IOCTL32_H__
#define __XFS_IOCTL32_H__
-extern long xfs_file_compat_ioctl(struct file *, unsigned, unsigned long);
-extern long xfs_file_compat_invis_ioctl(struct file *, unsigned, unsigned long);
+#include <linux/compat.h>
+
+/*
+ * on 32-bit arches, ioctl argument structures may have different sizes
+ * and/or alignment. We define compat structures which match the
+ * 32-bit sizes/alignments here, and their associated ioctl numbers.
+ *
+ * xfs_ioctl32.c contains routines to copy these structures in and out.
+ */
+
+/* stock kernel-level ioctls we support */
+#define XFS_IOC_GETXFLAGS_32 FS_IOC32_GETFLAGS
+#define XFS_IOC_SETXFLAGS_32 FS_IOC32_SETFLAGS
+#define XFS_IOC_GETVERSION_32 FS_IOC32_GETVERSION
+
+/*
+ * On intel, even if sizes match, alignment and/or padding may differ.
+ */
+#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
+#define BROKEN_X86_ALIGNMENT
+#define __compat_packed __attribute__((packed))
+#else
+#define __compat_packed
+#endif
+
+typedef struct compat_xfs_bstime {
+ compat_time_t tv_sec; /* seconds */
+ __s32 tv_nsec; /* and nanoseconds */
+} compat_xfs_bstime_t;
+
+typedef struct compat_xfs_bstat {
+ __u64 bs_ino; /* inode number */
+ __u16 bs_mode; /* type and mode */
+ __u16 bs_nlink; /* number of links */
+ __u32 bs_uid; /* user id */
+ __u32 bs_gid; /* group id */
+ __u32 bs_rdev; /* device value */
+ __s32 bs_blksize; /* block size */
+ __s64 bs_size; /* file size */
+ compat_xfs_bstime_t bs_atime; /* access time */
+ compat_xfs_bstime_t bs_mtime; /* modify time */
+ compat_xfs_bstime_t bs_ctime; /* inode change time */
+ int64_t bs_blocks; /* number of blocks */
+ __u32 bs_xflags; /* extended flags */
+ __s32 bs_extsize; /* extent size */
+ __s32 bs_extents; /* number of extents */
+ __u32 bs_gen; /* generation count */
+ __u16 bs_projid; /* project id */
+ unsigned char bs_pad[14]; /* pad space, unused */
+ __u32 bs_dmevmask; /* DMIG event mask */
+ __u16 bs_dmstate; /* DMIG state info */
+ __u16 bs_aextents; /* attribute number of extents */
+} __compat_packed compat_xfs_bstat_t;
+
+typedef struct compat_xfs_fsop_bulkreq {
+ compat_uptr_t lastip; /* last inode # pointer */
+ __s32 icount; /* count of entries in buffer */
+ compat_uptr_t ubuffer; /* user buffer for inode desc. */
+ compat_uptr_t ocount; /* output count pointer */
+} compat_xfs_fsop_bulkreq_t;
+
+#define XFS_IOC_FSBULKSTAT_32 \
+ _IOWR('X', 101, struct compat_xfs_fsop_bulkreq)
+#define XFS_IOC_FSBULKSTAT_SINGLE_32 \
+ _IOWR('X', 102, struct compat_xfs_fsop_bulkreq)
+#define XFS_IOC_FSINUMBERS_32 \
+ _IOWR('X', 103, struct compat_xfs_fsop_bulkreq)
+
+typedef struct compat_xfs_fsop_handlereq {
+ __u32 fd; /* fd for FD_TO_HANDLE */
+ compat_uptr_t path; /* user pathname */
+ __u32 oflags; /* open flags */
+ compat_uptr_t ihandle; /* user supplied handle */
+ __u32 ihandlen; /* user supplied length */
+ compat_uptr_t ohandle; /* user buffer for handle */
+ compat_uptr_t ohandlen; /* user buffer length */
+} compat_xfs_fsop_handlereq_t;
+
+#define XFS_IOC_PATH_TO_FSHANDLE_32 \
+ _IOWR('X', 104, struct compat_xfs_fsop_handlereq)
+#define XFS_IOC_PATH_TO_HANDLE_32 \
+ _IOWR('X', 105, struct compat_xfs_fsop_handlereq)
+#define XFS_IOC_FD_TO_HANDLE_32 \
+ _IOWR('X', 106, struct compat_xfs_fsop_handlereq)
+#define XFS_IOC_OPEN_BY_HANDLE_32 \
+ _IOWR('X', 107, struct compat_xfs_fsop_handlereq)
+#define XFS_IOC_READLINK_BY_HANDLE_32 \
+ _IOWR('X', 108, struct compat_xfs_fsop_handlereq)
+
+/* The bstat field in the swapext struct needs translation */
+typedef struct compat_xfs_swapext {
+ __int64_t sx_version; /* version */
+ __int64_t sx_fdtarget; /* fd of target file */
+ __int64_t sx_fdtmp; /* fd of tmp file */
+ xfs_off_t sx_offset; /* offset into file */
+ xfs_off_t sx_length; /* leng from offset */
+ char sx_pad[16]; /* pad space, unused */
+ compat_xfs_bstat_t sx_stat; /* stat of target b4 copy */
+} __compat_packed compat_xfs_swapext_t;
+
+#define XFS_IOC_SWAPEXT_32 _IOWR('X', 109, struct compat_xfs_swapext)
+
+typedef struct compat_xfs_fsop_attrlist_handlereq {
+ struct compat_xfs_fsop_handlereq hreq; /* handle interface structure */
+ struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */
+ __u32 flags; /* which namespace to use */
+ __u32 buflen; /* length of buffer supplied */
+ compat_uptr_t buffer; /* returned names */
+} __compat_packed compat_xfs_fsop_attrlist_handlereq_t;
+
+/* Note: actually this is read/write */
+#define XFS_IOC_ATTRLIST_BY_HANDLE_32 \
+ _IOW('X', 122, struct compat_xfs_fsop_attrlist_handlereq)
+
+/* am_opcodes defined in xfs_fs.h */
+typedef struct compat_xfs_attr_multiop {
+ __u32 am_opcode;
+ __s32 am_error;
+ compat_uptr_t am_attrname;
+ compat_uptr_t am_attrvalue;
+ __u32 am_length;
+ __u32 am_flags;
+} compat_xfs_attr_multiop_t;
+
+typedef struct compat_xfs_fsop_attrmulti_handlereq {
+ struct compat_xfs_fsop_handlereq hreq; /* handle interface structure */
+ __u32 opcount;/* count of following multiop */
+ /* ptr to compat_xfs_attr_multiop */
+ compat_uptr_t ops; /* attr_multi data */
+} compat_xfs_fsop_attrmulti_handlereq_t;
+
+#define XFS_IOC_ATTRMULTI_BY_HANDLE_32 \
+ _IOW('X', 123, struct compat_xfs_fsop_attrmulti_handlereq)
+
+typedef struct compat_xfs_fsop_setdm_handlereq {
+ struct compat_xfs_fsop_handlereq hreq; /* handle information */
+ /* ptr to struct fsdmidata */
+ compat_uptr_t data; /* DMAPI data */
+} compat_xfs_fsop_setdm_handlereq_t;
+
+#define XFS_IOC_FSSETDM_BY_HANDLE_32 \
+ _IOW('X', 121, struct compat_xfs_fsop_setdm_handlereq)
+
+#ifdef BROKEN_X86_ALIGNMENT
+/* on ia32 l_start is on a 32-bit boundary */
+typedef struct compat_xfs_flock64 {
+ __s16 l_type;
+ __s16 l_whence;
+ __s64 l_start __attribute__((packed));
+ /* len == 0 means until end of file */
+ __s64 l_len __attribute__((packed));
+ __s32 l_sysid;
+ __u32 l_pid;
+ __s32 l_pad[4]; /* reserve area */
+} compat_xfs_flock64_t;
+
+#define XFS_IOC_ALLOCSP_32 _IOW('X', 10, struct compat_xfs_flock64)
+#define XFS_IOC_FREESP_32 _IOW('X', 11, struct compat_xfs_flock64)
+#define XFS_IOC_ALLOCSP64_32 _IOW('X', 36, struct compat_xfs_flock64)
+#define XFS_IOC_FREESP64_32 _IOW('X', 37, struct compat_xfs_flock64)
+#define XFS_IOC_RESVSP_32 _IOW('X', 40, struct compat_xfs_flock64)
+#define XFS_IOC_UNRESVSP_32 _IOW('X', 41, struct compat_xfs_flock64)
+#define XFS_IOC_RESVSP64_32 _IOW('X', 42, struct compat_xfs_flock64)
+#define XFS_IOC_UNRESVSP64_32 _IOW('X', 43, struct compat_xfs_flock64)
+
+typedef struct compat_xfs_fsop_geom_v1 {
+ __u32 blocksize; /* filesystem (data) block size */
+ __u32 rtextsize; /* realtime extent size */
+ __u32 agblocks; /* fsblocks in an AG */
+ __u32 agcount; /* number of allocation groups */
+ __u32 logblocks; /* fsblocks in the log */
+ __u32 sectsize; /* (data) sector size, bytes */
+ __u32 inodesize; /* inode size in bytes */
+ __u32 imaxpct; /* max allowed inode space(%) */
+ __u64 datablocks; /* fsblocks in data subvolume */
+ __u64 rtblocks; /* fsblocks in realtime subvol */
+ __u64 rtextents; /* rt extents in realtime subvol*/
+ __u64 logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ __u32 sunit; /* stripe unit, fsblocks */
+ __u32 swidth; /* stripe width, fsblocks */
+ __s32 version; /* structure version */
+ __u32 flags; /* superblock version flags */
+ __u32 logsectsize; /* log sector size, bytes */
+ __u32 rtsectsize; /* realtime sector size, bytes */
+ __u32 dirblocksize; /* directory block size, bytes */
+} __attribute__((packed)) compat_xfs_fsop_geom_v1_t;
+
+#define XFS_IOC_FSGEOMETRY_V1_32 \
+ _IOR('X', 100, struct compat_xfs_fsop_geom_v1)
+
+typedef struct compat_xfs_inogrp {
+ __u64 xi_startino; /* starting inode number */
+ __s32 xi_alloccount; /* # bits set in allocmask */
+ __u64 xi_allocmask; /* mask of allocated inodes */
+} __attribute__((packed)) compat_xfs_inogrp_t;
+
+/* These growfs input structures have padding on the end, so must translate */
+typedef struct compat_xfs_growfs_data {
+ __u64 newblocks; /* new data subvol size, fsblocks */
+ __u32 imaxpct; /* new inode space percentage limit */
+} __attribute__((packed)) compat_xfs_growfs_data_t;
+
+typedef struct compat_xfs_growfs_rt {
+ __u64 newblocks; /* new realtime size, fsblocks */
+ __u32 extsize; /* new realtime extent size, fsblocks */
+} __attribute__((packed)) compat_xfs_growfs_rt_t;
+
+#define XFS_IOC_FSGROWFSDATA_32 _IOW('X', 110, struct compat_xfs_growfs_data)
+#define XFS_IOC_FSGROWFSRT_32 _IOW('X', 112, struct compat_xfs_growfs_rt)
+
+#endif /* BROKEN_X86_ALIGNMENT */
#endif /* __XFS_IOCTL32_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 095d271f3434..7aa53fefc67f 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -53,6 +53,7 @@
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/falloc.h>
+#include <linux/fiemap.h>
/*
* Bring the atime in the XFS inode uptodate.
@@ -64,14 +65,14 @@ xfs_synchronize_atime(
{
struct inode *inode = VFS_I(ip);
- if (inode) {
+ if (!(inode->i_state & I_CLEAR)) {
ip->i_d.di_atime.t_sec = (__int32_t)inode->i_atime.tv_sec;
ip->i_d.di_atime.t_nsec = (__int32_t)inode->i_atime.tv_nsec;
}
}
/*
- * If the linux inode exists, mark it dirty.
+ * If the linux inode is valid, mark it dirty.
* Used when commiting a dirty inode into a transaction so that
* the inode will get written back by the linux code
*/
@@ -81,7 +82,7 @@ xfs_mark_inode_dirty_sync(
{
struct inode *inode = VFS_I(ip);
- if (inode)
+ if (!(inode->i_state & (I_WILL_FREE|I_FREEING|I_CLEAR)))
mark_inode_dirty_sync(inode);
}
@@ -128,7 +129,7 @@ xfs_ichgtime(
if (sync_it) {
SYNCHRONIZE();
ip->i_update_core = 1;
- mark_inode_dirty_sync(inode);
+ xfs_mark_inode_dirty_sync(ip);
}
}
@@ -158,8 +159,6 @@ xfs_init_security(
}
error = xfs_attr_set(ip, name, value, length, ATTR_SECURE);
- if (!error)
- xfs_iflags_set(ip, XFS_IMODIFIED);
kfree(name);
kfree(value);
@@ -260,7 +259,6 @@ xfs_vn_mknod(
error = _ACL_INHERIT(inode, mode, default_acl);
if (unlikely(error))
goto out_cleanup_inode;
- xfs_iflags_set(ip, XFS_IMODIFIED);
_ACL_FREE(default_acl);
}
@@ -366,21 +364,17 @@ xfs_vn_link(
struct inode *dir,
struct dentry *dentry)
{
- struct inode *inode; /* inode of guy being linked to */
+ struct inode *inode = old_dentry->d_inode;
struct xfs_name name;
int error;
- inode = old_dentry->d_inode;
xfs_dentry_to_name(&name, dentry);
- igrab(inode);
error = xfs_link(XFS_I(dir), XFS_I(inode), &name);
- if (unlikely(error)) {
- iput(inode);
+ if (unlikely(error))
return -error;
- }
- xfs_iflags_set(XFS_I(dir), XFS_IMODIFIED);
+ atomic_inc(&inode->i_count);
d_instantiate(dentry, inode);
return 0;
}
@@ -601,7 +595,7 @@ xfs_vn_setattr(
struct dentry *dentry,
struct iattr *iattr)
{
- return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0, NULL);
+ return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0);
}
/*
@@ -642,7 +636,7 @@ xfs_vn_fallocate(
xfs_ilock(ip, XFS_IOLOCK_EXCL);
error = xfs_change_file_space(ip, XFS_IOC_RESVSP, &bf,
- 0, NULL, XFS_ATTR_NOLOCK);
+ 0, XFS_ATTR_NOLOCK);
if (!error && !(mode & FALLOC_FL_KEEP_SIZE) &&
offset + len > i_size_read(inode))
new_size = offset + len;
@@ -653,7 +647,7 @@ xfs_vn_fallocate(
iattr.ia_valid = ATTR_SIZE;
iattr.ia_size = new_size;
- error = xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK, NULL);
+ error = xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK);
}
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
@@ -661,6 +655,88 @@ out_error:
return error;
}
+#define XFS_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+
+/*
+ * Call fiemap helper to fill in user data.
+ * Returns positive errors to xfs_getbmap.
+ */
+STATIC int
+xfs_fiemap_format(
+ void **arg,
+ struct getbmapx *bmv,
+ int *full)
+{
+ int error;
+ struct fiemap_extent_info *fieinfo = *arg;
+ u32 fiemap_flags = 0;
+ u64 logical, physical, length;
+
+ /* Do nothing for a hole */
+ if (bmv->bmv_block == -1LL)
+ return 0;
+
+ logical = BBTOB(bmv->bmv_offset);
+ physical = BBTOB(bmv->bmv_block);
+ length = BBTOB(bmv->bmv_length);
+
+ if (bmv->bmv_oflags & BMV_OF_PREALLOC)
+ fiemap_flags |= FIEMAP_EXTENT_UNWRITTEN;
+ else if (bmv->bmv_oflags & BMV_OF_DELALLOC) {
+ fiemap_flags |= FIEMAP_EXTENT_DELALLOC;
+ physical = 0; /* no block yet */
+ }
+ if (bmv->bmv_oflags & BMV_OF_LAST)
+ fiemap_flags |= FIEMAP_EXTENT_LAST;
+
+ error = fiemap_fill_next_extent(fieinfo, logical, physical,
+ length, fiemap_flags);
+ if (error > 0) {
+ error = 0;
+ *full = 1; /* user array now full */
+ }
+
+ return -error;
+}
+
+STATIC int
+xfs_vn_fiemap(
+ struct inode *inode,
+ struct fiemap_extent_info *fieinfo,
+ u64 start,
+ u64 length)
+{
+ xfs_inode_t *ip = XFS_I(inode);
+ struct getbmapx bm;
+ int error;
+
+ error = fiemap_check_flags(fieinfo, XFS_FIEMAP_FLAGS);
+ if (error)
+ return error;
+
+ /* Set up bmap header for xfs internal routine */
+ bm.bmv_offset = BTOBB(start);
+ /* Special case for whole file */
+ if (length == FIEMAP_MAX_OFFSET)
+ bm.bmv_length = -1LL;
+ else
+ bm.bmv_length = BTOBB(length);
+
+ /* our formatter will tell xfs_getbmap when to stop. */
+ bm.bmv_count = MAXEXTNUM;
+ bm.bmv_iflags = BMV_IF_PREALLOC;
+ if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR)
+ bm.bmv_iflags |= BMV_IF_ATTRFORK;
+ if (!(fieinfo->fi_flags & FIEMAP_FLAG_SYNC))
+ bm.bmv_iflags |= BMV_IF_DELALLOC;
+
+ error = xfs_getbmap(ip, &bm, xfs_fiemap_format, fieinfo);
+ if (error)
+ return -error;
+
+ return 0;
+}
+
static const struct inode_operations xfs_inode_operations = {
.permission = xfs_vn_permission,
.truncate = xfs_vn_truncate,
@@ -671,6 +747,7 @@ static const struct inode_operations xfs_inode_operations = {
.removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.fallocate = xfs_vn_fallocate,
+ .fiemap = xfs_vn_fiemap,
};
static const struct inode_operations xfs_dir_inode_operations = {
@@ -766,12 +843,20 @@ xfs_diflags_to_iflags(
* When reading existing inodes from disk this is called directly
* from xfs_iget, when creating a new inode it is called from
* xfs_ialloc after setting up the inode.
+ *
+ * We are always called with an uninitialised linux inode here.
+ * We need to initialise the necessary fields and take a reference
+ * on it.
*/
void
xfs_setup_inode(
struct xfs_inode *ip)
{
- struct inode *inode = ip->i_vnode;
+ struct inode *inode = &ip->i_vnode;
+
+ inode->i_ino = ip->i_ino;
+ inode->i_state = I_NEW|I_LOCK;
+ inode_add_to_lists(ip->i_mount->m_super, inode);
inode->i_mode = ip->i_d.di_mode;
inode->i_nlink = ip->i_d.di_nlink;
@@ -799,7 +884,6 @@ xfs_setup_inode(
inode->i_ctime.tv_sec = ip->i_d.di_ctime.t_sec;
inode->i_ctime.tv_nsec = ip->i_d.di_ctime.t_nsec;
xfs_diflags_to_iflags(inode, ip);
- xfs_iflags_clear(ip, XFS_IMODIFIED);
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
diff --git a/fs/xfs/linux-2.6/xfs_iops.h b/fs/xfs/linux-2.6/xfs_iops.h
index 8b1a1e31dc21..ef41c92ce66e 100644
--- a/fs/xfs/linux-2.6/xfs_iops.h
+++ b/fs/xfs/linux-2.6/xfs_iops.h
@@ -22,7 +22,6 @@ struct xfs_inode;
extern const struct file_operations xfs_file_operations;
extern const struct file_operations xfs_dir_file_operations;
-extern const struct file_operations xfs_invis_file_operations;
extern ssize_t xfs_vn_listxattr(struct dentry *, char *data, size_t size);
diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h
index cc0f7b3a9795..507492d6dccd 100644
--- a/fs/xfs/linux-2.6/xfs_linux.h
+++ b/fs/xfs/linux-2.6/xfs_linux.h
@@ -21,18 +21,12 @@
#include <linux/types.h>
/*
- * Some types are conditional depending on the target system.
* XFS_BIG_BLKNOS needs block layer disk addresses to be 64 bits.
- * XFS_BIG_INUMS needs the VFS inode number to be 64 bits, as well
- * as requiring XFS_BIG_BLKNOS to be set.
+ * XFS_BIG_INUMS requires XFS_BIG_BLKNOS to be set.
*/
#if defined(CONFIG_LBD) || (BITS_PER_LONG == 64)
# define XFS_BIG_BLKNOS 1
-# if BITS_PER_LONG == 64
-# define XFS_BIG_INUMS 1
-# else
-# define XFS_BIG_INUMS 0
-# endif
+# define XFS_BIG_INUMS 1
#else
# define XFS_BIG_BLKNOS 0
# define XFS_BIG_INUMS 0
@@ -77,6 +71,7 @@
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/ctype.h>
+#include <linux/writeback.h>
#include <asm/page.h>
#include <asm/div64.h>
@@ -85,7 +80,6 @@
#include <asm/byteorder.h>
#include <asm/unaligned.h>
-#include <xfs_vfs.h>
#include <xfs_cred.h>
#include <xfs_vnode.h>
#include <xfs_stats.h>
@@ -107,7 +101,6 @@
#undef HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */
#endif
-#define restricted_chown xfs_params.restrict_chown.val
#define irix_sgid_inherit xfs_params.sgid_inherit.val
#define irix_symlink_mode xfs_params.symlink_mode.val
#define xfs_panic_mask xfs_params.panic_mask.val
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 1957e5357d04..7e90daa0d1d1 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -51,7 +51,6 @@
#include "xfs_vnodeops.h"
#include <linux/capability.h>
-#include <linux/mount.h>
#include <linux/writeback.h>
@@ -243,7 +242,7 @@ xfs_read(
if (unlikely(ioflags & IO_ISDIRECT)) {
if (inode->i_mapping->nrpages)
- ret = xfs_flushinval_pages(ip, (*offset & PAGE_CACHE_MASK),
+ ret = -xfs_flushinval_pages(ip, (*offset & PAGE_CACHE_MASK),
-1, FI_REMAPF_LOCKED);
mutex_unlock(&inode->i_mutex);
if (ret) {
@@ -668,15 +667,8 @@ start:
if (new_size > xip->i_size)
xip->i_new_size = new_size;
- /*
- * We're not supposed to change timestamps in readonly-mounted
- * filesystems. Throw it away if anyone asks us.
- */
- if (likely(!(ioflags & IO_INVIS) &&
- !mnt_want_write(file->f_path.mnt))) {
+ if (likely(!(ioflags & IO_INVIS)))
xfs_ichgtime(xip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- mnt_drop_write(file->f_path.mnt);
- }
/*
* If the offset is beyond the size of the file, we have a couple
@@ -715,7 +707,6 @@ start:
}
}
-retry:
/* We can write back this queue in page reclaim */
current->backing_dev_info = mapping->backing_dev_info;
@@ -771,6 +762,17 @@ retry:
if (ret == -EIOCBQUEUED && !(ioflags & IO_ISAIO))
ret = wait_on_sync_kiocb(iocb);
+ isize = i_size_read(inode);
+ if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
+ *offset = isize;
+
+ if (*offset > xip->i_size) {
+ xfs_ilock(xip, XFS_ILOCK_EXCL);
+ if (*offset > xip->i_size)
+ xip->i_size = *offset;
+ xfs_iunlock(xip, XFS_ILOCK_EXCL);
+ }
+
if (ret == -ENOSPC &&
DM_EVENT_ENABLED(xip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) {
xfs_iunlock(xip, iolock);
@@ -784,20 +786,7 @@ retry:
xfs_ilock(xip, iolock);
if (error)
goto out_unlock_internal;
- pos = xip->i_size;
- ret = 0;
- goto retry;
- }
-
- isize = i_size_read(inode);
- if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
- *offset = isize;
-
- if (*offset > xip->i_size) {
- xfs_ilock(xip, XFS_ILOCK_EXCL);
- if (*offset > xip->i_size)
- xip->i_size = *offset;
- xfs_iunlock(xip, XFS_ILOCK_EXCL);
+ goto start;
}
error = -ret;
@@ -855,13 +844,7 @@ retry:
int
xfs_bdstrat_cb(struct xfs_buf *bp)
{
- xfs_mount_t *mp;
-
- mp = XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *);
- if (!XFS_FORCED_SHUTDOWN(mp)) {
- xfs_buf_iorequest(bp);
- return 0;
- } else {
+ if (XFS_FORCED_SHUTDOWN(bp->b_mount)) {
xfs_buftrace("XFS__BDSTRAT IOERROR", bp);
/*
* Metadata write that didn't get logged but
@@ -874,6 +857,9 @@ xfs_bdstrat_cb(struct xfs_buf *bp)
else
return (xfs_bioerror(bp));
}
+
+ xfs_buf_iorequest(bp);
+ return 0;
}
/*
diff --git a/fs/xfs/linux-2.6/xfs_stats.c b/fs/xfs/linux-2.6/xfs_stats.c
index 3d5b67c075c7..c3526d445f6a 100644
--- a/fs/xfs/linux-2.6/xfs_stats.c
+++ b/fs/xfs/linux-2.6/xfs_stats.c
@@ -53,11 +53,15 @@ xfs_read_xfsstats(
{ "icluster", XFSSTAT_END_INODE_CLUSTER },
{ "vnodes", XFSSTAT_END_VNODE_OPS },
{ "buf", XFSSTAT_END_BUF },
+ { "abtb2", XFSSTAT_END_ABTB_V2 },
+ { "abtc2", XFSSTAT_END_ABTC_V2 },
+ { "bmbt2", XFSSTAT_END_BMBT_V2 },
+ { "ibt2", XFSSTAT_END_IBT_V2 },
};
/* Loop over all stats groups */
for (i=j=len = 0; i < ARRAY_SIZE(xstats); i++) {
- len += sprintf(buffer + len, xstats[i].desc);
+ len += sprintf(buffer + len, "%s", xstats[i].desc);
/* inner loop does each group */
while (j < xstats[i].endpoint) {
val = 0;
diff --git a/fs/xfs/linux-2.6/xfs_stats.h b/fs/xfs/linux-2.6/xfs_stats.h
index e83820febc9f..736854b1ca1a 100644
--- a/fs/xfs/linux-2.6/xfs_stats.h
+++ b/fs/xfs/linux-2.6/xfs_stats.h
@@ -118,6 +118,71 @@ struct xfsstats {
__uint32_t xb_page_retries;
__uint32_t xb_page_found;
__uint32_t xb_get_read;
+/* Version 2 btree counters */
+#define XFSSTAT_END_ABTB_V2 (XFSSTAT_END_BUF+15)
+ __uint32_t xs_abtb_2_lookup;
+ __uint32_t xs_abtb_2_compare;
+ __uint32_t xs_abtb_2_insrec;
+ __uint32_t xs_abtb_2_delrec;
+ __uint32_t xs_abtb_2_newroot;
+ __uint32_t xs_abtb_2_killroot;
+ __uint32_t xs_abtb_2_increment;
+ __uint32_t xs_abtb_2_decrement;
+ __uint32_t xs_abtb_2_lshift;
+ __uint32_t xs_abtb_2_rshift;
+ __uint32_t xs_abtb_2_split;
+ __uint32_t xs_abtb_2_join;
+ __uint32_t xs_abtb_2_alloc;
+ __uint32_t xs_abtb_2_free;
+ __uint32_t xs_abtb_2_moves;
+#define XFSSTAT_END_ABTC_V2 (XFSSTAT_END_ABTB_V2+15)
+ __uint32_t xs_abtc_2_lookup;
+ __uint32_t xs_abtc_2_compare;
+ __uint32_t xs_abtc_2_insrec;
+ __uint32_t xs_abtc_2_delrec;
+ __uint32_t xs_abtc_2_newroot;
+ __uint32_t xs_abtc_2_killroot;
+ __uint32_t xs_abtc_2_increment;
+ __uint32_t xs_abtc_2_decrement;
+ __uint32_t xs_abtc_2_lshift;
+ __uint32_t xs_abtc_2_rshift;
+ __uint32_t xs_abtc_2_split;
+ __uint32_t xs_abtc_2_join;
+ __uint32_t xs_abtc_2_alloc;
+ __uint32_t xs_abtc_2_free;
+ __uint32_t xs_abtc_2_moves;
+#define XFSSTAT_END_BMBT_V2 (XFSSTAT_END_ABTC_V2+15)
+ __uint32_t xs_bmbt_2_lookup;
+ __uint32_t xs_bmbt_2_compare;
+ __uint32_t xs_bmbt_2_insrec;
+ __uint32_t xs_bmbt_2_delrec;
+ __uint32_t xs_bmbt_2_newroot;
+ __uint32_t xs_bmbt_2_killroot;
+ __uint32_t xs_bmbt_2_increment;
+ __uint32_t xs_bmbt_2_decrement;
+ __uint32_t xs_bmbt_2_lshift;
+ __uint32_t xs_bmbt_2_rshift;
+ __uint32_t xs_bmbt_2_split;
+ __uint32_t xs_bmbt_2_join;
+ __uint32_t xs_bmbt_2_alloc;
+ __uint32_t xs_bmbt_2_free;
+ __uint32_t xs_bmbt_2_moves;
+#define XFSSTAT_END_IBT_V2 (XFSSTAT_END_BMBT_V2+15)
+ __uint32_t xs_ibt_2_lookup;
+ __uint32_t xs_ibt_2_compare;
+ __uint32_t xs_ibt_2_insrec;
+ __uint32_t xs_ibt_2_delrec;
+ __uint32_t xs_ibt_2_newroot;
+ __uint32_t xs_ibt_2_killroot;
+ __uint32_t xs_ibt_2_increment;
+ __uint32_t xs_ibt_2_decrement;
+ __uint32_t xs_ibt_2_lshift;
+ __uint32_t xs_ibt_2_rshift;
+ __uint32_t xs_ibt_2_split;
+ __uint32_t xs_ibt_2_join;
+ __uint32_t xs_ibt_2_alloc;
+ __uint32_t xs_ibt_2_free;
+ __uint32_t xs_ibt_2_moves;
/* Extra precision counters */
__uint64_t xs_xstrat_bytes;
__uint64_t xs_write_bytes;
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index 37ebe36056eb..36f6cc703ef2 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -18,7 +18,6 @@
#include "xfs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
-#include "xfs_clnt.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
@@ -36,6 +35,7 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_ialloc.h"
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
@@ -48,7 +48,6 @@
#include "xfs_buf_item.h"
#include "xfs_utils.h"
#include "xfs_vnodeops.h"
-#include "xfs_vfsops.h"
#include "xfs_version.h"
#include "xfs_log_priv.h"
#include "xfs_trans_priv.h"
@@ -58,6 +57,7 @@
#include "xfs_extfree_item.h"
#include "xfs_mru_cache.h"
#include "xfs_inode_item.h"
+#include "xfs_sync.h"
#include <linux/namei.h>
#include <linux/init.h>
@@ -70,36 +70,9 @@
static struct quotactl_ops xfs_quotactl_operations;
static struct super_operations xfs_super_operations;
-static kmem_zone_t *xfs_vnode_zone;
static kmem_zone_t *xfs_ioend_zone;
mempool_t *xfs_ioend_pool;
-STATIC struct xfs_mount_args *
-xfs_args_allocate(
- struct super_block *sb,
- int silent)
-{
- struct xfs_mount_args *args;
-
- args = kzalloc(sizeof(struct xfs_mount_args), GFP_KERNEL);
- if (!args)
- return NULL;
-
- args->logbufs = args->logbufsize = -1;
- strncpy(args->fsname, sb->s_id, MAXNAMELEN);
-
- /* Copy the already-parsed mount(2) flags we're interested in */
- if (sb->s_flags & MS_DIRSYNC)
- args->flags |= XFSMNT_DIRSYNC;
- if (sb->s_flags & MS_SYNCHRONOUS)
- args->flags |= XFSMNT_WSYNC;
- if (silent)
- args->flags |= XFSMNT_QUIET;
- args->flags |= XFSMNT_32BITINODES;
-
- return args;
-}
-
#define MNTOPT_LOGBUFS "logbufs" /* number of XFS log buffers */
#define MNTOPT_LOGBSIZE "logbsize" /* size of XFS log buffers */
#define MNTOPT_LOGDEV "logdev" /* log device */
@@ -188,26 +161,54 @@ suffix_strtoul(char *s, char **endp, unsigned int base)
return simple_strtoul((const char *)s, endp, base) << shift_left_factor;
}
+/*
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock has _not_ yet been read in.
+ *
+ * Note that this function leaks the various device name allocations on
+ * failure. The caller takes care of them.
+ */
STATIC int
xfs_parseargs(
struct xfs_mount *mp,
char *options,
- struct xfs_mount_args *args,
- int update)
+ char **mtpt)
{
+ struct super_block *sb = mp->m_super;
char *this_char, *value, *eov;
- int dsunit, dswidth, vol_dsunit, vol_dswidth;
- int iosize;
+ int dsunit = 0;
+ int dswidth = 0;
+ int iosize = 0;
int dmapi_implies_ikeep = 1;
+ uchar_t iosizelog = 0;
+
+ /*
+ * Copy binary VFS mount flags we are interested in.
+ */
+ if (sb->s_flags & MS_RDONLY)
+ mp->m_flags |= XFS_MOUNT_RDONLY;
+ if (sb->s_flags & MS_DIRSYNC)
+ mp->m_flags |= XFS_MOUNT_DIRSYNC;
+ if (sb->s_flags & MS_SYNCHRONOUS)
+ mp->m_flags |= XFS_MOUNT_WSYNC;
+
+ /*
+ * Set some default flags that could be cleared by the mount option
+ * parsing.
+ */
+ mp->m_flags |= XFS_MOUNT_BARRIER;
+ mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
+ mp->m_flags |= XFS_MOUNT_SMALL_INUMS;
- args->flags |= XFSMNT_BARRIER;
- args->flags2 |= XFSMNT2_COMPAT_IOSIZE;
+ /*
+ * These can be overridden by the mount option parsing.
+ */
+ mp->m_logbufs = -1;
+ mp->m_logbsize = -1;
if (!options)
goto done;
- iosize = dsunit = dswidth = vol_dsunit = vol_dswidth = 0;
-
while ((this_char = strsep(&options, ",")) != NULL) {
if (!*this_char)
continue;
@@ -221,7 +222,7 @@ xfs_parseargs(
this_char);
return EINVAL;
}
- args->logbufs = simple_strtoul(value, &eov, 10);
+ mp->m_logbufs = simple_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_LOGBSIZE)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -229,7 +230,7 @@ xfs_parseargs(
this_char);
return EINVAL;
}
- args->logbufsize = suffix_strtoul(value, &eov, 10);
+ mp->m_logbsize = suffix_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_LOGDEV)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -237,7 +238,9 @@ xfs_parseargs(
this_char);
return EINVAL;
}
- strncpy(args->logname, value, MAXNAMELEN);
+ mp->m_logname = kstrndup(value, MAXNAMELEN, GFP_KERNEL);
+ if (!mp->m_logname)
+ return ENOMEM;
} else if (!strcmp(this_char, MNTOPT_MTPT)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -245,7 +248,9 @@ xfs_parseargs(
this_char);
return EINVAL;
}
- strncpy(args->mtpt, value, MAXNAMELEN);
+ *mtpt = kstrndup(value, MAXNAMELEN, GFP_KERNEL);
+ if (!*mtpt)
+ return ENOMEM;
} else if (!strcmp(this_char, MNTOPT_RTDEV)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -253,7 +258,9 @@ xfs_parseargs(
this_char);
return EINVAL;
}
- strncpy(args->rtname, value, MAXNAMELEN);
+ mp->m_rtname = kstrndup(value, MAXNAMELEN, GFP_KERNEL);
+ if (!mp->m_rtname)
+ return ENOMEM;
} else if (!strcmp(this_char, MNTOPT_BIOSIZE)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -262,8 +269,7 @@ xfs_parseargs(
return EINVAL;
}
iosize = simple_strtoul(value, &eov, 10);
- args->flags |= XFSMNT_IOSIZE;
- args->iosizelog = (uint8_t) iosize;
+ iosizelog = ffs(iosize) - 1;
} else if (!strcmp(this_char, MNTOPT_ALLOCSIZE)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -272,8 +278,7 @@ xfs_parseargs(
return EINVAL;
}
iosize = suffix_strtoul(value, &eov, 10);
- args->flags |= XFSMNT_IOSIZE;
- args->iosizelog = ffs(iosize) - 1;
+ iosizelog = ffs(iosize) - 1;
} else if (!strcmp(this_char, MNTOPT_GRPID) ||
!strcmp(this_char, MNTOPT_BSDGROUPS)) {
mp->m_flags |= XFS_MOUNT_GRPID;
@@ -281,23 +286,25 @@ xfs_parseargs(
!strcmp(this_char, MNTOPT_SYSVGROUPS)) {
mp->m_flags &= ~XFS_MOUNT_GRPID;
} else if (!strcmp(this_char, MNTOPT_WSYNC)) {
- args->flags |= XFSMNT_WSYNC;
+ mp->m_flags |= XFS_MOUNT_WSYNC;
} else if (!strcmp(this_char, MNTOPT_OSYNCISOSYNC)) {
- args->flags |= XFSMNT_OSYNCISOSYNC;
+ mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
} else if (!strcmp(this_char, MNTOPT_NORECOVERY)) {
- args->flags |= XFSMNT_NORECOVERY;
+ mp->m_flags |= XFS_MOUNT_NORECOVERY;
} else if (!strcmp(this_char, MNTOPT_INO64)) {
- args->flags |= XFSMNT_INO64;
-#if !XFS_BIG_INUMS
+#if XFS_BIG_INUMS
+ mp->m_flags |= XFS_MOUNT_INO64;
+ mp->m_inoadd = XFS_INO64_OFFSET;
+#else
cmn_err(CE_WARN,
"XFS: %s option not allowed on this system",
this_char);
return EINVAL;
#endif
} else if (!strcmp(this_char, MNTOPT_NOALIGN)) {
- args->flags |= XFSMNT_NOALIGN;
+ mp->m_flags |= XFS_MOUNT_NOALIGN;
} else if (!strcmp(this_char, MNTOPT_SWALLOC)) {
- args->flags |= XFSMNT_SWALLOC;
+ mp->m_flags |= XFS_MOUNT_SWALLOC;
} else if (!strcmp(this_char, MNTOPT_SUNIT)) {
if (!value || !*value) {
cmn_err(CE_WARN,
@@ -315,7 +322,7 @@ xfs_parseargs(
}
dswidth = simple_strtoul(value, &eov, 10);
} else if (!strcmp(this_char, MNTOPT_64BITINODE)) {
- args->flags &= ~XFSMNT_32BITINODES;
+ mp->m_flags &= ~XFS_MOUNT_SMALL_INUMS;
#if !XFS_BIG_INUMS
cmn_err(CE_WARN,
"XFS: %s option not allowed on this system",
@@ -323,56 +330,61 @@ xfs_parseargs(
return EINVAL;
#endif
} else if (!strcmp(this_char, MNTOPT_NOUUID)) {
- args->flags |= XFSMNT_NOUUID;
+ mp->m_flags |= XFS_MOUNT_NOUUID;
} else if (!strcmp(this_char, MNTOPT_BARRIER)) {
- args->flags |= XFSMNT_BARRIER;
+ mp->m_flags |= XFS_MOUNT_BARRIER;
} else if (!strcmp(this_char, MNTOPT_NOBARRIER)) {
- args->flags &= ~XFSMNT_BARRIER;
+ mp->m_flags &= ~XFS_MOUNT_BARRIER;
} else if (!strcmp(this_char, MNTOPT_IKEEP)) {
- args->flags |= XFSMNT_IKEEP;
+ mp->m_flags |= XFS_MOUNT_IKEEP;
} else if (!strcmp(this_char, MNTOPT_NOIKEEP)) {
dmapi_implies_ikeep = 0;
- args->flags &= ~XFSMNT_IKEEP;
+ mp->m_flags &= ~XFS_MOUNT_IKEEP;
} else if (!strcmp(this_char, MNTOPT_LARGEIO)) {
- args->flags2 &= ~XFSMNT2_COMPAT_IOSIZE;
+ mp->m_flags &= ~XFS_MOUNT_COMPAT_IOSIZE;
} else if (!strcmp(this_char, MNTOPT_NOLARGEIO)) {
- args->flags2 |= XFSMNT2_COMPAT_IOSIZE;
+ mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
} else if (!strcmp(this_char, MNTOPT_ATTR2)) {
- args->flags |= XFSMNT_ATTR2;
+ mp->m_flags |= XFS_MOUNT_ATTR2;
} else if (!strcmp(this_char, MNTOPT_NOATTR2)) {
- args->flags &= ~XFSMNT_ATTR2;
- args->flags |= XFSMNT_NOATTR2;
+ mp->m_flags &= ~XFS_MOUNT_ATTR2;
+ mp->m_flags |= XFS_MOUNT_NOATTR2;
} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
- args->flags2 |= XFSMNT2_FILESTREAMS;
+ mp->m_flags |= XFS_MOUNT_FILESTREAMS;
} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
- args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
- args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
+ mp->m_qflags &= ~(XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE |
+ XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE |
+ XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE |
+ XFS_UQUOTA_ENFD | XFS_OQUOTA_ENFD);
} else if (!strcmp(this_char, MNTOPT_QUOTA) ||
!strcmp(this_char, MNTOPT_UQUOTA) ||
!strcmp(this_char, MNTOPT_USRQUOTA)) {
- args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF;
+ mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE |
+ XFS_UQUOTA_ENFD);
} else if (!strcmp(this_char, MNTOPT_QUOTANOENF) ||
!strcmp(this_char, MNTOPT_UQUOTANOENF)) {
- args->flags |= XFSMNT_UQUOTA;
- args->flags &= ~XFSMNT_UQUOTAENF;
+ mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
+ mp->m_qflags &= ~XFS_UQUOTA_ENFD;
} else if (!strcmp(this_char, MNTOPT_PQUOTA) ||
!strcmp(this_char, MNTOPT_PRJQUOTA)) {
- args->flags |= XFSMNT_PQUOTA | XFSMNT_PQUOTAENF;
+ mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE |
+ XFS_OQUOTA_ENFD);
} else if (!strcmp(this_char, MNTOPT_PQUOTANOENF)) {
- args->flags |= XFSMNT_PQUOTA;
- args->flags &= ~XFSMNT_PQUOTAENF;
+ mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
+ mp->m_qflags &= ~XFS_OQUOTA_ENFD;
} else if (!strcmp(this_char, MNTOPT_GQUOTA) ||
!strcmp(this_char, MNTOPT_GRPQUOTA)) {
- args->flags |= XFSMNT_GQUOTA | XFSMNT_GQUOTAENF;
+ mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE |
+ XFS_OQUOTA_ENFD);
} else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) {
- args->flags |= XFSMNT_GQUOTA;
- args->flags &= ~XFSMNT_GQUOTAENF;
+ mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
+ mp->m_qflags &= ~XFS_OQUOTA_ENFD;
} else if (!strcmp(this_char, MNTOPT_DMAPI)) {
- args->flags |= XFSMNT_DMAPI;
+ mp->m_flags |= XFS_MOUNT_DMAPI;
} else if (!strcmp(this_char, MNTOPT_XDSM)) {
- args->flags |= XFSMNT_DMAPI;
+ mp->m_flags |= XFS_MOUNT_DMAPI;
} else if (!strcmp(this_char, MNTOPT_DMI)) {
- args->flags |= XFSMNT_DMAPI;
+ mp->m_flags |= XFS_MOUNT_DMAPI;
} else if (!strcmp(this_char, "ihashsize")) {
cmn_err(CE_WARN,
"XFS: ihashsize no longer used, option is deprecated.");
@@ -390,27 +402,29 @@ xfs_parseargs(
}
}
- if (args->flags & XFSMNT_NORECOVERY) {
- if ((mp->m_flags & XFS_MOUNT_RDONLY) == 0) {
- cmn_err(CE_WARN,
- "XFS: no-recovery mounts must be read-only.");
- return EINVAL;
- }
+ /*
+ * no recovery flag requires a read-only mount
+ */
+ if ((mp->m_flags & XFS_MOUNT_NORECOVERY) &&
+ !(mp->m_flags & XFS_MOUNT_RDONLY)) {
+ cmn_err(CE_WARN, "XFS: no-recovery mounts must be read-only.");
+ return EINVAL;
}
- if ((args->flags & XFSMNT_NOALIGN) && (dsunit || dswidth)) {
+ if ((mp->m_flags & XFS_MOUNT_NOALIGN) && (dsunit || dswidth)) {
cmn_err(CE_WARN,
"XFS: sunit and swidth options incompatible with the noalign option");
return EINVAL;
}
- if ((args->flags & XFSMNT_GQUOTA) && (args->flags & XFSMNT_PQUOTA)) {
+ if ((mp->m_qflags & (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE)) &&
+ (mp->m_qflags & (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE))) {
cmn_err(CE_WARN,
"XFS: cannot mount with both project and group quota");
return EINVAL;
}
- if ((args->flags & XFSMNT_DMAPI) && *args->mtpt == '\0') {
+ if ((mp->m_flags & XFS_MOUNT_DMAPI) && (!*mtpt || *mtpt[0] == '\0')) {
printk("XFS: %s option needs the mount point option as well\n",
MNTOPT_DMAPI);
return EINVAL;
@@ -438,27 +452,66 @@ xfs_parseargs(
* Note that if "ikeep" or "noikeep" mount options are
* supplied, then they are honored.
*/
- if ((args->flags & XFSMNT_DMAPI) && dmapi_implies_ikeep)
- args->flags |= XFSMNT_IKEEP;
+ if ((mp->m_flags & XFS_MOUNT_DMAPI) && dmapi_implies_ikeep)
+ mp->m_flags |= XFS_MOUNT_IKEEP;
- if ((args->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
+done:
+ if (!(mp->m_flags & XFS_MOUNT_NOALIGN)) {
+ /*
+ * At this point the superblock has not been read
+ * in, therefore we do not know the block size.
+ * Before the mount call ends we will convert
+ * these to FSBs.
+ */
if (dsunit) {
- args->sunit = dsunit;
- args->flags |= XFSMNT_RETERR;
- } else {
- args->sunit = vol_dsunit;
+ mp->m_dalign = dsunit;
+ mp->m_flags |= XFS_MOUNT_RETERR;
}
- dswidth ? (args->swidth = dswidth) :
- (args->swidth = vol_dswidth);
- } else {
- args->sunit = args->swidth = 0;
+
+ if (dswidth)
+ mp->m_swidth = dswidth;
+ }
+
+ if (mp->m_logbufs != -1 &&
+ mp->m_logbufs != 0 &&
+ (mp->m_logbufs < XLOG_MIN_ICLOGS ||
+ mp->m_logbufs > XLOG_MAX_ICLOGS)) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufs value: %d [not %d-%d]",
+ mp->m_logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
+ return XFS_ERROR(EINVAL);
+ }
+ if (mp->m_logbsize != -1 &&
+ mp->m_logbsize != 0 &&
+ (mp->m_logbsize < XLOG_MIN_RECORD_BSIZE ||
+ mp->m_logbsize > XLOG_MAX_RECORD_BSIZE ||
+ !is_power_of_2(mp->m_logbsize))) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
+ mp->m_logbsize);
+ return XFS_ERROR(EINVAL);
+ }
+
+ mp->m_fsname = kstrndup(sb->s_id, MAXNAMELEN, GFP_KERNEL);
+ if (!mp->m_fsname)
+ return ENOMEM;
+ mp->m_fsname_len = strlen(mp->m_fsname) + 1;
+
+ if (iosizelog) {
+ if (iosizelog > XFS_MAX_IO_LOG ||
+ iosizelog < XFS_MIN_IO_LOG) {
+ cmn_err(CE_WARN,
+ "XFS: invalid log iosize: %d [not %d-%d]",
+ iosizelog, XFS_MIN_IO_LOG,
+ XFS_MAX_IO_LOG);
+ return XFS_ERROR(EINVAL);
+ }
+
+ mp->m_flags |= XFS_MOUNT_DFLT_IOSIZE;
+ mp->m_readio_log = iosizelog;
+ mp->m_writeio_log = iosizelog;
}
-done:
- if (args->flags & XFSMNT_32BITINODES)
- mp->m_flags |= XFS_MOUNT_SMALL_INUMS;
- if (args->flags2)
- args->flags |= XFSMNT_FLAGS2;
return 0;
}
@@ -704,8 +757,7 @@ xfs_close_devices(
*/
STATIC int
xfs_open_devices(
- struct xfs_mount *mp,
- struct xfs_mount_args *args)
+ struct xfs_mount *mp)
{
struct block_device *ddev = mp->m_super->s_bdev;
struct block_device *logdev = NULL, *rtdev = NULL;
@@ -714,14 +766,14 @@ xfs_open_devices(
/*
* Open real time and log devices - order is important.
*/
- if (args->logname[0]) {
- error = xfs_blkdev_get(mp, args->logname, &logdev);
+ if (mp->m_logname) {
+ error = xfs_blkdev_get(mp, mp->m_logname, &logdev);
if (error)
goto out;
}
- if (args->rtname[0]) {
- error = xfs_blkdev_get(mp, args->rtname, &rtdev);
+ if (mp->m_rtname) {
+ error = xfs_blkdev_get(mp, mp->m_rtname, &rtdev);
if (error)
goto out_close_logdev;
@@ -813,18 +865,18 @@ xfs_setup_devices(
*/
void
xfsaild_wakeup(
- xfs_mount_t *mp,
+ struct xfs_ail *ailp,
xfs_lsn_t threshold_lsn)
{
- mp->m_ail.xa_target = threshold_lsn;
- wake_up_process(mp->m_ail.xa_task);
+ ailp->xa_target = threshold_lsn;
+ wake_up_process(ailp->xa_task);
}
int
xfsaild(
void *data)
{
- xfs_mount_t *mp = (xfs_mount_t *)data;
+ struct xfs_ail *ailp = data;
xfs_lsn_t last_pushed_lsn = 0;
long tout = 0;
@@ -836,11 +888,11 @@ xfsaild(
/* swsusp */
try_to_freeze();
- ASSERT(mp->m_log);
- if (XFS_FORCED_SHUTDOWN(mp))
+ ASSERT(ailp->xa_mount->m_log);
+ if (XFS_FORCED_SHUTDOWN(ailp->xa_mount))
continue;
- tout = xfsaild_push(mp, &last_pushed_lsn);
+ tout = xfsaild_push(ailp, &last_pushed_lsn);
}
return 0;
@@ -848,43 +900,82 @@ xfsaild(
int
xfsaild_start(
- xfs_mount_t *mp)
+ struct xfs_ail *ailp)
{
- mp->m_ail.xa_target = 0;
- mp->m_ail.xa_task = kthread_run(xfsaild, mp, "xfsaild");
- if (IS_ERR(mp->m_ail.xa_task))
- return -PTR_ERR(mp->m_ail.xa_task);
+ ailp->xa_target = 0;
+ ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild");
+ if (IS_ERR(ailp->xa_task))
+ return -PTR_ERR(ailp->xa_task);
return 0;
}
void
xfsaild_stop(
- xfs_mount_t *mp)
+ struct xfs_ail *ailp)
{
- kthread_stop(mp->m_ail.xa_task);
+ kthread_stop(ailp->xa_task);
}
-
+/* Catch misguided souls that try to use this interface on XFS */
STATIC struct inode *
xfs_fs_alloc_inode(
struct super_block *sb)
{
- return kmem_zone_alloc(xfs_vnode_zone, KM_SLEEP);
+ BUG();
+ return NULL;
}
+/*
+ * Now that the generic code is guaranteed not to be accessing
+ * the linux inode, we can reclaim the inode.
+ */
STATIC void
xfs_fs_destroy_inode(
- struct inode *inode)
+ struct inode *inode)
{
- kmem_zone_free(xfs_vnode_zone, inode);
+ xfs_inode_t *ip = XFS_I(inode);
+
+ XFS_STATS_INC(vn_reclaim);
+ if (xfs_reclaim(ip))
+ panic("%s: cannot reclaim 0x%p\n", __func__, inode);
}
+/*
+ * Slab object creation initialisation for the XFS inode.
+ * This covers only the idempotent fields in the XFS inode;
+ * all other fields need to be initialised on allocation
+ * from the slab. This avoids the need to repeatedly intialise
+ * fields in the xfs inode that left in the initialise state
+ * when freeing the inode.
+ */
STATIC void
xfs_fs_inode_init_once(
- void *vnode)
+ void *inode)
{
- inode_init_once((struct inode *)vnode);
+ struct xfs_inode *ip = inode;
+
+ memset(ip, 0, sizeof(struct xfs_inode));
+
+ /* vfs inode */
+ inode_init_once(VFS_I(ip));
+
+ /* xfs inode */
+ atomic_set(&ip->i_iocount, 0);
+ atomic_set(&ip->i_pincount, 0);
+ spin_lock_init(&ip->i_flags_lock);
+ init_waitqueue_head(&ip->i_ipin_wait);
+ /*
+ * Because we want to use a counting completion, complete
+ * the flush completion once to allow a single access to
+ * the flush completion without blocking.
+ */
+ init_completion(&ip->i_flush);
+ complete(&ip->i_flush);
+
+ mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER,
+ "xfsino", ip->i_ino);
+ mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino);
}
/*
@@ -898,21 +989,26 @@ xfs_fs_write_inode(
struct inode *inode,
int sync)
{
+ struct xfs_inode *ip = XFS_I(inode);
int error = 0;
int flags = 0;
- xfs_itrace_entry(XFS_I(inode));
+ xfs_itrace_entry(ip);
if (sync) {
- filemap_fdatawait(inode->i_mapping);
+ error = xfs_wait_on_pages(ip, 0, -1);
+ if (error)
+ goto out_error;
flags |= FLUSH_SYNC;
}
- error = xfs_inode_flush(XFS_I(inode), flags);
+ error = xfs_inode_flush(ip, flags);
+
+out_error:
/*
* if we failed to write out the inode then mark
* it dirty again so we'll try again later.
*/
if (error)
- mark_inode_dirty_sync(inode);
+ xfs_mark_inode_dirty_sync(ip);
return -error;
}
@@ -923,164 +1019,12 @@ xfs_fs_clear_inode(
{
xfs_inode_t *ip = XFS_I(inode);
- /*
- * ip can be null when xfs_iget_core calls xfs_idestroy if we
- * find an inode with di_mode == 0 but without IGET_CREATE set.
- */
- if (ip) {
- xfs_itrace_entry(ip);
- XFS_STATS_INC(vn_rele);
- XFS_STATS_INC(vn_remove);
- XFS_STATS_INC(vn_reclaim);
- XFS_STATS_DEC(vn_active);
-
- xfs_inactive(ip);
- xfs_iflags_clear(ip, XFS_IMODIFIED);
- if (xfs_reclaim(ip))
- panic("%s: cannot reclaim 0x%p\n", __func__, inode);
- }
-
- ASSERT(XFS_I(inode) == NULL);
-}
+ xfs_itrace_entry(ip);
+ XFS_STATS_INC(vn_rele);
+ XFS_STATS_INC(vn_remove);
+ XFS_STATS_DEC(vn_active);
-/*
- * Enqueue a work item to be picked up by the vfs xfssyncd thread.
- * Doing this has two advantages:
- * - It saves on stack space, which is tight in certain situations
- * - It can be used (with care) as a mechanism to avoid deadlocks.
- * Flushing while allocating in a full filesystem requires both.
- */
-STATIC void
-xfs_syncd_queue_work(
- struct xfs_mount *mp,
- void *data,
- void (*syncer)(struct xfs_mount *, void *))
-{
- struct bhv_vfs_sync_work *work;
-
- work = kmem_alloc(sizeof(struct bhv_vfs_sync_work), KM_SLEEP);
- INIT_LIST_HEAD(&work->w_list);
- work->w_syncer = syncer;
- work->w_data = data;
- work->w_mount = mp;
- spin_lock(&mp->m_sync_lock);
- list_add_tail(&work->w_list, &mp->m_sync_list);
- spin_unlock(&mp->m_sync_lock);
- wake_up_process(mp->m_sync_task);
-}
-
-/*
- * Flush delayed allocate data, attempting to free up reserved space
- * from existing allocations. At this point a new allocation attempt
- * has failed with ENOSPC and we are in the process of scratching our
- * heads, looking about for more room...
- */
-STATIC void
-xfs_flush_inode_work(
- struct xfs_mount *mp,
- void *arg)
-{
- struct inode *inode = arg;
- filemap_flush(inode->i_mapping);
- iput(inode);
-}
-
-void
-xfs_flush_inode(
- xfs_inode_t *ip)
-{
- struct inode *inode = VFS_I(ip);
-
- igrab(inode);
- xfs_syncd_queue_work(ip->i_mount, inode, xfs_flush_inode_work);
- delay(msecs_to_jiffies(500));
-}
-
-/*
- * This is the "bigger hammer" version of xfs_flush_inode_work...
- * (IOW, "If at first you don't succeed, use a Bigger Hammer").
- */
-STATIC void
-xfs_flush_device_work(
- struct xfs_mount *mp,
- void *arg)
-{
- struct inode *inode = arg;
- sync_blockdev(mp->m_super->s_bdev);
- iput(inode);
-}
-
-void
-xfs_flush_device(
- xfs_inode_t *ip)
-{
- struct inode *inode = VFS_I(ip);
-
- igrab(inode);
- xfs_syncd_queue_work(ip->i_mount, inode, xfs_flush_device_work);
- delay(msecs_to_jiffies(500));
- xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
-}
-
-STATIC void
-xfs_sync_worker(
- struct xfs_mount *mp,
- void *unused)
-{
- int error;
-
- if (!(mp->m_flags & XFS_MOUNT_RDONLY))
- error = xfs_sync(mp, SYNC_FSDATA | SYNC_BDFLUSH | SYNC_ATTR);
- mp->m_sync_seq++;
- wake_up(&mp->m_wait_single_sync_task);
-}
-
-STATIC int
-xfssyncd(
- void *arg)
-{
- struct xfs_mount *mp = arg;
- long timeleft;
- bhv_vfs_sync_work_t *work, *n;
- LIST_HEAD (tmp);
-
- set_freezable();
- timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10);
- for (;;) {
- timeleft = schedule_timeout_interruptible(timeleft);
- /* swsusp */
- try_to_freeze();
- if (kthread_should_stop() && list_empty(&mp->m_sync_list))
- break;
-
- spin_lock(&mp->m_sync_lock);
- /*
- * We can get woken by laptop mode, to do a sync -
- * that's the (only!) case where the list would be
- * empty with time remaining.
- */
- if (!timeleft || list_empty(&mp->m_sync_list)) {
- if (!timeleft)
- timeleft = xfs_syncd_centisecs *
- msecs_to_jiffies(10);
- INIT_LIST_HEAD(&mp->m_sync_work.w_list);
- list_add_tail(&mp->m_sync_work.w_list,
- &mp->m_sync_list);
- }
- list_for_each_entry_safe(work, n, &mp->m_sync_list, w_list)
- list_move(&work->w_list, &tmp);
- spin_unlock(&mp->m_sync_lock);
-
- list_for_each_entry_safe(work, n, &tmp, w_list) {
- (*work->w_syncer)(mp, work->w_data);
- list_del(&work->w_list);
- if (work == &mp->m_sync_work)
- continue;
- kmem_free(work);
- }
- }
-
- return 0;
+ xfs_inactive(ip);
}
STATIC void
@@ -1099,11 +1043,9 @@ xfs_fs_put_super(
struct xfs_mount *mp = XFS_M(sb);
struct xfs_inode *rip = mp->m_rootip;
int unmount_event_flags = 0;
- int error;
- kthread_stop(mp->m_sync_task);
-
- xfs_sync(mp, SYNC_ATTR | SYNC_DELWRI);
+ xfs_syncd_stop(mp);
+ xfs_sync_inodes(mp, SYNC_ATTR|SYNC_DELWRI);
#ifdef HAVE_DMAPI
if (mp->m_flags & XFS_MOUNT_DMAPI) {
@@ -1128,18 +1070,6 @@ xfs_fs_put_super(
xfs_filestream_unmount(mp);
XFS_bflush(mp->m_ddev_targp);
- error = xfs_unmount_flush(mp, 0);
- WARN_ON(error);
-
- /*
- * If we're forcing a shutdown, typically because of a media error,
- * we want to make sure we invalidate dirty pages that belong to
- * referenced vnodes as well.
- */
- if (XFS_FORCED_SHUTDOWN(mp)) {
- error = xfs_sync(mp, SYNC_WAIT | SYNC_CLOSE);
- ASSERT(error != EFSCORRUPTED);
- }
if (mp->m_flags & XFS_MOUNT_DMAPI) {
XFS_SEND_UNMOUNT(mp, rip, DM_RIGHT_NULL, 0, 0,
@@ -1161,7 +1091,7 @@ xfs_fs_write_super(
struct super_block *sb)
{
if (!(sb->s_flags & MS_RDONLY))
- xfs_sync(XFS_M(sb), SYNC_FSDATA);
+ xfs_sync_fsdata(XFS_M(sb), 0);
sb->s_dirt = 0;
}
@@ -1172,7 +1102,6 @@ xfs_fs_sync_super(
{
struct xfs_mount *mp = XFS_M(sb);
int error;
- int flags;
/*
* Treat a sync operation like a freeze. This is to work
@@ -1186,20 +1115,10 @@ xfs_fs_sync_super(
* dirty the Linux inode until after the transaction I/O
* completes.
*/
- if (wait || unlikely(sb->s_frozen == SB_FREEZE_WRITE)) {
- /*
- * First stage of freeze - no more writers will make progress
- * now we are here, so we flush delwri and delalloc buffers
- * here, then wait for all I/O to complete. Data is frozen at
- * that point. Metadata is not frozen, transactions can still
- * occur here so don't bother flushing the buftarg (i.e
- * SYNC_QUIESCE) because it'll just get dirty again.
- */
- flags = SYNC_DATA_QUIESCE;
- } else
- flags = SYNC_FSDATA;
-
- error = xfs_sync(mp, flags);
+ if (wait || unlikely(sb->s_frozen == SB_FREEZE_WRITE))
+ error = xfs_quiesce_data(mp);
+ else
+ error = xfs_sync_fsdata(mp, 0);
sb->s_dirt = 0;
if (unlikely(laptop_mode)) {
@@ -1337,9 +1256,8 @@ xfs_fs_remount(
/* rw -> ro */
if (!(mp->m_flags & XFS_MOUNT_RDONLY) && (*flags & MS_RDONLY)) {
- xfs_filestream_flush(mp);
- xfs_sync(mp, SYNC_DATA_QUIESCE);
- xfs_attr_quiesce(mp);
+ xfs_quiesce_data(mp);
+ xfs_quiesce_attr(mp);
mp->m_flags |= XFS_MOUNT_RDONLY;
}
@@ -1348,7 +1266,7 @@ xfs_fs_remount(
/*
* Second stage of a freeze. The data is already frozen so we only
- * need to take care of themetadata. Once that's done write a dummy
+ * need to take care of the metadata. Once that's done write a dummy
* record to dirty the log in case of a crash while frozen.
*/
STATIC void
@@ -1357,7 +1275,7 @@ xfs_fs_lockfs(
{
struct xfs_mount *mp = XFS_M(sb);
- xfs_attr_quiesce(mp);
+ xfs_quiesce_attr(mp);
xfs_fs_log_dummy(mp);
}
@@ -1422,175 +1340,28 @@ xfs_fs_setxquota(
/*
* This function fills in xfs_mount_t fields based on mount args.
- * Note: the superblock has _not_ yet been read in.
- */
-STATIC int
-xfs_start_flags(
- struct xfs_mount_args *ap,
- struct xfs_mount *mp)
-{
- int error;
-
- /* Values are in BBs */
- if ((ap->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
- /*
- * At this point the superblock has not been read
- * in, therefore we do not know the block size.
- * Before the mount call ends we will convert
- * these to FSBs.
- */
- mp->m_dalign = ap->sunit;
- mp->m_swidth = ap->swidth;
- }
-
- if (ap->logbufs != -1 &&
- ap->logbufs != 0 &&
- (ap->logbufs < XLOG_MIN_ICLOGS ||
- ap->logbufs > XLOG_MAX_ICLOGS)) {
- cmn_err(CE_WARN,
- "XFS: invalid logbufs value: %d [not %d-%d]",
- ap->logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
- return XFS_ERROR(EINVAL);
- }
- mp->m_logbufs = ap->logbufs;
- if (ap->logbufsize != -1 &&
- ap->logbufsize != 0 &&
- (ap->logbufsize < XLOG_MIN_RECORD_BSIZE ||
- ap->logbufsize > XLOG_MAX_RECORD_BSIZE ||
- !is_power_of_2(ap->logbufsize))) {
- cmn_err(CE_WARN,
- "XFS: invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
- ap->logbufsize);
- return XFS_ERROR(EINVAL);
- }
-
- error = ENOMEM;
-
- mp->m_logbsize = ap->logbufsize;
- mp->m_fsname_len = strlen(ap->fsname) + 1;
-
- mp->m_fsname = kstrdup(ap->fsname, GFP_KERNEL);
- if (!mp->m_fsname)
- goto out;
-
- if (ap->rtname[0]) {
- mp->m_rtname = kstrdup(ap->rtname, GFP_KERNEL);
- if (!mp->m_rtname)
- goto out_free_fsname;
-
- }
-
- if (ap->logname[0]) {
- mp->m_logname = kstrdup(ap->logname, GFP_KERNEL);
- if (!mp->m_logname)
- goto out_free_rtname;
- }
-
- if (ap->flags & XFSMNT_WSYNC)
- mp->m_flags |= XFS_MOUNT_WSYNC;
-#if XFS_BIG_INUMS
- if (ap->flags & XFSMNT_INO64) {
- mp->m_flags |= XFS_MOUNT_INO64;
- mp->m_inoadd = XFS_INO64_OFFSET;
- }
-#endif
- if (ap->flags & XFSMNT_RETERR)
- mp->m_flags |= XFS_MOUNT_RETERR;
- if (ap->flags & XFSMNT_NOALIGN)
- mp->m_flags |= XFS_MOUNT_NOALIGN;
- if (ap->flags & XFSMNT_SWALLOC)
- mp->m_flags |= XFS_MOUNT_SWALLOC;
- if (ap->flags & XFSMNT_OSYNCISOSYNC)
- mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
- if (ap->flags & XFSMNT_32BITINODES)
- mp->m_flags |= XFS_MOUNT_32BITINODES;
-
- if (ap->flags & XFSMNT_IOSIZE) {
- if (ap->iosizelog > XFS_MAX_IO_LOG ||
- ap->iosizelog < XFS_MIN_IO_LOG) {
- cmn_err(CE_WARN,
- "XFS: invalid log iosize: %d [not %d-%d]",
- ap->iosizelog, XFS_MIN_IO_LOG,
- XFS_MAX_IO_LOG);
- return XFS_ERROR(EINVAL);
- }
-
- mp->m_flags |= XFS_MOUNT_DFLT_IOSIZE;
- mp->m_readio_log = mp->m_writeio_log = ap->iosizelog;
- }
-
- if (ap->flags & XFSMNT_IKEEP)
- mp->m_flags |= XFS_MOUNT_IKEEP;
- if (ap->flags & XFSMNT_DIRSYNC)
- mp->m_flags |= XFS_MOUNT_DIRSYNC;
- if (ap->flags & XFSMNT_ATTR2)
- mp->m_flags |= XFS_MOUNT_ATTR2;
- if (ap->flags & XFSMNT_NOATTR2)
- mp->m_flags |= XFS_MOUNT_NOATTR2;
-
- if (ap->flags2 & XFSMNT2_COMPAT_IOSIZE)
- mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
-
- /*
- * no recovery flag requires a read-only mount
- */
- if (ap->flags & XFSMNT_NORECOVERY) {
- if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
- cmn_err(CE_WARN,
- "XFS: tried to mount a FS read-write without recovery!");
- return XFS_ERROR(EINVAL);
- }
- mp->m_flags |= XFS_MOUNT_NORECOVERY;
- }
-
- if (ap->flags & XFSMNT_NOUUID)
- mp->m_flags |= XFS_MOUNT_NOUUID;
- if (ap->flags & XFSMNT_BARRIER)
- mp->m_flags |= XFS_MOUNT_BARRIER;
- else
- mp->m_flags &= ~XFS_MOUNT_BARRIER;
-
- if (ap->flags2 & XFSMNT2_FILESTREAMS)
- mp->m_flags |= XFS_MOUNT_FILESTREAMS;
-
- if (ap->flags & XFSMNT_DMAPI)
- mp->m_flags |= XFS_MOUNT_DMAPI;
- return 0;
-
-
- out_free_rtname:
- kfree(mp->m_rtname);
- out_free_fsname:
- kfree(mp->m_fsname);
- out:
- return error;
-}
-
-/*
- * This function fills in xfs_mount_t fields based on mount args.
* Note: the superblock _has_ now been read in.
*/
STATIC int
xfs_finish_flags(
- struct xfs_mount_args *ap,
struct xfs_mount *mp)
{
int ronly = (mp->m_flags & XFS_MOUNT_RDONLY);
/* Fail a mount where the logbuf is smaller then the log stripe */
if (xfs_sb_version_haslogv2(&mp->m_sb)) {
- if ((ap->logbufsize <= 0) &&
- (mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE)) {
+ if (mp->m_logbsize <= 0 &&
+ mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE) {
mp->m_logbsize = mp->m_sb.sb_logsunit;
- } else if (ap->logbufsize > 0 &&
- ap->logbufsize < mp->m_sb.sb_logsunit) {
+ } else if (mp->m_logbsize > 0 &&
+ mp->m_logbsize < mp->m_sb.sb_logsunit) {
cmn_err(CE_WARN,
"XFS: logbuf size must be greater than or equal to log stripe size");
return XFS_ERROR(EINVAL);
}
} else {
/* Fail a mount if the logbuf is larger than 32K */
- if (ap->logbufsize > XLOG_BIG_RECORD_BSIZE) {
+ if (mp->m_logbsize > XLOG_BIG_RECORD_BSIZE) {
cmn_err(CE_WARN,
"XFS: logbuf size for version 1 logs must be 16K or 32K");
return XFS_ERROR(EINVAL);
@@ -1602,7 +1373,7 @@ xfs_finish_flags(
* told by noattr2 to turn it off
*/
if (xfs_sb_version_hasattr2(&mp->m_sb) &&
- !(ap->flags & XFSMNT_NOATTR2))
+ !(mp->m_flags & XFS_MOUNT_NOATTR2))
mp->m_flags |= XFS_MOUNT_ATTR2;
/*
@@ -1614,48 +1385,6 @@ xfs_finish_flags(
return XFS_ERROR(EROFS);
}
- /*
- * check for shared mount.
- */
- if (ap->flags & XFSMNT_SHARED) {
- if (!xfs_sb_version_hasshared(&mp->m_sb))
- return XFS_ERROR(EINVAL);
-
- /*
- * For IRIX 6.5, shared mounts must have the shared
- * version bit set, have the persistent readonly
- * field set, must be version 0 and can only be mounted
- * read-only.
- */
- if (!ronly || !(mp->m_sb.sb_flags & XFS_SBF_READONLY) ||
- (mp->m_sb.sb_shared_vn != 0))
- return XFS_ERROR(EINVAL);
-
- mp->m_flags |= XFS_MOUNT_SHARED;
-
- /*
- * Shared XFS V0 can't deal with DMI. Return EINVAL.
- */
- if (mp->m_sb.sb_shared_vn == 0 && (ap->flags & XFSMNT_DMAPI))
- return XFS_ERROR(EINVAL);
- }
-
- if (ap->flags & XFSMNT_UQUOTA) {
- mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_UQUOTAENF)
- mp->m_qflags |= XFS_UQUOTA_ENFD;
- }
-
- if (ap->flags & XFSMNT_GQUOTA) {
- mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_GQUOTAENF)
- mp->m_qflags |= XFS_OQUOTA_ENFD;
- } else if (ap->flags & XFSMNT_PQUOTA) {
- mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_PQUOTAENF)
- mp->m_qflags |= XFS_OQUOTA_ENFD;
- }
-
return 0;
}
@@ -1667,19 +1396,14 @@ xfs_fs_fill_super(
{
struct inode *root;
struct xfs_mount *mp = NULL;
- struct xfs_mount_args *args;
int flags = 0, error = ENOMEM;
-
- args = xfs_args_allocate(sb, silent);
- if (!args)
- return -ENOMEM;
+ char *mtpt = NULL;
mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL);
if (!mp)
- goto out_free_args;
+ goto out;
spin_lock_init(&mp->m_sb_lock);
- mutex_init(&mp->m_ilock);
mutex_init(&mp->m_growlock);
atomic_set(&mp->m_active_trans, 0);
INIT_LIST_HEAD(&mp->m_sync_list);
@@ -1689,12 +1413,9 @@ xfs_fs_fill_super(
mp->m_super = sb;
sb->s_fs_info = mp;
- if (sb->s_flags & MS_RDONLY)
- mp->m_flags |= XFS_MOUNT_RDONLY;
-
- error = xfs_parseargs(mp, (char *)data, args, 0);
+ error = xfs_parseargs(mp, (char *)data, &mtpt);
if (error)
- goto out_free_mp;
+ goto out_free_fsname;
sb_min_blocksize(sb, BBSIZE);
sb->s_xattr = xfs_xattr_handlers;
@@ -1702,33 +1423,28 @@ xfs_fs_fill_super(
sb->s_qcop = &xfs_quotactl_operations;
sb->s_op = &xfs_super_operations;
- error = xfs_dmops_get(mp, args);
+ error = xfs_dmops_get(mp);
if (error)
- goto out_free_mp;
- error = xfs_qmops_get(mp, args);
+ goto out_free_fsname;
+ error = xfs_qmops_get(mp);
if (error)
goto out_put_dmops;
- if (args->flags & XFSMNT_QUIET)
+ if (silent)
flags |= XFS_MFSI_QUIET;
- error = xfs_open_devices(mp, args);
+ error = xfs_open_devices(mp);
if (error)
goto out_put_qmops;
if (xfs_icsb_init_counters(mp))
mp->m_flags |= XFS_MOUNT_NO_PERCPU_SB;
- /*
- * Setup flags based on mount(2) options and then the superblock
- */
- error = xfs_start_flags(args, mp);
- if (error)
- goto out_free_fsname;
error = xfs_readsb(mp, flags);
if (error)
- goto out_free_fsname;
- error = xfs_finish_flags(args, mp);
+ goto out_destroy_counters;
+
+ error = xfs_finish_flags(mp);
if (error)
goto out_free_sb;
@@ -1747,7 +1463,7 @@ xfs_fs_fill_super(
if (error)
goto out_filestream_unmount;
- XFS_SEND_MOUNT(mp, DM_RIGHT_NULL, args->mtpt, args->fsname);
+ XFS_SEND_MOUNT(mp, DM_RIGHT_NULL, mtpt, mp->m_fsname);
sb->s_dirt = 1;
sb->s_magic = XFS_SB_MAGIC;
@@ -1772,35 +1488,31 @@ xfs_fs_fill_super(
goto fail_vnrele;
}
- mp->m_sync_work.w_syncer = xfs_sync_worker;
- mp->m_sync_work.w_mount = mp;
- mp->m_sync_task = kthread_run(xfssyncd, mp, "xfssyncd");
- if (IS_ERR(mp->m_sync_task)) {
- error = -PTR_ERR(mp->m_sync_task);
+ error = xfs_syncd_init(mp);
+ if (error)
goto fail_vnrele;
- }
- xfs_itrace_exit(XFS_I(sb->s_root->d_inode));
+ kfree(mtpt);
- kfree(args);
+ xfs_itrace_exit(XFS_I(sb->s_root->d_inode));
return 0;
out_filestream_unmount:
xfs_filestream_unmount(mp);
out_free_sb:
xfs_freesb(mp);
- out_free_fsname:
- xfs_free_fsname(mp);
+ out_destroy_counters:
xfs_icsb_destroy_counters(mp);
xfs_close_devices(mp);
out_put_qmops:
xfs_qmops_put(mp);
out_put_dmops:
xfs_dmops_put(mp);
- out_free_mp:
+ out_free_fsname:
+ xfs_free_fsname(mp);
+ kfree(mtpt);
kfree(mp);
- out_free_args:
- kfree(args);
+ out:
return -error;
fail_vnrele:
@@ -1820,8 +1532,6 @@ xfs_fs_fill_super(
xfs_filestream_unmount(mp);
XFS_bflush(mp->m_ddev_targp);
- error = xfs_unmount_flush(mp, 0);
- WARN_ON(error);
xfs_unmountfs(mp);
goto out_free_sb;
@@ -1882,10 +1592,19 @@ xfs_alloc_trace_bufs(void)
if (!xfs_bmap_trace_buf)
goto out_free_alloc_trace;
#endif
-#ifdef XFS_BMBT_TRACE
+#ifdef XFS_BTREE_TRACE
+ xfs_allocbt_trace_buf = ktrace_alloc(XFS_ALLOCBT_TRACE_SIZE,
+ KM_MAYFAIL);
+ if (!xfs_allocbt_trace_buf)
+ goto out_free_bmap_trace;
+
+ xfs_inobt_trace_buf = ktrace_alloc(XFS_INOBT_TRACE_SIZE, KM_MAYFAIL);
+ if (!xfs_inobt_trace_buf)
+ goto out_free_allocbt_trace;
+
xfs_bmbt_trace_buf = ktrace_alloc(XFS_BMBT_TRACE_SIZE, KM_MAYFAIL);
if (!xfs_bmbt_trace_buf)
- goto out_free_bmap_trace;
+ goto out_free_inobt_trace;
#endif
#ifdef XFS_ATTR_TRACE
xfs_attr_trace_buf = ktrace_alloc(XFS_ATTR_TRACE_SIZE, KM_MAYFAIL);
@@ -1907,8 +1626,12 @@ xfs_alloc_trace_bufs(void)
ktrace_free(xfs_attr_trace_buf);
out_free_bmbt_trace:
#endif
-#ifdef XFS_BMBT_TRACE
+#ifdef XFS_BTREE_TRACE
ktrace_free(xfs_bmbt_trace_buf);
+ out_free_inobt_trace:
+ ktrace_free(xfs_inobt_trace_buf);
+ out_free_allocbt_trace:
+ ktrace_free(xfs_allocbt_trace_buf);
out_free_bmap_trace:
#endif
#ifdef XFS_BMAP_TRACE
@@ -1931,8 +1654,10 @@ xfs_free_trace_bufs(void)
#ifdef XFS_ATTR_TRACE
ktrace_free(xfs_attr_trace_buf);
#endif
-#ifdef XFS_BMBT_TRACE
+#ifdef XFS_BTREE_TRACE
ktrace_free(xfs_bmbt_trace_buf);
+ ktrace_free(xfs_inobt_trace_buf);
+ ktrace_free(xfs_allocbt_trace_buf);
#endif
#ifdef XFS_BMAP_TRACE
ktrace_free(xfs_bmap_trace_buf);
@@ -1945,16 +1670,10 @@ xfs_free_trace_bufs(void)
STATIC int __init
xfs_init_zones(void)
{
- xfs_vnode_zone = kmem_zone_init_flags(sizeof(struct inode), "xfs_vnode",
- KM_ZONE_HWALIGN | KM_ZONE_RECLAIM |
- KM_ZONE_SPREAD,
- xfs_fs_inode_init_once);
- if (!xfs_vnode_zone)
- goto out;
xfs_ioend_zone = kmem_zone_init(sizeof(xfs_ioend_t), "xfs_ioend");
if (!xfs_ioend_zone)
- goto out_destroy_vnode_zone;
+ goto out;
xfs_ioend_pool = mempool_create_slab_pool(4 * MAX_BUF_PER_PAGE,
xfs_ioend_zone);
@@ -1970,6 +1689,7 @@ xfs_init_zones(void)
"xfs_bmap_free_item");
if (!xfs_bmap_free_item_zone)
goto out_destroy_log_ticket_zone;
+
xfs_btree_cur_zone = kmem_zone_init(sizeof(xfs_btree_cur_t),
"xfs_btree_cur");
if (!xfs_btree_cur_zone)
@@ -2017,8 +1737,8 @@ xfs_init_zones(void)
xfs_inode_zone =
kmem_zone_init_flags(sizeof(xfs_inode_t), "xfs_inode",
- KM_ZONE_HWALIGN | KM_ZONE_RECLAIM |
- KM_ZONE_SPREAD, NULL);
+ KM_ZONE_HWALIGN | KM_ZONE_RECLAIM | KM_ZONE_SPREAD,
+ xfs_fs_inode_init_once);
if (!xfs_inode_zone)
goto out_destroy_efi_zone;
@@ -2066,8 +1786,6 @@ xfs_init_zones(void)
mempool_destroy(xfs_ioend_pool);
out_destroy_ioend_zone:
kmem_zone_destroy(xfs_ioend_zone);
- out_destroy_vnode_zone:
- kmem_zone_destroy(xfs_vnode_zone);
out:
return -ENOMEM;
}
@@ -2092,7 +1810,6 @@ xfs_destroy_zones(void)
kmem_zone_destroy(xfs_log_ticket_zone);
mempool_destroy(xfs_ioend_pool);
kmem_zone_destroy(xfs_ioend_zone);
- kmem_zone_destroy(xfs_vnode_zone);
}
@@ -2100,13 +1817,12 @@ STATIC int __init
init_xfs_fs(void)
{
int error;
- static char message[] __initdata = KERN_INFO \
- XFS_VERSION_STRING " with " XFS_BUILD_OPTIONS " enabled\n";
- printk(message);
+ printk(KERN_INFO XFS_VERSION_STRING " with "
+ XFS_BUILD_OPTIONS " enabled\n");
ktrace_init(64);
- vn_init();
+ xfs_ioend_init();
xfs_dir_startup();
error = xfs_init_zones();
diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h
index fe2ef4e6a0f9..d5d776d4cd67 100644
--- a/fs/xfs/linux-2.6/xfs_super.h
+++ b/fs/xfs/linux-2.6/xfs_super.h
@@ -20,24 +20,12 @@
#include <linux/exportfs.h>
-#ifdef CONFIG_XFS_DMAPI
-# define vfs_insertdmapi(vfs) vfs_insertops(vfsp, &xfs_dmops)
-# define vfs_initdmapi() dmapi_init()
-# define vfs_exitdmapi() dmapi_uninit()
-#else
-# define vfs_insertdmapi(vfs) do { } while (0)
-# define vfs_initdmapi() do { } while (0)
-# define vfs_exitdmapi() do { } while (0)
-#endif
-
#ifdef CONFIG_XFS_QUOTA
-# define vfs_insertquota(vfs) vfs_insertops(vfsp, &xfs_qmops)
extern void xfs_qm_init(void);
extern void xfs_qm_exit(void);
# define vfs_initquota() xfs_qm_init()
# define vfs_exitquota() xfs_qm_exit()
#else
-# define vfs_insertquota(vfs) do { } while (0)
# define vfs_initquota() do { } while (0)
# define vfs_exitquota() do { } while (0)
#endif
@@ -101,9 +89,6 @@ struct block_device;
extern __uint64_t xfs_max_file_offset(unsigned int);
-extern void xfs_flush_inode(struct xfs_inode *);
-extern void xfs_flush_device(struct xfs_inode *);
-
extern void xfs_blkdev_issue_flush(struct xfs_buftarg *);
extern const struct export_operations xfs_export_operations;
diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c
new file mode 100644
index 000000000000..2ed035354c26
--- /dev/null
+++ b/fs/xfs/linux-2.6/xfs_sync.c
@@ -0,0 +1,762 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, 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.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_types.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_inode.h"
+#include "xfs_dinode.h"
+#include "xfs_error.h"
+#include "xfs_mru_cache.h"
+#include "xfs_filestream.h"
+#include "xfs_vnodeops.h"
+#include "xfs_utils.h"
+#include "xfs_buf_item.h"
+#include "xfs_inode_item.h"
+#include "xfs_rw.h"
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+/*
+ * Sync all the inodes in the given AG according to the
+ * direction given by the flags.
+ */
+STATIC int
+xfs_sync_inodes_ag(
+ xfs_mount_t *mp,
+ int ag,
+ int flags)
+{
+ xfs_perag_t *pag = &mp->m_perag[ag];
+ int nr_found;
+ uint32_t first_index = 0;
+ int error = 0;
+ int last_error = 0;
+ int fflag = XFS_B_ASYNC;
+
+ if (flags & SYNC_DELWRI)
+ fflag = XFS_B_DELWRI;
+ if (flags & SYNC_WAIT)
+ fflag = 0; /* synchronous overrides all */
+
+ do {
+ struct inode *inode;
+ xfs_inode_t *ip = NULL;
+ int lock_flags = XFS_ILOCK_SHARED;
+
+ /*
+ * use a gang lookup to find the next inode in the tree
+ * as the tree is sparse and a gang lookup walks to find
+ * the number of objects requested.
+ */
+ read_lock(&pag->pag_ici_lock);
+ nr_found = radix_tree_gang_lookup(&pag->pag_ici_root,
+ (void**)&ip, first_index, 1);
+
+ if (!nr_found) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
+ }
+
+ /*
+ * Update the index for the next lookup. Catch overflows
+ * into the next AG range which can occur if we have inodes
+ * in the last block of the AG and we are currently
+ * pointing to the last inode.
+ */
+ first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1);
+ if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
+ }
+
+ /* nothing to sync during shutdown */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ read_unlock(&pag->pag_ici_lock);
+ return 0;
+ }
+
+ /*
+ * If we can't get a reference on the inode, it must be
+ * in reclaim. Leave it for the reclaim code to flush.
+ */
+ inode = VFS_I(ip);
+ if (!igrab(inode)) {
+ read_unlock(&pag->pag_ici_lock);
+ continue;
+ }
+ read_unlock(&pag->pag_ici_lock);
+
+ /* avoid new or bad inodes */
+ if (is_bad_inode(inode) ||
+ xfs_iflags_test(ip, XFS_INEW)) {
+ IRELE(ip);
+ continue;
+ }
+
+ /*
+ * If we have to flush data or wait for I/O completion
+ * we need to hold the iolock.
+ */
+ if ((flags & SYNC_DELWRI) && VN_DIRTY(inode)) {
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+ lock_flags |= XFS_IOLOCK_SHARED;
+ error = xfs_flush_pages(ip, 0, -1, fflag, FI_NONE);
+ if (flags & SYNC_IOWAIT)
+ xfs_ioend_wait(ip);
+ }
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ if ((flags & SYNC_ATTR) && !xfs_inode_clean(ip)) {
+ if (flags & SYNC_WAIT) {
+ xfs_iflock(ip);
+ if (!xfs_inode_clean(ip))
+ error = xfs_iflush(ip, XFS_IFLUSH_SYNC);
+ else
+ xfs_ifunlock(ip);
+ } else if (xfs_iflock_nowait(ip)) {
+ if (!xfs_inode_clean(ip))
+ error = xfs_iflush(ip, XFS_IFLUSH_DELWRI);
+ else
+ xfs_ifunlock(ip);
+ }
+ }
+ xfs_iput(ip, lock_flags);
+
+ if (error)
+ last_error = error;
+ /*
+ * bail out if the filesystem is corrupted.
+ */
+ if (error == EFSCORRUPTED)
+ return XFS_ERROR(error);
+
+ } while (nr_found);
+
+ return last_error;
+}
+
+int
+xfs_sync_inodes(
+ xfs_mount_t *mp,
+ int flags)
+{
+ int error;
+ int last_error;
+ int i;
+ int lflags = XFS_LOG_FORCE;
+
+ if (mp->m_flags & XFS_MOUNT_RDONLY)
+ return 0;
+ error = 0;
+ last_error = 0;
+
+ if (flags & SYNC_WAIT)
+ lflags |= XFS_LOG_SYNC;
+
+ for (i = 0; i < mp->m_sb.sb_agcount; i++) {
+ if (!mp->m_perag[i].pag_ici_init)
+ continue;
+ error = xfs_sync_inodes_ag(mp, i, flags);
+ if (error)
+ last_error = error;
+ if (error == EFSCORRUPTED)
+ break;
+ }
+ if (flags & SYNC_DELWRI)
+ xfs_log_force(mp, 0, lflags);
+
+ return XFS_ERROR(last_error);
+}
+
+STATIC int
+xfs_commit_dummy_trans(
+ struct xfs_mount *mp,
+ uint log_flags)
+{
+ struct xfs_inode *ip = mp->m_rootip;
+ struct xfs_trans *tp;
+ int error;
+
+ /*
+ * Put a dummy transaction in the log to tell recovery
+ * that all others are OK.
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
+ error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0);
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ /* XXX(hch): ignoring the error here.. */
+ error = xfs_trans_commit(tp, 0);
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+ xfs_log_force(mp, 0, log_flags);
+ return 0;
+}
+
+int
+xfs_sync_fsdata(
+ struct xfs_mount *mp,
+ int flags)
+{
+ struct xfs_buf *bp;
+ struct xfs_buf_log_item *bip;
+ int error = 0;
+
+ /*
+ * If this is xfssyncd() then only sync the superblock if we can
+ * lock it without sleeping and it is not pinned.
+ */
+ if (flags & SYNC_BDFLUSH) {
+ ASSERT(!(flags & SYNC_WAIT));
+
+ bp = xfs_getsb(mp, XFS_BUF_TRYLOCK);
+ if (!bp)
+ goto out;
+
+ bip = XFS_BUF_FSPRIVATE(bp, struct xfs_buf_log_item *);
+ if (!bip || !xfs_buf_item_dirty(bip) || XFS_BUF_ISPINNED(bp))
+ goto out_brelse;
+ } else {
+ bp = xfs_getsb(mp, 0);
+
+ /*
+ * If the buffer is pinned then push on the log so we won't
+ * get stuck waiting in the write for someone, maybe
+ * ourselves, to flush the log.
+ *
+ * Even though we just pushed the log above, we did not have
+ * the superblock buffer locked at that point so it can
+ * become pinned in between there and here.
+ */
+ if (XFS_BUF_ISPINNED(bp))
+ xfs_log_force(mp, 0, XFS_LOG_FORCE);
+ }
+
+
+ if (flags & SYNC_WAIT)
+ XFS_BUF_UNASYNC(bp);
+ else
+ XFS_BUF_ASYNC(bp);
+
+ return xfs_bwrite(mp, bp);
+
+ out_brelse:
+ xfs_buf_relse(bp);
+ out:
+ return error;
+}
+
+/*
+ * When remounting a filesystem read-only or freezing the filesystem, we have
+ * two phases to execute. This first phase is syncing the data before we
+ * quiesce the filesystem, and the second is flushing all the inodes out after
+ * we've waited for all the transactions created by the first phase to
+ * complete. The second phase ensures that the inodes are written to their
+ * location on disk rather than just existing in transactions in the log. This
+ * means after a quiesce there is no log replay required to write the inodes to
+ * disk (this is the main difference between a sync and a quiesce).
+ */
+/*
+ * First stage of freeze - no writers will make progress now we are here,
+ * so we flush delwri and delalloc buffers here, then wait for all I/O to
+ * complete. Data is frozen at that point. Metadata is not frozen,
+ * transactions can still occur here so don't bother flushing the buftarg
+ * because it'll just get dirty again.
+ */
+int
+xfs_quiesce_data(
+ struct xfs_mount *mp)
+{
+ int error;
+
+ /* push non-blocking */
+ xfs_sync_inodes(mp, SYNC_DELWRI|SYNC_BDFLUSH);
+ XFS_QM_DQSYNC(mp, SYNC_BDFLUSH);
+ xfs_filestream_flush(mp);
+
+ /* push and block */
+ xfs_sync_inodes(mp, SYNC_DELWRI|SYNC_WAIT|SYNC_IOWAIT);
+ XFS_QM_DQSYNC(mp, SYNC_WAIT);
+
+ /* write superblock and hoover up shutdown errors */
+ error = xfs_sync_fsdata(mp, 0);
+
+ /* flush data-only devices */
+ if (mp->m_rtdev_targp)
+ XFS_bflush(mp->m_rtdev_targp);
+
+ return error;
+}
+
+STATIC void
+xfs_quiesce_fs(
+ struct xfs_mount *mp)
+{
+ int count = 0, pincount;
+
+ xfs_flush_buftarg(mp->m_ddev_targp, 0);
+ xfs_reclaim_inodes(mp, 0, XFS_IFLUSH_DELWRI_ELSE_ASYNC);
+
+ /*
+ * This loop must run at least twice. The first instance of the loop
+ * will flush most meta data but that will generate more meta data
+ * (typically directory updates). Which then must be flushed and
+ * logged before we can write the unmount record.
+ */
+ do {
+ xfs_sync_inodes(mp, SYNC_ATTR|SYNC_WAIT);
+ pincount = xfs_flush_buftarg(mp->m_ddev_targp, 1);
+ if (!pincount) {
+ delay(50);
+ count++;
+ }
+ } while (count < 2);
+}
+
+/*
+ * Second stage of a quiesce. The data is already synced, now we have to take
+ * care of the metadata. New transactions are already blocked, so we need to
+ * wait for any remaining transactions to drain out before proceding.
+ */
+void
+xfs_quiesce_attr(
+ struct xfs_mount *mp)
+{
+ int error = 0;
+
+ /* wait for all modifications to complete */
+ while (atomic_read(&mp->m_active_trans) > 0)
+ delay(100);
+
+ /* flush inodes and push all remaining buffers out to disk */
+ xfs_quiesce_fs(mp);
+
+ ASSERT_ALWAYS(atomic_read(&mp->m_active_trans) == 0);
+
+ /* Push the superblock and write an unmount record */
+ error = xfs_log_sbcount(mp, 1);
+ if (error)
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "xfs_attr_quiesce: failed to log sb changes. "
+ "Frozen image may not be consistent.");
+ xfs_log_unmount_write(mp);
+ xfs_unmountfs_writesb(mp);
+}
+
+/*
+ * Enqueue a work item to be picked up by the vfs xfssyncd thread.
+ * Doing this has two advantages:
+ * - It saves on stack space, which is tight in certain situations
+ * - It can be used (with care) as a mechanism to avoid deadlocks.
+ * Flushing while allocating in a full filesystem requires both.
+ */
+STATIC void
+xfs_syncd_queue_work(
+ struct xfs_mount *mp,
+ void *data,
+ void (*syncer)(struct xfs_mount *, void *))
+{
+ struct bhv_vfs_sync_work *work;
+
+ work = kmem_alloc(sizeof(struct bhv_vfs_sync_work), KM_SLEEP);
+ INIT_LIST_HEAD(&work->w_list);
+ work->w_syncer = syncer;
+ work->w_data = data;
+ work->w_mount = mp;
+ spin_lock(&mp->m_sync_lock);
+ list_add_tail(&work->w_list, &mp->m_sync_list);
+ spin_unlock(&mp->m_sync_lock);
+ wake_up_process(mp->m_sync_task);
+}
+
+/*
+ * Flush delayed allocate data, attempting to free up reserved space
+ * from existing allocations. At this point a new allocation attempt
+ * has failed with ENOSPC and we are in the process of scratching our
+ * heads, looking about for more room...
+ */
+STATIC void
+xfs_flush_inode_work(
+ struct xfs_mount *mp,
+ void *arg)
+{
+ struct inode *inode = arg;
+ filemap_flush(inode->i_mapping);
+ iput(inode);
+}
+
+void
+xfs_flush_inode(
+ xfs_inode_t *ip)
+{
+ struct inode *inode = VFS_I(ip);
+
+ igrab(inode);
+ xfs_syncd_queue_work(ip->i_mount, inode, xfs_flush_inode_work);
+ delay(msecs_to_jiffies(500));
+}
+
+/*
+ * This is the "bigger hammer" version of xfs_flush_inode_work...
+ * (IOW, "If at first you don't succeed, use a Bigger Hammer").
+ */
+STATIC void
+xfs_flush_device_work(
+ struct xfs_mount *mp,
+ void *arg)
+{
+ struct inode *inode = arg;
+ sync_blockdev(mp->m_super->s_bdev);
+ iput(inode);
+}
+
+void
+xfs_flush_device(
+ xfs_inode_t *ip)
+{
+ struct inode *inode = VFS_I(ip);
+
+ igrab(inode);
+ xfs_syncd_queue_work(ip->i_mount, inode, xfs_flush_device_work);
+ delay(msecs_to_jiffies(500));
+ xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
+}
+
+/*
+ * Every sync period we need to unpin all items, reclaim inodes, sync
+ * quota and write out the superblock. We might need to cover the log
+ * to indicate it is idle.
+ */
+STATIC void
+xfs_sync_worker(
+ struct xfs_mount *mp,
+ void *unused)
+{
+ int error;
+
+ if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ xfs_reclaim_inodes(mp, 0, XFS_IFLUSH_DELWRI_ELSE_ASYNC);
+ /* dgc: errors ignored here */
+ error = XFS_QM_DQSYNC(mp, SYNC_BDFLUSH);
+ error = xfs_sync_fsdata(mp, SYNC_BDFLUSH);
+ if (xfs_log_need_covered(mp))
+ error = xfs_commit_dummy_trans(mp, XFS_LOG_FORCE);
+ }
+ mp->m_sync_seq++;
+ wake_up(&mp->m_wait_single_sync_task);
+}
+
+STATIC int
+xfssyncd(
+ void *arg)
+{
+ struct xfs_mount *mp = arg;
+ long timeleft;
+ bhv_vfs_sync_work_t *work, *n;
+ LIST_HEAD (tmp);
+
+ set_freezable();
+ timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10);
+ for (;;) {
+ timeleft = schedule_timeout_interruptible(timeleft);
+ /* swsusp */
+ try_to_freeze();
+ if (kthread_should_stop() && list_empty(&mp->m_sync_list))
+ break;
+
+ spin_lock(&mp->m_sync_lock);
+ /*
+ * We can get woken by laptop mode, to do a sync -
+ * that's the (only!) case where the list would be
+ * empty with time remaining.
+ */
+ if (!timeleft || list_empty(&mp->m_sync_list)) {
+ if (!timeleft)
+ timeleft = xfs_syncd_centisecs *
+ msecs_to_jiffies(10);
+ INIT_LIST_HEAD(&mp->m_sync_work.w_list);
+ list_add_tail(&mp->m_sync_work.w_list,
+ &mp->m_sync_list);
+ }
+ list_for_each_entry_safe(work, n, &mp->m_sync_list, w_list)
+ list_move(&work->w_list, &tmp);
+ spin_unlock(&mp->m_sync_lock);
+
+ list_for_each_entry_safe(work, n, &tmp, w_list) {
+ (*work->w_syncer)(mp, work->w_data);
+ list_del(&work->w_list);
+ if (work == &mp->m_sync_work)
+ continue;
+ kmem_free(work);
+ }
+ }
+
+ return 0;
+}
+
+int
+xfs_syncd_init(
+ struct xfs_mount *mp)
+{
+ mp->m_sync_work.w_syncer = xfs_sync_worker;
+ mp->m_sync_work.w_mount = mp;
+ mp->m_sync_task = kthread_run(xfssyncd, mp, "xfssyncd");
+ if (IS_ERR(mp->m_sync_task))
+ return -PTR_ERR(mp->m_sync_task);
+ return 0;
+}
+
+void
+xfs_syncd_stop(
+ struct xfs_mount *mp)
+{
+ kthread_stop(mp->m_sync_task);
+}
+
+int
+xfs_reclaim_inode(
+ xfs_inode_t *ip,
+ int locked,
+ int sync_mode)
+{
+ xfs_perag_t *pag = xfs_get_perag(ip->i_mount, ip->i_ino);
+
+ /* The hash lock here protects a thread in xfs_iget_core from
+ * racing with us on linking the inode back with a vnode.
+ * Once we have the XFS_IRECLAIM flag set it will not touch
+ * us.
+ */
+ write_lock(&pag->pag_ici_lock);
+ spin_lock(&ip->i_flags_lock);
+ if (__xfs_iflags_test(ip, XFS_IRECLAIM) ||
+ !__xfs_iflags_test(ip, XFS_IRECLAIMABLE)) {
+ spin_unlock(&ip->i_flags_lock);
+ write_unlock(&pag->pag_ici_lock);
+ if (locked) {
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ return 1;
+ }
+ __xfs_iflags_set(ip, XFS_IRECLAIM);
+ spin_unlock(&ip->i_flags_lock);
+ write_unlock(&pag->pag_ici_lock);
+ xfs_put_perag(ip->i_mount, pag);
+
+ /*
+ * If the inode is still dirty, then flush it out. If the inode
+ * is not in the AIL, then it will be OK to flush it delwri as
+ * long as xfs_iflush() does not keep any references to the inode.
+ * We leave that decision up to xfs_iflush() since it has the
+ * knowledge of whether it's OK to simply do a delwri flush of
+ * the inode or whether we need to wait until the inode is
+ * pulled from the AIL.
+ * We get the flush lock regardless, though, just to make sure
+ * we don't free it while it is being flushed.
+ */
+ if (!locked) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_iflock(ip);
+ }
+
+ /*
+ * In the case of a forced shutdown we rely on xfs_iflush() to
+ * wait for the inode to be unpinned before returning an error.
+ */
+ if (!is_bad_inode(VFS_I(ip)) && xfs_iflush(ip, sync_mode) == 0) {
+ /* synchronize with xfs_iflush_done */
+ xfs_iflock(ip);
+ xfs_ifunlock(ip);
+ }
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_ireclaim(ip);
+ return 0;
+}
+
+/*
+ * We set the inode flag atomically with the radix tree tag.
+ * Once we get tag lookups on the radix tree, this inode flag
+ * can go away.
+ */
+void
+xfs_inode_set_reclaim_tag(
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_perag_t *pag = xfs_get_perag(mp, ip->i_ino);
+
+ read_lock(&pag->pag_ici_lock);
+ spin_lock(&ip->i_flags_lock);
+ radix_tree_tag_set(&pag->pag_ici_root,
+ XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG);
+ __xfs_iflags_set(ip, XFS_IRECLAIMABLE);
+ spin_unlock(&ip->i_flags_lock);
+ read_unlock(&pag->pag_ici_lock);
+ xfs_put_perag(mp, pag);
+}
+
+void
+__xfs_inode_clear_reclaim_tag(
+ xfs_mount_t *mp,
+ xfs_perag_t *pag,
+ xfs_inode_t *ip)
+{
+ radix_tree_tag_clear(&pag->pag_ici_root,
+ XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG);
+}
+
+void
+xfs_inode_clear_reclaim_tag(
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_perag_t *pag = xfs_get_perag(mp, ip->i_ino);
+
+ read_lock(&pag->pag_ici_lock);
+ spin_lock(&ip->i_flags_lock);
+ __xfs_inode_clear_reclaim_tag(mp, pag, ip);
+ spin_unlock(&ip->i_flags_lock);
+ read_unlock(&pag->pag_ici_lock);
+ xfs_put_perag(mp, pag);
+}
+
+
+STATIC void
+xfs_reclaim_inodes_ag(
+ xfs_mount_t *mp,
+ int ag,
+ int noblock,
+ int mode)
+{
+ xfs_inode_t *ip = NULL;
+ xfs_perag_t *pag = &mp->m_perag[ag];
+ int nr_found;
+ uint32_t first_index;
+ int skipped;
+
+restart:
+ first_index = 0;
+ skipped = 0;
+ do {
+ /*
+ * use a gang lookup to find the next inode in the tree
+ * as the tree is sparse and a gang lookup walks to find
+ * the number of objects requested.
+ */
+ read_lock(&pag->pag_ici_lock);
+ nr_found = radix_tree_gang_lookup_tag(&pag->pag_ici_root,
+ (void**)&ip, first_index, 1,
+ XFS_ICI_RECLAIM_TAG);
+
+ if (!nr_found) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
+ }
+
+ /*
+ * Update the index for the next lookup. Catch overflows
+ * into the next AG range which can occur if we have inodes
+ * in the last block of the AG and we are currently
+ * pointing to the last inode.
+ */
+ first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1);
+ if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
+ }
+
+ /* ignore if already under reclaim */
+ if (xfs_iflags_test(ip, XFS_IRECLAIM)) {
+ read_unlock(&pag->pag_ici_lock);
+ continue;
+ }
+
+ if (noblock) {
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+ read_unlock(&pag->pag_ici_lock);
+ continue;
+ }
+ if (xfs_ipincount(ip) ||
+ !xfs_iflock_nowait(ip)) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ read_unlock(&pag->pag_ici_lock);
+ continue;
+ }
+ }
+ read_unlock(&pag->pag_ici_lock);
+
+ /*
+ * hmmm - this is an inode already in reclaim. Do
+ * we even bother catching it here?
+ */
+ if (xfs_reclaim_inode(ip, noblock, mode))
+ skipped++;
+ } while (nr_found);
+
+ if (skipped) {
+ delay(1);
+ goto restart;
+ }
+ return;
+
+}
+
+int
+xfs_reclaim_inodes(
+ xfs_mount_t *mp,
+ int noblock,
+ int mode)
+{
+ int i;
+
+ for (i = 0; i < mp->m_sb.sb_agcount; i++) {
+ if (!mp->m_perag[i].pag_ici_init)
+ continue;
+ xfs_reclaim_inodes_ag(mp, i, noblock, mode);
+ }
+ return 0;
+}
+
+
diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h
new file mode 100644
index 000000000000..5f6de1efe1f6
--- /dev/null
+++ b/fs/xfs/linux-2.6/xfs_sync.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, 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.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef XFS_SYNC_H
+#define XFS_SYNC_H 1
+
+struct xfs_mount;
+
+typedef struct bhv_vfs_sync_work {
+ struct list_head w_list;
+ struct xfs_mount *w_mount;
+ void *w_data; /* syncer routine argument */
+ void (*w_syncer)(struct xfs_mount *, void *);
+} bhv_vfs_sync_work_t;
+
+#define SYNC_ATTR 0x0001 /* sync attributes */
+#define SYNC_DELWRI 0x0002 /* look at delayed writes */
+#define SYNC_WAIT 0x0004 /* wait for i/o to complete */
+#define SYNC_BDFLUSH 0x0008 /* BDFLUSH is calling -- don't block */
+#define SYNC_IOWAIT 0x0010 /* wait for all I/O to complete */
+
+int xfs_syncd_init(struct xfs_mount *mp);
+void xfs_syncd_stop(struct xfs_mount *mp);
+
+int xfs_sync_inodes(struct xfs_mount *mp, int flags);
+int xfs_sync_fsdata(struct xfs_mount *mp, int flags);
+
+int xfs_quiesce_data(struct xfs_mount *mp);
+void xfs_quiesce_attr(struct xfs_mount *mp);
+
+void xfs_flush_inode(struct xfs_inode *ip);
+void xfs_flush_device(struct xfs_inode *ip);
+
+int xfs_reclaim_inode(struct xfs_inode *ip, int locked, int sync_mode);
+int xfs_reclaim_inodes(struct xfs_mount *mp, int noblock, int mode);
+
+void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
+void xfs_inode_clear_reclaim_tag(struct xfs_inode *ip);
+void __xfs_inode_clear_reclaim_tag(struct xfs_mount *mp, struct xfs_perag *pag,
+ struct xfs_inode *ip);
+#endif
diff --git a/fs/xfs/linux-2.6/xfs_sysctl.c b/fs/xfs/linux-2.6/xfs_sysctl.c
index 7dacb5bbde3f..916c0ffb6083 100644
--- a/fs/xfs/linux-2.6/xfs_sysctl.c
+++ b/fs/xfs/linux-2.6/xfs_sysctl.c
@@ -56,17 +56,6 @@ xfs_stats_clear_proc_handler(
static ctl_table xfs_table[] = {
{
- .ctl_name = XFS_RESTRICT_CHOWN,
- .procname = "restrict_chown",
- .data = &xfs_params.restrict_chown.val,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
- .extra1 = &xfs_params.restrict_chown.min,
- .extra2 = &xfs_params.restrict_chown.max
- },
- {
.ctl_name = XFS_SGID_INHERIT,
.procname = "irix_sgid_inherit",
.data = &xfs_params.sgid_inherit.val,
diff --git a/fs/xfs/linux-2.6/xfs_sysctl.h b/fs/xfs/linux-2.6/xfs_sysctl.h
index 4aadb8056c37..b9937d450f8e 100644
--- a/fs/xfs/linux-2.6/xfs_sysctl.h
+++ b/fs/xfs/linux-2.6/xfs_sysctl.h
@@ -31,7 +31,6 @@ typedef struct xfs_sysctl_val {
} xfs_sysctl_val_t;
typedef struct xfs_param {
- xfs_sysctl_val_t restrict_chown;/* Root/non-root can give away files.*/
xfs_sysctl_val_t sgid_inherit; /* Inherit S_ISGID if process' GID is
* not a member of parent dir GID. */
xfs_sysctl_val_t symlink_mode; /* Link creat mode affected by umask */
@@ -68,7 +67,7 @@ typedef struct xfs_param {
enum {
/* XFS_REFCACHE_SIZE = 1 */
/* XFS_REFCACHE_PURGE = 2 */
- XFS_RESTRICT_CHOWN = 3,
+ /* XFS_RESTRICT_CHOWN = 3 */
XFS_SGID_INHERIT = 4,
XFS_SYMLINK_MODE = 5,
XFS_PANIC_MASK = 6,
diff --git a/fs/xfs/linux-2.6/xfs_vfs.h b/fs/xfs/linux-2.6/xfs_vfs.h
deleted file mode 100644
index 7e60c7776b1c..000000000000
--- a/fs/xfs/linux-2.6/xfs_vfs.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2000-2006 Silicon Graphics, 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.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_VFS_H__
-#define __XFS_VFS_H__
-
-#include <linux/vfs.h>
-#include "xfs_fs.h"
-
-struct inode;
-
-struct fid;
-struct cred;
-struct seq_file;
-struct super_block;
-struct xfs_inode;
-struct xfs_mount;
-struct xfs_mount_args;
-
-typedef struct kstatfs bhv_statvfs_t;
-
-typedef struct bhv_vfs_sync_work {
- struct list_head w_list;
- struct xfs_mount *w_mount;
- void *w_data; /* syncer routine argument */
- void (*w_syncer)(struct xfs_mount *, void *);
-} bhv_vfs_sync_work_t;
-
-#define SYNC_ATTR 0x0001 /* sync attributes */
-#define SYNC_CLOSE 0x0002 /* close file system down */
-#define SYNC_DELWRI 0x0004 /* look at delayed writes */
-#define SYNC_WAIT 0x0008 /* wait for i/o to complete */
-#define SYNC_BDFLUSH 0x0010 /* BDFLUSH is calling -- don't block */
-#define SYNC_FSDATA 0x0020 /* flush fs data (e.g. superblocks) */
-#define SYNC_REFCACHE 0x0040 /* prune some of the nfs ref cache */
-#define SYNC_REMOUNT 0x0080 /* remount readonly, no dummy LRs */
-#define SYNC_IOWAIT 0x0100 /* wait for all I/O to complete */
-
-/*
- * When remounting a filesystem read-only or freezing the filesystem,
- * we have two phases to execute. This first phase is syncing the data
- * before we quiesce the fielsystem, and the second is flushing all the
- * inodes out after we've waited for all the transactions created by
- * the first phase to complete. The second phase uses SYNC_INODE_QUIESCE
- * to ensure that the inodes are written to their location on disk
- * rather than just existing in transactions in the log. This means
- * after a quiesce there is no log replay required to write the inodes
- * to disk (this is the main difference between a sync and a quiesce).
- */
-#define SYNC_DATA_QUIESCE (SYNC_DELWRI|SYNC_FSDATA|SYNC_WAIT|SYNC_IOWAIT)
-#define SYNC_INODE_QUIESCE (SYNC_REMOUNT|SYNC_ATTR|SYNC_WAIT)
-
-#define SHUTDOWN_META_IO_ERROR 0x0001 /* write attempt to metadata failed */
-#define SHUTDOWN_LOG_IO_ERROR 0x0002 /* write attempt to the log failed */
-#define SHUTDOWN_FORCE_UMOUNT 0x0004 /* shutdown from a forced unmount */
-#define SHUTDOWN_CORRUPT_INCORE 0x0008 /* corrupt in-memory data structures */
-#define SHUTDOWN_REMOTE_REQ 0x0010 /* shutdown came from remote cell */
-#define SHUTDOWN_DEVICE_REQ 0x0020 /* failed all paths to the device */
-
-#define xfs_test_for_freeze(mp) ((mp)->m_super->s_frozen)
-#define xfs_wait_for_freeze(mp,l) vfs_check_frozen((mp)->m_super, (l))
-
-#endif /* __XFS_VFS_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_vnode.c b/fs/xfs/linux-2.6/xfs_vnode.c
deleted file mode 100644
index b52528bbbfff..000000000000
--- a/fs/xfs/linux-2.6/xfs_vnode.c
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (c) 2000-2003,2005 Silicon Graphics, 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.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "xfs.h"
-#include "xfs_vnodeops.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_inode.h"
-
-/*
- * And this gunk is needed for xfs_mount.h"
- */
-#include "xfs_log.h"
-#include "xfs_trans.h"
-#include "xfs_sb.h"
-#include "xfs_dmapi.h"
-#include "xfs_inum.h"
-#include "xfs_ag.h"
-#include "xfs_mount.h"
-
-
-/*
- * Dedicated vnode inactive/reclaim sync wait queues.
- * Prime number of hash buckets since address is used as the key.
- */
-#define NVSYNC 37
-#define vptosync(v) (&vsync[((unsigned long)v) % NVSYNC])
-static wait_queue_head_t vsync[NVSYNC];
-
-void __init
-vn_init(void)
-{
- int i;
-
- for (i = 0; i < NVSYNC; i++)
- init_waitqueue_head(&vsync[i]);
-}
-
-void
-vn_iowait(
- xfs_inode_t *ip)
-{
- wait_queue_head_t *wq = vptosync(ip);
-
- wait_event(*wq, (atomic_read(&ip->i_iocount) == 0));
-}
-
-void
-vn_iowake(
- xfs_inode_t *ip)
-{
- if (atomic_dec_and_test(&ip->i_iocount))
- wake_up(vptosync(ip));
-}
-
-/*
- * Volume managers supporting multiple paths can send back ENODEV when the
- * final path disappears. In this case continuing to fill the page cache
- * with dirty data which cannot be written out is evil, so prevent that.
- */
-void
-vn_ioerror(
- xfs_inode_t *ip,
- int error,
- char *f,
- int l)
-{
- if (unlikely(error == -ENODEV))
- xfs_do_force_shutdown(ip->i_mount, SHUTDOWN_DEVICE_REQ, f, l);
-}
-
-#ifdef XFS_INODE_TRACE
-
-/*
- * Reference count of Linux inode if present, -1 if the xfs_inode
- * has no associated Linux inode.
- */
-static inline int xfs_icount(struct xfs_inode *ip)
-{
- struct inode *vp = VFS_I(ip);
-
- if (vp)
- return vn_count(vp);
- return -1;
-}
-
-#define KTRACE_ENTER(ip, vk, s, line, ra) \
- ktrace_enter( (ip)->i_trace, \
-/* 0 */ (void *)(__psint_t)(vk), \
-/* 1 */ (void *)(s), \
-/* 2 */ (void *)(__psint_t) line, \
-/* 3 */ (void *)(__psint_t)xfs_icount(ip), \
-/* 4 */ (void *)(ra), \
-/* 5 */ NULL, \
-/* 6 */ (void *)(__psint_t)current_cpu(), \
-/* 7 */ (void *)(__psint_t)current_pid(), \
-/* 8 */ (void *)__return_address, \
-/* 9 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL)
-
-/*
- * Vnode tracing code.
- */
-void
-_xfs_itrace_entry(xfs_inode_t *ip, const char *func, inst_t *ra)
-{
- KTRACE_ENTER(ip, INODE_KTRACE_ENTRY, func, 0, ra);
-}
-
-void
-_xfs_itrace_exit(xfs_inode_t *ip, const char *func, inst_t *ra)
-{
- KTRACE_ENTER(ip, INODE_KTRACE_EXIT, func, 0, ra);
-}
-
-void
-xfs_itrace_hold(xfs_inode_t *ip, char *file, int line, inst_t *ra)
-{
- KTRACE_ENTER(ip, INODE_KTRACE_HOLD, file, line, ra);
-}
-
-void
-_xfs_itrace_ref(xfs_inode_t *ip, char *file, int line, inst_t *ra)
-{
- KTRACE_ENTER(ip, INODE_KTRACE_REF, file, line, ra);
-}
-
-void
-xfs_itrace_rele(xfs_inode_t *ip, char *file, int line, inst_t *ra)
-{
- KTRACE_ENTER(ip, INODE_KTRACE_RELE, file, line, ra);
-}
-#endif /* XFS_INODE_TRACE */
diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h
index 683ce16210ff..f65983a230d3 100644
--- a/fs/xfs/linux-2.6/xfs_vnode.h
+++ b/fs/xfs/linux-2.6/xfs_vnode.h
@@ -18,7 +18,10 @@
#ifndef __XFS_VNODE_H__
#define __XFS_VNODE_H__
+#include "xfs_fs.h"
+
struct file;
+struct xfs_inode;
struct xfs_iomap;
struct attrlist_cursor_kern;
@@ -51,40 +54,6 @@ struct attrlist_cursor_kern;
Prevent VM access to the pages until
the operation completes. */
-
-extern void vn_init(void);
-
-/*
- * Yeah, these don't take vnode anymore at all, all this should be
- * cleaned up at some point.
- */
-extern void vn_iowait(struct xfs_inode *ip);
-extern void vn_iowake(struct xfs_inode *ip);
-extern void vn_ioerror(struct xfs_inode *ip, int error, char *f, int l);
-
-static inline int vn_count(struct inode *vp)
-{
- return atomic_read(&vp->i_count);
-}
-
-#define IHOLD(ip) \
-do { \
- ASSERT(atomic_read(&VFS_I(ip)->i_count) > 0) ; \
- atomic_inc(&(VFS_I(ip)->i_count)); \
- xfs_itrace_hold((ip), __FILE__, __LINE__, (inst_t *)__return_address); \
-} while (0)
-
-#define IRELE(ip) \
-do { \
- xfs_itrace_rele((ip), __FILE__, __LINE__, (inst_t *)__return_address); \
- iput(VFS_I(ip)); \
-} while (0)
-
-static inline struct inode *vn_grab(struct inode *vp)
-{
- return igrab(vp);
-}
-
/*
* Dealing with bad inodes
*/
@@ -121,39 +90,4 @@ static inline void vn_atime_to_time_t(struct inode *vp, time_t *tt)
PAGECACHE_TAG_DIRTY)
-/*
- * Tracking vnode activity.
- */
-#if defined(XFS_INODE_TRACE)
-
-#define INODE_TRACE_SIZE 16 /* number of trace entries */
-#define INODE_KTRACE_ENTRY 1
-#define INODE_KTRACE_EXIT 2
-#define INODE_KTRACE_HOLD 3
-#define INODE_KTRACE_REF 4
-#define INODE_KTRACE_RELE 5
-
-extern void _xfs_itrace_entry(struct xfs_inode *, const char *, inst_t *);
-extern void _xfs_itrace_exit(struct xfs_inode *, const char *, inst_t *);
-extern void xfs_itrace_hold(struct xfs_inode *, char *, int, inst_t *);
-extern void _xfs_itrace_ref(struct xfs_inode *, char *, int, inst_t *);
-extern void xfs_itrace_rele(struct xfs_inode *, char *, int, inst_t *);
-#define xfs_itrace_entry(ip) \
- _xfs_itrace_entry(ip, __func__, (inst_t *)__return_address)
-#define xfs_itrace_exit(ip) \
- _xfs_itrace_exit(ip, __func__, (inst_t *)__return_address)
-#define xfs_itrace_exit_tag(ip, tag) \
- _xfs_itrace_exit(ip, tag, (inst_t *)__return_address)
-#define xfs_itrace_ref(ip) \
- _xfs_itrace_ref(ip, __FILE__, __LINE__, (inst_t *)__return_address)
-
-#else
-#define xfs_itrace_entry(a)
-#define xfs_itrace_exit(a)
-#define xfs_itrace_exit_tag(a, b)
-#define xfs_itrace_hold(a, b, c, d)
-#define xfs_itrace_ref(a)
-#define xfs_itrace_rele(a, b, c, d)
-#endif
-
#endif /* __XFS_VNODE_H__ */
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c
index f2705f2fd43c..591ca6602bfb 100644
--- a/fs/xfs/quota/xfs_dquot.c
+++ b/fs/xfs/quota/xfs_dquot.c
@@ -101,7 +101,7 @@ xfs_qm_dqinit(
if (brandnewdquot) {
dqp->dq_flnext = dqp->dq_flprev = dqp;
mutex_init(&dqp->q_qlock);
- sv_init(&dqp->q_pinwait, SV_DEFAULT, "pdq");
+ init_waitqueue_head(&dqp->q_pinwait);
/*
* Because we want to use a counting completion, complete
@@ -131,7 +131,7 @@ xfs_qm_dqinit(
dqp->q_res_bcount = 0;
dqp->q_res_icount = 0;
dqp->q_res_rtbcount = 0;
- dqp->q_pincount = 0;
+ atomic_set(&dqp->q_pincount, 0);
dqp->q_hash = NULL;
ASSERT(dqp->dq_flnext == dqp->dq_flprev);
@@ -1221,16 +1221,14 @@ xfs_qm_dqflush(
xfs_dqtrace_entry(dqp, "DQFLUSH");
/*
- * If not dirty, nada.
+ * If not dirty, or it's pinned and we are not supposed to
+ * block, nada.
*/
- if (!XFS_DQ_IS_DIRTY(dqp)) {
+ if (!XFS_DQ_IS_DIRTY(dqp) ||
+ (!(flags & XFS_QMOPT_SYNC) && atomic_read(&dqp->q_pincount) > 0)) {
xfs_dqfunlock(dqp);
- return (0);
+ return 0;
}
-
- /*
- * Cant flush a pinned dquot. Wait for it.
- */
xfs_qm_dqunpin_wait(dqp);
/*
@@ -1274,10 +1272,8 @@ xfs_qm_dqflush(
dqp->dq_flags &= ~(XFS_DQ_DIRTY);
mp = dqp->q_mount;
- /* lsn is 64 bits */
- spin_lock(&mp->m_ail_lock);
- dqp->q_logitem.qli_flush_lsn = dqp->q_logitem.qli_item.li_lsn;
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_copy_lsn(mp->m_ail, &dqp->q_logitem.qli_flush_lsn,
+ &dqp->q_logitem.qli_item.li_lsn);
/*
* Attach an iodone routine so that we can remove this dquot from the
@@ -1323,8 +1319,10 @@ xfs_qm_dqflush_done(
xfs_dq_logitem_t *qip)
{
xfs_dquot_t *dqp;
+ struct xfs_ail *ailp;
dqp = qip->qli_dquot;
+ ailp = qip->qli_item.li_ailp;
/*
* We only want to pull the item from the AIL if its
@@ -1337,15 +1335,12 @@ xfs_qm_dqflush_done(
if ((qip->qli_item.li_flags & XFS_LI_IN_AIL) &&
qip->qli_item.li_lsn == qip->qli_flush_lsn) {
- spin_lock(&dqp->q_mount->m_ail_lock);
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ spin_lock(&ailp->xa_lock);
if (qip->qli_item.li_lsn == qip->qli_flush_lsn)
- xfs_trans_delete_ail(dqp->q_mount,
- (xfs_log_item_t*)qip);
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t*)qip);
else
- spin_unlock(&dqp->q_mount->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
/*
@@ -1375,7 +1370,7 @@ xfs_dqunlock(
mutex_unlock(&(dqp->q_qlock));
if (dqp->q_logitem.qli_dquot == dqp) {
/* Once was dqp->q_mount, but might just have been cleared */
- xfs_trans_unlocked_item(dqp->q_logitem.qli_item.li_mountp,
+ xfs_trans_unlocked_item(dqp->q_logitem.qli_item.li_ailp,
(xfs_log_item_t*)&(dqp->q_logitem));
}
}
@@ -1489,7 +1484,7 @@ xfs_qm_dqpurge(
"xfs_qm_dqpurge: dquot %p flush failed", dqp);
xfs_dqflock(dqp);
}
- ASSERT(dqp->q_pincount == 0);
+ ASSERT(atomic_read(&dqp->q_pincount) == 0);
ASSERT(XFS_FORCED_SHUTDOWN(mp) ||
!(dqp->q_logitem.qli_item.li_flags & XFS_LI_IN_AIL));
diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h
index 8958d0faf8d3..7e455337e2ba 100644
--- a/fs/xfs/quota/xfs_dquot.h
+++ b/fs/xfs/quota/xfs_dquot.h
@@ -83,8 +83,8 @@ typedef struct xfs_dquot {
xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */
mutex_t q_qlock; /* quota lock */
struct completion q_flush; /* flush completion queue */
- uint q_pincount; /* pin count for this dquot */
- sv_t q_pinwait; /* sync var for pinning */
+ atomic_t q_pincount; /* dquot pin count */
+ wait_queue_head_t q_pinwait; /* dquot pinning wait queue */
#ifdef XFS_DQUOT_TRACE
struct ktrace *q_trace; /* trace header structure */
#endif
diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c
index f028644caa5e..1728f6a7c4f5 100644
--- a/fs/xfs/quota/xfs_dquot_item.c
+++ b/fs/xfs/quota/xfs_dquot_item.c
@@ -88,25 +88,22 @@ xfs_qm_dquot_logitem_format(
/*
* Increment the pin count of the given dquot.
- * This value is protected by pinlock spinlock in the xQM structure.
*/
STATIC void
xfs_qm_dquot_logitem_pin(
xfs_dq_logitem_t *logitem)
{
- xfs_dquot_t *dqp;
+ xfs_dquot_t *dqp = logitem->qli_dquot;
- dqp = logitem->qli_dquot;
ASSERT(XFS_DQ_IS_LOCKED(dqp));
- spin_lock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
- dqp->q_pincount++;
- spin_unlock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
+ atomic_inc(&dqp->q_pincount);
}
/*
* Decrement the pin count of the given dquot, and wake up
* anyone in xfs_dqwait_unpin() if the count goes to 0. The
- * dquot must have been previously pinned with a call to xfs_dqpin().
+ * dquot must have been previously pinned with a call to
+ * xfs_qm_dquot_logitem_pin().
*/
/* ARGSUSED */
STATIC void
@@ -114,16 +111,11 @@ xfs_qm_dquot_logitem_unpin(
xfs_dq_logitem_t *logitem,
int stale)
{
- xfs_dquot_t *dqp;
+ xfs_dquot_t *dqp = logitem->qli_dquot;
- dqp = logitem->qli_dquot;
- ASSERT(dqp->q_pincount > 0);
- spin_lock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
- dqp->q_pincount--;
- if (dqp->q_pincount == 0) {
- sv_broadcast(&dqp->q_pinwait);
- }
- spin_unlock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
+ ASSERT(atomic_read(&dqp->q_pincount) > 0);
+ if (atomic_dec_and_test(&dqp->q_pincount))
+ wake_up(&dqp->q_pinwait);
}
/* ARGSUSED */
@@ -193,21 +185,14 @@ xfs_qm_dqunpin_wait(
xfs_dquot_t *dqp)
{
ASSERT(XFS_DQ_IS_LOCKED(dqp));
- if (dqp->q_pincount == 0) {
+ if (atomic_read(&dqp->q_pincount) == 0)
return;
- }
/*
* Give the log a push so we don't wait here too long.
*/
xfs_log_force(dqp->q_mount, (xfs_lsn_t)0, XFS_LOG_FORCE);
- spin_lock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
- if (dqp->q_pincount == 0) {
- spin_unlock(&(XFS_DQ_TO_QINF(dqp)->qi_pinlock));
- return;
- }
- sv_wait(&(dqp->q_pinwait), PINOD,
- &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s);
+ wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));
}
/*
@@ -310,7 +295,7 @@ xfs_qm_dquot_logitem_trylock(
uint retval;
dqp = qip->qli_dquot;
- if (dqp->q_pincount > 0)
+ if (atomic_read(&dqp->q_pincount) > 0)
return (XFS_ITEM_PINNED);
if (! xfs_qm_dqlock_nowait(dqp))
@@ -568,14 +553,16 @@ xfs_qm_qoffend_logitem_committed(
xfs_lsn_t lsn)
{
xfs_qoff_logitem_t *qfs;
+ struct xfs_ail *ailp;
qfs = qfe->qql_start_lip;
- spin_lock(&qfs->qql_item.li_mountp->m_ail_lock);
+ ailp = qfs->qql_item.li_ailp;
+ spin_lock(&ailp->xa_lock);
/*
* Delete the qoff-start logitem from the AIL.
- * xfs_trans_delete_ail() drops the AIL lock.
+ * xfs_trans_ail_delete() drops the AIL lock.
*/
- xfs_trans_delete_ail(qfs->qql_item.li_mountp, (xfs_log_item_t *)qfs);
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)qfs);
kmem_free(qfs);
kmem_free(qfe);
return (xfs_lsn_t)-1;
diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c
index df0ffef9775a..6b13960cf318 100644
--- a/fs/xfs/quota/xfs_qm.c
+++ b/fs/xfs/quota/xfs_qm.c
@@ -20,7 +20,6 @@
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
-#include "xfs_clnt.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
@@ -396,13 +395,10 @@ xfs_qm_mount_quotas(
/*
* Called from the vfsops layer.
*/
-int
+void
xfs_qm_unmount_quotas(
xfs_mount_t *mp)
{
- xfs_inode_t *uqp, *gqp;
- int error = 0;
-
/*
* Release the dquots that root inode, et al might be holding,
* before we flush quotas and blow away the quotainfo structure.
@@ -415,43 +411,18 @@ xfs_qm_unmount_quotas(
xfs_qm_dqdetach(mp->m_rsumip);
/*
- * Flush out the quota inodes.
+ * Release the quota inodes.
*/
- uqp = gqp = NULL;
if (mp->m_quotainfo) {
- if ((uqp = mp->m_quotainfo->qi_uquotaip) != NULL) {
- xfs_ilock(uqp, XFS_ILOCK_EXCL);
- xfs_iflock(uqp);
- error = xfs_iflush(uqp, XFS_IFLUSH_SYNC);
- xfs_iunlock(uqp, XFS_ILOCK_EXCL);
- if (unlikely(error == EFSCORRUPTED)) {
- XFS_ERROR_REPORT("xfs_qm_unmount_quotas(1)",
- XFS_ERRLEVEL_LOW, mp);
- goto out;
- }
+ if (mp->m_quotainfo->qi_uquotaip) {
+ IRELE(mp->m_quotainfo->qi_uquotaip);
+ mp->m_quotainfo->qi_uquotaip = NULL;
}
- if ((gqp = mp->m_quotainfo->qi_gquotaip) != NULL) {
- xfs_ilock(gqp, XFS_ILOCK_EXCL);
- xfs_iflock(gqp);
- error = xfs_iflush(gqp, XFS_IFLUSH_SYNC);
- xfs_iunlock(gqp, XFS_ILOCK_EXCL);
- if (unlikely(error == EFSCORRUPTED)) {
- XFS_ERROR_REPORT("xfs_qm_unmount_quotas(2)",
- XFS_ERRLEVEL_LOW, mp);
- goto out;
- }
+ if (mp->m_quotainfo->qi_gquotaip) {
+ IRELE(mp->m_quotainfo->qi_gquotaip);
+ mp->m_quotainfo->qi_gquotaip = NULL;
}
}
- if (uqp) {
- IRELE(uqp);
- mp->m_quotainfo->qi_uquotaip = NULL;
- }
- if (gqp) {
- IRELE(gqp);
- mp->m_quotainfo->qi_gquotaip = NULL;
- }
-out:
- return XFS_ERROR(error);
}
/*
@@ -987,14 +958,10 @@ xfs_qm_dqdetach(
}
/*
- * This is called by VFS_SYNC and flags arg determines the caller,
- * and its motives, as done in xfs_sync.
- *
- * vfs_sync: SYNC_FSDATA|SYNC_ATTR|SYNC_BDFLUSH 0x31
- * syscall sync: SYNC_FSDATA|SYNC_ATTR|SYNC_DELWRI 0x25
- * umountroot : SYNC_WAIT | SYNC_CLOSE | SYNC_ATTR | SYNC_FSDATA
+ * This is called to sync quotas. We can be told to use non-blocking
+ * semantics by either the SYNC_BDFLUSH flag or the absence of the
+ * SYNC_WAIT flag.
*/
-
int
xfs_qm_sync(
xfs_mount_t *mp,
@@ -1137,7 +1104,6 @@ xfs_qm_init_quotainfo(
return error;
}
- spin_lock_init(&qinf->qi_pinlock);
xfs_qm_list_init(&qinf->qi_dqlist, "mpdqlist", 0);
qinf->qi_dqreclaims = 0;
@@ -1234,7 +1200,6 @@ xfs_qm_destroy_quotainfo(
*/
xfs_qm_rele_quotafs_ref(mp);
- spinlock_destroy(&qi->qi_pinlock);
xfs_qm_list_destroy(&qi->qi_dqlist);
if (qi->qi_uquotaip) {
diff --git a/fs/xfs/quota/xfs_qm.h b/fs/xfs/quota/xfs_qm.h
index 44f25349e478..ddf09166387c 100644
--- a/fs/xfs/quota/xfs_qm.h
+++ b/fs/xfs/quota/xfs_qm.h
@@ -106,7 +106,6 @@ typedef struct xfs_qm {
typedef struct xfs_quotainfo {
xfs_inode_t *qi_uquotaip; /* user quota inode */
xfs_inode_t *qi_gquotaip; /* group quota inode */
- spinlock_t qi_pinlock; /* dquot pinning lock */
xfs_dqlist_t qi_dqlist; /* all dquots in filesys */
int qi_dqreclaims; /* a change here indicates
a removal in the dqlist */
@@ -168,7 +167,7 @@ extern void xfs_qm_destroy_quotainfo(xfs_mount_t *);
extern void xfs_qm_mount_quotas(xfs_mount_t *);
extern int xfs_qm_quotacheck(xfs_mount_t *);
extern void xfs_qm_unmount_quotadestroy(xfs_mount_t *);
-extern int xfs_qm_unmount_quotas(xfs_mount_t *);
+extern void xfs_qm_unmount_quotas(xfs_mount_t *);
extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t);
extern int xfs_qm_sync(xfs_mount_t *, int);
diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c
index eea2e60b456b..bc6c5cca3e12 100644
--- a/fs/xfs/quota/xfs_qm_bhv.c
+++ b/fs/xfs/quota/xfs_qm_bhv.c
@@ -20,7 +20,6 @@
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
-#include "xfs_clnt.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
@@ -51,7 +50,7 @@
STATIC void
xfs_fill_statvfs_from_dquot(
- bhv_statvfs_t *statp,
+ struct kstatfs *statp,
xfs_disk_dquot_t *dp)
{
__uint64_t limit;
@@ -88,7 +87,7 @@ xfs_fill_statvfs_from_dquot(
STATIC void
xfs_qm_statvfs(
xfs_inode_t *ip,
- bhv_statvfs_t *statp)
+ struct kstatfs *statp)
{
xfs_mount_t *mp = ip->i_mount;
xfs_dquot_t *dqp;
diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c
index 1a3b803dfa55..68139b38aede 100644
--- a/fs/xfs/quota/xfs_qm_syscalls.c
+++ b/fs/xfs/quota/xfs_qm_syscalls.c
@@ -127,7 +127,7 @@ xfs_qm_quotactl(
break;
case Q_XQUOTASYNC:
- return (xfs_sync_inodes(mp, SYNC_DELWRI, NULL));
+ return xfs_sync_inodes(mp, SYNC_DELWRI);
default:
break;
@@ -1022,101 +1022,104 @@ xfs_qm_export_flags(
/*
- * Go thru all the inodes in the file system, releasing their dquots.
- * Note that the mount structure gets modified to indicate that quotas are off
- * AFTER this, in the case of quotaoff. This also gets called from
- * xfs_rootumount.
+ * Release all the dquots on the inodes in an AG.
*/
-void
-xfs_qm_dqrele_all_inodes(
- struct xfs_mount *mp,
- uint flags)
+STATIC void
+xfs_qm_dqrele_inodes_ag(
+ xfs_mount_t *mp,
+ int ag,
+ uint flags)
{
- xfs_inode_t *ip, *topino;
- uint ireclaims;
- struct inode *vp;
- boolean_t vnode_refd;
+ xfs_inode_t *ip = NULL;
+ xfs_perag_t *pag = &mp->m_perag[ag];
+ int first_index = 0;
+ int nr_found;
- ASSERT(mp->m_quotainfo);
-
- XFS_MOUNT_ILOCK(mp);
-again:
- ip = mp->m_inodes;
- if (ip == NULL) {
- XFS_MOUNT_IUNLOCK(mp);
- return;
- }
do {
- /* Skip markers inserted by xfs_sync */
- if (ip->i_mount == NULL) {
- ip = ip->i_mnext;
- continue;
+ /*
+ * use a gang lookup to find the next inode in the tree
+ * as the tree is sparse and a gang lookup walks to find
+ * the number of objects requested.
+ */
+ read_lock(&pag->pag_ici_lock);
+ nr_found = radix_tree_gang_lookup(&pag->pag_ici_root,
+ (void**)&ip, first_index, 1);
+
+ if (!nr_found) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
}
- /* Root inode, rbmip and rsumip have associated blocks */
+
+ /*
+ * Update the index for the next lookup. Catch overflows
+ * into the next AG range which can occur if we have inodes
+ * in the last block of the AG and we are currently
+ * pointing to the last inode.
+ */
+ first_index = XFS_INO_TO_AGINO(mp, ip->i_ino + 1);
+ if (first_index < XFS_INO_TO_AGINO(mp, ip->i_ino)) {
+ read_unlock(&pag->pag_ici_lock);
+ break;
+ }
+
+ /* skip quota inodes */
if (ip == XFS_QI_UQIP(mp) || ip == XFS_QI_GQIP(mp)) {
ASSERT(ip->i_udquot == NULL);
ASSERT(ip->i_gdquot == NULL);
- ip = ip->i_mnext;
+ read_unlock(&pag->pag_ici_lock);
continue;
}
- vp = VFS_I(ip);
- if (!vp) {
- ASSERT(ip->i_udquot == NULL);
- ASSERT(ip->i_gdquot == NULL);
- ip = ip->i_mnext;
+
+ /*
+ * If we can't get a reference on the inode, it must be
+ * in reclaim. Leave it for the reclaim code to flush.
+ */
+ if (!igrab(VFS_I(ip))) {
+ read_unlock(&pag->pag_ici_lock);
continue;
}
- vnode_refd = B_FALSE;
- if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) {
- ireclaims = mp->m_ireclaims;
- topino = mp->m_inodes;
- vp = vn_grab(vp);
- if (!vp)
- goto again;
-
- XFS_MOUNT_IUNLOCK(mp);
- /* XXX restart limit ? */
- xfs_ilock(ip, XFS_ILOCK_EXCL);
- vnode_refd = B_TRUE;
- } else {
- ireclaims = mp->m_ireclaims;
- topino = mp->m_inodes;
- XFS_MOUNT_IUNLOCK(mp);
+ read_unlock(&pag->pag_ici_lock);
+
+ /* avoid new inodes though we shouldn't find any here */
+ if (xfs_iflags_test(ip, XFS_INEW)) {
+ IRELE(ip);
+ continue;
}
- /*
- * We don't keep the mountlock across the dqrele() call,
- * since it can take a while..
- */
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) {
xfs_qm_dqrele(ip->i_udquot);
ip->i_udquot = NULL;
}
- if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) {
+ if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) &&
+ ip->i_gdquot) {
xfs_qm_dqrele(ip->i_gdquot);
ip->i_gdquot = NULL;
}
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- /*
- * Wait until we've dropped the ilock and mountlock to
- * do the vn_rele. Or be condemned to an eternity in the
- * inactive code in hell.
- */
- if (vnode_refd)
- IRELE(ip);
- XFS_MOUNT_ILOCK(mp);
- /*
- * If an inode was inserted or removed, we gotta
- * start over again.
- */
- if (topino != mp->m_inodes || mp->m_ireclaims != ireclaims) {
- /* XXX use a sentinel */
- goto again;
- }
- ip = ip->i_mnext;
- } while (ip != mp->m_inodes);
+ xfs_iput(ip, XFS_ILOCK_EXCL);
+
+ } while (nr_found);
+}
+
+/*
+ * Go thru all the inodes in the file system, releasing their dquots.
+ * Note that the mount structure gets modified to indicate that quotas are off
+ * AFTER this, in the case of quotaoff. This also gets called from
+ * xfs_rootumount.
+ */
+void
+xfs_qm_dqrele_all_inodes(
+ struct xfs_mount *mp,
+ uint flags)
+{
+ int i;
- XFS_MOUNT_IUNLOCK(mp);
+ ASSERT(mp->m_quotainfo);
+ for (i = 0; i < mp->m_sb.sb_agcount; i++) {
+ if (!mp->m_perag[i].pag_ici_init)
+ continue;
+ xfs_qm_dqrele_inodes_ag(mp, i, flags);
+ }
}
/*------------------------------------------------------------------------*/
diff --git a/fs/xfs/support/debug.c b/fs/xfs/support/debug.c
index c27abef7b84f..ae5482965424 100644
--- a/fs/xfs/support/debug.c
+++ b/fs/xfs/support/debug.c
@@ -18,6 +18,13 @@
#include <xfs.h>
#include "debug.h"
+/* xfs_mount.h drags a lot of crap in, sorry.. */
+#include "xfs_sb.h"
+#include "xfs_inum.h"
+#include "xfs_ag.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+
static char message[1024]; /* keep it off the stack */
static DEFINE_SPINLOCK(xfs_err_lock);
@@ -55,22 +62,42 @@ cmn_err(register int level, char *fmt, ...)
}
void
-icmn_err(register int level, char *fmt, va_list ap)
+xfs_fs_vcmn_err(
+ int level,
+ struct xfs_mount *mp,
+ char *fmt,
+ va_list ap)
{
- ulong flags;
- int len;
+ unsigned long flags;
+ int len = 0;
level &= XFS_ERR_MASK;
- if(level > XFS_MAX_ERR_LEVEL)
+ if (level > XFS_MAX_ERR_LEVEL)
level = XFS_MAX_ERR_LEVEL;
+
spin_lock_irqsave(&xfs_err_lock,flags);
- len = vsnprintf(message, sizeof(message), fmt, ap);
+
+ if (mp) {
+ len = sprintf(message, "Filesystem \"%s\": ", mp->m_fsname);
+
+ /*
+ * Skip the printk if we can't print anything useful
+ * due to an over-long device name.
+ */
+ if (len >= sizeof(message))
+ goto out;
+ }
+
+ len = vsnprintf(message + len, sizeof(message) - len, fmt, ap);
if (len >= sizeof(message))
len = sizeof(message) - 1;
if (message[len-1] == '\n')
message[len-1] = 0;
+
printk("%s%s\n", err_level[level], message);
+ out:
spin_unlock_irqrestore(&xfs_err_lock,flags);
+
BUG_ON(level == CE_PANIC);
}
@@ -84,5 +111,5 @@ assfail(char *expr, char *file, int line)
void
xfs_hex_dump(void *p, int length)
{
- print_hex_dump(KERN_ALERT, "", DUMP_PREFIX_OFFSET, 16, 1, p, length, 1);
+ print_hex_dump(KERN_ALERT, "", DUMP_PREFIX_ADDRESS, 16, 1, p, length, 1);
}
diff --git a/fs/xfs/support/debug.h b/fs/xfs/support/debug.h
index 75845f950814..6f4fd37c67af 100644
--- a/fs/xfs/support/debug.h
+++ b/fs/xfs/support/debug.h
@@ -27,8 +27,6 @@
#define CE_ALERT 1 /* alert */
#define CE_PANIC 0 /* panic */
-extern void icmn_err(int, char *, va_list)
- __attribute__ ((format (printf, 2, 0)));
extern void cmn_err(int, char *, ...)
__attribute__ ((format (printf, 2, 3)));
extern void assfail(char *expr, char *f, int l);
diff --git a/fs/xfs/support/ktrace.c b/fs/xfs/support/ktrace.c
index a34ef05489b1..2d494c26717f 100644
--- a/fs/xfs/support/ktrace.c
+++ b/fs/xfs/support/ktrace.c
@@ -113,21 +113,16 @@ ktrace_alloc(int nentries, unsigned int __nocast sleep)
void
ktrace_free(ktrace_t *ktp)
{
- int entries_size;
-
if (ktp == (ktrace_t *)NULL)
return;
/*
* Special treatment for the Vnode trace buffer.
*/
- if (ktp->kt_nentries == ktrace_zentries) {
+ if (ktp->kt_nentries == ktrace_zentries)
kmem_zone_free(ktrace_ent_zone, ktp->kt_entries);
- } else {
- entries_size = (int)(ktp->kt_nentries * sizeof(ktrace_entry_t));
-
+ else
kmem_free(ktp->kt_entries);
- }
kmem_zone_free(ktrace_hdr_zone, ktp);
}
diff --git a/fs/xfs/xfs.h b/fs/xfs/xfs.h
index 540e4c989825..17254b529c54 100644
--- a/fs/xfs/xfs.h
+++ b/fs/xfs/xfs.h
@@ -30,7 +30,7 @@
#define XFS_ATTR_TRACE 1
#define XFS_BLI_TRACE 1
#define XFS_BMAP_TRACE 1
-#define XFS_BMBT_TRACE 1
+#define XFS_BTREE_TRACE 1
#define XFS_DIR2_TRACE 1
#define XFS_DQUOT_TRACE 1
#define XFS_ILOCK_TRACE 1
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index b2f639a1416f..a8cdd73999a4 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -366,7 +366,7 @@ xfs_acl_allow_set(
return ENOTDIR;
if (vp->i_sb->s_flags & MS_RDONLY)
return EROFS;
- if (XFS_I(vp)->i_d.di_uid != current->fsuid && !capable(CAP_FOWNER))
+ if (XFS_I(vp)->i_d.di_uid != current_fsuid() && !capable(CAP_FOWNER))
return EPERM;
return 0;
}
@@ -413,13 +413,13 @@ xfs_acl_access(
switch (fap->acl_entry[i].ae_tag) {
case ACL_USER_OBJ:
seen_userobj = 1;
- if (fuid != current->fsuid)
+ if (fuid != current_fsuid())
continue;
matched.ae_tag = ACL_USER_OBJ;
matched.ae_perm = allows;
break;
case ACL_USER:
- if (fap->acl_entry[i].ae_id != current->fsuid)
+ if (fap->acl_entry[i].ae_id != current_fsuid())
continue;
matched.ae_tag = ACL_USER;
matched.ae_perm = allows;
@@ -758,7 +758,7 @@ xfs_acl_setmode(
if (gap && nomask)
iattr.ia_mode |= gap->ae_perm << 3;
- return xfs_setattr(XFS_I(vp), &iattr, 0, sys_cred);
+ return xfs_setattr(XFS_I(vp), &iattr, 0);
}
/*
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
index 61b292a9fb41..f2e21817a226 100644
--- a/fs/xfs/xfs_ag.h
+++ b/fs/xfs/xfs_ag.h
@@ -91,6 +91,8 @@ typedef struct xfs_agf {
#define XFS_AGF_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGF_DADDR(mp))
#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)XFS_BUF_PTR(bp))
+extern int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
/*
* Size of the unlinked inode hash table in the agi.
@@ -142,6 +144,9 @@ typedef struct xfs_agi {
#define XFS_AGI_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGI_DADDR(mp))
#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)XFS_BUF_PTR(bp))
+extern int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_agnumber_t agno, struct xfs_buf **bpp);
+
/*
* The third a.g. block contains the a.g. freelist, an array
* of block pointers to blocks owned by the allocation btree code.
@@ -192,17 +197,23 @@ typedef struct xfs_perag
xfs_agino_t pagi_freecount; /* number of free inodes */
xfs_agino_t pagi_count; /* number of allocated inodes */
int pagb_count; /* pagb slots in use */
+ xfs_perag_busy_t *pagb_list; /* unstable blocks */
#ifdef __KERNEL__
spinlock_t pagb_lock; /* lock for pagb_list */
-#endif
- xfs_perag_busy_t *pagb_list; /* unstable blocks */
+
atomic_t pagf_fstrms; /* # of filestreams active in this AG */
int pag_ici_init; /* incore inode cache initialised */
rwlock_t pag_ici_lock; /* incore inode lock */
struct radix_tree_root pag_ici_root; /* incore inode cache root */
+#endif
} xfs_perag_t;
+/*
+ * tags for inode radix tree
+ */
+#define XFS_ICI_RECLAIM_TAG 0 /* inode is to be reclaimed */
+
#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels)
#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \
(MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + MIN(cl + 1, XFS_AG_MAXLEVELS(mp)))
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index 1956f83489f1..028e44e58ea9 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -90,6 +90,92 @@ STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *,
*/
/*
+ * Lookup the record equal to [bno, len] in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_alloc_lookup_eq(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
+ * Lookup the first record greater than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_alloc_lookup_ge(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+ * Lookup the first record less than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_alloc_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Update the record referred to by cur to the value given
+ * by [bno, len].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int /* error */
+xfs_alloc_update(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len) /* length of extent */
+{
+ union xfs_btree_rec rec;
+
+ rec.alloc.ar_startblock = cpu_to_be32(bno);
+ rec.alloc.ar_blockcount = cpu_to_be32(len);
+ return xfs_btree_update(cur, &rec);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+STATIC int /* error */
+xfs_alloc_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t *bno, /* output: starting block of extent */
+ xfs_extlen_t *len, /* output: length of extent */
+ int *stat) /* output: success/failure */
+{
+ union xfs_btree_rec *rec;
+ int error;
+
+ error = xfs_btree_get_rec(cur, &rec, stat);
+ if (!error && *stat == 1) {
+ *bno = be32_to_cpu(rec->alloc.ar_startblock);
+ *len = be32_to_cpu(rec->alloc.ar_blockcount);
+ }
+ return error;
+}
+
+/*
* Compute aligned version of the found extent.
* Takes alignment and min length into account.
*/
@@ -294,21 +380,20 @@ xfs_alloc_fixup_trees(
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
}
+
#ifdef DEBUG
- {
- xfs_alloc_block_t *bnoblock;
- xfs_alloc_block_t *cntblock;
-
- if (bno_cur->bc_nlevels == 1 &&
- cnt_cur->bc_nlevels == 1) {
- bnoblock = XFS_BUF_TO_ALLOC_BLOCK(bno_cur->bc_bufs[0]);
- cntblock = XFS_BUF_TO_ALLOC_BLOCK(cnt_cur->bc_bufs[0]);
- XFS_WANT_CORRUPTED_RETURN(
- be16_to_cpu(bnoblock->bb_numrecs) ==
- be16_to_cpu(cntblock->bb_numrecs));
- }
+ if (bno_cur->bc_nlevels == 1 && cnt_cur->bc_nlevels == 1) {
+ struct xfs_btree_block *bnoblock;
+ struct xfs_btree_block *cntblock;
+
+ bnoblock = XFS_BUF_TO_BLOCK(bno_cur->bc_bufs[0]);
+ cntblock = XFS_BUF_TO_BLOCK(cnt_cur->bc_bufs[0]);
+
+ XFS_WANT_CORRUPTED_RETURN(
+ bnoblock->bb_numrecs == cntblock->bb_numrecs);
}
#endif
+
/*
* Deal with all four cases: the allocated record is contained
* within the freespace record, so we can have new freespace
@@ -333,7 +418,7 @@ xfs_alloc_fixup_trees(
/*
* Delete the entry from the by-size btree.
*/
- if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ if ((error = xfs_btree_delete(cnt_cur, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
/*
@@ -343,7 +428,7 @@ xfs_alloc_fixup_trees(
if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno1, nflen1, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 0);
- if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ if ((error = xfs_btree_insert(cnt_cur, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
}
@@ -351,7 +436,7 @@ xfs_alloc_fixup_trees(
if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno2, nflen2, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 0);
- if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ if ((error = xfs_btree_insert(cnt_cur, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
}
@@ -362,7 +447,7 @@ xfs_alloc_fixup_trees(
/*
* No remaining freespace, just delete the by-block tree entry.
*/
- if ((error = xfs_alloc_delete(bno_cur, &i)))
+ if ((error = xfs_btree_delete(bno_cur, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
} else {
@@ -379,7 +464,7 @@ xfs_alloc_fixup_trees(
if ((error = xfs_alloc_lookup_eq(bno_cur, nfbno2, nflen2, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 0);
- if ((error = xfs_alloc_insert(bno_cur, &i)))
+ if ((error = xfs_btree_insert(bno_cur, &i)))
return error;
XFS_WANT_CORRUPTED_RETURN(i == 1);
}
@@ -640,8 +725,8 @@ xfs_alloc_ag_vextent_exact(
/*
* Allocate/initialize a cursor for the by-number freespace btree.
*/
- bno_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_BNO, NULL, 0);
+ bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO);
/*
* Lookup bno and minlen in the btree (minlen is irrelevant, really).
* Look for the closest free block <= bno, it must contain bno
@@ -696,8 +781,8 @@ xfs_alloc_ag_vextent_exact(
* We are allocating agbno for rlen [agbno .. end]
* Allocate/initialize a cursor for the by-size btree.
*/
- cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_CNT, NULL, 0);
+ cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT);
ASSERT(args->agbno + args->len <=
be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length));
if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen,
@@ -759,8 +844,8 @@ xfs_alloc_ag_vextent_near(
/*
* Get a cursor for the by-size btree.
*/
- cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_CNT, NULL, 0);
+ cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT);
ltlen = 0;
bno_cur_lt = bno_cur_gt = NULL;
/*
@@ -818,7 +903,7 @@ xfs_alloc_ag_vextent_near(
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
if (ltlen >= args->minlen)
break;
- if ((error = xfs_alloc_increment(cnt_cur, 0, &i)))
+ if ((error = xfs_btree_increment(cnt_cur, 0, &i)))
goto error0;
} while (i);
ASSERT(ltlen >= args->minlen);
@@ -828,7 +913,7 @@ xfs_alloc_ag_vextent_near(
i = cnt_cur->bc_ptrs[0];
for (j = 1, blen = 0, bdiff = 0;
!error && j && (blen < args->maxlen || bdiff > 0);
- error = xfs_alloc_increment(cnt_cur, 0, &j)) {
+ error = xfs_btree_increment(cnt_cur, 0, &j)) {
/*
* For each entry, decide if it's better than
* the previous best entry.
@@ -886,8 +971,8 @@ xfs_alloc_ag_vextent_near(
/*
* Set up a cursor for the by-bno tree.
*/
- bno_cur_lt = xfs_btree_init_cursor(args->mp, args->tp,
- args->agbp, args->agno, XFS_BTNUM_BNO, NULL, 0);
+ bno_cur_lt = xfs_allocbt_init_cursor(args->mp, args->tp,
+ args->agbp, args->agno, XFS_BTNUM_BNO);
/*
* Fix up the btree entries.
*/
@@ -914,8 +999,8 @@ xfs_alloc_ag_vextent_near(
/*
* Allocate and initialize the cursor for the leftward search.
*/
- bno_cur_lt = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_BNO, NULL, 0);
+ bno_cur_lt = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO);
/*
* Lookup <= bno to find the leftward search's starting point.
*/
@@ -938,7 +1023,7 @@ xfs_alloc_ag_vextent_near(
* Increment the cursor, so we will point at the entry just right
* of the leftward entry if any, or to the leftmost entry.
*/
- if ((error = xfs_alloc_increment(bno_cur_gt, 0, &i)))
+ if ((error = xfs_btree_increment(bno_cur_gt, 0, &i)))
goto error0;
if (!i) {
/*
@@ -961,7 +1046,7 @@ xfs_alloc_ag_vextent_near(
args->minlen, &ltbnoa, &ltlena);
if (ltlena >= args->minlen)
break;
- if ((error = xfs_alloc_decrement(bno_cur_lt, 0, &i)))
+ if ((error = xfs_btree_decrement(bno_cur_lt, 0, &i)))
goto error0;
if (!i) {
xfs_btree_del_cursor(bno_cur_lt,
@@ -977,7 +1062,7 @@ xfs_alloc_ag_vextent_near(
args->minlen, &gtbnoa, &gtlena);
if (gtlena >= args->minlen)
break;
- if ((error = xfs_alloc_increment(bno_cur_gt, 0, &i)))
+ if ((error = xfs_btree_increment(bno_cur_gt, 0, &i)))
goto error0;
if (!i) {
xfs_btree_del_cursor(bno_cur_gt,
@@ -1066,7 +1151,7 @@ xfs_alloc_ag_vextent_near(
/*
* Fell off the right end.
*/
- if ((error = xfs_alloc_increment(
+ if ((error = xfs_btree_increment(
bno_cur_gt, 0, &i)))
goto error0;
if (!i) {
@@ -1162,7 +1247,7 @@ xfs_alloc_ag_vextent_near(
/*
* Fell off the left end.
*/
- if ((error = xfs_alloc_decrement(
+ if ((error = xfs_btree_decrement(
bno_cur_lt, 0, &i)))
goto error0;
if (!i) {
@@ -1267,8 +1352,8 @@ xfs_alloc_ag_vextent_size(
/*
* Allocate and initialize a cursor for the by-size btree.
*/
- cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_CNT, NULL, 0);
+ cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT);
bno_cur = NULL;
/*
* Look for an entry >= maxlen+alignment-1 blocks.
@@ -1321,7 +1406,7 @@ xfs_alloc_ag_vextent_size(
bestflen = flen;
bestfbno = fbno;
for (;;) {
- if ((error = xfs_alloc_decrement(cnt_cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cnt_cur, 0, &i)))
goto error0;
if (i == 0)
break;
@@ -1372,8 +1457,8 @@ xfs_alloc_ag_vextent_size(
/*
* Allocate and initialize a cursor for the by-block tree.
*/
- bno_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
- args->agno, XFS_BTNUM_BNO, NULL, 0);
+ bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO);
if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen,
rbno, rlen, XFSA_FIXUP_CNT_OK)))
goto error0;
@@ -1416,7 +1501,7 @@ xfs_alloc_ag_vextent_small(
xfs_extlen_t flen;
int i;
- if ((error = xfs_alloc_decrement(ccur, 0, &i)))
+ if ((error = xfs_btree_decrement(ccur, 0, &i)))
goto error0;
if (i) {
if ((error = xfs_alloc_get_rec(ccur, &fbno, &flen, &i)))
@@ -1515,8 +1600,7 @@ xfs_free_ag_extent(
/*
* Allocate and initialize a cursor for the by-block btree.
*/
- bno_cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_BNO, NULL,
- 0);
+ bno_cur = xfs_allocbt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_BNO);
cnt_cur = NULL;
/*
* Look for a neighboring block on the left (lower block numbers)
@@ -1549,7 +1633,7 @@ xfs_free_ag_extent(
* Look for a neighboring block on the right (higher block numbers)
* that is contiguous with this space.
*/
- if ((error = xfs_alloc_increment(bno_cur, 0, &haveright)))
+ if ((error = xfs_btree_increment(bno_cur, 0, &haveright)))
goto error0;
if (haveright) {
/*
@@ -1575,8 +1659,7 @@ xfs_free_ag_extent(
/*
* Now allocate and initialize a cursor for the by-size tree.
*/
- cnt_cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_CNT, NULL,
- 0);
+ cnt_cur = xfs_allocbt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_CNT);
/*
* Have both left and right contiguous neighbors.
* Merge all three into a single free block.
@@ -1588,7 +1671,7 @@ xfs_free_ag_extent(
if ((error = xfs_alloc_lookup_eq(cnt_cur, ltbno, ltlen, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ if ((error = xfs_btree_delete(cnt_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
/*
@@ -1597,19 +1680,19 @@ xfs_free_ag_extent(
if ((error = xfs_alloc_lookup_eq(cnt_cur, gtbno, gtlen, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ if ((error = xfs_btree_delete(cnt_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
/*
* Delete the old by-block entry for the right block.
*/
- if ((error = xfs_alloc_delete(bno_cur, &i)))
+ if ((error = xfs_btree_delete(bno_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
/*
* Move the by-block cursor back to the left neighbor.
*/
- if ((error = xfs_alloc_decrement(bno_cur, 0, &i)))
+ if ((error = xfs_btree_decrement(bno_cur, 0, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
#ifdef DEBUG
@@ -1648,14 +1731,14 @@ xfs_free_ag_extent(
if ((error = xfs_alloc_lookup_eq(cnt_cur, ltbno, ltlen, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ if ((error = xfs_btree_delete(cnt_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
/*
* Back up the by-block cursor to the left neighbor, and
* update its length.
*/
- if ((error = xfs_alloc_decrement(bno_cur, 0, &i)))
+ if ((error = xfs_btree_decrement(bno_cur, 0, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
nbno = ltbno;
@@ -1674,7 +1757,7 @@ xfs_free_ag_extent(
if ((error = xfs_alloc_lookup_eq(cnt_cur, gtbno, gtlen, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ if ((error = xfs_btree_delete(cnt_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
/*
@@ -1693,7 +1776,7 @@ xfs_free_ag_extent(
else {
nbno = bno;
nlen = len;
- if ((error = xfs_alloc_insert(bno_cur, &i)))
+ if ((error = xfs_btree_insert(bno_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
}
@@ -1705,7 +1788,7 @@ xfs_free_ag_extent(
if ((error = xfs_alloc_lookup_eq(cnt_cur, nbno, nlen, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 0, error0);
- if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ if ((error = xfs_btree_insert(cnt_cur, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
@@ -2150,51 +2233,83 @@ xfs_alloc_put_freelist(
* Read in the allocation group header (free/alloc section).
*/
int /* error */
-xfs_alloc_read_agf(
- xfs_mount_t *mp, /* mount point structure */
- xfs_trans_t *tp, /* transaction pointer */
- xfs_agnumber_t agno, /* allocation group number */
- int flags, /* XFS_ALLOC_FLAG_... */
- xfs_buf_t **bpp) /* buffer for the ag freelist header */
+xfs_read_agf(
+ struct xfs_mount *mp, /* mount point structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags, /* XFS_BUF_ */
+ struct xfs_buf **bpp) /* buffer for the ag freelist header */
{
- xfs_agf_t *agf; /* ag freelist header */
+ struct xfs_agf *agf; /* ag freelist header */
int agf_ok; /* set if agf is consistent */
- xfs_buf_t *bp; /* return value */
- xfs_perag_t *pag; /* per allocation group data */
int error;
ASSERT(agno != NULLAGNUMBER);
error = xfs_trans_read_buf(
mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1),
- (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XFS_BUF_TRYLOCK : 0U,
- &bp);
+ XFS_FSS_TO_BB(mp, 1), flags, bpp);
if (error)
return error;
- ASSERT(!bp || !XFS_BUF_GETERROR(bp));
- if (!bp) {
- *bpp = NULL;
+ if (!*bpp)
return 0;
- }
+
+ ASSERT(!XFS_BUF_GETERROR(*bpp));
+ agf = XFS_BUF_TO_AGF(*bpp);
+
/*
* Validate the magic number of the agf block.
*/
- agf = XFS_BUF_TO_AGF(bp);
agf_ok =
be32_to_cpu(agf->agf_magicnum) == XFS_AGF_MAGIC &&
XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum)) &&
be32_to_cpu(agf->agf_freeblks) <= be32_to_cpu(agf->agf_length) &&
be32_to_cpu(agf->agf_flfirst) < XFS_AGFL_SIZE(mp) &&
be32_to_cpu(agf->agf_fllast) < XFS_AGFL_SIZE(mp) &&
- be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp);
+ be32_to_cpu(agf->agf_flcount) <= XFS_AGFL_SIZE(mp) &&
+ be32_to_cpu(agf->agf_seqno) == agno;
+ if (xfs_sb_version_haslazysbcount(&mp->m_sb))
+ agf_ok = agf_ok && be32_to_cpu(agf->agf_btreeblks) <=
+ be32_to_cpu(agf->agf_length);
if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
XFS_RANDOM_ALLOC_READ_AGF))) {
XFS_CORRUPTION_ERROR("xfs_alloc_read_agf",
XFS_ERRLEVEL_LOW, mp, agf);
- xfs_trans_brelse(tp, bp);
+ xfs_trans_brelse(tp, *bpp);
return XFS_ERROR(EFSCORRUPTED);
}
+
+ XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_AGF, XFS_AGF_REF);
+ return 0;
+}
+
+/*
+ * Read in the allocation group header (free/alloc section).
+ */
+int /* error */
+xfs_alloc_read_agf(
+ struct xfs_mount *mp, /* mount point structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags, /* XFS_ALLOC_FLAG_... */
+ struct xfs_buf **bpp) /* buffer for the ag freelist header */
+{
+ struct xfs_agf *agf; /* ag freelist header */
+ struct xfs_perag *pag; /* per allocation group data */
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+
+ error = xfs_read_agf(mp, tp, agno,
+ (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XFS_BUF_TRYLOCK : 0,
+ bpp);
+ if (error)
+ return error;
+ if (!*bpp)
+ return 0;
+ ASSERT(!XFS_BUF_GETERROR(*bpp));
+
+ agf = XFS_BUF_TO_AGF(*bpp);
pag = &mp->m_perag[agno];
if (!pag->pagf_init) {
pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks);
@@ -2213,6 +2328,7 @@ xfs_alloc_read_agf(
#ifdef DEBUG
else if (!XFS_FORCED_SHUTDOWN(mp)) {
ASSERT(pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks));
+ ASSERT(pag->pagf_btreeblks == be32_to_cpu(agf->agf_btreeblks));
ASSERT(pag->pagf_flcount == be32_to_cpu(agf->agf_flcount));
ASSERT(pag->pagf_longest == be32_to_cpu(agf->agf_longest));
ASSERT(pag->pagf_levels[XFS_BTNUM_BNOi] ==
@@ -2221,8 +2337,6 @@ xfs_alloc_read_agf(
be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]));
}
#endif
- XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGF, XFS_AGF_REF);
- *bpp = bp;
return 0;
}
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h
index 5aec15d0651e..588172796f7b 100644
--- a/fs/xfs/xfs_alloc.h
+++ b/fs/xfs/xfs_alloc.h
@@ -121,6 +121,19 @@ extern ktrace_t *xfs_alloc_trace_buf;
#define XFS_ALLOC_KTRACE_BUSYSEARCH 6
#endif
+void
+xfs_alloc_mark_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ xfs_extlen_t len);
+
+void
+xfs_alloc_clear_busy(xfs_trans_t *tp,
+ xfs_agnumber_t ag,
+ int idx);
+
+#endif /* __KERNEL__ */
+
/*
* Compute and fill in value of m_ag_maxlevels.
*/
@@ -196,18 +209,4 @@ xfs_free_extent(
xfs_fsblock_t bno, /* starting block number of extent */
xfs_extlen_t len); /* length of extent */
-void
-xfs_alloc_mark_busy(xfs_trans_t *tp,
- xfs_agnumber_t agno,
- xfs_agblock_t bno,
- xfs_extlen_t len);
-
-void
-xfs_alloc_clear_busy(xfs_trans_t *tp,
- xfs_agnumber_t ag,
- int idx);
-
-
-#endif /* __KERNEL__ */
-
#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c
index 3ce2645508ae..733cb75a8c5d 100644
--- a/fs/xfs/xfs_alloc_btree.c
+++ b/fs/xfs/xfs_alloc_btree.c
@@ -35,2177 +35,464 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_ialloc.h"
#include "xfs_alloc.h"
#include "xfs_error.h"
-/*
- * Prototypes for internal functions.
- */
-STATIC void xfs_alloc_log_block(xfs_trans_t *, xfs_buf_t *, int);
-STATIC void xfs_alloc_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC void xfs_alloc_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC void xfs_alloc_log_recs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC int xfs_alloc_lshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_alloc_newroot(xfs_btree_cur_t *, int *);
-STATIC int xfs_alloc_rshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_alloc_split(xfs_btree_cur_t *, int, xfs_agblock_t *,
- xfs_alloc_key_t *, xfs_btree_cur_t **, int *);
-STATIC int xfs_alloc_updkey(xfs_btree_cur_t *, xfs_alloc_key_t *, int);
+STATIC struct xfs_btree_cur *
+xfs_allocbt_dup_cursor(
+ struct xfs_btree_cur *cur)
+{
+ return xfs_allocbt_init_cursor(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agbp, cur->bc_private.a.agno,
+ cur->bc_btnum);
+}
-/*
- * Internal functions.
- */
+STATIC void
+xfs_allocbt_set_root(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr,
+ int inc)
+{
+ struct xfs_buf *agbp = cur->bc_private.a.agbp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+ xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
+ int btnum = cur->bc_btnum;
-/*
- * Single level of the xfs_alloc_delete record deletion routine.
- * Delete record pointed to by cur/level.
- * Remove the record from its block then rebalance the tree.
- * Return 0 for error, 1 for done, 2 to go on to the next level.
- */
-STATIC int /* error */
-xfs_alloc_delrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level removing record from */
- int *stat) /* fail/done/go-on */
+ ASSERT(ptr->s != 0);
+
+ agf->agf_roots[btnum] = ptr->s;
+ be32_add_cpu(&agf->agf_levels[btnum], inc);
+ cur->bc_mp->m_perag[seqno].pagf_levels[btnum] += inc;
+
+ xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_ROOTS | XFS_AGF_LEVELS);
+}
+
+STATIC int
+xfs_allocbt_alloc_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *start,
+ union xfs_btree_ptr *new,
+ int length,
+ int *stat)
{
- xfs_agf_t *agf; /* allocation group freelist header */
- xfs_alloc_block_t *block; /* btree block record/key lives in */
- xfs_agblock_t bno; /* btree block number */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop index */
- xfs_alloc_key_t key; /* kp points here if block is level 0 */
- xfs_agblock_t lbno; /* left block's block number */
- xfs_buf_t *lbp; /* left block's buffer pointer */
- xfs_alloc_block_t *left; /* left btree block */
- xfs_alloc_key_t *lkp=NULL; /* left block key pointer */
- xfs_alloc_ptr_t *lpp=NULL; /* left block address pointer */
- int lrecs=0; /* number of records in left block */
- xfs_alloc_rec_t *lrp; /* left block record pointer */
- xfs_mount_t *mp; /* mount structure */
- int ptr; /* index in btree block for this rec */
- xfs_agblock_t rbno; /* right block's block number */
- xfs_buf_t *rbp; /* right block's buffer pointer */
- xfs_alloc_block_t *right; /* right btree block */
- xfs_alloc_key_t *rkp; /* right block key pointer */
- xfs_alloc_ptr_t *rpp; /* right block address pointer */
- int rrecs=0; /* number of records in right block */
- int numrecs;
- xfs_alloc_rec_t *rrp; /* right block record pointer */
- xfs_btree_cur_t *tcur; /* temporary btree cursor */
+ int error;
+ xfs_agblock_t bno;
- /*
- * Get the index of the entry being deleted, check for nothing there.
- */
- ptr = cur->bc_ptrs[level];
- if (ptr == 0) {
- *stat = 0;
- return 0;
- }
- /*
- * Get the buffer & block containing the record or key/ptr.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+ /* Allocate the new block from the freelist. If we can't, give up. */
+ error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp,
+ &bno, 1);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
return error;
-#endif
- /*
- * Fail if we're off the end of the block.
- */
- numrecs = be16_to_cpu(block->bb_numrecs);
- if (ptr > numrecs) {
+ }
+
+ if (bno == NULLAGBLOCK) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
*stat = 0;
return 0;
}
- XFS_STATS_INC(xs_abt_delrec);
- /*
- * It's a nonleaf. Excise the key and ptr being deleted, by
- * sliding the entries past them down one.
- * Log the changed areas of the block.
- */
- if (level > 0) {
- lkp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
- lpp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = ptr; i < numrecs; i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(lpp[i]), level)))
- return error;
- }
-#endif
- if (ptr < numrecs) {
- memmove(&lkp[ptr - 1], &lkp[ptr],
- (numrecs - ptr) * sizeof(*lkp));
- memmove(&lpp[ptr - 1], &lpp[ptr],
- (numrecs - ptr) * sizeof(*lpp));
- xfs_alloc_log_ptrs(cur, bp, ptr, numrecs - 1);
- xfs_alloc_log_keys(cur, bp, ptr, numrecs - 1);
- }
- }
- /*
- * It's a leaf. Excise the record being deleted, by sliding the
- * entries past it down one. Log the changed areas of the block.
- */
- else {
- lrp = XFS_ALLOC_REC_ADDR(block, 1, cur);
- if (ptr < numrecs) {
- memmove(&lrp[ptr - 1], &lrp[ptr],
- (numrecs - ptr) * sizeof(*lrp));
- xfs_alloc_log_recs(cur, bp, ptr, numrecs - 1);
- }
- /*
- * If it's the first record in the block, we'll need a key
- * structure to pass up to the next level (updkey).
- */
- if (ptr == 1) {
- key.ar_startblock = lrp->ar_startblock;
- key.ar_blockcount = lrp->ar_blockcount;
- lkp = &key;
- }
- }
- /*
- * Decrement and log the number of entries in the block.
- */
- numrecs--;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
- /*
- * See if the longest free extent in the allocation group was
- * changed by this operation. True if it's the by-size btree, and
- * this is the leaf level, and there is no right sibling block,
- * and this was the last record.
- */
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- mp = cur->bc_mp;
- if (level == 0 &&
- cur->bc_btnum == XFS_BTNUM_CNT &&
- be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK &&
- ptr > numrecs) {
- ASSERT(ptr == numrecs + 1);
- /*
- * There are still records in the block. Grab the size
- * from the last one.
- */
- if (numrecs) {
- rrp = XFS_ALLOC_REC_ADDR(block, numrecs, cur);
- agf->agf_longest = rrp->ar_blockcount;
- }
- /*
- * No free extents left.
- */
- else
- agf->agf_longest = 0;
- mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_longest =
- be32_to_cpu(agf->agf_longest);
- xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
- XFS_AGF_LONGEST);
- }
- /*
- * Is this the root level? If so, we're almost done.
- */
- if (level == cur->bc_nlevels - 1) {
- /*
- * If this is the root level,
- * and there's only one entry left,
- * and it's NOT the leaf level,
- * then we can get rid of this level.
- */
- if (numrecs == 1 && level > 0) {
- /*
- * lpp is still set to the first pointer in the block.
- * Make it the new root of the btree.
- */
- bno = be32_to_cpu(agf->agf_roots[cur->bc_btnum]);
- agf->agf_roots[cur->bc_btnum] = *lpp;
- be32_add_cpu(&agf->agf_levels[cur->bc_btnum], -1);
- mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_levels[cur->bc_btnum]--;
- /*
- * Put this buffer/block on the ag's freelist.
- */
- error = xfs_alloc_put_freelist(cur->bc_tp,
- cur->bc_private.a.agbp, NULL, bno, 1);
- if (error)
- return error;
- /*
- * Since blocks move to the free list without the
- * coordination used in xfs_bmap_finish, we can't allow
- * block to be available for reallocation and
- * non-transaction writing (user data) until we know
- * that the transaction that moved it to the free list
- * is permanently on disk. We track the blocks by
- * declaring these blocks as "busy"; the busy list is
- * maintained on a per-ag basis and each transaction
- * records which entries should be removed when the
- * iclog commits to disk. If a busy block is
- * allocated, the iclog is pushed up to the LSN
- * that freed the block.
- */
- xfs_alloc_mark_busy(cur->bc_tp,
- be32_to_cpu(agf->agf_seqno), bno, 1);
+ xfs_trans_agbtree_delta(cur->bc_tp, 1);
+ new->s = cpu_to_be32(bno);
- xfs_trans_agbtree_delta(cur->bc_tp, -1);
- xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
- XFS_AGF_ROOTS | XFS_AGF_LEVELS);
- /*
- * Update the cursor so there's one fewer level.
- */
- xfs_btree_setbuf(cur, level, NULL);
- cur->bc_nlevels--;
- } else if (level > 0 &&
- (error = xfs_alloc_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * If we deleted the leftmost entry in the block, update the
- * key values above us in the tree.
- */
- if (ptr == 1 && (error = xfs_alloc_updkey(cur, lkp, level + 1)))
- return error;
- /*
- * If the number of records remaining in the block is at least
- * the minimum, we're done.
- */
- if (numrecs >= XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
- if (level > 0 && (error = xfs_alloc_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * Otherwise, we have to move some records around to keep the
- * tree balanced. Look at the left and right sibling blocks to
- * see if we can re-balance by moving only one record.
- */
- rbno = be32_to_cpu(block->bb_rightsib);
- lbno = be32_to_cpu(block->bb_leftsib);
- bno = NULLAGBLOCK;
- ASSERT(rbno != NULLAGBLOCK || lbno != NULLAGBLOCK);
- /*
- * Duplicate the cursor so our btree manipulations here won't
- * disrupt the next level up.
- */
- if ((error = xfs_btree_dup_cursor(cur, &tcur)))
- return error;
- /*
- * If there's a right sibling, see if it's ok to shift an entry
- * out of it.
- */
- if (rbno != NULLAGBLOCK) {
- /*
- * Move the temp cursor to the last entry in the next block.
- * Actually any entry but the first would suffice.
- */
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_increment(tcur, level, &i)))
- goto error0;
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- /*
- * Grab a pointer to the block.
- */
- rbp = tcur->bc_bufs[level];
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- goto error0;
-#endif
- /*
- * Grab the current block number, for future use.
- */
- bno = be32_to_cpu(right->bb_leftsib);
- /*
- * If right block is full enough so that removing one entry
- * won't make it too empty, and left-shifting an entry out
- * of right to us works, we're done.
- */
- if (be16_to_cpu(right->bb_numrecs) - 1 >=
- XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
- if ((error = xfs_alloc_lshift(tcur, level, &i)))
- goto error0;
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_ALLOC_BLOCK_MINRECS(level, cur));
- xfs_btree_del_cursor(tcur,
- XFS_BTREE_NOERROR);
- if (level > 0 &&
- (error = xfs_alloc_decrement(cur, level,
- &i)))
- return error;
- *stat = 1;
- return 0;
- }
- }
- /*
- * Otherwise, grab the number of records in right for
- * future reference, and fix up the temp cursor to point
- * to our block again (last record).
- */
- rrecs = be16_to_cpu(right->bb_numrecs);
- if (lbno != NULLAGBLOCK) {
- i = xfs_btree_firstrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_decrement(tcur, level, &i)))
- goto error0;
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- }
- }
- /*
- * If there's a left sibling, see if it's ok to shift an entry
- * out of it.
- */
- if (lbno != NULLAGBLOCK) {
- /*
- * Move the temp cursor to the first entry in the
- * previous block.
- */
- i = xfs_btree_firstrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_decrement(tcur, level, &i)))
- goto error0;
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- xfs_btree_firstrec(tcur, level);
- /*
- * Grab a pointer to the block.
- */
- lbp = tcur->bc_bufs[level];
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- goto error0;
-#endif
- /*
- * Grab the current block number, for future use.
- */
- bno = be32_to_cpu(left->bb_rightsib);
- /*
- * If left block is full enough so that removing one entry
- * won't make it too empty, and right-shifting an entry out
- * of left to us works, we're done.
- */
- if (be16_to_cpu(left->bb_numrecs) - 1 >=
- XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
- if ((error = xfs_alloc_rshift(tcur, level, &i)))
- goto error0;
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_ALLOC_BLOCK_MINRECS(level, cur));
- xfs_btree_del_cursor(tcur,
- XFS_BTREE_NOERROR);
- if (level == 0)
- cur->bc_ptrs[0]++;
- *stat = 1;
- return 0;
- }
- }
- /*
- * Otherwise, grab the number of records in right for
- * future reference.
- */
- lrecs = be16_to_cpu(left->bb_numrecs);
- }
- /*
- * Delete the temp cursor, we're done with it.
- */
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- /*
- * If here, we need to do a join to keep the tree balanced.
- */
- ASSERT(bno != NULLAGBLOCK);
- /*
- * See if we can join with the left neighbor block.
- */
- if (lbno != NULLAGBLOCK &&
- lrecs + numrecs <= XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
- /*
- * Set "right" to be the starting block,
- * "left" to be the left neighbor.
- */
- rbno = bno;
- right = block;
- rrecs = be16_to_cpu(right->bb_numrecs);
- rbp = bp;
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, lbno, 0, &lbp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
- lrecs = be16_to_cpu(left->bb_numrecs);
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
- }
- /*
- * If that won't work, see if we can join with the right neighbor block.
- */
- else if (rbno != NULLAGBLOCK &&
- rrecs + numrecs <= XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
- /*
- * Set "left" to be the starting block,
- * "right" to be the right neighbor.
- */
- lbno = bno;
- left = block;
- lrecs = be16_to_cpu(left->bb_numrecs);
- lbp = bp;
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, rbno, 0, &rbp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
- rrecs = be16_to_cpu(right->bb_numrecs);
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
- }
- /*
- * Otherwise, we can't fix the imbalance.
- * Just return. This is probably a logic error, but it's not fatal.
- */
- else {
- if (level > 0 && (error = xfs_alloc_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * We're now going to join "left" and "right" by moving all the stuff
- * in "right" to "left" and deleting "right".
- */
- if (level > 0) {
- /*
- * It's a non-leaf. Move keys and pointers.
- */
- lkp = XFS_ALLOC_KEY_ADDR(left, lrecs + 1, cur);
- lpp = XFS_ALLOC_PTR_ADDR(left, lrecs + 1, cur);
- rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
- rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < rrecs; i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i]), level)))
- return error;
- }
-#endif
- memcpy(lkp, rkp, rrecs * sizeof(*lkp));
- memcpy(lpp, rpp, rrecs * sizeof(*lpp));
- xfs_alloc_log_keys(cur, lbp, lrecs + 1, lrecs + rrecs);
- xfs_alloc_log_ptrs(cur, lbp, lrecs + 1, lrecs + rrecs);
- } else {
- /*
- * It's a leaf. Move records.
- */
- lrp = XFS_ALLOC_REC_ADDR(left, lrecs + 1, cur);
- rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
- memcpy(lrp, rrp, rrecs * sizeof(*lrp));
- xfs_alloc_log_recs(cur, lbp, lrecs + 1, lrecs + rrecs);
- }
- /*
- * If we joined with the left neighbor, set the buffer in the
- * cursor to the left block, and fix up the index.
- */
- if (bp != lbp) {
- xfs_btree_setbuf(cur, level, lbp);
- cur->bc_ptrs[level] += lrecs;
- }
- /*
- * If we joined with the right neighbor and there's a level above
- * us, increment the cursor at that level.
- */
- else if (level + 1 < cur->bc_nlevels &&
- (error = xfs_alloc_increment(cur, level + 1, &i)))
- return error;
- /*
- * Fix up the number of records in the surviving block.
- */
- lrecs += rrecs;
- left->bb_numrecs = cpu_to_be16(lrecs);
- /*
- * Fix up the right block pointer in the surviving block, and log it.
- */
- left->bb_rightsib = right->bb_rightsib;
- xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
- /*
- * If there is a right sibling now, make it point to the
- * remaining block.
- */
- if (be32_to_cpu(left->bb_rightsib) != NULLAGBLOCK) {
- xfs_alloc_block_t *rrblock;
- xfs_buf_t *rrbp;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+}
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(left->bb_rightsib), 0,
- &rrbp, XFS_ALLOC_BTREE_REF)))
- return error;
- rrblock = XFS_BUF_TO_ALLOC_BLOCK(rrbp);
- if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
- return error;
- rrblock->bb_leftsib = cpu_to_be32(lbno);
- xfs_alloc_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
- }
- /*
- * Free the deleting block by putting it on the freelist.
- */
- error = xfs_alloc_put_freelist(cur->bc_tp,
- cur->bc_private.a.agbp, NULL, rbno, 1);
+STATIC int
+xfs_allocbt_free_block(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp)
+{
+ struct xfs_buf *agbp = cur->bc_private.a.agbp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+ xfs_agblock_t bno;
+ int error;
+
+ bno = XFS_DADDR_TO_AGBNO(cur->bc_mp, XFS_BUF_ADDR(bp));
+ error = xfs_alloc_put_freelist(cur->bc_tp, agbp, NULL, bno, 1);
if (error)
return error;
+
/*
- * Since blocks move to the free list without the coordination
- * used in xfs_bmap_finish, we can't allow block to be available
- * for reallocation and non-transaction writing (user data)
- * until we know that the transaction that moved it to the free
- * list is permanently on disk. We track the blocks by declaring
- * these blocks as "busy"; the busy list is maintained on a
- * per-ag basis and each transaction records which entries
- * should be removed when the iclog commits to disk. If a
- * busy block is allocated, the iclog is pushed up to the
+ * Since blocks move to the free list without the coordination used in
+ * xfs_bmap_finish, we can't allow block to be available for
+ * reallocation and non-transaction writing (user data) until we know
+ * that the transaction that moved it to the free list is permanently
+ * on disk. We track the blocks by declaring these blocks as "busy";
+ * the busy list is maintained on a per-ag basis and each transaction
+ * records which entries should be removed when the iclog commits to
+ * disk. If a busy block is allocated, the iclog is pushed up to the
* LSN that freed the block.
*/
xfs_alloc_mark_busy(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1);
xfs_trans_agbtree_delta(cur->bc_tp, -1);
-
- /*
- * Adjust the current level's cursor so that we're left referring
- * to the right node, after we're done.
- * If this leaves the ptr value 0 our caller will fix it up.
- */
- if (level > 0)
- cur->bc_ptrs[level]--;
- /*
- * Return value means the next level up has something to do.
- */
- *stat = 2;
return 0;
-
-error0:
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
}
/*
- * Insert one record/level. Return information to the caller
- * allowing the next level up to proceed if necessary.
+ * Update the longest extent in the AGF
*/
-STATIC int /* error */
-xfs_alloc_insrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to insert record at */
- xfs_agblock_t *bnop, /* i/o: block number inserted */
- xfs_alloc_rec_t *recp, /* i/o: record data inserted */
- xfs_btree_cur_t **curp, /* output: new cursor replacing cur */
- int *stat) /* output: success/failure */
+STATIC void
+xfs_allocbt_update_lastrec(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ union xfs_btree_rec *rec,
+ int ptr,
+ int reason)
{
- xfs_agf_t *agf; /* allocation group freelist header */
- xfs_alloc_block_t *block; /* btree block record/key lives in */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop index */
- xfs_alloc_key_t key; /* key value being inserted */
- xfs_alloc_key_t *kp; /* pointer to btree keys */
- xfs_agblock_t nbno; /* block number of allocated block */
- xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */
- xfs_alloc_key_t nkey; /* new key value, from split */
- xfs_alloc_rec_t nrec; /* new record value, for caller */
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
+ __be32 len;
int numrecs;
- int optr; /* old ptr value */
- xfs_alloc_ptr_t *pp; /* pointer to btree addresses */
- int ptr; /* index in btree block for this rec */
- xfs_alloc_rec_t *rp; /* pointer to btree records */
- ASSERT(be32_to_cpu(recp->ar_blockcount) > 0);
+ ASSERT(cur->bc_btnum == XFS_BTNUM_CNT);
+
+ switch (reason) {
+ case LASTREC_UPDATE:
+ /*
+ * If this is the last leaf block and it's the last record,
+ * then update the size of the longest extent in the AG.
+ */
+ if (ptr != xfs_btree_get_numrecs(block))
+ return;
+ len = rec->alloc.ar_blockcount;
+ break;
+ case LASTREC_INSREC:
+ if (be32_to_cpu(rec->alloc.ar_blockcount) <=
+ be32_to_cpu(agf->agf_longest))
+ return;
+ len = rec->alloc.ar_blockcount;
+ break;
+ case LASTREC_DELREC:
+ numrecs = xfs_btree_get_numrecs(block);
+ if (ptr <= numrecs)
+ return;
+ ASSERT(ptr == numrecs + 1);
- /*
- * GCC doesn't understand the (arguably complex) control flow in
- * this function and complains about uninitialized structure fields
- * without this.
- */
- memset(&nrec, 0, sizeof(nrec));
+ if (numrecs) {
+ xfs_alloc_rec_t *rrp;
- /*
- * If we made it to the root level, allocate a new root block
- * and we're done.
- */
- if (level >= cur->bc_nlevels) {
- XFS_STATS_INC(xs_abt_insrec);
- if ((error = xfs_alloc_newroot(cur, &i)))
- return error;
- *bnop = NULLAGBLOCK;
- *stat = i;
- return 0;
- }
- /*
- * Make a key out of the record data to be inserted, and save it.
- */
- key.ar_startblock = recp->ar_startblock;
- key.ar_blockcount = recp->ar_blockcount;
- optr = ptr = cur->bc_ptrs[level];
- /*
- * If we're off the left edge, return failure.
- */
- if (ptr == 0) {
- *stat = 0;
- return 0;
- }
- XFS_STATS_INC(xs_abt_insrec);
- /*
- * Get pointers to the btree buffer and block.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- numrecs = be16_to_cpu(block->bb_numrecs);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
- /*
- * Check that the new entry is being inserted in the right place.
- */
- if (ptr <= numrecs) {
- if (level == 0) {
- rp = XFS_ALLOC_REC_ADDR(block, ptr, cur);
- xfs_btree_check_rec(cur->bc_btnum, recp, rp);
+ rrp = XFS_ALLOC_REC_ADDR(cur->bc_mp, block, numrecs);
+ len = rrp->ar_blockcount;
} else {
- kp = XFS_ALLOC_KEY_ADDR(block, ptr, cur);
- xfs_btree_check_key(cur->bc_btnum, &key, kp);
- }
- }
-#endif
- nbno = NULLAGBLOCK;
- ncur = NULL;
- /*
- * If the block is full, we can't insert the new entry until we
- * make the block un-full.
- */
- if (numrecs == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
- /*
- * First, try shifting an entry to the right neighbor.
- */
- if ((error = xfs_alloc_rshift(cur, level, &i)))
- return error;
- if (i) {
- /* nothing */
- }
- /*
- * Next, try shifting an entry to the left neighbor.
- */
- else {
- if ((error = xfs_alloc_lshift(cur, level, &i)))
- return error;
- if (i)
- optr = ptr = cur->bc_ptrs[level];
- else {
- /*
- * Next, try splitting the current block in
- * half. If this works we have to re-set our
- * variables because we could be in a
- * different block now.
- */
- if ((error = xfs_alloc_split(cur, level, &nbno,
- &nkey, &ncur, &i)))
- return error;
- if (i) {
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
-#ifdef DEBUG
- if ((error =
- xfs_btree_check_sblock(cur,
- block, level, bp)))
- return error;
-#endif
- ptr = cur->bc_ptrs[level];
- nrec.ar_startblock = nkey.ar_startblock;
- nrec.ar_blockcount = nkey.ar_blockcount;
- }
- /*
- * Otherwise the insert fails.
- */
- else {
- *stat = 0;
- return 0;
- }
- }
- }
- }
- /*
- * At this point we know there's room for our new entry in the block
- * we're pointing at.
- */
- numrecs = be16_to_cpu(block->bb_numrecs);
- if (level > 0) {
- /*
- * It's a non-leaf entry. Make a hole for the new data
- * in the key and ptr regions of the block.
- */
- kp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
- pp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = numrecs; i >= ptr; i--) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(pp[i - 1]), level)))
- return error;
+ len = 0;
}
-#endif
- memmove(&kp[ptr], &kp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*kp));
- memmove(&pp[ptr], &pp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*pp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, *bnop, level)))
- return error;
-#endif
- /*
- * Now stuff the new data in, bump numrecs and log the new data.
- */
- kp[ptr - 1] = key;
- pp[ptr - 1] = cpu_to_be32(*bnop);
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_alloc_log_keys(cur, bp, ptr, numrecs);
- xfs_alloc_log_ptrs(cur, bp, ptr, numrecs);
-#ifdef DEBUG
- if (ptr < numrecs)
- xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1,
- kp + ptr);
-#endif
- } else {
- /*
- * It's a leaf entry. Make a hole for the new record.
- */
- rp = XFS_ALLOC_REC_ADDR(block, 1, cur);
- memmove(&rp[ptr], &rp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*rp));
- /*
- * Now stuff the new record in, bump numrecs
- * and log the new data.
- */
- rp[ptr - 1] = *recp;
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_alloc_log_recs(cur, bp, ptr, numrecs);
-#ifdef DEBUG
- if (ptr < numrecs)
- xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1,
- rp + ptr);
-#endif
- }
- /*
- * Log the new number of records in the btree header.
- */
- xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
- /*
- * If we inserted at the start of a block, update the parents' keys.
- */
- if (optr == 1 && (error = xfs_alloc_updkey(cur, &key, level + 1)))
- return error;
- /*
- * Look to see if the longest extent in the allocation group
- * needs to be updated.
- */
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- if (level == 0 &&
- cur->bc_btnum == XFS_BTNUM_CNT &&
- be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK &&
- be32_to_cpu(recp->ar_blockcount) > be32_to_cpu(agf->agf_longest)) {
- /*
- * If this is a leaf in the by-size btree and there
- * is no right sibling block and this block is bigger
- * than the previous longest block, update it.
- */
- agf->agf_longest = recp->ar_blockcount;
- cur->bc_mp->m_perag[be32_to_cpu(agf->agf_seqno)].pagf_longest
- = be32_to_cpu(recp->ar_blockcount);
- xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
- XFS_AGF_LONGEST);
+ break;
+ default:
+ ASSERT(0);
+ return;
}
- /*
- * Return the new block number, if any.
- * If there is one, give back a record value and a cursor too.
- */
- *bnop = nbno;
- if (nbno != NULLAGBLOCK) {
- *recp = nrec;
- *curp = ncur;
- }
- *stat = 1;
- return 0;
+
+ agf->agf_longest = len;
+ cur->bc_mp->m_perag[seqno].pagf_longest = be32_to_cpu(len);
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp, XFS_AGF_LONGEST);
}
-/*
- * Log header fields from a btree block.
- */
-STATIC void
-xfs_alloc_log_block(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_buf_t *bp, /* buffer containing btree block */
- int fields) /* mask of fields: XFS_BB_... */
+STATIC int
+xfs_allocbt_get_minrecs(
+ struct xfs_btree_cur *cur,
+ int level)
{
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- static const short offsets[] = { /* table of offsets */
- offsetof(xfs_alloc_block_t, bb_magic),
- offsetof(xfs_alloc_block_t, bb_level),
- offsetof(xfs_alloc_block_t, bb_numrecs),
- offsetof(xfs_alloc_block_t, bb_leftsib),
- offsetof(xfs_alloc_block_t, bb_rightsib),
- sizeof(xfs_alloc_block_t)
- };
+ return cur->bc_mp->m_alloc_mnr[level != 0];
+}
- xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first, &last);
- xfs_trans_log_buf(tp, bp, first, last);
+STATIC int
+xfs_allocbt_get_maxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ return cur->bc_mp->m_alloc_mxr[level != 0];
}
-/*
- * Log keys from a btree block (nonleaf).
- */
STATIC void
-xfs_alloc_log_keys(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int kfirst, /* index of first key to log */
- int klast) /* index of last key to log */
+xfs_allocbt_init_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
{
- xfs_alloc_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- xfs_alloc_key_t *kp; /* key pointer in btree block */
- int last; /* last byte offset logged */
+ ASSERT(rec->alloc.ar_startblock != 0);
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- kp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ key->alloc.ar_startblock = rec->alloc.ar_startblock;
+ key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
}
-/*
- * Log block pointer fields from a btree block (nonleaf).
- */
STATIC void
-xfs_alloc_log_ptrs(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int pfirst, /* index of first pointer to log */
- int plast) /* index of last pointer to log */
+xfs_allocbt_init_rec_from_key(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
{
- xfs_alloc_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- xfs_alloc_ptr_t *pp; /* block-pointer pointer in btree blk */
+ ASSERT(key->alloc.ar_startblock != 0);
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- pp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ rec->alloc.ar_startblock = key->alloc.ar_startblock;
+ rec->alloc.ar_blockcount = key->alloc.ar_blockcount;
}
-/*
- * Log records from a btree block (leaf).
- */
STATIC void
-xfs_alloc_log_recs(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int rfirst, /* index of first record to log */
- int rlast) /* index of last record to log */
+xfs_allocbt_init_rec_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec)
{
- xfs_alloc_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- xfs_alloc_rec_t *rp; /* record pointer for btree block */
-
+ ASSERT(cur->bc_rec.a.ar_startblock != 0);
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- rp = XFS_ALLOC_REC_ADDR(block, 1, cur);
-#ifdef DEBUG
- {
- xfs_agf_t *agf;
- xfs_alloc_rec_t *p;
-
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- for (p = &rp[rfirst - 1]; p <= &rp[rlast - 1]; p++)
- ASSERT(be32_to_cpu(p->ar_startblock) +
- be32_to_cpu(p->ar_blockcount) <=
- be32_to_cpu(agf->agf_length));
- }
-#endif
- first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
+ rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
}
-/*
- * Lookup the record. The cursor is made to point to it, based on dir.
- * Return 0 if can't find any such record, 1 for success.
- */
-STATIC int /* error */
-xfs_alloc_lookup(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_lookup_t dir, /* <=, ==, or >= */
- int *stat) /* success/failure */
+STATIC void
+xfs_allocbt_init_ptr_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
{
- xfs_agblock_t agbno; /* a.g. relative btree block number */
- xfs_agnumber_t agno; /* allocation group number */
- xfs_alloc_block_t *block=NULL; /* current btree block */
- int diff; /* difference for the current key */
- int error; /* error return value */
- int keyno=0; /* current key number */
- int level; /* level in the btree */
- xfs_mount_t *mp; /* file system mount point */
-
- XFS_STATS_INC(xs_abt_lookup);
- /*
- * Get the allocation group header, and the root block number.
- */
- mp = cur->bc_mp;
-
- {
- xfs_agf_t *agf; /* a.g. freespace header */
-
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- agno = be32_to_cpu(agf->agf_seqno);
- agbno = be32_to_cpu(agf->agf_roots[cur->bc_btnum]);
- }
- /*
- * Iterate over each level in the btree, starting at the root.
- * For each level above the leaves, find the key we need, based
- * on the lookup record, then follow the corresponding block
- * pointer down to the next level.
- */
- for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
- xfs_buf_t *bp; /* buffer pointer for btree block */
- xfs_daddr_t d; /* disk address of btree block */
-
- /*
- * Get the disk address we're looking for.
- */
- d = XFS_AGB_TO_DADDR(mp, agno, agbno);
- /*
- * If the old buffer at this level is for a different block,
- * throw it away, otherwise just use it.
- */
- bp = cur->bc_bufs[level];
- if (bp && XFS_BUF_ADDR(bp) != d)
- bp = NULL;
- if (!bp) {
- /*
- * Need to get a new buffer. Read it, then
- * set it in the cursor, releasing the old one.
- */
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp, agno,
- agbno, 0, &bp, XFS_ALLOC_BTREE_REF)))
- return error;
- xfs_btree_setbuf(cur, level, bp);
- /*
- * Point to the btree block, now that we have the buffer
- */
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, level,
- bp)))
- return error;
- } else
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- /*
- * If we already had a key match at a higher level, we know
- * we need to use the first entry in this block.
- */
- if (diff == 0)
- keyno = 1;
- /*
- * Otherwise we need to search this block. Do a binary search.
- */
- else {
- int high; /* high entry number */
- xfs_alloc_key_t *kkbase=NULL;/* base of keys in block */
- xfs_alloc_rec_t *krbase=NULL;/* base of records in block */
- int low; /* low entry number */
-
- /*
- * Get a pointer to keys or records.
- */
- if (level > 0)
- kkbase = XFS_ALLOC_KEY_ADDR(block, 1, cur);
- else
- krbase = XFS_ALLOC_REC_ADDR(block, 1, cur);
- /*
- * Set low and high entry numbers, 1-based.
- */
- low = 1;
- if (!(high = be16_to_cpu(block->bb_numrecs))) {
- /*
- * If the block is empty, the tree must
- * be an empty leaf.
- */
- ASSERT(level == 0 && cur->bc_nlevels == 1);
- cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
- *stat = 0;
- return 0;
- }
- /*
- * Binary search the block.
- */
- while (low <= high) {
- xfs_extlen_t blockcount; /* key value */
- xfs_agblock_t startblock; /* key value */
-
- XFS_STATS_INC(xs_abt_compare);
- /*
- * keyno is average of low and high.
- */
- keyno = (low + high) >> 1;
- /*
- * Get startblock & blockcount.
- */
- if (level > 0) {
- xfs_alloc_key_t *kkp;
-
- kkp = kkbase + keyno - 1;
- startblock = be32_to_cpu(kkp->ar_startblock);
- blockcount = be32_to_cpu(kkp->ar_blockcount);
- } else {
- xfs_alloc_rec_t *krp;
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- krp = krbase + keyno - 1;
- startblock = be32_to_cpu(krp->ar_startblock);
- blockcount = be32_to_cpu(krp->ar_blockcount);
- }
- /*
- * Compute difference to get next direction.
- */
- if (cur->bc_btnum == XFS_BTNUM_BNO)
- diff = (int)startblock -
- (int)cur->bc_rec.a.ar_startblock;
- else if (!(diff = (int)blockcount -
- (int)cur->bc_rec.a.ar_blockcount))
- diff = (int)startblock -
- (int)cur->bc_rec.a.ar_startblock;
- /*
- * Less than, move right.
- */
- if (diff < 0)
- low = keyno + 1;
- /*
- * Greater than, move left.
- */
- else if (diff > 0)
- high = keyno - 1;
- /*
- * Equal, we're done.
- */
- else
- break;
- }
- }
- /*
- * If there are more levels, set up for the next level
- * by getting the block number and filling in the cursor.
- */
- if (level > 0) {
- /*
- * If we moved left, need the previous key number,
- * unless there isn't one.
- */
- if (diff > 0 && --keyno < 1)
- keyno = 1;
- agbno = be32_to_cpu(*XFS_ALLOC_PTR_ADDR(block, keyno, cur));
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, agbno, level)))
- return error;
-#endif
- cur->bc_ptrs[level] = keyno;
- }
- }
- /*
- * Done with the search.
- * See if we need to adjust the results.
- */
- if (dir != XFS_LOOKUP_LE && diff < 0) {
- keyno++;
- /*
- * If ge search and we went off the end of the block, but it's
- * not the last block, we're in the wrong block.
- */
- if (dir == XFS_LOOKUP_GE &&
- keyno > be16_to_cpu(block->bb_numrecs) &&
- be32_to_cpu(block->bb_rightsib) != NULLAGBLOCK) {
- int i;
+ ASSERT(cur->bc_private.a.agno == be32_to_cpu(agf->agf_seqno));
+ ASSERT(agf->agf_roots[cur->bc_btnum] != 0);
- cur->bc_ptrs[0] = keyno;
- if ((error = xfs_alloc_increment(cur, 0, &i)))
- return error;
- XFS_WANT_CORRUPTED_RETURN(i == 1);
- *stat = 1;
- return 0;
- }
- }
- else if (dir == XFS_LOOKUP_LE && diff > 0)
- keyno--;
- cur->bc_ptrs[0] = keyno;
- /*
- * Return if we succeeded or not.
- */
- if (keyno == 0 || keyno > be16_to_cpu(block->bb_numrecs))
- *stat = 0;
- else
- *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
- return 0;
+ ptr->s = agf->agf_roots[cur->bc_btnum];
}
-/*
- * Move 1 record left from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_alloc_lshift(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to shift record on */
- int *stat) /* success/failure */
+STATIC __int64_t
+xfs_allocbt_key_diff(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key)
{
- int error; /* error return value */
-#ifdef DEBUG
- int i; /* loop index */
-#endif
- xfs_alloc_key_t key; /* key value for leaf level upward */
- xfs_buf_t *lbp; /* buffer for left neighbor block */
- xfs_alloc_block_t *left; /* left neighbor btree block */
- int nrec; /* new number of left block entries */
- xfs_buf_t *rbp; /* buffer for right (current) block */
- xfs_alloc_block_t *right; /* right (current) btree block */
- xfs_alloc_key_t *rkp=NULL; /* key pointer for right block */
- xfs_alloc_ptr_t *rpp=NULL; /* address pointer for right block */
- xfs_alloc_rec_t *rrp=NULL; /* record pointer for right block */
+ xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
+ xfs_alloc_key_t *kp = &key->alloc;
+ __int64_t diff;
- /*
- * Set up variables for this block as "right".
- */
- rbp = cur->bc_bufs[level];
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
-#endif
- /*
- * If we've got no left sibling then we can't shift an entry left.
- */
- if (be32_to_cpu(right->bb_leftsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * If the cursor entry is the one that would be moved, don't
- * do it... it's too complicated.
- */
- if (cur->bc_ptrs[level] <= 1) {
- *stat = 0;
- return 0;
- }
- /*
- * Set up the left neighbor as "left".
- */
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(right->bb_leftsib),
- 0, &lbp, XFS_ALLOC_BTREE_REF)))
- return error;
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
- /*
- * If it's full, it can't take another entry.
- */
- if (be16_to_cpu(left->bb_numrecs) == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
- *stat = 0;
- return 0;
+ if (cur->bc_btnum == XFS_BTNUM_BNO) {
+ return (__int64_t)be32_to_cpu(kp->ar_startblock) -
+ rec->ar_startblock;
}
- nrec = be16_to_cpu(left->bb_numrecs) + 1;
- /*
- * If non-leaf, copy a key and a ptr to the left block.
- */
- if (level > 0) {
- xfs_alloc_key_t *lkp; /* key pointer for left block */
- xfs_alloc_ptr_t *lpp; /* address pointer for left block */
- lkp = XFS_ALLOC_KEY_ADDR(left, nrec, cur);
- rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
- *lkp = *rkp;
- xfs_alloc_log_keys(cur, lbp, nrec, nrec);
- lpp = XFS_ALLOC_PTR_ADDR(left, nrec, cur);
- rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(*rpp), level)))
- return error;
-#endif
- *lpp = *rpp;
- xfs_alloc_log_ptrs(cur, lbp, nrec, nrec);
- xfs_btree_check_key(cur->bc_btnum, lkp - 1, lkp);
- }
- /*
- * If leaf, copy a record to the left block.
- */
- else {
- xfs_alloc_rec_t *lrp; /* record pointer for left block */
+ diff = (__int64_t)be32_to_cpu(kp->ar_blockcount) - rec->ar_blockcount;
+ if (diff)
+ return diff;
- lrp = XFS_ALLOC_REC_ADDR(left, nrec, cur);
- rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
- *lrp = *rrp;
- xfs_alloc_log_recs(cur, lbp, nrec, nrec);
- xfs_btree_check_rec(cur->bc_btnum, lrp - 1, lrp);
- }
- /*
- * Bump and log left's numrecs, decrement and log right's numrecs.
- */
- be16_add_cpu(&left->bb_numrecs, 1);
- xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
- be16_add_cpu(&right->bb_numrecs, -1);
- xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
- /*
- * Slide the contents of right down one entry.
- */
- if (level > 0) {
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(right->bb_numrecs); i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i + 1]),
- level)))
- return error;
- }
-#endif
- memmove(rkp, rkp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memmove(rpp, rpp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
- xfs_alloc_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- xfs_alloc_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- } else {
- memmove(rrp, rrp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- xfs_alloc_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- key.ar_startblock = rrp->ar_startblock;
- key.ar_blockcount = rrp->ar_blockcount;
- rkp = &key;
- }
- /*
- * Update the parent key values of right.
- */
- if ((error = xfs_alloc_updkey(cur, rkp, level + 1)))
- return error;
- /*
- * Slide the cursor value left one.
- */
- cur->bc_ptrs[level]--;
- *stat = 1;
- return 0;
+ return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
}
-/*
- * Allocate a new root block, fill it in.
- */
-STATIC int /* error */
-xfs_alloc_newroot(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
+STATIC int
+xfs_allocbt_kill_root(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ int level,
+ union xfs_btree_ptr *newroot)
{
- int error; /* error return value */
- xfs_agblock_t lbno; /* left block number */
- xfs_buf_t *lbp; /* left btree buffer */
- xfs_alloc_block_t *left; /* left btree block */
- xfs_mount_t *mp; /* mount structure */
- xfs_agblock_t nbno; /* new block number */
- xfs_buf_t *nbp; /* new (root) buffer */
- xfs_alloc_block_t *new; /* new (root) btree block */
- int nptr; /* new value for key index, 1 or 2 */
- xfs_agblock_t rbno; /* right block number */
- xfs_buf_t *rbp; /* right btree buffer */
- xfs_alloc_block_t *right; /* right btree block */
-
- mp = cur->bc_mp;
+ int error;
- ASSERT(cur->bc_nlevels < XFS_AG_MAXLEVELS(mp));
- /*
- * Get a buffer from the freelist blocks, for the new root.
- */
- error = xfs_alloc_get_freelist(cur->bc_tp,
- cur->bc_private.a.agbp, &nbno, 1);
- if (error)
- return error;
- /*
- * None available, we fail.
- */
- if (nbno == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- xfs_trans_agbtree_delta(cur->bc_tp, 1);
- nbp = xfs_btree_get_bufs(mp, cur->bc_tp, cur->bc_private.a.agno, nbno,
- 0);
- new = XFS_BUF_TO_ALLOC_BLOCK(nbp);
- /*
- * Set the root data in the a.g. freespace structure.
- */
- {
- xfs_agf_t *agf; /* a.g. freespace header */
- xfs_agnumber_t seqno;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_STATS_INC(cur, killroot);
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- agf->agf_roots[cur->bc_btnum] = cpu_to_be32(nbno);
- be32_add_cpu(&agf->agf_levels[cur->bc_btnum], 1);
- seqno = be32_to_cpu(agf->agf_seqno);
- mp->m_perag[seqno].pagf_levels[cur->bc_btnum]++;
- xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
- XFS_AGF_ROOTS | XFS_AGF_LEVELS);
- }
/*
- * At the previous root level there are now two blocks: the old
- * root, and the new block generated when it was split.
- * We don't know which one the cursor is pointing at, so we
- * set up variables "left" and "right" for each case.
+ * Update the root pointer, decreasing the level by 1 and then
+ * free the old root.
*/
- lbp = cur->bc_bufs[cur->bc_nlevels - 1];
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, cur->bc_nlevels - 1, lbp)))
+ xfs_allocbt_set_root(cur, newroot, -1);
+ error = xfs_allocbt_free_block(cur, bp);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
return error;
-#endif
- if (be32_to_cpu(left->bb_rightsib) != NULLAGBLOCK) {
- /*
- * Our block is left, pick up the right block.
- */
- lbno = XFS_DADDR_TO_AGBNO(mp, XFS_BUF_ADDR(lbp));
- rbno = be32_to_cpu(left->bb_rightsib);
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, rbno, 0, &rbp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
- if ((error = xfs_btree_check_sblock(cur, right,
- cur->bc_nlevels - 1, rbp)))
- return error;
- nptr = 1;
- } else {
- /*
- * Our block is right, pick up the left block.
- */
- rbp = lbp;
- right = left;
- rbno = XFS_DADDR_TO_AGBNO(mp, XFS_BUF_ADDR(rbp));
- lbno = be32_to_cpu(right->bb_leftsib);
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, lbno, 0, &lbp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
- if ((error = xfs_btree_check_sblock(cur, left,
- cur->bc_nlevels - 1, lbp)))
- return error;
- nptr = 2;
}
- /*
- * Fill in the new block's btree header and log it.
- */
- new->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
- new->bb_level = cpu_to_be16(cur->bc_nlevels);
- new->bb_numrecs = cpu_to_be16(2);
- new->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
- new->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
- xfs_alloc_log_block(cur->bc_tp, nbp, XFS_BB_ALL_BITS);
- ASSERT(lbno != NULLAGBLOCK && rbno != NULLAGBLOCK);
- /*
- * Fill in the key data in the new root.
- */
- {
- xfs_alloc_key_t *kp; /* btree key pointer */
- kp = XFS_ALLOC_KEY_ADDR(new, 1, cur);
- if (be16_to_cpu(left->bb_level) > 0) {
- kp[0] = *XFS_ALLOC_KEY_ADDR(left, 1, cur);
- kp[1] = *XFS_ALLOC_KEY_ADDR(right, 1, cur);
- } else {
- xfs_alloc_rec_t *rp; /* btree record pointer */
+ XFS_BTREE_STATS_INC(cur, free);
- rp = XFS_ALLOC_REC_ADDR(left, 1, cur);
- kp[0].ar_startblock = rp->ar_startblock;
- kp[0].ar_blockcount = rp->ar_blockcount;
- rp = XFS_ALLOC_REC_ADDR(right, 1, cur);
- kp[1].ar_startblock = rp->ar_startblock;
- kp[1].ar_blockcount = rp->ar_blockcount;
- }
- }
- xfs_alloc_log_keys(cur, nbp, 1, 2);
- /*
- * Fill in the pointer data in the new root.
- */
- {
- xfs_alloc_ptr_t *pp; /* btree address pointer */
+ xfs_btree_setbuf(cur, level, NULL);
+ cur->bc_nlevels--;
- pp = XFS_ALLOC_PTR_ADDR(new, 1, cur);
- pp[0] = cpu_to_be32(lbno);
- pp[1] = cpu_to_be32(rbno);
- }
- xfs_alloc_log_ptrs(cur, nbp, 1, 2);
- /*
- * Fix up the cursor.
- */
- xfs_btree_setbuf(cur, cur->bc_nlevels, nbp);
- cur->bc_ptrs[cur->bc_nlevels] = nptr;
- cur->bc_nlevels++;
- *stat = 1;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
return 0;
}
-/*
- * Move 1 record right from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_alloc_rshift(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to shift record on */
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i; /* loop index */
- xfs_alloc_key_t key; /* key value for leaf level upward */
- xfs_buf_t *lbp; /* buffer for left (current) block */
- xfs_alloc_block_t *left; /* left (current) btree block */
- xfs_buf_t *rbp; /* buffer for right neighbor block */
- xfs_alloc_block_t *right; /* right neighbor btree block */
- xfs_alloc_key_t *rkp; /* key pointer for right block */
- xfs_btree_cur_t *tcur; /* temporary cursor */
-
- /*
- * Set up variables for this block as "left".
- */
- lbp = cur->bc_bufs[level];
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
-#endif
- /*
- * If we've got no right sibling then we can't shift an entry right.
- */
- if (be32_to_cpu(left->bb_rightsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * If the cursor entry is the one that would be moved, don't
- * do it... it's too complicated.
- */
- if (cur->bc_ptrs[level] >= be16_to_cpu(left->bb_numrecs)) {
- *stat = 0;
- return 0;
- }
- /*
- * Set up the right neighbor as "right".
- */
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(left->bb_rightsib),
- 0, &rbp, XFS_ALLOC_BTREE_REF)))
- return error;
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
- /*
- * If it's full, it can't take another entry.
- */
- if (be16_to_cpu(right->bb_numrecs) == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
- *stat = 0;
- return 0;
- }
- /*
- * Make a hole at the start of the right neighbor block, then
- * copy the last left block entry to the hole.
- */
- if (level > 0) {
- xfs_alloc_key_t *lkp; /* key pointer for left block */
- xfs_alloc_ptr_t *lpp; /* address pointer for left block */
- xfs_alloc_ptr_t *rpp; /* address pointer for right block */
-
- lkp = XFS_ALLOC_KEY_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- lpp = XFS_ALLOC_PTR_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
- rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
#ifdef DEBUG
- for (i = be16_to_cpu(right->bb_numrecs) - 1; i >= 0; i--) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i]), level)))
- return error;
- }
-#endif
- memmove(rkp + 1, rkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memmove(rpp + 1, rpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(*lpp), level)))
- return error;
-#endif
- *rkp = *lkp;
- *rpp = *lpp;
- xfs_alloc_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- xfs_alloc_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- xfs_btree_check_key(cur->bc_btnum, rkp, rkp + 1);
+STATIC int
+xfs_allocbt_keys_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ if (cur->bc_btnum == XFS_BTNUM_BNO) {
+ return be32_to_cpu(k1->alloc.ar_startblock) <
+ be32_to_cpu(k2->alloc.ar_startblock);
} else {
- xfs_alloc_rec_t *lrp; /* record pointer for left block */
- xfs_alloc_rec_t *rrp; /* record pointer for right block */
-
- lrp = XFS_ALLOC_REC_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
- memmove(rrp + 1, rrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- *rrp = *lrp;
- xfs_alloc_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- key.ar_startblock = rrp->ar_startblock;
- key.ar_blockcount = rrp->ar_blockcount;
- rkp = &key;
- xfs_btree_check_rec(cur->bc_btnum, rrp, rrp + 1);
+ return be32_to_cpu(k1->alloc.ar_blockcount) <
+ be32_to_cpu(k2->alloc.ar_blockcount) ||
+ (k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
+ be32_to_cpu(k1->alloc.ar_startblock) <
+ be32_to_cpu(k2->alloc.ar_startblock));
}
- /*
- * Decrement and log left's numrecs, bump and log right's numrecs.
- */
- be16_add_cpu(&left->bb_numrecs, -1);
- xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
- be16_add_cpu(&right->bb_numrecs, 1);
- xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
- /*
- * Using a temporary cursor, update the parent key values of the
- * block on the right.
- */
- if ((error = xfs_btree_dup_cursor(cur, &tcur)))
- return error;
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_alloc_increment(tcur, level, &i)) ||
- (error = xfs_alloc_updkey(tcur, rkp, level + 1)))
- goto error0;
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- *stat = 1;
- return 0;
-error0:
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
}
-/*
- * Split cur/level block in half.
- * Return new block number and its first record (to be inserted into parent).
- */
-STATIC int /* error */
-xfs_alloc_split(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to split */
- xfs_agblock_t *bnop, /* output: block number allocated */
- xfs_alloc_key_t *keyp, /* output: first key of new block */
- xfs_btree_cur_t **curp, /* output: new cursor */
- int *stat) /* success/failure */
+STATIC int
+xfs_allocbt_recs_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2)
{
- int error; /* error return value */
- int i; /* loop index/record number */
- xfs_agblock_t lbno; /* left (current) block number */
- xfs_buf_t *lbp; /* buffer for left block */
- xfs_alloc_block_t *left; /* left (current) btree block */
- xfs_agblock_t rbno; /* right (new) block number */
- xfs_buf_t *rbp; /* buffer for right block */
- xfs_alloc_block_t *right; /* right (new) btree block */
-
- /*
- * Allocate the new block from the freelist.
- * If we can't do it, we're toast. Give up.
- */
- error = xfs_alloc_get_freelist(cur->bc_tp,
- cur->bc_private.a.agbp, &rbno, 1);
- if (error)
- return error;
- if (rbno == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- xfs_trans_agbtree_delta(cur->bc_tp, 1);
- rbp = xfs_btree_get_bufs(cur->bc_mp, cur->bc_tp, cur->bc_private.a.agno,
- rbno, 0);
- /*
- * Set up the new block as "right".
- */
- right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
- /*
- * "Left" is the current (according to the cursor) block.
- */
- lbp = cur->bc_bufs[level];
- left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
-#endif
- /*
- * Fill in the btree header for the new block.
- */
- right->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
- right->bb_level = left->bb_level;
- right->bb_numrecs = cpu_to_be16(be16_to_cpu(left->bb_numrecs) / 2);
- /*
- * Make sure that if there's an odd number of entries now, that
- * each new block will have the same number of entries.
- */
- if ((be16_to_cpu(left->bb_numrecs) & 1) &&
- cur->bc_ptrs[level] <= be16_to_cpu(right->bb_numrecs) + 1)
- be16_add_cpu(&right->bb_numrecs, 1);
- i = be16_to_cpu(left->bb_numrecs) - be16_to_cpu(right->bb_numrecs) + 1;
- /*
- * For non-leaf blocks, copy keys and addresses over to the new block.
- */
- if (level > 0) {
- xfs_alloc_key_t *lkp; /* left btree key pointer */
- xfs_alloc_ptr_t *lpp; /* left btree address pointer */
- xfs_alloc_key_t *rkp; /* right btree key pointer */
- xfs_alloc_ptr_t *rpp; /* right btree address pointer */
-
- lkp = XFS_ALLOC_KEY_ADDR(left, i, cur);
- lpp = XFS_ALLOC_PTR_ADDR(left, i, cur);
- rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
- rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(right->bb_numrecs); i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(lpp[i]), level)))
- return error;
- }
-#endif
- memcpy(rkp, lkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memcpy(rpp, lpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
- xfs_alloc_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- xfs_alloc_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- *keyp = *rkp;
+ if (cur->bc_btnum == XFS_BTNUM_BNO) {
+ return be32_to_cpu(r1->alloc.ar_startblock) +
+ be32_to_cpu(r1->alloc.ar_blockcount) <=
+ be32_to_cpu(r2->alloc.ar_startblock);
+ } else {
+ return be32_to_cpu(r1->alloc.ar_blockcount) <
+ be32_to_cpu(r2->alloc.ar_blockcount) ||
+ (r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
+ be32_to_cpu(r1->alloc.ar_startblock) <
+ be32_to_cpu(r2->alloc.ar_startblock));
}
- /*
- * For leaf blocks, copy records over to the new block.
- */
- else {
- xfs_alloc_rec_t *lrp; /* left btree record pointer */
- xfs_alloc_rec_t *rrp; /* right btree record pointer */
+}
+#endif /* DEBUG */
- lrp = XFS_ALLOC_REC_ADDR(left, i, cur);
- rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
- memcpy(rrp, lrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- xfs_alloc_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- keyp->ar_startblock = rrp->ar_startblock;
- keyp->ar_blockcount = rrp->ar_blockcount;
- }
- /*
- * Find the left block number by looking in the buffer.
- * Adjust numrecs, sibling pointers.
- */
- lbno = XFS_DADDR_TO_AGBNO(cur->bc_mp, XFS_BUF_ADDR(lbp));
- be16_add_cpu(&left->bb_numrecs, -(be16_to_cpu(right->bb_numrecs)));
- right->bb_rightsib = left->bb_rightsib;
- left->bb_rightsib = cpu_to_be32(rbno);
- right->bb_leftsib = cpu_to_be32(lbno);
- xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_ALL_BITS);
- xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
- /*
- * If there's a block to the new block's right, make that block
- * point back to right instead of to left.
- */
- if (be32_to_cpu(right->bb_rightsib) != NULLAGBLOCK) {
- xfs_alloc_block_t *rrblock; /* rr btree block */
- xfs_buf_t *rrbp; /* buffer for rrblock */
+#ifdef XFS_BTREE_TRACE
+ktrace_t *xfs_allocbt_trace_buf;
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(right->bb_rightsib), 0,
- &rrbp, XFS_ALLOC_BTREE_REF)))
- return error;
- rrblock = XFS_BUF_TO_ALLOC_BLOCK(rrbp);
- if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
- return error;
- rrblock->bb_leftsib = cpu_to_be32(rbno);
- xfs_alloc_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
- }
- /*
- * If the cursor is really in the right block, move it there.
- * If it's just pointing past the last entry in left, then we'll
- * insert there, so don't change anything in that case.
- */
- if (cur->bc_ptrs[level] > be16_to_cpu(left->bb_numrecs) + 1) {
- xfs_btree_setbuf(cur, level, rbp);
- cur->bc_ptrs[level] -= be16_to_cpu(left->bb_numrecs);
- }
- /*
- * If there are more levels, we'll need another cursor which refers to
- * the right block, no matter where this cursor was.
- */
- if (level + 1 < cur->bc_nlevels) {
- if ((error = xfs_btree_dup_cursor(cur, curp)))
- return error;
- (*curp)->bc_ptrs[level + 1]++;
- }
- *bnop = rbno;
- *stat = 1;
- return 0;
+STATIC void
+xfs_allocbt_trace_enter(
+ struct xfs_btree_cur *cur,
+ const char *func,
+ char *s,
+ int type,
+ int line,
+ __psunsigned_t a0,
+ __psunsigned_t a1,
+ __psunsigned_t a2,
+ __psunsigned_t a3,
+ __psunsigned_t a4,
+ __psunsigned_t a5,
+ __psunsigned_t a6,
+ __psunsigned_t a7,
+ __psunsigned_t a8,
+ __psunsigned_t a9,
+ __psunsigned_t a10)
+{
+ ktrace_enter(xfs_allocbt_trace_buf, (void *)(__psint_t)type,
+ (void *)func, (void *)s, NULL, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
}
-/*
- * Update keys at all levels from here to the root along the cursor's path.
- */
-STATIC int /* error */
-xfs_alloc_updkey(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_alloc_key_t *keyp, /* new key value to update to */
- int level) /* starting level for update */
+STATIC void
+xfs_allocbt_trace_cursor(
+ struct xfs_btree_cur *cur,
+ __uint32_t *s0,
+ __uint64_t *l0,
+ __uint64_t *l1)
{
- int ptr; /* index of key in block */
-
- /*
- * Go up the tree from this level toward the root.
- * At each level, update the key value to the value input.
- * Stop when we reach a level where the cursor isn't pointing
- * at the first entry in the block.
- */
- for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
- xfs_alloc_block_t *block; /* btree block */
- xfs_buf_t *bp; /* buffer for block */
-#ifdef DEBUG
- int error; /* error return value */
-#endif
- xfs_alloc_key_t *kp; /* ptr to btree block keys */
-
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
-#endif
- ptr = cur->bc_ptrs[level];
- kp = XFS_ALLOC_KEY_ADDR(block, ptr, cur);
- *kp = *keyp;
- xfs_alloc_log_keys(cur, bp, ptr, ptr);
- }
- return 0;
+ *s0 = cur->bc_private.a.agno;
+ *l0 = cur->bc_rec.a.ar_startblock;
+ *l1 = cur->bc_rec.a.ar_blockcount;
}
-/*
- * Externally visible routines.
- */
-
-/*
- * Decrement cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-int /* error */
-xfs_alloc_decrement(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level in btree, 0 is leaf */
- int *stat) /* success/failure */
+STATIC void
+xfs_allocbt_trace_key(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key,
+ __uint64_t *l0,
+ __uint64_t *l1)
{
- xfs_alloc_block_t *block; /* btree block */
- int error; /* error return value */
- int lev; /* btree level */
-
- ASSERT(level < cur->bc_nlevels);
- /*
- * Read-ahead to the left at this level.
- */
- xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
- /*
- * Decrement the ptr at this level. If we're still in the block
- * then we're done.
- */
- if (--cur->bc_ptrs[level] > 0) {
- *stat = 1;
- return 0;
- }
- /*
- * Get a pointer to the btree block.
- */
- block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[level]);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level,
- cur->bc_bufs[level])))
- return error;
-#endif
- /*
- * If we just went off the left edge of the tree, return failure.
- */
- if (be32_to_cpu(block->bb_leftsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * March up the tree decrementing pointers.
- * Stop when we don't go off the left edge of a block.
- */
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- if (--cur->bc_ptrs[lev] > 0)
- break;
- /*
- * Read-ahead the left block, we're going to read it
- * in the next loop.
- */
- xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
- }
- /*
- * If we went off the root then we are seriously confused.
- */
- ASSERT(lev < cur->bc_nlevels);
- /*
- * Now walk back down the tree, fixing up the cursor's buffer
- * pointers and key numbers.
- */
- for (block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[lev]); lev > level; ) {
- xfs_agblock_t agbno; /* block number of btree block */
- xfs_buf_t *bp; /* buffer pointer for block */
-
- agbno = be32_to_cpu(*XFS_ALLOC_PTR_ADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, agbno, 0, &bp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
- cur->bc_ptrs[lev] = be16_to_cpu(block->bb_numrecs);
- }
- *stat = 1;
- return 0;
+ *l0 = be32_to_cpu(key->alloc.ar_startblock);
+ *l1 = be32_to_cpu(key->alloc.ar_blockcount);
}
-/*
- * Delete the record pointed to by cur.
- * The cursor refers to the place where the record was (could be inserted)
- * when the operation returns.
- */
-int /* error */
-xfs_alloc_delete(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
+STATIC void
+xfs_allocbt_trace_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ __uint64_t *l0,
+ __uint64_t *l1,
+ __uint64_t *l2)
{
- int error; /* error return value */
- int i; /* result code */
- int level; /* btree level */
-
- /*
- * Go up the tree, starting at leaf level.
- * If 2 is returned then a join was done; go to the next level.
- * Otherwise we are done.
- */
- for (level = 0, i = 2; i == 2; level++) {
- if ((error = xfs_alloc_delrec(cur, level, &i)))
- return error;
- }
- if (i == 0) {
- for (level = 1; level < cur->bc_nlevels; level++) {
- if (cur->bc_ptrs[level] == 0) {
- if ((error = xfs_alloc_decrement(cur, level, &i)))
- return error;
- break;
- }
- }
- }
- *stat = i;
- return 0;
+ *l0 = be32_to_cpu(rec->alloc.ar_startblock);
+ *l1 = be32_to_cpu(rec->alloc.ar_blockcount);
+ *l2 = 0;
}
+#endif /* XFS_BTREE_TRACE */
+
+static const struct xfs_btree_ops xfs_allocbt_ops = {
+ .rec_len = sizeof(xfs_alloc_rec_t),
+ .key_len = sizeof(xfs_alloc_key_t),
+
+ .dup_cursor = xfs_allocbt_dup_cursor,
+ .set_root = xfs_allocbt_set_root,
+ .kill_root = xfs_allocbt_kill_root,
+ .alloc_block = xfs_allocbt_alloc_block,
+ .free_block = xfs_allocbt_free_block,
+ .update_lastrec = xfs_allocbt_update_lastrec,
+ .get_minrecs = xfs_allocbt_get_minrecs,
+ .get_maxrecs = xfs_allocbt_get_maxrecs,
+ .init_key_from_rec = xfs_allocbt_init_key_from_rec,
+ .init_rec_from_key = xfs_allocbt_init_rec_from_key,
+ .init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
+ .init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
+ .key_diff = xfs_allocbt_key_diff,
-/*
- * Get the data from the pointed-to record.
- */
-int /* error */
-xfs_alloc_get_rec(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t *bno, /* output: starting block of extent */
- xfs_extlen_t *len, /* output: length of extent */
- int *stat) /* output: success/failure */
-{
- xfs_alloc_block_t *block; /* btree block */
#ifdef DEBUG
- int error; /* error return value */
+ .keys_inorder = xfs_allocbt_keys_inorder,
+ .recs_inorder = xfs_allocbt_recs_inorder,
#endif
- int ptr; /* record number */
- ptr = cur->bc_ptrs[0];
- block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[0]);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, 0, cur->bc_bufs[0])))
- return error;
+#ifdef XFS_BTREE_TRACE
+ .trace_enter = xfs_allocbt_trace_enter,
+ .trace_cursor = xfs_allocbt_trace_cursor,
+ .trace_key = xfs_allocbt_trace_key,
+ .trace_record = xfs_allocbt_trace_record,
#endif
- /*
- * Off the right end or left end, return failure.
- */
- if (ptr > be16_to_cpu(block->bb_numrecs) || ptr <= 0) {
- *stat = 0;
- return 0;
- }
- /*
- * Point to the record and extract its data.
- */
- {
- xfs_alloc_rec_t *rec; /* record data */
-
- rec = XFS_ALLOC_REC_ADDR(block, ptr, cur);
- *bno = be32_to_cpu(rec->ar_startblock);
- *len = be32_to_cpu(rec->ar_blockcount);
- }
- *stat = 1;
- return 0;
-}
+};
/*
- * Increment cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
+ * Allocate a new allocation btree cursor.
*/
-int /* error */
-xfs_alloc_increment(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level in btree, 0 is leaf */
- int *stat) /* success/failure */
+struct xfs_btree_cur * /* new alloc btree cursor */
+xfs_allocbt_init_cursor(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* buffer for agf structure */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_btnum_t btnum) /* btree identifier */
{
- xfs_alloc_block_t *block; /* btree block */
- xfs_buf_t *bp; /* tree block buffer */
- int error; /* error return value */
- int lev; /* btree level */
-
- ASSERT(level < cur->bc_nlevels);
- /*
- * Read-ahead to the right at this level.
- */
- xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
- /*
- * Get a pointer to the btree block.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
-#endif
- /*
- * Increment the ptr at this level. If we're still in the block
- * then we're done.
- */
- if (++cur->bc_ptrs[level] <= be16_to_cpu(block->bb_numrecs)) {
- *stat = 1;
- return 0;
- }
- /*
- * If we just went off the right edge of the tree, return failure.
- */
- if (be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * March up the tree incrementing pointers.
- * Stop when we don't go off the right edge of a block.
- */
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- bp = cur->bc_bufs[lev];
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
-#endif
- if (++cur->bc_ptrs[lev] <= be16_to_cpu(block->bb_numrecs))
- break;
- /*
- * Read-ahead the right block, we're going to read it
- * in the next loop.
- */
- xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
- }
- /*
- * If we went off the root then we are seriously confused.
- */
- ASSERT(lev < cur->bc_nlevels);
- /*
- * Now walk back down the tree, fixing up the cursor's buffer
- * pointers and key numbers.
- */
- for (bp = cur->bc_bufs[lev], block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- lev > level; ) {
- xfs_agblock_t agbno; /* block number of btree block */
+ struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
+ struct xfs_btree_cur *cur;
- agbno = be32_to_cpu(*XFS_ALLOC_PTR_ADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, agbno, 0, &bp,
- XFS_ALLOC_BTREE_REF)))
- return error;
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_ALLOC_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
- cur->bc_ptrs[lev] = 1;
- }
- *stat = 1;
- return 0;
-}
+ ASSERT(btnum == XFS_BTNUM_BNO || btnum == XFS_BTNUM_CNT);
-/*
- * Insert the current record at the point referenced by cur.
- * The cursor may be inconsistent on return if splits have been done.
- */
-int /* error */
-xfs_alloc_insert(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i; /* result value, 0 for failure */
- int level; /* current level number in btree */
- xfs_agblock_t nbno; /* new block number (split result) */
- xfs_btree_cur_t *ncur; /* new cursor (split result) */
- xfs_alloc_rec_t nrec; /* record being inserted this level */
- xfs_btree_cur_t *pcur; /* previous level's cursor */
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
- level = 0;
- nbno = NULLAGBLOCK;
- nrec.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
- nrec.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
- ncur = NULL;
- pcur = cur;
- /*
- * Loop going up the tree, starting at the leaf level.
- * Stop when we don't get a split block, that must mean that
- * the insert is finished with this level.
- */
- do {
- /*
- * Insert nrec/nbno into this level of the tree.
- * Note if we fail, nbno will be null.
- */
- if ((error = xfs_alloc_insrec(pcur, level++, &nbno, &nrec, &ncur,
- &i))) {
- if (pcur != cur)
- xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
- return error;
- }
- /*
- * See if the cursor we just used is trash.
- * Can't trash the caller's cursor, but otherwise we should
- * if ncur is a new cursor or we're about to be done.
- */
- if (pcur != cur && (ncur || nbno == NULLAGBLOCK)) {
- cur->bc_nlevels = pcur->bc_nlevels;
- xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
- }
- /*
- * If we got a new cursor, switch to it.
- */
- if (ncur) {
- pcur = ncur;
- ncur = NULL;
- }
- } while (nbno != NULLAGBLOCK);
- *stat = i;
- return 0;
-}
+ cur->bc_tp = tp;
+ cur->bc_mp = mp;
+ cur->bc_nlevels = be32_to_cpu(agf->agf_levels[btnum]);
+ cur->bc_btnum = btnum;
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
-/*
- * Lookup the record equal to [bno, len] in the btree given by cur.
- */
-int /* error */
-xfs_alloc_lookup_eq(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t bno, /* starting block of extent */
- xfs_extlen_t len, /* length of extent */
- int *stat) /* success/failure */
-{
- cur->bc_rec.a.ar_startblock = bno;
- cur->bc_rec.a.ar_blockcount = len;
- return xfs_alloc_lookup(cur, XFS_LOOKUP_EQ, stat);
-}
+ cur->bc_ops = &xfs_allocbt_ops;
+ if (btnum == XFS_BTNUM_CNT)
+ cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
-/*
- * Lookup the first record greater than or equal to [bno, len]
- * in the btree given by cur.
- */
-int /* error */
-xfs_alloc_lookup_ge(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t bno, /* starting block of extent */
- xfs_extlen_t len, /* length of extent */
- int *stat) /* success/failure */
-{
- cur->bc_rec.a.ar_startblock = bno;
- cur->bc_rec.a.ar_blockcount = len;
- return xfs_alloc_lookup(cur, XFS_LOOKUP_GE, stat);
-}
+ cur->bc_private.a.agbp = agbp;
+ cur->bc_private.a.agno = agno;
-/*
- * Lookup the first record less than or equal to [bno, len]
- * in the btree given by cur.
- */
-int /* error */
-xfs_alloc_lookup_le(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t bno, /* starting block of extent */
- xfs_extlen_t len, /* length of extent */
- int *stat) /* success/failure */
-{
- cur->bc_rec.a.ar_startblock = bno;
- cur->bc_rec.a.ar_blockcount = len;
- return xfs_alloc_lookup(cur, XFS_LOOKUP_LE, stat);
+ return cur;
}
/*
- * Update the record referred to by cur, to the value given by [bno, len].
- * This either works (return 0) or gets an EFSCORRUPTED error.
+ * Calculate number of records in an alloc btree block.
*/
-int /* error */
-xfs_alloc_update(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t bno, /* starting block of extent */
- xfs_extlen_t len) /* length of extent */
+int
+xfs_allocbt_maxrecs(
+ struct xfs_mount *mp,
+ int blocklen,
+ int leaf)
{
- xfs_alloc_block_t *block; /* btree block to update */
- int error; /* error return value */
- int ptr; /* current record number (updating) */
+ blocklen -= XFS_ALLOC_BLOCK_LEN(mp);
- ASSERT(len > 0);
- /*
- * Pick up the a.g. freelist struct and the current block.
- */
- block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[0]);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, 0, cur->bc_bufs[0])))
- return error;
-#endif
- /*
- * Get the address of the rec to be updated.
- */
- ptr = cur->bc_ptrs[0];
- {
- xfs_alloc_rec_t *rp; /* pointer to updated record */
-
- rp = XFS_ALLOC_REC_ADDR(block, ptr, cur);
- /*
- * Fill in the new contents and log them.
- */
- rp->ar_startblock = cpu_to_be32(bno);
- rp->ar_blockcount = cpu_to_be32(len);
- xfs_alloc_log_recs(cur, cur->bc_bufs[0], ptr, ptr);
- }
- /*
- * If it's the by-size btree and it's the last leaf block and
- * it's the last record... then update the size of the longest
- * extent in the a.g., which we cache in the a.g. freelist header.
- */
- if (cur->bc_btnum == XFS_BTNUM_CNT &&
- be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK &&
- ptr == be16_to_cpu(block->bb_numrecs)) {
- xfs_agf_t *agf; /* a.g. freespace header */
- xfs_agnumber_t seqno;
-
- agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
- seqno = be32_to_cpu(agf->agf_seqno);
- cur->bc_mp->m_perag[seqno].pagf_longest = len;
- agf->agf_longest = cpu_to_be32(len);
- xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
- XFS_AGF_LONGEST);
- }
- /*
- * Updating first record in leaf. Pass new key value up to our parent.
- */
- if (ptr == 1) {
- xfs_alloc_key_t key; /* key containing [bno, len] */
-
- key.ar_startblock = cpu_to_be32(bno);
- key.ar_blockcount = cpu_to_be32(len);
- if ((error = xfs_alloc_updkey(cur, &key, 1)))
- return error;
- }
- return 0;
+ if (leaf)
+ return blocklen / sizeof(xfs_alloc_rec_t);
+ return blocklen / (sizeof(xfs_alloc_key_t) + sizeof(xfs_alloc_ptr_t));
}
diff --git a/fs/xfs/xfs_alloc_btree.h b/fs/xfs/xfs_alloc_btree.h
index 5bd1a2c8bd07..a6caa0022c9b 100644
--- a/fs/xfs/xfs_alloc_btree.h
+++ b/fs/xfs/xfs_alloc_btree.h
@@ -24,7 +24,6 @@
struct xfs_buf;
struct xfs_btree_cur;
-struct xfs_btree_sblock;
struct xfs_mount;
/*
@@ -50,16 +49,6 @@ typedef struct xfs_alloc_rec_incore {
/* btree pointer type */
typedef __be32 xfs_alloc_ptr_t;
-/* btree block header type */
-typedef struct xfs_btree_sblock xfs_alloc_block_t;
-
-#define XFS_BUF_TO_ALLOC_BLOCK(bp) ((xfs_alloc_block_t *)XFS_BUF_PTR(bp))
-
-/*
- * Real block structures have a size equal to the disk block size.
- */
-#define XFS_ALLOC_BLOCK_MAXRECS(lev,cur) ((cur)->bc_mp->m_alloc_mxr[lev != 0])
-#define XFS_ALLOC_BLOCK_MINRECS(lev,cur) ((cur)->bc_mp->m_alloc_mnr[lev != 0])
/*
* Minimum and maximum blocksize and sectorsize.
@@ -83,73 +72,39 @@ typedef struct xfs_btree_sblock xfs_alloc_block_t;
#define XFS_CNT_BLOCK(mp) ((xfs_agblock_t)(XFS_BNO_BLOCK(mp) + 1))
/*
- * Record, key, and pointer address macros for btree blocks.
- */
-#define XFS_ALLOC_REC_ADDR(bb,i,cur) \
- XFS_BTREE_REC_ADDR(xfs_alloc, bb, i)
-
-#define XFS_ALLOC_KEY_ADDR(bb,i,cur) \
- XFS_BTREE_KEY_ADDR(xfs_alloc, bb, i)
-
-#define XFS_ALLOC_PTR_ADDR(bb,i,cur) \
- XFS_BTREE_PTR_ADDR(xfs_alloc, bb, i, XFS_ALLOC_BLOCK_MAXRECS(1, cur))
-
-/*
- * Decrement cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-extern int xfs_alloc_decrement(struct xfs_btree_cur *cur, int level, int *stat);
-
-/*
- * Delete the record pointed to by cur.
- * The cursor refers to the place where the record was (could be inserted)
- * when the operation returns.
- */
-extern int xfs_alloc_delete(struct xfs_btree_cur *cur, int *stat);
-
-/*
- * Get the data from the pointed-to record.
- */
-extern int xfs_alloc_get_rec(struct xfs_btree_cur *cur, xfs_agblock_t *bno,
- xfs_extlen_t *len, int *stat);
-
-/*
- * Increment cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-extern int xfs_alloc_increment(struct xfs_btree_cur *cur, int level, int *stat);
-
-/*
- * Insert the current record at the point referenced by cur.
- * The cursor may be inconsistent on return if splits have been done.
- */
-extern int xfs_alloc_insert(struct xfs_btree_cur *cur, int *stat);
-
-/*
- * Lookup the record equal to [bno, len] in the btree given by cur.
- */
-extern int xfs_alloc_lookup_eq(struct xfs_btree_cur *cur, xfs_agblock_t bno,
- xfs_extlen_t len, int *stat);
-
-/*
- * Lookup the first record greater than or equal to [bno, len]
- * in the btree given by cur.
- */
-extern int xfs_alloc_lookup_ge(struct xfs_btree_cur *cur, xfs_agblock_t bno,
- xfs_extlen_t len, int *stat);
-
-/*
- * Lookup the first record less than or equal to [bno, len]
- * in the btree given by cur.
+ * Btree block header size depends on a superblock flag.
+ *
+ * (not quite yet, but soon)
*/
-extern int xfs_alloc_lookup_le(struct xfs_btree_cur *cur, xfs_agblock_t bno,
- xfs_extlen_t len, int *stat);
+#define XFS_ALLOC_BLOCK_LEN(mp) XFS_BTREE_SBLOCK_LEN
/*
- * Update the record referred to by cur, to the value given by [bno, len].
- * This either works (return 0) or gets an EFSCORRUPTED error.
- */
-extern int xfs_alloc_update(struct xfs_btree_cur *cur, xfs_agblock_t bno,
- xfs_extlen_t len);
+ * Record, key, and pointer address macros for btree blocks.
+ *
+ * (note that some of these may appear unused, but they are used in userspace)
+ */
+#define XFS_ALLOC_REC_ADDR(mp, block, index) \
+ ((xfs_alloc_rec_t *) \
+ ((char *)(block) + \
+ XFS_ALLOC_BLOCK_LEN(mp) + \
+ (((index) - 1) * sizeof(xfs_alloc_rec_t))))
+
+#define XFS_ALLOC_KEY_ADDR(mp, block, index) \
+ ((xfs_alloc_key_t *) \
+ ((char *)(block) + \
+ XFS_ALLOC_BLOCK_LEN(mp) + \
+ ((index) - 1) * sizeof(xfs_alloc_key_t)))
+
+#define XFS_ALLOC_PTR_ADDR(mp, block, index, maxrecs) \
+ ((xfs_alloc_ptr_t *) \
+ ((char *)(block) + \
+ XFS_ALLOC_BLOCK_LEN(mp) + \
+ (maxrecs) * sizeof(xfs_alloc_key_t) + \
+ ((index) - 1) * sizeof(xfs_alloc_ptr_t)))
+
+extern struct xfs_btree_cur *xfs_allocbt_init_cursor(struct xfs_mount *,
+ struct xfs_trans *, struct xfs_buf *,
+ xfs_agnumber_t, xfs_btnum_t);
+extern int xfs_allocbt_maxrecs(struct xfs_mount *, int, int);
#endif /* __XFS_ALLOC_BTREE_H__ */
diff --git a/fs/xfs/xfs_arch.h b/fs/xfs/xfs_arch.h
index 0b3b5efe848c..53d5e70d1360 100644
--- a/fs/xfs/xfs_arch.h
+++ b/fs/xfs/xfs_arch.h
@@ -41,21 +41,36 @@
#endif
#ifdef XFS_NATIVE_HOST
-#define cpu_to_be16(val) ((__be16)(val))
-#define cpu_to_be32(val) ((__be32)(val))
-#define cpu_to_be64(val) ((__be64)(val))
-#define be16_to_cpu(val) ((__uint16_t)(val))
-#define be32_to_cpu(val) ((__uint32_t)(val))
-#define be64_to_cpu(val) ((__uint64_t)(val))
+#define cpu_to_be16(val) ((__force __be16)(__u16)(val))
+#define cpu_to_be32(val) ((__force __be32)(__u32)(val))
+#define cpu_to_be64(val) ((__force __be64)(__u64)(val))
+#define be16_to_cpu(val) ((__force __u16)(__be16)(val))
+#define be32_to_cpu(val) ((__force __u32)(__be32)(val))
+#define be64_to_cpu(val) ((__force __u64)(__be64)(val))
#else
-#define cpu_to_be16(val) (__swab16((__uint16_t)(val)))
-#define cpu_to_be32(val) (__swab32((__uint32_t)(val)))
-#define cpu_to_be64(val) (__swab64((__uint64_t)(val)))
-#define be16_to_cpu(val) (__swab16((__be16)(val)))
-#define be32_to_cpu(val) (__swab32((__be32)(val)))
-#define be64_to_cpu(val) (__swab64((__be64)(val)))
+#define cpu_to_be16(val) ((__force __be16)__swab16((__u16)(val)))
+#define cpu_to_be32(val) ((__force __be32)__swab32((__u32)(val)))
+#define cpu_to_be64(val) ((__force __be64)__swab64((__u64)(val)))
+#define be16_to_cpu(val) (__swab16((__force __u16)(__be16)(val)))
+#define be32_to_cpu(val) (__swab32((__force __u32)(__be32)(val)))
+#define be64_to_cpu(val) (__swab64((__force __u64)(__be64)(val)))
#endif
+static inline void be16_add_cpu(__be16 *a, __s16 b)
+{
+ *a = cpu_to_be16(be16_to_cpu(*a) + b);
+}
+
+static inline void be32_add_cpu(__be32 *a, __s32 b)
+{
+ *a = cpu_to_be32(be32_to_cpu(*a) + b);
+}
+
+static inline void be64_add_cpu(__be64 *a, __s64 b)
+{
+ *a = cpu_to_be64(be64_to_cpu(*a) + b);
+}
+
#endif /* __KERNEL__ */
/* do we need conversion? */
diff --git a/fs/xfs/xfs_bit.h b/fs/xfs/xfs_bit.h
index 8e0e463dae2d..bca7b243c319 100644
--- a/fs/xfs/xfs_bit.h
+++ b/fs/xfs/xfs_bit.h
@@ -61,8 +61,7 @@ static inline int xfs_highbit64(__uint64_t v)
/* Get low bit set out of 32-bit argument, -1 if none set */
static inline int xfs_lowbit32(__uint32_t v)
{
- unsigned long t = v;
- return (v) ? find_first_bit(&t, 32) : -1;
+ return ffs(v) - 1;
}
/* Get low bit set out of 64-bit argument, -1 if none set */
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index a1aab9275d5a..138308e70d14 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -393,8 +393,8 @@ xfs_bmap_count_leaves(
STATIC void
xfs_bmap_disk_count_leaves(
- xfs_extnum_t idx,
- xfs_bmbt_block_t *block,
+ struct xfs_mount *mp,
+ struct xfs_btree_block *block,
int numrecs,
int *count);
@@ -402,6 +402,53 @@ xfs_bmap_disk_count_leaves(
* Bmap internal routines.
*/
+STATIC int /* error */
+xfs_bmbt_lookup_eq(
+ struct xfs_btree_cur *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.b.br_startoff = off;
+ cur->bc_rec.b.br_startblock = bno;
+ cur->bc_rec.b.br_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+STATIC int /* error */
+xfs_bmbt_lookup_ge(
+ struct xfs_btree_cur *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.b.br_startoff = off;
+ cur->bc_rec.b.br_startblock = bno;
+ cur->bc_rec.b.br_blockcount = len;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+* Update the record referred to by cur to the value given
+ * by [off, bno, len, state].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_bmbt_update(
+ struct xfs_btree_cur *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ xfs_exntst_t state)
+{
+ union xfs_btree_rec rec;
+
+ xfs_bmbt_disk_set_allf(&rec.bmbt, off, bno, len, state);
+ return xfs_btree_update(cur, &rec);
+}
+
/*
* Called from xfs_bmap_add_attrfork to handle btree format files.
*/
@@ -422,15 +469,14 @@ xfs_bmap_add_attrfork_btree(
if (ip->i_df.if_broot_bytes <= XFS_IFORK_DSIZE(ip))
*flags |= XFS_ILOG_DBROOT;
else {
- cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
- XFS_DATA_FORK);
+ cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK);
cur->bc_private.b.flist = flist;
cur->bc_private.b.firstblock = *firstblock;
if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
goto error0;
/* must be at least one entry */
XFS_WANT_CORRUPTED_GOTO(stat == 1, error0);
- if ((error = xfs_bmbt_newroot(cur, flags, &stat)))
+ if ((error = xfs_btree_new_iroot(cur, flags, &stat)))
goto error0;
if (stat == 0) {
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
@@ -818,10 +864,10 @@ xfs_bmap_add_extent_delay_real(
RIGHT.br_blockcount, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
@@ -931,7 +977,7 @@ xfs_bmap_add_extent_delay_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1007,7 +1053,7 @@ xfs_bmap_add_extent_delay_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1097,7 +1143,7 @@ xfs_bmap_add_extent_delay_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1152,7 +1198,7 @@ xfs_bmap_add_extent_delay_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1379,16 +1425,16 @@ xfs_bmap_add_extent_unwritten_real(
RIGHT.br_blockcount, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
@@ -1428,10 +1474,10 @@ xfs_bmap_add_extent_unwritten_real(
&i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
@@ -1471,10 +1517,10 @@ xfs_bmap_add_extent_unwritten_real(
RIGHT.br_blockcount, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
if ((error = xfs_bmbt_update(cur, new->br_startoff,
@@ -1557,7 +1603,7 @@ xfs_bmap_add_extent_unwritten_real(
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
if (xfs_bmbt_update(cur, LEFT.br_startoff,
LEFT.br_startblock,
@@ -1605,7 +1651,7 @@ xfs_bmap_add_extent_unwritten_real(
oldext)))
goto done;
cur->bc_rec.b = *new;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1647,7 +1693,7 @@ xfs_bmap_add_extent_unwritten_real(
PREV.br_blockcount - new->br_blockcount,
oldext)))
goto done;
- if ((error = xfs_bmbt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto done;
if ((error = xfs_bmbt_update(cur, new->br_startoff,
new->br_startblock,
@@ -1695,7 +1741,7 @@ xfs_bmap_add_extent_unwritten_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = XFS_EXT_NORM;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -1743,7 +1789,7 @@ xfs_bmap_add_extent_unwritten_real(
cur->bc_rec.b = PREV;
cur->bc_rec.b.br_blockcount =
new->br_startoff - PREV.br_startoff;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
/*
@@ -1758,7 +1804,7 @@ xfs_bmap_add_extent_unwritten_real(
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
/* new middle extent - newext */
cur->bc_rec.b.br_state = new->br_state;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -2106,10 +2152,10 @@ xfs_bmap_add_extent_hole_real(
right.br_blockcount, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
- if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ if ((error = xfs_btree_decrement(cur, 0, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
if ((error = xfs_bmbt_update(cur, left.br_startoff,
@@ -2218,7 +2264,7 @@ xfs_bmap_add_extent_hole_real(
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 0, done);
cur->bc_rec.b.br_state = new->br_state;
- if ((error = xfs_bmbt_insert(cur, &i)))
+ if ((error = xfs_btree_insert(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
}
@@ -2996,24 +3042,24 @@ xfs_bmap_btree_to_extents(
int whichfork) /* data or attr fork */
{
/* REFERENCED */
- xfs_bmbt_block_t *cblock;/* child btree block */
+ struct xfs_btree_block *cblock;/* child btree block */
xfs_fsblock_t cbno; /* child block number */
xfs_buf_t *cbp; /* child block's buffer */
int error; /* error return value */
xfs_ifork_t *ifp; /* inode fork data */
xfs_mount_t *mp; /* mount point structure */
__be64 *pp; /* ptr to block address */
- xfs_bmbt_block_t *rblock;/* root btree block */
+ struct xfs_btree_block *rblock;/* root btree block */
+ mp = ip->i_mount;
ifp = XFS_IFORK_PTR(ip, whichfork);
ASSERT(ifp->if_flags & XFS_IFEXTENTS);
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
rblock = ifp->if_broot;
ASSERT(be16_to_cpu(rblock->bb_level) == 1);
ASSERT(be16_to_cpu(rblock->bb_numrecs) == 1);
- ASSERT(XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes) == 1);
- mp = ip->i_mount;
- pp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, ifp->if_broot_bytes);
+ ASSERT(xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0) == 1);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, ifp->if_broot_bytes);
cbno = be64_to_cpu(*pp);
*logflagsp = 0;
#ifdef DEBUG
@@ -3023,8 +3069,8 @@ xfs_bmap_btree_to_extents(
if ((error = xfs_btree_read_bufl(mp, tp, cbno, 0, &cbp,
XFS_BMAP_BTREE_REF)))
return error;
- cblock = XFS_BUF_TO_BMBT_BLOCK(cbp);
- if ((error = xfs_btree_check_lblock(cur, cblock, 0, cbp)))
+ cblock = XFS_BUF_TO_BLOCK(cbp);
+ if ((error = xfs_btree_check_block(cur, cblock, 0, cbp)))
return error;
xfs_bmap_add_free(cbno, 1, cur->bc_private.b.flist, mp);
ip->i_d.di_nblocks--;
@@ -3170,7 +3216,7 @@ xfs_bmap_del_extent(
flags |= XFS_ILOG_FEXT(whichfork);
break;
}
- if ((error = xfs_bmbt_delete(cur, &i)))
+ if ((error = xfs_btree_delete(cur, &i)))
goto done;
XFS_WANT_CORRUPTED_GOTO(i == 1, done);
break;
@@ -3254,10 +3300,10 @@ xfs_bmap_del_extent(
got.br_startblock, temp,
got.br_state)))
goto done;
- if ((error = xfs_bmbt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto done;
cur->bc_rec.b = new;
- error = xfs_bmbt_insert(cur, &i);
+ error = xfs_btree_insert(cur, &i);
if (error && error != ENOSPC)
goto done;
/*
@@ -3404,11 +3450,11 @@ xfs_bmap_extents_to_btree(
int *logflagsp, /* inode logging flags */
int whichfork) /* data or attr fork */
{
- xfs_bmbt_block_t *ablock; /* allocated (child) bt block */
+ struct xfs_btree_block *ablock; /* allocated (child) bt block */
xfs_buf_t *abp; /* buffer for ablock */
xfs_alloc_arg_t args; /* allocation arguments */
xfs_bmbt_rec_t *arp; /* child record pointer */
- xfs_bmbt_block_t *block; /* btree root block */
+ struct xfs_btree_block *block; /* btree root block */
xfs_btree_cur_t *cur; /* bmap btree cursor */
xfs_bmbt_rec_host_t *ep; /* extent record pointer */
int error; /* error return value */
@@ -3428,6 +3474,7 @@ xfs_bmap_extents_to_btree(
*/
xfs_iroot_realloc(ip, 1, whichfork);
ifp->if_flags |= XFS_IFBROOT;
+
/*
* Fill in the root.
*/
@@ -3435,14 +3482,14 @@ xfs_bmap_extents_to_btree(
block->bb_magic = cpu_to_be32(XFS_BMAP_MAGIC);
block->bb_level = cpu_to_be16(1);
block->bb_numrecs = cpu_to_be16(1);
- block->bb_leftsib = cpu_to_be64(NULLDFSBNO);
- block->bb_rightsib = cpu_to_be64(NULLDFSBNO);
+ block->bb_u.l.bb_leftsib = cpu_to_be64(NULLDFSBNO);
+ block->bb_u.l.bb_rightsib = cpu_to_be64(NULLDFSBNO);
+
/*
* Need a cursor. Can't allocate until bb_level is filled in.
*/
mp = ip->i_mount;
- cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
- whichfork);
+ cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
cur->bc_private.b.firstblock = *firstblock;
cur->bc_private.b.flist = flist;
cur->bc_private.b.flags = wasdel ? XFS_BTCUR_BPRV_WASDEL : 0;
@@ -3489,12 +3536,12 @@ xfs_bmap_extents_to_btree(
/*
* Fill in the child block.
*/
- ablock = XFS_BUF_TO_BMBT_BLOCK(abp);
+ ablock = XFS_BUF_TO_BLOCK(abp);
ablock->bb_magic = cpu_to_be32(XFS_BMAP_MAGIC);
ablock->bb_level = 0;
- ablock->bb_leftsib = cpu_to_be64(NULLDFSBNO);
- ablock->bb_rightsib = cpu_to_be64(NULLDFSBNO);
- arp = XFS_BMAP_REC_IADDR(ablock, 1, cur);
+ ablock->bb_u.l.bb_leftsib = cpu_to_be64(NULLDFSBNO);
+ ablock->bb_u.l.bb_rightsib = cpu_to_be64(NULLDFSBNO);
+ arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
for (cnt = i = 0; i < nextents; i++) {
ep = xfs_iext_get_ext(ifp, i);
@@ -3505,21 +3552,24 @@ xfs_bmap_extents_to_btree(
}
}
ASSERT(cnt == XFS_IFORK_NEXTENTS(ip, whichfork));
- ablock->bb_numrecs = cpu_to_be16(cnt);
+ xfs_btree_set_numrecs(ablock, cnt);
+
/*
* Fill in the root key and pointer.
*/
- kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
- arp = XFS_BMAP_REC_IADDR(ablock, 1, cur);
+ kp = XFS_BMBT_KEY_ADDR(mp, block, 1);
+ arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
kp->br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(arp));
- pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+ pp = XFS_BMBT_PTR_ADDR(mp, block, 1, xfs_bmbt_get_maxrecs(cur,
+ be16_to_cpu(block->bb_level)));
*pp = cpu_to_be64(args.fsbno);
+
/*
* Do all this logging at the end so that
* the root is at the right level.
*/
- xfs_bmbt_log_block(cur, abp, XFS_BB_ALL_BITS);
- xfs_bmbt_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs));
+ xfs_btree_log_block(cur, abp, XFS_BB_ALL_BITS);
+ xfs_btree_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs));
ASSERT(*curp == NULL);
*curp = cur;
*logflagsp = XFS_ILOG_CORE | XFS_ILOG_FBROOT(whichfork);
@@ -4176,7 +4226,7 @@ xfs_bmap_compute_maxlevels(
maxleafents = MAXAEXTNUM;
sz = XFS_BMDR_SPACE_CALC(MINABTPTRS);
}
- maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
+ maxrootrecs = xfs_bmdr_maxrecs(mp, sz, 0);
minleafrecs = mp->m_bmap_dmnr[0];
minnoderecs = mp->m_bmap_dmnr[1];
maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
@@ -4242,9 +4292,15 @@ xfs_bmap_finish(
* We have a new transaction, so we should return committed=1,
* even though we're returning an error.
*/
- if (error) {
+ if (error)
return error;
- }
+
+ /*
+ * transaction commit worked ok so we can drop the extra ticket
+ * reference that we gained in xfs_trans_dup()
+ */
+ xfs_log_ticket_put(ntp->t_ticket);
+
if ((error = xfs_trans_reserve(ntp, 0, logres, 0, XFS_TRANS_PERM_LOG_RES,
logcount)))
return error;
@@ -4474,6 +4530,22 @@ xfs_bmap_one_block(
return rval;
}
+STATIC int
+xfs_bmap_sanity_check(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp,
+ int level)
+{
+ struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
+
+ if (be32_to_cpu(block->bb_magic) != XFS_BMAP_MAGIC ||
+ be16_to_cpu(block->bb_level) != level ||
+ be16_to_cpu(block->bb_numrecs) == 0 ||
+ be16_to_cpu(block->bb_numrecs) > mp->m_bmap_dmxr[level != 0])
+ return 0;
+ return 1;
+}
+
/*
* Read in the extents to if_extents.
* All inode fields are set up by caller, we just traverse the btree
@@ -4486,7 +4558,7 @@ xfs_bmap_read_extents(
xfs_inode_t *ip, /* incore inode */
int whichfork) /* data or attr fork */
{
- xfs_bmbt_block_t *block; /* current btree block */
+ struct xfs_btree_block *block; /* current btree block */
xfs_fsblock_t bno; /* block # of "block" */
xfs_buf_t *bp; /* buffer for "block" */
int error; /* error return value */
@@ -4510,7 +4582,7 @@ xfs_bmap_read_extents(
*/
level = be16_to_cpu(block->bb_level);
ASSERT(level > 0);
- pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
bno = be64_to_cpu(*pp);
ASSERT(bno != NULLDFSBNO);
ASSERT(XFS_FSB_TO_AGNO(mp, bno) < mp->m_sb.sb_agcount);
@@ -4523,13 +4595,13 @@ xfs_bmap_read_extents(
if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp,
XFS_BMAP_BTREE_REF)))
return error;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
XFS_WANT_CORRUPTED_GOTO(
- XFS_BMAP_SANITY_CHECK(mp, block, level),
+ xfs_bmap_sanity_check(mp, bp, level),
error0);
if (level == 0)
break;
- pp = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]);
+ pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
bno = be64_to_cpu(*pp);
XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, bno), error0);
xfs_trans_brelse(tp, bp);
@@ -4549,7 +4621,7 @@ xfs_bmap_read_extents(
xfs_extnum_t start;
- num_recs = be16_to_cpu(block->bb_numrecs);
+ num_recs = xfs_btree_get_numrecs(block);
if (unlikely(i + num_recs > room)) {
ASSERT(i + num_recs <= room);
xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
@@ -4561,18 +4633,18 @@ xfs_bmap_read_extents(
goto error0;
}
XFS_WANT_CORRUPTED_GOTO(
- XFS_BMAP_SANITY_CHECK(mp, block, 0),
+ xfs_bmap_sanity_check(mp, bp, 0),
error0);
/*
* Read-ahead the next leaf block, if any.
*/
- nextbno = be64_to_cpu(block->bb_rightsib);
+ nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
if (nextbno != NULLFSBLOCK)
xfs_btree_reada_bufl(mp, nextbno, 1);
/*
* Copy records into the extent records.
*/
- frp = XFS_BTREE_REC_ADDR(xfs_bmbt, block, 1);
+ frp = XFS_BMBT_REC_ADDR(mp, block, 1);
start = i;
for (j = 0; j < num_recs; j++, i++, frp++) {
xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
@@ -4603,7 +4675,7 @@ xfs_bmap_read_extents(
if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp,
XFS_BMAP_BTREE_REF)))
return error;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
}
ASSERT(i == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)));
ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork));
@@ -5029,8 +5101,7 @@ xfs_bmapi(
if (abno == NULLFSBLOCK)
break;
if ((ifp->if_flags & XFS_IFBROOT) && !cur) {
- cur = xfs_btree_init_cursor(mp,
- tp, NULL, 0, XFS_BTNUM_BMAP,
+ cur = xfs_bmbt_init_cursor(mp, tp,
ip, whichfork);
cur->bc_private.b.firstblock =
*firstblock;
@@ -5147,9 +5218,8 @@ xfs_bmapi(
*/
ASSERT(mval->br_blockcount <= len);
if ((ifp->if_flags & XFS_IFBROOT) && !cur) {
- cur = xfs_btree_init_cursor(mp,
- tp, NULL, 0, XFS_BTNUM_BMAP,
- ip, whichfork);
+ cur = xfs_bmbt_init_cursor(mp,
+ tp, ip, whichfork);
cur->bc_private.b.firstblock =
*firstblock;
cur->bc_private.b.flist = flist;
@@ -5440,8 +5510,7 @@ xfs_bunmapi(
logflags = 0;
if (ifp->if_flags & XFS_IFBROOT) {
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
- cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
- whichfork);
+ cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
cur->bc_private.b.firstblock = *firstblock;
cur->bc_private.b.flist = flist;
cur->bc_private.b.flags = 0;
@@ -5742,14 +5811,17 @@ error0:
STATIC int
xfs_getbmapx_fix_eof_hole(
xfs_inode_t *ip, /* xfs incore inode pointer */
- struct getbmap *out, /* output structure */
+ struct getbmapx *out, /* output structure */
int prealloced, /* this is a file with
- * preallocated data space */
+ * preallocated data space */
__int64_t end, /* last block requested */
xfs_fsblock_t startblock)
{
__int64_t fixlen;
xfs_mount_t *mp; /* file system mount point */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t lastx; /* last extent pointer */
+ xfs_fileoff_t fileblock;
if (startblock == HOLESTARTBLOCK) {
mp = ip->i_mount;
@@ -5763,21 +5835,33 @@ xfs_getbmapx_fix_eof_hole(
out->bmv_length = fixlen;
}
} else {
- out->bmv_block = XFS_FSB_TO_DB(ip, startblock);
+ if (startblock == DELAYSTARTBLOCK)
+ out->bmv_block = -2;
+ else
+ out->bmv_block = XFS_FSB_TO_DB(ip, startblock);
+ fileblock = XFS_BB_TO_FSB(ip->i_mount, out->bmv_offset);
+ ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ if (xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
+ (lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
+ out->bmv_oflags |= BMV_OF_LAST;
}
return 1;
}
/*
- * Fcntl interface to xfs_bmapi.
+ * Get inode's extents as described in bmv, and format for output.
+ * Calls formatter to fill the user's buffer until all extents
+ * are mapped, until the passed-in bmv->bmv_count slots have
+ * been filled, or until the formatter short-circuits the loop,
+ * if it is tracking filled-in extents on its own.
*/
int /* error code */
xfs_getbmap(
xfs_inode_t *ip,
- struct getbmap *bmv, /* user bmap structure */
- void __user *ap, /* pointer to user's array */
- int interface) /* interface flags */
+ struct getbmapx *bmv, /* user bmap structure */
+ xfs_bmap_format_t formatter, /* format to user */
+ void *arg) /* formatter arg */
{
__int64_t bmvend; /* last block requested */
int error; /* return value */
@@ -5790,19 +5874,17 @@ xfs_getbmap(
int nexleft; /* # of user extents left */
int subnex; /* # of bmapi's can do */
int nmap; /* number of map entries */
- struct getbmap out; /* output structure */
+ struct getbmapx out; /* output structure */
int whichfork; /* data or attr fork */
int prealloced; /* this is a file with
* preallocated data space */
- int sh_unwritten; /* true, if unwritten */
- /* extents listed separately */
+ int iflags; /* interface flags */
int bmapi_flags; /* flags for xfs_bmapi */
- __int32_t oflags; /* getbmapx bmv_oflags field */
mp = ip->i_mount;
+ iflags = bmv->bmv_iflags;
- whichfork = interface & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
- sh_unwritten = (interface & BMV_IF_PREALLOC) != 0;
+ whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
/* If the BMV_IF_NO_DMAPI_READ interface bit specified, do not
* generate a DMAPI read event. Otherwise, if the DM_EVENT_READ
@@ -5817,7 +5899,7 @@ xfs_getbmap(
* could misinterpret holes in a DMAPI file as true holes,
* when in fact they may represent offline user data.
*/
- if ((interface & BMV_IF_NO_DMAPI_READ) == 0 &&
+ if ((iflags & BMV_IF_NO_DMAPI_READ) == 0 &&
DM_EVENT_ENABLED(ip, DM_EVENT_READ) &&
whichfork == XFS_DATA_FORK) {
error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, 0, 0, 0, NULL);
@@ -5873,8 +5955,9 @@ xfs_getbmap(
xfs_ilock(ip, XFS_IOLOCK_SHARED);
- if (whichfork == XFS_DATA_FORK &&
- (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
+ if (((iflags & BMV_IF_DELALLOC) == 0) &&
+ (whichfork == XFS_DATA_FORK) &&
+ (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
/* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
error = xfs_flush_pages(ip, (xfs_off_t)0,
-1, 0, FI_REMAPF);
@@ -5884,7 +5967,8 @@ xfs_getbmap(
}
}
- ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0);
+ ASSERT(whichfork == XFS_ATTR_FORK || (iflags & BMV_IF_DELALLOC) ||
+ ip->i_delayed_blks == 0);
lock = xfs_ilock_map_shared(ip);
@@ -5896,7 +5980,7 @@ xfs_getbmap(
nex = XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1;
bmapi_flags = XFS_BMAPI_AFLAG(whichfork) |
- ((sh_unwritten) ? 0 : XFS_BMAPI_IGSTATE);
+ ((iflags & BMV_IF_PREALLOC) ? 0 : XFS_BMAPI_IGSTATE);
/*
* Allocate enough space to handle "subnex" maps at a time.
@@ -5906,9 +5990,12 @@ xfs_getbmap(
bmv->bmv_entries = 0;
- if (XFS_IFORK_NEXTENTS(ip, whichfork) == 0) {
- error = 0;
- goto unlock_and_return;
+ if ((XFS_IFORK_NEXTENTS(ip, whichfork) == 0)) {
+ if (((iflags & BMV_IF_DELALLOC) == 0) ||
+ whichfork == XFS_ATTR_FORK) {
+ error = 0;
+ goto unlock_and_return;
+ }
}
nexleft = nex;
@@ -5924,52 +6011,40 @@ xfs_getbmap(
ASSERT(nmap <= subnex);
for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
- nexleft--;
- oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ?
- BMV_OF_PREALLOC : 0;
+ out.bmv_oflags = 0;
+ if (map[i].br_state == XFS_EXT_UNWRITTEN)
+ out.bmv_oflags |= BMV_OF_PREALLOC;
+ else if (map[i].br_startblock == DELAYSTARTBLOCK)
+ out.bmv_oflags |= BMV_OF_DELALLOC;
out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff);
out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
- ASSERT(map[i].br_startblock != DELAYSTARTBLOCK);
+ out.bmv_unused1 = out.bmv_unused2 = 0;
+ ASSERT(((iflags & BMV_IF_DELALLOC) != 0) ||
+ (map[i].br_startblock != DELAYSTARTBLOCK));
if (map[i].br_startblock == HOLESTARTBLOCK &&
whichfork == XFS_ATTR_FORK) {
/* came to the end of attribute fork */
+ out.bmv_oflags |= BMV_OF_LAST;
goto unlock_and_return;
} else {
+ int full = 0; /* user array is full */
+
if (!xfs_getbmapx_fix_eof_hole(ip, &out,
prealloced, bmvend,
map[i].br_startblock)) {
goto unlock_and_return;
}
- /* return either getbmap/getbmapx structure. */
- if (interface & BMV_IF_EXTENDED) {
- struct getbmapx outx;
-
- GETBMAP_CONVERT(out,outx);
- outx.bmv_oflags = oflags;
- outx.bmv_unused1 = outx.bmv_unused2 = 0;
- if (copy_to_user(ap, &outx,
- sizeof(outx))) {
- error = XFS_ERROR(EFAULT);
- goto unlock_and_return;
- }
- } else {
- if (copy_to_user(ap, &out,
- sizeof(out))) {
- error = XFS_ERROR(EFAULT);
- goto unlock_and_return;
- }
- }
+ /* format results & advance arg */
+ error = formatter(&arg, &out, &full);
+ if (error || full)
+ goto unlock_and_return;
+ nexleft--;
bmv->bmv_offset =
out.bmv_offset + out.bmv_length;
bmv->bmv_length = MAX((__int64_t)0,
(__int64_t)(bmvend - bmv->bmv_offset));
bmv->bmv_entries++;
- ap = (interface & BMV_IF_EXTENDED) ?
- (void __user *)
- ((struct getbmapx __user *)ap + 1) :
- (void __user *)
- ((struct getbmap __user *)ap + 1);
}
}
} while (nmap && nexleft && bmv->bmv_length);
@@ -6131,7 +6206,7 @@ xfs_bmap_get_bp(
void
xfs_check_block(
- xfs_bmbt_block_t *block,
+ struct xfs_btree_block *block,
xfs_mount_t *mp,
int root,
short sz)
@@ -6143,36 +6218,29 @@ xfs_check_block(
ASSERT(be16_to_cpu(block->bb_level) > 0);
prevp = NULL;
- for( i = 1; i <= be16_to_cpu(block->bb_numrecs); i++) {
+ for( i = 1; i <= xfs_btree_get_numrecs(block); i++) {
dmxr = mp->m_bmap_dmxr[0];
-
- if (root) {
- keyp = XFS_BMAP_BROOT_KEY_ADDR(block, i, sz);
- } else {
- keyp = XFS_BTREE_KEY_ADDR(xfs_bmbt, block, i);
- }
+ keyp = XFS_BMBT_KEY_ADDR(mp, block, i);
if (prevp) {
- xfs_btree_check_key(XFS_BTNUM_BMAP, prevp, keyp);
+ ASSERT(be64_to_cpu(prevp->br_startoff) <
+ be64_to_cpu(keyp->br_startoff));
}
prevp = keyp;
/*
* Compare the block numbers to see if there are dups.
*/
+ if (root)
+ pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, i, sz);
+ else
+ pp = XFS_BMBT_PTR_ADDR(mp, block, i, dmxr);
- if (root) {
- pp = XFS_BMAP_BROOT_PTR_ADDR(block, i, sz);
- } else {
- pp = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, i, dmxr);
- }
for (j = i+1; j <= be16_to_cpu(block->bb_numrecs); j++) {
- if (root) {
- thispa = XFS_BMAP_BROOT_PTR_ADDR(block, j, sz);
- } else {
- thispa = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, j,
- dmxr);
- }
+ if (root)
+ thispa = XFS_BMAP_BROOT_PTR_ADDR(mp, block, j, sz);
+ else
+ thispa = XFS_BMBT_PTR_ADDR(mp, block, j, dmxr);
if (*thispa == *pp) {
cmn_err(CE_WARN, "%s: thispa(%d) == pp(%d) %Ld",
__func__, j, i,
@@ -6195,7 +6263,7 @@ xfs_bmap_check_leaf_extents(
xfs_inode_t *ip, /* incore inode pointer */
int whichfork) /* data or attr fork */
{
- xfs_bmbt_block_t *block; /* current btree block */
+ struct xfs_btree_block *block; /* current btree block */
xfs_fsblock_t bno; /* block # of "block" */
xfs_buf_t *bp; /* buffer for "block" */
int error; /* error return value */
@@ -6223,7 +6291,7 @@ xfs_bmap_check_leaf_extents(
level = be16_to_cpu(block->bb_level);
ASSERT(level > 0);
xfs_check_block(block, mp, 1, ifp->if_broot_bytes);
- pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
bno = be64_to_cpu(*pp);
ASSERT(bno != NULLDFSBNO);
@@ -6245,9 +6313,9 @@ xfs_bmap_check_leaf_extents(
if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp,
XFS_BMAP_BTREE_REF)))
goto error_norelse;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
XFS_WANT_CORRUPTED_GOTO(
- XFS_BMAP_SANITY_CHECK(mp, block, level),
+ xfs_bmap_sanity_check(mp, bp, level),
error0);
if (level == 0)
break;
@@ -6258,7 +6326,7 @@ xfs_bmap_check_leaf_extents(
*/
xfs_check_block(block, mp, 0, 0);
- pp = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]);
+ pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
bno = be64_to_cpu(*pp);
XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, bno), error0);
if (bp_release) {
@@ -6280,13 +6348,13 @@ xfs_bmap_check_leaf_extents(
xfs_extnum_t num_recs;
- num_recs = be16_to_cpu(block->bb_numrecs);
+ num_recs = xfs_btree_get_numrecs(block);
/*
* Read-ahead the next leaf block, if any.
*/
- nextbno = be64_to_cpu(block->bb_rightsib);
+ nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
/*
* Check all the extents to make sure they are OK.
@@ -6294,13 +6362,17 @@ xfs_bmap_check_leaf_extents(
* conform with the first entry in this one.
*/
- ep = XFS_BTREE_REC_ADDR(xfs_bmbt, block, 1);
+ ep = XFS_BMBT_REC_ADDR(mp, block, 1);
if (i) {
- xfs_btree_check_rec(XFS_BTNUM_BMAP, &last, ep);
+ ASSERT(xfs_bmbt_disk_get_startoff(&last) +
+ xfs_bmbt_disk_get_blockcount(&last) <=
+ xfs_bmbt_disk_get_startoff(ep));
}
for (j = 1; j < num_recs; j++) {
- nextp = XFS_BTREE_REC_ADDR(xfs_bmbt, block, j + 1);
- xfs_btree_check_rec(XFS_BTNUM_BMAP, ep, nextp);
+ nextp = XFS_BMBT_REC_ADDR(mp, block, j + 1);
+ ASSERT(xfs_bmbt_disk_get_startoff(ep) +
+ xfs_bmbt_disk_get_blockcount(ep) <=
+ xfs_bmbt_disk_get_startoff(nextp));
ep = nextp;
}
@@ -6326,7 +6398,7 @@ xfs_bmap_check_leaf_extents(
if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp,
XFS_BMAP_BTREE_REF)))
goto error_norelse;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
}
if (bp_release) {
bp_release = 0;
@@ -6356,7 +6428,7 @@ xfs_bmap_count_blocks(
int whichfork, /* data or attr fork */
int *count) /* out: count of blocks */
{
- xfs_bmbt_block_t *block; /* current btree block */
+ struct xfs_btree_block *block; /* current btree block */
xfs_fsblock_t bno; /* block # of "block" */
xfs_ifork_t *ifp; /* fork structure */
int level; /* btree level, for checking */
@@ -6379,7 +6451,7 @@ xfs_bmap_count_blocks(
block = ifp->if_broot;
level = be16_to_cpu(block->bb_level);
ASSERT(level > 0);
- pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
bno = be64_to_cpu(*pp);
ASSERT(bno != NULLDFSBNO);
ASSERT(XFS_FSB_TO_AGNO(mp, bno) < mp->m_sb.sb_agcount);
@@ -6413,29 +6485,29 @@ xfs_bmap_count_tree(
__be64 *pp;
xfs_fsblock_t bno = blockno;
xfs_fsblock_t nextbno;
- xfs_bmbt_block_t *block, *nextblock;
+ struct xfs_btree_block *block, *nextblock;
int numrecs;
if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, XFS_BMAP_BTREE_REF)))
return error;
*count += 1;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
if (--level) {
/* Not at node above leafs, count this level of nodes */
- nextbno = be64_to_cpu(block->bb_rightsib);
+ nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
while (nextbno != NULLFSBLOCK) {
if ((error = xfs_btree_read_bufl(mp, tp, nextbno,
0, &nbp, XFS_BMAP_BTREE_REF)))
return error;
*count += 1;
- nextblock = XFS_BUF_TO_BMBT_BLOCK(nbp);
- nextbno = be64_to_cpu(nextblock->bb_rightsib);
+ nextblock = XFS_BUF_TO_BLOCK(nbp);
+ nextbno = be64_to_cpu(nextblock->bb_u.l.bb_rightsib);
xfs_trans_brelse(tp, nbp);
}
/* Dive to the next level */
- pp = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]);
+ pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
bno = be64_to_cpu(*pp);
if (unlikely((error =
xfs_bmap_count_tree(mp, tp, ifp, bno, level, count)) < 0)) {
@@ -6448,9 +6520,9 @@ xfs_bmap_count_tree(
} else {
/* count all level 1 nodes and their leaves */
for (;;) {
- nextbno = be64_to_cpu(block->bb_rightsib);
+ nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
numrecs = be16_to_cpu(block->bb_numrecs);
- xfs_bmap_disk_count_leaves(0, block, numrecs, count);
+ xfs_bmap_disk_count_leaves(mp, block, numrecs, count);
xfs_trans_brelse(tp, bp);
if (nextbno == NULLFSBLOCK)
break;
@@ -6459,7 +6531,7 @@ xfs_bmap_count_tree(
XFS_BMAP_BTREE_REF)))
return error;
*count += 1;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
}
}
return 0;
@@ -6489,8 +6561,8 @@ xfs_bmap_count_leaves(
*/
STATIC void
xfs_bmap_disk_count_leaves(
- xfs_extnum_t idx,
- xfs_bmbt_block_t *block,
+ struct xfs_mount *mp,
+ struct xfs_btree_block *block,
int numrecs,
int *count)
{
@@ -6498,7 +6570,7 @@ xfs_bmap_disk_count_leaves(
xfs_bmbt_rec_t *frp;
for (b = 1; b <= numrecs; b++) {
- frp = XFS_BTREE_REC_ADDR(xfs_bmbt, block, idx + b);
+ frp = XFS_BMBT_REC_ADDR(mp, block, b);
*count += xfs_bmbt_disk_get_blockcount(frp);
}
}
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
index 9f3e3a836d15..284571c05ed0 100644
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -137,9 +137,7 @@ typedef struct xfs_bmalloca {
char conv; /* overwriting unwritten extents */
} xfs_bmalloca_t;
-#ifdef __KERNEL__
-
-#if defined(XFS_BMAP_TRACE)
+#if defined(__KERNEL__) && defined(XFS_BMAP_TRACE)
/*
* Trace operations for bmap extent tracing
*/
@@ -163,9 +161,12 @@ xfs_bmap_trace_exlist(
int whichfork); /* data or attr fork */
#define XFS_BMAP_TRACE_EXLIST(ip,c,w) \
xfs_bmap_trace_exlist(__func__,ip,c,w)
-#else
+
+#else /* __KERNEL__ && XFS_BMAP_TRACE */
+
#define XFS_BMAP_TRACE_EXLIST(ip,c,w)
-#endif
+
+#endif /* __KERNEL__ && XFS_BMAP_TRACE */
/*
* Convert inode from non-attributed to attributed.
@@ -206,20 +207,6 @@ xfs_bmap_compute_maxlevels(
int whichfork); /* data or attr fork */
/*
- * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
- * caller. Frees all the extents that need freeing, which must be done
- * last due to locking considerations.
- *
- * Return 1 if the given transaction was committed and a new one allocated,
- * and 0 otherwise.
- */
-int /* error */
-xfs_bmap_finish(
- struct xfs_trans **tp, /* transaction pointer addr */
- xfs_bmap_free_t *flist, /* i/o: list extents to free */
- int *committed); /* xact committed or not */
-
-/*
* Returns the file-relative block number of the first unused block in the file.
* This is the lowest-address hole if the file has holes, else the first block
* past the end of file.
@@ -344,14 +331,43 @@ xfs_bunmapi(
int *done); /* set if not done yet */
/*
- * Fcntl interface to xfs_bmapi.
+ * Check an extent list, which has just been read, for
+ * any bit in the extent flag field.
+ */
+int
+xfs_check_nostate_extents(
+ struct xfs_ifork *ifp,
+ xfs_extnum_t idx,
+ xfs_extnum_t num);
+
+#ifdef __KERNEL__
+
+/*
+ * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
+ * caller. Frees all the extents that need freeing, which must be done
+ * last due to locking considerations.
+ *
+ * Return 1 if the given transaction was committed and a new one allocated,
+ * and 0 otherwise.
+ */
+int /* error */
+xfs_bmap_finish(
+ struct xfs_trans **tp, /* transaction pointer addr */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ int *committed); /* xact committed or not */
+
+/* bmap to userspace formatter - copy to user & advance pointer */
+typedef int (*xfs_bmap_format_t)(void **, struct getbmapx *, int *);
+
+/*
+ * Get inode's extents as described in bmv, and format for output.
*/
int /* error code */
xfs_getbmap(
xfs_inode_t *ip,
- struct getbmap *bmv, /* user bmap structure */
- void __user *ap, /* pointer to user's array */
- int iflags); /* interface flags */
+ struct getbmapx *bmv, /* user bmap structure */
+ xfs_bmap_format_t formatter, /* format to user */
+ void *arg); /* formatter arg */
/*
* Check if the endoff is outside the last extent. If so the caller will grow
@@ -375,16 +391,6 @@ xfs_bmap_count_blocks(
int *count);
/*
- * Check an extent list, which has just been read, for
- * any bit in the extent flag field.
- */
-int
-xfs_check_nostate_extents(
- struct xfs_ifork *ifp,
- xfs_extnum_t idx,
- xfs_extnum_t num);
-
-/*
* Search the extent records for the entry containing block bno.
* If bno lies in a hole, point to the next entry. If bno lies
* past eof, *eofp will be set, and *prevp will contain the last
diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c
index 23efad29a5cd..8f1ec73725d3 100644
--- a/fs/xfs/xfs_bmap_btree.c
+++ b/fs/xfs/xfs_bmap_btree.c
@@ -37,1406 +37,13 @@
#include "xfs_inode_item.h"
#include "xfs_alloc.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_ialloc.h"
#include "xfs_itable.h"
#include "xfs_bmap.h"
#include "xfs_error.h"
#include "xfs_quota.h"
-#if defined(XFS_BMBT_TRACE)
-ktrace_t *xfs_bmbt_trace_buf;
-#endif
-
-/*
- * Prototypes for internal btree functions.
- */
-
-
-STATIC int xfs_bmbt_killroot(xfs_btree_cur_t *);
-STATIC void xfs_bmbt_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC void xfs_bmbt_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC int xfs_bmbt_lshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_bmbt_rshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_bmbt_split(xfs_btree_cur_t *, int, xfs_fsblock_t *,
- __uint64_t *, xfs_btree_cur_t **, int *);
-STATIC int xfs_bmbt_updkey(xfs_btree_cur_t *, xfs_bmbt_key_t *, int);
-
-
-#if defined(XFS_BMBT_TRACE)
-
-static char ARGS[] = "args";
-static char ENTRY[] = "entry";
-static char ERROR[] = "error";
-#undef EXIT
-static char EXIT[] = "exit";
-
-/*
- * Add a trace buffer entry for the arguments given to the routine,
- * generic form.
- */
-STATIC void
-xfs_bmbt_trace_enter(
- const char *func,
- xfs_btree_cur_t *cur,
- char *s,
- int type,
- int line,
- __psunsigned_t a0,
- __psunsigned_t a1,
- __psunsigned_t a2,
- __psunsigned_t a3,
- __psunsigned_t a4,
- __psunsigned_t a5,
- __psunsigned_t a6,
- __psunsigned_t a7,
- __psunsigned_t a8,
- __psunsigned_t a9,
- __psunsigned_t a10)
-{
- xfs_inode_t *ip;
- int whichfork;
-
- ip = cur->bc_private.b.ip;
- whichfork = cur->bc_private.b.whichfork;
- ktrace_enter(xfs_bmbt_trace_buf,
- (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
- (void *)func, (void *)s, (void *)ip, (void *)cur,
- (void *)a0, (void *)a1, (void *)a2, (void *)a3,
- (void *)a4, (void *)a5, (void *)a6, (void *)a7,
- (void *)a8, (void *)a9, (void *)a10);
- ASSERT(ip->i_btrace);
- ktrace_enter(ip->i_btrace,
- (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
- (void *)func, (void *)s, (void *)ip, (void *)cur,
- (void *)a0, (void *)a1, (void *)a2, (void *)a3,
- (void *)a4, (void *)a5, (void *)a6, (void *)a7,
- (void *)a8, (void *)a9, (void *)a10);
-}
-/*
- * Add a trace buffer entry for arguments, for a buffer & 1 integer arg.
- */
-STATIC void
-xfs_bmbt_trace_argbi(
- const char *func,
- xfs_btree_cur_t *cur,
- xfs_buf_t *b,
- int i,
- int line)
-{
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGBI, line,
- (__psunsigned_t)b, i, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for a buffer & 2 integer args.
- */
-STATIC void
-xfs_bmbt_trace_argbii(
- const char *func,
- xfs_btree_cur_t *cur,
- xfs_buf_t *b,
- int i0,
- int i1,
- int line)
-{
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGBII, line,
- (__psunsigned_t)b, i0, i1, 0,
- 0, 0, 0, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for 3 block-length args
- * and an integer arg.
- */
-STATIC void
-xfs_bmbt_trace_argfffi(
- const char *func,
- xfs_btree_cur_t *cur,
- xfs_dfiloff_t o,
- xfs_dfsbno_t b,
- xfs_dfilblks_t i,
- int j,
- int line)
-{
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGFFFI, line,
- o >> 32, (int)o, b >> 32, (int)b,
- i >> 32, (int)i, (int)j, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for one integer arg.
- */
-STATIC void
-xfs_bmbt_trace_argi(
- const char *func,
- xfs_btree_cur_t *cur,
- int i,
- int line)
-{
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGI, line,
- i, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for int, fsblock, key.
- */
-STATIC void
-xfs_bmbt_trace_argifk(
- const char *func,
- xfs_btree_cur_t *cur,
- int i,
- xfs_fsblock_t f,
- xfs_dfiloff_t o,
- int line)
-{
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFK, line,
- i, (xfs_dfsbno_t)f >> 32, (int)f, o >> 32,
- (int)o, 0, 0, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for int, fsblock, rec.
- */
-STATIC void
-xfs_bmbt_trace_argifr(
- const char *func,
- xfs_btree_cur_t *cur,
- int i,
- xfs_fsblock_t f,
- xfs_bmbt_rec_t *r,
- int line)
-{
- xfs_dfsbno_t b;
- xfs_dfilblks_t c;
- xfs_dfsbno_t d;
- xfs_dfiloff_t o;
- xfs_bmbt_irec_t s;
-
- d = (xfs_dfsbno_t)f;
- xfs_bmbt_disk_get_all(r, &s);
- o = (xfs_dfiloff_t)s.br_startoff;
- b = (xfs_dfsbno_t)s.br_startblock;
- c = s.br_blockcount;
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFR, line,
- i, d >> 32, (int)d, o >> 32,
- (int)o, b >> 32, (int)b, c >> 32,
- (int)c, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for arguments, for int, key.
- */
-STATIC void
-xfs_bmbt_trace_argik(
- const char *func,
- xfs_btree_cur_t *cur,
- int i,
- xfs_bmbt_key_t *k,
- int line)
-{
- xfs_dfiloff_t o;
-
- o = be64_to_cpu(k->br_startoff);
- xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFK, line,
- i, o >> 32, (int)o, 0,
- 0, 0, 0, 0,
- 0, 0, 0);
-}
-
-/*
- * Add a trace buffer entry for the cursor/operation.
- */
-STATIC void
-xfs_bmbt_trace_cursor(
- const char *func,
- xfs_btree_cur_t *cur,
- char *s,
- int line)
-{
- xfs_bmbt_rec_host_t r;
-
- xfs_bmbt_set_all(&r, &cur->bc_rec.b);
- xfs_bmbt_trace_enter(func, cur, s, XFS_BMBT_KTRACE_CUR, line,
- (cur->bc_nlevels << 24) | (cur->bc_private.b.flags << 16) |
- cur->bc_private.b.allocated,
- r.l0 >> 32, (int)r.l0,
- r.l1 >> 32, (int)r.l1,
- (unsigned long)cur->bc_bufs[0], (unsigned long)cur->bc_bufs[1],
- (unsigned long)cur->bc_bufs[2], (unsigned long)cur->bc_bufs[3],
- (cur->bc_ptrs[0] << 16) | cur->bc_ptrs[1],
- (cur->bc_ptrs[2] << 16) | cur->bc_ptrs[3]);
-}
-
-#define XFS_BMBT_TRACE_ARGBI(c,b,i) \
- xfs_bmbt_trace_argbi(__func__, c, b, i, __LINE__)
-#define XFS_BMBT_TRACE_ARGBII(c,b,i,j) \
- xfs_bmbt_trace_argbii(__func__, c, b, i, j, __LINE__)
-#define XFS_BMBT_TRACE_ARGFFFI(c,o,b,i,j) \
- xfs_bmbt_trace_argfffi(__func__, c, o, b, i, j, __LINE__)
-#define XFS_BMBT_TRACE_ARGI(c,i) \
- xfs_bmbt_trace_argi(__func__, c, i, __LINE__)
-#define XFS_BMBT_TRACE_ARGIFK(c,i,f,s) \
- xfs_bmbt_trace_argifk(__func__, c, i, f, s, __LINE__)
-#define XFS_BMBT_TRACE_ARGIFR(c,i,f,r) \
- xfs_bmbt_trace_argifr(__func__, c, i, f, r, __LINE__)
-#define XFS_BMBT_TRACE_ARGIK(c,i,k) \
- xfs_bmbt_trace_argik(__func__, c, i, k, __LINE__)
-#define XFS_BMBT_TRACE_CURSOR(c,s) \
- xfs_bmbt_trace_cursor(__func__, c, s, __LINE__)
-#else
-#define XFS_BMBT_TRACE_ARGBI(c,b,i)
-#define XFS_BMBT_TRACE_ARGBII(c,b,i,j)
-#define XFS_BMBT_TRACE_ARGFFFI(c,o,b,i,j)
-#define XFS_BMBT_TRACE_ARGI(c,i)
-#define XFS_BMBT_TRACE_ARGIFK(c,i,f,s)
-#define XFS_BMBT_TRACE_ARGIFR(c,i,f,r)
-#define XFS_BMBT_TRACE_ARGIK(c,i,k)
-#define XFS_BMBT_TRACE_CURSOR(c,s)
-#endif /* XFS_BMBT_TRACE */
-
-
-/*
- * Internal functions.
- */
-
-/*
- * Delete record pointed to by cur/level.
- */
-STATIC int /* error */
-xfs_bmbt_delrec(
- xfs_btree_cur_t *cur,
- int level,
- int *stat) /* success/failure */
-{
- xfs_bmbt_block_t *block; /* bmap btree block */
- xfs_fsblock_t bno; /* fs-relative block number */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop counter */
- int j; /* temp state */
- xfs_bmbt_key_t key; /* bmap btree key */
- xfs_bmbt_key_t *kp=NULL; /* pointer to bmap btree key */
- xfs_fsblock_t lbno; /* left sibling block number */
- xfs_buf_t *lbp; /* left buffer pointer */
- xfs_bmbt_block_t *left; /* left btree block */
- xfs_bmbt_key_t *lkp; /* left btree key */
- xfs_bmbt_ptr_t *lpp; /* left address pointer */
- int lrecs=0; /* left record count */
- xfs_bmbt_rec_t *lrp; /* left record pointer */
- xfs_mount_t *mp; /* file system mount point */
- xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
- int ptr; /* key/record index */
- xfs_fsblock_t rbno; /* right sibling block number */
- xfs_buf_t *rbp; /* right buffer pointer */
- xfs_bmbt_block_t *right; /* right btree block */
- xfs_bmbt_key_t *rkp; /* right btree key */
- xfs_bmbt_rec_t *rp; /* pointer to bmap btree rec */
- xfs_bmbt_ptr_t *rpp; /* right address pointer */
- xfs_bmbt_block_t *rrblock; /* right-right btree block */
- xfs_buf_t *rrbp; /* right-right buffer pointer */
- int rrecs=0; /* right record count */
- xfs_bmbt_rec_t *rrp; /* right record pointer */
- xfs_btree_cur_t *tcur; /* temporary btree cursor */
- int numrecs; /* temporary numrec count */
- int numlrecs, numrrecs;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, level);
- ptr = cur->bc_ptrs[level];
- tcur = NULL;
- if (ptr == 0) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- block = xfs_bmbt_get_block(cur, level, &bp);
- numrecs = be16_to_cpu(block->bb_numrecs);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
-#endif
- if (ptr > numrecs) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- XFS_STATS_INC(xs_bmbt_delrec);
- if (level > 0) {
- kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
- pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = ptr; i < numrecs; i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, pp[i], level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- }
-#endif
- if (ptr < numrecs) {
- memmove(&kp[ptr - 1], &kp[ptr],
- (numrecs - ptr) * sizeof(*kp));
- memmove(&pp[ptr - 1], &pp[ptr],
- (numrecs - ptr) * sizeof(*pp));
- xfs_bmbt_log_ptrs(cur, bp, ptr, numrecs - 1);
- xfs_bmbt_log_keys(cur, bp, ptr, numrecs - 1);
- }
- } else {
- rp = XFS_BMAP_REC_IADDR(block, 1, cur);
- if (ptr < numrecs) {
- memmove(&rp[ptr - 1], &rp[ptr],
- (numrecs - ptr) * sizeof(*rp));
- xfs_bmbt_log_recs(cur, bp, ptr, numrecs - 1);
- }
- if (ptr == 1) {
- key.br_startoff =
- cpu_to_be64(xfs_bmbt_disk_get_startoff(rp));
- kp = &key;
- }
- }
- numrecs--;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_bmbt_log_block(cur, bp, XFS_BB_NUMRECS);
- /*
- * We're at the root level.
- * First, shrink the root block in-memory.
- * Try to get rid of the next level down.
- * If we can't then there's nothing left to do.
- */
- if (level == cur->bc_nlevels - 1) {
- xfs_iroot_realloc(cur->bc_private.b.ip, -1,
- cur->bc_private.b.whichfork);
- if ((error = xfs_bmbt_killroot(cur))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &j))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- if (ptr == 1 && (error = xfs_bmbt_updkey(cur, kp, level + 1))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (numrecs >= XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
- if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &j))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- rbno = be64_to_cpu(block->bb_rightsib);
- lbno = be64_to_cpu(block->bb_leftsib);
- /*
- * One child of root, need to get a chance to copy its contents
- * into the root and delete it. Can't go up to next level,
- * there's nothing to delete there.
- */
- if (lbno == NULLFSBLOCK && rbno == NULLFSBLOCK &&
- level == cur->bc_nlevels - 2) {
- if ((error = xfs_bmbt_killroot(cur))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- ASSERT(rbno != NULLFSBLOCK || lbno != NULLFSBLOCK);
- if ((error = xfs_btree_dup_cursor(cur, &tcur))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- bno = NULLFSBLOCK;
- if (rbno != NULLFSBLOCK) {
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_bmbt_increment(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- rbp = tcur->bc_bufs[level];
- right = XFS_BUF_TO_BMBT_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
-#endif
- bno = be64_to_cpu(right->bb_leftsib);
- if (be16_to_cpu(right->bb_numrecs) - 1 >=
- XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
- if ((error = xfs_bmbt_lshift(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_BMAP_BLOCK_IMINRECS(level, tcur));
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- tcur = NULL;
- if (level > 0) {
- if ((error = xfs_bmbt_decrement(cur,
- level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur,
- ERROR);
- goto error0;
- }
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- }
- rrecs = be16_to_cpu(right->bb_numrecs);
- if (lbno != NULLFSBLOCK) {
- i = xfs_btree_firstrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_bmbt_decrement(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- }
- }
- if (lbno != NULLFSBLOCK) {
- i = xfs_btree_firstrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- /*
- * decrement to last in block
- */
- if ((error = xfs_bmbt_decrement(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- i = xfs_btree_firstrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- lbp = tcur->bc_bufs[level];
- left = XFS_BUF_TO_BMBT_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
-#endif
- bno = be64_to_cpu(left->bb_rightsib);
- if (be16_to_cpu(left->bb_numrecs) - 1 >=
- XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
- if ((error = xfs_bmbt_rshift(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_BMAP_BLOCK_IMINRECS(level, tcur));
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- tcur = NULL;
- if (level == 0)
- cur->bc_ptrs[0]++;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- }
- lrecs = be16_to_cpu(left->bb_numrecs);
- }
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- tcur = NULL;
- mp = cur->bc_mp;
- ASSERT(bno != NULLFSBLOCK);
- if (lbno != NULLFSBLOCK &&
- lrecs + be16_to_cpu(block->bb_numrecs) <= XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
- rbno = bno;
- right = block;
- rbp = bp;
- if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, lbno, 0, &lbp,
- XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- left = XFS_BUF_TO_BMBT_BLOCK(lbp);
- if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- } else if (rbno != NULLFSBLOCK &&
- rrecs + be16_to_cpu(block->bb_numrecs) <=
- XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
- lbno = bno;
- left = block;
- lbp = bp;
- if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, rbno, 0, &rbp,
- XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- right = XFS_BUF_TO_BMBT_BLOCK(rbp);
- if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- lrecs = be16_to_cpu(left->bb_numrecs);
- } else {
- if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- numlrecs = be16_to_cpu(left->bb_numrecs);
- numrrecs = be16_to_cpu(right->bb_numrecs);
- if (level > 0) {
- lkp = XFS_BMAP_KEY_IADDR(left, numlrecs + 1, cur);
- lpp = XFS_BMAP_PTR_IADDR(left, numlrecs + 1, cur);
- rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
- rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < numrrecs; i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, rpp[i], level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- }
-#endif
- memcpy(lkp, rkp, numrrecs * sizeof(*lkp));
- memcpy(lpp, rpp, numrrecs * sizeof(*lpp));
- xfs_bmbt_log_keys(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
- xfs_bmbt_log_ptrs(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
- } else {
- lrp = XFS_BMAP_REC_IADDR(left, numlrecs + 1, cur);
- rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
- memcpy(lrp, rrp, numrrecs * sizeof(*lrp));
- xfs_bmbt_log_recs(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
- }
- be16_add_cpu(&left->bb_numrecs, numrrecs);
- left->bb_rightsib = right->bb_rightsib;
- xfs_bmbt_log_block(cur, lbp, XFS_BB_RIGHTSIB | XFS_BB_NUMRECS);
- if (be64_to_cpu(left->bb_rightsib) != NULLDFSBNO) {
- if ((error = xfs_btree_read_bufl(mp, cur->bc_tp,
- be64_to_cpu(left->bb_rightsib),
- 0, &rrbp, XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- rrblock = XFS_BUF_TO_BMBT_BLOCK(rrbp);
- if ((error = xfs_btree_check_lblock(cur, rrblock, level, rrbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- rrblock->bb_leftsib = cpu_to_be64(lbno);
- xfs_bmbt_log_block(cur, rrbp, XFS_BB_LEFTSIB);
- }
- xfs_bmap_add_free(XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(rbp)), 1,
- cur->bc_private.b.flist, mp);
- cur->bc_private.b.ip->i_d.di_nblocks--;
- xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
- XFS_TRANS_MOD_DQUOT_BYINO(mp, cur->bc_tp, cur->bc_private.b.ip,
- XFS_TRANS_DQ_BCOUNT, -1L);
- xfs_trans_binval(cur->bc_tp, rbp);
- if (bp != lbp) {
- cur->bc_bufs[level] = lbp;
- cur->bc_ptrs[level] += lrecs;
- cur->bc_ra[level] = 0;
- } else if ((error = xfs_bmbt_increment(cur, level + 1, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- goto error0;
- }
- if (level > 0)
- cur->bc_ptrs[level]--;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 2;
- return 0;
-
-error0:
- if (tcur)
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
-}
-
-/*
- * Insert one record/level. Return information to the caller
- * allowing the next level up to proceed if necessary.
- */
-STATIC int /* error */
-xfs_bmbt_insrec(
- xfs_btree_cur_t *cur,
- int level,
- xfs_fsblock_t *bnop,
- xfs_bmbt_rec_t *recp,
- xfs_btree_cur_t **curp,
- int *stat) /* no-go/done/continue */
-{
- xfs_bmbt_block_t *block; /* bmap btree block */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop index */
- xfs_bmbt_key_t key; /* bmap btree key */
- xfs_bmbt_key_t *kp=NULL; /* pointer to bmap btree key */
- int logflags; /* inode logging flags */
- xfs_fsblock_t nbno; /* new block number */
- struct xfs_btree_cur *ncur; /* new btree cursor */
- __uint64_t startoff; /* new btree key value */
- xfs_bmbt_rec_t nrec; /* new record count */
- int optr; /* old key/record index */
- xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
- int ptr; /* key/record index */
- xfs_bmbt_rec_t *rp=NULL; /* pointer to bmap btree rec */
- int numrecs;
-
- ASSERT(level < cur->bc_nlevels);
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGIFR(cur, level, *bnop, recp);
- ncur = NULL;
- key.br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(recp));
- optr = ptr = cur->bc_ptrs[level];
- if (ptr == 0) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- XFS_STATS_INC(xs_bmbt_insrec);
- block = xfs_bmbt_get_block(cur, level, &bp);
- numrecs = be16_to_cpu(block->bb_numrecs);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (ptr <= numrecs) {
- if (level == 0) {
- rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
- xfs_btree_check_rec(XFS_BTNUM_BMAP, recp, rp);
- } else {
- kp = XFS_BMAP_KEY_IADDR(block, ptr, cur);
- xfs_btree_check_key(XFS_BTNUM_BMAP, &key, kp);
- }
- }
-#endif
- nbno = NULLFSBLOCK;
- if (numrecs == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
- if (numrecs < XFS_BMAP_BLOCK_DMAXRECS(level, cur)) {
- /*
- * A root block, that can be made bigger.
- */
- xfs_iroot_realloc(cur->bc_private.b.ip, 1,
- cur->bc_private.b.whichfork);
- block = xfs_bmbt_get_block(cur, level, &bp);
- } else if (level == cur->bc_nlevels - 1) {
- if ((error = xfs_bmbt_newroot(cur, &logflags, stat)) ||
- *stat == 0) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip,
- logflags);
- block = xfs_bmbt_get_block(cur, level, &bp);
- } else {
- if ((error = xfs_bmbt_rshift(cur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (i) {
- /* nothing */
- } else {
- if ((error = xfs_bmbt_lshift(cur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (i) {
- optr = ptr = cur->bc_ptrs[level];
- } else {
- if ((error = xfs_bmbt_split(cur, level,
- &nbno, &startoff, &ncur,
- &i))) {
- XFS_BMBT_TRACE_CURSOR(cur,
- ERROR);
- return error;
- }
- if (i) {
- block = xfs_bmbt_get_block(
- cur, level, &bp);
-#ifdef DEBUG
- if ((error =
- xfs_btree_check_lblock(cur,
- block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(
- cur, ERROR);
- return error;
- }
-#endif
- ptr = cur->bc_ptrs[level];
- xfs_bmbt_disk_set_allf(&nrec,
- startoff, 0, 0,
- XFS_EXT_NORM);
- } else {
- XFS_BMBT_TRACE_CURSOR(cur,
- EXIT);
- *stat = 0;
- return 0;
- }
- }
- }
- }
- }
- numrecs = be16_to_cpu(block->bb_numrecs);
- if (level > 0) {
- kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
- pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = numrecs; i >= ptr; i--) {
- if ((error = xfs_btree_check_lptr_disk(cur, pp[i - 1],
- level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memmove(&kp[ptr], &kp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*kp));
- memmove(&pp[ptr], &pp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*pp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr(cur, *bnop, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- kp[ptr - 1] = key;
- pp[ptr - 1] = cpu_to_be64(*bnop);
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_bmbt_log_keys(cur, bp, ptr, numrecs);
- xfs_bmbt_log_ptrs(cur, bp, ptr, numrecs);
- } else {
- rp = XFS_BMAP_REC_IADDR(block, 1, cur);
- memmove(&rp[ptr], &rp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*rp));
- rp[ptr - 1] = *recp;
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_bmbt_log_recs(cur, bp, ptr, numrecs);
- }
- xfs_bmbt_log_block(cur, bp, XFS_BB_NUMRECS);
-#ifdef DEBUG
- if (ptr < numrecs) {
- if (level == 0)
- xfs_btree_check_rec(XFS_BTNUM_BMAP, rp + ptr - 1,
- rp + ptr);
- else
- xfs_btree_check_key(XFS_BTNUM_BMAP, kp + ptr - 1,
- kp + ptr);
- }
-#endif
- if (optr == 1 && (error = xfs_bmbt_updkey(cur, &key, level + 1))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- *bnop = nbno;
- if (nbno != NULLFSBLOCK) {
- *recp = nrec;
- *curp = ncur;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-}
-
-STATIC int
-xfs_bmbt_killroot(
- xfs_btree_cur_t *cur)
-{
- xfs_bmbt_block_t *block;
- xfs_bmbt_block_t *cblock;
- xfs_buf_t *cbp;
- xfs_bmbt_key_t *ckp;
- xfs_bmbt_ptr_t *cpp;
-#ifdef DEBUG
- int error;
-#endif
- int i;
- xfs_bmbt_key_t *kp;
- xfs_inode_t *ip;
- xfs_ifork_t *ifp;
- int level;
- xfs_bmbt_ptr_t *pp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- level = cur->bc_nlevels - 1;
- ASSERT(level >= 1);
- /*
- * Don't deal with the root block needs to be a leaf case.
- * We're just going to turn the thing back into extents anyway.
- */
- if (level == 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
- }
- block = xfs_bmbt_get_block(cur, level, &cbp);
- /*
- * Give up if the root has multiple children.
- */
- if (be16_to_cpu(block->bb_numrecs) != 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
- }
- /*
- * Only do this if the next level will fit.
- * Then the data must be copied up to the inode,
- * instead of freeing the root you free the next level.
- */
- cbp = cur->bc_bufs[level - 1];
- cblock = XFS_BUF_TO_BMBT_BLOCK(cbp);
- if (be16_to_cpu(cblock->bb_numrecs) > XFS_BMAP_BLOCK_DMAXRECS(level, cur)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
- }
- ASSERT(be64_to_cpu(cblock->bb_leftsib) == NULLDFSBNO);
- ASSERT(be64_to_cpu(cblock->bb_rightsib) == NULLDFSBNO);
- ip = cur->bc_private.b.ip;
- ifp = XFS_IFORK_PTR(ip, cur->bc_private.b.whichfork);
- ASSERT(XFS_BMAP_BLOCK_IMAXRECS(level, cur) ==
- XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes));
- i = (int)(be16_to_cpu(cblock->bb_numrecs) - XFS_BMAP_BLOCK_IMAXRECS(level, cur));
- if (i) {
- xfs_iroot_realloc(ip, i, cur->bc_private.b.whichfork);
- block = ifp->if_broot;
- }
- be16_add_cpu(&block->bb_numrecs, i);
- ASSERT(block->bb_numrecs == cblock->bb_numrecs);
- kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
- ckp = XFS_BMAP_KEY_IADDR(cblock, 1, cur);
- memcpy(kp, ckp, be16_to_cpu(block->bb_numrecs) * sizeof(*kp));
- pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
- cpp = XFS_BMAP_PTR_IADDR(cblock, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(cblock->bb_numrecs); i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, cpp[i], level - 1))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memcpy(pp, cpp, be16_to_cpu(block->bb_numrecs) * sizeof(*pp));
- xfs_bmap_add_free(XFS_DADDR_TO_FSB(cur->bc_mp, XFS_BUF_ADDR(cbp)), 1,
- cur->bc_private.b.flist, cur->bc_mp);
- ip->i_d.di_nblocks--;
- XFS_TRANS_MOD_DQUOT_BYINO(cur->bc_mp, cur->bc_tp, ip,
- XFS_TRANS_DQ_BCOUNT, -1L);
- xfs_trans_binval(cur->bc_tp, cbp);
- cur->bc_bufs[level - 1] = NULL;
- be16_add_cpu(&block->bb_level, -1);
- xfs_trans_log_inode(cur->bc_tp, ip,
- XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
- cur->bc_nlevels--;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
-}
-
-/*
- * Log key values from the btree block.
- */
-STATIC void
-xfs_bmbt_log_keys(
- xfs_btree_cur_t *cur,
- xfs_buf_t *bp,
- int kfirst,
- int klast)
-{
- xfs_trans_t *tp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGBII(cur, bp, kfirst, klast);
- tp = cur->bc_tp;
- if (bp) {
- xfs_bmbt_block_t *block;
- int first;
- xfs_bmbt_key_t *kp;
- int last;
-
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- kp = XFS_BMAP_KEY_DADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(tp, bp, first, last);
- } else {
- xfs_inode_t *ip;
-
- ip = cur->bc_private.b.ip;
- xfs_trans_log_inode(tp, ip,
- XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-}
-
-/*
- * Log pointer values from the btree block.
- */
-STATIC void
-xfs_bmbt_log_ptrs(
- xfs_btree_cur_t *cur,
- xfs_buf_t *bp,
- int pfirst,
- int plast)
-{
- xfs_trans_t *tp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGBII(cur, bp, pfirst, plast);
- tp = cur->bc_tp;
- if (bp) {
- xfs_bmbt_block_t *block;
- int first;
- int last;
- xfs_bmbt_ptr_t *pp;
-
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- pp = XFS_BMAP_PTR_DADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(tp, bp, first, last);
- } else {
- xfs_inode_t *ip;
-
- ip = cur->bc_private.b.ip;
- xfs_trans_log_inode(tp, ip,
- XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-}
-
-/*
- * Lookup the record. The cursor is made to point to it, based on dir.
- */
-STATIC int /* error */
-xfs_bmbt_lookup(
- xfs_btree_cur_t *cur,
- xfs_lookup_t dir,
- int *stat) /* success/failure */
-{
- xfs_bmbt_block_t *block=NULL;
- xfs_buf_t *bp;
- xfs_daddr_t d;
- xfs_sfiloff_t diff;
- int error; /* error return value */
- xfs_fsblock_t fsbno=0;
- int high;
- int i;
- int keyno=0;
- xfs_bmbt_key_t *kkbase=NULL;
- xfs_bmbt_key_t *kkp;
- xfs_bmbt_rec_t *krbase=NULL;
- xfs_bmbt_rec_t *krp;
- int level;
- int low;
- xfs_mount_t *mp;
- xfs_bmbt_ptr_t *pp;
- xfs_bmbt_irec_t *rp;
- xfs_fileoff_t startoff;
- xfs_trans_t *tp;
-
- XFS_STATS_INC(xs_bmbt_lookup);
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, (int)dir);
- tp = cur->bc_tp;
- mp = cur->bc_mp;
- rp = &cur->bc_rec.b;
- for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
- if (level < cur->bc_nlevels - 1) {
- d = XFS_FSB_TO_DADDR(mp, fsbno);
- bp = cur->bc_bufs[level];
- if (bp && XFS_BUF_ADDR(bp) != d)
- bp = NULL;
- if (!bp) {
- if ((error = xfs_btree_read_bufl(mp, tp, fsbno,
- 0, &bp, XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- xfs_btree_setbuf(cur, level, bp);
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- if ((error = xfs_btree_check_lblock(cur, block,
- level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- } else
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- } else
- block = xfs_bmbt_get_block(cur, level, &bp);
- if (diff == 0)
- keyno = 1;
- else {
- if (level > 0)
- kkbase = XFS_BMAP_KEY_IADDR(block, 1, cur);
- else
- krbase = XFS_BMAP_REC_IADDR(block, 1, cur);
- low = 1;
- if (!(high = be16_to_cpu(block->bb_numrecs))) {
- ASSERT(level == 0);
- cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- while (low <= high) {
- XFS_STATS_INC(xs_bmbt_compare);
- keyno = (low + high) >> 1;
- if (level > 0) {
- kkp = kkbase + keyno - 1;
- startoff = be64_to_cpu(kkp->br_startoff);
- } else {
- krp = krbase + keyno - 1;
- startoff = xfs_bmbt_disk_get_startoff(krp);
- }
- diff = (xfs_sfiloff_t)
- (startoff - rp->br_startoff);
- if (diff < 0)
- low = keyno + 1;
- else if (diff > 0)
- high = keyno - 1;
- else
- break;
- }
- }
- if (level > 0) {
- if (diff > 0 && --keyno < 1)
- keyno = 1;
- pp = XFS_BMAP_PTR_IADDR(block, keyno, cur);
- fsbno = be64_to_cpu(*pp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr(cur, fsbno, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- cur->bc_ptrs[level] = keyno;
- }
- }
- if (dir != XFS_LOOKUP_LE && diff < 0) {
- keyno++;
- /*
- * If ge search and we went off the end of the block, but it's
- * not the last block, we're in the wrong block.
- */
- if (dir == XFS_LOOKUP_GE && keyno > be16_to_cpu(block->bb_numrecs) &&
- be64_to_cpu(block->bb_rightsib) != NULLDFSBNO) {
- cur->bc_ptrs[0] = keyno;
- if ((error = xfs_bmbt_increment(cur, 0, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- XFS_WANT_CORRUPTED_RETURN(i == 1);
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- }
- else if (dir == XFS_LOOKUP_LE && diff > 0)
- keyno--;
- cur->bc_ptrs[0] = keyno;
- if (keyno == 0 || keyno > be16_to_cpu(block->bb_numrecs)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- } else {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
- }
- return 0;
-}
-
-/*
- * Move 1 record left from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_bmbt_lshift(
- xfs_btree_cur_t *cur,
- int level,
- int *stat) /* success/failure */
-{
- int error; /* error return value */
-#ifdef DEBUG
- int i; /* loop counter */
-#endif
- xfs_bmbt_key_t key; /* bmap btree key */
- xfs_buf_t *lbp; /* left buffer pointer */
- xfs_bmbt_block_t *left; /* left btree block */
- xfs_bmbt_key_t *lkp=NULL; /* left btree key */
- xfs_bmbt_ptr_t *lpp; /* left address pointer */
- int lrecs; /* left record count */
- xfs_bmbt_rec_t *lrp=NULL; /* left record pointer */
- xfs_mount_t *mp; /* file system mount point */
- xfs_buf_t *rbp; /* right buffer pointer */
- xfs_bmbt_block_t *right; /* right btree block */
- xfs_bmbt_key_t *rkp=NULL; /* right btree key */
- xfs_bmbt_ptr_t *rpp=NULL; /* right address pointer */
- xfs_bmbt_rec_t *rrp=NULL; /* right record pointer */
- int rrecs; /* right record count */
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, level);
- if (level == cur->bc_nlevels - 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- rbp = cur->bc_bufs[level];
- right = XFS_BUF_TO_BMBT_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- if (be64_to_cpu(right->bb_leftsib) == NULLDFSBNO) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- if (cur->bc_ptrs[level] <= 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- mp = cur->bc_mp;
- if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, be64_to_cpu(right->bb_leftsib), 0,
- &lbp, XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- left = XFS_BUF_TO_BMBT_BLOCK(lbp);
- if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (be16_to_cpu(left->bb_numrecs) == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- lrecs = be16_to_cpu(left->bb_numrecs) + 1;
- if (level > 0) {
- lkp = XFS_BMAP_KEY_IADDR(left, lrecs, cur);
- rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
- *lkp = *rkp;
- xfs_bmbt_log_keys(cur, lbp, lrecs, lrecs);
- lpp = XFS_BMAP_PTR_IADDR(left, lrecs, cur);
- rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr_disk(cur, *rpp, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- *lpp = *rpp;
- xfs_bmbt_log_ptrs(cur, lbp, lrecs, lrecs);
- } else {
- lrp = XFS_BMAP_REC_IADDR(left, lrecs, cur);
- rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
- *lrp = *rrp;
- xfs_bmbt_log_recs(cur, lbp, lrecs, lrecs);
- }
- left->bb_numrecs = cpu_to_be16(lrecs);
- xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS);
-#ifdef DEBUG
- if (level > 0)
- xfs_btree_check_key(XFS_BTNUM_BMAP, lkp - 1, lkp);
- else
- xfs_btree_check_rec(XFS_BTNUM_BMAP, lrp - 1, lrp);
-#endif
- rrecs = be16_to_cpu(right->bb_numrecs) - 1;
- right->bb_numrecs = cpu_to_be16(rrecs);
- xfs_bmbt_log_block(cur, rbp, XFS_BB_NUMRECS);
- if (level > 0) {
-#ifdef DEBUG
- for (i = 0; i < rrecs; i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, rpp[i + 1],
- level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memmove(rkp, rkp + 1, rrecs * sizeof(*rkp));
- memmove(rpp, rpp + 1, rrecs * sizeof(*rpp));
- xfs_bmbt_log_keys(cur, rbp, 1, rrecs);
- xfs_bmbt_log_ptrs(cur, rbp, 1, rrecs);
- } else {
- memmove(rrp, rrp + 1, rrecs * sizeof(*rrp));
- xfs_bmbt_log_recs(cur, rbp, 1, rrecs);
- key.br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(rrp));
- rkp = &key;
- }
- if ((error = xfs_bmbt_updkey(cur, rkp, level + 1))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- cur->bc_ptrs[level]--;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-}
-
-/*
- * Move 1 record right from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_bmbt_rshift(
- xfs_btree_cur_t *cur,
- int level,
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i; /* loop counter */
- xfs_bmbt_key_t key; /* bmap btree key */
- xfs_buf_t *lbp; /* left buffer pointer */
- xfs_bmbt_block_t *left; /* left btree block */
- xfs_bmbt_key_t *lkp; /* left btree key */
- xfs_bmbt_ptr_t *lpp; /* left address pointer */
- xfs_bmbt_rec_t *lrp; /* left record pointer */
- xfs_mount_t *mp; /* file system mount point */
- xfs_buf_t *rbp; /* right buffer pointer */
- xfs_bmbt_block_t *right; /* right btree block */
- xfs_bmbt_key_t *rkp; /* right btree key */
- xfs_bmbt_ptr_t *rpp; /* right address pointer */
- xfs_bmbt_rec_t *rrp=NULL; /* right record pointer */
- struct xfs_btree_cur *tcur; /* temporary btree cursor */
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, level);
- if (level == cur->bc_nlevels - 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- lbp = cur->bc_bufs[level];
- left = XFS_BUF_TO_BMBT_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- if (be64_to_cpu(left->bb_rightsib) == NULLDFSBNO) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- if (cur->bc_ptrs[level] >= be16_to_cpu(left->bb_numrecs)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- mp = cur->bc_mp;
- if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, be64_to_cpu(left->bb_rightsib), 0,
- &rbp, XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- right = XFS_BUF_TO_BMBT_BLOCK(rbp);
- if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (be16_to_cpu(right->bb_numrecs) == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- if (level > 0) {
- lkp = XFS_BMAP_KEY_IADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- lpp = XFS_BMAP_PTR_IADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
- rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = be16_to_cpu(right->bb_numrecs) - 1; i >= 0; i--) {
- if ((error = xfs_btree_check_lptr_disk(cur, rpp[i], level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memmove(rkp + 1, rkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memmove(rpp + 1, rpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr_disk(cur, *lpp, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- *rkp = *lkp;
- *rpp = *lpp;
- xfs_bmbt_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- xfs_bmbt_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- } else {
- lrp = XFS_BMAP_REC_IADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
- memmove(rrp + 1, rrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- *rrp = *lrp;
- xfs_bmbt_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- key.br_startoff = cpu_to_be64(xfs_bmbt_disk_get_startoff(rrp));
- rkp = &key;
- }
- be16_add_cpu(&left->bb_numrecs, -1);
- xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS);
- be16_add_cpu(&right->bb_numrecs, 1);
-#ifdef DEBUG
- if (level > 0)
- xfs_btree_check_key(XFS_BTNUM_BMAP, rkp, rkp + 1);
- else
- xfs_btree_check_rec(XFS_BTNUM_BMAP, rrp, rrp + 1);
-#endif
- xfs_bmbt_log_block(cur, rbp, XFS_BB_NUMRECS);
- if ((error = xfs_btree_dup_cursor(cur, &tcur))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_bmbt_increment(tcur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(tcur, ERROR);
- goto error1;
- }
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_bmbt_updkey(tcur, rkp, level + 1))) {
- XFS_BMBT_TRACE_CURSOR(tcur, ERROR);
- goto error1;
- }
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-error0:
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
-error1:
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
-}
-
/*
* Determine the extent state.
*/
@@ -1453,229 +60,15 @@ xfs_extent_state(
return XFS_EXT_NORM;
}
-
-/*
- * Split cur/level block in half.
- * Return new block number and its first record (to be inserted into parent).
- */
-STATIC int /* error */
-xfs_bmbt_split(
- xfs_btree_cur_t *cur,
- int level,
- xfs_fsblock_t *bnop,
- __uint64_t *startoff,
- xfs_btree_cur_t **curp,
- int *stat) /* success/failure */
-{
- xfs_alloc_arg_t args; /* block allocation args */
- int error; /* error return value */
- int i; /* loop counter */
- xfs_fsblock_t lbno; /* left sibling block number */
- xfs_buf_t *lbp; /* left buffer pointer */
- xfs_bmbt_block_t *left; /* left btree block */
- xfs_bmbt_key_t *lkp; /* left btree key */
- xfs_bmbt_ptr_t *lpp; /* left address pointer */
- xfs_bmbt_rec_t *lrp; /* left record pointer */
- xfs_buf_t *rbp; /* right buffer pointer */
- xfs_bmbt_block_t *right; /* right btree block */
- xfs_bmbt_key_t *rkp; /* right btree key */
- xfs_bmbt_ptr_t *rpp; /* right address pointer */
- xfs_bmbt_block_t *rrblock; /* right-right btree block */
- xfs_buf_t *rrbp; /* right-right buffer pointer */
- xfs_bmbt_rec_t *rrp; /* right record pointer */
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGIFK(cur, level, *bnop, *startoff);
- args.tp = cur->bc_tp;
- args.mp = cur->bc_mp;
- lbp = cur->bc_bufs[level];
- lbno = XFS_DADDR_TO_FSB(args.mp, XFS_BUF_ADDR(lbp));
- left = XFS_BUF_TO_BMBT_BLOCK(lbp);
- args.fsbno = cur->bc_private.b.firstblock;
- args.firstblock = args.fsbno;
- args.minleft = 0;
- if (args.fsbno == NULLFSBLOCK) {
- args.fsbno = lbno;
- args.type = XFS_ALLOCTYPE_START_BNO;
- /*
- * Make sure there is sufficient room left in the AG to
- * complete a full tree split for an extent insert. If
- * we are converting the middle part of an extent then
- * we may need space for two tree splits.
- *
- * We are relying on the caller to make the correct block
- * reservation for this operation to succeed. If the
- * reservation amount is insufficient then we may fail a
- * block allocation here and corrupt the filesystem.
- */
- args.minleft = xfs_trans_get_block_res(args.tp);
- } else if (cur->bc_private.b.flist->xbf_low)
- args.type = XFS_ALLOCTYPE_START_BNO;
- else
- args.type = XFS_ALLOCTYPE_NEAR_BNO;
- args.mod = args.alignment = args.total = args.isfl =
- args.userdata = args.minalignslop = 0;
- args.minlen = args.maxlen = args.prod = 1;
- args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
- if (!args.wasdel && xfs_trans_get_block_res(args.tp) == 0) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return XFS_ERROR(ENOSPC);
- }
- if ((error = xfs_alloc_vextent(&args))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (args.fsbno == NULLFSBLOCK && args.minleft) {
- /*
- * Could not find an AG with enough free space to satisfy
- * a full btree split. Try again without minleft and if
- * successful activate the lowspace algorithm.
- */
- args.fsbno = 0;
- args.type = XFS_ALLOCTYPE_FIRST_AG;
- args.minleft = 0;
- if ((error = xfs_alloc_vextent(&args))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- cur->bc_private.b.flist->xbf_low = 1;
- }
- if (args.fsbno == NULLFSBLOCK) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- ASSERT(args.len == 1);
- cur->bc_private.b.firstblock = args.fsbno;
- cur->bc_private.b.allocated++;
- cur->bc_private.b.ip->i_d.di_nblocks++;
- xfs_trans_log_inode(args.tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
- XFS_TRANS_MOD_DQUOT_BYINO(args.mp, args.tp, cur->bc_private.b.ip,
- XFS_TRANS_DQ_BCOUNT, 1L);
- rbp = xfs_btree_get_bufl(args.mp, args.tp, args.fsbno, 0);
- right = XFS_BUF_TO_BMBT_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, left, level, rbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- right->bb_magic = cpu_to_be32(XFS_BMAP_MAGIC);
- right->bb_level = left->bb_level;
- right->bb_numrecs = cpu_to_be16(be16_to_cpu(left->bb_numrecs) / 2);
- if ((be16_to_cpu(left->bb_numrecs) & 1) &&
- cur->bc_ptrs[level] <= be16_to_cpu(right->bb_numrecs) + 1)
- be16_add_cpu(&right->bb_numrecs, 1);
- i = be16_to_cpu(left->bb_numrecs) - be16_to_cpu(right->bb_numrecs) + 1;
- if (level > 0) {
- lkp = XFS_BMAP_KEY_IADDR(left, i, cur);
- lpp = XFS_BMAP_PTR_IADDR(left, i, cur);
- rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
- rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(right->bb_numrecs); i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, lpp[i], level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memcpy(rkp, lkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memcpy(rpp, lpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
- xfs_bmbt_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- xfs_bmbt_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- *startoff = be64_to_cpu(rkp->br_startoff);
- } else {
- lrp = XFS_BMAP_REC_IADDR(left, i, cur);
- rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
- memcpy(rrp, lrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- xfs_bmbt_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- *startoff = xfs_bmbt_disk_get_startoff(rrp);
- }
- be16_add_cpu(&left->bb_numrecs, -(be16_to_cpu(right->bb_numrecs)));
- right->bb_rightsib = left->bb_rightsib;
- left->bb_rightsib = cpu_to_be64(args.fsbno);
- right->bb_leftsib = cpu_to_be64(lbno);
- xfs_bmbt_log_block(cur, rbp, XFS_BB_ALL_BITS);
- xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
- if (be64_to_cpu(right->bb_rightsib) != NULLDFSBNO) {
- if ((error = xfs_btree_read_bufl(args.mp, args.tp,
- be64_to_cpu(right->bb_rightsib), 0, &rrbp,
- XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- rrblock = XFS_BUF_TO_BMBT_BLOCK(rrbp);
- if ((error = xfs_btree_check_lblock(cur, rrblock, level, rrbp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- rrblock->bb_leftsib = cpu_to_be64(args.fsbno);
- xfs_bmbt_log_block(cur, rrbp, XFS_BB_LEFTSIB);
- }
- if (cur->bc_ptrs[level] > be16_to_cpu(left->bb_numrecs) + 1) {
- xfs_btree_setbuf(cur, level, rbp);
- cur->bc_ptrs[level] -= be16_to_cpu(left->bb_numrecs);
- }
- if (level + 1 < cur->bc_nlevels) {
- if ((error = xfs_btree_dup_cursor(cur, curp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- (*curp)->bc_ptrs[level + 1]++;
- }
- *bnop = args.fsbno;
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-}
-
-
-/*
- * Update keys for the record.
- */
-STATIC int
-xfs_bmbt_updkey(
- xfs_btree_cur_t *cur,
- xfs_bmbt_key_t *keyp, /* on-disk format */
- int level)
-{
- xfs_bmbt_block_t *block;
- xfs_buf_t *bp;
-#ifdef DEBUG
- int error;
-#endif
- xfs_bmbt_key_t *kp;
- int ptr;
-
- ASSERT(level >= 1);
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGIK(cur, level, keyp);
- for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
- block = xfs_bmbt_get_block(cur, level, &bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- ptr = cur->bc_ptrs[level];
- kp = XFS_BMAP_KEY_IADDR(block, ptr, cur);
- *kp = *keyp;
- xfs_bmbt_log_keys(cur, bp, ptr, ptr);
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
-}
-
/*
* Convert on-disk form of btree root to in-memory form.
*/
void
xfs_bmdr_to_bmbt(
+ struct xfs_mount *mp,
xfs_bmdr_block_t *dblock,
int dblocklen,
- xfs_bmbt_block_t *rblock,
+ struct xfs_btree_block *rblock,
int rblocklen)
{
int dmxr;
@@ -1688,129 +81,19 @@ xfs_bmdr_to_bmbt(
rblock->bb_level = dblock->bb_level;
ASSERT(be16_to_cpu(rblock->bb_level) > 0);
rblock->bb_numrecs = dblock->bb_numrecs;
- rblock->bb_leftsib = cpu_to_be64(NULLDFSBNO);
- rblock->bb_rightsib = cpu_to_be64(NULLDFSBNO);
- dmxr = (int)XFS_BTREE_BLOCK_MAXRECS(dblocklen, xfs_bmdr, 0);
- fkp = XFS_BTREE_KEY_ADDR(xfs_bmdr, dblock, 1);
- tkp = XFS_BMAP_BROOT_KEY_ADDR(rblock, 1, rblocklen);
- fpp = XFS_BTREE_PTR_ADDR(xfs_bmdr, dblock, 1, dmxr);
- tpp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, rblocklen);
+ rblock->bb_u.l.bb_leftsib = cpu_to_be64(NULLDFSBNO);
+ rblock->bb_u.l.bb_rightsib = cpu_to_be64(NULLDFSBNO);
+ dmxr = xfs_bmdr_maxrecs(mp, dblocklen, 0);
+ fkp = XFS_BMDR_KEY_ADDR(dblock, 1);
+ tkp = XFS_BMBT_KEY_ADDR(mp, rblock, 1);
+ fpp = XFS_BMDR_PTR_ADDR(dblock, 1, dmxr);
+ tpp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, rblocklen);
dmxr = be16_to_cpu(dblock->bb_numrecs);
memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
}
/*
- * Decrement cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-int /* error */
-xfs_bmbt_decrement(
- xfs_btree_cur_t *cur,
- int level,
- int *stat) /* success/failure */
-{
- xfs_bmbt_block_t *block;
- xfs_buf_t *bp;
- int error; /* error return value */
- xfs_fsblock_t fsbno;
- int lev;
- xfs_mount_t *mp;
- xfs_trans_t *tp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, level);
- ASSERT(level < cur->bc_nlevels);
- if (level < cur->bc_nlevels - 1)
- xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
- if (--cur->bc_ptrs[level] > 0) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- block = xfs_bmbt_get_block(cur, level, &bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- if (be64_to_cpu(block->bb_leftsib) == NULLDFSBNO) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- if (--cur->bc_ptrs[lev] > 0)
- break;
- if (lev < cur->bc_nlevels - 1)
- xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
- }
- if (lev == cur->bc_nlevels) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- tp = cur->bc_tp;
- mp = cur->bc_mp;
- for (block = xfs_bmbt_get_block(cur, lev, &bp); lev > level; ) {
- fsbno = be64_to_cpu(*XFS_BMAP_PTR_IADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufl(mp, tp, fsbno, 0, &bp,
- XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- cur->bc_ptrs[lev] = be16_to_cpu(block->bb_numrecs);
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-}
-
-/*
- * Delete the record pointed to by cur.
- */
-int /* error */
-xfs_bmbt_delete(
- xfs_btree_cur_t *cur,
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i;
- int level;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- for (level = 0, i = 2; i == 2; level++) {
- if ((error = xfs_bmbt_delrec(cur, level, &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
- if (i == 0) {
- for (level = 1; level < cur->bc_nlevels; level++) {
- if (cur->bc_ptrs[level] == 0) {
- if ((error = xfs_bmbt_decrement(cur, level,
- &i))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- break;
- }
- }
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = i;
- return 0;
-}
-
-/*
* Convert a compressed bmap extent record to an uncompressed form.
* This code must be in sync with the routines xfs_bmbt_get_startoff,
* xfs_bmbt_get_startblock, xfs_bmbt_get_blockcount and xfs_bmbt_get_state.
@@ -1864,31 +147,6 @@ xfs_bmbt_get_all(
}
/*
- * Get the block pointer for the given level of the cursor.
- * Fill in the buffer pointer, if applicable.
- */
-xfs_bmbt_block_t *
-xfs_bmbt_get_block(
- xfs_btree_cur_t *cur,
- int level,
- xfs_buf_t **bpp)
-{
- xfs_ifork_t *ifp;
- xfs_bmbt_block_t *rval;
-
- if (level < cur->bc_nlevels - 1) {
- *bpp = cur->bc_bufs[level];
- rval = XFS_BUF_TO_BMBT_BLOCK(*bpp);
- } else {
- *bpp = NULL;
- ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
- cur->bc_private.b.whichfork);
- rval = ifp->if_broot;
- }
- return rval;
-}
-
-/*
* Extract the blockcount field from an in memory bmap extent record.
*/
xfs_filblks_t
@@ -1950,7 +208,8 @@ xfs_bmbt_disk_get_all(
xfs_bmbt_rec_t *r,
xfs_bmbt_irec_t *s)
{
- __xfs_bmbt_get_all(be64_to_cpu(r->l0), be64_to_cpu(r->l1), s);
+ __xfs_bmbt_get_all(get_unaligned_be64(&r->l0),
+ get_unaligned_be64(&r->l1), s);
}
/*
@@ -1974,348 +233,6 @@ xfs_bmbt_disk_get_startoff(
XFS_MASK64LO(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
}
-/*
- * Increment cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-int /* error */
-xfs_bmbt_increment(
- xfs_btree_cur_t *cur,
- int level,
- int *stat) /* success/failure */
-{
- xfs_bmbt_block_t *block;
- xfs_buf_t *bp;
- int error; /* error return value */
- xfs_fsblock_t fsbno;
- int lev;
- xfs_mount_t *mp;
- xfs_trans_t *tp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGI(cur, level);
- ASSERT(level < cur->bc_nlevels);
- if (level < cur->bc_nlevels - 1)
- xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
- block = xfs_bmbt_get_block(cur, level, &bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- if (++cur->bc_ptrs[level] <= be16_to_cpu(block->bb_numrecs)) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
- }
- if (be64_to_cpu(block->bb_rightsib) == NULLDFSBNO) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- block = xfs_bmbt_get_block(cur, lev, &bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- if (++cur->bc_ptrs[lev] <= be16_to_cpu(block->bb_numrecs))
- break;
- if (lev < cur->bc_nlevels - 1)
- xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
- }
- if (lev == cur->bc_nlevels) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- tp = cur->bc_tp;
- mp = cur->bc_mp;
- for (block = xfs_bmbt_get_block(cur, lev, &bp); lev > level; ) {
- fsbno = be64_to_cpu(*XFS_BMAP_PTR_IADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufl(mp, tp, fsbno, 0, &bp,
- XFS_BMAP_BTREE_REF))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- cur->bc_ptrs[lev] = 1;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 1;
- return 0;
-}
-
-/*
- * Insert the current record at the point referenced by cur.
- *
- * A multi-level split of the tree on insert will invalidate the original
- * cursor. All callers of this function should assume that the cursor is
- * no longer valid and revalidate it.
- */
-int /* error */
-xfs_bmbt_insert(
- xfs_btree_cur_t *cur,
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i;
- int level;
- xfs_fsblock_t nbno;
- xfs_btree_cur_t *ncur;
- xfs_bmbt_rec_t nrec;
- xfs_btree_cur_t *pcur;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- level = 0;
- nbno = NULLFSBLOCK;
- xfs_bmbt_disk_set_all(&nrec, &cur->bc_rec.b);
- ncur = NULL;
- pcur = cur;
- do {
- if ((error = xfs_bmbt_insrec(pcur, level++, &nbno, &nrec, &ncur,
- &i))) {
- if (pcur != cur)
- xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if (pcur != cur && (ncur || nbno == NULLFSBLOCK)) {
- cur->bc_nlevels = pcur->bc_nlevels;
- cur->bc_private.b.allocated +=
- pcur->bc_private.b.allocated;
- pcur->bc_private.b.allocated = 0;
- ASSERT((cur->bc_private.b.firstblock != NULLFSBLOCK) ||
- XFS_IS_REALTIME_INODE(cur->bc_private.b.ip));
- cur->bc_private.b.firstblock =
- pcur->bc_private.b.firstblock;
- ASSERT(cur->bc_private.b.flist ==
- pcur->bc_private.b.flist);
- xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
- }
- if (ncur) {
- pcur = ncur;
- ncur = NULL;
- }
- } while (nbno != NULLFSBLOCK);
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = i;
- return 0;
-error0:
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
-}
-
-/*
- * Log fields from the btree block header.
- */
-void
-xfs_bmbt_log_block(
- xfs_btree_cur_t *cur,
- xfs_buf_t *bp,
- int fields)
-{
- int first;
- int last;
- xfs_trans_t *tp;
- static const short offsets[] = {
- offsetof(xfs_bmbt_block_t, bb_magic),
- offsetof(xfs_bmbt_block_t, bb_level),
- offsetof(xfs_bmbt_block_t, bb_numrecs),
- offsetof(xfs_bmbt_block_t, bb_leftsib),
- offsetof(xfs_bmbt_block_t, bb_rightsib),
- sizeof(xfs_bmbt_block_t)
- };
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGBI(cur, bp, fields);
- tp = cur->bc_tp;
- if (bp) {
- xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first,
- &last);
- xfs_trans_log_buf(tp, bp, first, last);
- } else
- xfs_trans_log_inode(tp, cur->bc_private.b.ip,
- XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-}
-
-/*
- * Log record values from the btree block.
- */
-void
-xfs_bmbt_log_recs(
- xfs_btree_cur_t *cur,
- xfs_buf_t *bp,
- int rfirst,
- int rlast)
-{
- xfs_bmbt_block_t *block;
- int first;
- int last;
- xfs_bmbt_rec_t *rp;
- xfs_trans_t *tp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGBII(cur, bp, rfirst, rlast);
- ASSERT(bp);
- tp = cur->bc_tp;
- block = XFS_BUF_TO_BMBT_BLOCK(bp);
- rp = XFS_BMAP_REC_DADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(tp, bp, first, last);
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
-}
-
-int /* error */
-xfs_bmbt_lookup_eq(
- xfs_btree_cur_t *cur,
- xfs_fileoff_t off,
- xfs_fsblock_t bno,
- xfs_filblks_t len,
- int *stat) /* success/failure */
-{
- cur->bc_rec.b.br_startoff = off;
- cur->bc_rec.b.br_startblock = bno;
- cur->bc_rec.b.br_blockcount = len;
- return xfs_bmbt_lookup(cur, XFS_LOOKUP_EQ, stat);
-}
-
-int /* error */
-xfs_bmbt_lookup_ge(
- xfs_btree_cur_t *cur,
- xfs_fileoff_t off,
- xfs_fsblock_t bno,
- xfs_filblks_t len,
- int *stat) /* success/failure */
-{
- cur->bc_rec.b.br_startoff = off;
- cur->bc_rec.b.br_startblock = bno;
- cur->bc_rec.b.br_blockcount = len;
- return xfs_bmbt_lookup(cur, XFS_LOOKUP_GE, stat);
-}
-
-/*
- * Give the bmap btree a new root block. Copy the old broot contents
- * down into a real block and make the broot point to it.
- */
-int /* error */
-xfs_bmbt_newroot(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *logflags, /* logging flags for inode */
- int *stat) /* return status - 0 fail */
-{
- xfs_alloc_arg_t args; /* allocation arguments */
- xfs_bmbt_block_t *block; /* bmap btree block */
- xfs_buf_t *bp; /* buffer for block */
- xfs_bmbt_block_t *cblock; /* child btree block */
- xfs_bmbt_key_t *ckp; /* child key pointer */
- xfs_bmbt_ptr_t *cpp; /* child ptr pointer */
- int error; /* error return code */
-#ifdef DEBUG
- int i; /* loop counter */
-#endif
- xfs_bmbt_key_t *kp; /* pointer to bmap btree key */
- int level; /* btree level */
- xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- level = cur->bc_nlevels - 1;
- block = xfs_bmbt_get_block(cur, level, &bp);
- /*
- * Copy the root into a real block.
- */
- args.mp = cur->bc_mp;
- pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
- args.tp = cur->bc_tp;
- args.fsbno = cur->bc_private.b.firstblock;
- args.mod = args.minleft = args.alignment = args.total = args.isfl =
- args.userdata = args.minalignslop = 0;
- args.minlen = args.maxlen = args.prod = 1;
- args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
- args.firstblock = args.fsbno;
- if (args.fsbno == NULLFSBLOCK) {
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr_disk(cur, *pp, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- args.fsbno = be64_to_cpu(*pp);
- args.type = XFS_ALLOCTYPE_START_BNO;
- } else if (cur->bc_private.b.flist->xbf_low)
- args.type = XFS_ALLOCTYPE_START_BNO;
- else
- args.type = XFS_ALLOCTYPE_NEAR_BNO;
- if ((error = xfs_alloc_vextent(&args))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- if (args.fsbno == NULLFSBLOCK) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *stat = 0;
- return 0;
- }
- ASSERT(args.len == 1);
- cur->bc_private.b.firstblock = args.fsbno;
- cur->bc_private.b.allocated++;
- cur->bc_private.b.ip->i_d.di_nblocks++;
- XFS_TRANS_MOD_DQUOT_BYINO(args.mp, args.tp, cur->bc_private.b.ip,
- XFS_TRANS_DQ_BCOUNT, 1L);
- bp = xfs_btree_get_bufl(args.mp, cur->bc_tp, args.fsbno, 0);
- cblock = XFS_BUF_TO_BMBT_BLOCK(bp);
- *cblock = *block;
- be16_add_cpu(&block->bb_level, 1);
- block->bb_numrecs = cpu_to_be16(1);
- cur->bc_nlevels++;
- cur->bc_ptrs[level + 1] = 1;
- kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
- ckp = XFS_BMAP_KEY_IADDR(cblock, 1, cur);
- memcpy(ckp, kp, be16_to_cpu(cblock->bb_numrecs) * sizeof(*kp));
- cpp = XFS_BMAP_PTR_IADDR(cblock, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(cblock->bb_numrecs); i++) {
- if ((error = xfs_btree_check_lptr_disk(cur, pp[i], level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- }
-#endif
- memcpy(cpp, pp, be16_to_cpu(cblock->bb_numrecs) * sizeof(*pp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_lptr(cur, args.fsbno, level))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- *pp = cpu_to_be64(args.fsbno);
- xfs_iroot_realloc(cur->bc_private.b.ip, 1 - be16_to_cpu(cblock->bb_numrecs),
- cur->bc_private.b.whichfork);
- xfs_btree_setbuf(cur, level, bp);
- /*
- * Do all this logging at the end so that
- * the root is at the right level.
- */
- xfs_bmbt_log_block(cur, bp, XFS_BB_ALL_BITS);
- xfs_bmbt_log_keys(cur, bp, 1, be16_to_cpu(cblock->bb_numrecs));
- xfs_bmbt_log_ptrs(cur, bp, 1, be16_to_cpu(cblock->bb_numrecs));
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- *logflags |=
- XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork);
- *stat = 1;
- return 0;
-}
/*
* Set all the fields in a bmap extent record from the arguments.
@@ -2512,7 +429,8 @@ xfs_bmbt_set_state(
*/
void
xfs_bmbt_to_bmdr(
- xfs_bmbt_block_t *rblock,
+ struct xfs_mount *mp,
+ struct xfs_btree_block *rblock,
int rblocklen,
xfs_bmdr_block_t *dblock,
int dblocklen)
@@ -2524,67 +442,22 @@ xfs_bmbt_to_bmdr(
__be64 *tpp;
ASSERT(be32_to_cpu(rblock->bb_magic) == XFS_BMAP_MAGIC);
- ASSERT(be64_to_cpu(rblock->bb_leftsib) == NULLDFSBNO);
- ASSERT(be64_to_cpu(rblock->bb_rightsib) == NULLDFSBNO);
+ ASSERT(be64_to_cpu(rblock->bb_u.l.bb_leftsib) == NULLDFSBNO);
+ ASSERT(be64_to_cpu(rblock->bb_u.l.bb_rightsib) == NULLDFSBNO);
ASSERT(be16_to_cpu(rblock->bb_level) > 0);
dblock->bb_level = rblock->bb_level;
dblock->bb_numrecs = rblock->bb_numrecs;
- dmxr = (int)XFS_BTREE_BLOCK_MAXRECS(dblocklen, xfs_bmdr, 0);
- fkp = XFS_BMAP_BROOT_KEY_ADDR(rblock, 1, rblocklen);
- tkp = XFS_BTREE_KEY_ADDR(xfs_bmdr, dblock, 1);
- fpp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, rblocklen);
- tpp = XFS_BTREE_PTR_ADDR(xfs_bmdr, dblock, 1, dmxr);
+ dmxr = xfs_bmdr_maxrecs(mp, dblocklen, 0);
+ fkp = XFS_BMBT_KEY_ADDR(mp, rblock, 1);
+ tkp = XFS_BMDR_KEY_ADDR(dblock, 1);
+ fpp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, rblocklen);
+ tpp = XFS_BMDR_PTR_ADDR(dblock, 1, dmxr);
dmxr = be16_to_cpu(dblock->bb_numrecs);
memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
}
/*
- * Update the record to the passed values.
- */
-int
-xfs_bmbt_update(
- xfs_btree_cur_t *cur,
- xfs_fileoff_t off,
- xfs_fsblock_t bno,
- xfs_filblks_t len,
- xfs_exntst_t state)
-{
- xfs_bmbt_block_t *block;
- xfs_buf_t *bp;
- int error;
- xfs_bmbt_key_t key;
- int ptr;
- xfs_bmbt_rec_t *rp;
-
- XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
- XFS_BMBT_TRACE_ARGFFFI(cur, (xfs_dfiloff_t)off, (xfs_dfsbno_t)bno,
- (xfs_dfilblks_t)len, (int)state);
- block = xfs_bmbt_get_block(cur, 0, &bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_lblock(cur, block, 0, bp))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
-#endif
- ptr = cur->bc_ptrs[0];
- rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
- xfs_bmbt_disk_set_allf(rp, off, bno, len, state);
- xfs_bmbt_log_recs(cur, bp, ptr, ptr);
- if (ptr > 1) {
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
- }
- key.br_startoff = cpu_to_be64(off);
- if ((error = xfs_bmbt_updkey(cur, &key, 1))) {
- XFS_BMBT_TRACE_CURSOR(cur, ERROR);
- return error;
- }
- XFS_BMBT_TRACE_CURSOR(cur, EXIT);
- return 0;
-}
-
-/*
* Check extent records, which have just been read, for
* any bit in the extent flag field. ASSERT on debug
* kernels, as this condition should not occur.
@@ -2608,3 +481,451 @@ xfs_check_nostate_extents(
}
return 0;
}
+
+
+STATIC struct xfs_btree_cur *
+xfs_bmbt_dup_cursor(
+ struct xfs_btree_cur *cur)
+{
+ struct xfs_btree_cur *new;
+
+ new = xfs_bmbt_init_cursor(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.b.ip, cur->bc_private.b.whichfork);
+
+ /*
+ * Copy the firstblock, flist, and flags values,
+ * since init cursor doesn't get them.
+ */
+ new->bc_private.b.firstblock = cur->bc_private.b.firstblock;
+ new->bc_private.b.flist = cur->bc_private.b.flist;
+ new->bc_private.b.flags = cur->bc_private.b.flags;
+
+ return new;
+}
+
+STATIC void
+xfs_bmbt_update_cursor(
+ struct xfs_btree_cur *src,
+ struct xfs_btree_cur *dst)
+{
+ ASSERT((dst->bc_private.b.firstblock != NULLFSBLOCK) ||
+ (dst->bc_private.b.ip->i_d.di_flags & XFS_DIFLAG_REALTIME));
+ ASSERT(dst->bc_private.b.flist == src->bc_private.b.flist);
+
+ dst->bc_private.b.allocated += src->bc_private.b.allocated;
+ dst->bc_private.b.firstblock = src->bc_private.b.firstblock;
+
+ src->bc_private.b.allocated = 0;
+}
+
+STATIC int
+xfs_bmbt_alloc_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *start,
+ union xfs_btree_ptr *new,
+ int length,
+ int *stat)
+{
+ xfs_alloc_arg_t args; /* block allocation args */
+ int error; /* error return value */
+
+ memset(&args, 0, sizeof(args));
+ args.tp = cur->bc_tp;
+ args.mp = cur->bc_mp;
+ args.fsbno = cur->bc_private.b.firstblock;
+ args.firstblock = args.fsbno;
+
+ if (args.fsbno == NULLFSBLOCK) {
+ args.fsbno = be64_to_cpu(start->l);
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ /*
+ * Make sure there is sufficient room left in the AG to
+ * complete a full tree split for an extent insert. If
+ * we are converting the middle part of an extent then
+ * we may need space for two tree splits.
+ *
+ * We are relying on the caller to make the correct block
+ * reservation for this operation to succeed. If the
+ * reservation amount is insufficient then we may fail a
+ * block allocation here and corrupt the filesystem.
+ */
+ args.minleft = xfs_trans_get_block_res(args.tp);
+ } else if (cur->bc_private.b.flist->xbf_low) {
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ } else {
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ }
+
+ args.minlen = args.maxlen = args.prod = 1;
+ args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
+ if (!args.wasdel && xfs_trans_get_block_res(args.tp) == 0) {
+ error = XFS_ERROR(ENOSPC);
+ goto error0;
+ }
+ error = xfs_alloc_vextent(&args);
+ if (error)
+ goto error0;
+
+ if (args.fsbno == NULLFSBLOCK && args.minleft) {
+ /*
+ * Could not find an AG with enough free space to satisfy
+ * a full btree split. Try again without minleft and if
+ * successful activate the lowspace algorithm.
+ */
+ args.fsbno = 0;
+ args.type = XFS_ALLOCTYPE_FIRST_AG;
+ args.minleft = 0;
+ error = xfs_alloc_vextent(&args);
+ if (error)
+ goto error0;
+ cur->bc_private.b.flist->xbf_low = 1;
+ }
+ if (args.fsbno == NULLFSBLOCK) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+ }
+ ASSERT(args.len == 1);
+ cur->bc_private.b.firstblock = args.fsbno;
+ cur->bc_private.b.allocated++;
+ cur->bc_private.b.ip->i_d.di_nblocks++;
+ xfs_trans_log_inode(args.tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
+ XFS_TRANS_MOD_DQUOT_BYINO(args.mp, args.tp, cur->bc_private.b.ip,
+ XFS_TRANS_DQ_BCOUNT, 1L);
+
+ new->l = cpu_to_be64(args.fsbno);
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+ error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+STATIC int
+xfs_bmbt_free_block(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_inode *ip = cur->bc_private.b.ip;
+ struct xfs_trans *tp = cur->bc_tp;
+ xfs_fsblock_t fsbno = XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(bp));
+
+ xfs_bmap_add_free(fsbno, 1, cur->bc_private.b.flist, mp);
+ ip->i_d.di_nblocks--;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
+ xfs_trans_binval(tp, bp);
+ return 0;
+}
+
+STATIC int
+xfs_bmbt_get_minrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ if (level == cur->bc_nlevels - 1) {
+ struct xfs_ifork *ifp;
+
+ ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
+ cur->bc_private.b.whichfork);
+
+ return xfs_bmbt_maxrecs(cur->bc_mp,
+ ifp->if_broot_bytes, level == 0) / 2;
+ }
+
+ return cur->bc_mp->m_bmap_dmnr[level != 0];
+}
+
+int
+xfs_bmbt_get_maxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ if (level == cur->bc_nlevels - 1) {
+ struct xfs_ifork *ifp;
+
+ ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
+ cur->bc_private.b.whichfork);
+
+ return xfs_bmbt_maxrecs(cur->bc_mp,
+ ifp->if_broot_bytes, level == 0);
+ }
+
+ return cur->bc_mp->m_bmap_dmxr[level != 0];
+
+}
+
+/*
+ * Get the maximum records we could store in the on-disk format.
+ *
+ * For non-root nodes this is equivalent to xfs_bmbt_get_maxrecs, but
+ * for the root node this checks the available space in the dinode fork
+ * so that we can resize the in-memory buffer to match it. After a
+ * resize to the maximum size this function returns the same value
+ * as xfs_bmbt_get_maxrecs for the root node, too.
+ */
+STATIC int
+xfs_bmbt_get_dmaxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ if (level != cur->bc_nlevels - 1)
+ return cur->bc_mp->m_bmap_dmxr[level != 0];
+ return xfs_bmdr_maxrecs(cur->bc_mp, cur->bc_private.b.forksize,
+ level == 0);
+}
+
+STATIC void
+xfs_bmbt_init_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ key->bmbt.br_startoff =
+ cpu_to_be64(xfs_bmbt_disk_get_startoff(&rec->bmbt));
+}
+
+STATIC void
+xfs_bmbt_init_rec_from_key(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ ASSERT(key->bmbt.br_startoff != 0);
+
+ xfs_bmbt_disk_set_allf(&rec->bmbt, be64_to_cpu(key->bmbt.br_startoff),
+ 0, 0, XFS_EXT_NORM);
+}
+
+STATIC void
+xfs_bmbt_init_rec_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec)
+{
+ xfs_bmbt_disk_set_all(&rec->bmbt, &cur->bc_rec.b);
+}
+
+STATIC void
+xfs_bmbt_init_ptr_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
+{
+ ptr->l = 0;
+}
+
+STATIC __int64_t
+xfs_bmbt_key_diff(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key)
+{
+ return (__int64_t)be64_to_cpu(key->bmbt.br_startoff) -
+ cur->bc_rec.b.br_startoff;
+}
+
+#ifdef DEBUG
+STATIC int
+xfs_bmbt_keys_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return be64_to_cpu(k1->bmbt.br_startoff) <
+ be64_to_cpu(k2->bmbt.br_startoff);
+}
+
+STATIC int
+xfs_bmbt_recs_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2)
+{
+ return xfs_bmbt_disk_get_startoff(&r1->bmbt) +
+ xfs_bmbt_disk_get_blockcount(&r1->bmbt) <=
+ xfs_bmbt_disk_get_startoff(&r2->bmbt);
+}
+#endif /* DEBUG */
+
+#ifdef XFS_BTREE_TRACE
+ktrace_t *xfs_bmbt_trace_buf;
+
+STATIC void
+xfs_bmbt_trace_enter(
+ struct xfs_btree_cur *cur,
+ const char *func,
+ char *s,
+ int type,
+ int line,
+ __psunsigned_t a0,
+ __psunsigned_t a1,
+ __psunsigned_t a2,
+ __psunsigned_t a3,
+ __psunsigned_t a4,
+ __psunsigned_t a5,
+ __psunsigned_t a6,
+ __psunsigned_t a7,
+ __psunsigned_t a8,
+ __psunsigned_t a9,
+ __psunsigned_t a10)
+{
+ struct xfs_inode *ip = cur->bc_private.b.ip;
+ int whichfork = cur->bc_private.b.whichfork;
+
+ ktrace_enter(xfs_bmbt_trace_buf,
+ (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
+ (void *)func, (void *)s, (void *)ip, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
+ ktrace_enter(ip->i_btrace,
+ (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
+ (void *)func, (void *)s, (void *)ip, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
+}
+
+STATIC void
+xfs_bmbt_trace_cursor(
+ struct xfs_btree_cur *cur,
+ __uint32_t *s0,
+ __uint64_t *l0,
+ __uint64_t *l1)
+{
+ struct xfs_bmbt_rec_host r;
+
+ xfs_bmbt_set_all(&r, &cur->bc_rec.b);
+
+ *s0 = (cur->bc_nlevels << 24) |
+ (cur->bc_private.b.flags << 16) |
+ cur->bc_private.b.allocated;
+ *l0 = r.l0;
+ *l1 = r.l1;
+}
+
+STATIC void
+xfs_bmbt_trace_key(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key,
+ __uint64_t *l0,
+ __uint64_t *l1)
+{
+ *l0 = be64_to_cpu(key->bmbt.br_startoff);
+ *l1 = 0;
+}
+
+STATIC void
+xfs_bmbt_trace_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ __uint64_t *l0,
+ __uint64_t *l1,
+ __uint64_t *l2)
+{
+ struct xfs_bmbt_irec irec;
+
+ xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
+ *l0 = irec.br_startoff;
+ *l1 = irec.br_startblock;
+ *l2 = irec.br_blockcount;
+}
+#endif /* XFS_BTREE_TRACE */
+
+static const struct xfs_btree_ops xfs_bmbt_ops = {
+ .rec_len = sizeof(xfs_bmbt_rec_t),
+ .key_len = sizeof(xfs_bmbt_key_t),
+
+ .dup_cursor = xfs_bmbt_dup_cursor,
+ .update_cursor = xfs_bmbt_update_cursor,
+ .alloc_block = xfs_bmbt_alloc_block,
+ .free_block = xfs_bmbt_free_block,
+ .get_maxrecs = xfs_bmbt_get_maxrecs,
+ .get_minrecs = xfs_bmbt_get_minrecs,
+ .get_dmaxrecs = xfs_bmbt_get_dmaxrecs,
+ .init_key_from_rec = xfs_bmbt_init_key_from_rec,
+ .init_rec_from_key = xfs_bmbt_init_rec_from_key,
+ .init_rec_from_cur = xfs_bmbt_init_rec_from_cur,
+ .init_ptr_from_cur = xfs_bmbt_init_ptr_from_cur,
+ .key_diff = xfs_bmbt_key_diff,
+
+#ifdef DEBUG
+ .keys_inorder = xfs_bmbt_keys_inorder,
+ .recs_inorder = xfs_bmbt_recs_inorder,
+#endif
+
+#ifdef XFS_BTREE_TRACE
+ .trace_enter = xfs_bmbt_trace_enter,
+ .trace_cursor = xfs_bmbt_trace_cursor,
+ .trace_key = xfs_bmbt_trace_key,
+ .trace_record = xfs_bmbt_trace_record,
+#endif
+};
+
+/*
+ * Allocate a new bmap btree cursor.
+ */
+struct xfs_btree_cur * /* new bmap btree cursor */
+xfs_bmbt_init_cursor(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* inode owning the btree */
+ int whichfork) /* data or attr fork */
+{
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
+ struct xfs_btree_cur *cur;
+
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+
+ cur->bc_tp = tp;
+ cur->bc_mp = mp;
+ cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
+ cur->bc_btnum = XFS_BTNUM_BMAP;
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
+
+ cur->bc_ops = &xfs_bmbt_ops;
+ cur->bc_flags = XFS_BTREE_LONG_PTRS | XFS_BTREE_ROOT_IN_INODE;
+
+ cur->bc_private.b.forksize = XFS_IFORK_SIZE(ip, whichfork);
+ cur->bc_private.b.ip = ip;
+ cur->bc_private.b.firstblock = NULLFSBLOCK;
+ cur->bc_private.b.flist = NULL;
+ cur->bc_private.b.allocated = 0;
+ cur->bc_private.b.flags = 0;
+ cur->bc_private.b.whichfork = whichfork;
+
+ return cur;
+}
+
+/*
+ * Calculate number of records in a bmap btree block.
+ */
+int
+xfs_bmbt_maxrecs(
+ struct xfs_mount *mp,
+ int blocklen,
+ int leaf)
+{
+ blocklen -= XFS_BMBT_BLOCK_LEN(mp);
+
+ if (leaf)
+ return blocklen / sizeof(xfs_bmbt_rec_t);
+ return blocklen / (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t));
+}
+
+/*
+ * Calculate number of records in a bmap btree inode root.
+ */
+int
+xfs_bmdr_maxrecs(
+ struct xfs_mount *mp,
+ int blocklen,
+ int leaf)
+{
+ blocklen -= sizeof(xfs_bmdr_block_t);
+
+ if (leaf)
+ return blocklen / sizeof(xfs_bmdr_rec_t);
+ return blocklen / (sizeof(xfs_bmdr_key_t) + sizeof(xfs_bmdr_ptr_t));
+}
diff --git a/fs/xfs/xfs_bmap_btree.h b/fs/xfs/xfs_bmap_btree.h
index cd0d4b4bb816..a4555abb6622 100644
--- a/fs/xfs/xfs_bmap_btree.h
+++ b/fs/xfs/xfs_bmap_btree.h
@@ -21,9 +21,10 @@
#define XFS_BMAP_MAGIC 0x424d4150 /* 'BMAP' */
struct xfs_btree_cur;
-struct xfs_btree_lblock;
+struct xfs_btree_block;
struct xfs_mount;
struct xfs_inode;
+struct xfs_trans;
/*
* Bmap root header, on-disk form only.
@@ -145,71 +146,60 @@ typedef struct xfs_bmbt_key {
/* btree pointer type */
typedef __be64 xfs_bmbt_ptr_t, xfs_bmdr_ptr_t;
-/* btree block header type */
-typedef struct xfs_btree_lblock xfs_bmbt_block_t;
-
-#define XFS_BUF_TO_BMBT_BLOCK(bp) ((xfs_bmbt_block_t *)XFS_BUF_PTR(bp))
-
-#define XFS_BMAP_RBLOCK_DSIZE(lev,cur) ((cur)->bc_private.b.forksize)
-#define XFS_BMAP_RBLOCK_ISIZE(lev,cur) \
- ((int)XFS_IFORK_PTR((cur)->bc_private.b.ip, \
- (cur)->bc_private.b.whichfork)->if_broot_bytes)
-
-#define XFS_BMAP_BLOCK_DMAXRECS(lev,cur) \
- (((lev) == (cur)->bc_nlevels - 1 ? \
- XFS_BTREE_BLOCK_MAXRECS(XFS_BMAP_RBLOCK_DSIZE(lev,cur), \
- xfs_bmdr, (lev) == 0) : \
- ((cur)->bc_mp->m_bmap_dmxr[(lev) != 0])))
-#define XFS_BMAP_BLOCK_IMAXRECS(lev,cur) \
- (((lev) == (cur)->bc_nlevels - 1 ? \
- XFS_BTREE_BLOCK_MAXRECS(XFS_BMAP_RBLOCK_ISIZE(lev,cur),\
- xfs_bmbt, (lev) == 0) : \
- ((cur)->bc_mp->m_bmap_dmxr[(lev) != 0])))
-
-#define XFS_BMAP_BLOCK_DMINRECS(lev,cur) \
- (((lev) == (cur)->bc_nlevels - 1 ? \
- XFS_BTREE_BLOCK_MINRECS(XFS_BMAP_RBLOCK_DSIZE(lev,cur),\
- xfs_bmdr, (lev) == 0) : \
- ((cur)->bc_mp->m_bmap_dmnr[(lev) != 0])))
-#define XFS_BMAP_BLOCK_IMINRECS(lev,cur) \
- (((lev) == (cur)->bc_nlevels - 1 ? \
- XFS_BTREE_BLOCK_MINRECS(XFS_BMAP_RBLOCK_ISIZE(lev,cur),\
- xfs_bmbt, (lev) == 0) : \
- ((cur)->bc_mp->m_bmap_dmnr[(lev) != 0])))
-
-#define XFS_BMAP_REC_DADDR(bb,i,cur) (XFS_BTREE_REC_ADDR(xfs_bmbt, bb, i))
-
-#define XFS_BMAP_REC_IADDR(bb,i,cur) (XFS_BTREE_REC_ADDR(xfs_bmbt, bb, i))
-
-#define XFS_BMAP_KEY_DADDR(bb,i,cur) \
- (XFS_BTREE_KEY_ADDR(xfs_bmbt, bb, i))
-
-#define XFS_BMAP_KEY_IADDR(bb,i,cur) \
- (XFS_BTREE_KEY_ADDR(xfs_bmbt, bb, i))
-
-#define XFS_BMAP_PTR_DADDR(bb,i,cur) \
- (XFS_BTREE_PTR_ADDR(xfs_bmbt, bb, i, XFS_BMAP_BLOCK_DMAXRECS( \
- be16_to_cpu((bb)->bb_level), cur)))
-#define XFS_BMAP_PTR_IADDR(bb,i,cur) \
- (XFS_BTREE_PTR_ADDR(xfs_bmbt, bb, i, XFS_BMAP_BLOCK_IMAXRECS( \
- be16_to_cpu((bb)->bb_level), cur)))
+/*
+ * Btree block header size depends on a superblock flag.
+ *
+ * (not quite yet, but soon)
+ */
+#define XFS_BMBT_BLOCK_LEN(mp) XFS_BTREE_LBLOCK_LEN
+
+#define XFS_BMBT_REC_ADDR(mp, block, index) \
+ ((xfs_bmbt_rec_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(mp) + \
+ ((index) - 1) * sizeof(xfs_bmbt_rec_t)))
+
+#define XFS_BMBT_KEY_ADDR(mp, block, index) \
+ ((xfs_bmbt_key_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(mp) + \
+ ((index) - 1) * sizeof(xfs_bmbt_key_t)))
+
+#define XFS_BMBT_PTR_ADDR(mp, block, index, maxrecs) \
+ ((xfs_bmbt_ptr_t *) \
+ ((char *)(block) + \
+ XFS_BMBT_BLOCK_LEN(mp) + \
+ (maxrecs) * sizeof(xfs_bmbt_key_t) + \
+ ((index) - 1) * sizeof(xfs_bmbt_ptr_t)))
+
+#define XFS_BMDR_REC_ADDR(block, index) \
+ ((xfs_bmdr_rec_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ ((index) - 1) * sizeof(xfs_bmdr_rec_t)))
+
+#define XFS_BMDR_KEY_ADDR(block, index) \
+ ((xfs_bmdr_key_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ ((index) - 1) * sizeof(xfs_bmdr_key_t)))
+
+#define XFS_BMDR_PTR_ADDR(block, index, maxrecs) \
+ ((xfs_bmdr_ptr_t *) \
+ ((char *)(block) + \
+ sizeof(struct xfs_bmdr_block) + \
+ (maxrecs) * sizeof(xfs_bmdr_key_t) + \
+ ((index) - 1) * sizeof(xfs_bmdr_ptr_t)))
/*
* These are to be used when we know the size of the block and
* we don't have a cursor.
*/
-#define XFS_BMAP_BROOT_REC_ADDR(bb,i,sz) \
- (XFS_BTREE_REC_ADDR(xfs_bmbt,bb,i))
-#define XFS_BMAP_BROOT_KEY_ADDR(bb,i,sz) \
- (XFS_BTREE_KEY_ADDR(xfs_bmbt,bb,i))
-#define XFS_BMAP_BROOT_PTR_ADDR(bb,i,sz) \
- (XFS_BTREE_PTR_ADDR(xfs_bmbt,bb,i,XFS_BMAP_BROOT_MAXRECS(sz)))
-
-#define XFS_BMAP_BROOT_NUMRECS(bb) be16_to_cpu((bb)->bb_numrecs)
-#define XFS_BMAP_BROOT_MAXRECS(sz) XFS_BTREE_BLOCK_MAXRECS(sz,xfs_bmbt,0)
+#define XFS_BMAP_BROOT_PTR_ADDR(mp, bb, i, sz) \
+ XFS_BMBT_PTR_ADDR(mp, bb, i, xfs_bmbt_maxrecs(mp, sz, 0))
#define XFS_BMAP_BROOT_SPACE_CALC(nrecs) \
- (int)(sizeof(xfs_bmbt_block_t) + \
+ (int)(XFS_BTREE_LBLOCK_LEN + \
((nrecs) * (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t))))
#define XFS_BMAP_BROOT_SPACE(bb) \
@@ -223,42 +213,12 @@ typedef struct xfs_btree_lblock xfs_bmbt_block_t;
*/
#define XFS_BM_MAXLEVELS(mp,w) ((mp)->m_bm_maxlevels[(w)])
-#define XFS_BMAP_SANITY_CHECK(mp,bb,level) \
- (be32_to_cpu((bb)->bb_magic) == XFS_BMAP_MAGIC && \
- be16_to_cpu((bb)->bb_level) == level && \
- be16_to_cpu((bb)->bb_numrecs) > 0 && \
- be16_to_cpu((bb)->bb_numrecs) <= (mp)->m_bmap_dmxr[(level) != 0])
-
-
-#ifdef __KERNEL__
-
-#if defined(XFS_BMBT_TRACE)
-/*
- * Trace buffer entry types.
- */
-#define XFS_BMBT_KTRACE_ARGBI 1
-#define XFS_BMBT_KTRACE_ARGBII 2
-#define XFS_BMBT_KTRACE_ARGFFFI 3
-#define XFS_BMBT_KTRACE_ARGI 4
-#define XFS_BMBT_KTRACE_ARGIFK 5
-#define XFS_BMBT_KTRACE_ARGIFR 6
-#define XFS_BMBT_KTRACE_ARGIK 7
-#define XFS_BMBT_KTRACE_CUR 8
-
-#define XFS_BMBT_TRACE_SIZE 4096 /* size of global trace buffer */
-#define XFS_BMBT_KTRACE_SIZE 32 /* size of per-inode trace buffer */
-extern ktrace_t *xfs_bmbt_trace_buf;
-#endif
-
/*
* Prototypes for xfs_bmap.c to call.
*/
-extern void xfs_bmdr_to_bmbt(xfs_bmdr_block_t *, int, xfs_bmbt_block_t *, int);
-extern int xfs_bmbt_decrement(struct xfs_btree_cur *, int, int *);
-extern int xfs_bmbt_delete(struct xfs_btree_cur *, int *);
+extern void xfs_bmdr_to_bmbt(struct xfs_mount *, xfs_bmdr_block_t *, int,
+ struct xfs_btree_block *, int);
extern void xfs_bmbt_get_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
-extern xfs_bmbt_block_t *xfs_bmbt_get_block(struct xfs_btree_cur *cur,
- int, struct xfs_buf **bpp);
extern xfs_filblks_t xfs_bmbt_get_blockcount(xfs_bmbt_rec_host_t *r);
extern xfs_fsblock_t xfs_bmbt_get_startblock(xfs_bmbt_rec_host_t *r);
extern xfs_fileoff_t xfs_bmbt_get_startoff(xfs_bmbt_rec_host_t *r);
@@ -268,22 +228,6 @@ extern void xfs_bmbt_disk_get_all(xfs_bmbt_rec_t *r, xfs_bmbt_irec_t *s);
extern xfs_filblks_t xfs_bmbt_disk_get_blockcount(xfs_bmbt_rec_t *r);
extern xfs_fileoff_t xfs_bmbt_disk_get_startoff(xfs_bmbt_rec_t *r);
-extern int xfs_bmbt_increment(struct xfs_btree_cur *, int, int *);
-extern int xfs_bmbt_insert(struct xfs_btree_cur *, int *);
-extern void xfs_bmbt_log_block(struct xfs_btree_cur *, struct xfs_buf *, int);
-extern void xfs_bmbt_log_recs(struct xfs_btree_cur *, struct xfs_buf *, int,
- int);
-extern int xfs_bmbt_lookup_eq(struct xfs_btree_cur *, xfs_fileoff_t,
- xfs_fsblock_t, xfs_filblks_t, int *);
-extern int xfs_bmbt_lookup_ge(struct xfs_btree_cur *, xfs_fileoff_t,
- xfs_fsblock_t, xfs_filblks_t, int *);
-
-/*
- * Give the bmap btree a new root block. Copy the old broot contents
- * down into a real block and make the broot point to it.
- */
-extern int xfs_bmbt_newroot(struct xfs_btree_cur *cur, int *lflags, int *stat);
-
extern void xfs_bmbt_set_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
extern void xfs_bmbt_set_allf(xfs_bmbt_rec_host_t *r, xfs_fileoff_t o,
xfs_fsblock_t b, xfs_filblks_t c, xfs_exntst_t v);
@@ -296,10 +240,15 @@ extern void xfs_bmbt_disk_set_all(xfs_bmbt_rec_t *r, xfs_bmbt_irec_t *s);
extern void xfs_bmbt_disk_set_allf(xfs_bmbt_rec_t *r, xfs_fileoff_t o,
xfs_fsblock_t b, xfs_filblks_t c, xfs_exntst_t v);
-extern void xfs_bmbt_to_bmdr(xfs_bmbt_block_t *, int, xfs_bmdr_block_t *, int);
-extern int xfs_bmbt_update(struct xfs_btree_cur *, xfs_fileoff_t,
- xfs_fsblock_t, xfs_filblks_t, xfs_exntst_t);
+extern void xfs_bmbt_to_bmdr(struct xfs_mount *, struct xfs_btree_block *, int,
+ xfs_bmdr_block_t *, int);
+
+extern int xfs_bmbt_get_maxrecs(struct xfs_btree_cur *, int level);
+extern int xfs_bmdr_maxrecs(struct xfs_mount *, int blocklen, int leaf);
+extern int xfs_bmbt_maxrecs(struct xfs_mount *, int blocklen, int leaf);
+
+extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
+ struct xfs_trans *, struct xfs_inode *, int);
-#endif /* __KERNEL__ */
#endif /* __XFS_BMAP_BTREE_H__ */
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
index cc593a84c345..7ed59267420d 100644
--- a/fs/xfs/xfs_btree.c
+++ b/fs/xfs/xfs_btree.c
@@ -34,7 +34,9 @@
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
+#include "xfs_inode_item.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_ialloc.h"
#include "xfs_error.h"
@@ -50,135 +52,33 @@ const __uint32_t xfs_magics[XFS_BTNUM_MAX] = {
XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, XFS_BMAP_MAGIC, XFS_IBT_MAGIC
};
-/*
- * Checking routine: return maxrecs for the block.
- */
-STATIC int /* number of records fitting in block */
-xfs_btree_maxrecs(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_block_t *block) /* generic btree block pointer */
-{
- switch (cur->bc_btnum) {
- case XFS_BTNUM_BNO:
- case XFS_BTNUM_CNT:
- return (int)XFS_ALLOC_BLOCK_MAXRECS(
- be16_to_cpu(block->bb_h.bb_level), cur);
- case XFS_BTNUM_BMAP:
- return (int)XFS_BMAP_BLOCK_IMAXRECS(
- be16_to_cpu(block->bb_h.bb_level), cur);
- case XFS_BTNUM_INO:
- return (int)XFS_INOBT_BLOCK_MAXRECS(
- be16_to_cpu(block->bb_h.bb_level), cur);
- default:
- ASSERT(0);
- return 0;
- }
-}
-
-/*
- * External routines.
- */
-
-#ifdef DEBUG
-/*
- * Debug routine: check that block header is ok.
- */
-void
-xfs_btree_check_block(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_block_t *block, /* generic btree block pointer */
- int level, /* level of the btree block */
- xfs_buf_t *bp) /* buffer containing block, if any */
-{
- if (XFS_BTREE_LONG_PTRS(cur->bc_btnum))
- xfs_btree_check_lblock(cur, (xfs_btree_lblock_t *)block, level,
- bp);
- else
- xfs_btree_check_sblock(cur, (xfs_btree_sblock_t *)block, level,
- bp);
-}
-
-/*
- * Debug routine: check that keys are in the right order.
- */
-void
-xfs_btree_check_key(
- xfs_btnum_t btnum, /* btree identifier */
- void *ak1, /* pointer to left (lower) key */
- void *ak2) /* pointer to right (higher) key */
-{
- switch (btnum) {
- case XFS_BTNUM_BNO: {
- xfs_alloc_key_t *k1;
- xfs_alloc_key_t *k2;
-
- k1 = ak1;
- k2 = ak2;
- ASSERT(be32_to_cpu(k1->ar_startblock) < be32_to_cpu(k2->ar_startblock));
- break;
- }
- case XFS_BTNUM_CNT: {
- xfs_alloc_key_t *k1;
- xfs_alloc_key_t *k2;
-
- k1 = ak1;
- k2 = ak2;
- ASSERT(be32_to_cpu(k1->ar_blockcount) < be32_to_cpu(k2->ar_blockcount) ||
- (k1->ar_blockcount == k2->ar_blockcount &&
- be32_to_cpu(k1->ar_startblock) < be32_to_cpu(k2->ar_startblock)));
- break;
- }
- case XFS_BTNUM_BMAP: {
- xfs_bmbt_key_t *k1;
- xfs_bmbt_key_t *k2;
-
- k1 = ak1;
- k2 = ak2;
- ASSERT(be64_to_cpu(k1->br_startoff) < be64_to_cpu(k2->br_startoff));
- break;
- }
- case XFS_BTNUM_INO: {
- xfs_inobt_key_t *k1;
- xfs_inobt_key_t *k2;
-
- k1 = ak1;
- k2 = ak2;
- ASSERT(be32_to_cpu(k1->ir_startino) < be32_to_cpu(k2->ir_startino));
- break;
- }
- default:
- ASSERT(0);
- }
-}
-#endif /* DEBUG */
-/*
- * Checking routine: check that long form block header is ok.
- */
-/* ARGSUSED */
-int /* error (0 or EFSCORRUPTED) */
+STATIC int /* error (0 or EFSCORRUPTED) */
xfs_btree_check_lblock(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_lblock_t *block, /* btree long form block pointer */
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_btree_block *block, /* btree long form block pointer */
int level, /* level of the btree block */
- xfs_buf_t *bp) /* buffer for block, if any */
+ struct xfs_buf *bp) /* buffer for block, if any */
{
int lblock_ok; /* block passes checks */
- xfs_mount_t *mp; /* file system mount point */
+ struct xfs_mount *mp; /* file system mount point */
mp = cur->bc_mp;
lblock_ok =
be32_to_cpu(block->bb_magic) == xfs_magics[cur->bc_btnum] &&
be16_to_cpu(block->bb_level) == level &&
be16_to_cpu(block->bb_numrecs) <=
- xfs_btree_maxrecs(cur, (xfs_btree_block_t *)block) &&
- block->bb_leftsib &&
- (be64_to_cpu(block->bb_leftsib) == NULLDFSBNO ||
- XFS_FSB_SANITY_CHECK(mp, be64_to_cpu(block->bb_leftsib))) &&
- block->bb_rightsib &&
- (be64_to_cpu(block->bb_rightsib) == NULLDFSBNO ||
- XFS_FSB_SANITY_CHECK(mp, be64_to_cpu(block->bb_rightsib)));
- if (unlikely(XFS_TEST_ERROR(!lblock_ok, mp, XFS_ERRTAG_BTREE_CHECK_LBLOCK,
+ cur->bc_ops->get_maxrecs(cur, level) &&
+ block->bb_u.l.bb_leftsib &&
+ (be64_to_cpu(block->bb_u.l.bb_leftsib) == NULLDFSBNO ||
+ XFS_FSB_SANITY_CHECK(mp,
+ be64_to_cpu(block->bb_u.l.bb_leftsib))) &&
+ block->bb_u.l.bb_rightsib &&
+ (be64_to_cpu(block->bb_u.l.bb_rightsib) == NULLDFSBNO ||
+ XFS_FSB_SANITY_CHECK(mp,
+ be64_to_cpu(block->bb_u.l.bb_rightsib)));
+ if (unlikely(XFS_TEST_ERROR(!lblock_ok, mp,
+ XFS_ERRTAG_BTREE_CHECK_LBLOCK,
XFS_RANDOM_BTREE_CHECK_LBLOCK))) {
if (bp)
xfs_buftrace("LBTREE ERROR", bp);
@@ -189,98 +89,15 @@ xfs_btree_check_lblock(
return 0;
}
-/*
- * Checking routine: check that (long) pointer is ok.
- */
-int /* error (0 or EFSCORRUPTED) */
-xfs_btree_check_lptr(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_dfsbno_t ptr, /* btree block disk address */
- int level) /* btree block level */
-{
- xfs_mount_t *mp; /* file system mount point */
-
- mp = cur->bc_mp;
- XFS_WANT_CORRUPTED_RETURN(
- level > 0 &&
- ptr != NULLDFSBNO &&
- XFS_FSB_SANITY_CHECK(mp, ptr));
- return 0;
-}
-
-#ifdef DEBUG
-/*
- * Debug routine: check that records are in the right order.
- */
-void
-xfs_btree_check_rec(
- xfs_btnum_t btnum, /* btree identifier */
- void *ar1, /* pointer to left (lower) record */
- void *ar2) /* pointer to right (higher) record */
-{
- switch (btnum) {
- case XFS_BTNUM_BNO: {
- xfs_alloc_rec_t *r1;
- xfs_alloc_rec_t *r2;
-
- r1 = ar1;
- r2 = ar2;
- ASSERT(be32_to_cpu(r1->ar_startblock) +
- be32_to_cpu(r1->ar_blockcount) <=
- be32_to_cpu(r2->ar_startblock));
- break;
- }
- case XFS_BTNUM_CNT: {
- xfs_alloc_rec_t *r1;
- xfs_alloc_rec_t *r2;
-
- r1 = ar1;
- r2 = ar2;
- ASSERT(be32_to_cpu(r1->ar_blockcount) < be32_to_cpu(r2->ar_blockcount) ||
- (r1->ar_blockcount == r2->ar_blockcount &&
- be32_to_cpu(r1->ar_startblock) < be32_to_cpu(r2->ar_startblock)));
- break;
- }
- case XFS_BTNUM_BMAP: {
- xfs_bmbt_rec_t *r1;
- xfs_bmbt_rec_t *r2;
-
- r1 = ar1;
- r2 = ar2;
- ASSERT(xfs_bmbt_disk_get_startoff(r1) +
- xfs_bmbt_disk_get_blockcount(r1) <=
- xfs_bmbt_disk_get_startoff(r2));
- break;
- }
- case XFS_BTNUM_INO: {
- xfs_inobt_rec_t *r1;
- xfs_inobt_rec_t *r2;
-
- r1 = ar1;
- r2 = ar2;
- ASSERT(be32_to_cpu(r1->ir_startino) + XFS_INODES_PER_CHUNK <=
- be32_to_cpu(r2->ir_startino));
- break;
- }
- default:
- ASSERT(0);
- }
-}
-#endif /* DEBUG */
-
-/*
- * Checking routine: check that block header is ok.
- */
-/* ARGSUSED */
-int /* error (0 or EFSCORRUPTED) */
+STATIC int /* error (0 or EFSCORRUPTED) */
xfs_btree_check_sblock(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_sblock_t *block, /* btree short form block pointer */
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_btree_block *block, /* btree short form block pointer */
int level, /* level of the btree block */
- xfs_buf_t *bp) /* buffer containing block */
+ struct xfs_buf *bp) /* buffer containing block */
{
- xfs_buf_t *agbp; /* buffer for ag. freespace struct */
- xfs_agf_t *agf; /* ag. freespace structure */
+ struct xfs_buf *agbp; /* buffer for ag. freespace struct */
+ struct xfs_agf *agf; /* ag. freespace structure */
xfs_agblock_t agflen; /* native ag. freespace length */
int sblock_ok; /* block passes checks */
@@ -291,13 +108,13 @@ xfs_btree_check_sblock(
be32_to_cpu(block->bb_magic) == xfs_magics[cur->bc_btnum] &&
be16_to_cpu(block->bb_level) == level &&
be16_to_cpu(block->bb_numrecs) <=
- xfs_btree_maxrecs(cur, (xfs_btree_block_t *)block) &&
- (be32_to_cpu(block->bb_leftsib) == NULLAGBLOCK ||
- be32_to_cpu(block->bb_leftsib) < agflen) &&
- block->bb_leftsib &&
- (be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK ||
- be32_to_cpu(block->bb_rightsib) < agflen) &&
- block->bb_rightsib;
+ cur->bc_ops->get_maxrecs(cur, level) &&
+ (be32_to_cpu(block->bb_u.s.bb_leftsib) == NULLAGBLOCK ||
+ be32_to_cpu(block->bb_u.s.bb_leftsib) < agflen) &&
+ block->bb_u.s.bb_leftsib &&
+ (be32_to_cpu(block->bb_u.s.bb_rightsib) == NULLAGBLOCK ||
+ be32_to_cpu(block->bb_u.s.bb_rightsib) < agflen) &&
+ block->bb_u.s.bb_rightsib;
if (unlikely(XFS_TEST_ERROR(!sblock_ok, cur->bc_mp,
XFS_ERRTAG_BTREE_CHECK_SBLOCK,
XFS_RANDOM_BTREE_CHECK_SBLOCK))) {
@@ -311,27 +128,78 @@ xfs_btree_check_sblock(
}
/*
- * Checking routine: check that (short) pointer is ok.
+ * Debug routine: check that block header is ok.
+ */
+int
+xfs_btree_check_block(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_btree_block *block, /* generic btree block pointer */
+ int level, /* level of the btree block */
+ struct xfs_buf *bp) /* buffer containing block, if any */
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ return xfs_btree_check_lblock(cur, block, level, bp);
+ else
+ return xfs_btree_check_sblock(cur, block, level, bp);
+}
+
+/*
+ * Check that (long) pointer is ok.
*/
int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_lptr(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_dfsbno_t bno, /* btree block disk address */
+ int level) /* btree block level */
+{
+ XFS_WANT_CORRUPTED_RETURN(
+ level > 0 &&
+ bno != NULLDFSBNO &&
+ XFS_FSB_SANITY_CHECK(cur->bc_mp, bno));
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check that (short) pointer is ok.
+ */
+STATIC int /* error (0 or EFSCORRUPTED) */
xfs_btree_check_sptr(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t ptr, /* btree block disk address */
- int level) /* btree block level */
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* btree block disk address */
+ int level) /* btree block level */
{
- xfs_buf_t *agbp; /* buffer for ag. freespace struct */
- xfs_agf_t *agf; /* ag. freespace structure */
+ xfs_agblock_t agblocks = cur->bc_mp->m_sb.sb_agblocks;
- agbp = cur->bc_private.a.agbp;
- agf = XFS_BUF_TO_AGF(agbp);
XFS_WANT_CORRUPTED_RETURN(
level > 0 &&
- ptr != NULLAGBLOCK && ptr != 0 &&
- ptr < be32_to_cpu(agf->agf_length));
+ bno != NULLAGBLOCK &&
+ bno != 0 &&
+ bno < agblocks);
return 0;
}
/*
+ * Check that block ptr is ok.
+ */
+STATIC int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_ptr(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ union xfs_btree_ptr *ptr, /* btree block disk address */
+ int index, /* offset from ptr to check */
+ int level) /* btree block level */
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ return xfs_btree_check_lptr(cur,
+ be64_to_cpu((&ptr->l)[index]), level);
+ } else {
+ return xfs_btree_check_sptr(cur,
+ be32_to_cpu((&ptr->s)[index]), level);
+ }
+}
+#endif
+
+/*
* Delete the btree cursor.
*/
void
@@ -387,16 +255,17 @@ xfs_btree_dup_cursor(
tp = cur->bc_tp;
mp = cur->bc_mp;
+
/*
* Allocate a new cursor like the old one.
*/
- new = xfs_btree_init_cursor(mp, tp, cur->bc_private.a.agbp,
- cur->bc_private.a.agno, cur->bc_btnum, cur->bc_private.b.ip,
- cur->bc_private.b.whichfork);
+ new = cur->bc_ops->dup_cursor(cur);
+
/*
* Copy the record currently in the cursor.
*/
new->bc_rec = cur->bc_rec;
+
/*
* For each level current, re-get the buffer and copy the ptr value.
*/
@@ -416,46 +285,174 @@ xfs_btree_dup_cursor(
} else
new->bc_bufs[i] = NULL;
}
- /*
- * For bmap btrees, copy the firstblock, flist, and flags values,
- * since init cursor doesn't get them.
- */
- if (new->bc_btnum == XFS_BTNUM_BMAP) {
- new->bc_private.b.firstblock = cur->bc_private.b.firstblock;
- new->bc_private.b.flist = cur->bc_private.b.flist;
- new->bc_private.b.flags = cur->bc_private.b.flags;
- }
*ncur = new;
return 0;
}
/*
+ * XFS btree block layout and addressing:
+ *
+ * There are two types of blocks in the btree: leaf and non-leaf blocks.
+ *
+ * The leaf record start with a header then followed by records containing
+ * the values. A non-leaf block also starts with the same header, and
+ * then first contains lookup keys followed by an equal number of pointers
+ * to the btree blocks at the previous level.
+ *
+ * +--------+-------+-------+-------+-------+-------+-------+
+ * Leaf: | header | rec 1 | rec 2 | rec 3 | rec 4 | rec 5 | rec N |
+ * +--------+-------+-------+-------+-------+-------+-------+
+ *
+ * +--------+-------+-------+-------+-------+-------+-------+
+ * Non-Leaf: | header | key 1 | key 2 | key N | ptr 1 | ptr 2 | ptr N |
+ * +--------+-------+-------+-------+-------+-------+-------+
+ *
+ * The header is called struct xfs_btree_block for reasons better left unknown
+ * and comes in different versions for short (32bit) and long (64bit) block
+ * pointers. The record and key structures are defined by the btree instances
+ * and opaque to the btree core. The block pointers are simple disk endian
+ * integers, available in a short (32bit) and long (64bit) variant.
+ *
+ * The helpers below calculate the offset of a given record, key or pointer
+ * into a btree block (xfs_btree_*_offset) or return a pointer to the given
+ * record, key or pointer (xfs_btree_*_addr). Note that all addressing
+ * inside the btree block is done using indices starting at one, not zero!
+ */
+
+/*
+ * Return size of the btree block header for this btree instance.
+ */
+static inline size_t xfs_btree_block_len(struct xfs_btree_cur *cur)
+{
+ return (cur->bc_flags & XFS_BTREE_LONG_PTRS) ?
+ XFS_BTREE_LBLOCK_LEN :
+ XFS_BTREE_SBLOCK_LEN;
+}
+
+/*
+ * Return size of btree block pointers for this btree instance.
+ */
+static inline size_t xfs_btree_ptr_len(struct xfs_btree_cur *cur)
+{
+ return (cur->bc_flags & XFS_BTREE_LONG_PTRS) ?
+ sizeof(__be64) : sizeof(__be32);
+}
+
+/*
+ * Calculate offset of the n-th record in a btree block.
+ */
+STATIC size_t
+xfs_btree_rec_offset(
+ struct xfs_btree_cur *cur,
+ int n)
+{
+ return xfs_btree_block_len(cur) +
+ (n - 1) * cur->bc_ops->rec_len;
+}
+
+/*
+ * Calculate offset of the n-th key in a btree block.
+ */
+STATIC size_t
+xfs_btree_key_offset(
+ struct xfs_btree_cur *cur,
+ int n)
+{
+ return xfs_btree_block_len(cur) +
+ (n - 1) * cur->bc_ops->key_len;
+}
+
+/*
+ * Calculate offset of the n-th block pointer in a btree block.
+ */
+STATIC size_t
+xfs_btree_ptr_offset(
+ struct xfs_btree_cur *cur,
+ int n,
+ int level)
+{
+ return xfs_btree_block_len(cur) +
+ cur->bc_ops->get_maxrecs(cur, level) * cur->bc_ops->key_len +
+ (n - 1) * xfs_btree_ptr_len(cur);
+}
+
+/*
+ * Return a pointer to the n-th record in the btree block.
+ */
+STATIC union xfs_btree_rec *
+xfs_btree_rec_addr(
+ struct xfs_btree_cur *cur,
+ int n,
+ struct xfs_btree_block *block)
+{
+ return (union xfs_btree_rec *)
+ ((char *)block + xfs_btree_rec_offset(cur, n));
+}
+
+/*
+ * Return a pointer to the n-th key in the btree block.
+ */
+STATIC union xfs_btree_key *
+xfs_btree_key_addr(
+ struct xfs_btree_cur *cur,
+ int n,
+ struct xfs_btree_block *block)
+{
+ return (union xfs_btree_key *)
+ ((char *)block + xfs_btree_key_offset(cur, n));
+}
+
+/*
+ * Return a pointer to the n-th block pointer in the btree block.
+ */
+STATIC union xfs_btree_ptr *
+xfs_btree_ptr_addr(
+ struct xfs_btree_cur *cur,
+ int n,
+ struct xfs_btree_block *block)
+{
+ int level = xfs_btree_get_level(block);
+
+ ASSERT(block->bb_level != 0);
+
+ return (union xfs_btree_ptr *)
+ ((char *)block + xfs_btree_ptr_offset(cur, n, level));
+}
+
+/*
+ * Get a the root block which is stored in the inode.
+ *
+ * For now this btree implementation assumes the btree root is always
+ * stored in the if_broot field of an inode fork.
+ */
+STATIC struct xfs_btree_block *
+xfs_btree_get_iroot(
+ struct xfs_btree_cur *cur)
+{
+ struct xfs_ifork *ifp;
+
+ ifp = XFS_IFORK_PTR(cur->bc_private.b.ip, cur->bc_private.b.whichfork);
+ return (struct xfs_btree_block *)ifp->if_broot;
+}
+
+/*
* Retrieve the block pointer from the cursor at the given level.
- * This may be a bmap btree root or from a buffer.
+ * This may be an inode btree root or from a buffer.
*/
-STATIC xfs_btree_block_t * /* generic btree block pointer */
+STATIC struct xfs_btree_block * /* generic btree block pointer */
xfs_btree_get_block(
- xfs_btree_cur_t *cur, /* btree cursor */
+ struct xfs_btree_cur *cur, /* btree cursor */
int level, /* level in btree */
- xfs_buf_t **bpp) /* buffer containing the block */
-{
- xfs_btree_block_t *block; /* return value */
- xfs_buf_t *bp; /* return buffer */
- xfs_ifork_t *ifp; /* inode fork pointer */
- int whichfork; /* data or attr fork */
-
- if (cur->bc_btnum == XFS_BTNUM_BMAP && level == cur->bc_nlevels - 1) {
- whichfork = cur->bc_private.b.whichfork;
- ifp = XFS_IFORK_PTR(cur->bc_private.b.ip, whichfork);
- block = (xfs_btree_block_t *)ifp->if_broot;
- bp = NULL;
- } else {
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_BLOCK(bp);
+ struct xfs_buf **bpp) /* buffer containing the block */
+{
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ (level == cur->bc_nlevels - 1)) {
+ *bpp = NULL;
+ return xfs_btree_get_iroot(cur);
}
- ASSERT(block != NULL);
- *bpp = bp;
- return block;
+
+ *bpp = cur->bc_bufs[level];
+ return XFS_BUF_TO_BLOCK(*bpp);
}
/*
@@ -505,97 +502,6 @@ xfs_btree_get_bufs(
}
/*
- * Allocate a new btree cursor.
- * The cursor is either for allocation (A) or bmap (B) or inodes (I).
- */
-xfs_btree_cur_t * /* new btree cursor */
-xfs_btree_init_cursor(
- xfs_mount_t *mp, /* file system mount point */
- xfs_trans_t *tp, /* transaction pointer */
- xfs_buf_t *agbp, /* (A only) buffer for agf structure */
- /* (I only) buffer for agi structure */
- xfs_agnumber_t agno, /* (AI only) allocation group number */
- xfs_btnum_t btnum, /* btree identifier */
- xfs_inode_t *ip, /* (B only) inode owning the btree */
- int whichfork) /* (B only) data or attr fork */
-{
- xfs_agf_t *agf; /* (A) allocation group freespace */
- xfs_agi_t *agi; /* (I) allocation group inodespace */
- xfs_btree_cur_t *cur; /* return value */
- xfs_ifork_t *ifp; /* (I) inode fork pointer */
- int nlevels=0; /* number of levels in the btree */
-
- ASSERT(xfs_btree_cur_zone != NULL);
- /*
- * Allocate a new cursor.
- */
- cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
- /*
- * Deduce the number of btree levels from the arguments.
- */
- switch (btnum) {
- case XFS_BTNUM_BNO:
- case XFS_BTNUM_CNT:
- agf = XFS_BUF_TO_AGF(agbp);
- nlevels = be32_to_cpu(agf->agf_levels[btnum]);
- break;
- case XFS_BTNUM_BMAP:
- ifp = XFS_IFORK_PTR(ip, whichfork);
- nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
- break;
- case XFS_BTNUM_INO:
- agi = XFS_BUF_TO_AGI(agbp);
- nlevels = be32_to_cpu(agi->agi_level);
- break;
- default:
- ASSERT(0);
- }
- /*
- * Fill in the common fields.
- */
- cur->bc_tp = tp;
- cur->bc_mp = mp;
- cur->bc_nlevels = nlevels;
- cur->bc_btnum = btnum;
- cur->bc_blocklog = mp->m_sb.sb_blocklog;
- /*
- * Fill in private fields.
- */
- switch (btnum) {
- case XFS_BTNUM_BNO:
- case XFS_BTNUM_CNT:
- /*
- * Allocation btree fields.
- */
- cur->bc_private.a.agbp = agbp;
- cur->bc_private.a.agno = agno;
- break;
- case XFS_BTNUM_INO:
- /*
- * Inode allocation btree fields.
- */
- cur->bc_private.a.agbp = agbp;
- cur->bc_private.a.agno = agno;
- break;
- case XFS_BTNUM_BMAP:
- /*
- * Bmap btree fields.
- */
- cur->bc_private.b.forksize = XFS_IFORK_SIZE(ip, whichfork);
- cur->bc_private.b.ip = ip;
- cur->bc_private.b.firstblock = NULLFSBLOCK;
- cur->bc_private.b.flist = NULL;
- cur->bc_private.b.allocated = 0;
- cur->bc_private.b.flags = 0;
- cur->bc_private.b.whichfork = whichfork;
- break;
- default:
- ASSERT(0);
- }
- return cur;
-}
-
-/*
* Check for the cursor referring to the last block at the given level.
*/
int /* 1=is last block, 0=not last block */
@@ -603,12 +509,12 @@ xfs_btree_islastblock(
xfs_btree_cur_t *cur, /* btree cursor */
int level) /* level to check */
{
- xfs_btree_block_t *block; /* generic btree block pointer */
+ struct xfs_btree_block *block; /* generic btree block pointer */
xfs_buf_t *bp; /* buffer containing block */
block = xfs_btree_get_block(cur, level, &bp);
xfs_btree_check_block(cur, block, level, bp);
- if (XFS_BTREE_LONG_PTRS(cur->bc_btnum))
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
return be64_to_cpu(block->bb_u.l.bb_rightsib) == NULLDFSBNO;
else
return be32_to_cpu(block->bb_u.s.bb_rightsib) == NULLAGBLOCK;
@@ -618,12 +524,12 @@ xfs_btree_islastblock(
* Change the cursor to point to the first record at the given level.
* Other levels are unaffected.
*/
-int /* success=1, failure=0 */
+STATIC int /* success=1, failure=0 */
xfs_btree_firstrec(
xfs_btree_cur_t *cur, /* btree cursor */
int level) /* level to change */
{
- xfs_btree_block_t *block; /* generic btree block pointer */
+ struct xfs_btree_block *block; /* generic btree block pointer */
xfs_buf_t *bp; /* buffer containing block */
/*
@@ -634,7 +540,7 @@ xfs_btree_firstrec(
/*
* It's empty, there is no such record.
*/
- if (!block->bb_h.bb_numrecs)
+ if (!block->bb_numrecs)
return 0;
/*
* Set the ptr value to 1, that's the first record/key.
@@ -647,12 +553,12 @@ xfs_btree_firstrec(
* Change the cursor to point to the last record in the current block
* at the given level. Other levels are unaffected.
*/
-int /* success=1, failure=0 */
+STATIC int /* success=1, failure=0 */
xfs_btree_lastrec(
xfs_btree_cur_t *cur, /* btree cursor */
int level) /* level to change */
{
- xfs_btree_block_t *block; /* generic btree block pointer */
+ struct xfs_btree_block *block; /* generic btree block pointer */
xfs_buf_t *bp; /* buffer containing block */
/*
@@ -663,12 +569,12 @@ xfs_btree_lastrec(
/*
* It's empty, there is no such record.
*/
- if (!block->bb_h.bb_numrecs)
+ if (!block->bb_numrecs)
return 0;
/*
* Set the ptr value to numrecs, that's the last record/key.
*/
- cur->bc_ptrs[level] = be16_to_cpu(block->bb_h.bb_numrecs);
+ cur->bc_ptrs[level] = be16_to_cpu(block->bb_numrecs);
return 1;
}
@@ -817,66 +723,84 @@ xfs_btree_reada_bufs(
xfs_baread(mp->m_ddev_targp, d, mp->m_bsize * count);
}
+STATIC int
+xfs_btree_readahead_lblock(
+ struct xfs_btree_cur *cur,
+ int lr,
+ struct xfs_btree_block *block)
+{
+ int rval = 0;
+ xfs_fsblock_t left = be64_to_cpu(block->bb_u.l.bb_leftsib);
+ xfs_fsblock_t right = be64_to_cpu(block->bb_u.l.bb_rightsib);
+
+ if ((lr & XFS_BTCUR_LEFTRA) && left != NULLDFSBNO) {
+ xfs_btree_reada_bufl(cur->bc_mp, left, 1);
+ rval++;
+ }
+
+ if ((lr & XFS_BTCUR_RIGHTRA) && right != NULLDFSBNO) {
+ xfs_btree_reada_bufl(cur->bc_mp, right, 1);
+ rval++;
+ }
+
+ return rval;
+}
+
+STATIC int
+xfs_btree_readahead_sblock(
+ struct xfs_btree_cur *cur,
+ int lr,
+ struct xfs_btree_block *block)
+{
+ int rval = 0;
+ xfs_agblock_t left = be32_to_cpu(block->bb_u.s.bb_leftsib);
+ xfs_agblock_t right = be32_to_cpu(block->bb_u.s.bb_rightsib);
+
+
+ if ((lr & XFS_BTCUR_LEFTRA) && left != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
+ left, 1);
+ rval++;
+ }
+
+ if ((lr & XFS_BTCUR_RIGHTRA) && right != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
+ right, 1);
+ rval++;
+ }
+
+ return rval;
+}
+
/*
* Read-ahead btree blocks, at the given level.
* Bits in lr are set from XFS_BTCUR_{LEFT,RIGHT}RA.
*/
-int
-xfs_btree_readahead_core(
- xfs_btree_cur_t *cur, /* btree cursor */
+STATIC int
+xfs_btree_readahead(
+ struct xfs_btree_cur *cur, /* btree cursor */
int lev, /* level in btree */
int lr) /* left/right bits */
{
- xfs_alloc_block_t *a;
- xfs_bmbt_block_t *b;
- xfs_inobt_block_t *i;
- int rval = 0;
+ struct xfs_btree_block *block;
+
+ /*
+ * No readahead needed if we are at the root level and the
+ * btree root is stored in the inode.
+ */
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ (lev == cur->bc_nlevels - 1))
+ return 0;
+
+ if ((cur->bc_ra[lev] | lr) == cur->bc_ra[lev])
+ return 0;
- ASSERT(cur->bc_bufs[lev] != NULL);
cur->bc_ra[lev] |= lr;
- switch (cur->bc_btnum) {
- case XFS_BTNUM_BNO:
- case XFS_BTNUM_CNT:
- a = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[lev]);
- if ((lr & XFS_BTCUR_LEFTRA) && be32_to_cpu(a->bb_leftsib) != NULLAGBLOCK) {
- xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
- be32_to_cpu(a->bb_leftsib), 1);
- rval++;
- }
- if ((lr & XFS_BTCUR_RIGHTRA) && be32_to_cpu(a->bb_rightsib) != NULLAGBLOCK) {
- xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
- be32_to_cpu(a->bb_rightsib), 1);
- rval++;
- }
- break;
- case XFS_BTNUM_BMAP:
- b = XFS_BUF_TO_BMBT_BLOCK(cur->bc_bufs[lev]);
- if ((lr & XFS_BTCUR_LEFTRA) && be64_to_cpu(b->bb_leftsib) != NULLDFSBNO) {
- xfs_btree_reada_bufl(cur->bc_mp, be64_to_cpu(b->bb_leftsib), 1);
- rval++;
- }
- if ((lr & XFS_BTCUR_RIGHTRA) && be64_to_cpu(b->bb_rightsib) != NULLDFSBNO) {
- xfs_btree_reada_bufl(cur->bc_mp, be64_to_cpu(b->bb_rightsib), 1);
- rval++;
- }
- break;
- case XFS_BTNUM_INO:
- i = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[lev]);
- if ((lr & XFS_BTCUR_LEFTRA) && be32_to_cpu(i->bb_leftsib) != NULLAGBLOCK) {
- xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
- be32_to_cpu(i->bb_leftsib), 1);
- rval++;
- }
- if ((lr & XFS_BTCUR_RIGHTRA) && be32_to_cpu(i->bb_rightsib) != NULLAGBLOCK) {
- xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
- be32_to_cpu(i->bb_rightsib), 1);
- rval++;
- }
- break;
- default:
- ASSERT(0);
- }
- return rval;
+ block = XFS_BUF_TO_BLOCK(cur->bc_bufs[lev]);
+
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ return xfs_btree_readahead_lblock(cur, lr, block);
+ return xfs_btree_readahead_sblock(cur, lr, block);
}
/*
@@ -889,7 +813,7 @@ xfs_btree_setbuf(
int lev, /* level in btree */
xfs_buf_t *bp) /* new buffer to set */
{
- xfs_btree_block_t *b; /* btree block */
+ struct xfs_btree_block *b; /* btree block */
xfs_buf_t *obp; /* old buffer pointer */
obp = cur->bc_bufs[lev];
@@ -900,7 +824,7 @@ xfs_btree_setbuf(
if (!bp)
return;
b = XFS_BUF_TO_BLOCK(bp);
- if (XFS_BTREE_LONG_PTRS(cur->bc_btnum)) {
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
if (be64_to_cpu(b->bb_u.l.bb_leftsib) == NULLDFSBNO)
cur->bc_ra[lev] |= XFS_BTCUR_LEFTRA;
if (be64_to_cpu(b->bb_u.l.bb_rightsib) == NULLDFSBNO)
@@ -912,3 +836,2855 @@ xfs_btree_setbuf(
cur->bc_ra[lev] |= XFS_BTCUR_RIGHTRA;
}
}
+
+STATIC int
+xfs_btree_ptr_is_null(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ return be64_to_cpu(ptr->l) == NULLFSBLOCK;
+ else
+ return be32_to_cpu(ptr->s) == NULLAGBLOCK;
+}
+
+STATIC void
+xfs_btree_set_ptr_null(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ ptr->l = cpu_to_be64(NULLFSBLOCK);
+ else
+ ptr->s = cpu_to_be32(NULLAGBLOCK);
+}
+
+/*
+ * Get/set/init sibling pointers
+ */
+STATIC void
+xfs_btree_get_sibling(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ union xfs_btree_ptr *ptr,
+ int lr)
+{
+ ASSERT(lr == XFS_BB_LEFTSIB || lr == XFS_BB_RIGHTSIB);
+
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ if (lr == XFS_BB_RIGHTSIB)
+ ptr->l = block->bb_u.l.bb_rightsib;
+ else
+ ptr->l = block->bb_u.l.bb_leftsib;
+ } else {
+ if (lr == XFS_BB_RIGHTSIB)
+ ptr->s = block->bb_u.s.bb_rightsib;
+ else
+ ptr->s = block->bb_u.s.bb_leftsib;
+ }
+}
+
+STATIC void
+xfs_btree_set_sibling(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ union xfs_btree_ptr *ptr,
+ int lr)
+{
+ ASSERT(lr == XFS_BB_LEFTSIB || lr == XFS_BB_RIGHTSIB);
+
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ if (lr == XFS_BB_RIGHTSIB)
+ block->bb_u.l.bb_rightsib = ptr->l;
+ else
+ block->bb_u.l.bb_leftsib = ptr->l;
+ } else {
+ if (lr == XFS_BB_RIGHTSIB)
+ block->bb_u.s.bb_rightsib = ptr->s;
+ else
+ block->bb_u.s.bb_leftsib = ptr->s;
+ }
+}
+
+STATIC void
+xfs_btree_init_block(
+ struct xfs_btree_cur *cur,
+ int level,
+ int numrecs,
+ struct xfs_btree_block *new) /* new block */
+{
+ new->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
+ new->bb_level = cpu_to_be16(level);
+ new->bb_numrecs = cpu_to_be16(numrecs);
+
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ new->bb_u.l.bb_leftsib = cpu_to_be64(NULLFSBLOCK);
+ new->bb_u.l.bb_rightsib = cpu_to_be64(NULLFSBLOCK);
+ } else {
+ new->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
+ new->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
+ }
+}
+
+/*
+ * Return true if ptr is the last record in the btree and
+ * we need to track updateѕ to this record. The decision
+ * will be further refined in the update_lastrec method.
+ */
+STATIC int
+xfs_btree_is_lastrec(
+ struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ int level)
+{
+ union xfs_btree_ptr ptr;
+
+ if (level > 0)
+ return 0;
+ if (!(cur->bc_flags & XFS_BTREE_LASTREC_UPDATE))
+ return 0;
+
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
+ if (!xfs_btree_ptr_is_null(cur, &ptr))
+ return 0;
+ return 1;
+}
+
+STATIC void
+xfs_btree_buf_to_ptr(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ union xfs_btree_ptr *ptr)
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ ptr->l = cpu_to_be64(XFS_DADDR_TO_FSB(cur->bc_mp,
+ XFS_BUF_ADDR(bp)));
+ else {
+ ptr->s = cpu_to_be32(XFS_DADDR_TO_AGBNO(cur->bc_mp,
+ XFS_BUF_ADDR(bp)));
+ }
+}
+
+STATIC xfs_daddr_t
+xfs_btree_ptr_to_daddr(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ ASSERT(be64_to_cpu(ptr->l) != NULLFSBLOCK);
+
+ return XFS_FSB_TO_DADDR(cur->bc_mp, be64_to_cpu(ptr->l));
+ } else {
+ ASSERT(cur->bc_private.a.agno != NULLAGNUMBER);
+ ASSERT(be32_to_cpu(ptr->s) != NULLAGBLOCK);
+
+ return XFS_AGB_TO_DADDR(cur->bc_mp, cur->bc_private.a.agno,
+ be32_to_cpu(ptr->s));
+ }
+}
+
+STATIC void
+xfs_btree_set_refs(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp)
+{
+ switch (cur->bc_btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_MAP, XFS_ALLOC_BTREE_REF);
+ break;
+ case XFS_BTNUM_INO:
+ XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_INOMAP, XFS_INO_BTREE_REF);
+ break;
+ case XFS_BTNUM_BMAP:
+ XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_MAP, XFS_BMAP_BTREE_REF);
+ break;
+ default:
+ ASSERT(0);
+ }
+}
+
+STATIC int
+xfs_btree_get_buf_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr,
+ int flags,
+ struct xfs_btree_block **block,
+ struct xfs_buf **bpp)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ xfs_daddr_t d;
+
+ /* need to sort out how callers deal with failures first */
+ ASSERT(!(flags & XFS_BUF_TRYLOCK));
+
+ d = xfs_btree_ptr_to_daddr(cur, ptr);
+ *bpp = xfs_trans_get_buf(cur->bc_tp, mp->m_ddev_targp, d,
+ mp->m_bsize, flags);
+
+ ASSERT(*bpp);
+ ASSERT(!XFS_BUF_GETERROR(*bpp));
+
+ *block = XFS_BUF_TO_BLOCK(*bpp);
+ return 0;
+}
+
+/*
+ * Read in the buffer at the given ptr and return the buffer and
+ * the block pointer within the buffer.
+ */
+STATIC int
+xfs_btree_read_buf_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr,
+ int level,
+ int flags,
+ struct xfs_btree_block **block,
+ struct xfs_buf **bpp)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ xfs_daddr_t d;
+ int error;
+
+ /* need to sort out how callers deal with failures first */
+ ASSERT(!(flags & XFS_BUF_TRYLOCK));
+
+ d = xfs_btree_ptr_to_daddr(cur, ptr);
+ error = xfs_trans_read_buf(mp, cur->bc_tp, mp->m_ddev_targp, d,
+ mp->m_bsize, flags, bpp);
+ if (error)
+ return error;
+
+ ASSERT(*bpp != NULL);
+ ASSERT(!XFS_BUF_GETERROR(*bpp));
+
+ xfs_btree_set_refs(cur, *bpp);
+ *block = XFS_BUF_TO_BLOCK(*bpp);
+
+ error = xfs_btree_check_block(cur, *block, level, *bpp);
+ if (error)
+ xfs_trans_brelse(cur->bc_tp, *bpp);
+ return error;
+}
+
+/*
+ * Copy keys from one btree block to another.
+ */
+STATIC void
+xfs_btree_copy_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *dst_key,
+ union xfs_btree_key *src_key,
+ int numkeys)
+{
+ ASSERT(numkeys >= 0);
+ memcpy(dst_key, src_key, numkeys * cur->bc_ops->key_len);
+}
+
+/*
+ * Copy records from one btree block to another.
+ */
+STATIC void
+xfs_btree_copy_recs(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *dst_rec,
+ union xfs_btree_rec *src_rec,
+ int numrecs)
+{
+ ASSERT(numrecs >= 0);
+ memcpy(dst_rec, src_rec, numrecs * cur->bc_ops->rec_len);
+}
+
+/*
+ * Copy block pointers from one btree block to another.
+ */
+STATIC void
+xfs_btree_copy_ptrs(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *dst_ptr,
+ union xfs_btree_ptr *src_ptr,
+ int numptrs)
+{
+ ASSERT(numptrs >= 0);
+ memcpy(dst_ptr, src_ptr, numptrs * xfs_btree_ptr_len(cur));
+}
+
+/*
+ * Shift keys one index left/right inside a single btree block.
+ */
+STATIC void
+xfs_btree_shift_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key,
+ int dir,
+ int numkeys)
+{
+ char *dst_key;
+
+ ASSERT(numkeys >= 0);
+ ASSERT(dir == 1 || dir == -1);
+
+ dst_key = (char *)key + (dir * cur->bc_ops->key_len);
+ memmove(dst_key, key, numkeys * cur->bc_ops->key_len);
+}
+
+/*
+ * Shift records one index left/right inside a single btree block.
+ */
+STATIC void
+xfs_btree_shift_recs(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ int dir,
+ int numrecs)
+{
+ char *dst_rec;
+
+ ASSERT(numrecs >= 0);
+ ASSERT(dir == 1 || dir == -1);
+
+ dst_rec = (char *)rec + (dir * cur->bc_ops->rec_len);
+ memmove(dst_rec, rec, numrecs * cur->bc_ops->rec_len);
+}
+
+/*
+ * Shift block pointers one index left/right inside a single btree block.
+ */
+STATIC void
+xfs_btree_shift_ptrs(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr,
+ int dir,
+ int numptrs)
+{
+ char *dst_ptr;
+
+ ASSERT(numptrs >= 0);
+ ASSERT(dir == 1 || dir == -1);
+
+ dst_ptr = (char *)ptr + (dir * xfs_btree_ptr_len(cur));
+ memmove(dst_ptr, ptr, numptrs * xfs_btree_ptr_len(cur));
+}
+
+/*
+ * Log key values from the btree block.
+ */
+STATIC void
+xfs_btree_log_keys(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ int first,
+ int last)
+{
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGBII(cur, bp, first, last);
+
+ if (bp) {
+ xfs_trans_log_buf(cur->bc_tp, bp,
+ xfs_btree_key_offset(cur, first),
+ xfs_btree_key_offset(cur, last + 1) - 1);
+ } else {
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip,
+ xfs_ilog_fbroot(cur->bc_private.b.whichfork));
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+}
+
+/*
+ * Log record values from the btree block.
+ */
+void
+xfs_btree_log_recs(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ int first,
+ int last)
+{
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGBII(cur, bp, first, last);
+
+ xfs_trans_log_buf(cur->bc_tp, bp,
+ xfs_btree_rec_offset(cur, first),
+ xfs_btree_rec_offset(cur, last + 1) - 1);
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+}
+
+/*
+ * Log block pointer fields from a btree block (nonleaf).
+ */
+STATIC void
+xfs_btree_log_ptrs(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_buf *bp, /* buffer containing btree block */
+ int first, /* index of first pointer to log */
+ int last) /* index of last pointer to log */
+{
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGBII(cur, bp, first, last);
+
+ if (bp) {
+ struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
+ int level = xfs_btree_get_level(block);
+
+ xfs_trans_log_buf(cur->bc_tp, bp,
+ xfs_btree_ptr_offset(cur, first, level),
+ xfs_btree_ptr_offset(cur, last + 1, level) - 1);
+ } else {
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip,
+ xfs_ilog_fbroot(cur->bc_private.b.whichfork));
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+}
+
+/*
+ * Log fields from a btree block header.
+ */
+void
+xfs_btree_log_block(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_buf *bp, /* buffer containing btree block */
+ int fields) /* mask of fields: XFS_BB_... */
+{
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ static const short soffsets[] = { /* table of offsets (short) */
+ offsetof(struct xfs_btree_block, bb_magic),
+ offsetof(struct xfs_btree_block, bb_level),
+ offsetof(struct xfs_btree_block, bb_numrecs),
+ offsetof(struct xfs_btree_block, bb_u.s.bb_leftsib),
+ offsetof(struct xfs_btree_block, bb_u.s.bb_rightsib),
+ XFS_BTREE_SBLOCK_LEN
+ };
+ static const short loffsets[] = { /* table of offsets (long) */
+ offsetof(struct xfs_btree_block, bb_magic),
+ offsetof(struct xfs_btree_block, bb_level),
+ offsetof(struct xfs_btree_block, bb_numrecs),
+ offsetof(struct xfs_btree_block, bb_u.l.bb_leftsib),
+ offsetof(struct xfs_btree_block, bb_u.l.bb_rightsib),
+ XFS_BTREE_LBLOCK_LEN
+ };
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGBI(cur, bp, fields);
+
+ if (bp) {
+ xfs_btree_offsets(fields,
+ (cur->bc_flags & XFS_BTREE_LONG_PTRS) ?
+ loffsets : soffsets,
+ XFS_BB_NUM_BITS, &first, &last);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ } else {
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip,
+ xfs_ilog_fbroot(cur->bc_private.b.whichfork));
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+}
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_btree_increment(
+ struct xfs_btree_cur *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ struct xfs_btree_block *block;
+ union xfs_btree_ptr ptr;
+ struct xfs_buf *bp;
+ int error; /* error return value */
+ int lev;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, level);
+
+ ASSERT(level < cur->bc_nlevels);
+
+ /* Read-ahead to the right at this level. */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
+
+ /* Get a pointer to the btree block. */
+ block = xfs_btree_get_block(cur, level, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto error0;
+#endif
+
+ /* We're done if we remain in the block after the increment. */
+ if (++cur->bc_ptrs[level] <= xfs_btree_get_numrecs(block))
+ goto out1;
+
+ /* Fail if we just went off the right edge of the tree. */
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
+ if (xfs_btree_ptr_is_null(cur, &ptr))
+ goto out0;
+
+ XFS_BTREE_STATS_INC(cur, increment);
+
+ /*
+ * March up the tree incrementing pointers.
+ * Stop when we don't go off the right edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ block = xfs_btree_get_block(cur, lev, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, lev, bp);
+ if (error)
+ goto error0;
+#endif
+
+ if (++cur->bc_ptrs[lev] <= xfs_btree_get_numrecs(block))
+ break;
+
+ /* Read-ahead the right block for the next loop. */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
+ }
+
+ /*
+ * If we went off the root then we are either seriously
+ * confused or have the tree root in an inode.
+ */
+ if (lev == cur->bc_nlevels) {
+ if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+ goto out0;
+ ASSERT(0);
+ error = EFSCORRUPTED;
+ goto error0;
+ }
+ ASSERT(lev < cur->bc_nlevels);
+
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (block = xfs_btree_get_block(cur, lev, &bp); lev > level; ) {
+ union xfs_btree_ptr *ptrp;
+
+ ptrp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[lev], block);
+ error = xfs_btree_read_buf_block(cur, ptrp, --lev,
+ 0, &block, &bp);
+ if (error)
+ goto error0;
+
+ xfs_btree_setbuf(cur, lev, bp);
+ cur->bc_ptrs[lev] = 1;
+ }
+out1:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_btree_decrement(
+ struct xfs_btree_cur *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ struct xfs_btree_block *block;
+ xfs_buf_t *bp;
+ int error; /* error return value */
+ int lev;
+ union xfs_btree_ptr ptr;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, level);
+
+ ASSERT(level < cur->bc_nlevels);
+
+ /* Read-ahead to the left at this level. */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
+
+ /* We're done if we remain in the block after the decrement. */
+ if (--cur->bc_ptrs[level] > 0)
+ goto out1;
+
+ /* Get a pointer to the btree block. */
+ block = xfs_btree_get_block(cur, level, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto error0;
+#endif
+
+ /* Fail if we just went off the left edge of the tree. */
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_LEFTSIB);
+ if (xfs_btree_ptr_is_null(cur, &ptr))
+ goto out0;
+
+ XFS_BTREE_STATS_INC(cur, decrement);
+
+ /*
+ * March up the tree decrementing pointers.
+ * Stop when we don't go off the left edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ if (--cur->bc_ptrs[lev] > 0)
+ break;
+ /* Read-ahead the left block for the next loop. */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
+ }
+
+ /*
+ * If we went off the root then we are seriously confused.
+ * or the root of the tree is in an inode.
+ */
+ if (lev == cur->bc_nlevels) {
+ if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+ goto out0;
+ ASSERT(0);
+ error = EFSCORRUPTED;
+ goto error0;
+ }
+ ASSERT(lev < cur->bc_nlevels);
+
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (block = xfs_btree_get_block(cur, lev, &bp); lev > level; ) {
+ union xfs_btree_ptr *ptrp;
+
+ ptrp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[lev], block);
+ error = xfs_btree_read_buf_block(cur, ptrp, --lev,
+ 0, &block, &bp);
+ if (error)
+ goto error0;
+ xfs_btree_setbuf(cur, lev, bp);
+ cur->bc_ptrs[lev] = xfs_btree_get_numrecs(block);
+ }
+out1:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+STATIC int
+xfs_btree_lookup_get_block(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level in the btree */
+ union xfs_btree_ptr *pp, /* ptr to btree block */
+ struct xfs_btree_block **blkp) /* return btree block */
+{
+ struct xfs_buf *bp; /* buffer pointer for btree block */
+ int error = 0;
+
+ /* special case the root block if in an inode */
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ (level == cur->bc_nlevels - 1)) {
+ *blkp = xfs_btree_get_iroot(cur);
+ return 0;
+ }
+
+ /*
+ * If the old buffer at this level for the disk address we are
+ * looking for re-use it.
+ *
+ * Otherwise throw it away and get a new one.
+ */
+ bp = cur->bc_bufs[level];
+ if (bp && XFS_BUF_ADDR(bp) == xfs_btree_ptr_to_daddr(cur, pp)) {
+ *blkp = XFS_BUF_TO_BLOCK(bp);
+ return 0;
+ }
+
+ error = xfs_btree_read_buf_block(cur, pp, level, 0, blkp, &bp);
+ if (error)
+ return error;
+
+ xfs_btree_setbuf(cur, level, bp);
+ return 0;
+}
+
+/*
+ * Get current search key. For level 0 we don't actually have a key
+ * structure so we make one up from the record. For all other levels
+ * we just return the right key.
+ */
+STATIC union xfs_btree_key *
+xfs_lookup_get_search_key(
+ struct xfs_btree_cur *cur,
+ int level,
+ int keyno,
+ struct xfs_btree_block *block,
+ union xfs_btree_key *kp)
+{
+ if (level == 0) {
+ cur->bc_ops->init_key_from_rec(kp,
+ xfs_btree_rec_addr(cur, keyno, block));
+ return kp;
+ }
+
+ return xfs_btree_key_addr(cur, keyno, block);
+}
+
+/*
+ * Lookup the record. The cursor is made to point to it, based on dir.
+ * Return 0 if can't find any such record, 1 for success.
+ */
+int /* error */
+xfs_btree_lookup(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_lookup_t dir, /* <=, ==, or >= */
+ int *stat) /* success/failure */
+{
+ struct xfs_btree_block *block; /* current btree block */
+ __int64_t diff; /* difference for the current key */
+ int error; /* error return value */
+ int keyno; /* current key number */
+ int level; /* level in the btree */
+ union xfs_btree_ptr *pp; /* ptr to btree block */
+ union xfs_btree_ptr ptr; /* ptr to btree block */
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, dir);
+
+ XFS_BTREE_STATS_INC(cur, lookup);
+
+ block = NULL;
+ keyno = 0;
+
+ /* initialise start pointer from cursor */
+ cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+ pp = &ptr;
+
+ /*
+ * Iterate over each level in the btree, starting at the root.
+ * For each level above the leaves, find the key we need, based
+ * on the lookup record, then follow the corresponding block
+ * pointer down to the next level.
+ */
+ for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
+ /* Get the block we need to do the lookup on. */
+ error = xfs_btree_lookup_get_block(cur, level, pp, &block);
+ if (error)
+ goto error0;
+
+ if (diff == 0) {
+ /*
+ * If we already had a key match at a higher level, we
+ * know we need to use the first entry in this block.
+ */
+ keyno = 1;
+ } else {
+ /* Otherwise search this block. Do a binary search. */
+
+ int high; /* high entry number */
+ int low; /* low entry number */
+
+ /* Set low and high entry numbers, 1-based. */
+ low = 1;
+ high = xfs_btree_get_numrecs(block);
+ if (!high) {
+ /* Block is empty, must be an empty leaf. */
+ ASSERT(level == 0 && cur->bc_nlevels == 1);
+
+ cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+ }
+
+ /* Binary search the block. */
+ while (low <= high) {
+ union xfs_btree_key key;
+ union xfs_btree_key *kp;
+
+ XFS_BTREE_STATS_INC(cur, compare);
+
+ /* keyno is average of low and high. */
+ keyno = (low + high) >> 1;
+
+ /* Get current search key */
+ kp = xfs_lookup_get_search_key(cur, level,
+ keyno, block, &key);
+
+ /*
+ * Compute difference to get next direction:
+ * - less than, move right
+ * - greater than, move left
+ * - equal, we're done
+ */
+ diff = cur->bc_ops->key_diff(cur, kp);
+ if (diff < 0)
+ low = keyno + 1;
+ else if (diff > 0)
+ high = keyno - 1;
+ else
+ break;
+ }
+ }
+
+ /*
+ * If there are more levels, set up for the next level
+ * by getting the block number and filling in the cursor.
+ */
+ if (level > 0) {
+ /*
+ * If we moved left, need the previous key number,
+ * unless there isn't one.
+ */
+ if (diff > 0 && --keyno < 1)
+ keyno = 1;
+ pp = xfs_btree_ptr_addr(cur, keyno, block);
+
+#ifdef DEBUG
+ error = xfs_btree_check_ptr(cur, pp, 0, level);
+ if (error)
+ goto error0;
+#endif
+ cur->bc_ptrs[level] = keyno;
+ }
+ }
+
+ /* Done with the search. See if we need to adjust the results. */
+ if (dir != XFS_LOOKUP_LE && diff < 0) {
+ keyno++;
+ /*
+ * If ge search and we went off the end of the block, but it's
+ * not the last block, we're in the wrong block.
+ */
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
+ if (dir == XFS_LOOKUP_GE &&
+ keyno > xfs_btree_get_numrecs(block) &&
+ !xfs_btree_ptr_is_null(cur, &ptr)) {
+ int i;
+
+ cur->bc_ptrs[0] = keyno;
+ error = xfs_btree_increment(cur, 0, &i);
+ if (error)
+ goto error0;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+ }
+ } else if (dir == XFS_LOOKUP_LE && diff > 0)
+ keyno--;
+ cur->bc_ptrs[0] = keyno;
+
+ /* Return if we succeeded or not. */
+ if (keyno == 0 || keyno > xfs_btree_get_numrecs(block))
+ *stat = 0;
+ else if (dir != XFS_LOOKUP_EQ || diff == 0)
+ *stat = 1;
+ else
+ *stat = 0;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Update keys at all levels from here to the root along the cursor's path.
+ */
+STATIC int
+xfs_btree_updkey(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *keyp,
+ int level)
+{
+ struct xfs_btree_block *block;
+ struct xfs_buf *bp;
+ union xfs_btree_key *kp;
+ int ptr;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGIK(cur, level, keyp);
+
+ ASSERT(!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) || level >= 1);
+
+ /*
+ * Go up the tree from this level toward the root.
+ * At each level, update the key value to the value input.
+ * Stop when we reach a level where the cursor isn't pointing
+ * at the first entry in the block.
+ */
+ for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
+#ifdef DEBUG
+ int error;
+#endif
+ block = xfs_btree_get_block(cur, level, &bp);
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+ }
+#endif
+ ptr = cur->bc_ptrs[level];
+ kp = xfs_btree_key_addr(cur, ptr, block);
+ xfs_btree_copy_keys(cur, kp, keyp, 1);
+ xfs_btree_log_keys(cur, bp, ptr, ptr);
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+}
+
+/*
+ * Update the record referred to by cur to the value in the
+ * given record. This either works (return 0) or gets an
+ * EFSCORRUPTED error.
+ */
+int
+xfs_btree_update(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_btree_block *block;
+ struct xfs_buf *bp;
+ int error;
+ int ptr;
+ union xfs_btree_rec *rp;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGR(cur, rec);
+
+ /* Pick up the current block. */
+ block = xfs_btree_get_block(cur, 0, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, 0, bp);
+ if (error)
+ goto error0;
+#endif
+ /* Get the address of the rec to be updated. */
+ ptr = cur->bc_ptrs[0];
+ rp = xfs_btree_rec_addr(cur, ptr, block);
+
+ /* Fill in the new contents and log them. */
+ xfs_btree_copy_recs(cur, rp, rec, 1);
+ xfs_btree_log_recs(cur, bp, ptr, ptr);
+
+ /*
+ * If we are tracking the last record in the tree and
+ * we are at the far right edge of the tree, update it.
+ */
+ if (xfs_btree_is_lastrec(cur, block, 0)) {
+ cur->bc_ops->update_lastrec(cur, block, rec,
+ ptr, LASTREC_UPDATE);
+ }
+
+ /* Updating first rec in leaf. Pass new key value up to our parent. */
+ if (ptr == 1) {
+ union xfs_btree_key key;
+
+ cur->bc_ops->init_key_from_rec(&key, rec);
+ error = xfs_btree_updkey(cur, &key, 1);
+ if (error)
+ goto error0;
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Move 1 record left from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_btree_lshift(
+ struct xfs_btree_cur *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ union xfs_btree_key key; /* btree key */
+ struct xfs_buf *lbp; /* left buffer pointer */
+ struct xfs_btree_block *left; /* left btree block */
+ int lrecs; /* left record count */
+ struct xfs_buf *rbp; /* right buffer pointer */
+ struct xfs_btree_block *right; /* right btree block */
+ int rrecs; /* right record count */
+ union xfs_btree_ptr lptr; /* left btree pointer */
+ union xfs_btree_key *rkp = NULL; /* right btree key */
+ union xfs_btree_ptr *rpp = NULL; /* right address pointer */
+ union xfs_btree_rec *rrp = NULL; /* right record pointer */
+ int error; /* error return value */
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, level);
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ level == cur->bc_nlevels - 1)
+ goto out0;
+
+ /* Set up variables for this block as "right". */
+ right = xfs_btree_get_block(cur, level, &rbp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, right, level, rbp);
+ if (error)
+ goto error0;
+#endif
+
+ /* If we've got no left sibling then we can't shift an entry left. */
+ xfs_btree_get_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
+ if (xfs_btree_ptr_is_null(cur, &lptr))
+ goto out0;
+
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ if (cur->bc_ptrs[level] <= 1)
+ goto out0;
+
+ /* Set up the left neighbor as "left". */
+ error = xfs_btree_read_buf_block(cur, &lptr, level, 0, &left, &lbp);
+ if (error)
+ goto error0;
+
+ /* If it's full, it can't take another entry. */
+ lrecs = xfs_btree_get_numrecs(left);
+ if (lrecs == cur->bc_ops->get_maxrecs(cur, level))
+ goto out0;
+
+ rrecs = xfs_btree_get_numrecs(right);
+
+ /*
+ * We add one entry to the left side and remove one for the right side.
+ * Accout for it here, the changes will be updated on disk and logged
+ * later.
+ */
+ lrecs++;
+ rrecs--;
+
+ XFS_BTREE_STATS_INC(cur, lshift);
+ XFS_BTREE_STATS_ADD(cur, moves, 1);
+
+ /*
+ * If non-leaf, copy a key and a ptr to the left block.
+ * Log the changes to the left block.
+ */
+ if (level > 0) {
+ /* It's a non-leaf. Move keys and pointers. */
+ union xfs_btree_key *lkp; /* left btree key */
+ union xfs_btree_ptr *lpp; /* left address pointer */
+
+ lkp = xfs_btree_key_addr(cur, lrecs, left);
+ rkp = xfs_btree_key_addr(cur, 1, right);
+
+ lpp = xfs_btree_ptr_addr(cur, lrecs, left);
+ rpp = xfs_btree_ptr_addr(cur, 1, right);
+#ifdef DEBUG
+ error = xfs_btree_check_ptr(cur, rpp, 0, level);
+ if (error)
+ goto error0;
+#endif
+ xfs_btree_copy_keys(cur, lkp, rkp, 1);
+ xfs_btree_copy_ptrs(cur, lpp, rpp, 1);
+
+ xfs_btree_log_keys(cur, lbp, lrecs, lrecs);
+ xfs_btree_log_ptrs(cur, lbp, lrecs, lrecs);
+
+ ASSERT(cur->bc_ops->keys_inorder(cur,
+ xfs_btree_key_addr(cur, lrecs - 1, left), lkp));
+ } else {
+ /* It's a leaf. Move records. */
+ union xfs_btree_rec *lrp; /* left record pointer */
+
+ lrp = xfs_btree_rec_addr(cur, lrecs, left);
+ rrp = xfs_btree_rec_addr(cur, 1, right);
+
+ xfs_btree_copy_recs(cur, lrp, rrp, 1);
+ xfs_btree_log_recs(cur, lbp, lrecs, lrecs);
+
+ ASSERT(cur->bc_ops->recs_inorder(cur,
+ xfs_btree_rec_addr(cur, lrecs - 1, left), lrp));
+ }
+
+ xfs_btree_set_numrecs(left, lrecs);
+ xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS);
+
+ xfs_btree_set_numrecs(right, rrecs);
+ xfs_btree_log_block(cur, rbp, XFS_BB_NUMRECS);
+
+ /*
+ * Slide the contents of right down one entry.
+ */
+ XFS_BTREE_STATS_ADD(cur, moves, rrecs - 1);
+ if (level > 0) {
+ /* It's a nonleaf. operate on keys and ptrs */
+#ifdef DEBUG
+ int i; /* loop index */
+
+ for (i = 0; i < rrecs; i++) {
+ error = xfs_btree_check_ptr(cur, rpp, i + 1, level);
+ if (error)
+ goto error0;
+ }
+#endif
+ xfs_btree_shift_keys(cur,
+ xfs_btree_key_addr(cur, 2, right),
+ -1, rrecs);
+ xfs_btree_shift_ptrs(cur,
+ xfs_btree_ptr_addr(cur, 2, right),
+ -1, rrecs);
+
+ xfs_btree_log_keys(cur, rbp, 1, rrecs);
+ xfs_btree_log_ptrs(cur, rbp, 1, rrecs);
+ } else {
+ /* It's a leaf. operate on records */
+ xfs_btree_shift_recs(cur,
+ xfs_btree_rec_addr(cur, 2, right),
+ -1, rrecs);
+ xfs_btree_log_recs(cur, rbp, 1, rrecs);
+
+ /*
+ * If it's the first record in the block, we'll need a key
+ * structure to pass up to the next level (updkey).
+ */
+ cur->bc_ops->init_key_from_rec(&key,
+ xfs_btree_rec_addr(cur, 1, right));
+ rkp = &key;
+ }
+
+ /* Update the parent key values of right. */
+ error = xfs_btree_updkey(cur, rkp, level + 1);
+ if (error)
+ goto error0;
+
+ /* Slide the cursor value left one. */
+ cur->bc_ptrs[level]--;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Move 1 record right from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_btree_rshift(
+ struct xfs_btree_cur *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ union xfs_btree_key key; /* btree key */
+ struct xfs_buf *lbp; /* left buffer pointer */
+ struct xfs_btree_block *left; /* left btree block */
+ struct xfs_buf *rbp; /* right buffer pointer */
+ struct xfs_btree_block *right; /* right btree block */
+ struct xfs_btree_cur *tcur; /* temporary btree cursor */
+ union xfs_btree_ptr rptr; /* right block pointer */
+ union xfs_btree_key *rkp; /* right btree key */
+ int rrecs; /* right record count */
+ int lrecs; /* left record count */
+ int error; /* error return value */
+ int i; /* loop counter */
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, level);
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ (level == cur->bc_nlevels - 1))
+ goto out0;
+
+ /* Set up variables for this block as "left". */
+ left = xfs_btree_get_block(cur, level, &lbp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, left, level, lbp);
+ if (error)
+ goto error0;
+#endif
+
+ /* If we've got no right sibling then we can't shift an entry right. */
+ xfs_btree_get_sibling(cur, left, &rptr, XFS_BB_RIGHTSIB);
+ if (xfs_btree_ptr_is_null(cur, &rptr))
+ goto out0;
+
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ lrecs = xfs_btree_get_numrecs(left);
+ if (cur->bc_ptrs[level] >= lrecs)
+ goto out0;
+
+ /* Set up the right neighbor as "right". */
+ error = xfs_btree_read_buf_block(cur, &rptr, level, 0, &right, &rbp);
+ if (error)
+ goto error0;
+
+ /* If it's full, it can't take another entry. */
+ rrecs = xfs_btree_get_numrecs(right);
+ if (rrecs == cur->bc_ops->get_maxrecs(cur, level))
+ goto out0;
+
+ XFS_BTREE_STATS_INC(cur, rshift);
+ XFS_BTREE_STATS_ADD(cur, moves, rrecs);
+
+ /*
+ * Make a hole at the start of the right neighbor block, then
+ * copy the last left block entry to the hole.
+ */
+ if (level > 0) {
+ /* It's a nonleaf. make a hole in the keys and ptrs */
+ union xfs_btree_key *lkp;
+ union xfs_btree_ptr *lpp;
+ union xfs_btree_ptr *rpp;
+
+ lkp = xfs_btree_key_addr(cur, lrecs, left);
+ lpp = xfs_btree_ptr_addr(cur, lrecs, left);
+ rkp = xfs_btree_key_addr(cur, 1, right);
+ rpp = xfs_btree_ptr_addr(cur, 1, right);
+
+#ifdef DEBUG
+ for (i = rrecs - 1; i >= 0; i--) {
+ error = xfs_btree_check_ptr(cur, rpp, i, level);
+ if (error)
+ goto error0;
+ }
+#endif
+
+ xfs_btree_shift_keys(cur, rkp, 1, rrecs);
+ xfs_btree_shift_ptrs(cur, rpp, 1, rrecs);
+
+#ifdef DEBUG
+ error = xfs_btree_check_ptr(cur, lpp, 0, level);
+ if (error)
+ goto error0;
+#endif
+
+ /* Now put the new data in, and log it. */
+ xfs_btree_copy_keys(cur, rkp, lkp, 1);
+ xfs_btree_copy_ptrs(cur, rpp, lpp, 1);
+
+ xfs_btree_log_keys(cur, rbp, 1, rrecs + 1);
+ xfs_btree_log_ptrs(cur, rbp, 1, rrecs + 1);
+
+ ASSERT(cur->bc_ops->keys_inorder(cur, rkp,
+ xfs_btree_key_addr(cur, 2, right)));
+ } else {
+ /* It's a leaf. make a hole in the records */
+ union xfs_btree_rec *lrp;
+ union xfs_btree_rec *rrp;
+
+ lrp = xfs_btree_rec_addr(cur, lrecs, left);
+ rrp = xfs_btree_rec_addr(cur, 1, right);
+
+ xfs_btree_shift_recs(cur, rrp, 1, rrecs);
+
+ /* Now put the new data in, and log it. */
+ xfs_btree_copy_recs(cur, rrp, lrp, 1);
+ xfs_btree_log_recs(cur, rbp, 1, rrecs + 1);
+
+ cur->bc_ops->init_key_from_rec(&key, rrp);
+ rkp = &key;
+
+ ASSERT(cur->bc_ops->recs_inorder(cur, rrp,
+ xfs_btree_rec_addr(cur, 2, right)));
+ }
+
+ /*
+ * Decrement and log left's numrecs, bump and log right's numrecs.
+ */
+ xfs_btree_set_numrecs(left, --lrecs);
+ xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS);
+
+ xfs_btree_set_numrecs(right, ++rrecs);
+ xfs_btree_log_block(cur, rbp, XFS_BB_NUMRECS);
+
+ /*
+ * Using a temporary cursor, update the parent key values of the
+ * block on the right.
+ */
+ error = xfs_btree_dup_cursor(cur, &tcur);
+ if (error)
+ goto error0;
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ error = xfs_btree_increment(tcur, level, &i);
+ if (error)
+ goto error1;
+
+ error = xfs_btree_updkey(tcur, rkp, level + 1);
+ if (error)
+ goto error1;
+
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+
+error1:
+ XFS_BTREE_TRACE_CURSOR(tcur, XBT_ERROR);
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Split cur/level block in half.
+ * Return new block number and the key to its first
+ * record (to be inserted into parent).
+ */
+STATIC int /* error */
+xfs_btree_split(
+ struct xfs_btree_cur *cur,
+ int level,
+ union xfs_btree_ptr *ptrp,
+ union xfs_btree_key *key,
+ struct xfs_btree_cur **curp,
+ int *stat) /* success/failure */
+{
+ union xfs_btree_ptr lptr; /* left sibling block ptr */
+ struct xfs_buf *lbp; /* left buffer pointer */
+ struct xfs_btree_block *left; /* left btree block */
+ union xfs_btree_ptr rptr; /* right sibling block ptr */
+ struct xfs_buf *rbp; /* right buffer pointer */
+ struct xfs_btree_block *right; /* right btree block */
+ union xfs_btree_ptr rrptr; /* right-right sibling ptr */
+ struct xfs_buf *rrbp; /* right-right buffer pointer */
+ struct xfs_btree_block *rrblock; /* right-right btree block */
+ int lrecs;
+ int rrecs;
+ int src_index;
+ int error; /* error return value */
+#ifdef DEBUG
+ int i;
+#endif
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGIPK(cur, level, *ptrp, key);
+
+ XFS_BTREE_STATS_INC(cur, split);
+
+ /* Set up left block (current one). */
+ left = xfs_btree_get_block(cur, level, &lbp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, left, level, lbp);
+ if (error)
+ goto error0;
+#endif
+
+ xfs_btree_buf_to_ptr(cur, lbp, &lptr);
+
+ /* Allocate the new block. If we can't do it, we're toast. Give up. */
+ error = cur->bc_ops->alloc_block(cur, &lptr, &rptr, 1, stat);
+ if (error)
+ goto error0;
+ if (*stat == 0)
+ goto out0;
+ XFS_BTREE_STATS_INC(cur, alloc);
+
+ /* Set up the new block as "right". */
+ error = xfs_btree_get_buf_block(cur, &rptr, 0, &right, &rbp);
+ if (error)
+ goto error0;
+
+ /* Fill in the btree header for the new right block. */
+ xfs_btree_init_block(cur, xfs_btree_get_level(left), 0, right);
+
+ /*
+ * Split the entries between the old and the new block evenly.
+ * Make sure that if there's an odd number of entries now, that
+ * each new block will have the same number of entries.
+ */
+ lrecs = xfs_btree_get_numrecs(left);
+ rrecs = lrecs / 2;
+ if ((lrecs & 1) && cur->bc_ptrs[level] <= rrecs + 1)
+ rrecs++;
+ src_index = (lrecs - rrecs + 1);
+
+ XFS_BTREE_STATS_ADD(cur, moves, rrecs);
+
+ /*
+ * Copy btree block entries from the left block over to the
+ * new block, the right. Update the right block and log the
+ * changes.
+ */
+ if (level > 0) {
+ /* It's a non-leaf. Move keys and pointers. */
+ union xfs_btree_key *lkp; /* left btree key */
+ union xfs_btree_ptr *lpp; /* left address pointer */
+ union xfs_btree_key *rkp; /* right btree key */
+ union xfs_btree_ptr *rpp; /* right address pointer */
+
+ lkp = xfs_btree_key_addr(cur, src_index, left);
+ lpp = xfs_btree_ptr_addr(cur, src_index, left);
+ rkp = xfs_btree_key_addr(cur, 1, right);
+ rpp = xfs_btree_ptr_addr(cur, 1, right);
+
+#ifdef DEBUG
+ for (i = src_index; i < rrecs; i++) {
+ error = xfs_btree_check_ptr(cur, lpp, i, level);
+ if (error)
+ goto error0;
+ }
+#endif
+
+ xfs_btree_copy_keys(cur, rkp, lkp, rrecs);
+ xfs_btree_copy_ptrs(cur, rpp, lpp, rrecs);
+
+ xfs_btree_log_keys(cur, rbp, 1, rrecs);
+ xfs_btree_log_ptrs(cur, rbp, 1, rrecs);
+
+ /* Grab the keys to the entries moved to the right block */
+ xfs_btree_copy_keys(cur, key, rkp, 1);
+ } else {
+ /* It's a leaf. Move records. */
+ union xfs_btree_rec *lrp; /* left record pointer */
+ union xfs_btree_rec *rrp; /* right record pointer */
+
+ lrp = xfs_btree_rec_addr(cur, src_index, left);
+ rrp = xfs_btree_rec_addr(cur, 1, right);
+
+ xfs_btree_copy_recs(cur, rrp, lrp, rrecs);
+ xfs_btree_log_recs(cur, rbp, 1, rrecs);
+
+ cur->bc_ops->init_key_from_rec(key,
+ xfs_btree_rec_addr(cur, 1, right));
+ }
+
+
+ /*
+ * Find the left block number by looking in the buffer.
+ * Adjust numrecs, sibling pointers.
+ */
+ xfs_btree_get_sibling(cur, left, &rrptr, XFS_BB_RIGHTSIB);
+ xfs_btree_set_sibling(cur, right, &rrptr, XFS_BB_RIGHTSIB);
+ xfs_btree_set_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
+ xfs_btree_set_sibling(cur, left, &rptr, XFS_BB_RIGHTSIB);
+
+ lrecs -= rrecs;
+ xfs_btree_set_numrecs(left, lrecs);
+ xfs_btree_set_numrecs(right, xfs_btree_get_numrecs(right) + rrecs);
+
+ xfs_btree_log_block(cur, rbp, XFS_BB_ALL_BITS);
+ xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+
+ /*
+ * If there's a block to the new block's right, make that block
+ * point back to right instead of to left.
+ */
+ if (!xfs_btree_ptr_is_null(cur, &rrptr)) {
+ error = xfs_btree_read_buf_block(cur, &rrptr, level,
+ 0, &rrblock, &rrbp);
+ if (error)
+ goto error0;
+ xfs_btree_set_sibling(cur, rrblock, &rptr, XFS_BB_LEFTSIB);
+ xfs_btree_log_block(cur, rrbp, XFS_BB_LEFTSIB);
+ }
+ /*
+ * If the cursor is really in the right block, move it there.
+ * If it's just pointing past the last entry in left, then we'll
+ * insert there, so don't change anything in that case.
+ */
+ if (cur->bc_ptrs[level] > lrecs + 1) {
+ xfs_btree_setbuf(cur, level, rbp);
+ cur->bc_ptrs[level] -= lrecs;
+ }
+ /*
+ * If there are more levels, we'll need another cursor which refers
+ * the right block, no matter where this cursor was.
+ */
+ if (level + 1 < cur->bc_nlevels) {
+ error = xfs_btree_dup_cursor(cur, curp);
+ if (error)
+ goto error0;
+ (*curp)->bc_ptrs[level + 1]++;
+ }
+ *ptrp = rptr;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Copy the old inode root contents into a real block and make the
+ * broot point to it.
+ */
+int /* error */
+xfs_btree_new_iroot(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *logflags, /* logging flags for inode */
+ int *stat) /* return status - 0 fail */
+{
+ struct xfs_buf *cbp; /* buffer for cblock */
+ struct xfs_btree_block *block; /* btree block */
+ struct xfs_btree_block *cblock; /* child btree block */
+ union xfs_btree_key *ckp; /* child key pointer */
+ union xfs_btree_ptr *cpp; /* child ptr pointer */
+ union xfs_btree_key *kp; /* pointer to btree key */
+ union xfs_btree_ptr *pp; /* pointer to block addr */
+ union xfs_btree_ptr nptr; /* new block addr */
+ int level; /* btree level */
+ int error; /* error return code */
+#ifdef DEBUG
+ int i; /* loop counter */
+#endif
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_STATS_INC(cur, newroot);
+
+ ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
+
+ level = cur->bc_nlevels - 1;
+
+ block = xfs_btree_get_iroot(cur);
+ pp = xfs_btree_ptr_addr(cur, 1, block);
+
+ /* Allocate the new block. If we can't do it, we're toast. Give up. */
+ error = cur->bc_ops->alloc_block(cur, pp, &nptr, 1, stat);
+ if (error)
+ goto error0;
+ if (*stat == 0) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+ }
+ XFS_BTREE_STATS_INC(cur, alloc);
+
+ /* Copy the root into a real block. */
+ error = xfs_btree_get_buf_block(cur, &nptr, 0, &cblock, &cbp);
+ if (error)
+ goto error0;
+
+ memcpy(cblock, block, xfs_btree_block_len(cur));
+
+ be16_add_cpu(&block->bb_level, 1);
+ xfs_btree_set_numrecs(block, 1);
+ cur->bc_nlevels++;
+ cur->bc_ptrs[level + 1] = 1;
+
+ kp = xfs_btree_key_addr(cur, 1, block);
+ ckp = xfs_btree_key_addr(cur, 1, cblock);
+ xfs_btree_copy_keys(cur, ckp, kp, xfs_btree_get_numrecs(cblock));
+
+ cpp = xfs_btree_ptr_addr(cur, 1, cblock);
+#ifdef DEBUG
+ for (i = 0; i < be16_to_cpu(cblock->bb_numrecs); i++) {
+ error = xfs_btree_check_ptr(cur, pp, i, level);
+ if (error)
+ goto error0;
+ }
+#endif
+ xfs_btree_copy_ptrs(cur, cpp, pp, xfs_btree_get_numrecs(cblock));
+
+#ifdef DEBUG
+ error = xfs_btree_check_ptr(cur, &nptr, 0, level);
+ if (error)
+ goto error0;
+#endif
+ xfs_btree_copy_ptrs(cur, pp, &nptr, 1);
+
+ xfs_iroot_realloc(cur->bc_private.b.ip,
+ 1 - xfs_btree_get_numrecs(cblock),
+ cur->bc_private.b.whichfork);
+
+ xfs_btree_setbuf(cur, level, cbp);
+
+ /*
+ * Do all this logging at the end so that
+ * the root is at the right level.
+ */
+ xfs_btree_log_block(cur, cbp, XFS_BB_ALL_BITS);
+ xfs_btree_log_keys(cur, cbp, 1, be16_to_cpu(cblock->bb_numrecs));
+ xfs_btree_log_ptrs(cur, cbp, 1, be16_to_cpu(cblock->bb_numrecs));
+
+ *logflags |=
+ XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork);
+ *stat = 1;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Allocate a new root block, fill it in.
+ */
+STATIC int /* error */
+xfs_btree_new_root(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ struct xfs_btree_block *block; /* one half of the old root block */
+ struct xfs_buf *bp; /* buffer containing block */
+ int error; /* error return value */
+ struct xfs_buf *lbp; /* left buffer pointer */
+ struct xfs_btree_block *left; /* left btree block */
+ struct xfs_buf *nbp; /* new (root) buffer */
+ struct xfs_btree_block *new; /* new (root) btree block */
+ int nptr; /* new value for key index, 1 or 2 */
+ struct xfs_buf *rbp; /* right buffer pointer */
+ struct xfs_btree_block *right; /* right btree block */
+ union xfs_btree_ptr rptr;
+ union xfs_btree_ptr lptr;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_STATS_INC(cur, newroot);
+
+ /* initialise our start point from the cursor */
+ cur->bc_ops->init_ptr_from_cur(cur, &rptr);
+
+ /* Allocate the new block. If we can't do it, we're toast. Give up. */
+ error = cur->bc_ops->alloc_block(cur, &rptr, &lptr, 1, stat);
+ if (error)
+ goto error0;
+ if (*stat == 0)
+ goto out0;
+ XFS_BTREE_STATS_INC(cur, alloc);
+
+ /* Set up the new block. */
+ error = xfs_btree_get_buf_block(cur, &lptr, 0, &new, &nbp);
+ if (error)
+ goto error0;
+
+ /* Set the root in the holding structure increasing the level by 1. */
+ cur->bc_ops->set_root(cur, &lptr, 1);
+
+ /*
+ * At the previous root level there are now two blocks: the old root,
+ * and the new block generated when it was split. We don't know which
+ * one the cursor is pointing at, so we set up variables "left" and
+ * "right" for each case.
+ */
+ block = xfs_btree_get_block(cur, cur->bc_nlevels - 1, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, cur->bc_nlevels - 1, bp);
+ if (error)
+ goto error0;
+#endif
+
+ xfs_btree_get_sibling(cur, block, &rptr, XFS_BB_RIGHTSIB);
+ if (!xfs_btree_ptr_is_null(cur, &rptr)) {
+ /* Our block is left, pick up the right block. */
+ lbp = bp;
+ xfs_btree_buf_to_ptr(cur, lbp, &lptr);
+ left = block;
+ error = xfs_btree_read_buf_block(cur, &rptr,
+ cur->bc_nlevels - 1, 0, &right, &rbp);
+ if (error)
+ goto error0;
+ bp = rbp;
+ nptr = 1;
+ } else {
+ /* Our block is right, pick up the left block. */
+ rbp = bp;
+ xfs_btree_buf_to_ptr(cur, rbp, &rptr);
+ right = block;
+ xfs_btree_get_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
+ error = xfs_btree_read_buf_block(cur, &lptr,
+ cur->bc_nlevels - 1, 0, &left, &lbp);
+ if (error)
+ goto error0;
+ bp = lbp;
+ nptr = 2;
+ }
+ /* Fill in the new block's btree header and log it. */
+ xfs_btree_init_block(cur, cur->bc_nlevels, 2, new);
+ xfs_btree_log_block(cur, nbp, XFS_BB_ALL_BITS);
+ ASSERT(!xfs_btree_ptr_is_null(cur, &lptr) &&
+ !xfs_btree_ptr_is_null(cur, &rptr));
+
+ /* Fill in the key data in the new root. */
+ if (xfs_btree_get_level(left) > 0) {
+ xfs_btree_copy_keys(cur,
+ xfs_btree_key_addr(cur, 1, new),
+ xfs_btree_key_addr(cur, 1, left), 1);
+ xfs_btree_copy_keys(cur,
+ xfs_btree_key_addr(cur, 2, new),
+ xfs_btree_key_addr(cur, 1, right), 1);
+ } else {
+ cur->bc_ops->init_key_from_rec(
+ xfs_btree_key_addr(cur, 1, new),
+ xfs_btree_rec_addr(cur, 1, left));
+ cur->bc_ops->init_key_from_rec(
+ xfs_btree_key_addr(cur, 2, new),
+ xfs_btree_rec_addr(cur, 1, right));
+ }
+ xfs_btree_log_keys(cur, nbp, 1, 2);
+
+ /* Fill in the pointer data in the new root. */
+ xfs_btree_copy_ptrs(cur,
+ xfs_btree_ptr_addr(cur, 1, new), &lptr, 1);
+ xfs_btree_copy_ptrs(cur,
+ xfs_btree_ptr_addr(cur, 2, new), &rptr, 1);
+ xfs_btree_log_ptrs(cur, nbp, 1, 2);
+
+ /* Fix up the cursor. */
+ xfs_btree_setbuf(cur, cur->bc_nlevels, nbp);
+ cur->bc_ptrs[cur->bc_nlevels] = nptr;
+ cur->bc_nlevels++;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+}
+
+STATIC int
+xfs_btree_make_block_unfull(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* btree level */
+ int numrecs,/* # of recs in block */
+ int *oindex,/* old tree index */
+ int *index, /* new tree index */
+ union xfs_btree_ptr *nptr, /* new btree ptr */
+ struct xfs_btree_cur **ncur, /* new btree cursor */
+ union xfs_btree_rec *nrec, /* new record */
+ int *stat)
+{
+ union xfs_btree_key key; /* new btree key value */
+ int error = 0;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ level == cur->bc_nlevels - 1) {
+ struct xfs_inode *ip = cur->bc_private.b.ip;
+
+ if (numrecs < cur->bc_ops->get_dmaxrecs(cur, level)) {
+ /* A root block that can be made bigger. */
+
+ xfs_iroot_realloc(ip, 1, cur->bc_private.b.whichfork);
+ } else {
+ /* A root block that needs replacing */
+ int logflags = 0;
+
+ error = xfs_btree_new_iroot(cur, &logflags, stat);
+ if (error || *stat == 0)
+ return error;
+
+ xfs_trans_log_inode(cur->bc_tp, ip, logflags);
+ }
+
+ return 0;
+ }
+
+ /* First, try shifting an entry to the right neighbor. */
+ error = xfs_btree_rshift(cur, level, stat);
+ if (error || *stat)
+ return error;
+
+ /* Next, try shifting an entry to the left neighbor. */
+ error = xfs_btree_lshift(cur, level, stat);
+ if (error)
+ return error;
+
+ if (*stat) {
+ *oindex = *index = cur->bc_ptrs[level];
+ return 0;
+ }
+
+ /*
+ * Next, try splitting the current block in half.
+ *
+ * If this works we have to re-set our variables because we
+ * could be in a different block now.
+ */
+ error = xfs_btree_split(cur, level, nptr, &key, ncur, stat);
+ if (error || *stat == 0)
+ return error;
+
+
+ *index = cur->bc_ptrs[level];
+ cur->bc_ops->init_rec_from_key(&key, nrec);
+ return 0;
+}
+
+/*
+ * Insert one record/level. Return information to the caller
+ * allowing the next level up to proceed if necessary.
+ */
+STATIC int
+xfs_btree_insrec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level to insert record at */
+ union xfs_btree_ptr *ptrp, /* i/o: block number inserted */
+ union xfs_btree_rec *recp, /* i/o: record data inserted */
+ struct xfs_btree_cur **curp, /* output: new cursor replacing cur */
+ int *stat) /* success/failure */
+{
+ struct xfs_btree_block *block; /* btree block */
+ struct xfs_buf *bp; /* buffer for block */
+ union xfs_btree_key key; /* btree key */
+ union xfs_btree_ptr nptr; /* new block ptr */
+ struct xfs_btree_cur *ncur; /* new btree cursor */
+ union xfs_btree_rec nrec; /* new record count */
+ int optr; /* old key/record index */
+ int ptr; /* key/record index */
+ int numrecs;/* number of records */
+ int error; /* error return value */
+#ifdef DEBUG
+ int i;
+#endif
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGIPR(cur, level, *ptrp, recp);
+
+ ncur = NULL;
+
+ /*
+ * If we have an external root pointer, and we've made it to the
+ * root level, allocate a new root block and we're done.
+ */
+ if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ (level >= cur->bc_nlevels)) {
+ error = xfs_btree_new_root(cur, stat);
+ xfs_btree_set_ptr_null(cur, ptrp);
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return error;
+ }
+
+ /* If we're off the left edge, return failure. */
+ ptr = cur->bc_ptrs[level];
+ if (ptr == 0) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+ }
+
+ /* Make a key out of the record data to be inserted, and save it. */
+ cur->bc_ops->init_key_from_rec(&key, recp);
+
+ optr = ptr;
+
+ XFS_BTREE_STATS_INC(cur, insrec);
+
+ /* Get pointers to the btree buffer and block. */
+ block = xfs_btree_get_block(cur, level, &bp);
+ numrecs = xfs_btree_get_numrecs(block);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto error0;
+
+ /* Check that the new entry is being inserted in the right place. */
+ if (ptr <= numrecs) {
+ if (level == 0) {
+ ASSERT(cur->bc_ops->recs_inorder(cur, recp,
+ xfs_btree_rec_addr(cur, ptr, block)));
+ } else {
+ ASSERT(cur->bc_ops->keys_inorder(cur, &key,
+ xfs_btree_key_addr(cur, ptr, block)));
+ }
+ }
+#endif
+
+ /*
+ * If the block is full, we can't insert the new entry until we
+ * make the block un-full.
+ */
+ xfs_btree_set_ptr_null(cur, &nptr);
+ if (numrecs == cur->bc_ops->get_maxrecs(cur, level)) {
+ error = xfs_btree_make_block_unfull(cur, level, numrecs,
+ &optr, &ptr, &nptr, &ncur, &nrec, stat);
+ if (error || *stat == 0)
+ goto error0;
+ }
+
+ /*
+ * The current block may have changed if the block was
+ * previously full and we have just made space in it.
+ */
+ block = xfs_btree_get_block(cur, level, &bp);
+ numrecs = xfs_btree_get_numrecs(block);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ return error;
+#endif
+
+ /*
+ * At this point we know there's room for our new entry in the block
+ * we're pointing at.
+ */
+ XFS_BTREE_STATS_ADD(cur, moves, numrecs - ptr + 1);
+
+ if (level > 0) {
+ /* It's a nonleaf. make a hole in the keys and ptrs */
+ union xfs_btree_key *kp;
+ union xfs_btree_ptr *pp;
+
+ kp = xfs_btree_key_addr(cur, ptr, block);
+ pp = xfs_btree_ptr_addr(cur, ptr, block);
+
+#ifdef DEBUG
+ for (i = numrecs - ptr; i >= 0; i--) {
+ error = xfs_btree_check_ptr(cur, pp, i, level);
+ if (error)
+ return error;
+ }
+#endif
+
+ xfs_btree_shift_keys(cur, kp, 1, numrecs - ptr + 1);
+ xfs_btree_shift_ptrs(cur, pp, 1, numrecs - ptr + 1);
+
+#ifdef DEBUG
+ error = xfs_btree_check_ptr(cur, ptrp, 0, level);
+ if (error)
+ goto error0;
+#endif
+
+ /* Now put the new data in, bump numrecs and log it. */
+ xfs_btree_copy_keys(cur, kp, &key, 1);
+ xfs_btree_copy_ptrs(cur, pp, ptrp, 1);
+ numrecs++;
+ xfs_btree_set_numrecs(block, numrecs);
+ xfs_btree_log_ptrs(cur, bp, ptr, numrecs);
+ xfs_btree_log_keys(cur, bp, ptr, numrecs);
+#ifdef DEBUG
+ if (ptr < numrecs) {
+ ASSERT(cur->bc_ops->keys_inorder(cur, kp,
+ xfs_btree_key_addr(cur, ptr + 1, block)));
+ }
+#endif
+ } else {
+ /* It's a leaf. make a hole in the records */
+ union xfs_btree_rec *rp;
+
+ rp = xfs_btree_rec_addr(cur, ptr, block);
+
+ xfs_btree_shift_recs(cur, rp, 1, numrecs - ptr + 1);
+
+ /* Now put the new data in, bump numrecs and log it. */
+ xfs_btree_copy_recs(cur, rp, recp, 1);
+ xfs_btree_set_numrecs(block, ++numrecs);
+ xfs_btree_log_recs(cur, bp, ptr, numrecs);
+#ifdef DEBUG
+ if (ptr < numrecs) {
+ ASSERT(cur->bc_ops->recs_inorder(cur, rp,
+ xfs_btree_rec_addr(cur, ptr + 1, block)));
+ }
+#endif
+ }
+
+ /* Log the new number of records in the btree header. */
+ xfs_btree_log_block(cur, bp, XFS_BB_NUMRECS);
+
+ /* If we inserted at the start of a block, update the parents' keys. */
+ if (optr == 1) {
+ error = xfs_btree_updkey(cur, &key, level + 1);
+ if (error)
+ goto error0;
+ }
+
+ /*
+ * If we are tracking the last record in the tree and
+ * we are at the far right edge of the tree, update it.
+ */
+ if (xfs_btree_is_lastrec(cur, block, level)) {
+ cur->bc_ops->update_lastrec(cur, block, recp,
+ ptr, LASTREC_INSREC);
+ }
+
+ /*
+ * Return the new block number, if any.
+ * If there is one, give back a record value and a cursor too.
+ */
+ *ptrp = nptr;
+ if (!xfs_btree_ptr_is_null(cur, &nptr)) {
+ *recp = nrec;
+ *curp = ncur;
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Insert the record at the point referenced by cur.
+ *
+ * A multi-level split of the tree on insert will invalidate the original
+ * cursor. All callers of this function should assume that the cursor is
+ * no longer valid and revalidate it.
+ */
+int
+xfs_btree_insert(
+ struct xfs_btree_cur *cur,
+ int *stat)
+{
+ int error; /* error return value */
+ int i; /* result value, 0 for failure */
+ int level; /* current level number in btree */
+ union xfs_btree_ptr nptr; /* new block number (split result) */
+ struct xfs_btree_cur *ncur; /* new cursor (split result) */
+ struct xfs_btree_cur *pcur; /* previous level's cursor */
+ union xfs_btree_rec rec; /* record to insert */
+
+ level = 0;
+ ncur = NULL;
+ pcur = cur;
+
+ xfs_btree_set_ptr_null(cur, &nptr);
+ cur->bc_ops->init_rec_from_cur(cur, &rec);
+
+ /*
+ * Loop going up the tree, starting at the leaf level.
+ * Stop when we don't get a split block, that must mean that
+ * the insert is finished with this level.
+ */
+ do {
+ /*
+ * Insert nrec/nptr into this level of the tree.
+ * Note if we fail, nptr will be null.
+ */
+ error = xfs_btree_insrec(pcur, level, &nptr, &rec, &ncur, &i);
+ if (error) {
+ if (pcur != cur)
+ xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
+ goto error0;
+ }
+
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ level++;
+
+ /*
+ * See if the cursor we just used is trash.
+ * Can't trash the caller's cursor, but otherwise we should
+ * if ncur is a new cursor or we're about to be done.
+ */
+ if (pcur != cur &&
+ (ncur || xfs_btree_ptr_is_null(cur, &nptr))) {
+ /* Save the state from the cursor before we trash it */
+ if (cur->bc_ops->update_cursor)
+ cur->bc_ops->update_cursor(pcur, cur);
+ cur->bc_nlevels = pcur->bc_nlevels;
+ xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
+ }
+ /* If we got a new cursor, switch to it. */
+ if (ncur) {
+ pcur = ncur;
+ ncur = NULL;
+ }
+ } while (!xfs_btree_ptr_is_null(cur, &nptr));
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = i;
+ return 0;
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Try to merge a non-leaf block back into the inode root.
+ *
+ * Note: the killroot names comes from the fact that we're effectively
+ * killing the old root block. But because we can't just delete the
+ * inode we have to copy the single block it was pointing to into the
+ * inode.
+ */
+int
+xfs_btree_kill_iroot(
+ struct xfs_btree_cur *cur)
+{
+ int whichfork = cur->bc_private.b.whichfork;
+ struct xfs_inode *ip = cur->bc_private.b.ip;
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
+ struct xfs_btree_block *block;
+ struct xfs_btree_block *cblock;
+ union xfs_btree_key *kp;
+ union xfs_btree_key *ckp;
+ union xfs_btree_ptr *pp;
+ union xfs_btree_ptr *cpp;
+ struct xfs_buf *cbp;
+ int level;
+ int index;
+ int numrecs;
+#ifdef DEBUG
+ union xfs_btree_ptr ptr;
+ int i;
+#endif
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+ ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
+ ASSERT(cur->bc_nlevels > 1);
+
+ /*
+ * Don't deal with the root block needs to be a leaf case.
+ * We're just going to turn the thing back into extents anyway.
+ */
+ level = cur->bc_nlevels - 1;
+ if (level == 1)
+ goto out0;
+
+ /*
+ * Give up if the root has multiple children.
+ */
+ block = xfs_btree_get_iroot(cur);
+ if (xfs_btree_get_numrecs(block) != 1)
+ goto out0;
+
+ cblock = xfs_btree_get_block(cur, level - 1, &cbp);
+ numrecs = xfs_btree_get_numrecs(cblock);
+
+ /*
+ * Only do this if the next level will fit.
+ * Then the data must be copied up to the inode,
+ * instead of freeing the root you free the next level.
+ */
+ if (numrecs > cur->bc_ops->get_dmaxrecs(cur, level))
+ goto out0;
+
+ XFS_BTREE_STATS_INC(cur, killroot);
+
+#ifdef DEBUG
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_LEFTSIB);
+ ASSERT(xfs_btree_ptr_is_null(cur, &ptr));
+ xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
+ ASSERT(xfs_btree_ptr_is_null(cur, &ptr));
+#endif
+
+ index = numrecs - cur->bc_ops->get_maxrecs(cur, level);
+ if (index) {
+ xfs_iroot_realloc(cur->bc_private.b.ip, index,
+ cur->bc_private.b.whichfork);
+ block = ifp->if_broot;
+ }
+
+ be16_add_cpu(&block->bb_numrecs, index);
+ ASSERT(block->bb_numrecs == cblock->bb_numrecs);
+
+ kp = xfs_btree_key_addr(cur, 1, block);
+ ckp = xfs_btree_key_addr(cur, 1, cblock);
+ xfs_btree_copy_keys(cur, kp, ckp, numrecs);
+
+ pp = xfs_btree_ptr_addr(cur, 1, block);
+ cpp = xfs_btree_ptr_addr(cur, 1, cblock);
+#ifdef DEBUG
+ for (i = 0; i < numrecs; i++) {
+ int error;
+
+ error = xfs_btree_check_ptr(cur, cpp, i, level - 1);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+ }
+ }
+#endif
+ xfs_btree_copy_ptrs(cur, pp, cpp, numrecs);
+
+ cur->bc_ops->free_block(cur, cbp);
+ XFS_BTREE_STATS_INC(cur, free);
+
+ cur->bc_bufs[level - 1] = NULL;
+ be16_add_cpu(&block->bb_level, -1);
+ xfs_trans_log_inode(cur->bc_tp, ip,
+ XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
+ cur->bc_nlevels--;
+out0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ return 0;
+}
+
+STATIC int
+xfs_btree_dec_cursor(
+ struct xfs_btree_cur *cur,
+ int level,
+ int *stat)
+{
+ int error;
+ int i;
+
+ if (level > 0) {
+ error = xfs_btree_decrement(cur, level, &i);
+ if (error)
+ return error;
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Single level of the btree record deletion routine.
+ * Delete record pointed to by cur/level.
+ * Remove the record from its block then rebalance the tree.
+ * Return 0 for error, 1 for done, 2 to go on to the next level.
+ */
+STATIC int /* error */
+xfs_btree_delrec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level removing record from */
+ int *stat) /* fail/done/go-on */
+{
+ struct xfs_btree_block *block; /* btree block */
+ union xfs_btree_ptr cptr; /* current block ptr */
+ struct xfs_buf *bp; /* buffer for block */
+ int error; /* error return value */
+ int i; /* loop counter */
+ union xfs_btree_key key; /* storage for keyp */
+ union xfs_btree_key *keyp = &key; /* passed to the next level */
+ union xfs_btree_ptr lptr; /* left sibling block ptr */
+ struct xfs_buf *lbp; /* left buffer pointer */
+ struct xfs_btree_block *left; /* left btree block */
+ int lrecs = 0; /* left record count */
+ int ptr; /* key/record index */
+ union xfs_btree_ptr rptr; /* right sibling block ptr */
+ struct xfs_buf *rbp; /* right buffer pointer */
+ struct xfs_btree_block *right; /* right btree block */
+ struct xfs_btree_block *rrblock; /* right-right btree block */
+ struct xfs_buf *rrbp; /* right-right buffer pointer */
+ int rrecs = 0; /* right record count */
+ struct xfs_btree_cur *tcur; /* temporary btree cursor */
+ int numrecs; /* temporary numrec count */
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_TRACE_ARGI(cur, level);
+
+ tcur = NULL;
+
+ /* Get the index of the entry being deleted, check for nothing there. */
+ ptr = cur->bc_ptrs[level];
+ if (ptr == 0) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+ }
+
+ /* Get the buffer & block containing the record or key/ptr. */
+ block = xfs_btree_get_block(cur, level, &bp);
+ numrecs = xfs_btree_get_numrecs(block);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto error0;
+#endif
+
+ /* Fail if we're off the end of the block. */
+ if (ptr > numrecs) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 0;
+ return 0;
+ }
+
+ XFS_BTREE_STATS_INC(cur, delrec);
+ XFS_BTREE_STATS_ADD(cur, moves, numrecs - ptr);
+
+ /* Excise the entries being deleted. */
+ if (level > 0) {
+ /* It's a nonleaf. operate on keys and ptrs */
+ union xfs_btree_key *lkp;
+ union xfs_btree_ptr *lpp;
+
+ lkp = xfs_btree_key_addr(cur, ptr + 1, block);
+ lpp = xfs_btree_ptr_addr(cur, ptr + 1, block);
+
+#ifdef DEBUG
+ for (i = 0; i < numrecs - ptr; i++) {
+ error = xfs_btree_check_ptr(cur, lpp, i, level);
+ if (error)
+ goto error0;
+ }
+#endif
+
+ if (ptr < numrecs) {
+ xfs_btree_shift_keys(cur, lkp, -1, numrecs - ptr);
+ xfs_btree_shift_ptrs(cur, lpp, -1, numrecs - ptr);
+ xfs_btree_log_keys(cur, bp, ptr, numrecs - 1);
+ xfs_btree_log_ptrs(cur, bp, ptr, numrecs - 1);
+ }
+
+ /*
+ * If it's the first record in the block, we'll need to pass a
+ * key up to the next level (updkey).
+ */
+ if (ptr == 1)
+ keyp = xfs_btree_key_addr(cur, 1, block);
+ } else {
+ /* It's a leaf. operate on records */
+ if (ptr < numrecs) {
+ xfs_btree_shift_recs(cur,
+ xfs_btree_rec_addr(cur, ptr + 1, block),
+ -1, numrecs - ptr);
+ xfs_btree_log_recs(cur, bp, ptr, numrecs - 1);
+ }
+
+ /*
+ * If it's the first record in the block, we'll need a key
+ * structure to pass up to the next level (updkey).
+ */
+ if (ptr == 1) {
+ cur->bc_ops->init_key_from_rec(&key,
+ xfs_btree_rec_addr(cur, 1, block));
+ keyp = &key;
+ }
+ }
+
+ /*
+ * Decrement and log the number of entries in the block.
+ */
+ xfs_btree_set_numrecs(block, --numrecs);
+ xfs_btree_log_block(cur, bp, XFS_BB_NUMRECS);
+
+ /*
+ * If we are tracking the last record in the tree and
+ * we are at the far right edge of the tree, update it.
+ */
+ if (xfs_btree_is_lastrec(cur, block, level)) {
+ cur->bc_ops->update_lastrec(cur, block, NULL,
+ ptr, LASTREC_DELREC);
+ }
+
+ /*
+ * We're at the root level. First, shrink the root block in-memory.
+ * Try to get rid of the next level down. If we can't then there's
+ * nothing left to do.
+ */
+ if (level == cur->bc_nlevels - 1) {
+ if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) {
+ xfs_iroot_realloc(cur->bc_private.b.ip, -1,
+ cur->bc_private.b.whichfork);
+
+ error = xfs_btree_kill_iroot(cur);
+ if (error)
+ goto error0;
+
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ *stat = 1;
+ return 0;
+ }
+
+ /*
+ * If this is the root level, and there's only one entry left,
+ * and it's NOT the leaf level, then we can get rid of this
+ * level.
+ */
+ if (numrecs == 1 && level > 0) {
+ union xfs_btree_ptr *pp;
+ /*
+ * pp is still set to the first pointer in the block.
+ * Make it the new root of the btree.
+ */
+ pp = xfs_btree_ptr_addr(cur, 1, block);
+ error = cur->bc_ops->kill_root(cur, bp, level, pp);
+ if (error)
+ goto error0;
+ } else if (level > 0) {
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ }
+ *stat = 1;
+ return 0;
+ }
+
+ /*
+ * If we deleted the leftmost entry in the block, update the
+ * key values above us in the tree.
+ */
+ if (ptr == 1) {
+ error = xfs_btree_updkey(cur, keyp, level + 1);
+ if (error)
+ goto error0;
+ }
+
+ /*
+ * If the number of records remaining in the block is at least
+ * the minimum, we're done.
+ */
+ if (numrecs >= cur->bc_ops->get_minrecs(cur, level)) {
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ return 0;
+ }
+
+ /*
+ * Otherwise, we have to move some records around to keep the
+ * tree balanced. Look at the left and right sibling blocks to
+ * see if we can re-balance by moving only one record.
+ */
+ xfs_btree_get_sibling(cur, block, &rptr, XFS_BB_RIGHTSIB);
+ xfs_btree_get_sibling(cur, block, &lptr, XFS_BB_LEFTSIB);
+
+ if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) {
+ /*
+ * One child of root, need to get a chance to copy its contents
+ * into the root and delete it. Can't go up to next level,
+ * there's nothing to delete there.
+ */
+ if (xfs_btree_ptr_is_null(cur, &rptr) &&
+ xfs_btree_ptr_is_null(cur, &lptr) &&
+ level == cur->bc_nlevels - 2) {
+ error = xfs_btree_kill_iroot(cur);
+ if (!error)
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ return 0;
+ }
+ }
+
+ ASSERT(!xfs_btree_ptr_is_null(cur, &rptr) ||
+ !xfs_btree_ptr_is_null(cur, &lptr));
+
+ /*
+ * Duplicate the cursor so our btree manipulations here won't
+ * disrupt the next level up.
+ */
+ error = xfs_btree_dup_cursor(cur, &tcur);
+ if (error)
+ goto error0;
+
+ /*
+ * If there's a right sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (!xfs_btree_ptr_is_null(cur, &rptr)) {
+ /*
+ * Move the temp cursor to the last entry in the next block.
+ * Actually any entry but the first would suffice.
+ */
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ error = xfs_btree_increment(tcur, level, &i);
+ if (error)
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ /* Grab a pointer to the block. */
+ right = xfs_btree_get_block(tcur, level, &rbp);
+#ifdef DEBUG
+ error = xfs_btree_check_block(tcur, right, level, rbp);
+ if (error)
+ goto error0;
+#endif
+ /* Grab the current block number, for future use. */
+ xfs_btree_get_sibling(tcur, right, &cptr, XFS_BB_LEFTSIB);
+
+ /*
+ * If right block is full enough so that removing one entry
+ * won't make it too empty, and left-shifting an entry out
+ * of right to us works, we're done.
+ */
+ if (xfs_btree_get_numrecs(right) - 1 >=
+ cur->bc_ops->get_minrecs(tcur, level)) {
+ error = xfs_btree_lshift(tcur, level, &i);
+ if (error)
+ goto error0;
+ if (i) {
+ ASSERT(xfs_btree_get_numrecs(block) >=
+ cur->bc_ops->get_minrecs(tcur, level));
+
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ return 0;
+ }
+ }
+
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference, and fix up the temp cursor to point
+ * to our block again (last record).
+ */
+ rrecs = xfs_btree_get_numrecs(right);
+ if (!xfs_btree_ptr_is_null(cur, &lptr)) {
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ error = xfs_btree_decrement(tcur, level, &i);
+ if (error)
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ }
+
+ /*
+ * If there's a left sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (!xfs_btree_ptr_is_null(cur, &lptr)) {
+ /*
+ * Move the temp cursor to the first entry in the
+ * previous block.
+ */
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ error = xfs_btree_decrement(tcur, level, &i);
+ if (error)
+ goto error0;
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+
+ /* Grab a pointer to the block. */
+ left = xfs_btree_get_block(tcur, level, &lbp);
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, left, level, lbp);
+ if (error)
+ goto error0;
+#endif
+ /* Grab the current block number, for future use. */
+ xfs_btree_get_sibling(tcur, left, &cptr, XFS_BB_RIGHTSIB);
+
+ /*
+ * If left block is full enough so that removing one entry
+ * won't make it too empty, and right-shifting an entry out
+ * of left to us works, we're done.
+ */
+ if (xfs_btree_get_numrecs(left) - 1 >=
+ cur->bc_ops->get_minrecs(tcur, level)) {
+ error = xfs_btree_rshift(tcur, level, &i);
+ if (error)
+ goto error0;
+ if (i) {
+ ASSERT(xfs_btree_get_numrecs(block) >=
+ cur->bc_ops->get_minrecs(tcur, level));
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+ if (level == 0)
+ cur->bc_ptrs[0]++;
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = 1;
+ return 0;
+ }
+ }
+
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference.
+ */
+ lrecs = xfs_btree_get_numrecs(left);
+ }
+
+ /* Delete the temp cursor, we're done with it. */
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+
+ /* If here, we need to do a join to keep the tree balanced. */
+ ASSERT(!xfs_btree_ptr_is_null(cur, &cptr));
+
+ if (!xfs_btree_ptr_is_null(cur, &lptr) &&
+ lrecs + xfs_btree_get_numrecs(block) <=
+ cur->bc_ops->get_maxrecs(cur, level)) {
+ /*
+ * Set "right" to be the starting block,
+ * "left" to be the left neighbor.
+ */
+ rptr = cptr;
+ right = block;
+ rbp = bp;
+ error = xfs_btree_read_buf_block(cur, &lptr, level,
+ 0, &left, &lbp);
+ if (error)
+ goto error0;
+
+ /*
+ * If that won't work, see if we can join with the right neighbor block.
+ */
+ } else if (!xfs_btree_ptr_is_null(cur, &rptr) &&
+ rrecs + xfs_btree_get_numrecs(block) <=
+ cur->bc_ops->get_maxrecs(cur, level)) {
+ /*
+ * Set "left" to be the starting block,
+ * "right" to be the right neighbor.
+ */
+ lptr = cptr;
+ left = block;
+ lbp = bp;
+ error = xfs_btree_read_buf_block(cur, &rptr, level,
+ 0, &right, &rbp);
+ if (error)
+ goto error0;
+
+ /*
+ * Otherwise, we can't fix the imbalance.
+ * Just return. This is probably a logic error, but it's not fatal.
+ */
+ } else {
+ error = xfs_btree_dec_cursor(cur, level, stat);
+ if (error)
+ goto error0;
+ return 0;
+ }
+
+ rrecs = xfs_btree_get_numrecs(right);
+ lrecs = xfs_btree_get_numrecs(left);
+
+ /*
+ * We're now going to join "left" and "right" by moving all the stuff
+ * in "right" to "left" and deleting "right".
+ */
+ XFS_BTREE_STATS_ADD(cur, moves, rrecs);
+ if (level > 0) {
+ /* It's a non-leaf. Move keys and pointers. */
+ union xfs_btree_key *lkp; /* left btree key */
+ union xfs_btree_ptr *lpp; /* left address pointer */
+ union xfs_btree_key *rkp; /* right btree key */
+ union xfs_btree_ptr *rpp; /* right address pointer */
+
+ lkp = xfs_btree_key_addr(cur, lrecs + 1, left);
+ lpp = xfs_btree_ptr_addr(cur, lrecs + 1, left);
+ rkp = xfs_btree_key_addr(cur, 1, right);
+ rpp = xfs_btree_ptr_addr(cur, 1, right);
+#ifdef DEBUG
+ for (i = 1; i < rrecs; i++) {
+ error = xfs_btree_check_ptr(cur, rpp, i, level);
+ if (error)
+ goto error0;
+ }
+#endif
+ xfs_btree_copy_keys(cur, lkp, rkp, rrecs);
+ xfs_btree_copy_ptrs(cur, lpp, rpp, rrecs);
+
+ xfs_btree_log_keys(cur, lbp, lrecs + 1, lrecs + rrecs);
+ xfs_btree_log_ptrs(cur, lbp, lrecs + 1, lrecs + rrecs);
+ } else {
+ /* It's a leaf. Move records. */
+ union xfs_btree_rec *lrp; /* left record pointer */
+ union xfs_btree_rec *rrp; /* right record pointer */
+
+ lrp = xfs_btree_rec_addr(cur, lrecs + 1, left);
+ rrp = xfs_btree_rec_addr(cur, 1, right);
+
+ xfs_btree_copy_recs(cur, lrp, rrp, rrecs);
+ xfs_btree_log_recs(cur, lbp, lrecs + 1, lrecs + rrecs);
+ }
+
+ XFS_BTREE_STATS_INC(cur, join);
+
+ /*
+ * Fix up the the number of records and right block pointer in the
+ * surviving block, and log it.
+ */
+ xfs_btree_set_numrecs(left, lrecs + rrecs);
+ xfs_btree_get_sibling(cur, right, &cptr, XFS_BB_RIGHTSIB),
+ xfs_btree_set_sibling(cur, left, &cptr, XFS_BB_RIGHTSIB);
+ xfs_btree_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+
+ /* If there is a right sibling, point it to the remaining block. */
+ xfs_btree_get_sibling(cur, left, &cptr, XFS_BB_RIGHTSIB);
+ if (!xfs_btree_ptr_is_null(cur, &cptr)) {
+ error = xfs_btree_read_buf_block(cur, &cptr, level,
+ 0, &rrblock, &rrbp);
+ if (error)
+ goto error0;
+ xfs_btree_set_sibling(cur, rrblock, &lptr, XFS_BB_LEFTSIB);
+ xfs_btree_log_block(cur, rrbp, XFS_BB_LEFTSIB);
+ }
+
+ /* Free the deleted block. */
+ error = cur->bc_ops->free_block(cur, rbp);
+ if (error)
+ goto error0;
+ XFS_BTREE_STATS_INC(cur, free);
+
+ /*
+ * If we joined with the left neighbor, set the buffer in the
+ * cursor to the left block, and fix up the index.
+ */
+ if (bp != lbp) {
+ cur->bc_bufs[level] = lbp;
+ cur->bc_ptrs[level] += lrecs;
+ cur->bc_ra[level] = 0;
+ }
+ /*
+ * If we joined with the right neighbor and there's a level above
+ * us, increment the cursor at that level.
+ */
+ else if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) ||
+ (level + 1 < cur->bc_nlevels)) {
+ error = xfs_btree_increment(cur, level + 1, &i);
+ if (error)
+ goto error0;
+ }
+
+ /*
+ * Readjust the ptr at this level if it's not a leaf, since it's
+ * still pointing at the deletion point, which makes the cursor
+ * inconsistent. If this makes the ptr 0, the caller fixes it up.
+ * We can't use decrement because it would change the next level up.
+ */
+ if (level > 0)
+ cur->bc_ptrs[level]--;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ /* Return value means the next level up has something to do. */
+ *stat = 2;
+ return 0;
+
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ if (tcur)
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Delete the record pointed to by cur.
+ * The cursor refers to the place where the record was (could be inserted)
+ * when the operation returns.
+ */
+int /* error */
+xfs_btree_delete(
+ struct xfs_btree_cur *cur,
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int level;
+ int i;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+ /*
+ * Go up the tree, starting at leaf level.
+ *
+ * If 2 is returned then a join was done; go to the next level.
+ * Otherwise we are done.
+ */
+ for (level = 0, i = 2; i == 2; level++) {
+ error = xfs_btree_delrec(cur, level, &i);
+ if (error)
+ goto error0;
+ }
+
+ if (i == 0) {
+ for (level = 1; level < cur->bc_nlevels; level++) {
+ if (cur->bc_ptrs[level] == 0) {
+ error = xfs_btree_decrement(cur, level, &i);
+ if (error)
+ goto error0;
+ break;
+ }
+ }
+ }
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+ *stat = i;
+ return 0;
+error0:
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+ return error;
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_btree_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ union xfs_btree_rec **recp, /* output: btree record */
+ int *stat) /* output: success/failure */
+{
+ struct xfs_btree_block *block; /* btree block */
+ struct xfs_buf *bp; /* buffer pointer */
+ int ptr; /* record number */
+#ifdef DEBUG
+ int error; /* error return value */
+#endif
+
+ ptr = cur->bc_ptrs[0];
+ block = xfs_btree_get_block(cur, 0, &bp);
+
+#ifdef DEBUG
+ error = xfs_btree_check_block(cur, block, 0, bp);
+ if (error)
+ return error;
+#endif
+
+ /*
+ * Off the right end or left end, return failure.
+ */
+ if (ptr > xfs_btree_get_numrecs(block) || ptr <= 0) {
+ *stat = 0;
+ return 0;
+ }
+
+ /*
+ * Point to the record and extract its data.
+ */
+ *recp = xfs_btree_rec_addr(cur, ptr, block);
+ *stat = 1;
+ return 0;
+}
diff --git a/fs/xfs/xfs_btree.h b/fs/xfs/xfs_btree.h
index 1f528a2a3754..789fffdf8b2f 100644
--- a/fs/xfs/xfs_btree.h
+++ b/fs/xfs/xfs_btree.h
@@ -39,39 +39,19 @@ extern kmem_zone_t *xfs_btree_cur_zone;
#define XFS_BTNUM_INO ((xfs_btnum_t)XFS_BTNUM_INOi)
/*
- * Short form header: space allocation btrees.
- */
-typedef struct xfs_btree_sblock {
- __be32 bb_magic; /* magic number for block type */
- __be16 bb_level; /* 0 is a leaf */
- __be16 bb_numrecs; /* current # of data records */
- __be32 bb_leftsib; /* left sibling block or NULLAGBLOCK */
- __be32 bb_rightsib; /* right sibling block or NULLAGBLOCK */
-} xfs_btree_sblock_t;
-
-/*
- * Long form header: bmap btrees.
- */
-typedef struct xfs_btree_lblock {
- __be32 bb_magic; /* magic number for block type */
- __be16 bb_level; /* 0 is a leaf */
- __be16 bb_numrecs; /* current # of data records */
- __be64 bb_leftsib; /* left sibling block or NULLDFSBNO */
- __be64 bb_rightsib; /* right sibling block or NULLDFSBNO */
-} xfs_btree_lblock_t;
-
-/*
- * Combined header and structure, used by common code.
+ * Generic btree header.
+ *
+ * This is a comination of the actual format used on disk for short and long
+ * format btrees. The first three fields are shared by both format, but
+ * the pointers are different and should be used with care.
+ *
+ * To get the size of the actual short or long form headers please use
+ * the size macros below. Never use sizeof(xfs_btree_block).
*/
-typedef struct xfs_btree_hdr
-{
+struct xfs_btree_block {
__be32 bb_magic; /* magic number for block type */
__be16 bb_level; /* 0 is a leaf */
__be16 bb_numrecs; /* current # of data records */
-} xfs_btree_hdr_t;
-
-typedef struct xfs_btree_block {
- xfs_btree_hdr_t bb_h; /* header */
union {
struct {
__be32 bb_leftsib;
@@ -82,7 +62,36 @@ typedef struct xfs_btree_block {
__be64 bb_rightsib;
} l; /* long form pointers */
} bb_u; /* rest */
-} xfs_btree_block_t;
+};
+
+#define XFS_BTREE_SBLOCK_LEN 16 /* size of a short form block */
+#define XFS_BTREE_LBLOCK_LEN 24 /* size of a long form block */
+
+
+/*
+ * Generic key, ptr and record wrapper structures.
+ *
+ * These are disk format structures, and are converted where necessary
+ * by the btree specific code that needs to interpret them.
+ */
+union xfs_btree_ptr {
+ __be32 s; /* short form ptr */
+ __be64 l; /* long form ptr */
+};
+
+union xfs_btree_key {
+ xfs_bmbt_key_t bmbt;
+ xfs_bmdr_key_t bmbr; /* bmbt root block */
+ xfs_alloc_key_t alloc;
+ xfs_inobt_key_t inobt;
+};
+
+union xfs_btree_rec {
+ xfs_bmbt_rec_t bmbt;
+ xfs_bmdr_rec_t bmbr; /* bmbt root block */
+ xfs_alloc_rec_t alloc;
+ xfs_inobt_rec_t inobt;
+};
/*
* For logging record fields.
@@ -96,46 +105,131 @@ typedef struct xfs_btree_block {
#define XFS_BB_ALL_BITS ((1 << XFS_BB_NUM_BITS) - 1)
/*
- * Boolean to select which form of xfs_btree_block_t.bb_u to use.
- */
-#define XFS_BTREE_LONG_PTRS(btnum) ((btnum) == XFS_BTNUM_BMAP)
-
-/*
* Magic numbers for btree blocks.
*/
extern const __uint32_t xfs_magics[];
/*
- * Maximum and minimum records in a btree block.
- * Given block size, type prefix, and leaf flag (0 or 1).
- * The divisor below is equivalent to lf ? (e1) : (e2) but that produces
- * compiler warnings.
- */
-#define XFS_BTREE_BLOCK_MAXRECS(bsz,t,lf) \
- ((int)(((bsz) - (uint)sizeof(t ## _block_t)) / \
- (((lf) * (uint)sizeof(t ## _rec_t)) + \
- ((1 - (lf)) * \
- ((uint)sizeof(t ## _key_t) + (uint)sizeof(t ## _ptr_t))))))
-#define XFS_BTREE_BLOCK_MINRECS(bsz,t,lf) \
- (XFS_BTREE_BLOCK_MAXRECS(bsz,t,lf) / 2)
-
-/*
- * Record, key, and pointer address calculation macros.
- * Given block size, type prefix, block pointer, and index of requested entry
- * (first entry numbered 1).
- */
-#define XFS_BTREE_REC_ADDR(t,bb,i) \
- ((t ## _rec_t *)((char *)(bb) + sizeof(t ## _block_t) + \
- ((i) - 1) * sizeof(t ## _rec_t)))
-#define XFS_BTREE_KEY_ADDR(t,bb,i) \
- ((t ## _key_t *)((char *)(bb) + sizeof(t ## _block_t) + \
- ((i) - 1) * sizeof(t ## _key_t)))
-#define XFS_BTREE_PTR_ADDR(t,bb,i,mxr) \
- ((t ## _ptr_t *)((char *)(bb) + sizeof(t ## _block_t) + \
- (mxr) * sizeof(t ## _key_t) + ((i) - 1) * sizeof(t ## _ptr_t)))
+ * Generic stats interface
+ */
+#define __XFS_BTREE_STATS_INC(type, stat) \
+ XFS_STATS_INC(xs_ ## type ## _2_ ## stat)
+#define XFS_BTREE_STATS_INC(cur, stat) \
+do { \
+ switch (cur->bc_btnum) { \
+ case XFS_BTNUM_BNO: __XFS_BTREE_STATS_INC(abtb, stat); break; \
+ case XFS_BTNUM_CNT: __XFS_BTREE_STATS_INC(abtc, stat); break; \
+ case XFS_BTNUM_BMAP: __XFS_BTREE_STATS_INC(bmbt, stat); break; \
+ case XFS_BTNUM_INO: __XFS_BTREE_STATS_INC(ibt, stat); break; \
+ case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
+ } \
+} while (0)
+
+#define __XFS_BTREE_STATS_ADD(type, stat, val) \
+ XFS_STATS_ADD(xs_ ## type ## _2_ ## stat, val)
+#define XFS_BTREE_STATS_ADD(cur, stat, val) \
+do { \
+ switch (cur->bc_btnum) { \
+ case XFS_BTNUM_BNO: __XFS_BTREE_STATS_ADD(abtb, stat, val); break; \
+ case XFS_BTNUM_CNT: __XFS_BTREE_STATS_ADD(abtc, stat, val); break; \
+ case XFS_BTNUM_BMAP: __XFS_BTREE_STATS_ADD(bmbt, stat, val); break; \
+ case XFS_BTNUM_INO: __XFS_BTREE_STATS_ADD(ibt, stat, val); break; \
+ case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
+ } \
+} while (0)
#define XFS_BTREE_MAXLEVELS 8 /* max of all btrees */
+struct xfs_btree_ops {
+ /* size of the key and record structures */
+ size_t key_len;
+ size_t rec_len;
+
+ /* cursor operations */
+ struct xfs_btree_cur *(*dup_cursor)(struct xfs_btree_cur *);
+ void (*update_cursor)(struct xfs_btree_cur *src,
+ struct xfs_btree_cur *dst);
+
+ /* update btree root pointer */
+ void (*set_root)(struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *nptr, int level_change);
+ int (*kill_root)(struct xfs_btree_cur *cur, struct xfs_buf *bp,
+ int level, union xfs_btree_ptr *newroot);
+
+ /* block allocation / freeing */
+ int (*alloc_block)(struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *start_bno,
+ union xfs_btree_ptr *new_bno,
+ int length, int *stat);
+ int (*free_block)(struct xfs_btree_cur *cur, struct xfs_buf *bp);
+
+ /* update last record information */
+ void (*update_lastrec)(struct xfs_btree_cur *cur,
+ struct xfs_btree_block *block,
+ union xfs_btree_rec *rec,
+ int ptr, int reason);
+
+ /* records in block/level */
+ int (*get_minrecs)(struct xfs_btree_cur *cur, int level);
+ int (*get_maxrecs)(struct xfs_btree_cur *cur, int level);
+
+ /* records on disk. Matter for the root in inode case. */
+ int (*get_dmaxrecs)(struct xfs_btree_cur *cur, int level);
+
+ /* init values of btree structures */
+ void (*init_key_from_rec)(union xfs_btree_key *key,
+ union xfs_btree_rec *rec);
+ void (*init_rec_from_key)(union xfs_btree_key *key,
+ union xfs_btree_rec *rec);
+ void (*init_rec_from_cur)(struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec);
+ void (*init_ptr_from_cur)(struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr);
+
+ /* difference between key value and cursor value */
+ __int64_t (*key_diff)(struct xfs_btree_cur *cur,
+ union xfs_btree_key *key);
+
+#ifdef DEBUG
+ /* check that k1 is lower than k2 */
+ int (*keys_inorder)(struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2);
+
+ /* check that r1 is lower than r2 */
+ int (*recs_inorder)(struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2);
+#endif
+
+ /* btree tracing */
+#ifdef XFS_BTREE_TRACE
+ void (*trace_enter)(struct xfs_btree_cur *, const char *,
+ char *, int, int, __psunsigned_t,
+ __psunsigned_t, __psunsigned_t,
+ __psunsigned_t, __psunsigned_t,
+ __psunsigned_t, __psunsigned_t,
+ __psunsigned_t, __psunsigned_t,
+ __psunsigned_t, __psunsigned_t);
+ void (*trace_cursor)(struct xfs_btree_cur *, __uint32_t *,
+ __uint64_t *, __uint64_t *);
+ void (*trace_key)(struct xfs_btree_cur *,
+ union xfs_btree_key *, __uint64_t *,
+ __uint64_t *);
+ void (*trace_record)(struct xfs_btree_cur *,
+ union xfs_btree_rec *, __uint64_t *,
+ __uint64_t *, __uint64_t *);
+#endif
+};
+
+/*
+ * Reasons for the update_lastrec method to be called.
+ */
+#define LASTREC_UPDATE 0
+#define LASTREC_INSREC 1
+#define LASTREC_DELREC 2
+
+
/*
* Btree cursor structure.
* This collects all information needed by the btree code in one place.
@@ -144,6 +238,8 @@ typedef struct xfs_btree_cur
{
struct xfs_trans *bc_tp; /* transaction we're in, if any */
struct xfs_mount *bc_mp; /* file system mount struct */
+ const struct xfs_btree_ops *bc_ops;
+ uint bc_flags; /* btree features - below */
union {
xfs_alloc_rec_incore_t a;
xfs_bmbt_irec_t b;
@@ -175,94 +271,40 @@ typedef struct xfs_btree_cur
} bc_private; /* per-btree type data */
} xfs_btree_cur_t;
+/* cursor flags */
+#define XFS_BTREE_LONG_PTRS (1<<0) /* pointers are 64bits long */
+#define XFS_BTREE_ROOT_IN_INODE (1<<1) /* root may be variable size */
+#define XFS_BTREE_LASTREC_UPDATE (1<<2) /* track last rec externally */
+
+
#define XFS_BTREE_NOERROR 0
#define XFS_BTREE_ERROR 1
/*
* Convert from buffer to btree block header.
*/
-#define XFS_BUF_TO_BLOCK(bp) ((xfs_btree_block_t *)XFS_BUF_PTR(bp))
-#define XFS_BUF_TO_LBLOCK(bp) ((xfs_btree_lblock_t *)XFS_BUF_PTR(bp))
-#define XFS_BUF_TO_SBLOCK(bp) ((xfs_btree_sblock_t *)XFS_BUF_PTR(bp))
+#define XFS_BUF_TO_BLOCK(bp) ((struct xfs_btree_block *)XFS_BUF_PTR(bp))
-#ifdef __KERNEL__
-
-#ifdef DEBUG
/*
- * Debug routine: check that block header is ok.
+ * Check that block header is ok.
*/
-void
+int
xfs_btree_check_block(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_block_t *block, /* generic btree block pointer */
- int level, /* level of the btree block */
- struct xfs_buf *bp); /* buffer containing block, if any */
-
-/*
- * Debug routine: check that keys are in the right order.
- */
-void
-xfs_btree_check_key(
- xfs_btnum_t btnum, /* btree identifier */
- void *ak1, /* pointer to left (lower) key */
- void *ak2); /* pointer to right (higher) key */
-
-/*
- * Debug routine: check that records are in the right order.
- */
-void
-xfs_btree_check_rec(
- xfs_btnum_t btnum, /* btree identifier */
- void *ar1, /* pointer to left (lower) record */
- void *ar2); /* pointer to right (higher) record */
-#else
-#define xfs_btree_check_block(a,b,c,d)
-#define xfs_btree_check_key(a,b,c)
-#define xfs_btree_check_rec(a,b,c)
-#endif /* DEBUG */
-
-/*
- * Checking routine: check that long form block header is ok.
- */
-int /* error (0 or EFSCORRUPTED) */
-xfs_btree_check_lblock(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_lblock_t *block, /* btree long form block pointer */
+ struct xfs_btree_cur *cur, /* btree cursor */
+ struct xfs_btree_block *block, /* generic btree block pointer */
int level, /* level of the btree block */
struct xfs_buf *bp); /* buffer containing block, if any */
/*
- * Checking routine: check that (long) pointer is ok.
+ * Check that (long) pointer is ok.
*/
int /* error (0 or EFSCORRUPTED) */
xfs_btree_check_lptr(
- xfs_btree_cur_t *cur, /* btree cursor */
+ struct xfs_btree_cur *cur, /* btree cursor */
xfs_dfsbno_t ptr, /* btree block disk address */
int level); /* btree block level */
-#define xfs_btree_check_lptr_disk(cur, ptr, level) \
- xfs_btree_check_lptr(cur, be64_to_cpu(ptr), level)
-
-/*
- * Checking routine: check that short form block header is ok.
- */
-int /* error (0 or EFSCORRUPTED) */
-xfs_btree_check_sblock(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_btree_sblock_t *block, /* btree short form block pointer */
- int level, /* level of the btree block */
- struct xfs_buf *bp); /* buffer containing block */
-
-/*
- * Checking routine: check that (short) pointer is ok.
- */
-int /* error (0 or EFSCORRUPTED) */
-xfs_btree_check_sptr(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agblock_t ptr, /* btree block disk address */
- int level); /* btree block level */
-
/*
* Delete the btree cursor.
*/
@@ -281,15 +323,6 @@ xfs_btree_dup_cursor(
xfs_btree_cur_t **ncur);/* output cursor */
/*
- * Change the cursor to point to the first record in the current block
- * at the given level. Other levels are unaffected.
- */
-int /* success=1, failure=0 */
-xfs_btree_firstrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level); /* level to change */
-
-/*
* Get a buffer for the block, return it with no data read.
* Long-form addressing.
*/
@@ -313,20 +346,6 @@ xfs_btree_get_bufs(
uint lock); /* lock flags for get_buf */
/*
- * Allocate a new btree cursor.
- * The cursor is either for allocation (A) or bmap (B).
- */
-xfs_btree_cur_t * /* new btree cursor */
-xfs_btree_init_cursor(
- struct xfs_mount *mp, /* file system mount point */
- struct xfs_trans *tp, /* transaction pointer */
- struct xfs_buf *agbp, /* (A only) buffer for agf structure */
- xfs_agnumber_t agno, /* (A only) allocation group number */
- xfs_btnum_t btnum, /* btree identifier */
- struct xfs_inode *ip, /* (B only) inode owning the btree */
- int whichfork); /* (B only) data/attr fork */
-
-/*
* Check for the cursor referring to the last block at the given level.
*/
int /* 1=is last block, 0=not last block */
@@ -335,15 +354,6 @@ xfs_btree_islastblock(
int level); /* level to check */
/*
- * Change the cursor to point to the last record in the current block
- * at the given level. Other levels are unaffected.
- */
-int /* success=1, failure=0 */
-xfs_btree_lastrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level); /* level to change */
-
-/*
* Compute first and last byte offsets for the fields given.
* Interprets the offsets table, which contains struct field offsets.
*/
@@ -404,39 +414,53 @@ xfs_btree_reada_bufs(
xfs_extlen_t count); /* count of filesystem blocks */
/*
- * Read-ahead btree blocks, at the given level.
- * Bits in lr are set from XFS_BTCUR_{LEFT,RIGHT}RA.
+ * Set the buffer for level "lev" in the cursor to bp, releasing
+ * any previous buffer.
*/
-int /* readahead block count */
-xfs_btree_readahead_core(
+void
+xfs_btree_setbuf(
xfs_btree_cur_t *cur, /* btree cursor */
int lev, /* level in btree */
- int lr); /* left/right bits */
+ struct xfs_buf *bp); /* new buffer to set */
-static inline int /* readahead block count */
-xfs_btree_readahead(
- xfs_btree_cur_t *cur, /* btree cursor */
- int lev, /* level in btree */
- int lr) /* left/right bits */
-{
- if ((cur->bc_ra[lev] | lr) == cur->bc_ra[lev])
- return 0;
- return xfs_btree_readahead_core(cur, lev, lr);
-}
+/*
+ * Common btree core entry points.
+ */
+int xfs_btree_increment(struct xfs_btree_cur *, int, int *);
+int xfs_btree_decrement(struct xfs_btree_cur *, int, int *);
+int xfs_btree_lookup(struct xfs_btree_cur *, xfs_lookup_t, int *);
+int xfs_btree_update(struct xfs_btree_cur *, union xfs_btree_rec *);
+int xfs_btree_new_iroot(struct xfs_btree_cur *, int *, int *);
+int xfs_btree_kill_iroot(struct xfs_btree_cur *);
+int xfs_btree_insert(struct xfs_btree_cur *, int *);
+int xfs_btree_delete(struct xfs_btree_cur *, int *);
+int xfs_btree_get_rec(struct xfs_btree_cur *, union xfs_btree_rec **, int *);
+/*
+ * Internal btree helpers also used by xfs_bmap.c.
+ */
+void xfs_btree_log_block(struct xfs_btree_cur *, struct xfs_buf *, int);
+void xfs_btree_log_recs(struct xfs_btree_cur *, struct xfs_buf *, int, int);
/*
- * Set the buffer for level "lev" in the cursor to bp, releasing
- * any previous buffer.
+ * Helpers.
*/
-void
-xfs_btree_setbuf(
- xfs_btree_cur_t *cur, /* btree cursor */
- int lev, /* level in btree */
- struct xfs_buf *bp); /* new buffer to set */
+static inline int xfs_btree_get_numrecs(struct xfs_btree_block *block)
+{
+ return be16_to_cpu(block->bb_numrecs);
+}
+
+static inline void xfs_btree_set_numrecs(struct xfs_btree_block *block,
+ __uint16_t numrecs)
+{
+ block->bb_numrecs = cpu_to_be16(numrecs);
+}
-#endif /* __KERNEL__ */
+static inline int xfs_btree_get_level(struct xfs_btree_block *block)
+{
+ return be16_to_cpu(block->bb_level);
+}
/*
diff --git a/fs/xfs/xfs_btree_trace.c b/fs/xfs/xfs_btree_trace.c
new file mode 100644
index 000000000000..44ff942a0fda
--- /dev/null
+++ b/fs/xfs/xfs_btree_trace.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2008 Silicon Graphics, 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.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "xfs.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
+
+STATIC void
+xfs_btree_trace_ptr(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr ptr,
+ __psunsigned_t *high,
+ __psunsigned_t *low)
+{
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ __u64 val = be64_to_cpu(ptr.l);
+ *high = val >> 32;
+ *low = (int)val;
+ } else {
+ *high = 0;
+ *low = be32_to_cpu(ptr.s);
+ }
+}
+
+/*
+ * Add a trace buffer entry for arguments, for a buffer & 1 integer arg.
+ */
+void
+xfs_btree_trace_argbi(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *b,
+ int i,
+ int line)
+{
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGBI,
+ line, (__psunsigned_t)b, i, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for a buffer & 2 integer args.
+ */
+void
+xfs_btree_trace_argbii(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *b,
+ int i0,
+ int i1,
+ int line)
+{
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGBII,
+ line, (__psunsigned_t)b, i0, i1, 0, 0, 0, 0,
+ 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for 3 block-length args
+ * and an integer arg.
+ */
+void
+xfs_btree_trace_argfffi(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ xfs_dfiloff_t o,
+ xfs_dfsbno_t b,
+ xfs_dfilblks_t i,
+ int j,
+ int line)
+{
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGFFFI,
+ line,
+ o >> 32, (int)o,
+ b >> 32, (int)b,
+ i >> 32, (int)i,
+ (int)j, 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for one integer arg.
+ */
+void
+xfs_btree_trace_argi(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ int i,
+ int line)
+{
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGI,
+ line, i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, fsblock, key.
+ */
+void
+xfs_btree_trace_argipk(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ int i,
+ union xfs_btree_ptr ptr,
+ union xfs_btree_key *key,
+ int line)
+{
+ __psunsigned_t high, low;
+ __uint64_t l0, l1;
+
+ xfs_btree_trace_ptr(cur, ptr, &high, &low);
+ cur->bc_ops->trace_key(cur, key, &l0, &l1);
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGIPK,
+ line, i, high, low,
+ l0 >> 32, (int)l0,
+ l1 >> 32, (int)l1,
+ 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, fsblock, rec.
+ */
+void
+xfs_btree_trace_argipr(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ int i,
+ union xfs_btree_ptr ptr,
+ union xfs_btree_rec *rec,
+ int line)
+{
+ __psunsigned_t high, low;
+ __uint64_t l0, l1, l2;
+
+ xfs_btree_trace_ptr(cur, ptr, &high, &low);
+ cur->bc_ops->trace_record(cur, rec, &l0, &l1, &l2);
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGIPR,
+ line, i,
+ high, low,
+ l0 >> 32, (int)l0,
+ l1 >> 32, (int)l1,
+ l2 >> 32, (int)l2,
+ 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, key.
+ */
+void
+xfs_btree_trace_argik(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ int i,
+ union xfs_btree_key *key,
+ int line)
+{
+ __uint64_t l0, l1;
+
+ cur->bc_ops->trace_key(cur, key, &l0, &l1);
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGIK,
+ line, i,
+ l0 >> 32, (int)l0,
+ l1 >> 32, (int)l1,
+ 0, 0, 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for record.
+ */
+void
+xfs_btree_trace_argr(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ int line)
+{
+ __uint64_t l0, l1, l2;
+
+ cur->bc_ops->trace_record(cur, rec, &l0, &l1, &l2);
+ cur->bc_ops->trace_enter(cur, func, XBT_ARGS, XFS_BTREE_KTRACE_ARGR,
+ line,
+ l0 >> 32, (int)l0,
+ l1 >> 32, (int)l1,
+ l2 >> 32, (int)l2,
+ 0, 0, 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for the cursor/operation.
+ */
+void
+xfs_btree_trace_cursor(
+ const char *func,
+ struct xfs_btree_cur *cur,
+ int type,
+ int line)
+{
+ __uint32_t s0;
+ __uint64_t l0, l1;
+ char *s;
+
+ switch (type) {
+ case XBT_ARGS:
+ s = "args";
+ break;
+ case XBT_ENTRY:
+ s = "entry";
+ break;
+ case XBT_ERROR:
+ s = "error";
+ break;
+ case XBT_EXIT:
+ s = "exit";
+ break;
+ default:
+ s = "unknown";
+ break;
+ }
+
+ cur->bc_ops->trace_cursor(cur, &s0, &l0, &l1);
+ cur->bc_ops->trace_enter(cur, func, s, XFS_BTREE_KTRACE_CUR, line,
+ s0,
+ l0 >> 32, (int)l0,
+ l1 >> 32, (int)l1,
+ (__psunsigned_t)cur->bc_bufs[0],
+ (__psunsigned_t)cur->bc_bufs[1],
+ (__psunsigned_t)cur->bc_bufs[2],
+ (__psunsigned_t)cur->bc_bufs[3],
+ (cur->bc_ptrs[0] << 16) | cur->bc_ptrs[1],
+ (cur->bc_ptrs[2] << 16) | cur->bc_ptrs[3]);
+}
diff --git a/fs/xfs/xfs_btree_trace.h b/fs/xfs/xfs_btree_trace.h
new file mode 100644
index 000000000000..b3f5eb3c3c6c
--- /dev/null
+++ b/fs/xfs/xfs_btree_trace.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2008 Silicon Graphics, 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.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __XFS_BTREE_TRACE_H__
+#define __XFS_BTREE_TRACE_H__
+
+struct xfs_btree_cur;
+struct xfs_buf;
+
+
+/*
+ * Trace hooks.
+ * i,j = integer (32 bit)
+ * b = btree block buffer (xfs_buf_t)
+ * p = btree ptr
+ * r = btree record
+ * k = btree key
+ */
+
+#ifdef XFS_BTREE_TRACE
+
+/*
+ * Trace buffer entry types.
+ */
+#define XFS_BTREE_KTRACE_ARGBI 1
+#define XFS_BTREE_KTRACE_ARGBII 2
+#define XFS_BTREE_KTRACE_ARGFFFI 3
+#define XFS_BTREE_KTRACE_ARGI 4
+#define XFS_BTREE_KTRACE_ARGIPK 5
+#define XFS_BTREE_KTRACE_ARGIPR 6
+#define XFS_BTREE_KTRACE_ARGIK 7
+#define XFS_BTREE_KTRACE_ARGR 8
+#define XFS_BTREE_KTRACE_CUR 9
+
+/*
+ * Sub-types for cursor traces.
+ */
+#define XBT_ARGS 0
+#define XBT_ENTRY 1
+#define XBT_ERROR 2
+#define XBT_EXIT 3
+
+void xfs_btree_trace_argbi(const char *, struct xfs_btree_cur *,
+ struct xfs_buf *, int, int);
+void xfs_btree_trace_argbii(const char *, struct xfs_btree_cur *,
+ struct xfs_buf *, int, int, int);
+void xfs_btree_trace_argfffi(const char *, struct xfs_btree_cur *,
+ xfs_dfiloff_t, xfs_dfsbno_t, xfs_dfilblks_t, int, int);
+void xfs_btree_trace_argi(const char *, struct xfs_btree_cur *, int, int);
+void xfs_btree_trace_argipk(const char *, struct xfs_btree_cur *, int,
+ union xfs_btree_ptr, union xfs_btree_key *, int);
+void xfs_btree_trace_argipr(const char *, struct xfs_btree_cur *, int,
+ union xfs_btree_ptr, union xfs_btree_rec *, int);
+void xfs_btree_trace_argik(const char *, struct xfs_btree_cur *, int,
+ union xfs_btree_key *, int);
+void xfs_btree_trace_argr(const char *, struct xfs_btree_cur *,
+ union xfs_btree_rec *, int);
+void xfs_btree_trace_cursor(const char *, struct xfs_btree_cur *, int, int);
+
+
+#define XFS_ALLOCBT_TRACE_SIZE 4096 /* size of global trace buffer */
+extern ktrace_t *xfs_allocbt_trace_buf;
+
+#define XFS_INOBT_TRACE_SIZE 4096 /* size of global trace buffer */
+extern ktrace_t *xfs_inobt_trace_buf;
+
+#define XFS_BMBT_TRACE_SIZE 4096 /* size of global trace buffer */
+#define XFS_BMBT_KTRACE_SIZE 32 /* size of per-inode trace buffer */
+extern ktrace_t *xfs_bmbt_trace_buf;
+
+
+#define XFS_BTREE_TRACE_ARGBI(c, b, i) \
+ xfs_btree_trace_argbi(__func__, c, b, i, __LINE__)
+#define XFS_BTREE_TRACE_ARGBII(c, b, i, j) \
+ xfs_btree_trace_argbii(__func__, c, b, i, j, __LINE__)
+#define XFS_BTREE_TRACE_ARGFFFI(c, o, b, i, j) \
+ xfs_btree_trace_argfffi(__func__, c, o, b, i, j, __LINE__)
+#define XFS_BTREE_TRACE_ARGI(c, i) \
+ xfs_btree_trace_argi(__func__, c, i, __LINE__)
+#define XFS_BTREE_TRACE_ARGIPK(c, i, p, k) \
+ xfs_btree_trace_argipk(__func__, c, i, p, k, __LINE__)
+#define XFS_BTREE_TRACE_ARGIPR(c, i, p, r) \
+ xfs_btree_trace_argipr(__func__, c, i, p, r, __LINE__)
+#define XFS_BTREE_TRACE_ARGIK(c, i, k) \
+ xfs_btree_trace_argik(__func__, c, i, k, __LINE__)
+#define XFS_BTREE_TRACE_ARGR(c, r) \
+ xfs_btree_trace_argr(__func__, c, r, __LINE__)
+#define XFS_BTREE_TRACE_CURSOR(c, t) \
+ xfs_btree_trace_cursor(__func__, c, t, __LINE__)
+#else
+#define XFS_BTREE_TRACE_ARGBI(c, b, i)
+#define XFS_BTREE_TRACE_ARGBII(c, b, i, j)
+#define XFS_BTREE_TRACE_ARGFFFI(c, o, b, i, j)
+#define XFS_BTREE_TRACE_ARGI(c, i)
+#define XFS_BTREE_TRACE_ARGIPK(c, i, p, s)
+#define XFS_BTREE_TRACE_ARGIPR(c, i, p, r)
+#define XFS_BTREE_TRACE_ARGIK(c, i, k)
+#define XFS_BTREE_TRACE_ARGR(c, r)
+#define XFS_BTREE_TRACE_CURSOR(c, t)
+#endif /* XFS_BTREE_TRACE */
+
+#endif /* __XFS_BTREE_TRACE_H__ */
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 002fc2617c8e..92af4098c7e8 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -375,7 +375,7 @@ xfs_buf_item_unpin(
xfs_buf_log_item_t *bip,
int stale)
{
- xfs_mount_t *mp;
+ struct xfs_ail *ailp;
xfs_buf_t *bp;
int freed;
@@ -387,7 +387,7 @@ xfs_buf_item_unpin(
xfs_buftrace("XFS_UNPIN", bp);
freed = atomic_dec_and_test(&bip->bli_refcount);
- mp = bip->bli_item.li_mountp;
+ ailp = bip->bli_item.li_ailp;
xfs_bunpin(bp);
if (freed && stale) {
ASSERT(bip->bli_flags & XFS_BLI_STALE);
@@ -399,17 +399,17 @@ xfs_buf_item_unpin(
xfs_buftrace("XFS_UNPIN STALE", bp);
/*
* If we get called here because of an IO error, we may
- * or may not have the item on the AIL. xfs_trans_delete_ail()
+ * or may not have the item on the AIL. xfs_trans_ail_delete()
* will take care of that situation.
- * xfs_trans_delete_ail() drops the AIL lock.
+ * xfs_trans_ail_delete() drops the AIL lock.
*/
if (bip->bli_flags & XFS_BLI_STALE_INODE) {
xfs_buf_do_callbacks(bp, (xfs_log_item_t *)bip);
XFS_BUF_SET_FSPRIVATE(bp, NULL);
XFS_BUF_CLR_IODONE_FUNC(bp);
} else {
- spin_lock(&mp->m_ail_lock);
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip);
+ spin_lock(&ailp->xa_lock);
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)bip);
xfs_buf_item_relse(bp);
ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL);
}
@@ -707,8 +707,8 @@ xfs_buf_item_init(
* the first. If we do already have one, there is
* nothing to do here so return.
*/
- if (XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *) != mp)
- XFS_BUF_SET_FSPRIVATE3(bp, mp);
+ if (bp->b_mount != mp)
+ bp->b_mount = mp;
XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb);
if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) {
lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
@@ -731,6 +731,7 @@ xfs_buf_item_init(
bip->bli_item.li_type = XFS_LI_BUF;
bip->bli_item.li_ops = &xfs_buf_item_ops;
bip->bli_item.li_mountp = mp;
+ bip->bli_item.li_ailp = mp->m_ail;
bip->bli_buf = bp;
xfs_buf_hold(bp);
bip->bli_format.blf_type = XFS_LI_BUF;
@@ -997,21 +998,7 @@ xfs_buf_iodone_callbacks(
xfs_buf_do_callbacks(bp, lip);
XFS_BUF_SET_FSPRIVATE(bp, NULL);
XFS_BUF_CLR_IODONE_FUNC(bp);
-
- /*
- * XFS_SHUT flag gets set when we go thru the
- * entire buffer cache and deliberately start
- * throwing away delayed write buffers.
- * Since there's no biowait done on those,
- * we should just brelse them.
- */
- if (XFS_BUF_ISSHUT(bp)) {
- XFS_BUF_UNSHUT(bp);
- xfs_buf_relse(bp);
- } else {
- xfs_biodone(bp);
- }
-
+ xfs_biodone(bp);
return;
}
@@ -1122,27 +1109,23 @@ xfs_buf_iodone(
xfs_buf_t *bp,
xfs_buf_log_item_t *bip)
{
- struct xfs_mount *mp;
+ struct xfs_ail *ailp = bip->bli_item.li_ailp;
ASSERT(bip->bli_buf == bp);
xfs_buf_rele(bp);
- mp = bip->bli_item.li_mountp;
/*
* If we are forcibly shutting down, this may well be
* off the AIL already. That's because we simulate the
* log-committed callbacks to unpin these buffers. Or we may never
* have put this item on AIL because of the transaction was
- * aborted forcibly. xfs_trans_delete_ail() takes care of these.
+ * aborted forcibly. xfs_trans_ail_delete() takes care of these.
*
* Either way, AIL is useless if we're forcing a shutdown.
*/
- spin_lock(&mp->m_ail_lock);
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip);
+ spin_lock(&ailp->xa_lock);
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)bip);
xfs_buf_item_free(bip);
}
diff --git a/fs/xfs/xfs_clnt.h b/fs/xfs/xfs_clnt.h
deleted file mode 100644
index d2ce5dd70d87..000000000000
--- a/fs/xfs/xfs_clnt.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2000-2005 Silicon Graphics, 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.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_CLNT_H__
-#define __XFS_CLNT_H__
-
-/*
- * XFS arguments structure, constructed from the arguments we
- * are passed via the mount system call.
- *
- * NOTE: The mount system call is handled differently between
- * Linux and IRIX. In IRIX we worked work with a binary data
- * structure coming in across the syscall interface from user
- * space (the mount userspace knows about each filesystem type
- * and the set of valid options for it, and converts the users
- * argument string into a binary structure _before_ making the
- * system call), and the ABI issues that this implies.
- *
- * In Linux, we are passed a comma separated set of options;
- * ie. a NULL terminated string of characters. Userspace mount
- * code does not have any knowledge of mount options expected by
- * each filesystem type and so each filesystem parses its mount
- * options in kernel space.
- *
- * For the Linux port, we kept this structure pretty much intact
- * and use it internally (because the existing code groks it).
- */
-struct xfs_mount_args {
- int flags; /* flags -> see XFSMNT_... macros below */
- int flags2; /* flags -> see XFSMNT2_... macros below */
- int logbufs; /* Number of log buffers, -1 to default */
- int logbufsize; /* Size of log buffers, -1 to default */
- char fsname[MAXNAMELEN+1]; /* data device name */
- char rtname[MAXNAMELEN+1]; /* realtime device filename */
- char logname[MAXNAMELEN+1]; /* journal device filename */
- char mtpt[MAXNAMELEN+1]; /* filesystem mount point */
- int sunit; /* stripe unit (BBs) */
- int swidth; /* stripe width (BBs), multiple of sunit */
- uchar_t iosizelog; /* log2 of the preferred I/O size */
- int ihashsize; /* inode hash table size (buckets) */
-};
-
-/*
- * XFS mount option flags -- args->flags1
- */
-#define XFSMNT_ATTR2 0x00000001 /* allow ATTR2 EA format */
-#define XFSMNT_WSYNC 0x00000002 /* safe mode nfs mount
- * compatible */
-#define XFSMNT_INO64 0x00000004 /* move inode numbers up
- * past 2^32 */
-#define XFSMNT_UQUOTA 0x00000008 /* user quota accounting */
-#define XFSMNT_PQUOTA 0x00000010 /* IRIX prj quota accounting */
-#define XFSMNT_UQUOTAENF 0x00000020 /* user quota limit
- * enforcement */
-#define XFSMNT_PQUOTAENF 0x00000040 /* IRIX project quota limit
- * enforcement */
-#define XFSMNT_QUIET 0x00000080 /* don't report mount errors */
-#define XFSMNT_NOALIGN 0x00000200 /* don't allocate at
- * stripe boundaries*/
-#define XFSMNT_RETERR 0x00000400 /* return error to user */
-#define XFSMNT_NORECOVERY 0x00000800 /* no recovery, implies
- * read-only mount */
-#define XFSMNT_SHARED 0x00001000 /* shared XFS mount */
-#define XFSMNT_IOSIZE 0x00002000 /* optimize for I/O size */
-#define XFSMNT_OSYNCISOSYNC 0x00004000 /* o_sync is REALLY o_sync */
- /* (osyncisdsync is default) */
-#define XFSMNT_NOATTR2 0x00008000 /* turn off ATTR2 EA format */
-#define XFSMNT_32BITINODES 0x00200000 /* restrict inodes to 32
- * bits of address space */
-#define XFSMNT_GQUOTA 0x00400000 /* group quota accounting */
-#define XFSMNT_GQUOTAENF 0x00800000 /* group quota limit
- * enforcement */
-#define XFSMNT_NOUUID 0x01000000 /* Ignore fs uuid */
-#define XFSMNT_DMAPI 0x02000000 /* enable dmapi/xdsm */
-#define XFSMNT_BARRIER 0x04000000 /* use write barriers */
-#define XFSMNT_IKEEP 0x08000000 /* inode cluster delete */
-#define XFSMNT_SWALLOC 0x10000000 /* turn on stripe width
- * allocation */
-#define XFSMNT_DIRSYNC 0x40000000 /* sync creat,link,unlink,rename
- * symlink,mkdir,rmdir,mknod */
-#define XFSMNT_FLAGS2 0x80000000 /* more flags set in flags2 */
-
-/*
- * XFS mount option flags -- args->flags2
- */
-#define XFSMNT2_COMPAT_IOSIZE 0x00000001 /* don't report large preferred
- * I/O size in stat(2) */
-#define XFSMNT2_FILESTREAMS 0x00000002 /* enable the filestreams
- * allocator */
-
-#endif /* __XFS_CLNT_H__ */
diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h
index 8be0b00ede9a..70b710c1792d 100644
--- a/fs/xfs/xfs_da_btree.h
+++ b/fs/xfs/xfs_da_btree.h
@@ -72,27 +72,7 @@ typedef struct xfs_da_intnode {
typedef struct xfs_da_node_hdr xfs_da_node_hdr_t;
typedef struct xfs_da_node_entry xfs_da_node_entry_t;
-#define XFS_DA_MAXHASH ((xfs_dahash_t)-1) /* largest valid hash value */
-
#define XFS_LBSIZE(mp) (mp)->m_sb.sb_blocksize
-#define XFS_LBLOG(mp) (mp)->m_sb.sb_blocklog
-
-#define XFS_DA_MAKE_BNOENTRY(mp,bno,entry) \
- (((bno) << (mp)->m_dircook_elog) | (entry))
-#define XFS_DA_MAKE_COOKIE(mp,bno,entry,hash) \
- (((xfs_off_t)XFS_DA_MAKE_BNOENTRY(mp, bno, entry) << 32) | (hash))
-#define XFS_DA_COOKIE_HASH(mp,cookie) ((xfs_dahash_t)cookie)
-#define XFS_DA_COOKIE_BNO(mp,cookie) \
- ((((xfs_off_t)(cookie) >> 31) == -1LL ? \
- (xfs_dablk_t)0 : \
- (xfs_dablk_t)((xfs_off_t)(cookie) >> \
- ((mp)->m_dircook_elog + 32))))
-#define XFS_DA_COOKIE_ENTRY(mp,cookie) \
- ((((xfs_off_t)(cookie) >> 31) == -1LL ? \
- (xfs_dablk_t)0 : \
- (xfs_dablk_t)(((xfs_off_t)(cookie) >> 32) & \
- ((1 << (mp)->m_dircook_elog) - 1))))
-
/*========================================================================
* Btree searching and modification structure definitions.
@@ -226,9 +206,8 @@ struct xfs_nameops {
};
-#ifdef __KERNEL__
/*========================================================================
- * Function prototypes for the kernel.
+ * Function prototypes.
*========================================================================*/
/*
@@ -289,6 +268,5 @@ xfs_daddr_t xfs_da_blkno(xfs_dabuf_t *dabuf);
extern struct kmem_zone *xfs_da_state_zone;
extern struct kmem_zone *xfs_dabuf_zone;
-#endif /* __KERNEL__ */
#endif /* __XFS_DA_BTREE_H__ */
diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c
index 75b0cd4da0ea..b4c1ee713492 100644
--- a/fs/xfs/xfs_dfrag.c
+++ b/fs/xfs/xfs_dfrag.c
@@ -49,9 +49,8 @@
*/
int
xfs_swapext(
- xfs_swapext_t __user *sxu)
+ xfs_swapext_t *sxp)
{
- xfs_swapext_t *sxp;
xfs_inode_t *ip, *tip;
struct file *file, *target_file;
int error = 0;
@@ -62,11 +61,6 @@ xfs_swapext(
goto out;
}
- if (copy_from_user(sxp, sxu, sizeof(xfs_swapext_t))) {
- error = XFS_ERROR(EFAULT);
- goto out_free_sxp;
- }
-
/* Pull information for the target fd */
file = fget((int)sxp->sx_fdtarget);
if (!file) {
diff --git a/fs/xfs/xfs_dfrag.h b/fs/xfs/xfs_dfrag.h
index da178205be68..4f55a6306558 100644
--- a/fs/xfs/xfs_dfrag.h
+++ b/fs/xfs/xfs_dfrag.h
@@ -46,7 +46,7 @@ typedef struct xfs_swapext
/*
* Syscall interface for xfs_swapext
*/
-int xfs_swapext(struct xfs_swapext __user *sx);
+int xfs_swapext(struct xfs_swapext *sx);
int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
struct xfs_swapext *sxp);
diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h
index c9065eaf2a4d..162e8726df5e 100644
--- a/fs/xfs/xfs_dinode.h
+++ b/fs/xfs/xfs_dinode.h
@@ -18,32 +18,29 @@
#ifndef __XFS_DINODE_H__
#define __XFS_DINODE_H__
-struct xfs_buf;
-struct xfs_mount;
+#define XFS_DINODE_MAGIC 0x494e /* 'IN' */
+#define XFS_DINODE_GOOD_VERSION(v) (((v) == 1 || (v) == 2))
-#define XFS_DINODE_VERSION_1 1
-#define XFS_DINODE_VERSION_2 2
-#define XFS_DINODE_GOOD_VERSION(v) \
- (((v) == XFS_DINODE_VERSION_1 || (v) == XFS_DINODE_VERSION_2))
-#define XFS_DINODE_MAGIC 0x494e /* 'IN' */
-
-/*
- * Disk inode structure.
- * This is just the header; the inode is expanded to fill a variable size
- * with the last field expanding. It is split into the core and "other"
- * because we only need the core part in the in-core inode.
- */
typedef struct xfs_timestamp {
__be32 t_sec; /* timestamp seconds */
__be32 t_nsec; /* timestamp nanoseconds */
} xfs_timestamp_t;
/*
- * Note: Coordinate changes to this structure with the XFS_DI_* #defines
- * below, the offsets table in xfs_ialloc_log_di() and struct xfs_icdinode
- * in xfs_inode.h.
+ * On-disk inode structure.
+ *
+ * This is just the header or "dinode core", the inode is expanded to fill a
+ * variable size the leftover area split into a data and an attribute fork.
+ * The format of the data and attribute fork depends on the format of the
+ * inode as indicated by di_format and di_aformat. To access the data and
+ * attribute use the XFS_DFORK_PTR, XFS_DFORK_DPTR, and XFS_DFORK_PTR macros
+ * below.
+ *
+ * There is a very similar struct icdinode in xfs_inode which matches the
+ * layout of the first 96 bytes of this structure, but is kept in native
+ * format instead of big endian.
*/
-typedef struct xfs_dinode_core {
+typedef struct xfs_dinode {
__be16 di_magic; /* inode magic # = XFS_DINODE_MAGIC */
__be16 di_mode; /* mode and type of file */
__u8 di_version; /* inode version */
@@ -69,34 +66,12 @@ typedef struct xfs_dinode_core {
__be16 di_dmstate; /* DMIG state info */
__be16 di_flags; /* random flags, XFS_DIFLAG_... */
__be32 di_gen; /* generation number */
-} xfs_dinode_core_t;
-#define DI_MAX_FLUSH 0xffff
+ /* di_next_unlinked is the only non-core field in the old dinode */
+ __be32 di_next_unlinked;/* agi unlinked list ptr */
+} __attribute__((packed)) xfs_dinode_t;
-typedef struct xfs_dinode
-{
- xfs_dinode_core_t di_core;
- /*
- * In adding anything between the core and the union, be
- * sure to update the macros like XFS_LITINO below and
- * XFS_BMAP_RBLOCK_DSIZE in xfs_bmap_btree.h.
- */
- __be32 di_next_unlinked;/* agi unlinked list ptr */
- union {
- xfs_bmdr_block_t di_bmbt; /* btree root block */
- xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */
- xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */
- char di_c[1]; /* local contents */
- __be32 di_dev; /* device for S_IFCHR/S_IFBLK */
- uuid_t di_muuid; /* mount point value */
- char di_symlink[1]; /* local symbolic link */
- } di_u;
- union {
- xfs_bmdr_block_t di_abmbt; /* btree root block */
- xfs_bmbt_rec_32_t di_abmx[1]; /* extent list */
- xfs_attr_shortform_t di_attrsf; /* shortform attribute list */
- } di_a;
-} xfs_dinode_t;
+#define DI_MAX_FLUSH 0xffff
/*
* The 32 bit link count in the inode theoretically maxes out at UINT_MAX.
@@ -107,50 +82,14 @@ typedef struct xfs_dinode
#define XFS_MAXLINK_1 65535U
/*
- * Bit names for logging disk inodes only
- */
-#define XFS_DI_MAGIC 0x0000001
-#define XFS_DI_MODE 0x0000002
-#define XFS_DI_VERSION 0x0000004
-#define XFS_DI_FORMAT 0x0000008
-#define XFS_DI_ONLINK 0x0000010
-#define XFS_DI_UID 0x0000020
-#define XFS_DI_GID 0x0000040
-#define XFS_DI_NLINK 0x0000080
-#define XFS_DI_PROJID 0x0000100
-#define XFS_DI_PAD 0x0000200
-#define XFS_DI_ATIME 0x0000400
-#define XFS_DI_MTIME 0x0000800
-#define XFS_DI_CTIME 0x0001000
-#define XFS_DI_SIZE 0x0002000
-#define XFS_DI_NBLOCKS 0x0004000
-#define XFS_DI_EXTSIZE 0x0008000
-#define XFS_DI_NEXTENTS 0x0010000
-#define XFS_DI_NAEXTENTS 0x0020000
-#define XFS_DI_FORKOFF 0x0040000
-#define XFS_DI_AFORMAT 0x0080000
-#define XFS_DI_DMEVMASK 0x0100000
-#define XFS_DI_DMSTATE 0x0200000
-#define XFS_DI_FLAGS 0x0400000
-#define XFS_DI_GEN 0x0800000
-#define XFS_DI_NEXT_UNLINKED 0x1000000
-#define XFS_DI_U 0x2000000
-#define XFS_DI_A 0x4000000
-#define XFS_DI_NUM_BITS 27
-#define XFS_DI_ALL_BITS ((1 << XFS_DI_NUM_BITS) - 1)
-#define XFS_DI_CORE_BITS (XFS_DI_ALL_BITS & ~(XFS_DI_U|XFS_DI_A))
-
-/*
* Values for di_format
*/
-typedef enum xfs_dinode_fmt
-{
- XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */
- XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */
- /* LNK: di_symlink */
- XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */
- XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */
- XFS_DINODE_FMT_UUID /* MNT: di_uuid */
+typedef enum xfs_dinode_fmt {
+ XFS_DINODE_FMT_DEV, /* xfs_dev_t */
+ XFS_DINODE_FMT_LOCAL, /* bulk data */
+ XFS_DINODE_FMT_EXTENTS, /* struct xfs_bmbt_rec */
+ XFS_DINODE_FMT_BTREE, /* struct xfs_bmdr_block */
+ XFS_DINODE_FMT_UUID /* uuid_t */
} xfs_dinode_fmt_t;
/*
@@ -166,13 +105,13 @@ typedef enum xfs_dinode_fmt
*/
#define XFS_LITINO(mp) ((mp)->m_litino)
#define XFS_BROOT_SIZE_ADJ \
- (sizeof(xfs_bmbt_block_t) - sizeof(xfs_bmdr_block_t))
+ (XFS_BTREE_LBLOCK_LEN - sizeof(xfs_bmdr_block_t))
/*
* Inode data & attribute fork sizes, per inode.
*/
-#define XFS_DFORK_Q(dip) ((dip)->di_core.di_forkoff != 0)
-#define XFS_DFORK_BOFF(dip) ((int)((dip)->di_core.di_forkoff << 3))
+#define XFS_DFORK_Q(dip) ((dip)->di_forkoff != 0)
+#define XFS_DFORK_BOFF(dip) ((int)((dip)->di_forkoff << 3))
#define XFS_DFORK_DSIZE(dip,mp) \
(XFS_DFORK_Q(dip) ? \
@@ -187,23 +126,42 @@ typedef enum xfs_dinode_fmt
XFS_DFORK_DSIZE(dip, mp) : \
XFS_DFORK_ASIZE(dip, mp))
-#define XFS_DFORK_DPTR(dip) ((dip)->di_u.di_c)
+/*
+ * Return pointers to the data or attribute forks.
+ */
+#define XFS_DFORK_DPTR(dip) \
+ ((char *)(dip) + sizeof(struct xfs_dinode))
#define XFS_DFORK_APTR(dip) \
- ((dip)->di_u.di_c + XFS_DFORK_BOFF(dip))
+ (XFS_DFORK_DPTR(dip) + XFS_DFORK_BOFF(dip))
#define XFS_DFORK_PTR(dip,w) \
((w) == XFS_DATA_FORK ? XFS_DFORK_DPTR(dip) : XFS_DFORK_APTR(dip))
+
#define XFS_DFORK_FORMAT(dip,w) \
((w) == XFS_DATA_FORK ? \
- (dip)->di_core.di_format : \
- (dip)->di_core.di_aformat)
+ (dip)->di_format : \
+ (dip)->di_aformat)
#define XFS_DFORK_NEXTENTS(dip,w) \
((w) == XFS_DATA_FORK ? \
- be32_to_cpu((dip)->di_core.di_nextents) : \
- be16_to_cpu((dip)->di_core.di_anextents))
+ be32_to_cpu((dip)->di_nextents) : \
+ be16_to_cpu((dip)->di_anextents))
#define XFS_BUF_TO_DINODE(bp) ((xfs_dinode_t *)XFS_BUF_PTR(bp))
/*
+ * For block and character special files the 32bit dev_t is stored at the
+ * beginning of the data fork.
+ */
+static inline xfs_dev_t xfs_dinode_get_rdev(struct xfs_dinode *dip)
+{
+ return be32_to_cpu(*(__be32 *)XFS_DFORK_DPTR(dip));
+}
+
+static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
+{
+ *(__be32 *)XFS_DFORK_DPTR(dip) = cpu_to_be32(rdev);
+}
+
+/*
* Values for di_flags
* There should be a one-to-one correspondence between these flags and the
* XFS_XFLAG_s.
diff --git a/fs/xfs/xfs_dir2_sf.h b/fs/xfs/xfs_dir2_sf.h
index deecc9d238f8..6ac44b550d39 100644
--- a/fs/xfs/xfs_dir2_sf.h
+++ b/fs/xfs/xfs_dir2_sf.h
@@ -34,13 +34,6 @@ struct xfs_mount;
struct xfs_trans;
/*
- * Maximum size of a shortform directory.
- */
-#define XFS_DIR2_SF_MAX_SIZE \
- (XFS_DINODE_MAX_SIZE - (uint)sizeof(xfs_dinode_core_t) - \
- (uint)sizeof(xfs_agino_t))
-
-/*
* Inode number stored as 8 8-bit values.
*/
typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t;
diff --git a/fs/xfs/xfs_dmops.c b/fs/xfs/xfs_dmops.c
index a1e55fb9d5dd..e71e2581c0c3 100644
--- a/fs/xfs/xfs_dmops.c
+++ b/fs/xfs/xfs_dmops.c
@@ -25,7 +25,6 @@
#include "xfs_inum.h"
#include "xfs_ag.h"
#include "xfs_mount.h"
-#include "xfs_clnt.h"
static struct xfs_dmops xfs_dmcore_stub = {
@@ -38,9 +37,9 @@ static struct xfs_dmops xfs_dmcore_stub = {
};
int
-xfs_dmops_get(struct xfs_mount *mp, struct xfs_mount_args *args)
+xfs_dmops_get(struct xfs_mount *mp)
{
- if (args->flags & XFSMNT_DMAPI) {
+ if (mp->m_flags & XFS_MOUNT_DMAPI) {
cmn_err(CE_WARN,
"XFS: dmapi support not available in this kernel.");
return EINVAL;
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index f227ecd1a294..92d5cd5bf4f2 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -153,21 +153,6 @@ xfs_errortag_clearall(xfs_mount_t *mp, int loud)
}
#endif /* DEBUG */
-static void
-xfs_fs_vcmn_err(int level, xfs_mount_t *mp, char *fmt, va_list ap)
-{
- if (mp != NULL) {
- char *newfmt;
- int len = 16 + mp->m_fsname_len + strlen(fmt);
-
- newfmt = kmem_alloc(len, KM_SLEEP);
- sprintf(newfmt, "Filesystem \"%s\": %s", mp->m_fsname, fmt);
- icmn_err(level, newfmt, ap);
- kmem_free(newfmt);
- } else {
- icmn_err(level, fmt, ap);
- }
-}
void
xfs_fs_cmn_err(int level, xfs_mount_t *mp, char *fmt, ...)
diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
index 11543f10b0c6..0c93051c4651 100644
--- a/fs/xfs/xfs_error.h
+++ b/fs/xfs/xfs_error.h
@@ -159,11 +159,15 @@ extern int xfs_errortag_clearall(xfs_mount_t *mp, int loud);
#define XFS_PTAG_FSBLOCK_ZERO 0x00000080
struct xfs_mount;
-/* PRINTFLIKE4 */
+
+extern void xfs_fs_vcmn_err(int level, struct xfs_mount *mp,
+ char *fmt, va_list ap)
+ __attribute__ ((format (printf, 3, 0)));
extern void xfs_cmn_err(int panic_tag, int level, struct xfs_mount *mp,
- char *fmt, ...);
-/* PRINTFLIKE3 */
-extern void xfs_fs_cmn_err(int level, struct xfs_mount *mp, char *fmt, ...);
+ char *fmt, ...)
+ __attribute__ ((format (printf, 4, 5)));
+extern void xfs_fs_cmn_err(int level, struct xfs_mount *mp, char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)));
extern void xfs_hex_dump(void *p, int length);
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index 8aa28f751b2a..05a4bdd4be39 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -108,19 +108,16 @@ xfs_efi_item_pin(xfs_efi_log_item_t *efip)
STATIC void
xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int stale)
{
- xfs_mount_t *mp;
+ struct xfs_ail *ailp = efip->efi_item.li_ailp;
- mp = efip->efi_item.li_mountp;
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
if (efip->efi_flags & XFS_EFI_CANCELED) {
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip);
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip);
xfs_efi_item_free(efip);
} else {
efip->efi_flags |= XFS_EFI_COMMITTED;
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
}
@@ -134,26 +131,23 @@ xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int stale)
STATIC void
xfs_efi_item_unpin_remove(xfs_efi_log_item_t *efip, xfs_trans_t *tp)
{
- xfs_mount_t *mp;
+ struct xfs_ail *ailp = efip->efi_item.li_ailp;
xfs_log_item_desc_t *lidp;
- mp = efip->efi_item.li_mountp;
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
if (efip->efi_flags & XFS_EFI_CANCELED) {
/*
* free the xaction descriptor pointing to this item
*/
lidp = xfs_trans_find_item(tp, (xfs_log_item_t *) efip);
xfs_trans_free_item(tp, lidp);
- /*
- * pull the item off the AIL.
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip);
+
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip);
xfs_efi_item_free(efip);
} else {
efip->efi_flags |= XFS_EFI_COMMITTED;
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
}
@@ -268,6 +262,7 @@ xfs_efi_init(xfs_mount_t *mp,
efip->efi_item.li_type = XFS_LI_EFI;
efip->efi_item.li_ops = &xfs_efi_item_ops;
efip->efi_item.li_mountp = mp;
+ efip->efi_item.li_ailp = mp->m_ail;
efip->efi_format.efi_nextents = nextents;
efip->efi_format.efi_id = (__psint_t)(void*)efip;
@@ -345,25 +340,22 @@ void
xfs_efi_release(xfs_efi_log_item_t *efip,
uint nextents)
{
- xfs_mount_t *mp;
- int extents_left;
+ struct xfs_ail *ailp = efip->efi_item.li_ailp;
+ int extents_left;
- mp = efip->efi_item.li_mountp;
ASSERT(efip->efi_next_extent > 0);
ASSERT(efip->efi_flags & XFS_EFI_COMMITTED);
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
ASSERT(efip->efi_next_extent >= nextents);
efip->efi_next_extent -= nextents;
extents_left = efip->efi_next_extent;
if (extents_left == 0) {
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip);
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip);
xfs_efi_item_free(efip);
} else {
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
}
@@ -565,6 +557,7 @@ xfs_efd_init(xfs_mount_t *mp,
efdp->efd_item.li_type = XFS_LI_EFD;
efdp->efd_item.li_ops = &xfs_efd_item_ops;
efdp->efd_item.li_mountp = mp;
+ efdp->efd_item.li_ailp = mp->m_ail;
efdp->efd_efip = efip;
efdp->efd_format.efd_nextents = nextents;
efdp->efd_format.efd_efi_id = efip->efi_format.efi_id;
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
index 01c0cc88d3f3..589c41c38446 100644
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -113,22 +113,14 @@ struct getbmapx {
#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */
#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */
#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */
-#define BMV_IF_VALID (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC)
-#ifdef __KERNEL__
-#define BMV_IF_EXTENDED 0x40000000 /* getpmapx if set */
-#endif
+#define BMV_IF_DELALLOC 0x8 /* rtn status BMV_OF_DELALLOC if req */
+#define BMV_IF_VALID \
+ (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC|BMV_IF_DELALLOC)
/* bmv_oflags values - returned for for each non-header segment */
#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
-
-/* Convert getbmap <-> getbmapx - move fields from p1 to p2. */
-#define GETBMAP_CONVERT(p1,p2) { \
- p2.bmv_offset = p1.bmv_offset; \
- p2.bmv_block = p1.bmv_block; \
- p2.bmv_length = p1.bmv_length; \
- p2.bmv_count = p1.bmv_count; \
- p2.bmv_entries = p1.bmv_entries; }
-
+#define BMV_OF_DELALLOC 0x2 /* segment = delayed allocation */
+#define BMV_OF_LAST 0x4 /* segment is the last in the file */
/*
* Structure for XFS_IOC_FSSETDM.
@@ -426,10 +418,6 @@ typedef struct xfs_handle {
#define XFS_IOC_GETXFLAGS FS_IOC_GETFLAGS
#define XFS_IOC_SETXFLAGS FS_IOC_SETFLAGS
#define XFS_IOC_GETVERSION FS_IOC_GETVERSION
-/* 32-bit compat counterparts */
-#define XFS_IOC32_GETXFLAGS FS_IOC32_GETFLAGS
-#define XFS_IOC32_SETXFLAGS FS_IOC32_SETFLAGS
-#define XFS_IOC32_GETVERSION FS_IOC32_GETVERSION
/*
* ioctl commands that replace IRIX fcntl()'s
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 84583cf73db3..852b6d32e8d0 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -126,7 +126,7 @@ xfs_growfs_data_private(
xfs_extlen_t agsize;
xfs_extlen_t tmpsize;
xfs_alloc_rec_t *arec;
- xfs_btree_sblock_t *block;
+ struct xfs_btree_block *block;
xfs_buf_t *bp;
int bucket;
int dpct;
@@ -251,14 +251,14 @@ xfs_growfs_data_private(
bp = xfs_buf_get(mp->m_ddev_targp,
XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
BTOBB(mp->m_sb.sb_blocksize), 0);
- block = XFS_BUF_TO_SBLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
memset(block, 0, mp->m_sb.sb_blocksize);
block->bb_magic = cpu_to_be32(XFS_ABTB_MAGIC);
block->bb_level = 0;
block->bb_numrecs = cpu_to_be16(1);
- block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
- block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
- arec = XFS_BTREE_REC_ADDR(xfs_alloc, block, 1);
+ block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
+ block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
+ arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
arec->ar_blockcount = cpu_to_be32(
agsize - be32_to_cpu(arec->ar_startblock));
@@ -272,14 +272,14 @@ xfs_growfs_data_private(
bp = xfs_buf_get(mp->m_ddev_targp,
XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
BTOBB(mp->m_sb.sb_blocksize), 0);
- block = XFS_BUF_TO_SBLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
memset(block, 0, mp->m_sb.sb_blocksize);
block->bb_magic = cpu_to_be32(XFS_ABTC_MAGIC);
block->bb_level = 0;
block->bb_numrecs = cpu_to_be16(1);
- block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
- block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
- arec = XFS_BTREE_REC_ADDR(xfs_alloc, block, 1);
+ block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
+ block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
+ arec = XFS_ALLOC_REC_ADDR(mp, block, 1);
arec->ar_startblock = cpu_to_be32(XFS_PREALLOC_BLOCKS(mp));
arec->ar_blockcount = cpu_to_be32(
agsize - be32_to_cpu(arec->ar_startblock));
@@ -294,13 +294,13 @@ xfs_growfs_data_private(
bp = xfs_buf_get(mp->m_ddev_targp,
XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
BTOBB(mp->m_sb.sb_blocksize), 0);
- block = XFS_BUF_TO_SBLOCK(bp);
+ block = XFS_BUF_TO_BLOCK(bp);
memset(block, 0, mp->m_sb.sb_blocksize);
block->bb_magic = cpu_to_be32(XFS_IBT_MAGIC);
block->bb_level = 0;
block->bb_numrecs = 0;
- block->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
- block->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
+ block->bb_u.s.bb_leftsib = cpu_to_be32(NULLAGBLOCK);
+ block->bb_u.s.bb_rightsib = cpu_to_be32(NULLAGBLOCK);
error = xfs_bwrite(mp, bp);
if (error) {
goto error0;
@@ -435,6 +435,9 @@ xfs_growfs_data(
xfs_growfs_data_t *in)
{
int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
if (!mutex_trylock(&mp->m_growlock))
return XFS_ERROR(EWOULDBLOCK);
error = xfs_growfs_data_private(mp, in);
@@ -448,6 +451,9 @@ xfs_growfs_log(
xfs_growfs_log_t *in)
{
int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
if (!mutex_trylock(&mp->m_growlock))
return XFS_ERROR(EWOULDBLOCK);
error = xfs_growfs_log_private(mp, in);
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
index aad8c5da38af..e6ebbaeb4dc6 100644
--- a/fs/xfs/xfs_ialloc.c
+++ b/fs/xfs/xfs_ialloc.c
@@ -41,68 +41,6 @@
#include "xfs_error.h"
#include "xfs_bmap.h"
-/*
- * Log specified fields for the inode given by bp and off.
- */
-STATIC void
-xfs_ialloc_log_di(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_buf_t *bp, /* inode buffer */
- int off, /* index of inode in buffer */
- int fields) /* bitmask of fields to log */
-{
- int first; /* first byte number */
- int ioffset; /* off in bytes */
- int last; /* last byte number */
- xfs_mount_t *mp; /* mount point structure */
- static const short offsets[] = { /* field offsets */
- /* keep in sync with bits */
- offsetof(xfs_dinode_core_t, di_magic),
- offsetof(xfs_dinode_core_t, di_mode),
- offsetof(xfs_dinode_core_t, di_version),
- offsetof(xfs_dinode_core_t, di_format),
- offsetof(xfs_dinode_core_t, di_onlink),
- offsetof(xfs_dinode_core_t, di_uid),
- offsetof(xfs_dinode_core_t, di_gid),
- offsetof(xfs_dinode_core_t, di_nlink),
- offsetof(xfs_dinode_core_t, di_projid),
- offsetof(xfs_dinode_core_t, di_pad),
- offsetof(xfs_dinode_core_t, di_atime),
- offsetof(xfs_dinode_core_t, di_mtime),
- offsetof(xfs_dinode_core_t, di_ctime),
- offsetof(xfs_dinode_core_t, di_size),
- offsetof(xfs_dinode_core_t, di_nblocks),
- offsetof(xfs_dinode_core_t, di_extsize),
- offsetof(xfs_dinode_core_t, di_nextents),
- offsetof(xfs_dinode_core_t, di_anextents),
- offsetof(xfs_dinode_core_t, di_forkoff),
- offsetof(xfs_dinode_core_t, di_aformat),
- offsetof(xfs_dinode_core_t, di_dmevmask),
- offsetof(xfs_dinode_core_t, di_dmstate),
- offsetof(xfs_dinode_core_t, di_flags),
- offsetof(xfs_dinode_core_t, di_gen),
- offsetof(xfs_dinode_t, di_next_unlinked),
- offsetof(xfs_dinode_t, di_u),
- offsetof(xfs_dinode_t, di_a),
- sizeof(xfs_dinode_t)
- };
-
-
- ASSERT(offsetof(xfs_dinode_t, di_core) == 0);
- ASSERT((fields & (XFS_DI_U|XFS_DI_A)) == 0);
- mp = tp->t_mountp;
- /*
- * Get the inode-relative first and last bytes for these fields
- */
- xfs_btree_offsets(fields, offsets, XFS_DI_NUM_BITS, &first, &last);
- /*
- * Convert to buffer offsets and log it.
- */
- ioffset = off << mp->m_sb.sb_inodelog;
- first += ioffset;
- last += ioffset;
- xfs_trans_log_buf(tp, bp, first, last);
-}
/*
* Allocation group level functions.
@@ -119,6 +57,102 @@ xfs_ialloc_cluster_alignment(
}
/*
+ * Lookup the record equal to ino in the btree given by cur.
+ */
+STATIC int /* error */
+xfs_inobt_lookup_eq(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
+ * Lookup the first record greater than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_ge(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+ * Lookup the first record less than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Update the record referred to by cur to the value given
+ * by [ino, fcnt, free].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int /* error */
+xfs_inobt_update(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free) /* free inode mask */
+{
+ union xfs_btree_rec rec;
+
+ rec.inobt.ir_startino = cpu_to_be32(ino);
+ rec.inobt.ir_freecount = cpu_to_be32(fcnt);
+ rec.inobt.ir_free = cpu_to_be64(free);
+ return xfs_btree_update(cur, &rec);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_inobt_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t *ino, /* output: starting inode of chunk */
+ __int32_t *fcnt, /* output: number of free inodes */
+ xfs_inofree_t *free, /* output: free inode mask */
+ int *stat) /* output: success/failure */
+{
+ union xfs_btree_rec *rec;
+ int error;
+
+ error = xfs_btree_get_rec(cur, &rec, stat);
+ if (!error && *stat == 1) {
+ *ino = be32_to_cpu(rec->inobt.ir_startino);
+ *fcnt = be32_to_cpu(rec->inobt.ir_freecount);
+ *free = be64_to_cpu(rec->inobt.ir_free);
+ }
+ return error;
+}
+
+/*
* Allocate new inodes in the allocation group specified by agbp.
* Return 0 for success, else error code.
*/
@@ -287,9 +321,9 @@ xfs_ialloc_ag_alloc(
* able to use the file system.
*/
if (xfs_sb_version_hasnlink(&args.mp->m_sb))
- version = XFS_DINODE_VERSION_2;
+ version = 2;
else
- version = XFS_DINODE_VERSION_1;
+ version = 1;
/*
* Seed the new inode cluster with a random generation number. This
@@ -310,18 +344,25 @@ xfs_ialloc_ag_alloc(
XFS_BUF_LOCK);
ASSERT(fbuf);
ASSERT(!XFS_BUF_GETERROR(fbuf));
+
/*
- * Set initial values for the inodes in this buffer.
+ * Initialize all inodes in this buffer and then log them.
+ *
+ * XXX: It would be much better if we had just one transaction to
+ * log a whole cluster of inodes instead of all the indivdual
+ * transactions causing a lot of log traffic.
*/
xfs_biozero(fbuf, 0, ninodes << args.mp->m_sb.sb_inodelog);
for (i = 0; i < ninodes; i++) {
+ int ioffset = i << args.mp->m_sb.sb_inodelog;
+ uint isize = sizeof(struct xfs_dinode);
+
free = XFS_MAKE_IPTR(args.mp, fbuf, i);
- free->di_core.di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
- free->di_core.di_version = version;
- free->di_core.di_gen = cpu_to_be32(gen);
+ free->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
+ free->di_version = version;
+ free->di_gen = cpu_to_be32(gen);
free->di_next_unlinked = cpu_to_be32(NULLAGINO);
- xfs_ialloc_log_di(tp, fbuf, i,
- XFS_DI_CORE_BITS | XFS_DI_NEXT_UNLINKED);
+ xfs_trans_log_buf(tp, fbuf, ioffset, ioffset + isize - 1);
}
xfs_trans_inode_alloc_buf(tp, fbuf);
}
@@ -335,8 +376,7 @@ xfs_ialloc_ag_alloc(
/*
* Insert records describing the new inode chunk into the btree.
*/
- cur = xfs_btree_init_cursor(args.mp, tp, agbp, agno,
- XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ cur = xfs_inobt_init_cursor(args.mp, tp, agbp, agno);
for (thisino = newino;
thisino < newino + newlen;
thisino += XFS_INODES_PER_CHUNK) {
@@ -346,7 +386,7 @@ xfs_ialloc_ag_alloc(
return error;
}
ASSERT(i == 0);
- if ((error = xfs_inobt_insert(cur, &i))) {
+ if ((error = xfs_btree_insert(cur, &i))) {
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
return error;
}
@@ -676,8 +716,7 @@ nextag:
*/
agno = tagno;
*IO_agbp = NULL;
- cur = xfs_btree_init_cursor(mp, tp, agbp, be32_to_cpu(agi->agi_seqno),
- XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ cur = xfs_inobt_init_cursor(mp, tp, agbp, be32_to_cpu(agi->agi_seqno));
/*
* If pagino is 0 (this is the root inode allocation) use newino.
* This must work because we've just allocated some.
@@ -697,7 +736,7 @@ nextag:
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
freecount += rec.ir_freecount;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error0;
} while (i == 1);
@@ -741,7 +780,7 @@ nextag:
/*
* Search left with tcur, back up 1 record.
*/
- if ((error = xfs_inobt_decrement(tcur, 0, &i)))
+ if ((error = xfs_btree_decrement(tcur, 0, &i)))
goto error1;
doneleft = !i;
if (!doneleft) {
@@ -755,7 +794,7 @@ nextag:
/*
* Search right with cur, go forward 1 record.
*/
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error1;
doneright = !i;
if (!doneright) {
@@ -817,7 +856,7 @@ nextag:
* further left.
*/
if (useleft) {
- if ((error = xfs_inobt_decrement(tcur, 0,
+ if ((error = xfs_btree_decrement(tcur, 0,
&i)))
goto error1;
doneleft = !i;
@@ -837,7 +876,7 @@ nextag:
* further right.
*/
else {
- if ((error = xfs_inobt_increment(cur, 0,
+ if ((error = xfs_btree_increment(cur, 0,
&i)))
goto error1;
doneright = !i;
@@ -892,7 +931,7 @@ nextag:
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
if (rec.ir_freecount > 0)
break;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
}
@@ -926,7 +965,7 @@ nextag:
goto error0;
XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
freecount += rec.ir_freecount;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error0;
} while (i == 1);
ASSERT(freecount == be32_to_cpu(agi->agi_freecount) ||
@@ -1022,8 +1061,7 @@ xfs_difree(
/*
* Initialize the cursor.
*/
- cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,
- (xfs_inode_t *)0, 0);
+ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
#ifdef DEBUG
if (cur->bc_nlevels == 1) {
int freecount = 0;
@@ -1036,7 +1074,7 @@ xfs_difree(
goto error0;
if (i) {
freecount += rec.ir_freecount;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error0;
}
} while (i == 1);
@@ -1098,8 +1136,8 @@ xfs_difree(
xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen);
xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1));
- if ((error = xfs_inobt_delete(cur, &i))) {
- cmn_err(CE_WARN, "xfs_difree: xfs_inobt_delete returned an error %d on %s.\n",
+ if ((error = xfs_btree_delete(cur, &i))) {
+ cmn_err(CE_WARN, "xfs_difree: xfs_btree_delete returned an error %d on %s.\n",
error, mp->m_fsname);
goto error0;
}
@@ -1141,7 +1179,7 @@ xfs_difree(
goto error0;
if (i) {
freecount += rec.ir_freecount;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
+ if ((error = xfs_btree_increment(cur, 0, &i)))
goto error0;
}
} while (i == 1);
@@ -1158,36 +1196,28 @@ error0:
}
/*
- * Return the location of the inode in bno/off, for mapping it into a buffer.
+ * Return the location of the inode in imap, for mapping it into a buffer.
*/
-/*ARGSUSED*/
int
-xfs_dilocate(
- xfs_mount_t *mp, /* file system mount structure */
- xfs_trans_t *tp, /* transaction pointer */
+xfs_imap(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
xfs_ino_t ino, /* inode to locate */
- xfs_fsblock_t *bno, /* output: block containing inode */
- int *len, /* output: num blocks in inode cluster */
- int *off, /* output: index in block of inode */
- uint flags) /* flags concerning inode lookup */
+ struct xfs_imap *imap, /* location map structure */
+ uint flags) /* flags for inode btree lookup */
{
xfs_agblock_t agbno; /* block number of inode in the alloc group */
- xfs_buf_t *agbp; /* agi buffer */
xfs_agino_t agino; /* inode number within alloc group */
xfs_agnumber_t agno; /* allocation group number */
int blks_per_cluster; /* num blocks per inode cluster */
xfs_agblock_t chunk_agbno; /* first block in inode chunk */
- xfs_agino_t chunk_agino; /* first agino in inode chunk */
- __int32_t chunk_cnt; /* count of free inodes in chunk */
- xfs_inofree_t chunk_free; /* mask of free inodes in chunk */
xfs_agblock_t cluster_agbno; /* first block in inode cluster */
- xfs_btree_cur_t *cur; /* inode btree cursor */
int error; /* error code */
- int i; /* temp state */
int offset; /* index of inode in its buffer */
int offset_agbno; /* blks from chunk start to inode */
ASSERT(ino != NULLFSINO);
+
/*
* Split up the inode number into its parts.
*/
@@ -1198,24 +1228,24 @@ xfs_dilocate(
ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
#ifdef DEBUG
/* no diagnostics for bulkstat, ino comes from userspace */
- if (flags & XFS_IMAP_BULKSTAT)
+ if (flags & XFS_IGET_BULKSTAT)
return XFS_ERROR(EINVAL);
if (agno >= mp->m_sb.sb_agcount) {
xfs_fs_cmn_err(CE_ALERT, mp,
- "xfs_dilocate: agno (%d) >= "
+ "xfs_imap: agno (%d) >= "
"mp->m_sb.sb_agcount (%d)",
agno, mp->m_sb.sb_agcount);
}
if (agbno >= mp->m_sb.sb_agblocks) {
xfs_fs_cmn_err(CE_ALERT, mp,
- "xfs_dilocate: agbno (0x%llx) >= "
+ "xfs_imap: agbno (0x%llx) >= "
"mp->m_sb.sb_agblocks (0x%lx)",
(unsigned long long) agbno,
(unsigned long) mp->m_sb.sb_agblocks);
}
if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
xfs_fs_cmn_err(CE_ALERT, mp,
- "xfs_dilocate: ino (0x%llx) != "
+ "xfs_imap: ino (0x%llx) != "
"XFS_AGINO_TO_INO(mp, agno, agino) "
"(0x%llx)",
ino, XFS_AGINO_TO_INO(mp, agno, agino));
@@ -1224,65 +1254,89 @@ xfs_dilocate(
#endif /* DEBUG */
return XFS_ERROR(EINVAL);
}
- if ((mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) ||
- !(flags & XFS_IMAP_LOOKUP)) {
+
+ /*
+ * If the inode cluster size is the same as the blocksize or
+ * smaller we get to the buffer by simple arithmetics.
+ */
+ if (XFS_INODE_CLUSTER_SIZE(mp) <= mp->m_sb.sb_blocksize) {
offset = XFS_INO_TO_OFFSET(mp, ino);
ASSERT(offset < mp->m_sb.sb_inopblock);
- *bno = XFS_AGB_TO_FSB(mp, agno, agbno);
- *off = offset;
- *len = 1;
+
+ imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ imap->im_len = XFS_FSB_TO_BB(mp, 1);
+ imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
return 0;
}
+
blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;
- if (*bno != NULLFSBLOCK) {
+
+ /*
+ * If we get a block number passed from bulkstat we can use it to
+ * find the buffer easily.
+ */
+ if (imap->im_blkno) {
offset = XFS_INO_TO_OFFSET(mp, ino);
ASSERT(offset < mp->m_sb.sb_inopblock);
- cluster_agbno = XFS_FSB_TO_AGBNO(mp, *bno);
- *off = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
- offset;
- *len = blks_per_cluster;
+
+ cluster_agbno = XFS_DADDR_TO_AGBNO(mp, imap->im_blkno);
+ offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock;
+
+ imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster);
+ imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
return 0;
}
+
+ /*
+ * If the inode chunks are aligned then use simple maths to
+ * find the location. Otherwise we have to do a btree
+ * lookup to find the location.
+ */
if (mp->m_inoalign_mask) {
offset_agbno = agbno & mp->m_inoalign_mask;
chunk_agbno = agbno - offset_agbno;
} else {
+ xfs_btree_cur_t *cur; /* inode btree cursor */
+ xfs_agino_t chunk_agino; /* first agino in inode chunk */
+ __int32_t chunk_cnt; /* count of free inodes in chunk */
+ xfs_inofree_t chunk_free; /* mask of free inodes in chunk */
+ xfs_buf_t *agbp; /* agi buffer */
+ int i; /* temp state */
+
down_read(&mp->m_peraglock);
error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
up_read(&mp->m_peraglock);
if (error) {
-#ifdef DEBUG
- xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
"xfs_ialloc_read_agi() returned "
"error %d, agno %d",
error, agno);
-#endif /* DEBUG */
return error;
}
- cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,
- (xfs_inode_t *)0, 0);
- if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {
-#ifdef DEBUG
- xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+
+ cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
+ error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i);
+ if (error) {
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
"xfs_inobt_lookup_le() failed");
-#endif /* DEBUG */
goto error0;
}
- if ((error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,
- &chunk_free, &i))) {
-#ifdef DEBUG
- xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+
+ error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,
+ &chunk_free, &i);
+ if (error) {
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
"xfs_inobt_get_rec() failed");
-#endif /* DEBUG */
goto error0;
}
if (i == 0) {
#ifdef DEBUG
- xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
"xfs_inobt_get_rec() failed");
#endif /* DEBUG */
error = XFS_ERROR(EINVAL);
}
+ error0:
xfs_trans_brelse(tp, agbp);
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
if (error)
@@ -1290,19 +1344,35 @@ xfs_dilocate(
chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_agino);
offset_agbno = agbno - chunk_agbno;
}
+
ASSERT(agbno >= chunk_agbno);
cluster_agbno = chunk_agbno +
((offset_agbno / blks_per_cluster) * blks_per_cluster);
offset = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
XFS_INO_TO_OFFSET(mp, ino);
- *bno = XFS_AGB_TO_FSB(mp, agno, cluster_agbno);
- *off = offset;
- *len = blks_per_cluster;
+
+ imap->im_blkno = XFS_AGB_TO_DADDR(mp, agno, cluster_agbno);
+ imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster);
+ imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
+
+ /*
+ * If the inode number maps to a block outside the bounds
+ * of the file system then return NULL rather than calling
+ * read_buf and panicing when we get an error from the
+ * driver.
+ */
+ if ((imap->im_blkno + imap->im_len) >
+ XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
+ "(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > "
+ " XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)",
+ (unsigned long long) imap->im_blkno,
+ (unsigned long long) imap->im_len,
+ XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));
+ return XFS_ERROR(EINVAL);
+ }
+
return 0;
-error0:
- xfs_trans_brelse(tp, agbp);
- xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
- return error;
}
/*
@@ -1370,70 +1440,95 @@ xfs_ialloc_log_agi(
xfs_trans_log_buf(tp, bp, first, last);
}
+#ifdef DEBUG
+STATIC void
+xfs_check_agi_unlinked(
+ struct xfs_agi *agi)
+{
+ int i;
+
+ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++)
+ ASSERT(agi->agi_unlinked[i]);
+}
+#else
+#define xfs_check_agi_unlinked(agi)
+#endif
+
/*
* Read in the allocation group header (inode allocation section)
*/
int
-xfs_ialloc_read_agi(
- xfs_mount_t *mp, /* file system mount structure */
- xfs_trans_t *tp, /* transaction pointer */
- xfs_agnumber_t agno, /* allocation group number */
- xfs_buf_t **bpp) /* allocation group hdr buf */
+xfs_read_agi(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ struct xfs_buf **bpp) /* allocation group hdr buf */
{
- xfs_agi_t *agi; /* allocation group header */
- int agi_ok; /* agi is consistent */
- xfs_buf_t *bp; /* allocation group hdr buf */
- xfs_perag_t *pag; /* per allocation group data */
- int error;
+ struct xfs_agi *agi; /* allocation group header */
+ int agi_ok; /* agi is consistent */
+ int error;
ASSERT(agno != NULLAGNUMBER);
- error = xfs_trans_read_buf(
- mp, tp, mp->m_ddev_targp,
+
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ XFS_FSS_TO_BB(mp, 1), 0, bpp);
if (error)
return error;
- ASSERT(bp && !XFS_BUF_GETERROR(bp));
+
+ ASSERT(*bpp && !XFS_BUF_GETERROR(*bpp));
+ agi = XFS_BUF_TO_AGI(*bpp);
/*
* Validate the magic number of the agi block.
*/
- agi = XFS_BUF_TO_AGI(bp);
- agi_ok =
- be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC &&
- XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum));
+ agi_ok = be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC &&
+ XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)) &&
+ be32_to_cpu(agi->agi_seqno) == agno;
if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
XFS_RANDOM_IALLOC_READ_AGI))) {
- XFS_CORRUPTION_ERROR("xfs_ialloc_read_agi", XFS_ERRLEVEL_LOW,
+ XFS_CORRUPTION_ERROR("xfs_read_agi", XFS_ERRLEVEL_LOW,
mp, agi);
- xfs_trans_brelse(tp, bp);
+ xfs_trans_brelse(tp, *bpp);
return XFS_ERROR(EFSCORRUPTED);
}
+
+ XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_AGI, XFS_AGI_REF);
+
+ xfs_check_agi_unlinked(agi);
+ return 0;
+}
+
+int
+xfs_ialloc_read_agi(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ struct xfs_buf **bpp) /* allocation group hdr buf */
+{
+ struct xfs_agi *agi; /* allocation group header */
+ struct xfs_perag *pag; /* per allocation group data */
+ int error;
+
+ error = xfs_read_agi(mp, tp, agno, bpp);
+ if (error)
+ return error;
+
+ agi = XFS_BUF_TO_AGI(*bpp);
pag = &mp->m_perag[agno];
+
if (!pag->pagi_init) {
pag->pagi_freecount = be32_to_cpu(agi->agi_freecount);
pag->pagi_count = be32_to_cpu(agi->agi_count);
pag->pagi_init = 1;
- } else {
- /*
- * It's possible for these to be out of sync if
- * we are in the middle of a forced shutdown.
- */
- ASSERT(pag->pagi_freecount == be32_to_cpu(agi->agi_freecount) ||
- XFS_FORCED_SHUTDOWN(mp));
}
-#ifdef DEBUG
- {
- int i;
-
- for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++)
- ASSERT(agi->agi_unlinked[i]);
- }
-#endif
-
- XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGI, XFS_AGI_REF);
- *bpp = bp;
+ /*
+ * It's possible for these to be out of sync if
+ * we are in the middle of a forced shutdown.
+ */
+ ASSERT(pag->pagi_freecount == be32_to_cpu(agi->agi_freecount) ||
+ XFS_FORCED_SHUTDOWN(mp));
return 0;
}
diff --git a/fs/xfs/xfs_ialloc.h b/fs/xfs/xfs_ialloc.h
index 4e30ec1d13bc..50f558a4e0a8 100644
--- a/fs/xfs/xfs_ialloc.h
+++ b/fs/xfs/xfs_ialloc.h
@@ -20,6 +20,7 @@
struct xfs_buf;
struct xfs_dinode;
+struct xfs_imap;
struct xfs_mount;
struct xfs_trans;
@@ -56,7 +57,6 @@ static inline int xfs_ialloc_find_free(xfs_inofree_t *fp)
}
-#ifdef __KERNEL__
/*
* Allocate an inode on disk.
* Mode is used to tell whether the new inode will need space, and whether
@@ -105,17 +105,14 @@ xfs_difree(
xfs_ino_t *first_ino); /* first inode in deleted cluster */
/*
- * Return the location of the inode in bno/len/off,
- * for mapping it into a buffer.
+ * Return the location of the inode in imap, for mapping it into a buffer.
*/
int
-xfs_dilocate(
+xfs_imap(
struct xfs_mount *mp, /* file system mount structure */
struct xfs_trans *tp, /* transaction pointer */
xfs_ino_t ino, /* inode to locate */
- xfs_fsblock_t *bno, /* output: block containing inode */
- int *len, /* output: num blocks in cluster*/
- int *off, /* output: index in block of inode */
+ struct xfs_imap *imap, /* location map structure */
uint flags); /* flags for inode btree lookup */
/*
@@ -154,6 +151,24 @@ xfs_ialloc_pagi_init(
struct xfs_trans *tp, /* transaction pointer */
xfs_agnumber_t agno); /* allocation group number */
-#endif /* __KERNEL__ */
+/*
+ * Lookup the first record greater than or equal to ino
+ * in the btree given by cur.
+ */
+int xfs_inobt_lookup_ge(struct xfs_btree_cur *cur, xfs_agino_t ino,
+ __int32_t fcnt, xfs_inofree_t free, int *stat);
+
+/*
+ * Lookup the first record less than or equal to ino
+ * in the btree given by cur.
+ */
+int xfs_inobt_lookup_le(struct xfs_btree_cur *cur, xfs_agino_t ino,
+ __int32_t fcnt, xfs_inofree_t free, int *stat);
+
+/*
+ * Get the data from the pointed-to record.
+ */
+extern int xfs_inobt_get_rec(struct xfs_btree_cur *cur, xfs_agino_t *ino,
+ __int32_t *fcnt, xfs_inofree_t *free, int *stat);
#endif /* __XFS_IALLOC_H__ */
diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c
index 83502f3edef0..99f2408e8d8e 100644
--- a/fs/xfs/xfs_ialloc_btree.c
+++ b/fs/xfs/xfs_ialloc_btree.c
@@ -35,2044 +35,349 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_ialloc.h"
#include "xfs_alloc.h"
#include "xfs_error.h"
-STATIC void xfs_inobt_log_block(xfs_trans_t *, xfs_buf_t *, int);
-STATIC void xfs_inobt_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC void xfs_inobt_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC void xfs_inobt_log_recs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
-STATIC int xfs_inobt_lshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_inobt_newroot(xfs_btree_cur_t *, int *);
-STATIC int xfs_inobt_rshift(xfs_btree_cur_t *, int, int *);
-STATIC int xfs_inobt_split(xfs_btree_cur_t *, int, xfs_agblock_t *,
- xfs_inobt_key_t *, xfs_btree_cur_t **, int *);
-STATIC int xfs_inobt_updkey(xfs_btree_cur_t *, xfs_inobt_key_t *, int);
-/*
- * Single level of the xfs_inobt_delete record deletion routine.
- * Delete record pointed to by cur/level.
- * Remove the record from its block then rebalance the tree.
- * Return 0 for error, 1 for done, 2 to go on to the next level.
- */
-STATIC int /* error */
-xfs_inobt_delrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level removing record from */
- int *stat) /* fail/done/go-on */
+STATIC int
+xfs_inobt_get_minrecs(
+ struct xfs_btree_cur *cur,
+ int level)
{
- xfs_buf_t *agbp; /* buffer for a.g. inode header */
- xfs_mount_t *mp; /* mount structure */
- xfs_agi_t *agi; /* allocation group inode header */
- xfs_inobt_block_t *block; /* btree block record/key lives in */
- xfs_agblock_t bno; /* btree block number */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop index */
- xfs_inobt_key_t key; /* kp points here if block is level 0 */
- xfs_inobt_key_t *kp = NULL; /* pointer to btree keys */
- xfs_agblock_t lbno; /* left block's block number */
- xfs_buf_t *lbp; /* left block's buffer pointer */
- xfs_inobt_block_t *left; /* left btree block */
- xfs_inobt_key_t *lkp; /* left block key pointer */
- xfs_inobt_ptr_t *lpp; /* left block address pointer */
- int lrecs = 0; /* number of records in left block */
- xfs_inobt_rec_t *lrp; /* left block record pointer */
- xfs_inobt_ptr_t *pp = NULL; /* pointer to btree addresses */
- int ptr; /* index in btree block for this rec */
- xfs_agblock_t rbno; /* right block's block number */
- xfs_buf_t *rbp; /* right block's buffer pointer */
- xfs_inobt_block_t *right; /* right btree block */
- xfs_inobt_key_t *rkp; /* right block key pointer */
- xfs_inobt_rec_t *rp; /* pointer to btree records */
- xfs_inobt_ptr_t *rpp; /* right block address pointer */
- int rrecs = 0; /* number of records in right block */
- int numrecs;
- xfs_inobt_rec_t *rrp; /* right block record pointer */
- xfs_btree_cur_t *tcur; /* temporary btree cursor */
-
- mp = cur->bc_mp;
-
- /*
- * Get the index of the entry being deleted, check for nothing there.
- */
- ptr = cur->bc_ptrs[level];
- if (ptr == 0) {
- *stat = 0;
- return 0;
- }
-
- /*
- * Get the buffer & block containing the record or key/ptr.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
-#endif
- /*
- * Fail if we're off the end of the block.
- */
+ return cur->bc_mp->m_inobt_mnr[level != 0];
+}
- numrecs = be16_to_cpu(block->bb_numrecs);
- if (ptr > numrecs) {
- *stat = 0;
- return 0;
- }
- /*
- * It's a nonleaf. Excise the key and ptr being deleted, by
- * sliding the entries past them down one.
- * Log the changed areas of the block.
- */
- if (level > 0) {
- kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
- pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = ptr; i < numrecs; i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(pp[i]), level)))
- return error;
- }
-#endif
- if (ptr < numrecs) {
- memmove(&kp[ptr - 1], &kp[ptr],
- (numrecs - ptr) * sizeof(*kp));
- memmove(&pp[ptr - 1], &pp[ptr],
- (numrecs - ptr) * sizeof(*kp));
- xfs_inobt_log_keys(cur, bp, ptr, numrecs - 1);
- xfs_inobt_log_ptrs(cur, bp, ptr, numrecs - 1);
- }
- }
- /*
- * It's a leaf. Excise the record being deleted, by sliding the
- * entries past it down one. Log the changed areas of the block.
- */
- else {
- rp = XFS_INOBT_REC_ADDR(block, 1, cur);
- if (ptr < numrecs) {
- memmove(&rp[ptr - 1], &rp[ptr],
- (numrecs - ptr) * sizeof(*rp));
- xfs_inobt_log_recs(cur, bp, ptr, numrecs - 1);
- }
- /*
- * If it's the first record in the block, we'll need a key
- * structure to pass up to the next level (updkey).
- */
- if (ptr == 1) {
- key.ir_startino = rp->ir_startino;
- kp = &key;
- }
- }
- /*
- * Decrement and log the number of entries in the block.
- */
- numrecs--;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_inobt_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
- /*
- * Is this the root level? If so, we're almost done.
- */
- if (level == cur->bc_nlevels - 1) {
- /*
- * If this is the root level,
- * and there's only one entry left,
- * and it's NOT the leaf level,
- * then we can get rid of this level.
- */
- if (numrecs == 1 && level > 0) {
- agbp = cur->bc_private.a.agbp;
- agi = XFS_BUF_TO_AGI(agbp);
- /*
- * pp is still set to the first pointer in the block.
- * Make it the new root of the btree.
- */
- bno = be32_to_cpu(agi->agi_root);
- agi->agi_root = *pp;
- be32_add_cpu(&agi->agi_level, -1);
- /*
- * Free the block.
- */
- if ((error = xfs_free_extent(cur->bc_tp,
- XFS_AGB_TO_FSB(mp, cur->bc_private.a.agno, bno), 1)))
- return error;
- xfs_trans_binval(cur->bc_tp, bp);
- xfs_ialloc_log_agi(cur->bc_tp, agbp,
- XFS_AGI_ROOT | XFS_AGI_LEVEL);
- /*
- * Update the cursor so there's one fewer level.
- */
- cur->bc_bufs[level] = NULL;
- cur->bc_nlevels--;
- } else if (level > 0 &&
- (error = xfs_inobt_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * If we deleted the leftmost entry in the block, update the
- * key values above us in the tree.
- */
- if (ptr == 1 && (error = xfs_inobt_updkey(cur, kp, level + 1)))
- return error;
- /*
- * If the number of records remaining in the block is at least
- * the minimum, we're done.
- */
- if (numrecs >= XFS_INOBT_BLOCK_MINRECS(level, cur)) {
- if (level > 0 &&
- (error = xfs_inobt_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * Otherwise, we have to move some records around to keep the
- * tree balanced. Look at the left and right sibling blocks to
- * see if we can re-balance by moving only one record.
- */
- rbno = be32_to_cpu(block->bb_rightsib);
- lbno = be32_to_cpu(block->bb_leftsib);
- bno = NULLAGBLOCK;
- ASSERT(rbno != NULLAGBLOCK || lbno != NULLAGBLOCK);
- /*
- * Duplicate the cursor so our btree manipulations here won't
- * disrupt the next level up.
- */
- if ((error = xfs_btree_dup_cursor(cur, &tcur)))
- return error;
- /*
- * If there's a right sibling, see if it's ok to shift an entry
- * out of it.
- */
- if (rbno != NULLAGBLOCK) {
- /*
- * Move the temp cursor to the last entry in the next block.
- * Actually any entry but the first would suffice.
- */
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- if ((error = xfs_inobt_increment(tcur, level, &i)))
- goto error0;
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- i = xfs_btree_lastrec(tcur, level);
- XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
- /*
- * Grab a pointer to the block.
- */
- rbp = tcur->bc_bufs[level];
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- goto error0;
-#endif
- /*
- * Grab the current block number, for future use.
- */
- bno = be32_to_cpu(right->bb_leftsib);
- /*
- * If right block is full enough so that removing one entry
- * won't make it too empty, and left-shifting an entry out
- * of right to us works, we're done.
- */
- if (be16_to_cpu(right->bb_numrecs) - 1 >=
- XFS_INOBT_BLOCK_MINRECS(level, cur)) {
- if ((error = xfs_inobt_lshift(tcur, level, &i)))
- goto error0;
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_INOBT_BLOCK_MINRECS(level, cur));
- xfs_btree_del_cursor(tcur,
- XFS_BTREE_NOERROR);
- if (level > 0 &&
- (error = xfs_inobt_decrement(cur, level,
- &i)))
- return error;
- *stat = 1;
- return 0;
- }
- }
- /*
- * Otherwise, grab the number of records in right for
- * future reference, and fix up the temp cursor to point
- * to our block again (last record).
- */
- rrecs = be16_to_cpu(right->bb_numrecs);
- if (lbno != NULLAGBLOCK) {
- xfs_btree_firstrec(tcur, level);
- if ((error = xfs_inobt_decrement(tcur, level, &i)))
- goto error0;
- }
- }
- /*
- * If there's a left sibling, see if it's ok to shift an entry
- * out of it.
- */
- if (lbno != NULLAGBLOCK) {
- /*
- * Move the temp cursor to the first entry in the
- * previous block.
- */
- xfs_btree_firstrec(tcur, level);
- if ((error = xfs_inobt_decrement(tcur, level, &i)))
- goto error0;
- xfs_btree_firstrec(tcur, level);
- /*
- * Grab a pointer to the block.
- */
- lbp = tcur->bc_bufs[level];
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- goto error0;
-#endif
- /*
- * Grab the current block number, for future use.
- */
- bno = be32_to_cpu(left->bb_rightsib);
- /*
- * If left block is full enough so that removing one entry
- * won't make it too empty, and right-shifting an entry out
- * of left to us works, we're done.
- */
- if (be16_to_cpu(left->bb_numrecs) - 1 >=
- XFS_INOBT_BLOCK_MINRECS(level, cur)) {
- if ((error = xfs_inobt_rshift(tcur, level, &i)))
- goto error0;
- if (i) {
- ASSERT(be16_to_cpu(block->bb_numrecs) >=
- XFS_INOBT_BLOCK_MINRECS(level, cur));
- xfs_btree_del_cursor(tcur,
- XFS_BTREE_NOERROR);
- if (level == 0)
- cur->bc_ptrs[0]++;
- *stat = 1;
- return 0;
- }
- }
- /*
- * Otherwise, grab the number of records in right for
- * future reference.
- */
- lrecs = be16_to_cpu(left->bb_numrecs);
- }
- /*
- * Delete the temp cursor, we're done with it.
- */
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- /*
- * If here, we need to do a join to keep the tree balanced.
- */
- ASSERT(bno != NULLAGBLOCK);
- /*
- * See if we can join with the left neighbor block.
- */
- if (lbno != NULLAGBLOCK &&
- lrecs + numrecs <= XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
- /*
- * Set "right" to be the starting block,
- * "left" to be the left neighbor.
- */
- rbno = bno;
- right = block;
- rrecs = be16_to_cpu(right->bb_numrecs);
- rbp = bp;
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, lbno, 0, &lbp,
- XFS_INO_BTREE_REF)))
- return error;
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
- lrecs = be16_to_cpu(left->bb_numrecs);
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
- }
- /*
- * If that won't work, see if we can join with the right neighbor block.
- */
- else if (rbno != NULLAGBLOCK &&
- rrecs + numrecs <= XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
- /*
- * Set "left" to be the starting block,
- * "right" to be the right neighbor.
- */
- lbno = bno;
- left = block;
- lrecs = be16_to_cpu(left->bb_numrecs);
- lbp = bp;
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, rbno, 0, &rbp,
- XFS_INO_BTREE_REF)))
- return error;
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
- rrecs = be16_to_cpu(right->bb_numrecs);
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
- }
- /*
- * Otherwise, we can't fix the imbalance.
- * Just return. This is probably a logic error, but it's not fatal.
- */
- else {
- if (level > 0 && (error = xfs_inobt_decrement(cur, level, &i)))
- return error;
- *stat = 1;
- return 0;
- }
- /*
- * We're now going to join "left" and "right" by moving all the stuff
- * in "right" to "left" and deleting "right".
- */
- if (level > 0) {
- /*
- * It's a non-leaf. Move keys and pointers.
- */
- lkp = XFS_INOBT_KEY_ADDR(left, lrecs + 1, cur);
- lpp = XFS_INOBT_PTR_ADDR(left, lrecs + 1, cur);
- rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
- rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < rrecs; i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i]), level)))
- return error;
- }
-#endif
- memcpy(lkp, rkp, rrecs * sizeof(*lkp));
- memcpy(lpp, rpp, rrecs * sizeof(*lpp));
- xfs_inobt_log_keys(cur, lbp, lrecs + 1, lrecs + rrecs);
- xfs_inobt_log_ptrs(cur, lbp, lrecs + 1, lrecs + rrecs);
- } else {
- /*
- * It's a leaf. Move records.
- */
- lrp = XFS_INOBT_REC_ADDR(left, lrecs + 1, cur);
- rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
- memcpy(lrp, rrp, rrecs * sizeof(*lrp));
- xfs_inobt_log_recs(cur, lbp, lrecs + 1, lrecs + rrecs);
- }
- /*
- * If we joined with the left neighbor, set the buffer in the
- * cursor to the left block, and fix up the index.
- */
- if (bp != lbp) {
- xfs_btree_setbuf(cur, level, lbp);
- cur->bc_ptrs[level] += lrecs;
- }
- /*
- * If we joined with the right neighbor and there's a level above
- * us, increment the cursor at that level.
- */
- else if (level + 1 < cur->bc_nlevels &&
- (error = xfs_alloc_increment(cur, level + 1, &i)))
- return error;
- /*
- * Fix up the number of records in the surviving block.
- */
- lrecs += rrecs;
- left->bb_numrecs = cpu_to_be16(lrecs);
- /*
- * Fix up the right block pointer in the surviving block, and log it.
- */
- left->bb_rightsib = right->bb_rightsib;
- xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
- /*
- * If there is a right sibling now, make it point to the
- * remaining block.
- */
- if (be32_to_cpu(left->bb_rightsib) != NULLAGBLOCK) {
- xfs_inobt_block_t *rrblock;
- xfs_buf_t *rrbp;
+STATIC struct xfs_btree_cur *
+xfs_inobt_dup_cursor(
+ struct xfs_btree_cur *cur)
+{
+ return xfs_inobt_init_cursor(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agbp, cur->bc_private.a.agno);
+}
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(left->bb_rightsib), 0,
- &rrbp, XFS_INO_BTREE_REF)))
- return error;
- rrblock = XFS_BUF_TO_INOBT_BLOCK(rrbp);
- if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
- return error;
- rrblock->bb_leftsib = cpu_to_be32(lbno);
- xfs_inobt_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
- }
- /*
- * Free the deleting block.
- */
- if ((error = xfs_free_extent(cur->bc_tp, XFS_AGB_TO_FSB(mp,
- cur->bc_private.a.agno, rbno), 1)))
- return error;
- xfs_trans_binval(cur->bc_tp, rbp);
- /*
- * Readjust the ptr at this level if it's not a leaf, since it's
- * still pointing at the deletion point, which makes the cursor
- * inconsistent. If this makes the ptr 0, the caller fixes it up.
- * We can't use decrement because it would change the next level up.
- */
- if (level > 0)
- cur->bc_ptrs[level]--;
- /*
- * Return value means the next level up has something to do.
- */
- *stat = 2;
- return 0;
+STATIC void
+xfs_inobt_set_root(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *nptr,
+ int inc) /* level change */
+{
+ struct xfs_buf *agbp = cur->bc_private.a.agbp;
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
-error0:
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
+ agi->agi_root = nptr->s;
+ be32_add_cpu(&agi->agi_level, inc);
+ xfs_ialloc_log_agi(cur->bc_tp, agbp, XFS_AGI_ROOT | XFS_AGI_LEVEL);
}
-/*
- * Insert one record/level. Return information to the caller
- * allowing the next level up to proceed if necessary.
- */
-STATIC int /* error */
-xfs_inobt_insrec(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to insert record at */
- xfs_agblock_t *bnop, /* i/o: block number inserted */
- xfs_inobt_rec_t *recp, /* i/o: record data inserted */
- xfs_btree_cur_t **curp, /* output: new cursor replacing cur */
- int *stat) /* success/failure */
+STATIC int
+xfs_inobt_alloc_block(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *start,
+ union xfs_btree_ptr *new,
+ int length,
+ int *stat)
{
- xfs_inobt_block_t *block; /* btree block record/key lives in */
- xfs_buf_t *bp; /* buffer for block */
- int error; /* error return value */
- int i; /* loop index */
- xfs_inobt_key_t key; /* key value being inserted */
- xfs_inobt_key_t *kp=NULL; /* pointer to btree keys */
- xfs_agblock_t nbno; /* block number of allocated block */
- xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */
- xfs_inobt_key_t nkey; /* new key value, from split */
- xfs_inobt_rec_t nrec; /* new record value, for caller */
- int numrecs;
- int optr; /* old ptr value */
- xfs_inobt_ptr_t *pp; /* pointer to btree addresses */
- int ptr; /* index in btree block for this rec */
- xfs_inobt_rec_t *rp=NULL; /* pointer to btree records */
+ xfs_alloc_arg_t args; /* block allocation args */
+ int error; /* error return value */
+ xfs_agblock_t sbno = be32_to_cpu(start->s);
- /*
- * GCC doesn't understand the (arguably complex) control flow in
- * this function and complains about uninitialized structure fields
- * without this.
- */
- memset(&nrec, 0, sizeof(nrec));
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
- /*
- * If we made it to the root level, allocate a new root block
- * and we're done.
- */
- if (level >= cur->bc_nlevels) {
- error = xfs_inobt_newroot(cur, &i);
- *bnop = NULLAGBLOCK;
- *stat = i;
+ memset(&args, 0, sizeof(args));
+ args.tp = cur->bc_tp;
+ args.mp = cur->bc_mp;
+ args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.a.agno, sbno);
+ args.minlen = 1;
+ args.maxlen = 1;
+ args.prod = 1;
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+
+ error = xfs_alloc_vextent(&args);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
return error;
}
- /*
- * Make a key out of the record data to be inserted, and save it.
- */
- key.ir_startino = recp->ir_startino;
- optr = ptr = cur->bc_ptrs[level];
- /*
- * If we're off the left edge, return failure.
- */
- if (ptr == 0) {
+ if (args.fsbno == NULLFSBLOCK) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
*stat = 0;
return 0;
}
- /*
- * Get pointers to the btree buffer and block.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- numrecs = be16_to_cpu(block->bb_numrecs);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
- /*
- * Check that the new entry is being inserted in the right place.
- */
- if (ptr <= numrecs) {
- if (level == 0) {
- rp = XFS_INOBT_REC_ADDR(block, ptr, cur);
- xfs_btree_check_rec(cur->bc_btnum, recp, rp);
- } else {
- kp = XFS_INOBT_KEY_ADDR(block, ptr, cur);
- xfs_btree_check_key(cur->bc_btnum, &key, kp);
- }
- }
-#endif
- nbno = NULLAGBLOCK;
- ncur = NULL;
- /*
- * If the block is full, we can't insert the new entry until we
- * make the block un-full.
- */
- if (numrecs == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
- /*
- * First, try shifting an entry to the right neighbor.
- */
- if ((error = xfs_inobt_rshift(cur, level, &i)))
- return error;
- if (i) {
- /* nothing */
- }
- /*
- * Next, try shifting an entry to the left neighbor.
- */
- else {
- if ((error = xfs_inobt_lshift(cur, level, &i)))
- return error;
- if (i) {
- optr = ptr = cur->bc_ptrs[level];
- } else {
- /*
- * Next, try splitting the current block
- * in half. If this works we have to
- * re-set our variables because
- * we could be in a different block now.
- */
- if ((error = xfs_inobt_split(cur, level, &nbno,
- &nkey, &ncur, &i)))
- return error;
- if (i) {
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur,
- block, level, bp)))
- return error;
-#endif
- ptr = cur->bc_ptrs[level];
- nrec.ir_startino = nkey.ir_startino;
- } else {
- /*
- * Otherwise the insert fails.
- */
- *stat = 0;
- return 0;
- }
- }
- }
- }
- /*
- * At this point we know there's room for our new entry in the block
- * we're pointing at.
- */
- numrecs = be16_to_cpu(block->bb_numrecs);
- if (level > 0) {
- /*
- * It's a non-leaf entry. Make a hole for the new data
- * in the key and ptr regions of the block.
- */
- kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
- pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
-#ifdef DEBUG
- for (i = numrecs; i >= ptr; i--) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(pp[i - 1]), level)))
- return error;
- }
-#endif
- memmove(&kp[ptr], &kp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*kp));
- memmove(&pp[ptr], &pp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*pp));
- /*
- * Now stuff the new data in, bump numrecs and log the new data.
- */
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, *bnop, level)))
- return error;
-#endif
- kp[ptr - 1] = key;
- pp[ptr - 1] = cpu_to_be32(*bnop);
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_inobt_log_keys(cur, bp, ptr, numrecs);
- xfs_inobt_log_ptrs(cur, bp, ptr, numrecs);
- } else {
- /*
- * It's a leaf entry. Make a hole for the new record.
- */
- rp = XFS_INOBT_REC_ADDR(block, 1, cur);
- memmove(&rp[ptr], &rp[ptr - 1],
- (numrecs - ptr + 1) * sizeof(*rp));
- /*
- * Now stuff the new record in, bump numrecs
- * and log the new data.
- */
- rp[ptr - 1] = *recp;
- numrecs++;
- block->bb_numrecs = cpu_to_be16(numrecs);
- xfs_inobt_log_recs(cur, bp, ptr, numrecs);
- }
- /*
- * Log the new number of records in the btree header.
- */
- xfs_inobt_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
-#ifdef DEBUG
- /*
- * Check that the key/record is in the right place, now.
- */
- if (ptr < numrecs) {
- if (level == 0)
- xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1,
- rp + ptr);
- else
- xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1,
- kp + ptr);
- }
-#endif
- /*
- * If we inserted at the start of a block, update the parents' keys.
- */
- if (optr == 1 && (error = xfs_inobt_updkey(cur, &key, level + 1)))
- return error;
- /*
- * Return the new block number, if any.
- * If there is one, give back a record value and a cursor too.
- */
- *bnop = nbno;
- if (nbno != NULLAGBLOCK) {
- *recp = nrec;
- *curp = ncur;
- }
+ ASSERT(args.len == 1);
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+
+ new->s = cpu_to_be32(XFS_FSB_TO_AGBNO(args.mp, args.fsbno));
*stat = 1;
return 0;
}
-/*
- * Log header fields from a btree block.
- */
-STATIC void
-xfs_inobt_log_block(
- xfs_trans_t *tp, /* transaction pointer */
- xfs_buf_t *bp, /* buffer containing btree block */
- int fields) /* mask of fields: XFS_BB_... */
+STATIC int
+xfs_inobt_free_block(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp)
{
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- static const short offsets[] = { /* table of offsets */
- offsetof(xfs_inobt_block_t, bb_magic),
- offsetof(xfs_inobt_block_t, bb_level),
- offsetof(xfs_inobt_block_t, bb_numrecs),
- offsetof(xfs_inobt_block_t, bb_leftsib),
- offsetof(xfs_inobt_block_t, bb_rightsib),
- sizeof(xfs_inobt_block_t)
- };
+ xfs_fsblock_t fsbno;
+ int error;
- xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first, &last);
- xfs_trans_log_buf(tp, bp, first, last);
+ fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, XFS_BUF_ADDR(bp));
+ error = xfs_free_extent(cur->bc_tp, fsbno, 1);
+ if (error)
+ return error;
+
+ xfs_trans_binval(cur->bc_tp, bp);
+ return error;
}
-/*
- * Log keys from a btree block (nonleaf).
- */
-STATIC void
-xfs_inobt_log_keys(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int kfirst, /* index of first key to log */
- int klast) /* index of last key to log */
+STATIC int
+xfs_inobt_get_maxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
{
- xfs_inobt_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- xfs_inobt_key_t *kp; /* key pointer in btree block */
- int last; /* last byte offset logged */
-
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ return cur->bc_mp->m_inobt_mxr[level != 0];
}
-/*
- * Log block pointer fields from a btree block (nonleaf).
- */
STATIC void
-xfs_inobt_log_ptrs(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int pfirst, /* index of first pointer to log */
- int plast) /* index of last pointer to log */
+xfs_inobt_init_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
{
- xfs_inobt_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- xfs_inobt_ptr_t *pp; /* block-pointer pointer in btree blk */
-
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+ key->inobt.ir_startino = rec->inobt.ir_startino;
}
-/*
- * Log records from a btree block (leaf).
- */
STATIC void
-xfs_inobt_log_recs(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_buf_t *bp, /* buffer containing btree block */
- int rfirst, /* index of first record to log */
- int rlast) /* index of last record to log */
+xfs_inobt_init_rec_from_key(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
{
- xfs_inobt_block_t *block; /* btree block to log from */
- int first; /* first byte offset logged */
- int last; /* last byte offset logged */
- xfs_inobt_rec_t *rp; /* record pointer for btree block */
+ rec->inobt.ir_startino = key->inobt.ir_startino;
+}
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- rp = XFS_INOBT_REC_ADDR(block, 1, cur);
- first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
- last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
- xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+STATIC void
+xfs_inobt_init_rec_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec)
+{
+ rec->inobt.ir_startino = cpu_to_be32(cur->bc_rec.i.ir_startino);
+ rec->inobt.ir_freecount = cpu_to_be32(cur->bc_rec.i.ir_freecount);
+ rec->inobt.ir_free = cpu_to_be64(cur->bc_rec.i.ir_free);
}
/*
- * Lookup the record. The cursor is made to point to it, based on dir.
- * Return 0 if can't find any such record, 1 for success.
+ * intial value of ptr for lookup
*/
-STATIC int /* error */
-xfs_inobt_lookup(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_lookup_t dir, /* <=, ==, or >= */
- int *stat) /* success/failure */
+STATIC void
+xfs_inobt_init_ptr_from_cur(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_ptr *ptr)
{
- xfs_agblock_t agbno; /* a.g. relative btree block number */
- xfs_agnumber_t agno; /* allocation group number */
- xfs_inobt_block_t *block=NULL; /* current btree block */
- __int64_t diff; /* difference for the current key */
- int error; /* error return value */
- int keyno=0; /* current key number */
- int level; /* level in the btree */
- xfs_mount_t *mp; /* file system mount point */
-
- /*
- * Get the allocation group header, and the root block number.
- */
- mp = cur->bc_mp;
- {
- xfs_agi_t *agi; /* a.g. inode header */
-
- agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
- agno = be32_to_cpu(agi->agi_seqno);
- agbno = be32_to_cpu(agi->agi_root);
- }
- /*
- * Iterate over each level in the btree, starting at the root.
- * For each level above the leaves, find the key we need, based
- * on the lookup record, then follow the corresponding block
- * pointer down to the next level.
- */
- for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
- xfs_buf_t *bp; /* buffer pointer for btree block */
- xfs_daddr_t d; /* disk address of btree block */
-
- /*
- * Get the disk address we're looking for.
- */
- d = XFS_AGB_TO_DADDR(mp, agno, agbno);
- /*
- * If the old buffer at this level is for a different block,
- * throw it away, otherwise just use it.
- */
- bp = cur->bc_bufs[level];
- if (bp && XFS_BUF_ADDR(bp) != d)
- bp = NULL;
- if (!bp) {
- /*
- * Need to get a new buffer. Read it, then
- * set it in the cursor, releasing the old one.
- */
- if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
- agno, agbno, 0, &bp, XFS_INO_BTREE_REF)))
- return error;
- xfs_btree_setbuf(cur, level, bp);
- /*
- * Point to the btree block, now that we have the buffer
- */
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, level,
- bp)))
- return error;
- } else
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- /*
- * If we already had a key match at a higher level, we know
- * we need to use the first entry in this block.
- */
- if (diff == 0)
- keyno = 1;
- /*
- * Otherwise we need to search this block. Do a binary search.
- */
- else {
- int high; /* high entry number */
- xfs_inobt_key_t *kkbase=NULL;/* base of keys in block */
- xfs_inobt_rec_t *krbase=NULL;/* base of records in block */
- int low; /* low entry number */
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
- /*
- * Get a pointer to keys or records.
- */
- if (level > 0)
- kkbase = XFS_INOBT_KEY_ADDR(block, 1, cur);
- else
- krbase = XFS_INOBT_REC_ADDR(block, 1, cur);
- /*
- * Set low and high entry numbers, 1-based.
- */
- low = 1;
- if (!(high = be16_to_cpu(block->bb_numrecs))) {
- /*
- * If the block is empty, the tree must
- * be an empty leaf.
- */
- ASSERT(level == 0 && cur->bc_nlevels == 1);
- cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
- *stat = 0;
- return 0;
- }
- /*
- * Binary search the block.
- */
- while (low <= high) {
- xfs_agino_t startino; /* key value */
-
- /*
- * keyno is average of low and high.
- */
- keyno = (low + high) >> 1;
- /*
- * Get startino.
- */
- if (level > 0) {
- xfs_inobt_key_t *kkp;
-
- kkp = kkbase + keyno - 1;
- startino = be32_to_cpu(kkp->ir_startino);
- } else {
- xfs_inobt_rec_t *krp;
-
- krp = krbase + keyno - 1;
- startino = be32_to_cpu(krp->ir_startino);
- }
- /*
- * Compute difference to get next direction.
- */
- diff = (__int64_t)
- startino - cur->bc_rec.i.ir_startino;
- /*
- * Less than, move right.
- */
- if (diff < 0)
- low = keyno + 1;
- /*
- * Greater than, move left.
- */
- else if (diff > 0)
- high = keyno - 1;
- /*
- * Equal, we're done.
- */
- else
- break;
- }
- }
- /*
- * If there are more levels, set up for the next level
- * by getting the block number and filling in the cursor.
- */
- if (level > 0) {
- /*
- * If we moved left, need the previous key number,
- * unless there isn't one.
- */
- if (diff > 0 && --keyno < 1)
- keyno = 1;
- agbno = be32_to_cpu(*XFS_INOBT_PTR_ADDR(block, keyno, cur));
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, agbno, level)))
- return error;
-#endif
- cur->bc_ptrs[level] = keyno;
- }
- }
- /*
- * Done with the search.
- * See if we need to adjust the results.
- */
- if (dir != XFS_LOOKUP_LE && diff < 0) {
- keyno++;
- /*
- * If ge search and we went off the end of the block, but it's
- * not the last block, we're in the wrong block.
- */
- if (dir == XFS_LOOKUP_GE &&
- keyno > be16_to_cpu(block->bb_numrecs) &&
- be32_to_cpu(block->bb_rightsib) != NULLAGBLOCK) {
- int i;
+ ASSERT(cur->bc_private.a.agno == be32_to_cpu(agi->agi_seqno));
- cur->bc_ptrs[0] = keyno;
- if ((error = xfs_inobt_increment(cur, 0, &i)))
- return error;
- ASSERT(i == 1);
- *stat = 1;
- return 0;
- }
- }
- else if (dir == XFS_LOOKUP_LE && diff > 0)
- keyno--;
- cur->bc_ptrs[0] = keyno;
- /*
- * Return if we succeeded or not.
- */
- if (keyno == 0 || keyno > be16_to_cpu(block->bb_numrecs))
- *stat = 0;
- else
- *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
- return 0;
+ ptr->s = agi->agi_root;
}
-/*
- * Move 1 record left from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_inobt_lshift(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to shift record on */
- int *stat) /* success/failure */
+STATIC __int64_t
+xfs_inobt_key_diff(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key)
{
- int error; /* error return value */
-#ifdef DEBUG
- int i; /* loop index */
-#endif
- xfs_inobt_key_t key; /* key value for leaf level upward */
- xfs_buf_t *lbp; /* buffer for left neighbor block */
- xfs_inobt_block_t *left; /* left neighbor btree block */
- xfs_inobt_key_t *lkp=NULL; /* key pointer for left block */
- xfs_inobt_ptr_t *lpp; /* address pointer for left block */
- xfs_inobt_rec_t *lrp=NULL; /* record pointer for left block */
- int nrec; /* new number of left block entries */
- xfs_buf_t *rbp; /* buffer for right (current) block */
- xfs_inobt_block_t *right; /* right (current) btree block */
- xfs_inobt_key_t *rkp=NULL; /* key pointer for right block */
- xfs_inobt_ptr_t *rpp=NULL; /* address pointer for right block */
- xfs_inobt_rec_t *rrp=NULL; /* record pointer for right block */
-
- /*
- * Set up variables for this block as "right".
- */
- rbp = cur->bc_bufs[level];
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
-#endif
- /*
- * If we've got no left sibling then we can't shift an entry left.
- */
- if (be32_to_cpu(right->bb_leftsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * If the cursor entry is the one that would be moved, don't
- * do it... it's too complicated.
- */
- if (cur->bc_ptrs[level] <= 1) {
- *stat = 0;
- return 0;
- }
- /*
- * Set up the left neighbor as "left".
- */
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(right->bb_leftsib),
- 0, &lbp, XFS_INO_BTREE_REF)))
- return error;
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
- /*
- * If it's full, it can't take another entry.
- */
- if (be16_to_cpu(left->bb_numrecs) == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
- *stat = 0;
- return 0;
- }
- nrec = be16_to_cpu(left->bb_numrecs) + 1;
- /*
- * If non-leaf, copy a key and a ptr to the left block.
- */
- if (level > 0) {
- lkp = XFS_INOBT_KEY_ADDR(left, nrec, cur);
- rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
- *lkp = *rkp;
- xfs_inobt_log_keys(cur, lbp, nrec, nrec);
- lpp = XFS_INOBT_PTR_ADDR(left, nrec, cur);
- rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(*rpp), level)))
- return error;
-#endif
- *lpp = *rpp;
- xfs_inobt_log_ptrs(cur, lbp, nrec, nrec);
- }
- /*
- * If leaf, copy a record to the left block.
- */
- else {
- lrp = XFS_INOBT_REC_ADDR(left, nrec, cur);
- rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
- *lrp = *rrp;
- xfs_inobt_log_recs(cur, lbp, nrec, nrec);
- }
- /*
- * Bump and log left's numrecs, decrement and log right's numrecs.
- */
- be16_add_cpu(&left->bb_numrecs, 1);
- xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
-#ifdef DEBUG
- if (level > 0)
- xfs_btree_check_key(cur->bc_btnum, lkp - 1, lkp);
- else
- xfs_btree_check_rec(cur->bc_btnum, lrp - 1, lrp);
-#endif
- be16_add_cpu(&right->bb_numrecs, -1);
- xfs_inobt_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
- /*
- * Slide the contents of right down one entry.
- */
- if (level > 0) {
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(right->bb_numrecs); i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i + 1]),
- level)))
- return error;
- }
-#endif
- memmove(rkp, rkp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memmove(rpp, rpp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
- xfs_inobt_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- xfs_inobt_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- } else {
- memmove(rrp, rrp + 1, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- xfs_inobt_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- key.ir_startino = rrp->ir_startino;
- rkp = &key;
- }
- /*
- * Update the parent key values of right.
- */
- if ((error = xfs_inobt_updkey(cur, rkp, level + 1)))
- return error;
- /*
- * Slide the cursor value left one.
- */
- cur->bc_ptrs[level]--;
- *stat = 1;
- return 0;
+ return (__int64_t)be32_to_cpu(key->inobt.ir_startino) -
+ cur->bc_rec.i.ir_startino;
}
-/*
- * Allocate a new root block, fill it in.
- */
-STATIC int /* error */
-xfs_inobt_newroot(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
+STATIC int
+xfs_inobt_kill_root(
+ struct xfs_btree_cur *cur,
+ struct xfs_buf *bp,
+ int level,
+ union xfs_btree_ptr *newroot)
{
- xfs_agi_t *agi; /* a.g. inode header */
- xfs_alloc_arg_t args; /* allocation argument structure */
- xfs_inobt_block_t *block; /* one half of the old root block */
- xfs_buf_t *bp; /* buffer containing block */
- int error; /* error return value */
- xfs_inobt_key_t *kp; /* btree key pointer */
- xfs_agblock_t lbno; /* left block number */
- xfs_buf_t *lbp; /* left buffer pointer */
- xfs_inobt_block_t *left; /* left btree block */
- xfs_buf_t *nbp; /* new (root) buffer */
- xfs_inobt_block_t *new; /* new (root) btree block */
- int nptr; /* new value for key index, 1 or 2 */
- xfs_inobt_ptr_t *pp; /* btree address pointer */
- xfs_agblock_t rbno; /* right block number */
- xfs_buf_t *rbp; /* right buffer pointer */
- xfs_inobt_block_t *right; /* right btree block */
- xfs_inobt_rec_t *rp; /* btree record pointer */
+ int error;
- ASSERT(cur->bc_nlevels < XFS_IN_MAXLEVELS(cur->bc_mp));
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+ XFS_BTREE_STATS_INC(cur, killroot);
/*
- * Get a block & a buffer.
+ * Update the root pointer, decreasing the level by 1 and then
+ * free the old root.
*/
- agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
- args.tp = cur->bc_tp;
- args.mp = cur->bc_mp;
- args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.a.agno,
- be32_to_cpu(agi->agi_root));
- args.mod = args.minleft = args.alignment = args.total = args.wasdel =
- args.isfl = args.userdata = args.minalignslop = 0;
- args.minlen = args.maxlen = args.prod = 1;
- args.type = XFS_ALLOCTYPE_NEAR_BNO;
- if ((error = xfs_alloc_vextent(&args)))
+ xfs_inobt_set_root(cur, newroot, -1);
+ error = xfs_inobt_free_block(cur, bp);
+ if (error) {
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
return error;
- /*
- * None available, we fail.
- */
- if (args.fsbno == NULLFSBLOCK) {
- *stat = 0;
- return 0;
- }
- ASSERT(args.len == 1);
- nbp = xfs_btree_get_bufs(args.mp, args.tp, args.agno, args.agbno, 0);
- new = XFS_BUF_TO_INOBT_BLOCK(nbp);
- /*
- * Set the root data in the a.g. inode structure.
- */
- agi->agi_root = cpu_to_be32(args.agbno);
- be32_add_cpu(&agi->agi_level, 1);
- xfs_ialloc_log_agi(args.tp, cur->bc_private.a.agbp,
- XFS_AGI_ROOT | XFS_AGI_LEVEL);
- /*
- * At the previous root level there are now two blocks: the old
- * root, and the new block generated when it was split.
- * We don't know which one the cursor is pointing at, so we
- * set up variables "left" and "right" for each case.
- */
- bp = cur->bc_bufs[cur->bc_nlevels - 1];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, cur->bc_nlevels - 1, bp)))
- return error;
-#endif
- if (be32_to_cpu(block->bb_rightsib) != NULLAGBLOCK) {
- /*
- * Our block is left, pick up the right block.
- */
- lbp = bp;
- lbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(lbp));
- left = block;
- rbno = be32_to_cpu(left->bb_rightsib);
- if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
- rbno, 0, &rbp, XFS_INO_BTREE_REF)))
- return error;
- bp = rbp;
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
- if ((error = xfs_btree_check_sblock(cur, right,
- cur->bc_nlevels - 1, rbp)))
- return error;
- nptr = 1;
- } else {
- /*
- * Our block is right, pick up the left block.
- */
- rbp = bp;
- rbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(rbp));
- right = block;
- lbno = be32_to_cpu(right->bb_leftsib);
- if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
- lbno, 0, &lbp, XFS_INO_BTREE_REF)))
- return error;
- bp = lbp;
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
- if ((error = xfs_btree_check_sblock(cur, left,
- cur->bc_nlevels - 1, lbp)))
- return error;
- nptr = 2;
- }
- /*
- * Fill in the new block's btree header and log it.
- */
- new->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
- new->bb_level = cpu_to_be16(cur->bc_nlevels);
- new->bb_numrecs = cpu_to_be16(2);
- new->bb_leftsib = cpu_to_be32(NULLAGBLOCK);
- new->bb_rightsib = cpu_to_be32(NULLAGBLOCK);
- xfs_inobt_log_block(args.tp, nbp, XFS_BB_ALL_BITS);
- ASSERT(lbno != NULLAGBLOCK && rbno != NULLAGBLOCK);
- /*
- * Fill in the key data in the new root.
- */
- kp = XFS_INOBT_KEY_ADDR(new, 1, cur);
- if (be16_to_cpu(left->bb_level) > 0) {
- kp[0] = *XFS_INOBT_KEY_ADDR(left, 1, cur);
- kp[1] = *XFS_INOBT_KEY_ADDR(right, 1, cur);
- } else {
- rp = XFS_INOBT_REC_ADDR(left, 1, cur);
- kp[0].ir_startino = rp->ir_startino;
- rp = XFS_INOBT_REC_ADDR(right, 1, cur);
- kp[1].ir_startino = rp->ir_startino;
}
- xfs_inobt_log_keys(cur, nbp, 1, 2);
- /*
- * Fill in the pointer data in the new root.
- */
- pp = XFS_INOBT_PTR_ADDR(new, 1, cur);
- pp[0] = cpu_to_be32(lbno);
- pp[1] = cpu_to_be32(rbno);
- xfs_inobt_log_ptrs(cur, nbp, 1, 2);
- /*
- * Fix up the cursor.
- */
- xfs_btree_setbuf(cur, cur->bc_nlevels, nbp);
- cur->bc_ptrs[cur->bc_nlevels] = nptr;
- cur->bc_nlevels++;
- *stat = 1;
- return 0;
-}
-/*
- * Move 1 record right from cur/level if possible.
- * Update cur to reflect the new path.
- */
-STATIC int /* error */
-xfs_inobt_rshift(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to shift record on */
- int *stat) /* success/failure */
-{
- int error; /* error return value */
- int i; /* loop index */
- xfs_inobt_key_t key; /* key value for leaf level upward */
- xfs_buf_t *lbp; /* buffer for left (current) block */
- xfs_inobt_block_t *left; /* left (current) btree block */
- xfs_inobt_key_t *lkp; /* key pointer for left block */
- xfs_inobt_ptr_t *lpp; /* address pointer for left block */
- xfs_inobt_rec_t *lrp; /* record pointer for left block */
- xfs_buf_t *rbp; /* buffer for right neighbor block */
- xfs_inobt_block_t *right; /* right neighbor btree block */
- xfs_inobt_key_t *rkp; /* key pointer for right block */
- xfs_inobt_ptr_t *rpp; /* address pointer for right block */
- xfs_inobt_rec_t *rrp=NULL; /* record pointer for right block */
- xfs_btree_cur_t *tcur; /* temporary cursor */
+ XFS_BTREE_STATS_INC(cur, free);
- /*
- * Set up variables for this block as "left".
- */
- lbp = cur->bc_bufs[level];
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
-#endif
- /*
- * If we've got no right sibling then we can't shift an entry right.
- */
- if (be32_to_cpu(left->bb_rightsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * If the cursor entry is the one that would be moved, don't
- * do it... it's too complicated.
- */
- if (cur->bc_ptrs[level] >= be16_to_cpu(left->bb_numrecs)) {
- *stat = 0;
- return 0;
- }
- /*
- * Set up the right neighbor as "right".
- */
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, be32_to_cpu(left->bb_rightsib),
- 0, &rbp, XFS_INO_BTREE_REF)))
- return error;
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
- if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
- return error;
- /*
- * If it's full, it can't take another entry.
- */
- if (be16_to_cpu(right->bb_numrecs) == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
- *stat = 0;
- return 0;
- }
- /*
- * Make a hole at the start of the right neighbor block, then
- * copy the last left block entry to the hole.
- */
- if (level > 0) {
- lkp = XFS_INOBT_KEY_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- lpp = XFS_INOBT_PTR_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
- rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = be16_to_cpu(right->bb_numrecs) - 1; i >= 0; i--) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(rpp[i]), level)))
- return error;
- }
-#endif
- memmove(rkp + 1, rkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memmove(rpp + 1, rpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
-#ifdef DEBUG
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(*lpp), level)))
- return error;
-#endif
- *rkp = *lkp;
- *rpp = *lpp;
- xfs_inobt_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- xfs_inobt_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- } else {
- lrp = XFS_INOBT_REC_ADDR(left, be16_to_cpu(left->bb_numrecs), cur);
- rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
- memmove(rrp + 1, rrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- *rrp = *lrp;
- xfs_inobt_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs) + 1);
- key.ir_startino = rrp->ir_startino;
- rkp = &key;
- }
- /*
- * Decrement and log left's numrecs, bump and log right's numrecs.
- */
- be16_add_cpu(&left->bb_numrecs, -1);
- xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
- be16_add_cpu(&right->bb_numrecs, 1);
-#ifdef DEBUG
- if (level > 0)
- xfs_btree_check_key(cur->bc_btnum, rkp, rkp + 1);
- else
- xfs_btree_check_rec(cur->bc_btnum, rrp, rrp + 1);
-#endif
- xfs_inobt_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
- /*
- * Using a temporary cursor, update the parent key values of the
- * block on the right.
- */
- if ((error = xfs_btree_dup_cursor(cur, &tcur)))
- return error;
- xfs_btree_lastrec(tcur, level);
- if ((error = xfs_inobt_increment(tcur, level, &i)) ||
- (error = xfs_inobt_updkey(tcur, rkp, level + 1))) {
- xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
- return error;
- }
- xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
- *stat = 1;
+ cur->bc_bufs[level] = NULL;
+ cur->bc_nlevels--;
+
+ XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
return 0;
}
-/*
- * Split cur/level block in half.
- * Return new block number and its first record (to be inserted into parent).
- */
-STATIC int /* error */
-xfs_inobt_split(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level to split */
- xfs_agblock_t *bnop, /* output: block number allocated */
- xfs_inobt_key_t *keyp, /* output: first key of new block */
- xfs_btree_cur_t **curp, /* output: new cursor */
- int *stat) /* success/failure */
-{
- xfs_alloc_arg_t args; /* allocation argument structure */
- int error; /* error return value */
- int i; /* loop index/record number */
- xfs_agblock_t lbno; /* left (current) block number */
- xfs_buf_t *lbp; /* buffer for left block */
- xfs_inobt_block_t *left; /* left (current) btree block */
- xfs_inobt_key_t *lkp; /* left btree key pointer */
- xfs_inobt_ptr_t *lpp; /* left btree address pointer */
- xfs_inobt_rec_t *lrp; /* left btree record pointer */
- xfs_buf_t *rbp; /* buffer for right block */
- xfs_inobt_block_t *right; /* right (new) btree block */
- xfs_inobt_key_t *rkp; /* right btree key pointer */
- xfs_inobt_ptr_t *rpp; /* right btree address pointer */
- xfs_inobt_rec_t *rrp; /* right btree record pointer */
-
- /*
- * Set up left block (current one).
- */
- lbp = cur->bc_bufs[level];
- args.tp = cur->bc_tp;
- args.mp = cur->bc_mp;
- lbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(lbp));
- /*
- * Allocate the new block.
- * If we can't do it, we're toast. Give up.
- */
- args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.a.agno, lbno);
- args.mod = args.minleft = args.alignment = args.total = args.wasdel =
- args.isfl = args.userdata = args.minalignslop = 0;
- args.minlen = args.maxlen = args.prod = 1;
- args.type = XFS_ALLOCTYPE_NEAR_BNO;
- if ((error = xfs_alloc_vextent(&args)))
- return error;
- if (args.fsbno == NULLFSBLOCK) {
- *stat = 0;
- return 0;
- }
- ASSERT(args.len == 1);
- rbp = xfs_btree_get_bufs(args.mp, args.tp, args.agno, args.agbno, 0);
- /*
- * Set up the new block as "right".
- */
- right = XFS_BUF_TO_INOBT_BLOCK(rbp);
- /*
- * "Left" is the current (according to the cursor) block.
- */
- left = XFS_BUF_TO_INOBT_BLOCK(lbp);
#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
- return error;
-#endif
- /*
- * Fill in the btree header for the new block.
- */
- right->bb_magic = cpu_to_be32(xfs_magics[cur->bc_btnum]);
- right->bb_level = left->bb_level;
- right->bb_numrecs = cpu_to_be16(be16_to_cpu(left->bb_numrecs) / 2);
- /*
- * Make sure that if there's an odd number of entries now, that
- * each new block will have the same number of entries.
- */
- if ((be16_to_cpu(left->bb_numrecs) & 1) &&
- cur->bc_ptrs[level] <= be16_to_cpu(right->bb_numrecs) + 1)
- be16_add_cpu(&right->bb_numrecs, 1);
- i = be16_to_cpu(left->bb_numrecs) - be16_to_cpu(right->bb_numrecs) + 1;
- /*
- * For non-leaf blocks, copy keys and addresses over to the new block.
- */
- if (level > 0) {
- lkp = XFS_INOBT_KEY_ADDR(left, i, cur);
- lpp = XFS_INOBT_PTR_ADDR(left, i, cur);
- rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
- rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
-#ifdef DEBUG
- for (i = 0; i < be16_to_cpu(right->bb_numrecs); i++) {
- if ((error = xfs_btree_check_sptr(cur, be32_to_cpu(lpp[i]), level)))
- return error;
- }
-#endif
- memcpy(rkp, lkp, be16_to_cpu(right->bb_numrecs) * sizeof(*rkp));
- memcpy(rpp, lpp, be16_to_cpu(right->bb_numrecs) * sizeof(*rpp));
- xfs_inobt_log_keys(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- xfs_inobt_log_ptrs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- *keyp = *rkp;
- }
- /*
- * For leaf blocks, copy records over to the new block.
- */
- else {
- lrp = XFS_INOBT_REC_ADDR(left, i, cur);
- rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
- memcpy(rrp, lrp, be16_to_cpu(right->bb_numrecs) * sizeof(*rrp));
- xfs_inobt_log_recs(cur, rbp, 1, be16_to_cpu(right->bb_numrecs));
- keyp->ir_startino = rrp->ir_startino;
- }
- /*
- * Find the left block number by looking in the buffer.
- * Adjust numrecs, sibling pointers.
- */
- be16_add_cpu(&left->bb_numrecs, -(be16_to_cpu(right->bb_numrecs)));
- right->bb_rightsib = left->bb_rightsib;
- left->bb_rightsib = cpu_to_be32(args.agbno);
- right->bb_leftsib = cpu_to_be32(lbno);
- xfs_inobt_log_block(args.tp, rbp, XFS_BB_ALL_BITS);
- xfs_inobt_log_block(args.tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
- /*
- * If there's a block to the new block's right, make that block
- * point back to right instead of to left.
- */
- if (be32_to_cpu(right->bb_rightsib) != NULLAGBLOCK) {
- xfs_inobt_block_t *rrblock; /* rr btree block */
- xfs_buf_t *rrbp; /* buffer for rrblock */
-
- if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
- be32_to_cpu(right->bb_rightsib), 0, &rrbp,
- XFS_INO_BTREE_REF)))
- return error;
- rrblock = XFS_BUF_TO_INOBT_BLOCK(rrbp);
- if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
- return error;
- rrblock->bb_leftsib = cpu_to_be32(args.agbno);
- xfs_inobt_log_block(args.tp, rrbp, XFS_BB_LEFTSIB);
- }
- /*
- * If the cursor is really in the right block, move it there.
- * If it's just pointing past the last entry in left, then we'll
- * insert there, so don't change anything in that case.
- */
- if (cur->bc_ptrs[level] > be16_to_cpu(left->bb_numrecs) + 1) {
- xfs_btree_setbuf(cur, level, rbp);
- cur->bc_ptrs[level] -= be16_to_cpu(left->bb_numrecs);
- }
- /*
- * If there are more levels, we'll need another cursor which refers
- * the right block, no matter where this cursor was.
- */
- if (level + 1 < cur->bc_nlevels) {
- if ((error = xfs_btree_dup_cursor(cur, curp)))
- return error;
- (*curp)->bc_ptrs[level + 1]++;
- }
- *bnop = args.agbno;
- *stat = 1;
- return 0;
+STATIC int
+xfs_inobt_keys_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return be32_to_cpu(k1->inobt.ir_startino) <
+ be32_to_cpu(k2->inobt.ir_startino);
}
-/*
- * Update keys at all levels from here to the root along the cursor's path.
- */
-STATIC int /* error */
-xfs_inobt_updkey(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_inobt_key_t *keyp, /* new key value to update to */
- int level) /* starting level for update */
+STATIC int
+xfs_inobt_recs_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2)
{
- int ptr; /* index of key in block */
-
- /*
- * Go up the tree from this level toward the root.
- * At each level, update the key value to the value input.
- * Stop when we reach a level where the cursor isn't pointing
- * at the first entry in the block.
- */
- for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
- xfs_buf_t *bp; /* buffer for block */
- xfs_inobt_block_t *block; /* btree block */
-#ifdef DEBUG
- int error; /* error return value */
-#endif
- xfs_inobt_key_t *kp; /* ptr to btree block keys */
-
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
-#endif
- ptr = cur->bc_ptrs[level];
- kp = XFS_INOBT_KEY_ADDR(block, ptr, cur);
- *kp = *keyp;
- xfs_inobt_log_keys(cur, bp, ptr, ptr);
- }
- return 0;
+ return be32_to_cpu(r1->inobt.ir_startino) + XFS_INODES_PER_CHUNK <=
+ be32_to_cpu(r2->inobt.ir_startino);
}
+#endif /* DEBUG */
-/*
- * Externally visible routines.
- */
+#ifdef XFS_BTREE_TRACE
+ktrace_t *xfs_inobt_trace_buf;
-/*
- * Decrement cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-int /* error */
-xfs_inobt_decrement(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level in btree, 0 is leaf */
- int *stat) /* success/failure */
+STATIC void
+xfs_inobt_trace_enter(
+ struct xfs_btree_cur *cur,
+ const char *func,
+ char *s,
+ int type,
+ int line,
+ __psunsigned_t a0,
+ __psunsigned_t a1,
+ __psunsigned_t a2,
+ __psunsigned_t a3,
+ __psunsigned_t a4,
+ __psunsigned_t a5,
+ __psunsigned_t a6,
+ __psunsigned_t a7,
+ __psunsigned_t a8,
+ __psunsigned_t a9,
+ __psunsigned_t a10)
{
- xfs_inobt_block_t *block; /* btree block */
- int error;
- int lev; /* btree level */
-
- ASSERT(level < cur->bc_nlevels);
- /*
- * Read-ahead to the left at this level.
- */
- xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
- /*
- * Decrement the ptr at this level. If we're still in the block
- * then we're done.
- */
- if (--cur->bc_ptrs[level] > 0) {
- *stat = 1;
- return 0;
- }
- /*
- * Get a pointer to the btree block.
- */
- block = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[level]);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level,
- cur->bc_bufs[level])))
- return error;
-#endif
- /*
- * If we just went off the left edge of the tree, return failure.
- */
- if (be32_to_cpu(block->bb_leftsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * March up the tree decrementing pointers.
- * Stop when we don't go off the left edge of a block.
- */
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- if (--cur->bc_ptrs[lev] > 0)
- break;
- /*
- * Read-ahead the left block, we're going to read it
- * in the next loop.
- */
- xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
- }
- /*
- * If we went off the root then we are seriously confused.
- */
- ASSERT(lev < cur->bc_nlevels);
- /*
- * Now walk back down the tree, fixing up the cursor's buffer
- * pointers and key numbers.
- */
- for (block = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[lev]); lev > level; ) {
- xfs_agblock_t agbno; /* block number of btree block */
- xfs_buf_t *bp; /* buffer containing btree block */
-
- agbno = be32_to_cpu(*XFS_INOBT_PTR_ADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, agbno, 0, &bp,
- XFS_INO_BTREE_REF)))
- return error;
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
- cur->bc_ptrs[lev] = be16_to_cpu(block->bb_numrecs);
- }
- *stat = 1;
- return 0;
+ ktrace_enter(xfs_inobt_trace_buf, (void *)(__psint_t)type,
+ (void *)func, (void *)s, NULL, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
}
-/*
- * Delete the record pointed to by cur.
- * The cursor refers to the place where the record was (could be inserted)
- * when the operation returns.
- */
-int /* error */
-xfs_inobt_delete(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
+STATIC void
+xfs_inobt_trace_cursor(
+ struct xfs_btree_cur *cur,
+ __uint32_t *s0,
+ __uint64_t *l0,
+ __uint64_t *l1)
{
- int error;
- int i; /* result code */
- int level; /* btree level */
-
- /*
- * Go up the tree, starting at leaf level.
- * If 2 is returned then a join was done; go to the next level.
- * Otherwise we are done.
- */
- for (level = 0, i = 2; i == 2; level++) {
- if ((error = xfs_inobt_delrec(cur, level, &i)))
- return error;
- }
- if (i == 0) {
- for (level = 1; level < cur->bc_nlevels; level++) {
- if (cur->bc_ptrs[level] == 0) {
- if ((error = xfs_inobt_decrement(cur, level, &i)))
- return error;
- break;
- }
- }
- }
- *stat = i;
- return 0;
+ *s0 = cur->bc_private.a.agno;
+ *l0 = cur->bc_rec.i.ir_startino;
+ *l1 = cur->bc_rec.i.ir_free;
}
-
-/*
- * Get the data from the pointed-to record.
- */
-int /* error */
-xfs_inobt_get_rec(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agino_t *ino, /* output: starting inode of chunk */
- __int32_t *fcnt, /* output: number of free inodes */
- xfs_inofree_t *free, /* output: free inode mask */
- int *stat) /* output: success/failure */
+STATIC void
+xfs_inobt_trace_key(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key,
+ __uint64_t *l0,
+ __uint64_t *l1)
{
- xfs_inobt_block_t *block; /* btree block */
- xfs_buf_t *bp; /* buffer containing btree block */
-#ifdef DEBUG
- int error; /* error return value */
-#endif
- int ptr; /* record number */
- xfs_inobt_rec_t *rec; /* record data */
-
- bp = cur->bc_bufs[0];
- ptr = cur->bc_ptrs[0];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, 0, bp)))
- return error;
-#endif
- /*
- * Off the right end or left end, return failure.
- */
- if (ptr > be16_to_cpu(block->bb_numrecs) || ptr <= 0) {
- *stat = 0;
- return 0;
- }
- /*
- * Point to the record and extract its data.
- */
- rec = XFS_INOBT_REC_ADDR(block, ptr, cur);
- *ino = be32_to_cpu(rec->ir_startino);
- *fcnt = be32_to_cpu(rec->ir_freecount);
- *free = be64_to_cpu(rec->ir_free);
- *stat = 1;
- return 0;
+ *l0 = be32_to_cpu(key->inobt.ir_startino);
+ *l1 = 0;
}
-/*
- * Increment cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-int /* error */
-xfs_inobt_increment(
- xfs_btree_cur_t *cur, /* btree cursor */
- int level, /* level in btree, 0 is leaf */
- int *stat) /* success/failure */
+STATIC void
+xfs_inobt_trace_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ __uint64_t *l0,
+ __uint64_t *l1,
+ __uint64_t *l2)
{
- xfs_inobt_block_t *block; /* btree block */
- xfs_buf_t *bp; /* buffer containing btree block */
- int error; /* error return value */
- int lev; /* btree level */
+ *l0 = be32_to_cpu(rec->inobt.ir_startino);
+ *l1 = be32_to_cpu(rec->inobt.ir_freecount);
+ *l2 = be64_to_cpu(rec->inobt.ir_free);
+}
+#endif /* XFS_BTREE_TRACE */
+
+static const struct xfs_btree_ops xfs_inobt_ops = {
+ .rec_len = sizeof(xfs_inobt_rec_t),
+ .key_len = sizeof(xfs_inobt_key_t),
+
+ .dup_cursor = xfs_inobt_dup_cursor,
+ .set_root = xfs_inobt_set_root,
+ .kill_root = xfs_inobt_kill_root,
+ .alloc_block = xfs_inobt_alloc_block,
+ .free_block = xfs_inobt_free_block,
+ .get_minrecs = xfs_inobt_get_minrecs,
+ .get_maxrecs = xfs_inobt_get_maxrecs,
+ .init_key_from_rec = xfs_inobt_init_key_from_rec,
+ .init_rec_from_key = xfs_inobt_init_rec_from_key,
+ .init_rec_from_cur = xfs_inobt_init_rec_from_cur,
+ .init_ptr_from_cur = xfs_inobt_init_ptr_from_cur,
+ .key_diff = xfs_inobt_key_diff,
- ASSERT(level < cur->bc_nlevels);
- /*
- * Read-ahead to the right at this level.
- */
- xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
- /*
- * Get a pointer to the btree block.
- */
- bp = cur->bc_bufs[level];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
- return error;
-#endif
- /*
- * Increment the ptr at this level. If we're still in the block
- * then we're done.
- */
- if (++cur->bc_ptrs[level] <= be16_to_cpu(block->bb_numrecs)) {
- *stat = 1;
- return 0;
- }
- /*
- * If we just went off the right edge of the tree, return failure.
- */
- if (be32_to_cpu(block->bb_rightsib) == NULLAGBLOCK) {
- *stat = 0;
- return 0;
- }
- /*
- * March up the tree incrementing pointers.
- * Stop when we don't go off the right edge of a block.
- */
- for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
- bp = cur->bc_bufs[lev];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
+ .keys_inorder = xfs_inobt_keys_inorder,
+ .recs_inorder = xfs_inobt_recs_inorder,
#endif
- if (++cur->bc_ptrs[lev] <= be16_to_cpu(block->bb_numrecs))
- break;
- /*
- * Read-ahead the right block, we're going to read it
- * in the next loop.
- */
- xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
- }
- /*
- * If we went off the root then we are seriously confused.
- */
- ASSERT(lev < cur->bc_nlevels);
- /*
- * Now walk back down the tree, fixing up the cursor's buffer
- * pointers and key numbers.
- */
- for (bp = cur->bc_bufs[lev], block = XFS_BUF_TO_INOBT_BLOCK(bp);
- lev > level; ) {
- xfs_agblock_t agbno; /* block number of btree block */
- agbno = be32_to_cpu(*XFS_INOBT_PTR_ADDR(block, cur->bc_ptrs[lev], cur));
- if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
- cur->bc_private.a.agno, agbno, 0, &bp,
- XFS_INO_BTREE_REF)))
- return error;
- lev--;
- xfs_btree_setbuf(cur, lev, bp);
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
- if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
- return error;
- cur->bc_ptrs[lev] = 1;
- }
- *stat = 1;
- return 0;
-}
+#ifdef XFS_BTREE_TRACE
+ .trace_enter = xfs_inobt_trace_enter,
+ .trace_cursor = xfs_inobt_trace_cursor,
+ .trace_key = xfs_inobt_trace_key,
+ .trace_record = xfs_inobt_trace_record,
+#endif
+};
/*
- * Insert the current record at the point referenced by cur.
- * The cursor may be inconsistent on return if splits have been done.
+ * Allocate a new inode btree cursor.
*/
-int /* error */
-xfs_inobt_insert(
- xfs_btree_cur_t *cur, /* btree cursor */
- int *stat) /* success/failure */
+struct xfs_btree_cur * /* new inode btree cursor */
+xfs_inobt_init_cursor(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* buffer for agi structure */
+ xfs_agnumber_t agno) /* allocation group number */
{
- int error; /* error return value */
- int i; /* result value, 0 for failure */
- int level; /* current level number in btree */
- xfs_agblock_t nbno; /* new block number (split result) */
- xfs_btree_cur_t *ncur; /* new cursor (split result) */
- xfs_inobt_rec_t nrec; /* record being inserted this level */
- xfs_btree_cur_t *pcur; /* previous level's cursor */
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
+ struct xfs_btree_cur *cur;
- level = 0;
- nbno = NULLAGBLOCK;
- nrec.ir_startino = cpu_to_be32(cur->bc_rec.i.ir_startino);
- nrec.ir_freecount = cpu_to_be32(cur->bc_rec.i.ir_freecount);
- nrec.ir_free = cpu_to_be64(cur->bc_rec.i.ir_free);
- ncur = NULL;
- pcur = cur;
- /*
- * Loop going up the tree, starting at the leaf level.
- * Stop when we don't get a split block, that must mean that
- * the insert is finished with this level.
- */
- do {
- /*
- * Insert nrec/nbno into this level of the tree.
- * Note if we fail, nbno will be null.
- */
- if ((error = xfs_inobt_insrec(pcur, level++, &nbno, &nrec, &ncur,
- &i))) {
- if (pcur != cur)
- xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
- return error;
- }
- /*
- * See if the cursor we just used is trash.
- * Can't trash the caller's cursor, but otherwise we should
- * if ncur is a new cursor or we're about to be done.
- */
- if (pcur != cur && (ncur || nbno == NULLAGBLOCK)) {
- cur->bc_nlevels = pcur->bc_nlevels;
- xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
- }
- /*
- * If we got a new cursor, switch to it.
- */
- if (ncur) {
- pcur = ncur;
- ncur = NULL;
- }
- } while (nbno != NULLAGBLOCK);
- *stat = i;
- return 0;
-}
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
-/*
- * Lookup the record equal to ino in the btree given by cur.
- */
-int /* error */
-xfs_inobt_lookup_eq(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agino_t ino, /* starting inode of chunk */
- __int32_t fcnt, /* free inode count */
- xfs_inofree_t free, /* free inode mask */
- int *stat) /* success/failure */
-{
- cur->bc_rec.i.ir_startino = ino;
- cur->bc_rec.i.ir_freecount = fcnt;
- cur->bc_rec.i.ir_free = free;
- return xfs_inobt_lookup(cur, XFS_LOOKUP_EQ, stat);
-}
+ cur->bc_tp = tp;
+ cur->bc_mp = mp;
+ cur->bc_nlevels = be32_to_cpu(agi->agi_level);
+ cur->bc_btnum = XFS_BTNUM_INO;
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
-/*
- * Lookup the first record greater than or equal to ino
- * in the btree given by cur.
- */
-int /* error */
-xfs_inobt_lookup_ge(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agino_t ino, /* starting inode of chunk */
- __int32_t fcnt, /* free inode count */
- xfs_inofree_t free, /* free inode mask */
- int *stat) /* success/failure */
-{
- cur->bc_rec.i.ir_startino = ino;
- cur->bc_rec.i.ir_freecount = fcnt;
- cur->bc_rec.i.ir_free = free;
- return xfs_inobt_lookup(cur, XFS_LOOKUP_GE, stat);
-}
+ cur->bc_ops = &xfs_inobt_ops;
-/*
- * Lookup the first record less than or equal to ino
- * in the btree given by cur.
- */
-int /* error */
-xfs_inobt_lookup_le(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agino_t ino, /* starting inode of chunk */
- __int32_t fcnt, /* free inode count */
- xfs_inofree_t free, /* free inode mask */
- int *stat) /* success/failure */
-{
- cur->bc_rec.i.ir_startino = ino;
- cur->bc_rec.i.ir_freecount = fcnt;
- cur->bc_rec.i.ir_free = free;
- return xfs_inobt_lookup(cur, XFS_LOOKUP_LE, stat);
+ cur->bc_private.a.agbp = agbp;
+ cur->bc_private.a.agno = agno;
+
+ return cur;
}
/*
- * Update the record referred to by cur, to the value given
- * by [ino, fcnt, free].
- * This either works (return 0) or gets an EFSCORRUPTED error.
+ * Calculate number of records in an inobt btree block.
*/
-int /* error */
-xfs_inobt_update(
- xfs_btree_cur_t *cur, /* btree cursor */
- xfs_agino_t ino, /* starting inode of chunk */
- __int32_t fcnt, /* free inode count */
- xfs_inofree_t free) /* free inode mask */
+int
+xfs_inobt_maxrecs(
+ struct xfs_mount *mp,
+ int blocklen,
+ int leaf)
{
- xfs_inobt_block_t *block; /* btree block to update */
- xfs_buf_t *bp; /* buffer containing btree block */
- int error; /* error return value */
- int ptr; /* current record number (updating) */
- xfs_inobt_rec_t *rp; /* pointer to updated record */
+ blocklen -= XFS_INOBT_BLOCK_LEN(mp);
- /*
- * Pick up the current block.
- */
- bp = cur->bc_bufs[0];
- block = XFS_BUF_TO_INOBT_BLOCK(bp);
-#ifdef DEBUG
- if ((error = xfs_btree_check_sblock(cur, block, 0, bp)))
- return error;
-#endif
- /*
- * Get the address of the rec to be updated.
- */
- ptr = cur->bc_ptrs[0];
- rp = XFS_INOBT_REC_ADDR(block, ptr, cur);
- /*
- * Fill in the new contents and log them.
- */
- rp->ir_startino = cpu_to_be32(ino);
- rp->ir_freecount = cpu_to_be32(fcnt);
- rp->ir_free = cpu_to_be64(free);
- xfs_inobt_log_recs(cur, bp, ptr, ptr);
- /*
- * Updating first record in leaf. Pass new key value up to our parent.
- */
- if (ptr == 1) {
- xfs_inobt_key_t key; /* key containing [ino] */
-
- key.ir_startino = cpu_to_be32(ino);
- if ((error = xfs_inobt_updkey(cur, &key, 1)))
- return error;
- }
- return 0;
+ if (leaf)
+ return blocklen / sizeof(xfs_inobt_rec_t);
+ return blocklen / (sizeof(xfs_inobt_key_t) + sizeof(xfs_inobt_ptr_t));
}
diff --git a/fs/xfs/xfs_ialloc_btree.h b/fs/xfs/xfs_ialloc_btree.h
index 8efc4a5b8b92..37e5dd01a577 100644
--- a/fs/xfs/xfs_ialloc_btree.h
+++ b/fs/xfs/xfs_ialloc_btree.h
@@ -24,7 +24,6 @@
struct xfs_buf;
struct xfs_btree_cur;
-struct xfs_btree_sblock;
struct xfs_mount;
/*
@@ -70,11 +69,6 @@ typedef struct xfs_inobt_key {
/* btree pointer type */
typedef __be32 xfs_inobt_ptr_t;
-/* btree block header type */
-typedef struct xfs_btree_sblock xfs_inobt_block_t;
-
-#define XFS_BUF_TO_INOBT_BLOCK(bp) ((xfs_inobt_block_t *)XFS_BUF_PTR(bp))
-
/*
* Bit manipulations for ir_free.
*/
@@ -85,14 +79,6 @@ typedef struct xfs_btree_sblock xfs_inobt_block_t;
#define XFS_INOBT_CLR_FREE(rp,i) ((rp)->ir_free &= ~XFS_INOBT_MASK(i))
/*
- * Real block structures have a size equal to the disk block size.
- */
-#define XFS_INOBT_BLOCK_MAXRECS(lev,cur) ((cur)->bc_mp->m_inobt_mxr[lev != 0])
-#define XFS_INOBT_BLOCK_MINRECS(lev,cur) ((cur)->bc_mp->m_inobt_mnr[lev != 0])
-#define XFS_INOBT_IS_LAST_REC(cur) \
- ((cur)->bc_ptrs[0] == be16_to_cpu(XFS_BUF_TO_INOBT_BLOCK((cur)->bc_bufs[0])->bb_numrecs))
-
-/*
* Maximum number of inode btree levels.
*/
#define XFS_IN_MAXLEVELS(mp) ((mp)->m_in_maxlevels)
@@ -104,75 +90,38 @@ typedef struct xfs_btree_sblock xfs_inobt_block_t;
#define XFS_PREALLOC_BLOCKS(mp) ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
/*
- * Record, key, and pointer address macros for btree blocks.
- */
-#define XFS_INOBT_REC_ADDR(bb,i,cur) \
- (XFS_BTREE_REC_ADDR(xfs_inobt, bb, i))
-
-#define XFS_INOBT_KEY_ADDR(bb,i,cur) \
- (XFS_BTREE_KEY_ADDR(xfs_inobt, bb, i))
-
-#define XFS_INOBT_PTR_ADDR(bb,i,cur) \
- (XFS_BTREE_PTR_ADDR(xfs_inobt, bb, \
- i, XFS_INOBT_BLOCK_MAXRECS(1, cur)))
-
-/*
- * Decrement cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-extern int xfs_inobt_decrement(struct xfs_btree_cur *cur, int level, int *stat);
-
-/*
- * Delete the record pointed to by cur.
- * The cursor refers to the place where the record was (could be inserted)
- * when the operation returns.
- */
-extern int xfs_inobt_delete(struct xfs_btree_cur *cur, int *stat);
-
-/*
- * Get the data from the pointed-to record.
- */
-extern int xfs_inobt_get_rec(struct xfs_btree_cur *cur, xfs_agino_t *ino,
- __int32_t *fcnt, xfs_inofree_t *free, int *stat);
-
-/*
- * Increment cursor by one record at the level.
- * For nonzero levels the leaf-ward information is untouched.
- */
-extern int xfs_inobt_increment(struct xfs_btree_cur *cur, int level, int *stat);
-
-/*
- * Insert the current record at the point referenced by cur.
- * The cursor may be inconsistent on return if splits have been done.
- */
-extern int xfs_inobt_insert(struct xfs_btree_cur *cur, int *stat);
-
-/*
- * Lookup the record equal to ino in the btree given by cur.
- */
-extern int xfs_inobt_lookup_eq(struct xfs_btree_cur *cur, xfs_agino_t ino,
- __int32_t fcnt, xfs_inofree_t free, int *stat);
-
-/*
- * Lookup the first record greater than or equal to ino
- * in the btree given by cur.
- */
-extern int xfs_inobt_lookup_ge(struct xfs_btree_cur *cur, xfs_agino_t ino,
- __int32_t fcnt, xfs_inofree_t free, int *stat);
-
-/*
- * Lookup the first record less than or equal to ino
- * in the btree given by cur.
+ * Btree block header size depends on a superblock flag.
+ *
+ * (not quite yet, but soon)
*/
-extern int xfs_inobt_lookup_le(struct xfs_btree_cur *cur, xfs_agino_t ino,
- __int32_t fcnt, xfs_inofree_t free, int *stat);
+#define XFS_INOBT_BLOCK_LEN(mp) XFS_BTREE_SBLOCK_LEN
/*
- * Update the record referred to by cur, to the value given
- * by [ino, fcnt, free].
- * This either works (return 0) or gets an EFSCORRUPTED error.
- */
-extern int xfs_inobt_update(struct xfs_btree_cur *cur, xfs_agino_t ino,
- __int32_t fcnt, xfs_inofree_t free);
+ * Record, key, and pointer address macros for btree blocks.
+ *
+ * (note that some of these may appear unused, but they are used in userspace)
+ */
+#define XFS_INOBT_REC_ADDR(mp, block, index) \
+ ((xfs_inobt_rec_t *) \
+ ((char *)(block) + \
+ XFS_INOBT_BLOCK_LEN(mp) + \
+ (((index) - 1) * sizeof(xfs_inobt_rec_t))))
+
+#define XFS_INOBT_KEY_ADDR(mp, block, index) \
+ ((xfs_inobt_key_t *) \
+ ((char *)(block) + \
+ XFS_INOBT_BLOCK_LEN(mp) + \
+ ((index) - 1) * sizeof(xfs_inobt_key_t)))
+
+#define XFS_INOBT_PTR_ADDR(mp, block, index, maxrecs) \
+ ((xfs_inobt_ptr_t *) \
+ ((char *)(block) + \
+ XFS_INOBT_BLOCK_LEN(mp) + \
+ (maxrecs) * sizeof(xfs_inobt_key_t) + \
+ ((index) - 1) * sizeof(xfs_inobt_ptr_t)))
+
+extern struct xfs_btree_cur *xfs_inobt_init_cursor(struct xfs_mount *,
+ struct xfs_trans *, struct xfs_buf *, xfs_agnumber_t);
+extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);
#endif /* __XFS_IALLOC_BTREE_H__ */
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c
index e229e9e001c2..e2fb6210d4c5 100644
--- a/fs/xfs/xfs_iget.c
+++ b/fs/xfs/xfs_iget.c
@@ -38,281 +38,283 @@
#include "xfs_ialloc.h"
#include "xfs_quota.h"
#include "xfs_utils.h"
+#include "xfs_trans_priv.h"
+#include "xfs_inode_item.h"
+#include "xfs_bmap.h"
+#include "xfs_btree_trace.h"
+#include "xfs_dir2_trace.h"
+
/*
- * Look up an inode by number in the given file system.
- * The inode is looked up in the cache held in each AG.
- * If the inode is found in the cache, attach it to the provided
- * vnode.
- *
- * If it is not in core, read it in from the file system's device,
- * add it to the cache and attach the provided vnode.
- *
- * The inode is locked according to the value of the lock_flags parameter.
- * This flag parameter indicates how and if the inode's IO lock and inode lock
- * should be taken.
- *
- * mp -- the mount point structure for the current file system. It points
- * to the inode hash table.
- * tp -- a pointer to the current transaction if there is one. This is
- * simply passed through to the xfs_iread() call.
- * ino -- the number of the inode desired. This is the unique identifier
- * within the file system for the inode being requested.
- * lock_flags -- flags indicating how to lock the inode. See the comment
- * for xfs_ilock() for a list of valid values.
- * bno -- the block number starting the buffer containing the inode,
- * if known (as by bulkstat), else 0.
+ * Allocate and initialise an xfs_inode.
*/
-STATIC int
-xfs_iget_core(
- struct inode *inode,
- xfs_mount_t *mp,
- xfs_trans_t *tp,
- xfs_ino_t ino,
- uint flags,
- uint lock_flags,
- xfs_inode_t **ipp,
- xfs_daddr_t bno)
+STATIC struct xfs_inode *
+xfs_inode_alloc(
+ struct xfs_mount *mp,
+ xfs_ino_t ino)
{
- struct inode *old_inode;
- xfs_inode_t *ip;
- xfs_inode_t *iq;
- int error;
- unsigned long first_index, mask;
- xfs_perag_t *pag;
- xfs_agino_t agino;
+ struct xfs_inode *ip;
- /* the radix tree exists only in inode capable AGs */
- if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_maxagi)
- return EINVAL;
+ /*
+ * if this didn't occur in transactions, we could use
+ * KM_MAYFAIL and return NULL here on ENOMEM. Set the
+ * code up to do this anyway.
+ */
+ ip = kmem_zone_alloc(xfs_inode_zone, KM_SLEEP);
+ if (!ip)
+ return NULL;
- /* get the perag structure and ensure that it's inode capable */
- pag = xfs_get_perag(mp, ino);
- if (!pag->pagi_inodeok)
- return EINVAL;
- ASSERT(pag->pag_ici_init);
- agino = XFS_INO_TO_AGINO(mp, ino);
+ ASSERT(atomic_read(&ip->i_iocount) == 0);
+ ASSERT(atomic_read(&ip->i_pincount) == 0);
+ ASSERT(!spin_is_locked(&ip->i_flags_lock));
+ ASSERT(completion_done(&ip->i_flush));
-again:
- read_lock(&pag->pag_ici_lock);
- ip = radix_tree_lookup(&pag->pag_ici_root, agino);
+ /*
+ * initialise the VFS inode here to get failures
+ * out of the way early.
+ */
+ if (!inode_init_always(mp->m_super, VFS_I(ip))) {
+ kmem_zone_free(xfs_inode_zone, ip);
+ return NULL;
+ }
+
+ /* initialise the xfs inode */
+ ip->i_ino = ino;
+ ip->i_mount = mp;
+ memset(&ip->i_imap, 0, sizeof(struct xfs_imap));
+ ip->i_afp = NULL;
+ memset(&ip->i_df, 0, sizeof(xfs_ifork_t));
+ ip->i_flags = 0;
+ ip->i_update_core = 0;
+ ip->i_update_size = 0;
+ ip->i_delayed_blks = 0;
+ memset(&ip->i_d, 0, sizeof(xfs_icdinode_t));
+ ip->i_size = 0;
+ ip->i_new_size = 0;
+
+ /*
+ * Initialize inode's trace buffers.
+ */
+#ifdef XFS_INODE_TRACE
+ ip->i_trace = ktrace_alloc(INODE_TRACE_SIZE, KM_NOFS);
+#endif
+#ifdef XFS_BMAP_TRACE
+ ip->i_xtrace = ktrace_alloc(XFS_BMAP_KTRACE_SIZE, KM_NOFS);
+#endif
+#ifdef XFS_BTREE_TRACE
+ ip->i_btrace = ktrace_alloc(XFS_BMBT_KTRACE_SIZE, KM_NOFS);
+#endif
+#ifdef XFS_RW_TRACE
+ ip->i_rwtrace = ktrace_alloc(XFS_RW_KTRACE_SIZE, KM_NOFS);
+#endif
+#ifdef XFS_ILOCK_TRACE
+ ip->i_lock_trace = ktrace_alloc(XFS_ILOCK_KTRACE_SIZE, KM_NOFS);
+#endif
+#ifdef XFS_DIR2_TRACE
+ ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_NOFS);
+#endif
+
+ return ip;
+}
+
+/*
+ * Check the validity of the inode we just found it the cache
+ */
+static int
+xfs_iget_cache_hit(
+ struct xfs_perag *pag,
+ struct xfs_inode *ip,
+ int flags,
+ int lock_flags) __releases(pag->pag_ici_lock)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ int error = EAGAIN;
+
+ /*
+ * If INEW is set this inode is being set up
+ * If IRECLAIM is set this inode is being torn down
+ * Pause and try again.
+ */
+ if (xfs_iflags_test(ip, (XFS_INEW|XFS_IRECLAIM))) {
+ XFS_STATS_INC(xs_ig_frecycle);
+ goto out_error;
+ }
+
+ /* If IRECLAIMABLE is set, we've torn down the vfs inode part */
+ if (xfs_iflags_test(ip, XFS_IRECLAIMABLE)) {
- if (ip != NULL) {
/*
- * If INEW is set this inode is being set up
- * we need to pause and try again.
+ * If lookup is racing with unlink, then we should return an
+ * error immediately so we don't remove it from the reclaim
+ * list and potentially leak the inode.
*/
- if (xfs_iflags_test(ip, XFS_INEW)) {
- read_unlock(&pag->pag_ici_lock);
- delay(1);
- XFS_STATS_INC(xs_ig_frecycle);
-
- goto again;
+ if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) {
+ error = ENOENT;
+ goto out_error;
}
- old_inode = ip->i_vnode;
- if (old_inode == NULL) {
- /*
- * If IRECLAIM is set this inode is
- * on its way out of the system,
- * we need to pause and try again.
- */
- if (xfs_iflags_test(ip, XFS_IRECLAIM)) {
- read_unlock(&pag->pag_ici_lock);
- delay(1);
- XFS_STATS_INC(xs_ig_frecycle);
-
- goto again;
- }
- ASSERT(xfs_iflags_test(ip, XFS_IRECLAIMABLE));
-
- /*
- * If lookup is racing with unlink, then we
- * should return an error immediately so we
- * don't remove it from the reclaim list and
- * potentially leak the inode.
- */
- if ((ip->i_d.di_mode == 0) &&
- !(flags & XFS_IGET_CREATE)) {
- read_unlock(&pag->pag_ici_lock);
- xfs_put_perag(mp, pag);
- return ENOENT;
- }
-
- xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
-
- XFS_STATS_INC(xs_ig_found);
- xfs_iflags_clear(ip, XFS_IRECLAIMABLE);
- read_unlock(&pag->pag_ici_lock);
-
- XFS_MOUNT_ILOCK(mp);
- list_del_init(&ip->i_reclaim);
- XFS_MOUNT_IUNLOCK(mp);
-
- goto finish_inode;
-
- } else if (inode != old_inode) {
- /* The inode is being torn down, pause and
- * try again.
- */
- if (old_inode->i_state & (I_FREEING | I_CLEAR)) {
- read_unlock(&pag->pag_ici_lock);
- delay(1);
- XFS_STATS_INC(xs_ig_frecycle);
-
- goto again;
- }
-/* Chances are the other vnode (the one in the inode) is being torn
-* down right now, and we landed on top of it. Question is, what do
-* we do? Unhook the old inode and hook up the new one?
-*/
- cmn_err(CE_PANIC,
- "xfs_iget_core: ambiguous vns: vp/0x%p, invp/0x%p",
- old_inode, inode);
- }
+ xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
/*
- * Inode cache hit
+ * We need to re-initialise the VFS inode as it has been
+ * 'freed' by the VFS. Do this here so we can deal with
+ * errors cleanly, then tag it so it can be set up correctly
+ * later.
*/
- read_unlock(&pag->pag_ici_lock);
- XFS_STATS_INC(xs_ig_found);
-
-finish_inode:
- if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
- xfs_put_perag(mp, pag);
- return ENOENT;
+ if (!inode_init_always(mp->m_super, VFS_I(ip))) {
+ error = ENOMEM;
+ goto out_error;
}
- if (lock_flags != 0)
- xfs_ilock(ip, lock_flags);
+ /*
+ * We must set the XFS_INEW flag before clearing the
+ * XFS_IRECLAIMABLE flag so that if a racing lookup does
+ * not find the XFS_IRECLAIMABLE above but has the igrab()
+ * below succeed we can safely check XFS_INEW to detect
+ * that this inode is still being initialised.
+ */
+ xfs_iflags_set(ip, XFS_INEW);
+ xfs_iflags_clear(ip, XFS_IRECLAIMABLE);
+
+ /* clear the radix tree reclaim flag as well. */
+ __xfs_inode_clear_reclaim_tag(mp, pag, ip);
+ } else if (!igrab(VFS_I(ip))) {
+ /* If the VFS inode is being torn down, pause and try again. */
+ XFS_STATS_INC(xs_ig_frecycle);
+ goto out_error;
+ } else if (xfs_iflags_test(ip, XFS_INEW)) {
+ /*
+ * We are racing with another cache hit that is
+ * currently recycling this inode out of the XFS_IRECLAIMABLE
+ * state. Wait for the initialisation to complete before
+ * continuing.
+ */
+ wait_on_inode(VFS_I(ip));
+ }
- xfs_iflags_clear(ip, XFS_ISTALE);
- xfs_itrace_exit_tag(ip, "xfs_iget.found");
- goto return_ip;
+ if (ip->i_d.di_mode == 0 && !(flags & XFS_IGET_CREATE)) {
+ error = ENOENT;
+ iput(VFS_I(ip));
+ goto out_error;
}
- /*
- * Inode cache miss
- */
+ /* We've got a live one. */
read_unlock(&pag->pag_ici_lock);
- XFS_STATS_INC(xs_ig_missed);
- /*
- * Read the disk inode attributes into a new inode structure and get
- * a new vnode for it. This should also initialize i_ino and i_mount.
- */
- error = xfs_iread(mp, tp, ino, &ip, bno,
- (flags & XFS_IGET_BULKSTAT) ? XFS_IMAP_BULKSTAT : 0);
- if (error) {
- xfs_put_perag(mp, pag);
- return error;
- }
+ if (lock_flags != 0)
+ xfs_ilock(ip, lock_flags);
- xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
+ xfs_iflags_clear(ip, XFS_ISTALE);
+ xfs_itrace_exit_tag(ip, "xfs_iget.found");
+ XFS_STATS_INC(xs_ig_found);
+ return 0;
+
+out_error:
+ read_unlock(&pag->pag_ici_lock);
+ return error;
+}
- mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER,
- "xfsino", ip->i_ino);
- mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", ip->i_ino);
- init_waitqueue_head(&ip->i_ipin_wait);
- atomic_set(&ip->i_pincount, 0);
+static int
+xfs_iget_cache_miss(
+ struct xfs_mount *mp,
+ struct xfs_perag *pag,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ struct xfs_inode **ipp,
+ xfs_daddr_t bno,
+ int flags,
+ int lock_flags) __releases(pag->pag_ici_lock)
+{
+ struct xfs_inode *ip;
+ int error;
+ unsigned long first_index, mask;
+ xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ino);
- /*
- * Because we want to use a counting completion, complete
- * the flush completion once to allow a single access to
- * the flush completion without blocking.
- */
- init_completion(&ip->i_flush);
- complete(&ip->i_flush);
+ ip = xfs_inode_alloc(mp, ino);
+ if (!ip)
+ return ENOMEM;
- if (lock_flags)
- xfs_ilock(ip, lock_flags);
+ error = xfs_iread(mp, tp, ip, bno, flags);
+ if (error)
+ goto out_destroy;
+
+ xfs_itrace_exit_tag(ip, "xfs_iget.alloc");
if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) {
- xfs_idestroy(ip);
- xfs_put_perag(mp, pag);
- return ENOENT;
+ error = ENOENT;
+ goto out_destroy;
}
+ if (lock_flags)
+ xfs_ilock(ip, lock_flags);
+
/*
* Preload the radix tree so we can insert safely under the
- * write spinlock.
+ * write spinlock. Note that we cannot sleep inside the preload
+ * region.
*/
if (radix_tree_preload(GFP_KERNEL)) {
- xfs_idestroy(ip);
- delay(1);
- goto again;
+ error = EAGAIN;
+ goto out_unlock;
}
+
mask = ~(((XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog)) - 1);
first_index = agino & mask;
write_lock(&pag->pag_ici_lock);
- /*
- * insert the new inode
- */
+
+ /* insert the new inode */
error = radix_tree_insert(&pag->pag_ici_root, agino, ip);
if (unlikely(error)) {
- BUG_ON(error != -EEXIST);
- write_unlock(&pag->pag_ici_lock);
- radix_tree_preload_end();
- xfs_idestroy(ip);
+ WARN_ON(error != -EEXIST);
XFS_STATS_INC(xs_ig_dup);
- goto again;
+ error = EAGAIN;
+ goto out_preload_end;
}
- /*
- * These values _must_ be set before releasing the radix tree lock!
- */
+ /* These values _must_ be set before releasing the radix tree lock! */
ip->i_udquot = ip->i_gdquot = NULL;
xfs_iflags_set(ip, XFS_INEW);
write_unlock(&pag->pag_ici_lock);
radix_tree_preload_end();
-
- /*
- * Link ip to its mount and thread it on the mount's inode list.
- */
- XFS_MOUNT_ILOCK(mp);
- if ((iq = mp->m_inodes)) {
- ASSERT(iq->i_mprev->i_mnext == iq);
- ip->i_mprev = iq->i_mprev;
- iq->i_mprev->i_mnext = ip;
- iq->i_mprev = ip;
- ip->i_mnext = iq;
- } else {
- ip->i_mnext = ip;
- ip->i_mprev = ip;
- }
- mp->m_inodes = ip;
-
- XFS_MOUNT_IUNLOCK(mp);
- xfs_put_perag(mp, pag);
-
- return_ip:
- ASSERT(ip->i_df.if_ext_max ==
- XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t));
-
- xfs_iflags_set(ip, XFS_IMODIFIED);
*ipp = ip;
-
- /*
- * Set up the Linux with the Linux inode.
- */
- ip->i_vnode = inode;
- inode->i_private = ip;
-
- /*
- * If we have a real type for an on-disk inode, we can set ops(&unlock)
- * now. If it's a new inode being created, xfs_ialloc will handle it.
- */
- if (ip->i_d.di_mode != 0)
- xfs_setup_inode(ip);
return 0;
-}
+out_preload_end:
+ write_unlock(&pag->pag_ici_lock);
+ radix_tree_preload_end();
+out_unlock:
+ if (lock_flags)
+ xfs_iunlock(ip, lock_flags);
+out_destroy:
+ xfs_destroy_inode(ip);
+ return error;
+}
/*
- * The 'normal' internal xfs_iget, if needed it will
- * 'allocate', or 'get', the vnode.
+ * Look up an inode by number in the given file system.
+ * The inode is looked up in the cache held in each AG.
+ * If the inode is found in the cache, initialise the vfs inode
+ * if necessary.
+ *
+ * If it is not in core, read it in from the file system's device,
+ * add it to the cache and initialise the vfs inode.
+ *
+ * The inode is locked according to the value of the lock_flags parameter.
+ * This flag parameter indicates how and if the inode's IO lock and inode lock
+ * should be taken.
+ *
+ * mp -- the mount point structure for the current file system. It points
+ * to the inode hash table.
+ * tp -- a pointer to the current transaction if there is one. This is
+ * simply passed through to the xfs_iread() call.
+ * ino -- the number of the inode desired. This is the unique identifier
+ * within the file system for the inode being requested.
+ * lock_flags -- flags indicating how to lock the inode. See the comment
+ * for xfs_ilock() for a list of valid values.
+ * bno -- the block number starting the buffer containing the inode,
+ * if known (as by bulkstat), else 0.
*/
int
xfs_iget(
@@ -324,61 +326,64 @@ xfs_iget(
xfs_inode_t **ipp,
xfs_daddr_t bno)
{
- struct inode *inode;
xfs_inode_t *ip;
int error;
+ xfs_perag_t *pag;
+ xfs_agino_t agino;
- XFS_STATS_INC(xs_ig_attempts);
+ /* the radix tree exists only in inode capable AGs */
+ if (XFS_INO_TO_AGNO(mp, ino) >= mp->m_maxagi)
+ return EINVAL;
-retry:
- inode = iget_locked(mp->m_super, ino);
- if (!inode)
- /* If we got no inode we are out of memory */
- return ENOMEM;
+ /* get the perag structure and ensure that it's inode capable */
+ pag = xfs_get_perag(mp, ino);
+ if (!pag->pagi_inodeok)
+ return EINVAL;
+ ASSERT(pag->pag_ici_init);
+ agino = XFS_INO_TO_AGINO(mp, ino);
- if (inode->i_state & I_NEW) {
- XFS_STATS_INC(vn_active);
- XFS_STATS_INC(vn_alloc);
-
- error = xfs_iget_core(inode, mp, tp, ino, flags,
- lock_flags, ipp, bno);
- if (error) {
- make_bad_inode(inode);
- if (inode->i_state & I_NEW)
- unlock_new_inode(inode);
- iput(inode);
- }
- return error;
+again:
+ error = 0;
+ read_lock(&pag->pag_ici_lock);
+ ip = radix_tree_lookup(&pag->pag_ici_root, agino);
+
+ if (ip) {
+ error = xfs_iget_cache_hit(pag, ip, flags, lock_flags);
+ if (error)
+ goto out_error_or_again;
+ } else {
+ read_unlock(&pag->pag_ici_lock);
+ XFS_STATS_INC(xs_ig_missed);
+
+ error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, bno,
+ flags, lock_flags);
+ if (error)
+ goto out_error_or_again;
}
+ xfs_put_perag(mp, pag);
+ *ipp = ip;
+
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t));
/*
- * If the inode is not fully constructed due to
- * filehandle mismatches wait for the inode to go
- * away and try again.
- *
- * iget_locked will call __wait_on_freeing_inode
- * to wait for the inode to go away.
+ * If we have a real type for an on-disk inode, we can set ops(&unlock)
+ * now. If it's a new inode being created, xfs_ialloc will handle it.
*/
- if (is_bad_inode(inode)) {
- iput(inode);
- delay(1);
- goto retry;
- }
+ if (xfs_iflags_test(ip, XFS_INEW) && ip->i_d.di_mode != 0)
+ xfs_setup_inode(ip);
+ return 0;
- ip = XFS_I(inode);
- if (!ip) {
- iput(inode);
+out_error_or_again:
+ if (error == EAGAIN) {
delay(1);
- goto retry;
+ goto again;
}
-
- if (lock_flags != 0)
- xfs_ilock(ip, lock_flags);
- XFS_STATS_INC(xs_ig_found);
- *ipp = ip;
- return 0;
+ xfs_put_perag(mp, pag);
+ return error;
}
+
/*
* Look for the inode corresponding to the given ino in the hash table.
* If it is there and its i_transp pointer matches tp, return it.
@@ -444,99 +449,109 @@ xfs_iput_new(
IRELE(ip);
}
-
/*
- * This routine embodies the part of the reclaim code that pulls
- * the inode from the inode hash table and the mount structure's
- * inode list.
- * This should only be called from xfs_reclaim().
+ * This is called free all the memory associated with an inode.
+ * It must free the inode itself and any buffers allocated for
+ * if_extents/if_data and if_broot. It must also free the lock
+ * associated with the inode.
+ *
+ * Note: because we don't initialise everything on reallocation out
+ * of the zone, we must ensure we nullify everything correctly before
+ * freeing the structure.
*/
void
-xfs_ireclaim(xfs_inode_t *ip)
+xfs_ireclaim(
+ struct xfs_inode *ip)
{
- /*
- * Remove from old hash list and mount list.
- */
- XFS_STATS_INC(xs_ig_reclaims);
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_perag *pag;
- xfs_iextract(ip);
-
- /*
- * Here we do a spurious inode lock in order to coordinate with
- * xfs_sync(). This is because xfs_sync() references the inodes
- * in the mount list without taking references on the corresponding
- * vnodes. We make that OK here by ensuring that we wait until
- * the inode is unlocked in xfs_sync() before we go ahead and
- * free it. We get both the regular lock and the io lock because
- * the xfs_sync() code may need to drop the regular one but will
- * still hold the io lock.
- */
- xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
-
- /*
- * Release dquots (and their references) if any. An inode may escape
- * xfs_inactive and get here via vn_alloc->vn_reclaim path.
- */
- XFS_QM_DQDETACH(ip->i_mount, ip);
-
- /*
- * Pull our behavior descriptor from the vnode chain.
- */
- if (ip->i_vnode) {
- ip->i_vnode->i_private = NULL;
- ip->i_vnode = NULL;
- }
+ XFS_STATS_INC(xs_ig_reclaims);
/*
- * Free all memory associated with the inode.
+ * Remove the inode from the per-AG radix tree. It doesn't matter
+ * if it was never added to it because radix_tree_delete can deal
+ * with that case just fine.
*/
- xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
- xfs_idestroy(ip);
-}
-
-/*
- * This routine removes an about-to-be-destroyed inode from
- * all of the lists in which it is located with the exception
- * of the behavior chain.
- */
-void
-xfs_iextract(
- xfs_inode_t *ip)
-{
- xfs_mount_t *mp = ip->i_mount;
- xfs_perag_t *pag = xfs_get_perag(mp, ip->i_ino);
- xfs_inode_t *iq;
-
+ pag = xfs_get_perag(mp, ip->i_ino);
write_lock(&pag->pag_ici_lock);
radix_tree_delete(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, ip->i_ino));
write_unlock(&pag->pag_ici_lock);
xfs_put_perag(mp, pag);
/*
- * Remove from mount's inode list.
+ * Here we do an (almost) spurious inode lock in order to coordinate
+ * with inode cache radix tree lookups. This is because the lookup
+ * can reference the inodes in the cache without taking references.
+ *
+ * We make that OK here by ensuring that we wait until the inode is
+ * unlocked after the lookup before we go ahead and free it. We get
+ * both the ilock and the iolock because the code may need to drop the
+ * ilock one but will still hold the iolock.
*/
- XFS_MOUNT_ILOCK(mp);
- ASSERT((ip->i_mnext != NULL) && (ip->i_mprev != NULL));
- iq = ip->i_mnext;
- iq->i_mprev = ip->i_mprev;
- ip->i_mprev->i_mnext = iq;
-
+ xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
/*
- * Fix up the head pointer if it points to the inode being deleted.
+ * Release dquots (and their references) if any.
*/
- if (mp->m_inodes == ip) {
- if (ip == iq) {
- mp->m_inodes = NULL;
- } else {
- mp->m_inodes = iq;
- }
+ XFS_QM_DQDETACH(ip->i_mount, ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+
+ switch (ip->i_d.di_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ xfs_idestroy_fork(ip, XFS_DATA_FORK);
+ break;
}
- /* Deal with the deleted inodes list */
- list_del_init(&ip->i_reclaim);
+ if (ip->i_afp)
+ xfs_idestroy_fork(ip, XFS_ATTR_FORK);
- mp->m_ireclaims++;
- XFS_MOUNT_IUNLOCK(mp);
+#ifdef XFS_INODE_TRACE
+ ktrace_free(ip->i_trace);
+#endif
+#ifdef XFS_BMAP_TRACE
+ ktrace_free(ip->i_xtrace);
+#endif
+#ifdef XFS_BTREE_TRACE
+ ktrace_free(ip->i_btrace);
+#endif
+#ifdef XFS_RW_TRACE
+ ktrace_free(ip->i_rwtrace);
+#endif
+#ifdef XFS_ILOCK_TRACE
+ ktrace_free(ip->i_lock_trace);
+#endif
+#ifdef XFS_DIR2_TRACE
+ ktrace_free(ip->i_dir_trace);
+#endif
+ if (ip->i_itemp) {
+ /*
+ * Only if we are shutting down the fs will we see an
+ * inode still in the AIL. If it is there, we should remove
+ * it to prevent a use-after-free from occurring.
+ */
+ xfs_log_item_t *lip = &ip->i_itemp->ili_item;
+ struct xfs_ail *ailp = lip->li_ailp;
+
+ ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
+ XFS_FORCED_SHUTDOWN(ip->i_mount));
+ if (lip->li_flags & XFS_LI_IN_AIL) {
+ spin_lock(&ailp->xa_lock);
+ if (lip->li_flags & XFS_LI_IN_AIL)
+ xfs_trans_ail_delete(ailp, lip);
+ else
+ spin_unlock(&ailp->xa_lock);
+ }
+ xfs_inode_item_destroy(ip);
+ ip->i_itemp = NULL;
+ }
+ /* asserts to verify all state is correct here */
+ ASSERT(atomic_read(&ip->i_iocount) == 0);
+ ASSERT(atomic_read(&ip->i_pincount) == 0);
+ ASSERT(!spin_is_locked(&ip->i_flags_lock));
+ ASSERT(completion_done(&ip->i_flush));
+ kmem_zone_free(xfs_inode_zone, ip);
}
/*
@@ -737,7 +752,7 @@ xfs_iunlock(
* it is in the AIL and anyone is waiting on it. Don't do
* this if the caller has asked us not to.
*/
- xfs_trans_unlocked_item(ip->i_mount,
+ xfs_trans_unlocked_item(ip->i_itemp->ili_item.li_ailp,
(xfs_log_item_t*)(ip->i_itemp));
}
xfs_ilock_trace(ip, 3, lock_flags, (inst_t *)__return_address);
@@ -790,3 +805,51 @@ xfs_isilocked(
}
#endif
+#ifdef XFS_INODE_TRACE
+
+#define KTRACE_ENTER(ip, vk, s, line, ra) \
+ ktrace_enter((ip)->i_trace, \
+/* 0 */ (void *)(__psint_t)(vk), \
+/* 1 */ (void *)(s), \
+/* 2 */ (void *)(__psint_t) line, \
+/* 3 */ (void *)(__psint_t)atomic_read(&VFS_I(ip)->i_count), \
+/* 4 */ (void *)(ra), \
+/* 5 */ NULL, \
+/* 6 */ (void *)(__psint_t)current_cpu(), \
+/* 7 */ (void *)(__psint_t)current_pid(), \
+/* 8 */ (void *)__return_address, \
+/* 9 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL)
+
+/*
+ * Vnode tracing code.
+ */
+void
+_xfs_itrace_entry(xfs_inode_t *ip, const char *func, inst_t *ra)
+{
+ KTRACE_ENTER(ip, INODE_KTRACE_ENTRY, func, 0, ra);
+}
+
+void
+_xfs_itrace_exit(xfs_inode_t *ip, const char *func, inst_t *ra)
+{
+ KTRACE_ENTER(ip, INODE_KTRACE_EXIT, func, 0, ra);
+}
+
+void
+xfs_itrace_hold(xfs_inode_t *ip, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(ip, INODE_KTRACE_HOLD, file, line, ra);
+}
+
+void
+_xfs_itrace_ref(xfs_inode_t *ip, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(ip, INODE_KTRACE_REF, file, line, ra);
+}
+
+void
+xfs_itrace_rele(xfs_inode_t *ip, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(ip, INODE_KTRACE_RELE, file, line, ra);
+}
+#endif /* XFS_INODE_TRACE */
diff --git a/fs/xfs/xfs_imap.h b/fs/xfs/xfs_imap.h
deleted file mode 100644
index d36450003983..000000000000
--- a/fs/xfs/xfs_imap.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2000,2005 Silicon Graphics, 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.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#ifndef __XFS_IMAP_H__
-#define __XFS_IMAP_H__
-
-/*
- * This is the structure passed to xfs_imap() to map
- * an inode number to its on disk location.
- */
-typedef struct xfs_imap {
- xfs_daddr_t im_blkno; /* starting BB of inode chunk */
- uint im_len; /* length in BBs of inode chunk */
- xfs_agblock_t im_agblkno; /* logical block of inode chunk in ag */
- ushort im_ioffset; /* inode offset in block in "inodes" */
- ushort im_boffset; /* inode offset in block in bytes */
-} xfs_imap_t;
-
-#ifdef __KERNEL__
-struct xfs_mount;
-struct xfs_trans;
-int xfs_imap(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
- xfs_imap_t *, uint);
-#endif
-
-#endif /* __XFS_IMAP_H__ */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index a391b955df01..5a5e035e5d38 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -23,7 +23,6 @@
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
-#include "xfs_imap.h"
#include "xfs_trans.h"
#include "xfs_trans_priv.h"
#include "xfs_sb.h"
@@ -41,6 +40,7 @@
#include "xfs_buf_item.h"
#include "xfs_inode_item.h"
#include "xfs_btree.h"
+#include "xfs_btree_trace.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_bmap.h"
@@ -133,10 +133,10 @@ STATIC int
xfs_imap_to_bp(
xfs_mount_t *mp,
xfs_trans_t *tp,
- xfs_imap_t *imap,
+ struct xfs_imap *imap,
xfs_buf_t **bpp,
uint buf_flags,
- uint imap_flags)
+ uint iget_flags)
{
int error;
int i;
@@ -173,12 +173,12 @@ xfs_imap_to_bp(
dip = (xfs_dinode_t *)xfs_buf_offset(bp,
(i << mp->m_sb.sb_inodelog));
- di_ok = be16_to_cpu(dip->di_core.di_magic) == XFS_DINODE_MAGIC &&
- XFS_DINODE_GOOD_VERSION(dip->di_core.di_version);
+ di_ok = be16_to_cpu(dip->di_magic) == XFS_DINODE_MAGIC &&
+ XFS_DINODE_GOOD_VERSION(dip->di_version);
if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
XFS_ERRTAG_ITOBP_INOTOBP,
XFS_RANDOM_ITOBP_INOTOBP))) {
- if (imap_flags & XFS_IMAP_BULKSTAT) {
+ if (iget_flags & XFS_IGET_BULKSTAT) {
xfs_trans_brelse(tp, bp);
return XFS_ERROR(EINVAL);
}
@@ -190,7 +190,7 @@ xfs_imap_to_bp(
"daddr %lld #%d (magic=%x)",
XFS_BUFTARG_NAME(mp->m_ddev_targp),
(unsigned long long)imap->im_blkno, i,
- be16_to_cpu(dip->di_core.di_magic));
+ be16_to_cpu(dip->di_magic));
#endif
xfs_trans_brelse(tp, bp);
return XFS_ERROR(EFSCORRUPTED);
@@ -221,25 +221,26 @@ xfs_imap_to_bp(
* Use xfs_imap() to determine the size and location of the
* buffer to read from disk.
*/
-STATIC int
+int
xfs_inotobp(
xfs_mount_t *mp,
xfs_trans_t *tp,
xfs_ino_t ino,
xfs_dinode_t **dipp,
xfs_buf_t **bpp,
- int *offset)
+ int *offset,
+ uint imap_flags)
{
- xfs_imap_t imap;
+ struct xfs_imap imap;
xfs_buf_t *bp;
int error;
imap.im_blkno = 0;
- error = xfs_imap(mp, tp, ino, &imap, XFS_IMAP_LOOKUP);
+ error = xfs_imap(mp, tp, ino, &imap, imap_flags);
if (error)
return error;
- error = xfs_imap_to_bp(mp, tp, &imap, &bp, XFS_BUF_LOCK, 0);
+ error = xfs_imap_to_bp(mp, tp, &imap, &bp, XFS_BUF_LOCK, imap_flags);
if (error)
return error;
@@ -260,15 +261,11 @@ xfs_inotobp(
* If a non-zero error is returned, then the contents of bpp and
* dipp are undefined.
*
- * If the inode is new and has not yet been initialized, use xfs_imap()
- * to determine the size and location of the buffer to read from disk.
- * If the inode has already been mapped to its buffer and read in once,
- * then use the mapping information stored in the inode rather than
- * calling xfs_imap(). This allows us to avoid the overhead of looking
- * at the inode btree for small block file systems (see xfs_dilocate()).
- * We can tell whether the inode has been mapped in before by comparing
- * its disk block address to 0. Only uninitialized inodes will have
- * 0 for the disk block address.
+ * The inode is expected to already been mapped to its buffer and read
+ * in once, thus we can use the mapping information stored in the inode
+ * rather than calling xfs_imap(). This allows us to avoid the overhead
+ * of looking at the inode btree for small block file systems
+ * (see xfs_imap()).
*/
int
xfs_itobp(
@@ -277,40 +274,14 @@ xfs_itobp(
xfs_inode_t *ip,
xfs_dinode_t **dipp,
xfs_buf_t **bpp,
- xfs_daddr_t bno,
- uint imap_flags,
uint buf_flags)
{
- xfs_imap_t imap;
xfs_buf_t *bp;
int error;
- if (ip->i_blkno == (xfs_daddr_t)0) {
- imap.im_blkno = bno;
- error = xfs_imap(mp, tp, ip->i_ino, &imap,
- XFS_IMAP_LOOKUP | imap_flags);
- if (error)
- return error;
+ ASSERT(ip->i_imap.im_blkno != 0);
- /*
- * Fill in the fields in the inode that will be used to
- * map the inode to its buffer from now on.
- */
- ip->i_blkno = imap.im_blkno;
- ip->i_len = imap.im_len;
- ip->i_boffset = imap.im_boffset;
- } else {
- /*
- * We've already mapped the inode once, so just use the
- * mapping that we saved the first time.
- */
- imap.im_blkno = ip->i_blkno;
- imap.im_len = ip->i_len;
- imap.im_boffset = ip->i_boffset;
- }
- ASSERT(bno == 0 || bno == imap.im_blkno);
-
- error = xfs_imap_to_bp(mp, tp, &imap, &bp, buf_flags, imap_flags);
+ error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &bp, buf_flags, 0);
if (error)
return error;
@@ -321,7 +292,7 @@ xfs_itobp(
return EAGAIN;
}
- *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
+ *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);
*bpp = bp;
return 0;
}
@@ -348,26 +319,26 @@ xfs_iformat(
XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
error = 0;
- if (unlikely(be32_to_cpu(dip->di_core.di_nextents) +
- be16_to_cpu(dip->di_core.di_anextents) >
- be64_to_cpu(dip->di_core.di_nblocks))) {
+ if (unlikely(be32_to_cpu(dip->di_nextents) +
+ be16_to_cpu(dip->di_anextents) >
+ be64_to_cpu(dip->di_nblocks))) {
xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
"corrupt dinode %Lu, extent total = %d, nblocks = %Lu.",
(unsigned long long)ip->i_ino,
- (int)(be32_to_cpu(dip->di_core.di_nextents) +
- be16_to_cpu(dip->di_core.di_anextents)),
+ (int)(be32_to_cpu(dip->di_nextents) +
+ be16_to_cpu(dip->di_anextents)),
(unsigned long long)
- be64_to_cpu(dip->di_core.di_nblocks));
+ be64_to_cpu(dip->di_nblocks));
XFS_CORRUPTION_ERROR("xfs_iformat(1)", XFS_ERRLEVEL_LOW,
ip->i_mount, dip);
return XFS_ERROR(EFSCORRUPTED);
}
- if (unlikely(dip->di_core.di_forkoff > ip->i_mount->m_sb.sb_inodesize)) {
+ if (unlikely(dip->di_forkoff > ip->i_mount->m_sb.sb_inodesize)) {
xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
"corrupt dinode %Lu, forkoff = 0x%x.",
(unsigned long long)ip->i_ino,
- dip->di_core.di_forkoff);
+ dip->di_forkoff);
XFS_CORRUPTION_ERROR("xfs_iformat(2)", XFS_ERRLEVEL_LOW,
ip->i_mount, dip);
return XFS_ERROR(EFSCORRUPTED);
@@ -378,25 +349,25 @@ xfs_iformat(
case S_IFCHR:
case S_IFBLK:
case S_IFSOCK:
- if (unlikely(dip->di_core.di_format != XFS_DINODE_FMT_DEV)) {
+ if (unlikely(dip->di_format != XFS_DINODE_FMT_DEV)) {
XFS_CORRUPTION_ERROR("xfs_iformat(3)", XFS_ERRLEVEL_LOW,
ip->i_mount, dip);
return XFS_ERROR(EFSCORRUPTED);
}
ip->i_d.di_size = 0;
ip->i_size = 0;
- ip->i_df.if_u2.if_rdev = be32_to_cpu(dip->di_u.di_dev);
+ ip->i_df.if_u2.if_rdev = xfs_dinode_get_rdev(dip);
break;
case S_IFREG:
case S_IFLNK:
case S_IFDIR:
- switch (dip->di_core.di_format) {
+ switch (dip->di_format) {
case XFS_DINODE_FMT_LOCAL:
/*
* no local regular files yet
*/
- if (unlikely((be16_to_cpu(dip->di_core.di_mode) & S_IFMT) == S_IFREG)) {
+ if (unlikely((be16_to_cpu(dip->di_mode) & S_IFMT) == S_IFREG)) {
xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
"corrupt inode %Lu "
"(local format for regular file).",
@@ -407,7 +378,7 @@ xfs_iformat(
return XFS_ERROR(EFSCORRUPTED);
}
- di_size = be64_to_cpu(dip->di_core.di_size);
+ di_size = be64_to_cpu(dip->di_size);
if (unlikely(di_size > XFS_DFORK_DSIZE(dip, ip->i_mount))) {
xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount,
"corrupt inode %Lu "
@@ -449,7 +420,7 @@ xfs_iformat(
ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP);
ip->i_afp->if_ext_max =
XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
- switch (dip->di_core.di_aformat) {
+ switch (dip->di_aformat) {
case XFS_DINODE_FMT_LOCAL:
atp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
size = be16_to_cpu(atp->hdr.totsize);
@@ -621,7 +592,7 @@ xfs_iformat_btree(
ifp = XFS_IFORK_PTR(ip, whichfork);
dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
size = XFS_BMAP_BROOT_SPACE(dfp);
- nrecs = XFS_BMAP_BROOT_NUMRECS(dfp);
+ nrecs = be16_to_cpu(dfp->bb_numrecs);
/*
* blow out if -- fork has less extents than can fit in
@@ -649,8 +620,9 @@ xfs_iformat_btree(
* Copy and convert from the on-disk structure
* to the in-memory structure.
*/
- xfs_bmdr_to_bmbt(dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
- ifp->if_broot, size);
+ xfs_bmdr_to_bmbt(ip->i_mount, dfp,
+ XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
+ ifp->if_broot, size);
ifp->if_flags &= ~XFS_IFEXTENTS;
ifp->if_flags |= XFS_IFBROOT;
@@ -660,7 +632,7 @@ xfs_iformat_btree(
void
xfs_dinode_from_disk(
xfs_icdinode_t *to,
- xfs_dinode_core_t *from)
+ xfs_dinode_t *from)
{
to->di_magic = be16_to_cpu(from->di_magic);
to->di_mode = be16_to_cpu(from->di_mode);
@@ -694,7 +666,7 @@ xfs_dinode_from_disk(
void
xfs_dinode_to_disk(
- xfs_dinode_core_t *to,
+ xfs_dinode_t *to,
xfs_icdinode_t *from)
{
to->di_magic = cpu_to_be16(from->di_magic);
@@ -781,93 +753,57 @@ uint
xfs_dic2xflags(
xfs_dinode_t *dip)
{
- xfs_dinode_core_t *dic = &dip->di_core;
-
- return _xfs_dic2xflags(be16_to_cpu(dic->di_flags)) |
+ return _xfs_dic2xflags(be16_to_cpu(dip->di_flags)) |
(XFS_DFORK_Q(dip) ? XFS_XFLAG_HASATTR : 0);
}
/*
- * Given a mount structure and an inode number, return a pointer
- * to a newly allocated in-core inode corresponding to the given
- * inode number.
- *
- * Initialize the inode's attributes and extent pointers if it
- * already has them (it will not if the inode has no links).
+ * Read the disk inode attributes into the in-core inode structure.
*/
int
xfs_iread(
xfs_mount_t *mp,
xfs_trans_t *tp,
- xfs_ino_t ino,
- xfs_inode_t **ipp,
+ xfs_inode_t *ip,
xfs_daddr_t bno,
- uint imap_flags)
+ uint iget_flags)
{
xfs_buf_t *bp;
xfs_dinode_t *dip;
- xfs_inode_t *ip;
int error;
- ASSERT(xfs_inode_zone != NULL);
-
- ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP);
- ip->i_ino = ino;
- ip->i_mount = mp;
- atomic_set(&ip->i_iocount, 0);
- spin_lock_init(&ip->i_flags_lock);
-
/*
- * Get pointer's to the on-disk inode and the buffer containing it.
- * If the inode number refers to a block outside the file system
- * then xfs_itobp() will return NULL. In this case we should
- * return NULL as well. Set i_blkno to 0 so that xfs_itobp() will
- * know that this is a new incore inode.
+ * Fill in the location information in the in-core inode.
*/
- error = xfs_itobp(mp, tp, ip, &dip, &bp, bno, imap_flags, XFS_BUF_LOCK);
- if (error) {
- kmem_zone_free(xfs_inode_zone, ip);
+ ip->i_imap.im_blkno = bno;
+ error = xfs_imap(mp, tp, ip->i_ino, &ip->i_imap, iget_flags);
+ if (error)
return error;
- }
+ ASSERT(bno == 0 || bno == ip->i_imap.im_blkno);
/*
- * Initialize inode's trace buffers.
- * Do this before xfs_iformat in case it adds entries.
+ * Get pointers to the on-disk inode and the buffer containing it.
*/
-#ifdef XFS_INODE_TRACE
- ip->i_trace = ktrace_alloc(INODE_TRACE_SIZE, KM_NOFS);
-#endif
-#ifdef XFS_BMAP_TRACE
- ip->i_xtrace = ktrace_alloc(XFS_BMAP_KTRACE_SIZE, KM_NOFS);
-#endif
-#ifdef XFS_BMBT_TRACE
- ip->i_btrace = ktrace_alloc(XFS_BMBT_KTRACE_SIZE, KM_NOFS);
-#endif
-#ifdef XFS_RW_TRACE
- ip->i_rwtrace = ktrace_alloc(XFS_RW_KTRACE_SIZE, KM_NOFS);
-#endif
-#ifdef XFS_ILOCK_TRACE
- ip->i_lock_trace = ktrace_alloc(XFS_ILOCK_KTRACE_SIZE, KM_NOFS);
-#endif
-#ifdef XFS_DIR2_TRACE
- ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_NOFS);
-#endif
+ error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &bp,
+ XFS_BUF_LOCK, iget_flags);
+ if (error)
+ return error;
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);
/*
* If we got something that isn't an inode it means someone
* (nfs or dmi) has a stale handle.
*/
- if (be16_to_cpu(dip->di_core.di_magic) != XFS_DINODE_MAGIC) {
- kmem_zone_free(xfs_inode_zone, ip);
- xfs_trans_brelse(tp, bp);
+ if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC) {
#ifdef DEBUG
xfs_fs_cmn_err(CE_ALERT, mp, "xfs_iread: "
- "dip->di_core.di_magic (0x%x) != "
+ "dip->di_magic (0x%x) != "
"XFS_DINODE_MAGIC (0x%x)",
- be16_to_cpu(dip->di_core.di_magic),
+ be16_to_cpu(dip->di_magic),
XFS_DINODE_MAGIC);
#endif /* DEBUG */
- return XFS_ERROR(EINVAL);
+ error = XFS_ERROR(EINVAL);
+ goto out_brelse;
}
/*
@@ -877,24 +813,22 @@ xfs_iread(
* specific information.
* Otherwise, just get the truly permanent information.
*/
- if (dip->di_core.di_mode) {
- xfs_dinode_from_disk(&ip->i_d, &dip->di_core);
+ if (dip->di_mode) {
+ xfs_dinode_from_disk(&ip->i_d, dip);
error = xfs_iformat(ip, dip);
if (error) {
- kmem_zone_free(xfs_inode_zone, ip);
- xfs_trans_brelse(tp, bp);
#ifdef DEBUG
xfs_fs_cmn_err(CE_ALERT, mp, "xfs_iread: "
"xfs_iformat() returned error %d",
error);
#endif /* DEBUG */
- return error;
+ goto out_brelse;
}
} else {
- ip->i_d.di_magic = be16_to_cpu(dip->di_core.di_magic);
- ip->i_d.di_version = dip->di_core.di_version;
- ip->i_d.di_gen = be32_to_cpu(dip->di_core.di_gen);
- ip->i_d.di_flushiter = be16_to_cpu(dip->di_core.di_flushiter);
+ ip->i_d.di_magic = be16_to_cpu(dip->di_magic);
+ ip->i_d.di_version = dip->di_version;
+ ip->i_d.di_gen = be32_to_cpu(dip->di_gen);
+ ip->i_d.di_flushiter = be16_to_cpu(dip->di_flushiter);
/*
* Make sure to pull in the mode here as well in
* case the inode is released without being used.
@@ -911,8 +845,6 @@ xfs_iread(
XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
}
- INIT_LIST_HEAD(&ip->i_reclaim);
-
/*
* The inode format changed when we moved the link count and
* made it 32 bits long. If this is an old format inode,
@@ -924,7 +856,7 @@ xfs_iread(
* the new format. We don't change the version number so that we
* can distinguish this from a real new format inode.
*/
- if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ if (ip->i_d.di_version == 1) {
ip->i_d.di_nlink = ip->i_d.di_onlink;
ip->i_d.di_onlink = 0;
ip->i_d.di_projid = 0;
@@ -938,7 +870,7 @@ xfs_iread(
* around for a while. This helps to keep recently accessed
* meta-data in-core longer.
*/
- XFS_BUF_SET_REF(bp, XFS_INO_REF);
+ XFS_BUF_SET_REF(bp, XFS_INO_REF);
/*
* Use xfs_trans_brelse() to release the buffer containing the
@@ -953,9 +885,9 @@ xfs_iread(
* to worry about the inode being changed just because we released
* the buffer.
*/
+ out_brelse:
xfs_trans_brelse(tp, bp);
- *ipp = ip;
- return 0;
+ return error;
}
/*
@@ -1049,6 +981,7 @@ xfs_ialloc(
uint flags;
int error;
timespec_t tv;
+ int filestreams = 0;
/*
* Call the space management code to pick
@@ -1056,9 +989,8 @@ xfs_ialloc(
*/
error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc,
ialloc_context, call_again, &ino);
- if (error != 0) {
+ if (error)
return error;
- }
if (*call_again || ino == NULLFSINO) {
*ipp = NULL;
return 0;
@@ -1072,9 +1004,8 @@ xfs_ialloc(
*/
error = xfs_trans_iget(tp->t_mountp, tp, ino,
XFS_IGET_CREATE, XFS_ILOCK_EXCL, &ip);
- if (error != 0) {
+ if (error)
return error;
- }
ASSERT(ip != NULL);
ip->i_d.di_mode = (__uint16_t)mode;
@@ -1093,8 +1024,8 @@ xfs_ialloc(
* here rather than here and in the flush/logging code.
*/
if (xfs_sb_version_hasnlink(&tp->t_mountp->m_sb) &&
- ip->i_d.di_version == XFS_DINODE_VERSION_1) {
- ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_version == 1) {
+ ip->i_d.di_version = 2;
/*
* We've already zeroed the old link count, the projid field,
* and the pad field.
@@ -1104,7 +1035,7 @@ xfs_ialloc(
/*
* Project ids won't be stored on disk if we are using a version 1 inode.
*/
- if ((prid != 0) && (ip->i_d.di_version == XFS_DINODE_VERSION_1))
+ if ((prid != 0) && (ip->i_d.di_version == 1))
xfs_bump_ino_vers2(tp, ip);
if (pip && XFS_INHERIT_GID(pip)) {
@@ -1155,13 +1086,12 @@ xfs_ialloc(
flags |= XFS_ILOG_DEV;
break;
case S_IFREG:
- if (pip && xfs_inode_is_filestream(pip)) {
- error = xfs_filestream_associate(pip, ip);
- if (error < 0)
- return -error;
- if (!error)
- xfs_iflags_set(ip, XFS_IFILESTREAM);
- }
+ /*
+ * we can't set up filestreams until after the VFS inode
+ * is set up properly.
+ */
+ if (pip && xfs_inode_is_filestream(pip))
+ filestreams = 1;
/* fall through */
case S_IFDIR:
if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
@@ -1227,6 +1157,15 @@ xfs_ialloc(
/* now that we have an i_mode we can setup inode ops and unlock */
xfs_setup_inode(ip);
+ /* now we have set up the vfs inode we can associate the filestream */
+ if (filestreams) {
+ error = xfs_filestream_associate(pip, ip);
+ if (error < 0)
+ return -error;
+ if (!error)
+ xfs_iflags_set(ip, XFS_IFILESTREAM);
+ }
+
*ipp = ip;
return 0;
}
@@ -1383,8 +1322,8 @@ xfs_itrunc_trace(
* direct I/O with the truncate operation. Also, because we hold
* the IOLOCK in exclusive mode, we prevent new direct I/Os from being
* started until the truncate completes and drops the lock. Essentially,
- * the vn_iowait() call forms an I/O barrier that provides strict ordering
- * between direct I/Os and the truncate operation.
+ * the xfs_ioend_wait() call forms an I/O barrier that provides strict
+ * ordering between direct I/Os and the truncate operation.
*
* The flags parameter can have either the value XFS_ITRUNC_DEFINITE
* or XFS_ITRUNC_MAYBE. The XFS_ITRUNC_MAYBE value should be used
@@ -1415,7 +1354,7 @@ xfs_itruncate_start(
/* wait for the completion of any pending DIOs */
if (new_size == 0 || new_size < ip->i_size)
- vn_iowait(ip);
+ xfs_ioend_wait(ip);
/*
* Call toss_pages or flushinval_pages to get rid of pages
@@ -1726,8 +1665,14 @@ xfs_itruncate_finish(
xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
xfs_trans_ihold(ntp, ip);
- if (!error)
- error = xfs_trans_reserve(ntp, 0,
+ if (error)
+ return error;
+ /*
+ * transaction commit worked ok so we can drop the extra ticket
+ * reference that we gained in xfs_trans_dup()
+ */
+ xfs_log_ticket_put(ntp->t_ticket);
+ error = xfs_trans_reserve(ntp, 0,
XFS_ITRUNCATE_LOG_RES(mp), 0,
XFS_TRANS_PERM_LOG_RES,
XFS_ITRUNCATE_LOG_COUNT);
@@ -1781,13 +1726,10 @@ xfs_iunlink(
xfs_dinode_t *dip;
xfs_buf_t *agibp;
xfs_buf_t *ibp;
- xfs_agnumber_t agno;
- xfs_daddr_t agdaddr;
xfs_agino_t agino;
short bucket_index;
int offset;
int error;
- int agi_ok;
ASSERT(ip->i_d.di_nlink == 0);
ASSERT(ip->i_d.di_mode != 0);
@@ -1795,31 +1737,15 @@ xfs_iunlink(
mp = tp->t_mountp;
- agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
- agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
-
/*
* Get the agi buffer first. It ensures lock ordering
* on the list.
*/
- error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr,
- XFS_FSS_TO_BB(mp, 1), 0, &agibp);
+ error = xfs_read_agi(mp, tp, XFS_INO_TO_AGNO(mp, ip->i_ino), &agibp);
if (error)
return error;
-
- /*
- * Validate the magic number of the agi block.
- */
agi = XFS_BUF_TO_AGI(agibp);
- agi_ok =
- be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC &&
- XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum));
- if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK,
- XFS_RANDOM_IUNLINK))) {
- XFS_CORRUPTION_ERROR("xfs_iunlink", XFS_ERRLEVEL_LOW, mp, agi);
- xfs_trans_brelse(tp, agibp);
- return XFS_ERROR(EFSCORRUPTED);
- }
+
/*
* Get the index into the agi hash table for the
* list this inode will go on.
@@ -1837,14 +1763,14 @@ xfs_iunlink(
* Here we put the head pointer into our next pointer,
* and then we fall through to point the head at us.
*/
- error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0, XFS_BUF_LOCK);
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, XFS_BUF_LOCK);
if (error)
return error;
ASSERT(be32_to_cpu(dip->di_next_unlinked) == NULLAGINO);
/* both on-disk, don't endian flip twice */
dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
- offset = ip->i_boffset +
+ offset = ip->i_imap.im_boffset +
offsetof(xfs_dinode_t, di_next_unlinked);
xfs_trans_inode_buf(tp, ibp);
xfs_trans_log_buf(tp, ibp, offset,
@@ -1879,7 +1805,6 @@ xfs_iunlink_remove(
xfs_buf_t *agibp;
xfs_buf_t *ibp;
xfs_agnumber_t agno;
- xfs_daddr_t agdaddr;
xfs_agino_t agino;
xfs_agino_t next_agino;
xfs_buf_t *last_ibp;
@@ -1887,45 +1812,20 @@ xfs_iunlink_remove(
short bucket_index;
int offset, last_offset = 0;
int error;
- int agi_ok;
- /*
- * First pull the on-disk inode from the AGI unlinked list.
- */
mp = tp->t_mountp;
-
agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
- agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
/*
* Get the agi buffer first. It ensures lock ordering
* on the list.
*/
- error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr,
- XFS_FSS_TO_BB(mp, 1), 0, &agibp);
- if (error) {
- cmn_err(CE_WARN,
- "xfs_iunlink_remove: xfs_trans_read_buf() returned an error %d on %s. Returning error.",
- error, mp->m_fsname);
+ error = xfs_read_agi(mp, tp, agno, &agibp);
+ if (error)
return error;
- }
- /*
- * Validate the magic number of the agi block.
- */
+
agi = XFS_BUF_TO_AGI(agibp);
- agi_ok =
- be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC &&
- XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum));
- if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK_REMOVE,
- XFS_RANDOM_IUNLINK_REMOVE))) {
- XFS_CORRUPTION_ERROR("xfs_iunlink_remove", XFS_ERRLEVEL_LOW,
- mp, agi);
- xfs_trans_brelse(tp, agibp);
- cmn_err(CE_WARN,
- "xfs_iunlink_remove: XFS_TEST_ERROR() returned an error on %s. Returning EFSCORRUPTED.",
- mp->m_fsname);
- return XFS_ERROR(EFSCORRUPTED);
- }
+
/*
* Get the index into the agi hash table for the
* list this inode will go on.
@@ -1945,7 +1845,7 @@ xfs_iunlink_remove(
* of dealing with the buffer when there is no need to
* change it.
*/
- error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0, XFS_BUF_LOCK);
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, XFS_BUF_LOCK);
if (error) {
cmn_err(CE_WARN,
"xfs_iunlink_remove: xfs_itobp() returned an error %d on %s. Returning error.",
@@ -1956,7 +1856,7 @@ xfs_iunlink_remove(
ASSERT(next_agino != 0);
if (next_agino != NULLAGINO) {
dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
- offset = ip->i_boffset +
+ offset = ip->i_imap.im_boffset +
offsetof(xfs_dinode_t, di_next_unlinked);
xfs_trans_inode_buf(tp, ibp);
xfs_trans_log_buf(tp, ibp, offset,
@@ -1992,7 +1892,7 @@ xfs_iunlink_remove(
}
next_ino = XFS_AGINO_TO_INO(mp, agno, next_agino);
error = xfs_inotobp(mp, tp, next_ino, &last_dip,
- &last_ibp, &last_offset);
+ &last_ibp, &last_offset, 0);
if (error) {
cmn_err(CE_WARN,
"xfs_iunlink_remove: xfs_inotobp() returned an error %d on %s. Returning error.",
@@ -2007,7 +1907,7 @@ xfs_iunlink_remove(
* Now last_ibp points to the buffer previous to us on
* the unlinked list. Pull us from the list.
*/
- error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0, 0, XFS_BUF_LOCK);
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, XFS_BUF_LOCK);
if (error) {
cmn_err(CE_WARN,
"xfs_iunlink_remove: xfs_itobp() returned an error %d on %s. Returning error.",
@@ -2019,7 +1919,7 @@ xfs_iunlink_remove(
ASSERT(next_agino != agino);
if (next_agino != NULLAGINO) {
dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
- offset = ip->i_boffset +
+ offset = ip->i_imap.im_boffset +
offsetof(xfs_dinode_t, di_next_unlinked);
xfs_trans_inode_buf(tp, ibp);
xfs_trans_log_buf(tp, ibp, offset,
@@ -2160,9 +2060,9 @@ xfs_ifree_cluster(
iip = (xfs_inode_log_item_t *)lip;
ASSERT(iip->ili_logged == 1);
lip->li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done;
- spin_lock(&mp->m_ail_lock);
- iip->ili_flush_lsn = iip->ili_item.li_lsn;
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_copy_lsn(mp->m_ail,
+ &iip->ili_flush_lsn,
+ &iip->ili_item.li_lsn);
xfs_iflags_set(iip->ili_inode, XFS_ISTALE);
pre_flushed++;
}
@@ -2183,9 +2083,8 @@ xfs_ifree_cluster(
iip->ili_last_fields = iip->ili_format.ilf_fields;
iip->ili_format.ilf_fields = 0;
iip->ili_logged = 1;
- spin_lock(&mp->m_ail_lock);
- iip->ili_flush_lsn = iip->ili_item.li_lsn;
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
+ &iip->ili_item.li_lsn);
xfs_buf_attach_iodone(bp,
(void(*)(xfs_buf_t*,xfs_log_item_t*))
@@ -2263,7 +2162,7 @@ xfs_ifree(
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- error = xfs_itobp(ip->i_mount, tp, ip, &dip, &ibp, 0, 0, XFS_BUF_LOCK);
+ error = xfs_itobp(ip->i_mount, tp, ip, &dip, &ibp, XFS_BUF_LOCK);
if (error)
return error;
@@ -2279,7 +2178,7 @@ xfs_ifree(
* This is a temporary hack that would require a proper fix
* in the future.
*/
- dip->di_core.di_mode = 0;
+ dip->di_mode = 0;
if (delete) {
xfs_ifree_cluster(ip, tp, first_ino);
@@ -2312,9 +2211,10 @@ xfs_iroot_realloc(
int rec_diff,
int whichfork)
{
+ struct xfs_mount *mp = ip->i_mount;
int cur_max;
xfs_ifork_t *ifp;
- xfs_bmbt_block_t *new_broot;
+ struct xfs_btree_block *new_broot;
int new_max;
size_t new_size;
char *np;
@@ -2335,8 +2235,7 @@ xfs_iroot_realloc(
*/
if (ifp->if_broot_bytes == 0) {
new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(rec_diff);
- ifp->if_broot = (xfs_bmbt_block_t*)kmem_alloc(new_size,
- KM_SLEEP);
+ ifp->if_broot = kmem_alloc(new_size, KM_SLEEP);
ifp->if_broot_bytes = (int)new_size;
return;
}
@@ -2347,18 +2246,16 @@ xfs_iroot_realloc(
* location. The records don't change location because
* they are kept butted up against the btree block header.
*/
- cur_max = XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes);
+ cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
new_max = cur_max + rec_diff;
new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max);
- ifp->if_broot = (xfs_bmbt_block_t *)
- kmem_realloc(ifp->if_broot,
- new_size,
+ ifp->if_broot = kmem_realloc(ifp->if_broot, new_size,
(size_t)XFS_BMAP_BROOT_SPACE_CALC(cur_max), /* old size */
KM_SLEEP);
- op = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
- ifp->if_broot_bytes);
- np = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
- (int)new_size);
+ op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
+ ifp->if_broot_bytes);
+ np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
+ (int)new_size);
ifp->if_broot_bytes = (int)new_size;
ASSERT(ifp->if_broot_bytes <=
XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ);
@@ -2372,7 +2269,7 @@ xfs_iroot_realloc(
* records, just get rid of the root and clear the status bit.
*/
ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0));
- cur_max = XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes);
+ cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
new_max = cur_max + rec_diff;
ASSERT(new_max >= 0);
if (new_max > 0)
@@ -2380,11 +2277,11 @@ xfs_iroot_realloc(
else
new_size = 0;
if (new_size > 0) {
- new_broot = (xfs_bmbt_block_t *)kmem_alloc(new_size, KM_SLEEP);
+ new_broot = kmem_alloc(new_size, KM_SLEEP);
/*
* First copy over the btree block header.
*/
- memcpy(new_broot, ifp->if_broot, sizeof(xfs_bmbt_block_t));
+ memcpy(new_broot, ifp->if_broot, XFS_BTREE_LBLOCK_LEN);
} else {
new_broot = NULL;
ifp->if_flags &= ~XFS_IFBROOT;
@@ -2397,18 +2294,16 @@ xfs_iroot_realloc(
/*
* First copy the records.
*/
- op = (char *)XFS_BMAP_BROOT_REC_ADDR(ifp->if_broot, 1,
- ifp->if_broot_bytes);
- np = (char *)XFS_BMAP_BROOT_REC_ADDR(new_broot, 1,
- (int)new_size);
+ op = (char *)XFS_BMBT_REC_ADDR(mp, ifp->if_broot, 1);
+ np = (char *)XFS_BMBT_REC_ADDR(mp, new_broot, 1);
memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_rec_t));
/*
* Then copy the pointers.
*/
- op = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
+ op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
ifp->if_broot_bytes);
- np = (char *)XFS_BMAP_BROOT_PTR_ADDR(new_broot, 1,
+ np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, new_broot, 1,
(int)new_size);
memcpy(np, op, new_max * (uint)sizeof(xfs_dfsbno_t));
}
@@ -2511,64 +2406,6 @@ xfs_idata_realloc(
ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
}
-
-
-
-/*
- * Map inode to disk block and offset.
- *
- * mp -- the mount point structure for the current file system
- * tp -- the current transaction
- * ino -- the inode number of the inode to be located
- * imap -- this structure is filled in with the information necessary
- * to retrieve the given inode from disk
- * flags -- flags to pass to xfs_dilocate indicating whether or not
- * lookups in the inode btree were OK or not
- */
-int
-xfs_imap(
- xfs_mount_t *mp,
- xfs_trans_t *tp,
- xfs_ino_t ino,
- xfs_imap_t *imap,
- uint flags)
-{
- xfs_fsblock_t fsbno;
- int len;
- int off;
- int error;
-
- fsbno = imap->im_blkno ?
- XFS_DADDR_TO_FSB(mp, imap->im_blkno) : NULLFSBLOCK;
- error = xfs_dilocate(mp, tp, ino, &fsbno, &len, &off, flags);
- if (error)
- return error;
-
- imap->im_blkno = XFS_FSB_TO_DADDR(mp, fsbno);
- imap->im_len = XFS_FSB_TO_BB(mp, len);
- imap->im_agblkno = XFS_FSB_TO_AGBNO(mp, fsbno);
- imap->im_ioffset = (ushort)off;
- imap->im_boffset = (ushort)(off << mp->m_sb.sb_inodelog);
-
- /*
- * If the inode number maps to a block outside the bounds
- * of the file system then return NULL rather than calling
- * read_buf and panicing when we get an error from the
- * driver.
- */
- if ((imap->im_blkno + imap->im_len) >
- XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
- xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
- "(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > "
- " XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)",
- (unsigned long long) imap->im_blkno,
- (unsigned long long) imap->im_len,
- XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));
- return EINVAL;
- }
- return 0;
-}
-
void
xfs_idestroy_fork(
xfs_inode_t *ip,
@@ -2613,70 +2450,6 @@ xfs_idestroy_fork(
}
/*
- * This is called free all the memory associated with an inode.
- * It must free the inode itself and any buffers allocated for
- * if_extents/if_data and if_broot. It must also free the lock
- * associated with the inode.
- */
-void
-xfs_idestroy(
- xfs_inode_t *ip)
-{
- switch (ip->i_d.di_mode & S_IFMT) {
- case S_IFREG:
- case S_IFDIR:
- case S_IFLNK:
- xfs_idestroy_fork(ip, XFS_DATA_FORK);
- break;
- }
- if (ip->i_afp)
- xfs_idestroy_fork(ip, XFS_ATTR_FORK);
- mrfree(&ip->i_lock);
- mrfree(&ip->i_iolock);
-
-#ifdef XFS_INODE_TRACE
- ktrace_free(ip->i_trace);
-#endif
-#ifdef XFS_BMAP_TRACE
- ktrace_free(ip->i_xtrace);
-#endif
-#ifdef XFS_BMBT_TRACE
- ktrace_free(ip->i_btrace);
-#endif
-#ifdef XFS_RW_TRACE
- ktrace_free(ip->i_rwtrace);
-#endif
-#ifdef XFS_ILOCK_TRACE
- ktrace_free(ip->i_lock_trace);
-#endif
-#ifdef XFS_DIR2_TRACE
- ktrace_free(ip->i_dir_trace);
-#endif
- if (ip->i_itemp) {
- /*
- * Only if we are shutting down the fs will we see an
- * inode still in the AIL. If it is there, we should remove
- * it to prevent a use-after-free from occurring.
- */
- xfs_mount_t *mp = ip->i_mount;
- xfs_log_item_t *lip = &ip->i_itemp->ili_item;
-
- ASSERT(((lip->li_flags & XFS_LI_IN_AIL) == 0) ||
- XFS_FORCED_SHUTDOWN(ip->i_mount));
- if (lip->li_flags & XFS_LI_IN_AIL) {
- spin_lock(&mp->m_ail_lock);
- if (lip->li_flags & XFS_LI_IN_AIL)
- xfs_trans_delete_ail(mp, lip);
- else
- spin_unlock(&mp->m_ail_lock);
- }
- xfs_inode_item_destroy(ip);
- }
- kmem_zone_free(xfs_inode_zone, ip);
-}
-
-
-/*
* Increment the pin count of the given buffer.
* This value is protected by ipinlock spinlock in the mount structure.
*/
@@ -2880,7 +2653,7 @@ xfs_iflush_fork(
ASSERT(ifp->if_broot_bytes <=
(XFS_IFORK_SIZE(ip, whichfork) +
XFS_BROOT_SIZE_ADJ));
- xfs_bmbt_to_bmdr(ifp->if_broot, ifp->if_broot_bytes,
+ xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes,
(xfs_bmdr_block_t *)cp,
XFS_DFORK_SIZE(dip, mp, whichfork));
}
@@ -2889,15 +2662,16 @@ xfs_iflush_fork(
case XFS_DINODE_FMT_DEV:
if (iip->ili_format.ilf_fields & XFS_ILOG_DEV) {
ASSERT(whichfork == XFS_DATA_FORK);
- dip->di_u.di_dev = cpu_to_be32(ip->i_df.if_u2.if_rdev);
+ xfs_dinode_put_rdev(dip, ip->i_df.if_u2.if_rdev);
}
break;
case XFS_DINODE_FMT_UUID:
if (iip->ili_format.ilf_fields & XFS_ILOG_UUID) {
ASSERT(whichfork == XFS_DATA_FORK);
- memcpy(&dip->di_u.di_muuid, &ip->i_df.if_u2.if_uuid,
- sizeof(uuid_t));
+ memcpy(XFS_DFORK_DPTR(dip),
+ &ip->i_df.if_u2.if_uuid,
+ sizeof(uuid_t));
}
break;
@@ -3030,7 +2804,6 @@ cluster_corrupt_out:
XFS_BUF_CLR_BDSTRAT_FUNC(bp);
XFS_BUF_UNDONE(bp);
XFS_BUF_STALE(bp);
- XFS_BUF_SHUT(bp);
XFS_BUF_ERROR(bp,EIO);
xfs_biodone(bp);
} else {
@@ -3172,7 +2945,7 @@ xfs_iflush(
/*
* Get the buffer containing the on-disk inode.
*/
- error = xfs_itobp(mp, NULL, ip, &dip, &bp, 0, 0,
+ error = xfs_itobp(mp, NULL, ip, &dip, &bp,
noblock ? XFS_BUF_TRYLOCK : XFS_BUF_LOCK);
if (error || !bp) {
xfs_ifunlock(ip);
@@ -3253,7 +3026,7 @@ xfs_iflush_int(
}
/* set *dip = inode's place in the buffer */
- dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_boffset);
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);
/*
* Clear i_update_core before copying out the data.
@@ -3275,11 +3048,11 @@ xfs_iflush_int(
*/
xfs_synchronize_atime(ip);
- if (XFS_TEST_ERROR(be16_to_cpu(dip->di_core.di_magic) != XFS_DINODE_MAGIC,
+ if (XFS_TEST_ERROR(be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC,
mp, XFS_ERRTAG_IFLUSH_1, XFS_RANDOM_IFLUSH_1)) {
xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
"xfs_iflush: Bad inode %Lu magic number 0x%x, ptr 0x%p",
- ip->i_ino, be16_to_cpu(dip->di_core.di_magic), dip);
+ ip->i_ino, be16_to_cpu(dip->di_magic), dip);
goto corrupt_out;
}
if (XFS_TEST_ERROR(ip->i_d.di_magic != XFS_DINODE_MAGIC,
@@ -3342,7 +3115,7 @@ xfs_iflush_int(
* because if the inode is dirty at all the core must
* be.
*/
- xfs_dinode_to_disk(&dip->di_core, &ip->i_d);
+ xfs_dinode_to_disk(dip, &ip->i_d);
/* Wrap, we never let the log put out DI_MAX_FLUSH */
if (ip->i_d.di_flushiter == DI_MAX_FLUSH)
@@ -3354,28 +3127,27 @@ xfs_iflush_int(
* convert back to the old inode format. If the superblock version
* has been updated, then make the conversion permanent.
*/
- ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1 ||
- xfs_sb_version_hasnlink(&mp->m_sb));
- if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ ASSERT(ip->i_d.di_version == 1 || xfs_sb_version_hasnlink(&mp->m_sb));
+ if (ip->i_d.di_version == 1) {
if (!xfs_sb_version_hasnlink(&mp->m_sb)) {
/*
* Convert it back.
*/
ASSERT(ip->i_d.di_nlink <= XFS_MAXLINK_1);
- dip->di_core.di_onlink = cpu_to_be16(ip->i_d.di_nlink);
+ dip->di_onlink = cpu_to_be16(ip->i_d.di_nlink);
} else {
/*
* The superblock version has already been bumped,
* so just make the conversion to the new inode
* format permanent.
*/
- ip->i_d.di_version = XFS_DINODE_VERSION_2;
- dip->di_core.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_version = 2;
+ dip->di_version = 2;
ip->i_d.di_onlink = 0;
- dip->di_core.di_onlink = 0;
+ dip->di_onlink = 0;
memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
- memset(&(dip->di_core.di_pad[0]), 0,
- sizeof(dip->di_core.di_pad));
+ memset(&(dip->di_pad[0]), 0,
+ sizeof(dip->di_pad));
ASSERT(ip->i_d.di_projid == 0);
}
}
@@ -3418,10 +3190,8 @@ xfs_iflush_int(
iip->ili_format.ilf_fields = 0;
iip->ili_logged = 1;
- ASSERT(sizeof(xfs_lsn_t) == 8); /* don't lock if it shrinks */
- spin_lock(&mp->m_ail_lock);
- iip->ili_flush_lsn = iip->ili_item.li_lsn;
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
+ &iip->ili_item.li_lsn);
/*
* Attach the function xfs_iflush_done to the inode's
@@ -3459,45 +3229,8 @@ corrupt_out:
}
-/*
- * Flush all inactive inodes in mp.
- */
-void
-xfs_iflush_all(
- xfs_mount_t *mp)
-{
- xfs_inode_t *ip;
-
- again:
- XFS_MOUNT_ILOCK(mp);
- ip = mp->m_inodes;
- if (ip == NULL)
- goto out;
-
- do {
- /* Make sure we skip markers inserted by sync */
- if (ip->i_mount == NULL) {
- ip = ip->i_mnext;
- continue;
- }
-
- if (!VFS_I(ip)) {
- XFS_MOUNT_IUNLOCK(mp);
- xfs_finish_reclaim(ip, 0, XFS_IFLUSH_ASYNC);
- goto again;
- }
-
- ASSERT(vn_count(VFS_I(ip)) == 0);
-
- ip = ip->i_mnext;
- } while (ip != mp->m_inodes);
- out:
- XFS_MOUNT_IUNLOCK(mp);
-}
#ifdef XFS_ILOCK_TRACE
-ktrace_t *xfs_ilock_trace_buf;
-
void
xfs_ilock_trace(xfs_inode_t *ip, int lock, unsigned int lockflags, inst_t *ra)
{
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 1420c49674d7..1f175fa34b22 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -19,8 +19,7 @@
#define __XFS_INODE_H__
struct xfs_dinode;
-struct xfs_dinode_core;
-
+struct xfs_inode;
/*
* Fork identifiers.
@@ -63,7 +62,7 @@ typedef struct xfs_ext_irec {
typedef struct xfs_ifork {
int if_bytes; /* bytes in if_u1 */
int if_real_bytes; /* bytes allocated in if_u1 */
- xfs_bmbt_block_t *if_broot; /* file's incore btree root */
+ struct xfs_btree_block *if_broot; /* file's incore btree root */
short if_broot_bytes; /* bytes allocated for root */
unsigned char if_flags; /* per-fork flags */
unsigned char if_ext_max; /* max # of extent records */
@@ -84,52 +83,14 @@ typedef struct xfs_ifork {
} xfs_ifork_t;
/*
- * Flags for xfs_ichgtime().
+ * Inode location information. Stored in the inode and passed to
+ * xfs_imap_to_bp() to get a buffer and dinode for a given inode.
*/
-#define XFS_ICHGTIME_MOD 0x1 /* data fork modification timestamp */
-#define XFS_ICHGTIME_CHG 0x2 /* inode field change timestamp */
-
-/*
- * Per-fork incore inode flags.
- */
-#define XFS_IFINLINE 0x01 /* Inline data is read in */
-#define XFS_IFEXTENTS 0x02 /* All extent pointers are read in */
-#define XFS_IFBROOT 0x04 /* i_broot points to the bmap b-tree root */
-#define XFS_IFEXTIREC 0x08 /* Indirection array of extent blocks */
-
-/*
- * Flags for xfs_itobp(), xfs_imap() and xfs_dilocate().
- */
-#define XFS_IMAP_LOOKUP 0x1
-#define XFS_IMAP_BULKSTAT 0x2
-
-#ifdef __KERNEL__
-struct bhv_desc;
-struct cred;
-struct ktrace;
-struct xfs_buf;
-struct xfs_bmap_free;
-struct xfs_bmbt_irec;
-struct xfs_bmbt_block;
-struct xfs_inode;
-struct xfs_inode_log_item;
-struct xfs_mount;
-struct xfs_trans;
-struct xfs_dquot;
-
-#if defined(XFS_ILOCK_TRACE)
-#define XFS_ILOCK_KTRACE_SIZE 32
-extern ktrace_t *xfs_ilock_trace_buf;
-extern void xfs_ilock_trace(struct xfs_inode *, int, unsigned int, inst_t *);
-#else
-#define xfs_ilock_trace(i,n,f,ra)
-#endif
-
-typedef struct dm_attrs_s {
- __uint32_t da_dmevmask; /* DMIG event mask */
- __uint16_t da_dmstate; /* DMIG state info */
- __uint16_t da_pad; /* DMIG extra padding */
-} dm_attrs_t;
+struct xfs_imap {
+ xfs_daddr_t im_blkno; /* starting BB of inode chunk */
+ ushort im_len; /* length in BBs of inode chunk */
+ ushort im_boffset; /* inode offset in block in bytes */
+};
/*
* This is the xfs in-core inode structure.
@@ -160,7 +121,7 @@ typedef struct xfs_ictimestamp {
} xfs_ictimestamp_t;
/*
- * NOTE: This structure must be kept identical to struct xfs_dinode_core
+ * NOTE: This structure must be kept identical to struct xfs_dinode
* in xfs_dinode.h except for the endianess annotations.
*/
typedef struct xfs_icdinode {
@@ -191,27 +152,97 @@ typedef struct xfs_icdinode {
__uint32_t di_gen; /* generation number */
} xfs_icdinode_t;
-typedef struct {
- struct xfs_inode *ip_mnext; /* next inode in mount list */
- struct xfs_inode *ip_mprev; /* ptr to prev inode */
- struct xfs_mount *ip_mount; /* fs mount struct ptr */
-} xfs_iptr_t;
+/*
+ * Flags for xfs_ichgtime().
+ */
+#define XFS_ICHGTIME_MOD 0x1 /* data fork modification timestamp */
+#define XFS_ICHGTIME_CHG 0x2 /* inode field change timestamp */
+
+/*
+ * Per-fork incore inode flags.
+ */
+#define XFS_IFINLINE 0x01 /* Inline data is read in */
+#define XFS_IFEXTENTS 0x02 /* All extent pointers are read in */
+#define XFS_IFBROOT 0x04 /* i_broot points to the bmap b-tree root */
+#define XFS_IFEXTIREC 0x08 /* Indirection array of extent blocks */
+
+/*
+ * Fork handling.
+ */
+
+#define XFS_IFORK_Q(ip) ((ip)->i_d.di_forkoff != 0)
+#define XFS_IFORK_BOFF(ip) ((int)((ip)->i_d.di_forkoff << 3))
+
+#define XFS_IFORK_PTR(ip,w) \
+ ((w) == XFS_DATA_FORK ? \
+ &(ip)->i_df : \
+ (ip)->i_afp)
+#define XFS_IFORK_DSIZE(ip) \
+ (XFS_IFORK_Q(ip) ? \
+ XFS_IFORK_BOFF(ip) : \
+ XFS_LITINO((ip)->i_mount))
+#define XFS_IFORK_ASIZE(ip) \
+ (XFS_IFORK_Q(ip) ? \
+ XFS_LITINO((ip)->i_mount) - XFS_IFORK_BOFF(ip) : \
+ 0)
+#define XFS_IFORK_SIZE(ip,w) \
+ ((w) == XFS_DATA_FORK ? \
+ XFS_IFORK_DSIZE(ip) : \
+ XFS_IFORK_ASIZE(ip))
+#define XFS_IFORK_FORMAT(ip,w) \
+ ((w) == XFS_DATA_FORK ? \
+ (ip)->i_d.di_format : \
+ (ip)->i_d.di_aformat)
+#define XFS_IFORK_FMT_SET(ip,w,n) \
+ ((w) == XFS_DATA_FORK ? \
+ ((ip)->i_d.di_format = (n)) : \
+ ((ip)->i_d.di_aformat = (n)))
+#define XFS_IFORK_NEXTENTS(ip,w) \
+ ((w) == XFS_DATA_FORK ? \
+ (ip)->i_d.di_nextents : \
+ (ip)->i_d.di_anextents)
+#define XFS_IFORK_NEXT_SET(ip,w,n) \
+ ((w) == XFS_DATA_FORK ? \
+ ((ip)->i_d.di_nextents = (n)) : \
+ ((ip)->i_d.di_anextents = (n)))
+
+
+
+#ifdef __KERNEL__
+
+struct bhv_desc;
+struct cred;
+struct ktrace;
+struct xfs_buf;
+struct xfs_bmap_free;
+struct xfs_bmbt_irec;
+struct xfs_inode_log_item;
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_dquot;
+
+#if defined(XFS_ILOCK_TRACE)
+#define XFS_ILOCK_KTRACE_SIZE 32
+extern void xfs_ilock_trace(struct xfs_inode *, int, unsigned int, inst_t *);
+#else
+#define xfs_ilock_trace(i,n,f,ra)
+#endif
+
+typedef struct dm_attrs_s {
+ __uint32_t da_dmevmask; /* DMIG event mask */
+ __uint16_t da_dmstate; /* DMIG state info */
+ __uint16_t da_pad; /* DMIG extra padding */
+} dm_attrs_t;
typedef struct xfs_inode {
/* Inode linking and identification information. */
- struct xfs_inode *i_mnext; /* next inode in mount list */
- struct xfs_inode *i_mprev; /* ptr to prev inode */
struct xfs_mount *i_mount; /* fs mount struct ptr */
- struct list_head i_reclaim; /* reclaim list */
- struct inode *i_vnode; /* vnode backpointer */
struct xfs_dquot *i_udquot; /* user dquot */
struct xfs_dquot *i_gdquot; /* group dquot */
/* Inode location stuff */
xfs_ino_t i_ino; /* inode number (agno/agino)*/
- xfs_daddr_t i_blkno; /* blkno of inode buffer */
- ushort i_len; /* len of inode buffer */
- ushort i_boffset; /* off of inode in buffer */
+ struct xfs_imap i_imap; /* location for xfs_imap() */
/* Extent information. */
xfs_ifork_t *i_afp; /* attribute fork pointer */
@@ -230,7 +261,6 @@ typedef struct xfs_inode {
unsigned short i_flags; /* see defined flags below */
unsigned char i_update_core; /* timestamps/size is dirty */
unsigned char i_update_size; /* di_size field is dirty */
- unsigned int i_gen; /* generation count */
unsigned int i_delayed_blks; /* count of delay alloc blks */
xfs_icdinode_t i_d; /* most of ondisk inode */
@@ -238,6 +268,10 @@ typedef struct xfs_inode {
xfs_fsize_t i_size; /* in-memory size */
xfs_fsize_t i_new_size; /* size when write completes */
atomic_t i_iocount; /* outstanding I/O count */
+
+ /* VFS inode */
+ struct inode i_vnode; /* embedded VFS inode */
+
/* Trace buffers per inode. */
#ifdef XFS_INODE_TRACE
struct ktrace *i_trace; /* general inode trace */
@@ -245,7 +279,7 @@ typedef struct xfs_inode {
#ifdef XFS_BMAP_TRACE
struct ktrace *i_xtrace; /* inode extent list trace */
#endif
-#ifdef XFS_BMBT_TRACE
+#ifdef XFS_BTREE_TRACE
struct ktrace *i_btrace; /* inode bmap btree trace */
#endif
#ifdef XFS_RW_TRACE
@@ -265,13 +299,30 @@ typedef struct xfs_inode {
/* Convert from vfs inode to xfs inode */
static inline struct xfs_inode *XFS_I(struct inode *inode)
{
- return (struct xfs_inode *)inode->i_private;
+ return container_of(inode, struct xfs_inode, i_vnode);
}
/* convert from xfs inode to vfs inode */
static inline struct inode *VFS_I(struct xfs_inode *ip)
{
- return (struct inode *)ip->i_vnode;
+ return &ip->i_vnode;
+}
+
+/*
+ * Get rid of a partially initialized inode.
+ *
+ * We have to go through destroy_inode to make sure allocations
+ * from init_inode_always like the security data are undone.
+ *
+ * We mark the inode bad so that it takes the short cut in
+ * the reclaim path instead of going through the flush path
+ * which doesn't make sense for an inode that has never seen the
+ * light of day.
+ */
+static inline void xfs_destroy_inode(struct xfs_inode *ip)
+{
+ make_bad_inode(VFS_I(ip));
+ return destroy_inode(VFS_I(ip));
}
/*
@@ -327,65 +378,36 @@ xfs_iflags_test_and_clear(xfs_inode_t *ip, unsigned short flags)
spin_unlock(&ip->i_flags_lock);
return ret;
}
-#endif /* __KERNEL__ */
-
/*
- * Fork handling.
+ * Manage the i_flush queue embedded in the inode. This completion
+ * queue synchronizes processes attempting to flush the in-core
+ * inode back to disk.
*/
+static inline void xfs_iflock(xfs_inode_t *ip)
+{
+ wait_for_completion(&ip->i_flush);
+}
-#define XFS_IFORK_Q(ip) ((ip)->i_d.di_forkoff != 0)
-#define XFS_IFORK_BOFF(ip) ((int)((ip)->i_d.di_forkoff << 3))
-
-#define XFS_IFORK_PTR(ip,w) \
- ((w) == XFS_DATA_FORK ? \
- &(ip)->i_df : \
- (ip)->i_afp)
-#define XFS_IFORK_DSIZE(ip) \
- (XFS_IFORK_Q(ip) ? \
- XFS_IFORK_BOFF(ip) : \
- XFS_LITINO((ip)->i_mount))
-#define XFS_IFORK_ASIZE(ip) \
- (XFS_IFORK_Q(ip) ? \
- XFS_LITINO((ip)->i_mount) - XFS_IFORK_BOFF(ip) : \
- 0)
-#define XFS_IFORK_SIZE(ip,w) \
- ((w) == XFS_DATA_FORK ? \
- XFS_IFORK_DSIZE(ip) : \
- XFS_IFORK_ASIZE(ip))
-#define XFS_IFORK_FORMAT(ip,w) \
- ((w) == XFS_DATA_FORK ? \
- (ip)->i_d.di_format : \
- (ip)->i_d.di_aformat)
-#define XFS_IFORK_FMT_SET(ip,w,n) \
- ((w) == XFS_DATA_FORK ? \
- ((ip)->i_d.di_format = (n)) : \
- ((ip)->i_d.di_aformat = (n)))
-#define XFS_IFORK_NEXTENTS(ip,w) \
- ((w) == XFS_DATA_FORK ? \
- (ip)->i_d.di_nextents : \
- (ip)->i_d.di_anextents)
-#define XFS_IFORK_NEXT_SET(ip,w,n) \
- ((w) == XFS_DATA_FORK ? \
- ((ip)->i_d.di_nextents = (n)) : \
- ((ip)->i_d.di_anextents = (n)))
+static inline int xfs_iflock_nowait(xfs_inode_t *ip)
+{
+ return try_wait_for_completion(&ip->i_flush);
+}
-#ifdef __KERNEL__
+static inline void xfs_ifunlock(xfs_inode_t *ip)
+{
+ complete(&ip->i_flush);
+}
/*
* In-core inode flags.
*/
-#define XFS_IGRIO 0x0001 /* inode used for guaranteed rate i/o */
-#define XFS_IUIOSZ 0x0002 /* inode i/o sizes have been explicitly set */
-#define XFS_IQUIESCE 0x0004 /* we have started quiescing for this inode */
-#define XFS_IRECLAIM 0x0008 /* we have started reclaiming this inode */
-#define XFS_ISTALE 0x0010 /* inode has been staled */
-#define XFS_IRECLAIMABLE 0x0020 /* inode can be reclaimed */
-#define XFS_INEW 0x0040
-#define XFS_IFILESTREAM 0x0080 /* inode is in a filestream directory */
-#define XFS_IMODIFIED 0x0100 /* XFS inode state possibly differs */
- /* to the Linux inode state. */
-#define XFS_ITRUNCATED 0x0200 /* truncated down so flush-on-close */
+#define XFS_IRECLAIM 0x0001 /* we have started reclaiming this inode */
+#define XFS_ISTALE 0x0002 /* inode has been staled */
+#define XFS_IRECLAIMABLE 0x0004 /* inode can be reclaimed */
+#define XFS_INEW 0x0008 /* inode has just been allocated */
+#define XFS_IFILESTREAM 0x0010 /* inode is in a filestream directory */
+#define XFS_ITRUNCATED 0x0020 /* truncated down so flush-on-close */
/*
* Flags for inode locking.
@@ -460,16 +482,8 @@ xfs_iflags_test_and_clear(xfs_inode_t *ip, unsigned short flags)
((pip)->i_d.di_mode & S_ISGID))
/*
- * Flags for xfs_iget()
- */
-#define XFS_IGET_CREATE 0x1
-#define XFS_IGET_BULKSTAT 0x2
-
-/*
* xfs_iget.c prototypes.
*/
-void xfs_ihash_init(struct xfs_mount *);
-void xfs_ihash_free(struct xfs_mount *);
xfs_inode_t *xfs_inode_incore(struct xfs_mount *, xfs_ino_t,
struct xfs_trans *);
int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
@@ -484,25 +498,13 @@ int xfs_isilocked(xfs_inode_t *, uint);
uint xfs_ilock_map_shared(xfs_inode_t *);
void xfs_iunlock_map_shared(xfs_inode_t *, uint);
void xfs_ireclaim(xfs_inode_t *);
-int xfs_finish_reclaim(xfs_inode_t *, int, int);
-int xfs_finish_reclaim_all(struct xfs_mount *, int);
/*
* xfs_inode.c prototypes.
*/
-int xfs_itobp(struct xfs_mount *, struct xfs_trans *,
- xfs_inode_t *, struct xfs_dinode **, struct xfs_buf **,
- xfs_daddr_t, uint, uint);
-int xfs_iread(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
- xfs_inode_t **, xfs_daddr_t, uint);
-int xfs_iread_extents(struct xfs_trans *, xfs_inode_t *, int);
int xfs_ialloc(struct xfs_trans *, xfs_inode_t *, mode_t,
- xfs_nlink_t, xfs_dev_t, struct cred *, xfs_prid_t,
+ xfs_nlink_t, xfs_dev_t, cred_t *, xfs_prid_t,
int, struct xfs_buf **, boolean_t *, xfs_inode_t **);
-void xfs_dinode_from_disk(struct xfs_icdinode *,
- struct xfs_dinode_core *);
-void xfs_dinode_to_disk(struct xfs_dinode_core *,
- struct xfs_icdinode *);
uint xfs_ip2xflags(struct xfs_inode *);
uint xfs_dic2xflags(struct xfs_dinode *);
@@ -513,17 +515,10 @@ int xfs_itruncate_finish(struct xfs_trans **, xfs_inode_t *,
xfs_fsize_t, int, int);
int xfs_iunlink(struct xfs_trans *, xfs_inode_t *);
-void xfs_idestroy_fork(xfs_inode_t *, int);
-void xfs_idestroy(xfs_inode_t *);
-void xfs_idata_realloc(xfs_inode_t *, int, int);
-void xfs_iextract(xfs_inode_t *);
void xfs_iext_realloc(xfs_inode_t *, int, int);
-void xfs_iroot_realloc(xfs_inode_t *, int, int);
void xfs_ipin(xfs_inode_t *);
void xfs_iunpin(xfs_inode_t *);
-int xfs_iextents_copy(xfs_inode_t *, xfs_bmbt_rec_t *, int);
int xfs_iflush(xfs_inode_t *, uint);
-void xfs_iflush_all(struct xfs_mount *);
void xfs_ichgtime(xfs_inode_t *, int);
xfs_fsize_t xfs_file_last_byte(xfs_inode_t *);
void xfs_lock_inodes(xfs_inode_t **, int, uint);
@@ -532,6 +527,77 @@ void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);
void xfs_synchronize_atime(xfs_inode_t *);
void xfs_mark_inode_dirty_sync(xfs_inode_t *);
+#if defined(XFS_INODE_TRACE)
+
+#define INODE_TRACE_SIZE 16 /* number of trace entries */
+#define INODE_KTRACE_ENTRY 1
+#define INODE_KTRACE_EXIT 2
+#define INODE_KTRACE_HOLD 3
+#define INODE_KTRACE_REF 4
+#define INODE_KTRACE_RELE 5
+
+extern void _xfs_itrace_entry(struct xfs_inode *, const char *, inst_t *);
+extern void _xfs_itrace_exit(struct xfs_inode *, const char *, inst_t *);
+extern void xfs_itrace_hold(struct xfs_inode *, char *, int, inst_t *);
+extern void _xfs_itrace_ref(struct xfs_inode *, char *, int, inst_t *);
+extern void xfs_itrace_rele(struct xfs_inode *, char *, int, inst_t *);
+#define xfs_itrace_entry(ip) \
+ _xfs_itrace_entry(ip, __func__, (inst_t *)__return_address)
+#define xfs_itrace_exit(ip) \
+ _xfs_itrace_exit(ip, __func__, (inst_t *)__return_address)
+#define xfs_itrace_exit_tag(ip, tag) \
+ _xfs_itrace_exit(ip, tag, (inst_t *)__return_address)
+#define xfs_itrace_ref(ip) \
+ _xfs_itrace_ref(ip, __FILE__, __LINE__, (inst_t *)__return_address)
+
+#else
+#define xfs_itrace_entry(a)
+#define xfs_itrace_exit(a)
+#define xfs_itrace_exit_tag(a, b)
+#define xfs_itrace_hold(a, b, c, d)
+#define xfs_itrace_ref(a)
+#define xfs_itrace_rele(a, b, c, d)
+#endif
+
+#define IHOLD(ip) \
+do { \
+ ASSERT(atomic_read(&VFS_I(ip)->i_count) > 0) ; \
+ atomic_inc(&(VFS_I(ip)->i_count)); \
+ xfs_itrace_hold((ip), __FILE__, __LINE__, (inst_t *)__return_address); \
+} while (0)
+
+#define IRELE(ip) \
+do { \
+ xfs_itrace_rele((ip), __FILE__, __LINE__, (inst_t *)__return_address); \
+ iput(VFS_I(ip)); \
+} while (0)
+
+#endif /* __KERNEL__ */
+
+/*
+ * Flags for xfs_iget()
+ */
+#define XFS_IGET_CREATE 0x1
+#define XFS_IGET_BULKSTAT 0x2
+
+int xfs_inotobp(struct xfs_mount *, struct xfs_trans *,
+ xfs_ino_t, struct xfs_dinode **,
+ struct xfs_buf **, int *, uint);
+int xfs_itobp(struct xfs_mount *, struct xfs_trans *,
+ struct xfs_inode *, struct xfs_dinode **,
+ struct xfs_buf **, uint);
+int xfs_iread(struct xfs_mount *, struct xfs_trans *,
+ struct xfs_inode *, xfs_daddr_t, uint);
+void xfs_dinode_from_disk(struct xfs_icdinode *,
+ struct xfs_dinode *);
+void xfs_dinode_to_disk(struct xfs_dinode *,
+ struct xfs_icdinode *);
+void xfs_idestroy_fork(struct xfs_inode *, int);
+void xfs_idata_realloc(struct xfs_inode *, int, int);
+void xfs_iroot_realloc(struct xfs_inode *, int, int);
+int xfs_iread_extents(struct xfs_trans *, struct xfs_inode *, int);
+int xfs_iextents_copy(struct xfs_inode *, xfs_bmbt_rec_t *, int);
+
xfs_bmbt_rec_host_t *xfs_iext_get_ext(xfs_ifork_t *, xfs_extnum_t);
void xfs_iext_insert(xfs_ifork_t *, xfs_extnum_t, xfs_extnum_t,
xfs_bmbt_irec_t *);
@@ -561,7 +627,8 @@ void xfs_iext_irec_update_extoffs(xfs_ifork_t *, int, int);
#define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount))
#ifdef DEBUG
-void xfs_isize_check(struct xfs_mount *, xfs_inode_t *, xfs_fsize_t);
+void xfs_isize_check(struct xfs_mount *, struct xfs_inode *,
+ xfs_fsize_t);
#else /* DEBUG */
#define xfs_isize_check(mp, ip, isize)
#endif /* DEBUG */
@@ -576,26 +643,4 @@ extern struct kmem_zone *xfs_ifork_zone;
extern struct kmem_zone *xfs_inode_zone;
extern struct kmem_zone *xfs_ili_zone;
-/*
- * Manage the i_flush queue embedded in the inode. This completion
- * queue synchronizes processes attempting to flush the in-core
- * inode back to disk.
- */
-static inline void xfs_iflock(xfs_inode_t *ip)
-{
- wait_for_completion(&ip->i_flush);
-}
-
-static inline int xfs_iflock_nowait(xfs_inode_t *ip)
-{
- return try_wait_for_completion(&ip->i_flush);
-}
-
-static inline void xfs_ifunlock(xfs_inode_t *ip)
-{
- complete(&ip->i_flush);
-}
-
-#endif /* __KERNEL__ */
-
#endif /* __XFS_INODE_H__ */
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 97c7452e2620..977c4aec587e 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -281,7 +281,7 @@ xfs_inode_item_format(
xfs_mark_inode_dirty_sync(ip);
vecp->i_addr = (xfs_caddr_t)&ip->i_d;
- vecp->i_len = sizeof(xfs_dinode_core_t);
+ vecp->i_len = sizeof(struct xfs_icdinode);
XLOG_VEC_SET_TYPE(vecp, XLOG_REG_TYPE_ICORE);
vecp++;
nvecs++;
@@ -296,9 +296,8 @@ xfs_inode_item_format(
* has a new version number, then we don't bother converting back.
*/
mp = ip->i_mount;
- ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1 ||
- xfs_sb_version_hasnlink(&mp->m_sb));
- if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ ASSERT(ip->i_d.di_version == 1 || xfs_sb_version_hasnlink(&mp->m_sb));
+ if (ip->i_d.di_version == 1) {
if (!xfs_sb_version_hasnlink(&mp->m_sb)) {
/*
* Convert it back.
@@ -311,7 +310,7 @@ xfs_inode_item_format(
* so just make the conversion to the new inode
* format permanent.
*/
- ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_version = 2;
ip->i_d.di_onlink = 0;
memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
}
@@ -932,6 +931,7 @@ xfs_inode_item_init(
iip->ili_item.li_type = XFS_LI_INODE;
iip->ili_item.li_ops = &xfs_inode_item_ops;
iip->ili_item.li_mountp = mp;
+ iip->ili_item.li_ailp = mp->m_ail;
iip->ili_inode = ip;
/*
@@ -942,9 +942,9 @@ xfs_inode_item_init(
iip->ili_format.ilf_type = XFS_LI_INODE;
iip->ili_format.ilf_ino = ip->i_ino;
- iip->ili_format.ilf_blkno = ip->i_blkno;
- iip->ili_format.ilf_len = ip->i_len;
- iip->ili_format.ilf_boffset = ip->i_boffset;
+ iip->ili_format.ilf_blkno = ip->i_imap.im_blkno;
+ iip->ili_format.ilf_len = ip->i_imap.im_len;
+ iip->ili_format.ilf_boffset = ip->i_imap.im_boffset;
}
/*
@@ -976,9 +976,8 @@ xfs_iflush_done(
xfs_buf_t *bp,
xfs_inode_log_item_t *iip)
{
- xfs_inode_t *ip;
-
- ip = iip->ili_inode;
+ xfs_inode_t *ip = iip->ili_inode;
+ struct xfs_ail *ailp = iip->ili_item.li_ailp;
/*
* We only want to pull the item from the AIL if it is
@@ -991,15 +990,12 @@ xfs_iflush_done(
*/
if (iip->ili_logged &&
(iip->ili_item.li_lsn == iip->ili_flush_lsn)) {
- spin_lock(&ip->i_mount->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
if (iip->ili_item.li_lsn == iip->ili_flush_lsn) {
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(ip->i_mount,
- (xfs_log_item_t*)iip);
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t*)iip);
} else {
- spin_unlock(&ip->i_mount->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
}
@@ -1031,21 +1027,20 @@ void
xfs_iflush_abort(
xfs_inode_t *ip)
{
- xfs_inode_log_item_t *iip;
+ xfs_inode_log_item_t *iip = ip->i_itemp;
xfs_mount_t *mp;
iip = ip->i_itemp;
mp = ip->i_mount;
if (iip) {
+ struct xfs_ail *ailp = iip->ili_item.li_ailp;
if (iip->ili_item.li_flags & XFS_LI_IN_AIL) {
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
if (iip->ili_item.li_flags & XFS_LI_IN_AIL) {
- /*
- * xfs_trans_delete_ail() drops the AIL lock.
- */
- xfs_trans_delete_ail(mp, (xfs_log_item_t *)iip);
+ /* xfs_trans_ail_delete() drops the AIL lock. */
+ xfs_trans_ail_delete(ailp, (xfs_log_item_t *)iip);
} else
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
iip->ili_logged = 0;
/*
diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h
index 40513077ab36..1ff04cc323ad 100644
--- a/fs/xfs/xfs_inode_item.h
+++ b/fs/xfs/xfs_inode_item.h
@@ -112,6 +112,24 @@ typedef struct xfs_inode_log_format_64 {
#define XFS_ILI_IOLOCKED_ANY (XFS_ILI_IOLOCKED_EXCL | XFS_ILI_IOLOCKED_SHARED)
+#define XFS_ILOG_FBROOT(w) xfs_ilog_fbroot(w)
+static inline int xfs_ilog_fbroot(int w)
+{
+ return (w == XFS_DATA_FORK ? XFS_ILOG_DBROOT : XFS_ILOG_ABROOT);
+}
+
+#define XFS_ILOG_FEXT(w) xfs_ilog_fext(w)
+static inline int xfs_ilog_fext(int w)
+{
+ return (w == XFS_DATA_FORK ? XFS_ILOG_DEXT : XFS_ILOG_AEXT);
+}
+
+#define XFS_ILOG_FDATA(w) xfs_ilog_fdata(w)
+static inline int xfs_ilog_fdata(int w)
+{
+ return (w == XFS_DATA_FORK ? XFS_ILOG_DDATA : XFS_ILOG_ADATA);
+}
+
#ifdef __KERNEL__
struct xfs_buf;
@@ -148,26 +166,6 @@ typedef struct xfs_inode_log_item {
} xfs_inode_log_item_t;
-#define XFS_ILOG_FDATA(w) xfs_ilog_fdata(w)
-static inline int xfs_ilog_fdata(int w)
-{
- return (w == XFS_DATA_FORK ? XFS_ILOG_DDATA : XFS_ILOG_ADATA);
-}
-
-#endif /* __KERNEL__ */
-
-#define XFS_ILOG_FBROOT(w) xfs_ilog_fbroot(w)
-static inline int xfs_ilog_fbroot(int w)
-{
- return (w == XFS_DATA_FORK ? XFS_ILOG_DBROOT : XFS_ILOG_ABROOT);
-}
-
-#define XFS_ILOG_FEXT(w) xfs_ilog_fext(w)
-static inline int xfs_ilog_fext(int w)
-{
- return (w == XFS_DATA_FORK ? XFS_ILOG_DEXT : XFS_ILOG_AEXT);
-}
-
static inline int xfs_inode_clean(xfs_inode_t *ip)
{
return (!ip->i_itemp ||
@@ -175,9 +173,6 @@ static inline int xfs_inode_clean(xfs_inode_t *ip)
!ip->i_update_core;
}
-
-#ifdef __KERNEL__
-
extern void xfs_inode_item_init(struct xfs_inode *, struct xfs_mount *);
extern void xfs_inode_item_destroy(struct xfs_inode *);
extern void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *);
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 67f22b2b44b3..911062cf73a6 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -290,7 +290,6 @@ STATIC int
xfs_iomap_eof_align_last_fsb(
xfs_mount_t *mp,
xfs_inode_t *ip,
- xfs_fsize_t isize,
xfs_extlen_t extsize,
xfs_fileoff_t *last_fsb)
{
@@ -306,14 +305,14 @@ xfs_iomap_eof_align_last_fsb(
* stripe width and we are allocating past the allocation eof.
*/
else if (mp->m_swidth && (mp->m_flags & XFS_MOUNT_SWALLOC) &&
- (isize >= XFS_FSB_TO_B(mp, mp->m_swidth)))
+ (ip->i_size >= XFS_FSB_TO_B(mp, mp->m_swidth)))
new_last_fsb = roundup_64(*last_fsb, mp->m_swidth);
/*
* Roundup the allocation request to a stripe unit (m_dalign) boundary
* if the file size is >= stripe unit size, and we are allocating past
* the allocation eof.
*/
- else if (mp->m_dalign && (isize >= XFS_FSB_TO_B(mp, mp->m_dalign)))
+ else if (mp->m_dalign && (ip->i_size >= XFS_FSB_TO_B(mp, mp->m_dalign)))
new_last_fsb = roundup_64(*last_fsb, mp->m_dalign);
/*
@@ -403,7 +402,6 @@ xfs_iomap_write_direct(
xfs_filblks_t count_fsb, resaligned;
xfs_fsblock_t firstfsb;
xfs_extlen_t extsz, temp;
- xfs_fsize_t isize;
int nimaps;
int bmapi_flag;
int quota_flag;
@@ -426,15 +424,10 @@ xfs_iomap_write_direct(
rt = XFS_IS_REALTIME_INODE(ip);
extsz = xfs_get_extsz_hint(ip);
- isize = ip->i_size;
- if (ip->i_new_size > isize)
- isize = ip->i_new_size;
-
offset_fsb = XFS_B_TO_FSBT(mp, offset);
last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count)));
- if ((offset + count) > isize) {
- error = xfs_iomap_eof_align_last_fsb(mp, ip, isize, extsz,
- &last_fsb);
+ if ((offset + count) > ip->i_size) {
+ error = xfs_iomap_eof_align_last_fsb(mp, ip, extsz, &last_fsb);
if (error)
goto error_out;
} else {
@@ -559,7 +552,6 @@ STATIC int
xfs_iomap_eof_want_preallocate(
xfs_mount_t *mp,
xfs_inode_t *ip,
- xfs_fsize_t isize,
xfs_off_t offset,
size_t count,
int ioflag,
@@ -573,7 +565,7 @@ xfs_iomap_eof_want_preallocate(
int n, error, imaps;
*prealloc = 0;
- if ((ioflag & BMAPI_SYNC) || (offset + count) <= isize)
+ if ((ioflag & BMAPI_SYNC) || (offset + count) <= ip->i_size)
return 0;
/*
@@ -617,7 +609,6 @@ xfs_iomap_write_delay(
xfs_fileoff_t ioalign;
xfs_fsblock_t firstblock;
xfs_extlen_t extsz;
- xfs_fsize_t isize;
int nimaps;
xfs_bmbt_irec_t imap[XFS_WRITE_IMAPS];
int prealloc, fsynced = 0;
@@ -637,11 +628,7 @@ xfs_iomap_write_delay(
offset_fsb = XFS_B_TO_FSBT(mp, offset);
retry:
- isize = ip->i_size;
- if (ip->i_new_size > isize)
- isize = ip->i_new_size;
-
- error = xfs_iomap_eof_want_preallocate(mp, ip, isize, offset, count,
+ error = xfs_iomap_eof_want_preallocate(mp, ip, offset, count,
ioflag, imap, XFS_WRITE_IMAPS, &prealloc);
if (error)
return error;
@@ -655,8 +642,7 @@ retry:
}
if (prealloc || extsz) {
- error = xfs_iomap_eof_align_last_fsb(mp, ip, isize, extsz,
- &last_fsb);
+ error = xfs_iomap_eof_align_last_fsb(mp, ip, extsz, &last_fsb);
if (error)
return error;
}
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index cf6754a3c5b3..e19d0a8d5618 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -69,7 +69,7 @@ xfs_bulkstat_one_iget(
}
ASSERT(ip != NULL);
- ASSERT(ip->i_blkno != (xfs_daddr_t)0);
+ ASSERT(ip->i_imap.im_blkno != 0);
dic = &ip->i_d;
@@ -125,13 +125,9 @@ STATIC void
xfs_bulkstat_one_dinode(
xfs_mount_t *mp, /* mount point for filesystem */
xfs_ino_t ino, /* inode number to get data for */
- xfs_dinode_t *dip, /* dinode inode pointer */
+ xfs_dinode_t *dic, /* dinode inode pointer */
xfs_bstat_t *buf) /* return buffer */
{
- xfs_dinode_core_t *dic; /* dinode core info pointer */
-
- dic = &dip->di_core;
-
/*
* The inode format changed when we moved the link count and
* made it 32 bits long. If this is an old format inode,
@@ -143,7 +139,7 @@ xfs_bulkstat_one_dinode(
* the new format. We don't change the version number so that we
* can distinguish this from a real new format inode.
*/
- if (dic->di_version == XFS_DINODE_VERSION_1) {
+ if (dic->di_version == 1) {
buf->bs_nlink = be16_to_cpu(dic->di_onlink);
buf->bs_projid = 0;
} else {
@@ -162,7 +158,7 @@ xfs_bulkstat_one_dinode(
buf->bs_mtime.tv_nsec = be32_to_cpu(dic->di_mtime.t_nsec);
buf->bs_ctime.tv_sec = be32_to_cpu(dic->di_ctime.t_sec);
buf->bs_ctime.tv_nsec = be32_to_cpu(dic->di_ctime.t_nsec);
- buf->bs_xflags = xfs_dic2xflags(dip);
+ buf->bs_xflags = xfs_dic2xflags(dic);
buf->bs_extsize = be32_to_cpu(dic->di_extsize) << mp->m_sb.sb_blocklog;
buf->bs_extents = be32_to_cpu(dic->di_nextents);
buf->bs_gen = be32_to_cpu(dic->di_gen);
@@ -173,7 +169,7 @@ xfs_bulkstat_one_dinode(
switch (dic->di_format) {
case XFS_DINODE_FMT_DEV:
- buf->bs_rdev = be32_to_cpu(dip->di_u.di_dev);
+ buf->bs_rdev = xfs_dinode_get_rdev(dic);
buf->bs_blksize = BLKDEV_IOSIZE;
buf->bs_blocks = 0;
break;
@@ -192,27 +188,34 @@ xfs_bulkstat_one_dinode(
}
}
+/* Return 0 on success or positive error */
STATIC int
xfs_bulkstat_one_fmt(
void __user *ubuffer,
+ int ubsize,
+ int *ubused,
const xfs_bstat_t *buffer)
{
+ if (ubsize < sizeof(*buffer))
+ return XFS_ERROR(ENOMEM);
if (copy_to_user(ubuffer, buffer, sizeof(*buffer)))
- return -EFAULT;
- return sizeof(*buffer);
+ return XFS_ERROR(EFAULT);
+ if (ubused)
+ *ubused = sizeof(*buffer);
+ return 0;
}
/*
* Return stat information for one inode.
* Return 0 if ok, else errno.
*/
-int /* error status */
-xfs_bulkstat_one(
+int /* error status */
+xfs_bulkstat_one_int(
xfs_mount_t *mp, /* mount point for filesystem */
xfs_ino_t ino, /* inode number to get data for */
void __user *buffer, /* buffer to place output in */
int ubsize, /* size of buffer */
- void *private_data, /* my private data */
+ bulkstat_one_fmt_pf formatter, /* formatter, copy to user */
xfs_daddr_t bno, /* starting bno of inode cluster */
int *ubused, /* bytes used by me */
void *dibuff, /* on-disk inode buffer */
@@ -221,15 +224,12 @@ xfs_bulkstat_one(
xfs_bstat_t *buf; /* return buffer */
int error = 0; /* error value */
xfs_dinode_t *dip; /* dinode inode pointer */
- bulkstat_one_fmt_pf formatter = private_data ? : xfs_bulkstat_one_fmt;
dip = (xfs_dinode_t *)dibuff;
*stat = BULKSTAT_RV_NOTHING;
if (!buffer || xfs_internal_inum(mp, ino))
return XFS_ERROR(EINVAL);
- if (ubsize < sizeof(*buf))
- return XFS_ERROR(ENOMEM);
buf = kmem_alloc(sizeof(*buf), KM_SLEEP);
@@ -244,21 +244,34 @@ xfs_bulkstat_one(
xfs_bulkstat_one_dinode(mp, ino, dip, buf);
}
- error = formatter(buffer, buf);
- if (error < 0) {
- error = EFAULT;
+ error = formatter(buffer, ubsize, ubused, buf);
+ if (error)
goto out_free;
- }
*stat = BULKSTAT_RV_DIDONE;
- if (ubused)
- *ubused = error;
out_free:
kmem_free(buf);
return error;
}
+int
+xfs_bulkstat_one(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t ino, /* inode number to get data for */
+ void __user *buffer, /* buffer to place output in */
+ int ubsize, /* size of buffer */
+ void *private_data, /* my private data */
+ xfs_daddr_t bno, /* starting bno of inode cluster */
+ int *ubused, /* bytes used by me */
+ void *dibuff, /* on-disk inode buffer */
+ int *stat) /* BULKSTAT_RV_... */
+{
+ return xfs_bulkstat_one_int(mp, ino, buffer, ubsize,
+ xfs_bulkstat_one_fmt, bno,
+ ubused, dibuff, stat);
+}
+
/*
* Test to see whether we can use the ondisk inode directly, based
* on the given bulkstat flags, filling in dipp accordingly.
@@ -287,19 +300,19 @@ xfs_bulkstat_use_dinode(
* to disk yet. This is a temporary hack that would require a proper
* fix in the future.
*/
- if (be16_to_cpu(dip->di_core.di_magic) != XFS_DINODE_MAGIC ||
- !XFS_DINODE_GOOD_VERSION(dip->di_core.di_version) ||
- !dip->di_core.di_mode)
+ if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC ||
+ !XFS_DINODE_GOOD_VERSION(dip->di_version) ||
+ !dip->di_mode)
return 0;
if (flags & BULKSTAT_FG_QUICK) {
*dipp = dip;
return 1;
}
/* BULKSTAT_FG_INLINE: if attr fork is local, or not there, use it */
- aformat = dip->di_core.di_aformat;
+ aformat = dip->di_aformat;
if ((XFS_DFORK_Q(dip) == 0) ||
(aformat == XFS_DINODE_FMT_LOCAL) ||
- (aformat == XFS_DINODE_FMT_EXTENTS && !dip->di_core.di_anextents)) {
+ (aformat == XFS_DINODE_FMT_EXTENTS && !dip->di_anextents)) {
*dipp = dip;
return 1;
}
@@ -359,7 +372,6 @@ xfs_bulkstat(
int ubused; /* bytes used by formatter */
xfs_buf_t *bp; /* ptr to on-disk inode cluster buf */
xfs_dinode_t *dip; /* ptr into bp for specific inode */
- xfs_inode_t *ip; /* ptr to in-core inode struct */
/*
* Get the last inode value, see if there's nothing to do.
@@ -416,8 +428,7 @@ xfs_bulkstat(
/*
* Allocate and initialize a btree cursor for ialloc btree.
*/
- cur = xfs_btree_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_INO,
- (xfs_inode_t *)0, 0);
+ cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno);
irbp = irbuf;
irbufend = irbuf + nirbuf;
end_of_ag = 0;
@@ -472,7 +483,7 @@ xfs_bulkstat(
* In any case, increment to the next record.
*/
if (!error)
- error = xfs_inobt_increment(cur, 0, &tmp);
+ error = xfs_btree_increment(cur, 0, &tmp);
} else {
/*
* Start of ag. Lookup the first inode chunk.
@@ -539,7 +550,7 @@ xfs_bulkstat(
* Set agino to after this chunk and bump the cursor.
*/
agino = gino + XFS_INODES_PER_CHUNK;
- error = xfs_inobt_increment(cur, 0, &tmp);
+ error = xfs_btree_increment(cur, 0, &tmp);
cond_resched();
}
/*
@@ -586,6 +597,8 @@ xfs_bulkstat(
if (flags & (BULKSTAT_FG_QUICK |
BULKSTAT_FG_INLINE)) {
+ int offset;
+
ino = XFS_AGINO_TO_INO(mp, agno,
agino);
bno = XFS_AGB_TO_DADDR(mp, agno,
@@ -594,21 +607,15 @@ xfs_bulkstat(
/*
* Get the inode cluster buffer
*/
- ASSERT(xfs_inode_zone != NULL);
- ip = kmem_zone_zalloc(xfs_inode_zone,
- KM_SLEEP);
- ip->i_ino = ino;
- ip->i_mount = mp;
- spin_lock_init(&ip->i_flags_lock);
if (bp)
xfs_buf_relse(bp);
- error = xfs_itobp(mp, NULL, ip,
- &dip, &bp, bno,
- XFS_IMAP_BULKSTAT,
- XFS_BUF_LOCK);
+
+ error = xfs_inotobp(mp, NULL, ino, &dip,
+ &bp, &offset,
+ XFS_IGET_BULKSTAT);
+
if (!error)
- clustidx = ip->i_boffset / mp->m_sb.sb_inodesize;
- kmem_zone_free(xfs_inode_zone, ip);
+ clustidx = offset / mp->m_sb.sb_inodesize;
if (XFS_TEST_ERROR(error != 0,
mp, XFS_ERRTAG_BULKSTAT_READ_CHUNK,
XFS_RANDOM_BULKSTAT_READ_CHUNK)) {
@@ -842,8 +849,7 @@ xfs_inumbers(
agino = 0;
continue;
}
- cur = xfs_btree_init_cursor(mp, NULL, agbp, agno,
- XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno);
error = xfs_inobt_lookup_ge(cur, agino, 0, 0, &tmp);
if (error) {
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
@@ -887,7 +893,7 @@ xfs_inumbers(
bufidx = 0;
}
if (left) {
- error = xfs_inobt_increment(cur, 0, &tmp);
+ error = xfs_btree_increment(cur, 0, &tmp);
if (error) {
xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
cur = NULL;
diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h
index a1f18fce9b70..1fb04e7deb61 100644
--- a/fs/xfs/xfs_itable.h
+++ b/fs/xfs/xfs_itable.h
@@ -71,9 +71,23 @@ xfs_bulkstat_single(
typedef int (*bulkstat_one_fmt_pf)( /* used size in bytes or negative error */
void __user *ubuffer, /* buffer to write to */
+ int ubsize, /* remaining user buffer sz */
+ int *ubused, /* bytes used by formatter */
const xfs_bstat_t *buffer); /* buffer to read from */
int
+xfs_bulkstat_one_int(
+ xfs_mount_t *mp,
+ xfs_ino_t ino,
+ void __user *buffer,
+ int ubsize,
+ bulkstat_one_fmt_pf formatter,
+ xfs_daddr_t bno,
+ int *ubused,
+ void *dibuff,
+ int *stat);
+
+int
xfs_bulkstat_one(
xfs_mount_t *mp,
xfs_ino_t ino,
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 3608a0f0a5f6..f4726f702a9e 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -100,12 +100,11 @@ STATIC void xlog_ungrant_log_space(xlog_t *log,
/* local ticket functions */
-STATIC xlog_ticket_t *xlog_ticket_get(xlog_t *log,
+STATIC xlog_ticket_t *xlog_ticket_alloc(xlog_t *log,
int unit_bytes,
int count,
char clientid,
uint flags);
-STATIC void xlog_ticket_put(xlog_t *log, xlog_ticket_t *ticket);
#if defined(DEBUG)
STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr);
@@ -360,7 +359,7 @@ xfs_log_done(xfs_mount_t *mp,
*/
xlog_trace_loggrant(log, ticket, "xfs_log_done: (non-permanent)");
xlog_ungrant_log_space(log, ticket);
- xlog_ticket_put(log, ticket);
+ xfs_log_ticket_put(ticket);
} else {
xlog_trace_loggrant(log, ticket, "xfs_log_done: (permanent)");
xlog_regrant_reserve_log_space(log, ticket);
@@ -514,7 +513,7 @@ xfs_log_reserve(xfs_mount_t *mp,
retval = xlog_regrant_write_log_space(log, internal_ticket);
} else {
/* may sleep if need to allocate more tickets */
- internal_ticket = xlog_ticket_get(log, unit_bytes, cnt,
+ internal_ticket = xlog_ticket_alloc(log, unit_bytes, cnt,
client, flags);
if (!internal_ticket)
return XFS_ERROR(ENOMEM);
@@ -572,12 +571,12 @@ xfs_log_mount(
/*
* Initialize the AIL now we have a log.
*/
- spin_lock_init(&mp->m_ail_lock);
error = xfs_trans_ail_init(mp);
if (error) {
cmn_err(CE_WARN, "XFS: AIL initialisation failed: error %d", error);
goto error;
}
+ mp->m_log->l_ailp = mp->m_ail;
/*
* skip log recovery on a norecovery mount. pretend it all
@@ -730,8 +729,8 @@ xfs_log_unmount_write(xfs_mount_t *mp)
spin_lock(&log->l_icloglock);
iclog = log->l_iclog;
atomic_inc(&iclog->ic_refcnt);
- spin_unlock(&log->l_icloglock);
xlog_state_want_sync(log, iclog);
+ spin_unlock(&log->l_icloglock);
error = xlog_state_release_iclog(log, iclog);
spin_lock(&log->l_icloglock);
@@ -749,7 +748,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)
if (tic) {
xlog_trace_loggrant(log, tic, "unmount rec");
xlog_ungrant_log_space(log, tic);
- xlog_ticket_put(log, tic);
+ xfs_log_ticket_put(tic);
}
} else {
/*
@@ -768,9 +767,9 @@ xfs_log_unmount_write(xfs_mount_t *mp)
spin_lock(&log->l_icloglock);
iclog = log->l_iclog;
atomic_inc(&iclog->ic_refcnt);
- spin_unlock(&log->l_icloglock);
xlog_state_want_sync(log, iclog);
+ spin_unlock(&log->l_icloglock);
error = xlog_state_release_iclog(log, iclog);
spin_lock(&log->l_icloglock);
@@ -906,7 +905,7 @@ xfs_log_move_tail(xfs_mount_t *mp,
int
xfs_log_need_covered(xfs_mount_t *mp)
{
- int needed = 0, gen;
+ int needed = 0;
xlog_t *log = mp->m_log;
if (!xfs_fs_writable(mp))
@@ -915,7 +914,7 @@ xfs_log_need_covered(xfs_mount_t *mp)
spin_lock(&log->l_icloglock);
if (((log->l_covered_state == XLOG_STATE_COVER_NEED) ||
(log->l_covered_state == XLOG_STATE_COVER_NEED2))
- && !xfs_trans_first_ail(mp, &gen)
+ && !xfs_trans_ail_tail(log->l_ailp)
&& xlog_iclogs_empty(log)) {
if (log->l_covered_state == XLOG_STATE_COVER_NEED)
log->l_covered_state = XLOG_STATE_COVER_DONE;
@@ -952,7 +951,7 @@ xlog_assign_tail_lsn(xfs_mount_t *mp)
xfs_lsn_t tail_lsn;
xlog_t *log = mp->m_log;
- tail_lsn = xfs_trans_tail_ail(mp);
+ tail_lsn = xfs_trans_ail_tail(mp->m_ail);
spin_lock(&log->l_grant_lock);
if (tail_lsn != 0) {
log->l_tail_lsn = tail_lsn;
@@ -1030,12 +1029,6 @@ xlog_iodone(xfs_buf_t *bp)
ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) == (unsigned long) 2);
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
aborted = 0;
-
- /*
- * Some versions of cpp barf on the recursive definition of
- * ic_log -> hic_fields.ic_log and expand ic_log twice when
- * it is passed through two macros. Workaround broken cpp.
- */
l = iclog->ic_log;
/*
@@ -1302,7 +1295,7 @@ xlog_alloc_log(xfs_mount_t *mp,
XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb);
XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
iclog->ic_bp = bp;
- iclog->hic_data = bp->b_addr;
+ iclog->ic_data = bp->b_addr;
#ifdef DEBUG
log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header);
#endif
@@ -1322,7 +1315,7 @@ xlog_alloc_log(xfs_mount_t *mp,
atomic_set(&iclog->ic_refcnt, 0);
spin_lock_init(&iclog->ic_callback_lock);
iclog->ic_callback_tail = &(iclog->ic_callback);
- iclog->ic_datap = (char *)iclog->hic_data + log->l_iclog_hsize;
+ iclog->ic_datap = (char *)iclog->ic_data + log->l_iclog_hsize;
ASSERT(XFS_BUF_ISBUSY(iclog->ic_bp));
ASSERT(XFS_BUF_VALUSEMA(iclog->ic_bp) <= 0);
@@ -1446,7 +1439,7 @@ xlog_grant_push_ail(xfs_mount_t *mp,
*/
if (threshold_lsn &&
!XLOG_FORCED_SHUTDOWN(log))
- xfs_trans_push_ail(mp, threshold_lsn);
+ xfs_trans_ail_push(log->l_ailp, threshold_lsn);
} /* xlog_grant_push_ail */
@@ -1991,7 +1984,9 @@ xlog_write(xfs_mount_t * mp,
if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) {
xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
record_cnt = data_cnt = 0;
+ spin_lock(&log->l_icloglock);
xlog_state_want_sync(log, iclog);
+ spin_unlock(&log->l_icloglock);
if (commit_iclog) {
ASSERT(flags & XLOG_COMMIT_TRANS);
*commit_iclog = iclog;
@@ -3200,7 +3195,7 @@ try_again:
STATIC void
xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
{
- spin_lock(&log->l_icloglock);
+ ASSERT(spin_is_locked(&log->l_icloglock));
if (iclog->ic_state == XLOG_STATE_ACTIVE) {
xlog_state_switch_iclogs(log, iclog, 0);
@@ -3208,10 +3203,7 @@ xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
ASSERT(iclog->ic_state &
(XLOG_STATE_WANT_SYNC|XLOG_STATE_IOERROR));
}
-
- spin_unlock(&log->l_icloglock);
-} /* xlog_state_want_sync */
-
+}
/*****************************************************************************
@@ -3222,22 +3214,33 @@ xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
*/
/*
- * Free a used ticket.
+ * Free a used ticket when it's refcount falls to zero.
*/
-STATIC void
-xlog_ticket_put(xlog_t *log,
- xlog_ticket_t *ticket)
+void
+xfs_log_ticket_put(
+ xlog_ticket_t *ticket)
{
- sv_destroy(&ticket->t_wait);
- kmem_zone_free(xfs_log_ticket_zone, ticket);
-} /* xlog_ticket_put */
+ ASSERT(atomic_read(&ticket->t_ref) > 0);
+ if (atomic_dec_and_test(&ticket->t_ref)) {
+ sv_destroy(&ticket->t_wait);
+ kmem_zone_free(xfs_log_ticket_zone, ticket);
+ }
+}
+xlog_ticket_t *
+xfs_log_ticket_get(
+ xlog_ticket_t *ticket)
+{
+ ASSERT(atomic_read(&ticket->t_ref) > 0);
+ atomic_inc(&ticket->t_ref);
+ return ticket;
+}
/*
* Allocate and initialise a new log ticket.
*/
STATIC xlog_ticket_t *
-xlog_ticket_get(xlog_t *log,
+xlog_ticket_alloc(xlog_t *log,
int unit_bytes,
int cnt,
char client,
@@ -3308,6 +3311,7 @@ xlog_ticket_get(xlog_t *log,
unit_bytes += 2*BBSIZE;
}
+ atomic_set(&tic->t_ref, 1);
tic->t_unit_res = unit_bytes;
tic->t_curr_res = unit_bytes;
tic->t_cnt = cnt;
@@ -3323,7 +3327,7 @@ xlog_ticket_get(xlog_t *log,
xlog_tic_reset_res(tic);
return tic;
-} /* xlog_ticket_get */
+}
/******************************************************************************
@@ -3452,7 +3456,7 @@ xlog_verify_iclog(xlog_t *log,
ptr = iclog->ic_datap;
base_ptr = ptr;
ophead = (xlog_op_header_t *)ptr;
- xhdr = (xlog_in_core_2_t *)&iclog->ic_header;
+ xhdr = iclog->ic_data;
for (i = 0; i < len; i++) {
ophead = (xlog_op_header_t *)ptr;
@@ -3558,7 +3562,8 @@ xfs_log_force_umount(
if (!log ||
log->l_flags & XLOG_ACTIVE_RECOVERY) {
mp->m_flags |= XFS_MOUNT_FS_SHUTDOWN;
- XFS_BUF_DONE(mp->m_sb_bp);
+ if (mp->m_sb_bp)
+ XFS_BUF_DONE(mp->m_sb_bp);
return 0;
}
@@ -3579,7 +3584,9 @@ xfs_log_force_umount(
spin_lock(&log->l_icloglock);
spin_lock(&log->l_grant_lock);
mp->m_flags |= XFS_MOUNT_FS_SHUTDOWN;
- XFS_BUF_DONE(mp->m_sb_bp);
+ if (mp->m_sb_bp)
+ XFS_BUF_DONE(mp->m_sb_bp);
+
/*
* This flag is sort of redundant because of the mount flag, but
* it's good to maintain the separation between the log and the rest
diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h
index d47b91f10822..8a3e84e900a3 100644
--- a/fs/xfs/xfs_log.h
+++ b/fs/xfs/xfs_log.h
@@ -134,6 +134,7 @@ typedef struct xfs_log_callback {
#ifdef __KERNEL__
/* Log manager interfaces */
struct xfs_mount;
+struct xlog_ticket;
xfs_lsn_t xfs_log_done(struct xfs_mount *mp,
xfs_log_ticket_t ticket,
void **iclog,
@@ -177,6 +178,9 @@ int xfs_log_need_covered(struct xfs_mount *mp);
void xlog_iodone(struct xfs_buf *);
+struct xlog_ticket * xfs_log_ticket_get(struct xlog_ticket *ticket);
+void xfs_log_ticket_put(struct xlog_ticket *ticket);
+
#endif
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index e7d8f84443fa..654167be0efb 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -245,6 +245,7 @@ typedef struct xlog_ticket {
struct xlog_ticket *t_next; /* :4|8 */
struct xlog_ticket *t_prev; /* :4|8 */
xlog_tid_t t_tid; /* transaction identifier : 4 */
+ atomic_t t_ref; /* ticket reference count : 4 */
int t_curr_res; /* current reservation in bytes : 4 */
int t_unit_res; /* unit reservation in bytes : 4 */
char t_ocnt; /* original count : 1 */
@@ -309,6 +310,16 @@ typedef struct xlog_rec_ext_header {
} xlog_rec_ext_header_t;
#ifdef __KERNEL__
+
+/*
+ * Quite misnamed, because this union lays out the actual on-disk log buffer.
+ */
+typedef union xlog_in_core2 {
+ xlog_rec_header_t hic_header;
+ xlog_rec_ext_header_t hic_xheader;
+ char hic_sector[XLOG_HEADER_SIZE];
+} xlog_in_core_2_t;
+
/*
* - A log record header is 512 bytes. There is plenty of room to grow the
* xlog_rec_header_t into the reserved space.
@@ -338,7 +349,7 @@ typedef struct xlog_rec_ext_header {
* We'll put all the read-only and l_icloglock fields in the first cacheline,
* and move everything else out to subsequent cachelines.
*/
-typedef struct xlog_iclog_fields {
+typedef struct xlog_in_core {
sv_t ic_force_wait;
sv_t ic_write_wait;
struct xlog_in_core *ic_next;
@@ -361,41 +372,11 @@ typedef struct xlog_iclog_fields {
/* reference counts need their own cacheline */
atomic_t ic_refcnt ____cacheline_aligned_in_smp;
-} xlog_iclog_fields_t;
-
-typedef union xlog_in_core2 {
- xlog_rec_header_t hic_header;
- xlog_rec_ext_header_t hic_xheader;
- char hic_sector[XLOG_HEADER_SIZE];
-} xlog_in_core_2_t;
-
-typedef struct xlog_in_core {
- xlog_iclog_fields_t hic_fields;
- xlog_in_core_2_t *hic_data;
+ xlog_in_core_2_t *ic_data;
+#define ic_header ic_data->hic_header
} xlog_in_core_t;
/*
- * Defines to save our code from this glop.
- */
-#define ic_force_wait hic_fields.ic_force_wait
-#define ic_write_wait hic_fields.ic_write_wait
-#define ic_next hic_fields.ic_next
-#define ic_prev hic_fields.ic_prev
-#define ic_bp hic_fields.ic_bp
-#define ic_log hic_fields.ic_log
-#define ic_callback hic_fields.ic_callback
-#define ic_callback_lock hic_fields.ic_callback_lock
-#define ic_callback_tail hic_fields.ic_callback_tail
-#define ic_trace hic_fields.ic_trace
-#define ic_size hic_fields.ic_size
-#define ic_offset hic_fields.ic_offset
-#define ic_refcnt hic_fields.ic_refcnt
-#define ic_bwritecnt hic_fields.ic_bwritecnt
-#define ic_state hic_fields.ic_state
-#define ic_datap hic_fields.ic_datap
-#define ic_header hic_data->hic_header
-
-/*
* The reservation head lsn is not made up of a cycle number and block number.
* Instead, it uses a cycle number and byte number. Logs don't expect to
* overflow 31 bits worth of byte offset, so using a byte number will mean
@@ -404,6 +385,7 @@ typedef struct xlog_in_core {
typedef struct log {
/* The following fields don't need locking */
struct xfs_mount *l_mp; /* mount point */
+ struct xfs_ail *l_ailp; /* AIL log is working with */
struct xfs_buf *l_xbuf; /* extra buffer for log
* wrapping */
struct xfs_buftarg *l_targ; /* buftarg of log */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 70e3ba32e6be..35cca98bd94c 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -36,7 +36,6 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
-#include "xfs_imap.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_log_priv.h"
@@ -54,10 +53,8 @@ STATIC void xlog_recover_insert_item_backq(xlog_recover_item_t **q,
xlog_recover_item_t *item);
#if defined(DEBUG)
STATIC void xlog_recover_check_summary(xlog_t *);
-STATIC void xlog_recover_check_ail(xfs_mount_t *, xfs_log_item_t *, int);
#else
#define xlog_recover_check_summary(log)
-#define xlog_recover_check_ail(mp, lip, gen)
#endif
@@ -270,21 +267,16 @@ STATIC void
xlog_recover_iodone(
struct xfs_buf *bp)
{
- xfs_mount_t *mp;
-
- ASSERT(XFS_BUF_FSPRIVATE(bp, void *));
-
if (XFS_BUF_GETERROR(bp)) {
/*
* We're not going to bother about retrying
* this during recovery. One strike!
*/
- mp = XFS_BUF_FSPRIVATE(bp, xfs_mount_t *);
xfs_ioerror_alert("xlog_recover_iodone",
- mp, bp, XFS_BUF_ADDR(bp));
- xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
+ bp->b_mount, bp, XFS_BUF_ADDR(bp));
+ xfs_force_shutdown(bp->b_mount, SHUTDOWN_META_IO_ERROR);
}
- XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ bp->b_mount = NULL;
XFS_BUF_CLR_IODONE_FUNC(bp);
xfs_biodone(bp);
}
@@ -2228,9 +2220,8 @@ xlog_recover_do_buffer_trans(
XFS_BUF_STALE(bp);
error = xfs_bwrite(mp, bp);
} else {
- ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
- XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
- XFS_BUF_SET_FSPRIVATE(bp, mp);
+ ASSERT(bp->b_mount == NULL || bp->b_mount == mp);
+ bp->b_mount = mp;
XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
xfs_bdwrite(mp, bp);
}
@@ -2247,7 +2238,6 @@ xlog_recover_do_inode_trans(
xfs_inode_log_format_t *in_f;
xfs_mount_t *mp;
xfs_buf_t *bp;
- xfs_imap_t imap;
xfs_dinode_t *dip;
xfs_ino_t ino;
int len;
@@ -2275,54 +2265,35 @@ xlog_recover_do_inode_trans(
}
ino = in_f->ilf_ino;
mp = log->l_mp;
- if (ITEM_TYPE(item) == XFS_LI_INODE) {
- imap.im_blkno = (xfs_daddr_t)in_f->ilf_blkno;
- imap.im_len = in_f->ilf_len;
- imap.im_boffset = in_f->ilf_boffset;
- } else {
- /*
- * It's an old inode format record. We don't know where
- * its cluster is located on disk, and we can't allow
- * xfs_imap() to figure it out because the inode btrees
- * are not ready to be used. Therefore do not pass the
- * XFS_IMAP_LOOKUP flag to xfs_imap(). This will give
- * us only the single block in which the inode lives
- * rather than its cluster, so we must make sure to
- * invalidate the buffer when we write it out below.
- */
- imap.im_blkno = 0;
- error = xfs_imap(log->l_mp, NULL, ino, &imap, 0);
- if (error)
- goto error;
- }
/*
* Inode buffers can be freed, look out for it,
* and do not replay the inode.
*/
- if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) {
+ if (xlog_check_buffer_cancelled(log, in_f->ilf_blkno,
+ in_f->ilf_len, 0)) {
error = 0;
goto error;
}
- bp = xfs_buf_read_flags(mp->m_ddev_targp, imap.im_blkno, imap.im_len,
- XFS_BUF_LOCK);
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, in_f->ilf_blkno,
+ in_f->ilf_len, XFS_BUF_LOCK);
if (XFS_BUF_ISERROR(bp)) {
xfs_ioerror_alert("xlog_recover_do..(read#2)", mp,
- bp, imap.im_blkno);
+ bp, in_f->ilf_blkno);
error = XFS_BUF_GETERROR(bp);
xfs_buf_relse(bp);
goto error;
}
error = 0;
ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
- dip = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, in_f->ilf_boffset);
/*
* Make sure the place we're flushing out to really looks
* like an inode!
*/
- if (unlikely(be16_to_cpu(dip->di_core.di_magic) != XFS_DINODE_MAGIC)) {
+ if (unlikely(be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC)) {
xfs_buf_relse(bp);
xfs_fs_cmn_err(CE_ALERT, mp,
"xfs_inode_recover: Bad inode magic number, dino ptr = 0x%p, dino bp = 0x%p, ino = %Ld",
@@ -2345,12 +2316,12 @@ xlog_recover_do_inode_trans(
}
/* Skip replay when the on disk inode is newer than the log one */
- if (dicp->di_flushiter < be16_to_cpu(dip->di_core.di_flushiter)) {
+ if (dicp->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
/*
* Deal with the wrap case, DI_MAX_FLUSH is less
* than smaller numbers
*/
- if (be16_to_cpu(dip->di_core.di_flushiter) == DI_MAX_FLUSH &&
+ if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
dicp->di_flushiter < (DI_MAX_FLUSH >> 1)) {
/* do nothing */
} else {
@@ -2410,7 +2381,7 @@ xlog_recover_do_inode_trans(
error = EFSCORRUPTED;
goto error;
}
- if (unlikely(item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t))) {
+ if (unlikely(item->ri_buf[1].i_len > sizeof(struct xfs_icdinode))) {
XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(7)",
XFS_ERRLEVEL_LOW, mp, dicp);
xfs_buf_relse(bp);
@@ -2422,23 +2393,24 @@ xlog_recover_do_inode_trans(
}
/* The core is in in-core format */
- xfs_dinode_to_disk(&dip->di_core,
- (xfs_icdinode_t *)item->ri_buf[1].i_addr);
+ xfs_dinode_to_disk(dip, (xfs_icdinode_t *)item->ri_buf[1].i_addr);
/* the rest is in on-disk format */
- if (item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t)) {
- memcpy((xfs_caddr_t) dip + sizeof(xfs_dinode_core_t),
- item->ri_buf[1].i_addr + sizeof(xfs_dinode_core_t),
- item->ri_buf[1].i_len - sizeof(xfs_dinode_core_t));
+ if (item->ri_buf[1].i_len > sizeof(struct xfs_icdinode)) {
+ memcpy((xfs_caddr_t) dip + sizeof(struct xfs_icdinode),
+ item->ri_buf[1].i_addr + sizeof(struct xfs_icdinode),
+ item->ri_buf[1].i_len - sizeof(struct xfs_icdinode));
}
fields = in_f->ilf_fields;
switch (fields & (XFS_ILOG_DEV | XFS_ILOG_UUID)) {
case XFS_ILOG_DEV:
- dip->di_u.di_dev = cpu_to_be32(in_f->ilf_u.ilfu_rdev);
+ xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev);
break;
case XFS_ILOG_UUID:
- dip->di_u.di_muuid = in_f->ilf_u.ilfu_uuid;
+ memcpy(XFS_DFORK_DPTR(dip),
+ &in_f->ilf_u.ilfu_uuid,
+ sizeof(uuid_t));
break;
}
@@ -2454,12 +2426,12 @@ xlog_recover_do_inode_trans(
switch (fields & XFS_ILOG_DFORK) {
case XFS_ILOG_DDATA:
case XFS_ILOG_DEXT:
- memcpy(&dip->di_u, src, len);
+ memcpy(XFS_DFORK_DPTR(dip), src, len);
break;
case XFS_ILOG_DBROOT:
- xfs_bmbt_to_bmdr((xfs_bmbt_block_t *)src, len,
- &(dip->di_u.di_bmbt),
+ xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len,
+ (xfs_bmdr_block_t *)XFS_DFORK_DPTR(dip),
XFS_DFORK_DSIZE(dip, mp));
break;
@@ -2496,8 +2468,8 @@ xlog_recover_do_inode_trans(
case XFS_ILOG_ABROOT:
dest = XFS_DFORK_APTR(dip);
- xfs_bmbt_to_bmdr((xfs_bmbt_block_t *)src, len,
- (xfs_bmdr_block_t*)dest,
+ xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src,
+ len, (xfs_bmdr_block_t*)dest,
XFS_DFORK_ASIZE(dip, mp));
break;
@@ -2512,9 +2484,8 @@ xlog_recover_do_inode_trans(
write_inode_buffer:
if (ITEM_TYPE(item) == XFS_LI_INODE) {
- ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
- XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
- XFS_BUF_SET_FSPRIVATE(bp, mp);
+ ASSERT(bp->b_mount == NULL || bp->b_mount == mp);
+ bp->b_mount = mp;
XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
xfs_bdwrite(mp, bp);
} else {
@@ -2645,9 +2616,8 @@ xlog_recover_do_dquot_trans(
memcpy(ddq, recddq, item->ri_buf[1].i_len);
ASSERT(dq_f->qlf_size == 2);
- ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
- XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
- XFS_BUF_SET_FSPRIVATE(bp, mp);
+ ASSERT(bp->b_mount == NULL || bp->b_mount == mp);
+ bp->b_mount = mp;
XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
xfs_bdwrite(mp, bp);
@@ -2689,11 +2659,11 @@ xlog_recover_do_efi_trans(
efip->efi_next_extent = efi_formatp->efi_nextents;
efip->efi_flags |= XFS_EFI_COMMITTED;
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&log->l_ailp->xa_lock);
/*
- * xfs_trans_update_ail() drops the AIL lock.
+ * xfs_trans_ail_update() drops the AIL lock.
*/
- xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn);
+ xfs_trans_ail_update(log->l_ailp, (xfs_log_item_t *)efip, lsn);
return 0;
}
@@ -2712,12 +2682,12 @@ xlog_recover_do_efd_trans(
xlog_recover_item_t *item,
int pass)
{
- xfs_mount_t *mp;
xfs_efd_log_format_t *efd_formatp;
xfs_efi_log_item_t *efip = NULL;
xfs_log_item_t *lip;
- int gen;
__uint64_t efi_id;
+ struct xfs_ail_cursor cur;
+ struct xfs_ail *ailp = log->l_ailp;
if (pass == XLOG_RECOVER_PASS1) {
return;
@@ -2734,25 +2704,26 @@ xlog_recover_do_efd_trans(
* Search for the efi with the id in the efd format structure
* in the AIL.
*/
- mp = log->l_mp;
- spin_lock(&mp->m_ail_lock);
- lip = xfs_trans_first_ail(mp, &gen);
+ spin_lock(&ailp->xa_lock);
+ lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
while (lip != NULL) {
if (lip->li_type == XFS_LI_EFI) {
efip = (xfs_efi_log_item_t *)lip;
if (efip->efi_format.efi_id == efi_id) {
/*
- * xfs_trans_delete_ail() drops the
+ * xfs_trans_ail_delete() drops the
* AIL lock.
*/
- xfs_trans_delete_ail(mp, lip);
+ xfs_trans_ail_delete(ailp, lip);
xfs_efi_item_free(efip);
- return;
+ spin_lock(&ailp->xa_lock);
+ break;
}
}
- lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ lip = xfs_trans_ail_cursor_next(ailp, &cur);
}
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_cursor_done(ailp, &cur);
+ spin_unlock(&ailp->xa_lock);
}
/*
@@ -3036,33 +3007,6 @@ abort_error:
}
/*
- * Verify that once we've encountered something other than an EFI
- * in the AIL that there are no more EFIs in the AIL.
- */
-#if defined(DEBUG)
-STATIC void
-xlog_recover_check_ail(
- xfs_mount_t *mp,
- xfs_log_item_t *lip,
- int gen)
-{
- int orig_gen = gen;
-
- do {
- ASSERT(lip->li_type != XFS_LI_EFI);
- lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
- /*
- * The check will be bogus if we restart from the
- * beginning of the AIL, so ASSERT that we don't.
- * We never should since we're holding the AIL lock
- * the entire time.
- */
- ASSERT(gen == orig_gen);
- } while (lip != NULL);
-}
-#endif /* DEBUG */
-
-/*
* When this is called, all of the EFIs which did not have
* corresponding EFDs should be in the AIL. What we do now
* is free the extents associated with each one.
@@ -3086,20 +3030,23 @@ xlog_recover_process_efis(
{
xfs_log_item_t *lip;
xfs_efi_log_item_t *efip;
- int gen;
- xfs_mount_t *mp;
int error = 0;
+ struct xfs_ail_cursor cur;
+ struct xfs_ail *ailp;
- mp = log->l_mp;
- spin_lock(&mp->m_ail_lock);
-
- lip = xfs_trans_first_ail(mp, &gen);
+ ailp = log->l_ailp;
+ spin_lock(&ailp->xa_lock);
+ lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
while (lip != NULL) {
/*
* We're done when we see something other than an EFI.
+ * There should be no EFIs left in the AIL now.
*/
if (lip->li_type != XFS_LI_EFI) {
- xlog_recover_check_ail(mp, lip, gen);
+#ifdef DEBUG
+ for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
+ ASSERT(lip->li_type != XFS_LI_EFI);
+#endif
break;
}
@@ -3108,18 +3055,20 @@ xlog_recover_process_efis(
*/
efip = (xfs_efi_log_item_t *)lip;
if (efip->efi_flags & XFS_EFI_RECOVERED) {
- lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ lip = xfs_trans_ail_cursor_next(ailp, &cur);
continue;
}
- spin_unlock(&mp->m_ail_lock);
- error = xlog_recover_process_efi(mp, efip);
+ spin_unlock(&ailp->xa_lock);
+ error = xlog_recover_process_efi(log->l_mp, efip);
+ spin_lock(&ailp->xa_lock);
if (error)
- return error;
- spin_lock(&mp->m_ail_lock);
- lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ goto out;
+ lip = xfs_trans_ail_cursor_next(ailp, &cur);
}
- spin_unlock(&mp->m_ail_lock);
+out:
+ xfs_trans_ail_cursor_done(ailp, &cur);
+ spin_unlock(&ailp->xa_lock);
return error;
}
@@ -3140,19 +3089,16 @@ xlog_recover_clear_agi_bucket(
int error;
tp = xfs_trans_alloc(mp, XFS_TRANS_CLEAR_AGI_BUCKET);
- error = xfs_trans_reserve(tp, 0, XFS_CLEAR_AGI_BUCKET_LOG_RES(mp), 0, 0, 0);
- if (!error)
- error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1), 0, &agibp);
+ error = xfs_trans_reserve(tp, 0, XFS_CLEAR_AGI_BUCKET_LOG_RES(mp),
+ 0, 0, 0);
if (error)
goto out_abort;
- error = EINVAL;
- agi = XFS_BUF_TO_AGI(agibp);
- if (be32_to_cpu(agi->agi_magicnum) != XFS_AGI_MAGIC)
+ error = xfs_read_agi(mp, tp, agno, &agibp);
+ if (error)
goto out_abort;
+ agi = XFS_BUF_TO_AGI(agibp);
agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket);
@@ -3172,6 +3118,62 @@ out_error:
return;
}
+STATIC xfs_agino_t
+xlog_recover_process_one_iunlink(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ xfs_agino_t agino,
+ int bucket)
+{
+ struct xfs_buf *ibp;
+ struct xfs_dinode *dip;
+ struct xfs_inode *ip;
+ xfs_ino_t ino;
+ int error;
+
+ ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ error = xfs_iget(mp, NULL, ino, 0, 0, &ip, 0);
+ if (error)
+ goto fail;
+
+ /*
+ * Get the on disk inode to find the next inode in the bucket.
+ */
+ error = xfs_itobp(mp, NULL, ip, &dip, &ibp, XFS_BUF_LOCK);
+ if (error)
+ goto fail_iput;
+
+ ASSERT(ip->i_d.di_nlink == 0);
+ ASSERT(ip->i_d.di_mode != 0);
+
+ /* setup for the next pass */
+ agino = be32_to_cpu(dip->di_next_unlinked);
+ xfs_buf_relse(ibp);
+
+ /*
+ * Prevent any DMAPI event from being sent when the reference on
+ * the inode is dropped.
+ */
+ ip->i_d.di_dmevmask = 0;
+
+ IRELE(ip);
+ return agino;
+
+ fail_iput:
+ IRELE(ip);
+ fail:
+ /*
+ * We can't read in the inode this bucket points to, or this inode
+ * is messed up. Just ditch this bucket of inodes. We will lose
+ * some inodes and space, but at least we won't hang.
+ *
+ * Call xlog_recover_clear_agi_bucket() to perform a transaction to
+ * clear the inode pointer in the bucket.
+ */
+ xlog_recover_clear_agi_bucket(mp, agno, bucket);
+ return NULLAGINO;
+}
+
/*
* xlog_iunlink_recover
*
@@ -3192,11 +3194,7 @@ xlog_recover_process_iunlinks(
xfs_agnumber_t agno;
xfs_agi_t *agi;
xfs_buf_t *agibp;
- xfs_buf_t *ibp;
- xfs_dinode_t *dip;
- xfs_inode_t *ip;
xfs_agino_t agino;
- xfs_ino_t ino;
int bucket;
int error;
uint mp_dmevmask;
@@ -3213,22 +3211,21 @@ xlog_recover_process_iunlinks(
/*
* Find the agi for this ag.
*/
- agibp = xfs_buf_read(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1), 0);
- if (XFS_BUF_ISERROR(agibp)) {
- xfs_ioerror_alert("xlog_recover_process_iunlinks(#1)",
- log->l_mp, agibp,
- XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)));
+ error = xfs_read_agi(mp, NULL, agno, &agibp);
+ if (error) {
+ /*
+ * AGI is b0rked. Don't process it.
+ *
+ * We should probably mark the filesystem as corrupt
+ * after we've recovered all the ag's we can....
+ */
+ continue;
}
agi = XFS_BUF_TO_AGI(agibp);
- ASSERT(XFS_AGI_MAGIC == be32_to_cpu(agi->agi_magicnum));
for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) {
-
agino = be32_to_cpu(agi->agi_unlinked[bucket]);
while (agino != NULLAGINO) {
-
/*
* Release the agi buffer so that it can
* be acquired in the normal course of the
@@ -3236,87 +3233,17 @@ xlog_recover_process_iunlinks(
*/
xfs_buf_relse(agibp);
- ino = XFS_AGINO_TO_INO(mp, agno, agino);
- error = xfs_iget(mp, NULL, ino, 0, 0, &ip, 0);
- ASSERT(error || (ip != NULL));
-
- if (!error) {
- /*
- * Get the on disk inode to find the
- * next inode in the bucket.
- */
- error = xfs_itobp(mp, NULL, ip, &dip,
- &ibp, 0, 0,
- XFS_BUF_LOCK);
- ASSERT(error || (dip != NULL));
- }
-
- if (!error) {
- ASSERT(ip->i_d.di_nlink == 0);
-
- /* setup for the next pass */
- agino = be32_to_cpu(
- dip->di_next_unlinked);
- xfs_buf_relse(ibp);
- /*
- * Prevent any DMAPI event from
- * being sent when the
- * reference on the inode is
- * dropped.
- */
- ip->i_d.di_dmevmask = 0;
-
- /*
- * If this is a new inode, handle
- * it specially. Otherwise,
- * just drop our reference to the
- * inode. If there are no
- * other references, this will
- * send the inode to
- * xfs_inactive() which will
- * truncate the file and free
- * the inode.
- */
- if (ip->i_d.di_mode == 0)
- xfs_iput_new(ip, 0);
- else
- IRELE(ip);
- } else {
- /*
- * We can't read in the inode
- * this bucket points to, or
- * this inode is messed up. Just
- * ditch this bucket of inodes. We
- * will lose some inodes and space,
- * but at least we won't hang. Call
- * xlog_recover_clear_agi_bucket()
- * to perform a transaction to clear
- * the inode pointer in the bucket.
- */
- xlog_recover_clear_agi_bucket(mp, agno,
- bucket);
-
- agino = NULLAGINO;
- }
+ agino = xlog_recover_process_one_iunlink(mp,
+ agno, agino, bucket);
/*
* Reacquire the agibuffer and continue around
- * the loop.
+ * the loop. This should never fail as we know
+ * the buffer was good earlier on.
*/
- agibp = xfs_buf_read(mp->m_ddev_targp,
- XFS_AG_DADDR(mp, agno,
- XFS_AGI_DADDR(mp)),
- XFS_FSS_TO_BB(mp, 1), 0);
- if (XFS_BUF_ISERROR(agibp)) {
- xfs_ioerror_alert(
- "xlog_recover_process_iunlinks(#2)",
- log->l_mp, agibp,
- XFS_AG_DADDR(mp, agno,
- XFS_AGI_DADDR(mp)));
- }
+ error = xfs_read_agi(mp, NULL, agno, &agibp);
+ ASSERT(error == 0);
agi = XFS_BUF_TO_AGI(agibp);
- ASSERT(XFS_AGI_MAGIC == be32_to_cpu(
- agi->agi_magicnum));
}
}
@@ -3367,7 +3294,6 @@ xlog_pack_data(
int size = iclog->ic_offset + roundoff;
__be32 cycle_lsn;
xfs_caddr_t dp;
- xlog_in_core_2_t *xhdr;
xlog_pack_data_checksum(log, iclog, size);
@@ -3382,7 +3308,8 @@ xlog_pack_data(
}
if (xfs_sb_version_haslogv2(&log->l_mp->m_sb)) {
- xhdr = (xlog_in_core_2_t *)&iclog->ic_header;
+ xlog_in_core_2_t *xhdr = iclog->ic_data;
+
for ( ; i < BTOBB(size); i++) {
j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
@@ -3440,7 +3367,6 @@ xlog_unpack_data(
xlog_t *log)
{
int i, j, k;
- xlog_in_core_2_t *xhdr;
for (i = 0; i < BTOBB(be32_to_cpu(rhead->h_len)) &&
i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
@@ -3449,7 +3375,7 @@ xlog_unpack_data(
}
if (xfs_sb_version_haslogv2(&log->l_mp->m_sb)) {
- xhdr = (xlog_in_core_2_t *)rhead;
+ xlog_in_core_2_t *xhdr = (xlog_in_core_2_t *)rhead;
for ( ; i < BTOBB(be32_to_cpu(rhead->h_len)); i++) {
j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
@@ -4003,11 +3929,8 @@ xlog_recover_check_summary(
{
xfs_mount_t *mp;
xfs_agf_t *agfp;
- xfs_agi_t *agip;
xfs_buf_t *agfbp;
xfs_buf_t *agibp;
- xfs_daddr_t agfdaddr;
- xfs_daddr_t agidaddr;
xfs_buf_t *sbbp;
#ifdef XFS_LOUD_RECOVERY
xfs_sb_t *sbp;
@@ -4016,6 +3939,7 @@ xlog_recover_check_summary(
__uint64_t freeblks;
__uint64_t itotal;
__uint64_t ifree;
+ int error;
mp = log->l_mp;
@@ -4023,37 +3947,27 @@ xlog_recover_check_summary(
itotal = 0LL;
ifree = 0LL;
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
- agfdaddr = XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp));
- agfbp = xfs_buf_read(mp->m_ddev_targp, agfdaddr,
- XFS_FSS_TO_BB(mp, 1), 0);
- if (XFS_BUF_ISERROR(agfbp)) {
- xfs_ioerror_alert("xlog_recover_check_summary(agf)",
- mp, agfbp, agfdaddr);
- }
- agfp = XFS_BUF_TO_AGF(agfbp);
- ASSERT(XFS_AGF_MAGIC == be32_to_cpu(agfp->agf_magicnum));
- ASSERT(XFS_AGF_GOOD_VERSION(be32_to_cpu(agfp->agf_versionnum)));
- ASSERT(be32_to_cpu(agfp->agf_seqno) == agno);
-
- freeblks += be32_to_cpu(agfp->agf_freeblks) +
- be32_to_cpu(agfp->agf_flcount);
- xfs_buf_relse(agfbp);
-
- agidaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
- agibp = xfs_buf_read(mp->m_ddev_targp, agidaddr,
- XFS_FSS_TO_BB(mp, 1), 0);
- if (XFS_BUF_ISERROR(agibp)) {
- xfs_ioerror_alert("xlog_recover_check_summary(agi)",
- mp, agibp, agidaddr);
+ error = xfs_read_agf(mp, NULL, agno, 0, &agfbp);
+ if (error) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xlog_recover_check_summary(agf)"
+ "agf read failed agno %d error %d",
+ agno, error);
+ } else {
+ agfp = XFS_BUF_TO_AGF(agfbp);
+ freeblks += be32_to_cpu(agfp->agf_freeblks) +
+ be32_to_cpu(agfp->agf_flcount);
+ xfs_buf_relse(agfbp);
}
- agip = XFS_BUF_TO_AGI(agibp);
- ASSERT(XFS_AGI_MAGIC == be32_to_cpu(agip->agi_magicnum));
- ASSERT(XFS_AGI_GOOD_VERSION(be32_to_cpu(agip->agi_versionnum)));
- ASSERT(be32_to_cpu(agip->agi_seqno) == agno);
- itotal += be32_to_cpu(agip->agi_count);
- ifree += be32_to_cpu(agip->agi_freecount);
- xfs_buf_relse(agibp);
+ error = xfs_read_agi(mp, NULL, agno, &agibp);
+ if (!error) {
+ struct xfs_agi *agi = XFS_BUF_TO_AGI(agibp);
+
+ itotal += be32_to_cpu(agi->agi_count);
+ ifree += be32_to_cpu(agi->agi_freecount);
+ xfs_buf_relse(agibp);
+ }
}
sbbp = xfs_getsb(mp, 0);
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 15f5dd22fbb2..3c97c6463a4e 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -567,8 +567,6 @@ xfs_readsb(xfs_mount_t *mp, int flags)
STATIC void
xfs_mount_common(xfs_mount_t *mp, xfs_sb_t *sbp)
{
- int i;
-
mp->m_agfrotor = mp->m_agirotor = 0;
spin_lock_init(&mp->m_agirotor_lock);
mp->m_maxagi = mp->m_sb.sb_agcount;
@@ -577,12 +575,10 @@ xfs_mount_common(xfs_mount_t *mp, xfs_sb_t *sbp)
mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
mp->m_agno_log = xfs_highbit32(sbp->sb_agcount - 1) + 1;
mp->m_agino_log = sbp->sb_inopblog + sbp->sb_agblklog;
- mp->m_litino = sbp->sb_inodesize -
- ((uint)sizeof(xfs_dinode_core_t) + (uint)sizeof(xfs_agino_t));
+ mp->m_litino = sbp->sb_inodesize - sizeof(struct xfs_dinode);
mp->m_blockmask = sbp->sb_blocksize - 1;
mp->m_blockwsize = sbp->sb_blocksize >> XFS_WORDLOG;
mp->m_blockwmask = mp->m_blockwsize - 1;
- INIT_LIST_HEAD(&mp->m_del_inodes);
/*
* Setup for attributes, in case they get created.
@@ -605,24 +601,20 @@ xfs_mount_common(xfs_mount_t *mp, xfs_sb_t *sbp)
}
ASSERT(mp->m_attroffset < XFS_LITINO(mp));
- for (i = 0; i < 2; i++) {
- mp->m_alloc_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
- xfs_alloc, i == 0);
- mp->m_alloc_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
- xfs_alloc, i == 0);
- }
- for (i = 0; i < 2; i++) {
- mp->m_bmap_dmxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
- xfs_bmbt, i == 0);
- mp->m_bmap_dmnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
- xfs_bmbt, i == 0);
- }
- for (i = 0; i < 2; i++) {
- mp->m_inobt_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
- xfs_inobt, i == 0);
- mp->m_inobt_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
- xfs_inobt, i == 0);
- }
+ mp->m_alloc_mxr[0] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 1);
+ mp->m_alloc_mxr[1] = xfs_allocbt_maxrecs(mp, sbp->sb_blocksize, 0);
+ mp->m_alloc_mnr[0] = mp->m_alloc_mxr[0] / 2;
+ mp->m_alloc_mnr[1] = mp->m_alloc_mxr[1] / 2;
+
+ mp->m_inobt_mxr[0] = xfs_inobt_maxrecs(mp, sbp->sb_blocksize, 1);
+ mp->m_inobt_mxr[1] = xfs_inobt_maxrecs(mp, sbp->sb_blocksize, 0);
+ mp->m_inobt_mnr[0] = mp->m_inobt_mxr[0] / 2;
+ mp->m_inobt_mnr[1] = mp->m_inobt_mxr[1] / 2;
+
+ mp->m_bmap_dmxr[0] = xfs_bmbt_maxrecs(mp, sbp->sb_blocksize, 1);
+ mp->m_bmap_dmxr[1] = xfs_bmbt_maxrecs(mp, sbp->sb_blocksize, 0);
+ mp->m_bmap_dmnr[0] = mp->m_bmap_dmxr[0] / 2;
+ mp->m_bmap_dmnr[1] = mp->m_bmap_dmxr[1] / 2;
mp->m_bsize = XFS_FSB_TO_BB(mp, 1);
mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK,
@@ -1228,6 +1220,16 @@ xfs_unmountfs(
__uint64_t resblks;
int error;
+ /*
+ * Release dquot that rootinode, rbmino and rsumino might be holding,
+ * and release the quota inodes.
+ */
+ XFS_QM_UNMOUNT(mp);
+
+ if (mp->m_rbmip)
+ IRELE(mp->m_rbmip);
+ if (mp->m_rsumip)
+ IRELE(mp->m_rsumip);
IRELE(mp->m_rootip);
/*
@@ -1241,7 +1243,7 @@ xfs_unmountfs(
* need to force the log first.
*/
xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC);
- xfs_iflush_all(mp);
+ xfs_reclaim_inodes(mp, 0, XFS_IFLUSH_ASYNC);
XFS_QM_DQPURGEALL(mp, XFS_QMOPT_QUOTALL | XFS_QMOPT_UMOUNTING);
@@ -1288,11 +1290,6 @@ xfs_unmountfs(
xfs_unmountfs_wait(mp); /* wait for async bufs */
xfs_log_unmount(mp); /* Done! No more fs ops. */
- /*
- * All inodes from this mount point should be freed.
- */
- ASSERT(mp->m_inodes == NULL);
-
if ((mp->m_flags & XFS_MOUNT_NOUUID) == 0)
uuid_table_remove(&mp->m_sb.sb_uuid);
@@ -1365,24 +1362,6 @@ xfs_log_sbcount(
return error;
}
-STATIC void
-xfs_mark_shared_ro(
- xfs_mount_t *mp,
- xfs_buf_t *bp)
-{
- xfs_dsb_t *sb = XFS_BUF_TO_SBP(bp);
- __uint16_t version;
-
- if (!(sb->sb_flags & XFS_SBF_READONLY))
- sb->sb_flags |= XFS_SBF_READONLY;
-
- version = be16_to_cpu(sb->sb_versionnum);
- if ((version & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4 ||
- !(version & XFS_SB_VERSION_SHAREDBIT))
- version |= XFS_SB_VERSION_SHAREDBIT;
- sb->sb_versionnum = cpu_to_be16(version);
-}
-
int
xfs_unmountfs_writesb(xfs_mount_t *mp)
{
@@ -1398,12 +1377,6 @@ xfs_unmountfs_writesb(xfs_mount_t *mp)
sbp = xfs_getsb(mp, 0);
- /*
- * mark shared-readonly if desired
- */
- if (mp->m_mk_sharedro)
- xfs_mark_shared_ro(mp, sbp);
-
XFS_BUF_UNDONE(sbp);
XFS_BUF_UNREAD(sbp);
XFS_BUF_UNDELAYWRITE(sbp);
@@ -1415,8 +1388,6 @@ xfs_unmountfs_writesb(xfs_mount_t *mp)
if (error)
xfs_ioerror_alert("xfs_unmountfs_writesb",
mp, sbp, XFS_BUF_ADDR(sbp));
- if (error && mp->m_mk_sharedro)
- xfs_fs_cmn_err(CE_ALERT, mp, "Superblock write error detected while unmounting. Filesystem may not be marked shared readonly");
xfs_buf_relse(sbp);
}
return error;
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index f3c1024b1241..c1e028467327 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -18,7 +18,6 @@
#ifndef __XFS_MOUNT_H__
#define __XFS_MOUNT_H__
-
typedef struct xfs_trans_reservations {
uint tr_write; /* extent alloc trans */
uint tr_itruncate; /* truncate trans */
@@ -44,14 +43,16 @@ typedef struct xfs_trans_reservations {
} xfs_trans_reservations_t;
#ifndef __KERNEL__
-/*
- * Moved here from xfs_ag.h to avoid reordering header files
- */
+
#define XFS_DADDR_TO_AGNO(mp,d) \
((xfs_agnumber_t)(XFS_BB_TO_FSBT(mp, d) / (mp)->m_sb.sb_agblocks))
#define XFS_DADDR_TO_AGBNO(mp,d) \
((xfs_agblock_t)(XFS_BB_TO_FSBT(mp, d) % (mp)->m_sb.sb_agblocks))
-#else
+
+#else /* __KERNEL__ */
+
+#include "xfs_sync.h"
+
struct cred;
struct log;
struct xfs_mount_args;
@@ -62,6 +63,7 @@ struct xfs_extdelta;
struct xfs_swapext;
struct xfs_mru_cache;
struct xfs_nameops;
+struct xfs_ail;
/*
* Prototypes and functions for the Data Migration subsystem.
@@ -115,7 +117,7 @@ struct xfs_quotainfo;
typedef int (*xfs_qminit_t)(struct xfs_mount *, uint *, uint *);
typedef int (*xfs_qmmount_t)(struct xfs_mount *, uint, uint);
-typedef int (*xfs_qmunmount_t)(struct xfs_mount *);
+typedef void (*xfs_qmunmount_t)(struct xfs_mount *);
typedef void (*xfs_qmdone_t)(struct xfs_mount *);
typedef void (*xfs_dqrele_t)(struct xfs_dquot *);
typedef int (*xfs_dqattach_t)(struct xfs_inode *, uint);
@@ -132,7 +134,7 @@ typedef struct xfs_dquot * (*xfs_dqvopchown_t)(
struct xfs_dquot **, struct xfs_dquot *);
typedef int (*xfs_dqvopchownresv_t)(struct xfs_trans *, struct xfs_inode *,
struct xfs_dquot *, struct xfs_dquot *, uint);
-typedef void (*xfs_dqstatvfs_t)(struct xfs_inode *, bhv_statvfs_t *);
+typedef void (*xfs_dqstatvfs_t)(struct xfs_inode *, struct kstatfs *);
typedef int (*xfs_dqsync_t)(struct xfs_mount *, int flags);
typedef int (*xfs_quotactl_t)(struct xfs_mount *, int, int, xfs_caddr_t);
@@ -223,18 +225,10 @@ extern void xfs_icsb_sync_counters_locked(struct xfs_mount *, int);
#define xfs_icsb_sync_counters_locked(mp, flags) do { } while (0)
#endif
-typedef struct xfs_ail {
- struct list_head xa_ail;
- uint xa_gen;
- struct task_struct *xa_task;
- xfs_lsn_t xa_target;
-} xfs_ail_t;
-
typedef struct xfs_mount {
struct super_block *m_super;
xfs_tid_t m_tid; /* next unused tid for fs */
- spinlock_t m_ail_lock; /* fs AIL mutex */
- xfs_ail_t m_ail; /* fs active log item list */
+ struct xfs_ail *m_ail; /* fs active log item list */
xfs_sb_t m_sb; /* copy of fs superblock */
spinlock_t m_sb_lock; /* sb counter lock */
struct xfs_buf *m_sb_bp; /* buffer for superblock */
@@ -247,10 +241,6 @@ typedef struct xfs_mount {
xfs_agnumber_t m_agirotor; /* last ag dir inode alloced */
spinlock_t m_agirotor_lock;/* .. and lock protecting it */
xfs_agnumber_t m_maxagi; /* highest inode alloc group */
- struct xfs_inode *m_inodes; /* active inode list */
- struct list_head m_del_inodes; /* inodes to reclaim */
- mutex_t m_ilock; /* inode list mutex */
- uint m_ireclaims; /* count of calls to reclaim*/
uint m_readio_log; /* min read size log bytes */
uint m_readio_blocks; /* min read size blocks */
uint m_writeio_log; /* min write size log bytes */
@@ -267,7 +257,6 @@ typedef struct xfs_mount {
xfs_buftarg_t *m_ddev_targp; /* saves taking the address */
xfs_buftarg_t *m_logdev_targp;/* ptr to log device */
xfs_buftarg_t *m_rtdev_targp; /* ptr to rt device */
- __uint8_t m_dircook_elog; /* log d-cookie entry bits */
__uint8_t m_blkbit_log; /* blocklog + NBBY */
__uint8_t m_blkbb_log; /* blocklog - BBSHIFT */
__uint8_t m_agno_log; /* log #ag's */
@@ -276,12 +265,12 @@ typedef struct xfs_mount {
uint m_blockmask; /* sb_blocksize-1 */
uint m_blockwsize; /* sb_blocksize in words */
uint m_blockwmask; /* blockwsize-1 */
- uint m_alloc_mxr[2]; /* XFS_ALLOC_BLOCK_MAXRECS */
- uint m_alloc_mnr[2]; /* XFS_ALLOC_BLOCK_MINRECS */
- uint m_bmap_dmxr[2]; /* XFS_BMAP_BLOCK_DMAXRECS */
- uint m_bmap_dmnr[2]; /* XFS_BMAP_BLOCK_DMINRECS */
- uint m_inobt_mxr[2]; /* XFS_INOBT_BLOCK_MAXRECS */
- uint m_inobt_mnr[2]; /* XFS_INOBT_BLOCK_MINRECS */
+ uint m_alloc_mxr[2]; /* max alloc btree records */
+ uint m_alloc_mnr[2]; /* min alloc btree records */
+ uint m_bmap_dmxr[2]; /* max bmap btree records */
+ uint m_bmap_dmnr[2]; /* min bmap btree records */
+ uint m_inobt_mxr[2]; /* max inobt btree records */
+ uint m_inobt_mnr[2]; /* min inobt btree records */
uint m_ag_maxlevels; /* XFS_AG_MAXLEVELS */
uint m_bm_maxlevels[2]; /* XFS_BM_MAXLEVELS */
uint m_in_maxlevels; /* XFS_IN_MAXLEVELS */
@@ -312,9 +301,6 @@ typedef struct xfs_mount {
int m_sinoalign; /* stripe unit inode alignment */
int m_attr_magicpct;/* 37% of the blocksize */
int m_dir_magicpct; /* 37% of the dir blocksize */
- __uint8_t m_mk_sharedro; /* mark shared ro on unmount */
- __uint8_t m_inode_quiesce;/* call quiesce on new inodes.
- field governed by m_ilock */
__uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
int m_dirblksize; /* directory block sz--bytes */
@@ -362,7 +348,6 @@ typedef struct xfs_mount {
#define XFS_MOUNT_ATTR2 (1ULL << 8) /* allow use of attr2 format */
#define XFS_MOUNT_GRPID (1ULL << 9) /* group-ID assigned from directory */
#define XFS_MOUNT_NORECOVERY (1ULL << 10) /* no recovery - dirty fs */
-#define XFS_MOUNT_SHARED (1ULL << 11) /* shared mount */
#define XFS_MOUNT_DFLT_IOSIZE (1ULL << 12) /* set default i/o size */
#define XFS_MOUNT_OSYNCISOSYNC (1ULL << 13) /* o_sync is REALLY o_sync */
/* osyncisdsync is now default*/
@@ -439,6 +424,16 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
#define xfs_force_shutdown(m,f) \
xfs_do_force_shutdown(m, f, __FILE__, __LINE__)
+#define SHUTDOWN_META_IO_ERROR 0x0001 /* write attempt to metadata failed */
+#define SHUTDOWN_LOG_IO_ERROR 0x0002 /* write attempt to the log failed */
+#define SHUTDOWN_FORCE_UMOUNT 0x0004 /* shutdown from a forced unmount */
+#define SHUTDOWN_CORRUPT_INCORE 0x0008 /* corrupt in-memory data structures */
+#define SHUTDOWN_REMOTE_REQ 0x0010 /* shutdown came from remote cell */
+#define SHUTDOWN_DEVICE_REQ 0x0020 /* failed all paths to the device */
+
+#define xfs_test_for_freeze(mp) ((mp)->m_super->s_frozen)
+#define xfs_wait_for_freeze(mp,l) vfs_check_frozen((mp)->m_super, (l))
+
/*
* Flags for xfs_mountfs
*/
@@ -508,14 +503,12 @@ typedef struct xfs_mod_sb {
#define XFS_MOUNT_ILOCK(mp) mutex_lock(&((mp)->m_ilock))
#define XFS_MOUNT_IUNLOCK(mp) mutex_unlock(&((mp)->m_ilock))
-extern void xfs_mod_sb(xfs_trans_t *, __int64_t);
extern int xfs_log_sbcount(xfs_mount_t *, uint);
extern int xfs_mountfs(xfs_mount_t *mp);
extern void xfs_mountfs_check_barriers(xfs_mount_t *mp);
extern void xfs_unmountfs(xfs_mount_t *);
extern int xfs_unmountfs_writesb(xfs_mount_t *);
-extern int xfs_unmount_flush(xfs_mount_t *, int);
extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int64_t, int);
extern int xfs_mod_incore_sb_unlocked(xfs_mount_t *, xfs_sb_field_t,
int64_t, int);
@@ -525,20 +518,20 @@ extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int);
extern int xfs_readsb(xfs_mount_t *, int);
extern void xfs_freesb(xfs_mount_t *);
extern int xfs_fs_writable(xfs_mount_t *);
-extern int xfs_syncsub(xfs_mount_t *, int, int *);
-extern int xfs_sync_inodes(xfs_mount_t *, int, int *);
-extern xfs_agnumber_t xfs_initialize_perag(xfs_mount_t *, xfs_agnumber_t);
-extern void xfs_sb_from_disk(struct xfs_sb *, struct xfs_dsb *);
-extern void xfs_sb_to_disk(struct xfs_dsb *, struct xfs_sb *, __int64_t);
extern int xfs_sb_validate_fsb_count(struct xfs_sb *, __uint64_t);
-extern int xfs_dmops_get(struct xfs_mount *, struct xfs_mount_args *);
+extern int xfs_dmops_get(struct xfs_mount *);
extern void xfs_dmops_put(struct xfs_mount *);
-extern int xfs_qmops_get(struct xfs_mount *, struct xfs_mount_args *);
+extern int xfs_qmops_get(struct xfs_mount *);
extern void xfs_qmops_put(struct xfs_mount *);
extern struct xfs_dmops xfs_dmcore_xfs;
#endif /* __KERNEL__ */
+extern void xfs_mod_sb(struct xfs_trans *, __int64_t);
+extern xfs_agnumber_t xfs_initialize_perag(struct xfs_mount *, xfs_agnumber_t);
+extern void xfs_sb_from_disk(struct xfs_sb *, struct xfs_dsb *);
+extern void xfs_sb_to_disk(struct xfs_dsb *, struct xfs_sb *, __int64_t);
+
#endif /* __XFS_MOUNT_H__ */
diff --git a/fs/xfs/xfs_qmops.c b/fs/xfs/xfs_qmops.c
index a294e58db8dd..27f80581520a 100644
--- a/fs/xfs/xfs_qmops.c
+++ b/fs/xfs/xfs_qmops.c
@@ -28,7 +28,6 @@
#include "xfs_mount.h"
#include "xfs_quota.h"
#include "xfs_error.h"
-#include "xfs_clnt.h"
STATIC struct xfs_dquot *
@@ -131,9 +130,9 @@ static struct xfs_qmops xfs_qmcore_stub = {
};
int
-xfs_qmops_get(struct xfs_mount *mp, struct xfs_mount_args *args)
+xfs_qmops_get(struct xfs_mount *mp)
{
- if (args->flags & (XFSMNT_UQUOTA | XFSMNT_PQUOTA | XFSMNT_GQUOTA)) {
+ if (XFS_IS_QUOTA_RUNNING(mp)) {
#ifdef CONFIG_XFS_QUOTA
mp->m_qm_ops = &xfs_qmcore_xfs;
#else
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index 12c4ec775af8..48965ecaa155 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -84,11 +84,9 @@ typedef struct xfs_dqblk {
#define XFS_DQ_USER 0x0001 /* a user quota */
#define XFS_DQ_PROJ 0x0002 /* project quota */
#define XFS_DQ_GROUP 0x0004 /* a group quota */
-#define XFS_DQ_FLOCKED 0x0008 /* flush lock taken */
-#define XFS_DQ_DIRTY 0x0010 /* dquot is dirty */
-#define XFS_DQ_WANT 0x0020 /* for lookup/reclaim race */
-#define XFS_DQ_INACTIVE 0x0040 /* dq off mplist & hashlist */
-#define XFS_DQ_MARKER 0x0080 /* sentinel */
+#define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */
+#define XFS_DQ_WANT 0x0010 /* for lookup/reclaim race */
+#define XFS_DQ_INACTIVE 0x0020 /* dq off mplist & hashlist */
#define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP)
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c
index c903130be7fd..86471bb40fd4 100644
--- a/fs/xfs/xfs_rename.c
+++ b/fs/xfs/xfs_rename.c
@@ -42,31 +42,6 @@
/*
- * Given an array of up to 4 inode pointers, unlock the pointed to inodes.
- * If there are fewer than 4 entries in the array, the empty entries will
- * be at the end and will have NULL pointers in them.
- */
-STATIC void
-xfs_rename_unlock4(
- xfs_inode_t **i_tab,
- uint lock_mode)
-{
- int i;
-
- xfs_iunlock(i_tab[0], lock_mode);
- for (i = 1; i < 4; i++) {
- if (i_tab[i] == NULL)
- break;
-
- /*
- * Watch out for duplicate entries in the table.
- */
- if (i_tab[i] != i_tab[i-1])
- xfs_iunlock(i_tab[i], lock_mode);
- }
-}
-
-/*
* Enter all inodes for a rename transaction into a sorted array.
*/
STATIC void
@@ -205,19 +180,6 @@ xfs_rename(
xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);
/*
- * If we are using project inheritance, we only allow renames
- * into our tree when the project IDs are the same; else the
- * tree quota mechanism would be circumvented.
- */
- if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
- (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) {
- error = XFS_ERROR(EXDEV);
- xfs_rename_unlock4(inodes, XFS_ILOCK_EXCL);
- xfs_trans_cancel(tp, cancel_flags);
- goto std_return;
- }
-
- /*
* Join all the inodes to the transaction. From this point on,
* we can rely on either trans_commit or trans_cancel to unlock
* them. Note that we need to add a vnode reference to the
@@ -242,6 +204,17 @@ xfs_rename(
}
/*
+ * If we are using project inheritance, we only allow renames
+ * into our tree when the project IDs are the same; else the
+ * tree quota mechanism would be circumvented.
+ */
+ if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
+ (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) {
+ error = XFS_ERROR(EXDEV);
+ goto error_return;
+ }
+
+ /*
* Set up the target.
*/
if (target_ip == NULL) {
@@ -367,19 +340,11 @@ xfs_rename(
&first_block, &free_list, spaceres);
if (error)
goto abort_return;
- xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- /*
- * Update the generation counts on all the directory inodes
- * that we're modifying.
- */
- src_dp->i_gen++;
+ xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
-
- if (new_parent) {
- target_dp->i_gen++;
+ if (new_parent)
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
- }
/*
* If this is a synchronous mount, make sure that the
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index e2f68de16159..edf12c7b834c 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -85,7 +85,6 @@ xfs_growfs_rt_alloc(
{
xfs_fileoff_t bno; /* block number in file */
xfs_buf_t *bp; /* temporary buffer for zeroing */
- int cancelflags; /* flags for xfs_trans_cancel */
int committed; /* transaction committed flag */
xfs_daddr_t d; /* disk block address */
int error; /* error return value */
@@ -96,15 +95,16 @@ xfs_growfs_rt_alloc(
xfs_bmbt_irec_t map; /* block map output */
int nmap; /* number of block maps */
int resblks; /* space reservation */
- xfs_trans_t *tp; /* transaction pointer */
/*
* Allocate space to the file, as necessary.
*/
while (oblocks < nblocks) {
+ int cancelflags = 0;
+ xfs_trans_t *tp;
+
tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ALLOC);
resblks = XFS_GROWFSRT_SPACE_RES(mp, nblocks - oblocks);
- cancelflags = 0;
/*
* Reserve space & log for one extent added to the file.
*/
@@ -171,7 +171,9 @@ xfs_growfs_rt_alloc(
mp->m_bsize, 0);
if (bp == NULL) {
error = XFS_ERROR(EIO);
- goto error_cancel;
+error_cancel:
+ xfs_trans_cancel(tp, cancelflags);
+ goto error;
}
memset(XFS_BUF_PTR(bp), 0, mp->m_sb.sb_blocksize);
xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
@@ -188,8 +190,6 @@ xfs_growfs_rt_alloc(
oblocks = map.br_startoff + map.br_blockcount;
}
return 0;
-error_cancel:
- xfs_trans_cancel(tp, cancelflags);
error:
return error;
}
@@ -1856,7 +1856,6 @@ xfs_growfs_rt(
{
xfs_rtblock_t bmbno; /* bitmap block number */
xfs_buf_t *bp; /* temporary buffer */
- int cancelflags; /* flags for xfs_trans_cancel */
int error; /* error return value */
xfs_inode_t *ip; /* bitmap inode, used as lock */
xfs_mount_t *nmp; /* new (fake) mount structure */
@@ -1872,13 +1871,13 @@ xfs_growfs_rt(
xfs_extlen_t rsumblocks; /* current number of rt summary blks */
xfs_sb_t *sbp; /* old superblock */
xfs_fsblock_t sumbno; /* summary block number */
- xfs_trans_t *tp; /* transaction pointer */
sbp = &mp->m_sb;
- cancelflags = 0;
/*
* Initial error checking.
*/
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
if (mp->m_rtdev_targp == NULL || mp->m_rbmip == NULL ||
(nrblocks = in->newblocks) <= sbp->sb_rblocks ||
(sbp->sb_rblocks && (in->extsize != sbp->sb_rextsize)))
@@ -1942,6 +1941,9 @@ xfs_growfs_rt(
((sbp->sb_rextents & ((1 << mp->m_blkbit_log) - 1)) != 0);
bmbno < nrbmblocks;
bmbno++) {
+ xfs_trans_t *tp;
+ int cancelflags = 0;
+
*nmp = *mp;
nsbp = &nmp->m_sb;
/*
@@ -1967,16 +1969,15 @@ xfs_growfs_rt(
* Start a transaction, get the log reservation.
*/
tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_FREE);
- cancelflags = 0;
if ((error = xfs_trans_reserve(tp, 0,
XFS_GROWRTFREE_LOG_RES(nmp), 0, 0, 0)))
- break;
+ goto error_cancel;
/*
* Lock out other callers by grabbing the bitmap inode lock.
*/
if ((error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0,
XFS_ILOCK_EXCL, &ip)))
- break;
+ goto error_cancel;
ASSERT(ip == mp->m_rbmip);
/*
* Update the bitmap inode's size.
@@ -1990,7 +1991,7 @@ xfs_growfs_rt(
*/
if ((error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0,
XFS_ILOCK_EXCL, &ip)))
- break;
+ goto error_cancel;
ASSERT(ip == mp->m_rsumip);
/*
* Update the summary inode's size.
@@ -2005,7 +2006,7 @@ xfs_growfs_rt(
mp->m_rsumlevels != nmp->m_rsumlevels) {
error = xfs_rtcopy_summary(mp, nmp, tp);
if (error)
- break;
+ goto error_cancel;
}
/*
* Update superblock fields.
@@ -2031,8 +2032,11 @@ xfs_growfs_rt(
bp = NULL;
error = xfs_rtfree_range(nmp, tp, sbp->sb_rextents,
nsbp->sb_rextents - sbp->sb_rextents, &bp, &sumbno);
- if (error)
+ if (error) {
+error_cancel:
+ xfs_trans_cancel(tp, cancelflags);
break;
+ }
/*
* Mark more blocks free in the superblock.
*/
@@ -2045,15 +2049,10 @@ xfs_growfs_rt(
mp->m_rsumsize = nrsumsize;
error = xfs_trans_commit(tp, 0);
- if (error) {
- tp = NULL;
+ if (error)
break;
- }
}
- if (error && tp)
- xfs_trans_cancel(tp, cancelflags);
-
/*
* Free the fake mp structure.
*/
diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c
index 3a82576dde9a..36f3a21c54d2 100644
--- a/fs/xfs/xfs_rw.c
+++ b/fs/xfs/xfs_rw.c
@@ -406,7 +406,7 @@ xfs_bwrite(
* XXXsup how does this work for quotas.
*/
XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb);
- XFS_BUF_SET_FSPRIVATE3(bp, mp);
+ bp->b_mount = mp;
XFS_BUF_WRITE(bp);
if ((error = XFS_bwrite(bp))) {
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
index 3f8cf1587f4c..1ed71916e4c9 100644
--- a/fs/xfs/xfs_sb.h
+++ b/fs/xfs/xfs_sb.h
@@ -79,6 +79,7 @@ struct xfs_mount;
#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */
#define XFS_SB_VERSION2_RESERVED4BIT 0x00000004
#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */
+#define XFS_SB_VERSION2_PARENTBIT 0x00000010 /* parent pointers */
#define XFS_SB_VERSION2_OKREALFBITS \
(XFS_SB_VERSION2_LAZYSBCOUNTBIT | \
@@ -296,30 +297,34 @@ typedef enum {
#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS)
-#ifdef __KERNEL__
static inline int xfs_sb_good_version(xfs_sb_t *sbp)
{
- return (((sbp->sb_versionnum >= XFS_SB_VERSION_1) && \
- (sbp->sb_versionnum <= XFS_SB_VERSION_3)) || \
- ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- !((sbp->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) || \
- ((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) && \
- (sbp->sb_features2 & ~XFS_SB_VERSION2_OKREALBITS))) && \
- (sbp->sb_shared_vn <= XFS_SB_MAX_SHARED_VN)));
-}
+ /* We always support version 1-3 */
+ if (sbp->sb_versionnum >= XFS_SB_VERSION_1 &&
+ sbp->sb_versionnum <= XFS_SB_VERSION_3)
+ return 1;
+
+ /* We support version 4 if all feature bits are supported */
+ if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) {
+ if ((sbp->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) ||
+ ((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) &&
+ (sbp->sb_features2 & ~XFS_SB_VERSION2_OKREALBITS)))
+ return 0;
+
+#ifdef __KERNEL__
+ if (sbp->sb_shared_vn > XFS_SB_MAX_SHARED_VN)
+ return 0;
#else
-static inline int xfs_sb_good_version(xfs_sb_t *sbp)
-{
- return (((sbp->sb_versionnum >= XFS_SB_VERSION_1) && \
- (sbp->sb_versionnum <= XFS_SB_VERSION_3)) || \
- ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- !((sbp->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) || \
- ((sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) && \
- (sbp->sb_features2 & ~XFS_SB_VERSION2_OKREALBITS))) && \
- (!(sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) || \
- (sbp->sb_shared_vn <= XFS_SB_MAX_SHARED_VN))));
+ if ((sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) &&
+ sbp->sb_shared_vn > XFS_SB_MAX_SHARED_VN)
+ return 0;
+#endif
+
+ return 1;
+ }
+
+ return 0;
}
-#endif /* __KERNEL__ */
/*
* Detect a mismatched features2 field. Older kernels read/wrote
@@ -332,123 +337,127 @@ static inline int xfs_sb_has_mismatched_features2(xfs_sb_t *sbp)
static inline unsigned xfs_sb_version_tonew(unsigned v)
{
- return ((((v) == XFS_SB_VERSION_1) ? \
- 0 : \
- (((v) == XFS_SB_VERSION_2) ? \
- XFS_SB_VERSION_ATTRBIT : \
- (XFS_SB_VERSION_ATTRBIT | XFS_SB_VERSION_NLINKBIT))) | \
- XFS_SB_VERSION_4);
+ if (v == XFS_SB_VERSION_1)
+ return XFS_SB_VERSION_4;
+
+ if (v == XFS_SB_VERSION_2)
+ return XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT;
+
+ return XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT |
+ XFS_SB_VERSION_NLINKBIT;
}
static inline unsigned xfs_sb_version_toold(unsigned v)
{
- return (((v) & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT)) ? \
- 0 : \
- (((v) & XFS_SB_VERSION_NLINKBIT) ? \
- XFS_SB_VERSION_3 : \
- (((v) & XFS_SB_VERSION_ATTRBIT) ? \
- XFS_SB_VERSION_2 : \
- XFS_SB_VERSION_1)));
+ if (v & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT))
+ return 0;
+ if (v & XFS_SB_VERSION_NLINKBIT)
+ return XFS_SB_VERSION_3;
+ if (v & XFS_SB_VERSION_ATTRBIT)
+ return XFS_SB_VERSION_2;
+ return XFS_SB_VERSION_1;
}
static inline int xfs_sb_version_hasattr(xfs_sb_t *sbp)
{
- return ((sbp)->sb_versionnum == XFS_SB_VERSION_2) || \
- ((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \
- ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_ATTRBIT));
+ return sbp->sb_versionnum == XFS_SB_VERSION_2 ||
+ sbp->sb_versionnum == XFS_SB_VERSION_3 ||
+ (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_ATTRBIT));
}
static inline void xfs_sb_version_addattr(xfs_sb_t *sbp)
{
- (sbp)->sb_versionnum = (((sbp)->sb_versionnum == XFS_SB_VERSION_1) ? \
- XFS_SB_VERSION_2 : \
- ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) ? \
- ((sbp)->sb_versionnum | XFS_SB_VERSION_ATTRBIT) : \
- (XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT)));
+ if (sbp->sb_versionnum == XFS_SB_VERSION_1)
+ sbp->sb_versionnum = XFS_SB_VERSION_2;
+ else if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
+ sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+ else
+ sbp->sb_versionnum = XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT;
}
static inline int xfs_sb_version_hasnlink(xfs_sb_t *sbp)
{
- return ((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \
- ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_NLINKBIT));
+ return sbp->sb_versionnum == XFS_SB_VERSION_3 ||
+ (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_NLINKBIT));
}
static inline void xfs_sb_version_addnlink(xfs_sb_t *sbp)
{
- (sbp)->sb_versionnum = ((sbp)->sb_versionnum <= XFS_SB_VERSION_2 ? \
- XFS_SB_VERSION_3 : \
- ((sbp)->sb_versionnum | XFS_SB_VERSION_NLINKBIT));
+ if (sbp->sb_versionnum <= XFS_SB_VERSION_2)
+ sbp->sb_versionnum = XFS_SB_VERSION_3;
+ else
+ sbp->sb_versionnum |= XFS_SB_VERSION_NLINKBIT;
}
static inline int xfs_sb_version_hasquota(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_QUOTABIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_QUOTABIT);
}
static inline void xfs_sb_version_addquota(xfs_sb_t *sbp)
{
- (sbp)->sb_versionnum = \
- (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 ? \
- ((sbp)->sb_versionnum | XFS_SB_VERSION_QUOTABIT) : \
- (xfs_sb_version_tonew((sbp)->sb_versionnum) | \
- XFS_SB_VERSION_QUOTABIT));
+ if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4)
+ sbp->sb_versionnum |= XFS_SB_VERSION_QUOTABIT;
+ else
+ sbp->sb_versionnum = xfs_sb_version_tonew(sbp->sb_versionnum) |
+ XFS_SB_VERSION_QUOTABIT;
}
static inline int xfs_sb_version_hasalign(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_ALIGNBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_ALIGNBIT);
}
static inline int xfs_sb_version_hasdalign(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_DALIGNBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_DALIGNBIT);
}
static inline int xfs_sb_version_hasshared(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_SHAREDBIT);
}
static inline int xfs_sb_version_hasdirv2(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
}
static inline int xfs_sb_version_haslogv2(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_LOGV2BIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_LOGV2BIT);
}
static inline int xfs_sb_version_hasextflgbit(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT);
}
static inline int xfs_sb_version_hassector(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
}
static inline int xfs_sb_version_hasasciici(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
(sbp->sb_versionnum & XFS_SB_VERSION_BORGBIT);
}
static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
{
- return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
- ((sbp)->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT);
+ return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 &&
+ (sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT);
}
/*
@@ -463,22 +472,20 @@ static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
static inline int xfs_sb_version_haslazysbcount(xfs_sb_t *sbp)
{
- return (xfs_sb_version_hasmorebits(sbp) && \
- ((sbp)->sb_features2 & XFS_SB_VERSION2_LAZYSBCOUNTBIT));
+ return xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_LAZYSBCOUNTBIT);
}
static inline int xfs_sb_version_hasattr2(xfs_sb_t *sbp)
{
- return (xfs_sb_version_hasmorebits(sbp)) && \
- ((sbp)->sb_features2 & XFS_SB_VERSION2_ATTR2BIT);
+ return xfs_sb_version_hasmorebits(sbp) &&
+ (sbp->sb_features2 & XFS_SB_VERSION2_ATTR2BIT);
}
static inline void xfs_sb_version_addattr2(xfs_sb_t *sbp)
{
- ((sbp)->sb_versionnum = \
- ((sbp)->sb_versionnum | XFS_SB_VERSION_MOREBITSBIT), \
- ((sbp)->sb_features2 = \
- ((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
+ sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
+ sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
}
static inline void xfs_sb_version_removeattr2(xfs_sb_t *sbp)
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 4e1c22a23be5..8570b826fedd 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -290,7 +290,7 @@ xfs_trans_dup(
ASSERT(tp->t_ticket != NULL);
ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE);
- ntp->t_ticket = tp->t_ticket;
+ ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
tp->t_blk_res = tp->t_blk_res_used;
ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
@@ -1260,6 +1260,13 @@ xfs_trans_roll(
trans = *tpp;
/*
+ * transaction commit worked ok so we can drop the extra ticket
+ * reference that we gained in xfs_trans_dup()
+ */
+ xfs_log_ticket_put(trans->t_ticket);
+
+
+ /*
* Reserve space in the log for th next transaction.
* This also pushes items in the "AIL", the list of logged items,
* out to disk if they are taking up space at the tail of the log
@@ -1383,11 +1390,12 @@ xfs_trans_chunk_committed(
xfs_log_item_desc_t *lidp;
xfs_log_item_t *lip;
xfs_lsn_t item_lsn;
- struct xfs_mount *mp;
int i;
lidp = licp->lic_descs;
for (i = 0; i < licp->lic_unused; i++, lidp++) {
+ struct xfs_ail *ailp;
+
if (xfs_lic_isfree(licp, i)) {
continue;
}
@@ -1424,19 +1432,19 @@ xfs_trans_chunk_committed(
* This would cause the earlier transaction to fail
* the test below.
*/
- mp = lip->li_mountp;
- spin_lock(&mp->m_ail_lock);
+ ailp = lip->li_ailp;
+ spin_lock(&ailp->xa_lock);
if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) {
/*
* This will set the item's lsn to item_lsn
* and update the position of the item in
* the AIL.
*
- * xfs_trans_update_ail() drops the AIL lock.
+ * xfs_trans_ail_update() drops the AIL lock.
*/
- xfs_trans_update_ail(mp, lip, item_lsn);
+ xfs_trans_ail_update(ailp, lip, item_lsn);
} else {
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
/*
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index 74c80bd2b0ec..d6fe4a88d79f 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -18,6 +18,8 @@
#ifndef __XFS_TRANS_H__
#define __XFS_TRANS_H__
+struct xfs_log_item;
+
/*
* This is the structure written in the log at the head of
* every transaction. It identifies the type and id of the
@@ -98,76 +100,6 @@ typedef struct xfs_trans_header {
#define XFS_TRANS_TYPE_MAX 41
/* new transaction types need to be reflected in xfs_logprint(8) */
-
-#ifdef __KERNEL__
-struct xfs_buf;
-struct xfs_buftarg;
-struct xfs_efd_log_item;
-struct xfs_efi_log_item;
-struct xfs_inode;
-struct xfs_item_ops;
-struct xfs_log_iovec;
-struct xfs_log_item;
-struct xfs_log_item_desc;
-struct xfs_mount;
-struct xfs_trans;
-struct xfs_dquot_acct;
-
-typedef struct xfs_log_item {
- struct list_head li_ail; /* AIL pointers */
- xfs_lsn_t li_lsn; /* last on-disk lsn */
- struct xfs_log_item_desc *li_desc; /* ptr to current desc*/
- struct xfs_mount *li_mountp; /* ptr to fs mount */
- uint li_type; /* item type */
- uint li_flags; /* misc flags */
- struct xfs_log_item *li_bio_list; /* buffer item list */
- void (*li_cb)(struct xfs_buf *,
- struct xfs_log_item *);
- /* buffer item iodone */
- /* callback func */
- struct xfs_item_ops *li_ops; /* function list */
-} xfs_log_item_t;
-
-#define XFS_LI_IN_AIL 0x1
-#define XFS_LI_ABORTED 0x2
-
-typedef struct xfs_item_ops {
- uint (*iop_size)(xfs_log_item_t *);
- void (*iop_format)(xfs_log_item_t *, struct xfs_log_iovec *);
- void (*iop_pin)(xfs_log_item_t *);
- void (*iop_unpin)(xfs_log_item_t *, int);
- void (*iop_unpin_remove)(xfs_log_item_t *, struct xfs_trans *);
- uint (*iop_trylock)(xfs_log_item_t *);
- void (*iop_unlock)(xfs_log_item_t *);
- xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
- void (*iop_push)(xfs_log_item_t *);
- void (*iop_pushbuf)(xfs_log_item_t *);
- void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
-} xfs_item_ops_t;
-
-#define IOP_SIZE(ip) (*(ip)->li_ops->iop_size)(ip)
-#define IOP_FORMAT(ip,vp) (*(ip)->li_ops->iop_format)(ip, vp)
-#define IOP_PIN(ip) (*(ip)->li_ops->iop_pin)(ip)
-#define IOP_UNPIN(ip, flags) (*(ip)->li_ops->iop_unpin)(ip, flags)
-#define IOP_UNPIN_REMOVE(ip,tp) (*(ip)->li_ops->iop_unpin_remove)(ip, tp)
-#define IOP_TRYLOCK(ip) (*(ip)->li_ops->iop_trylock)(ip)
-#define IOP_UNLOCK(ip) (*(ip)->li_ops->iop_unlock)(ip)
-#define IOP_COMMITTED(ip, lsn) (*(ip)->li_ops->iop_committed)(ip, lsn)
-#define IOP_PUSH(ip) (*(ip)->li_ops->iop_push)(ip)
-#define IOP_PUSHBUF(ip) (*(ip)->li_ops->iop_pushbuf)(ip)
-#define IOP_COMMITTING(ip, lsn) (*(ip)->li_ops->iop_committing)(ip, lsn)
-
-/*
- * Return values for the IOP_TRYLOCK() routines.
- */
-#define XFS_ITEM_SUCCESS 0
-#define XFS_ITEM_PINNED 1
-#define XFS_ITEM_LOCKED 2
-#define XFS_ITEM_FLUSHING 3
-#define XFS_ITEM_PUSHBUF 4
-
-#endif /* __KERNEL__ */
-
/*
* This structure is used to track log items associated with
* a transaction. It points to the log item and keeps some
@@ -176,7 +108,7 @@ typedef struct xfs_item_ops {
* once we get to commit processing (see xfs_trans_commit()).
*/
typedef struct xfs_log_item_desc {
- xfs_log_item_t *lid_item;
+ struct xfs_log_item *lid_item;
ushort lid_size;
unsigned char lid_flags;
unsigned char lid_index;
@@ -276,94 +208,6 @@ xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp)
(xfs_caddr_t)(((xfs_log_item_chunk_t*)0)->lic_descs));
}
-#ifdef __KERNEL__
-/*
- * This structure is used to maintain a list of block ranges that have been
- * freed in the transaction. The ranges are listed in the perag[] busy list
- * between when they're freed and the transaction is committed to disk.
- */
-
-typedef struct xfs_log_busy_slot {
- xfs_agnumber_t lbc_ag;
- ushort lbc_idx; /* index in perag.busy[] */
-} xfs_log_busy_slot_t;
-
-#define XFS_LBC_NUM_SLOTS 31
-typedef struct xfs_log_busy_chunk {
- struct xfs_log_busy_chunk *lbc_next;
- uint lbc_free; /* free slots bitmask */
- ushort lbc_unused; /* first unused */
- xfs_log_busy_slot_t lbc_busy[XFS_LBC_NUM_SLOTS];
-} xfs_log_busy_chunk_t;
-
-#define XFS_LBC_MAX_SLOT (XFS_LBC_NUM_SLOTS - 1)
-#define XFS_LBC_FREEMASK ((1U << XFS_LBC_NUM_SLOTS) - 1)
-
-#define XFS_LBC_INIT(cp) ((cp)->lbc_free = XFS_LBC_FREEMASK)
-#define XFS_LBC_CLAIM(cp, slot) ((cp)->lbc_free &= ~(1 << (slot)))
-#define XFS_LBC_SLOT(cp, slot) (&((cp)->lbc_busy[(slot)]))
-#define XFS_LBC_VACANCY(cp) (((cp)->lbc_free) & XFS_LBC_FREEMASK)
-#define XFS_LBC_ISFREE(cp, slot) ((cp)->lbc_free & (1 << (slot)))
-
-/*
- * This is the type of function which can be given to xfs_trans_callback()
- * to be called upon the transaction's commit to disk.
- */
-typedef void (*xfs_trans_callback_t)(struct xfs_trans *, void *);
-
-/*
- * This is the structure maintained for every active transaction.
- */
-typedef struct xfs_trans {
- unsigned int t_magic; /* magic number */
- xfs_log_callback_t t_logcb; /* log callback struct */
- unsigned int t_type; /* transaction type */
- unsigned int t_log_res; /* amt of log space resvd */
- unsigned int t_log_count; /* count for perm log res */
- unsigned int t_blk_res; /* # of blocks resvd */
- unsigned int t_blk_res_used; /* # of resvd blocks used */
- unsigned int t_rtx_res; /* # of rt extents resvd */
- unsigned int t_rtx_res_used; /* # of resvd rt extents used */
- xfs_log_ticket_t t_ticket; /* log mgr ticket */
- xfs_lsn_t t_lsn; /* log seq num of start of
- * transaction. */
- xfs_lsn_t t_commit_lsn; /* log seq num of end of
- * transaction. */
- struct xfs_mount *t_mountp; /* ptr to fs mount struct */
- struct xfs_dquot_acct *t_dqinfo; /* acctg info for dquots */
- xfs_trans_callback_t t_callback; /* transaction callback */
- void *t_callarg; /* callback arg */
- unsigned int t_flags; /* misc flags */
- int64_t t_icount_delta; /* superblock icount change */
- int64_t t_ifree_delta; /* superblock ifree change */
- int64_t t_fdblocks_delta; /* superblock fdblocks chg */
- int64_t t_res_fdblocks_delta; /* on-disk only chg */
- int64_t t_frextents_delta;/* superblock freextents chg*/
- int64_t t_res_frextents_delta; /* on-disk only chg */
-#ifdef DEBUG
- int64_t t_ag_freeblks_delta; /* debugging counter */
- int64_t t_ag_flist_delta; /* debugging counter */
- int64_t t_ag_btree_delta; /* debugging counter */
-#endif
- int64_t t_dblocks_delta;/* superblock dblocks change */
- int64_t t_agcount_delta;/* superblock agcount change */
- int64_t t_imaxpct_delta;/* superblock imaxpct change */
- int64_t t_rextsize_delta;/* superblock rextsize chg */
- int64_t t_rbmblocks_delta;/* superblock rbmblocks chg */
- int64_t t_rblocks_delta;/* superblock rblocks change */
- int64_t t_rextents_delta;/* superblocks rextents chg */
- int64_t t_rextslog_delta;/* superblocks rextslog chg */
- unsigned int t_items_free; /* log item descs free */
- xfs_log_item_chunk_t t_items; /* first log item desc chunk */
- xfs_trans_header_t t_header; /* header for in-log trans */
- unsigned int t_busy_free; /* busy descs free */
- xfs_log_busy_chunk_t t_busy; /* busy/async free blocks */
- unsigned long t_pflags; /* saved process flags state */
-} xfs_trans_t;
-
-#endif /* __KERNEL__ */
-
-
#define XFS_TRANS_MAGIC 0x5452414E /* 'TRAN' */
/*
* Values for t_flags.
@@ -906,6 +750,157 @@ typedef struct xfs_trans {
#define XFS_DQUOT_REF 1
#ifdef __KERNEL__
+
+struct xfs_buf;
+struct xfs_buftarg;
+struct xfs_efd_log_item;
+struct xfs_efi_log_item;
+struct xfs_inode;
+struct xfs_item_ops;
+struct xfs_log_iovec;
+struct xfs_log_item_desc;
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_dquot_acct;
+
+typedef struct xfs_log_item {
+ struct list_head li_ail; /* AIL pointers */
+ xfs_lsn_t li_lsn; /* last on-disk lsn */
+ struct xfs_log_item_desc *li_desc; /* ptr to current desc*/
+ struct xfs_mount *li_mountp; /* ptr to fs mount */
+ struct xfs_ail *li_ailp; /* ptr to AIL */
+ uint li_type; /* item type */
+ uint li_flags; /* misc flags */
+ struct xfs_log_item *li_bio_list; /* buffer item list */
+ void (*li_cb)(struct xfs_buf *,
+ struct xfs_log_item *);
+ /* buffer item iodone */
+ /* callback func */
+ struct xfs_item_ops *li_ops; /* function list */
+} xfs_log_item_t;
+
+#define XFS_LI_IN_AIL 0x1
+#define XFS_LI_ABORTED 0x2
+
+typedef struct xfs_item_ops {
+ uint (*iop_size)(xfs_log_item_t *);
+ void (*iop_format)(xfs_log_item_t *, struct xfs_log_iovec *);
+ void (*iop_pin)(xfs_log_item_t *);
+ void (*iop_unpin)(xfs_log_item_t *, int);
+ void (*iop_unpin_remove)(xfs_log_item_t *, struct xfs_trans *);
+ uint (*iop_trylock)(xfs_log_item_t *);
+ void (*iop_unlock)(xfs_log_item_t *);
+ xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
+ void (*iop_push)(xfs_log_item_t *);
+ void (*iop_pushbuf)(xfs_log_item_t *);
+ void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
+} xfs_item_ops_t;
+
+#define IOP_SIZE(ip) (*(ip)->li_ops->iop_size)(ip)
+#define IOP_FORMAT(ip,vp) (*(ip)->li_ops->iop_format)(ip, vp)
+#define IOP_PIN(ip) (*(ip)->li_ops->iop_pin)(ip)
+#define IOP_UNPIN(ip, flags) (*(ip)->li_ops->iop_unpin)(ip, flags)
+#define IOP_UNPIN_REMOVE(ip,tp) (*(ip)->li_ops->iop_unpin_remove)(ip, tp)
+#define IOP_TRYLOCK(ip) (*(ip)->li_ops->iop_trylock)(ip)
+#define IOP_UNLOCK(ip) (*(ip)->li_ops->iop_unlock)(ip)
+#define IOP_COMMITTED(ip, lsn) (*(ip)->li_ops->iop_committed)(ip, lsn)
+#define IOP_PUSH(ip) (*(ip)->li_ops->iop_push)(ip)
+#define IOP_PUSHBUF(ip) (*(ip)->li_ops->iop_pushbuf)(ip)
+#define IOP_COMMITTING(ip, lsn) (*(ip)->li_ops->iop_committing)(ip, lsn)
+
+/*
+ * Return values for the IOP_TRYLOCK() routines.
+ */
+#define XFS_ITEM_SUCCESS 0
+#define XFS_ITEM_PINNED 1
+#define XFS_ITEM_LOCKED 2
+#define XFS_ITEM_FLUSHING 3
+#define XFS_ITEM_PUSHBUF 4
+
+/*
+ * This structure is used to maintain a list of block ranges that have been
+ * freed in the transaction. The ranges are listed in the perag[] busy list
+ * between when they're freed and the transaction is committed to disk.
+ */
+
+typedef struct xfs_log_busy_slot {
+ xfs_agnumber_t lbc_ag;
+ ushort lbc_idx; /* index in perag.busy[] */
+} xfs_log_busy_slot_t;
+
+#define XFS_LBC_NUM_SLOTS 31
+typedef struct xfs_log_busy_chunk {
+ struct xfs_log_busy_chunk *lbc_next;
+ uint lbc_free; /* free slots bitmask */
+ ushort lbc_unused; /* first unused */
+ xfs_log_busy_slot_t lbc_busy[XFS_LBC_NUM_SLOTS];
+} xfs_log_busy_chunk_t;
+
+#define XFS_LBC_MAX_SLOT (XFS_LBC_NUM_SLOTS - 1)
+#define XFS_LBC_FREEMASK ((1U << XFS_LBC_NUM_SLOTS) - 1)
+
+#define XFS_LBC_INIT(cp) ((cp)->lbc_free = XFS_LBC_FREEMASK)
+#define XFS_LBC_CLAIM(cp, slot) ((cp)->lbc_free &= ~(1 << (slot)))
+#define XFS_LBC_SLOT(cp, slot) (&((cp)->lbc_busy[(slot)]))
+#define XFS_LBC_VACANCY(cp) (((cp)->lbc_free) & XFS_LBC_FREEMASK)
+#define XFS_LBC_ISFREE(cp, slot) ((cp)->lbc_free & (1 << (slot)))
+
+/*
+ * This is the type of function which can be given to xfs_trans_callback()
+ * to be called upon the transaction's commit to disk.
+ */
+typedef void (*xfs_trans_callback_t)(struct xfs_trans *, void *);
+
+/*
+ * This is the structure maintained for every active transaction.
+ */
+typedef struct xfs_trans {
+ unsigned int t_magic; /* magic number */
+ xfs_log_callback_t t_logcb; /* log callback struct */
+ unsigned int t_type; /* transaction type */
+ unsigned int t_log_res; /* amt of log space resvd */
+ unsigned int t_log_count; /* count for perm log res */
+ unsigned int t_blk_res; /* # of blocks resvd */
+ unsigned int t_blk_res_used; /* # of resvd blocks used */
+ unsigned int t_rtx_res; /* # of rt extents resvd */
+ unsigned int t_rtx_res_used; /* # of resvd rt extents used */
+ xfs_log_ticket_t t_ticket; /* log mgr ticket */
+ xfs_lsn_t t_lsn; /* log seq num of start of
+ * transaction. */
+ xfs_lsn_t t_commit_lsn; /* log seq num of end of
+ * transaction. */
+ struct xfs_mount *t_mountp; /* ptr to fs mount struct */
+ struct xfs_dquot_acct *t_dqinfo; /* acctg info for dquots */
+ xfs_trans_callback_t t_callback; /* transaction callback */
+ void *t_callarg; /* callback arg */
+ unsigned int t_flags; /* misc flags */
+ int64_t t_icount_delta; /* superblock icount change */
+ int64_t t_ifree_delta; /* superblock ifree change */
+ int64_t t_fdblocks_delta; /* superblock fdblocks chg */
+ int64_t t_res_fdblocks_delta; /* on-disk only chg */
+ int64_t t_frextents_delta;/* superblock freextents chg*/
+ int64_t t_res_frextents_delta; /* on-disk only chg */
+#ifdef DEBUG
+ int64_t t_ag_freeblks_delta; /* debugging counter */
+ int64_t t_ag_flist_delta; /* debugging counter */
+ int64_t t_ag_btree_delta; /* debugging counter */
+#endif
+ int64_t t_dblocks_delta;/* superblock dblocks change */
+ int64_t t_agcount_delta;/* superblock agcount change */
+ int64_t t_imaxpct_delta;/* superblock imaxpct change */
+ int64_t t_rextsize_delta;/* superblock rextsize chg */
+ int64_t t_rbmblocks_delta;/* superblock rbmblocks chg */
+ int64_t t_rblocks_delta;/* superblock rblocks change */
+ int64_t t_rextents_delta;/* superblocks rextents chg */
+ int64_t t_rextslog_delta;/* superblocks rextslog chg */
+ unsigned int t_items_free; /* log item descs free */
+ xfs_log_item_chunk_t t_items; /* first log item desc chunk */
+ xfs_trans_header_t t_header; /* header for in-log trans */
+ unsigned int t_busy_free; /* busy descs free */
+ xfs_log_busy_chunk_t t_busy; /* busy/async free blocks */
+ unsigned long t_pflags; /* saved process flags state */
+} xfs_trans_t;
+
/*
* XFS transaction mechanism exported interfaces that are
* actually macros.
@@ -928,7 +923,6 @@ typedef struct xfs_trans {
/*
* XFS transaction mechanism exported interfaces.
*/
-void xfs_trans_init(struct xfs_mount *);
xfs_trans_t *xfs_trans_alloc(struct xfs_mount *, uint);
xfs_trans_t *_xfs_trans_alloc(struct xfs_mount *, uint);
xfs_trans_t *xfs_trans_dup(xfs_trans_t *);
@@ -975,13 +969,8 @@ int _xfs_trans_commit(xfs_trans_t *,
int *);
#define xfs_trans_commit(tp, flags) _xfs_trans_commit(tp, flags, NULL)
void xfs_trans_cancel(xfs_trans_t *, int);
-int xfs_trans_roll(struct xfs_trans **, struct xfs_inode *);
int xfs_trans_ail_init(struct xfs_mount *);
void xfs_trans_ail_destroy(struct xfs_mount *);
-void xfs_trans_push_ail(struct xfs_mount *, xfs_lsn_t);
-xfs_lsn_t xfs_trans_tail_ail(struct xfs_mount *);
-void xfs_trans_unlocked_item(struct xfs_mount *,
- xfs_log_item_t *);
xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
xfs_agnumber_t ag,
xfs_extlen_t idx);
@@ -990,4 +979,7 @@ extern kmem_zone_t *xfs_trans_zone;
#endif /* __KERNEL__ */
+void xfs_trans_init(struct xfs_mount *);
+int xfs_trans_roll(struct xfs_trans **, struct xfs_inode *);
+
#endif /* __XFS_TRANS_H__ */
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index 1f77c00af566..2d47f10f8bed 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * Copyright (c) 2008 Dave Chinner
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -28,13 +29,13 @@
#include "xfs_trans_priv.h"
#include "xfs_error.h"
-STATIC void xfs_ail_insert(xfs_ail_t *, xfs_log_item_t *);
-STATIC xfs_log_item_t * xfs_ail_delete(xfs_ail_t *, xfs_log_item_t *);
-STATIC xfs_log_item_t * xfs_ail_min(xfs_ail_t *);
-STATIC xfs_log_item_t * xfs_ail_next(xfs_ail_t *, xfs_log_item_t *);
+STATIC void xfs_ail_insert(struct xfs_ail *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_delete(struct xfs_ail *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_min(struct xfs_ail *);
+STATIC xfs_log_item_t * xfs_ail_next(struct xfs_ail *, xfs_log_item_t *);
#ifdef DEBUG
-STATIC void xfs_ail_check(xfs_ail_t *, xfs_log_item_t *);
+STATIC void xfs_ail_check(struct xfs_ail *, xfs_log_item_t *);
#else
#define xfs_ail_check(a,l)
#endif /* DEBUG */
@@ -50,20 +51,20 @@ STATIC void xfs_ail_check(xfs_ail_t *, xfs_log_item_t *);
* lsn of the last item in the AIL.
*/
xfs_lsn_t
-xfs_trans_tail_ail(
- xfs_mount_t *mp)
+xfs_trans_ail_tail(
+ struct xfs_ail *ailp)
{
xfs_lsn_t lsn;
xfs_log_item_t *lip;
- spin_lock(&mp->m_ail_lock);
- lip = xfs_ail_min(&mp->m_ail);
+ spin_lock(&ailp->xa_lock);
+ lip = xfs_ail_min(ailp);
if (lip == NULL) {
lsn = (xfs_lsn_t)0;
} else {
lsn = lip->li_lsn;
}
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
return lsn;
}
@@ -85,16 +86,125 @@ xfs_trans_tail_ail(
* any of the objects, so the lock is not needed.
*/
void
-xfs_trans_push_ail(
- xfs_mount_t *mp,
- xfs_lsn_t threshold_lsn)
+xfs_trans_ail_push(
+ struct xfs_ail *ailp,
+ xfs_lsn_t threshold_lsn)
{
- xfs_log_item_t *lip;
+ xfs_log_item_t *lip;
+
+ lip = xfs_ail_min(ailp);
+ if (lip && !XFS_FORCED_SHUTDOWN(ailp->xa_mount)) {
+ if (XFS_LSN_CMP(threshold_lsn, ailp->xa_target) > 0)
+ xfsaild_wakeup(ailp, threshold_lsn);
+ }
+}
+
+/*
+ * AIL traversal cursor initialisation.
+ *
+ * The cursor keeps track of where our current traversal is up
+ * to by tracking the next ƣtem in the list for us. However, for
+ * this to be safe, removing an object from the AIL needs to invalidate
+ * any cursor that points to it. hence the traversal cursor needs to
+ * be linked to the struct xfs_ail so that deletion can search all the
+ * active cursors for invalidation.
+ *
+ * We don't link the push cursor because it is embedded in the struct
+ * xfs_ail and hence easily findable.
+ */
+STATIC void
+xfs_trans_ail_cursor_init(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur)
+{
+ cur->item = NULL;
+ if (cur == &ailp->xa_cursors)
+ return;
+
+ cur->next = ailp->xa_cursors.next;
+ ailp->xa_cursors.next = cur;
+}
+
+/*
+ * Set the cursor to the next item, because when we look
+ * up the cursor the current item may have been freed.
+ */
+STATIC void
+xfs_trans_ail_cursor_set(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur,
+ struct xfs_log_item *lip)
+{
+ if (lip)
+ cur->item = xfs_ail_next(ailp, lip);
+}
+
+/*
+ * Get the next item in the traversal and advance the cursor.
+ * If the cursor was invalidated (inidicated by a lip of 1),
+ * restart the traversal.
+ */
+struct xfs_log_item *
+xfs_trans_ail_cursor_next(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur)
+{
+ struct xfs_log_item *lip = cur->item;
+
+ if ((__psint_t)lip & 1)
+ lip = xfs_ail_min(ailp);
+ xfs_trans_ail_cursor_set(ailp, cur, lip);
+ return lip;
+}
+
+/*
+ * Now that the traversal is complete, we need to remove the cursor
+ * from the list of traversing cursors. Avoid removing the embedded
+ * push cursor, but use the fact it is alway present to make the
+ * list deletion simple.
+ */
+void
+xfs_trans_ail_cursor_done(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *done)
+{
+ struct xfs_ail_cursor *prev = NULL;
+ struct xfs_ail_cursor *cur;
+
+ done->item = NULL;
+ if (done == &ailp->xa_cursors)
+ return;
+ prev = &ailp->xa_cursors;
+ for (cur = prev->next; cur; prev = cur, cur = prev->next) {
+ if (cur == done) {
+ prev->next = cur->next;
+ break;
+ }
+ }
+ ASSERT(cur);
+}
+
+/*
+ * Invalidate any cursor that is pointing to this item. This is
+ * called when an item is removed from the AIL. Any cursor pointing
+ * to this object is now invalid and the traversal needs to be
+ * terminated so it doesn't reference a freed object. We set the
+ * cursor item to a value of 1 so we can distinguish between an
+ * invalidation and the end of the list when getting the next item
+ * from the cursor.
+ */
+STATIC void
+xfs_trans_ail_cursor_clear(
+ struct xfs_ail *ailp,
+ struct xfs_log_item *lip)
+{
+ struct xfs_ail_cursor *cur;
- lip = xfs_ail_min(&mp->m_ail);
- if (lip && !XFS_FORCED_SHUTDOWN(mp)) {
- if (XFS_LSN_CMP(threshold_lsn, mp->m_ail.xa_target) > 0)
- xfsaild_wakeup(mp, threshold_lsn);
+ /* need to search all cursors */
+ for (cur = &ailp->xa_cursors; cur; cur = cur->next) {
+ if (cur->item == lip)
+ cur->item = (struct xfs_log_item *)
+ ((__psint_t)cur->item | 1);
}
}
@@ -103,25 +213,27 @@ xfs_trans_push_ail(
* Return the current tree generation number for use
* in calls to xfs_trans_next_ail().
*/
-STATIC xfs_log_item_t *
-xfs_trans_first_push_ail(
- xfs_mount_t *mp,
- int *gen,
- xfs_lsn_t lsn)
+xfs_log_item_t *
+xfs_trans_ail_cursor_first(
+ struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur,
+ xfs_lsn_t lsn)
{
- xfs_log_item_t *lip;
+ xfs_log_item_t *lip;
- lip = xfs_ail_min(&mp->m_ail);
- *gen = (int)mp->m_ail.xa_gen;
+ xfs_trans_ail_cursor_init(ailp, cur);
+ lip = xfs_ail_min(ailp);
if (lsn == 0)
- return lip;
+ goto out;
- list_for_each_entry(lip, &mp->m_ail.xa_ail, li_ail) {
+ list_for_each_entry(lip, &ailp->xa_ail, li_ail) {
if (XFS_LSN_CMP(lip->li_lsn, lsn) >= 0)
- return lip;
+ goto out;
}
-
- return NULL;
+ lip = NULL;
+out:
+ xfs_trans_ail_cursor_set(ailp, cur, lip);
+ return lip;
}
/*
@@ -129,29 +241,29 @@ xfs_trans_first_push_ail(
*/
long
xfsaild_push(
- xfs_mount_t *mp,
+ struct xfs_ail *ailp,
xfs_lsn_t *last_lsn)
{
long tout = 1000; /* milliseconds */
xfs_lsn_t last_pushed_lsn = *last_lsn;
- xfs_lsn_t target = mp->m_ail.xa_target;
+ xfs_lsn_t target = ailp->xa_target;
xfs_lsn_t lsn;
xfs_log_item_t *lip;
- int gen;
- int restarts;
int flush_log, count, stuck;
+ xfs_mount_t *mp = ailp->xa_mount;
+ struct xfs_ail_cursor *cur = &ailp->xa_cursors;
-#define XFS_TRANS_PUSH_AIL_RESTARTS 10
-
- spin_lock(&mp->m_ail_lock);
- lip = xfs_trans_first_push_ail(mp, &gen, *last_lsn);
+ spin_lock(&ailp->xa_lock);
+ xfs_trans_ail_cursor_init(ailp, cur);
+ lip = xfs_trans_ail_cursor_first(ailp, cur, *last_lsn);
if (!lip || XFS_FORCED_SHUTDOWN(mp)) {
/*
* AIL is empty or our push has reached the end.
*/
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_cursor_done(ailp, cur);
+ spin_unlock(&ailp->xa_lock);
last_pushed_lsn = 0;
- goto out;
+ return tout;
}
XFS_STATS_INC(xs_push_ail);
@@ -169,7 +281,7 @@ xfsaild_push(
*/
tout = 10;
lsn = lip->li_lsn;
- flush_log = stuck = count = restarts = 0;
+ flush_log = stuck = count = 0;
while ((XFS_LSN_CMP(lip->li_lsn, target) < 0)) {
int lock_result;
/*
@@ -184,7 +296,7 @@ xfsaild_push(
* skip to the next item in the list.
*/
lock_result = IOP_TRYLOCK(lip);
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
switch (lock_result) {
case XFS_ITEM_SUCCESS:
XFS_STATS_INC(xs_push_ail_success);
@@ -221,7 +333,7 @@ xfsaild_push(
break;
}
- spin_lock(&mp->m_ail_lock);
+ spin_lock(&ailp->xa_lock);
/* should we bother continuing? */
if (XFS_FORCED_SHUTDOWN(mp))
break;
@@ -244,14 +356,13 @@ xfsaild_push(
if (stuck > 100)
break;
- lip = xfs_trans_next_ail(mp, lip, &gen, &restarts);
+ lip = xfs_trans_ail_cursor_next(ailp, cur);
if (lip == NULL)
break;
- if (restarts > XFS_TRANS_PUSH_AIL_RESTARTS)
- break;
lsn = lip->li_lsn;
}
- spin_unlock(&mp->m_ail_lock);
+ xfs_trans_ail_cursor_done(ailp, cur);
+ spin_unlock(&ailp->xa_lock);
if (flush_log) {
/*
@@ -274,8 +385,7 @@ xfsaild_push(
*/
tout += 20;
last_pushed_lsn = 0;
- } else if ((restarts > XFS_TRANS_PUSH_AIL_RESTARTS) ||
- ((stuck * 100) / count > 90)) {
+ } else if ((stuck * 100) / count > 90) {
/*
* Either there is a lot of contention on the AIL or we
* are stuck due to operations in progress. "Stuck" in this
@@ -287,7 +397,6 @@ xfsaild_push(
*/
tout += 10;
}
-out:
*last_lsn = last_pushed_lsn;
return tout;
} /* xfsaild_push */
@@ -303,7 +412,7 @@ out:
*/
void
xfs_trans_unlocked_item(
- xfs_mount_t *mp,
+ struct xfs_ail *ailp,
xfs_log_item_t *lip)
{
xfs_log_item_t *min_lip;
@@ -315,7 +424,7 @@ xfs_trans_unlocked_item(
* over some potentially valid data.
*/
if (!(lip->li_flags & XFS_LI_IN_AIL) ||
- XFS_FORCED_SHUTDOWN(mp)) {
+ XFS_FORCED_SHUTDOWN(ailp->xa_mount)) {
return;
}
@@ -331,10 +440,10 @@ xfs_trans_unlocked_item(
* the call to xfs_log_move_tail() doesn't do anything if there's
* not enough free space to wake people up so we're safe calling it.
*/
- min_lip = xfs_ail_min(&mp->m_ail);
+ min_lip = xfs_ail_min(ailp);
if (min_lip == lip)
- xfs_log_move_tail(mp, 1);
+ xfs_log_move_tail(ailp->xa_mount, 1);
} /* xfs_trans_unlocked_item */
@@ -347,41 +456,37 @@ xfs_trans_unlocked_item(
* we move in the AIL is the minimum one, update the tail lsn in the
* log manager.
*
- * Increment the AIL's generation count to indicate that the tree
- * has changed.
- *
* This function must be called with the AIL lock held. The lock
* is dropped before returning.
*/
void
-xfs_trans_update_ail(
- xfs_mount_t *mp,
+xfs_trans_ail_update(
+ struct xfs_ail *ailp,
xfs_log_item_t *lip,
- xfs_lsn_t lsn) __releases(mp->m_ail_lock)
+ xfs_lsn_t lsn) __releases(ailp->xa_lock)
{
- xfs_log_item_t *dlip=NULL;
+ xfs_log_item_t *dlip = NULL;
xfs_log_item_t *mlip; /* ptr to minimum lip */
- mlip = xfs_ail_min(&mp->m_ail);
+ mlip = xfs_ail_min(ailp);
if (lip->li_flags & XFS_LI_IN_AIL) {
- dlip = xfs_ail_delete(&mp->m_ail, lip);
+ dlip = xfs_ail_delete(ailp, lip);
ASSERT(dlip == lip);
+ xfs_trans_ail_cursor_clear(ailp, dlip);
} else {
lip->li_flags |= XFS_LI_IN_AIL;
}
lip->li_lsn = lsn;
-
- xfs_ail_insert(&mp->m_ail, lip);
- mp->m_ail.xa_gen++;
+ xfs_ail_insert(ailp, lip);
if (mlip == dlip) {
- mlip = xfs_ail_min(&mp->m_ail);
- spin_unlock(&mp->m_ail_lock);
- xfs_log_move_tail(mp, mlip->li_lsn);
+ mlip = xfs_ail_min(ailp);
+ spin_unlock(&ailp->xa_lock);
+ xfs_log_move_tail(ailp->xa_mount, mlip->li_lsn);
} else {
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
@@ -403,29 +508,30 @@ xfs_trans_update_ail(
* is dropped before returning.
*/
void
-xfs_trans_delete_ail(
- xfs_mount_t *mp,
- xfs_log_item_t *lip) __releases(mp->m_ail_lock)
+xfs_trans_ail_delete(
+ struct xfs_ail *ailp,
+ xfs_log_item_t *lip) __releases(ailp->xa_lock)
{
xfs_log_item_t *dlip;
xfs_log_item_t *mlip;
if (lip->li_flags & XFS_LI_IN_AIL) {
- mlip = xfs_ail_min(&mp->m_ail);
- dlip = xfs_ail_delete(&mp->m_ail, lip);
+ mlip = xfs_ail_min(ailp);
+ dlip = xfs_ail_delete(ailp, lip);
ASSERT(dlip == lip);
+ xfs_trans_ail_cursor_clear(ailp, dlip);
lip->li_flags &= ~XFS_LI_IN_AIL;
lip->li_lsn = 0;
- mp->m_ail.xa_gen++;
if (mlip == dlip) {
- mlip = xfs_ail_min(&mp->m_ail);
- spin_unlock(&mp->m_ail_lock);
- xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
+ mlip = xfs_ail_min(ailp);
+ spin_unlock(&ailp->xa_lock);
+ xfs_log_move_tail(ailp->xa_mount,
+ (mlip ? mlip->li_lsn : 0));
} else {
- spin_unlock(&mp->m_ail_lock);
+ spin_unlock(&ailp->xa_lock);
}
}
else {
@@ -433,13 +539,13 @@ xfs_trans_delete_ail(
* If the file system is not being shutdown, we are in
* serious trouble if we get to this stage.
*/
- if (XFS_FORCED_SHUTDOWN(mp))
- spin_unlock(&mp->m_ail_lock);
- else {
+ struct xfs_mount *mp = ailp->xa_mount;
+
+ spin_unlock(&ailp->xa_lock);
+ if (!XFS_FORCED_SHUTDOWN(mp)) {
xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp,
"%s: attempting to delete a log item that is not in the AIL",
__func__);
- spin_unlock(&mp->m_ail_lock);
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
}
}
@@ -448,56 +554,6 @@ xfs_trans_delete_ail(
/*
- * Return the item in the AIL with the smallest lsn.
- * Return the current tree generation number for use
- * in calls to xfs_trans_next_ail().
- */
-xfs_log_item_t *
-xfs_trans_first_ail(
- xfs_mount_t *mp,
- int *gen)
-{
- xfs_log_item_t *lip;
-
- lip = xfs_ail_min(&mp->m_ail);
- *gen = (int)mp->m_ail.xa_gen;
-
- return lip;
-}
-
-/*
- * If the generation count of the tree has not changed since the
- * caller last took something from the AIL, then return the elmt
- * in the tree which follows the one given. If the count has changed,
- * then return the minimum elmt of the AIL and bump the restarts counter
- * if one is given.
- */
-xfs_log_item_t *
-xfs_trans_next_ail(
- xfs_mount_t *mp,
- xfs_log_item_t *lip,
- int *gen,
- int *restarts)
-{
- xfs_log_item_t *nlip;
-
- ASSERT(mp && lip && gen);
- if (mp->m_ail.xa_gen == *gen) {
- nlip = xfs_ail_next(&mp->m_ail, lip);
- } else {
- nlip = xfs_ail_min(&mp->m_ail);
- *gen = (int)mp->m_ail.xa_gen;
- if (restarts != NULL) {
- XFS_STATS_INC(xs_push_ail_restarts);
- (*restarts)++;
- }
- }
-
- return (nlip);
-}
-
-
-/*
* The active item list (AIL) is a doubly linked list of log
* items sorted by ascending lsn. The base of the list is
* a forw/back pointer pair embedded in the xfs mount structure.
@@ -515,15 +571,35 @@ int
xfs_trans_ail_init(
xfs_mount_t *mp)
{
- INIT_LIST_HEAD(&mp->m_ail.xa_ail);
- return xfsaild_start(mp);
+ struct xfs_ail *ailp;
+ int error;
+
+ ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL);
+ if (!ailp)
+ return ENOMEM;
+
+ ailp->xa_mount = mp;
+ INIT_LIST_HEAD(&ailp->xa_ail);
+ spin_lock_init(&ailp->xa_lock);
+ error = xfsaild_start(ailp);
+ if (error)
+ goto out_free_ailp;
+ mp->m_ail = ailp;
+ return 0;
+
+out_free_ailp:
+ kmem_free(ailp);
+ return error;
}
void
xfs_trans_ail_destroy(
xfs_mount_t *mp)
{
- xfsaild_stop(mp);
+ struct xfs_ail *ailp = mp->m_ail;
+
+ xfsaild_stop(ailp);
+ kmem_free(ailp);
}
/*
@@ -534,7 +610,7 @@ xfs_trans_ail_destroy(
*/
STATIC void
xfs_ail_insert(
- xfs_ail_t *ailp,
+ struct xfs_ail *ailp,
xfs_log_item_t *lip)
/* ARGSUSED */
{
@@ -568,7 +644,7 @@ xfs_ail_insert(
/*ARGSUSED*/
STATIC xfs_log_item_t *
xfs_ail_delete(
- xfs_ail_t *ailp,
+ struct xfs_ail *ailp,
xfs_log_item_t *lip)
/* ARGSUSED */
{
@@ -585,7 +661,7 @@ xfs_ail_delete(
*/
STATIC xfs_log_item_t *
xfs_ail_min(
- xfs_ail_t *ailp)
+ struct xfs_ail *ailp)
/* ARGSUSED */
{
if (list_empty(&ailp->xa_ail))
@@ -601,7 +677,7 @@ xfs_ail_min(
*/
STATIC xfs_log_item_t *
xfs_ail_next(
- xfs_ail_t *ailp,
+ struct xfs_ail *ailp,
xfs_log_item_t *lip)
/* ARGSUSED */
{
@@ -617,7 +693,7 @@ xfs_ail_next(
*/
STATIC void
xfs_ail_check(
- xfs_ail_t *ailp,
+ struct xfs_ail *ailp,
xfs_log_item_t *lip)
{
xfs_log_item_t *prev_lip;
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 4e855b5ced66..8ee2f8c8b0a6 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -527,9 +527,8 @@ xfs_trans_brelse(xfs_trans_t *tp,
lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
if (lip->li_type == XFS_LI_BUF) {
bip = XFS_BUF_FSPRIVATE(bp,xfs_buf_log_item_t*);
- xfs_trans_unlocked_item(
- bip->bli_item.li_mountp,
- lip);
+ xfs_trans_unlocked_item(bip->bli_item.li_ailp,
+ lip);
}
}
xfs_buf_relse(bp);
@@ -626,7 +625,7 @@ xfs_trans_brelse(xfs_trans_t *tp,
* tell the AIL that the buffer is being unlocked.
*/
if (bip != NULL) {
- xfs_trans_unlocked_item(bip->bli_item.li_mountp,
+ xfs_trans_unlocked_item(bip->bli_item.li_ailp,
(xfs_log_item_t*)bip);
}
diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c
index 2a1c0f071f91..23d276af2e0c 100644
--- a/fs/xfs/xfs_trans_inode.c
+++ b/fs/xfs/xfs_trans_inode.c
@@ -85,7 +85,6 @@ xfs_trans_iget(
{
int error;
xfs_inode_t *ip;
- xfs_inode_log_item_t *iip;
/*
* If the transaction pointer is NULL, just call the normal
@@ -138,34 +137,7 @@ xfs_trans_iget(
}
ASSERT(ip != NULL);
- /*
- * Get a log_item_desc to point at the new item.
- */
- if (ip->i_itemp == NULL)
- xfs_inode_item_init(ip, mp);
- iip = ip->i_itemp;
- (void) xfs_trans_add_item(tp, (xfs_log_item_t *)(iip));
-
- xfs_trans_inode_broot_debug(ip);
-
- /*
- * If the IO lock has been acquired, mark that in
- * the inode log item so we'll know to unlock it
- * when the transaction commits.
- */
- ASSERT(iip->ili_flags == 0);
- if (lock_flags & XFS_IOLOCK_EXCL) {
- iip->ili_flags |= XFS_ILI_IOLOCKED_EXCL;
- } else if (lock_flags & XFS_IOLOCK_SHARED) {
- iip->ili_flags |= XFS_ILI_IOLOCKED_SHARED;
- }
-
- /*
- * Initialize i_transp so we can find it with xfs_inode_incore()
- * above.
- */
- ip->i_transp = tp;
-
+ xfs_trans_ijoin(tp, ip, lock_flags);
*ipp = ip;
return 0;
}
diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c
index 3c666e8317f8..e110bf57d7f4 100644
--- a/fs/xfs/xfs_trans_item.c
+++ b/fs/xfs/xfs_trans_item.c
@@ -22,6 +22,14 @@
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_trans_priv.h"
+/* XXX: from here down needed until struct xfs_trans has it's own ailp */
+#include "xfs_bit.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
STATIC int xfs_trans_unlock_chunk(xfs_log_item_chunk_t *,
int, int, xfs_lsn_t);
@@ -79,6 +87,7 @@ xfs_trans_add_item(xfs_trans_t *tp, xfs_log_item_t *lip)
lidp->lid_size = 0;
lip->li_desc = lidp;
lip->li_mountp = tp->t_mountp;
+ lip->li_ailp = tp->t_mountp->m_ail;
return lidp;
}
@@ -120,6 +129,7 @@ xfs_trans_add_item(xfs_trans_t *tp, xfs_log_item_t *lip)
lidp->lid_size = 0;
lip->li_desc = lidp;
lip->li_mountp = tp->t_mountp;
+ lip->li_ailp = tp->t_mountp->m_ail;
return lidp;
}
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
index 3c748c456ed4..73e2ad397432 100644
--- a/fs/xfs/xfs_trans_priv.h
+++ b/fs/xfs/xfs_trans_priv.h
@@ -44,25 +44,93 @@ xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
xfs_extlen_t idx);
/*
- * From xfs_trans_ail.c
+ * AIL traversal cursor.
+ *
+ * Rather than using a generation number for detecting changes in the ail, use
+ * a cursor that is protected by the ail lock. The aild cursor exists in the
+ * struct xfs_ail, but other traversals can declare it on the stack and link it
+ * to the ail list.
+ *
+ * When an object is deleted from or moved int the AIL, the cursor list is
+ * searched to see if the object is a designated cursor item. If it is, it is
+ * deleted from the cursor so that the next time the cursor is used traversal
+ * will return to the start.
+ *
+ * This means a traversal colliding with a removal will cause a restart of the
+ * list scan, rather than any insertion or deletion anywhere in the list. The
+ * low bit of the item pointer is set if the cursor has been invalidated so
+ * that we can tell the difference between invalidation and reaching the end
+ * of the list to trigger traversal restarts.
*/
-void xfs_trans_update_ail(struct xfs_mount *mp,
- struct xfs_log_item *lip, xfs_lsn_t lsn)
- __releases(mp->m_ail_lock);
-void xfs_trans_delete_ail(struct xfs_mount *mp,
- struct xfs_log_item *lip)
- __releases(mp->m_ail_lock);
-struct xfs_log_item *xfs_trans_first_ail(struct xfs_mount *, int *);
-struct xfs_log_item *xfs_trans_next_ail(struct xfs_mount *,
- struct xfs_log_item *, int *, int *);
+struct xfs_ail_cursor {
+ struct xfs_ail_cursor *next;
+ struct xfs_log_item *item;
+};
+/*
+ * Private AIL structures.
+ *
+ * Eventually we need to drive the locking in here as well.
+ */
+struct xfs_ail {
+ struct xfs_mount *xa_mount;
+ struct list_head xa_ail;
+ uint xa_gen;
+ struct task_struct *xa_task;
+ xfs_lsn_t xa_target;
+ struct xfs_ail_cursor xa_cursors;
+ spinlock_t xa_lock;
+};
/*
- * AIL push thread support
+ * From xfs_trans_ail.c
*/
-long xfsaild_push(struct xfs_mount *, xfs_lsn_t *);
-void xfsaild_wakeup(struct xfs_mount *, xfs_lsn_t);
-int xfsaild_start(struct xfs_mount *);
-void xfsaild_stop(struct xfs_mount *);
+void xfs_trans_ail_update(struct xfs_ail *ailp,
+ struct xfs_log_item *lip, xfs_lsn_t lsn)
+ __releases(ailp->xa_lock);
+void xfs_trans_ail_delete(struct xfs_ail *ailp,
+ struct xfs_log_item *lip)
+ __releases(ailp->xa_lock);
+void xfs_trans_ail_push(struct xfs_ail *, xfs_lsn_t);
+void xfs_trans_unlocked_item(struct xfs_ail *,
+ xfs_log_item_t *);
+
+xfs_lsn_t xfs_trans_ail_tail(struct xfs_ail *ailp);
+
+struct xfs_log_item *xfs_trans_ail_cursor_first(struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur,
+ xfs_lsn_t lsn);
+struct xfs_log_item *xfs_trans_ail_cursor_next(struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur);
+void xfs_trans_ail_cursor_done(struct xfs_ail *ailp,
+ struct xfs_ail_cursor *cur);
+
+long xfsaild_push(struct xfs_ail *, xfs_lsn_t *);
+void xfsaild_wakeup(struct xfs_ail *, xfs_lsn_t);
+int xfsaild_start(struct xfs_ail *);
+void xfsaild_stop(struct xfs_ail *);
+#if BITS_PER_LONG != 64
+static inline void
+xfs_trans_ail_copy_lsn(
+ struct xfs_ail *ailp,
+ xfs_lsn_t *dst,
+ xfs_lsn_t *src)
+{
+ ASSERT(sizeof(xfs_lsn_t) == 8); /* don't lock if it shrinks */
+ spin_lock(&ailp->xa_lock);
+ *dst = *src;
+ spin_unlock(&ailp->xa_lock);
+}
+#else
+static inline void
+xfs_trans_ail_copy_lsn(
+ struct xfs_ail *ailp,
+ xfs_lsn_t *dst,
+ xfs_lsn_t *src)
+{
+ ASSERT(sizeof(xfs_lsn_t) == 8);
+ *dst = *src;
+}
+#endif
#endif /* __XFS_TRANS_PRIV_H__ */
diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c
index 35d4d414bcc2..fcc2285d03ed 100644
--- a/fs/xfs/xfs_utils.c
+++ b/fs/xfs/xfs_utils.c
@@ -172,6 +172,12 @@ xfs_dir_ialloc(
*ipp = NULL;
return code;
}
+
+ /*
+ * transaction commit worked ok so we can drop the extra ticket
+ * reference that we gained in xfs_trans_dup()
+ */
+ xfs_log_ticket_put(tp->t_ticket);
code = xfs_trans_reserve(tp, 0, log_res, 0,
XFS_TRANS_PERM_LOG_RES, log_count);
/*
@@ -268,9 +274,9 @@ xfs_bump_ino_vers2(
xfs_mount_t *mp;
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
- ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1);
+ ASSERT(ip->i_d.di_version == 1);
- ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_version = 2;
ip->i_d.di_onlink = 0;
memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
mp = tp->t_mountp;
@@ -302,7 +308,7 @@ xfs_bumplink(
ASSERT(ip->i_d.di_nlink > 0);
ip->i_d.di_nlink++;
inc_nlink(VFS_I(ip));
- if ((ip->i_d.di_version == XFS_DINODE_VERSION_1) &&
+ if ((ip->i_d.di_version == 1) &&
(ip->i_d.di_nlink > XFS_MAXLINK_1)) {
/*
* The inode has increased its number of links beyond
diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c
deleted file mode 100644
index 439dd3939dda..000000000000
--- a/fs/xfs/xfs_vfsops.c
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * Copyright (c) 2000-2005 Silicon Graphics, 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.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-#include "xfs.h"
-#include "xfs_fs.h"
-#include "xfs_types.h"
-#include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_inum.h"
-#include "xfs_trans.h"
-#include "xfs_sb.h"
-#include "xfs_ag.h"
-#include "xfs_dir2.h"
-#include "xfs_dmapi.h"
-#include "xfs_mount.h"
-#include "xfs_da_btree.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_alloc_btree.h"
-#include "xfs_dir2_sf.h"
-#include "xfs_attr_sf.h"
-#include "xfs_dinode.h"
-#include "xfs_inode.h"
-#include "xfs_inode_item.h"
-#include "xfs_btree.h"
-#include "xfs_alloc.h"
-#include "xfs_ialloc.h"
-#include "xfs_quota.h"
-#include "xfs_error.h"
-#include "xfs_bmap.h"
-#include "xfs_rw.h"
-#include "xfs_buf_item.h"
-#include "xfs_log_priv.h"
-#include "xfs_dir2_trace.h"
-#include "xfs_extfree_item.h"
-#include "xfs_acl.h"
-#include "xfs_attr.h"
-#include "xfs_clnt.h"
-#include "xfs_mru_cache.h"
-#include "xfs_filestream.h"
-#include "xfs_fsops.h"
-#include "xfs_vnodeops.h"
-#include "xfs_vfsops.h"
-#include "xfs_utils.h"
-
-
-STATIC void
-xfs_quiesce_fs(
- xfs_mount_t *mp)
-{
- int count = 0, pincount;
-
- xfs_flush_buftarg(mp->m_ddev_targp, 0);
- xfs_finish_reclaim_all(mp, 0);
-
- /* This loop must run at least twice.
- * The first instance of the loop will flush
- * most meta data but that will generate more
- * meta data (typically directory updates).
- * Which then must be flushed and logged before
- * we can write the unmount record.
- */
- do {
- xfs_syncsub(mp, SYNC_INODE_QUIESCE, NULL);
- pincount = xfs_flush_buftarg(mp->m_ddev_targp, 1);
- if (!pincount) {
- delay(50);
- count++;
- }
- } while (count < 2);
-}
-
-/*
- * Second stage of a quiesce. The data is already synced, now we have to take
- * care of the metadata. New transactions are already blocked, so we need to
- * wait for any remaining transactions to drain out before proceding.
- */
-void
-xfs_attr_quiesce(
- xfs_mount_t *mp)
-{
- int error = 0;
-
- /* wait for all modifications to complete */
- while (atomic_read(&mp->m_active_trans) > 0)
- delay(100);
-
- /* flush inodes and push all remaining buffers out to disk */
- xfs_quiesce_fs(mp);
-
- ASSERT_ALWAYS(atomic_read(&mp->m_active_trans) == 0);
-
- /* Push the superblock and write an unmount record */
- error = xfs_log_sbcount(mp, 1);
- if (error)
- xfs_fs_cmn_err(CE_WARN, mp,
- "xfs_attr_quiesce: failed to log sb changes. "
- "Frozen image may not be consistent.");
- xfs_log_unmount_write(mp);
- xfs_unmountfs_writesb(mp);
-}
-
-/*
- * xfs_unmount_flush implements a set of flush operation on special
- * inodes, which are needed as a separate set of operations so that
- * they can be called as part of relocation process.
- */
-int
-xfs_unmount_flush(
- xfs_mount_t *mp, /* Mount structure we are getting
- rid of. */
- int relocation) /* Called from vfs relocation. */
-{
- xfs_inode_t *rip = mp->m_rootip;
- xfs_inode_t *rbmip;
- xfs_inode_t *rsumip = NULL;
- int error;
-
- xfs_ilock(rip, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
- xfs_iflock(rip);
-
- /*
- * Flush out the real time inodes.
- */
- if ((rbmip = mp->m_rbmip) != NULL) {
- xfs_ilock(rbmip, XFS_ILOCK_EXCL);
- xfs_iflock(rbmip);
- error = xfs_iflush(rbmip, XFS_IFLUSH_SYNC);
- xfs_iunlock(rbmip, XFS_ILOCK_EXCL);
-
- if (error == EFSCORRUPTED)
- goto fscorrupt_out;
-
- ASSERT(vn_count(VFS_I(rbmip)) == 1);
-
- rsumip = mp->m_rsumip;
- xfs_ilock(rsumip, XFS_ILOCK_EXCL);
- xfs_iflock(rsumip);
- error = xfs_iflush(rsumip, XFS_IFLUSH_SYNC);
- xfs_iunlock(rsumip, XFS_ILOCK_EXCL);
-
- if (error == EFSCORRUPTED)
- goto fscorrupt_out;
-
- ASSERT(vn_count(VFS_I(rsumip)) == 1);
- }
-
- /*
- * Synchronously flush root inode to disk
- */
- error = xfs_iflush(rip, XFS_IFLUSH_SYNC);
- if (error == EFSCORRUPTED)
- goto fscorrupt_out2;
-
- if (vn_count(VFS_I(rip)) != 1 && !relocation) {
- xfs_iunlock(rip, XFS_ILOCK_EXCL);
- return XFS_ERROR(EBUSY);
- }
-
- /*
- * Release dquot that rootinode, rbmino and rsumino might be holding,
- * flush and purge the quota inodes.
- */
- error = XFS_QM_UNMOUNT(mp);
- if (error == EFSCORRUPTED)
- goto fscorrupt_out2;
-
- if (rbmip) {
- IRELE(rbmip);
- IRELE(rsumip);
- }
-
- xfs_iunlock(rip, XFS_ILOCK_EXCL);
- return 0;
-
-fscorrupt_out:
- xfs_ifunlock(rip);
-
-fscorrupt_out2:
- xfs_iunlock(rip, XFS_ILOCK_EXCL);
-
- return XFS_ERROR(EFSCORRUPTED);
-}
-
-/*
- * xfs_sync flushes any pending I/O to file system vfsp.
- *
- * This routine is called by vfs_sync() to make sure that things make it
- * out to disk eventually, on sync() system calls to flush out everything,
- * and when the file system is unmounted. For the vfs_sync() case, all
- * we really need to do is sync out the log to make all of our meta-data
- * updates permanent (except for timestamps). For calls from pflushd(),
- * dirty pages are kept moving by calling pdflush() on the inodes
- * containing them. We also flush the inodes that we can lock without
- * sleeping and the superblock if we can lock it without sleeping from
- * vfs_sync() so that items at the tail of the log are always moving out.
- *
- * Flags:
- * SYNC_BDFLUSH - We're being called from vfs_sync() so we don't want
- * to sleep if we can help it. All we really need
- * to do is ensure that the log is synced at least
- * periodically. We also push the inodes and
- * superblock if we can lock them without sleeping
- * and they are not pinned.
- * SYNC_ATTR - We need to flush the inodes. If SYNC_BDFLUSH is not
- * set, then we really want to lock each inode and flush
- * it.
- * SYNC_WAIT - All the flushes that take place in this call should
- * be synchronous.
- * SYNC_DELWRI - This tells us to push dirty pages associated with
- * inodes. SYNC_WAIT and SYNC_BDFLUSH are used to
- * determine if they should be flushed sync, async, or
- * delwri.
- * SYNC_CLOSE - This flag is passed when the system is being
- * unmounted. We should sync and invalidate everything.
- * SYNC_FSDATA - This indicates that the caller would like to make
- * sure the superblock is safe on disk. We can ensure
- * this by simply making sure the log gets flushed
- * if SYNC_BDFLUSH is set, and by actually writing it
- * out otherwise.
- * SYNC_IOWAIT - The caller wants us to wait for all data I/O to complete
- * before we return (including direct I/O). Forms the drain
- * side of the write barrier needed to safely quiesce the
- * filesystem.
- *
- */
-int
-xfs_sync(
- xfs_mount_t *mp,
- int flags)
-{
- int error;
-
- /*
- * Get the Quota Manager to flush the dquots.
- *
- * If XFS quota support is not enabled or this filesystem
- * instance does not use quotas XFS_QM_DQSYNC will always
- * return zero.
- */
- error = XFS_QM_DQSYNC(mp, flags);
- if (error) {
- /*
- * If we got an IO error, we will be shutting down.
- * So, there's nothing more for us to do here.
- */
- ASSERT(error != EIO || XFS_FORCED_SHUTDOWN(mp));
- if (XFS_FORCED_SHUTDOWN(mp))
- return XFS_ERROR(error);
- }
-
- if (flags & SYNC_IOWAIT)
- xfs_filestream_flush(mp);
-
- return xfs_syncsub(mp, flags, NULL);
-}
-
-/*
- * xfs sync routine for internal use
- *
- * This routine supports all of the flags defined for the generic vfs_sync
- * interface as explained above under xfs_sync.
- *
- */
-int
-xfs_sync_inodes(
- xfs_mount_t *mp,
- int flags,
- int *bypassed)
-{
- xfs_inode_t *ip = NULL;
- struct inode *vp = NULL;
- int error;
- int last_error;
- uint64_t fflag;
- uint lock_flags;
- uint base_lock_flags;
- boolean_t mount_locked;
- boolean_t vnode_refed;
- int preempt;
- xfs_iptr_t *ipointer;
-#ifdef DEBUG
- boolean_t ipointer_in = B_FALSE;
-
-#define IPOINTER_SET ipointer_in = B_TRUE
-#define IPOINTER_CLR ipointer_in = B_FALSE
-#else
-#define IPOINTER_SET
-#define IPOINTER_CLR
-#endif
-
-
-/* Insert a marker record into the inode list after inode ip. The list
- * must be locked when this is called. After the call the list will no
- * longer be locked.
- */
-#define IPOINTER_INSERT(ip, mp) { \
- ASSERT(ipointer_in == B_FALSE); \
- ipointer->ip_mnext = ip->i_mnext; \
- ipointer->ip_mprev = ip; \
- ip->i_mnext = (xfs_inode_t *)ipointer; \
- ipointer->ip_mnext->i_mprev = (xfs_inode_t *)ipointer; \
- preempt = 0; \
- XFS_MOUNT_IUNLOCK(mp); \
- mount_locked = B_FALSE; \
- IPOINTER_SET; \
- }
-
-/* Remove the marker from the inode list. If the marker was the only item
- * in the list then there are no remaining inodes and we should zero out
- * the whole list. If we are the current head of the list then move the head
- * past us.
- */
-#define IPOINTER_REMOVE(ip, mp) { \
- ASSERT(ipointer_in == B_TRUE); \
- if (ipointer->ip_mnext != (xfs_inode_t *)ipointer) { \
- ip = ipointer->ip_mnext; \
- ip->i_mprev = ipointer->ip_mprev; \
- ipointer->ip_mprev->i_mnext = ip; \
- if (mp->m_inodes == (xfs_inode_t *)ipointer) { \
- mp->m_inodes = ip; \
- } \
- } else { \
- ASSERT(mp->m_inodes == (xfs_inode_t *)ipointer); \
- mp->m_inodes = NULL; \
- ip = NULL; \
- } \
- IPOINTER_CLR; \
- }
-
-#define XFS_PREEMPT_MASK 0x7f
-
- ASSERT(!(flags & SYNC_BDFLUSH));
-
- if (bypassed)
- *bypassed = 0;
- if (mp->m_flags & XFS_MOUNT_RDONLY)
- return 0;
- error = 0;
- last_error = 0;
- preempt = 0;
-
- /* Allocate a reference marker */
- ipointer = (xfs_iptr_t *)kmem_zalloc(sizeof(xfs_iptr_t), KM_SLEEP);
-
- fflag = XFS_B_ASYNC; /* default is don't wait */
- if (flags & SYNC_DELWRI)
- fflag = XFS_B_DELWRI;
- if (flags & SYNC_WAIT)
- fflag = 0; /* synchronous overrides all */
-
- base_lock_flags = XFS_ILOCK_SHARED;
- if (flags & (SYNC_DELWRI | SYNC_CLOSE)) {
- /*
- * We need the I/O lock if we're going to call any of
- * the flush/inval routines.
- */
- base_lock_flags |= XFS_IOLOCK_SHARED;
- }
-
- XFS_MOUNT_ILOCK(mp);
-
- ip = mp->m_inodes;
-
- mount_locked = B_TRUE;
- vnode_refed = B_FALSE;
-
- IPOINTER_CLR;
-
- do {
- ASSERT(ipointer_in == B_FALSE);
- ASSERT(vnode_refed == B_FALSE);
-
- lock_flags = base_lock_flags;
-
- /*
- * There were no inodes in the list, just break out
- * of the loop.
- */
- if (ip == NULL) {
- break;
- }
-
- /*
- * We found another sync thread marker - skip it
- */
- if (ip->i_mount == NULL) {
- ip = ip->i_mnext;
- continue;
- }
-
- vp = VFS_I(ip);
-
- /*
- * If the vnode is gone then this is being torn down,
- * call reclaim if it is flushed, else let regular flush
- * code deal with it later in the loop.
- */
-
- if (vp == NULL) {
- /* Skip ones already in reclaim */
- if (ip->i_flags & XFS_IRECLAIM) {
- ip = ip->i_mnext;
- continue;
- }
- if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) {
- ip = ip->i_mnext;
- } else if ((xfs_ipincount(ip) == 0) &&
- xfs_iflock_nowait(ip)) {
- IPOINTER_INSERT(ip, mp);
-
- xfs_finish_reclaim(ip, 1,
- XFS_IFLUSH_DELWRI_ELSE_ASYNC);
-
- XFS_MOUNT_ILOCK(mp);
- mount_locked = B_TRUE;
- IPOINTER_REMOVE(ip, mp);
- } else {
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- ip = ip->i_mnext;
- }
- continue;
- }
-
- if (VN_BAD(vp)) {
- ip = ip->i_mnext;
- continue;
- }
-
- if (XFS_FORCED_SHUTDOWN(mp) && !(flags & SYNC_CLOSE)) {
- XFS_MOUNT_IUNLOCK(mp);
- kmem_free(ipointer);
- return 0;
- }
-
- /*
- * Try to lock without sleeping. We're out of order with
- * the inode list lock here, so if we fail we need to drop
- * the mount lock and try again. If we're called from
- * bdflush() here, then don't bother.
- *
- * The inode lock here actually coordinates with the
- * almost spurious inode lock in xfs_ireclaim() to prevent
- * the vnode we handle here without a reference from
- * being freed while we reference it. If we lock the inode
- * while it's on the mount list here, then the spurious inode
- * lock in xfs_ireclaim() after the inode is pulled from
- * the mount list will sleep until we release it here.
- * This keeps the vnode from being freed while we reference
- * it.
- */
- if (xfs_ilock_nowait(ip, lock_flags) == 0) {
- if (vp == NULL) {
- ip = ip->i_mnext;
- continue;
- }
-
- vp = vn_grab(vp);
- if (vp == NULL) {
- ip = ip->i_mnext;
- continue;
- }
-
- IPOINTER_INSERT(ip, mp);
- xfs_ilock(ip, lock_flags);
-
- ASSERT(vp == VFS_I(ip));
- ASSERT(ip->i_mount == mp);
-
- vnode_refed = B_TRUE;
- }
-
- /* From here on in the loop we may have a marker record
- * in the inode list.
- */
-
- /*
- * If we have to flush data or wait for I/O completion
- * we need to drop the ilock that we currently hold.
- * If we need to drop the lock, insert a marker if we
- * have not already done so.
- */
- if ((flags & (SYNC_CLOSE|SYNC_IOWAIT)) ||
- ((flags & SYNC_DELWRI) && VN_DIRTY(vp))) {
- if (mount_locked) {
- IPOINTER_INSERT(ip, mp);
- }
- xfs_iunlock(ip, XFS_ILOCK_SHARED);
-
- if (flags & SYNC_CLOSE) {
- /* Shutdown case. Flush and invalidate. */
- if (XFS_FORCED_SHUTDOWN(mp))
- xfs_tosspages(ip, 0, -1,
- FI_REMAPF);
- else
- error = xfs_flushinval_pages(ip,
- 0, -1, FI_REMAPF);
- } else if ((flags & SYNC_DELWRI) && VN_DIRTY(vp)) {
- error = xfs_flush_pages(ip, 0,
- -1, fflag, FI_NONE);
- }
-
- /*
- * When freezing, we need to wait ensure all I/O (including direct
- * I/O) is complete to ensure no further data modification can take
- * place after this point
- */
- if (flags & SYNC_IOWAIT)
- vn_iowait(ip);
-
- xfs_ilock(ip, XFS_ILOCK_SHARED);
- }
-
- if ((flags & SYNC_ATTR) &&
- (ip->i_update_core ||
- (ip->i_itemp && ip->i_itemp->ili_format.ilf_fields))) {
- if (mount_locked)
- IPOINTER_INSERT(ip, mp);
-
- if (flags & SYNC_WAIT) {
- xfs_iflock(ip);
- error = xfs_iflush(ip, XFS_IFLUSH_SYNC);
-
- /*
- * If we can't acquire the flush lock, then the inode
- * is already being flushed so don't bother waiting.
- *
- * If we can lock it then do a delwri flush so we can
- * combine multiple inode flushes in each disk write.
- */
- } else if (xfs_iflock_nowait(ip)) {
- error = xfs_iflush(ip, XFS_IFLUSH_DELWRI);
- } else if (bypassed) {
- (*bypassed)++;
- }
- }
-
- if (lock_flags != 0) {
- xfs_iunlock(ip, lock_flags);
- }
-
- if (vnode_refed) {
- /*
- * If we had to take a reference on the vnode
- * above, then wait until after we've unlocked
- * the inode to release the reference. This is
- * because we can be already holding the inode
- * lock when IRELE() calls xfs_inactive().
- *
- * Make sure to drop the mount lock before calling
- * IRELE() so that we don't trip over ourselves if
- * we have to go for the mount lock again in the
- * inactive code.
- */
- if (mount_locked) {
- IPOINTER_INSERT(ip, mp);
- }
-
- IRELE(ip);
-
- vnode_refed = B_FALSE;
- }
-
- if (error) {
- last_error = error;
- }
-
- /*
- * bail out if the filesystem is corrupted.
- */
- if (error == EFSCORRUPTED) {
- if (!mount_locked) {
- XFS_MOUNT_ILOCK(mp);
- IPOINTER_REMOVE(ip, mp);
- }
- XFS_MOUNT_IUNLOCK(mp);
- ASSERT(ipointer_in == B_FALSE);
- kmem_free(ipointer);
- return XFS_ERROR(error);
- }
-
- /* Let other threads have a chance at the mount lock
- * if we have looped many times without dropping the
- * lock.
- */
- if ((++preempt & XFS_PREEMPT_MASK) == 0) {
- if (mount_locked) {
- IPOINTER_INSERT(ip, mp);
- }
- }
-
- if (mount_locked == B_FALSE) {
- XFS_MOUNT_ILOCK(mp);
- mount_locked = B_TRUE;
- IPOINTER_REMOVE(ip, mp);
- continue;
- }
-
- ASSERT(ipointer_in == B_FALSE);
- ip = ip->i_mnext;
-
- } while (ip != mp->m_inodes);
-
- XFS_MOUNT_IUNLOCK(mp);
-
- ASSERT(ipointer_in == B_FALSE);
-
- kmem_free(ipointer);
- return XFS_ERROR(last_error);
-}
-
-/*
- * xfs sync routine for internal use
- *
- * This routine supports all of the flags defined for the generic vfs_sync
- * interface as explained above under xfs_sync.
- *
- */
-int
-xfs_syncsub(
- xfs_mount_t *mp,
- int flags,
- int *bypassed)
-{
- int error = 0;
- int last_error = 0;
- uint log_flags = XFS_LOG_FORCE;
- xfs_buf_t *bp;
- xfs_buf_log_item_t *bip;
-
- /*
- * Sync out the log. This ensures that the log is periodically
- * flushed even if there is not enough activity to fill it up.
- */
- if (flags & SYNC_WAIT)
- log_flags |= XFS_LOG_SYNC;
-
- xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
-
- if (flags & (SYNC_ATTR|SYNC_DELWRI)) {
- if (flags & SYNC_BDFLUSH)
- xfs_finish_reclaim_all(mp, 1);
- else
- error = xfs_sync_inodes(mp, flags, bypassed);
- }
-
- /*
- * Flushing out dirty data above probably generated more
- * log activity, so if this isn't vfs_sync() then flush
- * the log again.
- */
- if (flags & SYNC_DELWRI) {
- xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
- }
-
- if (flags & SYNC_FSDATA) {
- /*
- * If this is vfs_sync() then only sync the superblock
- * if we can lock it without sleeping and it is not pinned.
- */
- if (flags & SYNC_BDFLUSH) {
- bp = xfs_getsb(mp, XFS_BUF_TRYLOCK);
- if (bp != NULL) {
- bip = XFS_BUF_FSPRIVATE(bp,xfs_buf_log_item_t*);
- if ((bip != NULL) &&
- xfs_buf_item_dirty(bip)) {
- if (!(XFS_BUF_ISPINNED(bp))) {
- XFS_BUF_ASYNC(bp);
- error = xfs_bwrite(mp, bp);
- } else {
- xfs_buf_relse(bp);
- }
- } else {
- xfs_buf_relse(bp);
- }
- }
- } else {
- bp = xfs_getsb(mp, 0);
- /*
- * If the buffer is pinned then push on the log so
- * we won't get stuck waiting in the write for
- * someone, maybe ourselves, to flush the log.
- * Even though we just pushed the log above, we
- * did not have the superblock buffer locked at
- * that point so it can become pinned in between
- * there and here.
- */
- if (XFS_BUF_ISPINNED(bp))
- xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
- if (flags & SYNC_WAIT)
- XFS_BUF_UNASYNC(bp);
- else
- XFS_BUF_ASYNC(bp);
- error = xfs_bwrite(mp, bp);
- }
- if (error) {
- last_error = error;
- }
- }
-
- /*
- * Now check to see if the log needs a "dummy" transaction.
- */
- if (!(flags & SYNC_REMOUNT) && xfs_log_need_covered(mp)) {
- xfs_trans_t *tp;
- xfs_inode_t *ip;
-
- /*
- * Put a dummy transaction in the log to tell
- * recovery that all others are OK.
- */
- tp = xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
- if ((error = xfs_trans_reserve(tp, 0,
- XFS_ICHANGE_LOG_RES(mp),
- 0, 0, 0))) {
- xfs_trans_cancel(tp, 0);
- return error;
- }
-
- ip = mp->m_rootip;
- xfs_ilock(ip, XFS_ILOCK_EXCL);
-
- xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
- xfs_trans_ihold(tp, ip);
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
- error = xfs_trans_commit(tp, 0);
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
- }
-
- /*
- * When shutting down, we need to insure that the AIL is pushed
- * to disk or the filesystem can appear corrupt from the PROM.
- */
- if ((flags & (SYNC_CLOSE|SYNC_WAIT)) == (SYNC_CLOSE|SYNC_WAIT)) {
- XFS_bflush(mp->m_ddev_targp);
- if (mp->m_rtdev_targp) {
- XFS_bflush(mp->m_rtdev_targp);
- }
- }
-
- return XFS_ERROR(last_error);
-}
diff --git a/fs/xfs/xfs_vfsops.h b/fs/xfs/xfs_vfsops.h
deleted file mode 100644
index a74b05087da4..000000000000
--- a/fs/xfs/xfs_vfsops.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _XFS_VFSOPS_H
-#define _XFS_VFSOPS_H 1
-
-struct cred;
-struct xfs_fid;
-struct inode;
-struct kstatfs;
-struct xfs_mount;
-struct xfs_mount_args;
-
-int xfs_sync(struct xfs_mount *mp, int flags);
-void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
- int lnnum);
-void xfs_attr_quiesce(struct xfs_mount *mp);
-
-#endif /* _XFS_VFSOPS_H */
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index 8b6812f66a15..f07bf8768c3a 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -54,33 +54,10 @@
#include "xfs_vnodeops.h"
int
-xfs_open(
- xfs_inode_t *ip)
-{
- int mode;
-
- if (XFS_FORCED_SHUTDOWN(ip->i_mount))
- return XFS_ERROR(EIO);
-
- /*
- * If it's a directory with any blocks, read-ahead block 0
- * as we're almost certain to have the next operation be a read there.
- */
- if (S_ISDIR(ip->i_d.di_mode) && ip->i_d.di_nextents > 0) {
- mode = xfs_ilock_map_shared(ip);
- if (ip->i_d.di_nextents > 0)
- (void)xfs_da_reada_buf(NULL, ip, 0, XFS_DATA_FORK);
- xfs_iunlock(ip, mode);
- }
- return 0;
-}
-
-int
xfs_setattr(
struct xfs_inode *ip,
struct iattr *iattr,
- int flags,
- cred_t *credp)
+ int flags)
{
xfs_mount_t *mp = ip->i_mount;
struct inode *inode = VFS_I(ip);
@@ -93,7 +70,6 @@ xfs_setattr(
gid_t gid=0, igid=0;
int timeflags = 0;
struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2;
- int file_owner;
int need_iolock = 1;
xfs_itrace_entry(ip);
@@ -104,6 +80,10 @@ xfs_setattr(
if (XFS_FORCED_SHUTDOWN(mp))
return XFS_ERROR(EIO);
+ code = -inode_change_ok(inode, iattr);
+ if (code)
+ return code;
+
olddquot1 = olddquot2 = NULL;
udqp = gdqp = NULL;
@@ -181,62 +161,8 @@ xfs_setattr(
xfs_ilock(ip, lock_flags);
- /* boolean: are we the file owner? */
- file_owner = (current_fsuid() == ip->i_d.di_uid);
-
- /*
- * Change various properties of a file.
- * Only the owner or users with CAP_FOWNER
- * capability may do these things.
- */
- if (mask & (ATTR_MODE|ATTR_UID|ATTR_GID)) {
- /*
- * CAP_FOWNER overrides the following restrictions:
- *
- * The user ID of the calling process must be equal
- * to the file owner ID, except in cases where the
- * CAP_FSETID capability is applicable.
- */
- if (!file_owner && !capable(CAP_FOWNER)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
-
- /*
- * CAP_FSETID overrides the following restrictions:
- *
- * The effective user ID of the calling process shall match
- * the file owner when setting the set-user-ID and
- * set-group-ID bits on that file.
- *
- * The effective group ID or one of the supplementary group
- * IDs of the calling process shall match the group owner of
- * the file when setting the set-group-ID bit on that file
- */
- if (mask & ATTR_MODE) {
- mode_t m = 0;
-
- if ((iattr->ia_mode & S_ISUID) && !file_owner)
- m |= S_ISUID;
- if ((iattr->ia_mode & S_ISGID) &&
- !in_group_p((gid_t)ip->i_d.di_gid))
- m |= S_ISGID;
-#if 0
- /* Linux allows this, Irix doesn't. */
- if ((iattr->ia_mode & S_ISVTX) && !S_ISDIR(ip->i_d.di_mode))
- m |= S_ISVTX;
-#endif
- if (m && !capable(CAP_FSETID))
- iattr->ia_mode &= ~m;
- }
- }
-
/*
* Change file ownership. Must be the owner or privileged.
- * If the system was configured with the "restricted_chown"
- * option, the owner is not permitted to give away the file,
- * and can change the group id only to a group of which he
- * or she is a member.
*/
if (mask & (ATTR_UID|ATTR_GID)) {
/*
@@ -251,23 +177,6 @@ xfs_setattr(
uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
/*
- * CAP_CHOWN overrides the following restrictions:
- *
- * If _POSIX_CHOWN_RESTRICTED is defined, this capability
- * shall override the restriction that a process cannot
- * change the user ID of a file it owns and the restriction
- * that the group ID supplied to the chown() function
- * shall be equal to either the group ID or one of the
- * supplementary group IDs of the calling process.
- */
- if (restricted_chown &&
- (iuid != uid || (igid != gid &&
- !in_group_p((gid_t)gid))) &&
- !capable(CAP_CHOWN)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
- /*
* Do a quota reservation only if uid/gid is actually
* going to change.
*/
@@ -304,36 +213,22 @@ xfs_setattr(
code = XFS_ERROR(EINVAL);
goto error_return;
}
+
/*
* Make sure that the dquots are attached to the inode.
*/
- if ((code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED)))
+ code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED);
+ if (code)
goto error_return;
- }
-
- /*
- * Change file access or modified times.
- */
- if (mask & (ATTR_ATIME|ATTR_MTIME)) {
- if (!file_owner) {
- if ((mask & (ATTR_MTIME_SET|ATTR_ATIME_SET)) &&
- !capable(CAP_FOWNER)) {
- code = XFS_ERROR(EPERM);
- goto error_return;
- }
- }
- }
- /*
- * Now we can make the changes. Before we join the inode
- * to the transaction, if ATTR_SIZE is set then take care of
- * the part of the truncation that must be done without the
- * inode lock. This needs to be done before joining the inode
- * to the transaction, because the inode cannot be unlocked
- * once it is a part of the transaction.
- */
- if (mask & ATTR_SIZE) {
- code = 0;
+ /*
+ * Now we can make the changes. Before we join the inode
+ * to the transaction, if ATTR_SIZE is set then take care of
+ * the part of the truncation that must be done without the
+ * inode lock. This needs to be done before joining the inode
+ * to the transaction, because the inode cannot be unlocked
+ * once it is a part of the transaction.
+ */
if (iattr->ia_size > ip->i_size) {
/*
* Do the first part of growing a file: zero any data
@@ -366,7 +261,7 @@ xfs_setattr(
}
/* wait for all I/O to complete */
- vn_iowait(ip);
+ xfs_ioend_wait(ip);
if (!code)
code = xfs_itruncate_data(ip, iattr->ia_size);
@@ -388,17 +283,10 @@ xfs_setattr(
}
commit_flags = XFS_TRANS_RELEASE_LOG_RES;
xfs_ilock(ip, XFS_ILOCK_EXCL);
- }
- if (tp) {
xfs_trans_ijoin(tp, ip, lock_flags);
xfs_trans_ihold(tp, ip);
- }
- /*
- * Truncate file. Must have write permission and not be a directory.
- */
- if (mask & ATTR_SIZE) {
/*
* Only change the c/mtime if we are changing the size
* or we are explicitly asked to change it. This handles
@@ -438,28 +326,13 @@ xfs_setattr(
*/
xfs_iflags_set(ip, XFS_ITRUNCATED);
}
- }
-
- /*
- * Change file access modes.
- */
- if (mask & ATTR_MODE) {
- ip->i_d.di_mode &= S_IFMT;
- ip->i_d.di_mode |= iattr->ia_mode & ~S_IFMT;
-
- inode->i_mode &= S_IFMT;
- inode->i_mode |= iattr->ia_mode & ~S_IFMT;
-
- xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
- timeflags |= XFS_ICHGTIME_CHG;
+ } else if (tp) {
+ xfs_trans_ijoin(tp, ip, lock_flags);
+ xfs_trans_ihold(tp, ip);
}
/*
* Change file ownership. Must be the owner or privileged.
- * If the system was configured with the "restricted_chown"
- * option, the owner is not permitted to give away the file,
- * and can change the group id only to a group of which he
- * or she is a member.
*/
if (mask & (ATTR_UID|ATTR_GID)) {
/*
@@ -503,6 +376,24 @@ xfs_setattr(
timeflags |= XFS_ICHGTIME_CHG;
}
+ /*
+ * Change file access modes.
+ */
+ if (mask & ATTR_MODE) {
+ umode_t mode = iattr->ia_mode;
+
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ mode &= ~S_ISGID;
+
+ ip->i_d.di_mode &= S_IFMT;
+ ip->i_d.di_mode |= mode & ~S_IFMT;
+
+ inode->i_mode &= S_IFMT;
+ inode->i_mode |= mode & ~S_IFMT;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
/*
* Change file access or modified times.
@@ -713,7 +604,7 @@ xfs_fsync(
return XFS_ERROR(EIO);
/* capture size updates in I/O completion before writing the inode. */
- error = filemap_fdatawait(VFS_I(ip)->i_mapping);
+ error = xfs_wait_on_pages(ip, 0, -1);
if (error)
return XFS_ERROR(error);
@@ -1029,6 +920,12 @@ xfs_inactive_symlink_rmt(
goto error0;
}
/*
+ * transaction commit worked ok so we can drop the extra ticket
+ * reference that we gained in xfs_trans_dup()
+ */
+ xfs_log_ticket_put(tp->t_ticket);
+
+ /*
* Remove the memory for extent descriptions (just bookkeeping).
*/
if (ip->i_df.if_bytes)
@@ -1625,8 +1522,6 @@ xfs_create(
xfs_trans_set_sync(tp);
}
- dp->i_gen++;
-
/*
* Attach the dquot(s) to the inodes and modify them incore.
* These ids of the inode couldn't have changed since the new
@@ -1993,13 +1888,6 @@ xfs_remove(
}
xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- /*
- * Bump the in memory generation count on the parent
- * directory so that other can know that it has changed.
- */
- dp->i_gen++;
- xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
-
if (is_dir) {
/*
* Drop the link from ip's "..".
@@ -2009,7 +1897,7 @@ xfs_remove(
goto out_bmap_cancel;
/*
- * Drop the link from dp to ip.
+ * Drop the "." link from ip to self.
*/
error = xfs_droplink(tp, ip);
if (error)
@@ -2017,14 +1905,14 @@ xfs_remove(
} else {
/*
* When removing a non-directory we need to log the parent
- * inode here for the i_gen update. For a directory this is
- * done implicitly by the xfs_droplink call for the ".." entry.
+ * inode here. For a directory this is done implicitly
+ * by the xfs_droplink call for the ".." entry.
*/
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
}
/*
- * Drop the "." link from ip to self.
+ * Drop the link from dp to ip.
*/
error = xfs_droplink(tp, ip);
if (error)
@@ -2178,7 +2066,6 @@ xfs_link(
if (error)
goto abort_return;
xfs_ichgtime(tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- tdp->i_gen++;
xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);
error = xfs_bumplink(tp, sip);
@@ -2355,18 +2242,10 @@ xfs_mkdir(
}
xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
- /*
- * Bump the in memory version number of the parent directory
- * so that other processes accessing it will recognize that
- * the directory has changed.
- */
- dp->i_gen++;
-
error = xfs_dir_init(tp, cdp, dp);
if (error)
goto error2;
- cdp->i_gen = 1;
error = xfs_bumplink(tp, dp);
if (error)
goto error2;
@@ -2653,13 +2532,6 @@ xfs_symlink(
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
/*
- * Bump the in memory version number of the parent directory
- * so that other processes accessing it will recognize that
- * the directory has changed.
- */
- dp->i_gen++;
-
- /*
* If this is a synchronous mount, make sure that the
* symlink transaction goes to disk before returning to
* the user.
@@ -2809,7 +2681,7 @@ xfs_reclaim(
return 0;
}
- vn_iowait(ip);
+ xfs_ioend_wait(ip);
ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
@@ -2833,122 +2705,10 @@ xfs_reclaim(
if (!ip->i_update_core && (ip->i_itemp == NULL)) {
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_iflock(ip);
- return xfs_finish_reclaim(ip, 1, XFS_IFLUSH_DELWRI_ELSE_SYNC);
- } else {
- xfs_mount_t *mp = ip->i_mount;
-
- /* Protect sync and unpin from us */
- XFS_MOUNT_ILOCK(mp);
- spin_lock(&ip->i_flags_lock);
- __xfs_iflags_set(ip, XFS_IRECLAIMABLE);
- VFS_I(ip)->i_private = NULL;
- ip->i_vnode = NULL;
- spin_unlock(&ip->i_flags_lock);
- list_add_tail(&ip->i_reclaim, &mp->m_del_inodes);
- XFS_MOUNT_IUNLOCK(mp);
- }
- return 0;
-}
-
-int
-xfs_finish_reclaim(
- xfs_inode_t *ip,
- int locked,
- int sync_mode)
-{
- xfs_perag_t *pag = xfs_get_perag(ip->i_mount, ip->i_ino);
- struct inode *vp = VFS_I(ip);
-
- if (vp && VN_BAD(vp))
- goto reclaim;
-
- /* The hash lock here protects a thread in xfs_iget_core from
- * racing with us on linking the inode back with a vnode.
- * Once we have the XFS_IRECLAIM flag set it will not touch
- * us.
- */
- write_lock(&pag->pag_ici_lock);
- spin_lock(&ip->i_flags_lock);
- if (__xfs_iflags_test(ip, XFS_IRECLAIM) ||
- (!__xfs_iflags_test(ip, XFS_IRECLAIMABLE) && vp == NULL)) {
- spin_unlock(&ip->i_flags_lock);
- write_unlock(&pag->pag_ici_lock);
- if (locked) {
- xfs_ifunlock(ip);
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- }
- return 1;
- }
- __xfs_iflags_set(ip, XFS_IRECLAIM);
- spin_unlock(&ip->i_flags_lock);
- write_unlock(&pag->pag_ici_lock);
- xfs_put_perag(ip->i_mount, pag);
-
- /*
- * If the inode is still dirty, then flush it out. If the inode
- * is not in the AIL, then it will be OK to flush it delwri as
- * long as xfs_iflush() does not keep any references to the inode.
- * We leave that decision up to xfs_iflush() since it has the
- * knowledge of whether it's OK to simply do a delwri flush of
- * the inode or whether we need to wait until the inode is
- * pulled from the AIL.
- * We get the flush lock regardless, though, just to make sure
- * we don't free it while it is being flushed.
- */
- if (!locked) {
- xfs_ilock(ip, XFS_ILOCK_EXCL);
- xfs_iflock(ip);
+ xfs_iflags_set(ip, XFS_IRECLAIMABLE);
+ return xfs_reclaim_inode(ip, 1, XFS_IFLUSH_DELWRI_ELSE_SYNC);
}
-
- /*
- * In the case of a forced shutdown we rely on xfs_iflush() to
- * wait for the inode to be unpinned before returning an error.
- */
- if (xfs_iflush(ip, sync_mode) == 0) {
- /* synchronize with xfs_iflush_done */
- xfs_iflock(ip);
- xfs_ifunlock(ip);
- }
-
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
-
- reclaim:
- xfs_ireclaim(ip);
- return 0;
-}
-
-int
-xfs_finish_reclaim_all(xfs_mount_t *mp, int noblock)
-{
- int purged;
- xfs_inode_t *ip, *n;
- int done = 0;
-
- while (!done) {
- purged = 0;
- XFS_MOUNT_ILOCK(mp);
- list_for_each_entry_safe(ip, n, &mp->m_del_inodes, i_reclaim) {
- if (noblock) {
- if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0)
- continue;
- if (xfs_ipincount(ip) ||
- !xfs_iflock_nowait(ip)) {
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
- continue;
- }
- }
- XFS_MOUNT_IUNLOCK(mp);
- if (xfs_finish_reclaim(ip, noblock,
- XFS_IFLUSH_DELWRI_ELSE_ASYNC))
- delay(1);
- purged = 1;
- break;
- }
-
- done = !purged;
- }
-
- XFS_MOUNT_IUNLOCK(mp);
+ xfs_inode_set_reclaim_tag(ip);
return 0;
}
@@ -3197,6 +2957,8 @@ xfs_zero_remaining_bytes(
bp = xfs_buf_get_noaddr(mp->m_sb.sb_blocksize,
XFS_IS_REALTIME_INODE(ip) ?
mp->m_rtdev_targp : mp->m_ddev_targp);
+ if (!bp)
+ return XFS_ERROR(ENOMEM);
for (offset = startoff; offset <= endoff; offset = lastoffset + 1) {
offset_fsb = XFS_B_TO_FSBT(mp, offset);
@@ -3312,7 +3074,8 @@ xfs_free_file_space(
need_iolock = 0;
if (need_iolock) {
xfs_ilock(ip, XFS_IOLOCK_EXCL);
- vn_iowait(ip); /* wait for the completion of any pending DIOs */
+ /* wait for the completion of any pending DIOs */
+ xfs_ioend_wait(ip);
}
rounding = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);
@@ -3474,7 +3237,6 @@ xfs_change_file_space(
int cmd,
xfs_flock64_t *bf,
xfs_off_t offset,
- cred_t *credp,
int attr_flags)
{
xfs_mount_t *mp = ip->i_mount;
@@ -3562,7 +3324,7 @@ xfs_change_file_space(
iattr.ia_valid = ATTR_SIZE;
iattr.ia_size = startoffset;
- error = xfs_setattr(ip, &iattr, attr_flags, credp);
+ error = xfs_setattr(ip, &iattr, attr_flags);
if (error)
return error;
diff --git a/fs/xfs/xfs_vnodeops.h b/fs/xfs/xfs_vnodeops.h
index e932a96bec54..76df328c61b4 100644
--- a/fs/xfs/xfs_vnodeops.h
+++ b/fs/xfs/xfs_vnodeops.h
@@ -14,9 +14,7 @@ struct xfs_inode;
struct xfs_iomap;
-int xfs_open(struct xfs_inode *ip);
-int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags,
- struct cred *credp);
+int xfs_setattr(struct xfs_inode *ip, struct iattr *vap, int flags);
#define XFS_ATTR_DMI 0x01 /* invocation from a DMI function */
#define XFS_ATTR_NONBLOCK 0x02 /* return EAGAIN if operation would block */
#define XFS_ATTR_NOLOCK 0x04 /* Don't grab any conflicting locks */
@@ -28,24 +26,23 @@ int xfs_inactive(struct xfs_inode *ip);
int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode **ipp, struct xfs_name *ci_name);
int xfs_create(struct xfs_inode *dp, struct xfs_name *name, mode_t mode,
- xfs_dev_t rdev, struct xfs_inode **ipp, struct cred *credp);
+ xfs_dev_t rdev, struct xfs_inode **ipp, cred_t *credp);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode *ip);
int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
struct xfs_name *target_name);
int xfs_mkdir(struct xfs_inode *dp, struct xfs_name *dir_name,
- mode_t mode, struct xfs_inode **ipp, struct cred *credp);
+ mode_t mode, struct xfs_inode **ipp, cred_t *credp);
int xfs_readdir(struct xfs_inode *dp, void *dirent, size_t bufsize,
xfs_off_t *offset, filldir_t filldir);
int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,
const char *target_path, mode_t mode, struct xfs_inode **ipp,
- struct cred *credp);
+ cred_t *credp);
int xfs_inode_flush(struct xfs_inode *ip, int flags);
int xfs_set_dmattrs(struct xfs_inode *ip, u_int evmask, u_int16_t state);
int xfs_reclaim(struct xfs_inode *ip);
int xfs_change_file_space(struct xfs_inode *ip, int cmd,
- xfs_flock64_t *bf, xfs_off_t offset,
- struct cred *credp, int attr_flags);
+ xfs_flock64_t *bf, xfs_off_t offset, int attr_flags);
int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
struct xfs_inode *src_ip, struct xfs_inode *target_dp,
struct xfs_name *target_name, struct xfs_inode *target_ip);
@@ -56,8 +53,6 @@ int xfs_attr_set(struct xfs_inode *dp, const char *name, char *value,
int xfs_attr_remove(struct xfs_inode *dp, const char *name, int flags);
int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize,
int flags, struct attrlist_cursor_kern *cursor);
-int xfs_ioctl(struct xfs_inode *ip, struct file *filp,
- int ioflags, unsigned int cmd, void __user *arg);
ssize_t xfs_read(struct xfs_inode *ip, struct kiocb *iocb,
const struct iovec *iovp, unsigned int segs,
loff_t *offset, int ioflags);
@@ -78,5 +73,6 @@ int xfs_flushinval_pages(struct xfs_inode *ip, xfs_off_t first,
xfs_off_t last, int fiopt);
int xfs_flush_pages(struct xfs_inode *ip, xfs_off_t first,
xfs_off_t last, uint64_t flags, int fiopt);
+int xfs_wait_on_pages(struct xfs_inode *ip, xfs_off_t first, xfs_off_t last);
#endif /* _XFS_VNODEOPS_H */
diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h
index 09d33c7740f0..db8852d8bcf7 100644
--- a/include/acpi/acoutput.h
+++ b/include/acpi/acoutput.h
@@ -172,7 +172,7 @@
/* Defaults for debug_level, debug and normal */
-#define ACPI_DEBUG_DEFAULT (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT)
+#define ACPI_DEBUG_DEFAULT (ACPI_LV_INFO)
#define ACPI_NORMAL_DEFAULT (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT)
#define ACPI_DEBUG_ALL (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 029c8c06c151..0515e754449d 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -141,6 +141,10 @@ static inline void *acpi_os_acquire_object(acpi_cache_t * cache)
/*
* We need to show where it is safe to preempt execution of ACPICA
*/
-#define ACPI_PREEMPTION_POINT() cond_resched()
+#define ACPI_PREEMPTION_POINT() \
+ do { \
+ if (!irqs_disabled()) \
+ cond_resched(); \
+ } while (0)
#endif /* __ACLINUX_H__ */
diff --git a/include/asm-arm/plat-s3c/iic.h b/include/asm-arm/plat-s3c/iic.h
deleted file mode 100644
index 5106acaa1d0e..000000000000
--- a/include/asm-arm/plat-s3c/iic.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/iic.h
- *
- * Copyright (c) 2004 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C2410 - I2C Controller platfrom_device info
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef __ASM_ARCH_IIC_H
-#define __ASM_ARCH_IIC_H __FILE__
-
-#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */
-
-/* Notes:
- * 1) All frequencies are expressed in Hz
- * 2) A value of zero is `do not care`
-*/
-
-struct s3c2410_platform_i2c {
- int bus_num; /* bus number to use */
- unsigned int flags;
- unsigned int slave_addr; /* slave address for controller */
- unsigned long bus_freq; /* standard bus frequency */
- unsigned long max_freq; /* max frequency for the bus */
- unsigned long min_freq; /* min frequency for the bus */
- unsigned int sda_delay; /* pclks (s3c2440 only) */
-};
-
-#endif /* __ASM_ARCH_IIC_H */
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 12c07c1866b2..8af276361bf2 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -8,9 +8,17 @@
#ifdef CONFIG_GENERIC_BUG
#ifndef __ASSEMBLY__
struct bug_entry {
+#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
unsigned long bug_addr;
+#else
+ signed int bug_addr_disp;
+#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
+#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
const char *file;
+#else
+ signed int file_disp;
+#endif
unsigned short line;
#endif
unsigned short flags;
@@ -33,15 +41,14 @@ struct bug_entry {
#ifndef __WARN
#ifndef __ASSEMBLY__
-extern void warn_on_slowpath(const char *file, const int line);
extern void warn_slowpath(const char *file, const int line,
const char *fmt, ...) __attribute__((format(printf, 3, 4)));
#define WANT_WARN_ON_SLOWPATH
#endif
-#define __WARN() warn_on_slowpath(__FILE__, __LINE__)
-#define __WARN_printf(arg...) warn_slowpath(__FILE__, __LINE__, arg)
+#define __WARN() warn_slowpath(__FILE__, __LINE__, NULL)
+#define __WARN_printf(arg...) warn_slowpath(__FILE__, __LINE__, arg)
#else
-#define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0)
+#define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0)
#endif
#ifndef WARN_ON
diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h
index 18546d8eb78e..36fa286adad5 100644
--- a/include/asm-generic/memory_model.h
+++ b/include/asm-generic/memory_model.h
@@ -49,7 +49,7 @@
/* memmap is virtually contigious. */
#define __pfn_to_page(pfn) (vmemmap + (pfn))
-#define __page_to_pfn(page) ((page) - vmemmap)
+#define __page_to_pfn(page) (unsigned long)((page) - vmemmap)
#elif defined(CONFIG_SPARSEMEM)
/*
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index ef87f889ef62..72ebe91005a8 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -129,6 +129,10 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres
#define move_pte(pte, prot, old_addr, new_addr) (pte)
#endif
+#ifndef pgprot_writecombine
+#define pgprot_writecombine pgprot_noncached
+#endif
+
/*
* When walking page tables, get the address of the next boundary,
* or the end address of the range if that comes earlier. Although no
@@ -289,6 +293,52 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm,
#define arch_flush_lazy_cpu_mode() do {} while (0)
#endif
+#ifndef __HAVE_PFNMAP_TRACKING
+/*
+ * Interface that can be used by architecture code to keep track of
+ * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn)
+ *
+ * track_pfn_vma_new is called when a _new_ pfn mapping is being established
+ * for physical range indicated by pfn and size.
+ */
+static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
+ unsigned long pfn, unsigned long size)
+{
+ return 0;
+}
+
+/*
+ * Interface that can be used by architecture code to keep track of
+ * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn)
+ *
+ * track_pfn_vma_copy is called when vma that is covering the pfnmap gets
+ * copied through copy_page_range().
+ */
+static inline int track_pfn_vma_copy(struct vm_area_struct *vma)
+{
+ return 0;
+}
+
+/*
+ * Interface that can be used by architecture code to keep track of
+ * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn)
+ *
+ * untrack_pfn_vma is called while unmapping a pfnmap for a region.
+ * untrack can be called for a specific region indicated by pfn and size or
+ * can be for the entire vma (in which case size can be zero).
+ */
+static inline void untrack_pfn_vma(struct vm_area_struct *vma,
+ unsigned long pfn, unsigned long size)
+{
+}
+#else
+extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
+ unsigned long pfn, unsigned long size);
+extern int track_pfn_vma_copy(struct vm_area_struct *vma);
+extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
+ unsigned long size);
+#endif
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASM_GENERIC_PGTABLE_H */
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index eba835a2c2cd..c61fab1dd2f8 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -288,6 +288,16 @@
*(.kprobes.text) \
VMLINUX_SYMBOL(__kprobes_text_end) = .;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+#define IRQENTRY_TEXT \
+ ALIGN_FUNCTION(); \
+ VMLINUX_SYMBOL(__irqentry_text_start) = .; \
+ *(.irqentry.text) \
+ VMLINUX_SYMBOL(__irqentry_text_end) = .;
+#else
+#define IRQENTRY_TEXT
+#endif
+
/* Section used for early init (in .S files) */
#define HEAD_TEXT *(.head.text)
diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h
index 3e8106442d5a..9bde784e7bad 100644
--- a/include/asm-m68k/bitops.h
+++ b/include/asm-m68k/bitops.h
@@ -315,6 +315,11 @@ static inline int fls(int x)
return 32 - cnt;
}
+static inline int __fls(int x)
+{
+ return fls(x) - 1;
+}
+
#include <asm-generic/bitops/fls64.h>
#include <asm-generic/bitops/sched.h>
#include <asm-generic/bitops/hweight.h>
diff --git a/include/asm-m68k/byteorder.h b/include/asm-m68k/byteorder.h
index 81d420b35c80..b354acdafec8 100644
--- a/include/asm-m68k/byteorder.h
+++ b/include/asm-m68k/byteorder.h
@@ -4,22 +4,16 @@
#include <asm/types.h>
#include <linux/compiler.h>
-#ifdef __GNUC__
+#define __BIG_ENDIAN
+#define __SWAB_64_THRU_32__
-static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 val)
+static inline __attribute_const__ __u32 __arch_swab32(__u32 val)
{
__asm__("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
return val;
}
-#define __arch__swab32(x) ___arch__swab32(x)
+#define __arch_swab32 __arch_swab32
-#endif
-
-#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__)
-# define __BYTEORDER_HAS_U64__
-# define __SWAB_64_THRU_32__
-#endif
-
-#include <linux/byteorder/big_endian.h>
+#include <linux/byteorder.h>
#endif /* _M68K_BYTEORDER_H */
diff --git a/include/asm-m68k/machw.h b/include/asm-m68k/machw.h
index 35624998291c..2b4de0c2ce4a 100644
--- a/include/asm-m68k/machw.h
+++ b/include/asm-m68k/machw.h
@@ -26,28 +26,6 @@
#include <linux/types.h>
#if 0
-/* Mac SCSI Controller 5380 */
-
-#define MAC_5380_BAS (0x50F10000) /* This is definitely wrong!! */
-struct MAC_5380 {
- u_char scsi_data;
- u_char char_dummy1;
- u_char scsi_icr;
- u_char char_dummy2;
- u_char scsi_mode;
- u_char char_dummy3;
- u_char scsi_tcr;
- u_char char_dummy4;
- u_char scsi_idstat;
- u_char char_dummy5;
- u_char scsi_dmastat;
- u_char char_dummy6;
- u_char scsi_targrcv;
- u_char char_dummy7;
- u_char scsi_inircv;
-};
-#define mac_scsi ((*(volatile struct MAC_5380 *)MAC_5380_BAS))
-
/*
** SCC Z8530
*/
diff --git a/include/crypto/aes.h b/include/crypto/aes.h
index 40008d67ee3d..656a4c66a568 100644
--- a/include/crypto/aes.h
+++ b/include/crypto/aes.h
@@ -23,10 +23,10 @@ struct crypto_aes_ctx {
u32 key_dec[AES_MAX_KEYLENGTH_U32];
};
-extern u32 crypto_ft_tab[4][256];
-extern u32 crypto_fl_tab[4][256];
-extern u32 crypto_it_tab[4][256];
-extern u32 crypto_il_tab[4][256];
+extern const u32 crypto_ft_tab[4][256];
+extern const u32 crypto_fl_tab[4][256];
+extern const u32 crypto_it_tab[4][256];
+extern const u32 crypto_il_tab[4][256];
int crypto_aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
unsigned int key_len);
diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h
index 60d06e784be3..010545436efa 100644
--- a/include/crypto/algapi.h
+++ b/include/crypto/algapi.h
@@ -22,9 +22,18 @@ struct seq_file;
struct crypto_type {
unsigned int (*ctxsize)(struct crypto_alg *alg, u32 type, u32 mask);
+ unsigned int (*extsize)(struct crypto_alg *alg,
+ const struct crypto_type *frontend);
int (*init)(struct crypto_tfm *tfm, u32 type, u32 mask);
- void (*exit)(struct crypto_tfm *tfm);
+ int (*init_tfm)(struct crypto_tfm *tfm,
+ const struct crypto_type *frontend);
void (*show)(struct seq_file *m, struct crypto_alg *alg);
+ struct crypto_alg *(*lookup)(const char *name, u32 type, u32 mask);
+
+ unsigned int type;
+ unsigned int maskclear;
+ unsigned int maskset;
+ unsigned int tfmsize;
};
struct crypto_instance {
@@ -239,6 +248,11 @@ static inline struct crypto_hash *crypto_spawn_hash(struct crypto_spawn *spawn)
return __crypto_hash_cast(crypto_spawn_tfm(spawn, type, mask));
}
+static inline void *crypto_hash_ctx(struct crypto_hash *tfm)
+{
+ return crypto_tfm_ctx(&tfm->base);
+}
+
static inline void *crypto_hash_ctx_aligned(struct crypto_hash *tfm)
{
return crypto_tfm_ctx_aligned(&tfm->base);
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
index ee48ef8fb2ea..cd16d6e668ce 100644
--- a/include/crypto/hash.h
+++ b/include/crypto/hash.h
@@ -15,10 +15,40 @@
#include <linux/crypto.h>
+struct shash_desc {
+ struct crypto_shash *tfm;
+ u32 flags;
+
+ void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
+struct shash_alg {
+ int (*init)(struct shash_desc *desc);
+ int (*reinit)(struct shash_desc *desc);
+ int (*update)(struct shash_desc *desc, const u8 *data,
+ unsigned int len);
+ int (*final)(struct shash_desc *desc, u8 *out);
+ int (*finup)(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out);
+ int (*digest)(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out);
+ int (*setkey)(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen);
+
+ unsigned int descsize;
+ unsigned int digestsize;
+
+ struct crypto_alg base;
+};
+
struct crypto_ahash {
struct crypto_tfm base;
};
+struct crypto_shash {
+ struct crypto_tfm base;
+};
+
static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm *tfm)
{
return (struct crypto_ahash *)tfm;
@@ -87,6 +117,11 @@ static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
return crypto_ahash_crt(tfm)->reqsize;
}
+static inline void *ahash_request_ctx(struct ahash_request *req)
+{
+ return req->__ctx;
+}
+
static inline int crypto_ahash_setkey(struct crypto_ahash *tfm,
const u8 *key, unsigned int keylen)
{
@@ -101,6 +136,14 @@ static inline int crypto_ahash_digest(struct ahash_request *req)
return crt->digest(req);
}
+static inline void crypto_ahash_export(struct ahash_request *req, u8 *out)
+{
+ memcpy(out, ahash_request_ctx(req),
+ crypto_ahash_reqsize(crypto_ahash_reqtfm(req)));
+}
+
+int crypto_ahash_import(struct ahash_request *req, const u8 *in);
+
static inline int crypto_ahash_init(struct ahash_request *req)
{
struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req));
@@ -169,4 +212,86 @@ static inline void ahash_request_set_crypt(struct ahash_request *req,
req->result = result;
}
+struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
+ u32 mask);
+
+static inline struct crypto_tfm *crypto_shash_tfm(struct crypto_shash *tfm)
+{
+ return &tfm->base;
+}
+
+static inline void crypto_free_shash(struct crypto_shash *tfm)
+{
+ crypto_free_tfm(crypto_shash_tfm(tfm));
+}
+
+static inline unsigned int crypto_shash_alignmask(
+ struct crypto_shash *tfm)
+{
+ return crypto_tfm_alg_alignmask(crypto_shash_tfm(tfm));
+}
+
+static inline struct shash_alg *__crypto_shash_alg(struct crypto_alg *alg)
+{
+ return container_of(alg, struct shash_alg, base);
+}
+
+static inline struct shash_alg *crypto_shash_alg(struct crypto_shash *tfm)
+{
+ return __crypto_shash_alg(crypto_shash_tfm(tfm)->__crt_alg);
+}
+
+static inline unsigned int crypto_shash_digestsize(struct crypto_shash *tfm)
+{
+ return crypto_shash_alg(tfm)->digestsize;
+}
+
+static inline u32 crypto_shash_get_flags(struct crypto_shash *tfm)
+{
+ return crypto_tfm_get_flags(crypto_shash_tfm(tfm));
+}
+
+static inline void crypto_shash_set_flags(struct crypto_shash *tfm, u32 flags)
+{
+ crypto_tfm_set_flags(crypto_shash_tfm(tfm), flags);
+}
+
+static inline void crypto_shash_clear_flags(struct crypto_shash *tfm, u32 flags)
+{
+ crypto_tfm_clear_flags(crypto_shash_tfm(tfm), flags);
+}
+
+static inline unsigned int crypto_shash_descsize(struct crypto_shash *tfm)
+{
+ return crypto_shash_alg(tfm)->descsize;
+}
+
+static inline void *shash_desc_ctx(struct shash_desc *desc)
+{
+ return desc->__ctx;
+}
+
+int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
+ unsigned int keylen);
+int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out);
+
+static inline void crypto_shash_export(struct shash_desc *desc, u8 *out)
+{
+ memcpy(out, shash_desc_ctx(desc), crypto_shash_descsize(desc->tfm));
+}
+
+int crypto_shash_import(struct shash_desc *desc, const u8 *in);
+
+static inline int crypto_shash_init(struct shash_desc *desc)
+{
+ return crypto_shash_alg(desc->tfm)->init(desc);
+}
+
+int crypto_shash_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len);
+int crypto_shash_final(struct shash_desc *desc, u8 *out);
+int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out);
+
#endif /* _CRYPTO_HASH_H */
diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h
index 917ae57bad4a..82b70564bcab 100644
--- a/include/crypto/internal/hash.h
+++ b/include/crypto/internal/hash.h
@@ -39,6 +39,12 @@ extern const struct crypto_type crypto_ahash_type;
int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err);
int crypto_hash_walk_first(struct ahash_request *req,
struct crypto_hash_walk *walk);
+int crypto_hash_walk_first_compat(struct hash_desc *hdesc,
+ struct crypto_hash_walk *walk,
+ struct scatterlist *sg, unsigned int len);
+
+int crypto_register_shash(struct shash_alg *alg);
+int crypto_unregister_shash(struct shash_alg *alg);
static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm)
{
@@ -63,16 +69,16 @@ static inline struct ahash_request *ahash_dequeue_request(
return ahash_request_cast(crypto_dequeue_request(queue));
}
-static inline void *ahash_request_ctx(struct ahash_request *req)
-{
- return req->__ctx;
-}
-
static inline int ahash_tfm_in_queue(struct crypto_queue *queue,
struct crypto_ahash *tfm)
{
return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm));
}
+static inline void *crypto_shash_ctx(struct crypto_shash *tfm)
+{
+ return crypto_tfm_ctx(&tfm->base);
+}
+
#endif /* _CRYPTO_INTERNAL_HASH_H */
diff --git a/include/drm/Kbuild b/include/drm/Kbuild
index 82b6983b7fbb..b940fdfa3b25 100644
--- a/include/drm/Kbuild
+++ b/include/drm/Kbuild
@@ -1,4 +1,4 @@
-unifdef-y += drm.h drm_sarea.h
+unifdef-y += drm.h drm_sarea.h drm_mode.h
unifdef-y += i810_drm.h
unifdef-y += i830_drm.h
unifdef-y += i915_drm.h
diff --git a/include/drm/drm.h b/include/drm/drm.h
index f46ba4b57da4..32e5096554e9 100644
--- a/include/drm/drm.h
+++ b/include/drm/drm.h
@@ -173,6 +173,7 @@ enum drm_map_type {
_DRM_AGP = 3, /**< AGP/GART */
_DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */
_DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */
+ _DRM_GEM = 6, /**< GEM object */
};
/**
@@ -598,6 +599,8 @@ struct drm_gem_open {
uint64_t size;
};
+#include "drm_mode.h"
+
#define DRM_IOCTL_BASE 'd'
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr)
#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type)
@@ -634,6 +637,9 @@ struct drm_gem_open {
#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map)
#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map)
+#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e)
+#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f)
+
#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx)
#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx)
#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx)
@@ -664,6 +670,24 @@ struct drm_gem_open {
#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw)
+#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res)
+#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc)
+#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc)
+#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor)
+#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut)
+#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut)
+#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder)
+#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector)
+#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd)
+#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd)
+
+#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property)
+#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property)
+#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob)
+#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
+#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
+#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int)
+
/**
* Device specific ioctls should only be in their respective headers
* The device specific ioctl range is from 0x40 to 0x99.
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index d5e8e5c89548..afb7858c068d 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -105,6 +105,7 @@ struct drm_device;
#define DRIVER_FB_DMA 0x400
#define DRIVER_IRQ_VBL2 0x800
#define DRIVER_GEM 0x1000
+#define DRIVER_MODESET 0x2000
/***********************************************************************/
/** \name Begin the DRM... */
@@ -238,11 +239,11 @@ struct drm_device;
*/
#define LOCK_TEST_WITH_RETURN( dev, file_priv ) \
do { \
- if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) || \
- dev->lock.file_priv != file_priv ) { \
+ if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock) || \
+ file_priv->master->lock.file_priv != file_priv) { \
DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\
- __func__, _DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ),\
- dev->lock.file_priv, file_priv ); \
+ __func__, _DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock),\
+ file_priv->master->lock.file_priv, file_priv); \
return -EINVAL; \
} \
} while (0)
@@ -276,6 +277,7 @@ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
#define DRM_AUTH 0x1
#define DRM_MASTER 0x2
#define DRM_ROOT_ONLY 0x4
+#define DRM_CONTROL_ALLOW 0x8
struct drm_ioctl_desc {
unsigned int cmd;
@@ -379,21 +381,26 @@ struct drm_buf_entry {
/** File private data */
struct drm_file {
int authenticated;
- int master;
pid_t pid;
uid_t uid;
drm_magic_t magic;
unsigned long ioctl_count;
struct list_head lhead;
struct drm_minor *minor;
- int remove_auth_on_close;
unsigned long lock_count;
+
/** Mapping of mm object handles to object pointers. */
struct idr object_idr;
/** Lock for synchronization of access to object_idr. */
spinlock_t table_lock;
+
struct file *filp;
void *driver_priv;
+
+ int is_master; /* this file private is a master for a minor */
+ struct drm_master *master; /* master this node is currently associated with
+ N.B. not always minor->master */
+ struct list_head fbs;
};
/** Wait queue */
@@ -523,6 +530,8 @@ struct drm_map_list {
struct drm_hash_item hash;
struct drm_map *map; /**< mapping */
uint64_t user_token;
+ struct drm_master *master;
+ struct drm_mm_node *file_offset_node; /**< fake offset */
};
typedef struct drm_map drm_local_map_t;
@@ -563,6 +572,14 @@ struct drm_ati_pcigart_info {
};
/**
+ * GEM specific mm private for tracking GEM objects
+ */
+struct drm_gem_mm {
+ struct drm_mm offset_manager; /**< Offset mgmt for buffer objects */
+ struct drm_open_hash offset_hash; /**< User token hash table for maps */
+};
+
+/**
* This structure defines the drm_mm memory object, which will be used by the
* DRM for its buffer objects.
*/
@@ -579,6 +596,9 @@ struct drm_gem_object {
/** File representing the shmem storage */
struct file *filp;
+ /* Mapping info for this object */
+ struct drm_map_list map_list;
+
/**
* Size of the object, in bytes. Immutable over the object's
* lifetime.
@@ -612,6 +632,33 @@ struct drm_gem_object {
void *driver_private;
};
+#include "drm_crtc.h"
+
+/* per-master structure */
+struct drm_master {
+
+ struct kref refcount; /* refcount for this master */
+
+ struct list_head head; /**< each minor contains a list of masters */
+ struct drm_minor *minor; /**< link back to minor we are a master for */
+
+ char *unique; /**< Unique identifier: e.g., busid */
+ int unique_len; /**< Length of unique field */
+ int unique_size; /**< amount allocated */
+
+ int blocked; /**< Blocked due to VC switch? */
+
+ /** \name Authentication */
+ /*@{ */
+ struct drm_open_hash magiclist;
+ struct list_head magicfree;
+ /*@} */
+
+ struct drm_lock_data lock; /**< Information on hardware lock */
+
+ void *driver_priv; /**< Private structure for driver to use */
+};
+
/**
* DRM driver structure. This structure represent the common code for
* a family of cards. There will one drm_device for each card present
@@ -712,6 +759,10 @@ struct drm_driver {
void (*set_version) (struct drm_device *dev,
struct drm_set_version *sv);
+ /* Master routines */
+ int (*master_create)(struct drm_device *dev, struct drm_master *master);
+ void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
+
int (*proc_init)(struct drm_minor *minor);
void (*proc_cleanup)(struct drm_minor *minor);
@@ -724,6 +775,9 @@ struct drm_driver {
int (*gem_init_object) (struct drm_gem_object *obj);
void (*gem_free_object) (struct drm_gem_object *obj);
+ /* Driver private ops for this object */
+ struct vm_operations_struct *gem_vm_ops;
+
int major;
int minor;
int patchlevel;
@@ -737,10 +791,14 @@ struct drm_driver {
int num_ioctls;
struct file_operations fops;
struct pci_driver pci_driver;
+ /* List of devices hanging off this driver */
+ struct list_head device_list;
};
#define DRM_MINOR_UNASSIGNED 0
#define DRM_MINOR_LEGACY 1
+#define DRM_MINOR_CONTROL 2
+#define DRM_MINOR_RENDER 3
/**
* DRM minor structure. This structure represents a drm minor number.
@@ -752,6 +810,9 @@ struct drm_minor {
struct device kdev; /**< Linux device */
struct drm_device *dev;
struct proc_dir_entry *dev_root; /**< proc directory entry */
+ struct drm_master *master; /* currently active master for this node */
+ struct list_head master_list;
+ struct drm_mode_group mode_group;
};
/**
@@ -759,13 +820,10 @@ struct drm_minor {
* may contain multiple heads.
*/
struct drm_device {
- char *unique; /**< Unique identifier: e.g., busid */
- int unique_len; /**< Length of unique field */
+ struct list_head driver_item; /**< list of devices per driver */
char *devname; /**< For /proc/interrupts */
int if_version; /**< Highest interface version set */
- int blocked; /**< Blocked due to VC switch? */
-
/** \name Locks */
/*@{ */
spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */
@@ -788,12 +846,7 @@ struct drm_device {
atomic_t counts[15];
/*@} */
- /** \name Authentication */
- /*@{ */
struct list_head filelist;
- struct drm_open_hash magiclist; /**< magic hash table */
- struct list_head magicfree;
- /*@} */
/** \name Memory management */
/*@{ */
@@ -810,7 +863,7 @@ struct drm_device {
struct idr ctx_idr;
struct list_head vmalist; /**< List of vmas (for debugging) */
- struct drm_lock_data lock; /**< Information on hardware lock */
+
/*@} */
/** \name DMA queues (contexts) */
@@ -858,6 +911,7 @@ struct drm_device {
int *vblank_enabled; /* so we don't call enable more than
once per disable */
int *vblank_inmodeset; /* Display driver is setting mode */
+ u32 *last_vblank_wait; /* Last vblank seqno waited per CRTC */
struct timer_list vblank_disable_timer;
u32 max_vblank_count; /**< size of vblank counter register */
@@ -881,12 +935,15 @@ struct drm_device {
struct drm_sg_mem *sg; /**< Scatter gather memory */
int num_crtcs; /**< Number of CRTCs on this device */
void *dev_private; /**< device private data */
+ void *mm_private;
+ struct address_space *dev_mapping;
struct drm_sigdata sigdata; /**< For block_all_signals */
sigset_t sigmask;
struct drm_driver *driver;
drm_local_map_t *agp_buffer_map;
unsigned int agp_buffer_token;
+ struct drm_minor *control; /**< Control node for card */
struct drm_minor *primary; /**< render type primary screen head */
/** \name Drawable information */
@@ -895,6 +952,8 @@ struct drm_device {
struct idr drw_idr;
/*@} */
+ struct drm_mode_config mode_config; /**< Current mode config */
+
/** \name GEM information */
/*@{ */
spinlock_t object_name_lock;
@@ -997,6 +1056,8 @@ extern int drm_release(struct inode *inode, struct file *filp);
/* Mapping support (drm_vm.h) */
extern int drm_mmap(struct file *filp, struct vm_area_struct *vma);
+extern int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma);
+extern void drm_vm_open_locked(struct vm_area_struct *vma);
extern unsigned long drm_core_get_map_ofs(struct drm_map * map);
extern unsigned long drm_core_get_reg_ofs(struct drm_device *dev);
extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
@@ -1153,6 +1214,8 @@ extern int drm_vblank_get(struct drm_device *dev, int crtc);
extern void drm_vblank_put(struct drm_device *dev, int crtc);
extern void drm_vblank_cleanup(struct drm_device *dev);
/* Modesetting support */
+extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
+extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
extern int drm_modeset_ctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -1189,6 +1252,13 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle);
extern void drm_agp_chipset_flush(struct drm_device *dev);
/* Stub support (drm_stub.h) */
+extern int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+struct drm_master *drm_master_create(struct drm_minor *minor);
+extern struct drm_master *drm_master_get(struct drm_master *master);
+extern void drm_master_put(struct drm_master **master);
extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver);
extern int drm_put_dev(struct drm_device *dev);
@@ -1231,7 +1301,11 @@ struct drm_sysfs_class;
extern struct class *drm_sysfs_create(struct module *owner, char *name);
extern void drm_sysfs_destroy(void);
extern int drm_sysfs_device_add(struct drm_minor *minor);
+extern void drm_sysfs_hotplug_event(struct drm_device *dev);
extern void drm_sysfs_device_remove(struct drm_minor *minor);
+extern char *drm_get_connector_status_name(enum drm_connector_status status);
+extern int drm_sysfs_connector_add(struct drm_connector *connector);
+extern void drm_sysfs_connector_remove(struct drm_connector *connector);
/*
* Basic memory manager support (drm_mm.c)
@@ -1251,10 +1325,12 @@ extern int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size);
/* Graphics Execution Manager library functions (drm_gem.c) */
int drm_gem_init(struct drm_device *dev);
+void drm_gem_destroy(struct drm_device *dev);
void drm_gem_object_free(struct kref *kref);
struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev,
size_t size);
void drm_gem_object_handle_free(struct kref *kref);
+int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
static inline void
drm_gem_object_reference(struct drm_gem_object *obj)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
new file mode 100644
index 000000000000..0acb07f31fa4
--- /dev/null
+++ b/include/drm/drm_crtc.h
@@ -0,0 +1,733 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007-2008 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __DRM_CRTC_H__
+#define __DRM_CRTC_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <linux/fb.h>
+
+struct drm_device;
+struct drm_mode_set;
+struct drm_framebuffer;
+
+
+#define DRM_MODE_OBJECT_CRTC 0xcccccccc
+#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
+#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
+#define DRM_MODE_OBJECT_MODE 0xdededede
+#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
+#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
+#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
+
+struct drm_mode_object {
+ uint32_t id;
+ uint32_t type;
+};
+
+/*
+ * Note on terminology: here, for brevity and convenience, we refer to connector
+ * control chips as 'CRTCs'. They can control any type of connector, VGA, LVDS,
+ * DVI, etc. And 'screen' refers to the whole of the visible display, which
+ * may span multiple monitors (and therefore multiple CRTC and connector
+ * structures).
+ */
+
+enum drm_mode_status {
+ MODE_OK = 0, /* Mode OK */
+ MODE_HSYNC, /* hsync out of range */
+ MODE_VSYNC, /* vsync out of range */
+ MODE_H_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_V_ILLEGAL, /* mode has illegal horizontal timings */
+ MODE_BAD_WIDTH, /* requires an unsupported linepitch */
+ MODE_NOMODE, /* no mode with a maching name */
+ MODE_NO_INTERLACE, /* interlaced mode not supported */
+ MODE_NO_DBLESCAN, /* doublescan mode not supported */
+ MODE_NO_VSCAN, /* multiscan mode not supported */
+ MODE_MEM, /* insufficient video memory */
+ MODE_VIRTUAL_X, /* mode width too large for specified virtual size */
+ MODE_VIRTUAL_Y, /* mode height too large for specified virtual size */
+ MODE_MEM_VIRT, /* insufficient video memory given virtual size */
+ MODE_NOCLOCK, /* no fixed clock available */
+ MODE_CLOCK_HIGH, /* clock required is too high */
+ MODE_CLOCK_LOW, /* clock required is too low */
+ MODE_CLOCK_RANGE, /* clock/mode isn't in a ClockRange */
+ MODE_BAD_HVALUE, /* horizontal timing was out of range */
+ MODE_BAD_VVALUE, /* vertical timing was out of range */
+ MODE_BAD_VSCAN, /* VScan value out of range */
+ MODE_HSYNC_NARROW, /* horizontal sync too narrow */
+ MODE_HSYNC_WIDE, /* horizontal sync too wide */
+ MODE_HBLANK_NARROW, /* horizontal blanking too narrow */
+ MODE_HBLANK_WIDE, /* horizontal blanking too wide */
+ MODE_VSYNC_NARROW, /* vertical sync too narrow */
+ MODE_VSYNC_WIDE, /* vertical sync too wide */
+ MODE_VBLANK_NARROW, /* vertical blanking too narrow */
+ MODE_VBLANK_WIDE, /* vertical blanking too wide */
+ MODE_PANEL, /* exceeds panel dimensions */
+ MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */
+ MODE_ONE_WIDTH, /* only one width is supported */
+ MODE_ONE_HEIGHT, /* only one height is supported */
+ MODE_ONE_SIZE, /* only one resolution is supported */
+ MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */
+ MODE_UNVERIFIED = -3, /* mode needs to reverified */
+ MODE_BAD = -2, /* unspecified reason */
+ MODE_ERROR = -1 /* error condition */
+};
+
+#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \
+ DRM_MODE_TYPE_CRTC_C)
+
+#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \
+ .name = nm, .status = 0, .type = (t), .clock = (c), \
+ .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \
+ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \
+ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
+ .vscan = (vs), .flags = (f), .vrefresh = 0
+
+#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+
+struct drm_display_mode {
+ /* Header */
+ struct list_head head;
+ struct drm_mode_object base;
+
+ char name[DRM_DISPLAY_MODE_LEN];
+
+ int connector_count;
+ enum drm_mode_status status;
+ int type;
+
+ /* Proposed mode values */
+ int clock;
+ int hdisplay;
+ int hsync_start;
+ int hsync_end;
+ int htotal;
+ int hskew;
+ int vdisplay;
+ int vsync_start;
+ int vsync_end;
+ int vtotal;
+ int vscan;
+ unsigned int flags;
+
+ /* Addressable image size (may be 0 for projectors, etc.) */
+ int width_mm;
+ int height_mm;
+
+ /* Actual mode we give to hw */
+ int clock_index;
+ int synth_clock;
+ int crtc_hdisplay;
+ int crtc_hblank_start;
+ int crtc_hblank_end;
+ int crtc_hsync_start;
+ int crtc_hsync_end;
+ int crtc_htotal;
+ int crtc_hskew;
+ int crtc_vdisplay;
+ int crtc_vblank_start;
+ int crtc_vblank_end;
+ int crtc_vsync_start;
+ int crtc_vsync_end;
+ int crtc_vtotal;
+ int crtc_hadjusted;
+ int crtc_vadjusted;
+
+ /* Driver private mode info */
+ int private_size;
+ int *private;
+ int private_flags;
+
+ int vrefresh;
+ float hsync;
+};
+
+enum drm_connector_status {
+ connector_status_connected = 1,
+ connector_status_disconnected = 2,
+ connector_status_unknown = 3,
+};
+
+enum subpixel_order {
+ SubPixelUnknown = 0,
+ SubPixelHorizontalRGB,
+ SubPixelHorizontalBGR,
+ SubPixelVerticalRGB,
+ SubPixelVerticalBGR,
+ SubPixelNone,
+};
+
+
+/*
+ * Describes a given display (e.g. CRT or flat panel) and its limitations.
+ */
+struct drm_display_info {
+ char name[DRM_DISPLAY_INFO_LEN];
+ /* Input info */
+ bool serration_vsync;
+ bool sync_on_green;
+ bool composite_sync;
+ bool separate_syncs;
+ bool blank_to_black;
+ unsigned char video_level;
+ bool digital;
+ /* Physical size */
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ /* Display parameters */
+ unsigned char gamma; /* FIXME: storage format */
+ bool gtf_supported;
+ bool standard_color;
+ enum {
+ monochrome = 0,
+ rgb,
+ other,
+ unknown,
+ } display_type;
+ bool active_off_supported;
+ bool suspend_supported;
+ bool standby_supported;
+
+ /* Color info FIXME: storage format */
+ unsigned short redx, redy;
+ unsigned short greenx, greeny;
+ unsigned short bluex, bluey;
+ unsigned short whitex, whitey;
+
+ /* Clock limits FIXME: storage format */
+ unsigned int min_vfreq, max_vfreq;
+ unsigned int min_hfreq, max_hfreq;
+ unsigned int pixel_clock;
+
+ /* White point indices FIXME: storage format */
+ unsigned int wpx1, wpy1;
+ unsigned int wpgamma1;
+ unsigned int wpx2, wpy2;
+ unsigned int wpgamma2;
+
+ enum subpixel_order subpixel_order;
+
+ char *raw_edid; /* if any */
+};
+
+struct drm_framebuffer_funcs {
+ void (*destroy)(struct drm_framebuffer *framebuffer);
+ int (*create_handle)(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle);
+};
+
+struct drm_framebuffer {
+ struct drm_device *dev;
+ struct list_head head;
+ struct drm_mode_object base;
+ const struct drm_framebuffer_funcs *funcs;
+ unsigned int pitch;
+ unsigned int width;
+ unsigned int height;
+ /* depth can be 15 or 16 */
+ unsigned int depth;
+ int bits_per_pixel;
+ int flags;
+ void *fbdev;
+ u32 pseudo_palette[17];
+ struct list_head filp_head;
+};
+
+struct drm_property_blob {
+ struct drm_mode_object base;
+ struct list_head head;
+ unsigned int length;
+ void *data;
+};
+
+struct drm_property_enum {
+ uint64_t value;
+ struct list_head head;
+ char name[DRM_PROP_NAME_LEN];
+};
+
+struct drm_property {
+ struct list_head head;
+ struct drm_mode_object base;
+ uint32_t flags;
+ char name[DRM_PROP_NAME_LEN];
+ uint32_t num_values;
+ uint64_t *values;
+
+ struct list_head enum_blob_list;
+};
+
+struct drm_crtc;
+struct drm_connector;
+struct drm_encoder;
+
+/**
+ * drm_crtc_funcs - control CRTCs for a given device
+ * @dpms: control display power levels
+ * @save: save CRTC state
+ * @resore: restore CRTC state
+ * @lock: lock the CRTC
+ * @unlock: unlock the CRTC
+ * @shadow_allocate: allocate shadow pixmap
+ * @shadow_create: create shadow pixmap for rotation support
+ * @shadow_destroy: free shadow pixmap
+ * @mode_fixup: fixup proposed mode
+ * @mode_set: set the desired mode on the CRTC
+ * @gamma_set: specify color ramp for CRTC
+ * @destroy: deinit and free object.
+ *
+ * The drm_crtc_funcs structure is the central CRTC management structure
+ * in the DRM. Each CRTC controls one or more connectors (note that the name
+ * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc.
+ * connectors, not just CRTs).
+ *
+ * Each driver is responsible for filling out this structure at startup time,
+ * in addition to providing other modesetting features, like i2c and DDC
+ * bus accessors.
+ */
+struct drm_crtc_funcs {
+ /* Save CRTC state */
+ void (*save)(struct drm_crtc *crtc); /* suspend? */
+ /* Restore CRTC state */
+ void (*restore)(struct drm_crtc *crtc); /* resume? */
+
+ /* cursor controls */
+ int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
+ uint32_t handle, uint32_t width, uint32_t height);
+ int (*cursor_move)(struct drm_crtc *crtc, int x, int y);
+
+ /* Set gamma on the CRTC */
+ void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+ uint32_t size);
+ /* Object destroy routine */
+ void (*destroy)(struct drm_crtc *crtc);
+
+ int (*set_config)(struct drm_mode_set *set);
+};
+
+/**
+ * drm_crtc - central CRTC control structure
+ * @enabled: is this CRTC enabled?
+ * @x: x position on screen
+ * @y: y position on screen
+ * @desired_mode: new desired mode
+ * @desired_x: desired x for desired_mode
+ * @desired_y: desired y for desired_mode
+ * @funcs: CRTC control functions
+ *
+ * Each CRTC may have one or more connectors associated with it. This structure
+ * allows the CRTC to be controlled.
+ */
+struct drm_crtc {
+ struct drm_device *dev;
+ struct list_head head;
+
+ struct drm_mode_object base;
+
+ /* framebuffer the connector is currently bound to */
+ struct drm_framebuffer *fb;
+
+ bool enabled;
+
+ struct drm_display_mode mode;
+
+ int x, y;
+ struct drm_display_mode *desired_mode;
+ int desired_x, desired_y;
+ const struct drm_crtc_funcs *funcs;
+
+ /* CRTC gamma size for reporting to userspace */
+ uint32_t gamma_size;
+ uint16_t *gamma_store;
+
+ /* if you are using the helper */
+ void *helper_private;
+};
+
+
+/**
+ * drm_connector_funcs - control connectors on a given device
+ * @dpms: set power state (see drm_crtc_funcs above)
+ * @save: save connector state
+ * @restore: restore connector state
+ * @mode_valid: is this mode valid on the given connector?
+ * @mode_fixup: try to fixup proposed mode for this connector
+ * @mode_set: set this mode
+ * @detect: is this connector active?
+ * @get_modes: get mode list for this connector
+ * @set_property: property for this connector may need update
+ * @destroy: make object go away
+ *
+ * Each CRTC may have one or more connectors attached to it. The functions
+ * below allow the core DRM code to control connectors, enumerate available modes,
+ * etc.
+ */
+struct drm_connector_funcs {
+ void (*dpms)(struct drm_connector *connector, int mode);
+ void (*save)(struct drm_connector *connector);
+ void (*restore)(struct drm_connector *connector);
+ enum drm_connector_status (*detect)(struct drm_connector *connector);
+ void (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
+ int (*set_property)(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val);
+ void (*destroy)(struct drm_connector *connector);
+};
+
+struct drm_encoder_funcs {
+ void (*destroy)(struct drm_encoder *encoder);
+};
+
+#define DRM_CONNECTOR_MAX_UMODES 16
+#define DRM_CONNECTOR_MAX_PROPERTY 16
+#define DRM_CONNECTOR_LEN 32
+#define DRM_CONNECTOR_MAX_ENCODER 2
+
+/**
+ * drm_encoder - central DRM encoder structure
+ */
+struct drm_encoder {
+ struct drm_device *dev;
+ struct list_head head;
+
+ struct drm_mode_object base;
+ int encoder_type;
+ uint32_t possible_crtcs;
+ uint32_t possible_clones;
+
+ struct drm_crtc *crtc;
+ const struct drm_encoder_funcs *funcs;
+ void *helper_private;
+};
+
+/**
+ * drm_connector - central DRM connector control structure
+ * @crtc: CRTC this connector is currently connected to, NULL if none
+ * @interlace_allowed: can this connector handle interlaced modes?
+ * @doublescan_allowed: can this connector handle doublescan?
+ * @available_modes: modes available on this connector (from get_modes() + user)
+ * @initial_x: initial x position for this connector
+ * @initial_y: initial y position for this connector
+ * @status: connector connected?
+ * @funcs: connector control functions
+ *
+ * Each connector may be connected to one or more CRTCs, or may be clonable by
+ * another connector if they can share a CRTC. Each connector also has a specific
+ * position in the broader display (referred to as a 'screen' though it could
+ * span multiple monitors).
+ */
+struct drm_connector {
+ struct drm_device *dev;
+ struct device kdev;
+ struct device_attribute *attr;
+ struct list_head head;
+
+ struct drm_mode_object base;
+
+ int connector_type;
+ int connector_type_id;
+ bool interlace_allowed;
+ bool doublescan_allowed;
+ struct list_head modes; /* list of modes on this connector */
+
+ int initial_x, initial_y;
+ enum drm_connector_status status;
+
+ /* these are modes added by probing with DDC or the BIOS */
+ struct list_head probed_modes;
+
+ struct drm_display_info display_info;
+ const struct drm_connector_funcs *funcs;
+
+ struct list_head user_modes;
+ struct drm_property_blob *edid_blob_ptr;
+ u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY];
+ uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY];
+
+ void *helper_private;
+
+ uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
+ uint32_t force_encoder_id;
+ struct drm_encoder *encoder; /* currently active encoder */
+};
+
+/**
+ * struct drm_mode_set
+ *
+ * Represents a single crtc the connectors that it drives with what mode
+ * and from which framebuffer it scans out from.
+ *
+ * This is used to set modes.
+ */
+struct drm_mode_set {
+ struct list_head head;
+
+ struct drm_framebuffer *fb;
+ struct drm_crtc *crtc;
+ struct drm_display_mode *mode;
+
+ uint32_t x;
+ uint32_t y;
+
+ struct drm_connector **connectors;
+ size_t num_connectors;
+};
+
+/**
+ * struct drm_mode_config_funcs - configure CRTCs for a given screen layout
+ * @resize: adjust CRTCs as necessary for the proposed layout
+ *
+ * Currently only a resize hook is available. DRM will call back into the
+ * driver with a new screen width and height. If the driver can't support
+ * the proposed size, it can return false. Otherwise it should adjust
+ * the CRTC<->connector mappings as needed and update its view of the screen.
+ */
+struct drm_mode_config_funcs {
+ struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd);
+ int (*fb_changed)(struct drm_device *dev);
+};
+
+struct drm_mode_group {
+ uint32_t num_crtcs;
+ uint32_t num_encoders;
+ uint32_t num_connectors;
+
+ /* list of object IDs for this group */
+ uint32_t *id_list;
+};
+
+/**
+ * drm_mode_config - Mode configuration control structure
+ *
+ */
+struct drm_mode_config {
+ struct mutex mutex; /* protects configuration and IDR */
+ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
+ /* this is limited to one for now */
+ int num_fb;
+ struct list_head fb_list;
+ int num_connector;
+ struct list_head connector_list;
+ int num_encoder;
+ struct list_head encoder_list;
+
+ int num_crtc;
+ struct list_head crtc_list;
+
+ struct list_head property_list;
+
+ /* in-kernel framebuffers - hung of filp_head in drm_framebuffer */
+ struct list_head fb_kernel_list;
+
+ int min_width, min_height;
+ int max_width, max_height;
+ struct drm_mode_config_funcs *funcs;
+ unsigned long fb_base;
+
+ /* pointers to standard properties */
+ struct list_head property_blob_list;
+ struct drm_property *edid_property;
+ struct drm_property *dpms_property;
+
+ /* DVI-I properties */
+ struct drm_property *dvi_i_subconnector_property;
+ struct drm_property *dvi_i_select_subconnector_property;
+
+ /* TV properties */
+ struct drm_property *tv_subconnector_property;
+ struct drm_property *tv_select_subconnector_property;
+ struct drm_property *tv_mode_property;
+ struct drm_property *tv_left_margin_property;
+ struct drm_property *tv_right_margin_property;
+ struct drm_property *tv_top_margin_property;
+ struct drm_property *tv_bottom_margin_property;
+
+ /* Optional properties */
+ struct drm_property *scaling_mode_property;
+ struct drm_property *dithering_mode_property;
+};
+
+#define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
+#define obj_to_connector(x) container_of(x, struct drm_connector, base)
+#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
+#define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
+#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base)
+#define obj_to_property(x) container_of(x, struct drm_property, base)
+#define obj_to_blob(x) container_of(x, struct drm_property_blob, base)
+
+
+extern void drm_crtc_init(struct drm_device *dev,
+ struct drm_crtc *crtc,
+ const struct drm_crtc_funcs *funcs);
+extern void drm_crtc_cleanup(struct drm_crtc *crtc);
+
+extern void drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type);
+
+extern void drm_connector_cleanup(struct drm_connector *connector);
+
+extern void drm_encoder_init(struct drm_device *dev,
+ struct drm_encoder *encoder,
+ const struct drm_encoder_funcs *funcs,
+ int encoder_type);
+
+extern void drm_encoder_cleanup(struct drm_encoder *encoder);
+
+extern char *drm_get_connector_name(struct drm_connector *connector);
+extern char *drm_get_dpms_name(int val);
+extern char *drm_get_dvi_i_subconnector_name(int val);
+extern char *drm_get_dvi_i_select_name(int val);
+extern char *drm_get_tv_subconnector_name(int val);
+extern char *drm_get_tv_select_name(int val);
+extern void drm_fb_release(struct file *filp);
+extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
+extern struct edid *drm_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter);
+extern unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter);
+extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
+extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
+extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
+extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
+ struct drm_display_mode *mode);
+extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode);
+extern void drm_mode_config_init(struct drm_device *dev);
+extern void drm_mode_config_cleanup(struct drm_device *dev);
+extern void drm_mode_set_name(struct drm_display_mode *mode);
+extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2);
+extern int drm_mode_width(struct drm_display_mode *mode);
+extern int drm_mode_height(struct drm_display_mode *mode);
+
+/* for us by fb module */
+extern int drm_mode_attachmode_crtc(struct drm_device *dev,
+ struct drm_crtc *crtc,
+ struct drm_display_mode *mode);
+extern int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode);
+
+extern struct drm_display_mode *drm_mode_create(struct drm_device *dev);
+extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
+extern void drm_mode_list_concat(struct list_head *head,
+ struct list_head *new);
+extern void drm_mode_validate_size(struct drm_device *dev,
+ struct list_head *mode_list,
+ int maxX, int maxY, int maxPitch);
+extern void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose);
+extern void drm_mode_sort(struct list_head *mode_list);
+extern int drm_mode_vrefresh(struct drm_display_mode *mode);
+extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
+ int adjust_flags);
+extern void drm_mode_connector_list_update(struct drm_connector *connector);
+extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+ struct edid *edid);
+extern int drm_connector_property_set_value(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value);
+extern int drm_connector_property_get_value(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t *value);
+extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev);
+extern void drm_framebuffer_set_object(struct drm_device *dev,
+ unsigned long handle);
+extern int drm_framebuffer_init(struct drm_device *dev,
+ struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs);
+extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
+extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
+extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY);
+extern bool drm_crtc_in_use(struct drm_crtc *crtc);
+
+extern int drm_connector_attach_property(struct drm_connector *connector,
+ struct drm_property *property, uint64_t init_val);
+extern struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+ const char *name, int num_values);
+extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
+extern int drm_property_add_enum(struct drm_property *property, int index,
+ uint64_t value, const char *name);
+extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
+extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats,
+ char *formats[]);
+extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
+extern int drm_mode_create_dithering_property(struct drm_device *dev);
+extern char *drm_get_encoder_name(struct drm_encoder *encoder);
+
+extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder);
+extern void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder);
+extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size);
+extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
+/* IOCTLs */
+extern int drm_mode_getresources(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+extern int drm_mode_getcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getconnector(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_setcrtc(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_cursor_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_addfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_addmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_attachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_detachmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+
+extern int drm_mode_getproperty_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getblob_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_hotplug_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_replacefb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_getencoder(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+#endif /* __DRM_CRTC_H__ */
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
new file mode 100644
index 000000000000..4bc04cf460a7
--- /dev/null
+++ b/include/drm/drm_crtc_helper.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2006 Keith Packard
+ * Copyright © 2007-2008 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The DRM mode setting helper functions are common code for drivers to use if
+ * they wish. Drivers are not forced to use this code in their
+ * implementations but it would be useful if they code they do use at least
+ * provides a consistent interface and operation to userspace
+ */
+
+#ifndef __DRM_CRTC_HELPER_H__
+#define __DRM_CRTC_HELPER_H__
+
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/idr.h>
+
+#include <linux/fb.h>
+
+struct drm_crtc_helper_funcs {
+ /*
+ * Control power levels on the CRTC. If the mode passed in is
+ * unsupported, the provider must use the next lowest power level.
+ */
+ void (*dpms)(struct drm_crtc *crtc, int mode);
+ void (*prepare)(struct drm_crtc *crtc);
+ void (*commit)(struct drm_crtc *crtc);
+
+ /* Provider can fixup or change mode timings before modeset occurs */
+ bool (*mode_fixup)(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ /* Actually set the mode */
+ void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode, int x, int y,
+ struct drm_framebuffer *old_fb);
+
+ /* Move the crtc on the current fb to the given position *optional* */
+ void (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb);
+};
+
+struct drm_encoder_helper_funcs {
+ void (*dpms)(struct drm_encoder *encoder, int mode);
+ void (*save)(struct drm_encoder *encoder);
+ void (*restore)(struct drm_encoder *encoder);
+
+ bool (*mode_fixup)(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ void (*prepare)(struct drm_encoder *encoder);
+ void (*commit)(struct drm_encoder *encoder);
+ void (*mode_set)(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+ /* detect for DAC style encoders */
+ enum drm_connector_status (*detect)(struct drm_encoder *encoder,
+ struct drm_connector *connector);
+};
+
+struct drm_connector_helper_funcs {
+ int (*get_modes)(struct drm_connector *connector);
+ int (*mode_valid)(struct drm_connector *connector,
+ struct drm_display_mode *mode);
+ struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
+};
+
+extern void drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
+extern void drm_helper_disable_unused_functions(struct drm_device *dev);
+extern int drm_helper_hotplug_stage_two(struct drm_device *dev);
+extern bool drm_helper_initial_config(struct drm_device *dev, bool can_grow);
+extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
+extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb);
+extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc);
+
+extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+ struct drm_mode_fb_cmd *mode_cmd);
+
+static inline void drm_crtc_helper_add(struct drm_crtc *crtc,
+ const struct drm_crtc_helper_funcs *funcs)
+{
+ crtc->helper_private = (void *)funcs;
+}
+
+static inline void drm_encoder_helper_add(struct drm_encoder *encoder,
+ const struct drm_encoder_helper_funcs *funcs)
+{
+ encoder->helper_private = (void *)funcs;
+}
+
+static inline void drm_connector_helper_add(struct drm_connector *connector,
+ const struct drm_connector_helper_funcs *funcs)
+{
+ connector->helper_private = (void *)funcs;
+}
+
+extern int drm_helper_resume_force_mode(struct drm_device *dev);
+#endif
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
new file mode 100644
index 000000000000..c707c15f5164
--- /dev/null
+++ b/include/drm/drm_edid.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright © 2007-2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __DRM_EDID_H__
+#define __DRM_EDID_H__
+
+#include <linux/types.h>
+
+#define EDID_LENGTH 128
+#define DDC_ADDR 0x50
+
+#ifdef BIG_ENDIAN
+#error "EDID structure is little endian, need big endian versions"
+#else
+
+struct est_timings {
+ u8 t1;
+ u8 t2;
+ u8 mfg_rsvd;
+} __attribute__((packed));
+
+struct std_timing {
+ u8 hsize; /* need to multiply by 8 then add 248 */
+ u8 vfreq:6; /* need to add 60 */
+ u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */
+} __attribute__((packed));
+
+/* If detailed data is pixel timing */
+struct detailed_pixel_timing {
+ u8 hactive_lo;
+ u8 hblank_lo;
+ u8 hblank_hi:4;
+ u8 hactive_hi:4;
+ u8 vactive_lo;
+ u8 vblank_lo;
+ u8 vblank_hi:4;
+ u8 vactive_hi:4;
+ u8 hsync_offset_lo;
+ u8 hsync_pulse_width_lo;
+ u8 vsync_pulse_width_lo:4;
+ u8 vsync_offset_lo:4;
+ u8 hsync_pulse_width_hi:2;
+ u8 hsync_offset_hi:2;
+ u8 vsync_pulse_width_hi:2;
+ u8 vsync_offset_hi:2;
+ u8 width_mm_lo;
+ u8 height_mm_lo;
+ u8 height_mm_hi:4;
+ u8 width_mm_hi:4;
+ u8 hborder;
+ u8 vborder;
+ u8 unknown0:1;
+ u8 vsync_positive:1;
+ u8 hsync_positive:1;
+ u8 separate_sync:2;
+ u8 stereo:1;
+ u8 unknown6:1;
+ u8 interlaced:1;
+} __attribute__((packed));
+
+/* If it's not pixel timing, it'll be one of the below */
+struct detailed_data_string {
+ u8 str[13];
+} __attribute__((packed));
+
+struct detailed_data_monitor_range {
+ u8 min_vfreq;
+ u8 max_vfreq;
+ u8 min_hfreq_khz;
+ u8 max_hfreq_khz;
+ u8 pixel_clock_mhz; /* need to multiply by 10 */
+ u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */
+ u8 hfreq_start_khz; /* need to multiply by 2 */
+ u8 c; /* need to divide by 2 */
+ u16 m; /* FIXME: byte order */
+ u8 k;
+ u8 j; /* need to divide by 2 */
+} __attribute__((packed));
+
+struct detailed_data_wpindex {
+ u8 white_y_lo:2;
+ u8 white_x_lo:2;
+ u8 pad:4;
+ u8 white_x_hi;
+ u8 white_y_hi;
+ u8 gamma; /* need to divide by 100 then add 1 */
+} __attribute__((packed));
+
+struct detailed_data_color_point {
+ u8 windex1;
+ u8 wpindex1[3];
+ u8 windex2;
+ u8 wpindex2[3];
+} __attribute__((packed));
+
+struct detailed_non_pixel {
+ u8 pad1;
+ u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
+ fb=color point data, fa=standard timing data,
+ f9=undefined, f8=mfg. reserved */
+ u8 pad2;
+ union {
+ struct detailed_data_string str;
+ struct detailed_data_monitor_range range;
+ struct detailed_data_wpindex color;
+ struct std_timing timings[5];
+ } data;
+} __attribute__((packed));
+
+#define EDID_DETAIL_STD_MODES 0xfa
+#define EDID_DETAIL_MONITOR_CPDATA 0xfb
+#define EDID_DETAIL_MONITOR_NAME 0xfc
+#define EDID_DETAIL_MONITOR_RANGE 0xfd
+#define EDID_DETAIL_MONITOR_STRING 0xfe
+#define EDID_DETAIL_MONITOR_SERIAL 0xff
+
+struct detailed_timing {
+ u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */
+ union {
+ struct detailed_pixel_timing pixel_data;
+ struct detailed_non_pixel other_data;
+ } data;
+} __attribute__((packed));
+
+struct edid {
+ u8 header[8];
+ /* Vendor & product info */
+ u8 mfg_id[2];
+ u8 prod_code[2];
+ u32 serial; /* FIXME: byte order */
+ u8 mfg_week;
+ u8 mfg_year;
+ /* EDID version */
+ u8 version;
+ u8 revision;
+ /* Display info: */
+ /* input definition */
+ u8 serration_vsync:1;
+ u8 sync_on_green:1;
+ u8 composite_sync:1;
+ u8 separate_syncs:1;
+ u8 blank_to_black:1;
+ u8 video_level:2;
+ u8 digital:1; /* bits below must be zero if set */
+ u8 width_cm;
+ u8 height_cm;
+ u8 gamma;
+ /* feature support */
+ u8 default_gtf:1;
+ u8 preferred_timing:1;
+ u8 standard_color:1;
+ u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */
+ u8 pm_active_off:1;
+ u8 pm_suspend:1;
+ u8 pm_standby:1;
+ /* Color characteristics */
+ u8 red_green_lo;
+ u8 black_white_lo;
+ u8 red_x;
+ u8 red_y;
+ u8 green_x;
+ u8 green_y;
+ u8 blue_x;
+ u8 blue_y;
+ u8 white_x;
+ u8 white_y;
+ /* Est. timings and mfg rsvd timings*/
+ struct est_timings established_timings;
+ /* Standard timings 1-8*/
+ struct std_timing standard_timings[8];
+ /* Detailing timings 1-4 */
+ struct detailed_timing detailed_timings[4];
+ /* Number of 128 byte ext. blocks */
+ u8 extensions;
+ /* Checksum */
+ u8 checksum;
+} __attribute__((packed));
+
+#endif /* little endian structs */
+
+#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
+
+#endif /* __DRM_EDID_H__ */
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
new file mode 100644
index 000000000000..601d2bd839f6
--- /dev/null
+++ b/include/drm/drm_mode.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007 Jakob Bornecrantz <wallbraker@gmail.com>
+ * Copyright (c) 2008 Red Hat Inc.
+ * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA
+ * Copyright (c) 2007-2008 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _DRM_MODE_H
+#define _DRM_MODE_H
+
+#if !defined(__KERNEL__) && !defined(_KERNEL)
+#include <stdint.h>
+#else
+#include <linux/kernel.h>
+#endif
+
+#define DRM_DISPLAY_INFO_LEN 32
+#define DRM_CONNECTOR_NAME_LEN 32
+#define DRM_DISPLAY_MODE_LEN 32
+#define DRM_PROP_NAME_LEN 32
+
+#define DRM_MODE_TYPE_BUILTIN (1<<0)
+#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN)
+#define DRM_MODE_TYPE_PREFERRED (1<<3)
+#define DRM_MODE_TYPE_DEFAULT (1<<4)
+#define DRM_MODE_TYPE_USERDEF (1<<5)
+#define DRM_MODE_TYPE_DRIVER (1<<6)
+
+/* Video mode flags */
+/* bit compatible with the xorg definitions. */
+#define DRM_MODE_FLAG_PHSYNC (1<<0)
+#define DRM_MODE_FLAG_NHSYNC (1<<1)
+#define DRM_MODE_FLAG_PVSYNC (1<<2)
+#define DRM_MODE_FLAG_NVSYNC (1<<3)
+#define DRM_MODE_FLAG_INTERLACE (1<<4)
+#define DRM_MODE_FLAG_DBLSCAN (1<<5)
+#define DRM_MODE_FLAG_CSYNC (1<<6)
+#define DRM_MODE_FLAG_PCSYNC (1<<7)
+#define DRM_MODE_FLAG_NCSYNC (1<<8)
+#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */
+#define DRM_MODE_FLAG_BCAST (1<<10)
+#define DRM_MODE_FLAG_PIXMUX (1<<11)
+#define DRM_MODE_FLAG_DBLCLK (1<<12)
+#define DRM_MODE_FLAG_CLKDIV2 (1<<13)
+
+/* DPMS flags */
+/* bit compatible with the xorg definitions. */
+#define DRM_MODE_DPMS_ON 0
+#define DRM_MODE_DPMS_STANDBY 1
+#define DRM_MODE_DPMS_SUSPEND 2
+#define DRM_MODE_DPMS_OFF 3
+
+/* Scaling mode options */
+#define DRM_MODE_SCALE_NON_GPU 0
+#define DRM_MODE_SCALE_FULLSCREEN 1
+#define DRM_MODE_SCALE_NO_SCALE 2
+#define DRM_MODE_SCALE_ASPECT 3
+
+/* Dithering mode options */
+#define DRM_MODE_DITHERING_OFF 0
+#define DRM_MODE_DITHERING_ON 1
+
+struct drm_mode_modeinfo {
+ uint32_t clock;
+ uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+ uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+
+ uint32_t vrefresh; /* vertical refresh * 1000 */
+
+ uint32_t flags;
+ uint32_t type;
+ char name[DRM_DISPLAY_MODE_LEN];
+};
+
+struct drm_mode_card_res {
+ uint64_t fb_id_ptr;
+ uint64_t crtc_id_ptr;
+ uint64_t connector_id_ptr;
+ uint64_t encoder_id_ptr;
+ uint32_t count_fbs;
+ uint32_t count_crtcs;
+ uint32_t count_connectors;
+ uint32_t count_encoders;
+ uint32_t min_width, max_width;
+ uint32_t min_height, max_height;
+};
+
+struct drm_mode_crtc {
+ uint64_t set_connectors_ptr;
+ uint32_t count_connectors;
+
+ uint32_t crtc_id; /**< Id */
+ uint32_t fb_id; /**< Id of framebuffer */
+
+ uint32_t x, y; /**< Position on the frameuffer */
+
+ uint32_t gamma_size;
+ uint32_t mode_valid;
+ struct drm_mode_modeinfo mode;
+};
+
+#define DRM_MODE_ENCODER_NONE 0
+#define DRM_MODE_ENCODER_DAC 1
+#define DRM_MODE_ENCODER_TMDS 2
+#define DRM_MODE_ENCODER_LVDS 3
+#define DRM_MODE_ENCODER_TVDAC 4
+
+struct drm_mode_get_encoder {
+ uint32_t encoder_id;
+ uint32_t encoder_type;
+
+ uint32_t crtc_id; /**< Id of crtc */
+
+ uint32_t possible_crtcs;
+ uint32_t possible_clones;
+};
+
+/* This is for connectors with multiple signal types. */
+/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */
+#define DRM_MODE_SUBCONNECTOR_Automatic 0
+#define DRM_MODE_SUBCONNECTOR_Unknown 0
+#define DRM_MODE_SUBCONNECTOR_DVID 3
+#define DRM_MODE_SUBCONNECTOR_DVIA 4
+#define DRM_MODE_SUBCONNECTOR_Composite 5
+#define DRM_MODE_SUBCONNECTOR_SVIDEO 6
+#define DRM_MODE_SUBCONNECTOR_Component 8
+
+#define DRM_MODE_CONNECTOR_Unknown 0
+#define DRM_MODE_CONNECTOR_VGA 1
+#define DRM_MODE_CONNECTOR_DVII 2
+#define DRM_MODE_CONNECTOR_DVID 3
+#define DRM_MODE_CONNECTOR_DVIA 4
+#define DRM_MODE_CONNECTOR_Composite 5
+#define DRM_MODE_CONNECTOR_SVIDEO 6
+#define DRM_MODE_CONNECTOR_LVDS 7
+#define DRM_MODE_CONNECTOR_Component 8
+#define DRM_MODE_CONNECTOR_9PinDIN 9
+#define DRM_MODE_CONNECTOR_DisplayPort 10
+#define DRM_MODE_CONNECTOR_HDMIA 11
+#define DRM_MODE_CONNECTOR_HDMIB 12
+
+struct drm_mode_get_connector {
+
+ uint64_t encoders_ptr;
+ uint64_t modes_ptr;
+ uint64_t props_ptr;
+ uint64_t prop_values_ptr;
+
+ uint32_t count_modes;
+ uint32_t count_props;
+ uint32_t count_encoders;
+
+ uint32_t encoder_id; /**< Current Encoder */
+ uint32_t connector_id; /**< Id */
+ uint32_t connector_type;
+ uint32_t connector_type_id;
+
+ uint32_t connection;
+ uint32_t mm_width, mm_height; /**< HxW in millimeters */
+ uint32_t subpixel;
+};
+
+#define DRM_MODE_PROP_PENDING (1<<0)
+#define DRM_MODE_PROP_RANGE (1<<1)
+#define DRM_MODE_PROP_IMMUTABLE (1<<2)
+#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */
+#define DRM_MODE_PROP_BLOB (1<<4)
+
+struct drm_mode_property_enum {
+ uint64_t value;
+ char name[DRM_PROP_NAME_LEN];
+};
+
+struct drm_mode_get_property {
+ uint64_t values_ptr; /* values and blob lengths */
+ uint64_t enum_blob_ptr; /* enum and blob id ptrs */
+
+ uint32_t prop_id;
+ uint32_t flags;
+ char name[DRM_PROP_NAME_LEN];
+
+ uint32_t count_values;
+ uint32_t count_enum_blobs;
+};
+
+struct drm_mode_connector_set_property {
+ uint64_t value;
+ uint32_t prop_id;
+ uint32_t connector_id;
+};
+
+struct drm_mode_get_blob {
+ uint32_t blob_id;
+ uint32_t length;
+ uint64_t data;
+};
+
+struct drm_mode_fb_cmd {
+ uint32_t fb_id;
+ uint32_t width, height;
+ uint32_t pitch;
+ uint32_t bpp;
+ uint32_t depth;
+ /* driver specific handle */
+ uint32_t handle;
+};
+
+struct drm_mode_mode_cmd {
+ uint32_t connector_id;
+ struct drm_mode_modeinfo mode;
+};
+
+#define DRM_MODE_CURSOR_BO (1<<0)
+#define DRM_MODE_CURSOR_MOVE (1<<1)
+
+/*
+ * depending on the value in flags diffrent members are used.
+ *
+ * CURSOR_BO uses
+ * crtc
+ * width
+ * height
+ * handle - if 0 turns the cursor of
+ *
+ * CURSOR_MOVE uses
+ * crtc
+ * x
+ * y
+ */
+struct drm_mode_cursor {
+ uint32_t flags;
+ uint32_t crtc_id;
+ int32_t x;
+ int32_t y;
+ uint32_t width;
+ uint32_t height;
+ /* driver specific handle */
+ uint32_t handle;
+};
+
+struct drm_mode_crtc_lut {
+ uint32_t crtc_id;
+ uint32_t gamma_size;
+
+ /* pointers to arrays */
+ uint64_t red;
+ uint64_t green;
+ uint64_t blue;
+};
+
+#endif
diff --git a/include/drm/drm_sarea.h b/include/drm/drm_sarea.h
index 480037331e4e..ee5389d22c64 100644
--- a/include/drm/drm_sarea.h
+++ b/include/drm/drm_sarea.h
@@ -36,12 +36,12 @@
/* SAREA area needs to be at least a page */
#if defined(__alpha__)
-#define SAREA_MAX 0x2000
+#define SAREA_MAX 0x2000U
#elif defined(__ia64__)
-#define SAREA_MAX 0x10000 /* 64kB */
+#define SAREA_MAX 0x10000U /* 64kB */
#else
/* Intel 830M driver needs at least 8k SAREA */
-#define SAREA_MAX 0x2000
+#define SAREA_MAX 0x2000U
#endif
/** Maximum number of drawables in the SAREA */
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
index 152b34da927c..b3bcf72dc656 100644
--- a/include/drm/i915_drm.h
+++ b/include/drm/i915_drm.h
@@ -113,8 +113,31 @@ typedef struct _drm_i915_sarea {
int pipeB_y;
int pipeB_w;
int pipeB_h;
+
+ /* fill out some space for old userspace triple buffer */
+ drm_handle_t unused_handle;
+ uint32_t unused1, unused2, unused3;
+
+ /* buffer object handles for static buffers. May change
+ * over the lifetime of the client.
+ */
+ uint32_t front_bo_handle;
+ uint32_t back_bo_handle;
+ uint32_t unused_bo_handle;
+ uint32_t depth_bo_handle;
+
} drm_i915_sarea_t;
+/* due to userspace building against these headers we need some compat here */
+#define planeA_x pipeA_x
+#define planeA_y pipeA_y
+#define planeA_w pipeA_w
+#define planeA_h pipeA_h
+#define planeB_x pipeB_x
+#define planeB_y pipeB_y
+#define planeB_w pipeB_w
+#define planeB_h pipeB_h
+
/* Flags for perf_boxes
*/
#define I915_BOX_RING_EMPTY 0x1
@@ -160,6 +183,7 @@ typedef struct _drm_i915_sarea {
#define DRM_I915_GEM_SET_TILING 0x21
#define DRM_I915_GEM_GET_TILING 0x22
#define DRM_I915_GEM_GET_APERTURE 0x23
+#define DRM_I915_GEM_MMAP_GTT 0x24
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -177,6 +201,8 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
+#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init)
+#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
@@ -187,6 +213,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
+#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt)
#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish)
#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
@@ -196,7 +223,7 @@ typedef struct _drm_i915_sarea {
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
*/
-typedef struct _drm_i915_batchbuffer {
+typedef struct drm_i915_batchbuffer {
int start; /* agp offset */
int used; /* nr bytes in use */
int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */
@@ -382,6 +409,18 @@ struct drm_i915_gem_mmap {
uint64_t addr_ptr;
};
+struct drm_i915_gem_mmap_gtt {
+ /** Handle for the object being mapped. */
+ uint32_t handle;
+ uint32_t pad;
+ /**
+ * Fake offset to use for subsequent mmap call
+ *
+ * This is a fixed-size type for 32/64 compatibility.
+ */
+ uint64_t offset;
+};
+
struct drm_i915_gem_set_domain {
/** Handle for the object */
uint32_t handle;
diff --git a/include/keys/keyring-type.h b/include/keys/keyring-type.h
new file mode 100644
index 000000000000..843f872a4b63
--- /dev/null
+++ b/include/keys/keyring-type.h
@@ -0,0 +1,31 @@
+/* Keyring key type
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _KEYS_KEYRING_TYPE_H
+#define _KEYS_KEYRING_TYPE_H
+
+#include <linux/key.h>
+#include <linux/rcupdate.h>
+
+/*
+ * the keyring payload contains a list of the keys to which the keyring is
+ * subscribed
+ */
+struct keyring_list {
+ struct rcu_head rcu; /* RCU deletion hook */
+ unsigned short maxkeys; /* max keys this list can hold */
+ unsigned short nkeys; /* number of keys currently held */
+ unsigned short delkey; /* key to be unlinked by RCU */
+ struct key *keys[0];
+};
+
+
+#endif /* _KEYS_KEYRING_TYPE_H */
diff --git a/include/linux/aio.h b/include/linux/aio.h
index f6b8cf99b596..b16a957030f8 100644
--- a/include/linux/aio.h
+++ b/include/linux/aio.h
@@ -5,6 +5,7 @@
#include <linux/workqueue.h>
#include <linux/aio_abi.h>
#include <linux/uio.h>
+#include <linux/rcupdate.h>
#include <asm/atomic.h>
@@ -183,7 +184,7 @@ struct kioctx {
/* This needs improving */
unsigned long user_id;
- struct kioctx *next;
+ struct hlist_node list;
wait_queue_head_t wait;
@@ -199,6 +200,8 @@ struct kioctx {
struct aio_ring_info ring_info;
struct delayed_work wq;
+
+ struct rcu_head rcu_head;
};
/* prototypes */
diff --git a/include/linux/atm.h b/include/linux/atm.h
index c791ddd96939..d3b292174aeb 100644
--- a/include/linux/atm.h
+++ b/include/linux/atm.h
@@ -231,10 +231,21 @@ static __inline__ int atmpvc_addr_in_use(struct sockaddr_atmpvc addr)
*/
struct atmif_sioc {
- int number;
- int length;
- void __user *arg;
+ int number;
+ int length;
+ void __user *arg;
};
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+struct compat_atmif_sioc {
+ int number;
+ int length;
+ compat_uptr_t arg;
+};
+#endif
+#endif
+
typedef unsigned short atm_backend_t;
#endif
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index a3d07c29d16c..086e5c362d3a 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -100,6 +100,10 @@ struct atm_dev_stats {
/* use backend to make new if */
#define ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
/* add party to p2mp call */
+#ifdef CONFIG_COMPAT
+/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */
+#define COMPAT_ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL+4,struct compat_atm_iobuf)
+#endif
#define ATM_DROPPARTY _IOW('a', ATMIOC_SPECIAL+5,int)
/* drop party from p2mp call */
@@ -224,6 +228,13 @@ struct atm_cirange {
extern struct proc_dir_entry *atm_proc_root;
#endif
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+struct compat_atm_iobuf {
+ int length;
+ compat_uptr_t buffer;
+};
+#endif
struct k_atm_aal_stats {
#define __HANDLE_ITEM(i) atomic_t i
@@ -379,6 +390,10 @@ struct atmdev_ops { /* only send is required */
int (*open)(struct atm_vcc *vcc);
void (*close)(struct atm_vcc *vcc);
int (*ioctl)(struct atm_dev *dev,unsigned int cmd,void __user *arg);
+#ifdef CONFIG_COMPAT
+ int (*compat_ioctl)(struct atm_dev *dev,unsigned int cmd,
+ void __user *arg);
+#endif
int (*getsockopt)(struct atm_vcc *vcc,int level,int optname,
void __user *optval,int optlen);
int (*setsockopt)(struct atm_vcc *vcc,int level,int optname,
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 8f0672d13eb1..26c4f6f65a46 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -99,6 +99,8 @@
#define AUDIT_OBJ_PID 1318 /* ptrace target */
#define AUDIT_TTY 1319 /* Input on an administrative TTY */
#define AUDIT_EOE 1320 /* End of multi-record event */
+#define AUDIT_BPRM_FCAPS 1321 /* Information about fcaps increasing perms */
+#define AUDIT_CAPSET 1322 /* Record showing argument to sys_capset */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -453,6 +455,10 @@ extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_pr
extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
+extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
+ const struct cred *new,
+ const struct cred *old);
+extern int __audit_log_capset(pid_t pid, const struct cred *new, const struct cred *old);
static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
@@ -502,6 +508,24 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
return __audit_mq_getsetattr(mqdes, mqstat);
return 0;
}
+
+static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm,
+ const struct cred *new,
+ const struct cred *old)
+{
+ if (unlikely(!audit_dummy_context()))
+ return __audit_log_bprm_fcaps(bprm, new, old);
+ return 0;
+}
+
+static inline int audit_log_capset(pid_t pid, const struct cred *new,
+ const struct cred *old)
+{
+ if (unlikely(!audit_dummy_context()))
+ return __audit_log_capset(pid, new, old);
+ return 0;
+}
+
extern int audit_n_rules;
extern int audit_signals;
#else
@@ -534,6 +558,8 @@ extern int audit_signals;
#define audit_mq_timedreceive(d,l,p,t) ({ 0; })
#define audit_mq_notify(d,n) ({ 0; })
#define audit_mq_getsetattr(d,s) ({ 0; })
+#define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; })
+#define audit_log_capset(pid, ncr, ocr) ({ 0; })
#define audit_ptrace(t) ((void)0)
#define audit_n_rules 0
#define audit_signals 0
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 7394b5b349ff..6cbfbe297180 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -35,16 +35,20 @@ struct linux_binprm{
struct mm_struct *mm;
unsigned long p; /* current top of mem */
unsigned int sh_bang:1,
- misc_bang:1;
+ misc_bang:1,
+ cred_prepared:1,/* true if creds already prepared (multiple
+ * preps happen for interpreters) */
+ cap_effective:1;/* true if has elevated effective capabilities,
+ * false if not; except for init which inherits
+ * its parent's caps anyway */
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth;
struct file * file;
- int e_uid, e_gid;
- kernel_cap_t cap_post_exec_permitted;
- bool cap_effective;
- void *security;
+ struct cred *cred; /* new credentials */
+ int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
+ unsigned int per_clear; /* bits to clear in current->personality */
int argc, envc;
char * filename; /* Name of binary as seen by procps */
char * interp; /* Name of the binary really executed. Most
@@ -101,7 +105,7 @@ extern int setup_arg_pages(struct linux_binprm * bprm,
int executable_stack);
extern int bprm_mm_init(struct linux_binprm *bprm);
extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
-extern void compute_creds(struct linux_binprm *binprm);
+extern void install_exec_creds(struct linux_binprm *bprm);
extern int do_coredump(long signr, int exit_code, struct pt_regs * regs);
extern int set_binfmt(struct linux_binfmt *new);
extern void free_bprm(struct linux_binprm *);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 6a642098e5c3..18462c5b8fff 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -90,10 +90,11 @@ struct bio {
unsigned int bi_comp_cpu; /* completion CPU */
+ atomic_t bi_cnt; /* pin count */
+
struct bio_vec *bi_io_vec; /* the actual vec list */
bio_end_io_t *bi_end_io;
- atomic_t bi_cnt; /* pin count */
void *bi_private;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
@@ -101,6 +102,13 @@ struct bio {
#endif
bio_destructor_t *bi_destructor; /* destructor */
+
+ /*
+ * We can inline a number of vecs at the end of the bio, to avoid
+ * double allocations for a small number of bio_vecs. This member
+ * MUST obviously be kept at the very end of the bio.
+ */
+ struct bio_vec bi_inline_vecs[0];
};
/*
@@ -117,6 +125,7 @@ struct bio {
#define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */
#define BIO_NULL_MAPPED 9 /* contains invalid user pages */
#define BIO_FS_INTEGRITY 10 /* fs owns integrity data, not block layer */
+#define BIO_QUIET 11 /* Make BIO Quiet */
#define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag)))
/*
@@ -211,6 +220,11 @@ static inline void *bio_data(struct bio *bio)
return NULL;
}
+static inline int bio_has_allocated_vec(struct bio *bio)
+{
+ return bio->bi_io_vec && bio->bi_io_vec != bio->bi_inline_vecs;
+}
+
/*
* will die
*/
@@ -332,7 +346,7 @@ struct bio_pair {
extern struct bio_pair *bio_split(struct bio *bi, int first_sectors);
extern void bio_pair_release(struct bio_pair *dbio);
-extern struct bio_set *bioset_create(int, int);
+extern struct bio_set *bioset_create(unsigned int, unsigned int);
extern void bioset_free(struct bio_set *);
extern struct bio *bio_alloc(gfp_t, int);
@@ -377,6 +391,7 @@ extern struct bio *bio_copy_user_iov(struct request_queue *,
extern int bio_uncopy_user(struct bio *);
void zero_fill_bio(struct bio *bio);
extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *);
+extern void bvec_free_bs(struct bio_set *, struct bio_vec *, unsigned int);
extern unsigned int bvec_nr_vecs(unsigned short idx);
/*
@@ -395,13 +410,17 @@ static inline void bio_set_completion_cpu(struct bio *bio, unsigned int cpu)
*/
#define BIO_POOL_SIZE 2
#define BIOVEC_NR_POOLS 6
+#define BIOVEC_MAX_IDX (BIOVEC_NR_POOLS - 1)
struct bio_set {
+ struct kmem_cache *bio_slab;
+ unsigned int front_pad;
+
mempool_t *bio_pool;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
mempool_t *bio_integrity_pool;
#endif
- mempool_t *bvec_pools[BIOVEC_NR_POOLS];
+ mempool_t *bvec_pool;
};
struct biovec_slab {
@@ -411,6 +430,7 @@ struct biovec_slab {
};
extern struct bio_set *fs_bio_set;
+extern struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly;
/*
* a small number of entries is fine, not going to be performance critical.
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index a08c33a26ca9..2878811c6134 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -137,9 +137,12 @@ extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
)
+#define small_const_nbits(nbits) \
+ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
static inline void bitmap_zero(unsigned long *dst, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = 0UL;
else {
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
@@ -150,7 +153,7 @@ static inline void bitmap_zero(unsigned long *dst, int nbits)
static inline void bitmap_fill(unsigned long *dst, int nbits)
{
size_t nlongs = BITS_TO_LONGS(nbits);
- if (nlongs > 1) {
+ if (!small_const_nbits(nbits)) {
int len = (nlongs - 1) * sizeof(unsigned long);
memset(dst, 0xff, len);
}
@@ -160,7 +163,7 @@ static inline void bitmap_fill(unsigned long *dst, int nbits)
static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src;
else {
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
@@ -171,7 +174,7 @@ static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
static inline void bitmap_and(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src1 & *src2;
else
__bitmap_and(dst, src1, src2, nbits);
@@ -180,7 +183,7 @@ static inline void bitmap_and(unsigned long *dst, const unsigned long *src1,
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src1 | *src2;
else
__bitmap_or(dst, src1, src2, nbits);
@@ -189,7 +192,7 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src1 ^ *src2;
else
__bitmap_xor(dst, src1, src2, nbits);
@@ -198,7 +201,7 @@ static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src1 & ~(*src2);
else
__bitmap_andnot(dst, src1, src2, nbits);
@@ -207,7 +210,7 @@ static inline void bitmap_andnot(unsigned long *dst, const unsigned long *src1,
static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits);
else
__bitmap_complement(dst, src, nbits);
@@ -216,7 +219,7 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr
static inline int bitmap_equal(const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
else
return __bitmap_equal(src1, src2, nbits);
@@ -225,7 +228,7 @@ static inline int bitmap_equal(const unsigned long *src1,
static inline int bitmap_intersects(const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
else
return __bitmap_intersects(src1, src2, nbits);
@@ -234,7 +237,7 @@ static inline int bitmap_intersects(const unsigned long *src1,
static inline int bitmap_subset(const unsigned long *src1,
const unsigned long *src2, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return ! ((*src1 & ~(*src2)) & BITMAP_LAST_WORD_MASK(nbits));
else
return __bitmap_subset(src1, src2, nbits);
@@ -242,7 +245,7 @@ static inline int bitmap_subset(const unsigned long *src1,
static inline int bitmap_empty(const unsigned long *src, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
else
return __bitmap_empty(src, nbits);
@@ -250,7 +253,7 @@ static inline int bitmap_empty(const unsigned long *src, int nbits)
static inline int bitmap_full(const unsigned long *src, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
else
return __bitmap_full(src, nbits);
@@ -258,7 +261,7 @@ static inline int bitmap_full(const unsigned long *src, int nbits)
static inline int bitmap_weight(const unsigned long *src, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits));
return __bitmap_weight(src, nbits);
}
@@ -266,7 +269,7 @@ static inline int bitmap_weight(const unsigned long *src, int nbits)
static inline void bitmap_shift_right(unsigned long *dst,
const unsigned long *src, int n, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = *src >> n;
else
__bitmap_shift_right(dst, src, n, nbits);
@@ -275,7 +278,7 @@ static inline void bitmap_shift_right(unsigned long *dst,
static inline void bitmap_shift_left(unsigned long *dst,
const unsigned long *src, int n, int nbits)
{
- if (nbits <= BITS_PER_LONG)
+ if (small_const_nbits(nbits))
*dst = (*src << n) & BITMAP_LAST_WORD_MASK(nbits);
else
__bitmap_shift_left(dst, src, n, nbits);
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 024f2b027244..61829139795a 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -134,9 +134,20 @@ extern unsigned long find_first_bit(const unsigned long *addr,
*/
extern unsigned long find_first_zero_bit(const unsigned long *addr,
unsigned long size);
-
#endif /* CONFIG_GENERIC_FIND_FIRST_BIT */
+#ifdef CONFIG_GENERIC_FIND_LAST_BIT
+/**
+ * find_last_bit - find the last set bit in a memory region
+ * @addr: The address to start the search at
+ * @size: The maximum size to search
+ *
+ * Returns the bit number of the first set bit, or size.
+ */
+extern unsigned long find_last_bit(const unsigned long *addr,
+ unsigned long size);
+#endif /* CONFIG_GENERIC_FIND_LAST_BIT */
+
#ifdef CONFIG_GENERIC_FIND_NEXT_BIT
/**
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 031a315c0509..7035cec583b6 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -26,7 +26,6 @@ struct scsi_ioctl_command;
struct request_queue;
struct elevator_queue;
-typedef struct elevator_queue elevator_t;
struct request_pm_state;
struct blk_trace;
struct request;
@@ -313,7 +312,7 @@ struct request_queue
*/
struct list_head queue_head;
struct request *last_merge;
- elevator_t *elevator;
+ struct elevator_queue *elevator;
/*
* the queue request freelist, one for reads and one for writes
@@ -449,6 +448,7 @@ struct request_queue
#define QUEUE_FLAG_FAIL_IO 12 /* fake timeout */
#define QUEUE_FLAG_STACKABLE 13 /* supports request stacking */
#define QUEUE_FLAG_NONROT 14 /* non-rotational device (SSD) */
+#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */
static inline int queue_is_locked(struct request_queue *q)
{
@@ -522,22 +522,32 @@ enum {
* TAG_FLUSH : ordering by tag w/ pre and post flushes
* TAG_FUA : ordering by tag w/ pre flush and FUA write
*/
- QUEUE_ORDERED_NONE = 0x00,
- QUEUE_ORDERED_DRAIN = 0x01,
- QUEUE_ORDERED_TAG = 0x02,
-
- QUEUE_ORDERED_PREFLUSH = 0x10,
- QUEUE_ORDERED_POSTFLUSH = 0x20,
- QUEUE_ORDERED_FUA = 0x40,
-
- QUEUE_ORDERED_DRAIN_FLUSH = QUEUE_ORDERED_DRAIN |
- QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_POSTFLUSH,
- QUEUE_ORDERED_DRAIN_FUA = QUEUE_ORDERED_DRAIN |
- QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_FUA,
- QUEUE_ORDERED_TAG_FLUSH = QUEUE_ORDERED_TAG |
- QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_POSTFLUSH,
- QUEUE_ORDERED_TAG_FUA = QUEUE_ORDERED_TAG |
- QUEUE_ORDERED_PREFLUSH | QUEUE_ORDERED_FUA,
+ QUEUE_ORDERED_BY_DRAIN = 0x01,
+ QUEUE_ORDERED_BY_TAG = 0x02,
+ QUEUE_ORDERED_DO_PREFLUSH = 0x10,
+ QUEUE_ORDERED_DO_BAR = 0x20,
+ QUEUE_ORDERED_DO_POSTFLUSH = 0x40,
+ QUEUE_ORDERED_DO_FUA = 0x80,
+
+ QUEUE_ORDERED_NONE = 0x00,
+
+ QUEUE_ORDERED_DRAIN = QUEUE_ORDERED_BY_DRAIN |
+ QUEUE_ORDERED_DO_BAR,
+ QUEUE_ORDERED_DRAIN_FLUSH = QUEUE_ORDERED_DRAIN |
+ QUEUE_ORDERED_DO_PREFLUSH |
+ QUEUE_ORDERED_DO_POSTFLUSH,
+ QUEUE_ORDERED_DRAIN_FUA = QUEUE_ORDERED_DRAIN |
+ QUEUE_ORDERED_DO_PREFLUSH |
+ QUEUE_ORDERED_DO_FUA,
+
+ QUEUE_ORDERED_TAG = QUEUE_ORDERED_BY_TAG |
+ QUEUE_ORDERED_DO_BAR,
+ QUEUE_ORDERED_TAG_FLUSH = QUEUE_ORDERED_TAG |
+ QUEUE_ORDERED_DO_PREFLUSH |
+ QUEUE_ORDERED_DO_POSTFLUSH,
+ QUEUE_ORDERED_TAG_FUA = QUEUE_ORDERED_TAG |
+ QUEUE_ORDERED_DO_PREFLUSH |
+ QUEUE_ORDERED_DO_FUA,
/*
* Ordered operation sequence
@@ -585,7 +595,6 @@ enum {
#define blk_fua_rq(rq) ((rq)->cmd_flags & REQ_FUA)
#define blk_discard_rq(rq) ((rq)->cmd_flags & REQ_DISCARD)
#define blk_bidi_rq(rq) ((rq)->next_rq != NULL)
-#define blk_empty_barrier(rq) (blk_barrier_rq(rq) && blk_fs_request(rq) && !(rq)->hard_nr_sectors)
/* rq->queuelist of dequeued request must be list_empty() */
#define blk_queued_rq(rq) (!list_empty(&(rq)->queuelist))
@@ -855,10 +864,10 @@ extern void blk_queue_rq_timed_out(struct request_queue *, rq_timed_out_fn *);
extern void blk_queue_rq_timeout(struct request_queue *, unsigned int);
extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *);
-extern int blk_do_ordered(struct request_queue *, struct request **);
+extern bool blk_do_ordered(struct request_queue *, struct request **);
extern unsigned blk_ordered_cur_seq(struct request_queue *);
extern unsigned blk_ordered_req_seq(struct request *);
-extern void blk_ordered_complete_seq(struct request_queue *, unsigned, int);
+extern bool blk_ordered_complete_seq(struct request_queue *, unsigned, int);
extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
extern void blk_dump_rq_flags(struct request *, char *);
@@ -977,7 +986,6 @@ static inline void put_dev_sector(Sector p)
struct work_struct;
int kblockd_schedule_work(struct request_queue *q, struct work_struct *work);
-void kblockd_flush_work(struct work_struct *work);
#define MODULE_ALIAS_BLOCKDEV(major,minor) \
MODULE_ALIAS("block-major-" __stringify(major) "-" __stringify(minor))
diff --git a/include/linux/bottom_half.h b/include/linux/bottom_half.h
index 777dbf695d44..27b1bcffe408 100644
--- a/include/linux/bottom_half.h
+++ b/include/linux/bottom_half.h
@@ -2,7 +2,6 @@
#define _LINUX_BH_H
extern void local_bh_disable(void);
-extern void __local_bh_enable(void);
extern void _local_bh_enable(void);
extern void local_bh_enable(void);
extern void local_bh_enable_ip(unsigned long ip);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 3ce64b90118c..8605f8a74df9 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -35,6 +35,7 @@ enum bh_state_bits {
BH_Ordered, /* ordered write */
BH_Eopnotsupp, /* operation not supported (barrier) */
BH_Unwritten, /* Buffer is allocated on disk but not written */
+ BH_Quiet, /* Buffer Error Prinks to be quiet */
BH_PrivateStart,/* not a state bit, but the first bit available
* for private allocation by other entities
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9d1fe30b6f6c..e22f48c2a46f 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -53,6 +53,7 @@ typedef struct __user_cap_data_struct {
#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
#define VFS_CAP_REVISION_MASK 0xFF000000
+#define VFS_CAP_REVISION_SHIFT 24
#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
@@ -68,6 +69,9 @@ typedef struct __user_cap_data_struct {
#define VFS_CAP_U32 VFS_CAP_U32_2
#define VFS_CAP_REVISION VFS_CAP_REVISION_2
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+extern int file_caps_enabled;
+#endif
struct vfs_cap_data {
__le32 magic_etc; /* Little endian */
@@ -96,6 +100,13 @@ typedef struct kernel_cap_struct {
__u32 cap[_KERNEL_CAPABILITY_U32S];
} kernel_cap_t;
+/* exact same as vfs_cap_data but in cpu endian and always filled completely */
+struct cpu_vfs_cap_data {
+ __u32 magic_etc;
+ kernel_cap_t permitted;
+ kernel_cap_t inheritable;
+};
+
#define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct))
#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
@@ -454,6 +465,13 @@ static inline int cap_isclear(const kernel_cap_t a)
return 1;
}
+/*
+ * Check if "a" is a subset of "set".
+ * return 1 if ALL of the capabilities in "a" are also in "set"
+ * cap_issubset(0101, 1111) will return 1
+ * return 0 if ANY of the capabilities in "a" are not in "set"
+ * cap_issubset(1111, 0101) will return 0
+ */
static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
{
kernel_cap_t dest;
@@ -501,8 +519,6 @@ extern const kernel_cap_t __cap_empty_set;
extern const kernel_cap_t __cap_full_set;
extern const kernel_cap_t __cap_init_eff_set;
-kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
-
/**
* has_capability - Determine if a task has a superior capability available
* @t: The task in question
@@ -514,9 +530,14 @@ kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
* Note that this does not set PF_SUPERPRIV on the task.
*/
#define has_capability(t, cap) (security_capable((t), (cap)) == 0)
+#define has_capability_noaudit(t, cap) (security_capable_noaudit((t), (cap)) == 0)
extern int capable(int cap);
+/* audit system wants to get cap info from files as well */
+struct dentry;
+extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
+
#endif /* __KERNEL__ */
#endif /* !_LINUX_CAPABILITY_H */
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 9c22396e8b50..9c8d31bacf46 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -54,3 +54,9 @@ SUBSYS(freezer)
#endif
/* */
+
+#ifdef CONFIG_NET_CLS_CGROUP
+SUBSYS(net_cls)
+#endif
+
+/* */
diff --git a/include/linux/console.h b/include/linux/console.h
index 248e6e3b9b73..a67a90cf8268 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -153,4 +153,8 @@ void vcs_remove_sysfs(struct tty_struct *tty);
#define VESA_HSYNC_SUSPEND 2
#define VESA_POWERDOWN 3
+#ifdef CONFIG_VGA_CONSOLE
+extern bool vgacon_text_force(void);
+#endif
+
#endif /* _LINUX_CONSOLE_H */
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index d4bf52603e6b..9f315382610b 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -144,6 +144,7 @@
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
extern cpumask_t _unused_cpumask_arg_;
+#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
#define cpu_set(cpu, dst) __cpu_set((cpu), &(dst))
static inline void __cpu_set(int cpu, volatile cpumask_t *dstp)
{
@@ -267,6 +268,26 @@ static inline void __cpus_shift_left(cpumask_t *dstp,
{
bitmap_shift_left(dstp->bits, srcp->bits, n, nbits);
}
+#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */
+
+/**
+ * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
+ * @bitmap: the bitmap
+ *
+ * There are a few places where cpumask_var_t isn't appropriate and
+ * static cpumasks must be used (eg. very early boot), yet we don't
+ * expose the definition of 'struct cpumask'.
+ *
+ * This does the conversion, and can be used as a constant initializer.
+ */
+#define to_cpumask(bitmap) \
+ ((struct cpumask *)(1 ? (bitmap) \
+ : (void *)sizeof(__check_is_bitmap(bitmap))))
+
+static inline int __check_is_bitmap(const unsigned long *bitmap)
+{
+ return 1;
+}
/*
* Special-case data structure for "single bit set only" constant CPU masks.
@@ -278,13 +299,14 @@ static inline void __cpus_shift_left(cpumask_t *dstp,
extern const unsigned long
cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)];
-static inline const cpumask_t *get_cpu_mask(unsigned int cpu)
+static inline const struct cpumask *get_cpu_mask(unsigned int cpu)
{
const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG];
p -= cpu / BITS_PER_LONG;
- return (const cpumask_t *)p;
+ return to_cpumask(p);
}
+#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
/*
* In cases where we take the address of the cpumask immediately,
* gcc optimizes it out (it's a constant) and there's no huge stack
@@ -370,19 +392,22 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp,
{
bitmap_fold(dstp->bits, origp->bits, sz, nbits);
}
+#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */
#if NR_CPUS == 1
#define nr_cpu_ids 1
+#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
#define first_cpu(src) ({ (void)(src); 0; })
#define next_cpu(n, src) ({ (void)(src); 1; })
#define any_online_cpu(mask) 0
#define for_each_cpu_mask(cpu, mask) \
for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
-
+#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */
#else /* NR_CPUS > 1 */
extern int nr_cpu_ids;
+#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
int __first_cpu(const cpumask_t *srcp);
int __next_cpu(int n, const cpumask_t *srcp);
int __any_online_cpu(const cpumask_t *mask);
@@ -394,8 +419,10 @@ int __any_online_cpu(const cpumask_t *mask);
for ((cpu) = -1; \
(cpu) = next_cpu((cpu), (mask)), \
(cpu) < NR_CPUS; )
+#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */
#endif
+#ifndef CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
#if NR_CPUS <= 64
#define next_cpu_nr(n, src) next_cpu(n, src)
@@ -413,77 +440,67 @@ int __next_cpu_nr(int n, const cpumask_t *srcp);
(cpu) < nr_cpu_ids; )
#endif /* NR_CPUS > 64 */
+#endif /* !CONFIG_DISABLE_OBSOLETE_CPUMASK_FUNCTIONS */
/*
* The following particular system cpumasks and operations manage
- * possible, present, active and online cpus. Each of them is a fixed size
- * bitmap of size NR_CPUS.
+ * possible, present, active and online cpus.
*
- * #ifdef CONFIG_HOTPLUG_CPU
- * cpu_possible_map - has bit 'cpu' set iff cpu is populatable
- * cpu_present_map - has bit 'cpu' set iff cpu is populated
- * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler
- * cpu_active_map - has bit 'cpu' set iff cpu available to migration
- * #else
- * cpu_possible_map - has bit 'cpu' set iff cpu is populated
- * cpu_present_map - copy of cpu_possible_map
- * cpu_online_map - has bit 'cpu' set iff cpu available to scheduler
- * #endif
+ * cpu_possible_mask- has bit 'cpu' set iff cpu is populatable
+ * cpu_present_mask - has bit 'cpu' set iff cpu is populated
+ * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler
+ * cpu_active_mask - has bit 'cpu' set iff cpu available to migration
*
- * In either case, NR_CPUS is fixed at compile time, as the static
- * size of these bitmaps. The cpu_possible_map is fixed at boot
- * time, as the set of CPU id's that it is possible might ever
- * be plugged in at anytime during the life of that system boot.
- * The cpu_present_map is dynamic(*), representing which CPUs
- * are currently plugged in. And cpu_online_map is the dynamic
- * subset of cpu_present_map, indicating those CPUs available
- * for scheduling.
+ * If !CONFIG_HOTPLUG_CPU, present == possible, and active == online.
*
- * If HOTPLUG is enabled, then cpu_possible_map is forced to have
+ * The cpu_possible_mask is fixed at boot time, as the set of CPU id's
+ * that it is possible might ever be plugged in at anytime during the
+ * life of that system boot. The cpu_present_mask is dynamic(*),
+ * representing which CPUs are currently plugged in. And
+ * cpu_online_mask is the dynamic subset of cpu_present_mask,
+ * indicating those CPUs available for scheduling.
+ *
+ * If HOTPLUG is enabled, then cpu_possible_mask is forced to have
* all NR_CPUS bits set, otherwise it is just the set of CPUs that
* ACPI reports present at boot.
*
- * If HOTPLUG is enabled, then cpu_present_map varies dynamically,
+ * If HOTPLUG is enabled, then cpu_present_mask varies dynamically,
* depending on what ACPI reports as currently plugged in, otherwise
- * cpu_present_map is just a copy of cpu_possible_map.
+ * cpu_present_mask is just a copy of cpu_possible_mask.
*
- * (*) Well, cpu_present_map is dynamic in the hotplug case. If not
- * hotplug, it's a copy of cpu_possible_map, hence fixed at boot.
+ * (*) Well, cpu_present_mask is dynamic in the hotplug case. If not
+ * hotplug, it's a copy of cpu_possible_mask, hence fixed at boot.
*
* Subtleties:
* 1) UP arch's (NR_CPUS == 1, CONFIG_SMP not defined) hardcode
* assumption that their single CPU is online. The UP
- * cpu_{online,possible,present}_maps are placebos. Changing them
+ * cpu_{online,possible,present}_masks are placebos. Changing them
* will have no useful affect on the following num_*_cpus()
* and cpu_*() macros in the UP case. This ugliness is a UP
* optimization - don't waste any instructions or memory references
* asking if you're online or how many CPUs there are if there is
* only one CPU.
- * 2) Most SMP arch's #define some of these maps to be some
- * other map specific to that arch. Therefore, the following
- * must be #define macros, not inlines. To see why, examine
- * the assembly code produced by the following. Note that
- * set1() writes phys_x_map, but set2() writes x_map:
- * int x_map, phys_x_map;
- * #define set1(a) x_map = a
- * inline void set2(int a) { x_map = a; }
- * #define x_map phys_x_map
- * main(){ set1(3); set2(5); }
*/
-extern cpumask_t cpu_possible_map;
-extern cpumask_t cpu_online_map;
-extern cpumask_t cpu_present_map;
-extern cpumask_t cpu_active_map;
+extern const struct cpumask *const cpu_possible_mask;
+extern const struct cpumask *const cpu_online_mask;
+extern const struct cpumask *const cpu_present_mask;
+extern const struct cpumask *const cpu_active_mask;
+
+/* These strip const, as traditionally they weren't const. */
+#define cpu_possible_map (*(cpumask_t *)cpu_possible_mask)
+#define cpu_online_map (*(cpumask_t *)cpu_online_mask)
+#define cpu_present_map (*(cpumask_t *)cpu_present_mask)
+#define cpu_active_map (*(cpumask_t *)cpu_active_mask)
#if NR_CPUS > 1
-#define num_online_cpus() cpus_weight_nr(cpu_online_map)
-#define num_possible_cpus() cpus_weight_nr(cpu_possible_map)
-#define num_present_cpus() cpus_weight_nr(cpu_present_map)
-#define cpu_online(cpu) cpu_isset((cpu), cpu_online_map)
-#define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map)
-#define cpu_present(cpu) cpu_isset((cpu), cpu_present_map)
-#define cpu_active(cpu) cpu_isset((cpu), cpu_active_map)
+#define num_online_cpus() cpumask_weight(cpu_online_mask)
+#define num_possible_cpus() cpumask_weight(cpu_possible_mask)
+#define num_present_cpus() cpumask_weight(cpu_present_mask)
+#define cpu_online(cpu) cpumask_test_cpu((cpu), cpu_online_mask)
+#define cpu_possible(cpu) cpumask_test_cpu((cpu), cpu_possible_mask)
+#define cpu_present(cpu) cpumask_test_cpu((cpu), cpu_present_mask)
+#define cpu_active(cpu) cpumask_test_cpu((cpu), cpu_active_mask)
#else
#define num_online_cpus() 1
#define num_possible_cpus() 1
@@ -496,10 +513,6 @@ extern cpumask_t cpu_active_map;
#define cpu_is_offline(cpu) unlikely(!cpu_online(cpu))
-#define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_possible_map)
-#define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_online_map)
-#define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_present_map)
-
/* These are the new versions of the cpumask operators: passed by pointer.
* The older versions will be implemented in terms of these, then deleted. */
#define cpumask_bits(maskp) ((maskp)->bits)
@@ -687,7 +700,7 @@ static inline void cpumask_clear_cpu(int cpu, struct cpumask *dstp)
* No static inline type checking - see Subtlety (1) above.
*/
#define cpumask_test_cpu(cpu, cpumask) \
- test_bit(cpumask_check(cpu), (cpumask)->bits)
+ test_bit(cpumask_check(cpu), cpumask_bits((cpumask)))
/**
* cpumask_test_and_set_cpu - atomically test and set a cpu in a cpumask
@@ -930,7 +943,7 @@ static inline void cpumask_copy(struct cpumask *dstp,
static inline int cpumask_scnprintf(char *buf, int len,
const struct cpumask *srcp)
{
- return bitmap_scnprintf(buf, len, srcp->bits, nr_cpumask_bits);
+ return bitmap_scnprintf(buf, len, cpumask_bits(srcp), nr_cpumask_bits);
}
/**
@@ -944,7 +957,7 @@ static inline int cpumask_scnprintf(char *buf, int len,
static inline int cpumask_parse_user(const char __user *buf, int len,
struct cpumask *dstp)
{
- return bitmap_parse_user(buf, len, dstp->bits, nr_cpumask_bits);
+ return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpumask_bits);
}
/**
@@ -959,7 +972,8 @@ static inline int cpumask_parse_user(const char __user *buf, int len,
static inline int cpulist_scnprintf(char *buf, int len,
const struct cpumask *srcp)
{
- return bitmap_scnlistprintf(buf, len, srcp->bits, nr_cpumask_bits);
+ return bitmap_scnlistprintf(buf, len, cpumask_bits(srcp),
+ nr_cpumask_bits);
}
/**
@@ -972,26 +986,7 @@ static inline int cpulist_scnprintf(char *buf, int len,
*/
static inline int cpulist_parse(const char *buf, struct cpumask *dstp)
{
- return bitmap_parselist(buf, dstp->bits, nr_cpumask_bits);
-}
-
-/**
- * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
- * @bitmap: the bitmap
- *
- * There are a few places where cpumask_var_t isn't appropriate and
- * static cpumasks must be used (eg. very early boot), yet we don't
- * expose the definition of 'struct cpumask'.
- *
- * This does the conversion, and can be used as a constant initializer.
- */
-#define to_cpumask(bitmap) \
- ((struct cpumask *)(1 ? (bitmap) \
- : (void *)sizeof(__check_is_bitmap(bitmap))))
-
-static inline int __check_is_bitmap(const unsigned long *bitmap)
-{
- return 1;
+ return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpumask_bits);
}
/**
@@ -1025,6 +1020,7 @@ static inline size_t cpumask_size(void)
#ifdef CONFIG_CPUMASK_OFFSTACK
typedef struct cpumask *cpumask_var_t;
+bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node);
bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags);
void alloc_bootmem_cpumask_var(cpumask_var_t *mask);
void free_cpumask_var(cpumask_var_t mask);
@@ -1038,6 +1034,12 @@ static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
return true;
}
+static inline bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags,
+ int node)
+{
+ return true;
+}
+
static inline void alloc_bootmem_cpumask_var(cpumask_var_t *mask)
{
}
@@ -1051,12 +1053,6 @@ static inline void free_bootmem_cpumask_var(cpumask_var_t mask)
}
#endif /* CONFIG_CPUMASK_OFFSTACK */
-/* The pointer versions of the maps, these will become the primary versions. */
-#define cpu_possible_mask ((const struct cpumask *)&cpu_possible_map)
-#define cpu_online_mask ((const struct cpumask *)&cpu_online_map)
-#define cpu_present_mask ((const struct cpumask *)&cpu_present_map)
-#define cpu_active_mask ((const struct cpumask *)&cpu_active_map)
-
/* It's common to want to use cpu_all_mask in struct member initializers,
* so it has to refer to an address rather than a pointer. */
extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS);
@@ -1065,51 +1061,16 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS);
/* First bits of cpu_bit_bitmap are in fact unset. */
#define cpu_none_mask to_cpumask(cpu_bit_bitmap[0])
-/* Wrappers for arch boot code to manipulate normally-constant masks */
-static inline void set_cpu_possible(unsigned int cpu, bool possible)
-{
- if (possible)
- cpumask_set_cpu(cpu, &cpu_possible_map);
- else
- cpumask_clear_cpu(cpu, &cpu_possible_map);
-}
-
-static inline void set_cpu_present(unsigned int cpu, bool present)
-{
- if (present)
- cpumask_set_cpu(cpu, &cpu_present_map);
- else
- cpumask_clear_cpu(cpu, &cpu_present_map);
-}
-
-static inline void set_cpu_online(unsigned int cpu, bool online)
-{
- if (online)
- cpumask_set_cpu(cpu, &cpu_online_map);
- else
- cpumask_clear_cpu(cpu, &cpu_online_map);
-}
+#define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
+#define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask)
+#define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask)
-static inline void set_cpu_active(unsigned int cpu, bool active)
-{
- if (active)
- cpumask_set_cpu(cpu, &cpu_active_map);
- else
- cpumask_clear_cpu(cpu, &cpu_active_map);
-}
-
-static inline void init_cpu_present(const struct cpumask *src)
-{
- cpumask_copy(&cpu_present_map, src);
-}
-
-static inline void init_cpu_possible(const struct cpumask *src)
-{
- cpumask_copy(&cpu_possible_map, src);
-}
-
-static inline void init_cpu_online(const struct cpumask *src)
-{
- cpumask_copy(&cpu_online_map, src);
-}
+/* Wrappers for arch boot code to manipulate normally-constant masks */
+void set_cpu_possible(unsigned int cpu, bool possible);
+void set_cpu_present(unsigned int cpu, bool present);
+void set_cpu_online(unsigned int cpu, bool online);
+void set_cpu_active(unsigned int cpu, bool active);
+void init_cpu_present(const struct cpumask *src);
+void init_cpu_possible(const struct cpumask *src);
+void init_cpu_online(const struct cpumask *src);
#endif /* __LINUX_CPUMASK_H */
diff --git a/include/linux/crc32c.h b/include/linux/crc32c.h
index 508f512e5a2f..bd8b44d96bdc 100644
--- a/include/linux/crc32c.h
+++ b/include/linux/crc32c.h
@@ -3,9 +3,9 @@
#include <linux/types.h>
-extern u32 crc32c_le(u32 crc, unsigned char const *address, size_t length);
-extern u32 crc32c_be(u32 crc, unsigned char const *address, size_t length);
+extern u32 crc32c(u32 crc, const void *address, unsigned int length);
-#define crc32c(seed, data, length) crc32c_le(seed, (unsigned char const *)data, length)
+/* This macro exists for backwards-compatibility. */
+#define crc32c_le crc32c
#endif /* _LINUX_CRC32C_H */
diff --git a/include/linux/cred.h b/include/linux/cred.h
index b69222cc1fd2..3282ee4318e7 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -1,4 +1,4 @@
-/* Credentials management
+/* Credentials management - see Documentation/credentials.txt
*
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
@@ -12,39 +12,335 @@
#ifndef _LINUX_CRED_H
#define _LINUX_CRED_H
-#define get_current_user() (get_uid(current->user))
+#include <linux/capability.h>
+#include <linux/key.h>
+#include <asm/atomic.h>
-#define task_uid(task) ((task)->uid)
-#define task_gid(task) ((task)->gid)
-#define task_euid(task) ((task)->euid)
-#define task_egid(task) ((task)->egid)
+struct user_struct;
+struct cred;
+struct inode;
-#define current_uid() (current->uid)
-#define current_gid() (current->gid)
-#define current_euid() (current->euid)
-#define current_egid() (current->egid)
-#define current_suid() (current->suid)
-#define current_sgid() (current->sgid)
-#define current_fsuid() (current->fsuid)
-#define current_fsgid() (current->fsgid)
-#define current_cap() (current->cap_effective)
+/*
+ * COW Supplementary groups list
+ */
+#define NGROUPS_SMALL 32
+#define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
+
+struct group_info {
+ atomic_t usage;
+ int ngroups;
+ int nblocks;
+ gid_t small_block[NGROUPS_SMALL];
+ gid_t *blocks[0];
+};
+
+/**
+ * get_group_info - Get a reference to a group info structure
+ * @group_info: The group info to reference
+ *
+ * This gets a reference to a set of supplementary groups.
+ *
+ * If the caller is accessing a task's credentials, they must hold the RCU read
+ * lock when reading.
+ */
+static inline struct group_info *get_group_info(struct group_info *gi)
+{
+ atomic_inc(&gi->usage);
+ return gi;
+}
+
+/**
+ * put_group_info - Release a reference to a group info structure
+ * @group_info: The group info to release
+ */
+#define put_group_info(group_info) \
+do { \
+ if (atomic_dec_and_test(&(group_info)->usage)) \
+ groups_free(group_info); \
+} while (0)
+
+extern struct group_info *groups_alloc(int);
+extern struct group_info init_groups;
+extern void groups_free(struct group_info *);
+extern int set_current_groups(struct group_info *);
+extern int set_groups(struct cred *, struct group_info *);
+extern int groups_search(const struct group_info *, gid_t);
+
+/* access the groups "array" with this macro */
+#define GROUP_AT(gi, i) \
+ ((gi)->blocks[(i) / NGROUPS_PER_BLOCK][(i) % NGROUPS_PER_BLOCK])
+
+extern int in_group_p(gid_t);
+extern int in_egroup_p(gid_t);
+
+/*
+ * The common credentials for a thread group
+ * - shared by CLONE_THREAD
+ */
+#ifdef CONFIG_KEYS
+struct thread_group_cred {
+ atomic_t usage;
+ pid_t tgid; /* thread group process ID */
+ spinlock_t lock;
+ struct key *session_keyring; /* keyring inherited over fork */
+ struct key *process_keyring; /* keyring private to this process */
+ struct rcu_head rcu; /* RCU deletion hook */
+};
+#endif
+
+/*
+ * The security context of a task
+ *
+ * The parts of the context break down into two categories:
+ *
+ * (1) The objective context of a task. These parts are used when some other
+ * task is attempting to affect this one.
+ *
+ * (2) The subjective context. These details are used when the task is acting
+ * upon another object, be that a file, a task, a key or whatever.
+ *
+ * Note that some members of this structure belong to both categories - the
+ * LSM security pointer for instance.
+ *
+ * A task has two security pointers. task->real_cred points to the objective
+ * context that defines that task's actual details. The objective part of this
+ * context is used whenever that task is acted upon.
+ *
+ * task->cred points to the subjective context that defines the details of how
+ * that task is going to act upon another object. This may be overridden
+ * temporarily to point to another security context, but normally points to the
+ * same context as task->real_cred.
+ */
+struct cred {
+ atomic_t usage;
+ uid_t uid; /* real UID of the task */
+ gid_t gid; /* real GID of the task */
+ uid_t suid; /* saved UID of the task */
+ gid_t sgid; /* saved GID of the task */
+ uid_t euid; /* effective UID of the task */
+ gid_t egid; /* effective GID of the task */
+ uid_t fsuid; /* UID for VFS ops */
+ gid_t fsgid; /* GID for VFS ops */
+ unsigned securebits; /* SUID-less security management */
+ kernel_cap_t cap_inheritable; /* caps our children can inherit */
+ kernel_cap_t cap_permitted; /* caps we're permitted */
+ kernel_cap_t cap_effective; /* caps we can actually use */
+ kernel_cap_t cap_bset; /* capability bounding set */
+#ifdef CONFIG_KEYS
+ unsigned char jit_keyring; /* default keyring to attach requested
+ * keys to */
+ struct key *thread_keyring; /* keyring private to this thread */
+ struct key *request_key_auth; /* assumed request_key authority */
+ struct thread_group_cred *tgcred; /* thread-group shared credentials */
+#endif
+#ifdef CONFIG_SECURITY
+ void *security; /* subjective LSM security */
+#endif
+ struct user_struct *user; /* real user ID subscription */
+ struct group_info *group_info; /* supplementary groups for euid/fsgid */
+ struct rcu_head rcu; /* RCU deletion hook */
+};
+
+extern void __put_cred(struct cred *);
+extern int copy_creds(struct task_struct *, unsigned long);
+extern struct cred *prepare_creds(void);
+extern struct cred *prepare_exec_creds(void);
+extern struct cred *prepare_usermodehelper_creds(void);
+extern int commit_creds(struct cred *);
+extern void abort_creds(struct cred *);
+extern const struct cred *override_creds(const struct cred *);
+extern void revert_creds(const struct cred *);
+extern struct cred *prepare_kernel_cred(struct task_struct *);
+extern int change_create_files_as(struct cred *, struct inode *);
+extern int set_security_override(struct cred *, u32);
+extern int set_security_override_from_ctx(struct cred *, const char *);
+extern int set_create_files_as(struct cred *, struct inode *);
+extern void __init cred_init(void);
+
+/**
+ * get_new_cred - Get a reference on a new set of credentials
+ * @cred: The new credentials to reference
+ *
+ * Get a reference on the specified set of new credentials. The caller must
+ * release the reference.
+ */
+static inline struct cred *get_new_cred(struct cred *cred)
+{
+ atomic_inc(&cred->usage);
+ return cred;
+}
+
+/**
+ * get_cred - Get a reference on a set of credentials
+ * @cred: The credentials to reference
+ *
+ * Get a reference on the specified set of credentials. The caller must
+ * release the reference.
+ *
+ * This is used to deal with a committed set of credentials. Although the
+ * pointer is const, this will temporarily discard the const and increment the
+ * usage count. The purpose of this is to attempt to catch at compile time the
+ * accidental alteration of a set of credentials that should be considered
+ * immutable.
+ */
+static inline const struct cred *get_cred(const struct cred *cred)
+{
+ return get_new_cred((struct cred *) cred);
+}
+
+/**
+ * put_cred - Release a reference to a set of credentials
+ * @cred: The credentials to release
+ *
+ * Release a reference to a set of credentials, deleting them when the last ref
+ * is released.
+ *
+ * This takes a const pointer to a set of credentials because the credentials
+ * on task_struct are attached by const pointers to prevent accidental
+ * alteration of otherwise immutable credential sets.
+ */
+static inline void put_cred(const struct cred *_cred)
+{
+ struct cred *cred = (struct cred *) _cred;
+
+ BUG_ON(atomic_read(&(cred)->usage) <= 0);
+ if (atomic_dec_and_test(&(cred)->usage))
+ __put_cred(cred);
+}
+
+/**
+ * current_cred - Access the current task's subjective credentials
+ *
+ * Access the subjective credentials of the current task.
+ */
+#define current_cred() \
+ (current->cred)
+
+/**
+ * __task_cred - Access a task's objective credentials
+ * @task: The task to query
+ *
+ * Access the objective credentials of a task. The caller must hold the RCU
+ * readlock.
+ *
+ * The caller must make sure task doesn't go away, either by holding a ref on
+ * task or by holding tasklist_lock to prevent it from being unlinked.
+ */
+#define __task_cred(task) \
+ ((const struct cred *)(rcu_dereference((task)->real_cred)))
+
+/**
+ * get_task_cred - Get another task's objective credentials
+ * @task: The task to query
+ *
+ * Get the objective credentials of a task, pinning them so that they can't go
+ * away. Accessing a task's credentials directly is not permitted.
+ *
+ * The caller must make sure task doesn't go away, either by holding a ref on
+ * task or by holding tasklist_lock to prevent it from being unlinked.
+ */
+#define get_task_cred(task) \
+({ \
+ struct cred *__cred; \
+ rcu_read_lock(); \
+ __cred = (struct cred *) __task_cred((task)); \
+ get_cred(__cred); \
+ rcu_read_unlock(); \
+ __cred; \
+})
+
+/**
+ * get_current_cred - Get the current task's subjective credentials
+ *
+ * Get the subjective credentials of the current task, pinning them so that
+ * they can't go away. Accessing the current task's credentials directly is
+ * not permitted.
+ */
+#define get_current_cred() \
+ (get_cred(current_cred()))
+
+/**
+ * get_current_user - Get the current task's user_struct
+ *
+ * Get the user record of the current task, pinning it so that it can't go
+ * away.
+ */
+#define get_current_user() \
+({ \
+ struct user_struct *__u; \
+ struct cred *__cred; \
+ __cred = (struct cred *) current_cred(); \
+ __u = get_uid(__cred->user); \
+ __u; \
+})
+
+/**
+ * get_current_groups - Get the current task's supplementary group list
+ *
+ * Get the supplementary group list of the current task, pinning it so that it
+ * can't go away.
+ */
+#define get_current_groups() \
+({ \
+ struct group_info *__groups; \
+ struct cred *__cred; \
+ __cred = (struct cred *) current_cred(); \
+ __groups = get_group_info(__cred->group_info); \
+ __groups; \
+})
+
+#define task_cred_xxx(task, xxx) \
+({ \
+ __typeof__(((struct cred *)NULL)->xxx) ___val; \
+ rcu_read_lock(); \
+ ___val = __task_cred((task))->xxx; \
+ rcu_read_unlock(); \
+ ___val; \
+})
+
+#define task_uid(task) (task_cred_xxx((task), uid))
+#define task_euid(task) (task_cred_xxx((task), euid))
+
+#define current_cred_xxx(xxx) \
+({ \
+ current->cred->xxx; \
+})
+
+#define current_uid() (current_cred_xxx(uid))
+#define current_gid() (current_cred_xxx(gid))
+#define current_euid() (current_cred_xxx(euid))
+#define current_egid() (current_cred_xxx(egid))
+#define current_suid() (current_cred_xxx(suid))
+#define current_sgid() (current_cred_xxx(sgid))
+#define current_fsuid() (current_cred_xxx(fsuid))
+#define current_fsgid() (current_cred_xxx(fsgid))
+#define current_cap() (current_cred_xxx(cap_effective))
+#define current_user() (current_cred_xxx(user))
+#define current_user_ns() (current_cred_xxx(user)->user_ns)
+#define current_security() (current_cred_xxx(security))
#define current_uid_gid(_uid, _gid) \
do { \
- *(_uid) = current->uid; \
- *(_gid) = current->gid; \
+ const struct cred *__cred; \
+ __cred = current_cred(); \
+ *(_uid) = __cred->uid; \
+ *(_gid) = __cred->gid; \
} while(0)
-#define current_euid_egid(_uid, _gid) \
+#define current_euid_egid(_euid, _egid) \
do { \
- *(_uid) = current->euid; \
- *(_gid) = current->egid; \
+ const struct cred *__cred; \
+ __cred = current_cred(); \
+ *(_euid) = __cred->euid; \
+ *(_egid) = __cred->egid; \
} while(0)
-#define current_fsuid_fsgid(_uid, _gid) \
+#define current_fsuid_fsgid(_fsuid, _fsgid) \
do { \
- *(_uid) = current->fsuid; \
- *(_gid) = current->fsgid; \
+ const struct cred *__cred; \
+ __cred = current_cred(); \
+ *(_fsuid) = __cred->fsuid; \
+ *(_fsgid) = __cred->fsgid; \
} while(0)
#endif /* _LINUX_CRED_H */
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 3d2317e4af2e..3bacd71509fb 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -36,7 +36,8 @@
#define CRYPTO_ALG_TYPE_ABLKCIPHER 0x00000005
#define CRYPTO_ALG_TYPE_GIVCIPHER 0x00000006
#define CRYPTO_ALG_TYPE_DIGEST 0x00000008
-#define CRYPTO_ALG_TYPE_HASH 0x00000009
+#define CRYPTO_ALG_TYPE_HASH 0x00000008
+#define CRYPTO_ALG_TYPE_SHASH 0x00000009
#define CRYPTO_ALG_TYPE_AHASH 0x0000000a
#define CRYPTO_ALG_TYPE_RNG 0x0000000c
@@ -220,6 +221,7 @@ struct ablkcipher_alg {
struct ahash_alg {
int (*init)(struct ahash_request *req);
+ int (*reinit)(struct ahash_request *req);
int (*update)(struct ahash_request *req);
int (*final)(struct ahash_request *req);
int (*digest)(struct ahash_request *req);
@@ -480,6 +482,8 @@ struct crypto_tfm {
struct compress_tfm compress;
struct rng_tfm rng;
} crt_u;
+
+ void (*exit)(struct crypto_tfm *tfm);
struct crypto_alg *__crt_alg;
@@ -544,7 +548,9 @@ struct crypto_attr_u32 {
* Transform user interface.
*/
-struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags);
+struct crypto_tfm *crypto_alloc_tfm(const char *alg_name,
+ const struct crypto_type *frontend,
+ u32 type, u32 mask);
struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask);
void crypto_free_tfm(struct crypto_tfm *tfm);
diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h
new file mode 100644
index 000000000000..b0ef274e0031
--- /dev/null
+++ b/include/linux/dcbnl.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2008, 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., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Lucy Liu <lucy.liu@intel.com>
+ */
+
+#ifndef __LINUX_DCBNL_H__
+#define __LINUX_DCBNL_H__
+
+#define DCB_PROTO_VERSION 1
+
+struct dcbmsg {
+ unsigned char dcb_family;
+ __u8 cmd;
+ __u16 dcb_pad;
+};
+
+/**
+ * enum dcbnl_commands - supported DCB commands
+ *
+ * @DCB_CMD_UNDEFINED: unspecified command to catch errors
+ * @DCB_CMD_GSTATE: request the state of DCB in the device
+ * @DCB_CMD_SSTATE: set the state of DCB in the device
+ * @DCB_CMD_PGTX_GCFG: request the priority group configuration for Tx
+ * @DCB_CMD_PGTX_SCFG: set the priority group configuration for Tx
+ * @DCB_CMD_PGRX_GCFG: request the priority group configuration for Rx
+ * @DCB_CMD_PGRX_SCFG: set the priority group configuration for Rx
+ * @DCB_CMD_PFC_GCFG: request the priority flow control configuration
+ * @DCB_CMD_PFC_SCFG: set the priority flow control configuration
+ * @DCB_CMD_SET_ALL: apply all changes to the underlying device
+ * @DCB_CMD_GPERM_HWADDR: get the permanent MAC address of the underlying
+ * device. Only useful when using bonding.
+ * @DCB_CMD_GCAP: request the DCB capabilities of the device
+ * @DCB_CMD_GNUMTCS: get the number of traffic classes currently supported
+ * @DCB_CMD_SNUMTCS: set the number of traffic classes
+ * @DCB_CMD_GBCN: set backward congestion notification configuration
+ * @DCB_CMD_SBCN: get backward congestion notification configration.
+ */
+enum dcbnl_commands {
+ DCB_CMD_UNDEFINED,
+
+ DCB_CMD_GSTATE,
+ DCB_CMD_SSTATE,
+
+ DCB_CMD_PGTX_GCFG,
+ DCB_CMD_PGTX_SCFG,
+ DCB_CMD_PGRX_GCFG,
+ DCB_CMD_PGRX_SCFG,
+
+ DCB_CMD_PFC_GCFG,
+ DCB_CMD_PFC_SCFG,
+
+ DCB_CMD_SET_ALL,
+
+ DCB_CMD_GPERM_HWADDR,
+
+ DCB_CMD_GCAP,
+
+ DCB_CMD_GNUMTCS,
+ DCB_CMD_SNUMTCS,
+
+ DCB_CMD_PFC_GSTATE,
+ DCB_CMD_PFC_SSTATE,
+
+ DCB_CMD_BCN_GCFG,
+ DCB_CMD_BCN_SCFG,
+
+ __DCB_CMD_ENUM_MAX,
+ DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_attrs - DCB top-level netlink attributes
+ *
+ * @DCB_ATTR_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_ATTR_IFNAME: interface name of the underlying device (NLA_STRING)
+ * @DCB_ATTR_STATE: enable state of DCB in the device (NLA_U8)
+ * @DCB_ATTR_PFC_STATE: enable state of PFC in the device (NLA_U8)
+ * @DCB_ATTR_PFC_CFG: priority flow control configuration (NLA_NESTED)
+ * @DCB_ATTR_NUM_TC: number of traffic classes supported in the device (NLA_U8)
+ * @DCB_ATTR_PG_CFG: priority group configuration (NLA_NESTED)
+ * @DCB_ATTR_SET_ALL: bool to commit changes to hardware or not (NLA_U8)
+ * @DCB_ATTR_PERM_HWADDR: MAC address of the physical device (NLA_NESTED)
+ * @DCB_ATTR_CAP: DCB capabilities of the device (NLA_NESTED)
+ * @DCB_ATTR_NUMTCS: number of traffic classes supported (NLA_NESTED)
+ * @DCB_ATTR_BCN: backward congestion notification configuration (NLA_NESTED)
+ */
+enum dcbnl_attrs {
+ DCB_ATTR_UNDEFINED,
+
+ DCB_ATTR_IFNAME,
+ DCB_ATTR_STATE,
+ DCB_ATTR_PFC_STATE,
+ DCB_ATTR_PFC_CFG,
+ DCB_ATTR_NUM_TC,
+ DCB_ATTR_PG_CFG,
+ DCB_ATTR_SET_ALL,
+ DCB_ATTR_PERM_HWADDR,
+ DCB_ATTR_CAP,
+ DCB_ATTR_NUMTCS,
+ DCB_ATTR_BCN,
+
+ __DCB_ATTR_ENUM_MAX,
+ DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_pfc_attrs - DCB Priority Flow Control user priority nested attrs
+ *
+ * @DCB_PFC_UP_ATTR_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_PFC_UP_ATTR_0: Priority Flow Control value for User Priority 0 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_1: Priority Flow Control value for User Priority 1 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_2: Priority Flow Control value for User Priority 2 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_3: Priority Flow Control value for User Priority 3 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_4: Priority Flow Control value for User Priority 4 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_5: Priority Flow Control value for User Priority 5 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_6: Priority Flow Control value for User Priority 6 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_7: Priority Flow Control value for User Priority 7 (NLA_U8)
+ * @DCB_PFC_UP_ATTR_MAX: highest attribute number currently defined
+ * @DCB_PFC_UP_ATTR_ALL: apply to all priority flow control attrs (NLA_FLAG)
+ *
+ */
+enum dcbnl_pfc_up_attrs {
+ DCB_PFC_UP_ATTR_UNDEFINED,
+
+ DCB_PFC_UP_ATTR_0,
+ DCB_PFC_UP_ATTR_1,
+ DCB_PFC_UP_ATTR_2,
+ DCB_PFC_UP_ATTR_3,
+ DCB_PFC_UP_ATTR_4,
+ DCB_PFC_UP_ATTR_5,
+ DCB_PFC_UP_ATTR_6,
+ DCB_PFC_UP_ATTR_7,
+ DCB_PFC_UP_ATTR_ALL,
+
+ __DCB_PFC_UP_ATTR_ENUM_MAX,
+ DCB_PFC_UP_ATTR_MAX = __DCB_PFC_UP_ATTR_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_pg_attrs - DCB Priority Group attributes
+ *
+ * @DCB_PG_ATTR_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_PG_ATTR_TC_0: Priority Group Traffic Class 0 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_1: Priority Group Traffic Class 1 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_2: Priority Group Traffic Class 2 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_3: Priority Group Traffic Class 3 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_4: Priority Group Traffic Class 4 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_5: Priority Group Traffic Class 5 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_6: Priority Group Traffic Class 6 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_7: Priority Group Traffic Class 7 configuration (NLA_NESTED)
+ * @DCB_PG_ATTR_TC_MAX: highest attribute number currently defined
+ * @DCB_PG_ATTR_TC_ALL: apply to all traffic classes (NLA_NESTED)
+ * @DCB_PG_ATTR_BW_ID_0: Percent of link bandwidth for Priority Group 0 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_1: Percent of link bandwidth for Priority Group 1 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_2: Percent of link bandwidth for Priority Group 2 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_3: Percent of link bandwidth for Priority Group 3 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_4: Percent of link bandwidth for Priority Group 4 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_5: Percent of link bandwidth for Priority Group 5 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_6: Percent of link bandwidth for Priority Group 6 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_7: Percent of link bandwidth for Priority Group 7 (NLA_U8)
+ * @DCB_PG_ATTR_BW_ID_MAX: highest attribute number currently defined
+ * @DCB_PG_ATTR_BW_ID_ALL: apply to all priority groups (NLA_FLAG)
+ *
+ */
+enum dcbnl_pg_attrs {
+ DCB_PG_ATTR_UNDEFINED,
+
+ DCB_PG_ATTR_TC_0,
+ DCB_PG_ATTR_TC_1,
+ DCB_PG_ATTR_TC_2,
+ DCB_PG_ATTR_TC_3,
+ DCB_PG_ATTR_TC_4,
+ DCB_PG_ATTR_TC_5,
+ DCB_PG_ATTR_TC_6,
+ DCB_PG_ATTR_TC_7,
+ DCB_PG_ATTR_TC_MAX,
+ DCB_PG_ATTR_TC_ALL,
+
+ DCB_PG_ATTR_BW_ID_0,
+ DCB_PG_ATTR_BW_ID_1,
+ DCB_PG_ATTR_BW_ID_2,
+ DCB_PG_ATTR_BW_ID_3,
+ DCB_PG_ATTR_BW_ID_4,
+ DCB_PG_ATTR_BW_ID_5,
+ DCB_PG_ATTR_BW_ID_6,
+ DCB_PG_ATTR_BW_ID_7,
+ DCB_PG_ATTR_BW_ID_MAX,
+ DCB_PG_ATTR_BW_ID_ALL,
+
+ __DCB_PG_ATTR_ENUM_MAX,
+ DCB_PG_ATTR_MAX = __DCB_PG_ATTR_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_tc_attrs - DCB Traffic Class attributes
+ *
+ * @DCB_TC_ATTR_PARAM_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_TC_ATTR_PARAM_PGID: (NLA_U8) Priority group the traffic class belongs to
+ * Valid values are: 0-7
+ * @DCB_TC_ATTR_PARAM_UP_MAPPING: (NLA_U8) Traffic class to user priority map
+ * Some devices may not support changing the
+ * user priority map of a TC.
+ * @DCB_TC_ATTR_PARAM_STRICT_PRIO: (NLA_U8) Strict priority setting
+ * 0 - none
+ * 1 - group strict
+ * 2 - link strict
+ * @DCB_TC_ATTR_PARAM_BW_PCT: optional - (NLA_U8) If supported by the device and
+ * not configured to use link strict priority,
+ * this is the percentage of bandwidth of the
+ * priority group this traffic class belongs to
+ * @DCB_TC_ATTR_PARAM_ALL: (NLA_FLAG) all traffic class parameters
+ *
+ */
+enum dcbnl_tc_attrs {
+ DCB_TC_ATTR_PARAM_UNDEFINED,
+
+ DCB_TC_ATTR_PARAM_PGID,
+ DCB_TC_ATTR_PARAM_UP_MAPPING,
+ DCB_TC_ATTR_PARAM_STRICT_PRIO,
+ DCB_TC_ATTR_PARAM_BW_PCT,
+ DCB_TC_ATTR_PARAM_ALL,
+
+ __DCB_TC_ATTR_PARAM_ENUM_MAX,
+ DCB_TC_ATTR_PARAM_MAX = __DCB_TC_ATTR_PARAM_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_cap_attrs - DCB Capability attributes
+ *
+ * @DCB_CAP_ATTR_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_CAP_ATTR_ALL: (NLA_FLAG) all capability parameters
+ * @DCB_CAP_ATTR_PG: (NLA_U8) device supports Priority Groups
+ * @DCB_CAP_ATTR_PFC: (NLA_U8) device supports Priority Flow Control
+ * @DCB_CAP_ATTR_UP2TC: (NLA_U8) device supports user priority to
+ * traffic class mapping
+ * @DCB_CAP_ATTR_PG_TCS: (NLA_U8) bitmap where each bit represents a
+ * number of traffic classes the device
+ * can be configured to use for Priority Groups
+ * @DCB_CAP_ATTR_PFC_TCS: (NLA_U8) bitmap where each bit represents a
+ * number of traffic classes the device can be
+ * configured to use for Priority Flow Control
+ * @DCB_CAP_ATTR_GSP: (NLA_U8) device supports group strict priority
+ * @DCB_CAP_ATTR_BCN: (NLA_U8) device supports Backwards Congestion
+ * Notification
+ */
+enum dcbnl_cap_attrs {
+ DCB_CAP_ATTR_UNDEFINED,
+ DCB_CAP_ATTR_ALL,
+ DCB_CAP_ATTR_PG,
+ DCB_CAP_ATTR_PFC,
+ DCB_CAP_ATTR_UP2TC,
+ DCB_CAP_ATTR_PG_TCS,
+ DCB_CAP_ATTR_PFC_TCS,
+ DCB_CAP_ATTR_GSP,
+ DCB_CAP_ATTR_BCN,
+
+ __DCB_CAP_ATTR_ENUM_MAX,
+ DCB_CAP_ATTR_MAX = __DCB_CAP_ATTR_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcbnl_numtcs_attrs - number of traffic classes
+ *
+ * @DCB_NUMTCS_ATTR_UNDEFINED: unspecified attribute to catch errors
+ * @DCB_NUMTCS_ATTR_ALL: (NLA_FLAG) all traffic class attributes
+ * @DCB_NUMTCS_ATTR_PG: (NLA_U8) number of traffic classes used for
+ * priority groups
+ * @DCB_NUMTCS_ATTR_PFC: (NLA_U8) number of traffic classes which can
+ * support priority flow control
+ */
+enum dcbnl_numtcs_attrs {
+ DCB_NUMTCS_ATTR_UNDEFINED,
+ DCB_NUMTCS_ATTR_ALL,
+ DCB_NUMTCS_ATTR_PG,
+ DCB_NUMTCS_ATTR_PFC,
+
+ __DCB_NUMTCS_ATTR_ENUM_MAX,
+ DCB_NUMTCS_ATTR_MAX = __DCB_NUMTCS_ATTR_ENUM_MAX - 1,
+};
+
+enum dcbnl_bcn_attrs{
+ DCB_BCN_ATTR_UNDEFINED = 0,
+
+ DCB_BCN_ATTR_RP_0,
+ DCB_BCN_ATTR_RP_1,
+ DCB_BCN_ATTR_RP_2,
+ DCB_BCN_ATTR_RP_3,
+ DCB_BCN_ATTR_RP_4,
+ DCB_BCN_ATTR_RP_5,
+ DCB_BCN_ATTR_RP_6,
+ DCB_BCN_ATTR_RP_7,
+ DCB_BCN_ATTR_RP_ALL,
+
+ DCB_BCN_ATTR_BCNA_0,
+ DCB_BCN_ATTR_BCNA_1,
+ DCB_BCN_ATTR_ALPHA,
+ DCB_BCN_ATTR_BETA,
+ DCB_BCN_ATTR_GD,
+ DCB_BCN_ATTR_GI,
+ DCB_BCN_ATTR_TMAX,
+ DCB_BCN_ATTR_TD,
+ DCB_BCN_ATTR_RMIN,
+ DCB_BCN_ATTR_W,
+ DCB_BCN_ATTR_RD,
+ DCB_BCN_ATTR_RU,
+ DCB_BCN_ATTR_WRTT,
+ DCB_BCN_ATTR_RI,
+ DCB_BCN_ATTR_C,
+ DCB_BCN_ATTR_ALL,
+
+ __DCB_BCN_ATTR_ENUM_MAX,
+ DCB_BCN_ATTR_MAX = __DCB_BCN_ATTR_ENUM_MAX - 1,
+};
+
+/**
+ * enum dcb_general_attr_values - general DCB attribute values
+ *
+ * @DCB_ATTR_UNDEFINED: value used to indicate an attribute is not supported
+ *
+ */
+enum dcb_general_attr_values {
+ DCB_ATTR_VALUE_UNDEFINED = 0xff
+};
+
+
+#endif /* __LINUX_DCBNL_H__ */
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 6080449fbec9..61734e27abb7 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -168,6 +168,8 @@ enum {
DCCPO_MIN_CCID_SPECIFIC = 128,
DCCPO_MAX_CCID_SPECIFIC = 255,
};
+/* maximum size of a single TLV-encoded DCCP option (sans type/len bytes) */
+#define DCCP_SINGLE_OPT_MAXLEN 253
/* DCCP CCIDS */
enum {
@@ -176,29 +178,23 @@ enum {
};
/* DCCP features (RFC 4340 section 6.4) */
-enum {
+enum dccp_feature_numbers {
DCCPF_RESERVED = 0,
DCCPF_CCID = 1,
- DCCPF_SHORT_SEQNOS = 2, /* XXX: not yet implemented */
+ DCCPF_SHORT_SEQNOS = 2,
DCCPF_SEQUENCE_WINDOW = 3,
- DCCPF_ECN_INCAPABLE = 4, /* XXX: not yet implemented */
+ DCCPF_ECN_INCAPABLE = 4,
DCCPF_ACK_RATIO = 5,
DCCPF_SEND_ACK_VECTOR = 6,
DCCPF_SEND_NDP_COUNT = 7,
DCCPF_MIN_CSUM_COVER = 8,
- DCCPF_DATA_CHECKSUM = 9, /* XXX: not yet implemented */
+ DCCPF_DATA_CHECKSUM = 9,
/* 10-127 reserved */
DCCPF_MIN_CCID_SPECIFIC = 128,
+ DCCPF_SEND_LEV_RATE = 192, /* RFC 4342, sec. 8.4 */
DCCPF_MAX_CCID_SPECIFIC = 255,
};
-/* this structure is argument to DCCP_SOCKOPT_CHANGE_X */
-struct dccp_so_feat {
- __u8 dccpsf_feat;
- __u8 __user *dccpsf_val;
- __u8 dccpsf_len;
-};
-
/* DCCP socket options */
#define DCCP_SOCKOPT_PACKET_SIZE 1 /* XXX deprecated, without effect */
#define DCCP_SOCKOPT_SERVICE 2
@@ -208,6 +204,10 @@ struct dccp_so_feat {
#define DCCP_SOCKOPT_SERVER_TIMEWAIT 6
#define DCCP_SOCKOPT_SEND_CSCOV 10
#define DCCP_SOCKOPT_RECV_CSCOV 11
+#define DCCP_SOCKOPT_AVAILABLE_CCIDS 12
+#define DCCP_SOCKOPT_CCID 13
+#define DCCP_SOCKOPT_TX_CCID 14
+#define DCCP_SOCKOPT_RX_CCID 15
#define DCCP_SOCKOPT_CCID_RX_INFO 128
#define DCCP_SOCKOPT_CCID_TX_INFO 192
@@ -360,7 +360,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb)
#define DCCPF_INITIAL_SEQUENCE_WINDOW 100
#define DCCPF_INITIAL_ACK_RATIO 2
#define DCCPF_INITIAL_CCID DCCPC_CCID2
-#define DCCPF_INITIAL_SEND_ACK_VECTOR 1
/* FIXME: for now we're default to 1 but it should really be 0 */
#define DCCPF_INITIAL_SEND_NDP_COUNT 1
@@ -370,20 +369,11 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb)
* Will be used to pass the state from dccp_request_sock to dccp_sock.
*
* @dccpms_sequence_window - Sequence Window Feature (section 7.5.2)
- * @dccpms_ccid - Congestion Control Id (CCID) (section 10)
- * @dccpms_send_ack_vector - Send Ack Vector Feature (section 11.5)
- * @dccpms_send_ndp_count - Send NDP Count Feature (7.7.2)
- * @dccpms_ack_ratio - Ack Ratio Feature (section 11.3)
* @dccpms_pending - List of features being negotiated
* @dccpms_conf -
*/
struct dccp_minisock {
__u64 dccpms_sequence_window;
- __u8 dccpms_rx_ccid;
- __u8 dccpms_tx_ccid;
- __u8 dccpms_send_ack_vector;
- __u8 dccpms_send_ndp_count;
- __u8 dccpms_ack_ratio;
struct list_head dccpms_pending;
struct list_head dccpms_conf;
};
@@ -411,6 +401,7 @@ extern void dccp_minisock_init(struct dccp_minisock *dmsk);
* @dreq_iss: initial sequence number sent on the Response (RFC 4340, 7.1)
* @dreq_isr: initial sequence number received on the Request
* @dreq_service: service code present on the Request (there is just one)
+ * @dreq_featneg: feature negotiation options for this connection
* The following two fields are analogous to the ones in dccp_sock:
* @dreq_timestamp_echo: last received timestamp to echo (13.1)
* @dreq_timestamp_echo: the time of receiving the last @dreq_timestamp_echo
@@ -420,6 +411,7 @@ struct dccp_request_sock {
__u64 dreq_iss;
__u64 dreq_isr;
__be32 dreq_service;
+ struct list_head dreq_featneg;
__u32 dreq_timestamp_echo;
__u32 dreq_timestamp_time;
};
@@ -493,10 +485,12 @@ struct dccp_ackvec;
* @dccps_r_ack_ratio - feature-remote Ack Ratio
* @dccps_pcslen - sender partial checksum coverage (via sockopt)
* @dccps_pcrlen - receiver partial checksum coverage (via sockopt)
+ * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2)
* @dccps_ndp_count - number of Non Data Packets since last data packet
* @dccps_mss_cache - current value of MSS (path MTU minus header sizes)
* @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4)
* @dccps_minisock - associated minisock (accessed via dccp_msk)
+ * @dccps_featneg - tracks feature-negotiation state (mostly during handshake)
* @dccps_hc_rx_ackvec - rx half connection ack vector
* @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection)
* @dccps_hc_tx_ccid - CCID used for the sender (or sending half-connection)
@@ -529,11 +523,13 @@ struct dccp_sock {
__u32 dccps_timestamp_time;
__u16 dccps_l_ack_ratio;
__u16 dccps_r_ack_ratio;
- __u16 dccps_pcslen;
- __u16 dccps_pcrlen;
+ __u8 dccps_pcslen:4;
+ __u8 dccps_pcrlen:4;
+ __u8 dccps_send_ndp_count:1;
__u64 dccps_ndp_count:48;
unsigned long dccps_rate_last;
struct dccp_minisock dccps_minisock;
+ struct list_head dccps_featneg;
struct dccp_ackvec *dccps_hc_rx_ackvec;
struct ccid *dccps_hc_rx_ccid;
struct ccid *dccps_hc_tx_ccid;
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index e5084eb5943a..34161907b2f8 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -44,8 +44,10 @@ extern const struct dmi_device * dmi_find_device(int type, const char *name,
extern void dmi_scan_machine(void);
extern int dmi_get_year(int field);
extern int dmi_name_in_vendors(const char *str);
+extern int dmi_name_in_serial(const char *str);
extern int dmi_available;
extern int dmi_walk(void (*decode)(const struct dmi_header *));
+extern bool dmi_match(enum dmi_field f, const char *str);
#else
@@ -56,9 +58,12 @@ static inline const struct dmi_device * dmi_find_device(int type, const char *na
static inline void dmi_scan_machine(void) { return; }
static inline int dmi_get_year(int year) { return 0; }
static inline int dmi_name_in_vendors(const char *s) { return 0; }
+static inline int dmi_name_in_serial(const char *s) { return 0; }
#define dmi_available 0
static inline int dmi_walk(void (*decode)(const struct dmi_header *))
{ return -1; }
+static inline bool dmi_match(enum dmi_field f, const char *str)
+ { return false; }
#endif
diff --git a/include/linux/elevator.h b/include/linux/elevator.h
index 92f6f634e3e6..7a204256b155 100644
--- a/include/linux/elevator.h
+++ b/include/linux/elevator.h
@@ -28,7 +28,7 @@ typedef void (elevator_activate_req_fn) (struct request_queue *, struct request
typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *);
typedef void *(elevator_init_fn) (struct request_queue *);
-typedef void (elevator_exit_fn) (elevator_t *);
+typedef void (elevator_exit_fn) (struct elevator_queue *);
struct elevator_ops
{
@@ -62,8 +62,8 @@ struct elevator_ops
struct elv_fs_entry {
struct attribute attr;
- ssize_t (*show)(elevator_t *, char *);
- ssize_t (*store)(elevator_t *, const char *, size_t);
+ ssize_t (*show)(struct elevator_queue *, char *);
+ ssize_t (*store)(struct elevator_queue *, const char *, size_t);
};
/*
@@ -130,7 +130,7 @@ extern ssize_t elv_iosched_show(struct request_queue *, char *);
extern ssize_t elv_iosched_store(struct request_queue *, const char *, size_t);
extern int elevator_init(struct request_queue *, char *);
-extern void elevator_exit(elevator_t *);
+extern void elevator_exit(struct elevator_queue *);
extern int elv_rq_merge_ok(struct request *, struct bio *);
/*
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index 25d62e6e3290..1cb0f0b90926 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -27,6 +27,7 @@
#include <linux/if_ether.h>
#include <linux/netdevice.h>
#include <linux/random.h>
+#include <asm/unaligned.h>
#ifdef __KERNEL__
extern __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
@@ -41,6 +42,10 @@ extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh);
extern void eth_header_cache_update(struct hh_cache *hh,
const struct net_device *dev,
const unsigned char *haddr);
+extern int eth_mac_addr(struct net_device *dev, void *p);
+extern int eth_change_mtu(struct net_device *dev, int new_mtu);
+extern int eth_validate_addr(struct net_device *dev);
+
extern struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count);
@@ -136,6 +141,47 @@ static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2)
BUILD_BUG_ON(ETH_ALEN != 6);
return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
}
+
+static inline unsigned long zap_last_2bytes(unsigned long value)
+{
+#ifdef __BIG_ENDIAN
+ return value >> 16;
+#else
+ return value << 16;
+#endif
+}
+
+/**
+ * compare_ether_addr_64bits - Compare two Ethernet addresses
+ * @addr1: Pointer to an array of 8 bytes
+ * @addr2: Pointer to an other array of 8 bytes
+ *
+ * Compare two ethernet addresses, returns 0 if equal.
+ * Same result than "memcmp(addr1, addr2, ETH_ALEN)" but without conditional
+ * branches, and possibly long word memory accesses on CPU allowing cheap
+ * unaligned memory reads.
+ * arrays = { byte1, byte2, byte3, byte4, byte6, byte7, pad1, pad2}
+ *
+ * Please note that alignment of addr1 & addr2 is only guaranted to be 16 bits.
+ */
+
+static inline unsigned compare_ether_addr_64bits(const u8 addr1[6+2],
+ const u8 addr2[6+2])
+{
+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ unsigned long fold = ((*(unsigned long *)addr1) ^
+ (*(unsigned long *)addr2));
+
+ if (sizeof(fold) == 8)
+ return zap_last_2bytes(fold) != 0;
+
+ fold |= zap_last_2bytes((*(unsigned long *)(addr1 + 4)) ^
+ (*(unsigned long *)(addr2 + 4)));
+ return fold != 0;
+#else
+ return compare_ether_addr(addr1, addr2);
+#endif
+}
#endif /* __KERNEL__ */
#endif /* _LINUX_ETHERDEVICE_H */
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index b4b038b89ee6..27c67a542235 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -467,6 +467,8 @@ struct ethtool_ops {
#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */
#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */
+#define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */
+#define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/include/linux/fault-inject.h b/include/linux/fault-inject.h
index 32368c4f0326..06ca9b21dad2 100644
--- a/include/linux/fault-inject.h
+++ b/include/linux/fault-inject.h
@@ -81,4 +81,13 @@ static inline void cleanup_fault_attr_dentries(struct fault_attr *attr)
#endif /* CONFIG_FAULT_INJECTION */
+#ifdef CONFIG_FAILSLAB
+extern bool should_failslab(size_t size, gfp_t gfpflags);
+#else
+static inline bool should_failslab(size_t size, gfp_t gfpflags)
+{
+ return false;
+}
+#endif /* CONFIG_FAILSLAB */
+
#endif /* _LINUX_FAULT_INJECT_H */
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 75a81eaf3430..1ee63df5be92 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -888,7 +888,7 @@ struct fb_info {
#define fb_writeq sbus_writeq
#define fb_memset sbus_memset_io
-#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__) || defined(__avr32__)
+#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || defined(__sh__) || defined(__powerpc__) || defined(__avr32__)
#define fb_readb __raw_readb
#define fb_readw __raw_readw
diff --git a/include/linux/fddidevice.h b/include/linux/fddidevice.h
index e61e42dfd317..155bafd9e886 100644
--- a/include/linux/fddidevice.h
+++ b/include/linux/fddidevice.h
@@ -27,6 +27,7 @@
#ifdef __KERNEL__
extern __be16 fddi_type_trans(struct sk_buff *skb,
struct net_device *dev);
+extern int fddi_change_mtu(struct net_device *dev, int new_mtu);
extern struct net_device *alloc_fddidev(int sizeof_priv);
#endif
diff --git a/include/linux/filter.h b/include/linux/filter.h
index b6ea9aa9e853..1354aaf6abbe 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -122,7 +122,8 @@ struct sock_fprog /* Required for SO_ATTACH_FILTER. */
#define SKF_AD_PKTTYPE 4
#define SKF_AD_IFINDEX 8
#define SKF_AD_NLATTR 12
-#define SKF_AD_MAX 16
+#define SKF_AD_NLATTR_NEST 16
+#define SKF_AD_MAX 20
#define SKF_NET_OFF (-0x100000)
#define SKF_LL_OFF (-0x200000)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 4a853ef6fd35..001ded4845b4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -82,6 +82,14 @@ extern int dir_notify_enable;
(specialy hack for floppy.c) */
#define FMODE_WRITE_IOCTL ((__force fmode_t)128)
+/*
+ * Don't update ctime and mtime.
+ *
+ * Currently a special hack for the XFS open_by_handle ioctl, but we'll
+ * hopefully graduate it to a proper O_CMTIME flag supported by open(2) soon.
+ */
+#define FMODE_NOCMTIME ((__force fmode_t)2048)
+
#define RW_MASK 1
#define RWA_MASK 2
#define READ 0
@@ -316,6 +324,7 @@ struct poll_table_struct;
struct kstatfs;
struct vm_area_struct;
struct vfsmount;
+struct cred;
extern void __init inode_init(void);
extern void __init inode_init_early(void);
@@ -827,7 +836,7 @@ struct file {
fmode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
- unsigned int f_uid, f_gid;
+ const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
@@ -1194,7 +1203,7 @@ enum {
#define has_fs_excl() atomic_read(&current->fs_excl)
#define is_owner_or_cap(inode) \
- ((current->fsuid == (inode)->i_uid) || capable(CAP_FOWNER))
+ ((current_fsuid() == (inode)->i_uid) || capable(CAP_FOWNER))
/* not quite ready to be deprecated, but... */
extern void lock_super(struct super_block *);
@@ -1674,7 +1683,8 @@ extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
extern long do_sys_open(int dfd, const char __user *filename, int flags,
int mode);
extern struct file *filp_open(const char *, int, int);
-extern struct file * dentry_open(struct dentry *, struct vfsmount *, int);
+extern struct file * dentry_open(struct dentry *, struct vfsmount *, int,
+ const struct cred *);
extern int filp_close(struct file *, fl_owner_t id);
extern char * getname(const char __user *);
@@ -1875,7 +1885,9 @@ extern loff_t default_llseek(struct file *file, loff_t offset, int origin);
extern loff_t vfs_llseek(struct file *file, loff_t offset, int origin);
+extern struct inode * inode_init_always(struct super_block *, struct inode *);
extern void inode_init_once(struct inode *);
+extern void inode_add_to_lists(struct super_block *, struct inode *);
extern void iput(struct inode *);
extern struct inode * igrab(struct inode *);
extern ino_t iunique(struct super_block *, ino_t);
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 708bab58d8d0..d9051d717d27 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -47,12 +47,7 @@
struct gianfar_platform_data {
/* device specific information */
u32 device_flags;
- /* board specific information */
- u32 board_flags;
- int mdio_bus; /* Bus controlled by us */
- char bus_id[MII_BUS_ID_SIZE]; /* Bus PHY is on */
- u32 phy_id;
- u8 mac_addr[6];
+ char bus_id[BUS_ID_SIZE];
phy_interface_t interface;
};
@@ -61,17 +56,6 @@ struct gianfar_mdio_data {
int irq[32];
};
-/* Flags related to gianfar device features */
-#define FSL_GIANFAR_DEV_HAS_GIGABIT 0x00000001
-#define FSL_GIANFAR_DEV_HAS_COALESCE 0x00000002
-#define FSL_GIANFAR_DEV_HAS_RMON 0x00000004
-#define FSL_GIANFAR_DEV_HAS_MULTI_INTR 0x00000008
-#define FSL_GIANFAR_DEV_HAS_CSUM 0x00000010
-#define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020
-#define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040
-#define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080
-#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100
-
/* Flags in gianfar_platform_data */
#define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */
#define FSL_GIANFAR_BRD_IS_REDUCED 0x00000002 /* Set if RGMII, RMII */
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 985b28dc2ba9..677432b9cb7e 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -87,6 +87,14 @@ static inline void ftrace_stop(void) { }
static inline void ftrace_start(void) { }
#endif /* CONFIG_FUNCTION_TRACER */
+#ifdef CONFIG_STACK_TRACER
+extern int stack_tracer_enabled;
+int
+stack_trace_sysctl(struct ctl_table *table, int write,
+ struct file *file, void __user *buffer, size_t *lenp,
+ loff_t *ppos);
+#endif
+
#ifdef CONFIG_DYNAMIC_FTRACE
/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
#include <asm/ftrace.h>
@@ -295,7 +303,7 @@ extern void ftrace_dump(void);
static inline void
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { }
static inline int
-ftrace_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 0)));
+ftrace_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
static inline void tracing_start(void) { }
static inline void tracing_stop(void) { }
@@ -378,6 +386,16 @@ struct ftrace_graph_ret {
*/
#define __notrace_funcgraph notrace
+/*
+ * We want to which function is an entrypoint of a hardirq.
+ * That will help us to put a signal on output.
+ */
+#define __irq_entry __attribute__((__section__(".irqentry.text")))
+
+/* Limits of hardirq entrypoints */
+extern char __irqentry_text_start[];
+extern char __irqentry_text_end[];
+
#define FTRACE_RETFUNC_DEPTH 50
#define FTRACE_RETSTACK_ALLOC_SIZE 32
/* Type of the callback handlers for tracing function graph*/
@@ -415,6 +433,7 @@ static inline void unpause_graph_tracing(void)
#else
#define __notrace_funcgraph
+#define __irq_entry
static inline void ftrace_graph_init_task(struct task_struct *t) { }
static inline void ftrace_graph_exit_task(struct task_struct *t) { }
diff --git a/include/linux/futex.h b/include/linux/futex.h
index 8f627b9ae2b1..3bf5bb5a34f9 100644
--- a/include/linux/futex.h
+++ b/include/linux/futex.h
@@ -25,7 +25,8 @@ union ktime;
#define FUTEX_WAKE_BITSET 10
#define FUTEX_PRIVATE_FLAG 128
-#define FUTEX_CMD_MASK ~FUTEX_PRIVATE_FLAG
+#define FUTEX_CLOCK_REALTIME 256
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 3df7742ce246..16948eaecae3 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -126,6 +126,7 @@ struct blk_scsi_cmd_filter {
struct disk_part_tbl {
struct rcu_head rcu_head;
int len;
+ struct hd_struct *last_lookup;
struct hd_struct *part[];
};
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index 89a56d79e4c6..f83288347dda 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -119,13 +119,17 @@ static inline void account_system_vtime(struct task_struct *tsk)
}
#endif
-#if defined(CONFIG_PREEMPT_RCU) && defined(CONFIG_NO_HZ)
+#if defined(CONFIG_NO_HZ) && !defined(CONFIG_CLASSIC_RCU)
extern void rcu_irq_enter(void);
extern void rcu_irq_exit(void);
+extern void rcu_nmi_enter(void);
+extern void rcu_nmi_exit(void);
#else
# define rcu_irq_enter() do { } while (0)
# define rcu_irq_exit() do { } while (0)
-#endif /* CONFIG_PREEMPT_RCU */
+# define rcu_nmi_enter() do { } while (0)
+# define rcu_nmi_exit() do { } while (0)
+#endif /* #if defined(CONFIG_NO_HZ) && !defined(CONFIG_CLASSIC_RCU) */
/*
* It is safe to do non-atomic ops on ->hardirq_context,
@@ -135,7 +139,6 @@ extern void rcu_irq_exit(void);
*/
#define __irq_enter() \
do { \
- rcu_irq_enter(); \
account_system_vtime(current); \
add_preempt_count(HARDIRQ_OFFSET); \
trace_hardirq_enter(); \
@@ -154,7 +157,6 @@ extern void irq_enter(void);
trace_hardirq_exit(); \
account_system_vtime(current); \
sub_preempt_count(HARDIRQ_OFFSET); \
- rcu_irq_exit(); \
} while (0)
/*
@@ -166,11 +168,14 @@ extern void irq_exit(void);
do { \
ftrace_nmi_enter(); \
lockdep_off(); \
+ rcu_nmi_enter(); \
__irq_enter(); \
} while (0)
+
#define nmi_exit() \
do { \
__irq_exit(); \
+ rcu_nmi_exit(); \
lockdep_on(); \
ftrace_nmi_exit(); \
} while (0)
diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h
index c59769693bee..fd47a151665e 100644
--- a/include/linux/hdlc.h
+++ b/include/linux/hdlc.h
@@ -43,7 +43,7 @@ struct hdlc_proto {
};
-/* Pointed to by dev->priv */
+/* Pointed to by netdev_priv(dev) */
typedef struct hdlc_device {
/* used by HDLC layer to take control over HDLC device from hw driver*/
int (*attach)(struct net_device *dev,
@@ -80,7 +80,7 @@ struct net_device *alloc_hdlcdev(void *priv);
static inline struct hdlc_device* dev_to_hdlc(struct net_device *dev)
{
- return dev->priv;
+ return netdev_priv(dev);
}
static __inline__ void debug_frame(const struct sk_buff *skb)
diff --git a/include/linux/hippidevice.h b/include/linux/hippidevice.h
index bab303dafd6e..f148e4908410 100644
--- a/include/linux/hippidevice.h
+++ b/include/linux/hippidevice.h
@@ -32,7 +32,9 @@ struct hippi_cb {
};
extern __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev);
-
+extern int hippi_change_mtu(struct net_device *dev, int new_mtu);
+extern int hippi_mac_addr(struct net_device *dev, void *p);
+extern int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p);
extern struct net_device *alloc_hippi_dev(int sizeof_priv);
#endif
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 3eba43878dcb..bd37078c2d7d 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -43,26 +43,6 @@ enum hrtimer_restart {
};
/*
- * hrtimer callback modes:
- *
- * HRTIMER_CB_SOFTIRQ: Callback must run in softirq context
- * HRTIMER_CB_IRQSAFE_PERCPU: Callback must run in hardirq context
- * Special mode for tick emulation and
- * scheduler timer. Such timers are per
- * cpu and not allowed to be migrated on
- * cpu unplug.
- * HRTIMER_CB_IRQSAFE_UNLOCKED: Callback should run in hardirq context
- * with timer->base lock unlocked
- * used for timers which call wakeup to
- * avoid lock order problems with rq->lock
- */
-enum hrtimer_cb_mode {
- HRTIMER_CB_SOFTIRQ,
- HRTIMER_CB_IRQSAFE_PERCPU,
- HRTIMER_CB_IRQSAFE_UNLOCKED,
-};
-
-/*
* Values to track state of the timer
*
* Possible states:
@@ -70,7 +50,6 @@ enum hrtimer_cb_mode {
* 0x00 inactive
* 0x01 enqueued into rbtree
* 0x02 callback function running
- * 0x04 callback pending (high resolution mode)
*
* Special cases:
* 0x03 callback function running and enqueued
@@ -92,8 +71,7 @@ enum hrtimer_cb_mode {
#define HRTIMER_STATE_INACTIVE 0x00
#define HRTIMER_STATE_ENQUEUED 0x01
#define HRTIMER_STATE_CALLBACK 0x02
-#define HRTIMER_STATE_PENDING 0x04
-#define HRTIMER_STATE_MIGRATE 0x08
+#define HRTIMER_STATE_MIGRATE 0x04
/**
* struct hrtimer - the basic hrtimer structure
@@ -109,8 +87,6 @@ enum hrtimer_cb_mode {
* @function: timer expiry callback function
* @base: pointer to the timer base (per cpu and per clock)
* @state: state information (See bit values above)
- * @cb_mode: high resolution timer feature to select the callback execution
- * mode
* @cb_entry: list head to enqueue an expired timer into the callback list
* @start_site: timer statistics field to store the site where the timer
* was started
@@ -129,7 +105,6 @@ struct hrtimer {
struct hrtimer_clock_base *base;
unsigned long state;
struct list_head cb_entry;
- enum hrtimer_cb_mode cb_mode;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
@@ -188,15 +163,11 @@ struct hrtimer_clock_base {
* @check_clocks: Indictator, when set evaluate time source and clock
* event devices whether high resolution mode can be
* activated.
- * @cb_pending: Expired timers are moved from the rbtree to this
- * list in the timer interrupt. The list is processed
- * in the softirq.
* @nr_events: Total number of timer interrupt events
*/
struct hrtimer_cpu_base {
spinlock_t lock;
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
- struct list_head cb_pending;
#ifdef CONFIG_HIGH_RES_TIMERS
ktime_t expires_next;
int hres_active;
@@ -404,8 +375,7 @@ static inline int hrtimer_active(const struct hrtimer *timer)
*/
static inline int hrtimer_is_queued(struct hrtimer *timer)
{
- return timer->state &
- (HRTIMER_STATE_ENQUEUED | HRTIMER_STATE_PENDING);
+ return timer->state & HRTIMER_STATE_ENQUEUED;
}
/*
diff --git a/include/linux/ide.h b/include/linux/ide.h
index 010fb26a1579..e99c56de7f56 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -122,8 +122,6 @@ struct ide_io_ports {
#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
#define SECTOR_SIZE 512
-#define IDE_LARGE_SEEK(b1,b2,t) (((b1) > (b2) + (t)) || ((b2) > (b1) + (t)))
-
/*
* Timeouts for various operations:
*/
@@ -172,9 +170,7 @@ typedef int (ide_ack_intr_t)(struct hwif_s *);
enum { ide_unknown, ide_generic, ide_pci,
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd65xx, ide_umc8672, ide_ht6560b,
- ide_rz1000, ide_trm290,
- ide_cmd646, ide_cy82c693, ide_4drives,
- ide_pmac, ide_acorn,
+ ide_4drives, ide_pmac, ide_acorn,
ide_au1xxx, ide_palm3710
};
@@ -496,8 +492,6 @@ enum {
* when more than one interrupt is needed.
*/
IDE_AFLAG_LIMIT_NFRAMES = (1 << 7),
- /* Seeking in progress. */
- IDE_AFLAG_SEEKING = (1 << 8),
/* Saved TOC information is current. */
IDE_AFLAG_TOC_VALID = (1 << 9),
/* We think that the drive door is locked. */
@@ -845,8 +839,6 @@ typedef struct hwif_s {
unsigned extra_ports; /* number of extra dma ports */
unsigned present : 1; /* this interface exists */
- unsigned serialized : 1; /* serialized all channel operation */
- unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
struct device gendev;
@@ -909,6 +901,8 @@ typedef struct hwgroup_s {
int req_gen;
int req_gen_timer;
+
+ spinlock_t lock;
} ide_hwgroup_t;
typedef struct ide_driver_s ide_driver_t;
@@ -1122,6 +1116,14 @@ enum {
IDE_PM_COMPLETED,
};
+int generic_ide_suspend(struct device *, pm_message_t);
+int generic_ide_resume(struct device *);
+
+void ide_complete_power_step(ide_drive_t *, struct request *);
+ide_startstop_t ide_start_power_step(ide_drive_t *, struct request *);
+void ide_complete_pm_request(ide_drive_t *, struct request *);
+void ide_check_pm_state(ide_drive_t *, struct request *);
+
/*
* Subdrivers support.
*
@@ -1376,8 +1378,8 @@ enum {
IDE_HFLAG_LEGACY_IRQS = (1 << 21),
/* force use of legacy IRQs */
IDE_HFLAG_FORCE_LEGACY_IRQS = (1 << 22),
- /* limit LBA48 requests to 256 sectors */
- IDE_HFLAG_RQSIZE_256 = (1 << 23),
+ /* host is TRM290 */
+ IDE_HFLAG_TRM290 = (1 << 23),
/* use 32-bit I/O ops */
IDE_HFLAG_IO_32BIT = (1 << 24),
/* unmask IRQs */
@@ -1415,6 +1417,9 @@ struct ide_port_info {
ide_pci_enablebit_t enablebits[2];
hwif_chipset_t chipset;
+
+ u16 max_sectors; /* if < than the default one */
+
u32 host_flags;
u8 pio_mask;
u8 swdma_mask;
@@ -1610,13 +1615,13 @@ extern struct mutex ide_cfg_mtx;
/*
* Structure locking:
*
- * ide_cfg_mtx and ide_lock together protect changes to
- * ide_hwif_t->{next,hwgroup}
+ * ide_cfg_mtx and hwgroup->lock together protect changes to
+ * ide_hwif_t->next
* ide_drive_t->next
*
- * ide_hwgroup_t->busy: ide_lock
- * ide_hwgroup_t->hwif: ide_lock
- * ide_hwif_t->mate: constant, no locking
+ * ide_hwgroup_t->busy: hwgroup->lock
+ * ide_hwgroup_t->hwif: hwgroup->lock
+ * ide_hwif_t->{hwgroup,mate}: constant, no locking
* ide_drive_t->hwif: constant, no locking
*/
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 14126bc36641..c4e6ca1a6306 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -12,8 +12,8 @@
* published by the Free Software Foundation.
*/
-#ifndef IEEE80211_H
-#define IEEE80211_H
+#ifndef LINUX_IEEE80211_H
+#define LINUX_IEEE80211_H
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -97,7 +97,10 @@
#define IEEE80211_MAX_FRAME_LEN 2352
#define IEEE80211_MAX_SSID_LEN 32
+
#define IEEE80211_MAX_MESH_ID_LEN 32
+#define IEEE80211_MESH_CONFIG_LEN 19
+
#define IEEE80211_QOS_CTL_LEN 2
#define IEEE80211_QOS_CTL_TID_MASK 0x000F
#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007
@@ -666,6 +669,13 @@ struct ieee80211_cts {
u8 ra[6];
} __attribute__ ((packed));
+struct ieee80211_pspoll {
+ __le16 frame_control;
+ __le16 aid;
+ u8 bssid[6];
+ u8 ta[6];
+} __attribute__ ((packed));
+
/**
* struct ieee80211_bar - HT Block Ack Request
*
@@ -685,28 +695,88 @@ struct ieee80211_bar {
#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004
+
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
+/**
+ * struct ieee80211_mcs_info - MCS information
+ * @rx_mask: RX mask
+ * @rx_highest: highest supported RX rate
+ * @tx_params: TX parameters
+ */
+struct ieee80211_mcs_info {
+ u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN];
+ __le16 rx_highest;
+ u8 tx_params;
+ u8 reserved[3];
+} __attribute__((packed));
+
+/* 802.11n HT capability MSC set */
+#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
+#define IEEE80211_HT_MCS_TX_DEFINED 0x01
+#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02
+/* value 0 == 1 stream etc */
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4
+#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10
+
+/*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \
+ (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8)
+
/**
* struct ieee80211_ht_cap - HT capabilities
*
- * This structure refers to "HT capabilities element" as
- * described in 802.11n draft section 7.3.2.52
+ * This structure is the "HT capabilities element" as
+ * described in 802.11n D5.0 7.3.2.57
*/
struct ieee80211_ht_cap {
__le16 cap_info;
u8 ampdu_params_info;
- u8 supp_mcs_set[16];
+
+ /* 16 bytes MCS information */
+ struct ieee80211_mcs_info mcs;
+
__le16 extended_ht_cap_info;
__le32 tx_BF_cap_info;
u8 antenna_selection_info;
} __attribute__ ((packed));
+/* 802.11n HT capabilities masks (for cap_info) */
+#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
+#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
+#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_GRN_FLD 0x0010
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+#define IEEE80211_HT_CAP_TX_STBC 0x0080
+#define IEEE80211_HT_CAP_RX_STBC 0x0300
+#define IEEE80211_HT_CAP_DELAY_BA 0x0400
+#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
+#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
+#define IEEE80211_HT_CAP_PSMP_SUPPORT 0x2000
+#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000
+#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000
+
+/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
+#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
+#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+
/**
- * struct ieee80211_ht_cap - HT additional information
+ * struct ieee80211_ht_info - HT information
*
- * This structure refers to "HT information element" as
- * described in 802.11n draft section 7.3.2.53
+ * This structure is the "HT information element" as
+ * described in 802.11n D5.0 7.3.2.58
*/
-struct ieee80211_ht_addt_info {
+struct ieee80211_ht_info {
u8 control_chan;
u8 ht_param;
__le16 operation_mode;
@@ -714,36 +784,33 @@ struct ieee80211_ht_addt_info {
u8 basic_set[16];
} __attribute__ ((packed));
-/* 802.11n HT capabilities masks */
-#define IEEE80211_HT_CAP_SUP_WIDTH 0x0002
-#define IEEE80211_HT_CAP_SM_PS 0x000C
-#define IEEE80211_HT_CAP_GRN_FLD 0x0010
-#define IEEE80211_HT_CAP_SGI_20 0x0020
-#define IEEE80211_HT_CAP_SGI_40 0x0040
-#define IEEE80211_HT_CAP_DELAY_BA 0x0400
-#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
-#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
-/* 802.11n HT capability AMPDU settings */
-#define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03
-#define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C
-/* 802.11n HT capability MSC set */
-#define IEEE80211_SUPP_MCS_SET_UEQM 4
-#define IEEE80211_HT_CAP_MAX_STREAMS 4
-#define IEEE80211_SUPP_MCS_SET_LEN 10
-/* maximum streams the spec allows */
-#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01
-#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02
-#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C
-#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10
-/* 802.11n HT IE masks */
-#define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03
-#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00
-#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01
-#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03
-#define IEEE80211_HT_IE_CHA_WIDTH 0x04
-#define IEEE80211_HT_IE_HT_PROTECTION 0x0003
-#define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004
-#define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010
+/* for ht_param */
+#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03
+#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00
+#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01
+#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03
+#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04
+#define IEEE80211_HT_PARAM_RIFS_MODE 0x08
+#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10
+#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0
+
+/* for operation_mode */
+#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1
+#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3
+#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004
+#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010
+
+/* for stbc_param */
+#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040
+#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080
+#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100
+#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200
+#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400
+#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800
+
/* block-ack parameters */
#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
@@ -769,7 +836,6 @@ struct ieee80211_ht_addt_info {
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
-#define WLAN_AUTH_FAST_BSS_TRANSITION 2
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128
@@ -949,7 +1015,7 @@ enum ieee80211_eid {
WLAN_EID_EXT_SUPP_RATES = 50,
/* 802.11n */
WLAN_EID_HT_CAPABILITY = 45,
- WLAN_EID_HT_EXTRA_INFO = 61,
+ WLAN_EID_HT_INFORMATION = 61,
/* 802.11i */
WLAN_EID_RSN = 48,
WLAN_EID_WPA = 221,
@@ -976,6 +1042,68 @@ enum ieee80211_spectrum_mgmt_actioncode {
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};
+/*
+ * IEEE 802.11-2007 7.3.2.9 Country information element
+ *
+ * Minimum length is 8 octets, ie len must be evenly
+ * divisible by 2
+ */
+
+/* Although the spec says 8 I'm seeing 6 in practice */
+#define IEEE80211_COUNTRY_IE_MIN_LEN 6
+
+/*
+ * For regulatory extension stuff see IEEE 802.11-2007
+ * Annex I (page 1141) and Annex J (page 1147). Also
+ * review 7.3.2.9.
+ *
+ * When dot11RegulatoryClassesRequired is true and the
+ * first_channel/reg_extension_id is >= 201 then the IE
+ * compromises of the 'ext' struct represented below:
+ *
+ * - Regulatory extension ID - when generating IE this just needs
+ * to be monotonically increasing for each triplet passed in
+ * the IE
+ * - Regulatory class - index into set of rules
+ * - Coverage class - index into air propagation time (Table 7-27),
+ * in microseconds, you can compute the air propagation time from
+ * the index by multiplying by 3, so index 10 yields a propagation
+ * of 10 us. Valid values are 0-31, values 32-255 are not defined
+ * yet. A value of 0 inicates air propagation of <= 1 us.
+ *
+ * See also Table I.2 for Emission limit sets and table
+ * I.3 for Behavior limit sets. Table J.1 indicates how to map
+ * a reg_class to an emission limit set and behavior limit set.
+ */
+#define IEEE80211_COUNTRY_EXTENSION_ID 201
+
+/*
+ * Channels numbers in the IE must be monotonically increasing
+ * if dot11RegulatoryClassesRequired is not true.
+ *
+ * If dot11RegulatoryClassesRequired is true consecutive
+ * subband triplets following a regulatory triplet shall
+ * have monotonically increasing first_channel number fields.
+ *
+ * Channel numbers shall not overlap.
+ *
+ * Note that max_power is signed.
+ */
+struct ieee80211_country_ie_triplet {
+ union {
+ struct {
+ u8 first_channel;
+ u8 num_channels;
+ s8 max_power;
+ } __attribute__ ((packed)) chans;
+ struct {
+ u8 reg_extension_id;
+ u8 reg_class;
+ u8 coverage_class;
+ } __attribute__ ((packed)) ext;
+ };
+} __attribute__ ((packed));
+
/* BACK action code */
enum ieee80211_back_actioncode {
WLAN_ACTION_ADDBA_REQ = 0,
@@ -1057,4 +1185,4 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
return hdr->addr1;
}
-#endif /* IEEE80211_H */
+#endif /* LINUX_IEEE80211_H */
diff --git a/include/linux/if.h b/include/linux/if.h
index 65246846c844..2a6e29620a96 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -65,6 +65,7 @@
#define IFF_BONDING 0x20 /* bonding master or slave */
#define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */
#define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */
+#define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index 4d3401812e6c..5ff89809a581 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -87,6 +87,9 @@
#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */
#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */
+#define ARPHRD_PHONET 820 /* PhoNet media type */
+#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
+
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
diff --git a/include/linux/in.h b/include/linux/in.h
index db458beef19d..d60122a3a088 100644
--- a/include/linux/in.h
+++ b/include/linux/in.h
@@ -80,6 +80,10 @@ struct in_addr {
/* BSD compatibility */
#define IP_RECVRETOPTS IP_RETOPTS
+/* TProxy original addresses */
+#define IP_ORIGDSTADDR 20
+#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR
+
/* IP_MTU_DISCOVER values */
#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
#define IP_PMTUDISC_WANT 1 /* Use per route hints */
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 23fd8909b9e5..959f5522d10a 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -57,7 +57,6 @@ extern struct nsproxy init_nsproxy;
.mnt_ns = NULL, \
INIT_NET_NS(net_ns) \
INIT_IPC_NS(ipc_ns) \
- .user_ns = &init_user_ns, \
}
#define INIT_SIGHAND(sighand) { \
@@ -113,6 +112,8 @@ extern struct group_info init_groups;
# define CAP_INIT_BSET CAP_INIT_EFF_SET
#endif
+extern struct cred init_cred;
+
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -147,13 +148,10 @@ extern struct group_info init_groups;
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
- .group_info = &init_groups, \
- .cap_effective = CAP_INIT_EFF_SET, \
- .cap_inheritable = CAP_INIT_INH_SET, \
- .cap_permitted = CAP_FULL_SET, \
- .cap_bset = CAP_INIT_BSET, \
- .securebits = SECUREBITS_DEFAULT, \
- .user = INIT_USER, \
+ .real_cred = &init_cred, \
+ .cred = &init_cred, \
+ .cred_exec_mutex = \
+ __MUTEX_INITIALIZER(tsk.cred_exec_mutex), \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
diff --git a/include/linux/input.h b/include/linux/input.h
index 5341e8251f8c..9a6355f74db2 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -659,6 +659,8 @@ struct input_absinfo {
#define SW_RADIO SW_RFKILL_ALL /* deprecated */
#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */
#define SW_DOCK 0x05 /* set = plugged into dock */
+#define SW_LINEOUT_INSERT 0x06 /* set = inserted */
+#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 7e85a6e89e41..91f1ef8e5810 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -109,7 +109,7 @@ extern void enable_irq(unsigned int irq);
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
-extern cpumask_t irq_default_affinity;
+extern cpumask_var_t irq_default_affinity;
extern int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask);
extern int irq_can_set_affinity(unsigned int irq);
@@ -253,9 +253,6 @@ enum
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
-#ifdef CONFIG_HIGH_RES_TIMERS
- HRTIMER_SOFTIRQ,
-#endif
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 641e026eee8f..0b816cae533e 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -278,6 +278,7 @@ struct ipv6_pinfo {
struct in6_addr saddr;
struct in6_addr rcv_saddr;
struct in6_addr daddr;
+ struct in6_pktinfo sticky_pktinfo;
struct in6_addr *daddr_cache;
#ifdef CONFIG_IPV6_SUBTREES
struct in6_addr *saddr_cache;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index fde5e6132018..5845bdc1ac09 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -135,6 +135,9 @@ struct irq_2_iommu;
/**
* struct irq_desc - interrupt descriptor
* @irq: interrupt number for this descriptor
+ * @timer_rand_state: pointer to timer rand state struct
+ * @kstat_irqs: irq stats per cpu
+ * @irq_2_iommu: iommu with this irq
* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
* @chip: low level interrupt hardware access
* @msi_desc: MSI descriptor
@@ -146,8 +149,8 @@ struct irq_2_iommu;
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
- * @irqs_unhandled: stats field for spurious unhandled interrupts
* @last_unhandled: aging timer for unhandled count
+ * @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity: IRQ affinity on SMP
* @cpu: cpu index useful for balancing
@@ -175,8 +178,8 @@ struct irq_desc {
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
- unsigned int irqs_unhandled;
unsigned long last_unhandled; /* Aging timer for unhandled count */
+ unsigned int irqs_unhandled;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index abb6ac639e8e..1a9cf78bfce5 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -115,10 +115,20 @@ static inline u64 get_jiffies_64(void)
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
+/*
+ * Calculate whether a is in the range of [b, c].
+ */
#define time_in_range(a,b,c) \
(time_after_eq(a,b) && \
time_before_eq(a,c))
+/*
+ * Calculate whether a is in the range of [b, c).
+ */
+#define time_in_range_open(a,b,c) \
+ (time_after_eq(a,b) && \
+ time_before(a,c))
+
/* Same as above, but does so with platform independent 64bit types.
* These must be used when utilizing jiffies_64 (i.e. return value of
* get_jiffies_64() */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 269df5a17b30..ca9ff6411dfa 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -372,18 +372,6 @@ static inline char *pack_hex_byte(char *buf, u8 byte)
((unsigned char *)&addr)[3]
#define NIPQUAD_FMT "%u.%u.%u.%u"
-#define NIP6(addr) \
- ntohs((addr).s6_addr16[0]), \
- ntohs((addr).s6_addr16[1]), \
- ntohs((addr).s6_addr16[2]), \
- ntohs((addr).s6_addr16[3]), \
- ntohs((addr).s6_addr16[4]), \
- ntohs((addr).s6_addr16[5]), \
- ntohs((addr).s6_addr16[6]), \
- ntohs((addr).s6_addr16[7])
-#define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
-#define NIP6_SEQFMT "%04x%04x%04x%04x%04x%04x%04x%04x"
-
#if defined(__LITTLE_ENDIAN)
#define HIPQUAD(addr) \
((unsigned char *)&addr)[3], \
diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h
deleted file mode 100644
index e8b8a7a5c496..000000000000
--- a/include/linux/key-ui.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* key-ui.h: key userspace interface stuff
- *
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#ifndef _LINUX_KEY_UI_H
-#define _LINUX_KEY_UI_H
-
-#include <linux/key.h>
-
-/* the key tree */
-extern struct rb_root key_serial_tree;
-extern spinlock_t key_serial_lock;
-
-/* required permissions */
-#define KEY_VIEW 0x01 /* require permission to view attributes */
-#define KEY_READ 0x02 /* require permission to read content */
-#define KEY_WRITE 0x04 /* require permission to update / modify */
-#define KEY_SEARCH 0x08 /* require permission to search (keyring) or find (key) */
-#define KEY_LINK 0x10 /* require permission to link */
-#define KEY_SETATTR 0x20 /* require permission to change attributes */
-#define KEY_ALL 0x3f /* all the above permissions */
-
-/*
- * the keyring payload contains a list of the keys to which the keyring is
- * subscribed
- */
-struct keyring_list {
- struct rcu_head rcu; /* RCU deletion hook */
- unsigned short maxkeys; /* max keys this list can hold */
- unsigned short nkeys; /* number of keys currently held */
- unsigned short delkey; /* key to be unlinked by RCU */
- struct key *keys[0];
-};
-
-/*
- * check to see whether permission is granted to use a key in the desired way
- */
-extern int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
- key_perm_t perm);
-
-static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
-{
- return key_task_permission(key_ref, current, perm);
-}
-
-extern key_ref_t lookup_user_key(struct task_struct *context,
- key_serial_t id, int create, int partial,
- key_perm_t perm);
-
-extern long join_session_keyring(const char *name);
-
-extern struct key_type *key_type_lookup(const char *type);
-extern void key_type_put(struct key_type *ktype);
-
-#define key_negative_timeout 60 /* default timeout on a negative key's existence */
-
-
-#endif /* _LINUX_KEY_UI_H */
diff --git a/include/linux/key.h b/include/linux/key.h
index 1b70e35a71e3..21d32a142c00 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -73,6 +73,7 @@ struct key;
struct seq_file;
struct user_struct;
struct signal_struct;
+struct cred;
struct key_type;
struct key_owner;
@@ -181,7 +182,7 @@ struct key {
extern struct key *key_alloc(struct key_type *type,
const char *desc,
uid_t uid, gid_t gid,
- struct task_struct *ctx,
+ const struct cred *cred,
key_perm_t perm,
unsigned long flags);
@@ -249,7 +250,7 @@ extern int key_unlink(struct key *keyring,
struct key *key);
extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- struct task_struct *ctx,
+ const struct cred *cred,
unsigned long flags,
struct key *dest);
@@ -276,24 +277,11 @@ extern ctl_table key_sysctls[];
/*
* the userspace interface
*/
-extern void switch_uid_keyring(struct user_struct *new_user);
-extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
-extern int copy_thread_group_keys(struct task_struct *tsk);
-extern void exit_keys(struct task_struct *tsk);
-extern void exit_thread_group_keys(struct signal_struct *tg);
-extern int suid_keys(struct task_struct *tsk);
-extern int exec_keys(struct task_struct *tsk);
+extern int install_thread_keyring_to_cred(struct cred *cred);
extern void key_fsuid_changed(struct task_struct *tsk);
extern void key_fsgid_changed(struct task_struct *tsk);
extern void key_init(void);
-#define __install_session_keyring(tsk, keyring) \
-({ \
- struct key *old_session = tsk->signal->session_keyring; \
- tsk->signal->session_keyring = keyring; \
- old_session; \
-})
-
#else /* CONFIG_KEYS */
#define key_validate(k) 0
@@ -302,17 +290,9 @@ extern void key_init(void);
#define key_revoke(k) do { } while(0)
#define key_put(k) do { } while(0)
#define key_ref_put(k) do { } while(0)
-#define make_key_ref(k, p) ({ NULL; })
-#define key_ref_to_ptr(k) ({ NULL; })
+#define make_key_ref(k, p) NULL
+#define key_ref_to_ptr(k) NULL
#define is_key_possessed(k) 0
-#define switch_uid_keyring(u) do { } while(0)
-#define __install_session_keyring(t, k) ({ NULL; })
-#define copy_keys(f,t) 0
-#define copy_thread_group_keys(t) 0
-#define exit_keys(t) do { } while(0)
-#define exit_thread_group_keys(tg) do { } while(0)
-#define suid_keys(t) do { } while(0)
-#define exec_keys(t) do { } while(0)
#define key_fsuid_changed(t) do { } while(0)
#define key_fsgid_changed(t) do { } while(0)
#define key_init() do { } while(0)
diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h
index 656ee6b77a4a..c0688eb72093 100644
--- a/include/linux/keyctl.h
+++ b/include/linux/keyctl.h
@@ -1,6 +1,6 @@
/* keyctl.h: keyctl command IDs
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2004, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -20,6 +20,7 @@
#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed request_key auth key */
+#define KEY_SPEC_REQUESTOR_KEYRING -8 /* - key ID for request_key() dest keyring */
/* request-key default keyrings */
#define KEY_REQKEY_DEFL_NO_CHANGE -1
@@ -30,6 +31,7 @@
#define KEY_REQKEY_DEFL_USER_KEYRING 4
#define KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5
#define KEY_REQKEY_DEFL_GROUP_KEYRING 6
+#define KEY_REQKEY_DEFL_REQUESTOR_KEYRING 7
/* keyctl commands */
#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
diff --git a/include/linux/lguest_launcher.h b/include/linux/lguest_launcher.h
index e7217dc58f39..a53407a4165c 100644
--- a/include/linux/lguest_launcher.h
+++ b/include/linux/lguest_launcher.h
@@ -54,9 +54,13 @@ struct lguest_vqconfig {
/* Write command first word is a request. */
enum lguest_req
{
- LHREQ_INITIALIZE, /* + base, pfnlimit, pgdir, start */
+ LHREQ_INITIALIZE, /* + base, pfnlimit, start */
LHREQ_GETDMA, /* No longer used */
LHREQ_IRQ, /* + irq */
LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */
};
+
+/* The alignment to use between consumer and producer parts of vring.
+ * x86 pagesize for historical reasons. */
+#define LGUEST_VRING_ALIGN 4096
#endif /* _LINUX_LGUEST_LAUNCHER */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index ed3f26eb5df1..3449de597eff 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -213,10 +213,11 @@ enum {
ATA_PFLAG_FROZEN = (1 << 2), /* port is frozen */
ATA_PFLAG_RECOVERED = (1 << 3), /* recovery action performed */
ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */
- ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */
ATA_PFLAG_INITIALIZING = (1 << 7), /* being initialized, don't touch */
ATA_PFLAG_RESETTING = (1 << 8), /* reset in progress */
+ ATA_PFLAG_UNLOADING = (1 << 9), /* driver is being unloaded */
+ ATA_PFLAG_UNLOADED = (1 << 10), /* driver is unloaded */
ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */
ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */
@@ -1285,26 +1286,62 @@ static inline int ata_link_active(struct ata_link *link)
return ata_tag_valid(link->active_tag) || link->sactive;
}
-extern struct ata_link *__ata_port_next_link(struct ata_port *ap,
- struct ata_link *link,
- bool dev_only);
+/*
+ * Iterators
+ *
+ * ATA_LITER_* constants are used to select link iteration mode and
+ * ATA_DITER_* device iteration mode.
+ *
+ * For a custom iteration directly using ata_{link|dev}_next(), if
+ * @link or @dev, respectively, is NULL, the first element is
+ * returned. @dev and @link can be any valid device or link and the
+ * next element according to the iteration mode will be returned.
+ * After the last element, NULL is returned.
+ */
+enum ata_link_iter_mode {
+ ATA_LITER_EDGE, /* if present, PMP links only; otherwise,
+ * host link. no slave link */
+ ATA_LITER_HOST_FIRST, /* host link followed by PMP or slave links */
+ ATA_LITER_PMP_FIRST, /* PMP links followed by host link,
+ * slave link still comes after host link */
+};
-#define __ata_port_for_each_link(link, ap) \
- for ((link) = __ata_port_next_link((ap), NULL, false); (link); \
- (link) = __ata_port_next_link((ap), (link), false))
+enum ata_dev_iter_mode {
+ ATA_DITER_ENABLED,
+ ATA_DITER_ENABLED_REVERSE,
+ ATA_DITER_ALL,
+ ATA_DITER_ALL_REVERSE,
+};
-#define ata_port_for_each_link(link, ap) \
- for ((link) = __ata_port_next_link((ap), NULL, true); (link); \
- (link) = __ata_port_next_link((ap), (link), true))
+extern struct ata_link *ata_link_next(struct ata_link *link,
+ struct ata_port *ap,
+ enum ata_link_iter_mode mode);
-#define ata_link_for_each_dev(dev, link) \
- for ((dev) = (link)->device; \
- (dev) < (link)->device + ata_link_max_devices(link) || ((dev) = NULL); \
- (dev)++)
+extern struct ata_device *ata_dev_next(struct ata_device *dev,
+ struct ata_link *link,
+ enum ata_dev_iter_mode mode);
+
+/*
+ * Shortcut notation for iterations
+ *
+ * ata_for_each_link() iterates over each link of @ap according to
+ * @mode. @link points to the current link in the loop. @link is
+ * NULL after loop termination. ata_for_each_dev() works the same way
+ * except that it iterates over each device of @link.
+ *
+ * Note that the mode prefixes ATA_{L|D}ITER_ shouldn't need to be
+ * specified when using the following shorthand notations. Only the
+ * mode itself (EDGE, HOST_FIRST, ENABLED, etc...) should be
+ * specified. This not only increases brevity but also makes it
+ * impossible to use ATA_LITER_* for device iteration or vice-versa.
+ */
+#define ata_for_each_link(link, ap, mode) \
+ for ((link) = ata_link_next(NULL, (ap), ATA_LITER_##mode); (link); \
+ (link) = ata_link_next((link), (ap), ATA_LITER_##mode))
-#define ata_link_for_each_dev_reverse(dev, link) \
- for ((dev) = (link)->device + ata_link_max_devices(link) - 1; \
- (dev) >= (link)->device || ((dev) = NULL); (dev)--)
+#define ata_for_each_dev(dev, link, mode) \
+ for ((dev) = ata_dev_next(NULL, (link), ATA_DITER_##mode); (dev); \
+ (dev) = ata_dev_next((dev), (link), ATA_DITER_##mode))
/**
* ata_ncq_enabled - Test whether NCQ is enabled
diff --git a/include/linux/linkage.h b/include/linux/linkage.h
index 9fd1f859021b..fee9e59649c1 100644
--- a/include/linux/linkage.h
+++ b/include/linux/linkage.h
@@ -64,14 +64,6 @@
name:
#endif
-#define KPROBE_ENTRY(name) \
- .pushsection .kprobes.text, "ax"; \
- ENTRY(name)
-
-#define KPROBE_END(name) \
- END(name); \
- .popsection
-
#ifndef END
#define END(name) \
.size name, .-name
diff --git a/include/linux/list_nulls.h b/include/linux/list_nulls.h
new file mode 100644
index 000000000000..93150ecf3ea4
--- /dev/null
+++ b/include/linux/list_nulls.h
@@ -0,0 +1,94 @@
+#ifndef _LINUX_LIST_NULLS_H
+#define _LINUX_LIST_NULLS_H
+
+/*
+ * Special version of lists, where end of list is not a NULL pointer,
+ * but a 'nulls' marker, which can have many different values.
+ * (up to 2^31 different values guaranteed on all platforms)
+ *
+ * In the standard hlist, termination of a list is the NULL pointer.
+ * In this special 'nulls' variant, we use the fact that objects stored in
+ * a list are aligned on a word (4 or 8 bytes alignment).
+ * We therefore use the last significant bit of 'ptr' :
+ * Set to 1 : This is a 'nulls' end-of-list marker (ptr >> 1)
+ * Set to 0 : This is a pointer to some object (ptr)
+ */
+
+struct hlist_nulls_head {
+ struct hlist_nulls_node *first;
+};
+
+struct hlist_nulls_node {
+ struct hlist_nulls_node *next, **pprev;
+};
+#define INIT_HLIST_NULLS_HEAD(ptr, nulls) \
+ ((ptr)->first = (struct hlist_nulls_node *) (1UL | (((long)nulls) << 1)))
+
+#define hlist_nulls_entry(ptr, type, member) container_of(ptr,type,member)
+/**
+ * ptr_is_a_nulls - Test if a ptr is a nulls
+ * @ptr: ptr to be tested
+ *
+ */
+static inline int is_a_nulls(const struct hlist_nulls_node *ptr)
+{
+ return ((unsigned long)ptr & 1);
+}
+
+/**
+ * get_nulls_value - Get the 'nulls' value of the end of chain
+ * @ptr: end of chain
+ *
+ * Should be called only if is_a_nulls(ptr);
+ */
+static inline unsigned long get_nulls_value(const struct hlist_nulls_node *ptr)
+{
+ return ((unsigned long)ptr) >> 1;
+}
+
+static inline int hlist_nulls_unhashed(const struct hlist_nulls_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_nulls_empty(const struct hlist_nulls_head *h)
+{
+ return is_a_nulls(h->first);
+}
+
+static inline void __hlist_nulls_del(struct hlist_nulls_node *n)
+{
+ struct hlist_nulls_node *next = n->next;
+ struct hlist_nulls_node **pprev = n->pprev;
+ *pprev = next;
+ if (!is_a_nulls(next))
+ next->pprev = pprev;
+}
+
+/**
+ * hlist_nulls_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ */
+#define hlist_nulls_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ (!is_a_nulls(pos)) && \
+ ({ tpos = hlist_nulls_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_nulls_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ *
+ */
+#define hlist_nulls_for_each_entry_from(tpos, pos, member) \
+ for (; (!is_a_nulls(pos)) && \
+ ({ tpos = hlist_nulls_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+#endif
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index e5872dc994c0..fbc48f898521 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -41,6 +41,7 @@ struct nlmclnt_initdata {
size_t addrlen;
unsigned short protocol;
u32 nfs_version;
+ int noresvport;
};
/*
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index b56d5aa9b194..23da3fa69efa 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -49,6 +49,7 @@ struct nlm_host {
unsigned short h_proto; /* transport proto */
unsigned short h_reclaiming : 1,
h_server : 1, /* server side, not client side */
+ h_noresvport : 1,
h_inuse : 1;
wait_queue_head_t h_gracewait; /* wait while reclaiming */
struct rw_semaphore h_rwsem; /* Reboot recovery lock */
@@ -220,7 +221,8 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
const size_t salen,
const unsigned short protocol,
const u32 version,
- const char *hostname);
+ const char *hostname,
+ int noresvport);
struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
const char *hostname,
const size_t hostname_len);
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 8956daf64abd..23bf02fb124f 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -314,8 +314,15 @@ extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
extern void lock_release(struct lockdep_map *lock, int nested,
unsigned long ip);
-extern void lock_set_subclass(struct lockdep_map *lock, unsigned int subclass,
- unsigned long ip);
+extern void lock_set_class(struct lockdep_map *lock, const char *name,
+ struct lock_class_key *key, unsigned int subclass,
+ unsigned long ip);
+
+static inline void lock_set_subclass(struct lockdep_map *lock,
+ unsigned int subclass, unsigned long ip)
+{
+ lock_set_class(lock, lock->name, lock->key, subclass, ip);
+}
# define INIT_LOCKDEP .lockdep_recursion = 0,
@@ -333,6 +340,7 @@ static inline void lockdep_on(void)
# define lock_acquire(l, s, t, r, c, n, i) do { } while (0)
# define lock_release(l, n, i) do { } while (0)
+# define lock_set_class(l, n, k, s, i) do { } while (0)
# define lock_set_subclass(l, s, i) do { } while (0)
# define lockdep_init() do { } while (0)
# define lockdep_info() do { } while (0)
@@ -382,7 +390,7 @@ do { \
#endif /* CONFIG_LOCK_STAT */
-#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_GENERIC_HARDIRQS)
+#ifdef CONFIG_GENERIC_HARDIRQS
extern void early_init_irq_lock_class(void);
#else
static inline void early_init_irq_lock_class(void)
diff --git a/include/linux/mdio-gpio.h b/include/linux/mdio-gpio.h
new file mode 100644
index 000000000000..e9d3fdfe41d7
--- /dev/null
+++ b/include/linux/mdio-gpio.h
@@ -0,0 +1,25 @@
+/*
+ * MDIO-GPIO bus platform data structures
+ *
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef __LINUX_MDIO_GPIO_H
+#define __LINUX_MDIO_GPIO_H
+
+#include <linux/mdio-bitbang.h>
+
+struct mdio_gpio_platform_data {
+ /* GPIO numbers for bus pins */
+ unsigned int mdc;
+ unsigned int mdio;
+
+ unsigned int phy_mask;
+ int irqs[PHY_MAX_ADDR];
+};
+
+#endif /* __LINUX_MDIO_GPIO_H */
diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
index 217bb22ebb8e..af95a1d2f3a1 100644
--- a/include/linux/mfd/wm8350/audio.h
+++ b/include/linux/mfd/wm8350/audio.h
@@ -1,7 +1,7 @@
/*
* audio.h -- Audio Driver for Wolfson WM8350 PMIC
*
- * Copyright 2007 Wolfson Microelectronics PLC
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -70,9 +70,9 @@
#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */
#define WM8350_VMID_OFF 0
-#define WM8350_VMID_500K 1
-#define WM8350_VMID_100K 2
-#define WM8350_VMID_10K 3
+#define WM8350_VMID_300K 1
+#define WM8350_VMID_50K 2
+#define WM8350_VMID_5K 3
/*
* R40 (0x28) - Clock Control 1
@@ -591,8 +591,38 @@
#define WM8350_IRQ_CODEC_MICSCD 41
#define WM8350_IRQ_CODEC_MICD 42
+/*
+ * WM8350 Platform data.
+ *
+ * This must be initialised per platform for best audio performance.
+ * Please see WM8350 datasheet for information.
+ */
+struct wm8350_audio_platform_data {
+ int vmid_discharge_msecs; /* VMID --> OFF discharge time */
+ int drain_msecs; /* OFF drain time */
+ int cap_discharge_msecs; /* Cap ON (from OFF) discharge time */
+ int vmid_charge_msecs; /* vmid power up time */
+ u32 vmid_s_curve:2; /* vmid enable s curve speed */
+ u32 dis_out4:2; /* out4 discharge speed */
+ u32 dis_out3:2; /* out3 discharge speed */
+ u32 dis_out2:2; /* out2 discharge speed */
+ u32 dis_out1:2; /* out1 discharge speed */
+ u32 vroi_out4:1; /* out4 tie off */
+ u32 vroi_out3:1; /* out3 tie off */
+ u32 vroi_out2:1; /* out2 tie off */
+ u32 vroi_out1:1; /* out1 tie off */
+ u32 vroi_enable:1; /* enable tie off */
+ u32 codec_current_on:2; /* current level ON */
+ u32 codec_current_standby:2; /* current level STANDBY */
+ u32 codec_current_charge:2; /* codec current @ vmid charge */
+};
+
+struct snd_soc_codec;
+
struct wm8350_codec {
struct platform_device *pdev;
+ struct snd_soc_codec *codec;
+ struct wm8350_audio_platform_data *platform_data;
};
#endif
diff --git a/include/linux/mii.h b/include/linux/mii.h
index 151b7e0182c7..ad748588faf1 100644
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -135,6 +135,10 @@
#define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */
#define LPA_1000HALF 0x0400 /* Link partner 1000BASE-T half duplex */
+/* Flow control flags */
+#define FLOW_CTRL_TX 0x01
+#define FLOW_CTRL_RX 0x02
+
/* This structure is used in all SIOCxMIIxxx ioctl calls */
struct mii_ioctl_data {
__u16 phy_id;
@@ -235,5 +239,34 @@ static inline unsigned int mii_duplex (unsigned int duplex_lock,
return 0;
}
+/**
+ * mii_resolve_flowctrl_fdx
+ * @lcladv: value of MII ADVERTISE register
+ * @rmtadv: value of MII LPA register
+ *
+ * Resolve full duplex flow control as per IEEE 802.3-2005 table 28B-3
+ */
+static inline u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv)
+{
+ u8 cap = 0;
+
+ if (lcladv & ADVERTISE_PAUSE_CAP) {
+ if (lcladv & ADVERTISE_PAUSE_ASYM) {
+ if (rmtadv & LPA_PAUSE_CAP)
+ cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ else if (rmtadv & LPA_PAUSE_ASYM)
+ cap = FLOW_CTRL_RX;
+ } else {
+ if (rmtadv & LPA_PAUSE_CAP)
+ cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ }
+ } else if (lcladv & ADVERTISE_PAUSE_ASYM) {
+ if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM))
+ cap = FLOW_CTRL_TX;
+ }
+
+ return cap;
+}
+
#endif /* __KERNEL__ */
#endif /* __LINUX_MII_H__ */
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 371086fd946f..8f659cc29960 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -206,6 +206,7 @@ struct mlx4_caps {
int reserved_cqs;
int num_eqs;
int reserved_eqs;
+ int num_comp_vectors;
int num_mpts;
int num_mtt_segs;
int fmr_reserved_mtts;
@@ -328,6 +329,7 @@ struct mlx4_cq {
int arm_sn;
int cqn;
+ unsigned vector;
atomic_t refcount;
struct completion free;
@@ -437,7 +439,7 @@ void mlx4_free_hwq_res(struct mlx4_dev *mdev, struct mlx4_hwq_resources *wqres,
int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt,
struct mlx4_uar *uar, u64 db_rec, struct mlx4_cq *cq,
- int collapsed);
+ unsigned vector, int collapsed);
void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq);
int mlx4_qp_reserve_range(struct mlx4_dev *dev, int cnt, int align, int *base);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index ffee2f743418..aaa8b843be28 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -145,6 +145,23 @@ extern pgprot_t protection_map[16];
#define FAULT_FLAG_WRITE 0x01 /* Fault was a write access */
#define FAULT_FLAG_NONLINEAR 0x02 /* Fault was via a nonlinear mapping */
+/*
+ * This interface is used by x86 PAT code to identify a pfn mapping that is
+ * linear over entire vma. This is to optimize PAT code that deals with
+ * marking the physical region with a particular prot. This is not for generic
+ * mm use. Note also that this check will not work if the pfn mapping is
+ * linear for a vma starting at physical address 0. In which case PAT code
+ * falls back to slow path of reserving physical range page by page.
+ */
+static inline int is_linear_pfn_mapping(struct vm_area_struct *vma)
+{
+ return ((vma->vm_flags & VM_PFNMAP) && vma->vm_pgoff);
+}
+
+static inline int is_pfn_mapping(struct vm_area_struct *vma)
+{
+ return (vma->vm_flags & VM_PFNMAP);
+}
/*
* vm_fault is filled by the the pagefault handler and passed to the vma's
@@ -781,6 +798,8 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *vma);
void unmap_mapping_range(struct address_space *mapping,
loff_t const holebegin, loff_t const holelen, int even_cows);
+int follow_phys(struct vm_area_struct *vma, unsigned long address,
+ unsigned int flags, unsigned long *prot, resource_size_t *phys);
int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
void *buf, int len, int write);
@@ -1286,5 +1305,7 @@ int vmemmap_populate_basepages(struct page *start_page,
int vmemmap_populate(struct page *start_page, unsigned long pages, int node);
void vmemmap_populate_print_last(void);
+extern void *alloc_locked_buffer(size_t size);
+extern void free_locked_buffer(void *buffer, size_t size);
#endif /* __KERNEL__ */
#endif /* _LINUX_MM_H */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index fe825471d5aa..9cfc9b627fdd 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -232,8 +232,9 @@ struct mm_struct {
struct core_state *core_state; /* coredumping support */
/* aio bits */
- rwlock_t ioctx_list_lock; /* aio lock */
- struct kioctx *ioctx_list;
+ spinlock_t ioctx_lock;
+ struct hlist_head ioctx_list;
+
#ifdef CONFIG_MM_OWNER
/*
* "owner" points to a task that is regarded as the canonical
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index 6f4c180179e2..5375faca1f72 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -117,6 +117,7 @@ struct sioc_mif_req6
#include <linux/pim.h>
#include <linux/skbuff.h> /* for struct sk_buff_head */
+#include <net/net_namespace.h>
#ifdef CONFIG_IPV6_MROUTE
static inline int ip6_mroute_opt(int opt)
@@ -187,6 +188,9 @@ struct mif_device
struct mfc6_cache
{
struct mfc6_cache *next; /* Next entry on cache line */
+#ifdef CONFIG_NET_NS
+ struct net *mfc6_net;
+#endif
struct in6_addr mf6c_mcastgrp; /* Group the entry belongs to */
struct in6_addr mf6c_origin; /* Source of packet */
mifi_t mf6c_parent; /* Source interface */
@@ -209,6 +213,18 @@ struct mfc6_cache
} mfc_un;
};
+static inline
+struct net *mfc6_net(const struct mfc6_cache *mfc)
+{
+ return read_pnet(&mfc->mfc6_net);
+}
+
+static inline
+void mfc6_net_set(struct mfc6_cache *mfc, struct net *net)
+{
+ write_pnet(&mfc->mfc6_net, hold_net(net));
+}
+
#define MFC_STATIC 1
#define MFC_NOTIFY 2
@@ -229,13 +245,17 @@ struct mfc6_cache
#ifdef __KERNEL__
struct rtmsg;
-extern int ip6mr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait);
+extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
+ struct rtmsg *rtm, int nowait);
#ifdef CONFIG_IPV6_MROUTE
-extern struct sock *mroute6_socket;
+static inline struct sock *mroute6_socket(struct net *net)
+{
+ return net->ipv6.mroute6_sk;
+}
extern int ip6mr_sk_done(struct sock *sk);
#else
-#define mroute6_socket NULL
+static inline struct sock *mroute6_socket(struct net *net) { return NULL; }
static inline int ip6mr_sk_done(struct sock *sk) { return 0; }
#endif
#endif
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index e26f54952892..41e1224651cf 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -43,6 +43,9 @@
#include <net/net_namespace.h>
#include <net/dsa.h>
+#ifdef CONFIG_DCB
+#include <net/dcbnl.h>
+#endif
struct vlan_group;
struct ethtool_ops;
@@ -311,8 +314,9 @@ struct napi_struct {
spinlock_t poll_lock;
int poll_owner;
struct net_device *dev;
- struct list_head dev_list;
#endif
+ struct list_head dev_list;
+ struct sk_buff *gro_list;
};
enum
@@ -373,22 +377,8 @@ static inline int napi_reschedule(struct napi_struct *napi)
*
* Mark NAPI processing as complete.
*/
-static inline void __napi_complete(struct napi_struct *n)
-{
- BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
- list_del(&n->poll_list);
- smp_mb__before_clear_bit();
- clear_bit(NAPI_STATE_SCHED, &n->state);
-}
-
-static inline void napi_complete(struct napi_struct *n)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- __napi_complete(n);
- local_irq_restore(flags);
-}
+extern void __napi_complete(struct napi_struct *n);
+extern void napi_complete(struct napi_struct *n);
/**
* napi_disable - prevent NAPI from scheduling
@@ -452,6 +442,147 @@ struct netdev_queue {
struct Qdisc *qdisc_sleeping;
} ____cacheline_aligned_in_smp;
+
+/*
+ * This structure defines the management hooks for network devices.
+ * The following hooks can be defined; unless noted otherwise, they are
+ * optional and can be filled with a null pointer.
+ *
+ * int (*ndo_init)(struct net_device *dev);
+ * This function is called once when network device is registered.
+ * The network device can use this to any late stage initializaton
+ * or semantic validattion. It can fail with an error code which will
+ * be propogated back to register_netdev
+ *
+ * void (*ndo_uninit)(struct net_device *dev);
+ * This function is called when device is unregistered or when registration
+ * fails. It is not called if init fails.
+ *
+ * int (*ndo_open)(struct net_device *dev);
+ * This function is called when network device transistions to the up
+ * state.
+ *
+ * int (*ndo_stop)(struct net_device *dev);
+ * This function is called when network device transistions to the down
+ * state.
+ *
+ * int (*ndo_hard_start_xmit)(struct sk_buff *skb, struct net_device *dev);
+ * Called when a packet needs to be transmitted.
+ * Must return NETDEV_TX_OK , NETDEV_TX_BUSY, or NETDEV_TX_LOCKED,
+ * Required can not be NULL.
+ *
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb);
+ * Called to decide which queue to when device supports multiple
+ * transmit queues.
+ *
+ * void (*ndo_change_rx_flags)(struct net_device *dev, int flags);
+ * This function is called to allow device receiver to make
+ * changes to configuration when multicast or promiscious is enabled.
+ *
+ * void (*ndo_set_rx_mode)(struct net_device *dev);
+ * This function is called device changes address list filtering.
+ *
+ * void (*ndo_set_multicast_list)(struct net_device *dev);
+ * This function is called when the multicast address list changes.
+ *
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ * This function is called when the Media Access Control address
+ * needs to be changed. If not this interface is not defined, the
+ * mac address can not be changed.
+ *
+ * int (*ndo_validate_addr)(struct net_device *dev);
+ * Test if Media Access Control address is valid for the device.
+ *
+ * int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
+ * Called when a user request an ioctl which can't be handled by
+ * the generic interface code. If not defined ioctl's return
+ * not supported error code.
+ *
+ * int (*ndo_set_config)(struct net_device *dev, struct ifmap *map);
+ * Used to set network devices bus interface parameters. This interface
+ * is retained for legacy reason, new devices should use the bus
+ * interface (PCI) for low level management.
+ *
+ * int (*ndo_change_mtu)(struct net_device *dev, int new_mtu);
+ * Called when a user wants to change the Maximum Transfer Unit
+ * of a device. If not defined, any request to change MTU will
+ * will return an error.
+ *
+ * void (*ndo_tx_timeout)(struct net_device *dev);
+ * Callback uses when the transmitter has not made any progress
+ * for dev->watchdog ticks.
+ *
+ * struct net_device_stats* (*get_stats)(struct net_device *dev);
+ * Called when a user wants to get the network device usage
+ * statistics. If not defined, the counters in dev->stats will
+ * be used.
+ *
+ * void (*ndo_vlan_rx_register)(struct net_device *dev, struct vlan_group *grp);
+ * If device support VLAN receive accleration
+ * (ie. dev->features & NETIF_F_HW_VLAN_RX), then this function is called
+ * when vlan groups for the device changes. Note: grp is NULL
+ * if no vlan's groups are being used.
+ *
+ * void (*ndo_vlan_rx_add_vid)(struct net_device *dev, unsigned short vid);
+ * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER)
+ * this function is called when a VLAN id is registered.
+ *
+ * void (*ndo_vlan_rx_kill_vid)(struct net_device *dev, unsigned short vid);
+ * If device support VLAN filtering (dev->features & NETIF_F_HW_VLAN_FILTER)
+ * this function is called when a VLAN id is unregistered.
+ *
+ * void (*ndo_poll_controller)(struct net_device *dev);
+ */
+#define HAVE_NET_DEVICE_OPS
+struct net_device_ops {
+ int (*ndo_init)(struct net_device *dev);
+ void (*ndo_uninit)(struct net_device *dev);
+ int (*ndo_open)(struct net_device *dev);
+ int (*ndo_stop)(struct net_device *dev);
+ int (*ndo_start_xmit) (struct sk_buff *skb,
+ struct net_device *dev);
+ u16 (*ndo_select_queue)(struct net_device *dev,
+ struct sk_buff *skb);
+#define HAVE_CHANGE_RX_FLAGS
+ void (*ndo_change_rx_flags)(struct net_device *dev,
+ int flags);
+#define HAVE_SET_RX_MODE
+ void (*ndo_set_rx_mode)(struct net_device *dev);
+#define HAVE_MULTICAST
+ void (*ndo_set_multicast_list)(struct net_device *dev);
+#define HAVE_SET_MAC_ADDR
+ int (*ndo_set_mac_address)(struct net_device *dev,
+ void *addr);
+#define HAVE_VALIDATE_ADDR
+ int (*ndo_validate_addr)(struct net_device *dev);
+#define HAVE_PRIVATE_IOCTL
+ int (*ndo_do_ioctl)(struct net_device *dev,
+ struct ifreq *ifr, int cmd);
+#define HAVE_SET_CONFIG
+ int (*ndo_set_config)(struct net_device *dev,
+ struct ifmap *map);
+#define HAVE_CHANGE_MTU
+ int (*ndo_change_mtu)(struct net_device *dev,
+ int new_mtu);
+ int (*ndo_neigh_setup)(struct net_device *dev,
+ struct neigh_parms *);
+#define HAVE_TX_TIMEOUT
+ void (*ndo_tx_timeout) (struct net_device *dev);
+
+ struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
+
+ void (*ndo_vlan_rx_register)(struct net_device *dev,
+ struct vlan_group *grp);
+ void (*ndo_vlan_rx_add_vid)(struct net_device *dev,
+ unsigned short vid);
+ void (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
+ unsigned short vid);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+#define HAVE_NETDEV_POLL
+ void (*ndo_poll_controller)(struct net_device *dev);
+#endif
+};
+
/*
* The DEVICE structure.
* Actually, this whole structure is a big mistake. It mixes I/O
@@ -496,14 +627,7 @@ struct net_device
unsigned long state;
struct list_head dev_list;
-#ifdef CONFIG_NETPOLL
struct list_head napi_list;
-#endif
-
- /* The device initialization function. Called only once. */
- int (*init)(struct net_device *dev);
-
- /* ------- Fields preinitialized in Space.c finish here ------- */
/* Net device features */
unsigned long features;
@@ -522,6 +646,7 @@ struct net_device
#define NETIF_F_LLTX 4096 /* LockLess TX - deprecated. Please */
/* do not use LLTX in new drivers */
#define NETIF_F_NETNS_LOCAL 8192 /* Does not change network namespaces */
+#define NETIF_F_GRO 16384 /* Generic receive offload */
#define NETIF_F_LRO 32768 /* large receive offload */
/* Segmentation offload features */
@@ -547,15 +672,13 @@ struct net_device
* for all in netdev_increment_features.
*/
#define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \
- NETIF_F_SG | NETIF_F_HIGHDMA | \
+ NETIF_F_SG | NETIF_F_HIGHDMA | \
NETIF_F_FRAGLIST)
/* Interface index. Unique device identifier */
int ifindex;
int iflink;
-
- struct net_device_stats* (*get_stats)(struct net_device *dev);
struct net_device_stats stats;
#ifdef CONFIG_WIRELESS_EXT
@@ -565,18 +688,13 @@ struct net_device
/* Instance data managed by the core of Wireless Extensions. */
struct iw_public_data * wireless_data;
#endif
+ /* Management operations */
+ const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
/* Hardware header description */
const struct header_ops *header_ops;
- /*
- * This marks the end of the "visible" part of the structure. All
- * fields hereafter are internal to the system, and may change at
- * will (read: may be cleaned up at will).
- */
-
-
unsigned int flags; /* interface flags (a la BSD) */
unsigned short gflags;
unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */
@@ -635,7 +753,7 @@ struct net_device
unsigned long last_rx; /* Time of last Rx */
/* Interface address info used in eth_type_trans() */
unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast
- because most packets are unicast) */
+ because most packets are unicast) */
unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
@@ -654,18 +772,12 @@ struct net_device
/*
* One part is mostly used on xmit path (device)
*/
- void *priv; /* pointer to private data */
- int (*hard_start_xmit) (struct sk_buff *skb,
- struct net_device *dev);
/* These may be needed for future network-power-down code. */
unsigned long trans_start; /* Time (in jiffies) of last Tx */
int watchdog_timeo; /* used by dev_watchdog() */
struct timer_list watchdog_timer;
-/*
- * refcnt is a very hot point, so align it on SMP
- */
/* Number of references to this device */
atomic_t refcnt ____cacheline_aligned_in_smp;
@@ -684,56 +796,12 @@ struct net_device
NETREG_RELEASED, /* called free_netdev */
} reg_state;
- /* Called after device is detached from network. */
- void (*uninit)(struct net_device *dev);
- /* Called after last user reference disappears. */
- void (*destructor)(struct net_device *dev);
-
- /* Pointers to interface service routines. */
- int (*open)(struct net_device *dev);
- int (*stop)(struct net_device *dev);
-#define HAVE_NETDEV_POLL
-#define HAVE_CHANGE_RX_FLAGS
- void (*change_rx_flags)(struct net_device *dev,
- int flags);
-#define HAVE_SET_RX_MODE
- void (*set_rx_mode)(struct net_device *dev);
-#define HAVE_MULTICAST
- void (*set_multicast_list)(struct net_device *dev);
-#define HAVE_SET_MAC_ADDR
- int (*set_mac_address)(struct net_device *dev,
- void *addr);
-#define HAVE_VALIDATE_ADDR
- int (*validate_addr)(struct net_device *dev);
-#define HAVE_PRIVATE_IOCTL
- int (*do_ioctl)(struct net_device *dev,
- struct ifreq *ifr, int cmd);
-#define HAVE_SET_CONFIG
- int (*set_config)(struct net_device *dev,
- struct ifmap *map);
-#define HAVE_CHANGE_MTU
- int (*change_mtu)(struct net_device *dev, int new_mtu);
-
-#define HAVE_TX_TIMEOUT
- void (*tx_timeout) (struct net_device *dev);
-
- void (*vlan_rx_register)(struct net_device *dev,
- struct vlan_group *grp);
- void (*vlan_rx_add_vid)(struct net_device *dev,
- unsigned short vid);
- void (*vlan_rx_kill_vid)(struct net_device *dev,
- unsigned short vid);
+ /* Called from unregister, can be used to call free_netdev */
+ void (*destructor)(struct net_device *dev);
- int (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
#ifdef CONFIG_NETPOLL
struct netpoll_info *npinfo;
#endif
-#ifdef CONFIG_NET_POLL_CONTROLLER
- void (*poll_controller)(struct net_device *dev);
-#endif
-
- u16 (*select_queue)(struct net_device *dev,
- struct sk_buff *skb);
#ifdef CONFIG_NET_NS
/* Network namespace this network device is inside */
@@ -764,6 +832,49 @@ struct net_device
/* for setting kernel sock attribute on TCP connection setup */
#define GSO_MAX_SIZE 65536
unsigned int gso_max_size;
+
+#ifdef CONFIG_DCB
+ /* Data Center Bridging netlink ops */
+ struct dcbnl_rtnl_ops *dcbnl_ops;
+#endif
+
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
+ struct {
+ int (*init)(struct net_device *dev);
+ void (*uninit)(struct net_device *dev);
+ int (*open)(struct net_device *dev);
+ int (*stop)(struct net_device *dev);
+ int (*hard_start_xmit) (struct sk_buff *skb,
+ struct net_device *dev);
+ u16 (*select_queue)(struct net_device *dev,
+ struct sk_buff *skb);
+ void (*change_rx_flags)(struct net_device *dev,
+ int flags);
+ void (*set_rx_mode)(struct net_device *dev);
+ void (*set_multicast_list)(struct net_device *dev);
+ int (*set_mac_address)(struct net_device *dev,
+ void *addr);
+ int (*validate_addr)(struct net_device *dev);
+ int (*do_ioctl)(struct net_device *dev,
+ struct ifreq *ifr, int cmd);
+ int (*set_config)(struct net_device *dev,
+ struct ifmap *map);
+ int (*change_mtu)(struct net_device *dev, int new_mtu);
+ int (*neigh_setup)(struct net_device *dev,
+ struct neigh_parms *);
+ void (*tx_timeout) (struct net_device *dev);
+ struct net_device_stats* (*get_stats)(struct net_device *dev);
+ void (*vlan_rx_register)(struct net_device *dev,
+ struct vlan_group *grp);
+ void (*vlan_rx_add_vid)(struct net_device *dev,
+ unsigned short vid);
+ void (*vlan_rx_kill_vid)(struct net_device *dev,
+ unsigned short vid);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ void (*poll_controller)(struct net_device *dev);
+#endif
+ };
+#endif
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
@@ -859,22 +970,8 @@ static inline void *netdev_priv(const struct net_device *dev)
* netif_napi_add() must be used to initialize a napi context prior to calling
* *any* of the other napi related functions.
*/
-static inline void netif_napi_add(struct net_device *dev,
- struct napi_struct *napi,
- int (*poll)(struct napi_struct *, int),
- int weight)
-{
- INIT_LIST_HEAD(&napi->poll_list);
- napi->poll = poll;
- napi->weight = weight;
-#ifdef CONFIG_NETPOLL
- napi->dev = dev;
- list_add(&napi->dev_list, &dev->napi_list);
- spin_lock_init(&napi->poll_lock);
- napi->poll_owner = -1;
-#endif
- set_bit(NAPI_STATE_SCHED, &napi->state);
-}
+void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int), int weight);
/**
* netif_napi_del - remove a napi context
@@ -882,12 +979,20 @@ static inline void netif_napi_add(struct net_device *dev,
*
* netif_napi_del() removes a napi context from the network device napi list
*/
-static inline void netif_napi_del(struct napi_struct *napi)
-{
-#ifdef CONFIG_NETPOLL
- list_del(&napi->dev_list);
-#endif
-}
+void netif_napi_del(struct napi_struct *napi);
+
+struct napi_gro_cb {
+ /* This is non-zero if the packet may be of the same flow. */
+ int same_flow;
+
+ /* This is non-zero if the packet cannot be merged with the new skb. */
+ int flush;
+
+ /* Number of segments aggregated. */
+ int count;
+};
+
+#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
struct packet_type {
__be16 type; /* This is really htons(ether_type). */
@@ -899,6 +1004,9 @@ struct packet_type {
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
int (*gso_send_check)(struct sk_buff *skb);
+ struct sk_buff **(*gro_receive)(struct sk_buff **head,
+ struct sk_buff *skb);
+ int (*gro_complete)(struct sk_buff *skb);
void *af_packet_priv;
struct list_head list;
};
@@ -1252,6 +1360,9 @@ extern int netif_rx(struct sk_buff *skb);
extern int netif_rx_ni(struct sk_buff *skb);
#define HAVE_NETIF_RECEIVE_SKB 1
extern int netif_receive_skb(struct sk_buff *skb);
+extern void napi_gro_flush(struct napi_struct *napi);
+extern int napi_gro_receive(struct napi_struct *napi,
+ struct sk_buff *skb);
extern void netif_nit_deliver(struct sk_buff *skb);
extern int dev_valid_name(const char *name);
extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
@@ -1444,8 +1555,7 @@ static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits)
}
/* Test if receive needs to be scheduled but only if up */
-static inline int netif_rx_schedule_prep(struct net_device *dev,
- struct napi_struct *napi)
+static inline int netif_rx_schedule_prep(struct napi_struct *napi)
{
return napi_schedule_prep(napi);
}
@@ -1453,27 +1563,24 @@ static inline int netif_rx_schedule_prep(struct net_device *dev,
/* Add interface to tail of rx poll list. This assumes that _prep has
* already been called and returned 1.
*/
-static inline void __netif_rx_schedule(struct net_device *dev,
- struct napi_struct *napi)
+static inline void __netif_rx_schedule(struct napi_struct *napi)
{
__napi_schedule(napi);
}
/* Try to reschedule poll. Called by irq handler. */
-static inline void netif_rx_schedule(struct net_device *dev,
- struct napi_struct *napi)
+static inline void netif_rx_schedule(struct napi_struct *napi)
{
- if (netif_rx_schedule_prep(dev, napi))
- __netif_rx_schedule(dev, napi);
+ if (netif_rx_schedule_prep(napi))
+ __netif_rx_schedule(napi);
}
/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). */
-static inline int netif_rx_reschedule(struct net_device *dev,
- struct napi_struct *napi)
+static inline int netif_rx_reschedule(struct napi_struct *napi)
{
if (napi_schedule_prep(napi)) {
- __netif_rx_schedule(dev, napi);
+ __netif_rx_schedule(napi);
return 1;
}
return 0;
@@ -1482,8 +1589,7 @@ static inline int netif_rx_reschedule(struct net_device *dev,
/* same as netif_rx_complete, except that local_irq_save(flags)
* has already been issued
*/
-static inline void __netif_rx_complete(struct net_device *dev,
- struct napi_struct *napi)
+static inline void __netif_rx_complete(struct napi_struct *napi)
{
__napi_complete(napi);
}
@@ -1493,20 +1599,9 @@ static inline void __netif_rx_complete(struct net_device *dev,
* it completes the work. The device cannot be out of poll list at this
* moment, it is BUG().
*/
-static inline void netif_rx_complete(struct net_device *dev,
- struct napi_struct *napi)
+static inline void netif_rx_complete(struct napi_struct *napi)
{
- unsigned long flags;
-
- /*
- * don't let napi dequeue from the cpu poll list
- * just in case its running on a different cpu
- */
- if (unlikely(test_bit(NAPI_STATE_NPSVC, &napi->state)))
- return;
- local_irq_save(flags);
- __netif_rx_complete(dev, napi);
- local_irq_restore(flags);
+ napi_complete(napi);
}
static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu)
@@ -1683,6 +1778,8 @@ extern void netdev_features_change(struct net_device *dev);
/* Load a device via the kmod */
extern void dev_load(struct net *net, const char *name);
extern void dev_mcast_init(void);
+extern const struct net_device_stats *dev_get_stats(struct net_device *dev);
+
extern int netdev_max_backlog;
extern int weight_p;
extern int netdev_set_master(struct net_device *dev, struct net_device *master);
@@ -1731,6 +1828,8 @@ static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
{
return skb_is_gso(skb) &&
(!skb_gso_ok(skb, dev->features) ||
+ (skb_shinfo(skb)->frag_list &&
+ !(dev->features & NETIF_F_FRAGLIST)) ||
unlikely(skb->ip_summed != CHECKSUM_PARTIAL));
}
@@ -1749,26 +1848,31 @@ static inline int skb_bond_should_drop(struct sk_buff *skb)
struct net_device *dev = skb->dev;
struct net_device *master = dev->master;
- if (master &&
- (dev->priv_flags & IFF_SLAVE_INACTIVE)) {
- if ((dev->priv_flags & IFF_SLAVE_NEEDARP) &&
- skb->protocol == __constant_htons(ETH_P_ARP))
- return 0;
+ if (master) {
+ if (master->priv_flags & IFF_MASTER_ARPMON)
+ dev->last_rx = jiffies;
- if (master->priv_flags & IFF_MASTER_ALB) {
- if (skb->pkt_type != PACKET_BROADCAST &&
- skb->pkt_type != PACKET_MULTICAST)
+ if (dev->priv_flags & IFF_SLAVE_INACTIVE) {
+ if ((dev->priv_flags & IFF_SLAVE_NEEDARP) &&
+ skb->protocol == __constant_htons(ETH_P_ARP))
return 0;
- }
- if (master->priv_flags & IFF_MASTER_8023AD &&
- skb->protocol == __constant_htons(ETH_P_SLOW))
- return 0;
- return 1;
+ if (master->priv_flags & IFF_MASTER_ALB) {
+ if (skb->pkt_type != PACKET_BROADCAST &&
+ skb->pkt_type != PACKET_MULTICAST)
+ return 0;
+ }
+ if (master->priv_flags & IFF_MASTER_8023AD &&
+ skb->protocol == __constant_htons(ETH_P_SLOW))
+ return 0;
+
+ return 1;
+ }
}
return 0;
}
+extern struct pernet_operations __net_initdata loopback_net_ops;
#endif /* __KERNEL__ */
#endif /* _LINUX_DEV_H */
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index c19595c89304..29fe9ea1d346 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -141,6 +141,7 @@ enum ctattr_protonat {
#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
enum ctattr_natseq {
+ CTA_NAT_SEQ_UNSPEC,
CTA_NAT_SEQ_CORRECTION_POS,
CTA_NAT_SEQ_OFFSET_BEFORE,
CTA_NAT_SEQ_OFFSET_AFTER,
diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
index d45e29cd1cfb..e40ddb94b1af 100644
--- a/include/linux/netfilter_bridge/ebtables.h
+++ b/include/linux/netfilter_bridge/ebtables.h
@@ -300,7 +300,8 @@ struct ebt_table
#define EBT_ALIGN(s) (((s) + (__alignof__(struct ebt_replace)-1)) & \
~(__alignof__(struct ebt_replace)-1))
-extern int ebt_register_table(struct ebt_table *table);
+extern struct ebt_table *ebt_register_table(struct net *net,
+ struct ebt_table *table);
extern void ebt_unregister_table(struct ebt_table *table);
extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
diff --git a/include/linux/netfilter_ipv4/ipt_policy.h b/include/linux/netfilter_ipv4/ipt_policy.h
index b9478a255301..1037fb2cd206 100644
--- a/include/linux/netfilter_ipv4/ipt_policy.h
+++ b/include/linux/netfilter_ipv4/ipt_policy.h
@@ -1,6 +1,8 @@
#ifndef _IPT_POLICY_H
#define _IPT_POLICY_H
+#include <linux/netfilter/xt_policy.h>
+
#define IPT_POLICY_MAX_ELEM XT_POLICY_MAX_ELEM
/* ipt_policy_flags */
diff --git a/include/linux/netfilter_ipv6/ip6t_policy.h b/include/linux/netfilter_ipv6/ip6t_policy.h
index 6bab3163d2fb..b1c449d7ec89 100644
--- a/include/linux/netfilter_ipv6/ip6t_policy.h
+++ b/include/linux/netfilter_ipv6/ip6t_policy.h
@@ -1,6 +1,8 @@
#ifndef _IP6T_POLICY_H
#define _IP6T_POLICY_H
+#include <linux/netfilter/xt_policy.h>
+
#define IP6T_POLICY_MAX_ELEM XT_POLICY_MAX_ELEM
/* ip6t_policy_flags */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 9ff1b54908f3..51b09a1f46c3 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -242,7 +242,8 @@ __nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len, int flags)
nlh->nlmsg_flags = flags;
nlh->nlmsg_pid = pid;
nlh->nlmsg_seq = seq;
- memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size);
+ if (!__builtin_constant_p(size) || NLMSG_ALIGN(size) - size != 0)
+ memset(NLMSG_DATA(nlh) + len, 0, NLMSG_ALIGN(size) - size);
return nlh;
}
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index e3d79593fb3a..e38d3c9dccda 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -94,11 +94,6 @@ static inline void netpoll_poll_unlock(void *have)
rcu_read_unlock();
}
-static inline void netpoll_netdev_init(struct net_device *dev)
-{
- INIT_LIST_HEAD(&dev->napi_list);
-}
-
#else
static inline int netpoll_rx(struct sk_buff *skb)
{
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 4eaa8347a0d9..db867b04ac3c 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -83,7 +83,7 @@ struct nfs_open_context {
struct rpc_cred *cred;
struct nfs4_state *state;
fl_owner_t lockowner;
- int mode;
+ fmode_t mode;
unsigned long flags;
#define NFS_CONTEXT_ERROR_WRITE (0)
@@ -130,7 +130,10 @@ struct nfs_inode {
*
* We need to revalidate the cached attrs for this inode if
*
- * jiffies - read_cache_jiffies > attrtimeo
+ * jiffies - read_cache_jiffies >= attrtimeo
+ *
+ * Please note the comparison is greater than or equal
+ * so that zero timeout values can be specified.
*/
unsigned long read_cache_jiffies;
unsigned long attrtimeo;
@@ -180,7 +183,7 @@ struct nfs_inode {
/* NFSv4 state */
struct list_head open_states;
struct nfs_delegation *delegation;
- int delegation_state;
+ fmode_t delegation_state;
struct rw_semaphore rwsem;
#endif /* CONFIG_NFS_V4*/
struct inode vfs_inode;
@@ -342,7 +345,7 @@ extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx);
-extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode);
+extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
extern u64 nfs_compat_user_ino64(u64 fileid);
extern void nfs_fattr_init(struct nfs_fattr *fattr);
@@ -533,12 +536,6 @@ static inline void nfs3_forget_cached_acls(struct inode *inode)
#endif /* CONFIG_NFS_V3_ACL */
/*
- * linux/fs/mount_clnt.c
- */
-extern int nfs_mount(struct sockaddr *, size_t, char *, char *,
- int, int, struct nfs_fh *);
-
-/*
* inline functions
*/
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 4e477ae58699..9bb81aec91cf 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -42,12 +42,6 @@ struct nfs_client {
struct rb_root cl_openowner_id;
struct rb_root cl_lockowner_id;
- /*
- * The following rwsem ensures exclusive access to the server
- * while we recover the state following a lease expiration.
- */
- struct rw_semaphore cl_sem;
-
struct list_head cl_delegations;
struct rb_root cl_state_owners;
spinlock_t cl_lock;
diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h
index 6549a06ac16e..4499016e6d0d 100644
--- a/include/linux/nfs_mount.h
+++ b/include/linux/nfs_mount.h
@@ -45,7 +45,7 @@ struct nfs_mount_data {
char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
};
-/* bits in the flags field */
+/* bits in the flags field visible to user space */
#define NFS_MOUNT_SOFT 0x0001 /* 1 */
#define NFS_MOUNT_INTR 0x0002 /* 1 */ /* now unused, but ABI */
@@ -68,5 +68,6 @@ struct nfs_mount_data {
/* The following are for internal use only */
#define NFS_MOUNT_LOOKUP_CACHE_NONEG 0x10000
#define NFS_MOUNT_LOOKUP_CACHE_NONE 0x20000
+#define NFS_MOUNT_NORESVPORT 0x40000
#endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index c1c31acb8a2b..a550b528319f 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -120,13 +120,14 @@ struct nfs_openargs {
const struct nfs_fh * fh;
struct nfs_seqid * seqid;
int open_flags;
+ fmode_t fmode;
__u64 clientid;
__u64 id;
union {
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
nfs4_stateid delegation; /* CLAIM_DELEGATE_CUR */
- int delegation_type; /* CLAIM_PREVIOUS */
+ fmode_t delegation_type; /* CLAIM_PREVIOUS */
} u;
const struct qstr * name;
const struct nfs_server *server; /* Needed for ID mapping */
@@ -143,7 +144,7 @@ struct nfs_openres {
struct nfs_fattr * dir_attr;
struct nfs_seqid * seqid;
const struct nfs_server *server;
- int delegation_type;
+ fmode_t delegation_type;
nfs4_stateid delegation;
__u32 do_recall;
__u64 maxsize;
@@ -171,7 +172,7 @@ struct nfs_closeargs {
struct nfs_fh * fh;
nfs4_stateid * stateid;
struct nfs_seqid * seqid;
- int open_flags;
+ fmode_t fmode;
const u32 * bitmask;
};
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index d0fe2e378452..128298c0362d 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -124,6 +124,8 @@ struct nfs4_client {
nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */
__be32 cl_addr; /* client ipaddress */
+ u32 cl_flavor; /* setclientid pseudoflavor */
+ char *cl_principal; /* setclientid principal name */
struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 9bad65400fba..e86ed59f9ad5 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -3,7 +3,26 @@
/*
* 802.11 netlink interface public header
*
- * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006, 2007, 2008 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
*/
/**
@@ -25,8 +44,10 @@
*
* @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
* to get a list of all present wiphys.
- * @NL80211_CMD_SET_WIPHY: set wiphy name, needs %NL80211_ATTR_WIPHY and
- * %NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or
+ * %NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET.
* @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
* or rename notification. Has attributes %NL80211_ATTR_WIPHY and
* %NL80211_ATTR_WIPHY_NAME.
@@ -106,6 +127,12 @@
* to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
* store this as a valid request and then query userspace for it.
*
+ * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -148,6 +175,9 @@ enum nl80211_commands {
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,
+ NL80211_CMD_GET_MESH_PARAMS,
+ NL80211_CMD_SET_MESH_PARAMS,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -169,6 +199,15 @@ enum nl80211_commands {
* @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
* /sys/class/ieee80211/<phyname>/index
* @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ * this attribute)
+ * NL80211_CHAN_HT20 = HT20 only
+ * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
*
* @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
* @NL80211_ATTR_IFNAME: network interface name
@@ -234,6 +273,9 @@ enum nl80211_commands {
* (u8, 0 or 1)
* @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
* (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ * rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
*
* @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION)
@@ -296,6 +338,14 @@ enum nl80211_attrs {
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,
+ NL80211_ATTR_MESH_PARAMS,
+
+ NL80211_ATTR_BSS_BASIC_RATES,
+
+ NL80211_ATTR_WIPHY_TXQ_PARAMS,
+ NL80211_ATTR_WIPHY_FREQ,
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -307,6 +357,10 @@ enum nl80211_attrs {
* here
*/
#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32
@@ -371,6 +425,32 @@ enum nl80211_sta_flags {
};
/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+ __NL80211_RATE_INFO_INVALID,
+ NL80211_RATE_INFO_BITRATE,
+ NL80211_RATE_INFO_MCS,
+ NL80211_RATE_INFO_40_MHZ_WIDTH,
+ NL80211_RATE_INFO_SHORT_GI,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_sta_info - station information
*
* These attribute types are used with %NL80211_ATTR_STA_INFO
@@ -382,6 +462,9 @@ enum nl80211_sta_flags {
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * containing info as possible, see &enum nl80211_sta_info_txrate.
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
@@ -391,6 +474,8 @@ enum nl80211_sta_info {
NL80211_STA_INFO_LLID,
NL80211_STA_INFO_PLID,
NL80211_STA_INFO_PLINK_STATE,
+ NL80211_STA_INFO_SIGNAL,
+ NL80211_STA_INFO_TX_BITRATE,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -452,17 +537,29 @@ enum nl80211_mpath_info {
* an array of nested frequency attributes
* @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
* an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ * defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
*/
enum nl80211_band_attr {
__NL80211_BAND_ATTR_INVALID,
NL80211_BAND_ATTR_FREQS,
NL80211_BAND_ATTR_RATES,
+ NL80211_BAND_ATTR_HT_MCS_SET,
+ NL80211_BAND_ATTR_HT_CAPA,
+ NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
/* keep last */
__NL80211_BAND_ATTR_AFTER_LAST,
NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
};
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
/**
* enum nl80211_frequency_attr - frequency attributes
* @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
@@ -474,6 +571,8 @@ enum nl80211_band_attr {
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
* on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+ * (100 * dBm).
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
@@ -482,12 +581,15 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
NL80211_FREQUENCY_ATTR_NO_IBSS,
NL80211_FREQUENCY_ATTR_RADAR,
+ NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
};
+#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+
/**
* enum nl80211_bitrate_attr - bitrate attributes
* @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
@@ -594,4 +696,119 @@ enum nl80211_mntr_flags {
NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
};
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ * millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
+ * open peer links when we detect compatible mesh peers.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ * containing a PREQ that an MP can send to a particular destination (path
+ * target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ * (in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ * until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ * points receiving a PREQ shall consider the forwarding information from the
+ * root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which an MP can send only one action frame containing a PREQ
+ * reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ * that it takes for an HWMP information element to propagate across the mesh
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+ __NL80211_MESHCONF_INVALID,
+ NL80211_MESHCONF_RETRY_TIMEOUT,
+ NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ NL80211_MESHCONF_HOLDING_TIMEOUT,
+ NL80211_MESHCONF_MAX_PEER_LINKS,
+ NL80211_MESHCONF_MAX_RETRIES,
+ NL80211_MESHCONF_TTL,
+ NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ NL80211_MESHCONF_PATH_REFRESH_TIME,
+ NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+
+ /* keep last */
+ __NL80211_MESHCONF_ATTR_AFTER_LAST,
+ NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ * disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+ __NL80211_TXQ_ATTR_INVALID,
+ NL80211_TXQ_ATTR_QUEUE,
+ NL80211_TXQ_ATTR_TXOP,
+ NL80211_TXQ_ATTR_CWMIN,
+ NL80211_TXQ_ATTR_CWMAX,
+ NL80211_TXQ_ATTR_AIFS,
+
+ /* keep last */
+ __NL80211_TXQ_ATTR_AFTER_LAST,
+ NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_txq_q {
+ NL80211_TXQ_Q_VO,
+ NL80211_TXQ_Q_VI,
+ NL80211_TXQ_Q_BE,
+ NL80211_TXQ_Q_BK
+};
+
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
+};
#endif /* __LINUX_NL80211_H */
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index c8a768e59640..afad7dec1b36 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -27,7 +27,6 @@ struct nsproxy {
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns;
- struct user_namespace *user_ns;
struct net *net_ns;
};
extern struct nsproxy init_nsproxy;
diff --git a/include/linux/of.h b/include/linux/of.h
index e2488f5e7cb2..6a7efa242f5e 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -57,6 +57,12 @@ extern struct device_node *of_get_next_child(const struct device_node *node,
for (child = of_get_next_child(parent, NULL); child != NULL; \
child = of_get_next_child(parent, child))
+extern struct device_node *of_find_node_with_property(
+ struct device_node *from, const char *prop_name);
+#define for_each_node_with_property(dn, prop_name) \
+ for (dn = of_find_node_with_property(NULL, prop_name); dn; \
+ dn = of_find_node_with_property(dn, prop_name))
+
extern struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp);
diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h
index 67db101d0eb8..fc2472c3c254 100644
--- a/include/linux/of_gpio.h
+++ b/include/linux/of_gpio.h
@@ -14,9 +14,22 @@
#ifndef __LINUX_OF_GPIO_H
#define __LINUX_OF_GPIO_H
+#include <linux/compiler.h>
+#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/gpio.h>
+struct device_node;
+
+/*
+ * This is Linux-specific flags. By default controllers' and Linux' mapping
+ * match, but GPIO controllers are free to translate their own flags to
+ * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended.
+ */
+enum of_gpio_flags {
+ OF_GPIO_ACTIVE_LOW = 0x1,
+};
+
#ifdef CONFIG_OF_GPIO
/*
@@ -26,7 +39,7 @@ struct of_gpio_chip {
struct gpio_chip gc;
int gpio_cells;
int (*xlate)(struct of_gpio_chip *of_gc, struct device_node *np,
- const void *gpio_spec);
+ const void *gpio_spec, enum of_gpio_flags *flags);
};
static inline struct of_gpio_chip *to_of_gpio_chip(struct gpio_chip *gc)
@@ -50,20 +63,43 @@ static inline struct of_mm_gpio_chip *to_of_mm_gpio_chip(struct gpio_chip *gc)
return container_of(of_gc, struct of_mm_gpio_chip, of_gc);
}
-extern int of_get_gpio(struct device_node *np, int index);
+extern int of_get_gpio_flags(struct device_node *np, int index,
+ enum of_gpio_flags *flags);
+extern unsigned int of_gpio_count(struct device_node *np);
+
extern int of_mm_gpiochip_add(struct device_node *np,
struct of_mm_gpio_chip *mm_gc);
extern int of_gpio_simple_xlate(struct of_gpio_chip *of_gc,
struct device_node *np,
- const void *gpio_spec);
+ const void *gpio_spec,
+ enum of_gpio_flags *flags);
#else
/* Drivers may not strictly depend on the GPIO support, so let them link. */
-static inline int of_get_gpio(struct device_node *np, int index)
+static inline int of_get_gpio_flags(struct device_node *np, int index,
+ enum of_gpio_flags *flags)
{
return -ENOSYS;
}
+static inline unsigned int of_gpio_count(struct device_node *np)
+{
+ return 0;
+}
+
#endif /* CONFIG_OF_GPIO */
+/**
+ * of_get_gpio - Get a GPIO number to use with GPIO API
+ * @np: device node to get GPIO from
+ * @index: index of the GPIO
+ *
+ * Returns GPIO number to use with Linux generic GPIO API, or one of the errno
+ * value on the error condition.
+ */
+static inline int of_get_gpio(struct device_node *np, int index)
+{
+ return of_get_gpio_flags(np, index, NULL);
+}
+
#endif /* __LINUX_OF_GPIO_H */
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index a8efcfeea732..3d327b67d7e2 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -26,8 +26,7 @@ extern struct bus_type of_platform_bus_type;
/*
* An of_platform_driver driver is attached to a basic of_device on
- * the "platform bus" (of_platform_bus_type) (or ISA, EBUS and SBUS
- * busses on sparc).
+ * the "platform bus" (of_platform_bus_type).
*/
struct of_platform_driver
{
diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h
index 5231861f357d..1ce9fe572e51 100644
--- a/include/linux/oprofile.h
+++ b/include/linux/oprofile.h
@@ -86,8 +86,7 @@ int oprofile_arch_init(struct oprofile_operations * ops);
void oprofile_arch_exit(void);
/**
- * Add a sample. This may be called from any context. Pass
- * smp_processor_id() as cpu.
+ * Add a sample. This may be called from any context.
*/
void oprofile_add_sample(struct pt_regs * const regs, unsigned long event);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index feb4657bb043..03b0b8c3c81b 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -134,6 +134,11 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
};
+enum pci_irq_reroute_variant {
+ INTEL_IRQ_REROUTE_VARIANT = 1,
+ MAX_IRQ_REROUTE_VARIANTS = 3
+};
+
typedef unsigned short __bitwise pci_bus_flags_t;
enum pci_bus_flags {
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
@@ -218,6 +223,7 @@ struct pci_dev {
unsigned int no_msi:1; /* device may not use msi */
unsigned int block_ucfg_access:1; /* userspace config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
+ unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
unsigned int msi_enabled:1;
unsigned int msix_enabled:1;
unsigned int ari_enabled:1; /* ARI forwarding */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1800f1d6e40d..b6e694454280 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2304,6 +2304,10 @@
#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329
#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A
#define PCI_DEVICE_ID_INTEL_PXHV 0x032C
+#define PCI_DEVICE_ID_INTEL_80332_0 0x0330
+#define PCI_DEVICE_ID_INTEL_80332_1 0x0332
+#define PCI_DEVICE_ID_INTEL_80333_0 0x0370
+#define PCI_DEVICE_ID_INTEL_80333_1 0x0372
#define PCI_DEVICE_ID_INTEL_82375 0x0482
#define PCI_DEVICE_ID_INTEL_82424 0x0483
#define PCI_DEVICE_ID_INTEL_82378 0x0484
@@ -2376,6 +2380,7 @@
#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4
#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6
#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
+#define PCI_DEVICE_ID_INTEL_ESB_10 0x25ac
#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500
#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501
#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 77c4ed60b982..d7e54d98869f 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -467,6 +467,8 @@ int genphy_restart_aneg(struct phy_device *phydev);
int genphy_config_aneg(struct phy_device *phydev);
int genphy_update_link(struct phy_device *phydev);
int genphy_read_status(struct phy_device *phydev);
+int genphy_suspend(struct phy_device *phydev);
+int genphy_resume(struct phy_device *phydev);
void phy_driver_unregister(struct phy_driver *drv);
int phy_driver_register(struct phy_driver *new_driver);
void phy_prepare_link(struct phy_device *phydev,
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 7cf7824df778..e6aa8482ad7a 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -394,6 +394,20 @@ enum
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
+
+/* Cgroup classifier */
+
+enum
+{
+ TCA_CGROUP_UNSPEC,
+ TCA_CGROUP_ACT,
+ TCA_CGROUP_POLICE,
+ TCA_CGROUP_EMATCHES,
+ __TCA_CGROUP_MAX,
+};
+
+#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1)
+
/* Extended Matches */
struct tcf_ematch_tree_hdr
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 5d921fa91a5b..e3f133adba78 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -500,4 +500,20 @@ struct tc_netem_corrupt
#define NETEM_DIST_SCALE 8192
+/* DRR */
+
+enum
+{
+ TCA_DRR_UNSPEC,
+ TCA_DRR_QUANTUM,
+ __TCA_DRR_MAX
+};
+
+#define TCA_DRR_MAX (__TCA_DRR_MAX - 1)
+
+struct tc_drr_stats
+{
+ u32 deficit;
+};
+
#endif
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index a7c721355549..4f71bf4e628c 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -45,7 +45,11 @@ struct k_itimer {
int it_requeue_pending; /* waiting to requeue this timer */
#define REQUEUE_PENDING 1
int it_sigev_notify; /* notify word of sigevent struct */
- struct task_struct *it_process; /* process to send signal to */
+ struct signal_struct *it_signal;
+ union {
+ struct pid *it_pid; /* pid of process to send signal to */
+ struct task_struct *it_process; /* for clock_nanosleep */
+ };
struct sigqueue *sigq; /* signal queue entry. */
union {
struct {
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 22641d5d45df..98b93ca4db06 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -94,6 +94,7 @@ extern void ptrace_notify(int exit_code);
extern void __ptrace_link(struct task_struct *child,
struct task_struct *new_parent);
extern void __ptrace_unlink(struct task_struct *child);
+extern void ptrace_fork(struct task_struct *task, unsigned long clone_flags);
#define PTRACE_MODE_READ 1
#define PTRACE_MODE_ATTACH 2
/* Returns 0 on success, -errno on denial. */
@@ -313,6 +314,27 @@ static inline void user_enable_block_step(struct task_struct *task)
#define arch_ptrace_stop(code, info) do { } while (0)
#endif
+#ifndef arch_ptrace_untrace
+/*
+ * Do machine-specific work before untracing child.
+ *
+ * This is called for a normal detach as well as from ptrace_exit()
+ * when the tracing task dies.
+ *
+ * Called with write_lock(&tasklist_lock) held.
+ */
+#define arch_ptrace_untrace(task) do { } while (0)
+#endif
+
+#ifndef arch_ptrace_fork
+/*
+ * Do machine-specific work to initialize a new task.
+ *
+ * This is called from copy_process().
+ */
+#define arch_ptrace_fork(child, clone_flags) do { } while (0)
+#endif
+
extern int task_current_syscall(struct task_struct *target, long *callno,
unsigned long args[6], unsigned int maxargs,
unsigned long *sp, unsigned long *pc);
diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h
index 301dda829e37..f3f697df1d71 100644
--- a/include/linux/rcuclassic.h
+++ b/include/linux/rcuclassic.h
@@ -59,8 +59,8 @@ struct rcu_ctrlblk {
int signaled;
spinlock_t lock ____cacheline_internodealigned_in_smp;
- cpumask_t cpumask; /* CPUs that need to switch in order */
- /* for current batch to proceed. */
+ DECLARE_BITMAP(cpumask, NR_CPUS); /* CPUs that need to switch for */
+ /* current batch to proceed. */
} ____cacheline_internodealigned_in_smp;
/* Is batch a before batch b ? */
diff --git a/include/linux/rculist_nulls.h b/include/linux/rculist_nulls.h
new file mode 100644
index 000000000000..f9ddd03961a8
--- /dev/null
+++ b/include/linux/rculist_nulls.h
@@ -0,0 +1,110 @@
+#ifndef _LINUX_RCULIST_NULLS_H
+#define _LINUX_RCULIST_NULLS_H
+
+#ifdef __KERNEL__
+
+/*
+ * RCU-protected list version
+ */
+#include <linux/list_nulls.h>
+#include <linux/rcupdate.h>
+
+/**
+ * hlist_nulls_del_init_rcu - deletes entry from hash list with re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: hlist_nulls_unhashed() on the node return true after this. It is
+ * useful for RCU based read lockfree traversal if the writer side
+ * must know if the list entry is still hashed or already unhashed.
+ *
+ * In particular, it means that we can not poison the forward pointers
+ * that may still be used for walking the hash list and we can only
+ * zero the pprev pointer so list_unhashed() will return true after
+ * this.
+ *
+ * The caller must take whatever precautions are necessary (such as
+ * holding appropriate locks) to avoid racing with another
+ * list-mutation primitive, such as hlist_nulls_add_head_rcu() or
+ * hlist_nulls_del_rcu(), running on this same list. However, it is
+ * perfectly legal to run concurrently with the _rcu list-traversal
+ * primitives, such as hlist_nulls_for_each_entry_rcu().
+ */
+static inline void hlist_nulls_del_init_rcu(struct hlist_nulls_node *n)
+{
+ if (!hlist_nulls_unhashed(n)) {
+ __hlist_nulls_del(n);
+ n->pprev = NULL;
+ }
+}
+
+/**
+ * hlist_nulls_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: hlist_nulls_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_nulls_add_head_rcu()
+ * or hlist_nulls_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_nulls_for_each_entry().
+ */
+static inline void hlist_nulls_del_rcu(struct hlist_nulls_node *n)
+{
+ __hlist_nulls_del(n);
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_nulls_add_head_rcu
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist_nulls,
+ * while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_nulls_add_head_rcu()
+ * or hlist_nulls_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_nulls_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs. Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_nulls_add_head_rcu(struct hlist_nulls_node *n,
+ struct hlist_nulls_head *h)
+{
+ struct hlist_nulls_node *first = h->first;
+
+ n->next = first;
+ n->pprev = &h->first;
+ rcu_assign_pointer(h->first, n);
+ if (!is_a_nulls(first))
+ first->pprev = &n->next;
+}
+/**
+ * hlist_nulls_for_each_entry_rcu - iterate over rcu list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_nulls_node to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_nulls_node within the struct.
+ *
+ */
+#define hlist_nulls_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = rcu_dereference((head)->first); \
+ (!is_a_nulls(pos)) && \
+ ({ tpos = hlist_nulls_entry(pos, typeof(*tpos), member); 1; }); \
+ pos = rcu_dereference(pos->next))
+
+#endif
+#endif
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 895dc9c1088c..1168fbcea8d4 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -52,11 +52,15 @@ struct rcu_head {
void (*func)(struct rcu_head *head);
};
-#ifdef CONFIG_CLASSIC_RCU
+#if defined(CONFIG_CLASSIC_RCU)
#include <linux/rcuclassic.h>
-#else /* #ifdef CONFIG_CLASSIC_RCU */
+#elif defined(CONFIG_TREE_RCU)
+#include <linux/rcutree.h>
+#elif defined(CONFIG_PREEMPT_RCU)
#include <linux/rcupreempt.h>
-#endif /* #else #ifdef CONFIG_CLASSIC_RCU */
+#else
+#error "Unknown RCU implementation specified to kernel configuration"
+#endif /* #else #if defined(CONFIG_CLASSIC_RCU) */
#define RCU_HEAD_INIT { .next = NULL, .func = NULL }
#define RCU_HEAD(head) struct rcu_head head = RCU_HEAD_INIT
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
new file mode 100644
index 000000000000..d4368b7975c3
--- /dev/null
+++ b/include/linux/rcutree.h
@@ -0,0 +1,329 @@
+/*
+ * Read-Copy Update mechanism for mutual exclusion (tree-based version)
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * Copyright IBM Corporation, 2008
+ *
+ * Author: Dipankar Sarma <dipankar@in.ibm.com>
+ * Paul E. McKenney <paulmck@linux.vnet.ibm.com> Hierarchical algorithm
+ *
+ * Based on the original work by Paul McKenney <paulmck@us.ibm.com>
+ * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen.
+ *
+ * For detailed explanation of Read-Copy Update mechanism see -
+ * Documentation/RCU
+ */
+
+#ifndef __LINUX_RCUTREE_H
+#define __LINUX_RCUTREE_H
+
+#include <linux/cache.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/percpu.h>
+#include <linux/cpumask.h>
+#include <linux/seqlock.h>
+
+/*
+ * Define shape of hierarchy based on NR_CPUS and CONFIG_RCU_FANOUT.
+ * In theory, it should be possible to add more levels straightforwardly.
+ * In practice, this has not been tested, so there is probably some
+ * bug somewhere.
+ */
+#define MAX_RCU_LVLS 3
+#define RCU_FANOUT (CONFIG_RCU_FANOUT)
+#define RCU_FANOUT_SQ (RCU_FANOUT * RCU_FANOUT)
+#define RCU_FANOUT_CUBE (RCU_FANOUT_SQ * RCU_FANOUT)
+
+#if NR_CPUS <= RCU_FANOUT
+# define NUM_RCU_LVLS 1
+# define NUM_RCU_LVL_0 1
+# define NUM_RCU_LVL_1 (NR_CPUS)
+# define NUM_RCU_LVL_2 0
+# define NUM_RCU_LVL_3 0
+#elif NR_CPUS <= RCU_FANOUT_SQ
+# define NUM_RCU_LVLS 2
+# define NUM_RCU_LVL_0 1
+# define NUM_RCU_LVL_1 (((NR_CPUS) + RCU_FANOUT - 1) / RCU_FANOUT)
+# define NUM_RCU_LVL_2 (NR_CPUS)
+# define NUM_RCU_LVL_3 0
+#elif NR_CPUS <= RCU_FANOUT_CUBE
+# define NUM_RCU_LVLS 3
+# define NUM_RCU_LVL_0 1
+# define NUM_RCU_LVL_1 (((NR_CPUS) + RCU_FANOUT_SQ - 1) / RCU_FANOUT_SQ)
+# define NUM_RCU_LVL_2 (((NR_CPUS) + (RCU_FANOUT) - 1) / (RCU_FANOUT))
+# define NUM_RCU_LVL_3 NR_CPUS
+#else
+# error "CONFIG_RCU_FANOUT insufficient for NR_CPUS"
+#endif /* #if (NR_CPUS) <= RCU_FANOUT */
+
+#define RCU_SUM (NUM_RCU_LVL_0 + NUM_RCU_LVL_1 + NUM_RCU_LVL_2 + NUM_RCU_LVL_3)
+#define NUM_RCU_NODES (RCU_SUM - NR_CPUS)
+
+/*
+ * Dynticks per-CPU state.
+ */
+struct rcu_dynticks {
+ int dynticks_nesting; /* Track nesting level, sort of. */
+ int dynticks; /* Even value for dynticks-idle, else odd. */
+ int dynticks_nmi; /* Even value for either dynticks-idle or */
+ /* not in nmi handler, else odd. So this */
+ /* remains even for nmi from irq handler. */
+};
+
+/*
+ * Definition for node within the RCU grace-period-detection hierarchy.
+ */
+struct rcu_node {
+ spinlock_t lock;
+ unsigned long qsmask; /* CPUs or groups that need to switch in */
+ /* order for current grace period to proceed.*/
+ unsigned long qsmaskinit;
+ /* Per-GP initialization for qsmask. */
+ unsigned long grpmask; /* Mask to apply to parent qsmask. */
+ int grplo; /* lowest-numbered CPU or group here. */
+ int grphi; /* highest-numbered CPU or group here. */
+ u8 grpnum; /* CPU/group number for next level up. */
+ u8 level; /* root is at level 0. */
+ struct rcu_node *parent;
+} ____cacheline_internodealigned_in_smp;
+
+/* Index values for nxttail array in struct rcu_data. */
+#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */
+#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */
+#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */
+#define RCU_NEXT_TAIL 3
+#define RCU_NEXT_SIZE 4
+
+/* Per-CPU data for read-copy update. */
+struct rcu_data {
+ /* 1) quiescent-state and grace-period handling : */
+ long completed; /* Track rsp->completed gp number */
+ /* in order to detect GP end. */
+ long gpnum; /* Highest gp number that this CPU */
+ /* is aware of having started. */
+ long passed_quiesc_completed;
+ /* Value of completed at time of qs. */
+ bool passed_quiesc; /* User-mode/idle loop etc. */
+ bool qs_pending; /* Core waits for quiesc state. */
+ bool beenonline; /* CPU online at least once. */
+ struct rcu_node *mynode; /* This CPU's leaf of hierarchy */
+ unsigned long grpmask; /* Mask to apply to leaf qsmask. */
+
+ /* 2) batch handling */
+ /*
+ * If nxtlist is not NULL, it is partitioned as follows.
+ * Any of the partitions might be empty, in which case the
+ * pointer to that partition will be equal to the pointer for
+ * the following partition. When the list is empty, all of
+ * the nxttail elements point to nxtlist, which is NULL.
+ *
+ * [*nxttail[RCU_NEXT_READY_TAIL], NULL = *nxttail[RCU_NEXT_TAIL]):
+ * Entries that might have arrived after current GP ended
+ * [*nxttail[RCU_WAIT_TAIL], *nxttail[RCU_NEXT_READY_TAIL]):
+ * Entries known to have arrived before current GP ended
+ * [*nxttail[RCU_DONE_TAIL], *nxttail[RCU_WAIT_TAIL]):
+ * Entries that batch # <= ->completed - 1: waiting for current GP
+ * [nxtlist, *nxttail[RCU_DONE_TAIL]):
+ * Entries that batch # <= ->completed
+ * The grace period for these entries has completed, and
+ * the other grace-period-completed entries may be moved
+ * here temporarily in rcu_process_callbacks().
+ */
+ struct rcu_head *nxtlist;
+ struct rcu_head **nxttail[RCU_NEXT_SIZE];
+ long qlen; /* # of queued callbacks */
+ long blimit; /* Upper limit on a processed batch */
+
+#ifdef CONFIG_NO_HZ
+ /* 3) dynticks interface. */
+ struct rcu_dynticks *dynticks; /* Shared per-CPU dynticks state. */
+ int dynticks_snap; /* Per-GP tracking for dynticks. */
+ int dynticks_nmi_snap; /* Per-GP tracking for dynticks_nmi. */
+#endif /* #ifdef CONFIG_NO_HZ */
+
+ /* 4) reasons this CPU needed to be kicked by force_quiescent_state */
+#ifdef CONFIG_NO_HZ
+ unsigned long dynticks_fqs; /* Kicked due to dynticks idle. */
+#endif /* #ifdef CONFIG_NO_HZ */
+ unsigned long offline_fqs; /* Kicked due to being offline. */
+ unsigned long resched_ipi; /* Sent a resched IPI. */
+
+ /* 5) state to allow this CPU to force_quiescent_state on others */
+ long n_rcu_pending; /* rcu_pending() calls since boot. */
+ long n_rcu_pending_force_qs; /* when to force quiescent states. */
+
+ int cpu;
+};
+
+/* Values for signaled field in struct rcu_state. */
+#define RCU_GP_INIT 0 /* Grace period being initialized. */
+#define RCU_SAVE_DYNTICK 1 /* Need to scan dyntick state. */
+#define RCU_FORCE_QS 2 /* Need to force quiescent state. */
+#ifdef CONFIG_NO_HZ
+#define RCU_SIGNAL_INIT RCU_SAVE_DYNTICK
+#else /* #ifdef CONFIG_NO_HZ */
+#define RCU_SIGNAL_INIT RCU_FORCE_QS
+#endif /* #else #ifdef CONFIG_NO_HZ */
+
+#define RCU_JIFFIES_TILL_FORCE_QS 3 /* for rsp->jiffies_force_qs */
+#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
+#define RCU_SECONDS_TILL_STALL_CHECK (10 * HZ) /* for rsp->jiffies_stall */
+#define RCU_SECONDS_TILL_STALL_RECHECK (30 * HZ) /* for rsp->jiffies_stall */
+#define RCU_STALL_RAT_DELAY 2 /* Allow other CPUs time */
+ /* to take at least one */
+ /* scheduling clock irq */
+ /* before ratting on them. */
+
+#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
+
+/*
+ * RCU global state, including node hierarchy. This hierarchy is
+ * represented in "heap" form in a dense array. The root (first level)
+ * of the hierarchy is in ->node[0] (referenced by ->level[0]), the second
+ * level in ->node[1] through ->node[m] (->node[1] referenced by ->level[1]),
+ * and the third level in ->node[m+1] and following (->node[m+1] referenced
+ * by ->level[2]). The number of levels is determined by the number of
+ * CPUs and by CONFIG_RCU_FANOUT. Small systems will have a "hierarchy"
+ * consisting of a single rcu_node.
+ */
+struct rcu_state {
+ struct rcu_node node[NUM_RCU_NODES]; /* Hierarchy. */
+ struct rcu_node *level[NUM_RCU_LVLS]; /* Hierarchy levels. */
+ u32 levelcnt[MAX_RCU_LVLS + 1]; /* # nodes in each level. */
+ u8 levelspread[NUM_RCU_LVLS]; /* kids/node in each level. */
+ struct rcu_data *rda[NR_CPUS]; /* array of rdp pointers. */
+
+ /* The following fields are guarded by the root rcu_node's lock. */
+
+ u8 signaled ____cacheline_internodealigned_in_smp;
+ /* Force QS state. */
+ long gpnum; /* Current gp number. */
+ long completed; /* # of last completed gp. */
+ spinlock_t onofflock; /* exclude on/offline and */
+ /* starting new GP. */
+ 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 */
+ /* force_quiescent_state(). */
+ unsigned long n_force_qs_lh; /* ~Number of calls leaving */
+ /* due to lock unavailable. */
+ unsigned long n_force_qs_ngp; /* Number of calls leaving */
+ /* due to no GP active. */
+#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
+ unsigned long gp_start; /* Time at which GP started, */
+ /* but in jiffies. */
+ unsigned long jiffies_stall; /* Time at which to check */
+ /* for CPU stalls. */
+#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
+#ifdef CONFIG_NO_HZ
+ long dynticks_completed; /* Value of completed @ snap. */
+#endif /* #ifdef CONFIG_NO_HZ */
+};
+
+extern struct rcu_state rcu_state;
+DECLARE_PER_CPU(struct rcu_data, rcu_data);
+
+extern struct rcu_state rcu_bh_state;
+DECLARE_PER_CPU(struct rcu_data, rcu_bh_data);
+
+/*
+ * Increment the quiescent state counter.
+ * The counter is a bit degenerated: We do not need to know
+ * how many quiescent states passed, just if there was at least
+ * one since the start of the grace period. Thus just a flag.
+ */
+static inline void rcu_qsctr_inc(int cpu)
+{
+ struct rcu_data *rdp = &per_cpu(rcu_data, cpu);
+ rdp->passed_quiesc = 1;
+ rdp->passed_quiesc_completed = rdp->completed;
+}
+static inline void rcu_bh_qsctr_inc(int cpu)
+{
+ struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu);
+ rdp->passed_quiesc = 1;
+ rdp->passed_quiesc_completed = rdp->completed;
+}
+
+extern int rcu_pending(int cpu);
+extern int rcu_needs_cpu(int cpu);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+extern struct lockdep_map rcu_lock_map;
+# define rcu_read_acquire() \
+ lock_acquire(&rcu_lock_map, 0, 0, 2, 1, NULL, _THIS_IP_)
+# define rcu_read_release() lock_release(&rcu_lock_map, 1, _THIS_IP_)
+#else
+# define rcu_read_acquire() do { } while (0)
+# define rcu_read_release() do { } while (0)
+#endif
+
+static inline void __rcu_read_lock(void)
+{
+ preempt_disable();
+ __acquire(RCU);
+ rcu_read_acquire();
+}
+static inline void __rcu_read_unlock(void)
+{
+ rcu_read_release();
+ __release(RCU);
+ preempt_enable();
+}
+static inline void __rcu_read_lock_bh(void)
+{
+ local_bh_disable();
+ __acquire(RCU_BH);
+ rcu_read_acquire();
+}
+static inline void __rcu_read_unlock_bh(void)
+{
+ rcu_read_release();
+ __release(RCU_BH);
+ local_bh_enable();
+}
+
+#define __synchronize_sched() synchronize_rcu()
+
+#define call_rcu_sched(head, func) call_rcu(head, func)
+
+static inline void rcu_init_sched(void)
+{
+}
+
+extern void __rcu_init(void);
+extern void rcu_check_callbacks(int cpu, int user);
+extern void rcu_restart_cpu(int cpu);
+
+extern long rcu_batches_completed(void);
+extern long rcu_batches_completed_bh(void);
+
+#ifdef CONFIG_NO_HZ
+void rcu_enter_nohz(void);
+void rcu_exit_nohz(void);
+#else /* CONFIG_NO_HZ */
+static inline void rcu_enter_nohz(void)
+{
+}
+static inline void rcu_exit_nohz(void)
+{
+}
+#endif /* CONFIG_NO_HZ */
+
+#endif /* __LINUX_RCUTREE_H */
diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
index 4cd64b0d9825..164332cbb77c 100644
--- a/include/linux/rfkill.h
+++ b/include/linux/rfkill.h
@@ -108,6 +108,7 @@ struct rfkill {
struct device dev;
struct list_head node;
+ enum rfkill_state state_for_resume;
};
#define to_rfkill(d) container_of(d, struct rfkill, dev)
@@ -148,11 +149,4 @@ static inline char *rfkill_get_led_name(struct rfkill *rfkill)
#endif
}
-/* rfkill notification chain */
-#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill
- switch has changed */
-
-int register_rfkill_notifier(struct notifier_block *nb);
-int unregister_rfkill_notifier(struct notifier_block *nb);
-
#endif /* RFKILL_H */
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
index d363467c8f13..b3b359660082 100644
--- a/include/linux/ring_buffer.h
+++ b/include/linux/ring_buffer.h
@@ -118,6 +118,8 @@ void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu);
unsigned long ring_buffer_entries(struct ring_buffer *buffer);
unsigned long ring_buffer_overruns(struct ring_buffer *buffer);
+unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu);
+unsigned long ring_buffer_overrun_cpu(struct ring_buffer *buffer, int cpu);
u64 ring_buffer_time_stamp(int cpu);
void ring_buffer_normalize_time_stamp(int cpu, u64 *ts);
diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h
index 90987b7bcc1b..32c0547ffafc 100644
--- a/include/linux/rio_drv.h
+++ b/include/linux/rio_drv.h
@@ -427,9 +427,9 @@ void rio_dev_put(struct rio_dev *);
* Get the unique RIO device identifier. Returns the device
* identifier string.
*/
-static inline char *rio_name(struct rio_dev *rdev)
+static inline const char *rio_name(struct rio_dev *rdev)
{
- return rdev->dev.bus_id;
+ return dev_name(&rdev->dev);
}
/**
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 2b3d51c6ec9c..e88f7058b3a1 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -107,6 +107,11 @@ enum {
RTM_GETADDRLABEL,
#define RTM_GETADDRLABEL RTM_GETADDRLABEL
+ RTM_GETDCB = 78,
+#define RTM_GETDCB RTM_GETDCB
+ RTM_SETDCB,
+#define RTM_SETDCB RTM_SETDCB
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
diff --git a/include/linux/sched.h b/include/linux/sched.h
index e5f928a079e8..158d53d07765 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -571,12 +571,6 @@ struct signal_struct {
*/
struct rlimit rlim[RLIM_NLIMITS];
- /* keep the process-shared keyrings here so that they do the right
- * thing in threads created with CLONE_THREAD */
-#ifdef CONFIG_KEYS
- struct key *session_keyring; /* keyring inherited over fork */
- struct key *process_keyring; /* keyring private to this process */
-#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
struct pacct_struct pacct; /* per-process accounting information */
#endif
@@ -647,6 +641,7 @@ struct user_struct {
/* Hash table maintenance information */
struct hlist_node uidhash_node;
uid_t uid;
+ struct user_namespace *user_ns;
#ifdef CONFIG_USER_SCHED
struct task_group *tg;
@@ -664,6 +659,7 @@ extern struct user_struct *find_user(uid_t);
extern struct user_struct root_user;
#define INIT_USER (&root_user)
+
struct backing_dev_info;
struct reclaim_state;
@@ -671,8 +667,7 @@ struct reclaim_state;
struct sched_info {
/* cumulative counters */
unsigned long pcount; /* # of times run on this cpu */
- unsigned long long cpu_time, /* time spent on the cpu */
- run_delay; /* time spent waiting on a runqueue */
+ unsigned long long run_delay; /* time spent waiting on a runqueue */
/* timestamps */
unsigned long long last_arrival,/* when we last ran on a cpu */
@@ -941,38 +936,7 @@ partition_sched_domains(int ndoms_new, struct cpumask *doms_new,
#endif /* !CONFIG_SMP */
struct io_context; /* See blkdev.h */
-#define NGROUPS_SMALL 32
-#define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
-struct group_info {
- int ngroups;
- atomic_t usage;
- gid_t small_block[NGROUPS_SMALL];
- int nblocks;
- gid_t *blocks[0];
-};
-/*
- * get_group_info() must be called with the owning task locked (via task_lock())
- * when task != current. The reason being that the vast majority of callers are
- * looking at current->group_info, which can not be changed except by the
- * current task. Changing current->group_info requires the task lock, too.
- */
-#define get_group_info(group_info) do { \
- atomic_inc(&(group_info)->usage); \
-} while (0)
-
-#define put_group_info(group_info) do { \
- if (atomic_dec_and_test(&(group_info)->usage)) \
- groups_free(group_info); \
-} while (0)
-
-extern struct group_info *groups_alloc(int gidsetsize);
-extern void groups_free(struct group_info *group_info);
-extern int set_current_groups(struct group_info *group_info);
-extern int groups_search(struct group_info *group_info, gid_t grp);
-/* access the groups "array" with this macro */
-#define GROUP_AT(gi, i) \
- ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK])
#ifdef ARCH_HAS_PREFETCH_SWITCH_STACK
extern void prefetch_stack(struct task_struct *t);
@@ -1228,6 +1192,7 @@ struct task_struct {
* The buffer to hold the BTS data.
*/
void *bts_buffer;
+ size_t bts_size;
#endif /* CONFIG_X86_PTRACE_BTS */
/* PID/PID hash table linkage. */
@@ -1251,17 +1216,12 @@ struct task_struct {
struct list_head cpu_timers[3];
/* process credentials */
- uid_t uid,euid,suid,fsuid;
- gid_t gid,egid,sgid,fsgid;
- struct group_info *group_info;
- kernel_cap_t cap_effective, cap_inheritable, cap_permitted, cap_bset;
- struct user_struct *user;
- unsigned securebits;
-#ifdef CONFIG_KEYS
- unsigned char jit_keyring; /* default keyring to attach requested keys to */
- struct key *request_key_auth; /* assumed request_key authority */
- struct key *thread_keyring; /* keyring private to this thread */
-#endif
+ const struct cred *real_cred; /* objective and real subjective task
+ * credentials (COW) */
+ const struct cred *cred; /* effective (overridable) subjective task
+ * credentials (COW) */
+ struct mutex cred_exec_mutex; /* execve vs ptrace cred calculation mutex */
+
char comm[TASK_COMM_LEN]; /* executable name excluding path
- access with [gs]et_task_comm (which lock
it with task_lock())
@@ -1298,9 +1258,6 @@ struct task_struct {
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
-#ifdef CONFIG_SECURITY
- void *security;
-#endif
struct audit_context *audit_context;
#ifdef CONFIG_AUDITSYSCALL
uid_t loginuid;
@@ -1857,7 +1814,6 @@ static inline struct user_struct *get_uid(struct user_struct *u)
return u;
}
extern void free_uid(struct user_struct *);
-extern void switch_uid(struct user_struct *);
extern void release_uids(struct user_namespace *ns);
#include <asm/current.h>
@@ -1876,9 +1832,6 @@ extern void wake_up_new_task(struct task_struct *tsk,
extern void sched_fork(struct task_struct *p, int clone_flags);
extern void sched_dead(struct task_struct *p);
-extern int in_group_p(gid_t);
-extern int in_egroup_p(gid_t);
-
extern void proc_caches_init(void);
extern void flush_signals(struct task_struct *);
extern void ignore_signals(struct task_struct *);
@@ -2010,6 +1963,8 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
+extern bool is_single_threaded(struct task_struct *);
+
/*
* Careful: do_each_thread/while_each_thread is a double loop so
* 'break' will not work as expected - use goto instead.
diff --git a/include/linux/securebits.h b/include/linux/securebits.h
index 92f09bdf1175..d2c5ed845bcc 100644
--- a/include/linux/securebits.h
+++ b/include/linux/securebits.h
@@ -32,7 +32,7 @@
setting is locked or not. A setting which is locked cannot be
changed from user-level. */
#define issecure_mask(X) (1 << (X))
-#define issecure(X) (issecure_mask(X) & current->securebits)
+#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
issecure_mask(SECURE_NO_SETUID_FIXUP) | \
diff --git a/include/linux/security.h b/include/linux/security.h
index e3d4ecda2673..3416cb85e77b 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -37,6 +37,10 @@
/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10
+/* If capable should audit the security request */
+#define SECURITY_CAP_NOAUDIT 0
+#define SECURITY_CAP_AUDIT 1
+
struct ctl_table;
struct audit_krule;
@@ -44,25 +48,25 @@ struct audit_krule;
* These functions are in security/capability.c and are used
* as the default capabilities functions
*/
-extern int cap_capable(struct task_struct *tsk, int cap);
+extern int cap_capable(struct task_struct *tsk, int cap, int audit);
extern int cap_settime(struct timespec *ts, struct timezone *tz);
extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent);
extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern int cap_capset_check(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern void cap_capset_set(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
-extern int cap_bprm_set_security(struct linux_binprm *bprm);
-extern void cap_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
+extern int cap_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted);
+extern int cap_bprm_set_creds(struct linux_binprm *bprm);
extern int cap_bprm_secureexec(struct linux_binprm *bprm);
extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags);
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
-extern int cap_task_post_setuid(uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
-extern void cap_task_reparent_to_init(struct task_struct *p);
+extern int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags);
extern int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p);
+ unsigned long arg4, unsigned long arg5);
extern int cap_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp);
extern int cap_task_setioprio(struct task_struct *p, int ioprio);
extern int cap_task_setnice(struct task_struct *p, int nice);
@@ -105,7 +109,7 @@ extern unsigned long mmap_min_addr;
struct sched_param;
struct request_sock;
-/* bprm_apply_creds unsafe reasons */
+/* bprm->unsafe reasons */
#define LSM_UNSAFE_SHARE 1
#define LSM_UNSAFE_PTRACE 2
#define LSM_UNSAFE_PTRACE_CAP 4
@@ -149,36 +153,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
*
* Security hooks for program execution operations.
*
- * @bprm_alloc_security:
- * Allocate and attach a security structure to the @bprm->security field.
- * The security field is initialized to NULL when the bprm structure is
- * allocated.
- * @bprm contains the linux_binprm structure to be modified.
- * Return 0 if operation was successful.
- * @bprm_free_security:
- * @bprm contains the linux_binprm structure to be modified.
- * Deallocate and clear the @bprm->security field.
- * @bprm_apply_creds:
- * Compute and set the security attributes of a process being transformed
- * by an execve operation based on the old attributes (current->security)
- * and the information saved in @bprm->security by the set_security hook.
- * Since this hook function (and its caller) are void, this hook can not
- * return an error. However, it can leave the security attributes of the
- * process unchanged if an access failure occurs at this point.
- * bprm_apply_creds is called under task_lock. @unsafe indicates various
- * reasons why it may be unsafe to change security state.
- * @bprm contains the linux_binprm structure.
- * @bprm_post_apply_creds:
- * Runs after bprm_apply_creds with the task_lock dropped, so that
- * functions which cannot be called safely under the task_lock can
- * be used. This hook is a good place to perform state changes on
- * the process such as closing open file descriptors to which access
- * is no longer granted if the attributes were changed.
- * Note that a security module might need to save state between
- * bprm_apply_creds and bprm_post_apply_creds to store the decision
- * on whether the process may proceed.
- * @bprm contains the linux_binprm structure.
- * @bprm_set_security:
+ * @bprm_set_creds:
* Save security information in the bprm->security field, typically based
* on information about the bprm->file, for later use by the apply_creds
* hook. This hook may also optionally check permissions (e.g. for
@@ -191,15 +166,30 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
* @bprm_check_security:
- * This hook mediates the point when a search for a binary handler will
- * begin. It allows a check the @bprm->security value which is set in
- * the preceding set_security call. The primary difference from
- * set_security is that the argv list and envp list are reliably
- * available in @bprm. This hook may be called multiple times
- * during a single execve; and in each pass set_security is called
- * first.
+ * This hook mediates the point when a search for a binary handler will
+ * begin. It allows a check the @bprm->security value which is set in the
+ * preceding set_creds call. The primary difference from set_creds is
+ * that the argv list and envp list are reliably available in @bprm. This
+ * hook may be called multiple times during a single execve; and in each
+ * pass set_creds is called first.
* @bprm contains the linux_binprm structure.
* Return 0 if the hook is successful and permission is granted.
+ * @bprm_committing_creds:
+ * Prepare to install the new security attributes of a process being
+ * transformed by an execve operation, based on the old credentials
+ * pointed to by @current->cred and the information set in @bprm->cred by
+ * the bprm_set_creds hook. @bprm points to the linux_binprm structure.
+ * This hook is a good place to perform state changes on the process such
+ * as closing open file descriptors to which access will no longer be
+ * granted when the attributes are changed. This is called immediately
+ * before commit_creds().
+ * @bprm_committed_creds:
+ * Tidy up after the installation of the new security attributes of a
+ * process being transformed by an execve operation. The new credentials
+ * have, by this point, been set to @current->cred. @bprm points to the
+ * linux_binprm structure. This hook is a good place to perform state
+ * changes on the process such as clearing out non-inheritable signal
+ * state. This is called immediately after commit_creds().
* @bprm_secureexec:
* Return a boolean value (0 or 1) indicating whether a "secure exec"
* is required. The flag is passed in the auxiliary table
@@ -585,15 +575,31 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted.
- * @task_alloc_security:
- * @p contains the task_struct for child process.
- * Allocate and attach a security structure to the p->security field. The
- * security field is initialized to NULL when the task structure is
- * allocated.
- * Return 0 if operation was successful.
- * @task_free_security:
- * @p contains the task_struct for process.
- * Deallocate and clear the p->security field.
+ * @cred_free:
+ * @cred points to the credentials.
+ * Deallocate and clear the cred->security field in a set of credentials.
+ * @cred_prepare:
+ * @new points to the new credentials.
+ * @old points to the original credentials.
+ * @gfp indicates the atomicity of any memory allocations.
+ * Prepare a new set of credentials by copying the data from the old set.
+ * @cred_commit:
+ * @new points to the new credentials.
+ * @old points to the original credentials.
+ * Install a new set of credentials.
+ * @kernel_act_as:
+ * Set the credentials for a kernel service to act as (subjective context).
+ * @new points to the credentials to be modified.
+ * @secid specifies the security ID to be set
+ * The current task must be the one that nominated @secid.
+ * Return 0 if successful.
+ * @kernel_create_files_as:
+ * Set the file creation context in a set of credentials to be the same as
+ * the objective context of the specified inode.
+ * @new points to the credentials to be modified.
+ * @inode points to the inode to use as a reference.
+ * The current task must be the one that nominated @inode.
+ * Return 0 if successful.
* @task_setuid:
* Check permission before setting one or more of the user identity
* attributes of the current process. The @flags parameter indicates
@@ -606,15 +612,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @id2 contains a uid.
* @flags contains one of the LSM_SETID_* values.
* Return 0 if permission is granted.
- * @task_post_setuid:
+ * @task_fix_setuid:
* Update the module's state after setting one or more of the user
* identity attributes of the current process. The @flags parameter
* indicates which of the set*uid system calls invoked this hook. If
- * @flags is LSM_SETID_FS, then @old_ruid is the old fs uid and the other
- * parameters are not used.
- * @old_ruid contains the old real uid (or fs uid if LSM_SETID_FS).
- * @old_euid contains the old effective uid (or -1 if LSM_SETID_FS).
- * @old_suid contains the old saved uid (or -1 if LSM_SETID_FS).
+ * @new is the set of credentials that will be installed. Modifications
+ * should be made to this rather than to @current->cred.
+ * @old is the set of credentials that are being replaces
* @flags contains one of the LSM_SETID_* values.
* Return 0 on success.
* @task_setgid:
@@ -717,13 +721,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @arg3 contains a argument.
* @arg4 contains a argument.
* @arg5 contains a argument.
- * @rc_p contains a pointer to communicate back the forced return code
- * Return 0 if permission is granted, and non-zero if the security module
- * has taken responsibility (setting *rc_p) for the prctl call.
- * @task_reparent_to_init:
- * Set the security attributes in @p->security for a kernel thread that
- * is being reparented to the init task.
- * @p contains the task_struct for the kernel thread.
+ * Return -ENOSYS if no-one wanted to handle this op, any other value to
+ * cause prctl() to return immediately with that value.
* @task_to_inode:
* Set the security attributes for an inode based on an associated task's
* security attributes, e.g. for /proc/pid inodes.
@@ -1000,7 +999,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* See whether a specific operational right is granted to a process on a
* key.
* @key_ref refers to the key (key pointer + possession attribute bit).
- * @context points to the process to provide the context against which to
+ * @cred points to the credentials to provide the context against which to
* evaluate the security data on the key.
* @perm describes the combination of permissions required of this key.
* Return 1 if permission granted, 0 if permission denied and -ve it the
@@ -1162,6 +1161,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @child process.
* Security modules may also want to perform a process tracing check
* during an execve in the set_security or apply_creds hooks of
+ * tracing check during an execve in the bprm_set_creds hook of
* binprm_security_ops if the process is being traced and its security
* attributes would be changed by the execve.
* @child contains the task_struct structure for the target process.
@@ -1185,29 +1185,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
* Return 0 if the capability sets were successfully obtained.
- * @capset_check:
- * Check permission before setting the @effective, @inheritable, and
- * @permitted capability sets for the @target process.
- * Caveat: @target is also set to current if a set of processes is
- * specified (i.e. all processes other than current and init or a
- * particular process group). Hence, the capset_set hook may need to
- * revalidate permission to the actual target process.
- * @target contains the task_struct structure for target process.
- * @effective contains the effective capability set.
- * @inheritable contains the inheritable capability set.
- * @permitted contains the permitted capability set.
- * Return 0 if permission is granted.
- * @capset_set:
+ * @capset:
* Set the @effective, @inheritable, and @permitted capability sets for
- * the @target process. Since capset_check cannot always check permission
- * to the real @target process, this hook may also perform permission
- * checking to determine if the current process is allowed to set the
- * capability sets of the @target process. However, this hook has no way
- * of returning an error due to the structure of the sys_capset code.
- * @target contains the task_struct structure for target process.
+ * the current process.
+ * @new contains the new credentials structure for target process.
+ * @old contains the current credentials structure for target process.
* @effective contains the effective capability set.
* @inheritable contains the inheritable capability set.
* @permitted contains the permitted capability set.
+ * Return 0 and update @new if permission is granted.
* @capable:
* Check whether the @tsk process has the @cap capability.
* @tsk contains the task_struct for the process.
@@ -1299,15 +1285,12 @@ struct security_operations {
int (*capget) (struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted);
- int (*capset_check) (struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted);
- void (*capset_set) (struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted);
- int (*capable) (struct task_struct *tsk, int cap);
+ int (*capset) (struct cred *new,
+ const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted);
+ int (*capable) (struct task_struct *tsk, int cap, int audit);
int (*acct) (struct file *file);
int (*sysctl) (struct ctl_table *table, int op);
int (*quotactl) (int cmds, int type, int id, struct super_block *sb);
@@ -1316,18 +1299,16 @@ struct security_operations {
int (*settime) (struct timespec *ts, struct timezone *tz);
int (*vm_enough_memory) (struct mm_struct *mm, long pages);
- int (*bprm_alloc_security) (struct linux_binprm *bprm);
- void (*bprm_free_security) (struct linux_binprm *bprm);
- void (*bprm_apply_creds) (struct linux_binprm *bprm, int unsafe);
- void (*bprm_post_apply_creds) (struct linux_binprm *bprm);
- int (*bprm_set_security) (struct linux_binprm *bprm);
+ int (*bprm_set_creds) (struct linux_binprm *bprm);
int (*bprm_check_security) (struct linux_binprm *bprm);
int (*bprm_secureexec) (struct linux_binprm *bprm);
+ void (*bprm_committing_creds) (struct linux_binprm *bprm);
+ void (*bprm_committed_creds) (struct linux_binprm *bprm);
int (*sb_alloc_security) (struct super_block *sb);
void (*sb_free_security) (struct super_block *sb);
int (*sb_copy_data) (char *orig, char *copy);
- int (*sb_kern_mount) (struct super_block *sb, void *data);
+ int (*sb_kern_mount) (struct super_block *sb, int flags, void *data);
int (*sb_show_options) (struct seq_file *m, struct super_block *sb);
int (*sb_statfs) (struct dentry *dentry);
int (*sb_mount) (char *dev_name, struct path *path,
@@ -1406,14 +1387,18 @@ struct security_operations {
int (*file_send_sigiotask) (struct task_struct *tsk,
struct fown_struct *fown, int sig);
int (*file_receive) (struct file *file);
- int (*dentry_open) (struct file *file);
+ int (*dentry_open) (struct file *file, const struct cred *cred);
int (*task_create) (unsigned long clone_flags);
- int (*task_alloc_security) (struct task_struct *p);
- void (*task_free_security) (struct task_struct *p);
+ void (*cred_free) (struct cred *cred);
+ int (*cred_prepare)(struct cred *new, const struct cred *old,
+ gfp_t gfp);
+ void (*cred_commit)(struct cred *new, const struct cred *old);
+ int (*kernel_act_as)(struct cred *new, u32 secid);
+ int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
- int (*task_post_setuid) (uid_t old_ruid /* or fsuid */ ,
- uid_t old_euid, uid_t old_suid, int flags);
+ int (*task_fix_setuid) (struct cred *new, const struct cred *old,
+ int flags);
int (*task_setgid) (gid_t id0, gid_t id1, gid_t id2, int flags);
int (*task_setpgid) (struct task_struct *p, pid_t pgid);
int (*task_getpgid) (struct task_struct *p);
@@ -1433,8 +1418,7 @@ struct security_operations {
int (*task_wait) (struct task_struct *p);
int (*task_prctl) (int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
- unsigned long arg5, long *rc_p);
- void (*task_reparent_to_init) (struct task_struct *p);
+ unsigned long arg5);
void (*task_to_inode) (struct task_struct *p, struct inode *inode);
int (*ipc_permission) (struct kern_ipc_perm *ipcp, short flag);
@@ -1539,10 +1523,10 @@ struct security_operations {
/* key management security hooks */
#ifdef CONFIG_KEYS
- int (*key_alloc) (struct key *key, struct task_struct *tsk, unsigned long flags);
+ int (*key_alloc) (struct key *key, const struct cred *cred, unsigned long flags);
void (*key_free) (struct key *key);
int (*key_permission) (key_ref_t key_ref,
- struct task_struct *context,
+ const struct cred *cred,
key_perm_t perm);
int (*key_getsecurity)(struct key *key, char **_buffer);
#endif /* CONFIG_KEYS */
@@ -1568,15 +1552,12 @@ int security_capget(struct task_struct *target,
kernel_cap_t *effective,
kernel_cap_t *inheritable,
kernel_cap_t *permitted);
-int security_capset_check(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted);
-void security_capset_set(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted);
+int security_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted);
int security_capable(struct task_struct *tsk, int cap);
+int security_capable_noaudit(struct task_struct *tsk, int cap);
int security_acct(struct file *file);
int security_sysctl(struct ctl_table *table, int op);
int security_quotactl(int cmds, int type, int id, struct super_block *sb);
@@ -1586,17 +1567,15 @@ int security_settime(struct timespec *ts, struct timezone *tz);
int security_vm_enough_memory(long pages);
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
int security_vm_enough_memory_kern(long pages);
-int security_bprm_alloc(struct linux_binprm *bprm);
-void security_bprm_free(struct linux_binprm *bprm);
-void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
-void security_bprm_post_apply_creds(struct linux_binprm *bprm);
-int security_bprm_set(struct linux_binprm *bprm);
+int security_bprm_set_creds(struct linux_binprm *bprm);
int security_bprm_check(struct linux_binprm *bprm);
+void security_bprm_committing_creds(struct linux_binprm *bprm);
+void security_bprm_committed_creds(struct linux_binprm *bprm);
int security_bprm_secureexec(struct linux_binprm *bprm);
int security_sb_alloc(struct super_block *sb);
void security_sb_free(struct super_block *sb);
int security_sb_copy_data(char *orig, char *copy);
-int security_sb_kern_mount(struct super_block *sb, void *data);
+int security_sb_kern_mount(struct super_block *sb, int flags, void *data);
int security_sb_show_options(struct seq_file *m, struct super_block *sb);
int security_sb_statfs(struct dentry *dentry);
int security_sb_mount(char *dev_name, struct path *path,
@@ -1663,13 +1642,16 @@ int security_file_set_fowner(struct file *file);
int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
-int security_dentry_open(struct file *file);
+int security_dentry_open(struct file *file, const struct cred *cred);
int security_task_create(unsigned long clone_flags);
-int security_task_alloc(struct task_struct *p);
-void security_task_free(struct task_struct *p);
+void security_cred_free(struct cred *cred);
+int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+void security_commit_creds(struct cred *new, const struct cred *old);
+int security_kernel_act_as(struct cred *new, u32 secid);
+int security_kernel_create_files_as(struct cred *new, struct inode *inode);
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags);
-int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
- uid_t old_suid, int flags);
+int security_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags);
int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags);
int security_task_setpgid(struct task_struct *p, pid_t pgid);
int security_task_getpgid(struct task_struct *p);
@@ -1688,8 +1670,7 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
int sig, u32 secid);
int security_task_wait(struct task_struct *p);
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p);
-void security_task_reparent_to_init(struct task_struct *p);
+ unsigned long arg4, unsigned long arg5);
void security_task_to_inode(struct task_struct *p, struct inode *inode);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
@@ -1764,25 +1745,23 @@ static inline int security_capget(struct task_struct *target,
return cap_capget(target, effective, inheritable, permitted);
}
-static inline int security_capset_check(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
+static inline int security_capset(struct cred *new,
+ const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
- return cap_capset_check(target, effective, inheritable, permitted);
+ return cap_capset(new, old, effective, inheritable, permitted);
}
-static inline void security_capset_set(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
+static inline int security_capable(struct task_struct *tsk, int cap)
{
- cap_capset_set(target, effective, inheritable, permitted);
+ return cap_capable(tsk, cap, SECURITY_CAP_AUDIT);
}
-static inline int security_capable(struct task_struct *tsk, int cap)
+static inline int security_capable_noaudit(struct task_struct *tsk, int cap)
{
- return cap_capable(tsk, cap);
+ return cap_capable(tsk, cap, SECURITY_CAP_NOAUDIT);
}
static inline int security_acct(struct file *file)
@@ -1835,32 +1814,22 @@ static inline int security_vm_enough_memory_kern(long pages)
return cap_vm_enough_memory(current->mm, pages);
}
-static inline int security_bprm_alloc(struct linux_binprm *bprm)
-{
- return 0;
-}
-
-static inline void security_bprm_free(struct linux_binprm *bprm)
-{ }
-
-static inline void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+static inline int security_bprm_set_creds(struct linux_binprm *bprm)
{
- cap_bprm_apply_creds(bprm, unsafe);
+ return cap_bprm_set_creds(bprm);
}
-static inline void security_bprm_post_apply_creds(struct linux_binprm *bprm)
+static inline int security_bprm_check(struct linux_binprm *bprm)
{
- return;
+ return 0;
}
-static inline int security_bprm_set(struct linux_binprm *bprm)
+static inline void security_bprm_committing_creds(struct linux_binprm *bprm)
{
- return cap_bprm_set_security(bprm);
}
-static inline int security_bprm_check(struct linux_binprm *bprm)
+static inline void security_bprm_committed_creds(struct linux_binprm *bprm)
{
- return 0;
}
static inline int security_bprm_secureexec(struct linux_binprm *bprm)
@@ -1881,7 +1850,7 @@ static inline int security_sb_copy_data(char *orig, char *copy)
return 0;
}
-static inline int security_sb_kern_mount(struct super_block *sb, void *data)
+static inline int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
return 0;
}
@@ -2177,7 +2146,8 @@ static inline int security_file_receive(struct file *file)
return 0;
}
-static inline int security_dentry_open(struct file *file)
+static inline int security_dentry_open(struct file *file,
+ const struct cred *cred)
{
return 0;
}
@@ -2187,13 +2157,31 @@ static inline int security_task_create(unsigned long clone_flags)
return 0;
}
-static inline int security_task_alloc(struct task_struct *p)
+static inline void security_cred_free(struct cred *cred)
+{ }
+
+static inline int security_prepare_creds(struct cred *new,
+ const struct cred *old,
+ gfp_t gfp)
{
return 0;
}
-static inline void security_task_free(struct task_struct *p)
-{ }
+static inline void security_commit_creds(struct cred *new,
+ const struct cred *old)
+{
+}
+
+static inline int security_kernel_act_as(struct cred *cred, u32 secid)
+{
+ return 0;
+}
+
+static inline int security_kernel_create_files_as(struct cred *cred,
+ struct inode *inode)
+{
+ return 0;
+}
static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
int flags)
@@ -2201,10 +2189,11 @@ static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
return 0;
}
-static inline int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
- uid_t old_suid, int flags)
+static inline int security_task_fix_setuid(struct cred *new,
+ const struct cred *old,
+ int flags)
{
- return cap_task_post_setuid(old_ruid, old_euid, old_suid, flags);
+ return cap_task_fix_setuid(new, old, flags);
}
static inline int security_task_setgid(gid_t id0, gid_t id1, gid_t id2,
@@ -2291,14 +2280,9 @@ static inline int security_task_wait(struct task_struct *p)
static inline int security_task_prctl(int option, unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
- unsigned long arg5, long *rc_p)
-{
- return cap_task_prctl(option, arg2, arg3, arg3, arg5, rc_p);
-}
-
-static inline void security_task_reparent_to_init(struct task_struct *p)
+ unsigned long arg5)
{
- cap_task_reparent_to_init(p);
+ return cap_task_prctl(option, arg2, arg3, arg3, arg5);
}
static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -2724,16 +2708,16 @@ static inline void security_skb_classify_flow(struct sk_buff *skb, struct flowi
#ifdef CONFIG_KEYS
#ifdef CONFIG_SECURITY
-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags);
+int security_key_alloc(struct key *key, const struct cred *cred, unsigned long flags);
void security_key_free(struct key *key);
int security_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm);
+ const struct cred *cred, key_perm_t perm);
int security_key_getsecurity(struct key *key, char **_buffer);
#else
static inline int security_key_alloc(struct key *key,
- struct task_struct *tsk,
+ const struct cred *cred,
unsigned long flags)
{
return 0;
@@ -2744,7 +2728,7 @@ static inline void security_key_free(struct key *key)
}
static inline int security_key_permission(key_ref_t key_ref,
- struct task_struct *context,
+ const struct cred *cred,
key_perm_t perm)
{
return 0;
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h
index b3dfa72f13b9..40ea5058c2ec 100644
--- a/include/linux/seq_file.h
+++ b/include/linux/seq_file.h
@@ -50,10 +50,11 @@ int seq_path(struct seq_file *, struct path *, char *);
int seq_dentry(struct seq_file *, struct dentry *, char *);
int seq_path_root(struct seq_file *m, struct path *path, struct path *root,
char *esc);
-int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits);
-static inline int seq_cpumask(struct seq_file *m, cpumask_t *mask)
+int seq_bitmap(struct seq_file *m, const unsigned long *bits,
+ unsigned int nr_bits);
+static inline int seq_cpumask(struct seq_file *m, const struct cpumask *mask)
{
- return seq_bitmap(m, mask->bits, NR_CPUS);
+ return seq_bitmap(m, mask->bits, nr_cpu_ids);
}
static inline int seq_nodemask(struct seq_file *m, nodemask_t *mask)
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 4e4f1277f3bf..feb3b939ec4b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -158,6 +158,8 @@
/* SH-SCI */
#define PORT_SCIFA 83
+#define PORT_S3C6400 84
+
#ifdef __KERNEL__
#include <linux/compiler.h>
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 2725f4e5a9bf..cf2cb50f77d1 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -250,6 +250,9 @@ typedef unsigned char *sk_buff_data_t;
* @tc_verd: traffic control verdict
* @ndisc_nodetype: router type (from link layer)
* @do_not_encrypt: set to prevent encryption of this frame
+ * @requeue: set to indicate that the wireless core should attempt
+ * a software retry on this frame if we failed to
+ * receive an ACK for it
* @dma_cookie: a cookie to one of several possible DMA operations
* done by skb DMA functions
* @secmark: security marking
@@ -269,8 +272,9 @@ struct sk_buff {
struct dst_entry *dst;
struct rtable *rtable;
};
+#ifdef CONFIG_XFRM
struct sec_path *sp;
-
+#endif
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
@@ -325,6 +329,7 @@ struct sk_buff {
#endif
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
__u8 do_not_encrypt:1;
+ __u8 requeue:1;
#endif
/* 0/13/14 bit hole */
@@ -488,6 +493,19 @@ static inline bool skb_queue_is_last(const struct sk_buff_head *list,
}
/**
+ * skb_queue_is_first - check if skb is the first entry in the queue
+ * @list: queue head
+ * @skb: buffer
+ *
+ * Returns true if @skb is the first buffer on the list.
+ */
+static inline bool skb_queue_is_first(const struct sk_buff_head *list,
+ const struct sk_buff *skb)
+{
+ return (skb->prev == (struct sk_buff *) list);
+}
+
+/**
* skb_queue_next - return the next packet in the queue
* @list: queue head
* @skb: current buffer
@@ -506,6 +524,24 @@ static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list,
}
/**
+ * skb_queue_prev - return the prev packet in the queue
+ * @list: queue head
+ * @skb: current buffer
+ *
+ * Return the prev packet in @list before @skb. It is only valid to
+ * call this if skb_queue_is_first() evaluates to false.
+ */
+static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list,
+ const struct sk_buff *skb)
+{
+ /* This BUG_ON may seem severe, but if we just return then we
+ * are going to dereference garbage.
+ */
+ BUG_ON(skb_queue_is_first(list, skb));
+ return skb->prev;
+}
+
+/**
* skb_get - reference buffer
* @skb: buffer to reference
*
@@ -1647,8 +1683,12 @@ extern int skb_splice_bits(struct sk_buff *skb,
extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
extern void skb_split(struct sk_buff *skb,
struct sk_buff *skb1, const u32 len);
+extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb,
+ int shiftlen);
extern struct sk_buff *skb_segment(struct sk_buff *skb, int features);
+extern int skb_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb);
static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
int len, void *buffer)
@@ -1864,6 +1904,18 @@ static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_bu
to->queue_mapping = from->queue_mapping;
}
+#ifdef CONFIG_XFRM
+static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
+{
+ return skb->sp;
+}
+#else
+static inline struct sec_path *skb_sec_path(struct sk_buff *skb)
+{
+ return NULL;
+}
+#endif
+
static inline int skb_is_gso(const struct sk_buff *skb)
{
return skb_shinfo(skb)->gso_size;
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 000da12b5cf0..f96d13c281e8 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -253,9 +253,9 @@ static inline void *kmem_cache_alloc_node(struct kmem_cache *cachep,
* request comes from.
*/
#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB)
-extern void *__kmalloc_track_caller(size_t, gfp_t, void*);
+extern void *__kmalloc_track_caller(size_t, gfp_t, unsigned long);
#define kmalloc_track_caller(size, flags) \
- __kmalloc_track_caller(size, flags, __builtin_return_address(0))
+ __kmalloc_track_caller(size, flags, _RET_IP_)
#else
#define kmalloc_track_caller(size, flags) \
__kmalloc(size, flags)
@@ -271,10 +271,10 @@ extern void *__kmalloc_track_caller(size_t, gfp_t, void*);
* allocation request comes from.
*/
#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB)
-extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, void *);
+extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, unsigned long);
#define kmalloc_node_track_caller(size, flags, node) \
__kmalloc_node_track_caller(size, flags, node, \
- __builtin_return_address(0))
+ _RET_IP_)
#else
#define kmalloc_node_track_caller(size, flags, node) \
__kmalloc_node(size, flags, node)
@@ -285,7 +285,7 @@ extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, void *);
#define kmalloc_node_track_caller(size, flags, node) \
kmalloc_track_caller(size, flags)
-#endif /* DEBUG_SLAB */
+#endif /* CONFIG_NUMA */
/*
* Shortcuts
diff --git a/include/linux/smp.h b/include/linux/smp.h
index 6e7ba16ff454..b82466968101 100644
--- a/include/linux/smp.h
+++ b/include/linux/smp.h
@@ -21,6 +21,9 @@ struct call_single_data {
u16 priv;
};
+/* total number of cpus in this system (may exceed NR_CPUS) */
+extern unsigned int total_cpus;
+
#ifdef CONFIG_SMP
#include <linux/preempt.h>
@@ -64,15 +67,16 @@ extern void smp_cpus_done(unsigned int max_cpus);
* Call a function on all other processors
*/
int smp_call_function(void(*func)(void *info), void *info, int wait);
-/* Deprecated: use smp_call_function_many() which uses a cpumask ptr. */
-int smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info,
- int wait);
+void smp_call_function_many(const struct cpumask *mask,
+ void (*func)(void *info), void *info, bool wait);
-static inline void smp_call_function_many(const struct cpumask *mask,
- void (*func)(void *info), void *info,
- int wait)
+/* Deprecated: Use smp_call_function_many which takes a pointer to the mask. */
+static inline int
+smp_call_function_mask(cpumask_t mask, void(*func)(void *info), void *info,
+ int wait)
{
- smp_call_function_mask(*mask, func, info, wait);
+ smp_call_function_many(&mask, func, info, wait);
+ return 0;
}
int smp_call_function_single(int cpuid, void (*func) (void *info), void *info,
diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h
new file mode 100644
index 000000000000..1cbf0313adde
--- /dev/null
+++ b/include/linux/smsc911x.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * This program is free software; you can redistribute 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 __LINUX_SMSC911X_H__
+#define __LINUX_SMSC911X_H__
+
+#include <linux/phy.h>
+
+/* platform_device configuration data, should be assigned to
+ * the platform_device's dev.platform_data */
+struct smsc911x_platform_config {
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+ unsigned int flags;
+ phy_interface_t phy_interface;
+};
+
+/* Constants for platform_device irq polarity configuration */
+#define SMSC911X_IRQ_POLARITY_ACTIVE_LOW 0
+#define SMSC911X_IRQ_POLARITY_ACTIVE_HIGH 1
+
+/* Constants for platform_device irq type configuration */
+#define SMSC911X_IRQ_TYPE_OPEN_DRAIN 0
+#define SMSC911X_IRQ_TYPE_PUSH_PULL 1
+
+/* Constants for flags */
+#define SMSC911X_USE_16BIT (BIT(0))
+#define SMSC911X_USE_32BIT (BIT(1))
+
+#endif /* __LINUX_SMSC911X_H__ */
diff --git a/include/linux/snmp.h b/include/linux/snmp.h
index 7a6e6bba4a71..aee3f1e1d1ce 100644
--- a/include/linux/snmp.h
+++ b/include/linux/snmp.h
@@ -216,6 +216,9 @@ enum
LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */
LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */
LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */
+ LINUX_MIB_SACKSHIFTED,
+ LINUX_MIB_SACKMERGED,
+ LINUX_MIB_SACKSHIFTFALLBACK,
__LINUX_MIB_MAX
};
diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h
index faf1519b5adc..74d59a641362 100644
--- a/include/linux/stop_machine.h
+++ b/include/linux/stop_machine.h
@@ -23,7 +23,7 @@
*
* This can be thought of as a very heavy write lock, equivalent to
* grabbing every spinlock in the kernel. */
-int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus);
+int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
/**
* __stop_machine: freeze the machine on all CPUs and run this function
@@ -34,11 +34,11 @@ int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus);
* Description: This is a special version of the above, which assumes cpus
* won't come or go while it's being called. Used by hotplug cpu.
*/
-int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus);
+int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus);
#else
static inline int stop_machine(int (*fn)(void *), void *data,
- const cpumask_t *cpus)
+ const struct cpumask *cpus)
{
int ret;
local_irq_disable();
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 6f0ee1b84a4f..c39a21040dcb 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -58,6 +58,7 @@ struct rpc_clnt {
struct rpc_timeout cl_timeout_default;
struct rpc_program * cl_program;
char cl_inline_name[32];
+ char *cl_principal; /* target to authenticate to */
};
/*
@@ -108,6 +109,7 @@ struct rpc_create_args {
u32 version;
rpc_authflavor_t authflavor;
unsigned long flags;
+ char *client_name;
};
/* Values for "flags" field */
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index 51b977a4ca20..cea764c2359f 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -15,6 +15,7 @@ struct rpc_pipe_ops {
ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t);
ssize_t (*downcall)(struct file *, const char __user *, size_t);
void (*release_pipe)(struct inode *);
+ int (*open_pipe)(struct inode *);
void (*destroy_msg)(struct rpc_pipe_msg *);
};
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index 6fd7b016517f..0127daca4354 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -139,14 +139,14 @@ static inline char *__svc_print_addr(struct sockaddr *addr,
{
switch (addr->sa_family) {
case AF_INET:
- snprintf(buf, len, "%u.%u.%u.%u, port=%u",
- NIPQUAD(((struct sockaddr_in *) addr)->sin_addr),
+ snprintf(buf, len, "%pI4, port=%u",
+ &((struct sockaddr_in *)addr)->sin_addr,
ntohs(((struct sockaddr_in *) addr)->sin_port));
break;
case AF_INET6:
- snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x, port=%u",
- NIP6(((struct sockaddr_in6 *) addr)->sin6_addr),
+ snprintf(buf, len, "%pI6, port=%u",
+ &((struct sockaddr_in6 *)addr)->sin6_addr,
ntohs(((struct sockaddr_in6 *) addr)->sin6_port));
break;
diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h
index c9165d9771a8..ca7d725861fc 100644
--- a/include/linux/sunrpc/svcauth_gss.h
+++ b/include/linux/sunrpc/svcauth_gss.h
@@ -20,6 +20,7 @@ int gss_svc_init(void);
void gss_svc_shutdown(void);
int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
u32 svcauth_gss_flavor(struct auth_domain *dom);
+char *svc_gss_principal(struct svc_rqst *);
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index e4057d729f03..49e1eb454465 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -37,21 +37,6 @@ struct xdr_netobj {
typedef int (*kxdrproc_t)(void *rqstp, __be32 *data, void *obj);
/*
- * We're still requiring the BKL in the xdr code until it's been
- * more carefully audited, at which point this wrapper will become
- * unnecessary.
- */
-static inline int rpc_call_xdrproc(kxdrproc_t xdrproc, void *rqstp, __be32 *data, void *obj)
-{
- int ret;
-
- lock_kernel();
- ret = xdrproc(rqstp, data, obj);
- unlock_kernel();
- return ret;
-}
-
-/*
* Basic structure for transmission/reception of a client XDR message.
* Features a header (for a linear buffer containing RPC headers
* and the data payload for short messages), and then an array of
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 4d80a118d538..11fc71d50c1e 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -76,8 +76,7 @@ struct rpc_rqst {
struct list_head rq_list;
__u32 * rq_buffer; /* XDR encode buffer */
- size_t rq_bufsize,
- rq_callsize,
+ size_t rq_callsize,
rq_rcvsize;
struct xdr_buf rq_private_buf; /* The receive buffer
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index b18ec5533e8c..325af1de0351 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -7,9 +7,31 @@ struct device;
struct dma_attrs;
struct scatterlist;
+/*
+ * Maximum allowable number of contiguous slabs to map,
+ * must be a power of 2. What is the appropriate value ?
+ * The complexity of {map,unmap}_single is linearly dependent on this value.
+ */
+#define IO_TLB_SEGSIZE 128
+
+
+/*
+ * log of the size of each IO TLB slab. The number of slabs is command line
+ * controllable.
+ */
+#define IO_TLB_SHIFT 11
+
extern void
swiotlb_init(void);
+extern void *swiotlb_alloc_boot(size_t bytes, unsigned long nslabs);
+extern void *swiotlb_alloc(unsigned order, unsigned long nslabs);
+
+extern dma_addr_t swiotlb_phys_to_bus(phys_addr_t address);
+extern phys_addr_t swiotlb_bus_to_phys(dma_addr_t address);
+
+extern int swiotlb_arch_range_needs_mapping(void *ptr, size_t size);
+
extern void
*swiotlb_alloc_coherent(struct device *hwdev, size_t size,
dma_addr_t *dma_handle, gfp_t flags);
diff --git a/include/linux/threads.h b/include/linux/threads.h
index 38d1a5d6568e..052b12bec8bd 100644
--- a/include/linux/threads.h
+++ b/include/linux/threads.h
@@ -8,17 +8,17 @@
*/
/*
- * Maximum supported processors that can run under SMP. This value is
- * set via configure setting. The maximum is equal to the size of the
- * bitmasks used on that platform, i.e. 32 or 64. Setting this smaller
- * saves quite a bit of memory.
+ * Maximum supported processors. Setting this smaller saves quite a
+ * bit of memory. Use nr_cpu_ids instead of this except for static bitmaps.
*/
-#ifdef CONFIG_SMP
-#define NR_CPUS CONFIG_NR_CPUS
-#else
-#define NR_CPUS 1
+#ifndef CONFIG_NR_CPUS
+/* FIXME: This should be fixed in the arch's Kconfig */
+#define CONFIG_NR_CPUS 1
#endif
+/* Places which use this should consider cpumask_var_t. */
+#define NR_CPUS CONFIG_NR_CPUS
+
#define MIN_THREADS_LEFT_FOR_ROOT 4
/*
diff --git a/include/linux/tick.h b/include/linux/tick.h
index b6ec8189ac0c..469b82d88b3b 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -84,10 +84,10 @@ static inline void tick_cancel_sched_timer(int cpu) { }
# ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
extern struct tick_device *tick_get_broadcast_device(void);
-extern cpumask_t *tick_get_broadcast_mask(void);
+extern struct cpumask *tick_get_broadcast_mask(void);
# ifdef CONFIG_TICK_ONESHOT
-extern cpumask_t *tick_get_broadcast_oneshot_mask(void);
+extern struct cpumask *tick_get_broadcast_oneshot_mask(void);
# endif
# endif /* BROADCAST */
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 9007313b5b71..998a55d80acf 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -53,47 +53,11 @@
#ifndef _LINUX_TIMEX_H
#define _LINUX_TIMEX_H
-#include <linux/compiler.h>
#include <linux/time.h>
-#include <asm/param.h>
-
#define NTP_API 4 /* NTP API version */
/*
- * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
- * for a slightly underdamped convergence characteristic. SHIFT_KH
- * establishes the damping of the FLL and is chosen by wisdom and black
- * art.
- *
- * MAXTC establishes the maximum time constant of the PLL. With the
- * SHIFT_KG and SHIFT_KF values given and a time constant range from
- * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
- * respectively.
- */
-#define SHIFT_PLL 4 /* PLL frequency factor (shift) */
-#define SHIFT_FLL 2 /* FLL frequency factor (shift) */
-#define MAXTC 10 /* maximum time constant (shift) */
-
-/*
- * SHIFT_USEC defines the scaling (shift) of the time_freq and
- * time_tolerance variables, which represent the current frequency
- * offset and maximum frequency tolerance.
- */
-#define SHIFT_USEC 16 /* frequency offset scale (shift) */
-#define PPM_SCALE (NSEC_PER_USEC << (NTP_SCALE_SHIFT - SHIFT_USEC))
-#define PPM_SCALE_INV_SHIFT 19
-#define PPM_SCALE_INV ((1ll << (PPM_SCALE_INV_SHIFT + NTP_SCALE_SHIFT)) / \
- PPM_SCALE + 1)
-
-#define MAXPHASE 500000000l /* max phase error (ns) */
-#define MAXFREQ 500000 /* max frequency error (ns/s) */
-#define MAXFREQ_SCALED ((s64)MAXFREQ << NTP_SCALE_SHIFT)
-#define MINSEC 256 /* min interval between updates (s) */
-#define MAXSEC 2048 /* max interval between updates (s) */
-#define NTP_PHASE_LIMIT ((MAXPHASE / NSEC_PER_USEC) << 5) /* beyond max. dispersion */
-
-/*
* syscall interface - used (mainly by NTP daemon)
* to discipline kernel clock oscillator
*/
@@ -199,9 +163,46 @@ struct timex {
#define TIME_BAD TIME_ERROR /* bw compat */
#ifdef __KERNEL__
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/param.h>
+
#include <asm/timex.h>
/*
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic. SHIFT_KH
+ * establishes the damping of the FLL and is chosen by wisdom and black
+ * art.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_PLL 4 /* PLL frequency factor (shift) */
+#define SHIFT_FLL 2 /* FLL frequency factor (shift) */
+#define MAXTC 10 /* maximum time constant (shift) */
+
+/*
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define PPM_SCALE (NSEC_PER_USEC << (NTP_SCALE_SHIFT - SHIFT_USEC))
+#define PPM_SCALE_INV_SHIFT 19
+#define PPM_SCALE_INV ((1ll << (PPM_SCALE_INV_SHIFT + NTP_SCALE_SHIFT)) / \
+ PPM_SCALE + 1)
+
+#define MAXPHASE 500000000l /* max phase error (ns) */
+#define MAXFREQ 500000 /* max frequency error (ns/s) */
+#define MAXFREQ_SCALED ((s64)MAXFREQ << NTP_SCALE_SHIFT)
+#define MINSEC 256 /* min interval between updates (s) */
+#define MAXSEC 2048 /* max interval between updates (s) */
+#define NTP_PHASE_LIMIT ((MAXPHASE / NSEC_PER_USEC) << 5) /* beyond max. dispersion */
+
+/*
* kernel variables
* Note: maximum error = NTP synch distance = dispersion + delay / 2;
* estimated error = NTP dispersion.
diff --git a/include/linux/tty.h b/include/linux/tty.h
index eaec37c9d83d..3f4954c55e53 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -442,6 +442,7 @@ extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data,
size_t size);
extern void tty_audit_exit(void);
extern void tty_audit_fork(struct signal_struct *sig);
+extern void tty_audit_tiocsti(struct tty_struct *tty, char ch);
extern void tty_audit_push(struct tty_struct *tty);
extern void tty_audit_push_task(struct task_struct *tsk,
uid_t loginuid, u32 sessionid);
@@ -450,6 +451,9 @@ static inline void tty_audit_add_data(struct tty_struct *tty,
unsigned char *data, size_t size)
{
}
+static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch)
+{
+}
static inline void tty_audit_exit(void)
{
}
diff --git a/include/linux/types.h b/include/linux/types.h
index 1d98330b1f2c..121f349cb7ec 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -135,19 +135,14 @@ typedef __s64 int64_t;
*
* Linux always considers sectors to be 512 bytes long independently
* of the devices real block size.
+ *
+ * blkcnt_t is the type of the inode's block count.
*/
#ifdef CONFIG_LBD
typedef u64 sector_t;
-#else
-typedef unsigned long sector_t;
-#endif
-
-/*
- * The type of the inode's block count.
- */
-#ifdef CONFIG_LSF
typedef u64 blkcnt_t;
#else
+typedef unsigned long sector_t;
typedef unsigned long blkcnt_t;
#endif
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index b5f41d4c2eec..315bcd375224 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -12,7 +12,7 @@
struct user_namespace {
struct kref kref;
struct hlist_head uidhash_table[UIDHASH_SZ];
- struct user_struct *root_user;
+ struct user_struct *creator;
};
extern struct user_namespace init_user_ns;
@@ -26,8 +26,7 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
return ns;
}
-extern struct user_namespace *copy_user_ns(int flags,
- struct user_namespace *old_ns);
+extern int create_user_ns(struct cred *new);
extern void free_user_ns(struct kref *kref);
static inline void put_user_ns(struct user_namespace *ns)
@@ -43,13 +42,9 @@ static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
return &init_user_ns;
}
-static inline struct user_namespace *copy_user_ns(int flags,
- struct user_namespace *old_ns)
+static inline int create_user_ns(struct cred *new)
{
- if (flags & CLONE_NEWUSER)
- return ERR_PTR(-EINVAL);
-
- return old_ns;
+ return -EINVAL;
}
static inline void put_user_ns(struct user_namespace *ns)
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 4669d7e72e75..1f126e30766c 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -293,6 +293,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */
+#define V4L2_PIX_FMT_VYUY v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16 YVU422 planar */
#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16 YVU411 planar */
#define V4L2_PIX_FMT_Y41P v4l2_fourcc('Y', '4', '1', 'P') /* 12 YUV 4:1:1 */
@@ -304,6 +305,8 @@ struct v4l2_pix_format {
/* two planes -- one Y, one Cr + Cb interleaved */
#define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
#define V4L2_PIX_FMT_NV21 v4l2_fourcc('N', 'V', '2', '1') /* 12 Y/CrCb 4:2:0 */
+#define V4L2_PIX_FMT_NV16 v4l2_fourcc('N', 'V', '1', '6') /* 16 Y/CbCr 4:2:2 */
+#define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */
/* The following formats are not defined in the V4L2 specification */
#define V4L2_PIX_FMT_YUV410 v4l2_fourcc('Y', 'U', 'V', '9') /* 9 YUV 4:1:0 */
@@ -1050,7 +1053,7 @@ enum v4l2_mpeg_video_bitrate_mode {
#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210)
#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211)
-/* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */
+/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
#define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000)
#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0)
enum v4l2_mpeg_cx2341x_video_spatial_filter_mode {
@@ -1117,6 +1120,12 @@ enum v4l2_exposure_auto_type {
#define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+11)
#define V4L2_CID_FOCUS_AUTO (V4L2_CID_CAMERA_CLASS_BASE+12)
+#define V4L2_CID_ZOOM_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+13)
+#define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14)
+#define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15)
+
+#define V4L2_CID_PRIVACY (V4L2_CID_CAMERA_CLASS_BASE+16)
+
/*
* T U N I N G
*/
@@ -1369,6 +1378,7 @@ struct v4l2_streamparm {
#define V4L2_CHIP_MATCH_HOST 0 /* Match against chip ID on host (0 for the host) */
#define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver ID */
#define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */
+#define V4L2_CHIP_MATCH_AC97 3 /* Match against anciliary AC97 chip */
struct v4l2_register {
__u32 match_type; /* Match type */
@@ -1458,6 +1468,8 @@ struct v4l2_chip_ident {
#define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident)
#endif
#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek)
+/* Reminder: when adding new ioctls please add support for them to
+ drivers/media/video/v4l2-compat-ioctl32.c as well! */
#ifdef __OLD_VIDIOC_
/* for compatibility, will go away some day */
diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h
index c30c7bfbf39b..8726ff77763e 100644
--- a/include/linux/virtio_balloon.h
+++ b/include/linux/virtio_balloon.h
@@ -10,6 +10,9 @@
/* The feature bitmap for virtio balloon */
#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
+/* Size of a PFN in the balloon interface. */
+#define VIRTIO_BALLOON_PFN_SHIFT 12
+
struct virtio_balloon_config
{
/* Number of pages host wants Guest to give up. */
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h
index 19a0da0dba41..7615ffcdd555 100644
--- a/include/linux/virtio_console.h
+++ b/include/linux/virtio_console.h
@@ -7,6 +7,17 @@
/* The ID for virtio console */
#define VIRTIO_ID_CONSOLE 3
+/* Feature bits */
+#define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */
+
+struct virtio_console_config {
+ /* colums of the screens */
+ __u16 cols;
+ /* rows of the screens */
+ __u16 rows;
+} __attribute__((packed));
+
+
#ifdef __KERNEL__
int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int));
#endif /* __KERNEL__ */
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 5e33761b9b8a..5cdd0aa8bde9 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -20,6 +20,7 @@
#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */
#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */
#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */
+#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
struct virtio_net_config
{
@@ -44,4 +45,12 @@ struct virtio_net_hdr
__u16 csum_start; /* Position to start checksumming from */
__u16 csum_offset; /* Offset after that to place checksum */
};
+
+/* This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated. */
+struct virtio_net_hdr_mrg_rxbuf {
+ struct virtio_net_hdr hdr;
+ __u16 num_buffers; /* Number of merged rx buffers */
+};
+
#endif /* _LINUX_VIRTIO_NET_H */
diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h
index cdef35742932..cd0fd5d181a6 100644
--- a/include/linux/virtio_pci.h
+++ b/include/linux/virtio_pci.h
@@ -53,4 +53,12 @@
/* Virtio ABI version, this must match exactly */
#define VIRTIO_PCI_ABI_VERSION 0
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size. */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12
+
+/* The alignment to use between consumer and producer parts of vring.
+ * x86 pagesize again. */
+#define VIRTIO_PCI_VRING_ALIGN 4096
#endif
diff --git a/include/linux/virtio_ring.h b/include/linux/virtio_ring.h
index c4a598fb3826..71e03722fb59 100644
--- a/include/linux/virtio_ring.h
+++ b/include/linux/virtio_ring.h
@@ -83,7 +83,7 @@ struct vring {
* __u16 avail_idx;
* __u16 available[num];
*
- * // Padding to the next page boundary.
+ * // Padding to the next align boundary.
* char pad[];
*
* // A ring of used descriptor heads with free-running index.
@@ -93,19 +93,19 @@ struct vring {
* };
*/
static inline void vring_init(struct vring *vr, unsigned int num, void *p,
- unsigned long pagesize)
+ unsigned long align)
{
vr->num = num;
vr->desc = p;
vr->avail = p + num*sizeof(struct vring_desc);
- vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + pagesize-1)
- & ~(pagesize - 1));
+ vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1)
+ & ~(align - 1));
}
-static inline unsigned vring_size(unsigned int num, unsigned long pagesize)
+static inline unsigned vring_size(unsigned int num, unsigned long align)
{
return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (2 + num)
- + pagesize - 1) & ~(pagesize - 1))
+ + align - 1) & ~(align - 1))
+ sizeof(__u16) * 2 + sizeof(struct vring_used_elem) * num;
}
@@ -115,6 +115,7 @@ struct virtio_device;
struct virtqueue;
struct virtqueue *vring_new_virtqueue(unsigned int num,
+ unsigned int vring_align,
struct virtio_device *vdev,
void *pages,
void (*notify)(struct virtqueue *vq),
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 4bc1e6b86cb2..52f3abd453a1 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -199,6 +199,9 @@ enum {
#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO
XFRM_MSG_GETSPDINFO,
#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO
+
+ XFRM_MSG_MAPPING,
+#define XFRM_MSG_MAPPING XFRM_MSG_MAPPING
__XFRM_MSG_MAX
};
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -438,6 +441,15 @@ struct xfrm_user_migrate {
__u16 new_family;
};
+struct xfrm_user_mapping {
+ struct xfrm_usersa_id id;
+ __u32 reqid;
+ xfrm_address_t old_saddr;
+ xfrm_address_t new_saddr;
+ __be16 old_sport;
+ __be16 new_sport;
+};
+
#ifndef __KERNEL__
/* backwards compatibility for userspace */
#define XFRMGRP_ACQUIRE 1
@@ -464,6 +476,8 @@ enum xfrm_nlgroups {
#define XFRMNLGRP_REPORT XFRMNLGRP_REPORT
XFRMNLGRP_MIGRATE,
#define XFRMNLGRP_MIGRATE XFRMNLGRP_MIGRATE
+ XFRMNLGRP_MAPPING,
+#define XFRMNLGRP_MAPPING XFRMNLGRP_MAPPING
__XFRMNLGRP_MAX
};
#define XFRMNLGRP_MAX (__XFRMNLGRP_MAX - 1)
diff --git a/include/media/i2c-addr.h b/include/media/i2c-addr.h
index e7ff44a35ca0..5d0f56054d26 100644
--- a/include/media/i2c-addr.h
+++ b/include/media/i2c-addr.h
@@ -12,8 +12,6 @@
/* bttv address list */
#define I2C_ADDR_TSA5522 0xc2
#define I2C_ADDR_TDA7432 0x8a
-#define I2C_ADDR_BT832_ALT1 0x88
-#define I2C_ADDR_BT832_ALT2 0x8a // alternate setting
#define I2C_ADDR_TDA8425 0x82
#define I2C_ADDR_TDA9840 0x84
#define I2C_ADDR_TDA9850 0xb6 /* also used by 9855,9873 */
diff --git a/include/media/ir-common.h b/include/media/ir-common.h
index 38f2d93c3957..5bf2ea00678c 100644
--- a/include/media/ir-common.h
+++ b/include/media/ir-common.h
@@ -157,6 +157,8 @@ extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE];
#endif
/*
diff --git a/include/media/ov772x.h b/include/media/ov772x.h
new file mode 100644
index 000000000000..e391d55edb95
--- /dev/null
+++ b/include/media/ov772x.h
@@ -0,0 +1,21 @@
+/* ov772x Camera
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.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 __OV772X_H__
+#define __OV772X_H__
+
+#include <media/soc_camera.h>
+
+struct ov772x_camera_info {
+ unsigned long buswidth;
+ struct soc_camera_link link;
+};
+
+#endif /* __OV772X_H__ */
diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
index 1d104096619c..6bbb0d93bb5f 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/saa7146_vv.h
@@ -216,8 +216,7 @@ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data);
extern struct saa7146_use_ops saa7146_video_uops;
int saa7146_start_preview(struct saa7146_fh *fh);
int saa7146_stop_preview(struct saa7146_fh *fh);
-int saa7146_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg);
+int saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg);
/* from saa7146_vbi.c */
extern struct saa7146_use_ops saa7146_vbi_uops;
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index c5de7bb19fda..425b6a98c95c 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -12,9 +12,10 @@
#ifndef SOC_CAMERA_H
#define SOC_CAMERA_H
+#include <linux/mutex.h>
+#include <linux/pm.h>
#include <linux/videodev2.h>
#include <media/videobuf-core.h>
-#include <linux/pm.h>
struct soc_camera_device {
struct list_head list;
@@ -36,14 +37,19 @@ struct soc_camera_device {
unsigned char iface; /* Host number */
unsigned char devnum; /* Device number per host */
unsigned char buswidth; /* See comment in .c */
+ struct soc_camera_sense *sense; /* See comment in struct definition */
struct soc_camera_ops *ops;
struct video_device *vdev;
const struct soc_camera_data_format *current_fmt;
const struct soc_camera_data_format *formats;
int num_formats;
+ struct soc_camera_format_xlate *user_formats;
+ int num_user_formats;
struct module *owner;
- /* soc_camera.c private count. Only accessed with video_lock held */
+ void *host_priv; /* Per-device host private data */
+ /* soc_camera.c private count. Only accessed with .video_lock held */
int use_count;
+ struct mutex video_lock; /* Protects device data */
};
struct soc_camera_file {
@@ -56,7 +62,7 @@ struct soc_camera_host {
struct device dev;
unsigned char nr; /* Host number */
void *priv;
- char *drv_name;
+ const char *drv_name;
struct soc_camera_host_ops *ops;
};
@@ -64,25 +70,33 @@ struct soc_camera_host_ops {
struct module *owner;
int (*add)(struct soc_camera_device *);
void (*remove)(struct soc_camera_device *);
- int (*suspend)(struct soc_camera_device *, pm_message_t state);
+ int (*suspend)(struct soc_camera_device *, pm_message_t);
int (*resume)(struct soc_camera_device *);
- int (*set_fmt_cap)(struct soc_camera_device *, __u32,
- struct v4l2_rect *);
- int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *);
+ int (*get_formats)(struct soc_camera_device *, int,
+ struct soc_camera_format_xlate *);
+ int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *);
+ int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
void (*init_videobuf)(struct videobuf_queue *,
struct soc_camera_device *);
int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *);
int (*querycap)(struct soc_camera_host *, struct v4l2_capability *);
- int (*try_bus_param)(struct soc_camera_device *, __u32);
int (*set_bus_param)(struct soc_camera_device *, __u32);
unsigned int (*poll)(struct file *, poll_table *);
};
+#define SOCAM_SENSOR_INVERT_PCLK (1 << 0)
+#define SOCAM_SENSOR_INVERT_MCLK (1 << 1)
+#define SOCAM_SENSOR_INVERT_HSYNC (1 << 2)
+#define SOCAM_SENSOR_INVERT_VSYNC (1 << 3)
+#define SOCAM_SENSOR_INVERT_DATA (1 << 4)
+
struct soc_camera_link {
/* Camera bus id, used to match a camera and a bus */
int bus_id;
/* GPIO number to switch between 8 and 10 bit modes */
unsigned int gpio;
+ /* Per camera SOCAM_SENSOR_* bus flags */
+ unsigned long flags;
/* Optional callbacks to power on or off and reset the sensor */
int (*power)(struct device *, int);
int (*reset)(struct device *);
@@ -106,13 +120,35 @@ extern void soc_camera_device_unregister(struct soc_camera_device *icd);
extern int soc_camera_video_start(struct soc_camera_device *icd);
extern void soc_camera_video_stop(struct soc_camera_device *icd);
+extern const struct soc_camera_data_format *soc_camera_format_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc);
+extern const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc);
+
struct soc_camera_data_format {
- char *name;
+ const char *name;
unsigned int depth;
__u32 fourcc;
enum v4l2_colorspace colorspace;
};
+/**
+ * struct soc_camera_format_xlate - match between host and sensor formats
+ * @cam_fmt: sensor format provided by the sensor
+ * @host_fmt: host format after host translation from cam_fmt
+ * @buswidth: bus width for this format
+ *
+ * Host and sensor translation structure. Used in table of host and sensor
+ * formats matchings in soc_camera_device. A host can override the generic list
+ * generation by implementing get_formats(), and use it for format checks and
+ * format setup.
+ */
+struct soc_camera_format_xlate {
+ const struct soc_camera_data_format *cam_fmt;
+ const struct soc_camera_data_format *host_fmt;
+ unsigned char buswidth;
+};
+
struct soc_camera_ops {
struct module *owner;
int (*probe)(struct soc_camera_device *);
@@ -123,13 +159,14 @@ struct soc_camera_ops {
int (*release)(struct soc_camera_device *);
int (*start_capture)(struct soc_camera_device *);
int (*stop_capture)(struct soc_camera_device *);
- int (*set_fmt_cap)(struct soc_camera_device *, __u32,
- struct v4l2_rect *);
- int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *);
+ int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *);
+ int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
unsigned long (*query_bus_param)(struct soc_camera_device *);
int (*set_bus_param)(struct soc_camera_device *, unsigned long);
int (*get_chip_id)(struct soc_camera_device *,
struct v4l2_chip_ident *);
+ int (*set_std)(struct soc_camera_device *, v4l2_std_id *);
+ int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);
#ifdef CONFIG_VIDEO_ADV_DEBUG
int (*get_register)(struct soc_camera_device *, struct v4l2_register *);
int (*set_register)(struct soc_camera_device *, struct v4l2_register *);
@@ -140,6 +177,32 @@ struct soc_camera_ops {
int num_controls;
};
+#define SOCAM_SENSE_PCLK_CHANGED (1 << 0)
+
+/**
+ * This struct can be attached to struct soc_camera_device by the host driver
+ * to request sense from the camera, for example, when calling .set_fmt(). The
+ * host then can check which flags are set and verify respective values if any.
+ * For example, if SOCAM_SENSE_PCLK_CHANGED is set, it means, pixclock has
+ * changed during this operation. After completion the host should detach sense.
+ *
+ * @flags ored SOCAM_SENSE_* flags
+ * @master_clock if the host wants to be informed about pixel-clock
+ * change, it better set master_clock.
+ * @pixel_clock_max maximum pixel clock frequency supported by the host,
+ * camera is not allowed to exceed this.
+ * @pixel_clock if the camera driver changed pixel clock during this
+ * operation, it sets SOCAM_SENSE_PCLK_CHANGED, uses
+ * master_clock to calculate the new pixel-clock and
+ * sets this field.
+ */
+struct soc_camera_sense {
+ unsigned long flags;
+ unsigned long master_clock;
+ unsigned long pixel_clock_max;
+ unsigned long pixel_clock;
+};
+
static inline struct v4l2_queryctrl const *soc_camera_find_qctrl(
struct soc_camera_ops *ops, int id)
{
@@ -158,15 +221,20 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl(
#define SOCAM_HSYNC_ACTIVE_LOW (1 << 3)
#define SOCAM_VSYNC_ACTIVE_HIGH (1 << 4)
#define SOCAM_VSYNC_ACTIVE_LOW (1 << 5)
-#define SOCAM_DATAWIDTH_8 (1 << 6)
-#define SOCAM_DATAWIDTH_9 (1 << 7)
-#define SOCAM_DATAWIDTH_10 (1 << 8)
-#define SOCAM_DATAWIDTH_16 (1 << 9)
-#define SOCAM_PCLK_SAMPLE_RISING (1 << 10)
-#define SOCAM_PCLK_SAMPLE_FALLING (1 << 11)
+#define SOCAM_DATAWIDTH_4 (1 << 6)
+#define SOCAM_DATAWIDTH_8 (1 << 7)
+#define SOCAM_DATAWIDTH_9 (1 << 8)
+#define SOCAM_DATAWIDTH_10 (1 << 9)
+#define SOCAM_DATAWIDTH_15 (1 << 10)
+#define SOCAM_DATAWIDTH_16 (1 << 11)
+#define SOCAM_PCLK_SAMPLE_RISING (1 << 12)
+#define SOCAM_PCLK_SAMPLE_FALLING (1 << 13)
+#define SOCAM_DATA_ACTIVE_HIGH (1 << 14)
+#define SOCAM_DATA_ACTIVE_LOW (1 << 15)
-#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_9 | \
- SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_16)
+#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \
+ SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \
+ SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_16)
static inline unsigned long soc_camera_bus_param_compatible(
unsigned long camera_flags, unsigned long bus_flags)
@@ -182,4 +250,7 @@ static inline unsigned long soc_camera_bus_param_compatible(
return (!hsync || !vsync || !pclk) ? 0 : common_flags;
}
+extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
+ unsigned long flags);
+
#endif
diff --git a/include/media/tvp514x.h b/include/media/tvp514x.h
new file mode 100644
index 000000000000..5e7ee968c6dc
--- /dev/null
+++ b/include/media/tvp514x.h
@@ -0,0 +1,118 @@
+/*
+ * drivers/media/video/tvp514x.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 _TVP514X_H
+#define _TVP514X_H
+
+/*
+ * Other macros
+ */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+#define TVP514X_XCLK_BT656 (27000000)
+
+/* Number of pixels and number of lines per frame for different standards */
+#define NTSC_NUM_ACTIVE_PIXELS (720)
+#define NTSC_NUM_ACTIVE_LINES (480)
+#define PAL_NUM_ACTIVE_PIXELS (720)
+#define PAL_NUM_ACTIVE_LINES (576)
+
+/**
+ * enum tvp514x_input - enum for different decoder input pin
+ * configuration.
+ */
+enum tvp514x_input {
+ /*
+ * CVBS input selection
+ */
+ INPUT_CVBS_VI1A = 0x0,
+ INPUT_CVBS_VI1B,
+ INPUT_CVBS_VI1C,
+ INPUT_CVBS_VI2A = 0x04,
+ INPUT_CVBS_VI2B,
+ INPUT_CVBS_VI2C,
+ INPUT_CVBS_VI3A = 0x08,
+ INPUT_CVBS_VI3B,
+ INPUT_CVBS_VI3C,
+ INPUT_CVBS_VI4A = 0x0C,
+ /*
+ * S-Video input selection
+ */
+ INPUT_SVIDEO_VI2A_VI1A = 0x44,
+ INPUT_SVIDEO_VI2B_VI1B,
+ INPUT_SVIDEO_VI2C_VI1C,
+ INPUT_SVIDEO_VI2A_VI3A = 0x54,
+ INPUT_SVIDEO_VI2B_VI3B,
+ INPUT_SVIDEO_VI2C_VI3C,
+ INPUT_SVIDEO_VI4A_VI1A = 0x4C,
+ INPUT_SVIDEO_VI4A_VI1B,
+ INPUT_SVIDEO_VI4A_VI1C,
+ INPUT_SVIDEO_VI4A_VI3A = 0x5C,
+ INPUT_SVIDEO_VI4A_VI3B,
+ INPUT_SVIDEO_VI4A_VI3C,
+
+ /* Need to add entries for
+ * RGB, YPbPr and SCART.
+ */
+ INPUT_INVALID
+};
+
+/**
+ * enum tvp514x_output - enum for output format
+ * supported.
+ *
+ */
+enum tvp514x_output {
+ OUTPUT_10BIT_422_EMBEDDED_SYNC = 0,
+ OUTPUT_20BIT_422_SEPERATE_SYNC,
+ OUTPUT_10BIT_422_SEPERATE_SYNC = 3,
+ OUTPUT_INVALID
+};
+
+/**
+ * struct tvp514x_platform_data - Platform data values and access functions.
+ * @power_set: Power state access function, zero is off, non-zero is on.
+ * @ifparm: Interface parameters access function.
+ * @priv_data_set: Device private data (pointer) access function.
+ * @clk_polarity: Clock polarity of the current interface.
+ * @ hs_polarity: HSYNC Polarity configuration for current interface.
+ * @ vs_polarity: VSYNC Polarity configuration for current interface.
+ */
+struct tvp514x_platform_data {
+ char *master;
+ int (*power_set) (enum v4l2_power on);
+ int (*ifparm) (struct v4l2_ifparm *p);
+ int (*priv_data_set) (void *);
+ /* Interface control params */
+ bool clk_polarity;
+ bool hs_polarity;
+ bool vs_polarity;
+};
+
+
+#endif /* ifndef _TVP514X_H */
diff --git a/include/media/tw9910.h b/include/media/tw9910.h
new file mode 100644
index 000000000000..73231e7880d8
--- /dev/null
+++ b/include/media/tw9910.h
@@ -0,0 +1,39 @@
+/*
+ * tw9910 Driver header
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x.h
+ *
+ * Copyright (C) Kuninori Morimoto <morimoto.kuninori@renesas.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 __TW9910_H__
+#define __TW9910_H__
+
+#include <media/soc_camera.h>
+
+enum tw9910_mpout_pin {
+ TW9910_MPO_VLOSS,
+ TW9910_MPO_HLOCK,
+ TW9910_MPO_SLOCK,
+ TW9910_MPO_VLOCK,
+ TW9910_MPO_MONO,
+ TW9910_MPO_DET50,
+ TW9910_MPO_FIELD,
+ TW9910_MPO_RTCO,
+};
+
+struct tw9910_video_info {
+ unsigned long buswidth;
+ enum tw9910_mpout_pin mpout;
+ struct soc_camera_link link;
+};
+
+
+#endif /* __TW9910_H__ */
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index d73a8e9028a5..43dbb659f1f5 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -60,6 +60,8 @@ enum {
/* OmniVision sensors: reserved range 250-299 */
V4L2_IDENT_OV7670 = 250,
+ V4L2_IDENT_OV7720 = 251,
+ V4L2_IDENT_OV7725 = 252,
/* Conexant MPEG encoder/decoders: reserved range 410-420 */
V4L2_IDENT_CX23415 = 415,
@@ -69,6 +71,9 @@ enum {
/* module vp27smpx: just ident 2700 */
V4L2_IDENT_VP27SMPX = 2700,
+ /* module tvp5150 */
+ V4L2_IDENT_TVP5150 = 5150,
+
/* module cs5345: just ident 5345 */
V4L2_IDENT_CS5345 = 5345,
@@ -82,6 +87,9 @@ enum {
/* module wm8775: just ident 8775 */
V4L2_IDENT_WM8775 = 8775,
+ /* module tw9910: just ident 9910 */
+ V4L2_IDENT_TW9910 = 9910,
+
/* module cs53132a: just ident 53132 */
V4L2_IDENT_CS53l32A = 53132,
@@ -166,8 +174,10 @@ enum {
V4L2_IDENT_MT9M001C12ST = 45000,
V4L2_IDENT_MT9M001C12STM = 45005,
V4L2_IDENT_MT9M111 = 45007,
+ V4L2_IDENT_MT9M112 = 45008,
V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */
V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */
+ V4L2_IDENT_MT9T031 = 45020,
};
#endif
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 2f8719abf5cb..f99c866d8c37 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -57,6 +57,29 @@
/* ------------------------------------------------------------------------- */
+/* These printk constructs can be used with v4l2_device and v4l2_subdev */
+#define v4l2_printk(level, dev, fmt, arg...) \
+ printk(level "%s: " fmt, (dev)->name , ## arg)
+
+#define v4l2_err(dev, fmt, arg...) \
+ v4l2_printk(KERN_ERR, dev, fmt , ## arg)
+
+#define v4l2_warn(dev, fmt, arg...) \
+ v4l2_printk(KERN_WARNING, dev, fmt , ## arg)
+
+#define v4l2_info(dev, fmt, arg...) \
+ v4l2_printk(KERN_INFO, dev, fmt , ## arg)
+
+/* These three macros assume that the debug level is set with a module
+ parameter called 'debug'. */
+#define v4l2_dbg(level, debug, dev, fmt, arg...) \
+ do { \
+ if (debug >= (level)) \
+ v4l2_printk(KERN_DEBUG, dev, fmt , ## arg); \
+ } while (0)
+
+/* ------------------------------------------------------------------------- */
+
/* Priority helper functions */
struct v4l2_prio_state {
@@ -104,11 +127,29 @@ struct i2c_driver;
struct i2c_adapter;
struct i2c_client;
struct i2c_device_id;
+struct v4l2_device;
+struct v4l2_subdev;
+struct v4l2_subdev_ops;
int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver,
const char *name,
int (*probe)(struct i2c_client *, const struct i2c_device_id *));
+/* Load an i2c module and return an initialized v4l2_subdev struct.
+ Only call request_module if module_name != NULL.
+ The client_type argument is the name of the chip that's on the adapter. */
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type, u8 addr);
+/* Probe and load an i2c module and return an initialized v4l2_subdev struct.
+ Only call request_module if module_name != NULL.
+ The client_type argument is the name of the chip that's on the adapter. */
+struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type,
+ const unsigned short *addrs);
+/* Initialize an v4l2_subdev with data from an i2c_client struct */
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops);
+
/* ------------------------------------------------------------------------- */
/* Internal ioctls */
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index a0a6b41c5e09..0a88d1d17d30 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -25,6 +25,12 @@
#define VFL_TYPE_MAX 4
struct v4l2_ioctl_callbacks;
+struct v4l2_device;
+
+/* Flag to mark the video_device struct as unregistered.
+ Drivers can set this flag if they want to block all future
+ device access. It is set by video_unregister_device. */
+#define V4L2_FL_UNREGISTERED (0)
/*
* Newer version of video_device, handled by videodev2.c
@@ -39,15 +45,20 @@ struct video_device
/* sysfs */
struct device dev; /* v4l device */
- struct cdev cdev; /* character device */
- void (*cdev_release)(struct kobject *kobj);
+ struct cdev *cdev; /* character device */
+
+ /* Set either parent or v4l2_dev if your driver uses v4l2_device */
struct device *parent; /* device parent */
+ struct v4l2_device *v4l2_dev; /* v4l2_device parent */
/* device info */
char name[32];
int vfl_type;
+ /* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
+ /* use bitops to set/clear/test flags */
+ unsigned long flags;
/* attribute to differentiate multiple indices on one physical device */
int index;
@@ -58,7 +69,7 @@ struct video_device
v4l2_std_id current_norm; /* Current tvnorm */
/* callbacks */
- void (*release)(struct video_device *vfd);
+ void (*release)(struct video_device *vdev);
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
@@ -67,36 +78,41 @@ struct video_device
/* dev to video-device */
#define to_video_device(cd) container_of(cd, struct video_device, dev)
-/* Register and unregister devices. Note that if video_register_device fails,
+/* Register video devices. Note that if video_register_device fails,
the release() callback of the video_device structure is *not* called, so
the caller is responsible for freeing any data. Usually that means that
- you call video_device_release() on failure. */
-int __must_check video_register_device(struct video_device *vfd, int type, int nr);
-int __must_check video_register_device_index(struct video_device *vfd,
+ you call video_device_release() on failure.
+
+ Also note that vdev->minor is set to -1 if the registration failed. */
+int __must_check video_register_device(struct video_device *vdev, int type, int nr);
+int __must_check video_register_device_index(struct video_device *vdev,
int type, int nr, int index);
-void video_unregister_device(struct video_device *vfd);
+
+/* Unregister video devices. Will do nothing if vdev == NULL or
+ vdev->minor < 0. */
+void video_unregister_device(struct video_device *vdev);
/* helper functions to alloc/release struct video_device, the
latter can also be used for video_device->release(). */
struct video_device * __must_check video_device_alloc(void);
-/* this release function frees the vfd pointer */
-void video_device_release(struct video_device *vfd);
+/* this release function frees the vdev pointer */
+void video_device_release(struct video_device *vdev);
/* this release function does nothing, use when the video_device is a
static global struct. Note that having a static video_device is
a dubious construction at best. */
-void video_device_release_empty(struct video_device *vfd);
+void video_device_release_empty(struct video_device *vdev);
/* helper functions to access driver private data. */
-static inline void *video_get_drvdata(struct video_device *dev)
+static inline void *video_get_drvdata(struct video_device *vdev)
{
- return dev_get_drvdata(&dev->dev);
+ return dev_get_drvdata(&vdev->dev);
}
-static inline void video_set_drvdata(struct video_device *dev, void *data)
+static inline void video_set_drvdata(struct video_device *vdev, void *data)
{
- dev_set_drvdata(&dev->dev, data);
+ dev_set_drvdata(&vdev->dev, data);
}
struct video_device *video_devdata(struct file *file);
@@ -108,4 +124,9 @@ static inline void *video_drvdata(struct file *file)
return video_get_drvdata(video_devdata(file));
}
+static inline int video_is_unregistered(struct video_device *vdev)
+{
+ return test_bit(V4L2_FL_UNREGISTERED, &vdev->flags);
+}
+
#endif /* _V4L2_DEV_H */
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
new file mode 100644
index 000000000000..97b283a04289
--- /dev/null
+++ b/include/media/v4l2-device.h
@@ -0,0 +1,109 @@
+/*
+ V4L2 device support header.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute 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 _V4L2_DEVICE_H
+#define _V4L2_DEVICE_H
+
+#include <media/v4l2-subdev.h>
+
+/* Each instance of a V4L2 device should create the v4l2_device struct,
+ either stand-alone or embedded in a larger struct.
+
+ It allows easy access to sub-devices (see v4l2-subdev.h) and provides
+ basic V4L2 device-level support.
+ */
+
+#define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16)
+
+struct v4l2_device {
+ /* dev->driver_data points to this struct */
+ struct device *dev;
+ /* used to keep track of the registered subdevs */
+ struct list_head subdevs;
+ /* lock this struct; can be used by the driver as well if this
+ struct is embedded into a larger struct. */
+ spinlock_t lock;
+ /* unique device name, by default the driver name + bus ID */
+ char name[V4L2_DEVICE_NAME_SIZE];
+};
+
+/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */
+int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
+/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
+
+/* Register a subdev with a v4l2 device. While registered the subdev module
+ is marked as in-use. An error is returned if the module is no longer
+ loaded when you attempt to register it. */
+int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd);
+/* Unregister a subdev with a v4l2 device. Can also be called if the subdev
+ wasn't registered. In that case it will do nothing. */
+void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
+
+/* Iterate over all subdevs. */
+#define v4l2_device_for_each_subdev(sd, dev) \
+ list_for_each_entry(sd, &(dev)->subdevs, list)
+
+/* Call the specified callback for all subdevs matching the condition.
+ Ignore any errors. Note that you cannot add or delete a subdev
+ while walking the subdevs list. */
+#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \
+ do { \
+ struct v4l2_subdev *sd; \
+ \
+ list_for_each_entry(sd, &(dev)->subdevs, list) \
+ if ((cond) && sd->ops->o && sd->ops->o->f) \
+ sd->ops->o->f(sd , ##args); \
+ } while (0)
+
+/* Call the specified callback for all subdevs matching the condition.
+ If the callback returns an error other than 0 or -ENOIOCTLCMD, then
+ return with that error code. Note that you cannot add or delete a
+ subdev while walking the subdevs list. */
+#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \
+({ \
+ struct v4l2_subdev *sd; \
+ int err = 0; \
+ \
+ list_for_each_entry(sd, &(dev)->subdevs, list) { \
+ if ((cond) && sd->ops->o && sd->ops->o->f) \
+ err = sd->ops->o->f(sd , ##args); \
+ if (err && err != -ENOIOCTLCMD) \
+ break; \
+ } \
+ (err == -ENOIOCTLCMD) ? 0 : err; \
+})
+
+/* Call the specified callback for all subdevs matching grp_id (if 0, then
+ match them all). Ignore any errors. Note that you cannot add or delete
+ a subdev while walking the subdevs list. */
+#define v4l2_device_call_all(dev, grp_id, o, f, args...) \
+ __v4l2_device_call_subdevs(dev, \
+ !(grp_id) || sd->grp_id == (grp_id), o, f , ##args)
+
+/* Call the specified callback for all subdevs matching grp_id (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. Note that you cannot
+ add or delete a subdev while walking the subdevs list. */
+#define v4l2_device_call_until_err(dev, grp_id, o, f, args...) \
+ __v4l2_device_call_subdevs_until_err(dev, \
+ !(grp_id) || sd->grp_id == (grp_id), o, f , ##args)
+
+#endif
diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h
index 9c2df41dbf92..ecda3c725837 100644
--- a/include/media/v4l2-int-device.h
+++ b/include/media/v4l2-int-device.h
@@ -183,6 +183,9 @@ enum v4l2_int_ioctl_num {
vidioc_int_s_crop_num,
vidioc_int_g_parm_num,
vidioc_int_s_parm_num,
+ vidioc_int_querystd_num,
+ vidioc_int_s_std_num,
+ vidioc_int_s_video_routing_num,
/*
*
@@ -284,6 +287,9 @@ V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);
V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);
V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);
+V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);
+V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);
+V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *);
V4L2_INT_WRAPPER_0(dev_init);
V4L2_INT_WRAPPER_0(dev_exit);
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index e6ba25b3d7c8..fcdb58c4ce07 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -232,6 +232,12 @@ struct v4l2_ioctl_ops {
int (*vidioc_g_chip_ident) (struct file *file, void *fh,
struct v4l2_chip_ident *chip);
+ int (*vidioc_enum_framesizes) (struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize);
+
+ int (*vidioc_enum_frameintervals) (struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival);
+
/* For other private ioctls */
int (*vidioc_default) (struct file *file, void *fh,
int cmd, void *arg);
@@ -285,15 +291,13 @@ extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd,
unsigned long arg);
/* Include support for obsoleted stuff */
-extern int video_usercopy(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg,
- int (*func)(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg));
+extern int video_usercopy(struct file *file, unsigned int cmd,
+ unsigned long arg, v4l2_kioctl func);
/* Standard handlers for V4L ioctl's */
/* This prototype is used on fops.unlocked_ioctl */
-extern int __video_ioctl2(struct file *file,
+extern long __video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg);
/* This prototype is used on fops.ioctl
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
new file mode 100644
index 000000000000..ceef016bb0b7
--- /dev/null
+++ b/include/media/v4l2-subdev.h
@@ -0,0 +1,189 @@
+/*
+ V4L2 sub-device support header.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute 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 _V4L2_SUBDEV_H
+#define _V4L2_SUBDEV_H
+
+#include <media/v4l2-common.h>
+
+struct v4l2_device;
+struct v4l2_subdev;
+struct tuner_setup;
+
+/* Sub-devices are devices that are connected somehow to the main bridge
+ device. These devices are usually audio/video muxers/encoders/decoders or
+ sensors and webcam controllers.
+
+ Usually these devices are controlled through an i2c bus, but other busses
+ may also be used.
+
+ The v4l2_subdev struct provides a way of accessing these devices in a
+ generic manner. Most operations that these sub-devices support fall in
+ a few categories: core ops, audio ops, video ops and tuner ops.
+
+ More categories can be added if needed, although this should remain a
+ limited set (no more than approx. 8 categories).
+
+ Each category has its own set of ops that subdev drivers can implement.
+
+ A subdev driver can leave the pointer to the category ops NULL if
+ it does not implement them (e.g. an audio subdev will generally not
+ implement the video category ops). The exception is the core category:
+ this must always be present.
+
+ These ops are all used internally so it is no problem to change, remove
+ or add ops or move ops from one to another category. Currently these
+ ops are based on the original ioctls, but since ops are not limited to
+ one argument there is room for improvement here once all i2c subdev
+ drivers are converted to use these ops.
+ */
+
+/* Core ops: it is highly recommended to implement at least these ops:
+
+ g_chip_ident
+ log_status
+ g_register
+ s_register
+
+ This provides basic debugging support.
+
+ The ioctl ops is meant for generic ioctl-like commands. Depending on
+ the use-case it might be better to use subdev-specific ops (currently
+ not yet implemented) since ops provide proper type-checking.
+ */
+struct v4l2_subdev_core_ops {
+ int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip);
+ int (*log_status)(struct v4l2_subdev *sd);
+ int (*init)(struct v4l2_subdev *sd, u32 val);
+ int (*s_standby)(struct v4l2_subdev *sd, u32 standby);
+ int (*reset)(struct v4l2_subdev *sd, u32 val);
+ int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
+ int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+ int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+ int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+ int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+ int (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg);
+ int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg);
+#endif
+};
+
+struct v4l2_subdev_tuner_ops {
+ int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type);
+ int (*s_radio)(struct v4l2_subdev *sd);
+ int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
+ int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);
+ int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
+ int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
+ int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);
+ int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
+ int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
+};
+
+struct v4l2_subdev_audio_ops {
+ int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
+ int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);
+ int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route);
+};
+
+struct v4l2_subdev_video_ops {
+ int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route);
+ int (*s_crystal_freq)(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq);
+ int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);
+ int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);
+ int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
+ int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
+ int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
+ int (*s_stream)(struct v4l2_subdev *sd, int enable);
+ int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
+ int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
+};
+
+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;
+};
+
+#define V4L2_SUBDEV_NAME_SIZE 32
+
+/* Each instance of a subdev driver should create this struct, either
+ stand-alone or embedded in a larger struct.
+ */
+struct v4l2_subdev {
+ struct list_head list;
+ struct module *owner;
+ struct v4l2_device *dev;
+ const struct v4l2_subdev_ops *ops;
+ /* name must be unique */
+ char name[V4L2_SUBDEV_NAME_SIZE];
+ /* can be used to group similar subdevs, value is driver-specific */
+ u32 grp_id;
+ /* pointer to private data */
+ void *priv;
+};
+
+static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p)
+{
+ sd->priv = p;
+}
+
+static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd)
+{
+ return sd->priv;
+}
+
+/* Convert an ioctl-type command to the proper v4l2_subdev_ops function call.
+ This is used by subdev modules that can be called by both old-style ioctl
+ commands and through the v4l2_subdev_ops.
+
+ The ioctl API of the subdev driver can call this function to call the
+ right ops based on the ioctl cmd and arg.
+
+ Once all subdev drivers have been converted and all drivers no longer
+ use the ioctl interface, then this function can be removed.
+ */
+int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg);
+
+static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_ops *ops)
+{
+ INIT_LIST_HEAD(&sd->list);
+ /* ops->core MUST be set */
+ BUG_ON(!ops || !ops->core);
+ sd->ops = ops;
+ sd->dev = NULL;
+ sd->name[0] = '\0';
+ sd->grp_id = 0;
+ sd->priv = NULL;
+}
+
+/* Call an ops of a v4l2_subdev, doing the right checks against
+ NULL pointers.
+
+ Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+ */
+#define v4l2_subdev_call(sd, o, f, args...) \
+ (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \
+ (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
+
+#endif
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 996d12df7594..a04f8463ac7e 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -54,8 +54,8 @@
#define SOL_RFCOMM 18
#define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
-#define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg)
-#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
+#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
+#define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
/* Connection and socket states */
enum {
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 3cc294919312..3645139e68c7 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -54,7 +54,7 @@
/* HCI device quirks */
enum {
- HCI_QUIRK_RESET_ON_INIT,
+ HCI_QUIRK_NO_RESET,
HCI_QUIRK_RAW_DEVICE,
HCI_QUIRK_FIXUP_BUFFER_SIZE
};
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0e85ec39b638..23c0ab74ded6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5,6 +5,8 @@
#include <linux/skbuff.h>
#include <linux/nl80211.h>
#include <net/genetlink.h>
+/* remove once we remove the wext stuff */
+#include <net/iw_handler.h>
/*
* 802.11 configuration in-kernel interface
@@ -167,6 +169,9 @@ struct station_parameters {
* @STATION_INFO_LLID: @llid filled
* @STATION_INFO_PLID: @plid filled
* @STATION_INFO_PLINK_STATE: @plink_state filled
+ * @STATION_INFO_SIGNAL: @signal filled
+ * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled
+ * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -175,6 +180,39 @@ enum station_info_flags {
STATION_INFO_LLID = 1<<3,
STATION_INFO_PLID = 1<<4,
STATION_INFO_PLINK_STATE = 1<<5,
+ STATION_INFO_SIGNAL = 1<<6,
+ STATION_INFO_TX_BITRATE = 1<<7,
+};
+
+/**
+ * enum station_info_rate_flags - bitrate info flags
+ *
+ * Used by the driver to indicate the specific rate transmission
+ * type for 802.11n transmissions.
+ *
+ * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled
+ * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission
+ * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
+ */
+enum rate_info_flags {
+ RATE_INFO_FLAGS_MCS = 1<<0,
+ RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1,
+ RATE_INFO_FLAGS_SHORT_GI = 1<<2,
+};
+
+/**
+ * struct rate_info - bitrate information
+ *
+ * Information about a receiving or transmitting bitrate
+ *
+ * @flags: bitflag of flags from &enum rate_info_flags
+ * @mcs: mcs index if struct describes a 802.11n bitrate
+ * @legacy: bitrate in 100kbit/s for 802.11abg
+ */
+struct rate_info {
+ u8 flags;
+ u8 mcs;
+ u16 legacy;
};
/**
@@ -189,6 +227,8 @@ enum station_info_flags {
* @llid: mesh local link id
* @plid: mesh peer link id
* @plink_state: mesh peer link state
+ * @signal: signal strength of last received packet in dBm
+ * @txrate: current unicast bitrate to this station
*/
struct station_info {
u32 filled;
@@ -198,6 +238,8 @@ struct station_info {
u16 llid;
u16 plid;
u8 plink_state;
+ s8 signal;
+ struct rate_info txrate;
};
/**
@@ -280,11 +322,16 @@ struct mpath_info {
* (0 = no, 1 = yes, -1 = do not change)
* @use_short_slot_time: Whether the use of short slot time is allowed
* (0 = no, 1 = yes, -1 = do not change)
+ * @basic_rates: basic rates in IEEE 802.11 format
+ * (or NULL for no change)
+ * @basic_rates_len: number of basic rates
*/
struct bss_parameters {
int use_cts_prot;
int use_short_preamble;
int use_short_slot_time;
+ u8 *basic_rates;
+ u8 basic_rates_len;
};
/**
@@ -331,25 +378,65 @@ struct ieee80211_regdomain {
struct ieee80211_reg_rule reg_rules[];
};
-#define MHZ_TO_KHZ(freq) (freq * 1000)
-#define KHZ_TO_MHZ(freq) (freq / 1000)
-#define DBI_TO_MBI(gain) (gain * 100)
-#define MBI_TO_DBI(gain) (gain / 100)
-#define DBM_TO_MBM(gain) (gain * 100)
-#define MBM_TO_DBM(gain) (gain / 100)
+#define MHZ_TO_KHZ(freq) ((freq) * 1000)
+#define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define DBI_TO_MBI(gain) ((gain) * 100)
+#define MBI_TO_DBI(gain) ((gain) / 100)
+#define DBM_TO_MBM(gain) ((gain) * 100)
+#define MBM_TO_DBM(gain) ((gain) / 100)
#define REG_RULE(start, end, bw, gain, eirp, reg_flags) { \
- .freq_range.start_freq_khz = (start) * 1000, \
- .freq_range.end_freq_khz = (end) * 1000, \
- .freq_range.max_bandwidth_khz = (bw) * 1000, \
- .power_rule.max_antenna_gain = (gain) * 100, \
- .power_rule.max_eirp = (eirp) * 100, \
+ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+ .power_rule.max_antenna_gain = DBI_TO_MBI(gain), \
+ .power_rule.max_eirp = DBM_TO_MBM(eirp), \
.flags = reg_flags, \
}
+struct mesh_config {
+ /* Timeouts in ms */
+ /* Mesh plink management parameters */
+ u16 dot11MeshRetryTimeout;
+ u16 dot11MeshConfirmTimeout;
+ u16 dot11MeshHoldingTimeout;
+ u16 dot11MeshMaxPeerLinks;
+ u8 dot11MeshMaxRetries;
+ u8 dot11MeshTTL;
+ bool auto_open_plinks;
+ /* HWMP parameters */
+ u8 dot11MeshHWMPmaxPREQretries;
+ u32 path_refresh_time;
+ u16 min_discovery_timeout;
+ u32 dot11MeshHWMPactivePathTimeout;
+ u16 dot11MeshHWMPpreqMinInterval;
+ u16 dot11MeshHWMPnetDiameterTraversalTime;
+};
+
+/**
+ * struct ieee80211_txq_params - TX queue parameters
+ * @queue: TX queue identifier (NL80211_TXQ_Q_*)
+ * @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled
+ * @cwmin: Minimum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @cwmax: Maximum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @aifs: Arbitration interframe space [0..255]
+ */
+struct ieee80211_txq_params {
+ enum nl80211_txq_q queue;
+ u16 txop;
+ u16 cwmin;
+ u16 cwmax;
+ u8 aifs;
+};
+
/* from net/wireless.h */
struct wiphy;
+/* from net/ieee80211.h */
+struct ieee80211_channel;
+
/**
* struct cfg80211_ops - backend description for wireless configuration
*
@@ -397,9 +484,19 @@ struct wiphy;
*
* @change_station: Modify a given station.
*
+ * @get_mesh_params: Put the current mesh parameters into *params
+ *
+ * @set_mesh_params: Set mesh parameters.
+ * The mask is a bitfield which tells us which parameters to
+ * set, and which to leave alone.
+ *
* @set_mesh_cfg: set mesh parameters (by now, just mesh id)
*
* @change_bss: Modify parameters for a given BSS.
+ *
+ * @set_txq_params: Set TX queue parameters
+ *
+ * @set_channel: Set channel
*/
struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -452,9 +549,30 @@ struct cfg80211_ops {
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
-
+ int (*get_mesh_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf);
+ int (*set_mesh_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct mesh_config *nconf, u32 mask);
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params);
+
+ int (*set_txq_params)(struct wiphy *wiphy,
+ struct ieee80211_txq_params *params);
+
+ int (*set_channel)(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type);
};
+/* temporary wext handlers */
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra);
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+
#endif /* __NET_CFG80211_H */
diff --git a/include/net/checksum.h b/include/net/checksum.h
index 07602b7fa218..ba55d8b8c87c 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -98,7 +98,7 @@ static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to)
{
__be32 diff[] = { ~from, to };
- *sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
+ *sum = csum_fold(csum_partial(diff, sizeof(diff), ~csum_unfold(*sum)));
}
static inline void csum_replace2(__sum16 *sum, __be16 from, __be16 to)
diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h
new file mode 100644
index 000000000000..775cfc8055be
--- /dev/null
+++ b/include/net/dcbnl.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2008, 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., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Lucy Liu <lucy.liu@intel.com>
+ */
+
+#ifndef __NET_DCBNL_H__
+#define __NET_DCBNL_H__
+
+/*
+ * Ops struct for the netlink callbacks. Used by DCB-enabled drivers through
+ * the netdevice struct.
+ */
+struct dcbnl_rtnl_ops {
+ u8 (*getstate)(struct net_device *);
+ u8 (*setstate)(struct net_device *, u8);
+ void (*getpermhwaddr)(struct net_device *, u8 *);
+ void (*setpgtccfgtx)(struct net_device *, int, u8, u8, u8, u8);
+ void (*setpgbwgcfgtx)(struct net_device *, int, u8);
+ void (*setpgtccfgrx)(struct net_device *, int, u8, u8, u8, u8);
+ void (*setpgbwgcfgrx)(struct net_device *, int, u8);
+ void (*getpgtccfgtx)(struct net_device *, int, u8 *, u8 *, u8 *, u8 *);
+ void (*getpgbwgcfgtx)(struct net_device *, int, u8 *);
+ void (*getpgtccfgrx)(struct net_device *, int, u8 *, u8 *, u8 *, u8 *);
+ void (*getpgbwgcfgrx)(struct net_device *, int, u8 *);
+ void (*setpfccfg)(struct net_device *, int, u8);
+ void (*getpfccfg)(struct net_device *, int, u8 *);
+ u8 (*setall)(struct net_device *);
+ u8 (*getcap)(struct net_device *, int, u8 *);
+ u8 (*getnumtcs)(struct net_device *, int, u8 *);
+ u8 (*setnumtcs)(struct net_device *, int, u8);
+ u8 (*getpfcstate)(struct net_device *);
+ void (*setpfcstate)(struct net_device *, u8);
+ void (*getbcncfg)(struct net_device *, int, u32 *);
+ void (*setbcncfg)(struct net_device *, int, u32);
+ void (*getbcnrp)(struct net_device *, int, u8 *);
+ void (*setbcnrp)(struct net_device *, int, u8);
+};
+
+#endif /* __NET_DCBNL_H__ */
diff --git a/include/net/dn.h b/include/net/dn.h
index 627778384c84..e5469f7b67a3 100644
--- a/include/net/dn.h
+++ b/include/net/dn.h
@@ -4,9 +4,7 @@
#include <linux/dn.h>
#include <net/sock.h>
#include <asm/byteorder.h>
-
-#define dn_ntohs(x) le16_to_cpu(x)
-#define dn_htons(x) cpu_to_le16(x)
+#include <asm/unaligned.h>
struct dn_scp /* Session Control Port */
{
@@ -175,7 +173,7 @@ struct dn_skb_cb {
static inline __le16 dn_eth2dn(unsigned char *ethaddr)
{
- return dn_htons(ethaddr[4] | (ethaddr[5] << 8));
+ return get_unaligned((__le16 *)(ethaddr + 4));
}
static inline __le16 dn_saddr2dn(struct sockaddr_dn *saddr)
@@ -185,7 +183,7 @@ static inline __le16 dn_saddr2dn(struct sockaddr_dn *saddr)
static inline void dn_dn2eth(unsigned char *ethaddr, __le16 addr)
{
- __u16 a = dn_ntohs(addr);
+ __u16 a = le16_to_cpu(addr);
ethaddr[0] = 0xAA;
ethaddr[1] = 0x00;
ethaddr[2] = 0x04;
diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h
index 30125119c950..c378be7bf960 100644
--- a/include/net/dn_fib.h
+++ b/include/net/dn_fib.h
@@ -181,9 +181,9 @@ static inline void dn_fib_res_put(struct dn_fib_res *res)
static inline __le16 dnet_make_mask(int n)
{
- if (n)
- return dn_htons(~((1<<(16-n))-1));
- return 0;
+ if (n)
+ return cpu_to_le16(~((1 << (16 - n)) - 1));
+ return cpu_to_le16(0);
}
#endif /* _NET_DN_FIB_H */
diff --git a/include/net/dst.h b/include/net/dst.h
index 8a8b71e5f3f1..6be3b082a070 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -59,8 +59,11 @@ struct dst_entry
struct neighbour *neighbour;
struct hh_cache *hh;
+#ifdef CONFIG_XFRM
struct xfrm_state *xfrm;
-
+#else
+ void *__pad1;
+#endif
int (*input)(struct sk_buff*);
int (*output)(struct sk_buff*);
@@ -70,8 +73,20 @@ struct dst_entry
#ifdef CONFIG_NET_CLS_ROUTE
__u32 tclassid;
+#else
+ __u32 __pad2;
#endif
+
+ /*
+ * Align __refcnt to a 64 bytes alignment
+ * (L1_CACHE_SIZE would be too much)
+ */
+#ifdef CONFIG_64BIT
+ long __pad_to_align_refcnt[2];
+#else
+ long __pad_to_align_refcnt[1];
+#endif
/*
* __refcnt wants to be on a different cache line from
* input/output/ops or performance tanks badly
@@ -103,7 +118,6 @@ struct dst_ops
void (*link_failure)(struct sk_buff *);
void (*update_pmtu)(struct dst_entry *dst, u32 mtu);
int (*local_out)(struct sk_buff *skb);
- int entry_size;
atomic_t entries;
struct kmem_cache *kmem_cachep;
@@ -157,6 +171,11 @@ dst_metric_locked(struct dst_entry *dst, int metric)
static inline void dst_hold(struct dst_entry * dst)
{
+ /*
+ * If your kernel compilation stops here, please check
+ * __pad_to_align_refcnt declaration in struct dst_entry
+ */
+ BUILD_BUG_ON(offsetof(struct dst_entry, __refcnt) & 63);
atomic_inc(&dst->__refcnt);
}
@@ -272,21 +291,21 @@ enum {
struct flowi;
#ifndef CONFIG_XFRM
-static inline int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
- struct sock *sk, int flags)
+static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
+ struct flowi *fl, struct sock *sk, int flags)
{
return 0;
}
-static inline int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
- struct sock *sk, int flags)
+static inline int __xfrm_lookup(struct net *net, struct dst_entry **dst_p,
+ struct flowi *fl, struct sock *sk, int flags)
{
return 0;
}
#else
-extern int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
- struct sock *sk, int flags);
-extern int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
- struct sock *sk, int flags);
+extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p,
+ struct flowi *fl, struct sock *sk, int flags);
+extern int __xfrm_lookup(struct net *net, struct dst_entry **dst_p,
+ struct flowi *fl, struct sock *sk, int flags);
#endif
#endif
diff --git a/include/net/flow.h b/include/net/flow.h
index b45a5e4fcadd..809970b7dfee 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -84,12 +84,13 @@ struct flowi {
#define FLOW_DIR_OUT 1
#define FLOW_DIR_FWD 2
+struct net;
struct sock;
-typedef int (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir,
- void **objp, atomic_t **obj_refp);
+typedef int (*flow_resolve_t)(struct net *net, struct flowi *key, u16 family,
+ u8 dir, void **objp, atomic_t **obj_refp);
-extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
- flow_resolve_t resolver);
+extern void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family,
+ u8 dir, flow_resolve_t resolver);
extern void flow_cache_flush(void);
extern atomic_t flow_cache_genid;
diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
index 8cd8185fa2ed..d136b5240ef2 100644
--- a/include/net/gen_stats.h
+++ b/include/net/gen_stats.h
@@ -45,5 +45,6 @@ extern void gen_kill_estimator(struct gnet_stats_basic *bstats,
extern int gen_replace_estimator(struct gnet_stats_basic *bstats,
struct gnet_stats_rate_est *rate_est,
spinlock_t *stats_lock, struct nlattr *opt);
-
+extern bool gen_estimator_active(const struct gnet_stats_basic *bstats,
+ const struct gnet_stats_rate_est *rate_est);
#endif
diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h
index 93a56de3594b..adb7cf31f781 100644
--- a/include/net/ieee80211.h
+++ b/include/net/ieee80211.h
@@ -28,6 +28,9 @@
#include <linux/if_ether.h> /* ETH_ALEN */
#include <linux/kernel.h> /* ARRAY_SIZE */
#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+
+#include <net/lib80211.h>
#define IEEE80211_VERSION "git-1.1.13"
@@ -127,10 +130,6 @@ static inline bool ieee80211_ratelimit_debug(u32 level)
}
#endif /* CONFIG_IEEE80211_DEBUG */
-/* escape_essid() is intended to be used in debug (and possibly error)
- * messages. It should never be used for passing essid to user space. */
-const char *escape_essid(const char *essid, u8 essid_len);
-
/*
* To use the debug system:
*
@@ -218,94 +217,6 @@ struct ieee80211_snap_hdr {
#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG)
#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
-/* Authentication algorithms */
-#define WLAN_AUTH_OPEN 0
-#define WLAN_AUTH_SHARED_KEY 1
-#define WLAN_AUTH_LEAP 2
-
-#define WLAN_AUTH_CHALLENGE_LEN 128
-
-#define WLAN_CAPABILITY_ESS (1<<0)
-#define WLAN_CAPABILITY_IBSS (1<<1)
-#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
-#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
-#define WLAN_CAPABILITY_PRIVACY (1<<4)
-#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
-#define WLAN_CAPABILITY_PBCC (1<<6)
-#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
-#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
-#define WLAN_CAPABILITY_QOS (1<<9)
-#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
-#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
-
-/* 802.11g ERP information element */
-#define WLAN_ERP_NON_ERP_PRESENT (1<<0)
-#define WLAN_ERP_USE_PROTECTION (1<<1)
-#define WLAN_ERP_BARKER_PREAMBLE (1<<2)
-
-/* Status codes */
-enum ieee80211_statuscode {
- WLAN_STATUS_SUCCESS = 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE = 1,
- WLAN_STATUS_CAPS_UNSUPPORTED = 10,
- WLAN_STATUS_REASSOC_NO_ASSOC = 11,
- WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12,
- WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13,
- WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14,
- WLAN_STATUS_CHALLENGE_FAIL = 15,
- WLAN_STATUS_AUTH_TIMEOUT = 16,
- WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17,
- WLAN_STATUS_ASSOC_DENIED_RATES = 18,
- /* 802.11b */
- WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19,
- WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20,
- WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21,
- /* 802.11h */
- WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22,
- WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23,
- WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24,
- /* 802.11g */
- WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25,
- WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26,
- /* 802.11i */
- WLAN_STATUS_INVALID_IE = 40,
- WLAN_STATUS_INVALID_GROUP_CIPHER = 41,
- WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42,
- WLAN_STATUS_INVALID_AKMP = 43,
- WLAN_STATUS_UNSUPP_RSN_VERSION = 44,
- WLAN_STATUS_INVALID_RSN_IE_CAP = 45,
- WLAN_STATUS_CIPHER_SUITE_REJECTED = 46,
-};
-
-/* Reason codes */
-enum ieee80211_reasoncode {
- WLAN_REASON_UNSPECIFIED = 1,
- WLAN_REASON_PREV_AUTH_NOT_VALID = 2,
- WLAN_REASON_DEAUTH_LEAVING = 3,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4,
- WLAN_REASON_DISASSOC_AP_BUSY = 5,
- WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6,
- WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7,
- WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8,
- WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9,
- /* 802.11h */
- WLAN_REASON_DISASSOC_BAD_POWER = 10,
- WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11,
- /* 802.11i */
- WLAN_REASON_INVALID_IE = 13,
- WLAN_REASON_MIC_FAILURE = 14,
- WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
- WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16,
- WLAN_REASON_IE_DIFFERENT = 17,
- WLAN_REASON_INVALID_GROUP_CIPHER = 18,
- WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19,
- WLAN_REASON_INVALID_AKMP = 20,
- WLAN_REASON_UNSUPP_RSN_VERSION = 21,
- WLAN_REASON_INVALID_RSN_IE_CAP = 22,
- WLAN_REASON_IEEE8021X_FAILED = 23,
- WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
-};
-
/* Action categories - 802.11h */
enum ieee80211_actioncategories {
WLAN_ACTION_SPECTRUM_MGMT = 0,
@@ -446,8 +357,6 @@ struct ieee80211_stats {
struct ieee80211_device;
-#include "ieee80211_crypt.h"
-
#define SEC_KEY_1 (1<<0)
#define SEC_KEY_2 (1<<1)
#define SEC_KEY_3 (1<<2)
@@ -476,9 +385,8 @@ struct ieee80211_device;
#define SCM_TEMPORAL_KEY_LENGTH 16
struct ieee80211_security {
- u16 active_key:2,
- enabled:1,
- auth_mode:2, auth_algo:4, unicast_uses_group:1, encrypt:1;
+ u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1;
+ u8 auth_mode;
u8 encode_alg[WEP_KEYS];
u8 key_sizes[WEP_KEYS];
u8 keys[WEP_KEYS][SCM_KEY_LEN];
@@ -534,15 +442,6 @@ enum ieee80211_mfie {
MFIE_TYPE_QOS_PARAMETER = 222,
};
-/* Minimal header; can be used for passing 802.11 frames with sufficient
- * information to determine what type of underlying data type is actually
- * stored in the data. */
-struct ieee80211_hdr {
- __le16 frame_ctl;
- __le16 duration_id;
- u8 payload[0];
-} __attribute__ ((packed));
-
struct ieee80211_hdr_1addr {
__le16 frame_ctl;
__le16 duration_id;
@@ -590,18 +489,6 @@ struct ieee80211_hdr_3addrqos {
__le16 qos_ctl;
} __attribute__ ((packed));
-struct ieee80211_hdr_4addrqos {
- __le16 frame_ctl;
- __le16 duration_id;
- u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
- u8 addr3[ETH_ALEN];
- __le16 seq_ctl;
- u8 addr4[ETH_ALEN];
- u8 payload[0];
- __le16 qos_ctl;
-} __attribute__ ((packed));
-
struct ieee80211_info_element {
u8 id;
u8 len;
@@ -733,7 +620,6 @@ struct ieee80211_txb {
#define MAX_WPA_IE_LEN 64
-#define NETWORK_EMPTY_ESSID (1<<0)
#define NETWORK_HAS_OFDM (1<<1)
#define NETWORK_HAS_CCK (1<<2)
@@ -1050,11 +936,7 @@ struct ieee80211_device {
size_t wpa_ie_len;
u8 *wpa_ie;
- struct list_head crypt_deinit_list;
- struct ieee80211_crypt_data *crypt[WEP_KEYS];
- int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
- struct timer_list crypt_deinit_timer;
- int crypt_quiesced;
+ struct lib80211_crypt_info crypt_info;
int bcrx_sta_key; /* use individual keys to override default keys even
* with RX of broad/multicast frames */
@@ -1135,22 +1017,6 @@ static inline void *ieee80211_priv(struct net_device *dev)
return ((struct ieee80211_device *)netdev_priv(dev))->priv;
}
-static inline int ieee80211_is_empty_essid(const char *essid, int essid_len)
-{
- /* Single white space is for Linksys APs */
- if (essid_len == 1 && essid[0] == ' ')
- return 1;
-
- /* Otherwise, if the entire essid is 0, we assume it is hidden */
- while (essid_len) {
- essid_len--;
- if (essid[essid_len] != '\0')
- return 0;
- }
-
- return 1;
-}
-
static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee,
int mode)
{
@@ -1208,7 +1074,7 @@ static inline int ieee80211_get_hdrlen(u16 fc)
static inline u8 *ieee80211_get_payload(struct ieee80211_hdr *hdr)
{
- switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl))) {
+ switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control))) {
case IEEE80211_1ADDR_LEN:
return ((struct ieee80211_hdr_1addr *)hdr)->payload;
case IEEE80211_2ADDR_LEN:
diff --git a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h
deleted file mode 100644
index b3d65e0bedd3..000000000000
--- a/include/net/ieee80211_crypt.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Original code based on Host AP (software wireless LAN access point) driver
- * for Intersil Prism2/2.5/3.
- *
- * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <j@w1.fi>
- * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
- *
- * Adaption to a generic IEEE 802.11 stack by James Ketrenos
- * <jketreno@linux.intel.com>
- *
- * Copyright (c) 2004, Intel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- */
-
-/*
- * This file defines the interface to the ieee80211 crypto module.
- */
-#ifndef IEEE80211_CRYPT_H
-#define IEEE80211_CRYPT_H
-
-#include <linux/types.h>
-#include <linux/list.h>
-#include <net/ieee80211.h>
-#include <asm/atomic.h>
-
-enum {
- IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0),
-};
-
-struct sk_buff;
-struct module;
-
-struct ieee80211_crypto_ops {
- const char *name;
- struct list_head list;
-
- /* init new crypto context (e.g., allocate private data space,
- * select IV, etc.); returns NULL on failure or pointer to allocated
- * private data on success */
- void *(*init) (int keyidx);
-
- /* deinitialize crypto context and free allocated private data */
- void (*deinit) (void *priv);
-
- int (*build_iv) (struct sk_buff * skb, int hdr_len,
- u8 *key, int keylen, void *priv);
-
- /* encrypt/decrypt return < 0 on error or >= 0 on success. The return
- * value from decrypt_mpdu is passed as the keyidx value for
- * decrypt_msdu. skb must have enough head and tail room for the
- * encryption; if not, error will be returned; these functions are
- * called for all MPDUs (i.e., fragments).
- */
- int (*encrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
- int (*decrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
-
- /* These functions are called for full MSDUs, i.e. full frames.
- * These can be NULL if full MSDU operations are not needed. */
- int (*encrypt_msdu) (struct sk_buff * skb, int hdr_len, void *priv);
- int (*decrypt_msdu) (struct sk_buff * skb, int keyidx, int hdr_len,
- void *priv);
-
- int (*set_key) (void *key, int len, u8 * seq, void *priv);
- int (*get_key) (void *key, int len, u8 * seq, void *priv);
-
- /* procfs handler for printing out key information and possible
- * statistics */
- char *(*print_stats) (char *p, void *priv);
-
- /* Crypto specific flag get/set for configuration settings */
- unsigned long (*get_flags) (void *priv);
- unsigned long (*set_flags) (unsigned long flags, void *priv);
-
- /* maximum number of bytes added by encryption; encrypt buf is
- * allocated with extra_prefix_len bytes, copy of in_buf, and
- * extra_postfix_len; encrypt need not use all this space, but
- * the result must start at the beginning of the buffer and correct
- * length must be returned */
- int extra_mpdu_prefix_len, extra_mpdu_postfix_len;
- int extra_msdu_prefix_len, extra_msdu_postfix_len;
-
- struct module *owner;
-};
-
-struct ieee80211_crypt_data {
- struct list_head list; /* delayed deletion list */
- struct ieee80211_crypto_ops *ops;
- void *priv;
- atomic_t refcnt;
-};
-
-struct ieee80211_device;
-
-int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops);
-int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops);
-struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name);
-void ieee80211_crypt_deinit_entries(struct ieee80211_device *, int);
-void ieee80211_crypt_deinit_handler(unsigned long);
-void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
- struct ieee80211_crypt_data **crypt);
-void ieee80211_crypt_quiescing(struct ieee80211_device *ieee);
-
-#endif
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index d364fd594ea4..384698cb773a 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -1,7 +1,4 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
-
-/*-
+/*
* Copyright (c) 2003, 2004 David Young. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,8 +39,6 @@
#include <linux/kernel.h>
#include <asm/unaligned.h>
-/* Radiotap header version (from official NetBSD feed) */
-#define IEEE80211RADIOTAP_VERSION "1.5"
/* Base version of the radiotap packet header data */
#define PKTHDR_RADIOTAP_VERSION 0
@@ -62,12 +57,8 @@
* readers.
*/
-/* XXX tcpdump/libpcap do not tolerate variable-length headers,
- * yet, so we pad every radiotap header to 64 bytes. Ugh.
- */
-#define IEEE80211_RADIOTAP_HDRLEN 64
-
-/* The radio capture header precedes the 802.11 header.
+/*
+ * The radio capture header precedes the 802.11 header.
* All data in the header is little endian on all platforms.
*/
struct ieee80211_radiotap_header {
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 5cc182f9ecae..f44bb5c77a70 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -41,8 +41,8 @@
* I'll experiment with dynamic table growth later.
*/
struct inet_ehash_bucket {
- struct hlist_head chain;
- struct hlist_head twchain;
+ struct hlist_nulls_head chain;
+ struct hlist_nulls_head twchain;
};
/* There are a few simple rules, which allow for local port reuse by
@@ -77,13 +77,20 @@ struct inet_ehash_bucket {
* ports are created in O(1) time? I thought so. ;-) -DaveM
*/
struct inet_bind_bucket {
+#ifdef CONFIG_NET_NS
struct net *ib_net;
+#endif
unsigned short port;
signed short fastreuse;
struct hlist_node node;
struct hlist_head owners;
};
+static inline struct net *ib_net(struct inet_bind_bucket *ib)
+{
+ return read_pnet(&ib->ib_net);
+}
+
#define inet_bind_bucket_for_each(tb, node, head) \
hlist_for_each_entry(tb, node, head, node)
@@ -92,6 +99,18 @@ struct inet_bind_hashbucket {
struct hlist_head chain;
};
+/*
+ * Sockets can be hashed in established or listening table
+ * We must use different 'nulls' end-of-chain value for listening
+ * hash table, or we might find a socket that was closed and
+ * reallocated/inserted into established hash table
+ */
+#define LISTENING_NULLS_BASE (1U << 29)
+struct inet_listen_hashbucket {
+ spinlock_t lock;
+ struct hlist_nulls_head head;
+};
+
/* This is for listening sockets, thus all sockets which possess wildcards. */
#define INET_LHTABLE_SIZE 32 /* Yes, really, this is all you need. */
@@ -104,7 +123,7 @@ struct inet_hashinfo {
* TIME_WAIT sockets use a separate chain (twchain).
*/
struct inet_ehash_bucket *ehash;
- rwlock_t *ehash_locks;
+ spinlock_t *ehash_locks;
unsigned int ehash_size;
unsigned int ehash_locks_mask;
@@ -116,22 +135,21 @@ struct inet_hashinfo {
unsigned int bhash_size;
/* Note : 4 bytes padding on 64 bit arches */
- /* All sockets in TCP_LISTEN state will be in here. This is the only
- * table where wildcard'd TCP sockets can exist. Hash function here
- * is just local port number.
- */
- struct hlist_head listening_hash[INET_LHTABLE_SIZE];
+ struct kmem_cache *bind_bucket_cachep;
/* All the above members are written once at bootup and
* never written again _or_ are predominantly read-access.
*
* Now align to a new cache line as all the following members
- * are often dirty.
+ * might be often dirty.
*/
- rwlock_t lhash_lock ____cacheline_aligned;
- atomic_t lhash_users;
- wait_queue_head_t lhash_wait;
- struct kmem_cache *bind_bucket_cachep;
+ /* All sockets in TCP_LISTEN state will be in here. This is the only
+ * table where wildcard'd TCP sockets can exist. Hash function here
+ * is just local port number.
+ */
+ struct inet_listen_hashbucket listening_hash[INET_LHTABLE_SIZE]
+ ____cacheline_aligned_in_smp;
+
};
static inline struct inet_ehash_bucket *inet_ehash_bucket(
@@ -141,7 +159,7 @@ static inline struct inet_ehash_bucket *inet_ehash_bucket(
return &hashinfo->ehash[hash & (hashinfo->ehash_size - 1)];
}
-static inline rwlock_t *inet_ehash_lockp(
+static inline spinlock_t *inet_ehash_lockp(
struct inet_hashinfo *hashinfo,
unsigned int hash)
{
@@ -166,16 +184,16 @@ static inline int inet_ehash_locks_alloc(struct inet_hashinfo *hashinfo)
size = 4096;
if (sizeof(rwlock_t) != 0) {
#ifdef CONFIG_NUMA
- if (size * sizeof(rwlock_t) > PAGE_SIZE)
- hashinfo->ehash_locks = vmalloc(size * sizeof(rwlock_t));
+ if (size * sizeof(spinlock_t) > PAGE_SIZE)
+ hashinfo->ehash_locks = vmalloc(size * sizeof(spinlock_t));
else
#endif
- hashinfo->ehash_locks = kmalloc(size * sizeof(rwlock_t),
+ hashinfo->ehash_locks = kmalloc(size * sizeof(spinlock_t),
GFP_KERNEL);
if (!hashinfo->ehash_locks)
return ENOMEM;
for (i = 0; i < size; i++)
- rwlock_init(&hashinfo->ehash_locks[i]);
+ spin_lock_init(&hashinfo->ehash_locks[i]);
}
hashinfo->ehash_locks_mask = size - 1;
return 0;
@@ -186,7 +204,7 @@ static inline void inet_ehash_locks_free(struct inet_hashinfo *hashinfo)
if (hashinfo->ehash_locks) {
#ifdef CONFIG_NUMA
unsigned int size = (hashinfo->ehash_locks_mask + 1) *
- sizeof(rwlock_t);
+ sizeof(spinlock_t);
if (size > PAGE_SIZE)
vfree(hashinfo->ehash_locks);
else
@@ -229,26 +247,7 @@ extern void __inet_inherit_port(struct sock *sk, struct sock *child);
extern void inet_put_port(struct sock *sk);
-extern void inet_listen_wlock(struct inet_hashinfo *hashinfo);
-
-/*
- * - We may sleep inside this lock.
- * - If sleeping is not required (or called from BH),
- * use plain read_(un)lock(&inet_hashinfo.lhash_lock).
- */
-static inline void inet_listen_lock(struct inet_hashinfo *hashinfo)
-{
- /* read_lock synchronizes to candidates to writers */
- read_lock(&hashinfo->lhash_lock);
- atomic_inc(&hashinfo->lhash_users);
- read_unlock(&hashinfo->lhash_lock);
-}
-
-static inline void inet_listen_unlock(struct inet_hashinfo *hashinfo)
-{
- if (atomic_dec_and_test(&hashinfo->lhash_users))
- wake_up(&hashinfo->lhash_wait);
-}
+void inet_hashinfo_init(struct inet_hashinfo *h);
extern void __inet_hash_nolisten(struct sock *sk);
extern void inet_hash(struct sock *sk);
@@ -299,25 +298,25 @@ typedef __u64 __bitwise __addrpair;
((__force __u64)(__be32)(__saddr)));
#endif /* __BIG_ENDIAN */
#define INET_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif)\
- (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \
+ (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \
((*((__addrpair *)&(inet_sk(__sk)->daddr))) == (__cookie)) && \
((*((__portpair *)&(inet_sk(__sk)->dport))) == (__ports)) && \
(!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif))))
#define INET_TW_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif)\
- (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \
+ (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \
((*((__addrpair *)&(inet_twsk(__sk)->tw_daddr))) == (__cookie)) && \
((*((__portpair *)&(inet_twsk(__sk)->tw_dport))) == (__ports)) && \
(!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif))))
#else /* 32-bit arch */
#define INET_ADDR_COOKIE(__name, __saddr, __daddr)
#define INET_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif) \
- (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \
+ (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \
(inet_sk(__sk)->daddr == (__saddr)) && \
(inet_sk(__sk)->rcv_saddr == (__daddr)) && \
((*((__portpair *)&(inet_sk(__sk)->dport))) == (__ports)) && \
(!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif))))
#define INET_TW_MATCH(__sk, __net, __hash,__cookie, __saddr, __daddr, __ports, __dif) \
- (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \
+ (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \
(inet_twsk(__sk)->tw_daddr == (__saddr)) && \
(inet_twsk(__sk)->tw_rcv_saddr == (__daddr)) && \
((*((__portpair *)&(inet_twsk(__sk)->tw_dport))) == (__ports)) && \
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index 80e4977631b8..4b8ece22b8e9 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -110,7 +110,7 @@ struct inet_timewait_sock {
#define tw_state __tw_common.skc_state
#define tw_reuse __tw_common.skc_reuse
#define tw_bound_dev_if __tw_common.skc_bound_dev_if
-#define tw_node __tw_common.skc_node
+#define tw_node __tw_common.skc_nulls_node
#define tw_bind_node __tw_common.skc_bind_node
#define tw_refcnt __tw_common.skc_refcnt
#define tw_hash __tw_common.skc_hash
@@ -137,10 +137,10 @@ struct inet_timewait_sock {
struct hlist_node tw_death_node;
};
-static inline void inet_twsk_add_node(struct inet_timewait_sock *tw,
- struct hlist_head *list)
+static inline void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
+ struct hlist_nulls_head *list)
{
- hlist_add_head(&tw->tw_node, list);
+ hlist_nulls_add_head_rcu(&tw->tw_node, list);
}
static inline void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
@@ -175,7 +175,7 @@ static inline int inet_twsk_del_dead_node(struct inet_timewait_sock *tw)
}
#define inet_twsk_for_each(tw, node, head) \
- hlist_for_each_entry(tw, node, head, tw_node)
+ hlist_nulls_for_each_entry(tw, node, head, tw_node)
#define inet_twsk_for_each_inmate(tw, node, jail) \
hlist_for_each_entry(tw, node, jail, tw_death_node)
diff --git a/include/net/ip.h b/include/net/ip.h
index bc026ecb513f..10868139e656 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -110,7 +110,7 @@ extern int ip_append_data(struct sock *sk,
int odd, struct sk_buff *skb),
void *from, int len, int protolen,
struct ipcm_cookie *ipc,
- struct rtable *rt,
+ struct rtable **rt,
unsigned int flags);
extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb);
extern ssize_t ip_append_page(struct sock *sk, struct page *page,
@@ -187,6 +187,7 @@ extern void inet_get_local_port_range(int *low, int *high);
extern int sysctl_ip_default_ttl;
extern int sysctl_ip_nonlocal_bind;
+extern struct ctl_path net_core_path[];
extern struct ctl_path net_ipv4_ctl_path[];
/* From inetpeer.c */
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index fe9fcf73c85e..ab9b003ab671 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -87,12 +87,12 @@ static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len,
int len;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
- len = snprintf(&buf[*idx], buf_len - *idx, "[" NIP6_FMT "]",
- NIP6(addr->in6)) + 1;
+ len = snprintf(&buf[*idx], buf_len - *idx, "[%pI6]",
+ &addr->in6) + 1;
else
#endif
- len = snprintf(&buf[*idx], buf_len - *idx, NIPQUAD_FMT,
- NIPQUAD(addr->ip)) + 1;
+ len = snprintf(&buf[*idx], buf_len - *idx, "%pI4",
+ &addr->ip) + 1;
*idx += len;
BUG_ON(*idx > buf_len + 1);
@@ -503,9 +503,6 @@ struct ip_vs_scheduler {
char *name; /* scheduler name */
atomic_t refcnt; /* reference counter */
struct module *module; /* THIS_MODULE/NULL */
-#ifdef CONFIG_IP_VS_IPV6
- int supports_ipv6; /* scheduler has IPv6 support */
-#endif
/* scheduler initializing service */
int (*init_service)(struct ip_vs_service *svc);
@@ -916,7 +913,7 @@ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
{
__be32 diff[2] = { ~old, new };
- return csum_partial((char *) diff, sizeof(diff), oldsum);
+ return csum_partial(diff, sizeof(diff), oldsum);
}
#ifdef CONFIG_IP_VS_IPV6
@@ -926,7 +923,7 @@ static inline __wsum ip_vs_check_diff16(const __be32 *old, const __be32 *new,
__be32 diff[8] = { ~old[3], ~old[2], ~old[1], ~old[0],
new[3], new[2], new[1], new[0] };
- return csum_partial((char *) diff, sizeof(diff), oldsum);
+ return csum_partial(diff, sizeof(diff), oldsum);
}
#endif
@@ -934,7 +931,7 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
{
__be16 diff[2] = { ~old, new };
- return csum_partial((char *) diff, sizeof(diff), oldsum);
+ return csum_partial(diff, sizeof(diff), oldsum);
}
#endif /* __KERNEL__ */
diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h
index 3025ae17ddbe..94c852d47d0f 100644
--- a/include/net/irda/irda_device.h
+++ b/include/net/irda/irda_device.h
@@ -135,9 +135,11 @@ struct dongle_reg {
/*
* Per-packet information we need to hide inside sk_buff
- * (must not exceed 48 bytes, check with struct sk_buff)
+ * (must not exceed 48 bytes, check with struct sk_buff)
+ * The default_qdisc_pad field is a temporary hack.
*/
struct irda_skb_cb {
+ unsigned int default_qdisc_pad;
magic_t magic; /* Be sure that we can trust the information */
__u32 next_speed; /* The Speed to be set *after* this frame */
__u16 mtt; /* Minimum turn around time */
diff --git a/include/net/iucv/iucv.h b/include/net/iucv/iucv.h
index fd70adbb3566..5e310c8d8e2f 100644
--- a/include/net/iucv/iucv.h
+++ b/include/net/iucv/iucv.h
@@ -337,12 +337,35 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
* established paths. This function will deal with RMDATA messages
* embedded in struct iucv_message as well.
*
+ * Locking: local_bh_enable/local_bh_disable
+ *
* Returns the result from the CP IUCV call.
*/
int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
u8 flags, void *buffer, size_t size, size_t *residual);
/**
+ * __iucv_message_receive
+ * @path: address of iucv path structure
+ * @msg: address of iucv msg structure
+ * @flags: flags that affect how the message is received (IUCV_IPBUFLST)
+ * @buffer: address of data buffer or address of struct iucv_array
+ * @size: length of data buffer
+ * @residual:
+ *
+ * This function receives messages that are being sent to you over
+ * established paths. This function will deal with RMDATA messages
+ * embedded in struct iucv_message as well.
+ *
+ * Locking: no locking.
+ *
+ * Returns the result from the CP IUCV call.
+ */
+int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
+ u8 flags, void *buffer, size_t size,
+ size_t *residual);
+
+/**
* iucv_message_reject
* @path: address of iucv path structure
* @msg: address of iucv msg structure
@@ -386,12 +409,34 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
* transmitted is in a buffer and this is a one-way message and the
* receiver will not reply to the message.
*
+ * Locking: local_bh_enable/local_bh_disable
+ *
* Returns the result from the CP IUCV call.
*/
int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
u8 flags, u32 srccls, void *buffer, size_t size);
/**
+ * __iucv_message_send
+ * @path: address of iucv path structure
+ * @msg: address of iucv msg structure
+ * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
+ * @srccls: source class of message
+ * @buffer: address of data buffer or address of struct iucv_array
+ * @size: length of send buffer
+ *
+ * This function transmits data to another application. Data to be
+ * transmitted is in a buffer and this is a one-way message and the
+ * receiver will not reply to the message.
+ *
+ * Locking: no locking.
+ *
+ * Returns the result from the CP IUCV call.
+ */
+int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
+ u8 flags, u32 srccls, void *buffer, size_t size);
+
+/**
* iucv_message_send2way
* @path: address of iucv path structure
* @msg: address of iucv msg structure
diff --git a/include/net/lib80211.h b/include/net/lib80211.h
new file mode 100644
index 000000000000..fb4e2784857d
--- /dev/null
+++ b/include/net/lib80211.h
@@ -0,0 +1,129 @@
+/*
+ * lib80211.h -- common bits for IEEE802.11 wireless drivers
+ *
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
+ *
+ * Some bits copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Original code based on Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <j@w1.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ *
+ * Copyright (c) 2004, Intel Corporation
+ *
+ */
+
+#ifndef LIB80211_H
+#define LIB80211_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <asm/atomic.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/ieee80211.h>
+#include <linux/timer.h>
+/* print_ssid() is intended to be used in debug (and possibly error)
+ * messages. It should never be used for passing ssid to user space. */
+const char *print_ssid(char *buf, const char *ssid, u8 ssid_len);
+#define DECLARE_SSID_BUF(var) char var[IEEE80211_MAX_SSID_LEN * 4 + 1] __maybe_unused
+
+#define NUM_WEP_KEYS 4
+
+enum {
+ IEEE80211_CRYPTO_TKIP_COUNTERMEASURES = (1 << 0),
+};
+
+struct lib80211_crypto_ops {
+ const char *name;
+ struct list_head list;
+
+ /* init new crypto context (e.g., allocate private data space,
+ * select IV, etc.); returns NULL on failure or pointer to allocated
+ * private data on success */
+ void *(*init) (int keyidx);
+
+ /* deinitialize crypto context and free allocated private data */
+ void (*deinit) (void *priv);
+
+ int (*build_iv) (struct sk_buff * skb, int hdr_len,
+ u8 *key, int keylen, void *priv);
+
+ /* encrypt/decrypt return < 0 on error or >= 0 on success. The return
+ * value from decrypt_mpdu is passed as the keyidx value for
+ * decrypt_msdu. skb must have enough head and tail room for the
+ * encryption; if not, error will be returned; these functions are
+ * called for all MPDUs (i.e., fragments).
+ */
+ int (*encrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
+ int (*decrypt_mpdu) (struct sk_buff * skb, int hdr_len, void *priv);
+
+ /* These functions are called for full MSDUs, i.e. full frames.
+ * These can be NULL if full MSDU operations are not needed. */
+ int (*encrypt_msdu) (struct sk_buff * skb, int hdr_len, void *priv);
+ int (*decrypt_msdu) (struct sk_buff * skb, int keyidx, int hdr_len,
+ void *priv);
+
+ int (*set_key) (void *key, int len, u8 * seq, void *priv);
+ int (*get_key) (void *key, int len, u8 * seq, void *priv);
+
+ /* procfs handler for printing out key information and possible
+ * statistics */
+ char *(*print_stats) (char *p, void *priv);
+
+ /* Crypto specific flag get/set for configuration settings */
+ unsigned long (*get_flags) (void *priv);
+ unsigned long (*set_flags) (unsigned long flags, void *priv);
+
+ /* maximum number of bytes added by encryption; encrypt buf is
+ * allocated with extra_prefix_len bytes, copy of in_buf, and
+ * extra_postfix_len; encrypt need not use all this space, but
+ * the result must start at the beginning of the buffer and correct
+ * length must be returned */
+ int extra_mpdu_prefix_len, extra_mpdu_postfix_len;
+ int extra_msdu_prefix_len, extra_msdu_postfix_len;
+
+ struct module *owner;
+};
+
+struct lib80211_crypt_data {
+ struct list_head list; /* delayed deletion list */
+ struct lib80211_crypto_ops *ops;
+ void *priv;
+ atomic_t refcnt;
+};
+
+struct lib80211_crypt_info {
+ char *name;
+ /* Most clients will already have a lock,
+ so just point to that. */
+ spinlock_t *lock;
+
+ struct lib80211_crypt_data *crypt[NUM_WEP_KEYS];
+ int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
+ struct list_head crypt_deinit_list;
+ struct timer_list crypt_deinit_timer;
+ int crypt_quiesced;
+};
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock);
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info);
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops);
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops);
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name);
+void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *, int);
+void lib80211_crypt_deinit_handler(unsigned long);
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt);
+void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
+
+#endif /* LIB80211_H */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 73d81bc6aa75..b3bd00a9d992 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3,7 +3,7 @@
*
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
- * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -107,7 +107,7 @@ enum ieee80211_max_queues {
* The information provided in this structure is required for QoS
* transmit queue configuration. Cf. IEEE 802.11 7.3.2.29.
*
- * @aifs: arbitration interface space [0..255]
+ * @aifs: arbitration interframe space [0..255]
* @cw_min: minimum contention window [a value of the form
* 2^n-1 in the range 1..32767]
* @cw_max: maximum contention window [like @cw_min]
@@ -164,6 +164,14 @@ enum ieee80211_bss_change {
};
/**
+ * struct ieee80211_bss_ht_conf - BSS's changing HT configuration
+ * @operation_mode: HT operation mode (like in &struct ieee80211_ht_info)
+ */
+struct ieee80211_bss_ht_conf {
+ u16 operation_mode;
+};
+
+/**
* struct ieee80211_bss_conf - holds the BSS's changing parameters
*
* This structure keeps information about a BSS (and an association
@@ -172,15 +180,17 @@ enum ieee80211_bss_change {
* @assoc: association status
* @aid: association ID number, valid only when @assoc is true
* @use_cts_prot: use CTS protection
- * @use_short_preamble: use 802.11b short preamble
- * @use_short_slot: use short slot time (only relevant for ERP)
+ * @use_short_preamble: use 802.11b short preamble;
+ * if the hardware cannot handle this it must set the
+ * IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE hardware flag
+ * @use_short_slot: use short slot time (only relevant for ERP);
+ * if the hardware cannot handle this it must set the
+ * IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
* @dtim_period: num of beacons before the next DTIM, for PSM
* @timestamp: beacon timestamp
* @beacon_int: beacon interval
* @assoc_capability: capabilities taken from assoc resp
- * @assoc_ht: association in HT mode
- * @ht_conf: ht capabilities
- * @ht_bss_conf: ht extended capabilities
+ * @ht: BSS's HT configuration
* @basic_rates: bitmap of basic rates, each bit stands for an
* index into the rate table configured by the driver in
* the current band.
@@ -198,10 +208,7 @@ struct ieee80211_bss_conf {
u16 assoc_capability;
u64 timestamp;
u64 basic_rates;
- /* ht related data */
- bool assoc_ht;
- struct ieee80211_ht_info *ht_conf;
- struct ieee80211_ht_bss_info *ht_bss_conf;
+ struct ieee80211_bss_ht_conf ht;
};
/**
@@ -210,29 +217,24 @@ struct ieee80211_bss_conf {
* These flags are used with the @flags member of &ieee80211_tx_info.
*
* @IEEE80211_TX_CTL_REQ_TX_STATUS: request TX status callback for this frame.
- * @IEEE80211_TX_CTL_USE_RTS_CTS: use RTS-CTS before sending frame
- * @IEEE80211_TX_CTL_USE_CTS_PROTECT: use CTS protection for the frame (e.g.,
- * for combined 802.11g / 802.11b networks)
+ * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
+ * number to this frame, taking care of not overwriting the fragment
+ * number and increasing the sequence number only when the
+ * IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly
+ * assign sequence numbers to QoS-data frames but cannot do so correctly
+ * for non-QoS-data and management frames because beacons need them from
+ * that counter as well and mac80211 cannot guarantee proper sequencing.
+ * If this flag is set, the driver should instruct the hardware to
+ * assign a sequence number to the frame or assign one itself. Cf. IEEE
+ * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
+ * beacons and always be clear for frames without a sequence number field.
* @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack
- * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: TBD
* @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination
* station
- * @IEEE80211_TX_CTL_REQUEUE: TBD
* @IEEE80211_TX_CTL_FIRST_FRAGMENT: this is a first fragment of the frame
- * @IEEE80211_TX_CTL_SHORT_PREAMBLE: TBD
- * @IEEE80211_TX_CTL_LONG_RETRY_LIMIT: this frame should be send using the
- * through set_retry_limit configured long retry value
* @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon
* @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU
- * @IEEE80211_TX_CTL_OFDM_HT: this frame can be sent in HT OFDM rates. number
- * of streams when this flag is on can be extracted from antenna_sel_tx,
- * so if 1 antenna is marked use SISO, 2 antennas marked use MIMO, n
- * antennas marked use MIMO_n.
- * @IEEE80211_TX_CTL_GREEN_FIELD: use green field protection for this frame
- * @IEEE80211_TX_CTL_40_MHZ_WIDTH: send this frame using 40 Mhz channel width
- * @IEEE80211_TX_CTL_DUP_DATA: duplicate data frame on both 20 Mhz channels
- * @IEEE80211_TX_CTL_SHORT_GI: send this frame using short guard interval
- * @IEEE80211_TX_CTL_INJECTED: TBD
+ * @IEEE80211_TX_CTL_INJECTED: Frame was injected, internal to mac80211.
* @IEEE80211_TX_STAT_TX_FILTERED: The frame was not transmitted
* because the destination STA was in powersave mode.
* @IEEE80211_TX_STAT_ACK: Frame was acknowledged
@@ -240,63 +242,67 @@ struct ieee80211_bss_conf {
* is for the whole aggregation.
* @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned,
* so consider using block ack request (BAR).
- * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
- * number to this frame, taking care of not overwriting the fragment
- * number and increasing the sequence number only when the
- * IEEE80211_TX_CTL_FIRST_FRAGMENT flags is set. mac80211 will properly
- * assign sequence numbers to QoS-data frames but cannot do so correctly
- * for non-QoS-data and management frames because beacons need them from
- * that counter as well and mac80211 cannot guarantee proper sequencing.
- * If this flag is set, the driver should instruct the hardware to
- * assign a sequence number to the frame or assign one itself. Cf. IEEE
- * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
- * beacons always be clear for frames without a sequence number field.
+ * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be
+ * set by rate control algorithms to indicate probe rate, will
+ * be cleared for fragmented frames (except on the last fragment)
*/
enum mac80211_tx_control_flags {
IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
- IEEE80211_TX_CTL_USE_RTS_CTS = BIT(2),
- IEEE80211_TX_CTL_USE_CTS_PROTECT = BIT(3),
- IEEE80211_TX_CTL_NO_ACK = BIT(4),
- IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(5),
- IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(6),
- IEEE80211_TX_CTL_REQUEUE = BIT(7),
- IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(8),
- IEEE80211_TX_CTL_SHORT_PREAMBLE = BIT(9),
- IEEE80211_TX_CTL_LONG_RETRY_LIMIT = BIT(10),
- IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(12),
- IEEE80211_TX_CTL_AMPDU = BIT(13),
- IEEE80211_TX_CTL_OFDM_HT = BIT(14),
- IEEE80211_TX_CTL_GREEN_FIELD = BIT(15),
- IEEE80211_TX_CTL_40_MHZ_WIDTH = BIT(16),
- IEEE80211_TX_CTL_DUP_DATA = BIT(17),
- IEEE80211_TX_CTL_SHORT_GI = BIT(18),
- IEEE80211_TX_CTL_INJECTED = BIT(19),
- IEEE80211_TX_STAT_TX_FILTERED = BIT(20),
- IEEE80211_TX_STAT_ACK = BIT(21),
- IEEE80211_TX_STAT_AMPDU = BIT(22),
- IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(23),
- IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(24),
+ IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1),
+ IEEE80211_TX_CTL_NO_ACK = BIT(2),
+ IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(3),
+ IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(4),
+ IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(5),
+ IEEE80211_TX_CTL_AMPDU = BIT(6),
+ IEEE80211_TX_CTL_INJECTED = BIT(7),
+ IEEE80211_TX_STAT_TX_FILTERED = BIT(8),
+ IEEE80211_TX_STAT_ACK = BIT(9),
+ IEEE80211_TX_STAT_AMPDU = BIT(10),
+ IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
+};
+
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_USE_RTS_CTS = BIT(0),
+ IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2),
+
+ /* rate index is an MCS rate number instead of an index */
+ IEEE80211_TX_RC_MCS = BIT(3),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(4),
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5),
+ IEEE80211_TX_RC_DUP_DATA = BIT(6),
+ IEEE80211_TX_RC_SHORT_GI = BIT(7),
};
-#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE \
- (sizeof(((struct sk_buff *)0)->cb) - 8)
-#define IEEE80211_TX_INFO_DRIVER_DATA_PTRS \
- (IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *))
+/* there are 40 bytes if you don't need the rateset to be kept */
+#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40
-/* maximum number of alternate rate retry stages */
-#define IEEE80211_TX_MAX_ALTRATE 3
+/* if you do need the rateset, then you have less space */
+#define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24
+
+/* maximum number of rate stages */
+#define IEEE80211_TX_MAX_RATES 5
/**
- * struct ieee80211_tx_altrate - alternate rate selection/status
+ * struct ieee80211_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @flags: rate control flags (&enum mac80211_rate_control_flags)
+ * @count: number of tries in this rate before going to the next rate
*
- * @rate_idx: rate index to attempt to send with
- * @limit: number of retries before fallback
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate along with the flags it used.
*/
-struct ieee80211_tx_altrate {
- s8 rate_idx;
- u8 limit;
-};
+struct ieee80211_tx_rate {
+ s8 idx;
+ u8 count;
+ u8 flags;
+} __attribute__((packed));
/**
* struct ieee80211_tx_info - skb transmit information
@@ -310,15 +316,13 @@ struct ieee80211_tx_altrate {
* it may be NULL.
*
* @flags: transmit info flags, defined above
- * @band: TBD
- * @tx_rate_idx: TBD
- * @antenna_sel_tx: TBD
+ * @band: the band to transmit on (use for checking for races)
+ * @antenna_sel_tx: antenna to use, 0 for automatic diversity
+ * @pad: padding, ignore
* @control: union for control data
* @status: union for status data
* @driver_data: array of driver_data pointers
* @retry_count: number of retries
- * @excessive_retries: set to 1 if the frame was retried many times
- * but not acknowledged
* @ampdu_ack_len: number of aggregated frames.
* relevant only if IEEE80211_TX_STATUS_AMPDU was set.
* @ampdu_ack_map: block ack bit map for the aggregation.
@@ -329,31 +333,44 @@ struct ieee80211_tx_info {
/* common information */
u32 flags;
u8 band;
- s8 tx_rate_idx;
+
u8 antenna_sel_tx;
- /* 1 byte hole */
+ /* 2 byte hole */
+ u8 pad[2];
union {
struct {
+ union {
+ /* rate control */
+ struct {
+ struct ieee80211_tx_rate rates[
+ IEEE80211_TX_MAX_RATES];
+ s8 rts_cts_rate_idx;
+ };
+ /* only needed before rate control */
+ unsigned long jiffies;
+ };
/* NB: vif can be NULL for injected frames */
struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
struct ieee80211_sta *sta;
- unsigned long jiffies;
- s8 rts_cts_rate_idx;
- u8 retry_limit;
- struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE];
} control;
struct {
+ struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
+ u8 ampdu_ack_len;
u64 ampdu_ack_map;
int ack_signal;
- struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE + 1];
- u8 retry_count;
- bool excessive_retries;
- u8 ampdu_ack_len;
+ /* 8 bytes free */
} status;
- void *driver_data[IEEE80211_TX_INFO_DRIVER_DATA_PTRS];
+ struct {
+ struct ieee80211_tx_rate driver_rates[
+ IEEE80211_TX_MAX_RATES];
+ void *rate_driver_data[
+ IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
+ };
+ void *driver_data[
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
};
};
@@ -362,6 +379,41 @@ static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
return (struct ieee80211_tx_info *)skb->cb;
}
+/**
+ * ieee80211_tx_info_clear_status - clear TX status
+ *
+ * @info: The &struct ieee80211_tx_info to be cleared.
+ *
+ * When the driver passes an skb back to mac80211, it must report
+ * a number of things in TX status. This function clears everything
+ * in the TX status but the rate control information (it does clear
+ * the count since you need to fill that in anyway).
+ *
+ * NOTE: You can only use this function if you do NOT use
+ * info->driver_data! Use info->rate_driver_data
+ * instead if you need only the less space that allows.
+ */
+static inline void
+ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
+{
+ int i;
+
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, control.rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, driver_rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != 8);
+ /* clear the rate counts */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++)
+ info->status.rates[i].count = 0;
+
+ BUILD_BUG_ON(
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23);
+ memset(&info->status.ampdu_ack_len, 0,
+ sizeof(struct ieee80211_tx_info) -
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+}
+
/**
* enum mac80211_rx_flags - receive flags
@@ -384,6 +436,9 @@ static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
* is valid. This is useful in monitor mode and necessary for beacon frames
* to enable IBSS merging.
* @RX_FLAG_SHORTPRE: Short preamble was used for this frame
+ * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
+ * @RX_FLAG_40MHZ: HT40 (40 MHz) was used
+ * @RX_FLAG_SHORT_GI: Short guard interval was used
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = 1<<0,
@@ -394,7 +449,10 @@ enum mac80211_rx_flags {
RX_FLAG_FAILED_FCS_CRC = 1<<5,
RX_FLAG_FAILED_PLCP_CRC = 1<<6,
RX_FLAG_TSFT = 1<<7,
- RX_FLAG_SHORTPRE = 1<<8
+ RX_FLAG_SHORTPRE = 1<<8,
+ RX_FLAG_HT = 1<<9,
+ RX_FLAG_40MHZ = 1<<10,
+ RX_FLAG_SHORT_GI = 1<<11,
};
/**
@@ -414,7 +472,8 @@ enum mac80211_rx_flags {
* @noise: noise when receiving this frame, in dBm.
* @qual: overall signal quality indication, in percent (0-100).
* @antenna: antenna used
- * @rate_idx: index of data rate into band's supported rates
+ * @rate_idx: index of data rate into band's supported rates or MCS index if
+ * HT rates are use (RX_FLAG_HT)
* @flag: %RX_FLAG_*
*/
struct ieee80211_rx_status {
@@ -434,21 +493,49 @@ struct ieee80211_rx_status {
*
* Flags to define PHY configuration options
*
- * @IEEE80211_CONF_SHORT_SLOT_TIME: use 802.11g short slot time
* @IEEE80211_CONF_RADIOTAP: add radiotap header at receive time (if supported)
- * @IEEE80211_CONF_SUPPORT_HT_MODE: use 802.11n HT capabilities (if supported)
* @IEEE80211_CONF_PS: Enable 802.11 power save mode
*/
enum ieee80211_conf_flags {
- /*
- * TODO: IEEE80211_CONF_SHORT_SLOT_TIME will be removed once drivers
- * have been converted to use bss_info_changed() for slot time
- * configuration
- */
- IEEE80211_CONF_SHORT_SLOT_TIME = (1<<0),
- IEEE80211_CONF_RADIOTAP = (1<<1),
- IEEE80211_CONF_SUPPORT_HT_MODE = (1<<2),
- IEEE80211_CONF_PS = (1<<3),
+ IEEE80211_CONF_RADIOTAP = (1<<0),
+ IEEE80211_CONF_PS = (1<<1),
+};
+
+/* XXX: remove all this once drivers stop trying to use it */
+static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void)
+{
+ return 0;
+}
+#define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME())
+
+struct ieee80211_ht_conf {
+ bool enabled;
+ enum nl80211_channel_type channel_type;
+};
+
+/**
+ * enum ieee80211_conf_changed - denotes which configuration changed
+ *
+ * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed
+ * @IEEE80211_CONF_CHANGE_BEACON_INTERVAL: the beacon interval changed
+ * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed
+ * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed
+ * @IEEE80211_CONF_CHANGE_PS: the PS flag changed
+ * @IEEE80211_CONF_CHANGE_POWER: the TX power changed
+ * @IEEE80211_CONF_CHANGE_CHANNEL: the channel changed
+ * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
+ * @IEEE80211_CONF_CHANGE_HT: HT configuration changed
+ */
+enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0),
+ IEEE80211_CONF_CHANGE_BEACON_INTERVAL = BIT(1),
+ IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
+ IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3),
+ IEEE80211_CONF_CHANGE_PS = BIT(4),
+ IEEE80211_CONF_CHANGE_POWER = BIT(5),
+ IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
+ IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
+ IEEE80211_CONF_CHANGE_HT = BIT(8),
};
/**
@@ -457,34 +544,31 @@ enum ieee80211_conf_flags {
* This struct indicates how the driver shall configure the hardware.
*
* @radio_enabled: when zero, driver is required to switch off the radio.
- * TODO make a flag
* @beacon_int: beacon interval (TODO make interface config)
* @listen_interval: listen interval in units of beacon interval
* @flags: configuration flags defined above
* @power_level: requested transmit power (in dBm)
- * @max_antenna_gain: maximum antenna gain (in dBi)
- * @antenna_sel_tx: transmit antenna selection, 0: default/diversity,
- * 1/2: antenna 0/1
- * @antenna_sel_rx: receive antenna selection, like @antenna_sel_tx
- * @ht_conf: describes current self configuration of 802.11n HT capabilies
- * @ht_bss_conf: describes current BSS configuration of 802.11n HT parameters
* @channel: the channel to tune to
+ * @ht: the HT configuration for the device
+ * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
+ * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ * but actually means the number of transmissions not the number of retries
+ * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
+ * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ * number of transmissions not the number of retries
*/
struct ieee80211_conf {
- int radio_enabled;
-
int beacon_int;
- u16 listen_interval;
u32 flags;
int power_level;
- int max_antenna_gain;
- u8 antenna_sel_tx;
- u8 antenna_sel_rx;
- struct ieee80211_channel *channel;
+ u16 listen_interval;
+ bool radio_enabled;
+
+ u8 long_frame_max_tx_count, short_frame_max_tx_count;
- struct ieee80211_ht_info ht_conf;
- struct ieee80211_ht_bss_info ht_bss_conf;
+ struct ieee80211_channel *channel;
+ struct ieee80211_ht_conf ht;
};
/**
@@ -494,11 +578,14 @@ struct ieee80211_conf {
* use during the life of a virtual interface.
*
* @type: type of this virtual interface
+ * @bss_conf: BSS configuration for this interface, either our own
+ * or the BSS we're associated to
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
*/
struct ieee80211_vif {
enum nl80211_iftype type;
+ struct ieee80211_bss_conf bss_conf;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
@@ -542,14 +629,12 @@ struct ieee80211_if_init_conf {
* enum ieee80211_if_conf_change - interface config change flags
*
* @IEEE80211_IFCC_BSSID: The BSSID changed.
- * @IEEE80211_IFCC_SSID: The SSID changed.
* @IEEE80211_IFCC_BEACON: The beacon for this interface changed
* (currently AP and MESH only), use ieee80211_beacon_get().
*/
enum ieee80211_if_conf_change {
IEEE80211_IFCC_BSSID = BIT(0),
- IEEE80211_IFCC_SSID = BIT(1),
- IEEE80211_IFCC_BEACON = BIT(2),
+ IEEE80211_IFCC_BEACON = BIT(1),
};
/**
@@ -557,11 +642,6 @@ enum ieee80211_if_conf_change {
*
* @changed: parameters that have changed, see &enum ieee80211_if_conf_change.
* @bssid: BSSID of the network we are associated to/creating.
- * @ssid: used (together with @ssid_len) by drivers for hardware that
- * generate beacons independently. The pointer is valid only during the
- * config_interface() call, so copy the value somewhere if you need
- * it.
- * @ssid_len: length of the @ssid field.
*
* This structure is passed to the config_interface() callback of
* &struct ieee80211_hw.
@@ -569,8 +649,6 @@ enum ieee80211_if_conf_change {
struct ieee80211_if_conf {
u32 changed;
u8 *bssid;
- u8 *ssid;
- size_t ssid_len;
};
/**
@@ -677,7 +755,7 @@ enum set_key_cmd {
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
* @supp_rates: Bitmap of supported rates (per band)
- * @ht_info: HT capabilities of this STA
+ * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information.
*/
@@ -685,7 +763,7 @@ struct ieee80211_sta {
u64 supp_rates[IEEE80211_NUM_BANDS];
u8 addr[ETH_ALEN];
u16 aid;
- struct ieee80211_ht_info ht_info;
+ struct ieee80211_sta_ht_cap ht_cap;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
@@ -695,13 +773,17 @@ struct ieee80211_sta {
* enum sta_notify_cmd - sta notify command
*
* Used with the sta_notify() callback in &struct ieee80211_ops, this
- * indicates addition and removal of a station to station table.
+ * indicates addition and removal of a station to station table,
+ * or if a associated station made a power state transition.
*
* @STA_NOTIFY_ADD: a station was added to the station table
* @STA_NOTIFY_REMOVE: a station being removed from the station table
+ * @STA_NOTIFY_SLEEP: a station is now sleeping
+ * @STA_NOTIFY_AWAKE: a sleeping station woke up
*/
enum sta_notify_cmd {
- STA_NOTIFY_ADD, STA_NOTIFY_REMOVE
+ STA_NOTIFY_ADD, STA_NOTIFY_REMOVE,
+ STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE,
};
/**
@@ -769,6 +851,14 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_SPECTRUM_MGMT:
* Hardware supports spectrum management defined in 802.11h
* Measurement, Channel Switch, Quieting, TPC
+ *
+ * @IEEE80211_HW_AMPDU_AGGREGATION:
+ * Hardware supports 11n A-MPDU aggregation.
+ *
+ * @IEEE80211_HW_NO_STACK_DYNAMIC_PS:
+ * Hardware which has dynamic power save support, meaning
+ * that power save is enabled in idle periods, and don't need support
+ * from stack.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_RX_INCLUDES_FCS = 1<<1,
@@ -780,6 +870,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SIGNAL_DBM = 1<<7,
IEEE80211_HW_NOISE_DBM = 1<<8,
IEEE80211_HW_SPECTRUM_MGMT = 1<<9,
+ IEEE80211_HW_AMPDU_AGGREGATION = 1<<10,
+ IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11,
};
/**
@@ -838,8 +930,8 @@ enum ieee80211_hw_flags {
* @sta_data_size: size (in bytes) of the drv_priv data area
* within &struct ieee80211_sta.
*
- * @max_altrates: maximum number of alternate rate retry stages
- * @max_altrate_tries: maximum number of tries for each stage
+ * @max_rates: maximum number of alternate rate retry stages
+ * @max_rate_tries: maximum number of tries for each stage
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
@@ -856,12 +948,10 @@ struct ieee80211_hw {
u16 ampdu_queues;
u16 max_listen_interval;
s8 max_signal;
- u8 max_altrates;
- u8 max_altrate_tries;
+ u8 max_rates;
+ u8 max_rate_tries;
};
-struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy);
-
/**
* SET_IEEE80211_DEV - set device for 802.11 hardware
*
@@ -874,7 +964,7 @@ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev
}
/**
- * SET_IEEE80211_PERM_ADDR - set the permanenet MAC address for 802.11 hardware
+ * SET_IEEE80211_PERM_ADDR - set the permanent MAC address for 802.11 hardware
*
* @hw: the &struct ieee80211_hw to set the MAC address for
* @addr: the address to set
@@ -898,9 +988,9 @@ static inline struct ieee80211_rate *
ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c)
{
- if (WARN_ON(c->tx_rate_idx < 0))
+ if (WARN_ON(c->control.rates[0].idx < 0))
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->tx_rate_idx];
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx];
}
static inline struct ieee80211_rate *
@@ -916,9 +1006,9 @@ static inline struct ieee80211_rate *
ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c, int idx)
{
- if (c->control.retries[idx].rate_idx < 0)
+ if (c->control.rates[idx + 1].idx < 0)
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->control.retries[idx].rate_idx];
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx];
}
/**
@@ -967,7 +1057,7 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
* This happens everytime the iv16 wraps around (every 65536 packets). The
* set_key() call will happen only once for each key (unless the AP did
* rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
- * provided by udpate_tkip_key only. The trigger that makes mac80211 call this
+ * provided by update_tkip_key only. The trigger that makes mac80211 call this
* handler is software decryption with wrap around of iv16.
*/
@@ -1060,12 +1150,14 @@ enum ieee80211_filter_flags {
* @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
* @IEEE80211_AMPDU_TX_START: start Tx aggregation
* @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
+ * @IEEE80211_AMPDU_TX_RESUME: resume TX aggregation
*/
enum ieee80211_ampdu_mlme_action {
IEEE80211_AMPDU_RX_START,
IEEE80211_AMPDU_RX_STOP,
IEEE80211_AMPDU_TX_START,
IEEE80211_AMPDU_TX_STOP,
+ IEEE80211_AMPDU_TX_RESUME,
};
/**
@@ -1101,7 +1193,7 @@ enum ieee80211_ampdu_mlme_action {
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
- * enabled. Because it is not called for monitor mode devices, @open
+ * enabled. Because it is not called for monitor mode devices, @start
* and @stop must be implemented.
* The driver should perform any initialization it needs before
* the device can be enabled. The initial configuration for the
@@ -1163,14 +1255,9 @@ enum ieee80211_ampdu_mlme_action {
*
* @set_rts_threshold: Configuration of RTS threshold (if device needs it)
*
- * @set_frag_threshold: Configuration of fragmentation threshold. Assign this if
- * the device does fragmentation by itself; if this method is assigned then
- * the stack will not do fragmentation.
- *
- * @set_retry_limit: Configuration of retry limits (if device needs it)
- *
- * @sta_notify: Notifies low level driver about addition or removal
- * of assocaited station or AP.
+ * @sta_notify: Notifies low level driver about addition, removal or power
+ * state transition of an associated station, AP, IBSS/WDS/mesh peer etc.
+ * Must be atomic.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
@@ -1194,8 +1281,6 @@ enum ieee80211_ampdu_mlme_action {
* This is needed only for IBSS mode and the result of this function is
* used to determine whether to reply to Probe Requests.
*
- * @conf_ht: Configures low level driver with 802.11n HT data. Must be atomic.
- *
* @ampdu_action: Perform a certain A-MPDU action
* The RA/TID combination determines the destination and TID we want
* the ampdu action to be performed for. The action is defined through
@@ -1211,7 +1296,7 @@ struct ieee80211_ops {
struct ieee80211_if_init_conf *conf);
void (*remove_interface)(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
- int (*config)(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+ int (*config)(struct ieee80211_hw *hw, u32 changed);
int (*config_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf);
@@ -1237,9 +1322,6 @@ struct ieee80211_ops {
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
u32 *iv32, u16 *iv16);
int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
- int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value);
- int (*set_retry_limit)(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retr);
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd, struct ieee80211_sta *sta);
int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
@@ -1472,7 +1554,7 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
* the next beacon frame from the 802.11 code. The low-level is responsible
* for calling this function before beacon data is needed (e.g., based on
* hardware interrupt). Returned skb is used only once and low-level driver
- * is responsible of freeing it.
+ * is responsible for freeing it.
*/
struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
@@ -1803,24 +1885,38 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
/* Rate control API */
+
/**
- * struct rate_selection - rate information for/from rate control algorithms
- *
- * @rate_idx: selected transmission rate index
- * @nonerp_idx: Non-ERP rate to use instead if ERP cannot be used
- * @probe_idx: rate for probing (or -1)
- * @max_rate_idx: maximum rate index that can be used, this is
- * input to the algorithm and will be enforced
- */
-struct rate_selection {
- s8 rate_idx, nonerp_idx, probe_idx, max_rate_idx;
+ * struct ieee80211_tx_rate_control - rate control information for/from RC algo
+ *
+ * @hw: The hardware the algorithm is invoked for.
+ * @sband: The band this frame is being transmitted on.
+ * @bss_conf: the current BSS configuration
+ * @reported_rate: The rate control algorithm can fill this in to indicate
+ * which rate should be reported to userspace as the current rate and
+ * used for rate calculations in the mesh network.
+ * @rts: whether RTS will be used for this frame because it is longer than the
+ * RTS threshold
+ * @short_preamble: whether mac80211 will request short-preamble transmission
+ * if the selected rate supports it
+ * @max_rate_idx: user-requested maximum rate (not MCS for now)
+ * @skb: the skb that will be transmitted, the control information in it needs
+ * to be filled in
+ */
+struct ieee80211_tx_rate_control {
+ struct ieee80211_hw *hw;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_conf *bss_conf;
+ struct sk_buff *skb;
+ struct ieee80211_tx_rate reported_rate;
+ bool rts, short_preamble;
+ u8 max_rate_idx;
};
struct rate_control_ops {
struct module *module;
const char *name;
void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
- void (*clear)(void *priv);
void (*free)(void *priv);
void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
@@ -1832,10 +1928,8 @@ struct rate_control_ops {
void (*tx_status)(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb);
- void (*get_rate)(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb,
- struct rate_selection *sel);
+ void (*get_rate)(void *priv, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc);
void (*add_sta_debugfs)(void *priv, void *priv_sta,
struct dentry *dir);
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 11dd0137c6a5..ce532f2222ce 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -108,6 +108,20 @@ extern void ndisc_send_redirect(struct sk_buff *skb,
extern int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir);
+extern struct sk_buff *ndisc_build_skb(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h,
+ const struct in6_addr *target,
+ int llinfo);
+
+extern void ndisc_send_skb(struct sk_buff *skb,
+ struct net_device *dev,
+ struct neighbour *neigh,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h);
+
/*
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index aa4b708654a4..d8d790e56d3d 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -180,9 +180,6 @@ struct neigh_table
__u32 hash_rnd;
unsigned int hash_chain_gc;
struct pneigh_entry **phash_buckets;
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *pde;
-#endif
};
/* flags for neigh_update() */
@@ -223,11 +220,7 @@ extern void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *p
static inline
struct net *neigh_parms_net(const struct neigh_parms *parms)
{
-#ifdef CONFIG_NET_NS
- return parms->net;
-#else
- return &init_net;
-#endif
+ return read_pnet(&parms->net);
}
extern unsigned long neigh_rand_reach_time(unsigned long base);
@@ -244,11 +237,7 @@ extern int pneigh_delete(struct neigh_table *tbl, struct net *net, const void
static inline
struct net *pneigh_net(const struct pneigh_entry *pneigh)
{
-#ifdef CONFIG_NET_NS
- return pneigh->net;
-#else
- return &init_net;
-#endif
+ return read_pnet(&pneigh->net);
}
extern void neigh_app_ns(struct neighbour *n);
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 700c53a3c6fa..6fc13d905c5f 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -19,6 +19,7 @@
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
#include <net/netns/conntrack.h>
#endif
+#include <net/netns/xfrm.h>
struct proc_dir_entry;
struct net_device;
@@ -74,6 +75,9 @@ struct net {
struct netns_ct ct;
#endif
#endif
+#ifdef CONFIG_XFRM
+ struct netns_xfrm xfrm;
+#endif
struct net_generic *gen;
};
@@ -192,6 +196,24 @@ static inline void release_net(struct net *net)
}
#endif
+#ifdef CONFIG_NET_NS
+
+static inline void write_pnet(struct net **pnet, struct net *net)
+{
+ *pnet = net;
+}
+
+static inline struct net *read_pnet(struct net * const *pnet)
+{
+ return *pnet;
+}
+
+#else
+
+#define write_pnet(pnet, net) do { (void)(net);} while (0)
+#define read_pnet(pnet) (&init_net)
+
+#endif
#define for_each_net(VAR) \
list_for_each_entry(VAR, &net_namespace_list, list)
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index b76a8685b5b5..2e0c53641cbe 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -199,7 +199,7 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple);
extern void nf_conntrack_hash_insert(struct nf_conn *ct);
-extern void nf_conntrack_flush(struct net *net);
+extern void nf_conntrack_flush(struct net *net, u32 pid, int report);
extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
unsigned int nhoff, u_int16_t l3num,
@@ -298,5 +298,8 @@ do { \
local_bh_enable(); \
} while (0)
+#define MODULE_ALIAS_NFCT_HELPER(helper) \
+ MODULE_ALIAS("nfct-helper-" helper)
+
#endif /* __KERNEL__ */
#endif /* _NF_CONNTRACK_H */
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index 1285ff26a014..0ff0dc69ca4a 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -17,6 +17,13 @@ struct nf_conntrack_ecache {
unsigned int events;
};
+/* This structure is passed to event handler */
+struct nf_ct_event {
+ struct nf_conn *ct;
+ u32 pid;
+ int report;
+};
+
extern struct atomic_notifier_head nf_conntrack_chain;
extern int nf_conntrack_register_notifier(struct notifier_block *nb);
extern int nf_conntrack_unregister_notifier(struct notifier_block *nb);
@@ -39,22 +46,56 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
local_bh_enable();
}
-static inline void nf_conntrack_event(enum ip_conntrack_events event,
- struct nf_conn *ct)
+static inline void
+nf_conntrack_event_report(enum ip_conntrack_events event,
+ struct nf_conn *ct,
+ u32 pid,
+ int report)
{
+ struct nf_ct_event item = {
+ .ct = ct,
+ .pid = pid,
+ .report = report
+ };
if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct))
- atomic_notifier_call_chain(&nf_conntrack_chain, event, ct);
+ atomic_notifier_call_chain(&nf_conntrack_chain, event, &item);
}
+static inline void
+nf_conntrack_event(enum ip_conntrack_events event, struct nf_conn *ct)
+{
+ nf_conntrack_event_report(event, ct, 0, 0);
+}
+
+struct nf_exp_event {
+ struct nf_conntrack_expect *exp;
+ u32 pid;
+ int report;
+};
+
extern struct atomic_notifier_head nf_ct_expect_chain;
extern int nf_ct_expect_register_notifier(struct notifier_block *nb);
extern int nf_ct_expect_unregister_notifier(struct notifier_block *nb);
static inline void
+nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
+ struct nf_conntrack_expect *exp,
+ u32 pid,
+ int report)
+{
+ struct nf_exp_event item = {
+ .exp = exp,
+ .pid = pid,
+ .report = report
+ };
+ atomic_notifier_call_chain(&nf_ct_expect_chain, event, &item);
+}
+
+static inline void
nf_ct_expect_event(enum ip_conntrack_expect_events event,
struct nf_conntrack_expect *exp)
{
- atomic_notifier_call_chain(&nf_ct_expect_chain, event, exp);
+ nf_ct_expect_event_report(event, exp, 0, 0);
}
extern int nf_conntrack_ecache_init(struct net *net);
@@ -66,9 +107,17 @@ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
struct nf_conn *ct) {}
static inline void nf_conntrack_event(enum ip_conntrack_events event,
struct nf_conn *ct) {}
+static inline void nf_conntrack_event_report(enum ip_conntrack_events event,
+ struct nf_conn *ct,
+ u32 pid,
+ int report) {}
static inline void nf_ct_deliver_cached_events(const struct nf_conn *ct) {}
static inline void nf_ct_expect_event(enum ip_conntrack_expect_events event,
struct nf_conntrack_expect *exp) {}
+static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e,
+ struct nf_conntrack_expect *exp,
+ u32 pid,
+ int report) {}
static inline void nf_ct_event_cache_flush(struct net *net) {}
static inline int nf_conntrack_ecache_init(struct net *net)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 37a7fc1164b0..ab17a159ac66 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -100,6 +100,8 @@ void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, u_int8_t,
u_int8_t, const __be16 *, const __be16 *);
void nf_ct_expect_put(struct nf_conntrack_expect *exp);
int nf_ct_expect_related(struct nf_conntrack_expect *expect);
+int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
+ u32 pid, int report);
#endif /*_NF_CONNTRACK_EXPECT_H*/
diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h
index f8060ab5a083..66d65a7caa39 100644
--- a/include/net/netfilter/nf_conntrack_helper.h
+++ b/include/net/netfilter/nf_conntrack_helper.h
@@ -39,9 +39,6 @@ struct nf_conntrack_helper
};
extern struct nf_conntrack_helper *
-__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple);
-
-extern struct nf_conntrack_helper *
__nf_conntrack_helper_find_byname(const char *name);
extern int nf_conntrack_helper_register(struct nf_conntrack_helper *);
@@ -49,6 +46,8 @@ extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
+extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags);
+
static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct)
{
return nf_ct_ext_find(ct, NF_CT_EXT_HELPER);
diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h
index 7f2f43c77284..debdaf75cecf 100644
--- a/include/net/netfilter/nf_conntrack_l4proto.h
+++ b/include/net/netfilter/nf_conntrack_l4proto.h
@@ -129,7 +129,7 @@ extern const struct nla_policy nf_ct_port_nla_policy[];
&& net_ratelimit())
#endif
#else
-#define LOG_INVALID(net, proto) 0
+static inline int LOG_INVALID(struct net *net, int proto) { return 0; }
#endif /* CONFIG_SYSCTL */
#endif /*_NF_CONNTRACK_PROTOCOL_H*/
diff --git a/include/net/netfilter/nf_conntrack_tuple.h b/include/net/netfilter/nf_conntrack_tuple.h
index a6874ba22d54..f2f6aa73dc10 100644
--- a/include/net/netfilter/nf_conntrack_tuple.h
+++ b/include/net/netfilter/nf_conntrack_tuple.h
@@ -112,20 +112,20 @@ struct nf_conntrack_tuple_mask
static inline void nf_ct_dump_tuple_ip(const struct nf_conntrack_tuple *t)
{
#ifdef DEBUG
- printk("tuple %p: %u " NIPQUAD_FMT ":%hu -> " NIPQUAD_FMT ":%hu\n",
+ printk("tuple %p: %u %pI4:%hu -> %pI4:%hu\n",
t, t->dst.protonum,
- NIPQUAD(t->src.u3.ip), ntohs(t->src.u.all),
- NIPQUAD(t->dst.u3.ip), ntohs(t->dst.u.all));
+ &t->src.u3.ip, ntohs(t->src.u.all),
+ &t->dst.u3.ip, ntohs(t->dst.u.all));
#endif
}
static inline void nf_ct_dump_tuple_ipv6(const struct nf_conntrack_tuple *t)
{
#ifdef DEBUG
- printk("tuple %p: %u " NIP6_FMT " %hu -> " NIP6_FMT " %hu\n",
+ printk("tuple %p: %u %pI6 %hu -> %pI6 %hu\n",
t, t->dst.protonum,
- NIP6(*(struct in6_addr *)t->src.u3.all), ntohs(t->src.u.all),
- NIP6(*(struct in6_addr *)t->dst.u3.all), ntohs(t->dst.u.all));
+ t->src.u3.all, ntohs(t->src.u.all),
+ t->dst.u3.all, ntohs(t->dst.u.all));
#endif
}
diff --git a/include/net/netfilter/nfnetlink_log.h b/include/net/netfilter/nfnetlink_log.h
new file mode 100644
index 000000000000..b0569ff0775e
--- /dev/null
+++ b/include/net/netfilter/nfnetlink_log.h
@@ -0,0 +1,14 @@
+#ifndef _KER_NFNETLINK_LOG_H
+#define _KER_NFNETLINK_LOG_H
+
+void
+nfulnl_log_packet(u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *li_user,
+ const char *prefix);
+
+#endif /* _KER_NFNETLINK_LOG_H */
+
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 3643bbb8e585..8a6150a3f4c7 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -233,7 +233,7 @@ extern int nla_parse(struct nlattr *tb[], int maxtype,
extern struct nlattr * nla_find(struct nlattr *head, int len, int attrtype);
extern size_t nla_strlcpy(char *dst, const struct nlattr *nla,
size_t dstsize);
-extern int nla_memcpy(void *dest, struct nlattr *src, int count);
+extern int nla_memcpy(void *dest, const struct nlattr *src, int count);
extern int nla_memcmp(const struct nlattr *nla, const void *data,
size_t size);
extern int nla_strcmp(const struct nlattr *nla, const char *str);
@@ -332,7 +332,7 @@ static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
*/
static inline int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
{
- return (remaining >= sizeof(struct nlmsghdr) &&
+ return (remaining >= (int) sizeof(struct nlmsghdr) &&
nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
nlh->nlmsg_len <= remaining);
}
@@ -741,7 +741,7 @@ static inline struct nlattr *nla_find_nested(struct nlattr *nla, int attrtype)
* See nla_parse()
*/
static inline int nla_parse_nested(struct nlattr *tb[], int maxtype,
- struct nlattr *nla,
+ const struct nlattr *nla,
const struct nla_policy *policy)
{
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
@@ -875,7 +875,7 @@ static inline int nla_put_msecs(struct sk_buff *skb, int attrtype,
* nla_get_u32 - return payload of u32 attribute
* @nla: u32 netlink attribute
*/
-static inline u32 nla_get_u32(struct nlattr *nla)
+static inline u32 nla_get_u32(const struct nlattr *nla)
{
return *(u32 *) nla_data(nla);
}
@@ -884,7 +884,7 @@ static inline u32 nla_get_u32(struct nlattr *nla)
* nla_get_be32 - return payload of __be32 attribute
* @nla: __be32 netlink attribute
*/
-static inline __be32 nla_get_be32(struct nlattr *nla)
+static inline __be32 nla_get_be32(const struct nlattr *nla)
{
return *(__be32 *) nla_data(nla);
}
@@ -893,7 +893,7 @@ static inline __be32 nla_get_be32(struct nlattr *nla)
* nla_get_u16 - return payload of u16 attribute
* @nla: u16 netlink attribute
*/
-static inline u16 nla_get_u16(struct nlattr *nla)
+static inline u16 nla_get_u16(const struct nlattr *nla)
{
return *(u16 *) nla_data(nla);
}
@@ -902,7 +902,7 @@ static inline u16 nla_get_u16(struct nlattr *nla)
* nla_get_be16 - return payload of __be16 attribute
* @nla: __be16 netlink attribute
*/
-static inline __be16 nla_get_be16(struct nlattr *nla)
+static inline __be16 nla_get_be16(const struct nlattr *nla)
{
return *(__be16 *) nla_data(nla);
}
@@ -911,7 +911,7 @@ static inline __be16 nla_get_be16(struct nlattr *nla)
* nla_get_le16 - return payload of __le16 attribute
* @nla: __le16 netlink attribute
*/
-static inline __le16 nla_get_le16(struct nlattr *nla)
+static inline __le16 nla_get_le16(const struct nlattr *nla)
{
return *(__le16 *) nla_data(nla);
}
@@ -920,7 +920,7 @@ static inline __le16 nla_get_le16(struct nlattr *nla)
* nla_get_u8 - return payload of u8 attribute
* @nla: u8 netlink attribute
*/
-static inline u8 nla_get_u8(struct nlattr *nla)
+static inline u8 nla_get_u8(const struct nlattr *nla)
{
return *(u8 *) nla_data(nla);
}
@@ -929,7 +929,7 @@ static inline u8 nla_get_u8(struct nlattr *nla)
* nla_get_u64 - return payload of u64 attribute
* @nla: u64 netlink attribute
*/
-static inline u64 nla_get_u64(struct nlattr *nla)
+static inline u64 nla_get_u64(const struct nlattr *nla)
{
u64 tmp;
@@ -942,7 +942,7 @@ static inline u64 nla_get_u64(struct nlattr *nla)
* nla_get_flag - return payload of flag attribute
* @nla: flag netlink attribute
*/
-static inline int nla_get_flag(struct nlattr *nla)
+static inline int nla_get_flag(const struct nlattr *nla)
{
return !!nla;
}
@@ -953,7 +953,7 @@ static inline int nla_get_flag(struct nlattr *nla)
*
* Returns the number of milliseconds in jiffies.
*/
-static inline unsigned long nla_get_msecs(struct nlattr *nla)
+static inline unsigned long nla_get_msecs(const struct nlattr *nla)
{
u64 msecs = nla_get_u64(nla);
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index ece1c926b5d1..977f482d97a9 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -49,6 +49,8 @@ struct netns_ipv4 {
int sysctl_icmp_ratelimit;
int sysctl_icmp_ratemask;
int sysctl_icmp_errors_use_inbound_ifaddr;
+ int sysctl_rt_cache_rebuild_count;
+ int current_rt_cache_rebuild_count;
struct timer_list rt_secret_timer;
atomic_t rt_genid;
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 2932721180c0..afab4e4cbac7 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -55,5 +55,17 @@ struct netns_ipv6 {
struct sock *ndisc_sk;
struct sock *tcp_sk;
struct sock *igmp_sk;
+#ifdef CONFIG_IPV6_MROUTE
+ struct sock *mroute6_sk;
+ struct mfc6_cache **mfc6_cache_array;
+ struct mif_device *vif6_table;
+ int maxvif;
+ atomic_t cache_resolve_queue_len;
+ int mroute_do_assert;
+ int mroute_do_pim;
+#ifdef CONFIG_IPV6_PIMSM_V2
+ int mroute_reg_vif_num;
+#endif
+#endif
};
#endif
diff --git a/include/net/netns/mib.h b/include/net/netns/mib.h
index 10cb7c336de5..0b44112e2366 100644
--- a/include/net/netns/mib.h
+++ b/include/net/netns/mib.h
@@ -20,6 +20,9 @@ struct netns_mib {
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
DEFINE_SNMP_STAT(struct icmpv6msg_mib, icmpv6msg_statistics);
#endif
+#ifdef CONFIG_XFRM_STATISTICS
+ DEFINE_SNMP_STAT(struct linux_xfrm_mib, xfrm_statistics);
+#endif
};
#endif
diff --git a/include/net/netns/x_tables.h b/include/net/netns/x_tables.h
index b8093971ccb4..9554a644a8f8 100644
--- a/include/net/netns/x_tables.h
+++ b/include/net/netns/x_tables.h
@@ -4,7 +4,12 @@
#include <linux/list.h>
#include <linux/netfilter.h>
+struct ebt_table;
+
struct netns_xt {
struct list_head tables[NFPROTO_NUMPROTO];
+ struct ebt_table *broute_table;
+ struct ebt_table *frame_filter;
+ struct ebt_table *frame_nat;
};
#endif
diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h
new file mode 100644
index 000000000000..1ba912749caa
--- /dev/null
+++ b/include/net/netns/xfrm.h
@@ -0,0 +1,56 @@
+#ifndef __NETNS_XFRM_H
+#define __NETNS_XFRM_H
+
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/xfrm.h>
+
+struct ctl_table_header;
+
+struct xfrm_policy_hash {
+ struct hlist_head *table;
+ unsigned int hmask;
+};
+
+struct netns_xfrm {
+ struct list_head state_all;
+ /*
+ * Hash table to find appropriate SA towards given target (endpoint of
+ * tunnel or destination of transport mode) allowed by selector.
+ *
+ * Main use is finding SA after policy selected tunnel or transport
+ * mode. Also, it can be used by ah/esp icmp error handler to find
+ * offending SA.
+ */
+ struct hlist_head *state_bydst;
+ struct hlist_head *state_bysrc;
+ struct hlist_head *state_byspi;
+ unsigned int state_hmask;
+ unsigned int state_num;
+ struct work_struct state_hash_work;
+ struct hlist_head state_gc_list;
+ struct work_struct state_gc_work;
+
+ wait_queue_head_t km_waitq;
+
+ struct list_head policy_all;
+ struct hlist_head *policy_byidx;
+ unsigned int policy_idx_hmask;
+ struct hlist_head policy_inexact[XFRM_POLICY_MAX * 2];
+ struct xfrm_policy_hash policy_bydst[XFRM_POLICY_MAX * 2];
+ unsigned int policy_count[XFRM_POLICY_MAX * 2];
+ struct work_struct policy_hash_work;
+
+ struct sock *nlsk;
+
+ u32 sysctl_aevent_etime;
+ u32 sysctl_aevent_rseqth;
+ int sysctl_larval_drop;
+ u32 sysctl_acq_expires;
+#ifdef CONFIG_SYSCTL
+ struct ctl_table_header *sysctl_hdr;
+#endif
+};
+
+#endif
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index fcd793030e4d..4c61cdce4e5f 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -35,12 +35,12 @@ struct pep_sock {
struct sock *listener;
struct sk_buff_head ctrlreq_queue;
#define PNPIPE_CTRLREQ_MAX 10
+ atomic_t tx_credits;
int ifindex;
u16 peer_type; /* peer type/subtype */
u8 pipe_handle;
u8 rx_credits;
- u8 tx_credits;
u8 rx_fc; /* RX flow control */
u8 tx_fc; /* TX flow control */
u8 init_enable; /* auto-enable at creation */
diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h
index c6a245184460..057b0a8a2885 100644
--- a/include/net/phonet/phonet.h
+++ b/include/net/phonet/phonet.h
@@ -46,7 +46,7 @@ static inline struct pn_sock *pn_sk(struct sock *sk)
extern const struct proto_ops phonet_dgram_ops;
-struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *sa);
+struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *sa);
void phonet_get_local_port_range(int *min, int *max);
void pn_sock_hash(struct sock *sk);
void pn_sock_unhash(struct sock *sk);
diff --git a/include/net/phonet/pn_dev.h b/include/net/phonet/pn_dev.h
index bbd2a836e04c..aa1c59a1d33f 100644
--- a/include/net/phonet/pn_dev.h
+++ b/include/net/phonet/pn_dev.h
@@ -43,7 +43,7 @@ struct net_device *phonet_device_get(struct net *net);
int phonet_address_add(struct net_device *dev, u8 addr);
int phonet_address_del(struct net_device *dev, u8 addr);
u8 phonet_address_get(struct net_device *dev, u8 addr);
-int phonet_address_lookup(u8 addr);
+int phonet_address_lookup(struct net *net, u8 addr);
#define PN_NO_ADDR 0xff
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index aa9e282db485..d1ca31444644 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -246,7 +246,7 @@ struct tcf_ematch_ops
};
extern int tcf_em_register(struct tcf_ematch_ops *);
-extern int tcf_em_unregister(struct tcf_ematch_ops *);
+extern void tcf_em_unregister(struct tcf_ematch_ops *);
extern int tcf_em_tree_validate(struct tcf_proto *, struct nlattr *,
struct tcf_ematch_tree *);
extern void tcf_em_tree_destroy(struct tcf_proto *, struct tcf_ematch_tree *);
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 8d024d7cb741..cb2965aa1b62 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -39,6 +39,9 @@ struct net_protocol {
int (*gso_send_check)(struct sk_buff *skb);
struct sk_buff *(*gso_segment)(struct sk_buff *skb,
int features);
+ struct sk_buff **(*gro_receive)(struct sk_buff **head,
+ struct sk_buff *skb);
+ int (*gro_complete)(struct sk_buff *skb);
unsigned int no_policy:1,
netns_ok:1;
};
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 3fe49d808957..f8c47429044a 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -53,7 +53,6 @@ struct Qdisc
atomic_t refcnt;
unsigned long state;
struct sk_buff *gso_skb;
- struct sk_buff_head requeue;
struct sk_buff_head q;
struct netdev_queue *dev_queue;
struct Qdisc *next_sched;
@@ -111,7 +110,7 @@ struct Qdisc_ops
int (*enqueue)(struct sk_buff *, struct Qdisc *);
struct sk_buff * (*dequeue)(struct Qdisc *);
- int (*requeue)(struct sk_buff *, struct Qdisc *);
+ struct sk_buff * (*peek)(struct Qdisc *);
unsigned int (*drop)(struct Qdisc *);
int (*init)(struct Qdisc *, struct nlattr *arg);
@@ -432,19 +431,38 @@ static inline struct sk_buff *qdisc_dequeue_tail(struct Qdisc *sch)
return __qdisc_dequeue_tail(sch, &sch->q);
}
-static inline int __qdisc_requeue(struct sk_buff *skb, struct Qdisc *sch,
- struct sk_buff_head *list)
+static inline struct sk_buff *qdisc_peek_head(struct Qdisc *sch)
{
- __skb_queue_head(list, skb);
- sch->qstats.backlog += qdisc_pkt_len(skb);
- sch->qstats.requeues++;
+ return skb_peek(&sch->q);
+}
- return NET_XMIT_SUCCESS;
+/* generic pseudo peek method for non-work-conserving qdisc */
+static inline struct sk_buff *qdisc_peek_dequeued(struct Qdisc *sch)
+{
+ /* we can reuse ->gso_skb because peek isn't called for root qdiscs */
+ if (!sch->gso_skb) {
+ sch->gso_skb = sch->dequeue(sch);
+ if (sch->gso_skb)
+ /* it's still part of the queue */
+ sch->q.qlen++;
+ }
+
+ return sch->gso_skb;
}
-static inline int qdisc_requeue(struct sk_buff *skb, struct Qdisc *sch)
+/* use instead of qdisc->dequeue() for all qdiscs queried with ->peek() */
+static inline struct sk_buff *qdisc_dequeue_peeked(struct Qdisc *sch)
{
- return __qdisc_requeue(skb, sch, &sch->q);
+ struct sk_buff *skb = sch->gso_skb;
+
+ if (skb) {
+ sch->gso_skb = NULL;
+ sch->q.qlen--;
+ } else {
+ skb = sch->dequeue(sch);
+ }
+
+ return skb;
}
static inline void __qdisc_reset_queue(struct Qdisc *sch,
diff --git a/include/net/scm.h b/include/net/scm.h
index 33e9986beb86..f45bb6eca7d4 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -55,8 +55,8 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm)
{
struct task_struct *p = current;
- scm->creds.uid = p->uid;
- scm->creds.gid = p->gid;
+ scm->creds.uid = current_uid();
+ scm->creds.gid = current_gid();
scm->creds.pid = task_tgid_vnr(p);
scm->fp = NULL;
scm->seq = 0;
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ed71b110edf7..bbb7742195b0 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -138,6 +138,7 @@ void sctp_write_space(struct sock *sk);
unsigned int sctp_poll(struct file *file, struct socket *sock,
poll_table *wait);
void sctp_sock_rfree(struct sk_buff *skb);
+extern struct percpu_counter sctp_sockets_allocated;
/*
* sctp/primitive.c
@@ -285,15 +286,15 @@ extern int sctp_debug_flag;
if (sctp_debug_flag) { \
if (saddr->sa.sa_family == AF_INET6) { \
printk(KERN_DEBUG \
- lead NIP6_FMT trail, \
+ lead "%pI6" trail, \
leadparm, \
- NIP6(saddr->v6.sin6_addr), \
+ &saddr->v6.sin6_addr, \
otherparms); \
} else { \
printk(KERN_DEBUG \
- lead NIPQUAD_FMT trail, \
+ lead "%pI4" trail, \
leadparm, \
- NIPQUAD(saddr->v4.sin_addr.s_addr), \
+ &saddr->v4.sin_addr.s_addr, \
otherparms); \
} \
}
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index f205b10f0ab9..b259fc5798fb 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -118,6 +118,8 @@ enum sctp_optname {
#define SCTP_PEER_AUTH_CHUNKS SCTP_PEER_AUTH_CHUNKS
SCTP_LOCAL_AUTH_CHUNKS, /* Read only */
#define SCTP_LOCAL_AUTH_CHUNKS SCTP_LOCAL_AUTH_CHUNKS
+ SCTP_GET_ASSOC_NUMBER, /* Read only */
+#define SCTP_GET_ASSOC_NUMBER SCTP_GET_ASSOC_NUMBER
/* Internal Socket Options. Some of the sctp library functions are
diff --git a/include/net/sock.h b/include/net/sock.h
index 2f47107f6d0f..5a3a151bd730 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -42,6 +42,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/list_nulls.h>
#include <linux/timer.h>
#include <linux/cache.h>
#include <linux/module.h>
@@ -52,6 +53,7 @@
#include <linux/security.h>
#include <linux/filter.h>
+#include <linux/rculist_nulls.h>
#include <asm/atomic.h>
#include <net/dst.h>
@@ -106,6 +108,7 @@ struct net;
* @skc_reuse: %SO_REUSEADDR setting
* @skc_bound_dev_if: bound device index if != 0
* @skc_node: main hash linkage for various protocol lookup tables
+ * @skc_nulls_node: main hash linkage for UDP/UDP-Lite protocol
* @skc_bind_node: bind hash linkage for various protocol lookup tables
* @skc_refcnt: reference count
* @skc_hash: hash value used with various protocol lookup tables
@@ -120,7 +123,10 @@ struct sock_common {
volatile unsigned char skc_state;
unsigned char skc_reuse;
int skc_bound_dev_if;
- struct hlist_node skc_node;
+ union {
+ struct hlist_node skc_node;
+ struct hlist_nulls_node skc_nulls_node;
+ };
struct hlist_node skc_bind_node;
atomic_t skc_refcnt;
unsigned int skc_hash;
@@ -206,6 +212,7 @@ struct sock {
#define sk_reuse __sk_common.skc_reuse
#define sk_bound_dev_if __sk_common.skc_bound_dev_if
#define sk_node __sk_common.skc_node
+#define sk_nulls_node __sk_common.skc_nulls_node
#define sk_bind_node __sk_common.skc_bind_node
#define sk_refcnt __sk_common.skc_refcnt
#define sk_hash __sk_common.skc_hash
@@ -229,7 +236,9 @@ struct sock {
} sk_backlog;
wait_queue_head_t *sk_sleep;
struct dst_entry *sk_dst_cache;
+#ifdef CONFIG_XFRM
struct xfrm_policy *sk_policy[2];
+#endif
rwlock_t sk_dst_lock;
atomic_t sk_rmem_alloc;
atomic_t sk_wmem_alloc;
@@ -237,7 +246,9 @@ struct sock {
int sk_sndbuf;
struct sk_buff_head sk_receive_queue;
struct sk_buff_head sk_write_queue;
+#ifdef CONFIG_NET_DMA
struct sk_buff_head sk_async_wait_queue;
+#endif
int sk_wmem_queued;
int sk_forward_alloc;
gfp_t sk_allocation;
@@ -269,7 +280,9 @@ struct sock {
struct sk_buff *sk_send_head;
__u32 sk_sndmsg_off;
int sk_write_pending;
+#ifdef CONFIG_SECURITY
void *sk_security;
+#endif
__u32 sk_mark;
/* XXX 4 bytes hole on 64 bit */
void (*sk_state_change)(struct sock *sk);
@@ -294,12 +307,30 @@ static inline struct sock *sk_head(const struct hlist_head *head)
return hlist_empty(head) ? NULL : __sk_head(head);
}
+static inline struct sock *__sk_nulls_head(const struct hlist_nulls_head *head)
+{
+ return hlist_nulls_entry(head->first, struct sock, sk_nulls_node);
+}
+
+static inline struct sock *sk_nulls_head(const struct hlist_nulls_head *head)
+{
+ return hlist_nulls_empty(head) ? NULL : __sk_nulls_head(head);
+}
+
static inline struct sock *sk_next(const struct sock *sk)
{
return sk->sk_node.next ?
hlist_entry(sk->sk_node.next, struct sock, sk_node) : NULL;
}
+static inline struct sock *sk_nulls_next(const struct sock *sk)
+{
+ return (!is_a_nulls(sk->sk_nulls_node.next)) ?
+ hlist_nulls_entry(sk->sk_nulls_node.next,
+ struct sock, sk_nulls_node) :
+ NULL;
+}
+
static inline int sk_unhashed(const struct sock *sk)
{
return hlist_unhashed(&sk->sk_node);
@@ -315,6 +346,11 @@ static __inline__ void sk_node_init(struct hlist_node *node)
node->pprev = NULL;
}
+static __inline__ void sk_nulls_node_init(struct hlist_nulls_node *node)
+{
+ node->pprev = NULL;
+}
+
static __inline__ void __sk_del_node(struct sock *sk)
{
__hlist_del(&sk->sk_node);
@@ -361,6 +397,27 @@ static __inline__ int sk_del_node_init(struct sock *sk)
return rc;
}
+static __inline__ int __sk_nulls_del_node_init_rcu(struct sock *sk)
+{
+ if (sk_hashed(sk)) {
+ hlist_nulls_del_init_rcu(&sk->sk_nulls_node);
+ return 1;
+ }
+ return 0;
+}
+
+static __inline__ int sk_nulls_del_node_init_rcu(struct sock *sk)
+{
+ int rc = __sk_nulls_del_node_init_rcu(sk);
+
+ if (rc) {
+ /* paranoid for a while -acme */
+ WARN_ON(atomic_read(&sk->sk_refcnt) == 1);
+ __sock_put(sk);
+ }
+ return rc;
+}
+
static __inline__ void __sk_add_node(struct sock *sk, struct hlist_head *list)
{
hlist_add_head(&sk->sk_node, list);
@@ -372,6 +429,17 @@ static __inline__ void sk_add_node(struct sock *sk, struct hlist_head *list)
__sk_add_node(sk, list);
}
+static __inline__ void __sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_head *list)
+{
+ hlist_nulls_add_head_rcu(&sk->sk_nulls_node, list);
+}
+
+static __inline__ void sk_nulls_add_node_rcu(struct sock *sk, struct hlist_nulls_head *list)
+{
+ sock_hold(sk);
+ __sk_nulls_add_node_rcu(sk, list);
+}
+
static __inline__ void __sk_del_bind_node(struct sock *sk)
{
__hlist_del(&sk->sk_bind_node);
@@ -385,9 +453,16 @@ static __inline__ void sk_add_bind_node(struct sock *sk,
#define sk_for_each(__sk, node, list) \
hlist_for_each_entry(__sk, node, list, sk_node)
+#define sk_nulls_for_each(__sk, node, list) \
+ hlist_nulls_for_each_entry(__sk, node, list, sk_nulls_node)
+#define sk_nulls_for_each_rcu(__sk, node, list) \
+ hlist_nulls_for_each_entry_rcu(__sk, node, list, sk_nulls_node)
#define sk_for_each_from(__sk, node) \
if (__sk && ({ node = &(__sk)->sk_node; 1; })) \
hlist_for_each_entry_from(__sk, node, sk_node)
+#define sk_nulls_for_each_from(__sk, node) \
+ if (__sk && ({ node = &(__sk)->sk_nulls_node; 1; })) \
+ hlist_nulls_for_each_entry_from(__sk, node, sk_nulls_node)
#define sk_for_each_continue(__sk, node) \
if (__sk && ({ node = &(__sk)->sk_node; 1; })) \
hlist_for_each_entry_continue(__sk, node, sk_node)
@@ -574,7 +649,7 @@ struct proto {
/* Memory pressure */
void (*enter_memory_pressure)(struct sock *sk);
atomic_t *memory_allocated; /* Current allocated memory. */
- atomic_t *sockets_allocated; /* Current number of sockets. */
+ struct percpu_counter *sockets_allocated; /* Current number of sockets. */
/*
* Pressure flag: try to collapse.
* Technical note: it is used by multiple contexts non atomically.
@@ -587,17 +662,18 @@ struct proto {
int *sysctl_rmem;
int max_header;
- struct kmem_cache *slab;
+ struct kmem_cache *slab;
unsigned int obj_size;
+ int slab_flags;
- atomic_t *orphan_count;
+ struct percpu_counter *orphan_count;
struct request_sock_ops *rsk_prot;
struct timewait_sock_ops *twsk_prot;
union {
struct inet_hashinfo *hashinfo;
- struct hlist_head *udp_hash;
+ struct udp_table *udp_table;
struct raw_hashinfo *raw_hash;
} h;
diff --git a/include/net/syncppp.h b/include/net/syncppp.h
deleted file mode 100644
index 9e306f7f579a..000000000000
--- a/include/net/syncppp.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Defines for synchronous PPP/Cisco link level subroutines.
- *
- * Copyright (C) 1994 Cronyx Ltd.
- * Author: Serge Vakulenko, <vak@zebub.msk.su>
- *
- * This software is distributed with NO WARRANTIES, not even the implied
- * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Authors grant any other persons or organizations permission to use
- * or modify this software as long as this message is kept with the software,
- * all derivative works or modified versions.
- *
- * Version 1.7, Wed Jun 7 22:12:02 MSD 1995
- *
- *
- *
- */
-
-#ifndef _SYNCPPP_H_
-#define _SYNCPPP_H_ 1
-
-#ifdef __KERNEL__
-struct slcp {
- u16 state; /* state machine */
- u32 magic; /* local magic number */
- u_char echoid; /* id of last keepalive echo request */
- u_char confid; /* id of last configuration request */
-};
-
-struct sipcp {
- u16 state; /* state machine */
- u_char confid; /* id of last configuration request */
-};
-
-struct sppp
-{
- struct sppp * pp_next; /* next interface in keepalive list */
- u32 pp_flags; /* use Cisco protocol instead of PPP */
- u16 pp_alivecnt; /* keepalive packets counter */
- u16 pp_loopcnt; /* loopback detection counter */
- u32 pp_seq; /* local sequence number */
- u32 pp_rseq; /* remote sequence number */
- struct slcp lcp; /* LCP params */
- struct sipcp ipcp; /* IPCP params */
- struct timer_list pp_timer;
- struct net_device *pp_if;
- char pp_link_state; /* Link status */
- spinlock_t lock;
-};
-
-struct ppp_device
-{
- struct net_device *dev; /* Network device pointer */
- struct sppp sppp; /* Synchronous PPP */
-};
-
-static inline struct sppp *sppp_of(struct net_device *dev)
-{
- struct ppp_device **ppp = dev->ml_priv;
- BUG_ON((*ppp)->dev != dev);
- return &(*ppp)->sppp;
-}
-
-#define PP_KEEPALIVE 0x01 /* use keepalive protocol */
-#define PP_CISCO 0x02 /* use Cisco protocol instead of PPP */
-#define PP_TIMO 0x04 /* cp_timeout routine active */
-#define PP_DEBUG 0x08
-
-#define PPP_MTU 1500 /* max. transmit unit */
-
-#define LCP_STATE_CLOSED 0 /* LCP state: closed (conf-req sent) */
-#define LCP_STATE_ACK_RCVD 1 /* LCP state: conf-ack received */
-#define LCP_STATE_ACK_SENT 2 /* LCP state: conf-ack sent */
-#define LCP_STATE_OPENED 3 /* LCP state: opened */
-
-#define IPCP_STATE_CLOSED 0 /* IPCP state: closed (conf-req sent) */
-#define IPCP_STATE_ACK_RCVD 1 /* IPCP state: conf-ack received */
-#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */
-#define IPCP_STATE_OPENED 3 /* IPCP state: opened */
-
-#define SPPP_LINK_DOWN 0 /* link down - no keepalive */
-#define SPPP_LINK_UP 1 /* link is up - keepalive ok */
-
-void sppp_attach (struct ppp_device *pd);
-void sppp_detach (struct net_device *dev);
-int sppp_do_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd);
-struct sk_buff *sppp_dequeue (struct net_device *dev);
-int sppp_isempty (struct net_device *dev);
-void sppp_flush (struct net_device *dev);
-int sppp_open (struct net_device *dev);
-int sppp_reopen (struct net_device *dev);
-int sppp_close (struct net_device *dev);
-#endif
-
-#define SPPPIOCCISCO (SIOCDEVPRIVATE)
-#define SPPPIOCPPP (SIOCDEVPRIVATE+1)
-#define SPPPIOCDEBUG (SIOCDEVPRIVATE+2)
-#define SPPPIOCSFLAGS (SIOCDEVPRIVATE+3)
-#define SPPPIOCGFLAGS (SIOCDEVPRIVATE+4)
-
-#endif /* _SYNCPPP_H_ */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 438014d57610..218235de8963 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -46,7 +46,7 @@
extern struct inet_hashinfo tcp_hashinfo;
-extern atomic_t tcp_orphan_count;
+extern struct percpu_counter tcp_orphan_count;
extern void tcp_time_wait(struct sock *sk, int state, int timeo);
#define MAX_TCP_HEADER (128 + MAX_HEADER)
@@ -238,7 +238,7 @@ extern int sysctl_tcp_slow_start_after_idle;
extern int sysctl_tcp_max_ssthresh;
extern atomic_t tcp_memory_allocated;
-extern atomic_t tcp_sockets_allocated;
+extern struct percpu_counter tcp_sockets_allocated;
extern int tcp_memory_pressure;
/*
@@ -472,8 +472,6 @@ extern void tcp_send_delayed_ack(struct sock *sk);
/* tcp_input.c */
extern void tcp_cwnd_application_limited(struct sock *sk);
-extern void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp,
- struct sk_buff *skb);
/* tcp_timer.c */
extern void tcp_init_xmit_timers(struct sock *);
@@ -590,7 +588,6 @@ struct tcp_skb_cb {
#define TCPCB_EVER_RETRANS 0x80 /* Ever retransmitted frame */
#define TCPCB_RETRANS (TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS)
- __u16 urg_ptr; /* Valid w/URG flags is set. */
__u32 ack_seq; /* Sequence number ACK'd */
};
@@ -764,8 +761,6 @@ static inline unsigned int tcp_packets_in_flight(const struct tcp_sock *tp)
return tp->packets_out - tcp_left_out(tp) + tp->retrans_out;
}
-extern int tcp_limit_reno_sacked(struct tcp_sock *tp);
-
/* If cwnd > ssthresh, we may raise ssthresh to be half-way to cwnd.
* The exception is rate halving phase, when cwnd is decreasing towards
* ssthresh.
@@ -1195,6 +1190,11 @@ static inline struct sk_buff *tcp_write_queue_next(struct sock *sk, struct sk_bu
return skb_queue_next(&sk->sk_write_queue, skb);
}
+static inline struct sk_buff *tcp_write_queue_prev(struct sock *sk, struct sk_buff *skb)
+{
+ return skb_queue_prev(&sk->sk_write_queue, skb);
+}
+
#define tcp_for_write_queue(skb, sk) \
skb_queue_walk(&(sk)->sk_write_queue, skb)
@@ -1358,6 +1358,12 @@ extern void tcp_v4_destroy_sock(struct sock *sk);
extern int tcp_v4_gso_send_check(struct sk_buff *skb);
extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features);
+extern struct sk_buff **tcp_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb);
+extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb);
+extern int tcp_gro_complete(struct sk_buff *skb);
+extern int tcp4_gro_complete(struct sk_buff *skb);
#ifdef CONFIG_PROC_FS
extern int tcp4_proc_init(void);
diff --git a/include/net/udp.h b/include/net/udp.h
index 1e205095ea68..90e6ce56be65 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -50,8 +50,15 @@ struct udp_skb_cb {
};
#define UDP_SKB_CB(__skb) ((struct udp_skb_cb *)((__skb)->cb))
-extern struct hlist_head udp_hash[UDP_HTABLE_SIZE];
-extern rwlock_t udp_hash_lock;
+struct udp_hslot {
+ struct hlist_nulls_head head;
+ spinlock_t lock;
+} __attribute__((aligned(2 * sizeof(long))));
+struct udp_table {
+ struct udp_hslot hash[UDP_HTABLE_SIZE];
+};
+extern struct udp_table udp_table;
+extern void udp_table_init(struct udp_table *);
/* Note: this must match 'valbool' in sock_setsockopt */
@@ -110,15 +117,7 @@ static inline void udp_lib_hash(struct sock *sk)
BUG();
}
-static inline void udp_lib_unhash(struct sock *sk)
-{
- write_lock_bh(&udp_hash_lock);
- if (sk_del_node_init(sk)) {
- inet_sk(sk)->num = 0;
- sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
- }
- write_unlock_bh(&udp_hash_lock);
-}
+extern void udp_lib_unhash(struct sock *sk);
static inline void udp_lib_close(struct sock *sk, long timeout)
{
@@ -187,7 +186,7 @@ extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
struct udp_seq_afinfo {
char *name;
sa_family_t family;
- struct hlist_head *hashtable;
+ struct udp_table *udp_table;
struct file_operations seq_fops;
struct seq_operations seq_ops;
};
@@ -196,7 +195,7 @@ struct udp_iter_state {
struct seq_net_private p;
sa_family_t family;
int bucket;
- struct hlist_head *hashtable;
+ struct udp_table *udp_table;
};
#ifdef CONFIG_PROC_FS
diff --git a/include/net/udplite.h b/include/net/udplite.h
index b76b2e377af4..afdffe607b24 100644
--- a/include/net/udplite.h
+++ b/include/net/udplite.h
@@ -11,7 +11,7 @@
#define UDPLITE_RECV_CSCOV 11 /* receiver partial coverage (threshold ) */
extern struct proto udplite_prot;
-extern struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
+extern struct udp_table udplite_table;
/*
* Checksum computation is all in software, hence simpler getfrag.
diff --git a/include/net/wireless.h b/include/net/wireless.h
index 721efb363db7..21c5d966142d 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -10,6 +10,7 @@
#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/list.h>
+#include <linux/ieee80211.h>
#include <net/cfg80211.h>
/**
@@ -133,23 +134,23 @@ struct ieee80211_rate {
};
/**
- * struct ieee80211_ht_info - describing STA's HT capabilities
+ * struct ieee80211_sta_ht_cap - STA's HT capabilities
*
* This structure describes most essential parameters needed
* to describe 802.11n HT capabilities for an STA.
*
- * @ht_supported: is HT supported by STA, 0: no, 1: yes
+ * @ht_supported: is HT supported by the STA
* @cap: HT capabilities map as described in 802.11n spec
* @ampdu_factor: Maximum A-MPDU length factor
* @ampdu_density: Minimum A-MPDU spacing
- * @supp_mcs_set: Supported MCS set as described in 802.11n spec
+ * @mcs: Supported MCS rates
*/
-struct ieee80211_ht_info {
+struct ieee80211_sta_ht_cap {
u16 cap; /* use IEEE80211_HT_CAP_ */
- u8 ht_supported;
+ bool ht_supported;
u8 ampdu_factor;
u8 ampdu_density;
- u8 supp_mcs_set[16];
+ struct ieee80211_mcs_info mcs;
};
/**
@@ -173,13 +174,18 @@ struct ieee80211_supported_band {
enum ieee80211_band band;
int n_channels;
int n_bitrates;
- struct ieee80211_ht_info ht_info;
+ struct ieee80211_sta_ht_cap ht_cap;
};
/**
* struct wiphy - wireless hardware description
* @idx: the wiphy index assigned to this item
* @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name>
+ * @fw_handles_regulatory: tells us the firmware for this device
+ * has its own regulatory solution and cannot identify the
+ * ISO / IEC 3166 alpha2 it belongs to. When this is enabled
+ * we will disregard the first regulatory hint (when the
+ * initiator is %REGDOM_SET_BY_CORE).
* @reg_notifier: the driver's regulatory notification callback
*/
struct wiphy {
@@ -191,6 +197,8 @@ struct wiphy {
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes;
+ bool fw_handles_regulatory;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
@@ -262,9 +270,9 @@ static inline struct device *wiphy_dev(struct wiphy *wiphy)
/**
* wiphy_name - get wiphy name
*/
-static inline char *wiphy_name(struct wiphy *wiphy)
+static inline const char *wiphy_name(struct wiphy *wiphy)
{
- return wiphy->dev.bus_id;
+ return dev_name(&wiphy->dev);
}
/**
@@ -340,55 +348,51 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq)
}
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if a driver is providing the hint this is the driver's very
- * own &struct wiphy
- * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
- * should be in. If @rd is set this should be NULL
- * @rd: a complete regulatory domain, if passed the caller need not worry
- * about freeing it
- *
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain by
- * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
- * domain should be in or by providing a completely build regulatory domain.
+ * ieee80211_get_response_rate - get basic rate for a given rate
*
- * Returns -EALREADY if *a regulatory domain* has already been set. Note that
- * this could be by another driver. It is safe for drivers to continue if
- * -EALREADY is returned, if drivers are not capable of world roaming they
- * should not register more channels than they support. Right now we only
- * support listening to the first driver hint. If the driver is capable
- * of world roaming but wants to respect its own EEPROM mappings for
- * specific regulatory domains it should register the @reg_notifier callback
- * on the &struct wiphy. Returns 0 if the hint went through fine or through an
- * intersection operation. Otherwise a standard error code is returned.
+ * @sband: the band to look for rates in
+ * @basic_rates: bitmap of basic rates
+ * @bitrate: the bitrate for which to find the basic rate
*
+ * This function returns the basic rate corresponding to a given
+ * bitrate, that is the next lower bitrate contained in the basic
+ * rate map, which is, for this function, given as a bitmap of
+ * indices of rates in the band's bitrate table.
*/
-extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, struct ieee80211_regdomain *rd);
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u64 basic_rates, int bitrate);
+
/**
* regulatory_hint - driver hint to the wireless core a regulatory domain
- * @wiphy: the driver's very own &struct wiphy
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
* @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain
* should be in. If @rd is set this should be NULL. Note that if you
* set this to NULL you should still set rd->alpha2 to some accepted
* alpha2.
- * @rd: a complete regulatory domain provided by the driver. If passed
- * the driver does not need to worry about freeing it.
*
* Wireless drivers can use this function to hint to the wireless core
* what it believes should be the current regulatory domain by
* giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
* domain should be in or by providing a completely build regulatory domain.
* If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried
- * for a regulatory domain structure for the respective country. If
- * a regulatory domain is build and passed you should set the alpha2
- * if possible, otherwise set it to the special value of "99" which tells
- * the wireless core it is unknown. If you pass a built regulatory domain
- * and we return non zero you are in charge of kfree()'ing the structure.
+ * for a regulatory domain structure for the respective country.
+ */
+extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+
+/**
+ * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
+ * @country_ie: pointer to the country IE
+ * @country_ie_len: length of the country IE
*
- * See __regulatory_hint() documentation for possible return values.
+ * We will intersect the rd with the what CRDA tells us should apply
+ * for the alpha2 this country IE belongs to, this prevents APs from
+ * sending us incorrect or outdated information against a country.
*/
-extern int regulatory_hint(struct wiphy *wiphy,
- const char *alpha2, struct ieee80211_regdomain *rd);
+extern void regulatory_hint_11d(struct wiphy *wiphy,
+ u8 *country_ie,
+ u8 country_ie_len);
#endif /* __NET_WIRELESS_H */
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 11c890ad8ebb..2e9f5c0018ae 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -38,22 +38,15 @@
MODULE_ALIAS("xfrm-type-" __stringify(family) "-" __stringify(proto))
#ifdef CONFIG_XFRM_STATISTICS
-DECLARE_SNMP_STAT(struct linux_xfrm_mib, xfrm_statistics);
-#define XFRM_INC_STATS(field) SNMP_INC_STATS(xfrm_statistics, field)
-#define XFRM_INC_STATS_BH(field) SNMP_INC_STATS_BH(xfrm_statistics, field)
-#define XFRM_INC_STATS_USER(field) SNMP_INC_STATS_USER(xfrm_statistics, field)
+#define XFRM_INC_STATS(net, field) SNMP_INC_STATS((net)->mib.xfrm_statistics, field)
+#define XFRM_INC_STATS_BH(net, field) SNMP_INC_STATS_BH((net)->mib.xfrm_statistics, field)
+#define XFRM_INC_STATS_USER(net, field) SNMP_INC_STATS_USER((net)-mib.xfrm_statistics, field)
#else
-#define XFRM_INC_STATS(field)
-#define XFRM_INC_STATS_BH(field)
-#define XFRM_INC_STATS_USER(field)
+#define XFRM_INC_STATS(net, field) ((void)(net))
+#define XFRM_INC_STATS_BH(net, field) ((void)(net))
+#define XFRM_INC_STATS_USER(net, field) ((void)(net))
#endif
-extern struct sock *xfrm_nl;
-extern u32 sysctl_xfrm_aevent_etime;
-extern u32 sysctl_xfrm_aevent_rseqth;
-extern int sysctl_xfrm_larval_drop;
-extern u32 sysctl_xfrm_acq_expires;
-
extern struct mutex xfrm_cfg_mutex;
/* Organization of SPD aka "XFRM rules"
@@ -130,6 +123,9 @@ struct xfrm_state_walk {
/* Full description of state of transformer. */
struct xfrm_state
{
+#ifdef CONFIG_NET_NS
+ struct net *xs_net;
+#endif
union {
struct hlist_node gclist;
struct hlist_node bydst;
@@ -223,6 +219,11 @@ struct xfrm_state
void *data;
};
+static inline struct net *xs_net(struct xfrm_state *x)
+{
+ return read_pnet(&x->xs_net);
+}
+
/* xflags - make enum if more show up */
#define XFRM_TIME_DEFER 1
@@ -249,6 +250,7 @@ struct km_event
u32 seq;
u32 pid;
u32 event;
+ struct net *net;
};
struct net_device;
@@ -257,10 +259,11 @@ struct xfrm_dst;
struct xfrm_policy_afinfo {
unsigned short family;
struct dst_ops *dst_ops;
- void (*garbage_collect)(void);
- struct dst_entry *(*dst_lookup)(int tos, xfrm_address_t *saddr,
+ void (*garbage_collect)(struct net *net);
+ struct dst_entry *(*dst_lookup)(struct net *net, int tos,
+ xfrm_address_t *saddr,
xfrm_address_t *daddr);
- int (*get_saddr)(xfrm_address_t *saddr, xfrm_address_t *daddr);
+ int (*get_saddr)(struct net *net, xfrm_address_t *saddr, xfrm_address_t *daddr);
struct dst_entry *(*find_bundle)(struct flowi *fl, struct xfrm_policy *policy);
void (*decode_session)(struct sk_buff *skb,
struct flowi *fl,
@@ -467,7 +470,9 @@ struct xfrm_policy_walk {
struct xfrm_policy
{
- struct xfrm_policy *next;
+#ifdef CONFIG_NET_NS
+ struct net *xp_net;
+#endif
struct hlist_node bydst;
struct hlist_node byidx;
@@ -492,6 +497,11 @@ struct xfrm_policy
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
};
+static inline struct net *xp_net(struct xfrm_policy *xp)
+{
+ return read_pnet(&xp->xp_net);
+}
+
struct xfrm_kmaddress {
xfrm_address_t local;
xfrm_address_t remote;
@@ -537,15 +547,13 @@ struct xfrm_mgr
struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir);
int (*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
int (*notify_policy)(struct xfrm_policy *x, int dir, struct km_event *c);
- int (*report)(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
+ int (*report)(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
int (*migrate)(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles, struct xfrm_kmaddress *k);
};
extern int xfrm_register_km(struct xfrm_mgr *km);
extern int xfrm_unregister_km(struct xfrm_mgr *km);
-extern unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
-
/*
* This structure is used for the duration where packets are being
* transformed by IPsec. As soon as the packet leaves IPsec the
@@ -882,6 +890,7 @@ struct xfrm_dst
u32 path_cookie;
};
+#ifdef CONFIG_XFRM
static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
{
dst_release(xdst->route);
@@ -894,6 +903,7 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
xdst->partner = NULL;
#endif
}
+#endif
extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
@@ -977,12 +987,13 @@ static inline int __xfrm_policy_check2(struct sock *sk, int dir,
struct sk_buff *skb,
unsigned int family, int reverse)
{
+ struct net *net = dev_net(skb->dev);
int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0);
if (sk && sk->sk_policy[XFRM_POLICY_IN])
return __xfrm_policy_check(sk, ndir, skb, family);
- return (!xfrm_policy_count[dir] && !skb->sp) ||
+ return (!net->xfrm.policy_count[dir] && !skb->sp) ||
(skb->dst->flags & DST_NOPOLICY) ||
__xfrm_policy_check(sk, ndir, skb, family);
}
@@ -1034,7 +1045,9 @@ extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family);
static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
- return !xfrm_policy_count[XFRM_POLICY_OUT] ||
+ struct net *net = dev_net(skb->dev);
+
+ return !net->xfrm.policy_count[XFRM_POLICY_OUT] ||
(skb->dst->flags & DST_NOXFRM) ||
__xfrm_route_forward(skb, family);
}
@@ -1268,7 +1281,8 @@ struct xfrm6_tunnel {
extern void xfrm_init(void);
extern void xfrm4_init(void);
-extern void xfrm_state_init(void);
+extern int xfrm_state_init(struct net *net);
+extern void xfrm_state_fini(struct net *net);
extern void xfrm4_state_init(void);
#ifdef CONFIG_XFRM
extern int xfrm6_init(void);
@@ -1287,19 +1301,30 @@ static inline void xfrm6_fini(void)
#endif
#ifdef CONFIG_XFRM_STATISTICS
-extern int xfrm_proc_init(void);
+extern int xfrm_proc_init(struct net *net);
+extern void xfrm_proc_fini(struct net *net);
+#endif
+
+extern int xfrm_sysctl_init(struct net *net);
+#ifdef CONFIG_SYSCTL
+extern void xfrm_sysctl_fini(struct net *net);
+#else
+static inline void xfrm_sysctl_fini(struct net *net)
+{
+}
#endif
extern void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto);
-extern int xfrm_state_walk(struct xfrm_state_walk *walk,
+extern int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*), void *);
extern void xfrm_state_walk_done(struct xfrm_state_walk *walk);
-extern struct xfrm_state *xfrm_state_alloc(void);
+extern struct xfrm_state *xfrm_state_alloc(struct net *net);
extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct flowi *fl, struct xfrm_tmpl *tmpl,
struct xfrm_policy *pol, int *err,
unsigned short family);
-extern struct xfrm_state * xfrm_stateonly_find(xfrm_address_t *daddr,
+extern struct xfrm_state * xfrm_stateonly_find(struct net *net,
+ xfrm_address_t *daddr,
xfrm_address_t *saddr,
unsigned short family,
u8 mode, u8 proto, u32 reqid);
@@ -1307,8 +1332,8 @@ extern int xfrm_state_check_expire(struct xfrm_state *x);
extern void xfrm_state_insert(struct xfrm_state *x);
extern int xfrm_state_add(struct xfrm_state *x);
extern int xfrm_state_update(struct xfrm_state *x);
-extern struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family);
-extern struct xfrm_state *xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family);
+extern struct xfrm_state *xfrm_state_lookup(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family);
+extern struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family);
#ifdef CONFIG_XFRM_SUB_POLICY
extern int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src,
int n, unsigned short family);
@@ -1345,9 +1370,9 @@ struct xfrmk_spdinfo {
u32 spdhmcnt;
};
-extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
+extern struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 seq);
extern int xfrm_state_delete(struct xfrm_state *x);
-extern int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info);
+extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info);
extern void xfrm_sad_getinfo(struct xfrmk_sadinfo *si);
extern void xfrm_spd_getinfo(struct xfrmk_spdinfo *si);
extern int xfrm_replay_check(struct xfrm_state *x,
@@ -1415,22 +1440,22 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
}
#endif
-struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp);
+struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp);
extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
-extern int xfrm_policy_walk(struct xfrm_policy_walk *walk,
+extern int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
int (*func)(struct xfrm_policy *, int, int, void*), void *);
extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
-struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u8 type, int dir,
struct xfrm_selector *sel,
struct xfrm_sec_ctx *ctx, int delete,
int *err);
-struct xfrm_policy *xfrm_policy_byid(u8, int dir, u32 id, int delete, int *err);
-int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info);
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u8, int dir, u32 id, int delete, int *err);
+int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info);
u32 xfrm_get_acqseq(void);
extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
-struct xfrm_state * xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
+struct xfrm_state * xfrm_find_acq(struct net *net, u8 mode, u32 reqid, u8 proto,
xfrm_address_t *daddr, xfrm_address_t *saddr,
int create, unsigned short family);
extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
@@ -1449,10 +1474,9 @@ extern int xfrm_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_kmaddress *k);
#endif
-extern wait_queue_head_t km_waitq;
extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid);
-extern int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
+extern int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
extern void xfrm_input_init(void);
extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq);
@@ -1497,18 +1521,20 @@ static inline int xfrm_policy_id2dir(u32 index)
return index & 7;
}
-static inline int xfrm_aevent_is_on(void)
+#ifdef CONFIG_XFRM
+static inline int xfrm_aevent_is_on(struct net *net)
{
struct sock *nlsk;
int ret = 0;
rcu_read_lock();
- nlsk = rcu_dereference(xfrm_nl);
+ nlsk = rcu_dereference(net->xfrm.nlsk);
if (nlsk)
ret = netlink_has_listeners(nlsk, XFRMNLGRP_AEVENTS);
rcu_read_unlock();
return ret;
}
+#endif
static inline int xfrm_alg_len(struct xfrm_algo *alg)
{
@@ -1536,9 +1562,11 @@ static inline void xfrm_states_delete(struct xfrm_state **states, int n)
}
#endif
+#ifdef CONFIG_XFRM
static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
{
return skb->sp->xvec[skb->sp->len - 1];
}
+#endif
#endif /* _NET_XFRM_H */
diff --git a/include/scsi/fc/fc_els.h b/include/scsi/fc/fc_els.h
new file mode 100644
index 000000000000..195ca014d3ce
--- /dev/null
+++ b/include/scsi/fc/fc_els.h
@@ -0,0 +1,816 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_ELS_H_
+#define _FC_ELS_H_
+
+/*
+ * Fibre Channel Switch - Enhanced Link Services definitions.
+ * From T11 FC-LS Rev 1.2 June 7, 2005.
+ */
+
+/*
+ * ELS Command codes - byte 0 of the frame payload
+ */
+enum fc_els_cmd {
+ ELS_LS_RJT = 0x01, /* ESL reject */
+ ELS_LS_ACC = 0x02, /* ESL Accept */
+ ELS_PLOGI = 0x03, /* N_Port login */
+ ELS_FLOGI = 0x04, /* F_Port login */
+ ELS_LOGO = 0x05, /* Logout */
+ ELS_ABTX = 0x06, /* Abort exchange - obsolete */
+ ELS_RCS = 0x07, /* read connection status */
+ ELS_RES = 0x08, /* read exchange status block */
+ ELS_RSS = 0x09, /* read sequence status block */
+ ELS_RSI = 0x0a, /* read sequence initiative */
+ ELS_ESTS = 0x0b, /* establish streaming */
+ ELS_ESTC = 0x0c, /* estimate credit */
+ ELS_ADVC = 0x0d, /* advise credit */
+ ELS_RTV = 0x0e, /* read timeout value */
+ ELS_RLS = 0x0f, /* read link error status block */
+ ELS_ECHO = 0x10, /* echo */
+ ELS_TEST = 0x11, /* test */
+ ELS_RRQ = 0x12, /* reinstate recovery qualifier */
+ ELS_REC = 0x13, /* read exchange concise */
+ ELS_SRR = 0x14, /* sequence retransmission request */
+ ELS_PRLI = 0x20, /* process login */
+ ELS_PRLO = 0x21, /* process logout */
+ ELS_SCN = 0x22, /* state change notification */
+ ELS_TPLS = 0x23, /* test process login state */
+ ELS_TPRLO = 0x24, /* third party process logout */
+ ELS_LCLM = 0x25, /* login control list mgmt (obs) */
+ ELS_GAID = 0x30, /* get alias_ID */
+ ELS_FACT = 0x31, /* fabric activate alias_id */
+ ELS_FDACDT = 0x32, /* fabric deactivate alias_id */
+ ELS_NACT = 0x33, /* N-port activate alias_id */
+ ELS_NDACT = 0x34, /* N-port deactivate alias_id */
+ ELS_QOSR = 0x40, /* quality of service request */
+ ELS_RVCS = 0x41, /* read virtual circuit status */
+ ELS_PDISC = 0x50, /* discover N_port service params */
+ ELS_FDISC = 0x51, /* discover F_port service params */
+ ELS_ADISC = 0x52, /* discover address */
+ ELS_RNC = 0x53, /* report node cap (obs) */
+ ELS_FARP_REQ = 0x54, /* FC ARP request */
+ ELS_FARP_REPL = 0x55, /* FC ARP reply */
+ ELS_RPS = 0x56, /* read port status block */
+ ELS_RPL = 0x57, /* read port list */
+ ELS_RPBC = 0x58, /* read port buffer condition */
+ ELS_FAN = 0x60, /* fabric address notification */
+ ELS_RSCN = 0x61, /* registered state change notification */
+ ELS_SCR = 0x62, /* state change registration */
+ ELS_RNFT = 0x63, /* report node FC-4 types */
+ ELS_CSR = 0x68, /* clock synch. request */
+ ELS_CSU = 0x69, /* clock synch. update */
+ ELS_LINIT = 0x70, /* loop initialize */
+ ELS_LSTS = 0x72, /* loop status */
+ ELS_RNID = 0x78, /* request node ID data */
+ ELS_RLIR = 0x79, /* registered link incident report */
+ ELS_LIRR = 0x7a, /* link incident record registration */
+ ELS_SRL = 0x7b, /* scan remote loop */
+ ELS_SBRP = 0x7c, /* set bit-error reporting params */
+ ELS_RPSC = 0x7d, /* report speed capabilities */
+ ELS_QSA = 0x7e, /* query security attributes */
+ ELS_EVFP = 0x7f, /* exchange virt. fabrics params */
+ ELS_LKA = 0x80, /* link keep-alive */
+ ELS_AUTH_ELS = 0x90, /* authentication ELS */
+};
+
+/*
+ * Initializer useful for decoding table.
+ * Please keep this in sync with the above definitions.
+ */
+#define FC_ELS_CMDS_INIT { \
+ [ELS_LS_RJT] = "LS_RJT", \
+ [ELS_LS_ACC] = "LS_ACC", \
+ [ELS_PLOGI] = "PLOGI", \
+ [ELS_FLOGI] = "FLOGI", \
+ [ELS_LOGO] = "LOGO", \
+ [ELS_ABTX] = "ABTX", \
+ [ELS_RCS] = "RCS", \
+ [ELS_RES] = "RES", \
+ [ELS_RSS] = "RSS", \
+ [ELS_RSI] = "RSI", \
+ [ELS_ESTS] = "ESTS", \
+ [ELS_ESTC] = "ESTC", \
+ [ELS_ADVC] = "ADVC", \
+ [ELS_RTV] = "RTV", \
+ [ELS_RLS] = "RLS", \
+ [ELS_ECHO] = "ECHO", \
+ [ELS_TEST] = "TEST", \
+ [ELS_RRQ] = "RRQ", \
+ [ELS_REC] = "REC", \
+ [ELS_SRR] = "SRR", \
+ [ELS_PRLI] = "PRLI", \
+ [ELS_PRLO] = "PRLO", \
+ [ELS_SCN] = "SCN", \
+ [ELS_TPLS] = "TPLS", \
+ [ELS_TPRLO] = "TPRLO", \
+ [ELS_LCLM] = "LCLM", \
+ [ELS_GAID] = "GAID", \
+ [ELS_FACT] = "FACT", \
+ [ELS_FDACDT] = "FDACDT", \
+ [ELS_NACT] = "NACT", \
+ [ELS_NDACT] = "NDACT", \
+ [ELS_QOSR] = "QOSR", \
+ [ELS_RVCS] = "RVCS", \
+ [ELS_PDISC] = "PDISC", \
+ [ELS_FDISC] = "FDISC", \
+ [ELS_ADISC] = "ADISC", \
+ [ELS_RNC] = "RNC", \
+ [ELS_FARP_REQ] = "FARP_REQ", \
+ [ELS_FARP_REPL] = "FARP_REPL", \
+ [ELS_RPS] = "RPS", \
+ [ELS_RPL] = "RPL", \
+ [ELS_RPBC] = "RPBC", \
+ [ELS_FAN] = "FAN", \
+ [ELS_RSCN] = "RSCN", \
+ [ELS_SCR] = "SCR", \
+ [ELS_RNFT] = "RNFT", \
+ [ELS_CSR] = "CSR", \
+ [ELS_CSU] = "CSU", \
+ [ELS_LINIT] = "LINIT", \
+ [ELS_LSTS] = "LSTS", \
+ [ELS_RNID] = "RNID", \
+ [ELS_RLIR] = "RLIR", \
+ [ELS_LIRR] = "LIRR", \
+ [ELS_SRL] = "SRL", \
+ [ELS_SBRP] = "SBRP", \
+ [ELS_RPSC] = "RPSC", \
+ [ELS_QSA] = "QSA", \
+ [ELS_EVFP] = "EVFP", \
+ [ELS_LKA] = "LKA", \
+ [ELS_AUTH_ELS] = "AUTH_ELS", \
+}
+
+/*
+ * LS_ACC payload.
+ */
+struct fc_els_ls_acc {
+ __u8 la_cmd; /* command code ELS_LS_ACC */
+ __u8 la_resv[3]; /* reserved */
+};
+
+/*
+ * ELS reject payload.
+ */
+struct fc_els_ls_rjt {
+ __u8 er_cmd; /* command code ELS_LS_RJT */
+ __u8 er_resv[4]; /* reserved must be zero */
+ __u8 er_reason; /* reason (enum fc_els_rjt_reason below) */
+ __u8 er_explan; /* explanation (enum fc_els_rjt_explan below) */
+ __u8 er_vendor; /* vendor specific code */
+};
+
+/*
+ * ELS reject reason codes (er_reason).
+ */
+enum fc_els_rjt_reason {
+ ELS_RJT_NONE = 0, /* no reject - not to be sent */
+ ELS_RJT_INVAL = 0x01, /* invalid ELS command code */
+ ELS_RJT_LOGIC = 0x03, /* logical error */
+ ELS_RJT_BUSY = 0x05, /* logical busy */
+ ELS_RJT_PROT = 0x07, /* protocol error */
+ ELS_RJT_UNAB = 0x09, /* unable to perform command request */
+ ELS_RJT_UNSUP = 0x0b, /* command not supported */
+ ELS_RJT_INPROG = 0x0e, /* command already in progress */
+ ELS_RJT_VENDOR = 0xff, /* vendor specific error */
+};
+
+
+/*
+ * reason code explanation (er_explan).
+ */
+enum fc_els_rjt_explan {
+ ELS_EXPL_NONE = 0x00, /* No additional explanation */
+ ELS_EXPL_SPP_OPT_ERR = 0x01, /* service parameter error - options */
+ ELS_EXPL_SPP_ICTL_ERR = 0x03, /* service parm error - initiator ctl */
+ ELS_EXPL_AH = 0x11, /* invalid association header */
+ ELS_EXPL_AH_REQ = 0x13, /* association_header required */
+ ELS_EXPL_SID = 0x15, /* invalid originator S_ID */
+ ELS_EXPL_OXID_RXID = 0x17, /* invalid OX_ID-RX_ID combination */
+ ELS_EXPL_INPROG = 0x19, /* Request already in progress */
+ ELS_EXPL_PLOGI_REQD = 0x1e, /* N_Port login required */
+ ELS_EXPL_INSUF_RES = 0x29, /* insufficient resources */
+ ELS_EXPL_UNAB_DATA = 0x2a, /* unable to supply requested data */
+ ELS_EXPL_UNSUPR = 0x2c, /* Request not supported */
+ ELS_EXPL_INV_LEN = 0x2d, /* Invalid payload length */
+ /* TBD - above definitions incomplete */
+};
+
+/*
+ * Common service parameters (N ports).
+ */
+struct fc_els_csp {
+ __u8 sp_hi_ver; /* highest version supported (obs.) */
+ __u8 sp_lo_ver; /* highest version supported (obs.) */
+ __be16 sp_bb_cred; /* buffer-to-buffer credits */
+ __be16 sp_features; /* common feature flags */
+ __be16 sp_bb_data; /* b-b state number and data field sz */
+ union {
+ struct {
+ __be16 _sp_tot_seq; /* total concurrent sequences */
+ __be16 _sp_rel_off; /* rel. offset by info cat */
+ } sp_plogi;
+ struct {
+ __be32 _sp_r_a_tov; /* resource alloc. timeout msec */
+ } sp_flogi_acc;
+ } sp_u;
+ __be32 sp_e_d_tov; /* error detect timeout value */
+};
+#define sp_tot_seq sp_u.sp_plogi._sp_tot_seq
+#define sp_rel_off sp_u.sp_plogi._sp_rel_off
+#define sp_r_a_tov sp_u.sp_flogi_acc._sp_r_a_tov
+
+#define FC_SP_BB_DATA_MASK 0xfff /* mask for data field size in sp_bb_data */
+
+/*
+ * Minimum and maximum values for max data field size in service parameters.
+ */
+#define FC_SP_MIN_MAX_PAYLOAD FC_MIN_MAX_PAYLOAD
+#define FC_SP_MAX_MAX_PAYLOAD FC_MAX_PAYLOAD
+
+/*
+ * sp_features
+ */
+#define FC_SP_FT_CIRO 0x8000 /* continuously increasing rel. off. */
+#define FC_SP_FT_CLAD 0x8000 /* clean address (in FLOGI LS_ACC) */
+#define FC_SP_FT_RAND 0x4000 /* random relative offset */
+#define FC_SP_FT_VAL 0x2000 /* valid vendor version level */
+#define FC_SP_FT_FPORT 0x1000 /* F port (1) vs. N port (0) */
+#define FC_SP_FT_ABB 0x0800 /* alternate BB_credit management */
+#define FC_SP_FT_EDTR 0x0400 /* E_D_TOV Resolution is nanoseconds */
+#define FC_SP_FT_MCAST 0x0200 /* multicast */
+#define FC_SP_FT_BCAST 0x0100 /* broadcast */
+#define FC_SP_FT_HUNT 0x0080 /* hunt group */
+#define FC_SP_FT_SIMP 0x0040 /* dedicated simplex */
+#define FC_SP_FT_SEC 0x0020 /* reserved for security */
+#define FC_SP_FT_CSYN 0x0010 /* clock synch. supported */
+#define FC_SP_FT_RTTOV 0x0008 /* R_T_TOV value 100 uS, else 100 mS */
+#define FC_SP_FT_HALF 0x0004 /* dynamic half duplex */
+#define FC_SP_FT_SEQC 0x0002 /* SEQ_CNT */
+#define FC_SP_FT_PAYL 0x0001 /* FLOGI payload length 256, else 116 */
+
+/*
+ * Class-specific service parameters.
+ */
+struct fc_els_cssp {
+ __be16 cp_class; /* class flags */
+ __be16 cp_init; /* initiator flags */
+ __be16 cp_recip; /* recipient flags */
+ __be16 cp_rdfs; /* receive data field size */
+ __be16 cp_con_seq; /* concurrent sequences */
+ __be16 cp_ee_cred; /* N-port end-to-end credit */
+ __u8 cp_resv1; /* reserved */
+ __u8 cp_open_seq; /* open sequences per exchange */
+ __u8 _cp_resv2[2]; /* reserved */
+};
+
+/*
+ * cp_class flags.
+ */
+#define FC_CPC_VALID 0x8000 /* class valid */
+#define FC_CPC_IMIX 0x4000 /* intermix mode */
+#define FC_CPC_SEQ 0x0800 /* sequential delivery */
+#define FC_CPC_CAMP 0x0200 /* camp-on */
+#define FC_CPC_PRI 0x0080 /* priority */
+
+/*
+ * cp_init flags.
+ * (TBD: not all flags defined here).
+ */
+#define FC_CPI_CSYN 0x0010 /* clock synch. capable */
+
+/*
+ * cp_recip flags.
+ */
+#define FC_CPR_CSYN 0x0008 /* clock synch. capable */
+
+/*
+ * NFC_ELS_FLOGI: Fabric login request.
+ * NFC_ELS_PLOGI: Port login request (same format).
+ */
+struct fc_els_flogi {
+ __u8 fl_cmd; /* command */
+ __u8 _fl_resvd[3]; /* must be zero */
+ struct fc_els_csp fl_csp; /* common service parameters */
+ __be64 fl_wwpn; /* port name */
+ __be64 fl_wwnn; /* node name */
+ struct fc_els_cssp fl_cssp[4]; /* class 1-4 service parameters */
+ __u8 fl_vend[16]; /* vendor version level */
+} __attribute__((__packed__));
+
+/*
+ * Process login service parameter page.
+ */
+struct fc_els_spp {
+ __u8 spp_type; /* type code or common service params */
+ __u8 spp_type_ext; /* type code extension */
+ __u8 spp_flags;
+ __u8 _spp_resvd;
+ __be32 spp_orig_pa; /* originator process associator */
+ __be32 spp_resp_pa; /* responder process associator */
+ __be32 spp_params; /* service parameters */
+};
+
+/*
+ * spp_flags.
+ */
+#define FC_SPP_OPA_VAL 0x80 /* originator proc. assoc. valid */
+#define FC_SPP_RPA_VAL 0x40 /* responder proc. assoc. valid */
+#define FC_SPP_EST_IMG_PAIR 0x20 /* establish image pair */
+#define FC_SPP_RESP_MASK 0x0f /* mask for response code (below) */
+
+/*
+ * SPP response code in spp_flags - lower 4 bits.
+ */
+enum fc_els_spp_resp {
+ FC_SPP_RESP_ACK = 1, /* request executed */
+ FC_SPP_RESP_RES = 2, /* unable due to lack of resources */
+ FC_SPP_RESP_INIT = 3, /* initialization not complete */
+ FC_SPP_RESP_NO_PA = 4, /* unknown process associator */
+ FC_SPP_RESP_CONF = 5, /* configuration precludes image pair */
+ FC_SPP_RESP_COND = 6, /* request completed conditionally */
+ FC_SPP_RESP_MULT = 7, /* unable to handle multiple SPPs */
+ FC_SPP_RESP_INVL = 8, /* SPP is invalid */
+};
+
+/*
+ * ELS_RRQ - Reinstate Recovery Qualifier
+ */
+struct fc_els_rrq {
+ __u8 rrq_cmd; /* command (0x12) */
+ __u8 rrq_zero[3]; /* specified as zero - part of cmd */
+ __u8 rrq_resvd; /* reserved */
+ __u8 rrq_s_id[3]; /* originator FID */
+ __be16 rrq_ox_id; /* originator exchange ID */
+ __be16 rrq_rx_id; /* responders exchange ID */
+};
+
+/*
+ * ELS_REC - Read exchange concise.
+ */
+struct fc_els_rec {
+ __u8 rec_cmd; /* command (0x13) */
+ __u8 rec_zero[3]; /* specified as zero - part of cmd */
+ __u8 rec_resvd; /* reserved */
+ __u8 rec_s_id[3]; /* originator FID */
+ __be16 rec_ox_id; /* originator exchange ID */
+ __be16 rec_rx_id; /* responders exchange ID */
+};
+
+/*
+ * ELS_REC LS_ACC payload.
+ */
+struct fc_els_rec_acc {
+ __u8 reca_cmd; /* accept (0x02) */
+ __u8 reca_zero[3]; /* specified as zero - part of cmd */
+ __be16 reca_ox_id; /* originator exchange ID */
+ __be16 reca_rx_id; /* responders exchange ID */
+ __u8 reca_resvd1; /* reserved */
+ __u8 reca_ofid[3]; /* originator FID */
+ __u8 reca_resvd2; /* reserved */
+ __u8 reca_rfid[3]; /* responder FID */
+ __be32 reca_fc4value; /* FC4 value */
+ __be32 reca_e_stat; /* ESB (exchange status block) status */
+};
+
+/*
+ * ELS_PRLI - Process login request and response.
+ */
+struct fc_els_prli {
+ __u8 prli_cmd; /* command */
+ __u8 prli_spp_len; /* length of each serv. parm. page */
+ __be16 prli_len; /* length of entire payload */
+ /* service parameter pages follow */
+};
+
+/*
+ * ELS_ADISC payload
+ */
+struct fc_els_adisc {
+ __u8 adisc_cmd;
+ __u8 adisc_resv[3];
+ __u8 adisc_resv1;
+ __u8 adisc_hard_addr[3];
+ __be64 adisc_wwpn;
+ __be64 adisc_wwnn;
+ __u8 adisc_resv2;
+ __u8 adisc_port_id[3];
+} __attribute__((__packed__));
+
+/*
+ * ELS_LOGO - process or fabric logout.
+ */
+struct fc_els_logo {
+ __u8 fl_cmd; /* command code */
+ __u8 fl_zero[3]; /* specified as zero - part of cmd */
+ __u8 fl_resvd; /* reserved */
+ __u8 fl_n_port_id[3];/* N port ID */
+ __be64 fl_n_port_wwn; /* port name */
+};
+
+/*
+ * ELS_RTV - read timeout value.
+ */
+struct fc_els_rtv {
+ __u8 rtv_cmd; /* command code 0x0e */
+ __u8 rtv_zero[3]; /* specified as zero - part of cmd */
+};
+
+/*
+ * LS_ACC for ELS_RTV - read timeout value.
+ */
+struct fc_els_rtv_acc {
+ __u8 rtv_cmd; /* command code 0x02 */
+ __u8 rtv_zero[3]; /* specified as zero - part of cmd */
+ __be32 rtv_r_a_tov; /* resource allocation timeout value */
+ __be32 rtv_e_d_tov; /* error detection timeout value */
+ __be32 rtv_toq; /* timeout qualifier (see below) */
+};
+
+/*
+ * rtv_toq bits.
+ */
+#define FC_ELS_RTV_EDRES (1 << 26) /* E_D_TOV resolution is nS else mS */
+#define FC_ELS_RTV_RTTOV (1 << 19) /* R_T_TOV is 100 uS else 100 mS */
+
+/*
+ * ELS_SCR - state change registration payload.
+ */
+struct fc_els_scr {
+ __u8 scr_cmd; /* command code */
+ __u8 scr_resv[6]; /* reserved */
+ __u8 scr_reg_func; /* registration function (see below) */
+};
+
+enum fc_els_scr_func {
+ ELS_SCRF_FAB = 1, /* fabric-detected registration */
+ ELS_SCRF_NPORT = 2, /* Nx_Port-detected registration */
+ ELS_SCRF_FULL = 3, /* full registration */
+ ELS_SCRF_CLEAR = 255, /* remove any current registrations */
+};
+
+/*
+ * ELS_RSCN - registered state change notification payload.
+ */
+struct fc_els_rscn {
+ __u8 rscn_cmd; /* RSCN opcode (0x61) */
+ __u8 rscn_page_len; /* page length (4) */
+ __be16 rscn_plen; /* payload length including this word */
+
+ /* followed by 4-byte generic affected Port_ID pages */
+};
+
+struct fc_els_rscn_page {
+ __u8 rscn_page_flags; /* event and address format */
+ __u8 rscn_fid[3]; /* fabric ID */
+};
+
+#define ELS_RSCN_EV_QUAL_BIT 2 /* shift count for event qualifier */
+#define ELS_RSCN_EV_QUAL_MASK 0xf /* mask for event qualifier */
+#define ELS_RSCN_ADDR_FMT_BIT 0 /* shift count for address format */
+#define ELS_RSCN_ADDR_FMT_MASK 0x3 /* mask for address format */
+
+enum fc_els_rscn_ev_qual {
+ ELS_EV_QUAL_NONE = 0, /* unspecified */
+ ELS_EV_QUAL_NS_OBJ = 1, /* changed name server object */
+ ELS_EV_QUAL_PORT_ATTR = 2, /* changed port attribute */
+ ELS_EV_QUAL_SERV_OBJ = 3, /* changed service object */
+ ELS_EV_QUAL_SW_CONFIG = 4, /* changed switch configuration */
+ ELS_EV_QUAL_REM_OBJ = 5, /* removed object */
+};
+
+enum fc_els_rscn_addr_fmt {
+ ELS_ADDR_FMT_PORT = 0, /* rscn_fid is a port address */
+ ELS_ADDR_FMT_AREA = 1, /* rscn_fid is a area address */
+ ELS_ADDR_FMT_DOM = 2, /* rscn_fid is a domain address */
+ ELS_ADDR_FMT_FAB = 3, /* anything on fabric may have changed */
+};
+
+/*
+ * ELS_RNID - request Node ID.
+ */
+struct fc_els_rnid {
+ __u8 rnid_cmd; /* RNID opcode (0x78) */
+ __u8 rnid_resv[3]; /* reserved */
+ __u8 rnid_fmt; /* data format */
+ __u8 rnid_resv2[3]; /* reserved */
+};
+
+/*
+ * Node Identification Data formats (rnid_fmt)
+ */
+enum fc_els_rnid_fmt {
+ ELS_RNIDF_NONE = 0, /* no specific identification data */
+ ELS_RNIDF_GEN = 0xdf, /* general topology discovery format */
+};
+
+/*
+ * ELS_RNID response.
+ */
+struct fc_els_rnid_resp {
+ __u8 rnid_cmd; /* response code (LS_ACC) */
+ __u8 rnid_resv[3]; /* reserved */
+ __u8 rnid_fmt; /* data format */
+ __u8 rnid_cid_len; /* common ID data length */
+ __u8 rnid_resv2; /* reserved */
+ __u8 rnid_sid_len; /* specific ID data length */
+};
+
+struct fc_els_rnid_cid {
+ __be64 rnid_wwpn; /* N port name */
+ __be64 rnid_wwnn; /* node name */
+};
+
+struct fc_els_rnid_gen {
+ __u8 rnid_vend_id[16]; /* vendor-unique ID */
+ __be32 rnid_atype; /* associated type (see below) */
+ __be32 rnid_phys_port; /* physical port number */
+ __be32 rnid_att_nodes; /* number of attached nodes */
+ __u8 rnid_node_mgmt; /* node management (see below) */
+ __u8 rnid_ip_ver; /* IP version (see below) */
+ __be16 rnid_prot_port; /* UDP / TCP port number */
+ __be32 rnid_ip_addr[4]; /* IP address */
+ __u8 rnid_resvd[2]; /* reserved */
+ __be16 rnid_vend_spec; /* vendor-specific field */
+};
+
+enum fc_els_rnid_atype {
+ ELS_RNIDA_UNK = 0x01, /* unknown */
+ ELS_RNIDA_OTHER = 0x02, /* none of the following */
+ ELS_RNIDA_HUB = 0x03,
+ ELS_RNIDA_SWITCH = 0x04,
+ ELS_RNIDA_GATEWAY = 0x05,
+ ELS_RNIDA_CONV = 0x06, /* Obsolete, do not use this value */
+ ELS_RNIDA_HBA = 0x07, /* Obsolete, do not use this value */
+ ELS_RNIDA_PROXY = 0x08, /* Obsolete, do not use this value */
+ ELS_RNIDA_STORAGE = 0x09,
+ ELS_RNIDA_HOST = 0x0a,
+ ELS_RNIDA_SUBSYS = 0x0b, /* storage subsystem (e.g., RAID) */
+ ELS_RNIDA_ACCESS = 0x0e, /* access device (e.g. media changer) */
+ ELS_RNIDA_NAS = 0x11, /* NAS server */
+ ELS_RNIDA_BRIDGE = 0x12, /* bridge */
+ ELS_RNIDA_VIRT = 0x13, /* virtualization device */
+ ELS_RNIDA_MF = 0xff, /* multifunction device (bits below) */
+ ELS_RNIDA_MF_HUB = 1UL << 31, /* hub */
+ ELS_RNIDA_MF_SW = 1UL << 30, /* switch */
+ ELS_RNIDA_MF_GW = 1UL << 29, /* gateway */
+ ELS_RNIDA_MF_ST = 1UL << 28, /* storage */
+ ELS_RNIDA_MF_HOST = 1UL << 27, /* host */
+ ELS_RNIDA_MF_SUB = 1UL << 26, /* storage subsystem */
+ ELS_RNIDA_MF_ACC = 1UL << 25, /* storage access dev */
+ ELS_RNIDA_MF_WDM = 1UL << 24, /* wavelength division mux */
+ ELS_RNIDA_MF_NAS = 1UL << 23, /* NAS server */
+ ELS_RNIDA_MF_BR = 1UL << 22, /* bridge */
+ ELS_RNIDA_MF_VIRT = 1UL << 21, /* virtualization device */
+};
+
+enum fc_els_rnid_mgmt {
+ ELS_RNIDM_SNMP = 0,
+ ELS_RNIDM_TELNET = 1,
+ ELS_RNIDM_HTTP = 2,
+ ELS_RNIDM_HTTPS = 3,
+ ELS_RNIDM_XML = 4, /* HTTP + XML */
+};
+
+enum fc_els_rnid_ipver {
+ ELS_RNIDIP_NONE = 0, /* no IP support or node mgmt. */
+ ELS_RNIDIP_V4 = 1, /* IPv4 */
+ ELS_RNIDIP_V6 = 2, /* IPv6 */
+};
+
+/*
+ * ELS RPL - Read Port List.
+ */
+struct fc_els_rpl {
+ __u8 rpl_cmd; /* command */
+ __u8 rpl_resv[5]; /* reserved - must be zero */
+ __be16 rpl_max_size; /* maximum response size or zero */
+ __u8 rpl_resv1; /* reserved - must be zero */
+ __u8 rpl_index[3]; /* starting index */
+};
+
+/*
+ * Port number block in RPL response.
+ */
+struct fc_els_pnb {
+ __be32 pnb_phys_pn; /* physical port number */
+ __u8 pnb_resv; /* reserved */
+ __u8 pnb_port_id[3]; /* port ID */
+ __be64 pnb_wwpn; /* port name */
+};
+
+/*
+ * RPL LS_ACC response.
+ */
+struct fc_els_rpl_resp {
+ __u8 rpl_cmd; /* ELS_LS_ACC */
+ __u8 rpl_resv1; /* reserved - must be zero */
+ __be16 rpl_plen; /* payload length */
+ __u8 rpl_resv2; /* reserved - must be zero */
+ __u8 rpl_llen[3]; /* list length */
+ __u8 rpl_resv3; /* reserved - must be zero */
+ __u8 rpl_index[3]; /* starting index */
+ struct fc_els_pnb rpl_pnb[1]; /* variable number of PNBs */
+};
+
+/*
+ * Link Error Status Block.
+ */
+struct fc_els_lesb {
+ __be32 lesb_link_fail; /* link failure count */
+ __be32 lesb_sync_loss; /* loss of synchronization count */
+ __be32 lesb_sig_loss; /* loss of signal count */
+ __be32 lesb_prim_err; /* primitive sequence error count */
+ __be32 lesb_inv_word; /* invalid transmission word count */
+ __be32 lesb_inv_crc; /* invalid CRC count */
+};
+
+/*
+ * ELS RPS - Read Port Status Block request.
+ */
+struct fc_els_rps {
+ __u8 rps_cmd; /* command */
+ __u8 rps_resv[2]; /* reserved - must be zero */
+ __u8 rps_flag; /* flag - see below */
+ __be64 rps_port_spec; /* port selection */
+};
+
+enum fc_els_rps_flag {
+ FC_ELS_RPS_DID = 0x00, /* port identified by D_ID of req. */
+ FC_ELS_RPS_PPN = 0x01, /* port_spec is physical port number */
+ FC_ELS_RPS_WWPN = 0x02, /* port_spec is port WWN */
+};
+
+/*
+ * ELS RPS LS_ACC response.
+ */
+struct fc_els_rps_resp {
+ __u8 rps_cmd; /* command - LS_ACC */
+ __u8 rps_resv[2]; /* reserved - must be zero */
+ __u8 rps_flag; /* flag - see below */
+ __u8 rps_resv2[2]; /* reserved */
+ __be16 rps_status; /* port status - see below */
+ struct fc_els_lesb rps_lesb; /* link error status block */
+};
+
+enum fc_els_rps_resp_flag {
+ FC_ELS_RPS_LPEV = 0x01, /* L_port extension valid */
+};
+
+enum fc_els_rps_resp_status {
+ FC_ELS_RPS_PTP = 1 << 5, /* point-to-point connection */
+ FC_ELS_RPS_LOOP = 1 << 4, /* loop mode */
+ FC_ELS_RPS_FAB = 1 << 3, /* fabric present */
+ FC_ELS_RPS_NO_SIG = 1 << 2, /* loss of signal */
+ FC_ELS_RPS_NO_SYNC = 1 << 1, /* loss of synchronization */
+ FC_ELS_RPS_RESET = 1 << 0, /* in link reset protocol */
+};
+
+/*
+ * ELS LIRR - Link Incident Record Registration request.
+ */
+struct fc_els_lirr {
+ __u8 lirr_cmd; /* command */
+ __u8 lirr_resv[3]; /* reserved - must be zero */
+ __u8 lirr_func; /* registration function */
+ __u8 lirr_fmt; /* FC-4 type of RLIR requested */
+ __u8 lirr_resv2[2]; /* reserved - must be zero */
+};
+
+enum fc_els_lirr_func {
+ ELS_LIRR_SET_COND = 0x01, /* set - conditionally receive */
+ ELS_LIRR_SET_UNCOND = 0x02, /* set - unconditionally receive */
+ ELS_LIRR_CLEAR = 0xff /* clear registration */
+};
+
+/*
+ * ELS SRL - Scan Remote Loop request.
+ */
+struct fc_els_srl {
+ __u8 srl_cmd; /* command */
+ __u8 srl_resv[3]; /* reserved - must be zero */
+ __u8 srl_flag; /* flag - see below */
+ __u8 srl_flag_param[3]; /* flag parameter */
+};
+
+enum fc_els_srl_flag {
+ FC_ELS_SRL_ALL = 0x00, /* scan all FL ports */
+ FC_ELS_SRL_ONE = 0x01, /* scan specified loop */
+ FC_ELS_SRL_EN_PER = 0x02, /* enable periodic scanning (param) */
+ FC_ELS_SRL_DIS_PER = 0x03, /* disable periodic scanning */
+};
+
+/*
+ * ELS RLS - Read Link Error Status Block request.
+ */
+struct fc_els_rls {
+ __u8 rls_cmd; /* command */
+ __u8 rls_resv[4]; /* reserved - must be zero */
+ __u8 rls_port_id[3]; /* port ID */
+};
+
+/*
+ * ELS RLS LS_ACC Response.
+ */
+struct fc_els_rls_resp {
+ __u8 rls_cmd; /* ELS_LS_ACC */
+ __u8 rls_resv[3]; /* reserved - must be zero */
+ struct fc_els_lesb rls_lesb; /* link error status block */
+};
+
+/*
+ * ELS RLIR - Registered Link Incident Report.
+ * This is followed by the CLIR and the CLID, described below.
+ */
+struct fc_els_rlir {
+ __u8 rlir_cmd; /* command */
+ __u8 rlir_resv[3]; /* reserved - must be zero */
+ __u8 rlir_fmt; /* format (FC4-type if type specific) */
+ __u8 rlir_clr_len; /* common link incident record length */
+ __u8 rlir_cld_len; /* common link incident desc. length */
+ __u8 rlir_slr_len; /* spec. link incident record length */
+};
+
+/*
+ * CLIR - Common Link Incident Record Data. - Sent via RLIR.
+ */
+struct fc_els_clir {
+ __be64 clir_wwpn; /* incident port name */
+ __be64 clir_wwnn; /* incident port node name */
+ __u8 clir_port_type; /* incident port type */
+ __u8 clir_port_id[3]; /* incident port ID */
+
+ __be64 clir_conn_wwpn; /* connected port name */
+ __be64 clir_conn_wwnn; /* connected node name */
+ __be64 clir_fab_name; /* fabric name */
+ __be32 clir_phys_port; /* physical port number */
+ __be32 clir_trans_id; /* transaction ID */
+ __u8 clir_resv[3]; /* reserved */
+ __u8 clir_ts_fmt; /* time stamp format */
+ __be64 clir_timestamp; /* time stamp */
+};
+
+/*
+ * CLIR clir_ts_fmt - time stamp format values.
+ */
+enum fc_els_clir_ts_fmt {
+ ELS_CLIR_TS_UNKNOWN = 0, /* time stamp field unknown */
+ ELS_CLIR_TS_SEC_FRAC = 1, /* time in seconds and fractions */
+ ELS_CLIR_TS_CSU = 2, /* time in clock synch update format */
+};
+
+/*
+ * Common Link Incident Descriptor - sent via RLIR.
+ */
+struct fc_els_clid {
+ __u8 clid_iq; /* incident qualifier flags */
+ __u8 clid_ic; /* incident code */
+ __be16 clid_epai; /* domain/area of ISL */
+};
+
+/*
+ * CLID incident qualifier flags.
+ */
+enum fc_els_clid_iq {
+ ELS_CLID_SWITCH = 0x20, /* incident port is a switch node */
+ ELS_CLID_E_PORT = 0x10, /* incident is an ISL (E) port */
+ ELS_CLID_SEV_MASK = 0x0c, /* severity 2-bit field mask */
+ ELS_CLID_SEV_INFO = 0x00, /* report is informational */
+ ELS_CLID_SEV_INOP = 0x08, /* link not operational */
+ ELS_CLID_SEV_DEG = 0x04, /* link degraded but operational */
+ ELS_CLID_LASER = 0x02, /* subassembly is a laser */
+ ELS_CLID_FRU = 0x01, /* format can identify a FRU */
+};
+
+/*
+ * CLID incident code.
+ */
+enum fc_els_clid_ic {
+ ELS_CLID_IC_IMPL = 1, /* implicit incident */
+ ELS_CLID_IC_BER = 2, /* bit-error-rate threshold exceeded */
+ ELS_CLID_IC_LOS = 3, /* loss of synch or signal */
+ ELS_CLID_IC_NOS = 4, /* non-operational primitive sequence */
+ ELS_CLID_IC_PST = 5, /* primitive sequence timeout */
+ ELS_CLID_IC_INVAL = 6, /* invalid primitive sequence */
+ ELS_CLID_IC_LOOP_TO = 7, /* loop initialization time out */
+ ELS_CLID_IC_LIP = 8, /* receiving LIP */
+};
+
+#endif /* _FC_ELS_H_ */
diff --git a/include/scsi/fc/fc_encaps.h b/include/scsi/fc/fc_encaps.h
new file mode 100644
index 000000000000..f180c3e16220
--- /dev/null
+++ b/include/scsi/fc/fc_encaps.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+#ifndef _FC_ENCAPS_H_
+#define _FC_ENCAPS_H_
+
+/*
+ * Protocol definitions from RFC 3643 - Fibre Channel Frame Encapsulation.
+ *
+ * Note: The frame length field is the number of 32-bit words in
+ * the encapsulation including the fcip_encaps_header, CRC and EOF words.
+ * The minimum frame length value in bytes is (32 + 24 + 4 + 4) * 4 = 64.
+ * The maximum frame length value in bytes is (32 + 24 + 2112 + 4 + 4) = 2172.
+ */
+#define FC_ENCAPS_MIN_FRAME_LEN 64 /* min frame len (bytes) (see above) */
+#define FC_ENCAPS_MAX_FRAME_LEN (FC_ENCAPS_MIN_FRAME_LEN + FC_MAX_PAYLOAD)
+
+#define FC_ENCAPS_VER 1 /* current version number */
+
+struct fc_encaps_hdr {
+ __u8 fc_proto; /* protocol number */
+ __u8 fc_ver; /* version of encapsulation */
+ __u8 fc_proto_n; /* ones complement of protocol */
+ __u8 fc_ver_n; /* ones complement of version */
+
+ unsigned char fc_proto_data[8]; /* protocol specific data */
+
+ __be16 fc_len_flags; /* 10-bit length/4 w/ 6 flag bits */
+ __be16 fc_len_flags_n; /* ones complement of length / flags */
+
+ /*
+ * Offset 0x10
+ */
+ __be32 fc_time[2]; /* time stamp: seconds and fraction */
+ __be32 fc_crc; /* CRC */
+ __be32 fc_sof; /* start of frame (see FC_SOF below) */
+
+ /* 0x20 - FC frame content followed by EOF word */
+};
+
+#define FCIP_ENCAPS_HDR_LEN 0x20 /* expected length for asserts */
+
+/*
+ * Macro's for making redundant copies of EOF and SOF.
+ */
+#define FC_XY(x, y) ((((x) & 0xff) << 8) | ((y) & 0xff))
+#define FC_XYXY(x, y) ((FCIP_XY(x, y) << 16) | FCIP_XY(x, y))
+#define FC_XYNN(x, y) (FCIP_XYXY(x, y) ^ 0xffff)
+
+#define FC_SOF_ENCODE(n) FC_XYNN(n, n)
+#define FC_EOF_ENCODE(n) FC_XYNN(n, n)
+
+/*
+ * SOF / EOF bytes.
+ */
+enum fc_sof {
+ FC_SOF_F = 0x28, /* fabric */
+ FC_SOF_I4 = 0x29, /* initiate class 4 */
+ FC_SOF_I2 = 0x2d, /* initiate class 2 */
+ FC_SOF_I3 = 0x2e, /* initiate class 3 */
+ FC_SOF_N4 = 0x31, /* normal class 4 */
+ FC_SOF_N2 = 0x35, /* normal class 2 */
+ FC_SOF_N3 = 0x36, /* normal class 3 */
+ FC_SOF_C4 = 0x39, /* activate class 4 */
+} __attribute__((packed));
+
+enum fc_eof {
+ FC_EOF_N = 0x41, /* normal (not last frame of seq) */
+ FC_EOF_T = 0x42, /* terminate (last frame of sequence) */
+ FC_EOF_RT = 0x44,
+ FC_EOF_DT = 0x46, /* disconnect-terminate class-1 */
+ FC_EOF_NI = 0x49, /* normal-invalid */
+ FC_EOF_DTI = 0x4e, /* disconnect-terminate-invalid */
+ FC_EOF_RTI = 0x4f,
+ FC_EOF_A = 0x50, /* abort */
+} __attribute__((packed));
+
+#define FC_SOF_CLASS_MASK 0x06 /* mask for class of service in SOF */
+
+/*
+ * Define classes in terms of the SOF code (initial).
+ */
+enum fc_class {
+ FC_CLASS_NONE = 0, /* software value indicating no class */
+ FC_CLASS_2 = FC_SOF_I2,
+ FC_CLASS_3 = FC_SOF_I3,
+ FC_CLASS_4 = FC_SOF_I4,
+ FC_CLASS_F = FC_SOF_F,
+};
+
+/*
+ * Determine whether SOF code indicates the need for a BLS ACK.
+ */
+static inline int fc_sof_needs_ack(enum fc_sof sof)
+{
+ return (~sof) & 0x02; /* true for class 1, 2, 4, 6, or F */
+}
+
+/*
+ * Given an fc_class, return the normal (non-initial) SOF value.
+ */
+static inline enum fc_sof fc_sof_normal(enum fc_class class)
+{
+ return class + FC_SOF_N3 - FC_SOF_I3; /* diff is always 8 */
+}
+
+/*
+ * Compute class from SOF value.
+ */
+static inline enum fc_class fc_sof_class(enum fc_sof sof)
+{
+ return (sof & 0x7) | FC_SOF_F;
+}
+
+/*
+ * Determine whether SOF is for the initial frame of a sequence.
+ */
+static inline int fc_sof_is_init(enum fc_sof sof)
+{
+ return sof < 0x30;
+}
+
+#endif /* _FC_ENCAPS_H_ */
diff --git a/include/scsi/fc/fc_fc2.h b/include/scsi/fc/fc_fc2.h
new file mode 100644
index 000000000000..cff8a8c22f50
--- /dev/null
+++ b/include/scsi/fc/fc_fc2.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FC2_H_
+#define _FC_FC2_H_
+
+/*
+ * Fibre Channel Exchanges and Sequences.
+ */
+#ifndef PACKED
+#define PACKED __attribute__ ((__packed__))
+#endif /* PACKED */
+
+
+/*
+ * Sequence Status Block.
+ * This format is set by the FC-FS standard and is sent over the wire.
+ * Note that the fields aren't all naturally aligned.
+ */
+struct fc_ssb {
+ __u8 ssb_seq_id; /* sequence ID */
+ __u8 _ssb_resvd;
+ __be16 ssb_low_seq_cnt; /* lowest SEQ_CNT */
+
+ __be16 ssb_high_seq_cnt; /* highest SEQ_CNT */
+ __be16 ssb_s_stat; /* sequence status flags */
+
+ __be16 ssb_err_seq_cnt; /* error SEQ_CNT */
+ __u8 ssb_fh_cs_ctl; /* frame header CS_CTL */
+ __be16 ssb_fh_ox_id; /* frame header OX_ID */
+ __be16 ssb_rx_id; /* responder's exchange ID */
+ __u8 _ssb_resvd2[2];
+} PACKED;
+
+/*
+ * The SSB should be 17 bytes. Since it's layout is somewhat strange,
+ * we define the size here so that code can ASSERT that the size comes out
+ * correct.
+ */
+#define FC_SSB_SIZE 17 /* length of fc_ssb for assert */
+
+/*
+ * ssb_s_stat - flags from FC-FS-2 T11/1619-D Rev 0.90.
+ */
+#define SSB_ST_RESP (1 << 15) /* sequence responder */
+#define SSB_ST_ACTIVE (1 << 14) /* sequence is active */
+#define SSB_ST_ABNORMAL (1 << 12) /* abnormal ending condition */
+
+#define SSB_ST_REQ_MASK (3 << 10) /* ACK, abort sequence condition */
+#define SSB_ST_REQ_CONT (0 << 10)
+#define SSB_ST_REQ_ABORT (1 << 10)
+#define SSB_ST_REQ_STOP (2 << 10)
+#define SSB_ST_REQ_RETRANS (3 << 10)
+
+#define SSB_ST_ABTS (1 << 9) /* ABTS protocol completed */
+#define SSB_ST_RETRANS (1 << 8) /* retransmission completed */
+#define SSB_ST_TIMEOUT (1 << 7) /* sequence timed out by recipient */
+#define SSB_ST_P_RJT (1 << 6) /* P_RJT transmitted */
+
+#define SSB_ST_CLASS_BIT 4 /* class of service field LSB */
+#define SSB_ST_CLASS_MASK 3 /* class of service mask */
+#define SSB_ST_ACK (1 << 3) /* ACK (EOFt or EOFdt) transmitted */
+
+/*
+ * Exchange Status Block.
+ * This format is set by the FC-FS standard and is sent over the wire.
+ * Note that the fields aren't all naturally aligned.
+ */
+struct fc_esb {
+ __u8 esb_cs_ctl; /* CS_CTL for frame header */
+ __be16 esb_ox_id; /* originator exchange ID */
+ __be16 esb_rx_id; /* responder exchange ID */
+ __be32 esb_orig_fid; /* fabric ID of originator */
+ __be32 esb_resp_fid; /* fabric ID of responder */
+ __be32 esb_e_stat; /* status */
+ __u8 _esb_resvd[4];
+ __u8 esb_service_params[112]; /* TBD */
+ __u8 esb_seq_status[8]; /* sequence statuses, 8 bytes each */
+} __attribute__((packed));;
+
+
+/*
+ * Define expected size for ASSERTs.
+ * See comments on FC_SSB_SIZE.
+ */
+#define FC_ESB_SIZE (1 + 5*4 + 112 + 8) /* expected size */
+
+/*
+ * esb_e_stat - flags from FC-FS-2 T11/1619-D Rev 0.90.
+ */
+#define ESB_ST_RESP (1 << 31) /* responder to exchange */
+#define ESB_ST_SEQ_INIT (1 << 30) /* port holds sequence initiaive */
+#define ESB_ST_COMPLETE (1 << 29) /* exchange is complete */
+#define ESB_ST_ABNORMAL (1 << 28) /* abnormal ending condition */
+#define ESB_ST_REC_QUAL (1 << 26) /* recovery qualifier active */
+
+#define ESB_ST_ERRP_BIT 24 /* LSB for error policy */
+#define ESB_ST_ERRP_MASK (3 << 24) /* mask for error policy */
+#define ESB_ST_ERRP_MULT (0 << 24) /* abort, discard multiple sequences */
+#define ESB_ST_ERRP_SING (1 << 24) /* abort, discard single sequence */
+#define ESB_ST_ERRP_INF (2 << 24) /* process with infinite buffers */
+#define ESB_ST_ERRP_IMM (3 << 24) /* discard mult. with immed. retran. */
+
+#define ESB_ST_OX_ID_INVL (1 << 23) /* originator XID invalid */
+#define ESB_ST_RX_ID_INVL (1 << 22) /* responder XID invalid */
+#define ESB_ST_PRI_INUSE (1 << 21) /* priority / preemption in use */
+
+#endif /* _FC_FC2_H_ */
diff --git a/include/scsi/fc/fc_fcoe.h b/include/scsi/fc/fc_fcoe.h
new file mode 100644
index 000000000000..57aaa8f0d613
--- /dev/null
+++ b/include/scsi/fc/fc_fcoe.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FCOE_H_
+#define _FC_FCOE_H_
+
+/*
+ * FCoE - Fibre Channel over Ethernet.
+ */
+
+/*
+ * The FCoE ethertype eventually goes in net/if_ether.h.
+ */
+#ifndef ETH_P_FCOE
+#define ETH_P_FCOE 0x8906 /* FCOE ether type */
+#endif
+
+#ifndef ETH_P_8021Q
+#define ETH_P_8021Q 0x8100
+#endif
+
+/*
+ * FC_FCOE_OUI hasn't been standardized yet. XXX TBD.
+ */
+#ifndef FC_FCOE_OUI
+#define FC_FCOE_OUI 0x0efc00 /* upper 24 bits of FCOE dest MAC TBD */
+#endif
+
+/*
+ * The destination MAC address for the fabric login may get a different OUI.
+ * This isn't standardized yet.
+ */
+#ifndef FC_FCOE_FLOGI_MAC
+/* gateway MAC - TBD */
+#define FC_FCOE_FLOGI_MAC { 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe }
+#endif
+
+#define FC_FCOE_VER 0 /* version */
+
+/*
+ * Ethernet Addresses based on FC S_ID and D_ID.
+ * Generated by FC_FCOE_OUI | S_ID/D_ID
+ */
+#define FC_FCOE_ENCAPS_ID(n) (((u64) FC_FCOE_OUI << 24) | (n))
+#define FC_FCOE_DECAPS_ID(n) ((n) >> 24)
+
+/*
+ * FCoE frame header - 14 bytes
+ *
+ * This is the August 2007 version of the FCoE header as defined by T11.
+ * This follows the VLAN header, which includes the ethertype.
+ */
+struct fcoe_hdr {
+ __u8 fcoe_ver; /* version field - upper 4 bits */
+ __u8 fcoe_resvd[12]; /* reserved - send zero and ignore */
+ __u8 fcoe_sof; /* start of frame per RFC 3643 */
+};
+
+#define FC_FCOE_DECAPS_VER(hp) ((hp)->fcoe_ver >> 4)
+#define FC_FCOE_ENCAPS_VER(hp, ver) ((hp)->fcoe_ver = (ver) << 4)
+
+/*
+ * FCoE CRC & EOF - 8 bytes.
+ */
+struct fcoe_crc_eof {
+ __le32 fcoe_crc32; /* CRC for FC packet */
+ __u8 fcoe_eof; /* EOF from RFC 3643 */
+ __u8 fcoe_resvd[3]; /* reserved - send zero and ignore */
+} __attribute__((packed));
+
+/*
+ * Minimum FCoE + FC header length
+ * 14 bytes FCoE header + 24 byte FC header = 38 bytes
+ */
+#define FCOE_HEADER_LEN 38
+
+/*
+ * Minimum FCoE frame size
+ * 14 bytes FCoE header + 24 byte FC header + 8 byte FCoE trailer = 46 bytes
+ */
+#define FCOE_MIN_FRAME 46
+
+/*
+ * fc_fcoe_set_mac - Store OUI + DID into MAC address field.
+ * @mac: mac address to be set
+ * @did: fc dest id to use
+ */
+static inline void fc_fcoe_set_mac(u8 *mac, u8 *did)
+{
+ mac[0] = (u8) (FC_FCOE_OUI >> 16);
+ mac[1] = (u8) (FC_FCOE_OUI >> 8);
+ mac[2] = (u8) FC_FCOE_OUI;
+ mac[3] = did[0];
+ mac[4] = did[1];
+ mac[5] = did[2];
+}
+
+#endif /* _FC_FCOE_H_ */
diff --git a/include/scsi/fc/fc_fcp.h b/include/scsi/fc/fc_fcp.h
new file mode 100644
index 000000000000..5d38f1989f37
--- /dev/null
+++ b/include/scsi/fc/fc_fcp.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FCP_H_
+#define _FC_FCP_H_
+
+/*
+ * Fibre Channel Protocol for SCSI.
+ * From T10 FCP-3, T10 project 1560-D Rev 4, Sept. 13, 2005.
+ */
+
+/*
+ * fc/fs.h defines FC_TYPE_FCP.
+ */
+
+/*
+ * Service parameter page parameters (word 3 bits) for Process Login.
+ */
+#define FCP_SPPF_TASK_RETRY_ID 0x0200 /* task retry ID requested */
+#define FCP_SPPF_RETRY 0x0100 /* retry supported */
+#define FCP_SPPF_CONF_COMPL 0x0080 /* confirmed completion allowed */
+#define FCP_SPPF_OVLY_ALLOW 0x0040 /* data overlay allowed */
+#define FCP_SPPF_INIT_FCN 0x0020 /* initiator function */
+#define FCP_SPPF_TARG_FCN 0x0010 /* target function */
+#define FCP_SPPF_RD_XRDY_DIS 0x0002 /* disable XFER_RDY for reads */
+#define FCP_SPPF_WR_XRDY_DIS 0x0001 /* disable XFER_RDY for writes */
+
+/*
+ * FCP_CMND IU Payload.
+ */
+struct fcp_cmnd {
+ __u8 fc_lun[8]; /* logical unit number */
+ __u8 fc_cmdref; /* commmand reference number */
+ __u8 fc_pri_ta; /* priority and task attribute */
+ __u8 fc_tm_flags; /* task management flags */
+ __u8 fc_flags; /* additional len & flags */
+ __u8 fc_cdb[16]; /* base CDB */
+ __be32 fc_dl; /* data length (must follow fc_cdb) */
+};
+
+#define FCP_CMND_LEN 32 /* expected length of structure */
+
+struct fcp_cmnd32 {
+ __u8 fc_lun[8]; /* logical unit number */
+ __u8 fc_cmdref; /* commmand reference number */
+ __u8 fc_pri_ta; /* priority and task attribute */
+ __u8 fc_tm_flags; /* task management flags */
+ __u8 fc_flags; /* additional len & flags */
+ __u8 fc_cdb[32]; /* base CDB */
+ __be32 fc_dl; /* data length (must follow fc_cdb) */
+};
+
+#define FCP_CMND32_LEN 48 /* expected length of structure */
+#define FCP_CMND32_ADD_LEN (16 / 4) /* Additional cdb length */
+
+/*
+ * fc_pri_ta.
+ */
+#define FCP_PTA_SIMPLE 0 /* simple task attribute */
+#define FCP_PTA_HEADQ 1 /* head of queue task attribute */
+#define FCP_PTA_ORDERED 2 /* ordered task attribute */
+#define FCP_PTA_ACA 4 /* auto. contigent allegiance */
+#define FCP_PRI_SHIFT 3 /* priority field starts in bit 3 */
+#define FCP_PRI_RESVD_MASK 0x80 /* reserved bits in priority field */
+
+/*
+ * fc_tm_flags - task management flags field.
+ */
+#define FCP_TMF_CLR_ACA 0x40 /* clear ACA condition */
+#define FCP_TMF_LUN_RESET 0x10 /* logical unit reset task management */
+#define FCP_TMF_CLR_TASK_SET 0x04 /* clear task set */
+#define FCP_TMF_ABT_TASK_SET 0x02 /* abort task set */
+
+/*
+ * fc_flags.
+ * Bits 7:2 are the additional FCP_CDB length / 4.
+ */
+#define FCP_CFL_LEN_MASK 0xfc /* mask for additional length */
+#define FCP_CFL_LEN_SHIFT 2 /* shift bits for additional length */
+#define FCP_CFL_RDDATA 0x02 /* read data */
+#define FCP_CFL_WRDATA 0x01 /* write data */
+
+/*
+ * FCP_TXRDY IU - transfer ready payload.
+ */
+struct fcp_txrdy {
+ __be32 ft_data_ro; /* data relative offset */
+ __be32 ft_burst_len; /* burst length */
+ __u8 _ft_resvd[4]; /* reserved */
+};
+
+#define FCP_TXRDY_LEN 12 /* expected length of structure */
+
+/*
+ * FCP_RESP IU - response payload.
+ *
+ * The response payload comes in three parts: the flags/status, the
+ * sense/response lengths and the sense data/response info section.
+ *
+ * From FCP3r04, note 6 of section 9.5.13:
+ *
+ * Some early implementations presented the FCP_RSP IU without the FCP_RESID,
+ * FCP_SNS_LEN, and FCP_RSP_LEN fields if the FCP_RESID_UNDER, FCP_RESID_OVER,
+ * FCP_SNS_LEN_VALID, and FCP_RSP_LEN_VALID bits were all set to zero. This
+ * non-standard behavior should be tolerated.
+ *
+ * All response frames will always contain the fcp_resp template. Some
+ * will also include the fcp_resp_len template.
+ */
+struct fcp_resp {
+ __u8 _fr_resvd[8]; /* reserved */
+ __be16 fr_retry_delay; /* retry delay timer */
+ __u8 fr_flags; /* flags */
+ __u8 fr_status; /* SCSI status code */
+};
+
+#define FCP_RESP_LEN 12 /* expected length of structure */
+
+struct fcp_resp_ext {
+ __be32 fr_resid; /* Residual value */
+ __be32 fr_sns_len; /* SCSI Sense length */
+ __be32 fr_rsp_len; /* Response Info length */
+
+ /*
+ * Optionally followed by RSP info and/or SNS info and/or
+ * bidirectional read residual length, if any.
+ */
+};
+
+#define FCP_RESP_EXT_LEN 12 /* expected length of the structure */
+
+struct fcp_resp_rsp_info {
+ __u8 _fr_resvd[3]; /* reserved */
+ __u8 rsp_code; /* Response Info Code */
+ __u8 _fr_resvd2[4]; /* reserved */
+};
+
+struct fcp_resp_with_ext {
+ struct fcp_resp resp;
+ struct fcp_resp_ext ext;
+};
+
+#define FCP_RESP_WITH_EXT (FCP_RESP_LEN + FCP_RESP_EXT_LEN)
+
+/*
+ * fr_flags.
+ */
+#define FCP_BIDI_RSP 0x80 /* bidirectional read response */
+#define FCP_BIDI_READ_UNDER 0x40 /* bidir. read less than requested */
+#define FCP_BIDI_READ_OVER 0x20 /* DL insufficient for full transfer */
+#define FCP_CONF_REQ 0x10 /* confirmation requested */
+#define FCP_RESID_UNDER 0x08 /* transfer shorter than expected */
+#define FCP_RESID_OVER 0x04 /* DL insufficient for full transfer */
+#define FCP_SNS_LEN_VAL 0x02 /* SNS_LEN field is valid */
+#define FCP_RSP_LEN_VAL 0x01 /* RSP_LEN field is valid */
+
+/*
+ * rsp_codes
+ */
+enum fcp_resp_rsp_codes {
+ FCP_TMF_CMPL = 0,
+ FCP_DATA_LEN_INVALID = 1,
+ FCP_CMND_FIELDS_INVALID = 2,
+ FCP_DATA_PARAM_MISMATCH = 3,
+ FCP_TMF_REJECTED = 4,
+ FCP_TMF_FAILED = 5,
+ FCP_TMF_INVALID_LUN = 9,
+};
+
+/*
+ * FCP SRR Link Service request - Sequence Retransmission Request.
+ */
+struct fcp_srr {
+ __u8 srr_op; /* opcode ELS_SRR */
+ __u8 srr_resvd[3]; /* opcode / reserved - must be zero */
+ __be16 srr_ox_id; /* OX_ID of failed command */
+ __be16 srr_rx_id; /* RX_ID of failed command */
+ __be32 srr_rel_off; /* relative offset */
+ __u8 srr_r_ctl; /* r_ctl for the information unit */
+ __u8 srr_resvd2[3]; /* reserved */
+};
+
+#endif /* _FC_FCP_H_ */
diff --git a/include/scsi/fc/fc_fs.h b/include/scsi/fc/fc_fs.h
new file mode 100644
index 000000000000..3e4801d2bdbb
--- /dev/null
+++ b/include/scsi/fc/fc_fs.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FS_H_
+#define _FC_FS_H_
+
+/*
+ * Fibre Channel Framing and Signalling definitions.
+ * From T11 FC-FS-2 Rev 0.90 - 9 August 2005.
+ */
+
+/*
+ * Frame header
+ */
+struct fc_frame_header {
+ __u8 fh_r_ctl; /* routing control */
+ __u8 fh_d_id[3]; /* Destination ID */
+
+ __u8 fh_cs_ctl; /* class of service control / pri */
+ __u8 fh_s_id[3]; /* Source ID */
+
+ __u8 fh_type; /* see enum fc_fh_type below */
+ __u8 fh_f_ctl[3]; /* frame control */
+
+ __u8 fh_seq_id; /* sequence ID */
+ __u8 fh_df_ctl; /* data field control */
+ __be16 fh_seq_cnt; /* sequence count */
+
+ __be16 fh_ox_id; /* originator exchange ID */
+ __be16 fh_rx_id; /* responder exchange ID */
+ __be32 fh_parm_offset; /* parameter or relative offset */
+};
+
+#define FC_FRAME_HEADER_LEN 24 /* expected length of structure */
+
+#define FC_MAX_PAYLOAD 2112U /* max payload length in bytes */
+#define FC_MIN_MAX_PAYLOAD 256U /* lower limit on max payload */
+
+#define FC_MAX_FRAME (FC_MAX_PAYLOAD + FC_FRAME_HEADER_LEN)
+#define FC_MIN_MAX_FRAME (FC_MIN_MAX_PAYLOAD + FC_FRAME_HEADER_LEN)
+
+/*
+ * fh_r_ctl - Routing control definitions.
+ */
+ /*
+ * FC-4 device_data.
+ */
+enum fc_rctl {
+ FC_RCTL_DD_UNCAT = 0x00, /* uncategorized information */
+ FC_RCTL_DD_SOL_DATA = 0x01, /* solicited data */
+ FC_RCTL_DD_UNSOL_CTL = 0x02, /* unsolicited control */
+ FC_RCTL_DD_SOL_CTL = 0x03, /* solicited control or reply */
+ FC_RCTL_DD_UNSOL_DATA = 0x04, /* unsolicited data */
+ FC_RCTL_DD_DATA_DESC = 0x05, /* data descriptor */
+ FC_RCTL_DD_UNSOL_CMD = 0x06, /* unsolicited command */
+ FC_RCTL_DD_CMD_STATUS = 0x07, /* command status */
+
+#define FC_RCTL_ILS_REQ FC_RCTL_DD_UNSOL_CTL /* ILS request */
+#define FC_RCTL_ILS_REP FC_RCTL_DD_SOL_CTL /* ILS reply */
+
+ /*
+ * Extended Link_Data
+ */
+ FC_RCTL_ELS_REQ = 0x22, /* extended link services request */
+ FC_RCTL_ELS_REP = 0x23, /* extended link services reply */
+ FC_RCTL_ELS4_REQ = 0x32, /* FC-4 ELS request */
+ FC_RCTL_ELS4_REP = 0x33, /* FC-4 ELS reply */
+ /*
+ * Optional Extended Headers
+ */
+ FC_RCTL_VFTH = 0x50, /* virtual fabric tagging header */
+ FC_RCTL_IFRH = 0x51, /* inter-fabric routing header */
+ FC_RCTL_ENCH = 0x52, /* encapsulation header */
+ /*
+ * Basic Link Services fh_r_ctl values.
+ */
+ FC_RCTL_BA_NOP = 0x80, /* basic link service NOP */
+ FC_RCTL_BA_ABTS = 0x81, /* basic link service abort */
+ FC_RCTL_BA_RMC = 0x82, /* remove connection */
+ FC_RCTL_BA_ACC = 0x84, /* basic accept */
+ FC_RCTL_BA_RJT = 0x85, /* basic reject */
+ FC_RCTL_BA_PRMT = 0x86, /* dedicated connection preempted */
+ /*
+ * Link Control Information.
+ */
+ FC_RCTL_ACK_1 = 0xc0, /* acknowledge_1 */
+ FC_RCTL_ACK_0 = 0xc1, /* acknowledge_0 */
+ FC_RCTL_P_RJT = 0xc2, /* port reject */
+ FC_RCTL_F_RJT = 0xc3, /* fabric reject */
+ FC_RCTL_P_BSY = 0xc4, /* port busy */
+ FC_RCTL_F_BSY = 0xc5, /* fabric busy to data frame */
+ FC_RCTL_F_BSYL = 0xc6, /* fabric busy to link control frame */
+ FC_RCTL_LCR = 0xc7, /* link credit reset */
+ FC_RCTL_END = 0xc9, /* end */
+};
+ /* incomplete list of definitions */
+
+/*
+ * R_CTL names initializer.
+ * Please keep this matching the above definitions.
+ */
+#define FC_RCTL_NAMES_INIT { \
+ [FC_RCTL_DD_UNCAT] = "uncat", \
+ [FC_RCTL_DD_SOL_DATA] = "sol data", \
+ [FC_RCTL_DD_UNSOL_CTL] = "unsol ctl", \
+ [FC_RCTL_DD_SOL_CTL] = "sol ctl/reply", \
+ [FC_RCTL_DD_UNSOL_DATA] = "unsol data", \
+ [FC_RCTL_DD_DATA_DESC] = "data desc", \
+ [FC_RCTL_DD_UNSOL_CMD] = "unsol cmd", \
+ [FC_RCTL_DD_CMD_STATUS] = "cmd status", \
+ [FC_RCTL_ELS_REQ] = "ELS req", \
+ [FC_RCTL_ELS_REP] = "ELS rep", \
+ [FC_RCTL_ELS4_REQ] = "FC-4 ELS req", \
+ [FC_RCTL_ELS4_REP] = "FC-4 ELS rep", \
+ [FC_RCTL_BA_NOP] = "BLS NOP", \
+ [FC_RCTL_BA_ABTS] = "BLS abort", \
+ [FC_RCTL_BA_RMC] = "BLS remove connection", \
+ [FC_RCTL_BA_ACC] = "BLS accept", \
+ [FC_RCTL_BA_RJT] = "BLS reject", \
+ [FC_RCTL_BA_PRMT] = "BLS dedicated connection preempted", \
+ [FC_RCTL_ACK_1] = "LC ACK_1", \
+ [FC_RCTL_ACK_0] = "LC ACK_0", \
+ [FC_RCTL_P_RJT] = "LC port reject", \
+ [FC_RCTL_F_RJT] = "LC fabric reject", \
+ [FC_RCTL_P_BSY] = "LC port busy", \
+ [FC_RCTL_F_BSY] = "LC fabric busy to data frame", \
+ [FC_RCTL_F_BSYL] = "LC fabric busy to link control frame",\
+ [FC_RCTL_LCR] = "LC link credit reset", \
+ [FC_RCTL_END] = "LC end", \
+}
+
+/*
+ * Well-known fabric addresses.
+ */
+enum fc_well_known_fid {
+ FC_FID_BCAST = 0xffffff, /* broadcast */
+ FC_FID_FLOGI = 0xfffffe, /* fabric login */
+ FC_FID_FCTRL = 0xfffffd, /* fabric controller */
+ FC_FID_DIR_SERV = 0xfffffc, /* directory server */
+ FC_FID_TIME_SERV = 0xfffffb, /* time server */
+ FC_FID_MGMT_SERV = 0xfffffa, /* management server */
+ FC_FID_QOS = 0xfffff9, /* QoS Facilitator */
+ FC_FID_ALIASES = 0xfffff8, /* alias server (FC-PH2) */
+ FC_FID_SEC_KEY = 0xfffff7, /* Security key dist. server */
+ FC_FID_CLOCK = 0xfffff6, /* clock synch server */
+ FC_FID_MCAST_SERV = 0xfffff5, /* multicast server */
+};
+
+#define FC_FID_WELL_KNOWN_MAX 0xffffff /* highest well-known fabric ID */
+#define FC_FID_WELL_KNOWN_BASE 0xfffff5 /* start of well-known fabric ID */
+
+/*
+ * Other well-known addresses, outside the above contiguous range.
+ */
+#define FC_FID_DOM_MGR 0xfffc00 /* domain manager base */
+
+/*
+ * Fabric ID bytes.
+ */
+#define FC_FID_DOMAIN 0
+#define FC_FID_PORT 1
+#define FC_FID_LINK 2
+
+/*
+ * fh_type codes
+ */
+enum fc_fh_type {
+ FC_TYPE_BLS = 0x00, /* basic link service */
+ FC_TYPE_ELS = 0x01, /* extended link service */
+ FC_TYPE_IP = 0x05, /* IP over FC, RFC 4338 */
+ FC_TYPE_FCP = 0x08, /* SCSI FCP */
+ FC_TYPE_CT = 0x20, /* Fibre Channel Services (FC-CT) */
+ FC_TYPE_ILS = 0x22, /* internal link service */
+};
+
+/*
+ * FC_TYPE names initializer.
+ * Please keep this matching the above definitions.
+ */
+#define FC_TYPE_NAMES_INIT { \
+ [FC_TYPE_BLS] = "BLS", \
+ [FC_TYPE_ELS] = "ELS", \
+ [FC_TYPE_IP] = "IP", \
+ [FC_TYPE_FCP] = "FCP", \
+ [FC_TYPE_CT] = "CT", \
+ [FC_TYPE_ILS] = "ILS", \
+}
+
+/*
+ * Exchange IDs.
+ */
+#define FC_XID_UNKNOWN 0xffff /* unknown exchange ID */
+#define FC_XID_MIN 0x0 /* supported min exchange ID */
+#define FC_XID_MAX 0xfffe /* supported max exchange ID */
+
+/*
+ * fh_f_ctl - Frame control flags.
+ */
+#define FC_FC_EX_CTX (1 << 23) /* sent by responder to exchange */
+#define FC_FC_SEQ_CTX (1 << 22) /* sent by responder to sequence */
+#define FC_FC_FIRST_SEQ (1 << 21) /* first sequence of this exchange */
+#define FC_FC_LAST_SEQ (1 << 20) /* last sequence of this exchange */
+#define FC_FC_END_SEQ (1 << 19) /* last frame of sequence */
+#define FC_FC_END_CONN (1 << 18) /* end of class 1 connection pending */
+#define FC_FC_RES_B17 (1 << 17) /* reserved */
+#define FC_FC_SEQ_INIT (1 << 16) /* transfer of sequence initiative */
+#define FC_FC_X_ID_REASS (1 << 15) /* exchange ID has been changed */
+#define FC_FC_X_ID_INVAL (1 << 14) /* exchange ID invalidated */
+
+#define FC_FC_ACK_1 (1 << 12) /* 13:12 = 1: ACK_1 expected */
+#define FC_FC_ACK_N (2 << 12) /* 13:12 = 2: ACK_N expected */
+#define FC_FC_ACK_0 (3 << 12) /* 13:12 = 3: ACK_0 expected */
+
+#define FC_FC_RES_B11 (1 << 11) /* reserved */
+#define FC_FC_RES_B10 (1 << 10) /* reserved */
+#define FC_FC_RETX_SEQ (1 << 9) /* retransmitted sequence */
+#define FC_FC_UNI_TX (1 << 8) /* unidirectional transmit (class 1) */
+#define FC_FC_CONT_SEQ(i) ((i) << 6)
+#define FC_FC_ABT_SEQ(i) ((i) << 4)
+#define FC_FC_REL_OFF (1 << 3) /* parameter is relative offset */
+#define FC_FC_RES2 (1 << 2) /* reserved */
+#define FC_FC_FILL(i) ((i) & 3) /* 1:0: bytes of trailing fill */
+
+/*
+ * BA_ACC payload.
+ */
+struct fc_ba_acc {
+ __u8 ba_seq_id_val; /* SEQ_ID validity */
+#define FC_BA_SEQ_ID_VAL 0x80
+ __u8 ba_seq_id; /* SEQ_ID of seq last deliverable */
+ __u8 ba_resvd[2]; /* reserved */
+ __be16 ba_ox_id; /* OX_ID for aborted seq or exch */
+ __be16 ba_rx_id; /* RX_ID for aborted seq or exch */
+ __be16 ba_low_seq_cnt; /* low SEQ_CNT of aborted seq */
+ __be16 ba_high_seq_cnt; /* high SEQ_CNT of aborted seq */
+};
+
+/*
+ * BA_RJT: Basic Reject payload.
+ */
+struct fc_ba_rjt {
+ __u8 br_resvd; /* reserved */
+ __u8 br_reason; /* reason code */
+ __u8 br_explan; /* reason explanation */
+ __u8 br_vendor; /* vendor unique code */
+};
+
+/*
+ * BA_RJT reason codes.
+ * From FS-2.
+ */
+enum fc_ba_rjt_reason {
+ FC_BA_RJT_NONE = 0, /* in software this means no reject */
+ FC_BA_RJT_INVL_CMD = 0x01, /* invalid command code */
+ FC_BA_RJT_LOG_ERR = 0x03, /* logical error */
+ FC_BA_RJT_LOG_BUSY = 0x05, /* logical busy */
+ FC_BA_RJT_PROTO_ERR = 0x07, /* protocol error */
+ FC_BA_RJT_UNABLE = 0x09, /* unable to perform request */
+ FC_BA_RJT_VENDOR = 0xff, /* vendor-specific (see br_vendor) */
+};
+
+/*
+ * BA_RJT reason code explanations.
+ */
+enum fc_ba_rjt_explan {
+ FC_BA_RJT_EXP_NONE = 0x00, /* no additional expanation */
+ FC_BA_RJT_INV_XID = 0x03, /* invalid OX_ID-RX_ID combination */
+ FC_BA_RJT_ABT = 0x05, /* sequence aborted, no seq info */
+};
+
+/*
+ * P_RJT or F_RJT: Port Reject or Fabric Reject parameter field.
+ */
+struct fc_pf_rjt {
+ __u8 rj_action; /* reserved */
+ __u8 rj_reason; /* reason code */
+ __u8 rj_resvd; /* reserved */
+ __u8 rj_vendor; /* vendor unique code */
+};
+
+/*
+ * P_RJT and F_RJT reject reason codes.
+ */
+enum fc_pf_rjt_reason {
+ FC_RJT_NONE = 0, /* non-reject (reserved by standard) */
+ FC_RJT_INVL_DID = 0x01, /* invalid destination ID */
+ FC_RJT_INVL_SID = 0x02, /* invalid source ID */
+ FC_RJT_P_UNAV_T = 0x03, /* port unavailable, temporary */
+ FC_RJT_P_UNAV = 0x04, /* port unavailable, permanent */
+ FC_RJT_CLS_UNSUP = 0x05, /* class not supported */
+ FC_RJT_DEL_USAGE = 0x06, /* delimiter usage error */
+ FC_RJT_TYPE_UNSUP = 0x07, /* type not supported */
+ FC_RJT_LINK_CTL = 0x08, /* invalid link control */
+ FC_RJT_R_CTL = 0x09, /* invalid R_CTL field */
+ FC_RJT_F_CTL = 0x0a, /* invalid F_CTL field */
+ FC_RJT_OX_ID = 0x0b, /* invalid originator exchange ID */
+ FC_RJT_RX_ID = 0x0c, /* invalid responder exchange ID */
+ FC_RJT_SEQ_ID = 0x0d, /* invalid sequence ID */
+ FC_RJT_DF_CTL = 0x0e, /* invalid DF_CTL field */
+ FC_RJT_SEQ_CNT = 0x0f, /* invalid SEQ_CNT field */
+ FC_RJT_PARAM = 0x10, /* invalid parameter field */
+ FC_RJT_EXCH_ERR = 0x11, /* exchange error */
+ FC_RJT_PROTO = 0x12, /* protocol error */
+ FC_RJT_LEN = 0x13, /* incorrect length */
+ FC_RJT_UNEXP_ACK = 0x14, /* unexpected ACK */
+ FC_RJT_FAB_CLASS = 0x15, /* class unsupported by fabric entity */
+ FC_RJT_LOGI_REQ = 0x16, /* login required */
+ FC_RJT_SEQ_XS = 0x17, /* excessive sequences attempted */
+ FC_RJT_EXCH_EST = 0x18, /* unable to establish exchange */
+ FC_RJT_FAB_UNAV = 0x1a, /* fabric unavailable */
+ FC_RJT_VC_ID = 0x1b, /* invalid VC_ID (class 4) */
+ FC_RJT_CS_CTL = 0x1c, /* invalid CS_CTL field */
+ FC_RJT_INSUF_RES = 0x1d, /* insuff. resources for VC (Class 4) */
+ FC_RJT_INVL_CLS = 0x1f, /* invalid class of service */
+ FC_RJT_PREEMT_RJT = 0x20, /* preemption request rejected */
+ FC_RJT_PREEMT_DIS = 0x21, /* preemption not enabled */
+ FC_RJT_MCAST_ERR = 0x22, /* multicast error */
+ FC_RJT_MCAST_ET = 0x23, /* multicast error terminate */
+ FC_RJT_PRLI_REQ = 0x24, /* process login required */
+ FC_RJT_INVL_ATT = 0x25, /* invalid attachment */
+ FC_RJT_VENDOR = 0xff, /* vendor specific reject */
+};
+
+#endif /* _FC_FS_H_ */
diff --git a/include/scsi/fc/fc_gs.h b/include/scsi/fc/fc_gs.h
new file mode 100644
index 000000000000..ffab0272c65a
--- /dev/null
+++ b/include/scsi/fc/fc_gs.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_GS_H_
+#define _FC_GS_H_
+
+/*
+ * Fibre Channel Services - Common Transport.
+ * From T11.org FC-GS-2 Rev 5.3 November 1998.
+ */
+
+struct fc_ct_hdr {
+ __u8 ct_rev; /* revision */
+ __u8 ct_in_id[3]; /* N_Port ID of original requestor */
+ __u8 ct_fs_type; /* type of fibre channel service */
+ __u8 ct_fs_subtype; /* subtype */
+ __u8 ct_options;
+ __u8 _ct_resvd1;
+ __be16 ct_cmd; /* command / response code */
+ __be16 ct_mr_size; /* maximum / residual size */
+ __u8 _ct_resvd2;
+ __u8 ct_reason; /* reject reason */
+ __u8 ct_explan; /* reason code explanation */
+ __u8 ct_vendor; /* vendor unique data */
+};
+
+#define FC_CT_HDR_LEN 16 /* expected sizeof (struct fc_ct_hdr) */
+
+enum fc_ct_rev {
+ FC_CT_REV = 1 /* common transport revision */
+};
+
+/*
+ * ct_fs_type values.
+ */
+enum fc_ct_fs_type {
+ FC_FST_ALIAS = 0xf8, /* alias service */
+ FC_FST_MGMT = 0xfa, /* management service */
+ FC_FST_TIME = 0xfb, /* time service */
+ FC_FST_DIR = 0xfc, /* directory service */
+};
+
+/*
+ * ct_cmd: Command / response codes
+ */
+enum fc_ct_cmd {
+ FC_FS_RJT = 0x8001, /* reject */
+ FC_FS_ACC = 0x8002, /* accept */
+};
+
+/*
+ * FS_RJT reason codes.
+ */
+enum fc_ct_reason {
+ FC_FS_RJT_CMD = 0x01, /* invalid command code */
+ FC_FS_RJT_VER = 0x02, /* invalid version level */
+ FC_FS_RJT_LOG = 0x03, /* logical error */
+ FC_FS_RJT_IUSIZ = 0x04, /* invalid IU size */
+ FC_FS_RJT_BSY = 0x05, /* logical busy */
+ FC_FS_RJT_PROTO = 0x07, /* protocol error */
+ FC_FS_RJT_UNABL = 0x09, /* unable to perform command request */
+ FC_FS_RJT_UNSUP = 0x0b, /* command not supported */
+};
+
+/*
+ * FS_RJT reason code explanations.
+ */
+enum fc_ct_explan {
+ FC_FS_EXP_NONE = 0x00, /* no additional explanation */
+ FC_FS_EXP_PID = 0x01, /* port ID not registered */
+ FC_FS_EXP_PNAM = 0x02, /* port name not registered */
+ FC_FS_EXP_NNAM = 0x03, /* node name not registered */
+ FC_FS_EXP_COS = 0x04, /* class of service not registered */
+ /* definitions not complete */
+};
+
+#endif /* _FC_GS_H_ */
diff --git a/include/scsi/fc/fc_ns.h b/include/scsi/fc/fc_ns.h
new file mode 100644
index 000000000000..790d7b97d4bc
--- /dev/null
+++ b/include/scsi/fc/fc_ns.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_NS_H_
+#define _FC_NS_H_
+
+/*
+ * Fibre Channel Services - Name Service (dNS)
+ * From T11.org FC-GS-2 Rev 5.3 November 1998.
+ */
+
+/*
+ * Common-transport sub-type for Name Server.
+ */
+#define FC_NS_SUBTYPE 2 /* fs_ct_hdr.ct_fs_subtype */
+
+/*
+ * Name server Requests.
+ * Note: this is an incomplete list, some unused requests are omitted.
+ */
+enum fc_ns_req {
+ FC_NS_GA_NXT = 0x0100, /* get all next */
+ FC_NS_GI_A = 0x0101, /* get identifiers - scope */
+ FC_NS_GPN_ID = 0x0112, /* get port name by ID */
+ FC_NS_GNN_ID = 0x0113, /* get node name by ID */
+ FC_NS_GID_PN = 0x0121, /* get ID for port name */
+ FC_NS_GID_NN = 0x0131, /* get IDs for node name */
+ FC_NS_GID_FT = 0x0171, /* get IDs by FC4 type */
+ FC_NS_GPN_FT = 0x0172, /* get port names by FC4 type */
+ FC_NS_GID_PT = 0x01a1, /* get IDs by port type */
+ FC_NS_RFT_ID = 0x0217, /* reg FC4 type for ID */
+ FC_NS_RPN_ID = 0x0212, /* reg port name for ID */
+ FC_NS_RNN_ID = 0x0213, /* reg node name for ID */
+};
+
+/*
+ * Port type values.
+ */
+enum fc_ns_pt {
+ FC_NS_UNID_PORT = 0x00, /* unidentified */
+ FC_NS_N_PORT = 0x01, /* N port */
+ FC_NS_NL_PORT = 0x02, /* NL port */
+ FC_NS_FNL_PORT = 0x03, /* F/NL port */
+ FC_NS_NX_PORT = 0x7f, /* Nx port */
+ FC_NS_F_PORT = 0x81, /* F port */
+ FC_NS_FL_PORT = 0x82, /* FL port */
+ FC_NS_E_PORT = 0x84, /* E port */
+ FC_NS_B_PORT = 0x85, /* B port */
+};
+
+/*
+ * Port type object.
+ */
+struct fc_ns_pt_obj {
+ __u8 pt_type;
+};
+
+/*
+ * Port ID object
+ */
+struct fc_ns_fid {
+ __u8 fp_flags; /* flags for responses only */
+ __u8 fp_fid[3];
+};
+
+/*
+ * fp_flags in port ID object, for responses only.
+ */
+#define FC_NS_FID_LAST 0x80 /* last object */
+
+/*
+ * FC4-types object.
+ */
+#define FC_NS_TYPES 256 /* number of possible FC-4 types */
+#define FC_NS_BPW 32 /* bits per word in bitmap */
+
+struct fc_ns_fts {
+ __be32 ff_type_map[FC_NS_TYPES / FC_NS_BPW]; /* bitmap of FC-4 types */
+};
+
+/*
+ * GID_PT request.
+ */
+struct fc_ns_gid_pt {
+ __u8 fn_pt_type;
+ __u8 fn_domain_id_scope;
+ __u8 fn_area_id_scope;
+ __u8 fn_resvd;
+};
+
+/*
+ * GID_FT or GPN_FT request.
+ */
+struct fc_ns_gid_ft {
+ __u8 fn_resvd;
+ __u8 fn_domain_id_scope;
+ __u8 fn_area_id_scope;
+ __u8 fn_fc4_type;
+};
+
+/*
+ * GPN_FT response.
+ */
+struct fc_gpn_ft_resp {
+ __u8 fp_flags; /* see fp_flags definitions above */
+ __u8 fp_fid[3]; /* port ID */
+ __be32 fp_resvd;
+ __be64 fp_wwpn; /* port name */
+};
+
+/*
+ * GID_PN request
+ */
+struct fc_ns_gid_pn {
+ __be64 fn_wwpn; /* port name */
+};
+
+/*
+ * GID_PN response
+ */
+struct fc_gid_pn_resp {
+ __u8 fp_resvd;
+ __u8 fp_fid[3]; /* port ID */
+};
+
+/*
+ * RFT_ID request - register FC-4 types for ID.
+ */
+struct fc_ns_rft_id {
+ struct fc_ns_fid fr_fid; /* port ID object */
+ struct fc_ns_fts fr_fts; /* FC-4 types object */
+};
+
+/*
+ * RPN_ID request - register port name for ID.
+ * RNN_ID request - register node name for ID.
+ */
+struct fc_ns_rn_id {
+ struct fc_ns_fid fr_fid; /* port ID object */
+ __be64 fr_wwn; /* node name or port name */
+} __attribute__((__packed__));
+
+#endif /* _FC_NS_H_ */
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h
new file mode 100644
index 000000000000..6300f556bce5
--- /dev/null
+++ b/include/scsi/fc_encode.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_ENCODE_H_
+#define _FC_ENCODE_H_
+#include <asm/unaligned.h>
+
+struct fc_ns_rft {
+ struct fc_ns_fid fid; /* port ID object */
+ struct fc_ns_fts fts; /* FC4-types object */
+};
+
+struct fc_ct_req {
+ struct fc_ct_hdr hdr;
+ union {
+ struct fc_ns_gid_ft gid;
+ struct fc_ns_rn_id rn;
+ struct fc_ns_rft rft;
+ } payload;
+};
+
+/**
+ * fill FC header fields in specified fc_frame
+ */
+static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl,
+ u32 did, u32 sid, enum fc_fh_type type,
+ u32 f_ctl, u32 parm_offset)
+{
+ struct fc_frame_header *fh;
+
+ fh = fc_frame_header_get(fp);
+ WARN_ON(r_ctl == 0);
+ fh->fh_r_ctl = r_ctl;
+ hton24(fh->fh_d_id, did);
+ hton24(fh->fh_s_id, sid);
+ fh->fh_type = type;
+ hton24(fh->fh_f_ctl, f_ctl);
+ fh->fh_cs_ctl = 0;
+ fh->fh_df_ctl = 0;
+ fh->fh_parm_offset = htonl(parm_offset);
+}
+
+/**
+ * fc_ct_hdr_fill- fills ct header and reset ct payload
+ * returns pointer to ct request.
+ */
+static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp,
+ unsigned int op, size_t req_size)
+{
+ struct fc_ct_req *ct;
+ size_t ct_plen;
+
+ ct_plen = sizeof(struct fc_ct_hdr) + req_size;
+ ct = fc_frame_payload_get(fp, ct_plen);
+ memset(ct, 0, ct_plen);
+ ct->hdr.ct_rev = FC_CT_REV;
+ ct->hdr.ct_fs_type = FC_FST_DIR;
+ ct->hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+ ct->hdr.ct_cmd = htons((u16) op);
+ return ct;
+}
+
+/**
+ * fc_ct_fill - Fill in a name service request frame
+ */
+static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp,
+ unsigned int op, enum fc_rctl *r_ctl, u32 *did,
+ enum fc_fh_type *fh_type)
+{
+ struct fc_ct_req *ct;
+
+ switch (op) {
+ case FC_NS_GPN_FT:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft));
+ ct->payload.gid.fn_fc4_type = FC_TYPE_FCP;
+ break;
+
+ case FC_NS_RFT_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft));
+ hton24(ct->payload.rft.fid.fp_fid,
+ fc_host_port_id(lport->host));
+ ct->payload.rft.fts = lport->fcts;
+ break;
+
+ case FC_NS_RPN_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id));
+ hton24(ct->payload.rn.fr_fid.fp_fid,
+ fc_host_port_id(lport->host));
+ ct->payload.rft.fts = lport->fcts;
+ put_unaligned_be64(lport->wwpn, &ct->payload.rn.fr_wwn);
+ break;
+
+ default:
+ FC_DBG("Invalid op code %x \n", op);
+ return -EINVAL;
+ }
+ *r_ctl = FC_RCTL_DD_UNSOL_CTL;
+ *did = FC_FID_DIR_SERV;
+ *fh_type = FC_TYPE_CT;
+ return 0;
+}
+
+/**
+ * fc_plogi_fill - Fill in plogi request frame
+ */
+static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
+ unsigned int op)
+{
+ struct fc_els_flogi *plogi;
+ struct fc_els_csp *csp;
+ struct fc_els_cssp *cp;
+
+ plogi = fc_frame_payload_get(fp, sizeof(*plogi));
+ memset(plogi, 0, sizeof(*plogi));
+ plogi->fl_cmd = (u8) op;
+ put_unaligned_be64(lport->wwpn, &plogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &plogi->fl_wwnn);
+
+ csp = &plogi->fl_csp;
+ csp->sp_hi_ver = 0x20;
+ csp->sp_lo_ver = 0x20;
+ csp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ csp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &plogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+ csp->sp_features = htons(FC_SP_FT_CIRO);
+ csp->sp_tot_seq = htons(255); /* seq. we accept */
+ csp->sp_rel_off = htons(0x1f);
+ csp->sp_e_d_tov = htonl(lport->e_d_tov);
+
+ cp->cp_rdfs = htons((u16) lport->mfs);
+ cp->cp_con_seq = htons(255);
+ cp->cp_open_seq = 1;
+}
+
+/**
+ * fc_flogi_fill - Fill in a flogi request frame.
+ */
+static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_csp *sp;
+ struct fc_els_cssp *cp;
+ struct fc_els_flogi *flogi;
+
+ flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+ memset(flogi, 0, sizeof(*flogi));
+ flogi->fl_cmd = (u8) ELS_FLOGI;
+ put_unaligned_be64(lport->wwpn, &flogi->fl_wwpn);
+ put_unaligned_be64(lport->wwnn, &flogi->fl_wwnn);
+ sp = &flogi->fl_csp;
+ sp->sp_hi_ver = 0x20;
+ sp->sp_lo_ver = 0x20;
+ sp->sp_bb_cred = htons(10); /* this gets set by gateway */
+ sp->sp_bb_data = htons((u16) lport->mfs);
+ cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */
+ cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+}
+
+/**
+ * fc_logo_fill - Fill in a logo request frame.
+ */
+static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_logo *logo;
+
+ logo = fc_frame_payload_get(fp, sizeof(*logo));
+ memset(logo, 0, sizeof(*logo));
+ logo->fl_cmd = ELS_LOGO;
+ hton24(logo->fl_n_port_id, fc_host_port_id(lport->host));
+ logo->fl_n_port_wwn = htonll(lport->wwpn);
+}
+
+/**
+ * fc_rtv_fill - Fill in RTV (read timeout value) request frame.
+ */
+static inline void fc_rtv_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_rtv *rtv;
+
+ rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+ memset(rtv, 0, sizeof(*rtv));
+ rtv->rtv_cmd = ELS_RTV;
+}
+
+/**
+ * fc_rec_fill - Fill in rec request frame
+ */
+static inline void fc_rec_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_rec *rec;
+ struct fc_exch *ep = fc_seq_exch(fr_seq(fp));
+
+ rec = fc_frame_payload_get(fp, sizeof(*rec));
+ memset(rec, 0, sizeof(*rec));
+ rec->rec_cmd = ELS_REC;
+ hton24(rec->rec_s_id, fc_host_port_id(lport->host));
+ rec->rec_ox_id = htons(ep->oxid);
+ rec->rec_rx_id = htons(ep->rxid);
+}
+
+/**
+ * fc_prli_fill - Fill in prli request frame
+ */
+static inline void fc_prli_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct {
+ struct fc_els_prli prli;
+ struct fc_els_spp spp;
+ } *pp;
+
+ pp = fc_frame_payload_get(fp, sizeof(*pp));
+ memset(pp, 0, sizeof(*pp));
+ pp->prli.prli_cmd = ELS_PRLI;
+ pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
+ pp->prli.prli_len = htons(sizeof(*pp));
+ pp->spp.spp_type = FC_TYPE_FCP;
+ pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
+ pp->spp.spp_params = htonl(lport->service_params);
+}
+
+/**
+ * fc_scr_fill - Fill in a scr request frame.
+ */
+static inline void fc_scr_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_scr *scr;
+
+ scr = fc_frame_payload_get(fp, sizeof(*scr));
+ memset(scr, 0, sizeof(*scr));
+ scr->scr_cmd = ELS_SCR;
+ scr->scr_reg_func = ELS_SCRF_FULL;
+}
+
+/**
+ * fc_els_fill - Fill in an ELS request frame
+ */
+static inline int fc_els_fill(struct fc_lport *lport, struct fc_rport *rport,
+ struct fc_frame *fp, unsigned int op,
+ enum fc_rctl *r_ctl, u32 *did, enum fc_fh_type *fh_type)
+{
+ switch (op) {
+ case ELS_PLOGI:
+ fc_plogi_fill(lport, fp, ELS_PLOGI);
+ *did = rport->port_id;
+ break;
+
+ case ELS_FLOGI:
+ fc_flogi_fill(lport, fp);
+ *did = FC_FID_FLOGI;
+ break;
+
+ case ELS_LOGO:
+ fc_logo_fill(lport, fp);
+ *did = FC_FID_FLOGI;
+ /*
+ * if rport is valid then it
+ * is port logo, therefore
+ * set did to rport id.
+ */
+ if (rport)
+ *did = rport->port_id;
+ break;
+
+ case ELS_RTV:
+ fc_rtv_fill(lport, fp);
+ *did = rport->port_id;
+ break;
+
+ case ELS_REC:
+ fc_rec_fill(lport, fp);
+ *did = rport->port_id;
+ break;
+
+ case ELS_PRLI:
+ fc_prli_fill(lport, fp);
+ *did = rport->port_id;
+ break;
+
+ case ELS_SCR:
+ fc_scr_fill(lport, fp);
+ *did = FC_FID_FCTRL;
+ break;
+
+ default:
+ FC_DBG("Invalid op code %x \n", op);
+ return -EINVAL;
+ }
+
+ *r_ctl = FC_RCTL_ELS_REQ;
+ *fh_type = FC_TYPE_ELS;
+ return 0;
+}
+#endif /* _FC_ENCODE_H_ */
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
new file mode 100644
index 000000000000..04d34a71355f
--- /dev/null
+++ b/include/scsi/fc_frame.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FRAME_H_
+#define _FC_FRAME_H_
+
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_encaps.h>
+
+/*
+ * The fc_frame interface is used to pass frame data between functions.
+ * The frame includes the data buffer, length, and SOF / EOF delimiter types.
+ * A pointer to the port structure of the receiving port is also includeded.
+ */
+
+#define FC_FRAME_HEADROOM 32 /* headroom for VLAN + FCoE headers */
+#define FC_FRAME_TAILROOM 8 /* trailer space for FCoE */
+
+/*
+ * Information about an individual fibre channel frame received or to be sent.
+ * The buffer may be in up to 4 additional non-contiguous sections,
+ * but the linear section must hold the frame header.
+ */
+#define FC_FRAME_SG_LEN 4 /* scatter/gather list maximum length */
+
+#define fp_skb(fp) (&((fp)->skb))
+#define fr_hdr(fp) ((fp)->skb.data)
+#define fr_len(fp) ((fp)->skb.len)
+#define fr_cb(fp) ((struct fcoe_rcv_info *)&((fp)->skb.cb[0]))
+#define fr_dev(fp) (fr_cb(fp)->fr_dev)
+#define fr_seq(fp) (fr_cb(fp)->fr_seq)
+#define fr_sof(fp) (fr_cb(fp)->fr_sof)
+#define fr_eof(fp) (fr_cb(fp)->fr_eof)
+#define fr_flags(fp) (fr_cb(fp)->fr_flags)
+#define fr_max_payload(fp) (fr_cb(fp)->fr_max_payload)
+#define fr_cmd(fp) (fr_cb(fp)->fr_cmd)
+#define fr_dir(fp) (fr_cmd(fp)->sc_data_direction)
+#define fr_crc(fp) (fr_cb(fp)->fr_crc)
+
+struct fc_frame {
+ struct sk_buff skb;
+};
+
+struct fcoe_rcv_info {
+ struct packet_type *ptype;
+ struct fc_lport *fr_dev; /* transport layer private pointer */
+ struct fc_seq *fr_seq; /* for use with exchange manager */
+ struct scsi_cmnd *fr_cmd; /* for use of scsi command */
+ u32 fr_crc;
+ u16 fr_max_payload; /* max FC payload */
+ enum fc_sof fr_sof; /* start of frame delimiter */
+ enum fc_eof fr_eof; /* end of frame delimiter */
+ u8 fr_flags; /* flags - see below */
+};
+
+
+/*
+ * Get fc_frame pointer for an skb that's already been imported.
+ */
+static inline struct fcoe_rcv_info *fcoe_dev_from_skb(const struct sk_buff *skb)
+{
+ BUILD_BUG_ON(sizeof(struct fcoe_rcv_info) > sizeof(skb->cb));
+ return (struct fcoe_rcv_info *) skb->cb;
+}
+
+/*
+ * fr_flags.
+ */
+#define FCPHF_CRC_UNCHECKED 0x01 /* CRC not computed, still appended */
+
+/*
+ * Initialize a frame.
+ * We don't do a complete memset here for performance reasons.
+ * The caller must set fr_free, fr_hdr, fr_len, fr_sof, and fr_eof eventually.
+ */
+static inline void fc_frame_init(struct fc_frame *fp)
+{
+ fr_dev(fp) = NULL;
+ fr_seq(fp) = NULL;
+ fr_flags(fp) = 0;
+}
+
+struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len);
+
+struct fc_frame *__fc_frame_alloc(size_t payload_len);
+
+/*
+ * Get frame for sending via port.
+ */
+static inline struct fc_frame *_fc_frame_alloc(struct fc_lport *dev,
+ size_t payload_len)
+{
+ return __fc_frame_alloc(payload_len);
+}
+
+/*
+ * Allocate fc_frame structure and buffer. Set the initial length to
+ * payload_size + sizeof (struct fc_frame_header).
+ */
+static inline struct fc_frame *fc_frame_alloc(struct fc_lport *dev, size_t len)
+{
+ struct fc_frame *fp;
+
+ /*
+ * Note: Since len will often be a constant multiple of 4,
+ * this check will usually be evaluated and eliminated at compile time.
+ */
+ if ((len % 4) != 0)
+ fp = fc_frame_alloc_fill(dev, len);
+ else
+ fp = _fc_frame_alloc(dev, len);
+ return fp;
+}
+
+/*
+ * Free the fc_frame structure and buffer.
+ */
+static inline void fc_frame_free(struct fc_frame *fp)
+{
+ kfree_skb(fp_skb(fp));
+}
+
+static inline int fc_frame_is_linear(struct fc_frame *fp)
+{
+ return !skb_is_nonlinear(fp_skb(fp));
+}
+
+/*
+ * Get frame header from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking.
+ */
+static inline
+struct fc_frame_header *fc_frame_header_get(const struct fc_frame *fp)
+{
+ WARN_ON(fr_len(fp) < sizeof(struct fc_frame_header));
+ return (struct fc_frame_header *) fr_hdr(fp);
+}
+
+/*
+ * Get frame payload from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking.
+ * The len parameter is the minimum length for the payload portion.
+ * Returns NULL if the frame is too short.
+ *
+ * This assumes the interesting part of the payload is in the first part
+ * of the buffer for received data. This may not be appropriate to use for
+ * buffers being transmitted.
+ */
+static inline void *fc_frame_payload_get(const struct fc_frame *fp,
+ size_t len)
+{
+ void *pp = NULL;
+
+ if (fr_len(fp) >= sizeof(struct fc_frame_header) + len)
+ pp = fc_frame_header_get(fp) + 1;
+ return pp;
+}
+
+/*
+ * Get frame payload opcode (first byte) from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking. Return 0
+ * if the frame has no payload.
+ */
+static inline u8 fc_frame_payload_op(const struct fc_frame *fp)
+{
+ u8 *cp;
+
+ cp = fc_frame_payload_get(fp, sizeof(u8));
+ if (!cp)
+ return 0;
+ return *cp;
+
+}
+
+/*
+ * Get FC class from frame.
+ */
+static inline enum fc_class fc_frame_class(const struct fc_frame *fp)
+{
+ return fc_sof_class(fr_sof(fp));
+}
+
+/*
+ * Check the CRC in a frame.
+ * The CRC immediately follows the last data item *AFTER* the length.
+ * The return value is zero if the CRC matches.
+ */
+u32 fc_frame_crc_check(struct fc_frame *);
+
+static inline u8 fc_frame_rctl(const struct fc_frame *fp)
+{
+ return fc_frame_header_get(fp)->fh_r_ctl;
+}
+
+static inline bool fc_frame_is_cmd(const struct fc_frame *fp)
+{
+ return fc_frame_rctl(fp) == FC_RCTL_DD_UNSOL_CMD;
+}
+
+static inline bool fc_frame_is_read(const struct fc_frame *fp)
+{
+ if (fc_frame_is_cmd(fp) && fr_cmd(fp))
+ return fr_dir(fp) == DMA_FROM_DEVICE;
+ return false;
+}
+
+static inline bool fc_frame_is_write(const struct fc_frame *fp)
+{
+ if (fc_frame_is_cmd(fp) && fr_cmd(fp))
+ return fr_dir(fp) == DMA_TO_DEVICE;
+ return false;
+}
+
+/*
+ * Check for leaks.
+ * Print the frame header of any currently allocated frame, assuming there
+ * should be none at this point.
+ */
+void fc_frame_leak_check(void);
+
+#endif /* _FC_FRAME_H_ */
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h
new file mode 100644
index 000000000000..8dca2af14ffc
--- /dev/null
+++ b/include/scsi/fc_transport_fcoe.h
@@ -0,0 +1,54 @@
+#ifndef FC_TRANSPORT_FCOE_H
+#define FC_TRANSPORT_FCOE_H
+
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <scsi/scsi_host.h>
+#include <scsi/libfc.h>
+
+/**
+ * struct fcoe_transport - FCoE transport struct for generic transport
+ * for Ethernet devices as well as pure HBAs
+ *
+ * @name: name for thsi transport
+ * @bus: physical bus type (pci_bus_type)
+ * @driver: physical bus driver for network device
+ * @create: entry create function
+ * @destroy: exit destroy function
+ * @list: list of transports
+ */
+struct fcoe_transport {
+ char *name;
+ unsigned short vendor;
+ unsigned short device;
+ struct bus_type *bus;
+ struct device_driver *driver;
+ int (*create)(struct net_device *device);
+ int (*destroy)(struct net_device *device);
+ bool (*match)(struct net_device *device);
+ struct list_head list;
+ struct list_head devlist;
+ struct mutex devlock;
+};
+
+/**
+ * MODULE_ALIAS_FCOE_PCI
+ *
+ * some care must be taken with this, vendor and device MUST be a hex value
+ * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB)
+ */
+#define MODULE_ALIAS_FCOE_PCI(vendor, device) \
+ MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device))
+
+/* exported funcs */
+int fcoe_transport_attach(struct net_device *netdev);
+int fcoe_transport_release(struct net_device *netdev);
+int fcoe_transport_register(struct fcoe_transport *t);
+int fcoe_transport_unregister(struct fcoe_transport *t);
+int fcoe_load_transport_driver(struct net_device *netdev);
+int __init fcoe_transport_init(void);
+int __exit fcoe_transport_exit(void);
+
+/* fcow_sw is the default transport */
+extern struct fcoe_transport fcoe_sw_transport;
+#endif /* FC_TRANSPORT_FCOE_H */
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index 0c9514de5df7..d0ed5226f8c4 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -333,8 +333,11 @@ enum iscsi_host_param {
#define CAP_TEXT_NEGO 0x80
#define CAP_MARKERS 0x100
#define CAP_FW_DB 0x200
-#define CAP_SENDTARGETS_OFFLOAD 0x400
-#define CAP_DATA_PATH_OFFLOAD 0x800
+#define CAP_SENDTARGETS_OFFLOAD 0x400 /* offload discovery process */
+#define CAP_DATA_PATH_OFFLOAD 0x800 /* offload entire IO path */
+#define CAP_DIGEST_OFFLOAD 0x1000 /* offload hdr and data digests */
+#define CAP_PADDING_OFFLOAD 0x2000 /* offload padding insertion, removal,
+ and verification */
/*
* These flags describes reason of stop_conn() call
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
new file mode 100644
index 000000000000..9f2876397dda
--- /dev/null
+++ b/include/scsi/libfc.h
@@ -0,0 +1,938 @@
+/*
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _LIBFC_H_
+#define _LIBFC_H_
+
+#include <linux/timer.h>
+#include <linux/if.h>
+
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/fc_frame.h>
+
+#define LIBFC_DEBUG
+
+#ifdef LIBFC_DEBUG
+/* Log messages */
+#define FC_DBG(fmt, args...) \
+ do { \
+ printk(KERN_INFO "%s " fmt, __func__, ##args); \
+ } while (0)
+#else
+#define FC_DBG(fmt, args...)
+#endif
+
+/*
+ * libfc error codes
+ */
+#define FC_NO_ERR 0 /* no error */
+#define FC_EX_TIMEOUT 1 /* Exchange timeout */
+#define FC_EX_CLOSED 2 /* Exchange closed */
+
+/* some helpful macros */
+
+#define ntohll(x) be64_to_cpu(x)
+#define htonll(x) cpu_to_be64(x)
+
+#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2]))
+
+#define hton24(p, v) do { \
+ p[0] = (((v) >> 16) & 0xFF); \
+ p[1] = (((v) >> 8) & 0xFF); \
+ p[2] = ((v) & 0xFF); \
+ } while (0)
+
+/*
+ * FC HBA status
+ */
+#define FC_PAUSE (1 << 1)
+#define FC_LINK_UP (1 << 0)
+
+enum fc_lport_state {
+ LPORT_ST_NONE = 0,
+ LPORT_ST_FLOGI,
+ LPORT_ST_DNS,
+ LPORT_ST_RPN_ID,
+ LPORT_ST_RFT_ID,
+ LPORT_ST_SCR,
+ LPORT_ST_READY,
+ LPORT_ST_LOGO,
+ LPORT_ST_RESET
+};
+
+enum fc_disc_event {
+ DISC_EV_NONE = 0,
+ DISC_EV_SUCCESS,
+ DISC_EV_FAILED
+};
+
+enum fc_rport_state {
+ RPORT_ST_NONE = 0,
+ RPORT_ST_INIT, /* initialized */
+ RPORT_ST_PLOGI, /* waiting for PLOGI completion */
+ RPORT_ST_PRLI, /* waiting for PRLI completion */
+ RPORT_ST_RTV, /* waiting for RTV completion */
+ RPORT_ST_READY, /* ready for use */
+ RPORT_ST_LOGO, /* port logout sent */
+};
+
+enum fc_rport_trans_state {
+ FC_PORTSTATE_ROGUE,
+ FC_PORTSTATE_REAL,
+};
+
+/**
+ * struct fc_disc_port - temporary discovery port to hold rport identifiers
+ * @lp: Fibre Channel host port instance
+ * @peers: node for list management during discovery and RSCN processing
+ * @ids: identifiers structure to pass to fc_remote_port_add()
+ * @rport_work: work struct for starting the rport state machine
+ */
+struct fc_disc_port {
+ struct fc_lport *lp;
+ struct list_head peers;
+ struct fc_rport_identifiers ids;
+ struct work_struct rport_work;
+};
+
+enum fc_rport_event {
+ RPORT_EV_NONE = 0,
+ RPORT_EV_CREATED,
+ RPORT_EV_FAILED,
+ RPORT_EV_STOP,
+ RPORT_EV_LOGO
+};
+
+struct fc_rport_operations {
+ void (*event_callback)(struct fc_lport *, struct fc_rport *,
+ enum fc_rport_event);
+};
+
+/**
+ * struct fc_rport_libfc_priv - libfc internal information about a remote port
+ * @local_port: Fibre Channel host port instance
+ * @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges
+ * @flags: REC and RETRY supported flags
+ * @max_seq: maximum number of concurrent sequences
+ * @retries: retry count in current state
+ * @e_d_tov: error detect timeout value (in msec)
+ * @r_a_tov: resource allocation timeout value (in msec)
+ * @rp_mutex: mutex protects rport
+ * @retry_work:
+ * @event_callback: Callback for rport READY, FAILED or LOGO
+ */
+struct fc_rport_libfc_priv {
+ struct fc_lport *local_port;
+ enum fc_rport_state rp_state;
+ u16 flags;
+ #define FC_RP_FLAGS_REC_SUPPORTED (1 << 0)
+ #define FC_RP_FLAGS_RETRY (1 << 1)
+ u16 max_seq;
+ unsigned int retries;
+ unsigned int e_d_tov;
+ unsigned int r_a_tov;
+ enum fc_rport_trans_state trans_state;
+ struct mutex rp_mutex;
+ struct delayed_work retry_work;
+ enum fc_rport_event event;
+ struct fc_rport_operations *ops;
+ struct list_head peers;
+ struct work_struct event_work;
+};
+
+#define PRIV_TO_RPORT(x) \
+ (struct fc_rport *)((void *)x - sizeof(struct fc_rport));
+#define RPORT_TO_PRIV(x) \
+ (struct fc_rport_libfc_priv *)((void *)x + sizeof(struct fc_rport));
+
+struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
+
+static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
+{
+ rport->node_name = wwnn;
+ rport->port_name = wwpn;
+}
+
+/*
+ * fcoe stats structure
+ */
+struct fcoe_dev_stats {
+ u64 SecondsSinceLastReset;
+ u64 TxFrames;
+ u64 TxWords;
+ u64 RxFrames;
+ u64 RxWords;
+ u64 ErrorFrames;
+ u64 DumpedFrames;
+ u64 LinkFailureCount;
+ u64 LossOfSignalCount;
+ u64 InvalidTxWordCount;
+ u64 InvalidCRCCount;
+ u64 InputRequests;
+ u64 OutputRequests;
+ u64 ControlRequests;
+ u64 InputMegabytes;
+ u64 OutputMegabytes;
+};
+
+/*
+ * els data is used for passing ELS respone specific
+ * data to send ELS response mainly using infomation
+ * in exchange and sequence in EM layer.
+ */
+struct fc_seq_els_data {
+ struct fc_frame *fp;
+ enum fc_els_rjt_reason reason;
+ enum fc_els_rjt_explan explan;
+};
+
+/*
+ * FCP request structure, one for each scsi cmd request
+ */
+struct fc_fcp_pkt {
+ /*
+ * housekeeping stuff
+ */
+ struct fc_lport *lp; /* handle to hba struct */
+ u16 state; /* scsi_pkt state state */
+ u16 tgt_flags; /* target flags */
+ atomic_t ref_cnt; /* fcp pkt ref count */
+ spinlock_t scsi_pkt_lock; /* Must be taken before the host lock
+ * if both are held at the same time */
+ /*
+ * SCSI I/O related stuff
+ */
+ struct scsi_cmnd *cmd; /* scsi command pointer. set/clear
+ * under host lock */
+ struct list_head list; /* tracks queued commands. access under
+ * host lock */
+ /*
+ * timeout related stuff
+ */
+ struct timer_list timer; /* command timer */
+ struct completion tm_done;
+ int wait_for_comp;
+ unsigned long start_time; /* start jiffie */
+ unsigned long end_time; /* end jiffie */
+ unsigned long last_pkt_time; /* jiffies of last frame received */
+
+ /*
+ * scsi cmd and data transfer information
+ */
+ u32 data_len;
+ /*
+ * transport related veriables
+ */
+ struct fcp_cmnd cdb_cmd;
+ size_t xfer_len;
+ u32 xfer_contig_end; /* offset of end of contiguous xfer */
+ u16 max_payload; /* max payload size in bytes */
+
+ /*
+ * scsi/fcp return status
+ */
+ u32 io_status; /* SCSI result upper 24 bits */
+ u8 cdb_status;
+ u8 status_code; /* FCP I/O status */
+ /* bit 3 Underrun bit 2: overrun */
+ u8 scsi_comp_flags;
+ u32 req_flags; /* bit 0: read bit:1 write */
+ u32 scsi_resid; /* residule length */
+
+ struct fc_rport *rport; /* remote port pointer */
+ struct fc_seq *seq_ptr; /* current sequence pointer */
+ /*
+ * Error Processing
+ */
+ u8 recov_retry; /* count of recovery retries */
+ struct fc_seq *recov_seq; /* sequence for REC or SRR */
+};
+
+/*
+ * Structure and function definitions for managing Fibre Channel Exchanges
+ * and Sequences
+ *
+ * fc_exch holds state for one exchange and links to its active sequence.
+ *
+ * fc_seq holds the state for an individual sequence.
+ */
+
+struct fc_exch_mgr;
+
+/*
+ * Sequence.
+ */
+struct fc_seq {
+ u8 id; /* seq ID */
+ u16 ssb_stat; /* status flags for sequence status block */
+ u16 cnt; /* frames sent so far on sequence */
+ u32 rec_data; /* FC-4 value for REC */
+};
+
+#define FC_EX_DONE (1 << 0) /* ep is completed */
+#define FC_EX_RST_CLEANUP (1 << 1) /* reset is forcing completion */
+
+/*
+ * Exchange.
+ *
+ * Locking notes: The ex_lock protects following items:
+ * state, esb_stat, f_ctl, seq.ssb_stat
+ * seq_id
+ * sequence allocation
+ */
+struct fc_exch {
+ struct fc_exch_mgr *em; /* exchange manager */
+ u32 state; /* internal driver state */
+ u16 xid; /* our exchange ID */
+ struct list_head ex_list; /* free or busy list linkage */
+ spinlock_t ex_lock; /* lock covering exchange state */
+ atomic_t ex_refcnt; /* reference counter */
+ struct delayed_work timeout_work; /* timer for upper level protocols */
+ struct fc_lport *lp; /* fc device instance */
+ u16 oxid; /* originator's exchange ID */
+ u16 rxid; /* responder's exchange ID */
+ u32 oid; /* originator's FCID */
+ u32 sid; /* source FCID */
+ u32 did; /* destination FCID */
+ u32 esb_stat; /* exchange status for ESB */
+ u32 r_a_tov; /* r_a_tov from rport (msec) */
+ u8 seq_id; /* next sequence ID to use */
+ u32 f_ctl; /* F_CTL flags for sequences */
+ u8 fh_type; /* frame type */
+ enum fc_class class; /* class of service */
+ struct fc_seq seq; /* single sequence */
+ /*
+ * Handler for responses to this current exchange.
+ */
+ void (*resp)(struct fc_seq *, struct fc_frame *, void *);
+ void (*destructor)(struct fc_seq *, void *);
+ /*
+ * arg is passed as void pointer to exchange
+ * resp and destructor handlers
+ */
+ void *arg;
+};
+#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+
+struct libfc_function_template {
+
+ /**
+ * Mandatory Fields
+ *
+ * These handlers must be implemented by the LLD.
+ */
+
+ /*
+ * Interface to send a FC frame
+ */
+ int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
+
+ /**
+ * Optional Fields
+ *
+ * The LLD may choose to implement any of the following handlers.
+ * If LLD doesn't specify hander and leaves its pointer NULL then
+ * the default libfc function will be used for that handler.
+ */
+
+ /**
+ * ELS/CT interfaces
+ */
+
+ /*
+ * elsct_send - sends ELS/CT frame
+ */
+ struct fc_seq *(*elsct_send)(struct fc_lport *lport,
+ struct fc_rport *rport,
+ struct fc_frame *fp,
+ unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void *arg, u32 timer_msec);
+ /**
+ * Exhance Manager interfaces
+ */
+
+ /*
+ * Send the FC frame payload using a new exchange and sequence.
+ *
+ * The frame pointer with some of the header's fields must be
+ * filled before calling exch_seq_send(), those fields are,
+ *
+ * - routing control
+ * - FC port did
+ * - FC port sid
+ * - FC header type
+ * - frame control
+ * - parameter or relative offset
+ *
+ * The exchange response handler is set in this routine to resp()
+ * function pointer. It can be called in two scenarios: if a timeout
+ * occurs or if a response frame is received for the exchange. The
+ * fc_frame pointer in response handler will also indicate timeout
+ * as error using IS_ERR related macros.
+ *
+ * The exchange destructor handler is also set in this routine.
+ * The destructor handler is invoked by EM layer when exchange
+ * is about to free, this can be used by caller to free its
+ * resources along with exchange free.
+ *
+ * The arg is passed back to resp and destructor handler.
+ *
+ * The timeout value (in msec) for an exchange is set if non zero
+ * timer_msec argument is specified. The timer is canceled when
+ * it fires or when the exchange is done. The exchange timeout handler
+ * is registered by EM layer.
+ */
+ struct fc_seq *(*exch_seq_send)(struct fc_lport *lp,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *sp,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *sp,
+ void *arg),
+ void *arg, unsigned int timer_msec);
+
+ /*
+ * send a frame using existing sequence and exchange.
+ */
+ int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp,
+ struct fc_frame *fp);
+
+ /*
+ * Send ELS response using mainly infomation
+ * in exchange and sequence in EM layer.
+ */
+ void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data);
+
+ /*
+ * Abort an exchange and sequence. Generally called because of a
+ * exchange timeout or an abort from the upper layer.
+ *
+ * A timer_msec can be specified for abort timeout, if non-zero
+ * timer_msec value is specified then exchange resp handler
+ * will be called with timeout error if no response to abort.
+ */
+ int (*seq_exch_abort)(const struct fc_seq *req_sp,
+ unsigned int timer_msec);
+
+ /*
+ * Indicate that an exchange/sequence tuple is complete and the memory
+ * allocated for the related objects may be freed.
+ */
+ void (*exch_done)(struct fc_seq *sp);
+
+ /*
+ * Assigns a EM and a free XID for an new exchange and then
+ * allocates a new exchange and sequence pair.
+ * The fp can be used to determine free XID.
+ */
+ struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp);
+
+ /*
+ * Release previously assigned XID by exch_get API.
+ * The LLD may implement this if XID is assigned by LLD
+ * in exch_get().
+ */
+ void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ u16 ex_id);
+
+ /*
+ * Start a new sequence on the same exchange/sequence tuple.
+ */
+ struct fc_seq *(*seq_start_next)(struct fc_seq *sp);
+
+ /*
+ * Reset an exchange manager, completing all sequences and exchanges.
+ * If s_id is non-zero, reset only exchanges originating from that FID.
+ * If d_id is non-zero, reset only exchanges sending to that FID.
+ */
+ void (*exch_mgr_reset)(struct fc_exch_mgr *,
+ u32 s_id, u32 d_id);
+
+ void (*rport_flush_queue)(void);
+ /**
+ * Local Port interfaces
+ */
+
+ /*
+ * Receive a frame to a local port.
+ */
+ void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp,
+ struct fc_frame *fp);
+
+ int (*lport_reset)(struct fc_lport *);
+
+ /**
+ * Remote Port interfaces
+ */
+
+ /*
+ * Initiates the RP state machine. It is called from the LP module.
+ * This function will issue the following commands to the N_Port
+ * identified by the FC ID provided.
+ *
+ * - PLOGI
+ * - PRLI
+ * - RTV
+ */
+ int (*rport_login)(struct fc_rport *rport);
+
+ /*
+ * Logoff, and remove the rport from the transport if
+ * it had been added. This will send a LOGO to the target.
+ */
+ int (*rport_logoff)(struct fc_rport *rport);
+
+ /*
+ * Recieve a request from a remote port.
+ */
+ void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
+ struct fc_rport *);
+
+ struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
+
+ /**
+ * FCP interfaces
+ */
+
+ /*
+ * Send a fcp cmd from fsp pkt.
+ * Called with the SCSI host lock unlocked and irqs disabled.
+ *
+ * The resp handler is called when FCP_RSP received.
+ *
+ */
+ int (*fcp_cmd_send)(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+ void (*resp)(struct fc_seq *, struct fc_frame *fp,
+ void *arg));
+
+ /*
+ * Used at least durring linkdown and reset
+ */
+ void (*fcp_cleanup)(struct fc_lport *lp);
+
+ /*
+ * Abort all I/O on a local port
+ */
+ void (*fcp_abort_io)(struct fc_lport *lp);
+
+ /**
+ * Discovery interfaces
+ */
+
+ void (*disc_recv_req)(struct fc_seq *,
+ struct fc_frame *, struct fc_lport *);
+
+ /*
+ * Start discovery for a local port.
+ */
+ void (*disc_start)(void (*disc_callback)(struct fc_lport *,
+ enum fc_disc_event),
+ struct fc_lport *);
+
+ /*
+ * Stop discovery for a given lport. This will remove
+ * all discovered rports
+ */
+ void (*disc_stop) (struct fc_lport *);
+
+ /*
+ * Stop discovery for a given lport. This will block
+ * until all discovered rports are deleted from the
+ * FC transport class
+ */
+ void (*disc_stop_final) (struct fc_lport *);
+};
+
+/* information used by the discovery layer */
+struct fc_disc {
+ unsigned char retry_count;
+ unsigned char delay;
+ unsigned char pending;
+ unsigned char requested;
+ unsigned short seq_count;
+ unsigned char buf_len;
+ enum fc_disc_event event;
+
+ void (*disc_callback)(struct fc_lport *,
+ enum fc_disc_event);
+
+ struct list_head rports;
+ struct fc_lport *lport;
+ struct mutex disc_mutex;
+ struct fc_gpn_ft_resp partial_buf; /* partial name buffer */
+ struct delayed_work disc_work;
+};
+
+struct fc_lport {
+ struct list_head list;
+
+ /* Associations */
+ struct Scsi_Host *host;
+ struct fc_exch_mgr *emp;
+ struct fc_rport *dns_rp;
+ struct fc_rport *ptp_rp;
+ void *scsi_priv;
+ struct fc_disc disc;
+
+ /* Operational Information */
+ struct libfc_function_template tt;
+ u16 link_status;
+ enum fc_lport_state state;
+ unsigned long boot_time;
+
+ struct fc_host_statistics host_stats;
+ struct fcoe_dev_stats *dev_stats[NR_CPUS];
+ u64 wwpn;
+ u64 wwnn;
+ u8 retry_count;
+
+ /* Capabilities */
+ u32 sg_supp:1; /* scatter gather supported */
+ u32 seq_offload:1; /* seq offload supported */
+ u32 crc_offload:1; /* crc offload supported */
+ u32 lro_enabled:1; /* large receive offload */
+ u32 mfs; /* max FC payload size */
+ unsigned int service_params;
+ unsigned int e_d_tov;
+ unsigned int r_a_tov;
+ u8 max_retry_count;
+ u16 link_speed;
+ u16 link_supported_speeds;
+ u16 lro_xid; /* max xid for fcoe lro */
+ struct fc_ns_fts fcts; /* FC-4 type masks */
+ struct fc_els_rnid_gen rnid_gen; /* RNID information */
+
+ /* Semaphores */
+ struct mutex lp_mutex;
+
+ /* Miscellaneous */
+ struct delayed_work retry_work;
+ struct delayed_work disc_work;
+};
+
+/**
+ * FC_LPORT HELPER FUNCTIONS
+ *****************************/
+static inline void *lport_priv(const struct fc_lport *lp)
+{
+ return (void *)(lp + 1);
+}
+
+static inline int fc_lport_test_ready(struct fc_lport *lp)
+{
+ return lp->state == LPORT_ST_READY;
+}
+
+static inline void fc_set_wwnn(struct fc_lport *lp, u64 wwnn)
+{
+ lp->wwnn = wwnn;
+}
+
+static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn)
+{
+ lp->wwpn = wwnn;
+}
+
+static inline void fc_lport_state_enter(struct fc_lport *lp,
+ enum fc_lport_state state)
+{
+ if (state != lp->state)
+ lp->retry_count = 0;
+ lp->state = state;
+}
+
+
+/**
+ * LOCAL PORT LAYER
+ *****************************/
+int fc_lport_init(struct fc_lport *lp);
+
+/*
+ * Destroy the specified local port by finding and freeing all
+ * fc_rports associated with it and then by freeing the fc_lport
+ * itself.
+ */
+int fc_lport_destroy(struct fc_lport *lp);
+
+/*
+ * Logout the specified local port from the fabric
+ */
+int fc_fabric_logoff(struct fc_lport *lp);
+
+/*
+ * Initiate the LP state machine. This handler will use fc_host_attr
+ * to store the FLOGI service parameters, so fc_host_attr must be
+ * initialized before calling this handler.
+ */
+int fc_fabric_login(struct fc_lport *lp);
+
+/*
+ * The link is up for the given local port.
+ */
+void fc_linkup(struct fc_lport *);
+
+/*
+ * Link is down for the given local port.
+ */
+void fc_linkdown(struct fc_lport *);
+
+/*
+ * Pause and unpause traffic.
+ */
+void fc_pause(struct fc_lport *);
+void fc_unpause(struct fc_lport *);
+
+/*
+ * Configure the local port.
+ */
+int fc_lport_config(struct fc_lport *);
+
+/*
+ * Reset the local port.
+ */
+int fc_lport_reset(struct fc_lport *);
+
+/*
+ * Set the mfs or reset
+ */
+int fc_set_mfs(struct fc_lport *lp, u32 mfs);
+
+
+/**
+ * REMOTE PORT LAYER
+ *****************************/
+int fc_rport_init(struct fc_lport *lp);
+void fc_rport_terminate_io(struct fc_rport *rp);
+
+/**
+ * DISCOVERY LAYER
+ *****************************/
+int fc_disc_init(struct fc_lport *lp);
+
+
+/**
+ * SCSI LAYER
+ *****************************/
+/*
+ * Initialize the SCSI block of libfc
+ */
+int fc_fcp_init(struct fc_lport *);
+
+/*
+ * This section provides an API which allows direct interaction
+ * with the SCSI-ml. Each of these functions satisfies a function
+ * pointer defined in Scsi_Host and therefore is always called
+ * directly from the SCSI-ml.
+ */
+int fc_queuecommand(struct scsi_cmnd *sc_cmd,
+ void (*done)(struct scsi_cmnd *));
+
+/*
+ * complete processing of a fcp packet
+ *
+ * This function may sleep if a fsp timer is pending.
+ * The host lock must not be held by caller.
+ */
+void fc_fcp_complete(struct fc_fcp_pkt *fsp);
+
+/*
+ * Send an ABTS frame to the target device. The sc_cmd argument
+ * is a pointer to the SCSI command to be aborted.
+ */
+int fc_eh_abort(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Reset a LUN by sending send the tm cmd to the target.
+ */
+int fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Reset the host adapter.
+ */
+int fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Check rport status.
+ */
+int fc_slave_alloc(struct scsi_device *sdev);
+
+/*
+ * Adjust the queue depth.
+ */
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth);
+
+/*
+ * Change the tag type.
+ */
+int fc_change_queue_type(struct scsi_device *sdev, int tag_type);
+
+/*
+ * Free memory pools used by the FCP layer.
+ */
+void fc_fcp_destroy(struct fc_lport *);
+
+/**
+ * ELS/CT interface
+ *****************************/
+/*
+ * Initializes ELS/CT interface
+ */
+int fc_elsct_init(struct fc_lport *lp);
+
+
+/**
+ * EXCHANGE MANAGER LAYER
+ *****************************/
+/*
+ * Initializes Exchange Manager related
+ * function pointers in struct libfc_function_template.
+ */
+int fc_exch_init(struct fc_lport *lp);
+
+/*
+ * Allocates an Exchange Manager (EM).
+ *
+ * The EM manages exchanges for their allocation and
+ * free, also allows exchange lookup for received
+ * frame.
+ *
+ * The class is used for initializing FC class of
+ * allocated exchange from EM.
+ *
+ * The min_xid and max_xid will limit new
+ * exchange ID (XID) within this range for
+ * a new exchange.
+ * The LLD may choose to have multiple EMs,
+ * e.g. one EM instance per CPU receive thread in LLD.
+ * The LLD can use exch_get() of struct libfc_function_template
+ * to specify XID for a new exchange within
+ * a specified EM instance.
+ *
+ * The em_idx to uniquely identify an EM instance.
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+ enum fc_class class,
+ u16 min_xid,
+ u16 max_xid);
+
+/*
+ * Free an exchange manager.
+ */
+void fc_exch_mgr_free(struct fc_exch_mgr *mp);
+
+/*
+ * Receive a frame on specified local port and exchange manager.
+ */
+void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
+ struct fc_frame *fp);
+
+/*
+ * This function is for exch_seq_send function pointer in
+ * struct libfc_function_template, see comment block on
+ * exch_seq_send for description of this function.
+ */
+struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *sp,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *sp,
+ void *arg),
+ void *arg, u32 timer_msec);
+
+/*
+ * send a frame using existing sequence and exchange.
+ */
+int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp);
+
+/*
+ * Send ELS response using mainly infomation
+ * in exchange and sequence in EM layer.
+ */
+void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data);
+
+/*
+ * This function is for seq_exch_abort function pointer in
+ * struct libfc_function_template, see comment block on
+ * seq_exch_abort for description of this function.
+ */
+int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec);
+
+/*
+ * Indicate that an exchange/sequence tuple is complete and the memory
+ * allocated for the related objects may be freed.
+ */
+void fc_exch_done(struct fc_seq *sp);
+
+/*
+ * Assigns a EM and XID for a frame and then allocates
+ * a new exchange and sequence pair.
+ * The fp can be used to determine free XID.
+ */
+struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp);
+
+/*
+ * Allocate a new exchange and sequence pair.
+ * if ex_id is zero then next free exchange id
+ * from specified exchange manger mp will be assigned.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
+ struct fc_frame *fp, u16 ex_id);
+/*
+ * Start a new sequence on the same exchange as the supplied sequence.
+ */
+struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
+
+/*
+ * Reset an exchange manager, completing all sequences and exchanges.
+ * If s_id is non-zero, reset only exchanges originating from that FID.
+ * If d_id is non-zero, reset only exchanges sending to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id);
+
+/*
+ * Functions for fc_functions_template
+ */
+void fc_get_host_speed(struct Scsi_Host *shost);
+void fc_get_host_port_type(struct Scsi_Host *shost);
+void fc_get_host_port_state(struct Scsi_Host *shost);
+void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout);
+struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
+
+/*
+ * module setup functions.
+ */
+int fc_setup_exch_mgr(void);
+void fc_destroy_exch_mgr(void);
+int fc_setup_rport(void);
+void fc_destroy_rport(void);
+
+#endif /* _LIBFC_H_ */
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
new file mode 100644
index 000000000000..89fdbb9a6a1b
--- /dev/null
+++ b/include/scsi/libfcoe.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _LIBFCOE_H
+#define _LIBFCOE_H
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <scsi/fc/fc_fcoe.h>
+#include <scsi/libfc.h>
+
+/*
+ * this percpu struct for fcoe
+ */
+struct fcoe_percpu_s {
+ int cpu;
+ struct task_struct *thread;
+ struct sk_buff_head fcoe_rx_list;
+ struct page *crc_eof_page;
+ int crc_eof_offset;
+};
+
+/*
+ * the fcoe sw transport private data
+ */
+struct fcoe_softc {
+ struct list_head list;
+ struct fc_lport *lp;
+ struct net_device *real_dev;
+ struct net_device *phys_dev; /* device with ethtool_ops */
+ struct packet_type fcoe_packet_type;
+ struct sk_buff_head fcoe_pending_queue;
+
+ u8 dest_addr[ETH_ALEN];
+ u8 ctl_src_addr[ETH_ALEN];
+ u8 data_src_addr[ETH_ALEN];
+ /*
+ * fcoe protocol address learning related stuff
+ */
+ u16 flogi_oxid;
+ u8 flogi_progress;
+ u8 address_mode;
+};
+
+static inline struct fcoe_softc *fcoe_softc(
+ const struct fc_lport *lp)
+{
+ return (struct fcoe_softc *)lport_priv(lp);
+}
+
+static inline struct net_device *fcoe_netdev(
+ const struct fc_lport *lp)
+{
+ return fcoe_softc(lp)->real_dev;
+}
+
+static inline struct fcoe_hdr *skb_fcoe_header(const struct sk_buff *skb)
+{
+ return (struct fcoe_hdr *)skb_network_header(skb);
+}
+
+static inline int skb_fcoe_offset(const struct sk_buff *skb)
+{
+ return skb_network_offset(skb);
+}
+
+static inline struct fc_frame_header *skb_fc_header(const struct sk_buff *skb)
+{
+ return (struct fc_frame_header *)skb_transport_header(skb);
+}
+
+static inline int skb_fc_offset(const struct sk_buff *skb)
+{
+ return skb_transport_offset(skb);
+}
+
+static inline void skb_reset_fc_header(struct sk_buff *skb)
+{
+ skb_reset_network_header(skb);
+ skb_set_transport_header(skb, skb_network_offset(skb) +
+ sizeof(struct fcoe_hdr));
+}
+
+static inline bool skb_fc_is_data(const struct sk_buff *skb)
+{
+ return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_SOL_DATA;
+}
+
+static inline bool skb_fc_is_cmd(const struct sk_buff *skb)
+{
+ return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD;
+}
+
+static inline bool skb_fc_has_exthdr(const struct sk_buff *skb)
+{
+ return (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_VFTH) ||
+ (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_IFRH) ||
+ (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_ENCH);
+}
+
+static inline bool skb_fc_is_roff(const struct sk_buff *skb)
+{
+ return skb_fc_header(skb)->fh_f_ctl[2] & FC_FC_REL_OFF;
+}
+
+static inline u16 skb_fc_oxid(const struct sk_buff *skb)
+{
+ return be16_to_cpu(skb_fc_header(skb)->fh_ox_id);
+}
+
+static inline u16 skb_fc_rxid(const struct sk_buff *skb)
+{
+ return be16_to_cpu(skb_fc_header(skb)->fh_rx_id);
+}
+
+/* FIXME - DMA_BIDIRECTIONAL ? */
+#define skb_cb(skb) ((struct fcoe_rcv_info *)&((skb)->cb[0]))
+#define skb_cmd(skb) (skb_cb(skb)->fr_cmd)
+#define skb_dir(skb) (skb_cmd(skb)->sc_data_direction)
+static inline bool skb_fc_is_read(const struct sk_buff *skb)
+{
+ if (skb_fc_is_cmd(skb) && skb_cmd(skb))
+ return skb_dir(skb) == DMA_FROM_DEVICE;
+ return false;
+}
+
+static inline bool skb_fc_is_write(const struct sk_buff *skb)
+{
+ if (skb_fc_is_cmd(skb) && skb_cmd(skb))
+ return skb_dir(skb) == DMA_TO_DEVICE;
+ return false;
+}
+
+/* libfcoe funcs */
+int fcoe_reset(struct Scsi_Host *shost);
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+ unsigned int scheme, unsigned int port);
+
+u32 fcoe_fc_crc(struct fc_frame *fp);
+int fcoe_xmit(struct fc_lport *, struct fc_frame *);
+int fcoe_rcv(struct sk_buff *, struct net_device *,
+ struct packet_type *, struct net_device *);
+
+int fcoe_percpu_receive_thread(void *arg);
+void fcoe_clean_pending_queue(struct fc_lport *lp);
+void fcoe_percpu_clean(struct fc_lport *lp);
+void fcoe_watchdog(ulong vp);
+int fcoe_link_ok(struct fc_lport *lp);
+
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
+int fcoe_hostlist_add(const struct fc_lport *);
+int fcoe_hostlist_remove(const struct fc_lport *);
+
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
+int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
+
+/* fcoe sw hba */
+int __init fcoe_sw_init(void);
+int __exit fcoe_sw_exit(void);
+#endif /* _LIBFCOE_H */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 61e53f14f7e1..7360e1916e75 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -30,6 +30,7 @@
#include <linux/workqueue.h>
#include <scsi/iscsi_proto.h>
#include <scsi/iscsi_if.h>
+#include <scsi/scsi_transport_iscsi.h>
struct scsi_transport_template;
struct scsi_host_template;
@@ -70,12 +71,12 @@ enum {
/* Connection suspend "bit" */
#define ISCSI_SUSPEND_BIT 1
-#define ISCSI_ITT_MASK (0x1fff)
+#define ISCSI_ITT_MASK 0x1fff
#define ISCSI_TOTAL_CMDS_MAX 4096
/* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */
#define ISCSI_TOTAL_CMDS_MIN 16
#define ISCSI_AGE_SHIFT 28
-#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT)
+#define ISCSI_AGE_MASK 0xf
#define ISCSI_ADDRESS_BUF_LEN 64
@@ -93,24 +94,38 @@ enum {
ISCSI_TASK_RUNNING,
};
+struct iscsi_r2t_info {
+ __be32 ttt; /* copied from R2T */
+ __be32 exp_statsn; /* copied from R2T */
+ uint32_t data_length; /* copied from R2T */
+ uint32_t data_offset; /* copied from R2T */
+ int data_count; /* DATA-Out payload progress */
+ int datasn;
+ /* LLDs should set/update these values */
+ int sent; /* R2T sequence progress */
+};
+
struct iscsi_task {
/*
* Because LLDs allocate their hdr differently, this is a pointer
* and length to that storage. It must be setup at session
* creation time.
*/
- struct iscsi_cmd *hdr;
+ struct iscsi_hdr *hdr;
unsigned short hdr_max;
unsigned short hdr_len; /* accumulated size of hdr used */
+ /* copied values in case we need to send tmfs */
+ itt_t hdr_itt;
+ __be32 cmdsn;
+ uint8_t lun[8];
+
int itt; /* this ITT */
- uint32_t unsol_datasn;
unsigned imm_count; /* imm-data (bytes) */
- unsigned unsol_count; /* unsolicited (bytes)*/
/* offset in unsolicited stream (bytes); */
- unsigned unsol_offset;
- unsigned data_count; /* remaining Data-Out */
+ struct iscsi_r2t_info unsol_r2t;
char *data; /* mgmt payload */
+ unsigned data_count;
struct scsi_cmnd *sc; /* associated SCSI cmd*/
struct iscsi_conn *conn; /* used connection */
@@ -121,6 +136,11 @@ struct iscsi_task {
void *dd_data; /* driver/transport data */
};
+static inline int iscsi_task_has_unsol_data(struct iscsi_task *task)
+{
+ return task->unsol_r2t.data_length > task->unsol_r2t.sent;
+}
+
static inline void* iscsi_next_hdr(struct iscsi_task *task)
{
return (void*)task->hdr + task->hdr_len;
@@ -376,8 +396,9 @@ extern void iscsi_suspend_tx(struct iscsi_conn *conn);
* pdu and task processing
*/
extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
-extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *,
- struct iscsi_data *hdr);
+extern void iscsi_prep_data_out_pdu(struct iscsi_task *task,
+ struct iscsi_r2t_info *r2t,
+ struct iscsi_data *hdr);
extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
char *, uint32_t);
extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h
new file mode 100644
index 000000000000..83e32f6d7859
--- /dev/null
+++ b/include/scsi/libiscsi_tcp.h
@@ -0,0 +1,132 @@
+/*
+ * iSCSI over TCP/IP Data-Path lib
+ *
+ * Copyright (C) 2008 Mike Christie
+ * Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+ * maintained by open-iscsi@googlegroups.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.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#ifndef LIBISCSI_TCP_H
+#define LIBISCSI_TCP_H
+
+#include <scsi/libiscsi.h>
+
+struct iscsi_tcp_conn;
+struct iscsi_segment;
+struct sk_buff;
+struct hash_desc;
+
+typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
+ struct iscsi_segment *);
+
+struct iscsi_segment {
+ unsigned char *data;
+ unsigned int size;
+ unsigned int copied;
+ unsigned int total_size;
+ unsigned int total_copied;
+
+ struct hash_desc *hash;
+ unsigned char recv_digest[ISCSI_DIGEST_SIZE];
+ unsigned char digest[ISCSI_DIGEST_SIZE];
+ unsigned int digest_len;
+
+ struct scatterlist *sg;
+ void *sg_mapped;
+ unsigned int sg_offset;
+
+ iscsi_segment_done_fn_t *done;
+};
+
+/* Socket connection recieve helper */
+struct iscsi_tcp_recv {
+ struct iscsi_hdr *hdr;
+ struct iscsi_segment segment;
+
+ /* Allocate buffer for BHS + AHS */
+ uint32_t hdr_buf[64];
+
+ /* copied and flipped values */
+ int datalen;
+};
+
+struct iscsi_tcp_conn {
+ struct iscsi_conn *iscsi_conn;
+ void *dd_data;
+ int stop_stage; /* conn_stop() flag: *
+ * stop to recover, *
+ * stop to terminate */
+ /* control data */
+ struct iscsi_tcp_recv in; /* TCP receive context */
+ /* CRC32C (Rx) LLD should set this is they do not offload */
+ struct hash_desc *rx_hash;
+};
+
+struct iscsi_tcp_task {
+ uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
+ int data_offset;
+ struct iscsi_r2t_info *r2t; /* in progress solict R2T */
+ struct iscsi_pool r2tpool;
+ struct kfifo *r2tqueue;
+ void *dd_data;
+};
+
+enum {
+ ISCSI_TCP_SEGMENT_DONE, /* curr seg has been processed */
+ ISCSI_TCP_SKB_DONE, /* skb is out of data */
+ ISCSI_TCP_CONN_ERR, /* iscsi layer has fired a conn err */
+ ISCSI_TCP_SUSPENDED, /* conn is suspended */
+};
+
+extern void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn);
+extern int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+ unsigned int offset, bool offloaded, int *status);
+extern void iscsi_tcp_cleanup_task(struct iscsi_task *task);
+extern int iscsi_tcp_task_init(struct iscsi_task *task);
+extern int iscsi_tcp_task_xmit(struct iscsi_task *task);
+
+/* segment helpers */
+extern int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn);
+extern int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
+ struct iscsi_segment *segment, int recv,
+ unsigned copied);
+extern void iscsi_tcp_segment_unmap(struct iscsi_segment *segment);
+
+extern void iscsi_segment_init_linear(struct iscsi_segment *segment,
+ void *data, size_t size,
+ iscsi_segment_done_fn_t *done,
+ struct hash_desc *hash);
+extern int
+iscsi_segment_seek_sg(struct iscsi_segment *segment,
+ struct scatterlist *sg_list, unsigned int sg_count,
+ unsigned int offset, size_t size,
+ iscsi_segment_done_fn_t *done, struct hash_desc *hash);
+
+/* digest helpers */
+extern void iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr,
+ size_t hdrlen,
+ unsigned char digest[ISCSI_DIGEST_SIZE]);
+extern struct iscsi_cls_conn *
+iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size,
+ uint32_t conn_idx);
+extern void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn);
+
+/* misc helpers */
+extern int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session);
+extern void iscsi_tcp_r2tpool_free(struct iscsi_session *session);
+
+extern void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats);
+#endif /* LIBISCSI_TCP_H */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index a37a8148a310..01a4c58f8bad 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -159,8 +159,6 @@ struct scsi_device {
atomic_t iodone_cnt;
atomic_t ioerr_cnt;
- int timeout;
-
struct device sdev_gendev,
sdev_dev;
@@ -367,10 +365,11 @@ extern int scsi_is_target_device(const struct device *);
extern int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
unsigned char *sense, int timeout, int retries,
- int flag);
+ int flag, int *resid);
extern int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd,
int data_direction, void *buffer, unsigned bufflen,
- struct scsi_sense_hdr *, int timeout, int retries);
+ struct scsi_sense_hdr *, int timeout, int retries,
+ int *resid);
extern int scsi_execute_async(struct scsi_device *sdev,
const unsigned char *cmd, int cmd_len, int data_direction,
void *buffer, unsigned bufflen, int use_sg,
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index c667cc396545..b50aabe2861e 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -113,10 +113,18 @@ struct iscsi_transport {
char *data, uint32_t data_size);
void (*get_stats) (struct iscsi_cls_conn *conn,
struct iscsi_stats *stats);
+
int (*init_task) (struct iscsi_task *task);
int (*xmit_task) (struct iscsi_task *task);
- void (*cleanup_task) (struct iscsi_conn *conn,
- struct iscsi_task *task);
+ void (*cleanup_task) (struct iscsi_task *task);
+
+ int (*alloc_pdu) (struct iscsi_task *task, uint8_t opcode);
+ int (*xmit_pdu) (struct iscsi_task *task);
+ int (*init_pdu) (struct iscsi_task *task, unsigned int offset,
+ unsigned int count);
+ void (*parse_pdu_itt) (struct iscsi_conn *conn, itt_t itt,
+ int *index, int *age);
+
void (*session_recovery_timedout) (struct iscsi_cls_session *session);
struct iscsi_endpoint *(*ep_connect) (struct sockaddr *dst_addr,
int non_blocking);
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 9c309daf492b..251fc1cd5002 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -281,10 +281,12 @@
/* specific - Analog Devices */
#define AC97_AD_TEST 0x5a /* test register */
#define AC97_AD_TEST2 0x5c /* undocumented test register 2 */
+#define AC97_AD_HPFD_SHIFT 12 /* High Pass Filter Disable */
#define AC97_AD_CODEC_CFG 0x70 /* codec configuration */
#define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */
#define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */
#define AC97_AD_MISC 0x76 /* Misc Control Bits */
+#define AC97_AD_VREFD_SHIFT 2 /* V_REFOUT Disable (AD1888) */
/* specific - Cirrus Logic */
#define AC97_CSR_ACMODE 0x5e /* AC Mode Register */
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 2c4dc908a54a..1c02ed1d7c4a 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -575,6 +575,7 @@ enum {
#define SNDRV_TIMER_GLOBAL_SYSTEM 0
#define SNDRV_TIMER_GLOBAL_RTC 1
#define SNDRV_TIMER_GLOBAL_HPET 2
+#define SNDRV_TIMER_GLOBAL_HRTIMER 3
/* info flags */
#define SNDRV_TIMER_FLG_SLAVE (1<<0) /* cannot be controlled */
diff --git a/include/sound/core.h b/include/sound/core.h
index 1508c4ec1ba9..f632484bc743 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -353,7 +353,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
* snd_printk - printk wrapper
* @fmt: format string
*
- * Works like print() but prints the file and the line of the caller
+ * Works like printk() but prints the file and the line of the caller
* when configured with CONFIG_SND_VERBOSE_PRINTK.
*/
#define snd_printk(fmt, args...) \
@@ -380,18 +380,40 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
printk(fmt ,##args)
#endif
+/**
+ * snd_BUG - give a BUG warning message and stack trace
+ *
+ * Calls WARN() if CONFIG_SND_DEBUG is set.
+ * Ignored when CONFIG_SND_DEBUG is not set.
+ */
#define snd_BUG() WARN(1, "BUG?\n")
+
+/**
+ * snd_BUG_ON - debugging check macro
+ * @cond: condition to evaluate
+ *
+ * When CONFIG_SND_DEBUG is set, this macro evaluates the given condition,
+ * and call WARN() and returns the value if it's non-zero.
+ *
+ * When CONFIG_SND_DEBUG is not set, this just returns zero, and the given
+ * condition is ignored.
+ *
+ * NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n.
+ * Thus, don't put any statement that influences on the code behavior,
+ * such as pre/post increment, to the argument of this macro.
+ * If you want to evaluate and give a warning, use standard WARN_ON().
+ */
#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond))
#else /* !CONFIG_SND_DEBUG */
#define snd_printd(fmt, args...) do { } while (0)
#define snd_BUG() do { } while (0)
-static inline int __snd_bug_on(void)
+static inline int __snd_bug_on(int cond)
{
return 0;
}
-#define snd_BUG_ON(cond) __snd_bug_on() /* always false */
+#define snd_BUG_ON(cond) __snd_bug_on(0 && (cond)) /* always false */
#endif /* CONFIG_SND_DEBUG */
diff --git a/include/sound/info.h b/include/sound/info.h
index 8ae72e74f898..7c2ee1a21b00 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -40,30 +40,34 @@ struct snd_info_buffer {
struct snd_info_entry;
struct snd_info_entry_text {
- void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
- void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer);
+ void (*read)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer);
+ void (*write)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer);
};
struct snd_info_entry_ops {
- int (*open) (struct snd_info_entry *entry,
- unsigned short mode, void **file_private_data);
- int (*release) (struct snd_info_entry * entry,
- unsigned short mode, void *file_private_data);
- long (*read) (struct snd_info_entry *entry, void *file_private_data,
- struct file * file, char __user *buf,
+ int (*open)(struct snd_info_entry *entry,
+ unsigned short mode, void **file_private_data);
+ int (*release)(struct snd_info_entry *entry,
+ unsigned short mode, void *file_private_data);
+ long (*read)(struct snd_info_entry *entry, void *file_private_data,
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos);
+ long (*write)(struct snd_info_entry *entry, void *file_private_data,
+ struct file *file, const char __user *buf,
unsigned long count, unsigned long pos);
- long (*write) (struct snd_info_entry *entry, void *file_private_data,
- struct file * file, const char __user *buf,
- unsigned long count, unsigned long pos);
- long long (*llseek) (struct snd_info_entry *entry, void *file_private_data,
- struct file * file, long long offset, int orig);
- unsigned int (*poll) (struct snd_info_entry *entry, void *file_private_data,
- struct file * file, poll_table * wait);
- int (*ioctl) (struct snd_info_entry *entry, void *file_private_data,
- struct file * file, unsigned int cmd, unsigned long arg);
- int (*mmap) (struct snd_info_entry *entry, void *file_private_data,
- struct inode * inode, struct file * file,
- struct vm_area_struct * vma);
+ long long (*llseek)(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ long long offset, int orig);
+ unsigned int(*poll)(struct snd_info_entry *entry,
+ void *file_private_data, struct file *file,
+ poll_table *wait);
+ int (*ioctl)(struct snd_info_entry *entry, void *file_private_data,
+ struct file *file, unsigned int cmd, unsigned long arg);
+ int (*mmap)(struct snd_info_entry *entry, void *file_private_data,
+ struct inode *inode, struct file *file,
+ struct vm_area_struct *vma);
};
struct snd_info_entry {
@@ -106,34 +110,37 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
#endif
-int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) __attribute__ ((format (printf, 2, 3)));
+int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
+ __attribute__ ((format (printf, 2, 3)));
int snd_info_init(void);
int snd_info_done(void);
-int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len);
+int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
char *snd_info_get_str(char *dest, char *src, int len);
-struct snd_info_entry *snd_info_create_module_entry(struct module * module,
+struct snd_info_entry *snd_info_create_module_entry(struct module *module,
const char *name,
- struct snd_info_entry * parent);
-struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card,
+ struct snd_info_entry *parent);
+struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
- struct snd_info_entry * parent);
-void snd_info_free_entry(struct snd_info_entry * entry);
-int snd_info_store_text(struct snd_info_entry * entry);
-int snd_info_restore_text(struct snd_info_entry * entry);
-
-int snd_info_card_create(struct snd_card * card);
-int snd_info_card_register(struct snd_card * card);
-int snd_info_card_free(struct snd_card * card);
-void snd_info_card_disconnect(struct snd_card * card);
-int snd_info_register(struct snd_info_entry * entry);
+ struct snd_info_entry *parent);
+void snd_info_free_entry(struct snd_info_entry *entry);
+int snd_info_store_text(struct snd_info_entry *entry);
+int snd_info_restore_text(struct snd_info_entry *entry);
+
+int snd_info_card_create(struct snd_card *card);
+int snd_info_card_register(struct snd_card *card);
+int snd_info_card_free(struct snd_card *card);
+void snd_info_card_disconnect(struct snd_card *card);
+void snd_info_card_id_change(struct snd_card *card);
+int snd_info_register(struct snd_info_entry *entry);
/* for card drivers */
-int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
+int snd_card_proc_new(struct snd_card *card, const char *name,
+ struct snd_info_entry **entryp);
static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
- void *private_data,
- void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
+ void *private_data,
+ void (*read)(struct snd_info_entry *, struct snd_info_buffer *))
{
entry->private_data = private_data;
entry->c.text.read = read;
@@ -146,21 +153,22 @@ int snd_info_check_reserved_words(const char *str);
#define snd_seq_root NULL
#define snd_oss_root NULL
-static inline int snd_iprintf(struct snd_info_buffer * buffer, char *fmt,...) { return 0; }
+static inline int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) { return 0; }
static inline int snd_info_init(void) { return 0; }
static inline int snd_info_done(void) { return 0; }
-static inline int snd_info_get_line(struct snd_info_buffer * buffer, char *line, int len) { return 0; }
+static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { return 0; }
static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
-static inline struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, struct snd_info_entry * parent) { return NULL; }
-static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card * card, const char *name, struct snd_info_entry * parent) { return NULL; }
-static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
-
-static inline int snd_info_card_create(struct snd_card * card) { return 0; }
-static inline int snd_info_card_register(struct snd_card * card) { return 0; }
-static inline int snd_info_card_free(struct snd_card * card) { return 0; }
-static inline void snd_info_card_disconnect(struct snd_card * card) { }
-static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
+static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; }
+static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; }
+static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; }
+
+static inline int snd_info_card_create(struct snd_card *card) { return 0; }
+static inline int snd_info_card_register(struct snd_card *card) { return 0; }
+static inline int snd_info_card_free(struct snd_card *card) { return 0; }
+static inline void snd_info_card_disconnect(struct snd_card *card) { }
+static inline void snd_info_card_id_change(struct snd_card *card) { }
+static inline int snd_info_register(struct snd_info_entry *entry) { return 0; }
static inline int snd_card_proc_new(struct snd_card *card, const char *name,
struct snd_info_entry **entryp) { return -EINVAL; }
diff --git a/include/sound/jack.h b/include/sound/jack.h
index b1b2b8b59adb..2e0315cdd0d6 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -35,6 +35,8 @@ enum snd_jack_types {
SND_JACK_HEADPHONE = 0x0001,
SND_JACK_MICROPHONE = 0x0002,
SND_JACK_HEADSET = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
+ SND_JACK_LINEOUT = 0x0004,
+ SND_JACK_MECHANICAL = 0x0008, /* If detected separately */
};
struct snd_jack {
diff --git a/include/sound/l3.h b/include/sound/l3.h
new file mode 100644
index 000000000000..423a08f0f1b0
--- /dev/null
+++ b/include/sound/l3.h
@@ -0,0 +1,18 @@
+#ifndef _L3_H_
+#define _L3_H_ 1
+
+struct l3_pins {
+ void (*setdat)(int);
+ void (*setclk)(int);
+ void (*setmode)(int);
+ int data_hold;
+ int data_setup;
+ int clock_high;
+ int mode_hold;
+ int mode;
+ int mode_setup;
+};
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
+
+#endif
diff --git a/include/sound/s3c24xx_uda134x.h b/include/sound/s3c24xx_uda134x.h
new file mode 100644
index 000000000000..33df4cb909d3
--- /dev/null
+++ b/include/sound/s3c24xx_uda134x.h
@@ -0,0 +1,14 @@
+#ifndef _S3C24XX_UDA134X_H_
+#define _S3C24XX_UDA134X_H_ 1
+
+#include <sound/uda134x.h>
+
+struct s3c24xx_uda134x_platform_data {
+ int l3_clk;
+ int l3_mode;
+ int l3_data;
+ void (*power) (int);
+ int model;
+};
+
+#endif
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
new file mode 100644
index 000000000000..24247f763608
--- /dev/null
+++ b/include/sound/soc-dai.h
@@ -0,0 +1,231 @@
+/*
+ * linux/sound/soc-dai.h -- ALSA SoC Layer
+ *
+ * Copyright: 2005-2008 Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Digital Audio Interface (DAI) API.
+ */
+
+#ifndef __LINUX_SND_SOC_DAI_H
+#define __LINUX_SND_SOC_DAI_H
+
+
+#include <linux/list.h>
+
+struct snd_pcm_substream;
+
+/*
+ * DAI hardware audio formats.
+ *
+ * Describes the physical PCM data formating and clocking. Add new formats
+ * to the end.
+ */
+#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
+#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right Justified mode */
+#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
+#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
+
+/* left and right justified also known as MSB and LSB respectively */
+#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
+#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
+
+/*
+ * DAI Clock gating.
+ *
+ * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * sending or receiving PCM data in a frame. This can be used to save power.
+ */
+#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
+#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated */
+
+/*
+ * DAI Left/Right Clocks.
+ *
+ * Specifies whether the DAI can support different samples for similtanious
+ * playback and capture. This usually requires a seperate physical frame
+ * clock for playback and capture.
+ */
+#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
+#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
+
+/*
+ * TDM
+ *
+ * Time Division Multiplexing. Allows PCM data to be multplexed with other
+ * data on the DAI.
+ */
+#define SND_SOC_DAIFMT_TDM (1 << 6)
+
+/*
+ * DAI hardware signal inversions.
+ *
+ * Specifies whether the DAI can also support inverted clocks for the specified
+ * format.
+ */
+#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
+#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
+#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
+
+/*
+ * DAI hardware clock masters.
+ *
+ * This is wrt the codec, the inverse is true for the interface
+ * i.e. if the codec is clk and frm master then the interface is
+ * clk and frame slave.
+ */
+#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
+#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
+#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
+
+#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
+#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
+#define SND_SOC_DAIFMT_INV_MASK 0x0f00
+#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
+
+/*
+ * Master Clock Directions
+ */
+#define SND_SOC_CLOCK_IN 0
+#define SND_SOC_CLOCK_OUT 1
+
+struct snd_soc_dai_ops;
+struct snd_soc_dai;
+struct snd_ac97_bus_ops;
+
+/* Digital Audio Interface registration */
+int snd_soc_register_dai(struct snd_soc_dai *dai);
+void snd_soc_unregister_dai(struct snd_soc_dai *dai);
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count);
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count);
+
+/* Digital Audio Interface clocking API.*/
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir);
+
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+ int div_id, int div);
+
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+/* Digital Audio interface formatting */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
+
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int mask, int slots);
+
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
+
+/* Digital Audio Interface mute */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+
+/*
+ * Digital Audio Interface.
+ *
+ * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
+ * operations an capabilities. Codec and platfom drivers will register a this
+ * structure for every DAI they have.
+ *
+ * This structure covers the clocking, formating and ALSA operations for each
+ * interface a
+ */
+struct snd_soc_dai_ops {
+ /*
+ * DAI clocking configuration, all optional.
+ * Called by soc_card drivers, normally in their hw_params.
+ */
+ int (*set_sysclk)(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir);
+ int (*set_pll)(struct snd_soc_dai *dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out);
+ int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
+
+ /*
+ * DAI format configuration
+ * Called by soc_card drivers, normally in their hw_params.
+ */
+ int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
+ int (*set_tdm_slot)(struct snd_soc_dai *dai,
+ unsigned int mask, int slots);
+ int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
+
+ /*
+ * DAI digital mute - optional.
+ * Called by soc-core to minimise any pops.
+ */
+ int (*digital_mute)(struct snd_soc_dai *dai, int mute);
+
+ /*
+ * ALSA PCM audio operations - all optional.
+ * Called by soc-core during audio PCM operations.
+ */
+ int (*startup)(struct snd_pcm_substream *,
+ struct snd_soc_dai *);
+ void (*shutdown)(struct snd_pcm_substream *,
+ struct snd_soc_dai *);
+ int (*hw_params)(struct snd_pcm_substream *,
+ struct snd_pcm_hw_params *, struct snd_soc_dai *);
+ int (*hw_free)(struct snd_pcm_substream *,
+ struct snd_soc_dai *);
+ int (*prepare)(struct snd_pcm_substream *,
+ struct snd_soc_dai *);
+ int (*trigger)(struct snd_pcm_substream *, int,
+ struct snd_soc_dai *);
+};
+
+/*
+ * Digital Audio Interface runtime data.
+ *
+ * Holds runtime data for a DAI.
+ */
+struct snd_soc_dai {
+ /* DAI description */
+ char *name;
+ unsigned int id;
+ int ac97_control;
+
+ struct device *dev;
+
+ /* DAI callbacks */
+ int (*probe)(struct platform_device *pdev,
+ struct snd_soc_dai *dai);
+ void (*remove)(struct platform_device *pdev,
+ struct snd_soc_dai *dai);
+ int (*suspend)(struct snd_soc_dai *dai);
+ int (*resume)(struct snd_soc_dai *dai);
+
+ /* ops */
+ struct snd_soc_dai_ops ops;
+
+ /* DAI capabilities */
+ struct snd_soc_pcm_stream capture;
+ struct snd_soc_pcm_stream playback;
+
+ /* DAI runtime info */
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_codec *codec;
+ unsigned int active;
+ unsigned char pop_wait:1;
+ void *dma_data;
+
+ /* DAI private data */
+ void *private_data;
+
+ /* parent codec/platform */
+ union {
+ struct snd_soc_codec *codec;
+ struct snd_soc_platform *platform;
+ };
+
+ struct list_head list;
+};
+
+#endif
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index ca699a3017f3..7ee2f70ca42e 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
int num);
/* dapm path setup */
-int __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
- const char *sink_name, const char *control_name, const char *src_name);
int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
void snd_soc_dapm_free(struct snd_soc_device *socdev);
int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 5e0189876afd..f86e455d3828 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -21,8 +21,6 @@
#include <sound/control.h>
#include <sound/ac97_codec.h>
-#define SND_SOC_VERSION "0.13.2"
-
/*
* Convenience kcontrol builders
*/
@@ -145,105 +143,31 @@ enum snd_soc_bias_level {
SND_SOC_BIAS_OFF,
};
-/*
- * Digital Audio Interface (DAI) types
- */
-#define SND_SOC_DAI_AC97 0x1
-#define SND_SOC_DAI_I2S 0x2
-#define SND_SOC_DAI_PCM 0x4
-#define SND_SOC_DAI_AC97_BUS 0x8 /* for custom i.e. non ac97_codec.c */
-
-/*
- * DAI hardware audio formats
- */
-#define SND_SOC_DAIFMT_I2S 0 /* I2S mode */
-#define SND_SOC_DAIFMT_RIGHT_J 1 /* Right justified mode */
-#define SND_SOC_DAIFMT_LEFT_J 2 /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A 3 /* L data msb after FRM or LRC */
-#define SND_SOC_DAIFMT_DSP_B 4 /* L data msb during FRM or LRC */
-#define SND_SOC_DAIFMT_AC97 5 /* AC97 */
-
-#define SND_SOC_DAIFMT_MSB SND_SOC_DAIFMT_LEFT_J
-#define SND_SOC_DAIFMT_LSB SND_SOC_DAIFMT_RIGHT_J
-
-/*
- * DAI Gating
- */
-#define SND_SOC_DAIFMT_CONT (0 << 4) /* continuous clock */
-#define SND_SOC_DAIFMT_GATED (1 << 4) /* clock is gated when not Tx/Rx */
-
-/*
- * DAI Sync
- * Synchronous LR (Left Right) clocks and Frame signals.
- */
-#define SND_SOC_DAIFMT_SYNC (0 << 5) /* Tx FRM = Rx FRM */
-#define SND_SOC_DAIFMT_ASYNC (1 << 5) /* Tx FRM ~ Rx FRM */
-
-/*
- * TDM
- */
-#define SND_SOC_DAIFMT_TDM (1 << 6)
-
-/*
- * DAI hardware signal inversions
- */
-#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bclk + frm */
-#define SND_SOC_DAIFMT_NB_IF (1 << 8) /* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF (2 << 8) /* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF (3 << 8) /* invert bclk + frm */
-
-/*
- * DAI hardware clock masters
- * This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
- * clk and frame slave.
- */
-#define SND_SOC_DAIFMT_CBM_CFM (0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM (1 << 12) /* codec clk slave & frm master */
-#define SND_SOC_DAIFMT_CBM_CFS (2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS (3 << 12) /* codec clk & frm slave */
-
-#define SND_SOC_DAIFMT_FORMAT_MASK 0x000f
-#define SND_SOC_DAIFMT_CLOCK_MASK 0x00f0
-#define SND_SOC_DAIFMT_INV_MASK 0x0f00
-#define SND_SOC_DAIFMT_MASTER_MASK 0xf000
-
-
-/*
- * Master Clock Directions
- */
-#define SND_SOC_CLOCK_IN 0
-#define SND_SOC_CLOCK_OUT 1
-
-/*
- * AC97 codec ID's bitmask
- */
-#define SND_SOC_DAI_AC97_ID0 (1 << 0)
-#define SND_SOC_DAI_AC97_ID1 (1 << 1)
-#define SND_SOC_DAI_AC97_ID2 (1 << 2)
-#define SND_SOC_DAI_AC97_ID3 (1 << 3)
-
struct snd_soc_device;
struct snd_soc_pcm_stream;
struct snd_soc_ops;
struct snd_soc_dai_mode;
struct snd_soc_pcm_runtime;
struct snd_soc_dai;
+struct snd_soc_platform;
struct snd_soc_codec;
-struct snd_soc_machine_config;
struct soc_enum;
struct snd_soc_ac97_ops;
-struct snd_soc_clock_info;
typedef int (*hw_write_t)(void *,const char* ,int);
typedef int (*hw_read_t)(void *,char* ,int);
extern struct snd_ac97_bus_ops soc_ac97_ops;
+int snd_soc_register_platform(struct snd_soc_platform *platform);
+void snd_soc_unregister_platform(struct snd_soc_platform *platform);
+int snd_soc_register_codec(struct snd_soc_codec *codec);
+void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+
/* pcm <-> DAI connect */
void snd_soc_free_pcms(struct snd_soc_device *socdev);
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_register_card(struct snd_soc_device *socdev);
+int snd_soc_init_card(struct snd_soc_device *socdev);
/* set runtime hw params */
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -263,27 +187,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
struct snd_ac97_bus_ops *ops, int num);
void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
-/* Digital Audio Interface clocking API.*/
-int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir);
-
-int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
- int div_id, int div);
-
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
-
-/* Digital Audio interface formatting */
-int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
-
-int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int mask, int slots);
-
-int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
-
-/* Digital Audio Interface mute */
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
-
/*
*Controls
*/
@@ -341,66 +244,14 @@ struct snd_soc_ops {
int (*trigger)(struct snd_pcm_substream *, int);
};
-/* ASoC DAI ops */
-struct snd_soc_dai_ops {
- /* DAI clocking configuration */
- int (*set_sysclk)(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir);
- int (*set_pll)(struct snd_soc_dai *dai,
- int pll_id, unsigned int freq_in, unsigned int freq_out);
- int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
-
- /* DAI format configuration */
- int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
- int (*set_tdm_slot)(struct snd_soc_dai *dai,
- unsigned int mask, int slots);
- int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
-
- /* digital mute */
- int (*digital_mute)(struct snd_soc_dai *dai, int mute);
-};
-
-/* SoC DAI (Digital Audio Interface) */
-struct snd_soc_dai {
- /* DAI description */
- char *name;
- unsigned int id;
- unsigned char type;
-
- /* DAI callbacks */
- int (*probe)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
- void (*remove)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
- int (*suspend)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
- int (*resume)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
-
- /* ops */
- struct snd_soc_ops ops;
- struct snd_soc_dai_ops dai_ops;
-
- /* DAI capabilities */
- struct snd_soc_pcm_stream capture;
- struct snd_soc_pcm_stream playback;
-
- /* DAI runtime info */
- struct snd_pcm_runtime *runtime;
- struct snd_soc_codec *codec;
- unsigned int active;
- unsigned char pop_wait:1;
- void *dma_data;
-
- /* DAI private data */
- void *private_data;
-};
-
/* SoC Audio Codec */
struct snd_soc_codec {
char *name;
struct module *owner;
struct mutex mutex;
+ struct device *dev;
+
+ struct list_head list;
/* callbacks */
int (*set_bias_level)(struct snd_soc_codec *,
@@ -426,6 +277,7 @@ struct snd_soc_codec {
short reg_cache_step;
/* dapm */
+ u32 pop_time;
struct list_head dapm_widgets;
struct list_head dapm_paths;
enum snd_soc_bias_level bias_level;
@@ -435,6 +287,11 @@ struct snd_soc_codec {
/* codec DAI's */
struct snd_soc_dai *dai;
unsigned int num_dai;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_reg;
+ struct dentry *debugfs_pop_time;
+#endif
};
/* codec device */
@@ -448,13 +305,12 @@ struct snd_soc_codec_device {
/* SoC platform interface */
struct snd_soc_platform {
char *name;
+ struct list_head list;
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
- int (*suspend)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
- int (*resume)(struct platform_device *pdev,
- struct snd_soc_dai *dai);
+ int (*suspend)(struct snd_soc_dai *dai);
+ int (*resume)(struct snd_soc_dai *dai);
/* pcm creation and destruction */
int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
@@ -484,9 +340,14 @@ struct snd_soc_dai_link {
struct snd_pcm *pcm;
};
-/* SoC machine */
-struct snd_soc_machine {
+/* SoC card */
+struct snd_soc_card {
char *name;
+ struct device *dev;
+
+ struct list_head list;
+
+ int instantiated;
int (*probe)(struct platform_device *pdev);
int (*remove)(struct platform_device *pdev);
@@ -499,23 +360,26 @@ struct snd_soc_machine {
int (*resume_post)(struct platform_device *pdev);
/* callbacks */
- int (*set_bias_level)(struct snd_soc_machine *,
+ int (*set_bias_level)(struct snd_soc_card *,
enum snd_soc_bias_level level);
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
+
+ struct snd_soc_device *socdev;
+
+ struct snd_soc_platform *platform;
+ struct delayed_work delayed_work;
+ struct work_struct deferred_resume_work;
};
/* SoC Device - the audio subsystem */
struct snd_soc_device {
struct device *dev;
- struct snd_soc_machine *machine;
- struct snd_soc_platform *platform;
+ struct snd_soc_card *card;
struct snd_soc_codec *codec;
struct snd_soc_codec_device *codec_dev;
- struct delayed_work delayed_work;
- struct work_struct deferred_resume_work;
void *codec_data;
};
@@ -542,4 +406,6 @@ struct soc_enum {
void *dapm;
};
+#include <sound/soc-dai.h>
+
#endif
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
new file mode 100644
index 000000000000..475ef8bb7dcd
--- /dev/null
+++ b/include/sound/uda134x.h
@@ -0,0 +1,26 @@
+/*
+ * uda134x.h -- UDA134x ALSA SoC Codec driver
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * This program is free software; you can 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 _UDA134X_H
+#define _UDA134X_H
+
+#include <sound/l3.h>
+
+struct uda134x_platform_data {
+ struct l3_pins l3;
+ void (*power) (int);
+ int model;
+#define UDA134X_UDA1340 1
+#define UDA134X_UDA1341 2
+#define UDA134X_UDA1344 3
+};
+
+#endif /* _UDA134X_H */
diff --git a/include/sound/version.h b/include/sound/version.h
index 4aafeda88634..2b48237e23bf 100644
--- a/include/sound/version.h
+++ b/include/sound/version.h
@@ -1,3 +1,3 @@
/* include/version.h */
-#define CONFIG_SND_VERSION "1.0.18rc3"
+#define CONFIG_SND_VERSION "1.0.18a"
#define CONFIG_SND_DATE ""
diff --git a/include/trace/sched.h b/include/trace/sched.h
index 9b2854abf7e2..0d81098ee9fc 100644
--- a/include/trace/sched.h
+++ b/include/trace/sched.h
@@ -17,12 +17,12 @@ DECLARE_TRACE(sched_wait_task,
TPARGS(rq, p));
DECLARE_TRACE(sched_wakeup,
- TPPROTO(struct rq *rq, struct task_struct *p),
- TPARGS(rq, p));
+ TPPROTO(struct rq *rq, struct task_struct *p, int success),
+ TPARGS(rq, p, success));
DECLARE_TRACE(sched_wakeup_new,
- TPPROTO(struct rq *rq, struct task_struct *p),
- TPARGS(rq, p));
+ TPPROTO(struct rq *rq, struct task_struct *p, int success),
+ TPARGS(rq, p, success));
DECLARE_TRACE(sched_switch,
TPPROTO(struct rq *rq, struct task_struct *prev,
@@ -30,8 +30,8 @@ DECLARE_TRACE(sched_switch,
TPARGS(rq, prev, next));
DECLARE_TRACE(sched_migrate_task,
- TPPROTO(struct rq *rq, struct task_struct *p, int dest_cpu),
- TPARGS(rq, p, dest_cpu));
+ TPPROTO(struct task_struct *p, int orig_cpu, int dest_cpu),
+ TPARGS(p, orig_cpu, dest_cpu));
DECLARE_TRACE(sched_process_free,
TPPROTO(struct task_struct *p),
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 1a4bc6ada606..25144ab22b95 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -37,6 +37,7 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
struct sh_mobile_lcdc_sys_bus_cfg {
unsigned long ldmt2r;
unsigned long ldmt3r;
+ unsigned long deferred_io_msec;
};
struct sh_mobile_lcdc_sys_bus_ops {
diff --git a/include/xen/interface/event_channel.h b/include/xen/interface/event_channel.h
index 919b5bdcb2bd..2090881c3650 100644
--- a/include/xen/interface/event_channel.h
+++ b/include/xen/interface/event_channel.h
@@ -9,6 +9,8 @@
#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__
#define __XEN_PUBLIC_EVENT_CHANNEL_H__
+#include <xen/interface/xen.h>
+
typedef uint32_t evtchn_port_t;
DEFINE_GUEST_HANDLE(evtchn_port_t);
diff --git a/init/Kconfig b/init/Kconfig
index b3782c6d5ede..f6281711166d 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -588,6 +588,13 @@ config KALLSYMS_ALL
Say N.
+config KALLSYMS_STRIP_GENERATED
+ bool "Strip machine generated symbols from kallsyms"
+ depends on KALLSYMS_ALL
+ default y
+ help
+ Say N if you want kallsyms to retain even machine generated symbols.
+
config KALLSYMS_EXTRA_PASS
bool "Do an extra kallsyms pass"
depends on KALLSYMS
@@ -938,10 +945,90 @@ source "block/Kconfig"
config PREEMPT_NOTIFIERS
bool
+choice
+ prompt "RCU Implementation"
+ default CLASSIC_RCU
+
config CLASSIC_RCU
- def_bool !PREEMPT_RCU
+ bool "Classic RCU"
help
This option selects the classic RCU implementation that is
designed for best read-side performance on non-realtime
- systems. Classic RCU is the default. Note that the
- PREEMPT_RCU symbol is used to select/deselect this option.
+ systems.
+
+ Select this option if you are unsure.
+
+config TREE_RCU
+ bool "Tree-based hierarchical RCU"
+ help
+ This option selects the RCU implementation that is
+ designed for very large SMP system with hundreds or
+ thousands of CPUs.
+
+config PREEMPT_RCU
+ bool "Preemptible RCU"
+ depends on PREEMPT
+ help
+ This option reduces the latency of the kernel by making certain
+ RCU sections preemptible. Normally RCU code is non-preemptible, if
+ this option is selected then read-only RCU sections become
+ preemptible. This helps latency, but may expose bugs due to
+ now-naive assumptions about each RCU read-side critical section
+ remaining on a given CPU through its execution.
+
+endchoice
+
+config RCU_TRACE
+ bool "Enable tracing for RCU"
+ depends on TREE_RCU || PREEMPT_RCU
+ help
+ This option provides tracing in RCU which presents stats
+ in debugfs for debugging RCU implementation.
+
+ Say Y here if you want to enable RCU tracing
+ Say N if you are unsure.
+
+config RCU_FANOUT
+ int "Tree-based hierarchical RCU fanout value"
+ range 2 64 if 64BIT
+ range 2 32 if !64BIT
+ depends on TREE_RCU
+ default 64 if 64BIT
+ default 32 if !64BIT
+ help
+ This option controls the fanout of hierarchical implementations
+ of RCU, allowing RCU to work efficiently on machines with
+ large numbers of CPUs. This value must be at least the cube
+ root of NR_CPUS, which allows NR_CPUS up to 32,768 for 32-bit
+ systems and up to 262,144 for 64-bit systems.
+
+ Select a specific number if testing RCU itself.
+ Take the default if unsure.
+
+config RCU_FANOUT_EXACT
+ bool "Disable tree-based hierarchical RCU auto-balancing"
+ depends on TREE_RCU
+ default n
+ help
+ This option forces use of the exact RCU_FANOUT value specified,
+ regardless of imbalances in the hierarchy. This is useful for
+ testing RCU itself, and might one day be useful on systems with
+ strong NUMA behavior.
+
+ Without RCU_FANOUT_EXACT, the code will balance the hierarchy.
+
+ Say N if unsure.
+
+config TREE_RCU_TRACE
+ def_bool RCU_TRACE && TREE_RCU
+ select DEBUG_FS
+ help
+ This option provides tracing for the TREE_RCU implementation,
+ permitting Makefile to trivially select kernel/rcutree_trace.c.
+
+config PREEMPT_RCU_TRACE
+ def_bool RCU_TRACE && PREEMPT_RCU
+ select DEBUG_FS
+ help
+ This option provides tracing for the PREEMPT_RCU implementation,
+ permitting Makefile to trivially select kernel/rcupreempt_trace.c.
diff --git a/init/main.c b/init/main.c
index 9d761aa53296..546ebd2f44ba 100644
--- a/init/main.c
+++ b/init/main.c
@@ -380,12 +380,7 @@ EXPORT_SYMBOL(nr_cpu_ids);
/* An arch may set nr_cpu_ids earlier if needed, so this would be redundant */
static void __init setup_nr_cpu_ids(void)
{
- int cpu, highest_cpu = 0;
-
- for_each_possible_cpu(cpu)
- highest_cpu = cpu;
-
- nr_cpu_ids = highest_cpu + 1;
+ nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
}
#ifndef CONFIG_HAVE_SETUP_PER_CPU_AREA
@@ -527,9 +522,9 @@ static void __init boot_cpu_init(void)
{
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
- cpu_set(cpu, cpu_online_map);
- cpu_set(cpu, cpu_present_map);
- cpu_set(cpu, cpu_possible_map);
+ set_cpu_online(cpu, true);
+ set_cpu_present(cpu, true);
+ set_cpu_possible(cpu, true);
}
void __init __weak smp_setup_processor_id(void)
@@ -681,6 +676,7 @@ asmlinkage void __init start_kernel(void)
efi_enter_virtual_mode();
#endif
thread_info_cache_init();
+ cred_init();
fork_init(num_physpages);
proc_caches_init();
buffer_init();
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 68eb857cfdea..d9393f8e4c3e 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -112,13 +112,14 @@ static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode)
static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
struct mq_attr *attr)
{
+ struct user_struct *u = current_user();
struct inode *inode;
inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mtime = inode->i_ctime = inode->i_atime =
CURRENT_TIME;
@@ -126,7 +127,6 @@ static struct inode *mqueue_get_inode(struct super_block *sb, int mode,
if (S_ISREG(mode)) {
struct mqueue_inode_info *info;
struct task_struct *p = current;
- struct user_struct *u = p->user;
unsigned long mq_bytes, mq_msg_tblsz;
inode->i_fop = &mqueue_file_operations;
@@ -507,7 +507,7 @@ static void __do_notify(struct mqueue_inode_info *info)
sig_i.si_code = SI_MESGQ;
sig_i.si_value = info->notify.sigev_value;
sig_i.si_pid = task_tgid_vnr(current);
- sig_i.si_uid = current->uid;
+ sig_i.si_uid = current_uid();
kill_pid_info(info->notify.sigev_signo,
&sig_i, info->notify_owner);
@@ -594,6 +594,7 @@ static int mq_attr_ok(struct mq_attr *attr)
static struct file *do_create(struct dentry *dir, struct dentry *dentry,
int oflag, mode_t mode, struct mq_attr __user *u_attr)
{
+ const struct cred *cred = current_cred();
struct mq_attr attr;
struct file *result;
int ret;
@@ -618,7 +619,7 @@ static struct file *do_create(struct dentry *dir, struct dentry *dentry,
if (ret)
goto out_drop_write;
- result = dentry_open(dentry, mqueue_mnt, oflag);
+ result = dentry_open(dentry, mqueue_mnt, oflag, cred);
/*
* dentry_open() took a persistent mnt_want_write(),
* so we can now drop this one.
@@ -637,8 +638,10 @@ out:
/* Opens existing queue */
static struct file *do_open(struct dentry *dentry, int oflag)
{
-static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
- MAY_READ | MAY_WRITE };
+ const struct cred *cred = current_cred();
+
+ static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
+ MAY_READ | MAY_WRITE };
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) {
dput(dentry);
@@ -652,7 +655,7 @@ static int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE,
return ERR_PTR(-EACCES);
}
- return dentry_open(dentry, mqueue_mnt, oflag);
+ return dentry_open(dentry, mqueue_mnt, oflag, cred);
}
asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode,
diff --git a/ipc/shm.c b/ipc/shm.c
index 867e5d6a55c2..38a055758a9b 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -366,7 +366,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
if (shmflg & SHM_HUGETLB) {
/* hugetlb_file_setup takes care of mlock user accounting */
file = hugetlb_file_setup(name, size);
- shp->mlock_user = current->user;
+ shp->mlock_user = current_user();
} else {
int acctflag = VM_ACCOUNT;
/*
@@ -752,9 +752,10 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
goto out_unlock;
if (!capable(CAP_IPC_LOCK)) {
+ uid_t euid = current_euid();
err = -EPERM;
- if (current->euid != shp->shm_perm.uid &&
- current->euid != shp->shm_perm.cuid)
+ if (euid != shp->shm_perm.uid &&
+ euid != shp->shm_perm.cuid)
goto out_unlock;
if (cmd == SHM_LOCK &&
!current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
@@ -766,7 +767,7 @@ asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
goto out_unlock;
if(cmd==SHM_LOCK) {
- struct user_struct * user = current->user;
+ struct user_struct *user = current_user();
if (!is_file_hugepages(shp->shm_file)) {
err = shmem_lock(shp->shm_file, 1, user);
if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
diff --git a/ipc/util.c b/ipc/util.c
index 361fd1c96fcf..5a1808c774a2 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -258,6 +258,8 @@ int ipc_get_maxid(struct ipc_ids *ids)
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{
+ uid_t euid;
+ gid_t egid;
int id, err;
if (size > IPCMNI)
@@ -280,8 +282,9 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
ids->in_use++;
- new->cuid = new->uid = current->euid;
- new->gid = new->cgid = current->egid;
+ current_euid_egid(&euid, &egid);
+ new->cuid = new->uid = euid;
+ new->gid = new->cgid = egid;
new->seq = ids->seq++;
if(ids->seq > ids->seq_max)
@@ -620,13 +623,15 @@ void ipc_rcu_putref(void *ptr)
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
+ uid_t euid = current_euid();
int requested_mode, granted_mode, err;
if (unlikely((err = audit_ipc_obj(ipcp))))
return err;
requested_mode = (flag >> 6) | (flag >> 3) | flag;
granted_mode = ipcp->mode;
- if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
+ if (euid == ipcp->cuid ||
+ euid == ipcp->uid)
granted_mode >>= 6;
else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
granted_mode >>= 3;
@@ -788,6 +793,7 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd,
struct ipc64_perm *perm, int extra_perm)
{
struct kern_ipc_perm *ipcp;
+ uid_t euid;
int err;
down_write(&ids->rw_mutex);
@@ -807,8 +813,10 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_ids *ids, int id, int cmd,
if (err)
goto out_unlock;
}
- if (current->euid == ipcp->cuid ||
- current->euid == ipcp->uid || capable(CAP_SYS_ADMIN))
+
+ euid = current_euid();
+ if (euid == ipcp->cuid ||
+ euid == ipcp->uid || capable(CAP_SYS_ADMIN))
return ipcp;
err = -EPERM;
diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt
index 9fdba03dc1fc..bf987b95b356 100644
--- a/kernel/Kconfig.preempt
+++ b/kernel/Kconfig.preempt
@@ -52,28 +52,3 @@ config PREEMPT
endchoice
-config PREEMPT_RCU
- bool "Preemptible RCU"
- depends on PREEMPT
- default n
- help
- This option reduces the latency of the kernel by making certain
- RCU sections preemptible. Normally RCU code is non-preemptible, if
- this option is selected then read-only RCU sections become
- preemptible. This helps latency, but may expose bugs due to
- now-naive assumptions about each RCU read-side critical section
- remaining on a given CPU through its execution.
-
- Say N if you are unsure.
-
-config RCU_TRACE
- bool "Enable tracing for RCU - currently stats in debugfs"
- depends on PREEMPT_RCU
- select DEBUG_FS
- default y
- help
- This option provides tracing in RCU which presents stats
- in debugfs for debugging RCU implementation.
-
- Say Y here if you want to enable RCU tracing
- Say N if you are unsure.
diff --git a/kernel/Makefile b/kernel/Makefile
index 6a212b842d86..e1c5bf3365c0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
rcupdate.o extable.o params.o posix-timers.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 pm_qos_params.o sched_clock.o
+ notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o
ifdef CONFIG_FUNCTION_TRACER
# Do not trace debug files and internal ftrace files
@@ -73,10 +73,10 @@ obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
obj-$(CONFIG_SECCOMP) += seccomp.o
obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
obj-$(CONFIG_CLASSIC_RCU) += rcuclassic.o
+obj-$(CONFIG_TREE_RCU) += rcutree.o
obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
-ifeq ($(CONFIG_PREEMPT_RCU),y)
-obj-$(CONFIG_RCU_TRACE) += rcupreempt_trace.o
-endif
+obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o
+obj-$(CONFIG_PREEMPT_RCU_TRACE) += rcupreempt_trace.o
obj-$(CONFIG_RELAY) += relay.o
obj-$(CONFIG_SYSCTL) += utsname_sysctl.o
obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
diff --git a/kernel/acct.c b/kernel/acct.c
index f6006a60df5d..d57b7cbb98b6 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -530,15 +530,14 @@ static void do_acct_process(struct bsd_acct_struct *acct,
do_div(elapsed, AHZ);
ac.ac_btime = get_seconds() - elapsed;
/* we really need to bite the bullet and change layout */
- ac.ac_uid = current->uid;
- ac.ac_gid = current->gid;
+ current_uid_gid(&ac.ac_uid, &ac.ac_gid);
#if ACCT_VERSION==2
ac.ac_ahz = AHZ;
#endif
#if ACCT_VERSION==1 || ACCT_VERSION==2
/* backward-compatible 16 bit fields */
- ac.ac_uid16 = current->uid;
- ac.ac_gid16 = current->gid;
+ ac.ac_uid16 = ac.ac_uid;
+ ac.ac_gid16 = ac.ac_gid;
#endif
#if ACCT_VERSION==3
ac.ac_pid = task_tgid_nr_ns(current, ns);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 2a3f0afc4d2a..4819f3711973 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -65,6 +65,7 @@
#include <linux/highmem.h>
#include <linux/syscalls.h>
#include <linux/inotify.h>
+#include <linux/capability.h>
#include "audit.h"
@@ -84,6 +85,15 @@ int audit_n_rules;
/* determines whether we collect data for signals sent */
int audit_signals;
+struct audit_cap_data {
+ kernel_cap_t permitted;
+ kernel_cap_t inheritable;
+ union {
+ unsigned int fE; /* effective bit of a file capability */
+ kernel_cap_t effective; /* effective set of a process */
+ };
+};
+
/* When fs/namei.c:getname() is called, we store the pointer in name and
* we don't let putname() free it (instead we free all of the saved
* pointers at syscall exit time).
@@ -100,6 +110,8 @@ struct audit_names {
gid_t gid;
dev_t rdev;
u32 osid;
+ struct audit_cap_data fcap;
+ unsigned int fcap_ver;
};
struct audit_aux_data {
@@ -184,6 +196,20 @@ struct audit_aux_data_pids {
int pid_count;
};
+struct audit_aux_data_bprm_fcaps {
+ struct audit_aux_data d;
+ struct audit_cap_data fcap;
+ unsigned int fcap_ver;
+ struct audit_cap_data old_pcap;
+ struct audit_cap_data new_pcap;
+};
+
+struct audit_aux_data_capset {
+ struct audit_aux_data d;
+ pid_t pid;
+ struct audit_cap_data cap;
+};
+
struct audit_tree_refs {
struct audit_tree_refs *next;
struct audit_chunk *c[31];
@@ -421,6 +447,7 @@ static int audit_filter_rules(struct task_struct *tsk,
struct audit_names *name,
enum audit_state *state)
{
+ const struct cred *cred = get_task_cred(tsk);
int i, j, need_sid = 1;
u32 sid;
@@ -440,28 +467,28 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_UID:
- result = audit_comparator(tsk->uid, f->op, f->val);
+ result = audit_comparator(cred->uid, f->op, f->val);
break;
case AUDIT_EUID:
- result = audit_comparator(tsk->euid, f->op, f->val);
+ result = audit_comparator(cred->euid, f->op, f->val);
break;
case AUDIT_SUID:
- result = audit_comparator(tsk->suid, f->op, f->val);
+ result = audit_comparator(cred->suid, f->op, f->val);
break;
case AUDIT_FSUID:
- result = audit_comparator(tsk->fsuid, f->op, f->val);
+ result = audit_comparator(cred->fsuid, f->op, f->val);
break;
case AUDIT_GID:
- result = audit_comparator(tsk->gid, f->op, f->val);
+ result = audit_comparator(cred->gid, f->op, f->val);
break;
case AUDIT_EGID:
- result = audit_comparator(tsk->egid, f->op, f->val);
+ result = audit_comparator(cred->egid, f->op, f->val);
break;
case AUDIT_SGID:
- result = audit_comparator(tsk->sgid, f->op, f->val);
+ result = audit_comparator(cred->sgid, f->op, f->val);
break;
case AUDIT_FSGID:
- result = audit_comparator(tsk->fsgid, f->op, f->val);
+ result = audit_comparator(cred->fsgid, f->op, f->val);
break;
case AUDIT_PERS:
result = audit_comparator(tsk->personality, f->op, f->val);
@@ -615,8 +642,10 @@ static int audit_filter_rules(struct task_struct *tsk,
break;
}
- if (!result)
+ if (!result) {
+ put_cred(cred);
return 0;
+ }
}
if (rule->filterkey && ctx)
ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
@@ -624,6 +653,7 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
}
+ put_cred(cred);
return 1;
}
@@ -1171,8 +1201,38 @@ static void audit_log_execve_info(struct audit_context *context,
kfree(buf);
}
+static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
+{
+ int i;
+
+ audit_log_format(ab, " %s=", prefix);
+ CAP_FOR_EACH_U32(i) {
+ audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+ }
+}
+
+static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+{
+ kernel_cap_t *perm = &name->fcap.permitted;
+ kernel_cap_t *inh = &name->fcap.inheritable;
+ int log = 0;
+
+ if (!cap_isclear(*perm)) {
+ audit_log_cap(ab, "cap_fp", perm);
+ log = 1;
+ }
+ if (!cap_isclear(*inh)) {
+ audit_log_cap(ab, "cap_fi", inh);
+ log = 1;
+ }
+
+ if (log)
+ audit_log_format(ab, " cap_fe=%d cap_fver=%x", name->fcap.fE, name->fcap_ver);
+}
+
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{
+ const struct cred *cred;
int i, call_panic = 0;
struct audit_buffer *ab;
struct audit_aux_data *aux;
@@ -1182,14 +1242,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
context->pid = tsk->pid;
if (!context->ppid)
context->ppid = sys_getppid();
- context->uid = tsk->uid;
- context->gid = tsk->gid;
- context->euid = tsk->euid;
- context->suid = tsk->suid;
- context->fsuid = tsk->fsuid;
- context->egid = tsk->egid;
- context->sgid = tsk->sgid;
- context->fsgid = tsk->fsgid;
+ cred = current_cred();
+ context->uid = cred->uid;
+ context->gid = cred->gid;
+ context->euid = cred->euid;
+ context->suid = cred->suid;
+ context->fsuid = cred->fsuid;
+ context->egid = cred->egid;
+ context->sgid = cred->sgid;
+ context->fsgid = cred->fsgid;
context->personality = tsk->personality;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
@@ -1334,6 +1395,28 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]);
break; }
+ case AUDIT_BPRM_FCAPS: {
+ struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
+ audit_log_format(ab, "fver=%x", axs->fcap_ver);
+ audit_log_cap(ab, "fp", &axs->fcap.permitted);
+ audit_log_cap(ab, "fi", &axs->fcap.inheritable);
+ audit_log_format(ab, " fe=%d", axs->fcap.fE);
+ audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
+ audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
+ audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
+ audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
+ audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
+ audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
+ break; }
+
+ case AUDIT_CAPSET: {
+ struct audit_aux_data_capset *axs = (void *)aux;
+ audit_log_format(ab, "pid=%d", axs->pid);
+ audit_log_cap(ab, "cap_pi", &axs->cap.inheritable);
+ audit_log_cap(ab, "cap_pp", &axs->cap.permitted);
+ audit_log_cap(ab, "cap_pe", &axs->cap.effective);
+ break; }
+
}
audit_log_end(ab);
}
@@ -1421,6 +1504,8 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
}
}
+ audit_log_fcaps(ab, n);
+
audit_log_end(ab);
}
@@ -1802,8 +1887,36 @@ static int audit_inc_name_count(struct audit_context *context,
return 0;
}
+
+static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry)
+{
+ struct cpu_vfs_cap_data caps;
+ int rc;
+
+ memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t));
+ memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t));
+ name->fcap.fE = 0;
+ name->fcap_ver = 0;
+
+ if (!dentry)
+ return 0;
+
+ rc = get_vfs_caps_from_disk(dentry, &caps);
+ if (rc)
+ return rc;
+
+ name->fcap.permitted = caps.permitted;
+ name->fcap.inheritable = caps.inheritable;
+ name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+ name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+ return 0;
+}
+
+
/* Copy inode data into an audit_names. */
-static void audit_copy_inode(struct audit_names *name, const struct inode *inode)
+static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
+ const struct inode *inode)
{
name->ino = inode->i_ino;
name->dev = inode->i_sb->s_dev;
@@ -1812,6 +1925,7 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode
name->gid = inode->i_gid;
name->rdev = inode->i_rdev;
security_inode_getsecid(inode, &name->osid);
+ audit_copy_fcaps(name, dentry);
}
/**
@@ -1846,7 +1960,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
context->names[idx].name = NULL;
}
handle_path(dentry);
- audit_copy_inode(&context->names[idx], inode);
+ audit_copy_inode(&context->names[idx], dentry, inode);
}
/**
@@ -1907,7 +2021,7 @@ void __audit_inode_child(const char *dname, const struct dentry *dentry,
if (!strcmp(dname, n->name) ||
!audit_compare_dname_path(dname, n->name, &dirlen)) {
if (inode)
- audit_copy_inode(n, inode);
+ audit_copy_inode(n, NULL, inode);
else
n->ino = (unsigned long)-1;
found_child = n->name;
@@ -1921,7 +2035,7 @@ add_names:
return;
idx = context->name_count - 1;
context->names[idx].name = NULL;
- audit_copy_inode(&context->names[idx], parent);
+ audit_copy_inode(&context->names[idx], NULL, parent);
}
if (!found_child) {
@@ -1942,7 +2056,7 @@ add_names:
}
if (inode)
- audit_copy_inode(&context->names[idx], inode);
+ audit_copy_inode(&context->names[idx], NULL, inode);
else
context->names[idx].ino = (unsigned long)-1;
}
@@ -1996,7 +2110,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
audit_log_format(ab, "login pid=%d uid=%u "
"old auid=%u new auid=%u"
" old ses=%u new ses=%u",
- task->pid, task->uid,
+ task->pid, task_uid(task),
task->loginuid, loginuid,
task->sessionid, sessionid);
audit_log_end(ab);
@@ -2379,7 +2493,7 @@ void __audit_ptrace(struct task_struct *t)
context->target_pid = t->pid;
context->target_auid = audit_get_loginuid(t);
- context->target_uid = t->uid;
+ context->target_uid = task_uid(t);
context->target_sessionid = audit_get_sessionid(t);
security_task_getsecid(t, &context->target_sid);
memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
@@ -2398,6 +2512,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
struct audit_aux_data_pids *axp;
struct task_struct *tsk = current;
struct audit_context *ctx = tsk->audit_context;
+ uid_t uid = current_uid(), t_uid = task_uid(t);
if (audit_pid && t->tgid == audit_pid) {
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
@@ -2405,7 +2520,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
if (tsk->loginuid != -1)
audit_sig_uid = tsk->loginuid;
else
- audit_sig_uid = tsk->uid;
+ audit_sig_uid = uid;
security_task_getsecid(tsk, &audit_sig_sid);
}
if (!audit_signals || audit_dummy_context())
@@ -2417,7 +2532,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
if (!ctx->target_pid) {
ctx->target_pid = t->tgid;
ctx->target_auid = audit_get_loginuid(t);
- ctx->target_uid = t->uid;
+ ctx->target_uid = t_uid;
ctx->target_sessionid = audit_get_sessionid(t);
security_task_getsecid(t, &ctx->target_sid);
memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
@@ -2438,7 +2553,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
axp->target_pid[axp->pid_count] = t->tgid;
axp->target_auid[axp->pid_count] = audit_get_loginuid(t);
- axp->target_uid[axp->pid_count] = t->uid;
+ axp->target_uid[axp->pid_count] = t_uid;
axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
security_task_getsecid(t, &axp->target_sid[axp->pid_count]);
memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
@@ -2448,6 +2563,86 @@ int __audit_signal_info(int sig, struct task_struct *t)
}
/**
+ * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps
+ * @bprm: pointer to the bprm being processed
+ * @new: the proposed new credentials
+ * @old: the old credentials
+ *
+ * Simply check if the proc already has the caps given by the file and if not
+ * store the priv escalation info for later auditing at the end of the syscall
+ *
+ * -Eric
+ */
+int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
+ const struct cred *new, const struct cred *old)
+{
+ struct audit_aux_data_bprm_fcaps *ax;
+ struct audit_context *context = current->audit_context;
+ struct cpu_vfs_cap_data vcaps;
+ struct dentry *dentry;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->d.type = AUDIT_BPRM_FCAPS;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+
+ dentry = dget(bprm->file->f_dentry);
+ get_vfs_caps_from_disk(dentry, &vcaps);
+ dput(dentry);
+
+ ax->fcap.permitted = vcaps.permitted;
+ ax->fcap.inheritable = vcaps.inheritable;
+ ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+ ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+ ax->old_pcap.permitted = old->cap_permitted;
+ ax->old_pcap.inheritable = old->cap_inheritable;
+ ax->old_pcap.effective = old->cap_effective;
+
+ ax->new_pcap.permitted = new->cap_permitted;
+ ax->new_pcap.inheritable = new->cap_inheritable;
+ ax->new_pcap.effective = new->cap_effective;
+ return 0;
+}
+
+/**
+ * __audit_log_capset - store information about the arguments to the capset syscall
+ * @pid: target pid of the capset call
+ * @new: the new credentials
+ * @old: the old (current) credentials
+ *
+ * Record the aguments userspace sent to sys_capset for later printing by the
+ * audit system if applicable
+ */
+int __audit_log_capset(pid_t pid,
+ const struct cred *new, const struct cred *old)
+{
+ struct audit_aux_data_capset *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!audit_enabled || !context || context->dummy))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->d.type = AUDIT_CAPSET;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+
+ ax->pid = pid;
+ ax->cap.effective = new->cap_effective;
+ ax->cap.inheritable = new->cap_effective;
+ ax->cap.permitted = new->cap_permitted;
+
+ return 0;
+}
+
+/**
* audit_core_dumps - record information about processes that end abnormally
* @signr: signal value
*
@@ -2458,7 +2653,8 @@ void audit_core_dumps(long signr)
{
struct audit_buffer *ab;
u32 sid;
- uid_t auid = audit_get_loginuid(current);
+ uid_t auid = audit_get_loginuid(current), uid;
+ gid_t gid;
unsigned int sessionid = audit_get_sessionid(current);
if (!audit_enabled)
@@ -2468,8 +2664,9 @@ void audit_core_dumps(long signr)
return;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
+ current_uid_gid(&uid, &gid);
audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u",
- auid, current->uid, current->gid, sessionid);
+ auid, uid, gid, sessionid);
security_task_getsecid(current, &sid);
if (sid) {
char *ctx = NULL;
diff --git a/kernel/capability.c b/kernel/capability.c
index 33e51e78c2d8..36b4b4daebec 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -7,6 +7,7 @@
* 30 May 2002: Cleanup, Robert M. Love <rml@tech9.net>
*/
+#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -14,12 +15,7 @@
#include <linux/syscalls.h>
#include <linux/pid_namespace.h>
#include <asm/uaccess.h>
-
-/*
- * This lock protects task->cap_* for all tasks including current.
- * Locking rule: acquire this prior to tasklist_lock.
- */
-static DEFINE_SPINLOCK(task_capability_lock);
+#include "cred-internals.h"
/*
* Leveraged for setting/resetting capabilities
@@ -33,6 +29,17 @@ EXPORT_SYMBOL(__cap_empty_set);
EXPORT_SYMBOL(__cap_full_set);
EXPORT_SYMBOL(__cap_init_eff_set);
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+int file_caps_enabled = 1;
+
+static int __init file_caps_disable(char *str)
+{
+ file_caps_enabled = 0;
+ return 1;
+}
+__setup("no_file_caps", file_caps_disable);
+#endif
+
/*
* More recent versions of libcap are available from:
*
@@ -115,167 +122,12 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
return 0;
}
-#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
-
-/*
- * Without filesystem capability support, we nominally support one process
- * setting the capabilities of another
- */
-static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
- kernel_cap_t *pIp, kernel_cap_t *pPp)
-{
- struct task_struct *target;
- int ret;
-
- spin_lock(&task_capability_lock);
- read_lock(&tasklist_lock);
-
- if (pid && pid != task_pid_vnr(current)) {
- target = find_task_by_vpid(pid);
- if (!target) {
- ret = -ESRCH;
- goto out;
- }
- } else
- target = current;
-
- ret = security_capget(target, pEp, pIp, pPp);
-
-out:
- read_unlock(&tasklist_lock);
- spin_unlock(&task_capability_lock);
-
- return ret;
-}
-
-/*
- * cap_set_pg - set capabilities for all processes in a given process
- * group. We call this holding task_capability_lock and tasklist_lock.
- */
-static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
-{
- struct task_struct *g, *target;
- int ret = -EPERM;
- int found = 0;
- struct pid *pgrp;
-
- spin_lock(&task_capability_lock);
- read_lock(&tasklist_lock);
-
- pgrp = find_vpid(pgrp_nr);
- do_each_pid_task(pgrp, PIDTYPE_PGID, g) {
- target = g;
- while_each_thread(g, target) {
- if (!security_capset_check(target, effective,
- inheritable, permitted)) {
- security_capset_set(target, effective,
- inheritable, permitted);
- ret = 0;
- }
- found = 1;
- }
- } while_each_pid_task(pgrp, PIDTYPE_PGID, g);
-
- read_unlock(&tasklist_lock);
- spin_unlock(&task_capability_lock);
-
- if (!found)
- ret = 0;
- return ret;
-}
-
-/*
- * cap_set_all - set capabilities for all processes other than init
- * and self. We call this holding task_capability_lock and tasklist_lock.
- */
-static inline int cap_set_all(kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
-{
- struct task_struct *g, *target;
- int ret = -EPERM;
- int found = 0;
-
- spin_lock(&task_capability_lock);
- read_lock(&tasklist_lock);
-
- do_each_thread(g, target) {
- if (target == current
- || is_container_init(target->group_leader))
- continue;
- found = 1;
- if (security_capset_check(target, effective, inheritable,
- permitted))
- continue;
- ret = 0;
- security_capset_set(target, effective, inheritable, permitted);
- } while_each_thread(g, target);
-
- read_unlock(&tasklist_lock);
- spin_unlock(&task_capability_lock);
-
- if (!found)
- ret = 0;
-
- return ret;
-}
-
-/*
- * Given the target pid does not refer to the current process we
- * need more elaborate support... (This support is not present when
- * filesystem capabilities are configured.)
- */
-static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
-{
- struct task_struct *target;
- int ret;
-
- if (!capable(CAP_SETPCAP))
- return -EPERM;
-
- if (pid == -1) /* all procs other than current and init */
- return cap_set_all(effective, inheritable, permitted);
-
- else if (pid < 0) /* all procs in process group */
- return cap_set_pg(-pid, effective, inheritable, permitted);
-
- /* target != current */
- spin_lock(&task_capability_lock);
- read_lock(&tasklist_lock);
-
- target = find_task_by_vpid(pid);
- if (!target)
- ret = -ESRCH;
- else {
- ret = security_capset_check(target, effective, inheritable,
- permitted);
-
- /* having verified that the proposed changes are legal,
- we now put them into effect. */
- if (!ret)
- security_capset_set(target, effective, inheritable,
- permitted);
- }
-
- read_unlock(&tasklist_lock);
- spin_unlock(&task_capability_lock);
-
- return ret;
-}
-
-#else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */
-
/*
- * If we have configured with filesystem capability support, then the
- * only thing that can change the capabilities of the current process
- * is the current process. As such, we can't be in this code at the
- * same time as we are in the process of setting capabilities in this
- * process. The net result is that we can limit our use of locks to
- * when we are reading the caps of another process.
+ * The only thing that can change the capabilities of the current
+ * process is the current process. As such, we can't be in this code
+ * at the same time as we are in the process of setting capabilities
+ * in this process. The net result is that we can limit our use of
+ * locks to when we are reading the caps of another process.
*/
static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
kernel_cap_t *pIp, kernel_cap_t *pPp)
@@ -285,7 +137,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
if (pid && (pid != task_pid_vnr(current))) {
struct task_struct *target;
- spin_lock(&task_capability_lock);
read_lock(&tasklist_lock);
target = find_task_by_vpid(pid);
@@ -295,50 +146,12 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
ret = security_capget(target, pEp, pIp, pPp);
read_unlock(&tasklist_lock);
- spin_unlock(&task_capability_lock);
} else
ret = security_capget(current, pEp, pIp, pPp);
return ret;
}
-/*
- * With filesystem capability support configured, the kernel does not
- * permit the changing of capabilities in one process by another
- * process. (CAP_SETPCAP has much less broad semantics when configured
- * this way.)
- */
-static inline int do_sys_capset_other_tasks(pid_t pid,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
-{
- return -EPERM;
-}
-
-#endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
-
-/*
- * Atomically modify the effective capabilities returning the original
- * value. No permission check is performed here - it is assumed that the
- * caller is permitted to set the desired effective capabilities.
- */
-kernel_cap_t cap_set_effective(const kernel_cap_t pE_new)
-{
- kernel_cap_t pE_old;
-
- spin_lock(&task_capability_lock);
-
- pE_old = current->cap_effective;
- current->cap_effective = pE_new;
-
- spin_unlock(&task_capability_lock);
-
- return pE_old;
-}
-
-EXPORT_SYMBOL(cap_set_effective);
-
/**
* sys_capget - get the capabilities of a given process.
* @header: pointer to struct that contains capability version and
@@ -366,7 +179,6 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
return -EINVAL;
ret = cap_get_target_pid(pid, &pE, &pI, &pP);
-
if (!ret) {
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i;
@@ -412,16 +224,14 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
* @data: pointer to struct that contains the effective, permitted,
* and inheritable capabilities
*
- * Set capabilities for a given process, all processes, or all
- * processes in a given process group.
+ * Set capabilities for the current process only. The ability to any other
+ * process(es) has been deprecated and removed.
*
* The restrictions on setting capabilities are specified as:
*
- * [pid is for the 'target' task. 'current' is the calling task.]
- *
- * I: any raised capabilities must be a subset of the (old current) permitted
- * P: any raised capabilities must be a subset of the (old current) permitted
- * E: must be set to a subset of (new target) permitted
+ * I: any raised capabilities must be a subset of the old permitted
+ * P: any raised capabilities must be a subset of the old permitted
+ * E: must be set to a subset of new permitted
*
* Returns 0 on success and < 0 on error.
*/
@@ -430,6 +240,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i, tocopy;
kernel_cap_t inheritable, permitted, effective;
+ struct cred *new;
int ret;
pid_t pid;
@@ -440,10 +251,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
if (get_user(pid, &header->pid))
return -EFAULT;
- if (copy_from_user(&kdata, data, tocopy
- * sizeof(struct __user_cap_data_struct))) {
+ /* may only affect current now */
+ if (pid != 0 && pid != task_pid_vnr(current))
+ return -EPERM;
+
+ if (copy_from_user(&kdata, data,
+ tocopy * sizeof(struct __user_cap_data_struct)))
return -EFAULT;
- }
for (i = 0; i < tocopy; i++) {
effective.cap[i] = kdata[i].effective;
@@ -457,32 +271,23 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
i++;
}
- if (pid && (pid != task_pid_vnr(current)))
- ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
- &permitted);
- else {
- /*
- * This lock is required even when filesystem
- * capability support is configured - it protects the
- * sys_capget() call from returning incorrect data in
- * the case that the targeted process is not the
- * current one.
- */
- spin_lock(&task_capability_lock);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- ret = security_capset_check(current, &effective, &inheritable,
- &permitted);
- /*
- * Having verified that the proposed changes are
- * legal, we now put them into effect.
- */
- if (!ret)
- security_capset_set(current, &effective, &inheritable,
- &permitted);
- spin_unlock(&task_capability_lock);
- }
+ ret = security_capset(new, current_cred(),
+ &effective, &inheritable, &permitted);
+ if (ret < 0)
+ goto error;
+
+ ret = audit_log_capset(pid, new, current_cred());
+ if (ret < 0)
+ return ret;
+ return commit_creds(new);
+error:
+ abort_creds(new);
return ret;
}
@@ -498,6 +303,11 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
*/
int capable(int cap)
{
+ if (unlikely(!cap_valid(cap))) {
+ printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap);
+ BUG();
+ }
+
if (has_capability(current, cap)) {
current->flags |= PF_SUPERPRIV;
return 1;
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 8185a0f09594..48348dde6d81 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -571,8 +571,8 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
@@ -1024,7 +1024,7 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
if (ret == -EBUSY) {
mutex_unlock(&cgroup_mutex);
mutex_unlock(&inode->i_mutex);
- goto drop_new_super;
+ goto free_cg_links;
}
/* EBUSY should be the only error here */
@@ -1073,10 +1073,11 @@ static int cgroup_get_sb(struct file_system_type *fs_type,
return simple_set_mnt(mnt, sb);
+ free_cg_links:
+ free_cg_links(&tmp_cg_links);
drop_new_super:
up_write(&sb->s_umount);
deactivate_super(sb);
- free_cg_links(&tmp_cg_links);
return ret;
}
@@ -1279,6 +1280,7 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
{
struct task_struct *tsk;
+ const struct cred *cred = current_cred(), *tcred;
int ret;
if (pid) {
@@ -1288,14 +1290,16 @@ static int attach_task_by_pid(struct cgroup *cgrp, u64 pid)
rcu_read_unlock();
return -ESRCH;
}
- get_task_struct(tsk);
- rcu_read_unlock();
- if ((current->euid) && (current->euid != tsk->uid)
- && (current->euid != tsk->suid)) {
- put_task_struct(tsk);
+ tcred = __task_cred(tsk);
+ if (cred->euid &&
+ cred->euid != tcred->uid &&
+ cred->euid != tcred->suid) {
+ rcu_read_unlock();
return -EACCES;
}
+ get_task_struct(tsk);
+ rcu_read_unlock();
} else {
tsk = current;
get_task_struct(tsk);
@@ -2934,9 +2938,6 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
again:
root = subsys->root;
if (root == &rootnode) {
- printk(KERN_INFO
- "Not cloning cgroup for unused subsystem %s\n",
- subsys->name);
mutex_unlock(&cgroup_mutex);
return 0;
}
diff --git a/kernel/compat.c b/kernel/compat.c
index 8eafe3eb50d9..d52e2ec1deb5 100644
--- a/kernel/compat.c
+++ b/kernel/compat.c
@@ -454,16 +454,16 @@ asmlinkage long compat_sys_waitid(int which, compat_pid_t pid,
}
static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr,
- unsigned len, cpumask_t *new_mask)
+ unsigned len, struct cpumask *new_mask)
{
unsigned long *k;
- if (len < sizeof(cpumask_t))
- memset(new_mask, 0, sizeof(cpumask_t));
- else if (len > sizeof(cpumask_t))
- len = sizeof(cpumask_t);
+ if (len < cpumask_size())
+ memset(new_mask, 0, cpumask_size());
+ else if (len > cpumask_size())
+ len = cpumask_size();
- k = cpus_addr(*new_mask);
+ k = cpumask_bits(new_mask);
return compat_get_bitmap(k, user_mask_ptr, len * 8);
}
@@ -471,40 +471,51 @@ asmlinkage long compat_sys_sched_setaffinity(compat_pid_t pid,
unsigned int len,
compat_ulong_t __user *user_mask_ptr)
{
- cpumask_t new_mask;
+ cpumask_var_t new_mask;
int retval;
- retval = compat_get_user_cpu_mask(user_mask_ptr, len, &new_mask);
+ if (!alloc_cpumask_var(&new_mask, GFP_KERNEL))
+ return -ENOMEM;
+
+ retval = compat_get_user_cpu_mask(user_mask_ptr, len, new_mask);
if (retval)
- return retval;
+ goto out;
- return sched_setaffinity(pid, &new_mask);
+ retval = sched_setaffinity(pid, new_mask);
+out:
+ free_cpumask_var(new_mask);
+ return retval;
}
asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, unsigned int len,
compat_ulong_t __user *user_mask_ptr)
{
int ret;
- cpumask_t mask;
+ cpumask_var_t mask;
unsigned long *k;
- unsigned int min_length = sizeof(cpumask_t);
+ unsigned int min_length = cpumask_size();
- if (NR_CPUS <= BITS_PER_COMPAT_LONG)
+ if (nr_cpu_ids <= BITS_PER_COMPAT_LONG)
min_length = sizeof(compat_ulong_t);
if (len < min_length)
return -EINVAL;
- ret = sched_getaffinity(pid, &mask);
+ if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+ return -ENOMEM;
+
+ ret = sched_getaffinity(pid, mask);
if (ret < 0)
- return ret;
+ goto out;
- k = cpus_addr(mask);
+ k = cpumask_bits(mask);
ret = compat_put_bitmap(user_mask_ptr, k, min_length * 8);
- if (ret)
- return ret;
+ if (ret == 0)
+ ret = min_length;
- return min_length;
+out:
+ free_cpumask_var(mask);
+ return ret;
}
int get_compat_itimerspec(struct itimerspec *dst,
diff --git a/kernel/cpu.c b/kernel/cpu.c
index bae131a1211b..47fff3b63cbf 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -15,30 +15,8 @@
#include <linux/stop_machine.h>
#include <linux/mutex.h>
-/*
- * Represents all cpu's present in the system
- * In systems capable of hotplug, this map could dynamically grow
- * as new cpu's are detected in the system via any platform specific
- * method, such as ACPI for e.g.
- */
-cpumask_t cpu_present_map __read_mostly;
-EXPORT_SYMBOL(cpu_present_map);
-
-/*
- * Represents all cpu's that are currently online.
- */
-cpumask_t cpu_online_map __read_mostly;
-EXPORT_SYMBOL(cpu_online_map);
-
-#ifdef CONFIG_INIT_ALL_POSSIBLE
-cpumask_t cpu_possible_map __read_mostly = CPU_MASK_ALL;
-#else
-cpumask_t cpu_possible_map __read_mostly;
-#endif
-EXPORT_SYMBOL(cpu_possible_map);
-
#ifdef CONFIG_SMP
-/* Serializes the updates to cpu_online_map, cpu_present_map */
+/* Serializes the updates to cpu_online_mask, cpu_present_mask */
static DEFINE_MUTEX(cpu_add_remove_lock);
static __cpuinitdata RAW_NOTIFIER_HEAD(cpu_chain);
@@ -65,8 +43,6 @@ void __init cpu_hotplug_init(void)
cpu_hotplug.refcount = 0;
}
-cpumask_t cpu_active_map;
-
#ifdef CONFIG_HOTPLUG_CPU
void get_online_cpus(void)
@@ -97,7 +73,7 @@ EXPORT_SYMBOL_GPL(put_online_cpus);
/*
* The following two API's must be used when attempting
- * to serialize the updates to cpu_online_map, cpu_present_map.
+ * to serialize the updates to cpu_online_mask, cpu_present_mask.
*/
void cpu_maps_update_begin(void)
{
@@ -218,7 +194,7 @@ static int __ref take_cpu_down(void *_param)
static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
{
int err, nr_calls = 0;
- cpumask_t old_allowed, tmp;
+ cpumask_var_t old_allowed;
void *hcpu = (void *)(long)cpu;
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
struct take_cpu_down_param tcd_param = {
@@ -232,6 +208,9 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
if (!cpu_online(cpu))
return -EINVAL;
+ if (!alloc_cpumask_var(&old_allowed, GFP_KERNEL))
+ return -ENOMEM;
+
cpu_hotplug_begin();
err = __raw_notifier_call_chain(&cpu_chain, CPU_DOWN_PREPARE | mod,
hcpu, -1, &nr_calls);
@@ -246,13 +225,11 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
}
/* Ensure that we are not runnable on dying cpu */
- old_allowed = current->cpus_allowed;
- cpus_setall(tmp);
- cpu_clear(cpu, tmp);
- set_cpus_allowed_ptr(current, &tmp);
- tmp = cpumask_of_cpu(cpu);
+ cpumask_copy(old_allowed, &current->cpus_allowed);
+ set_cpus_allowed_ptr(current,
+ cpumask_of(cpumask_any_but(cpu_online_mask, cpu)));
- err = __stop_machine(take_cpu_down, &tcd_param, &tmp);
+ err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
if (err) {
/* CPU didn't die: tell everyone. Can't complain. */
if (raw_notifier_call_chain(&cpu_chain, CPU_DOWN_FAILED | mod,
@@ -278,7 +255,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
check_for_tasks(cpu);
out_allowed:
- set_cpus_allowed_ptr(current, &old_allowed);
+ set_cpus_allowed_ptr(current, old_allowed);
out_release:
cpu_hotplug_done();
if (!err) {
@@ -286,6 +263,7 @@ out_release:
hcpu) == NOTIFY_BAD)
BUG();
}
+ free_cpumask_var(old_allowed);
return err;
}
@@ -304,7 +282,7 @@ int __ref cpu_down(unsigned int cpu)
/*
* Make sure the all cpus did the reschedule and are not
- * using stale version of the cpu_active_map.
+ * using stale version of the cpu_active_mask.
* This is not strictly necessary becuase stop_machine()
* that we run down the line already provides the required
* synchronization. But it's really a side effect and we do not
@@ -368,7 +346,7 @@ out_notify:
int __cpuinit cpu_up(unsigned int cpu)
{
int err = 0;
- if (!cpu_isset(cpu, cpu_possible_map)) {
+ if (!cpu_possible(cpu)) {
printk(KERN_ERR "can't online cpu %d because it is not "
"configured as may-hotadd at boot time\n", cpu);
#if defined(CONFIG_IA64) || defined(CONFIG_X86_64)
@@ -393,25 +371,25 @@ out:
}
#ifdef CONFIG_PM_SLEEP_SMP
-static cpumask_t frozen_cpus;
+static cpumask_var_t frozen_cpus;
int disable_nonboot_cpus(void)
{
int cpu, first_cpu, error = 0;
cpu_maps_update_begin();
- first_cpu = first_cpu(cpu_online_map);
+ first_cpu = cpumask_first(cpu_online_mask);
/* We take down all of the non-boot CPUs in one shot to avoid races
* with the userspace trying to use the CPU hotplug at the same time
*/
- cpus_clear(frozen_cpus);
+ cpumask_clear(frozen_cpus);
printk("Disabling non-boot CPUs ...\n");
for_each_online_cpu(cpu) {
if (cpu == first_cpu)
continue;
error = _cpu_down(cpu, 1);
if (!error) {
- cpu_set(cpu, frozen_cpus);
+ cpumask_set_cpu(cpu, frozen_cpus);
printk("CPU%d is down\n", cpu);
} else {
printk(KERN_ERR "Error taking CPU%d down: %d\n",
@@ -437,11 +415,11 @@ void __ref enable_nonboot_cpus(void)
/* Allow everyone to use the CPU hotplug again */
cpu_maps_update_begin();
cpu_hotplug_disabled = 0;
- if (cpus_empty(frozen_cpus))
+ if (cpumask_empty(frozen_cpus))
goto out;
printk("Enabling non-boot CPUs ...\n");
- for_each_cpu_mask_nr(cpu, frozen_cpus) {
+ for_each_cpu(cpu, frozen_cpus) {
error = _cpu_up(cpu, 1);
if (!error) {
printk("CPU%d is up\n", cpu);
@@ -449,10 +427,18 @@ void __ref enable_nonboot_cpus(void)
}
printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
}
- cpus_clear(frozen_cpus);
+ cpumask_clear(frozen_cpus);
out:
cpu_maps_update_done();
}
+
+static int alloc_frozen_cpus(void)
+{
+ if (!alloc_cpumask_var(&frozen_cpus, GFP_KERNEL|__GFP_ZERO))
+ return -ENOMEM;
+ return 0;
+}
+core_initcall(alloc_frozen_cpus);
#endif /* CONFIG_PM_SLEEP_SMP */
/**
@@ -468,7 +454,7 @@ void __cpuinit notify_cpu_starting(unsigned int cpu)
unsigned long val = CPU_STARTING;
#ifdef CONFIG_PM_SLEEP_SMP
- if (cpu_isset(cpu, frozen_cpus))
+ if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus))
val = CPU_STARTING_FROZEN;
#endif /* CONFIG_PM_SLEEP_SMP */
raw_notifier_call_chain(&cpu_chain, val, (void *)(long)cpu);
@@ -480,7 +466,7 @@ void __cpuinit notify_cpu_starting(unsigned int cpu)
* cpu_bit_bitmap[] is a special, "compressed" data structure that
* represents all NR_CPUS bits binary values of 1<<nr.
*
- * It is used by cpumask_of_cpu() to get a constant address to a CPU
+ * It is used by cpumask_of() to get a constant address to a CPU
* mask value that has a single bit set only.
*/
@@ -503,3 +489,71 @@ EXPORT_SYMBOL_GPL(cpu_bit_bitmap);
const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
EXPORT_SYMBOL(cpu_all_bits);
+
+#ifdef CONFIG_INIT_ALL_POSSIBLE
+static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
+ = CPU_BITS_ALL;
+#else
+static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
+#endif
+const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
+EXPORT_SYMBOL(cpu_possible_mask);
+
+static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
+EXPORT_SYMBOL(cpu_online_mask);
+
+static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
+EXPORT_SYMBOL(cpu_present_mask);
+
+static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
+const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
+EXPORT_SYMBOL(cpu_active_mask);
+
+void set_cpu_possible(unsigned int cpu, bool possible)
+{
+ if (possible)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits));
+}
+
+void set_cpu_present(unsigned int cpu, bool present)
+{
+ if (present)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits));
+}
+
+void set_cpu_online(unsigned int cpu, bool online)
+{
+ if (online)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
+}
+
+void set_cpu_active(unsigned int cpu, bool active)
+{
+ if (active)
+ cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
+ else
+ cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
+}
+
+void init_cpu_present(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_present_bits), src);
+}
+
+void init_cpu_possible(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_possible_bits), src);
+}
+
+void init_cpu_online(const struct cpumask *src)
+{
+ cpumask_copy(to_cpumask(cpu_online_bits), src);
+}
diff --git a/kernel/cred-internals.h b/kernel/cred-internals.h
new file mode 100644
index 000000000000..2dc4fc2d0bf1
--- /dev/null
+++ b/kernel/cred-internals.h
@@ -0,0 +1,21 @@
+/* Internal credentials stuff
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+/*
+ * user.c
+ */
+static inline void sched_switch_user(struct task_struct *p)
+{
+#ifdef CONFIG_USER_SCHED
+ sched_move_task(p);
+#endif /* CONFIG_USER_SCHED */
+}
+
diff --git a/kernel/cred.c b/kernel/cred.c
new file mode 100644
index 000000000000..ff7bc071991c
--- /dev/null
+++ b/kernel/cred.c
@@ -0,0 +1,588 @@
+/* Task credentials management - see Documentation/credentials.txt
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/init_task.h>
+#include <linux/security.h>
+#include <linux/cn_proc.h>
+#include "cred-internals.h"
+
+static struct kmem_cache *cred_jar;
+
+/*
+ * The common credentials for the initial task's thread group
+ */
+#ifdef CONFIG_KEYS
+static struct thread_group_cred init_tgcred = {
+ .usage = ATOMIC_INIT(2),
+ .tgid = 0,
+ .lock = SPIN_LOCK_UNLOCKED,
+};
+#endif
+
+/*
+ * The initial credentials for the initial task
+ */
+struct cred init_cred = {
+ .usage = ATOMIC_INIT(4),
+ .securebits = SECUREBITS_DEFAULT,
+ .cap_inheritable = CAP_INIT_INH_SET,
+ .cap_permitted = CAP_FULL_SET,
+ .cap_effective = CAP_INIT_EFF_SET,
+ .cap_bset = CAP_INIT_BSET,
+ .user = INIT_USER,
+ .group_info = &init_groups,
+#ifdef CONFIG_KEYS
+ .tgcred = &init_tgcred,
+#endif
+};
+
+/*
+ * Dispose of the shared task group credentials
+ */
+#ifdef CONFIG_KEYS
+static void release_tgcred_rcu(struct rcu_head *rcu)
+{
+ struct thread_group_cred *tgcred =
+ container_of(rcu, struct thread_group_cred, rcu);
+
+ BUG_ON(atomic_read(&tgcred->usage) != 0);
+
+ key_put(tgcred->session_keyring);
+ key_put(tgcred->process_keyring);
+ kfree(tgcred);
+}
+#endif
+
+/*
+ * Release a set of thread group credentials.
+ */
+static void release_tgcred(struct cred *cred)
+{
+#ifdef CONFIG_KEYS
+ struct thread_group_cred *tgcred = cred->tgcred;
+
+ if (atomic_dec_and_test(&tgcred->usage))
+ call_rcu(&tgcred->rcu, release_tgcred_rcu);
+#endif
+}
+
+/*
+ * The RCU callback to actually dispose of a set of credentials
+ */
+static void put_cred_rcu(struct rcu_head *rcu)
+{
+ struct cred *cred = container_of(rcu, struct cred, rcu);
+
+ if (atomic_read(&cred->usage) != 0)
+ panic("CRED: put_cred_rcu() sees %p with usage %d\n",
+ cred, atomic_read(&cred->usage));
+
+ security_cred_free(cred);
+ key_put(cred->thread_keyring);
+ key_put(cred->request_key_auth);
+ release_tgcred(cred);
+ put_group_info(cred->group_info);
+ free_uid(cred->user);
+ kmem_cache_free(cred_jar, cred);
+}
+
+/**
+ * __put_cred - Destroy a set of credentials
+ * @cred: The record to release
+ *
+ * Destroy a set of credentials on which no references remain.
+ */
+void __put_cred(struct cred *cred)
+{
+ BUG_ON(atomic_read(&cred->usage) != 0);
+
+ call_rcu(&cred->rcu, put_cred_rcu);
+}
+EXPORT_SYMBOL(__put_cred);
+
+/**
+ * prepare_creds - Prepare a new set of credentials for modification
+ *
+ * Prepare a new set of task credentials for modification. A task's creds
+ * shouldn't generally be modified directly, therefore this function is used to
+ * prepare a new copy, which the caller then modifies and then commits by
+ * calling commit_creds().
+ *
+ * Preparation involves making a copy of the objective creds for modification.
+ *
+ * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
+ *
+ * Call commit_creds() or abort_creds() to clean up.
+ */
+struct cred *prepare_creds(void)
+{
+ struct task_struct *task = current;
+ const struct cred *old;
+ struct cred *new;
+
+ BUG_ON(atomic_read(&task->real_cred->usage) < 1);
+
+ new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ old = task->cred;
+ memcpy(new, old, sizeof(struct cred));
+
+ atomic_set(&new->usage, 1);
+ get_group_info(new->group_info);
+ get_uid(new->user);
+
+#ifdef CONFIG_KEYS
+ key_get(new->thread_keyring);
+ key_get(new->request_key_auth);
+ atomic_inc(&new->tgcred->usage);
+#endif
+
+#ifdef CONFIG_SECURITY
+ new->security = NULL;
+#endif
+
+ if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+ goto error;
+ return new;
+
+error:
+ abort_creds(new);
+ return NULL;
+}
+EXPORT_SYMBOL(prepare_creds);
+
+/*
+ * Prepare credentials for current to perform an execve()
+ * - The caller must hold current->cred_exec_mutex
+ */
+struct cred *prepare_exec_creds(void)
+{
+ struct thread_group_cred *tgcred = NULL;
+ struct cred *new;
+
+#ifdef CONFIG_KEYS
+ tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+ if (!tgcred)
+ return NULL;
+#endif
+
+ new = prepare_creds();
+ if (!new) {
+ kfree(tgcred);
+ return new;
+ }
+
+#ifdef CONFIG_KEYS
+ /* newly exec'd tasks don't get a thread keyring */
+ key_put(new->thread_keyring);
+ new->thread_keyring = NULL;
+
+ /* create a new per-thread-group creds for all this set of threads to
+ * share */
+ memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
+
+ atomic_set(&tgcred->usage, 1);
+ spin_lock_init(&tgcred->lock);
+
+ /* inherit the session keyring; new process keyring */
+ key_get(tgcred->session_keyring);
+ tgcred->process_keyring = NULL;
+
+ release_tgcred(new);
+ new->tgcred = tgcred;
+#endif
+
+ return new;
+}
+
+/*
+ * prepare new credentials for the usermode helper dispatcher
+ */
+struct cred *prepare_usermodehelper_creds(void)
+{
+#ifdef CONFIG_KEYS
+ struct thread_group_cred *tgcred = NULL;
+#endif
+ struct cred *new;
+
+#ifdef CONFIG_KEYS
+ tgcred = kzalloc(sizeof(*new->tgcred), GFP_ATOMIC);
+ if (!tgcred)
+ return NULL;
+#endif
+
+ new = kmem_cache_alloc(cred_jar, GFP_ATOMIC);
+ if (!new)
+ return NULL;
+
+ memcpy(new, &init_cred, sizeof(struct cred));
+
+ atomic_set(&new->usage, 1);
+ get_group_info(new->group_info);
+ get_uid(new->user);
+
+#ifdef CONFIG_KEYS
+ new->thread_keyring = NULL;
+ new->request_key_auth = NULL;
+ new->jit_keyring = KEY_REQKEY_DEFL_DEFAULT;
+
+ atomic_set(&tgcred->usage, 1);
+ spin_lock_init(&tgcred->lock);
+ new->tgcred = tgcred;
+#endif
+
+#ifdef CONFIG_SECURITY
+ new->security = NULL;
+#endif
+ if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
+ goto error;
+
+ BUG_ON(atomic_read(&new->usage) != 1);
+ return new;
+
+error:
+ put_cred(new);
+ return NULL;
+}
+
+/*
+ * Copy credentials for the new process created by fork()
+ *
+ * We share if we can, but under some circumstances we have to generate a new
+ * set.
+ *
+ * The new process gets the current process's subjective credentials as its
+ * objective and subjective credentials
+ */
+int copy_creds(struct task_struct *p, unsigned long clone_flags)
+{
+#ifdef CONFIG_KEYS
+ struct thread_group_cred *tgcred;
+#endif
+ struct cred *new;
+ int ret;
+
+ mutex_init(&p->cred_exec_mutex);
+
+ if (
+#ifdef CONFIG_KEYS
+ !p->cred->thread_keyring &&
+#endif
+ clone_flags & CLONE_THREAD
+ ) {
+ p->real_cred = get_cred(p->cred);
+ get_cred(p->cred);
+ atomic_inc(&p->cred->user->processes);
+ return 0;
+ }
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ if (clone_flags & CLONE_NEWUSER) {
+ ret = create_user_ns(new);
+ if (ret < 0)
+ goto error_put;
+ }
+
+#ifdef CONFIG_KEYS
+ /* new threads get their own thread keyrings if their parent already
+ * had one */
+ if (new->thread_keyring) {
+ key_put(new->thread_keyring);
+ new->thread_keyring = NULL;
+ if (clone_flags & CLONE_THREAD)
+ install_thread_keyring_to_cred(new);
+ }
+
+ /* we share the process and session keyrings between all the threads in
+ * a process - this is slightly icky as we violate COW credentials a
+ * bit */
+ if (!(clone_flags & CLONE_THREAD)) {
+ tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
+ if (!tgcred) {
+ ret = -ENOMEM;
+ goto error_put;
+ }
+ atomic_set(&tgcred->usage, 1);
+ spin_lock_init(&tgcred->lock);
+ tgcred->process_keyring = NULL;
+ tgcred->session_keyring = key_get(new->tgcred->session_keyring);
+
+ release_tgcred(new);
+ new->tgcred = tgcred;
+ }
+#endif
+
+ atomic_inc(&new->user->processes);
+ p->cred = p->real_cred = get_cred(new);
+ return 0;
+
+error_put:
+ put_cred(new);
+ return ret;
+}
+
+/**
+ * commit_creds - Install new credentials upon the current task
+ * @new: The credentials to be assigned
+ *
+ * Install a new set of credentials to the current task, using RCU to replace
+ * the old set. Both the objective and the subjective credentials pointers are
+ * updated. This function may not be called if the subjective credentials are
+ * in an overridden state.
+ *
+ * This function eats the caller's reference to the new credentials.
+ *
+ * Always returns 0 thus allowing this function to be tail-called at the end
+ * of, say, sys_setgid().
+ */
+int commit_creds(struct cred *new)
+{
+ struct task_struct *task = current;
+ const struct cred *old;
+
+ BUG_ON(task->cred != task->real_cred);
+ BUG_ON(atomic_read(&task->real_cred->usage) < 2);
+ BUG_ON(atomic_read(&new->usage) < 1);
+
+ old = task->real_cred;
+ security_commit_creds(new, old);
+
+ get_cred(new); /* we will require a ref for the subj creds too */
+
+ /* dumpability changes */
+ if (old->euid != new->euid ||
+ old->egid != new->egid ||
+ old->fsuid != new->fsuid ||
+ old->fsgid != new->fsgid ||
+ !cap_issubset(new->cap_permitted, old->cap_permitted)) {
+ set_dumpable(task->mm, suid_dumpable);
+ task->pdeath_signal = 0;
+ smp_wmb();
+ }
+
+ /* alter the thread keyring */
+ if (new->fsuid != old->fsuid)
+ key_fsuid_changed(task);
+ if (new->fsgid != old->fsgid)
+ key_fsgid_changed(task);
+
+ /* do it
+ * - What if a process setreuid()'s and this brings the
+ * new uid over his NPROC rlimit? We can check this now
+ * cheaply with the new uid cache, so if it matters
+ * we should be checking for it. -DaveM
+ */
+ if (new->user != old->user)
+ atomic_inc(&new->user->processes);
+ rcu_assign_pointer(task->real_cred, new);
+ rcu_assign_pointer(task->cred, new);
+ if (new->user != old->user)
+ atomic_dec(&old->user->processes);
+
+ sched_switch_user(task);
+
+ /* send notifications */
+ if (new->uid != old->uid ||
+ new->euid != old->euid ||
+ new->suid != old->suid ||
+ new->fsuid != old->fsuid)
+ proc_id_connector(task, PROC_EVENT_UID);
+
+ if (new->gid != old->gid ||
+ new->egid != old->egid ||
+ new->sgid != old->sgid ||
+ new->fsgid != old->fsgid)
+ proc_id_connector(task, PROC_EVENT_GID);
+
+ /* release the old obj and subj refs both */
+ put_cred(old);
+ put_cred(old);
+ return 0;
+}
+EXPORT_SYMBOL(commit_creds);
+
+/**
+ * abort_creds - Discard a set of credentials and unlock the current task
+ * @new: The credentials that were going to be applied
+ *
+ * Discard a set of credentials that were under construction and unlock the
+ * current task.
+ */
+void abort_creds(struct cred *new)
+{
+ BUG_ON(atomic_read(&new->usage) < 1);
+ put_cred(new);
+}
+EXPORT_SYMBOL(abort_creds);
+
+/**
+ * override_creds - Override the current process's subjective credentials
+ * @new: The credentials to be assigned
+ *
+ * Install a set of temporary override subjective credentials on the current
+ * process, returning the old set for later reversion.
+ */
+const struct cred *override_creds(const struct cred *new)
+{
+ const struct cred *old = current->cred;
+
+ rcu_assign_pointer(current->cred, get_cred(new));
+ return old;
+}
+EXPORT_SYMBOL(override_creds);
+
+/**
+ * revert_creds - Revert a temporary subjective credentials override
+ * @old: The credentials to be restored
+ *
+ * Revert a temporary set of override subjective credentials to an old set,
+ * discarding the override set.
+ */
+void revert_creds(const struct cred *old)
+{
+ const struct cred *override = current->cred;
+
+ rcu_assign_pointer(current->cred, old);
+ put_cred(override);
+}
+EXPORT_SYMBOL(revert_creds);
+
+/*
+ * initialise the credentials stuff
+ */
+void __init cred_init(void)
+{
+ /* allocate a slab in which we can store credentials */
+ cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+}
+
+/**
+ * prepare_kernel_cred - Prepare a set of credentials for a kernel service
+ * @daemon: A userspace daemon to be used as a reference
+ *
+ * Prepare a set of credentials for a kernel service. This can then be used to
+ * override a task's own credentials so that work can be done on behalf of that
+ * task that requires a different subjective context.
+ *
+ * @daemon is used to provide a base for the security record, but can be NULL.
+ * If @daemon is supplied, then the security data will be derived from that;
+ * otherwise they'll be set to 0 and no groups, full capabilities and no keys.
+ *
+ * The caller may change these controls afterwards if desired.
+ *
+ * Returns the new credentials or NULL if out of memory.
+ *
+ * Does not take, and does not return holding current->cred_replace_mutex.
+ */
+struct cred *prepare_kernel_cred(struct task_struct *daemon)
+{
+ const struct cred *old;
+ struct cred *new;
+
+ new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ if (daemon)
+ old = get_task_cred(daemon);
+ else
+ old = get_cred(&init_cred);
+
+ get_uid(new->user);
+ get_group_info(new->group_info);
+
+#ifdef CONFIG_KEYS
+ atomic_inc(&init_tgcred.usage);
+ new->tgcred = &init_tgcred;
+ new->request_key_auth = NULL;
+ new->thread_keyring = NULL;
+ new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+#endif
+
+#ifdef CONFIG_SECURITY
+ new->security = NULL;
+#endif
+ if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
+ goto error;
+
+ atomic_set(&new->usage, 1);
+ put_cred(old);
+ return new;
+
+error:
+ put_cred(new);
+ return NULL;
+}
+EXPORT_SYMBOL(prepare_kernel_cred);
+
+/**
+ * set_security_override - Set the security ID in a set of credentials
+ * @new: The credentials to alter
+ * @secid: The LSM security ID to set
+ *
+ * Set the LSM security ID in a set of credentials so that the subjective
+ * security is overridden when an alternative set of credentials is used.
+ */
+int set_security_override(struct cred *new, u32 secid)
+{
+ return security_kernel_act_as(new, secid);
+}
+EXPORT_SYMBOL(set_security_override);
+
+/**
+ * set_security_override_from_ctx - Set the security ID in a set of credentials
+ * @new: The credentials to alter
+ * @secctx: The LSM security context to generate the security ID from.
+ *
+ * Set the LSM security ID in a set of credentials so that the subjective
+ * security is overridden when an alternative set of credentials is used. The
+ * security ID is specified in string form as a security context to be
+ * interpreted by the LSM.
+ */
+int set_security_override_from_ctx(struct cred *new, const char *secctx)
+{
+ u32 secid;
+ int ret;
+
+ ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
+ if (ret < 0)
+ return ret;
+
+ return set_security_override(new, secid);
+}
+EXPORT_SYMBOL(set_security_override_from_ctx);
+
+/**
+ * set_create_files_as - Set the LSM file create context in a set of credentials
+ * @new: The credentials to alter
+ * @inode: The inode to take the context from
+ *
+ * Change the LSM file creation context in a set of credentials to be the same
+ * as the object context of the specified inode, so that the new inodes have
+ * the same MAC context as that inode.
+ */
+int set_create_files_as(struct cred *new, struct inode *inode)
+{
+ new->fsuid = inode->i_uid;
+ new->fsgid = inode->i_gid;
+ return security_kernel_create_files_as(new, inode);
+}
+EXPORT_SYMBOL(set_create_files_as);
diff --git a/kernel/delayacct.c b/kernel/delayacct.c
index b3179dad71be..abb6e17505e2 100644
--- a/kernel/delayacct.c
+++ b/kernel/delayacct.c
@@ -127,7 +127,7 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk)
*/
t1 = tsk->sched_info.pcount;
t2 = tsk->sched_info.run_delay;
- t3 = tsk->sched_info.cpu_time;
+ t3 = tsk->se.sum_exec_runtime;
d->cpu_count += t1;
diff --git a/kernel/exit.c b/kernel/exit.c
index 61ba5b4b10cf..c9e5a1c14e08 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -46,12 +46,14 @@
#include <linux/blkdev.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/tracehook.h>
+#include <linux/init_task.h>
#include <trace/sched.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/pgtable.h>
#include <asm/mmu_context.h>
+#include "cred-internals.h"
DEFINE_TRACE(sched_process_free);
DEFINE_TRACE(sched_process_exit);
@@ -168,7 +170,10 @@ void release_task(struct task_struct * p)
int zap_leader;
repeat:
tracehook_prepare_release_task(p);
- atomic_dec(&p->user->processes);
+ /* don't need to get the RCU readlock here - the process is dead and
+ * can't be modifying its own credentials */
+ atomic_dec(&__task_cred(p)->user->processes);
+
proc_flush_task(p);
write_lock_irq(&tasklist_lock);
tracehook_finish_release_task(p);
@@ -343,12 +348,12 @@ static void reparent_to_kthreadd(void)
/* cpus_allowed? */
/* rt_priority? */
/* signals? */
- security_task_reparent_to_init(current);
memcpy(current->signal->rlim, init_task.signal->rlim,
sizeof(current->signal->rlim));
- atomic_inc(&(INIT_USER->__count));
+
+ atomic_inc(&init_cred.usage);
+ commit_creds(&init_cred);
write_unlock_irq(&tasklist_lock);
- switch_uid(INIT_USER);
}
void __set_special_pids(struct pid *pid)
@@ -1032,8 +1037,6 @@ NORET_TYPE void do_exit(long code)
* task into the wait for ever nirwana as well.
*/
tsk->flags |= PF_EXITPIDONE;
- if (tsk->io_context)
- exit_io_context();
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
}
@@ -1082,7 +1085,6 @@ NORET_TYPE void do_exit(long code)
check_stack_usage();
exit_thread();
cgroup_exit(tsk, 1);
- exit_keys(tsk);
if (group_dead && tsk->signal->leader)
disassociate_ctty(1);
@@ -1266,12 +1268,12 @@ static int wait_task_zombie(struct task_struct *p, int options,
unsigned long state;
int retval, status, traced;
pid_t pid = task_pid_vnr(p);
+ uid_t uid = __task_cred(p)->uid;
if (!likely(options & WEXITED))
return 0;
if (unlikely(options & WNOWAIT)) {
- uid_t uid = p->uid;
int exit_code = p->exit_code;
int why, status;
@@ -1392,7 +1394,7 @@ static int wait_task_zombie(struct task_struct *p, int options,
if (!retval && infop)
retval = put_user(pid, &infop->si_pid);
if (!retval && infop)
- retval = put_user(p->uid, &infop->si_uid);
+ retval = put_user(uid, &infop->si_uid);
if (!retval)
retval = pid;
@@ -1457,7 +1459,8 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
if (!unlikely(options & WNOWAIT))
p->exit_code = 0;
- uid = p->uid;
+ /* don't need the RCU readlock here as we're holding a spinlock */
+ uid = __task_cred(p)->uid;
unlock_sig:
spin_unlock_irq(&p->sighand->siglock);
if (!exit_code)
@@ -1531,10 +1534,10 @@ static int wait_task_continued(struct task_struct *p, int options,
}
if (!unlikely(options & WNOWAIT))
p->signal->flags &= ~SIGNAL_STOP_CONTINUED;
+ uid = __task_cred(p)->uid;
spin_unlock_irq(&p->sighand->siglock);
pid = task_pid_vnr(p);
- uid = p->uid;
get_task_struct(p);
read_unlock(&tasklist_lock);
diff --git a/kernel/fork.c b/kernel/fork.c
index 7b93da72d4a2..43cbf30669e6 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -151,9 +151,8 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current);
- security_task_free(tsk);
- free_uid(tsk->user);
- put_group_info(tsk->group_info);
+ put_cred(tsk->real_cred);
+ put_cred(tsk->cred);
delayacct_tsk_free(tsk);
if (!profile_handoff_task(tsk))
@@ -416,8 +415,8 @@ static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p)
set_mm_counter(mm, file_rss, 0);
set_mm_counter(mm, anon_rss, 0);
spin_lock_init(&mm->page_table_lock);
- rwlock_init(&mm->ioctx_list_lock);
- mm->ioctx_list = NULL;
+ spin_lock_init(&mm->ioctx_lock);
+ INIT_HLIST_HEAD(&mm->ioctx_list);
mm->free_area_cache = TASK_UNMAPPED_BASE;
mm->cached_hole_size = ~0UL;
mm_init_owner(mm, p);
@@ -822,12 +821,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
if (!sig)
return -ENOMEM;
- ret = copy_thread_group_keys(tsk);
- if (ret < 0) {
- kmem_cache_free(signal_cachep, sig);
- return ret;
- }
-
atomic_set(&sig->count, 1);
atomic_set(&sig->live, 1);
init_waitqueue_head(&sig->wait_chldexit);
@@ -872,7 +865,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
void __cleanup_signal(struct signal_struct *sig)
{
thread_group_cputime_free(sig);
- exit_thread_group_keys(sig);
tty_kref_put(sig->tty);
kmem_cache_free(signal_cachep, sig);
}
@@ -988,16 +980,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
retval = -EAGAIN;
- if (atomic_read(&p->user->processes) >=
+ if (atomic_read(&p->real_cred->user->processes) >=
p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
- p->user != current->nsproxy->user_ns->root_user)
+ p->real_cred->user != INIT_USER)
goto bad_fork_free;
}
- atomic_inc(&p->user->__count);
- atomic_inc(&p->user->processes);
- get_group_info(p->group_info);
+ retval = copy_creds(p, clone_flags);
+ if (retval < 0)
+ goto bad_fork_free;
/*
* If multiple threads are within copy_process(), then this check
@@ -1052,10 +1044,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
do_posix_clock_monotonic_gettime(&p->start_time);
p->real_start_time = p->start_time;
monotonic_to_bootbased(&p->real_start_time);
-#ifdef CONFIG_SECURITY
- p->security = NULL;
-#endif
- p->cap_bset = current->cap_bset;
p->io_context = NULL;
p->audit_context = NULL;
cgroup_fork(p);
@@ -1096,14 +1084,14 @@ static struct task_struct *copy_process(unsigned long clone_flags,
#ifdef CONFIG_DEBUG_MUTEXES
p->blocked_on = NULL; /* not blocked yet */
#endif
+ if (unlikely(ptrace_reparented(current)))
+ ptrace_fork(p, clone_flags);
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, clone_flags);
- if ((retval = security_task_alloc(p)))
- goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
- goto bad_fork_cleanup_security;
+ goto bad_fork_cleanup_policy;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
@@ -1117,10 +1105,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p)))
goto bad_fork_cleanup_signal;
- if ((retval = copy_keys(clone_flags, p)))
- goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p)))
- goto bad_fork_cleanup_keys;
+ goto bad_fork_cleanup_mm;
if ((retval = copy_io(clone_flags, p)))
goto bad_fork_cleanup_namespaces;
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
@@ -1289,8 +1275,6 @@ bad_fork_cleanup_io:
put_io_context(p->io_context);
bad_fork_cleanup_namespaces:
exit_task_namespaces(p);
-bad_fork_cleanup_keys:
- exit_keys(p);
bad_fork_cleanup_mm:
if (p->mm)
mmput(p->mm);
@@ -1306,8 +1290,6 @@ bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
-bad_fork_cleanup_security:
- security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
mpol_put(p->mempolicy);
@@ -1320,9 +1302,9 @@ bad_fork_cleanup_cgroup:
bad_fork_cleanup_put_domain:
module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
- put_group_info(p->group_info);
- atomic_dec(&p->user->processes);
- free_uid(p->user);
+ atomic_dec(&p->cred->user->processes);
+ put_cred(p->real_cred);
+ put_cred(p->cred);
bad_fork_free:
free_task(p);
fork_out:
@@ -1366,6 +1348,21 @@ long do_fork(unsigned long clone_flags,
long nr;
/*
+ * Do some preliminary argument and permissions checking before we
+ * actually start allocating stuff
+ */
+ if (clone_flags & CLONE_NEWUSER) {
+ if (clone_flags & CLONE_THREAD)
+ return -EINVAL;
+ /* hopefully this check will go away when userns support is
+ * complete
+ */
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
+ !capable(CAP_SETGID))
+ return -EPERM;
+ }
+
+ /*
* We hope to recycle these flags after 2.6.26
*/
if (unlikely(clone_flags & CLONE_STOPPED)) {
@@ -1613,8 +1610,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
err = -EINVAL;
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
- CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
- CLONE_NEWNET))
+ CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET))
goto bad_unshare_out;
/*
diff --git a/kernel/futex.c b/kernel/futex.c
index e10c5c8786a6..7c6cbabe52b3 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -92,11 +92,12 @@ struct futex_pi_state {
* A futex_q has a woken state, just like tasks have TASK_RUNNING.
* It is considered woken when plist_node_empty(&q->list) || q->lock_ptr == 0.
* The order of wakup is always to make the first condition true, then
- * wake up q->waiters, then make the second condition true.
+ * wake up q->waiter, then make the second condition true.
*/
struct futex_q {
struct plist_node list;
- wait_queue_head_t waiters;
+ /* There can only be a single waiter */
+ wait_queue_head_t waiter;
/* Which hash list lock to use: */
spinlock_t *lock_ptr;
@@ -405,13 +406,20 @@ static void free_pi_state(struct futex_pi_state *pi_state)
static struct task_struct * futex_find_get_task(pid_t pid)
{
struct task_struct *p;
+ const struct cred *cred = current_cred(), *pcred;
rcu_read_lock();
p = find_task_by_vpid(pid);
- if (!p || ((current->euid != p->euid) && (current->euid != p->uid)))
+ if (!p) {
p = ERR_PTR(-ESRCH);
- else
- get_task_struct(p);
+ } else {
+ pcred = __task_cred(p);
+ if (cred->euid != pcred->euid &&
+ cred->euid != pcred->uid)
+ p = ERR_PTR(-ESRCH);
+ else
+ get_task_struct(p);
+ }
rcu_read_unlock();
@@ -573,7 +581,7 @@ static void wake_futex(struct futex_q *q)
* The lock in wake_up_all() is a crucial memory barrier after the
* plist_del() and also before assigning to q->lock_ptr.
*/
- wake_up_all(&q->waiters);
+ wake_up(&q->waiter);
/*
* The waiting task can free the futex_q as soon as this is written,
* without taking any locks. This must come last.
@@ -930,7 +938,7 @@ static inline struct futex_hash_bucket *queue_lock(struct futex_q *q)
{
struct futex_hash_bucket *hb;
- init_waitqueue_head(&q->waiters);
+ init_waitqueue_head(&q->waiter);
get_futex_key_refs(&q->key);
hb = hash_futex(&q->key);
@@ -1142,12 +1150,13 @@ handle_fault:
* In case we must use restart_block to restart a futex_wait,
* we encode in the 'flags' shared capability
*/
-#define FLAGS_SHARED 1
+#define FLAGS_SHARED 0x01
+#define FLAGS_CLOCKRT 0x02
static long futex_wait_restart(struct restart_block *restart);
static int futex_wait(u32 __user *uaddr, int fshared,
- u32 val, ktime_t *abs_time, u32 bitset)
+ u32 val, ktime_t *abs_time, u32 bitset, int clockrt)
{
struct task_struct *curr = current;
DECLARE_WAITQUEUE(wait, curr);
@@ -1220,7 +1229,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
/* add_wait_queue is the barrier after __set_current_state. */
__set_current_state(TASK_INTERRUPTIBLE);
- add_wait_queue(&q.waiters, &wait);
+ add_wait_queue(&q.waiter, &wait);
/*
* !plist_node_empty() is safe here without any lock.
* q.lock_ptr != 0 is not safe, because of ordering against wakeup.
@@ -1233,8 +1242,10 @@ static int futex_wait(u32 __user *uaddr, int fshared,
slack = current->timer_slack_ns;
if (rt_task(current))
slack = 0;
- hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_ABS);
+ hrtimer_init_on_stack(&t.timer,
+ clockrt ? CLOCK_REALTIME :
+ CLOCK_MONOTONIC,
+ HRTIMER_MODE_ABS);
hrtimer_init_sleeper(&t, current);
hrtimer_set_expires_range_ns(&t.timer, *abs_time, slack);
@@ -1289,6 +1300,8 @@ static int futex_wait(u32 __user *uaddr, int fshared,
if (fshared)
restart->futex.flags |= FLAGS_SHARED;
+ if (clockrt)
+ restart->futex.flags |= FLAGS_CLOCKRT;
return -ERESTART_RESTARTBLOCK;
}
@@ -1312,7 +1325,8 @@ static long futex_wait_restart(struct restart_block *restart)
if (restart->futex.flags & FLAGS_SHARED)
fshared = 1;
return (long)futex_wait(uaddr, fshared, restart->futex.val, &t,
- restart->futex.bitset);
+ restart->futex.bitset,
+ restart->futex.flags & FLAGS_CLOCKRT);
}
@@ -1558,12 +1572,11 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
uaddr_faulted:
/*
- * We have to r/w *(int __user *)uaddr, but we can't modify it
- * non-atomically. Therefore, if get_user below is not
- * enough, we need to handle the fault ourselves, while
- * still holding the mmap_sem.
- *
- * ... and hb->lock. :-) --ANK
+ * We have to r/w *(int __user *)uaddr, and we have to modify it
+ * atomically. Therefore, if we continue to fault after get_user()
+ * below, we need to handle the fault ourselves, while still holding
+ * the mmap_sem. This can occur if the uaddr is under contention as
+ * we have to drop the mmap_sem in order to call get_user().
*/
queue_unlock(&q, hb);
@@ -1575,7 +1588,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
}
ret = get_user(uval, uaddr);
- if (!ret && (uval != -EFAULT))
+ if (!ret)
goto retry;
if (to)
@@ -1669,12 +1682,11 @@ out:
pi_faulted:
/*
- * We have to r/w *(int __user *)uaddr, but we can't modify it
- * non-atomically. Therefore, if get_user below is not
- * enough, we need to handle the fault ourselves, while
- * still holding the mmap_sem.
- *
- * ... and hb->lock. --ANK
+ * We have to r/w *(int __user *)uaddr, and we have to modify it
+ * atomically. Therefore, if we continue to fault after get_user()
+ * below, we need to handle the fault ourselves, while still holding
+ * the mmap_sem. This can occur if the uaddr is under contention as
+ * we have to drop the mmap_sem in order to call get_user().
*/
spin_unlock(&hb->lock);
@@ -1687,7 +1699,7 @@ pi_faulted:
}
ret = get_user(uval, uaddr);
- if (!ret && (uval != -EFAULT))
+ if (!ret)
goto retry;
return ret;
@@ -1742,6 +1754,7 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
{
struct robust_list_head __user *head;
unsigned long ret;
+ const struct cred *cred = current_cred(), *pcred;
if (!futex_cmpxchg_enabled)
return -ENOSYS;
@@ -1757,8 +1770,10 @@ sys_get_robust_list(int pid, struct robust_list_head __user * __user *head_ptr,
if (!p)
goto err_unlock;
ret = -EPERM;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
- !capable(CAP_SYS_PTRACE))
+ pcred = __task_cred(p);
+ if (cred->euid != pcred->euid &&
+ cred->euid != pcred->uid &&
+ !capable(CAP_SYS_PTRACE))
goto err_unlock;
head = p->robust_list;
rcu_read_unlock();
@@ -1905,18 +1920,22 @@ void exit_robust_list(struct task_struct *curr)
long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
u32 __user *uaddr2, u32 val2, u32 val3)
{
- int ret = -ENOSYS;
+ int clockrt, ret = -ENOSYS;
int cmd = op & FUTEX_CMD_MASK;
int fshared = 0;
if (!(op & FUTEX_PRIVATE_FLAG))
fshared = 1;
+ clockrt = op & FUTEX_CLOCK_REALTIME;
+ if (clockrt && cmd != FUTEX_WAIT_BITSET)
+ return -ENOSYS;
+
switch (cmd) {
case FUTEX_WAIT:
val3 = FUTEX_BITSET_MATCH_ANY;
case FUTEX_WAIT_BITSET:
- ret = futex_wait(uaddr, fshared, val, timeout, val3);
+ ret = futex_wait(uaddr, fshared, val, timeout, val3, clockrt);
break;
case FUTEX_WAKE:
val3 = FUTEX_BITSET_MATCH_ANY;
diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c
index 04ac3a9e42cf..d607a5b9ee29 100644
--- a/kernel/futex_compat.c
+++ b/kernel/futex_compat.c
@@ -135,6 +135,7 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
{
struct compat_robust_list_head __user *head;
unsigned long ret;
+ const struct cred *cred = current_cred(), *pcred;
if (!futex_cmpxchg_enabled)
return -ENOSYS;
@@ -150,8 +151,10 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr,
if (!p)
goto err_unlock;
ret = -EPERM;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
- !capable(CAP_SYS_PTRACE))
+ pcred = __task_cred(p);
+ if (cred->euid != pcred->euid &&
+ cred->euid != pcred->uid &&
+ !capable(CAP_SYS_PTRACE))
goto err_unlock;
head = p->compat_robust_list;
read_unlock(&tasklist_lock);
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 47e63349d1b2..bda9cb924276 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -442,22 +442,6 @@ static inline void debug_hrtimer_activate(struct hrtimer *timer) { }
static inline void debug_hrtimer_deactivate(struct hrtimer *timer) { }
#endif
-/*
- * Check, whether the timer is on the callback pending list
- */
-static inline int hrtimer_cb_pending(const struct hrtimer *timer)
-{
- return timer->state & HRTIMER_STATE_PENDING;
-}
-
-/*
- * Remove a timer from the callback pending list
- */
-static inline void hrtimer_remove_cb_pending(struct hrtimer *timer)
-{
- list_del_init(&timer->cb_entry);
-}
-
/* High resolution timer related functions */
#ifdef CONFIG_HIGH_RES_TIMERS
@@ -651,6 +635,8 @@ static inline void hrtimer_init_timer_hres(struct hrtimer *timer)
{
}
+static void __run_hrtimer(struct hrtimer *timer);
+
/*
* When High resolution timers are active, try to reprogram. Note, that in case
* the state has HRTIMER_STATE_CALLBACK set, no reprogramming and no expiry
@@ -661,31 +647,14 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
struct hrtimer_clock_base *base)
{
if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) {
-
- /* Timer is expired, act upon the callback mode */
- switch(timer->cb_mode) {
- case HRTIMER_CB_IRQSAFE_PERCPU:
- case HRTIMER_CB_IRQSAFE_UNLOCKED:
- /*
- * This is solely for the sched tick emulation with
- * dynamic tick support to ensure that we do not
- * restart the tick right on the edge and end up with
- * the tick timer in the softirq ! The calling site
- * takes care of this. Also used for hrtimer sleeper !
- */
- debug_hrtimer_deactivate(timer);
- return 1;
- case HRTIMER_CB_SOFTIRQ:
- /*
- * Move everything else into the softirq pending list !
- */
- list_add_tail(&timer->cb_entry,
- &base->cpu_base->cb_pending);
- timer->state = HRTIMER_STATE_PENDING;
- return 1;
- default:
- BUG();
- }
+ /*
+ * XXX: recursion check?
+ * hrtimer_forward() should round up with timer granularity
+ * so that we never get into inf recursion here,
+ * it doesn't do that though
+ */
+ __run_hrtimer(timer);
+ return 1;
}
return 0;
}
@@ -724,11 +693,6 @@ static int hrtimer_switch_to_hres(void)
return 1;
}
-static inline void hrtimer_raise_softirq(void)
-{
- raise_softirq(HRTIMER_SOFTIRQ);
-}
-
#else
static inline int hrtimer_hres_active(void) { return 0; }
@@ -747,7 +711,6 @@ static inline int hrtimer_reprogram(struct hrtimer *timer,
{
return 0;
}
-static inline void hrtimer_raise_softirq(void) { }
#endif /* CONFIG_HIGH_RES_TIMERS */
@@ -890,10 +853,7 @@ static void __remove_hrtimer(struct hrtimer *timer,
struct hrtimer_clock_base *base,
unsigned long newstate, int reprogram)
{
- /* High res. callback list. NOP for !HIGHRES */
- if (hrtimer_cb_pending(timer))
- hrtimer_remove_cb_pending(timer);
- else {
+ if (timer->state & HRTIMER_STATE_ENQUEUED) {
/*
* Remove the timer from the rbtree and replace the
* first entry pointer if necessary.
@@ -953,7 +913,7 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n
{
struct hrtimer_clock_base *base, *new_base;
unsigned long flags;
- int ret, raise;
+ int ret;
base = lock_hrtimer_base(timer, &flags);
@@ -988,26 +948,8 @@ hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long delta_n
enqueue_hrtimer(timer, new_base,
new_base->cpu_base == &__get_cpu_var(hrtimer_bases));
- /*
- * The timer may be expired and moved to the cb_pending
- * list. We can not raise the softirq with base lock held due
- * to a possible deadlock with runqueue lock.
- */
- raise = timer->state == HRTIMER_STATE_PENDING;
-
- /*
- * We use preempt_disable to prevent this task from migrating after
- * setting up the softirq and raising it. Otherwise, if me migrate
- * we will raise the softirq on the wrong CPU.
- */
- preempt_disable();
-
unlock_hrtimer_base(timer, &flags);
- if (raise)
- hrtimer_raise_softirq();
- preempt_enable();
-
return ret;
}
EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
@@ -1192,75 +1134,6 @@ int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp)
}
EXPORT_SYMBOL_GPL(hrtimer_get_res);
-static void run_hrtimer_pending(struct hrtimer_cpu_base *cpu_base)
-{
- spin_lock_irq(&cpu_base->lock);
-
- while (!list_empty(&cpu_base->cb_pending)) {
- enum hrtimer_restart (*fn)(struct hrtimer *);
- struct hrtimer *timer;
- int restart;
- int emulate_hardirq_ctx = 0;
-
- timer = list_entry(cpu_base->cb_pending.next,
- struct hrtimer, cb_entry);
-
- debug_hrtimer_deactivate(timer);
- timer_stats_account_hrtimer(timer);
-
- fn = timer->function;
- /*
- * A timer might have been added to the cb_pending list
- * when it was migrated during a cpu-offline operation.
- * Emulate hardirq context for such timers.
- */
- if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU ||
- timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED)
- emulate_hardirq_ctx = 1;
-
- __remove_hrtimer(timer, timer->base, HRTIMER_STATE_CALLBACK, 0);
- spin_unlock_irq(&cpu_base->lock);
-
- if (unlikely(emulate_hardirq_ctx)) {
- local_irq_disable();
- restart = fn(timer);
- local_irq_enable();
- } else
- restart = fn(timer);
-
- spin_lock_irq(&cpu_base->lock);
-
- timer->state &= ~HRTIMER_STATE_CALLBACK;
- if (restart == HRTIMER_RESTART) {
- BUG_ON(hrtimer_active(timer));
- /*
- * Enqueue the timer, allow reprogramming of the event
- * device
- */
- enqueue_hrtimer(timer, timer->base, 1);
- } else if (hrtimer_active(timer)) {
- /*
- * If the timer was rearmed on another CPU, reprogram
- * the event device.
- */
- struct hrtimer_clock_base *base = timer->base;
-
- if (base->first == &timer->node &&
- hrtimer_reprogram(timer, base)) {
- /*
- * Timer is expired. Thus move it from tree to
- * pending list again.
- */
- __remove_hrtimer(timer, base,
- HRTIMER_STATE_PENDING, 0);
- list_add_tail(&timer->cb_entry,
- &base->cpu_base->cb_pending);
- }
- }
- }
- spin_unlock_irq(&cpu_base->lock);
-}
-
static void __run_hrtimer(struct hrtimer *timer)
{
struct hrtimer_clock_base *base = timer->base;
@@ -1268,25 +1141,21 @@ static void __run_hrtimer(struct hrtimer *timer)
enum hrtimer_restart (*fn)(struct hrtimer *);
int restart;
+ WARN_ON(!irqs_disabled());
+
debug_hrtimer_deactivate(timer);
__remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0);
timer_stats_account_hrtimer(timer);
-
fn = timer->function;
- if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU ||
- timer->cb_mode == HRTIMER_CB_IRQSAFE_UNLOCKED) {
- /*
- * Used for scheduler timers, avoid lock inversion with
- * rq->lock and tasklist_lock.
- *
- * These timers are required to deal with enqueue expiry
- * themselves and are not allowed to migrate.
- */
- spin_unlock(&cpu_base->lock);
- restart = fn(timer);
- spin_lock(&cpu_base->lock);
- } else
- restart = fn(timer);
+
+ /*
+ * Because we run timers from hardirq context, there is no chance
+ * they get migrated to another cpu, therefore its safe to unlock
+ * the timer base.
+ */
+ spin_unlock(&cpu_base->lock);
+ restart = fn(timer);
+ spin_lock(&cpu_base->lock);
/*
* Note: We clear the CALLBACK bit after enqueue_hrtimer to avoid
@@ -1311,7 +1180,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
struct hrtimer_clock_base *base;
ktime_t expires_next, now;
- int i, raise = 0;
+ int i;
BUG_ON(!cpu_base->hres_active);
cpu_base->nr_events++;
@@ -1360,16 +1229,6 @@ void hrtimer_interrupt(struct clock_event_device *dev)
break;
}
- /* Move softirq callbacks to the pending list */
- if (timer->cb_mode == HRTIMER_CB_SOFTIRQ) {
- __remove_hrtimer(timer, base,
- HRTIMER_STATE_PENDING, 0);
- list_add_tail(&timer->cb_entry,
- &base->cpu_base->cb_pending);
- raise = 1;
- continue;
- }
-
__run_hrtimer(timer);
}
spin_unlock(&cpu_base->lock);
@@ -1383,10 +1242,6 @@ void hrtimer_interrupt(struct clock_event_device *dev)
if (tick_program_event(expires_next, 0))
goto retry;
}
-
- /* Raise softirq ? */
- if (raise)
- raise_softirq(HRTIMER_SOFTIRQ);
}
/**
@@ -1413,11 +1268,6 @@ void hrtimer_peek_ahead_timers(void)
local_irq_restore(flags);
}
-static void run_hrtimer_softirq(struct softirq_action *h)
-{
- run_hrtimer_pending(&__get_cpu_var(hrtimer_bases));
-}
-
#endif /* CONFIG_HIGH_RES_TIMERS */
/*
@@ -1429,8 +1279,6 @@ static void run_hrtimer_softirq(struct softirq_action *h)
*/
void hrtimer_run_pending(void)
{
- struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
-
if (hrtimer_hres_active())
return;
@@ -1444,8 +1292,6 @@ void hrtimer_run_pending(void)
*/
if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
hrtimer_switch_to_hres();
-
- run_hrtimer_pending(cpu_base);
}
/*
@@ -1482,14 +1328,6 @@ void hrtimer_run_queues(void)
hrtimer_get_expires_tv64(timer))
break;
- if (timer->cb_mode == HRTIMER_CB_SOFTIRQ) {
- __remove_hrtimer(timer, base,
- HRTIMER_STATE_PENDING, 0);
- list_add_tail(&timer->cb_entry,
- &base->cpu_base->cb_pending);
- continue;
- }
-
__run_hrtimer(timer);
}
spin_unlock(&cpu_base->lock);
@@ -1516,9 +1354,6 @@ void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
{
sl->timer.function = hrtimer_wakeup;
sl->task = task;
-#ifdef CONFIG_HIGH_RES_TIMERS
- sl->timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
-#endif
}
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
@@ -1655,18 +1490,16 @@ static void __cpuinit init_hrtimers_cpu(int cpu)
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
cpu_base->clock_base[i].cpu_base = cpu_base;
- INIT_LIST_HEAD(&cpu_base->cb_pending);
hrtimer_init_hres(cpu_base);
}
#ifdef CONFIG_HOTPLUG_CPU
-static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
- struct hrtimer_clock_base *new_base, int dcpu)
+static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
+ struct hrtimer_clock_base *new_base)
{
struct hrtimer *timer;
struct rb_node *node;
- int raise = 0;
while ((node = rb_first(&old_base->active))) {
timer = rb_entry(node, struct hrtimer, node);
@@ -1674,18 +1507,6 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
debug_hrtimer_deactivate(timer);
/*
- * Should not happen. Per CPU timers should be
- * canceled _before_ the migration code is called
- */
- if (timer->cb_mode == HRTIMER_CB_IRQSAFE_PERCPU) {
- __remove_hrtimer(timer, old_base,
- HRTIMER_STATE_INACTIVE, 0);
- WARN(1, "hrtimer (%p %p)active but cpu %d dead\n",
- timer, timer->function, dcpu);
- continue;
- }
-
- /*
* Mark it as STATE_MIGRATE not INACTIVE otherwise the
* timer could be seen as !active and just vanish away
* under us on another CPU
@@ -1693,69 +1514,34 @@ static int migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
__remove_hrtimer(timer, old_base, HRTIMER_STATE_MIGRATE, 0);
timer->base = new_base;
/*
- * Enqueue the timer. Allow reprogramming of the event device
+ * Enqueue the timers on the new cpu, but do not reprogram
+ * the timer as that would enable a deadlock between
+ * hrtimer_enqueue_reprogramm() running the timer and us still
+ * holding a nested base lock.
+ *
+ * Instead we tickle the hrtimer interrupt after the migration
+ * is done, which will run all expired timers and re-programm
+ * the timer device.
*/
- enqueue_hrtimer(timer, new_base, 1);
+ enqueue_hrtimer(timer, new_base, 0);
-#ifdef CONFIG_HIGH_RES_TIMERS
- /*
- * Happens with high res enabled when the timer was
- * already expired and the callback mode is
- * HRTIMER_CB_IRQSAFE_UNLOCKED (hrtimer_sleeper). The
- * enqueue code does not move them to the soft irq
- * pending list for performance/latency reasons, but
- * in the migration state, we need to do that
- * otherwise we end up with a stale timer.
- */
- if (timer->state == HRTIMER_STATE_MIGRATE) {
- timer->state = HRTIMER_STATE_PENDING;
- list_add_tail(&timer->cb_entry,
- &new_base->cpu_base->cb_pending);
- raise = 1;
- }
-#endif
/* Clear the migration state bit */
timer->state &= ~HRTIMER_STATE_MIGRATE;
}
- return raise;
-}
-
-#ifdef CONFIG_HIGH_RES_TIMERS
-static int migrate_hrtimer_pending(struct hrtimer_cpu_base *old_base,
- struct hrtimer_cpu_base *new_base)
-{
- struct hrtimer *timer;
- int raise = 0;
-
- while (!list_empty(&old_base->cb_pending)) {
- timer = list_entry(old_base->cb_pending.next,
- struct hrtimer, cb_entry);
-
- __remove_hrtimer(timer, timer->base, HRTIMER_STATE_PENDING, 0);
- timer->base = &new_base->clock_base[timer->base->index];
- list_add_tail(&timer->cb_entry, &new_base->cb_pending);
- raise = 1;
- }
- return raise;
-}
-#else
-static int migrate_hrtimer_pending(struct hrtimer_cpu_base *old_base,
- struct hrtimer_cpu_base *new_base)
-{
- return 0;
}
-#endif
-static void migrate_hrtimers(int cpu)
+static int migrate_hrtimers(int scpu)
{
struct hrtimer_cpu_base *old_base, *new_base;
- int i, raise = 0;
+ int dcpu, i;
- BUG_ON(cpu_online(cpu));
- old_base = &per_cpu(hrtimer_bases, cpu);
+ BUG_ON(cpu_online(scpu));
+ old_base = &per_cpu(hrtimer_bases, scpu);
new_base = &get_cpu_var(hrtimer_bases);
- tick_cancel_sched_timer(cpu);
+ dcpu = smp_processor_id();
+
+ tick_cancel_sched_timer(scpu);
/*
* The caller is globally serialized and nobody else
* takes two locks at once, deadlock is not possible.
@@ -1764,41 +1550,47 @@ static void migrate_hrtimers(int cpu)
spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
- if (migrate_hrtimer_list(&old_base->clock_base[i],
- &new_base->clock_base[i], cpu))
- raise = 1;
+ migrate_hrtimer_list(&old_base->clock_base[i],
+ &new_base->clock_base[i]);
}
- if (migrate_hrtimer_pending(old_base, new_base))
- raise = 1;
-
spin_unlock(&old_base->lock);
spin_unlock_irq(&new_base->lock);
put_cpu_var(hrtimer_bases);
- if (raise)
- hrtimer_raise_softirq();
+ return dcpu;
+}
+
+static void tickle_timers(void *arg)
+{
+ hrtimer_peek_ahead_timers();
}
+
#endif /* CONFIG_HOTPLUG_CPU */
static int __cpuinit hrtimer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
- unsigned int cpu = (long)hcpu;
+ int scpu = (long)hcpu;
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
- init_hrtimers_cpu(cpu);
+ init_hrtimers_cpu(scpu);
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_DEAD:
case CPU_DEAD_FROZEN:
- clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &cpu);
- migrate_hrtimers(cpu);
+ {
+ int dcpu;
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_CPU_DEAD, &scpu);
+ dcpu = migrate_hrtimers(scpu);
+ smp_call_function_single(dcpu, tickle_timers, NULL, 0);
break;
+ }
#endif
default:
@@ -1817,9 +1609,6 @@ void __init hrtimers_init(void)
hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
register_cpu_notifier(&hrtimers_nb);
-#ifdef CONFIG_HIGH_RES_TIMERS
- open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
-#endif
}
/**
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index b343deedae91..f63c706d25e1 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -125,6 +125,7 @@ int set_irq_type(unsigned int irq, unsigned int type)
return -ENODEV;
}
+ type &= IRQ_TYPE_SENSE_MASK;
if (type == IRQ_TYPE_NONE)
return 0;
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index f1a23069c20a..6492400cb50d 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -422,11 +422,8 @@ out:
}
#endif
-
-#ifdef CONFIG_TRACE_IRQFLAGS
void early_init_irq_lock_class(void)
{
-#ifndef CONFIG_SPARSE_IRQ
struct irq_desc *desc;
int i;
@@ -436,9 +433,7 @@ void early_init_irq_lock_class(void)
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
}
-#endif
}
-#endif
#ifdef CONFIG_SPARSE_IRQ
unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 10ad2f87ed9a..cd0cd8dcb345 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -16,8 +16,15 @@
#include "internals.h"
#ifdef CONFIG_SMP
+cpumask_var_t irq_default_affinity;
-cpumask_t irq_default_affinity = CPU_MASK_ALL;
+static int init_irq_default_affinity(void)
+{
+ alloc_cpumask_var(&irq_default_affinity, GFP_KERNEL);
+ cpumask_setall(irq_default_affinity);
+ return 0;
+}
+core_initcall(init_irq_default_affinity);
/**
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
@@ -127,7 +134,7 @@ int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc)
desc->status &= ~IRQ_AFFINITY_SET;
}
- cpumask_and(&desc->affinity, cpu_online_mask, &irq_default_affinity);
+ cpumask_and(&desc->affinity, cpu_online_mask, irq_default_affinity);
set_affinity:
desc->chip->set_affinity(irq, &desc->affinity);
@@ -368,16 +375,18 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
return 0;
}
- ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK);
+ /* caller masked out all except trigger mode flags */
+ ret = chip->set_type(irq, flags);
if (ret)
pr_err("setting trigger mode %d for irq %u failed (%pF)\n",
- (int)(flags & IRQF_TRIGGER_MASK),
- irq, chip->set_type);
+ (int)flags, irq, chip->set_type);
else {
+ if (flags & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+ flags |= IRQ_LEVEL;
/* note that IRQF_TRIGGER_MASK == IRQ_TYPE_SENSE_MASK */
- desc->status &= ~IRQ_TYPE_SENSE_MASK;
- desc->status |= flags & IRQ_TYPE_SENSE_MASK;
+ desc->status &= ~(IRQ_LEVEL | IRQ_TYPE_SENSE_MASK);
+ desc->status |= flags;
}
return ret;
@@ -457,7 +466,8 @@ __setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new)
/* Setup the type (level, edge polarity) if configured: */
if (new->flags & IRQF_TRIGGER_MASK) {
- ret = __irq_set_trigger(desc, irq, new->flags);
+ ret = __irq_set_trigger(desc, irq,
+ new->flags & IRQF_TRIGGER_MASK);
if (ret) {
spin_unlock_irqrestore(&desc->lock, flags);
@@ -671,6 +681,18 @@ int request_irq(unsigned int irq, irq_handler_t handler,
struct irq_desc *desc;
int retval;
+ /*
+ * handle_IRQ_event() always ignores IRQF_DISABLED except for
+ * the _first_ irqaction (sigh). That can cause oopsing, but
+ * the behavior is classified as "will not fix" so we need to
+ * start nudging drivers away from using that idiom.
+ */
+ if ((irqflags & (IRQF_SHARED|IRQF_DISABLED))
+ == (IRQF_SHARED|IRQF_DISABLED))
+ pr_warning("IRQ %d/%s: IRQF_DISABLED is not "
+ "guaranteed on shared IRQs\n",
+ irq, devname);
+
#ifdef CONFIG_LOCKDEP
/*
* Lockdep wants atomic interrupt handlers:
diff --git a/kernel/irq/numa_migrate.c b/kernel/irq/numa_migrate.c
index 0178e2296990..089c3746358a 100644
--- a/kernel/irq/numa_migrate.c
+++ b/kernel/irq/numa_migrate.c
@@ -1,13 +1,8 @@
/*
- * linux/kernel/irq/handle.c
- *
- * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar
- * Copyright (C) 2005-2006, Thomas Gleixner, Russell King
- *
- * This file contains the core interrupt handling code.
- *
- * Detailed information is available in Documentation/DocBook/genericirq
+ * NUMA irq-desc migration code
*
+ * Migrate IRQ data structures (irq_desc, chip_data, etc.) over to
+ * the new "home node" of the IRQ.
*/
#include <linux/irq.h>
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index d2c0e5ee53c5..2abd3a7716ed 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -20,7 +20,7 @@ static struct proc_dir_entry *root_irq_dir;
static int irq_affinity_proc_show(struct seq_file *m, void *v)
{
struct irq_desc *desc = irq_to_desc((long)m->private);
- cpumask_t *mask = &desc->affinity;
+ const struct cpumask *mask = &desc->affinity;
#ifdef CONFIG_GENERIC_PENDING_IRQ
if (desc->status & IRQ_MOVE_PENDING)
@@ -93,7 +93,7 @@ static const struct file_operations irq_affinity_proc_fops = {
static int default_affinity_show(struct seq_file *m, void *v)
{
- seq_cpumask(m, &irq_default_affinity);
+ seq_cpumask(m, irq_default_affinity);
seq_putc(m, '\n');
return 0;
}
@@ -101,27 +101,37 @@ static int default_affinity_show(struct seq_file *m, void *v)
static ssize_t default_affinity_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
- cpumask_t new_value;
+ cpumask_var_t new_value;
int err;
- err = cpumask_parse_user(buffer, count, &new_value);
+ if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
+ return -ENOMEM;
+
+ err = cpumask_parse_user(buffer, count, new_value);
if (err)
- return err;
+ goto out;
- if (!is_affinity_mask_valid(new_value))
- return -EINVAL;
+ if (!is_affinity_mask_valid(new_value)) {
+ err = -EINVAL;
+ goto out;
+ }
/*
* Do not allow disabling IRQs completely - it's a too easy
* way to make the system unusable accidentally :-) At least
* one online CPU still has to be targeted.
*/
- if (!cpus_intersects(new_value, cpu_online_map))
- return -EINVAL;
+ if (!cpumask_intersects(new_value, cpu_online_mask)) {
+ err = -EINVAL;
+ goto out;
+ }
- irq_default_affinity = new_value;
+ cpumask_copy(irq_default_affinity, new_value);
+ err = count;
- return count;
+out:
+ free_cpumask_var(new_value);
+ return err;
}
static int default_affinity_open(struct inode *inode, struct file *file)
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 7b8b0f21a5b1..e694afa0eb8c 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -30,20 +30,19 @@
#define all_var 0
#endif
-/* These will be re-linked against their real values during the second link stage */
-extern const unsigned long kallsyms_addresses[] __attribute__((weak));
-extern const u8 kallsyms_names[] __attribute__((weak));
+extern const unsigned long kallsyms_addresses[];
+extern const u8 kallsyms_names[];
/* tell the compiler that the count isn't in the small data section if the arch
* has one (eg: FRV)
*/
extern const unsigned long kallsyms_num_syms
-__attribute__((weak, section(".rodata")));
+ __attribute__((__section__(".rodata")));
-extern const u8 kallsyms_token_table[] __attribute__((weak));
-extern const u16 kallsyms_token_index[] __attribute__((weak));
+extern const u8 kallsyms_token_table[];
+extern const u16 kallsyms_token_index[];
-extern const unsigned long kallsyms_markers[] __attribute__((weak));
+extern const unsigned long kallsyms_markers[];
static inline int is_kernel_inittext(unsigned long addr)
{
@@ -168,9 +167,6 @@ static unsigned long get_symbol_pos(unsigned long addr,
unsigned long symbol_start = 0, symbol_end = 0;
unsigned long i, low, high, mid;
- /* This kernel should never had been booted. */
- BUG_ON(!kallsyms_addresses);
-
/* do a binary search on the sorted kallsyms_addresses array */
low = 0;
high = kallsyms_num_syms;
diff --git a/kernel/kexec.c b/kernel/kexec.c
index ac0fde7b54d0..3fb855ad6aa0 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -1116,7 +1116,7 @@ void crash_save_cpu(struct pt_regs *regs, int cpu)
struct elf_prstatus prstatus;
u32 *buf;
- if ((cpu < 0) || (cpu >= NR_CPUS))
+ if ((cpu < 0) || (cpu >= nr_cpu_ids))
return;
/* Using ELF notes here is opportunistic.
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 3d3c3ea3a023..b46dbb908669 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -118,10 +118,10 @@ EXPORT_SYMBOL(request_module);
struct subprocess_info {
struct work_struct work;
struct completion *complete;
+ struct cred *cred;
char *path;
char **argv;
char **envp;
- struct key *ring;
enum umh_wait wait;
int retval;
struct file *stdin;
@@ -134,19 +134,20 @@ struct subprocess_info {
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
- struct key *new_session, *old_session;
int retval;
- /* Unblock all signals and set the session keyring. */
- new_session = key_get(sub_info->ring);
+ BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
+ /* Unblock all signals */
spin_lock_irq(&current->sighand->siglock);
- old_session = __install_session_keyring(current, new_session);
flush_signal_handlers(current, 1);
sigemptyset(&current->blocked);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- key_put(old_session);
+ /* Install the credentials */
+ commit_creds(sub_info->cred);
+ sub_info->cred = NULL;
/* Install input pipe when needed */
if (sub_info->stdin) {
@@ -185,6 +186,8 @@ void call_usermodehelper_freeinfo(struct subprocess_info *info)
{
if (info->cleanup)
(*info->cleanup)(info->argv, info->envp);
+ if (info->cred)
+ put_cred(info->cred);
kfree(info);
}
EXPORT_SYMBOL(call_usermodehelper_freeinfo);
@@ -240,6 +243,8 @@ static void __call_usermodehelper(struct work_struct *work)
pid_t pid;
enum umh_wait wait = sub_info->wait;
+ BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
/* CLONE_VFORK: wait until the usermode helper has execve'd
* successfully We need the data structures to stay around
* until that is done. */
@@ -362,6 +367,9 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
sub_info->path = path;
sub_info->argv = argv;
sub_info->envp = envp;
+ sub_info->cred = prepare_usermodehelper_creds();
+ if (!sub_info->cred)
+ return NULL;
out:
return sub_info;
@@ -376,7 +384,13 @@ EXPORT_SYMBOL(call_usermodehelper_setup);
void call_usermodehelper_setkeys(struct subprocess_info *info,
struct key *session_keyring)
{
- info->ring = session_keyring;
+#ifdef CONFIG_KEYS
+ struct thread_group_cred *tgcred = info->cred->tgcred;
+ key_put(tgcred->session_keyring);
+ tgcred->session_keyring = key_get(session_keyring);
+#else
+ BUG();
+#endif
}
EXPORT_SYMBOL(call_usermodehelper_setkeys);
@@ -444,6 +458,8 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
DECLARE_COMPLETION_ONSTACK(done);
int retval = 0;
+ BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
+
helper_lock();
if (sub_info->path[0] == '\0')
goto out;
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index c4c7df23f8c7..06b0c3568f0b 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -292,14 +292,12 @@ void lockdep_off(void)
{
current->lockdep_recursion++;
}
-
EXPORT_SYMBOL(lockdep_off);
void lockdep_on(void)
{
current->lockdep_recursion--;
}
-
EXPORT_SYMBOL(lockdep_on);
/*
@@ -581,7 +579,8 @@ static void print_lock_class_header(struct lock_class *class, int depth)
/*
* printk all lock dependencies starting at <entry>:
*/
-static void print_lock_dependencies(struct lock_class *class, int depth)
+static void __used
+print_lock_dependencies(struct lock_class *class, int depth)
{
struct lock_list *entry;
@@ -2513,7 +2512,6 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name,
if (subclass)
register_lock_class(lock, subclass, 1);
}
-
EXPORT_SYMBOL_GPL(lockdep_init_map);
/*
@@ -2694,8 +2692,9 @@ static int check_unlock(struct task_struct *curr, struct lockdep_map *lock,
}
static int
-__lock_set_subclass(struct lockdep_map *lock,
- unsigned int subclass, unsigned long ip)
+__lock_set_class(struct lockdep_map *lock, const char *name,
+ struct lock_class_key *key, unsigned int subclass,
+ unsigned long ip)
{
struct task_struct *curr = current;
struct held_lock *hlock, *prev_hlock;
@@ -2722,6 +2721,7 @@ __lock_set_subclass(struct lockdep_map *lock,
return print_unlock_inbalance_bug(curr, lock, ip);
found_it:
+ lockdep_init_map(lock, name, key, 0);
class = register_lock_class(lock, subclass, 0);
hlock->class_idx = class - lock_classes + 1;
@@ -2906,9 +2906,9 @@ static void check_flags(unsigned long flags)
#endif
}
-void
-lock_set_subclass(struct lockdep_map *lock,
- unsigned int subclass, unsigned long ip)
+void lock_set_class(struct lockdep_map *lock, const char *name,
+ struct lock_class_key *key, unsigned int subclass,
+ unsigned long ip)
{
unsigned long flags;
@@ -2918,13 +2918,12 @@ lock_set_subclass(struct lockdep_map *lock,
raw_local_irq_save(flags);
current->lockdep_recursion = 1;
check_flags(flags);
- if (__lock_set_subclass(lock, subclass, ip))
+ if (__lock_set_class(lock, name, key, subclass, ip))
check_chain_key(current);
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}
-
-EXPORT_SYMBOL_GPL(lock_set_subclass);
+EXPORT_SYMBOL_GPL(lock_set_class);
/*
* We are not always called with irqs disabled - do that here,
@@ -2948,7 +2947,6 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}
-
EXPORT_SYMBOL_GPL(lock_acquire);
void lock_release(struct lockdep_map *lock, int nested,
@@ -2966,7 +2964,6 @@ void lock_release(struct lockdep_map *lock, int nested,
current->lockdep_recursion = 0;
raw_local_irq_restore(flags);
}
-
EXPORT_SYMBOL_GPL(lock_release);
#ifdef CONFIG_LOCK_STAT
@@ -3451,7 +3448,6 @@ retry:
if (unlock)
read_unlock(&tasklist_lock);
}
-
EXPORT_SYMBOL_GPL(debug_show_all_locks);
/*
@@ -3472,7 +3468,6 @@ void debug_show_held_locks(struct task_struct *task)
{
__debug_show_held_locks(task);
}
-
EXPORT_SYMBOL_GPL(debug_show_held_locks);
void lockdep_sys_exit(void)
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 1d3ef29a2583..63598dca2d0c 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -80,12 +80,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
goto out_pid;
}
- new_nsp->user_ns = copy_user_ns(flags, tsk->nsproxy->user_ns);
- if (IS_ERR(new_nsp->user_ns)) {
- err = PTR_ERR(new_nsp->user_ns);
- goto out_user;
- }
-
new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
if (IS_ERR(new_nsp->net_ns)) {
err = PTR_ERR(new_nsp->net_ns);
@@ -95,9 +89,6 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
return new_nsp;
out_net:
- if (new_nsp->user_ns)
- put_user_ns(new_nsp->user_ns);
-out_user:
if (new_nsp->pid_ns)
put_pid_ns(new_nsp->pid_ns);
out_pid:
@@ -130,7 +121,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
get_nsproxy(old_ns);
if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
- CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)))
+ CLONE_NEWPID | CLONE_NEWNET)))
return 0;
if (!capable(CAP_SYS_ADMIN)) {
@@ -173,8 +164,6 @@ void free_nsproxy(struct nsproxy *ns)
put_ipc_ns(ns->ipc_ns);
if (ns->pid_ns)
put_pid_ns(ns->pid_ns);
- if (ns->user_ns)
- put_user_ns(ns->user_ns);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
}
@@ -189,7 +178,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
int err = 0;
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
- CLONE_NEWUSER | CLONE_NEWNET)))
+ CLONE_NEWNET)))
return 0;
if (!capable(CAP_SYS_ADMIN))
diff --git a/kernel/panic.c b/kernel/panic.c
index 4d5088355bfe..13f06349a786 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -21,6 +21,7 @@
#include <linux/debug_locks.h>
#include <linux/random.h>
#include <linux/kallsyms.h>
+#include <linux/dmi.h>
int panic_on_oops;
static unsigned long tainted_mask;
@@ -321,36 +322,27 @@ void oops_exit(void)
}
#ifdef WANT_WARN_ON_SLOWPATH
-void warn_on_slowpath(const char *file, int line)
-{
- char function[KSYM_SYMBOL_LEN];
- unsigned long caller = (unsigned long) __builtin_return_address(0);
- sprint_symbol(function, caller);
-
- printk(KERN_WARNING "------------[ cut here ]------------\n");
- printk(KERN_WARNING "WARNING: at %s:%d %s()\n", file,
- line, function);
- print_modules();
- dump_stack();
- print_oops_end_marker();
- add_taint(TAINT_WARN);
-}
-EXPORT_SYMBOL(warn_on_slowpath);
-
-
void warn_slowpath(const char *file, int line, const char *fmt, ...)
{
va_list args;
char function[KSYM_SYMBOL_LEN];
unsigned long caller = (unsigned long)__builtin_return_address(0);
+ const char *board;
+
sprint_symbol(function, caller);
printk(KERN_WARNING "------------[ cut here ]------------\n");
printk(KERN_WARNING "WARNING: at %s:%d %s()\n", file,
line, function);
- va_start(args, fmt);
- vprintk(fmt, args);
- va_end(args);
+ board = dmi_get_system_info(DMI_PRODUCT_NAME);
+ if (board)
+ printk(KERN_WARNING "Hardware name: %s\n", board);
+
+ if (fmt) {
+ va_start(args, fmt);
+ vprintk(fmt, args);
+ va_end(args);
+ }
print_modules();
dump_stack();
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 5e79c662294b..887c63787de6 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -116,7 +116,7 @@ static DEFINE_SPINLOCK(idr_lock);
* must supply functions here, even if the function just returns
* ENOSYS. The standard POSIX timer management code assumes the
* following: 1.) The k_itimer struct (sched.h) is used for the
- * timer. 2.) The list, it_lock, it_clock, it_id and it_process
+ * timer. 2.) The list, it_lock, it_clock, it_id and it_pid
* fields are not modified by timer code.
*
* At this time all functions EXCEPT clock_nanosleep can be
@@ -197,6 +197,11 @@ static int common_timer_create(struct k_itimer *new_timer)
return 0;
}
+static int no_timer_create(struct k_itimer *new_timer)
+{
+ return -EOPNOTSUPP;
+}
+
/*
* Return nonzero if we know a priori this clockid_t value is bogus.
*/
@@ -248,6 +253,7 @@ static __init int init_posix_timers(void)
.clock_getres = hrtimer_get_res,
.clock_get = posix_get_monotonic_raw,
.clock_set = do_posix_clock_nosettime,
+ .timer_create = no_timer_create,
};
register_posix_clock(CLOCK_REALTIME, &clock_realtime);
@@ -313,7 +319,8 @@ void do_schedule_next_timer(struct siginfo *info)
int posix_timer_event(struct k_itimer *timr, int si_private)
{
- int shared, ret;
+ struct task_struct *task;
+ int shared, ret = -1;
/*
* FIXME: if ->sigq is queued we can race with
* dequeue_signal()->do_schedule_next_timer().
@@ -327,8 +334,13 @@ int posix_timer_event(struct k_itimer *timr, int si_private)
*/
timr->sigq->info.si_sys_private = si_private;
- shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
- ret = send_sigqueue(timr->sigq, timr->it_process, shared);
+ rcu_read_lock();
+ task = pid_task(timr->it_pid, PIDTYPE_PID);
+ if (task) {
+ shared = !(timr->it_sigev_notify & SIGEV_THREAD_ID);
+ ret = send_sigqueue(timr->sigq, task, shared);
+ }
+ rcu_read_unlock();
/* If we failed to send the signal the timer stops. */
return ret > 0;
}
@@ -405,7 +417,7 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
return ret;
}
-static struct task_struct * good_sigevent(sigevent_t * event)
+static struct pid *good_sigevent(sigevent_t * event)
{
struct task_struct *rtn = current->group_leader;
@@ -419,7 +431,7 @@ static struct task_struct * good_sigevent(sigevent_t * event)
((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX)))
return NULL;
- return rtn;
+ return task_pid(rtn);
}
void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock)
@@ -458,6 +470,7 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
idr_remove(&posix_timers_id, tmr->it_id);
spin_unlock_irqrestore(&idr_lock, flags);
}
+ put_pid(tmr->it_pid);
sigqueue_free(tmr->sigq);
kmem_cache_free(posix_timers_cache, tmr);
}
@@ -471,7 +484,6 @@ sys_timer_create(const clockid_t which_clock,
{
struct k_itimer *new_timer;
int error, new_timer_id;
- struct task_struct *process;
sigevent_t event;
int it_id_set = IT_ID_NOT_SET;
@@ -525,11 +537,9 @@ sys_timer_create(const clockid_t which_clock,
goto out;
}
rcu_read_lock();
- process = good_sigevent(&event);
- if (process)
- get_task_struct(process);
+ new_timer->it_pid = get_pid(good_sigevent(&event));
rcu_read_unlock();
- if (!process) {
+ if (!new_timer->it_pid) {
error = -EINVAL;
goto out;
}
@@ -537,8 +547,7 @@ sys_timer_create(const clockid_t which_clock,
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = SIGALRM;
event.sigev_value.sival_int = new_timer->it_id;
- process = current->group_leader;
- get_task_struct(process);
+ new_timer->it_pid = get_pid(task_tgid(current));
}
new_timer->it_sigev_notify = event.sigev_notify;
@@ -548,7 +557,7 @@ sys_timer_create(const clockid_t which_clock,
new_timer->sigq->info.si_code = SI_TIMER;
spin_lock_irq(&current->sighand->siglock);
- new_timer->it_process = process;
+ new_timer->it_signal = current->signal;
list_add(&new_timer->list, &current->signal->posix_timers);
spin_unlock_irq(&current->sighand->siglock);
@@ -583,8 +592,7 @@ static struct k_itimer *lock_timer(timer_t timer_id, unsigned long *flags)
timr = idr_find(&posix_timers_id, (int)timer_id);
if (timr) {
spin_lock(&timr->it_lock);
- if (timr->it_process &&
- same_thread_group(timr->it_process, current)) {
+ if (timr->it_signal == current->signal) {
spin_unlock(&idr_lock);
return timr;
}
@@ -831,8 +839,7 @@ retry_delete:
* This keeps any tasks waiting on the spin lock from thinking
* they got something (see the lock code above).
*/
- put_task_struct(timer->it_process);
- timer->it_process = NULL;
+ timer->it_signal = NULL;
unlock_timer(timer, flags);
release_posix_timer(timer, IT_ID_SET);
@@ -858,8 +865,7 @@ retry_delete:
* This keeps any tasks waiting on the spin lock from thinking
* they got something (see the lock code above).
*/
- put_task_struct(timer->it_process);
- timer->it_process = NULL;
+ timer->it_signal = NULL;
unlock_timer(timer, flags);
release_posix_timer(timer, IT_ID_SET);
diff --git a/kernel/power/poweroff.c b/kernel/power/poweroff.c
index 72016f051477..97890831e1b5 100644
--- a/kernel/power/poweroff.c
+++ b/kernel/power/poweroff.c
@@ -27,7 +27,7 @@ static DECLARE_WORK(poweroff_work, do_poweroff);
static void handle_poweroff(int key, struct tty_struct *tty)
{
/* run sysrq poweroff on boot cpu */
- schedule_work_on(first_cpu(cpu_online_map), &poweroff_work);
+ schedule_work_on(cpumask_first(cpu_online_mask), &poweroff_work);
}
static struct sysrq_key_op sysrq_poweroff_op = {
diff --git a/kernel/printk.c b/kernel/printk.c
index f492f1583d77..e651ab05655f 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -662,7 +662,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
if (recursion_bug) {
recursion_bug = 0;
strcpy(printk_buf, recursion_bug_msg);
- printed_len = sizeof(recursion_bug_msg);
+ printed_len = strlen(recursion_bug_msg);
}
/* Emit the output into the temporary buffer */
printed_len += vscnprintf(printk_buf + printed_len,
diff --git a/kernel/profile.c b/kernel/profile.c
index 4cb7d68fed82..d18e2d2654f2 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -45,7 +45,7 @@ static unsigned long prof_len, prof_shift;
int prof_on __read_mostly;
EXPORT_SYMBOL_GPL(prof_on);
-static cpumask_t prof_cpu_mask = CPU_MASK_ALL;
+static cpumask_var_t prof_cpu_mask;
#ifdef CONFIG_SMP
static DEFINE_PER_CPU(struct profile_hit *[2], cpu_profile_hits);
static DEFINE_PER_CPU(int, cpu_profile_flip);
@@ -113,9 +113,13 @@ int __ref profile_init(void)
buffer_bytes = prof_len*sizeof(atomic_t);
if (!slab_is_available()) {
prof_buffer = alloc_bootmem(buffer_bytes);
+ alloc_bootmem_cpumask_var(&prof_cpu_mask);
return 0;
}
+ if (!alloc_cpumask_var(&prof_cpu_mask, GFP_KERNEL))
+ return -ENOMEM;
+
prof_buffer = kzalloc(buffer_bytes, GFP_KERNEL);
if (prof_buffer)
return 0;
@@ -128,6 +132,7 @@ int __ref profile_init(void)
if (prof_buffer)
return 0;
+ free_cpumask_var(prof_cpu_mask);
return -ENOMEM;
}
@@ -386,13 +391,15 @@ out_free:
return NOTIFY_BAD;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
- cpu_set(cpu, prof_cpu_mask);
+ if (prof_cpu_mask != NULL)
+ cpumask_set_cpu(cpu, prof_cpu_mask);
break;
case CPU_UP_CANCELED:
case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
case CPU_DEAD_FROZEN:
- cpu_clear(cpu, prof_cpu_mask);
+ if (prof_cpu_mask != NULL)
+ cpumask_clear_cpu(cpu, prof_cpu_mask);
if (per_cpu(cpu_profile_hits, cpu)[0]) {
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
per_cpu(cpu_profile_hits, cpu)[0] = NULL;
@@ -430,7 +437,8 @@ void profile_tick(int type)
if (type == CPU_PROFILING && timer_hook)
timer_hook(regs);
- if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask))
+ if (!user_mode(regs) && prof_cpu_mask != NULL &&
+ cpumask_test_cpu(smp_processor_id(), prof_cpu_mask))
profile_hit(type, (void *)profile_pc(regs));
}
@@ -442,7 +450,7 @@ void profile_tick(int type)
static int prof_cpu_mask_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
- int len = cpumask_scnprintf(page, count, (cpumask_t *)data);
+ int len = cpumask_scnprintf(page, count, data);
if (count - len < 2)
return -EINVAL;
len += sprintf(page + len, "\n");
@@ -452,16 +460,20 @@ static int prof_cpu_mask_read_proc(char *page, char **start, off_t off,
static int prof_cpu_mask_write_proc(struct file *file,
const char __user *buffer, unsigned long count, void *data)
{
- cpumask_t *mask = (cpumask_t *)data;
+ struct cpumask *mask = data;
unsigned long full_count = count, err;
- cpumask_t new_value;
+ cpumask_var_t new_value;
- err = cpumask_parse_user(buffer, count, &new_value);
- if (err)
- return err;
+ if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
+ return -ENOMEM;
- *mask = new_value;
- return full_count;
+ err = cpumask_parse_user(buffer, count, new_value);
+ if (!err) {
+ cpumask_copy(mask, new_value);
+ err = full_count;
+ }
+ free_cpumask_var(new_value);
+ return err;
}
void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
@@ -472,7 +484,7 @@ void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir)
entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
if (!entry)
return;
- entry->data = (void *)&prof_cpu_mask;
+ entry->data = prof_cpu_mask;
entry->read_proc = prof_cpu_mask_read_proc;
entry->write_proc = prof_cpu_mask_write_proc;
}
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4c8bcd7dd8e0..29dc700e198c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -25,6 +25,17 @@
#include <asm/pgtable.h>
#include <asm/uaccess.h>
+
+/*
+ * Initialize a new task whose father had been ptraced.
+ *
+ * Called from copy_process().
+ */
+void ptrace_fork(struct task_struct *child, unsigned long clone_flags)
+{
+ arch_ptrace_fork(child, clone_flags);
+}
+
/*
* ptrace a task: make the debugger its new parent and
* move it to the ptrace list.
@@ -72,6 +83,7 @@ void __ptrace_unlink(struct task_struct *child)
child->parent = child->real_parent;
list_del_init(&child->ptrace_entry);
+ arch_ptrace_untrace(child);
if (task_is_traced(child))
ptrace_untrace(child);
}
@@ -115,6 +127,8 @@ int ptrace_check_attach(struct task_struct *child, int kill)
int __ptrace_may_access(struct task_struct *task, unsigned int mode)
{
+ const struct cred *cred = current_cred(), *tcred;
+
/* May we inspect the given task?
* This check is used both for attaching with ptrace
* and for allowing access to sensitive information in /proc.
@@ -127,13 +141,19 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
/* Don't let security modules deny introspection */
if (task == current)
return 0;
- if (((current->uid != task->euid) ||
- (current->uid != task->suid) ||
- (current->uid != task->uid) ||
- (current->gid != task->egid) ||
- (current->gid != task->sgid) ||
- (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
+ rcu_read_lock();
+ tcred = __task_cred(task);
+ if ((cred->uid != tcred->euid ||
+ cred->uid != tcred->suid ||
+ cred->uid != tcred->uid ||
+ cred->gid != tcred->egid ||
+ cred->gid != tcred->sgid ||
+ cred->gid != tcred->gid) &&
+ !capable(CAP_SYS_PTRACE)) {
+ rcu_read_unlock();
return -EPERM;
+ }
+ rcu_read_unlock();
smp_rmb();
if (task->mm)
dumpable = get_dumpable(task->mm);
@@ -163,6 +183,14 @@ int ptrace_attach(struct task_struct *task)
if (same_thread_group(task, current))
goto out;
+ /* Protect exec's credential calculations against our interference;
+ * SUID, SGID and LSM creds get determined differently under ptrace.
+ */
+ retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+ if (retval < 0)
+ goto out;
+
+ retval = -EPERM;
repeat:
/*
* Nasty, nasty.
@@ -202,6 +230,7 @@ repeat:
bad:
write_unlock_irqrestore(&tasklist_lock, flags);
task_unlock(task);
+ mutex_unlock(&current->cred_exec_mutex);
out:
return retval;
}
diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c
index c03ca3e61919..6ec495f60ead 100644
--- a/kernel/rcuclassic.c
+++ b/kernel/rcuclassic.c
@@ -63,14 +63,14 @@ static struct rcu_ctrlblk rcu_ctrlblk = {
.completed = -300,
.pending = -300,
.lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock),
- .cpumask = CPU_MASK_NONE,
+ .cpumask = CPU_BITS_NONE,
};
static struct rcu_ctrlblk rcu_bh_ctrlblk = {
.cur = -300,
.completed = -300,
.pending = -300,
.lock = __SPIN_LOCK_UNLOCKED(&rcu_bh_ctrlblk.lock),
- .cpumask = CPU_MASK_NONE,
+ .cpumask = CPU_BITS_NONE,
};
DEFINE_PER_CPU(struct rcu_data, rcu_data) = { 0L };
@@ -85,7 +85,6 @@ static void force_quiescent_state(struct rcu_data *rdp,
struct rcu_ctrlblk *rcp)
{
int cpu;
- cpumask_t cpumask;
unsigned long flags;
set_need_resched();
@@ -96,10 +95,10 @@ static void force_quiescent_state(struct rcu_data *rdp,
* Don't send IPI to itself. With irqs disabled,
* rdp->cpu is the current cpu.
*
- * cpu_online_map is updated by the _cpu_down()
+ * cpu_online_mask is updated by the _cpu_down()
* using __stop_machine(). Since we're in irqs disabled
* section, __stop_machine() is not exectuting, hence
- * the cpu_online_map is stable.
+ * the cpu_online_mask is stable.
*
* However, a cpu might have been offlined _just_ before
* we disabled irqs while entering here.
@@ -107,13 +106,14 @@ static void force_quiescent_state(struct rcu_data *rdp,
* notification, leading to the offlined cpu's bit
* being set in the rcp->cpumask.
*
- * Hence cpumask = (rcp->cpumask & cpu_online_map) to prevent
+ * Hence cpumask = (rcp->cpumask & cpu_online_mask) to prevent
* sending smp_reschedule() to an offlined CPU.
*/
- cpus_and(cpumask, rcp->cpumask, cpu_online_map);
- cpu_clear(rdp->cpu, cpumask);
- for_each_cpu_mask_nr(cpu, cpumask)
- smp_send_reschedule(cpu);
+ for_each_cpu_and(cpu,
+ to_cpumask(rcp->cpumask), cpu_online_mask) {
+ if (cpu != rdp->cpu)
+ smp_send_reschedule(cpu);
+ }
}
spin_unlock_irqrestore(&rcp->lock, flags);
}
@@ -193,7 +193,7 @@ static void print_other_cpu_stall(struct rcu_ctrlblk *rcp)
printk(KERN_ERR "INFO: RCU detected CPU stalls:");
for_each_possible_cpu(cpu) {
- if (cpu_isset(cpu, rcp->cpumask))
+ if (cpumask_test_cpu(cpu, to_cpumask(rcp->cpumask)))
printk(" %d", cpu);
}
printk(" (detected by %d, t=%ld jiffies)\n",
@@ -221,7 +221,8 @@ static void check_cpu_stall(struct rcu_ctrlblk *rcp)
long delta;
delta = jiffies - rcp->jiffies_stall;
- if (cpu_isset(smp_processor_id(), rcp->cpumask) && delta >= 0) {
+ if (cpumask_test_cpu(smp_processor_id(), to_cpumask(rcp->cpumask)) &&
+ delta >= 0) {
/* We haven't checked in, so go dump stack. */
print_cpu_stall(rcp);
@@ -406,8 +407,8 @@ static void rcu_start_batch(struct rcu_ctrlblk *rcp)
*/
static void cpu_quiet(int cpu, struct rcu_ctrlblk *rcp)
{
- cpu_clear(cpu, rcp->cpumask);
- if (cpus_empty(rcp->cpumask)) {
+ cpumask_clear_cpu(cpu, to_cpumask(rcp->cpumask));
+ if (cpumask_empty(to_cpumask(rcp->cpumask))) {
/* batch completed ! */
rcp->completed = rcp->cur;
rcu_start_batch(rcp);
diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c
index 59236e8b9daa..f9dc8f3720f6 100644
--- a/kernel/rcupreempt.c
+++ b/kernel/rcupreempt.c
@@ -164,7 +164,8 @@ static char *rcu_try_flip_state_names[] =
{ "idle", "waitack", "waitzero", "waitmb" };
#endif /* #ifdef CONFIG_RCU_TRACE */
-static cpumask_t rcu_cpu_online_map __read_mostly = CPU_MASK_NONE;
+static DECLARE_BITMAP(rcu_cpu_online_map, NR_CPUS) __read_mostly
+ = CPU_BITS_NONE;
/*
* Enum and per-CPU flag to determine when each CPU has seen
@@ -551,6 +552,16 @@ void rcu_irq_exit(void)
}
}
+void rcu_nmi_enter(void)
+{
+ rcu_irq_enter();
+}
+
+void rcu_nmi_exit(void)
+{
+ rcu_irq_exit();
+}
+
static void dyntick_save_progress_counter(int cpu)
{
struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
@@ -748,7 +759,7 @@ rcu_try_flip_idle(void)
/* Now ask each CPU for acknowledgement of the flip. */
- for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
+ for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) {
per_cpu(rcu_flip_flag, cpu) = rcu_flipped;
dyntick_save_progress_counter(cpu);
}
@@ -766,7 +777,7 @@ rcu_try_flip_waitack(void)
int cpu;
RCU_TRACE_ME(rcupreempt_trace_try_flip_a1);
- for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
+ for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
if (rcu_try_flip_waitack_needed(cpu) &&
per_cpu(rcu_flip_flag, cpu) != rcu_flip_seen) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_ae1);
@@ -798,7 +809,7 @@ rcu_try_flip_waitzero(void)
/* Check to see if the sum of the "last" counters is zero. */
RCU_TRACE_ME(rcupreempt_trace_try_flip_z1);
- for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
+ for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
sum += RCU_DATA_CPU(cpu)->rcu_flipctr[lastidx];
if (sum != 0) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_ze1);
@@ -813,7 +824,7 @@ rcu_try_flip_waitzero(void)
smp_mb(); /* ^^^^^^^^^^^^ */
/* Call for a memory barrier from each CPU. */
- for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
+ for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map)) {
per_cpu(rcu_mb_flag, cpu) = rcu_mb_needed;
dyntick_save_progress_counter(cpu);
}
@@ -833,7 +844,7 @@ rcu_try_flip_waitmb(void)
int cpu;
RCU_TRACE_ME(rcupreempt_trace_try_flip_m1);
- for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
+ for_each_cpu(cpu, to_cpumask(rcu_cpu_online_map))
if (rcu_try_flip_waitmb_needed(cpu) &&
per_cpu(rcu_mb_flag, cpu) != rcu_mb_done) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_me1);
@@ -1022,7 +1033,7 @@ void rcu_offline_cpu(int cpu)
RCU_DATA_CPU(cpu)->rcu_flipctr[0] = 0;
RCU_DATA_CPU(cpu)->rcu_flipctr[1] = 0;
- cpu_clear(cpu, rcu_cpu_online_map);
+ cpumask_clear_cpu(cpu, to_cpumask(rcu_cpu_online_map));
spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);
@@ -1062,7 +1073,7 @@ void __cpuinit rcu_online_cpu(int cpu)
struct rcu_data *rdp;
spin_lock_irqsave(&rcu_ctrlblk.fliplock, flags);
- cpu_set(cpu, rcu_cpu_online_map);
+ cpumask_set_cpu(cpu, to_cpumask(rcu_cpu_online_map));
spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);
/*
@@ -1420,7 +1431,7 @@ void __init __rcu_init(void)
* We don't need protection against CPU-Hotplug here
* since
* a) If a CPU comes online while we are iterating over the
- * cpu_online_map below, we would only end up making a
+ * cpu_online_mask below, we would only end up making a
* duplicate call to rcu_online_cpu() which sets the corresponding
* CPU's mask in the rcu_cpu_online_map.
*
diff --git a/kernel/rcupreempt_trace.c b/kernel/rcupreempt_trace.c
index 35c2d3360ecf..7c2665cac172 100644
--- a/kernel/rcupreempt_trace.c
+++ b/kernel/rcupreempt_trace.c
@@ -149,12 +149,12 @@ static void rcupreempt_trace_sum(struct rcupreempt_trace *sp)
sp->done_length += cp->done_length;
sp->done_add += cp->done_add;
sp->done_remove += cp->done_remove;
- atomic_set(&sp->done_invoked, atomic_read(&cp->done_invoked));
+ atomic_add(atomic_read(&cp->done_invoked), &sp->done_invoked);
sp->rcu_check_callbacks += cp->rcu_check_callbacks;
- atomic_set(&sp->rcu_try_flip_1,
- atomic_read(&cp->rcu_try_flip_1));
- atomic_set(&sp->rcu_try_flip_e1,
- atomic_read(&cp->rcu_try_flip_e1));
+ atomic_add(atomic_read(&cp->rcu_try_flip_1),
+ &sp->rcu_try_flip_1);
+ atomic_add(atomic_read(&cp->rcu_try_flip_e1),
+ &sp->rcu_try_flip_e1);
sp->rcu_try_flip_i1 += cp->rcu_try_flip_i1;
sp->rcu_try_flip_ie1 += cp->rcu_try_flip_ie1;
sp->rcu_try_flip_g1 += cp->rcu_try_flip_g1;
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index 85cb90588a55..3245b40952c6 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -39,6 +39,7 @@
#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
+#include <linux/reboot.h>
#include <linux/freezer.h>
#include <linux/cpu.h>
#include <linux/delay.h>
@@ -108,7 +109,6 @@ struct rcu_torture {
int rtort_mbtest;
};
-static int fullstop = 0; /* stop generating callbacks at test end. */
static LIST_HEAD(rcu_torture_freelist);
static struct rcu_torture *rcu_torture_current = NULL;
static long rcu_torture_current_version = 0;
@@ -136,6 +136,30 @@ static int stutter_pause_test = 0;
#endif
int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
+#define FULLSTOP_SIGNALED 1 /* Bail due to signal. */
+#define FULLSTOP_CLEANUP 2 /* Orderly shutdown. */
+static int fullstop; /* stop generating callbacks at test end. */
+DEFINE_MUTEX(fullstop_mutex); /* protect fullstop transitions and */
+ /* spawning of kthreads. */
+
+/*
+ * Detect and respond to a signal-based shutdown.
+ */
+static int
+rcutorture_shutdown_notify(struct notifier_block *unused1,
+ unsigned long unused2, void *unused3)
+{
+ if (fullstop)
+ return NOTIFY_DONE;
+ if (signal_pending(current)) {
+ mutex_lock(&fullstop_mutex);
+ if (!ACCESS_ONCE(fullstop))
+ fullstop = FULLSTOP_SIGNALED;
+ mutex_unlock(&fullstop_mutex);
+ }
+ return NOTIFY_DONE;
+}
+
/*
* Allocate an element from the rcu_tortures pool.
*/
@@ -199,11 +223,12 @@ rcu_random(struct rcu_random_state *rrsp)
static void
rcu_stutter_wait(void)
{
- while (stutter_pause_test || !rcutorture_runnable)
+ while ((stutter_pause_test || !rcutorture_runnable) && !fullstop) {
if (rcutorture_runnable)
schedule_timeout_interruptible(1);
else
schedule_timeout_interruptible(round_jiffies_relative(HZ));
+ }
}
/*
@@ -599,7 +624,7 @@ rcu_torture_writer(void *arg)
rcu_stutter_wait();
} while (!kthread_should_stop() && !fullstop);
VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
- while (!kthread_should_stop())
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
schedule_timeout_uninterruptible(1);
return 0;
}
@@ -624,7 +649,7 @@ rcu_torture_fakewriter(void *arg)
} while (!kthread_should_stop() && !fullstop);
VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
- while (!kthread_should_stop())
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
schedule_timeout_uninterruptible(1);
return 0;
}
@@ -734,7 +759,7 @@ rcu_torture_reader(void *arg)
VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
if (irqreader && cur_ops->irqcapable)
del_timer_sync(&t);
- while (!kthread_should_stop())
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
schedule_timeout_uninterruptible(1);
return 0;
}
@@ -831,7 +856,7 @@ rcu_torture_stats(void *arg)
do {
schedule_timeout_interruptible(stat_interval * HZ);
rcu_torture_stats_print();
- } while (!kthread_should_stop());
+ } while (!kthread_should_stop() && !fullstop);
VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping");
return 0;
}
@@ -843,49 +868,52 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */
*/
static void rcu_torture_shuffle_tasks(void)
{
- cpumask_t tmp_mask;
+ cpumask_var_t tmp_mask;
int i;
- cpus_setall(tmp_mask);
+ if (!alloc_cpumask_var(&tmp_mask, GFP_KERNEL))
+ BUG();
+
+ cpumask_setall(tmp_mask);
get_online_cpus();
/* No point in shuffling if there is only one online CPU (ex: UP) */
- if (num_online_cpus() == 1) {
- put_online_cpus();
- return;
- }
+ if (num_online_cpus() == 1)
+ goto out;
if (rcu_idle_cpu != -1)
- cpu_clear(rcu_idle_cpu, tmp_mask);
+ cpumask_clear_cpu(rcu_idle_cpu, tmp_mask);
- set_cpus_allowed_ptr(current, &tmp_mask);
+ set_cpus_allowed_ptr(current, tmp_mask);
if (reader_tasks) {
for (i = 0; i < nrealreaders; i++)
if (reader_tasks[i])
set_cpus_allowed_ptr(reader_tasks[i],
- &tmp_mask);
+ tmp_mask);
}
if (fakewriter_tasks) {
for (i = 0; i < nfakewriters; i++)
if (fakewriter_tasks[i])
set_cpus_allowed_ptr(fakewriter_tasks[i],
- &tmp_mask);
+ tmp_mask);
}
if (writer_task)
- set_cpus_allowed_ptr(writer_task, &tmp_mask);
+ set_cpus_allowed_ptr(writer_task, tmp_mask);
if (stats_task)
- set_cpus_allowed_ptr(stats_task, &tmp_mask);
+ set_cpus_allowed_ptr(stats_task, tmp_mask);
if (rcu_idle_cpu == -1)
rcu_idle_cpu = num_online_cpus() - 1;
else
rcu_idle_cpu--;
+out:
put_online_cpus();
+ free_cpumask_var(tmp_mask);
}
/* Shuffle tasks across CPUs, with the intent of allowing each CPU in the
@@ -899,7 +927,7 @@ rcu_torture_shuffle(void *arg)
do {
schedule_timeout_interruptible(shuffle_interval * HZ);
rcu_torture_shuffle_tasks();
- } while (!kthread_should_stop());
+ } while (!kthread_should_stop() && !fullstop);
VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping");
return 0;
}
@@ -914,10 +942,10 @@ rcu_torture_stutter(void *arg)
do {
schedule_timeout_interruptible(stutter * HZ);
stutter_pause_test = 1;
- if (!kthread_should_stop())
+ if (!kthread_should_stop() && !fullstop)
schedule_timeout_interruptible(stutter * HZ);
stutter_pause_test = 0;
- } while (!kthread_should_stop());
+ } while (!kthread_should_stop() && !fullstop);
VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping");
return 0;
}
@@ -934,12 +962,27 @@ rcu_torture_print_module_parms(char *tag)
stutter, irqreader);
}
+static struct notifier_block rcutorture_nb = {
+ .notifier_call = rcutorture_shutdown_notify,
+};
+
static void
rcu_torture_cleanup(void)
{
int i;
- fullstop = 1;
+ mutex_lock(&fullstop_mutex);
+ if (!fullstop) {
+ /* If being signaled, let it happen, then exit. */
+ mutex_unlock(&fullstop_mutex);
+ schedule_timeout_interruptible(10 * HZ);
+ if (cur_ops->cb_barrier != NULL)
+ cur_ops->cb_barrier();
+ return;
+ }
+ fullstop = FULLSTOP_CLEANUP;
+ mutex_unlock(&fullstop_mutex);
+ unregister_reboot_notifier(&rcutorture_nb);
if (stutter_task) {
VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task");
kthread_stop(stutter_task);
@@ -1015,6 +1058,8 @@ rcu_torture_init(void)
{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
&srcu_ops, &sched_ops, &sched_ops_sync, };
+ mutex_lock(&fullstop_mutex);
+
/* Process args and tell the world that the torturer is on the job. */
for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
cur_ops = torture_ops[i];
@@ -1024,6 +1069,7 @@ rcu_torture_init(void)
if (i == ARRAY_SIZE(torture_ops)) {
printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n",
torture_type);
+ mutex_unlock(&fullstop_mutex);
return (-EINVAL);
}
if (cur_ops->init)
@@ -1146,9 +1192,12 @@ rcu_torture_init(void)
goto unwind;
}
}
+ register_reboot_notifier(&rcutorture_nb);
+ mutex_unlock(&fullstop_mutex);
return 0;
unwind:
+ mutex_unlock(&fullstop_mutex);
rcu_torture_cleanup();
return firsterr;
}
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
new file mode 100644
index 000000000000..a342b032112c
--- /dev/null
+++ b/kernel/rcutree.c
@@ -0,0 +1,1535 @@
+/*
+ * Read-Copy Update mechanism for mutual exclusion
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * Copyright IBM Corporation, 2008
+ *
+ * Authors: Dipankar Sarma <dipankar@in.ibm.com>
+ * Manfred Spraul <manfred@colorfullife.com>
+ * Paul E. McKenney <paulmck@linux.vnet.ibm.com> Hierarchical version
+ *
+ * Based on the original work by Paul McKenney <paulmck@us.ibm.com>
+ * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen.
+ *
+ * For detailed explanation of Read-Copy Update mechanism see -
+ * Documentation/RCU
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/rcupdate.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <asm/atomic.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/moduleparam.h>
+#include <linux/percpu.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/mutex.h>
+#include <linux/time.h>
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key rcu_lock_key;
+struct lockdep_map rcu_lock_map =
+ STATIC_LOCKDEP_MAP_INIT("rcu_read_lock", &rcu_lock_key);
+EXPORT_SYMBOL_GPL(rcu_lock_map);
+#endif
+
+/* Data structures. */
+
+#define RCU_STATE_INITIALIZER(name) { \
+ .level = { &name.node[0] }, \
+ .levelcnt = { \
+ NUM_RCU_LVL_0, /* root of hierarchy. */ \
+ NUM_RCU_LVL_1, \
+ NUM_RCU_LVL_2, \
+ NUM_RCU_LVL_3, /* == MAX_RCU_LVLS */ \
+ }, \
+ .signaled = RCU_SIGNAL_INIT, \
+ .gpnum = -300, \
+ .completed = -300, \
+ .onofflock = __SPIN_LOCK_UNLOCKED(&name.onofflock), \
+ .fqslock = __SPIN_LOCK_UNLOCKED(&name.fqslock), \
+ .n_force_qs = 0, \
+ .n_force_qs_ngp = 0, \
+}
+
+struct rcu_state rcu_state = RCU_STATE_INITIALIZER(rcu_state);
+DEFINE_PER_CPU(struct rcu_data, rcu_data);
+
+struct rcu_state rcu_bh_state = RCU_STATE_INITIALIZER(rcu_bh_state);
+DEFINE_PER_CPU(struct rcu_data, rcu_bh_data);
+
+#ifdef CONFIG_NO_HZ
+DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks);
+#endif /* #ifdef CONFIG_NO_HZ */
+
+static int blimit = 10; /* Maximum callbacks per softirq. */
+static int qhimark = 10000; /* If this many pending, ignore blimit. */
+static int qlowmark = 100; /* Once only this many pending, use blimit. */
+
+static void force_quiescent_state(struct rcu_state *rsp, int relaxed);
+
+/*
+ * Return the number of RCU batches processed thus far for debug & stats.
+ */
+long rcu_batches_completed(void)
+{
+ return rcu_state.completed;
+}
+EXPORT_SYMBOL_GPL(rcu_batches_completed);
+
+/*
+ * Return the number of RCU BH batches processed thus far for debug & stats.
+ */
+long rcu_batches_completed_bh(void)
+{
+ return rcu_bh_state.completed;
+}
+EXPORT_SYMBOL_GPL(rcu_batches_completed_bh);
+
+/*
+ * Does the CPU have callbacks ready to be invoked?
+ */
+static int
+cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
+{
+ return &rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL];
+}
+
+/*
+ * Does the current CPU require a yet-as-unscheduled grace period?
+ */
+static int
+cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ /* ACCESS_ONCE() because we are accessing outside of lock. */
+ return *rdp->nxttail[RCU_DONE_TAIL] &&
+ ACCESS_ONCE(rsp->completed) == ACCESS_ONCE(rsp->gpnum);
+}
+
+/*
+ * Return the root node of the specified rcu_state structure.
+ */
+static struct rcu_node *rcu_get_root(struct rcu_state *rsp)
+{
+ return &rsp->node[0];
+}
+
+#ifdef CONFIG_SMP
+
+/*
+ * 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, it is in a quiescent state. We can
+ * trust its state not to change because interrupts are disabled.
+ */
+ if (cpu_is_offline(rdp->cpu)) {
+ rdp->offline_fqs++;
+ return 1;
+ }
+
+ /* The CPU is online, so send it a reschedule IPI. */
+ if (rdp->cpu != smp_processor_id())
+ smp_send_reschedule(rdp->cpu);
+ else
+ set_need_resched();
+ rdp->resched_ipi++;
+ return 0;
+}
+
+#endif /* #ifdef CONFIG_SMP */
+
+#ifdef CONFIG_NO_HZ
+static DEFINE_RATELIMIT_STATE(rcu_rs, 10 * HZ, 5);
+
+/**
+ * rcu_enter_nohz - inform RCU that current CPU is entering nohz
+ *
+ * Enter nohz mode, in other words, -leave- the mode in which RCU
+ * read-side critical sections can occur. (Though RCU read-side
+ * critical sections can occur in irq handlers in nohz mode, a possibility
+ * handled by rcu_irq_enter() and rcu_irq_exit()).
+ */
+void rcu_enter_nohz(void)
+{
+ unsigned long flags;
+ struct rcu_dynticks *rdtp;
+
+ smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
+ local_irq_save(flags);
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ rdtp->dynticks++;
+ rdtp->dynticks_nesting--;
+ WARN_ON_RATELIMIT(rdtp->dynticks & 0x1, &rcu_rs);
+ local_irq_restore(flags);
+}
+
+/*
+ * rcu_exit_nohz - inform RCU that current CPU is leaving nohz
+ *
+ * Exit nohz mode, in other words, -enter- the mode in which RCU
+ * read-side critical sections normally occur.
+ */
+void rcu_exit_nohz(void)
+{
+ unsigned long flags;
+ struct rcu_dynticks *rdtp;
+
+ local_irq_save(flags);
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ rdtp->dynticks++;
+ rdtp->dynticks_nesting++;
+ WARN_ON_RATELIMIT(!(rdtp->dynticks & 0x1), &rcu_rs);
+ local_irq_restore(flags);
+ smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
+}
+
+/**
+ * rcu_nmi_enter - inform RCU of entry to NMI context
+ *
+ * If the CPU was idle with dynamic ticks active, and there is no
+ * irq handler running, this updates rdtp->dynticks_nmi to let the
+ * RCU grace-period handling know that the CPU is active.
+ */
+void rcu_nmi_enter(void)
+{
+ struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks);
+
+ if (rdtp->dynticks & 0x1)
+ return;
+ rdtp->dynticks_nmi++;
+ WARN_ON_RATELIMIT(!(rdtp->dynticks_nmi & 0x1), &rcu_rs);
+ smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
+}
+
+/**
+ * rcu_nmi_exit - inform RCU of exit from NMI context
+ *
+ * If the CPU was idle with dynamic ticks active, and there is no
+ * irq handler running, this updates rdtp->dynticks_nmi to let the
+ * RCU grace-period handling know that the CPU is no longer active.
+ */
+void rcu_nmi_exit(void)
+{
+ struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks);
+
+ if (rdtp->dynticks & 0x1)
+ return;
+ smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
+ rdtp->dynticks_nmi++;
+ WARN_ON_RATELIMIT(rdtp->dynticks_nmi & 0x1, &rcu_rs);
+}
+
+/**
+ * rcu_irq_enter - inform RCU of entry to hard irq context
+ *
+ * If the CPU was idle with dynamic ticks active, this updates the
+ * rdtp->dynticks to let the RCU handling know that the CPU is active.
+ */
+void rcu_irq_enter(void)
+{
+ struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks);
+
+ if (rdtp->dynticks_nesting++)
+ return;
+ rdtp->dynticks++;
+ WARN_ON_RATELIMIT(!(rdtp->dynticks & 0x1), &rcu_rs);
+ smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
+}
+
+/**
+ * rcu_irq_exit - inform RCU of exit from hard irq context
+ *
+ * If the CPU was idle with dynamic ticks active, update the rdp->dynticks
+ * to put let the RCU handling be aware that the CPU is going back to idle
+ * with no ticks.
+ */
+void rcu_irq_exit(void)
+{
+ struct rcu_dynticks *rdtp = &__get_cpu_var(rcu_dynticks);
+
+ if (--rdtp->dynticks_nesting)
+ return;
+ smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
+ rdtp->dynticks++;
+ WARN_ON_RATELIMIT(rdtp->dynticks & 0x1, &rcu_rs);
+
+ /* If the interrupt queued a callback, get out of dyntick mode. */
+ if (__get_cpu_var(rcu_data).nxtlist ||
+ __get_cpu_var(rcu_bh_data).nxtlist)
+ set_need_resched();
+}
+
+/*
+ * Record the specified "completed" value, which is later used to validate
+ * dynticks counter manipulations. Specify "rsp->completed - 1" to
+ * unconditionally invalidate any future dynticks manipulations (which is
+ * useful at the beginning of a grace period).
+ */
+static void dyntick_record_completed(struct rcu_state *rsp, long comp)
+{
+ rsp->dynticks_completed = comp;
+}
+
+#ifdef CONFIG_SMP
+
+/*
+ * Recall the previously recorded value of the completion for dynticks.
+ */
+static long dyntick_recall_completed(struct rcu_state *rsp)
+{
+ return rsp->dynticks_completed;
+}
+
+/*
+ * Snapshot the specified CPU's dynticks counter so that we can later
+ * credit them with an implicit quiescent state. Return 1 if this CPU
+ * is already in a quiescent state courtesy of dynticks idle mode.
+ */
+static int dyntick_save_progress_counter(struct rcu_data *rdp)
+{
+ int ret;
+ int snap;
+ int snap_nmi;
+
+ snap = rdp->dynticks->dynticks;
+ snap_nmi = rdp->dynticks->dynticks_nmi;
+ smp_mb(); /* Order sampling of snap with end of grace period. */
+ rdp->dynticks_snap = snap;
+ rdp->dynticks_nmi_snap = snap_nmi;
+ ret = ((snap & 0x1) == 0) && ((snap_nmi & 0x1) == 0);
+ if (ret)
+ rdp->dynticks_fqs++;
+ return ret;
+}
+
+/*
+ * 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.
+ */
+static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
+{
+ long curr;
+ long curr_nmi;
+ long snap;
+ long snap_nmi;
+
+ curr = rdp->dynticks->dynticks;
+ snap = rdp->dynticks_snap;
+ curr_nmi = rdp->dynticks->dynticks_nmi;
+ snap_nmi = rdp->dynticks_nmi_snap;
+ smp_mb(); /* force ordering with cpu entering/leaving dynticks. */
+
+ /*
+ * If the CPU passed through or entered a dynticks idle phase with
+ * no active irq/NMI handlers, then we can safely pretend that the CPU
+ * already acknowledged the request to pass through a quiescent
+ * state. Either way, that CPU cannot possibly be in an RCU
+ * read-side critical section that started before the beginning
+ * of the current RCU grace period.
+ */
+ if ((curr != snap || (curr & 0x1) == 0) &&
+ (curr_nmi != snap_nmi || (curr_nmi & 0x1) == 0)) {
+ rdp->dynticks_fqs++;
+ return 1;
+ }
+
+ /* Go check for the CPU being offline. */
+ return rcu_implicit_offline_qs(rdp);
+}
+
+#endif /* #ifdef CONFIG_SMP */
+
+#else /* #ifdef CONFIG_NO_HZ */
+
+static void dyntick_record_completed(struct rcu_state *rsp, long comp)
+{
+}
+
+#ifdef CONFIG_SMP
+
+/*
+ * If there are no dynticks, then the only way that a CPU can passively
+ * be in a quiescent state is to be offline. Unlike dynticks idle, which
+ * is a point in time during the prior (already finished) grace period,
+ * an offline CPU is always in a quiescent state, and thus can be
+ * unconditionally applied. So just return the current value of completed.
+ */
+static long dyntick_recall_completed(struct rcu_state *rsp)
+{
+ return rsp->completed;
+}
+
+static int dyntick_save_progress_counter(struct rcu_data *rdp)
+{
+ return 0;
+}
+
+static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
+{
+ return rcu_implicit_offline_qs(rdp);
+}
+
+#endif /* #ifdef CONFIG_SMP */
+
+#endif /* #else #ifdef CONFIG_NO_HZ */
+
+#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
+
+static void record_gp_stall_check_time(struct rcu_state *rsp)
+{
+ rsp->gp_start = jiffies;
+ rsp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_CHECK;
+}
+
+static void print_other_cpu_stall(struct rcu_state *rsp)
+{
+ int cpu;
+ long delta;
+ unsigned long flags;
+ struct rcu_node *rnp = rcu_get_root(rsp);
+ struct rcu_node *rnp_cur = rsp->level[NUM_RCU_LVLS - 1];
+ struct rcu_node *rnp_end = &rsp->node[NUM_RCU_NODES];
+
+ /* Only let one CPU complain about others per time interval. */
+
+ spin_lock_irqsave(&rnp->lock, flags);
+ delta = jiffies - rsp->jiffies_stall;
+ if (delta < RCU_STALL_RAT_DELAY || rsp->gpnum == rsp->completed) {
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+ rsp->jiffies_stall = jiffies + RCU_SECONDS_TILL_STALL_RECHECK;
+ spin_unlock_irqrestore(&rnp->lock, flags);
+
+ /* OK, time to rat on our buddy... */
+
+ printk(KERN_ERR "INFO: RCU detected CPU stalls:");
+ for (; rnp_cur < rnp_end; rnp_cur++) {
+ if (rnp_cur->qsmask == 0)
+ continue;
+ for (cpu = 0; cpu <= rnp_cur->grphi - rnp_cur->grplo; cpu++)
+ if (rnp_cur->qsmask & (1UL << cpu))
+ printk(" %d", rnp_cur->grplo + cpu);
+ }
+ printk(" (detected by %d, t=%ld jiffies)\n",
+ smp_processor_id(), (long)(jiffies - rsp->gp_start));
+ force_quiescent_state(rsp, 0); /* Kick them all. */
+}
+
+static void print_cpu_stall(struct rcu_state *rsp)
+{
+ unsigned long flags;
+ struct rcu_node *rnp = rcu_get_root(rsp);
+
+ printk(KERN_ERR "INFO: RCU detected CPU %d stall (t=%lu jiffies)\n",
+ smp_processor_id(), jiffies - rsp->gp_start);
+ dump_stack();
+ spin_lock_irqsave(&rnp->lock, flags);
+ if ((long)(jiffies - rsp->jiffies_stall) >= 0)
+ rsp->jiffies_stall =
+ jiffies + RCU_SECONDS_TILL_STALL_RECHECK;
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ set_need_resched(); /* kick ourselves to get things going. */
+}
+
+static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ long delta;
+ struct rcu_node *rnp;
+
+ delta = jiffies - rsp->jiffies_stall;
+ rnp = rdp->mynode;
+ if ((rnp->qsmask & rdp->grpmask) && delta >= 0) {
+
+ /* We haven't checked in, so go dump stack. */
+ print_cpu_stall(rsp);
+
+ } else if (rsp->gpnum != rsp->completed &&
+ delta >= RCU_STALL_RAT_DELAY) {
+
+ /* They had two time units to dump stack, so complain. */
+ print_other_cpu_stall(rsp);
+ }
+}
+
+#else /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
+
+static void record_gp_stall_check_time(struct rcu_state *rsp)
+{
+}
+
+static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+}
+
+#endif /* #else #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
+
+/*
+ * Update CPU-local rcu_data state to record the newly noticed grace period.
+ * This is used both when we started the grace period and when we notice
+ * that someone else started the grace period.
+ */
+static void note_new_gpnum(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ rdp->qs_pending = 1;
+ rdp->passed_quiesc = 0;
+ rdp->gpnum = rsp->gpnum;
+ rdp->n_rcu_pending_force_qs = rdp->n_rcu_pending +
+ RCU_JIFFIES_TILL_FORCE_QS;
+}
+
+/*
+ * Did someone else start a new RCU grace period start since we last
+ * checked? Update local state appropriately if so. Must be called
+ * on the CPU corresponding to rdp.
+ */
+static int
+check_for_new_grace_period(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ local_irq_save(flags);
+ if (rdp->gpnum != rsp->gpnum) {
+ note_new_gpnum(rsp, rdp);
+ ret = 1;
+ }
+ local_irq_restore(flags);
+ return ret;
+}
+
+/*
+ * 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.
+ */
+static void
+rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
+ __releases(rcu_get_root(rsp)->lock)
+{
+ struct rcu_data *rdp = rsp->rda[smp_processor_id()];
+ struct rcu_node *rnp = rcu_get_root(rsp);
+ struct rcu_node *rnp_cur;
+ struct rcu_node *rnp_end;
+
+ if (!cpu_needs_another_gp(rsp, rdp)) {
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+
+ /* Advance to a new grace period and initialize state. */
+ rsp->gpnum++;
+ rsp->signaled = RCU_GP_INIT; /* Hold off force_quiescent_state. */
+ rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
+ rdp->n_rcu_pending_force_qs = rdp->n_rcu_pending +
+ RCU_JIFFIES_TILL_FORCE_QS;
+ record_gp_stall_check_time(rsp);
+ dyntick_record_completed(rsp, rsp->completed - 1);
+ note_new_gpnum(rsp, rdp);
+
+ /*
+ * Because we are first, we know that all our callbacks will
+ * be covered by this upcoming grace period, even the ones
+ * that were registered arbitrarily recently.
+ */
+ rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
+ rdp->nxttail[RCU_WAIT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
+
+ /* Special-case the common single-level case. */
+ if (NUM_RCU_NODES == 1) {
+ rnp->qsmask = rnp->qsmaskinit;
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+
+ spin_unlock(&rnp->lock); /* leave irqs disabled. */
+
+
+ /* Exclude any concurrent CPU-hotplug operations. */
+ spin_lock(&rsp->onofflock); /* irqs already disabled. */
+
+ /*
+ * Set the quiescent-state-needed bits in all the non-leaf RCU
+ * nodes for all currently online CPUs. 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 grace period is in
+ * progress. In addition, we have excluded CPU-hotplug operations.
+ *
+ * We therefore do not need to hold any locks. Any required
+ * memory barriers will be supplied by the locks guarding the
+ * leaf rcu_nodes in the hierarchy.
+ */
+
+ rnp_end = rsp->level[NUM_RCU_LVLS - 1];
+ for (rnp_cur = &rsp->node[0]; rnp_cur < rnp_end; rnp_cur++)
+ rnp_cur->qsmask = rnp_cur->qsmaskinit;
+
+ /*
+ * Now set up the leaf nodes. Here we must be careful. First,
+ * we need to hold the lock in order to exclude other CPUs, which
+ * might be contending for the leaf nodes' locks. Second, as
+ * soon as we initialize a given leaf node, its CPUs might run
+ * up the rest of the hierarchy. We must therefore acquire locks
+ * for each node that we touch during this stage. (But we still
+ * are excluding 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.
+ */
+ rnp_end = &rsp->node[NUM_RCU_NODES];
+ rnp_cur = rsp->level[NUM_RCU_LVLS - 1];
+ for (; rnp_cur < rnp_end; rnp_cur++) {
+ spin_lock(&rnp_cur->lock); /* irqs already disabled. */
+ rnp_cur->qsmask = rnp_cur->qsmaskinit;
+ spin_unlock(&rnp_cur->lock); /* irqs already disabled. */
+ }
+
+ rsp->signaled = RCU_SIGNAL_INIT; /* force_quiescent_state now OK. */
+ spin_unlock_irqrestore(&rsp->onofflock, flags);
+}
+
+/*
+ * Advance this CPU's callbacks, but only if the current grace period
+ * has ended. This may be called only from the CPU to whom the rdp
+ * belongs.
+ */
+static void
+rcu_process_gp_end(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ long completed_snap;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ completed_snap = ACCESS_ONCE(rsp->completed); /* outside of lock. */
+
+ /* Did another grace period end? */
+ if (rdp->completed != completed_snap) {
+
+ /* Advance callbacks. No harm if list empty. */
+ rdp->nxttail[RCU_DONE_TAIL] = rdp->nxttail[RCU_WAIT_TAIL];
+ rdp->nxttail[RCU_WAIT_TAIL] = rdp->nxttail[RCU_NEXT_READY_TAIL];
+ rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
+
+ /* Remember that we saw this grace-period completion. */
+ rdp->completed = completed_snap;
+ }
+ local_irq_restore(flags);
+}
+
+/*
+ * Similar to cpu_quiet(), for which it is a helper function. Allows
+ * a group of CPUs to be quieted at one go, though all the CPUs in the
+ * group must be represented by the same leaf rcu_node structure.
+ * That structure's lock must be held upon entry, and it is released
+ * before return.
+ */
+static void
+cpu_quiet_msk(unsigned long mask, struct rcu_state *rsp, struct rcu_node *rnp,
+ unsigned long flags)
+ __releases(rnp->lock)
+{
+ /* Walk up the rcu_node hierarchy. */
+ for (;;) {
+ if (!(rnp->qsmask & mask)) {
+
+ /* Our bit has already been cleared, so done. */
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+ rnp->qsmask &= ~mask;
+ if (rnp->qsmask != 0) {
+
+ /* Other bits still set at this level, so done. */
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+ mask = rnp->grpmask;
+ if (rnp->parent == NULL) {
+
+ /* No more levels. Exit loop holding root lock. */
+
+ break;
+ }
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ rnp = rnp->parent;
+ spin_lock_irqsave(&rnp->lock, flags);
+ }
+
+ /*
+ * Get here if we are the last CPU to pass through a quiescent
+ * state for this grace period. Clean up and let rcu_start_gp()
+ * start up the next grace period if one is needed. Note that
+ * we still hold rnp->lock, as required by rcu_start_gp(), which
+ * will release it.
+ */
+ rsp->completed = rsp->gpnum;
+ rcu_process_gp_end(rsp, rsp->rda[smp_processor_id()]);
+ rcu_start_gp(rsp, flags); /* releases rnp->lock. */
+}
+
+/*
+ * Record a quiescent state for the specified CPU, which must either be
+ * the current CPU or an offline CPU. The lastcomp argument is used to
+ * make sure we are still in the grace period of interest. We don't want
+ * to end the current grace period based on quiescent states detected in
+ * an earlier grace period!
+ */
+static void
+cpu_quiet(int cpu, struct rcu_state *rsp, struct rcu_data *rdp, long lastcomp)
+{
+ unsigned long flags;
+ unsigned long mask;
+ struct rcu_node *rnp;
+
+ rnp = rdp->mynode;
+ spin_lock_irqsave(&rnp->lock, flags);
+ if (lastcomp != ACCESS_ONCE(rsp->completed)) {
+
+ /*
+ * Someone beat us to it for this grace period, so leave.
+ * The race with GP start is resolved by the fact that we
+ * hold the leaf rcu_node lock, so that the per-CPU bits
+ * cannot yet be initialized -- so we would simply find our
+ * CPU's bit already cleared in cpu_quiet_msk() if this race
+ * occurred.
+ */
+ rdp->passed_quiesc = 0; /* try again later! */
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+ mask = rdp->grpmask;
+ if ((rnp->qsmask & mask) == 0) {
+ spin_unlock_irqrestore(&rnp->lock, flags);
+ } else {
+ rdp->qs_pending = 0;
+
+ /*
+ * This GP can't end until cpu checks in, so all of our
+ * callbacks can be processed during the next GP.
+ */
+ rdp = rsp->rda[smp_processor_id()];
+ rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
+
+ cpu_quiet_msk(mask, rsp, rnp, flags); /* releases rnp->lock */
+ }
+}
+
+/*
+ * Check to see if there is a new grace period of which this CPU
+ * is not yet aware, and if so, set up local rcu_data state for it.
+ * Otherwise, see if this CPU has just passed through its first
+ * quiescent state for this grace period, and record that fact if so.
+ */
+static void
+rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ /* If there is now a new grace period, record and return. */
+ if (check_for_new_grace_period(rsp, rdp))
+ return;
+
+ /*
+ * Does this CPU still need to do its part for current grace period?
+ * If no, return and let the other CPUs do their part as well.
+ */
+ if (!rdp->qs_pending)
+ return;
+
+ /*
+ * Was there a quiescent state since the beginning of the grace
+ * period? If no, then exit and wait for the next call.
+ */
+ if (!rdp->passed_quiesc)
+ return;
+
+ /* Tell RCU we are done (but cpu_quiet() will be the judge of that). */
+ cpu_quiet(rdp->cpu, rsp, rdp, rdp->passed_quiesc_completed);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+/*
+ * Remove the outgoing CPU from the bitmasks in the rcu_node hierarchy
+ * and move all callbacks from the outgoing CPU to the current one.
+ */
+static void __rcu_offline_cpu(int cpu, struct rcu_state *rsp)
+{
+ int i;
+ unsigned long flags;
+ long lastcomp;
+ unsigned long mask;
+ struct rcu_data *rdp = rsp->rda[cpu];
+ struct rcu_data *rdp_me;
+ struct rcu_node *rnp;
+
+ /* Exclude any attempts to start a new grace period. */
+ spin_lock_irqsave(&rsp->onofflock, flags);
+
+ /* Remove the outgoing CPU from the masks in the rcu_node hierarchy. */
+ rnp = rdp->mynode;
+ mask = rdp->grpmask; /* rnp->grplo is constant. */
+ do {
+ spin_lock(&rnp->lock); /* irqs already disabled. */
+ rnp->qsmaskinit &= ~mask;
+ if (rnp->qsmaskinit != 0) {
+ spin_unlock(&rnp->lock); /* irqs already disabled. */
+ break;
+ }
+ mask = rnp->grpmask;
+ spin_unlock(&rnp->lock); /* irqs already disabled. */
+ rnp = rnp->parent;
+ } while (rnp != NULL);
+ lastcomp = rsp->completed;
+
+ spin_unlock(&rsp->onofflock); /* irqs remain disabled. */
+
+ /* Being offline is a quiescent state, so go record it. */
+ cpu_quiet(cpu, rsp, rdp, lastcomp);
+
+ /*
+ * Move callbacks from the outgoing CPU to the running CPU.
+ * Note that the outgoing CPU is now quiscent, so it is now
+ * (uncharacteristically) safe to access it rcu_data structure.
+ * Note also that we must carefully retain the order of the
+ * outgoing CPU's callbacks in order for rcu_barrier() to work
+ * correctly. Finally, note that we start all the callbacks
+ * afresh, even those that have passed through a grace period
+ * and are therefore ready to invoke. The theory is that hotplug
+ * events are rare, and that if they are frequent enough to
+ * indefinitely delay callbacks, you have far worse things to
+ * be worrying about.
+ */
+ rdp_me = rsp->rda[smp_processor_id()];
+ if (rdp->nxtlist != NULL) {
+ *rdp_me->nxttail[RCU_NEXT_TAIL] = rdp->nxtlist;
+ rdp_me->nxttail[RCU_NEXT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
+ rdp->nxtlist = NULL;
+ for (i = 0; i < RCU_NEXT_SIZE; i++)
+ rdp->nxttail[i] = &rdp->nxtlist;
+ rdp_me->qlen += rdp->qlen;
+ rdp->qlen = 0;
+ }
+ local_irq_restore(flags);
+}
+
+/*
+ * Remove the specified CPU from the RCU hierarchy and move any pending
+ * callbacks that it might have to the current CPU. This code assumes
+ * that at least one CPU in the system will remain running at all times.
+ * Any attempt to offline -all- CPUs is likely to strand RCU callbacks.
+ */
+static void rcu_offline_cpu(int cpu)
+{
+ __rcu_offline_cpu(cpu, &rcu_state);
+ __rcu_offline_cpu(cpu, &rcu_bh_state);
+}
+
+#else /* #ifdef CONFIG_HOTPLUG_CPU */
+
+static void rcu_offline_cpu(int cpu)
+{
+}
+
+#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */
+
+/*
+ * Invoke any RCU callbacks that have made it to the end of their grace
+ * period. Thottle as specified by rdp->blimit.
+ */
+static void rcu_do_batch(struct rcu_data *rdp)
+{
+ unsigned long flags;
+ struct rcu_head *next, *list, **tail;
+ int count;
+
+ /* If no callbacks are ready, just return.*/
+ if (!cpu_has_callbacks_ready_to_invoke(rdp))
+ return;
+
+ /*
+ * Extract the list of ready callbacks, disabling to prevent
+ * races with call_rcu() from interrupt handlers.
+ */
+ local_irq_save(flags);
+ list = rdp->nxtlist;
+ rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL];
+ *rdp->nxttail[RCU_DONE_TAIL] = NULL;
+ tail = rdp->nxttail[RCU_DONE_TAIL];
+ for (count = RCU_NEXT_SIZE - 1; count >= 0; count--)
+ if (rdp->nxttail[count] == rdp->nxttail[RCU_DONE_TAIL])
+ rdp->nxttail[count] = &rdp->nxtlist;
+ local_irq_restore(flags);
+
+ /* Invoke callbacks. */
+ count = 0;
+ while (list) {
+ next = list->next;
+ prefetch(next);
+ list->func(list);
+ list = next;
+ if (++count >= rdp->blimit)
+ break;
+ }
+
+ local_irq_save(flags);
+
+ /* Update count, and requeue any remaining callbacks. */
+ rdp->qlen -= count;
+ if (list != NULL) {
+ *tail = rdp->nxtlist;
+ rdp->nxtlist = list;
+ for (count = 0; count < RCU_NEXT_SIZE; count++)
+ if (&rdp->nxtlist == rdp->nxttail[count])
+ rdp->nxttail[count] = tail;
+ else
+ break;
+ }
+
+ /* Reinstate batch limit if we have worked down the excess. */
+ if (rdp->blimit == LONG_MAX && rdp->qlen <= qlowmark)
+ rdp->blimit = blimit;
+
+ local_irq_restore(flags);
+
+ /* Re-raise the RCU softirq if there are callbacks remaining. */
+ if (cpu_has_callbacks_ready_to_invoke(rdp))
+ raise_softirq(RCU_SOFTIRQ);
+}
+
+/*
+ * Check to see if this CPU is in a non-context-switch quiescent state
+ * (user mode or idle loop for rcu, non-softirq execution for rcu_bh).
+ * Also schedule the RCU softirq handler.
+ *
+ * This function must be called with hardirqs disabled. It is normally
+ * invoked from the scheduling-clock interrupt. If rcu_pending returns
+ * false, there is no point in invoking rcu_check_callbacks().
+ */
+void rcu_check_callbacks(int cpu, int user)
+{
+ if (user ||
+ (idle_cpu(cpu) && !in_softirq() &&
+ hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
+
+ /*
+ * Get here if this CPU took its interrupt from user
+ * mode or from the idle loop, and if this is not a
+ * nested interrupt. In this case, the CPU is in
+ * a quiescent state, so count it.
+ *
+ * No memory barrier is required here because both
+ * rcu_qsctr_inc() and rcu_bh_qsctr_inc() reference
+ * only CPU-local variables that other CPUs neither
+ * access nor modify, at least not while the corresponding
+ * CPU is online.
+ */
+
+ rcu_qsctr_inc(cpu);
+ rcu_bh_qsctr_inc(cpu);
+
+ } else if (!in_softirq()) {
+
+ /*
+ * Get here if this CPU did not take its interrupt from
+ * softirq, in other words, if it is not interrupting
+ * a rcu_bh read-side critical section. This is an _bh
+ * critical section, so count it.
+ */
+
+ rcu_bh_qsctr_inc(cpu);
+ }
+ raise_softirq(RCU_SOFTIRQ);
+}
+
+#ifdef CONFIG_SMP
+
+/*
+ * Scan the leaf rcu_node structures, processing dyntick state for any that
+ * have not yet encountered a quiescent state, using the function specified.
+ * Returns 1 if the current grace period ends while scanning (possibly
+ * because we made it end).
+ */
+static int rcu_process_dyntick(struct rcu_state *rsp, long lastcomp,
+ int (*f)(struct rcu_data *))
+{
+ unsigned long bit;
+ int cpu;
+ unsigned long flags;
+ unsigned long mask;
+ struct rcu_node *rnp_cur = rsp->level[NUM_RCU_LVLS - 1];
+ struct rcu_node *rnp_end = &rsp->node[NUM_RCU_NODES];
+
+ for (; rnp_cur < rnp_end; rnp_cur++) {
+ mask = 0;
+ spin_lock_irqsave(&rnp_cur->lock, flags);
+ if (rsp->completed != lastcomp) {
+ spin_unlock_irqrestore(&rnp_cur->lock, flags);
+ return 1;
+ }
+ if (rnp_cur->qsmask == 0) {
+ spin_unlock_irqrestore(&rnp_cur->lock, flags);
+ continue;
+ }
+ cpu = rnp_cur->grplo;
+ bit = 1;
+ for (; cpu <= rnp_cur->grphi; cpu++, bit <<= 1) {
+ if ((rnp_cur->qsmask & bit) != 0 && f(rsp->rda[cpu]))
+ mask |= bit;
+ }
+ if (mask != 0 && rsp->completed == lastcomp) {
+
+ /* cpu_quiet_msk() releases rnp_cur->lock. */
+ cpu_quiet_msk(mask, rsp, rnp_cur, flags);
+ continue;
+ }
+ spin_unlock_irqrestore(&rnp_cur->lock, flags);
+ }
+ return 0;
+}
+
+/*
+ * 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)
+{
+ unsigned long flags;
+ long lastcomp;
+ struct rcu_data *rdp = rsp->rda[smp_processor_id()];
+ struct rcu_node *rnp = rcu_get_root(rsp);
+ u8 signaled;
+
+ if (ACCESS_ONCE(rsp->completed) == ACCESS_ONCE(rsp->gpnum))
+ return; /* No grace period in progress, nothing to force. */
+ if (!spin_trylock_irqsave(&rsp->fqslock, flags)) {
+ rsp->n_force_qs_lh++; /* Inexact, can lose counts. Tough! */
+ return; /* Someone else is already on the job. */
+ }
+ if (relaxed &&
+ (long)(rsp->jiffies_force_qs - jiffies) >= 0 &&
+ (rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending) >= 0)
+ goto unlock_ret; /* no emergency and done recently. */
+ rsp->n_force_qs++;
+ spin_lock(&rnp->lock);
+ lastcomp = rsp->completed;
+ signaled = rsp->signaled;
+ rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
+ rdp->n_rcu_pending_force_qs = rdp->n_rcu_pending +
+ RCU_JIFFIES_TILL_FORCE_QS;
+ if (lastcomp == rsp->gpnum) {
+ rsp->n_force_qs_ngp++;
+ spin_unlock(&rnp->lock);
+ goto unlock_ret; /* no GP in progress, time updated. */
+ }
+ spin_unlock(&rnp->lock);
+ switch (signaled) {
+ case RCU_GP_INIT:
+
+ break; /* grace period still initializing, ignore. */
+
+ case RCU_SAVE_DYNTICK:
+
+ if (RCU_SIGNAL_INIT != RCU_SAVE_DYNTICK)
+ break; /* So gcc recognizes the dead code. */
+
+ /* Record dyntick-idle state. */
+ if (rcu_process_dyntick(rsp, lastcomp,
+ dyntick_save_progress_counter))
+ goto unlock_ret;
+
+ /* Update state, record completion counter. */
+ spin_lock(&rnp->lock);
+ if (lastcomp == rsp->completed) {
+ rsp->signaled = RCU_FORCE_QS;
+ dyntick_record_completed(rsp, lastcomp);
+ }
+ spin_unlock(&rnp->lock);
+ break;
+
+ case RCU_FORCE_QS:
+
+ /* Check dyntick-idle state, send IPI to laggarts. */
+ if (rcu_process_dyntick(rsp, dyntick_recall_completed(rsp),
+ rcu_implicit_dynticks_qs))
+ goto unlock_ret;
+
+ /* Leave state in case more forcing is required. */
+
+ break;
+ }
+unlock_ret:
+ spin_unlock_irqrestore(&rsp->fqslock, flags);
+}
+
+#else /* #ifdef CONFIG_SMP */
+
+static void force_quiescent_state(struct rcu_state *rsp, int relaxed)
+{
+ set_need_resched();
+}
+
+#endif /* #else #ifdef CONFIG_SMP */
+
+/*
+ * This does the RCU processing work from softirq context for the
+ * specified rcu_state and rcu_data structures. This may be called
+ * only from the CPU to whom the rdp belongs.
+ */
+static void
+__rcu_process_callbacks(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ unsigned long flags;
+
+ /*
+ * If an RCU GP has gone long enough, go check for dyntick
+ * idle CPUs and, if needed, send resched IPIs.
+ */
+ if ((long)(ACCESS_ONCE(rsp->jiffies_force_qs) - jiffies) < 0 ||
+ (rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending) < 0)
+ force_quiescent_state(rsp, 1);
+
+ /*
+ * Advance callbacks in response to end of earlier grace
+ * period that some other CPU ended.
+ */
+ rcu_process_gp_end(rsp, rdp);
+
+ /* Update RCU state based on any recent quiescent states. */
+ rcu_check_quiescent_state(rsp, rdp);
+
+ /* Does this CPU require a not-yet-started grace period? */
+ if (cpu_needs_another_gp(rsp, rdp)) {
+ spin_lock_irqsave(&rcu_get_root(rsp)->lock, flags);
+ rcu_start_gp(rsp, flags); /* releases above lock */
+ }
+
+ /* If there are callbacks ready, invoke them. */
+ rcu_do_batch(rdp);
+}
+
+/*
+ * Do softirq processing for the current CPU.
+ */
+static void rcu_process_callbacks(struct softirq_action *unused)
+{
+ /*
+ * Memory references from any prior RCU read-side critical sections
+ * executed by the interrupted code must be seen before any RCU
+ * grace-period manipulations below.
+ */
+ smp_mb(); /* See above block comment. */
+
+ __rcu_process_callbacks(&rcu_state, &__get_cpu_var(rcu_data));
+ __rcu_process_callbacks(&rcu_bh_state, &__get_cpu_var(rcu_bh_data));
+
+ /*
+ * Memory references from any later RCU read-side critical sections
+ * executed by the interrupted code must be seen after any RCU
+ * grace-period manipulations above.
+ */
+ smp_mb(); /* See above block comment. */
+}
+
+static void
+__call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
+ struct rcu_state *rsp)
+{
+ unsigned long flags;
+ struct rcu_data *rdp;
+
+ 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
+ * end, but never vice versa, since this CPU has to pass through
+ * a quiescent state betweentimes.
+ */
+ local_irq_save(flags);
+ rdp = rsp->rda[smp_processor_id()];
+ rcu_process_gp_end(rsp, rdp);
+ check_for_new_grace_period(rsp, rdp);
+
+ /* Add the callback to our list. */
+ *rdp->nxttail[RCU_NEXT_TAIL] = head;
+ rdp->nxttail[RCU_NEXT_TAIL] = &head->next;
+
+ /* Start a new grace period if one not already started. */
+ if (ACCESS_ONCE(rsp->completed) == ACCESS_ONCE(rsp->gpnum)) {
+ unsigned long nestflag;
+ struct rcu_node *rnp_root = rcu_get_root(rsp);
+
+ spin_lock_irqsave(&rnp_root->lock, nestflag);
+ rcu_start_gp(rsp, nestflag); /* releases rnp_root->lock. */
+ }
+
+ /* Force the grace period if too many callbacks or too long waiting. */
+ if (unlikely(++rdp->qlen > qhimark)) {
+ rdp->blimit = LONG_MAX;
+ force_quiescent_state(rsp, 0);
+ } else if ((long)(ACCESS_ONCE(rsp->jiffies_force_qs) - jiffies) < 0 ||
+ (rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending) < 0)
+ force_quiescent_state(rsp, 1);
+ local_irq_restore(flags);
+}
+
+/*
+ * Queue an RCU callback for invocation after a grace period.
+ */
+void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+{
+ __call_rcu(head, func, &rcu_state);
+}
+EXPORT_SYMBOL_GPL(call_rcu);
+
+/*
+ * Queue an RCU for invocation after a quicker grace period.
+ */
+void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+{
+ __call_rcu(head, func, &rcu_bh_state);
+}
+EXPORT_SYMBOL_GPL(call_rcu_bh);
+
+/*
+ * Check to see if there is any immediate RCU-related work to be done
+ * by the current CPU, for the specified type of RCU, returning 1 if so.
+ * The checks are in order of increasing expense: checks that can be
+ * carried out against CPU-local state are performed first. However,
+ * we must check for CPU stalls first, else we might not get a chance.
+ */
+static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
+{
+ rdp->n_rcu_pending++;
+
+ /* Check for CPU stalls, if enabled. */
+ check_cpu_stall(rsp, rdp);
+
+ /* Is the RCU core waiting for a quiescent state from this CPU? */
+ if (rdp->qs_pending)
+ return 1;
+
+ /* Does this CPU have callbacks ready to invoke? */
+ if (cpu_has_callbacks_ready_to_invoke(rdp))
+ return 1;
+
+ /* Has RCU gone idle with this CPU needing another grace period? */
+ if (cpu_needs_another_gp(rsp, rdp))
+ return 1;
+
+ /* Has another RCU grace period completed? */
+ if (ACCESS_ONCE(rsp->completed) != rdp->completed) /* outside of lock */
+ return 1;
+
+ /* Has a new RCU grace period started? */
+ if (ACCESS_ONCE(rsp->gpnum) != rdp->gpnum) /* outside of lock */
+ return 1;
+
+ /* Has an RCU GP gone long enough to send resched IPIs &c? */
+ if (ACCESS_ONCE(rsp->completed) != ACCESS_ONCE(rsp->gpnum) &&
+ ((long)(ACCESS_ONCE(rsp->jiffies_force_qs) - jiffies) < 0 ||
+ (rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending) < 0))
+ return 1;
+
+ /* nothing to do */
+ return 0;
+}
+
+/*
+ * Check to see if there is any immediate RCU-related work to be done
+ * by the current CPU, returning 1 if so. This function is part of the
+ * RCU implementation; it is -not- an exported member of the RCU API.
+ */
+int rcu_pending(int cpu)
+{
+ return __rcu_pending(&rcu_state, &per_cpu(rcu_data, cpu)) ||
+ __rcu_pending(&rcu_bh_state, &per_cpu(rcu_bh_data, cpu));
+}
+
+/*
+ * Check to see if any future RCU-related work will need to be done
+ * by the current CPU, even if none need be done immediately, returning
+ * 1 if so. This function is part of the RCU implementation; it is -not-
+ * an exported member of the RCU API.
+ */
+int rcu_needs_cpu(int cpu)
+{
+ /* RCU callbacks either ready or pending? */
+ return per_cpu(rcu_data, cpu).nxtlist ||
+ per_cpu(rcu_bh_data, cpu).nxtlist;
+}
+
+/*
+ * Initialize a CPU's per-CPU RCU data. We take this "scorched earth"
+ * approach so that we don't have to worry about how long the CPU has
+ * been gone, or whether it ever was online previously. We do trust the
+ * ->mynode field, as it is constant for a given struct rcu_data and
+ * initialized during early boot.
+ *
+ * Note that only one online or offline event can be happening at a given
+ * time. Note also that we can accept some slop in the rsp->completed
+ * access due to the fact that this CPU cannot possibly have any RCU
+ * callbacks in flight yet.
+ */
+static void
+rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
+{
+ unsigned long flags;
+ int i;
+ long lastcomp;
+ unsigned long mask;
+ struct rcu_data *rdp = rsp->rda[cpu];
+ struct rcu_node *rnp = rcu_get_root(rsp);
+
+ /* Set up local state, ensuring consistent view of global state. */
+ spin_lock_irqsave(&rnp->lock, flags);
+ lastcomp = rsp->completed;
+ rdp->completed = lastcomp;
+ rdp->gpnum = lastcomp;
+ rdp->passed_quiesc = 0; /* We could be racing with new GP, */
+ rdp->qs_pending = 1; /* so set up to respond to current GP. */
+ rdp->beenonline = 1; /* We have now been online. */
+ rdp->passed_quiesc_completed = lastcomp - 1;
+ rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo);
+ rdp->nxtlist = NULL;
+ for (i = 0; i < RCU_NEXT_SIZE; i++)
+ rdp->nxttail[i] = &rdp->nxtlist;
+ rdp->qlen = 0;
+ rdp->blimit = blimit;
+#ifdef CONFIG_NO_HZ
+ rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
+#endif /* #ifdef CONFIG_NO_HZ */
+ rdp->cpu = cpu;
+ spin_unlock(&rnp->lock); /* irqs remain disabled. */
+
+ /*
+ * A new grace period might start here. If so, we won't be part
+ * of it, but that is OK, as we are currently in a quiescent state.
+ */
+
+ /* Exclude any attempts to start a new GP on large systems. */
+ spin_lock(&rsp->onofflock); /* irqs already disabled. */
+
+ /* Add CPU to rcu_node bitmasks. */
+ rnp = rdp->mynode;
+ mask = rdp->grpmask;
+ do {
+ /* Exclude any attempts to start a new GP on small systems. */
+ spin_lock(&rnp->lock); /* irqs already disabled. */
+ rnp->qsmaskinit |= mask;
+ mask = rnp->grpmask;
+ spin_unlock(&rnp->lock); /* irqs already disabled. */
+ rnp = rnp->parent;
+ } while (rnp != NULL && !(rnp->qsmaskinit & mask));
+
+ spin_unlock(&rsp->onofflock); /* irqs remain disabled. */
+
+ /*
+ * A new grace period might start here. If so, we will be part of
+ * it, and its gpnum will be greater than ours, so we will
+ * participate. It is also possible for the gpnum to have been
+ * incremented before this function was called, and the bitmasks
+ * to not be filled out until now, in which case we will also
+ * participate due to our gpnum being behind.
+ */
+
+ /* Since it is coming online, the CPU is in a quiescent state. */
+ cpu_quiet(cpu, rsp, rdp, lastcomp);
+ local_irq_restore(flags);
+}
+
+static void __cpuinit rcu_online_cpu(int cpu)
+{
+#ifdef CONFIG_NO_HZ
+ struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
+
+ rdtp->dynticks_nesting = 1;
+ rdtp->dynticks |= 1; /* need consecutive #s even for hotplug. */
+ rdtp->dynticks_nmi = (rdtp->dynticks_nmi + 1) & ~0x1;
+#endif /* #ifdef CONFIG_NO_HZ */
+ rcu_init_percpu_data(cpu, &rcu_state);
+ rcu_init_percpu_data(cpu, &rcu_bh_state);
+ open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
+}
+
+/*
+ * Handle CPU online/offline notifcation events.
+ */
+static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ long cpu = (long)hcpu;
+
+ switch (action) {
+ case CPU_UP_PREPARE:
+ case CPU_UP_PREPARE_FROZEN:
+ rcu_online_cpu(cpu);
+ break;
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ case CPU_UP_CANCELED:
+ case CPU_UP_CANCELED_FROZEN:
+ rcu_offline_cpu(cpu);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+/*
+ * Compute the per-level fanout, either using the exact fanout specified
+ * or balancing the tree, depending on CONFIG_RCU_FANOUT_EXACT.
+ */
+#ifdef CONFIG_RCU_FANOUT_EXACT
+static void __init rcu_init_levelspread(struct rcu_state *rsp)
+{
+ int i;
+
+ for (i = NUM_RCU_LVLS - 1; i >= 0; i--)
+ rsp->levelspread[i] = CONFIG_RCU_FANOUT;
+}
+#else /* #ifdef CONFIG_RCU_FANOUT_EXACT */
+static void __init rcu_init_levelspread(struct rcu_state *rsp)
+{
+ int ccur;
+ int cprv;
+ int i;
+
+ cprv = NR_CPUS;
+ for (i = NUM_RCU_LVLS - 1; i >= 0; i--) {
+ ccur = rsp->levelcnt[i];
+ rsp->levelspread[i] = (cprv + ccur - 1) / ccur;
+ cprv = ccur;
+ }
+}
+#endif /* #else #ifdef CONFIG_RCU_FANOUT_EXACT */
+
+/*
+ * Helper function for rcu_init() that initializes one rcu_state structure.
+ */
+static void __init rcu_init_one(struct rcu_state *rsp)
+{
+ int cpustride = 1;
+ int i;
+ int j;
+ struct rcu_node *rnp;
+
+ /* Initialize the level-tracking arrays. */
+
+ for (i = 1; i < NUM_RCU_LVLS; i++)
+ rsp->level[i] = rsp->level[i - 1] + rsp->levelcnt[i - 1];
+ rcu_init_levelspread(rsp);
+
+ /* Initialize the elements themselves, starting from the leaves. */
+
+ for (i = NUM_RCU_LVLS - 1; i >= 0; i--) {
+ cpustride *= rsp->levelspread[i];
+ rnp = rsp->level[i];
+ for (j = 0; j < rsp->levelcnt[i]; j++, rnp++) {
+ spin_lock_init(&rnp->lock);
+ rnp->qsmask = 0;
+ rnp->qsmaskinit = 0;
+ rnp->grplo = j * cpustride;
+ rnp->grphi = (j + 1) * cpustride - 1;
+ if (rnp->grphi >= NR_CPUS)
+ rnp->grphi = NR_CPUS - 1;
+ if (i == 0) {
+ rnp->grpnum = 0;
+ rnp->grpmask = 0;
+ rnp->parent = NULL;
+ } else {
+ rnp->grpnum = j % rsp->levelspread[i - 1];
+ rnp->grpmask = 1UL << rnp->grpnum;
+ rnp->parent = rsp->level[i - 1] +
+ j / rsp->levelspread[i - 1];
+ }
+ rnp->level = i;
+ }
+ }
+}
+
+/*
+ * Helper macro for __rcu_init(). To be used nowhere else!
+ * Assigns leaf node pointers into each CPU's rcu_data structure.
+ */
+#define RCU_DATA_PTR_INIT(rsp, rcu_data) \
+do { \
+ rnp = (rsp)->level[NUM_RCU_LVLS - 1]; \
+ j = 0; \
+ for_each_possible_cpu(i) { \
+ if (i > rnp[j].grphi) \
+ j++; \
+ per_cpu(rcu_data, i).mynode = &rnp[j]; \
+ (rsp)->rda[i] = &per_cpu(rcu_data, i); \
+ } \
+} while (0)
+
+static struct notifier_block __cpuinitdata rcu_nb = {
+ .notifier_call = rcu_cpu_notify,
+};
+
+void __init __rcu_init(void)
+{
+ int i; /* All used by RCU_DATA_PTR_INIT(). */
+ int j;
+ struct rcu_node *rnp;
+
+ printk(KERN_WARNING "Experimental hierarchical RCU implementation.\n");
+#ifdef CONFIG_RCU_CPU_STALL_DETECTOR
+ printk(KERN_INFO "RCU-based detection of stalled CPUs is enabled.\n");
+#endif /* #ifdef CONFIG_RCU_CPU_STALL_DETECTOR */
+ rcu_init_one(&rcu_state);
+ RCU_DATA_PTR_INIT(&rcu_state, rcu_data);
+ rcu_init_one(&rcu_bh_state);
+ RCU_DATA_PTR_INIT(&rcu_bh_state, rcu_bh_data);
+
+ for_each_online_cpu(i)
+ rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long)i);
+ /* Register notifier for non-boot CPUs */
+ register_cpu_notifier(&rcu_nb);
+ printk(KERN_WARNING "Experimental hierarchical RCU init done.\n");
+}
+
+module_param(blimit, int, 0);
+module_param(qhimark, int, 0);
+module_param(qlowmark, int, 0);
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c
new file mode 100644
index 000000000000..d6db3e837826
--- /dev/null
+++ b/kernel/rcutree_trace.c
@@ -0,0 +1,271 @@
+/*
+ * Read-Copy Update tracing for classic implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * 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.
+ *
+ * Copyright IBM Corporation, 2008
+ *
+ * Papers: http://www.rdrop.com/users/paulmck/RCU
+ *
+ * For detailed explanation of Read-Copy Update mechanism see -
+ * Documentation/RCU
+ *
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/rcupdate.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <asm/atomic.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/moduleparam.h>
+#include <linux/percpu.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
+{
+ if (!rdp->beenonline)
+ return;
+ seq_printf(m, "%3d%cc=%ld g=%ld pq=%d pqc=%ld qp=%d rpfq=%ld rp=%x",
+ rdp->cpu,
+ cpu_is_offline(rdp->cpu) ? '!' : ' ',
+ rdp->completed, rdp->gpnum,
+ rdp->passed_quiesc, rdp->passed_quiesc_completed,
+ rdp->qs_pending,
+ rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending,
+ (int)(rdp->n_rcu_pending & 0xffff));
+#ifdef CONFIG_NO_HZ
+ seq_printf(m, " dt=%d/%d dn=%d df=%lu",
+ rdp->dynticks->dynticks,
+ rdp->dynticks->dynticks_nesting,
+ rdp->dynticks->dynticks_nmi,
+ rdp->dynticks_fqs);
+#endif /* #ifdef CONFIG_NO_HZ */
+ seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi);
+ seq_printf(m, " ql=%ld b=%ld\n", rdp->qlen, rdp->blimit);
+}
+
+#define PRINT_RCU_DATA(name, func, m) \
+ do { \
+ int _p_r_d_i; \
+ \
+ for_each_possible_cpu(_p_r_d_i) \
+ func(m, &per_cpu(name, _p_r_d_i)); \
+ } while (0)
+
+static int show_rcudata(struct seq_file *m, void *unused)
+{
+ seq_puts(m, "rcu:\n");
+ PRINT_RCU_DATA(rcu_data, print_one_rcu_data, m);
+ seq_puts(m, "rcu_bh:\n");
+ PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data, m);
+ return 0;
+}
+
+static int rcudata_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_rcudata, NULL);
+}
+
+static struct file_operations rcudata_fops = {
+ .owner = THIS_MODULE,
+ .open = rcudata_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)
+{
+ if (!rdp->beenonline)
+ return;
+ seq_printf(m, "%d,%s,%ld,%ld,%d,%ld,%d,%ld,%ld",
+ rdp->cpu,
+ cpu_is_offline(rdp->cpu) ? "\"Y\"" : "\"N\"",
+ rdp->completed, rdp->gpnum,
+ rdp->passed_quiesc, rdp->passed_quiesc_completed,
+ rdp->qs_pending,
+ rdp->n_rcu_pending_force_qs - rdp->n_rcu_pending,
+ rdp->n_rcu_pending);
+#ifdef CONFIG_NO_HZ
+ seq_printf(m, ",%d,%d,%d,%lu",
+ rdp->dynticks->dynticks,
+ rdp->dynticks->dynticks_nesting,
+ rdp->dynticks->dynticks_nmi,
+ rdp->dynticks_fqs);
+#endif /* #ifdef CONFIG_NO_HZ */
+ seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi);
+ seq_printf(m, ",%ld,%ld\n", rdp->qlen, rdp->blimit);
+}
+
+static int show_rcudata_csv(struct seq_file *m, void *unused)
+{
+ seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\",\"rpfq\",\"rp\",");
+#ifdef CONFIG_NO_HZ
+ seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\",");
+#endif /* #ifdef CONFIG_NO_HZ */
+ seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n");
+ seq_puts(m, "\"rcu:\"\n");
+ PRINT_RCU_DATA(rcu_data, print_one_rcu_data_csv, m);
+ seq_puts(m, "\"rcu_bh:\"\n");
+ PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data_csv, m);
+ return 0;
+}
+
+static int rcudata_csv_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_rcudata_csv, NULL);
+}
+
+static struct file_operations rcudata_csv_fops = {
+ .owner = THIS_MODULE,
+ .open = rcudata_csv_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
+{
+ int level = 0;
+ struct rcu_node *rnp;
+
+ seq_printf(m, "c=%ld g=%ld s=%d jfq=%ld j=%x "
+ "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu\n",
+ rsp->completed, rsp->gpnum, rsp->signaled,
+ (long)(rsp->jiffies_force_qs - jiffies),
+ (int)(jiffies & 0xffff),
+ rsp->n_force_qs, rsp->n_force_qs_ngp,
+ rsp->n_force_qs - rsp->n_force_qs_ngp,
+ rsp->n_force_qs_lh);
+ for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) {
+ if (rnp->level != level) {
+ seq_puts(m, "\n");
+ level = rnp->level;
+ }
+ seq_printf(m, "%lx/%lx %d:%d ^%d ",
+ rnp->qsmask, rnp->qsmaskinit,
+ rnp->grplo, rnp->grphi, rnp->grpnum);
+ }
+ seq_puts(m, "\n");
+}
+
+static int show_rcuhier(struct seq_file *m, void *unused)
+{
+ seq_puts(m, "rcu:\n");
+ print_one_rcu_state(m, &rcu_state);
+ seq_puts(m, "rcu_bh:\n");
+ print_one_rcu_state(m, &rcu_bh_state);
+ return 0;
+}
+
+static int rcuhier_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_rcuhier, NULL);
+}
+
+static struct file_operations rcuhier_fops = {
+ .owner = THIS_MODULE,
+ .open = rcuhier_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int show_rcugp(struct seq_file *m, void *unused)
+{
+ seq_printf(m, "rcu: completed=%ld gpnum=%ld\n",
+ rcu_state.completed, rcu_state.gpnum);
+ seq_printf(m, "rcu_bh: completed=%ld gpnum=%ld\n",
+ rcu_bh_state.completed, rcu_bh_state.gpnum);
+ return 0;
+}
+
+static int rcugp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_rcugp, NULL);
+}
+
+static struct file_operations rcugp_fops = {
+ .owner = THIS_MODULE,
+ .open = rcugp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *rcudir, *datadir, *datadir_csv, *hierdir, *gpdir;
+static int __init rcuclassic_trace_init(void)
+{
+ rcudir = debugfs_create_dir("rcu", NULL);
+ if (!rcudir)
+ goto out;
+
+ datadir = debugfs_create_file("rcudata", 0444, rcudir,
+ NULL, &rcudata_fops);
+ if (!datadir)
+ goto free_out;
+
+ datadir_csv = debugfs_create_file("rcudata.csv", 0444, rcudir,
+ NULL, &rcudata_csv_fops);
+ if (!datadir_csv)
+ goto free_out;
+
+ gpdir = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);
+ if (!gpdir)
+ goto free_out;
+
+ hierdir = debugfs_create_file("rcuhier", 0444, rcudir,
+ NULL, &rcuhier_fops);
+ if (!hierdir)
+ goto free_out;
+ return 0;
+free_out:
+ if (datadir)
+ debugfs_remove(datadir);
+ if (datadir_csv)
+ debugfs_remove(datadir_csv);
+ if (gpdir)
+ debugfs_remove(gpdir);
+ debugfs_remove(rcudir);
+out:
+ return 1;
+}
+
+static void __exit rcuclassic_trace_cleanup(void)
+{
+ debugfs_remove(datadir);
+ debugfs_remove(datadir_csv);
+ debugfs_remove(gpdir);
+ debugfs_remove(hierdir);
+ debugfs_remove(rcudir);
+}
+
+
+module_init(rcuclassic_trace_init);
+module_exit(rcuclassic_trace_cleanup);
+
+MODULE_AUTHOR("Paul E. McKenney");
+MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation");
+MODULE_LICENSE("GPL");
diff --git a/kernel/resource.c b/kernel/resource.c
index 4337063663ef..e633106b12f6 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -853,6 +853,15 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size)
if (PFN_DOWN(p->start) <= PFN_DOWN(addr) &&
PFN_DOWN(p->end) >= PFN_DOWN(addr + size - 1))
continue;
+ /*
+ * if a resource is "BUSY", it's not a hardware resource
+ * but a driver mapping of such a resource; we don't want
+ * to warn for those; some drivers legitimately map only
+ * partial hardware resources. (example: vesafb)
+ */
+ if (p->flags & IORESOURCE_BUSY)
+ continue;
+
printk(KERN_WARNING "resource map sanity check conflict: "
"0x%llx 0x%llx 0x%llx 0x%llx %s\n",
(unsigned long long)addr,
diff --git a/kernel/sched.c b/kernel/sched.c
index 756d981d91a4..27ba1d642f0f 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -209,7 +209,6 @@ void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime)
hrtimer_init(&rt_b->rt_period_timer,
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
rt_b->rt_period_timer.function = sched_rt_period_timer;
- rt_b->rt_period_timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
}
static inline int rt_bandwidth_enabled(void)
@@ -361,7 +360,9 @@ static inline struct task_group *task_group(struct task_struct *p)
struct task_group *tg;
#ifdef CONFIG_USER_SCHED
- tg = p->user->tg;
+ rcu_read_lock();
+ tg = __task_cred(p)->user->tg;
+ rcu_read_unlock();
#elif defined(CONFIG_CGROUP_SCHED)
tg = container_of(task_subsys_state(p, cpu_cgroup_subsys_id),
struct task_group, css);
@@ -610,6 +611,8 @@ struct rq {
#ifdef CONFIG_SCHEDSTATS
/* latency stats */
struct sched_info rq_sched_info;
+ unsigned long long rq_cpu_time;
+ /* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */
/* sys_sched_yield() stats */
unsigned int yld_exp_empty;
@@ -1143,7 +1146,6 @@ static void init_rq_hrtick(struct rq *rq)
hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
rq->hrtick_timer.function = hrtick;
- rq->hrtick_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU;
}
#else /* CONFIG_SCHED_HRTICK */
static inline void hrtick_clear(struct rq *rq)
@@ -1871,6 +1873,8 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
clock_offset = old_rq->clock - new_rq->clock;
+ trace_sched_migrate_task(p, task_cpu(p), new_cpu);
+
#ifdef CONFIG_SCHEDSTATS
if (p->se.wait_start)
p->se.wait_start -= clock_offset;
@@ -2277,6 +2281,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
smp_wmb();
rq = task_rq_lock(p, &flags);
+ update_rq_clock(rq);
old_state = p->state;
if (!(old_state & state))
goto out;
@@ -2334,12 +2339,11 @@ out_activate:
schedstat_inc(p, se.nr_wakeups_local);
else
schedstat_inc(p, se.nr_wakeups_remote);
- update_rq_clock(rq);
activate_task(rq, p, 1);
success = 1;
out_running:
- trace_sched_wakeup(rq, p);
+ trace_sched_wakeup(rq, p, success);
check_preempt_curr(rq, p, sync);
p->state = TASK_RUNNING;
@@ -2472,7 +2476,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
p->sched_class->task_new(rq, p);
inc_nr_running(rq);
}
- trace_sched_wakeup_new(rq, p);
+ trace_sched_wakeup_new(rq, p, 1);
check_preempt_curr(rq, p, 0);
#ifdef CONFIG_SMP
if (p->sched_class->task_wake_up)
@@ -2851,7 +2855,6 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu)
|| unlikely(!cpu_active(dest_cpu)))
goto out;
- trace_sched_migrate_task(rq, p, dest_cpu);
/* force the process onto the specified CPU */
if (migrate_task(p, dest_cpu, &req)) {
/* Need to wait for migration thread (might exit: take ref). */
@@ -5187,6 +5190,22 @@ __setscheduler(struct rq *rq, struct task_struct *p, int policy, int prio)
set_load_weight(p);
}
+/*
+ * check the target process has a UID that matches the current process's
+ */
+static bool check_same_owner(struct task_struct *p)
+{
+ const struct cred *cred = current_cred(), *pcred;
+ bool match;
+
+ rcu_read_lock();
+ pcred = __task_cred(p);
+ match = (cred->euid == pcred->euid ||
+ cred->euid == pcred->uid);
+ rcu_read_unlock();
+ return match;
+}
+
static int __sched_setscheduler(struct task_struct *p, int policy,
struct sched_param *param, bool user)
{
@@ -5246,8 +5265,7 @@ recheck:
return -EPERM;
/* can't change other user's priorities */
- if ((current->euid != p->euid) &&
- (current->euid != p->uid))
+ if (!check_same_owner(p))
return -EPERM;
}
@@ -5486,8 +5504,7 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
goto out_free_cpus_allowed;
}
retval = -EPERM;
- if ((current->euid != p->euid) && (current->euid != p->uid) &&
- !capable(CAP_SYS_NICE))
+ if (!check_same_owner(p) && !capable(CAP_SYS_NICE))
goto out_unlock;
retval = security_task_setscheduler(p, 0, NULL);
@@ -9423,6 +9440,41 @@ cpuacct_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp)
kfree(ca);
}
+static u64 cpuacct_cpuusage_read(struct cpuacct *ca, int cpu)
+{
+ u64 *cpuusage = percpu_ptr(ca->cpuusage, cpu);
+ u64 data;
+
+#ifndef CONFIG_64BIT
+ /*
+ * Take rq->lock to make 64-bit read safe on 32-bit platforms.
+ */
+ spin_lock_irq(&cpu_rq(cpu)->lock);
+ data = *cpuusage;
+ spin_unlock_irq(&cpu_rq(cpu)->lock);
+#else
+ data = *cpuusage;
+#endif
+
+ return data;
+}
+
+static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val)
+{
+ u64 *cpuusage = percpu_ptr(ca->cpuusage, cpu);
+
+#ifndef CONFIG_64BIT
+ /*
+ * Take rq->lock to make 64-bit write safe on 32-bit platforms.
+ */
+ spin_lock_irq(&cpu_rq(cpu)->lock);
+ *cpuusage = val;
+ spin_unlock_irq(&cpu_rq(cpu)->lock);
+#else
+ *cpuusage = val;
+#endif
+}
+
/* return total cpu usage (in nanoseconds) of a group */
static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft)
{
@@ -9430,17 +9482,8 @@ static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft)
u64 totalcpuusage = 0;
int i;
- for_each_possible_cpu(i) {
- u64 *cpuusage = percpu_ptr(ca->cpuusage, i);
-
- /*
- * Take rq->lock to make 64-bit addition safe on 32-bit
- * platforms.
- */
- spin_lock_irq(&cpu_rq(i)->lock);
- totalcpuusage += *cpuusage;
- spin_unlock_irq(&cpu_rq(i)->lock);
- }
+ for_each_present_cpu(i)
+ totalcpuusage += cpuacct_cpuusage_read(ca, i);
return totalcpuusage;
}
@@ -9457,23 +9500,39 @@ static int cpuusage_write(struct cgroup *cgrp, struct cftype *cftype,
goto out;
}
- for_each_possible_cpu(i) {
- u64 *cpuusage = percpu_ptr(ca->cpuusage, i);
+ for_each_present_cpu(i)
+ cpuacct_cpuusage_write(ca, i, 0);
- spin_lock_irq(&cpu_rq(i)->lock);
- *cpuusage = 0;
- spin_unlock_irq(&cpu_rq(i)->lock);
- }
out:
return err;
}
+static int cpuacct_percpu_seq_read(struct cgroup *cgroup, struct cftype *cft,
+ struct seq_file *m)
+{
+ struct cpuacct *ca = cgroup_ca(cgroup);
+ u64 percpu;
+ int i;
+
+ for_each_present_cpu(i) {
+ percpu = cpuacct_cpuusage_read(ca, i);
+ seq_printf(m, "%llu ", (unsigned long long) percpu);
+ }
+ seq_printf(m, "\n");
+ return 0;
+}
+
static struct cftype files[] = {
{
.name = "usage",
.read_u64 = cpuusage_read,
.write_u64 = cpuusage_write,
},
+ {
+ .name = "usage_percpu",
+ .read_seq_string = cpuacct_percpu_seq_read,
+ },
+
};
static int cpuacct_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
index 36b5e34fa99e..56c0efe902a7 100644
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -492,6 +492,8 @@ static void update_curr(struct cfs_rq *cfs_rq)
* overflow on 32 bits):
*/
delta_exec = (unsigned long)(now - curr->exec_start);
+ if (!delta_exec)
+ return;
__update_curr(cfs_rq, curr, delta_exec);
curr->exec_start = now;
@@ -1361,12 +1363,11 @@ static void check_preempt_wakeup(struct rq *rq, struct task_struct *p, int sync)
{
struct task_struct *curr = rq->curr;
struct sched_entity *se = &curr->se, *pse = &p->se;
+ struct cfs_rq *cfs_rq = task_cfs_rq(curr);
- if (unlikely(rt_prio(p->prio))) {
- struct cfs_rq *cfs_rq = task_cfs_rq(curr);
+ update_curr(cfs_rq);
- update_rq_clock(rq);
- update_curr(cfs_rq);
+ if (unlikely(rt_prio(p->prio))) {
resched_task(curr);
return;
}
diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c
index 1bbd99014011..833b6d44483c 100644
--- a/kernel/sched_rt.c
+++ b/kernel/sched_rt.c
@@ -77,7 +77,7 @@ static inline u64 sched_rt_period(struct rt_rq *rt_rq)
}
#define for_each_leaf_rt_rq(rt_rq, rq) \
- list_for_each_entry(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
+ list_for_each_entry_rcu(rt_rq, &rq->leaf_rt_rq_list, leaf_rt_rq_list)
static inline struct rq *rq_of_rt_rq(struct rt_rq *rt_rq)
{
diff --git a/kernel/sched_stats.h b/kernel/sched_stats.h
index 5fcf0e184586..f2773b5d1226 100644
--- a/kernel/sched_stats.h
+++ b/kernel/sched_stats.h
@@ -31,7 +31,7 @@ static int show_schedstat(struct seq_file *seq, void *v)
rq->yld_act_empty, rq->yld_exp_empty, rq->yld_count,
rq->sched_switch, rq->sched_count, rq->sched_goidle,
rq->ttwu_count, rq->ttwu_local,
- rq->rq_sched_info.cpu_time,
+ rq->rq_cpu_time,
rq->rq_sched_info.run_delay, rq->rq_sched_info.pcount);
seq_printf(seq, "\n");
@@ -124,7 +124,7 @@ static inline void
rq_sched_info_depart(struct rq *rq, unsigned long long delta)
{
if (rq)
- rq->rq_sched_info.cpu_time += delta;
+ rq->rq_cpu_time += delta;
}
static inline void
@@ -237,7 +237,6 @@ static inline void sched_info_depart(struct task_struct *t)
unsigned long long delta = task_rq(t)->clock -
t->sched_info.last_arrival;
- t->sched_info.cpu_time += delta;
rq_sched_info_depart(task_rq(t), delta);
if (t->state == TASK_RUNNING)
diff --git a/kernel/signal.c b/kernel/signal.c
index e9afe63da24b..8e95855ff3cf 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -179,6 +179,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
return sig;
}
+/*
+ * allocate a new signal queue record
+ * - this may be called without locks if and only if t == current, otherwise an
+ * appopriate lock must be held to stop the target task from exiting
+ */
static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
int override_rlimit)
{
@@ -186,11 +191,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
struct user_struct *user;
/*
- * In order to avoid problems with "switch_user()", we want to make
- * sure that the compiler doesn't re-load "t->user"
+ * We won't get problems with the target's UID changing under us
+ * because changing it requires RCU be used, and if t != current, the
+ * caller must be holding the RCU readlock (by way of a spinlock) and
+ * we use RCU protection here
*/
- user = t->user;
- barrier();
+ user = get_uid(__task_cred(t)->user);
atomic_inc(&user->sigpending);
if (override_rlimit ||
atomic_read(&user->sigpending) <=
@@ -198,12 +204,14 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags,
q = kmem_cache_alloc(sigqueue_cachep, flags);
if (unlikely(q == NULL)) {
atomic_dec(&user->sigpending);
+ free_uid(user);
} else {
INIT_LIST_HEAD(&q->list);
q->flags = 0;
- q->user = get_uid(user);
+ q->user = user;
}
- return(q);
+
+ return q;
}
static void __sigqueue_free(struct sigqueue *q)
@@ -564,10 +572,12 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s)
/*
* Bad permissions for sending the signal
+ * - the caller must hold at least the RCU read lock
*/
static int check_kill_permission(int sig, struct siginfo *info,
struct task_struct *t)
{
+ const struct cred *cred = current_cred(), *tcred;
struct pid *sid;
int error;
@@ -581,8 +591,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
if (error)
return error;
- if ((current->euid ^ t->suid) && (current->euid ^ t->uid) &&
- (current->uid ^ t->suid) && (current->uid ^ t->uid) &&
+ tcred = __task_cred(t);
+ if ((cred->euid ^ tcred->suid) &&
+ (cred->euid ^ tcred->uid) &&
+ (cred->uid ^ tcred->suid) &&
+ (cred->uid ^ tcred->uid) &&
!capable(CAP_KILL)) {
switch (sig) {
case SIGCONT:
@@ -846,7 +859,7 @@ static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
q->info.si_errno = 0;
q->info.si_code = SI_USER;
q->info.si_pid = task_pid_vnr(current);
- q->info.si_uid = current->uid;
+ q->info.si_uid = current_uid();
break;
case (unsigned long) SEND_SIG_PRIV:
q->info.si_signo = sig;
@@ -1010,6 +1023,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long
return sighand;
}
+/*
+ * send signal info to all the members of a group
+ * - the caller must hold the RCU read lock at least
+ */
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
unsigned long flags;
@@ -1031,8 +1048,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
/*
* __kill_pgrp_info() sends a signal to a process group: this is what the tty
* control characters do (^C, ^Z etc)
+ * - the caller must hold at least a readlock on tasklist_lock
*/
-
int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp)
{
struct task_struct *p = NULL;
@@ -1088,6 +1105,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
{
int ret = -EINVAL;
struct task_struct *p;
+ const struct cred *pcred;
if (!valid_signal(sig))
return ret;
@@ -1098,9 +1116,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
ret = -ESRCH;
goto out_unlock;
}
- if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info)))
- && (euid != p->suid) && (euid != p->uid)
- && (uid != p->suid) && (uid != p->uid)) {
+ pcred = __task_cred(p);
+ if ((info == SEND_SIG_NOINFO ||
+ (!is_si_special(info) && SI_FROMUSER(info))) &&
+ euid != pcred->suid && euid != pcred->uid &&
+ uid != pcred->suid && uid != pcred->uid) {
ret = -EPERM;
goto out_unlock;
}
@@ -1371,10 +1391,9 @@ int do_notify_parent(struct task_struct *tsk, int sig)
*/
rcu_read_lock();
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
+ info.si_uid = __task_cred(tsk)->uid;
rcu_read_unlock();
- info.si_uid = tsk->uid;
-
thread_group_cputime(tsk, &cputime);
info.si_utime = cputime_to_jiffies(cputime.utime);
info.si_stime = cputime_to_jiffies(cputime.stime);
@@ -1442,10 +1461,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
*/
rcu_read_lock();
info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);
+ info.si_uid = __task_cred(tsk)->uid;
rcu_read_unlock();
- info.si_uid = tsk->uid;
-
info.si_utime = cputime_to_clock_t(tsk->utime);
info.si_stime = cputime_to_clock_t(tsk->stime);
@@ -1600,7 +1618,7 @@ void ptrace_notify(int exit_code)
info.si_signo = SIGTRAP;
info.si_code = exit_code;
info.si_pid = task_pid_vnr(current);
- info.si_uid = current->uid;
+ info.si_uid = current_uid();
/* Let the debugger run. */
spin_lock_irq(&current->sighand->siglock);
@@ -1712,7 +1730,7 @@ static int ptrace_signal(int signr, siginfo_t *info,
info->si_errno = 0;
info->si_code = SI_USER;
info->si_pid = task_pid_vnr(current->parent);
- info->si_uid = current->parent->uid;
+ info->si_uid = task_uid(current->parent);
}
/* If the (new) signal is now blocked, requeue it. */
@@ -2213,7 +2231,7 @@ sys_kill(pid_t pid, int sig)
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = task_tgid_vnr(current);
- info.si_uid = current->uid;
+ info.si_uid = current_uid();
return kill_something_info(sig, &info, pid);
}
@@ -2230,7 +2248,7 @@ static int do_tkill(pid_t tgid, pid_t pid, int sig)
info.si_errno = 0;
info.si_code = SI_TKILL;
info.si_pid = task_tgid_vnr(current);
- info.si_uid = current->uid;
+ info.si_uid = current_uid();
rcu_read_lock();
p = find_task_by_vpid(pid);
diff --git a/kernel/smp.c b/kernel/smp.c
index 75c8dde58c55..5cfa0e5e3e88 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -24,8 +24,8 @@ struct call_function_data {
struct call_single_data csd;
spinlock_t lock;
unsigned int refs;
- cpumask_t cpumask;
struct rcu_head rcu_head;
+ unsigned long cpumask_bits[];
};
struct call_single_queue {
@@ -110,13 +110,13 @@ void generic_smp_call_function_interrupt(void)
list_for_each_entry_rcu(data, &call_function_queue, csd.list) {
int refs;
- if (!cpu_isset(cpu, data->cpumask))
+ if (!cpumask_test_cpu(cpu, to_cpumask(data->cpumask_bits)))
continue;
data->csd.func(data->csd.info);
spin_lock(&data->lock);
- cpu_clear(cpu, data->cpumask);
+ cpumask_clear_cpu(cpu, to_cpumask(data->cpumask_bits));
WARN_ON(data->refs == 0);
data->refs--;
refs = data->refs;
@@ -223,7 +223,7 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
local_irq_save(flags);
func(info);
local_irq_restore(flags);
- } else if ((unsigned)cpu < NR_CPUS && cpu_online(cpu)) {
+ } else if ((unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) {
struct call_single_data *data = NULL;
if (!wait) {
@@ -266,51 +266,19 @@ void __smp_call_function_single(int cpu, struct call_single_data *data)
generic_exec_single(cpu, data);
}
-/* Dummy function */
-static void quiesce_dummy(void *unused)
-{
-}
-
-/*
- * Ensure stack based data used in call function mask is safe to free.
- *
- * This is needed by smp_call_function_mask when using on-stack data, because
- * a single call function queue is shared by all CPUs, and any CPU may pick up
- * the data item on the queue at any time before it is deleted. So we need to
- * ensure that all CPUs have transitioned through a quiescent state after
- * this call.
- *
- * This is a very slow function, implemented by sending synchronous IPIs to
- * all possible CPUs. For this reason, we have to alloc data rather than use
- * stack based data even in the case of synchronous calls. The stack based
- * data is then just used for deadlock/oom fallback which will be very rare.
- *
- * If a faster scheme can be made, we could go back to preferring stack based
- * data -- the data allocation/free is non-zero cost.
- */
-static void smp_call_function_mask_quiesce_stack(cpumask_t mask)
-{
- struct call_single_data data;
- int cpu;
-
- data.func = quiesce_dummy;
- data.info = NULL;
-
- for_each_cpu_mask(cpu, mask) {
- data.flags = CSD_FLAG_WAIT;
- generic_exec_single(cpu, &data);
- }
-}
+/* FIXME: Shim for archs using old arch_send_call_function_ipi API. */
+#ifndef arch_send_call_function_ipi_mask
+#define arch_send_call_function_ipi_mask(maskp) \
+ arch_send_call_function_ipi(*(maskp))
+#endif
/**
- * smp_call_function_mask(): Run a function on a set of other CPUs.
- * @mask: The set of cpus to run on.
+ * smp_call_function_many(): Run a function on a set of other CPUs.
+ * @mask: The set of cpus to run on (only runs on online subset).
* @func: The function to run. This must be fast and non-blocking.
* @info: An arbitrary pointer to pass to the function.
* @wait: If true, wait (atomically) until function has completed on other CPUs.
*
- * Returns 0 on success, else a negative status code.
- *
* If @wait is true, then returns once @func has returned. Note that @wait
* will be implicitly turned on in case of allocation failures, since
* we fall back to on-stack allocation.
@@ -319,53 +287,57 @@ static void smp_call_function_mask_quiesce_stack(cpumask_t mask)
* hardware interrupt handler or from a bottom half handler. Preemption
* must be disabled when calling this function.
*/
-int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
- int wait)
+void smp_call_function_many(const struct cpumask *mask,
+ void (*func)(void *), void *info,
+ bool wait)
{
- struct call_function_data d;
- struct call_function_data *data = NULL;
- cpumask_t allbutself;
+ struct call_function_data *data;
unsigned long flags;
- int cpu, num_cpus;
- int slowpath = 0;
+ int cpu, next_cpu;
/* Can deadlock when called with interrupts disabled */
WARN_ON(irqs_disabled());
- cpu = smp_processor_id();
- allbutself = cpu_online_map;
- cpu_clear(cpu, allbutself);
- cpus_and(mask, mask, allbutself);
- num_cpus = cpus_weight(mask);
-
- /*
- * If zero CPUs, return. If just a single CPU, turn this request
- * into a targetted single call instead since it's faster.
- */
- if (!num_cpus)
- return 0;
- else if (num_cpus == 1) {
- cpu = first_cpu(mask);
- return smp_call_function_single(cpu, func, info, wait);
+ /* So, what's a CPU they want? Ignoring this one. */
+ cpu = cpumask_first_and(mask, cpu_online_mask);
+ if (cpu == smp_processor_id())
+ cpu = cpumask_next_and(cpu, mask, cpu_online_mask);
+ /* No online cpus? We're done. */
+ if (cpu >= nr_cpu_ids)
+ return;
+
+ /* Do we have another CPU which isn't us? */
+ next_cpu = cpumask_next_and(cpu, mask, cpu_online_mask);
+ if (next_cpu == smp_processor_id())
+ next_cpu = cpumask_next_and(next_cpu, mask, cpu_online_mask);
+
+ /* Fastpath: do that cpu by itself. */
+ if (next_cpu >= nr_cpu_ids) {
+ smp_call_function_single(cpu, func, info, wait);
+ return;
}
- data = kmalloc(sizeof(*data), GFP_ATOMIC);
- if (data) {
- data->csd.flags = CSD_FLAG_ALLOC;
- if (wait)
- data->csd.flags |= CSD_FLAG_WAIT;
- } else {
- data = &d;
- data->csd.flags = CSD_FLAG_WAIT;
- wait = 1;
- slowpath = 1;
+ data = kmalloc(sizeof(*data) + cpumask_size(), GFP_ATOMIC);
+ if (unlikely(!data)) {
+ /* Slow path. */
+ for_each_online_cpu(cpu) {
+ if (cpu == smp_processor_id())
+ continue;
+ if (cpumask_test_cpu(cpu, mask))
+ smp_call_function_single(cpu, func, info, wait);
+ }
+ return;
}
spin_lock_init(&data->lock);
+ data->csd.flags = CSD_FLAG_ALLOC;
+ if (wait)
+ data->csd.flags |= CSD_FLAG_WAIT;
data->csd.func = func;
data->csd.info = info;
- data->refs = num_cpus;
- data->cpumask = mask;
+ cpumask_and(to_cpumask(data->cpumask_bits), mask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), to_cpumask(data->cpumask_bits));
+ data->refs = cpumask_weight(to_cpumask(data->cpumask_bits));
spin_lock_irqsave(&call_function_lock, flags);
list_add_tail_rcu(&data->csd.list, &call_function_queue);
@@ -377,18 +349,13 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info,
smp_mb();
/* Send a message to all CPUs in the map */
- arch_send_call_function_ipi(mask);
+ arch_send_call_function_ipi_mask(to_cpumask(data->cpumask_bits));
/* optionally wait for the CPUs to complete */
- if (wait) {
+ if (wait)
csd_flag_wait(&data->csd);
- if (unlikely(slowpath))
- smp_call_function_mask_quiesce_stack(mask);
- }
-
- return 0;
}
-EXPORT_SYMBOL(smp_call_function_mask);
+EXPORT_SYMBOL(smp_call_function_many);
/**
* smp_call_function(): Run a function on all other CPUs.
@@ -396,7 +363,7 @@ EXPORT_SYMBOL(smp_call_function_mask);
* @info: An arbitrary pointer to pass to the function.
* @wait: If true, wait (atomically) until function has completed on other CPUs.
*
- * Returns 0 on success, else a negative status code.
+ * Returns 0.
*
* If @wait is true, then returns once @func has returned; otherwise
* it returns just before the target cpu calls @func. In case of allocation
@@ -407,12 +374,10 @@ EXPORT_SYMBOL(smp_call_function_mask);
*/
int smp_call_function(void (*func)(void *), void *info, int wait)
{
- int ret;
-
preempt_disable();
- ret = smp_call_function_mask(cpu_online_map, func, info, wait);
+ smp_call_function_many(cpu_online_mask, func, info, wait);
preempt_enable();
- return ret;
+ return 0;
}
EXPORT_SYMBOL(smp_call_function);
diff --git a/kernel/softirq.c b/kernel/softirq.c
index e7c69a720d69..b7568d7def23 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -102,20 +102,6 @@ void local_bh_disable(void)
EXPORT_SYMBOL(local_bh_disable);
-void __local_bh_enable(void)
-{
- WARN_ON_ONCE(in_irq());
-
- /*
- * softirqs should never be enabled by __local_bh_enable(),
- * it always nests inside local_bh_enable() sections:
- */
- WARN_ON_ONCE(softirq_count() == SOFTIRQ_OFFSET);
-
- sub_preempt_count(SOFTIRQ_OFFSET);
-}
-EXPORT_SYMBOL_GPL(__local_bh_enable);
-
/*
* Special-case - softirqs can safely be enabled in
* cond_resched_softirq(), or by __do_softirq(),
@@ -269,6 +255,7 @@ void irq_enter(void)
{
int cpu = smp_processor_id();
+ rcu_irq_enter();
if (idle_cpu(cpu) && !in_interrupt()) {
__irq_enter();
tick_check_idle(cpu);
@@ -295,9 +282,9 @@ void irq_exit(void)
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
- if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
- tick_nohz_stop_sched_tick(0);
rcu_irq_exit();
+ if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
+ tick_nohz_stop_sched_tick(0);
#endif
preempt_enable_no_resched();
}
@@ -746,7 +733,7 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb,
break;
/* Unbind so it can run. Fall thru. */
kthread_bind(per_cpu(ksoftirqd, hotcpu),
- any_online_cpu(cpu_online_map));
+ cpumask_any(cpu_online_mask));
case CPU_DEAD:
case CPU_DEAD_FROZEN: {
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
diff --git a/kernel/softlockup.c b/kernel/softlockup.c
index 1ab790c67b17..d9188c66278a 100644
--- a/kernel/softlockup.c
+++ b/kernel/softlockup.c
@@ -303,17 +303,15 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
break;
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
- check_cpu = any_online_cpu(cpu_online_map);
+ check_cpu = cpumask_any(cpu_online_mask);
wake_up_process(per_cpu(watchdog_task, hotcpu));
break;
#ifdef CONFIG_HOTPLUG_CPU
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
if (hotcpu == check_cpu) {
- cpumask_t temp_cpu_online_map = cpu_online_map;
-
- cpu_clear(hotcpu, temp_cpu_online_map);
- check_cpu = any_online_cpu(temp_cpu_online_map);
+ /* Pick any other online cpu. */
+ check_cpu = cpumask_any_but(cpu_online_mask, hotcpu);
}
break;
@@ -323,7 +321,7 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
break;
/* Unbind so it can run. Fall thru. */
kthread_bind(per_cpu(watchdog_task, hotcpu),
- any_online_cpu(cpu_online_map));
+ cpumask_any(cpu_online_mask));
case CPU_DEAD:
case CPU_DEAD_FROZEN:
p = per_cpu(watchdog_task, hotcpu);
diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c
index 94b527ef1d1e..eb212f8f8bc8 100644
--- a/kernel/stacktrace.c
+++ b/kernel/stacktrace.c
@@ -6,6 +6,7 @@
* Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
*/
#include <linux/sched.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/stacktrace.h>
@@ -24,3 +25,13 @@ void print_stack_trace(struct stack_trace *trace, int spaces)
}
EXPORT_SYMBOL_GPL(print_stack_trace);
+/*
+ * Architectures that do not implement save_stack_trace_tsk get this
+ * weak alias and a once-per-bootup warning (whenever this facility
+ * is utilized - for example by procfs):
+ */
+__weak void
+save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ WARN_ONCE(1, KERN_INFO "save_stack_trace_tsk() not implemented yet.\n");
+}
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index 24e8ceacc388..286c41722e8c 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -69,10 +69,10 @@ static void stop_cpu(struct work_struct *unused)
int err;
if (!active_cpus) {
- if (cpu == first_cpu(cpu_online_map))
+ if (cpu == cpumask_first(cpu_online_mask))
smdata = &active;
} else {
- if (cpu_isset(cpu, *active_cpus))
+ if (cpumask_test_cpu(cpu, active_cpus))
smdata = &active;
}
/* Simple state machine */
@@ -109,7 +109,7 @@ static int chill(void *unused)
return 0;
}
-int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
+int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
{
struct work_struct *sm_work;
int i, ret;
@@ -142,7 +142,7 @@ int __stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
return ret;
}
-int stop_machine(int (*fn)(void *), void *data, const cpumask_t *cpus)
+int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
{
int ret;
diff --git a/kernel/sys.c b/kernel/sys.c
index 5fc3a0cfb994..d356d79e84ac 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -112,12 +112,17 @@ EXPORT_SYMBOL(cad_pid);
void (*pm_power_off_prepare)(void);
+/*
+ * set the priority of a task
+ * - the caller must hold the RCU read lock
+ */
static int set_one_prio(struct task_struct *p, int niceval, int error)
{
+ const struct cred *cred = current_cred(), *pcred = __task_cred(p);
int no_nice;
- if (p->uid != current->euid &&
- p->euid != current->euid && !capable(CAP_SYS_NICE)) {
+ if (pcred->uid != cred->euid &&
+ pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
error = -EPERM;
goto out;
}
@@ -141,6 +146,7 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
{
struct task_struct *g, *p;
struct user_struct *user;
+ const struct cred *cred = current_cred();
int error = -EINVAL;
struct pid *pgrp;
@@ -174,18 +180,18 @@ asmlinkage long sys_setpriority(int which, int who, int niceval)
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
break;
case PRIO_USER:
- user = current->user;
+ user = (struct user_struct *) cred->user;
if (!who)
- who = current->uid;
- else
- if ((who != current->uid) && !(user = find_user(who)))
- goto out_unlock; /* No processes for this user */
+ who = cred->uid;
+ else if ((who != cred->uid) &&
+ !(user = find_user(who)))
+ goto out_unlock; /* No processes for this user */
do_each_thread(g, p)
- if (p->uid == who)
+ if (__task_cred(p)->uid == who)
error = set_one_prio(p, niceval, error);
while_each_thread(g, p);
- if (who != current->uid)
+ if (who != cred->uid)
free_uid(user); /* For find_user() */
break;
}
@@ -205,6 +211,7 @@ asmlinkage long sys_getpriority(int which, int who)
{
struct task_struct *g, *p;
struct user_struct *user;
+ const struct cred *cred = current_cred();
long niceval, retval = -ESRCH;
struct pid *pgrp;
@@ -236,21 +243,21 @@ asmlinkage long sys_getpriority(int which, int who)
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
break;
case PRIO_USER:
- user = current->user;
+ user = (struct user_struct *) cred->user;
if (!who)
- who = current->uid;
- else
- if ((who != current->uid) && !(user = find_user(who)))
- goto out_unlock; /* No processes for this user */
+ who = cred->uid;
+ else if ((who != cred->uid) &&
+ !(user = find_user(who)))
+ goto out_unlock; /* No processes for this user */
do_each_thread(g, p)
- if (p->uid == who) {
+ if (__task_cred(p)->uid == who) {
niceval = 20 - task_nice(p);
if (niceval > retval)
retval = niceval;
}
while_each_thread(g, p);
- if (who != current->uid)
+ if (who != cred->uid)
free_uid(user); /* for find_user() */
break;
}
@@ -472,46 +479,48 @@ void ctrl_alt_del(void)
*/
asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
{
- int old_rgid = current->gid;
- int old_egid = current->egid;
- int new_rgid = old_rgid;
- int new_egid = old_egid;
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
+
retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
if (retval)
- return retval;
+ goto error;
+ retval = -EPERM;
if (rgid != (gid_t) -1) {
- if ((old_rgid == rgid) ||
- (current->egid==rgid) ||
+ if (old->gid == rgid ||
+ old->egid == rgid ||
capable(CAP_SETGID))
- new_rgid = rgid;
+ new->gid = rgid;
else
- return -EPERM;
+ goto error;
}
if (egid != (gid_t) -1) {
- if ((old_rgid == egid) ||
- (current->egid == egid) ||
- (current->sgid == egid) ||
+ if (old->gid == egid ||
+ old->egid == egid ||
+ old->sgid == egid ||
capable(CAP_SETGID))
- new_egid = egid;
+ new->egid = egid;
else
- return -EPERM;
- }
- if (new_egid != old_egid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
+ goto error;
}
+
if (rgid != (gid_t) -1 ||
- (egid != (gid_t) -1 && egid != old_rgid))
- current->sgid = new_egid;
- current->fsgid = new_egid;
- current->egid = new_egid;
- current->gid = new_rgid;
- key_fsgid_changed(current);
- proc_id_connector(current, PROC_EVENT_GID);
- return 0;
+ (egid != (gid_t) -1 && egid != old->gid))
+ new->sgid = new->egid;
+ new->fsgid = new->egid;
+
+ return commit_creds(new);
+
+error:
+ abort_creds(new);
+ return retval;
}
/*
@@ -521,56 +530,54 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
*/
asmlinkage long sys_setgid(gid_t gid)
{
- int old_egid = current->egid;
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
+
retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
if (retval)
- return retval;
+ goto error;
- if (capable(CAP_SETGID)) {
- if (old_egid != gid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->gid = current->egid = current->sgid = current->fsgid = gid;
- } else if ((gid == current->gid) || (gid == current->sgid)) {
- if (old_egid != gid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->egid = current->fsgid = gid;
- }
+ retval = -EPERM;
+ if (capable(CAP_SETGID))
+ new->gid = new->egid = new->sgid = new->fsgid = gid;
+ else if (gid == old->gid || gid == old->sgid)
+ new->egid = new->fsgid = gid;
else
- return -EPERM;
+ goto error;
- key_fsgid_changed(current);
- proc_id_connector(current, PROC_EVENT_GID);
- return 0;
+ return commit_creds(new);
+
+error:
+ abort_creds(new);
+ return retval;
}
-static int set_user(uid_t new_ruid, int dumpclear)
+/*
+ * change the user struct in a credentials set to match the new UID
+ */
+static int set_user(struct cred *new)
{
struct user_struct *new_user;
- new_user = alloc_uid(current->nsproxy->user_ns, new_ruid);
+ new_user = alloc_uid(current_user_ns(), new->uid);
if (!new_user)
return -EAGAIN;
if (atomic_read(&new_user->processes) >=
current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
- new_user != current->nsproxy->user_ns->root_user) {
+ new_user != INIT_USER) {
free_uid(new_user);
return -EAGAIN;
}
- switch_uid(new_user);
-
- if (dumpclear) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->uid = new_ruid;
+ free_uid(new->user);
+ new->user = new_user;
return 0;
}
@@ -591,54 +598,56 @@ static int set_user(uid_t new_ruid, int dumpclear)
*/
asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
{
- int old_ruid, old_euid, old_suid, new_ruid, new_euid;
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
+
retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
if (retval)
- return retval;
-
- new_ruid = old_ruid = current->uid;
- new_euid = old_euid = current->euid;
- old_suid = current->suid;
+ goto error;
+ retval = -EPERM;
if (ruid != (uid_t) -1) {
- new_ruid = ruid;
- if ((old_ruid != ruid) &&
- (current->euid != ruid) &&
+ new->uid = ruid;
+ if (old->uid != ruid &&
+ old->euid != ruid &&
!capable(CAP_SETUID))
- return -EPERM;
+ goto error;
}
if (euid != (uid_t) -1) {
- new_euid = euid;
- if ((old_ruid != euid) &&
- (current->euid != euid) &&
- (current->suid != euid) &&
+ new->euid = euid;
+ if (old->uid != euid &&
+ old->euid != euid &&
+ old->suid != euid &&
!capable(CAP_SETUID))
- return -EPERM;
+ goto error;
}
- if (new_ruid != old_ruid && set_user(new_ruid, new_euid != old_euid) < 0)
- return -EAGAIN;
+ retval = -EAGAIN;
+ if (new->uid != old->uid && set_user(new) < 0)
+ goto error;
- if (new_euid != old_euid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->fsuid = current->euid = new_euid;
if (ruid != (uid_t) -1 ||
- (euid != (uid_t) -1 && euid != old_ruid))
- current->suid = current->euid;
- current->fsuid = current->euid;
+ (euid != (uid_t) -1 && euid != old->uid))
+ new->suid = new->euid;
+ new->fsuid = new->euid;
- key_fsuid_changed(current);
- proc_id_connector(current, PROC_EVENT_UID);
-
- return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE);
-}
+ retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
+ if (retval < 0)
+ goto error;
+ return commit_creds(new);
+error:
+ abort_creds(new);
+ return retval;
+}
/*
* setuid() is implemented like SysV with SAVED_IDS
@@ -653,36 +662,41 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
*/
asmlinkage long sys_setuid(uid_t uid)
{
- int old_euid = current->euid;
- int old_ruid, old_suid, new_suid;
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
+
retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
if (retval)
- return retval;
+ goto error;
- old_ruid = current->uid;
- old_suid = current->suid;
- new_suid = old_suid;
-
+ retval = -EPERM;
if (capable(CAP_SETUID)) {
- if (uid != old_ruid && set_user(uid, old_euid != uid) < 0)
- return -EAGAIN;
- new_suid = uid;
- } else if ((uid != current->uid) && (uid != new_suid))
- return -EPERM;
-
- if (old_euid != uid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
+ new->suid = new->uid = uid;
+ if (uid != old->uid && set_user(new) < 0) {
+ retval = -EAGAIN;
+ goto error;
+ }
+ } else if (uid != old->uid && uid != new->suid) {
+ goto error;
}
- current->fsuid = current->euid = uid;
- current->suid = new_suid;
- key_fsuid_changed(current);
- proc_id_connector(current, PROC_EVENT_UID);
+ new->fsuid = new->euid = uid;
+
+ retval = security_task_fix_setuid(new, old, LSM_SETID_ID);
+ if (retval < 0)
+ goto error;
- return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID);
+ return commit_creds(new);
+
+error:
+ abort_creds(new);
+ return retval;
}
@@ -692,54 +706,63 @@ asmlinkage long sys_setuid(uid_t uid)
*/
asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
- int old_ruid = current->uid;
- int old_euid = current->euid;
- int old_suid = current->suid;
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
if (retval)
- return retval;
+ goto error;
+ old = current_cred();
+ retval = -EPERM;
if (!capable(CAP_SETUID)) {
- if ((ruid != (uid_t) -1) && (ruid != current->uid) &&
- (ruid != current->euid) && (ruid != current->suid))
- return -EPERM;
- if ((euid != (uid_t) -1) && (euid != current->uid) &&
- (euid != current->euid) && (euid != current->suid))
- return -EPERM;
- if ((suid != (uid_t) -1) && (suid != current->uid) &&
- (suid != current->euid) && (suid != current->suid))
- return -EPERM;
+ if (ruid != (uid_t) -1 && ruid != old->uid &&
+ ruid != old->euid && ruid != old->suid)
+ goto error;
+ if (euid != (uid_t) -1 && euid != old->uid &&
+ euid != old->euid && euid != old->suid)
+ goto error;
+ if (suid != (uid_t) -1 && suid != old->uid &&
+ suid != old->euid && suid != old->suid)
+ goto error;
}
+
+ retval = -EAGAIN;
if (ruid != (uid_t) -1) {
- if (ruid != current->uid && set_user(ruid, euid != current->euid) < 0)
- return -EAGAIN;
+ new->uid = ruid;
+ if (ruid != old->uid && set_user(new) < 0)
+ goto error;
}
- if (euid != (uid_t) -1) {
- if (euid != current->euid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->euid = euid;
- }
- current->fsuid = current->euid;
+ if (euid != (uid_t) -1)
+ new->euid = euid;
if (suid != (uid_t) -1)
- current->suid = suid;
+ new->suid = suid;
+ new->fsuid = new->euid;
+
+ retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
+ if (retval < 0)
+ goto error;
- key_fsuid_changed(current);
- proc_id_connector(current, PROC_EVENT_UID);
+ return commit_creds(new);
- return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES);
+error:
+ abort_creds(new);
+ return retval;
}
asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __user *suid)
{
+ const struct cred *cred = current_cred();
int retval;
- if (!(retval = put_user(current->uid, ruid)) &&
- !(retval = put_user(current->euid, euid)))
- retval = put_user(current->suid, suid);
+ if (!(retval = put_user(cred->uid, ruid)) &&
+ !(retval = put_user(cred->euid, euid)))
+ retval = put_user(cred->suid, suid);
return retval;
}
@@ -749,48 +772,55 @@ asmlinkage long sys_getresuid(uid_t __user *ruid, uid_t __user *euid, uid_t __us
*/
asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
{
+ const struct cred *old;
+ struct cred *new;
int retval;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
+
retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
if (retval)
- return retval;
+ goto error;
+ retval = -EPERM;
if (!capable(CAP_SETGID)) {
- if ((rgid != (gid_t) -1) && (rgid != current->gid) &&
- (rgid != current->egid) && (rgid != current->sgid))
- return -EPERM;
- if ((egid != (gid_t) -1) && (egid != current->gid) &&
- (egid != current->egid) && (egid != current->sgid))
- return -EPERM;
- if ((sgid != (gid_t) -1) && (sgid != current->gid) &&
- (sgid != current->egid) && (sgid != current->sgid))
- return -EPERM;
+ if (rgid != (gid_t) -1 && rgid != old->gid &&
+ rgid != old->egid && rgid != old->sgid)
+ goto error;
+ if (egid != (gid_t) -1 && egid != old->gid &&
+ egid != old->egid && egid != old->sgid)
+ goto error;
+ if (sgid != (gid_t) -1 && sgid != old->gid &&
+ sgid != old->egid && sgid != old->sgid)
+ goto error;
}
- if (egid != (gid_t) -1) {
- if (egid != current->egid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
- }
- current->egid = egid;
- }
- current->fsgid = current->egid;
+
if (rgid != (gid_t) -1)
- current->gid = rgid;
+ new->gid = rgid;
+ if (egid != (gid_t) -1)
+ new->egid = egid;
if (sgid != (gid_t) -1)
- current->sgid = sgid;
+ new->sgid = sgid;
+ new->fsgid = new->egid;
- key_fsgid_changed(current);
- proc_id_connector(current, PROC_EVENT_GID);
- return 0;
+ return commit_creds(new);
+
+error:
+ abort_creds(new);
+ return retval;
}
asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __user *sgid)
{
+ const struct cred *cred = current_cred();
int retval;
- if (!(retval = put_user(current->gid, rgid)) &&
- !(retval = put_user(current->egid, egid)))
- retval = put_user(current->sgid, sgid);
+ if (!(retval = put_user(cred->gid, rgid)) &&
+ !(retval = put_user(cred->egid, egid)))
+ retval = put_user(cred->sgid, sgid);
return retval;
}
@@ -804,27 +834,35 @@ asmlinkage long sys_getresgid(gid_t __user *rgid, gid_t __user *egid, gid_t __us
*/
asmlinkage long sys_setfsuid(uid_t uid)
{
- int old_fsuid;
+ const struct cred *old;
+ struct cred *new;
+ uid_t old_fsuid;
- old_fsuid = current->fsuid;
- if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS))
- return old_fsuid;
+ new = prepare_creds();
+ if (!new)
+ return current_fsuid();
+ old = current_cred();
+ old_fsuid = old->fsuid;
- if (uid == current->uid || uid == current->euid ||
- uid == current->suid || uid == current->fsuid ||
+ if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
+ goto error;
+
+ if (uid == old->uid || uid == old->euid ||
+ uid == old->suid || uid == old->fsuid ||
capable(CAP_SETUID)) {
if (uid != old_fsuid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
+ new->fsuid = uid;
+ if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
+ goto change_okay;
}
- current->fsuid = uid;
}
- key_fsuid_changed(current);
- proc_id_connector(current, PROC_EVENT_UID);
-
- security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS);
+error:
+ abort_creds(new);
+ return old_fsuid;
+change_okay:
+ commit_creds(new);
return old_fsuid;
}
@@ -833,23 +871,34 @@ asmlinkage long sys_setfsuid(uid_t uid)
*/
asmlinkage long sys_setfsgid(gid_t gid)
{
- int old_fsgid;
+ const struct cred *old;
+ struct cred *new;
+ gid_t old_fsgid;
+
+ new = prepare_creds();
+ if (!new)
+ return current_fsgid();
+ old = current_cred();
+ old_fsgid = old->fsgid;
- old_fsgid = current->fsgid;
if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
- return old_fsgid;
+ goto error;
- if (gid == current->gid || gid == current->egid ||
- gid == current->sgid || gid == current->fsgid ||
+ if (gid == old->gid || gid == old->egid ||
+ gid == old->sgid || gid == old->fsgid ||
capable(CAP_SETGID)) {
if (gid != old_fsgid) {
- set_dumpable(current->mm, suid_dumpable);
- smp_wmb();
+ new->fsgid = gid;
+ goto change_okay;
}
- current->fsgid = gid;
- key_fsgid_changed(current);
- proc_id_connector(current, PROC_EVENT_GID);
}
+
+error:
+ abort_creds(new);
+ return old_fsgid;
+
+change_okay:
+ commit_creds(new);
return old_fsgid;
}
@@ -1118,7 +1167,7 @@ EXPORT_SYMBOL(groups_free);
/* export the group_info to a user-space array */
static int groups_to_user(gid_t __user *grouplist,
- struct group_info *group_info)
+ const struct group_info *group_info)
{
int i;
unsigned int count = group_info->ngroups;
@@ -1186,7 +1235,7 @@ static void groups_sort(struct group_info *group_info)
}
/* a simple bsearch */
-int groups_search(struct group_info *group_info, gid_t grp)
+int groups_search(const struct group_info *group_info, gid_t grp)
{
unsigned int left, right;
@@ -1208,51 +1257,74 @@ int groups_search(struct group_info *group_info, gid_t grp)
return 0;
}
-/* validate and set current->group_info */
-int set_current_groups(struct group_info *group_info)
+/**
+ * set_groups - Change a group subscription in a set of credentials
+ * @new: The newly prepared set of credentials to alter
+ * @group_info: The group list to install
+ *
+ * Validate a group subscription and, if valid, insert it into a set
+ * of credentials.
+ */
+int set_groups(struct cred *new, struct group_info *group_info)
{
int retval;
- struct group_info *old_info;
retval = security_task_setgroups(group_info);
if (retval)
return retval;
+ put_group_info(new->group_info);
groups_sort(group_info);
get_group_info(group_info);
+ new->group_info = group_info;
+ return 0;
+}
+
+EXPORT_SYMBOL(set_groups);
- task_lock(current);
- old_info = current->group_info;
- current->group_info = group_info;
- task_unlock(current);
+/**
+ * set_current_groups - Change current's group subscription
+ * @group_info: The group list to impose
+ *
+ * Validate a group subscription and, if valid, impose it upon current's task
+ * security record.
+ */
+int set_current_groups(struct group_info *group_info)
+{
+ struct cred *new;
+ int ret;
- put_group_info(old_info);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- return 0;
+ ret = set_groups(new, group_info);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
+ }
+
+ return commit_creds(new);
}
EXPORT_SYMBOL(set_current_groups);
asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
{
- int i = 0;
-
- /*
- * SMP: Nobody else can change our grouplist. Thus we are
- * safe.
- */
+ const struct cred *cred = current_cred();
+ int i;
if (gidsetsize < 0)
return -EINVAL;
/* no need to grab task_lock here; it cannot change */
- i = current->group_info->ngroups;
+ i = cred->group_info->ngroups;
if (gidsetsize) {
if (i > gidsetsize) {
i = -EINVAL;
goto out;
}
- if (groups_to_user(grouplist, current->group_info)) {
+ if (groups_to_user(grouplist, cred->group_info)) {
i = -EFAULT;
goto out;
}
@@ -1296,9 +1368,11 @@ asmlinkage long sys_setgroups(int gidsetsize, gid_t __user *grouplist)
*/
int in_group_p(gid_t grp)
{
+ const struct cred *cred = current_cred();
int retval = 1;
- if (grp != current->fsgid)
- retval = groups_search(current->group_info, grp);
+
+ if (grp != cred->fsgid)
+ retval = groups_search(cred->group_info, grp);
return retval;
}
@@ -1306,9 +1380,11 @@ EXPORT_SYMBOL(in_group_p);
int in_egroup_p(gid_t grp)
{
+ const struct cred *cred = current_cred();
int retval = 1;
- if (grp != current->egid)
- retval = groups_search(current->group_info, grp);
+
+ if (grp != cred->egid)
+ retval = groups_search(cred->group_info, grp);
return retval;
}
@@ -1624,50 +1700,56 @@ asmlinkage long sys_umask(int mask)
asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
- long error = 0;
+ struct task_struct *me = current;
+ unsigned char comm[sizeof(me->comm)];
+ long error;
- if (security_task_prctl(option, arg2, arg3, arg4, arg5, &error))
+ error = security_task_prctl(option, arg2, arg3, arg4, arg5);
+ if (error != -ENOSYS)
return error;
+ error = 0;
switch (option) {
case PR_SET_PDEATHSIG:
if (!valid_signal(arg2)) {
error = -EINVAL;
break;
}
- current->pdeath_signal = arg2;
+ me->pdeath_signal = arg2;
+ error = 0;
break;
case PR_GET_PDEATHSIG:
- error = put_user(current->pdeath_signal, (int __user *)arg2);
+ error = put_user(me->pdeath_signal, (int __user *)arg2);
break;
case PR_GET_DUMPABLE:
- error = get_dumpable(current->mm);
+ error = get_dumpable(me->mm);
break;
case PR_SET_DUMPABLE:
if (arg2 < 0 || arg2 > 1) {
error = -EINVAL;
break;
}
- set_dumpable(current->mm, arg2);
+ set_dumpable(me->mm, arg2);
+ error = 0;
break;
case PR_SET_UNALIGN:
- error = SET_UNALIGN_CTL(current, arg2);
+ error = SET_UNALIGN_CTL(me, arg2);
break;
case PR_GET_UNALIGN:
- error = GET_UNALIGN_CTL(current, arg2);
+ error = GET_UNALIGN_CTL(me, arg2);
break;
case PR_SET_FPEMU:
- error = SET_FPEMU_CTL(current, arg2);
+ error = SET_FPEMU_CTL(me, arg2);
break;
case PR_GET_FPEMU:
- error = GET_FPEMU_CTL(current, arg2);
+ error = GET_FPEMU_CTL(me, arg2);
break;
case PR_SET_FPEXC:
- error = SET_FPEXC_CTL(current, arg2);
+ error = SET_FPEXC_CTL(me, arg2);
break;
case PR_GET_FPEXC:
- error = GET_FPEXC_CTL(current, arg2);
+ error = GET_FPEXC_CTL(me, arg2);
break;
case PR_GET_TIMING:
error = PR_TIMING_STATISTICAL;
@@ -1675,33 +1757,28 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
case PR_SET_TIMING:
if (arg2 != PR_TIMING_STATISTICAL)
error = -EINVAL;
+ else
+ error = 0;
break;
- case PR_SET_NAME: {
- struct task_struct *me = current;
- unsigned char ncomm[sizeof(me->comm)];
-
- ncomm[sizeof(me->comm)-1] = 0;
- if (strncpy_from_user(ncomm, (char __user *)arg2,
- sizeof(me->comm)-1) < 0)
+ case PR_SET_NAME:
+ comm[sizeof(me->comm)-1] = 0;
+ if (strncpy_from_user(comm, (char __user *)arg2,
+ sizeof(me->comm) - 1) < 0)
return -EFAULT;
- set_task_comm(me, ncomm);
+ set_task_comm(me, comm);
return 0;
- }
- case PR_GET_NAME: {
- struct task_struct *me = current;
- unsigned char tcomm[sizeof(me->comm)];
-
- get_task_comm(tcomm, me);
- if (copy_to_user((char __user *)arg2, tcomm, sizeof(tcomm)))
+ case PR_GET_NAME:
+ get_task_comm(comm, me);
+ if (copy_to_user((char __user *)arg2, comm,
+ sizeof(comm)))
return -EFAULT;
return 0;
- }
case PR_GET_ENDIAN:
- error = GET_ENDIAN(current, arg2);
+ error = GET_ENDIAN(me, arg2);
break;
case PR_SET_ENDIAN:
- error = SET_ENDIAN(current, arg2);
+ error = SET_ENDIAN(me, arg2);
break;
case PR_GET_SECCOMP:
@@ -1725,6 +1802,7 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
current->default_timer_slack_ns;
else
current->timer_slack_ns = arg2;
+ error = 0;
break;
default:
error = -EINVAL;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c83f566e940a..ff6d45c7626f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -121,6 +121,10 @@ extern int sg_big_buff;
#include <asm/system.h>
#endif
+#ifdef CONFIG_SPARC64
+extern int sysctl_tsb_ratio;
+#endif
+
#ifdef __hppa__
extern int pwrsw_enabled;
extern int unaligned_enabled;
@@ -451,6 +455,16 @@ static struct ctl_table kern_table[] = {
.proc_handler = &proc_dointvec,
},
#endif
+#ifdef CONFIG_SPARC64
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "tsb-ratio",
+ .data = &sysctl_tsb_ratio,
+ .maxlen = sizeof (int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
#ifdef __hppa__
{
.ctl_name = KERN_HPPA_PWRSW,
@@ -487,6 +501,16 @@ static struct ctl_table kern_table[] = {
.proc_handler = &ftrace_enable_sysctl,
},
#endif
+#ifdef CONFIG_STACK_TRACER
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "stack_tracer_enabled",
+ .data = &stack_tracer_enabled,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &stack_trace_sysctl,
+ },
+#endif
#ifdef CONFIG_TRACING
{
.ctl_name = CTL_UNNUMBERED,
@@ -1661,7 +1685,7 @@ out:
static int test_perm(int mode, int op)
{
- if (!current->euid)
+ if (!current_euid())
mode >>= 6;
else if (in_egroup_p(0))
mode >>= 3;
diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c
index c35da23ab8fb..fafeb48f27c0 100644
--- a/kernel/sysctl_check.c
+++ b/kernel/sysctl_check.c
@@ -730,7 +730,6 @@ static const struct trans_ctl_table trans_fs_quota_table[] = {
};
static const struct trans_ctl_table trans_fs_xfs_table[] = {
- { XFS_RESTRICT_CHOWN, "restrict_chown" },
{ XFS_SGID_INHERIT, "irix_sgid_inherit" },
{ XFS_SYMLINK_MODE, "irix_symlink_mode" },
{ XFS_PANIC_MASK, "panic_mask" },
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 6d7dc4ec4aa5..888adbcca30c 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -290,18 +290,17 @@ ret:
return;
}
-static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
+static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
{
struct listener_list *listeners;
struct listener *s, *tmp;
unsigned int cpu;
- cpumask_t mask = *maskp;
- if (!cpus_subset(mask, cpu_possible_map))
+ if (!cpumask_subset(mask, cpu_possible_mask))
return -EINVAL;
if (isadd == REGISTER) {
- for_each_cpu_mask_nr(cpu, mask) {
+ for_each_cpu(cpu, mask) {
s = kmalloc_node(sizeof(struct listener), GFP_KERNEL,
cpu_to_node(cpu));
if (!s)
@@ -320,7 +319,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
/* Deregister or cleanup */
cleanup:
- for_each_cpu_mask_nr(cpu, mask) {
+ for_each_cpu(cpu, mask) {
listeners = &per_cpu(listener_array, cpu);
down_write(&listeners->sem);
list_for_each_entry_safe(s, tmp, &listeners->list, list) {
@@ -335,7 +334,7 @@ cleanup:
return 0;
}
-static int parse(struct nlattr *na, cpumask_t *mask)
+static int parse(struct nlattr *na, struct cpumask *mask)
{
char *data;
int len;
@@ -428,23 +427,33 @@ err:
static int taskstats_user_cmd(struct sk_buff *skb, struct genl_info *info)
{
- int rc = 0;
+ int rc;
struct sk_buff *rep_skb;
struct taskstats *stats;
size_t size;
- cpumask_t mask;
+ cpumask_var_t mask;
+
+ if (!alloc_cpumask_var(&mask, GFP_KERNEL))
+ return -ENOMEM;
- rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], &mask);
+ rc = parse(info->attrs[TASKSTATS_CMD_ATTR_REGISTER_CPUMASK], mask);
if (rc < 0)
- return rc;
- if (rc == 0)
- return add_del_listener(info->snd_pid, &mask, REGISTER);
+ goto free_return_rc;
+ if (rc == 0) {
+ rc = add_del_listener(info->snd_pid, mask, REGISTER);
+ goto free_return_rc;
+ }
- rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], &mask);
+ rc = parse(info->attrs[TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK], mask);
if (rc < 0)
+ goto free_return_rc;
+ if (rc == 0) {
+ rc = add_del_listener(info->snd_pid, mask, DEREGISTER);
+free_return_rc:
+ free_cpumask_var(mask);
return rc;
- if (rc == 0)
- return add_del_listener(info->snd_pid, &mask, DEREGISTER);
+ }
+ free_cpumask_var(mask);
/*
* Size includes space for nested attributes
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index 9ed2eec97526..ca89e1593f08 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -145,10 +145,11 @@ static void clocksource_watchdog(unsigned long data)
* Cycle through CPUs to check if the CPUs stay
* synchronized to each other.
*/
- int next_cpu = next_cpu_nr(raw_smp_processor_id(), cpu_online_map);
+ int next_cpu = cpumask_next(raw_smp_processor_id(),
+ cpu_online_mask);
if (next_cpu >= nr_cpu_ids)
- next_cpu = first_cpu(cpu_online_map);
+ next_cpu = cpumask_first(cpu_online_mask);
watchdog_timer.expires += WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer, next_cpu);
}
@@ -173,7 +174,7 @@ static void clocksource_check_watchdog(struct clocksource *cs)
watchdog_last = watchdog->read();
watchdog_timer.expires = jiffies + WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer,
- first_cpu(cpu_online_map));
+ cpumask_first(cpu_online_mask));
}
} else {
if (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS)
@@ -195,7 +196,7 @@ static void clocksource_check_watchdog(struct clocksource *cs)
watchdog_timer.expires =
jiffies + WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer,
- first_cpu(cpu_online_map));
+ cpumask_first(cpu_online_mask));
}
}
}
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 8ff15e5d486b..f5f793d92415 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -131,7 +131,7 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
{
enum hrtimer_restart res = HRTIMER_NORESTART;
- write_seqlock_irq(&xtime_lock);
+ write_seqlock(&xtime_lock);
switch (time_state) {
case TIME_OK:
@@ -164,7 +164,7 @@ static enum hrtimer_restart ntp_leap_second(struct hrtimer *timer)
}
update_vsyscall(&xtime, clock);
- write_sequnlock_irq(&xtime_lock);
+ write_sequnlock(&xtime_lock);
return res;
}
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 9590af2327be..118a3b3b3f9a 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -28,7 +28,9 @@
*/
struct tick_device tick_broadcast_device;
-static cpumask_t tick_broadcast_mask;
+/* FIXME: Use cpumask_var_t. */
+static DECLARE_BITMAP(tick_broadcast_mask, NR_CPUS);
+static DECLARE_BITMAP(tmpmask, NR_CPUS);
static DEFINE_SPINLOCK(tick_broadcast_lock);
static int tick_broadcast_force;
@@ -46,9 +48,9 @@ struct tick_device *tick_get_broadcast_device(void)
return &tick_broadcast_device;
}
-cpumask_t *tick_get_broadcast_mask(void)
+struct cpumask *tick_get_broadcast_mask(void)
{
- return &tick_broadcast_mask;
+ return to_cpumask(tick_broadcast_mask);
}
/*
@@ -72,7 +74,7 @@ int tick_check_broadcast_device(struct clock_event_device *dev)
clockevents_exchange_device(NULL, dev);
tick_broadcast_device.evtdev = dev;
- if (!cpus_empty(tick_broadcast_mask))
+ if (!cpumask_empty(tick_get_broadcast_mask()))
tick_broadcast_start_periodic(dev);
return 1;
}
@@ -104,7 +106,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
*/
if (!tick_device_is_functional(dev)) {
dev->event_handler = tick_handle_periodic;
- cpu_set(cpu, tick_broadcast_mask);
+ cpumask_set_cpu(cpu, tick_get_broadcast_mask());
tick_broadcast_start_periodic(tick_broadcast_device.evtdev);
ret = 1;
} else {
@@ -116,7 +118,7 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) {
int cpu = smp_processor_id();
- cpu_clear(cpu, tick_broadcast_mask);
+ cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
tick_broadcast_clear_oneshot(cpu);
}
}
@@ -125,9 +127,9 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
}
/*
- * Broadcast the event to the cpus, which are set in the mask
+ * Broadcast the event to the cpus, which are set in the mask (mangled).
*/
-static void tick_do_broadcast(cpumask_t mask)
+static void tick_do_broadcast(struct cpumask *mask)
{
int cpu = smp_processor_id();
struct tick_device *td;
@@ -135,22 +137,21 @@ static void tick_do_broadcast(cpumask_t mask)
/*
* Check, if the current cpu is in the mask
*/
- if (cpu_isset(cpu, mask)) {
- cpu_clear(cpu, mask);
+ if (cpumask_test_cpu(cpu, mask)) {
+ cpumask_clear_cpu(cpu, mask);
td = &per_cpu(tick_cpu_device, cpu);
td->evtdev->event_handler(td->evtdev);
}
- if (!cpus_empty(mask)) {
+ if (!cpumask_empty(mask)) {
/*
* It might be necessary to actually check whether the devices
* have different broadcast functions. For now, just use the
* one of the first device. This works as long as we have this
* misfeature only on x86 (lapic)
*/
- cpu = first_cpu(mask);
- td = &per_cpu(tick_cpu_device, cpu);
- td->evtdev->broadcast(&mask);
+ td = &per_cpu(tick_cpu_device, cpumask_first(mask));
+ td->evtdev->broadcast(mask);
}
}
@@ -160,12 +161,11 @@ static void tick_do_broadcast(cpumask_t mask)
*/
static void tick_do_periodic_broadcast(void)
{
- cpumask_t mask;
-
spin_lock(&tick_broadcast_lock);
- cpus_and(mask, cpu_online_map, tick_broadcast_mask);
- tick_do_broadcast(mask);
+ cpumask_and(to_cpumask(tmpmask),
+ cpu_online_mask, tick_get_broadcast_mask());
+ tick_do_broadcast(to_cpumask(tmpmask));
spin_unlock(&tick_broadcast_lock);
}
@@ -228,13 +228,13 @@ static void tick_do_broadcast_on_off(void *why)
if (!tick_device_is_functional(dev))
goto out;
- bc_stopped = cpus_empty(tick_broadcast_mask);
+ bc_stopped = cpumask_empty(tick_get_broadcast_mask());
switch (*reason) {
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
- if (!cpu_isset(cpu, tick_broadcast_mask)) {
- cpu_set(cpu, tick_broadcast_mask);
+ if (!cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
+ cpumask_set_cpu(cpu, tick_get_broadcast_mask());
if (tick_broadcast_device.mode ==
TICKDEV_MODE_PERIODIC)
clockevents_shutdown(dev);
@@ -244,8 +244,8 @@ static void tick_do_broadcast_on_off(void *why)
break;
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
if (!tick_broadcast_force &&
- cpu_isset(cpu, tick_broadcast_mask)) {
- cpu_clear(cpu, tick_broadcast_mask);
+ cpumask_test_cpu(cpu, tick_get_broadcast_mask())) {
+ cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
if (tick_broadcast_device.mode ==
TICKDEV_MODE_PERIODIC)
tick_setup_periodic(dev, 0);
@@ -253,7 +253,7 @@ static void tick_do_broadcast_on_off(void *why)
break;
}
- if (cpus_empty(tick_broadcast_mask)) {
+ if (cpumask_empty(tick_get_broadcast_mask())) {
if (!bc_stopped)
clockevents_shutdown(bc);
} else if (bc_stopped) {
@@ -272,7 +272,7 @@ out:
*/
void tick_broadcast_on_off(unsigned long reason, int *oncpu)
{
- if (!cpu_isset(*oncpu, cpu_online_map))
+ if (!cpumask_test_cpu(*oncpu, cpu_online_mask))
printk(KERN_ERR "tick-broadcast: ignoring broadcast for "
"offline CPU #%d\n", *oncpu);
else
@@ -303,10 +303,10 @@ void tick_shutdown_broadcast(unsigned int *cpup)
spin_lock_irqsave(&tick_broadcast_lock, flags);
bc = tick_broadcast_device.evtdev;
- cpu_clear(cpu, tick_broadcast_mask);
+ cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
- if (bc && cpus_empty(tick_broadcast_mask))
+ if (bc && cpumask_empty(tick_get_broadcast_mask()))
clockevents_shutdown(bc);
}
@@ -342,10 +342,10 @@ int tick_resume_broadcast(void)
switch (tick_broadcast_device.mode) {
case TICKDEV_MODE_PERIODIC:
- if(!cpus_empty(tick_broadcast_mask))
+ if (!cpumask_empty(tick_get_broadcast_mask()))
tick_broadcast_start_periodic(bc);
- broadcast = cpu_isset(smp_processor_id(),
- tick_broadcast_mask);
+ broadcast = cpumask_test_cpu(smp_processor_id(),
+ tick_get_broadcast_mask());
break;
case TICKDEV_MODE_ONESHOT:
broadcast = tick_resume_broadcast_oneshot(bc);
@@ -360,14 +360,15 @@ int tick_resume_broadcast(void)
#ifdef CONFIG_TICK_ONESHOT
-static cpumask_t tick_broadcast_oneshot_mask;
+/* FIXME: use cpumask_var_t. */
+static DECLARE_BITMAP(tick_broadcast_oneshot_mask, NR_CPUS);
/*
- * Debugging: see timer_list.c
+ * Exposed for debugging: see timer_list.c
*/
-cpumask_t *tick_get_broadcast_oneshot_mask(void)
+struct cpumask *tick_get_broadcast_oneshot_mask(void)
{
- return &tick_broadcast_oneshot_mask;
+ return to_cpumask(tick_broadcast_oneshot_mask);
}
static int tick_broadcast_set_event(ktime_t expires, int force)
@@ -389,7 +390,7 @@ int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
*/
void tick_check_oneshot_broadcast(int cpu)
{
- if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
+ if (cpumask_test_cpu(cpu, to_cpumask(tick_broadcast_oneshot_mask))) {
struct tick_device *td = &per_cpu(tick_cpu_device, cpu);
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT);
@@ -402,7 +403,6 @@ void tick_check_oneshot_broadcast(int cpu)
static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
{
struct tick_device *td;
- cpumask_t mask;
ktime_t now, next_event;
int cpu;
@@ -410,13 +410,13 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
again:
dev->next_event.tv64 = KTIME_MAX;
next_event.tv64 = KTIME_MAX;
- mask = CPU_MASK_NONE;
+ cpumask_clear(to_cpumask(tmpmask));
now = ktime_get();
/* Find all expired events */
- for_each_cpu_mask_nr(cpu, tick_broadcast_oneshot_mask) {
+ for_each_cpu(cpu, tick_get_broadcast_oneshot_mask()) {
td = &per_cpu(tick_cpu_device, cpu);
if (td->evtdev->next_event.tv64 <= now.tv64)
- cpu_set(cpu, mask);
+ cpumask_set_cpu(cpu, to_cpumask(tmpmask));
else if (td->evtdev->next_event.tv64 < next_event.tv64)
next_event.tv64 = td->evtdev->next_event.tv64;
}
@@ -424,7 +424,7 @@ again:
/*
* Wakeup the cpus which have an expired event.
*/
- tick_do_broadcast(mask);
+ tick_do_broadcast(to_cpumask(tmpmask));
/*
* Two reasons for reprogram:
@@ -476,15 +476,16 @@ void tick_broadcast_oneshot_control(unsigned long reason)
goto out;
if (reason == CLOCK_EVT_NOTIFY_BROADCAST_ENTER) {
- if (!cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
- cpu_set(cpu, tick_broadcast_oneshot_mask);
+ if (!cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
+ cpumask_set_cpu(cpu, tick_get_broadcast_oneshot_mask());
clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
if (dev->next_event.tv64 < bc->next_event.tv64)
tick_broadcast_set_event(dev->next_event, 1);
}
} else {
- if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
- cpu_clear(cpu, tick_broadcast_oneshot_mask);
+ if (cpumask_test_cpu(cpu, tick_get_broadcast_oneshot_mask())) {
+ cpumask_clear_cpu(cpu,
+ tick_get_broadcast_oneshot_mask());
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
if (dev->next_event.tv64 != KTIME_MAX)
tick_program_event(dev->next_event, 1);
@@ -502,15 +503,16 @@ out:
*/
static void tick_broadcast_clear_oneshot(int cpu)
{
- cpu_clear(cpu, tick_broadcast_oneshot_mask);
+ cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
}
-static void tick_broadcast_init_next_event(cpumask_t *mask, ktime_t expires)
+static void tick_broadcast_init_next_event(struct cpumask *mask,
+ ktime_t expires)
{
struct tick_device *td;
int cpu;
- for_each_cpu_mask_nr(cpu, *mask) {
+ for_each_cpu(cpu, mask) {
td = &per_cpu(tick_cpu_device, cpu);
if (td->evtdev)
td->evtdev->next_event = expires;
@@ -526,7 +528,6 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
if (bc->event_handler != tick_handle_oneshot_broadcast) {
int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC;
int cpu = smp_processor_id();
- cpumask_t mask;
bc->event_handler = tick_handle_oneshot_broadcast;
clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
@@ -540,13 +541,15 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
* oneshot_mask bits for those and program the
* broadcast device to fire.
*/
- mask = tick_broadcast_mask;
- cpu_clear(cpu, mask);
- cpus_or(tick_broadcast_oneshot_mask,
- tick_broadcast_oneshot_mask, mask);
-
- if (was_periodic && !cpus_empty(mask)) {
- tick_broadcast_init_next_event(&mask, tick_next_period);
+ cpumask_copy(to_cpumask(tmpmask), tick_get_broadcast_mask());
+ cpumask_clear_cpu(cpu, to_cpumask(tmpmask));
+ cpumask_or(tick_get_broadcast_oneshot_mask(),
+ tick_get_broadcast_oneshot_mask(),
+ to_cpumask(tmpmask));
+
+ if (was_periodic && !cpumask_empty(to_cpumask(tmpmask))) {
+ tick_broadcast_init_next_event(to_cpumask(tmpmask),
+ tick_next_period);
tick_broadcast_set_event(tick_next_period, 1);
} else
bc->next_event.tv64 = KTIME_MAX;
@@ -585,7 +588,7 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
* Clear the broadcast mask flag for the dead cpu, but do not
* stop the broadcast device!
*/
- cpu_clear(cpu, tick_broadcast_oneshot_mask);
+ cpumask_clear_cpu(cpu, tick_get_broadcast_oneshot_mask());
spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c
index f8372be74122..63e05d423a09 100644
--- a/kernel/time/tick-common.c
+++ b/kernel/time/tick-common.c
@@ -254,7 +254,7 @@ static int tick_check_new_device(struct clock_event_device *newdev)
curdev = NULL;
}
clockevents_exchange_device(curdev, newdev);
- tick_setup_device(td, newdev, cpu, &cpumask_of_cpu(cpu));
+ tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
@@ -299,9 +299,9 @@ static void tick_shutdown(unsigned int *cpup)
}
/* Transfer the do_timer job away from this cpu */
if (*cpup == tick_do_timer_cpu) {
- int cpu = first_cpu(cpu_online_map);
+ int cpu = cpumask_first(cpu_online_mask);
- tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu :
+ tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
TICK_DO_TIMER_NONE;
}
spin_unlock_irqrestore(&tick_device_lock, flags);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 70f872c71f4e..76a574bbef97 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -247,7 +247,7 @@ void tick_nohz_stop_sched_tick(int inidle)
if (need_resched())
goto end;
- if (unlikely(local_softirq_pending())) {
+ if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
static int ratelimit;
if (ratelimit < 10) {
@@ -282,8 +282,31 @@ void tick_nohz_stop_sched_tick(int inidle)
/* Schedule the tick, if we are at least one jiffie off */
if ((long)delta_jiffies >= 1) {
+ /*
+ * calculate the expiry time for the next timer wheel
+ * timer
+ */
+ expires = ktime_add_ns(last_update, tick_period.tv64 *
+ delta_jiffies);
+
+ /*
+ * If this cpu is the one which updates jiffies, then
+ * give up the assignment and let it be taken by the
+ * cpu which runs the tick timer next, which might be
+ * this cpu as well. If we don't drop this here the
+ * jiffies might be stale and do_timer() never
+ * invoked.
+ */
+ if (cpu == tick_do_timer_cpu)
+ tick_do_timer_cpu = TICK_DO_TIMER_NONE;
+
if (delta_jiffies > 1)
cpumask_set_cpu(cpu, nohz_cpu_mask);
+
+ /* Skip reprogram of event if its not changed */
+ if (ts->tick_stopped && ktime_equal(expires, dev->next_event))
+ goto out;
+
/*
* nohz_stop_sched_tick can be called several times before
* the nohz_restart_sched_tick is called. This happens when
@@ -306,17 +329,6 @@ void tick_nohz_stop_sched_tick(int inidle)
rcu_enter_nohz();
}
- /*
- * If this cpu is the one which updates jiffies, then
- * give up the assignment and let it be taken by the
- * cpu which runs the tick timer next, which might be
- * this cpu as well. If we don't drop this here the
- * jiffies might be stale and do_timer() never
- * invoked.
- */
- if (cpu == tick_do_timer_cpu)
- tick_do_timer_cpu = TICK_DO_TIMER_NONE;
-
ts->idle_sleeps++;
/*
@@ -332,12 +344,7 @@ void tick_nohz_stop_sched_tick(int inidle)
goto out;
}
- /*
- * calculate the expiry time for the next timer wheel
- * timer
- */
- expires = ktime_add_ns(last_update, tick_period.tv64 *
- delta_jiffies);
+ /* Mark expiries */
ts->idle_expires = expires;
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
@@ -681,7 +688,6 @@ void tick_setup_sched_timer(void)
*/
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ts->sched_timer.function = tick_sched_timer;
- ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU;
/* Get the next period (per cpu) */
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
diff --git a/kernel/timer.c b/kernel/timer.c
index dbd50fabe4c7..566257d1dc10 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1192,25 +1192,25 @@ asmlinkage long sys_getppid(void)
asmlinkage long sys_getuid(void)
{
/* Only we change this so SMP safe */
- return current->uid;
+ return current_uid();
}
asmlinkage long sys_geteuid(void)
{
/* Only we change this so SMP safe */
- return current->euid;
+ return current_euid();
}
asmlinkage long sys_getgid(void)
{
/* Only we change this so SMP safe */
- return current->gid;
+ return current_gid();
}
asmlinkage long sys_getegid(void)
{
/* Only we change this so SMP safe */
- return current->egid;
+ return current_egid();
}
#endif
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index bde6f03512d5..e2a4ff6fc3a6 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -244,16 +244,21 @@ config STACK_TRACER
This tracer works by hooking into every function call that the
kernel executes, and keeping a maximum stack depth value and
- stack-trace saved. Because this logic has to execute in every
- kernel function, all the time, this option can slow down the
- kernel measurably and is generally intended for kernel
- developers only.
+ stack-trace saved. If this is configured with DYNAMIC_FTRACE
+ then it will not have any overhead while the stack tracer
+ is disabled.
+
+ To enable the stack tracer on bootup, pass in 'stacktrace'
+ on the kernel command line.
+
+ The stack tracer can also be enabled or disabled via the
+ sysctl kernel.stack_tracer_enabled
Say N if unsure.
-config BTS_TRACER
+config HW_BRANCH_TRACER
depends on HAVE_HW_BRANCH_TRACER
- bool "Trace branches"
+ bool "Trace hw branches"
select TRACING
help
This tracer records all branches on the system in a circular
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 62dc561b6676..349d5a93653f 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -31,7 +31,7 @@ obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
-obj-$(CONFIG_BTS_TRACER) += trace_bts.o
+obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
obj-$(CONFIG_POWER_TRACER) += trace_power.o
libftrace-y := ftrace.o
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index a12f80efceaa..2f32969c09df 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1047,6 +1047,13 @@ ftrace_match(unsigned char *buff, int len, int enable)
int type = MATCH_FULL;
unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
unsigned i, match = 0, search_len = 0;
+ int not = 0;
+
+ if (buff[0] == '!') {
+ not = 1;
+ buff++;
+ len--;
+ }
for (i = 0; i < len; i++) {
if (buff[i] == '*') {
@@ -1100,8 +1107,12 @@ ftrace_match(unsigned char *buff, int len, int enable)
matched = 1;
break;
}
- if (matched)
- rec->flags |= flag;
+ if (matched) {
+ if (not)
+ rec->flags &= ~flag;
+ else
+ rec->flags |= flag;
+ }
}
pg = pg->next;
}
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 7f69cfeaadf7..a9d9760dc7b6 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -69,6 +69,7 @@ void tracing_on(void)
{
set_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags);
}
+EXPORT_SYMBOL_GPL(tracing_on);
/**
* tracing_off - turn off all tracing buffers
@@ -82,6 +83,7 @@ void tracing_off(void)
{
clear_bit(RB_BUFFERS_ON_BIT, &ring_buffer_flags);
}
+EXPORT_SYMBOL_GPL(tracing_off);
/**
* tracing_off_permanent - permanently disable ring buffers
@@ -107,16 +109,18 @@ u64 ring_buffer_time_stamp(int cpu)
preempt_disable_notrace();
/* shift to debug/test normalization and TIME_EXTENTS */
time = sched_clock() << DEBUG_SHIFT;
- preempt_enable_notrace();
+ preempt_enable_no_resched_notrace();
return time;
}
+EXPORT_SYMBOL_GPL(ring_buffer_time_stamp);
void ring_buffer_normalize_time_stamp(int cpu, u64 *ts)
{
/* Just stupid testing the normalize function and deltas */
*ts >>= DEBUG_SHIFT;
}
+EXPORT_SYMBOL_GPL(ring_buffer_normalize_time_stamp);
#define RB_EVNT_HDR_SIZE (sizeof(struct ring_buffer_event))
#define RB_ALIGNMENT_SHIFT 2
@@ -166,6 +170,7 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
{
return rb_event_length(event);
}
+EXPORT_SYMBOL_GPL(ring_buffer_event_length);
/* inline for ring buffer fast paths */
static inline void *
@@ -187,9 +192,10 @@ void *ring_buffer_event_data(struct ring_buffer_event *event)
{
return rb_event_data(event);
}
+EXPORT_SYMBOL_GPL(ring_buffer_event_data);
#define for_each_buffer_cpu(buffer, cpu) \
- for_each_cpu_mask(cpu, buffer->cpumask)
+ for_each_cpu(cpu, buffer->cpumask)
#define TS_SHIFT 27
#define TS_MASK ((1ULL << TS_SHIFT) - 1)
@@ -258,11 +264,10 @@ struct ring_buffer_per_cpu {
};
struct ring_buffer {
- unsigned long size;
unsigned pages;
unsigned flags;
int cpus;
- cpumask_t cpumask;
+ cpumask_var_t cpumask;
atomic_t record_disabled;
struct mutex mutex;
@@ -428,7 +433,7 @@ extern int ring_buffer_page_too_big(void);
/**
* ring_buffer_alloc - allocate a new ring_buffer
- * @size: the size in bytes that is needed.
+ * @size: the size in bytes per cpu that is needed.
* @flags: attributes to set for the ring buffer.
*
* Currently the only flag that is available is the RB_FL_OVERWRITE
@@ -453,6 +458,9 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
if (!buffer)
return NULL;
+ if (!alloc_cpumask_var(&buffer->cpumask, GFP_KERNEL))
+ goto fail_free_buffer;
+
buffer->pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE);
buffer->flags = flags;
@@ -460,14 +468,14 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
if (buffer->pages == 1)
buffer->pages++;
- buffer->cpumask = cpu_possible_map;
+ cpumask_copy(buffer->cpumask, cpu_possible_mask);
buffer->cpus = nr_cpu_ids;
bsize = sizeof(void *) * nr_cpu_ids;
buffer->buffers = kzalloc(ALIGN(bsize, cache_line_size()),
GFP_KERNEL);
if (!buffer->buffers)
- goto fail_free_buffer;
+ goto fail_free_cpumask;
for_each_buffer_cpu(buffer, cpu) {
buffer->buffers[cpu] =
@@ -487,10 +495,14 @@ struct ring_buffer *ring_buffer_alloc(unsigned long size, unsigned flags)
}
kfree(buffer->buffers);
+ fail_free_cpumask:
+ free_cpumask_var(buffer->cpumask);
+
fail_free_buffer:
kfree(buffer);
return NULL;
}
+EXPORT_SYMBOL_GPL(ring_buffer_alloc);
/**
* ring_buffer_free - free a ring buffer.
@@ -504,8 +516,11 @@ ring_buffer_free(struct ring_buffer *buffer)
for_each_buffer_cpu(buffer, cpu)
rb_free_cpu_buffer(buffer->buffers[cpu]);
+ free_cpumask_var(buffer->cpumask);
+
kfree(buffer);
}
+EXPORT_SYMBOL_GPL(ring_buffer_free);
static void rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer);
@@ -681,6 +696,7 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size)
mutex_unlock(&buffer->mutex);
return -ENOMEM;
}
+EXPORT_SYMBOL_GPL(ring_buffer_resize);
static inline int rb_null_event(struct ring_buffer_event *event)
{
@@ -839,6 +855,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
* back to us). This allows us to do a simple loop to
* assign the commit to the tail.
*/
+ again:
while (cpu_buffer->commit_page != cpu_buffer->tail_page) {
cpu_buffer->commit_page->page->commit =
cpu_buffer->commit_page->write;
@@ -854,6 +871,17 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
cpu_buffer->commit_page->write;
barrier();
}
+
+ /* again, keep gcc from optimizing */
+ barrier();
+
+ /*
+ * If an interrupt came in just after the first while loop
+ * and pushed the tail page forward, we will be left with
+ * a dangling commit that will never go forward.
+ */
+ if (unlikely(cpu_buffer->commit_page != cpu_buffer->tail_page))
+ goto again;
}
static void rb_reset_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
@@ -951,12 +979,15 @@ static struct ring_buffer_event *
__rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
unsigned type, unsigned long length, u64 *ts)
{
- struct buffer_page *tail_page, *head_page, *reader_page;
+ struct buffer_page *tail_page, *head_page, *reader_page, *commit_page;
unsigned long tail, write;
struct ring_buffer *buffer = cpu_buffer->buffer;
struct ring_buffer_event *event;
unsigned long flags;
+ commit_page = cpu_buffer->commit_page;
+ /* we just need to protect against interrupts */
+ barrier();
tail_page = cpu_buffer->tail_page;
write = local_add_return(length, &tail_page->write);
tail = write - length;
@@ -982,7 +1013,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
* it all the way around the buffer, bail, and warn
* about it.
*/
- if (unlikely(next_page == cpu_buffer->commit_page)) {
+ if (unlikely(next_page == commit_page)) {
WARN_ON_ONCE(1);
goto out_unlock;
}
@@ -1260,7 +1291,7 @@ ring_buffer_lock_reserve(struct ring_buffer *buffer,
cpu = raw_smp_processor_id();
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
goto out;
cpu_buffer = buffer->buffers[cpu];
@@ -1290,6 +1321,7 @@ ring_buffer_lock_reserve(struct ring_buffer *buffer,
ftrace_preempt_enable(resched);
return NULL;
}
+EXPORT_SYMBOL_GPL(ring_buffer_lock_reserve);
static void rb_commit(struct ring_buffer_per_cpu *cpu_buffer,
struct ring_buffer_event *event)
@@ -1336,6 +1368,7 @@ int ring_buffer_unlock_commit(struct ring_buffer *buffer,
return 0;
}
+EXPORT_SYMBOL_GPL(ring_buffer_unlock_commit);
/**
* ring_buffer_write - write data to the buffer without reserving
@@ -1371,7 +1404,7 @@ int ring_buffer_write(struct ring_buffer *buffer,
cpu = raw_smp_processor_id();
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
goto out;
cpu_buffer = buffer->buffers[cpu];
@@ -1397,6 +1430,7 @@ int ring_buffer_write(struct ring_buffer *buffer,
return ret;
}
+EXPORT_SYMBOL_GPL(ring_buffer_write);
static inline int rb_per_cpu_empty(struct ring_buffer_per_cpu *cpu_buffer)
{
@@ -1423,6 +1457,7 @@ void ring_buffer_record_disable(struct ring_buffer *buffer)
{
atomic_inc(&buffer->record_disabled);
}
+EXPORT_SYMBOL_GPL(ring_buffer_record_disable);
/**
* ring_buffer_record_enable - enable writes to the buffer
@@ -1435,6 +1470,7 @@ void ring_buffer_record_enable(struct ring_buffer *buffer)
{
atomic_dec(&buffer->record_disabled);
}
+EXPORT_SYMBOL_GPL(ring_buffer_record_enable);
/**
* ring_buffer_record_disable_cpu - stop all writes into the cpu_buffer
@@ -1450,12 +1486,13 @@ void ring_buffer_record_disable_cpu(struct ring_buffer *buffer, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return;
cpu_buffer = buffer->buffers[cpu];
atomic_inc(&cpu_buffer->record_disabled);
}
+EXPORT_SYMBOL_GPL(ring_buffer_record_disable_cpu);
/**
* ring_buffer_record_enable_cpu - enable writes to the buffer
@@ -1469,12 +1506,13 @@ void ring_buffer_record_enable_cpu(struct ring_buffer *buffer, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return;
cpu_buffer = buffer->buffers[cpu];
atomic_dec(&cpu_buffer->record_disabled);
}
+EXPORT_SYMBOL_GPL(ring_buffer_record_enable_cpu);
/**
* ring_buffer_entries_cpu - get the number of entries in a cpu buffer
@@ -1485,12 +1523,13 @@ unsigned long ring_buffer_entries_cpu(struct ring_buffer *buffer, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return 0;
cpu_buffer = buffer->buffers[cpu];
return cpu_buffer->entries;
}
+EXPORT_SYMBOL_GPL(ring_buffer_entries_cpu);
/**
* ring_buffer_overrun_cpu - get the number of overruns in a cpu_buffer
@@ -1501,12 +1540,13 @@ unsigned long ring_buffer_overrun_cpu(struct ring_buffer *buffer, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return 0;
cpu_buffer = buffer->buffers[cpu];
return cpu_buffer->overrun;
}
+EXPORT_SYMBOL_GPL(ring_buffer_overrun_cpu);
/**
* ring_buffer_entries - get the number of entries in a buffer
@@ -1529,6 +1569,7 @@ unsigned long ring_buffer_entries(struct ring_buffer *buffer)
return entries;
}
+EXPORT_SYMBOL_GPL(ring_buffer_entries);
/**
* ring_buffer_overrun_cpu - get the number of overruns in buffer
@@ -1551,6 +1592,7 @@ unsigned long ring_buffer_overruns(struct ring_buffer *buffer)
return overruns;
}
+EXPORT_SYMBOL_GPL(ring_buffer_overruns);
static void rb_iter_reset(struct ring_buffer_iter *iter)
{
@@ -1586,6 +1628,7 @@ void ring_buffer_iter_reset(struct ring_buffer_iter *iter)
rb_iter_reset(iter);
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
}
+EXPORT_SYMBOL_GPL(ring_buffer_iter_reset);
/**
* ring_buffer_iter_empty - check if an iterator has no more to read
@@ -1600,6 +1643,7 @@ int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
return iter->head_page == cpu_buffer->commit_page &&
iter->head == rb_commit_index(cpu_buffer);
}
+EXPORT_SYMBOL_GPL(ring_buffer_iter_empty);
static void
rb_update_read_stamp(struct ring_buffer_per_cpu *cpu_buffer,
@@ -1814,7 +1858,7 @@ rb_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
struct buffer_page *reader;
int nr_loops = 0;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return NULL;
cpu_buffer = buffer->buffers[cpu];
@@ -1866,6 +1910,7 @@ rb_buffer_peek(struct ring_buffer *buffer, int cpu, u64 *ts)
return NULL;
}
+EXPORT_SYMBOL_GPL(ring_buffer_peek);
static struct ring_buffer_event *
rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
@@ -1926,6 +1971,7 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
return NULL;
}
+EXPORT_SYMBOL_GPL(ring_buffer_iter_peek);
/**
* ring_buffer_peek - peek at the next event to be read
@@ -1987,7 +2033,7 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts)
struct ring_buffer_event *event;
unsigned long flags;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return NULL;
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
@@ -2003,6 +2049,7 @@ ring_buffer_consume(struct ring_buffer *buffer, int cpu, u64 *ts)
return event;
}
+EXPORT_SYMBOL_GPL(ring_buffer_consume);
/**
* ring_buffer_read_start - start a non consuming read of the buffer
@@ -2023,7 +2070,7 @@ ring_buffer_read_start(struct ring_buffer *buffer, int cpu)
struct ring_buffer_iter *iter;
unsigned long flags;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return NULL;
iter = kmalloc(sizeof(*iter), GFP_KERNEL);
@@ -2045,6 +2092,7 @@ ring_buffer_read_start(struct ring_buffer *buffer, int cpu)
return iter;
}
+EXPORT_SYMBOL_GPL(ring_buffer_read_start);
/**
* ring_buffer_finish - finish reading the iterator of the buffer
@@ -2061,6 +2109,7 @@ ring_buffer_read_finish(struct ring_buffer_iter *iter)
atomic_dec(&cpu_buffer->record_disabled);
kfree(iter);
}
+EXPORT_SYMBOL_GPL(ring_buffer_read_finish);
/**
* ring_buffer_read - read the next item in the ring buffer by the iterator
@@ -2087,6 +2136,7 @@ ring_buffer_read(struct ring_buffer_iter *iter, u64 *ts)
return event;
}
+EXPORT_SYMBOL_GPL(ring_buffer_read);
/**
* ring_buffer_size - return the size of the ring buffer (in bytes)
@@ -2096,6 +2146,7 @@ unsigned long ring_buffer_size(struct ring_buffer *buffer)
{
return BUF_PAGE_SIZE * buffer->pages;
}
+EXPORT_SYMBOL_GPL(ring_buffer_size);
static void
rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)
@@ -2129,7 +2180,7 @@ void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu)
struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu];
unsigned long flags;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return;
spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
@@ -2142,6 +2193,7 @@ void ring_buffer_reset_cpu(struct ring_buffer *buffer, int cpu)
spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
}
+EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu);
/**
* ring_buffer_reset - reset a ring buffer
@@ -2154,6 +2206,7 @@ void ring_buffer_reset(struct ring_buffer *buffer)
for_each_buffer_cpu(buffer, cpu)
ring_buffer_reset_cpu(buffer, cpu);
}
+EXPORT_SYMBOL_GPL(ring_buffer_reset);
/**
* rind_buffer_empty - is the ring buffer empty?
@@ -2172,6 +2225,7 @@ int ring_buffer_empty(struct ring_buffer *buffer)
}
return 1;
}
+EXPORT_SYMBOL_GPL(ring_buffer_empty);
/**
* ring_buffer_empty_cpu - is a cpu buffer of a ring buffer empty?
@@ -2182,12 +2236,13 @@ int ring_buffer_empty_cpu(struct ring_buffer *buffer, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
- if (!cpu_isset(cpu, buffer->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer->cpumask))
return 1;
cpu_buffer = buffer->buffers[cpu];
return rb_per_cpu_empty(cpu_buffer);
}
+EXPORT_SYMBOL_GPL(ring_buffer_empty_cpu);
/**
* ring_buffer_swap_cpu - swap a CPU buffer between two ring buffers
@@ -2205,13 +2260,12 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
struct ring_buffer_per_cpu *cpu_buffer_a;
struct ring_buffer_per_cpu *cpu_buffer_b;
- if (!cpu_isset(cpu, buffer_a->cpumask) ||
- !cpu_isset(cpu, buffer_b->cpumask))
+ if (!cpumask_test_cpu(cpu, buffer_a->cpumask) ||
+ !cpumask_test_cpu(cpu, buffer_b->cpumask))
return -EINVAL;
/* At least make sure the two buffers are somewhat the same */
- if (buffer_a->size != buffer_b->size ||
- buffer_a->pages != buffer_b->pages)
+ if (buffer_a->pages != buffer_b->pages)
return -EINVAL;
cpu_buffer_a = buffer_a->buffers[cpu];
@@ -2237,6 +2291,7 @@ int ring_buffer_swap_cpu(struct ring_buffer *buffer_a,
return 0;
}
+EXPORT_SYMBOL_GPL(ring_buffer_swap_cpu);
static void rb_remove_entries(struct ring_buffer_per_cpu *cpu_buffer,
struct buffer_data_page *bpage)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 6adf660fc816..c580233add95 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -30,7 +30,6 @@
#include <linux/gfp.h>
#include <linux/fs.h>
#include <linux/kprobes.h>
-#include <linux/seq_file.h>
#include <linux/writeback.h>
#include <linux/stacktrace.h>
@@ -90,10 +89,10 @@ static inline void ftrace_enable_cpu(void)
preempt_enable();
}
-static cpumask_t __read_mostly tracing_buffer_mask;
+static cpumask_var_t __read_mostly tracing_buffer_mask;
#define for_each_tracing_cpu(cpu) \
- for_each_cpu_mask(cpu, tracing_buffer_mask)
+ for_each_cpu(cpu, tracing_buffer_mask)
/*
* ftrace_dump_on_oops - variable to dump ftrace buffer on oops
@@ -287,6 +286,7 @@ static const char *trace_options[] = {
"annotate",
"userstacktrace",
"sym-userobj",
+ "printk-msg-only",
NULL
};
@@ -320,7 +320,7 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
memcpy(data->comm, tsk->comm, TASK_COMM_LEN);
data->pid = tsk->pid;
- data->uid = tsk->uid;
+ data->uid = task_uid(tsk);
data->nice = tsk->static_prio - 20 - MAX_RT_PRIO;
data->policy = tsk->policy;
data->rt_priority = tsk->rt_priority;
@@ -678,6 +678,16 @@ void tracing_reset(struct trace_array *tr, int cpu)
ftrace_enable_cpu();
}
+void tracing_reset_online_cpus(struct trace_array *tr)
+{
+ int cpu;
+
+ tr->time_start = ftrace_now(tr->cpu);
+
+ for_each_online_cpu(cpu)
+ tracing_reset(tr, cpu);
+}
+
#define SAVED_CMDLINES 128
static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
static unsigned map_cmdline_to_pid[SAVED_CMDLINES];
@@ -1299,7 +1309,7 @@ enum trace_file_type {
TRACE_FILE_ANNOTATE = 2,
};
-static void trace_iterator_increment(struct trace_iterator *iter, int cpu)
+static void trace_iterator_increment(struct trace_iterator *iter)
{
/* Don't allow ftrace to trace into the ring buffers */
ftrace_disable_cpu();
@@ -1378,7 +1388,7 @@ static void *find_next_entry_inc(struct trace_iterator *iter)
iter->ent = __find_next_entry(iter, &iter->cpu, &iter->ts);
if (iter->ent)
- trace_iterator_increment(iter, iter->cpu);
+ trace_iterator_increment(iter);
return iter->ent ? iter : NULL;
}
@@ -1747,6 +1757,13 @@ lat_print_timestamp(struct trace_seq *s, u64 abs_usecs,
static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
+static int task_state_char(unsigned long state)
+{
+ int bit = state ? __ffs(state) + 1 : 0;
+
+ return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?';
+}
+
/*
* The message is supposed to contain an ending newline.
* If the printing stops prematurely, try to add a newline of our own.
@@ -1794,10 +1811,10 @@ static void test_cpu_buff_start(struct trace_iterator *iter)
if (!(iter->iter_flags & TRACE_FILE_ANNOTATE))
return;
- if (cpu_isset(iter->cpu, iter->started))
+ if (cpumask_test_cpu(iter->cpu, iter->started))
return;
- cpu_set(iter->cpu, iter->started);
+ cpumask_set_cpu(iter->cpu, iter->started);
trace_seq_printf(s, "##### CPU %u buffer started ####\n", iter->cpu);
}
@@ -1815,7 +1832,6 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
char *comm;
int S, T;
int i;
- unsigned state;
if (entry->type == TRACE_CONT)
return TRACE_TYPE_HANDLED;
@@ -1861,12 +1877,8 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
trace_assign_type(field, entry);
- T = field->next_state < sizeof(state_to_char) ?
- state_to_char[field->next_state] : 'X';
-
- state = field->prev_state ?
- __ffs(field->prev_state) + 1 : 0;
- S = state < sizeof(state_to_char) - 1 ? state_to_char[state] : 'X';
+ T = task_state_char(field->next_state);
+ S = task_state_char(field->prev_state);
comm = trace_find_cmdline(field->next_pid);
trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c %s\n",
field->prev_pid,
@@ -2007,10 +2019,8 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
trace_assign_type(field, entry);
- S = field->prev_state < sizeof(state_to_char) ?
- state_to_char[field->prev_state] : 'X';
- T = field->next_state < sizeof(state_to_char) ?
- state_to_char[field->next_state] : 'X';
+ T = task_state_char(field->next_state);
+ S = task_state_char(field->prev_state);
ret = trace_seq_printf(s, " %5d:%3d:%c %s [%03d] %5d:%3d:%c\n",
field->prev_pid,
field->prev_prio,
@@ -2140,12 +2150,9 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
trace_assign_type(field, entry);
- S = field->prev_state < sizeof(state_to_char) ?
- state_to_char[field->prev_state] : 'X';
- T = field->next_state < sizeof(state_to_char) ?
- state_to_char[field->next_state] : 'X';
- if (entry->type == TRACE_WAKE)
- S = '+';
+ T = task_state_char(field->next_state);
+ S = entry->type == TRACE_WAKE ? '+' :
+ task_state_char(field->prev_state);
ret = trace_seq_printf(s, "%d %d %c %d %d %d %c\n",
field->prev_pid,
field->prev_prio,
@@ -2232,12 +2239,9 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
trace_assign_type(field, entry);
- S = field->prev_state < sizeof(state_to_char) ?
- state_to_char[field->prev_state] : 'X';
- T = field->next_state < sizeof(state_to_char) ?
- state_to_char[field->next_state] : 'X';
- if (entry->type == TRACE_WAKE)
- S = '+';
+ T = task_state_char(field->next_state);
+ S = entry->type == TRACE_WAKE ? '+' :
+ task_state_char(field->prev_state);
SEQ_PUT_HEX_FIELD_RET(s, field->prev_pid);
SEQ_PUT_HEX_FIELD_RET(s, field->prev_prio);
SEQ_PUT_HEX_FIELD_RET(s, S);
@@ -2265,6 +2269,25 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
return TRACE_TYPE_HANDLED;
}
+static enum print_line_t print_printk_msg_only(struct trace_iterator *iter)
+{
+ struct trace_seq *s = &iter->seq;
+ struct trace_entry *entry = iter->ent;
+ struct print_entry *field;
+ int ret;
+
+ trace_assign_type(field, entry);
+
+ ret = trace_seq_printf(s, field->buf);
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ if (entry->flags & TRACE_FLAG_CONT)
+ trace_seq_print_cont(s, iter);
+
+ return TRACE_TYPE_HANDLED;
+}
+
static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
{
struct trace_seq *s = &iter->seq;
@@ -2345,6 +2368,11 @@ static enum print_line_t print_trace_line(struct trace_iterator *iter)
return ret;
}
+ if (iter->ent->type == TRACE_PRINT &&
+ trace_flags & TRACE_ITER_PRINTK &&
+ trace_flags & TRACE_ITER_PRINTK_MSGONLY)
+ return print_printk_msg_only(iter);
+
if (trace_flags & TRACE_ITER_BIN)
return print_bin_fmt(iter);
@@ -2425,7 +2453,7 @@ __tracing_open(struct inode *inode, struct file *file, int *ret)
/* Notify the tracer early; before we stop tracing. */
if (iter->trace && iter->trace->open)
- iter->trace->open(iter);
+ iter->trace->open(iter);
/* Annotate start of buffers if we had overruns */
if (ring_buffer_overruns(iter->tr->buffer))
@@ -2618,13 +2646,7 @@ static struct file_operations show_traces_fops = {
/*
* Only trace on a CPU if the bitmask is set:
*/
-static cpumask_t tracing_cpumask = CPU_MASK_ALL;
-
-/*
- * When tracing/tracing_cpu_mask is modified then this holds
- * the new bitmask we are about to install:
- */
-static cpumask_t tracing_cpumask_new;
+static cpumask_var_t tracing_cpumask;
/*
* The tracer itself will not take this lock, but still we want
@@ -2646,7 +2668,7 @@ tracing_cpumask_read(struct file *filp, char __user *ubuf,
mutex_lock(&tracing_cpumask_update_lock);
- len = cpumask_scnprintf(mask_str, count, &tracing_cpumask);
+ len = cpumask_scnprintf(mask_str, count, tracing_cpumask);
if (count - len < 2) {
count = -EINVAL;
goto out_err;
@@ -2665,9 +2687,13 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf,
size_t count, loff_t *ppos)
{
int err, cpu;
+ cpumask_var_t tracing_cpumask_new;
+
+ if (!alloc_cpumask_var(&tracing_cpumask_new, GFP_KERNEL))
+ return -ENOMEM;
mutex_lock(&tracing_cpumask_update_lock);
- err = cpumask_parse_user(ubuf, count, &tracing_cpumask_new);
+ err = cpumask_parse_user(ubuf, count, tracing_cpumask_new);
if (err)
goto err_unlock;
@@ -2678,26 +2704,28 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf,
* Increase/decrease the disabled counter if we are
* about to flip a bit in the cpumask:
*/
- if (cpu_isset(cpu, tracing_cpumask) &&
- !cpu_isset(cpu, tracing_cpumask_new)) {
+ if (cpumask_test_cpu(cpu, tracing_cpumask) &&
+ !cpumask_test_cpu(cpu, tracing_cpumask_new)) {
atomic_inc(&global_trace.data[cpu]->disabled);
}
- if (!cpu_isset(cpu, tracing_cpumask) &&
- cpu_isset(cpu, tracing_cpumask_new)) {
+ if (!cpumask_test_cpu(cpu, tracing_cpumask) &&
+ cpumask_test_cpu(cpu, tracing_cpumask_new)) {
atomic_dec(&global_trace.data[cpu]->disabled);
}
}
__raw_spin_unlock(&ftrace_max_lock);
local_irq_enable();
- tracing_cpumask = tracing_cpumask_new;
+ cpumask_copy(tracing_cpumask, tracing_cpumask_new);
mutex_unlock(&tracing_cpumask_update_lock);
+ free_cpumask_var(tracing_cpumask_new);
return count;
err_unlock:
mutex_unlock(&tracing_cpumask_update_lock);
+ free_cpumask_var(tracing_cpumask);
return err;
}
@@ -3086,10 +3114,15 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
if (!iter)
return -ENOMEM;
+ if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) {
+ kfree(iter);
+ return -ENOMEM;
+ }
+
mutex_lock(&trace_types_lock);
/* trace pipe does not show start of buffer */
- cpus_setall(iter->started);
+ cpumask_setall(iter->started);
iter->tr = &global_trace;
iter->trace = current_trace;
@@ -3106,6 +3139,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)
{
struct trace_iterator *iter = file->private_data;
+ free_cpumask_var(iter->started);
kfree(iter);
atomic_dec(&tracing_reader);
@@ -3724,7 +3758,6 @@ void ftrace_dump(void)
static DEFINE_SPINLOCK(ftrace_dump_lock);
/* use static because iter can be a bit big for the stack */
static struct trace_iterator iter;
- static cpumask_t mask;
static int dump_ran;
unsigned long flags;
int cnt = 0, cpu;
@@ -3758,8 +3791,6 @@ void ftrace_dump(void)
* and then release the locks again.
*/
- cpus_clear(mask);
-
while (!trace_empty(&iter)) {
if (!cnt)
@@ -3795,19 +3826,28 @@ __init static int tracer_alloc_buffers(void)
{
struct trace_array_cpu *data;
int i;
+ int ret = -ENOMEM;
- /* TODO: make the number of buffers hot pluggable with CPUS */
- tracing_buffer_mask = cpu_possible_map;
+ if (!alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL))
+ goto out;
+
+ if (!alloc_cpumask_var(&tracing_cpumask, GFP_KERNEL))
+ goto out_free_buffer_mask;
+ cpumask_copy(tracing_buffer_mask, cpu_possible_mask);
+ cpumask_copy(tracing_cpumask, cpu_all_mask);
+
+ /* TODO: make the number of buffers hot pluggable with CPUS */
global_trace.buffer = ring_buffer_alloc(trace_buf_size,
TRACE_BUFFER_FLAGS);
if (!global_trace.buffer) {
printk(KERN_ERR "tracer: failed to allocate ring buffer!\n");
WARN_ON(1);
- return 0;
+ goto out_free_cpumask;
}
global_trace.entries = ring_buffer_size(global_trace.buffer);
+
#ifdef CONFIG_TRACER_MAX_TRACE
max_tr.buffer = ring_buffer_alloc(trace_buf_size,
TRACE_BUFFER_FLAGS);
@@ -3815,7 +3855,7 @@ __init static int tracer_alloc_buffers(void)
printk(KERN_ERR "tracer: failed to allocate max ring buffer!\n");
WARN_ON(1);
ring_buffer_free(global_trace.buffer);
- return 0;
+ goto out_free_cpumask;
}
max_tr.entries = ring_buffer_size(max_tr.buffer);
WARN_ON(max_tr.entries != global_trace.entries);
@@ -3845,8 +3885,14 @@ __init static int tracer_alloc_buffers(void)
&trace_panic_notifier);
register_die_notifier(&trace_die_notifier);
+ ret = 0;
- return 0;
+out_free_cpumask:
+ free_cpumask_var(tracing_cpumask);
+out_free_buffer_mask:
+ free_cpumask_var(tracing_buffer_mask);
+out:
+ return ret;
}
early_initcall(tracer_alloc_buffers);
fs_initcall(tracer_init_debugfs);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5ac697065a48..4d3d381bfd95 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -28,7 +28,7 @@ enum trace_type {
TRACE_GRAPH_RET,
TRACE_GRAPH_ENT,
TRACE_USER_STACK,
- TRACE_BTS,
+ TRACE_HW_BRANCHES,
TRACE_POWER,
__TRACE_LAST_TYPE
@@ -159,10 +159,10 @@ struct trace_branch {
char correct;
};
-struct bts_entry {
+struct hw_branch_entry {
struct trace_entry ent;
- unsigned long from;
- unsigned long to;
+ u64 from;
+ u64 to;
};
struct trace_power {
@@ -278,7 +278,7 @@ extern void __ftrace_bad_type(void);
TRACE_GRAPH_ENT); \
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
TRACE_GRAPH_RET); \
- IF_ASSIGN(var, ent, struct bts_entry, TRACE_BTS);\
+ IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\
IF_ASSIGN(var, ent, struct trace_power, TRACE_POWER); \
__ftrace_bad_type(); \
} while (0)
@@ -368,12 +368,13 @@ struct trace_iterator {
loff_t pos;
long idx;
- cpumask_t started;
+ cpumask_var_t started;
};
int tracing_is_enabled(void);
void trace_wake_up(void);
void tracing_reset(struct trace_array *tr, int cpu);
+void tracing_reset_online_cpus(struct trace_array *tr);
int tracing_open_generic(struct inode *inode, struct file *filp);
struct dentry *tracing_init_dentry(void);
void init_tracer_sysprof_debugfs(struct dentry *d_tracer);
@@ -414,9 +415,7 @@ void trace_function(struct trace_array *tr,
void trace_graph_return(struct ftrace_graph_ret *trace);
int trace_graph_entry(struct ftrace_graph_ent *trace);
-void trace_bts(struct trace_array *tr,
- unsigned long from,
- unsigned long to);
+void trace_hw_branch(struct trace_array *tr, u64 from, u64 to);
void tracing_start_cmdline_record(void);
void tracing_stop_cmdline_record(void);
@@ -580,7 +579,8 @@ enum trace_iterator_flags {
TRACE_ITER_BRANCH = 0x1000,
TRACE_ITER_ANNOTATE = 0x2000,
TRACE_ITER_USERSTACKTRACE = 0x4000,
- TRACE_ITER_SYM_USEROBJ = 0x8000
+ TRACE_ITER_SYM_USEROBJ = 0x8000,
+ TRACE_ITER_PRINTK_MSGONLY = 0x10000
};
/*
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
index a4fa2c57e34e..366c8c333e13 100644
--- a/kernel/trace/trace_boot.c
+++ b/kernel/trace/trace_boot.c
@@ -37,22 +37,12 @@ void disable_boot_trace(void)
tracing_stop_sched_switch_record();
}
-static void reset_boot_trace(struct trace_array *tr)
-{
- int cpu;
-
- tr->time_start = ftrace_now(tr->cpu);
-
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
-}
-
static int boot_trace_init(struct trace_array *tr)
{
int cpu;
boot_trace = tr;
- for_each_cpu_mask(cpu, cpu_possible_map)
+ for_each_cpu(cpu, cpu_possible_mask)
tracing_reset(tr, cpu);
tracing_sched_switch_assign_trace(tr);
@@ -130,7 +120,7 @@ struct tracer boot_tracer __read_mostly =
{
.name = "initcall",
.init = boot_trace_init,
- .reset = reset_boot_trace,
+ .reset = tracing_reset_online_cpus,
.print_line = initcall_print_line,
};
diff --git a/kernel/trace/trace_bts.c b/kernel/trace/trace_bts.c
deleted file mode 100644
index 23b76e4690ef..000000000000
--- a/kernel/trace/trace_bts.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * BTS tracer
- *
- * Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com>
- *
- */
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/debugfs.h>
-#include <linux/ftrace.h>
-#include <linux/kallsyms.h>
-
-#include <asm/ds.h>
-
-#include "trace.h"
-
-
-#define SIZEOF_BTS (1 << 13)
-
-static DEFINE_PER_CPU(struct bts_tracer *, tracer);
-static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer);
-
-#define this_tracer per_cpu(tracer, smp_processor_id())
-#define this_buffer per_cpu(buffer, smp_processor_id())
-
-
-/*
- * Information to interpret a BTS record.
- * This will go into an in-kernel BTS interface.
- */
-static unsigned char sizeof_field;
-static unsigned long debugctl_mask;
-
-#define sizeof_bts (3 * sizeof_field)
-
-static void bts_trace_cpuinit(struct cpuinfo_x86 *c)
-{
- switch (c->x86) {
- case 0x6:
- switch (c->x86_model) {
- case 0x0 ... 0xC:
- break;
- case 0xD:
- case 0xE: /* Pentium M */
- sizeof_field = sizeof(long);
- debugctl_mask = (1<<6)|(1<<7);
- break;
- default:
- sizeof_field = 8;
- debugctl_mask = (1<<6)|(1<<7);
- break;
- }
- break;
- case 0xF:
- switch (c->x86_model) {
- case 0x0:
- case 0x1:
- case 0x2: /* Netburst */
- sizeof_field = sizeof(long);
- debugctl_mask = (1<<2)|(1<<3);
- break;
- default:
- /* sorry, don't know about them */
- break;
- }
- break;
- default:
- /* sorry, don't know about them */
- break;
- }
-}
-
-static inline void bts_enable(void)
-{
- unsigned long debugctl;
-
- rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
- wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl | debugctl_mask);
-}
-
-static inline void bts_disable(void)
-{
- unsigned long debugctl;
-
- rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
- wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl & ~debugctl_mask);
-}
-
-static void bts_trace_reset(struct trace_array *tr)
-{
- int cpu;
-
- tr->time_start = ftrace_now(tr->cpu);
-
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
-}
-
-static void bts_trace_start_cpu(void *arg)
-{
- this_tracer =
- ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS,
- /* ovfl = */ NULL, /* th = */ (size_t)-1);
- if (IS_ERR(this_tracer)) {
- this_tracer = NULL;
- return;
- }
-
- bts_enable();
-}
-
-static void bts_trace_start(struct trace_array *tr)
-{
- int cpu;
-
- bts_trace_reset(tr);
-
- for_each_cpu_mask(cpu, cpu_possible_map)
- smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1);
-}
-
-static void bts_trace_stop_cpu(void *arg)
-{
- if (this_tracer) {
- bts_disable();
-
- ds_release_bts(this_tracer);
- this_tracer = NULL;
- }
-}
-
-static void bts_trace_stop(struct trace_array *tr)
-{
- int cpu;
-
- for_each_cpu_mask(cpu, cpu_possible_map)
- smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1);
-}
-
-static int bts_trace_init(struct trace_array *tr)
-{
- bts_trace_cpuinit(&boot_cpu_data);
- bts_trace_reset(tr);
- bts_trace_start(tr);
-
- return 0;
-}
-
-static void bts_trace_print_header(struct seq_file *m)
-{
-#ifdef __i386__
- seq_puts(m, "# CPU# FROM TO FUNCTION\n");
- seq_puts(m, "# | | | |\n");
-#else
- seq_puts(m,
- "# CPU# FROM TO FUNCTION\n");
- seq_puts(m,
- "# | | | |\n");
-#endif
-}
-
-static enum print_line_t bts_trace_print_line(struct trace_iterator *iter)
-{
- struct trace_entry *entry = iter->ent;
- struct trace_seq *seq = &iter->seq;
- struct bts_entry *it;
-
- trace_assign_type(it, entry);
-
- if (entry->type == TRACE_BTS) {
- int ret;
-#ifdef CONFIG_KALLSYMS
- char function[KSYM_SYMBOL_LEN];
- sprint_symbol(function, it->from);
-#else
- char *function = "<unknown>";
-#endif
-
- ret = trace_seq_printf(seq, "%4d 0x%lx -> 0x%lx [%s]\n",
- entry->cpu, it->from, it->to, function);
- if (!ret)
- return TRACE_TYPE_PARTIAL_LINE;;
- return TRACE_TYPE_HANDLED;
- }
- return TRACE_TYPE_UNHANDLED;
-}
-
-void trace_bts(struct trace_array *tr, unsigned long from, unsigned long to)
-{
- struct ring_buffer_event *event;
- struct bts_entry *entry;
- unsigned long irq;
-
- event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq);
- if (!event)
- return;
- entry = ring_buffer_event_data(event);
- tracing_generic_entry_update(&entry->ent, 0, from);
- entry->ent.type = TRACE_BTS;
- entry->ent.cpu = smp_processor_id();
- entry->from = from;
- entry->to = to;
- ring_buffer_unlock_commit(tr->buffer, event, irq);
-}
-
-static void trace_bts_at(struct trace_array *tr, size_t index)
-{
- const void *raw = NULL;
- unsigned long from, to;
- int err;
-
- err = ds_access_bts(this_tracer, index, &raw);
- if (err < 0)
- return;
-
- from = *(const unsigned long *)raw;
- to = *(const unsigned long *)((const char *)raw + sizeof_field);
-
- trace_bts(tr, from, to);
-}
-
-static void trace_bts_cpu(void *arg)
-{
- struct trace_array *tr = (struct trace_array *) arg;
- size_t index = 0, end = 0, i;
- int err;
-
- if (!this_tracer)
- return;
-
- bts_disable();
-
- err = ds_get_bts_index(this_tracer, &index);
- if (err < 0)
- goto out;
-
- err = ds_get_bts_end(this_tracer, &end);
- if (err < 0)
- goto out;
-
- for (i = index; i < end; i++)
- trace_bts_at(tr, i);
-
- for (i = 0; i < index; i++)
- trace_bts_at(tr, i);
-
-out:
- bts_enable();
-}
-
-static void trace_bts_prepare(struct trace_iterator *iter)
-{
- int cpu;
-
- for_each_cpu_mask(cpu, cpu_possible_map)
- smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1);
-}
-
-struct tracer bts_tracer __read_mostly =
-{
- .name = "bts",
- .init = bts_trace_init,
- .reset = bts_trace_stop,
- .print_header = bts_trace_print_header,
- .print_line = bts_trace_print_line,
- .start = bts_trace_start,
- .stop = bts_trace_stop,
- .open = trace_bts_prepare
-};
-
-__init static int init_bts_trace(void)
-{
- return register_tracer(&bts_tracer);
-}
-device_initcall(init_bts_trace);
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index e74f6d0a3216..9236d7e25a16 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -16,20 +16,10 @@
#include "trace.h"
-static void function_reset(struct trace_array *tr)
-{
- int cpu;
-
- tr->time_start = ftrace_now(tr->cpu);
-
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
-}
-
static void start_function_trace(struct trace_array *tr)
{
tr->cpu = get_cpu();
- function_reset(tr);
+ tracing_reset_online_cpus(tr);
put_cpu();
tracing_start_cmdline_record();
@@ -55,7 +45,7 @@ static void function_trace_reset(struct trace_array *tr)
static void function_trace_start(struct trace_array *tr)
{
- function_reset(tr);
+ tracing_reset_online_cpus(tr);
}
static struct tracer function_trace __read_mostly =
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index af60eef4cbcc..930c08e5b38e 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -79,7 +79,7 @@ print_graph_cpu(struct trace_seq *s, int cpu)
int i;
int ret;
int log10_this = log10_cpu(cpu);
- int log10_all = log10_cpu(cpus_weight_nr(cpu_online_map));
+ int log10_all = log10_cpu(cpumask_weight(cpu_online_mask));
/*
@@ -231,6 +231,49 @@ trace_branch_is_leaf(struct trace_iterator *iter,
return true;
}
+static enum print_line_t
+print_graph_irq(struct trace_seq *s, unsigned long addr,
+ enum trace_type type, int cpu, pid_t pid)
+{
+ int ret;
+
+ if (addr < (unsigned long)__irqentry_text_start ||
+ addr >= (unsigned long)__irqentry_text_end)
+ return TRACE_TYPE_UNHANDLED;
+
+ if (type == TRACE_GRAPH_ENT) {
+ ret = trace_seq_printf(s, "==========> | ");
+ } else {
+ /* Cpu */
+ if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
+ ret = print_graph_cpu(s, cpu);
+ if (ret == TRACE_TYPE_PARTIAL_LINE)
+ return TRACE_TYPE_PARTIAL_LINE;
+ }
+ /* Proc */
+ if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
+ ret = print_graph_proc(s, pid);
+ if (ret == TRACE_TYPE_PARTIAL_LINE)
+ return TRACE_TYPE_PARTIAL_LINE;
+
+ ret = trace_seq_printf(s, " | ");
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+ }
+
+ /* No overhead */
+ if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
+ ret = trace_seq_printf(s, " ");
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+ }
+
+ ret = trace_seq_printf(s, "<========== |\n");
+ }
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+ return TRACE_TYPE_HANDLED;
+}
static enum print_line_t
print_graph_duration(unsigned long long duration, struct trace_seq *s)
@@ -344,7 +387,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
static enum print_line_t
print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
- struct trace_seq *s)
+ struct trace_seq *s, pid_t pid, int cpu)
{
int i;
int ret;
@@ -357,8 +400,18 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
return TRACE_TYPE_PARTIAL_LINE;
}
- /* No time */
- ret = trace_seq_printf(s, " | ");
+ /* Interrupt */
+ ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, pid);
+ if (ret == TRACE_TYPE_UNHANDLED) {
+ /* No time */
+ ret = trace_seq_printf(s, " | ");
+ if (!ret)
+ return TRACE_TYPE_PARTIAL_LINE;
+ } else {
+ if (ret == TRACE_TYPE_PARTIAL_LINE)
+ return TRACE_TYPE_PARTIAL_LINE;
+ }
+
/* Function */
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
@@ -410,7 +463,7 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
if (trace_branch_is_leaf(iter, field))
return print_graph_entry_leaf(iter, field, s);
else
- return print_graph_entry_nested(field, s);
+ return print_graph_entry_nested(field, s, iter->ent->pid, cpu);
}
@@ -474,6 +527,11 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
}
+
+ ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid);
+ if (ret == TRACE_TYPE_PARTIAL_LINE)
+ return TRACE_TYPE_PARTIAL_LINE;
+
return TRACE_TYPE_HANDLED;
}
diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c
new file mode 100644
index 000000000000..649df22d435f
--- /dev/null
+++ b/kernel/trace/trace_hw_branches.c
@@ -0,0 +1,195 @@
+/*
+ * h/w branch tracer for x86 based on bts
+ *
+ * Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/ftrace.h>
+#include <linux/kallsyms.h>
+
+#include <asm/ds.h>
+
+#include "trace.h"
+
+
+#define SIZEOF_BTS (1 << 13)
+
+static DEFINE_PER_CPU(struct bts_tracer *, tracer);
+static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer);
+
+#define this_tracer per_cpu(tracer, smp_processor_id())
+#define this_buffer per_cpu(buffer, smp_processor_id())
+
+
+static void bts_trace_start_cpu(void *arg)
+{
+ if (this_tracer)
+ ds_release_bts(this_tracer);
+
+ this_tracer =
+ ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS,
+ /* ovfl = */ NULL, /* th = */ (size_t)-1,
+ BTS_KERNEL);
+ if (IS_ERR(this_tracer)) {
+ this_tracer = NULL;
+ return;
+ }
+}
+
+static void bts_trace_start(struct trace_array *tr)
+{
+ int cpu;
+
+ tracing_reset_online_cpus(tr);
+
+ for_each_cpu(cpu, cpu_possible_mask)
+ smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1);
+}
+
+static void bts_trace_stop_cpu(void *arg)
+{
+ if (this_tracer) {
+ ds_release_bts(this_tracer);
+ this_tracer = NULL;
+ }
+}
+
+static void bts_trace_stop(struct trace_array *tr)
+{
+ int cpu;
+
+ for_each_cpu(cpu, cpu_possible_mask)
+ smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1);
+}
+
+static int bts_trace_init(struct trace_array *tr)
+{
+ tracing_reset_online_cpus(tr);
+ bts_trace_start(tr);
+
+ return 0;
+}
+
+static void bts_trace_print_header(struct seq_file *m)
+{
+ seq_puts(m,
+ "# CPU# FROM TO FUNCTION\n");
+ seq_puts(m,
+ "# | | | |\n");
+}
+
+static enum print_line_t bts_trace_print_line(struct trace_iterator *iter)
+{
+ struct trace_entry *entry = iter->ent;
+ struct trace_seq *seq = &iter->seq;
+ struct hw_branch_entry *it;
+
+ trace_assign_type(it, entry);
+
+ if (entry->type == TRACE_HW_BRANCHES) {
+ if (trace_seq_printf(seq, "%4d ", entry->cpu) &&
+ trace_seq_printf(seq, "0x%016llx -> 0x%016llx ",
+ it->from, it->to) &&
+ (!it->from ||
+ seq_print_ip_sym(seq, it->from, /* sym_flags = */ 0)) &&
+ trace_seq_printf(seq, "\n"))
+ return TRACE_TYPE_HANDLED;
+ return TRACE_TYPE_PARTIAL_LINE;;
+ }
+ return TRACE_TYPE_UNHANDLED;
+}
+
+void trace_hw_branch(struct trace_array *tr, u64 from, u64 to)
+{
+ struct ring_buffer_event *event;
+ struct hw_branch_entry *entry;
+ unsigned long irq;
+
+ event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq);
+ if (!event)
+ return;
+ entry = ring_buffer_event_data(event);
+ tracing_generic_entry_update(&entry->ent, 0, from);
+ entry->ent.type = TRACE_HW_BRANCHES;
+ entry->ent.cpu = smp_processor_id();
+ entry->from = from;
+ entry->to = to;
+ ring_buffer_unlock_commit(tr->buffer, event, irq);
+}
+
+static void trace_bts_at(struct trace_array *tr,
+ const struct bts_trace *trace, void *at)
+{
+ struct bts_struct bts;
+ int err = 0;
+
+ WARN_ON_ONCE(!trace->read);
+ if (!trace->read)
+ return;
+
+ err = trace->read(this_tracer, at, &bts);
+ if (err < 0)
+ return;
+
+ switch (bts.qualifier) {
+ case BTS_BRANCH:
+ trace_hw_branch(tr, bts.variant.lbr.from, bts.variant.lbr.to);
+ break;
+ }
+}
+
+static void trace_bts_cpu(void *arg)
+{
+ struct trace_array *tr = (struct trace_array *) arg;
+ const struct bts_trace *trace;
+ unsigned char *at;
+
+ if (!this_tracer)
+ return;
+
+ ds_suspend_bts(this_tracer);
+ trace = ds_read_bts(this_tracer);
+ if (!trace)
+ goto out;
+
+ for (at = trace->ds.top; (void *)at < trace->ds.end;
+ at += trace->ds.size)
+ trace_bts_at(tr, trace, at);
+
+ for (at = trace->ds.begin; (void *)at < trace->ds.top;
+ at += trace->ds.size)
+ trace_bts_at(tr, trace, at);
+
+out:
+ ds_resume_bts(this_tracer);
+}
+
+static void trace_bts_prepare(struct trace_iterator *iter)
+{
+ int cpu;
+
+ for_each_cpu(cpu, cpu_possible_mask)
+ smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1);
+}
+
+struct tracer bts_tracer __read_mostly =
+{
+ .name = "hw-branch-tracer",
+ .init = bts_trace_init,
+ .reset = bts_trace_stop,
+ .print_header = bts_trace_print_header,
+ .print_line = bts_trace_print_line,
+ .start = bts_trace_start,
+ .stop = bts_trace_stop,
+ .open = trace_bts_prepare
+};
+
+__init static int init_bts_trace(void)
+{
+ return register_tracer(&bts_tracer);
+}
+device_initcall(init_bts_trace);
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index 2fb6da6523b3..fffcb069f1dc 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -22,14 +22,10 @@ static unsigned long prev_overruns;
static void mmio_reset_data(struct trace_array *tr)
{
- int cpu;
-
overrun_detected = false;
prev_overruns = 0;
- tr->time_start = ftrace_now(tr->cpu);
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
+ tracing_reset_online_cpus(tr);
}
static int mmio_trace_init(struct trace_array *tr)
diff --git a/kernel/trace/trace_power.c b/kernel/trace/trace_power.c
index a7172a352f62..7bda248daf55 100644
--- a/kernel/trace/trace_power.c
+++ b/kernel/trace/trace_power.c
@@ -39,7 +39,7 @@ static int power_trace_init(struct trace_array *tr)
trace_power_enabled = 1;
- for_each_cpu_mask(cpu, cpu_possible_map)
+ for_each_cpu(cpu, cpu_possible_mask)
tracing_reset(tr, cpu);
return 0;
}
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c
index 863390557b44..df175cb4564f 100644
--- a/kernel/trace/trace_sched_switch.c
+++ b/kernel/trace/trace_sched_switch.c
@@ -49,7 +49,7 @@ probe_sched_switch(struct rq *__rq, struct task_struct *prev,
}
static void
-probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
+probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
{
struct trace_array_cpu *data;
unsigned long flags;
@@ -72,16 +72,6 @@ probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
local_irq_restore(flags);
}
-static void sched_switch_reset(struct trace_array *tr)
-{
- int cpu;
-
- tr->time_start = ftrace_now(tr->cpu);
-
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
-}
-
static int tracing_sched_register(void)
{
int ret;
@@ -197,7 +187,7 @@ void tracing_sched_switch_assign_trace(struct trace_array *tr)
static void start_sched_trace(struct trace_array *tr)
{
- sched_switch_reset(tr);
+ tracing_reset_online_cpus(tr);
tracing_start_sched_switch_record();
}
@@ -221,7 +211,7 @@ static void sched_switch_trace_reset(struct trace_array *tr)
static void sched_switch_trace_start(struct trace_array *tr)
{
- sched_switch_reset(tr);
+ tracing_reset_online_cpus(tr);
tracing_start_sched_switch();
}
@@ -247,3 +237,4 @@ __init static int init_sched_switch_trace(void)
return register_tracer(&sched_switch_trace);
}
device_initcall(init_sched_switch_trace);
+
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 0067b49746c1..43586b689e31 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -211,7 +211,7 @@ static void wakeup_reset(struct trace_array *tr)
}
static void
-probe_wakeup(struct rq *rq, struct task_struct *p)
+probe_wakeup(struct rq *rq, struct task_struct *p, int success)
{
int cpu = smp_processor_id();
unsigned long flags;
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 0b863f2cbc8e..d0871bc0aca5 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -10,6 +10,7 @@
#include <linux/debugfs.h>
#include <linux/ftrace.h>
#include <linux/module.h>
+#include <linux/sysctl.h>
#include <linux/init.h>
#include <linux/fs.h>
#include "trace.h"
@@ -31,6 +32,10 @@ static raw_spinlock_t max_stack_lock =
static int stack_trace_disabled __read_mostly;
static DEFINE_PER_CPU(int, trace_active);
+static DEFINE_MUTEX(stack_sysctl_mutex);
+
+int stack_tracer_enabled;
+static int last_stack_tracer_enabled;
static inline void check_stack(void)
{
@@ -174,7 +179,7 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,
return count;
}
-static struct file_operations stack_max_size_fops = {
+static const struct file_operations stack_max_size_fops = {
.open = tracing_open_generic,
.read = stack_max_size_read,
.write = stack_max_size_write,
@@ -272,7 +277,7 @@ static int t_show(struct seq_file *m, void *v)
return 0;
}
-static struct seq_operations stack_trace_seq_ops = {
+static const struct seq_operations stack_trace_seq_ops = {
.start = t_start,
.next = t_next,
.stop = t_stop,
@@ -288,12 +293,47 @@ static int stack_trace_open(struct inode *inode, struct file *file)
return ret;
}
-static struct file_operations stack_trace_fops = {
+static const struct file_operations stack_trace_fops = {
.open = stack_trace_open,
.read = seq_read,
.llseek = seq_lseek,
};
+int
+stack_trace_sysctl(struct ctl_table *table, int write,
+ struct file *file, void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret;
+
+ mutex_lock(&stack_sysctl_mutex);
+
+ ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
+
+ if (ret || !write ||
+ (last_stack_tracer_enabled == stack_tracer_enabled))
+ goto out;
+
+ last_stack_tracer_enabled = stack_tracer_enabled;
+
+ if (stack_tracer_enabled)
+ register_ftrace_function(&trace_ops);
+ else
+ unregister_ftrace_function(&trace_ops);
+
+ out:
+ mutex_unlock(&stack_sysctl_mutex);
+ return ret;
+}
+
+static __init int enable_stacktrace(char *str)
+{
+ stack_tracer_enabled = 1;
+ last_stack_tracer_enabled = 1;
+ return 1;
+}
+__setup("stacktrace", enable_stacktrace);
+
static __init int stack_trace_init(void)
{
struct dentry *d_tracer;
@@ -311,7 +351,8 @@ static __init int stack_trace_init(void)
if (!entry)
pr_warning("Could not create debugfs 'stack_trace' entry\n");
- register_ftrace_function(&trace_ops);
+ if (stack_tracer_enabled)
+ register_ftrace_function(&trace_ops);
return 0;
}
diff --git a/kernel/trace/trace_sysprof.c b/kernel/trace/trace_sysprof.c
index 54960edb96d0..eaca5ad803ff 100644
--- a/kernel/trace/trace_sysprof.c
+++ b/kernel/trace/trace_sysprof.c
@@ -196,27 +196,19 @@ static enum hrtimer_restart stack_trace_timer_fn(struct hrtimer *hrtimer)
return HRTIMER_RESTART;
}
-static void start_stack_timer(int cpu)
+static void start_stack_timer(void *unused)
{
- struct hrtimer *hrtimer = &per_cpu(stack_trace_hrtimer, cpu);
+ struct hrtimer *hrtimer = &__get_cpu_var(stack_trace_hrtimer);
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer->function = stack_trace_timer_fn;
- hrtimer->cb_mode = HRTIMER_CB_IRQSAFE_PERCPU;
hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL);
}
static void start_stack_timers(void)
{
- cpumask_t saved_mask = current->cpus_allowed;
- int cpu;
-
- for_each_online_cpu(cpu) {
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
- start_stack_timer(cpu);
- }
- set_cpus_allowed_ptr(current, &saved_mask);
+ on_each_cpu(start_stack_timer, NULL, 1);
}
static void stop_stack_timer(int cpu)
@@ -234,20 +226,10 @@ static void stop_stack_timers(void)
stop_stack_timer(cpu);
}
-static void stack_reset(struct trace_array *tr)
-{
- int cpu;
-
- tr->time_start = ftrace_now(tr->cpu);
-
- for_each_online_cpu(cpu)
- tracing_reset(tr, cpu);
-}
-
static void start_stack_trace(struct trace_array *tr)
{
mutex_lock(&sample_timer_lock);
- stack_reset(tr);
+ tracing_reset_online_cpus(tr);
start_stack_timers();
tracer_enabled = 1;
mutex_unlock(&sample_timer_lock);
diff --git a/kernel/tsacct.c b/kernel/tsacct.c
index 8ebcd8532dfb..2dc06ab35716 100644
--- a/kernel/tsacct.c
+++ b/kernel/tsacct.c
@@ -27,6 +27,7 @@
*/
void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
{
+ const struct cred *tcred;
struct timespec uptime, ts;
u64 ac_etime;
@@ -53,10 +54,11 @@ void bacct_add_tsk(struct taskstats *stats, struct task_struct *tsk)
stats->ac_flag |= AXSIG;
stats->ac_nice = task_nice(tsk);
stats->ac_sched = tsk->policy;
- stats->ac_uid = tsk->uid;
- stats->ac_gid = tsk->gid;
stats->ac_pid = tsk->pid;
rcu_read_lock();
+ tcred = __task_cred(tsk);
+ stats->ac_uid = tcred->uid;
+ stats->ac_gid = tcred->gid;
stats->ac_ppid = pid_alive(tsk) ?
rcu_dereference(tsk->real_parent)->tgid : 0;
rcu_read_unlock();
diff --git a/kernel/uid16.c b/kernel/uid16.c
index 3e41c1673e2f..2460c3199b5a 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -84,11 +84,12 @@ asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)
asmlinkage long sys_getresuid16(old_uid_t __user *ruid, old_uid_t __user *euid, old_uid_t __user *suid)
{
+ const struct cred *cred = current_cred();
int retval;
- if (!(retval = put_user(high2lowuid(current->uid), ruid)) &&
- !(retval = put_user(high2lowuid(current->euid), euid)))
- retval = put_user(high2lowuid(current->suid), suid);
+ if (!(retval = put_user(high2lowuid(cred->uid), ruid)) &&
+ !(retval = put_user(high2lowuid(cred->euid), euid)))
+ retval = put_user(high2lowuid(cred->suid), suid);
return retval;
}
@@ -104,11 +105,12 @@ asmlinkage long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)
asmlinkage long sys_getresgid16(old_gid_t __user *rgid, old_gid_t __user *egid, old_gid_t __user *sgid)
{
+ const struct cred *cred = current_cred();
int retval;
- if (!(retval = put_user(high2lowgid(current->gid), rgid)) &&
- !(retval = put_user(high2lowgid(current->egid), egid)))
- retval = put_user(high2lowgid(current->sgid), sgid);
+ if (!(retval = put_user(high2lowgid(cred->gid), rgid)) &&
+ !(retval = put_user(high2lowgid(cred->egid), egid)))
+ retval = put_user(high2lowgid(cred->sgid), sgid);
return retval;
}
@@ -161,25 +163,24 @@ static int groups16_from_user(struct group_info *group_info,
asmlinkage long sys_getgroups16(int gidsetsize, old_gid_t __user *grouplist)
{
- int i = 0;
+ const struct cred *cred = current_cred();
+ int i;
if (gidsetsize < 0)
return -EINVAL;
- get_group_info(current->group_info);
- i = current->group_info->ngroups;
+ i = cred->group_info->ngroups;
if (gidsetsize) {
if (i > gidsetsize) {
i = -EINVAL;
goto out;
}
- if (groups16_to_user(grouplist, current->group_info)) {
+ if (groups16_to_user(grouplist, cred->group_info)) {
i = -EFAULT;
goto out;
}
}
out:
- put_group_info(current->group_info);
return i;
}
@@ -210,20 +211,20 @@ asmlinkage long sys_setgroups16(int gidsetsize, old_gid_t __user *grouplist)
asmlinkage long sys_getuid16(void)
{
- return high2lowuid(current->uid);
+ return high2lowuid(current_uid());
}
asmlinkage long sys_geteuid16(void)
{
- return high2lowuid(current->euid);
+ return high2lowuid(current_euid());
}
asmlinkage long sys_getgid16(void)
{
- return high2lowgid(current->gid);
+ return high2lowgid(current_gid());
}
asmlinkage long sys_getegid16(void)
{
- return high2lowgid(current->egid);
+ return high2lowgid(current_egid());
}
diff --git a/kernel/user.c b/kernel/user.c
index cec2224bc9f5..477b6660f447 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -16,12 +16,13 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/user_namespace.h>
+#include "cred-internals.h"
struct user_namespace init_user_ns = {
.kref = {
- .refcount = ATOMIC_INIT(2),
+ .refcount = ATOMIC_INIT(1),
},
- .root_user = &root_user,
+ .creator = &root_user,
};
EXPORT_SYMBOL_GPL(init_user_ns);
@@ -47,12 +48,14 @@ static struct kmem_cache *uid_cachep;
*/
static DEFINE_SPINLOCK(uidhash_lock);
+/* root_user.__count is 2, 1 for init task cred, 1 for init_user_ns->creator */
struct user_struct root_user = {
- .__count = ATOMIC_INIT(1),
+ .__count = ATOMIC_INIT(2),
.processes = ATOMIC_INIT(1),
.files = ATOMIC_INIT(0),
.sigpending = ATOMIC_INIT(0),
.locked_shm = 0,
+ .user_ns = &init_user_ns,
#ifdef CONFIG_USER_SCHED
.tg = &init_task_group,
#endif
@@ -106,16 +109,10 @@ static int sched_create_user(struct user_struct *up)
return rc;
}
-static void sched_switch_user(struct task_struct *p)
-{
- sched_move_task(p);
-}
-
#else /* CONFIG_USER_SCHED */
static void sched_destroy_user(struct user_struct *up) { }
static int sched_create_user(struct user_struct *up) { return 0; }
-static void sched_switch_user(struct task_struct *p) { }
#endif /* CONFIG_USER_SCHED */
@@ -244,13 +241,21 @@ static struct kobj_type uids_ktype = {
.release = uids_release,
};
-/* create /sys/kernel/uids/<uid>/cpu_share file for this user */
+/*
+ * Create /sys/kernel/uids/<uid>/cpu_share file for this user
+ * We do not create this file for users in a user namespace (until
+ * sysfs tagging is implemented).
+ *
+ * See Documentation/scheduler/sched-design-CFS.txt for ramifications.
+ */
static int uids_user_create(struct user_struct *up)
{
struct kobject *kobj = &up->kobj;
int error;
memset(kobj, 0, sizeof(struct kobject));
+ if (up->user_ns != &init_user_ns)
+ return 0;
kobj->kset = uids_kset;
error = kobject_init_and_add(kobj, &uids_ktype, NULL, "%d", up->uid);
if (error) {
@@ -286,6 +291,8 @@ static void remove_user_sysfs_dir(struct work_struct *w)
unsigned long flags;
int remove_user = 0;
+ if (up->user_ns != &init_user_ns)
+ return;
/* Make uid_hash_remove() + sysfs_remove_file() + kobject_del()
* atomic.
*/
@@ -321,12 +328,13 @@ done:
* IRQ state (as stored in flags) is restored and uidhash_lock released
* upon function exit.
*/
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
{
/* restore back the count */
atomic_inc(&up->__count);
spin_unlock_irqrestore(&uidhash_lock, flags);
+ put_user_ns(up->user_ns);
INIT_WORK(&up->work, remove_user_sysfs_dir);
schedule_work(&up->work);
}
@@ -342,13 +350,14 @@ static inline void uids_mutex_unlock(void) { }
* IRQ state (as stored in flags) is restored and uidhash_lock released
* upon function exit.
*/
-static inline void free_user(struct user_struct *up, unsigned long flags)
+static void free_user(struct user_struct *up, unsigned long flags)
{
uid_hash_remove(up);
spin_unlock_irqrestore(&uidhash_lock, flags);
sched_destroy_user(up);
key_put(up->uid_keyring);
key_put(up->session_keyring);
+ put_user_ns(up->user_ns);
kmem_cache_free(uid_cachep, up);
}
@@ -364,7 +373,7 @@ struct user_struct *find_user(uid_t uid)
{
struct user_struct *ret;
unsigned long flags;
- struct user_namespace *ns = current->nsproxy->user_ns;
+ struct user_namespace *ns = current_user_ns();
spin_lock_irqsave(&uidhash_lock, flags);
ret = uid_hash_find(uid, uidhashentry(ns, uid));
@@ -411,6 +420,8 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
if (sched_create_user(new) < 0)
goto out_free_user;
+ new->user_ns = get_user_ns(ns);
+
if (uids_user_create(new))
goto out_destoy_sched;
@@ -434,7 +445,6 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
up = new;
}
spin_unlock_irq(&uidhash_lock);
-
}
uids_mutex_unlock();
@@ -443,6 +453,7 @@ struct user_struct *alloc_uid(struct user_namespace *ns, uid_t uid)
out_destoy_sched:
sched_destroy_user(new);
+ put_user_ns(new->user_ns);
out_free_user:
kmem_cache_free(uid_cachep, new);
out_unlock:
@@ -450,63 +461,6 @@ out_unlock:
return NULL;
}
-void switch_uid(struct user_struct *new_user)
-{
- struct user_struct *old_user;
-
- /* What if a process setreuid()'s and this brings the
- * new uid over his NPROC rlimit? We can check this now
- * cheaply with the new uid cache, so if it matters
- * we should be checking for it. -DaveM
- */
- old_user = current->user;
- atomic_inc(&new_user->processes);
- atomic_dec(&old_user->processes);
- switch_uid_keyring(new_user);
- current->user = new_user;
- sched_switch_user(current);
-
- /*
- * We need to synchronize with __sigqueue_alloc()
- * doing a get_uid(p->user).. If that saw the old
- * user value, we need to wait until it has exited
- * its critical region before we can free the old
- * structure.
- */
- smp_mb();
- spin_unlock_wait(&current->sighand->siglock);
-
- free_uid(old_user);
- suid_keys(current);
-}
-
-#ifdef CONFIG_USER_NS
-void release_uids(struct user_namespace *ns)
-{
- int i;
- unsigned long flags;
- struct hlist_head *head;
- struct hlist_node *nd;
-
- spin_lock_irqsave(&uidhash_lock, flags);
- /*
- * collapse the chains so that the user_struct-s will
- * be still alive, but not in hashes. subsequent free_uid()
- * will free them.
- */
- for (i = 0; i < UIDHASH_SZ; i++) {
- head = ns->uidhash_table + i;
- while (!hlist_empty(head)) {
- nd = head->first;
- hlist_del_init(nd);
- }
- }
- spin_unlock_irqrestore(&uidhash_lock, flags);
-
- free_uid(ns->root_user);
-}
-#endif
-
static int __init uid_cache_init(void)
{
int n;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 532858fa5b88..79084311ee57 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -9,60 +9,55 @@
#include <linux/nsproxy.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
+#include <linux/cred.h>
/*
- * Clone a new ns copying an original user ns, setting refcount to 1
- * @old_ns: namespace to clone
- * Return NULL on error (failure to kmalloc), new ns otherwise
+ * Create a new user namespace, deriving the creator from the user in the
+ * passed credentials, and replacing that user with the new root user for the
+ * new namespace.
+ *
+ * This is called by copy_creds(), which will finish setting the target task's
+ * credentials.
*/
-static struct user_namespace *clone_user_ns(struct user_namespace *old_ns)
+int create_user_ns(struct cred *new)
{
struct user_namespace *ns;
- struct user_struct *new_user;
+ struct user_struct *root_user;
int n;
ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
if (!ns)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
kref_init(&ns->kref);
for (n = 0; n < UIDHASH_SZ; ++n)
INIT_HLIST_HEAD(ns->uidhash_table + n);
- /* Insert new root user. */
- ns->root_user = alloc_uid(ns, 0);
- if (!ns->root_user) {
+ /* Alloc new root user. */
+ root_user = alloc_uid(ns, 0);
+ if (!root_user) {
kfree(ns);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
- /* Reset current->user with a new one */
- new_user = alloc_uid(ns, current->uid);
- if (!new_user) {
- free_uid(ns->root_user);
- kfree(ns);
- return ERR_PTR(-ENOMEM);
- }
-
- switch_uid(new_user);
- return ns;
-}
-
-struct user_namespace * copy_user_ns(int flags, struct user_namespace *old_ns)
-{
- struct user_namespace *new_ns;
-
- BUG_ON(!old_ns);
- get_user_ns(old_ns);
-
- if (!(flags & CLONE_NEWUSER))
- return old_ns;
+ /* set the new root user in the credentials under preparation */
+ ns->creator = new->user;
+ new->user = root_user;
+ new->uid = new->euid = new->suid = new->fsuid = 0;
+ new->gid = new->egid = new->sgid = new->fsgid = 0;
+ put_group_info(new->group_info);
+ new->group_info = get_group_info(&init_groups);
+#ifdef CONFIG_KEYS
+ key_put(new->request_key_auth);
+ new->request_key_auth = NULL;
+#endif
+ /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */
- new_ns = clone_user_ns(old_ns);
+ /* alloc_uid() incremented the userns refcount. Just set it to 1 */
+ kref_set(&ns->kref, 1);
- put_user_ns(old_ns);
- return new_ns;
+ return 0;
}
void free_user_ns(struct kref *kref)
@@ -70,7 +65,7 @@ void free_user_ns(struct kref *kref)
struct user_namespace *ns;
ns = container_of(kref, struct user_namespace, kref);
- release_uids(ns);
+ free_uid(ns->creator);
kfree(ns);
}
EXPORT_SYMBOL(free_user_ns);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index d4dc69ddebd7..2f445833ae37 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -73,7 +73,7 @@ static DEFINE_SPINLOCK(workqueue_lock);
static LIST_HEAD(workqueues);
static int singlethread_cpu __read_mostly;
-static cpumask_t cpu_singlethread_map __read_mostly;
+static const struct cpumask *cpu_singlethread_map __read_mostly;
/*
* _cpu_down() first removes CPU from cpu_online_map, then CPU_DEAD
* flushes cwq->worklist. This means that flush_workqueue/wait_on_work
@@ -81,24 +81,24 @@ static cpumask_t cpu_singlethread_map __read_mostly;
* use cpu_possible_map, the cpumask below is more a documentation
* than optimization.
*/
-static cpumask_t cpu_populated_map __read_mostly;
+static cpumask_var_t cpu_populated_map __read_mostly;
/* If it's single threaded, it isn't in the list of workqueues. */
-static inline int is_single_threaded(struct workqueue_struct *wq)
+static inline int is_wq_single_threaded(struct workqueue_struct *wq)
{
return wq->singlethread;
}
-static const cpumask_t *wq_cpu_map(struct workqueue_struct *wq)
+static const struct cpumask *wq_cpu_map(struct workqueue_struct *wq)
{
- return is_single_threaded(wq)
- ? &cpu_singlethread_map : &cpu_populated_map;
+ return is_wq_single_threaded(wq)
+ ? cpu_singlethread_map : cpu_populated_map;
}
static
struct cpu_workqueue_struct *wq_per_cpu(struct workqueue_struct *wq, int cpu)
{
- if (unlikely(is_single_threaded(wq)))
+ if (unlikely(is_wq_single_threaded(wq)))
cpu = singlethread_cpu;
return per_cpu_ptr(wq->cpu_wq, cpu);
}
@@ -410,7 +410,7 @@ static int flush_cpu_workqueue(struct cpu_workqueue_struct *cwq)
*/
void flush_workqueue(struct workqueue_struct *wq)
{
- const cpumask_t *cpu_map = wq_cpu_map(wq);
+ const struct cpumask *cpu_map = wq_cpu_map(wq);
int cpu;
might_sleep();
@@ -532,7 +532,7 @@ static void wait_on_work(struct work_struct *work)
{
struct cpu_workqueue_struct *cwq;
struct workqueue_struct *wq;
- const cpumask_t *cpu_map;
+ const struct cpumask *cpu_map;
int cpu;
might_sleep();
@@ -769,7 +769,7 @@ static int create_workqueue_thread(struct cpu_workqueue_struct *cwq, int cpu)
{
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
struct workqueue_struct *wq = cwq->wq;
- const char *fmt = is_single_threaded(wq) ? "%s" : "%s/%d";
+ const char *fmt = is_wq_single_threaded(wq) ? "%s" : "%s/%d";
struct task_struct *p;
p = kthread_create(worker_thread, cwq, fmt, wq->name, cpu);
@@ -903,7 +903,7 @@ static void cleanup_workqueue_thread(struct cpu_workqueue_struct *cwq)
*/
void destroy_workqueue(struct workqueue_struct *wq)
{
- const cpumask_t *cpu_map = wq_cpu_map(wq);
+ const struct cpumask *cpu_map = wq_cpu_map(wq);
int cpu;
cpu_maps_update_begin();
@@ -933,7 +933,7 @@ static int __devinit workqueue_cpu_callback(struct notifier_block *nfb,
switch (action) {
case CPU_UP_PREPARE:
- cpu_set(cpu, cpu_populated_map);
+ cpumask_set_cpu(cpu, cpu_populated_map);
}
undo:
list_for_each_entry(wq, &workqueues, list) {
@@ -964,7 +964,7 @@ undo:
switch (action) {
case CPU_UP_CANCELED:
case CPU_POST_DEAD:
- cpu_clear(cpu, cpu_populated_map);
+ cpumask_clear_cpu(cpu, cpu_populated_map);
}
return ret;
@@ -1017,9 +1017,11 @@ EXPORT_SYMBOL_GPL(work_on_cpu);
void __init init_workqueues(void)
{
- cpu_populated_map = cpu_online_map;
- singlethread_cpu = first_cpu(cpu_possible_map);
- cpu_singlethread_map = cpumask_of_cpu(singlethread_cpu);
+ alloc_cpumask_var(&cpu_populated_map, GFP_KERNEL);
+
+ cpumask_copy(cpu_populated_map, cpu_online_mask);
+ singlethread_cpu = cpumask_first(cpu_possible_mask);
+ cpu_singlethread_map = cpumask_of(singlethread_cpu);
hotcpu_notifier(workqueue_cpu_callback, 0);
keventd_wq = create_workqueue("events");
BUG_ON(!keventd_wq);
diff --git a/lib/Kconfig b/lib/Kconfig
index 7823f8342abf..03c2c24b9083 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -13,6 +13,10 @@ config GENERIC_FIND_FIRST_BIT
config GENERIC_FIND_NEXT_BIT
bool
+config GENERIC_FIND_LAST_BIT
+ bool
+ default y
+
config CRC_CCITT
tristate "CRC-CCITT functions"
help
@@ -64,6 +68,8 @@ config CRC7
config LIBCRC32C
tristate "CRC32c (Castagnoli, et al) Cyclic Redundancy-Check"
+ select CRYPTO
+ select CRYPTO_CRC32C
help
This option is provided for the case where no in-kernel-tree
modules require CRC32c functions, but a module built outside the
@@ -164,4 +170,8 @@ config CPUMASK_OFFSTACK
them on the stack. This is a bit more expensive, but avoids
stack overflow.
+config DISABLE_OBSOLETE_CPUMASK_FUNCTIONS
+ bool "Disable obsolete cpumask functions" if DEBUG_PER_CPU_MAPS
+ depends on EXPERIMENTAL && BROKEN
+
endmenu
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1e3fd3e3436a..2e75478e9c69 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -252,6 +252,14 @@ config DEBUG_OBJECTS_TIMERS
timer routines to track the life time of timer objects and
validate the timer operations.
+config DEBUG_OBJECTS_ENABLE_DEFAULT
+ int "debug_objects bootup default value (0-1)"
+ range 0 1
+ default "1"
+ depends on DEBUG_OBJECTS
+ help
+ Debug objects boot parameter default value
+
config DEBUG_SLAB
bool "Debug slab memory allocations"
depends on DEBUG_KERNEL && SLAB
@@ -629,6 +637,19 @@ config RCU_CPU_STALL_DETECTOR
Say N if you are unsure.
+config RCU_CPU_STALL_DETECTOR
+ bool "Check for stalled CPUs delaying RCU grace periods"
+ depends on CLASSIC_RCU || TREE_RCU
+ default n
+ help
+ This option causes RCU to printk information on which
+ CPUs are delaying the current grace period, but only when
+ the grace period extends for excessive time periods.
+
+ Say Y if you want RCU to perform such checks.
+
+ Say N if you are unsure.
+
config KPROBES_SANITY_TEST
bool "Kprobes sanity tests"
depends on DEBUG_KERNEL
@@ -709,6 +730,7 @@ config FAULT_INJECTION
config FAILSLAB
bool "Fault-injection capability for kmalloc"
depends on FAULT_INJECTION
+ depends on SLAB || SLUB
help
Provide fault-injection capability for kmalloc.
diff --git a/lib/Makefile b/lib/Makefile
index 7cb65d85aeb0..32b0e64ded27 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -11,7 +11,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
rbtree.o radix-tree.o dump_stack.o \
idr.o int_sqrt.o extable.o prio_tree.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
- proportions.o prio_heap.o ratelimit.o show_mem.o
+ proportions.o prio_heap.o ratelimit.o show_mem.o is_single_threaded.o
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
@@ -37,6 +37,7 @@ lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
lib-$(CONFIG_GENERIC_FIND_FIRST_BIT) += find_next_bit.o
lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
+lib-$(CONFIG_GENERIC_FIND_LAST_BIT) += find_last_bit.o
obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o
obj-$(CONFIG_PLIST) += plist.o
diff --git a/lib/bug.c b/lib/bug.c
index bfeafd60ee9f..300e41afbf97 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -5,6 +5,8 @@
CONFIG_BUG - emit BUG traps. Nothing happens without this.
CONFIG_GENERIC_BUG - enable this code.
+ CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit pointers relative to
+ the containing struct bug_entry for bug_addr and file.
CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG
CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable
@@ -43,6 +45,15 @@
extern const struct bug_entry __start___bug_table[], __stop___bug_table[];
+static inline unsigned long bug_addr(const struct bug_entry *bug)
+{
+#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
+ return bug->bug_addr;
+#else
+ return (unsigned long)bug + bug->bug_addr_disp;
+#endif
+}
+
#ifdef CONFIG_MODULES
static LIST_HEAD(module_bug_list);
@@ -55,7 +66,7 @@ static const struct bug_entry *module_find_bug(unsigned long bugaddr)
unsigned i;
for (i = 0; i < mod->num_bugs; ++i, ++bug)
- if (bugaddr == bug->bug_addr)
+ if (bugaddr == bug_addr(bug))
return bug;
}
return NULL;
@@ -108,7 +119,7 @@ const struct bug_entry *find_bug(unsigned long bugaddr)
const struct bug_entry *bug;
for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
- if (bugaddr == bug->bug_addr)
+ if (bugaddr == bug_addr(bug))
return bug;
return module_find_bug(bugaddr);
@@ -133,7 +144,11 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs)
if (bug) {
#ifdef CONFIG_DEBUG_BUGVERBOSE
+#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
file = bug->file;
+#else
+ file = (const char *)bug + bug->file_disp;
+#endif
line = bug->line;
#endif
warning = (bug->flags & BUGFLAG_WARNING) != 0;
diff --git a/lib/cpumask.c b/lib/cpumask.c
index 8d03f22c6ced..3389e2440da0 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -76,15 +76,28 @@ int cpumask_any_but(const struct cpumask *mask, unsigned int cpu)
/* These are not inline because of header tangles. */
#ifdef CONFIG_CPUMASK_OFFSTACK
-bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
+/**
+ * alloc_cpumask_var_node - allocate a struct cpumask on a given node
+ * @mask: pointer to cpumask_var_t where the cpumask is returned
+ * @flags: GFP_ flags
+ *
+ * Only defined when CONFIG_CPUMASK_OFFSTACK=y, otherwise is
+ * a nop returning a constant 1 (in <linux/cpumask.h>)
+ * Returns TRUE if memory allocation succeeded, FALSE otherwise.
+ *
+ * In addition, mask will be NULL if this fails. Note that gcc is
+ * usually smart enough to know that mask can never be NULL if
+ * CONFIG_CPUMASK_OFFSTACK=n, so does code elimination in that case
+ * too.
+ */
+bool alloc_cpumask_var_node(cpumask_var_t *mask, gfp_t flags, int node)
{
if (likely(slab_is_available()))
- *mask = kmalloc(cpumask_size(), flags);
+ *mask = kmalloc_node(cpumask_size(), flags, node);
else {
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
printk(KERN_ERR
"=> alloc_cpumask_var: kmalloc not available!\n");
- dump_stack();
#endif
*mask = NULL;
}
@@ -94,21 +107,64 @@ bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
dump_stack();
}
#endif
+ /* FIXME: Bandaid to save us from old primitives which go to NR_CPUS. */
+ if (*mask) {
+ unsigned int tail;
+ tail = BITS_TO_LONGS(NR_CPUS - nr_cpumask_bits) * sizeof(long);
+ memset(cpumask_bits(*mask) + cpumask_size() - tail,
+ 0, tail);
+ }
+
return *mask != NULL;
}
+EXPORT_SYMBOL(alloc_cpumask_var_node);
+
+/**
+ * alloc_cpumask_var - allocate a struct cpumask
+ * @mask: pointer to cpumask_var_t where the cpumask is returned
+ * @flags: GFP_ flags
+ *
+ * Only defined when CONFIG_CPUMASK_OFFSTACK=y, otherwise is
+ * a nop returning a constant 1 (in <linux/cpumask.h>).
+ *
+ * See alloc_cpumask_var_node.
+ */
+bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
+{
+ return alloc_cpumask_var_node(mask, flags, numa_node_id());
+}
EXPORT_SYMBOL(alloc_cpumask_var);
+/**
+ * alloc_bootmem_cpumask_var - allocate a struct cpumask from the bootmem arena.
+ * @mask: pointer to cpumask_var_t where the cpumask is returned
+ *
+ * Only defined when CONFIG_CPUMASK_OFFSTACK=y, otherwise is
+ * a nop (in <linux/cpumask.h>).
+ * Either returns an allocated (zero-filled) cpumask, or causes the
+ * system to panic.
+ */
void __init alloc_bootmem_cpumask_var(cpumask_var_t *mask)
{
*mask = alloc_bootmem(cpumask_size());
}
+/**
+ * free_cpumask_var - frees memory allocated for a struct cpumask.
+ * @mask: cpumask to free
+ *
+ * This is safe on a NULL mask.
+ */
void free_cpumask_var(cpumask_var_t mask)
{
kfree(mask);
}
EXPORT_SYMBOL(free_cpumask_var);
+/**
+ * free_bootmem_cpumask_var - frees result of alloc_bootmem_cpumask_var
+ * @mask: cpumask to free
+ */
void __init free_bootmem_cpumask_var(cpumask_var_t mask)
{
free_bootmem((unsigned long)mask, cpumask_size());
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index e3ab374e1334..5d99be1fd988 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -45,7 +45,9 @@ static struct kmem_cache *obj_cache;
static int debug_objects_maxchain __read_mostly;
static int debug_objects_fixups __read_mostly;
static int debug_objects_warnings __read_mostly;
-static int debug_objects_enabled __read_mostly;
+static int debug_objects_enabled __read_mostly
+ = CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
+
static struct debug_obj_descr *descr_test __read_mostly;
static int __init enable_object_debug(char *str)
diff --git a/lib/find_last_bit.c b/lib/find_last_bit.c
new file mode 100644
index 000000000000..5d202e36bdd8
--- /dev/null
+++ b/lib/find_last_bit.c
@@ -0,0 +1,45 @@
+/* find_last_bit.c: fallback find next bit implementation
+ *
+ * Copyright (C) 2008 IBM Corporation
+ * Written by Rusty Russell <rusty@rustcorp.com.au>
+ * (Inspired by David Howell's find_next_bit implementation)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <asm/types.h>
+#include <asm/byteorder.h>
+
+unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
+{
+ unsigned long words;
+ unsigned long tmp;
+
+ /* Start at final word. */
+ words = size / BITS_PER_LONG;
+
+ /* Partial final word? */
+ if (size & (BITS_PER_LONG-1)) {
+ tmp = (addr[words] & (~0UL >> (BITS_PER_LONG
+ - (size & (BITS_PER_LONG-1)))));
+ if (tmp)
+ goto found;
+ }
+
+ while (words) {
+ tmp = addr[--words];
+ if (tmp) {
+found:
+ return words * BITS_PER_LONG + __fls(tmp);
+ }
+ }
+
+ /* Not found */
+ return size;
+}
+EXPORT_SYMBOL(find_last_bit);
diff --git a/lib/is_single_threaded.c b/lib/is_single_threaded.c
new file mode 100644
index 000000000000..f1ed2fe76c65
--- /dev/null
+++ b/lib/is_single_threaded.c
@@ -0,0 +1,45 @@
+/* Function to determine if a thread group is single threaded or not
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from security/selinux/hooks.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+
+/**
+ * is_single_threaded - Determine if a thread group is single-threaded or not
+ * @p: A task in the thread group in question
+ *
+ * This returns true if the thread group to which a task belongs is single
+ * threaded, false if it is not.
+ */
+bool is_single_threaded(struct task_struct *p)
+{
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+
+ if (atomic_read(&p->signal->count) != 1)
+ goto no;
+
+ if (atomic_read(&p->mm->mm_users) != 1) {
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t) {
+ if (t->mm == mm && t != p)
+ goto no_unlock;
+ } while_each_thread(g, t);
+ read_unlock(&tasklist_lock);
+ }
+
+ return true;
+
+no_unlock:
+ read_unlock(&tasklist_lock);
+no:
+ return false;
+}
diff --git a/lib/libcrc32c.c b/lib/libcrc32c.c
index b5c3287d8ea4..244f5480c898 100644
--- a/lib/libcrc32c.c
+++ b/lib/libcrc32c.c
@@ -30,168 +30,52 @@
* any later version.
*
*/
-#include <linux/crc32c.h>
-#include <linux/compiler.h>
-#include <linux/module.h>
-
-MODULE_AUTHOR("Clay Haapala <chaapala@cisco.com>");
-MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations");
-MODULE_LICENSE("GPL");
-#define CRC32C_POLY_BE 0x1EDC6F41
-#define CRC32C_POLY_LE 0x82F63B78
+#include <crypto/hash.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
-#ifndef CRC_LE_BITS
-# define CRC_LE_BITS 8
-#endif
+static struct crypto_shash *tfm;
+u32 crc32c(u32 crc, const void *address, unsigned int length)
+{
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(tfm)];
+ } desc;
+ int err;
-/*
- * Haven't generated a big-endian table yet, but the bit-wise version
- * should at least work.
- */
-#if defined CRC_BE_BITS && CRC_BE_BITS != 1
-#undef CRC_BE_BITS
-#endif
-#ifndef CRC_BE_BITS
-# define CRC_BE_BITS 1
-#endif
+ desc.shash.tfm = tfm;
+ desc.shash.flags = 0;
+ *(u32 *)desc.ctx = crc;
-EXPORT_SYMBOL(crc32c_le);
+ err = crypto_shash_update(&desc.shash, address, length);
+ BUG_ON(err);
-#if CRC_LE_BITS == 1
-/*
- * Compute things bit-wise, as done in crc32.c. We could share the tight
- * loop below with crc32 and vary the POLY if we don't find value in terms
- * of space and maintainability in keeping the two modules separate.
- */
-u32 __pure
-crc32c_le(u32 crc, unsigned char const *p, size_t len)
-{
- int i;
- while (len--) {
- crc ^= *p++;
- for (i = 0; i < 8; i++)
- crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
- }
- return crc;
+ return *(u32 *)desc.ctx;
}
-#else
-
-/*
- * This is the CRC-32C table
- * Generated with:
- * width = 32 bits
- * poly = 0x1EDC6F41
- * reflect input bytes = true
- * reflect output bytes = true
- */
-
-static const u32 crc32c_table[256] = {
- 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
- 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
- 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
- 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
- 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
- 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
- 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
- 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
- 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
- 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
- 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
- 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
- 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
- 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
- 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
- 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
- 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
- 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
- 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
- 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
- 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
- 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
- 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
- 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
- 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
- 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
- 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
- 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
- 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
- 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
- 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
- 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
- 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
- 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
- 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
- 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
- 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
- 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
- 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
- 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
- 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
- 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
- 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
- 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
- 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
- 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
- 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
- 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
- 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
- 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
- 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
- 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
- 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
- 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
- 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
- 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
- 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
- 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
- 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
- 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
- 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
- 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
- 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
- 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
-};
-/*
- * Steps through buffer one byte at at time, calculates reflected
- * crc using table.
- */
+EXPORT_SYMBOL(crc32c);
-u32 __pure
-crc32c_le(u32 crc, unsigned char const *data, size_t length)
+static int __init libcrc32c_mod_init(void)
{
- while (length--)
- crc =
- crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8);
+ tfm = crypto_alloc_shash("crc32c", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
- return crc;
+ return 0;
}
-#endif /* CRC_LE_BITS == 8 */
-
-EXPORT_SYMBOL(crc32c_be);
-
-#if CRC_BE_BITS == 1
-u32 __pure
-crc32c_be(u32 crc, unsigned char const *p, size_t len)
+static void __exit libcrc32c_mod_fini(void)
{
- int i;
- while (len--) {
- crc ^= *p++ << 24;
- for (i = 0; i < 8; i++)
- crc =
- (crc << 1) ^ ((crc & 0x80000000) ? CRC32C_POLY_BE :
- 0);
- }
- return crc;
+ crypto_free_shash(tfm);
}
-#endif
-/*
- * Unit test
- *
- * A small unit test suite is implemented as part of the crypto suite.
- * Select CRYPTO_CRC32C and use the tcrypt module to run the tests.
- */
+module_init(libcrc32c_mod_init);
+module_exit(libcrc32c_mod_fini);
+
+MODULE_AUTHOR("Clay Haapala <chaapala@cisco.com>");
+MODULE_DESCRIPTION("CRC32c (Castagnoli) calculations");
+MODULE_LICENSE("GPL");
diff --git a/lib/swiotlb.c b/lib/swiotlb.c
index 5f6c629a924d..fa2dc4e5f9ba 100644
--- a/lib/swiotlb.c
+++ b/lib/swiotlb.c
@@ -21,9 +21,12 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/spinlock.h>
+#include <linux/swiotlb.h>
#include <linux/string.h>
+#include <linux/swiotlb.h>
#include <linux/types.h>
#include <linux/ctype.h>
+#include <linux/highmem.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -36,22 +39,6 @@
#define OFFSET(val,align) ((unsigned long) \
( (val) & ( (align) - 1)))
-#define SG_ENT_VIRT_ADDRESS(sg) (sg_virt((sg)))
-#define SG_ENT_PHYS_ADDRESS(sg) virt_to_bus(SG_ENT_VIRT_ADDRESS(sg))
-
-/*
- * Maximum allowable number of contiguous slabs to map,
- * must be a power of 2. What is the appropriate value ?
- * The complexity of {map,unmap}_single is linearly dependent on this value.
- */
-#define IO_TLB_SEGSIZE 128
-
-/*
- * log of the size of each IO TLB slab. The number of slabs is command line
- * controllable.
- */
-#define IO_TLB_SHIFT 11
-
#define SLABS_PER_PAGE (1 << (PAGE_SHIFT - IO_TLB_SHIFT))
/*
@@ -102,7 +89,10 @@ static unsigned int io_tlb_index;
* We need to save away the original address corresponding to a mapped entry
* for the sync operations.
*/
-static unsigned char **io_tlb_orig_addr;
+static struct swiotlb_phys_addr {
+ struct page *page;
+ unsigned int offset;
+} *io_tlb_orig_addr;
/*
* Protect the above data structures in the map and unmap calls
@@ -126,6 +116,72 @@ setup_io_tlb_npages(char *str)
__setup("swiotlb=", setup_io_tlb_npages);
/* make io_tlb_overflow tunable too? */
+void * __weak swiotlb_alloc_boot(size_t size, unsigned long nslabs)
+{
+ return alloc_bootmem_low_pages(size);
+}
+
+void * __weak swiotlb_alloc(unsigned order, unsigned long nslabs)
+{
+ return (void *)__get_free_pages(GFP_DMA | __GFP_NOWARN, order);
+}
+
+dma_addr_t __weak swiotlb_phys_to_bus(phys_addr_t paddr)
+{
+ return paddr;
+}
+
+phys_addr_t __weak swiotlb_bus_to_phys(dma_addr_t baddr)
+{
+ return baddr;
+}
+
+static dma_addr_t swiotlb_virt_to_bus(volatile void *address)
+{
+ return swiotlb_phys_to_bus(virt_to_phys(address));
+}
+
+static void *swiotlb_bus_to_virt(dma_addr_t address)
+{
+ return phys_to_virt(swiotlb_bus_to_phys(address));
+}
+
+int __weak swiotlb_arch_range_needs_mapping(void *ptr, size_t size)
+{
+ return 0;
+}
+
+static dma_addr_t swiotlb_sg_to_bus(struct scatterlist *sg)
+{
+ return swiotlb_phys_to_bus(page_to_phys(sg_page(sg)) + sg->offset);
+}
+
+static void swiotlb_print_info(unsigned long bytes)
+{
+ phys_addr_t pstart, pend;
+ dma_addr_t bstart, bend;
+
+ pstart = virt_to_phys(io_tlb_start);
+ pend = virt_to_phys(io_tlb_end);
+
+ bstart = swiotlb_phys_to_bus(pstart);
+ bend = swiotlb_phys_to_bus(pend);
+
+ printk(KERN_INFO "Placing %luMB software IO TLB between %p - %p\n",
+ bytes >> 20, io_tlb_start, io_tlb_end);
+ if (pstart != bstart || pend != bend)
+ printk(KERN_INFO "software IO TLB at phys %#llx - %#llx"
+ " bus %#llx - %#llx\n",
+ (unsigned long long)pstart,
+ (unsigned long long)pend,
+ (unsigned long long)bstart,
+ (unsigned long long)bend);
+ else
+ printk(KERN_INFO "software IO TLB at phys %#llx - %#llx\n",
+ (unsigned long long)pstart,
+ (unsigned long long)pend);
+}
+
/*
* Statically reserve bounce buffer space and initialize bounce buffer data
* structures for the software IO TLB used to implement the DMA API.
@@ -145,7 +201,7 @@ swiotlb_init_with_default_size(size_t default_size)
/*
* Get IO TLB memory from the low pages
*/
- io_tlb_start = alloc_bootmem_low_pages(bytes);
+ io_tlb_start = swiotlb_alloc_boot(bytes, io_tlb_nslabs);
if (!io_tlb_start)
panic("Cannot allocate SWIOTLB buffer");
io_tlb_end = io_tlb_start + bytes;
@@ -159,7 +215,7 @@ swiotlb_init_with_default_size(size_t default_size)
for (i = 0; i < io_tlb_nslabs; i++)
io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE);
io_tlb_index = 0;
- io_tlb_orig_addr = alloc_bootmem(io_tlb_nslabs * sizeof(char *));
+ io_tlb_orig_addr = alloc_bootmem(io_tlb_nslabs * sizeof(struct swiotlb_phys_addr));
/*
* Get the overflow emergency buffer
@@ -168,8 +224,7 @@ swiotlb_init_with_default_size(size_t default_size)
if (!io_tlb_overflow_buffer)
panic("Cannot allocate SWIOTLB overflow buffer!\n");
- printk(KERN_INFO "Placing software IO TLB between 0x%lx - 0x%lx\n",
- virt_to_bus(io_tlb_start), virt_to_bus(io_tlb_end));
+ swiotlb_print_info(bytes);
}
void __init
@@ -202,8 +257,7 @@ swiotlb_late_init_with_default_size(size_t default_size)
bytes = io_tlb_nslabs << IO_TLB_SHIFT;
while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
- io_tlb_start = (char *)__get_free_pages(GFP_DMA | __GFP_NOWARN,
- order);
+ io_tlb_start = swiotlb_alloc(order, io_tlb_nslabs);
if (io_tlb_start)
break;
order--;
@@ -235,12 +289,12 @@ swiotlb_late_init_with_default_size(size_t default_size)
io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE);
io_tlb_index = 0;
- io_tlb_orig_addr = (unsigned char **)__get_free_pages(GFP_KERNEL,
- get_order(io_tlb_nslabs * sizeof(char *)));
+ io_tlb_orig_addr = (struct swiotlb_phys_addr *)__get_free_pages(GFP_KERNEL,
+ get_order(io_tlb_nslabs * sizeof(struct swiotlb_phys_addr)));
if (!io_tlb_orig_addr)
goto cleanup3;
- memset(io_tlb_orig_addr, 0, io_tlb_nslabs * sizeof(char *));
+ memset(io_tlb_orig_addr, 0, io_tlb_nslabs * sizeof(struct swiotlb_phys_addr));
/*
* Get the overflow emergency buffer
@@ -250,9 +304,7 @@ swiotlb_late_init_with_default_size(size_t default_size)
if (!io_tlb_overflow_buffer)
goto cleanup4;
- printk(KERN_INFO "Placing %luMB software IO TLB between 0x%lx - "
- "0x%lx\n", bytes >> 20,
- virt_to_bus(io_tlb_start), virt_to_bus(io_tlb_end));
+ swiotlb_print_info(bytes);
return 0;
@@ -279,16 +331,69 @@ address_needs_mapping(struct device *hwdev, dma_addr_t addr, size_t size)
return !is_buffer_dma_capable(dma_get_mask(hwdev), addr, size);
}
+static inline int range_needs_mapping(void *ptr, size_t size)
+{
+ return swiotlb_force || swiotlb_arch_range_needs_mapping(ptr, size);
+}
+
static int is_swiotlb_buffer(char *addr)
{
return addr >= io_tlb_start && addr < io_tlb_end;
}
+static struct swiotlb_phys_addr swiotlb_bus_to_phys_addr(char *dma_addr)
+{
+ int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT;
+ struct swiotlb_phys_addr buffer = io_tlb_orig_addr[index];
+ buffer.offset += (long)dma_addr & ((1 << IO_TLB_SHIFT) - 1);
+ buffer.page += buffer.offset >> PAGE_SHIFT;
+ buffer.offset &= PAGE_SIZE - 1;
+ return buffer;
+}
+
+static void
+__sync_single(struct swiotlb_phys_addr buffer, char *dma_addr, size_t size, int dir)
+{
+ if (PageHighMem(buffer.page)) {
+ size_t len, bytes;
+ char *dev, *host, *kmp;
+
+ len = size;
+ while (len != 0) {
+ unsigned long flags;
+
+ bytes = len;
+ if ((bytes + buffer.offset) > PAGE_SIZE)
+ bytes = PAGE_SIZE - buffer.offset;
+ local_irq_save(flags); /* protects KM_BOUNCE_READ */
+ kmp = kmap_atomic(buffer.page, KM_BOUNCE_READ);
+ dev = dma_addr + size - len;
+ host = kmp + buffer.offset;
+ if (dir == DMA_FROM_DEVICE)
+ memcpy(host, dev, bytes);
+ else
+ memcpy(dev, host, bytes);
+ kunmap_atomic(kmp, KM_BOUNCE_READ);
+ local_irq_restore(flags);
+ len -= bytes;
+ buffer.page++;
+ buffer.offset = 0;
+ }
+ } else {
+ void *v = page_address(buffer.page) + buffer.offset;
+
+ if (dir == DMA_TO_DEVICE)
+ memcpy(dma_addr, v, size);
+ else
+ memcpy(v, dma_addr, size);
+ }
+}
+
/*
* Allocates bounce buffer and returns its kernel virtual address.
*/
static void *
-map_single(struct device *hwdev, char *buffer, size_t size, int dir)
+map_single(struct device *hwdev, struct swiotlb_phys_addr buffer, size_t size, int dir)
{
unsigned long flags;
char *dma_addr;
@@ -298,11 +403,16 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir)
unsigned long mask;
unsigned long offset_slots;
unsigned long max_slots;
+ struct swiotlb_phys_addr slot_buf;
mask = dma_get_seg_boundary(hwdev);
- start_dma_addr = virt_to_bus(io_tlb_start) & mask;
+ start_dma_addr = swiotlb_virt_to_bus(io_tlb_start) & mask;
offset_slots = ALIGN(start_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
+
+ /*
+ * Carefully handle integer overflow which can occur when mask == ~0UL.
+ */
max_slots = mask + 1
? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT
: 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
@@ -378,10 +488,15 @@ found:
* This is needed when we sync the memory. Then we sync the buffer if
* needed.
*/
- for (i = 0; i < nslots; i++)
- io_tlb_orig_addr[index+i] = buffer + (i << IO_TLB_SHIFT);
+ slot_buf = buffer;
+ for (i = 0; i < nslots; i++) {
+ slot_buf.page += slot_buf.offset >> PAGE_SHIFT;
+ slot_buf.offset &= PAGE_SIZE - 1;
+ io_tlb_orig_addr[index+i] = slot_buf;
+ slot_buf.offset += 1 << IO_TLB_SHIFT;
+ }
if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)
- memcpy(dma_addr, buffer, size);
+ __sync_single(buffer, dma_addr, size, DMA_TO_DEVICE);
return dma_addr;
}
@@ -395,17 +510,17 @@ unmap_single(struct device *hwdev, char *dma_addr, size_t size, int dir)
unsigned long flags;
int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT;
- char *buffer = io_tlb_orig_addr[index];
+ struct swiotlb_phys_addr buffer = swiotlb_bus_to_phys_addr(dma_addr);
/*
* First, sync the memory before unmapping the entry
*/
- if (buffer && ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
+ if ((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL))
/*
* bounce... copy the data back into the original buffer * and
* delete the bounce buffer.
*/
- memcpy(buffer, dma_addr, size);
+ __sync_single(buffer, dma_addr, size, DMA_FROM_DEVICE);
/*
* Return the buffer to the free list by setting the corresponding
@@ -437,21 +552,18 @@ static void
sync_single(struct device *hwdev, char *dma_addr, size_t size,
int dir, int target)
{
- int index = (dma_addr - io_tlb_start) >> IO_TLB_SHIFT;
- char *buffer = io_tlb_orig_addr[index];
-
- buffer += ((unsigned long)dma_addr & ((1 << IO_TLB_SHIFT) - 1));
+ struct swiotlb_phys_addr buffer = swiotlb_bus_to_phys_addr(dma_addr);
switch (target) {
case SYNC_FOR_CPU:
if (likely(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL))
- memcpy(buffer, dma_addr, size);
+ __sync_single(buffer, dma_addr, size, DMA_FROM_DEVICE);
else
BUG_ON(dir != DMA_TO_DEVICE);
break;
case SYNC_FOR_DEVICE:
if (likely(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
- memcpy(dma_addr, buffer, size);
+ __sync_single(buffer, dma_addr, size, DMA_TO_DEVICE);
else
BUG_ON(dir != DMA_FROM_DEVICE);
break;
@@ -473,7 +585,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size,
dma_mask = hwdev->coherent_dma_mask;
ret = (void *)__get_free_pages(flags, order);
- if (ret && !is_buffer_dma_capable(dma_mask, virt_to_bus(ret), size)) {
+ if (ret && !is_buffer_dma_capable(dma_mask, swiotlb_virt_to_bus(ret), size)) {
/*
* The allocated memory isn't reachable by the device.
* Fall back on swiotlb_map_single().
@@ -488,13 +600,16 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size,
* swiotlb_map_single(), which will grab memory from
* the lowest available address range.
*/
- ret = map_single(hwdev, NULL, size, DMA_FROM_DEVICE);
+ struct swiotlb_phys_addr buffer;
+ buffer.page = virt_to_page(NULL);
+ buffer.offset = 0;
+ ret = map_single(hwdev, buffer, size, DMA_FROM_DEVICE);
if (!ret)
return NULL;
}
memset(ret, 0, size);
- dev_addr = virt_to_bus(ret);
+ dev_addr = swiotlb_virt_to_bus(ret);
/* Confirm address can be DMA'd by device */
if (!is_buffer_dma_capable(dma_mask, dev_addr, size)) {
@@ -554,8 +669,9 @@ dma_addr_t
swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size,
int dir, struct dma_attrs *attrs)
{
- dma_addr_t dev_addr = virt_to_bus(ptr);
+ dma_addr_t dev_addr = swiotlb_virt_to_bus(ptr);
void *map;
+ struct swiotlb_phys_addr buffer;
BUG_ON(dir == DMA_NONE);
/*
@@ -563,19 +679,22 @@ swiotlb_map_single_attrs(struct device *hwdev, void *ptr, size_t size,
* we can safely return the device addr and not worry about bounce
* buffering it.
*/
- if (!address_needs_mapping(hwdev, dev_addr, size) && !swiotlb_force)
+ if (!address_needs_mapping(hwdev, dev_addr, size) &&
+ !range_needs_mapping(ptr, size))
return dev_addr;
/*
* Oh well, have to allocate and map a bounce buffer.
*/
- map = map_single(hwdev, ptr, size, dir);
+ buffer.page = virt_to_page(ptr);
+ buffer.offset = (unsigned long)ptr & ~PAGE_MASK;
+ map = map_single(hwdev, buffer, size, dir);
if (!map) {
swiotlb_full(hwdev, size, dir, 1);
map = io_tlb_overflow_buffer;
}
- dev_addr = virt_to_bus(map);
+ dev_addr = swiotlb_virt_to_bus(map);
/*
* Ensure that the address returned is DMA'ble
@@ -605,7 +724,7 @@ void
swiotlb_unmap_single_attrs(struct device *hwdev, dma_addr_t dev_addr,
size_t size, int dir, struct dma_attrs *attrs)
{
- char *dma_addr = bus_to_virt(dev_addr);
+ char *dma_addr = swiotlb_bus_to_virt(dev_addr);
BUG_ON(dir == DMA_NONE);
if (is_swiotlb_buffer(dma_addr))
@@ -635,7 +754,7 @@ static void
swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr,
size_t size, int dir, int target)
{
- char *dma_addr = bus_to_virt(dev_addr);
+ char *dma_addr = swiotlb_bus_to_virt(dev_addr);
BUG_ON(dir == DMA_NONE);
if (is_swiotlb_buffer(dma_addr))
@@ -666,7 +785,7 @@ swiotlb_sync_single_range(struct device *hwdev, dma_addr_t dev_addr,
unsigned long offset, size_t size,
int dir, int target)
{
- char *dma_addr = bus_to_virt(dev_addr) + offset;
+ char *dma_addr = swiotlb_bus_to_virt(dev_addr) + offset;
BUG_ON(dir == DMA_NONE);
if (is_swiotlb_buffer(dma_addr))
@@ -714,18 +833,20 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
int dir, struct dma_attrs *attrs)
{
struct scatterlist *sg;
- void *addr;
+ struct swiotlb_phys_addr buffer;
dma_addr_t dev_addr;
int i;
BUG_ON(dir == DMA_NONE);
for_each_sg(sgl, sg, nelems, i) {
- addr = SG_ENT_VIRT_ADDRESS(sg);
- dev_addr = virt_to_bus(addr);
- if (swiotlb_force ||
+ dev_addr = swiotlb_sg_to_bus(sg);
+ if (range_needs_mapping(sg_virt(sg), sg->length) ||
address_needs_mapping(hwdev, dev_addr, sg->length)) {
- void *map = map_single(hwdev, addr, sg->length, dir);
+ void *map;
+ buffer.page = sg_page(sg);
+ buffer.offset = sg->offset;
+ map = map_single(hwdev, buffer, sg->length, dir);
if (!map) {
/* Don't panic here, we expect map_sg users
to do proper error handling. */
@@ -735,7 +856,7 @@ swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
sgl[0].dma_length = 0;
return 0;
}
- sg->dma_address = virt_to_bus(map);
+ sg->dma_address = swiotlb_virt_to_bus(map);
} else
sg->dma_address = dev_addr;
sg->dma_length = sg->length;
@@ -765,11 +886,11 @@ swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
BUG_ON(dir == DMA_NONE);
for_each_sg(sgl, sg, nelems, i) {
- if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg))
- unmap_single(hwdev, bus_to_virt(sg->dma_address),
+ if (sg->dma_address != swiotlb_sg_to_bus(sg))
+ unmap_single(hwdev, swiotlb_bus_to_virt(sg->dma_address),
sg->dma_length, dir);
else if (dir == DMA_FROM_DEVICE)
- dma_mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length);
+ dma_mark_clean(swiotlb_bus_to_virt(sg->dma_address), sg->dma_length);
}
}
EXPORT_SYMBOL(swiotlb_unmap_sg_attrs);
@@ -798,11 +919,11 @@ swiotlb_sync_sg(struct device *hwdev, struct scatterlist *sgl,
BUG_ON(dir == DMA_NONE);
for_each_sg(sgl, sg, nelems, i) {
- if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg))
- sync_single(hwdev, bus_to_virt(sg->dma_address),
+ if (sg->dma_address != swiotlb_sg_to_bus(sg))
+ sync_single(hwdev, swiotlb_bus_to_virt(sg->dma_address),
sg->dma_length, dir, target);
else if (dir == DMA_FROM_DEVICE)
- dma_mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length);
+ dma_mark_clean(swiotlb_bus_to_virt(sg->dma_address), sg->dma_length);
}
}
@@ -823,7 +944,7 @@ swiotlb_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg,
int
swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
{
- return (dma_addr == virt_to_bus(io_tlb_overflow_buffer));
+ return (dma_addr == swiotlb_virt_to_bus(io_tlb_overflow_buffer));
}
/*
@@ -835,7 +956,7 @@ swiotlb_dma_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
int
swiotlb_dma_supported(struct device *hwdev, u64 mask)
{
- return virt_to_bus(io_tlb_end - 1) <= mask;
+ return swiotlb_virt_to_bus(io_tlb_end - 1) <= mask;
}
EXPORT_SYMBOL(swiotlb_map_single);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index a013bbc23717..3b777025d876 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -581,6 +581,62 @@ static char *resource_string(char *buf, char *end, struct resource *res, int fie
return string(buf, end, sym, field_width, precision, flags);
}
+static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width,
+ int precision, int flags)
+{
+ char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */
+ char *p = mac_addr;
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ p = pack_hex_byte(p, addr[i]);
+ if (!(flags & SPECIAL) && i != 5)
+ *p++ = ':';
+ }
+ *p = '\0';
+
+ return string(buf, end, mac_addr, field_width, precision, flags & ~SPECIAL);
+}
+
+static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,
+ int precision, int flags)
+{
+ char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */
+ char *p = ip6_addr;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ p = pack_hex_byte(p, addr[2 * i]);
+ p = pack_hex_byte(p, addr[2 * i + 1]);
+ if (!(flags & SPECIAL) && i != 7)
+ *p++ = ':';
+ }
+ *p = '\0';
+
+ return string(buf, end, ip6_addr, field_width, precision, flags & ~SPECIAL);
+}
+
+static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,
+ int precision, int flags)
+{
+ char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */
+ char temp[3]; /* hold each IP quad in reverse order */
+ char *p = ip4_addr;
+ int i, digits;
+
+ for (i = 0; i < 4; i++) {
+ digits = put_dec_trunc(temp, addr[i]) - temp;
+ /* reverse the digits in the quad */
+ while (digits--)
+ *p++ = temp[digits];
+ if (i != 3)
+ *p++ = '.';
+ }
+ *p = '\0';
+
+ return string(buf, end, ip4_addr, field_width, precision, flags & ~SPECIAL);
+}
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -592,6 +648,12 @@ static char *resource_string(char *buf, char *end, struct resource *res, int fie
* - 'S' For symbolic direct pointers
* - 'R' For a struct resource pointer, it prints the range of
* addresses (not the name nor the flags)
+ * - 'M' For a 6-byte MAC address, it prints the address in the
+ * usual colon-separated hex notation
+ * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated
+ * decimal for v4 and colon separated network-order 16 bit hex for v6)
+ * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
+ * currently the same
*
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
@@ -607,6 +669,21 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field
return symbol_string(buf, end, ptr, field_width, precision, flags);
case 'R':
return resource_string(buf, end, ptr, field_width, precision, flags);
+ case 'm':
+ flags |= SPECIAL;
+ /* Fallthrough */
+ case 'M':
+ return mac_address_string(buf, end, ptr, field_width, precision, flags);
+ case 'i':
+ flags |= SPECIAL;
+ /* Fallthrough */
+ case 'I':
+ if (fmt[1] == '6')
+ return ip6_addr_string(buf, end, ptr, field_width, precision, flags);
+ if (fmt[1] == '4')
+ return ip4_addr_string(buf, end, ptr, field_width, precision, flags);
+ flags &= ~SPECIAL;
+ break;
}
flags |= SMALL;
if (field_width == -1) {
diff --git a/mm/Makefile b/mm/Makefile
index c06b45a1ff5f..51c27709cc7c 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SLOB) += slob.o
obj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o
obj-$(CONFIG_SLAB) += slab.o
obj-$(CONFIG_SLUB) += slub.o
+obj-$(CONFIG_FAILSLAB) += failslab.o
obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o
obj-$(CONFIG_FS_XIP) += filemap_xip.o
obj-$(CONFIG_MIGRATION) += migrate.o
diff --git a/mm/bounce.c b/mm/bounce.c
index bf0cf7c8387b..e590272fe7a8 100644
--- a/mm/bounce.c
+++ b/mm/bounce.c
@@ -198,8 +198,13 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
/*
* irk, bounce it
*/
- if (!bio)
- bio = bio_alloc(GFP_NOIO, (*bio_orig)->bi_vcnt);
+ if (!bio) {
+ unsigned int cnt = (*bio_orig)->bi_vcnt;
+
+ bio = bio_alloc(GFP_NOIO, cnt);
+ memset(bio->bi_io_vec, 0, cnt * sizeof(struct bio_vec));
+ }
+
to = bio->bi_io_vec + i;
diff --git a/mm/failslab.c b/mm/failslab.c
new file mode 100644
index 000000000000..7c6ea6493f80
--- /dev/null
+++ b/mm/failslab.c
@@ -0,0 +1,59 @@
+#include <linux/fault-inject.h>
+
+static struct {
+ struct fault_attr attr;
+ u32 ignore_gfp_wait;
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+ struct dentry *ignore_gfp_wait_file;
+#endif
+} failslab = {
+ .attr = FAULT_ATTR_INITIALIZER,
+ .ignore_gfp_wait = 1,
+};
+
+bool should_failslab(size_t size, gfp_t gfpflags)
+{
+ if (gfpflags & __GFP_NOFAIL)
+ return false;
+
+ if (failslab.ignore_gfp_wait && (gfpflags & __GFP_WAIT))
+ return false;
+
+ return should_fail(&failslab.attr, size);
+}
+
+static int __init setup_failslab(char *str)
+{
+ return setup_fault_attr(&failslab.attr, str);
+}
+__setup("failslab=", setup_failslab);
+
+#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
+
+static int __init failslab_debugfs_init(void)
+{
+ mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
+ struct dentry *dir;
+ int err;
+
+ err = init_fault_attr_dentries(&failslab.attr, "failslab");
+ if (err)
+ return err;
+ dir = failslab.attr.dentries.dir;
+
+ failslab.ignore_gfp_wait_file =
+ debugfs_create_bool("ignore-gfp-wait", mode, dir,
+ &failslab.ignore_gfp_wait);
+
+ if (!failslab.ignore_gfp_wait_file) {
+ err = -ENOMEM;
+ debugfs_remove(failslab.ignore_gfp_wait_file);
+ cleanup_fault_attr_dentries(&failslab.attr);
+ }
+
+ return err;
+}
+
+late_initcall(failslab_debugfs_init);
+
+#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
diff --git a/mm/memory.c b/mm/memory.c
index fc031d68327e..0a2010a9518c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -669,6 +669,16 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
if (is_vm_hugetlb_page(vma))
return copy_hugetlb_page_range(dst_mm, src_mm, vma);
+ if (unlikely(is_pfn_mapping(vma))) {
+ /*
+ * We do not free on error cases below as remove_vma
+ * gets called on error from higher level routine
+ */
+ ret = track_pfn_vma_copy(vma);
+ if (ret)
+ return ret;
+ }
+
/*
* We need to invalidate the secondary MMU mappings only when
* there could be a permission downgrade on the ptes of the
@@ -915,6 +925,9 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp,
if (vma->vm_flags & VM_ACCOUNT)
*nr_accounted += (end - start) >> PAGE_SHIFT;
+ if (unlikely(is_pfn_mapping(vma)))
+ untrack_pfn_vma(vma, 0, 0);
+
while (start != end) {
if (!tlb_start_valid) {
tlb_start = start;
@@ -1430,6 +1443,7 @@ out:
int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn)
{
+ int ret;
/*
* Technically, architectures with pte_special can avoid all these
* restrictions (same for remap_pfn_range). However we would like
@@ -1444,7 +1458,15 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
if (addr < vma->vm_start || addr >= vma->vm_end)
return -EFAULT;
- return insert_pfn(vma, addr, pfn, vma->vm_page_prot);
+ if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE))
+ return -EINVAL;
+
+ ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot);
+
+ if (ret)
+ untrack_pfn_vma(vma, pfn, PAGE_SIZE);
+
+ return ret;
}
EXPORT_SYMBOL(vm_insert_pfn);
@@ -1575,14 +1597,17 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
* behaviour that some programs depend on. We mark the "original"
* un-COW'ed pages by matching them up with "vma->vm_pgoff".
*/
- if (is_cow_mapping(vma->vm_flags)) {
- if (addr != vma->vm_start || end != vma->vm_end)
- return -EINVAL;
+ if (addr == vma->vm_start && end == vma->vm_end)
vma->vm_pgoff = pfn;
- }
+ else if (is_cow_mapping(vma->vm_flags))
+ return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
+ err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size));
+ if (err)
+ return -EINVAL;
+
BUG_ON(addr >= end);
pfn -= addr >> PAGE_SHIFT;
pgd = pgd_offset(mm, addr);
@@ -1594,6 +1619,10 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
if (err)
break;
} while (pgd++, addr = next, addr != end);
+
+ if (err)
+ untrack_pfn_vma(vma, pfn, PAGE_ALIGN(size));
+
return err;
}
EXPORT_SYMBOL(remap_pfn_range);
@@ -2865,9 +2894,9 @@ int in_gate_area_no_task(unsigned long addr)
#endif /* __HAVE_ARCH_GATE_AREA */
#ifdef CONFIG_HAVE_IOREMAP_PROT
-static resource_size_t follow_phys(struct vm_area_struct *vma,
- unsigned long address, unsigned int flags,
- unsigned long *prot)
+int follow_phys(struct vm_area_struct *vma,
+ unsigned long address, unsigned int flags,
+ unsigned long *prot, resource_size_t *phys)
{
pgd_t *pgd;
pud_t *pud;
@@ -2876,24 +2905,26 @@ static resource_size_t follow_phys(struct vm_area_struct *vma,
spinlock_t *ptl;
resource_size_t phys_addr = 0;
struct mm_struct *mm = vma->vm_mm;
+ int ret = -EINVAL;
- VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP)));
+ if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
+ goto out;
pgd = pgd_offset(mm, address);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- goto no_page_table;
+ goto out;
pud = pud_offset(pgd, address);
if (pud_none(*pud) || unlikely(pud_bad(*pud)))
- goto no_page_table;
+ goto out;
pmd = pmd_offset(pud, address);
if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- goto no_page_table;
+ goto out;
/* We cannot handle huge page PFN maps. Luckily they don't exist. */
if (pmd_huge(*pmd))
- goto no_page_table;
+ goto out;
ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
if (!ptep)
@@ -2908,13 +2939,13 @@ static resource_size_t follow_phys(struct vm_area_struct *vma,
phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */
*prot = pgprot_val(pte_pgprot(pte));
+ *phys = phys_addr;
+ ret = 0;
unlock:
pte_unmap_unlock(ptep, ptl);
out:
- return phys_addr;
-no_page_table:
- return 0;
+ return ret;
}
int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
@@ -2925,12 +2956,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
void *maddr;
int offset = addr & (PAGE_SIZE-1);
- if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
- return -EINVAL;
-
- phys_addr = follow_phys(vma, addr, write, &prot);
-
- if (!phys_addr)
+ if (follow_phys(vma, addr, write, &prot, &phys_addr))
return -EINVAL;
maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index e9493b1c1117..e412ffa8e52e 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1114,6 +1114,7 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
const unsigned long __user *old_nodes,
const unsigned long __user *new_nodes)
{
+ const struct cred *cred = current_cred(), *tcred;
struct mm_struct *mm;
struct task_struct *task;
nodemask_t old;
@@ -1148,12 +1149,16 @@ asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
* capabilities, superuser privileges or the same
* userid as the target process.
*/
- if ((current->euid != task->suid) && (current->euid != task->uid) &&
- (current->uid != task->suid) && (current->uid != task->uid) &&
+ rcu_read_lock();
+ tcred = __task_cred(task);
+ if (cred->euid != tcred->suid && cred->euid != tcred->uid &&
+ cred->uid != tcred->suid && cred->uid != tcred->uid &&
!capable(CAP_SYS_NICE)) {
+ rcu_read_unlock();
err = -EPERM;
goto out;
}
+ rcu_read_unlock();
task_nodes = cpuset_mems_allowed(task);
/* Is the user allowed to access the target nodes? */
diff --git a/mm/migrate.c b/mm/migrate.c
index 037b0967c1e3..21631ab8c08b 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1075,6 +1075,7 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
const int __user *nodes,
int __user *status, int flags)
{
+ const struct cred *cred = current_cred(), *tcred;
struct task_struct *task;
struct mm_struct *mm;
int err;
@@ -1105,12 +1106,16 @@ asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages,
* capabilities, superuser privileges or the same
* userid as the target process.
*/
- if ((current->euid != task->suid) && (current->euid != task->uid) &&
- (current->uid != task->suid) && (current->uid != task->uid) &&
+ rcu_read_lock();
+ tcred = __task_cred(task);
+ if (cred->euid != tcred->suid && cred->euid != tcred->uid &&
+ cred->uid != tcred->suid && cred->uid != tcred->uid &&
!capable(CAP_SYS_NICE)) {
+ rcu_read_unlock();
err = -EPERM;
goto out;
}
+ rcu_read_unlock();
err = security_task_movememory(task);
if (err)
diff --git a/mm/mlock.c b/mm/mlock.c
index 1ada366570cb..3035a56e7616 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -667,3 +667,48 @@ void user_shm_unlock(size_t size, struct user_struct *user)
spin_unlock(&shmlock_user_lock);
free_uid(user);
}
+
+void *alloc_locked_buffer(size_t size)
+{
+ unsigned long rlim, vm, pgsz;
+ void *buffer = NULL;
+
+ pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+ down_write(&current->mm->mmap_sem);
+
+ rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT;
+ vm = current->mm->total_vm + pgsz;
+ if (rlim < vm)
+ goto out;
+
+ rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT;
+ vm = current->mm->locked_vm + pgsz;
+ if (rlim < vm)
+ goto out;
+
+ buffer = kzalloc(size, GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ current->mm->total_vm += pgsz;
+ current->mm->locked_vm += pgsz;
+
+ out:
+ up_write(&current->mm->mmap_sem);
+ return buffer;
+}
+
+void free_locked_buffer(void *buffer, size_t size)
+{
+ unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT;
+
+ down_write(&current->mm->mmap_sem);
+
+ current->mm->total_vm -= pgsz;
+ current->mm->locked_vm -= pgsz;
+
+ up_write(&current->mm->mmap_sem);
+
+ kfree(buffer);
+}
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index a0a01902f551..558f9afe6e4e 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -128,8 +128,8 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
* Superuser processes are usually more important, so we make it
* less likely that we kill those.
*/
- if (has_capability(p, CAP_SYS_ADMIN) ||
- has_capability(p, CAP_SYS_RESOURCE))
+ if (has_capability_noaudit(p, CAP_SYS_ADMIN) ||
+ has_capability_noaudit(p, CAP_SYS_RESOURCE))
points /= 4;
/*
@@ -138,7 +138,7 @@ unsigned long badness(struct task_struct *p, unsigned long uptime)
* tend to only have this flag set on applications they think
* of as important.
*/
- if (has_capability(p, CAP_SYS_RAWIO))
+ if (has_capability_noaudit(p, CAP_SYS_RAWIO))
points /= 4;
/*
@@ -299,9 +299,9 @@ static void dump_tasks(const struct mem_cgroup *mem)
task_lock(p);
printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n",
- p->pid, p->uid, p->tgid, p->mm->total_vm,
- get_mm_rss(p->mm), (int)task_cpu(p), p->oomkilladj,
- p->comm);
+ p->pid, __task_cred(p)->uid, p->tgid,
+ p->mm->total_vm, get_mm_rss(p->mm), (int)task_cpu(p),
+ p->oomkilladj, p->comm);
task_unlock(p);
} while_each_thread(g, p);
}
diff --git a/mm/pdflush.c b/mm/pdflush.c
index a0a14c4d5072..15de509b68fd 100644
--- a/mm/pdflush.c
+++ b/mm/pdflush.c
@@ -172,7 +172,16 @@ static int __pdflush(struct pdflush_work *my_work)
static int pdflush(void *dummy)
{
struct pdflush_work my_work;
- cpumask_t cpus_allowed;
+ cpumask_var_t cpus_allowed;
+
+ /*
+ * Since the caller doesn't even check kthread_run() worked, let's not
+ * freak out too much if this fails.
+ */
+ if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) {
+ printk(KERN_WARNING "pdflush failed to allocate cpumask\n");
+ return 0;
+ }
/*
* pdflush can spend a lot of time doing encryption via dm-crypt. We
@@ -187,8 +196,9 @@ static int pdflush(void *dummy)
* This is needed as pdflush's are dynamically created and destroyed.
* The boottime pdflush's are easily placed w/o these 2 lines.
*/
- cpuset_cpus_allowed(current, &cpus_allowed);
- set_cpus_allowed_ptr(current, &cpus_allowed);
+ cpuset_cpus_allowed(current, cpus_allowed);
+ set_cpus_allowed_ptr(current, cpus_allowed);
+ free_cpumask_var(cpus_allowed);
return __pdflush(&my_work);
}
diff --git a/mm/shmem.c b/mm/shmem.c
index 0ed075215e5f..f1b0d4871f3a 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1513,8 +1513,8 @@ shmem_get_inode(struct super_block *sb, int mode, dev_t dev)
inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
inode->i_blocks = 0;
inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -2278,8 +2278,8 @@ static int shmem_fill_super(struct super_block *sb,
sbinfo->max_blocks = 0;
sbinfo->max_inodes = 0;
sbinfo->mode = S_IRWXUGO | S_ISVTX;
- sbinfo->uid = current->fsuid;
- sbinfo->gid = current->fsgid;
+ sbinfo->uid = current_fsuid();
+ sbinfo->gid = current_fsgid();
sbinfo->mpol = NULL;
sb->s_fs_info = sbinfo;
diff --git a/mm/slab.c b/mm/slab.c
index 09187517f9dc..ddc41f337d58 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2123,6 +2123,8 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep)
*
* @name must be valid until the cache is destroyed. This implies that
* the module calling this has to destroy the cache before getting unloaded.
+ * Note that kmem_cache_name() is not guaranteed to return the same pointer,
+ * therefore applications must manage it themselves.
*
* The flags are
*
@@ -2155,7 +2157,7 @@ kmem_cache_create (const char *name, size_t size, size_t align,
/*
* We use cache_chain_mutex to ensure a consistent view of
- * cpu_online_map as well. Please see cpuup_callback
+ * cpu_online_mask as well. Please see cpuup_callback
*/
get_online_cpus();
mutex_lock(&cache_chain_mutex);
@@ -2609,7 +2611,7 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
if (OFF_SLAB(cachep)) {
/* Slab management obj is off-slab. */
slabp = kmem_cache_alloc_node(cachep->slabp_cache,
- local_flags & ~GFP_THISNODE, nodeid);
+ local_flags, nodeid);
if (!slabp)
return NULL;
} else {
@@ -2997,7 +2999,7 @@ retry:
* there must be at least one object available for
* allocation.
*/
- BUG_ON(slabp->inuse < 0 || slabp->inuse >= cachep->num);
+ BUG_ON(slabp->inuse >= cachep->num);
while (slabp->inuse < cachep->num && batchcount--) {
STATS_INC_ALLOCED(cachep);
@@ -3106,79 +3108,14 @@ static void *cache_alloc_debugcheck_after(struct kmem_cache *cachep,
#define cache_alloc_debugcheck_after(a,b,objp,d) (objp)
#endif
-#ifdef CONFIG_FAILSLAB
-
-static struct failslab_attr {
-
- struct fault_attr attr;
-
- u32 ignore_gfp_wait;
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
- struct dentry *ignore_gfp_wait_file;
-#endif
-
-} failslab = {
- .attr = FAULT_ATTR_INITIALIZER,
- .ignore_gfp_wait = 1,
-};
-
-static int __init setup_failslab(char *str)
-{
- return setup_fault_attr(&failslab.attr, str);
-}
-__setup("failslab=", setup_failslab);
-
-static int should_failslab(struct kmem_cache *cachep, gfp_t flags)
+static bool slab_should_failslab(struct kmem_cache *cachep, gfp_t flags)
{
if (cachep == &cache_cache)
- return 0;
- if (flags & __GFP_NOFAIL)
- return 0;
- if (failslab.ignore_gfp_wait && (flags & __GFP_WAIT))
- return 0;
+ return false;
- return should_fail(&failslab.attr, obj_size(cachep));
+ return should_failslab(obj_size(cachep), flags);
}
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-
-static int __init failslab_debugfs(void)
-{
- mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
- struct dentry *dir;
- int err;
-
- err = init_fault_attr_dentries(&failslab.attr, "failslab");
- if (err)
- return err;
- dir = failslab.attr.dentries.dir;
-
- failslab.ignore_gfp_wait_file =
- debugfs_create_bool("ignore-gfp-wait", mode, dir,
- &failslab.ignore_gfp_wait);
-
- if (!failslab.ignore_gfp_wait_file) {
- err = -ENOMEM;
- debugfs_remove(failslab.ignore_gfp_wait_file);
- cleanup_fault_attr_dentries(&failslab.attr);
- }
-
- return err;
-}
-
-late_initcall(failslab_debugfs);
-
-#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
-
-#else /* CONFIG_FAILSLAB */
-
-static inline int should_failslab(struct kmem_cache *cachep, gfp_t flags)
-{
- return 0;
-}
-
-#endif /* CONFIG_FAILSLAB */
-
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
void *objp;
@@ -3381,7 +3318,7 @@ __cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, int nodeid,
unsigned long save_flags;
void *ptr;
- if (should_failslab(cachep, flags))
+ if (slab_should_failslab(cachep, flags))
return NULL;
cache_alloc_debugcheck_before(cachep, flags);
@@ -3457,7 +3394,7 @@ __cache_alloc(struct kmem_cache *cachep, gfp_t flags, void *caller)
unsigned long save_flags;
void *objp;
- if (should_failslab(cachep, flags))
+ if (slab_should_failslab(cachep, flags))
return NULL;
cache_alloc_debugcheck_before(cachep, flags);
@@ -3686,9 +3623,9 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
EXPORT_SYMBOL(__kmalloc_node);
void *__kmalloc_node_track_caller(size_t size, gfp_t flags,
- int node, void *caller)
+ int node, unsigned long caller)
{
- return __do_kmalloc_node(size, flags, node, caller);
+ return __do_kmalloc_node(size, flags, node, (void *)caller);
}
EXPORT_SYMBOL(__kmalloc_node_track_caller);
#else
@@ -3730,9 +3667,9 @@ void *__kmalloc(size_t size, gfp_t flags)
}
EXPORT_SYMBOL(__kmalloc);
-void *__kmalloc_track_caller(size_t size, gfp_t flags, void *caller)
+void *__kmalloc_track_caller(size_t size, gfp_t flags, unsigned long caller)
{
- return __do_kmalloc(size, flags, caller);
+ return __do_kmalloc(size, flags, (void *)caller);
}
EXPORT_SYMBOL(__kmalloc_track_caller);
diff --git a/mm/slub.c b/mm/slub.c
index 8e516e29f989..f0e2892fe403 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -24,6 +24,7 @@
#include <linux/kallsyms.h>
#include <linux/memory.h>
#include <linux/math64.h>
+#include <linux/fault-inject.h>
/*
* Lock order:
@@ -153,6 +154,10 @@
#define ARCH_SLAB_MINALIGN __alignof__(unsigned long long)
#endif
+#define OO_SHIFT 16
+#define OO_MASK ((1 << OO_SHIFT) - 1)
+#define MAX_OBJS_PER_PAGE 65535 /* since page.objects is u16 */
+
/* Internal SLUB flags */
#define __OBJECT_POISON 0x80000000 /* Poison object */
#define __SYSFS_ADD_DEFERRED 0x40000000 /* Not yet visible via sysfs */
@@ -178,7 +183,7 @@ static LIST_HEAD(slab_caches);
* Tracking user of a slab.
*/
struct track {
- void *addr; /* Called from address */
+ unsigned long addr; /* Called from address */
int cpu; /* Was running on cpu */
int pid; /* Pid context */
unsigned long when; /* When did the operation occur */
@@ -290,7 +295,7 @@ static inline struct kmem_cache_order_objects oo_make(int order,
unsigned long size)
{
struct kmem_cache_order_objects x = {
- (order << 16) + (PAGE_SIZE << order) / size
+ (order << OO_SHIFT) + (PAGE_SIZE << order) / size
};
return x;
@@ -298,12 +303,12 @@ static inline struct kmem_cache_order_objects oo_make(int order,
static inline int oo_order(struct kmem_cache_order_objects x)
{
- return x.x >> 16;
+ return x.x >> OO_SHIFT;
}
static inline int oo_objects(struct kmem_cache_order_objects x)
{
- return x.x & ((1 << 16) - 1);
+ return x.x & OO_MASK;
}
#ifdef CONFIG_SLUB_DEBUG
@@ -367,7 +372,7 @@ static struct track *get_track(struct kmem_cache *s, void *object,
}
static void set_track(struct kmem_cache *s, void *object,
- enum track_item alloc, void *addr)
+ enum track_item alloc, unsigned long addr)
{
struct track *p;
@@ -391,8 +396,8 @@ static void init_tracking(struct kmem_cache *s, void *object)
if (!(s->flags & SLAB_STORE_USER))
return;
- set_track(s, object, TRACK_FREE, NULL);
- set_track(s, object, TRACK_ALLOC, NULL);
+ set_track(s, object, TRACK_FREE, 0UL);
+ set_track(s, object, TRACK_ALLOC, 0UL);
}
static void print_track(const char *s, struct track *t)
@@ -401,7 +406,7 @@ static void print_track(const char *s, struct track *t)
return;
printk(KERN_ERR "INFO: %s in %pS age=%lu cpu=%u pid=%d\n",
- s, t->addr, jiffies - t->when, t->cpu, t->pid);
+ s, (void *)t->addr, jiffies - t->when, t->cpu, t->pid);
}
static void print_tracking(struct kmem_cache *s, void *object)
@@ -692,7 +697,7 @@ static int check_object(struct kmem_cache *s, struct page *page,
if (!check_valid_pointer(s, page, get_freepointer(s, p))) {
object_err(s, page, p, "Freepointer corrupt");
/*
- * No choice but to zap it and thus loose the remainder
+ * No choice but to zap it and thus lose the remainder
* of the free objects in this slab. May cause
* another error because the object count is now wrong.
*/
@@ -764,8 +769,8 @@ static int on_freelist(struct kmem_cache *s, struct page *page, void *search)
}
max_objects = (PAGE_SIZE << compound_order(page)) / s->size;
- if (max_objects > 65535)
- max_objects = 65535;
+ if (max_objects > MAX_OBJS_PER_PAGE)
+ max_objects = MAX_OBJS_PER_PAGE;
if (page->objects != max_objects) {
slab_err(s, page, "Wrong number of objects. Found %d but "
@@ -866,7 +871,7 @@ static void setup_object_debug(struct kmem_cache *s, struct page *page,
}
static int alloc_debug_processing(struct kmem_cache *s, struct page *page,
- void *object, void *addr)
+ void *object, unsigned long addr)
{
if (!check_slab(s, page))
goto bad;
@@ -906,7 +911,7 @@ bad:
}
static int free_debug_processing(struct kmem_cache *s, struct page *page,
- void *object, void *addr)
+ void *object, unsigned long addr)
{
if (!check_slab(s, page))
goto fail;
@@ -1029,10 +1034,10 @@ static inline void setup_object_debug(struct kmem_cache *s,
struct page *page, void *object) {}
static inline int alloc_debug_processing(struct kmem_cache *s,
- struct page *page, void *object, void *addr) { return 0; }
+ struct page *page, void *object, unsigned long addr) { return 0; }
static inline int free_debug_processing(struct kmem_cache *s,
- struct page *page, void *object, void *addr) { return 0; }
+ struct page *page, void *object, unsigned long addr) { return 0; }
static inline int slab_pad_check(struct kmem_cache *s, struct page *page)
{ return 1; }
@@ -1499,8 +1504,8 @@ static inline int node_match(struct kmem_cache_cpu *c, int node)
* we need to allocate a new slab. This is the slowest path since it involves
* a call to the page allocator and the setup of a new slab.
*/
-static void *__slab_alloc(struct kmem_cache *s,
- gfp_t gfpflags, int node, void *addr, struct kmem_cache_cpu *c)
+static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
+ unsigned long addr, struct kmem_cache_cpu *c)
{
void **object;
struct page *new;
@@ -1584,13 +1589,18 @@ debug:
* Otherwise we can simply pick the next object from the lockless free list.
*/
static __always_inline void *slab_alloc(struct kmem_cache *s,
- gfp_t gfpflags, int node, void *addr)
+ gfp_t gfpflags, int node, unsigned long addr)
{
void **object;
struct kmem_cache_cpu *c;
unsigned long flags;
unsigned int objsize;
+ might_sleep_if(gfpflags & __GFP_WAIT);
+
+ if (should_failslab(s->objsize, gfpflags))
+ return NULL;
+
local_irq_save(flags);
c = get_cpu_slab(s, smp_processor_id());
objsize = c->objsize;
@@ -1613,14 +1623,14 @@ static __always_inline void *slab_alloc(struct kmem_cache *s,
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
- return slab_alloc(s, gfpflags, -1, __builtin_return_address(0));
+ return slab_alloc(s, gfpflags, -1, _RET_IP_);
}
EXPORT_SYMBOL(kmem_cache_alloc);
#ifdef CONFIG_NUMA
void *kmem_cache_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node)
{
- return slab_alloc(s, gfpflags, node, __builtin_return_address(0));
+ return slab_alloc(s, gfpflags, node, _RET_IP_);
}
EXPORT_SYMBOL(kmem_cache_alloc_node);
#endif
@@ -1634,7 +1644,7 @@ EXPORT_SYMBOL(kmem_cache_alloc_node);
* handling required then we can return immediately.
*/
static void __slab_free(struct kmem_cache *s, struct page *page,
- void *x, void *addr, unsigned int offset)
+ void *x, unsigned long addr, unsigned int offset)
{
void *prior;
void **object = (void *)x;
@@ -1704,7 +1714,7 @@ debug:
* with all sorts of special processing.
*/
static __always_inline void slab_free(struct kmem_cache *s,
- struct page *page, void *x, void *addr)
+ struct page *page, void *x, unsigned long addr)
{
void **object = (void *)x;
struct kmem_cache_cpu *c;
@@ -1731,11 +1741,11 @@ void kmem_cache_free(struct kmem_cache *s, void *x)
page = virt_to_head_page(x);
- slab_free(s, page, x, __builtin_return_address(0));
+ slab_free(s, page, x, _RET_IP_);
}
EXPORT_SYMBOL(kmem_cache_free);
-/* Figure out on which slab object the object resides */
+/* Figure out on which slab page the object resides */
static struct page *get_object_page(const void *x)
{
struct page *page = virt_to_head_page(x);
@@ -1807,8 +1817,8 @@ static inline int slab_order(int size, int min_objects,
int rem;
int min_order = slub_min_order;
- if ((PAGE_SIZE << min_order) / size > 65535)
- return get_order(size * 65535) - 1;
+ if ((PAGE_SIZE << min_order) / size > MAX_OBJS_PER_PAGE)
+ return get_order(size * MAX_OBJS_PER_PAGE) - 1;
for (order = max(min_order,
fls(min_objects * size - 1) - PAGE_SHIFT);
@@ -1960,7 +1970,7 @@ static DEFINE_PER_CPU(struct kmem_cache_cpu,
kmem_cache_cpu)[NR_KMEM_CACHE_CPU];
static DEFINE_PER_CPU(struct kmem_cache_cpu *, kmem_cache_cpu_free);
-static cpumask_t kmem_cach_cpu_free_init_once = CPU_MASK_NONE;
+static DECLARE_BITMAP(kmem_cach_cpu_free_init_once, CONFIG_NR_CPUS);
static struct kmem_cache_cpu *alloc_kmem_cache_cpu(struct kmem_cache *s,
int cpu, gfp_t flags)
@@ -2035,13 +2045,13 @@ static void init_alloc_cpu_cpu(int cpu)
{
int i;
- if (cpu_isset(cpu, kmem_cach_cpu_free_init_once))
+ if (cpumask_test_cpu(cpu, to_cpumask(kmem_cach_cpu_free_init_once)))
return;
for (i = NR_KMEM_CACHE_CPU - 1; i >= 0; i--)
free_kmem_cache_cpu(&per_cpu(kmem_cache_cpu, cpu)[i], cpu);
- cpu_set(cpu, kmem_cach_cpu_free_init_once);
+ cpumask_set_cpu(cpu, to_cpumask(kmem_cach_cpu_free_init_once));
}
static void __init init_alloc_cpu(void)
@@ -2073,8 +2083,7 @@ static inline int alloc_kmem_cache_cpus(struct kmem_cache *s, gfp_t flags)
* when allocating for the kmalloc_node_cache. This is used for bootstrapping
* memory on a fresh node that has no slab structures yet.
*/
-static struct kmem_cache_node *early_kmem_cache_node_alloc(gfp_t gfpflags,
- int node)
+static void early_kmem_cache_node_alloc(gfp_t gfpflags, int node)
{
struct page *page;
struct kmem_cache_node *n;
@@ -2112,7 +2121,6 @@ static struct kmem_cache_node *early_kmem_cache_node_alloc(gfp_t gfpflags,
local_irq_save(flags);
add_partial(n, page, 0);
local_irq_restore(flags);
- return n;
}
static void free_kmem_cache_nodes(struct kmem_cache *s)
@@ -2144,8 +2152,7 @@ static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t gfpflags)
n = &s->local_node;
else {
if (slab_state == DOWN) {
- n = early_kmem_cache_node_alloc(gfpflags,
- node);
+ early_kmem_cache_node_alloc(gfpflags, node);
continue;
}
n = kmem_cache_alloc_node(kmalloc_caches,
@@ -2659,7 +2666,7 @@ void *__kmalloc(size_t size, gfp_t flags)
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
- return slab_alloc(s, flags, -1, __builtin_return_address(0));
+ return slab_alloc(s, flags, -1, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc);
@@ -2687,7 +2694,7 @@ void *__kmalloc_node(size_t size, gfp_t flags, int node)
if (unlikely(ZERO_OR_NULL_PTR(s)))
return s;
- return slab_alloc(s, flags, node, __builtin_return_address(0));
+ return slab_alloc(s, flags, node, _RET_IP_);
}
EXPORT_SYMBOL(__kmalloc_node);
#endif
@@ -2744,7 +2751,7 @@ void kfree(const void *x)
put_page(page);
return;
}
- slab_free(page->slab, page, object, __builtin_return_address(0));
+ slab_free(page->slab, page, object, _RET_IP_);
}
EXPORT_SYMBOL(kfree);
@@ -3123,8 +3130,12 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size,
s->inuse = max_t(int, s->inuse, ALIGN(size, sizeof(void *)));
up_write(&slub_lock);
- if (sysfs_slab_alias(s, name))
+ if (sysfs_slab_alias(s, name)) {
+ down_write(&slub_lock);
+ s->refcount--;
+ up_write(&slub_lock);
goto err;
+ }
return s;
}
@@ -3134,8 +3145,13 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size,
size, align, flags, ctor)) {
list_add(&s->list, &slab_caches);
up_write(&slub_lock);
- if (sysfs_slab_add(s))
+ if (sysfs_slab_add(s)) {
+ down_write(&slub_lock);
+ list_del(&s->list);
+ up_write(&slub_lock);
+ kfree(s);
goto err;
+ }
return s;
}
kfree(s);
@@ -3202,7 +3218,7 @@ static struct notifier_block __cpuinitdata slab_notifier = {
#endif
-void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, void *caller)
+void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller)
{
struct kmem_cache *s;
@@ -3218,7 +3234,7 @@ void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, void *caller)
}
void *__kmalloc_node_track_caller(size_t size, gfp_t gfpflags,
- int node, void *caller)
+ int node, unsigned long caller)
{
struct kmem_cache *s;
@@ -3429,13 +3445,13 @@ static void resiliency_test(void) {};
struct location {
unsigned long count;
- void *addr;
+ unsigned long addr;
long long sum_time;
long min_time;
long max_time;
long min_pid;
long max_pid;
- cpumask_t cpus;
+ DECLARE_BITMAP(cpus, NR_CPUS);
nodemask_t nodes;
};
@@ -3477,7 +3493,7 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
{
long start, end, pos;
struct location *l;
- void *caddr;
+ unsigned long caddr;
unsigned long age = jiffies - track->when;
start = -1;
@@ -3510,7 +3526,8 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
if (track->pid > l->max_pid)
l->max_pid = track->pid;
- cpu_set(track->cpu, l->cpus);
+ cpumask_set_cpu(track->cpu,
+ to_cpumask(l->cpus));
}
node_set(page_to_nid(virt_to_page(track)), l->nodes);
return 1;
@@ -3540,8 +3557,8 @@ static int add_location(struct loc_track *t, struct kmem_cache *s,
l->max_time = age;
l->min_pid = track->pid;
l->max_pid = track->pid;
- cpus_clear(l->cpus);
- cpu_set(track->cpu, l->cpus);
+ cpumask_clear(to_cpumask(l->cpus));
+ cpumask_set_cpu(track->cpu, to_cpumask(l->cpus));
nodes_clear(l->nodes);
node_set(page_to_nid(virt_to_page(track)), l->nodes);
return 1;
@@ -3622,11 +3639,12 @@ static int list_locations(struct kmem_cache *s, char *buf,
len += sprintf(buf + len, " pid=%ld",
l->min_pid);
- if (num_online_cpus() > 1 && !cpus_empty(l->cpus) &&
+ if (num_online_cpus() > 1 &&
+ !cpumask_empty(to_cpumask(l->cpus)) &&
len < PAGE_SIZE - 60) {
len += sprintf(buf + len, " cpus=");
len += cpulist_scnprintf(buf + len, PAGE_SIZE - len - 50,
- &l->cpus);
+ to_cpumask(l->cpus));
}
if (num_online_nodes() > 1 && !nodes_empty(l->nodes) &&
@@ -4345,7 +4363,7 @@ static void sysfs_slab_remove(struct kmem_cache *s)
/*
* Need to buffer aliases during bootup until sysfs becomes
- * available lest we loose that information.
+ * available lest we lose that information.
*/
struct saved_alias {
struct kmem_cache *s;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 90cb67a5417c..54a9f87e5162 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1462,6 +1462,15 @@ static int __init procswaps_init(void)
__initcall(procswaps_init);
#endif /* CONFIG_PROC_FS */
+#ifdef MAX_SWAPFILES_CHECK
+static int __init max_swapfiles_check(void)
+{
+ MAX_SWAPFILES_CHECK();
+ return 0;
+}
+late_initcall(max_swapfiles_check);
+#endif
+
/*
* Written 01/25/92 by Simmule Turner, heavily changed by Linus.
*
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 62e7f62fb559..d196f46c8808 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1902,7 +1902,7 @@ static int kswapd(void *p)
};
node_to_cpumask_ptr(cpumask, pgdat->node_id);
- if (!cpus_empty(*cpumask))
+ if (!cpumask_empty(cpumask))
set_cpus_allowed_ptr(tsk, cpumask);
current->reclaim_state = &reclaim_state;
@@ -2141,7 +2141,7 @@ static int __devinit cpu_callback(struct notifier_block *nfb,
pg_data_t *pgdat = NODE_DATA(nid);
node_to_cpumask_ptr(mask, pgdat->node_id);
- if (any_online_cpu(*mask) < nr_cpu_ids)
+ if (cpumask_any_and(cpu_online_mask, mask) < nr_cpu_ids)
/* One of our CPUs online: restore mask */
set_cpus_allowed_ptr(pgdat->kswapd, mask);
}
diff --git a/mm/vmstat.c b/mm/vmstat.c
index c3ccfda23adc..91149746bb8d 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -20,7 +20,7 @@
DEFINE_PER_CPU(struct vm_event_state, vm_event_states) = {{0}};
EXPORT_PER_CPU_SYMBOL(vm_event_states);
-static void sum_vm_events(unsigned long *ret, cpumask_t *cpumask)
+static void sum_vm_events(unsigned long *ret, const struct cpumask *cpumask)
{
int cpu;
int i;
@@ -43,7 +43,7 @@ static void sum_vm_events(unsigned long *ret, cpumask_t *cpumask)
void all_vm_events(unsigned long *ret)
{
get_online_cpus();
- sum_vm_events(ret, &cpu_online_map);
+ sum_vm_events(ret, cpu_online_mask);
put_online_cpus();
}
EXPORT_SYMBOL_GPL(all_vm_events);
diff --git a/net/802/fddi.c b/net/802/fddi.c
index 0549317b9356..f1611a1e06a7 100644
--- a/net/802/fddi.c
+++ b/net/802/fddi.c
@@ -167,23 +167,27 @@ __be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev)
EXPORT_SYMBOL(fddi_type_trans);
-static int fddi_change_mtu(struct net_device *dev, int new_mtu)
+int fddi_change_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN))
return(-EINVAL);
dev->mtu = new_mtu;
return(0);
}
+EXPORT_SYMBOL(fddi_change_mtu);
static const struct header_ops fddi_header_ops = {
.create = fddi_header,
.rebuild = fddi_rebuild_header,
};
+
static void fddi_setup(struct net_device *dev)
{
- dev->change_mtu = fddi_change_mtu;
dev->header_ops = &fddi_header_ops;
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
+ dev->change_mtu = fddi_change_mtu,
+#endif
dev->type = ARPHRD_FDDI;
dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */
diff --git a/net/802/hippi.c b/net/802/hippi.c
index e35dc1e0915d..313b9ebf92ee 100644
--- a/net/802/hippi.c
+++ b/net/802/hippi.c
@@ -144,7 +144,7 @@ __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev)
EXPORT_SYMBOL(hippi_type_trans);
-static int hippi_change_mtu(struct net_device *dev, int new_mtu)
+int hippi_change_mtu(struct net_device *dev, int new_mtu)
{
/*
* HIPPI's got these nice large MTUs.
@@ -154,12 +154,13 @@ static int hippi_change_mtu(struct net_device *dev, int new_mtu)
dev->mtu = new_mtu;
return(0);
}
+EXPORT_SYMBOL(hippi_change_mtu);
/*
* For HIPPI we will actually use the lower 4 bytes of the hardware
* address as the I-FIELD rather than the actual hardware address.
*/
-static int hippi_mac_addr(struct net_device *dev, void *p)
+int hippi_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
if (netif_running(dev))
@@ -167,8 +168,9 @@ static int hippi_mac_addr(struct net_device *dev, void *p)
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
return 0;
}
+EXPORT_SYMBOL(hippi_mac_addr);
-static int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
{
/* Never send broadcast/multicast ARP messages */
p->mcast_probes = 0;
@@ -181,6 +183,7 @@ static int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
p->ucast_probes = 0;
return 0;
}
+EXPORT_SYMBOL(hippi_neigh_setup_dev);
static const struct header_ops hippi_header_ops = {
.create = hippi_header,
@@ -190,11 +193,12 @@ static const struct header_ops hippi_header_ops = {
static void hippi_setup(struct net_device *dev)
{
- dev->set_multicast_list = NULL;
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
dev->change_mtu = hippi_change_mtu;
- dev->header_ops = &hippi_header_ops;
dev->set_mac_address = hippi_mac_addr;
dev->neigh_setup = hippi_neigh_setup_dev;
+#endif
+ dev->header_ops = &hippi_header_ops;
/*
* We don't support HIPPI `ARP' for the time being, and probably
diff --git a/net/802/tr.c b/net/802/tr.c
index 18c66475d8c3..158150fee462 100644
--- a/net/802/tr.c
+++ b/net/802/tr.c
@@ -285,10 +285,7 @@ void tr_source_route(struct sk_buff *skb,struct trh_hdr *trh,
if(entry)
{
#if TR_SR_DEBUG
-{
-DECLARE_MAC_BUF(mac);
-printk("source routing for %s\n",print_mac(mac, trh->daddr));
-}
+printk("source routing for %pM\n", trh->daddr);
#endif
if(!entry->local_ring && (ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8)
{
@@ -370,9 +367,8 @@ static void tr_add_rif_info(struct trh_hdr *trh, struct net_device *dev)
if(entry==NULL)
{
#if TR_SR_DEBUG
- DECLARE_MAC_BUF(mac);
- printk("adding rif_entry: addr:%s rcf:%04X\n",
- print_mac(mac, trh->saddr), ntohs(trh->rcf));
+ printk("adding rif_entry: addr:%pM rcf:%04X\n",
+ trh->saddr, ntohs(trh->rcf));
#endif
/*
* Allocate our new entry. A failure to allocate loses
@@ -417,11 +413,8 @@ static void tr_add_rif_info(struct trh_hdr *trh, struct net_device *dev)
!(trh->rcf & htons(TR_RCF_BROADCAST_MASK)))
{
#if TR_SR_DEBUG
-{
-DECLARE_MAC_BUF(mac);
-printk("updating rif_entry: addr:%s rcf:%04X\n",
- print_mac(mac, trh->saddr), ntohs(trh->rcf));
-}
+printk("updating rif_entry: addr:%pM rcf:%04X\n",
+ trh->saddr, ntohs(trh->rcf));
#endif
entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK);
memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
@@ -532,7 +525,6 @@ static int rif_seq_show(struct seq_file *seq, void *v)
{
int j, rcf_len, segment, brdgnmb;
struct rif_cache *entry = v;
- DECLARE_MAC_BUF(mac);
if (v == SEQ_START_TOKEN)
seq_puts(seq,
@@ -542,9 +534,9 @@ static int rif_seq_show(struct seq_file *seq, void *v)
long ttl = (long) (entry->last_used + sysctl_tr_rif_timeout)
- (long) jiffies;
- seq_printf(seq, "%s %s %7li ",
+ seq_printf(seq, "%s %pM %7li ",
dev?dev->name:"?",
- print_mac(mac, entry->addr),
+ entry->addr,
ttl/HZ);
if (entry->local_ring)
@@ -643,7 +635,7 @@ static struct ctl_table tr_table[] = {
.data = &sysctl_tr_rif_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ 0 },
};
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index f0e335aa20df..41e8f65bd3f0 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -46,10 +46,10 @@ int vlan_net_id;
/* Our listing of VLAN group(s) */
static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE];
-static char vlan_fullname[] = "802.1Q VLAN Support";
-static char vlan_version[] = DRV_VERSION;
-static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
-static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
+const char vlan_fullname[] = "802.1Q VLAN Support";
+const char vlan_version[] = DRV_VERSION;
+static const char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
+static const char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
static struct packet_type vlan_packet_type = {
.type = __constant_htons(ETH_P_8021Q),
@@ -144,6 +144,7 @@ void unregister_vlan_dev(struct net_device *dev)
{
struct vlan_dev_info *vlan = vlan_dev_info(dev);
struct net_device *real_dev = vlan->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
struct vlan_group *grp;
u16 vlan_id = vlan->vlan_id;
@@ -156,7 +157,7 @@ void unregister_vlan_dev(struct net_device *dev)
* HW accelerating devices or SW vlan input packet processing.
*/
if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
- real_dev->vlan_rx_kill_vid(real_dev, vlan_id);
+ ops->ndo_vlan_rx_kill_vid(real_dev, vlan_id);
vlan_group_set_device(grp, vlan_id, NULL);
grp->nr_vlans--;
@@ -170,7 +171,7 @@ void unregister_vlan_dev(struct net_device *dev)
vlan_gvrp_uninit_applicant(real_dev);
if (real_dev->features & NETIF_F_HW_VLAN_RX)
- real_dev->vlan_rx_register(real_dev, NULL);
+ ops->ndo_vlan_rx_register(real_dev, NULL);
hlist_del_rcu(&grp->hlist);
@@ -205,21 +206,21 @@ static void vlan_transfer_operstate(const struct net_device *dev,
int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
{
- char *name = real_dev->name;
+ const char *name = real_dev->name;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
pr_info("8021q: VLANs not supported on %s\n", name);
return -EOPNOTSUPP;
}
- if ((real_dev->features & NETIF_F_HW_VLAN_RX) &&
- !real_dev->vlan_rx_register) {
+ if ((real_dev->features & NETIF_F_HW_VLAN_RX) && !ops->ndo_vlan_rx_register) {
pr_info("8021q: device %s has buggy VLAN hw accel\n", name);
return -EOPNOTSUPP;
}
if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) &&
- (!real_dev->vlan_rx_add_vid || !real_dev->vlan_rx_kill_vid)) {
+ (!ops->ndo_vlan_rx_add_vid || !ops->ndo_vlan_rx_kill_vid)) {
pr_info("8021q: Device %s has buggy VLAN hw accel\n", name);
return -EOPNOTSUPP;
}
@@ -240,6 +241,7 @@ int register_vlan_dev(struct net_device *dev)
{
struct vlan_dev_info *vlan = vlan_dev_info(dev);
struct net_device *real_dev = vlan->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
u16 vlan_id = vlan->vlan_id;
struct vlan_group *grp, *ngrp = NULL;
int err;
@@ -275,9 +277,9 @@ int register_vlan_dev(struct net_device *dev)
grp->nr_vlans++;
if (ngrp && real_dev->features & NETIF_F_HW_VLAN_RX)
- real_dev->vlan_rx_register(real_dev, ngrp);
+ ops->ndo_vlan_rx_register(real_dev, ngrp);
if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
- real_dev->vlan_rx_add_vid(real_dev, vlan_id);
+ ops->ndo_vlan_rx_add_vid(real_dev, vlan_id);
return 0;
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index a6603a4d917f..82570bc2a180 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -108,8 +108,10 @@ static inline int vlan_gvrp_init(void) { return 0; }
static inline void vlan_gvrp_uninit(void) {}
#endif
-int vlan_netlink_init(void);
-void vlan_netlink_fini(void);
+extern const char vlan_fullname[];
+extern const char vlan_version[];
+extern int vlan_netlink_init(void);
+extern void vlan_netlink_fini(void);
extern struct rtnl_link_ops vlan_link_ops;
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c
index 68ced4bf158c..dd86a1dc4cd0 100644
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -47,8 +47,6 @@ int vlan_hwaccel_do_receive(struct sk_buff *skb)
skb->priority = vlan_get_ingress_priority(dev, skb->vlan_tci);
skb->vlan_tci = 0;
- dev->last_rx = jiffies;
-
stats = &dev->stats;
stats->rx_packets++;
stats->rx_bytes += skb->len;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 8883e9c8a223..89a3bbdfca3f 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -163,8 +163,6 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
goto err_unlock;
}
- skb->dev->last_rx = jiffies;
-
stats = &skb->dev->stats;
stats->rx_packets++;
stats->rx_bytes += skb->len;
@@ -526,6 +524,7 @@ out:
static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
struct ifreq ifrr;
int err = -EOPNOTSUPP;
@@ -536,8 +535,8 @@ static int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
- if (real_dev->do_ioctl && netif_device_present(real_dev))
- err = real_dev->do_ioctl(real_dev, &ifrr, cmd);
+ if (netif_device_present(real_dev) && ops->ndo_do_ioctl)
+ err = ops->ndo_do_ioctl(real_dev, &ifrr, cmd);
break;
}
@@ -594,6 +593,8 @@ static const struct header_ops vlan_header_ops = {
.parse = eth_header_parse,
};
+static const struct net_device_ops vlan_netdev_ops, vlan_netdev_accel_ops;
+
static int vlan_dev_init(struct net_device *dev)
{
struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
@@ -620,11 +621,11 @@ static int vlan_dev_init(struct net_device *dev)
if (real_dev->features & NETIF_F_HW_VLAN_TX) {
dev->header_ops = real_dev->header_ops;
dev->hard_header_len = real_dev->hard_header_len;
- dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
+ dev->netdev_ops = &vlan_netdev_accel_ops;
} else {
dev->header_ops = &vlan_header_ops;
dev->hard_header_len = real_dev->hard_header_len + VLAN_HLEN;
- dev->hard_start_xmit = vlan_dev_hard_start_xmit;
+ dev->netdev_ops = &vlan_netdev_ops;
}
if (is_vlan_dev(real_dev))
@@ -648,6 +649,26 @@ static void vlan_dev_uninit(struct net_device *dev)
}
}
+static int vlan_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ const struct vlan_dev_info *vlan = vlan_dev_info(dev);
+ struct net_device *real_dev = vlan->real_dev;
+
+ if (!real_dev->ethtool_ops->get_settings)
+ return -EOPNOTSUPP;
+
+ return real_dev->ethtool_ops->get_settings(real_dev, cmd);
+}
+
+static void vlan_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, vlan_fullname);
+ strcpy(info->version, vlan_version);
+ strcpy(info->fw_version, "N/A");
+}
+
static u32 vlan_ethtool_get_rx_csum(struct net_device *dev)
{
const struct vlan_dev_info *vlan = vlan_dev_info(dev);
@@ -672,11 +693,43 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev)
}
static const struct ethtool_ops vlan_ethtool_ops = {
+ .get_settings = vlan_ethtool_get_settings,
+ .get_drvinfo = vlan_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_rx_csum = vlan_ethtool_get_rx_csum,
.get_flags = vlan_ethtool_get_flags,
};
+static const struct net_device_ops vlan_netdev_ops = {
+ .ndo_change_mtu = vlan_dev_change_mtu,
+ .ndo_init = vlan_dev_init,
+ .ndo_uninit = vlan_dev_uninit,
+ .ndo_open = vlan_dev_open,
+ .ndo_stop = vlan_dev_stop,
+ .ndo_start_xmit = vlan_dev_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = vlan_dev_set_mac_address,
+ .ndo_set_rx_mode = vlan_dev_set_rx_mode,
+ .ndo_set_multicast_list = vlan_dev_set_rx_mode,
+ .ndo_change_rx_flags = vlan_dev_change_rx_flags,
+ .ndo_do_ioctl = vlan_dev_ioctl,
+};
+
+static const struct net_device_ops vlan_netdev_accel_ops = {
+ .ndo_change_mtu = vlan_dev_change_mtu,
+ .ndo_init = vlan_dev_init,
+ .ndo_uninit = vlan_dev_uninit,
+ .ndo_open = vlan_dev_open,
+ .ndo_stop = vlan_dev_stop,
+ .ndo_start_xmit = vlan_dev_hwaccel_hard_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = vlan_dev_set_mac_address,
+ .ndo_set_rx_mode = vlan_dev_set_rx_mode,
+ .ndo_set_multicast_list = vlan_dev_set_rx_mode,
+ .ndo_change_rx_flags = vlan_dev_change_rx_flags,
+ .ndo_do_ioctl = vlan_dev_ioctl,
+};
+
void vlan_setup(struct net_device *dev)
{
ether_setup(dev);
@@ -684,16 +737,7 @@ void vlan_setup(struct net_device *dev)
dev->priv_flags |= IFF_802_1Q_VLAN;
dev->tx_queue_len = 0;
- dev->change_mtu = vlan_dev_change_mtu;
- dev->init = vlan_dev_init;
- dev->uninit = vlan_dev_uninit;
- dev->open = vlan_dev_open;
- dev->stop = vlan_dev_stop;
- dev->set_mac_address = vlan_dev_set_mac_address;
- dev->set_rx_mode = vlan_dev_set_rx_mode;
- dev->set_multicast_list = vlan_dev_set_rx_mode;
- dev->change_rx_flags = vlan_dev_change_rx_flags;
- dev->do_ioctl = vlan_dev_ioctl;
+ dev->netdev_ops = &vlan_netdev_ops;
dev->destructor = free_netdev;
dev->ethtool_ops = &vlan_ethtool_ops;
diff --git a/net/9p/client.c b/net/9p/client.c
index 4b529454616d..821f1ec0b2c3 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -627,7 +627,7 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)
memset(&fid->qid, 0, sizeof(struct p9_qid));
fid->mode = -1;
fid->rdir_fpos = 0;
- fid->uid = current->fsuid;
+ fid->uid = current_fsuid();
fid->clnt = clnt;
fid->aux = NULL;
diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c
index 2f1fe5fc1228..7fa0eb20b2f6 100644
--- a/net/9p/trans_rdma.c
+++ b/net/9p/trans_rdma.c
@@ -528,8 +528,6 @@ static void rdma_close(struct p9_client *client)
/**
* alloc_rdma - Allocate and initialize the rdma transport structure
- * @msize: MTU
- * @dotu: Extension attribute
* @opts: Mount options structure
*/
static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts)
diff --git a/net/Kconfig b/net/Kconfig
index d789d79551ae..6ec2cce7c167 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -27,11 +27,14 @@ menu "Networking options"
config NET_NS
bool "Network namespace support"
default n
- depends on EXPERIMENTAL && !SYSFS && NAMESPACES
+ depends on EXPERIMENTAL && NAMESPACES
help
Allow user space to create what appear to be multiple instances
of the network stack.
+config COMPAT_NET_DEV_OPS
+ def_bool y
+
source "net/packet/Kconfig"
source "net/unix/Kconfig"
source "net/xfrm/Kconfig"
@@ -191,6 +194,7 @@ source "net/lapb/Kconfig"
source "net/econet/Kconfig"
source "net/wanrouter/Kconfig"
source "net/sched/Kconfig"
+source "net/dcb/Kconfig"
menu "Network testing"
@@ -247,7 +251,6 @@ if WIRELESS
source "net/wireless/Kconfig"
source "net/mac80211/Kconfig"
-source "net/ieee80211/Kconfig"
endif # WIRELESS
diff --git a/net/Makefile b/net/Makefile
index 27d1f10dc0e0..ba4460432b7c 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -51,12 +51,14 @@ obj-$(CONFIG_IP_DCCP) += dccp/
obj-$(CONFIG_IP_SCTP) += sctp/
obj-y += wireless/
obj-$(CONFIG_MAC80211) += mac80211/
-obj-$(CONFIG_IEEE80211) += ieee80211/
obj-$(CONFIG_TIPC) += tipc/
obj-$(CONFIG_NETLABEL) += netlabel/
obj-$(CONFIG_IUCV) += iucv/
obj-$(CONFIG_RFKILL) += rfkill/
obj-$(CONFIG_NET_9P) += 9p/
+ifneq ($(CONFIG_DCB),)
+obj-y += dcb/
+endif
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_SYSCTL) += sysctl_net.o
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index b25c1e909d14..b03ff58e9308 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -995,7 +995,6 @@ static int aarp_seq_show(struct seq_file *seq, void *v)
struct aarp_iter_state *iter = seq->private;
struct aarp_entry *entry = v;
unsigned long now = jiffies;
- DECLARE_MAC_BUF(mac);
if (v == SEQ_START_TOKEN)
seq_puts(seq,
@@ -1006,7 +1005,7 @@ static int aarp_seq_show(struct seq_file *seq, void *v)
ntohs(entry->target_addr.s_net),
(unsigned int) entry->target_addr.s_node,
entry->dev ? entry->dev->name : "????");
- seq_printf(seq, "%s", print_mac(mac, entry->hwaddr));
+ seq_printf(seq, "%pM", entry->hwaddr);
seq_printf(seq, " %8s",
dt2str((long)entry->expires_at - (long)now));
if (iter->table == unresolved)
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index d3134e7e6ee8..5abce07fb50a 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -815,9 +815,6 @@ static int atif_ioctl(int cmd, void __user *arg)
return -EPERM;
if (sa->sat_family != AF_APPLETALK)
return -EINVAL;
- if (!atif)
- return -EADDRNOTAVAIL;
-
/*
* for now, we only support proxy AARP on ELAP;
* we should be able to do it for LocalTalk, too.
@@ -1284,7 +1281,7 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
skb->dev = dev;
skb_reset_transport_header(skb);
- stats = dev->priv;
+ stats = netdev_priv(dev);
stats->rx_packets++;
stats->rx_bytes += skb->len + 13;
netif_rx(skb); /* Send the SKB up to a higher place. */
diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c
index 621805dfa2f4..8d237b15183b 100644
--- a/net/appletalk/sysctl_net_atalk.c
+++ b/net/appletalk/sysctl_net_atalk.c
@@ -17,8 +17,8 @@ static struct ctl_table atalk_table[] = {
.data = &sysctl_aarp_expiry_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_ATALK_AARP_TICK_TIME,
@@ -26,8 +26,8 @@ static struct ctl_table atalk_table[] = {
.data = &sysctl_aarp_tick_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_ATALK_AARP_RETRANSMIT_LIMIT,
@@ -35,7 +35,7 @@ static struct ctl_table atalk_table[] = {
.data = &sysctl_aarp_retransmit_limit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_ATALK_AARP_RESOLVE_TIME,
@@ -43,8 +43,8 @@ static struct ctl_table atalk_table[] = {
.data = &sysctl_aarp_resolve_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{ 0 },
};
diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c
index 1b88311f2130..b5674dc2083d 100644
--- a/net/atm/atm_sysfs.c
+++ b/net/atm/atm_sysfs.c
@@ -149,7 +149,7 @@ int atm_register_sysfs(struct atm_dev *adev)
cdev->class = &atm_class;
dev_set_drvdata(cdev, adev);
- snprintf(cdev->bus_id, BUS_ID_SIZE, "%s%d", adev->type, adev->number);
+ dev_set_name(cdev, "%s%d", adev->type, adev->number);
err = device_register(cdev);
if (err < 0)
return err;
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 280de481edc7..ea9438fc6855 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -101,7 +101,7 @@ static LIST_HEAD(br2684_devs);
static inline struct br2684_dev *BRPRIV(const struct net_device *net_dev)
{
- return (struct br2684_dev *)net_dev->priv;
+ return (struct br2684_dev *)netdev_priv(net_dev);
}
static inline struct net_device *list_entry_brdev(const struct list_head *le)
@@ -698,12 +698,11 @@ static int br2684_seq_show(struct seq_file *seq, void *v)
br2684_devs);
const struct net_device *net_dev = brdev->net_dev;
const struct br2684_vcc *brvcc;
- DECLARE_MAC_BUF(mac);
- seq_printf(seq, "dev %.16s: num=%d, mac=%s (%s)\n",
+ seq_printf(seq, "dev %.16s: num=%d, mac=%pM (%s)\n",
net_dev->name,
brdev->number,
- print_mac(mac, net_dev->dev_addr),
+ net_dev->dev_addr,
brdev->mac_was_set ? "set" : "auto");
list_for_each_entry(brvcc, &brdev->brvccs, brvccs) {
diff --git a/net/atm/clip.c b/net/atm/clip.c
index 5b5b96344ce6..2d33a83be799 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -822,8 +822,8 @@ static void atmarp_info(struct seq_file *seq, struct net_device *dev,
seq_printf(seq, "%-6s%-4s%-4s%5ld ",
dev->name, svc ? "SVC" : "PVC", llc ? "LLC" : "NULL", exp);
- off = scnprintf(buf, sizeof(buf) - 1, "%d.%d.%d.%d",
- NIPQUAD(entry->ip));
+ off = scnprintf(buf, sizeof(buf) - 1, "%pI4",
+ &entry->ip);
while (off < 16)
buf[off++] = ' ';
buf[off] = '\0';
diff --git a/net/atm/common.h b/net/atm/common.h
index 16f32c1fa1c9..92e2981f479f 100644
--- a/net/atm/common.h
+++ b/net/atm/common.h
@@ -19,6 +19,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
size_t total_len);
unsigned int vcc_poll(struct file *file, struct socket *sock, poll_table *wait);
int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
int vcc_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen);
int vcc_getsockopt(struct socket *sock, int level, int optname,
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index 7afd8e7754fd..76ed3c8d26e6 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -19,6 +19,7 @@
#include <linux/atmlec.h>
#include <linux/mutex.h>
#include <asm/ioctls.h>
+#include <net/compat.h>
#include "resources.h"
#include "signaling.h" /* for WAITING and sigd_attach */
@@ -46,7 +47,7 @@ void deregister_atm_ioctl(struct atm_ioctl *ioctl)
EXPORT_SYMBOL(register_atm_ioctl);
EXPORT_SYMBOL(deregister_atm_ioctl);
-int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+static int do_vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg, int compat)
{
struct sock *sk = sock->sk;
struct atm_vcc *vcc;
@@ -80,13 +81,25 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
goto done;
}
case SIOCGSTAMP: /* borrowed from IP */
- error = sock_get_timestamp(sk, argp);
+#ifdef CONFIG_COMPAT
+ if (compat)
+ error = compat_sock_get_timestamp(sk, argp);
+ else
+#endif
+ error = sock_get_timestamp(sk, argp);
goto done;
case SIOCGSTAMPNS: /* borrowed from IP */
- error = sock_get_timestampns(sk, argp);
+#ifdef CONFIG_COMPAT
+ if (compat)
+ error = compat_sock_get_timestampns(sk, argp);
+ else
+#endif
+ error = sock_get_timestampns(sk, argp);
goto done;
case ATM_SETSC:
- printk(KERN_WARNING "ATM_SETSC is obsolete\n");
+ if (net_ratelimit())
+ printk(KERN_WARNING "ATM_SETSC is obsolete; used by %s:%d\n",
+ current->comm, task_pid_nr(current));
error = 0;
goto done;
case ATMSIGD_CTRL:
@@ -99,12 +112,23 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
* info uses kernel pointers as opaque references,
* so the holder of the file descriptor can scribble
* on the kernel... so we should make sure that we
- * have the same privledges that /proc/kcore needs
+ * have the same privileges that /proc/kcore needs
*/
if (!capable(CAP_SYS_RAWIO)) {
error = -EPERM;
goto done;
}
+#ifdef CONFIG_COMPAT
+ /* WTF? I don't even want to _think_ about making this
+ work for 32-bit userspace. TBH I don't really want
+ to think about it at all. dwmw2. */
+ if (compat) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "32-bit task cannot be atmsigd\n");
+ error = -EINVAL;
+ goto done;
+ }
+#endif
error = sigd_attach(vcc);
if (!error)
sock->state = SS_CONNECTED;
@@ -155,8 +179,21 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if (error != -ENOIOCTLCMD)
goto done;
- error = atm_dev_ioctl(cmd, argp);
+ error = atm_dev_ioctl(cmd, argp, compat);
done:
return error;
}
+
+
+int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ return do_vcc_ioctl(sock, cmd, arg, 0);
+}
+
+#ifdef CONFIG_COMPAT
+int vcc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ return do_vcc_ioctl(sock, cmd, arg, 1);
+}
+#endif
diff --git a/net/atm/lec.c b/net/atm/lec.c
index 8f701cde5945..e5e301550e8a 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -152,7 +152,7 @@ static void lec_handle_bridge(struct sk_buff *skb, struct net_device *dev)
buff += 4;
mesg->content.normal.flag = *buff & 0x01; /* 0x01 is topology change */
- priv = (struct lec_priv *)dev->priv;
+ priv = netdev_priv(dev);
atm_force_charge(priv->lecd, skb2->truesize);
sk = sk_atm(priv->lecd);
skb_queue_tail(&sk->sk_receive_queue, skb2);
@@ -218,7 +218,7 @@ static unsigned char *get_tr_dst(unsigned char *packet, unsigned char *rdesc)
static int lec_open(struct net_device *dev)
{
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
netif_start_queue(dev);
memset(&priv->stats, 0, sizeof(struct net_device_stats));
@@ -252,7 +252,7 @@ static void lec_tx_timeout(struct net_device *dev)
static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *skb2;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
struct lecdatahdr_8023 *lec_h;
struct atm_vcc *vcc;
struct lec_arp_table *entry;
@@ -373,19 +373,13 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
pr_debug("%s:lec_start_xmit: queuing packet, ",
dev->name);
- pr_debug("MAC address " MAC_FMT "\n",
- lec_h->h_dest[0], lec_h->h_dest[1],
- lec_h->h_dest[2], lec_h->h_dest[3],
- lec_h->h_dest[4], lec_h->h_dest[5]);
+ pr_debug("MAC address %pM\n", lec_h->h_dest);
skb_queue_tail(&entry->tx_wait, skb);
} else {
pr_debug
("%s:lec_start_xmit: tx queue full or no arp entry, dropping, ",
dev->name);
- pr_debug("MAC address " MAC_FMT "\n",
- lec_h->h_dest[0], lec_h->h_dest[1],
- lec_h->h_dest[2], lec_h->h_dest[3],
- lec_h->h_dest[4], lec_h->h_dest[5]);
+ pr_debug("MAC address %pM\n", lec_h->h_dest);
priv->stats.tx_dropped++;
dev_kfree_skb(skb);
}
@@ -397,10 +391,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
pr_debug("lec.c: emptying tx queue, ");
- pr_debug("MAC address " MAC_FMT "\n",
- lec_h->h_dest[0], lec_h->h_dest[1],
- lec_h->h_dest[2], lec_h->h_dest[3],
- lec_h->h_dest[4], lec_h->h_dest[5]);
+ pr_debug("MAC address %pM\n", lec_h->h_dest);
lec_send(vcc, skb2, priv);
}
@@ -442,14 +433,14 @@ static int lec_close(struct net_device *dev)
*/
static struct net_device_stats *lec_get_stats(struct net_device *dev)
{
- return &((struct lec_priv *)dev->priv)->stats;
+ return &((struct lec_priv *)netdev_priv(dev))->stats;
}
static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
{
unsigned long flags;
struct net_device *dev = (struct net_device *)vcc->proto_data;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
struct atmlec_msg *mesg;
struct lec_arp_table *entry;
int i;
@@ -539,15 +530,8 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
{
struct net_bridge_fdb_entry *f;
- pr_debug
- ("%s: bridge zeppelin asks about " MAC_FMT "\n",
- dev->name,
- mesg->content.proxy.mac_addr[0],
- mesg->content.proxy.mac_addr[1],
- mesg->content.proxy.mac_addr[2],
- mesg->content.proxy.mac_addr[3],
- mesg->content.proxy.mac_addr[4],
- mesg->content.proxy.mac_addr[5]);
+ pr_debug("%s: bridge zeppelin asks about %pM\n",
+ dev->name, mesg->content.proxy.mac_addr);
if (br_fdb_get_hook == NULL || dev->br_port == NULL)
break;
@@ -596,7 +580,7 @@ static void lec_atm_close(struct atm_vcc *vcc)
{
struct sk_buff *skb;
struct net_device *dev = (struct net_device *)vcc->proto_data;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
priv->lecd = NULL;
/* Do something needful? */
@@ -727,7 +711,7 @@ static void lec_push(struct atm_vcc *vcc, struct sk_buff *skb)
{
unsigned long flags;
struct net_device *dev = (struct net_device *)vcc->proto_data;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
#if DUMP_PACKETS >0
int i = 0;
@@ -874,7 +858,7 @@ static int lec_vcc_attach(struct atm_vcc *vcc, void __user *arg)
vpriv->old_pop = vcc->pop;
vcc->user_back = vpriv;
vcc->pop = lec_pop;
- lec_vcc_added(dev_lec[ioc_data.dev_num]->priv,
+ lec_vcc_added(netdev_priv(dev_lec[ioc_data.dev_num]),
&ioc_data, vcc, vcc->push);
vcc->proto_data = dev_lec[ioc_data.dev_num];
vcc->push = lec_push;
@@ -886,7 +870,8 @@ static int lec_mcast_attach(struct atm_vcc *vcc, int arg)
if (arg < 0 || arg >= MAX_LEC_ITF || !dev_lec[arg])
return -EINVAL;
vcc->proto_data = dev_lec[arg];
- return (lec_mcast_make((struct lec_priv *)dev_lec[arg]->priv, vcc));
+ return lec_mcast_make((struct lec_priv *)netdev_priv(dev_lec[arg]),
+ vcc);
}
/* Initialize device. */
@@ -928,11 +913,11 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
return -EINVAL;
}
- priv = dev_lec[i]->priv;
+ priv = netdev_priv(dev_lec[i]);
priv->is_trdev = is_trdev;
lec_init(dev_lec[i]);
} else {
- priv = dev_lec[i]->priv;
+ priv = netdev_priv(dev_lec[i]);
if (priv->lecd)
return -EADDRINUSE;
}
@@ -1093,7 +1078,8 @@ static void *lec_itf_walk(struct lec_state *state, loff_t *l)
void *v;
dev = state->dev ? state->dev : dev_lec[state->itf];
- v = (dev && dev->priv) ? lec_priv_walk(state, l, dev->priv) : NULL;
+ v = (dev && netdev_priv(dev)) ?
+ lec_priv_walk(state, l, netdev_priv(dev)) : NULL;
if (!v && dev) {
dev_put(dev);
/* Partial state reset for the next time we get called */
@@ -1255,7 +1241,7 @@ static void __exit lane_module_cleanup(void)
for (i = 0; i < MAX_LEC_ITF; i++) {
if (dev_lec[i] != NULL) {
- priv = (struct lec_priv *)dev_lec[i]->priv;
+ priv = netdev_priv(dev_lec[i]);
unregister_netdev(dev_lec[i]);
free_netdev(dev_lec[i]);
dev_lec[i] = NULL;
@@ -1279,7 +1265,7 @@ static int lane2_resolve(struct net_device *dev, const u8 *dst_mac, int force,
u8 **tlvs, u32 *sizeoftlvs)
{
unsigned long flags;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
struct lec_arp_table *table;
struct sk_buff *skb;
int retval;
@@ -1326,7 +1312,7 @@ static int lane2_associate_req(struct net_device *dev, const u8 *lan_dst,
{
int retval;
struct sk_buff *skb;
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
if (compare_ether_addr(lan_dst, dev->dev_addr))
return (0); /* not our mac address */
@@ -1363,7 +1349,7 @@ static void lane2_associate_ind(struct net_device *dev, const u8 *mac_addr,
#if 0
int i = 0;
#endif
- struct lec_priv *priv = (struct lec_priv *)dev->priv;
+ struct lec_priv *priv = netdev_priv(dev);
#if 0 /*
* Why have the TLVs in LE_ARP entries
* since we do not use them? When you
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index 11b16d16661c..039d5cc72c3d 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -232,8 +232,8 @@ void atm_mpoa_disp_qos(struct seq_file *m)
seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n");
while (qos != NULL) {
- seq_printf(m, "%u.%u.%u.%u\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n",
- NIPQUAD(qos->ipaddr),
+ seq_printf(m, "%pI4\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n",
+ &qos->ipaddr,
qos->qos.txtp.max_pcr, qos->qos.txtp.pcr, qos->qos.txtp.min_pcr, qos->qos.txtp.max_cdv, qos->qos.txtp.max_sdu,
qos->qos.rxtp.max_pcr, qos->qos.rxtp.pcr, qos->qos.rxtp.min_pcr, qos->qos.rxtp.max_cdv, qos->qos.rxtp.max_sdu);
qos = qos->next;
@@ -341,8 +341,8 @@ static const char *mpoa_device_type_string(char type)
}
/*
- * lec device calls this via its dev->priv->lane2_ops->associate_indicator()
- * when it sees a TLV in LE_ARP packet.
+ * lec device calls this via its netdev_priv(dev)->lane2_ops
+ * ->associate_indicator() when it sees a TLV in LE_ARP packet.
* We fill in the pointer above when we see a LANE2 lec initializing
* See LANE2 spec 3.1.5
*
@@ -595,8 +595,8 @@ static int atm_mpoa_vcc_attach(struct atm_vcc *vcc, void __user *arg)
if (in_entry != NULL) mpc->in_ops->put(in_entry);
return -EINVAL;
}
- printk("mpoa: (%s) mpc_vcc_attach: attaching ingress SVC, entry = %u.%u.%u.%u\n",
- mpc->dev->name, NIPQUAD(in_entry->ctrl_info.in_dst_ip));
+ printk("mpoa: (%s) mpc_vcc_attach: attaching ingress SVC, entry = %pI4\n",
+ mpc->dev->name, &in_entry->ctrl_info.in_dst_ip);
in_entry->shortcut = vcc;
mpc->in_ops->put(in_entry);
} else {
@@ -627,8 +627,8 @@ static void mpc_vcc_close(struct atm_vcc *vcc, struct net_device *dev)
dprintk("mpoa: (%s) mpc_vcc_close:\n", dev->name);
in_entry = mpc->in_ops->get_by_vcc(vcc, mpc);
if (in_entry) {
- dprintk("mpoa: (%s) mpc_vcc_close: ingress SVC closed ip = %u.%u.%u.%u\n",
- mpc->dev->name, NIPQUAD(in_entry->ctrl_info.in_dst_ip));
+ dprintk("mpoa: (%s) mpc_vcc_close: ingress SVC closed ip = %pI4\n",
+ mpc->dev->name, &in_entry->ctrl_info.in_dst_ip);
in_entry->shortcut = NULL;
mpc->in_ops->put(in_entry);
}
@@ -785,7 +785,7 @@ static int atm_mpoa_mpoad_attach (struct atm_vcc *vcc, int arg)
}
if (mpc->dev) { /* check if the lec is LANE2 capable */
- priv = (struct lec_priv *)mpc->dev->priv;
+ priv = netdev_priv(mpc->dev);
if (priv->lane_version < 2) {
dev_put(mpc->dev);
mpc->dev = NULL;
@@ -845,7 +845,7 @@ static void mpoad_close(struct atm_vcc *vcc)
mpc->mpoad_vcc = NULL;
if (mpc->dev) {
- struct lec_priv *priv = (struct lec_priv *)mpc->dev->priv;
+ struct lec_priv *priv = netdev_priv(mpc->dev);
priv->lane2_ops->associate_indicator = NULL;
stop_mpc(mpc);
dev_put(mpc->dev);
@@ -976,7 +976,7 @@ static int mpoa_event_listener(struct notifier_block *mpoa_notifier, unsigned lo
switch (event) {
case NETDEV_REGISTER: /* a new lec device was allocated */
- priv = (struct lec_priv *)dev->priv;
+ priv = netdev_priv(dev);
if (priv->lane_version < 2)
break;
priv->lane2_ops->associate_indicator = lane2_assoc_ind;
@@ -1098,7 +1098,8 @@ static void check_qos_and_open_shortcut(struct k_message *msg, struct mpoa_clien
entry->shortcut = eg_entry->shortcut;
}
if(entry->shortcut){
- dprintk("mpoa: (%s) using egress SVC to reach %u.%u.%u.%u\n",client->dev->name, NIPQUAD(dst_ip));
+ dprintk("mpoa: (%s) using egress SVC to reach %pI4\n",
+ client->dev->name, &dst_ip);
client->eg_ops->put(eg_entry);
return;
}
@@ -1123,7 +1124,8 @@ static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc)
__be32 dst_ip = msg->content.in_info.in_dst_ip;
in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc);
- dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %u.%u.%u.%u\n", mpc->dev->name, NIPQUAD(dst_ip));
+ dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %pI4\n",
+ mpc->dev->name, &dst_ip);
ddprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", mpc->dev->name, entry);
if(entry == NULL){
printk("\nmpoa: (%s) ARGH, received res. reply for an entry that doesn't exist.\n", mpc->dev->name);
@@ -1171,14 +1173,14 @@ static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc)
in_cache_entry *entry = mpc->in_ops->get_with_mask(dst_ip, mpc, mask);
if(entry == NULL){
- printk("mpoa: (%s) ingress_purge_rcvd: purge for a non-existing entry, ", mpc->dev->name);
- printk("ip = %u.%u.%u.%u\n", NIPQUAD(dst_ip));
+ printk("mpoa: (%s) ingress_purge_rcvd: purge for a non-existing entry, ip = %pI4\n",
+ mpc->dev->name, &dst_ip);
return;
}
do {
- dprintk("mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %u.%u.%u.%u\n" ,
- mpc->dev->name, NIPQUAD(dst_ip));
+ dprintk("mpoa: (%s) ingress_purge_rcvd: removing an ingress entry, ip = %pI4\n",
+ mpc->dev->name, &dst_ip);
write_lock_bh(&mpc->ingress_lock);
mpc->in_ops->remove_entry(entry, mpc);
write_unlock_bh(&mpc->ingress_lock);
@@ -1322,7 +1324,7 @@ static void set_mpc_ctrl_addr_rcvd(struct k_message *mesg, struct mpoa_client *m
dprintk("\n");
if (mpc->dev) {
- priv = (struct lec_priv *)mpc->dev->priv;
+ priv = netdev_priv(mpc->dev);
retval = priv->lane2_ops->associate_req(mpc->dev, mpc->dev->dev_addr, tlv, sizeof(tlv));
if (retval == 0)
printk("mpoa: (%s) MPOA device type TLV association failed\n", mpc->dev->name);
@@ -1472,7 +1474,7 @@ static void __exit atm_mpoa_cleanup(void)
tmp = mpc->next;
if (mpc->dev != NULL) {
stop_mpc(mpc);
- priv = (struct lec_priv *)mpc->dev->priv;
+ priv = netdev_priv(mpc->dev);
if (priv->lane2_ops != NULL)
priv->lane2_ops->associate_indicator = NULL;
}
diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c
index 24799e3e78f7..4504a4b339bb 100644
--- a/net/atm/mpoa_caches.c
+++ b/net/atm/mpoa_caches.c
@@ -94,7 +94,7 @@ static in_cache_entry *in_cache_add_entry(__be32 dst_ip,
return NULL;
}
- dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %u.%u.%u.%u\n", NIPQUAD(dst_ip));
+ dprintk("mpoa: mpoa_caches.c: adding an ingress entry, ip = %pI4\n", &dst_ip);
atomic_set(&entry->use, 1);
dprintk("mpoa: mpoa_caches.c: new_in_cache_entry: about to lock\n");
@@ -150,7 +150,8 @@ static int cache_hit(in_cache_entry *entry, struct mpoa_client *mpc)
if( entry->count > mpc->parameters.mpc_p1 &&
entry->entry_state == INGRESS_INVALID){
- dprintk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %u.%u.%u.%u, sending MPOA res req\n", mpc->dev->name, NIPQUAD(entry->ctrl_info.in_dst_ip));
+ dprintk("mpoa: (%s) mpoa_caches.c: threshold exceeded for ip %pI4, sending MPOA res req\n",
+ mpc->dev->name, &entry->ctrl_info.in_dst_ip);
entry->entry_state = INGRESS_RESOLVING;
msg.type = SND_MPOA_RES_RQST;
memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN );
@@ -184,7 +185,8 @@ static void in_cache_remove_entry(in_cache_entry *entry,
struct k_message msg;
vcc = entry->shortcut;
- dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %u.%u.%u.%u\n",NIPQUAD(entry->ctrl_info.in_dst_ip));
+ dprintk("mpoa: mpoa_caches.c: removing an ingress entry, ip = %pI4\n",
+ &entry->ctrl_info.in_dst_ip);
if (entry->prev != NULL)
entry->prev->next = entry->next;
@@ -228,7 +230,8 @@ static void clear_count_and_expired(struct mpoa_client *client)
next_entry = entry->next;
if((now.tv_sec - entry->tv.tv_sec)
> entry->ctrl_info.holding_time){
- dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(entry->ctrl_info.in_dst_ip));
+ dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %pI4\n",
+ &entry->ctrl_info.in_dst_ip);
client->in_ops->remove_entry(entry, client);
}
entry = next_entry;
@@ -453,7 +456,8 @@ static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_cli
return NULL;
}
- dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(msg->content.eg_info.eg_dst_ip));
+ dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %pI4, this should be our IP\n",
+ &msg->content.eg_info.eg_dst_ip);
atomic_set(&entry->use, 1);
dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry: about to lock\n");
@@ -469,8 +473,8 @@ static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_cli
do_gettimeofday(&(entry->tv));
entry->entry_state = EGRESS_RESOLVED;
dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id));
- dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n",
- NIPQUAD(entry->ctrl_info.mps_ip));
+ dprintk("mpoa: mpoa_caches.c: mps_ip = %pI4\n",
+ &entry->ctrl_info.mps_ip);
atomic_inc(&entry->use);
write_unlock_irq(&client->egress_lock);
diff --git a/net/atm/pvc.c b/net/atm/pvc.c
index 43e8bf5ed001..e1d22d9430dd 100644
--- a/net/atm/pvc.c
+++ b/net/atm/pvc.c
@@ -113,6 +113,9 @@ static const struct proto_ops pvc_proto_ops = {
.getname = pvc_getname,
.poll = vcc_poll,
.ioctl = vcc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vcc_compat_ioctl,
+#endif
.listen = sock_no_listen,
.shutdown = pvc_shutdown,
.setsockopt = pvc_setsockopt,
diff --git a/net/atm/resources.c b/net/atm/resources.c
index a34ba948af96..56b7322ff461 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -195,20 +195,39 @@ static int fetch_stats(struct atm_dev *dev, struct atm_dev_stats __user *arg, in
}
-int atm_dev_ioctl(unsigned int cmd, void __user *arg)
+int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat)
{
void __user *buf;
int error, len, number, size = 0;
struct atm_dev *dev;
struct list_head *p;
int *tmp_buf, *tmp_p;
- struct atm_iobuf __user *iobuf = arg;
- struct atmif_sioc __user *sioc = arg;
+ int __user *sioc_len;
+ int __user *iobuf_len;
+
+#ifndef CONFIG_COMPAT
+ compat = 0; /* Just so the compiler _knows_ */
+#endif
+
switch (cmd) {
case ATM_GETNAMES:
- if (get_user(buf, &iobuf->buffer))
- return -EFAULT;
- if (get_user(len, &iobuf->length))
+
+ if (compat) {
+#ifdef CONFIG_COMPAT
+ struct compat_atm_iobuf __user *ciobuf = arg;
+ compat_uptr_t cbuf;
+ iobuf_len = &ciobuf->length;
+ if (get_user(cbuf, &ciobuf->buffer))
+ return -EFAULT;
+ buf = compat_ptr(cbuf);
+#endif
+ } else {
+ struct atm_iobuf __user *iobuf = arg;
+ iobuf_len = &iobuf->length;
+ if (get_user(buf, &iobuf->buffer))
+ return -EFAULT;
+ }
+ if (get_user(len, iobuf_len))
return -EFAULT;
mutex_lock(&atm_dev_mutex);
list_for_each(p, &atm_devs)
@@ -229,7 +248,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
}
mutex_unlock(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) ||
- put_user(size, &iobuf->length))
+ put_user(size, iobuf_len))
? -EFAULT : 0;
kfree(tmp_buf);
return error;
@@ -237,13 +256,32 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
break;
}
- if (get_user(buf, &sioc->arg))
- return -EFAULT;
- if (get_user(len, &sioc->length))
- return -EFAULT;
- if (get_user(number, &sioc->number))
- return -EFAULT;
-
+ if (compat) {
+#ifdef CONFIG_COMPAT
+ struct compat_atmif_sioc __user *csioc = arg;
+ compat_uptr_t carg;
+
+ sioc_len = &csioc->length;
+ if (get_user(carg, &csioc->arg))
+ return -EFAULT;
+ buf = compat_ptr(carg);
+
+ if (get_user(len, &csioc->length))
+ return -EFAULT;
+ if (get_user(number, &csioc->number))
+ return -EFAULT;
+#endif
+ } else {
+ struct atmif_sioc __user *sioc = arg;
+
+ sioc_len = &sioc->length;
+ if (get_user(buf, &sioc->arg))
+ return -EFAULT;
+ if (get_user(len, &sioc->length))
+ return -EFAULT;
+ if (get_user(number, &sioc->number))
+ return -EFAULT;
+ }
if (!(dev = try_then_request_module(atm_dev_lookup(number),
"atm-device-%d", number)))
return -ENODEV;
@@ -358,7 +396,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
size = error;
/* may return 0, but later on size == 0 means "don't
write the length" */
- error = put_user(size, &sioc->length)
+ error = put_user(size, sioc_len)
? -EFAULT : 0;
goto done;
case ATM_SETLOOP:
@@ -380,11 +418,21 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
}
/* fall through */
default:
- if (!dev->ops->ioctl) {
- error = -EINVAL;
- goto done;
+ if (compat) {
+#ifdef CONFIG_COMPAT
+ if (!dev->ops->compat_ioctl) {
+ error = -EINVAL;
+ goto done;
+ }
+ size = dev->ops->compat_ioctl(dev, cmd, buf);
+#endif
+ } else {
+ if (!dev->ops->ioctl) {
+ error = -EINVAL;
+ goto done;
+ }
+ size = dev->ops->ioctl(dev, cmd, buf);
}
- size = dev->ops->ioctl(dev, cmd, buf);
if (size < 0) {
error = (size == -ENOIOCTLCMD ? -EINVAL : size);
goto done;
@@ -392,7 +440,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
}
if (size)
- error = put_user(size, &sioc->length)
+ error = put_user(size, sioc_len)
? -EFAULT : 0;
else
error = 0;
diff --git a/net/atm/resources.h b/net/atm/resources.h
index 1d004aaaeec1..126fb1840dfb 100644
--- a/net/atm/resources.h
+++ b/net/atm/resources.h
@@ -13,7 +13,7 @@
extern struct list_head atm_devs;
extern struct mutex atm_dev_mutex;
-int atm_dev_ioctl(unsigned int cmd, void __user *arg);
+int atm_dev_ioctl(unsigned int cmd, void __user *arg, int compat);
#ifdef CONFIG_PROC_FS
diff --git a/net/atm/svc.c b/net/atm/svc.c
index 8fb54dc870b3..7b831b526d0b 100644
--- a/net/atm/svc.c
+++ b/net/atm/svc.c
@@ -608,6 +608,22 @@ static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return error;
}
+#ifdef CONFIG_COMPAT
+static int svc_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ /* The definition of ATM_ADDPARTY uses the size of struct atm_iobuf.
+ But actually it takes a struct sockaddr_atmsvc, which doesn't need
+ compat handling. So all we have to do is fix up cmd... */
+ if (cmd == COMPAT_ATM_ADDPARTY)
+ cmd = ATM_ADDPARTY;
+
+ if (cmd == ATM_ADDPARTY || cmd == ATM_DROPPARTY)
+ return svc_ioctl(sock, cmd, arg);
+ else
+ return vcc_compat_ioctl(sock, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
static const struct proto_ops svc_proto_ops = {
.family = PF_ATMSVC,
.owner = THIS_MODULE,
@@ -620,6 +636,9 @@ static const struct proto_ops svc_proto_ops = {
.getname = svc_getname,
.poll = vcc_poll,
.ioctl = svc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = svc_compat_ioctl,
+#endif
.listen = svc_listen,
.shutdown = svc_shutdown,
.setsockopt = svc_setsockopt,
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 28c71574a781..00d9e5e13158 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1045,7 +1045,7 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (addr->fsa_ax25.sax25_family != AF_AX25)
return -EINVAL;
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (user) {
call = user->call;
ax25_uid_put(user);
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 4a5ba978a804..5f1d2107a1dd 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -200,19 +200,15 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
skb_reset_transport_header(skb);
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ goto free;
/*
* Parse the address header.
*/
- if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+ if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL)
+ goto free;
/*
* Ours perhaps ?
@@ -239,10 +235,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
ax25_send_to_raw(&dest, skb, skb->data[1]);
- if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
- kfree_skb(skb);
- return 0;
- }
+ if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0)
+ goto free;
/* Now we are pointing at the pid byte */
switch (skb->data[1]) {
@@ -301,10 +295,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
* If not, should we DM the incoming frame (except DMs) or
* silently ignore them. For now we stay quiet.
*/
- if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
- kfree_skb(skb);
- return 0;
- }
+ if (ax25_dev->values[AX25_VALUES_CONMODE] == 0)
+ goto free;
/* LAPB */
@@ -339,8 +331,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
+ goto free;
}
/* b) received SABM(E) */
@@ -372,15 +363,12 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
sk->sk_ack_backlog++;
bh_unlock_sock(sk);
} else {
- if (!mine) {
- kfree_skb(skb);
- return 0;
- }
+ if (!mine)
+ goto free;
if ((ax25 = ax25_create_cb()) == NULL) {
ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
+ goto free;
}
ax25_fillin_cb(ax25, ax25_dev);
@@ -436,9 +424,10 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_data_ready(sk, skb->len);
sock_put(sk);
- } else
+ } else {
+free:
kfree_skb(skb);
-
+ }
return 0;
}
diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c
index 8672cd84fdf9..c833ba4c45a5 100644
--- a/net/ax25/ax25_route.c
+++ b/net/ax25/ax25_route.c
@@ -421,7 +421,7 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
goto put;
}
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (user) {
ax25->source_addr = user->call;
ax25_uid_put(user);
diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c
index f288fc4aef9b..62ee3fb34732 100644
--- a/net/ax25/sysctl_net_ax25.c
+++ b/net/ax25/sysctl_net_ax25.c
@@ -24,7 +24,9 @@ static int min_idle[1], max_idle[] = {65535000};
static int min_n2[] = {1}, max_n2[] = {31};
static int min_paclen[] = {1}, max_paclen[] = {512};
static int min_proto[1], max_proto[] = { AX25_PROTO_MAX };
+#ifdef CONFIG_AX25_DAMA_SLAVE
static int min_ds_timeout[1], max_ds_timeout[] = {65535000};
+#endif
static struct ctl_table_header *ax25_table_header;
@@ -43,8 +45,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "ip_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_ipdefmode,
.extra2 = &max_ipdefmode
},
@@ -53,8 +55,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "ax25_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_axdefmode,
.extra2 = &max_axdefmode
},
@@ -63,8 +65,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "backoff_type",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_backoff,
.extra2 = &max_backoff
},
@@ -73,8 +75,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "connect_mode",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_conmode,
.extra2 = &max_conmode
},
@@ -83,8 +85,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "standard_window_size",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_window,
.extra2 = &max_window
},
@@ -93,8 +95,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "extended_window_size",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_ewindow,
.extra2 = &max_ewindow
},
@@ -103,8 +105,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "t1_timeout",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t1,
.extra2 = &max_t1
},
@@ -113,8 +115,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "t2_timeout",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t2,
.extra2 = &max_t2
},
@@ -123,8 +125,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "t3_timeout",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t3,
.extra2 = &max_t3
},
@@ -133,8 +135,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "idle_timeout",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_idle,
.extra2 = &max_idle
},
@@ -143,8 +145,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "maximum_retry_count",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_n2,
.extra2 = &max_n2
},
@@ -153,8 +155,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "maximum_packet_length",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_paclen,
.extra2 = &max_paclen
},
@@ -163,8 +165,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "protocol",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_proto,
.extra2 = &max_proto
},
@@ -174,8 +176,8 @@ static const ctl_table ax25_param_table[] = {
.procname = "dama_slave_timeout",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_ds_timeout,
.extra2 = &max_ds_timeout
},
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 8f9431a12c6f..744ed3f07ef3 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -41,18 +41,14 @@
#include <net/bluetooth/bluetooth.h>
-#ifndef CONFIG_BT_SOCK_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
-#define VERSION "2.13"
+#define VERSION "2.14"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
+static DEFINE_RWLOCK(bt_proto_lock);
-static struct lock_class_key bt_slock_key[BT_MAX_PROTO];
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
static const char *bt_key_strings[BT_MAX_PROTO] = {
"sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
@@ -65,6 +61,7 @@ static const char *bt_key_strings[BT_MAX_PROTO] = {
"sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP",
};
+static struct lock_class_key bt_slock_key[BT_MAX_PROTO];
static const char *bt_slock_key_strings[BT_MAX_PROTO] = {
"slock-AF_BLUETOOTH-BTPROTO_L2CAP",
"slock-AF_BLUETOOTH-BTPROTO_HCI",
@@ -75,7 +72,25 @@ static const char *bt_slock_key_strings[BT_MAX_PROTO] = {
"slock-AF_BLUETOOTH-BTPROTO_HIDP",
"slock-AF_BLUETOOTH-BTPROTO_AVDTP",
};
-static DEFINE_RWLOCK(bt_proto_lock);
+
+static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+{
+ struct sock *sk = sock->sk;
+
+ if (!sk)
+ return;
+
+ BUG_ON(sock_owned_by_user(sk));
+
+ sock_lock_init_class_and_name(sk,
+ bt_slock_key_strings[proto], &bt_slock_key[proto],
+ bt_key_strings[proto], &bt_lock_key[proto]);
+}
+#else
+static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+{
+}
+#endif
int bt_sock_register(int proto, struct net_proto_family *ops)
{
@@ -117,21 +132,6 @@ int bt_sock_unregister(int proto)
}
EXPORT_SYMBOL(bt_sock_unregister);
-static void bt_reclassify_sock_lock(struct socket *sock, int proto)
-{
- struct sock *sk = sock->sk;
-
- if (!sk)
- return;
- BUG_ON(sock_owned_by_user(sk));
-
- sock_lock_init_class_and_name(sk,
- bt_slock_key_strings[proto],
- &bt_slock_key[proto],
- bt_key_strings[proto],
- &bt_lock_key[proto]);
-}
-
static int bt_sock_create(struct net *net, struct socket *sock, int proto)
{
int err;
@@ -151,7 +151,7 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto)
if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) {
err = bt_proto[proto]->create(net, sock, proto);
- bt_reclassify_sock_lock(sock, proto);
+ bt_sock_reclassify_lock(sock, proto);
module_put(bt_proto[proto]->owner);
}
@@ -240,7 +240,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
size_t copied;
int err;
- BT_DBG("sock %p sk %p len %d", sock, sk, len);
+ BT_DBG("sock %p sk %p len %zu", sock, sk, len);
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index b69bf4e7c48b..d20f8a40f36e 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -161,7 +161,7 @@ struct bnep_session {
struct msghdr msg;
struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
- u64 mc_filter;
+ unsigned long long mc_filter;
struct socket *sock;
struct net_device *dev;
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 80ba30cf4b68..70fea8bdb4e5 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -52,11 +52,6 @@
#include "bnep.h"
-#ifndef CONFIG_BT_BNEP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.3"
static int compress_src = 1;
@@ -311,7 +306,6 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
struct sk_buff *nskb;
u8 type;
- dev->last_rx = jiffies;
s->stats.rx_bytes += skb->len;
type = *(u8 *) skb->data; skb_pull(skb, 1);
@@ -566,7 +560,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
goto failed;
}
- s = dev->priv;
+ s = netdev_priv(dev);
/* This is rx header therefore addresses are swapped.
* ie eh.h_dest is our local address. */
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index d9fa0ab2c87f..f897da6e0444 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -41,11 +41,6 @@
#include "bnep.h"
-#ifndef CONFIG_BT_BNEP_DEBUG
-#undef BT_DBG
-#define BT_DBG( A... )
-#endif
-
#define BNEP_TX_QUEUE_LEN 20
static int bnep_net_open(struct net_device *dev)
@@ -62,14 +57,14 @@ static int bnep_net_close(struct net_device *dev)
static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
{
- struct bnep_session *s = dev->priv;
+ struct bnep_session *s = netdev_priv(dev);
return &s->stats;
}
static void bnep_net_set_mc_list(struct net_device *dev)
{
#ifdef CONFIG_BT_BNEP_MC_FILTER
- struct bnep_session *s = dev->priv;
+ struct bnep_session *s = netdev_priv(dev);
struct sock *sk = s->sock->sk;
struct bnep_set_filter_req *r;
struct sk_buff *skb;
@@ -183,7 +178,7 @@ static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session
static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct bnep_session *s = dev->priv;
+ struct bnep_session *s = netdev_priv(dev);
struct sock *sk = s->sock->sk;
BT_DBG("skb %p, dev %p", skb, dev);
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 8ffb57f2303a..e857628b0b27 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -46,11 +46,6 @@
#include "bnep.h"
-#ifndef CONFIG_BT_BNEP_DEBUG
-#undef BT_DBG
-#define BT_DBG( A... )
-#endif
-
static int bnep_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 3e9d5bb3fefb..78958c0f9a40 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -42,11 +42,6 @@
#include "cmtp.h"
-#ifndef CONFIG_BT_CMTP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define CAPI_INTEROPERABILITY 0x20
#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index ca60a4517fd3..c9cac7719efe 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -44,11 +44,6 @@
#include "cmtp.h"
-#ifndef CONFIG_BT_CMTP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.0"
static DECLARE_RWSEM(cmtp_session_sem);
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 8c7f7bc4e0ba..16b0fad74f6e 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -43,11 +43,6 @@
#include "cmtp.h"
-#ifndef CONFIG_BT_CMTP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
static int cmtp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b7002429f152..a4a789f24c8d 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -45,11 +45,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCI_CORE_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
void hci_acl_connect(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 278a3ace14f6..ba78cc1eb8d9 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -48,11 +48,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCI_CORE_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
@@ -205,7 +200,7 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
/* Mandatory initialization */
/* Reset */
- if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
+ if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks))
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
/* Read Local Supported Features */
@@ -290,7 +285,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
{
__le16 policy = cpu_to_le16(opt);
- BT_DBG("%s %x", hdev->name, opt);
+ BT_DBG("%s %x", hdev->name, policy);
/* Default link policy */
hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
@@ -756,7 +751,7 @@ int hci_get_dev_list(void __user *arg)
size = sizeof(*dl) + dev_num * sizeof(*dr);
- if (!(dl = kmalloc(size, GFP_KERNEL)))
+ if (!(dl = kzalloc(size, GFP_KERNEL)))
return -ENOMEM;
dr = dl->dev_req;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index ad7a553d7713..f91ba690f5d2 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -45,11 +45,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCI_CORE_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index d62579b67959..4f9621f759a0 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -49,11 +49,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCI_SOCK_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
/* ----- HCI socket interface ----- */
static inline int hci_test_bit(int nr, void *addr)
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index f4f6615cad9f..1a1f916be44e 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -6,11 +6,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#ifndef CONFIG_BT_HCI_CORE_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
struct class *bt_class = NULL;
EXPORT_SYMBOL_GPL(bt_class);
@@ -113,8 +108,7 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
conn->dev.class = bt_class;
conn->dev.parent = &hdev->dev;
- snprintf(conn->dev.bus_id, BUS_ID_SIZE, "%s:%d",
- hdev->name, conn->handle);
+ dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
dev_set_drvdata(&conn->dev, conn);
@@ -132,7 +126,7 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
*/
static int __match_tty(struct device *dev, void *data)
{
- return !strncmp(dev->bus_id, "rfcomm", 6);
+ return !strncmp(dev_name(dev), "rfcomm", 6);
}
static void del_conn(struct work_struct *work)
@@ -421,7 +415,7 @@ int hci_register_sysfs(struct hci_dev *hdev)
dev->class = bt_class;
dev->parent = hdev->parent;
- strlcpy(dev->bus_id, hdev->name, BUS_ID_SIZE);
+ dev_set_name(dev, "%s", hdev->name);
dev_set_drvdata(dev, hdev);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index acdeab3d9807..b18676870d55 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -47,11 +47,6 @@
#include "hidp.h"
-#ifndef CONFIG_BT_HIDP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.2"
static DECLARE_RWSEM(hidp_session_sem);
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index f4dd02ca9a96..37c9d7d2e688 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -39,11 +39,6 @@
#include "hidp.h"
-#ifndef CONFIG_BT_HIDP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
static int hidp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 9610a9c85b98..b93748e224ff 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -50,11 +50,6 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#ifndef CONFIG_BT_L2CAP_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "2.11"
static u32 l2cap_feat_mask = 0x0000;
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index ba537fae0a4c..acd84fd524b8 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -46,11 +46,6 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
-#ifndef CONFIG_BT_RFCOMM_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "1.10"
static int disable_cfc = 0;
@@ -1786,8 +1781,6 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
if (err < 0)
return;
- __module_get(nsock->ops->owner);
-
/* Set our callbacks */
nsock->sk->sk_data_ready = rfcomm_l2data_ready;
nsock->sk->sk_state_change = rfcomm_l2state_change;
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 8a972b6ba85f..d3fc6fca38d0 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -50,11 +50,6 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
-#ifndef CONFIG_BT_RFCOMM_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
static const struct proto_ops rfcomm_sock_ops;
static struct bt_sock_list rfcomm_sk_list = {
@@ -644,7 +639,7 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_namelen = 0;
- BT_DBG("sk %p size %d", sk, size);
+ BT_DBG("sk %p size %zu", sk, size);
lock_sock(sk);
@@ -792,7 +787,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk = sock->sk;
+ struct sock *sk __maybe_unused = sock->sk;
int err;
BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index d3340dd52bcf..d030c69cb5a3 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -39,11 +39,6 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/rfcomm.h>
-#ifndef CONFIG_BT_RFCOMM_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */
#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */
#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */
@@ -58,7 +53,7 @@ struct rfcomm_dev {
char name[12];
int id;
unsigned long flags;
- int opened;
+ atomic_t opened;
int err;
bdaddr_t src;
@@ -261,6 +256,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
dev->flags = req->flags &
((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
+ atomic_set(&dev->opened, 0);
+
init_waitqueue_head(&dev->wait);
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
@@ -301,18 +298,15 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
out:
write_unlock_bh(&rfcomm_dev_lock);
- if (err < 0) {
- kfree(dev);
- return err;
- }
+ if (err < 0)
+ goto free;
dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL);
if (IS_ERR(dev->tty_dev)) {
err = PTR_ERR(dev->tty_dev);
list_del(&dev->list);
- kfree(dev);
- return err;
+ goto free;
}
dev_set_drvdata(dev->tty_dev, dev);
@@ -324,16 +318,20 @@ out:
BT_ERR("Failed to create channel attribute");
return dev->id;
+
+free:
+ kfree(dev);
+ return err;
}
static void rfcomm_dev_del(struct rfcomm_dev *dev)
{
BT_DBG("dev %p", dev);
- if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
- BUG_ON(1);
- else
- set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
+ BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
+
+ if (atomic_read(&dev->opened) > 0)
+ return;
write_lock_bh(&rfcomm_dev_lock);
list_del_init(&dev->list);
@@ -689,9 +687,10 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
if (!dev)
return -ENODEV;
- BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
+ BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst),
+ dev->channel, atomic_read(&dev->opened));
- if (dev->opened++ != 0)
+ if (atomic_inc_return(&dev->opened) > 1)
return 0;
dlc = dev->dlc;
@@ -747,9 +746,10 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
if (!dev)
return;
- BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
+ BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
+ atomic_read(&dev->opened));
- if (--dev->opened == 0) {
+ if (atomic_dec_and_test(&dev->opened)) {
if (dev->tty_dev->parent)
device_move(dev->tty_dev, NULL);
@@ -763,6 +763,14 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
tty->driver_data = NULL;
dev->tty = NULL;
rfcomm_dlc_unlock(dev->dlc);
+
+ if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
+ write_lock_bh(&rfcomm_dev_lock);
+ list_del_init(&dev->list);
+ write_unlock_bh(&rfcomm_dev_lock);
+
+ rfcomm_dev_put(dev);
+ }
}
rfcomm_dev_put(dev);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 0cc91e6da76d..46fd8bf9a690 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -48,11 +48,6 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/sco.h>
-#ifndef CONFIG_BT_SCO_DEBUG
-#undef BT_DBG
-#define BT_DBG(D...)
-#endif
-
#define VERSION "0.6"
static int disable_esco = 0;
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 6c023f0f8252..18538d7460d7 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -147,7 +147,7 @@ static int br_set_tx_csum(struct net_device *dev, u32 data)
return 0;
}
-static struct ethtool_ops br_ethtool_ops = {
+static const struct ethtool_ops br_ethtool_ops = {
.get_drvinfo = br_getinfo,
.get_link = ethtool_op_get_link,
.get_tx_csum = ethtool_op_get_tx_csum,
@@ -160,21 +160,25 @@ static struct ethtool_ops br_ethtool_ops = {
.get_flags = ethtool_op_get_flags,
};
+static const struct net_device_ops br_netdev_ops = {
+ .ndo_open = br_dev_open,
+ .ndo_stop = br_dev_stop,
+ .ndo_start_xmit = br_dev_xmit,
+ .ndo_set_mac_address = br_set_mac_address,
+ .ndo_set_multicast_list = br_dev_set_multicast_list,
+ .ndo_change_mtu = br_change_mtu,
+ .ndo_do_ioctl = br_dev_ioctl,
+};
+
void br_dev_setup(struct net_device *dev)
{
random_ether_addr(dev->dev_addr);
ether_setup(dev);
- dev->do_ioctl = br_dev_ioctl;
- dev->hard_start_xmit = br_dev_xmit;
- dev->open = br_dev_open;
- dev->set_multicast_list = br_dev_set_multicast_list;
- dev->change_mtu = br_change_mtu;
+ dev->netdev_ops = &br_netdev_ops;
dev->destructor = free_netdev;
SET_ETHTOOL_OPS(dev, &br_ethtool_ops);
- dev->stop = br_dev_stop;
dev->tx_queue_len = 0;
- dev->set_mac_address = br_set_mac_address;
dev->priv_flags = IFF_EBRIDGE;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 0a09ccf68c1c..727c5c510a60 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -373,7 +373,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
return -EINVAL;
- if (dev->hard_start_xmit == br_dev_xmit)
+ if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)
return -ELOOP;
if (dev->br_port != NULL)
@@ -460,7 +460,7 @@ void br_net_exit(struct net *net)
restart:
for_each_netdev(net, dev) {
if (dev->priv_flags & IFF_EBRIDGE) {
- del_br(dev->priv);
+ del_br(netdev_priv(dev));
goto restart;
}
}
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 45f61c348e36..a65e43a17fbb 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -109,7 +109,6 @@ static struct dst_ops fake_dst_ops = {
.family = AF_INET,
.protocol = __constant_htons(ETH_P_IP),
.update_pmtu = fake_update_pmtu,
- .entry_size = sizeof(struct rtable),
.entries = ATOMIC_INIT(0),
};
@@ -370,7 +369,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
goto free_skb;
- if (!ip_route_output_key(&init_net, &rt, &fl)) {
+ if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
/* - Bridged-and-DNAT'ed traffic doesn't
* require ip_forwarding. */
if (((struct dst_entry *)rt)->dev == dev) {
@@ -951,35 +950,35 @@ static ctl_table brnf_table[] = {
.data = &brnf_call_arptables,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &brnf_sysctl_call_tables,
+ .proc_handler = brnf_sysctl_call_tables,
},
{
.procname = "bridge-nf-call-iptables",
.data = &brnf_call_iptables,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &brnf_sysctl_call_tables,
+ .proc_handler = brnf_sysctl_call_tables,
},
{
.procname = "bridge-nf-call-ip6tables",
.data = &brnf_call_ip6tables,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &brnf_sysctl_call_tables,
+ .proc_handler = brnf_sysctl_call_tables,
},
{
.procname = "bridge-nf-filter-vlan-tagged",
.data = &brnf_filter_vlan_tagged,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &brnf_sysctl_call_tables,
+ .proc_handler = brnf_sysctl_call_tables,
},
{
.procname = "bridge-nf-filter-pppoe-tagged",
.data = &brnf_filter_pppoe_tagged,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &brnf_sysctl_call_tables,
+ .proc_handler = brnf_sysctl_call_tables,
},
{ .ctl_name = 0 }
};
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 158dee8b4965..603d89248e71 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -22,7 +22,7 @@
#include "br_private.h"
#define to_dev(obj) container_of(obj, struct device, kobj)
-#define to_bridge(cd) ((struct net_bridge *)(to_net_dev(cd)->priv))
+#define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd)))
/*
* Common code for storing bridge parameters.
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 3d33c608906a..d44cbf8c374a 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -79,7 +79,6 @@ print_ports(const struct sk_buff *skb, uint8_t protocol, int offset)
}
}
-#define myNIPQUAD(a) a[0], a[1], a[2], a[3]
static void
ebt_log_packet(u_int8_t pf, unsigned int hooknum,
const struct sk_buff *skb, const struct net_device *in,
@@ -113,9 +112,8 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
printk(" INCOMPLETE IP header");
goto out;
}
- printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP "
- "tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr),
- NIPQUAD(ih->daddr), ih->tos, ih->protocol);
+ printk(" IP SRC=%pI4 IP DST=%pI4, IP tos=0x%02X, IP proto=%d",
+ &ih->saddr, &ih->daddr, ih->tos, ih->protocol);
print_ports(skb, ih->protocol, ih->ihl*4);
goto out;
}
@@ -133,10 +131,8 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
printk(" INCOMPLETE IPv6 header");
goto out;
}
- printk(" IPv6 SRC=%x:%x:%x:%x:%x:%x:%x:%x "
- "IPv6 DST=%x:%x:%x:%x:%x:%x:%x:%x, IPv6 "
- "priority=0x%01X, Next Header=%d", NIP6(ih->saddr),
- NIP6(ih->daddr), ih->priority, ih->nexthdr);
+ printk(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d",
+ &ih->saddr, &ih->daddr, ih->priority, ih->nexthdr);
nexthdr = ih->nexthdr;
offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr);
if (offset_ph == -1)
@@ -177,12 +173,10 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
}
printk(" ARP MAC SRC=");
print_MAC(ap->mac_src);
- printk(" ARP IP SRC=%u.%u.%u.%u",
- myNIPQUAD(ap->ip_src));
+ printk(" ARP IP SRC=%pI4", ap->ip_src);
printk(" ARP MAC DST=");
print_MAC(ap->mac_dst);
- printk(" ARP IP DST=%u.%u.%u.%u",
- myNIPQUAD(ap->ip_dst));
+ printk(" ARP IP DST=%pI4", ap->ip_dst);
}
}
out:
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 246626bb0c87..8604dfc1fc3b 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -56,29 +56,47 @@ static int ebt_broute(struct sk_buff *skb)
int ret;
ret = ebt_do_table(NF_BR_BROUTING, skb, skb->dev, NULL,
- &broute_table);
+ dev_net(skb->dev)->xt.broute_table);
if (ret == NF_DROP)
return 1; /* route it */
return 0; /* bridge it */
}
+static int __net_init broute_net_init(struct net *net)
+{
+ net->xt.broute_table = ebt_register_table(net, &broute_table);
+ if (IS_ERR(net->xt.broute_table))
+ return PTR_ERR(net->xt.broute_table);
+ return 0;
+}
+
+static void __net_exit broute_net_exit(struct net *net)
+{
+ ebt_unregister_table(net->xt.broute_table);
+}
+
+static struct pernet_operations broute_net_ops = {
+ .init = broute_net_init,
+ .exit = broute_net_exit,
+};
+
static int __init ebtable_broute_init(void)
{
int ret;
- ret = ebt_register_table(&broute_table);
+ ret = register_pernet_subsys(&broute_net_ops);
if (ret < 0)
return ret;
/* see br_input.c */
rcu_assign_pointer(br_should_route_hook, ebt_broute);
- return ret;
+ return 0;
}
static void __exit ebtable_broute_fini(void)
{
rcu_assign_pointer(br_should_route_hook, NULL);
synchronize_net();
- ebt_unregister_table(&broute_table);
+ unregister_pernet_subsys(&broute_net_ops);
}
module_init(ebtable_broute_init);
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 1a58af51a2e2..2b2e8040a9c6 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -61,29 +61,36 @@ static struct ebt_table frame_filter =
};
static unsigned int
-ebt_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
+ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, int (*okfn)(struct sk_buff *))
{
- return ebt_do_table(hook, skb, in, out, &frame_filter);
+ return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter);
+}
+
+static unsigned int
+ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter);
}
static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
{
- .hook = ebt_hook,
+ .hook = ebt_in_hook,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_LOCAL_IN,
.priority = NF_BR_PRI_FILTER_BRIDGED,
},
{
- .hook = ebt_hook,
+ .hook = ebt_in_hook,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_FORWARD,
.priority = NF_BR_PRI_FILTER_BRIDGED,
},
{
- .hook = ebt_hook,
+ .hook = ebt_out_hook,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_LOCAL_OUT,
@@ -91,23 +98,41 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
},
};
+static int __net_init frame_filter_net_init(struct net *net)
+{
+ net->xt.frame_filter = ebt_register_table(net, &frame_filter);
+ if (IS_ERR(net->xt.frame_filter))
+ return PTR_ERR(net->xt.frame_filter);
+ return 0;
+}
+
+static void __net_exit frame_filter_net_exit(struct net *net)
+{
+ ebt_unregister_table(net->xt.frame_filter);
+}
+
+static struct pernet_operations frame_filter_net_ops = {
+ .init = frame_filter_net_init,
+ .exit = frame_filter_net_exit,
+};
+
static int __init ebtable_filter_init(void)
{
int ret;
- ret = ebt_register_table(&frame_filter);
+ ret = register_pernet_subsys(&frame_filter_net_ops);
if (ret < 0)
return ret;
ret = nf_register_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
if (ret < 0)
- ebt_unregister_table(&frame_filter);
+ unregister_pernet_subsys(&frame_filter_net_ops);
return ret;
}
static void __exit ebtable_filter_fini(void)
{
nf_unregister_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
- ebt_unregister_table(&frame_filter);
+ unregister_pernet_subsys(&frame_filter_net_ops);
}
module_init(ebtable_filter_init);
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index f60c1e78e575..3fe1ae87e35f 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -61,36 +61,36 @@ static struct ebt_table frame_nat =
};
static unsigned int
-ebt_nat_dst(unsigned int hook, struct sk_buff *skb, const struct net_device *in
+ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
- return ebt_do_table(hook, skb, in, out, &frame_nat);
+ return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat);
}
static unsigned int
-ebt_nat_src(unsigned int hook, struct sk_buff *skb, const struct net_device *in
+ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
- return ebt_do_table(hook, skb, in, out, &frame_nat);
+ return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat);
}
static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
{
- .hook = ebt_nat_dst,
+ .hook = ebt_nat_out,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_LOCAL_OUT,
.priority = NF_BR_PRI_NAT_DST_OTHER,
},
{
- .hook = ebt_nat_src,
+ .hook = ebt_nat_out,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_POST_ROUTING,
.priority = NF_BR_PRI_NAT_SRC,
},
{
- .hook = ebt_nat_dst,
+ .hook = ebt_nat_in,
.owner = THIS_MODULE,
.pf = PF_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
@@ -98,23 +98,41 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
},
};
+static int __net_init frame_nat_net_init(struct net *net)
+{
+ net->xt.frame_nat = ebt_register_table(net, &frame_nat);
+ if (IS_ERR(net->xt.frame_nat))
+ return PTR_ERR(net->xt.frame_nat);
+ return 0;
+}
+
+static void __net_exit frame_nat_net_exit(struct net *net)
+{
+ ebt_unregister_table(net->xt.frame_nat);
+}
+
+static struct pernet_operations frame_nat_net_ops = {
+ .init = frame_nat_net_init,
+ .exit = frame_nat_net_exit,
+};
+
static int __init ebtable_nat_init(void)
{
int ret;
- ret = ebt_register_table(&frame_nat);
+ ret = register_pernet_subsys(&frame_nat_net_ops);
if (ret < 0)
return ret;
ret = nf_register_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
if (ret < 0)
- ebt_unregister_table(&frame_nat);
+ unregister_pernet_subsys(&frame_nat_net_ops);
return ret;
}
static void __exit ebtable_nat_fini(void)
{
nf_unregister_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
- ebt_unregister_table(&frame_nat);
+ unregister_pernet_subsys(&frame_nat_net_ops);
}
module_init(ebtable_nat_init);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 0fa208e86405..fa108c46e851 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -55,7 +55,6 @@
static DEFINE_MUTEX(ebt_mutex);
-static LIST_HEAD(ebt_tables);
static struct xt_target ebt_standard_target = {
.name = "standard",
@@ -315,9 +314,11 @@ find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
}
static inline struct ebt_table *
-find_table_lock(const char *name, int *error, struct mutex *mutex)
+find_table_lock(struct net *net, const char *name, int *error,
+ struct mutex *mutex)
{
- return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+ return find_inlist_lock(&net->xt.tables[NFPROTO_BRIDGE], name,
+ "ebtable_", error, mutex);
}
static inline int
@@ -944,7 +945,7 @@ static void get_counters(struct ebt_counter *oldcounters,
}
/* replace the table */
-static int do_replace(void __user *user, unsigned int len)
+static int do_replace(struct net *net, void __user *user, unsigned int len)
{
int ret, i, countersize;
struct ebt_table_info *newinfo;
@@ -1016,7 +1017,7 @@ static int do_replace(void __user *user, unsigned int len)
if (ret != 0)
goto free_counterstmp;
- t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
if (!t) {
ret = -ENOENT;
goto free_iterate;
@@ -1097,7 +1098,7 @@ free_newinfo:
return ret;
}
-int ebt_register_table(struct ebt_table *table)
+struct ebt_table *ebt_register_table(struct net *net, struct ebt_table *table)
{
struct ebt_table_info *newinfo;
struct ebt_table *t;
@@ -1109,14 +1110,21 @@ int ebt_register_table(struct ebt_table *table)
repl->entries_size == 0 ||
repl->counters || table->private) {
BUGPRINT("Bad table data for ebt_register_table!!!\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Don't add one table to multiple lists. */
+ table = kmemdup(table, sizeof(struct ebt_table), GFP_KERNEL);
+ if (!table) {
+ ret = -ENOMEM;
+ goto out;
}
countersize = COUNTER_OFFSET(repl->nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
ret = -ENOMEM;
if (!newinfo)
- return -ENOMEM;
+ goto free_table;
p = vmalloc(repl->entries_size);
if (!p)
@@ -1148,7 +1156,7 @@ int ebt_register_table(struct ebt_table *table)
if (table->check && table->check(newinfo, table->valid_hooks)) {
BUGPRINT("The table doesn't like its own initial data, lol\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
table->private = newinfo;
@@ -1157,7 +1165,7 @@ int ebt_register_table(struct ebt_table *table)
if (ret != 0)
goto free_chainstack;
- list_for_each_entry(t, &ebt_tables, list) {
+ list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {
if (strcmp(t->name, table->name) == 0) {
ret = -EEXIST;
BUGPRINT("Table name already exists\n");
@@ -1170,9 +1178,9 @@ int ebt_register_table(struct ebt_table *table)
ret = -ENOENT;
goto free_unlock;
}
- list_add(&table->list, &ebt_tables);
+ list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]);
mutex_unlock(&ebt_mutex);
- return 0;
+ return table;
free_unlock:
mutex_unlock(&ebt_mutex);
free_chainstack:
@@ -1184,7 +1192,10 @@ free_chainstack:
vfree(newinfo->entries);
free_newinfo:
vfree(newinfo);
- return ret;
+free_table:
+ kfree(table);
+out:
+ return ERR_PTR(ret);
}
void ebt_unregister_table(struct ebt_table *table)
@@ -1198,6 +1209,10 @@ void ebt_unregister_table(struct ebt_table *table)
mutex_lock(&ebt_mutex);
list_del(&table->list);
mutex_unlock(&ebt_mutex);
+ EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
+ ebt_cleanup_entry, NULL);
+ if (table->private->nentries)
+ module_put(table->me);
vfree(table->private->entries);
if (table->private->chainstack) {
for_each_possible_cpu(i)
@@ -1205,10 +1220,11 @@ void ebt_unregister_table(struct ebt_table *table)
vfree(table->private->chainstack);
}
vfree(table->private);
+ kfree(table);
}
/* userspace just supplied us with counters */
-static int update_counters(void __user *user, unsigned int len)
+static int update_counters(struct net *net, void __user *user, unsigned int len)
{
int i, ret;
struct ebt_counter *tmp;
@@ -1228,7 +1244,7 @@ static int update_counters(void __user *user, unsigned int len)
return -ENOMEM;
}
- t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ t = find_table_lock(net, hlp.name, &ret, &ebt_mutex);
if (!t)
goto free_tmp;
@@ -1386,10 +1402,10 @@ static int do_ebt_set_ctl(struct sock *sk,
switch(cmd) {
case EBT_SO_SET_ENTRIES:
- ret = do_replace(user, len);
+ ret = do_replace(sock_net(sk), user, len);
break;
case EBT_SO_SET_COUNTERS:
- ret = update_counters(user, len);
+ ret = update_counters(sock_net(sk), user, len);
break;
default:
ret = -EINVAL;
@@ -1406,7 +1422,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT;
- t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex);
if (!t)
return ret;
diff --git a/net/can/raw.c b/net/can/raw.c
index 6e0663faaf9f..27aab63df467 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -641,17 +641,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT,
&err);
- if (!skb) {
- dev_put(dev);
- return err;
- }
+ if (!skb)
+ goto put_dev;
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
- if (err < 0) {
- kfree_skb(skb);
- dev_put(dev);
- return err;
- }
+ if (err < 0)
+ goto free_skb;
skb->dev = dev;
skb->sk = sk;
@@ -660,9 +655,16 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
dev_put(dev);
if (err)
- return err;
+ goto send_failed;
return size;
+
+free_skb:
+ kfree_skb(skb);
+put_dev:
+ dev_put(dev);
+send_failed:
+ return err;
}
static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
diff --git a/net/core/datagram.c b/net/core/datagram.c
index ee631843c2f5..5e2ac0c4b07c 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -209,7 +209,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags,
void skb_free_datagram(struct sock *sk, struct sk_buff *skb)
{
kfree_skb(skb);
- sk_mem_reclaim(sk);
+ sk_mem_reclaim_partial(sk);
}
/**
@@ -248,8 +248,7 @@ int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
spin_unlock_bh(&sk->sk_receive_queue.lock);
}
- kfree_skb(skb);
- sk_mem_reclaim(sk);
+ skb_free_datagram(sk, skb);
return err;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 9174c77d3112..09c66a449da6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -108,7 +108,6 @@
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/module.h>
-#include <linux/kallsyms.h>
#include <linux/netpoll.h>
#include <linux/rcupdate.h>
#include <linux/delay.h>
@@ -130,6 +129,9 @@
#include "net-sysfs.h"
+/* Instead of increasing this, you should create a hash table. */
+#define MAX_GRO_SKBS 8
+
/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
@@ -281,8 +283,8 @@ static const unsigned short netdev_lock_type[] =
ARPHRD_PIMREG, ARPHRD_HIPPI, ARPHRD_ASH, ARPHRD_ECONET,
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211,
- ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_VOID,
- ARPHRD_NONE};
+ ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET,
+ ARPHRD_PHONET_PIPE, ARPHRD_VOID, ARPHRD_NONE};
static const char *netdev_lock_name[] =
{"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
@@ -298,8 +300,8 @@ static const char *netdev_lock_name[] =
"_xmit_PIMREG", "_xmit_HIPPI", "_xmit_ASH", "_xmit_ECONET",
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211",
- "_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_VOID",
- "_xmit_NONE"};
+ "_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET",
+ "_xmit_PHONET_PIPE", "_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
static struct lock_class_key netdev_addr_lock_key[ARRAY_SIZE(netdev_lock_type)];
@@ -924,10 +926,15 @@ int dev_change_name(struct net_device *dev, const char *newname)
strlcpy(dev->name, newname, IFNAMSIZ);
rollback:
- ret = device_rename(&dev->dev, dev->name);
- if (ret) {
- memcpy(dev->name, oldname, IFNAMSIZ);
- return ret;
+ /* For now only devices in the initial network namespace
+ * are in sysfs.
+ */
+ if (net == &init_net) {
+ ret = device_rename(&dev->dev, dev->name);
+ if (ret) {
+ memcpy(dev->name, oldname, IFNAMSIZ);
+ return ret;
+ }
}
write_lock_bh(&dev_base_lock);
@@ -1055,6 +1062,7 @@ void dev_load(struct net *net, const char *name)
*/
int dev_open(struct net_device *dev)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
int ret = 0;
ASSERT_RTNL();
@@ -1077,11 +1085,11 @@ int dev_open(struct net_device *dev)
*/
set_bit(__LINK_STATE_START, &dev->state);
- if (dev->validate_addr)
- ret = dev->validate_addr(dev);
+ if (ops->ndo_validate_addr)
+ ret = ops->ndo_validate_addr(dev);
- if (!ret && dev->open)
- ret = dev->open(dev);
+ if (!ret && ops->ndo_open)
+ ret = ops->ndo_open(dev);
/*
* If it went open OK then:
@@ -1125,6 +1133,7 @@ int dev_open(struct net_device *dev)
*/
int dev_close(struct net_device *dev)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
ASSERT_RTNL();
might_sleep();
@@ -1157,8 +1166,8 @@ int dev_close(struct net_device *dev)
* We allow it to be called even after a DETACH hot-plug
* event.
*/
- if (dev->stop)
- dev->stop(dev);
+ if (ops->ndo_stop)
+ ops->ndo_stop(dev);
/*
* Device is now down.
@@ -1527,8 +1536,6 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
__be16 type = skb->protocol;
int err;
- BUG_ON(skb_shinfo(skb)->frag_list);
-
skb_reset_mac_header(skb);
skb->mac_len = skb->network_header - skb->mac_header;
__skb_pull(skb, skb->mac_len);
@@ -1654,6 +1661,9 @@ static int dev_gso_segment(struct sk_buff *skb)
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ prefetch(&dev->netdev_ops->ndo_start_xmit);
if (likely(!skb->next)) {
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
@@ -1665,7 +1675,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
goto gso;
}
- return dev->hard_start_xmit(skb, dev);
+ return ops->ndo_start_xmit(skb, dev);
}
gso:
@@ -1675,7 +1685,7 @@ gso:
skb->next = nskb->next;
nskb->next = NULL;
- rc = dev->hard_start_xmit(nskb, dev);
+ rc = ops->ndo_start_xmit(nskb, dev);
if (unlikely(rc)) {
nskb->next = skb->next;
skb->next = nskb;
@@ -1749,10 +1759,11 @@ static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb)
static struct netdev_queue *dev_pick_tx(struct net_device *dev,
struct sk_buff *skb)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
u16 queue_index = 0;
- if (dev->select_queue)
- queue_index = dev->select_queue(dev, skb);
+ if (ops->ndo_select_queue)
+ queue_index = ops->ndo_select_queue(dev, skb);
else if (dev->real_num_tx_queues > 1)
queue_index = simple_tx_hash(dev, skb);
@@ -2251,8 +2262,10 @@ int netif_receive_skb(struct sk_buff *skb)
rcu_read_lock();
/* Don't receive packets in an exiting network namespace */
- if (!net_alive(dev_net(skb->dev)))
+ if (!net_alive(dev_net(skb->dev))) {
+ kfree_skb(skb);
goto out;
+ }
#ifdef CONFIG_NET_CLS_ACT
if (skb->tc_verd & TC_NCLS) {
@@ -2325,6 +2338,125 @@ static void flush_backlog(void *arg)
}
}
+static int napi_gro_complete(struct sk_buff *skb)
+{
+ struct packet_type *ptype;
+ __be16 type = skb->protocol;
+ struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+ int err = -ENOENT;
+
+ if (!skb_shinfo(skb)->frag_list)
+ goto out;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ptype, head, list) {
+ if (ptype->type != type || ptype->dev || !ptype->gro_complete)
+ continue;
+
+ err = ptype->gro_complete(skb);
+ break;
+ }
+ rcu_read_unlock();
+
+ if (err) {
+ WARN_ON(&ptype->list == head);
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+
+out:
+ __skb_push(skb, -skb_network_offset(skb));
+ return netif_receive_skb(skb);
+}
+
+void napi_gro_flush(struct napi_struct *napi)
+{
+ struct sk_buff *skb, *next;
+
+ for (skb = napi->gro_list; skb; skb = next) {
+ next = skb->next;
+ skb->next = NULL;
+ napi_gro_complete(skb);
+ }
+
+ napi->gro_list = NULL;
+}
+EXPORT_SYMBOL(napi_gro_flush);
+
+int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
+{
+ struct sk_buff **pp = NULL;
+ struct packet_type *ptype;
+ __be16 type = skb->protocol;
+ struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+ int count = 0;
+ int same_flow;
+ int mac_len;
+
+ if (!(skb->dev->features & NETIF_F_GRO))
+ goto normal;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ptype, head, list) {
+ struct sk_buff *p;
+
+ if (ptype->type != type || ptype->dev || !ptype->gro_receive)
+ continue;
+
+ skb_reset_network_header(skb);
+ mac_len = skb->network_header - skb->mac_header;
+ skb->mac_len = mac_len;
+ NAPI_GRO_CB(skb)->same_flow = 0;
+ NAPI_GRO_CB(skb)->flush = 0;
+
+ for (p = napi->gro_list; p; p = p->next) {
+ count++;
+ NAPI_GRO_CB(p)->same_flow =
+ p->mac_len == mac_len &&
+ !memcmp(skb_mac_header(p), skb_mac_header(skb),
+ mac_len);
+ NAPI_GRO_CB(p)->flush = 0;
+ }
+
+ pp = ptype->gro_receive(&napi->gro_list, skb);
+ break;
+ }
+ rcu_read_unlock();
+
+ if (&ptype->list == head)
+ goto normal;
+
+ same_flow = NAPI_GRO_CB(skb)->same_flow;
+
+ if (pp) {
+ struct sk_buff *nskb = *pp;
+
+ *pp = nskb->next;
+ nskb->next = NULL;
+ napi_gro_complete(nskb);
+ count--;
+ }
+
+ if (same_flow)
+ goto ok;
+
+ if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) {
+ __skb_push(skb, -skb_network_offset(skb));
+ goto normal;
+ }
+
+ NAPI_GRO_CB(skb)->count = 1;
+ skb->next = napi->gro_list;
+ napi->gro_list = skb;
+
+ok:
+ return NET_RX_SUCCESS;
+
+normal:
+ return netif_receive_skb(skb);
+}
+EXPORT_SYMBOL(napi_gro_receive);
+
static int process_backlog(struct napi_struct *napi, int quota)
{
int work = 0;
@@ -2344,9 +2476,11 @@ static int process_backlog(struct napi_struct *napi, int quota)
}
local_irq_enable();
- netif_receive_skb(skb);
+ napi_gro_receive(napi, skb);
} while (++work < quota && jiffies == start_time);
+ napi_gro_flush(napi);
+
return work;
}
@@ -2367,11 +2501,73 @@ void __napi_schedule(struct napi_struct *n)
}
EXPORT_SYMBOL(__napi_schedule);
+void __napi_complete(struct napi_struct *n)
+{
+ BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
+ BUG_ON(n->gro_list);
+
+ list_del(&n->poll_list);
+ smp_mb__before_clear_bit();
+ clear_bit(NAPI_STATE_SCHED, &n->state);
+}
+EXPORT_SYMBOL(__napi_complete);
+
+void napi_complete(struct napi_struct *n)
+{
+ unsigned long flags;
+
+ /*
+ * don't let napi dequeue from the cpu poll list
+ * just in case its running on a different cpu
+ */
+ if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state)))
+ return;
+
+ napi_gro_flush(n);
+ local_irq_save(flags);
+ __napi_complete(n);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(napi_complete);
+
+void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int), int weight)
+{
+ INIT_LIST_HEAD(&napi->poll_list);
+ napi->gro_list = NULL;
+ napi->poll = poll;
+ napi->weight = weight;
+ list_add(&napi->dev_list, &dev->napi_list);
+#ifdef CONFIG_NETPOLL
+ napi->dev = dev;
+ spin_lock_init(&napi->poll_lock);
+ napi->poll_owner = -1;
+#endif
+ set_bit(NAPI_STATE_SCHED, &napi->state);
+}
+EXPORT_SYMBOL(netif_napi_add);
+
+void netif_napi_del(struct napi_struct *napi)
+{
+ struct sk_buff *skb, *next;
+
+ list_del_init(&napi->dev_list);
+
+ for (skb = napi->gro_list; skb; skb = next) {
+ next = skb->next;
+ skb->next = NULL;
+ kfree_skb(skb);
+ }
+
+ napi->gro_list = NULL;
+}
+EXPORT_SYMBOL(netif_napi_del);
+
static void net_rx_action(struct softirq_action *h)
{
struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
- unsigned long start_time = jiffies;
+ unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
void *have;
@@ -2382,13 +2578,10 @@ static void net_rx_action(struct softirq_action *h)
int work, weight;
/* If softirq window is exhuasted then punt.
- *
- * Note that this is a slight policy change from the
- * previous NAPI code, which would allow up to 2
- * jiffies to pass before breaking out. The test
- * used to be "jiffies - start_time > 1".
+ * Allow this to run for 2 jiffies since which will allow
+ * an average latency of 1.5/HZ.
*/
- if (unlikely(budget <= 0 || jiffies != start_time))
+ if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
goto softnet_break;
local_irq_enable();
@@ -2615,7 +2808,7 @@ void dev_seq_stop(struct seq_file *seq, void *v)
static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{
- struct net_device_stats *stats = dev->get_stats(dev);
+ const struct net_device_stats *stats = dev_get_stats(dev);
seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
"%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
@@ -2797,31 +2990,6 @@ static void ptype_seq_stop(struct seq_file *seq, void *v)
rcu_read_unlock();
}
-static void ptype_seq_decode(struct seq_file *seq, void *sym)
-{
-#ifdef CONFIG_KALLSYMS
- unsigned long offset = 0, symsize;
- const char *symname;
- char *modname;
- char namebuf[128];
-
- symname = kallsyms_lookup((unsigned long)sym, &symsize, &offset,
- &modname, namebuf);
-
- if (symname) {
- char *delim = ":";
-
- if (!modname)
- modname = delim = "";
- seq_printf(seq, "%s%s%s%s+0x%lx", delim, modname, delim,
- symname, offset);
- return;
- }
-#endif
-
- seq_printf(seq, "[%p]", sym);
-}
-
static int ptype_seq_show(struct seq_file *seq, void *v)
{
struct packet_type *pt = v;
@@ -2834,10 +3002,8 @@ static int ptype_seq_show(struct seq_file *seq, void *v)
else
seq_printf(seq, "%04x", ntohs(pt->type));
- seq_printf(seq, " %-8s ",
- pt->dev ? pt->dev->name : "");
- ptype_seq_decode(seq, pt->func);
- seq_putc(seq, '\n');
+ seq_printf(seq, " %-8s %pF\n",
+ pt->dev ? pt->dev->name : "", pt->func);
}
return 0;
@@ -2954,13 +3120,17 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
static void dev_change_rx_flags(struct net_device *dev, int flags)
{
- if (dev->flags & IFF_UP && dev->change_rx_flags)
- dev->change_rx_flags(dev, flags);
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if ((dev->flags & IFF_UP) && ops->ndo_change_rx_flags)
+ ops->ndo_change_rx_flags(dev, flags);
}
static int __dev_set_promiscuity(struct net_device *dev, int inc)
{
unsigned short old_flags = dev->flags;
+ uid_t uid;
+ gid_t gid;
ASSERT_RTNL();
@@ -2985,15 +3155,17 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
printk(KERN_INFO "device %s %s promiscuous mode\n",
dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
"left");
- if (audit_enabled)
+ if (audit_enabled) {
+ current_uid_gid(&uid, &gid);
audit_log(current->audit_context, GFP_ATOMIC,
AUDIT_ANOM_PROMISCUOUS,
"dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
dev->name, (dev->flags & IFF_PROMISC),
(old_flags & IFF_PROMISC),
audit_get_loginuid(current),
- current->uid, current->gid,
+ uid, gid,
audit_get_sessionid(current));
+ }
dev_change_rx_flags(dev, IFF_PROMISC);
}
@@ -3075,6 +3247,8 @@ int dev_set_allmulti(struct net_device *dev, int inc)
*/
void __dev_set_rx_mode(struct net_device *dev)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
/* dev_open will call this function so the list will stay sane. */
if (!(dev->flags&IFF_UP))
return;
@@ -3082,8 +3256,8 @@ void __dev_set_rx_mode(struct net_device *dev)
if (!netif_device_present(dev))
return;
- if (dev->set_rx_mode)
- dev->set_rx_mode(dev);
+ if (ops->ndo_set_rx_mode)
+ ops->ndo_set_rx_mode(dev);
else {
/* Unicast addresses changes may only happen under the rtnl,
* therefore calling __dev_set_promiscuity here is safe.
@@ -3096,8 +3270,8 @@ void __dev_set_rx_mode(struct net_device *dev)
dev->uc_promisc = 0;
}
- if (dev->set_multicast_list)
- dev->set_multicast_list(dev);
+ if (ops->ndo_set_multicast_list)
+ ops->ndo_set_multicast_list(dev);
}
}
@@ -3456,6 +3630,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
*/
int dev_set_mtu(struct net_device *dev, int new_mtu)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
int err;
if (new_mtu == dev->mtu)
@@ -3469,10 +3644,11 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
return -ENODEV;
err = 0;
- if (dev->change_mtu)
- err = dev->change_mtu(dev, new_mtu);
+ if (ops->ndo_change_mtu)
+ err = ops->ndo_change_mtu(dev, new_mtu);
else
dev->mtu = new_mtu;
+
if (!err && dev->flags & IFF_UP)
call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
return err;
@@ -3487,15 +3663,16 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
*/
int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
int err;
- if (!dev->set_mac_address)
+ if (!ops->ndo_set_mac_address)
return -EOPNOTSUPP;
if (sa->sa_family != dev->type)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
- err = dev->set_mac_address(dev, sa);
+ err = ops->ndo_set_mac_address(dev, sa);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return err;
@@ -3575,10 +3752,13 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
{
int err;
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
+ const struct net_device_ops *ops;
if (!dev)
return -ENODEV;
+ ops = dev->netdev_ops;
+
switch (cmd) {
case SIOCSIFFLAGS: /* Set interface flags */
return dev_change_flags(dev, ifr->ifr_flags);
@@ -3602,15 +3782,15 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
return 0;
case SIOCSIFMAP:
- if (dev->set_config) {
+ if (ops->ndo_set_config) {
if (!netif_device_present(dev))
return -ENODEV;
- return dev->set_config(dev, &ifr->ifr_map);
+ return ops->ndo_set_config(dev, &ifr->ifr_map);
}
return -EOPNOTSUPP;
case SIOCADDMULTI:
- if ((!dev->set_multicast_list && !dev->set_rx_mode) ||
+ if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
@@ -3619,7 +3799,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
dev->addr_len, 1);
case SIOCDELMULTI:
- if ((!dev->set_multicast_list && !dev->set_rx_mode) ||
+ if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
@@ -3657,10 +3837,9 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
cmd == SIOCBRDELIF ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
- if (dev->do_ioctl) {
+ if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
- err = dev->do_ioctl(dev, ifr,
- cmd);
+ err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
@@ -3921,8 +4100,8 @@ static void rollback_registered(struct net_device *dev)
*/
dev_addr_discard(dev);
- if (dev->uninit)
- dev->uninit(dev);
+ if (dev->netdev_ops->ndo_uninit)
+ dev->netdev_ops->ndo_uninit(dev);
/* Notifier chain MUST detach us from master device. */
WARN_ON(dev->master);
@@ -4012,7 +4191,7 @@ int register_netdevice(struct net_device *dev)
struct hlist_head *head;
struct hlist_node *p;
int ret;
- struct net *net;
+ struct net *net = dev_net(dev);
BUG_ON(dev_boot_phase);
ASSERT_RTNL();
@@ -4021,8 +4200,7 @@ int register_netdevice(struct net_device *dev)
/* When net_device's are persistent, this will be fatal. */
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
- BUG_ON(!dev_net(dev));
- net = dev_net(dev);
+ BUG_ON(!net);
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
@@ -4030,9 +4208,46 @@ int register_netdevice(struct net_device *dev)
dev->iflink = -1;
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
+ /* Netdevice_ops API compatiability support.
+ * This is temporary until all network devices are converted.
+ */
+ if (dev->netdev_ops) {
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ dev->init = ops->ndo_init;
+ dev->uninit = ops->ndo_uninit;
+ dev->open = ops->ndo_open;
+ dev->change_rx_flags = ops->ndo_change_rx_flags;
+ dev->set_rx_mode = ops->ndo_set_rx_mode;
+ dev->set_multicast_list = ops->ndo_set_multicast_list;
+ dev->set_mac_address = ops->ndo_set_mac_address;
+ dev->validate_addr = ops->ndo_validate_addr;
+ dev->do_ioctl = ops->ndo_do_ioctl;
+ dev->set_config = ops->ndo_set_config;
+ dev->change_mtu = ops->ndo_change_mtu;
+ dev->tx_timeout = ops->ndo_tx_timeout;
+ dev->get_stats = ops->ndo_get_stats;
+ dev->vlan_rx_register = ops->ndo_vlan_rx_register;
+ dev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
+ dev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = ops->ndo_poll_controller;
+#endif
+ } else {
+ char drivername[64];
+ pr_info("%s (%s): not using net_device_ops yet\n",
+ dev->name, netdev_drivername(dev, drivername, 64));
+
+ /* This works only because net_device_ops and the
+ compatiablity structure are the same. */
+ dev->netdev_ops = (void *) &(dev->init);
+ }
+#endif
+
/* Init, if this function is available */
- if (dev->init) {
- ret = dev->init(dev);
+ if (dev->netdev_ops->ndo_init) {
+ ret = dev->netdev_ops->ndo_init(dev);
if (ret) {
if (ret > 0)
ret = -EIO;
@@ -4110,8 +4325,8 @@ out:
return ret;
err_uninit:
- if (dev->uninit)
- dev->uninit(dev);
+ if (dev->netdev_ops->ndo_uninit)
+ dev->netdev_ops->ndo_uninit(dev);
goto out;
}
@@ -4267,10 +4482,24 @@ void netdev_run_todo(void)
}
}
-static struct net_device_stats *internal_stats(struct net_device *dev)
-{
- return &dev->stats;
+/**
+ * dev_get_stats - get network device statistics
+ * @dev: device to get statistics from
+ *
+ * Get network statistics from device. The device driver may provide
+ * its own method by setting dev->netdev_ops->get_stats; otherwise
+ * the internal statistics structure is used.
+ */
+const struct net_device_stats *dev_get_stats(struct net_device *dev)
+ {
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (ops->ndo_get_stats)
+ return ops->ndo_get_stats(dev);
+ else
+ return &dev->stats;
}
+EXPORT_SYMBOL(dev_get_stats);
static void netdev_init_one_queue(struct net_device *dev,
struct netdev_queue *queue,
@@ -4339,18 +4568,11 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
dev->num_tx_queues = queue_count;
dev->real_num_tx_queues = queue_count;
- if (sizeof_priv) {
- dev->priv = ((char *)dev +
- ((sizeof(struct net_device) + NETDEV_ALIGN_CONST)
- & ~NETDEV_ALIGN_CONST));
- }
-
dev->gso_max_size = GSO_MAX_SIZE;
netdev_init_queues(dev);
- dev->get_stats = internal_stats;
- netpoll_netdev_init(dev);
+ INIT_LIST_HEAD(&dev->napi_list);
setup(dev);
strcpy(dev->name, name);
return dev;
@@ -4367,10 +4589,15 @@ EXPORT_SYMBOL(alloc_netdev_mq);
*/
void free_netdev(struct net_device *dev)
{
+ struct napi_struct *p, *n;
+
release_net(dev_net(dev));
kfree(dev->_tx);
+ list_for_each_entry_safe(p, n, &dev->napi_list, dev_list)
+ netif_napi_del(p);
+
/* Compatibility with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) {
kfree((char *)dev - dev->padded);
@@ -4463,6 +4690,15 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
if (dev->features & NETIF_F_NETNS_LOCAL)
goto out;
+#ifdef CONFIG_SYSFS
+ /* Don't allow real devices to be moved when sysfs
+ * is enabled.
+ */
+ err = -EINVAL;
+ if (dev->dev.parent)
+ goto out;
+#endif
+
/* Ensure the device has been registrered */
err = -EINVAL;
if (dev->reg_state != NETREG_REGISTERED)
@@ -4520,6 +4756,8 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
*/
dev_addr_discard(dev);
+ netdev_unregister_kobject(dev);
+
/* Actually switch the network namespace */
dev_net_set(dev, net);
@@ -4536,7 +4774,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
}
/* Fixup kobjects */
- netdev_unregister_kobject(dev);
err = netdev_register_kobject(dev);
WARN_ON(err);
@@ -4829,13 +5066,14 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
static void __net_exit default_device_exit(struct net *net)
{
- struct net_device *dev, *next;
+ struct net_device *dev;
/*
* Push all migratable of the network devices back to the
* initial network namespace
*/
rtnl_lock();
- for_each_netdev_safe(net, dev, next) {
+restart:
+ for_each_netdev(net, dev) {
int err;
char fb_name[IFNAMSIZ];
@@ -4843,6 +5081,12 @@ static void __net_exit default_device_exit(struct net *net)
if (dev->features & NETIF_F_NETNS_LOCAL)
continue;
+ /* Delete virtual devices */
+ if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink) {
+ dev->rtnl_link_ops->dellink(dev);
+ goto restart;
+ }
+
/* Push remaing network devices to init_net */
snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
err = dev_change_net_namespace(dev, &init_net, fb_name);
@@ -4851,6 +5095,7 @@ static void __net_exit default_device_exit(struct net *net)
__func__, dev->name, err);
BUG();
}
+ goto restart;
}
rtnl_unlock();
}
@@ -4889,9 +5134,6 @@ static int __init net_dev_init(void)
if (register_pernet_subsys(&netdev_net_ops))
goto out;
- if (register_pernet_device(&default_device_ops))
- goto out;
-
/*
* Initialise the packet receive queues.
*/
@@ -4906,12 +5148,28 @@ static int __init net_dev_init(void)
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
+ queue->backlog.gro_list = NULL;
}
- netdev_dma_register();
-
dev_boot_phase = 0;
+ /* The loopback device is special if any other network devices
+ * is present in a network namespace the loopback device must
+ * be present. Since we now dynamically allocate and free the
+ * loopback device ensure this invariant is maintained by
+ * keeping the loopback device as the first device on the
+ * list of network devices. Ensuring the loopback devices
+ * is the first device that appears and the last network device
+ * that disappears.
+ */
+ if (register_pernet_device(&loopback_net_ops))
+ goto out;
+
+ if (register_pernet_device(&default_device_ops))
+ goto out;
+
+ netdev_dma_register();
+
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
diff --git a/net/core/dst.c b/net/core/dst.c
index 09c1530f4681..57bc4d5b8d08 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -263,9 +263,11 @@ again:
void dst_release(struct dst_entry *dst)
{
if (dst) {
- WARN_ON(atomic_read(&dst->__refcnt) < 1);
+ int newrefcnt;
+
smp_mb__before_atomic_dec();
- atomic_dec(&dst->__refcnt);
+ newrefcnt = atomic_dec_return(&dst->__refcnt);
+ WARN_ON(newrefcnt < 0);
}
}
EXPORT_SYMBOL(dst_release);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 14ada537f895..947710a36ced 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -528,6 +528,22 @@ static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
return dev->ethtool_ops->set_tx_csum(dev, edata.data);
}
+static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata;
+
+ if (!dev->ethtool_ops->set_rx_csum)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ if (!edata.data && dev->ethtool_ops->set_sg)
+ dev->features &= ~NETIF_F_GRO;
+
+ return dev->ethtool_ops->set_rx_csum(dev, edata.data);
+}
+
static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
{
struct ethtool_value edata;
@@ -599,6 +615,34 @@ static int ethtool_set_gso(struct net_device *dev, char __user *useraddr)
return 0;
}
+static int ethtool_get_gro(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata = { ETHTOOL_GGRO };
+
+ edata.data = dev->features & NETIF_F_GRO;
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ethtool_set_gro(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ if (edata.data) {
+ if (!dev->ethtool_ops->get_rx_csum ||
+ !dev->ethtool_ops->get_rx_csum(dev))
+ return -EINVAL;
+ dev->features |= NETIF_F_GRO;
+ } else
+ dev->features &= ~NETIF_F_GRO;
+
+ return 0;
+}
+
static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
{
struct ethtool_test test;
@@ -932,8 +976,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
dev->ethtool_ops->get_rx_csum);
break;
case ETHTOOL_SRXCSUM:
- rc = ethtool_set_value(dev, useraddr,
- dev->ethtool_ops->set_rx_csum);
+ rc = ethtool_set_rx_csum(dev, useraddr);
break;
case ETHTOOL_GTXCSUM:
rc = ethtool_get_value(dev, useraddr, ethcmd,
@@ -1014,6 +1057,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SRXFH:
rc = ethtool_set_rxhash(dev, useraddr);
break;
+ case ETHTOOL_GGRO:
+ rc = ethtool_get_gro(dev, useraddr);
+ break;
+ case ETHTOOL_SGRO:
+ rc = ethtool_set_gro(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 79de3b14a8d1..32b3a0152d7a 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -664,17 +664,18 @@ static int __init fib_rules_init(void)
rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL);
rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule);
- err = register_netdevice_notifier(&fib_rules_notifier);
+ err = register_pernet_subsys(&fib_rules_net_ops);
if (err < 0)
goto fail;
- err = register_pernet_subsys(&fib_rules_net_ops);
+ err = register_netdevice_notifier(&fib_rules_notifier);
if (err < 0)
goto fail_unregister;
+
return 0;
fail_unregister:
- unregister_netdevice_notifier(&fib_rules_notifier);
+ unregister_pernet_subsys(&fib_rules_net_ops);
fail:
rtnl_unregister(PF_UNSPEC, RTM_NEWRULE);
rtnl_unregister(PF_UNSPEC, RTM_DELRULE);
diff --git a/net/core/filter.c b/net/core/filter.c
index df3744355839..d1d779ca096d 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -319,6 +319,25 @@ load_b:
A = 0;
continue;
}
+ case SKF_AD_NLATTR_NEST: {
+ struct nlattr *nla;
+
+ if (skb_is_nonlinear(skb))
+ return 0;
+ if (A > skb->len - sizeof(struct nlattr))
+ return 0;
+
+ nla = (struct nlattr *)&skb->data[A];
+ if (nla->nla_len > A - skb->len)
+ return 0;
+
+ nla = nla_find_nested(nla, X);
+ if (nla)
+ A = (void *)nla - (void *)skb->data;
+ else
+ A = 0;
+ continue;
+ }
default:
return 0;
}
diff --git a/net/core/flow.c b/net/core/flow.c
index 5cf81052d044..96015871ecea 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -165,7 +165,7 @@ static int flow_key_compare(struct flowi *key1, struct flowi *key2)
return 0;
}
-void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
+void *flow_cache_lookup(struct net *net, struct flowi *key, u16 family, u8 dir,
flow_resolve_t resolver)
{
struct flow_cache_entry *fle, **head;
@@ -225,7 +225,7 @@ nocache:
void *obj;
atomic_t *obj_ref;
- err = resolver(key, family, dir, &obj, &obj_ref);
+ err = resolver(net, key, family, dir, &obj, &obj_ref);
if (fle && !err) {
fle->genid = atomic_read(&flow_cache_genid);
@@ -307,7 +307,7 @@ void flow_cache_flush(void)
put_online_cpus();
}
-static void __devinit flow_cache_cpu_prepare(int cpu)
+static void __init flow_cache_cpu_prepare(int cpu)
{
struct tasklet_struct *tasklet;
unsigned long order;
diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c
index 57abe8266be1..9cc9f95b109e 100644
--- a/net/core/gen_estimator.c
+++ b/net/core/gen_estimator.c
@@ -31,6 +31,7 @@
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
+#include <linux/rbtree.h>
#include <net/sock.h>
#include <net/gen_stats.h>
@@ -89,6 +90,7 @@ struct gen_estimator
u32 avpps;
u32 avbps;
struct rcu_head e_rcu;
+ struct rb_node node;
};
struct gen_estimator_head
@@ -102,6 +104,9 @@ static struct gen_estimator_head elist[EST_MAX_INTERVAL+1];
/* Protects against NULL dereference */
static DEFINE_RWLOCK(est_lock);
+/* Protects against soft lockup during large deletion */
+static struct rb_root est_root = RB_ROOT;
+
static void est_timer(unsigned long arg)
{
int idx = (int)arg;
@@ -139,6 +144,46 @@ skip:
rcu_read_unlock();
}
+static void gen_add_node(struct gen_estimator *est)
+{
+ struct rb_node **p = &est_root.rb_node, *parent = NULL;
+
+ while (*p) {
+ struct gen_estimator *e;
+
+ parent = *p;
+ e = rb_entry(parent, struct gen_estimator, node);
+
+ if (est->bstats > e->bstats)
+ p = &parent->rb_right;
+ else
+ p = &parent->rb_left;
+ }
+ rb_link_node(&est->node, parent, p);
+ rb_insert_color(&est->node, &est_root);
+}
+
+static
+struct gen_estimator *gen_find_node(const struct gnet_stats_basic *bstats,
+ const struct gnet_stats_rate_est *rate_est)
+{
+ struct rb_node *p = est_root.rb_node;
+
+ while (p) {
+ struct gen_estimator *e;
+
+ e = rb_entry(p, struct gen_estimator, node);
+
+ if (bstats > e->bstats)
+ p = p->rb_right;
+ else if (bstats < e->bstats || rate_est != e->rate_est)
+ p = p->rb_left;
+ else
+ return e;
+ }
+ return NULL;
+}
+
/**
* gen_new_estimator - create a new rate estimator
* @bstats: basic statistics
@@ -194,8 +239,11 @@ int gen_new_estimator(struct gnet_stats_basic *bstats,
mod_timer(&elist[idx].timer, jiffies + ((HZ/4) << idx));
list_add_rcu(&est->list, &elist[idx].list);
+ gen_add_node(est);
+
return 0;
}
+EXPORT_SYMBOL(gen_new_estimator);
static void __gen_kill_estimator(struct rcu_head *head)
{
@@ -209,36 +257,27 @@ static void __gen_kill_estimator(struct rcu_head *head)
* @bstats: basic statistics
* @rate_est: rate estimator statistics
*
- * Removes the rate estimator specified by &bstats and &rate_est
- * and deletes the timer.
+ * Removes the rate estimator specified by &bstats and &rate_est.
*
* NOTE: Called under rtnl_mutex
*/
void gen_kill_estimator(struct gnet_stats_basic *bstats,
- struct gnet_stats_rate_est *rate_est)
+ struct gnet_stats_rate_est *rate_est)
{
- int idx;
- struct gen_estimator *e, *n;
-
- for (idx=0; idx <= EST_MAX_INTERVAL; idx++) {
-
- /* Skip non initialized indexes */
- if (!elist[idx].timer.function)
- continue;
+ struct gen_estimator *e;
- list_for_each_entry_safe(e, n, &elist[idx].list, list) {
- if (e->rate_est != rate_est || e->bstats != bstats)
- continue;
+ while ((e = gen_find_node(bstats, rate_est))) {
+ rb_erase(&e->node, &est_root);
- write_lock_bh(&est_lock);
- e->bstats = NULL;
- write_unlock_bh(&est_lock);
+ write_lock_bh(&est_lock);
+ e->bstats = NULL;
+ write_unlock_bh(&est_lock);
- list_del_rcu(&e->list);
- call_rcu(&e->e_rcu, __gen_kill_estimator);
- }
+ list_del_rcu(&e->list);
+ call_rcu(&e->e_rcu, __gen_kill_estimator);
}
}
+EXPORT_SYMBOL(gen_kill_estimator);
/**
* gen_replace_estimator - replace rate estimator configuration
@@ -259,8 +298,20 @@ int gen_replace_estimator(struct gnet_stats_basic *bstats,
gen_kill_estimator(bstats, rate_est);
return gen_new_estimator(bstats, rate_est, stats_lock, opt);
}
+EXPORT_SYMBOL(gen_replace_estimator);
+/**
+ * gen_estimator_active - test if estimator is currently in use
+ * @bstats: basic statistics
+ * @rate_est: rate estimator statistics
+ *
+ * Returns true if estimator is active, and false if not.
+ */
+bool gen_estimator_active(const struct gnet_stats_basic *bstats,
+ const struct gnet_stats_rate_est *rate_est)
+{
+ ASSERT_RTNL();
-EXPORT_SYMBOL(gen_kill_estimator);
-EXPORT_SYMBOL(gen_new_estimator);
-EXPORT_SYMBOL(gen_replace_estimator);
+ return gen_find_node(bstats, rate_est) != NULL;
+}
+EXPORT_SYMBOL(gen_estimator_active);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1dc728b38589..f66c58df8953 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -531,9 +531,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
if (!n)
goto out;
-#ifdef CONFIG_NET_NS
- n->net = hold_net(net);
-#endif
+ write_pnet(&n->net, hold_net(net));
memcpy(n->key, pkey, key_len);
n->dev = dev;
if (dev)
@@ -1329,9 +1327,9 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
struct neigh_table *tbl)
{
struct neigh_parms *p, *ref;
- struct net *net;
+ struct net *net = dev_net(dev);
+ const struct net_device_ops *ops = dev->netdev_ops;
- net = dev_net(dev);
ref = lookup_neigh_params(tbl, net, 0);
if (!ref)
return NULL;
@@ -1340,20 +1338,17 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
if (p) {
p->tbl = tbl;
atomic_set(&p->refcnt, 1);
- INIT_RCU_HEAD(&p->rcu_head);
p->reachable_time =
neigh_rand_reach_time(p->base_reachable_time);
- if (dev->neigh_setup && dev->neigh_setup(dev, p)) {
+ if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) {
kfree(p);
return NULL;
}
dev_hold(dev);
p->dev = dev;
-#ifdef CONFIG_NET_NS
- p->net = hold_net(net);
-#endif
+ write_pnet(&p->net, hold_net(net));
p->sysctl_table = NULL;
write_lock_bh(&tbl->lock);
p->next = tbl->parms.next;
@@ -1408,11 +1403,8 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
unsigned long now = jiffies;
unsigned long phsize;
-#ifdef CONFIG_NET_NS
- tbl->parms.net = &init_net;
-#endif
+ write_pnet(&tbl->parms.net, &init_net);
atomic_set(&tbl->parms.refcnt, 1);
- INIT_RCU_HEAD(&tbl->parms.rcu_head);
tbl->parms.reachable_time =
neigh_rand_reach_time(tbl->parms.base_reachable_time);
@@ -1426,9 +1418,8 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
panic("cannot create neighbour cache statistics");
#ifdef CONFIG_PROC_FS
- tbl->pde = proc_create_data(tbl->id, 0, init_net.proc_net_stat,
- &neigh_stat_seq_fops, tbl);
- if (!tbl->pde)
+ if (!proc_create_data(tbl->id, 0, init_net.proc_net_stat,
+ &neigh_stat_seq_fops, tbl))
panic("cannot create neighbour proc dir entry");
#endif
@@ -2423,7 +2414,7 @@ static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
if (*pos == 0)
return SEQ_START_TOKEN;
- for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
@@ -2438,7 +2429,7 @@ static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
struct neigh_table *tbl = pde->data;
int cpu;
- for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
@@ -2568,128 +2559,128 @@ static struct neigh_sysctl_table {
.procname = "mcast_solicit",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NEIGH_UCAST_SOLICIT,
.procname = "ucast_solicit",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NEIGH_APP_SOLICIT,
.procname = "app_solicit",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "retrans_time",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_userhz_jiffies,
+ .proc_handler = proc_dointvec_userhz_jiffies,
},
{
.ctl_name = NET_NEIGH_REACHABLE_TIME,
.procname = "base_reachable_time",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_NEIGH_DELAY_PROBE_TIME,
.procname = "delay_first_probe_time",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_NEIGH_GC_STALE_TIME,
.procname = "gc_stale_time",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_NEIGH_UNRES_QLEN,
.procname = "unres_qlen",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NEIGH_PROXY_QLEN,
.procname = "proxy_qlen",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "anycast_delay",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_userhz_jiffies,
+ .proc_handler = proc_dointvec_userhz_jiffies,
},
{
.procname = "proxy_delay",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_userhz_jiffies,
+ .proc_handler = proc_dointvec_userhz_jiffies,
},
{
.procname = "locktime",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_userhz_jiffies,
+ .proc_handler = proc_dointvec_userhz_jiffies,
},
{
.ctl_name = NET_NEIGH_RETRANS_TIME_MS,
.procname = "retrans_time_ms",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies,
},
{
.ctl_name = NET_NEIGH_REACHABLE_TIME_MS,
.procname = "base_reachable_time_ms",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies,
},
{
.ctl_name = NET_NEIGH_GC_INTERVAL,
.procname = "gc_interval",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_NEIGH_GC_THRESH1,
.procname = "gc_thresh1",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NEIGH_GC_THRESH2,
.procname = "gc_thresh2",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NEIGH_GC_THRESH3,
.procname = "gc_thresh3",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{},
},
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 92d6b9467314..6ac29a46e23e 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -270,7 +270,6 @@ static ssize_t netstat_show(const struct device *d,
unsigned long offset)
{
struct net_device *dev = to_net_dev(d);
- struct net_device_stats *stats;
ssize_t ret = -EINVAL;
WARN_ON(offset > sizeof(struct net_device_stats) ||
@@ -278,7 +277,7 @@ static ssize_t netstat_show(const struct device *d,
read_lock(&dev_base_lock);
if (dev_isalive(dev)) {
- stats = dev->get_stats(dev);
+ const struct net_device_stats *stats = dev_get_stats(dev);
ret = sprintf(buf, fmt_ulong,
*(unsigned long *)(((u8 *) stats) + offset));
}
@@ -428,6 +427,9 @@ static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
struct net_device *dev = to_net_dev(d);
int retval;
+ if (!net_eq(dev_net(dev), &init_net))
+ return 0;
+
/* pass interface to uevent. */
retval = add_uevent_var(env, "INTERFACE=%s", dev->name);
if (retval)
@@ -476,6 +478,10 @@ void netdev_unregister_kobject(struct net_device * net)
struct device *dev = &(net->dev);
kobject_get(&dev->kobj);
+
+ if (dev_net(net) != &init_net)
+ return;
+
device_del(dev);
}
@@ -490,7 +496,7 @@ int netdev_register_kobject(struct net_device *net)
dev->groups = groups;
BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ);
- strlcpy(dev->bus_id, net->name, BUS_ID_SIZE);
+ dev_set_name(dev, net->name);
#ifdef CONFIG_SYSFS
*groups++ = &netstat_group;
@@ -501,6 +507,9 @@ int netdev_register_kobject(struct net_device *net)
#endif
#endif /* CONFIG_SYSFS */
+ if (dev_net(net) != &init_net)
+ return 0;
+
return device_add(dev);
}
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 1895a4ca9c4f..55cffad2f328 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -47,7 +47,6 @@ static __net_init int setup_net(struct net *net)
goto out;
ng->len = INITIAL_NET_GEN_PTRS;
- INIT_RCU_HEAD(&ng->rcu);
rcu_assign_pointer(net->gen, ng);
error = 0;
@@ -478,7 +477,6 @@ int net_assign_generic(struct net *net, int id, void *data)
*/
ng->len = id;
- INIT_RCU_HEAD(&ng->rcu);
memcpy(&ng->ptr, &old_ng->ptr, old_ng->len);
rcu_assign_pointer(net->gen, ng);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index dadac6281f20..755414cd49d1 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -58,6 +58,7 @@ static void queue_process(struct work_struct *work)
while ((skb = skb_dequeue(&npinfo->txq))) {
struct net_device *dev = skb->dev;
+ const struct net_device_ops *ops = dev->netdev_ops;
struct netdev_queue *txq;
if (!netif_device_present(dev) || !netif_running(dev)) {
@@ -71,7 +72,7 @@ static void queue_process(struct work_struct *work)
__netif_tx_lock(txq, smp_processor_id());
if (netif_tx_queue_stopped(txq) ||
netif_tx_queue_frozen(txq) ||
- dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) {
+ ops->ndo_start_xmit(skb, dev) != NETDEV_TX_OK) {
skb_queue_head(&npinfo->txq, skb);
__netif_tx_unlock(txq);
local_irq_restore(flags);
@@ -174,12 +175,13 @@ static void service_arp_queue(struct netpoll_info *npi)
void netpoll_poll(struct netpoll *np)
{
struct net_device *dev = np->dev;
+ const struct net_device_ops *ops = dev->netdev_ops;
- if (!dev || !netif_running(dev) || !dev->poll_controller)
+ if (!dev || !netif_running(dev) || !ops->ndo_poll_controller)
return;
/* Process pending work on NIC */
- dev->poll_controller(dev);
+ ops->ndo_poll_controller(dev);
poll_napi(dev);
@@ -274,6 +276,7 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
int status = NETDEV_TX_BUSY;
unsigned long tries;
struct net_device *dev = np->dev;
+ const struct net_device_ops *ops = dev->netdev_ops;
struct netpoll_info *npinfo = np->dev->npinfo;
if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) {
@@ -294,7 +297,7 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
tries > 0; --tries) {
if (__netif_tx_trylock(txq)) {
if (!netif_tx_queue_stopped(txq))
- status = dev->hard_start_xmit(skb, dev);
+ status = ops->ndo_start_xmit(skb, dev);
__netif_tx_unlock(txq);
if (status == NETDEV_TX_OK)
@@ -345,7 +348,7 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
udph->check = csum_tcpudp_magic(htonl(np->local_ip),
htonl(np->remote_ip),
udp_len, IPPROTO_UDP,
- csum_partial((unsigned char *)udph, udp_len, 0));
+ csum_partial(udph, udp_len, 0));
if (udph->check == 0)
udph->check = CSUM_MANGLED_0;
@@ -555,7 +558,6 @@ out:
void netpoll_print_options(struct netpoll *np)
{
- DECLARE_MAC_BUF(mac);
printk(KERN_INFO "%s: local port %d\n",
np->name, np->local_port);
printk(KERN_INFO "%s: local IP %d.%d.%d.%d\n",
@@ -566,8 +568,8 @@ void netpoll_print_options(struct netpoll *np)
np->name, np->remote_port);
printk(KERN_INFO "%s: remote IP %d.%d.%d.%d\n",
np->name, HIPQUAD(np->remote_ip));
- printk(KERN_INFO "%s: remote ethernet address %s\n",
- np->name, print_mac(mac, np->remote_mac));
+ printk(KERN_INFO "%s: remote ethernet address %pM\n",
+ np->name, np->remote_mac);
}
int netpoll_parse_options(struct netpoll *np, char *opt)
@@ -697,7 +699,7 @@ int netpoll_setup(struct netpoll *np)
atomic_inc(&npinfo->refcnt);
}
- if (!ndev->poll_controller) {
+ if (!ndev->netdev_ops->ndo_poll_controller) {
printk(KERN_ERR "%s: %s doesn't support polling, aborting.\n",
np->name, np->dev_name);
err = -ENOTSUPP;
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8997e912aaaf..65498483325a 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -422,6 +422,7 @@ static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
const char *ifname);
static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
static void pktgen_run_all_threads(void);
+static void pktgen_reset_all_threads(void);
static void pktgen_stop_all_threads_ifs(void);
static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
static void pktgen_stop(struct pktgen_thread *t);
@@ -480,6 +481,9 @@ static ssize_t pgctrl_write(struct file *file, const char __user * buf,
else if (!strcmp(data, "start"))
pktgen_run_all_threads();
+ else if (!strcmp(data, "reset"))
+ pktgen_reset_all_threads();
+
else
printk(KERN_WARNING "pktgen: Unknown command: %s\n", data);
@@ -509,7 +513,6 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
__u64 sa;
__u64 stopped;
__u64 now = getCurUs();
- DECLARE_MAC_BUF(mac);
seq_printf(seq,
"Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
@@ -554,12 +557,12 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
seq_puts(seq, " src_mac: ");
- seq_printf(seq, "%s ",
- print_mac(mac, is_zero_ether_addr(pkt_dev->src_mac) ?
- pkt_dev->odev->dev_addr : pkt_dev->src_mac));
+ seq_printf(seq, "%pM ",
+ is_zero_ether_addr(pkt_dev->src_mac) ?
+ pkt_dev->odev->dev_addr : pkt_dev->src_mac);
seq_printf(seq, "dst_mac: ");
- seq_printf(seq, "%s\n", print_mac(mac, pkt_dev->dst_mac));
+ seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
seq_printf(seq,
" udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
@@ -2162,7 +2165,8 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
struct xfrm_state *x = pkt_dev->flows[flow].x;
if (!x) {
/*slow path: we dont already have xfrm_state*/
- x = xfrm_stateonly_find((xfrm_address_t *)&pkt_dev->cur_daddr,
+ x = xfrm_stateonly_find(&init_net,
+ (xfrm_address_t *)&pkt_dev->cur_daddr,
(xfrm_address_t *)&pkt_dev->cur_saddr,
AF_INET,
pkt_dev->ipsmode,
@@ -3169,6 +3173,24 @@ static void pktgen_run_all_threads(void)
pktgen_wait_all_threads_run();
}
+static void pktgen_reset_all_threads(void)
+{
+ struct pktgen_thread *t;
+
+ pr_debug("pktgen: entering pktgen_reset_all_threads.\n");
+
+ mutex_lock(&pktgen_thread_lock);
+
+ list_for_each_entry(t, &pktgen_threads, th_list)
+ t->control |= (T_REMDEVALL);
+
+ mutex_unlock(&pktgen_thread_lock);
+
+ schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
+
+ pktgen_wait_all_threads_run();
+}
+
static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
{
__u64 total_us, bps, mbps, pps, idle;
@@ -3331,14 +3353,14 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
{
- struct net_device *odev = NULL;
+ struct net_device *odev = pkt_dev->odev;
+ int (*xmit)(struct sk_buff *, struct net_device *)
+ = odev->netdev_ops->ndo_start_xmit;
struct netdev_queue *txq;
__u64 idle_start = 0;
u16 queue_map;
int ret;
- odev = pkt_dev->odev;
-
if (pkt_dev->delay_us || pkt_dev->delay_ns) {
u64 now;
@@ -3419,7 +3441,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
atomic_inc(&(pkt_dev->skb->users));
retry_now:
- ret = odev->hard_start_xmit(pkt_dev->skb, odev);
+ ret = (*xmit)(pkt_dev->skb, odev);
if (likely(ret == NETDEV_TX_OK)) {
pkt_dev->last_ok = 1;
pkt_dev->sofar++;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 4dfb6b4d4559..790dd205bb5d 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -551,7 +551,7 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
}
static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
- struct net_device_stats *b)
+ const struct net_device_stats *b)
{
a->rx_packets = b->rx_packets;
a->tx_packets = b->tx_packets;
@@ -609,7 +609,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq;
struct ifinfomsg *ifm;
struct nlmsghdr *nlh;
- struct net_device_stats *stats;
+ const struct net_device_stats *stats;
struct nlattr *attr;
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags);
@@ -666,7 +666,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
if (attr == NULL)
goto nla_put_failure;
- stats = dev->get_stats(dev);
+ stats = dev_get_stats(dev);
copy_rtnl_link_stats(nla_data(attr), stats);
if (dev->rtnl_link_ops) {
@@ -762,6 +762,7 @@ static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
struct nlattr **tb, char *ifname, int modified)
{
+ const struct net_device_ops *ops = dev->netdev_ops;
int send_addr_notify = 0;
int err;
@@ -783,7 +784,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
struct rtnl_link_ifmap *u_map;
struct ifmap k_map;
- if (!dev->set_config) {
+ if (!ops->ndo_set_config) {
err = -EOPNOTSUPP;
goto errout;
}
@@ -801,7 +802,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
k_map.dma = (unsigned char) u_map->dma;
k_map.port = (unsigned char) u_map->port;
- err = dev->set_config(dev, &k_map);
+ err = ops->ndo_set_config(dev, &k_map);
if (err < 0)
goto errout;
@@ -812,7 +813,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
struct sockaddr *sa;
int len;
- if (!dev->set_mac_address) {
+ if (!ops->ndo_set_mac_address) {
err = -EOPNOTSUPP;
goto errout;
}
@@ -831,7 +832,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
sa->sa_family = dev->type;
memcpy(sa->sa_data, nla_data(tb[IFLA_ADDRESS]),
dev->addr_len);
- err = dev->set_mac_address(dev, sa);
+ err = ops->ndo_set_mac_address(dev, sa);
kfree(sa);
if (err)
goto errout;
diff --git a/net/core/scm.c b/net/core/scm.c
index b12303dd39d9..b7ba91b074b3 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -44,11 +44,13 @@
static __inline__ int scm_check_creds(struct ucred *creds)
{
+ const struct cred *cred = current_cred();
+
if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
- ((creds->uid == current->uid || creds->uid == current->euid ||
- creds->uid == current->suid) || capable(CAP_SETUID)) &&
- ((creds->gid == current->gid || creds->gid == current->egid ||
- creds->gid == current->sgid) || capable(CAP_SETGID))) {
+ ((creds->uid == cred->uid || creds->uid == cred->euid ||
+ creds->uid == cred->suid) || capable(CAP_SETUID)) &&
+ ((creds->gid == cred->gid || creds->gid == cred->egid ||
+ creds->gid == cred->sgid) || capable(CAP_SETGID))) {
return 0;
}
return -EPERM;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 65f7757465bd..b8d0abb26433 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -501,7 +501,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->network_header = old->network_header;
new->mac_header = old->mac_header;
new->dst = dst_clone(old->dst);
-#ifdef CONFIG_INET
+#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
memcpy(new->cb, old->cb, sizeof(old->cb));
@@ -556,6 +556,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
C(truesize);
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
C(do_not_encrypt);
+ C(requeue);
#endif
atomic_set(&n->users, 1);
@@ -2017,6 +2018,148 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
skb_split_no_header(skb, skb1, len, pos);
}
+/* Shifting from/to a cloned skb is a no-go.
+ *
+ * Caller cannot keep skb_shinfo related pointers past calling here!
+ */
+static int skb_prepare_for_shift(struct sk_buff *skb)
+{
+ return skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+}
+
+/**
+ * skb_shift - Shifts paged data partially from skb to another
+ * @tgt: buffer into which tail data gets added
+ * @skb: buffer from which the paged data comes from
+ * @shiftlen: shift up to this many bytes
+ *
+ * Attempts to shift up to shiftlen worth of bytes, which may be less than
+ * the length of the skb, from tgt to skb. Returns number bytes shifted.
+ * It's up to caller to free skb if everything was shifted.
+ *
+ * If @tgt runs out of frags, the whole operation is aborted.
+ *
+ * Skb cannot include anything else but paged data while tgt is allowed
+ * to have non-paged data as well.
+ *
+ * TODO: full sized shift could be optimized but that would need
+ * specialized skb free'er to handle frags without up-to-date nr_frags.
+ */
+int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
+{
+ int from, to, merge, todo;
+ struct skb_frag_struct *fragfrom, *fragto;
+
+ BUG_ON(shiftlen > skb->len);
+ BUG_ON(skb_headlen(skb)); /* Would corrupt stream */
+
+ todo = shiftlen;
+ from = 0;
+ to = skb_shinfo(tgt)->nr_frags;
+ fragfrom = &skb_shinfo(skb)->frags[from];
+
+ /* Actual merge is delayed until the point when we know we can
+ * commit all, so that we don't have to undo partial changes
+ */
+ if (!to ||
+ !skb_can_coalesce(tgt, to, fragfrom->page, fragfrom->page_offset)) {
+ merge = -1;
+ } else {
+ merge = to - 1;
+
+ todo -= fragfrom->size;
+ if (todo < 0) {
+ if (skb_prepare_for_shift(skb) ||
+ skb_prepare_for_shift(tgt))
+ return 0;
+
+ /* All previous frag pointers might be stale! */
+ fragfrom = &skb_shinfo(skb)->frags[from];
+ fragto = &skb_shinfo(tgt)->frags[merge];
+
+ fragto->size += shiftlen;
+ fragfrom->size -= shiftlen;
+ fragfrom->page_offset += shiftlen;
+
+ goto onlymerged;
+ }
+
+ from++;
+ }
+
+ /* Skip full, not-fitting skb to avoid expensive operations */
+ if ((shiftlen == skb->len) &&
+ (skb_shinfo(skb)->nr_frags - from) > (MAX_SKB_FRAGS - to))
+ return 0;
+
+ if (skb_prepare_for_shift(skb) || skb_prepare_for_shift(tgt))
+ return 0;
+
+ while ((todo > 0) && (from < skb_shinfo(skb)->nr_frags)) {
+ if (to == MAX_SKB_FRAGS)
+ return 0;
+
+ fragfrom = &skb_shinfo(skb)->frags[from];
+ fragto = &skb_shinfo(tgt)->frags[to];
+
+ if (todo >= fragfrom->size) {
+ *fragto = *fragfrom;
+ todo -= fragfrom->size;
+ from++;
+ to++;
+
+ } else {
+ get_page(fragfrom->page);
+ fragto->page = fragfrom->page;
+ fragto->page_offset = fragfrom->page_offset;
+ fragto->size = todo;
+
+ fragfrom->page_offset += todo;
+ fragfrom->size -= todo;
+ todo = 0;
+
+ to++;
+ break;
+ }
+ }
+
+ /* Ready to "commit" this state change to tgt */
+ skb_shinfo(tgt)->nr_frags = to;
+
+ if (merge >= 0) {
+ fragfrom = &skb_shinfo(skb)->frags[0];
+ fragto = &skb_shinfo(tgt)->frags[merge];
+
+ fragto->size += fragfrom->size;
+ put_page(fragfrom->page);
+ }
+
+ /* Reposition in the original skb */
+ to = 0;
+ while (from < skb_shinfo(skb)->nr_frags)
+ skb_shinfo(skb)->frags[to++] = skb_shinfo(skb)->frags[from++];
+ skb_shinfo(skb)->nr_frags = to;
+
+ BUG_ON(todo > 0 && !skb_shinfo(skb)->nr_frags);
+
+onlymerged:
+ /* Most likely the tgt won't ever need its checksum anymore, skb on
+ * the other hand might need it if it needs to be resent
+ */
+ tgt->ip_summed = CHECKSUM_PARTIAL;
+ skb->ip_summed = CHECKSUM_PARTIAL;
+
+ /* Yak, is it really working this way? Some helper please? */
+ skb->len -= shiftlen;
+ skb->data_len -= shiftlen;
+ skb->truesize -= shiftlen;
+ tgt->len += shiftlen;
+ tgt->data_len += shiftlen;
+ tgt->truesize += shiftlen;
+
+ return shiftlen;
+}
+
/**
* skb_prepare_seq_read - Prepare a sequential read of skb data
* @skb: the buffer to read
@@ -2285,6 +2428,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
{
struct sk_buff *segs = NULL;
struct sk_buff *tail = NULL;
+ struct sk_buff *fskb = skb_shinfo(skb)->frag_list;
unsigned int mss = skb_shinfo(skb)->gso_size;
unsigned int doffset = skb->data - skb_mac_header(skb);
unsigned int offset = doffset;
@@ -2304,7 +2448,6 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
struct sk_buff *nskb;
skb_frag_t *frag;
int hsize;
- int k;
int size;
len = skb->len - offset;
@@ -2317,9 +2460,36 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
if (hsize > len || !sg)
hsize = len;
- nskb = alloc_skb(hsize + doffset + headroom, GFP_ATOMIC);
- if (unlikely(!nskb))
- goto err;
+ if (!hsize && i >= nfrags) {
+ BUG_ON(fskb->len != len);
+
+ pos += len;
+ nskb = skb_clone(fskb, GFP_ATOMIC);
+ fskb = fskb->next;
+
+ if (unlikely(!nskb))
+ goto err;
+
+ hsize = skb_end_pointer(nskb) - nskb->head;
+ if (skb_cow_head(nskb, doffset + headroom)) {
+ kfree_skb(nskb);
+ goto err;
+ }
+
+ nskb->truesize += skb_end_pointer(nskb) - nskb->head -
+ hsize;
+ skb_release_head_state(nskb);
+ __skb_push(nskb, doffset);
+ } else {
+ nskb = alloc_skb(hsize + doffset + headroom,
+ GFP_ATOMIC);
+
+ if (unlikely(!nskb))
+ goto err;
+
+ skb_reserve(nskb, headroom);
+ __skb_put(nskb, doffset);
+ }
if (segs)
tail->next = nskb;
@@ -2330,13 +2500,15 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
__copy_skb_header(nskb, skb);
nskb->mac_len = skb->mac_len;
- skb_reserve(nskb, headroom);
skb_reset_mac_header(nskb);
skb_set_network_header(nskb, skb->mac_len);
nskb->transport_header = (nskb->network_header +
skb_network_header_len(skb));
- skb_copy_from_linear_data(skb, skb_put(nskb, doffset),
- doffset);
+ skb_copy_from_linear_data(skb, nskb->data, doffset);
+
+ if (pos >= offset + len)
+ continue;
+
if (!sg) {
nskb->ip_summed = CHECKSUM_NONE;
nskb->csum = skb_copy_and_csum_bits(skb, offset,
@@ -2346,14 +2518,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
}
frag = skb_shinfo(nskb)->frags;
- k = 0;
skb_copy_from_linear_data_offset(skb, offset,
skb_put(nskb, hsize), hsize);
- while (pos < offset + len) {
- BUG_ON(i >= nfrags);
-
+ while (pos < offset + len && i < nfrags) {
*frag = skb_shinfo(skb)->frags[i];
get_page(frag->page);
size = frag->size;
@@ -2363,20 +2532,39 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
frag->size -= offset - pos;
}
- k++;
+ skb_shinfo(nskb)->nr_frags++;
if (pos + size <= offset + len) {
i++;
pos += size;
} else {
frag->size -= pos + size - (offset + len);
- break;
+ goto skip_fraglist;
}
frag++;
}
- skb_shinfo(nskb)->nr_frags = k;
+ if (pos < offset + len) {
+ struct sk_buff *fskb2 = fskb;
+
+ BUG_ON(pos + fskb->len != offset + len);
+
+ pos += fskb->len;
+ fskb = fskb->next;
+
+ if (fskb2->next) {
+ fskb2 = skb_clone(fskb2, GFP_ATOMIC);
+ if (!fskb2)
+ goto err;
+ } else
+ skb_get(fskb2);
+
+ BUG_ON(skb_shinfo(nskb)->frag_list);
+ skb_shinfo(nskb)->frag_list = fskb2;
+ }
+
+skip_fraglist:
nskb->data_len = len - hsize;
nskb->len += nskb->data_len;
nskb->truesize += nskb->data_len;
@@ -2394,6 +2582,65 @@ err:
EXPORT_SYMBOL_GPL(skb_segment);
+int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+ struct sk_buff *p = *head;
+ struct sk_buff *nskb;
+ unsigned int headroom;
+ unsigned int hlen = p->data - skb_mac_header(p);
+
+ if (hlen + p->len + skb->len >= 65536)
+ return -E2BIG;
+
+ if (skb_shinfo(p)->frag_list)
+ goto merge;
+
+ headroom = skb_headroom(p);
+ nskb = netdev_alloc_skb(p->dev, headroom);
+ if (unlikely(!nskb))
+ return -ENOMEM;
+
+ __copy_skb_header(nskb, p);
+ nskb->mac_len = p->mac_len;
+
+ skb_reserve(nskb, headroom);
+
+ skb_set_mac_header(nskb, -hlen);
+ skb_set_network_header(nskb, skb_network_offset(p));
+ skb_set_transport_header(nskb, skb_transport_offset(p));
+
+ memcpy(skb_mac_header(nskb), skb_mac_header(p), hlen);
+
+ *NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p);
+ skb_shinfo(nskb)->frag_list = p;
+ skb_header_release(p);
+ nskb->prev = p;
+
+ nskb->data_len += p->len;
+ nskb->truesize += p->len;
+ nskb->len += p->len;
+
+ *head = nskb;
+ nskb->next = p->next;
+ p->next = NULL;
+
+ p = nskb;
+
+merge:
+ NAPI_GRO_CB(p)->count++;
+ p->prev->next = skb;
+ p->prev = skb;
+ skb_header_release(skb);
+
+ p->data_len += skb->len;
+ p->truesize += skb->len;
+ p->len += skb->len;
+
+ NAPI_GRO_CB(skb)->same_flow = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(skb_gro_receive);
+
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
diff --git a/net/core/sock.c b/net/core/sock.c
index edf7220889a4..f3a0d08cbb48 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1071,7 +1071,7 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
newsk->sk_sleep = NULL;
if (newsk->sk_prot->sockets_allocated)
- atomic_inc(newsk->sk_prot->sockets_allocated);
+ percpu_counter_inc(newsk->sk_prot->sockets_allocated);
}
out:
return newsk;
@@ -1463,8 +1463,12 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind)
}
if (prot->memory_pressure) {
- if (!*prot->memory_pressure ||
- prot->sysctl_mem[2] > atomic_read(prot->sockets_allocated) *
+ int alloc;
+
+ if (!*prot->memory_pressure)
+ return 1;
+ alloc = percpu_counter_read_positive(prot->sockets_allocated);
+ if (prot->sysctl_mem[2] > alloc *
sk_mem_pages(sk->sk_wmem_queued +
atomic_read(&sk->sk_rmem_alloc) +
sk->sk_forward_alloc))
@@ -2037,7 +2041,8 @@ int proto_register(struct proto *prot, int alloc_slab)
{
if (alloc_slab) {
prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ SLAB_HWCACHE_ALIGN | prot->slab_flags,
+ NULL);
if (prot->slab == NULL) {
printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
@@ -2076,7 +2081,9 @@ int proto_register(struct proto *prot, int alloc_slab)
prot->twsk_prot->twsk_slab =
kmem_cache_create(prot->twsk_prot->twsk_slab_name,
prot->twsk_prot->twsk_obj_size,
- 0, SLAB_HWCACHE_ALIGN,
+ 0,
+ SLAB_HWCACHE_ALIGN |
+ prot->slab_flags,
NULL);
if (prot->twsk_prot->twsk_slab == NULL)
goto out_free_timewait_sock_slab_name;
@@ -2164,7 +2171,7 @@ static void proto_seq_printf(struct seq_file *seq, struct proto *proto)
"%2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c %2c\n",
proto->name,
proto->obj_size,
- proto->sockets_allocated != NULL ? atomic_read(proto->sockets_allocated) : -1,
+ sock_prot_inuse_get(seq_file_net(seq), proto),
proto->memory_allocated != NULL ? atomic_read(proto->memory_allocated) : -1,
proto->memory_pressure != NULL ? *proto->memory_pressure ? "yes" : "no" : "NI",
proto->max_header,
@@ -2218,7 +2225,8 @@ static const struct seq_operations proto_seq_ops = {
static int proto_seq_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &proto_seq_ops);
+ return seq_open_net(inode, file, &proto_seq_ops,
+ sizeof(struct seq_net_private));
}
static const struct file_operations proto_seq_fops = {
@@ -2226,13 +2234,31 @@ static const struct file_operations proto_seq_fops = {
.open = proto_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
+};
+
+static __net_init int proto_init_net(struct net *net)
+{
+ if (!proc_net_fops_create(net, "protocols", S_IRUGO, &proto_seq_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static __net_exit void proto_exit_net(struct net *net)
+{
+ proc_net_remove(net, "protocols");
+}
+
+
+static __net_initdata struct pernet_operations proto_net_ops = {
+ .init = proto_init_net,
+ .exit = proto_exit_net,
};
static int __init proto_init(void)
{
- /* register /proc/net/protocols */
- return proc_net_fops_create(&init_net, "protocols", S_IRUGO, &proto_seq_fops) == NULL ? -ENOBUFS : 0;
+ return register_pernet_subsys(&proto_net_ops);
}
subsys_initcall(proto_init);
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index f686467ff12b..83d3398559ea 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -12,7 +12,6 @@
#include <linux/netdevice.h>
#include <linux/init.h>
#include <net/sock.h>
-#include <net/xfrm.h>
static struct ctl_table net_core_table[] = {
#ifdef CONFIG_NET
@@ -22,7 +21,7 @@ static struct ctl_table net_core_table[] = {
.data = &sysctl_wmem_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_RMEM_MAX,
@@ -30,7 +29,7 @@ static struct ctl_table net_core_table[] = {
.data = &sysctl_rmem_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_WMEM_DEFAULT,
@@ -38,7 +37,7 @@ static struct ctl_table net_core_table[] = {
.data = &sysctl_wmem_default,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_RMEM_DEFAULT,
@@ -46,7 +45,7 @@ static struct ctl_table net_core_table[] = {
.data = &sysctl_rmem_default,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_DEV_WEIGHT,
@@ -54,7 +53,7 @@ static struct ctl_table net_core_table[] = {
.data = &weight_p,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_MAX_BACKLOG,
@@ -62,7 +61,7 @@ static struct ctl_table net_core_table[] = {
.data = &netdev_max_backlog,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_MSG_COST,
@@ -70,8 +69,8 @@ static struct ctl_table net_core_table[] = {
.data = &net_ratelimit_state.interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_CORE_MSG_BURST,
@@ -79,7 +78,7 @@ static struct ctl_table net_core_table[] = {
.data = &net_ratelimit_state.burst,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_CORE_OPTMEM_MAX,
@@ -87,42 +86,8 @@ static struct ctl_table net_core_table[] = {
.data = &sysctl_optmem_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
-#ifdef CONFIG_XFRM
- {
- .ctl_name = NET_CORE_AEVENT_ETIME,
- .procname = "xfrm_aevent_etime",
- .data = &sysctl_xfrm_aevent_etime,
- .maxlen = sizeof(u32),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
- {
- .ctl_name = NET_CORE_AEVENT_RSEQTH,
- .procname = "xfrm_aevent_rseqth",
- .data = &sysctl_xfrm_aevent_rseqth,
- .maxlen = sizeof(u32),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
- {
- .ctl_name = CTL_UNNUMBERED,
- .procname = "xfrm_larval_drop",
- .data = &sysctl_xfrm_larval_drop,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
- {
- .ctl_name = CTL_UNNUMBERED,
- .procname = "xfrm_acq_expires",
- .data = &sysctl_xfrm_acq_expires,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
-#endif /* CONFIG_XFRM */
#endif /* CONFIG_NET */
{
.ctl_name = NET_CORE_BUDGET,
@@ -130,7 +95,7 @@ static struct ctl_table net_core_table[] = {
.data = &netdev_budget,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_CORE_WARNINGS,
@@ -138,7 +103,7 @@ static struct ctl_table net_core_table[] = {
.data = &net_msg_warn,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
@@ -150,12 +115,12 @@ static struct ctl_table netns_core_table[] = {
.data = &init_net.core.sysctl_somaxconn,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
-static __net_initdata struct ctl_path net_core_path[] = {
+__net_initdata struct ctl_path net_core_path[] = {
{ .procname = "net", .ctl_name = CTL_NET, },
{ .procname = "core", .ctl_name = NET_CORE, },
{ },
@@ -207,8 +172,11 @@ static __net_initdata struct pernet_operations sysctl_core_ops = {
static __init int sysctl_core_init(void)
{
+ static struct ctl_table empty[1];
+
+ register_sysctl_paths(net_core_path, empty);
register_net_sysctl_rotable(net_core_path, net_core_table);
return register_pernet_subsys(&sysctl_core_ops);
}
-__initcall(sysctl_core_init);
+fs_initcall(sysctl_core_init);
diff --git a/net/dcb/Kconfig b/net/dcb/Kconfig
new file mode 100644
index 000000000000..4066d59c8de5
--- /dev/null
+++ b/net/dcb/Kconfig
@@ -0,0 +1,22 @@
+config DCB
+ bool "Data Center Bridging support"
+ default n
+ ---help---
+ This enables support for configuring Data Center Bridging (DCB)
+ features on DCB capable Ethernet adapters via rtnetlink. Say 'Y'
+ if you have a DCB capable Ethernet adapter which supports this
+ interface and you are connected to a DCB capable switch.
+
+ DCB is a collection of Ethernet enhancements which allow DCB capable
+ NICs and switches to support network traffic with differing
+ requirements (highly reliable, no drops vs. best effort vs. low
+ latency) to co-exist on Ethernet.
+
+ DCB features include:
+ Enhanced Transmission Selection (aka Priority Grouping) - provides a
+ framework for assigning bandwidth guarantees to traffic classes.
+ Priority-based Flow Control (PFC) - a MAC control pause frame which
+ works at the granularity of the 802.1p priority instead of the
+ link (802.3x).
+
+ If unsure, say N.
diff --git a/net/dcb/Makefile b/net/dcb/Makefile
new file mode 100644
index 000000000000..9930f4cde818
--- /dev/null
+++ b/net/dcb/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DCB) += dcbnl.o
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
new file mode 100644
index 000000000000..5dbfe5fdc0d6
--- /dev/null
+++ b/net/dcb/dcbnl.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (c) 2008, 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., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Author: Lucy Liu <lucy.liu@intel.com>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/netlink.h>
+#include <net/rtnetlink.h>
+#include <linux/dcbnl.h>
+#include <linux/rtnetlink.h>
+#include <net/sock.h>
+
+/**
+ * Data Center Bridging (DCB) is a collection of Ethernet enhancements
+ * intended to allow network traffic with differing requirements
+ * (highly reliable, no drops vs. best effort vs. low latency) to operate
+ * and co-exist on Ethernet. Current DCB features are:
+ *
+ * Enhanced Transmission Selection (aka Priority Grouping [PG]) - provides a
+ * framework for assigning bandwidth guarantees to traffic classes.
+ *
+ * Priority-based Flow Control (PFC) - provides a flow control mechanism which
+ * can work independently for each 802.1p priority.
+ *
+ * Congestion Notification - provides a mechanism for end-to-end congestion
+ * control for protocols which do not have built-in congestion management.
+ *
+ * More information about the emerging standards for these Ethernet features
+ * can be found at: http://www.ieee802.org/1/pages/dcbridges.html
+ *
+ * This file implements an rtnetlink interface to allow configuration of DCB
+ * features for capable devices.
+ */
+
+MODULE_AUTHOR("Lucy Liu, <lucy.liu@intel.com>");
+MODULE_DESCRIPTION("Data Center Bridging netlink interface");
+MODULE_LICENSE("GPL");
+
+/**************** DCB attribute policies *************************************/
+
+/* DCB netlink attributes policy */
+static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
+ [DCB_ATTR_IFNAME] = {.type = NLA_NUL_STRING, .len = IFNAMSIZ - 1},
+ [DCB_ATTR_STATE] = {.type = NLA_U8},
+ [DCB_ATTR_PFC_CFG] = {.type = NLA_NESTED},
+ [DCB_ATTR_PG_CFG] = {.type = NLA_NESTED},
+ [DCB_ATTR_SET_ALL] = {.type = NLA_U8},
+ [DCB_ATTR_PERM_HWADDR] = {.type = NLA_FLAG},
+ [DCB_ATTR_CAP] = {.type = NLA_NESTED},
+ [DCB_ATTR_PFC_STATE] = {.type = NLA_U8},
+ [DCB_ATTR_BCN] = {.type = NLA_NESTED},
+};
+
+/* DCB priority flow control to User Priority nested attributes */
+static struct nla_policy dcbnl_pfc_up_nest[DCB_PFC_UP_ATTR_MAX + 1] = {
+ [DCB_PFC_UP_ATTR_0] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_1] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_2] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_3] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_4] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_5] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_6] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_7] = {.type = NLA_U8},
+ [DCB_PFC_UP_ATTR_ALL] = {.type = NLA_FLAG},
+};
+
+/* DCB priority grouping nested attributes */
+static struct nla_policy dcbnl_pg_nest[DCB_PG_ATTR_MAX + 1] = {
+ [DCB_PG_ATTR_TC_0] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_1] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_2] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_3] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_4] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_5] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_6] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_7] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_TC_ALL] = {.type = NLA_NESTED},
+ [DCB_PG_ATTR_BW_ID_0] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_1] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_2] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_3] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_4] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_5] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_6] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_7] = {.type = NLA_U8},
+ [DCB_PG_ATTR_BW_ID_ALL] = {.type = NLA_FLAG},
+};
+
+/* DCB traffic class nested attributes. */
+static struct nla_policy dcbnl_tc_param_nest[DCB_TC_ATTR_PARAM_MAX + 1] = {
+ [DCB_TC_ATTR_PARAM_PGID] = {.type = NLA_U8},
+ [DCB_TC_ATTR_PARAM_UP_MAPPING] = {.type = NLA_U8},
+ [DCB_TC_ATTR_PARAM_STRICT_PRIO] = {.type = NLA_U8},
+ [DCB_TC_ATTR_PARAM_BW_PCT] = {.type = NLA_U8},
+ [DCB_TC_ATTR_PARAM_ALL] = {.type = NLA_FLAG},
+};
+
+/* DCB capabilities nested attributes. */
+static struct nla_policy dcbnl_cap_nest[DCB_CAP_ATTR_MAX + 1] = {
+ [DCB_CAP_ATTR_ALL] = {.type = NLA_FLAG},
+ [DCB_CAP_ATTR_PG] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_PFC] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_UP2TC] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_PG_TCS] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_PFC_TCS] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_GSP] = {.type = NLA_U8},
+ [DCB_CAP_ATTR_BCN] = {.type = NLA_U8},
+};
+
+/* DCB capabilities nested attributes. */
+static struct nla_policy dcbnl_numtcs_nest[DCB_NUMTCS_ATTR_MAX + 1] = {
+ [DCB_NUMTCS_ATTR_ALL] = {.type = NLA_FLAG},
+ [DCB_NUMTCS_ATTR_PG] = {.type = NLA_U8},
+ [DCB_NUMTCS_ATTR_PFC] = {.type = NLA_U8},
+};
+
+/* DCB BCN nested attributes. */
+static struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
+ [DCB_BCN_ATTR_RP_0] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_1] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_2] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_3] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_4] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_5] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_6] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_7] = {.type = NLA_U8},
+ [DCB_BCN_ATTR_RP_ALL] = {.type = NLA_FLAG},
+ [DCB_BCN_ATTR_BCNA_0] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_BCNA_1] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_ALPHA] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_BETA] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_GD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_GI] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_TMAX] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_TD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RMIN] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_W] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RD] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RU] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_WRTT] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_RI] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_C] = {.type = NLA_U32},
+ [DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG},
+};
+
+/* standard netlink reply call */
+static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
+ u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct dcbmsg *dcb;
+ struct nlmsghdr *nlh;
+ int ret = -EINVAL;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ return ret;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, event, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = cmd;
+ dcb->dcb_pad = 0;
+
+ ret = nla_put_u8(dcbnl_skb, attr, value);
+ if (ret)
+ goto err;
+
+ /* end the message, assign the nlmsg_len. */
+ nlmsg_end(dcbnl_skb, nlh);
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+ return ret;
+}
+
+static int dcbnl_getstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+
+ /* if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->getstate) */
+ if (!netdev->dcbnl_ops->getstate)
+ return ret;
+
+ ret = dcbnl_reply(netdev->dcbnl_ops->getstate(netdev), RTM_GETDCB,
+ DCB_CMD_GSTATE, DCB_ATTR_STATE, pid, seq, flags);
+
+ return ret;
+}
+
+static int dcbnl_getpfccfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1], *nest;
+ u8 value;
+ int ret = -EINVAL;
+ int i;
+ int getall = 0;
+
+ if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->getpfccfg)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
+ tb[DCB_ATTR_PFC_CFG],
+ dcbnl_pfc_up_nest);
+ if (ret)
+ goto err_out;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto err_out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_PFC_GCFG;
+
+ nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PFC_CFG);
+ if (!nest)
+ goto err;
+
+ if (data[DCB_PFC_UP_ATTR_ALL])
+ getall = 1;
+
+ for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
+ if (!getall && !data[i])
+ continue;
+
+ netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0,
+ &value);
+ ret = nla_put_u8(dcbnl_skb, i, value);
+
+ if (ret) {
+ nla_nest_cancel(dcbnl_skb, nest);
+ goto err;
+ }
+ }
+ nla_nest_end(dcbnl_skb, nest);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ return -EINVAL;
+}
+
+static int dcbnl_getperm_hwaddr(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ u8 perm_addr[MAX_ADDR_LEN];
+ int ret = -EINVAL;
+
+ if (!netdev->dcbnl_ops->getpermhwaddr)
+ return ret;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto err_out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_GPERM_HWADDR;
+
+ netdev->dcbnl_ops->getpermhwaddr(netdev, perm_addr);
+
+ ret = nla_put(dcbnl_skb, DCB_ATTR_PERM_HWADDR, sizeof(perm_addr),
+ perm_addr);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ return -EINVAL;
+}
+
+static int dcbnl_getcap(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *data[DCB_CAP_ATTR_MAX + 1], *nest;
+ u8 value;
+ int ret = -EINVAL;
+ int i;
+ int getall = 0;
+
+ if (!tb[DCB_ATTR_CAP] || !netdev->dcbnl_ops->getcap)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP],
+ dcbnl_cap_nest);
+ if (ret)
+ goto err_out;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto err_out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_GCAP;
+
+ nest = nla_nest_start(dcbnl_skb, DCB_ATTR_CAP);
+ if (!nest)
+ goto err;
+
+ if (data[DCB_CAP_ATTR_ALL])
+ getall = 1;
+
+ for (i = DCB_CAP_ATTR_ALL+1; i <= DCB_CAP_ATTR_MAX; i++) {
+ if (!getall && !data[i])
+ continue;
+
+ if (!netdev->dcbnl_ops->getcap(netdev, i, &value)) {
+ ret = nla_put_u8(dcbnl_skb, i, value);
+
+ if (ret) {
+ nla_nest_cancel(dcbnl_skb, nest);
+ goto err;
+ }
+ }
+ }
+ nla_nest_end(dcbnl_skb, nest);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ return -EINVAL;
+}
+
+static int dcbnl_getnumtcs(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1], *nest;
+ u8 value;
+ int ret = -EINVAL;
+ int i;
+ int getall = 0;
+
+ if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->getnumtcs)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
+ dcbnl_numtcs_nest);
+ if (ret) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_GNUMTCS;
+
+ nest = nla_nest_start(dcbnl_skb, DCB_ATTR_NUMTCS);
+ if (!nest) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (data[DCB_NUMTCS_ATTR_ALL])
+ getall = 1;
+
+ for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
+ if (!getall && !data[i])
+ continue;
+
+ ret = netdev->dcbnl_ops->getnumtcs(netdev, i, &value);
+ if (!ret) {
+ ret = nla_put_u8(dcbnl_skb, i, value);
+
+ if (ret) {
+ nla_nest_cancel(dcbnl_skb, nest);
+ ret = -EINVAL;
+ goto err;
+ }
+ } else {
+ goto err;
+ }
+ }
+ nla_nest_end(dcbnl_skb, nest);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ return ret;
+}
+
+static int dcbnl_setnumtcs(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct nlattr *data[DCB_NUMTCS_ATTR_MAX + 1];
+ int ret = -EINVAL;
+ u8 value;
+ int i;
+
+ if (!tb[DCB_ATTR_NUMTCS] || !netdev->dcbnl_ops->setnumtcs)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS],
+ dcbnl_numtcs_nest);
+
+ if (ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = DCB_NUMTCS_ATTR_ALL+1; i <= DCB_NUMTCS_ATTR_MAX; i++) {
+ if (data[i] == NULL)
+ continue;
+
+ value = nla_get_u8(data[i]);
+
+ ret = netdev->dcbnl_ops->setnumtcs(netdev, i, value);
+
+ if (ret)
+ goto operr;
+ }
+
+operr:
+ ret = dcbnl_reply(!!ret, RTM_SETDCB, DCB_CMD_SNUMTCS,
+ DCB_ATTR_NUMTCS, pid, seq, flags);
+
+err:
+ return ret;
+}
+
+static int dcbnl_getpfcstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+
+ if (!netdev->dcbnl_ops->getpfcstate)
+ return ret;
+
+ ret = dcbnl_reply(netdev->dcbnl_ops->getpfcstate(netdev), RTM_GETDCB,
+ DCB_CMD_PFC_GSTATE, DCB_ATTR_PFC_STATE,
+ pid, seq, flags);
+
+ return ret;
+}
+
+static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+ u8 value;
+
+ if (!tb[DCB_ATTR_PFC_STATE] || !netdev->dcbnl_ops->setpfcstate)
+ return ret;
+
+ value = nla_get_u8(tb[DCB_ATTR_PFC_STATE]);
+
+ netdev->dcbnl_ops->setpfcstate(netdev, value);
+
+ ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SSTATE, DCB_ATTR_PFC_STATE,
+ pid, seq, flags);
+
+ return ret;
+}
+
+static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags, int dir)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *pg_nest, *param_nest, *data;
+ struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
+ struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
+ u8 prio, pgid, tc_pct, up_map;
+ int ret = -EINVAL;
+ int getall = 0;
+ int i;
+
+ if (!tb[DCB_ATTR_PG_CFG] ||
+ !netdev->dcbnl_ops->getpgtccfgtx ||
+ !netdev->dcbnl_ops->getpgtccfgrx ||
+ !netdev->dcbnl_ops->getpgbwgcfgtx ||
+ !netdev->dcbnl_ops->getpgbwgcfgrx)
+ return ret;
+
+ ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
+ tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
+
+ if (ret)
+ goto err_out;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto err_out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = (dir) ? DCB_CMD_PGRX_GCFG : DCB_CMD_PGTX_GCFG;
+
+ pg_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_PG_CFG);
+ if (!pg_nest)
+ goto err;
+
+ if (pg_tb[DCB_PG_ATTR_TC_ALL])
+ getall = 1;
+
+ for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
+ if (!getall && !pg_tb[i])
+ continue;
+
+ if (pg_tb[DCB_PG_ATTR_TC_ALL])
+ data = pg_tb[DCB_PG_ATTR_TC_ALL];
+ else
+ data = pg_tb[i];
+ ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
+ data, dcbnl_tc_param_nest);
+ if (ret)
+ goto err_pg;
+
+ param_nest = nla_nest_start(dcbnl_skb, i);
+ if (!param_nest)
+ goto err_pg;
+
+ pgid = DCB_ATTR_VALUE_UNDEFINED;
+ prio = DCB_ATTR_VALUE_UNDEFINED;
+ tc_pct = DCB_ATTR_VALUE_UNDEFINED;
+ up_map = DCB_ATTR_VALUE_UNDEFINED;
+
+ if (dir) {
+ /* Rx */
+ netdev->dcbnl_ops->getpgtccfgrx(netdev,
+ i - DCB_PG_ATTR_TC_0, &prio,
+ &pgid, &tc_pct, &up_map);
+ } else {
+ /* Tx */
+ netdev->dcbnl_ops->getpgtccfgtx(netdev,
+ i - DCB_PG_ATTR_TC_0, &prio,
+ &pgid, &tc_pct, &up_map);
+ }
+
+ if (param_tb[DCB_TC_ATTR_PARAM_PGID] ||
+ param_tb[DCB_TC_ATTR_PARAM_ALL]) {
+ ret = nla_put_u8(dcbnl_skb,
+ DCB_TC_ATTR_PARAM_PGID, pgid);
+ if (ret)
+ goto err_param;
+ }
+ if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING] ||
+ param_tb[DCB_TC_ATTR_PARAM_ALL]) {
+ ret = nla_put_u8(dcbnl_skb,
+ DCB_TC_ATTR_PARAM_UP_MAPPING, up_map);
+ if (ret)
+ goto err_param;
+ }
+ if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO] ||
+ param_tb[DCB_TC_ATTR_PARAM_ALL]) {
+ ret = nla_put_u8(dcbnl_skb,
+ DCB_TC_ATTR_PARAM_STRICT_PRIO, prio);
+ if (ret)
+ goto err_param;
+ }
+ if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT] ||
+ param_tb[DCB_TC_ATTR_PARAM_ALL]) {
+ ret = nla_put_u8(dcbnl_skb, DCB_TC_ATTR_PARAM_BW_PCT,
+ tc_pct);
+ if (ret)
+ goto err_param;
+ }
+ nla_nest_end(dcbnl_skb, param_nest);
+ }
+
+ if (pg_tb[DCB_PG_ATTR_BW_ID_ALL])
+ getall = 1;
+ else
+ getall = 0;
+
+ for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
+ if (!getall && !pg_tb[i])
+ continue;
+
+ tc_pct = DCB_ATTR_VALUE_UNDEFINED;
+
+ if (dir) {
+ /* Rx */
+ netdev->dcbnl_ops->getpgbwgcfgrx(netdev,
+ i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
+ } else {
+ /* Tx */
+ netdev->dcbnl_ops->getpgbwgcfgtx(netdev,
+ i - DCB_PG_ATTR_BW_ID_0, &tc_pct);
+ }
+ ret = nla_put_u8(dcbnl_skb, i, tc_pct);
+
+ if (ret)
+ goto err_pg;
+ }
+
+ nla_nest_end(dcbnl_skb, pg_nest);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err_param:
+ nla_nest_cancel(dcbnl_skb, param_nest);
+err_pg:
+ nla_nest_cancel(dcbnl_skb, pg_nest);
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ ret = -EINVAL;
+ return ret;
+}
+
+static int dcbnl_pgtx_getcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 0);
+}
+
+static int dcbnl_pgrx_getcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ return __dcbnl_pg_getcfg(netdev, tb, pid, seq, flags, 1);
+}
+
+static int dcbnl_setstate(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+ u8 value;
+
+ if (!tb[DCB_ATTR_STATE] || !netdev->dcbnl_ops->setstate)
+ return ret;
+
+ value = nla_get_u8(tb[DCB_ATTR_STATE]);
+
+ ret = dcbnl_reply(netdev->dcbnl_ops->setstate(netdev, value),
+ RTM_SETDCB, DCB_CMD_SSTATE, DCB_ATTR_STATE,
+ pid, seq, flags);
+
+ return ret;
+}
+
+static int dcbnl_setpfccfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct nlattr *data[DCB_PFC_UP_ATTR_MAX + 1];
+ int i;
+ int ret = -EINVAL;
+ u8 value;
+
+ if (!tb[DCB_ATTR_PFC_CFG] || !netdev->dcbnl_ops->setpfccfg)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX,
+ tb[DCB_ATTR_PFC_CFG],
+ dcbnl_pfc_up_nest);
+ if (ret)
+ goto err;
+
+ for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) {
+ if (data[i] == NULL)
+ continue;
+ value = nla_get_u8(data[i]);
+ netdev->dcbnl_ops->setpfccfg(netdev,
+ data[i]->nla_type - DCB_PFC_UP_ATTR_0, value);
+ }
+
+ ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_PFC_SCFG, DCB_ATTR_PFC_CFG,
+ pid, seq, flags);
+err:
+ return ret;
+}
+
+static int dcbnl_setall(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+
+ if (!tb[DCB_ATTR_SET_ALL] || !netdev->dcbnl_ops->setall)
+ return ret;
+
+ ret = dcbnl_reply(netdev->dcbnl_ops->setall(netdev), RTM_SETDCB,
+ DCB_CMD_SET_ALL, DCB_ATTR_SET_ALL, pid, seq, flags);
+
+ return ret;
+}
+
+static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags, int dir)
+{
+ struct nlattr *pg_tb[DCB_PG_ATTR_MAX + 1];
+ struct nlattr *param_tb[DCB_TC_ATTR_PARAM_MAX + 1];
+ int ret = -EINVAL;
+ int i;
+ u8 pgid;
+ u8 up_map;
+ u8 prio;
+ u8 tc_pct;
+
+ if (!tb[DCB_ATTR_PG_CFG] ||
+ !netdev->dcbnl_ops->setpgtccfgtx ||
+ !netdev->dcbnl_ops->setpgtccfgrx ||
+ !netdev->dcbnl_ops->setpgbwgcfgtx ||
+ !netdev->dcbnl_ops->setpgbwgcfgrx)
+ return ret;
+
+ ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX,
+ tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest);
+ if (ret)
+ goto err;
+
+ for (i = DCB_PG_ATTR_TC_0; i <= DCB_PG_ATTR_TC_7; i++) {
+ if (!pg_tb[i])
+ continue;
+
+ ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX,
+ pg_tb[i], dcbnl_tc_param_nest);
+ if (ret)
+ goto err;
+
+ pgid = DCB_ATTR_VALUE_UNDEFINED;
+ prio = DCB_ATTR_VALUE_UNDEFINED;
+ tc_pct = DCB_ATTR_VALUE_UNDEFINED;
+ up_map = DCB_ATTR_VALUE_UNDEFINED;
+
+ if (param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO])
+ prio =
+ nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_STRICT_PRIO]);
+
+ if (param_tb[DCB_TC_ATTR_PARAM_PGID])
+ pgid = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_PGID]);
+
+ if (param_tb[DCB_TC_ATTR_PARAM_BW_PCT])
+ tc_pct = nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_BW_PCT]);
+
+ if (param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING])
+ up_map =
+ nla_get_u8(param_tb[DCB_TC_ATTR_PARAM_UP_MAPPING]);
+
+ /* dir: Tx = 0, Rx = 1 */
+ if (dir) {
+ /* Rx */
+ netdev->dcbnl_ops->setpgtccfgrx(netdev,
+ i - DCB_PG_ATTR_TC_0,
+ prio, pgid, tc_pct, up_map);
+ } else {
+ /* Tx */
+ netdev->dcbnl_ops->setpgtccfgtx(netdev,
+ i - DCB_PG_ATTR_TC_0,
+ prio, pgid, tc_pct, up_map);
+ }
+ }
+
+ for (i = DCB_PG_ATTR_BW_ID_0; i <= DCB_PG_ATTR_BW_ID_7; i++) {
+ if (!pg_tb[i])
+ continue;
+
+ tc_pct = nla_get_u8(pg_tb[i]);
+
+ /* dir: Tx = 0, Rx = 1 */
+ if (dir) {
+ /* Rx */
+ netdev->dcbnl_ops->setpgbwgcfgrx(netdev,
+ i - DCB_PG_ATTR_BW_ID_0, tc_pct);
+ } else {
+ /* Tx */
+ netdev->dcbnl_ops->setpgbwgcfgtx(netdev,
+ i - DCB_PG_ATTR_BW_ID_0, tc_pct);
+ }
+ }
+
+ ret = dcbnl_reply(0, RTM_SETDCB,
+ (dir ? DCB_CMD_PGRX_SCFG : DCB_CMD_PGTX_SCFG),
+ DCB_ATTR_PG_CFG, pid, seq, flags);
+
+err:
+ return ret;
+}
+
+static int dcbnl_pgtx_setcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 0);
+}
+
+static int dcbnl_pgrx_setcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ return __dcbnl_pg_setcfg(netdev, tb, pid, seq, flags, 1);
+}
+
+static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *bcn_nest;
+ struct nlattr *bcn_tb[DCB_BCN_ATTR_MAX + 1];
+ u8 value_byte;
+ u32 value_integer;
+ int ret = -EINVAL;
+ bool getall = false;
+ int i;
+
+ if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->getbcnrp ||
+ !netdev->dcbnl_ops->getbcncfg)
+ return ret;
+
+ ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX,
+ tb[DCB_ATTR_BCN], dcbnl_bcn_nest);
+
+ if (ret)
+ goto err_out;
+
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto err_out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_BCN_GCFG;
+
+ bcn_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_BCN);
+ if (!bcn_nest)
+ goto err;
+
+ if (bcn_tb[DCB_BCN_ATTR_ALL])
+ getall = true;
+
+ for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
+ if (!getall && !bcn_tb[i])
+ continue;
+
+ netdev->dcbnl_ops->getbcnrp(netdev, i - DCB_BCN_ATTR_RP_0,
+ &value_byte);
+ ret = nla_put_u8(dcbnl_skb, i, value_byte);
+ if (ret)
+ goto err_bcn;
+ }
+
+ for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
+ if (!getall && !bcn_tb[i])
+ continue;
+
+ netdev->dcbnl_ops->getbcncfg(netdev, i,
+ &value_integer);
+ ret = nla_put_u32(dcbnl_skb, i, value_integer);
+ if (ret)
+ goto err_bcn;
+ }
+
+ nla_nest_end(dcbnl_skb, bcn_nest);
+
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err_bcn:
+ nla_nest_cancel(dcbnl_skb, bcn_nest);
+nlmsg_failure:
+err:
+ kfree(dcbnl_skb);
+err_out:
+ ret = -EINVAL;
+ return ret;
+}
+
+static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct nlattr *data[DCB_BCN_ATTR_MAX + 1];
+ int i;
+ int ret = -EINVAL;
+ u8 value_byte;
+ u32 value_int;
+
+ if (!tb[DCB_ATTR_BCN] || !netdev->dcbnl_ops->setbcncfg
+ || !netdev->dcbnl_ops->setbcnrp)
+ return ret;
+
+ ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX,
+ tb[DCB_ATTR_BCN],
+ dcbnl_pfc_up_nest);
+ if (ret)
+ goto err;
+
+ for (i = DCB_BCN_ATTR_RP_0; i <= DCB_BCN_ATTR_RP_7; i++) {
+ if (data[i] == NULL)
+ continue;
+ value_byte = nla_get_u8(data[i]);
+ netdev->dcbnl_ops->setbcnrp(netdev,
+ data[i]->nla_type - DCB_BCN_ATTR_RP_0, value_byte);
+ }
+
+ for (i = DCB_BCN_ATTR_BCNA_0; i <= DCB_BCN_ATTR_RI; i++) {
+ if (data[i] == NULL)
+ continue;
+ value_int = nla_get_u32(data[i]);
+ netdev->dcbnl_ops->setbcncfg(netdev,
+ i, value_int);
+ }
+
+ ret = dcbnl_reply(0, RTM_SETDCB, DCB_CMD_BCN_SCFG, DCB_ATTR_BCN,
+ pid, seq, flags);
+err:
+ return ret;
+}
+
+static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *netdev;
+ struct dcbmsg *dcb = (struct dcbmsg *)NLMSG_DATA(nlh);
+ struct nlattr *tb[DCB_ATTR_MAX + 1];
+ u32 pid = skb ? NETLINK_CB(skb).pid : 0;
+ int ret = -EINVAL;
+
+ if (net != &init_net)
+ return -EINVAL;
+
+ ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
+ dcbnl_rtnl_policy);
+ if (ret < 0)
+ return ret;
+
+ if (!tb[DCB_ATTR_IFNAME])
+ return -EINVAL;
+
+ netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
+ if (!netdev)
+ return -EINVAL;
+
+ if (!netdev->dcbnl_ops)
+ goto errout;
+
+ switch (dcb->cmd) {
+ case DCB_CMD_GSTATE:
+ ret = dcbnl_getstate(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PFC_GCFG:
+ ret = dcbnl_getpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_GPERM_HWADDR:
+ ret = dcbnl_getperm_hwaddr(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PGTX_GCFG:
+ ret = dcbnl_pgtx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PGRX_GCFG:
+ ret = dcbnl_pgrx_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_BCN_GCFG:
+ ret = dcbnl_bcn_getcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_SSTATE:
+ ret = dcbnl_setstate(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PFC_SCFG:
+ ret = dcbnl_setpfccfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+
+ case DCB_CMD_SET_ALL:
+ ret = dcbnl_setall(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PGTX_SCFG:
+ ret = dcbnl_pgtx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PGRX_SCFG:
+ ret = dcbnl_pgrx_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_GCAP:
+ ret = dcbnl_getcap(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_GNUMTCS:
+ ret = dcbnl_getnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_SNUMTCS:
+ ret = dcbnl_setnumtcs(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PFC_GSTATE:
+ ret = dcbnl_getpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_PFC_SSTATE:
+ ret = dcbnl_setpfcstate(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_BCN_SCFG:
+ ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ default:
+ goto errout;
+ }
+errout:
+ ret = -EINVAL;
+out:
+ dev_put(netdev);
+ return ret;
+}
+
+static int __init dcbnl_init(void)
+{
+ rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL);
+ rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL);
+
+ return 0;
+}
+module_init(dcbnl_init);
+
+static void __exit dcbnl_exit(void)
+{
+ rtnl_unregister(PF_UNSPEC, RTM_GETDCB);
+ rtnl_unregister(PF_UNSPEC, RTM_SETDCB);
+}
+module_exit(dcbnl_exit);
+
+
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index 1e8be246ad15..01e4d39fa232 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -12,7 +12,6 @@
#include "ackvec.h"
#include "dccp.h"
-#include <linux/dccp.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -68,7 +67,7 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
struct dccp_sock *dp = dccp_sk(sk);
struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec;
/* Figure out how many options do we need to represent the ackvec */
- const u16 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_MAX_ACKVEC_OPT_LEN);
+ const u8 nr_opts = DIV_ROUND_UP(av->av_vec_len, DCCP_SINGLE_OPT_MAXLEN);
u16 len = av->av_vec_len + 2 * nr_opts, i;
u32 elapsed_time;
const unsigned char *tail, *from;
@@ -100,8 +99,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
for (i = 0; i < nr_opts; ++i) {
int copylen = len;
- if (len > DCCP_MAX_ACKVEC_OPT_LEN)
- copylen = DCCP_MAX_ACKVEC_OPT_LEN;
+ if (len > DCCP_SINGLE_OPT_MAXLEN)
+ copylen = DCCP_SINGLE_OPT_MAXLEN;
*to++ = DCCPO_ACK_VECTOR_0;
*to++ = copylen + 2;
@@ -432,7 +431,7 @@ found:
int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb,
u64 *ackno, const u8 opt, const u8 *value, const u8 len)
{
- if (len > DCCP_MAX_ACKVEC_OPT_LEN)
+ if (len > DCCP_SINGLE_OPT_MAXLEN)
return -1;
/* dccp_ackvector_print(DCCP_SKB_CB(skb)->dccpd_ack_seq, value, len); */
diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h
index bcb64fb4acef..4ccee030524e 100644
--- a/net/dccp/ackvec.h
+++ b/net/dccp/ackvec.h
@@ -11,15 +11,14 @@
* published by the Free Software Foundation.
*/
+#include <linux/dccp.h>
#include <linux/compiler.h>
#include <linux/ktime.h>
#include <linux/list.h>
#include <linux/types.h>
-/* Read about the ECN nonce to see why it is 253 */
-#define DCCP_MAX_ACKVEC_OPT_LEN 253
/* We can spread an ack vector across multiple options */
-#define DCCP_MAX_ACKVEC_LEN (DCCP_MAX_ACKVEC_OPT_LEN * 2)
+#define DCCP_MAX_ACKVEC_LEN (DCCP_SINGLE_OPT_MAXLEN * 2)
#define DCCP_ACKVEC_STATE_RECEIVED 0
#define DCCP_ACKVEC_STATE_ECN_MARKED (1 << 6)
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c
index 8fe931a3d7a1..bcc643f992ae 100644
--- a/net/dccp/ccid.c
+++ b/net/dccp/ccid.c
@@ -13,6 +13,13 @@
#include "ccid.h"
+static u8 builtin_ccids[] = {
+ DCCPC_CCID2, /* CCID2 is supported by default */
+#if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE)
+ DCCPC_CCID3,
+#endif
+};
+
static struct ccid_operations *ccids[CCID_MAX];
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
static atomic_t ccids_lockct = ATOMIC_INIT(0);
@@ -86,6 +93,47 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
}
}
+/* check that up to @array_len members in @ccid_array are supported */
+bool ccid_support_check(u8 const *ccid_array, u8 array_len)
+{
+ u8 i, j, found;
+
+ for (i = 0, found = 0; i < array_len; i++, found = 0) {
+ for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++)
+ found = (ccid_array[i] == builtin_ccids[j]);
+ if (!found)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array
+ * @ccid_array: pointer to copy into
+ * @array_len: value to return length into
+ * This function allocates memory - caller must see that it is freed after use.
+ */
+int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
+{
+ *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any());
+ if (*ccid_array == NULL)
+ return -ENOBUFS;
+ *array_len = ARRAY_SIZE(builtin_ccids);
+ return 0;
+}
+
+int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ if (len < sizeof(builtin_ccids))
+ return -EINVAL;
+
+ if (put_user(sizeof(builtin_ccids), optlen) ||
+ copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids)))
+ return -EFAULT;
+ return 0;
+}
+
int ccid_register(struct ccid_operations *ccid_ops)
{
int err = -ENOBUFS;
@@ -205,20 +253,6 @@ out_module_put:
EXPORT_SYMBOL_GPL(ccid_new);
-struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp)
-{
- return ccid_new(id, sk, 1, gfp);
-}
-
-EXPORT_SYMBOL_GPL(ccid_hc_rx_new);
-
-struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp)
-{
- return ccid_new(id, sk, 0, gfp);
-}
-
-EXPORT_SYMBOL_GPL(ccid_hc_tx_new);
-
static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx)
{
struct ccid_operations *ccid_ops;
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index fdeae7b57319..18f69423a708 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -103,13 +103,31 @@ static inline void *ccid_priv(const struct ccid *ccid)
return (void *)ccid->ccid_priv;
}
+extern bool ccid_support_check(u8 const *ccid_array, u8 array_len);
+extern int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len);
+extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
+ char __user *, int __user *);
+
extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx,
gfp_t gfp);
-extern struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk,
- gfp_t gfp);
-extern struct ccid *ccid_hc_tx_new(unsigned char id, struct sock *sk,
- gfp_t gfp);
+static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp)
+{
+ struct ccid *ccid = dp->dccps_hc_rx_ccid;
+
+ if (ccid == NULL || ccid->ccid_ops == NULL)
+ return -1;
+ return ccid->ccid_ops->ccid_id;
+}
+
+static inline int ccid_get_current_tx_ccid(struct dccp_sock *dp)
+{
+ struct ccid *ccid = dp->dccps_hc_tx_ccid;
+
+ if (ccid == NULL || ccid->ccid_ops == NULL)
+ return -1;
+ return ccid->ccid_ops->ccid_id;
+}
extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk);
extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk);
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 9a430734530c..c9ea19a4d85e 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -25,7 +25,7 @@
/*
* This implementation should follow RFC 4341
*/
-
+#include "../feat.h"
#include "../ccid.h"
#include "../dccp.h"
#include "ccid2.h"
@@ -147,8 +147,8 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
DCCP_WARN("Limiting Ack Ratio (%u) to %u\n", val, max_ratio);
val = max_ratio;
}
- if (val > 0xFFFF) /* RFC 4340, 11.3 */
- val = 0xFFFF;
+ if (val > DCCPF_ACK_RATIO_MAX)
+ val = DCCPF_ACK_RATIO_MAX;
if (val == dp->dccps_l_ack_ratio)
return;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index b4bc6e095a0e..0bc4c9a02e19 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -49,7 +49,7 @@ extern int dccp_debug;
extern struct inet_hashinfo dccp_hashinfo;
-extern atomic_t dccp_orphan_count;
+extern struct percpu_counter dccp_orphan_count;
extern void dccp_time_wait(struct sock *sk, int state, int timeo);
@@ -98,9 +98,6 @@ extern int sysctl_dccp_retries2;
extern int sysctl_dccp_feat_sequence_window;
extern int sysctl_dccp_feat_rx_ccid;
extern int sysctl_dccp_feat_tx_ccid;
-extern int sysctl_dccp_feat_ack_ratio;
-extern int sysctl_dccp_feat_send_ack_vector;
-extern int sysctl_dccp_feat_send_ndp_count;
extern int sysctl_dccp_tx_qlen;
extern int sysctl_dccp_sync_ratelimit;
@@ -252,7 +249,8 @@ extern const char *dccp_state_name(const int state);
extern void dccp_set_state(struct sock *sk, const int state);
extern void dccp_done(struct sock *sk);
-extern void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb);
+extern int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp,
+ struct sk_buff const *skb);
extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb);
@@ -435,12 +433,19 @@ static inline int dccp_ack_pending(const struct sock *sk)
const struct dccp_sock *dp = dccp_sk(sk);
return dp->dccps_timestamp_echo != 0 ||
#ifdef CONFIG_IP_DCCP_ACKVEC
- (dccp_msk(sk)->dccpms_send_ack_vector &&
+ (dp->dccps_hc_rx_ackvec != NULL &&
dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) ||
#endif
inet_csk_ack_scheduled(sk);
}
+extern int dccp_feat_finalise_settings(struct dccp_sock *dp);
+extern int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq);
+extern int dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*,
+ struct sk_buff *skb);
+extern int dccp_feat_activate_values(struct sock *sk, struct list_head *fn);
+extern void dccp_feat_list_purge(struct list_head *fn_list);
+
extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb);
extern int dccp_insert_options_rsk(struct dccp_request_sock*, struct sk_buff*);
extern int dccp_insert_option_elapsed_time(struct sock *sk,
diff --git a/net/dccp/diag.c b/net/dccp/diag.c
index d8a3509b26f6..b21f261da75e 100644
--- a/net/dccp/diag.c
+++ b/net/dccp/diag.c
@@ -29,11 +29,14 @@ static void dccp_get_info(struct sock *sk, struct tcp_info *info)
info->tcpi_backoff = icsk->icsk_backoff;
info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
- if (dccp_msk(sk)->dccpms_send_ack_vector)
+ if (dp->dccps_hc_rx_ackvec != NULL)
info->tcpi_options |= TCPI_OPT_SACK;
- ccid_hc_rx_get_info(dp->dccps_hc_rx_ccid, sk, info);
- ccid_hc_tx_get_info(dp->dccps_hc_tx_ccid, sk, info);
+ if (dp->dccps_hc_rx_ccid != NULL)
+ ccid_hc_rx_get_info(dp->dccps_hc_rx_ccid, sk, info);
+
+ if (dp->dccps_hc_tx_ccid != NULL)
+ ccid_hc_tx_get_info(dp->dccps_hc_tx_ccid, sk, info);
}
static void dccp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
@@ -45,7 +48,7 @@ static void dccp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
dccp_get_info(sk, _info);
}
-static struct inet_diag_handler dccp_diag_handler = {
+static const struct inet_diag_handler dccp_diag_handler = {
.idiag_hashinfo = &dccp_hashinfo,
.idiag_get_info = dccp_diag_get_info,
.idiag_type = DCCPDIAG_GETSOCK,
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index 933a0ecf8d46..30f9fb76b921 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -1,11 +1,17 @@
/*
* net/dccp/feat.c
*
- * An implementation of the DCCP protocol
- * Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ * Feature negotiation for the DCCP protocol (RFC 4340, section 6)
+ *
+ * Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk>
+ * Rewrote from scratch, some bits from earlier code by
+ * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
*
* ASSUMPTIONS
* -----------
+ * o Feature negotiation is coordinated with connection setup (as in TCP), wild
+ * changes of parameters of an established connection are not supported.
* o All currently known SP features have 1-byte quantities. If in the future
* extensions of RFCs 4340..42 define features with item lengths larger than
* one byte, a feature-specific extension of the code will be required.
@@ -15,597 +21,1185 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-
#include <linux/module.h>
-
#include "ccid.h"
#include "feat.h"
-#define DCCP_FEAT_SP_NOAGREE (-123)
-
-int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
- u8 *val, u8 len, gfp_t gfp)
-{
- struct dccp_opt_pend *opt;
-
- dccp_feat_debug(type, feature, *val);
-
- if (len > 3) {
- DCCP_WARN("invalid length %d\n", len);
- return -EINVAL;
- }
- /* XXX add further sanity checks */
-
- /* check if that feature is already being negotiated */
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- /* ok we found a negotiation for this option already */
- if (opt->dccpop_feat == feature && opt->dccpop_type == type) {
- dccp_pr_debug("Replacing old\n");
- /* replace */
- BUG_ON(opt->dccpop_val == NULL);
- kfree(opt->dccpop_val);
- opt->dccpop_val = val;
- opt->dccpop_len = len;
- opt->dccpop_conf = 0;
- return 0;
- }
- }
-
- /* negotiation for a new feature */
- opt = kmalloc(sizeof(*opt), gfp);
- if (opt == NULL)
- return -ENOMEM;
-
- opt->dccpop_type = type;
- opt->dccpop_feat = feature;
- opt->dccpop_len = len;
- opt->dccpop_val = val;
- opt->dccpop_conf = 0;
- opt->dccpop_sc = NULL;
-
- BUG_ON(opt->dccpop_val == NULL);
-
- list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending);
- return 0;
-}
-
-EXPORT_SYMBOL_GPL(dccp_feat_change);
-
-static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr)
+/*
+ * Feature activation handlers.
+ *
+ * These all use an u64 argument, to provide enough room for NN/SP features. At
+ * this stage the negotiated values have been checked to be within their range.
+ */
+static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct dccp_minisock *dmsk = dccp_msk(sk);
- /* figure out if we are changing our CCID or the peer's */
- const int rx = type == DCCPO_CHANGE_R;
- const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid;
- struct ccid *new_ccid;
+ struct ccid *new_ccid = ccid_new(ccid, sk, rx, gfp_any());
- /* Check if nothing is being changed. */
- if (ccid_nr == new_ccid_nr)
- return 0;
-
- new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC);
if (new_ccid == NULL)
return -ENOMEM;
if (rx) {
ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
dp->dccps_hc_rx_ccid = new_ccid;
- dmsk->dccpms_rx_ccid = new_ccid_nr;
} else {
ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
dp->dccps_hc_tx_ccid = new_ccid;
- dmsk->dccpms_tx_ccid = new_ccid_nr;
}
+ return 0;
+}
+static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx)
+{
+ if (!rx)
+ dccp_msk(sk)->dccpms_sequence_window = seq_win;
return 0;
}
-static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val)
+static int dccp_hdlr_ack_ratio(struct sock *sk, u64 ratio, bool rx)
{
- dccp_feat_debug(type, feat, val);
+ if (rx)
+ dccp_sk(sk)->dccps_r_ack_ratio = ratio;
+ else
+ dccp_sk(sk)->dccps_l_ack_ratio = ratio;
+ return 0;
+}
- switch (feat) {
- case DCCPF_CCID:
- return dccp_feat_update_ccid(sk, type, val);
- default:
- dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n",
- dccp_feat_typename(type), feat);
- break;
+static int dccp_hdlr_ackvec(struct sock *sk, u64 enable, bool rx)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+
+ if (rx) {
+ if (enable && dp->dccps_hc_rx_ackvec == NULL) {
+ dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(gfp_any());
+ if (dp->dccps_hc_rx_ackvec == NULL)
+ return -ENOMEM;
+ } else if (!enable) {
+ dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
+ dp->dccps_hc_rx_ackvec = NULL;
+ }
}
return 0;
}
-static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt,
- u8 *rpref, u8 rlen)
+static int dccp_hdlr_ndp(struct sock *sk, u64 enable, bool rx)
{
- struct dccp_sock *dp = dccp_sk(sk);
- u8 *spref, slen, *res = NULL;
- int i, j, rc, agree = 1;
+ if (!rx)
+ dccp_sk(sk)->dccps_send_ndp_count = (enable > 0);
+ return 0;
+}
- BUG_ON(rpref == NULL);
+/*
+ * Minimum Checksum Coverage is located at the RX side (9.2.1). This means that
+ * `rx' holds when the sending peer informs about his partial coverage via a
+ * ChangeR() option. In the other case, we are the sender and the receiver
+ * announces its coverage via ChangeL() options. The policy here is to honour
+ * such communication by enabling the corresponding partial coverage - but only
+ * if it has not been set manually before; the warning here means that all
+ * packets will be dropped.
+ */
+static int dccp_hdlr_min_cscov(struct sock *sk, u64 cscov, bool rx)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
- /* check if we are the black sheep */
- if (dp->dccps_role == DCCP_ROLE_CLIENT) {
- spref = rpref;
- slen = rlen;
- rpref = opt->dccpop_val;
- rlen = opt->dccpop_len;
- } else {
- spref = opt->dccpop_val;
- slen = opt->dccpop_len;
+ if (rx)
+ dp->dccps_pcrlen = cscov;
+ else {
+ if (dp->dccps_pcslen == 0)
+ dp->dccps_pcslen = cscov;
+ else if (cscov > dp->dccps_pcslen)
+ DCCP_WARN("CsCov %u too small, peer requires >= %u\n",
+ dp->dccps_pcslen, (u8)cscov);
}
- /*
- * Now we have server preference list in spref and client preference in
- * rpref
- */
- BUG_ON(spref == NULL);
- BUG_ON(rpref == NULL);
+ return 0;
+}
- /* FIXME sanity check vals */
+static const struct {
+ u8 feat_num; /* DCCPF_xxx */
+ enum dccp_feat_type rxtx; /* RX or TX */
+ enum dccp_feat_type reconciliation; /* SP or NN */
+ u8 default_value; /* as in 6.4 */
+ int (*activation_hdlr)(struct sock *sk, u64 val, bool rx);
+/*
+ * Lookup table for location and type of features (from RFC 4340/4342)
+ * +--------------------------+----+-----+----+----+---------+-----------+
+ * | Feature | Location | Reconc. | Initial | Section |
+ * | | RX | TX | SP | NN | Value | Reference |
+ * +--------------------------+----+-----+----+----+---------+-----------+
+ * | DCCPF_CCID | | X | X | | 2 | 10 |
+ * | DCCPF_SHORT_SEQNOS | | X | X | | 0 | 7.6.1 |
+ * | DCCPF_SEQUENCE_WINDOW | | X | | X | 100 | 7.5.2 |
+ * | DCCPF_ECN_INCAPABLE | X | | X | | 0 | 12.1 |
+ * | DCCPF_ACK_RATIO | | X | | X | 2 | 11.3 |
+ * | DCCPF_SEND_ACK_VECTOR | X | | X | | 0 | 11.5 |
+ * | DCCPF_SEND_NDP_COUNT | | X | X | | 0 | 7.7.2 |
+ * | DCCPF_MIN_CSUM_COVER | X | | X | | 0 | 9.2.1 |
+ * | DCCPF_DATA_CHECKSUM | X | | X | | 0 | 9.3.1 |
+ * | DCCPF_SEND_LEV_RATE | X | | X | | 0 | 4342/8.4 |
+ * +--------------------------+----+-----+----+----+---------+-----------+
+ */
+} dccp_feat_table[] = {
+ { DCCPF_CCID, FEAT_AT_TX, FEAT_SP, 2, dccp_hdlr_ccid },
+ { DCCPF_SHORT_SEQNOS, FEAT_AT_TX, FEAT_SP, 0, NULL },
+ { DCCPF_SEQUENCE_WINDOW, FEAT_AT_TX, FEAT_NN, 100, dccp_hdlr_seq_win },
+ { DCCPF_ECN_INCAPABLE, FEAT_AT_RX, FEAT_SP, 0, NULL },
+ { DCCPF_ACK_RATIO, FEAT_AT_TX, FEAT_NN, 2, dccp_hdlr_ack_ratio},
+ { DCCPF_SEND_ACK_VECTOR, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_ackvec },
+ { DCCPF_SEND_NDP_COUNT, FEAT_AT_TX, FEAT_SP, 0, dccp_hdlr_ndp },
+ { DCCPF_MIN_CSUM_COVER, FEAT_AT_RX, FEAT_SP, 0, dccp_hdlr_min_cscov},
+ { DCCPF_DATA_CHECKSUM, FEAT_AT_RX, FEAT_SP, 0, NULL },
+ { DCCPF_SEND_LEV_RATE, FEAT_AT_RX, FEAT_SP, 0, NULL },
+};
+#define DCCP_FEAT_SUPPORTED_MAX ARRAY_SIZE(dccp_feat_table)
+
+/**
+ * dccp_feat_index - Hash function to map feature number into array position
+ * Returns consecutive array index or -1 if the feature is not understood.
+ */
+static int dccp_feat_index(u8 feat_num)
+{
+ /* The first 9 entries are occupied by the types from RFC 4340, 6.4 */
+ if (feat_num > DCCPF_RESERVED && feat_num <= DCCPF_DATA_CHECKSUM)
+ return feat_num - 1;
- /* Are values in any order? XXX Lame "algorithm" here */
- for (i = 0; i < slen; i++) {
- for (j = 0; j < rlen; j++) {
- if (spref[i] == rpref[j]) {
- res = &spref[i];
- break;
- }
- }
- if (res)
- break;
+ /*
+ * Other features: add cases for new feature types here after adding
+ * them to the above table.
+ */
+ switch (feat_num) {
+ case DCCPF_SEND_LEV_RATE:
+ return DCCP_FEAT_SUPPORTED_MAX - 1;
}
+ return -1;
+}
- /* we didn't agree on anything */
- if (res == NULL) {
- /* confirm previous value */
- switch (opt->dccpop_feat) {
- case DCCPF_CCID:
- /* XXX did i get this right? =P */
- if (opt->dccpop_type == DCCPO_CHANGE_L)
- res = &dccp_msk(sk)->dccpms_tx_ccid;
- else
- res = &dccp_msk(sk)->dccpms_rx_ccid;
- break;
+static u8 dccp_feat_type(u8 feat_num)
+{
+ int idx = dccp_feat_index(feat_num);
- default:
- DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat);
- /* XXX implement res */
- return -EFAULT;
- }
+ if (idx < 0)
+ return FEAT_UNKNOWN;
+ return dccp_feat_table[idx].reconciliation;
+}
- dccp_pr_debug("Don't agree... reconfirming %d\n", *res);
- agree = 0; /* this is used for mandatory options... */
- }
+static int dccp_feat_default_value(u8 feat_num)
+{
+ int idx = dccp_feat_index(feat_num);
+ /*
+ * There are no default values for unknown features, so encountering a
+ * negative index here indicates a serious problem somewhere else.
+ */
+ DCCP_BUG_ON(idx < 0);
- /* need to put result and our preference list */
- rlen = 1 + opt->dccpop_len;
- rpref = kmalloc(rlen, GFP_ATOMIC);
- if (rpref == NULL)
- return -ENOMEM;
+ return idx < 0 ? 0 : dccp_feat_table[idx].default_value;
+}
- *rpref = *res;
- memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len);
+static int __dccp_feat_activate(struct sock *sk, const int idx,
+ const bool is_local, dccp_feat_val const *fval)
+{
+ bool rx;
+ u64 val;
- /* put it in the "confirm queue" */
- if (opt->dccpop_sc == NULL) {
- opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC);
- if (opt->dccpop_sc == NULL) {
- kfree(rpref);
- return -ENOMEM;
+ if (idx < 0 || idx >= DCCP_FEAT_SUPPORTED_MAX)
+ return -1;
+ if (dccp_feat_table[idx].activation_hdlr == NULL)
+ return 0;
+
+ if (fval == NULL) {
+ val = dccp_feat_table[idx].default_value;
+ } else if (dccp_feat_table[idx].reconciliation == FEAT_SP) {
+ if (fval->sp.vec == NULL) {
+ /*
+ * This can happen when an empty Confirm is sent
+ * for an SP (i.e. known) feature. In this case
+ * we would be using the default anyway.
+ */
+ DCCP_CRIT("Feature #%d undefined: using default", idx);
+ val = dccp_feat_table[idx].default_value;
+ } else {
+ val = fval->sp.vec[0];
}
} else {
- /* recycle the confirm slot */
- BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
- kfree(opt->dccpop_sc->dccpoc_val);
- dccp_pr_debug("recycling confirm slot\n");
+ val = fval->nn;
}
- memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc));
- opt->dccpop_sc->dccpoc_val = rpref;
- opt->dccpop_sc->dccpoc_len = rlen;
+ /* Location is RX if this is a local-RX or remote-TX feature */
+ rx = (is_local == (dccp_feat_table[idx].rxtx == FEAT_AT_RX));
- /* update the option on our side [we are about to send the confirm] */
- rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res);
- if (rc) {
- kfree(opt->dccpop_sc->dccpoc_val);
- kfree(opt->dccpop_sc);
- opt->dccpop_sc = NULL;
- return rc;
- }
+ return dccp_feat_table[idx].activation_hdlr(sk, val, rx);
+}
- dccp_pr_debug("Will confirm %d\n", *rpref);
+/* Test for "Req'd" feature (RFC 4340, 6.4) */
+static inline int dccp_feat_must_be_understood(u8 feat_num)
+{
+ return feat_num == DCCPF_CCID || feat_num == DCCPF_SHORT_SEQNOS ||
+ feat_num == DCCPF_SEQUENCE_WINDOW;
+}
- /* say we want to change to X but we just got a confirm X, suppress our
- * change
- */
- if (!opt->dccpop_conf) {
- if (*opt->dccpop_val == *res)
- opt->dccpop_conf = 1;
- dccp_pr_debug("won't ask for change of same feature\n");
+/* copy constructor, fval must not already contain allocated memory */
+static int dccp_feat_clone_sp_val(dccp_feat_val *fval, u8 const *val, u8 len)
+{
+ fval->sp.len = len;
+ if (fval->sp.len > 0) {
+ fval->sp.vec = kmemdup(val, len, gfp_any());
+ if (fval->sp.vec == NULL) {
+ fval->sp.len = 0;
+ return -ENOBUFS;
+ }
}
+ return 0;
+}
- return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */
+static void dccp_feat_val_destructor(u8 feat_num, dccp_feat_val *val)
+{
+ if (unlikely(val == NULL))
+ return;
+ if (dccp_feat_type(feat_num) == FEAT_SP)
+ kfree(val->sp.vec);
+ memset(val, 0, sizeof(*val));
}
-static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+static struct dccp_feat_entry *
+ dccp_feat_clone_entry(struct dccp_feat_entry const *original)
{
- struct dccp_minisock *dmsk = dccp_msk(sk);
- struct dccp_opt_pend *opt;
- int rc = 1;
- u8 t;
+ struct dccp_feat_entry *new;
+ u8 type = dccp_feat_type(original->feat_num);
- /*
- * We received a CHANGE. We gotta match it against our own preference
- * list. If we got a CHANGE_R it means it's a change for us, so we need
- * to compare our CHANGE_L list.
- */
- if (type == DCCPO_CHANGE_L)
- t = DCCPO_CHANGE_R;
- else
- t = DCCPO_CHANGE_L;
+ if (type == FEAT_UNKNOWN)
+ return NULL;
- /* find our preference list for this feature */
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- if (opt->dccpop_type != t || opt->dccpop_feat != feature)
- continue;
+ new = kmemdup(original, sizeof(struct dccp_feat_entry), gfp_any());
+ if (new == NULL)
+ return NULL;
- /* find the winner from the two preference lists */
- rc = dccp_feat_reconcile(sk, opt, val, len);
- break;
+ if (type == FEAT_SP && dccp_feat_clone_sp_val(&new->val,
+ original->val.sp.vec,
+ original->val.sp.len)) {
+ kfree(new);
+ return NULL;
}
+ return new;
+}
- /* We didn't deal with the change. This can happen if we have no
- * preference list for the feature. In fact, it just shouldn't
- * happen---if we understand a feature, we should have a preference list
- * with at least the default value.
- */
- BUG_ON(rc == 1);
+static void dccp_feat_entry_destructor(struct dccp_feat_entry *entry)
+{
+ if (entry != NULL) {
+ dccp_feat_val_destructor(entry->feat_num, &entry->val);
+ kfree(entry);
+ }
+}
- return rc;
+/*
+ * List management functions
+ *
+ * Feature negotiation lists rely on and maintain the following invariants:
+ * - each feat_num in the list is known, i.e. we know its type and default value
+ * - each feat_num/is_local combination is unique (old entries are overwritten)
+ * - SP values are always freshly allocated
+ * - list is sorted in increasing order of feature number (faster lookup)
+ */
+static struct dccp_feat_entry *dccp_feat_list_lookup(struct list_head *fn_list,
+ u8 feat_num, bool is_local)
+{
+ struct dccp_feat_entry *entry;
+
+ list_for_each_entry(entry, fn_list, node) {
+ if (entry->feat_num == feat_num && entry->is_local == is_local)
+ return entry;
+ else if (entry->feat_num > feat_num)
+ break;
+ }
+ return NULL;
}
-static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+/**
+ * dccp_feat_entry_new - Central list update routine (called by all others)
+ * @head: list to add to
+ * @feat: feature number
+ * @local: whether the local (1) or remote feature with number @feat is meant
+ * This is the only constructor and serves to ensure the above invariants.
+ */
+static struct dccp_feat_entry *
+ dccp_feat_entry_new(struct list_head *head, u8 feat, bool local)
{
- struct dccp_opt_pend *opt;
- struct dccp_minisock *dmsk = dccp_msk(sk);
- u8 *copy;
- int rc;
+ struct dccp_feat_entry *entry;
+
+ list_for_each_entry(entry, head, node)
+ if (entry->feat_num == feat && entry->is_local == local) {
+ dccp_feat_val_destructor(entry->feat_num, &entry->val);
+ return entry;
+ } else if (entry->feat_num > feat) {
+ head = &entry->node;
+ break;
+ }
- /* NN features must be Change L (sec. 6.3.2) */
- if (type != DCCPO_CHANGE_L) {
- dccp_pr_debug("received %s for NN feature %d\n",
- dccp_feat_typename(type), feature);
- return -EFAULT;
+ entry = kmalloc(sizeof(*entry), gfp_any());
+ if (entry != NULL) {
+ entry->feat_num = feat;
+ entry->is_local = local;
+ list_add_tail(&entry->node, head);
}
+ return entry;
+}
- /* XXX sanity check opt val */
+/**
+ * dccp_feat_push_change - Add/overwrite a Change option in the list
+ * @fn_list: feature-negotiation list to update
+ * @feat: one of %dccp_feature_numbers
+ * @local: whether local (1) or remote (0) @feat_num is meant
+ * @needs_mandatory: whether to use Mandatory feature negotiation options
+ * @fval: pointer to NN/SP value to be inserted (will be copied)
+ */
+static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
+ u8 mandatory, dccp_feat_val *fval)
+{
+ struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
- /* copy option so we can confirm it */
- opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
- if (opt == NULL)
+ if (new == NULL)
return -ENOMEM;
- copy = kmemdup(val, len, GFP_ATOMIC);
- if (copy == NULL) {
- kfree(opt);
- return -ENOMEM;
- }
+ new->feat_num = feat;
+ new->is_local = local;
+ new->state = FEAT_INITIALISING;
+ new->needs_confirm = 0;
+ new->empty_confirm = 0;
+ new->val = *fval;
+ new->needs_mandatory = mandatory;
- opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */
- opt->dccpop_feat = feature;
- opt->dccpop_val = copy;
- opt->dccpop_len = len;
+ return 0;
+}
- /* change feature */
- rc = dccp_feat_update(sk, type, feature, *val);
- if (rc) {
- kfree(opt->dccpop_val);
- kfree(opt);
- return rc;
- }
+/**
+ * dccp_feat_push_confirm - Add a Confirm entry to the FN list
+ * @fn_list: feature-negotiation list to add to
+ * @feat: one of %dccp_feature_numbers
+ * @local: whether local (1) or remote (0) @feat_num is being confirmed
+ * @fval: pointer to NN/SP value to be inserted or NULL
+ * Returns 0 on success, a Reset code for further processing otherwise.
+ */
+static int dccp_feat_push_confirm(struct list_head *fn_list, u8 feat, u8 local,
+ dccp_feat_val *fval)
+{
+ struct dccp_feat_entry *new = dccp_feat_entry_new(fn_list, feat, local);
- dccp_feat_debug(type, feature, *copy);
+ if (new == NULL)
+ return DCCP_RESET_CODE_TOO_BUSY;
- list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
+ new->feat_num = feat;
+ new->is_local = local;
+ new->state = FEAT_STABLE; /* transition in 6.6.2 */
+ new->needs_confirm = 1;
+ new->empty_confirm = (fval == NULL);
+ new->val.nn = 0; /* zeroes the whole structure */
+ if (!new->empty_confirm)
+ new->val = *fval;
+ new->needs_mandatory = 0;
return 0;
}
-static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
- u8 type, u8 feature)
+static int dccp_push_empty_confirm(struct list_head *fn_list, u8 feat, u8 local)
{
- /* XXX check if other confirms for that are queued and recycle slot */
- struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
+ return dccp_feat_push_confirm(fn_list, feat, local, NULL);
+}
- if (opt == NULL) {
- /* XXX what do we do? Ignoring should be fine. It's a change
- * after all =P
- */
- return;
- }
+static inline void dccp_feat_list_pop(struct dccp_feat_entry *entry)
+{
+ list_del(&entry->node);
+ dccp_feat_entry_destructor(entry);
+}
- switch (type) {
- case DCCPO_CHANGE_L:
- opt->dccpop_type = DCCPO_CONFIRM_R;
- break;
- case DCCPO_CHANGE_R:
- opt->dccpop_type = DCCPO_CONFIRM_L;
- break;
- default:
- DCCP_WARN("invalid type %d\n", type);
- kfree(opt);
- return;
+void dccp_feat_list_purge(struct list_head *fn_list)
+{
+ struct dccp_feat_entry *entry, *next;
+
+ list_for_each_entry_safe(entry, next, fn_list, node)
+ dccp_feat_entry_destructor(entry);
+ INIT_LIST_HEAD(fn_list);
+}
+EXPORT_SYMBOL_GPL(dccp_feat_list_purge);
+
+/* generate @to as full clone of @from - @to must not contain any nodes */
+int dccp_feat_clone_list(struct list_head const *from, struct list_head *to)
+{
+ struct dccp_feat_entry *entry, *new;
+
+ INIT_LIST_HEAD(to);
+ list_for_each_entry(entry, from, node) {
+ new = dccp_feat_clone_entry(entry);
+ if (new == NULL)
+ goto cloning_failed;
+ list_add_tail(&new->node, to);
}
- opt->dccpop_feat = feature;
- opt->dccpop_val = NULL;
- opt->dccpop_len = 0;
+ return 0;
+
+cloning_failed:
+ dccp_feat_list_purge(to);
+ return -ENOMEM;
+}
- /* change feature */
- dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature);
+/**
+ * dccp_feat_valid_nn_length - Enforce length constraints on NN options
+ * Length is between 0 and %DCCP_OPTVAL_MAXLEN. Used for outgoing packets only,
+ * incoming options are accepted as long as their values are valid.
+ */
+static u8 dccp_feat_valid_nn_length(u8 feat_num)
+{
+ if (feat_num == DCCPF_ACK_RATIO) /* RFC 4340, 11.3 and 6.6.8 */
+ return 2;
+ if (feat_num == DCCPF_SEQUENCE_WINDOW) /* RFC 4340, 7.5.2 and 6.5 */
+ return 6;
+ return 0;
+}
- list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
+static u8 dccp_feat_is_valid_nn_val(u8 feat_num, u64 val)
+{
+ switch (feat_num) {
+ case DCCPF_ACK_RATIO:
+ return val <= DCCPF_ACK_RATIO_MAX;
+ case DCCPF_SEQUENCE_WINDOW:
+ return val >= DCCPF_SEQ_WMIN && val <= DCCPF_SEQ_WMAX;
+ }
+ return 0; /* feature unknown - so we can't tell */
}
-static void dccp_feat_flush_confirm(struct sock *sk)
+/* check that SP values are within the ranges defined in RFC 4340 */
+static u8 dccp_feat_is_valid_sp_val(u8 feat_num, u8 val)
{
- struct dccp_minisock *dmsk = dccp_msk(sk);
- /* Check if there is anything to confirm in the first place */
- int yes = !list_empty(&dmsk->dccpms_conf);
+ switch (feat_num) {
+ case DCCPF_CCID:
+ return val == DCCPC_CCID2 || val == DCCPC_CCID3;
+ /* Type-check Boolean feature values: */
+ case DCCPF_SHORT_SEQNOS:
+ case DCCPF_ECN_INCAPABLE:
+ case DCCPF_SEND_ACK_VECTOR:
+ case DCCPF_SEND_NDP_COUNT:
+ case DCCPF_DATA_CHECKSUM:
+ case DCCPF_SEND_LEV_RATE:
+ return val < 2;
+ case DCCPF_MIN_CSUM_COVER:
+ return val < 16;
+ }
+ return 0; /* feature unknown */
+}
- if (!yes) {
- struct dccp_opt_pend *opt;
+static u8 dccp_feat_sp_list_ok(u8 feat_num, u8 const *sp_list, u8 sp_len)
+{
+ if (sp_list == NULL || sp_len < 1)
+ return 0;
+ while (sp_len--)
+ if (!dccp_feat_is_valid_sp_val(feat_num, *sp_list++))
+ return 0;
+ return 1;
+}
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- if (opt->dccpop_conf) {
- yes = 1;
- break;
+/**
+ * dccp_feat_insert_opts - Generate FN options from current list state
+ * @skb: next sk_buff to be sent to the peer
+ * @dp: for client during handshake and general negotiation
+ * @dreq: used by the server only (all Changes/Confirms in LISTEN/RESPOND)
+ */
+int dccp_feat_insert_opts(struct dccp_sock *dp, struct dccp_request_sock *dreq,
+ struct sk_buff *skb)
+{
+ struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg;
+ struct dccp_feat_entry *pos, *next;
+ u8 opt, type, len, *ptr, nn_in_nbo[DCCP_OPTVAL_MAXLEN];
+ bool rpt;
+
+ /* put entries into @skb in the order they appear in the list */
+ list_for_each_entry_safe_reverse(pos, next, fn, node) {
+ opt = dccp_feat_genopt(pos);
+ type = dccp_feat_type(pos->feat_num);
+ rpt = false;
+
+ if (pos->empty_confirm) {
+ len = 0;
+ ptr = NULL;
+ } else {
+ if (type == FEAT_SP) {
+ len = pos->val.sp.len;
+ ptr = pos->val.sp.vec;
+ rpt = pos->needs_confirm;
+ } else if (type == FEAT_NN) {
+ len = dccp_feat_valid_nn_length(pos->feat_num);
+ ptr = nn_in_nbo;
+ dccp_encode_value_var(pos->val.nn, ptr, len);
+ } else {
+ DCCP_BUG("unknown feature %u", pos->feat_num);
+ return -1;
}
}
+
+ if (dccp_insert_fn_opt(skb, opt, pos->feat_num, ptr, len, rpt))
+ return -1;
+ if (pos->needs_mandatory && dccp_insert_option_mandatory(skb))
+ return -1;
+ /*
+ * Enter CHANGING after transmitting the Change option (6.6.2).
+ */
+ if (pos->state == FEAT_INITIALISING)
+ pos->state = FEAT_CHANGING;
}
+ return 0;
+}
- if (!yes)
- return;
+/**
+ * __feat_register_nn - Register new NN value on socket
+ * @fn: feature-negotiation list to register with
+ * @feat: an NN feature from %dccp_feature_numbers
+ * @mandatory: use Mandatory option if 1
+ * @nn_val: value to register (restricted to 4 bytes)
+ * Note that NN features are local by definition (RFC 4340, 6.3.2).
+ */
+static int __feat_register_nn(struct list_head *fn, u8 feat,
+ u8 mandatory, u64 nn_val)
+{
+ dccp_feat_val fval = { .nn = nn_val };
- /* OK there is something to confirm... */
- /* XXX check if packet is in flight? Send delayed ack?? */
- if (sk->sk_state == DCCP_OPEN)
- dccp_send_ack(sk);
+ if (dccp_feat_type(feat) != FEAT_NN ||
+ !dccp_feat_is_valid_nn_val(feat, nn_val))
+ return -EINVAL;
+
+ /* Don't bother with default values, they will be activated anyway. */
+ if (nn_val - (u64)dccp_feat_default_value(feat) == 0)
+ return 0;
+
+ return dccp_feat_push_change(fn, feat, 1, mandatory, &fval);
}
-int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+/**
+ * __feat_register_sp - Register new SP value/list on socket
+ * @fn: feature-negotiation list to register with
+ * @feat: an SP feature from %dccp_feature_numbers
+ * @is_local: whether the local (1) or the remote (0) @feat is meant
+ * @mandatory: use Mandatory option if 1
+ * @sp_val: SP value followed by optional preference list
+ * @sp_len: length of @sp_val in bytes
+ */
+static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local,
+ u8 mandatory, u8 const *sp_val, u8 sp_len)
{
- int rc;
+ dccp_feat_val fval;
- dccp_feat_debug(type, feature, *val);
+ if (dccp_feat_type(feat) != FEAT_SP ||
+ !dccp_feat_sp_list_ok(feat, sp_val, sp_len))
+ return -EINVAL;
- /* figure out if it's SP or NN feature */
- switch (feature) {
- /* deal with SP features */
- case DCCPF_CCID:
- rc = dccp_feat_sp(sk, type, feature, val, len);
- break;
+ /* Avoid negotiating alien CCIDs by only advertising supported ones */
+ if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len))
+ return -EOPNOTSUPP;
- /* deal with NN features */
- case DCCPF_ACK_RATIO:
- rc = dccp_feat_nn(sk, type, feature, val, len);
- break;
+ if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len))
+ return -ENOMEM;
- /* XXX implement other features */
- default:
- dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n",
- dccp_feat_typename(type), feature);
- rc = -EFAULT;
- break;
- }
+ return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval);
+}
- /* check if there were problems changing features */
- if (rc) {
- /* If we don't agree on SP, we sent a confirm for old value.
- * However we propagate rc to caller in case option was
- * mandatory
+/**
+ * dccp_feat_register_sp - Register requests to change SP feature values
+ * @sk: client or listening socket
+ * @feat: one of %dccp_feature_numbers
+ * @is_local: whether the local (1) or remote (0) @feat is meant
+ * @list: array of preferred values, in descending order of preference
+ * @len: length of @list in bytes
+ */
+int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
+ u8 const *list, u8 len)
+{ /* any changes must be registered before establishing the connection */
+ if (sk->sk_state != DCCP_CLOSED)
+ return -EISCONN;
+ if (dccp_feat_type(feat) != FEAT_SP)
+ return -EINVAL;
+ return __feat_register_sp(&dccp_sk(sk)->dccps_featneg, feat, is_local,
+ 0, list, len);
+}
+
+/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */
+int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val)
+{
+ /* any changes must be registered before establishing the connection */
+ if (sk->sk_state != DCCP_CLOSED)
+ return -EISCONN;
+ if (dccp_feat_type(feat) != FEAT_NN)
+ return -EINVAL;
+ return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val);
+}
+
+/*
+ * Tracking features whose value depend on the choice of CCID
+ *
+ * This is designed with an extension in mind so that a list walk could be done
+ * before activating any features. However, the existing framework was found to
+ * work satisfactorily up until now, the automatic verification is left open.
+ * When adding new CCIDs, add a corresponding dependency table here.
+ */
+static const struct ccid_dependency *dccp_feat_ccid_deps(u8 ccid, bool is_local)
+{
+ static const struct ccid_dependency ccid2_dependencies[2][2] = {
+ /*
+ * CCID2 mandates Ack Vectors (RFC 4341, 4.): as CCID is a TX
+ * feature and Send Ack Vector is an RX feature, `is_local'
+ * needs to be reversed.
*/
- if (rc != DCCP_FEAT_SP_NOAGREE)
- dccp_feat_empty_confirm(dccp_msk(sk), type, feature);
+ { /* Dependencies of the receiver-side (remote) CCID2 */
+ {
+ .dependent_feat = DCCPF_SEND_ACK_VECTOR,
+ .is_local = true,
+ .is_mandatory = true,
+ .val = 1
+ },
+ { 0, 0, 0, 0 }
+ },
+ { /* Dependencies of the sender-side (local) CCID2 */
+ {
+ .dependent_feat = DCCPF_SEND_ACK_VECTOR,
+ .is_local = false,
+ .is_mandatory = true,
+ .val = 1
+ },
+ { 0, 0, 0, 0 }
+ }
+ };
+ static const struct ccid_dependency ccid3_dependencies[2][5] = {
+ { /*
+ * Dependencies of the receiver-side CCID3
+ */
+ { /* locally disable Ack Vectors */
+ .dependent_feat = DCCPF_SEND_ACK_VECTOR,
+ .is_local = true,
+ .is_mandatory = false,
+ .val = 0
+ },
+ { /* see below why Send Loss Event Rate is on */
+ .dependent_feat = DCCPF_SEND_LEV_RATE,
+ .is_local = true,
+ .is_mandatory = true,
+ .val = 1
+ },
+ { /* NDP Count is needed as per RFC 4342, 6.1.1 */
+ .dependent_feat = DCCPF_SEND_NDP_COUNT,
+ .is_local = false,
+ .is_mandatory = true,
+ .val = 1
+ },
+ { 0, 0, 0, 0 },
+ },
+ { /*
+ * CCID3 at the TX side: we request that the HC-receiver
+ * will not send Ack Vectors (they will be ignored, so
+ * Mandatory is not set); we enable Send Loss Event Rate
+ * (Mandatory since the implementation does not support
+ * the Loss Intervals option of RFC 4342, 8.6).
+ * The last two options are for peer's information only.
+ */
+ {
+ .dependent_feat = DCCPF_SEND_ACK_VECTOR,
+ .is_local = false,
+ .is_mandatory = false,
+ .val = 0
+ },
+ {
+ .dependent_feat = DCCPF_SEND_LEV_RATE,
+ .is_local = false,
+ .is_mandatory = true,
+ .val = 1
+ },
+ { /* this CCID does not support Ack Ratio */
+ .dependent_feat = DCCPF_ACK_RATIO,
+ .is_local = true,
+ .is_mandatory = false,
+ .val = 0
+ },
+ { /* tell receiver we are sending NDP counts */
+ .dependent_feat = DCCPF_SEND_NDP_COUNT,
+ .is_local = true,
+ .is_mandatory = false,
+ .val = 1
+ },
+ { 0, 0, 0, 0 }
+ }
+ };
+ switch (ccid) {
+ case DCCPC_CCID2:
+ return ccid2_dependencies[is_local];
+ case DCCPC_CCID3:
+ return ccid3_dependencies[is_local];
+ default:
+ return NULL;
}
+}
- /* generate the confirm [if required] */
- dccp_feat_flush_confirm(sk);
-
+/**
+ * dccp_feat_propagate_ccid - Resolve dependencies of features on choice of CCID
+ * @fn: feature-negotiation list to update
+ * @id: CCID number to track
+ * @is_local: whether TX CCID (1) or RX CCID (0) is meant
+ * This function needs to be called after registering all other features.
+ */
+static int dccp_feat_propagate_ccid(struct list_head *fn, u8 id, bool is_local)
+{
+ const struct ccid_dependency *table = dccp_feat_ccid_deps(id, is_local);
+ int i, rc = (table == NULL);
+
+ for (i = 0; rc == 0 && table[i].dependent_feat != DCCPF_RESERVED; i++)
+ if (dccp_feat_type(table[i].dependent_feat) == FEAT_SP)
+ rc = __feat_register_sp(fn, table[i].dependent_feat,
+ table[i].is_local,
+ table[i].is_mandatory,
+ &table[i].val, 1);
+ else
+ rc = __feat_register_nn(fn, table[i].dependent_feat,
+ table[i].is_mandatory,
+ table[i].val);
return rc;
}
-EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
+/**
+ * dccp_feat_finalise_settings - Finalise settings before starting negotiation
+ * @dp: client or listening socket (settings will be inherited)
+ * This is called after all registrations (socket initialisation, sysctls, and
+ * sockopt calls), and before sending the first packet containing Change options
+ * (ie. client-Request or server-Response), to ensure internal consistency.
+ */
+int dccp_feat_finalise_settings(struct dccp_sock *dp)
+{
+ struct list_head *fn = &dp->dccps_featneg;
+ struct dccp_feat_entry *entry;
+ int i = 2, ccids[2] = { -1, -1 };
-int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
- u8 *val, u8 len)
+ /*
+ * Propagating CCIDs:
+ * 1) not useful to propagate CCID settings if this host advertises more
+ * than one CCID: the choice of CCID may still change - if this is
+ * the client, or if this is the server and the client sends
+ * singleton CCID values.
+ * 2) since is that propagate_ccid changes the list, we defer changing
+ * the sorted list until after the traversal.
+ */
+ list_for_each_entry(entry, fn, node)
+ if (entry->feat_num == DCCPF_CCID && entry->val.sp.len == 1)
+ ccids[entry->is_local] = entry->val.sp.vec[0];
+ while (i--)
+ if (ccids[i] > 0 && dccp_feat_propagate_ccid(fn, ccids[i], i))
+ return -1;
+ return 0;
+}
+
+/**
+ * dccp_feat_server_ccid_dependencies - Resolve CCID-dependent features
+ * It is the server which resolves the dependencies once the CCID has been
+ * fully negotiated. If no CCID has been negotiated, it uses the default CCID.
+ */
+int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq)
{
- u8 t;
- struct dccp_opt_pend *opt;
- struct dccp_minisock *dmsk = dccp_msk(sk);
- int found = 0;
- int all_confirmed = 1;
+ struct list_head *fn = &dreq->dreq_featneg;
+ struct dccp_feat_entry *entry;
+ u8 is_local, ccid;
- dccp_feat_debug(type, feature, *val);
+ for (is_local = 0; is_local <= 1; is_local++) {
+ entry = dccp_feat_list_lookup(fn, DCCPF_CCID, is_local);
- /* locate our change request */
- switch (type) {
- case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break;
- case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break;
- default: DCCP_WARN("invalid type %d\n", type);
- return 1;
+ if (entry != NULL && !entry->empty_confirm)
+ ccid = entry->val.sp.vec[0];
+ else
+ ccid = dccp_feat_default_value(DCCPF_CCID);
+ if (dccp_feat_propagate_ccid(fn, ccid, is_local))
+ return -1;
}
- /* XXX sanity check feature value */
+ return 0;
+}
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- if (!opt->dccpop_conf && opt->dccpop_type == t &&
- opt->dccpop_feat == feature) {
- found = 1;
- dccp_pr_debug("feature %d found\n", opt->dccpop_feat);
+/* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */
+static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen)
+{
+ u8 c, s;
- /* XXX do sanity check */
+ for (s = 0; s < slen; s++)
+ for (c = 0; c < clen; c++)
+ if (servlist[s] == clilist[c])
+ return servlist[s];
+ return -1;
+}
- opt->dccpop_conf = 1;
+/**
+ * dccp_feat_prefer - Move preferred entry to the start of array
+ * Reorder the @array_len elements in @array so that @preferred_value comes
+ * first. Returns >0 to indicate that @preferred_value does occur in @array.
+ */
+static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len)
+{
+ u8 i, does_occur = 0;
- /* We got a confirmation---change the option */
- dccp_feat_update(sk, opt->dccpop_type,
- opt->dccpop_feat, *val);
+ if (array != NULL) {
+ for (i = 0; i < array_len; i++)
+ if (array[i] == preferred_value) {
+ array[i] = array[0];
+ does_occur++;
+ }
+ if (does_occur)
+ array[0] = preferred_value;
+ }
+ return does_occur;
+}
- /* XXX check the return value of dccp_feat_update */
- break;
- }
+/**
+ * dccp_feat_reconcile - Reconcile SP preference lists
+ * @fval: SP list to reconcile into
+ * @arr: received SP preference list
+ * @len: length of @arr in bytes
+ * @is_server: whether this side is the server (and @fv is the server's list)
+ * @reorder: whether to reorder the list in @fv after reconciling with @arr
+ * When successful, > 0 is returned and the reconciled list is in @fval.
+ * A value of 0 means that negotiation failed (no shared entry).
+ */
+static int dccp_feat_reconcile(dccp_feat_val *fv, u8 *arr, u8 len,
+ bool is_server, bool reorder)
+{
+ int rc;
- if (!opt->dccpop_conf)
- all_confirmed = 0;
+ if (!fv->sp.vec || !arr) {
+ DCCP_CRIT("NULL feature value or array");
+ return 0;
}
- /* fix re-transmit timer */
- /* XXX gotta make sure that no option negotiation occurs during
- * connection shutdown. Consider that the CLOSEREQ is sent and timer is
- * on. if all options are confirmed it might kill timer which should
- * remain alive until close is received.
- */
- if (all_confirmed) {
- dccp_pr_debug("clear feat negotiation timer %p\n", sk);
- inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
- }
+ if (is_server)
+ rc = dccp_feat_preflist_match(fv->sp.vec, fv->sp.len, arr, len);
+ else
+ rc = dccp_feat_preflist_match(arr, len, fv->sp.vec, fv->sp.len);
- if (!found)
- dccp_pr_debug("%s(%d, ...) never requested\n",
- dccp_feat_typename(type), feature);
- return 0;
-}
+ if (!reorder)
+ return rc;
+ if (rc < 0)
+ return 0;
-EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv);
+ /*
+ * Reorder list: used for activating features and in dccp_insert_fn_opt.
+ */
+ return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len);
+}
-void dccp_feat_clean(struct dccp_minisock *dmsk)
+/**
+ * dccp_feat_change_recv - Process incoming ChangeL/R options
+ * @fn: feature-negotiation list to update
+ * @is_mandatory: whether the Change was preceded by a Mandatory option
+ * @opt: %DCCPO_CHANGE_L or %DCCPO_CHANGE_R
+ * @feat: one of %dccp_feature_numbers
+ * @val: NN value or SP value/preference list
+ * @len: length of @val in bytes
+ * @server: whether this node is the server (1) or the client (0)
+ */
+static u8 dccp_feat_change_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
+ u8 feat, u8 *val, u8 len, const bool server)
{
- struct dccp_opt_pend *opt, *next;
+ u8 defval, type = dccp_feat_type(feat);
+ const bool local = (opt == DCCPO_CHANGE_R);
+ struct dccp_feat_entry *entry;
+ dccp_feat_val fval;
- list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending,
- dccpop_node) {
- BUG_ON(opt->dccpop_val == NULL);
- kfree(opt->dccpop_val);
+ if (len == 0 || type == FEAT_UNKNOWN) /* 6.1 and 6.6.8 */
+ goto unknown_feature_or_value;
+
+ /*
+ * Negotiation of NN features: Change R is invalid, so there is no
+ * simultaneous negotiation; hence we do not look up in the list.
+ */
+ if (type == FEAT_NN) {
+ if (local || len > sizeof(fval.nn))
+ goto unknown_feature_or_value;
+
+ /* 6.3.2: "The feature remote MUST accept any valid value..." */
+ fval.nn = dccp_decode_value_var(val, len);
+ if (!dccp_feat_is_valid_nn_val(feat, fval.nn))
+ goto unknown_feature_or_value;
+
+ return dccp_feat_push_confirm(fn, feat, local, &fval);
+ }
+
+ /*
+ * Unidirectional/simultaneous negotiation of SP features (6.3.1)
+ */
+ entry = dccp_feat_list_lookup(fn, feat, local);
+ if (entry == NULL) {
+ /*
+ * No particular preferences have been registered. We deal with
+ * this situation by assuming that all valid values are equally
+ * acceptable, and apply the following checks:
+ * - if the peer's list is a singleton, we accept a valid value;
+ * - if we are the server, we first try to see if the peer (the
+ * client) advertises the default value. If yes, we use it,
+ * otherwise we accept the preferred value;
+ * - else if we are the client, we use the first list element.
+ */
+ if (dccp_feat_clone_sp_val(&fval, val, 1))
+ return DCCP_RESET_CODE_TOO_BUSY;
+
+ if (len > 1 && server) {
+ defval = dccp_feat_default_value(feat);
+ if (dccp_feat_preflist_match(&defval, 1, val, len) > -1)
+ fval.sp.vec[0] = defval;
+ } else if (!dccp_feat_is_valid_sp_val(feat, fval.sp.vec[0])) {
+ kfree(fval.sp.vec);
+ goto unknown_feature_or_value;
+ }
- if (opt->dccpop_sc != NULL) {
- BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
- kfree(opt->dccpop_sc->dccpoc_val);
- kfree(opt->dccpop_sc);
+ /* Treat unsupported CCIDs like invalid values */
+ if (feat == DCCPF_CCID && !ccid_support_check(fval.sp.vec, 1)) {
+ kfree(fval.sp.vec);
+ goto not_valid_or_not_known;
}
- kfree(opt);
+ return dccp_feat_push_confirm(fn, feat, local, &fval);
+
+ } else if (entry->state == FEAT_UNSTABLE) { /* 6.6.2 */
+ return 0;
}
- INIT_LIST_HEAD(&dmsk->dccpms_pending);
- list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
- BUG_ON(opt == NULL);
- if (opt->dccpop_val != NULL)
- kfree(opt->dccpop_val);
- kfree(opt);
+ if (dccp_feat_reconcile(&entry->val, val, len, server, true)) {
+ entry->empty_confirm = 0;
+ } else if (is_mandatory) {
+ return DCCP_RESET_CODE_MANDATORY_ERROR;
+ } else if (entry->state == FEAT_INITIALISING) {
+ /*
+ * Failed simultaneous negotiation (server only): try to `save'
+ * the connection by checking whether entry contains the default
+ * value for @feat. If yes, send an empty Confirm to signal that
+ * the received Change was not understood - which implies using
+ * the default value.
+ * If this also fails, we use Reset as the last resort.
+ */
+ WARN_ON(!server);
+ defval = dccp_feat_default_value(feat);
+ if (!dccp_feat_reconcile(&entry->val, &defval, 1, server, true))
+ return DCCP_RESET_CODE_OPTION_ERROR;
+ entry->empty_confirm = 1;
}
- INIT_LIST_HEAD(&dmsk->dccpms_conf);
-}
+ entry->needs_confirm = 1;
+ entry->needs_mandatory = 0;
+ entry->state = FEAT_STABLE;
+ return 0;
-EXPORT_SYMBOL_GPL(dccp_feat_clean);
+unknown_feature_or_value:
+ if (!is_mandatory)
+ return dccp_push_empty_confirm(fn, feat, local);
-/* this is to be called only when a listening sock creates its child. It is
- * assumed by the function---the confirm is not duplicated, but rather it is
- * "passed on".
+not_valid_or_not_known:
+ return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR
+ : DCCP_RESET_CODE_OPTION_ERROR;
+}
+
+/**
+ * dccp_feat_confirm_recv - Process received Confirm options
+ * @fn: feature-negotiation list to update
+ * @is_mandatory: whether @opt was preceded by a Mandatory option
+ * @opt: %DCCPO_CONFIRM_L or %DCCPO_CONFIRM_R
+ * @feat: one of %dccp_feature_numbers
+ * @val: NN value or SP value/preference list
+ * @len: length of @val in bytes
+ * @server: whether this node is server (1) or client (0)
*/
-int dccp_feat_clone(struct sock *oldsk, struct sock *newsk)
+static u8 dccp_feat_confirm_recv(struct list_head *fn, u8 is_mandatory, u8 opt,
+ u8 feat, u8 *val, u8 len, const bool server)
{
- struct dccp_minisock *olddmsk = dccp_msk(oldsk);
- struct dccp_minisock *newdmsk = dccp_msk(newsk);
- struct dccp_opt_pend *opt;
- int rc = 0;
+ u8 *plist, plen, type = dccp_feat_type(feat);
+ const bool local = (opt == DCCPO_CONFIRM_R);
+ struct dccp_feat_entry *entry = dccp_feat_list_lookup(fn, feat, local);
- INIT_LIST_HEAD(&newdmsk->dccpms_pending);
- INIT_LIST_HEAD(&newdmsk->dccpms_conf);
+ if (entry == NULL) { /* nothing queued: ignore or handle error */
+ if (is_mandatory && type == FEAT_UNKNOWN)
+ return DCCP_RESET_CODE_MANDATORY_ERROR;
+
+ if (!local && type == FEAT_NN) /* 6.3.2 */
+ goto confirmation_failed;
+ return 0;
+ }
- list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) {
- struct dccp_opt_pend *newopt;
- /* copy the value of the option */
- u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC);
+ if (entry->state != FEAT_CHANGING) /* 6.6.2 */
+ return 0;
- if (val == NULL)
- goto out_clean;
+ if (len == 0) {
+ if (dccp_feat_must_be_understood(feat)) /* 6.6.7 */
+ goto confirmation_failed;
+ /*
+ * Empty Confirm during connection setup: this means reverting
+ * to the `old' value, which in this case is the default. Since
+ * we handle default values automatically when no other values
+ * have been set, we revert to the old value by removing this
+ * entry from the list.
+ */
+ dccp_feat_list_pop(entry);
+ return 0;
+ }
- newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC);
- if (newopt == NULL) {
- kfree(val);
- goto out_clean;
- }
+ if (type == FEAT_NN) {
+ if (len > sizeof(entry->val.nn))
+ goto confirmation_failed;
- /* insert the option */
- newopt->dccpop_val = val;
- list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending);
+ if (entry->val.nn == dccp_decode_value_var(val, len))
+ goto confirmation_succeeded;
- /* XXX what happens with backlogs and multiple connections at
- * once...
- */
- /* the master socket no longer needs to worry about confirms */
- opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */
+ DCCP_WARN("Bogus Confirm for non-existing value\n");
+ goto confirmation_failed;
+ }
- /* reset state for a new socket */
- opt->dccpop_conf = 0;
+ /*
+ * Parsing SP Confirms: the first element of @val is the preferred
+ * SP value which the peer confirms, the remainder depends on @len.
+ * Note that only the confirmed value need to be a valid SP value.
+ */
+ if (!dccp_feat_is_valid_sp_val(feat, *val))
+ goto confirmation_failed;
+
+ if (len == 1) { /* peer didn't supply a preference list */
+ plist = val;
+ plen = len;
+ } else { /* preferred value + preference list */
+ plist = val + 1;
+ plen = len - 1;
}
- /* XXX not doing anything about the conf queue */
+ /* Check whether the peer got the reconciliation right (6.6.8) */
+ if (dccp_feat_reconcile(&entry->val, plist, plen, server, 0) != *val) {
+ DCCP_WARN("Confirm selected the wrong value %u\n", *val);
+ return DCCP_RESET_CODE_OPTION_ERROR;
+ }
+ entry->val.sp.vec[0] = *val;
-out:
- return rc;
+confirmation_succeeded:
+ entry->state = FEAT_STABLE;
+ return 0;
-out_clean:
- dccp_feat_clean(newdmsk);
- rc = -ENOMEM;
- goto out;
+confirmation_failed:
+ DCCP_WARN("Confirmation failed\n");
+ return is_mandatory ? DCCP_RESET_CODE_MANDATORY_ERROR
+ : DCCP_RESET_CODE_OPTION_ERROR;
}
-EXPORT_SYMBOL_GPL(dccp_feat_clone);
-
-static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat,
- u8 *val, u8 len)
+/**
+ * dccp_feat_parse_options - Process Feature-Negotiation Options
+ * @sk: for general use and used by the client during connection setup
+ * @dreq: used by the server during connection setup
+ * @mandatory: whether @opt was preceded by a Mandatory option
+ * @opt: %DCCPO_CHANGE_L | %DCCPO_CHANGE_R | %DCCPO_CONFIRM_L | %DCCPO_CONFIRM_R
+ * @feat: one of %dccp_feature_numbers
+ * @val: value contents of @opt
+ * @len: length of @val in bytes
+ * Returns 0 on success, a Reset code for ending the connection otherwise.
+ */
+int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
+ u8 mandatory, u8 opt, u8 feat, u8 *val, u8 len)
{
- int rc = -ENOMEM;
- u8 *copy = kmemdup(val, len, GFP_KERNEL);
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct list_head *fn = dreq ? &dreq->dreq_featneg : &dp->dccps_featneg;
+ bool server = false;
- if (copy != NULL) {
- rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL);
- if (rc)
- kfree(copy);
+ switch (sk->sk_state) {
+ /*
+ * Negotiation during connection setup
+ */
+ case DCCP_LISTEN:
+ server = true; /* fall through */
+ case DCCP_REQUESTING:
+ switch (opt) {
+ case DCCPO_CHANGE_L:
+ case DCCPO_CHANGE_R:
+ return dccp_feat_change_recv(fn, mandatory, opt, feat,
+ val, len, server);
+ case DCCPO_CONFIRM_R:
+ case DCCPO_CONFIRM_L:
+ return dccp_feat_confirm_recv(fn, mandatory, opt, feat,
+ val, len, server);
+ }
}
- return rc;
+ return 0; /* ignore FN options in all other states */
}
-int dccp_feat_init(struct dccp_minisock *dmsk)
+int dccp_feat_init(struct sock *sk)
{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
int rc;
- INIT_LIST_HEAD(&dmsk->dccpms_pending);
- INIT_LIST_HEAD(&dmsk->dccpms_conf);
-
- /* CCID L */
- rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID,
- &dmsk->dccpms_tx_ccid, 1);
- if (rc)
- goto out;
-
- /* CCID R */
- rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID,
- &dmsk->dccpms_rx_ccid, 1);
- if (rc)
- goto out;
+ INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */
+ INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */
/* Ack ratio */
- rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO,
- &dmsk->dccpms_ack_ratio, 1);
-out:
+ rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0,
+ dp->dccps_l_ack_ratio);
return rc;
}
EXPORT_SYMBOL_GPL(dccp_feat_init);
+int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_feat_entry *cur, *next;
+ int idx;
+ dccp_feat_val *fvals[DCCP_FEAT_SUPPORTED_MAX][2] = {
+ [0 ... DCCP_FEAT_SUPPORTED_MAX-1] = { NULL, NULL }
+ };
+
+ list_for_each_entry(cur, fn_list, node) {
+ /*
+ * An empty Confirm means that either an unknown feature type
+ * or an invalid value was present. In the first case there is
+ * nothing to activate, in the other the default value is used.
+ */
+ if (cur->empty_confirm)
+ continue;
+
+ idx = dccp_feat_index(cur->feat_num);
+ if (idx < 0) {
+ DCCP_BUG("Unknown feature %u", cur->feat_num);
+ goto activation_failed;
+ }
+ if (cur->state != FEAT_STABLE) {
+ DCCP_CRIT("Negotiation of %s %u failed in state %u",
+ cur->is_local ? "local" : "remote",
+ cur->feat_num, cur->state);
+ goto activation_failed;
+ }
+ fvals[idx][cur->is_local] = &cur->val;
+ }
+
+ /*
+ * Activate in decreasing order of index, so that the CCIDs are always
+ * activated as the last feature. This avoids the case where a CCID
+ * relies on the initialisation of one or more features that it depends
+ * on (e.g. Send NDP Count, Send Ack Vector, and Ack Ratio features).
+ */
+ for (idx = DCCP_FEAT_SUPPORTED_MAX; --idx >= 0;)
+ if (__dccp_feat_activate(sk, idx, 0, fvals[idx][0]) ||
+ __dccp_feat_activate(sk, idx, 1, fvals[idx][1])) {
+ DCCP_CRIT("Could not activate %d", idx);
+ goto activation_failed;
+ }
+
+ /* Clean up Change options which have been confirmed already */
+ list_for_each_entry_safe(cur, next, fn_list, node)
+ if (!cur->needs_confirm)
+ dccp_feat_list_pop(cur);
+
+ dccp_pr_debug("Activation OK\n");
+ return 0;
+
+activation_failed:
+ /*
+ * We clean up everything that may have been allocated, since
+ * it is difficult to track at which stage negotiation failed.
+ * This is ok, since all allocation functions below are robust
+ * against NULL arguments.
+ */
+ ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+ dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
+ dp->dccps_hc_rx_ackvec = NULL;
+ return -1;
+}
+
#ifdef CONFIG_IP_DCCP_DEBUG
const char *dccp_feat_typename(const u8 type)
{
@@ -639,6 +1233,8 @@ const char *dccp_feat_name(const u8 feat)
if (feat > DCCPF_DATA_CHECKSUM && feat < DCCPF_MIN_CCID_SPECIFIC)
return feature_names[DCCPF_RESERVED];
+ if (feat == DCCPF_SEND_LEV_RATE)
+ return "Send Loss Event Rate";
if (feat >= DCCPF_MIN_CCID_SPECIFIC)
return "CCID-specific";
diff --git a/net/dccp/feat.h b/net/dccp/feat.h
index e272222c7ace..9b46e2a7866e 100644
--- a/net/dccp/feat.h
+++ b/net/dccp/feat.h
@@ -3,17 +3,103 @@
/*
* net/dccp/feat.h
*
- * An implementation of the DCCP protocol
+ * Feature negotiation for the DCCP protocol (RFC 4340, section 6)
+ * Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk>
* Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.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.
+ * This program is free software; you can 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/types.h>
#include "dccp.h"
+/*
+ * Known limit values
+ */
+/* Ack Ratio takes 2-byte integer values (11.3) */
+#define DCCPF_ACK_RATIO_MAX 0xFFFF
+/* Wmin=32 and Wmax=2^46-1 from 7.5.2 */
+#define DCCPF_SEQ_WMIN 32
+#define DCCPF_SEQ_WMAX 0x3FFFFFFFFFFFull
+/* Maximum number of SP values that fit in a single (Confirm) option */
+#define DCCP_FEAT_MAX_SP_VALS (DCCP_SINGLE_OPT_MAXLEN - 2)
+
+enum dccp_feat_type {
+ FEAT_AT_RX = 1, /* located at RX side of half-connection */
+ FEAT_AT_TX = 2, /* located at TX side of half-connection */
+ FEAT_SP = 4, /* server-priority reconciliation (6.3.1) */
+ FEAT_NN = 8, /* non-negotiable reconciliation (6.3.2) */
+ FEAT_UNKNOWN = 0xFF /* not understood or invalid feature */
+};
+
+enum dccp_feat_state {
+ FEAT_DEFAULT = 0, /* using default values from 6.4 */
+ FEAT_INITIALISING, /* feature is being initialised */
+ FEAT_CHANGING, /* Change sent but not confirmed yet */
+ FEAT_UNSTABLE, /* local modification in state CHANGING */
+ FEAT_STABLE /* both ends (think they) agree */
+};
+
+/**
+ * dccp_feat_val - Container for SP or NN feature values
+ * @nn: single NN value
+ * @sp.vec: single SP value plus optional preference list
+ * @sp.len: length of @sp.vec in bytes
+ */
+typedef union {
+ u64 nn;
+ struct {
+ u8 *vec;
+ u8 len;
+ } sp;
+} dccp_feat_val;
+
+/**
+ * struct feat_entry - Data structure to perform feature negotiation
+ * @val: feature's current value (SP features may have preference list)
+ * @state: feature's current state
+ * @feat_num: one of %dccp_feature_numbers
+ * @needs_mandatory: whether Mandatory options should be sent
+ * @needs_confirm: whether to send a Confirm instead of a Change
+ * @empty_confirm: whether to send an empty Confirm (depends on @needs_confirm)
+ * @is_local: feature location (1) or feature-remote (0)
+ * @node: list pointers, entries arranged in FIFO order
+ */
+struct dccp_feat_entry {
+ dccp_feat_val val;
+ enum dccp_feat_state state:8;
+ u8 feat_num;
+
+ bool needs_mandatory,
+ needs_confirm,
+ empty_confirm,
+ is_local;
+
+ struct list_head node;
+};
+
+static inline u8 dccp_feat_genopt(struct dccp_feat_entry *entry)
+{
+ if (entry->needs_confirm)
+ return entry->is_local ? DCCPO_CONFIRM_L : DCCPO_CONFIRM_R;
+ return entry->is_local ? DCCPO_CHANGE_L : DCCPO_CHANGE_R;
+}
+
+/**
+ * struct ccid_dependency - Track changes resulting from choosing a CCID
+ * @dependent_feat: one of %dccp_feature_numbers
+ * @is_local: local (1) or remote (0) @dependent_feat
+ * @is_mandatory: whether presence of @dependent_feat is mission-critical or not
+ * @val: corresponding default value for @dependent_feat (u8 is sufficient here)
+ */
+struct ccid_dependency {
+ u8 dependent_feat;
+ bool is_local:1,
+ is_mandatory:1;
+ u8 val;
+};
+
#ifdef CONFIG_IP_DCCP_DEBUG
extern const char *dccp_feat_typename(const u8 type);
extern const char *dccp_feat_name(const u8 feat);
@@ -27,14 +113,30 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val)
#define dccp_feat_debug(type, feat, val)
#endif /* CONFIG_IP_DCCP_DEBUG */
-extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
- u8 *val, u8 len, gfp_t gfp);
-extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature,
- u8 *val, u8 len);
-extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
- u8 *val, u8 len);
-extern void dccp_feat_clean(struct dccp_minisock *dmsk);
-extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk);
-extern int dccp_feat_init(struct dccp_minisock *dmsk);
+extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
+ u8 const *list, u8 len);
+extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
+extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
+ u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
+extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
+extern int dccp_feat_init(struct sock *sk);
+
+/*
+ * Encoding variable-length options and their maximum length.
+ *
+ * This affects NN options (SP options are all u8) and other variable-length
+ * options (see table 3 in RFC 4340). The limit is currently given the Sequence
+ * Window NN value (sec. 7.5.2) and the NDP count (sec. 7.7) option, all other
+ * options consume less than 6 bytes (timestamps are 4 bytes).
+ * When updating this constant (e.g. due to new internet drafts / RFCs), make
+ * sure that you also update all code which refers to it.
+ */
+#define DCCP_OPTVAL_MAXLEN 6
+
+extern void dccp_encode_value_var(const u64 value, u8 *to, const u8 len);
+extern u64 dccp_decode_value_var(const u8 *bf, const u8 len);
+extern int dccp_insert_option_mandatory(struct sk_buff *skb);
+extern int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat,
+ u8 *val, u8 len, bool repeat_first);
#endif /* _DCCP_FEAT_H */
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 779d0ed9ae94..5eb443f656c1 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -163,7 +163,7 @@ static void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
- if (dccp_msk(sk)->dccpms_send_ack_vector)
+ if (dp->dccps_hc_rx_ackvec != NULL)
dccp_ackvec_check_rcv_ackno(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_ack_seq);
}
@@ -375,7 +375,7 @@ int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- if (dccp_msk(sk)->dccpms_send_ack_vector &&
+ if (dp->dccps_hc_rx_ackvec != NULL &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
@@ -421,20 +421,19 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
goto out_invalid_packet;
}
+ /*
+ * If option processing (Step 8) failed, return 1 here so that
+ * dccp_v4_do_rcv() sends a Reset. The Reset code depends on
+ * the option type and is set in dccp_parse_options().
+ */
if (dccp_parse_options(sk, NULL, skb))
- goto out_invalid_packet;
+ return 1;
/* Obtain usec RTT sample from SYN exchange (used by CCID 3) */
if (likely(dp->dccps_options_received.dccpor_timestamp_echo))
dp->dccps_syn_rtt = dccp_sample_rtt(sk, 10 * (tstamp -
dp->dccps_options_received.dccpor_timestamp_echo));
- if (dccp_msk(sk)->dccpms_send_ack_vector &&
- dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
- DCCP_SKB_CB(skb)->dccpd_seq,
- DCCP_ACKVEC_STATE_RECEIVED))
- goto out_invalid_packet; /* FIXME: change error code */
-
/* Stop the REQUEST timer */
inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
WARN_ON(sk->sk_send_head == NULL);
@@ -475,6 +474,15 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
*/
dccp_set_state(sk, DCCP_PARTOPEN);
+ /*
+ * If feature negotiation was successful, activate features now;
+ * an activation failure means that this host could not activate
+ * one ore more features (e.g. insufficient memory), which would
+ * leave at least one feature in an undefined state.
+ */
+ if (dccp_feat_activate_values(sk, &dp->dccps_featneg))
+ goto unable_to_proceed;
+
/* Make sure socket is routed, for correct metrics. */
icsk->icsk_af_ops->rebuild_header(sk);
@@ -509,6 +517,16 @@ out_invalid_packet:
/* dccp_v4_do_rcv will send a reset */
DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_PACKET_ERROR;
return 1;
+
+unable_to_proceed:
+ DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_ABORTED;
+ /*
+ * We mark this socket as no longer usable, so that the loop in
+ * dccp_sendmsg() terminates and the application gets notified.
+ */
+ dccp_set_state(sk, DCCP_CLOSED);
+ sk->sk_err = ECOMM;
+ return 1;
}
static int dccp_rcv_respond_partopen_state_process(struct sock *sk,
@@ -590,8 +608,6 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (inet_csk(sk)->icsk_af_ops->conn_request(sk,
skb) < 0)
return 1;
-
- /* FIXME: do congestion control initialization */
goto discard;
}
if (dh->dccph_type == DCCP_PKT_RESET)
@@ -602,7 +618,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
return 1;
}
- if (sk->sk_state != DCCP_REQUESTING) {
+ if (sk->sk_state != DCCP_REQUESTING && sk->sk_state != DCCP_RESPOND) {
if (dccp_check_seqno(sk, skb))
goto discard;
@@ -615,7 +631,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- if (dccp_msk(sk)->dccpms_send_ack_vector &&
+ if (dp->dccps_hc_rx_ackvec != NULL &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
@@ -667,8 +683,6 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
return 1;
case DCCP_REQUESTING:
- /* FIXME: do congestion control initialization */
-
queued = dccp_rcv_request_sent_state_process(sk, skb, dh, len);
if (queued >= 0)
return queued;
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index e3dfddab21cc..d1dd95289b89 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -545,6 +545,7 @@ out:
static void dccp_v4_reqsk_destructor(struct request_sock *req)
{
+ dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
kfree(inet_rsk(req)->opt);
}
@@ -595,7 +596,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (req == NULL)
goto drop;
- dccp_reqsk_init(req, skb);
+ if (dccp_reqsk_init(req, dccp_sk(sk), skb))
+ goto drop_and_free;
dreq = dccp_rsk(req);
if (dccp_parse_options(sk, dreq, skb))
@@ -792,12 +794,10 @@ static int dccp_v4_rcv(struct sk_buff *skb)
DCCP_SKB_CB(skb)->dccpd_seq = dccp_hdr_seq(dh);
DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
- dccp_pr_debug("%8.8s "
- "src=%u.%u.%u.%u@%-5d "
- "dst=%u.%u.%u.%u@%-5d seq=%llu",
+ dccp_pr_debug("%8.8s src=%pI4@%-5d dst=%pI4@%-5d seq=%llu",
dccp_packet_name(dh->dccph_type),
- NIPQUAD(iph->saddr), ntohs(dh->dccph_sport),
- NIPQUAD(iph->daddr), ntohs(dh->dccph_dport),
+ &iph->saddr, ntohs(dh->dccph_sport),
+ &iph->daddr, ntohs(dh->dccph_dport),
(unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
if (dccp_packet_without_ack(skb)) {
@@ -938,6 +938,7 @@ static struct proto dccp_v4_prot = {
.orphan_count = &dccp_orphan_count,
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp_sock),
+ .slab_flags = SLAB_DESTROY_BY_RCU,
.rsk_prot = &dccp_request_sock_ops,
.twsk_prot = &dccp_timewait_sock_ops,
.h.hashinfo = &dccp_hashinfo,
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index d4ce1224e008..b963f35c65f6 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -168,7 +168,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
goto out;
}
- err = xfrm_lookup(&dst, &fl, sk, 0);
+ err = xfrm_lookup(net, &dst, &fl, sk, 0);
if (err < 0) {
sk->sk_err_soft = -err;
goto out;
@@ -279,7 +279,7 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req)
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- err = xfrm_lookup(&dst, &fl, sk, 0);
+ err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0);
if (err < 0)
goto done;
@@ -304,6 +304,7 @@ done:
static void dccp_v6_reqsk_destructor(struct request_sock *req)
{
+ dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
if (inet6_rsk(req)->pktopts != NULL)
kfree_skb(inet6_rsk(req)->pktopts);
}
@@ -342,7 +343,7 @@ static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
/* sk = NULL, but it is safe for now. RST socket required. */
if (!ip6_dst_lookup(ctl_sk, &skb->dst, &fl)) {
- if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
+ if (xfrm_lookup(net, &skb->dst, &fl, NULL, 0) >= 0) {
ip6_xmit(ctl_sk, skb, &fl, NULL, 0);
DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
@@ -426,7 +427,8 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (req == NULL)
goto drop;
- dccp_reqsk_init(req, skb);
+ if (dccp_reqsk_init(req, dccp_sk(sk), skb))
+ goto drop_and_free;
dreq = dccp_rsk(req);
if (dccp_parse_options(sk, dreq, skb))
@@ -567,7 +569,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
goto out;
}
@@ -1002,7 +1004,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT);
+ err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
if (err < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
@@ -1138,6 +1140,7 @@ static struct proto dccp_v6_prot = {
.orphan_count = &dccp_orphan_count,
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp6_sock),
+ .slab_flags = SLAB_DESTROY_BY_RCU,
.rsk_prot = &dccp6_request_sock_ops,
.twsk_prot = &dccp6_timewait_sock_ops,
.h.hashinfo = &dccp_hashinfo,
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index e6bf99e3e41a..6821ae33dd37 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -45,11 +45,6 @@ EXPORT_SYMBOL_GPL(dccp_death_row);
void dccp_minisock_init(struct dccp_minisock *dmsk)
{
dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window;
- dmsk->dccpms_rx_ccid = sysctl_dccp_feat_rx_ccid;
- dmsk->dccpms_tx_ccid = sysctl_dccp_feat_tx_ccid;
- dmsk->dccpms_ack_ratio = sysctl_dccp_feat_ack_ratio;
- dmsk->dccpms_send_ack_vector = sysctl_dccp_feat_send_ack_vector;
- dmsk->dccpms_send_ndp_count = sysctl_dccp_feat_send_ndp_count;
}
void dccp_time_wait(struct sock *sk, int state, int timeo)
@@ -112,7 +107,7 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
struct sock *newsk = inet_csk_clone(sk, req, GFP_ATOMIC);
if (newsk != NULL) {
- const struct dccp_request_sock *dreq = dccp_rsk(req);
+ struct dccp_request_sock *dreq = dccp_rsk(req);
struct inet_connection_sock *newicsk = inet_csk(newsk);
struct dccp_sock *newdp = dccp_sk(newsk);
struct dccp_minisock *newdmsk = dccp_msk(newsk);
@@ -125,35 +120,7 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
newdp->dccps_timestamp_time = dreq->dreq_timestamp_time;
newicsk->icsk_rto = DCCP_TIMEOUT_INIT;
- if (dccp_feat_clone(sk, newsk))
- goto out_free;
-
- if (newdmsk->dccpms_send_ack_vector) {
- newdp->dccps_hc_rx_ackvec =
- dccp_ackvec_alloc(GFP_ATOMIC);
- if (unlikely(newdp->dccps_hc_rx_ackvec == NULL))
- goto out_free;
- }
-
- newdp->dccps_hc_rx_ccid =
- ccid_hc_rx_new(newdmsk->dccpms_rx_ccid,
- newsk, GFP_ATOMIC);
- newdp->dccps_hc_tx_ccid =
- ccid_hc_tx_new(newdmsk->dccpms_tx_ccid,
- newsk, GFP_ATOMIC);
- if (unlikely(newdp->dccps_hc_rx_ccid == NULL ||
- newdp->dccps_hc_tx_ccid == NULL)) {
- dccp_ackvec_free(newdp->dccps_hc_rx_ackvec);
- ccid_hc_rx_delete(newdp->dccps_hc_rx_ccid, newsk);
- ccid_hc_tx_delete(newdp->dccps_hc_tx_ccid, newsk);
-out_free:
- /* It is still raw copy of parent, so invalidate
- * destructor and make plain sk_free() */
- newsk->sk_destruct = NULL;
- sk_free(newsk);
- return NULL;
- }
-
+ INIT_LIST_HEAD(&newdp->dccps_featneg);
/*
* Step 3: Process LISTEN state
*
@@ -184,6 +151,17 @@ out_free:
dccp_set_seqno(&newdp->dccps_awl,
max48(newdp->dccps_awl, newdp->dccps_iss));
+ /*
+ * Activate features after initialising the sequence numbers,
+ * since CCID initialisation may depend on GSS, ISR, ISS etc.
+ */
+ if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
+ /* It is still raw copy of parent, so invalidate
+ * destructor and make plain sk_free() */
+ newsk->sk_destruct = NULL;
+ sk_free(newsk);
+ return NULL;
+ }
dccp_init_xmit_timers(newsk);
DCCP_INC_STATS_BH(DCCP_MIB_PASSIVEOPENS);
@@ -304,7 +282,8 @@ void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
EXPORT_SYMBOL_GPL(dccp_reqsk_send_ack);
-void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb)
+int dccp_reqsk_init(struct request_sock *req,
+ struct dccp_sock const *dp, struct sk_buff const *skb)
{
struct dccp_request_sock *dreq = dccp_rsk(req);
@@ -313,6 +292,9 @@ void dccp_reqsk_init(struct request_sock *req, struct sk_buff *skb)
inet_rsk(req)->acked = 0;
req->rcv_wnd = sysctl_dccp_feat_sequence_window;
dreq->dreq_timestamp_echo = 0;
+
+ /* inherit feature negotiation options from listening socket */
+ return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg);
}
EXPORT_SYMBOL_GPL(dccp_reqsk_init);
diff --git a/net/dccp/options.c b/net/dccp/options.c
index 0809b63cb055..7b1165c21f51 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -26,20 +26,21 @@
int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW;
int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID;
int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID;
-int sysctl_dccp_feat_ack_ratio = DCCPF_INITIAL_ACK_RATIO;
-int sysctl_dccp_feat_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR;
-int sysctl_dccp_feat_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT;
-static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len)
+u64 dccp_decode_value_var(const u8 *bf, const u8 len)
{
- u32 value = 0;
+ u64 value = 0;
+ if (len >= DCCP_OPTVAL_MAXLEN)
+ value += ((u64)*bf++) << 40;
+ if (len > 4)
+ value += ((u64)*bf++) << 32;
if (len > 3)
- value += *bf++ << 24;
+ value += ((u64)*bf++) << 24;
if (len > 2)
- value += *bf++ << 16;
+ value += ((u64)*bf++) << 16;
if (len > 1)
- value += *bf++ << 8;
+ value += ((u64)*bf++) << 8;
if (len > 0)
value += *bf;
@@ -64,7 +65,7 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
(dh->dccph_doff * 4);
struct dccp_options_received *opt_recv = &dp->dccps_options_received;
unsigned char opt, len;
- unsigned char *value;
+ unsigned char *uninitialized_var(value);
u32 elapsed_time;
__be32 opt_val;
int rc;
@@ -131,41 +132,19 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk),
(unsigned long long)opt_recv->dccpor_ndp);
break;
- case DCCPO_CHANGE_L:
- /* fall through */
- case DCCPO_CHANGE_R:
- if (pkt_type == DCCP_PKT_DATA)
+ case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R:
+ if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */
break;
- if (len < 2)
- goto out_invalid_option;
- rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
- len - 1);
- /*
- * When there is a change error, change_recv is
- * responsible for dealing with it. i.e. reply with an
- * empty confirm.
- * If the change was mandatory, then we need to die.
- */
- if (rc && mandatory)
- goto out_invalid_option;
- break;
- case DCCPO_CONFIRM_L:
- /* fall through */
- case DCCPO_CONFIRM_R:
- if (pkt_type == DCCP_PKT_DATA)
- break;
- if (len < 2) /* FIXME this disallows empty confirm */
- goto out_invalid_option;
- if (dccp_feat_confirm_recv(sk, opt, *value,
- value + 1, len - 1))
- goto out_invalid_option;
+ rc = dccp_feat_parse_options(sk, dreq, mandatory, opt,
+ *value, value + 1, len - 1);
+ if (rc)
+ goto out_featneg_failed;
break;
case DCCPO_ACK_VECTOR_0:
case DCCPO_ACK_VECTOR_1:
if (dccp_packet_without_ack(skb)) /* RFC 4340, 11.4 */
break;
-
- if (dccp_msk(sk)->dccpms_send_ack_vector &&
+ if (dp->dccps_hc_rx_ackvec != NULL &&
dccp_ackvec_parse(sk, skb, &ackno, opt, value, len))
goto out_invalid_option;
break;
@@ -289,8 +268,10 @@ out_nonsensical_length:
out_invalid_option:
DCCP_INC_STATS_BH(DCCP_MIB_INVALIDOPT);
- DCCP_SKB_CB(skb)->dccpd_reset_code = DCCP_RESET_CODE_OPTION_ERROR;
- DCCP_WARN("DCCP(%p): invalid option %d, len=%d", sk, opt, len);
+ rc = DCCP_RESET_CODE_OPTION_ERROR;
+out_featneg_failed:
+ DCCP_WARN("DCCP(%p): Option %d (len=%d) error=%u\n", sk, opt, len, rc);
+ DCCP_SKB_CB(skb)->dccpd_reset_code = rc;
DCCP_SKB_CB(skb)->dccpd_reset_data[0] = opt;
DCCP_SKB_CB(skb)->dccpd_reset_data[1] = len > 0 ? value[0] : 0;
DCCP_SKB_CB(skb)->dccpd_reset_data[2] = len > 1 ? value[1] : 0;
@@ -299,9 +280,12 @@ out_invalid_option:
EXPORT_SYMBOL_GPL(dccp_parse_options);
-static void dccp_encode_value_var(const u32 value, unsigned char *to,
- const unsigned int len)
+void dccp_encode_value_var(const u64 value, u8 *to, const u8 len)
{
+ if (len >= DCCP_OPTVAL_MAXLEN)
+ *to++ = (value & 0xFF0000000000ull) >> 40;
+ if (len > 4)
+ *to++ = (value & 0xFF00000000ull) >> 32;
if (len > 3)
*to++ = (value & 0xFF000000) >> 24;
if (len > 2)
@@ -461,23 +445,61 @@ static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,
return 0;
}
-static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat,
- u8 *val, u8 len)
+/**
+ * dccp_insert_option_mandatory - Mandatory option (5.8.2)
+ * Note that since we are using skb_push, this function needs to be called
+ * _after_ inserting the option it is supposed to influence (stack order).
+ */
+int dccp_insert_option_mandatory(struct sk_buff *skb)
+{
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len >= DCCP_MAX_OPT_LEN)
+ return -1;
+
+ DCCP_SKB_CB(skb)->dccpd_opt_len++;
+ *skb_push(skb, 1) = DCCPO_MANDATORY;
+ return 0;
+}
+
+/**
+ * dccp_insert_fn_opt - Insert single Feature-Negotiation option into @skb
+ * @type: %DCCPO_CHANGE_L, %DCCPO_CHANGE_R, %DCCPO_CONFIRM_L, %DCCPO_CONFIRM_R
+ * @feat: one out of %dccp_feature_numbers
+ * @val: NN value or SP array (preferred element first) to copy
+ * @len: true length of @val in bytes (excluding first element repetition)
+ * @repeat_first: whether to copy the first element of @val twice
+ * The last argument is used to construct Confirm options, where the preferred
+ * value and the preference list appear separately (RFC 4340, 6.3.1). Preference
+ * lists are kept such that the preferred entry is always first, so we only need
+ * to copy twice, and avoid the overhead of cloning into a bigger array.
+ */
+int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat,
+ u8 *val, u8 len, bool repeat_first)
{
- u8 *to;
+ u8 tot_len, *to;
- if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) {
- DCCP_WARN("packet too small for feature %d option!\n", feat);
+ /* take the `Feature' field and possible repetition into account */
+ if (len > (DCCP_SINGLE_OPT_MAXLEN - 2)) {
+ DCCP_WARN("length %u for feature %u too large\n", len, feat);
return -1;
}
- DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3;
+ if (unlikely(val == NULL || len == 0))
+ len = repeat_first = 0;
+ tot_len = 3 + repeat_first + len;
+
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + tot_len > DCCP_MAX_OPT_LEN) {
+ DCCP_WARN("packet too small for feature %d option!\n", feat);
+ return -1;
+ }
+ DCCP_SKB_CB(skb)->dccpd_opt_len += tot_len;
- to = skb_push(skb, len + 3);
+ to = skb_push(skb, tot_len);
*to++ = type;
- *to++ = len + 3;
+ *to++ = tot_len;
*to++ = feat;
+ if (repeat_first)
+ *to++ = *val;
if (len)
memcpy(to, val, len);
@@ -487,69 +509,6 @@ static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat,
return 0;
}
-static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb)
-{
- struct dccp_sock *dp = dccp_sk(sk);
- struct dccp_minisock *dmsk = dccp_msk(sk);
- struct dccp_opt_pend *opt, *next;
- int change = 0;
-
- /* confirm any options [NN opts] */
- list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
- dccp_insert_feat_opt(skb, opt->dccpop_type,
- opt->dccpop_feat, opt->dccpop_val,
- opt->dccpop_len);
- /* fear empty confirms */
- if (opt->dccpop_val)
- kfree(opt->dccpop_val);
- kfree(opt);
- }
- INIT_LIST_HEAD(&dmsk->dccpms_conf);
-
- /* see which features we need to send */
- list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
- /* see if we need to send any confirm */
- if (opt->dccpop_sc) {
- dccp_insert_feat_opt(skb, opt->dccpop_type + 1,
- opt->dccpop_feat,
- opt->dccpop_sc->dccpoc_val,
- opt->dccpop_sc->dccpoc_len);
-
- BUG_ON(!opt->dccpop_sc->dccpoc_val);
- kfree(opt->dccpop_sc->dccpoc_val);
- kfree(opt->dccpop_sc);
- opt->dccpop_sc = NULL;
- }
-
- /* any option not confirmed, re-send it */
- if (!opt->dccpop_conf) {
- dccp_insert_feat_opt(skb, opt->dccpop_type,
- opt->dccpop_feat, opt->dccpop_val,
- opt->dccpop_len);
- change++;
- }
- }
-
- /* Retransmit timer.
- * If this is the master listening sock, we don't set a timer on it. It
- * should be fine because if the dude doesn't receive our RESPONSE
- * [which will contain the CHANGE] he will send another REQUEST which
- * will "retrnasmit" the change.
- */
- if (change && dp->dccps_role != DCCP_ROLE_LISTEN) {
- dccp_pr_debug("reset feat negotiation timer %p\n", sk);
-
- /* XXX don't reset the timer on re-transmissions. I.e. reset it
- * only when sending new stuff i guess. Currently the timer
- * never backs off because on re-transmission it just resets it!
- */
- inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
- inet_csk(sk)->icsk_rto, DCCP_RTO_MAX);
- }
-
- return 0;
-}
-
/* The length of all options needs to be a multiple of 4 (5.8) */
static void dccp_insert_option_padding(struct sk_buff *skb)
{
@@ -565,19 +524,31 @@ static void dccp_insert_option_padding(struct sk_buff *skb)
int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct dccp_minisock *dmsk = dccp_msk(sk);
DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
- if (dmsk->dccpms_send_ndp_count &&
- dccp_insert_option_ndp(sk, skb))
+ if (dp->dccps_send_ndp_count && dccp_insert_option_ndp(sk, skb))
return -1;
- if (!dccp_packet_without_ack(skb)) {
- if (dmsk->dccpms_send_ack_vector &&
- dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) &&
- dccp_insert_option_ackvec(sk, skb))
+ if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA) {
+
+ /* Feature Negotiation */
+ if (dccp_feat_insert_opts(dp, NULL, skb))
return -1;
+
+ if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST) {
+ /*
+ * Obtain RTT sample from Request/Response exchange.
+ * This is currently used in CCID 3 initialisation.
+ */
+ if (dccp_insert_option_timestamp(sk, skb))
+ return -1;
+
+ } else if (dp->dccps_hc_rx_ackvec != NULL &&
+ dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) &&
+ dccp_insert_option_ackvec(sk, skb)) {
+ return -1;
+ }
}
if (dp->dccps_hc_rx_insert_options) {
@@ -586,21 +557,6 @@ int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
dp->dccps_hc_rx_insert_options = 0;
}
- /* Feature negotiation */
- /* Data packets can't do feat negotiation */
- if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA &&
- DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATAACK &&
- dccp_insert_options_feat(sk, skb))
- return -1;
-
- /*
- * Obtain RTT sample from Request/Response exchange.
- * This is currently used in CCID 3 initialisation.
- */
- if (DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_REQUEST &&
- dccp_insert_option_timestamp(sk, skb))
- return -1;
-
if (dp->dccps_timestamp_echo != 0 &&
dccp_insert_option_timestamp_echo(dp, NULL, skb))
return -1;
@@ -613,6 +569,9 @@ int dccp_insert_options_rsk(struct dccp_request_sock *dreq, struct sk_buff *skb)
{
DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
+ if (dccp_feat_insert_opts(NULL, dreq, skb))
+ return -1;
+
if (dreq->dreq_timestamp_echo != 0 &&
dccp_insert_option_timestamp_echo(NULL, dreq, skb))
return -1;
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 809d803d5006..22a618af4893 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -175,7 +175,7 @@ unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu)
* make it a multiple of 4
*/
- cur_mps -= ((5 + 6 + 10 + 6 + 6 + 6 + 3) / 4) * 4;
+ cur_mps -= roundup(5 + 6 + 10 + 6 + 6 + 6, 4);
/* And store cached results */
icsk->icsk_pmtu_cookie = pmtu;
@@ -339,10 +339,12 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE;
DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss;
- if (dccp_insert_options_rsk(dreq, skb)) {
- kfree_skb(skb);
- return NULL;
- }
+ /* Resolve feature dependencies resulting from choice of CCID */
+ if (dccp_feat_server_ccid_dependencies(dreq))
+ goto response_failed;
+
+ if (dccp_insert_options_rsk(dreq, skb))
+ goto response_failed;
/* Build and checksum header */
dh = dccp_zeroed_hdr(skb, dccp_header_size);
@@ -363,6 +365,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
inet_rsk(req)->acked = 1;
DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
return skb;
+response_failed:
+ kfree_skb(skb);
+ return NULL;
}
EXPORT_SYMBOL_GPL(dccp_make_response);
@@ -469,6 +474,10 @@ int dccp_connect(struct sock *sk)
struct sk_buff *skb;
struct inet_connection_sock *icsk = inet_csk(sk);
+ /* do not connect if feature negotiation setup fails */
+ if (dccp_feat_finalise_settings(dccp_sk(sk)))
+ return -EPROTO;
+
dccp_connect_init(sk);
skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation);
diff --git a/net/dccp/probe.c b/net/dccp/probe.c
index 81368a7f5379..37731da41481 100644
--- a/net/dccp/probe.c
+++ b/net/dccp/probe.c
@@ -74,30 +74,27 @@ static void printl(const char *fmt, ...)
static int jdccp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t size)
{
- const struct dccp_minisock *dmsk = dccp_msk(sk);
const struct inet_sock *inet = inet_sk(sk);
- const struct ccid3_hc_tx_sock *hctx;
+ struct ccid3_hc_tx_sock *hctx = NULL;
- if (dmsk->dccpms_tx_ccid == DCCPC_CCID3)
+ if (ccid_get_current_tx_ccid(dccp_sk(sk)) == DCCPC_CCID3)
hctx = ccid3_hc_tx_sk(sk);
- else
- hctx = NULL;
if (port == 0 || ntohs(inet->dport) == port ||
ntohs(inet->sport) == port) {
if (hctx)
- printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %d %d %d %u "
+ printl("%pI4:%u %pI4:%u %d %d %d %d %u "
"%llu %llu %d\n",
- NIPQUAD(inet->saddr), ntohs(inet->sport),
- NIPQUAD(inet->daddr), ntohs(inet->dport), size,
+ &inet->saddr, ntohs(inet->sport),
+ &inet->daddr, ntohs(inet->dport), size,
hctx->ccid3hctx_s, hctx->ccid3hctx_rtt,
hctx->ccid3hctx_p, hctx->ccid3hctx_x_calc,
hctx->ccid3hctx_x_recv >> 6,
hctx->ccid3hctx_x >> 6, hctx->ccid3hctx_t_ipi);
else
- printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d\n",
- NIPQUAD(inet->saddr), ntohs(inet->sport),
- NIPQUAD(inet->daddr), ntohs(inet->dport), size);
+ printl("%pI4:%u %pI4:%u %d\n",
+ &inet->saddr, ntohs(inet->sport),
+ &inet->daddr, ntohs(inet->dport), size);
}
jprobe_return();
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index d0bd34819761..1747ccae8e8d 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -40,16 +40,10 @@ DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
EXPORT_SYMBOL_GPL(dccp_statistics);
-atomic_t dccp_orphan_count = ATOMIC_INIT(0);
-
+struct percpu_counter dccp_orphan_count;
EXPORT_SYMBOL_GPL(dccp_orphan_count);
-struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
- .lhash_lock = RW_LOCK_UNLOCKED,
- .lhash_users = ATOMIC_INIT(0),
- .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
-};
-
+struct inet_hashinfo dccp_hashinfo;
EXPORT_SYMBOL_GPL(dccp_hashinfo);
/* the maximum queue length for tx in packets. 0 is no limit */
@@ -67,6 +61,9 @@ void dccp_set_state(struct sock *sk, const int state)
case DCCP_OPEN:
if (oldstate != DCCP_OPEN)
DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
+ /* Client retransmits all Confirm options until entering OPEN */
+ if (oldstate == DCCP_PARTOPEN)
+ dccp_feat_list_purge(&dccp_sk(sk)->dccps_featneg);
break;
case DCCP_CLOSED:
@@ -175,7 +172,6 @@ EXPORT_SYMBOL_GPL(dccp_state_name);
int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct dccp_minisock *dmsk = dccp_msk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
dccp_minisock_init(&dp->dccps_minisock);
@@ -193,45 +189,10 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
dccp_init_xmit_timers(sk);
- /*
- * FIXME: We're hardcoding the CCID, and doing this at this point makes
- * the listening (master) sock get CCID control blocks, which is not
- * necessary, but for now, to not mess with the test userspace apps,
- * lets leave it here, later the real solution is to do this in a
- * setsockopt(CCIDs-I-want/accept). -acme
- */
- if (likely(ctl_sock_initialized)) {
- int rc = dccp_feat_init(dmsk);
-
- if (rc)
- return rc;
-
- if (dmsk->dccpms_send_ack_vector) {
- dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
- if (dp->dccps_hc_rx_ackvec == NULL)
- return -ENOMEM;
- }
- dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
- sk, GFP_KERNEL);
- dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
- sk, GFP_KERNEL);
- if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
- dp->dccps_hc_tx_ccid == NULL)) {
- ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
- ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
- if (dmsk->dccpms_send_ack_vector) {
- dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
- dp->dccps_hc_rx_ackvec = NULL;
- }
- dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
- return -ENOMEM;
- }
- } else {
- /* control socket doesn't need feat nego */
- INIT_LIST_HEAD(&dmsk->dccpms_pending);
- INIT_LIST_HEAD(&dmsk->dccpms_conf);
- }
-
+ INIT_LIST_HEAD(&dp->dccps_featneg);
+ /* control socket doesn't need feat nego */
+ if (likely(ctl_sock_initialized))
+ return dccp_feat_init(sk);
return 0;
}
@@ -240,7 +201,6 @@ EXPORT_SYMBOL_GPL(dccp_init_sock);
void dccp_destroy_sock(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct dccp_minisock *dmsk = dccp_msk(sk);
/*
* DCCP doesn't use sk_write_queue, just sk_send_head
@@ -258,7 +218,7 @@ void dccp_destroy_sock(struct sock *sk)
kfree(dp->dccps_service_list);
dp->dccps_service_list = NULL;
- if (dmsk->dccpms_send_ack_vector) {
+ if (dp->dccps_hc_rx_ackvec != NULL) {
dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
dp->dccps_hc_rx_ackvec = NULL;
}
@@ -267,7 +227,7 @@ void dccp_destroy_sock(struct sock *sk)
dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
/* clean up feature negotiation state */
- dccp_feat_clean(dmsk);
+ dccp_feat_list_purge(&dp->dccps_featneg);
}
EXPORT_SYMBOL_GPL(dccp_destroy_sock);
@@ -277,6 +237,9 @@ static inline int dccp_listen_start(struct sock *sk, int backlog)
struct dccp_sock *dp = dccp_sk(sk);
dp->dccps_role = DCCP_ROLE_LISTEN;
+ /* do not start to listen if feature negotiation setup fails */
+ if (dccp_feat_finalise_settings(dp))
+ return -EPROTO;
return inet_csk_listen_start(sk, backlog);
}
@@ -466,42 +429,70 @@ static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
return 0;
}
-/* byte 1 is feature. the rest is the preference list */
-static int dccp_setsockopt_change(struct sock *sk, int type,
- struct dccp_so_feat __user *optval)
+static int dccp_setsockopt_cscov(struct sock *sk, int cscov, bool rx)
{
- struct dccp_so_feat opt;
- u8 *val;
- int rc;
+ u8 *list, len;
+ int i, rc;
- if (copy_from_user(&opt, optval, sizeof(opt)))
- return -EFAULT;
+ if (cscov < 0 || cscov > 15)
+ return -EINVAL;
/*
- * rfc4340: 6.1. Change Options
+ * Populate a list of permissible values, in the range cscov...15. This
+ * is necessary since feature negotiation of single values only works if
+ * both sides incidentally choose the same value. Since the list starts
+ * lowest-value first, negotiation will pick the smallest shared value.
*/
- if (opt.dccpsf_len < 1)
+ if (cscov == 0)
+ return 0;
+ len = 16 - cscov;
+
+ list = kmalloc(len, GFP_KERNEL);
+ if (list == NULL)
+ return -ENOBUFS;
+
+ for (i = 0; i < len; i++)
+ list[i] = cscov++;
+
+ rc = dccp_feat_register_sp(sk, DCCPF_MIN_CSUM_COVER, rx, list, len);
+
+ if (rc == 0) {
+ if (rx)
+ dccp_sk(sk)->dccps_pcrlen = cscov;
+ else
+ dccp_sk(sk)->dccps_pcslen = cscov;
+ }
+ kfree(list);
+ return rc;
+}
+
+static int dccp_setsockopt_ccid(struct sock *sk, int type,
+ char __user *optval, int optlen)
+{
+ u8 *val;
+ int rc = 0;
+
+ if (optlen < 1 || optlen > DCCP_FEAT_MAX_SP_VALS)
return -EINVAL;
- val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
- if (!val)
+ val = kmalloc(optlen, GFP_KERNEL);
+ if (val == NULL)
return -ENOMEM;
- if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
- rc = -EFAULT;
- goto out_free_val;
+ if (copy_from_user(val, optval, optlen)) {
+ kfree(val);
+ return -EFAULT;
}
- rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
- val, opt.dccpsf_len, GFP_KERNEL);
- if (rc)
- goto out_free_val;
+ lock_sock(sk);
+ if (type == DCCP_SOCKOPT_TX_CCID || type == DCCP_SOCKOPT_CCID)
+ rc = dccp_feat_register_sp(sk, DCCPF_CCID, 1, val, optlen);
-out:
- return rc;
+ if (!rc && (type == DCCP_SOCKOPT_RX_CCID || type == DCCP_SOCKOPT_CCID))
+ rc = dccp_feat_register_sp(sk, DCCPF_CCID, 0, val, optlen);
+ release_sock(sk);
-out_free_val:
kfree(val);
- goto out;
+ return rc;
}
static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
@@ -510,7 +501,21 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
struct dccp_sock *dp = dccp_sk(sk);
int val, err = 0;
- if (optlen < sizeof(int))
+ switch (optname) {
+ case DCCP_SOCKOPT_PACKET_SIZE:
+ DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
+ return 0;
+ case DCCP_SOCKOPT_CHANGE_L:
+ case DCCP_SOCKOPT_CHANGE_R:
+ DCCP_WARN("sockopt(CHANGE_L/R) is deprecated: fix your app\n");
+ return 0;
+ case DCCP_SOCKOPT_CCID:
+ case DCCP_SOCKOPT_RX_CCID:
+ case DCCP_SOCKOPT_TX_CCID:
+ return dccp_setsockopt_ccid(sk, optname, optval, optlen);
+ }
+
+ if (optlen < (int)sizeof(int))
return -EINVAL;
if (get_user(val, (int __user *)optval))
@@ -521,53 +526,24 @@ static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
lock_sock(sk);
switch (optname) {
- case DCCP_SOCKOPT_PACKET_SIZE:
- DCCP_WARN("sockopt(PACKET_SIZE) is deprecated: fix your app\n");
- err = 0;
- break;
- case DCCP_SOCKOPT_CHANGE_L:
- if (optlen != sizeof(struct dccp_so_feat))
- err = -EINVAL;
- else
- err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
- (struct dccp_so_feat __user *)
- optval);
- break;
- case DCCP_SOCKOPT_CHANGE_R:
- if (optlen != sizeof(struct dccp_so_feat))
- err = -EINVAL;
- else
- err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
- (struct dccp_so_feat __user *)
- optval);
- break;
case DCCP_SOCKOPT_SERVER_TIMEWAIT:
if (dp->dccps_role != DCCP_ROLE_SERVER)
err = -EOPNOTSUPP;
else
dp->dccps_server_timewait = (val != 0);
break;
- case DCCP_SOCKOPT_SEND_CSCOV: /* sender side, RFC 4340, sec. 9.2 */
- if (val < 0 || val > 15)
- err = -EINVAL;
- else
- dp->dccps_pcslen = val;
+ case DCCP_SOCKOPT_SEND_CSCOV:
+ err = dccp_setsockopt_cscov(sk, val, false);
break;
- case DCCP_SOCKOPT_RECV_CSCOV: /* receiver side, RFC 4340 sec. 9.2.1 */
- if (val < 0 || val > 15)
- err = -EINVAL;
- else {
- dp->dccps_pcrlen = val;
- /* FIXME: add feature negotiation,
- * ChangeL(MinimumChecksumCoverage, val) */
- }
+ case DCCP_SOCKOPT_RECV_CSCOV:
+ err = dccp_setsockopt_cscov(sk, val, true);
break;
default:
err = -ENOPROTOOPT;
break;
}
-
release_sock(sk);
+
return err;
}
@@ -648,6 +624,18 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
case DCCP_SOCKOPT_GET_CUR_MPS:
val = dp->dccps_mss_cache;
break;
+ case DCCP_SOCKOPT_AVAILABLE_CCIDS:
+ return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen);
+ case DCCP_SOCKOPT_TX_CCID:
+ val = ccid_get_current_tx_ccid(dp);
+ if (val < 0)
+ return -ENOPROTOOPT;
+ break;
+ case DCCP_SOCKOPT_RX_CCID:
+ val = ccid_get_current_rx_ccid(dp);
+ if (val < 0)
+ return -ENOPROTOOPT;
+ break;
case DCCP_SOCKOPT_SERVER_TIMEWAIT:
val = dp->dccps_server_timewait;
break;
@@ -976,7 +964,6 @@ adjudge_to_death:
state = sk->sk_state;
sock_hold(sk);
sock_orphan(sk);
- atomic_inc(sk->sk_prot->orphan_count);
/*
* It is the last release_sock in its life. It will remove backlog.
@@ -990,6 +977,8 @@ adjudge_to_death:
bh_lock_sock(sk);
WARN_ON(sock_owned_by_user(sk));
+ percpu_counter_inc(sk->sk_prot->orphan_count);
+
/* Have we already been destroyed by a softirq or backlog? */
if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
goto out;
@@ -1040,17 +1029,21 @@ static int __init dccp_init(void)
{
unsigned long goal;
int ehash_order, bhash_order, i;
- int rc = -ENOBUFS;
+ int rc;
BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
FIELD_SIZEOF(struct sk_buff, cb));
-
+ rc = percpu_counter_init(&dccp_orphan_count, 0);
+ if (rc)
+ goto out;
+ rc = -ENOBUFS;
+ inet_hashinfo_init(&dccp_hashinfo);
dccp_hashinfo.bind_bucket_cachep =
kmem_cache_create("dccp_bind_bucket",
sizeof(struct inet_bind_bucket), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!dccp_hashinfo.bind_bucket_cachep)
- goto out;
+ goto out_free_percpu;
/*
* Size and allocate the main established and bind bucket
@@ -1084,8 +1077,8 @@ static int __init dccp_init(void)
}
for (i = 0; i < dccp_hashinfo.ehash_size; i++) {
- INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].chain);
- INIT_HLIST_HEAD(&dccp_hashinfo.ehash[i].twchain);
+ INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i);
+ INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i);
}
if (inet_ehash_locks_alloc(&dccp_hashinfo))
@@ -1143,6 +1136,8 @@ out_free_dccp_ehash:
out_free_bind_bucket_cachep:
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
dccp_hashinfo.bind_bucket_cachep = NULL;
+out_free_percpu:
+ percpu_counter_destroy(&dccp_orphan_count);
goto out;
}
diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c
index 21295993fdb8..018e210875e1 100644
--- a/net/dccp/sysctl.c
+++ b/net/dccp/sysctl.c
@@ -41,27 +41,6 @@ static struct ctl_table dccp_default_table[] = {
.proc_handler = proc_dointvec,
},
{
- .procname = "ack_ratio",
- .data = &sysctl_dccp_feat_ack_ratio,
- .maxlen = sizeof(sysctl_dccp_feat_ack_ratio),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "send_ackvec",
- .data = &sysctl_dccp_feat_send_ack_vector,
- .maxlen = sizeof(sysctl_dccp_feat_send_ack_vector),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
- .procname = "send_ndp",
- .data = &sysctl_dccp_feat_send_ndp_count,
- .maxlen = sizeof(sysctl_dccp_feat_send_ndp_count),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- },
- {
.procname = "request_retries",
.data = &sysctl_dccp_request_retries,
.maxlen = sizeof(sysctl_dccp_request_retries),
diff --git a/net/dccp/timer.c b/net/dccp/timer.c
index 54b3c7e9e016..162d1e683c39 100644
--- a/net/dccp/timer.c
+++ b/net/dccp/timer.c
@@ -87,17 +87,6 @@ static void dccp_retransmit_timer(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
- /* retransmit timer is used for feature negotiation throughout
- * connection. In this case, no packet is re-transmitted, but rather an
- * ack is generated and pending changes are placed into its options.
- */
- if (sk->sk_send_head == NULL) {
- dccp_pr_debug("feat negotiation retransmit timeout %p\n", sk);
- if (sk->sk_state == DCCP_OPEN)
- dccp_send_ack(sk);
- goto backoff;
- }
-
/*
* More than than 4MSL (8 minutes) has passed, a RESET(aborted) was
* sent, no need to retransmit, this sock is dead.
@@ -126,7 +115,6 @@ static void dccp_retransmit_timer(struct sock *sk)
return;
}
-backoff:
icsk->icsk_backoff++;
icsk->icsk_rto = min(icsk->icsk_rto << 1, DCCP_RTO_MAX);
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 3c23ab33dbc0..cf0e18499297 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -167,7 +167,7 @@ static struct hlist_head *dn_find_list(struct sock *sk)
if (scp->addr.sdn_flags & SDF_WILD)
return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL;
- return &dn_sk_hash[dn_ntohs(scp->addrloc) & DN_SK_HASH_MASK];
+ return &dn_sk_hash[le16_to_cpu(scp->addrloc) & DN_SK_HASH_MASK];
}
/*
@@ -181,7 +181,7 @@ static int check_port(__le16 port)
if (port == 0)
return -1;
- sk_for_each(sk, node, &dn_sk_hash[dn_ntohs(port) & DN_SK_HASH_MASK]) {
+ sk_for_each(sk, node, &dn_sk_hash[le16_to_cpu(port) & DN_SK_HASH_MASK]) {
struct dn_scp *scp = DN_SK(sk);
if (scp->addrloc == port)
return -1;
@@ -195,12 +195,12 @@ static unsigned short port_alloc(struct sock *sk)
static unsigned short port = 0x2000;
unsigned short i_port = port;
- while(check_port(dn_htons(++port)) != 0) {
+ while(check_port(cpu_to_le16(++port)) != 0) {
if (port == i_port)
return 0;
}
- scp->addrloc = dn_htons(port);
+ scp->addrloc = cpu_to_le16(port);
return 1;
}
@@ -255,7 +255,7 @@ static struct hlist_head *listen_hash(struct sockaddr_dn *addr)
if (hash == 0) {
hash = addr->sdn_objnamel;
- for(i = 0; i < dn_ntohs(addr->sdn_objnamel); i++) {
+ for(i = 0; i < le16_to_cpu(addr->sdn_objnamel); i++) {
hash ^= addr->sdn_objname[i];
hash ^= (hash << 3);
}
@@ -297,16 +297,16 @@ int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned c
break;
case 1:
*buf++ = 0;
- *buf++ = dn_ntohs(sdn->sdn_objnamel);
- memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
- len = 3 + dn_ntohs(sdn->sdn_objnamel);
+ *buf++ = le16_to_cpu(sdn->sdn_objnamel);
+ memcpy(buf, sdn->sdn_objname, le16_to_cpu(sdn->sdn_objnamel));
+ len = 3 + le16_to_cpu(sdn->sdn_objnamel);
break;
case 2:
memset(buf, 0, 5);
buf += 5;
- *buf++ = dn_ntohs(sdn->sdn_objnamel);
- memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
- len = 7 + dn_ntohs(sdn->sdn_objnamel);
+ *buf++ = le16_to_cpu(sdn->sdn_objnamel);
+ memcpy(buf, sdn->sdn_objname, le16_to_cpu(sdn->sdn_objnamel));
+ len = 7 + le16_to_cpu(sdn->sdn_objnamel);
break;
}
@@ -327,7 +327,7 @@ int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn,
int namel = 12;
sdn->sdn_objnum = 0;
- sdn->sdn_objnamel = dn_htons(0);
+ sdn->sdn_objnamel = cpu_to_le16(0);
memset(sdn->sdn_objname, 0, DN_MAXOBJL);
if (len < 2)
@@ -361,13 +361,13 @@ int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn,
if (len < 0)
return -1;
- sdn->sdn_objnamel = dn_htons(*data++);
- len -= dn_ntohs(sdn->sdn_objnamel);
+ sdn->sdn_objnamel = cpu_to_le16(*data++);
+ len -= le16_to_cpu(sdn->sdn_objnamel);
- if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))
+ if ((len < 0) || (le16_to_cpu(sdn->sdn_objnamel) > namel))
return -1;
- memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));
+ memcpy(sdn->sdn_objname, data, le16_to_cpu(sdn->sdn_objnamel));
return size - len;
}
@@ -391,7 +391,7 @@ struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
continue;
if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
continue;
- if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0)
+ if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, le16_to_cpu(addr->sdn_objnamel)) != 0)
continue;
}
sock_hold(sk);
@@ -419,7 +419,7 @@ struct sock *dn_find_by_skb(struct sk_buff *skb)
struct dn_scp *scp;
read_lock(&dn_hash_lock);
- sk_for_each(sk, node, &dn_sk_hash[dn_ntohs(cb->dst_port) & DN_SK_HASH_MASK]) {
+ sk_for_each(sk, node, &dn_sk_hash[le16_to_cpu(cb->dst_port) & DN_SK_HASH_MASK]) {
scp = DN_SK(sk);
if (cb->src != dn_saddr2dn(&scp->peer))
continue;
@@ -734,10 +734,10 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (saddr->sdn_family != AF_DECnet)
return -EINVAL;
- if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2))
+ if (le16_to_cpu(saddr->sdn_nodeaddrl) && (le16_to_cpu(saddr->sdn_nodeaddrl) != 2))
return -EINVAL;
- if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL)
+ if (le16_to_cpu(saddr->sdn_objnamel) > DN_MAXOBJL)
return -EINVAL;
if (saddr->sdn_flags & ~SDF_WILD)
@@ -748,7 +748,7 @@ static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EACCES;
if (!(saddr->sdn_flags & SDF_WILD)) {
- if (dn_ntohs(saddr->sdn_nodeaddrl)) {
+ if (le16_to_cpu(saddr->sdn_nodeaddrl)) {
read_lock(&dev_base_lock);
ldev = NULL;
for_each_netdev(&init_net, dev) {
@@ -799,15 +799,15 @@ static int dn_auto_bind(struct socket *sock)
if ((scp->accessdata.acc_accl != 0) &&
(scp->accessdata.acc_accl <= 12)) {
- scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl);
- memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel));
+ scp->addr.sdn_objnamel = cpu_to_le16(scp->accessdata.acc_accl);
+ memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, le16_to_cpu(scp->addr.sdn_objnamel));
scp->accessdata.acc_accl = 0;
memset(scp->accessdata.acc_acc, 0, 40);
}
/* End of compatibility stuff */
- scp->addr.sdn_add.a_len = dn_htons(2);
+ scp->addr.sdn_add.a_len = cpu_to_le16(2);
rv = dn_dev_bind_default((__le16 *)scp->addr.sdn_add.a_addr);
if (rv == 0) {
rv = dn_hash_sock(sk);
@@ -1027,7 +1027,7 @@ static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
u16 len = *ptr++; /* yes, it's 8bit on the wire */
BUG_ON(len > 16); /* we've checked the contents earlier */
- opt->opt_optl = dn_htons(len);
+ opt->opt_optl = cpu_to_le16(len);
opt->opt_status = 0;
memcpy(opt->opt_data, ptr, len);
skb_pull(skb, len + 1);
@@ -1375,7 +1375,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char __us
if (optlen != sizeof(struct optdata_dn))
return -EINVAL;
- if (dn_ntohs(u.opt.opt_optl) > 16)
+ if (le16_to_cpu(u.opt.opt_optl) > 16)
return -EINVAL;
memcpy(&scp->conndata_out, &u.opt, optlen);
@@ -1388,7 +1388,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char __us
if (optlen != sizeof(struct optdata_dn))
return -EINVAL;
- if (dn_ntohs(u.opt.opt_optl) > 16)
+ if (le16_to_cpu(u.opt.opt_optl) > 16)
return -EINVAL;
memcpy(&scp->discdata_out, &u.opt, optlen);
@@ -2213,12 +2213,12 @@ static void dn_printable_object(struct sockaddr_dn *dn, unsigned char *buf)
{
int i;
- switch (dn_ntohs(dn->sdn_objnamel)) {
+ switch (le16_to_cpu(dn->sdn_objnamel)) {
case 0:
sprintf(buf, "%d", dn->sdn_objnum);
break;
default:
- for (i = 0; i < dn_ntohs(dn->sdn_objnamel); i++) {
+ for (i = 0; i < le16_to_cpu(dn->sdn_objnamel); i++) {
buf[i] = dn->sdn_objname[i];
if (IS_NOT_PRINTABLE(buf[i]))
buf[i] = '.';
@@ -2281,7 +2281,7 @@ static inline void dn_socket_format_entry(struct seq_file *seq, struct sock *sk)
seq_printf(seq,
"%6s/%04X %04d:%04d %04d:%04d %01d %-16s "
"%6s/%04X %04d:%04d %04d:%04d %01d %-16s %4s %s\n",
- dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
+ dn_addr2asc(le16_to_cpu(dn_saddr2dn(&scp->addr)), buf1),
scp->addrloc,
scp->numdat,
scp->numoth,
@@ -2289,7 +2289,7 @@ static inline void dn_socket_format_entry(struct seq_file *seq, struct sock *sk)
scp->ackxmt_oth,
scp->flowloc_sw,
local_object,
- dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
+ dn_addr2asc(le16_to_cpu(dn_saddr2dn(&scp->peer)), buf2),
scp->addrrem,
scp->numdat_rcv,
scp->numoth_rcv,
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index 28e26bd08e24..daf2b98b15fe 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -885,7 +885,7 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
memcpy(msg->tiver, dn_eco_version, 3);
dn_dn2eth(msg->id, ifa->ifa_local);
msg->iinfo = DN_RT_INFO_ENDN;
- msg->blksize = dn_htons(mtu2blksize(dev));
+ msg->blksize = cpu_to_le16(mtu2blksize(dev));
msg->area = 0x00;
memset(msg->seed, 0, 8);
memcpy(msg->neighbor, dn_hiord, ETH_ALEN);
@@ -895,13 +895,13 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
dn_dn2eth(msg->neighbor, dn->addr);
}
- msg->timer = dn_htons((unsigned short)dn_db->parms.t3);
+ msg->timer = cpu_to_le16((unsigned short)dn_db->parms.t3);
msg->mpd = 0x00;
msg->datalen = 0x02;
memset(msg->data, 0xAA, 2);
pktlen = (__le16 *)skb_push(skb,2);
- *pktlen = dn_htons(skb->len - 2);
+ *pktlen = cpu_to_le16(skb->len - 2);
skb_reset_network_header(skb);
@@ -929,7 +929,7 @@ static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db, struct dn
if (dn->priority != dn_db->parms.priority)
return 0;
- if (dn_ntohs(dn->addr) < dn_ntohs(ifa->ifa_local))
+ if (le16_to_cpu(dn->addr) < le16_to_cpu(ifa->ifa_local))
return 1;
return 0;
@@ -973,11 +973,11 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
ptr += ETH_ALEN;
*ptr++ = dn_db->parms.forwarding == 1 ?
DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
- *((__le16 *)ptr) = dn_htons(mtu2blksize(dev));
+ *((__le16 *)ptr) = cpu_to_le16(mtu2blksize(dev));
ptr += 2;
*ptr++ = dn_db->parms.priority; /* Priority */
*ptr++ = 0; /* Area: Reserved */
- *((__le16 *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
+ *((__le16 *)ptr) = cpu_to_le16((unsigned short)dn_db->parms.t3);
ptr += 2;
*ptr++ = 0; /* MPD: Reserved */
i1 = ptr++;
@@ -993,7 +993,7 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
skb_trim(skb, (27 + *i2));
pktlen = (__le16 *)skb_push(skb, 2);
- *pktlen = dn_htons(skb->len - 2);
+ *pktlen = cpu_to_le16(skb->len - 2);
skb_reset_network_header(skb);
@@ -1106,7 +1106,7 @@ static void dn_dev_set_timer(struct net_device *dev)
add_timer(&dn_db->timer);
}
-struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
+static struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
{
int i;
struct dn_dev_parms *p = dn_dev_list;
@@ -1401,8 +1401,8 @@ static int dn_dev_seq_show(struct seq_file *seq, void *v)
mtu2blksize(dev),
dn_db->parms.priority,
dn_db->parms.state, dn_db->parms.name,
- dn_db->router ? dn_addr2asc(dn_ntohs(*(__le16 *)dn_db->router->primary_key), router_buf) : "",
- dn_db->peer ? dn_addr2asc(dn_ntohs(*(__le16 *)dn_db->peer->primary_key), peer_buf) : "");
+ dn_db->router ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->router->primary_key), router_buf) : "",
+ dn_db->peer ? dn_addr2asc(le16_to_cpu(*(__le16 *)dn_db->peer->primary_key), peer_buf) : "");
}
return 0;
}
@@ -1445,7 +1445,7 @@ void __init dn_dev_init(void)
return;
}
- decnet_address = dn_htons((addr[0] << 10) | addr[1]);
+ decnet_address = cpu_to_le16((addr[0] << 10) | addr[1]);
dn_dev_devices_on();
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index 1ca13b17974d..05b5aa05e50e 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -250,7 +250,7 @@ static int dn_long_output(struct sk_buff *skb)
data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
lp = (struct dn_long_packet *)(data+3);
- *((__le16 *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = cpu_to_le16(skb->len - 2);
*(data + 2) = 1 | DN_RT_F_PF; /* Padding */
lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
@@ -294,7 +294,7 @@ static int dn_short_output(struct sk_buff *skb)
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((__le16 *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = cpu_to_le16(skb->len - 2);
sp = (struct dn_short_packet *)(data+2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
@@ -336,12 +336,12 @@ static int dn_phase3_output(struct sk_buff *skb)
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((__le16 *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = cpu_to_le16(skb->len - 2);
sp = (struct dn_short_packet *)(data + 2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
- sp->dstnode = cb->dst & dn_htons(0x03ff);
- sp->srcnode = cb->src & dn_htons(0x03ff);
+ sp->dstnode = cb->dst & cpu_to_le16(0x03ff);
+ sp->srcnode = cb->src & cpu_to_le16(0x03ff);
sp->forward = cb->hops & 0x3f;
skb_reset_network_header(skb);
@@ -394,7 +394,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
if (neigh->dev->type == ARPHRD_ETHER)
memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
- dn->blksize = dn_ntohs(msg->blksize);
+ dn->blksize = le16_to_cpu(msg->blksize);
dn->priority = msg->priority;
dn->flags &= ~DN_NDFLAG_P3;
@@ -410,7 +410,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
}
/* Only use routers in our area */
- if ((dn_ntohs(src)>>10) == (dn_ntohs((decnet_address))>>10)) {
+ if ((le16_to_cpu(src)>>10) == (le16_to_cpu((decnet_address))>>10)) {
if (!dn_db->router) {
dn_db->router = neigh_clone(neigh);
} else {
@@ -453,7 +453,7 @@ int dn_neigh_endnode_hello(struct sk_buff *skb)
if (neigh->dev->type == ARPHRD_ETHER)
memcpy(neigh->ha, &eth_hdr(skb)->h_source, ETH_ALEN);
dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
- dn->blksize = dn_ntohs(msg->blksize);
+ dn->blksize = le16_to_cpu(msg->blksize);
dn->priority = 0;
}
@@ -543,7 +543,7 @@ static inline void dn_neigh_format_entry(struct seq_file *seq,
read_lock(&n->lock);
seq_printf(seq, "%-7s %s%s%s %02x %02d %07ld %-8s\n",
- dn_addr2asc(dn_ntohs(dn->addr), buf),
+ dn_addr2asc(le16_to_cpu(dn->addr), buf),
(dn->flags&DN_NDFLAG_R1) ? "1" : "-",
(dn->flags&DN_NDFLAG_R2) ? "2" : "-",
(dn->flags&DN_NDFLAG_P3) ? "3" : "-",
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 4074a6e5d0de..5d8a2a56fd39 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -83,7 +83,9 @@ static void dn_log_martian(struct sk_buff *skb, const char *msg)
if (decnet_log_martians && net_ratelimit()) {
char *devname = skb->dev ? skb->dev->name : "???";
struct dn_skb_cb *cb = DN_SKB_CB(skb);
- printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, dn_ntohs(cb->src), dn_ntohs(cb->dst), dn_ntohs(cb->src_port), dn_ntohs(cb->dst_port));
+ printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n",
+ msg, devname, le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
+ le16_to_cpu(cb->src_port), le16_to_cpu(cb->dst_port));
}
}
@@ -133,7 +135,7 @@ static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
if (skb->len < 2)
return len;
- if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+ if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
skb_pull(skb, 2);
ptr++;
len += 2;
@@ -147,7 +149,7 @@ static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
if (skb->len < 2)
return len;
- if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+ if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
skb_pull(skb, 2);
len += 2;
if ((ack & 0x4000) == 0) {
@@ -237,7 +239,7 @@ static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason
cb->dst_port = msg->dstaddr;
cb->services = msg->services;
cb->info = msg->info;
- cb->segsize = dn_ntohs(msg->segsize);
+ cb->segsize = le16_to_cpu(msg->segsize);
if (!pskb_may_pull(skb, sizeof(*msg)))
goto err_out;
@@ -344,7 +346,7 @@ static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
ptr = skb->data;
cb->services = *ptr++;
cb->info = *ptr++;
- cb->segsize = dn_ntohs(*(__le16 *)ptr);
+ cb->segsize = le16_to_cpu(*(__le16 *)ptr);
if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
scp->persist = 0;
@@ -361,7 +363,7 @@ static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
if (skb->len > 0) {
u16 dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
- scp->conndata_in.opt_optl = dn_htons(dlen);
+ scp->conndata_in.opt_optl = cpu_to_le16(dlen);
skb_copy_from_linear_data_offset(skb, 1,
scp->conndata_in.opt_data, dlen);
}
@@ -396,17 +398,17 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- reason = dn_ntohs(*(__le16 *)skb->data);
+ reason = le16_to_cpu(*(__le16 *)skb->data);
skb_pull(skb, 2);
- scp->discdata_in.opt_status = dn_htons(reason);
+ scp->discdata_in.opt_status = cpu_to_le16(reason);
scp->discdata_in.opt_optl = 0;
memset(scp->discdata_in.opt_data, 0, 16);
if (skb->len > 0) {
u16 dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
- scp->discdata_in.opt_optl = dn_htons(dlen);
+ scp->discdata_in.opt_optl = cpu_to_le16(dlen);
skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen);
}
}
@@ -463,7 +465,7 @@ static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
if (skb->len != 2)
goto out;
- reason = dn_ntohs(*(__le16 *)skb->data);
+ reason = le16_to_cpu(*(__le16 *)skb->data);
sk->sk_state = TCP_CLOSE;
@@ -512,7 +514,7 @@ static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
if (skb->len != 4)
goto out;
- segnum = dn_ntohs(*(__le16 *)ptr);
+ segnum = le16_to_cpu(*(__le16 *)ptr);
ptr += 2;
lsflags = *(unsigned char *)ptr++;
fcval = *ptr;
@@ -620,7 +622,7 @@ static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- cb->segnum = segnum = dn_ntohs(*(__le16 *)skb->data);
+ cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
skb_pull(skb, 2);
if (seq_next(scp->numoth_rcv, segnum)) {
@@ -648,7 +650,7 @@ static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- cb->segnum = segnum = dn_ntohs(*(__le16 *)skb->data);
+ cb->segnum = segnum = le16_to_cpu(*(__le16 *)skb->data);
skb_pull(skb, 2);
if (seq_next(scp->numdat_rcv, segnum)) {
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
index 1964faf203e4..2013c25b7f5a 100644
--- a/net/decnet/dn_nsp_out.c
+++ b/net/decnet/dn_nsp_out.c
@@ -230,7 +230,6 @@ static inline unsigned dn_nsp_clone_and_send(struct sk_buff *skb,
/**
* dn_nsp_output - Try and send something from socket queues
* @sk: The socket whose queues are to be investigated
- * @gfp: The memory allocation flags
*
* Try and send the packet on the end of the data and other data queues.
* Other data gets priority over data, and if we retransmit a packet we
@@ -326,8 +325,8 @@ static __le16 *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned c
ptr = (__le16 *)dn_mk_common_header(scp, skb, msgflag, hlen);
- *ptr++ = dn_htons(acknum);
- *ptr++ = dn_htons(ackcrs);
+ *ptr++ = cpu_to_le16(acknum);
+ *ptr++ = cpu_to_le16(ackcrs);
return ptr;
}
@@ -345,7 +344,7 @@ static __le16 *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int o
cb->segnum = scp->numdat;
seq_add(&scp->numdat, 1);
}
- *(ptr++) = dn_htons(cb->segnum);
+ *(ptr++) = cpu_to_le16(cb->segnum);
return ptr;
}
@@ -523,7 +522,7 @@ void dn_send_conn_conf(struct sock *sk, gfp_t gfp)
struct dn_scp *scp = DN_SK(sk);
struct sk_buff *skb = NULL;
struct nsp_conn_init_msg *msg;
- __u8 len = (__u8)dn_ntohs(scp->conndata_out.opt_optl);
+ __u8 len = (__u8)le16_to_cpu(scp->conndata_out.opt_optl);
if ((skb = dn_alloc_skb(sk, 50 + len, gfp)) == NULL)
return;
@@ -534,7 +533,7 @@ void dn_send_conn_conf(struct sock *sk, gfp_t gfp)
msg->srcaddr = scp->addrloc;
msg->services = scp->services_loc;
msg->info = scp->info_loc;
- msg->segsize = dn_htons(scp->segsize_loc);
+ msg->segsize = cpu_to_le16(scp->segsize_loc);
*skb_put(skb,1) = len;
@@ -560,7 +559,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
if ((dst == NULL) || (rem == 0)) {
if (net_ratelimit())
- printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", dn_ntohs(rem), dst);
+ printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", le16_to_cpu(rem), dst);
return;
}
@@ -573,7 +572,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
msg += 2;
*(__le16 *)msg = loc;
msg += 2;
- *(__le16 *)msg = dn_htons(reason);
+ *(__le16 *)msg = cpu_to_le16(reason);
msg += 2;
if (msgflg == NSP_DISCINIT)
*msg++ = ddl;
@@ -599,10 +598,10 @@ void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg,
int ddl = 0;
if (msgflg == NSP_DISCINIT)
- ddl = dn_ntohs(scp->discdata_out.opt_optl);
+ ddl = le16_to_cpu(scp->discdata_out.opt_optl);
if (reason == 0)
- reason = dn_ntohs(scp->discdata_out.opt_status);
+ reason = le16_to_cpu(scp->discdata_out.opt_status);
dn_nsp_do_disc(sk, msgflg, reason, gfp, sk->sk_dst_cache, ddl,
scp->discdata_out.opt_data, scp->addrrem, scp->addrloc);
@@ -676,7 +675,7 @@ void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
msg->srcaddr = scp->addrloc;
msg->services = scp->services_loc; /* Requested flow control */
msg->info = scp->info_loc; /* Version Number */
- msg->segsize = dn_htons(scp->segsize_loc); /* Max segment size */
+ msg->segsize = cpu_to_le16(scp->segsize_loc); /* Max segment size */
if (scp->peer.sdn_objnum)
type = 0;
@@ -709,7 +708,7 @@ void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
if (aux > 0)
memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux);
- aux = (__u8)dn_ntohs(scp->conndata_out.opt_optl);
+ aux = (__u8)le16_to_cpu(scp->conndata_out.opt_optl);
*skb_put(skb, 1) = aux;
if (aux > 0)
memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux);
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 821bd1cdec04..c754670b7fca 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -131,7 +131,6 @@ static struct dst_ops dn_dst_ops = {
.negative_advice = dn_dst_negative_advice,
.link_failure = dn_dst_link_failure,
.update_pmtu = dn_dst_update_pmtu,
- .entry_size = sizeof(struct dn_route),
.entries = ATOMIC_INIT(0),
};
@@ -312,7 +311,7 @@ static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route *
return 0;
}
-void dn_run_flush(unsigned long dummy)
+static void dn_run_flush(unsigned long dummy)
{
int i;
struct dn_route *rt, *next;
@@ -476,7 +475,7 @@ static int dn_route_rx_packet(struct sk_buff *skb)
printk(KERN_DEBUG
"DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n",
(int)cb->rt_flags, devname, skb->len,
- dn_ntohs(cb->src), dn_ntohs(cb->dst),
+ le16_to_cpu(cb->src), le16_to_cpu(cb->dst),
err, skb->pkt_type);
}
@@ -576,7 +575,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
{
struct dn_skb_cb *cb;
unsigned char flags = 0;
- __u16 len = dn_ntohs(*(__le16 *)skb->data);
+ __u16 len = le16_to_cpu(*(__le16 *)skb->data);
struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr;
unsigned char padlen = 0;
@@ -774,7 +773,7 @@ static int dn_rt_bug(struct sk_buff *skb)
struct dn_skb_cb *cb = DN_SKB_CB(skb);
printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n",
- dn_ntohs(cb->src), dn_ntohs(cb->dst));
+ le16_to_cpu(cb->src), le16_to_cpu(cb->dst));
}
kfree_skb(skb);
@@ -817,7 +816,7 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
static inline int dn_match_addr(__le16 addr1, __le16 addr2)
{
- __u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2);
+ __u16 tmp = le16_to_cpu(addr1) ^ le16_to_cpu(addr2);
int match = 16;
while(tmp) {
tmp >>= 1;
@@ -887,8 +886,8 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old
if (decnet_debug_level & 16)
printk(KERN_DEBUG
"dn_route_output_slow: dst=%04x src=%04x mark=%d"
- " iif=%d oif=%d\n", dn_ntohs(oldflp->fld_dst),
- dn_ntohs(oldflp->fld_src),
+ " iif=%d oif=%d\n", le16_to_cpu(oldflp->fld_dst),
+ le16_to_cpu(oldflp->fld_src),
oldflp->mark, init_net.loopback_dev->ifindex, oldflp->oif);
/* If we have an output interface, verify its a DECnet device */
@@ -960,7 +959,7 @@ source_ok:
printk(KERN_DEBUG
"dn_route_output_slow: initial checks complete."
" dst=%o4x src=%04x oif=%d try_hard=%d\n",
- dn_ntohs(fl.fld_dst), dn_ntohs(fl.fld_src),
+ le16_to_cpu(fl.fld_dst), le16_to_cpu(fl.fld_src),
fl.oif, try_hard);
/*
@@ -1185,7 +1184,7 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int f
err = __dn_route_output_key(pprt, flp, flags);
if (err == 0 && flp->proto) {
- err = xfrm_lookup(pprt, flp, NULL, 0);
+ err = xfrm_lookup(&init_net, pprt, flp, NULL, 0);
}
return err;
}
@@ -1196,8 +1195,8 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock
err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD);
if (err == 0 && fl->proto) {
- err = xfrm_lookup(pprt, fl, sk, (flags & MSG_DONTWAIT) ?
- 0 : XFRM_LOOKUP_WAIT);
+ err = xfrm_lookup(&init_net, pprt, fl, sk,
+ (flags & MSG_DONTWAIT) ? 0 : XFRM_LOOKUP_WAIT);
}
return err;
}
@@ -1423,7 +1422,7 @@ e_neighbour:
goto done;
}
-int dn_route_input(struct sk_buff *skb)
+static int dn_route_input(struct sk_buff *skb)
{
struct dn_route *rt;
struct dn_skb_cb *cb = DN_SKB_CB(skb);
@@ -1712,8 +1711,8 @@ static int dn_rt_cache_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%-8s %-7s %-7s %04d %04d %04d\n",
rt->u.dst.dev ? rt->u.dst.dev->name : "*",
- dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1),
- dn_addr2asc(dn_ntohs(rt->rt_saddr), buf2),
+ dn_addr2asc(le16_to_cpu(rt->rt_daddr), buf1),
+ dn_addr2asc(le16_to_cpu(rt->rt_saddr), buf2),
atomic_read(&rt->u.dst.__refcnt),
rt->u.dst.__use,
(int) dst_metric(&rt->u.dst, RTAX_RTT));
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
index 3a2830ac89c2..69ad9280c693 100644
--- a/net/decnet/dn_table.c
+++ b/net/decnet/dn_table.c
@@ -85,7 +85,7 @@ static int dn_fib_hash_zombies;
static inline dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
{
- u16 h = dn_ntohs(key.datum)>>(16 - dz->dz_order);
+ u16 h = le16_to_cpu(key.datum)>>(16 - dz->dz_order);
h ^= (h >> 10);
h ^= (h >> 6);
h &= DZ_HASHMASK(dz);
diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c
index 36400b266896..965397af9a80 100644
--- a/net/decnet/sysctl_net_decnet.c
+++ b/net/decnet/sysctl_net_decnet.c
@@ -126,7 +126,7 @@ static int parse_addr(__le16 *addr, char *str)
if (INVALID_END_CHAR(*str))
return -1;
- *addr = dn_htons((area << 10) | node);
+ *addr = cpu_to_le16((area << 10) | node);
return 0;
}
@@ -201,7 +201,7 @@ static int dn_node_address_handler(ctl_table *table, int write,
return 0;
}
- dn_addr2asc(dn_ntohs(decnet_address), addr);
+ dn_addr2asc(le16_to_cpu(decnet_address), addr);
len = strlen(addr);
addr[len++] = '\n';
@@ -354,8 +354,8 @@ static ctl_table dn_table[] = {
.data = node_name,
.maxlen = 7,
.mode = 0644,
- .proc_handler = &proc_dostring,
- .strategy = &sysctl_string,
+ .proc_handler = proc_dostring,
+ .strategy = sysctl_string,
},
{
.ctl_name = NET_DECNET_DEFAULT_DEVICE,
@@ -371,8 +371,8 @@ static ctl_table dn_table[] = {
.data = &decnet_time_wait,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_decnet_time_wait,
.extra2 = &max_decnet_time_wait
},
@@ -382,8 +382,8 @@ static ctl_table dn_table[] = {
.data = &decnet_dn_count,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_state_count,
.extra2 = &max_state_count
},
@@ -393,8 +393,8 @@ static ctl_table dn_table[] = {
.data = &decnet_di_count,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_state_count,
.extra2 = &max_state_count
},
@@ -404,8 +404,8 @@ static ctl_table dn_table[] = {
.data = &decnet_dr_count,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_state_count,
.extra2 = &max_state_count
},
@@ -415,8 +415,8 @@ static ctl_table dn_table[] = {
.data = &decnet_dst_gc_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_decnet_dst_gc_interval,
.extra2 = &max_decnet_dst_gc_interval
},
@@ -426,8 +426,8 @@ static ctl_table dn_table[] = {
.data = &decnet_no_fc_max_cwnd,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_decnet_no_fc_max_cwnd,
.extra2 = &max_decnet_no_fc_max_cwnd
},
@@ -437,8 +437,8 @@ static ctl_table dn_table[] = {
.data = &sysctl_decnet_mem,
.maxlen = sizeof(sysctl_decnet_mem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec,
},
{
.ctl_name = NET_DECNET_RMEM,
@@ -446,8 +446,8 @@ static ctl_table dn_table[] = {
.data = &sysctl_decnet_rmem,
.maxlen = sizeof(sysctl_decnet_rmem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec,
},
{
.ctl_name = NET_DECNET_WMEM,
@@ -455,8 +455,8 @@ static ctl_table dn_table[] = {
.data = &sysctl_decnet_wmem,
.maxlen = sizeof(sysctl_decnet_wmem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec,
},
{
.ctl_name = NET_DECNET_DEBUG_LEVEL,
@@ -464,8 +464,8 @@ static ctl_table dn_table[] = {
.data = &decnet_debug_level,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec,
},
{0}
};
diff --git a/net/dsa/mv88e6060.c b/net/dsa/mv88e6060.c
index 54068ef251e8..85081ae9fe89 100644
--- a/net/dsa/mv88e6060.c
+++ b/net/dsa/mv88e6060.c
@@ -222,7 +222,7 @@ static void mv88e6060_poll_link(struct dsa_switch *ds)
for (i = 0; i < DSA_MAX_PORTS; i++) {
struct net_device *dev;
- int port_status;
+ int uninitialized_var(port_status);
int link;
int speed;
int duplex;
@@ -273,14 +273,14 @@ static struct dsa_switch_driver mv88e6060_switch_driver = {
.poll_link = mv88e6060_poll_link,
};
-int __init mv88e6060_init(void)
+static int __init mv88e6060_init(void)
{
register_switch_driver(&mv88e6060_switch_driver);
return 0;
}
module_init(mv88e6060_init);
-void __exit mv88e6060_cleanup(void)
+static void __exit mv88e6060_cleanup(void)
{
unregister_switch_driver(&mv88e6060_switch_driver);
}
diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c
index 555b164082fc..ec8c6a0482d3 100644
--- a/net/dsa/mv88e6123_61_65.c
+++ b/net/dsa/mv88e6123_61_65.c
@@ -407,14 +407,14 @@ static struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
.get_sset_count = mv88e6123_61_65_get_sset_count,
};
-int __init mv88e6123_61_65_init(void)
+static int __init mv88e6123_61_65_init(void)
{
register_switch_driver(&mv88e6123_61_65_switch_driver);
return 0;
}
module_init(mv88e6123_61_65_init);
-void __exit mv88e6123_61_65_cleanup(void)
+static void __exit mv88e6123_61_65_cleanup(void)
{
unregister_switch_driver(&mv88e6123_61_65_switch_driver);
}
diff --git a/net/dsa/mv88e6131.c b/net/dsa/mv88e6131.c
index 36e01eb863a0..374d46a01265 100644
--- a/net/dsa/mv88e6131.c
+++ b/net/dsa/mv88e6131.c
@@ -366,14 +366,14 @@ static struct dsa_switch_driver mv88e6131_switch_driver = {
.get_sset_count = mv88e6131_get_sset_count,
};
-int __init mv88e6131_init(void)
+static int __init mv88e6131_init(void)
{
register_switch_driver(&mv88e6131_switch_driver);
return 0;
}
module_init(mv88e6131_init);
-void __exit mv88e6131_cleanup(void)
+static void __exit mv88e6131_cleanup(void)
{
unregister_switch_driver(&mv88e6131_switch_driver);
}
diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c
index aa6c609c59f2..4e4d8b5ad03d 100644
--- a/net/dsa/mv88e6xxx.c
+++ b/net/dsa/mv88e6xxx.c
@@ -358,7 +358,7 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds)
for (i = 0; i < DSA_MAX_PORTS; i++) {
struct net_device *dev;
- int port_status;
+ int uninitialized_var(port_status);
int link;
int speed;
int duplex;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 1af5a79309e9..a3a410d20da0 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -352,7 +352,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
netif_carrier_off(slave_dev);
if (p->phy != NULL) {
- phy_attach(slave_dev, p->phy->dev.bus_id,
+ phy_attach(slave_dev, dev_name(&p->phy->dev),
0, PHY_INTERFACE_MODE_GMII);
p->phy->autoneg = AUTONEG_ENABLE;
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
index 31866543332e..f99a019b939e 100644
--- a/net/dsa/tag_dsa.c
+++ b/net/dsa/tag_dsa.c
@@ -162,7 +162,6 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- skb->dev->last_rx = jiffies;
skb->dev->stats.rx_packets++;
skb->dev->stats.rx_bytes += skb->len;
diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c
index 9f4ce55eae59..328ec957f786 100644
--- a/net/dsa/tag_edsa.c
+++ b/net/dsa/tag_edsa.c
@@ -181,7 +181,6 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- skb->dev->last_rx = jiffies;
skb->dev->stats.rx_packets++;
skb->dev->stats.rx_bytes += skb->len;
diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c
index efd26697e716..b59132878ad1 100644
--- a/net/dsa/tag_trailer.c
+++ b/net/dsa/tag_trailer.c
@@ -98,7 +98,6 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev,
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- skb->dev->last_rx = jiffies;
skb->dev->stats.rx_packets++;
skb->dev->stats.rx_bytes += skb->len;
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index b9d85af2dd31..280352aba403 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -165,8 +165,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
skb_pull(skb, ETH_HLEN);
eth = eth_hdr(skb);
- if (is_multicast_ether_addr(eth->h_dest)) {
- if (!compare_ether_addr(eth->h_dest, dev->broadcast))
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+ if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
@@ -181,7 +181,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
*/
else if (1 /*dev->flags&IFF_PROMISC */ ) {
- if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr)))
+ if (unlikely(compare_ether_addr_64bits(eth->h_dest, dev->dev_addr)))
skb->pkt_type = PACKET_OTHERHOST;
}
@@ -282,7 +282,7 @@ EXPORT_SYMBOL(eth_header_cache_update);
* This doesn't change hardware matching, so needs to be overridden
* for most real devices.
*/
-static int eth_mac_addr(struct net_device *dev, void *p)
+int eth_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
@@ -293,6 +293,7 @@ static int eth_mac_addr(struct net_device *dev, void *p)
memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
return 0;
}
+EXPORT_SYMBOL(eth_mac_addr);
/**
* eth_change_mtu - set new MTU size
@@ -302,21 +303,23 @@ static int eth_mac_addr(struct net_device *dev, void *p)
* Allow changing MTU size. Needs to be overridden for devices
* supporting jumbo frames.
*/
-static int eth_change_mtu(struct net_device *dev, int new_mtu)
+int eth_change_mtu(struct net_device *dev, int new_mtu)
{
if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
+EXPORT_SYMBOL(eth_change_mtu);
-static int eth_validate_addr(struct net_device *dev)
+int eth_validate_addr(struct net_device *dev)
{
if (!is_valid_ether_addr(dev->dev_addr))
return -EADDRNOTAVAIL;
return 0;
}
+EXPORT_SYMBOL(eth_validate_addr);
const struct header_ops eth_header_ops ____cacheline_aligned = {
.create = eth_header,
@@ -334,11 +337,11 @@ const struct header_ops eth_header_ops ____cacheline_aligned = {
void ether_setup(struct net_device *dev)
{
dev->header_ops = &eth_header_ops;
-
+#ifdef CONFIG_COMPAT_NET_DEV_OPS
dev->change_mtu = eth_change_mtu;
dev->set_mac_address = eth_mac_addr;
dev->validate_addr = eth_validate_addr;
-
+#endif
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
deleted file mode 100644
index 94ed7d3cd9da..000000000000
--- a/net/ieee80211/Kconfig
+++ /dev/null
@@ -1,73 +0,0 @@
-config IEEE80211
- tristate "Generic IEEE 802.11 Networking Stack (DEPRECATED)"
- ---help---
- This option enables the hardware independent IEEE 802.11
- networking stack. This component is deprecated in favor of the
- mac80211 component.
-
-config IEEE80211_DEBUG
- bool "Enable full debugging output"
- depends on IEEE80211
- ---help---
- This option will enable debug tracing output for the
- ieee80211 network stack.
-
- This will result in the kernel module being ~70k larger. You
- can control which debug output is sent to the kernel log by
- setting the value in
-
- /proc/net/ieee80211/debug_level
-
- For example:
-
- % echo 0x00000FFO > /proc/net/ieee80211/debug_level
-
- For a list of values you can assign to debug_level, you
- can look at the bit mask values in <net/ieee80211.h>
-
- If you are not trying to debug or develop the ieee80211
- subsystem, you most likely want to say N here.
-
-config IEEE80211_CRYPT_WEP
- tristate "IEEE 802.11 WEP encryption (802.1x)"
- depends on IEEE80211
- select CRYPTO
- select CRYPTO_ARC4
- select CRYPTO_ECB
- select CRC32
- ---help---
- Include software based cipher suites in support of IEEE
- 802.11's WEP. This is needed for WEP as well as 802.1x.
-
- This can be compiled as a module and it will be called
- "ieee80211_crypt_wep".
-
-config IEEE80211_CRYPT_CCMP
- tristate "IEEE 802.11i CCMP support"
- depends on IEEE80211
- select CRYPTO
- select CRYPTO_AES
- ---help---
- Include software based cipher suites in support of IEEE 802.11i
- (aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with CCMP enabled
- networks.
-
- This can be compiled as a module and it will be called
- "ieee80211_crypt_ccmp".
-
-config IEEE80211_CRYPT_TKIP
- tristate "IEEE 802.11i TKIP encryption"
- depends on IEEE80211
- select WIRELESS_EXT
- select CRYPTO
- select CRYPTO_MICHAEL_MIC
- select CRYPTO_ECB
- select CRC32
- ---help---
- Include software based cipher suites in support of IEEE 802.11i
- (aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with TKIP enabled
- networks.
-
- This can be compiled as a module and it will be called
- "ieee80211_crypt_tkip".
-
diff --git a/net/ieee80211/Makefile b/net/ieee80211/Makefile
deleted file mode 100644
index f988417121da..000000000000
--- a/net/ieee80211/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-obj-$(CONFIG_IEEE80211) += ieee80211.o
-obj-$(CONFIG_IEEE80211) += ieee80211_crypt.o
-obj-$(CONFIG_IEEE80211_CRYPT_WEP) += ieee80211_crypt_wep.o
-obj-$(CONFIG_IEEE80211_CRYPT_CCMP) += ieee80211_crypt_ccmp.o
-obj-$(CONFIG_IEEE80211_CRYPT_TKIP) += ieee80211_crypt_tkip.o
-ieee80211-objs := \
- ieee80211_module.o \
- ieee80211_tx.o \
- ieee80211_rx.o \
- ieee80211_wx.o \
- ieee80211_geo.o
-
diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c
deleted file mode 100644
index df5592c9339f..000000000000
--- a/net/ieee80211/ieee80211_crypt.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Host AP crypto routines
- *
- * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
- * Portions Copyright (C) 2004, Intel Corporation <jketreno@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 version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- *
- */
-
-#include <linux/errno.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <net/ieee80211.h>
-
-MODULE_AUTHOR("Jouni Malinen");
-MODULE_DESCRIPTION("HostAP crypto");
-MODULE_LICENSE("GPL");
-
-struct ieee80211_crypto_alg {
- struct list_head list;
- struct ieee80211_crypto_ops *ops;
-};
-
-static LIST_HEAD(ieee80211_crypto_algs);
-static DEFINE_SPINLOCK(ieee80211_crypto_lock);
-
-void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
-{
- struct ieee80211_crypt_data *entry, *next;
- unsigned long flags;
-
- spin_lock_irqsave(&ieee->lock, flags);
- list_for_each_entry_safe(entry, next, &ieee->crypt_deinit_list, list) {
- if (atomic_read(&entry->refcnt) != 0 && !force)
- continue;
-
- list_del(&entry->list);
-
- if (entry->ops) {
- entry->ops->deinit(entry->priv);
- module_put(entry->ops->owner);
- }
- kfree(entry);
- }
- spin_unlock_irqrestore(&ieee->lock, flags);
-}
-
-/* After this, crypt_deinit_list won't accept new members */
-void ieee80211_crypt_quiescing(struct ieee80211_device *ieee)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ieee->lock, flags);
- ieee->crypt_quiesced = 1;
- spin_unlock_irqrestore(&ieee->lock, flags);
-}
-
-void ieee80211_crypt_deinit_handler(unsigned long data)
-{
- struct ieee80211_device *ieee = (struct ieee80211_device *)data;
- unsigned long flags;
-
- ieee80211_crypt_deinit_entries(ieee, 0);
-
- spin_lock_irqsave(&ieee->lock, flags);
- if (!list_empty(&ieee->crypt_deinit_list) && !ieee->crypt_quiesced) {
- printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
- "deletion list\n", ieee->dev->name);
- ieee->crypt_deinit_timer.expires = jiffies + HZ;
- add_timer(&ieee->crypt_deinit_timer);
- }
- spin_unlock_irqrestore(&ieee->lock, flags);
-}
-
-void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
- struct ieee80211_crypt_data **crypt)
-{
- struct ieee80211_crypt_data *tmp;
- unsigned long flags;
-
- if (*crypt == NULL)
- return;
-
- tmp = *crypt;
- *crypt = NULL;
-
- /* must not run ops->deinit() while there may be pending encrypt or
- * decrypt operations. Use a list of delayed deinits to avoid needing
- * locking. */
-
- spin_lock_irqsave(&ieee->lock, flags);
- if (!ieee->crypt_quiesced) {
- list_add(&tmp->list, &ieee->crypt_deinit_list);
- if (!timer_pending(&ieee->crypt_deinit_timer)) {
- ieee->crypt_deinit_timer.expires = jiffies + HZ;
- add_timer(&ieee->crypt_deinit_timer);
- }
- }
- spin_unlock_irqrestore(&ieee->lock, flags);
-}
-
-int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
-{
- unsigned long flags;
- struct ieee80211_crypto_alg *alg;
-
- alg = kzalloc(sizeof(*alg), GFP_KERNEL);
- if (alg == NULL)
- return -ENOMEM;
-
- alg->ops = ops;
-
- spin_lock_irqsave(&ieee80211_crypto_lock, flags);
- list_add(&alg->list, &ieee80211_crypto_algs);
- spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
-
- printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n",
- ops->name);
-
- return 0;
-}
-
-int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
-{
- struct ieee80211_crypto_alg *alg;
- unsigned long flags;
-
- spin_lock_irqsave(&ieee80211_crypto_lock, flags);
- list_for_each_entry(alg, &ieee80211_crypto_algs, list) {
- if (alg->ops == ops)
- goto found;
- }
- spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
- return -EINVAL;
-
- found:
- printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
- "'%s'\n", ops->name);
- list_del(&alg->list);
- spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
- kfree(alg);
- return 0;
-}
-
-struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
-{
- struct ieee80211_crypto_alg *alg;
- unsigned long flags;
-
- spin_lock_irqsave(&ieee80211_crypto_lock, flags);
- list_for_each_entry(alg, &ieee80211_crypto_algs, list) {
- if (strcmp(alg->ops->name, name) == 0)
- goto found;
- }
- spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
- return NULL;
-
- found:
- spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
- return alg->ops;
-}
-
-static void *ieee80211_crypt_null_init(int keyidx)
-{
- return (void *)1;
-}
-
-static void ieee80211_crypt_null_deinit(void *priv)
-{
-}
-
-static struct ieee80211_crypto_ops ieee80211_crypt_null = {
- .name = "NULL",
- .init = ieee80211_crypt_null_init,
- .deinit = ieee80211_crypt_null_deinit,
- .owner = THIS_MODULE,
-};
-
-static int __init ieee80211_crypto_init(void)
-{
- return ieee80211_register_crypto_ops(&ieee80211_crypt_null);
-}
-
-static void __exit ieee80211_crypto_deinit(void)
-{
- ieee80211_unregister_crypto_ops(&ieee80211_crypt_null);
- BUG_ON(!list_empty(&ieee80211_crypto_algs));
-}
-
-EXPORT_SYMBOL(ieee80211_crypt_deinit_entries);
-EXPORT_SYMBOL(ieee80211_crypt_deinit_handler);
-EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit);
-EXPORT_SYMBOL(ieee80211_crypt_quiescing);
-
-EXPORT_SYMBOL(ieee80211_register_crypto_ops);
-EXPORT_SYMBOL(ieee80211_unregister_crypto_ops);
-EXPORT_SYMBOL(ieee80211_get_crypto_ops);
-
-module_init(ieee80211_crypto_init);
-module_exit(ieee80211_crypto_deinit);
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
deleted file mode 100644
index 208bf35b5546..000000000000
--- a/net/ieee80211/ieee80211_crypt_ccmp.c
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
- *
- * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <asm/string.h>
-#include <linux/wireless.h>
-
-#include <net/ieee80211.h>
-
-#include <linux/crypto.h>
-
-MODULE_AUTHOR("Jouni Malinen");
-MODULE_DESCRIPTION("Host AP crypt: CCMP");
-MODULE_LICENSE("GPL");
-
-#define AES_BLOCK_LEN 16
-#define CCMP_HDR_LEN 8
-#define CCMP_MIC_LEN 8
-#define CCMP_TK_LEN 16
-#define CCMP_PN_LEN 6
-
-struct ieee80211_ccmp_data {
- u8 key[CCMP_TK_LEN];
- int key_set;
-
- u8 tx_pn[CCMP_PN_LEN];
- u8 rx_pn[CCMP_PN_LEN];
-
- u32 dot11RSNAStatsCCMPFormatErrors;
- u32 dot11RSNAStatsCCMPReplays;
- u32 dot11RSNAStatsCCMPDecryptErrors;
-
- int key_idx;
-
- struct crypto_cipher *tfm;
-
- /* scratch buffers for virt_to_page() (crypto API) */
- u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
- tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
- u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
-};
-
-static inline void ieee80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
- const u8 pt[16], u8 ct[16])
-{
- crypto_cipher_encrypt_one(tfm, ct, pt);
-}
-
-static void *ieee80211_ccmp_init(int key_idx)
-{
- struct ieee80211_ccmp_data *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
- if (priv == NULL)
- goto fail;
- priv->key_idx = key_idx;
-
- priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->tfm)) {
- printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate "
- "crypto API aes\n");
- priv->tfm = NULL;
- goto fail;
- }
-
- return priv;
-
- fail:
- if (priv) {
- if (priv->tfm)
- crypto_free_cipher(priv->tfm);
- kfree(priv);
- }
-
- return NULL;
-}
-
-static void ieee80211_ccmp_deinit(void *priv)
-{
- struct ieee80211_ccmp_data *_priv = priv;
- if (_priv && _priv->tfm)
- crypto_free_cipher(_priv->tfm);
- kfree(priv);
-}
-
-static inline void xor_block(u8 * b, u8 * a, size_t len)
-{
- int i;
- for (i = 0; i < len; i++)
- b[i] ^= a[i];
-}
-
-static void ccmp_init_blocks(struct crypto_cipher *tfm,
- struct ieee80211_hdr_4addr *hdr,
- u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
-{
- u8 *pos, qc = 0;
- size_t aad_len;
- u16 fc;
- int a4_included, qc_included;
- u8 aad[2 * AES_BLOCK_LEN];
-
- fc = le16_to_cpu(hdr->frame_ctl);
- a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS));
- qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) &&
- (WLAN_FC_GET_STYPE(fc) & IEEE80211_STYPE_QOS_DATA));
- aad_len = 22;
- if (a4_included)
- aad_len += 6;
- if (qc_included) {
- pos = (u8 *) & hdr->addr4;
- if (a4_included)
- pos += 6;
- qc = *pos & 0x0f;
- aad_len += 2;
- }
-
- /* CCM Initial Block:
- * Flag (Include authentication header, M=3 (8-octet MIC),
- * L=1 (2-octet Dlen))
- * Nonce: 0x00 | A2 | PN
- * Dlen */
- b0[0] = 0x59;
- b0[1] = qc;
- memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
- memcpy(b0 + 8, pn, CCMP_PN_LEN);
- b0[14] = (dlen >> 8) & 0xff;
- b0[15] = dlen & 0xff;
-
- /* AAD:
- * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
- * A1 | A2 | A3
- * SC with bits 4..15 (seq#) masked to zero
- * A4 (if present)
- * QC (if present)
- */
- pos = (u8 *) hdr;
- aad[0] = 0; /* aad_len >> 8 */
- aad[1] = aad_len & 0xff;
- aad[2] = pos[0] & 0x8f;
- aad[3] = pos[1] & 0xc7;
- memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
- pos = (u8 *) & hdr->seq_ctl;
- aad[22] = pos[0] & 0x0f;
- aad[23] = 0; /* all bits masked */
- memset(aad + 24, 0, 8);
- if (a4_included)
- memcpy(aad + 24, hdr->addr4, ETH_ALEN);
- if (qc_included) {
- aad[a4_included ? 30 : 24] = qc;
- /* rest of QC masked */
- }
-
- /* Start with the first block and AAD */
- ieee80211_ccmp_aes_encrypt(tfm, b0, auth);
- xor_block(auth, aad, AES_BLOCK_LEN);
- ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
- xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
- ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
- b0[0] &= 0x07;
- b0[14] = b0[15] = 0;
- ieee80211_ccmp_aes_encrypt(tfm, b0, s0);
-}
-
-static int ieee80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
- u8 *aeskey, int keylen, void *priv)
-{
- struct ieee80211_ccmp_data *key = priv;
- int i;
- u8 *pos;
-
- if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
- return -1;
-
- if (aeskey != NULL && keylen >= CCMP_TK_LEN)
- memcpy(aeskey, key->key, CCMP_TK_LEN);
-
- pos = skb_push(skb, CCMP_HDR_LEN);
- memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
- pos += hdr_len;
-
- i = CCMP_PN_LEN - 1;
- while (i >= 0) {
- key->tx_pn[i]++;
- if (key->tx_pn[i] != 0)
- break;
- i--;
- }
-
- *pos++ = key->tx_pn[5];
- *pos++ = key->tx_pn[4];
- *pos++ = 0;
- *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
- *pos++ = key->tx_pn[3];
- *pos++ = key->tx_pn[2];
- *pos++ = key->tx_pn[1];
- *pos++ = key->tx_pn[0];
-
- return CCMP_HDR_LEN;
-}
-
-static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct ieee80211_ccmp_data *key = priv;
- int data_len, i, blocks, last, len;
- u8 *pos, *mic;
- struct ieee80211_hdr_4addr *hdr;
- u8 *b0 = key->tx_b0;
- u8 *b = key->tx_b;
- u8 *e = key->tx_e;
- u8 *s0 = key->tx_s0;
-
- if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
- return -1;
-
- data_len = skb->len - hdr_len;
- len = ieee80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
- if (len < 0)
- return -1;
-
- pos = skb->data + hdr_len + CCMP_HDR_LEN;
- mic = skb_put(skb, CCMP_MIC_LEN);
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
-
- blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
- last = data_len % AES_BLOCK_LEN;
-
- for (i = 1; i <= blocks; i++) {
- len = (i == blocks && last) ? last : AES_BLOCK_LEN;
- /* Authentication */
- xor_block(b, pos, len);
- ieee80211_ccmp_aes_encrypt(key->tfm, b, b);
- /* Encryption, with counter */
- b0[14] = (i >> 8) & 0xff;
- b0[15] = i & 0xff;
- ieee80211_ccmp_aes_encrypt(key->tfm, b0, e);
- xor_block(pos, e, len);
- pos += len;
- }
-
- for (i = 0; i < CCMP_MIC_LEN; i++)
- mic[i] = b[i] ^ s0[i];
-
- return 0;
-}
-
-/*
- * deal with seq counter wrapping correctly.
- * refer to timer_after() for jiffies wrapping handling
- */
-static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
-{
- u32 iv32_n, iv16_n;
- u32 iv32_o, iv16_o;
-
- iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
- iv16_n = (pn_n[4] << 8) | pn_n[5];
-
- iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
- iv16_o = (pn_o[4] << 8) | pn_o[5];
-
- if ((s32)iv32_n - (s32)iv32_o < 0 ||
- (iv32_n == iv32_o && iv16_n <= iv16_o))
- return 1;
- return 0;
-}
-
-static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct ieee80211_ccmp_data *key = priv;
- u8 keyidx, *pos;
- struct ieee80211_hdr_4addr *hdr;
- u8 *b0 = key->rx_b0;
- u8 *b = key->rx_b;
- u8 *a = key->rx_a;
- u8 pn[6];
- int i, blocks, last, len;
- size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
- u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
- DECLARE_MAC_BUF(mac);
-
- if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
- key->dot11RSNAStatsCCMPFormatErrors++;
- return -1;
- }
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- pos = skb->data + hdr_len;
- keyidx = pos[3];
- if (!(keyidx & (1 << 5))) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: received packet without ExtIV"
- " flag from %s\n", print_mac(mac, hdr->addr2));
- }
- key->dot11RSNAStatsCCMPFormatErrors++;
- return -2;
- }
- keyidx >>= 6;
- if (key->key_idx != keyidx) {
- printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
- return -6;
- }
- if (!key->key_set) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: received packet from %s"
- " with keyid=%d that does not have a configured"
- " key\n", print_mac(mac, hdr->addr2), keyidx);
- }
- return -3;
- }
-
- pn[0] = pos[7];
- pn[1] = pos[6];
- pn[2] = pos[5];
- pn[3] = pos[4];
- pn[4] = pos[1];
- pn[5] = pos[0];
- pos += 8;
-
- if (ccmp_replay_check(pn, key->rx_pn)) {
- if (ieee80211_ratelimit_debug(IEEE80211_DL_DROP)) {
- IEEE80211_DEBUG_DROP("CCMP: replay detected: STA=%s "
- "previous PN %02x%02x%02x%02x%02x%02x "
- "received PN %02x%02x%02x%02x%02x%02x\n",
- print_mac(mac, hdr->addr2),
- key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
- key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
- pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
- }
- key->dot11RSNAStatsCCMPReplays++;
- return -4;
- }
-
- ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
- xor_block(mic, b, CCMP_MIC_LEN);
-
- blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
- last = data_len % AES_BLOCK_LEN;
-
- for (i = 1; i <= blocks; i++) {
- len = (i == blocks && last) ? last : AES_BLOCK_LEN;
- /* Decrypt, with counter */
- b0[14] = (i >> 8) & 0xff;
- b0[15] = i & 0xff;
- ieee80211_ccmp_aes_encrypt(key->tfm, b0, b);
- xor_block(pos, b, len);
- /* Authentication */
- xor_block(a, pos, len);
- ieee80211_ccmp_aes_encrypt(key->tfm, a, a);
- pos += len;
- }
-
- if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "CCMP: decrypt failed: STA="
- "%s\n", print_mac(mac, hdr->addr2));
- }
- key->dot11RSNAStatsCCMPDecryptErrors++;
- return -5;
- }
-
- memcpy(key->rx_pn, pn, CCMP_PN_LEN);
-
- /* Remove hdr and MIC */
- memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
- skb_pull(skb, CCMP_HDR_LEN);
- skb_trim(skb, skb->len - CCMP_MIC_LEN);
-
- return keyidx;
-}
-
-static int ieee80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
-{
- struct ieee80211_ccmp_data *data = priv;
- int keyidx;
- struct crypto_cipher *tfm = data->tfm;
-
- keyidx = data->key_idx;
- memset(data, 0, sizeof(*data));
- data->key_idx = keyidx;
- data->tfm = tfm;
- if (len == CCMP_TK_LEN) {
- memcpy(data->key, key, CCMP_TK_LEN);
- data->key_set = 1;
- if (seq) {
- data->rx_pn[0] = seq[5];
- data->rx_pn[1] = seq[4];
- data->rx_pn[2] = seq[3];
- data->rx_pn[3] = seq[2];
- data->rx_pn[4] = seq[1];
- data->rx_pn[5] = seq[0];
- }
- crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
- } else if (len == 0)
- data->key_set = 0;
- else
- return -1;
-
- return 0;
-}
-
-static int ieee80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
-{
- struct ieee80211_ccmp_data *data = priv;
-
- if (len < CCMP_TK_LEN)
- return -1;
-
- if (!data->key_set)
- return 0;
- memcpy(key, data->key, CCMP_TK_LEN);
-
- if (seq) {
- seq[0] = data->tx_pn[5];
- seq[1] = data->tx_pn[4];
- seq[2] = data->tx_pn[3];
- seq[3] = data->tx_pn[2];
- seq[4] = data->tx_pn[1];
- seq[5] = data->tx_pn[0];
- }
-
- return CCMP_TK_LEN;
-}
-
-static char *ieee80211_ccmp_print_stats(char *p, void *priv)
-{
- struct ieee80211_ccmp_data *ccmp = priv;
-
- p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
- "tx_pn=%02x%02x%02x%02x%02x%02x "
- "rx_pn=%02x%02x%02x%02x%02x%02x "
- "format_errors=%d replays=%d decrypt_errors=%d\n",
- ccmp->key_idx, ccmp->key_set,
- ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
- ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
- ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
- ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
- ccmp->dot11RSNAStatsCCMPFormatErrors,
- ccmp->dot11RSNAStatsCCMPReplays,
- ccmp->dot11RSNAStatsCCMPDecryptErrors);
-
- return p;
-}
-
-static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = {
- .name = "CCMP",
- .init = ieee80211_ccmp_init,
- .deinit = ieee80211_ccmp_deinit,
- .build_iv = ieee80211_ccmp_hdr,
- .encrypt_mpdu = ieee80211_ccmp_encrypt,
- .decrypt_mpdu = ieee80211_ccmp_decrypt,
- .encrypt_msdu = NULL,
- .decrypt_msdu = NULL,
- .set_key = ieee80211_ccmp_set_key,
- .get_key = ieee80211_ccmp_get_key,
- .print_stats = ieee80211_ccmp_print_stats,
- .extra_mpdu_prefix_len = CCMP_HDR_LEN,
- .extra_mpdu_postfix_len = CCMP_MIC_LEN,
- .owner = THIS_MODULE,
-};
-
-static int __init ieee80211_crypto_ccmp_init(void)
-{
- return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp);
-}
-
-static void __exit ieee80211_crypto_ccmp_exit(void)
-{
- ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp);
-}
-
-module_init(ieee80211_crypto_ccmp_init);
-module_exit(ieee80211_crypto_ccmp_exit);
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
deleted file mode 100644
index bba0152e2d71..000000000000
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ /dev/null
@@ -1,787 +0,0 @@
-/*
- * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
- *
- * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/scatterlist.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/mm.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <asm/string.h>
-
-#include <net/ieee80211.h>
-
-#include <linux/crypto.h>
-#include <linux/crc32.h>
-
-MODULE_AUTHOR("Jouni Malinen");
-MODULE_DESCRIPTION("Host AP crypt: TKIP");
-MODULE_LICENSE("GPL");
-
-struct ieee80211_tkip_data {
-#define TKIP_KEY_LEN 32
- u8 key[TKIP_KEY_LEN];
- int key_set;
-
- u32 tx_iv32;
- u16 tx_iv16;
- u16 tx_ttak[5];
- int tx_phase1_done;
-
- u32 rx_iv32;
- u16 rx_iv16;
- u16 rx_ttak[5];
- int rx_phase1_done;
- u32 rx_iv32_new;
- u16 rx_iv16_new;
-
- u32 dot11RSNAStatsTKIPReplays;
- u32 dot11RSNAStatsTKIPICVErrors;
- u32 dot11RSNAStatsTKIPLocalMICFailures;
-
- int key_idx;
-
- struct crypto_blkcipher *rx_tfm_arc4;
- struct crypto_hash *rx_tfm_michael;
- struct crypto_blkcipher *tx_tfm_arc4;
- struct crypto_hash *tx_tfm_michael;
-
- /* scratch buffers for virt_to_page() (crypto API) */
- u8 rx_hdr[16], tx_hdr[16];
-
- unsigned long flags;
-};
-
-static unsigned long ieee80211_tkip_set_flags(unsigned long flags, void *priv)
-{
- struct ieee80211_tkip_data *_priv = priv;
- unsigned long old_flags = _priv->flags;
- _priv->flags = flags;
- return old_flags;
-}
-
-static unsigned long ieee80211_tkip_get_flags(void *priv)
-{
- struct ieee80211_tkip_data *_priv = priv;
- return _priv->flags;
-}
-
-static void *ieee80211_tkip_init(int key_idx)
-{
- struct ieee80211_tkip_data *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
- if (priv == NULL)
- goto fail;
-
- priv->key_idx = key_idx;
-
- priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
- CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->tx_tfm_arc4)) {
- printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
- "crypto API arc4\n");
- priv->tx_tfm_arc4 = NULL;
- goto fail;
- }
-
- priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
- CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->tx_tfm_michael)) {
- printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
- "crypto API michael_mic\n");
- priv->tx_tfm_michael = NULL;
- goto fail;
- }
-
- priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
- CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->rx_tfm_arc4)) {
- printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
- "crypto API arc4\n");
- priv->rx_tfm_arc4 = NULL;
- goto fail;
- }
-
- priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
- CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->rx_tfm_michael)) {
- printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
- "crypto API michael_mic\n");
- priv->rx_tfm_michael = NULL;
- goto fail;
- }
-
- return priv;
-
- fail:
- if (priv) {
- if (priv->tx_tfm_michael)
- crypto_free_hash(priv->tx_tfm_michael);
- if (priv->tx_tfm_arc4)
- crypto_free_blkcipher(priv->tx_tfm_arc4);
- if (priv->rx_tfm_michael)
- crypto_free_hash(priv->rx_tfm_michael);
- if (priv->rx_tfm_arc4)
- crypto_free_blkcipher(priv->rx_tfm_arc4);
- kfree(priv);
- }
-
- return NULL;
-}
-
-static void ieee80211_tkip_deinit(void *priv)
-{
- struct ieee80211_tkip_data *_priv = priv;
- if (_priv) {
- if (_priv->tx_tfm_michael)
- crypto_free_hash(_priv->tx_tfm_michael);
- if (_priv->tx_tfm_arc4)
- crypto_free_blkcipher(_priv->tx_tfm_arc4);
- if (_priv->rx_tfm_michael)
- crypto_free_hash(_priv->rx_tfm_michael);
- if (_priv->rx_tfm_arc4)
- crypto_free_blkcipher(_priv->rx_tfm_arc4);
- }
- kfree(priv);
-}
-
-static inline u16 RotR1(u16 val)
-{
- return (val >> 1) | (val << 15);
-}
-
-static inline u8 Lo8(u16 val)
-{
- return val & 0xff;
-}
-
-static inline u8 Hi8(u16 val)
-{
- return val >> 8;
-}
-
-static inline u16 Lo16(u32 val)
-{
- return val & 0xffff;
-}
-
-static inline u16 Hi16(u32 val)
-{
- return val >> 16;
-}
-
-static inline u16 Mk16(u8 hi, u8 lo)
-{
- return lo | (((u16) hi) << 8);
-}
-
-static inline u16 Mk16_le(__le16 * v)
-{
- return le16_to_cpu(*v);
-}
-
-static const u16 Sbox[256] = {
- 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
- 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
- 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
- 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
- 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
- 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
- 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
- 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
- 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
- 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
- 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
- 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
- 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
- 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
- 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
- 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
- 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
- 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
- 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
- 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
- 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
- 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
- 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
- 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
- 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
- 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
- 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
- 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
- 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
- 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
- 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
- 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
-};
-
-static inline u16 _S_(u16 v)
-{
- u16 t = Sbox[Hi8(v)];
- return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
-}
-
-#define PHASE1_LOOP_COUNT 8
-
-static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
- u32 IV32)
-{
- int i, j;
-
- /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
- TTAK[0] = Lo16(IV32);
- TTAK[1] = Hi16(IV32);
- TTAK[2] = Mk16(TA[1], TA[0]);
- TTAK[3] = Mk16(TA[3], TA[2]);
- TTAK[4] = Mk16(TA[5], TA[4]);
-
- for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
- j = 2 * (i & 1);
- TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
- TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
- TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
- TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
- TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
- }
-}
-
-static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
- u16 IV16)
-{
- /* Make temporary area overlap WEP seed so that the final copy can be
- * avoided on little endian hosts. */
- u16 *PPK = (u16 *) & WEPSeed[4];
-
- /* Step 1 - make copy of TTAK and bring in TSC */
- PPK[0] = TTAK[0];
- PPK[1] = TTAK[1];
- PPK[2] = TTAK[2];
- PPK[3] = TTAK[3];
- PPK[4] = TTAK[4];
- PPK[5] = TTAK[4] + IV16;
-
- /* Step 2 - 96-bit bijective mixing using S-box */
- PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
- PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
- PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
- PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
- PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
- PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
-
- PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
- PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
- PPK[2] += RotR1(PPK[1]);
- PPK[3] += RotR1(PPK[2]);
- PPK[4] += RotR1(PPK[3]);
- PPK[5] += RotR1(PPK[4]);
-
- /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
- * WEPSeed[0..2] is transmitted as WEP IV */
- WEPSeed[0] = Hi8(IV16);
- WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
- WEPSeed[2] = Lo8(IV16);
- WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
-
-#ifdef __BIG_ENDIAN
- {
- int i;
- for (i = 0; i < 6; i++)
- PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
- }
-#endif
-}
-
-static int ieee80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
- u8 * rc4key, int keylen, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- int len;
- u8 *pos;
- struct ieee80211_hdr_4addr *hdr;
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
-
- if (skb_headroom(skb) < 8 || skb->len < hdr_len)
- return -1;
-
- if (rc4key == NULL || keylen < 16)
- return -1;
-
- if (!tkey->tx_phase1_done) {
- tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
- tkey->tx_iv32);
- tkey->tx_phase1_done = 1;
- }
- tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
-
- len = skb->len - hdr_len;
- pos = skb_push(skb, 8);
- memmove(pos, pos + 8, hdr_len);
- pos += hdr_len;
-
- *pos++ = *rc4key;
- *pos++ = *(rc4key + 1);
- *pos++ = *(rc4key + 2);
- *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
- *pos++ = tkey->tx_iv32 & 0xff;
- *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
- *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
- *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
-
- tkey->tx_iv16++;
- if (tkey->tx_iv16 == 0) {
- tkey->tx_phase1_done = 0;
- tkey->tx_iv32++;
- }
-
- return 8;
-}
-
-static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
- int len;
- u8 rc4key[16], *pos, *icv;
- u32 crc;
- struct scatterlist sg;
- DECLARE_MAC_BUF(mac);
-
- if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
- if (net_ratelimit()) {
- struct ieee80211_hdr_4addr *hdr =
- (struct ieee80211_hdr_4addr *)skb->data;
- printk(KERN_DEBUG ": TKIP countermeasures: dropped "
- "TX packet to %s\n",
- print_mac(mac, hdr->addr1));
- }
- return -1;
- }
-
- if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
- return -1;
-
- len = skb->len - hdr_len;
- pos = skb->data + hdr_len;
-
- if ((ieee80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
- return -1;
-
- icv = skb_put(skb, 4);
-
- crc = ~crc32_le(~0, pos, len);
- icv[0] = crc;
- icv[1] = crc >> 8;
- icv[2] = crc >> 16;
- icv[3] = crc >> 24;
-
- crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
- sg_init_one(&sg, pos, len + 4);
- return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
-}
-
-/*
- * deal with seq counter wrapping correctly.
- * refer to timer_after() for jiffies wrapping handling
- */
-static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
- u32 iv32_o, u16 iv16_o)
-{
- if ((s32)iv32_n - (s32)iv32_o < 0 ||
- (iv32_n == iv32_o && iv16_n <= iv16_o))
- return 1;
- return 0;
-}
-
-static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
- u8 rc4key[16];
- u8 keyidx, *pos;
- u32 iv32;
- u16 iv16;
- struct ieee80211_hdr_4addr *hdr;
- u8 icv[4];
- u32 crc;
- struct scatterlist sg;
- int plen;
- DECLARE_MAC_BUF(mac);
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
-
- if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG ": TKIP countermeasures: dropped "
- "received packet from %s\n",
- print_mac(mac, hdr->addr2));
- }
- return -1;
- }
-
- if (skb->len < hdr_len + 8 + 4)
- return -1;
-
- pos = skb->data + hdr_len;
- keyidx = pos[3];
- if (!(keyidx & (1 << 5))) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "TKIP: received packet without ExtIV"
- " flag from %s\n", print_mac(mac, hdr->addr2));
- }
- return -2;
- }
- keyidx >>= 6;
- if (tkey->key_idx != keyidx) {
- printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
- return -6;
- }
- if (!tkey->key_set) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "TKIP: received packet from %s"
- " with keyid=%d that does not have a configured"
- " key\n", print_mac(mac, hdr->addr2), keyidx);
- }
- return -3;
- }
- iv16 = (pos[0] << 8) | pos[2];
- iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
- pos += 8;
-
- if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
- if (ieee80211_ratelimit_debug(IEEE80211_DL_DROP)) {
- IEEE80211_DEBUG_DROP("TKIP: replay detected: STA=%s"
- " previous TSC %08x%04x received TSC "
- "%08x%04x\n", print_mac(mac, hdr->addr2),
- tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
- }
- tkey->dot11RSNAStatsTKIPReplays++;
- return -4;
- }
-
- if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
- tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
- tkey->rx_phase1_done = 1;
- }
- tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
-
- plen = skb->len - hdr_len - 12;
-
- crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
- sg_init_one(&sg, pos, plen + 4);
- if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG ": TKIP: failed to decrypt "
- "received packet from %s\n",
- print_mac(mac, hdr->addr2));
- }
- return -7;
- }
-
- crc = ~crc32_le(~0, pos, plen);
- icv[0] = crc;
- icv[1] = crc >> 8;
- icv[2] = crc >> 16;
- icv[3] = crc >> 24;
- if (memcmp(icv, pos + plen, 4) != 0) {
- if (iv32 != tkey->rx_iv32) {
- /* Previously cached Phase1 result was already lost, so
- * it needs to be recalculated for the next packet. */
- tkey->rx_phase1_done = 0;
- }
- if (ieee80211_ratelimit_debug(IEEE80211_DL_DROP)) {
- IEEE80211_DEBUG_DROP("TKIP: ICV error detected: STA="
- "%s\n", print_mac(mac, hdr->addr2));
- }
- tkey->dot11RSNAStatsTKIPICVErrors++;
- return -5;
- }
-
- /* Update real counters only after Michael MIC verification has
- * completed */
- tkey->rx_iv32_new = iv32;
- tkey->rx_iv16_new = iv16;
-
- /* Remove IV and ICV */
- memmove(skb->data + 8, skb->data, hdr_len);
- skb_pull(skb, 8);
- skb_trim(skb, skb->len - 4);
-
- return keyidx;
-}
-
-static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
- u8 * data, size_t data_len, u8 * mic)
-{
- struct hash_desc desc;
- struct scatterlist sg[2];
-
- if (tfm_michael == NULL) {
- printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
- return -1;
- }
- sg_init_table(sg, 2);
- sg_set_buf(&sg[0], hdr, 16);
- sg_set_buf(&sg[1], data, data_len);
-
- if (crypto_hash_setkey(tfm_michael, key, 8))
- return -1;
-
- desc.tfm = tfm_michael;
- desc.flags = 0;
- return crypto_hash_digest(&desc, sg, data_len + 16, mic);
-}
-
-static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
-{
- struct ieee80211_hdr_4addr *hdr11;
- u16 stype;
-
- hdr11 = (struct ieee80211_hdr_4addr *)skb->data;
- stype = WLAN_FC_GET_STYPE(le16_to_cpu(hdr11->frame_ctl));
-
- switch (le16_to_cpu(hdr11->frame_ctl) &
- (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
- case IEEE80211_FCTL_TODS:
- memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
- break;
- case IEEE80211_FCTL_FROMDS:
- memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
- break;
- case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
- memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
- break;
- case 0:
- memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
- memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
- break;
- }
-
- if (stype & IEEE80211_STYPE_QOS_DATA) {
- const struct ieee80211_hdr_3addrqos *qoshdr =
- (struct ieee80211_hdr_3addrqos *)skb->data;
- hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID;
- } else
- hdr[12] = 0; /* priority */
-
- hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
-}
-
-static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
- void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- u8 *pos;
-
- if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
- printk(KERN_DEBUG "Invalid packet for Michael MIC add "
- "(tailroom=%d hdr_len=%d skb->len=%d)\n",
- skb_tailroom(skb), hdr_len, skb->len);
- return -1;
- }
-
- michael_mic_hdr(skb, tkey->tx_hdr);
- pos = skb_put(skb, 8);
- if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
- skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
- return -1;
-
- return 0;
-}
-
-static void ieee80211_michael_mic_failure(struct net_device *dev,
- struct ieee80211_hdr_4addr *hdr,
- int keyidx)
-{
- union iwreq_data wrqu;
- struct iw_michaelmicfailure ev;
-
- /* TODO: needed parameters: count, keyid, key type, TSC */
- memset(&ev, 0, sizeof(ev));
- ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
- if (hdr->addr1[0] & 0x01)
- ev.flags |= IW_MICFAILURE_GROUP;
- else
- ev.flags |= IW_MICFAILURE_PAIRWISE;
- ev.src_addr.sa_family = ARPHRD_ETHER;
- memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = sizeof(ev);
- wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
-}
-
-static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
- int hdr_len, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- u8 mic[8];
- DECLARE_MAC_BUF(mac);
-
- if (!tkey->key_set)
- return -1;
-
- michael_mic_hdr(skb, tkey->rx_hdr);
- if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
- skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
- return -1;
- if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
- struct ieee80211_hdr_4addr *hdr;
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- printk(KERN_DEBUG "%s: Michael MIC verification failed for "
- "MSDU from %s keyidx=%d\n",
- skb->dev ? skb->dev->name : "N/A", print_mac(mac, hdr->addr2),
- keyidx);
- if (skb->dev)
- ieee80211_michael_mic_failure(skb->dev, hdr, keyidx);
- tkey->dot11RSNAStatsTKIPLocalMICFailures++;
- return -1;
- }
-
- /* Update TSC counters for RX now that the packet verification has
- * completed. */
- tkey->rx_iv32 = tkey->rx_iv32_new;
- tkey->rx_iv16 = tkey->rx_iv16_new;
-
- skb_trim(skb, skb->len - 8);
-
- return 0;
-}
-
-static int ieee80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
- int keyidx;
- struct crypto_hash *tfm = tkey->tx_tfm_michael;
- struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
- struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
- struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
-
- keyidx = tkey->key_idx;
- memset(tkey, 0, sizeof(*tkey));
- tkey->key_idx = keyidx;
- tkey->tx_tfm_michael = tfm;
- tkey->tx_tfm_arc4 = tfm2;
- tkey->rx_tfm_michael = tfm3;
- tkey->rx_tfm_arc4 = tfm4;
- if (len == TKIP_KEY_LEN) {
- memcpy(tkey->key, key, TKIP_KEY_LEN);
- tkey->key_set = 1;
- tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
- if (seq) {
- tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
- (seq[3] << 8) | seq[2];
- tkey->rx_iv16 = (seq[1] << 8) | seq[0];
- }
- } else if (len == 0)
- tkey->key_set = 0;
- else
- return -1;
-
- return 0;
-}
-
-static int ieee80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
-{
- struct ieee80211_tkip_data *tkey = priv;
-
- if (len < TKIP_KEY_LEN)
- return -1;
-
- if (!tkey->key_set)
- return 0;
- memcpy(key, tkey->key, TKIP_KEY_LEN);
-
- if (seq) {
- /* Return the sequence number of the last transmitted frame. */
- u16 iv16 = tkey->tx_iv16;
- u32 iv32 = tkey->tx_iv32;
- if (iv16 == 0)
- iv32--;
- iv16--;
- seq[0] = tkey->tx_iv16;
- seq[1] = tkey->tx_iv16 >> 8;
- seq[2] = tkey->tx_iv32;
- seq[3] = tkey->tx_iv32 >> 8;
- seq[4] = tkey->tx_iv32 >> 16;
- seq[5] = tkey->tx_iv32 >> 24;
- }
-
- return TKIP_KEY_LEN;
-}
-
-static char *ieee80211_tkip_print_stats(char *p, void *priv)
-{
- struct ieee80211_tkip_data *tkip = priv;
- p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
- "tx_pn=%02x%02x%02x%02x%02x%02x "
- "rx_pn=%02x%02x%02x%02x%02x%02x "
- "replays=%d icv_errors=%d local_mic_failures=%d\n",
- tkip->key_idx, tkip->key_set,
- (tkip->tx_iv32 >> 24) & 0xff,
- (tkip->tx_iv32 >> 16) & 0xff,
- (tkip->tx_iv32 >> 8) & 0xff,
- tkip->tx_iv32 & 0xff,
- (tkip->tx_iv16 >> 8) & 0xff,
- tkip->tx_iv16 & 0xff,
- (tkip->rx_iv32 >> 24) & 0xff,
- (tkip->rx_iv32 >> 16) & 0xff,
- (tkip->rx_iv32 >> 8) & 0xff,
- tkip->rx_iv32 & 0xff,
- (tkip->rx_iv16 >> 8) & 0xff,
- tkip->rx_iv16 & 0xff,
- tkip->dot11RSNAStatsTKIPReplays,
- tkip->dot11RSNAStatsTKIPICVErrors,
- tkip->dot11RSNAStatsTKIPLocalMICFailures);
- return p;
-}
-
-static struct ieee80211_crypto_ops ieee80211_crypt_tkip = {
- .name = "TKIP",
- .init = ieee80211_tkip_init,
- .deinit = ieee80211_tkip_deinit,
- .build_iv = ieee80211_tkip_hdr,
- .encrypt_mpdu = ieee80211_tkip_encrypt,
- .decrypt_mpdu = ieee80211_tkip_decrypt,
- .encrypt_msdu = ieee80211_michael_mic_add,
- .decrypt_msdu = ieee80211_michael_mic_verify,
- .set_key = ieee80211_tkip_set_key,
- .get_key = ieee80211_tkip_get_key,
- .print_stats = ieee80211_tkip_print_stats,
- .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
- .extra_mpdu_postfix_len = 4, /* ICV */
- .extra_msdu_postfix_len = 8, /* MIC */
- .get_flags = ieee80211_tkip_get_flags,
- .set_flags = ieee80211_tkip_set_flags,
- .owner = THIS_MODULE,
-};
-
-static int __init ieee80211_crypto_tkip_init(void)
-{
- return ieee80211_register_crypto_ops(&ieee80211_crypt_tkip);
-}
-
-static void __exit ieee80211_crypto_tkip_exit(void)
-{
- ieee80211_unregister_crypto_ops(&ieee80211_crypt_tkip);
-}
-
-module_init(ieee80211_crypto_tkip_init);
-module_exit(ieee80211_crypto_tkip_exit);
diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c
deleted file mode 100644
index 3fa30c40779f..000000000000
--- a/net/ieee80211/ieee80211_crypt_wep.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Host AP crypt: host-based WEP encryption implementation for Host AP driver
- *
- * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/scatterlist.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <asm/string.h>
-
-#include <net/ieee80211.h>
-
-#include <linux/crypto.h>
-#include <linux/crc32.h>
-
-MODULE_AUTHOR("Jouni Malinen");
-MODULE_DESCRIPTION("Host AP crypt: WEP");
-MODULE_LICENSE("GPL");
-
-struct prism2_wep_data {
- u32 iv;
-#define WEP_KEY_LEN 13
- u8 key[WEP_KEY_LEN + 1];
- u8 key_len;
- u8 key_idx;
- struct crypto_blkcipher *tx_tfm;
- struct crypto_blkcipher *rx_tfm;
-};
-
-static void *prism2_wep_init(int keyidx)
-{
- struct prism2_wep_data *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
- if (priv == NULL)
- goto fail;
- priv->key_idx = keyidx;
-
- priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->tx_tfm)) {
- printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate "
- "crypto API arc4\n");
- priv->tx_tfm = NULL;
- goto fail;
- }
-
- priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(priv->rx_tfm)) {
- printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate "
- "crypto API arc4\n");
- priv->rx_tfm = NULL;
- goto fail;
- }
- /* start WEP IV from a random value */
- get_random_bytes(&priv->iv, 4);
-
- return priv;
-
- fail:
- if (priv) {
- if (priv->tx_tfm)
- crypto_free_blkcipher(priv->tx_tfm);
- if (priv->rx_tfm)
- crypto_free_blkcipher(priv->rx_tfm);
- kfree(priv);
- }
- return NULL;
-}
-
-static void prism2_wep_deinit(void *priv)
-{
- struct prism2_wep_data *_priv = priv;
- if (_priv) {
- if (_priv->tx_tfm)
- crypto_free_blkcipher(_priv->tx_tfm);
- if (_priv->rx_tfm)
- crypto_free_blkcipher(_priv->rx_tfm);
- }
- kfree(priv);
-}
-
-/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
-static int prism2_wep_build_iv(struct sk_buff *skb, int hdr_len,
- u8 *key, int keylen, void *priv)
-{
- struct prism2_wep_data *wep = priv;
- u32 klen, len;
- u8 *pos;
-
- if (skb_headroom(skb) < 4 || skb->len < hdr_len)
- return -1;
-
- len = skb->len - hdr_len;
- pos = skb_push(skb, 4);
- memmove(pos, pos + 4, hdr_len);
- pos += hdr_len;
-
- klen = 3 + wep->key_len;
-
- wep->iv++;
-
- /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
- * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
- * can be used to speedup attacks, so avoid using them. */
- if ((wep->iv & 0xff00) == 0xff00) {
- u8 B = (wep->iv >> 16) & 0xff;
- if (B >= 3 && B < klen)
- wep->iv += 0x0100;
- }
-
- /* Prepend 24-bit IV to RC4 key and TX frame */
- *pos++ = (wep->iv >> 16) & 0xff;
- *pos++ = (wep->iv >> 8) & 0xff;
- *pos++ = wep->iv & 0xff;
- *pos++ = wep->key_idx << 6;
-
- return 0;
-}
-
-/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
- * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
- * so the payload length increases with 8 bytes.
- *
- * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
- */
-static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct prism2_wep_data *wep = priv;
- struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
- u32 crc, klen, len;
- u8 *pos, *icv;
- struct scatterlist sg;
- u8 key[WEP_KEY_LEN + 3];
-
- /* other checks are in prism2_wep_build_iv */
- if (skb_tailroom(skb) < 4)
- return -1;
-
- /* add the IV to the frame */
- if (prism2_wep_build_iv(skb, hdr_len, NULL, 0, priv))
- return -1;
-
- /* Copy the IV into the first 3 bytes of the key */
- skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
-
- /* Copy rest of the WEP key (the secret part) */
- memcpy(key + 3, wep->key, wep->key_len);
-
- len = skb->len - hdr_len - 4;
- pos = skb->data + hdr_len + 4;
- klen = 3 + wep->key_len;
-
- /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
- crc = ~crc32_le(~0, pos, len);
- icv = skb_put(skb, 4);
- icv[0] = crc;
- icv[1] = crc >> 8;
- icv[2] = crc >> 16;
- icv[3] = crc >> 24;
-
- crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
- sg_init_one(&sg, pos, len + 4);
- return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
-}
-
-/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
- * the frame: IV (4 bytes), encrypted payload (including SNAP header),
- * ICV (4 bytes). len includes both IV and ICV.
- *
- * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
- * failure. If frame is OK, IV and ICV will be removed.
- */
-static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
-{
- struct prism2_wep_data *wep = priv;
- struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
- u32 crc, klen, plen;
- u8 key[WEP_KEY_LEN + 3];
- u8 keyidx, *pos, icv[4];
- struct scatterlist sg;
-
- if (skb->len < hdr_len + 8)
- return -1;
-
- pos = skb->data + hdr_len;
- key[0] = *pos++;
- key[1] = *pos++;
- key[2] = *pos++;
- keyidx = *pos++ >> 6;
- if (keyidx != wep->key_idx)
- return -1;
-
- klen = 3 + wep->key_len;
-
- /* Copy rest of the WEP key (the secret part) */
- memcpy(key + 3, wep->key, wep->key_len);
-
- /* Apply RC4 to data and compute CRC32 over decrypted data */
- plen = skb->len - hdr_len - 8;
-
- crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
- sg_init_one(&sg, pos, plen + 4);
- if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
- return -7;
-
- crc = ~crc32_le(~0, pos, plen);
- icv[0] = crc;
- icv[1] = crc >> 8;
- icv[2] = crc >> 16;
- icv[3] = crc >> 24;
- if (memcmp(icv, pos + plen, 4) != 0) {
- /* ICV mismatch - drop frame */
- return -2;
- }
-
- /* Remove IV and ICV */
- memmove(skb->data + 4, skb->data, hdr_len);
- skb_pull(skb, 4);
- skb_trim(skb, skb->len - 4);
-
- return 0;
-}
-
-static int prism2_wep_set_key(void *key, int len, u8 * seq, void *priv)
-{
- struct prism2_wep_data *wep = priv;
-
- if (len < 0 || len > WEP_KEY_LEN)
- return -1;
-
- memcpy(wep->key, key, len);
- wep->key_len = len;
-
- return 0;
-}
-
-static int prism2_wep_get_key(void *key, int len, u8 * seq, void *priv)
-{
- struct prism2_wep_data *wep = priv;
-
- if (len < wep->key_len)
- return -1;
-
- memcpy(key, wep->key, wep->key_len);
-
- return wep->key_len;
-}
-
-static char *prism2_wep_print_stats(char *p, void *priv)
-{
- struct prism2_wep_data *wep = priv;
- p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
- return p;
-}
-
-static struct ieee80211_crypto_ops ieee80211_crypt_wep = {
- .name = "WEP",
- .init = prism2_wep_init,
- .deinit = prism2_wep_deinit,
- .build_iv = prism2_wep_build_iv,
- .encrypt_mpdu = prism2_wep_encrypt,
- .decrypt_mpdu = prism2_wep_decrypt,
- .encrypt_msdu = NULL,
- .decrypt_msdu = NULL,
- .set_key = prism2_wep_set_key,
- .get_key = prism2_wep_get_key,
- .print_stats = prism2_wep_print_stats,
- .extra_mpdu_prefix_len = 4, /* IV */
- .extra_mpdu_postfix_len = 4, /* ICV */
- .owner = THIS_MODULE,
-};
-
-static int __init ieee80211_crypto_wep_init(void)
-{
- return ieee80211_register_crypto_ops(&ieee80211_crypt_wep);
-}
-
-static void __exit ieee80211_crypto_wep_exit(void)
-{
- ieee80211_unregister_crypto_ops(&ieee80211_crypt_wep);
-}
-
-module_init(ieee80211_crypto_wep_init);
-module_exit(ieee80211_crypto_wep_exit);
diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c
deleted file mode 100644
index 949772a5a7dc..000000000000
--- a/net/ieee80211/ieee80211_module.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*******************************************************************************
-
- Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
-
- Portions of this file are based on the WEP enablement code provided by the
- Host AP project hostap-drivers v0.1.3
- Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- <j@w1.fi>
- Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
-
- 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., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-*******************************************************************************/
-
-#include <linux/compiler.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/wireless.h>
-#include <linux/etherdevice.h>
-#include <asm/uaccess.h>
-#include <net/net_namespace.h>
-#include <net/arp.h>
-
-#include <net/ieee80211.h>
-
-#define DRV_DESCRIPTION "802.11 data/management/control stack"
-#define DRV_NAME "ieee80211"
-#define DRV_VERSION IEEE80211_VERSION
-#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>"
-
-MODULE_VERSION(DRV_VERSION);
-MODULE_DESCRIPTION(DRV_DESCRIPTION);
-MODULE_AUTHOR(DRV_COPYRIGHT);
-MODULE_LICENSE("GPL");
-
-static int ieee80211_networks_allocate(struct ieee80211_device *ieee)
-{
- if (ieee->networks)
- return 0;
-
- ieee->networks =
- kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network),
- GFP_KERNEL);
- if (!ieee->networks) {
- printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
- ieee->dev->name);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void ieee80211_network_reset(struct ieee80211_network *network)
-{
- if (!network)
- return;
-
- if (network->ibss_dfs) {
- kfree(network->ibss_dfs);
- network->ibss_dfs = NULL;
- }
-}
-
-static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
-{
- int i;
-
- if (!ieee->networks)
- return;
-
- for (i = 0; i < MAX_NETWORK_COUNT; i++)
- if (ieee->networks[i].ibss_dfs)
- kfree(ieee->networks[i].ibss_dfs);
-
- kfree(ieee->networks);
- ieee->networks = NULL;
-}
-
-static void ieee80211_networks_initialize(struct ieee80211_device *ieee)
-{
- int i;
-
- INIT_LIST_HEAD(&ieee->network_free_list);
- INIT_LIST_HEAD(&ieee->network_list);
- for (i = 0; i < MAX_NETWORK_COUNT; i++)
- list_add_tail(&ieee->networks[i].list,
- &ieee->network_free_list);
-}
-
-static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-static struct net_device_stats *ieee80211_generic_get_stats(
- struct net_device *dev)
-{
- struct ieee80211_device *ieee = netdev_priv(dev);
- return &ieee->stats;
-}
-
-struct net_device *alloc_ieee80211(int sizeof_priv)
-{
- struct ieee80211_device *ieee;
- struct net_device *dev;
- int err;
-
- IEEE80211_DEBUG_INFO("Initializing...\n");
-
- dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
- if (!dev) {
- IEEE80211_ERROR("Unable to allocate network device.\n");
- goto failed;
- }
- ieee = netdev_priv(dev);
- dev->hard_start_xmit = ieee80211_xmit;
- dev->change_mtu = ieee80211_change_mtu;
-
- /* Drivers are free to override this if the generic implementation
- * does not meet their needs. */
- dev->get_stats = ieee80211_generic_get_stats;
-
- ieee->dev = dev;
-
- err = ieee80211_networks_allocate(ieee);
- if (err) {
- IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err);
- goto failed_free_netdev;
- }
- ieee80211_networks_initialize(ieee);
-
- /* Default fragmentation threshold is maximum payload size */
- ieee->fts = DEFAULT_FTS;
- ieee->rts = DEFAULT_FTS;
- ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
- ieee->open_wep = 1;
-
- /* Default to enabling full open WEP with host based encrypt/decrypt */
- ieee->host_encrypt = 1;
- ieee->host_decrypt = 1;
- ieee->host_mc_decrypt = 1;
-
- /* Host fragementation in Open mode. Default is enabled.
- * Note: host fragmentation is always enabled if host encryption
- * is enabled. For cards can do hardware encryption, they must do
- * hardware fragmentation as well. So we don't need a variable
- * like host_enc_frag. */
- ieee->host_open_frag = 1;
- ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
-
- INIT_LIST_HEAD(&ieee->crypt_deinit_list);
- setup_timer(&ieee->crypt_deinit_timer, ieee80211_crypt_deinit_handler,
- (unsigned long)ieee);
- ieee->crypt_quiesced = 0;
-
- spin_lock_init(&ieee->lock);
-
- ieee->wpa_enabled = 0;
- ieee->drop_unencrypted = 0;
- ieee->privacy_invoked = 0;
-
- return dev;
-
-failed_free_netdev:
- free_netdev(dev);
-failed:
- return NULL;
-}
-
-void free_ieee80211(struct net_device *dev)
-{
- struct ieee80211_device *ieee = netdev_priv(dev);
-
- int i;
-
- ieee80211_crypt_quiescing(ieee);
- del_timer_sync(&ieee->crypt_deinit_timer);
- ieee80211_crypt_deinit_entries(ieee, 1);
-
- for (i = 0; i < WEP_KEYS; i++) {
- struct ieee80211_crypt_data *crypt = ieee->crypt[i];
- if (crypt) {
- if (crypt->ops) {
- crypt->ops->deinit(crypt->priv);
- module_put(crypt->ops->owner);
- }
- kfree(crypt);
- ieee->crypt[i] = NULL;
- }
- }
-
- ieee80211_networks_free(ieee);
- free_netdev(dev);
-}
-
-#ifdef CONFIG_IEEE80211_DEBUG
-
-static int debug = 0;
-u32 ieee80211_debug_level = 0;
-EXPORT_SYMBOL_GPL(ieee80211_debug_level);
-static struct proc_dir_entry *ieee80211_proc = NULL;
-
-static int show_debug_level(char *page, char **start, off_t offset,
- int count, int *eof, void *data)
-{
- return snprintf(page, count, "0x%08X\n", ieee80211_debug_level);
-}
-
-static int store_debug_level(struct file *file, const char __user * buffer,
- unsigned long count, void *data)
-{
- char buf[] = "0x00000000\n";
- unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
- unsigned long val;
-
- if (copy_from_user(buf, buffer, len))
- return count;
- buf[len] = 0;
- if (sscanf(buf, "%li", &val) != 1)
- printk(KERN_INFO DRV_NAME
- ": %s is not in hex or decimal form.\n", buf);
- else
- ieee80211_debug_level = val;
-
- return strnlen(buf, len);
-}
-#endif /* CONFIG_IEEE80211_DEBUG */
-
-static int __init ieee80211_init(void)
-{
-#ifdef CONFIG_IEEE80211_DEBUG
- struct proc_dir_entry *e;
-
- ieee80211_debug_level = debug;
- ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
- if (ieee80211_proc == NULL) {
- IEEE80211_ERROR("Unable to create " DRV_NAME
- " proc directory\n");
- return -EIO;
- }
- e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
- ieee80211_proc);
- if (!e) {
- remove_proc_entry(DRV_NAME, init_net.proc_net);
- ieee80211_proc = NULL;
- return -EIO;
- }
- e->read_proc = show_debug_level;
- e->write_proc = store_debug_level;
- e->data = NULL;
-#endif /* CONFIG_IEEE80211_DEBUG */
-
- printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
- printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n");
-
- return 0;
-}
-
-static void __exit ieee80211_exit(void)
-{
-#ifdef CONFIG_IEEE80211_DEBUG
- if (ieee80211_proc) {
- remove_proc_entry("debug_level", ieee80211_proc);
- remove_proc_entry(DRV_NAME, init_net.proc_net);
- ieee80211_proc = NULL;
- }
-#endif /* CONFIG_IEEE80211_DEBUG */
-}
-
-#ifdef CONFIG_IEEE80211_DEBUG
-#include <linux/moduleparam.h>
-module_param(debug, int, 0444);
-MODULE_PARM_DESC(debug, "debug output mask");
-#endif /* CONFIG_IEEE80211_DEBUG */
-
-module_exit(ieee80211_exit);
-module_init(ieee80211_init);
-
-const char *escape_essid(const char *essid, u8 essid_len)
-{
- static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- const char *s = essid;
- char *d = escaped;
-
- if (ieee80211_is_empty_essid(essid, essid_len)) {
- memcpy(escaped, "<hidden>", sizeof("<hidden>"));
- return escaped;
- }
-
- essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
- while (essid_len--) {
- if (*s == '\0') {
- *d++ = '\\';
- *d++ = '0';
- s++;
- } else {
- *d++ = *s++;
- }
- }
- *d = '\0';
- return escaped;
-}
-
-EXPORT_SYMBOL(alloc_ieee80211);
-EXPORT_SYMBOL(free_ieee80211);
-EXPORT_SYMBOL(escape_essid);
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
deleted file mode 100644
index 69dbc342a464..000000000000
--- a/net/ieee80211/ieee80211_rx.c
+++ /dev/null
@@ -1,1831 +0,0 @@
-/*
- * Original code based Host AP (software wireless LAN access point) driver
- * for Intersil Prism2/2.5/3 - hostap.o module, common routines
- *
- * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- * <j@w1.fi>
- * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2005, Intel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
- */
-
-#include <linux/compiler.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/wireless.h>
-#include <linux/etherdevice.h>
-#include <asm/uaccess.h>
-#include <linux/ctype.h>
-
-#include <net/ieee80211.h>
-
-static void ieee80211_monitor_rx(struct ieee80211_device *ieee,
- struct sk_buff *skb,
- struct ieee80211_rx_stats *rx_stats)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- u16 fc = le16_to_cpu(hdr->frame_ctl);
-
- skb->dev = ieee->dev;
- skb_reset_mac_header(skb);
- skb_pull(skb, ieee80211_get_hdrlen(fc));
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = htons(ETH_P_80211_RAW);
- memset(skb->cb, 0, sizeof(skb->cb));
- netif_rx(skb);
-}
-
-/* Called only as a tasklet (software IRQ) */
-static struct ieee80211_frag_entry *ieee80211_frag_cache_find(struct
- ieee80211_device
- *ieee,
- unsigned int seq,
- unsigned int frag,
- u8 * src,
- u8 * dst)
-{
- struct ieee80211_frag_entry *entry;
- int i;
-
- for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) {
- entry = &ieee->frag_cache[i];
- if (entry->skb != NULL &&
- time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
- IEEE80211_DEBUG_FRAG("expiring fragment cache entry "
- "seq=%u last_frag=%u\n",
- entry->seq, entry->last_frag);
- dev_kfree_skb_any(entry->skb);
- entry->skb = NULL;
- }
-
- if (entry->skb != NULL && entry->seq == seq &&
- (entry->last_frag + 1 == frag || frag == -1) &&
- !compare_ether_addr(entry->src_addr, src) &&
- !compare_ether_addr(entry->dst_addr, dst))
- return entry;
- }
-
- return NULL;
-}
-
-/* Called only as a tasklet (software IRQ) */
-static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee,
- struct ieee80211_hdr_4addr *hdr)
-{
- struct sk_buff *skb = NULL;
- u16 sc;
- unsigned int frag, seq;
- struct ieee80211_frag_entry *entry;
-
- sc = le16_to_cpu(hdr->seq_ctl);
- frag = WLAN_GET_SEQ_FRAG(sc);
- seq = WLAN_GET_SEQ_SEQ(sc);
-
- if (frag == 0) {
- /* Reserve enough space to fit maximum frame length */
- skb = dev_alloc_skb(ieee->dev->mtu +
- sizeof(struct ieee80211_hdr_4addr) +
- 8 /* LLC */ +
- 2 /* alignment */ +
- 8 /* WEP */ + ETH_ALEN /* WDS */ );
- if (skb == NULL)
- return NULL;
-
- entry = &ieee->frag_cache[ieee->frag_next_idx];
- ieee->frag_next_idx++;
- if (ieee->frag_next_idx >= IEEE80211_FRAG_CACHE_LEN)
- ieee->frag_next_idx = 0;
-
- if (entry->skb != NULL)
- dev_kfree_skb_any(entry->skb);
-
- entry->first_frag_time = jiffies;
- entry->seq = seq;
- entry->last_frag = frag;
- entry->skb = skb;
- memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
- memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
- } else {
- /* received a fragment of a frame for which the head fragment
- * should have already been received */
- entry = ieee80211_frag_cache_find(ieee, seq, frag, hdr->addr2,
- hdr->addr1);
- if (entry != NULL) {
- entry->last_frag = frag;
- skb = entry->skb;
- }
- }
-
- return skb;
-}
-
-/* Called only as a tasklet (software IRQ) */
-static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee,
- struct ieee80211_hdr_4addr *hdr)
-{
- u16 sc;
- unsigned int seq;
- struct ieee80211_frag_entry *entry;
-
- sc = le16_to_cpu(hdr->seq_ctl);
- seq = WLAN_GET_SEQ_SEQ(sc);
-
- entry = ieee80211_frag_cache_find(ieee, seq, -1, hdr->addr2,
- hdr->addr1);
-
- if (entry == NULL) {
- IEEE80211_DEBUG_FRAG("could not invalidate fragment cache "
- "entry (seq=%u)\n", seq);
- return -1;
- }
-
- entry->skb = NULL;
- return 0;
-}
-
-#ifdef NOT_YET
-/* ieee80211_rx_frame_mgtmt
- *
- * Responsible for handling management control frames
- *
- * Called by ieee80211_rx */
-static int
-ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb,
- struct ieee80211_rx_stats *rx_stats, u16 type,
- u16 stype)
-{
- if (ieee->iw_mode == IW_MODE_MASTER) {
- printk(KERN_DEBUG "%s: Master mode not yet suppported.\n",
- ieee->dev->name);
- return 0;
-/*
- hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr_4addr *)
- skb->data);*/
- }
-
- if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) {
- if (stype == WLAN_FC_STYPE_BEACON &&
- ieee->iw_mode == IW_MODE_MASTER) {
- struct sk_buff *skb2;
- /* Process beacon frames also in kernel driver to
- * update STA(AP) table statistics */
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2)
- hostap_rx(skb2->dev, skb2, rx_stats);
- }
-
- /* send management frames to the user space daemon for
- * processing */
- ieee->apdevstats.rx_packets++;
- ieee->apdevstats.rx_bytes += skb->len;
- prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT);
- return 0;
- }
-
- if (ieee->iw_mode == IW_MODE_MASTER) {
- if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) {
- printk(KERN_DEBUG "%s: unknown management frame "
- "(type=0x%02x, stype=0x%02x) dropped\n",
- skb->dev->name, type, stype);
- return -1;
- }
-
- hostap_rx(skb->dev, skb, rx_stats);
- return 0;
- }
-
- printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame "
- "received in non-Host AP mode\n", skb->dev->name);
- return -1;
-}
-#endif
-
-/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
-/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
-static unsigned char rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
-static unsigned char bridge_tunnel_header[] =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-/* No encapsulation header if EtherType < 0x600 (=length) */
-
-/* Called by ieee80211_rx_frame_decrypt */
-static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee,
- struct sk_buff *skb)
-{
- struct net_device *dev = ieee->dev;
- u16 fc, ethertype;
- struct ieee80211_hdr_3addr *hdr;
- u8 *pos;
-
- if (skb->len < 24)
- return 0;
-
- hdr = (struct ieee80211_hdr_3addr *)skb->data;
- fc = le16_to_cpu(hdr->frame_ctl);
-
- /* check that the frame is unicast frame to us */
- if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- IEEE80211_FCTL_TODS &&
- !compare_ether_addr(hdr->addr1, dev->dev_addr) &&
- !compare_ether_addr(hdr->addr3, dev->dev_addr)) {
- /* ToDS frame with own addr BSSID and DA */
- } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- IEEE80211_FCTL_FROMDS &&
- !compare_ether_addr(hdr->addr1, dev->dev_addr)) {
- /* FromDS frame with own addr as DA */
- } else
- return 0;
-
- if (skb->len < 24 + 8)
- return 0;
-
- /* check for port access entity Ethernet type */
- pos = skb->data + 24;
- ethertype = (pos[6] << 8) | pos[7];
- if (ethertype == ETH_P_PAE)
- return 1;
-
- return 0;
-}
-
-/* Called only as a tasklet (software IRQ), by ieee80211_rx */
-static int
-ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
- struct ieee80211_crypt_data *crypt)
-{
- struct ieee80211_hdr_3addr *hdr;
- int res, hdrlen;
-
- if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
- return 0;
-
- hdr = (struct ieee80211_hdr_3addr *)skb->data;
- hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
-
- atomic_inc(&crypt->refcnt);
- res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
- atomic_dec(&crypt->refcnt);
- if (res < 0) {
- IEEE80211_DEBUG_DROP("decryption failed (SA=" MAC_FMT
- ") res=%d\n",
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5],
- res);
- if (res == -2)
- IEEE80211_DEBUG_DROP("Decryption failed ICV "
- "mismatch (key %d)\n",
- skb->data[hdrlen + 3] >> 6);
- ieee->ieee_stats.rx_discards_undecryptable++;
- return -1;
- }
-
- return res;
-}
-
-/* Called only as a tasklet (software IRQ), by ieee80211_rx */
-static int
-ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee,
- struct sk_buff *skb, int keyidx,
- struct ieee80211_crypt_data *crypt)
-{
- struct ieee80211_hdr_3addr *hdr;
- int res, hdrlen;
-
- if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
- return 0;
-
- hdr = (struct ieee80211_hdr_3addr *)skb->data;
- hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
-
- atomic_inc(&crypt->refcnt);
- res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
- atomic_dec(&crypt->refcnt);
- if (res < 0) {
- printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
- " (SA=" MAC_FMT " keyidx=%d)\n",
- ieee->dev->name,
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5],
- keyidx);
- return -1;
- }
-
- return 0;
-}
-
-/* All received frames are sent to this function. @skb contains the frame in
- * IEEE 802.11 format, i.e., in the format it was sent over air.
- * This function is called only as a tasklet (software IRQ). */
-int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
- struct ieee80211_rx_stats *rx_stats)
-{
- struct net_device *dev = ieee->dev;
- struct ieee80211_hdr_4addr *hdr;
- size_t hdrlen;
- u16 fc, type, stype, sc;
- struct net_device_stats *stats;
- unsigned int frag;
- u8 *payload;
- u16 ethertype;
-#ifdef NOT_YET
- struct net_device *wds = NULL;
- struct sk_buff *skb2 = NULL;
- struct net_device *wds = NULL;
- int frame_authorized = 0;
- int from_assoc_ap = 0;
- void *sta = NULL;
-#endif
- u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN];
- struct ieee80211_crypt_data *crypt = NULL;
- int keyidx = 0;
- int can_be_decrypted = 0;
- DECLARE_MAC_BUF(mac);
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- stats = &ieee->stats;
-
- if (skb->len < 10) {
- printk(KERN_INFO "%s: SKB length < 10\n", dev->name);
- goto rx_dropped;
- }
-
- fc = le16_to_cpu(hdr->frame_ctl);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
- sc = le16_to_cpu(hdr->seq_ctl);
- frag = WLAN_GET_SEQ_FRAG(sc);
- hdrlen = ieee80211_get_hdrlen(fc);
-
- if (skb->len < hdrlen) {
- printk(KERN_INFO "%s: invalid SKB length %d\n",
- dev->name, skb->len);
- goto rx_dropped;
- }
-
- /* Put this code here so that we avoid duplicating it in all
- * Rx paths. - Jean II */
-#ifdef CONFIG_WIRELESS_EXT
-#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
- /* If spy monitoring on */
- if (ieee->spy_data.spy_number > 0) {
- struct iw_quality wstats;
-
- wstats.updated = 0;
- if (rx_stats->mask & IEEE80211_STATMASK_RSSI) {
- wstats.level = rx_stats->signal;
- wstats.updated |= IW_QUAL_LEVEL_UPDATED;
- } else
- wstats.updated |= IW_QUAL_LEVEL_INVALID;
-
- if (rx_stats->mask & IEEE80211_STATMASK_NOISE) {
- wstats.noise = rx_stats->noise;
- wstats.updated |= IW_QUAL_NOISE_UPDATED;
- } else
- wstats.updated |= IW_QUAL_NOISE_INVALID;
-
- if (rx_stats->mask & IEEE80211_STATMASK_SIGNAL) {
- wstats.qual = rx_stats->signal;
- wstats.updated |= IW_QUAL_QUAL_UPDATED;
- } else
- wstats.updated |= IW_QUAL_QUAL_INVALID;
-
- /* Update spy records */
- wireless_spy_update(ieee->dev, hdr->addr2, &wstats);
- }
-#endif /* IW_WIRELESS_SPY */
-#endif /* CONFIG_WIRELESS_EXT */
-
-#ifdef NOT_YET
- hostap_update_rx_stats(local->ap, hdr, rx_stats);
-#endif
-
- if (ieee->iw_mode == IW_MODE_MONITOR) {
- stats->rx_packets++;
- stats->rx_bytes += skb->len;
- ieee80211_monitor_rx(ieee, skb, rx_stats);
- return 1;
- }
-
- can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) ||
- is_broadcast_ether_addr(hdr->addr2)) ?
- ieee->host_mc_decrypt : ieee->host_decrypt;
-
- if (can_be_decrypted) {
- if (skb->len >= hdrlen + 3) {
- /* Top two-bits of byte 3 are the key index */
- keyidx = skb->data[hdrlen + 3] >> 6;
- }
-
- /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx
- * is only allowed 2-bits of storage, no value of keyidx can
- * be provided via above code that would result in keyidx
- * being out of range */
- crypt = ieee->crypt[keyidx];
-
-#ifdef NOT_YET
- sta = NULL;
-
- /* Use station specific key to override default keys if the
- * receiver address is a unicast address ("individual RA"). If
- * bcrx_sta_key parameter is set, station specific key is used
- * even with broad/multicast targets (this is against IEEE
- * 802.11, but makes it easier to use different keys with
- * stations that do not support WEP key mapping). */
-
- if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
- (void)hostap_handle_sta_crypto(local, hdr, &crypt,
- &sta);
-#endif
-
- /* allow NULL decrypt to indicate an station specific override
- * for default encryption */
- if (crypt && (crypt->ops == NULL ||
- crypt->ops->decrypt_mpdu == NULL))
- crypt = NULL;
-
- if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) {
- /* This seems to be triggered by some (multicast?)
- * frames from other than current BSS, so just drop the
- * frames silently instead of filling system log with
- * these reports. */
- IEEE80211_DEBUG_DROP("Decryption failed (not set)"
- " (SA=" MAC_FMT ")\n",
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5]);
- ieee->ieee_stats.rx_discards_undecryptable++;
- goto rx_dropped;
- }
- }
-#ifdef NOT_YET
- if (type != WLAN_FC_TYPE_DATA) {
- if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH &&
- fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt &&
- (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) {
- printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth "
- "from " MAC_FMT "\n", dev->name,
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5]);
- /* TODO: could inform hostapd about this so that it
- * could send auth failure report */
- goto rx_dropped;
- }
-
- if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
- goto rx_dropped;
- else
- goto rx_exit;
- }
-#endif
- /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */
- if (sc == ieee->prev_seq_ctl)
- goto rx_dropped;
- else
- ieee->prev_seq_ctl = sc;
-
- /* Data frame - extract src/dst addresses */
- if (skb->len < IEEE80211_3ADDR_LEN)
- goto rx_dropped;
-
- switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
- case IEEE80211_FCTL_FROMDS:
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr3, ETH_ALEN);
- break;
- case IEEE80211_FCTL_TODS:
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
- break;
- case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
- if (skb->len < IEEE80211_4ADDR_LEN)
- goto rx_dropped;
- memcpy(dst, hdr->addr3, ETH_ALEN);
- memcpy(src, hdr->addr4, ETH_ALEN);
- break;
- case 0:
- memcpy(dst, hdr->addr1, ETH_ALEN);
- memcpy(src, hdr->addr2, ETH_ALEN);
- break;
- }
-
-#ifdef NOT_YET
- if (hostap_rx_frame_wds(ieee, hdr, fc, &wds))
- goto rx_dropped;
- if (wds) {
- skb->dev = dev = wds;
- stats = hostap_get_stats(dev);
- }
-
- if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
- (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- IEEE80211_FCTL_FROMDS && ieee->stadev
- && !compare_ether_addr(hdr->addr2, ieee->assoc_ap_addr)) {
- /* Frame from BSSID of the AP for which we are a client */
- skb->dev = dev = ieee->stadev;
- stats = hostap_get_stats(dev);
- from_assoc_ap = 1;
- }
-#endif
-
- dev->last_rx = jiffies;
-
-#ifdef NOT_YET
- if ((ieee->iw_mode == IW_MODE_MASTER ||
- ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) {
- switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats,
- wds != NULL)) {
- case AP_RX_CONTINUE_NOT_AUTHORIZED:
- frame_authorized = 0;
- break;
- case AP_RX_CONTINUE:
- frame_authorized = 1;
- break;
- case AP_RX_DROP:
- goto rx_dropped;
- case AP_RX_EXIT:
- goto rx_exit;
- }
- }
-#endif
-
- /* Nullfunc frames may have PS-bit set, so they must be passed to
- * hostap_handle_sta_rx() before being dropped here. */
-
- stype &= ~IEEE80211_STYPE_QOS_DATA;
-
- if (stype != IEEE80211_STYPE_DATA &&
- stype != IEEE80211_STYPE_DATA_CFACK &&
- stype != IEEE80211_STYPE_DATA_CFPOLL &&
- stype != IEEE80211_STYPE_DATA_CFACKPOLL) {
- if (stype != IEEE80211_STYPE_NULLFUNC)
- IEEE80211_DEBUG_DROP("RX: dropped data frame "
- "with no data (type=0x%02x, "
- "subtype=0x%02x, len=%d)\n",
- type, stype, skb->len);
- goto rx_dropped;
- }
-
- /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
-
- if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
- (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0)
- goto rx_dropped;
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
-
- /* skb: hdr + (possibly fragmented) plaintext payload */
- // PR: FIXME: hostap has additional conditions in the "if" below:
- // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) &&
- if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) {
- int flen;
- struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr);
- IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
-
- if (!frag_skb) {
- IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG,
- "Rx cannot get skb from fragment "
- "cache (morefrag=%d seq=%u frag=%u)\n",
- (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
- WLAN_GET_SEQ_SEQ(sc), frag);
- goto rx_dropped;
- }
-
- flen = skb->len;
- if (frag != 0)
- flen -= hdrlen;
-
- if (frag_skb->tail + flen > frag_skb->end) {
- printk(KERN_WARNING "%s: host decrypted and "
- "reassembled frame did not fit skb\n",
- dev->name);
- ieee80211_frag_cache_invalidate(ieee, hdr);
- goto rx_dropped;
- }
-
- if (frag == 0) {
- /* copy first fragment (including full headers) into
- * beginning of the fragment cache skb */
- skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen);
- } else {
- /* append frame payload to the end of the fragment
- * cache skb */
- skb_copy_from_linear_data_offset(skb, hdrlen,
- skb_put(frag_skb, flen), flen);
- }
- dev_kfree_skb_any(skb);
- skb = NULL;
-
- if (fc & IEEE80211_FCTL_MOREFRAGS) {
- /* more fragments expected - leave the skb in fragment
- * cache for now; it will be delivered to upper layers
- * after all fragments have been received */
- goto rx_exit;
- }
-
- /* this was the last fragment and the frame will be
- * delivered, so remove skb from fragment cache */
- skb = frag_skb;
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- ieee80211_frag_cache_invalidate(ieee, hdr);
- }
-
- /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
- * encrypted/authenticated */
- if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted &&
- ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
- goto rx_dropped;
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) {
- if ( /*ieee->ieee802_1x && */
- ieee80211_is_eapol_frame(ieee, skb)) {
- /* pass unencrypted EAPOL frames even if encryption is
- * configured */
- } else {
- IEEE80211_DEBUG_DROP("encryption configured, but RX "
- "frame not encrypted (SA="
- MAC_FMT ")\n",
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5]);
- goto rx_dropped;
- }
- }
-
- if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep &&
- !ieee80211_is_eapol_frame(ieee, skb)) {
- IEEE80211_DEBUG_DROP("dropped unencrypted RX data "
- "frame from " MAC_FMT
- " (drop_unencrypted=1)\n",
- hdr->addr2[0], hdr->addr2[1],
- hdr->addr2[2], hdr->addr2[3],
- hdr->addr2[4], hdr->addr2[5]);
- goto rx_dropped;
- }
-
- /* If the frame was decrypted in hardware, we may need to strip off
- * any security data (IV, ICV, etc) that was left behind */
- if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) &&
- ieee->host_strip_iv_icv) {
- int trimlen = 0;
-
- /* Top two-bits of byte 3 are the key index */
- if (skb->len >= hdrlen + 3)
- keyidx = skb->data[hdrlen + 3] >> 6;
-
- /* To strip off any security data which appears before the
- * payload, we simply increase hdrlen (as the header gets
- * chopped off immediately below). For the security data which
- * appears after the payload, we use skb_trim. */
-
- switch (ieee->sec.encode_alg[keyidx]) {
- case SEC_ALG_WEP:
- /* 4 byte IV */
- hdrlen += 4;
- /* 4 byte ICV */
- trimlen = 4;
- break;
- case SEC_ALG_TKIP:
- /* 4 byte IV, 4 byte ExtIV */
- hdrlen += 8;
- /* 8 byte MIC, 4 byte ICV */
- trimlen = 12;
- break;
- case SEC_ALG_CCMP:
- /* 8 byte CCMP header */
- hdrlen += 8;
- /* 8 byte MIC */
- trimlen = 8;
- break;
- }
-
- if (skb->len < trimlen)
- goto rx_dropped;
-
- __skb_trim(skb, skb->len - trimlen);
-
- if (skb->len < hdrlen)
- goto rx_dropped;
- }
-
- /* skb: hdr + (possible reassembled) full plaintext payload */
-
- payload = skb->data + hdrlen;
- ethertype = (payload[6] << 8) | payload[7];
-
-#ifdef NOT_YET
- /* If IEEE 802.1X is used, check whether the port is authorized to send
- * the received frame. */
- if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) {
- if (ethertype == ETH_P_PAE) {
- printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n",
- dev->name);
- if (ieee->hostapd && ieee->apdev) {
- /* Send IEEE 802.1X frames to the user
- * space daemon for processing */
- prism2_rx_80211(ieee->apdev, skb, rx_stats,
- PRISM2_RX_MGMT);
- ieee->apdevstats.rx_packets++;
- ieee->apdevstats.rx_bytes += skb->len;
- goto rx_exit;
- }
- } else if (!frame_authorized) {
- printk(KERN_DEBUG "%s: dropped frame from "
- "unauthorized port (IEEE 802.1X): "
- "ethertype=0x%04x\n", dev->name, ethertype);
- goto rx_dropped;
- }
- }
-#endif
-
- /* convert hdr + possible LLC headers into Ethernet header */
- if (skb->len - hdrlen >= 8 &&
- ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 &&
- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
- memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation and
- * replace EtherType */
- skb_pull(skb, hdrlen + SNAP_SIZE);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- } else {
- __be16 len;
- /* Leave Ethernet header part of hdr and full payload */
- skb_pull(skb, hdrlen);
- len = htons(skb->len);
- memcpy(skb_push(skb, 2), &len, 2);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- }
-
-#ifdef NOT_YET
- if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
- IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) {
- /* Non-standard frame: get addr4 from its bogus location after
- * the payload */
- skb_copy_to_linear_data_offset(skb, ETH_ALEN,
- skb->data + skb->len - ETH_ALEN,
- ETH_ALEN);
- skb_trim(skb, skb->len - ETH_ALEN);
- }
-#endif
-
- stats->rx_packets++;
- stats->rx_bytes += skb->len;
-
-#ifdef NOT_YET
- if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) {
- if (dst[0] & 0x01) {
- /* copy multicast frame both to the higher layers and
- * to the wireless media */
- ieee->ap->bridged_multicast++;
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2 == NULL)
- printk(KERN_DEBUG "%s: skb_clone failed for "
- "multicast frame\n", dev->name);
- } else if (hostap_is_sta_assoc(ieee->ap, dst)) {
- /* send frame directly to the associated STA using
- * wireless media and not passing to higher layers */
- ieee->ap->bridged_unicast++;
- skb2 = skb;
- skb = NULL;
- }
- }
-
- if (skb2 != NULL) {
- /* send to wireless media */
- skb2->dev = dev;
- skb2->protocol = htons(ETH_P_802_3);
- skb_reset_mac_header(skb2);
- skb_reset_network_header(skb2);
- /* skb2->network_header += ETH_HLEN; */
- dev_queue_xmit(skb2);
- }
-#endif
-
- if (skb) {
- skb->protocol = eth_type_trans(skb, dev);
- memset(skb->cb, 0, sizeof(skb->cb));
- skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
- if (netif_rx(skb) == NET_RX_DROP) {
- /* netif_rx always succeeds, but it might drop
- * the packet. If it drops the packet, we log that
- * in our stats. */
- IEEE80211_DEBUG_DROP
- ("RX: netif_rx dropped the packet\n");
- stats->rx_dropped++;
- }
- }
-
- rx_exit:
-#ifdef NOT_YET
- if (sta)
- hostap_handle_sta_release(sta);
-#endif
- return 1;
-
- rx_dropped:
- stats->rx_dropped++;
-
- /* Returning 0 indicates to caller that we have not handled the SKB--
- * so it is still allocated and can be used again by underlying
- * hardware as a DMA target */
- return 0;
-}
-
-/* Filter out unrelated packets, call ieee80211_rx[_mgt]
- * This function takes over the skb, it should not be used again after calling
- * this function. */
-void ieee80211_rx_any(struct ieee80211_device *ieee,
- struct sk_buff *skb, struct ieee80211_rx_stats *stats)
-{
- struct ieee80211_hdr_4addr *hdr;
- int is_packet_for_us;
- u16 fc;
-
- if (ieee->iw_mode == IW_MODE_MONITOR) {
- if (!ieee80211_rx(ieee, skb, stats))
- dev_kfree_skb_irq(skb);
- return;
- }
-
- if (skb->len < sizeof(struct ieee80211_hdr))
- goto drop_free;
-
- hdr = (struct ieee80211_hdr_4addr *)skb->data;
- fc = le16_to_cpu(hdr->frame_ctl);
-
- if ((fc & IEEE80211_FCTL_VERS) != 0)
- goto drop_free;
-
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_MGMT:
- if (skb->len < sizeof(struct ieee80211_hdr_3addr))
- goto drop_free;
- ieee80211_rx_mgt(ieee, hdr, stats);
- dev_kfree_skb_irq(skb);
- return;
- case IEEE80211_FTYPE_DATA:
- break;
- case IEEE80211_FTYPE_CTL:
- return;
- default:
- return;
- }
-
- is_packet_for_us = 0;
- switch (ieee->iw_mode) {
- case IW_MODE_ADHOC:
- /* our BSS and not from/to DS */
- if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0)
- if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
- /* promisc: get all */
- if (ieee->dev->flags & IFF_PROMISC)
- is_packet_for_us = 1;
- /* to us */
- else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
- is_packet_for_us = 1;
- /* mcast */
- else if (is_multicast_ether_addr(hdr->addr1))
- is_packet_for_us = 1;
- }
- break;
- case IW_MODE_INFRA:
- /* our BSS (== from our AP) and from DS */
- if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0)
- if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
- /* promisc: get all */
- if (ieee->dev->flags & IFF_PROMISC)
- is_packet_for_us = 1;
- /* to us */
- else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
- is_packet_for_us = 1;
- /* mcast */
- else if (is_multicast_ether_addr(hdr->addr1)) {
- /* not our own packet bcasted from AP */
- if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN))
- is_packet_for_us = 1;
- }
- }
- break;
- default:
- /* ? */
- break;
- }
-
- if (is_packet_for_us)
- if (!ieee80211_rx(ieee, skb, stats))
- dev_kfree_skb_irq(skb);
- return;
-
-drop_free:
- dev_kfree_skb_irq(skb);
- ieee->stats.rx_dropped++;
- return;
-}
-
-#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
-
-static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
-
-/*
-* Make ther structure we read from the beacon packet has
-* the right values
-*/
-static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element
- *info_element, int sub_type)
-{
-
- if (info_element->qui_subtype != sub_type)
- return -1;
- if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN))
- return -1;
- if (info_element->qui_type != QOS_OUI_TYPE)
- return -1;
- if (info_element->version != QOS_VERSION_1)
- return -1;
-
- return 0;
-}
-
-/*
- * Parse a QoS parameter element
- */
-static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info
- *element_param, struct ieee80211_info_element
- *info_element)
-{
- int ret = 0;
- u16 size = sizeof(struct ieee80211_qos_parameter_info) - 2;
-
- if ((info_element == NULL) || (element_param == NULL))
- return -1;
-
- if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) {
- memcpy(element_param->info_element.qui, info_element->data,
- info_element->len);
- element_param->info_element.elementID = info_element->id;
- element_param->info_element.length = info_element->len;
- } else
- ret = -1;
- if (ret == 0)
- ret = ieee80211_verify_qos_info(&element_param->info_element,
- QOS_OUI_PARAM_SUB_TYPE);
- return ret;
-}
-
-/*
- * Parse a QoS information element
- */
-static int ieee80211_read_qos_info_element(struct
- ieee80211_qos_information_element
- *element_info, struct ieee80211_info_element
- *info_element)
-{
- int ret = 0;
- u16 size = sizeof(struct ieee80211_qos_information_element) - 2;
-
- if (element_info == NULL)
- return -1;
- if (info_element == NULL)
- return -1;
-
- if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) {
- memcpy(element_info->qui, info_element->data,
- info_element->len);
- element_info->elementID = info_element->id;
- element_info->length = info_element->len;
- } else
- ret = -1;
-
- if (ret == 0)
- ret = ieee80211_verify_qos_info(element_info,
- QOS_OUI_INFO_SUB_TYPE);
- return ret;
-}
-
-/*
- * Write QoS parameters from the ac parameters.
- */
-static int ieee80211_qos_convert_ac_to_parameters(struct
- ieee80211_qos_parameter_info
- *param_elm, struct
- ieee80211_qos_parameters
- *qos_param)
-{
- int rc = 0;
- int i;
- struct ieee80211_qos_ac_parameter *ac_params;
- u32 txop;
- u8 cw_min;
- u8 cw_max;
-
- for (i = 0; i < QOS_QUEUE_NUM; i++) {
- ac_params = &(param_elm->ac_params_record[i]);
-
- qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F;
- qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2;
-
- cw_min = ac_params->ecw_min_max & 0x0F;
- qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1);
-
- cw_max = (ac_params->ecw_min_max & 0xF0) >> 4;
- qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1);
-
- qos_param->flag[i] =
- (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00;
-
- txop = le16_to_cpu(ac_params->tx_op_limit) * 32;
- qos_param->tx_op_limit[i] = cpu_to_le16(txop);
- }
- return rc;
-}
-
-/*
- * we have a generic data element which it may contain QoS information or
- * parameters element. check the information element length to decide
- * which type to read
- */
-static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element
- *info_element,
- struct ieee80211_network *network)
-{
- int rc = 0;
- struct ieee80211_qos_parameters *qos_param = NULL;
- struct ieee80211_qos_information_element qos_info_element;
-
- rc = ieee80211_read_qos_info_element(&qos_info_element, info_element);
-
- if (rc == 0) {
- network->qos_data.param_count = qos_info_element.ac_info & 0x0F;
- network->flags |= NETWORK_HAS_QOS_INFORMATION;
- } else {
- struct ieee80211_qos_parameter_info param_element;
-
- rc = ieee80211_read_qos_param_element(&param_element,
- info_element);
- if (rc == 0) {
- qos_param = &(network->qos_data.parameters);
- ieee80211_qos_convert_ac_to_parameters(&param_element,
- qos_param);
- network->flags |= NETWORK_HAS_QOS_PARAMETERS;
- network->qos_data.param_count =
- param_element.info_element.ac_info & 0x0F;
- }
- }
-
- if (rc == 0) {
- IEEE80211_DEBUG_QOS("QoS is supported\n");
- network->qos_data.supported = 1;
- }
- return rc;
-}
-
-#ifdef CONFIG_IEEE80211_DEBUG
-#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
-
-static const char *get_info_element_string(u16 id)
-{
- switch (id) {
- MFIE_STRING(SSID);
- MFIE_STRING(RATES);
- MFIE_STRING(FH_SET);
- MFIE_STRING(DS_SET);
- MFIE_STRING(CF_SET);
- MFIE_STRING(TIM);
- MFIE_STRING(IBSS_SET);
- MFIE_STRING(COUNTRY);
- MFIE_STRING(HOP_PARAMS);
- MFIE_STRING(HOP_TABLE);
- MFIE_STRING(REQUEST);
- MFIE_STRING(CHALLENGE);
- MFIE_STRING(POWER_CONSTRAINT);
- MFIE_STRING(POWER_CAPABILITY);
- MFIE_STRING(TPC_REQUEST);
- MFIE_STRING(TPC_REPORT);
- MFIE_STRING(SUPP_CHANNELS);
- MFIE_STRING(CSA);
- MFIE_STRING(MEASURE_REQUEST);
- MFIE_STRING(MEASURE_REPORT);
- MFIE_STRING(QUIET);
- MFIE_STRING(IBSS_DFS);
- MFIE_STRING(ERP_INFO);
- MFIE_STRING(RSN);
- MFIE_STRING(RATES_EX);
- MFIE_STRING(GENERIC);
- MFIE_STRING(QOS_PARAMETER);
- default:
- return "UNKNOWN";
- }
-}
-#endif
-
-static int ieee80211_parse_info_param(struct ieee80211_info_element
- *info_element, u16 length,
- struct ieee80211_network *network)
-{
- u8 i;
-#ifdef CONFIG_IEEE80211_DEBUG
- char rates_str[64];
- char *p;
-#endif
-
- while (length >= sizeof(*info_element)) {
- if (sizeof(*info_element) + info_element->len > length) {
- IEEE80211_DEBUG_MGMT("Info elem: parse failed: "
- "info_element->len + 2 > left : "
- "info_element->len+2=%zd left=%d, id=%d.\n",
- info_element->len +
- sizeof(*info_element),
- length, info_element->id);
- /* We stop processing but don't return an error here
- * because some misbehaviour APs break this rule. ie.
- * Orinoco AP1000. */
- break;
- }
-
- switch (info_element->id) {
- case MFIE_TYPE_SSID:
- if (ieee80211_is_empty_essid(info_element->data,
- info_element->len)) {
- network->flags |= NETWORK_EMPTY_ESSID;
- break;
- }
-
- network->ssid_len = min(info_element->len,
- (u8) IW_ESSID_MAX_SIZE);
- memcpy(network->ssid, info_element->data,
- network->ssid_len);
- if (network->ssid_len < IW_ESSID_MAX_SIZE)
- memset(network->ssid + network->ssid_len, 0,
- IW_ESSID_MAX_SIZE - network->ssid_len);
-
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_SSID: '%s' len=%d.\n",
- network->ssid, network->ssid_len);
- break;
-
- case MFIE_TYPE_RATES:
-#ifdef CONFIG_IEEE80211_DEBUG
- p = rates_str;
-#endif
- network->rates_len = min(info_element->len,
- MAX_RATES_LENGTH);
- for (i = 0; i < network->rates_len; i++) {
- network->rates[i] = info_element->data[i];
-#ifdef CONFIG_IEEE80211_DEBUG
- p += snprintf(p, sizeof(rates_str) -
- (p - rates_str), "%02X ",
- network->rates[i]);
-#endif
- if (ieee80211_is_ofdm_rate
- (info_element->data[i])) {
- network->flags |= NETWORK_HAS_OFDM;
- if (info_element->data[i] &
- IEEE80211_BASIC_RATE_MASK)
- network->flags &=
- ~NETWORK_HAS_CCK;
- }
- }
-
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES: '%s' (%d)\n",
- rates_str, network->rates_len);
- break;
-
- case MFIE_TYPE_RATES_EX:
-#ifdef CONFIG_IEEE80211_DEBUG
- p = rates_str;
-#endif
- network->rates_ex_len = min(info_element->len,
- MAX_RATES_EX_LENGTH);
- for (i = 0; i < network->rates_ex_len; i++) {
- network->rates_ex[i] = info_element->data[i];
-#ifdef CONFIG_IEEE80211_DEBUG
- p += snprintf(p, sizeof(rates_str) -
- (p - rates_str), "%02X ",
- network->rates[i]);
-#endif
- if (ieee80211_is_ofdm_rate
- (info_element->data[i])) {
- network->flags |= NETWORK_HAS_OFDM;
- if (info_element->data[i] &
- IEEE80211_BASIC_RATE_MASK)
- network->flags &=
- ~NETWORK_HAS_CCK;
- }
- }
-
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
- rates_str, network->rates_ex_len);
- break;
-
- case MFIE_TYPE_DS_SET:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_DS_SET: %d\n",
- info_element->data[0]);
- network->channel = info_element->data[0];
- break;
-
- case MFIE_TYPE_FH_SET:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_FH_SET: ignored\n");
- break;
-
- case MFIE_TYPE_CF_SET:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_CF_SET: ignored\n");
- break;
-
- case MFIE_TYPE_TIM:
- network->tim.tim_count = info_element->data[0];
- network->tim.tim_period = info_element->data[1];
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: partially ignored\n");
- break;
-
- case MFIE_TYPE_ERP_INFO:
- network->erp_value = info_element->data[0];
- network->flags |= NETWORK_HAS_ERP_VALUE;
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
- network->erp_value);
- break;
-
- case MFIE_TYPE_IBSS_SET:
- network->atim_window = info_element->data[0];
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_IBSS_SET: %d\n",
- network->atim_window);
- break;
-
- case MFIE_TYPE_CHALLENGE:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_CHALLENGE: ignored\n");
- break;
-
- case MFIE_TYPE_GENERIC:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_GENERIC: %d bytes\n",
- info_element->len);
- if (!ieee80211_parse_qos_info_param_IE(info_element,
- network))
- break;
-
- if (info_element->len >= 4 &&
- info_element->data[0] == 0x00 &&
- info_element->data[1] == 0x50 &&
- info_element->data[2] == 0xf2 &&
- info_element->data[3] == 0x01) {
- network->wpa_ie_len = min(info_element->len + 2,
- MAX_WPA_IE_LEN);
- memcpy(network->wpa_ie, info_element,
- network->wpa_ie_len);
- }
- break;
-
- case MFIE_TYPE_RSN:
- IEEE80211_DEBUG_MGMT("MFIE_TYPE_RSN: %d bytes\n",
- info_element->len);
- network->rsn_ie_len = min(info_element->len + 2,
- MAX_WPA_IE_LEN);
- memcpy(network->rsn_ie, info_element,
- network->rsn_ie_len);
- break;
-
- case MFIE_TYPE_QOS_PARAMETER:
- printk(KERN_ERR
- "QoS Error need to parse QOS_PARAMETER IE\n");
- break;
- /* 802.11h */
- case MFIE_TYPE_POWER_CONSTRAINT:
- network->power_constraint = info_element->data[0];
- network->flags |= NETWORK_HAS_POWER_CONSTRAINT;
- break;
-
- case MFIE_TYPE_CSA:
- network->power_constraint = info_element->data[0];
- network->flags |= NETWORK_HAS_CSA;
- break;
-
- case MFIE_TYPE_QUIET:
- network->quiet.count = info_element->data[0];
- network->quiet.period = info_element->data[1];
- network->quiet.duration = info_element->data[2];
- network->quiet.offset = info_element->data[3];
- network->flags |= NETWORK_HAS_QUIET;
- break;
-
- case MFIE_TYPE_IBSS_DFS:
- if (network->ibss_dfs)
- break;
- network->ibss_dfs = kmemdup(info_element->data,
- info_element->len,
- GFP_ATOMIC);
- if (!network->ibss_dfs)
- return 1;
- network->flags |= NETWORK_HAS_IBSS_DFS;
- break;
-
- case MFIE_TYPE_TPC_REPORT:
- network->tpc_report.transmit_power =
- info_element->data[0];
- network->tpc_report.link_margin = info_element->data[1];
- network->flags |= NETWORK_HAS_TPC_REPORT;
- break;
-
- default:
- IEEE80211_DEBUG_MGMT
- ("Unsupported info element: %s (%d)\n",
- get_info_element_string(info_element->id),
- info_element->id);
- break;
- }
-
- length -= sizeof(*info_element) + info_element->len;
- info_element =
- (struct ieee80211_info_element *)&info_element->
- data[info_element->len];
- }
-
- return 0;
-}
-
-static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response
- *frame, struct ieee80211_rx_stats *stats)
-{
- struct ieee80211_network network_resp = {
- .ibss_dfs = NULL,
- };
- struct ieee80211_network *network = &network_resp;
- struct net_device *dev = ieee->dev;
-
- network->flags = 0;
- network->qos_data.active = 0;
- network->qos_data.supported = 0;
- network->qos_data.param_count = 0;
- network->qos_data.old_param_count = 0;
-
- //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF);
- network->atim_window = le16_to_cpu(frame->aid);
- network->listen_interval = le16_to_cpu(frame->status);
- memcpy(network->bssid, frame->header.addr3, ETH_ALEN);
- network->capability = le16_to_cpu(frame->capability);
- network->last_scanned = jiffies;
- network->rates_len = network->rates_ex_len = 0;
- network->last_associate = 0;
- network->ssid_len = 0;
- network->erp_value =
- (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0;
-
- if (stats->freq == IEEE80211_52GHZ_BAND) {
- /* for A band (No DS info) */
- network->channel = stats->received_channel;
- } else
- network->flags |= NETWORK_HAS_CCK;
-
- network->wpa_ie_len = 0;
- network->rsn_ie_len = 0;
-
- if (ieee80211_parse_info_param
- (frame->info_element, stats->len - sizeof(*frame), network))
- return 1;
-
- network->mode = 0;
- if (stats->freq == IEEE80211_52GHZ_BAND)
- network->mode = IEEE_A;
- else {
- if (network->flags & NETWORK_HAS_OFDM)
- network->mode |= IEEE_G;
- if (network->flags & NETWORK_HAS_CCK)
- network->mode |= IEEE_B;
- }
-
- if (ieee80211_is_empty_essid(network->ssid, network->ssid_len))
- network->flags |= NETWORK_EMPTY_ESSID;
-
- memcpy(&network->stats, stats, sizeof(network->stats));
-
- if (ieee->handle_assoc_response != NULL)
- ieee->handle_assoc_response(dev, frame, network);
-
- return 0;
-}
-
-/***************************************************/
-
-static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response
- *beacon,
- struct ieee80211_network *network,
- struct ieee80211_rx_stats *stats)
-{
- DECLARE_MAC_BUF(mac);
-
- network->qos_data.active = 0;
- network->qos_data.supported = 0;
- network->qos_data.param_count = 0;
- network->qos_data.old_param_count = 0;
-
- /* Pull out fixed field data */
- memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
- network->capability = le16_to_cpu(beacon->capability);
- network->last_scanned = jiffies;
- network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]);
- network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]);
- network->beacon_interval = le16_to_cpu(beacon->beacon_interval);
- /* Where to pull this? beacon->listen_interval; */
- network->listen_interval = 0x0A;
- network->rates_len = network->rates_ex_len = 0;
- network->last_associate = 0;
- network->ssid_len = 0;
- network->flags = 0;
- network->atim_window = 0;
- network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ?
- 0x3 : 0x0;
-
- if (stats->freq == IEEE80211_52GHZ_BAND) {
- /* for A band (No DS info) */
- network->channel = stats->received_channel;
- } else
- network->flags |= NETWORK_HAS_CCK;
-
- network->wpa_ie_len = 0;
- network->rsn_ie_len = 0;
-
- if (ieee80211_parse_info_param
- (beacon->info_element, stats->len - sizeof(*beacon), network))
- return 1;
-
- network->mode = 0;
- if (stats->freq == IEEE80211_52GHZ_BAND)
- network->mode = IEEE_A;
- else {
- if (network->flags & NETWORK_HAS_OFDM)
- network->mode |= IEEE_G;
- if (network->flags & NETWORK_HAS_CCK)
- network->mode |= IEEE_B;
- }
-
- if (network->mode == 0) {
- IEEE80211_DEBUG_SCAN("Filtered out '%s (%s)' "
- "network.\n",
- escape_essid(network->ssid,
- network->ssid_len),
- print_mac(mac, network->bssid));
- return 1;
- }
-
- if (ieee80211_is_empty_essid(network->ssid, network->ssid_len))
- network->flags |= NETWORK_EMPTY_ESSID;
-
- memcpy(&network->stats, stats, sizeof(network->stats));
-
- return 0;
-}
-
-static inline int is_same_network(struct ieee80211_network *src,
- struct ieee80211_network *dst)
-{
- /* A network is only a duplicate if the channel, BSSID, and ESSID
- * all match. We treat all <hidden> with the same BSSID and channel
- * as one network */
- return ((src->ssid_len == dst->ssid_len) &&
- (src->channel == dst->channel) &&
- !compare_ether_addr(src->bssid, dst->bssid) &&
- !memcmp(src->ssid, dst->ssid, src->ssid_len));
-}
-
-static void update_network(struct ieee80211_network *dst,
- struct ieee80211_network *src)
-{
- int qos_active;
- u8 old_param;
- DECLARE_MAC_BUF(mac);
-
- ieee80211_network_reset(dst);
- dst->ibss_dfs = src->ibss_dfs;
-
- /* We only update the statistics if they were created by receiving
- * the network information on the actual channel the network is on.
- *
- * This keeps beacons received on neighbor channels from bringing
- * down the signal level of an AP. */
- if (dst->channel == src->stats.received_channel)
- memcpy(&dst->stats, &src->stats,
- sizeof(struct ieee80211_rx_stats));
- else
- IEEE80211_DEBUG_SCAN("Network %s info received "
- "off channel (%d vs. %d)\n", print_mac(mac, src->bssid),
- dst->channel, src->stats.received_channel);
-
- dst->capability = src->capability;
- memcpy(dst->rates, src->rates, src->rates_len);
- dst->rates_len = src->rates_len;
- memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
- dst->rates_ex_len = src->rates_ex_len;
-
- dst->mode = src->mode;
- dst->flags = src->flags;
- dst->time_stamp[0] = src->time_stamp[0];
- dst->time_stamp[1] = src->time_stamp[1];
-
- dst->beacon_interval = src->beacon_interval;
- dst->listen_interval = src->listen_interval;
- dst->atim_window = src->atim_window;
- dst->erp_value = src->erp_value;
- dst->tim = src->tim;
-
- memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
- dst->wpa_ie_len = src->wpa_ie_len;
- memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
- dst->rsn_ie_len = src->rsn_ie_len;
-
- dst->last_scanned = jiffies;
- qos_active = src->qos_data.active;
- old_param = dst->qos_data.old_param_count;
- if (dst->flags & NETWORK_HAS_QOS_MASK)
- memcpy(&dst->qos_data, &src->qos_data,
- sizeof(struct ieee80211_qos_data));
- else {
- dst->qos_data.supported = src->qos_data.supported;
- dst->qos_data.param_count = src->qos_data.param_count;
- }
-
- if (dst->qos_data.supported == 1) {
- if (dst->ssid_len)
- IEEE80211_DEBUG_QOS
- ("QoS the network %s is QoS supported\n",
- dst->ssid);
- else
- IEEE80211_DEBUG_QOS
- ("QoS the network is QoS supported\n");
- }
- dst->qos_data.active = qos_active;
- dst->qos_data.old_param_count = old_param;
-
- /* dst->last_associate is not overwritten */
-}
-
-static inline int is_beacon(__le16 fc)
-{
- return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON);
-}
-
-static void ieee80211_process_probe_response(struct ieee80211_device
- *ieee, struct
- ieee80211_probe_response
- *beacon, struct ieee80211_rx_stats
- *stats)
-{
- struct net_device *dev = ieee->dev;
- struct ieee80211_network network = {
- .ibss_dfs = NULL,
- };
- struct ieee80211_network *target;
- struct ieee80211_network *oldest = NULL;
-#ifdef CONFIG_IEEE80211_DEBUG
- struct ieee80211_info_element *info_element = beacon->info_element;
-#endif
- unsigned long flags;
- DECLARE_MAC_BUF(mac);
-
- IEEE80211_DEBUG_SCAN("'%s' (%s"
- "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
- escape_essid(info_element->data, info_element->len),
- print_mac(mac, beacon->header.addr3),
- (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0',
- (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0');
-
- if (ieee80211_network_init(ieee, beacon, &network, stats)) {
- IEEE80211_DEBUG_SCAN("Dropped '%s' (%s) via %s.\n",
- escape_essid(info_element->data,
- info_element->len),
- print_mac(mac, beacon->header.addr3),
- is_beacon(beacon->header.frame_ctl) ?
- "BEACON" : "PROBE RESPONSE");
- return;
- }
-
- /* The network parsed correctly -- so now we scan our known networks
- * to see if we can find it in our list.
- *
- * NOTE: This search is definitely not optimized. Once its doing
- * the "right thing" we'll optimize it for efficiency if
- * necessary */
-
- /* Search for this entry in the list and update it if it is
- * already there. */
-
- spin_lock_irqsave(&ieee->lock, flags);
-
- list_for_each_entry(target, &ieee->network_list, list) {
- if (is_same_network(target, &network))
- break;
-
- if ((oldest == NULL) ||
- (target->last_scanned < oldest->last_scanned))
- oldest = target;
- }
-
- /* If we didn't find a match, then get a new network slot to initialize
- * with this beacon's information */
- if (&target->list == &ieee->network_list) {
- if (list_empty(&ieee->network_free_list)) {
- /* If there are no more slots, expire the oldest */
- list_del(&oldest->list);
- target = oldest;
- IEEE80211_DEBUG_SCAN("Expired '%s' (%s) from "
- "network list.\n",
- escape_essid(target->ssid,
- target->ssid_len),
- print_mac(mac, target->bssid));
- ieee80211_network_reset(target);
- } else {
- /* Otherwise just pull from the free list */
- target = list_entry(ieee->network_free_list.next,
- struct ieee80211_network, list);
- list_del(ieee->network_free_list.next);
- }
-
-#ifdef CONFIG_IEEE80211_DEBUG
- IEEE80211_DEBUG_SCAN("Adding '%s' (%s) via %s.\n",
- escape_essid(network.ssid,
- network.ssid_len),
- print_mac(mac, network.bssid),
- is_beacon(beacon->header.frame_ctl) ?
- "BEACON" : "PROBE RESPONSE");
-#endif
- memcpy(target, &network, sizeof(*target));
- network.ibss_dfs = NULL;
- list_add_tail(&target->list, &ieee->network_list);
- } else {
- IEEE80211_DEBUG_SCAN("Updating '%s' (%s) via %s.\n",
- escape_essid(target->ssid,
- target->ssid_len),
- print_mac(mac, target->bssid),
- is_beacon(beacon->header.frame_ctl) ?
- "BEACON" : "PROBE RESPONSE");
- update_network(target, &network);
- network.ibss_dfs = NULL;
- }
-
- spin_unlock_irqrestore(&ieee->lock, flags);
-
- if (is_beacon(beacon->header.frame_ctl)) {
- if (ieee->handle_beacon != NULL)
- ieee->handle_beacon(dev, beacon, target);
- } else {
- if (ieee->handle_probe_response != NULL)
- ieee->handle_probe_response(dev, beacon, target);
- }
-}
-
-void ieee80211_rx_mgt(struct ieee80211_device *ieee,
- struct ieee80211_hdr_4addr *header,
- struct ieee80211_rx_stats *stats)
-{
- switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) {
- case IEEE80211_STYPE_ASSOC_RESP:
- IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- ieee80211_handle_assoc_resp(ieee,
- (struct ieee80211_assoc_response *)
- header, stats);
- break;
-
- case IEEE80211_STYPE_REASSOC_RESP:
- IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- break;
-
- case IEEE80211_STYPE_PROBE_REQ:
- IEEE80211_DEBUG_MGMT("received auth (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
-
- if (ieee->handle_probe_request != NULL)
- ieee->handle_probe_request(ieee->dev,
- (struct
- ieee80211_probe_request *)
- header, stats);
- break;
-
- case IEEE80211_STYPE_PROBE_RESP:
- IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- IEEE80211_DEBUG_SCAN("Probe response\n");
- ieee80211_process_probe_response(ieee,
- (struct
- ieee80211_probe_response *)
- header, stats);
- break;
-
- case IEEE80211_STYPE_BEACON:
- IEEE80211_DEBUG_MGMT("received BEACON (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- IEEE80211_DEBUG_SCAN("Beacon\n");
- ieee80211_process_probe_response(ieee,
- (struct
- ieee80211_probe_response *)
- header, stats);
- break;
- case IEEE80211_STYPE_AUTH:
-
- IEEE80211_DEBUG_MGMT("received auth (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
-
- if (ieee->handle_auth != NULL)
- ieee->handle_auth(ieee->dev,
- (struct ieee80211_auth *)header);
- break;
-
- case IEEE80211_STYPE_DISASSOC:
- if (ieee->handle_disassoc != NULL)
- ieee->handle_disassoc(ieee->dev,
- (struct ieee80211_disassoc *)
- header);
- break;
-
- case IEEE80211_STYPE_ACTION:
- IEEE80211_DEBUG_MGMT("ACTION\n");
- if (ieee->handle_action)
- ieee->handle_action(ieee->dev,
- (struct ieee80211_action *)
- header, stats);
- break;
-
- case IEEE80211_STYPE_REASSOC_REQ:
- IEEE80211_DEBUG_MGMT("received reassoc (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
-
- IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n",
- ieee->dev->name);
- if (ieee->handle_reassoc_request != NULL)
- ieee->handle_reassoc_request(ieee->dev,
- (struct ieee80211_reassoc_request *)
- header);
- break;
-
- case IEEE80211_STYPE_ASSOC_REQ:
- IEEE80211_DEBUG_MGMT("received assoc (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
-
- IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n",
- ieee->dev->name);
- if (ieee->handle_assoc_request != NULL)
- ieee->handle_assoc_request(ieee->dev);
- break;
-
- case IEEE80211_STYPE_DEAUTH:
- IEEE80211_DEBUG_MGMT("DEAUTH\n");
- if (ieee->handle_deauth != NULL)
- ieee->handle_deauth(ieee->dev,
- (struct ieee80211_deauth *)
- header);
- break;
- default:
- IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n",
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n",
- ieee->dev->name,
- WLAN_FC_GET_STYPE(le16_to_cpu
- (header->frame_ctl)));
- break;
- }
-}
-
-EXPORT_SYMBOL_GPL(ieee80211_rx_any);
-EXPORT_SYMBOL(ieee80211_rx_mgt);
-EXPORT_SYMBOL(ieee80211_rx);
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
deleted file mode 100644
index d996547f7a62..000000000000
--- a/net/ieee80211/ieee80211_tx.c
+++ /dev/null
@@ -1,545 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
-#include <linux/compiler.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/tcp.h>
-#include <linux/types.h>
-#include <linux/wireless.h>
-#include <linux/etherdevice.h>
-#include <asm/uaccess.h>
-
-#include <net/ieee80211.h>
-
-/*
-
-802.11 Data Frame
-
- ,-------------------------------------------------------------------.
-Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
- |------|------|---------|---------|---------|------|---------|------|
-Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
- | | tion | (BSSID) | | | ence | data | |
- `--------------------------------------------------| |------'
-Total: 28 non-data bytes `----.----'
- |
- .- 'Frame data' expands, if WEP enabled, to <----------'
- |
- V
- ,-----------------------.
-Bytes | 4 | 0-2296 | 4 |
- |-----|-----------|-----|
-Desc. | IV | Encrypted | ICV |
- | | Packet | |
- `-----| |-----'
- `-----.-----'
- |
- .- 'Encrypted Packet' expands to
- |
- V
- ,---------------------------------------------------.
-Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
- |------|------|---------|----------|------|---------|
-Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
- | DSAP | SSAP | | | | Packet |
- | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
- `----------------------------------------------------
-Total: 8 non-data bytes
-
-802.3 Ethernet Data Frame
-
- ,-----------------------------------------.
-Bytes | 6 | 6 | 2 | Variable | 4 |
- |-------|-------|------|-----------|------|
-Desc. | Dest. | Source| Type | IP Packet | fcs |
- | MAC | MAC | | | |
- `-----------------------------------------'
-Total: 18 non-data bytes
-
-In the event that fragmentation is required, the incoming payload is split into
-N parts of size ieee->fts. The first fragment contains the SNAP header and the
-remaining packets are just data.
-
-If encryption is enabled, each fragment payload size is reduced by enough space
-to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
-So if you have 1500 bytes of payload with ieee->fts set to 500 without
-encryption it will take 3 frames. With WEP it will take 4 frames as the
-payload of each frame is reduced to 492 bytes.
-
-* SKB visualization
-*
-* ,- skb->data
-* |
-* | ETHERNET HEADER ,-<-- PAYLOAD
-* | | 14 bytes from skb->data
-* | 2 bytes for Type --> ,T. | (sizeof ethhdr)
-* | | | |
-* |,-Dest.--. ,--Src.---. | | |
-* | 6 bytes| | 6 bytes | | | |
-* v | | | | | |
-* 0 | v 1 | v | v 2
-* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
-* ^ | ^ | ^ |
-* | | | | | |
-* | | | | `T' <---- 2 bytes for Type
-* | | | |
-* | | '---SNAP--' <-------- 6 bytes for SNAP
-* | |
-* `-IV--' <-------------------- 4 bytes for IV (WEP)
-*
-* SNAP HEADER
-*
-*/
-
-static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
-static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
-
-static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
-{
- struct ieee80211_snap_hdr *snap;
- u8 *oui;
-
- snap = (struct ieee80211_snap_hdr *)data;
- snap->dsap = 0xaa;
- snap->ssap = 0xaa;
- snap->ctrl = 0x03;
-
- if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
- oui = P802_1H_OUI;
- else
- oui = RFC1042_OUI;
- snap->oui[0] = oui[0];
- snap->oui[1] = oui[1];
- snap->oui[2] = oui[2];
-
- memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
-
- return SNAP_SIZE + sizeof(u16);
-}
-
-static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
- struct sk_buff *frag, int hdr_len)
-{
- struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
- int res;
-
- if (crypt == NULL)
- return -1;
-
- /* To encrypt, frame format is:
- * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
- atomic_inc(&crypt->refcnt);
- res = 0;
- if (crypt->ops && crypt->ops->encrypt_mpdu)
- res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
-
- atomic_dec(&crypt->refcnt);
- if (res < 0) {
- printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
- ieee->dev->name, frag->len);
- ieee->ieee_stats.tx_discards++;
- return -1;
- }
-
- return 0;
-}
-
-void ieee80211_txb_free(struct ieee80211_txb *txb)
-{
- int i;
- if (unlikely(!txb))
- return;
- for (i = 0; i < txb->nr_frags; i++)
- if (txb->fragments[i])
- dev_kfree_skb_any(txb->fragments[i]);
- kfree(txb);
-}
-
-static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
- int headroom, gfp_t gfp_mask)
-{
- struct ieee80211_txb *txb;
- int i;
- txb = kmalloc(sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags),
- gfp_mask);
- if (!txb)
- return NULL;
-
- memset(txb, 0, sizeof(struct ieee80211_txb));
- txb->nr_frags = nr_frags;
- txb->frag_size = txb_size;
-
- for (i = 0; i < nr_frags; i++) {
- txb->fragments[i] = __dev_alloc_skb(txb_size + headroom,
- gfp_mask);
- if (unlikely(!txb->fragments[i])) {
- i--;
- break;
- }
- skb_reserve(txb->fragments[i], headroom);
- }
- if (unlikely(i != nr_frags)) {
- while (i >= 0)
- dev_kfree_skb_any(txb->fragments[i--]);
- kfree(txb);
- return NULL;
- }
- return txb;
-}
-
-static int ieee80211_classify(struct sk_buff *skb)
-{
- struct ethhdr *eth;
- struct iphdr *ip;
-
- eth = (struct ethhdr *)skb->data;
- if (eth->h_proto != htons(ETH_P_IP))
- return 0;
-
- ip = ip_hdr(skb);
- switch (ip->tos & 0xfc) {
- case 0x20:
- return 2;
- case 0x40:
- return 1;
- case 0x60:
- return 3;
- case 0x80:
- return 4;
- case 0xa0:
- return 5;
- case 0xc0:
- return 6;
- case 0xe0:
- return 7;
- default:
- return 0;
- }
-}
-
-/* Incoming skb is converted to a txb which consists of
- * a block of 802.11 fragment packets (stored as skbs) */
-int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ieee80211_device *ieee = netdev_priv(dev);
- struct ieee80211_txb *txb = NULL;
- struct ieee80211_hdr_3addrqos *frag_hdr;
- int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
- rts_required;
- unsigned long flags;
- struct net_device_stats *stats = &ieee->stats;
- int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
- __be16 ether_type;
- int bytes, fc, hdr_len;
- struct sk_buff *skb_frag;
- struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
- .duration_id = 0,
- .seq_ctl = 0,
- .qos_ctl = 0
- };
- u8 dest[ETH_ALEN], src[ETH_ALEN];
- struct ieee80211_crypt_data *crypt;
- int priority = skb->priority;
- int snapped = 0;
-
- if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
- return NETDEV_TX_BUSY;
-
- spin_lock_irqsave(&ieee->lock, flags);
-
- /* If there is no driver handler to take the TXB, dont' bother
- * creating it... */
- if (!ieee->hard_start_xmit) {
- printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
- goto success;
- }
-
- if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
- printk(KERN_WARNING "%s: skb too small (%d).\n",
- ieee->dev->name, skb->len);
- goto success;
- }
-
- ether_type = ((struct ethhdr *)skb->data)->h_proto;
-
- crypt = ieee->crypt[ieee->tx_keyidx];
-
- encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
- ieee->sec.encrypt;
-
- host_encrypt = ieee->host_encrypt && encrypt && crypt;
- host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt;
- host_build_iv = ieee->host_build_iv && encrypt && crypt;
-
- if (!encrypt && ieee->ieee802_1x &&
- ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
- stats->tx_dropped++;
- goto success;
- }
-
- /* Save source and destination addresses */
- skb_copy_from_linear_data(skb, dest, ETH_ALEN);
- skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
-
- if (host_encrypt || host_build_iv)
- fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
- IEEE80211_FCTL_PROTECTED;
- else
- fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
-
- if (ieee->iw_mode == IW_MODE_INFRA) {
- fc |= IEEE80211_FCTL_TODS;
- /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
- memcpy(header.addr1, ieee->bssid, ETH_ALEN);
- memcpy(header.addr2, src, ETH_ALEN);
- memcpy(header.addr3, dest, ETH_ALEN);
- } else if (ieee->iw_mode == IW_MODE_ADHOC) {
- /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
- memcpy(header.addr1, dest, ETH_ALEN);
- memcpy(header.addr2, src, ETH_ALEN);
- memcpy(header.addr3, ieee->bssid, ETH_ALEN);
- }
- hdr_len = IEEE80211_3ADDR_LEN;
-
- if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) {
- fc |= IEEE80211_STYPE_QOS_DATA;
- hdr_len += 2;
-
- skb->priority = ieee80211_classify(skb);
- header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID);
- }
- header.frame_ctl = cpu_to_le16(fc);
-
- /* Advance the SKB to the start of the payload */
- skb_pull(skb, sizeof(struct ethhdr));
-
- /* Determine total amount of storage required for TXB packets */
- bytes = skb->len + SNAP_SIZE + sizeof(u16);
-
- /* Encrypt msdu first on the whole data packet. */
- if ((host_encrypt || host_encrypt_msdu) &&
- crypt && crypt->ops && crypt->ops->encrypt_msdu) {
- int res = 0;
- int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len +
- crypt->ops->extra_msdu_postfix_len;
- struct sk_buff *skb_new = dev_alloc_skb(len);
-
- if (unlikely(!skb_new))
- goto failed;
-
- skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len);
- memcpy(skb_put(skb_new, hdr_len), &header, hdr_len);
- snapped = 1;
- ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
- ether_type);
- skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
- res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
- if (res < 0) {
- IEEE80211_ERROR("msdu encryption failed\n");
- dev_kfree_skb_any(skb_new);
- goto failed;
- }
- dev_kfree_skb_any(skb);
- skb = skb_new;
- bytes += crypt->ops->extra_msdu_prefix_len +
- crypt->ops->extra_msdu_postfix_len;
- skb_pull(skb, hdr_len);
- }
-
- if (host_encrypt || ieee->host_open_frag) {
- /* Determine fragmentation size based on destination (multicast
- * and broadcast are not fragmented) */
- if (is_multicast_ether_addr(dest) ||
- is_broadcast_ether_addr(dest))
- frag_size = MAX_FRAG_THRESHOLD;
- else
- frag_size = ieee->fts;
-
- /* Determine amount of payload per fragment. Regardless of if
- * this stack is providing the full 802.11 header, one will
- * eventually be affixed to this fragment -- so we must account
- * for it when determining the amount of payload space. */
- bytes_per_frag = frag_size - hdr_len;
- if (ieee->config &
- (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
- bytes_per_frag -= IEEE80211_FCS_LEN;
-
- /* Each fragment may need to have room for encryptiong
- * pre/postfix */
- if (host_encrypt)
- bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
- crypt->ops->extra_mpdu_postfix_len;
-
- /* Number of fragments is the total
- * bytes_per_frag / payload_per_fragment */
- nr_frags = bytes / bytes_per_frag;
- bytes_last_frag = bytes % bytes_per_frag;
- if (bytes_last_frag)
- nr_frags++;
- else
- bytes_last_frag = bytes_per_frag;
- } else {
- nr_frags = 1;
- bytes_per_frag = bytes_last_frag = bytes;
- frag_size = bytes + hdr_len;
- }
-
- rts_required = (frag_size > ieee->rts
- && ieee->config & CFG_IEEE80211_RTS);
- if (rts_required)
- nr_frags++;
-
- /* When we allocate the TXB we allocate enough space for the reserve
- * and full fragment bytes (bytes_per_frag doesn't include prefix,
- * postfix, header, FCS, etc.) */
- txb = ieee80211_alloc_txb(nr_frags, frag_size,
- ieee->tx_headroom, GFP_ATOMIC);
- if (unlikely(!txb)) {
- printk(KERN_WARNING "%s: Could not allocate TXB\n",
- ieee->dev->name);
- goto failed;
- }
- txb->encrypted = encrypt;
- if (host_encrypt)
- txb->payload_size = frag_size * (nr_frags - 1) +
- bytes_last_frag;
- else
- txb->payload_size = bytes;
-
- if (rts_required) {
- skb_frag = txb->fragments[0];
- frag_hdr =
- (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
-
- /*
- * Set header frame_ctl to the RTS.
- */
- header.frame_ctl =
- cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
- memcpy(frag_hdr, &header, hdr_len);
-
- /*
- * Restore header frame_ctl to the original data setting.
- */
- header.frame_ctl = cpu_to_le16(fc);
-
- if (ieee->config &
- (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
- skb_put(skb_frag, 4);
-
- txb->rts_included = 1;
- i = 1;
- } else
- i = 0;
-
- for (; i < nr_frags; i++) {
- skb_frag = txb->fragments[i];
-
- if (host_encrypt || host_build_iv)
- skb_reserve(skb_frag,
- crypt->ops->extra_mpdu_prefix_len);
-
- frag_hdr =
- (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
- memcpy(frag_hdr, &header, hdr_len);
-
- /* If this is not the last fragment, then add the MOREFRAGS
- * bit to the frame control */
- if (i != nr_frags - 1) {
- frag_hdr->frame_ctl =
- cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS);
- bytes = bytes_per_frag;
- } else {
- /* The last fragment takes the remaining length */
- bytes = bytes_last_frag;
- }
-
- if (i == 0 && !snapped) {
- ieee80211_copy_snap(skb_put
- (skb_frag, SNAP_SIZE + sizeof(u16)),
- ether_type);
- bytes -= SNAP_SIZE + sizeof(u16);
- }
-
- skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
-
- /* Advance the SKB... */
- skb_pull(skb, bytes);
-
- /* Encryption routine will move the header forward in order
- * to insert the IV between the header and the payload */
- if (host_encrypt)
- ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
- else if (host_build_iv) {
- atomic_inc(&crypt->refcnt);
- if (crypt->ops->build_iv)
- crypt->ops->build_iv(skb_frag, hdr_len,
- ieee->sec.keys[ieee->sec.active_key],
- ieee->sec.key_sizes[ieee->sec.active_key],
- crypt->priv);
- atomic_dec(&crypt->refcnt);
- }
-
- if (ieee->config &
- (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
- skb_put(skb_frag, 4);
- }
-
- success:
- spin_unlock_irqrestore(&ieee->lock, flags);
-
- dev_kfree_skb_any(skb);
-
- if (txb) {
- int ret = (*ieee->hard_start_xmit) (txb, dev, priority);
- if (ret == 0) {
- stats->tx_packets++;
- stats->tx_bytes += txb->payload_size;
- return 0;
- }
-
- ieee80211_txb_free(txb);
- }
-
- return 0;
-
- failed:
- spin_unlock_irqrestore(&ieee->lock, flags);
- netif_stop_queue(dev);
- stats->tx_errors++;
- return 1;
-}
-
-EXPORT_SYMBOL(ieee80211_txb_free);
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
deleted file mode 100644
index 973832dd7faf..000000000000
--- a/net/ieee80211/ieee80211_wx.c
+++ /dev/null
@@ -1,760 +0,0 @@
-/******************************************************************************
-
- Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
-
- Portions of this file are based on the WEP enablement code provided by the
- Host AP project hostap-drivers v0.1.3
- Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
- <j@w1.fi>
- Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
-
- 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., 59
- Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
- Contact Information:
- James P. Ketrenos <ipw2100-admin@linux.intel.com>
- Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
-******************************************************************************/
-
-#include <linux/kmod.h>
-#include <linux/module.h>
-#include <linux/jiffies.h>
-
-#include <net/ieee80211.h>
-#include <linux/wireless.h>
-
-static const char *ieee80211_modes[] = {
- "?", "a", "b", "ab", "g", "ag", "bg", "abg"
-};
-
-#define MAX_CUSTOM_LEN 64
-static char *ieee80211_translate_scan(struct ieee80211_device *ieee,
- char *start, char *stop,
- struct ieee80211_network *network,
- struct iw_request_info *info)
-{
- char custom[MAX_CUSTOM_LEN];
- char *p;
- struct iw_event iwe;
- int i, j;
- char *current_val; /* For rates */
- u8 rate;
-
- /* First entry *MUST* be the AP MAC address */
- iwe.cmd = SIOCGIWAP;
- iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
- memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
-
- /* Remaining entries will be displayed in the order we provide them */
-
- /* Add the ESSID */
- iwe.cmd = SIOCGIWESSID;
- iwe.u.data.flags = 1;
- if (network->flags & NETWORK_EMPTY_ESSID) {
- iwe.u.data.length = sizeof("<hidden>");
- start = iwe_stream_add_point(info, start, stop,
- &iwe, "<hidden>");
- } else {
- iwe.u.data.length = min(network->ssid_len, (u8) 32);
- start = iwe_stream_add_point(info, start, stop,
- &iwe, network->ssid);
- }
-
- /* Add the protocol name */
- iwe.cmd = SIOCGIWNAME;
- snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
- ieee80211_modes[network->mode]);
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
-
- /* Add mode */
- iwe.cmd = SIOCGIWMODE;
- if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
- if (network->capability & WLAN_CAPABILITY_ESS)
- iwe.u.mode = IW_MODE_MASTER;
- else
- iwe.u.mode = IW_MODE_ADHOC;
-
- start = iwe_stream_add_event(info, start, stop,
- &iwe, IW_EV_UINT_LEN);
- }
-
- /* Add channel and frequency */
- /* Note : userspace automatically computes channel using iwrange */
- iwe.cmd = SIOCGIWFREQ;
- iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel);
- iwe.u.freq.e = 6;
- iwe.u.freq.i = 0;
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
-
- /* Add encryption capability */
- iwe.cmd = SIOCGIWENCODE;
- if (network->capability & WLAN_CAPABILITY_PRIVACY)
- iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
- else
- iwe.u.data.flags = IW_ENCODE_DISABLED;
- iwe.u.data.length = 0;
- start = iwe_stream_add_point(info, start, stop,
- &iwe, network->ssid);
-
- /* Add basic and extended rates */
- /* Rate : stuffing multiple values in a single event require a bit
- * more of magic - Jean II */
- current_val = start + iwe_stream_lcp_len(info);
- iwe.cmd = SIOCGIWRATE;
- /* Those two flags are ignored... */
- iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
-
- for (i = 0, j = 0; i < network->rates_len;) {
- if (j < network->rates_ex_len &&
- ((network->rates_ex[j] & 0x7F) <
- (network->rates[i] & 0x7F)))
- rate = network->rates_ex[j++] & 0x7F;
- else
- rate = network->rates[i++] & 0x7F;
- /* Bit rate given in 500 kb/s units (+ 0x80) */
- iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
- /* Add new value to event */
- current_val = iwe_stream_add_value(info, start, current_val,
- stop, &iwe, IW_EV_PARAM_LEN);
- }
- for (; j < network->rates_ex_len; j++) {
- rate = network->rates_ex[j] & 0x7F;
- /* Bit rate given in 500 kb/s units (+ 0x80) */
- iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
- /* Add new value to event */
- current_val = iwe_stream_add_value(info, start, current_val,
- stop, &iwe, IW_EV_PARAM_LEN);
- }
- /* Check if we added any rate */
- if ((current_val - start) > iwe_stream_lcp_len(info))
- start = current_val;
-
- /* Add quality statistics */
- iwe.cmd = IWEVQUAL;
- iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
- IW_QUAL_NOISE_UPDATED;
-
- if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) {
- iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
- IW_QUAL_LEVEL_INVALID;
- iwe.u.qual.qual = 0;
- } else {
- if (ieee->perfect_rssi == ieee->worst_rssi)
- iwe.u.qual.qual = 100;
- else
- iwe.u.qual.qual =
- (100 *
- (ieee->perfect_rssi - ieee->worst_rssi) *
- (ieee->perfect_rssi - ieee->worst_rssi) -
- (ieee->perfect_rssi - network->stats.rssi) *
- (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
- 62 * (ieee->perfect_rssi -
- network->stats.rssi))) /
- ((ieee->perfect_rssi -
- ieee->worst_rssi) * (ieee->perfect_rssi -
- ieee->worst_rssi));
- if (iwe.u.qual.qual > 100)
- iwe.u.qual.qual = 100;
- else if (iwe.u.qual.qual < 1)
- iwe.u.qual.qual = 0;
- }
-
- if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) {
- iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
- iwe.u.qual.noise = 0;
- } else {
- iwe.u.qual.noise = network->stats.noise;
- }
-
- if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) {
- iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
- iwe.u.qual.level = 0;
- } else {
- iwe.u.qual.level = network->stats.signal;
- }
-
- start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
-
- iwe.cmd = IWEVCUSTOM;
- p = custom;
-
- iwe.u.data.length = p - custom;
- if (iwe.u.data.length)
- start = iwe_stream_add_point(info, start, stop, &iwe, custom);
-
- memset(&iwe, 0, sizeof(iwe));
- if (network->wpa_ie_len) {
- char buf[MAX_WPA_IE_LEN];
- memcpy(buf, network->wpa_ie, network->wpa_ie_len);
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = network->wpa_ie_len;
- start = iwe_stream_add_point(info, start, stop, &iwe, buf);
- }
-
- memset(&iwe, 0, sizeof(iwe));
- if (network->rsn_ie_len) {
- char buf[MAX_WPA_IE_LEN];
- memcpy(buf, network->rsn_ie, network->rsn_ie_len);
- iwe.cmd = IWEVGENIE;
- iwe.u.data.length = network->rsn_ie_len;
- start = iwe_stream_add_point(info, start, stop, &iwe, buf);
- }
-
- /* Add EXTRA: Age to display seconds since last beacon/probe response
- * for given network. */
- iwe.cmd = IWEVCUSTOM;
- p = custom;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
- " Last beacon: %dms ago",
- jiffies_to_msecs(jiffies - network->last_scanned));
- iwe.u.data.length = p - custom;
- if (iwe.u.data.length)
- start = iwe_stream_add_point(info, start, stop, &iwe, custom);
-
- /* Add spectrum management information */
- iwe.cmd = -1;
- p = custom;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
-
- if (ieee80211_get_channel_flags(ieee, network->channel) &
- IEEE80211_CH_INVALID) {
- iwe.cmd = IWEVCUSTOM;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
- }
-
- if (ieee80211_get_channel_flags(ieee, network->channel) &
- IEEE80211_CH_RADAR_DETECT) {
- iwe.cmd = IWEVCUSTOM;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
- }
-
- if (iwe.cmd == IWEVCUSTOM) {
- iwe.u.data.length = p - custom;
- start = iwe_stream_add_point(info, start, stop, &iwe, custom);
- }
-
- return start;
-}
-
-#define SCAN_ITEM_SIZE 128
-
-int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ieee80211_network *network;
- unsigned long flags;
- int err = 0;
-
- char *ev = extra;
- char *stop = ev + wrqu->data.length;
- int i = 0;
- DECLARE_MAC_BUF(mac);
-
- IEEE80211_DEBUG_WX("Getting scan\n");
-
- spin_lock_irqsave(&ieee->lock, flags);
-
- list_for_each_entry(network, &ieee->network_list, list) {
- i++;
- if (stop - ev < SCAN_ITEM_SIZE) {
- err = -E2BIG;
- break;
- }
-
- if (ieee->scan_age == 0 ||
- time_after(network->last_scanned + ieee->scan_age, jiffies))
- ev = ieee80211_translate_scan(ieee, ev, stop, network,
- info);
- else
- IEEE80211_DEBUG_SCAN("Not showing network '%s ("
- "%s)' due to age (%dms).\n",
- escape_essid(network->ssid,
- network->ssid_len),
- print_mac(mac, network->bssid),
- jiffies_to_msecs(jiffies -
- network->
- last_scanned));
- }
-
- spin_unlock_irqrestore(&ieee->lock, flags);
-
- wrqu->data.length = ev - extra;
- wrqu->data.flags = 0;
-
- IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
-
- return err;
-}
-
-int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *keybuf)
-{
- struct iw_point *erq = &(wrqu->encoding);
- struct net_device *dev = ieee->dev;
- struct ieee80211_security sec = {
- .flags = 0
- };
- int i, key, key_provided, len;
- struct ieee80211_crypt_data **crypt;
- int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv;
-
- IEEE80211_DEBUG_WX("SET_ENCODE\n");
-
- key = erq->flags & IW_ENCODE_INDEX;
- if (key) {
- if (key > WEP_KEYS)
- return -EINVAL;
- key--;
- key_provided = 1;
- } else {
- key_provided = 0;
- key = ieee->tx_keyidx;
- }
-
- IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
- "provided" : "default");
-
- crypt = &ieee->crypt[key];
-
- if (erq->flags & IW_ENCODE_DISABLED) {
- if (key_provided && *crypt) {
- IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
- key);
- ieee80211_crypt_delayed_deinit(ieee, crypt);
- } else
- IEEE80211_DEBUG_WX("Disabling encryption.\n");
-
- /* Check all the keys to see if any are still configured,
- * and if no key index was provided, de-init them all */
- for (i = 0; i < WEP_KEYS; i++) {
- if (ieee->crypt[i] != NULL) {
- if (key_provided)
- break;
- ieee80211_crypt_delayed_deinit(ieee,
- &ieee->crypt[i]);
- }
- }
-
- if (i == WEP_KEYS) {
- sec.enabled = 0;
- sec.encrypt = 0;
- sec.level = SEC_LEVEL_0;
- sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
- }
-
- goto done;
- }
-
- sec.enabled = 1;
- sec.encrypt = 1;
- sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
-
- if (*crypt != NULL && (*crypt)->ops != NULL &&
- strcmp((*crypt)->ops->name, "WEP") != 0) {
- /* changing to use WEP; deinit previously used algorithm
- * on this key */
- ieee80211_crypt_delayed_deinit(ieee, crypt);
- }
-
- if (*crypt == NULL && host_crypto) {
- struct ieee80211_crypt_data *new_crypt;
-
- /* take WEP into use */
- new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
- GFP_KERNEL);
- if (new_crypt == NULL)
- return -ENOMEM;
- new_crypt->ops = ieee80211_get_crypto_ops("WEP");
- if (!new_crypt->ops) {
- request_module("ieee80211_crypt_wep");
- new_crypt->ops = ieee80211_get_crypto_ops("WEP");
- }
-
- if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
- new_crypt->priv = new_crypt->ops->init(key);
-
- if (!new_crypt->ops || !new_crypt->priv) {
- kfree(new_crypt);
- new_crypt = NULL;
-
- printk(KERN_WARNING "%s: could not initialize WEP: "
- "load module ieee80211_crypt_wep\n", dev->name);
- return -EOPNOTSUPP;
- }
- *crypt = new_crypt;
- }
-
- /* If a new key was provided, set it up */
- if (erq->length > 0) {
- len = erq->length <= 5 ? 5 : 13;
- memcpy(sec.keys[key], keybuf, erq->length);
- if (len > erq->length)
- memset(sec.keys[key] + erq->length, 0,
- len - erq->length);
- IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
- key, escape_essid(sec.keys[key], len),
- erq->length, len);
- sec.key_sizes[key] = len;
- if (*crypt)
- (*crypt)->ops->set_key(sec.keys[key], len, NULL,
- (*crypt)->priv);
- sec.flags |= (1 << key);
- /* This ensures a key will be activated if no key is
- * explicitly set */
- if (key == sec.active_key)
- sec.flags |= SEC_ACTIVE_KEY;
-
- } else {
- if (host_crypto) {
- len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
- NULL, (*crypt)->priv);
- if (len == 0) {
- /* Set a default key of all 0 */
- IEEE80211_DEBUG_WX("Setting key %d to all "
- "zero.\n", key);
- memset(sec.keys[key], 0, 13);
- (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
- (*crypt)->priv);
- sec.key_sizes[key] = 13;
- sec.flags |= (1 << key);
- }
- }
- /* No key data - just set the default TX key index */
- if (key_provided) {
- IEEE80211_DEBUG_WX("Setting key %d to default Tx "
- "key.\n", key);
- ieee->tx_keyidx = key;
- sec.active_key = key;
- sec.flags |= SEC_ACTIVE_KEY;
- }
- }
- if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
- ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
- sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
- WLAN_AUTH_SHARED_KEY;
- sec.flags |= SEC_AUTH_MODE;
- IEEE80211_DEBUG_WX("Auth: %s\n",
- sec.auth_mode == WLAN_AUTH_OPEN ?
- "OPEN" : "SHARED KEY");
- }
-
- /* For now we just support WEP, so only set that security level...
- * TODO: When WPA is added this is one place that needs to change */
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
- sec.encode_alg[key] = SEC_ALG_WEP;
-
- done:
- if (ieee->set_security)
- ieee->set_security(dev, &sec);
-
- /* Do not reset port if card is in Managed mode since resetting will
- * generate new IEEE 802.11 authentication which may end up in looping
- * with IEEE 802.1X. If your hardware requires a reset after WEP
- * configuration (for example... Prism2), implement the reset_port in
- * the callbacks structures used to initialize the 802.11 stack. */
- if (ieee->reset_on_keychange &&
- ieee->iw_mode != IW_MODE_INFRA &&
- ieee->reset_port && ieee->reset_port(dev)) {
- printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
- return -EINVAL;
- }
- return 0;
-}
-
-int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *keybuf)
-{
- struct iw_point *erq = &(wrqu->encoding);
- int len, key;
- struct ieee80211_crypt_data *crypt;
- struct ieee80211_security *sec = &ieee->sec;
-
- IEEE80211_DEBUG_WX("GET_ENCODE\n");
-
- key = erq->flags & IW_ENCODE_INDEX;
- if (key) {
- if (key > WEP_KEYS)
- return -EINVAL;
- key--;
- } else
- key = ieee->tx_keyidx;
-
- crypt = ieee->crypt[key];
- erq->flags = key + 1;
-
- if (!sec->enabled) {
- erq->length = 0;
- erq->flags |= IW_ENCODE_DISABLED;
- return 0;
- }
-
- len = sec->key_sizes[key];
- memcpy(keybuf, sec->keys[key], len);
-
- erq->length = len;
- erq->flags |= IW_ENCODE_ENABLED;
-
- if (ieee->open_wep)
- erq->flags |= IW_ENCODE_OPEN;
- else
- erq->flags |= IW_ENCODE_RESTRICTED;
-
- return 0;
-}
-
-int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct net_device *dev = ieee->dev;
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- int i, idx, ret = 0;
- int group_key = 0;
- const char *alg, *module;
- struct ieee80211_crypto_ops *ops;
- struct ieee80211_crypt_data **crypt;
-
- struct ieee80211_security sec = {
- .flags = 0,
- };
-
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if (idx < 1 || idx > WEP_KEYS)
- return -EINVAL;
- idx--;
- } else
- idx = ieee->tx_keyidx;
-
- if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
- crypt = &ieee->crypt[idx];
- group_key = 1;
- } else {
- /* some Cisco APs use idx>0 for unicast in dynamic WEP */
- if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
- return -EINVAL;
- if (ieee->iw_mode == IW_MODE_INFRA)
- crypt = &ieee->crypt[idx];
- else
- return -EINVAL;
- }
-
- sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
- if ((encoding->flags & IW_ENCODE_DISABLED) ||
- ext->alg == IW_ENCODE_ALG_NONE) {
- if (*crypt)
- ieee80211_crypt_delayed_deinit(ieee, crypt);
-
- for (i = 0; i < WEP_KEYS; i++)
- if (ieee->crypt[i] != NULL)
- break;
-
- if (i == WEP_KEYS) {
- sec.enabled = 0;
- sec.encrypt = 0;
- sec.level = SEC_LEVEL_0;
- sec.flags |= SEC_LEVEL;
- }
- goto done;
- }
-
- sec.enabled = 1;
- sec.encrypt = 1;
-
- if (group_key ? !ieee->host_mc_decrypt :
- !(ieee->host_encrypt || ieee->host_decrypt ||
- ieee->host_encrypt_msdu))
- goto skip_host_crypt;
-
- switch (ext->alg) {
- case IW_ENCODE_ALG_WEP:
- alg = "WEP";
- module = "ieee80211_crypt_wep";
- break;
- case IW_ENCODE_ALG_TKIP:
- alg = "TKIP";
- module = "ieee80211_crypt_tkip";
- break;
- case IW_ENCODE_ALG_CCMP:
- alg = "CCMP";
- module = "ieee80211_crypt_ccmp";
- break;
- default:
- IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
- dev->name, ext->alg);
- ret = -EINVAL;
- goto done;
- }
-
- ops = ieee80211_get_crypto_ops(alg);
- if (ops == NULL) {
- request_module(module);
- ops = ieee80211_get_crypto_ops(alg);
- }
- if (ops == NULL) {
- IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
- dev->name, ext->alg);
- ret = -EINVAL;
- goto done;
- }
-
- if (*crypt == NULL || (*crypt)->ops != ops) {
- struct ieee80211_crypt_data *new_crypt;
-
- ieee80211_crypt_delayed_deinit(ieee, crypt);
-
- new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
- if (new_crypt == NULL) {
- ret = -ENOMEM;
- goto done;
- }
- new_crypt->ops = ops;
- if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
- new_crypt->priv = new_crypt->ops->init(idx);
- if (new_crypt->priv == NULL) {
- kfree(new_crypt);
- ret = -EINVAL;
- goto done;
- }
- *crypt = new_crypt;
- }
-
- if (ext->key_len > 0 && (*crypt)->ops->set_key &&
- (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
- (*crypt)->priv) < 0) {
- IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
- ret = -EINVAL;
- goto done;
- }
-
- skip_host_crypt:
- if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
- ieee->tx_keyidx = idx;
- sec.active_key = idx;
- sec.flags |= SEC_ACTIVE_KEY;
- }
-
- if (ext->alg != IW_ENCODE_ALG_NONE) {
- memcpy(sec.keys[idx], ext->key, ext->key_len);
- sec.key_sizes[idx] = ext->key_len;
- sec.flags |= (1 << idx);
- if (ext->alg == IW_ENCODE_ALG_WEP) {
- sec.encode_alg[idx] = SEC_ALG_WEP;
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_1;
- } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
- sec.encode_alg[idx] = SEC_ALG_TKIP;
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_2;
- } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
- sec.encode_alg[idx] = SEC_ALG_CCMP;
- sec.flags |= SEC_LEVEL;
- sec.level = SEC_LEVEL_3;
- }
- /* Don't set sec level for group keys. */
- if (group_key)
- sec.flags &= ~SEC_LEVEL;
- }
- done:
- if (ieee->set_security)
- ieee->set_security(ieee->dev, &sec);
-
- /*
- * Do not reset port if card is in Managed mode since resetting will
- * generate new IEEE 802.11 authentication which may end up in looping
- * with IEEE 802.1X. If your hardware requires a reset after WEP
- * configuration (for example... Prism2), implement the reset_port in
- * the callbacks structures used to initialize the 802.11 stack.
- */
- if (ieee->reset_on_keychange &&
- ieee->iw_mode != IW_MODE_INFRA &&
- ieee->reset_port && ieee->reset_port(dev)) {
- IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
- return -EINVAL;
- }
-
- return ret;
-}
-
-int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct iw_point *encoding = &wrqu->encoding;
- struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
- struct ieee80211_security *sec = &ieee->sec;
- int idx, max_key_len;
-
- max_key_len = encoding->length - sizeof(*ext);
- if (max_key_len < 0)
- return -EINVAL;
-
- idx = encoding->flags & IW_ENCODE_INDEX;
- if (idx) {
- if (idx < 1 || idx > WEP_KEYS)
- return -EINVAL;
- idx--;
- } else
- idx = ieee->tx_keyidx;
-
- if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
- ext->alg != IW_ENCODE_ALG_WEP)
- if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
- return -EINVAL;
-
- encoding->flags = idx + 1;
- memset(ext, 0, sizeof(*ext));
-
- if (!sec->enabled) {
- ext->alg = IW_ENCODE_ALG_NONE;
- ext->key_len = 0;
- encoding->flags |= IW_ENCODE_DISABLED;
- } else {
- if (sec->encode_alg[idx] == SEC_ALG_WEP)
- ext->alg = IW_ENCODE_ALG_WEP;
- else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
- ext->alg = IW_ENCODE_ALG_TKIP;
- else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
- ext->alg = IW_ENCODE_ALG_CCMP;
- else
- return -EINVAL;
-
- ext->key_len = sec->key_sizes[idx];
- memcpy(ext->key, sec->keys[idx], ext->key_len);
- encoding->flags |= IW_ENCODE_ENABLED;
- if (ext->key_len &&
- (ext->alg == IW_ENCODE_ALG_TKIP ||
- ext->alg == IW_ENCODE_ALG_CCMP))
- ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
-
- }
-
- return 0;
-}
-
-EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
-EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
-
-EXPORT_SYMBOL(ieee80211_wx_get_scan);
-EXPORT_SYMBOL(ieee80211_wx_set_encode);
-EXPORT_SYMBOL(ieee80211_wx_get_encode);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 1aa2dc9e380e..743f5542d65a 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -94,6 +94,7 @@
#include <linux/igmp.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
+#include <net/checksum.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
@@ -245,7 +246,7 @@ static inline int inet_netns_ok(struct net *net, int protocol)
int hash;
struct net_protocol *ipprot;
- if (net == &init_net)
+ if (net_eq(net, &init_net))
return 1;
hash = protocol & (MAX_INET_PROTOS - 1);
@@ -272,10 +273,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol)
int try_loading_module = 0;
int err;
- if (sock->type != SOCK_RAW &&
- sock->type != SOCK_DGRAM &&
- !inet_ehash_secret)
- build_ehash_secret();
+ if (unlikely(!inet_ehash_secret))
+ if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
+ build_ehash_secret();
sock->state = SS_UNCONNECTED;
@@ -1070,11 +1070,8 @@ static int inet_sk_reselect_saddr(struct sock *sk)
return 0;
if (sysctl_ip_dynaddr > 1) {
- printk(KERN_INFO "%s(): shifting inet->"
- "saddr from " NIPQUAD_FMT " to " NIPQUAD_FMT "\n",
- __func__,
- NIPQUAD(old_saddr),
- NIPQUAD(new_saddr));
+ printk(KERN_INFO "%s(): shifting inet->saddr from %pI4 to %pI4\n",
+ __func__, &old_saddr, &new_saddr);
}
inet->saddr = inet->rcv_saddr = new_saddr;
@@ -1245,6 +1242,100 @@ out:
return segs;
}
+static struct sk_buff **inet_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ struct net_protocol *ops;
+ struct sk_buff **pp = NULL;
+ struct sk_buff *p;
+ struct iphdr *iph;
+ int flush = 1;
+ int proto;
+ int id;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
+ goto out;
+
+ iph = ip_hdr(skb);
+ proto = iph->protocol & (MAX_INET_PROTOS - 1);
+
+ rcu_read_lock();
+ ops = rcu_dereference(inet_protos[proto]);
+ if (!ops || !ops->gro_receive)
+ goto out_unlock;
+
+ if (iph->version != 4 || iph->ihl != 5)
+ goto out_unlock;
+
+ if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
+ goto out_unlock;
+
+ flush = ntohs(iph->tot_len) != skb->len ||
+ iph->frag_off != htons(IP_DF);
+ id = ntohs(iph->id);
+
+ for (p = *head; p; p = p->next) {
+ struct iphdr *iph2;
+
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ iph2 = ip_hdr(p);
+
+ if (iph->protocol != iph2->protocol ||
+ iph->tos != iph2->tos ||
+ memcmp(&iph->saddr, &iph2->saddr, 8)) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ /* All fields must match except length and checksum. */
+ NAPI_GRO_CB(p)->flush |=
+ memcmp(&iph->frag_off, &iph2->frag_off, 4) ||
+ (u16)(ntohs(iph2->id) + NAPI_GRO_CB(p)->count) != id;
+
+ NAPI_GRO_CB(p)->flush |= flush;
+ }
+
+ NAPI_GRO_CB(skb)->flush |= flush;
+ __skb_pull(skb, sizeof(*iph));
+ skb_reset_transport_header(skb);
+
+ pp = ops->gro_receive(head, skb);
+
+out_unlock:
+ rcu_read_unlock();
+
+out:
+ NAPI_GRO_CB(skb)->flush |= flush;
+
+ return pp;
+}
+
+static int inet_gro_complete(struct sk_buff *skb)
+{
+ struct net_protocol *ops;
+ struct iphdr *iph = ip_hdr(skb);
+ int proto = iph->protocol & (MAX_INET_PROTOS - 1);
+ int err = -ENOSYS;
+ __be16 newlen = htons(skb->len - skb_network_offset(skb));
+
+ csum_replace2(&iph->check, iph->tot_len, newlen);
+ iph->tot_len = newlen;
+
+ rcu_read_lock();
+ ops = rcu_dereference(inet_protos[proto]);
+ if (WARN_ON(!ops || !ops->gro_complete))
+ goto out_unlock;
+
+ err = ops->gro_complete(skb);
+
+out_unlock:
+ rcu_read_unlock();
+
+ return err;
+}
+
int inet_ctl_sock_create(struct sock **sk, unsigned short family,
unsigned short type, unsigned char protocol,
struct net *net)
@@ -1311,6 +1402,7 @@ EXPORT_SYMBOL_GPL(snmp_mib_free);
#ifdef CONFIG_IP_MULTICAST
static struct net_protocol igmp_protocol = {
.handler = igmp_rcv,
+ .netns_ok = 1,
};
#endif
@@ -1319,6 +1411,8 @@ static struct net_protocol tcp_protocol = {
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
+ .gro_receive = tcp4_gro_receive,
+ .gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
};
@@ -1411,6 +1505,8 @@ static struct packet_type ip_packet_type = {
.func = ip_rcv,
.gso_send_check = inet_gso_send_check,
.gso_segment = inet_gso_segment,
+ .gro_receive = inet_gro_receive,
+ .gro_complete = inet_gro_complete,
};
static int __init inet_init(void)
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index 8219b7e0968d..e878e494296e 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -201,15 +201,16 @@ out:
static void ah4_err(struct sk_buff *skb, u32 info)
{
- struct iphdr *iph = (struct iphdr*)skb->data;
- struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+(iph->ihl<<2));
+ struct net *net = dev_net(skb->dev);
+ struct iphdr *iph = (struct iphdr *)skb->data;
+ struct ip_auth_hdr *ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
struct xfrm_state *x;
if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
+ x = xfrm_state_lookup(net, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
if (!x)
return;
printk(KERN_DEBUG "pmtu discovery on SA AH/%08x/%08x\n",
@@ -293,9 +294,7 @@ static void ah_destroy(struct xfrm_state *x)
return;
kfree(ahp->work_icv);
- ahp->work_icv = NULL;
crypto_free_hash(ahp->tfm);
- ahp->tfm = NULL;
kfree(ahp);
}
@@ -316,6 +315,7 @@ static struct net_protocol ah4_protocol = {
.handler = xfrm4_rcv,
.err_handler = ah4_err,
.no_policy = 1,
+ .netns_ok = 1,
};
static int __init ah4_init(void)
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 1a9dd66511fc..29a74c01d8de 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -506,7 +506,7 @@ int arp_bind_neighbour(struct dst_entry *dst)
if (dev == NULL)
return -EINVAL;
if (n == NULL) {
- __be32 nexthop = ((struct rtable*)dst)->rt_gateway;
+ __be32 nexthop = ((struct rtable *)dst)->rt_gateway;
if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))
nexthop = 0;
n = __neigh_lookup_errno(
@@ -640,14 +640,14 @@ struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
arp_ptr=(unsigned char *)(arp+1);
memcpy(arp_ptr, src_hw, dev->addr_len);
- arp_ptr+=dev->addr_len;
- memcpy(arp_ptr, &src_ip,4);
- arp_ptr+=4;
+ arp_ptr += dev->addr_len;
+ memcpy(arp_ptr, &src_ip, 4);
+ arp_ptr += 4;
if (target_hw != NULL)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
- arp_ptr+=dev->addr_len;
+ arp_ptr += dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4);
return skb;
@@ -818,18 +818,18 @@ static int arp_process(struct sk_buff *skb)
addr_type = rt->rt_type;
if (addr_type == RTN_LOCAL) {
- n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
- if (n) {
- int dont_send = 0;
-
- if (!dont_send)
- dont_send |= arp_ignore(in_dev,sip,tip);
- if (!dont_send && IN_DEV_ARPFILTER(in_dev))
- dont_send |= arp_filter(sip,tip,dev);
- if (!dont_send)
- arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+ int dont_send = 0;
- neigh_release(n);
+ if (!dont_send)
+ dont_send |= arp_ignore(in_dev,sip,tip);
+ if (!dont_send && IN_DEV_ARPFILTER(in_dev))
+ dont_send |= arp_filter(sip,tip,dev);
+ if (!dont_send) {
+ n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
+ if (n) {
+ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+ neigh_release(n);
+ }
}
goto out;
} else if (IN_DEV_FORWARD(in_dev)) {
@@ -1308,7 +1308,7 @@ static void arp_format_neigh_entry(struct seq_file *seq,
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
}
#endif
- sprintf(tbuf, NIPQUAD_FMT, NIPQUAD(*(u32*)n->primary_key));
+ sprintf(tbuf, "%pI4", n->primary_key);
seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n",
tbuf, hatype, arp_state_to_flags(n), hbuffer, dev->name);
read_unlock(&n->lock);
@@ -1321,7 +1321,7 @@ static void arp_format_pneigh_entry(struct seq_file *seq,
int hatype = dev ? dev->type : 0;
char tbuf[16];
- sprintf(tbuf, NIPQUAD_FMT, NIPQUAD(*(u32*)n->key));
+ sprintf(tbuf, "%pI4", n->key);
seq_printf(seq, "%-16s 0x%-10x0x%-10x%s * %s\n",
tbuf, hatype, ATF_PUBL | ATF_PERM, "00:00:00:00:00:00",
dev ? dev->name : "*");
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 2e78f6bd9775..e52799047a5f 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -490,7 +490,6 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
}
atomic_set(&doi_def->refcount, 1);
- INIT_RCU_HEAD(&doi_def->rcu);
spin_lock(&cipso_v4_doi_list_lock);
if (cipso_v4_doi_search(doi_def->doi) != NULL)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 56fce3ab6c55..309997edc8a5 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -112,13 +112,7 @@ static inline void devinet_sysctl_unregister(struct in_device *idev)
static struct in_ifaddr *inet_alloc_ifa(void)
{
- struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
-
- if (ifa) {
- INIT_RCU_HEAD(&ifa->rcu_head);
- }
-
- return ifa;
+ return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
}
static void inet_rcu_free_ifa(struct rcu_head *head)
@@ -161,7 +155,6 @@ static struct in_device *inetdev_init(struct net_device *dev)
in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
if (!in_dev)
goto out;
- INIT_RCU_HEAD(&in_dev->rcu_head);
memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
sizeof(in_dev->cnf));
in_dev->cnf.sysctl = NULL;
@@ -1108,7 +1101,7 @@ out:
}
static struct notifier_block ip_netdev_notifier = {
- .notifier_call =inetdev_event,
+ .notifier_call = inetdev_event,
};
static inline size_t inet_nlmsg_size(void)
@@ -1195,7 +1188,7 @@ done:
return skb->len;
}
-static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh,
+static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
u32 pid)
{
struct sk_buff *skb;
@@ -1262,7 +1255,7 @@ static void inet_forward_change(struct net *net)
}
static int devinet_conf_proc(ctl_table *ctl, int write,
- struct file* filp, void __user *buffer,
+ struct file *filp, void __user *buffer,
size_t *lenp, loff_t *ppos)
{
int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
@@ -1334,7 +1327,7 @@ static int devinet_conf_sysctl(ctl_table *table,
}
static int devinet_sysctl_forward(ctl_table *ctl, int write,
- struct file* filp, void __user *buffer,
+ struct file *filp, void __user *buffer,
size_t *lenp, loff_t *ppos)
{
int *valp = ctl->data;
@@ -1363,7 +1356,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
}
int ipv4_doint_and_flush(ctl_table *ctl, int write,
- struct file* filp, void __user *buffer,
+ struct file *filp, void __user *buffer,
size_t *lenp, loff_t *ppos)
{
int *valp = ctl->data;
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 21515d4c49eb..18bb383ea393 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -413,15 +413,16 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
static void esp4_err(struct sk_buff *skb, u32 info)
{
- struct iphdr *iph = (struct iphdr*)skb->data;
- struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+(iph->ihl<<2));
+ struct net *net = dev_net(skb->dev);
+ struct iphdr *iph = (struct iphdr *)skb->data;
+ struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
struct xfrm_state *x;
if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
return;
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
+ x = xfrm_state_lookup(net, (xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
if (!x)
return;
NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
@@ -618,6 +619,7 @@ static struct net_protocol esp4_protocol = {
.handler = xfrm4_rcv,
.err_handler = esp4_err,
.no_policy = 1,
+ .netns_ok = 1,
};
static int __init esp4_init(void)
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 65c1503f8cc8..741e4fa3e474 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -578,7 +578,7 @@ errout:
return err;
}
-static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct fib_config cfg;
@@ -600,7 +600,7 @@ errout:
return err;
}
-static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct net *net = sock_net(skb->sk);
struct fib_config cfg;
@@ -903,7 +903,7 @@ static void fib_disable_ip(struct net_device *dev, int force)
static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
{
- struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct net_device *dev = ifa->ifa_dev->dev;
switch (event) {
@@ -964,11 +964,11 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
}
static struct notifier_block fib_inetaddr_notifier = {
- .notifier_call =fib_inetaddr_event,
+ .notifier_call = fib_inetaddr_event,
};
static struct notifier_block fib_netdev_notifier = {
- .notifier_call =fib_netdev_event,
+ .notifier_call = fib_netdev_event,
};
static int __net_init ip_fib_net_init(struct net *net)
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c
index c8cac6c7f881..ded8c44fb848 100644
--- a/net/ipv4/fib_hash.c
+++ b/net/ipv4/fib_hash.c
@@ -247,7 +247,7 @@ fn_hash_lookup(struct fib_table *tb, const struct flowi *flp, struct fib_result
{
int err;
struct fn_zone *fz;
- struct fn_hash *t = (struct fn_hash*)tb->tb_data;
+ struct fn_hash *t = (struct fn_hash *)tb->tb_data;
read_lock(&fib_hash_lock);
for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
@@ -283,7 +283,7 @@ fn_hash_select_default(struct fib_table *tb, const struct flowi *flp, struct fib
struct fib_node *f;
struct fib_info *fi = NULL;
struct fib_info *last_resort;
- struct fn_hash *t = (struct fn_hash*)tb->tb_data;
+ struct fn_hash *t = (struct fn_hash *)tb->tb_data;
struct fn_zone *fz = t->fn_zones[0];
if (fz == NULL)
@@ -548,7 +548,7 @@ out:
static int fn_hash_delete(struct fib_table *tb, struct fib_config *cfg)
{
- struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fn_hash *table = (struct fn_hash *)tb->tb_data;
struct fib_node *f;
struct fib_alias *fa, *fa_to_delete;
struct fn_zone *fz;
@@ -748,7 +748,7 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
{
int m, s_m;
struct fn_zone *fz;
- struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fn_hash *table = (struct fn_hash *)tb->tb_data;
s_m = cb->args[2];
read_lock(&fib_hash_lock);
@@ -845,10 +845,10 @@ static struct fib_alias *fib_get_first(struct seq_file *seq)
struct hlist_node *node;
struct fib_node *fn;
- hlist_for_each_entry(fn,node,iter->hash_head,fn_hash) {
+ hlist_for_each_entry(fn, node, iter->hash_head, fn_hash) {
struct fib_alias *fa;
- list_for_each_entry(fa,&fn->fn_alias,fa_list) {
+ list_for_each_entry(fa, &fn->fn_alias, fa_list) {
iter->fn = fn;
iter->fa = fa;
goto out;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index ded2ae34eab1..4817dea3bc73 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -63,16 +63,16 @@ static DEFINE_SPINLOCK(fib_multipath_lock);
for (nhsel=0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
#define change_nexthops(fi) { int nhsel; struct fib_nh * nh; \
-for (nhsel=0, nh = (struct fib_nh*)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
+for (nhsel=0, nh = (struct fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
#else /* CONFIG_IP_ROUTE_MULTIPATH */
/* Hope, that gcc will optimize it to get rid of dummy loop */
-#define for_nexthops(fi) { int nhsel=0; const struct fib_nh * nh = (fi)->fib_nh; \
+#define for_nexthops(fi) { int nhsel = 0; const struct fib_nh * nh = (fi)->fib_nh; \
for (nhsel=0; nhsel < 1; nhsel++)
-#define change_nexthops(fi) { int nhsel=0; struct fib_nh * nh = (struct fib_nh*)((fi)->fib_nh); \
+#define change_nexthops(fi) { int nhsel = 0; struct fib_nh * nh = (struct fib_nh *)((fi)->fib_nh); \
for (nhsel=0; nhsel < 1; nhsel++)
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
@@ -358,7 +358,7 @@ int fib_detect_death(struct fib_info *fi, int order,
state = n->nud_state;
neigh_release(n);
}
- if (state==NUD_REACHABLE)
+ if (state == NUD_REACHABLE)
return 0;
if ((state&NUD_VALID) && order != dflt)
return 0;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 5cb72786a8af..ec0ae490f0b6 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2399,8 +2399,8 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
__be32 prf = htonl(mask_pfx(tn->key, tn->pos));
seq_indent(seq, iter->depth-1);
- seq_printf(seq, " +-- " NIPQUAD_FMT "/%d %d %d %d\n",
- NIPQUAD(prf), tn->pos, tn->bits, tn->full_children,
+ seq_printf(seq, " +-- %pI4/%d %d %d %d\n",
+ &prf, tn->pos, tn->bits, tn->full_children,
tn->empty_children);
} else {
@@ -2410,7 +2410,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
__be32 val = htonl(l->key);
seq_indent(seq, iter->depth);
- seq_printf(seq, " |-- " NIPQUAD_FMT "\n", NIPQUAD(val));
+ seq_printf(seq, " |-- %pI4\n", &val);
hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
struct fib_alias *fa;
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 72b2de76f1cd..705b33b184a3 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -321,12 +321,12 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd,
}
static void icmp_push_reply(struct icmp_bxm *icmp_param,
- struct ipcm_cookie *ipc, struct rtable *rt)
+ struct ipcm_cookie *ipc, struct rtable **rt)
{
struct sock *sk;
struct sk_buff *skb;
- sk = icmp_sk(dev_net(rt->u.dst.dev));
+ sk = icmp_sk(dev_net((*rt)->u.dst.dev));
if (ip_append_data(sk, icmp_glue_bits, icmp_param,
icmp_param->data_len+icmp_param->head_len,
icmp_param->head_len,
@@ -392,7 +392,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
}
if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type,
icmp_param->data.icmph.code))
- icmp_push_reply(icmp_param, &ipc, rt);
+ icmp_push_reply(icmp_param, &ipc, &rt);
ip_rt_put(rt);
out_unlock:
icmp_xmit_unlock(sk);
@@ -562,7 +562,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
/* No need to clone since we're just using its address. */
rt2 = rt;
- err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
+ err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0);
switch (err) {
case 0:
if (rt != rt2)
@@ -601,7 +601,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
if (err)
goto relookup_failed;
- err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
+ err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
switch (err) {
case 0:
@@ -635,7 +635,7 @@ route_done:
icmp_param.data_len = room;
icmp_param.head_len = sizeof(struct icmphdr);
- icmp_push_reply(&icmp_param, &ipc, rt);
+ icmp_push_reply(&icmp_param, &ipc, &rt);
ende:
ip_rt_put(rt);
out_unlock:
@@ -683,10 +683,8 @@ static void icmp_unreach(struct sk_buff *skb)
break;
case ICMP_FRAG_NEEDED:
if (ipv4_config.no_pmtu_disc) {
- LIMIT_NETDEBUG(KERN_INFO "ICMP: " NIPQUAD_FMT ": "
- "fragmentation needed "
- "and DF set.\n",
- NIPQUAD(iph->daddr));
+ LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: fragmentation needed and DF set.\n",
+ &iph->daddr);
} else {
info = ip_rt_frag_needed(net, iph,
ntohs(icmph->un.frag.mtu),
@@ -696,9 +694,8 @@ static void icmp_unreach(struct sk_buff *skb)
}
break;
case ICMP_SR_FAILED:
- LIMIT_NETDEBUG(KERN_INFO "ICMP: " NIPQUAD_FMT ": Source "
- "Route Failed.\n",
- NIPQUAD(iph->daddr));
+ LIMIT_NETDEBUG(KERN_INFO "ICMP: %pI4: Source Route Failed.\n",
+ &iph->daddr);
break;
default:
break;
@@ -729,12 +726,12 @@ static void icmp_unreach(struct sk_buff *skb)
if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {
if (net_ratelimit())
- printk(KERN_WARNING NIPQUAD_FMT " sent an invalid ICMP "
+ printk(KERN_WARNING "%pI4 sent an invalid ICMP "
"type %u, code %u "
- "error to a broadcast: " NIPQUAD_FMT " on %s\n",
- NIPQUAD(ip_hdr(skb)->saddr),
+ "error to a broadcast: %pI4 on %s\n",
+ &ip_hdr(skb)->saddr,
icmph->type, icmph->code,
- NIPQUAD(iph->daddr),
+ &iph->daddr,
skb->dev->name);
goto out;
}
@@ -952,9 +949,8 @@ static void icmp_address_reply(struct sk_buff *skb)
break;
}
if (!ifa && net_ratelimit()) {
- printk(KERN_INFO "Wrong address mask " NIPQUAD_FMT " from "
- "%s/" NIPQUAD_FMT "\n",
- NIPQUAD(*mp), dev->name, NIPQUAD(rt->rt_src));
+ printk(KERN_INFO "Wrong address mask %pI4 from %s/%pI4\n",
+ mp, dev->name, &rt->rt_src);
}
}
rcu_read_unlock();
@@ -976,9 +972,10 @@ int icmp_rcv(struct sk_buff *skb)
struct net *net = dev_net(rt->u.dst.dev);
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ struct sec_path *sp = skb_sec_path(skb);
int nh;
- if (!(skb->sp && skb->sp->xvec[skb->sp->len - 1]->props.flags &
+ if (!(sp && sp->xvec[sp->len - 1]->props.flags &
XFRM_STATE_ICMP))
goto drop;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a0d86455c53e..9eb6219af615 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -167,7 +167,7 @@ static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
spin_lock_bh(&im->lock);
if (del_timer(&im->timer))
atomic_dec(&im->refcnt);
- im->tm_running=0;
+ im->tm_running = 0;
im->reporter = 0;
im->unsolicit_count = 0;
spin_unlock_bh(&im->lock);
@@ -176,9 +176,9 @@ static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
/* It must be called with locked im->lock */
static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
{
- int tv=net_random() % max_delay;
+ int tv = net_random() % max_delay;
- im->tm_running=1;
+ im->tm_running = 1;
if (!mod_timer(&im->timer, jiffies+tv+2))
atomic_inc(&im->refcnt);
}
@@ -207,7 +207,7 @@ static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
if (del_timer(&im->timer)) {
if ((long)(im->timer.expires-jiffies) < max_delay) {
add_timer(&im->timer);
- im->tm_running=1;
+ im->tm_running = 1;
spin_unlock_bh(&im->lock);
return;
}
@@ -358,7 +358,7 @@ static int igmpv3_sendpack(struct sk_buff *skb)
static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
{
- return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc,type,gdel,sdel);
+ return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc, type, gdel, sdel);
}
static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
@@ -653,7 +653,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
return -1;
}
- skb=alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
+ skb = alloc_skb(IGMP_SIZE+LL_ALLOCATED_SPACE(dev), GFP_ATOMIC);
if (skb == NULL) {
ip_rt_put(rt);
return -1;
@@ -682,11 +682,11 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
((u8*)&iph[1])[3] = 0;
ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
- ih->type=type;
- ih->code=0;
- ih->csum=0;
- ih->group=group;
- ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
+ ih->type = type;
+ ih->code = 0;
+ ih->csum = 0;
+ ih->group = group;
+ ih->csum = ip_compute_csum((void *)ih, sizeof(struct igmphdr));
return ip_local_out(skb);
}
@@ -728,7 +728,7 @@ static void igmp_timer_expire(unsigned long data)
struct in_device *in_dev = im->interface;
spin_lock(&im->lock);
- im->tm_running=0;
+ im->tm_running = 0;
if (im->unsolicit_count) {
im->unsolicit_count--;
@@ -997,7 +997,7 @@ static void ip_mc_filter_add(struct in_device *in_dev, __be32 addr)
--ANK
*/
if (arp_mc_map(addr, buf, dev, 0) == 0)
- dev_mc_add(dev,buf,dev->addr_len,0);
+ dev_mc_add(dev, buf, dev->addr_len, 0);
}
/*
@@ -1010,7 +1010,7 @@ static void ip_mc_filter_del(struct in_device *in_dev, __be32 addr)
struct net_device *dev = in_dev->dev;
if (arp_mc_map(addr, buf, dev, 0) == 0)
- dev_mc_delete(dev,buf,dev->addr_len,0);
+ dev_mc_delete(dev, buf, dev->addr_len, 0);
}
#ifdef CONFIG_IP_MULTICAST
@@ -1210,10 +1210,10 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
if (!im)
goto out;
- im->users=1;
- im->interface=in_dev;
+ im->users = 1;
+ im->interface = in_dev;
in_dev_hold(in_dev);
- im->multiaddr=addr;
+ im->multiaddr = addr;
/* initial mode is (EX, empty) */
im->sfmode = MCAST_EXCLUDE;
im->sfcount[MCAST_INCLUDE] = 0;
@@ -1224,7 +1224,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
atomic_set(&im->refcnt, 1);
spin_lock_init(&im->lock);
#ifdef CONFIG_IP_MULTICAST
- im->tm_running=0;
+ im->tm_running = 0;
setup_timer(&im->timer, &igmp_timer_expire, (unsigned long)im);
im->unsolicit_count = IGMP_Unsolicited_Report_Count;
im->reporter = 0;
@@ -1232,8 +1232,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
#endif
im->loaded = 0;
write_lock_bh(&in_dev->mc_list_lock);
- im->next=in_dev->mc_list;
- in_dev->mc_list=im;
+ im->next = in_dev->mc_list;
+ in_dev->mc_list = im;
in_dev->mc_count++;
write_unlock_bh(&in_dev->mc_list_lock);
#ifdef CONFIG_IP_MULTICAST
@@ -1279,7 +1279,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr)
ASSERT_RTNL();
for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
- if (i->multiaddr==addr) {
+ if (i->multiaddr == addr) {
if (--i->users == 0) {
write_lock_bh(&in_dev->mc_list_lock);
*ip = i->next;
@@ -1738,7 +1738,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
{
int err;
__be32 addr = imr->imr_multiaddr.s_addr;
- struct ip_mc_socklist *iml=NULL, *i;
+ struct ip_mc_socklist *iml = NULL, *i;
struct in_device *in_dev;
struct inet_sock *inet = inet_sk(sk);
struct net *net = sock_net(sk);
@@ -1769,7 +1769,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
err = -ENOBUFS;
if (count >= sysctl_igmp_max_memberships)
goto done;
- iml = sock_kmalloc(sk,sizeof(*iml),GFP_KERNEL);
+ iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
if (iml == NULL)
goto done;
@@ -2275,6 +2275,7 @@ int ip_check_mc(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 p
#if defined(CONFIG_PROC_FS)
struct igmp_mc_iter_state {
+ struct seq_net_private p;
struct net_device *dev;
struct in_device *in_dev;
};
@@ -2283,11 +2284,12 @@ struct igmp_mc_iter_state {
static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq)
{
+ struct net *net = seq_file_net(seq);
struct ip_mc_list *im = NULL;
struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq);
state->in_dev = NULL;
- for_each_netdev(&init_net, state->dev) {
+ for_each_netdev(net, state->dev) {
struct in_device *in_dev;
in_dev = in_dev_get(state->dev);
if (!in_dev)
@@ -2408,7 +2410,7 @@ static const struct seq_operations igmp_mc_seq_ops = {
static int igmp_mc_seq_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &igmp_mc_seq_ops,
+ return seq_open_net(inode, file, &igmp_mc_seq_ops,
sizeof(struct igmp_mc_iter_state));
}
@@ -2417,10 +2419,11 @@ static const struct file_operations igmp_mc_seq_fops = {
.open = igmp_mc_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
struct igmp_mcf_iter_state {
+ struct seq_net_private p;
struct net_device *dev;
struct in_device *idev;
struct ip_mc_list *im;
@@ -2430,13 +2433,14 @@ struct igmp_mcf_iter_state {
static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
{
+ struct net *net = seq_file_net(seq);
struct ip_sf_list *psf = NULL;
struct ip_mc_list *im = NULL;
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
state->idev = NULL;
state->im = NULL;
- for_each_netdev(&init_net, state->dev) {
+ for_each_netdev(net, state->dev) {
struct in_device *idev;
idev = in_dev_get(state->dev);
if (unlikely(idev == NULL))
@@ -2567,7 +2571,7 @@ static const struct seq_operations igmp_mcf_seq_ops = {
static int igmp_mcf_seq_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &igmp_mcf_seq_ops,
+ return seq_open_net(inode, file, &igmp_mcf_seq_ops,
sizeof(struct igmp_mcf_iter_state));
}
@@ -2576,14 +2580,41 @@ static const struct file_operations igmp_mcf_seq_fops = {
.open = igmp_mcf_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
-int __init igmp_mc_proc_init(void)
+static int igmp_net_init(struct net *net)
{
- proc_net_fops_create(&init_net, "igmp", S_IRUGO, &igmp_mc_seq_fops);
- proc_net_fops_create(&init_net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops);
+ struct proc_dir_entry *pde;
+
+ pde = proc_net_fops_create(net, "igmp", S_IRUGO, &igmp_mc_seq_fops);
+ if (!pde)
+ goto out_igmp;
+ pde = proc_net_fops_create(net, "mcfilter", S_IRUGO, &igmp_mcf_seq_fops);
+ if (!pde)
+ goto out_mcfilter;
return 0;
+
+out_mcfilter:
+ proc_net_remove(net, "igmp");
+out_igmp:
+ return -ENOMEM;
+}
+
+static void igmp_net_exit(struct net *net)
+{
+ proc_net_remove(net, "mcfilter");
+ proc_net_remove(net, "igmp");
+}
+
+static struct pernet_operations igmp_net_ops = {
+ .init = igmp_net_init,
+ .exit = igmp_net_exit,
+};
+
+int __init igmp_mc_proc_init(void)
+{
+ return register_pernet_subsys(&igmp_net_ops);
}
#endif
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index bd1278a2d828..f26ab38680de 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -109,7 +109,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
hashinfo->bhash_size)];
spin_lock(&head->lock);
inet_bind_bucket_for_each(tb, node, &head->chain)
- if (tb->ib_net == net && tb->port == rover)
+ if (ib_net(tb) == net && tb->port == rover)
goto next;
break;
next:
@@ -137,7 +137,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum)
hashinfo->bhash_size)];
spin_lock(&head->lock);
inet_bind_bucket_for_each(tb, node, &head->chain)
- if (tb->ib_net == net && tb->port == snum)
+ if (ib_net(tb) == net && tb->port == snum)
goto tb_found;
}
tb = NULL;
@@ -323,7 +323,7 @@ void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len)
EXPORT_SYMBOL(inet_csk_reset_keepalive_timer);
-struct dst_entry* inet_csk_route_req(struct sock *sk,
+struct dst_entry *inet_csk_route_req(struct sock *sk,
const struct request_sock *req)
{
struct rtable *rt;
@@ -344,16 +344,17 @@ struct dst_entry* inet_csk_route_req(struct sock *sk,
struct net *net = sock_net(sk);
security_req_classify_flow(req, &fl);
- if (ip_route_output_flow(net, &rt, &fl, sk, 0)) {
- IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
- return NULL;
- }
- if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
- ip_rt_put(rt);
- IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
- return NULL;
- }
+ if (ip_route_output_flow(net, &rt, &fl, sk, 0))
+ goto no_route;
+ if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+ goto route_err;
return &rt->u.dst;
+
+route_err:
+ ip_rt_put(rt);
+no_route:
+ IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
+ return NULL;
}
EXPORT_SYMBOL_GPL(inet_csk_route_req);
@@ -561,7 +562,7 @@ void inet_csk_destroy_sock(struct sock *sk)
sk_refcnt_debug_release(sk);
- atomic_dec(sk->sk_prot->orphan_count);
+ percpu_counter_dec(sk->sk_prot->orphan_count);
sock_put(sk);
}
@@ -641,7 +642,7 @@ void inet_csk_listen_stop(struct sock *sk)
sock_orphan(child);
- atomic_inc(sk->sk_prot->orphan_count);
+ percpu_counter_inc(sk->sk_prot->orphan_count);
inet_csk_destroy_sock(child);
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 564230dabcb8..588a7796e3e3 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -718,13 +718,15 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV)))
goto skip_listen_ht;
- inet_listen_lock(hashinfo);
for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
struct sock *sk;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
+ struct inet_listen_hashbucket *ilb;
num = 0;
- sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
+ ilb = &hashinfo->listening_hash[i];
+ spin_lock_bh(&ilb->lock);
+ sk_nulls_for_each(sk, node, &ilb->head) {
struct inet_sock *inet = inet_sk(sk);
if (num < s_num) {
@@ -742,7 +744,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
goto syn_recv;
if (inet_csk_diag_dump(sk, skb, cb) < 0) {
- inet_listen_unlock(hashinfo);
+ spin_unlock_bh(&ilb->lock);
goto done;
}
@@ -751,7 +753,7 @@ syn_recv:
goto next_listen;
if (inet_diag_dump_reqs(skb, sk, cb) < 0) {
- inet_listen_unlock(hashinfo);
+ spin_unlock_bh(&ilb->lock);
goto done;
}
@@ -760,12 +762,12 @@ next_listen:
cb->args[4] = 0;
++num;
}
+ spin_unlock_bh(&ilb->lock);
s_num = 0;
cb->args[3] = 0;
cb->args[4] = 0;
}
- inet_listen_unlock(hashinfo);
skip_listen_ht:
cb->args[0] = 1;
s_i = num = s_num = 0;
@@ -776,20 +778,21 @@ skip_listen_ht:
for (i = s_i; i < hashinfo->ehash_size; i++) {
struct inet_ehash_bucket *head = &hashinfo->ehash[i];
- rwlock_t *lock = inet_ehash_lockp(hashinfo, i);
+ spinlock_t *lock = inet_ehash_lockp(hashinfo, i);
struct sock *sk;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
num = 0;
- if (hlist_empty(&head->chain) && hlist_empty(&head->twchain))
+ if (hlist_nulls_empty(&head->chain) &&
+ hlist_nulls_empty(&head->twchain))
continue;
if (i > s_i)
s_num = 0;
- read_lock_bh(lock);
- sk_for_each(sk, node, &head->chain) {
+ spin_lock_bh(lock);
+ sk_nulls_for_each(sk, node, &head->chain) {
struct inet_sock *inet = inet_sk(sk);
if (num < s_num)
@@ -803,7 +806,7 @@ skip_listen_ht:
r->id.idiag_dport)
goto next_normal;
if (inet_csk_diag_dump(sk, skb, cb) < 0) {
- read_unlock_bh(lock);
+ spin_unlock_bh(lock);
goto done;
}
next_normal:
@@ -825,14 +828,14 @@ next_normal:
r->id.idiag_dport)
goto next_dying;
if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
- read_unlock_bh(lock);
+ spin_unlock_bh(lock);
goto done;
}
next_dying:
++num;
}
}
- read_unlock_bh(lock);
+ spin_unlock_bh(lock);
}
done:
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 44981906fb91..6a1045da48d2 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -35,7 +35,7 @@ struct inet_bind_bucket *inet_bind_bucket_create(struct kmem_cache *cachep,
struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
if (tb != NULL) {
- tb->ib_net = hold_net(net);
+ write_pnet(&tb->ib_net, hold_net(net));
tb->port = snum;
tb->fastreuse = 0;
INIT_HLIST_HEAD(&tb->owners);
@@ -51,7 +51,7 @@ void inet_bind_bucket_destroy(struct kmem_cache *cachep, struct inet_bind_bucket
{
if (hlist_empty(&tb->owners)) {
__hlist_del(&tb->node);
- release_net(tb->ib_net);
+ release_net(ib_net(tb));
kmem_cache_free(cachep, tb);
}
}
@@ -110,33 +110,29 @@ void __inet_inherit_port(struct sock *sk, struct sock *child)
EXPORT_SYMBOL_GPL(__inet_inherit_port);
-/*
- * This lock without WQ_FLAG_EXCLUSIVE is good on UP and it can be very bad on SMP.
- * Look, when several writers sleep and reader wakes them up, all but one
- * immediately hit write lock and grab all the cpus. Exclusive sleep solves
- * this, _but_ remember, it adds useless work on UP machines (wake up each
- * exclusive lock release). It should be ifdefed really.
- */
-void inet_listen_wlock(struct inet_hashinfo *hashinfo)
- __acquires(hashinfo->lhash_lock)
+static inline int compute_score(struct sock *sk, struct net *net,
+ const unsigned short hnum, const __be32 daddr,
+ const int dif)
{
- write_lock(&hashinfo->lhash_lock);
-
- if (atomic_read(&hashinfo->lhash_users)) {
- DEFINE_WAIT(wait);
+ int score = -1;
+ struct inet_sock *inet = inet_sk(sk);
- for (;;) {
- prepare_to_wait_exclusive(&hashinfo->lhash_wait,
- &wait, TASK_UNINTERRUPTIBLE);
- if (!atomic_read(&hashinfo->lhash_users))
- break;
- write_unlock_bh(&hashinfo->lhash_lock);
- schedule();
- write_lock_bh(&hashinfo->lhash_lock);
+ if (net_eq(sock_net(sk), net) && inet->num == hnum &&
+ !ipv6_only_sock(sk)) {
+ __be32 rcv_saddr = inet->rcv_saddr;
+ score = sk->sk_family == PF_INET ? 1 : 0;
+ if (rcv_saddr) {
+ if (rcv_saddr != daddr)
+ return -1;
+ score += 2;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score += 2;
}
-
- finish_wait(&hashinfo->lhash_wait, &wait);
}
+ return score;
}
/*
@@ -145,72 +141,48 @@ void inet_listen_wlock(struct inet_hashinfo *hashinfo)
* remote address for the connection. So always assume those are both
* wildcarded during the search since they can never be otherwise.
*/
-static struct sock *inet_lookup_listener_slow(struct net *net,
- const struct hlist_head *head,
- const __be32 daddr,
- const unsigned short hnum,
- const int dif)
-{
- struct sock *result = NULL, *sk;
- const struct hlist_node *node;
- int hiscore = -1;
-
- sk_for_each(sk, node, head) {
- const struct inet_sock *inet = inet_sk(sk);
-
- if (net_eq(sock_net(sk), net) && inet->num == hnum &&
- !ipv6_only_sock(sk)) {
- const __be32 rcv_saddr = inet->rcv_saddr;
- int score = sk->sk_family == PF_INET ? 1 : 0;
-
- if (rcv_saddr) {
- if (rcv_saddr != daddr)
- continue;
- score += 2;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- continue;
- score += 2;
- }
- if (score == 5)
- return sk;
- if (score > hiscore) {
- hiscore = score;
- result = sk;
- }
- }
- }
- return result;
-}
-/* Optimize the common listener case. */
+
struct sock *__inet_lookup_listener(struct net *net,
struct inet_hashinfo *hashinfo,
const __be32 daddr, const unsigned short hnum,
const int dif)
{
- struct sock *sk = NULL;
- const struct hlist_head *head;
-
- read_lock(&hashinfo->lhash_lock);
- head = &hashinfo->listening_hash[inet_lhashfn(net, hnum)];
- if (!hlist_empty(head)) {
- const struct inet_sock *inet = inet_sk((sk = __sk_head(head)));
-
- if (inet->num == hnum && !sk->sk_node.next &&
- (!inet->rcv_saddr || inet->rcv_saddr == daddr) &&
- (sk->sk_family == PF_INET || !ipv6_only_sock(sk)) &&
- !sk->sk_bound_dev_if && net_eq(sock_net(sk), net))
- goto sherry_cache;
- sk = inet_lookup_listener_slow(net, head, daddr, hnum, dif);
+ struct sock *sk, *result;
+ struct hlist_nulls_node *node;
+ unsigned int hash = inet_lhashfn(net, hnum);
+ struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
+ int score, hiscore;
+
+ rcu_read_lock();
+begin:
+ result = NULL;
+ hiscore = -1;
+ sk_nulls_for_each_rcu(sk, node, &ilb->head) {
+ score = compute_score(sk, net, hnum, daddr, dif);
+ if (score > hiscore) {
+ result = sk;
+ hiscore = score;
+ }
}
- if (sk) {
-sherry_cache:
- sock_hold(sk);
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE)
+ goto begin;
+ if (result) {
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score(result, net, hnum, daddr,
+ dif) < hiscore)) {
+ sock_put(result);
+ goto begin;
+ }
}
- read_unlock(&hashinfo->lhash_lock);
- return sk;
+ rcu_read_unlock();
+ return result;
}
EXPORT_SYMBOL_GPL(__inet_lookup_listener);
@@ -223,35 +195,65 @@ struct sock * __inet_lookup_established(struct net *net,
INET_ADDR_COOKIE(acookie, saddr, daddr)
const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
struct sock *sk;
- const struct hlist_node *node;
+ const struct hlist_nulls_node *node;
/* Optimize here for direct hit, only listening connections can
* have wildcards anyways.
*/
unsigned int hash = inet_ehashfn(net, daddr, hnum, saddr, sport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash);
- rwlock_t *lock = inet_ehash_lockp(hashinfo, hash);
+ unsigned int slot = hash & (hashinfo->ehash_size - 1);
+ struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
- prefetch(head->chain.first);
- read_lock(lock);
- sk_for_each(sk, node, &head->chain) {
+ rcu_read_lock();
+begin:
+ sk_nulls_for_each_rcu(sk, node, &head->chain) {
if (INET_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif))
- goto hit; /* You sunk my battleship! */
+ saddr, daddr, ports, dif)) {
+ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
+ goto begintw;
+ if (unlikely(!INET_MATCH(sk, net, hash, acookie,
+ saddr, daddr, ports, dif))) {
+ sock_put(sk);
+ goto begin;
+ }
+ goto out;
+ }
}
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != slot)
+ goto begin;
+begintw:
/* Must check for a TIME_WAIT'er before going to listener hash. */
- sk_for_each(sk, node, &head->twchain) {
+ sk_nulls_for_each_rcu(sk, node, &head->twchain) {
if (INET_TW_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif))
- goto hit;
+ saddr, daddr, ports, dif)) {
+ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
+ sk = NULL;
+ goto out;
+ }
+ if (unlikely(!INET_TW_MATCH(sk, net, hash, acookie,
+ saddr, daddr, ports, dif))) {
+ sock_put(sk);
+ goto begintw;
+ }
+ goto out;
+ }
}
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != slot)
+ goto begintw;
sk = NULL;
out:
- read_unlock(lock);
+ rcu_read_unlock();
return sk;
-hit:
- sock_hold(sk);
- goto out;
}
EXPORT_SYMBOL_GPL(__inet_lookup_established);
@@ -270,16 +272,15 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
struct net *net = sock_net(sk);
unsigned int hash = inet_ehashfn(net, daddr, lport, saddr, inet->dport);
struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
- rwlock_t *lock = inet_ehash_lockp(hinfo, hash);
+ spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
struct sock *sk2;
- const struct hlist_node *node;
+ const struct hlist_nulls_node *node;
struct inet_timewait_sock *tw;
- prefetch(head->chain.first);
- write_lock(lock);
+ spin_lock(lock);
/* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &head->twchain) {
+ sk_nulls_for_each(sk2, node, &head->twchain) {
tw = inet_twsk(sk2);
if (INET_TW_MATCH(sk2, net, hash, acookie,
@@ -293,7 +294,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
tw = NULL;
/* And established part... */
- sk_for_each(sk2, node, &head->chain) {
+ sk_nulls_for_each(sk2, node, &head->chain) {
if (INET_MATCH(sk2, net, hash, acookie,
saddr, daddr, ports, dif))
goto not_unique;
@@ -306,9 +307,9 @@ unique:
inet->sport = htons(lport);
sk->sk_hash = hash;
WARN_ON(!sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
+ __sk_nulls_add_node_rcu(sk, &head->chain);
+ spin_unlock(lock);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
- write_unlock(lock);
if (twp) {
*twp = tw;
@@ -324,7 +325,7 @@ unique:
return 0;
not_unique:
- write_unlock(lock);
+ spin_unlock(lock);
return -EADDRNOTAVAIL;
}
@@ -338,8 +339,8 @@ static inline u32 inet_sk_port_offset(const struct sock *sk)
void __inet_hash_nolisten(struct sock *sk)
{
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
- struct hlist_head *list;
- rwlock_t *lock;
+ struct hlist_nulls_head *list;
+ spinlock_t *lock;
struct inet_ehash_bucket *head;
WARN_ON(!sk_unhashed(sk));
@@ -349,18 +350,17 @@ void __inet_hash_nolisten(struct sock *sk)
list = &head->chain;
lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
- write_lock(lock);
- __sk_add_node(sk, list);
+ spin_lock(lock);
+ __sk_nulls_add_node_rcu(sk, list);
+ spin_unlock(lock);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
- write_unlock(lock);
}
EXPORT_SYMBOL_GPL(__inet_hash_nolisten);
static void __inet_hash(struct sock *sk)
{
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
- struct hlist_head *list;
- rwlock_t *lock;
+ struct inet_listen_hashbucket *ilb;
if (sk->sk_state != TCP_LISTEN) {
__inet_hash_nolisten(sk);
@@ -368,14 +368,12 @@ static void __inet_hash(struct sock *sk)
}
WARN_ON(!sk_unhashed(sk));
- list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
- lock = &hashinfo->lhash_lock;
+ ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
- inet_listen_wlock(hashinfo);
- __sk_add_node(sk, list);
+ spin_lock(&ilb->lock);
+ __sk_nulls_add_node_rcu(sk, &ilb->head);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
- write_unlock(lock);
- wake_up(&hashinfo->lhash_wait);
+ spin_unlock(&ilb->lock);
}
void inet_hash(struct sock *sk)
@@ -390,27 +388,23 @@ EXPORT_SYMBOL_GPL(inet_hash);
void inet_unhash(struct sock *sk)
{
- rwlock_t *lock;
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
+ spinlock_t *lock;
+ int done;
if (sk_unhashed(sk))
- goto out;
+ return;
- if (sk->sk_state == TCP_LISTEN) {
- local_bh_disable();
- inet_listen_wlock(hashinfo);
- lock = &hashinfo->lhash_lock;
- } else {
+ if (sk->sk_state == TCP_LISTEN)
+ lock = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)].lock;
+ else
lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
- write_lock_bh(lock);
- }
- if (__sk_del_node_init(sk))
+ spin_lock_bh(lock);
+ done =__sk_nulls_del_node_init_rcu(sk);
+ if (done)
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
- write_unlock_bh(lock);
-out:
- if (sk->sk_state == TCP_LISTEN)
- wake_up(&hashinfo->lhash_wait);
+ spin_unlock_bh(lock);
}
EXPORT_SYMBOL_GPL(inet_unhash);
@@ -449,7 +443,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
* unique enough.
*/
inet_bind_bucket_for_each(tb, node, &head->chain) {
- if (tb->ib_net == net && tb->port == port) {
+ if (ib_net(tb) == net && tb->port == port) {
WARN_ON(hlist_empty(&tb->owners));
if (tb->fastreuse >= 0)
goto next_port;
@@ -524,3 +518,16 @@ int inet_hash_connect(struct inet_timewait_death_row *death_row,
}
EXPORT_SYMBOL_GPL(inet_hash_connect);
+
+void inet_hashinfo_init(struct inet_hashinfo *h)
+{
+ int i;
+
+ for (i = 0; i < INET_LHTABLE_SIZE; i++) {
+ spin_lock_init(&h->listening_hash[i].lock);
+ INIT_HLIST_NULLS_HEAD(&h->listening_hash[i].head,
+ i + LISTENING_NULLS_BASE);
+ }
+}
+
+EXPORT_SYMBOL_GPL(inet_hashinfo_init);
diff --git a/net/ipv4/inet_lro.c b/net/ipv4/inet_lro.c
index cfd034a2b96e..6a667dae315e 100644
--- a/net/ipv4/inet_lro.c
+++ b/net/ipv4/inet_lro.c
@@ -120,7 +120,7 @@ static void lro_update_tcp_ip_header(struct net_lro_desc *lro_desc)
iph->check = ip_fast_csum((u8 *)lro_desc->iph, iph->ihl);
tcph->check = 0;
- tcp_hdr_csum = csum_partial((u8 *)tcph, TCP_HDR_LEN(tcph), 0);
+ tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), 0);
lro_desc->data_csum = csum_add(lro_desc->data_csum, tcp_hdr_csum);
tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
lro_desc->ip_tot_len -
@@ -135,7 +135,7 @@ static __wsum lro_tcp_data_csum(struct iphdr *iph, struct tcphdr *tcph, int len)
__wsum tcp_ps_hdr_csum;
tcp_csum = ~csum_unfold(tcph->check);
- tcp_hdr_csum = csum_partial((u8 *)tcph, TCP_HDR_LEN(tcph), tcp_csum);
+ tcp_hdr_csum = csum_partial(tcph, TCP_HDR_LEN(tcph), tcp_csum);
tcp_ps_hdr_csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
len + TCP_HDR_LEN(tcph),
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 1c5fd38f8824..8554d0ea1719 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -20,16 +20,16 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw,
struct inet_bind_hashbucket *bhead;
struct inet_bind_bucket *tb;
/* Unlink from established hashes. */
- rwlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash);
+ spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash);
- write_lock(lock);
- if (hlist_unhashed(&tw->tw_node)) {
- write_unlock(lock);
+ spin_lock(lock);
+ if (hlist_nulls_unhashed(&tw->tw_node)) {
+ spin_unlock(lock);
return;
}
- __hlist_del(&tw->tw_node);
- sk_node_init(&tw->tw_node);
- write_unlock(lock);
+ hlist_nulls_del_rcu(&tw->tw_node);
+ sk_nulls_node_init(&tw->tw_node);
+ spin_unlock(lock);
/* Disassociate with bind bucket. */
bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), tw->tw_num,
@@ -76,7 +76,7 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
const struct inet_sock *inet = inet_sk(sk);
const struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_ehash_bucket *ehead = inet_ehash_bucket(hashinfo, sk->sk_hash);
- rwlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
+ spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
struct inet_bind_hashbucket *bhead;
/* Step 1: Put TW into bind hash. Original socket stays there too.
Note, that any socket with inet->num != 0 MUST be bound in
@@ -90,17 +90,21 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
spin_unlock(&bhead->lock);
- write_lock(lock);
+ spin_lock(lock);
- /* Step 2: Remove SK from established hash. */
- if (__sk_del_node_init(sk))
- sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
-
- /* Step 3: Hash TW into TIMEWAIT chain. */
- inet_twsk_add_node(tw, &ehead->twchain);
+ /*
+ * Step 2: Hash TW into TIMEWAIT chain.
+ * Should be done before removing sk from established chain
+ * because readers are lockless and search established first.
+ */
atomic_inc(&tw->tw_refcnt);
+ inet_twsk_add_node_rcu(tw, &ehead->twchain);
- write_unlock(lock);
+ /* Step 3: Remove SK from established hash. */
+ if (__sk_nulls_del_node_init_rcu(sk))
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+
+ spin_unlock(lock);
}
EXPORT_SYMBOL_GPL(__inet_twsk_hashdance);
@@ -416,17 +420,17 @@ void inet_twsk_purge(struct net *net, struct inet_hashinfo *hashinfo,
{
struct inet_timewait_sock *tw;
struct sock *sk;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
int h;
local_bh_disable();
for (h = 0; h < (hashinfo->ehash_size); h++) {
struct inet_ehash_bucket *head =
inet_ehash_bucket(hashinfo, h);
- rwlock_t *lock = inet_ehash_lockp(hashinfo, h);
+ spinlock_t *lock = inet_ehash_lockp(hashinfo, h);
restart:
- write_lock(lock);
- sk_for_each(sk, node, &head->twchain) {
+ spin_lock(lock);
+ sk_nulls_for_each(sk, node, &head->twchain) {
tw = inet_twsk(sk);
if (!net_eq(twsk_net(tw), net) ||
@@ -434,13 +438,13 @@ restart:
continue;
atomic_inc(&tw->tw_refcnt);
- write_unlock(lock);
+ spin_unlock(lock);
inet_twsk_deschedule(tw, twdr);
inet_twsk_put(tw);
goto restart;
}
- write_unlock(lock);
+ spin_unlock(lock);
}
local_bh_enable();
}
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index a456ceeac3f2..b1fbe18feb5a 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -144,7 +144,7 @@ static void unlink_from_unused(struct inet_peer *p)
* _stack is known to be NULL or not at compile time,
* so compiler will optimize the if (_stack) tests.
*/
-#define lookup(_daddr,_stack) \
+#define lookup(_daddr, _stack) \
({ \
struct inet_peer *u, **v; \
if (_stack != NULL) { \
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index 450016b89a18..df3fe50bbf0d 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -106,7 +106,7 @@ int ip_forward(struct sk_buff *skb)
* We now generate an ICMP HOST REDIRECT giving the route
* we calculated.
*/
- if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb->sp)
+ if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb))
ip_rt_send_redirect(skb);
skb->priority = rt_tos2priority(iph->tos);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index e4f81f54befe..6659ac000eeb 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -56,7 +56,7 @@ struct ipfrag_skb_cb
int offset;
};
-#define FRAG_CB(skb) ((struct ipfrag_skb_cb*)((skb)->cb))
+#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
/* Describe an entry in the "incomplete datagrams" queue. */
struct ipq {
@@ -559,9 +559,8 @@ out_nomem:
goto out_fail;
out_oversize:
if (net_ratelimit())
- printk(KERN_INFO
- "Oversized IP packet from " NIPQUAD_FMT ".\n",
- NIPQUAD(qp->saddr));
+ printk(KERN_INFO "Oversized IP packet from %pI4.\n",
+ &qp->saddr);
out_fail:
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_REASMFAILS);
return err;
@@ -608,7 +607,7 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
.data = &init_net.ipv4.frags.high_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_IPFRAG_LOW_THRESH,
@@ -616,7 +615,7 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
.data = &init_net.ipv4.frags.low_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_IPFRAG_TIME,
@@ -624,8 +623,8 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
.data = &init_net.ipv4.frags.timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{ }
};
@@ -637,15 +636,15 @@ static struct ctl_table ip4_frags_ctl_table[] = {
.data = &ip4_frags.secret_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.procname = "ipfrag_max_dist",
.data = &sysctl_ipfrag_max_dist,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &zero
},
{ }
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 85c487b8572b..0101521f366b 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -126,8 +126,6 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev);
/* Fallback tunnel: no source, no destination, no key, no options */
-static int ipgre_fb_tunnel_init(struct net_device *dev);
-
#define HASH_SIZE 16
static int ipgre_net_id;
@@ -371,7 +369,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
by themself???
*/
- struct iphdr *iph = (struct iphdr*)skb->data;
+ struct iphdr *iph = (struct iphdr *)skb->data;
__be16 *p = (__be16*)(skb->data+(iph->ihl<<2));
int grehlen = (iph->ihl<<2) + 4;
const int type = icmp_hdr(skb)->type;
@@ -632,7 +630,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
gre_hlen = 0;
- tiph = (struct iphdr*)skb->data;
+ tiph = (struct iphdr *)skb->data;
} else {
gre_hlen = tunnel->hlen;
tiph = &tunnel->parms.iph;
@@ -660,7 +658,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
if (neigh == NULL)
goto tx_error;
- addr6 = (struct in6_addr*)&neigh->primary_key;
+ addr6 = (struct in6_addr *)&neigh->primary_key;
addr_type = ipv6_addr_type(addr6);
if (addr_type == IPV6_ADDR_ANY) {
@@ -726,7 +724,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
}
#ifdef CONFIG_IPV6
else if (skb->protocol == htons(ETH_P_IPV6)) {
- struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
+ struct rt6_info *rt6 = (struct rt6_info *)skb->dst;
if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
if ((tunnel->parms.iph.daddr &&
@@ -800,7 +798,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
iph->ttl = old_iph->ttl;
#ifdef CONFIG_IPV6
else if (skb->protocol == htons(ETH_P_IPV6))
- iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;
+ iph->ttl = ((struct ipv6hdr *)old_iph)->hop_limit;
#endif
else
iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
@@ -962,7 +960,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
} else {
- unsigned nflags=0;
+ unsigned nflags = 0;
t = netdev_priv(dev);
@@ -1104,7 +1102,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
- struct iphdr *iph = (struct iphdr*) skb_mac_header(skb);
+ struct iphdr *iph = (struct iphdr *) skb_mac_header(skb);
memcpy(haddr, &iph->saddr, 4);
return 4;
}
@@ -1142,6 +1140,7 @@ static int ipgre_open(struct net_device *dev)
static int ipgre_close(struct net_device *dev)
{
struct ip_tunnel *t = netdev_priv(dev);
+
if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
struct in_device *in_dev;
in_dev = inetdev_by_index(dev_net(dev), t->mlink);
@@ -1155,14 +1154,22 @@ static int ipgre_close(struct net_device *dev)
#endif
+static const struct net_device_ops ipgre_netdev_ops = {
+ .ndo_init = ipgre_tunnel_init,
+ .ndo_uninit = ipgre_tunnel_uninit,
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+ .ndo_open = ipgre_open,
+ .ndo_stop = ipgre_close,
+#endif
+ .ndo_start_xmit = ipgre_tunnel_xmit,
+ .ndo_do_ioctl = ipgre_tunnel_ioctl,
+ .ndo_change_mtu = ipgre_tunnel_change_mtu,
+};
+
static void ipgre_tunnel_setup(struct net_device *dev)
{
- dev->init = ipgre_tunnel_init;
- dev->uninit = ipgre_tunnel_uninit;
+ dev->netdev_ops = &ipgre_netdev_ops;
dev->destructor = free_netdev;
- dev->hard_start_xmit = ipgre_tunnel_xmit;
- dev->do_ioctl = ipgre_tunnel_ioctl;
- dev->change_mtu = ipgre_tunnel_change_mtu;
dev->type = ARPHRD_IPGRE;
dev->needed_headroom = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
@@ -1194,8 +1201,6 @@ static int ipgre_tunnel_init(struct net_device *dev)
return -EINVAL;
dev->flags = IFF_BROADCAST;
dev->header_ops = &ipgre_header_ops;
- dev->open = ipgre_open;
- dev->stop = ipgre_close;
}
#endif
} else
@@ -1204,7 +1209,7 @@ static int ipgre_tunnel_init(struct net_device *dev)
return 0;
}
-static int ipgre_fb_tunnel_init(struct net_device *dev)
+static void ipgre_fb_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct iphdr *iph = &tunnel->parms.iph;
@@ -1220,7 +1225,6 @@ static int ipgre_fb_tunnel_init(struct net_device *dev)
dev_hold(dev);
ign->tunnels_wc[0] = tunnel;
- return 0;
}
@@ -1264,9 +1268,9 @@ static int ipgre_init_net(struct net *net)
err = -ENOMEM;
goto err_alloc_dev;
}
-
- ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init;
dev_net_set(ign->fb_tunnel_dev, net);
+
+ ipgre_fb_tunnel_init(ign->fb_tunnel_dev);
ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops;
if ((err = register_netdev(ign->fb_tunnel_dev)))
@@ -1397,16 +1401,22 @@ static int ipgre_tap_init(struct net_device *dev)
return 0;
}
+static const struct net_device_ops ipgre_tap_netdev_ops = {
+ .ndo_init = ipgre_tap_init,
+ .ndo_uninit = ipgre_tunnel_uninit,
+ .ndo_start_xmit = ipgre_tunnel_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = ipgre_tunnel_change_mtu,
+};
+
static void ipgre_tap_setup(struct net_device *dev)
{
ether_setup(dev);
- dev->init = ipgre_tap_init;
- dev->uninit = ipgre_tunnel_uninit;
+ dev->netdev_ops = &ipgre_netdev_ops;
dev->destructor = free_netdev;
- dev->hard_start_xmit = ipgre_tunnel_xmit;
- dev->change_mtu = ipgre_tunnel_change_mtu;
dev->iflink = 0;
dev->features |= NETIF_F_NETNS_LOCAL;
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index cfb38ac9d698..1a58a6fa1dc0 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -302,10 +302,8 @@ static inline int ip_rcv_options(struct sk_buff *skb)
if (!IN_DEV_SOURCE_ROUTE(in_dev)) {
if (IN_DEV_LOG_MARTIANS(in_dev) &&
net_ratelimit())
- printk(KERN_INFO "source route option "
- NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
- NIPQUAD(iph->saddr),
- NIPQUAD(iph->daddr));
+ printk(KERN_INFO "source route option %pI4 -> %pI4\n",
+ &iph->saddr, &iph->daddr);
in_dev_put(in_dev);
goto drop;
}
@@ -350,9 +348,9 @@ static int ip_rcv_finish(struct sk_buff *skb)
struct ip_rt_acct *st = per_cpu_ptr(ip_rt_acct, smp_processor_id());
u32 idx = skb->dst->tclassid;
st[idx&0xFF].o_packets++;
- st[idx&0xFF].o_bytes+=skb->len;
+ st[idx&0xFF].o_bytes += skb->len;
st[(idx>>16)&0xFF].i_packets++;
- st[(idx>>16)&0xFF].i_bytes+=skb->len;
+ st[(idx>>16)&0xFF].i_bytes += skb->len;
}
#endif
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index d2a8f8bb78a6..8ebe86dd72af 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -430,7 +430,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
* single device frame, and queue such a frame for sending.
*/
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{
struct iphdr *iph;
int raw = 0;
@@ -720,7 +720,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb),
void *from, int length, int hh_len, int fragheaderlen,
- int transhdrlen, int mtu,unsigned int flags)
+ int transhdrlen, int mtu, unsigned int flags)
{
struct sk_buff *skb;
int err;
@@ -741,7 +741,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
skb_reserve(skb, hh_len);
/* create space for UDP/IP header */
- skb_put(skb,fragheaderlen + transhdrlen);
+ skb_put(skb, fragheaderlen + transhdrlen);
/* initialize network header pointer */
skb_reset_network_header(skb);
@@ -778,7 +778,7 @@ int ip_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen,
- struct ipcm_cookie *ipc, struct rtable *rt,
+ struct ipcm_cookie *ipc, struct rtable **rtp,
unsigned int flags)
{
struct inet_sock *inet = inet_sk(sk);
@@ -793,6 +793,7 @@ int ip_append_data(struct sock *sk,
int offset = 0;
unsigned int maxfraglen, fragheaderlen;
int csummode = CHECKSUM_NONE;
+ struct rtable *rt;
if (flags&MSG_PROBE)
return 0;
@@ -812,7 +813,11 @@ int ip_append_data(struct sock *sk,
inet->cork.flags |= IPCORK_OPT;
inet->cork.addr = ipc->addr;
}
- dst_hold(&rt->u.dst);
+ rt = *rtp;
+ /*
+ * We steal reference to this route, caller should not release it
+ */
+ *rtp = NULL;
inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
rt->u.dst.dev->mtu :
dst_mtu(rt->u.dst.path);
@@ -1279,7 +1284,12 @@ int ip_push_pending_frames(struct sock *sk)
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
- skb->dst = dst_clone(&rt->u.dst);
+ /*
+ * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
+ * on dst refcount
+ */
+ inet->cork.dst = NULL;
+ skb->dst = &rt->u.dst;
if (iph->protocol == IPPROTO_ICMP)
icmp_out_count(net, ((struct icmphdr *)
@@ -1391,7 +1401,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
- &ipc, rt, MSG_DONTWAIT);
+ &ipc, &rt, MSG_DONTWAIT);
if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
if (arg->csumoffset >= 0)
*((__sum16 *)skb_transport_header(skb) +
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 465abf0a9869..43c05854d752 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -48,6 +48,7 @@
#define IP_CMSG_RECVOPTS 8
#define IP_CMSG_RETOPTS 16
#define IP_CMSG_PASSSEC 32
+#define IP_CMSG_ORIGDSTADDR 64
/*
* SOL_IP control messages.
@@ -94,7 +95,7 @@ static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
{
unsigned char optbuf[sizeof(struct ip_options) + 40];
- struct ip_options * opt = (struct ip_options*)optbuf;
+ struct ip_options * opt = (struct ip_options *)optbuf;
if (IPCB(skb)->opt.optlen == 0)
return;
@@ -126,6 +127,27 @@ static void ip_cmsg_recv_security(struct msghdr *msg, struct sk_buff *skb)
security_release_secctx(secdata, seclen);
}
+static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
+{
+ struct sockaddr_in sin;
+ struct iphdr *iph = ip_hdr(skb);
+ __be16 *ports = (__be16 *)skb_transport_header(skb);
+
+ if (skb_transport_offset(skb) + 4 > skb->len)
+ return;
+
+ /* All current transport protocols have the port numbers in the
+ * first four bytes of the transport header and this function is
+ * written with this assumption in mind.
+ */
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = iph->daddr;
+ sin.sin_port = ports[1];
+ memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ put_cmsg(msg, SOL_IP, IP_ORIGDSTADDR, sizeof(sin), &sin);
+}
void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
{
@@ -160,6 +182,12 @@ void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
if (flags & 1)
ip_cmsg_recv_security(msg, skb);
+
+ if ((flags>>=1) == 0)
+ return;
+ if (flags & 1)
+ ip_cmsg_recv_dstaddr(msg, skb);
+
}
int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc)
@@ -411,7 +439,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
int optname, char __user *optval, int optlen)
{
struct inet_sock *inet = inet_sk(sk);
- int val=0,err;
+ int val = 0, err;
if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) |
(1<<IP_RECVOPTS) | (1<<IP_RECVTOS) |
@@ -421,7 +449,8 @@ static int do_ip_setsockopt(struct sock *sk, int level,
(1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
(1<<IP_PASSSEC) | (1<<IP_TRANSPARENT))) ||
optname == IP_MULTICAST_TTL ||
- optname == IP_MULTICAST_LOOP) {
+ optname == IP_MULTICAST_LOOP ||
+ optname == IP_RECVORIGDSTADDR) {
if (optlen >= sizeof(int)) {
if (get_user(val, (int __user *) optval))
return -EFAULT;
@@ -437,7 +466,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
/* If optlen==0, it is equivalent to val == 0 */
if (ip_mroute_opt(optname))
- return ip_mroute_setsockopt(sk,optname,optval,optlen);
+ return ip_mroute_setsockopt(sk, optname, optval, optlen);
err = 0;
lock_sock(sk);
@@ -509,6 +538,12 @@ static int do_ip_setsockopt(struct sock *sk, int level,
else
inet->cmsg_flags &= ~IP_CMSG_PASSSEC;
break;
+ case IP_RECVORIGDSTADDR:
+ if (val)
+ inet->cmsg_flags |= IP_CMSG_ORIGDSTADDR;
+ else
+ inet->cmsg_flags &= ~IP_CMSG_ORIGDSTADDR;
+ break;
case IP_TOS: /* This sets both TOS and Precedence */
if (sk->sk_type == SOCK_STREAM) {
val &= ~3;
@@ -549,7 +584,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
goto e_inval;
if (optlen<1)
goto e_inval;
- if (val==-1)
+ if (val == -1)
val = 1;
if (val < 0 || val > 255)
goto e_inval;
@@ -573,12 +608,12 @@ static int do_ip_setsockopt(struct sock *sk, int level,
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
- if (copy_from_user(&mreq,optval,sizeof(mreq)))
+ if (copy_from_user(&mreq, optval, sizeof(mreq)))
break;
} else {
memset(&mreq, 0, sizeof(mreq));
if (optlen >= sizeof(struct in_addr) &&
- copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
+ copy_from_user(&mreq.imr_address, optval, sizeof(struct in_addr)))
break;
}
@@ -626,11 +661,11 @@ static int do_ip_setsockopt(struct sock *sk, int level,
goto e_inval;
err = -EFAULT;
if (optlen >= sizeof(struct ip_mreqn)) {
- if (copy_from_user(&mreq,optval,sizeof(mreq)))
+ if (copy_from_user(&mreq, optval, sizeof(mreq)))
break;
} else {
memset(&mreq, 0, sizeof(mreq));
- if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
+ if (copy_from_user(&mreq, optval, sizeof(struct ip_mreq)))
break;
}
@@ -808,7 +843,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
err = -ENOBUFS;
break;
}
- gsf = kmalloc(optlen,GFP_KERNEL);
+ gsf = kmalloc(optlen, GFP_KERNEL);
if (!gsf) {
err = -ENOBUFS;
break;
@@ -828,7 +863,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
goto mc_msf_out;
}
msize = IP_MSFILTER_SIZE(gsf->gf_numsrc);
- msf = kmalloc(msize,GFP_KERNEL);
+ msf = kmalloc(msize, GFP_KERNEL);
if (!msf) {
err = -ENOBUFS;
goto mc_msf_out;
@@ -971,9 +1006,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
if (ip_mroute_opt(optname))
- return ip_mroute_getsockopt(sk,optname,optval,optlen);
+ return ip_mroute_getsockopt(sk, optname, optval, optlen);
- if (get_user(len,optlen))
+ if (get_user(len, optlen))
return -EFAULT;
if (len < 0)
return -EINVAL;
@@ -984,7 +1019,7 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
case IP_OPTIONS:
{
unsigned char optbuf[sizeof(struct ip_options)+40];
- struct ip_options * opt = (struct ip_options*)optbuf;
+ struct ip_options * opt = (struct ip_options *)optbuf;
opt->optlen = 0;
if (inet->opt)
memcpy(optbuf, inet->opt,
@@ -1022,6 +1057,9 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
case IP_PASSSEC:
val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0;
break;
+ case IP_RECVORIGDSTADDR:
+ val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0;
+ break;
case IP_TOS:
val = inet->tos;
break;
@@ -1154,13 +1192,13 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname,
len = 1;
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval,&ucval,1))
+ if (copy_to_user(optval, &ucval, 1))
return -EFAULT;
} else {
len = min_t(unsigned int, sizeof(int), len);
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval,&val,len))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
}
return 0;
@@ -1178,7 +1216,7 @@ int ip_getsockopt(struct sock *sk, int level,
!ip_mroute_opt(optname)) {
int len;
- if (get_user(len,optlen))
+ if (get_user(len, optlen))
return -EFAULT;
lock_sock(sk);
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 38ccb6dfb02e..3262ce06294c 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -35,12 +35,12 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
return;
spi = htonl(ntohs(ipch->cpi));
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
+ x = xfrm_state_lookup(&init_net, (xfrm_address_t *)&iph->daddr,
spi, IPPROTO_COMP, AF_INET);
if (!x)
return;
- NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/" NIPQUAD_FMT "\n",
- spi, NIPQUAD(iph->daddr));
+ NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI4\n",
+ spi, &iph->daddr);
xfrm_state_put(x);
}
@@ -49,7 +49,7 @@ static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
{
struct xfrm_state *t;
- t = xfrm_state_alloc();
+ t = xfrm_state_alloc(&init_net);
if (t == NULL)
goto out;
@@ -85,7 +85,7 @@ static int ipcomp_tunnel_attach(struct xfrm_state *x)
int err = 0;
struct xfrm_state *t;
- t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr.a4,
+ t = xfrm_state_lookup(&init_net, (xfrm_address_t *)&x->id.daddr.a4,
x->props.saddr.a4, IPPROTO_IPIP, AF_INET);
if (!t) {
t = ipcomp_tunnel_create(x);
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 42065fff46c4..42a0f3dd3fd6 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -374,7 +374,7 @@ static int __init ic_defaults(void)
*/
if (!ic_host_name_set)
- sprintf(init_utsname()->nodename, NIPQUAD_FMT, NIPQUAD(ic_myaddr));
+ sprintf(init_utsname()->nodename, "%pI4", &ic_myaddr);
if (root_server_addr == NONE)
root_server_addr = ic_servaddr;
@@ -387,11 +387,11 @@ static int __init ic_defaults(void)
else if (IN_CLASSC(ntohl(ic_myaddr)))
ic_netmask = htonl(IN_CLASSC_NET);
else {
- printk(KERN_ERR "IP-Config: Unable to guess netmask for address " NIPQUAD_FMT "\n",
- NIPQUAD(ic_myaddr));
+ printk(KERN_ERR "IP-Config: Unable to guess netmask for address %pI4\n",
+ &ic_myaddr);
return -1;
}
- printk("IP-Config: Guessing netmask " NIPQUAD_FMT "\n", NIPQUAD(ic_netmask));
+ printk("IP-Config: Guessing netmask %pI4\n", &ic_netmask);
}
return 0;
@@ -979,10 +979,8 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
ic_myaddr = b->your_ip;
ic_servaddr = server_id;
#ifdef IPCONFIG_DEBUG
- printk("DHCP: Offered address " NIPQUAD_FMT,
- NIPQUAD(ic_myaddr));
- printk(" by server " NIPQUAD_FMT "\n",
- NIPQUAD(ic_servaddr));
+ printk("DHCP: Offered address %pI4 by server %pI4\n",
+ &ic_myaddr, &ic_servaddr);
#endif
/* The DHCP indicated server address takes
* precedence over the bootp header one if
@@ -1177,11 +1175,11 @@ static int __init ic_dynamic(void)
return -1;
}
- printk("IP-Config: Got %s answer from " NIPQUAD_FMT ", ",
+ printk("IP-Config: Got %s answer from %pI4, ",
((ic_got_reply & IC_RARP) ? "RARP"
: (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
- NIPQUAD(ic_servaddr));
- printk("my address is " NIPQUAD_FMT "\n", NIPQUAD(ic_myaddr));
+ &ic_servaddr);
+ printk("my address is %pI4\n", &ic_myaddr);
return 0;
}
@@ -1206,14 +1204,12 @@ static int pnp_seq_show(struct seq_file *seq, void *v)
"domain %s\n", ic_domain);
for (i = 0; i < CONF_NAMESERVERS_MAX; i++) {
if (ic_nameservers[i] != NONE)
- seq_printf(seq,
- "nameserver " NIPQUAD_FMT "\n",
- NIPQUAD(ic_nameservers[i]));
+ seq_printf(seq, "nameserver %pI4\n",
+ &ic_nameservers[i]);
}
if (ic_servaddr != NONE)
- seq_printf(seq,
- "bootserver " NIPQUAD_FMT "\n",
- NIPQUAD(ic_servaddr));
+ seq_printf(seq, "bootserver %pI4\n",
+ &ic_servaddr);
return 0;
}
@@ -1387,13 +1383,13 @@ static int __init ip_auto_config(void)
*/
printk("IP-Config: Complete:");
printk("\n device=%s", ic_dev->name);
- printk(", addr=" NIPQUAD_FMT, NIPQUAD(ic_myaddr));
- printk(", mask=" NIPQUAD_FMT, NIPQUAD(ic_netmask));
- printk(", gw=" NIPQUAD_FMT, NIPQUAD(ic_gateway));
+ printk(", addr=%pI4", &ic_myaddr);
+ printk(", mask=%pI4", &ic_netmask);
+ printk(", gw=%pI4", &ic_gateway);
printk(",\n host=%s, domain=%s, nis-domain=%s",
utsname()->nodename, ic_domain, utsname()->domainname);
- printk(",\n bootserver=" NIPQUAD_FMT, NIPQUAD(ic_servaddr));
- printk(", rootserver=" NIPQUAD_FMT, NIPQUAD(root_server_addr));
+ printk(",\n bootserver=%pI4", &ic_servaddr);
+ printk(", rootserver=%pI4", &root_server_addr);
printk(", rootpath=%s", root_server_path);
printk("\n");
#endif /* !SILENT */
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 29609d29df76..5079dfbc6f38 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -130,8 +130,8 @@ struct ipip_net {
struct net_device *fb_tunnel_dev;
};
-static int ipip_fb_tunnel_init(struct net_device *dev);
-static int ipip_tunnel_init(struct net_device *dev);
+static void ipip_fb_tunnel_init(struct net_device *dev);
+static void ipip_tunnel_init(struct net_device *dev);
static void ipip_tunnel_setup(struct net_device *dev);
static DEFINE_RWLOCK(ipip_lock);
@@ -245,9 +245,10 @@ static struct ip_tunnel * ipip_tunnel_locate(struct net *net,
}
nt = netdev_priv(dev);
- dev->init = ipip_tunnel_init;
nt->parms = *parms;
+ ipip_tunnel_init(dev);
+
if (register_netdevice(dev) < 0)
goto failed_free;
@@ -281,7 +282,7 @@ static int ipip_err(struct sk_buff *skb, u32 info)
8 bytes of packet payload. It means, that precise relaying of
ICMP in the real Internet is absolutely infeasible.
*/
- struct iphdr *iph = (struct iphdr*)skb->data;
+ struct iphdr *iph = (struct iphdr *)skb->data;
const int type = icmp_hdr(skb)->type;
const int code = icmp_hdr(skb)->code;
struct ip_tunnel *t;
@@ -691,12 +692,17 @@ static int ipip_tunnel_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static const struct net_device_ops ipip_netdev_ops = {
+ .ndo_uninit = ipip_tunnel_uninit,
+ .ndo_start_xmit = ipip_tunnel_xmit,
+ .ndo_do_ioctl = ipip_tunnel_ioctl,
+ .ndo_change_mtu = ipip_tunnel_change_mtu,
+
+};
+
static void ipip_tunnel_setup(struct net_device *dev)
{
- dev->uninit = ipip_tunnel_uninit;
- dev->hard_start_xmit = ipip_tunnel_xmit;
- dev->do_ioctl = ipip_tunnel_ioctl;
- dev->change_mtu = ipip_tunnel_change_mtu;
+ dev->netdev_ops = &ipip_netdev_ops;
dev->destructor = free_netdev;
dev->type = ARPHRD_TUNNEL;
@@ -708,11 +714,9 @@ static void ipip_tunnel_setup(struct net_device *dev)
dev->features |= NETIF_F_NETNS_LOCAL;
}
-static int ipip_tunnel_init(struct net_device *dev)
+static void ipip_tunnel_init(struct net_device *dev)
{
- struct ip_tunnel *tunnel;
-
- tunnel = netdev_priv(dev);
+ struct ip_tunnel *tunnel = netdev_priv(dev);
tunnel->dev = dev;
strcpy(tunnel->parms.name, dev->name);
@@ -721,11 +725,9 @@ static int ipip_tunnel_init(struct net_device *dev)
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
ipip_tunnel_bind_dev(dev);
-
- return 0;
}
-static int ipip_fb_tunnel_init(struct net_device *dev)
+static void ipip_fb_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct iphdr *iph = &tunnel->parms.iph;
@@ -740,7 +742,6 @@ static int ipip_fb_tunnel_init(struct net_device *dev)
dev_hold(dev);
ipn->tunnels_wc[0] = tunnel;
- return 0;
}
static struct xfrm_tunnel ipip_handler = {
@@ -792,10 +793,10 @@ static int ipip_init_net(struct net *net)
err = -ENOMEM;
goto err_alloc_dev;
}
-
- ipn->fb_tunnel_dev->init = ipip_fb_tunnel_init;
dev_net_set(ipn->fb_tunnel_dev, net);
+ ipip_fb_tunnel_init(ipn->fb_tunnel_dev);
+
if ((err = register_netdev(ipn->fb_tunnel_dev)))
goto err_reg_dev;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 25924b1eb2ef..14666449dc1c 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -124,8 +124,8 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
dev = __dev_get_by_name(&init_net, "tunl0");
if (dev) {
+ const struct net_device_ops *ops = dev->netdev_ops;
struct ifreq ifr;
- mm_segment_t oldfs;
struct ip_tunnel_parm p;
memset(&p, 0, sizeof(p));
@@ -137,9 +137,13 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
sprintf(p.name, "dvmrp%d", v->vifc_vifi);
ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- dev->do_ioctl(dev, &ifr, SIOCDELTUNNEL);
- set_fs(oldfs);
+ if (ops->ndo_do_ioctl) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(KERNEL_DS);
+ ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
+ set_fs(oldfs);
+ }
}
}
@@ -151,9 +155,9 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v)
dev = __dev_get_by_name(&init_net, "tunl0");
if (dev) {
+ const struct net_device_ops *ops = dev->netdev_ops;
int err;
struct ifreq ifr;
- mm_segment_t oldfs;
struct ip_tunnel_parm p;
struct in_device *in_dev;
@@ -166,9 +170,14 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v)
sprintf(p.name, "dvmrp%d", v->vifc_vifi);
ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
- set_fs(oldfs);
+ if (ops->ndo_do_ioctl) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+ set_fs(oldfs);
+ } else
+ err = -EOPNOTSUPP;
dev = NULL;
@@ -213,12 +222,16 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
+static const struct net_device_ops reg_vif_netdev_ops = {
+ .ndo_start_xmit = reg_vif_xmit,
+};
+
static void reg_vif_setup(struct net_device *dev)
{
dev->type = ARPHRD_PIMREG;
dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
dev->flags = IFF_NOARP;
- dev->hard_start_xmit = reg_vif_xmit;
+ dev->netdev_ops = &reg_vif_netdev_ops,
dev->destructor = free_netdev;
}
@@ -331,7 +344,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c)
atomic_dec(&cache_resolve_queue_len);
- while ((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
+ while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
nlh->nlmsg_type = NLMSG_ERROR;
@@ -477,13 +490,13 @@ static int vif_add(struct vifctl *vifc, int mrtsock)
/*
* Fill in the VIF structures
*/
- v->rate_limit=vifc->vifc_rate_limit;
- v->local=vifc->vifc_lcl_addr.s_addr;
- v->remote=vifc->vifc_rmt_addr.s_addr;
- v->flags=vifc->vifc_flags;
+ v->rate_limit = vifc->vifc_rate_limit;
+ v->local = vifc->vifc_lcl_addr.s_addr;
+ v->remote = vifc->vifc_rmt_addr.s_addr;
+ v->flags = vifc->vifc_flags;
if (!mrtsock)
v->flags |= VIFF_STATIC;
- v->threshold=vifc->vifc_threshold;
+ v->threshold = vifc->vifc_threshold;
v->bytes_in = 0;
v->bytes_out = 0;
v->pkt_in = 0;
@@ -494,7 +507,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock)
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
- v->dev=dev;
+ v->dev = dev;
#ifdef CONFIG_IP_PIMSM
if (v->flags&VIFF_REGISTER)
reg_vif_num = vifi;
@@ -507,7 +520,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock)
static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp)
{
- int line=MFC_HASH(mcastgrp,origin);
+ int line = MFC_HASH(mcastgrp, origin);
struct mfc_cache *c;
for (c=mfc_cache_array[line]; c; c = c->next) {
@@ -522,8 +535,8 @@ static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp)
*/
static struct mfc_cache *ipmr_cache_alloc(void)
{
- struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
- if (c==NULL)
+ struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
+ if (c == NULL)
return NULL;
c->mfc_un.res.minvif = MAXVIFS;
return c;
@@ -531,8 +544,8 @@ static struct mfc_cache *ipmr_cache_alloc(void)
static struct mfc_cache *ipmr_cache_alloc_unres(void)
{
- struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
- if (c==NULL)
+ struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
+ if (c == NULL)
return NULL;
skb_queue_head_init(&c->mfc_un.unres.unresolved);
c->mfc_un.unres.expires = jiffies + 10*HZ;
@@ -552,7 +565,7 @@ static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c)
* Play the pending entries through our router
*/
- while ((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
+ while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
@@ -637,7 +650,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
* Add our header
*/
- igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
+ igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
igmp->type =
msg->im_msgtype = assert;
igmp->code = 0;
@@ -653,7 +666,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
/*
* Deliver to mrouted
*/
- if ((ret=sock_queue_rcv_skb(mroute_socket,skb))<0) {
+ if ((ret = sock_queue_rcv_skb(mroute_socket, skb))<0) {
if (net_ratelimit())
printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
kfree_skb(skb);
@@ -685,7 +698,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb)
* Create a new entry if allowable
*/
- if (atomic_read(&cache_resolve_queue_len)>=10 ||
+ if (atomic_read(&cache_resolve_queue_len) >= 10 ||
(c=ipmr_cache_alloc_unres())==NULL) {
spin_unlock_bh(&mfc_unres_lock);
@@ -728,7 +741,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb)
kfree_skb(skb);
err = -ENOBUFS;
} else {
- skb_queue_tail(&c->mfc_un.unres.unresolved,skb);
+ skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
err = 0;
}
@@ -745,7 +758,7 @@ static int ipmr_mfc_delete(struct mfcctl *mfc)
int line;
struct mfc_cache *c, **cp;
- line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
+ line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
@@ -766,7 +779,7 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock)
int line;
struct mfc_cache *uc, *c, **cp;
- line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
+ line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
@@ -787,13 +800,13 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock)
if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
return -EINVAL;
- c=ipmr_cache_alloc();
- if (c==NULL)
+ c = ipmr_cache_alloc();
+ if (c == NULL)
return -ENOMEM;
- c->mfc_origin=mfc->mfcc_origin.s_addr;
- c->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
- c->mfc_parent=mfc->mfcc_parent;
+ c->mfc_origin = mfc->mfcc_origin.s_addr;
+ c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
+ c->mfc_parent = mfc->mfcc_parent;
ipmr_update_thresholds(c, mfc->mfcc_ttls);
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
@@ -846,7 +859,7 @@ static void mroute_clean_tables(struct sock *sk)
/*
* Wipe the cache
*/
- for (i=0;i<MFC_LINES;i++) {
+ for (i=0; i<MFC_LINES; i++) {
struct mfc_cache *c, **cp;
cp = &mfc_cache_array[i];
@@ -887,7 +900,7 @@ static void mrtsock_destruct(struct sock *sk)
IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)--;
write_lock_bh(&mrt_lock);
- mroute_socket=NULL;
+ mroute_socket = NULL;
write_unlock_bh(&mrt_lock);
mroute_clean_tables(sk);
@@ -902,7 +915,7 @@ static void mrtsock_destruct(struct sock *sk)
* MOSPF/PIM router set up we can clean this up.
*/
-int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int optlen)
+int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int optlen)
{
int ret;
struct vifctl vif;
@@ -918,7 +931,7 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int opt
if (sk->sk_type != SOCK_RAW ||
inet_sk(sk)->num != IPPROTO_IGMP)
return -EOPNOTSUPP;
- if (optlen!=sizeof(int))
+ if (optlen != sizeof(int))
return -ENOPROTOOPT;
rtnl_lock();
@@ -930,7 +943,7 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int opt
ret = ip_ra_control(sk, 1, mrtsock_destruct);
if (ret == 0) {
write_lock_bh(&mrt_lock);
- mroute_socket=sk;
+ mroute_socket = sk;
write_unlock_bh(&mrt_lock);
IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)++;
@@ -938,19 +951,19 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int opt
rtnl_unlock();
return ret;
case MRT_DONE:
- if (sk!=mroute_socket)
+ if (sk != mroute_socket)
return -EACCES;
return ip_ra_control(sk, 0, NULL);
case MRT_ADD_VIF:
case MRT_DEL_VIF:
- if (optlen!=sizeof(vif))
+ if (optlen != sizeof(vif))
return -EINVAL;
- if (copy_from_user(&vif,optval,sizeof(vif)))
+ if (copy_from_user(&vif, optval, sizeof(vif)))
return -EFAULT;
if (vif.vifc_vifi >= MAXVIFS)
return -ENFILE;
rtnl_lock();
- if (optname==MRT_ADD_VIF) {
+ if (optname == MRT_ADD_VIF) {
ret = vif_add(&vif, sk==mroute_socket);
} else {
ret = vif_delete(vif.vifc_vifi, 0);
@@ -964,12 +977,12 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int opt
*/
case MRT_ADD_MFC:
case MRT_DEL_MFC:
- if (optlen!=sizeof(mfc))
+ if (optlen != sizeof(mfc))
return -EINVAL;
- if (copy_from_user(&mfc,optval, sizeof(mfc)))
+ if (copy_from_user(&mfc, optval, sizeof(mfc)))
return -EFAULT;
rtnl_lock();
- if (optname==MRT_DEL_MFC)
+ if (optname == MRT_DEL_MFC)
ret = ipmr_mfc_delete(&mfc);
else
ret = ipmr_mfc_add(&mfc, sk==mroute_socket);
@@ -1028,12 +1041,12 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int opt
* Getsock opt support for the multicast routing system.
*/
-int ip_mroute_getsockopt(struct sock *sk,int optname,char __user *optval,int __user *optlen)
+int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
{
int olr;
int val;
- if (optname!=MRT_VERSION &&
+ if (optname != MRT_VERSION &&
#ifdef CONFIG_IP_PIMSM
optname!=MRT_PIM &&
#endif
@@ -1047,17 +1060,17 @@ int ip_mroute_getsockopt(struct sock *sk,int optname,char __user *optval,int __u
if (olr < 0)
return -EINVAL;
- if (put_user(olr,optlen))
+ if (put_user(olr, optlen))
return -EFAULT;
- if (optname==MRT_VERSION)
- val=0x0305;
+ if (optname == MRT_VERSION)
+ val = 0x0305;
#ifdef CONFIG_IP_PIMSM
- else if (optname==MRT_PIM)
- val=mroute_do_pim;
+ else if (optname == MRT_PIM)
+ val = mroute_do_pim;
#endif
else
- val=mroute_do_assert;
- if (copy_to_user(optval,&val,olr))
+ val = mroute_do_assert;
+ if (copy_to_user(optval, &val, olr))
return -EFAULT;
return 0;
}
@@ -1075,27 +1088,27 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
switch (cmd) {
case SIOCGETVIFCNT:
- if (copy_from_user(&vr,arg,sizeof(vr)))
+ if (copy_from_user(&vr, arg, sizeof(vr)))
return -EFAULT;
- if (vr.vifi>=maxvif)
+ if (vr.vifi >= maxvif)
return -EINVAL;
read_lock(&mrt_lock);
vif=&vif_table[vr.vifi];
if (VIF_EXISTS(vr.vifi)) {
- vr.icount=vif->pkt_in;
- vr.ocount=vif->pkt_out;
- vr.ibytes=vif->bytes_in;
- vr.obytes=vif->bytes_out;
+ vr.icount = vif->pkt_in;
+ vr.ocount = vif->pkt_out;
+ vr.ibytes = vif->bytes_in;
+ vr.obytes = vif->bytes_out;
read_unlock(&mrt_lock);
- if (copy_to_user(arg,&vr,sizeof(vr)))
+ if (copy_to_user(arg, &vr, sizeof(vr)))
return -EFAULT;
return 0;
}
read_unlock(&mrt_lock);
return -EADDRNOTAVAIL;
case SIOCGETSGCNT:
- if (copy_from_user(&sr,arg,sizeof(sr)))
+ if (copy_from_user(&sr, arg, sizeof(sr)))
return -EFAULT;
read_lock(&mrt_lock);
@@ -1106,7 +1119,7 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
sr.wrong_if = c->mfc_un.res.wrong_if;
read_unlock(&mrt_lock);
- if (copy_to_user(arg,&sr,sizeof(sr)))
+ if (copy_to_user(arg, &sr, sizeof(sr)))
return -EFAULT;
return 0;
}
@@ -1130,15 +1143,15 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
v=&vif_table[0];
- for (ct=0;ct<maxvif;ct++,v++) {
- if (v->dev==dev)
+ for (ct=0; ct<maxvif; ct++,v++) {
+ if (v->dev == dev)
vif_delete(ct, 1);
}
return NOTIFY_DONE;
}
-static struct notifier_block ip_mr_notifier={
+static struct notifier_block ip_mr_notifier = {
.notifier_call = ipmr_device_event,
};
@@ -1204,7 +1217,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
- vif->bytes_out+=skb->len;
+ vif->bytes_out += skb->len;
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT);
@@ -1254,7 +1267,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
}
vif->pkt_out++;
- vif->bytes_out+=skb->len;
+ vif->bytes_out += skb->len;
dst_release(skb->dst);
skb->dst = &rt->u.dst;
@@ -1352,7 +1365,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local
}
vif_table[vif].pkt_in++;
- vif_table[vif].bytes_in+=skb->len;
+ vif_table[vif].bytes_in += skb->len;
/*
* Forward the frame
@@ -1364,7 +1377,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local
if (skb2)
ipmr_queue_xmit(skb2, cache, psend);
}
- psend=ct;
+ psend = ct;
}
}
if (psend != -1) {
@@ -1428,7 +1441,7 @@ int ip_mr_input(struct sk_buff *skb)
/*
* No usable cache entry
*/
- if (cache==NULL) {
+ if (cache == NULL) {
int vif;
if (local) {
@@ -1469,29 +1482,13 @@ dont_forward:
return 0;
}
-#ifdef CONFIG_IP_PIMSM_V1
-/*
- * Handle IGMP messages of PIMv1
- */
-
-int pim_rcv_v1(struct sk_buff * skb)
+#ifdef CONFIG_IP_PIMSM
+static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen)
{
- struct igmphdr *pim;
- struct iphdr *encap;
- struct net_device *reg_dev = NULL;
-
- if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
- goto drop;
+ struct net_device *reg_dev = NULL;
+ struct iphdr *encap;
- pim = igmp_hdr(skb);
-
- if (!mroute_do_pim ||
- skb->len < sizeof(*pim) + sizeof(*encap) ||
- pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
- goto drop;
-
- encap = (struct iphdr *)(skb_transport_header(skb) +
- sizeof(struct igmphdr));
+ encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
/*
Check that:
a. packet is really destinted to a multicast group
@@ -1500,8 +1497,8 @@ int pim_rcv_v1(struct sk_buff * skb)
*/
if (!ipv4_is_multicast(encap->daddr) ||
encap->tot_len == 0 ||
- ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
- goto drop;
+ ntohs(encap->tot_len) + pimlen > skb->len)
+ return 1;
read_lock(&mrt_lock);
if (reg_vif_num >= 0)
@@ -1511,7 +1508,7 @@ int pim_rcv_v1(struct sk_buff * skb)
read_unlock(&mrt_lock);
if (reg_dev == NULL)
- goto drop;
+ return 1;
skb->mac_header = skb->network_header;
skb_pull(skb, (u8*)encap - skb->data);
@@ -1527,9 +1524,33 @@ int pim_rcv_v1(struct sk_buff * skb)
nf_reset(skb);
netif_rx(skb);
dev_put(reg_dev);
+
return 0;
- drop:
- kfree_skb(skb);
+}
+#endif
+
+#ifdef CONFIG_IP_PIMSM_V1
+/*
+ * Handle IGMP messages of PIMv1
+ */
+
+int pim_rcv_v1(struct sk_buff * skb)
+{
+ struct igmphdr *pim;
+
+ if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
+ goto drop;
+
+ pim = igmp_hdr(skb);
+
+ if (!mroute_do_pim ||
+ pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
+ goto drop;
+
+ if (__pim_rcv(skb, sizeof(*pim))) {
+drop:
+ kfree_skb(skb);
+ }
return 0;
}
#endif
@@ -1538,10 +1559,8 @@ int pim_rcv_v1(struct sk_buff * skb)
static int pim_rcv(struct sk_buff * skb)
{
struct pimreghdr *pim;
- struct iphdr *encap;
- struct net_device *reg_dev = NULL;
- if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
+ if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
goto drop;
pim = (struct pimreghdr *)skb_transport_header(skb);
@@ -1551,41 +1570,10 @@ static int pim_rcv(struct sk_buff * skb)
csum_fold(skb_checksum(skb, 0, skb->len, 0))))
goto drop;
- /* check if the inner packet is destined to mcast group */
- encap = (struct iphdr *)(skb_transport_header(skb) +
- sizeof(struct pimreghdr));
- if (!ipv4_is_multicast(encap->daddr) ||
- encap->tot_len == 0 ||
- ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
- goto drop;
-
- read_lock(&mrt_lock);
- if (reg_vif_num >= 0)
- reg_dev = vif_table[reg_vif_num].dev;
- if (reg_dev)
- dev_hold(reg_dev);
- read_unlock(&mrt_lock);
-
- if (reg_dev == NULL)
- goto drop;
-
- skb->mac_header = skb->network_header;
- skb_pull(skb, (u8*)encap - skb->data);
- skb_reset_network_header(skb);
- skb->dev = reg_dev;
- skb->protocol = htons(ETH_P_IP);
- skb->ip_summed = 0;
- skb->pkt_type = PACKET_HOST;
- dst_release(skb->dst);
- reg_dev->stats.rx_bytes += skb->len;
- reg_dev->stats.rx_packets++;
- skb->dst = NULL;
- nf_reset(skb);
- netif_rx(skb);
- dev_put(reg_dev);
- return 0;
- drop:
- kfree_skb(skb);
+ if (__pim_rcv(skb, sizeof(*pim))) {
+drop:
+ kfree_skb(skb);
+ }
return 0;
}
#endif
@@ -1602,13 +1590,13 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
if (dev)
RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
- mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
+ mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
if (c->mfc_un.res.ttls[ct] < 255) {
if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
goto rtattr_failure;
- nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
+ nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
nhp->rtnh_flags = 0;
nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
nhp->rtnh_ifindex = vif_table[ct].dev->ifindex;
@@ -1634,7 +1622,7 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
read_lock(&mrt_lock);
cache = ipmr_cache_find(rt->rt_src, rt->rt_dst);
- if (cache==NULL) {
+ if (cache == NULL) {
struct sk_buff *skb2;
struct iphdr *iph;
struct net_device *dev;
@@ -1866,15 +1854,16 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
const struct mfc_cache *mfc = v;
const struct ipmr_mfc_iter *it = seq->private;
- seq_printf(seq, "%08lX %08lX %-3d %8ld %8ld %8ld",
+ seq_printf(seq, "%08lX %08lX %-3hd",
(unsigned long) mfc->mfc_mcastgrp,
(unsigned long) mfc->mfc_origin,
- mfc->mfc_parent,
- mfc->mfc_un.res.pkt,
- mfc->mfc_un.res.bytes,
- mfc->mfc_un.res.wrong_if);
+ mfc->mfc_parent);
if (it->cache != &mfc_unres_queue) {
+ seq_printf(seq, " %8lu %8lu %8lu",
+ mfc->mfc_un.res.pkt,
+ mfc->mfc_un.res.bytes,
+ mfc->mfc_un.res.wrong_if);
for (n = mfc->mfc_un.res.minvif;
n < mfc->mfc_un.res.maxvif; n++ ) {
if (VIF_EXISTS(n)
@@ -1883,6 +1872,11 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
" %2d:%-3d",
n, mfc->mfc_un.res.ttls[n]);
}
+ } else {
+ /* unresolved mfc_caches don't contain
+ * pkt, bytes and wrong_if values
+ */
+ seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
}
seq_putc(seq, '\n');
}
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index 6efdb70b3eb2..fdf6811c31a2 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -66,7 +66,7 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
#ifdef CONFIG_XFRM
if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
xfrm_decode_session(skb, &fl, AF_INET) == 0)
- if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0))
+ if (xfrm_lookup(net, &skb->dst, &fl, skb->sk, 0))
return -1;
#endif
@@ -97,7 +97,7 @@ int ip_xfrm_me_harder(struct sk_buff *skb)
dst = ((struct xfrm_dst *)dst)->route;
dst_hold(dst);
- if (xfrm_lookup(&dst, &fl, skb->sk, 0) < 0)
+ if (xfrm_lookup(dev_net(dst->dev), &dst, &fl, skb->sk, 0) < 0)
return -1;
dst_release(skb->dst);
@@ -125,6 +125,7 @@ struct ip_rt_info {
__be32 daddr;
__be32 saddr;
u_int8_t tos;
+ u_int32_t mark;
};
static void nf_ip_saveroute(const struct sk_buff *skb,
@@ -138,6 +139,7 @@ static void nf_ip_saveroute(const struct sk_buff *skb,
rt_info->tos = iph->tos;
rt_info->daddr = iph->daddr;
rt_info->saddr = iph->saddr;
+ rt_info->mark = skb->mark;
}
}
@@ -150,6 +152,7 @@ static int nf_ip_reroute(struct sk_buff *skb,
const struct iphdr *iph = ip_hdr(skb);
if (!(iph->tos == rt_info->tos
+ && skb->mark == rt_info->mark
&& iph->daddr == rt_info->daddr
&& iph->saddr == rt_info->saddr))
return ip_route_me_harder(skb, RTN_UNSPEC);
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 8d70d29f1ccf..7ea88b61cb0d 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -142,15 +142,15 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
ARPT_INV_TGTIP)) {
dprintf("Source or target IP address mismatch.\n");
- dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
- NIPQUAD(src_ipaddr),
- NIPQUAD(arpinfo->smsk.s_addr),
- NIPQUAD(arpinfo->src.s_addr),
+ dprintf("SRC: %pI4. Mask: %pI4. Target: %pI4.%s\n",
+ &src_ipaddr,
+ &arpinfo->smsk.s_addr,
+ &arpinfo->src.s_addr,
arpinfo->invflags & ARPT_INV_SRCIP ? " (INV)" : "");
- dprintf("TGT: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
- NIPQUAD(tgt_ipaddr),
- NIPQUAD(arpinfo->tmsk.s_addr),
- NIPQUAD(arpinfo->tgt.s_addr),
+ dprintf("TGT: %pI4 Mask: %pI4 Target: %pI4.%s\n",
+ &tgt_ipaddr,
+ &arpinfo->tmsk.s_addr,
+ &arpinfo->tgt.s_addr,
arpinfo->invflags & ARPT_INV_TGTIP ? " (INV)" : "");
return 0;
}
diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c
index bee3d117661a..e091187e864f 100644
--- a/net/ipv4/netfilter/arptable_filter.c
+++ b/net/ipv4/netfilter/arptable_filter.c
@@ -75,16 +75,6 @@ static unsigned int arpt_out_hook(unsigned int hook,
dev_net(out)->ipv4.arptable_filter);
}
-static unsigned int arpt_forward_hook(unsigned int hook,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
-{
- return arpt_do_table(skb, hook, in, out,
- dev_net(in)->ipv4.arptable_filter);
-}
-
static struct nf_hook_ops arpt_ops[] __read_mostly = {
{
.hook = arpt_in_hook,
@@ -101,7 +91,7 @@ static struct nf_hook_ops arpt_ops[] __read_mostly = {
.priority = NF_IP_PRI_FILTER,
},
{
- .hook = arpt_forward_hook,
+ .hook = arpt_in_hook,
.owner = THIS_MODULE,
.pf = NFPROTO_ARP,
.hooknum = NF_ARP_FORWARD,
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 213fb27debc1..ef8b6ca068b2 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -94,15 +94,11 @@ ip_packet_match(const struct iphdr *ip,
IPT_INV_DSTIP)) {
dprintf("Source or dest mismatch.\n");
- dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
- NIPQUAD(ip->saddr),
- NIPQUAD(ipinfo->smsk.s_addr),
- NIPQUAD(ipinfo->src.s_addr),
+ dprintf("SRC: %pI4. Mask: %pI4. Target: %pI4.%s\n",
+ &ip->saddr, &ipinfo->smsk.s_addr, &ipinfo->src.s_addr,
ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
- dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
- NIPQUAD(ip->daddr),
- NIPQUAD(ipinfo->dmsk.s_addr),
- NIPQUAD(ipinfo->dst.s_addr),
+ dprintf("DST: %pI4 Mask: %pI4 Target: %pI4.%s\n",
+ &ip->daddr, &ipinfo->dmsk.s_addr, &ipinfo->dst.s_addr,
ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
return false;
}
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index 7ac1677419a9..2e4f98b85524 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -168,7 +168,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip,
char buffer[16];
/* create proc dir entry */
- sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
+ sprintf(buffer, "%pI4", &ip);
c->pde = proc_create_data(buffer, S_IWUSR|S_IRUSR,
clusterip_procdir,
&clusterip_proc_fops, c);
@@ -373,7 +373,7 @@ static bool clusterip_tg_check(const struct xt_tgchk_param *par)
config = clusterip_config_find_get(e->ip.dst.s_addr, 1);
if (!config) {
if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
- printk(KERN_WARNING "CLUSTERIP: no config found for %u.%u.%u.%u, need 'new'\n", NIPQUAD(e->ip.dst.s_addr));
+ printk(KERN_WARNING "CLUSTERIP: no config found for %pI4, need 'new'\n", &e->ip.dst.s_addr);
return false;
} else {
struct net_device *dev;
@@ -478,9 +478,8 @@ static void arp_print(struct arp_payload *payload)
}
hbuffer[--k]='\0';
- printk("src %u.%u.%u.%u@%s, dst %u.%u.%u.%u\n",
- NIPQUAD(payload->src_ip), hbuffer,
- NIPQUAD(payload->dst_ip));
+ printk("src %pI4@%s, dst %pI4\n",
+ &payload->src_ip, hbuffer, &payload->dst_ip);
}
#endif
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
index fc6ce04a3e35..27a78fbbd92b 100644
--- a/net/ipv4/netfilter/ipt_LOG.c
+++ b/net/ipv4/netfilter/ipt_LOG.c
@@ -54,8 +54,8 @@ static void dump_packet(const struct nf_loginfo *info,
/* Important fields:
* TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
- printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
- NIPQUAD(ih->saddr), NIPQUAD(ih->daddr));
+ printk("SRC=%pI4 DST=%pI4 ",
+ &ih->saddr, &ih->daddr);
/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
@@ -262,8 +262,7 @@ static void dump_packet(const struct nf_loginfo *info,
break;
case ICMP_REDIRECT:
/* Max length: 24 "GATEWAY=255.255.255.255 " */
- printk("GATEWAY=%u.%u.%u.%u ",
- NIPQUAD(ich->un.gateway));
+ printk("GATEWAY=%pI4 ", &ich->un.gateway);
/* Fall through */
case ICMP_DEST_UNREACH:
case ICMP_SOURCE_QUENCH:
@@ -340,8 +339,8 @@ static void dump_packet(const struct nf_loginfo *info,
read_lock_bh(&skb->sk->sk_callback_lock);
if (skb->sk->sk_socket && skb->sk->sk_socket->file)
printk("UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_uid,
- skb->sk->sk_socket->file->f_gid);
+ skb->sk->sk_socket->file->f_cred->fsuid,
+ skb->sk->sk_socket->file->f_cred->fsgid);
read_unlock_bh(&skb->sk->sk_callback_lock);
}
diff --git a/net/ipv4/netfilter/ipt_addrtype.c b/net/ipv4/netfilter/ipt_addrtype.c
index 88762f02779d..3b216be3bc9f 100644
--- a/net/ipv4/netfilter/ipt_addrtype.c
+++ b/net/ipv4/netfilter/ipt_addrtype.c
@@ -23,24 +23,25 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_DESCRIPTION("Xtables: address type match for IPv4");
-static inline bool match_type(const struct net_device *dev, __be32 addr,
- u_int16_t mask)
+static inline bool match_type(struct net *net, const struct net_device *dev,
+ __be32 addr, u_int16_t mask)
{
- return !!(mask & (1 << inet_dev_addr_type(&init_net, dev, addr)));
+ return !!(mask & (1 << inet_dev_addr_type(net, dev, addr)));
}
static bool
addrtype_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
{
+ struct net *net = dev_net(par->in ? par->in : par->out);
const struct ipt_addrtype_info *info = par->matchinfo;
const struct iphdr *iph = ip_hdr(skb);
bool ret = true;
if (info->source)
- ret &= match_type(NULL, iph->saddr, info->source) ^
+ ret &= match_type(net, NULL, iph->saddr, info->source) ^
info->invert_source;
if (info->dest)
- ret &= match_type(NULL, iph->daddr, info->dest) ^
+ ret &= match_type(net, NULL, iph->daddr, info->dest) ^
info->invert_dest;
return ret;
@@ -49,6 +50,7 @@ addrtype_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
static bool
addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par)
{
+ struct net *net = dev_net(par->in ? par->in : par->out);
const struct ipt_addrtype_info_v1 *info = par->matchinfo;
const struct iphdr *iph = ip_hdr(skb);
const struct net_device *dev = NULL;
@@ -60,10 +62,10 @@ addrtype_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par)
dev = par->out;
if (info->source)
- ret &= match_type(dev, iph->saddr, info->source) ^
+ ret &= match_type(net, dev, iph->saddr, info->source) ^
(info->flags & IPT_ADDRTYPE_INVERT_SOURCE);
if (ret && info->dest)
- ret &= match_type(dev, iph->daddr, info->dest) ^
+ ret &= match_type(net, dev, iph->daddr, info->dest) ^
!!(info->flags & IPT_ADDRTYPE_INVERT_DEST);
return ret;
}
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 4a7c35275396..b2141e11575e 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -60,9 +60,8 @@ static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
static int ipv4_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
- NIPQUAD(tuple->src.u3.ip),
- NIPQUAD(tuple->dst.u3.ip));
+ return seq_printf(s, "src=%pI4 dst=%pI4 ",
+ &tuple->src.u3.ip, &tuple->dst.u3.ip);
}
static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
@@ -198,7 +197,7 @@ static ctl_table ip_ct_sysctl_table[] = {
.data = &nf_conntrack_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_COUNT,
@@ -206,7 +205,7 @@ static ctl_table ip_ct_sysctl_table[] = {
.data = &init_net.ct.count,
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_BUCKETS,
@@ -214,7 +213,7 @@ static ctl_table ip_ct_sysctl_table[] = {
.data = &nf_conntrack_htable_size,
.maxlen = sizeof(unsigned int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_CHECKSUM,
@@ -222,7 +221,7 @@ static ctl_table ip_ct_sysctl_table[] = {
.data = &init_net.ct.sysctl_checksum,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_LOG_INVALID,
@@ -230,8 +229,8 @@ static ctl_table ip_ct_sysctl_table[] = {
.data = &init_net.ct.sysctl_log_invalid,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &log_invalid_proto_min,
.extra2 = &log_invalid_proto_max,
},
@@ -284,17 +283,17 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
.tuple.dst.u3.ip;
memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
- pr_debug("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n",
- NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ pr_debug("SO_ORIGINAL_DST: %pI4 %u\n",
+ &sin.sin_addr.s_addr, ntohs(sin.sin_port));
nf_ct_put(ct);
if (copy_to_user(user, &sin, sizeof(sin)) != 0)
return -EFAULT;
else
return 0;
}
- pr_debug("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n",
- NIPQUAD(tuple.src.u3.ip), ntohs(tuple.src.u.tcp.port),
- NIPQUAD(tuple.dst.u3.ip), ntohs(tuple.dst.u.tcp.port));
+ pr_debug("SO_ORIGINAL_DST: Can't find %pI4/%u-%pI4/%u.\n",
+ &tuple.src.u3.ip, ntohs(tuple.src.u.tcp.port),
+ &tuple.dst.u3.ip, ntohs(tuple.dst.u.tcp.port));
return -ENOENT;
}
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
index 313ebf00ee36..6ba5c557690c 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
@@ -291,7 +291,7 @@ static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
if (*pos == 0)
return SEQ_START_TOKEN;
- for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
@@ -306,7 +306,7 @@ static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
struct net *net = seq_file_net(seq);
int cpu;
- for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
index 4e8879220222..1fd3ef7718b6 100644
--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
@@ -272,7 +272,7 @@ static struct ctl_table icmp_sysctl_table[] = {
.data = &nf_ct_icmp_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
@@ -285,7 +285,7 @@ static struct ctl_table icmp_compat_sysctl_table[] = {
.data = &nf_ct_icmp_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index ee47bf28c825..7e8e6fc75413 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -119,10 +119,9 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
(ntohl(addr.ip) & 0xff000000) == 0x7f000000)
i = 0;
- pr_debug("nf_nat_ras: set signal address "
- "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(addr.ip), port,
- NIPQUAD(ct->tuplehash[!dir].tuple.dst.u3.ip),
+ pr_debug("nf_nat_ras: set signal address %pI4:%hu->%pI4:%hu\n",
+ &addr.ip, port,
+ &ct->tuplehash[!dir].tuple.dst.u3.ip,
info->sig_port[!dir]);
return set_h225_addr(skb, data, 0, &taddr[i],
&ct->tuplehash[!dir].
@@ -131,10 +130,9 @@ static int set_sig_addr(struct sk_buff *skb, struct nf_conn *ct,
} else if (addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
port == info->sig_port[dir]) {
/* GK->GW */
- pr_debug("nf_nat_ras: set signal address "
- "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(addr.ip), port,
- NIPQUAD(ct->tuplehash[!dir].tuple.src.u3.ip),
+ pr_debug("nf_nat_ras: set signal address %pI4:%hu->%pI4:%hu\n",
+ &addr.ip, port,
+ &ct->tuplehash[!dir].tuple.src.u3.ip,
info->sig_port[!dir]);
return set_h225_addr(skb, data, 0, &taddr[i],
&ct->tuplehash[!dir].
@@ -162,10 +160,9 @@ static int set_ras_addr(struct sk_buff *skb, struct nf_conn *ct,
if (get_h225_addr(ct, *data, &taddr[i], &addr, &port) &&
addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
port == ct->tuplehash[dir].tuple.src.u.udp.port) {
- pr_debug("nf_nat_ras: set rasAddress "
- "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(addr.ip), ntohs(port),
- NIPQUAD(ct->tuplehash[!dir].tuple.dst.u3.ip),
+ pr_debug("nf_nat_ras: set rasAddress %pI4:%hu->%pI4:%hu\n",
+ &addr.ip, ntohs(port),
+ &ct->tuplehash[!dir].tuple.dst.u3.ip,
ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port));
return set_h225_addr(skb, data, 0, &taddr[i],
&ct->tuplehash[!dir].tuple.dst.u3,
@@ -257,15 +254,15 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
}
/* Success */
- pr_debug("nf_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(rtp_exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_h323: expect RTP %pI4:%hu->%pI4:%hu\n",
+ &rtp_exp->tuple.src.u3.ip,
ntohs(rtp_exp->tuple.src.u.udp.port),
- NIPQUAD(rtp_exp->tuple.dst.u3.ip),
+ &rtp_exp->tuple.dst.u3.ip,
ntohs(rtp_exp->tuple.dst.u.udp.port));
- pr_debug("nf_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(rtcp_exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_h323: expect RTCP %pI4:%hu->%pI4:%hu\n",
+ &rtcp_exp->tuple.src.u3.ip,
ntohs(rtcp_exp->tuple.src.u.udp.port),
- NIPQUAD(rtcp_exp->tuple.dst.u3.ip),
+ &rtcp_exp->tuple.dst.u3.ip,
ntohs(rtcp_exp->tuple.dst.u.udp.port));
return 0;
@@ -307,10 +304,10 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct,
return -1;
}
- pr_debug("nf_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_h323: expect T.120 %pI4:%hu->%pI4:%hu\n",
+ &exp->tuple.src.u3.ip,
ntohs(exp->tuple.src.u.tcp.port),
- NIPQUAD(exp->tuple.dst.u3.ip),
+ &exp->tuple.dst.u3.ip,
ntohs(exp->tuple.dst.u.tcp.port));
return 0;
@@ -361,10 +358,10 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
return -1;
}
- pr_debug("nf_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_q931: expect H.245 %pI4:%hu->%pI4:%hu\n",
+ &exp->tuple.src.u3.ip,
ntohs(exp->tuple.src.u.tcp.port),
- NIPQUAD(exp->tuple.dst.u3.ip),
+ &exp->tuple.dst.u3.ip,
ntohs(exp->tuple.dst.u.tcp.port));
return 0;
@@ -455,10 +452,10 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
}
/* Success */
- pr_debug("nf_nat_ras: expect Q.931 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_ras: expect Q.931 %pI4:%hu->%pI4:%hu\n",
+ &exp->tuple.src.u3.ip,
ntohs(exp->tuple.src.u.tcp.port),
- NIPQUAD(exp->tuple.dst.u3.ip),
+ &exp->tuple.dst.u3.ip,
ntohs(exp->tuple.dst.u.tcp.port));
return 0;
@@ -524,11 +521,10 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
}
/* Success */
- pr_debug("nf_nat_q931: expect Call Forwarding "
- "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
- NIPQUAD(exp->tuple.src.u3.ip),
+ pr_debug("nf_nat_q931: expect Call Forwarding %pI4:%hu->%pI4:%hu\n",
+ &exp->tuple.src.u3.ip,
ntohs(exp->tuple.src.u.tcp.port),
- NIPQUAD(exp->tuple.dst.u3.ip),
+ &exp->tuple.dst.u3.ip,
ntohs(exp->tuple.dst.u.tcp.port));
return 0;
diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c
index fe6f9cef6c85..ea83a886b03e 100644
--- a/net/ipv4/netfilter/nf_nat_irc.c
+++ b/net/ipv4/netfilter/nf_nat_irc.c
@@ -55,8 +55,8 @@ static unsigned int help(struct sk_buff *skb,
ip = ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip);
sprintf(buffer, "%u %u", ip, port);
- pr_debug("nf_nat_irc: inserting '%s' == %u.%u.%u.%u, port %u\n",
- buffer, NIPQUAD(ip), port);
+ pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n",
+ buffer, &ip, port);
ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
matchoff, matchlen, buffer,
diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c
index 8d489e746b21..a7eb04719044 100644
--- a/net/ipv4/netfilter/nf_nat_rule.c
+++ b/net/ipv4/netfilter/nf_nat_rule.c
@@ -86,25 +86,6 @@ ipt_snat_target(struct sk_buff *skb, const struct xt_target_param *par)
return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC);
}
-/* Before 2.6.11 we did implicit source NAT if required. Warn about change. */
-static void warn_if_extra_mangle(struct net *net, __be32 dstip, __be32 srcip)
-{
- static int warned = 0;
- struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } };
- struct rtable *rt;
-
- if (ip_route_output_key(net, &rt, &fl) != 0)
- return;
-
- if (rt->rt_src != srcip && !warned) {
- printk("NAT: no longer support implicit source local NAT\n");
- printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n",
- NIPQUAD(srcip), NIPQUAD(dstip));
- warned = 1;
- }
- ip_rt_put(rt);
-}
-
static unsigned int
ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par)
{
@@ -120,11 +101,6 @@ ipt_dnat_target(struct sk_buff *skb, const struct xt_target_param *par)
/* Connection must be valid and new. */
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
- if (par->hooknum == NF_INET_LOCAL_OUT &&
- mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)
- warn_if_extra_mangle(dev_net(par->out), ip_hdr(skb)->daddr,
- mr->range[0].min_ip);
-
return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_DST);
}
@@ -166,8 +142,7 @@ alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
struct nf_nat_range range
= { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } };
- pr_debug("Allocating NULL binding for %p (%u.%u.%u.%u)\n",
- ct, NIPQUAD(ip));
+ pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, &ip);
return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
}
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index 14544320c545..07d61a57613c 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -74,8 +74,7 @@ static int map_addr(struct sk_buff *skb,
if (newaddr == addr->ip && newport == port)
return 1;
- buflen = sprintf(buffer, "%u.%u.%u.%u:%u",
- NIPQUAD(newaddr), ntohs(newport));
+ buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport));
return mangle_packet(skb, dptr, datalen, matchoff, matchlen,
buffer, buflen);
@@ -152,8 +151,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
&addr) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
- __be32 ip = ct->tuplehash[!dir].tuple.dst.u3.ip;
- buflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
+ buflen = sprintf(buffer, "%pI4",
+ &ct->tuplehash[!dir].tuple.dst.u3.ip);
if (!mangle_packet(skb, dptr, datalen, poff, plen,
buffer, buflen))
return NF_DROP;
@@ -166,8 +165,8 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
&addr) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
- __be32 ip = ct->tuplehash[!dir].tuple.src.u3.ip;
- buflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
+ buflen = sprintf(buffer, "%pI4",
+ &ct->tuplehash[!dir].tuple.src.u3.ip);
if (!mangle_packet(skb, dptr, datalen, poff, plen,
buffer, buflen))
return NF_DROP;
@@ -279,8 +278,7 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb,
if (exp->tuple.dst.u3.ip != exp->saved_ip ||
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
- buflen = sprintf(buffer, "%u.%u.%u.%u:%u",
- NIPQUAD(newip), port);
+ buflen = sprintf(buffer, "%pI4:%u", &newip, port);
if (!mangle_packet(skb, dptr, datalen, matchoff, matchlen,
buffer, buflen))
goto err;
@@ -345,7 +343,7 @@ static unsigned int ip_nat_sdp_addr(struct sk_buff *skb, const char **dptr,
char buffer[sizeof("nnn.nnn.nnn.nnn")];
unsigned int buflen;
- buflen = sprintf(buffer, NIPQUAD_FMT, NIPQUAD(addr->ip));
+ buflen = sprintf(buffer, "%pI4", &addr->ip);
if (mangle_sdp_packet(skb, dptr, dataoff, datalen, type, term,
buffer, buflen))
return 0;
@@ -380,7 +378,7 @@ static unsigned int ip_nat_sdp_session(struct sk_buff *skb, const char **dptr,
unsigned int buflen;
/* Mangle session description owner and contact addresses */
- buflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(addr->ip));
+ buflen = sprintf(buffer, "%pI4", &addr->ip);
if (mangle_sdp_packet(skb, dptr, dataoff, datalen,
SDP_HDR_OWNER_IP4, SDP_HDR_MEDIA,
buffer, buflen))
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index 8303e4b406c0..182f845de92f 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -930,8 +930,8 @@ static inline void mangle_address(unsigned char *begin,
}
if (debug)
- printk(KERN_DEBUG "bsalg: mapped %u.%u.%u.%u to "
- "%u.%u.%u.%u\n", NIPQUAD(old), NIPQUAD(*addr));
+ printk(KERN_DEBUG "bsalg: mapped %pI4 to %pI4\n",
+ &old, addr);
}
}
@@ -1267,9 +1267,8 @@ static int help(struct sk_buff *skb, unsigned int protoff,
*/
if (ntohs(udph->len) != skb->len - (iph->ihl << 2)) {
if (net_ratelimit())
- printk(KERN_WARNING "SNMP: dropping malformed packet "
- "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n",
- NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ printk(KERN_WARNING "SNMP: dropping malformed packet src=%pI4 dst=%pI4\n",
+ &iph->saddr, &iph->daddr);
return NF_DROP;
}
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index a631a1f110ca..eb62e58bff79 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -38,6 +38,7 @@
#include <net/tcp.h>
#include <net/udp.h>
#include <net/udplite.h>
+#include <linux/bottom_half.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -50,12 +51,17 @@
static int sockstat_seq_show(struct seq_file *seq, void *v)
{
struct net *net = seq->private;
+ int orphans, sockets;
+
+ local_bh_disable();
+ orphans = percpu_counter_sum_positive(&tcp_orphan_count),
+ sockets = percpu_counter_sum_positive(&tcp_sockets_allocated),
+ local_bh_enable();
socket_seq_show(seq);
seq_printf(seq, "TCP: inuse %d orphan %d tw %d alloc %d mem %d\n",
- sock_prot_inuse_get(net, &tcp_prot),
- atomic_read(&tcp_orphan_count),
- tcp_death_row.tw_count, atomic_read(&tcp_sockets_allocated),
+ sock_prot_inuse_get(net, &tcp_prot), orphans,
+ tcp_death_row.tw_count, sockets,
atomic_read(&tcp_memory_allocated));
seq_printf(seq, "UDP: inuse %d mem %d\n",
sock_prot_inuse_get(net, &udp_prot),
@@ -234,6 +240,9 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPSpuriousRTOs", LINUX_MIB_TCPSPURIOUSRTOS),
SNMP_MIB_ITEM("TCPMD5NotFound", LINUX_MIB_TCPMD5NOTFOUND),
SNMP_MIB_ITEM("TCPMD5Unexpected", LINUX_MIB_TCPMD5UNEXPECTED),
+ SNMP_MIB_ITEM("TCPSackShifted", LINUX_MIB_SACKSHIFTED),
+ SNMP_MIB_ITEM("TCPSackMerged", LINUX_MIB_SACKMERGED),
+ SNMP_MIB_ITEM("TCPSackShiftFallback", LINUX_MIB_SACKSHIFTFALLBACK),
SNMP_MIB_SENTINEL
};
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index cd975743bcd2..dff8bc4e0fac 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -247,7 +247,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
}
if (inet->recverr) {
- struct iphdr *iph = (struct iphdr*)skb->data;
+ struct iphdr *iph = (struct iphdr *)skb->data;
u8 *payload = skb->data + (iph->ihl << 2);
if (inet->hdrincl)
@@ -465,7 +465,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
*/
if (msg->msg_namelen) {
- struct sockaddr_in *usin = (struct sockaddr_in*)msg->msg_name;
+ struct sockaddr_in *usin = (struct sockaddr_in *)msg->msg_name;
err = -EINVAL;
if (msg->msg_namelen < sizeof(*usin))
goto out;
@@ -572,7 +572,7 @@ back_from_confirm:
ipc.addr = rt->rt_dst;
lock_sock(sk);
err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, len, 0,
- &ipc, rt, msg->msg_flags);
+ &ipc, &rt, msg->msg_flags);
if (err)
ip_flush_pending_frames(sk);
else if (!(msg->msg_flags & MSG_MORE))
@@ -851,7 +851,7 @@ struct proto raw_prot = {
static struct sock *raw_get_first(struct seq_file *seq)
{
struct sock *sk;
- struct raw_iter_state* state = raw_seq_private(seq);
+ struct raw_iter_state *state = raw_seq_private(seq);
for (state->bucket = 0; state->bucket < RAW_HTABLE_SIZE;
++state->bucket) {
@@ -868,7 +868,7 @@ found:
static struct sock *raw_get_next(struct seq_file *seq, struct sock *sk)
{
- struct raw_iter_state* state = raw_seq_private(seq);
+ struct raw_iter_state *state = raw_seq_private(seq);
do {
sk = sk_next(sk);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 2ea6dcc3e2cc..97f71153584f 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -129,6 +129,7 @@ static int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ;
static int ip_rt_min_pmtu __read_mostly = 512 + 20 + 20;
static int ip_rt_min_advmss __read_mostly = 256;
static int ip_rt_secret_interval __read_mostly = 10 * 60 * HZ;
+static int rt_chain_length_max __read_mostly = 20;
static void rt_worker_func(struct work_struct *work);
static DECLARE_DELAYED_WORK(expires_work, rt_worker_func);
@@ -145,6 +146,7 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst);
static void ipv4_link_failure(struct sk_buff *skb);
static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
static int rt_garbage_collect(struct dst_ops *ops);
+static void rt_emergency_hash_rebuild(struct net *net);
static struct dst_ops ipv4_dst_ops = {
@@ -158,7 +160,6 @@ static struct dst_ops ipv4_dst_ops = {
.link_failure = ipv4_link_failure,
.update_pmtu = ip_rt_update_pmtu,
.local_out = __ip_local_out,
- .entry_size = sizeof(struct rtable),
.entries = ATOMIC_INIT(0),
};
@@ -201,6 +202,7 @@ const __u8 ip_tos2prio[16] = {
struct rt_hash_bucket {
struct rtable *chain;
};
+
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || \
defined(CONFIG_PROVE_LOCKING)
/*
@@ -427,7 +429,7 @@ static void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos)
if (*pos == 0)
return SEQ_START_TOKEN;
- for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
@@ -440,7 +442,7 @@ static void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
int cpu;
- for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
@@ -674,6 +676,20 @@ static inline u32 rt_score(struct rtable *rt)
return score;
}
+static inline bool rt_caching(const struct net *net)
+{
+ return net->ipv4.current_rt_cache_rebuild_count <=
+ net->ipv4.sysctl_rt_cache_rebuild_count;
+}
+
+static inline bool compare_hash_inputs(const struct flowi *fl1,
+ const struct flowi *fl2)
+{
+ return (__force u32)(((fl1->nl_u.ip4_u.daddr ^ fl2->nl_u.ip4_u.daddr) |
+ (fl1->nl_u.ip4_u.saddr ^ fl2->nl_u.ip4_u.saddr) |
+ (fl1->iif ^ fl2->iif)) == 0);
+}
+
static inline int compare_keys(struct flowi *fl1, struct flowi *fl2)
{
return ((__force u32)((fl1->nl_u.ip4_u.daddr ^ fl2->nl_u.ip4_u.daddr) |
@@ -753,11 +769,24 @@ static void rt_do_flush(int process_context)
}
}
+/*
+ * While freeing expired entries, we compute average chain length
+ * and standard deviation, using fixed-point arithmetic.
+ * This to have an estimation of rt_chain_length_max
+ * rt_chain_length_max = max(elasticity, AVG + 4*SD)
+ * We use 3 bits for frational part, and 29 (or 61) for magnitude.
+ */
+
+#define FRACT_BITS 3
+#define ONE (1UL << FRACT_BITS)
+
static void rt_check_expire(void)
{
static unsigned int rover;
unsigned int i = rover, goal;
struct rtable *rth, **rthp;
+ unsigned long length = 0, samples = 0;
+ unsigned long sum = 0, sum2 = 0;
u64 mult;
mult = ((u64)ip_rt_gc_interval) << rt_hash_log;
@@ -766,6 +795,7 @@ static void rt_check_expire(void)
goal = (unsigned int)mult;
if (goal > rt_hash_mask)
goal = rt_hash_mask + 1;
+ length = 0;
for (; goal > 0; goal--) {
unsigned long tmo = ip_rt_gc_timeout;
@@ -775,6 +805,8 @@ static void rt_check_expire(void)
if (need_resched())
cond_resched();
+ samples++;
+
if (*rthp == NULL)
continue;
spin_lock_bh(rt_hash_lock_addr(i));
@@ -789,11 +821,29 @@ static void rt_check_expire(void)
if (time_before_eq(jiffies, rth->u.dst.expires)) {
tmo >>= 1;
rthp = &rth->u.dst.rt_next;
+ /*
+ * Only bump our length if the hash
+ * inputs on entries n and n+1 are not
+ * the same, we only count entries on
+ * a chain with equal hash inputs once
+ * so that entries for different QOS
+ * levels, and other non-hash input
+ * attributes don't unfairly skew
+ * the length computation
+ */
+ if ((*rthp == NULL) ||
+ !compare_hash_inputs(&(*rthp)->fl,
+ &rth->fl))
+ length += ONE;
continue;
}
} else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) {
tmo >>= 1;
rthp = &rth->u.dst.rt_next;
+ if ((*rthp == NULL) ||
+ !compare_hash_inputs(&(*rthp)->fl,
+ &rth->fl))
+ length += ONE;
continue;
}
@@ -802,6 +852,15 @@ static void rt_check_expire(void)
rt_free(rth);
}
spin_unlock_bh(rt_hash_lock_addr(i));
+ sum += length;
+ sum2 += length*length;
+ }
+ if (samples) {
+ unsigned long avg = sum / samples;
+ unsigned long sd = int_sqrt(sum2 / samples - avg*avg);
+ rt_chain_length_max = max_t(unsigned long,
+ ip_rt_gc_elasticity,
+ (avg + 4*sd) >> FRACT_BITS);
}
rover = i;
}
@@ -851,6 +910,26 @@ static void rt_secret_rebuild(unsigned long __net)
mod_timer(&net->ipv4.rt_secret_timer, jiffies + ip_rt_secret_interval);
}
+static void rt_secret_rebuild_oneshot(struct net *net)
+{
+ del_timer_sync(&net->ipv4.rt_secret_timer);
+ rt_cache_invalidate(net);
+ if (ip_rt_secret_interval) {
+ net->ipv4.rt_secret_timer.expires += ip_rt_secret_interval;
+ add_timer(&net->ipv4.rt_secret_timer);
+ }
+}
+
+static void rt_emergency_hash_rebuild(struct net *net)
+{
+ if (net_ratelimit()) {
+ printk(KERN_WARNING "Route hash chain too long!\n");
+ printk(KERN_WARNING "Adjust your secret_interval!\n");
+ }
+
+ rt_secret_rebuild_oneshot(net);
+}
+
/*
Short description of GC goals.
@@ -989,6 +1068,7 @@ out: return 0;
static int rt_intern_hash(unsigned hash, struct rtable *rt, struct rtable **rp)
{
struct rtable *rth, **rthp;
+ struct rtable *rthi;
unsigned long now;
struct rtable *cand, **candp;
u32 min_score;
@@ -1002,7 +1082,13 @@ restart:
candp = NULL;
now = jiffies;
+ if (!rt_caching(dev_net(rt->u.dst.dev))) {
+ rt_drop(rt);
+ return 0;
+ }
+
rthp = &rt_hash_table[hash].chain;
+ rthi = NULL;
spin_lock_bh(rt_hash_lock_addr(hash));
while ((rth = *rthp) != NULL) {
@@ -1048,6 +1134,17 @@ restart:
chain_length++;
rthp = &rth->u.dst.rt_next;
+
+ /*
+ * check to see if the next entry in the chain
+ * contains the same hash input values as rt. If it does
+ * This is where we will insert into the list, instead of
+ * at the head. This groups entries that differ by aspects not
+ * relvant to the hash function together, which we use to adjust
+ * our chain length
+ */
+ if (*rthp && compare_hash_inputs(&(*rthp)->fl, &rt->fl))
+ rthi = rth;
}
if (cand) {
@@ -1061,6 +1158,16 @@ restart:
*candp = cand->u.dst.rt_next;
rt_free(cand);
}
+ } else {
+ if (chain_length > rt_chain_length_max) {
+ struct net *net = dev_net(rt->u.dst.dev);
+ int num = ++net->ipv4.current_rt_cache_rebuild_count;
+ if (!rt_caching(dev_net(rt->u.dst.dev))) {
+ printk(KERN_WARNING "%s: %d rebuilds is over limit, route caching disabled\n",
+ rt->u.dst.dev->name, num);
+ }
+ rt_emergency_hash_rebuild(dev_net(rt->u.dst.dev));
+ }
}
/* Try to bind route to arp only if it is output
@@ -1098,14 +1205,17 @@ restart:
}
}
- rt->u.dst.rt_next = rt_hash_table[hash].chain;
+ if (rthi)
+ rt->u.dst.rt_next = rthi->u.dst.rt_next;
+ else
+ rt->u.dst.rt_next = rt_hash_table[hash].chain;
+
#if RT_CACHE_DEBUG >= 2
if (rt->u.dst.rt_next) {
struct rtable *trt;
- printk(KERN_DEBUG "rt_cache @%02x: " NIPQUAD_FMT, hash,
- NIPQUAD(rt->rt_dst));
+ printk(KERN_DEBUG "rt_cache @%02x: %pI4", hash, &rt->rt_dst);
for (trt = rt->u.dst.rt_next; trt; trt = trt->u.dst.rt_next)
- printk(" . " NIPQUAD_FMT, NIPQUAD(trt->rt_dst));
+ printk(" . %pI4", &trt->rt_dst);
printk("\n");
}
#endif
@@ -1114,7 +1224,11 @@ restart:
* previous writes to rt are comitted to memory
* before making rt visible to other CPUS.
*/
- rcu_assign_pointer(rt_hash_table[hash].chain, rt);
+ if (rthi)
+ rcu_assign_pointer(rthi->u.dst.rt_next, rt);
+ else
+ rcu_assign_pointer(rt_hash_table[hash].chain, rt);
+
spin_unlock_bh(rt_hash_lock_addr(hash));
*rp = rt;
return 0;
@@ -1217,6 +1331,9 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
|| ipv4_is_zeronet(new_gw))
goto reject_redirect;
+ if (!rt_caching(net))
+ goto reject_redirect;
+
if (!IN_DEV_SHARED_MEDIA(in_dev)) {
if (!inet_addr_onlink(in_dev, new_gw, old_gw))
goto reject_redirect;
@@ -1267,7 +1384,6 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
/* Copy all the information. */
*rt = *rth;
- INIT_RCU_HEAD(&rt->u.dst.rcu_head);
rt->u.dst.__use = 1;
atomic_set(&rt->u.dst.__refcnt, 1);
rt->u.dst.child = NULL;
@@ -1280,7 +1396,9 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
rt->u.dst.path = &rt->u.dst;
rt->u.dst.neighbour = NULL;
rt->u.dst.hh = NULL;
+#ifdef CONFIG_XFRM
rt->u.dst.xfrm = NULL;
+#endif
rt->rt_genid = rt_genid(net);
rt->rt_flags |= RTCF_REDIRECTED;
@@ -1324,11 +1442,10 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
reject_redirect:
#ifdef CONFIG_IP_ROUTE_VERBOSE
if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
- printk(KERN_INFO "Redirect from " NIPQUAD_FMT " on %s about "
- NIPQUAD_FMT " ignored.\n"
- " Advised path = " NIPQUAD_FMT " -> " NIPQUAD_FMT "\n",
- NIPQUAD(old_gw), dev->name, NIPQUAD(new_gw),
- NIPQUAD(saddr), NIPQUAD(daddr));
+ printk(KERN_INFO "Redirect from %pI4 on %s about %pI4 ignored.\n"
+ " Advised path = %pI4 -> %pI4\n",
+ &old_gw, dev->name, &new_gw,
+ &saddr, &daddr);
#endif
in_dev_put(in_dev);
}
@@ -1348,9 +1465,8 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
rt->fl.oif,
rt_genid(dev_net(dst->dev)));
#if RT_CACHE_DEBUG >= 1
- printk(KERN_DEBUG "ipv4_negative_advice: redirect to "
- NIPQUAD_FMT "/%02x dropped\n",
- NIPQUAD(rt->rt_dst), rt->fl.fl4_tos);
+ printk(KERN_DEBUG "ipv4_negative_advice: redirect to %pI4/%02x dropped\n",
+ &rt->rt_dst, rt->fl.fl4_tos);
#endif
rt_del(hash, rt);
ret = NULL;
@@ -1414,10 +1530,9 @@ void ip_rt_send_redirect(struct sk_buff *skb)
if (IN_DEV_LOG_MARTIANS(in_dev) &&
rt->u.dst.rate_tokens == ip_rt_redirect_number &&
net_ratelimit())
- printk(KERN_WARNING "host " NIPQUAD_FMT "/if%d ignores "
- "redirects for " NIPQUAD_FMT " to " NIPQUAD_FMT ".\n",
- NIPQUAD(rt->rt_src), rt->rt_iif,
- NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_gateway));
+ printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n",
+ &rt->rt_src, rt->rt_iif,
+ &rt->rt_dst, &rt->rt_gateway);
#endif
}
out:
@@ -1610,8 +1725,8 @@ static void ipv4_link_failure(struct sk_buff *skb)
static int ip_rt_bug(struct sk_buff *skb)
{
- printk(KERN_DEBUG "ip_rt_bug: " NIPQUAD_FMT " -> " NIPQUAD_FMT ", %s\n",
- NIPQUAD(ip_hdr(skb)->saddr), NIPQUAD(ip_hdr(skb)->daddr),
+ printk(KERN_DEBUG "ip_rt_bug: %pI4 -> %pI4, %s\n",
+ &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
skb->dev ? skb->dev->name : "?");
kfree_skb(skb);
return 0;
@@ -1788,9 +1903,8 @@ static void ip_handle_martian_source(struct net_device *dev,
* RFC1812 recommendation, if source is martian,
* the only hint is MAC header.
*/
- printk(KERN_WARNING "martian source " NIPQUAD_FMT " from "
- NIPQUAD_FMT", on dev %s\n",
- NIPQUAD(daddr), NIPQUAD(saddr), dev->name);
+ printk(KERN_WARNING "martian source %pI4 from %pI4, on dev %s\n",
+ &daddr, &saddr, dev->name);
if (dev->hard_header_len && skb_mac_header_was_set(skb)) {
int i;
const unsigned char *p = skb_mac_header(skb);
@@ -2099,9 +2213,8 @@ martian_destination:
RT_CACHE_STAT_INC(in_martian_dst);
#ifdef CONFIG_IP_ROUTE_VERBOSE
if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
- printk(KERN_WARNING "martian destination " NIPQUAD_FMT " from "
- NIPQUAD_FMT ", dev %s\n",
- NIPQUAD(daddr), NIPQUAD(saddr), dev->name);
+ printk(KERN_WARNING "martian destination %pI4 from %pI4, dev %s\n",
+ &daddr, &saddr, dev->name);
#endif
e_hostunreach:
@@ -2130,6 +2243,10 @@ int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr,
struct net *net;
net = dev_net(dev);
+
+ if (!rt_caching(net))
+ goto skip_cache;
+
tos &= IPTOS_RT_MASK;
hash = rt_hash(daddr, saddr, iif, rt_genid(net));
@@ -2154,6 +2271,7 @@ int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr,
}
rcu_read_unlock();
+skip_cache:
/* Multicast recognition logic is moved from route cache to here.
The problem was that too many Ethernet cards have broken/missing
hardware multicast filters :-( As result the host on multicasting
@@ -2539,6 +2657,9 @@ int __ip_route_output_key(struct net *net, struct rtable **rp,
unsigned hash;
struct rtable *rth;
+ if (!rt_caching(net))
+ goto slow_output;
+
hash = rt_hash(flp->fl4_dst, flp->fl4_src, flp->oif, rt_genid(net));
rcu_read_lock_bh();
@@ -2563,6 +2684,7 @@ int __ip_route_output_key(struct net *net, struct rtable **rp,
}
rcu_read_unlock_bh();
+slow_output:
return ip_route_output_slow(net, rp, flp);
}
@@ -2578,7 +2700,6 @@ static struct dst_ops ipv4_dst_blackhole_ops = {
.destroy = ipv4_dst_destroy,
.check = ipv4_dst_check,
.update_pmtu = ipv4_rt_blackhole_update_pmtu,
- .entry_size = sizeof(struct rtable),
.entries = ATOMIC_INIT(0),
};
@@ -2640,7 +2761,7 @@ int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp,
flp->fl4_src = (*rp)->rt_src;
if (!flp->fl4_dst)
flp->fl4_dst = (*rp)->rt_dst;
- err = __xfrm_lookup((struct dst_entry **)rp, flp, sk,
+ err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk,
flags ? XFRM_LOOKUP_WAIT : 0);
if (err == -EREMOTE)
err = ipv4_dst_blackhole(net, rp, flp);
@@ -2995,7 +3116,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ipv4_dst_ops.gc_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_MAX_SIZE,
@@ -3003,7 +3124,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_max_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
/* Deprecated. Use gc_min_interval_ms */
@@ -3013,8 +3134,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV4_ROUTE_GC_MIN_INTERVAL_MS,
@@ -3022,8 +3143,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies,
},
{
.ctl_name = NET_IPV4_ROUTE_GC_TIMEOUT,
@@ -3031,8 +3152,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_gc_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV4_ROUTE_GC_INTERVAL,
@@ -3040,8 +3161,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_gc_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV4_ROUTE_REDIRECT_LOAD,
@@ -3049,7 +3170,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_redirect_load,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_REDIRECT_NUMBER,
@@ -3057,7 +3178,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_redirect_number,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_REDIRECT_SILENCE,
@@ -3065,7 +3186,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_redirect_silence,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_ERROR_COST,
@@ -3073,7 +3194,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_error_cost,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_ERROR_BURST,
@@ -3081,7 +3202,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_error_burst,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_GC_ELASTICITY,
@@ -3089,7 +3210,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_gc_elasticity,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_MTU_EXPIRES,
@@ -3097,8 +3218,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_mtu_expires,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV4_ROUTE_MIN_PMTU,
@@ -3106,7 +3227,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_min_pmtu,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_MIN_ADVMSS,
@@ -3114,7 +3235,7 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_min_advmss,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_ROUTE_SECRET_INTERVAL,
@@ -3122,8 +3243,8 @@ static ctl_table ipv4_route_table[] = {
.data = &ip_rt_secret_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &ipv4_sysctl_rt_secret_interval,
- .strategy = &ipv4_sysctl_rt_secret_interval_strategy,
+ .proc_handler = ipv4_sysctl_rt_secret_interval,
+ .strategy = ipv4_sysctl_rt_secret_interval_strategy,
},
{ .ctl_name = 0 }
};
@@ -3151,8 +3272,8 @@ static struct ctl_table ipv4_route_flush_table[] = {
.procname = "flush",
.maxlen = sizeof(int),
.mode = 0200,
- .proc_handler = &ipv4_sysctl_rtcache_flush,
- .strategy = &ipv4_sysctl_rtcache_flush_strategy,
+ .proc_handler = ipv4_sysctl_rtcache_flush,
+ .strategy = ipv4_sysctl_rtcache_flush_strategy,
},
{ .ctl_name = 0 },
};
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 1bb10df8ce7d..4710d219f06a 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -195,7 +195,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_timestamps,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_WINDOW_SCALING,
@@ -203,7 +203,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_window_scaling,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_SACK,
@@ -211,7 +211,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_sack,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_RETRANS_COLLAPSE,
@@ -219,7 +219,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_retrans_collapse,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_DEFAULT_TTL,
@@ -227,8 +227,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_ip_default_ttl,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &ipv4_doint_and_flush,
- .strategy = &ipv4_doint_and_flush_strategy,
+ .proc_handler = ipv4_doint_and_flush,
+ .strategy = ipv4_doint_and_flush_strategy,
.extra2 = &init_net,
},
{
@@ -237,7 +237,7 @@ static struct ctl_table ipv4_table[] = {
.data = &ipv4_config.no_pmtu_disc,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_NONLOCAL_BIND,
@@ -245,7 +245,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_ip_nonlocal_bind,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_SYN_RETRIES,
@@ -253,7 +253,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_syn_retries,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_SYNACK_RETRIES,
@@ -261,7 +261,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_synack_retries,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_ORPHANS,
@@ -269,7 +269,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_max_orphans,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_TW_BUCKETS,
@@ -277,7 +277,7 @@ static struct ctl_table ipv4_table[] = {
.data = &tcp_death_row.sysctl_max_tw_buckets,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_DYNADDR,
@@ -285,7 +285,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_ip_dynaddr,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_TIME,
@@ -293,8 +293,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_keepalive_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_PROBES,
@@ -302,7 +302,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_keepalive_probes,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_INTVL,
@@ -310,8 +310,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_keepalive_intvl,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_IPV4_TCP_RETRIES1,
@@ -319,8 +319,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_retries1,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra2 = &tcp_retr1_max
},
{
@@ -329,7 +329,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_retries2,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_FIN_TIMEOUT,
@@ -337,8 +337,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_fin_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
#ifdef CONFIG_SYN_COOKIES
{
@@ -347,7 +347,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_syncookies,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#endif
{
@@ -356,7 +356,7 @@ static struct ctl_table ipv4_table[] = {
.data = &tcp_death_row.sysctl_tw_recycle,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_ABORT_ON_OVERFLOW,
@@ -364,7 +364,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_abort_on_overflow,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_STDURG,
@@ -372,7 +372,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_stdurg,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_RFC1337,
@@ -380,7 +380,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_rfc1337,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_SYN_BACKLOG,
@@ -388,7 +388,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_max_syn_backlog,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_LOCAL_PORT_RANGE,
@@ -396,8 +396,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_local_ports.range,
.maxlen = sizeof(sysctl_local_ports.range),
.mode = 0644,
- .proc_handler = &ipv4_local_port_range,
- .strategy = &ipv4_sysctl_local_port_range,
+ .proc_handler = ipv4_local_port_range,
+ .strategy = ipv4_sysctl_local_port_range,
},
#ifdef CONFIG_IP_MULTICAST
{
@@ -406,7 +406,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_igmp_max_memberships,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#endif
@@ -416,7 +416,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_igmp_max_msf,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_INET_PEER_THRESHOLD,
@@ -424,7 +424,7 @@ static struct ctl_table ipv4_table[] = {
.data = &inet_peer_threshold,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_INET_PEER_MINTTL,
@@ -432,8 +432,8 @@ static struct ctl_table ipv4_table[] = {
.data = &inet_peer_minttl,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_MAXTTL,
@@ -441,8 +441,8 @@ static struct ctl_table ipv4_table[] = {
.data = &inet_peer_maxttl,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_GC_MINTIME,
@@ -450,8 +450,8 @@ static struct ctl_table ipv4_table[] = {
.data = &inet_peer_gc_mintime,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_GC_MAXTIME,
@@ -459,8 +459,8 @@ static struct ctl_table ipv4_table[] = {
.data = &inet_peer_gc_maxtime,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{
.ctl_name = NET_TCP_ORPHAN_RETRIES,
@@ -468,7 +468,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_orphan_retries,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_FACK,
@@ -476,7 +476,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_fack,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_REORDERING,
@@ -484,7 +484,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_reordering,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_ECN,
@@ -492,7 +492,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_ecn,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_DSACK,
@@ -500,7 +500,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_dsack,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_MEM,
@@ -508,7 +508,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_mem,
.maxlen = sizeof(sysctl_tcp_mem),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_WMEM,
@@ -516,7 +516,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_wmem,
.maxlen = sizeof(sysctl_tcp_wmem),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_RMEM,
@@ -524,7 +524,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_rmem,
.maxlen = sizeof(sysctl_tcp_rmem),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_APP_WIN,
@@ -532,7 +532,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_app_win,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_ADV_WIN_SCALE,
@@ -540,7 +540,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_adv_win_scale,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_TW_REUSE,
@@ -548,7 +548,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_tw_reuse,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_FRTO,
@@ -556,7 +556,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_frto,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_FRTO_RESPONSE,
@@ -564,7 +564,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_frto_response,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_LOW_LATENCY,
@@ -572,7 +572,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_low_latency,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_TCP_NO_METRICS_SAVE,
@@ -580,7 +580,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_nometrics_save,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_TCP_MODERATE_RCVBUF,
@@ -588,7 +588,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_moderate_rcvbuf,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_TCP_TSO_WIN_DIVISOR,
@@ -596,15 +596,15 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_tso_win_divisor,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_TCP_CONG_CONTROL,
.procname = "tcp_congestion_control",
.mode = 0644,
.maxlen = TCP_CA_NAME_MAX,
- .proc_handler = &proc_tcp_congestion_control,
- .strategy = &sysctl_tcp_congestion_control,
+ .proc_handler = proc_tcp_congestion_control,
+ .strategy = sysctl_tcp_congestion_control,
},
{
.ctl_name = NET_TCP_ABC,
@@ -612,7 +612,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_abc,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_TCP_MTU_PROBING,
@@ -620,7 +620,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_mtu_probing,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_TCP_BASE_MSS,
@@ -628,7 +628,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_base_mss,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS,
@@ -636,7 +636,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_workaround_signed_windows,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#ifdef CONFIG_NET_DMA
{
@@ -645,7 +645,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_dma_copybreak,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#endif
{
@@ -654,7 +654,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_slow_start_after_idle,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#ifdef CONFIG_NETLABEL
{
@@ -663,7 +663,7 @@ static struct ctl_table ipv4_table[] = {
.data = &cipso_v4_cache_enabled,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_CIPSOV4_CACHE_BUCKET_SIZE,
@@ -671,7 +671,7 @@ static struct ctl_table ipv4_table[] = {
.data = &cipso_v4_cache_bucketsize,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_CIPSOV4_RBM_OPTFMT,
@@ -679,7 +679,7 @@ static struct ctl_table ipv4_table[] = {
.data = &cipso_v4_rbm_optfmt,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_CIPSOV4_RBM_STRICTVALID,
@@ -687,22 +687,22 @@ static struct ctl_table ipv4_table[] = {
.data = &cipso_v4_rbm_strictvalid,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif /* CONFIG_NETLABEL */
{
.procname = "tcp_available_congestion_control",
.maxlen = TCP_CA_BUF_MAX,
.mode = 0444,
- .proc_handler = &proc_tcp_available_congestion_control,
+ .proc_handler = proc_tcp_available_congestion_control,
},
{
.ctl_name = NET_TCP_ALLOWED_CONG_CONTROL,
.procname = "tcp_allowed_congestion_control",
.maxlen = TCP_CA_BUF_MAX,
.mode = 0644,
- .proc_handler = &proc_allowed_congestion_control,
- .strategy = &strategy_allowed_congestion_control,
+ .proc_handler = proc_allowed_congestion_control,
+ .strategy = strategy_allowed_congestion_control,
},
{
.ctl_name = NET_TCP_MAX_SSTHRESH,
@@ -710,7 +710,7 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_tcp_max_ssthresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -718,8 +718,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_udp_mem,
.maxlen = sizeof(sysctl_udp_mem),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &zero
},
{
@@ -728,8 +728,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_udp_rmem_min,
.maxlen = sizeof(sysctl_udp_rmem_min),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &zero
},
{
@@ -738,8 +738,8 @@ static struct ctl_table ipv4_table[] = {
.data = &sysctl_udp_wmem_min,
.maxlen = sizeof(sysctl_udp_wmem_min),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &zero
},
{ .ctl_name = 0 }
@@ -752,7 +752,7 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_echo_ignore_all,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS,
@@ -760,7 +760,7 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_echo_ignore_broadcasts,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES,
@@ -768,7 +768,7 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_ignore_bogus_error_responses,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_ERRORS_USE_INBOUND_IFADDR,
@@ -776,7 +776,7 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_errors_use_inbound_ifaddr,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_RATELIMIT,
@@ -784,8 +784,8 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_ratelimit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies
},
{
.ctl_name = NET_IPV4_ICMP_RATEMASK,
@@ -793,7 +793,15 @@ static struct ctl_table ipv4_net_table[] = {
.data = &init_net.ipv4.sysctl_icmp_ratemask,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "rt_cache_rebuild_count",
+ .data = &init_net.ipv4.sysctl_rt_cache_rebuild_count,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
},
{ }
};
@@ -827,8 +835,12 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
&net->ipv4.sysctl_icmp_ratelimit;
table[5].data =
&net->ipv4.sysctl_icmp_ratemask;
+ table[6].data =
+ &net->ipv4.sysctl_rt_cache_rebuild_count;
}
+ net->ipv4.sysctl_rt_cache_rebuild_count = 4;
+
net->ipv4.ipv4_hdr = register_net_sysctl_table(net,
net_ipv4_ctl_path, table);
if (net->ipv4.ipv4_hdr == NULL)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c5aca0bb116a..f28acf11fc67 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -277,8 +277,7 @@
int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT;
-atomic_t tcp_orphan_count = ATOMIC_INIT(0);
-
+struct percpu_counter tcp_orphan_count;
EXPORT_SYMBOL_GPL(tcp_orphan_count);
int sysctl_tcp_mem[3] __read_mostly;
@@ -290,9 +289,12 @@ EXPORT_SYMBOL(sysctl_tcp_rmem);
EXPORT_SYMBOL(sysctl_tcp_wmem);
atomic_t tcp_memory_allocated; /* Current allocated memory. */
-atomic_t tcp_sockets_allocated; /* Current number of TCP sockets. */
-
EXPORT_SYMBOL(tcp_memory_allocated);
+
+/*
+ * Current number of TCP sockets.
+ */
+struct percpu_counter tcp_sockets_allocated;
EXPORT_SYMBOL(tcp_sockets_allocated);
/*
@@ -1680,7 +1682,7 @@ void tcp_set_state(struct sock *sk, int state)
inet_put_port(sk);
/* fall through */
default:
- if (oldstate==TCP_ESTABLISHED)
+ if (oldstate == TCP_ESTABLISHED)
TCP_DEC_STATS(sock_net(sk), TCP_MIB_CURRESTAB);
}
@@ -1690,7 +1692,7 @@ void tcp_set_state(struct sock *sk, int state)
sk->sk_state = state;
#ifdef STATE_TRACE
- SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n",sk, statename[oldstate],statename[state]);
+ SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n", sk, statename[oldstate], statename[state]);
#endif
}
EXPORT_SYMBOL_GPL(tcp_set_state);
@@ -1834,7 +1836,6 @@ adjudge_to_death:
state = sk->sk_state;
sock_hold(sk);
sock_orphan(sk);
- atomic_inc(sk->sk_prot->orphan_count);
/* It is the last release_sock in its life. It will remove backlog. */
release_sock(sk);
@@ -1847,6 +1848,8 @@ adjudge_to_death:
bh_lock_sock(sk);
WARN_ON(sock_owned_by_user(sk));
+ percpu_counter_inc(sk->sk_prot->orphan_count);
+
/* Have we already been destroyed by a softirq or backlog? */
if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
goto out;
@@ -1885,9 +1888,11 @@ adjudge_to_death:
}
}
if (sk->sk_state != TCP_CLOSE) {
+ int orphan_count = percpu_counter_read_positive(
+ sk->sk_prot->orphan_count);
+
sk_mem_reclaim(sk);
- if (tcp_too_many_orphans(sk,
- atomic_read(sk->sk_prot->orphan_count))) {
+ if (tcp_too_many_orphans(sk, orphan_count)) {
if (net_ratelimit())
printk(KERN_INFO "TCP: too many of orphaned "
"sockets\n");
@@ -2461,6 +2466,106 @@ out:
}
EXPORT_SYMBOL(tcp_tso_segment);
+struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+ struct sk_buff **pp = NULL;
+ struct sk_buff *p;
+ struct tcphdr *th;
+ struct tcphdr *th2;
+ unsigned int thlen;
+ unsigned int flags;
+ unsigned int total;
+ unsigned int mss = 1;
+ int flush = 1;
+
+ if (!pskb_may_pull(skb, sizeof(*th)))
+ goto out;
+
+ th = tcp_hdr(skb);
+ thlen = th->doff * 4;
+ if (thlen < sizeof(*th))
+ goto out;
+
+ if (!pskb_may_pull(skb, thlen))
+ goto out;
+
+ th = tcp_hdr(skb);
+ __skb_pull(skb, thlen);
+
+ flags = tcp_flag_word(th);
+
+ for (; (p = *head); head = &p->next) {
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ th2 = tcp_hdr(p);
+
+ if (th->source != th2->source || th->dest != th2->dest) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ goto found;
+ }
+
+ goto out_check_final;
+
+found:
+ flush = NAPI_GRO_CB(p)->flush;
+ flush |= flags & TCP_FLAG_CWR;
+ flush |= (flags ^ tcp_flag_word(th2)) &
+ ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH);
+ flush |= th->ack_seq != th2->ack_seq || th->window != th2->window;
+ flush |= memcmp(th + 1, th2 + 1, thlen - sizeof(*th));
+
+ total = p->len;
+ mss = total;
+ if (skb_shinfo(p)->frag_list)
+ mss = skb_shinfo(p)->frag_list->len;
+
+ flush |= skb->len > mss || skb->len <= 0;
+ flush |= ntohl(th2->seq) + total != ntohl(th->seq);
+
+ if (flush || skb_gro_receive(head, skb)) {
+ mss = 1;
+ goto out_check_final;
+ }
+
+ p = *head;
+ th2 = tcp_hdr(p);
+ tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH);
+
+out_check_final:
+ flush = skb->len < mss;
+ flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST |
+ TCP_FLAG_SYN | TCP_FLAG_FIN);
+
+ if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
+ pp = head;
+
+out:
+ NAPI_GRO_CB(skb)->flush |= flush;
+
+ return pp;
+}
+
+int tcp_gro_complete(struct sk_buff *skb)
+{
+ struct tcphdr *th = tcp_hdr(skb);
+
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct tcphdr, check);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+
+ skb_shinfo(skb)->gso_size = skb_shinfo(skb)->frag_list->len;
+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+ if (th->cwr)
+ skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
+
+ return 0;
+}
+
#ifdef CONFIG_TCP_MD5SIG
static unsigned long tcp_md5sig_users;
static struct tcp_md5sig_pool **tcp_md5sig_pool;
@@ -2650,7 +2755,7 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
void tcp_done(struct sock *sk)
{
- if(sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
+ if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
tcp_set_state(sk, TCP_CLOSE);
@@ -2685,6 +2790,8 @@ void __init tcp_init(void)
BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
+ percpu_counter_init(&tcp_sockets_allocated, 0);
+ percpu_counter_init(&tcp_orphan_count, 0);
tcp_hashinfo.bind_bucket_cachep =
kmem_cache_create("tcp_bind_bucket",
sizeof(struct inet_bind_bucket), 0,
@@ -2707,8 +2814,8 @@ void __init tcp_init(void)
thash_entries ? 0 : 512 * 1024);
tcp_hashinfo.ehash_size = 1 << tcp_hashinfo.ehash_size;
for (i = 0; i < tcp_hashinfo.ehash_size; i++) {
- INIT_HLIST_HEAD(&tcp_hashinfo.ehash[i].chain);
- INIT_HLIST_HEAD(&tcp_hashinfo.ehash[i].twchain);
+ INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i);
+ INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].twchain, i);
}
if (inet_ehash_locks_alloc(&tcp_hashinfo))
panic("TCP: failed to alloc ehash_locks");
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 4a1221e5e8ee..ee467ec40c4f 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -1,13 +1,23 @@
/*
- * TCP CUBIC: Binary Increase Congestion control for TCP v2.2
+ * TCP CUBIC: Binary Increase Congestion control for TCP v2.3
* Home page:
* http://netsrv.csc.ncsu.edu/twiki/bin/view/Main/BIC
* This is from the implementation of CUBIC TCP in
- * Injong Rhee, Lisong Xu.
- * "CUBIC: A New TCP-Friendly High-Speed TCP Variant
- * in PFLDnet 2005
+ * Sangtae Ha, Injong Rhee and Lisong Xu,
+ * "CUBIC: A New TCP-Friendly High-Speed TCP Variant"
+ * in ACM SIGOPS Operating System Review, July 2008.
* Available from:
- * http://netsrv.csc.ncsu.edu/export/cubic-paper.pdf
+ * http://netsrv.csc.ncsu.edu/export/cubic_a_new_tcp_2008.pdf
+ *
+ * CUBIC integrates a new slow start algorithm, called HyStart.
+ * The details of HyStart are presented in
+ * Sangtae Ha and Injong Rhee,
+ * "Taming the Elephants: New TCP Slow Start", NCSU TechReport 2008.
+ * Available from:
+ * http://netsrv.csc.ncsu.edu/export/hystart_techreport_2008.pdf
+ *
+ * All testing results are available from:
+ * http://netsrv.csc.ncsu.edu/wiki/index.php/TCP_Testing
*
* Unless CUBIC is enabled and congestion window is large
* this behaves the same as the original Reno.
@@ -23,12 +33,26 @@
*/
#define BICTCP_HZ 10 /* BIC HZ 2^10 = 1024 */
+/* Two methods of hybrid slow start */
+#define HYSTART_ACK_TRAIN 0x1
+#define HYSTART_DELAY 0x2
+
+/* Number of delay samples for detecting the increase of delay */
+#define HYSTART_MIN_SAMPLES 8
+#define HYSTART_DELAY_MIN (2U<<3)
+#define HYSTART_DELAY_MAX (16U<<3)
+#define HYSTART_DELAY_THRESH(x) clamp(x, HYSTART_DELAY_MIN, HYSTART_DELAY_MAX)
+
static int fast_convergence __read_mostly = 1;
static int beta __read_mostly = 717; /* = 717/1024 (BICTCP_BETA_SCALE) */
static int initial_ssthresh __read_mostly;
static int bic_scale __read_mostly = 41;
static int tcp_friendliness __read_mostly = 1;
+static int hystart __read_mostly = 1;
+static int hystart_detect __read_mostly = HYSTART_ACK_TRAIN | HYSTART_DELAY;
+static int hystart_low_window __read_mostly = 16;
+
static u32 cube_rtt_scale __read_mostly;
static u32 beta_scale __read_mostly;
static u64 cube_factor __read_mostly;
@@ -44,6 +68,13 @@ module_param(bic_scale, int, 0444);
MODULE_PARM_DESC(bic_scale, "scale (scaled by 1024) value for bic function (bic_scale/1024)");
module_param(tcp_friendliness, int, 0644);
MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness");
+module_param(hystart, int, 0644);
+MODULE_PARM_DESC(hystart, "turn on/off hybrid slow start algorithm");
+module_param(hystart_detect, int, 0644);
+MODULE_PARM_DESC(hystart_detect, "hyrbrid slow start detection mechanisms"
+ " 1: packet-train 2: delay 3: both packet-train and delay");
+module_param(hystart_low_window, int, 0644);
+MODULE_PARM_DESC(hystart_low_window, "lower bound cwnd for hybrid slow start");
/* BIC TCP Parameters */
struct bictcp {
@@ -59,7 +90,13 @@ struct bictcp {
u32 ack_cnt; /* number of acks */
u32 tcp_cwnd; /* estimated tcp cwnd */
#define ACK_RATIO_SHIFT 4
- u32 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */
+ u16 delayed_ack; /* estimate the ratio of Packets/ACKs << 4 */
+ u8 sample_cnt; /* number of samples to decide curr_rtt */
+ u8 found; /* the exit point is found? */
+ u32 round_start; /* beginning of each round */
+ u32 end_seq; /* end_seq of the round */
+ u32 last_jiffies; /* last time when the ACK spacing is close */
+ u32 curr_rtt; /* the minimum rtt of current round */
};
static inline void bictcp_reset(struct bictcp *ca)
@@ -76,12 +113,28 @@ static inline void bictcp_reset(struct bictcp *ca)
ca->delayed_ack = 2 << ACK_RATIO_SHIFT;
ca->ack_cnt = 0;
ca->tcp_cwnd = 0;
+ ca->found = 0;
+}
+
+static inline void bictcp_hystart_reset(struct sock *sk)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct bictcp *ca = inet_csk_ca(sk);
+
+ ca->round_start = ca->last_jiffies = jiffies;
+ ca->end_seq = tp->snd_nxt;
+ ca->curr_rtt = 0;
+ ca->sample_cnt = 0;
}
static void bictcp_init(struct sock *sk)
{
bictcp_reset(inet_csk_ca(sk));
- if (initial_ssthresh)
+
+ if (hystart)
+ bictcp_hystart_reset(sk);
+
+ if (!hystart && initial_ssthresh)
tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
}
@@ -235,9 +288,11 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
- if (tp->snd_cwnd <= tp->snd_ssthresh)
+ if (tp->snd_cwnd <= tp->snd_ssthresh) {
+ if (hystart && after(ack, ca->end_seq))
+ bictcp_hystart_reset(sk);
tcp_slow_start(tp);
- else {
+ } else {
bictcp_update(ca, tp->snd_cwnd);
/* In dangerous area, increase slowly.
@@ -281,8 +336,45 @@ static u32 bictcp_undo_cwnd(struct sock *sk)
static void bictcp_state(struct sock *sk, u8 new_state)
{
- if (new_state == TCP_CA_Loss)
+ if (new_state == TCP_CA_Loss) {
bictcp_reset(inet_csk_ca(sk));
+ bictcp_hystart_reset(sk);
+ }
+}
+
+static void hystart_update(struct sock *sk, u32 delay)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct bictcp *ca = inet_csk_ca(sk);
+
+ if (!(ca->found & hystart_detect)) {
+ u32 curr_jiffies = jiffies;
+
+ /* first detection parameter - ack-train detection */
+ if (curr_jiffies - ca->last_jiffies <= msecs_to_jiffies(2)) {
+ ca->last_jiffies = curr_jiffies;
+ if (curr_jiffies - ca->round_start >= ca->delay_min>>4)
+ ca->found |= HYSTART_ACK_TRAIN;
+ }
+
+ /* obtain the minimum delay of more than sampling packets */
+ if (ca->sample_cnt < HYSTART_MIN_SAMPLES) {
+ if (ca->curr_rtt == 0 || ca->curr_rtt > delay)
+ ca->curr_rtt = delay;
+
+ ca->sample_cnt++;
+ } else {
+ if (ca->curr_rtt > ca->delay_min +
+ HYSTART_DELAY_THRESH(ca->delay_min>>4))
+ ca->found |= HYSTART_DELAY;
+ }
+ /*
+ * Either one of two conditions are met,
+ * we exit from slow start immediately.
+ */
+ if (ca->found & hystart_detect)
+ tp->snd_ssthresh = tp->snd_cwnd;
+ }
}
/* Track delayed acknowledgment ratio using sliding window
@@ -291,6 +383,7 @@ static void bictcp_state(struct sock *sk, u8 new_state)
static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
+ const struct tcp_sock *tp = tcp_sk(sk);
struct bictcp *ca = inet_csk_ca(sk);
u32 delay;
@@ -314,6 +407,11 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us)
/* first time call or link delay decreases */
if (ca->delay_min == 0 || ca->delay_min > delay)
ca->delay_min = delay;
+
+ /* hystart triggers when cwnd is larger than some threshold */
+ if (hystart && tp->snd_cwnd <= tp->snd_ssthresh &&
+ tp->snd_cwnd >= hystart_low_window)
+ hystart_update(sk, delay);
}
static struct tcp_congestion_ops cubictcp = {
@@ -372,4 +470,4 @@ module_exit(cubictcp_unregister);
MODULE_AUTHOR("Sangtae Ha, Stephen Hemminger");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CUBIC TCP");
-MODULE_VERSION("2.2");
+MODULE_VERSION("2.3");
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
index 838d491dfda7..fcbcd4ff6c5f 100644
--- a/net/ipv4/tcp_diag.c
+++ b/net/ipv4/tcp_diag.c
@@ -34,7 +34,7 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
tcp_get_info(sk, info);
}
-static struct inet_diag_handler tcp_diag_handler = {
+static const struct inet_diag_handler tcp_diag_handler = {
.idiag_hashinfo = &tcp_hashinfo,
.idiag_get_info = tcp_diag_get_info,
.idiag_type = TCPDIAG_GETSOCK,
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index d77c0d29e239..99b7ecbe8893 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -701,13 +701,10 @@ static inline void tcp_set_rto(struct sock *sk)
* all the algo is pure shit and should be replaced
* with correct one. It is exactly, which we pretend to do.
*/
-}
-/* NOTE: clamping at TCP_RTO_MIN is not required, current algo
- * guarantees that rto is higher.
- */
-static inline void tcp_bound_rto(struct sock *sk)
-{
+ /* NOTE: clamping at TCP_RTO_MIN is not required, current algo
+ * guarantees that rto is higher.
+ */
if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)
inet_csk(sk)->icsk_rto = TCP_RTO_MAX;
}
@@ -928,7 +925,6 @@ static void tcp_init_metrics(struct sock *sk)
tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
}
tcp_set_rto(sk);
- tcp_bound_rto(sk);
if (inet_csk(sk)->icsk_rto < TCP_TIMEOUT_INIT && !tp->rx_opt.saw_tstamp)
goto reset;
tp->snd_cwnd = tcp_init_cwnd(tp, dst);
@@ -1002,7 +998,8 @@ static void tcp_skb_mark_lost(struct tcp_sock *tp, struct sk_buff *skb)
}
}
-void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp, struct sk_buff *skb)
+static void tcp_skb_mark_lost_uncond_verify(struct tcp_sock *tp,
+ struct sk_buff *skb)
{
tcp_verify_retransmit_hint(tp, skb);
@@ -1236,31 +1233,58 @@ static int tcp_check_dsack(struct sock *sk, struct sk_buff *ack_skb,
return dup_sack;
}
+struct tcp_sacktag_state {
+ int reord;
+ int fack_count;
+ int flag;
+};
+
/* Check if skb is fully within the SACK block. In presence of GSO skbs,
* the incoming SACK may not exactly match but we can find smaller MSS
* aligned portion of it that matches. Therefore we might need to fragment
* which may fail and creates some hassle (caller must handle error case
* returns).
+ *
+ * FIXME: this could be merged to shift decision code
*/
static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
u32 start_seq, u32 end_seq)
{
int in_sack, err;
unsigned int pkt_len;
+ unsigned int mss;
in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
!before(end_seq, TCP_SKB_CB(skb)->end_seq);
if (tcp_skb_pcount(skb) > 1 && !in_sack &&
after(TCP_SKB_CB(skb)->end_seq, start_seq)) {
-
+ mss = tcp_skb_mss(skb);
in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq);
- if (!in_sack)
+ if (!in_sack) {
pkt_len = start_seq - TCP_SKB_CB(skb)->seq;
- else
+ if (pkt_len < mss)
+ pkt_len = mss;
+ } else {
pkt_len = end_seq - TCP_SKB_CB(skb)->seq;
- err = tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->gso_size);
+ if (pkt_len < mss)
+ return -EINVAL;
+ }
+
+ /* Round if necessary so that SACKs cover only full MSSes
+ * and/or the remaining small portion (if present)
+ */
+ if (pkt_len > mss) {
+ unsigned int new_len = (pkt_len / mss) * mss;
+ if (!in_sack && new_len < pkt_len) {
+ new_len += mss;
+ if (new_len > skb->len)
+ return 0;
+ }
+ pkt_len = new_len;
+ }
+ err = tcp_fragment(sk, skb, pkt_len, mss);
if (err < 0)
return err;
}
@@ -1268,24 +1292,25 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
return in_sack;
}
-static int tcp_sacktag_one(struct sk_buff *skb, struct sock *sk,
- int *reord, int dup_sack, int fack_count)
+static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk,
+ struct tcp_sacktag_state *state,
+ int dup_sack, int pcount)
{
struct tcp_sock *tp = tcp_sk(sk);
u8 sacked = TCP_SKB_CB(skb)->sacked;
- int flag = 0;
+ int fack_count = state->fack_count;
/* Account D-SACK for retransmitted packet. */
if (dup_sack && (sacked & TCPCB_RETRANS)) {
if (after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker))
tp->undo_retrans--;
if (sacked & TCPCB_SACKED_ACKED)
- *reord = min(fack_count, *reord);
+ state->reord = min(fack_count, state->reord);
}
/* Nothing to do; acked frame is about to be dropped (was ACKed). */
if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
- return flag;
+ return sacked;
if (!(sacked & TCPCB_SACKED_ACKED)) {
if (sacked & TCPCB_SACKED_RETRANS) {
@@ -1294,10 +1319,9 @@ static int tcp_sacktag_one(struct sk_buff *skb, struct sock *sk,
* that retransmission is still in flight.
*/
if (sacked & TCPCB_LOST) {
- TCP_SKB_CB(skb)->sacked &=
- ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
- tp->lost_out -= tcp_skb_pcount(skb);
- tp->retrans_out -= tcp_skb_pcount(skb);
+ sacked &= ~(TCPCB_LOST|TCPCB_SACKED_RETRANS);
+ tp->lost_out -= pcount;
+ tp->retrans_out -= pcount;
}
} else {
if (!(sacked & TCPCB_RETRANS)) {
@@ -1306,56 +1330,280 @@ static int tcp_sacktag_one(struct sk_buff *skb, struct sock *sk,
*/
if (before(TCP_SKB_CB(skb)->seq,
tcp_highest_sack_seq(tp)))
- *reord = min(fack_count, *reord);
+ state->reord = min(fack_count,
+ state->reord);
/* SACK enhanced F-RTO (RFC4138; Appendix B) */
if (!after(TCP_SKB_CB(skb)->end_seq, tp->frto_highmark))
- flag |= FLAG_ONLY_ORIG_SACKED;
+ state->flag |= FLAG_ONLY_ORIG_SACKED;
}
if (sacked & TCPCB_LOST) {
- TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
- tp->lost_out -= tcp_skb_pcount(skb);
+ sacked &= ~TCPCB_LOST;
+ tp->lost_out -= pcount;
}
}
- TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
- flag |= FLAG_DATA_SACKED;
- tp->sacked_out += tcp_skb_pcount(skb);
+ sacked |= TCPCB_SACKED_ACKED;
+ state->flag |= FLAG_DATA_SACKED;
+ tp->sacked_out += pcount;
- fack_count += tcp_skb_pcount(skb);
+ fack_count += pcount;
/* Lost marker hint past SACKed? Tweak RFC3517 cnt */
if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) &&
before(TCP_SKB_CB(skb)->seq,
TCP_SKB_CB(tp->lost_skb_hint)->seq))
- tp->lost_cnt_hint += tcp_skb_pcount(skb);
+ tp->lost_cnt_hint += pcount;
if (fack_count > tp->fackets_out)
tp->fackets_out = fack_count;
-
- if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp)))
- tcp_advance_highest_sack(sk, skb);
}
/* D-SACK. We can detect redundant retransmission in S|R and plain R
* frames and clear it. undo_retrans is decreased above, L|R frames
* are accounted above as well.
*/
- if (dup_sack && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS)) {
- TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
- tp->retrans_out -= tcp_skb_pcount(skb);
+ if (dup_sack && (sacked & TCPCB_SACKED_RETRANS)) {
+ sacked &= ~TCPCB_SACKED_RETRANS;
+ tp->retrans_out -= pcount;
}
- return flag;
+ return sacked;
+}
+
+static int tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
+ struct tcp_sacktag_state *state,
+ unsigned int pcount, int shifted, int mss)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *prev = tcp_write_queue_prev(sk, skb);
+
+ BUG_ON(!pcount);
+
+ /* Tweak before seqno plays */
+ if (!tcp_is_fack(tp) && tcp_is_sack(tp) && tp->lost_skb_hint &&
+ !before(TCP_SKB_CB(tp->lost_skb_hint)->seq, TCP_SKB_CB(skb)->seq))
+ tp->lost_cnt_hint += pcount;
+
+ TCP_SKB_CB(prev)->end_seq += shifted;
+ TCP_SKB_CB(skb)->seq += shifted;
+
+ skb_shinfo(prev)->gso_segs += pcount;
+ BUG_ON(skb_shinfo(skb)->gso_segs < pcount);
+ skb_shinfo(skb)->gso_segs -= pcount;
+
+ /* When we're adding to gso_segs == 1, gso_size will be zero,
+ * in theory this shouldn't be necessary but as long as DSACK
+ * code can come after this skb later on it's better to keep
+ * setting gso_size to something.
+ */
+ if (!skb_shinfo(prev)->gso_size) {
+ skb_shinfo(prev)->gso_size = mss;
+ skb_shinfo(prev)->gso_type = sk->sk_gso_type;
+ }
+
+ /* CHECKME: To clear or not to clear? Mimics normal skb currently */
+ if (skb_shinfo(skb)->gso_segs <= 1) {
+ skb_shinfo(skb)->gso_size = 0;
+ skb_shinfo(skb)->gso_type = 0;
+ }
+
+ /* We discard results */
+ tcp_sacktag_one(skb, sk, state, 0, pcount);
+
+ /* Difference in this won't matter, both ACKed by the same cumul. ACK */
+ TCP_SKB_CB(prev)->sacked |= (TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS);
+
+ if (skb->len > 0) {
+ BUG_ON(!tcp_skb_pcount(skb));
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SACKSHIFTED);
+ return 0;
+ }
+
+ /* Whole SKB was eaten :-) */
+
+ if (skb == tp->retransmit_skb_hint)
+ tp->retransmit_skb_hint = prev;
+ if (skb == tp->scoreboard_skb_hint)
+ tp->scoreboard_skb_hint = prev;
+ if (skb == tp->lost_skb_hint) {
+ tp->lost_skb_hint = prev;
+ tp->lost_cnt_hint -= tcp_skb_pcount(prev);
+ }
+
+ TCP_SKB_CB(skb)->flags |= TCP_SKB_CB(prev)->flags;
+ if (skb == tcp_highest_sack(sk))
+ tcp_advance_highest_sack(sk, skb);
+
+ tcp_unlink_write_queue(skb, sk);
+ sk_wmem_free_skb(sk, skb);
+
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SACKMERGED);
+
+ return 1;
+}
+
+/* I wish gso_size would have a bit more sane initialization than
+ * something-or-zero which complicates things
+ */
+static int tcp_skb_seglen(struct sk_buff *skb)
+{
+ return tcp_skb_pcount(skb) == 1 ? skb->len : tcp_skb_mss(skb);
+}
+
+/* Shifting pages past head area doesn't work */
+static int skb_can_shift(struct sk_buff *skb)
+{
+ return !skb_headlen(skb) && skb_is_nonlinear(skb);
+}
+
+/* Try collapsing SACK blocks spanning across multiple skbs to a single
+ * skb.
+ */
+static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
+ struct tcp_sacktag_state *state,
+ u32 start_seq, u32 end_seq,
+ int dup_sack)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *prev;
+ int mss;
+ int pcount = 0;
+ int len;
+ int in_sack;
+
+ if (!sk_can_gso(sk))
+ goto fallback;
+
+ /* Normally R but no L won't result in plain S */
+ if (!dup_sack &&
+ (TCP_SKB_CB(skb)->sacked & (TCPCB_LOST|TCPCB_SACKED_RETRANS)) == TCPCB_SACKED_RETRANS)
+ goto fallback;
+ if (!skb_can_shift(skb))
+ goto fallback;
+ /* This frame is about to be dropped (was ACKed). */
+ if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una))
+ goto fallback;
+
+ /* Can only happen with delayed DSACK + discard craziness */
+ if (unlikely(skb == tcp_write_queue_head(sk)))
+ goto fallback;
+ prev = tcp_write_queue_prev(sk, skb);
+
+ if ((TCP_SKB_CB(prev)->sacked & TCPCB_TAGBITS) != TCPCB_SACKED_ACKED)
+ goto fallback;
+
+ in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
+ !before(end_seq, TCP_SKB_CB(skb)->end_seq);
+
+ if (in_sack) {
+ len = skb->len;
+ pcount = tcp_skb_pcount(skb);
+ mss = tcp_skb_seglen(skb);
+
+ /* TODO: Fix DSACKs to not fragment already SACKed and we can
+ * drop this restriction as unnecessary
+ */
+ if (mss != tcp_skb_seglen(prev))
+ goto fallback;
+ } else {
+ if (!after(TCP_SKB_CB(skb)->end_seq, start_seq))
+ goto noop;
+ /* CHECKME: This is non-MSS split case only?, this will
+ * cause skipped skbs due to advancing loop btw, original
+ * has that feature too
+ */
+ if (tcp_skb_pcount(skb) <= 1)
+ goto noop;
+
+ in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq);
+ if (!in_sack) {
+ /* TODO: head merge to next could be attempted here
+ * if (!after(TCP_SKB_CB(skb)->end_seq, end_seq)),
+ * though it might not be worth of the additional hassle
+ *
+ * ...we can probably just fallback to what was done
+ * previously. We could try merging non-SACKed ones
+ * as well but it probably isn't going to buy off
+ * because later SACKs might again split them, and
+ * it would make skb timestamp tracking considerably
+ * harder problem.
+ */
+ goto fallback;
+ }
+
+ len = end_seq - TCP_SKB_CB(skb)->seq;
+ BUG_ON(len < 0);
+ BUG_ON(len > skb->len);
+
+ /* MSS boundaries should be honoured or else pcount will
+ * severely break even though it makes things bit trickier.
+ * Optimize common case to avoid most of the divides
+ */
+ mss = tcp_skb_mss(skb);
+
+ /* TODO: Fix DSACKs to not fragment already SACKed and we can
+ * drop this restriction as unnecessary
+ */
+ if (mss != tcp_skb_seglen(prev))
+ goto fallback;
+
+ if (len == mss) {
+ pcount = 1;
+ } else if (len < mss) {
+ goto noop;
+ } else {
+ pcount = len / mss;
+ len = pcount * mss;
+ }
+ }
+
+ if (!skb_shift(prev, skb, len))
+ goto fallback;
+ if (!tcp_shifted_skb(sk, skb, state, pcount, len, mss))
+ goto out;
+
+ /* Hole filled allows collapsing with the next as well, this is very
+ * useful when hole on every nth skb pattern happens
+ */
+ if (prev == tcp_write_queue_tail(sk))
+ goto out;
+ skb = tcp_write_queue_next(sk, prev);
+
+ if (!skb_can_shift(skb) ||
+ (skb == tcp_send_head(sk)) ||
+ ((TCP_SKB_CB(skb)->sacked & TCPCB_TAGBITS) != TCPCB_SACKED_ACKED) ||
+ (mss != tcp_skb_seglen(skb)))
+ goto out;
+
+ len = skb->len;
+ if (skb_shift(prev, skb, len)) {
+ pcount += tcp_skb_pcount(skb);
+ tcp_shifted_skb(sk, skb, state, tcp_skb_pcount(skb), len, mss);
+ }
+
+out:
+ state->fack_count += pcount;
+ return prev;
+
+noop:
+ return skb;
+
+fallback:
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SACKSHIFTFALLBACK);
+ return NULL;
}
static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
struct tcp_sack_block *next_dup,
+ struct tcp_sacktag_state *state,
u32 start_seq, u32 end_seq,
- int dup_sack_in, int *fack_count,
- int *reord, int *flag)
+ int dup_sack_in)
{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *tmp;
+
tcp_for_write_queue_from(skb, sk) {
int in_sack = 0;
int dup_sack = dup_sack_in;
@@ -1376,17 +1624,42 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
dup_sack = 1;
}
- if (in_sack <= 0)
- in_sack = tcp_match_skb_to_sack(sk, skb, start_seq,
- end_seq);
+ /* skb reference here is a bit tricky to get right, since
+ * shifting can eat and free both this skb and the next,
+ * so not even _safe variant of the loop is enough.
+ */
+ if (in_sack <= 0) {
+ tmp = tcp_shift_skb_data(sk, skb, state,
+ start_seq, end_seq, dup_sack);
+ if (tmp != NULL) {
+ if (tmp != skb) {
+ skb = tmp;
+ continue;
+ }
+
+ in_sack = 0;
+ } else {
+ in_sack = tcp_match_skb_to_sack(sk, skb,
+ start_seq,
+ end_seq);
+ }
+ }
+
if (unlikely(in_sack < 0))
break;
- if (in_sack)
- *flag |= tcp_sacktag_one(skb, sk, reord, dup_sack,
- *fack_count);
+ if (in_sack) {
+ TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk,
+ state,
+ dup_sack,
+ tcp_skb_pcount(skb));
+
+ if (!before(TCP_SKB_CB(skb)->seq,
+ tcp_highest_sack_seq(tp)))
+ tcp_advance_highest_sack(sk, skb);
+ }
- *fack_count += tcp_skb_pcount(skb);
+ state->fack_count += tcp_skb_pcount(skb);
}
return skb;
}
@@ -1395,16 +1668,17 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
* a normal way
*/
static struct sk_buff *tcp_sacktag_skip(struct sk_buff *skb, struct sock *sk,
- u32 skip_to_seq, int *fack_count)
+ struct tcp_sacktag_state *state,
+ u32 skip_to_seq)
{
tcp_for_write_queue_from(skb, sk) {
if (skb == tcp_send_head(sk))
break;
- if (!before(TCP_SKB_CB(skb)->end_seq, skip_to_seq))
+ if (after(TCP_SKB_CB(skb)->end_seq, skip_to_seq))
break;
- *fack_count += tcp_skb_pcount(skb);
+ state->fack_count += tcp_skb_pcount(skb);
}
return skb;
}
@@ -1412,18 +1686,17 @@ static struct sk_buff *tcp_sacktag_skip(struct sk_buff *skb, struct sock *sk,
static struct sk_buff *tcp_maybe_skipping_dsack(struct sk_buff *skb,
struct sock *sk,
struct tcp_sack_block *next_dup,
- u32 skip_to_seq,
- int *fack_count, int *reord,
- int *flag)
+ struct tcp_sacktag_state *state,
+ u32 skip_to_seq)
{
if (next_dup == NULL)
return skb;
if (before(next_dup->start_seq, skip_to_seq)) {
- skb = tcp_sacktag_skip(skb, sk, next_dup->start_seq, fack_count);
- skb = tcp_sacktag_walk(skb, sk, NULL,
- next_dup->start_seq, next_dup->end_seq,
- 1, fack_count, reord, flag);
+ skb = tcp_sacktag_skip(skb, sk, state, next_dup->start_seq);
+ skb = tcp_sacktag_walk(skb, sk, NULL, state,
+ next_dup->start_seq, next_dup->end_seq,
+ 1);
}
return skb;
@@ -1445,16 +1718,17 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
struct tcp_sack_block_wire *sp_wire = (struct tcp_sack_block_wire *)(ptr+2);
struct tcp_sack_block sp[TCP_NUM_SACKS];
struct tcp_sack_block *cache;
+ struct tcp_sacktag_state state;
struct sk_buff *skb;
int num_sacks = min(TCP_NUM_SACKS, (ptr[1] - TCPOLEN_SACK_BASE) >> 3);
int used_sacks;
- int reord = tp->packets_out;
- int flag = 0;
int found_dup_sack = 0;
- int fack_count;
int i, j;
int first_sack_index;
+ state.flag = 0;
+ state.reord = tp->packets_out;
+
if (!tp->sacked_out) {
if (WARN_ON(tp->fackets_out))
tp->fackets_out = 0;
@@ -1464,7 +1738,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
found_dup_sack = tcp_check_dsack(sk, ack_skb, sp_wire,
num_sacks, prior_snd_una);
if (found_dup_sack)
- flag |= FLAG_DSACKING_ACK;
+ state.flag |= FLAG_DSACKING_ACK;
/* Eliminate too old ACKs, but take into
* account more or less fresh ones, they can
@@ -1533,7 +1807,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
}
skb = tcp_write_queue_head(sk);
- fack_count = 0;
+ state.fack_count = 0;
i = 0;
if (!tp->sacked_out) {
@@ -1558,7 +1832,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
/* Event "B" in the comment above. */
if (after(end_seq, tp->high_seq))
- flag |= FLAG_DATA_LOST;
+ state.flag |= FLAG_DATA_LOST;
/* Skip too early cached blocks */
while (tcp_sack_cache_ok(tp, cache) &&
@@ -1571,13 +1845,13 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
/* Head todo? */
if (before(start_seq, cache->start_seq)) {
- skb = tcp_sacktag_skip(skb, sk, start_seq,
- &fack_count);
+ skb = tcp_sacktag_skip(skb, sk, &state,
+ start_seq);
skb = tcp_sacktag_walk(skb, sk, next_dup,
+ &state,
start_seq,
cache->start_seq,
- dup_sack, &fack_count,
- &reord, &flag);
+ dup_sack);
}
/* Rest of the block already fully processed? */
@@ -1585,9 +1859,8 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
goto advance_sp;
skb = tcp_maybe_skipping_dsack(skb, sk, next_dup,
- cache->end_seq,
- &fack_count, &reord,
- &flag);
+ &state,
+ cache->end_seq);
/* ...tail remains todo... */
if (tcp_highest_sack_seq(tp) == cache->end_seq) {
@@ -1595,13 +1868,12 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
skb = tcp_highest_sack(sk);
if (skb == NULL)
break;
- fack_count = tp->fackets_out;
+ state.fack_count = tp->fackets_out;
cache++;
goto walk;
}
- skb = tcp_sacktag_skip(skb, sk, cache->end_seq,
- &fack_count);
+ skb = tcp_sacktag_skip(skb, sk, &state, cache->end_seq);
/* Check overlap against next cached too (past this one already) */
cache++;
continue;
@@ -1611,20 +1883,20 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
skb = tcp_highest_sack(sk);
if (skb == NULL)
break;
- fack_count = tp->fackets_out;
+ state.fack_count = tp->fackets_out;
}
- skb = tcp_sacktag_skip(skb, sk, start_seq, &fack_count);
+ skb = tcp_sacktag_skip(skb, sk, &state, start_seq);
walk:
- skb = tcp_sacktag_walk(skb, sk, next_dup, start_seq, end_seq,
- dup_sack, &fack_count, &reord, &flag);
+ skb = tcp_sacktag_walk(skb, sk, next_dup, &state,
+ start_seq, end_seq, dup_sack);
advance_sp:
/* SACK enhanced FRTO (RFC4138, Appendix B): Clearing correct
* due to in-order walk
*/
if (after(end_seq, tp->frto_highmark))
- flag &= ~FLAG_ONLY_ORIG_SACKED;
+ state.flag &= ~FLAG_ONLY_ORIG_SACKED;
i++;
}
@@ -1641,10 +1913,10 @@ advance_sp:
tcp_verify_left_out(tp);
- if ((reord < tp->fackets_out) &&
+ if ((state.reord < tp->fackets_out) &&
((icsk->icsk_ca_state != TCP_CA_Loss) || tp->undo_marker) &&
(!tp->frto_highmark || after(tp->snd_una, tp->frto_highmark)))
- tcp_update_reordering(sk, tp->fackets_out - reord, 0);
+ tcp_update_reordering(sk, tp->fackets_out - state.reord, 0);
out:
@@ -1654,13 +1926,13 @@ out:
WARN_ON((int)tp->retrans_out < 0);
WARN_ON((int)tcp_packets_in_flight(tp) < 0);
#endif
- return flag;
+ return state.flag;
}
/* Limits sacked_out so that sum with lost_out isn't ever larger than
* packets_out. Returns zero if sacked_out adjustement wasn't necessary.
*/
-int tcp_limit_reno_sacked(struct tcp_sock *tp)
+static int tcp_limit_reno_sacked(struct tcp_sock *tp)
{
u32 holes;
@@ -2336,9 +2608,9 @@ static void DBGUNDO(struct sock *sk, const char *msg)
struct inet_sock *inet = inet_sk(sk);
if (sk->sk_family == AF_INET) {
- printk(KERN_DEBUG "Undo %s " NIPQUAD_FMT "/%u c%u l%u ss%u/%u p%u\n",
+ printk(KERN_DEBUG "Undo %s %pI4/%u c%u l%u ss%u/%u p%u\n",
msg,
- NIPQUAD(inet->daddr), ntohs(inet->dport),
+ &inet->daddr, ntohs(inet->dport),
tp->snd_cwnd, tcp_left_out(tp),
tp->snd_ssthresh, tp->prior_ssthresh,
tp->packets_out);
@@ -2346,9 +2618,9 @@ static void DBGUNDO(struct sock *sk, const char *msg)
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (sk->sk_family == AF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
- printk(KERN_DEBUG "Undo %s " NIP6_FMT "/%u c%u l%u ss%u/%u p%u\n",
+ printk(KERN_DEBUG "Undo %s %pI6/%u c%u l%u ss%u/%u p%u\n",
msg,
- NIP6(np->daddr), ntohs(inet->dport),
+ &np->daddr, ntohs(inet->dport),
tp->snd_cwnd, tcp_left_out(tp),
tp->snd_ssthresh, tp->prior_ssthresh,
tp->packets_out);
@@ -2559,6 +2831,56 @@ static void tcp_mtup_probe_success(struct sock *sk, struct sk_buff *skb)
tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
+/* Do a simple retransmit without using the backoff mechanisms in
+ * tcp_timer. This is used for path mtu discovery.
+ * The socket is already locked here.
+ */
+void tcp_simple_retransmit(struct sock *sk)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+ unsigned int mss = tcp_current_mss(sk, 0);
+ u32 prior_lost = tp->lost_out;
+
+ tcp_for_write_queue(skb, sk) {
+ if (skb == tcp_send_head(sk))
+ break;
+ if (tcp_skb_seglen(skb) > mss &&
+ !(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
+ if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
+ TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
+ tp->retrans_out -= tcp_skb_pcount(skb);
+ }
+ tcp_skb_mark_lost_uncond_verify(tp, skb);
+ }
+ }
+
+ tcp_clear_retrans_hints_partial(tp);
+
+ if (prior_lost == tp->lost_out)
+ return;
+
+ if (tcp_is_reno(tp))
+ tcp_limit_reno_sacked(tp);
+
+ tcp_verify_left_out(tp);
+
+ /* Don't muck with the congestion window here.
+ * Reason is that we do not increase amount of _data_
+ * in network, but units changed and effective
+ * cwnd/ssthresh really reduced now.
+ */
+ if (icsk->icsk_ca_state != TCP_CA_Loss) {
+ tp->high_seq = tp->snd_nxt;
+ tp->snd_ssthresh = tcp_current_ssthresh(sk);
+ tp->prior_ssthresh = 0;
+ tp->undo_marker = 0;
+ tcp_set_ca_state(sk, TCP_CA_Loss);
+ }
+ tcp_xmit_retransmit_queue(sk);
+}
+
/* Process an event, which can update packets-in-flight not trivially.
* Main goal of this function is to calculate new estimate for left_out,
* taking into account both packets sitting in receiver's buffer and
@@ -2730,6 +3052,13 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
tcp_xmit_retransmit_queue(sk);
}
+static void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt)
+{
+ tcp_rtt_estimator(sk, seq_rtt);
+ tcp_set_rto(sk);
+ inet_csk(sk)->icsk_backoff = 0;
+}
+
/* Read draft-ietf-tcplw-high-performance before mucking
* with this code. (Supersedes RFC1323)
*/
@@ -2751,11 +3080,8 @@ static void tcp_ack_saw_tstamp(struct sock *sk, int flag)
* in window is lost... Voila. --ANK (010210)
*/
struct tcp_sock *tp = tcp_sk(sk);
- const __u32 seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr;
- tcp_rtt_estimator(sk, seq_rtt);
- tcp_set_rto(sk);
- inet_csk(sk)->icsk_backoff = 0;
- tcp_bound_rto(sk);
+
+ tcp_valid_rtt_meas(sk, tcp_time_stamp - tp->rx_opt.rcv_tsecr);
}
static void tcp_ack_no_tstamp(struct sock *sk, u32 seq_rtt, int flag)
@@ -2772,10 +3098,7 @@ static void tcp_ack_no_tstamp(struct sock *sk, u32 seq_rtt, int flag)
if (flag & FLAG_RETRANS_DATA_ACKED)
return;
- tcp_rtt_estimator(sk, seq_rtt);
- tcp_set_rto(sk);
- inet_csk(sk)->icsk_backoff = 0;
- tcp_bound_rto(sk);
+ tcp_valid_rtt_meas(sk, seq_rtt);
}
static inline void tcp_ack_update_rtt(struct sock *sk, const int flag,
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 5c8fa7f1e327..9d839fa9331e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -51,6 +51,7 @@
*/
+#include <linux/bottom_half.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/module.h>
@@ -97,11 +98,7 @@ struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
}
#endif
-struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
- .lhash_lock = __RW_LOCK_UNLOCKED(tcp_hashinfo.lhash_lock),
- .lhash_users = ATOMIC_INIT(0),
- .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.lhash_wait),
-};
+struct inet_hashinfo tcp_hashinfo;
static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb)
{
@@ -492,7 +489,7 @@ void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
skb->csum_offset = offsetof(struct tcphdr, check);
} else {
th->check = tcp_v4_check(len, inet->saddr, inet->daddr,
- csum_partial((char *)th,
+ csum_partial(th,
th->doff << 2,
skb->csum));
}
@@ -726,7 +723,7 @@ static int __tcp_v4_send_synack(struct sock *sk, struct request_sock *req,
th->check = tcp_v4_check(skb->len,
ireq->loc_addr,
ireq->rmt_addr,
- csum_partial((char *)th, skb->len,
+ csum_partial(th, skb->len,
skb->csum));
err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
@@ -1139,10 +1136,9 @@ static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
if (net_ratelimit()) {
- printk(KERN_INFO "MD5 Hash failed for "
- "(" NIPQUAD_FMT ", %d)->(" NIPQUAD_FMT ", %d)%s\n",
- NIPQUAD(iph->saddr), ntohs(th->source),
- NIPQUAD(iph->daddr), ntohs(th->dest),
+ printk(KERN_INFO "MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s\n",
+ &iph->saddr, ntohs(th->source),
+ &iph->daddr, ntohs(th->dest),
genhash ? " tcp_v4_calc_md5_hash failed" : "");
}
return 1;
@@ -1297,10 +1293,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
* to destinations, already remembered
* to the moment of synflood.
*/
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open "
- "request from " NIPQUAD_FMT "/%u\n",
- NIPQUAD(saddr),
- ntohs(tcp_hdr(skb)->source));
+ LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI4/%u\n",
+ &saddr, ntohs(tcp_hdr(skb)->source));
goto drop_and_release;
}
@@ -1804,7 +1798,9 @@ static int tcp_v4_init_sock(struct sock *sk)
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
- atomic_inc(&tcp_sockets_allocated);
+ local_bh_disable();
+ percpu_counter_inc(&tcp_sockets_allocated);
+ local_bh_enable();
return 0;
}
@@ -1852,7 +1848,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
sk->sk_sndmsg_page = NULL;
}
- atomic_dec(&tcp_sockets_allocated);
+ percpu_counter_dec(&tcp_sockets_allocated);
}
EXPORT_SYMBOL(tcp_v4_destroy_sock);
@@ -1860,32 +1856,35 @@ EXPORT_SYMBOL(tcp_v4_destroy_sock);
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCP sock list dumping. */
-static inline struct inet_timewait_sock *tw_head(struct hlist_head *head)
+static inline struct inet_timewait_sock *tw_head(struct hlist_nulls_head *head)
{
- return hlist_empty(head) ? NULL :
+ return hlist_nulls_empty(head) ? NULL :
list_entry(head->first, struct inet_timewait_sock, tw_node);
}
static inline struct inet_timewait_sock *tw_next(struct inet_timewait_sock *tw)
{
- return tw->tw_node.next ?
- hlist_entry(tw->tw_node.next, typeof(*tw), tw_node) : NULL;
+ return !is_a_nulls(tw->tw_node.next) ?
+ hlist_nulls_entry(tw->tw_node.next, typeof(*tw), tw_node) : NULL;
}
static void *listening_get_next(struct seq_file *seq, void *cur)
{
struct inet_connection_sock *icsk;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
struct sock *sk = cur;
- struct tcp_iter_state* st = seq->private;
+ struct inet_listen_hashbucket *ilb;
+ struct tcp_iter_state *st = seq->private;
struct net *net = seq_file_net(seq);
if (!sk) {
st->bucket = 0;
- sk = sk_head(&tcp_hashinfo.listening_hash[0]);
+ ilb = &tcp_hashinfo.listening_hash[0];
+ spin_lock_bh(&ilb->lock);
+ sk = sk_nulls_head(&ilb->head);
goto get_sk;
}
-
+ ilb = &tcp_hashinfo.listening_hash[st->bucket];
++st->num;
if (st->state == TCP_SEQ_STATE_OPENREQ) {
@@ -1918,7 +1917,7 @@ get_req:
sk = sk_next(sk);
}
get_sk:
- sk_for_each_from(sk, node) {
+ sk_nulls_for_each_from(sk, node) {
if (sk->sk_family == st->family && net_eq(sock_net(sk), net)) {
cur = sk;
goto out;
@@ -1935,8 +1934,11 @@ start_req:
}
read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock);
}
+ spin_unlock_bh(&ilb->lock);
if (++st->bucket < INET_LHTABLE_SIZE) {
- sk = sk_head(&tcp_hashinfo.listening_hash[st->bucket]);
+ ilb = &tcp_hashinfo.listening_hash[st->bucket];
+ spin_lock_bh(&ilb->lock);
+ sk = sk_nulls_head(&ilb->head);
goto get_sk;
}
cur = NULL;
@@ -1957,28 +1959,28 @@ static void *listening_get_idx(struct seq_file *seq, loff_t *pos)
static inline int empty_bucket(struct tcp_iter_state *st)
{
- return hlist_empty(&tcp_hashinfo.ehash[st->bucket].chain) &&
- hlist_empty(&tcp_hashinfo.ehash[st->bucket].twchain);
+ return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain) &&
+ hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].twchain);
}
static void *established_get_first(struct seq_file *seq)
{
- struct tcp_iter_state* st = seq->private;
+ struct tcp_iter_state *st = seq->private;
struct net *net = seq_file_net(seq);
void *rc = NULL;
for (st->bucket = 0; st->bucket < tcp_hashinfo.ehash_size; ++st->bucket) {
struct sock *sk;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
struct inet_timewait_sock *tw;
- rwlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket);
+ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket);
/* Lockless fast path for the common case of empty buckets */
if (empty_bucket(st))
continue;
- read_lock_bh(lock);
- sk_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
+ spin_lock_bh(lock);
+ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[st->bucket].chain) {
if (sk->sk_family != st->family ||
!net_eq(sock_net(sk), net)) {
continue;
@@ -1996,7 +1998,7 @@ static void *established_get_first(struct seq_file *seq)
rc = tw;
goto out;
}
- read_unlock_bh(lock);
+ spin_unlock_bh(lock);
st->state = TCP_SEQ_STATE_ESTABLISHED;
}
out:
@@ -2007,8 +2009,8 @@ static void *established_get_next(struct seq_file *seq, void *cur)
{
struct sock *sk = cur;
struct inet_timewait_sock *tw;
- struct hlist_node *node;
- struct tcp_iter_state* st = seq->private;
+ struct hlist_nulls_node *node;
+ struct tcp_iter_state *st = seq->private;
struct net *net = seq_file_net(seq);
++st->num;
@@ -2024,7 +2026,7 @@ get_tw:
cur = tw;
goto out;
}
- read_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
+ spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
st->state = TCP_SEQ_STATE_ESTABLISHED;
/* Look for next non empty bucket */
@@ -2034,12 +2036,12 @@ get_tw:
if (st->bucket >= tcp_hashinfo.ehash_size)
return NULL;
- read_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
- sk = sk_head(&tcp_hashinfo.ehash[st->bucket].chain);
+ spin_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
+ sk = sk_nulls_head(&tcp_hashinfo.ehash[st->bucket].chain);
} else
- sk = sk_next(sk);
+ sk = sk_nulls_next(sk);
- sk_for_each_from(sk, node) {
+ sk_nulls_for_each_from(sk, node) {
if (sk->sk_family == st->family && net_eq(sock_net(sk), net))
goto found;
}
@@ -2067,14 +2069,12 @@ static void *established_get_idx(struct seq_file *seq, loff_t pos)
static void *tcp_get_idx(struct seq_file *seq, loff_t pos)
{
void *rc;
- struct tcp_iter_state* st = seq->private;
+ struct tcp_iter_state *st = seq->private;
- inet_listen_lock(&tcp_hashinfo);
st->state = TCP_SEQ_STATE_LISTENING;
rc = listening_get_idx(seq, &pos);
if (!rc) {
- inet_listen_unlock(&tcp_hashinfo);
st->state = TCP_SEQ_STATE_ESTABLISHED;
rc = established_get_idx(seq, pos);
}
@@ -2084,7 +2084,7 @@ static void *tcp_get_idx(struct seq_file *seq, loff_t pos)
static void *tcp_seq_start(struct seq_file *seq, loff_t *pos)
{
- struct tcp_iter_state* st = seq->private;
+ struct tcp_iter_state *st = seq->private;
st->state = TCP_SEQ_STATE_LISTENING;
st->num = 0;
return *pos ? tcp_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
@@ -2093,7 +2093,7 @@ static void *tcp_seq_start(struct seq_file *seq, loff_t *pos)
static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
void *rc = NULL;
- struct tcp_iter_state* st;
+ struct tcp_iter_state *st;
if (v == SEQ_START_TOKEN) {
rc = tcp_get_idx(seq, 0);
@@ -2106,7 +2106,6 @@ static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
case TCP_SEQ_STATE_LISTENING:
rc = listening_get_next(seq, v);
if (!rc) {
- inet_listen_unlock(&tcp_hashinfo);
st->state = TCP_SEQ_STATE_ESTABLISHED;
rc = established_get_first(seq);
}
@@ -2123,7 +2122,7 @@ out:
static void tcp_seq_stop(struct seq_file *seq, void *v)
{
- struct tcp_iter_state* st = seq->private;
+ struct tcp_iter_state *st = seq->private;
switch (st->state) {
case TCP_SEQ_STATE_OPENREQ:
@@ -2133,12 +2132,12 @@ static void tcp_seq_stop(struct seq_file *seq, void *v)
}
case TCP_SEQ_STATE_LISTENING:
if (v != SEQ_START_TOKEN)
- inet_listen_unlock(&tcp_hashinfo);
+ spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock);
break;
case TCP_SEQ_STATE_TIME_WAIT:
case TCP_SEQ_STATE_ESTABLISHED:
if (v)
- read_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
+ spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket));
break;
}
}
@@ -2284,7 +2283,7 @@ static void get_timewait4_sock(struct inet_timewait_sock *tw,
static int tcp4_seq_show(struct seq_file *seq, void *v)
{
- struct tcp_iter_state* st;
+ struct tcp_iter_state *st;
int len;
if (v == SEQ_START_TOKEN) {
@@ -2350,6 +2349,41 @@ void tcp4_proc_exit(void)
}
#endif /* CONFIG_PROC_FS */
+struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
+{
+ struct iphdr *iph = ip_hdr(skb);
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (!tcp_v4_check(skb->len, iph->saddr, iph->daddr,
+ skb->csum)) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ }
+
+ /* fall through */
+ case CHECKSUM_NONE:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ return tcp_gro_receive(head, skb);
+}
+EXPORT_SYMBOL(tcp4_gro_receive);
+
+int tcp4_gro_complete(struct sk_buff *skb)
+{
+ struct iphdr *iph = ip_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
+ th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
+ iph->saddr, iph->daddr, 0);
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+
+ return tcp_gro_complete(skb);
+}
+EXPORT_SYMBOL(tcp4_gro_complete);
+
struct proto tcp_prot = {
.name = "TCP",
.owner = THIS_MODULE,
@@ -2378,6 +2412,7 @@ struct proto tcp_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
+ .slab_flags = SLAB_DESTROY_BY_RCU,
.twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
.h.hashinfo = &tcp_hashinfo,
@@ -2407,6 +2442,7 @@ static struct pernet_operations __net_initdata tcp_sk_ops = {
void __init tcp_v4_init(void)
{
+ inet_hashinfo_init(&tcp_hashinfo);
if (register_pernet_device(&tcp_sk_ops))
panic("Failed to create the TCP control socket.\n");
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 779f2e9d0689..f67effbb102b 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -491,7 +491,7 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
* as a request_sock.
*/
-struct sock *tcp_check_req(struct sock *sk,struct sk_buff *skb,
+struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct request_sock **prev)
{
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index fe3b4bdfd251..557fe16cbfb0 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -42,7 +42,7 @@
/* People can turn this off for buggy TCP's found in printers etc. */
int sysctl_tcp_retrans_collapse __read_mostly = 1;
-/* People can turn this on to work with those rare, broken TCPs that
+/* People can turn this on to work with those rare, broken TCPs that
* interpret the window field as a signed quantity.
*/
int sysctl_tcp_workaround_signed_windows __read_mostly = 0;
@@ -484,7 +484,7 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
}
if (likely(sysctl_tcp_window_scaling)) {
opts->ws = tp->rx_opt.rcv_wscale;
- if(likely(opts->ws))
+ if (likely(opts->ws))
size += TCPOLEN_WSCALE_ALIGNED;
}
if (likely(sysctl_tcp_sack)) {
@@ -526,7 +526,7 @@ static unsigned tcp_synack_options(struct sock *sk,
if (likely(ireq->wscale_ok)) {
opts->ws = ireq->rcv_wscale;
- if(likely(opts->ws))
+ if (likely(opts->ws))
size += TCPOLEN_WSCALE_ALIGNED;
}
if (likely(doing_ts)) {
@@ -663,10 +663,14 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
th->urg_ptr = 0;
/* The urg_mode check is necessary during a below snd_una win probe */
- if (unlikely(tcp_urg_mode(tp) &&
- between(tp->snd_up, tcb->seq + 1, tcb->seq + 0xFFFF))) {
- th->urg_ptr = htons(tp->snd_up - tcb->seq);
- th->urg = 1;
+ if (unlikely(tcp_urg_mode(tp))) {
+ if (between(tp->snd_up, tcb->seq + 1, tcb->seq + 0xFFFF)) {
+ th->urg_ptr = htons(tp->snd_up - tcb->seq);
+ th->urg = 1;
+ } else if (after(tcb->seq + 0xFFFF, tp->snd_nxt)) {
+ th->urg_ptr = 0xFFFF;
+ th->urg = 1;
+ }
}
tcp_options_write((__be32 *)(th + 1), tp, &opts, &md5_hash_location);
@@ -1168,7 +1172,7 @@ static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb,
static inline int tcp_minshall_check(const struct tcp_sock *tp)
{
- return after(tp->snd_sml,tp->snd_una) &&
+ return after(tp->snd_sml, tp->snd_una) &&
!after(tp->snd_sml, tp->snd_nxt);
}
@@ -1334,7 +1338,7 @@ static int tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
/* Defer for less than two clock ticks. */
if (tp->tso_deferred &&
- ((jiffies << 1) >> 1) - (tp->tso_deferred >> 1) > 1)
+ (((u32)jiffies << 1) >> 1) - (tp->tso_deferred >> 1) > 1)
goto send_now;
in_flight = tcp_packets_in_flight(tp);
@@ -1519,7 +1523,8 @@ static int tcp_mtu_probe(struct sock *sk)
* Returns 1, if no segments are in flight and we have queued segments, but
* cannot send anything now because of SWS or another problem.
*/
-static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle)
+static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
+ int push_one, gfp_t gfp)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
@@ -1527,20 +1532,16 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle)
int cwnd_quota;
int result;
- /* If we are closed, the bytes will have to remain here.
- * In time closedown will finish, we empty the write queue and all
- * will be happy.
- */
- if (unlikely(sk->sk_state == TCP_CLOSE))
- return 0;
-
sent_pkts = 0;
- /* Do MTU probing. */
- if ((result = tcp_mtu_probe(sk)) == 0) {
- return 0;
- } else if (result > 0) {
- sent_pkts = 1;
+ if (!push_one) {
+ /* Do MTU probing. */
+ result = tcp_mtu_probe(sk);
+ if (!result) {
+ return 0;
+ } else if (result > 0) {
+ sent_pkts = 1;
+ }
}
while ((skb = tcp_send_head(sk))) {
@@ -1562,7 +1563,7 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle)
nonagle : TCP_NAGLE_PUSH))))
break;
} else {
- if (tcp_tso_should_defer(sk, skb))
+ if (!push_one && tcp_tso_should_defer(sk, skb))
break;
}
@@ -1577,7 +1578,7 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle)
TCP_SKB_CB(skb)->when = tcp_time_stamp;
- if (unlikely(tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC)))
+ if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
break;
/* Advance the send_head. This one is sent out.
@@ -1587,6 +1588,9 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle)
tcp_minshall_update(tp, mss_now, skb);
sent_pkts++;
+
+ if (push_one)
+ break;
}
if (likely(sent_pkts)) {
@@ -1605,10 +1609,18 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
{
struct sk_buff *skb = tcp_send_head(sk);
- if (skb) {
- if (tcp_write_xmit(sk, cur_mss, nonagle))
- tcp_check_probe_timer(sk);
- }
+ if (!skb)
+ return;
+
+ /* If we are closed, the bytes will have to remain here.
+ * In time closedown will finish, we empty the write queue and
+ * all will be happy.
+ */
+ if (unlikely(sk->sk_state == TCP_CLOSE))
+ return;
+
+ if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
+ tcp_check_probe_timer(sk);
}
/* Send _single_ skb sitting at the send head. This function requires
@@ -1616,38 +1628,11 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
*/
void tcp_push_one(struct sock *sk, unsigned int mss_now)
{
- struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb = tcp_send_head(sk);
- unsigned int tso_segs, cwnd_quota;
BUG_ON(!skb || skb->len < mss_now);
- tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
- cwnd_quota = tcp_snd_test(sk, skb, mss_now, TCP_NAGLE_PUSH);
-
- if (likely(cwnd_quota)) {
- unsigned int limit;
-
- BUG_ON(!tso_segs);
-
- limit = mss_now;
- if (tso_segs > 1 && !tcp_urg_mode(tp))
- limit = tcp_mss_split_point(sk, skb, mss_now,
- cwnd_quota);
-
- if (skb->len > limit &&
- unlikely(tso_fragment(sk, skb, limit, mss_now)))
- return;
-
- /* Send it out now. */
- TCP_SKB_CB(skb)->when = tcp_time_stamp;
-
- if (likely(!tcp_transmit_skb(sk, skb, 1, sk->sk_allocation))) {
- tcp_event_new_data_sent(sk, skb);
- tcp_cwnd_validate(sk);
- return;
- }
- }
+ tcp_write_xmit(sk, mss_now, TCP_NAGLE_PUSH, 1, sk->sk_allocation);
}
/* This function returns the amount that we can raise the
@@ -1767,46 +1752,22 @@ u32 __tcp_select_window(struct sock *sk)
return window;
}
-/* Attempt to collapse two adjacent SKB's during retransmission. */
-static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb,
- int mss_now)
+/* Collapses two adjacent SKB's during retransmission. */
+static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *next_skb = tcp_write_queue_next(sk, skb);
int skb_size, next_skb_size;
u16 flags;
- /* The first test we must make is that neither of these two
- * SKB's are still referenced by someone else.
- */
- if (skb_cloned(skb) || skb_cloned(next_skb))
- return;
-
skb_size = skb->len;
next_skb_size = next_skb->len;
flags = TCP_SKB_CB(skb)->flags;
- /* Also punt if next skb has been SACK'd. */
- if (TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED)
- return;
-
- /* Next skb is out of window. */
- if (after(TCP_SKB_CB(next_skb)->end_seq, tcp_wnd_end(tp)))
- return;
-
- /* Punt if not enough space exists in the first SKB for
- * the data in the second, or the total combined payload
- * would exceed the MSS.
- */
- if ((next_skb_size > skb_tailroom(skb)) ||
- ((skb_size + next_skb_size) > mss_now))
- return;
-
BUG_ON(tcp_skb_pcount(skb) != 1 || tcp_skb_pcount(next_skb) != 1);
tcp_highest_sack_combine(sk, next_skb, skb);
- /* Ok. We will be able to collapse the packet. */
tcp_unlink_write_queue(next_skb, sk);
skb_copy_from_linear_data(next_skb, skb_put(skb, next_skb_size),
@@ -1848,54 +1809,60 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb,
sk_wmem_free_skb(sk, next_skb);
}
-/* Do a simple retransmit without using the backoff mechanisms in
- * tcp_timer. This is used for path mtu discovery.
- * The socket is already locked here.
- */
-void tcp_simple_retransmit(struct sock *sk)
+static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb)
+{
+ if (tcp_skb_pcount(skb) > 1)
+ return 0;
+ /* TODO: SACK collapsing could be used to remove this condition */
+ if (skb_shinfo(skb)->nr_frags != 0)
+ return 0;
+ if (skb_cloned(skb))
+ return 0;
+ if (skb == tcp_send_head(sk))
+ return 0;
+ /* Some heurestics for collapsing over SACK'd could be invented */
+ if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)
+ return 0;
+
+ return 1;
+}
+
+static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
+ int space)
{
- const struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
- struct sk_buff *skb;
- unsigned int mss = tcp_current_mss(sk, 0);
- u32 prior_lost = tp->lost_out;
+ struct sk_buff *skb = to, *tmp;
+ int first = 1;
- tcp_for_write_queue(skb, sk) {
- if (skb == tcp_send_head(sk))
+ if (!sysctl_tcp_retrans_collapse)
+ return;
+ if (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN)
+ return;
+
+ tcp_for_write_queue_from_safe(skb, tmp, sk) {
+ if (!tcp_can_collapse(sk, skb))
break;
- if (skb->len > mss &&
- !(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
- if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
- TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_RETRANS;
- tp->retrans_out -= tcp_skb_pcount(skb);
- }
- tcp_skb_mark_lost_uncond_verify(tp, skb);
- }
- }
- tcp_clear_retrans_hints_partial(tp);
+ space -= skb->len;
- if (prior_lost == tp->lost_out)
- return;
+ if (first) {
+ first = 0;
+ continue;
+ }
- if (tcp_is_reno(tp))
- tcp_limit_reno_sacked(tp);
+ if (space < 0)
+ break;
+ /* Punt if not enough space exists in the first SKB for
+ * the data in the second
+ */
+ if (skb->len > skb_tailroom(to))
+ break;
- tcp_verify_left_out(tp);
+ if (after(TCP_SKB_CB(skb)->end_seq, tcp_wnd_end(tp)))
+ break;
- /* Don't muck with the congestion window here.
- * Reason is that we do not increase amount of _data_
- * in network, but units changed and effective
- * cwnd/ssthresh really reduced now.
- */
- if (icsk->icsk_ca_state != TCP_CA_Loss) {
- tp->high_seq = tp->snd_nxt;
- tp->snd_ssthresh = tcp_current_ssthresh(sk);
- tp->prior_ssthresh = 0;
- tp->undo_marker = 0;
- tcp_set_ca_state(sk, TCP_CA_Loss);
+ tcp_collapse_retrans(sk, to);
}
- tcp_xmit_retransmit_queue(sk);
}
/* This retransmits one SKB. Policy decisions and retransmit queue
@@ -1947,17 +1914,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
return -ENOMEM; /* We'll try again later. */
}
- /* Collapse two adjacent packets if worthwhile and we can. */
- if (!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&
- (skb->len < (cur_mss >> 1)) &&
- (!tcp_skb_is_last(sk, skb)) &&
- (tcp_write_queue_next(sk, skb) != tcp_send_head(sk)) &&
- (skb_shinfo(skb)->nr_frags == 0 &&
- skb_shinfo(tcp_write_queue_next(sk, skb))->nr_frags == 0) &&
- (tcp_skb_pcount(skb) == 1 &&
- tcp_skb_pcount(tcp_write_queue_next(sk, skb)) == 1) &&
- (sysctl_tcp_retrans_collapse != 0))
- tcp_retrans_try_collapse(sk, skb, cur_mss);
+ tcp_retrans_try_collapse(sk, skb, cur_mss);
/* Some Solaris stacks overoptimize and ignore the FIN on a
* retransmit when old data is attached. So strip it off
diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c
index 7ddc30f0744f..25524d4e372a 100644
--- a/net/ipv4/tcp_probe.c
+++ b/net/ipv4/tcp_probe.c
@@ -153,12 +153,11 @@ static int tcpprobe_sprint(char *tbuf, int n)
= ktime_to_timespec(ktime_sub(p->tstamp, tcp_probe.start));
return snprintf(tbuf, n,
- "%lu.%09lu " NIPQUAD_FMT ":%u " NIPQUAD_FMT ":%u"
- " %d %#x %#x %u %u %u %u\n",
+ "%lu.%09lu %pI4:%u %pI4:%u %d %#x %#x %u %u %u %u\n",
(unsigned long) tv.tv_sec,
(unsigned long) tv.tv_nsec,
- NIPQUAD(p->saddr), ntohs(p->sport),
- NIPQUAD(p->daddr), ntohs(p->dport),
+ &p->saddr, ntohs(p->sport),
+ &p->daddr, ntohs(p->dport),
p->length, p->snd_nxt, p->snd_una,
p->snd_cwnd, p->ssthresh, p->snd_wnd, p->srtt);
}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 6b6dff1164b9..0170e914f1b0 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -65,7 +65,7 @@ static void tcp_write_err(struct sock *sk)
static int tcp_out_of_resources(struct sock *sk, int do_reset)
{
struct tcp_sock *tp = tcp_sk(sk);
- int orphans = atomic_read(&tcp_orphan_count);
+ int orphans = percpu_counter_read_positive(&tcp_orphan_count);
/* If peer does not open window for long time, or did not transmit
* anything for long time, penalize it. */
@@ -171,7 +171,7 @@ static int tcp_write_timeout(struct sock *sk)
static void tcp_delack_timer(unsigned long data)
{
- struct sock *sk = (struct sock*)data;
+ struct sock *sk = (struct sock *)data;
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -299,15 +299,15 @@ static void tcp_retransmit_timer(struct sock *sk)
#ifdef TCP_DEBUG
struct inet_sock *inet = inet_sk(sk);
if (sk->sk_family == AF_INET) {
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: Treason uncloaked! Peer " NIPQUAD_FMT ":%u/%u shrinks window %u:%u. Repaired.\n",
- NIPQUAD(inet->daddr), ntohs(inet->dport),
+ LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
+ &inet->daddr, ntohs(inet->dport),
inet->num, tp->snd_una, tp->snd_nxt);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (sk->sk_family == AF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
- LIMIT_NETDEBUG(KERN_DEBUG "TCP: Treason uncloaked! Peer " NIP6_FMT ":%u/%u shrinks window %u:%u. Repaired.\n",
- NIP6(np->daddr), ntohs(inet->dport),
+ LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
+ &np->daddr, ntohs(inet->dport),
inet->num, tp->snd_una, tp->snd_nxt);
}
#endif
@@ -396,7 +396,7 @@ out:;
static void tcp_write_timer(unsigned long data)
{
- struct sock *sk = (struct sock*)data;
+ struct sock *sk = (struct sock *)data;
struct inet_connection_sock *icsk = inet_csk(sk);
int event;
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
index e03b10183a8b..9ec843a9bbb2 100644
--- a/net/ipv4/tcp_yeah.c
+++ b/net/ipv4/tcp_yeah.c
@@ -83,7 +83,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
else if (!yeah->doing_reno_now) {
/* Scalable */
- tp->snd_cwnd_cnt+=yeah->pkts_acked;
+ tp->snd_cwnd_cnt += yeah->pkts_acked;
if (tp->snd_cwnd_cnt > min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)){
if (tp->snd_cwnd < tp->snd_cwnd_clamp)
tp->snd_cwnd++;
@@ -224,7 +224,7 @@ static u32 tcp_yeah_ssthresh(struct sock *sk) {
reduction = max( reduction, tp->snd_cwnd >> TCP_YEAH_DELTA);
} else
- reduction = max(tp->snd_cwnd>>1,2U);
+ reduction = max(tp->snd_cwnd>>1, 2U);
yeah->fast_count = 0;
yeah->reno_count = max(yeah->reno_count>>1, 2U);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 98c1fd09be88..cf5ab0581eba 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -81,6 +81,8 @@
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <linux/bootmem.h>
+#include <linux/highmem.h>
+#include <linux/swap.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/module.h>
@@ -104,12 +106,8 @@
#include <net/xfrm.h>
#include "udp_impl.h"
-/*
- * Snmp MIB for the UDP layer
- */
-
-struct hlist_head udp_hash[UDP_HTABLE_SIZE];
-DEFINE_RWLOCK(udp_hash_lock);
+struct udp_table udp_table;
+EXPORT_SYMBOL(udp_table);
int sysctl_udp_mem[3] __read_mostly;
int sysctl_udp_rmem_min __read_mostly;
@@ -123,15 +121,15 @@ atomic_t udp_memory_allocated;
EXPORT_SYMBOL(udp_memory_allocated);
static int udp_lib_lport_inuse(struct net *net, __u16 num,
- const struct hlist_head udptable[],
+ const struct udp_hslot *hslot,
struct sock *sk,
int (*saddr_comp)(const struct sock *sk1,
const struct sock *sk2))
{
struct sock *sk2;
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
- sk_for_each(sk2, node, &udptable[udp_hashfn(net, num)])
+ sk_nulls_for_each(sk2, node, &hslot->head)
if (net_eq(sock_net(sk2), net) &&
sk2 != sk &&
sk2->sk_hash == num &&
@@ -154,12 +152,11 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
int (*saddr_comp)(const struct sock *sk1,
const struct sock *sk2 ) )
{
- struct hlist_head *udptable = sk->sk_prot->h.udp_hash;
+ struct udp_hslot *hslot;
+ struct udp_table *udptable = sk->sk_prot->h.udp_table;
int error = 1;
struct net *net = sock_net(sk);
- write_lock_bh(&udp_hash_lock);
-
if (!snum) {
int low, high, remaining;
unsigned rand;
@@ -171,26 +168,34 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
rand = net_random();
snum = first = rand % remaining + low;
rand |= 1;
- while (udp_lib_lport_inuse(net, snum, udptable, sk,
- saddr_comp)) {
+ for (;;) {
+ hslot = &udptable->hash[udp_hashfn(net, snum)];
+ spin_lock_bh(&hslot->lock);
+ if (!udp_lib_lport_inuse(net, snum, hslot, sk, saddr_comp))
+ break;
+ spin_unlock_bh(&hslot->lock);
do {
snum = snum + rand;
} while (snum < low || snum > high);
if (snum == first)
goto fail;
}
- } else if (udp_lib_lport_inuse(net, snum, udptable, sk, saddr_comp))
- goto fail;
-
+ } else {
+ hslot = &udptable->hash[udp_hashfn(net, snum)];
+ spin_lock_bh(&hslot->lock);
+ if (udp_lib_lport_inuse(net, snum, hslot, sk, saddr_comp))
+ goto fail_unlock;
+ }
inet_sk(sk)->num = snum;
sk->sk_hash = snum;
if (sk_unhashed(sk)) {
- sk_add_node(sk, &udptable[udp_hashfn(net, snum)]);
+ sk_nulls_add_node_rcu(sk, &hslot->head);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
}
error = 0;
+fail_unlock:
+ spin_unlock_bh(&hslot->lock);
fail:
- write_unlock_bh(&udp_hash_lock);
return error;
}
@@ -208,63 +213,91 @@ int udp_v4_get_port(struct sock *sk, unsigned short snum)
return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal);
}
+static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
+ unsigned short hnum,
+ __be16 sport, __be32 daddr, __be16 dport, int dif)
+{
+ int score = -1;
+
+ if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
+ !ipv6_only_sock(sk)) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ score = (sk->sk_family == PF_INET ? 1 : 0);
+ if (inet->rcv_saddr) {
+ if (inet->rcv_saddr != daddr)
+ return -1;
+ score += 2;
+ }
+ if (inet->daddr) {
+ if (inet->daddr != saddr)
+ return -1;
+ score += 2;
+ }
+ if (inet->dport) {
+ if (inet->dport != sport)
+ return -1;
+ score += 2;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score += 2;
+ }
+ }
+ return score;
+}
+
/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
* harder than this. -DaveM
*/
static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
__be16 sport, __be32 daddr, __be16 dport,
- int dif, struct hlist_head udptable[])
+ int dif, struct udp_table *udptable)
{
- struct sock *sk, *result = NULL;
- struct hlist_node *node;
+ struct sock *sk, *result;
+ struct hlist_nulls_node *node;
unsigned short hnum = ntohs(dport);
- int badness = -1;
-
- read_lock(&udp_hash_lock);
- sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) {
- struct inet_sock *inet = inet_sk(sk);
-
- if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
- !ipv6_only_sock(sk)) {
- int score = (sk->sk_family == PF_INET ? 1 : 0);
- if (inet->rcv_saddr) {
- if (inet->rcv_saddr != daddr)
- continue;
- score+=2;
- }
- if (inet->daddr) {
- if (inet->daddr != saddr)
- continue;
- score+=2;
- }
- if (inet->dport) {
- if (inet->dport != sport)
- continue;
- score+=2;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- continue;
- score+=2;
- }
- if (score == 9) {
- result = sk;
- break;
- } else if (score > badness) {
- result = sk;
- badness = score;
- }
+ unsigned int hash = udp_hashfn(net, hnum);
+ struct udp_hslot *hslot = &udptable->hash[hash];
+ int score, badness;
+
+ rcu_read_lock();
+begin:
+ result = NULL;
+ badness = -1;
+ sk_nulls_for_each_rcu(sk, node, &hslot->head) {
+ score = compute_score(sk, net, saddr, hnum, sport,
+ daddr, dport, dif);
+ if (score > badness) {
+ result = sk;
+ badness = score;
}
}
- if (result)
- sock_hold(result);
- read_unlock(&udp_hash_lock);
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != hash)
+ goto begin;
+
+ if (result) {
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score(result, net, saddr, hnum, sport,
+ daddr, dport, dif) < badness)) {
+ sock_put(result);
+ goto begin;
+ }
+ }
+ rcu_read_unlock();
return result;
}
static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
__be16 sport, __be16 dport,
- struct hlist_head udptable[])
+ struct udp_table *udptable)
{
struct sock *sk;
const struct iphdr *iph = ip_hdr(skb);
@@ -280,7 +313,7 @@ static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif)
{
- return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif, udp_hash);
+ return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table);
}
EXPORT_SYMBOL_GPL(udp4_lib_lookup);
@@ -289,11 +322,11 @@ static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk,
__be16 rmt_port, __be32 rmt_addr,
int dif)
{
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
struct sock *s = sk;
unsigned short hnum = ntohs(loc_port);
- sk_for_each_from(s, node) {
+ sk_nulls_for_each_from(s, node) {
struct inet_sock *inet = inet_sk(s);
if (!net_eq(sock_net(s), net) ||
@@ -324,7 +357,7 @@ found:
* to find the appropriate port.
*/
-void __udp4_lib_err(struct sk_buff *skb, u32 info, struct hlist_head udptable[])
+void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
{
struct inet_sock *inet;
struct iphdr *iph = (struct iphdr*)skb->data;
@@ -393,7 +426,7 @@ out:
void udp_err(struct sk_buff *skb, u32 info)
{
- __udp4_lib_err(skb, info, udp_hash);
+ __udp4_lib_err(skb, info, &udp_table);
}
/*
@@ -686,7 +719,7 @@ do_append_data:
up->len += ulen;
getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
- sizeof(struct udphdr), &ipc, rt,
+ sizeof(struct udphdr), &ipc, &rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err)
udp_flush_pending_frames(sk);
@@ -935,6 +968,23 @@ int udp_disconnect(struct sock *sk, int flags)
return 0;
}
+void udp_lib_unhash(struct sock *sk)
+{
+ if (sk_hashed(sk)) {
+ struct udp_table *udptable = sk->sk_prot->h.udp_table;
+ unsigned int hash = udp_hashfn(sock_net(sk), sk->sk_hash);
+ struct udp_hslot *hslot = &udptable->hash[hash];
+
+ spin_lock_bh(&hslot->lock);
+ if (sk_nulls_del_node_init_rcu(sk)) {
+ inet_sk(sk)->num = 0;
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ }
+ spin_unlock_bh(&hslot->lock);
+ }
+}
+EXPORT_SYMBOL(udp_lib_unhash);
+
static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
int is_udplite = IS_UDPLITE(sk);
@@ -1073,13 +1123,14 @@ drop:
static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
struct udphdr *uh,
__be32 saddr, __be32 daddr,
- struct hlist_head udptable[])
+ struct udp_table *udptable)
{
struct sock *sk;
+ struct udp_hslot *hslot = &udptable->hash[udp_hashfn(net, ntohs(uh->dest))];
int dif;
- read_lock(&udp_hash_lock);
- sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]);
+ spin_lock(&hslot->lock);
+ sk = sk_nulls_head(&hslot->head);
dif = skb->dev->ifindex;
sk = udp_v4_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
if (sk) {
@@ -1088,7 +1139,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
do {
struct sk_buff *skb1 = skb;
- sknext = udp_v4_mcast_next(net, sk_next(sk), uh->dest,
+ sknext = udp_v4_mcast_next(net, sk_nulls_next(sk), uh->dest,
daddr, uh->source, saddr,
dif);
if (sknext)
@@ -1105,7 +1156,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
} while (sknext);
} else
kfree_skb(skb);
- read_unlock(&udp_hash_lock);
+ spin_unlock(&hslot->lock);
return 0;
}
@@ -1151,7 +1202,7 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
* All we need to do is get the socket, and then do a checksum.
*/
-int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
+int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
int proto)
{
struct sock *sk;
@@ -1219,13 +1270,13 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
return 0;
short_packet:
- LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From " NIPQUAD_FMT ":%u %d/%d to " NIPQUAD_FMT ":%u\n",
+ LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",
proto == IPPROTO_UDPLITE ? "-Lite" : "",
- NIPQUAD(saddr),
+ &saddr,
ntohs(uh->source),
ulen,
skb->len,
- NIPQUAD(daddr),
+ &daddr,
ntohs(uh->dest));
goto drop;
@@ -1234,11 +1285,11 @@ csum_error:
* RFC1122: OK. Discards the bad packet silently (as far as
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
- LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From " NIPQUAD_FMT ":%u to " NIPQUAD_FMT ":%u ulen %d\n",
+ LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
proto == IPPROTO_UDPLITE ? "-Lite" : "",
- NIPQUAD(saddr),
+ &saddr,
ntohs(uh->source),
- NIPQUAD(daddr),
+ &daddr,
ntohs(uh->dest),
ulen);
drop:
@@ -1249,7 +1300,7 @@ drop:
int udp_rcv(struct sk_buff *skb)
{
- return __udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);
+ return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);
}
void udp_destroy_sock(struct sock *sk)
@@ -1491,7 +1542,8 @@ struct proto udp_prot = {
.sysctl_wmem = &sysctl_udp_wmem_min,
.sysctl_rmem = &sysctl_udp_rmem_min,
.obj_size = sizeof(struct udp_sock),
- .h.udp_hash = udp_hash,
+ .slab_flags = SLAB_DESTROY_BY_RCU,
+ .h.udp_table = &udp_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
@@ -1501,20 +1553,23 @@ struct proto udp_prot = {
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_PROC_FS
-static struct sock *udp_get_first(struct seq_file *seq)
+static struct sock *udp_get_first(struct seq_file *seq, int start)
{
struct sock *sk;
struct udp_iter_state *state = seq->private;
struct net *net = seq_file_net(seq);
- for (state->bucket = 0; state->bucket < UDP_HTABLE_SIZE; ++state->bucket) {
- struct hlist_node *node;
- sk_for_each(sk, node, state->hashtable + state->bucket) {
+ for (state->bucket = start; state->bucket < UDP_HTABLE_SIZE; ++state->bucket) {
+ struct hlist_nulls_node *node;
+ struct udp_hslot *hslot = &state->udp_table->hash[state->bucket];
+ spin_lock_bh(&hslot->lock);
+ sk_nulls_for_each(sk, node, &hslot->head) {
if (!net_eq(sock_net(sk), net))
continue;
if (sk->sk_family == state->family)
goto found;
}
+ spin_unlock_bh(&hslot->lock);
}
sk = NULL;
found:
@@ -1527,21 +1582,19 @@ static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk)
struct net *net = seq_file_net(seq);
do {
- sk = sk_next(sk);
-try_again:
- ;
+ sk = sk_nulls_next(sk);
} while (sk && (!net_eq(sock_net(sk), net) || sk->sk_family != state->family));
- if (!sk && ++state->bucket < UDP_HTABLE_SIZE) {
- sk = sk_head(state->hashtable + state->bucket);
- goto try_again;
+ if (!sk) {
+ spin_unlock_bh(&state->udp_table->hash[state->bucket].lock);
+ return udp_get_first(seq, state->bucket + 1);
}
return sk;
}
static struct sock *udp_get_idx(struct seq_file *seq, loff_t pos)
{
- struct sock *sk = udp_get_first(seq);
+ struct sock *sk = udp_get_first(seq, 0);
if (sk)
while (pos && (sk = udp_get_next(seq, sk)) != NULL)
@@ -1550,9 +1603,7 @@ static struct sock *udp_get_idx(struct seq_file *seq, loff_t pos)
}
static void *udp_seq_start(struct seq_file *seq, loff_t *pos)
- __acquires(udp_hash_lock)
{
- read_lock(&udp_hash_lock);
return *pos ? udp_get_idx(seq, *pos-1) : SEQ_START_TOKEN;
}
@@ -1570,9 +1621,11 @@ static void *udp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
}
static void udp_seq_stop(struct seq_file *seq, void *v)
- __releases(udp_hash_lock)
{
- read_unlock(&udp_hash_lock);
+ struct udp_iter_state *state = seq->private;
+
+ if (state->bucket < UDP_HTABLE_SIZE)
+ spin_unlock_bh(&state->udp_table->hash[state->bucket].lock);
}
static int udp_seq_open(struct inode *inode, struct file *file)
@@ -1588,7 +1641,7 @@ static int udp_seq_open(struct inode *inode, struct file *file)
s = ((struct seq_file *)file->private_data)->private;
s->family = afinfo->family;
- s->hashtable = afinfo->hashtable;
+ s->udp_table = afinfo->udp_table;
return err;
}
@@ -1660,7 +1713,7 @@ int udp4_seq_show(struct seq_file *seq, void *v)
static struct udp_seq_afinfo udp4_seq_afinfo = {
.name = "udp",
.family = AF_INET,
- .hashtable = udp_hash,
+ .udp_table = &udp_table,
.seq_fops = {
.owner = THIS_MODULE,
},
@@ -1695,16 +1748,28 @@ void udp4_proc_exit(void)
}
#endif /* CONFIG_PROC_FS */
+void __init udp_table_init(struct udp_table *table)
+{
+ int i;
+
+ for (i = 0; i < UDP_HTABLE_SIZE; i++) {
+ INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);
+ spin_lock_init(&table->hash[i].lock);
+ }
+}
+
void __init udp_init(void)
{
- unsigned long limit;
+ unsigned long nr_pages, limit;
+ udp_table_init(&udp_table);
/* Set the pressure threshold up by the same strategy of TCP. It is a
* fraction of global memory that is up to 1/2 at 256 MB, decreasing
* toward zero with the amount of memory, with a floor of 128 pages.
*/
- limit = min(nr_all_pages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT);
- limit = (limit * (nr_all_pages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11);
+ nr_pages = totalram_pages - totalhigh_pages;
+ limit = min(nr_pages, 1UL<<(28-PAGE_SHIFT)) >> (20-PAGE_SHIFT);
+ limit = (limit * (nr_pages >> (20-PAGE_SHIFT))) >> (PAGE_SHIFT-11);
limit = max(limit, 128UL);
sysctl_udp_mem[0] = limit / 4 * 3;
sysctl_udp_mem[1] = limit;
@@ -1715,8 +1780,6 @@ void __init udp_init(void)
}
EXPORT_SYMBOL(udp_disconnect);
-EXPORT_SYMBOL(udp_hash);
-EXPORT_SYMBOL(udp_hash_lock);
EXPORT_SYMBOL(udp_ioctl);
EXPORT_SYMBOL(udp_prot);
EXPORT_SYMBOL(udp_sendmsg);
diff --git a/net/ipv4/udp_impl.h b/net/ipv4/udp_impl.h
index 2e9bad2fa1bc..9f4a6165f722 100644
--- a/net/ipv4/udp_impl.h
+++ b/net/ipv4/udp_impl.h
@@ -5,8 +5,8 @@
#include <net/protocol.h>
#include <net/inet_common.h>
-extern int __udp4_lib_rcv(struct sk_buff *, struct hlist_head [], int );
-extern void __udp4_lib_err(struct sk_buff *, u32, struct hlist_head []);
+extern int __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int );
+extern void __udp4_lib_err(struct sk_buff *, u32, struct udp_table *);
extern int udp_v4_get_port(struct sock *sk, unsigned short snum);
diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c
index 3c807964da96..c784891cb7e5 100644
--- a/net/ipv4/udplite.c
+++ b/net/ipv4/udplite.c
@@ -12,16 +12,17 @@
*/
#include "udp_impl.h"
-struct hlist_head udplite_hash[UDP_HTABLE_SIZE];
+struct udp_table udplite_table;
+EXPORT_SYMBOL(udplite_table);
static int udplite_rcv(struct sk_buff *skb)
{
- return __udp4_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE);
+ return __udp4_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
}
static void udplite_err(struct sk_buff *skb, u32 info)
{
- __udp4_lib_err(skb, info, udplite_hash);
+ __udp4_lib_err(skb, info, &udplite_table);
}
static struct net_protocol udplite_protocol = {
@@ -50,7 +51,8 @@ struct proto udplite_prot = {
.unhash = udp_lib_unhash,
.get_port = udp_v4_get_port,
.obj_size = sizeof(struct udp_sock),
- .h.udp_hash = udplite_hash,
+ .slab_flags = SLAB_DESTROY_BY_RCU,
+ .h.udp_table = &udplite_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udp_setsockopt,
.compat_getsockopt = compat_udp_getsockopt,
@@ -71,7 +73,7 @@ static struct inet_protosw udplite4_protosw = {
static struct udp_seq_afinfo udplite4_seq_afinfo = {
.name = "udplite",
.family = AF_INET,
- .hashtable = udplite_hash,
+ .udp_table = &udplite_table,
.seq_fops = {
.owner = THIS_MODULE,
},
@@ -108,6 +110,7 @@ static inline int udplite4_proc_init(void)
void __init udplite4_register(void)
{
+ udp_table_init(&udplite_table);
if (proto_register(&udplite_prot, 1))
goto out_register_err;
@@ -126,5 +129,4 @@ out_register_err:
printk(KERN_CRIT "%s: Cannot add UDP-Lite protocol.\n", __func__);
}
-EXPORT_SYMBOL(udplite_hash);
EXPORT_SYMBOL(udplite_prot);
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
index 390dcb1354a5..4ec2162a437e 100644
--- a/net/ipv4/xfrm4_input.c
+++ b/net/ipv4/xfrm4_input.c
@@ -78,7 +78,6 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
struct udphdr *uh;
struct iphdr *iph;
int iphlen, len;
- int ret;
__u8 *udpdata;
__be32 *udpdata32;
@@ -152,8 +151,7 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
skb_reset_transport_header(skb);
/* process ESP */
- ret = xfrm4_rcv_encap(skb, IPPROTO_ESP, 0, encap_type);
- return ret;
+ return xfrm4_rcv_encap(skb, IPPROTO_ESP, 0, encap_type);
drop:
kfree_skb(skb);
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index c63de0a72aba..2ad24ba31f9d 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -18,7 +18,8 @@
static struct dst_ops xfrm4_dst_ops;
static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
-static struct dst_entry *xfrm4_dst_lookup(int tos, xfrm_address_t *saddr,
+static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos,
+ xfrm_address_t *saddr,
xfrm_address_t *daddr)
{
struct flowi fl = {
@@ -36,19 +37,20 @@ static struct dst_entry *xfrm4_dst_lookup(int tos, xfrm_address_t *saddr,
if (saddr)
fl.fl4_src = saddr->a4;
- err = __ip_route_output_key(&init_net, &rt, &fl);
+ err = __ip_route_output_key(net, &rt, &fl);
dst = &rt->u.dst;
if (err)
dst = ERR_PTR(err);
return dst;
}
-static int xfrm4_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
+static int xfrm4_get_saddr(struct net *net,
+ xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct dst_entry *dst;
struct rtable *rt;
- dst = xfrm4_dst_lookup(0, NULL, daddr);
+ dst = xfrm4_dst_lookup(net, 0, NULL, daddr);
if (IS_ERR(dst))
return -EHOSTUNREACH;
@@ -65,7 +67,7 @@ __xfrm4_find_bundle(struct flowi *fl, struct xfrm_policy *policy)
read_lock_bh(&policy->lock);
for (dst = policy->bundles; dst; dst = dst->next) {
- struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
+ struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
if (xdst->u.rt.fl.oif == fl->oif && /*XXX*/
xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
xdst->u.rt.fl.fl4_src == fl->fl4_src &&
@@ -187,7 +189,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse)
static inline int xfrm4_garbage_collect(struct dst_ops *ops)
{
- xfrm4_policy_afinfo.garbage_collect();
+ xfrm4_policy_afinfo.garbage_collect(&init_net);
return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
}
@@ -246,7 +248,6 @@ static struct dst_ops xfrm4_dst_ops = {
.ifdown = xfrm4_dst_ifdown,
.local_out = __ip_local_out,
.gc_thresh = 1024,
- .entry_size = sizeof(struct xfrm_dst),
.entries = ATOMIC_INIT(0),
};
diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c
index 55dc6beab9aa..1ef1366a0a03 100644
--- a/net/ipv4/xfrm4_state.c
+++ b/net/ipv4/xfrm4_state.c
@@ -13,8 +13,6 @@
#include <linux/ipsec.h>
#include <linux/netfilter_ipv4.h>
-static struct xfrm_state_afinfo xfrm4_state_afinfo;
-
static int xfrm4_init_flags(struct xfrm_state *x)
{
if (ipv4_config.no_pmtu_disc)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d9da5eb9dcb2..e92ad8455c63 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2031,8 +2031,8 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
if (dev->type == ARPHRD_SIT) {
+ const struct net_device_ops *ops = dev->netdev_ops;
struct ifreq ifr;
- mm_segment_t oldfs;
struct ip_tunnel_parm p;
err = -EADDRNOTAVAIL;
@@ -2048,9 +2048,14 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
p.iph.ttl = 64;
ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
- set_fs(oldfs);
+ if (ops->ndo_do_ioctl) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+ set_fs(oldfs);
+ } else
+ err = -EOPNOTSUPP;
if (err == 0) {
err = -ENOBUFS;
@@ -2988,9 +2993,8 @@ static void if6_seq_stop(struct seq_file *seq, void *v)
static int if6_seq_show(struct seq_file *seq, void *v)
{
struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
- seq_printf(seq,
- NIP6_SEQFMT " %02x %02x %02x %02x %8s\n",
- NIP6(ifp->addr),
+ seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n",
+ &ifp->addr,
ifp->idev->dev->ifindex,
ifp->prefix_len,
ifp->scope,
@@ -4033,8 +4037,8 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.forwarding,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &addrconf_sysctl_forward,
- .strategy = &addrconf_sysctl_forward_strategy,
+ .proc_handler = addrconf_sysctl_forward,
+ .strategy = addrconf_sysctl_forward_strategy,
},
{
.ctl_name = NET_IPV6_HOP_LIMIT,
@@ -4050,7 +4054,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.mtu6,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ACCEPT_RA,
@@ -4058,7 +4062,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_ra,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ACCEPT_REDIRECTS,
@@ -4066,7 +4070,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_redirects,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_AUTOCONF,
@@ -4074,7 +4078,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.autoconf,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_DAD_TRANSMITS,
@@ -4082,7 +4086,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.dad_transmits,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_RTR_SOLICITS,
@@ -4090,7 +4094,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.rtr_solicits,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_RTR_SOLICIT_INTERVAL,
@@ -4098,8 +4102,8 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.rtr_solicit_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_RTR_SOLICIT_DELAY,
@@ -4107,8 +4111,8 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.rtr_solicit_delay,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_FORCE_MLD_VERSION,
@@ -4116,7 +4120,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.force_mld_version,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#ifdef CONFIG_IPV6_PRIVACY
{
@@ -4125,7 +4129,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.use_tempaddr,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_TEMP_VALID_LFT,
@@ -4133,7 +4137,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.temp_valid_lft,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_TEMP_PREFERED_LFT,
@@ -4141,7 +4145,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.temp_prefered_lft,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_REGEN_MAX_RETRY,
@@ -4149,7 +4153,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.regen_max_retry,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_MAX_DESYNC_FACTOR,
@@ -4157,7 +4161,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.max_desync_factor,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif
{
@@ -4166,7 +4170,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.max_addresses,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ACCEPT_RA_DEFRTR,
@@ -4174,7 +4178,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_ra_defrtr,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ACCEPT_RA_PINFO,
@@ -4182,7 +4186,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_ra_pinfo,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#ifdef CONFIG_IPV6_ROUTER_PREF
{
@@ -4191,7 +4195,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_ra_rtr_pref,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_RTR_PROBE_INTERVAL,
@@ -4199,8 +4203,8 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.rtr_probe_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
#ifdef CONFIG_IPV6_ROUTE_INFO
{
@@ -4209,7 +4213,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_ra_rt_info_max_plen,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif
#endif
@@ -4219,7 +4223,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.proxy_ndp,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ACCEPT_SOURCE_ROUTE,
@@ -4227,7 +4231,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_source_route,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
{
@@ -4236,7 +4240,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.optimistic_dad,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif
@@ -4247,7 +4251,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.mc_forwarding,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif
{
@@ -4256,7 +4260,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.disable_ipv6,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -4264,7 +4268,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.accept_dad,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = 0, /* sentinel */
diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c
index 08909039d87b..6ff73c4c126a 100644
--- a/net/ipv6/addrlabel.c
+++ b/net/ipv6/addrlabel.c
@@ -186,10 +186,8 @@ u32 ipv6_addr_label(struct net *net,
label = p ? p->label : IPV6_ADDR_LABEL_DEFAULT;
rcu_read_unlock();
- ADDRLABEL(KERN_DEBUG "%s(addr=" NIP6_FMT ", type=%d, ifindex=%d) => %08x\n",
- __func__,
- NIP6(*addr), type, ifindex,
- label);
+ ADDRLABEL(KERN_DEBUG "%s(addr=%pI6, type=%d, ifindex=%d) => %08x\n",
+ __func__, addr, type, ifindex, label);
return label;
}
@@ -203,11 +201,8 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net,
struct ip6addrlbl_entry *newp;
int addrtype;
- ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u)\n",
- __func__,
- NIP6(*prefix), prefixlen,
- ifindex,
- (unsigned int)label);
+ ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u)\n",
+ __func__, prefix, prefixlen, ifindex, (unsigned int)label);
addrtype = ipv6_addr_type(prefix) & (IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK);
@@ -294,12 +289,9 @@ static int ip6addrlbl_add(struct net *net,
struct ip6addrlbl_entry *newp;
int ret = 0;
- ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n",
- __func__,
- NIP6(*prefix), prefixlen,
- ifindex,
- (unsigned int)label,
- replace);
+ ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d, label=%u, replace=%d)\n",
+ __func__, prefix, prefixlen, ifindex, (unsigned int)label,
+ replace);
newp = ip6addrlbl_alloc(net, prefix, prefixlen, ifindex, label);
if (IS_ERR(newp))
@@ -321,10 +313,8 @@ static int __ip6addrlbl_del(struct net *net,
struct hlist_node *pos, *n;
int ret = -ESRCH;
- ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n",
- __func__,
- NIP6(*prefix), prefixlen,
- ifindex);
+ ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
+ __func__, prefix, prefixlen, ifindex);
hlist_for_each_entry_safe(p, pos, n, &ip6addrlbl_table.head, list) {
if (p->prefixlen == prefixlen &&
@@ -347,10 +337,8 @@ static int ip6addrlbl_del(struct net *net,
struct in6_addr prefix_buf;
int ret;
- ADDRLABEL(KERN_DEBUG "%s(prefix=" NIP6_FMT ", prefixlen=%d, ifindex=%d)\n",
- __func__,
- NIP6(*prefix), prefixlen,
- ifindex);
+ ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n",
+ __func__, prefix, prefixlen, ifindex);
ipv6_addr_prefix(&prefix_buf, prefix, prefixlen);
spin_lock(&ip6addrlbl_table.lock);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 01edac888510..437b750b98fd 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -637,7 +637,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) {
sk->sk_err_soft = -err;
return err;
}
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 2ff0c8233e47..52449f7a1b71 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -407,6 +407,7 @@ out:
static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __be32 info)
{
+ struct net *net = dev_net(skb->dev);
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);
struct xfrm_state *x;
@@ -415,12 +416,12 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
type != ICMPV6_PKT_TOOBIG)
return;
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
+ x = xfrm_state_lookup(net, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);
if (!x)
return;
- NETDEBUG(KERN_DEBUG "pmtu discovery on SA AH/%08x/" NIP6_FMT "\n",
- ntohl(ah->spi), NIP6(iph->daddr));
+ NETDEBUG(KERN_DEBUG "pmtu discovery on SA AH/%08x/%pI6\n",
+ ntohl(ah->spi), &iph->daddr);
xfrm_state_put(x);
}
@@ -509,9 +510,7 @@ static void ah6_destroy(struct xfrm_state *x)
return;
kfree(ahp->work_icv);
- ahp->work_icv = NULL;
crypto_free_hash(ahp->tfm);
- ahp->tfm = NULL;
kfree(ahp);
}
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index 8336cd81cb4f..1ae58bec1de0 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -512,11 +512,9 @@ static int ac6_seq_show(struct seq_file *seq, void *v)
struct ifacaddr6 *im = (struct ifacaddr6 *)v;
struct ac6_iter_state *state = ac6_seq_private(seq);
- seq_printf(seq,
- "%-4d %-15s " NIP6_SEQFMT " %5d\n",
+ seq_printf(seq, "%-4d %-15s %pi6 %5d\n",
state->dev->ifindex, state->dev->name,
- NIP6(im->aca_addr),
- im->aca_users);
+ &im->aca_addr, im->aca_users);
return 0;
}
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index e44deb8d4df2..e2bdc6d83a43 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -175,7 +175,8 @@ ipv4_connected:
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
+ err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+ if (err < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
if (err < 0)
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index b181b08fb761..c2f250150db1 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -356,6 +356,7 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu)
static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __be32 info)
{
+ struct net *net = dev_net(skb->dev);
struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;
struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset);
struct xfrm_state *x;
@@ -364,11 +365,11 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
type != ICMPV6_PKT_TOOBIG)
return;
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
+ x = xfrm_state_lookup(net, (xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET6);
if (!x)
return;
- printk(KERN_DEBUG "pmtu discovery on SA ESP/%08x/" NIP6_FMT "\n",
- ntohl(esph->spi), NIP6(iph->daddr));
+ printk(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%pI6\n",
+ ntohl(esph->spi), &iph->daddr);
xfrm_state_put(x);
}
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6bfffec2371c..1c7f400a3cfe 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -219,7 +219,7 @@ static int ipv6_dest_hao(struct sk_buff *skb, int optoff)
if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
LIMIT_NETDEBUG(
- KERN_DEBUG "hao is not an unicast addr: " NIP6_FMT "\n", NIP6(hao->addr));
+ KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr);
goto discard;
}
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 9b7d19ae5ced..4f433847d95f 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -233,7 +233,7 @@ static int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct
icmp6h->icmp6_cksum = 0;
if (skb_queue_len(&sk->sk_write_queue) == 1) {
- skb->csum = csum_partial((char *)icmp6h,
+ skb->csum = csum_partial(icmp6h,
sizeof(struct icmp6hdr), skb->csum);
icmp6h->icmp6_cksum = csum_ipv6_magic(&fl->fl6_src,
&fl->fl6_dst,
@@ -246,7 +246,7 @@ static int icmpv6_push_pending_frames(struct sock *sk, struct flowi *fl, struct
tmp_csum = csum_add(tmp_csum, skb->csum);
}
- tmp_csum = csum_partial((char *)icmp6h,
+ tmp_csum = csum_partial(icmp6h,
sizeof(struct icmp6hdr), tmp_csum);
icmp6h->icmp6_cksum = csum_ipv6_magic(&fl->fl6_src,
&fl->fl6_dst,
@@ -427,7 +427,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
/* No need to clone since we're just using its address. */
dst2 = dst;
- err = xfrm_lookup(&dst, &fl, sk, 0);
+ err = xfrm_lookup(net, &dst, &fl, sk, 0);
switch (err) {
case 0:
if (dst != dst2)
@@ -446,7 +446,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
if (ip6_dst_lookup(sk, &dst2, &fl))
goto relookup_failed;
- err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP);
+ err = xfrm_lookup(net, &dst2, &fl, sk, XFRM_LOOKUP_ICMP);
switch (err) {
case 0:
dst_release(dst);
@@ -552,7 +552,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0)
goto out;
if (ipv6_addr_is_multicast(&fl.fl6_dst))
@@ -646,9 +646,10 @@ static int icmpv6_rcv(struct sk_buff *skb)
int type;
if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
+ struct sec_path *sp = skb_sec_path(skb);
int nh;
- if (!(skb->sp && skb->sp->xvec[skb->sp->len - 1]->props.flags &
+ if (!(sp && sp->xvec[sp->len - 1]->props.flags &
XFRM_STATE_ICMP))
goto drop_no_count;
@@ -680,8 +681,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
IPPROTO_ICMPV6, 0));
if (__skb_checksum_complete(skb)) {
- LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [" NIP6_FMT " > " NIP6_FMT "]\n",
- NIP6(*saddr), NIP6(*daddr));
+ LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]\n",
+ saddr, daddr);
goto discard_it;
}
}
@@ -955,8 +956,8 @@ ctl_table ipv6_icmp_table_template[] = {
.data = &init_net.ipv6.sysctl.icmpv6_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies
},
{ .ctl_name = 0 },
};
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 16d43f20b32f..3c3732d50c1a 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -219,7 +219,7 @@ int inet6_csk_xmit(struct sk_buff *skb, int ipfragok)
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) {
sk->sk_route_caps = 0;
kfree_skb(skb);
return err;
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 1646a5658255..8fe267feb81e 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -25,26 +25,30 @@
void __inet6_hash(struct sock *sk)
{
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
- struct hlist_head *list;
- rwlock_t *lock;
WARN_ON(!sk_unhashed(sk));
if (sk->sk_state == TCP_LISTEN) {
- list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
- lock = &hashinfo->lhash_lock;
- inet_listen_wlock(hashinfo);
+ struct inet_listen_hashbucket *ilb;
+
+ ilb = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
+ spin_lock(&ilb->lock);
+ __sk_nulls_add_node_rcu(sk, &ilb->head);
+ spin_unlock(&ilb->lock);
} else {
unsigned int hash;
+ struct hlist_nulls_head *list;
+ spinlock_t *lock;
+
sk->sk_hash = hash = inet6_sk_ehashfn(sk);
list = &inet_ehash_bucket(hashinfo, hash)->chain;
lock = inet_ehash_lockp(hashinfo, hash);
- write_lock(lock);
+ spin_lock(lock);
+ __sk_nulls_add_node_rcu(sk, list);
+ spin_unlock(lock);
}
- __sk_add_node(sk, list);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
- write_unlock(lock);
}
EXPORT_SYMBOL(__inet6_hash);
@@ -63,77 +67,122 @@ struct sock *__inet6_lookup_established(struct net *net,
const int dif)
{
struct sock *sk;
- const struct hlist_node *node;
+ const struct hlist_nulls_node *node;
const __portpair ports = INET_COMBINED_PORTS(sport, hnum);
/* Optimize here for direct hit, only listening connections can
* have wildcards anyways.
*/
unsigned int hash = inet6_ehashfn(net, daddr, hnum, saddr, sport);
- struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash);
- rwlock_t *lock = inet_ehash_lockp(hashinfo, hash);
+ unsigned int slot = hash & (hashinfo->ehash_size - 1);
+ struct inet_ehash_bucket *head = &hashinfo->ehash[slot];
- prefetch(head->chain.first);
- read_lock(lock);
- sk_for_each(sk, node, &head->chain) {
+
+ rcu_read_lock();
+begin:
+ sk_nulls_for_each_rcu(sk, node, &head->chain) {
/* For IPV6 do the cheaper port and family tests first. */
- if (INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif))
- goto hit; /* You sunk my battleship! */
+ if (INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
+ goto begintw;
+ if (!INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ sock_put(sk);
+ goto begin;
+ }
+ goto out;
+ }
}
+ if (get_nulls_value(node) != slot)
+ goto begin;
+
+begintw:
/* Must check for a TIME_WAIT'er before going to listener hash. */
- sk_for_each(sk, node, &head->twchain) {
- if (INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif))
- goto hit;
+ sk_nulls_for_each_rcu(sk, node, &head->twchain) {
+ if (INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
+ sk = NULL;
+ goto out;
+ }
+ if (!INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ sock_put(sk);
+ goto begintw;
+ }
+ goto out;
+ }
}
- read_unlock(lock);
- return NULL;
-
-hit:
- sock_hold(sk);
- read_unlock(lock);
+ if (get_nulls_value(node) != slot)
+ goto begintw;
+ sk = NULL;
+out:
+ rcu_read_unlock();
return sk;
}
EXPORT_SYMBOL(__inet6_lookup_established);
+static int inline compute_score(struct sock *sk, struct net *net,
+ const unsigned short hnum,
+ const struct in6_addr *daddr,
+ const int dif)
+{
+ int score = -1;
+
+ if (net_eq(sock_net(sk), net) && inet_sk(sk)->num == hnum &&
+ sk->sk_family == PF_INET6) {
+ const struct ipv6_pinfo *np = inet6_sk(sk);
+
+ score = 1;
+ if (!ipv6_addr_any(&np->rcv_saddr)) {
+ if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+ return -1;
+ score++;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score++;
+ }
+ }
+ return score;
+}
+
struct sock *inet6_lookup_listener(struct net *net,
struct inet_hashinfo *hashinfo, const struct in6_addr *daddr,
const unsigned short hnum, const int dif)
{
struct sock *sk;
- const struct hlist_node *node;
- struct sock *result = NULL;
- int score, hiscore = 0;
-
- read_lock(&hashinfo->lhash_lock);
- sk_for_each(sk, node,
- &hashinfo->listening_hash[inet_lhashfn(net, hnum)]) {
- if (net_eq(sock_net(sk), net) && inet_sk(sk)->num == hnum &&
- sk->sk_family == PF_INET6) {
- const struct ipv6_pinfo *np = inet6_sk(sk);
-
- score = 1;
- if (!ipv6_addr_any(&np->rcv_saddr)) {
- if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
- continue;
- score++;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- continue;
- score++;
- }
- if (score == 3) {
- result = sk;
- break;
- }
- if (score > hiscore) {
- hiscore = score;
- result = sk;
- }
+ const struct hlist_nulls_node *node;
+ struct sock *result;
+ int score, hiscore;
+ unsigned int hash = inet_lhashfn(net, hnum);
+ struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];
+
+ rcu_read_lock();
+begin:
+ result = NULL;
+ hiscore = -1;
+ sk_nulls_for_each(sk, node, &ilb->head) {
+ score = compute_score(sk, net, hnum, daddr, dif);
+ if (score > hiscore) {
+ hiscore = score;
+ result = sk;
+ }
+ }
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != hash + LISTENING_NULLS_BASE)
+ goto begin;
+ if (result) {
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score(result, net, hnum, daddr,
+ dif) < hiscore)) {
+ sock_put(result);
+ goto begin;
}
}
- if (result)
- sock_hold(result);
- read_unlock(&hashinfo->lhash_lock);
+ rcu_read_unlock();
return result;
}
@@ -170,16 +219,15 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
const unsigned int hash = inet6_ehashfn(net, daddr, lport, saddr,
inet->dport);
struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash);
- rwlock_t *lock = inet_ehash_lockp(hinfo, hash);
+ spinlock_t *lock = inet_ehash_lockp(hinfo, hash);
struct sock *sk2;
- const struct hlist_node *node;
+ const struct hlist_nulls_node *node;
struct inet_timewait_sock *tw;
- prefetch(head->chain.first);
- write_lock(lock);
+ spin_lock(lock);
/* Check TIME-WAIT sockets first. */
- sk_for_each(sk2, node, &head->twchain) {
+ sk_nulls_for_each(sk2, node, &head->twchain) {
tw = inet_twsk(sk2);
if (INET6_TW_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) {
@@ -192,7 +240,7 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
tw = NULL;
/* And established part... */
- sk_for_each(sk2, node, &head->chain) {
+ sk_nulls_for_each(sk2, node, &head->chain) {
if (INET6_MATCH(sk2, net, hash, saddr, daddr, ports, dif))
goto not_unique;
}
@@ -203,10 +251,10 @@ unique:
inet->num = lport;
inet->sport = htons(lport);
WARN_ON(!sk_unhashed(sk));
- __sk_add_node(sk, &head->chain);
+ __sk_nulls_add_node_rcu(sk, &head->chain);
sk->sk_hash = hash;
+ spin_unlock(lock);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
- write_unlock(lock);
if (twp != NULL) {
*twp = tw;
@@ -221,7 +269,7 @@ unique:
return 0;
not_unique:
- write_unlock(lock);
+ spin_unlock(lock);
return -EADDRNOTAVAIL;
}
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 37a4e777e347..c62dd247774f 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -388,7 +388,7 @@ fl_create(struct net *net, struct in6_flowlabel_req *freq, char __user *optval,
fl->owner = current->pid;
break;
case IPV6_FL_S_USER:
- fl->owner = current->euid;
+ fl->owner = current_euid();
break;
default:
err = -EINVAL;
@@ -464,7 +464,7 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl,
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
{
- int err;
+ int uninitialized_var(err);
struct net *net = sock_net(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_flowlabel_req freq;
@@ -696,14 +696,14 @@ static int ip6fl_seq_show(struct seq_file *seq, void *v)
else {
struct ip6_flowlabel *fl = v;
seq_printf(seq,
- "%05X %-1d %-6d %-6d %-6ld %-8ld " NIP6_SEQFMT " %-4d\n",
+ "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
(unsigned)ntohl(fl->label),
fl->share,
(unsigned)fl->owner,
atomic_read(&fl->users),
fl->linger/HZ,
(long)(fl->expires - jiffies)/HZ,
- NIP6(fl->dst),
+ &fl->dst,
fl->opt ? fl->opt->opt_nflen : 0);
}
return 0;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index c77db0b95e26..4b15938bef4d 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -137,7 +137,8 @@ static int ip6_output2(struct sk_buff *skb)
struct inet6_dev *idev = ip6_dst_idev(skb->dst);
if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
- ((mroute6_socket && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
+ ((mroute6_socket(dev_net(dev)) &&
+ !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
&ipv6_hdr(skb)->saddr))) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
@@ -490,7 +491,7 @@ int ip6_forward(struct sk_buff *skb)
We don't send redirects to frames decapsulated from IPsec.
*/
if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0 &&
- !skb->sp) {
+ !skb_sec_path(skb)) {
struct in6_addr *target = NULL;
struct rt6_info *rt;
struct neighbour *n = dst->neighbour;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 64ce3d33d9c6..58e2b0d93758 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -74,8 +74,8 @@ MODULE_LICENSE("GPL");
(addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \
(HASH_SIZE - 1))
-static int ip6_fb_tnl_dev_init(struct net_device *dev);
-static int ip6_tnl_dev_init(struct net_device *dev);
+static void ip6_fb_tnl_dev_init(struct net_device *dev);
+static void ip6_tnl_dev_init(struct net_device *dev);
static void ip6_tnl_dev_setup(struct net_device *dev);
static int ip6_tnl_net_id;
@@ -249,7 +249,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p)
}
t = netdev_priv(dev);
- dev->init = ip6_tnl_dev_init;
+ ip6_tnl_dev_init(dev);
t->parms = *p;
if ((err = register_netdevice(dev)) < 0)
@@ -846,6 +846,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
int encap_limit,
__u32 *pmtu)
{
+ struct net *net = dev_net(dev);
struct ip6_tnl *t = netdev_priv(dev);
struct net_device_stats *stats = &t->dev->stats;
struct ipv6hdr *ipv6h = ipv6_hdr(skb);
@@ -861,9 +862,9 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
if ((dst = ip6_tnl_dst_check(t)) != NULL)
dst_hold(dst);
else {
- dst = ip6_route_output(dev_net(dev), NULL, fl);
+ dst = ip6_route_output(net, NULL, fl);
- if (dst->error || xfrm_lookup(&dst, fl, NULL, 0) < 0)
+ if (dst->error || xfrm_lookup(net, &dst, fl, NULL, 0) < 0)
goto tx_err_link_failure;
}
@@ -1150,7 +1151,6 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
* ip6_tnl_change - update the tunnel parameters
* @t: tunnel to be changed
* @p: tunnel configuration parameters
- * @active: != 0 if tunnel is ready for use
*
* Description:
* ip6_tnl_change() updates the tunnel parameters
@@ -1306,6 +1306,14 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+
+static const struct net_device_ops ip6_tnl_netdev_ops = {
+ .ndo_uninit = ip6_tnl_dev_uninit,
+ .ndo_start_xmit = ip6_tnl_xmit,
+ .ndo_do_ioctl = ip6_tnl_ioctl,
+ .ndo_change_mtu = ip6_tnl_change_mtu,
+};
+
/**
* ip6_tnl_dev_setup - setup virtual tunnel device
* @dev: virtual device associated with tunnel
@@ -1316,11 +1324,8 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
static void ip6_tnl_dev_setup(struct net_device *dev)
{
- dev->uninit = ip6_tnl_dev_uninit;
+ dev->netdev_ops = &ip6_tnl_netdev_ops;
dev->destructor = free_netdev;
- dev->hard_start_xmit = ip6_tnl_xmit;
- dev->do_ioctl = ip6_tnl_ioctl;
- dev->change_mtu = ip6_tnl_change_mtu;
dev->type = ARPHRD_TUNNEL6;
dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr);
@@ -1349,13 +1354,11 @@ ip6_tnl_dev_init_gen(struct net_device *dev)
* @dev: virtual device associated with tunnel
**/
-static int
-ip6_tnl_dev_init(struct net_device *dev)
+static void ip6_tnl_dev_init(struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
ip6_tnl_dev_init_gen(dev);
ip6_tnl_link_config(t);
- return 0;
}
/**
@@ -1365,8 +1368,7 @@ ip6_tnl_dev_init(struct net_device *dev)
* Return: 0
**/
-static int
-ip6_fb_tnl_dev_init(struct net_device *dev)
+static void ip6_fb_tnl_dev_init(struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
@@ -1376,7 +1378,6 @@ ip6_fb_tnl_dev_init(struct net_device *dev)
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
ip6n->tnls_wc[0] = t;
- return 0;
}
static struct xfrm6_tunnel ip4ip6_handler = {
@@ -1428,10 +1429,10 @@ static int ip6_tnl_init_net(struct net *net)
if (!ip6n->fb_tnl_dev)
goto err_alloc_dev;
-
- ip6n->fb_tnl_dev->init = ip6_fb_tnl_dev_init;
dev_net_set(ip6n->fb_tnl_dev, net);
+ ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
+
err = register_netdev(ip6n->fb_tnl_dev);
if (err < 0)
goto err_register;
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 0524769632e7..3c51b2d827f4 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -49,9 +49,6 @@
#include <net/addrconf.h>
#include <linux/netfilter_ipv6.h>
-struct sock *mroute6_socket;
-
-
/* Big lock, protecting vif table, mrt cache and mroute socket state.
Note that the changes are semaphored via rtnl_lock.
*/
@@ -62,22 +59,9 @@ static DEFINE_RWLOCK(mrt_lock);
* Multicast router control variables
*/
-static struct mif_device vif6_table[MAXMIFS]; /* Devices */
-static int maxvif;
-
-#define MIF_EXISTS(idx) (vif6_table[idx].dev != NULL)
-
-static int mroute_do_assert; /* Set in PIM assert */
-#ifdef CONFIG_IPV6_PIMSM_V2
-static int mroute_do_pim;
-#else
-#define mroute_do_pim 0
-#endif
-
-static struct mfc6_cache *mfc6_cache_array[MFC6_LINES]; /* Forwarding cache */
+#define MIF_EXISTS(_net, _idx) ((_net)->ipv6.vif6_table[_idx].dev != NULL)
static struct mfc6_cache *mfc_unres_queue; /* Queue of unresolved entries */
-static atomic_t cache_resolve_queue_len; /* Size of unresolved */
/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);
@@ -93,8 +77,10 @@ static DEFINE_SPINLOCK(mfc_unres_lock);
static struct kmem_cache *mrt_cachep __read_mostly;
static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache);
-static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert);
+static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt,
+ mifi_t mifi, int assert);
static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm);
+static void mroute_clean_tables(struct net *net);
#ifdef CONFIG_IPV6_PIMSM_V2
static struct inet6_protocol pim6_protocol;
@@ -106,19 +92,22 @@ static struct timer_list ipmr_expire_timer;
#ifdef CONFIG_PROC_FS
struct ipmr_mfc_iter {
+ struct seq_net_private p;
struct mfc6_cache **cache;
int ct;
};
-static struct mfc6_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos)
+static struct mfc6_cache *ipmr_mfc_seq_idx(struct net *net,
+ struct ipmr_mfc_iter *it, loff_t pos)
{
struct mfc6_cache *mfc;
- it->cache = mfc6_cache_array;
+ it->cache = net->ipv6.mfc6_cache_array;
read_lock(&mrt_lock);
- for (it->ct = 0; it->ct < ARRAY_SIZE(mfc6_cache_array); it->ct++)
- for (mfc = mfc6_cache_array[it->ct]; mfc; mfc = mfc->next)
+ for (it->ct = 0; it->ct < MFC6_LINES; it->ct++)
+ for (mfc = net->ipv6.mfc6_cache_array[it->ct];
+ mfc; mfc = mfc->next)
if (pos-- == 0)
return mfc;
read_unlock(&mrt_lock);
@@ -126,7 +115,8 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos)
it->cache = &mfc_unres_queue;
spin_lock_bh(&mfc_unres_lock);
for (mfc = mfc_unres_queue; mfc; mfc = mfc->next)
- if (pos-- == 0)
+ if (net_eq(mfc6_net(mfc), net) &&
+ pos-- == 0)
return mfc;
spin_unlock_bh(&mfc_unres_lock);
@@ -142,17 +132,19 @@ static struct mfc6_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos)
*/
struct ipmr_vif_iter {
+ struct seq_net_private p;
int ct;
};
-static struct mif_device *ip6mr_vif_seq_idx(struct ipmr_vif_iter *iter,
+static struct mif_device *ip6mr_vif_seq_idx(struct net *net,
+ struct ipmr_vif_iter *iter,
loff_t pos)
{
- for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) {
- if (!MIF_EXISTS(iter->ct))
+ for (iter->ct = 0; iter->ct < net->ipv6.maxvif; ++iter->ct) {
+ if (!MIF_EXISTS(net, iter->ct))
continue;
if (pos-- == 0)
- return &vif6_table[iter->ct];
+ return &net->ipv6.vif6_table[iter->ct];
}
return NULL;
}
@@ -160,23 +152,26 @@ static struct mif_device *ip6mr_vif_seq_idx(struct ipmr_vif_iter *iter,
static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(mrt_lock)
{
+ struct net *net = seq_file_net(seq);
+
read_lock(&mrt_lock);
- return (*pos ? ip6mr_vif_seq_idx(seq->private, *pos - 1)
- : SEQ_START_TOKEN);
+ return *pos ? ip6mr_vif_seq_idx(net, seq->private, *pos - 1)
+ : SEQ_START_TOKEN;
}
static void *ip6mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct ipmr_vif_iter *iter = seq->private;
+ struct net *net = seq_file_net(seq);
++*pos;
if (v == SEQ_START_TOKEN)
- return ip6mr_vif_seq_idx(iter, 0);
+ return ip6mr_vif_seq_idx(net, iter, 0);
- while (++iter->ct < maxvif) {
- if (!MIF_EXISTS(iter->ct))
+ while (++iter->ct < net->ipv6.maxvif) {
+ if (!MIF_EXISTS(net, iter->ct))
continue;
- return &vif6_table[iter->ct];
+ return &net->ipv6.vif6_table[iter->ct];
}
return NULL;
}
@@ -189,6 +184,8 @@ static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
{
+ struct net *net = seq_file_net(seq);
+
if (v == SEQ_START_TOKEN) {
seq_puts(seq,
"Interface BytesIn PktsIn BytesOut PktsOut Flags\n");
@@ -198,7 +195,7 @@ static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
seq_printf(seq,
"%2td %-10s %8ld %7ld %8ld %7ld %05X\n",
- vif - vif6_table,
+ vif - net->ipv6.vif6_table,
name, vif->bytes_in, vif->pkt_in,
vif->bytes_out, vif->pkt_out,
vif->flags);
@@ -215,8 +212,8 @@ static struct seq_operations ip6mr_vif_seq_ops = {
static int ip6mr_vif_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &ip6mr_vif_seq_ops,
- sizeof(struct ipmr_vif_iter));
+ return seq_open_net(inode, file, &ip6mr_vif_seq_ops,
+ sizeof(struct ipmr_vif_iter));
}
static struct file_operations ip6mr_vif_fops = {
@@ -224,24 +221,27 @@ static struct file_operations ip6mr_vif_fops = {
.open = ip6mr_vif_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
{
- return (*pos ? ipmr_mfc_seq_idx(seq->private, *pos - 1)
- : SEQ_START_TOKEN);
+ struct net *net = seq_file_net(seq);
+
+ return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
+ : SEQ_START_TOKEN;
}
static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct mfc6_cache *mfc = v;
struct ipmr_mfc_iter *it = seq->private;
+ struct net *net = seq_file_net(seq);
++*pos;
if (v == SEQ_START_TOKEN)
- return ipmr_mfc_seq_idx(seq->private, 0);
+ return ipmr_mfc_seq_idx(net, seq->private, 0);
if (mfc->next)
return mfc->next;
@@ -249,10 +249,10 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
if (it->cache == &mfc_unres_queue)
goto end_of_list;
- BUG_ON(it->cache != mfc6_cache_array);
+ BUG_ON(it->cache != net->ipv6.mfc6_cache_array);
- while (++it->ct < ARRAY_SIZE(mfc6_cache_array)) {
- mfc = mfc6_cache_array[it->ct];
+ while (++it->ct < MFC6_LINES) {
+ mfc = net->ipv6.mfc6_cache_array[it->ct];
if (mfc)
return mfc;
}
@@ -277,16 +277,18 @@ static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
{
struct ipmr_mfc_iter *it = seq->private;
+ struct net *net = seq_file_net(seq);
if (it->cache == &mfc_unres_queue)
spin_unlock_bh(&mfc_unres_lock);
- else if (it->cache == mfc6_cache_array)
+ else if (it->cache == net->ipv6.mfc6_cache_array)
read_unlock(&mrt_lock);
}
static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
{
int n;
+ struct net *net = seq_file_net(seq);
if (v == SEQ_START_TOKEN) {
seq_puts(seq,
@@ -297,23 +299,28 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
const struct mfc6_cache *mfc = v;
const struct ipmr_mfc_iter *it = seq->private;
- seq_printf(seq,
- NIP6_FMT " " NIP6_FMT " %-3d %8ld %8ld %8ld",
- NIP6(mfc->mf6c_mcastgrp), NIP6(mfc->mf6c_origin),
- mfc->mf6c_parent,
- mfc->mfc_un.res.pkt,
- mfc->mfc_un.res.bytes,
- mfc->mfc_un.res.wrong_if);
+ seq_printf(seq, "%pI6 %pI6 %-3hd",
+ &mfc->mf6c_mcastgrp, &mfc->mf6c_origin,
+ mfc->mf6c_parent);
if (it->cache != &mfc_unres_queue) {
+ seq_printf(seq, " %8lu %8lu %8lu",
+ mfc->mfc_un.res.pkt,
+ mfc->mfc_un.res.bytes,
+ mfc->mfc_un.res.wrong_if);
for (n = mfc->mfc_un.res.minvif;
n < mfc->mfc_un.res.maxvif; n++) {
- if (MIF_EXISTS(n) &&
+ if (MIF_EXISTS(net, n) &&
mfc->mfc_un.res.ttls[n] < 255)
seq_printf(seq,
" %2d:%-3d",
n, mfc->mfc_un.res.ttls[n]);
}
+ } else {
+ /* unresolved mfc_caches don't contain
+ * pkt, bytes and wrong_if values
+ */
+ seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
}
seq_putc(seq, '\n');
}
@@ -329,8 +336,8 @@ static struct seq_operations ipmr_mfc_seq_ops = {
static int ipmr_mfc_open(struct inode *inode, struct file *file)
{
- return seq_open_private(file, &ipmr_mfc_seq_ops,
- sizeof(struct ipmr_mfc_iter));
+ return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
+ sizeof(struct ipmr_mfc_iter));
}
static struct file_operations ip6mr_mfc_fops = {
@@ -338,18 +345,19 @@ static struct file_operations ip6mr_mfc_fops = {
.open = ipmr_mfc_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
#endif
#ifdef CONFIG_IPV6_PIMSM_V2
-static int reg_vif_num = -1;
static int pim6_rcv(struct sk_buff *skb)
{
struct pimreghdr *pim;
struct ipv6hdr *encap;
struct net_device *reg_dev = NULL;
+ struct net *net = dev_net(skb->dev);
+ int reg_vif_num = net->ipv6.mroute_reg_vif_num;
if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
goto drop;
@@ -372,7 +380,7 @@ static int pim6_rcv(struct sk_buff *skb)
read_lock(&mrt_lock);
if (reg_vif_num >= 0)
- reg_dev = vif6_table[reg_vif_num].dev;
+ reg_dev = net->ipv6.vif6_table[reg_vif_num].dev;
if (reg_dev)
dev_hold(reg_dev);
read_unlock(&mrt_lock);
@@ -408,25 +416,32 @@ static struct inet6_protocol pim6_protocol = {
static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ struct net *net = dev_net(dev);
+
read_lock(&mrt_lock);
dev->stats.tx_bytes += skb->len;
dev->stats.tx_packets++;
- ip6mr_cache_report(skb, reg_vif_num, MRT6MSG_WHOLEPKT);
+ ip6mr_cache_report(net, skb, net->ipv6.mroute_reg_vif_num,
+ MRT6MSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
return 0;
}
+static const struct net_device_ops reg_vif_netdev_ops = {
+ .ndo_start_xmit = reg_vif_xmit,
+};
+
static void reg_vif_setup(struct net_device *dev)
{
dev->type = ARPHRD_PIMREG;
dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8;
dev->flags = IFF_NOARP;
- dev->hard_start_xmit = reg_vif_xmit;
+ dev->netdev_ops = &reg_vif_netdev_ops;
dev->destructor = free_netdev;
}
-static struct net_device *ip6mr_reg_vif(void)
+static struct net_device *ip6mr_reg_vif(struct net *net)
{
struct net_device *dev;
@@ -434,6 +449,8 @@ static struct net_device *ip6mr_reg_vif(void)
if (dev == NULL)
return NULL;
+ dev_net_set(dev, net);
+
if (register_netdevice(dev)) {
free_netdev(dev);
return NULL;
@@ -460,14 +477,14 @@ failure:
* Delete a VIF entry
*/
-static int mif6_delete(int vifi)
+static int mif6_delete(struct net *net, int vifi)
{
struct mif_device *v;
struct net_device *dev;
- if (vifi < 0 || vifi >= maxvif)
+ if (vifi < 0 || vifi >= net->ipv6.maxvif)
return -EADDRNOTAVAIL;
- v = &vif6_table[vifi];
+ v = &net->ipv6.vif6_table[vifi];
write_lock_bh(&mrt_lock);
dev = v->dev;
@@ -479,17 +496,17 @@ static int mif6_delete(int vifi)
}
#ifdef CONFIG_IPV6_PIMSM_V2
- if (vifi == reg_vif_num)
- reg_vif_num = -1;
+ if (vifi == net->ipv6.mroute_reg_vif_num)
+ net->ipv6.mroute_reg_vif_num = -1;
#endif
- if (vifi + 1 == maxvif) {
+ if (vifi + 1 == net->ipv6.maxvif) {
int tmp;
for (tmp = vifi - 1; tmp >= 0; tmp--) {
- if (MIF_EXISTS(tmp))
+ if (MIF_EXISTS(net, tmp))
break;
}
- maxvif = tmp + 1;
+ net->ipv6.maxvif = tmp + 1;
}
write_unlock_bh(&mrt_lock);
@@ -503,6 +520,12 @@ static int mif6_delete(int vifi)
return 0;
}
+static inline void ip6mr_cache_free(struct mfc6_cache *c)
+{
+ release_net(mfc6_net(c));
+ kmem_cache_free(mrt_cachep, c);
+}
+
/* Destroy an unresolved cache entry, killing queued skbs
and reporting error to netlink readers.
*/
@@ -510,8 +533,9 @@ static int mif6_delete(int vifi)
static void ip6mr_destroy_unres(struct mfc6_cache *c)
{
struct sk_buff *skb;
+ struct net *net = mfc6_net(c);
- atomic_dec(&cache_resolve_queue_len);
+ atomic_dec(&net->ipv6.cache_resolve_queue_len);
while((skb = skb_dequeue(&c->mfc_un.unres.unresolved)) != NULL) {
if (ipv6_hdr(skb)->version == 0) {
@@ -520,12 +544,12 @@ static void ip6mr_destroy_unres(struct mfc6_cache *c)
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
skb_trim(skb, nlh->nlmsg_len);
((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -ETIMEDOUT;
- rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
+ rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
} else
kfree_skb(skb);
}
- kmem_cache_free(mrt_cachep, c);
+ ip6mr_cache_free(c);
}
@@ -553,7 +577,7 @@ static void ipmr_do_expire_process(unsigned long dummy)
ip6mr_destroy_unres(c);
}
- if (atomic_read(&cache_resolve_queue_len))
+ if (mfc_unres_queue != NULL)
mod_timer(&ipmr_expire_timer, jiffies + expires);
}
@@ -564,7 +588,7 @@ static void ipmr_expire_process(unsigned long dummy)
return;
}
- if (atomic_read(&cache_resolve_queue_len))
+ if (mfc_unres_queue != NULL)
ipmr_do_expire_process(dummy);
spin_unlock(&mfc_unres_lock);
@@ -575,13 +599,15 @@ static void ipmr_expire_process(unsigned long dummy)
static void ip6mr_update_thresholds(struct mfc6_cache *cache, unsigned char *ttls)
{
int vifi;
+ struct net *net = mfc6_net(cache);
cache->mfc_un.res.minvif = MAXMIFS;
cache->mfc_un.res.maxvif = 0;
memset(cache->mfc_un.res.ttls, 255, MAXMIFS);
- for (vifi = 0; vifi < maxvif; vifi++) {
- if (MIF_EXISTS(vifi) && ttls[vifi] && ttls[vifi] < 255) {
+ for (vifi = 0; vifi < net->ipv6.maxvif; vifi++) {
+ if (MIF_EXISTS(net, vifi) &&
+ ttls[vifi] && ttls[vifi] < 255) {
cache->mfc_un.res.ttls[vifi] = ttls[vifi];
if (cache->mfc_un.res.minvif > vifi)
cache->mfc_un.res.minvif = vifi;
@@ -591,15 +617,15 @@ static void ip6mr_update_thresholds(struct mfc6_cache *cache, unsigned char *ttl
}
}
-static int mif6_add(struct mif6ctl *vifc, int mrtsock)
+static int mif6_add(struct net *net, struct mif6ctl *vifc, int mrtsock)
{
int vifi = vifc->mif6c_mifi;
- struct mif_device *v = &vif6_table[vifi];
+ struct mif_device *v = &net->ipv6.vif6_table[vifi];
struct net_device *dev;
int err;
/* Is vif busy ? */
- if (MIF_EXISTS(vifi))
+ if (MIF_EXISTS(net, vifi))
return -EADDRINUSE;
switch (vifc->mif6c_flags) {
@@ -609,9 +635,9 @@ static int mif6_add(struct mif6ctl *vifc, int mrtsock)
* Special Purpose VIF in PIM
* All the packets will be sent to the daemon
*/
- if (reg_vif_num >= 0)
+ if (net->ipv6.mroute_reg_vif_num >= 0)
return -EADDRINUSE;
- dev = ip6mr_reg_vif();
+ dev = ip6mr_reg_vif(net);
if (!dev)
return -ENOBUFS;
err = dev_set_allmulti(dev, 1);
@@ -623,7 +649,7 @@ static int mif6_add(struct mif6ctl *vifc, int mrtsock)
break;
#endif
case 0:
- dev = dev_get_by_index(&init_net, vifc->mif6c_pifi);
+ dev = dev_get_by_index(net, vifc->mif6c_pifi);
if (!dev)
return -EADDRNOTAVAIL;
err = dev_set_allmulti(dev, 1);
@@ -657,20 +683,22 @@ static int mif6_add(struct mif6ctl *vifc, int mrtsock)
v->dev = dev;
#ifdef CONFIG_IPV6_PIMSM_V2
if (v->flags & MIFF_REGISTER)
- reg_vif_num = vifi;
+ net->ipv6.mroute_reg_vif_num = vifi;
#endif
- if (vifi + 1 > maxvif)
- maxvif = vifi + 1;
+ if (vifi + 1 > net->ipv6.maxvif)
+ net->ipv6.maxvif = vifi + 1;
write_unlock_bh(&mrt_lock);
return 0;
}
-static struct mfc6_cache *ip6mr_cache_find(struct in6_addr *origin, struct in6_addr *mcastgrp)
+static struct mfc6_cache *ip6mr_cache_find(struct net *net,
+ struct in6_addr *origin,
+ struct in6_addr *mcastgrp)
{
int line = MFC6_HASH(mcastgrp, origin);
struct mfc6_cache *c;
- for (c = mfc6_cache_array[line]; c; c = c->next) {
+ for (c = net->ipv6.mfc6_cache_array[line]; c; c = c->next) {
if (ipv6_addr_equal(&c->mf6c_origin, origin) &&
ipv6_addr_equal(&c->mf6c_mcastgrp, mcastgrp))
break;
@@ -681,24 +709,24 @@ static struct mfc6_cache *ip6mr_cache_find(struct in6_addr *origin, struct in6_a
/*
* Allocate a multicast cache entry
*/
-static struct mfc6_cache *ip6mr_cache_alloc(void)
+static struct mfc6_cache *ip6mr_cache_alloc(struct net *net)
{
- struct mfc6_cache *c = kmem_cache_alloc(mrt_cachep, GFP_KERNEL);
+ struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
if (c == NULL)
return NULL;
- memset(c, 0, sizeof(*c));
c->mfc_un.res.minvif = MAXMIFS;
+ mfc6_net_set(c, net);
return c;
}
-static struct mfc6_cache *ip6mr_cache_alloc_unres(void)
+static struct mfc6_cache *ip6mr_cache_alloc_unres(struct net *net)
{
- struct mfc6_cache *c = kmem_cache_alloc(mrt_cachep, GFP_ATOMIC);
+ struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
if (c == NULL)
return NULL;
- memset(c, 0, sizeof(*c));
skb_queue_head_init(&c->mfc_un.unres.unresolved);
c->mfc_un.unres.expires = jiffies + 10 * HZ;
+ mfc6_net_set(c, net);
return c;
}
@@ -727,7 +755,7 @@ static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c)
skb_trim(skb, nlh->nlmsg_len);
((struct nlmsgerr *)NLMSG_DATA(nlh))->error = -EMSGSIZE;
}
- err = rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
+ err = rtnl_unicast(skb, mfc6_net(uc), NETLINK_CB(skb).pid);
} else
ip6_mr_forward(skb, c);
}
@@ -740,7 +768,8 @@ static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c)
* Called under mrt_lock.
*/
-static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert)
+static int ip6mr_cache_report(struct net *net, struct sk_buff *pkt, mifi_t mifi,
+ int assert)
{
struct sk_buff *skb;
struct mrt6msg *msg;
@@ -776,7 +805,7 @@ static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert)
msg = (struct mrt6msg *)skb_transport_header(skb);
msg->im6_mbz = 0;
msg->im6_msgtype = MRT6MSG_WHOLEPKT;
- msg->im6_mif = reg_vif_num;
+ msg->im6_mif = net->ipv6.mroute_reg_vif_num;
msg->im6_pad = 0;
ipv6_addr_copy(&msg->im6_src, &ipv6_hdr(pkt)->saddr);
ipv6_addr_copy(&msg->im6_dst, &ipv6_hdr(pkt)->daddr);
@@ -813,7 +842,7 @@ static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert)
skb_pull(skb, sizeof(struct ipv6hdr));
}
- if (mroute6_socket == NULL) {
+ if (net->ipv6.mroute6_sk == NULL) {
kfree_skb(skb);
return -EINVAL;
}
@@ -821,7 +850,8 @@ static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert)
/*
* Deliver to user space multicast routing algorithms
*/
- if ((ret = sock_queue_rcv_skb(mroute6_socket, skb)) < 0) {
+ ret = sock_queue_rcv_skb(net->ipv6.mroute6_sk, skb);
+ if (ret < 0) {
if (net_ratelimit())
printk(KERN_WARNING "mroute6: pending queue full, dropping entries.\n");
kfree_skb(skb);
@@ -835,14 +865,15 @@ static int ip6mr_cache_report(struct sk_buff *pkt, mifi_t mifi, int assert)
*/
static int
-ip6mr_cache_unresolved(mifi_t mifi, struct sk_buff *skb)
+ip6mr_cache_unresolved(struct net *net, mifi_t mifi, struct sk_buff *skb)
{
int err;
struct mfc6_cache *c;
spin_lock_bh(&mfc_unres_lock);
for (c = mfc_unres_queue; c; c = c->next) {
- if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
+ if (net_eq(mfc6_net(c), net) &&
+ ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr))
break;
}
@@ -852,8 +883,8 @@ ip6mr_cache_unresolved(mifi_t mifi, struct sk_buff *skb)
* Create a new entry if allowable
*/
- if (atomic_read(&cache_resolve_queue_len) >= 10 ||
- (c = ip6mr_cache_alloc_unres()) == NULL) {
+ if (atomic_read(&net->ipv6.cache_resolve_queue_len) >= 10 ||
+ (c = ip6mr_cache_alloc_unres(net)) == NULL) {
spin_unlock_bh(&mfc_unres_lock);
kfree_skb(skb);
@@ -870,18 +901,19 @@ ip6mr_cache_unresolved(mifi_t mifi, struct sk_buff *skb)
/*
* Reflect first query at pim6sd
*/
- if ((err = ip6mr_cache_report(skb, mifi, MRT6MSG_NOCACHE)) < 0) {
+ err = ip6mr_cache_report(net, skb, mifi, MRT6MSG_NOCACHE);
+ if (err < 0) {
/* If the report failed throw the cache entry
out - Brad Parker
*/
spin_unlock_bh(&mfc_unres_lock);
- kmem_cache_free(mrt_cachep, c);
+ ip6mr_cache_free(c);
kfree_skb(skb);
return err;
}
- atomic_inc(&cache_resolve_queue_len);
+ atomic_inc(&net->ipv6.cache_resolve_queue_len);
c->next = mfc_unres_queue;
mfc_unres_queue = c;
@@ -907,21 +939,22 @@ ip6mr_cache_unresolved(mifi_t mifi, struct sk_buff *skb)
* MFC6 cache manipulation by user space
*/
-static int ip6mr_mfc_delete(struct mf6cctl *mfc)
+static int ip6mr_mfc_delete(struct net *net, struct mf6cctl *mfc)
{
int line;
struct mfc6_cache *c, **cp;
line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr);
- for (cp = &mfc6_cache_array[line]; (c = *cp) != NULL; cp = &c->next) {
+ for (cp = &net->ipv6.mfc6_cache_array[line];
+ (c = *cp) != NULL; cp = &c->next) {
if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) &&
ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) {
write_lock_bh(&mrt_lock);
*cp = c->next;
write_unlock_bh(&mrt_lock);
- kmem_cache_free(mrt_cachep, c);
+ ip6mr_cache_free(c);
return 0;
}
}
@@ -932,19 +965,17 @@ static int ip6mr_device_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
+ struct net *net = dev_net(dev);
struct mif_device *v;
int ct;
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
- v = &vif6_table[0];
- for (ct = 0; ct < maxvif; ct++, v++) {
+ v = &net->ipv6.vif6_table[0];
+ for (ct = 0; ct < net->ipv6.maxvif; ct++, v++) {
if (v->dev == dev)
- mif6_delete(ct);
+ mif6_delete(net, ct);
}
return NOTIFY_DONE;
}
@@ -957,6 +988,66 @@ static struct notifier_block ip6_mr_notifier = {
* Setup for IP multicast routing
*/
+static int __net_init ip6mr_net_init(struct net *net)
+{
+ int err = 0;
+ net->ipv6.vif6_table = kcalloc(MAXMIFS, sizeof(struct mif_device),
+ GFP_KERNEL);
+ if (!net->ipv6.vif6_table) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Forwarding cache */
+ net->ipv6.mfc6_cache_array = kcalloc(MFC6_LINES,
+ sizeof(struct mfc6_cache *),
+ GFP_KERNEL);
+ if (!net->ipv6.mfc6_cache_array) {
+ err = -ENOMEM;
+ goto fail_mfc6_cache;
+ }
+
+#ifdef CONFIG_IPV6_PIMSM_V2
+ net->ipv6.mroute_reg_vif_num = -1;
+#endif
+
+#ifdef CONFIG_PROC_FS
+ err = -ENOMEM;
+ if (!proc_net_fops_create(net, "ip6_mr_vif", 0, &ip6mr_vif_fops))
+ goto proc_vif_fail;
+ if (!proc_net_fops_create(net, "ip6_mr_cache", 0, &ip6mr_mfc_fops))
+ goto proc_cache_fail;
+#endif
+ return 0;
+
+#ifdef CONFIG_PROC_FS
+proc_cache_fail:
+ proc_net_remove(net, "ip6_mr_vif");
+proc_vif_fail:
+ kfree(net->ipv6.mfc6_cache_array);
+#endif
+fail_mfc6_cache:
+ kfree(net->ipv6.vif6_table);
+fail:
+ return err;
+}
+
+static void __net_exit ip6mr_net_exit(struct net *net)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_remove(net, "ip6_mr_cache");
+ proc_net_remove(net, "ip6_mr_vif");
+#endif
+ mroute_clean_tables(net);
+ kfree(net->ipv6.mfc6_cache_array);
+ kfree(net->ipv6.vif6_table);
+}
+
+static struct pernet_operations ip6mr_net_ops = {
+ .init = ip6mr_net_init,
+ .exit = ip6mr_net_exit,
+};
+
int __init ip6_mr_init(void)
{
int err;
@@ -968,43 +1059,32 @@ int __init ip6_mr_init(void)
if (!mrt_cachep)
return -ENOMEM;
+ err = register_pernet_subsys(&ip6mr_net_ops);
+ if (err)
+ goto reg_pernet_fail;
+
setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
err = register_netdevice_notifier(&ip6_mr_notifier);
if (err)
goto reg_notif_fail;
-#ifdef CONFIG_PROC_FS
- err = -ENOMEM;
- if (!proc_net_fops_create(&init_net, "ip6_mr_vif", 0, &ip6mr_vif_fops))
- goto proc_vif_fail;
- if (!proc_net_fops_create(&init_net, "ip6_mr_cache",
- 0, &ip6mr_mfc_fops))
- goto proc_cache_fail;
-#endif
return 0;
-#ifdef CONFIG_PROC_FS
-proc_cache_fail:
- proc_net_remove(&init_net, "ip6_mr_vif");
-proc_vif_fail:
- unregister_netdevice_notifier(&ip6_mr_notifier);
-#endif
reg_notif_fail:
del_timer(&ipmr_expire_timer);
+ unregister_pernet_subsys(&ip6mr_net_ops);
+reg_pernet_fail:
kmem_cache_destroy(mrt_cachep);
return err;
}
void ip6_mr_cleanup(void)
{
-#ifdef CONFIG_PROC_FS
- proc_net_remove(&init_net, "ip6_mr_cache");
- proc_net_remove(&init_net, "ip6_mr_vif");
-#endif
unregister_netdevice_notifier(&ip6_mr_notifier);
del_timer(&ipmr_expire_timer);
+ unregister_pernet_subsys(&ip6mr_net_ops);
kmem_cache_destroy(mrt_cachep);
}
-static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
+static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock)
{
int line;
struct mfc6_cache *uc, *c, **cp;
@@ -1020,7 +1100,8 @@ static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr);
- for (cp = &mfc6_cache_array[line]; (c = *cp) != NULL; cp = &c->next) {
+ for (cp = &net->ipv6.mfc6_cache_array[line];
+ (c = *cp) != NULL; cp = &c->next) {
if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) &&
ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr))
break;
@@ -1039,7 +1120,7 @@ static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
if (!ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr))
return -EINVAL;
- c = ip6mr_cache_alloc();
+ c = ip6mr_cache_alloc(net);
if (c == NULL)
return -ENOMEM;
@@ -1051,8 +1132,8 @@ static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
c->mfc_flags |= MFC_STATIC;
write_lock_bh(&mrt_lock);
- c->next = mfc6_cache_array[line];
- mfc6_cache_array[line] = c;
+ c->next = net->ipv6.mfc6_cache_array[line];
+ net->ipv6.mfc6_cache_array[line] = c;
write_unlock_bh(&mrt_lock);
/*
@@ -1062,19 +1143,21 @@ static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
spin_lock_bh(&mfc_unres_lock);
for (cp = &mfc_unres_queue; (uc = *cp) != NULL;
cp = &uc->next) {
- if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
+ if (net_eq(mfc6_net(uc), net) &&
+ ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) &&
ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) {
*cp = uc->next;
- if (atomic_dec_and_test(&cache_resolve_queue_len))
- del_timer(&ipmr_expire_timer);
+ atomic_dec(&net->ipv6.cache_resolve_queue_len);
break;
}
}
+ if (mfc_unres_queue == NULL)
+ del_timer(&ipmr_expire_timer);
spin_unlock_bh(&mfc_unres_lock);
if (uc) {
ip6mr_cache_resolve(uc, c);
- kmem_cache_free(mrt_cachep, uc);
+ ip6mr_cache_free(uc);
}
return 0;
}
@@ -1083,25 +1166,25 @@ static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
* Close the multicast socket, and clear the vif tables etc
*/
-static void mroute_clean_tables(struct sock *sk)
+static void mroute_clean_tables(struct net *net)
{
int i;
/*
* Shut down all active vif entries
*/
- for (i = 0; i < maxvif; i++) {
- if (!(vif6_table[i].flags & VIFF_STATIC))
- mif6_delete(i);
+ for (i = 0; i < net->ipv6.maxvif; i++) {
+ if (!(net->ipv6.vif6_table[i].flags & VIFF_STATIC))
+ mif6_delete(net, i);
}
/*
* Wipe the cache
*/
- for (i = 0; i < ARRAY_SIZE(mfc6_cache_array); i++) {
+ for (i = 0; i < MFC6_LINES; i++) {
struct mfc6_cache *c, **cp;
- cp = &mfc6_cache_array[i];
+ cp = &net->ipv6.mfc6_cache_array[i];
while ((c = *cp) != NULL) {
if (c->mfc_flags & MFC_STATIC) {
cp = &c->next;
@@ -1111,22 +1194,22 @@ static void mroute_clean_tables(struct sock *sk)
*cp = c->next;
write_unlock_bh(&mrt_lock);
- kmem_cache_free(mrt_cachep, c);
+ ip6mr_cache_free(c);
}
}
- if (atomic_read(&cache_resolve_queue_len) != 0) {
- struct mfc6_cache *c;
+ if (atomic_read(&net->ipv6.cache_resolve_queue_len) != 0) {
+ struct mfc6_cache *c, **cp;
spin_lock_bh(&mfc_unres_lock);
- while (mfc_unres_queue != NULL) {
- c = mfc_unres_queue;
- mfc_unres_queue = c->next;
- spin_unlock_bh(&mfc_unres_lock);
-
+ cp = &mfc_unres_queue;
+ while ((c = *cp) != NULL) {
+ if (!net_eq(mfc6_net(c), net)) {
+ cp = &c->next;
+ continue;
+ }
+ *cp = c->next;
ip6mr_destroy_unres(c);
-
- spin_lock_bh(&mfc_unres_lock);
}
spin_unlock_bh(&mfc_unres_lock);
}
@@ -1135,11 +1218,12 @@ static void mroute_clean_tables(struct sock *sk)
static int ip6mr_sk_init(struct sock *sk)
{
int err = 0;
+ struct net *net = sock_net(sk);
rtnl_lock();
write_lock_bh(&mrt_lock);
- if (likely(mroute6_socket == NULL))
- mroute6_socket = sk;
+ if (likely(net->ipv6.mroute6_sk == NULL))
+ net->ipv6.mroute6_sk = sk;
else
err = -EADDRINUSE;
write_unlock_bh(&mrt_lock);
@@ -1152,14 +1236,15 @@ static int ip6mr_sk_init(struct sock *sk)
int ip6mr_sk_done(struct sock *sk)
{
int err = 0;
+ struct net *net = sock_net(sk);
rtnl_lock();
- if (sk == mroute6_socket) {
+ if (sk == net->ipv6.mroute6_sk) {
write_lock_bh(&mrt_lock);
- mroute6_socket = NULL;
+ net->ipv6.mroute6_sk = NULL;
write_unlock_bh(&mrt_lock);
- mroute_clean_tables(sk);
+ mroute_clean_tables(net);
} else
err = -EACCES;
rtnl_unlock();
@@ -1180,9 +1265,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
struct mif6ctl vif;
struct mf6cctl mfc;
mifi_t mifi;
+ struct net *net = sock_net(sk);
if (optname != MRT6_INIT) {
- if (sk != mroute6_socket && !capable(CAP_NET_ADMIN))
+ if (sk != net->ipv6.mroute6_sk && !capable(CAP_NET_ADMIN))
return -EACCES;
}
@@ -1207,7 +1293,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
if (vif.mif6c_mifi >= MAXMIFS)
return -ENFILE;
rtnl_lock();
- ret = mif6_add(&vif, sk == mroute6_socket);
+ ret = mif6_add(net, &vif, sk == net->ipv6.mroute6_sk);
rtnl_unlock();
return ret;
@@ -1217,7 +1303,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
if (copy_from_user(&mifi, optval, sizeof(mifi_t)))
return -EFAULT;
rtnl_lock();
- ret = mif6_delete(mifi);
+ ret = mif6_delete(net, mifi);
rtnl_unlock();
return ret;
@@ -1233,9 +1319,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
return -EFAULT;
rtnl_lock();
if (optname == MRT6_DEL_MFC)
- ret = ip6mr_mfc_delete(&mfc);
+ ret = ip6mr_mfc_delete(net, &mfc);
else
- ret = ip6mr_mfc_add(&mfc, sk == mroute6_socket);
+ ret = ip6mr_mfc_add(net, &mfc,
+ sk == net->ipv6.mroute6_sk);
rtnl_unlock();
return ret;
@@ -1247,7 +1334,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
int v;
if (get_user(v, (int __user *)optval))
return -EFAULT;
- mroute_do_assert = !!v;
+ net->ipv6.mroute_do_assert = !!v;
return 0;
}
@@ -1260,10 +1347,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int
v = !!v;
rtnl_lock();
ret = 0;
- if (v != mroute_do_pim) {
- mroute_do_pim = v;
- mroute_do_assert = v;
- if (mroute_do_pim)
+ if (v != net->ipv6.mroute_do_pim) {
+ net->ipv6.mroute_do_pim = v;
+ net->ipv6.mroute_do_assert = v;
+ if (net->ipv6.mroute_do_pim)
ret = inet6_add_protocol(&pim6_protocol,
IPPROTO_PIM);
else
@@ -1295,6 +1382,7 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval,
{
int olr;
int val;
+ struct net *net = sock_net(sk);
switch (optname) {
case MRT6_VERSION:
@@ -1302,11 +1390,11 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval,
break;
#ifdef CONFIG_IPV6_PIMSM_V2
case MRT6_PIM:
- val = mroute_do_pim;
+ val = net->ipv6.mroute_do_pim;
break;
#endif
case MRT6_ASSERT:
- val = mroute_do_assert;
+ val = net->ipv6.mroute_do_assert;
break;
default:
return -ENOPROTOOPT;
@@ -1336,16 +1424,17 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
struct sioc_mif_req6 vr;
struct mif_device *vif;
struct mfc6_cache *c;
+ struct net *net = sock_net(sk);
switch (cmd) {
case SIOCGETMIFCNT_IN6:
if (copy_from_user(&vr, arg, sizeof(vr)))
return -EFAULT;
- if (vr.mifi >= maxvif)
+ if (vr.mifi >= net->ipv6.maxvif)
return -EINVAL;
read_lock(&mrt_lock);
- vif = &vif6_table[vr.mifi];
- if (MIF_EXISTS(vr.mifi)) {
+ vif = &net->ipv6.vif6_table[vr.mifi];
+ if (MIF_EXISTS(net, vr.mifi)) {
vr.icount = vif->pkt_in;
vr.ocount = vif->pkt_out;
vr.ibytes = vif->bytes_in;
@@ -1363,7 +1452,7 @@ int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
return -EFAULT;
read_lock(&mrt_lock);
- c = ip6mr_cache_find(&sr.src.sin6_addr, &sr.grp.sin6_addr);
+ c = ip6mr_cache_find(net, &sr.src.sin6_addr, &sr.grp.sin6_addr);
if (c) {
sr.pktcnt = c->mfc_un.res.pkt;
sr.bytecnt = c->mfc_un.res.bytes;
@@ -1396,7 +1485,8 @@ static inline int ip6mr_forward2_finish(struct sk_buff *skb)
static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
{
struct ipv6hdr *ipv6h;
- struct mif_device *vif = &vif6_table[vifi];
+ struct net *net = mfc6_net(c);
+ struct mif_device *vif = &net->ipv6.vif6_table[vifi];
struct net_device *dev;
struct dst_entry *dst;
struct flowi fl;
@@ -1410,9 +1500,8 @@ static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
vif->bytes_out += skb->len;
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
- ip6mr_cache_report(skb, vifi, MRT6MSG_WHOLEPKT);
- kfree_skb(skb);
- return 0;
+ ip6mr_cache_report(net, skb, vifi, MRT6MSG_WHOLEPKT);
+ goto out_free;
}
#endif
@@ -1425,7 +1514,7 @@ static int ip6mr_forward2(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
}
};
- dst = ip6_route_output(&init_net, NULL, &fl);
+ dst = ip6_route_output(net, NULL, &fl);
if (!dst)
goto out_free;
@@ -1468,9 +1557,10 @@ out_free:
static int ip6mr_find_vif(struct net_device *dev)
{
+ struct net *net = dev_net(dev);
int ct;
- for (ct = maxvif - 1; ct >= 0; ct--) {
- if (vif6_table[ct].dev == dev)
+ for (ct = net->ipv6.maxvif - 1; ct >= 0; ct--) {
+ if (net->ipv6.vif6_table[ct].dev == dev)
break;
}
return ct;
@@ -1480,6 +1570,7 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache)
{
int psend = -1;
int vif, ct;
+ struct net *net = mfc6_net(cache);
vif = cache->mf6c_parent;
cache->mfc_un.res.pkt++;
@@ -1488,29 +1579,30 @@ static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache)
/*
* Wrong interface: drop packet and (maybe) send PIM assert.
*/
- if (vif6_table[vif].dev != skb->dev) {
+ if (net->ipv6.vif6_table[vif].dev != skb->dev) {
int true_vifi;
cache->mfc_un.res.wrong_if++;
true_vifi = ip6mr_find_vif(skb->dev);
- if (true_vifi >= 0 && mroute_do_assert &&
+ if (true_vifi >= 0 && net->ipv6.mroute_do_assert &&
/* pimsm uses asserts, when switching from RPT to SPT,
so that we cannot check that packet arrived on an oif.
It is bad, but otherwise we would need to move pretty
large chunk of pimd to kernel. Ough... --ANK
*/
- (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) &&
+ (net->ipv6.mroute_do_pim ||
+ cache->mfc_un.res.ttls[true_vifi] < 255) &&
time_after(jiffies,
cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
cache->mfc_un.res.last_assert = jiffies;
- ip6mr_cache_report(skb, true_vifi, MRT6MSG_WRONGMIF);
+ ip6mr_cache_report(net, skb, true_vifi, MRT6MSG_WRONGMIF);
}
goto dont_forward;
}
- vif6_table[vif].pkt_in++;
- vif6_table[vif].bytes_in += skb->len;
+ net->ipv6.vif6_table[vif].pkt_in++;
+ net->ipv6.vif6_table[vif].bytes_in += skb->len;
/*
* Forward the frame
@@ -1543,9 +1635,11 @@ dont_forward:
int ip6_mr_input(struct sk_buff *skb)
{
struct mfc6_cache *cache;
+ struct net *net = dev_net(skb->dev);
read_lock(&mrt_lock);
- cache = ip6mr_cache_find(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
+ cache = ip6mr_cache_find(net,
+ &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr);
/*
* No usable cache entry
@@ -1555,7 +1649,7 @@ int ip6_mr_input(struct sk_buff *skb)
vif = ip6mr_find_vif(skb->dev);
if (vif >= 0) {
- int err = ip6mr_cache_unresolved(vif, skb);
+ int err = ip6mr_cache_unresolved(net, vif, skb);
read_unlock(&mrt_lock);
return err;
@@ -1578,7 +1672,8 @@ ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm)
{
int ct;
struct rtnexthop *nhp;
- struct net_device *dev = vif6_table[c->mf6c_parent].dev;
+ struct net *net = mfc6_net(c);
+ struct net_device *dev = net->ipv6.vif6_table[c->mf6c_parent].dev;
u8 *b = skb_tail_pointer(skb);
struct rtattr *mp_head;
@@ -1594,7 +1689,7 @@ ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm)
nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
nhp->rtnh_flags = 0;
nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
- nhp->rtnh_ifindex = vif6_table[ct].dev->ifindex;
+ nhp->rtnh_ifindex = net->ipv6.vif6_table[ct].dev->ifindex;
nhp->rtnh_len = sizeof(*nhp);
}
}
@@ -1608,14 +1703,15 @@ rtattr_failure:
return -EMSGSIZE;
}
-int ip6mr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
+int ip6mr_get_route(struct net *net,
+ struct sk_buff *skb, struct rtmsg *rtm, int nowait)
{
int err;
struct mfc6_cache *cache;
struct rt6_info *rt = (struct rt6_info *)skb->dst;
read_lock(&mrt_lock);
- cache = ip6mr_cache_find(&rt->rt6i_src.addr, &rt->rt6i_dst.addr);
+ cache = ip6mr_cache_find(net, &rt->rt6i_src.addr, &rt->rt6i_dst.addr);
if (!cache) {
struct sk_buff *skb2;
@@ -1658,7 +1754,7 @@ int ip6mr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
ipv6_addr_copy(&iph->saddr, &rt->rt6i_src.addr);
ipv6_addr_copy(&iph->daddr, &rt->rt6i_dst.addr);
- err = ip6mr_cache_unresolved(vif, skb2);
+ err = ip6mr_cache_unresolved(net, vif, skb2);
read_unlock(&mrt_lock);
return err;
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 4545e4306862..3a0b3be7ece5 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -63,12 +63,12 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return;
spi = htonl(ntohs(ipcomph->cpi));
- x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
+ x = xfrm_state_lookup(&init_net, (xfrm_address_t *)&iph->daddr, spi, IPPROTO_COMP, AF_INET6);
if (!x)
return;
- printk(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/" NIP6_FMT "\n",
- spi, NIP6(iph->daddr));
+ printk(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%pI6\n",
+ spi, &iph->daddr);
xfrm_state_put(x);
}
@@ -76,7 +76,7 @@ static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x)
{
struct xfrm_state *t = NULL;
- t = xfrm_state_alloc();
+ t = xfrm_state_alloc(&init_net);
if (!t)
goto out;
@@ -114,7 +114,7 @@ static int ipcomp6_tunnel_attach(struct xfrm_state *x)
spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&x->props.saddr);
if (spi)
- t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr,
+ t = xfrm_state_lookup(&init_net, (xfrm_address_t *)&x->id.daddr,
spi, IPPROTO_IPV6, AF_INET6);
if (!t) {
t = ipcomp6_tunnel_create(x);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 2aa294be0c79..eeeaad2e8b5c 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -395,6 +395,28 @@ sticky_done:
break;
}
+ case IPV6_PKTINFO:
+ {
+ struct in6_pktinfo pkt;
+
+ if (optlen == 0)
+ goto e_inval;
+ else if (optlen < sizeof(struct in6_pktinfo) || optval == NULL)
+ goto e_inval;
+
+ if (copy_from_user(&pkt, optval, optlen)) {
+ retv = -EFAULT;
+ break;
+ }
+ if (sk->sk_bound_dev_if && pkt.ipi6_ifindex != sk->sk_bound_dev_if)
+ goto e_inval;
+
+ np->sticky_pktinfo.ipi6_ifindex = pkt.ipi6_ifindex;
+ ipv6_addr_copy(&np->sticky_pktinfo.ipi6_addr, &pkt.ipi6_addr);
+ retv = 0;
+ break;
+ }
+
case IPV6_2292PKTOPTIONS:
{
struct ipv6_txoptions *opt = NULL;
@@ -916,8 +938,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
} else {
if (np->rxopt.bits.rxinfo) {
struct in6_pktinfo src_info;
- src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : sk->sk_bound_dev_if;
- ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
+ src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
+ np->sticky_pktinfo.ipi6_ifindex;
+ np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) :
+ ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr));
put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
}
if (np->rxopt.bits.rxhlim) {
@@ -926,8 +950,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
}
if (np->rxopt.bits.rxoinfo) {
struct in6_pktinfo src_info;
- src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : sk->sk_bound_dev_if;
- ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr);
+ src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
+ np->sticky_pktinfo.ipi6_ifindex;
+ np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) :
+ ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr));
put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
}
if (np->rxopt.bits.rxohlim) {
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index d7b3c6d398ae..a51fb33e6864 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -303,20 +303,23 @@ static struct inet6_dev *ip6_mc_find_dev(struct net *net,
dev = dev_get_by_index(net, ifindex);
if (!dev)
- return NULL;
+ goto nodev;
idev = in6_dev_get(dev);
- if (!idev) {
- dev_put(dev);
- return NULL;
- }
+ if (!idev)
+ goto release;
read_lock_bh(&idev->lock);
- if (idev->dead) {
- read_unlock_bh(&idev->lock);
- in6_dev_put(idev);
- dev_put(dev);
- return NULL;
- }
+ if (idev->dead)
+ goto unlock_release;
+
return idev;
+
+unlock_release:
+ read_unlock_bh(&idev->lock);
+ in6_dev_put(idev);
+release:
+ dev_put(dev);
+nodev:
+ return NULL;
}
void ipv6_sock_mc_close(struct sock *sk)
@@ -1466,7 +1469,7 @@ static void mld_sendpack(struct sk_buff *skb)
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
skb->dev->ifindex);
- err = xfrm_lookup(&skb->dst, &fl, NULL, 0);
+ err = xfrm_lookup(net, &skb->dst, &fl, NULL, 0);
if (err)
goto err_out;
@@ -1817,7 +1820,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
hdr->icmp6_cksum = csum_ipv6_magic(saddr, snd_addr, len,
IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr, len, 0));
+ csum_partial(hdr, len, 0));
idev = in6_dev_get(skb->dev);
@@ -1831,7 +1834,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
skb->dev->ifindex);
- err = xfrm_lookup(&skb->dst, &fl, NULL, 0);
+ err = xfrm_lookup(net, &skb->dst, &fl, NULL, 0);
if (err)
goto err_out;
@@ -2430,9 +2433,9 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
seq_printf(seq,
- "%-4d %-15s " NIP6_SEQFMT " %5d %08X %ld\n",
+ "%-4d %-15s %pi6 %5d %08X %ld\n",
state->dev->ifindex, state->dev->name,
- NIP6(im->mca_addr),
+ &im->mca_addr,
im->mca_users, im->mca_flags,
(im->mca_flags&MAF_TIMER_RUNNING) ?
jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
@@ -2591,10 +2594,10 @@ static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
"Source Address", "INC", "EXC");
} else {
seq_printf(seq,
- "%3d %6.6s " NIP6_SEQFMT " " NIP6_SEQFMT " %6lu %6lu\n",
+ "%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
state->dev->ifindex, state->dev->name,
- NIP6(state->im->mca_addr),
- NIP6(psf->sf_addr),
+ &state->im->mca_addr,
+ &psf->sf_addr,
psf->sf_count[MCAST_INCLUDE],
psf->sf_count[MCAST_EXCLUDE]);
}
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 31295c8f6196..f995e19c87a9 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -205,6 +205,7 @@ static inline int mip6_report_rl_allow(struct timeval *stamp,
static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, struct flowi *fl)
{
+ struct net *net = xs_net(x);
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
struct ipv6_destopt_hao *hao = NULL;
struct xfrm_selector sel;
@@ -247,7 +248,7 @@ static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, struct
sel.sport_mask = htons(~0);
sel.ifindex = fl->oif;
- err = km_report(IPPROTO_DSTOPTS, &sel,
+ err = km_report(net, IPPROTO_DSTOPTS, &sel,
(hao ? (xfrm_address_t *)&hao->addr : NULL));
out:
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index d0f54d18e19b..3e2970841bd8 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -437,38 +437,20 @@ static void pndisc_destructor(struct pneigh_entry *n)
ipv6_dev_mc_dec(dev, &maddr);
}
-/*
- * Send a Neighbour Advertisement
- */
-static void __ndisc_send(struct net_device *dev,
- struct neighbour *neigh,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr,
- struct icmp6hdr *icmp6h, const struct in6_addr *target,
- int llinfo)
+struct sk_buff *ndisc_build_skb(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h,
+ const struct in6_addr *target,
+ int llinfo)
{
- struct flowi fl;
- struct dst_entry *dst;
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
- struct inet6_dev *idev;
int len;
int err;
- u8 *opt, type;
-
- type = icmp6h->icmp6_type;
-
- icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex);
-
- dst = icmp6_dst_alloc(dev, neigh, daddr);
- if (!dst)
- return;
-
- err = xfrm_lookup(&dst, &fl, NULL, 0);
- if (err < 0)
- return;
+ u8 *opt;
if (!dev->addr_len)
llinfo = 0;
@@ -485,8 +467,7 @@ static void __ndisc_send(struct net_device *dev,
ND_PRINTK0(KERN_ERR
"ICMPv6 ND: %s() failed to allocate an skb.\n",
__func__);
- dst_release(dst);
- return;
+ return NULL;
}
skb_reserve(skb, LL_RESERVED_SPACE(dev));
@@ -510,9 +491,45 @@ static void __ndisc_send(struct net_device *dev,
hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr,
+ csum_partial(hdr,
len, 0));
+ return skb;
+}
+
+EXPORT_SYMBOL(ndisc_build_skb);
+
+void ndisc_send_skb(struct sk_buff *skb,
+ struct net_device *dev,
+ struct neighbour *neigh,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h)
+{
+ struct flowi fl;
+ struct dst_entry *dst;
+ struct net *net = dev_net(dev);
+ struct sock *sk = net->ipv6.ndisc_sk;
+ struct inet6_dev *idev;
+ int err;
+ u8 type;
+
+ type = icmp6h->icmp6_type;
+
+ icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex);
+
+ dst = icmp6_dst_alloc(dev, neigh, daddr);
+ if (!dst) {
+ kfree_skb(skb);
+ return;
+ }
+
+ err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+ if (err < 0) {
+ kfree_skb(skb);
+ return;
+ }
+
skb->dst = dst;
idev = in6_dev_get(dst->dev);
@@ -529,6 +546,27 @@ static void __ndisc_send(struct net_device *dev,
in6_dev_put(idev);
}
+EXPORT_SYMBOL(ndisc_send_skb);
+
+/*
+ * Send a Neighbour Discover packet
+ */
+static void __ndisc_send(struct net_device *dev,
+ struct neighbour *neigh,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h, const struct in6_addr *target,
+ int llinfo)
+{
+ struct sk_buff *skb;
+
+ skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
+ if (!skb)
+ return;
+
+ ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
+}
+
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
const struct in6_addr *daddr,
const struct in6_addr *solicited_addr,
@@ -647,11 +685,8 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
if ((probes -= neigh->parms->ucast_probes) < 0) {
if (!(neigh->nud_state & NUD_VALID)) {
- ND_PRINTK1(KERN_DEBUG
- "%s(): trying to ucast probe in NUD_INVALID: "
- NIP6_FMT "\n",
- __func__,
- NIP6(*target));
+ ND_PRINTK1(KERN_DEBUG "%s(): trying to ucast probe in NUD_INVALID: %pI6\n",
+ __func__, target);
}
ndisc_send_ns(dev, neigh, target, target, saddr);
} else if ((probes -= neigh->parms->app_probes) < 0) {
@@ -1494,7 +1529,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
if (dst == NULL)
return;
- err = xfrm_lookup(&dst, &fl, NULL, 0);
+ err = xfrm_lookup(net, &dst, &fl, NULL, 0);
if (err)
return;
@@ -1582,7 +1617,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
len, IPPROTO_ICMPV6,
- csum_partial((u8 *) icmph, len, 0));
+ csum_partial(icmph, len, 0));
buff->dst = dst;
idev = in6_dev_get(dst->dev);
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index fd5b3a4e3329..834cea69fb53 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -29,7 +29,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
#ifdef CONFIG_XFRM
if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
xfrm_decode_session(skb, &fl, AF_INET6) == 0)
- if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0))
+ if (xfrm_lookup(net, &skb->dst, &fl, skb->sk, 0))
return -1;
#endif
@@ -56,6 +56,7 @@ EXPORT_SYMBOL(ip6_route_me_harder);
struct ip6_rt_info {
struct in6_addr daddr;
struct in6_addr saddr;
+ u_int32_t mark;
};
static void nf_ip6_saveroute(const struct sk_buff *skb,
@@ -68,6 +69,7 @@ static void nf_ip6_saveroute(const struct sk_buff *skb,
rt_info->daddr = iph->daddr;
rt_info->saddr = iph->saddr;
+ rt_info->mark = skb->mark;
}
}
@@ -79,7 +81,8 @@ static int nf_ip6_reroute(struct sk_buff *skb,
if (entry->hook == NF_INET_LOCAL_OUT) {
struct ipv6hdr *iph = ipv6_hdr(skb);
if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
- !ipv6_addr_equal(&iph->saddr, &rt_info->saddr))
+ !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
+ skb->mark != rt_info->mark)
return ip6_route_me_harder(skb);
}
return 0;
diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c
index caa441d09567..37adf5abc51e 100644
--- a/net/ipv6/netfilter/ip6t_LOG.c
+++ b/net/ipv6/netfilter/ip6t_LOG.c
@@ -61,7 +61,7 @@ static void dump_packet(const struct nf_loginfo *info,
}
/* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */
- printk("SRC=" NIP6_FMT " DST=" NIP6_FMT " ", NIP6(ih->saddr), NIP6(ih->daddr));
+ printk("SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr);
/* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */
printk("LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
@@ -364,8 +364,8 @@ static void dump_packet(const struct nf_loginfo *info,
read_lock_bh(&skb->sk->sk_callback_lock);
if (skb->sk->sk_socket && skb->sk->sk_socket->file)
printk("UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_uid,
- skb->sk->sk_socket->file->f_gid);
+ skb->sk->sk_socket->file->f_cred->fsuid,
+ skb->sk->sk_socket->file->f_cred->fsgid);
read_unlock_bh(&skb->sk->sk_callback_lock);
}
@@ -424,9 +424,8 @@ ip6t_log_packet(u_int8_t pf,
if (skb->dev->type == ARPHRD_SIT) {
const struct iphdr *iph =
(struct iphdr *)skb_mac_header(skb);
- printk("TUNNEL=%u.%u.%u.%u->%u.%u.%u.%u ",
- NIPQUAD(iph->saddr),
- NIPQUAD(iph->daddr));
+ printk("TUNNEL=%pI4->%pI4 ",
+ &iph->saddr, &iph->daddr);
}
} else
printk(" ");
diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c
index 0981b4ccb8b1..5a2d0a41694a 100644
--- a/net/ipv6/netfilter/ip6t_REJECT.c
+++ b/net/ipv6/netfilter/ip6t_REJECT.c
@@ -97,7 +97,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
dst = ip6_route_output(net, NULL, &fl);
if (dst == NULL)
return;
- if (dst->error || xfrm_lookup(&dst, &fl, NULL, 0))
+ if (dst->error || xfrm_lookup(net, &dst, &fl, NULL, 0))
return;
hh_len = (dst->dev->hard_header_len + 15)&~15;
diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
index b110a8a85a14..40d2e36d8fac 100644
--- a/net/ipv6/netfilter/ip6table_filter.c
+++ b/net/ipv6/netfilter/ip6table_filter.c
@@ -61,7 +61,7 @@ static struct xt_table packet_filter = {
/* The work comes in here from netfilter.c. */
static unsigned int
-ip6t_local_in_hook(unsigned int hook,
+ip6t_in_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
@@ -72,17 +72,6 @@ ip6t_local_in_hook(unsigned int hook,
}
static unsigned int
-ip6t_forward_hook(unsigned int hook,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
-{
- return ip6t_do_table(skb, hook, in, out,
- dev_net(in)->ipv6.ip6table_filter);
-}
-
-static unsigned int
ip6t_local_out_hook(unsigned int hook,
struct sk_buff *skb,
const struct net_device *in,
@@ -105,14 +94,14 @@ ip6t_local_out_hook(unsigned int hook,
static struct nf_hook_ops ip6t_ops[] __read_mostly = {
{
- .hook = ip6t_local_in_hook,
+ .hook = ip6t_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_FILTER,
},
{
- .hook = ip6t_forward_hook,
+ .hook = ip6t_in_hook,
.owner = THIS_MODULE,
.pf = PF_INET6,
.hooknum = NF_INET_FORWARD,
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index e91db16611d9..727b9530448a 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -56,9 +56,8 @@ static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
static int ipv6_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "src=" NIP6_FMT " dst=" NIP6_FMT " ",
- NIP6(*((struct in6_addr *)tuple->src.u3.ip6)),
- NIP6(*((struct in6_addr *)tuple->dst.u3.ip6)));
+ return seq_printf(s, "src=%pI6 dst=%pI6 ",
+ tuple->src.u3.ip6, tuple->dst.u3.ip6);
}
/*
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index 05726177903f..bd52151d31e9 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -253,7 +253,7 @@ static struct ctl_table icmpv6_sysctl_table[] = {
.data = &nf_ct_icmpv6_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 9967ac7a01a8..ed4d79a9e4a6 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -80,7 +80,7 @@ struct ctl_table nf_ct_ipv6_sysctl_table[] = {
.data = &nf_init_frags.timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = NET_NF_CONNTRACK_FRAG6_LOW_THRESH,
@@ -88,7 +88,7 @@ struct ctl_table nf_ct_ipv6_sysctl_table[] = {
.data = &nf_init_frags.low_thresh,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH,
@@ -96,7 +96,7 @@ struct ctl_table nf_ct_ipv6_sysctl_table[] = {
.data = &nf_init_frags.high_thresh,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ .ctl_name = 0 }
};
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 2ba04d41dc25..61f6827e5906 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -860,7 +860,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
+ err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+ if (err < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
if (err < 0)
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index af12de071f4c..3c575118fca5 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -642,7 +642,7 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = {
.data = &init_net.ipv6.frags.high_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV6_IP6FRAG_LOW_THRESH,
@@ -650,7 +650,7 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = {
.data = &init_net.ipv6.frags.low_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IPV6_IP6FRAG_TIME,
@@ -658,8 +658,8 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = {
.data = &init_net.ipv6.frags.timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{ }
};
@@ -671,8 +671,8 @@ static struct ctl_table ip6_frags_ctl_table[] = {
.data = &ip6_frags.secret_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies
},
{ }
};
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 89dc69924340..18c486cf4987 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -108,7 +108,6 @@ static struct dst_ops ip6_dst_ops_template = {
.link_failure = ip6_link_failure,
.update_pmtu = ip6_rt_update_pmtu,
.local_out = __ip6_local_out,
- .entry_size = sizeof(struct rt6_info),
.entries = ATOMIC_INIT(0),
};
@@ -122,7 +121,6 @@ static struct dst_ops ip6_dst_blackhole_ops = {
.destroy = ip6_dst_destroy,
.check = ip6_dst_check,
.update_pmtu = ip6_rt_blackhole_update_pmtu,
- .entry_size = sizeof(struct rt6_info),
.entries = ATOMIC_INIT(0),
};
@@ -2196,7 +2194,7 @@ static int rt6_fill_node(struct net *net,
if (iif) {
#ifdef CONFIG_IPV6_MROUTE
if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
- int err = ip6mr_get_route(skb, rtm, nowait);
+ int err = ip6mr_get_route(net, skb, rtm, nowait);
if (err <= 0) {
if (!nowait) {
if (err == 0)
@@ -2408,19 +2406,16 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
{
struct seq_file *m = p_arg;
- seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr),
- rt->rt6i_dst.plen);
+ seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen);
#ifdef CONFIG_IPV6_SUBTREES
- seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr),
- rt->rt6i_src.plen);
+ seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen);
#else
seq_puts(m, "00000000000000000000000000000000 00 ");
#endif
if (rt->rt6i_nexthop) {
- seq_printf(m, NIP6_SEQFMT,
- NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
+ seq_printf(m, "%pi6", rt->rt6i_nexthop->primary_key);
} else {
seq_puts(m, "00000000000000000000000000000000");
}
@@ -2502,7 +2497,7 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.flush_delay,
.maxlen = sizeof(int),
.mode = 0200,
- .proc_handler = &ipv6_sysctl_rtcache_flush
+ .proc_handler = ipv6_sysctl_rtcache_flush
},
{
.ctl_name = NET_IPV6_ROUTE_GC_THRESH,
@@ -2510,7 +2505,7 @@ ctl_table ipv6_route_table_template[] = {
.data = &ip6_dst_ops_template.gc_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ROUTE_MAX_SIZE,
@@ -2518,7 +2513,7 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_max_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL,
@@ -2526,8 +2521,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT,
@@ -2535,8 +2530,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_gc_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_INTERVAL,
@@ -2544,8 +2539,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_gc_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY,
@@ -2553,8 +2548,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_gc_elasticity,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES,
@@ -2562,8 +2557,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_mtu_expires,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS,
@@ -2571,8 +2566,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_min_advmss,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
@@ -2580,8 +2575,8 @@ ctl_table ipv6_route_table_template[] = {
.data = &init_net.ipv6.sysctl.ip6_rt_gc_min_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_ms_jiffies,
- .strategy = &sysctl_ms_jiffies,
+ .proc_handler = proc_dointvec_ms_jiffies,
+ .strategy = sysctl_ms_jiffies,
},
{ .ctl_name = 0 }
};
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index b7a50e968506..d3467e563f02 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -62,8 +62,8 @@
#define HASH_SIZE 16
#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
-static int ipip6_fb_tunnel_init(struct net_device *dev);
-static int ipip6_tunnel_init(struct net_device *dev);
+static void ipip6_fb_tunnel_init(struct net_device *dev);
+static void ipip6_tunnel_init(struct net_device *dev);
static void ipip6_tunnel_setup(struct net_device *dev);
static int sit_net_id;
@@ -188,7 +188,8 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct net *net,
}
nt = netdev_priv(dev);
- dev->init = ipip6_tunnel_init;
+ ipip6_tunnel_init(dev);
+
nt->parms = *parms;
if (parms->i_flags & SIT_ISATAP)
@@ -926,13 +927,17 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static const struct net_device_ops ipip6_netdev_ops = {
+ .ndo_uninit = ipip6_tunnel_uninit,
+ .ndo_start_xmit = ipip6_tunnel_xmit,
+ .ndo_do_ioctl = ipip6_tunnel_ioctl,
+ .ndo_change_mtu = ipip6_tunnel_change_mtu,
+};
+
static void ipip6_tunnel_setup(struct net_device *dev)
{
- dev->uninit = ipip6_tunnel_uninit;
+ dev->netdev_ops = &ipip6_netdev_ops;
dev->destructor = free_netdev;
- dev->hard_start_xmit = ipip6_tunnel_xmit;
- dev->do_ioctl = ipip6_tunnel_ioctl;
- dev->change_mtu = ipip6_tunnel_change_mtu;
dev->type = ARPHRD_SIT;
dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr);
@@ -943,11 +948,9 @@ static void ipip6_tunnel_setup(struct net_device *dev)
dev->features |= NETIF_F_NETNS_LOCAL;
}
-static int ipip6_tunnel_init(struct net_device *dev)
+static void ipip6_tunnel_init(struct net_device *dev)
{
- struct ip_tunnel *tunnel;
-
- tunnel = netdev_priv(dev);
+ struct ip_tunnel *tunnel = netdev_priv(dev);
tunnel->dev = dev;
strcpy(tunnel->parms.name, dev->name);
@@ -956,11 +959,9 @@ static int ipip6_tunnel_init(struct net_device *dev)
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
ipip6_tunnel_bind_dev(dev);
-
- return 0;
}
-static int ipip6_fb_tunnel_init(struct net_device *dev)
+static void ipip6_fb_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct iphdr *iph = &tunnel->parms.iph;
@@ -977,7 +978,6 @@ static int ipip6_fb_tunnel_init(struct net_device *dev)
dev_hold(dev);
sitn->tunnels_wc[0] = tunnel;
- return 0;
}
static struct xfrm_tunnel sit_handler = {
@@ -1025,16 +1025,17 @@ static int sit_init_net(struct net *net)
err = -ENOMEM;
goto err_alloc_dev;
}
-
- sitn->fb_tunnel_dev->init = ipip6_fb_tunnel_init;
dev_net_set(sitn->fb_tunnel_dev, net);
+ ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
+
if ((err = register_netdev(sitn->fb_tunnel_dev)))
goto err_reg_dev;
return 0;
err_reg_dev:
+ dev_put(sitn->fb_tunnel_dev);
free_netdev(sitn->fb_tunnel_dev);
err_alloc_dev:
/* nothing */
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 676c80b5b14b..711175e0571f 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -259,7 +259,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
goto out_free;
}
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 587f8f60c489..9048fe7e7ea7 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -35,7 +35,7 @@ static ctl_table ipv6_table_template[] = {
.data = &init_net.ipv6.sysctl.bindv6only,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
@@ -47,7 +47,7 @@ static ctl_table ipv6_table[] = {
.data = &sysctl_mld_max_msf,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index b6b356b7912a..e8b8337a8310 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -23,6 +23,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/bottom_half.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
@@ -260,7 +261,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
+ err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+ if (err < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
if (err < 0)
@@ -390,7 +392,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
goto out;
}
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) {
sk->sk_err_soft = -err;
goto out;
}
@@ -492,7 +494,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
goto done;
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
goto done;
skb = tcp_make_synack(sk, dst, req);
@@ -501,7 +503,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
th->check = tcp_v6_check(th, skb->len,
&treq->loc_addr, &treq->rmt_addr,
- csum_partial((char *)th, skb->len, skb->csum));
+ csum_partial(th, skb->len, skb->csum));
ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);
err = ip6_xmit(sk, skb, &fl, opt, 0);
@@ -872,12 +874,10 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
if (net_ratelimit()) {
- printk(KERN_INFO "MD5 Hash %s for "
- "(" NIP6_FMT ", %u)->"
- "(" NIP6_FMT ", %u)\n",
+ printk(KERN_INFO "MD5 Hash %s for (%pI6, %u)->(%pI6, %u)\n",
genhash ? "failed" : "mismatch",
- NIP6(ip6h->saddr), ntohs(th->source),
- NIP6(ip6h->daddr), ntohs(th->dest));
+ &ip6h->saddr, ntohs(th->source),
+ &ip6h->daddr, ntohs(th->dest));
}
return 1;
}
@@ -917,7 +917,7 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
skb->csum_offset = offsetof(struct tcphdr, check);
} else {
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
- csum_partial((char *)th, th->doff<<2,
+ csum_partial(th, th->doff<<2,
skb->csum));
}
}
@@ -999,7 +999,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
}
#endif
- buff->csum = csum_partial((char *)t1, tot_len, 0);
+ buff->csum = csum_partial(t1, tot_len, 0);
memset(&fl, 0, sizeof(fl));
ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
@@ -1020,7 +1020,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
* namespace
*/
if (!ip6_dst_lookup(ctl_sk, &buff->dst, &fl)) {
- if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+ if (xfrm_lookup(net, &buff->dst, &fl, NULL, 0) >= 0) {
ip6_xmit(ctl_sk, buff, &fl, NULL, 0);
TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
if (rst)
@@ -1318,7 +1318,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
goto out;
}
@@ -1831,7 +1831,9 @@ static int tcp_v6_init_sock(struct sock *sk)
sk->sk_sndbuf = sysctl_tcp_wmem[1];
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
- atomic_inc(&tcp_sockets_allocated);
+ local_bh_disable();
+ percpu_counter_inc(&tcp_sockets_allocated);
+ local_bh_enable();
return 0;
}
@@ -2045,6 +2047,7 @@ struct proto tcpv6_prot = {
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp6_sock),
+ .slab_flags = SLAB_DESTROY_BY_RCU,
.twsk_prot = &tcp6_timewait_sock_ops,
.rsk_prot = &tcp6_request_sock_ops,
.h.hashinfo = &tcp_hashinfo,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8b48512ebf6a..84b1a296eecb 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -54,62 +54,91 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum)
return udp_lib_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
+static inline int compute_score(struct sock *sk, struct net *net,
+ unsigned short hnum,
+ struct in6_addr *saddr, __be16 sport,
+ struct in6_addr *daddr, __be16 dport,
+ int dif)
+{
+ int score = -1;
+
+ if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
+ sk->sk_family == PF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet_sock *inet = inet_sk(sk);
+
+ score = 0;
+ if (inet->dport) {
+ if (inet->dport != sport)
+ return -1;
+ score++;
+ }
+ if (!ipv6_addr_any(&np->rcv_saddr)) {
+ if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
+ return -1;
+ score++;
+ }
+ if (!ipv6_addr_any(&np->daddr)) {
+ if (!ipv6_addr_equal(&np->daddr, saddr))
+ return -1;
+ score++;
+ }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score++;
+ }
+ }
+ return score;
+}
+
static struct sock *__udp6_lib_lookup(struct net *net,
struct in6_addr *saddr, __be16 sport,
struct in6_addr *daddr, __be16 dport,
- int dif, struct hlist_head udptable[])
+ int dif, struct udp_table *udptable)
{
- struct sock *sk, *result = NULL;
- struct hlist_node *node;
+ struct sock *sk, *result;
+ struct hlist_nulls_node *node;
unsigned short hnum = ntohs(dport);
- int badness = -1;
-
- read_lock(&udp_hash_lock);
- sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) {
- struct inet_sock *inet = inet_sk(sk);
-
- if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum &&
- sk->sk_family == PF_INET6) {
- struct ipv6_pinfo *np = inet6_sk(sk);
- int score = 0;
- if (inet->dport) {
- if (inet->dport != sport)
- continue;
- score++;
- }
- if (!ipv6_addr_any(&np->rcv_saddr)) {
- if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
- continue;
- score++;
- }
- if (!ipv6_addr_any(&np->daddr)) {
- if (!ipv6_addr_equal(&np->daddr, saddr))
- continue;
- score++;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- continue;
- score++;
- }
- if (score == 4) {
- result = sk;
- break;
- } else if (score > badness) {
- result = sk;
- badness = score;
- }
+ unsigned int hash = udp_hashfn(net, hnum);
+ struct udp_hslot *hslot = &udptable->hash[hash];
+ int score, badness;
+
+ rcu_read_lock();
+begin:
+ result = NULL;
+ badness = -1;
+ sk_nulls_for_each_rcu(sk, node, &hslot->head) {
+ score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif);
+ if (score > badness) {
+ result = sk;
+ badness = score;
}
}
- if (result)
- sock_hold(result);
- read_unlock(&udp_hash_lock);
+ /*
+ * if the nulls value we got at the end of this lookup is
+ * not the expected one, we must restart lookup.
+ * We probably met an item that was moved to another chain.
+ */
+ if (get_nulls_value(node) != hash)
+ goto begin;
+
+ if (result) {
+ if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
+ result = NULL;
+ else if (unlikely(compute_score(result, net, hnum, saddr, sport,
+ daddr, dport, dif) < badness)) {
+ sock_put(result);
+ goto begin;
+ }
+ }
+ rcu_read_unlock();
return result;
}
static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
__be16 sport, __be16 dport,
- struct hlist_head udptable[])
+ struct udp_table *udptable)
{
struct sock *sk;
struct ipv6hdr *iph = ipv6_hdr(skb);
@@ -253,7 +282,7 @@ csum_copy_err:
void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __be32 info,
- struct hlist_head udptable[] )
+ struct udp_table *udptable)
{
struct ipv6_pinfo *np;
struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
@@ -289,7 +318,7 @@ static __inline__ void udpv6_err(struct sk_buff *skb,
struct inet6_skb_parm *opt, int type,
int code, int offset, __be32 info )
{
- __udp6_lib_err(skb, opt, type, code, offset, info, udp_hash);
+ __udp6_lib_err(skb, opt, type, code, offset, info, &udp_table);
}
int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
@@ -347,11 +376,11 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
__be16 rmt_port, struct in6_addr *rmt_addr,
int dif)
{
- struct hlist_node *node;
+ struct hlist_nulls_node *node;
struct sock *s = sk;
unsigned short num = ntohs(loc_port);
- sk_for_each_from(s, node) {
+ sk_nulls_for_each_from(s, node) {
struct inet_sock *inet = inet_sk(s);
if (!net_eq(sock_net(s), net))
@@ -388,14 +417,15 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk,
*/
static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
struct in6_addr *saddr, struct in6_addr *daddr,
- struct hlist_head udptable[])
+ struct udp_table *udptable)
{
struct sock *sk, *sk2;
const struct udphdr *uh = udp_hdr(skb);
+ struct udp_hslot *hslot = &udptable->hash[udp_hashfn(net, ntohs(uh->dest))];
int dif;
- read_lock(&udp_hash_lock);
- sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]);
+ spin_lock(&hslot->lock);
+ sk = sk_nulls_head(&hslot->head);
dif = inet6_iif(skb);
sk = udp_v6_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
if (!sk) {
@@ -404,7 +434,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
}
sk2 = sk;
- while ((sk2 = udp_v6_mcast_next(net, sk_next(sk2), uh->dest, daddr,
+ while ((sk2 = udp_v6_mcast_next(net, sk_nulls_next(sk2), uh->dest, daddr,
uh->source, saddr, dif))) {
struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
if (buff) {
@@ -423,7 +453,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
out:
- read_unlock(&udp_hash_lock);
+ spin_unlock(&hslot->lock);
return 0;
}
@@ -461,7 +491,7 @@ static inline int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh,
return 0;
}
-int __udp6_lib_rcv(struct sk_buff *skb, struct hlist_head udptable[],
+int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
int proto)
{
struct sock *sk;
@@ -558,7 +588,7 @@ discard:
static __inline__ int udpv6_rcv(struct sk_buff *skb)
{
- return __udp6_lib_rcv(skb, udp_hash, IPPROTO_UDP);
+ return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
}
/*
@@ -763,6 +793,9 @@ do_udp_sendmsg:
if (!fl.oif)
fl.oif = sk->sk_bound_dev_if;
+ if (!fl.oif)
+ fl.oif = np->sticky_pktinfo.ipi6_ifindex;
+
if (msg->msg_controllen) {
opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_txoptions));
@@ -819,7 +852,8 @@ do_udp_sendmsg:
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
+ err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+ if (err < 0) {
if (err == -EREMOTE)
err = ip6_dst_blackhole(sk, &dst, &fl);
if (err < 0)
@@ -1022,7 +1056,7 @@ int udp6_seq_show(struct seq_file *seq, void *v)
static struct udp_seq_afinfo udp6_seq_afinfo = {
.name = "udp6",
.family = AF_INET6,
- .hashtable = udp_hash,
+ .udp_table = &udp_table,
.seq_fops = {
.owner = THIS_MODULE,
},
@@ -1064,7 +1098,8 @@ struct proto udpv6_prot = {
.sysctl_wmem = &sysctl_udp_wmem_min,
.sysctl_rmem = &sysctl_udp_rmem_min,
.obj_size = sizeof(struct udp6_sock),
- .h.udp_hash = udp_hash,
+ .slab_flags = SLAB_DESTROY_BY_RCU,
+ .h.udp_table = &udp_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udpv6_setsockopt,
.compat_getsockopt = compat_udpv6_getsockopt,
diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h
index 92dd7da766d8..23779208c334 100644
--- a/net/ipv6/udp_impl.h
+++ b/net/ipv6/udp_impl.h
@@ -7,9 +7,9 @@
#include <net/inet_common.h>
#include <net/transp_v6.h>
-extern int __udp6_lib_rcv(struct sk_buff *, struct hlist_head [], int );
+extern int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int );
extern void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *,
- int , int , int , __be32 , struct hlist_head []);
+ int , int , int , __be32 , struct udp_table *);
extern int udp_v6_get_port(struct sock *sk, unsigned short snum);
diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c
index 3cd1a1ac3d6c..ba162a824585 100644
--- a/net/ipv6/udplite.c
+++ b/net/ipv6/udplite.c
@@ -15,14 +15,14 @@
static int udplitev6_rcv(struct sk_buff *skb)
{
- return __udp6_lib_rcv(skb, udplite_hash, IPPROTO_UDPLITE);
+ return __udp6_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
}
static void udplitev6_err(struct sk_buff *skb,
struct inet6_skb_parm *opt,
int type, int code, int offset, __be32 info)
{
- __udp6_lib_err(skb, opt, type, code, offset, info, udplite_hash);
+ __udp6_lib_err(skb, opt, type, code, offset, info, &udplite_table);
}
static struct inet6_protocol udplitev6_protocol = {
@@ -49,7 +49,8 @@ struct proto udplitev6_prot = {
.unhash = udp_lib_unhash,
.get_port = udp_v6_get_port,
.obj_size = sizeof(struct udp6_sock),
- .h.udp_hash = udplite_hash,
+ .slab_flags = SLAB_DESTROY_BY_RCU,
+ .h.udp_table = &udplite_table,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_udpv6_setsockopt,
.compat_getsockopt = compat_udpv6_getsockopt,
@@ -95,7 +96,7 @@ void udplitev6_exit(void)
static struct udp_seq_afinfo udplite6_seq_afinfo = {
.name = "udplite6",
.family = AF_INET6,
- .hashtable = udplite_hash,
+ .udp_table = &udplite_table,
.seq_fops = {
.owner = THIS_MODULE,
},
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index a71c7ddcb41e..9084582d236b 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -58,6 +58,7 @@ EXPORT_SYMBOL(xfrm6_rcv);
int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
xfrm_address_t *saddr, u8 proto)
{
+ struct net *net = dev_net(skb->dev);
struct xfrm_state *x = NULL;
int i = 0;
@@ -67,7 +68,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
sp = secpath_dup(skb->sp);
if (!sp) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
goto drop;
}
if (skb->sp)
@@ -76,7 +77,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
}
if (1 + skb->sp->len == XFRM_MAX_DEPTH) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
@@ -100,7 +101,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
break;
}
- x = xfrm_state_lookup_byaddr(dst, src, proto, AF_INET6);
+ x = xfrm_state_lookup_byaddr(net, dst, src, proto, AF_INET6);
if (!x)
continue;
@@ -122,7 +123,7 @@ int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr,
}
if (!x) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINNOSTATES);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
xfrm_audit_state_notfound_simple(skb, AF_INET6);
goto drop;
}
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 08e4cbbe3f04..97ab068e8ccc 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -27,7 +27,8 @@
static struct dst_ops xfrm6_dst_ops;
static struct xfrm_policy_afinfo xfrm6_policy_afinfo;
-static struct dst_entry *xfrm6_dst_lookup(int tos, xfrm_address_t *saddr,
+static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos,
+ xfrm_address_t *saddr,
xfrm_address_t *daddr)
{
struct flowi fl = {};
@@ -38,7 +39,7 @@ static struct dst_entry *xfrm6_dst_lookup(int tos, xfrm_address_t *saddr,
if (saddr)
memcpy(&fl.fl6_src, saddr, sizeof(fl.fl6_src));
- dst = ip6_route_output(&init_net, NULL, &fl);
+ dst = ip6_route_output(net, NULL, &fl);
err = dst->error;
if (dst->error) {
@@ -49,12 +50,13 @@ static struct dst_entry *xfrm6_dst_lookup(int tos, xfrm_address_t *saddr,
return dst;
}
-static int xfrm6_get_saddr(xfrm_address_t *saddr, xfrm_address_t *daddr)
+static int xfrm6_get_saddr(struct net *net,
+ xfrm_address_t *saddr, xfrm_address_t *daddr)
{
struct dst_entry *dst;
struct net_device *dev;
- dst = xfrm6_dst_lookup(0, NULL, daddr);
+ dst = xfrm6_dst_lookup(net, 0, NULL, daddr);
if (IS_ERR(dst))
return -EHOSTUNREACH;
@@ -144,6 +146,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev)
static inline void
_decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
{
+ int onlyproto = 0;
u16 offset = skb_network_header_len(skb);
struct ipv6hdr *hdr = ipv6_hdr(skb);
struct ipv6_opt_hdr *exthdr;
@@ -159,6 +162,8 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
exthdr = (struct ipv6_opt_hdr *)(nh + offset);
switch (nexthdr) {
+ case NEXTHDR_FRAGMENT:
+ onlyproto = 1;
case NEXTHDR_ROUTING:
case NEXTHDR_HOP:
case NEXTHDR_DEST:
@@ -172,7 +177,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
case IPPROTO_TCP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
- if (pskb_may_pull(skb, nh + offset + 4 - skb->data)) {
+ if (!onlyproto && pskb_may_pull(skb, nh + offset + 4 - skb->data)) {
__be16 *ports = (__be16 *)exthdr;
fl->fl_ip_sport = ports[!!reverse];
@@ -182,7 +187,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
return;
case IPPROTO_ICMPV6:
- if (pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
+ if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) {
u8 *icmp = (u8 *)exthdr;
fl->fl_icmp_type = icmp[0];
@@ -193,7 +198,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
case IPPROTO_MH:
- if (pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
+ if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
struct ip6_mh *mh;
mh = (struct ip6_mh *)exthdr;
@@ -217,7 +222,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
static inline int xfrm6_garbage_collect(struct dst_ops *ops)
{
- xfrm6_policy_afinfo.garbage_collect();
+ xfrm6_policy_afinfo.garbage_collect(&init_net);
return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
}
@@ -274,7 +279,6 @@ static struct dst_ops xfrm6_dst_ops = {
.ifdown = xfrm6_dst_ifdown,
.local_out = __ip6_local_out,
.gc_thresh = 1024,
- .entry_size = sizeof(struct xfrm_dst),
.entries = ATOMIC_INIT(0),
};
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
index 60c78cfc2737..0e685b05496e 100644
--- a/net/ipv6/xfrm6_state.c
+++ b/net/ipv6/xfrm6_state.c
@@ -19,8 +19,6 @@
#include <net/ipv6.h>
#include <net/addrconf.h>
-static struct xfrm_state_afinfo xfrm6_state_afinfo;
-
static void
__xfrm6_init_tempsel(struct xfrm_state *x, struct flowi *fl,
struct xfrm_tmpl *tmpl,
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
index c2b278138604..80193db224d9 100644
--- a/net/ipv6/xfrm6_tunnel.c
+++ b/net/ipv6/xfrm6_tunnel.c
@@ -345,24 +345,23 @@ static struct xfrm6_tunnel xfrm46_tunnel_handler = {
static int __init xfrm6_tunnel_init(void)
{
if (xfrm_register_type(&xfrm6_tunnel_type, AF_INET6) < 0)
- return -EAGAIN;
-
- if (xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6)) {
- xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
- return -EAGAIN;
- }
- if (xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET)) {
- xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
- xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
- return -EAGAIN;
- }
- if (xfrm6_tunnel_spi_init() < 0) {
- xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
- xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
- xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
- return -EAGAIN;
- }
+ goto err;
+ if (xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6))
+ goto unreg;
+ if (xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET))
+ goto dereg6;
+ if (xfrm6_tunnel_spi_init() < 0)
+ goto dereg46;
return 0;
+
+dereg46:
+ xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET);
+dereg6:
+ xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6);
+unreg:
+ xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
+err:
+ return -EAGAIN;
}
static void __exit xfrm6_tunnel_fini(void)
diff --git a/net/ipx/sysctl_net_ipx.c b/net/ipx/sysctl_net_ipx.c
index 92fef864e852..633fcab35580 100644
--- a/net/ipx/sysctl_net_ipx.c
+++ b/net/ipx/sysctl_net_ipx.c
@@ -23,7 +23,7 @@ static struct ctl_table ipx_table[] = {
.data = &sysctl_ipx_pprop_broadcasting,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ 0 },
};
diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c
index 6be1ec26b30c..42f7d960d055 100644
--- a/net/irda/irlan/irlan_client.c
+++ b/net/irda/irlan/irlan_client.c
@@ -436,7 +436,6 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
__u16 tmp_cpu; /* Temporary value in host order */
__u8 *bytes;
int i;
- DECLARE_MAC_BUF(mac);
IRDA_DEBUG(4, "%s(), parm=%s\n", __func__ , param);
@@ -521,8 +520,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
/* FILTER_ENTRY, have we got an ethernet address? */
if (strcmp(param, "FILTER_ENTRY") == 0) {
bytes = value;
- IRDA_DEBUG(4, "Ethernet address = %s\n",
- print_mac(mac, bytes));
+ IRDA_DEBUG(4, "Ethernet address = %pM\n", bytes);
for (i = 0; i < 6; i++)
self->dev->dev_addr[i] = bytes[i];
}
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index 9a1cd87e7142..774d73a76852 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -207,7 +207,7 @@ static struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr)
if (!dev)
return NULL;
- self = dev->priv;
+ self = netdev_priv(dev);
self->dev = dev;
/*
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index f17b65af9c9b..2562ebc1b22c 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -1325,6 +1325,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
struct irlap_cb *self;
int command;
__u8 control;
+ int ret = -1;
if (!net_eq(dev_net(dev), &init_net))
goto out;
@@ -1333,25 +1334,21 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
self = (struct irlap_cb *) dev->atalk_ptr;
/* If the net device is down, then IrLAP is gone! */
- if (!self || self->magic != LAP_MAGIC) {
- dev_kfree_skb(skb);
- return -1;
- }
+ if (!self || self->magic != LAP_MAGIC)
+ goto err;
/* We are no longer an "old" protocol, so we need to handle
* share and non linear skbs. This should never happen, so
* we don't need to be clever about it. Jean II */
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IRDA_ERROR("%s: can't clone shared skb!\n", __func__);
- dev_kfree_skb(skb);
- return -1;
+ goto err;
}
/* Check if frame is large enough for parsing */
if (!pskb_may_pull(skb, 2)) {
IRDA_ERROR("%s: frame too short!\n", __func__);
- dev_kfree_skb(skb);
- return -1;
+ goto err;
}
command = skb->data[0] & CMD_FRAME;
@@ -1442,7 +1439,9 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
break;
}
out:
+ ret = 0;
+err:
/* Always drop our reference on the skb */
dev_kfree_skb(skb);
- return 0;
+ return ret;
}
diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c
index 9ab3df15425d..57f8817c3979 100644
--- a/net/irda/irsysctl.c
+++ b/net/irda/irsysctl.c
@@ -118,8 +118,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_discovery,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &do_discovery,
- .strategy = &sysctl_intvec
+ .proc_handler = do_discovery,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_IRDA_DEVNAME,
@@ -127,8 +127,8 @@ static ctl_table irda_table[] = {
.data = sysctl_devname,
.maxlen = 65,
.mode = 0644,
- .proc_handler = &do_devname,
- .strategy = &sysctl_string
+ .proc_handler = do_devname,
+ .strategy = sysctl_string
},
#ifdef CONFIG_IRDA_DEBUG
{
@@ -137,7 +137,7 @@ static ctl_table irda_table[] = {
.data = &irda_debug,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#endif
#ifdef CONFIG_IRDA_FAST_RR
@@ -147,7 +147,7 @@ static ctl_table irda_table[] = {
.data = &sysctl_fast_poll_increase,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
#endif
{
@@ -156,8 +156,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_discovery_slots,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_discovery_slots,
.extra2 = &max_discovery_slots
},
@@ -167,7 +167,7 @@ static ctl_table irda_table[] = {
.data = &sysctl_discovery_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{
.ctl_name = NET_IRDA_SLOT_TIMEOUT,
@@ -175,8 +175,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_slot_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_slot_timeout,
.extra2 = &max_slot_timeout
},
@@ -186,8 +186,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_max_baud_rate,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_max_baud_rate,
.extra2 = &max_max_baud_rate
},
@@ -197,8 +197,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_min_tx_turn_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_min_tx_turn_time,
.extra2 = &max_min_tx_turn_time
},
@@ -208,8 +208,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_max_tx_data_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_max_tx_data_size,
.extra2 = &max_max_tx_data_size
},
@@ -219,8 +219,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_max_tx_window,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_max_tx_window,
.extra2 = &max_max_tx_window
},
@@ -230,8 +230,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_max_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_max_noreply_time,
.extra2 = &max_max_noreply_time
},
@@ -241,8 +241,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_warn_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_warn_noreply_time,
.extra2 = &max_warn_noreply_time
},
@@ -252,8 +252,8 @@ static ctl_table irda_table[] = {
.data = &sysctl_lap_keepalive_time,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_lap_keepalive_time,
.extra2 = &max_lap_keepalive_time
},
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index 74e439e80823..ecf4eb2717cb 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -201,7 +201,7 @@ static void irttp_todo_expired(unsigned long data)
*
* Flushes (removes all frames) in transitt-buffer (tx_list)
*/
-void irttp_flush_queues(struct tsap_cb *self)
+static void irttp_flush_queues(struct tsap_cb *self)
{
struct sk_buff* skb;
@@ -1266,9 +1266,9 @@ static void irttp_connect_confirm(void *instance, void *sap,
* Some other device is connecting to this TSAP
*
*/
-void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos,
- __u32 max_seg_size, __u8 max_header_size,
- struct sk_buff *skb)
+static void irttp_connect_indication(void *instance, void *sap,
+ struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size,
+ struct sk_buff *skb)
{
struct tsap_cb *self;
struct lsap_cb *lsap;
@@ -1579,8 +1579,8 @@ EXPORT_SYMBOL(irttp_disconnect_request);
* Disconnect indication, TSAP disconnected by peer?
*
*/
-void irttp_disconnect_indication(void *instance, void *sap, LM_REASON reason,
- struct sk_buff *skb)
+static void irttp_disconnect_indication(void *instance, void *sap,
+ LM_REASON reason, struct sk_buff *skb)
{
struct tsap_cb *self;
@@ -1664,7 +1664,7 @@ static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb)
* Check if we have any frames to be transmitted, or if we have any
* available credit to give away.
*/
-void irttp_run_rx_queue(struct tsap_cb *self)
+static void irttp_run_rx_queue(struct tsap_cb *self)
{
struct sk_buff *skb;
int more = 0;
diff --git a/net/irda/timer.c b/net/irda/timer.c
index d730099080a2..0335ba0cc593 100644
--- a/net/irda/timer.c
+++ b/net/irda/timer.c
@@ -219,7 +219,7 @@ static void irlap_backoff_timer_expired(void *data)
*
*
*/
-void irlap_media_busy_expired(void* data)
+static void irlap_media_busy_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 29f7baa25110..af3192d2a5a3 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -8,6 +8,9 @@
* Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
*/
+#define KMSG_COMPONENT "af_iucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/list.h>
@@ -616,6 +619,8 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct iucv_sock *iucv = iucv_sk(sk);
struct sk_buff *skb;
struct iucv_message txmsg;
+ char user_id[9];
+ char appl_id[9];
int err;
err = sock_error(sk);
@@ -651,8 +656,15 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
err = iucv_message_send(iucv->path, &txmsg, 0, 0,
(void *) skb->data, skb->len);
if (err) {
- if (err == 3)
- printk(KERN_ERR "AF_IUCV msg limit exceeded\n");
+ if (err == 3) {
+ user_id[8] = 0;
+ memcpy(user_id, iucv->dst_user_id, 8);
+ appl_id[8] = 0;
+ memcpy(appl_id, iucv->dst_name, 8);
+ pr_err("Application %s on z/VM guest %s"
+ " exceeds message limit\n",
+ user_id, appl_id);
+ }
skb_unlink(skb, &iucv->send_skb_q);
err = -EPIPE;
goto fail;
@@ -1190,7 +1202,8 @@ static int __init afiucv_init(void)
int err;
if (!MACHINE_IS_VM) {
- printk(KERN_ERR "AF_IUCV connection needs VM as base\n");
+ pr_err("The af_iucv module cannot be loaded"
+ " without z/VM\n");
err = -EPROTONOSUPPORT;
goto out;
}
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index d7b54b5bfa69..8f57d4f4328a 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -30,6 +30,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "iucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
@@ -424,8 +427,8 @@ static void iucv_declare_cpu(void *data)
err = "Paging or storage error";
break;
}
- printk(KERN_WARNING "iucv_register: iucv_declare_buffer "
- "on cpu %i returned error 0x%02x (%s)\n", cpu, rc, err);
+ pr_warning("Defining an interrupt buffer on CPU %i"
+ " failed with 0x%02x (%s)\n", cpu, rc, err);
return;
}
@@ -957,7 +960,52 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg,
EXPORT_SYMBOL(iucv_message_purge);
/**
- * iucv_message_receive
+ * iucv_message_receive_iprmdata
+ * @path: address of iucv path structure
+ * @msg: address of iucv msg structure
+ * @flags: how the message is received (IUCV_IPBUFLST)
+ * @buffer: address of data buffer or address of struct iucv_array
+ * @size: length of data buffer
+ * @residual:
+ *
+ * Internal function used by iucv_message_receive and __iucv_message_receive
+ * to receive RMDATA data stored in struct iucv_message.
+ */
+static int iucv_message_receive_iprmdata(struct iucv_path *path,
+ struct iucv_message *msg,
+ u8 flags, void *buffer,
+ size_t size, size_t *residual)
+{
+ struct iucv_array *array;
+ u8 *rmmsg;
+ size_t copy;
+
+ /*
+ * Message is 8 bytes long and has been stored to the
+ * message descriptor itself.
+ */
+ if (residual)
+ *residual = abs(size - 8);
+ rmmsg = msg->rmmsg;
+ if (flags & IUCV_IPBUFLST) {
+ /* Copy to struct iucv_array. */
+ size = (size < 8) ? size : 8;
+ for (array = buffer; size > 0; array++) {
+ copy = min_t(size_t, size, array->length);
+ memcpy((u8 *)(addr_t) array->address,
+ rmmsg, copy);
+ rmmsg += copy;
+ size -= copy;
+ }
+ } else {
+ /* Copy to direct buffer. */
+ memcpy(buffer, rmmsg, min_t(size_t, size, 8));
+ }
+ return 0;
+}
+
+/**
+ * __iucv_message_receive
* @path: address of iucv path structure
* @msg: address of iucv msg structure
* @flags: how the message is received (IUCV_IPBUFLST)
@@ -969,44 +1017,19 @@ EXPORT_SYMBOL(iucv_message_purge);
* established paths. This function will deal with RMDATA messages
* embedded in struct iucv_message as well.
*
+ * Locking: no locking
+ *
* Returns the result from the CP IUCV call.
*/
-int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
- u8 flags, void *buffer, size_t size, size_t *residual)
+int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
+ u8 flags, void *buffer, size_t size, size_t *residual)
{
union iucv_param *parm;
- struct iucv_array *array;
- u8 *rmmsg;
- size_t copy;
int rc;
- if (msg->flags & IUCV_IPRMDATA) {
- /*
- * Message is 8 bytes long and has been stored to the
- * message descriptor itself.
- */
- rc = (size < 8) ? 5 : 0;
- if (residual)
- *residual = abs(size - 8);
- rmmsg = msg->rmmsg;
- if (flags & IUCV_IPBUFLST) {
- /* Copy to struct iucv_array. */
- size = (size < 8) ? size : 8;
- for (array = buffer; size > 0; array++) {
- copy = min_t(size_t, size, array->length);
- memcpy((u8 *)(addr_t) array->address,
- rmmsg, copy);
- rmmsg += copy;
- size -= copy;
- }
- } else {
- /* Copy to direct buffer. */
- memcpy(buffer, rmmsg, min_t(size_t, size, 8));
- }
- return 0;
- }
-
- local_bh_disable();
+ if (msg->flags & IUCV_IPRMDATA)
+ return iucv_message_receive_iprmdata(path, msg, flags,
+ buffer, size, residual);
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
parm->db.ipbfadr1 = (u32)(addr_t) buffer;
@@ -1022,6 +1045,37 @@ int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
if (residual)
*residual = parm->db.ipbfln1f;
}
+ return rc;
+}
+EXPORT_SYMBOL(__iucv_message_receive);
+
+/**
+ * iucv_message_receive
+ * @path: address of iucv path structure
+ * @msg: address of iucv msg structure
+ * @flags: how the message is received (IUCV_IPBUFLST)
+ * @buffer: address of data buffer or address of struct iucv_array
+ * @size: length of data buffer
+ * @residual:
+ *
+ * This function receives messages that are being sent to you over
+ * established paths. This function will deal with RMDATA messages
+ * embedded in struct iucv_message as well.
+ *
+ * Locking: local_bh_enable/local_bh_disable
+ *
+ * Returns the result from the CP IUCV call.
+ */
+int iucv_message_receive(struct iucv_path *path, struct iucv_message *msg,
+ u8 flags, void *buffer, size_t size, size_t *residual)
+{
+ int rc;
+
+ if (msg->flags & IUCV_IPRMDATA)
+ return iucv_message_receive_iprmdata(path, msg, flags,
+ buffer, size, residual);
+ local_bh_disable();
+ rc = __iucv_message_receive(path, msg, flags, buffer, size, residual);
local_bh_enable();
return rc;
}
@@ -1101,7 +1155,7 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg,
EXPORT_SYMBOL(iucv_message_reply);
/**
- * iucv_message_send
+ * __iucv_message_send
* @path: address of iucv path structure
* @msg: address of iucv msg structure
* @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
@@ -1113,15 +1167,16 @@ EXPORT_SYMBOL(iucv_message_reply);
* transmitted is in a buffer and this is a one-way message and the
* receiver will not reply to the message.
*
+ * Locking: no locking
+ *
* Returns the result from the CP IUCV call.
*/
-int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
+int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
u8 flags, u32 srccls, void *buffer, size_t size)
{
union iucv_param *parm;
int rc;
- local_bh_disable();
parm = iucv_param[smp_processor_id()];
memset(parm, 0, sizeof(union iucv_param));
if (flags & IUCV_IPRMDATA) {
@@ -1144,6 +1199,34 @@ int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
rc = iucv_call_b2f0(IUCV_SEND, parm);
if (!rc)
msg->id = parm->db.ipmsgid;
+ return rc;
+}
+EXPORT_SYMBOL(__iucv_message_send);
+
+/**
+ * iucv_message_send
+ * @path: address of iucv path structure
+ * @msg: address of iucv msg structure
+ * @flags: how the message is sent (IUCV_IPRMDATA, IUCV_IPPRTY, IUCV_IPBUFLST)
+ * @srccls: source class of message
+ * @buffer: address of send buffer or address of struct iucv_array
+ * @size: length of send buffer
+ *
+ * This function transmits data to another application. Data to be
+ * transmitted is in a buffer and this is a one-way message and the
+ * receiver will not reply to the message.
+ *
+ * Locking: local_bh_enable/local_bh_disable
+ *
+ * Returns the result from the CP IUCV call.
+ */
+int iucv_message_send(struct iucv_path *path, struct iucv_message *msg,
+ u8 flags, u32 srccls, void *buffer, size_t size)
+{
+ int rc;
+
+ local_bh_disable();
+ rc = __iucv_message_send(path, msg, flags, srccls, buffer, size);
local_bh_enable();
return rc;
}
@@ -1572,7 +1655,7 @@ static void iucv_external_interrupt(u16 code)
BUG_ON(p->iptype < 0x01 || p->iptype > 0x09);
work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
if (!work) {
- printk(KERN_WARNING "iucv_external_interrupt: out of memory\n");
+ pr_warning("iucv_external_interrupt: out of memory\n");
return;
}
memcpy(&work->data, p, sizeof(work->data));
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 5b22e011653b..f8bd8df5e257 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -27,6 +27,7 @@
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <net/xfrm.h>
#include <net/sock.h>
@@ -34,15 +35,16 @@
#define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
#define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
-
-/* List of all pfkey sockets. */
-static HLIST_HEAD(pfkey_table);
+static int pfkey_net_id;
+struct netns_pfkey {
+ /* List of all pfkey sockets. */
+ struct hlist_head table;
+ atomic_t socks_nr;
+};
static DECLARE_WAIT_QUEUE_HEAD(pfkey_table_wait);
static DEFINE_RWLOCK(pfkey_table_lock);
static atomic_t pfkey_table_users = ATOMIC_INIT(0);
-static atomic_t pfkey_socks_nr = ATOMIC_INIT(0);
-
struct pfkey_sock {
/* struct sock must be the first member of struct pfkey_sock */
struct sock sk;
@@ -89,6 +91,9 @@ static void pfkey_terminate_dump(struct pfkey_sock *pfk)
static void pfkey_sock_destruct(struct sock *sk)
{
+ struct net *net = sock_net(sk);
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
pfkey_terminate_dump(pfkey_sk(sk));
skb_queue_purge(&sk->sk_receive_queue);
@@ -100,7 +105,7 @@ static void pfkey_sock_destruct(struct sock *sk)
WARN_ON(atomic_read(&sk->sk_rmem_alloc));
WARN_ON(atomic_read(&sk->sk_wmem_alloc));
- atomic_dec(&pfkey_socks_nr);
+ atomic_dec(&net_pfkey->socks_nr);
}
static void pfkey_table_grab(void)
@@ -151,8 +156,11 @@ static const struct proto_ops pfkey_ops;
static void pfkey_insert(struct sock *sk)
{
+ struct net *net = sock_net(sk);
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
pfkey_table_grab();
- sk_add_node(sk, &pfkey_table);
+ sk_add_node(sk, &net_pfkey->table);
pfkey_table_ungrab();
}
@@ -171,12 +179,10 @@ static struct proto key_proto = {
static int pfkey_create(struct net *net, struct socket *sock, int protocol)
{
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
struct sock *sk;
int err;
- if (net != &init_net)
- return -EAFNOSUPPORT;
-
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (sock->type != SOCK_RAW)
@@ -195,7 +201,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol)
sk->sk_family = PF_KEY;
sk->sk_destruct = pfkey_sock_destruct;
- atomic_inc(&pfkey_socks_nr);
+ atomic_inc(&net_pfkey->socks_nr);
pfkey_insert(sk);
@@ -255,8 +261,10 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2,
#define BROADCAST_REGISTERED 2
#define BROADCAST_PROMISC_ONLY 4
static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
- int broadcast_flags, struct sock *one_sk)
+ int broadcast_flags, struct sock *one_sk,
+ struct net *net)
{
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
struct sock *sk;
struct hlist_node *node;
struct sk_buff *skb2 = NULL;
@@ -269,7 +277,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
return -ENOMEM;
pfkey_lock_table();
- sk_for_each(sk, node, &pfkey_table) {
+ sk_for_each(sk, node, &net_pfkey->table) {
struct pfkey_sock *pfk = pfkey_sk(sk);
int err2;
@@ -328,7 +336,7 @@ static int pfkey_do_dump(struct pfkey_sock *pfk)
hdr->sadb_msg_seq = 0;
hdr->sadb_msg_errno = rc;
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
- &pfk->sk);
+ &pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = NULL;
}
@@ -367,7 +375,7 @@ static int pfkey_error(struct sadb_msg *orig, int err, struct sock *sk)
hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
sizeof(uint64_t));
- pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk);
+ pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
return 0;
}
@@ -645,7 +653,7 @@ int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, xfrm_address_t *xaddr)
xaddr);
}
-static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
+static struct xfrm_state *pfkey_xfrm_state_lookup(struct net *net, struct sadb_msg *hdr, void **ext_hdrs)
{
struct sadb_sa *sa;
struct sadb_address *addr;
@@ -683,7 +691,7 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
if (!xaddr)
return NULL;
- return xfrm_state_lookup(xaddr, sa->sadb_sa_spi, proto, family);
+ return xfrm_state_lookup(net, xaddr, sa->sadb_sa_spi, proto, family);
}
#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
@@ -1058,7 +1066,8 @@ static inline struct sk_buff *pfkey_xfrm_state2msg_expire(struct xfrm_state *x,
return __pfkey_xfrm_state2msg(x, 0, hsc);
}
-static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
+static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
+ struct sadb_msg *hdr,
void **ext_hdrs)
{
struct xfrm_state *x;
@@ -1122,7 +1131,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
(key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
return ERR_PTR(-EINVAL);
- x = xfrm_state_alloc();
+ x = xfrm_state_alloc(net);
if (x == NULL)
return ERR_PTR(-ENOBUFS);
@@ -1298,6 +1307,7 @@ static int pfkey_reserved(struct sock *sk, struct sk_buff *skb, struct sadb_msg
static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
struct sk_buff *resp_skb;
struct sadb_x_sa2 *sa2;
struct sadb_address *saddr, *daddr;
@@ -1348,7 +1358,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
}
if (hdr->sadb_msg_seq) {
- x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+ x = xfrm_find_acq_byseq(net, hdr->sadb_msg_seq);
if (x && xfrm_addr_cmp(&x->id.daddr, xdaddr, family)) {
xfrm_state_put(x);
x = NULL;
@@ -1356,7 +1366,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
}
if (!x)
- x = xfrm_find_acq(mode, reqid, proto, xdaddr, xsaddr, 1, family);
+ x = xfrm_find_acq(net, mode, reqid, proto, xdaddr, xsaddr, 1, family);
if (x == NULL)
return -ENOENT;
@@ -1389,13 +1399,14 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
xfrm_state_put(x);
- pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk);
+ pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
return 0;
}
static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
struct xfrm_state *x;
if (hdr->sadb_msg_len != sizeof(struct sadb_msg)/8)
@@ -1404,14 +1415,14 @@ static int pfkey_acquire(struct sock *sk, struct sk_buff *skb, struct sadb_msg *
if (hdr->sadb_msg_seq == 0 || hdr->sadb_msg_errno == 0)
return 0;
- x = xfrm_find_acq_byseq(hdr->sadb_msg_seq);
+ x = xfrm_find_acq_byseq(net, hdr->sadb_msg_seq);
if (x == NULL)
return 0;
spin_lock_bh(&x->lock);
if (x->km.state == XFRM_STATE_ACQ) {
x->km.state = XFRM_STATE_ERROR;
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
}
spin_unlock_bh(&x->lock);
xfrm_state_put(x);
@@ -1476,18 +1487,19 @@ static int key_notify_sa(struct xfrm_state *x, struct km_event *c)
hdr->sadb_msg_seq = c->seq;
hdr->sadb_msg_pid = c->pid;
- pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
return 0;
}
static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
struct xfrm_state *x;
int err;
struct km_event c;
- x = pfkey_msg2xfrm_state(hdr, ext_hdrs);
+ x = pfkey_msg2xfrm_state(net, hdr, ext_hdrs);
if (IS_ERR(x))
return PTR_ERR(x);
@@ -1521,6 +1533,7 @@ out:
static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
struct xfrm_state *x;
struct km_event c;
int err;
@@ -1530,7 +1543,7 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
return -EINVAL;
- x = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
+ x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
if (x == NULL)
return -ESRCH;
@@ -1562,6 +1575,7 @@ out:
static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
__u8 proto;
struct sk_buff *out_skb;
struct sadb_msg *out_hdr;
@@ -1572,7 +1586,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
ext_hdrs[SADB_EXT_ADDRESS_DST-1]))
return -EINVAL;
- x = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
+ x = pfkey_xfrm_state_lookup(net, hdr, ext_hdrs);
if (x == NULL)
return -ESRCH;
@@ -1590,7 +1604,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
out_hdr->sadb_msg_reserved = 0;
out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
- pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk);
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
return 0;
}
@@ -1691,7 +1705,7 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, struct sadb_msg
return -ENOBUFS;
}
- pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk);
+ pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk, sock_net(sk));
return 0;
}
@@ -1713,13 +1727,14 @@ static int key_notify_sa_flush(struct km_event *c)
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
- pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;
}
static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
unsigned proto;
struct km_event c;
struct xfrm_audit audit_info;
@@ -1732,13 +1747,14 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hd
audit_info.loginuid = audit_get_loginuid(current);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = 0;
- err = xfrm_state_flush(proto, &audit_info);
+ err = xfrm_state_flush(net, proto, &audit_info);
if (err)
return err;
c.data.proto = proto;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
c.event = XFRM_MSG_FLUSHSA;
+ c.net = net;
km_state_notify(NULL, &c);
return 0;
@@ -1768,7 +1784,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr)
if (pfk->dump.skb)
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
- &pfk->sk);
+ &pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = out_skb;
return 0;
@@ -1776,7 +1792,8 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr)
static int pfkey_dump_sa(struct pfkey_sock *pfk)
{
- return xfrm_state_walk(&pfk->dump.u.state, dump_sa, (void *) pfk);
+ struct net *net = sock_net(&pfk->sk);
+ return xfrm_state_walk(net, &pfk->dump.u.state, dump_sa, (void *) pfk);
}
static void pfkey_dump_sa_done(struct pfkey_sock *pfk)
@@ -1817,7 +1834,7 @@ static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, struct sadb_msg *
return -EINVAL;
pfk->promisc = satype;
}
- pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL, BROADCAST_ALL, NULL);
+ pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
return 0;
}
@@ -1833,7 +1850,7 @@ static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
return 0;
}
-static u32 gen_reqid(void)
+static u32 gen_reqid(struct net *net)
{
struct xfrm_policy_walk walk;
u32 start;
@@ -1846,7 +1863,7 @@ static u32 gen_reqid(void)
if (reqid == 0)
reqid = IPSEC_MANUAL_REQID_MAX+1;
xfrm_policy_walk_init(&walk, XFRM_POLICY_TYPE_MAIN);
- rc = xfrm_policy_walk(&walk, check_reqid, (void*)&reqid);
+ rc = xfrm_policy_walk(net, &walk, check_reqid, (void*)&reqid);
xfrm_policy_walk_done(&walk);
if (rc != -EEXIST)
return reqid;
@@ -1857,6 +1874,7 @@ static u32 gen_reqid(void)
static int
parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
{
+ struct net *net = xp_net(xp);
struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
int mode;
@@ -1876,7 +1894,7 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
t->reqid = rq->sadb_x_ipsecrequest_reqid;
if (t->reqid > IPSEC_MANUAL_REQID_MAX)
t->reqid = 0;
- if (!t->reqid && !(t->reqid = gen_reqid()))
+ if (!t->reqid && !(t->reqid = gen_reqid(net)))
return -ENOBUFS;
}
@@ -2147,7 +2165,7 @@ static int key_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c
out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_seq = c->seq;
out_hdr->sadb_msg_pid = c->pid;
- pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
out:
return 0;
@@ -2155,6 +2173,7 @@ out:
static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
int err = 0;
struct sadb_lifetime *lifetime;
struct sadb_address *sa;
@@ -2174,7 +2193,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
if (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir >= IPSEC_DIR_MAX)
return -EINVAL;
- xp = xfrm_policy_alloc(GFP_KERNEL);
+ xp = xfrm_policy_alloc(net, GFP_KERNEL);
if (xp == NULL)
return -ENOBUFS;
@@ -2275,6 +2294,7 @@ out:
static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
int err;
struct sadb_address *sa;
struct sadb_x_policy *pol;
@@ -2324,7 +2344,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
return err;
}
- xp = xfrm_policy_bysel_ctx(XFRM_POLICY_TYPE_MAIN,
+ xp = xfrm_policy_bysel_ctx(net, XFRM_POLICY_TYPE_MAIN,
pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
1, &err);
security_xfrm_policy_free(pol_ctx);
@@ -2372,7 +2392,7 @@ static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, struct sadb
out_hdr->sadb_msg_errno = 0;
out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
- pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk);
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
err = 0;
out:
@@ -2557,6 +2577,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb,
static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
unsigned int dir;
int err = 0, delete;
struct sadb_x_policy *pol;
@@ -2571,8 +2592,8 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
return -EINVAL;
delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
- xp = xfrm_policy_byid(XFRM_POLICY_TYPE_MAIN, dir, pol->sadb_x_policy_id,
- delete, &err);
+ xp = xfrm_policy_byid(net, XFRM_POLICY_TYPE_MAIN, dir,
+ pol->sadb_x_policy_id, delete, &err);
if (xp == NULL)
return -ENOENT;
@@ -2625,7 +2646,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
if (pfk->dump.skb)
pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
- &pfk->sk);
+ &pfk->sk, sock_net(&pfk->sk));
pfk->dump.skb = out_skb;
return 0;
@@ -2633,7 +2654,8 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
static int pfkey_dump_sp(struct pfkey_sock *pfk)
{
- return xfrm_policy_walk(&pfk->dump.u.policy, dump_sp, (void *) pfk);
+ struct net *net = sock_net(&pfk->sk);
+ return xfrm_policy_walk(net, &pfk->dump.u.policy, dump_sp, (void *) pfk);
}
static void pfkey_dump_sp_done(struct pfkey_sock *pfk)
@@ -2672,13 +2694,14 @@ static int key_notify_policy_flush(struct km_event *c)
hdr->sadb_msg_version = PF_KEY_V2;
hdr->sadb_msg_errno = (uint8_t) 0;
hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
- pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL);
+ pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
return 0;
}
static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
{
+ struct net *net = sock_net(sk);
struct km_event c;
struct xfrm_audit audit_info;
int err;
@@ -2686,13 +2709,14 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, struct sadb_msg
audit_info.loginuid = audit_get_loginuid(current);
audit_info.sessionid = audit_get_sessionid(current);
audit_info.secid = 0;
- err = xfrm_policy_flush(XFRM_POLICY_TYPE_MAIN, &audit_info);
+ err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
if (err)
return err;
c.data.type = XFRM_POLICY_TYPE_MAIN;
c.event = XFRM_MSG_FLUSHPOLICY;
c.pid = hdr->sadb_msg_pid;
c.seq = hdr->sadb_msg_seq;
+ c.net = net;
km_policy_notify(NULL, 0, &c);
return 0;
@@ -2732,7 +2756,7 @@ static int pfkey_process(struct sock *sk, struct sk_buff *skb, struct sadb_msg *
int err;
pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
- BROADCAST_PROMISC_ONLY, NULL);
+ BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
memset(ext_hdrs, 0, sizeof(ext_hdrs));
err = parse_exthdrs(skb, hdr, ext_hdrs);
@@ -2935,13 +2959,16 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c)
out_hdr->sadb_msg_seq = 0;
out_hdr->sadb_msg_pid = 0;
- pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+ pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
return 0;
}
static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
{
- if (atomic_read(&pfkey_socks_nr) == 0)
+ struct net *net = x ? xs_net(x) : c->net;
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
+ if (atomic_read(&net_pfkey->socks_nr) == 0)
return 0;
switch (c->event) {
@@ -3103,12 +3130,13 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
xfrm_ctx->ctx_len);
}
- return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
}
static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
u8 *data, int len, int *dir)
{
+ struct net *net = sock_net(sk);
struct xfrm_policy *xp;
struct sadb_x_policy *pol = (struct sadb_x_policy*)data;
struct sadb_x_sec_ctx *sec_ctx;
@@ -3141,7 +3169,7 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
(!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND))
return NULL;
- xp = xfrm_policy_alloc(GFP_ATOMIC);
+ xp = xfrm_policy_alloc(net, GFP_ATOMIC);
if (xp == NULL) {
*dir = -ENOBUFS;
return NULL;
@@ -3300,7 +3328,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
n_port->sadb_x_nat_t_port_port = sport;
n_port->sadb_x_nat_t_port_reserved = 0;
- return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL);
+ return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x));
}
#ifdef CONFIG_NET_KEY_MIGRATE
@@ -3491,7 +3519,7 @@ static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
}
/* broadcast migrate message to sockets */
- pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL);
+ pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
return 0;
@@ -3645,6 +3673,8 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
{
+ struct net *net = seq_file_net(f);
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
struct sock *s;
struct hlist_node *node;
loff_t pos = *ppos;
@@ -3653,7 +3683,7 @@ static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
if (pos == 0)
return SEQ_START_TOKEN;
- sk_for_each(s, node, &pfkey_table)
+ sk_for_each(s, node, &net_pfkey->table)
if (pos-- == 1)
return s;
@@ -3662,9 +3692,12 @@ static void *pfkey_seq_start(struct seq_file *f, loff_t *ppos)
static void *pfkey_seq_next(struct seq_file *f, void *v, loff_t *ppos)
{
+ struct net *net = seq_file_net(f);
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
++*ppos;
return (v == SEQ_START_TOKEN) ?
- sk_head(&pfkey_table) :
+ sk_head(&net_pfkey->table) :
sk_next((struct sock *)v);
}
@@ -3682,38 +3715,39 @@ static struct seq_operations pfkey_seq_ops = {
static int pfkey_seq_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &pfkey_seq_ops);
+ return seq_open_net(inode, file, &pfkey_seq_ops,
+ sizeof(struct seq_net_private));
}
static struct file_operations pfkey_proc_ops = {
.open = pfkey_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
-static int pfkey_init_proc(void)
+static int __net_init pfkey_init_proc(struct net *net)
{
struct proc_dir_entry *e;
- e = proc_net_fops_create(&init_net, "pfkey", 0, &pfkey_proc_ops);
+ e = proc_net_fops_create(net, "pfkey", 0, &pfkey_proc_ops);
if (e == NULL)
return -ENOMEM;
return 0;
}
-static void pfkey_exit_proc(void)
+static void pfkey_exit_proc(struct net *net)
{
- proc_net_remove(&init_net, "pfkey");
+ proc_net_remove(net, "pfkey");
}
#else
-static inline int pfkey_init_proc(void)
+static int __net_init pfkey_init_proc(struct net *net)
{
return 0;
}
-static inline void pfkey_exit_proc(void)
+static void pfkey_exit_proc(struct net *net)
{
}
#endif
@@ -3729,10 +3763,51 @@ static struct xfrm_mgr pfkeyv2_mgr =
.migrate = pfkey_send_migrate,
};
+static int __net_init pfkey_net_init(struct net *net)
+{
+ struct netns_pfkey *net_pfkey;
+ int rv;
+
+ net_pfkey = kmalloc(sizeof(struct netns_pfkey), GFP_KERNEL);
+ if (!net_pfkey) {
+ rv = -ENOMEM;
+ goto out_kmalloc;
+ }
+ INIT_HLIST_HEAD(&net_pfkey->table);
+ atomic_set(&net_pfkey->socks_nr, 0);
+ rv = net_assign_generic(net, pfkey_net_id, net_pfkey);
+ if (rv < 0)
+ goto out_assign;
+ rv = pfkey_init_proc(net);
+ if (rv < 0)
+ goto out_proc;
+ return 0;
+
+out_proc:
+out_assign:
+ kfree(net_pfkey);
+out_kmalloc:
+ return rv;
+}
+
+static void __net_exit pfkey_net_exit(struct net *net)
+{
+ struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+
+ pfkey_exit_proc(net);
+ BUG_ON(!hlist_empty(&net_pfkey->table));
+ kfree(net_pfkey);
+}
+
+static struct pernet_operations pfkey_net_ops = {
+ .init = pfkey_net_init,
+ .exit = pfkey_net_exit,
+};
+
static void __exit ipsec_pfkey_exit(void)
{
+ unregister_pernet_gen_subsys(pfkey_net_id, &pfkey_net_ops);
xfrm_unregister_km(&pfkeyv2_mgr);
- pfkey_exit_proc();
sock_unregister(PF_KEY);
proto_unregister(&key_proto);
}
@@ -3747,16 +3822,16 @@ static int __init ipsec_pfkey_init(void)
err = sock_register(&pfkey_family_ops);
if (err != 0)
goto out_unregister_key_proto;
- err = pfkey_init_proc();
+ err = xfrm_register_km(&pfkeyv2_mgr);
if (err != 0)
goto out_sock_unregister;
- err = xfrm_register_km(&pfkeyv2_mgr);
+ err = register_pernet_gen_subsys(&pfkey_net_id, &pfkey_net_ops);
if (err != 0)
- goto out_remove_proc_entry;
+ goto out_xfrm_unregister_km;
out:
return err;
-out_remove_proc_entry:
- pfkey_exit_proc();
+out_xfrm_unregister_km:
+ xfrm_unregister_km(&pfkeyv2_mgr);
out_sock_unregister:
sock_unregister(PF_KEY);
out_unregister_key_proto:
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index 5bcc452a247f..56fd85ab358e 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -103,7 +103,6 @@ static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
* llc_ui_send_data - send data via reliable llc2 connection
* @sk: Connection the socket is using.
* @skb: Data the user wishes to send.
- * @addr: Source and destination fields provided by the user.
* @noblock: can we block waiting for data?
*
* Send data via reliable llc2 connection.
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index 48212c0a961c..b58bd7c6cdf8 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -27,8 +27,7 @@
static void llc_ui_format_mac(struct seq_file *seq, u8 *addr)
{
- DECLARE_MAC_BUF(mac);
- seq_printf(seq, "%s", print_mac(mac, addr));
+ seq_printf(seq, "%pM", addr);
}
static struct sock *llc_get_sk_idx(loff_t pos)
diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c
index 5bef1dcf18e3..57b9304d444c 100644
--- a/net/llc/sysctl_net_llc.c
+++ b/net/llc/sysctl_net_llc.c
@@ -20,8 +20,8 @@ static struct ctl_table llc2_timeout_table[] = {
.data = &sysctl_llc2_ack_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_LLC2_BUSY_TIMEOUT,
@@ -29,8 +29,8 @@ static struct ctl_table llc2_timeout_table[] = {
.data = &sysctl_llc2_busy_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_LLC2_P_TIMEOUT,
@@ -38,8 +38,8 @@ static struct ctl_table llc2_timeout_table[] = {
.data = &sysctl_llc2_p_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{
.ctl_name = NET_LLC2_REJ_TIMEOUT,
@@ -47,8 +47,8 @@ static struct ctl_table llc2_timeout_table[] = {
.data = &sysctl_llc2_rej_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{ 0 },
};
@@ -60,8 +60,8 @@ static struct ctl_table llc_station_table[] = {
.data = &sysctl_llc_station_ack_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
- .strategy = &sysctl_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
+ .strategy = sysctl_jiffies,
},
{ 0 },
};
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 7f710a27e91c..60c16162474c 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -16,20 +16,20 @@ menu "Rate control algorithm selection"
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
- default y
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
rate.
config MAC80211_RC_MINSTREL
- bool "Minstrel"
+ bool "Minstrel" if EMBEDDED
+ default y
---help---
This option enables the 'minstrel' TX rate control algorithm
choice
prompt "Default rate control algorithm"
- default MAC80211_RC_DEFAULT_PID
+ default MAC80211_RC_DEFAULT_MINSTREL
---help---
This option selects the default rate control algorithm
mac80211 will use. Note that this default can still be
@@ -55,8 +55,8 @@ endchoice
config MAC80211_RC_DEFAULT
string
- default "pid" if MAC80211_RC_DEFAULT_PID
default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
+ default "pid" if MAC80211_RC_DEFAULT_PID
default ""
endmenu
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 31cfd1f89a72..7d4971aa443f 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -46,3 +46,5 @@ rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 855126a3039d..9d4e4d846ec1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -17,13 +17,6 @@
#include "rate.h"
#include "mesh.h"
-struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- return &local->hw;
-}
-EXPORT_SYMBOL(wiphy_to_hw);
-
static bool nl80211_type_check(enum nl80211_iftype type)
{
switch (type) {
@@ -33,6 +26,8 @@ static bool nl80211_type_check(enum nl80211_iftype type)
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
#endif
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
return true;
default:
@@ -315,12 +310,35 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
- STATION_INFO_TX_BYTES;
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
+ if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = (s8)sta->last_signal;
+ }
+
+ sinfo->txrate.flags = 0;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ sta->local->hw.conf.channel->band];
+ sinfo->txrate.legacy =
+ sband->bitrates[sta->last_tx_rate.idx].bitrate;
+ } else
+ sinfo->txrate.mcs = sta->last_tx_rate.idx;
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
@@ -401,8 +419,10 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
*/
if (params->interval) {
sdata->local->hw.conf.beacon_int = params->interval;
- if (ieee80211_hw_config(sdata->local))
- return -EINVAL;
+ err = ieee80211_hw_config(sdata->local,
+ IEEE80211_CONF_CHANGE_BEACON_INTERVAL);
+ if (err < 0)
+ return err;
/*
* We updated some parameter so if below bails out
* it's not an error.
@@ -589,6 +609,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
+
/*
* FIXME: updating the flags is racy when this function is
* called from ieee80211_change_station(), this will
@@ -629,7 +651,6 @@ static void sta_apply_parameters(struct ieee80211_local *local,
if (params->supported_rates) {
rates = 0;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
for (i = 0; i < params->supported_rates_len; i++) {
int rate = (params->supported_rates[i] & 0x7f) * 5;
@@ -641,10 +662,10 @@ static void sta_apply_parameters(struct ieee80211_local *local,
sta->sta.supp_rates[local->oper_channel->band] = rates;
}
- if (params->ht_capa) {
- ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
- &sta->sta.ht_info);
- }
+ if (params->ht_capa)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ params->ht_capa,
+ &sta->sta.ht_cap);
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
switch (params->plink_action) {
@@ -665,6 +686,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
+ int layer2_update;
/* Prevent a race with changing the rate control algorithm */
if (!netif_running(dev))
@@ -695,17 +717,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta);
+ layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_AP;
+
rcu_read_lock();
err = sta_info_insert(sta);
if (err) {
/* STA has been freed */
+ if (err == -EEXIST && layer2_update) {
+ /* Need to update layer 2 devices on reassociation */
+ sta = sta_info_get(local, mac);
+ if (sta)
+ ieee80211_send_layer2_update(sta);
+ }
rcu_read_unlock();
return err;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_AP)
+ if (layer2_update)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
@@ -957,6 +987,72 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
rcu_read_unlock();
return 0;
}
+
+static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf)
+{
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+ memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
+ return 0;
+}
+
+static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
+{
+ return (mask >> (parm-1)) & 0x1;
+}
+
+static int ieee80211_set_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct mesh_config *nconf, u32 mask)
+{
+ struct mesh_config *conf;
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ /* Set the config options which we are interested in setting */
+ conf = &(sdata->u.mesh.mshcfg);
+ if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
+ conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
+ conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
+ conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
+ conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
+ conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
+ conf->dot11MeshTTL = nconf->dot11MeshTTL;
+ if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+ conf->auto_open_plinks = nconf->auto_open_plinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
+ conf->dot11MeshHWMPmaxPREQretries =
+ nconf->dot11MeshHWMPmaxPREQretries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
+ conf->path_refresh_time = nconf->path_refresh_time;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
+ conf->min_discovery_timeout = nconf->min_discovery_timeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
+ conf->dot11MeshHWMPactivePathTimeout =
+ nconf->dot11MeshHWMPactivePathTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
+ conf->dot11MeshHWMPpreqMinInterval =
+ nconf->dot11MeshHWMPpreqMinInterval;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ mask))
+ conf->dot11MeshHWMPnetDiameterTraversalTime =
+ nconf->dot11MeshHWMPnetDiameterTraversalTime;
+ return 0;
+}
+
#endif
static int ieee80211_change_bss(struct wiphy *wiphy,
@@ -972,25 +1068,79 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
return -EINVAL;
if (params->use_cts_prot >= 0) {
- sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+ sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (params->use_short_preamble >= 0) {
- sdata->bss_conf.use_short_preamble =
+ sdata->vif.bss_conf.use_short_preamble =
params->use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
if (params->use_short_slot_time >= 0) {
- sdata->bss_conf.use_short_slot =
+ sdata->vif.bss_conf.use_short_slot =
params->use_short_slot_time;
changed |= BSS_CHANGED_ERP_SLOT;
}
+ if (params->basic_rates) {
+ int i, j;
+ u32 rates = 0;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *sband =
+ wiphy->bands[local->oper_channel->band];
+
+ for (i = 0; i < params->basic_rates_len; i++) {
+ int rate = (params->basic_rates[i] & 0x7f) * 5;
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate)
+ rates |= BIT(j);
+ }
+ }
+ sdata->vif.bss_conf.basic_rates = rates;
+ changed |= BSS_CHANGED_BASIC_RATES;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
}
+static int ieee80211_set_txq_params(struct wiphy *wiphy,
+ struct ieee80211_txq_params *params)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_tx_queue_params p;
+
+ if (!local->ops->conf_tx)
+ return -EOPNOTSUPP;
+
+ memset(&p, 0, sizeof(p));
+ p.aifs = params->aifs;
+ p.cw_max = params->cwmax;
+ p.cw_min = params->cwmin;
+ p.txop = params->txop;
+ if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) {
+ printk(KERN_DEBUG "%s: failed to set TX queue "
+ "parameters for queue %d\n", local->mdev->name,
+ params->queue);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ieee80211_set_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ local->oper_channel = chan;
+ local->oper_channel_type = channel_type;
+
+ return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1013,6 +1163,10 @@ struct cfg80211_ops mac80211_config_ops = {
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .set_mesh_params = ieee80211_set_mesh_params,
+ .get_mesh_params = ieee80211_get_mesh_params,
#endif
.change_bss = ieee80211_change_bss,
+ .set_txq_params = ieee80211_set_txq_params,
+ .set_channel = ieee80211_set_channel,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 24ce54463310..2697a2fe608f 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -47,18 +47,14 @@ static const struct file_operations name## _ops = { \
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
local->hw.conf.channel->center_freq);
-DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d",
- local->hw.conf.antenna_sel_tx);
-DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d",
- local->hw.conf.antenna_sel_rx);
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
local->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
local->fragmentation_threshold);
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
- local->short_retry_limit);
+ local->hw.conf.short_frame_max_tx_count);
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
- local->long_retry_limit);
+ local->hw.conf.long_frame_max_tx_count);
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
local->total_ps_buffered);
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x",
@@ -202,8 +198,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
local->debugfs.keys = debugfs_create_dir("keys", phyd);
DEBUGFS_ADD(frequency);
- DEBUGFS_ADD(antenna_sel_tx);
- DEBUGFS_ADD(antenna_sel_rx);
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
@@ -258,8 +252,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
void debugfs_hw_del(struct ieee80211_local *local)
{
DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(antenna_sel_tx);
- DEBUGFS_DEL(antenna_sel_rx);
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index a3294d109322..6424ac565ae0 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -188,7 +188,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
{
static int keycount;
char buf[50];
- DECLARE_MAC_BUF(mac);
struct sta_info *sta;
if (!key->local->debugfs.keys)
@@ -206,8 +205,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
rcu_read_lock();
sta = rcu_dereference(key->sta);
if (sta)
- sprintf(buf, "../../stations/%s",
- print_mac(mac, sta->sta.addr));
+ sprintf(buf, "../../stations/%pM", sta->sta.addr);
rcu_read_unlock();
/* using sta as a boolean is fine outside RCU lock */
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 2ad504fc3414..c54219301724 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -41,29 +41,6 @@ static ssize_t ieee80211_if_read(
return ret;
}
-#ifdef CONFIG_MAC80211_MESH
-static ssize_t ieee80211_if_write(
- struct ieee80211_sub_if_data *sdata,
- char const __user *userbuf,
- size_t count, loff_t *ppos,
- int (*format)(struct ieee80211_sub_if_data *, char *))
-{
- char buf[10];
- int buf_size;
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, userbuf, buf_size))
- return count;
- read_lock(&dev_base_lock);
- if (sdata->dev->reg_state == NETREG_REGISTERED)
- (*format)(sdata, buf);
- read_unlock(&dev_base_lock);
-
- return count;
-}
-#endif
-
#define IEEE80211_IF_FMT(name, field, format_string) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
@@ -71,19 +48,6 @@ static ssize_t ieee80211_if_fmt_##name( \
{ \
return scnprintf(buf, buflen, format_string, sdata->field); \
}
-#define IEEE80211_IF_WFMT(name, field, type) \
-static int ieee80211_if_wfmt_##name( \
- struct ieee80211_sub_if_data *sdata, char *buf) \
-{ \
- unsigned long tmp; \
- char *endp; \
- \
- tmp = simple_strtoul(buf, &endp, 0); \
- if ((endp == buf) || ((type)tmp != tmp)) \
- return -EINVAL; \
- sdata->field = tmp; \
- return 0; \
-}
#define IEEE80211_IF_FMT_DEC(name, field) \
IEEE80211_IF_FMT(name, field, "%d\n")
#define IEEE80211_IF_FMT_HEX(name, field) \
@@ -104,8 +68,7 @@ static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
int buflen) \
{ \
- DECLARE_MAC_BUF(mac); \
- return scnprintf(buf, buflen, "%s\n", print_mac(mac, sdata->field));\
+ return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
#define __IEEE80211_IF_FILE(name) \
@@ -126,34 +89,6 @@ static const struct file_operations name##_ops = { \
IEEE80211_IF_FMT_##format(name, field) \
__IEEE80211_IF_FILE(name)
-#define __IEEE80211_IF_WFILE(name) \
-static ssize_t ieee80211_if_read_##name(struct file *file, \
- char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_read(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_fmt_##name); \
-} \
-static ssize_t ieee80211_if_write_##name(struct file *file, \
- const char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_write(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_wfmt_##name); \
-} \
-static const struct file_operations name##_ops = { \
- .read = ieee80211_if_read_##name, \
- .write = ieee80211_if_write_##name, \
- .open = mac80211_open_file_generic, \
-}
-
-#define IEEE80211_IF_WFILE(name, field, format, type) \
- IEEE80211_IF_FMT_##format(name, field) \
- IEEE80211_IF_WFMT(name, field, type) \
- __IEEE80211_IF_WFILE(name)
-
/* common attributes */
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
@@ -184,7 +119,7 @@ static ssize_t ieee80211_if_fmt_flags(
sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
- sdata->bss_conf.use_cts_prot ? "CTS prot\n" : "");
+ sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
}
__IEEE80211_IF_FILE(flags);
@@ -212,30 +147,30 @@ IEEE80211_IF_FILE(dropped_frames_no_route,
IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
/* Mesh parameters */
-IEEE80211_IF_WFILE(dot11MeshMaxRetries,
- u.mesh.mshcfg.dot11MeshMaxRetries, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
- u.mesh.mshcfg.dot11MeshRetryTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
- u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
- u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC, u8);
-IEEE80211_IF_WFILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
- u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
- u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
-IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
- u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
- u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
- u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
-IEEE80211_IF_WFILE(path_refresh_time,
- u.mesh.mshcfg.path_refresh_time, DEC, u32);
-IEEE80211_IF_WFILE(min_discovery_timeout,
- u.mesh.mshcfg.min_discovery_timeout, DEC, u16);
+IEEE80211_IF_FILE(dot11MeshMaxRetries,
+ u.mesh.mshcfg.dot11MeshMaxRetries, DEC);
+IEEE80211_IF_FILE(dot11MeshRetryTimeout,
+ u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshConfirmTimeout,
+ u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHoldingTimeout,
+ u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC);
+IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);
+IEEE80211_IF_FILE(dot11MeshMaxPeerLinks,
+ u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout,
+ u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval,
+ u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime,
+ u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries,
+ u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);
+IEEE80211_IF_FILE(path_refresh_time,
+ u.mesh.mshcfg.path_refresh_time, DEC);
+IEEE80211_IF_FILE(min_discovery_timeout,
+ u.mesh.mshcfg.min_discovery_timeout, DEC);
#endif
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index b85c4f27b361..a2fbe0131312 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \
.open = mac80211_open_file_generic, \
}
-#define STA_OPS_WR(name) \
-static const struct file_operations sta_ ##name## _ops = { \
- .read = sta_##name##_read, \
- .write = sta_##name##_write, \
- .open = mac80211_open_file_generic, \
-}
-
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
@@ -144,7 +137,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_rx[i]?
+ sta->ampdu_mlme.tid_state_rx[i] ?
sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :");
@@ -155,84 +148,20 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->ssn : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
-
-static ssize_t sta_agg_status_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct sta_info *sta = file->private_data;
- struct ieee80211_local *local = sta->sdata->local;
- struct ieee80211_hw *hw = &local->hw;
- u8 *da = sta->sta.addr;
- static int tid_static_tx[16] = {0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
- static int tid_static_rx[16] = {1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1};
- char *endp;
- char buf[32];
- int buf_size, rs;
- unsigned int tid_num;
- char state[4];
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- tid_num = simple_strtoul(buf, &endp, 0);
- if (endp == buf)
- return -EINVAL;
-
- if ((tid_num >= 100) && (tid_num <= 115)) {
- /* toggle Rx aggregation command */
- tid_num = tid_num - 100;
- if (tid_static_rx[tid_num] == 1) {
- strcpy(state, "off");
- ieee80211_sta_stop_rx_ba_session(sta->sdata, da, tid_num, 0,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
- sta->ampdu_mlme.tid_state_rx[tid_num] |=
- HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 0;
- } else {
- strcpy(state, "on ");
- sta->ampdu_mlme.tid_state_rx[tid_num] &=
- ~HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 1;
- }
- printk(KERN_DEBUG "debugfs - try switching tid %u %s\n",
- tid_num, state);
- } else if ((tid_num >= 0) && (tid_num <= 15)) {
- /* toggle Tx aggregation command */
- if (tid_static_tx[tid_num] == 0) {
- strcpy(state, "on ");
- rs = ieee80211_start_tx_ba_session(hw, da, tid_num);
- if (rs == 0)
- tid_static_tx[tid_num] = 1;
- } else {
- strcpy(state, "off");
- rs = ieee80211_stop_tx_ba_session(hw, da, tid_num, 1);
- if (rs == 0)
- tid_static_tx[tid_num] = 0;
- }
- printk(KERN_DEBUG "debugfs - switching tid %u %s, return=%d\n",
- tid_num, state, rs);
- }
-
- return count;
-}
-STA_OPS_WR(agg_status);
+STA_OPS(agg_status);
#define DEBUGFS_ADD(name) \
sta->debugfs.name = debugfs_create_file(#name, 0400, \
@@ -246,15 +175,14 @@ STA_OPS_WR(agg_status);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
struct dentry *stations_dir = sta->local->debugfs.stations;
- DECLARE_MAC_BUF(mbuf);
- u8 *mac;
+ u8 mac[3*ETH_ALEN];
sta->debugfs.add_has_run = true;
if (!stations_dir)
return;
- mac = print_mac(mbuf, sta->sta.addr);
+ snprintf(mac, sizeof(mac), "%pM", sta->sta.addr);
/*
* This might fail due to a race condition:
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
index 8de60de70bc9..0d95561c0ee0 100644
--- a/net/mac80211/event.c
+++ b/net/mac80211/event.c
@@ -21,14 +21,13 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
{
union iwreq_data wrqu;
char *buf = kmalloc(128, GFP_ATOMIC);
- DECLARE_MAC_BUF(mac);
if (buf) {
/* TODO: needed parameters: count, key type, TSC */
sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=%s)",
+ "keyid=%d %scast addr=%pM)",
keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
- print_mac(mac, hdr->addr2));
+ hdr->addr2);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index dc7d9a3d70d5..5f510a13b9f0 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -20,50 +20,138 @@
#include "sta_info.h"
#include "wme.h"
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info)
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap)
{
+ u8 ampdu_info, tx_mcs_set_cap;
+ int i, max_tx_streams;
- if (ht_info == NULL)
- return -EINVAL;
+ BUG_ON(!ht_cap);
+
+ memset(ht_cap, 0, sizeof(*ht_cap));
+
+ if (!ht_cap_ie)
+ return;
+
+ ht_cap->ht_supported = true;
- memset(ht_info, 0, sizeof(*ht_info));
+ ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
+ ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
+ ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
- if (ht_cap_ie) {
- u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+ ampdu_info = ht_cap_ie->ampdu_params_info;
+ ht_cap->ampdu_factor =
+ ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
+ ht_cap->ampdu_density =
+ (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
- ht_info->ht_supported = 1;
- ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
- ht_info->ampdu_factor =
- ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
- ht_info->ampdu_density =
- (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
- memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
- } else
- ht_info->ht_supported = 0;
+ /* own MCS TX capabilities */
+ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
- return 0;
+ /* can we TX with MCS rates? */
+ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
+ return;
+
+ /* Counting from 0, therefore +1 */
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
+ max_tx_streams =
+ ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+ else
+ max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
+
+ /*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+ for (i = 0; i < max_tx_streams; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
+ for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
+ i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] &
+ ht_cap_ie->mcs.rx_mask[i];
+
+ /* handle MCS rate 32 too */
+ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+ ht_cap->mcs.rx_mask[32/8] |= 1;
}
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info)
+/*
+ * ieee80211_enable_ht should be called only after the operating band
+ * has been determined as ht configuration depends on the hw's
+ * HT abilities for a specific band.
+ */
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags)
{
- if (bss_info == NULL)
- return -EINVAL;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_ht_conf ht;
+ u32 changed = 0;
+ bool enable_ht = true, ht_changed;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ memset(&ht, 0, sizeof(ht));
+
+ /* HT is not supported */
+ if (!sband->ht_cap.ht_supported)
+ enable_ht = false;
+
+ /* check that channel matches the right operating channel */
+ if (local->hw.conf.channel->center_freq !=
+ ieee80211_channel_to_frequency(hti->control_chan))
+ enable_ht = false;
+
+ if (enable_ht) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
+ switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ ht_changed = local->hw.conf.ht.enabled != enable_ht ||
+ channel_type != local->hw.conf.ht.channel_type;
+
+ local->oper_channel_type = channel_type;
+ local->hw.conf.ht.enabled = enable_ht;
- memset(bss_info, 0, sizeof(*bss_info));
+ if (ht_changed)
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT);
- if (ht_add_info_ie) {
- u16 op_mode;
- op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+ /* disable HT */
+ if (!enable_ht)
+ return 0;
- bss_info->primary_channel = ht_add_info_ie->control_chan;
- bss_info->bss_cap = ht_add_info_ie->ht_param;
- bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+ ht.operation_mode = le16_to_cpu(hti->operation_mode);
+
+ /* if bss configuration changed store the new one */
+ if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
+ changed |= BSS_CHANGED_HT;
+ sdata->vif.bss_conf.ht = ht;
}
- return 0;
+ return changed;
}
static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
@@ -241,7 +329,6 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
int ret, i;
- DECLARE_MAC_BUF(mac);
rcu_read_lock();
@@ -269,8 +356,8 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
BUG_ON(!local->ops->ampdu_action);
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
@@ -383,14 +470,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
u16 start_seq_num;
u8 *state;
int ret;
- DECLARE_MAC_BUF(mac);
- if (tid >= STA_TID_NUM)
+ if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
@@ -442,17 +528,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
- /* create a new queue for this aggregation */
- ret = ieee80211_ht_agg_queue_add(local, sta, tid);
+ if (hw->ampdu_queues) {
+ /* create a new queue for this aggregation */
+ ret = ieee80211_ht_agg_queue_add(local, sta, tid);
- /* case no queue is available to aggregation
- * don't switch to aggregation */
- if (ret) {
+ /* case no queue is available to aggregation
+ * don't switch to aggregation */
+ if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - queue unavailable for"
- " tid %d\n", tid);
+ printk(KERN_DEBUG "BA request denied - "
+ "queue unavailable for tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto err_unlock_queue;
+ goto err_unlock_queue;
+ }
}
sdata = sta->sdata;
@@ -471,7 +559,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
- ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
+ if (hw->ampdu_queues)
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
@@ -481,7 +570,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
/* Will put all the packets in the new SW queue */
- ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
@@ -524,7 +614,6 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
struct sta_info *sta;
u8 *state;
int ret = 0;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
@@ -546,11 +635,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
@@ -563,7 +653,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
if (ret) {
WARN_ON(ret != -EBUSY);
*state = HT_AGG_STATE_OPERATIONAL;
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
goto stop_BA_exit;
}
@@ -579,7 +670,6 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -594,8 +684,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
if (!sta) {
rcu_read_unlock();
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
return;
}
@@ -621,7 +710,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
@@ -634,7 +724,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
struct sta_info *sta;
u8 *state;
int agg_queue;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -645,16 +734,15 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
rcu_read_unlock();
return;
@@ -677,16 +765,18 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
- agg_queue = sta->tid_to_tx_q[tid];
-
- ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
-
- /* We just requeued the all the frames that were in the
- * removed queue, and since we might miss a softirq we do
- * netif_schedule_queue. ieee80211_wake_queue is not used
- * here as this queue is not necessarily stopped
- */
- netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
+ if (hw->ampdu_queues) {
+ agg_queue = sta->tid_to_tx_q[tid];
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+
+ /* We just requeued the all the frames that were in the
+ * removed queue, and since we might miss a softirq we do
+ * netif_schedule_queue. ieee80211_wake_queue is not used
+ * here as this queue is not necessarily stopped
+ */
+ netif_schedule_queue(netdev_get_tx_queue(local->mdev,
+ agg_queue));
+ }
spin_lock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
@@ -783,7 +873,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
u8 dialog_token;
int ret = -EOPNOTSUPP;
- DECLARE_MAC_BUF(mac);
/* extract session parameters from addba request frame */
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@@ -801,15 +890,16 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
+ /* XXX: check own ht delayed BA capability?? */
if (((ba_policy != 1)
- && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
+ && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA)))
|| (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "AddBA Req with bad params from "
- "%s on tid %u. policy %d, buffer size %d\n",
- print_mac(mac, mgmt->sa), tid, ba_policy,
+ "%pM on tid %u. policy %d, buffer size %d\n",
+ mgmt->sa, tid, ba_policy,
buf_size);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end_no_lock;
@@ -820,7 +910,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
sband = local->hw.wiphy->bands[conf->channel->band];
buf_size = IEEE80211_MIN_AMPDU_BUF;
- buf_size = buf_size << sband->ht_info.ampdu_factor;
+ buf_size = buf_size << sband->ht_cap.ampdu_factor;
}
@@ -831,8 +921,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "unexpected AddBA Req from "
- "%s on tid %u\n",
- print_mac(mac, mgmt->sa), tid);
+ "%pM on tid %u\n",
+ mgmt->sa, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end;
}
@@ -910,7 +1000,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
{
struct ieee80211_hw *hw = &local->hw;
u16 capab;
- u16 tid;
+ u16 tid, start_seq_num;
u8 *state;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
@@ -943,9 +1033,18 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
*state |= HT_ADDBA_RECEIVED_MSK;
sta->ampdu_mlme.addba_req_num[tid] = 0;
- if (*state == HT_AGG_STATE_OPERATIONAL)
+ if (*state == HT_AGG_STATE_OPERATIONAL &&
+ local->hw.ampdu_queues)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (local->ops->ampdu_action) {
+ (void)local->ops->ampdu_action(hw,
+ IEEE80211_AMPDU_TX_RESUME,
+ &sta->sta, tid, &start_seq_num);
+ }
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
spin_unlock_bh(&sta->lock);
} else {
sta->ampdu_mlme.addba_req_num[tid]++;
@@ -964,7 +1063,6 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
u16 tid, params;
u16 initiator;
- DECLARE_MAC_BUF(mac);
params = le16_to_cpu(mgmt->u.action.u.delba.params);
tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
@@ -972,9 +1070,8 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
- printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
- print_mac(mac, mgmt->sa),
- initiator ? "initiator" : "recipient", tid,
+ printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n",
+ mgmt->sa, initiator ? "initiator" : "recipient", tid,
mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 156e42a003ae..f3eec989662b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
#include <net/wireless.h>
#include <net/iw_handler.h>
#include <net/mac80211.h>
@@ -142,7 +143,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
#define IEEE80211_TX_FRAGMENTED BIT(0)
#define IEEE80211_TX_UNICAST BIT(1)
#define IEEE80211_TX_PS_BUFFERED BIT(2)
-#define IEEE80211_TX_PROBE_LAST_FRAG BIT(3)
struct ieee80211_tx_data {
struct sk_buff *skb;
@@ -153,11 +153,6 @@ struct ieee80211_tx_data {
struct ieee80211_key *key;
struct ieee80211_channel *channel;
- s8 rate_idx;
- /* use this rate (if set) for last fragment; rate can
- * be set to lower rate for the first fragments, e.g.,
- * when using CTS protection with IEEE 802.11g. */
- s8 last_frag_rate_idx;
/* Extra fragments (in addition to the first fragment
* in skb) */
@@ -192,7 +187,6 @@ struct ieee80211_rx_data {
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
- u16 ethertype;
unsigned int flags;
int sent_ps_buffered;
int queue;
@@ -203,9 +197,7 @@ struct ieee80211_rx_data {
struct ieee80211_tx_stored_packet {
struct sk_buff *skb;
struct sk_buff **extra_frag;
- s8 last_frag_rate_idx;
int num_extra_frag;
- bool last_frag_rate_ctrl_probe;
};
struct beacon_data {
@@ -219,9 +211,6 @@ struct ieee80211_if_ap {
struct list_head vlans;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- size_t ssid_len;
-
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
@@ -255,26 +244,6 @@ struct mesh_preq_queue {
u8 flags;
};
-struct mesh_config {
- /* Timeouts in ms */
- /* Mesh plink management parameters */
- u16 dot11MeshRetryTimeout;
- u16 dot11MeshConfirmTimeout;
- u16 dot11MeshHoldingTimeout;
- u16 dot11MeshMaxPeerLinks;
- u8 dot11MeshMaxRetries;
- u8 dot11MeshTTL;
- bool auto_open_plinks;
- /* HWMP parameters */
- u8 dot11MeshHWMPmaxPREQretries;
- u32 path_refresh_time;
- u16 min_discovery_timeout;
- u32 dot11MeshHWMPactivePathTimeout;
- u16 dot11MeshHWMPpreqMinInterval;
- u16 dot11MeshHWMPnetDiameterTraversalTime;
-};
-
-
/* flags used in struct ieee80211_if_sta.flags */
#define IEEE80211_STA_SSID_SET BIT(0)
#define IEEE80211_STA_BSSID_SET BIT(1)
@@ -438,8 +407,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_key *keys[NUM_DEFAULT_KEYS];
struct ieee80211_key *default_key;
- /* BSS configuration for this interface. */
- struct ieee80211_bss_conf bss_conf;
+ u16 sequence_number;
/*
* AP this belongs to: self in AP mode and
@@ -570,6 +538,11 @@ enum {
IEEE80211_ADDBA_MSG = 4,
};
+enum queue_stop_reason {
+ IEEE80211_QUEUE_STOP_REASON_DRIVER,
+ IEEE80211_QUEUE_STOP_REASON_PS,
+};
+
/* maximum number of hardware queues we support. */
#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
@@ -586,7 +559,8 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
-
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ spinlock_t queue_stop_reason_lock;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -633,8 +607,6 @@ struct ieee80211_local {
int rts_threshold;
int fragmentation_threshold;
- int short_retry_limit; /* dot11ShortRetryLimit */
- int long_retry_limit; /* dot11LongRetryLimit */
struct crypto_blkcipher *wep_tx_tfm;
struct crypto_blkcipher *wep_rx_tfm;
@@ -659,6 +631,7 @@ struct ieee80211_local {
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel;
+ enum nl80211_channel_type oper_channel_type;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
struct list_head bss_list;
@@ -722,13 +695,17 @@ struct ieee80211_local {
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
+ bool powersave;
+ int dynamic_ps_timeout;
+ struct work_struct dynamic_ps_enable_work;
+ struct work_struct dynamic_ps_disable_work;
+ struct timer_list dynamic_ps_timer;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *rcdir;
struct dentry *rcname;
struct dentry *frequency;
- struct dentry *antenna_sel_tx;
- struct dentry *antenna_sel_rx;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
@@ -817,7 +794,7 @@ struct ieee802_11_elems {
u8 *wmm_info;
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
- struct ieee80211_ht_addt_info *ht_info_elem;
+ struct ieee80211_ht_info *ht_info_elem;
u8 *mesh_config;
u8 *mesh_id;
u8 *peer_link;
@@ -869,11 +846,6 @@ static inline struct ieee80211_hw *local_to_hw(
return &local->hw;
}
-struct sta_attribute {
- struct attribute attr;
- ssize_t (*show)(const struct sta_info *, char *buf);
- ssize_t (*store)(struct sta_info *, const char *buf, size_t count);
-};
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
@@ -882,12 +854,9 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
}
-int ieee80211_hw_config(struct ieee80211_local *local);
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed);
void ieee80211_configure_filter(struct ieee80211_local *local);
@@ -906,8 +875,7 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates);
+ u8 *bssid, u8 *addr, u64 supp_rates);
int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
@@ -968,11 +936,12 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
/* HT */
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info);
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap);
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags);
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
@@ -1014,6 +983,15 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
enum ieee80211_band band);
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_timer(unsigned long data);
+
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8336fee68d3e..5abbc3f07dd6 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -65,7 +65,7 @@ static int ieee80211_open(struct net_device *dev)
struct ieee80211_if_init_conf conf;
u32 changed = 0;
int res;
- bool need_hw_reconfig = 0;
+ u32 hw_reconf_flags = 0;
u8 null_addr[ETH_ALEN] = {0};
/* fail early if user set an invalid address */
@@ -152,7 +152,8 @@ static int ieee80211_open(struct net_device *dev)
res = local->ops->start(local_to_hw(local));
if (res)
goto err_del_bss;
- need_hw_reconfig = 1;
+ /* we're brought up, everything changes */
+ hw_reconf_flags = ~0;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
@@ -198,8 +199,10 @@ static int ieee80211_open(struct net_device *dev)
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
- if (local->monitors == 1)
+ if (local->monitors == 1) {
local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail++;
@@ -226,8 +229,14 @@ static int ieee80211_open(struct net_device *dev)
if (res)
goto err_stop;
- if (ieee80211_vif_is_mesh(&sdata->vif))
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ local->fif_other_bss++;
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_start_mesh(sdata);
+ }
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_enable_keys(sdata);
@@ -279,8 +288,8 @@ static int ieee80211_open(struct net_device *dev)
atomic_inc(&local->iff_promiscs);
local->open_count++;
- if (need_hw_reconfig) {
- ieee80211_hw_config(local);
+ if (hw_reconf_flags) {
+ ieee80211_hw_config(local, hw_reconf_flags);
/*
* set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
@@ -322,6 +331,7 @@ static int ieee80211_stop(struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ u32 hw_reconf_flags = 0;
/*
* Stop TX on this interface first.
@@ -405,8 +415,10 @@ static int ieee80211_stop(struct net_device *dev)
}
local->monitors--;
- if (local->monitors == 0)
+ if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail--;
@@ -423,7 +435,11 @@ static int ieee80211_stop(struct net_device *dev)
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- sdata->u.sta.state = IEEE80211_STA_MLME_DISABLED;
+ /* Announce that we are leaving the network. */
+ if (sdata->u.sta.state != IEEE80211_STA_MLME_DISABLED)
+ ieee80211_sta_deauthenticate(sdata,
+ WLAN_REASON_DEAUTH_LEAVING);
+
memset(sdata->u.sta.bssid, 0, ETH_ALEN);
del_timer_sync(&sdata->u.sta.timer);
/*
@@ -450,8 +466,15 @@ static int ieee80211_stop(struct net_device *dev)
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
if (ieee80211_vif_is_mesh(&sdata->vif)) {
- /* allmulti is always set on mesh ifaces */
+ /* other_bss and allmulti are always set on mesh
+ * ifaces */
+ local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_stop_mesh(sdata);
}
/* fall through */
@@ -504,8 +527,15 @@ static int ieee80211_stop(struct net_device *dev)
tasklet_disable(&local->tx_pending_tasklet);
tasklet_disable(&local->tasklet);
+
+ /* no reconfiguring after stop! */
+ hw_reconf_flags = 0;
}
+ /* do after stop to avoid reconfiguring when we stop anyway */
+ if (hw_reconf_flags)
+ ieee80211_hw_config(local, hw_reconf_flags);
+
return 0;
}
@@ -668,6 +698,10 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == sdata->vif.type)
return 0;
+ /* Setting ad-hoc mode on non-IBSS channel is not supported. */
+ if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)
+ return -EOPNOTSUPP;
+
/*
* We could, here, on changes between IBSS/STA/MESH modes,
* invoke an MLME function instead that disassociates etc.
@@ -682,7 +716,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
ieee80211_setup_sdata(sdata, type);
/* reset some values that shouldn't be kept across type changes */
- sdata->bss_conf.basic_rates =
+ sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
sdata->local->hw.conf.channel->band);
sdata->drop_unencrypted = 0;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index a5b06fe71980..999f7aa42326 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -132,7 +132,6 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -154,16 +153,15 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
printk(KERN_ERR "mac80211-%s: failed to set key "
- "(%d, %s) to hardware (%d)\n",
+ "(%d, %pM) to hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -186,9 +184,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
if (ret)
printk(KERN_ERR "mac80211-%s: failed to remove key "
- "(%d, %s) from hardware (%d)\n",
+ "(%d, %pM) from hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
spin_lock(&todo_lock);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index ae62ad40ad63..24b14363d6e7 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -41,6 +41,8 @@
*/
struct ieee80211_tx_status_rtap_hdr {
struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 padding_for_rate;
__le16 tx_flags;
u8 data_retries;
} __attribute__ ((packed));
@@ -169,19 +171,13 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
conf.changed = changed;
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
conf.bssid = sdata->u.sta.bssid;
- conf.ssid = sdata->u.sta.ssid;
- conf.ssid_len = sdata->u.sta.ssid_len;
- } else if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ else if (sdata->vif.type == NL80211_IFTYPE_AP)
conf.bssid = sdata->dev->dev_addr;
- conf.ssid = sdata->u.ap.ssid;
- conf.ssid_len = sdata->u.ap.ssid_len;
- } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ else if (ieee80211_vif_is_mesh(&sdata->vif)) {
u8 zero[ETH_ALEN] = { 0 };
conf.bssid = zero;
- conf.ssid = zero;
- conf.ssid_len = 0;
} else {
WARN_ON(1);
return -EINVAL;
@@ -190,136 +186,73 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
return -EINVAL;
- if (WARN_ON(!conf.ssid && (changed & IEEE80211_IFCC_SSID)))
- return -EINVAL;
-
return local->ops->config_interface(local_to_hw(local),
&sdata->vif, &conf);
}
-int ieee80211_hw_config(struct ieee80211_local *local)
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
struct ieee80211_channel *chan;
int ret = 0;
+ int power;
+ enum nl80211_channel_type channel_type;
+
+ might_sleep();
- if (local->sw_scanning)
+ if (local->sw_scanning) {
chan = local->scan_channel;
- else
+ channel_type = NL80211_CHAN_NO_HT;
+ } else {
chan = local->oper_channel;
+ channel_type = local->oper_channel_type;
+ }
- local->hw.conf.channel = chan;
+ if (chan != local->hw.conf.channel ||
+ channel_type != local->hw.conf.ht.channel_type) {
+ local->hw.conf.channel = chan;
+ local->hw.conf.ht.channel_type = channel_type;
+ switch (channel_type) {
+ case NL80211_CHAN_NO_HT:
+ local->hw.conf.ht.enabled = false;
+ break;
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40MINUS:
+ case NL80211_CHAN_HT40PLUS:
+ local->hw.conf.ht.enabled = true;
+ break;
+ }
+ changed |= IEEE80211_CONF_CHANGE_CHANNEL;
+ }
if (!local->hw.conf.power_level)
- local->hw.conf.power_level = chan->max_power;
+ power = chan->max_power;
else
- local->hw.conf.power_level = min(chan->max_power,
- local->hw.conf.power_level);
-
- local->hw.conf.max_antenna_gain = chan->max_antenna_gain;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: HW CONFIG: freq=%d\n",
- wiphy_name(local->hw.wiphy), chan->center_freq);
-#endif
-
- if (local->open_count)
- ret = local->ops->config(local_to_hw(local), &local->hw.conf);
-
- return ret;
-}
-
-/**
- * ieee80211_handle_ht should be used only after legacy configuration
- * has been determined namely band, as ht configuration depends upon
- * the hardware's HT abilities for a _specific_ band.
- */
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap)
-{
- struct ieee80211_conf *conf = &local->hw.conf;
- struct ieee80211_supported_band *sband;
- struct ieee80211_ht_info ht_conf;
- struct ieee80211_ht_bss_info ht_bss_conf;
- u32 changed = 0;
- int i;
- u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS;
- u8 tx_mcs_set_cap;
-
- sband = local->hw.wiphy->bands[conf->channel->band];
-
- memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
- memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
-
- /* HT is not supported */
- if (!sband->ht_info.ht_supported) {
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- goto out;
+ power = min(chan->max_power, local->hw.conf.power_level);
+ if (local->hw.conf.power_level != power) {
+ changed |= IEEE80211_CONF_CHANGE_POWER;
+ local->hw.conf.power_level = power;
}
- /* disable HT */
- if (!enable_ht) {
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
- changed |= BSS_CHANGED_HT;
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- conf->ht_conf.ht_supported = 0;
- goto out;
+ if (changed && local->open_count) {
+ ret = local->ops->config(local_to_hw(local), changed);
+ /*
+ * Goal:
+ * HW reconfiguration should never fail, the driver has told
+ * us what it can support so it should live up to that promise.
+ *
+ * Current status:
+ * rfkill is not integrated with mac80211 and a
+ * configuration command can thus fail if hardware rfkill
+ * is enabled
+ *
+ * FIXME: integrate rfkill with mac80211 and then add this
+ * WARN_ON() back
+ *
+ */
+ /* WARN_ON(ret); */
}
-
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
- changed |= BSS_CHANGED_HT;
-
- conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
- ht_conf.ht_supported = 1;
-
- ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
- ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
- ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
- ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
- ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
- ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
-
- ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
- ht_conf.ampdu_density = req_ht_cap->ampdu_density;
-
- /* Bits 96-100 */
- tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12];
-
- /* configure suppoerted Tx MCS according to requested MCS
- * (based in most cases on Rx capabilities of peer) and self
- * Tx MCS capabilities (as defined by low level driver HW
- * Tx capabilities) */
- if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED))
- goto check_changed;
-
- /* Counting from 0 therfore + 1 */
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF)
- max_tx_streams = ((tx_mcs_set_cap &
- IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1;
-
- for (i = 0; i < max_tx_streams; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM)
- for (i = IEEE80211_SUPP_MCS_SET_UEQM;
- i < IEEE80211_SUPP_MCS_SET_LEN; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
-check_changed:
- /* if bss configuration changed store the new one */
- if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
- memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
- changed |= BSS_CHANGED_HT;
- memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
- memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
- }
-out:
- return changed;
+ return ret;
}
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -336,15 +269,18 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
if (local->ops->bss_info_changed)
local->ops->bss_info_changed(local_to_hw(local),
&sdata->vif,
- &sdata->bss_conf,
+ &sdata->vif.bss_conf,
changed);
}
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
- sdata->bss_conf.use_cts_prot = 0;
- sdata->bss_conf.use_short_preamble = 0;
- return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE;
+ sdata->vif.bss_conf.use_cts_prot = false;
+ sdata->vif.bss_conf.use_short_preamble = false;
+ sdata->vif.bss_conf.use_short_slot = false;
+ return BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_ERP_SLOT;
}
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
@@ -405,7 +341,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
dev_kfree_skb(skb);
break ;
default:
- WARN_ON(1);
+ WARN(1, "mac80211: Packet is of unknown type %d\n",
+ skb->pkt_type);
dev_kfree_skb(skb);
break;
}
@@ -466,8 +403,6 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
struct sta_info *sta,
struct sk_buff *skb)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
sta->tx_filtered_count++;
/*
@@ -514,10 +449,9 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
return;
}
- if (!test_sta_flags(sta, WLAN_STA_PS) &&
- !(info->flags & IEEE80211_TX_CTL_REQUEUE)) {
+ if (!test_sta_flags(sta, WLAN_STA_PS) && !skb->requeue) {
/* Software retry the packet once */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
ieee80211_remove_tx_extra(local, sta->key, skb);
dev_queue_xmit(skb);
return;
@@ -547,13 +481,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_sub_if_data *sdata;
struct net_device *prev_dev = NULL;
struct sta_info *sta;
+ int retry_count = -1, i;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /* the HW cannot have attempted that rate */
+ if (i >= hw->max_rates) {
+ info->status.rates[i].idx = -1;
+ info->status.rates[i].count = 0;
+ }
+
+ retry_count += info->status.rates[i].count;
+ }
+ if (retry_count < 0)
+ retry_count = 0;
rcu_read_lock();
+ sband = local->hw.wiphy->bands[info->band];
+
sta = sta_info_get(local, hdr->addr1);
if (sta) {
- if (info->status.excessive_retries &&
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
test_sta_flags(sta, WLAN_STA_PS)) {
/*
* The STA is in power save mode, so assume
@@ -584,12 +533,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_unlock();
return;
} else {
- if (info->status.excessive_retries)
+ if (!(info->flags & IEEE80211_TX_STAT_ACK))
sta->tx_retry_failed++;
- sta->tx_retry_count += info->status.retry_count;
+ sta->tx_retry_count += retry_count;
}
- sband = local->hw.wiphy->bands[info->band];
rate_control_tx_status(local, sband, sta, skb);
}
@@ -610,9 +558,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11TransmittedFrameCount++;
if (is_multicast_ether_addr(hdr->addr1))
local->dot11MulticastTransmittedFrameCount++;
- if (info->status.retry_count > 0)
+ if (retry_count > 0)
local->dot11RetryCount++;
- if (info->status.retry_count > 1)
+ if (retry_count > 1)
local->dot11MultipleRetryCount++;
}
@@ -656,19 +604,30 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
rthdr->hdr.it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
- (1 << IEEE80211_RADIOTAP_DATA_RETRIES));
+ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) |
+ (1 << IEEE80211_RADIOTAP_RATE));
if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
!is_multicast_ether_addr(hdr->addr1))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) &&
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
+ /*
+ * XXX: Once radiotap gets the bitmap reset thing the vendor
+ * extensions proposal contains, we can actually report
+ * the whole set of tries we did.
+ */
+ if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
- else if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
+ if (info->status.rates[0].idx >= 0 &&
+ !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+ rthdr->rate = sband->bitrates[
+ info->status.rates[0].idx].bitrate / 5;
- rthdr->data_retries = info->status.retry_count;
+ /* for now report the total retry_count */
+ rthdr->data_retries = retry_count;
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
@@ -753,20 +712,30 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
BUG_ON(!ops->configure_filter);
local->ops = ops;
- local->hw.queues = 1; /* default */
-
+ /* set up some defaults */
+ local->hw.queues = 1;
+ local->hw.max_rates = 1;
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
- local->short_retry_limit = 7;
- local->long_retry_limit = 4;
- local->hw.conf.radio_enabled = 1;
+ local->hw.conf.long_frame_max_tx_count = 4;
+ local->hw.conf.short_frame_max_tx_count = 7;
+ local->hw.conf.radio_enabled = true;
INIT_LIST_HEAD(&local->interfaces);
spin_lock_init(&local->key_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
+ INIT_WORK(&local->dynamic_ps_enable_work,
+ ieee80211_dynamic_ps_enable_work);
+ INIT_WORK(&local->dynamic_ps_disable_work,
+ ieee80211_dynamic_ps_disable_work);
+ setup_timer(&local->dynamic_ps_timer,
+ ieee80211_dynamic_ps_timer, (unsigned long) local);
+
sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
@@ -788,7 +757,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- const char *name;
int result;
enum ieee80211_band band;
struct net_device *mdev;
@@ -853,8 +821,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev->header_ops = &ieee80211_header_ops;
mdev->set_multicast_list = ieee80211_master_set_multicast_list;
- name = wiphy_dev(local->hw.wiphy)->driver->name;
- local->hw.workqueue = create_freezeable_workqueue(name);
+ local->hw.workqueue =
+ create_freezeable_workqueue(wiphy_name(local->hw.wiphy));
if (!local->hw.workqueue) {
result = -ENOMEM;
goto fail_workqueue;
@@ -921,12 +889,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->mdev->select_queue = ieee80211_select_queue;
- /* add one default STA interface */
- result = ieee80211_if_add(local, "wlan%d", NULL,
- NL80211_IFTYPE_STATION, NULL);
- if (result)
- printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
- wiphy_name(local->hw.wiphy));
+ /* add one default STA interface if supported */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+ result = ieee80211_if_add(local, "wlan%d", NULL,
+ NL80211_IFTYPE_STATION, NULL);
+ if (result)
+ printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
+ wiphy_name(local->hw.wiphy));
+ }
rtnl_unlock();
@@ -1013,7 +983,7 @@ static int __init ieee80211_init(void)
BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
- IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
ret = rc80211_minstrel_init();
if (ret)
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8013277924f2..82f568e94365 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -238,7 +238,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
pos = skb_put(skb, 21);
*pos++ = WLAN_EID_MESH_CONFIG;
- *pos++ = MESH_CFG_LEN;
+ *pos++ = IEEE80211_MESH_CONFIG_LEN;
/* Version */
*pos++ = 1;
@@ -473,7 +473,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
size_t len,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_local *local= sdata->local;
+ struct ieee80211_local *local = sdata->local;
struct ieee802_11_elems elems;
struct ieee80211_channel *channel;
u64 supp_rates = 0;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index e10471c6ba42..c197ab545e54 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -145,9 +145,6 @@ struct mesh_rmc {
};
-/* Mesh IEs constants */
-#define MESH_CFG_LEN 19
-
/*
* MESH_CFG_COMP_LEN Includes:
* - Active path selection protocol ID.
@@ -157,7 +154,7 @@ struct mesh_rmc {
* Does not include mesh capabilities, which may vary across nodes in the same
* mesh
*/
-#define MESH_CFG_CMP_LEN 17
+#define MESH_CFG_CMP_LEN (IEEE80211_MESH_CONFIG_LEN - 2)
/* Default values, timeouts in ms */
#define MESH_TTL 5
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 501c7831adb4..71fe60961230 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -218,12 +218,16 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
if (sta->fail_avg >= 100)
return MAX_METRIC;
+
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ return MAX_METRIC;
+
err = (sta->fail_avg << ARITH_SHIFT) / 100;
/* bitrate is in units of 100 Kbps, while we need rate in units of
* 1Mbps. This will be corrected on tx_time computation.
*/
- rate = sband->bitrates[sta->last_txrate_idx].bitrate;
+ rate = sband->bitrates[sta->last_tx_rate.idx].bitrate;
tx_time = (device_constant + 10 * test_frame_len / rate);
estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ;
@@ -759,7 +763,6 @@ enddiscovery:
*
* @skb: 802.11 frame to be sent
* @sdata: network subif the frame will be sent through
- * @fwd_frame: true if this frame was originally from a different host
*
* Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is
* found, the function will start a path discovery and queue the frame so it is
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index faac101c0f85..929ba542fd72 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -257,9 +257,6 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/*
* This STA is valid because sta_info_destroy() will
@@ -274,8 +271,8 @@ static void mesh_plink_timer(unsigned long data)
spin_unlock_bh(&sta->lock);
return;
}
- mpl_dbg("Mesh plink timer for %s fired on state %d\n",
- print_mac(mac, sta->sta.addr), sta->plink_state);
+ mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
+ sta->sta.addr, sta->plink_state);
reason = 0;
llid = sta->llid;
plid = sta->plid;
@@ -287,9 +284,9 @@ static void mesh_plink_timer(unsigned long data)
/* retry timer */
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
- mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
- print_mac(mac, sta->sta.addr),
- sta->plink_retries, sta->plink_timeout);
+ mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
+ sta->sta.addr, sta->plink_retries,
+ sta->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
@@ -337,9 +334,6 @@ int mesh_plink_open(struct sta_info *sta)
{
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
@@ -351,8 +345,8 @@ int mesh_plink_open(struct sta_info *sta)
sta->plink_state = PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink: starting establishment with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: starting establishment with %pM\n",
+ sta->sta.addr);
return mesh_plink_frame_tx(sdata, PLINK_OPEN,
sta->sta.addr, llid, 0, 0);
@@ -360,10 +354,6 @@ int mesh_plink_open(struct sta_info *sta)
void mesh_plink_block(struct sta_info *sta)
{
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
-
spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED;
@@ -374,12 +364,8 @@ int mesh_plink_close(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
__le16 llid, plid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
- mpl_dbg("Mesh plink: closing link with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: closing link with %pM\n", sta->sta.addr);
spin_lock_bh(&sta->lock);
sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
reason = sta->reason;
@@ -417,9 +403,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
u8 ie_len;
u8 *baseaddr;
__le16 plid, llid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/* need action_code, aux */
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
@@ -557,10 +540,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
}
- mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n",
- print_mac(mac, mgmt->sa), sta->plink_state,
- le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
- event);
+ mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %d %d %d %d\n",
+ mgmt->sa, sta->plink_state,
+ le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
+ event);
reason = 0;
switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */
@@ -660,8 +643,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -693,8 +676,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 409bb7716236..5ba721b6a399 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -14,7 +14,6 @@
#include <linux/delay.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
-#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/random.h>
@@ -236,7 +235,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_add_ie;
+ u8 *pos, *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
@@ -310,7 +309,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval =
+ mgmt->u.assoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
}
@@ -393,24 +392,25 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
/* wmm support is a must to HT */
if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
- sband->ht_info.ht_supported &&
- (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
- struct ieee80211_ht_addt_info *ht_add_info =
- (struct ieee80211_ht_addt_info *)ht_add_ie;
- u16 cap = sband->ht_info.cap;
+ sband->ht_cap.ht_supported &&
+ (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
+ ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
+ struct ieee80211_ht_info *ht_info =
+ (struct ieee80211_ht_info *)(ht_ie + 2);
+ u16 cap = sband->ht_cap.cap;
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
- switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
- case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
- case IEEE80211_HT_IE_CHA_SEC_BELOW:
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
@@ -424,9 +424,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
/* TODO: needs a define here for << 2 */
- *pos++ = sband->ht_info.ampdu_factor |
- (sband->ht_info.ampdu_density << 2);
- memcpy(pos, sband->ht_info.supp_mcs_set, 16);
+ *pos++ = sband->ht_cap.ampdu_factor |
+ (sband->ht_cap.ampdu_density << 2);
+ memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
kfree(ifsta->assocreq_ies);
@@ -568,25 +568,35 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
}
}
-static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
- bool use_protection,
- bool use_short_preamble)
+static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
+ u16 capab, bool erp_valid, u8 erp)
{
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- DECLARE_MAC_BUF(mac);
#endif
u32 changed = 0;
+ bool use_protection;
+ bool use_short_preamble;
+ bool use_short_slot;
+
+ if (erp_valid) {
+ use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
+ use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
+ } else {
+ use_protection = false;
+ use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE);
+ }
+
+ use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
if (use_protection != bss_conf->use_cts_prot) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
- "%s)\n",
+ printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
sdata->dev->name,
use_protection ? "enabled" : "disabled",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_cts_prot = use_protection;
@@ -597,40 +607,28 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: switched to %s barker preamble"
- " (BSSID=%s)\n",
+ " (BSSID=%pM)\n",
sdata->dev->name,
use_short_preamble ? "short" : "long",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
- return changed;
-}
-
-static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata,
- u8 erp_value)
-{
- bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
- bool use_short_preamble = (erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0;
-
- return ieee80211_handle_protect_preamb(sdata,
- use_protection, use_short_preamble);
-}
-
-static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss *bss)
-{
- u32 changed = 0;
-
- if (bss->has_erp_value)
- changed |= ieee80211_handle_erp_ie(sdata, bss->erp_value);
- else {
- u16 capab = bss->capability;
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+ if (use_short_slot != bss_conf->use_short_slot) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: switched to %s slot"
+ " (BSSID=%s)\n",
+ sdata->dev->name,
+ use_short_slot ? "short" : "long",
+ ifsta->bssid);
+ }
+#endif
+ bss_conf->use_short_slot = use_short_slot;
+ changed |= BSS_CHANGED_ERP_SLOT;
}
return changed;
@@ -701,14 +699,15 @@ static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+ struct ieee80211_if_sta *ifsta,
+ u32 bss_info_changed)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- u32 changed = BSS_CHANGED_ASSOC;
struct ieee80211_bss *bss;
+ bss_info_changed |= BSS_CHANGED_ASSOC;
ifsta->flags |= IEEE80211_STA_ASSOCIATED;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
@@ -719,22 +718,16 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->ssid, ifsta->ssid_len);
if (bss) {
/* set timing information */
- sdata->bss_conf.beacon_int = bss->beacon_int;
- sdata->bss_conf.timestamp = bss->timestamp;
- sdata->bss_conf.dtim_period = bss->dtim_period;
+ sdata->vif.bss_conf.beacon_int = bss->beacon_int;
+ sdata->vif.bss_conf.timestamp = bss->timestamp;
+ sdata->vif.bss_conf.dtim_period = bss->dtim_period;
- changed |= ieee80211_handle_bss_capability(sdata, bss);
+ bss_info_changed |= ieee80211_handle_bss_capability(sdata,
+ bss->capability, bss->has_erp_value, bss->erp_value);
ieee80211_rx_bss_put(local, bss);
}
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- changed |= BSS_CHANGED_HT;
- sdata->bss_conf.assoc_ht = 1;
- sdata->bss_conf.ht_conf = &conf->ht_conf;
- sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
- }
-
ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
ieee80211_sta_send_associnfo(sdata, ifsta);
@@ -742,14 +735,25 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->last_probe = jiffies;
ieee80211_led_assoc(local, 1);
- sdata->bss_conf.assoc = 1;
+ sdata->vif.bss_conf.assoc = 1;
/*
* For now just always ask the driver to update the basic rateset
* when we have associated, we aren't checking whether it actually
* changed or not.
*/
- changed |= BSS_CHANGED_BASIC_RATES;
- ieee80211_bss_info_change_notify(sdata, changed);
+ bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+ ieee80211_bss_info_change_notify(sdata, bss_info_changed);
+
+ if (local->powersave) {
+ if (local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ conf->flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
+ }
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
@@ -760,18 +764,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->direct_probe_tries++;
if (ifsta->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: direct probe to AP %s timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
- printk(KERN_DEBUG "%s: direct probe to AP %s try %d\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid),
+ printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
+ sdata->dev->name, ifsta->bssid,
ifsta->direct_probe_tries);
ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
@@ -791,33 +794,36 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->auth_tries++;
if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: authentication with AP %s"
+ printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
ieee80211_send_auth(sdata, ifsta, 1, NULL, 0, 0);
mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
+/*
+ * The disassoc 'reason' argument can be either our own reason
+ * if self disconnected or a reason code from the AP.
+ */
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta, bool deauth,
bool self_disconnected, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- u32 changed = BSS_CHANGED_ASSOC;
+ u32 changed = 0, config_changed = 0;
rcu_read_lock();
@@ -851,21 +857,40 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
- if (sdata->bss_conf.assoc_ht)
- changed |= BSS_CHANGED_HT;
-
- sdata->bss_conf.assoc_ht = 0;
- sdata->bss_conf.ht_conf = NULL;
- sdata->bss_conf.ht_bss_conf = NULL;
-
ieee80211_led_assoc(local, 0);
- sdata->bss_conf.assoc = 0;
+ changed |= BSS_CHANGED_ASSOC;
+ sdata->vif.bss_conf.assoc = false;
ieee80211_sta_send_apinfo(sdata, ifsta);
- if (self_disconnected)
+ if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT)
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ rcu_read_unlock();
+
+ local->hw.conf.ht.enabled = false;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
+ config_changed |= IEEE80211_CONF_CHANGE_HT;
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ config_changed |= IEEE80211_CONF_CHANGE_PS;
+ }
+
+ ieee80211_hw_config(local, config_changed);
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
sta_info_unlink(&sta);
rcu_read_unlock();
@@ -914,20 +939,19 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->assoc_tries++;
if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- printk(KERN_DEBUG "%s: association with AP %s"
+ printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: associate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
if (ieee80211_privacy_mismatch(sdata, ifsta)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", sdata->dev->name);
@@ -947,7 +971,6 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int disassoc;
- DECLARE_MAC_BUF(mac);
/* TODO: start monitoring current AP signal quality and number of
* missed beacons. Scan other channels every now and then and search
@@ -960,8 +983,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else {
disassoc = 0;
@@ -969,9 +992,9 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
printk(KERN_DEBUG "%s: No ProbeResp from "
- "current AP %s - assume out of "
+ "current AP %pM - assume out of "
"range\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else
ieee80211_send_probe_req(sdata, ifsta->bssid,
@@ -1032,7 +1055,6 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 auth_alg, auth_transaction, status_code;
- DECLARE_MAC_BUF(mac);
if (ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
sdata->vif.type != NL80211_IFTYPE_ADHOC)
@@ -1125,7 +1147,6 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1136,7 +1157,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
if (ifsta->flags & IEEE80211_STA_AUTHENTICATED)
- printk(KERN_DEBUG "%s: deauthenticated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE ||
ifsta->state == IEEE80211_STA_MLME_ASSOCIATE ||
@@ -1157,7 +1179,6 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1168,7 +1189,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
@@ -1176,7 +1198,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(sdata, ifsta, false, false, 0);
+ ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code);
}
@@ -1192,11 +1214,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u64 rates, basic_rates;
u16 capab_info, status_code, aid;
struct ieee802_11_elems elems;
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u8 *pos;
+ u32 changed = 0;
int i, j;
- DECLARE_MAC_BUF(mac);
- bool have_higher_than_11mbit = false;
+ bool have_higher_than_11mbit = false, newsta = false;
+ u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
@@ -1214,9 +1237,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
- printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
+ printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
"status=%d aid=%d)\n",
- sdata->dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
+ sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
if (status_code != WLAN_STATUS_SUCCESS) {
@@ -1259,7 +1282,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
struct ieee80211_bss *bss;
- int err;
+
+ newsta = true;
sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
if (!sta) {
@@ -1278,13 +1302,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_put(local, bss);
}
- err = sta_info_insert(sta);
- if (err) {
- printk(KERN_DEBUG "%s: failed to insert STA entry for"
- " the AP (error %d)\n", sdata->dev->name, err);
- rcu_read_unlock();
- return;
- }
/* update new sta with its last rx activity */
sta->last_rx = jiffies;
}
@@ -1308,34 +1325,40 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < elems.supp_rates_len; i++) {
int rate = (elems.supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
for (i = 0; i < elems.ext_supp_rates_len; i++) {
int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.ext_supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
- sdata->bss_conf.basic_rates = basic_rates;
+ sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
@@ -1344,31 +1367,43 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
- (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- struct ieee80211_ht_bss_info bss_info;
- ieee80211_ht_cap_ie_to_ht_info(
- elems.ht_cap_elem, &sta->sta.ht_info);
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- ieee80211_handle_ht(local, 1, &sta->sta.ht_info, &bss_info);
- }
+ if (elems.ht_cap_elem)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
rate_control_rate_init(sta);
- if (elems.wmm_param) {
+ if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);
- rcu_read_unlock();
+
+ if (newsta) {
+ int err = sta_info_insert(sta);
+ if (err) {
+ printk(KERN_DEBUG "%s: failed to insert STA entry for"
+ " the AP (error %d)\n", sdata->dev->name, err);
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (elems.wmm_param)
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- } else
- rcu_read_unlock();
+
+ if (elems.ht_info_elem && elems.wmm_param &&
+ (ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(sdata, ifsta);
+ ieee80211_set_associated(sdata, ifsta, changed);
ieee80211_associated(sdata, ifsta);
}
@@ -1386,6 +1421,13 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "response\n", sdata->dev->name);
+ return -ENOMEM;
+ }
+
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
/* Remove possible STA entries from other IBSS networks. */
@@ -1411,63 +1453,62 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
return res;
/* Build IBSS probe response */
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
- if (skb) {
- skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 24 + sizeof(mgmt->u.beacon));
- memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
- memset(mgmt->da, 0xff, ETH_ALEN);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(local->hw.conf.beacon_int);
- mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
- mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
-
- pos = skb_put(skb, 2 + ifsta->ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->ssid_len);
-
- rates = bss->supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, bss->supp_rates, rates);
+ skb_reserve(skb, local->hw.extra_tx_headroom);
- if (bss->band == IEEE80211_BAND_2GHZ) {
- pos = skb_put(skb, 2 + 1);
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(bss->freq);
- }
+ mgmt = (struct ieee80211_mgmt *)
+ skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
+ mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
- pos = skb_put(skb, 2 + 2);
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
+ pos = skb_put(skb, 2 + ifsta->ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ifsta->ssid_len;
+ memcpy(pos, ifsta->ssid, ifsta->ssid_len);
- if (bss->supp_rates_len > 8) {
- rates = bss->supp_rates_len - 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, &bss->supp_rates[8], rates);
- }
+ rates = bss->supp_rates_len;
+ if (rates > 8)
+ rates = 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, bss->supp_rates, rates);
- ifsta->probe_resp = skb;
+ if (bss->band == IEEE80211_BAND_2GHZ) {
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(bss->freq);
+ }
+
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_IBSS_PARAMS;
+ *pos++ = 2;
+ /* FIX: set ATIM window based on scan results */
+ *pos++ = 0;
+ *pos++ = 0;
- ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+ if (bss->supp_rates_len > 8) {
+ rates = bss->supp_rates_len - 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, &bss->supp_rates[8], rates);
}
+ ifsta->probe_resp = skb;
+
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+
+
rates = 0;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
for (i = 0; i < bss->supp_rates_len; i++) {
@@ -1507,8 +1548,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
u64 beacon_timestamp, rx_timestamp;
u64 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (elems->ds_params && elems->ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1538,17 +1577,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (sta->sta.supp_rates[band] != prev_rates)
printk(KERN_DEBUG "%s: updated supp_rates set "
- "for %s based on beacon info (0x%llx | "
+ "for %pM based on beacon info (0x%llx | "
"0x%llx -> 0x%llx)\n",
sdata->dev->name,
- print_mac(mac, sta->sta.addr),
+ sta->sta.addr,
(unsigned long long) prev_rates,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
#endif
} else {
- ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
- mgmt->sa, supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
rcu_read_unlock();
@@ -1595,8 +1633,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* e.g: at 1 MBit that means mactime is 192 usec earlier
* (=24 bytes * 8 usecs/byte) than the beacon timestamp.
*/
- int rate = local->hw.wiphy->bands[band]->
+ int rate;
+ if (rx_status->flag & RX_FLAG_HT) {
+ rate = 65; /* TODO: HT rates */
+ } else {
+ rate = local->hw.wiphy->bands[band]->
bitrates[rx_status->rate_idx].bitrate;
+ }
rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
} else if (local && local->ops && local->ops->get_tsf)
/* second best option: get current TSF */
@@ -1605,10 +1648,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* can't merge without knowing the TSF */
rx_timestamp = -1LLU;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "RX beacon SA=%s BSSID="
- "%s TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
- print_mac(mac, mgmt->sa),
- print_mac(mac2, mgmt->bssid),
+ printk(KERN_DEBUG "RX beacon SA=%pM BSSID="
+ "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
+ mgmt->sa, mgmt->bssid,
(unsigned long long)rx_timestamp,
(unsigned long long)beacon_timestamp,
(unsigned long long)(rx_timestamp - beacon_timestamp),
@@ -1617,13 +1659,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (beacon_timestamp > rx_timestamp) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: beacon TSF higher than "
- "local TSF - IBSS merge with BSSID %s\n",
- sdata->dev->name, print_mac(mac, mgmt->bssid));
+ "local TSF - IBSS merge with BSSID %pM\n",
+ sdata->dev->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, &sdata->u.sta, bss);
- ieee80211_ibss_add_sta(sdata, NULL,
- mgmt->bssid, mgmt->sa,
- supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
}
@@ -1671,8 +1711,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local->hw.conf;
u32 changed = 0;
+ bool erp_valid;
+ u8 erp_value = 0;
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1694,22 +1735,49 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- if (elems.erp_info && elems.erp_info_len >= 1)
- changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
- else {
- u16 capab = le16_to_cpu(mgmt->u.beacon.capab_info);
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+
+ if (elems.erp_info && elems.erp_info_len >= 1) {
+ erp_valid = true;
+ erp_value = elems.erp_info[0];
+ } else {
+ erp_valid = false;
}
+ changed |= ieee80211_handle_bss_capability(sdata,
+ le16_to_cpu(mgmt->u.beacon.capab_info),
+ erp_valid, erp_value);
+
+
+ if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param) {
+ struct sta_info *sta;
+ struct ieee80211_supported_band *sband;
+ u16 ap_ht_cap_flags;
+
+ rcu_read_lock();
- if (elems.ht_cap_elem && elems.ht_info_elem &&
- elems.wmm_param && conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- struct ieee80211_ht_bss_info bss_info;
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- changed |= ieee80211_handle_ht(local, 1, &conf->ht_conf,
- &bss_info);
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
+
+ rcu_read_unlock();
+
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
+ }
+
+ if (elems.country_elem) {
+ /* Note we are only reviewing this on beacons
+ * for the BSSID we are associated to */
+ regulatory_hint_11d(local->hw.wiphy,
+ elems.country_elem, elems.country_elem_len);
}
ieee80211_bss_info_change_notify(sdata, changed);
@@ -1727,11 +1795,6 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
u8 *pos, *end;
- DECLARE_MAC_BUF(mac);
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
-#endif
if (sdata->vif.type != NL80211_IFTYPE_ADHOC ||
ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED ||
@@ -1744,10 +1807,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
tx_last_beacon = 1;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: RX ProbeReq SA=%s DA=%s BSSID="
- "%s (tx_last_beacon=%d)\n",
- sdata->dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
- print_mac(mac3, mgmt->bssid), tx_last_beacon);
+ printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
+ " (tx_last_beacon=%d)\n",
+ sdata->dev->name, mgmt->sa, mgmt->da,
+ mgmt->bssid, tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (!tx_last_beacon)
@@ -1763,8 +1826,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
pos + 2 + pos[1] > end) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from %s\n",
- sdata->dev->name, print_mac(mac, mgmt->sa));
+ "from %pM\n",
+ sdata->dev->name, mgmt->sa);
#endif
return;
}
@@ -1783,8 +1846,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
resp = (struct ieee80211_mgmt *) skb->data;
memcpy(resp->da, mgmt->sa, ETH_ALEN);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
- sdata->dev->name, print_mac(mac, resp->da));
+ printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n",
+ sdata->dev->name, resp->da);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -1972,7 +2035,7 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
}
}
- if (hidden_ssid && ifsta->ssid_len == ssid_len)
+ if (hidden_ssid && (ifsta->ssid_len == ssid_len || ssid_len == 0))
return 1;
if (ssid_len == 1 && ssid[0] == ' ')
@@ -1990,7 +2053,6 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN], *pos;
int i;
int ret;
- DECLARE_MAC_BUF(mac);
#if 0
/* Easier testing, use fixed BSSID. */
@@ -2006,8 +2068,8 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
bssid[0] |= 0x02;
#endif
- printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
- sdata->dev->name, print_mac(mac, bssid));
+ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
+ sdata->dev->name, bssid);
bss = ieee80211_rx_bss_add(local, bssid,
local->hw.conf.channel->center_freq,
@@ -2050,8 +2112,6 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
int found = 0;
u8 bssid[ETH_ALEN];
int active_ibss;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (ifsta->ssid_len == 0)
return -EINVAL;
@@ -2068,8 +2128,7 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
|| !(bss->capability & WLAN_CAPABILITY_IBSS))
continue;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " bssid=%s found\n",
- print_mac(mac, bss->bssid));
+ printk(KERN_DEBUG " bssid=%pM found\n", bss->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
memcpy(bssid, bss->bssid, ETH_ALEN);
found = 1;
@@ -2080,9 +2139,8 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (found)
- printk(KERN_DEBUG " sta_find_ibss: selected %s current "
- "%s\n", print_mac(mac, bssid),
- print_mac(mac2, ifsta->bssid));
+ printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
+ "%pM\n", bssid, ifsta->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
@@ -2099,9 +2157,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
if (!bss)
goto dont_join;
- printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
+ printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
" based on configured SSID\n",
- sdata->dev->name, print_mac(mac, bssid));
+ sdata->dev->name, bssid);
ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
ieee80211_rx_bss_put(local, bss);
return ret;
@@ -2338,12 +2396,10 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates)
+ u8 *bssid,u8 *addr, u64 supp_rates)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- DECLARE_MAC_BUF(mac);
int band = local->hw.conf.channel->band;
/* TODO: Could consider removing the least recently used entry and
@@ -2351,7 +2407,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: No room for a new IBSS STA "
- "entry %s\n", sdata->dev->name, print_mac(mac, addr));
+ "entry %pM\n", sdata->dev->name, addr);
}
return NULL;
}
@@ -2360,8 +2416,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, addr), sdata->dev->name);
+ printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
#endif
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -2408,7 +2464,6 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
{
struct ieee80211_if_sta *ifsta;
- int res;
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
@@ -2420,19 +2475,6 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
memcpy(ifsta->ssid, ssid, len);
ifsta->ssid_len = len;
ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-
- res = 0;
- /*
- * Hack! MLME code needs to be cleaned up to have different
- * entry points for configuration and internal selection change
- */
- if (netif_running(sdata->dev))
- res = ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- if (res) {
- printk(KERN_DEBUG "%s: Failed to config new SSID to "
- "the low-level driver\n", sdata->dev->name);
- return res;
- }
}
if (len)
@@ -2560,3 +2602,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_disable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS)
+ return;
+
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+ struct ieee80211_local *local = (void *) data;
+
+ queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 5d786720d935..3fa7ab285066 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -199,48 +199,44 @@ static void rate_control_release(struct kref *kref)
}
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel)
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc)
{
struct rate_control_ref *ref = sdata->local->rate_ctrl;
void *priv_sta = NULL;
struct ieee80211_sta *ista = NULL;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
int i;
- sel->rate_idx = -1;
- sel->nonerp_idx = -1;
- sel->probe_idx = -1;
- sel->max_rate_idx = sdata->max_ratectrl_rateidx;
-
if (sta) {
ista = &sta->sta;
priv_sta = sta->rate_ctrl_priv;
}
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->control.rates[i].idx = -1;
+ info->control.rates[i].flags = 0;
+ info->control.rates[i].count = 1;
+ }
+
if (sta && sdata->force_unicast_rateidx > -1)
- sel->rate_idx = sdata->force_unicast_rateidx;
+ info->control.rates[0].idx = sdata->force_unicast_rateidx;
else
- ref->ops->get_rate(ref->priv, sband, ista, priv_sta, skb, sel);
-
- if (sdata->max_ratectrl_rateidx > -1 &&
- sel->rate_idx > sdata->max_ratectrl_rateidx)
- sel->rate_idx = sdata->max_ratectrl_rateidx;
-
- BUG_ON(sel->rate_idx < 0);
-
- /* Select a non-ERP backup rate. */
- if (sel->nonerp_idx < 0) {
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
- if (sband->bitrates[sel->rate_idx].bitrate < rate->bitrate)
- break;
-
- if (rate_supported(ista, sband->band, i) &&
- !(rate->flags & IEEE80211_RATE_ERP_G))
- sel->nonerp_idx = i;
- }
+ ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+
+ /*
+ * try to enforce the maximum rate the user wanted
+ */
+ if (sdata->max_ratectrl_rateidx > -1)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS)
+ continue;
+ info->control.rates[i].idx =
+ min_t(s8, info->control.rates[i].idx,
+ sdata->max_ratectrl_rateidx);
}
+
+ BUG_ON(info->control.rates[0].idx < 0);
}
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index d0092f847f82..928da625e281 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -31,9 +31,8 @@ struct rate_control_ref {
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local);
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel);
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc);
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref);
void rate_control_put(struct rate_control_ref *ref);
@@ -64,12 +63,6 @@ static inline void rate_control_rate_init(struct sta_info *sta)
}
-static inline void rate_control_clear(struct ieee80211_local *local)
-{
- struct rate_control_ref *ref = local->rate_ctrl;
- ref->ops->clear(ref->priv);
-}
-
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
struct ieee80211_sta *sta,
gfp_t gfp)
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index f6d69dab07a3..2b3b490a6073 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -126,7 +126,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
mr->adjusted_retry_count = mr->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
+ mr->sample_limit = 4;
} else {
+ mr->sample_limit = -1;
mr->adjusted_retry_count = mr->retry_count;
}
if (!mr->adjusted_retry_count)
@@ -169,30 +171,20 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
{
struct minstrel_sta_info *mi = priv_sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_altrate *ar = info->status.retries;
- struct minstrel_priv *mp = priv;
- int i, ndx, tries;
- int success = 0;
-
- if (!info->status.excessive_retries)
- success = 1;
+ struct ieee80211_tx_rate *ar = info->status.rates;
+ int i, ndx;
+ int success;
- if (!mp->has_mrr || (ar[0].rate_idx < 0)) {
- ndx = rix_to_ndx(mi, info->tx_rate_idx);
- tries = info->status.retry_count + 1;
- mi->r[ndx].success += success;
- mi->r[ndx].attempts += tries;
- return;
- }
+ success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- for (i = 0; i < 4; i++) {
- if (ar[i].rate_idx < 0)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (ar[i].idx < 0)
break;
- ndx = rix_to_ndx(mi, ar[i].rate_idx);
- mi->r[ndx].attempts += ar[i].limit + 1;
+ ndx = rix_to_ndx(mi, ar[i].idx);
+ mi->r[ndx].attempts += ar[i].count;
- if ((i != 3) && (ar[i + 1].rate_idx < 0))
+ if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0))
mi->r[ndx].success += success;
}
@@ -210,9 +202,9 @@ minstrel_get_retry_count(struct minstrel_rate *mr,
{
unsigned int retry = mr->adjusted_retry_count;
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
retry = max(2U, min(mr->retry_count_rtscts, retry));
- else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
retry = max(2U, min(mr->retry_count_cts, retry));
return retry;
}
@@ -233,15 +225,16 @@ minstrel_get_next_sample(struct minstrel_sta_info *mi)
return sample_ndx;
}
-void
-minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void
+minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta, struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
- struct ieee80211_tx_altrate *ar = info->control.retries;
+ struct ieee80211_tx_rate *ar = info->control.rates;
unsigned int ndx, sample_ndx = 0;
bool mrr;
bool sample_slower = false;
@@ -251,16 +244,12 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
int sample_rate;
if (!sta || !mi || use_low_rate(skb)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ ar[0].idx = rate_lowest_index(sband, sta);
+ ar[0].count = mp->max_retry;
return;
}
- mrr = mp->has_mrr;
-
- /* mac80211 does not allow mrr for RTS/CTS */
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
- mrr = false;
+ mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
if (time_after(jiffies, mi->stats_update + (mp->update_interval *
HZ) / 1000))
@@ -278,7 +267,8 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
(mi->sample_count + mi->sample_deferred / 2);
/* delta > 0: sampling required */
- if (delta > 0) {
+ if ((delta > 0) && (mrr || !mi->prev_sample)) {
+ struct minstrel_rate *msr;
if (mi->packet_count >= 10000) {
mi->sample_deferred = 0;
mi->sample_count = 0;
@@ -297,13 +287,20 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
sample_ndx = minstrel_get_next_sample(mi);
+ msr = &mi->r[sample_ndx];
sample = true;
- sample_slower = mrr && (mi->r[sample_ndx].perfect_tx_time >
+ sample_slower = mrr && (msr->perfect_tx_time >
mi->r[ndx].perfect_tx_time);
if (!sample_slower) {
- ndx = sample_ndx;
- mi->sample_count++;
+ if (msr->sample_limit != 0) {
+ ndx = sample_ndx;
+ mi->sample_count++;
+ if (msr->sample_limit > 0)
+ msr->sample_limit--;
+ } else {
+ sample = false;
+ }
} else {
/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
* packets that have the sampling rate deferred to the
@@ -315,13 +312,22 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
mi->sample_deferred++;
}
}
- sel->rate_idx = mi->r[ndx].rix;
- info->control.retry_limit = minstrel_get_retry_count(&mi->r[ndx], info);
+ mi->prev_sample = sample;
+
+ /* If we're not using MRR and the sampling rate already
+ * has a probability of >95%, we shouldn't be attempting
+ * to use it, as this only wastes precious airtime */
+ if (!mrr && sample && (mi->r[ndx].probability > 17100))
+ ndx = mi->max_tp_rate;
+
+ ar[0].idx = mi->r[ndx].rix;
+ ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info);
if (!mrr) {
- ar[0].rate_idx = mi->lowest_rix;
- ar[0].limit = mp->max_retry;
- ar[1].rate_idx = -1;
+ if (!sample)
+ ar[0].count = mp->max_retry;
+ ar[1].idx = mi->lowest_rix;
+ ar[1].count = mp->max_retry;
return;
}
@@ -336,9 +342,9 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
mrr_ndx[1] = mi->max_prob_rate;
mrr_ndx[2] = 0;
- for (i = 0; i < 3; i++) {
- ar[i].rate_idx = mi->r[mrr_ndx[i]].rix;
- ar[i].limit = mi->r[mrr_ndx[i]].adjusted_retry_count;
+ for (i = 1; i < 4; i++) {
+ ar[i].idx = mi->r[mrr_ndx[i - 1]].rix;
+ ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count;
}
}
@@ -415,6 +421,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
+ mr->sample_limit = -1;
mr->retry_count = 1;
mr->retry_count_cts = 1;
mr->retry_count_rtscts = 1;
@@ -500,11 +507,6 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
kfree(mi);
}
-static void
-minstrel_clear(void *priv)
-{
-}
-
static void *
minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
@@ -532,13 +534,13 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
/* maximum time that the hw is allowed to stay in one MRR segment */
mp->segment_size = 6000;
- if (hw->max_altrate_tries > 0)
- mp->max_retry = hw->max_altrate_tries;
+ if (hw->max_rate_tries > 0)
+ mp->max_retry = hw->max_rate_tries;
else
/* safe default, does not necessarily have to match hw properties */
mp->max_retry = 7;
- if (hw->max_altrates >= 3)
+ if (hw->max_rates >= 4)
mp->has_mrr = true;
mp->hw = hw;
@@ -558,7 +560,6 @@ static struct rate_control_ops mac80211_minstrel = {
.tx_status = minstrel_tx_status,
.get_rate = minstrel_get_rate,
.rate_init = minstrel_rate_init,
- .clear = minstrel_clear,
.alloc = minstrel_alloc,
.free = minstrel_free,
.alloc_sta = minstrel_alloc_sta,
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 9a90a6aee043..869fe0ef951d 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -16,6 +16,7 @@ struct minstrel_rate {
unsigned int perfect_tx_time;
unsigned int ack_time;
+ int sample_limit;
unsigned int retry_count;
unsigned int retry_count_cts;
unsigned int retry_count_rtscts;
@@ -57,6 +58,7 @@ struct minstrel_sta_info {
int n_rates;
struct minstrel_rate *r;
+ bool prev_sample;
/* sampling table */
u8 *sample_table;
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
index 01d64d53f3b9..1a873f00691a 100644
--- a/net/mac80211/rc80211_pid.h
+++ b/net/mac80211/rc80211_pid.h
@@ -49,7 +49,7 @@
/* Arithmetic right shift for positive and negative values for ISO C. */
#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
- (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)
+ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
enum rc_pid_event_type {
RC_PID_EVENT_TYPE_TX_STATUS,
@@ -61,6 +61,7 @@ enum rc_pid_event_type {
union rc_pid_event_data {
/* RC_PID_EVENT_TX_STATUS */
struct {
+ u32 flags;
struct ieee80211_tx_info tx_status;
};
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 86eb374e3b87..b16801cde06f 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -241,7 +241,7 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
- if (info->tx_rate_idx != spinfo->txrate_idx)
+ if (info->status.rates[0].idx != spinfo->txrate_idx)
return;
spinfo->tx_num_xmit++;
@@ -253,10 +253,10 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* We count frames that totally failed to be transmitted as two bad
* frames, those that made it out but had some retries as one good and
* one bad frame. */
- if (info->status.excessive_retries) {
+ if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
spinfo->tx_num_failed += 2;
spinfo->tx_num_xmit++;
- } else if (info->status.retry_count) {
+ } else if (info->status.rates[0].count > 1) {
spinfo->tx_num_failed++;
spinfo->tx_num_xmit++;
}
@@ -270,23 +270,32 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
}
static void
-rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb,
- struct rate_selection *sel)
+rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
u16 fc;
+ if (txrc->rts)
+ info->control.rates[0].count =
+ txrc->hw->conf.long_frame_max_tx_count;
+ else
+ info->control.rates[0].count =
+ txrc->hw->conf.short_frame_max_tx_count;
+
/* Send management frames and broadcast/multicast data using lowest
* rate. */
fc = le16_to_cpu(hdr->frame_control);
if (!sta || !spinfo ||
(fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
is_multicast_ether_addr(hdr->addr1)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
return;
}
@@ -295,7 +304,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
if (rateidx >= sband->n_bitrates)
rateidx = sband->n_bitrates - 1;
- sel->rate_idx = rateidx;
+ info->control.rates[0].idx = rateidx;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate(&spinfo->events,
@@ -394,11 +403,11 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sampling_period);
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_p);
+ debugfsdir, (u32 *)&pinfo->coeff_p);
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_i);
+ debugfsdir, (u32 *)&pinfo->coeff_i);
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_d);
+ debugfsdir, (u32 *)&pinfo->coeff_d);
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->smoothing_shift);
@@ -437,10 +446,6 @@ static void rate_control_pid_free(void *priv)
kfree(pinfo);
}
-static void rate_control_pid_clear(void *priv)
-{
-}
-
static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
gfp_t gfp)
{
@@ -471,7 +476,6 @@ static struct rate_control_ops mac80211_rcpid = {
.tx_status = rate_control_pid_tx_status,
.get_rate = rate_control_pid_get_rate,
.rate_init = rate_control_pid_rate_init,
- .clear = rate_control_pid_clear,
.alloc = rate_control_pid_alloc,
.free = rate_control_pid_free,
.alloc_sta = rate_control_pid_alloc_sta,
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
index 8121d3bc6835..a08a9b530347 100644
--- a/net/mac80211/rc80211_pid_debugfs.c
+++ b/net/mac80211/rc80211_pid_debugfs.c
@@ -43,6 +43,7 @@ void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
{
union rc_pid_event_data evd;
+ evd.flags = stat->flags;
memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
}
@@ -167,8 +168,8 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS:
p += snprintf(pb + p, length - p, "tx_status %u %u",
- ev->data.tx_status.status.excessive_retries,
- ev->data.tx_status.status.retry_count);
+ !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+ ev->data.tx_status.status.rates[0].idx);
break;
case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += snprintf(pb + p, length - p, "rate_change %d %d",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index cf6b121e1bbf..7175ae80c36a 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -26,10 +26,11 @@
#include "tkip.h"
#include "wme.h"
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req);
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req);
/*
* monitor mode reception
*
@@ -122,7 +123,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* radiotap header, set always present flags */
rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
@@ -148,7 +148,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- *pos = rate->bitrate / 5;
+ if (status->flag & RX_FLAG_HT) {
+ /*
+ * TODO: add following information into radiotap header once
+ * suitable fields are defined for it:
+ * - MCS index (status->rate_idx)
+ * - HT40 (status->flag & RX_FLAG_40MHZ)
+ * - short-GI (status->flag & RX_FLAG_SHORT_GI)
+ */
+ *pos = 0;
+ } else {
+ rthdr->it_present |= (1 << IEEE80211_RADIOTAP_RATE);
+ *pos = rate->bitrate / 5;
+ }
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
@@ -653,13 +665,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
static void ap_sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
- DECLARE_MAC_BUF(mac);
+ struct ieee80211_local *local = sdata->local;
atomic_inc(&sdata->bss->num_sta_ps);
set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -669,38 +684,37 @@ static int ap_sta_ps_end(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
int sent = 0;
- struct ieee80211_tx_info *info;
- DECLARE_MAC_BUF(mac);
atomic_dec(&sdata->bss->num_sta_ps);
clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_AWAKE, &sta->sta);
if (!skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Send all buffered frames to the station */
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
sent++;
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
local->total_ps_buffered--;
sent++;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d send PS frame "
+ printk(KERN_DEBUG "%s: STA %pM aid %d send PS frame "
"since STA not sleeping anymore\n", sdata->dev->name,
- print_mac(mac, sta->sta.addr), sta->sta.aid);
+ sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
@@ -745,17 +759,29 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->last_qual = rx->status->qual;
sta->last_noise = rx->status->noise;
+ /*
+ * Change STA power saving mode only at the end of a frame
+ * exchange sequence.
+ */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
- /* Change STA power saving mode only in the end of a frame
- * exchange sequence */
- if (test_sta_flags(sta, WLAN_STA_PS) &&
- !ieee80211_has_pm(hdr->frame_control))
- rx->sent_ps_buffered += ap_sta_ps_end(sta);
- else if (!test_sta_flags(sta, WLAN_STA_PS) &&
- ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_start(sta);
+ if (test_sta_flags(sta, WLAN_STA_PS)) {
+ /*
+ * Ignore doze->wake transitions that are
+ * indicated by non-data frames, the standard
+ * is unclear here, but for example going to
+ * PS mode and then scanning would cause a
+ * doze->wake transition for the probe request,
+ * and that is clearly undesirable.
+ */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_has_pm(hdr->frame_control))
+ rx->sent_ps_buffered += ap_sta_ps_end(sta);
+ } else {
+ if (ieee80211_has_pm(hdr->frame_control))
+ ap_sta_ps_start(sta);
+ }
}
/* Drop data::nullfunc frames silently, since they are used only to
@@ -789,15 +815,12 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) entry->skb_list.next->data;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
printk(KERN_DEBUG "%s: RX reassembly removed oldest "
"fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
- "addr1=%s addr2=%s\n",
+ "addr1=%pM addr2=%pM\n",
sdata->dev->name, idx,
jiffies - entry->first_frag_time, entry->seq,
- entry->last_frag, print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2));
+ entry->last_frag, hdr->addr1, hdr->addr2);
#endif
__skb_queue_purge(&entry->skb_list);
}
@@ -866,7 +889,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
unsigned int frag, seq;
struct ieee80211_fragment_entry *entry;
struct sk_buff *skb;
- DECLARE_MAC_BUF(mac);
hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control;
@@ -970,7 +992,6 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
- DECLARE_MAC_BUF(mac);
__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
@@ -1001,8 +1022,8 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
- print_mac(mac, rx->sta->sta.addr), rx->sta->sta.aid,
+ printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
+ rx->sta->sta.addr, rx->sta->sta.aid,
skb_queue_len(&rx->sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
@@ -1025,9 +1046,9 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
* Should we send it a null-func frame indicating we
* have nothing buffered for it?
*/
- printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
+ printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
"though there are no buffered frames for it\n",
- rx->dev->name, print_mac(mac, rx->sta->sta.addr));
+ rx->dev->name, rx->sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -1097,10 +1118,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
u8 src[ETH_ALEN] __aligned(2);
struct sk_buff *skb = rx->skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
@@ -1279,7 +1296,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
int remaining, err;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
if (unlikely(!ieee80211_is_data(fc)))
return RX_CONTINUE;
@@ -1552,14 +1568,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
return RX_DROP_MONITOR;
- /*
- * FIXME: revisit this, I'm sure we should handle most
- * of these frames in other modes as well!
- */
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
- return RX_CONTINUE;
-
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
@@ -1632,8 +1640,6 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
{
int keyidx;
unsigned int hdrlen;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len >= hdrlen + 4)
@@ -1854,10 +1860,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
- } else if (!rx->sta)
- rx->sta = ieee80211_ibss_add_sta(sdata, rx->skb,
- bssid, hdr->addr2,
- BIT(rx->status->rate_idx));
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (rx->status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = rx->status->rate_idx;
+ rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
@@ -2002,17 +2013,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
static inline int seq_less(u16 sq1, u16 sq2)
{
- return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1));
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
}
static inline u16 seq_inc(u16 sq)
{
- return ((sq + 1) & SEQ_MASK);
+ return (sq + 1) & SEQ_MASK;
}
static inline u16 seq_sub(u16 sq1, u16 sq2)
{
- return ((sq1 - sq2) & SEQ_MASK);
+ return (sq1 - sq2) & SEQ_MASK;
}
@@ -2020,10 +2031,11 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
* As it function blongs to Rx path it must be called with
* the proper rcu_read_lock protection for its flow.
*/
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req)
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rx_status status;
@@ -2062,7 +2074,13 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT) {
+ /* TODO: HT rates */
+ rate = sband->bitrates;
+ } else {
+ rate = &sband->bitrates
+ [status.rate_idx];
+ }
__ieee80211_rx_handle_packet(hw,
tid_agg_rx->reorder_buf[index],
&status, rate);
@@ -2106,7 +2124,10 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT)
+ rate = sband->bitrates; /* TODO: HT rates */
+ else
+ rate = &sband->bitrates[status.rate_idx];
__ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
&status, rate);
tid_agg_rx->stored_mpdu_num--;
@@ -2194,15 +2215,26 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
}
sband = local->hw.wiphy->bands[status->band];
-
- if (!sband ||
- status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates) {
+ if (!sband) {
WARN_ON(1);
return;
}
- rate = &sband->bitrates[status->rate_idx];
+ if (status->flag & RX_FLAG_HT) {
+ /* rate_idx is MCS index */
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= 76))
+ return;
+ /* HT rates are not in the table - use the highest legacy rate
+ * for now since other parts of mac80211 may not yet be fully
+ * MCS aware. */
+ rate = &sband->bitrates[sband->n_bitrates - 1];
+ } else {
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= sband->n_bitrates))
+ return;
+ rate = &sband->bitrates[status->rate_idx];
+ }
/*
* key references and virtual interfaces are protected using RCU
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 416bb41099f3..f5c7c3371929 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -159,7 +159,7 @@ ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_i
{
struct ieee80211_bss *bss;
- if (mesh_config_len != MESH_CFG_LEN)
+ if (mesh_config_len != IEEE80211_MESH_CONFIG_LEN)
return NULL;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
@@ -448,18 +448,17 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
if (local->hw_scanning) {
local->hw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ /*
+ * Somebody might have requested channel change during scan
+ * that we won't have acted upon, try now. ieee80211_hw_config
+ * will set the flag based on actual changes.
+ */
+ ieee80211_hw_config(local, 0);
goto done;
}
local->sw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
netif_tx_lock_bh(local->mdev);
netif_addr_lock(local->mdev);
@@ -546,12 +545,9 @@ void ieee80211_scan_work(struct work_struct *work)
if (!skip) {
local->scan_channel = chan;
- if (ieee80211_hw_config(local)) {
- printk(KERN_DEBUG "%s: failed to set freq to "
- "%d MHz for scan\n", wiphy_name(local->hw.wiphy),
- chan->center_freq);
+ if (ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
- }
}
/* advance state machine to next channel/band */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d254446b85b5..10c5539c20ab 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -137,14 +137,12 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
static void __sta_info_free(struct ieee80211_local *local,
struct sta_info *sta)
{
- DECLARE_MAC_BUF(mbuf);
-
rate_control_free_sta(sta);
rate_control_put(sta->rate_ctrl);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Destroyed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Destroyed STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
kfree(sta);
@@ -222,7 +220,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i;
- DECLARE_MAC_BUF(mbuf);
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
if (!sta)
@@ -263,8 +260,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Allocated STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Allocated STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
@@ -281,7 +278,6 @@ int sta_info_insert(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
int err = 0;
- DECLARE_MAC_BUF(mac);
/*
* Can't be a WARN_ON because it can be triggered through a race:
@@ -294,7 +290,7 @@ int sta_info_insert(struct sta_info *sta)
}
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->dev->dev_addr) == 0 ||
- is_multicast_ether_addr(sta->sta.addr))) {
+ is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
goto out_free;
}
@@ -322,8 +318,8 @@ int sta_info_insert(struct sta_info *sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Inserted STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Inserted STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -423,9 +419,6 @@ static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mbuf);
-#endif
/*
* pull caller's reference if we're already gone.
*/
@@ -468,8 +461,8 @@ static void __sta_info_unlink(struct sta_info **sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Removed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->sta.addr));
+ printk(KERN_DEBUG "%s: Removed STA %pM\n",
+ wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/*
@@ -544,7 +537,6 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
unsigned long flags;
struct sk_buff *skb;
struct ieee80211_sub_if_data *sdata;
- DECLARE_MAC_BUF(mac);
if (skb_queue_empty(&sta->ps_tx_buf))
return;
@@ -564,8 +556,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
sdata = sta->sdata;
local->total_ps_buffered--;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "Buffered frame expired (STA "
- "%s)\n", print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n",
+ sta->sta.addr);
#endif
dev_kfree_skb(skb);
@@ -809,15 +801,14 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
- DECLARE_MAC_BUF(mac);
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx + exp_time)) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
+ sdata->dev->name, sta->sta.addr);
#endif
__sta_info_unlink(&sta);
if (sta)
@@ -830,7 +821,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
}
struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
- const u8 *addr)
+ const u8 *addr)
{
struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 168a39a298bd..dc2606d0ae77 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -160,18 +160,17 @@ struct sta_ampdu_mlme {
* @list: global linked list entry
* @hnext: hash table linked list pointer
* @local: pointer to the global information
- * @sdata: TBD
- * @key: TBD
- * @rate_ctrl: TBD
- * @rate_ctrl_priv: TBD
+ * @sdata: virtual interface this station belongs to
+ * @key: peer key negotiated with this station, if any
+ * @rate_ctrl: rate control algorithm reference
+ * @rate_ctrl_priv: rate control private per-STA pointer
+ * @last_tx_rate: rate used for last transmit, to report to userspace as
+ * "the" transmit rate
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @flaglock: spinlock for flags accesses
- * @addr: MAC address of this STA
- * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
- * only used in AP (and IBSS?) mode
- * @listen_interval: TBD
- * @pin_status: TBD
+ * @listen_interval: listen interval of this station, when we're acting as AP
+ * @pin_status: used internally for pinning a STA struct into memory
* @flags: STA flags, see &enum ieee80211_sta_info_flags
* @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state
@@ -180,8 +179,8 @@ struct sta_ampdu_mlme {
* power saving state
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
- * @wep_weak_iv_count: TBD
- * @last_rx: TBD
+ * @wep_weak_iv_count: number of weak WEP IVs received from this station
+ * @last_rx: time (in jiffies) when last frame was received from this STA
* @num_duplicates: number of duplicate frames received from this STA
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
@@ -189,26 +188,26 @@ struct sta_ampdu_mlme {
* @last_qual: qual of last received frame from this STA
* @last_noise: noise of last received frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
- * @tx_filtered_count: TBD
- * @tx_retry_failed: TBD
- * @tx_retry_count: TBD
+ * @tx_filtered_count: number of frames the hardware filtered for this STA
+ * @tx_retry_failed: number of frames that failed retry
+ * @tx_retry_count: total number of retries for frames to this STA
* @fail_avg: moving percentage of failed MSDUs
* @tx_packets: number of RX/TX MSDUs
- * @tx_bytes: TBD
+ * @tx_bytes: number of bytes transmitted to this STA
* @tx_fragments: number of transmitted MPDUs
- * @last_txrate_idx: Index of the last used transmit rate
- * @tid_seq: TBD
- * @ampdu_mlme: TBD
+ * @last_txrate: description of the last used transmit rate
+ * @tid_seq: per-TID sequence numbers for sending to this STA
+ * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
* @tid_to_tx_q: map tid to tx queue
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
* @plink_retries: Retries in establishment
- * @ignore_plink_timer: TBD
- * @plink_state plink_state: TBD
- * @plink_timeout: TBD
- * @plink_timer: TBD
+ * @ignore_plink_timer: ignore the peer-link timer (used internally)
+ * @plink_state: peer link state
+ * @plink_timeout: timeout of peer link
+ * @plink_timer: peer link watch timer
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
*/
@@ -267,7 +266,7 @@ struct sta_info {
unsigned long tx_packets;
unsigned long tx_bytes;
unsigned long tx_fragments;
- unsigned int last_txrate_idx;
+ struct ieee80211_tx_rate last_tx_rate;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 34b32bc8f609..38fa111d2dc6 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -263,10 +263,9 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
(iv32 == key->u.tkip.rx[queue].iv32 &&
iv16 <= key->u.tkip.rx[queue].iv16))) {
#ifdef CONFIG_MAC80211_TKIP_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP replay detected for RX frame from "
- "%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
- print_mac(mac, ta),
+ "%pM (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
+ ta,
iv32, iv16, key->u.tkip.rx[queue].iv32,
key->u.tkip.rx[queue].iv16);
#endif
@@ -287,9 +286,8 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
{
int i;
u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY;
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s"
- " TK=", print_mac(mac, ta));
+ printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%pM"
+ " TK=", ta);
for (i = 0; i < 16; i++)
printk("%02x ",
key->conf.key[key_offset + i]);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1460537faf33..a4af3a124cce 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -46,13 +46,20 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+ /* assume HW handles this */
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ return 0;
+
+ /* uh huh? */
+ if (WARN_ON_ONCE(info->control.rates[0].idx < 0))
+ return 0;
sband = local->hw.wiphy->bands[tx->channel->band];
- txrate = &sband->bitrates[tx->rate_idx];
+ txrate = &sband->bitrates[info->control.rates[0].idx];
- erp = 0;
- if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = txrate->flags & IEEE80211_RATE_ERP_G;
+ erp = txrate->flags & IEEE80211_RATE_ERP_G;
/*
* data and mgmt (except PS Poll):
@@ -116,7 +123,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
if (r->bitrate > txrate->bitrate)
break;
- if (tx->sdata->bss_conf.basic_rates & BIT(i))
+ if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
rate = r->bitrate;
switch (sband->band) {
@@ -150,7 +157,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
* to closest integer */
dur = ieee80211_frame_duration(local, 10, rate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to
@@ -159,7 +166,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
/* next fragment */
dur += ieee80211_frame_duration(local, next_frag_len,
txrate->bitrate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
}
return cpu_to_le16(dur);
@@ -201,10 +208,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
tx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
ieee80211_is_data(hdr->frame_control))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: dropped data frame to not "
- "associated station %s\n",
- tx->dev->name, print_mac(mac, hdr->addr1));
+ "associated station %pM\n",
+ tx->dev->name, hdr->addr1);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
return TX_DROP;
@@ -331,7 +337,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
u32 staflags;
- DECLARE_MAC_BUF(mac);
if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control)))
return TX_CONTINUE;
@@ -341,9 +346,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
if (unlikely((staflags & WLAN_STA_PS) &&
!(staflags & WLAN_STA_PSPOLL))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS buffer (entries "
+ printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
"before %d)\n",
- print_mac(mac, sta->sta.addr), sta->sta.aid,
+ sta->sta.addr, sta->sta.aid,
skb_queue_len(&sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
@@ -352,9 +357,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: STA %s TX "
+ printk(KERN_DEBUG "%s: STA %pM TX "
"buffer full - dropping oldest frame\n",
- tx->dev->name, print_mac(mac, sta->sta.addr));
+ tx->dev->name, sta->sta.addr);
}
#endif
dev_kfree_skb(old);
@@ -371,9 +376,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
else if (unlikely(test_sta_flags(sta, WLAN_STA_PS))) {
- printk(KERN_DEBUG "%s: STA %s in PS mode, but pspoll "
+ printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll "
"set -> send frame\n", tx->dev->name,
- print_mac(mac, sta->sta.addr));
+ sta->sta.addr);
}
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
clear_sta_flags(sta, WLAN_STA_PSPOLL);
@@ -439,140 +444,154 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
{
- struct rate_selection rsel;
- struct ieee80211_supported_band *sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (void *)tx->skb->data;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate;
+ int i, len;
+ bool inval = false, rts = false, short_preamble = false;
+ struct ieee80211_tx_rate_control txrc;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ memset(&txrc, 0, sizeof(txrc));
- if (likely(tx->rate_idx < 0)) {
- rate_control_get_rate(tx->sdata, sband, tx->sta,
- tx->skb, &rsel);
- if (tx->sta)
- tx->sta->last_txrate_idx = rsel.rate_idx;
- tx->rate_idx = rsel.rate_idx;
- if (unlikely(rsel.probe_idx >= 0)) {
- info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- info->control.retries[0].rate_idx = tx->rate_idx;
- info->control.retries[0].limit = tx->local->hw.max_altrate_tries;
- tx->rate_idx = rsel.probe_idx;
- } else if (info->control.retries[0].limit == 0)
- info->control.retries[0].rate_idx = -1;
-
- if (unlikely(tx->rate_idx < 0))
- return TX_DROP;
- } else
- info->control.retries[0].rate_idx = -1;
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
- if (tx->sdata->bss_conf.use_cts_prot &&
- (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) {
- tx->last_frag_rate_idx = tx->rate_idx;
- if (rsel.probe_idx >= 0)
- tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
- else
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- tx->rate_idx = rsel.nonerp_idx;
- info->tx_rate_idx = rsel.nonerp_idx;
- info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- } else {
- tx->last_frag_rate_idx = tx->rate_idx;
- info->tx_rate_idx = tx->rate_idx;
+ len = min_t(int, tx->skb->len + FCS_LEN,
+ tx->local->fragmentation_threshold);
+
+ /* set up the tx rate control struct we give the RC algo */
+ txrc.hw = local_to_hw(tx->local);
+ txrc.sband = sband;
+ txrc.bss_conf = &tx->sdata->vif.bss_conf;
+ txrc.skb = tx->skb;
+ txrc.reported_rate.idx = -1;
+ txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx;
+
+ /* set up RTS protection if desired */
+ if (tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD &&
+ len > tx->local->rts_threshold) {
+ txrc.rts = rts = true;
}
- info->tx_rate_idx = tx->rate_idx;
- return TX_CONTINUE;
-}
+ /*
+ * Use short preamble if the BSS can handle it, but not for
+ * management frames unless we know the receiver can handle
+ * that -- the management frame might be to a station that
+ * just wants a probe response.
+ */
+ if (tx->sdata->vif.bss_conf.use_short_preamble &&
+ (ieee80211_is_data(hdr->frame_control) ||
+ (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
+ txrc.short_preamble = short_preamble = true;
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- struct ieee80211_supported_band *sband;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ rate_control_get_rate(tx->sdata, tx->sta, &txrc);
+
+ if (unlikely(info->control.rates[0].idx < 0))
+ return TX_DROP;
+
+ if (txrc.reported_rate.idx < 0)
+ txrc.reported_rate = info->control.rates[0];
if (tx->sta)
- info->control.sta = &tx->sta->sta;
+ tx->sta->last_tx_rate = txrc.reported_rate;
- if (!info->control.retry_limit) {
- if (!is_multicast_ether_addr(hdr->addr1)) {
- int len = min_t(int, tx->skb->len + FCS_LEN,
- tx->local->fragmentation_threshold);
- if (len > tx->local->rts_threshold
- && tx->local->rts_threshold <
- IEEE80211_MAX_RTS_THRESHOLD) {
- info->flags |= IEEE80211_TX_CTL_USE_RTS_CTS;
- info->flags |=
- IEEE80211_TX_CTL_LONG_RETRY_LIMIT;
- info->control.retry_limit =
- tx->local->long_retry_limit;
- } else {
- info->control.retry_limit =
- tx->local->short_retry_limit;
- }
- } else {
- info->control.retry_limit = 1;
- }
- }
+ if (unlikely(!info->control.rates[0].count))
+ info->control.rates[0].count = 1;
- if (tx->flags & IEEE80211_TX_FRAGMENTED) {
- /* Do not use multiple retry rates when sending fragmented
- * frames.
- * TODO: The last fragment could still use multiple retry
- * rates. */
- info->control.retries[0].rate_idx = -1;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ /*
+ * XXX: verify the rate is in the basic rateset
+ */
+ return TX_CONTINUE;
}
- /* Use CTS protection for unicast frames sent using extended rates if
- * there are associated non-ERP stations and RTS/CTS is not configured
- * for the frame. */
- if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_ERP_G) &&
- (tx->flags & IEEE80211_TX_UNICAST) &&
- tx->sdata->bss_conf.use_cts_prot &&
- !(info->flags & IEEE80211_TX_CTL_USE_RTS_CTS))
- info->flags |= IEEE80211_TX_CTL_USE_CTS_PROTECT;
-
- /* Transmit data frames using short preambles if the driver supports
- * short preambles at the selected rate and short preambles are
- * available on the network at the current point in time. */
- if (ieee80211_is_data(hdr->frame_control) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
- tx->sdata->bss_conf.use_short_preamble &&
- (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) {
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
+ /*
+ * set up the RTS/CTS rate as the fastest basic rate
+ * that is not faster than the data rate
+ *
+ * XXX: Should this check all retry rates?
+ */
+ if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ s8 baserate = 0;
+
+ rate = &sband->bitrates[info->control.rates[0].idx];
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ /* must be a basic rate */
+ if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i)))
+ continue;
+ /* must not be faster than the data rate */
+ if (sband->bitrates[i].bitrate > rate->bitrate)
+ continue;
+ /* maximum */
+ if (sband->bitrates[baserate].bitrate <
+ sband->bitrates[i].bitrate)
+ baserate = i;
+ }
+
+ info->control.rts_cts_rate_idx = baserate;
}
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
- struct ieee80211_rate *rate;
- s8 baserate = -1;
- int idx;
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /*
+ * make sure there's no valid rate following
+ * an invalid one, just in case drivers don't
+ * take the API seriously to stop at -1.
+ */
+ if (inval) {
+ info->control.rates[i].idx = -1;
+ continue;
+ }
+ if (info->control.rates[i].idx < 0) {
+ inval = true;
+ continue;
+ }
- /* Do not use multiple retry rates when using RTS/CTS */
- info->control.retries[0].rate_idx = -1;
+ /*
+ * For now assume MCS is already set up correctly, this
+ * needs to be fixed.
+ */
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) {
+ WARN_ON(info->control.rates[i].idx > 76);
+ continue;
+ }
- /* Use min(data rate, max base rate) as CTS/RTS rate */
- rate = &sband->bitrates[tx->rate_idx];
+ /* set up RTS protection if desired */
+ if (rts)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_RTS_CTS;
- for (idx = 0; idx < sband->n_bitrates; idx++) {
- if (sband->bitrates[idx].bitrate > rate->bitrate)
- continue;
- if (tx->sdata->bss_conf.basic_rates & BIT(idx) &&
- (baserate < 0 ||
- (sband->bitrates[baserate].bitrate
- < sband->bitrates[idx].bitrate)))
- baserate = idx;
+ /* RC is busted */
+ if (WARN_ON_ONCE(info->control.rates[i].idx >=
+ sband->n_bitrates)) {
+ info->control.rates[i].idx = -1;
+ continue;
}
- if (baserate >= 0)
- info->control.rts_cts_rate_idx = baserate;
- else
- info->control.rts_cts_rate_idx = 0;
+ rate = &sband->bitrates[info->control.rates[i].idx];
+
+ /* set up short preamble */
+ if (short_preamble &&
+ rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
+
+ /* set up G protection */
+ if (!rts && tx->sdata->vif.bss_conf.use_cts_prot &&
+ rate->flags & IEEE80211_RATE_ERP_G)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_CTS_PROTECT;
}
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
if (tx->sta)
info->control.sta = &tx->sta->sta;
@@ -602,8 +621,18 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
if (ieee80211_hdrlen(hdr->frame_control) < 24)
return TX_CONTINUE;
+ /*
+ * Anything but QoS data that has a sequence number field
+ * (is long enough) gets a sequence number from the global
+ * counter.
+ */
if (!ieee80211_is_data_qos(hdr->frame_control)) {
+ /* driver should assign sequence number */
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+ /* for pure STA mode without beacons, we can do it */
+ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
+ tx->sdata->sequence_number += 0x10;
+ tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ;
return TX_CONTINUE;
}
@@ -632,6 +661,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
struct sk_buff **frags, *first, *frag;
@@ -648,9 +678,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
* This scenario is handled in __ieee80211_tx_prepare but extra
* caution taken here as fragmented ampdu may cause Tx stop.
*/
- if (WARN_ON(tx->flags & IEEE80211_TX_CTL_AMPDU ||
- skb_get_queue_mapping(tx->skb) >=
- ieee80211_num_regular_queues(&tx->local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
return TX_DROP;
first = tx->skb;
@@ -684,20 +712,45 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
IEEE80211_ENCRYPT_TAILROOM);
if (!frag)
goto fail;
+
/* Make sure that all fragments use the same priority so
* that they end up using the same TX queue */
frag->priority = first->priority;
+
skb_reserve(frag, tx->local->tx_headroom +
IEEE80211_ENCRYPT_HEADROOM);
+
+ /* copy TX information */
+ info = IEEE80211_SKB_CB(frag);
+ memcpy(info, first->cb, sizeof(frag->cb));
+
+ /* copy/fill in 802.11 header */
fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
memcpy(fhdr, first->data, hdrlen);
- if (i == num_fragm - 2)
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+
+ if (i == num_fragm - 2) {
+ /* clear MOREFRAGS bit for the last fragment */
+ fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
+ } else {
+ /*
+ * No multi-rate retries for fragmented frames, that
+ * would completely throw off the NAV at other STAs.
+ */
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
+ info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ }
+
+ /* copy data */
copylen = left > per_fragm ? per_fragm : left;
memcpy(skb_put(frag, copylen), pos, copylen);
- memcpy(frag->cb, first->cb, sizeof(frag->cb));
+
skb_copy_queue_mapping(frag, first);
+
frag->do_not_encrypt = first->do_not_encrypt;
pos += copylen;
@@ -757,12 +810,10 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
tx->extra_frag[0]->len);
for (i = 0; i < tx->num_extra_frag; i++) {
- if (i + 1 < tx->num_extra_frag) {
+ if (i + 1 < tx->num_extra_frag)
next_len = tx->extra_frag[i + 1]->len;
- } else {
+ else
next_len = 0;
- tx->rate_idx = tx->last_frag_rate_idx;
- }
hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
hdr->duration_id = ieee80211_duration(tx, 0, next_len);
@@ -815,7 +866,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
sband = tx->local->hw.wiphy->bands[tx->channel->band];
@@ -829,8 +879,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
*/
while (!ret) {
- int i, target_rate;
-
ret = ieee80211_radiotap_iterator_next(&iterator);
if (ret)
@@ -844,38 +892,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
- case IEEE80211_RADIOTAP_RATE:
- /*
- * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
- * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
- */
- target_rate = (*iterator.this_arg) * 5;
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *r;
-
- r = &sband->bitrates[i];
-
- if (r->bitrate == target_rate) {
- tx->rate_idx = i;
- break;
- }
- }
- break;
-
- case IEEE80211_RADIOTAP_ANTENNA:
- /*
- * radiotap uses 0 for 1st ant, mac80211 is 1 for
- * 1st ant
- */
- info->antenna_sel_tx = (*iterator.this_arg) + 1;
- break;
-
-#if 0
- case IEEE80211_RADIOTAP_DBM_TX_POWER:
- control->power_level = *iterator.this_arg;
- break;
-#endif
-
case IEEE80211_RADIOTAP_FLAGS:
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
/*
@@ -933,7 +949,8 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int hdrlen;
+ int hdrlen, tid;
+ u8 *qc, *state;
memset(tx, 0, sizeof(*tx));
tx->skb = skb;
@@ -941,8 +958,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->local = local;
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
tx->channel = local->hw.conf.channel;
- tx->rate_idx = -1;
- tx->last_frag_rate_idx = -1;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
@@ -966,6 +981,15 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->sta = sta_info_get(local, hdr->addr1);
+ if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+
+ state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
+ if (*state == HT_AGG_STATE_OPERATIONAL)
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
+ }
+
if (is_multicast_ether_addr(hdr->addr1)) {
tx->flags &= ~IEEE80211_TX_UNICAST;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
@@ -977,7 +1001,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
if ((tx->flags & IEEE80211_TX_UNICAST) &&
skb->len + FCS_LEN > local->fragmentation_threshold &&
- !local->ops->set_frag_threshold &&
!(info->flags & IEEE80211_TX_CTL_AMPDU))
tx->flags |= IEEE80211_TX_FRAGMENTED;
else
@@ -1043,23 +1066,11 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
if (!tx->extra_frag[i])
continue;
info = IEEE80211_SKB_CB(tx->extra_frag[i]);
- info->flags &= ~(IEEE80211_TX_CTL_USE_RTS_CTS |
- IEEE80211_TX_CTL_USE_CTS_PROTECT |
- IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
if (netif_subqueue_stopped(local->mdev,
tx->extra_frag[i]))
return IEEE80211_TX_FRAG_AGAIN;
- if (i == tx->num_extra_frag) {
- info->tx_rate_idx = tx->last_frag_rate_idx;
-
- if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
- info->flags |=
- IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- else
- info->flags &=
- ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- }
ret = local->ops->tx(local_to_hw(local),
tx->extra_frag[i]);
@@ -1168,7 +1179,7 @@ retry:
* queues, there's no reason for a driver to reject
* a frame there, warn and drop it.
*/
- if (WARN_ON(queue >= ieee80211_num_regular_queues(&local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
goto drop;
store = &local->pending_packet[queue];
@@ -1196,9 +1207,6 @@ retry:
store->skb = skb;
store->extra_frag = tx.extra_frag;
store->num_extra_frag = tx.num_extra_frag;
- store->last_frag_rate_idx = tx.last_frag_rate_idx;
- store->last_frag_rate_ctrl_probe =
- !!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
}
out:
rcu_read_unlock();
@@ -1465,6 +1473,19 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0) {
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+ queue_work(local->hw.workqueue,
+ &local->dynamic_ps_disable_work);
+ }
+
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ }
+
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
@@ -1593,12 +1614,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
-
if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped frame to %s"
+ printk(KERN_DEBUG "%s: dropped frame to %pM"
" (unauthorized port)\n", dev->name,
- print_mac(mac, hdr.addr1));
+ hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
@@ -1757,10 +1776,7 @@ void ieee80211_tx_pending(unsigned long data)
store = &local->pending_packet[i];
tx.extra_frag = store->extra_frag;
tx.num_extra_frag = store->num_extra_frag;
- tx.last_frag_rate_idx = store->last_frag_rate_idx;
tx.flags = 0;
- if (store->last_frag_rate_ctrl_probe)
- tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
ret = __ieee80211_tx(local, store->skb, &tx);
if (ret) {
if (ret == IEEE80211_TX_FRAG_AGAIN)
@@ -1775,8 +1791,7 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
-static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss,
+static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
struct sk_buff *skb,
struct beacon_data *beacon)
{
@@ -1844,11 +1859,9 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
struct ieee80211_if_sta *ifsta = NULL;
- struct rate_selection rsel;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
enum ieee80211_band band = local->hw.conf.channel->band;
@@ -1858,7 +1871,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
rcu_read_lock();
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
@@ -1886,12 +1898,12 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
} else {
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
@@ -1952,33 +1964,23 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
skb->do_not_encrypt = 1;
info->band = band;
- rate_control_get_rate(sdata, sband, NULL, skb, &rsel);
-
- if (unlikely(rsel.rate_idx < 0)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
- "no rate found\n",
- wiphy_name(local->hw.wiphy));
- }
- dev_kfree_skb_any(skb);
- skb = NULL;
- goto out;
- }
+ /*
+ * XXX: For now, always use the lowest rate
+ */
+ info->control.rates[0].idx = 0;
+ info->control.rates[0].count = 1;
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
info->control.vif = vif;
- info->tx_rate_idx = rsel.rate_idx;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
- if (sdata->bss_conf.use_short_preamble &&
- sband->bitrates[rsel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
-
- info->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- info->control.retry_limit = 1;
-
-out:
+ out:
rcu_read_unlock();
return skb;
}
@@ -2023,14 +2025,12 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct sta_info *sta;
struct ieee80211_tx_data tx;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
struct ieee80211_tx_info *info;
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
bss = &sdata->u.ap;
if (!bss)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index cee4884b9d06..fb89e1d0aa03 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -239,7 +239,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -272,7 +272,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -312,7 +312,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -330,10 +330,20 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw)) {
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+ if (local->queue_stop_reasons[queue] != 0)
+ /* someone still has this queue stopped */
+ return;
+ }
+
if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet);
@@ -341,22 +351,74 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
netif_wake_subqueue(local->mdev, queue);
}
}
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_wake_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_wake_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_wake_queue);
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw))
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
+
netif_stop_subqueue(local->mdev, queue);
}
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_stop_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_stop_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_stop_queue);
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < ieee80211_num_queues(hw); i++)
- ieee80211_stop_queue(hw, i);
+ __ieee80211_stop_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_stop_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -367,12 +429,24 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
- ieee80211_wake_queue(hw, i);
+ __ieee80211_wake_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
@@ -532,8 +606,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
if (elen >= sizeof(struct ieee80211_ht_cap))
elems->ht_cap_elem = (void *)pos;
break;
- case WLAN_EID_HT_EXTRA_INFO:
- if (elen >= sizeof(struct ieee80211_ht_addt_info))
+ case WLAN_EID_HT_INFORMATION:
+ if (elen >= sizeof(struct ieee80211_ht_info))
elems->ht_info_elem = (void *)pos;
break;
case WLAN_EID_MESH_ID:
@@ -638,19 +712,16 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
- "%d MHz\n", sdata->dev->name, chan->center_freq);
+ chan->flags & IEEE80211_CHAN_NO_IBSS)
return ret;
- }
local->oper_channel = chan;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
if (local->sw_scanning || local->hw_scanning)
ret = 0;
else
- ret = ieee80211_hw_config(local);
-
- rate_control_clear(local);
+ ret = ieee80211_hw_config(
+ local, IEEE80211_CONF_CHANGE_CHANNEL);
}
return ret;
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index f0e2d3ecb5c4..7043ddc75498 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
+#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
@@ -49,17 +50,19 @@ void ieee80211_wep_free(struct ieee80211_local *local)
crypto_free_blkcipher(local->wep_rx_tfm);
}
-static inline int ieee80211_wep_weak_iv(u32 iv, int keylen)
+static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen)
{
- /* Fluhrer, Mantin, and Shamir have reported weaknesses in the
+ /*
+ * Fluhrer, Mantin, and Shamir have reported weaknesses in the
* key scheduling algorithm of RC4. At least IVs (KeyByte + 3,
- * 0xff, N) can be used to speedup attacks, so avoid using them. */
+ * 0xff, N) can be used to speedup attacks, so avoid using them.
+ */
if ((iv & 0xff00) == 0xff00) {
u8 B = (iv >> 16) & 0xff;
if (B >= 3 && B < 3 + keylen)
- return 1;
+ return true;
}
- return 0;
+ return false;
}
@@ -123,10 +126,10 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
{
struct blkcipher_desc desc = { .tfm = tfm };
struct scatterlist sg;
- __le32 *icv;
+ __le32 icv;
- icv = (__le32 *)(data + data_len);
- *icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ put_unaligned(icv, (__le32 *)(data + data_len));
crypto_blkcipher_setkey(tfm, rc4key, klen);
sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
@@ -268,7 +271,7 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
}
-u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
unsigned int hdrlen;
@@ -276,16 +279,13 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
u32 iv;
if (!ieee80211_has_protected(hdr->frame_control))
- return NULL;
+ return false;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
ivpos = skb->data + hdrlen;
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
- if (ieee80211_wep_weak_iv(iv, key->conf.keylen))
- return ivpos;
-
- return NULL;
+ return ieee80211_wep_weak_iv(iv, key->conf.keylen);
}
ieee80211_rx_result
@@ -329,6 +329,8 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
+ int i;
+
ieee80211_tx_set_protected(tx);
if (wep_encrypt_skb(tx, tx->skb) < 0) {
@@ -337,9 +339,8 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
}
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
+ if (wep_encrypt_skb(tx, tx->extra_frag[i])) {
I802_DEBUG_INC(tx->local->
tx_handlers_drop_wep);
return TX_DROP;
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index e587172115b8..d3f0db48314e 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -26,7 +26,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
-u8 *ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index ab4ddba874be..7162d5816f39 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -135,48 +135,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
return -EOPNOTSUPP;
}
-static int ieee80211_ioctl_giwname(struct net_device *dev,
- struct iw_request_info *info,
- char *name, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_supported_band *sband;
- u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0;
-
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ];
- if (sband) {
- is_a = 1;
- is_ht |= sband->ht_info.ht_supported;
- }
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ];
- if (sband) {
- int i;
- /* Check for mandatory rates */
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].bitrate == 10)
- is_b = 1;
- if (sband->bitrates[i].bitrate == 60)
- is_g = 1;
- }
- is_ht |= sband->ht_info.ht_supported;
- }
-
- strcpy(name, "IEEE 802.11");
- if (is_a)
- strcat(name, "a");
- if (is_b)
- strcat(name, "b");
- if (is_g)
- strcat(name, "g");
- if (is_ht)
- strcat(name, "n");
-
- return 0;
-}
-
-
static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
@@ -266,78 +224,6 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
}
-static int ieee80211_ioctl_siwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
- int type;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- return -EOPNOTSUPP;
-
- switch (*mode) {
- case IW_MODE_INFRA:
- type = NL80211_IFTYPE_STATION;
- break;
- case IW_MODE_ADHOC:
- /* Setting ad-hoc mode on non ibss channel is not
- * supported.
- */
- if (local->oper_channel &&
- (local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS))
- return -EOPNOTSUPP;
-
- type = NL80211_IFTYPE_ADHOC;
- break;
- case IW_MODE_REPEAT:
- type = NL80211_IFTYPE_WDS;
- break;
- case IW_MODE_MONITOR:
- type = NL80211_IFTYPE_MONITOR;
- break;
- default:
- return -EINVAL;
- }
-
- return ieee80211_if_change_type(sdata, type);
-}
-
-
-static int ieee80211_ioctl_giwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- *mode = IW_MODE_MASTER;
- break;
- case NL80211_IFTYPE_STATION:
- *mode = IW_MODE_INFRA;
- break;
- case NL80211_IFTYPE_ADHOC:
- *mode = IW_MODE_ADHOC;
- break;
- case NL80211_IFTYPE_MONITOR:
- *mode = IW_MODE_MONITOR;
- break;
- case NL80211_IFTYPE_WDS:
- *mode = IW_MODE_REPEAT;
- break;
- case NL80211_IFTYPE_AP_VLAN:
- *mode = IW_MODE_SECOND; /* FIXME */
- break;
- default:
- *mode = IW_MODE_AUTO;
- break;
- }
- return 0;
-}
-
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
@@ -415,13 +301,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
return 0;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- memcpy(sdata->u.ap.ssid, ssid, len);
- memset(sdata->u.ap.ssid + len, 0,
- IEEE80211_MAX_SSID_LEN - len);
- sdata->u.ap.ssid_len = len;
- return ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- }
return -EOPNOTSUPP;
}
@@ -445,15 +324,6 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
return res;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- len = sdata->u.ap.ssid_len;
- if (len > IW_ESSID_MAX_SIZE)
- len = IW_ESSID_MAX_SIZE;
- memcpy(ssid, sdata->u.ap.ssid, len);
- data->length = len;
- data->flags = 1;
- return 0;
- }
return -EOPNOTSUPP;
}
@@ -548,8 +418,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
- sdata->vif.type != NL80211_IFTYPE_AP)
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/* if SSID was specified explicitly then use that */
@@ -644,8 +513,8 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta && sta->last_txrate_idx < sband->n_bitrates)
- rate->value = sband->bitrates[sta->last_txrate_idx].bitrate;
+ if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
+ rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
else
rate->value = 0;
@@ -664,45 +533,35 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
union iwreq_data *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- bool need_reconfig = 0;
+ struct ieee80211_channel* chan = local->hw.conf.channel;
+ u32 reconf_flags = 0;
int new_power_level;
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
return -EINVAL;
if (data->txpower.flags & IW_TXPOW_RANGE)
return -EINVAL;
+ if (!chan)
+ return -EINVAL;
- if (data->txpower.fixed) {
- new_power_level = data->txpower.value;
- } else {
- /*
- * Automatic power level. Use maximum power for the current
- * channel. Should be part of rate control.
- */
- struct ieee80211_channel* chan = local->hw.conf.channel;
- if (!chan)
- return -EINVAL;
-
+ if (data->txpower.fixed)
+ new_power_level = min(data->txpower.value, chan->max_power);
+ else /* Automatic power level setting */
new_power_level = chan->max_power;
- }
if (local->hw.conf.power_level != new_power_level) {
local->hw.conf.power_level = new_power_level;
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
}
if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
local->hw.conf.radio_enabled = !(data->txpower.disabled);
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
- if (need_reconfig) {
- ieee80211_hw_config(local);
- /* The return value of hw_config is not of big interest here,
- * as it doesn't say that it failed because of _this_ config
- * change or something else. Ignore it. */
- }
+ if (reconf_flags)
+ ieee80211_hw_config(local, reconf_flags);
return 0;
}
@@ -779,14 +638,6 @@ static int ieee80211_ioctl_siwfrag(struct net_device *dev,
local->fragmentation_threshold = frag->value & ~0x1;
}
- /* If the wlan card performs fragmentation in hardware/firmware,
- * configure it here */
-
- if (local->ops->set_frag_threshold)
- return local->ops->set_frag_threshold(
- local_to_hw(local),
- local->fragmentation_threshold);
-
return 0;
}
@@ -814,21 +665,16 @@ static int ieee80211_ioctl_siwretry(struct net_device *dev,
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
return -EINVAL;
- if (retry->flags & IW_RETRY_MAX)
- local->long_retry_limit = retry->value;
- else if (retry->flags & IW_RETRY_MIN)
- local->short_retry_limit = retry->value;
- else {
- local->long_retry_limit = retry->value;
- local->short_retry_limit = retry->value;
+ if (retry->flags & IW_RETRY_MAX) {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ } else if (retry->flags & IW_RETRY_MIN) {
+ local->hw.conf.short_frame_max_tx_count = retry->value;
+ } else {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ local->hw.conf.short_frame_max_tx_count = retry->value;
}
- if (local->ops->set_retry_limit) {
- return local->ops->set_retry_limit(
- local_to_hw(local),
- local->short_retry_limit,
- local->long_retry_limit);
- }
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
return 0;
}
@@ -845,14 +691,15 @@ static int ieee80211_ioctl_giwretry(struct net_device *dev,
/* first return min value, iwconfig will ask max value
* later if needed */
retry->flags |= IW_RETRY_LIMIT;
- retry->value = local->short_retry_limit;
- if (local->long_retry_limit != local->short_retry_limit)
+ retry->value = local->hw.conf.short_frame_max_tx_count;
+ if (local->hw.conf.long_frame_max_tx_count !=
+ local->hw.conf.short_frame_max_tx_count)
retry->flags |= IW_RETRY_MIN;
return 0;
}
if (retry->flags & IW_RETRY_MAX) {
retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
- retry->value = local->long_retry_limit;
+ retry->value = local->hw.conf.long_frame_max_tx_count;
}
return 0;
@@ -983,25 +830,56 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
struct iw_param *wrq,
char *extra)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
+ int ret = 0, timeout = 0;
+ bool ps;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
if (wrq->disabled) {
- conf->flags &= ~IEEE80211_CONF_PS;
- return ieee80211_hw_config(local);
+ ps = false;
+ timeout = 0;
+ goto set;
}
switch (wrq->flags & IW_POWER_MODE) {
case IW_POWER_ON: /* If not specified */
case IW_POWER_MODE: /* If set all mask */
case IW_POWER_ALL_R: /* If explicitely state all */
- conf->flags |= IEEE80211_CONF_PS;
+ ps = true;
break;
- default: /* Otherwise we don't support it */
- return -EINVAL;
+ default: /* Otherwise we ignore */
+ break;
+ }
+
+ if (wrq->flags & IW_POWER_TIMEOUT)
+ timeout = wrq->value / 1000;
+
+set:
+ if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
+ return ret;
+
+ local->powersave = ps;
+ local->dynamic_ps_timeout = timeout;
+
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ if (local->powersave)
+ conf->flags |= IEEE80211_CONF_PS;
+ else
+ conf->flags &= ~IEEE80211_CONF_PS;
+ }
+ ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- return ieee80211_hw_config(local);
+ return ret;
}
static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -1010,9 +888,8 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_conf *conf = &local->hw.conf;
- wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);
+ wrqu->power.disabled = !local->powersave;
return 0;
}
@@ -1176,13 +1053,13 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
static const iw_handler ieee80211_handler[] =
{
(iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */
+ (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */
(iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */
- (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */
- (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */
+ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
+ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 139b5f267b34..ac71b38f7cb5 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -114,8 +114,8 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ieee80211_master_priv *mpriv = netdev_priv(dev);
struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct sta_info *sta;
u16 queue;
u8 tid;
@@ -124,21 +124,19 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
if (unlikely(queue >= local->hw.queues))
queue = local->hw.queues - 1;
- if (info->flags & IEEE80211_TX_CTL_REQUEUE) {
+ if (skb->requeue) {
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (sta) {
- struct ieee80211_hw *hw = &local->hw;
int ampdu_queue = sta->tid_to_tx_q[tid];
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -159,20 +157,18 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
*p++ = ack_policy | tid;
*p = 0;
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
- struct ieee80211_hw *hw = &local->hw;
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -206,13 +202,11 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
* on the previous queue
* since HT is strict in order */
#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit()) {
- DECLARE_MAC_BUF(mac);
+ if (net_ratelimit())
printk(KERN_DEBUG "allocated aggregation queue"
- " %d tid %d addr %s pool=0x%lX\n",
- i, tid, print_mac(mac, sta->sta.addr),
+ " %d tid %d addr %pM pool=0x%lX\n",
+ i, tid, sta->sta.addr,
local->queue_pool[0]);
- }
#endif /* CONFIG_MAC80211_HT_DEBUG */
return 0;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 6db649480e8f..7aa63caf8d50 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -49,8 +49,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
!(tx->flags & IEEE80211_TX_FRAGMENTED) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
!wpa_test) {
- /* hwaccel - with no need for preallocated room for Michael MIC
- */
+ /* hwaccel - with no need for preallocated room for MMIC */
return TX_CONTINUE;
}
@@ -67,8 +66,6 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
@@ -90,11 +87,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
int authenticator = 1, wpa_test = 0;
- DECLARE_MAC_BUF(mac);
- /*
- * No way to verify the MIC if the hardware stripped it
- */
+ /* No way to verify the MIC if the hardware stripped it */
if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
return RX_CONTINUE;
@@ -116,8 +110,6 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
@@ -202,6 +194,7 @@ ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -209,9 +202,8 @@ ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (tkip_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (tkip_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -227,7 +219,6 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -350,7 +341,7 @@ static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
}
-static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
+static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
{
pn[0] = hdr[7];
pn[1] = hdr[6];
@@ -358,7 +349,6 @@ static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
pn[3] = hdr[4];
pn[4] = hdr[1];
pn[5] = hdr[0];
- return (hdr[3] >> 6) & 0x03;
}
@@ -373,7 +363,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
- /* hwaccel - with no need for preallocated room for CCMP "
+ /* hwaccel - with no need for preallocated room for CCMP
* header or MIC fields */
info->control.hw_key = &tx->key->conf;
return 0;
@@ -426,6 +416,7 @@ ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -433,9 +424,8 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (ccmp_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (ccmp_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -453,7 +443,6 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
u8 pn[CCMP_PN_LEN];
int data_len;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -468,7 +457,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
(rx->status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
- (void) ccmp_hdr2pn(pn, skb->data + hdrlen);
+ ccmp_hdr2pn(pn, skb->data + hdrlen);
if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
@@ -483,9 +472,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf,
skb->data + hdrlen + CCMP_HDR_LEN, data_len,
skb->data + skb->len - CCMP_MIC_LEN,
- skb->data + hdrlen + CCMP_HDR_LEN)) {
+ skb->data + hdrlen + CCMP_HDR_LEN))
return RX_DROP_UNUSABLE;
- }
}
memcpy(key->u.ccmp.rx_pn[rx->queue], pn, CCMP_PN_LEN);
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 25dcef9f2194..c2bac9cd0caf 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -373,11 +373,10 @@ config NETFILTER_XT_TARGET_MARK
config NETFILTER_XT_TARGET_NFLOG
tristate '"NFLOG" target support'
default m if NETFILTER_ADVANCED=n
+ select NETFILTER_NETLINK_LOG
help
This option enables the NFLOG target, which allows to LOG
- messages through the netfilter logging API, which can use
- either the old LOG target, the old ULOG target or nfnetlink_log
- as backend.
+ messages through nfnetlink_log.
To compile it as a module, choose M here. If unsure, say N.
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index 9a24332fbed8..60aba45023ff 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -820,13 +820,11 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v)
#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6)
- seq_printf(seq,
- "%-3s " NIP6_FMT " %04X " NIP6_FMT
- " %04X " NIP6_FMT " %04X %-11s %7lu\n",
+ seq_printf(seq, "%-3s %pI6 %04X %pI6 %04X %pI6 %04X %-11s %7lu\n",
ip_vs_proto_name(cp->protocol),
- NIP6(cp->caddr.in6), ntohs(cp->cport),
- NIP6(cp->vaddr.in6), ntohs(cp->vport),
- NIP6(cp->daddr.in6), ntohs(cp->dport),
+ &cp->caddr.in6, ntohs(cp->cport),
+ &cp->vaddr.in6, ntohs(cp->vport),
+ &cp->daddr.in6, ntohs(cp->dport),
ip_vs_state_name(cp->protocol, cp->state),
(cp->timer.expires-jiffies)/HZ);
else
@@ -883,13 +881,11 @@ static int ip_vs_conn_sync_seq_show(struct seq_file *seq, void *v)
#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6)
- seq_printf(seq,
- "%-3s " NIP6_FMT " %04X " NIP6_FMT
- " %04X " NIP6_FMT " %04X %-11s %-6s %7lu\n",
+ seq_printf(seq, "%-3s %pI6 %04X %pI6 %04X %pI6 %04X %-11s %-6s %7lu\n",
ip_vs_proto_name(cp->protocol),
- NIP6(cp->caddr.in6), ntohs(cp->cport),
- NIP6(cp->vaddr.in6), ntohs(cp->vport),
- NIP6(cp->daddr.in6), ntohs(cp->dport),
+ &cp->caddr.in6, ntohs(cp->cport),
+ &cp->vaddr.in6, ntohs(cp->vport),
+ &cp->daddr.in6, ntohs(cp->dport),
ip_vs_state_name(cp->protocol, cp->state),
ip_vs_origin_name(cp->flags),
(cp->timer.expires-jiffies)/HZ);
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 958abf3e5f8c..cb3e031335eb 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -730,9 +730,9 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %u.%u.%u.%u->%u.%u.%u.%u\n",
+ IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
- NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ &iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
@@ -805,9 +805,9 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related)
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n",
+ IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) %pI6->%pI6\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
- NIP6(iph->saddr), NIP6(iph->daddr));
+ &iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
@@ -1070,9 +1070,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Incoming ICMP (%d,%d) %u.%u.%u.%u->%u.%u.%u.%u\n",
+ IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
- NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ &iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
@@ -1127,8 +1127,8 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
/* Ensure the checksum is correct */
if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
/* Failed checksum! */
- IP_VS_DBG(1, "Incoming ICMP: failed checksum from %d.%d.%d.%d!\n",
- NIPQUAD(iph->saddr));
+ IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
+ &iph->saddr);
goto out;
}
@@ -1175,9 +1175,9 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) " NIP6_FMT "->" NIP6_FMT "\n",
+ IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) %pI6->%pI6\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
- NIP6(iph->saddr), NIP6(iph->daddr));
+ &iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 0302cf3e5039..e01061f49cdc 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1168,15 +1168,9 @@ ip_vs_add_service(struct ip_vs_service_user_kern *u,
}
#ifdef CONFIG_IP_VS_IPV6
- if (u->af == AF_INET6) {
- if (!sched->supports_ipv6) {
- ret = -EAFNOSUPPORT;
- goto out_err;
- }
- if ((u->netmask < 1) || (u->netmask > 128)) {
- ret = -EINVAL;
- goto out_err;
- }
+ if (u->af == AF_INET6 && (u->netmask < 1 || u->netmask > 128)) {
+ ret = -EINVAL;
+ goto out_err;
}
#endif
@@ -1272,15 +1266,9 @@ ip_vs_edit_service(struct ip_vs_service *svc, struct ip_vs_service_user_kern *u)
old_sched = sched;
#ifdef CONFIG_IP_VS_IPV6
- if (u->af == AF_INET6) {
- if (!sched->supports_ipv6) {
- ret = -EAFNOSUPPORT;
- goto out;
- }
- if ((u->netmask < 1) || (u->netmask > 128)) {
- ret = -EINVAL;
- goto out;
- }
+ if (u->af == AF_INET6 && (u->netmask < 1 || u->netmask > 128)) {
+ ret = -EINVAL;
+ goto out;
}
#endif
@@ -1557,7 +1545,7 @@ static struct ctl_table vs_vars[] = {
.data = &sysctl_ip_vs_amemthresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#ifdef CONFIG_IP_VS_DEBUG
{
@@ -1565,7 +1553,7 @@ static struct ctl_table vs_vars[] = {
.data = &sysctl_ip_vs_debug_level,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
#endif
{
@@ -1573,28 +1561,28 @@ static struct ctl_table vs_vars[] = {
.data = &sysctl_ip_vs_am_droprate,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "drop_entry",
.data = &sysctl_ip_vs_drop_entry,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_do_defense_mode,
+ .proc_handler = proc_do_defense_mode,
},
{
.procname = "drop_packet",
.data = &sysctl_ip_vs_drop_packet,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_do_defense_mode,
+ .proc_handler = proc_do_defense_mode,
},
{
.procname = "secure_tcp",
.data = &sysctl_ip_vs_secure_tcp,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_do_defense_mode,
+ .proc_handler = proc_do_defense_mode,
},
#if 0
{
@@ -1602,84 +1590,84 @@ static struct ctl_table vs_vars[] = {
.data = &vs_timeout_table_dos.timeout[IP_VS_S_ESTABLISHED],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_synsent",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_SENT],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_synrecv",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_SYN_RECV],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_finwait",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_FIN_WAIT],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_timewait",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_TIME_WAIT],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_close",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_closewait",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_CLOSE_WAIT],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_lastack",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_LAST_ACK],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_listen",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_LISTEN],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_synack",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_SYNACK],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_udp",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_UDP],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "timeout_icmp",
.data = &vs_timeout_table_dos.timeout[IP_VS_S_ICMP],
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
#endif
{
@@ -1687,35 +1675,35 @@ static struct ctl_table vs_vars[] = {
.data = &sysctl_ip_vs_cache_bypass,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "expire_nodest_conn",
.data = &sysctl_ip_vs_expire_nodest_conn,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "expire_quiescent_template",
.data = &sysctl_ip_vs_expire_quiescent_template,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.procname = "sync_threshold",
.data = &sysctl_ip_vs_sync_threshold,
.maxlen = sizeof(sysctl_ip_vs_sync_threshold),
.mode = 0644,
- .proc_handler = &proc_do_sync_threshold,
+ .proc_handler = proc_do_sync_threshold,
},
{
.procname = "nat_icmp_send",
.data = &sysctl_ip_vs_nat_icmp_send,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ .ctl_name = 0 }
};
@@ -1867,9 +1855,9 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
if (iter->table == ip_vs_svc_table) {
#ifdef CONFIG_IP_VS_IPV6
if (svc->af == AF_INET6)
- seq_printf(seq, "%s [" NIP6_FMT "]:%04X %s ",
+ seq_printf(seq, "%s [%pI6]:%04X %s ",
ip_vs_proto_name(svc->protocol),
- NIP6(svc->addr.in6),
+ &svc->addr.in6,
ntohs(svc->port),
svc->scheduler->name);
else
@@ -1895,9 +1883,9 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
#ifdef CONFIG_IP_VS_IPV6
if (dest->af == AF_INET6)
seq_printf(seq,
- " -> [" NIP6_FMT "]:%04X"
+ " -> [%pI6]:%04X"
" %-7s %-6d %-10d %-10d\n",
- NIP6(dest->addr.in6),
+ &dest->addr.in6,
ntohs(dest->port),
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
@@ -2141,8 +2129,8 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
/* Check for valid protocol: TCP or UDP, even for fwmark!=0 */
if (usvc.protocol != IPPROTO_TCP && usvc.protocol != IPPROTO_UDP) {
- IP_VS_ERR("set_ctl: invalid protocol: %d %d.%d.%d.%d:%d %s\n",
- usvc.protocol, NIPQUAD(usvc.addr.ip),
+ IP_VS_ERR("set_ctl: invalid protocol: %d %pI4:%d %s\n",
+ usvc.protocol, &usvc.addr.ip,
ntohs(usvc.port), usvc.sched_name);
ret = -EFAULT;
goto out_unlock;
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index a16943fd72f1..a9dac74bb13f 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -64,9 +64,16 @@ struct ip_vs_dh_bucket {
/*
* Returns hash value for IPVS DH entry
*/
-static inline unsigned ip_vs_dh_hashkey(__be32 addr)
+static inline unsigned ip_vs_dh_hashkey(int af, const union nf_inet_addr *addr)
{
- return (ntohl(addr)*2654435761UL) & IP_VS_DH_TAB_MASK;
+ __be32 addr_fold = addr->ip;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6)
+ addr_fold = addr->ip6[0]^addr->ip6[1]^
+ addr->ip6[2]^addr->ip6[3];
+#endif
+ return (ntohl(addr_fold)*2654435761UL) & IP_VS_DH_TAB_MASK;
}
@@ -74,9 +81,10 @@ static inline unsigned ip_vs_dh_hashkey(__be32 addr)
* Get ip_vs_dest associated with supplied parameters.
*/
static inline struct ip_vs_dest *
-ip_vs_dh_get(struct ip_vs_dh_bucket *tbl, __be32 addr)
+ip_vs_dh_get(int af, struct ip_vs_dh_bucket *tbl,
+ const union nf_inet_addr *addr)
{
- return (tbl[ip_vs_dh_hashkey(addr)]).dest;
+ return (tbl[ip_vs_dh_hashkey(af, addr)]).dest;
}
@@ -202,12 +210,14 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{
struct ip_vs_dest *dest;
struct ip_vs_dh_bucket *tbl;
- struct iphdr *iph = ip_hdr(skb);
+ struct ip_vs_iphdr iph;
+
+ ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
IP_VS_DBG(6, "ip_vs_dh_schedule(): Scheduling...\n");
tbl = (struct ip_vs_dh_bucket *)svc->sched_data;
- dest = ip_vs_dh_get(tbl, iph->daddr);
+ dest = ip_vs_dh_get(svc->af, tbl, &iph.daddr);
if (!dest
|| !(dest->flags & IP_VS_DEST_F_AVAILABLE)
|| atomic_read(&dest->weight) <= 0
@@ -215,11 +225,10 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
return NULL;
}
- IP_VS_DBG(6, "DH: destination IP address %u.%u.%u.%u "
- "--> server %u.%u.%u.%u:%d\n",
- NIPQUAD(iph->daddr),
- NIPQUAD(dest->addr.ip),
- ntohs(dest->port));
+ IP_VS_DBG_BUF(6, "DH: destination IP address %s --> server %s:%d\n",
+ IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+ IP_VS_DBG_ADDR(svc->af, &dest->addr),
+ ntohs(dest->port));
return dest;
}
@@ -234,9 +243,6 @@ static struct ip_vs_scheduler ip_vs_dh_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_dh_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 0,
-#endif
.init_service = ip_vs_dh_init_svc,
.done_service = ip_vs_dh_done_svc,
.update_service = ip_vs_dh_update_svc,
diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c
index 2e7dbd8b73a4..428edbf481cc 100644
--- a/net/netfilter/ipvs/ip_vs_ftp.c
+++ b/net/netfilter/ipvs/ip_vs_ftp.c
@@ -178,10 +178,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
&start, &end) != 1)
return 1;
- IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> "
- "%u.%u.%u.%u:%d detected\n",
- NIPQUAD(from.ip), ntohs(port),
- NIPQUAD(cp->caddr.ip), 0);
+ IP_VS_DBG(7, "PASV response (%pI4:%d) -> %pI4:%d detected\n",
+ &from.ip, ntohs(port), &cp->caddr.ip, 0);
/*
* Now update or create an connection entry for it
@@ -312,8 +310,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
&start, &end) != 1)
return 1;
- IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n",
- NIPQUAD(to.ip), ntohs(port));
+ IP_VS_DBG(7, "PORT %pI4:%d detected\n", &to.ip, ntohs(port));
/* Passive mode off */
cp->app_data = NULL;
@@ -321,9 +318,9 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
/*
* Now update or create a connection entry for it
*/
- IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n",
+ IP_VS_DBG(7, "protocol %s %pI4:%d %pI4:%d\n",
ip_vs_proto_name(iph->protocol),
- NIPQUAD(to.ip), ntohs(port), NIPQUAD(cp->vaddr.ip), 0);
+ &to.ip, ntohs(port), &cp->vaddr.ip, 0);
n_cp = ip_vs_conn_in_get(AF_INET, iph->protocol,
&to, port,
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 6ecef3518cac..3eb5e2660c49 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -86,7 +86,8 @@ static int sysctl_ip_vs_lblc_expiration = 24*60*60*HZ;
*/
struct ip_vs_lblc_entry {
struct list_head list;
- __be32 addr; /* destination IP address */
+ int af; /* address family */
+ union nf_inet_addr addr; /* destination IP address */
struct ip_vs_dest *dest; /* real server (cache) */
unsigned long lastuse; /* last used time */
};
@@ -115,7 +116,7 @@ static ctl_table vs_vars_table[] = {
.data = &sysctl_ip_vs_lblc_expiration,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{ .ctl_name = 0 }
};
@@ -137,9 +138,17 @@ static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en)
/*
* Returns hash value for IPVS LBLC entry
*/
-static inline unsigned ip_vs_lblc_hashkey(__be32 addr)
+static inline unsigned
+ip_vs_lblc_hashkey(int af, const union nf_inet_addr *addr)
{
- return (ntohl(addr)*2654435761UL) & IP_VS_LBLC_TAB_MASK;
+ __be32 addr_fold = addr->ip;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6)
+ addr_fold = addr->ip6[0]^addr->ip6[1]^
+ addr->ip6[2]^addr->ip6[3];
+#endif
+ return (ntohl(addr_fold)*2654435761UL) & IP_VS_LBLC_TAB_MASK;
}
@@ -150,7 +159,7 @@ static inline unsigned ip_vs_lblc_hashkey(__be32 addr)
static void
ip_vs_lblc_hash(struct ip_vs_lblc_table *tbl, struct ip_vs_lblc_entry *en)
{
- unsigned hash = ip_vs_lblc_hashkey(en->addr);
+ unsigned hash = ip_vs_lblc_hashkey(en->af, &en->addr);
list_add(&en->list, &tbl->bucket[hash]);
atomic_inc(&tbl->entries);
@@ -162,13 +171,14 @@ ip_vs_lblc_hash(struct ip_vs_lblc_table *tbl, struct ip_vs_lblc_entry *en)
* lock
*/
static inline struct ip_vs_lblc_entry *
-ip_vs_lblc_get(struct ip_vs_lblc_table *tbl, __be32 addr)
+ip_vs_lblc_get(int af, struct ip_vs_lblc_table *tbl,
+ const union nf_inet_addr *addr)
{
- unsigned hash = ip_vs_lblc_hashkey(addr);
+ unsigned hash = ip_vs_lblc_hashkey(af, addr);
struct ip_vs_lblc_entry *en;
list_for_each_entry(en, &tbl->bucket[hash], list)
- if (en->addr == addr)
+ if (ip_vs_addr_equal(af, &en->addr, addr))
return en;
return NULL;
@@ -180,12 +190,12 @@ ip_vs_lblc_get(struct ip_vs_lblc_table *tbl, __be32 addr)
* address to a server. Called under write lock.
*/
static inline struct ip_vs_lblc_entry *
-ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, __be32 daddr,
+ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
struct ip_vs_dest *dest)
{
struct ip_vs_lblc_entry *en;
- en = ip_vs_lblc_get(tbl, daddr);
+ en = ip_vs_lblc_get(dest->af, tbl, daddr);
if (!en) {
en = kmalloc(sizeof(*en), GFP_ATOMIC);
if (!en) {
@@ -193,7 +203,8 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, __be32 daddr,
return NULL;
}
- en->addr = daddr;
+ en->af = dest->af;
+ ip_vs_addr_copy(dest->af, &en->addr, daddr);
en->lastuse = jiffies;
atomic_inc(&dest->refcnt);
@@ -369,7 +380,7 @@ static int ip_vs_lblc_done_svc(struct ip_vs_service *svc)
static inline struct ip_vs_dest *
-__ip_vs_lblc_schedule(struct ip_vs_service *svc, struct iphdr *iph)
+__ip_vs_lblc_schedule(struct ip_vs_service *svc)
{
struct ip_vs_dest *dest, *least;
int loh, doh;
@@ -420,12 +431,13 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc, struct iphdr *iph)
}
}
- IP_VS_DBG(6, "LBLC: server %d.%d.%d.%d:%d "
- "activeconns %d refcnt %d weight %d overhead %d\n",
- NIPQUAD(least->addr.ip), ntohs(least->port),
- atomic_read(&least->activeconns),
- atomic_read(&least->refcnt),
- atomic_read(&least->weight), loh);
+ IP_VS_DBG_BUF(6, "LBLC: server %s:%d "
+ "activeconns %d refcnt %d weight %d overhead %d\n",
+ IP_VS_DBG_ADDR(least->af, &least->addr),
+ ntohs(least->port),
+ atomic_read(&least->activeconns),
+ atomic_read(&least->refcnt),
+ atomic_read(&least->weight), loh);
return least;
}
@@ -459,15 +471,17 @@ static struct ip_vs_dest *
ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{
struct ip_vs_lblc_table *tbl = svc->sched_data;
- struct iphdr *iph = ip_hdr(skb);
+ struct ip_vs_iphdr iph;
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblc_entry *en;
+ ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+
IP_VS_DBG(6, "ip_vs_lblc_schedule(): Scheduling...\n");
/* First look in our cache */
read_lock(&svc->sched_lock);
- en = ip_vs_lblc_get(tbl, iph->daddr);
+ en = ip_vs_lblc_get(svc->af, tbl, &iph.daddr);
if (en) {
/* We only hold a read lock, but this is atomic */
en->lastuse = jiffies;
@@ -491,23 +505,21 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
goto out;
/* No cache entry or it is invalid, time to schedule */
- dest = __ip_vs_lblc_schedule(svc, iph);
+ dest = __ip_vs_lblc_schedule(svc);
if (!dest) {
- IP_VS_DBG(1, "no destination available\n");
+ IP_VS_ERR_RL("LBLC: no destination available\n");
return NULL;
}
/* If we fail to create a cache entry, we'll just use the valid dest */
write_lock(&svc->sched_lock);
- ip_vs_lblc_new(tbl, iph->daddr, dest);
+ ip_vs_lblc_new(tbl, &iph.daddr, dest);
write_unlock(&svc->sched_lock);
out:
- IP_VS_DBG(6, "LBLC: destination IP address %u.%u.%u.%u "
- "--> server %u.%u.%u.%u:%d\n",
- NIPQUAD(iph->daddr),
- NIPQUAD(dest->addr.ip),
- ntohs(dest->port));
+ IP_VS_DBG_BUF(6, "LBLC: destination IP address %s --> server %s:%d\n",
+ IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+ IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port));
return dest;
}
@@ -522,9 +534,6 @@ static struct ip_vs_scheduler ip_vs_lblc_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_lblc_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 0,
-#endif
.init_service = ip_vs_lblc_init_svc,
.done_service = ip_vs_lblc_done_svc,
.schedule = ip_vs_lblc_schedule,
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index 1f75ea83bcf8..c04ce56c7f0f 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -202,12 +202,13 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
}
}
- IP_VS_DBG(6, "ip_vs_dest_set_min: server %d.%d.%d.%d:%d "
- "activeconns %d refcnt %d weight %d overhead %d\n",
- NIPQUAD(least->addr.ip), ntohs(least->port),
- atomic_read(&least->activeconns),
- atomic_read(&least->refcnt),
- atomic_read(&least->weight), loh);
+ IP_VS_DBG_BUF(6, "ip_vs_dest_set_min: server %s:%d "
+ "activeconns %d refcnt %d weight %d overhead %d\n",
+ IP_VS_DBG_ADDR(least->af, &least->addr),
+ ntohs(least->port),
+ atomic_read(&least->activeconns),
+ atomic_read(&least->refcnt),
+ atomic_read(&least->weight), loh);
return least;
}
@@ -248,12 +249,12 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
}
}
- IP_VS_DBG(6, "ip_vs_dest_set_max: server %d.%d.%d.%d:%d "
- "activeconns %d refcnt %d weight %d overhead %d\n",
- NIPQUAD(most->addr.ip), ntohs(most->port),
- atomic_read(&most->activeconns),
- atomic_read(&most->refcnt),
- atomic_read(&most->weight), moh);
+ IP_VS_DBG_BUF(6, "ip_vs_dest_set_max: server %s:%d "
+ "activeconns %d refcnt %d weight %d overhead %d\n",
+ IP_VS_DBG_ADDR(most->af, &most->addr), ntohs(most->port),
+ atomic_read(&most->activeconns),
+ atomic_read(&most->refcnt),
+ atomic_read(&most->weight), moh);
return most;
}
@@ -264,7 +265,8 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
*/
struct ip_vs_lblcr_entry {
struct list_head list;
- __be32 addr; /* destination IP address */
+ int af; /* address family */
+ union nf_inet_addr addr; /* destination IP address */
struct ip_vs_dest_set set; /* destination server set */
unsigned long lastuse; /* last used time */
};
@@ -293,7 +295,7 @@ static ctl_table vs_vars_table[] = {
.data = &sysctl_ip_vs_lblcr_expiration,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{ .ctl_name = 0 }
};
@@ -311,9 +313,17 @@ static inline void ip_vs_lblcr_free(struct ip_vs_lblcr_entry *en)
/*
* Returns hash value for IPVS LBLCR entry
*/
-static inline unsigned ip_vs_lblcr_hashkey(__be32 addr)
+static inline unsigned
+ip_vs_lblcr_hashkey(int af, const union nf_inet_addr *addr)
{
- return (ntohl(addr)*2654435761UL) & IP_VS_LBLCR_TAB_MASK;
+ __be32 addr_fold = addr->ip;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6)
+ addr_fold = addr->ip6[0]^addr->ip6[1]^
+ addr->ip6[2]^addr->ip6[3];
+#endif
+ return (ntohl(addr_fold)*2654435761UL) & IP_VS_LBLCR_TAB_MASK;
}
@@ -324,7 +334,7 @@ static inline unsigned ip_vs_lblcr_hashkey(__be32 addr)
static void
ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en)
{
- unsigned hash = ip_vs_lblcr_hashkey(en->addr);
+ unsigned hash = ip_vs_lblcr_hashkey(en->af, &en->addr);
list_add(&en->list, &tbl->bucket[hash]);
atomic_inc(&tbl->entries);
@@ -336,13 +346,14 @@ ip_vs_lblcr_hash(struct ip_vs_lblcr_table *tbl, struct ip_vs_lblcr_entry *en)
* read lock.
*/
static inline struct ip_vs_lblcr_entry *
-ip_vs_lblcr_get(struct ip_vs_lblcr_table *tbl, __be32 addr)
+ip_vs_lblcr_get(int af, struct ip_vs_lblcr_table *tbl,
+ const union nf_inet_addr *addr)
{
- unsigned hash = ip_vs_lblcr_hashkey(addr);
+ unsigned hash = ip_vs_lblcr_hashkey(af, addr);
struct ip_vs_lblcr_entry *en;
list_for_each_entry(en, &tbl->bucket[hash], list)
- if (en->addr == addr)
+ if (ip_vs_addr_equal(af, &en->addr, addr))
return en;
return NULL;
@@ -354,12 +365,12 @@ ip_vs_lblcr_get(struct ip_vs_lblcr_table *tbl, __be32 addr)
* IP address to a server. Called under write lock.
*/
static inline struct ip_vs_lblcr_entry *
-ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, __be32 daddr,
+ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
struct ip_vs_dest *dest)
{
struct ip_vs_lblcr_entry *en;
- en = ip_vs_lblcr_get(tbl, daddr);
+ en = ip_vs_lblcr_get(dest->af, tbl, daddr);
if (!en) {
en = kmalloc(sizeof(*en), GFP_ATOMIC);
if (!en) {
@@ -367,7 +378,8 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, __be32 daddr,
return NULL;
}
- en->addr = daddr;
+ en->af = dest->af;
+ ip_vs_addr_copy(dest->af, &en->addr, daddr);
en->lastuse = jiffies;
/* initilize its dest set */
@@ -544,7 +556,7 @@ static int ip_vs_lblcr_done_svc(struct ip_vs_service *svc)
static inline struct ip_vs_dest *
-__ip_vs_lblcr_schedule(struct ip_vs_service *svc, struct iphdr *iph)
+__ip_vs_lblcr_schedule(struct ip_vs_service *svc)
{
struct ip_vs_dest *dest, *least;
int loh, doh;
@@ -596,12 +608,13 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc, struct iphdr *iph)
}
}
- IP_VS_DBG(6, "LBLCR: server %d.%d.%d.%d:%d "
- "activeconns %d refcnt %d weight %d overhead %d\n",
- NIPQUAD(least->addr.ip), ntohs(least->port),
- atomic_read(&least->activeconns),
- atomic_read(&least->refcnt),
- atomic_read(&least->weight), loh);
+ IP_VS_DBG_BUF(6, "LBLCR: server %s:%d "
+ "activeconns %d refcnt %d weight %d overhead %d\n",
+ IP_VS_DBG_ADDR(least->af, &least->addr),
+ ntohs(least->port),
+ atomic_read(&least->activeconns),
+ atomic_read(&least->refcnt),
+ atomic_read(&least->weight), loh);
return least;
}
@@ -635,15 +648,17 @@ static struct ip_vs_dest *
ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{
struct ip_vs_lblcr_table *tbl = svc->sched_data;
- struct iphdr *iph = ip_hdr(skb);
+ struct ip_vs_iphdr iph;
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblcr_entry *en;
+ ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+
IP_VS_DBG(6, "ip_vs_lblcr_schedule(): Scheduling...\n");
/* First look in our cache */
read_lock(&svc->sched_lock);
- en = ip_vs_lblcr_get(tbl, iph->daddr);
+ en = ip_vs_lblcr_get(svc->af, tbl, &iph.daddr);
if (en) {
/* We only hold a read lock, but this is atomic */
en->lastuse = jiffies;
@@ -673,9 +688,9 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
}
/* The cache entry is invalid, time to schedule */
- dest = __ip_vs_lblcr_schedule(svc, iph);
+ dest = __ip_vs_lblcr_schedule(svc);
if (!dest) {
- IP_VS_DBG(1, "no destination available\n");
+ IP_VS_ERR_RL("LBLCR: no destination available\n");
read_unlock(&svc->sched_lock);
return NULL;
}
@@ -691,7 +706,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
goto out;
/* No cache entry, time to schedule */
- dest = __ip_vs_lblcr_schedule(svc, iph);
+ dest = __ip_vs_lblcr_schedule(svc);
if (!dest) {
IP_VS_DBG(1, "no destination available\n");
return NULL;
@@ -699,15 +714,13 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
/* If we fail to create a cache entry, we'll just use the valid dest */
write_lock(&svc->sched_lock);
- ip_vs_lblcr_new(tbl, iph->daddr, dest);
+ ip_vs_lblcr_new(tbl, &iph.daddr, dest);
write_unlock(&svc->sched_lock);
out:
- IP_VS_DBG(6, "LBLCR: destination IP address %u.%u.%u.%u "
- "--> server %u.%u.%u.%u:%d\n",
- NIPQUAD(iph->daddr),
- NIPQUAD(dest->addr.ip),
- ntohs(dest->port));
+ IP_VS_DBG_BUF(6, "LBLCR: destination IP address %s --> server %s:%d\n",
+ IP_VS_DBG_ADDR(svc->af, &iph.daddr),
+ IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port));
return dest;
}
@@ -722,9 +735,6 @@ static struct ip_vs_scheduler ip_vs_lblcr_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_lblcr_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 0,
-#endif
.init_service = ip_vs_lblcr_init_svc,
.done_service = ip_vs_lblcr_done_svc,
.schedule = ip_vs_lblcr_schedule,
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index b69f808ac461..d0dadc8a65fd 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -66,11 +66,15 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
}
}
- if (least)
- IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d inactconns %d\n",
- IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
- atomic_read(&least->activeconns),
- atomic_read(&least->inactconns));
+ if (!least)
+ IP_VS_ERR_RL("LC: no destination available\n");
+ else
+ IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d "
+ "inactconns %d\n",
+ IP_VS_DBG_ADDR(svc->af, &least->addr),
+ ntohs(least->port),
+ atomic_read(&least->activeconns),
+ atomic_read(&least->inactconns));
return least;
}
@@ -81,9 +85,6 @@ static struct ip_vs_scheduler ip_vs_lc_scheduler = {
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_lc_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.schedule = ip_vs_lc_schedule,
};
diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c
index 9a2d8033f08f..694952db5026 100644
--- a/net/netfilter/ipvs/ip_vs_nq.c
+++ b/net/netfilter/ipvs/ip_vs_nq.c
@@ -95,8 +95,10 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
}
}
- if (!least)
+ if (!least) {
+ IP_VS_ERR_RL("NQ: no destination available\n");
return NULL;
+ }
out:
IP_VS_DBG_BUF(6, "NQ: server %s:%u "
@@ -116,9 +118,6 @@ static struct ip_vs_scheduler ip_vs_nq_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_nq_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.schedule = ip_vs_nq_schedule,
};
diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c
index 0791f9e08feb..a01520e3d6b8 100644
--- a/net/netfilter/ipvs/ip_vs_proto.c
+++ b/net/netfilter/ipvs/ip_vs_proto.c
@@ -164,26 +164,21 @@ ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp,
if (ih == NULL)
sprintf(buf, "%s TRUNCATED", pp->name);
else if (ih->frag_off & htons(IP_OFFSET))
- sprintf(buf, "%s %u.%u.%u.%u->%u.%u.%u.%u frag",
- pp->name, NIPQUAD(ih->saddr),
- NIPQUAD(ih->daddr));
+ sprintf(buf, "%s %pI4->%pI4 frag",
+ pp->name, &ih->saddr, &ih->daddr);
else {
__be16 _ports[2], *pptr
;
pptr = skb_header_pointer(skb, offset + ih->ihl*4,
sizeof(_ports), _ports);
if (pptr == NULL)
- sprintf(buf, "%s TRUNCATED %u.%u.%u.%u->%u.%u.%u.%u",
- pp->name,
- NIPQUAD(ih->saddr),
- NIPQUAD(ih->daddr));
+ sprintf(buf, "%s TRUNCATED %pI4->%pI4",
+ pp->name, &ih->saddr, &ih->daddr);
else
- sprintf(buf, "%s %u.%u.%u.%u:%u->%u.%u.%u.%u:%u",
+ sprintf(buf, "%s %pI4:%u->%pI4:%u",
pp->name,
- NIPQUAD(ih->saddr),
- ntohs(pptr[0]),
- NIPQUAD(ih->daddr),
- ntohs(pptr[1]));
+ &ih->saddr, ntohs(pptr[0]),
+ &ih->daddr, ntohs(pptr[1]));
}
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
@@ -203,26 +198,21 @@ ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp,
if (ih == NULL)
sprintf(buf, "%s TRUNCATED", pp->name);
else if (ih->nexthdr == IPPROTO_FRAGMENT)
- sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT " frag",
- pp->name, NIP6(ih->saddr),
- NIP6(ih->daddr));
+ sprintf(buf, "%s %pI6->%pI6 frag",
+ pp->name, &ih->saddr, &ih->daddr);
else {
__be16 _ports[2], *pptr;
pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr),
sizeof(_ports), _ports);
if (pptr == NULL)
- sprintf(buf, "%s TRUNCATED " NIP6_FMT "->" NIP6_FMT,
- pp->name,
- NIP6(ih->saddr),
- NIP6(ih->daddr));
+ sprintf(buf, "%s TRUNCATED %pI6->%pI6",
+ pp->name, &ih->saddr, &ih->daddr);
else
- sprintf(buf, "%s " NIP6_FMT ":%u->" NIP6_FMT ":%u",
+ sprintf(buf, "%s %pI6:%u->%pI6:%u",
pp->name,
- NIP6(ih->saddr),
- ntohs(pptr[0]),
- NIP6(ih->daddr),
- ntohs(pptr[1]));
+ &ih->saddr, ntohs(pptr[0]),
+ &ih->daddr, ntohs(pptr[1]));
}
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
index 80ab0c8e5b4a..79f56c1e7c19 100644
--- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
@@ -135,9 +135,8 @@ ah_esp_debug_packet_v4(struct ip_vs_protocol *pp, const struct sk_buff *skb,
if (ih == NULL)
sprintf(buf, "%s TRUNCATED", pp->name);
else
- sprintf(buf, "%s %u.%u.%u.%u->%u.%u.%u.%u",
- pp->name, NIPQUAD(ih->saddr),
- NIPQUAD(ih->daddr));
+ sprintf(buf, "%s %pI4->%pI4",
+ pp->name, &ih->saddr, &ih->daddr);
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
}
@@ -154,9 +153,8 @@ ah_esp_debug_packet_v6(struct ip_vs_protocol *pp, const struct sk_buff *skb,
if (ih == NULL)
sprintf(buf, "%s TRUNCATED", pp->name);
else
- sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT,
- pp->name, NIP6(ih->saddr),
- NIP6(ih->daddr));
+ sprintf(buf, "%s %pI6->%pI6",
+ pp->name, &ih->saddr, &ih->daddr);
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index dd4566ea2bff..8cba41802850 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -192,8 +192,8 @@ tcp_snat_handler(struct sk_buff *skb,
/* Adjust TCP checksums */
if (skb->ip_summed == CHECKSUM_PARTIAL) {
tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
- htonl(oldlen),
- htonl(skb->len - tcphoff));
+ htons(oldlen),
+ htons(skb->len - tcphoff));
} else if (!cp->app) {
/* Only port and addr are changed, do fast csum update */
tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
@@ -267,8 +267,8 @@ tcp_dnat_handler(struct sk_buff *skb,
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
- htonl(oldlen),
- htonl(skb->len - tcphoff));
+ htons(oldlen),
+ htons(skb->len - tcphoff));
} else if (!cp->app) {
/* Only port and addr are changed, do fast csum update */
tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 6eb6039d6343..d2930a71084b 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -203,8 +203,8 @@ udp_snat_handler(struct sk_buff *skb,
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
- htonl(oldlen),
- htonl(skb->len - udphoff));
+ htons(oldlen),
+ htons(skb->len - udphoff));
} else if (!cp->app && (udph->check != 0)) {
/* Only port and addr are changed, do fast csum update */
udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
@@ -279,8 +279,8 @@ udp_dnat_handler(struct sk_buff *skb,
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
- htonl(oldlen),
- htonl(skb->len - udphoff));
+ htons(oldlen),
+ htons(skb->len - udphoff));
} else if (!cp->app && (udph->check != 0)) {
/* Only port and addr are changed, do fast csum update */
udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c
index a22195f68ac4..2d16ab7f8c1e 100644
--- a/net/netfilter/ipvs/ip_vs_rr.c
+++ b/net/netfilter/ipvs/ip_vs_rr.c
@@ -69,6 +69,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
q = q->next;
} while (q != p);
write_unlock(&svc->sched_lock);
+ IP_VS_ERR_RL("RR: no destination available\n");
return NULL;
out:
@@ -89,9 +90,6 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = {
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.init_service = ip_vs_rr_init_svc,
.update_service = ip_vs_rr_update_svc,
.schedule = ip_vs_rr_schedule,
diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c
index 7d2f22f04b83..20e4657d2f3b 100644
--- a/net/netfilter/ipvs/ip_vs_sed.c
+++ b/net/netfilter/ipvs/ip_vs_sed.c
@@ -84,6 +84,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
goto nextstage;
}
}
+ IP_VS_ERR_RL("SED: no destination available\n");
return NULL;
/*
@@ -118,9 +119,6 @@ static struct ip_vs_scheduler ip_vs_sed_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_sed_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.schedule = ip_vs_sed_schedule,
};
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index 1d96de27fefd..75709ebeb630 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -61,9 +61,16 @@ struct ip_vs_sh_bucket {
/*
* Returns hash value for IPVS SH entry
*/
-static inline unsigned ip_vs_sh_hashkey(__be32 addr)
+static inline unsigned ip_vs_sh_hashkey(int af, const union nf_inet_addr *addr)
{
- return (ntohl(addr)*2654435761UL) & IP_VS_SH_TAB_MASK;
+ __be32 addr_fold = addr->ip;
+
+#ifdef CONFIG_IP_VS_IPV6
+ if (af == AF_INET6)
+ addr_fold = addr->ip6[0]^addr->ip6[1]^
+ addr->ip6[2]^addr->ip6[3];
+#endif
+ return (ntohl(addr_fold)*2654435761UL) & IP_VS_SH_TAB_MASK;
}
@@ -71,9 +78,10 @@ static inline unsigned ip_vs_sh_hashkey(__be32 addr)
* Get ip_vs_dest associated with supplied parameters.
*/
static inline struct ip_vs_dest *
-ip_vs_sh_get(struct ip_vs_sh_bucket *tbl, __be32 addr)
+ip_vs_sh_get(int af, struct ip_vs_sh_bucket *tbl,
+ const union nf_inet_addr *addr)
{
- return (tbl[ip_vs_sh_hashkey(addr)]).dest;
+ return (tbl[ip_vs_sh_hashkey(af, addr)]).dest;
}
@@ -199,24 +207,26 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
{
struct ip_vs_dest *dest;
struct ip_vs_sh_bucket *tbl;
- struct iphdr *iph = ip_hdr(skb);
+ struct ip_vs_iphdr iph;
+
+ ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
tbl = (struct ip_vs_sh_bucket *)svc->sched_data;
- dest = ip_vs_sh_get(tbl, iph->saddr);
+ dest = ip_vs_sh_get(svc->af, tbl, &iph.saddr);
if (!dest
|| !(dest->flags & IP_VS_DEST_F_AVAILABLE)
|| atomic_read(&dest->weight) <= 0
|| is_overloaded(dest)) {
+ IP_VS_ERR_RL("SH: no destination available\n");
return NULL;
}
- IP_VS_DBG(6, "SH: source IP address %u.%u.%u.%u "
- "--> server %u.%u.%u.%u:%d\n",
- NIPQUAD(iph->saddr),
- NIPQUAD(dest->addr.ip),
- ntohs(dest->port));
+ IP_VS_DBG_BUF(6, "SH: source IP address %s --> server %s:%d\n",
+ IP_VS_DBG_ADDR(svc->af, &iph.saddr),
+ IP_VS_DBG_ADDR(svc->af, &dest->addr),
+ ntohs(dest->port));
return dest;
}
@@ -231,9 +241,6 @@ static struct ip_vs_scheduler ip_vs_sh_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 0,
-#endif
.init_service = ip_vs_sh_init_svc,
.done_service = ip_vs_sh_done_svc,
.update_service = ip_vs_sh_update_svc,
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index de5e7e118eed..6be5d4efa51b 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -580,8 +580,8 @@ static int bind_mcastif_addr(struct socket *sock, char *ifname)
IP_VS_ERR("You probably need to specify IP address on "
"multicast interface.\n");
- IP_VS_DBG(7, "binding socket with (%s) %u.%u.%u.%u\n",
- ifname, NIPQUAD(addr));
+ IP_VS_DBG(7, "binding socket with (%s) %pI4\n",
+ ifname, &addr);
/* Now bind the socket with the address of multicast interface */
sin.sin_family = AF_INET;
diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c
index 8c596e712599..8e942565b47d 100644
--- a/net/netfilter/ipvs/ip_vs_wlc.c
+++ b/net/netfilter/ipvs/ip_vs_wlc.c
@@ -72,6 +72,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
goto nextstage;
}
}
+ IP_VS_ERR_RL("WLC: no destination available\n");
return NULL;
/*
@@ -106,9 +107,6 @@ static struct ip_vs_scheduler ip_vs_wlc_scheduler =
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.schedule = ip_vs_wlc_schedule,
};
diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c
index 7ea92fed50bf..f7d74ef1ecf9 100644
--- a/net/netfilter/ipvs/ip_vs_wrr.c
+++ b/net/netfilter/ipvs/ip_vs_wrr.c
@@ -155,6 +155,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
if (mark->cl == mark->cl->next) {
/* no dest entry */
+ IP_VS_ERR_RL("WRR: no destination available: "
+ "no destinations present\n");
dest = NULL;
goto out;
}
@@ -168,8 +170,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
*/
if (mark->cw == 0) {
mark->cl = &svc->destinations;
- IP_VS_ERR_RL("ip_vs_wrr_schedule(): "
- "no available servers\n");
+ IP_VS_ERR_RL("WRR: no destination "
+ "available\n");
dest = NULL;
goto out;
}
@@ -191,6 +193,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
/* back to the start, and no dest is found.
It is only possible when all dests are OVERLOADED */
dest = NULL;
+ IP_VS_ERR_RL("WRR: no destination available: "
+ "all destinations are overloaded\n");
goto out;
}
}
@@ -213,9 +217,6 @@ static struct ip_vs_scheduler ip_vs_wrr_scheduler = {
.refcnt = ATOMIC_INIT(0),
.module = THIS_MODULE,
.n_list = LIST_HEAD_INIT(ip_vs_wrr_scheduler.n_list),
-#ifdef CONFIG_IP_VS_IPV6
- .supports_ipv6 = 1,
-#endif
.init_service = ip_vs_wrr_init_svc,
.done_service = ip_vs_wrr_done_svc,
.update_service = ip_vs_wrr_update_svc,
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index e90d52f199bc..425ab144f15d 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -82,14 +82,13 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
if (ip_route_output_key(&init_net, &rt, &fl)) {
spin_unlock(&dest->dst_lock);
- IP_VS_DBG_RL("ip_route_output error, "
- "dest: %u.%u.%u.%u\n",
- NIPQUAD(dest->addr.ip));
+ IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n",
+ &dest->addr.ip);
return NULL;
}
__ip_vs_dst_set(dest, rtos, dst_clone(&rt->u.dst));
- IP_VS_DBG(10, "new dst %u.%u.%u.%u, refcnt=%d, rtos=%X\n",
- NIPQUAD(dest->addr.ip),
+ IP_VS_DBG(10, "new dst %pI4, refcnt=%d, rtos=%X\n",
+ &dest->addr.ip,
atomic_read(&rt->u.dst.__refcnt), rtos);
}
spin_unlock(&dest->dst_lock);
@@ -104,8 +103,8 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
};
if (ip_route_output_key(&init_net, &rt, &fl)) {
- IP_VS_DBG_RL("ip_route_output error, dest: "
- "%u.%u.%u.%u\n", NIPQUAD(cp->daddr.ip));
+ IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n",
+ &cp->daddr.ip);
return NULL;
}
}
@@ -141,14 +140,13 @@ __ip_vs_get_out_rt_v6(struct ip_vs_conn *cp)
NULL, &fl);
if (!rt) {
spin_unlock(&dest->dst_lock);
- IP_VS_DBG_RL("ip6_route_output error, "
- "dest: " NIP6_FMT "\n",
- NIP6(dest->addr.in6));
+ IP_VS_DBG_RL("ip6_route_output error, dest: %pI6\n",
+ &dest->addr.in6);
return NULL;
}
__ip_vs_dst_set(dest, 0, dst_clone(&rt->u.dst));
- IP_VS_DBG(10, "new dst " NIP6_FMT ", refcnt=%d\n",
- NIP6(dest->addr.in6),
+ IP_VS_DBG(10, "new dst %pI6, refcnt=%d\n",
+ &dest->addr.in6,
atomic_read(&rt->u.dst.__refcnt));
}
spin_unlock(&dest->dst_lock);
@@ -167,8 +165,8 @@ __ip_vs_get_out_rt_v6(struct ip_vs_conn *cp)
rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
if (!rt) {
- IP_VS_DBG_RL("ip6_route_output error, dest: "
- NIP6_FMT "\n", NIP6(cp->daddr.in6));
+ IP_VS_DBG_RL("ip6_route_output error, dest: %pI6\n",
+ &cp->daddr.in6);
return NULL;
}
}
@@ -237,8 +235,8 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
EnterFunction(10);
if (ip_route_output_key(&init_net, &rt, &fl)) {
- IP_VS_DBG_RL("ip_vs_bypass_xmit(): ip_route_output error, "
- "dest: %u.%u.%u.%u\n", NIPQUAD(iph->daddr));
+ IP_VS_DBG_RL("ip_vs_bypass_xmit(): ip_route_output error, dest: %pI4\n",
+ &iph->daddr);
goto tx_error_icmp;
}
@@ -301,8 +299,8 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
if (!rt) {
- IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): ip6_route_output error, "
- "dest: " NIP6_FMT "\n", NIP6(iph->daddr));
+ IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): ip6_route_output error, dest: %pI6\n",
+ &iph->daddr);
goto tx_error_icmp;
}
diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c
index b92df5c1dfcf..9fe8982bd7c9 100644
--- a/net/netfilter/nf_conntrack_acct.c
+++ b/net/netfilter/nf_conntrack_acct.c
@@ -35,7 +35,7 @@ static struct ctl_table acct_sysctl_table[] = {
.data = &init_net.ct.sysctl_acct,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{}
};
diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c
index 38aedeeaf4e1..4f8fcf498545 100644
--- a/net/netfilter/nf_conntrack_amanda.c
+++ b/net/netfilter/nf_conntrack_amanda.c
@@ -30,6 +30,7 @@ MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
MODULE_DESCRIPTION("Amanda connection tracking module");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_amanda");
+MODULE_ALIAS_NFCT_HELPER("amanda");
module_param(master_timeout, uint, 0600);
MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 233fdd2d7d21..7e83f74cd5de 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -39,13 +39,13 @@
#include <net/netfilter/nf_conntrack_extend.h>
#include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_core.h>
#define NF_CONNTRACK_VERSION "0.5.0"
-unsigned int
-(*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct,
- enum nf_nat_manip_type manip,
- struct nlattr *attr) __read_mostly;
+int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct,
+ enum nf_nat_manip_type manip,
+ struct nlattr *attr) __read_mostly;
EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook);
DEFINE_SPINLOCK(nf_conntrack_lock);
@@ -181,7 +181,8 @@ destroy_conntrack(struct nf_conntrack *nfct)
NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
NF_CT_ASSERT(!timer_pending(&ct->timeout));
- nf_conntrack_event(IPCT_DESTROY, ct);
+ if (!test_bit(IPS_DYING_BIT, &ct->status))
+ nf_conntrack_event(IPCT_DESTROY, ct);
set_bit(IPS_DYING_BIT, &ct->status);
/* To make sure we don't get any weird locking issues here:
@@ -586,14 +587,7 @@ init_conntrack(struct net *net,
nf_conntrack_get(&ct->master->ct_general);
NF_CT_STAT_INC(net, expect_new);
} else {
- struct nf_conntrack_helper *helper;
-
- helper = __nf_ct_helper_find(&repl_tuple);
- if (helper) {
- help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
- if (help)
- rcu_assign_pointer(help->helper, helper);
- }
+ __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
NF_CT_STAT_INC(net, new);
}
@@ -770,7 +764,6 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
const struct nf_conntrack_tuple *newreply)
{
struct nf_conn_help *help = nfct_help(ct);
- struct nf_conntrack_helper *helper;
/* Should be unconfirmed, so not in hash table yet */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
@@ -783,23 +776,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
return;
rcu_read_lock();
- helper = __nf_ct_helper_find(newreply);
- if (helper == NULL) {
- if (help)
- rcu_assign_pointer(help->helper, NULL);
- goto out;
- }
-
- if (help == NULL) {
- help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
- if (help == NULL)
- goto out;
- } else {
- memset(&help->help, 0, sizeof(help->help));
- }
-
- rcu_assign_pointer(help->helper, helper);
-out:
+ __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
@@ -994,8 +971,20 @@ void nf_ct_iterate_cleanup(struct net *net,
}
EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);
+struct __nf_ct_flush_report {
+ u32 pid;
+ int report;
+};
+
static int kill_all(struct nf_conn *i, void *data)
{
+ struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data;
+
+ /* get_next_corpse sets the dying bit for us */
+ nf_conntrack_event_report(IPCT_DESTROY,
+ i,
+ fr->pid,
+ fr->report);
return 1;
}
@@ -1009,9 +998,13 @@ void nf_ct_free_hashtable(struct hlist_head *hash, int vmalloced, unsigned int s
}
EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
-void nf_conntrack_flush(struct net *net)
+void nf_conntrack_flush(struct net *net, u32 pid, int report)
{
- nf_ct_iterate_cleanup(net, kill_all, NULL);
+ struct __nf_ct_flush_report fr = {
+ .pid = pid,
+ .report = report,
+ };
+ nf_ct_iterate_cleanup(net, kill_all, &fr);
}
EXPORT_SYMBOL_GPL(nf_conntrack_flush);
@@ -1027,7 +1020,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
nf_ct_event_cache_flush(net);
nf_conntrack_ecache_fini(net);
i_see_dead_people:
- nf_conntrack_flush(net);
+ nf_conntrack_flush(net, 0, 0);
if (atomic_read(&net->ct.count) != 0) {
schedule();
goto i_see_dead_people;
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index a5f5e2e65d13..dee4190209cc 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -35,9 +35,17 @@ static inline void
__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
{
if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct)
- && ecache->events)
- atomic_notifier_call_chain(&nf_conntrack_chain, ecache->events,
- ecache->ct);
+ && ecache->events) {
+ struct nf_ct_event item = {
+ .ct = ecache->ct,
+ .pid = 0,
+ .report = 0
+ };
+
+ atomic_notifier_call_chain(&nf_conntrack_chain,
+ ecache->events,
+ &item);
+ }
ecache->events = 0;
nf_ct_put(ecache->ct);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 37a703bc3b8e..3a8a34a6d37c 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -362,7 +362,7 @@ static inline int refresh_timer(struct nf_conntrack_expect *i)
return 1;
}
-int nf_ct_expect_related(struct nf_conntrack_expect *expect)
+static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
{
const struct nf_conntrack_expect_policy *p;
struct nf_conntrack_expect *i;
@@ -371,11 +371,8 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
struct net *net = nf_ct_exp_net(expect);
struct hlist_node *n;
unsigned int h;
- int ret;
-
- NF_CT_ASSERT(master_help);
+ int ret = 0;
- spin_lock_bh(&nf_conntrack_lock);
if (!master_help->helper) {
ret = -ESHUTDOWN;
goto out;
@@ -409,18 +406,50 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
printk(KERN_WARNING
"nf_conntrack: expectation table full\n");
ret = -EMFILE;
- goto out;
}
+out:
+ return ret;
+}
+
+int nf_ct_expect_related(struct nf_conntrack_expect *expect)
+{
+ int ret;
+
+ spin_lock_bh(&nf_conntrack_lock);
+ ret = __nf_ct_expect_check(expect);
+ if (ret < 0)
+ goto out;
nf_ct_expect_insert(expect);
+ atomic_inc(&expect->use);
+ spin_unlock_bh(&nf_conntrack_lock);
nf_ct_expect_event(IPEXP_NEW, expect);
- ret = 0;
+ nf_ct_expect_put(expect);
+ return ret;
out:
spin_unlock_bh(&nf_conntrack_lock);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_expect_related);
+int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
+ u32 pid, int report)
+{
+ int ret;
+
+ spin_lock_bh(&nf_conntrack_lock);
+ ret = __nf_ct_expect_check(expect);
+ if (ret < 0)
+ goto out;
+ nf_ct_expect_insert(expect);
+out:
+ spin_unlock_bh(&nf_conntrack_lock);
+ if (ret == 0)
+ nf_ct_expect_event_report(IPEXP_NEW, expect, pid, report);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
+
#ifdef CONFIG_PROC_FS
struct ct_expect_iter_state {
struct seq_net_private p;
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index 4f7107107e99..00fecc385f9b 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -29,6 +29,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
MODULE_DESCRIPTION("ftp connection tracking helper");
MODULE_ALIAS("ip_conntrack_ftp");
+MODULE_ALIAS_NFCT_HELPER("ftp");
/* This is slow, but it's simple. --RR */
static char *ftp_buffer;
@@ -357,7 +358,7 @@ static int help(struct sk_buff *skb,
int ret;
u32 seq;
int dir = CTINFO2DIR(ctinfo);
- unsigned int matchlen, matchoff;
+ unsigned int uninitialized_var(matchlen), uninitialized_var(matchoff);
struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;
struct nf_conntrack_expect *exp;
union nf_inet_addr *daddr;
@@ -427,10 +428,8 @@ static int help(struct sk_buff *skb,
connection tracking, not packet filtering.
However, it is necessary for accurate tracking in
this case. */
- if (net_ratelimit())
- printk("conntrack_ftp: partial %s %u+%u\n",
- search[dir][i].pattern,
- ntohl(th->seq), datalen);
+ pr_debug("conntrack_ftp: partial %s %u+%u\n",
+ search[dir][i].pattern, ntohl(th->seq), datalen);
ret = NF_DROP;
goto out;
} else if (found == 0) { /* No match */
@@ -462,16 +461,13 @@ static int help(struct sk_buff *skb,
different IP address. Simply don't record it for
NAT. */
if (cmd.l3num == PF_INET) {
- pr_debug("conntrack_ftp: NOT RECORDING: " NIPQUAD_FMT
- " != " NIPQUAD_FMT "\n",
- NIPQUAD(cmd.u3.ip),
- NIPQUAD(ct->tuplehash[dir].tuple.src.u3.ip));
+ pr_debug("conntrack_ftp: NOT RECORDING: %pI4 != %pI4\n",
+ &cmd.u3.ip,
+ &ct->tuplehash[dir].tuple.src.u3.ip);
} else {
- pr_debug("conntrack_ftp: NOT RECORDING: " NIP6_FMT
- " != " NIP6_FMT "\n",
- NIP6(*((struct in6_addr *)cmd.u3.ip6)),
- NIP6(*((struct in6_addr *)
- ct->tuplehash[dir].tuple.src.u3.ip6)));
+ pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n",
+ cmd.u3.ip6,
+ ct->tuplehash[dir].tuple.src.u3.ip6);
}
/* Thanks to Cristiano Lincoln Mattos
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index c1504f71cdff..687bd633c3d7 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -850,10 +850,8 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct,
get_h225_addr(ct, *data, &setup->destCallSignalAddress,
&addr, &port) &&
memcmp(&addr, &ct->tuplehash[!dir].tuple.src.u3, sizeof(addr))) {
- pr_debug("nf_ct_q931: set destCallSignalAddress "
- NIP6_FMT ":%hu->" NIP6_FMT ":%hu\n",
- NIP6(*(struct in6_addr *)&addr), ntohs(port),
- NIP6(*(struct in6_addr *)&ct->tuplehash[!dir].tuple.src.u3),
+ pr_debug("nf_ct_q931: set destCallSignalAddress %pI6:%hu->%pI6:%hu\n",
+ &addr, ntohs(port), &ct->tuplehash[!dir].tuple.src.u3,
ntohs(ct->tuplehash[!dir].tuple.src.u.tcp.port));
ret = set_h225_addr(skb, data, dataoff,
&setup->destCallSignalAddress,
@@ -868,10 +866,8 @@ static int process_setup(struct sk_buff *skb, struct nf_conn *ct,
get_h225_addr(ct, *data, &setup->sourceCallSignalAddress,
&addr, &port) &&
memcmp(&addr, &ct->tuplehash[!dir].tuple.dst.u3, sizeof(addr))) {
- pr_debug("nf_ct_q931: set sourceCallSignalAddress "
- NIP6_FMT ":%hu->" NIP6_FMT ":%hu\n",
- NIP6(*(struct in6_addr *)&addr), ntohs(port),
- NIP6(*(struct in6_addr *)&ct->tuplehash[!dir].tuple.dst.u3),
+ pr_debug("nf_ct_q931: set sourceCallSignalAddress %pI6:%hu->%pI6:%hu\n",
+ &addr, ntohs(port), &ct->tuplehash[!dir].tuple.dst.u3,
ntohs(ct->tuplehash[!dir].tuple.dst.u.tcp.port));
ret = set_h225_addr(skb, data, dataoff,
&setup->sourceCallSignalAddress,
@@ -1831,3 +1827,4 @@ MODULE_AUTHOR("Jing Min Zhao <zhaojingmin@users.sourceforge.net>");
MODULE_DESCRIPTION("H.323 connection tracking helper");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_h323");
+MODULE_ALIAS_NFCT_HELPER("h323");
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index c39b6a994133..a51bdac9f3a0 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -45,7 +45,7 @@ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
}
-struct nf_conntrack_helper *
+static struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_helper *helper;
@@ -63,7 +63,6 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
}
return NULL;
}
-EXPORT_SYMBOL_GPL(__nf_ct_helper_find);
struct nf_conntrack_helper *
__nf_conntrack_helper_find_byname(const char *name)
@@ -95,6 +94,35 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
+int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags)
+{
+ int ret = 0;
+ struct nf_conntrack_helper *helper;
+ struct nf_conn_help *help = nfct_help(ct);
+
+ helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ if (helper == NULL) {
+ if (help)
+ rcu_assign_pointer(help->helper, NULL);
+ goto out;
+ }
+
+ if (help == NULL) {
+ help = nf_ct_helper_ext_add(ct, flags);
+ if (help == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ } else {
+ memset(&help->help, 0, sizeof(help->help));
+ }
+
+ rcu_assign_pointer(help->helper, helper);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
+
static inline int unhelp(struct nf_conntrack_tuple_hash *i,
const struct nf_conntrack_helper *me)
{
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 20633fdf7e6b..409c8be58e7c 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -41,6 +41,7 @@ MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("IRC (DCC) connection tracking helper");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_irc");
+MODULE_ALIAS_NFCT_HELPER("irc");
module_param_array(ports, ushort, &ports_c, 0400);
MODULE_PARM_DESC(ports, "port numbers of IRC servers");
@@ -156,9 +157,9 @@ static int help(struct sk_buff *skb, unsigned int protoff,
/* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
iph = ip_hdr(skb);
- pr_debug("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u\n",
- NIPQUAD(iph->saddr), ntohs(th->source),
- NIPQUAD(iph->daddr), ntohs(th->dest));
+ pr_debug("DCC found in master %pI4:%u %pI4:%u\n",
+ &iph->saddr, ntohs(th->source),
+ &iph->daddr, ntohs(th->dest));
for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
@@ -185,10 +186,9 @@ static int help(struct sk_buff *skb, unsigned int protoff,
tuple->dst.u3.ip != htonl(dcc_ip)) {
if (net_ratelimit())
printk(KERN_WARNING
- "Forged DCC command from "
- "%u.%u.%u.%u: %u.%u.%u.%u:%u\n",
- NIPQUAD(tuple->src.u3.ip),
- HIPQUAD(dcc_ip), dcc_port);
+ "Forged DCC command from %pI4: %pI4:%u\n",
+ &tuple->src.u3.ip,
+ &dcc_ip, dcc_port);
continue;
}
diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c
index 08404e6755fb..5af4273b4668 100644
--- a/net/netfilter/nf_conntrack_netbios_ns.c
+++ b/net/netfilter/nf_conntrack_netbios_ns.c
@@ -37,6 +37,7 @@ MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_netbios_ns");
+MODULE_ALIAS_NFCT_HELPER("netbios_ns");
static unsigned int timeout __read_mostly = 3;
module_param(timeout, uint, 0400);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 5f4a6516b3b6..00e8c27130ff 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -105,16 +105,14 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
struct nf_conntrack_l3proto *l3proto;
struct nf_conntrack_l4proto *l4proto;
- l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+ l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
- nf_ct_l3proto_put(l3proto);
if (unlikely(ret < 0))
return ret;
- l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+ l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto);
- nf_ct_l4proto_put(l4proto);
return ret;
}
@@ -151,11 +149,9 @@ ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
struct nlattr *nest_proto;
int ret;
- l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct));
- if (!l4proto->to_nlattr) {
- nf_ct_l4proto_put(l4proto);
+ l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+ if (!l4proto->to_nlattr)
return 0;
- }
nest_proto = nla_nest_start(skb, CTA_PROTOINFO | NLA_F_NESTED);
if (!nest_proto)
@@ -163,14 +159,11 @@ ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
ret = l4proto->to_nlattr(skb, nest_proto, ct);
- nf_ct_l4proto_put(l4proto);
-
nla_nest_end(skb, nest_proto);
return ret;
nla_put_failure:
- nf_ct_l4proto_put(l4proto);
return -1;
}
@@ -184,7 +177,6 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
if (!help)
return 0;
- rcu_read_lock();
helper = rcu_dereference(help->helper);
if (!helper)
goto out;
@@ -199,11 +191,9 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
nla_nest_end(skb, nest_helper);
out:
- rcu_read_unlock();
return 0;
nla_put_failure:
- rcu_read_unlock();
return -1;
}
@@ -420,7 +410,8 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
struct nlattr *nest_parms;
- struct nf_conn *ct = (struct nf_conn *)ptr;
+ struct nf_ct_event *item = (struct nf_ct_event *)ptr;
+ struct nf_conn *ct = item->ct;
struct sk_buff *skb;
unsigned int type;
sk_buff_data_t b;
@@ -453,7 +444,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
b = skb->tail;
type |= NFNL_SUBSYS_CTNETLINK << 8;
- nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
+ nlh = NLMSG_PUT(skb, item->pid, 0, type, sizeof(struct nfgenmsg));
nfmsg = NLMSG_DATA(nlh);
nlh->nlmsg_flags = flags;
@@ -461,6 +452,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
+ rcu_read_lock();
nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
if (!nest_parms)
goto nla_put_failure;
@@ -517,13 +509,15 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
&& ctnetlink_dump_mark(skb, ct) < 0)
goto nla_put_failure;
#endif
+ rcu_read_unlock();
nlh->nlmsg_len = skb->tail - b;
- nfnetlink_send(skb, 0, group, 0);
+ nfnetlink_send(skb, item->pid, group, item->report);
return NOTIFY_DONE;
-nlmsg_failure:
nla_put_failure:
+ rcu_read_unlock();
+nlmsg_failure:
kfree_skb(skb);
return NOTIFY_DONE;
}
@@ -729,7 +723,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
else {
/* Flush the whole table */
- nf_conntrack_flush(&init_net);
+ nf_conntrack_flush(&init_net,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh));
return 0;
}
@@ -750,6 +746,14 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
}
}
+ nf_conntrack_event_report(IPCT_DESTROY,
+ ct,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh));
+
+ /* death_by_timeout would report the event again */
+ set_bit(IPS_DYING_BIT, &ct->status);
+
nf_ct_kill(ct);
nf_ct_put(ct);
@@ -795,8 +799,10 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
return -ENOMEM;
}
+ rcu_read_lock();
err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
IPCTNL_MSG_CT_NEW, 1, ct);
+ rcu_read_unlock();
nf_ct_put(ct);
if (err <= 0)
goto free;
@@ -922,8 +928,22 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
}
helper = __nf_conntrack_helper_find_byname(helpname);
- if (helper == NULL)
+ if (helper == NULL) {
+#ifdef CONFIG_MODULES
+ spin_unlock_bh(&nf_conntrack_lock);
+
+ if (request_module("nfct-helper-%s", helpname) < 0) {
+ spin_lock_bh(&nf_conntrack_lock);
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_bh(&nf_conntrack_lock);
+ helper = __nf_conntrack_helper_find_byname(helpname);
+ if (helper)
+ return -EAGAIN;
+#endif
return -EOPNOTSUPP;
+ }
if (help) {
if (help->helper == helper)
@@ -1079,15 +1099,38 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
return 0;
}
+static inline void
+ctnetlink_event_report(struct nf_conn *ct, u32 pid, int report)
+{
+ unsigned int events = 0;
+
+ if (test_bit(IPS_EXPECTED_BIT, &ct->status))
+ events |= IPCT_RELATED;
+ else
+ events |= IPCT_NEW;
+
+ nf_conntrack_event_report(IPCT_STATUS |
+ IPCT_HELPER |
+ IPCT_REFRESH |
+ IPCT_PROTOINFO |
+ IPCT_NATSEQADJ |
+ IPCT_MARK |
+ events,
+ ct,
+ pid,
+ report);
+}
+
static int
ctnetlink_create_conntrack(struct nlattr *cda[],
struct nf_conntrack_tuple *otuple,
struct nf_conntrack_tuple *rtuple,
- struct nf_conn *master_ct)
+ struct nf_conn *master_ct,
+ u32 pid,
+ int report)
{
struct nf_conn *ct;
int err = -EINVAL;
- struct nf_conn_help *help;
struct nf_conntrack_helper *helper;
ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC);
@@ -1102,16 +1145,55 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
ct->status |= IPS_CONFIRMED;
rcu_read_lock();
- helper = __nf_ct_helper_find(rtuple);
- if (helper) {
- help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
- if (help == NULL) {
+ if (cda[CTA_HELP]) {
+ char *helpname;
+
+ err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+ if (err < 0) {
+ rcu_read_unlock();
+ goto err;
+ }
+
+ helper = __nf_conntrack_helper_find_byname(helpname);
+ if (helper == NULL) {
+ rcu_read_unlock();
+#ifdef CONFIG_MODULES
+ if (request_module("nfct-helper-%s", helpname) < 0) {
+ err = -EOPNOTSUPP;
+ goto err;
+ }
+
+ rcu_read_lock();
+ helper = __nf_conntrack_helper_find_byname(helpname);
+ if (helper) {
+ rcu_read_unlock();
+ err = -EAGAIN;
+ goto err;
+ }
+ rcu_read_unlock();
+#endif
+ err = -EOPNOTSUPP;
+ goto err;
+ } else {
+ struct nf_conn_help *help;
+
+ help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
+ if (help == NULL) {
+ rcu_read_unlock();
+ err = -ENOMEM;
+ goto err;
+ }
+
+ /* not in hash table yet so not strictly necessary */
+ rcu_assign_pointer(help->helper, helper);
+ }
+ } else {
+ /* try an implicit helper assignation */
+ err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
+ if (err < 0) {
rcu_read_unlock();
- err = -ENOMEM;
goto err;
}
- /* not in hash table yet so not strictly necessary */
- rcu_assign_pointer(help->helper, helper);
}
if (cda[CTA_STATUS]) {
@@ -1151,9 +1233,12 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
ct->master = master_ct;
}
+ nf_conntrack_get(&ct->ct_general);
add_timer(&ct->timeout);
nf_conntrack_hash_insert(ct);
rcu_read_unlock();
+ ctnetlink_event_report(ct, pid, report);
+ nf_ct_put(ct);
return 0;
@@ -1209,7 +1294,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
goto out_unlock;
}
master_ct = nf_ct_tuplehash_to_ctrack(master_h);
- atomic_inc(&master_ct->ct_general.use);
+ nf_conntrack_get(&master_ct->ct_general);
}
err = -ENOENT;
@@ -1217,9 +1302,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
err = ctnetlink_create_conntrack(cda,
&otuple,
&rtuple,
- master_ct);
+ master_ct,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh));
spin_unlock_bh(&nf_conntrack_lock);
-
if (err < 0 && master_ct)
nf_ct_put(master_ct);
@@ -1231,6 +1317,8 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
* so there's no need to increase the refcount */
err = -EEXIST;
if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
+ struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+
/* we only allow nat config for new conntracks */
if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
err = -EOPNOTSUPP;
@@ -1241,8 +1329,19 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
err = -EOPNOTSUPP;
goto out_unlock;
}
- err = ctnetlink_change_conntrack(nf_ct_tuplehash_to_ctrack(h),
- cda);
+
+ err = ctnetlink_change_conntrack(ct, cda);
+ if (err == 0) {
+ nf_conntrack_get(&ct->ct_general);
+ spin_unlock_bh(&nf_conntrack_lock);
+ ctnetlink_event_report(ct,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh));
+ nf_ct_put(ct);
+ } else
+ spin_unlock_bh(&nf_conntrack_lock);
+
+ return err;
}
out_unlock:
@@ -1293,16 +1392,14 @@ ctnetlink_exp_dump_mask(struct sk_buff *skb,
if (!nest_parms)
goto nla_put_failure;
- l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+ l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
ret = ctnetlink_dump_tuples_ip(skb, &m, l3proto);
- nf_ct_l3proto_put(l3proto);
if (unlikely(ret < 0))
goto nla_put_failure;
- l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+ l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
ret = ctnetlink_dump_tuples_proto(skb, &m, l4proto);
- nf_ct_l4proto_put(l4proto);
if (unlikely(ret < 0))
goto nla_put_failure;
@@ -1379,7 +1476,8 @@ static int ctnetlink_expect_event(struct notifier_block *this,
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
- struct nf_conntrack_expect *exp = (struct nf_conntrack_expect *)ptr;
+ struct nf_exp_event *item = (struct nf_exp_event *)ptr;
+ struct nf_conntrack_expect *exp = item->exp;
struct sk_buff *skb;
unsigned int type;
sk_buff_data_t b;
@@ -1401,7 +1499,7 @@ static int ctnetlink_expect_event(struct notifier_block *this,
b = skb->tail;
type |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
- nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
+ nlh = NLMSG_PUT(skb, item->pid, 0, type, sizeof(struct nfgenmsg));
nfmsg = NLMSG_DATA(nlh);
nlh->nlmsg_flags = flags;
@@ -1409,15 +1507,18 @@ static int ctnetlink_expect_event(struct notifier_block *this,
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
+ rcu_read_lock();
if (ctnetlink_exp_dump_expect(skb, exp) < 0)
goto nla_put_failure;
+ rcu_read_unlock();
nlh->nlmsg_len = skb->tail - b;
- nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0);
+ nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW, item->report);
return NOTIFY_DONE;
-nlmsg_failure:
nla_put_failure:
+ rcu_read_unlock();
+nlmsg_failure:
kfree_skb(skb);
return NOTIFY_DONE;
}
@@ -1521,9 +1622,11 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
if (!skb2)
goto out;
+ rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid,
nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
1, exp);
+ rcu_read_unlock();
if (err <= 0)
goto free;
@@ -1624,7 +1727,7 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x, struct nlattr *cda[])
}
static int
-ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
+ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3, u32 pid, int report)
{
struct nf_conntrack_tuple tuple, mask, master_tuple;
struct nf_conntrack_tuple_hash *h = NULL;
@@ -1653,7 +1756,7 @@ ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
if (!help || !help->helper) {
/* such conntrack hasn't got any helper, abort */
- err = -EINVAL;
+ err = -EOPNOTSUPP;
goto out;
}
@@ -1671,7 +1774,7 @@ ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
memcpy(&exp->mask.src.u3, &mask.src.u3, sizeof(exp->mask.src.u3));
exp->mask.src.u.all = mask.src.u.all;
- err = nf_ct_expect_related(exp);
+ err = nf_ct_expect_related_report(exp, pid, report);
nf_ct_expect_put(exp);
out:
@@ -1704,8 +1807,12 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
if (!exp) {
spin_unlock_bh(&nf_conntrack_lock);
err = -ENOENT;
- if (nlh->nlmsg_flags & NLM_F_CREATE)
- err = ctnetlink_create_expect(cda, u3);
+ if (nlh->nlmsg_flags & NLM_F_CREATE) {
+ err = ctnetlink_create_expect(cda,
+ u3,
+ NETLINK_CB(skb).pid,
+ nlmsg_report(nlh));
+ }
return err;
}
diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c
index 1bc3001d1827..9e169ef2e854 100644
--- a/net/netfilter/nf_conntrack_pptp.c
+++ b/net/netfilter/nf_conntrack_pptp.c
@@ -37,6 +37,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
MODULE_ALIAS("ip_conntrack_pptp");
+MODULE_ALIAS_NFCT_HELPER("pptp");
static DEFINE_SPINLOCK(nf_pptp_lock);
diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c
index dbe680af85d2..4be80d7b8795 100644
--- a/net/netfilter/nf_conntrack_proto_generic.c
+++ b/net/netfilter/nf_conntrack_proto_generic.c
@@ -67,7 +67,7 @@ static struct ctl_table generic_sysctl_table[] = {
.data = &nf_ct_generic_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
@@ -80,7 +80,7 @@ static struct ctl_table generic_compat_sysctl_table[] = {
.data = &nf_ct_generic_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c
index 4ab62ad85dd4..1b279f9d6bf3 100644
--- a/net/netfilter/nf_conntrack_proto_gre.c
+++ b/net/netfilter/nf_conntrack_proto_gre.c
@@ -341,7 +341,7 @@ static int __init nf_ct_proto_gre_init(void)
return rv;
}
-static void nf_ct_proto_gre_fini(void)
+static void __exit nf_ct_proto_gre_fini(void)
{
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4);
unregister_pernet_gen_subsys(proto_gre_net_id, &proto_gre_net_ops);
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index ae8c2609e230..74e037901199 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -317,7 +317,7 @@ static int sctp_packet(struct nf_conn *ct,
goto out;
}
- old_state = new_state = SCTP_CONNTRACK_MAX;
+ old_state = new_state = SCTP_CONNTRACK_NONE;
write_lock_bh(&sctp_lock);
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
/* Special cases of Verification tag check (Sec 8.5.1) */
@@ -548,49 +548,49 @@ static struct ctl_table sctp_sysctl_table[] = {
.data = &sctp_timeouts[SCTP_CONNTRACK_CLOSED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_cookie_wait",
.data = &sctp_timeouts[SCTP_CONNTRACK_COOKIE_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_cookie_echoed",
.data = &sctp_timeouts[SCTP_CONNTRACK_COOKIE_ECHOED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_established",
.data = &sctp_timeouts[SCTP_CONNTRACK_ESTABLISHED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_shutdown_sent",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_shutdown_recd",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_RECD],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_sctp_timeout_shutdown_ack_sent",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_ACK_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
@@ -604,49 +604,49 @@ static struct ctl_table sctp_compat_sysctl_table[] = {
.data = &sctp_timeouts[SCTP_CONNTRACK_CLOSED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_cookie_wait",
.data = &sctp_timeouts[SCTP_CONNTRACK_COOKIE_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_cookie_echoed",
.data = &sctp_timeouts[SCTP_CONNTRACK_COOKIE_ECHOED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_established",
.data = &sctp_timeouts[SCTP_CONNTRACK_ESTABLISHED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_shutdown_sent",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_shutdown_recd",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_RECD],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_sctp_timeout_shutdown_ack_sent",
.data = &sctp_timeouts[SCTP_CONNTRACK_SHUTDOWN_ACK_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index f947ec41e391..a1edb9c1adee 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1192,70 +1192,70 @@ static struct ctl_table tcp_sysctl_table[] = {
.data = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_syn_recv",
.data = &tcp_timeouts[TCP_CONNTRACK_SYN_RECV],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_established",
.data = &tcp_timeouts[TCP_CONNTRACK_ESTABLISHED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_fin_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_FIN_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_close_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_CLOSE_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_last_ack",
.data = &tcp_timeouts[TCP_CONNTRACK_LAST_ACK],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_time_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_TIME_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_close",
.data = &tcp_timeouts[TCP_CONNTRACK_CLOSE],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_max_retrans",
.data = &nf_ct_tcp_timeout_max_retrans,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_tcp_timeout_unacknowledged",
.data = &nf_ct_tcp_timeout_unacknowledged,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = NET_NF_CONNTRACK_TCP_LOOSE,
@@ -1263,7 +1263,7 @@ static struct ctl_table tcp_sysctl_table[] = {
.data = &nf_ct_tcp_loose,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_TCP_BE_LIBERAL,
@@ -1271,7 +1271,7 @@ static struct ctl_table tcp_sysctl_table[] = {
.data = &nf_ct_tcp_be_liberal,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_TCP_MAX_RETRANS,
@@ -1279,7 +1279,7 @@ static struct ctl_table tcp_sysctl_table[] = {
.data = &nf_ct_tcp_max_retrans,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = 0
@@ -1293,63 +1293,63 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
.data = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_syn_recv",
.data = &tcp_timeouts[TCP_CONNTRACK_SYN_RECV],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_established",
.data = &tcp_timeouts[TCP_CONNTRACK_ESTABLISHED],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_fin_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_FIN_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_close_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_CLOSE_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_last_ack",
.data = &tcp_timeouts[TCP_CONNTRACK_LAST_ACK],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_time_wait",
.data = &tcp_timeouts[TCP_CONNTRACK_TIME_WAIT],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_close",
.data = &tcp_timeouts[TCP_CONNTRACK_CLOSE],
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_tcp_timeout_max_retrans",
.data = &nf_ct_tcp_timeout_max_retrans,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_TCP_LOOSE,
@@ -1357,7 +1357,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
.data = &nf_ct_tcp_loose,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_TCP_BE_LIBERAL,
@@ -1365,7 +1365,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
.data = &nf_ct_tcp_be_liberal,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_TCP_MAX_RETRANS,
@@ -1373,7 +1373,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
.data = &nf_ct_tcp_max_retrans,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = 0
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index 7c2ca48698be..2b8b1f579f93 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -143,14 +143,14 @@ static struct ctl_table udp_sysctl_table[] = {
.data = &nf_ct_udp_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "nf_conntrack_udp_timeout_stream",
.data = &nf_ct_udp_timeout_stream,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
@@ -163,14 +163,14 @@ static struct ctl_table udp_compat_sysctl_table[] = {
.data = &nf_ct_udp_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.procname = "ip_conntrack_udp_timeout_stream",
.data = &nf_ct_udp_timeout_stream,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c
index d22d839e4f94..4579d8de13b1 100644
--- a/net/netfilter/nf_conntrack_proto_udplite.c
+++ b/net/netfilter/nf_conntrack_proto_udplite.c
@@ -151,7 +151,7 @@ static struct ctl_table udplite_sysctl_table[] = {
.data = &nf_ct_udplite_timeout,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -159,7 +159,7 @@ static struct ctl_table udplite_sysctl_table[] = {
.data = &nf_ct_udplite_timeout_stream,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_jiffies,
+ .proc_handler = proc_dointvec_jiffies,
},
{
.ctl_name = 0
diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c
index a94294b2b23c..dcfecbb81c46 100644
--- a/net/netfilter/nf_conntrack_sane.c
+++ b/net/netfilter/nf_conntrack_sane.c
@@ -30,6 +30,7 @@
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Schmidt <mschmidt@redhat.com>");
MODULE_DESCRIPTION("SANE connection tracking helper");
+MODULE_ALIAS_NFCT_HELPER("sane");
static char *sane_buffer;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 6813f1c8863f..4b572163784b 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -28,6 +28,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
MODULE_DESCRIPTION("SIP connection tracking helper");
MODULE_ALIAS("ip_conntrack_sip");
+MODULE_ALIAS_NFCT_HELPER("sip");
#define MAX_PORTS 8
static unsigned short ports[MAX_PORTS];
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 98106d4e89f0..4da54b0b9233 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -200,7 +200,7 @@ static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
if (*pos == 0)
return SEQ_START_TOKEN;
- for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu + 1;
@@ -215,7 +215,7 @@ static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
struct net *net = seq_file_net(seq);
int cpu;
- for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu + 1;
@@ -336,7 +336,7 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &nf_conntrack_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_COUNT,
@@ -344,7 +344,7 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &init_net.ct.count,
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_BUCKETS,
@@ -352,7 +352,7 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &nf_conntrack_htable_size,
.maxlen = sizeof(unsigned int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_CHECKSUM,
@@ -360,7 +360,7 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &init_net.ct.sysctl_checksum,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = NET_NF_CONNTRACK_LOG_INVALID,
@@ -368,8 +368,8 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &init_net.ct.sysctl_log_invalid,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &log_invalid_proto_min,
.extra2 = &log_invalid_proto_max,
},
@@ -379,7 +379,7 @@ static ctl_table nf_ct_sysctl_table[] = {
.data = &nf_ct_expect_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ .ctl_name = 0 }
};
@@ -393,7 +393,7 @@ static ctl_table nf_ct_netfilter_table[] = {
.data = &nf_conntrack_max,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ .ctl_name = 0 }
};
diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c
index f57f6e7a71ee..46e646b2e9b9 100644
--- a/net/netfilter/nf_conntrack_tftp.c
+++ b/net/netfilter/nf_conntrack_tftp.c
@@ -22,6 +22,7 @@ MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
MODULE_DESCRIPTION("TFTP connection tracking helper");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ip_conntrack_tftp");
+MODULE_ALIAS_NFCT_HELPER("tftp");
#define MAX_PORTS 8
static unsigned short ports[MAX_PORTS];
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 41e0105d3828..fa49dc7fe100 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -30,6 +30,7 @@
#include <linux/random.h>
#include <net/sock.h>
#include <net/netfilter/nf_log.h>
+#include <net/netfilter/nfnetlink_log.h>
#include <asm/atomic.h>
@@ -474,8 +475,9 @@ __build_packet_message(struct nfulnl_instance *inst,
if (skb->sk) {
read_lock_bh(&skb->sk->sk_callback_lock);
if (skb->sk->sk_socket && skb->sk->sk_socket->file) {
- __be32 uid = htonl(skb->sk->sk_socket->file->f_uid);
- __be32 gid = htonl(skb->sk->sk_socket->file->f_gid);
+ struct file *file = skb->sk->sk_socket->file;
+ __be32 uid = htonl(file->f_cred->fsuid);
+ __be32 gid = htonl(file->f_cred->fsgid);
/* need to unlock here since NLA_PUT may goto */
read_unlock_bh(&skb->sk->sk_callback_lock);
NLA_PUT_BE32(inst->skb, NFULA_UID, uid);
@@ -533,7 +535,7 @@ static struct nf_loginfo default_loginfo = {
};
/* log handler for internal netfilter logging api */
-static void
+void
nfulnl_log_packet(u_int8_t pf,
unsigned int hooknum,
const struct sk_buff *skb,
@@ -648,6 +650,7 @@ alloc_failure:
/* FIXME: statistics */
goto unlock_and_release;
}
+EXPORT_SYMBOL_GPL(nfulnl_log_packet);
static int
nfulnl_rcv_nl_event(struct notifier_block *this,
diff --git a/net/netfilter/xt_NFLOG.c b/net/netfilter/xt_NFLOG.c
index 50e3a52d3b31..a57c5cf018ec 100644
--- a/net/netfilter/xt_NFLOG.c
+++ b/net/netfilter/xt_NFLOG.c
@@ -13,6 +13,7 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_NFLOG.h>
#include <net/netfilter/nf_log.h>
+#include <net/netfilter/nfnetlink_log.h>
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_DESCRIPTION("Xtables: packet logging to netlink using NFLOG");
@@ -31,8 +32,8 @@ nflog_tg(struct sk_buff *skb, const struct xt_target_param *par)
li.u.ulog.group = info->group;
li.u.ulog.qthreshold = info->threshold;
- nf_log_packet(par->family, par->hooknum, skb, par->in,
- par->out, &li, "%s", info->prefix);
+ nfulnl_log_packet(par->family, par->hooknum, skb, par->in,
+ par->out, &li, info->prefix);
return XT_CONTINUE;
}
diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c
index e5d3e8673287..0989f29ade2e 100644
--- a/net/netfilter/xt_dccp.c
+++ b/net/netfilter/xt_dccp.c
@@ -45,10 +45,8 @@ dccp_find_option(u_int8_t option,
unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
unsigned int i;
- if (dh->dccph_doff * 4 < __dccp_hdr_len(dh)) {
- *hotdrop = true;
- return false;
- }
+ if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
+ goto invalid;
if (!optlen)
return false;
@@ -57,9 +55,7 @@ dccp_find_option(u_int8_t option,
op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
if (op == NULL) {
/* If we don't have the whole header, drop packet. */
- spin_unlock_bh(&dccp_buflock);
- *hotdrop = true;
- return false;
+ goto partial;
}
for (i = 0; i < optlen; ) {
@@ -76,6 +72,12 @@ dccp_find_option(u_int8_t option,
spin_unlock_bh(&dccp_buflock);
return false;
+
+partial:
+ spin_unlock_bh(&dccp_buflock);
+invalid:
+ *hotdrop = true;
+ return false;
}
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index 6fc4292d46e6..f97fded024c4 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -893,23 +893,21 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
switch (family) {
case NFPROTO_IPV4:
- return seq_printf(s, "%ld %u.%u.%u.%u:%u->"
- "%u.%u.%u.%u:%u %u %u %u\n",
+ return seq_printf(s, "%ld %pI4:%u->%pI4:%u %u %u %u\n",
(long)(ent->expires - jiffies)/HZ,
- NIPQUAD(ent->dst.ip.src),
+ &ent->dst.ip.src,
ntohs(ent->dst.src_port),
- NIPQUAD(ent->dst.ip.dst),
+ &ent->dst.ip.dst,
ntohs(ent->dst.dst_port),
ent->rateinfo.credit, ent->rateinfo.credit_cap,
ent->rateinfo.cost);
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
case NFPROTO_IPV6:
- return seq_printf(s, "%ld " NIP6_FMT ":%u->"
- NIP6_FMT ":%u %u %u %u\n",
+ return seq_printf(s, "%ld %pI6:%u->%pI6:%u %u %u %u\n",
(long)(ent->expires - jiffies)/HZ,
- NIP6(*(struct in6_addr *)&ent->dst.ip6.src),
+ &ent->dst.ip6.src,
ntohs(ent->dst.src_port),
- NIP6(*(struct in6_addr *)&ent->dst.ip6.dst),
+ &ent->dst.ip6.dst,
ntohs(ent->dst.dst_port),
ent->rateinfo.credit, ent->rateinfo.credit_cap,
ent->rateinfo.cost);
diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c
index 7ac54eab0b00..501f9b623188 100644
--- a/net/netfilter/xt_iprange.c
+++ b/net/netfilter/xt_iprange.c
@@ -26,12 +26,11 @@ iprange_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
if ((ntohl(iph->saddr) < ntohl(info->src.min_ip)
|| ntohl(iph->saddr) > ntohl(info->src.max_ip))
^ !!(info->flags & IPRANGE_SRC_INV)) {
- pr_debug("src IP %u.%u.%u.%u NOT in range %s"
- "%u.%u.%u.%u-%u.%u.%u.%u\n",
- NIPQUAD(iph->saddr),
+ pr_debug("src IP %pI4 NOT in range %s%pI4-%pI4\n",
+ &iph->saddr,
info->flags & IPRANGE_SRC_INV ? "(INV) " : "",
- NIPQUAD(info->src.min_ip),
- NIPQUAD(info->src.max_ip));
+ &info->src.min_ip,
+ &info->src.max_ip);
return false;
}
}
@@ -39,12 +38,11 @@ iprange_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
if ((ntohl(iph->daddr) < ntohl(info->dst.min_ip)
|| ntohl(iph->daddr) > ntohl(info->dst.max_ip))
^ !!(info->flags & IPRANGE_DST_INV)) {
- pr_debug("dst IP %u.%u.%u.%u NOT in range %s"
- "%u.%u.%u.%u-%u.%u.%u.%u\n",
- NIPQUAD(iph->daddr),
+ pr_debug("dst IP %pI4 NOT in range %s%pI4-%pI4\n",
+ &iph->daddr,
info->flags & IPRANGE_DST_INV ? "(INV) " : "",
- NIPQUAD(info->dst.min_ip),
- NIPQUAD(info->dst.max_ip));
+ &info->dst.min_ip,
+ &info->dst.max_ip);
return false;
}
}
@@ -63,12 +61,11 @@ iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par)
m |= ntohl(iph->saddr) > ntohl(info->src_max.ip);
m ^= !!(info->flags & IPRANGE_SRC_INV);
if (m) {
- pr_debug("src IP " NIPQUAD_FMT " NOT in range %s"
- NIPQUAD_FMT "-" NIPQUAD_FMT "\n",
- NIPQUAD(iph->saddr),
+ pr_debug("src IP %pI4 NOT in range %s%pI4-%pI4\n",
+ &iph->saddr,
(info->flags & IPRANGE_SRC_INV) ? "(INV) " : "",
- NIPQUAD(info->src_max.ip),
- NIPQUAD(info->src_max.ip));
+ &info->src_max.ip,
+ &info->src_max.ip);
return false;
}
}
@@ -77,12 +74,11 @@ iprange_mt4(const struct sk_buff *skb, const struct xt_match_param *par)
m |= ntohl(iph->daddr) > ntohl(info->dst_max.ip);
m ^= !!(info->flags & IPRANGE_DST_INV);
if (m) {
- pr_debug("dst IP " NIPQUAD_FMT " NOT in range %s"
- NIPQUAD_FMT "-" NIPQUAD_FMT "\n",
- NIPQUAD(iph->daddr),
+ pr_debug("dst IP %pI4 NOT in range %s%pI4-%pI4\n",
+ &iph->daddr,
(info->flags & IPRANGE_DST_INV) ? "(INV) " : "",
- NIPQUAD(info->dst_min.ip),
- NIPQUAD(info->dst_max.ip));
+ &info->dst_min.ip,
+ &info->dst_max.ip);
return false;
}
}
diff --git a/net/netfilter/xt_owner.c b/net/netfilter/xt_owner.c
index f19ebd9b78f5..22b2a5e881ea 100644
--- a/net/netfilter/xt_owner.c
+++ b/net/netfilter/xt_owner.c
@@ -34,12 +34,12 @@ owner_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
return false;
if (info->match & IPT_OWNER_UID)
- if ((filp->f_uid != info->uid) ^
+ if ((filp->f_cred->fsuid != info->uid) ^
!!(info->invert & IPT_OWNER_UID))
return false;
if (info->match & IPT_OWNER_GID)
- if ((filp->f_gid != info->gid) ^
+ if ((filp->f_cred->fsgid != info->gid) ^
!!(info->invert & IPT_OWNER_GID))
return false;
@@ -60,12 +60,12 @@ owner_mt6_v0(const struct sk_buff *skb, const struct xt_match_param *par)
return false;
if (info->match & IP6T_OWNER_UID)
- if ((filp->f_uid != info->uid) ^
+ if ((filp->f_cred->fsuid != info->uid) ^
!!(info->invert & IP6T_OWNER_UID))
return false;
if (info->match & IP6T_OWNER_GID)
- if ((filp->f_gid != info->gid) ^
+ if ((filp->f_cred->fsgid != info->gid) ^
!!(info->invert & IP6T_OWNER_GID))
return false;
@@ -93,14 +93,14 @@ owner_mt(const struct sk_buff *skb, const struct xt_match_param *par)
(XT_OWNER_UID | XT_OWNER_GID)) == 0;
if (info->match & XT_OWNER_UID)
- if ((filp->f_uid >= info->uid_min &&
- filp->f_uid <= info->uid_max) ^
+ if ((filp->f_cred->fsuid >= info->uid_min &&
+ filp->f_cred->fsuid <= info->uid_max) ^
!(info->invert & XT_OWNER_UID))
return false;
if (info->match & XT_OWNER_GID)
- if ((filp->f_gid >= info->gid_min &&
- filp->f_gid <= info->gid_max) ^
+ if ((filp->f_cred->fsgid >= info->gid_min &&
+ filp->f_cred->fsgid <= info->gid_max) ^
!(info->invert & XT_OWNER_GID))
return false;
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 280c471bcdf4..fe80b614a400 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -72,9 +72,6 @@ struct recent_entry {
struct recent_table {
struct list_head list;
char name[XT_RECENT_NAME_LEN];
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *proc_old, *proc;
-#endif
unsigned int refcnt;
unsigned int entries;
struct list_head lru_list;
@@ -284,6 +281,9 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
{
const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *pde;
+#endif
unsigned i;
bool ret = false;
@@ -318,25 +318,25 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
for (i = 0; i < ip_list_hash_size; i++)
INIT_LIST_HEAD(&t->iphash[i]);
#ifdef CONFIG_PROC_FS
- t->proc = proc_create_data(t->name, ip_list_perms, recent_proc_dir,
+ pde = proc_create_data(t->name, ip_list_perms, recent_proc_dir,
&recent_mt_fops, t);
- if (t->proc == NULL) {
+ if (pde == NULL) {
kfree(t);
goto out;
}
+ pde->uid = ip_list_uid;
+ pde->gid = ip_list_gid;
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
- t->proc_old = proc_create_data(t->name, ip_list_perms, proc_old_dir,
+ pde = proc_create_data(t->name, ip_list_perms, proc_old_dir,
&recent_old_fops, t);
- if (t->proc_old == NULL) {
+ if (pde == NULL) {
remove_proc_entry(t->name, proc_old_dir);
kfree(t);
goto out;
}
- t->proc_old->uid = ip_list_uid;
- t->proc_old->gid = ip_list_gid;
+ pde->uid = ip_list_uid;
+ pde->gid = ip_list_gid;
#endif
- t->proc->uid = ip_list_uid;
- t->proc->gid = ip_list_gid;
#endif
spin_lock_bh(&recent_lock);
list_add_tail(&t->list, &tables);
@@ -422,13 +422,11 @@ static int recent_seq_show(struct seq_file *seq, void *v)
i = (e->index - 1) % ip_pkt_list_tot;
if (e->family == NFPROTO_IPV4)
- seq_printf(seq, "src=" NIPQUAD_FMT " ttl: %u last_seen: %lu "
- "oldest_pkt: %u", NIPQUAD(e->addr.ip), e->ttl,
- e->stamps[i], e->index);
+ seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u",
+ &e->addr.ip, e->ttl, e->stamps[i], e->index);
else
- seq_printf(seq, "src=" NIP6_FMT " ttl: %u last_seen: %lu "
- "oldest_pkt: %u", NIP6(e->addr.in6), e->ttl,
- e->stamps[i], e->index);
+ seq_printf(seq, "src=%pI6 ttl: %u last_seen: %lu oldest_pkt: %u",
+ &e->addr.in6, e->ttl, e->stamps[i], e->index);
for (i = 0; i < e->nstamps; i++)
seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]);
seq_printf(seq, "\n");
diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c
index 249f6b92f153..834c6eb7f484 100644
--- a/net/netlabel/netlabel_addrlist.c
+++ b/net/netlabel/netlabel_addrlist.c
@@ -337,7 +337,7 @@ void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
- audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr));
+ audit_log_format(audit_buf, " %s=%pI4", dir, &addr);
if (mask_val != 0xffffffff) {
u32 mask_len = 0;
while (mask_val > 0) {
@@ -371,7 +371,7 @@ void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
if (dev != NULL)
audit_log_format(audit_buf, " netif=%s", dev);
- audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr));
+ audit_log_format(audit_buf, " %s=%pI6", dir, addr);
if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
u32 mask_len = 0;
u32 mask_val;
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 0a0ef17b2a40..1821c5d50fb8 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -596,7 +596,6 @@ listdef_failure:
/**
* netlbl_mgmt_protocols_cb - Write an individual PROTOCOL message response
* @skb: the skb to write to
- * @seq: the NETLINK sequence number
* @cb: the NETLINK callback
* @protocol: the NetLabel protocol to use in the message
*
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 480184a857d2..9eb895c7a2a9 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -452,6 +452,10 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol)
if (err < 0)
goto out_module;
+ local_bh_disable();
+ sock_prot_inuse_add(net, &netlink_proto, 1);
+ local_bh_enable();
+
nlk = nlk_sk(sock->sk);
nlk->module = module;
out:
@@ -511,6 +515,9 @@ static int netlink_release(struct socket *sock)
kfree(nlk->groups);
nlk->groups = NULL;
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), &netlink_proto, -1);
+ local_bh_enable();
sock_put(sk);
return 0;
}
diff --git a/net/netlink/attr.c b/net/netlink/attr.c
index 2d106cfe1d27..56c3ce7fe29a 100644
--- a/net/netlink/attr.c
+++ b/net/netlink/attr.c
@@ -83,6 +83,12 @@ static int validate_nla(struct nlattr *nla, int maxtype,
if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla))
return -ERANGE;
break;
+ case NLA_NESTED:
+ /* a nested attributes is allowed to be empty; if its not,
+ * it must have a size of at least NLA_HDRLEN.
+ */
+ if (attrlen == 0)
+ break;
default:
if (pt->len)
minlen = pt->len;
@@ -233,7 +239,7 @@ size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
*
* Returns the number of bytes copied.
*/
-int nla_memcpy(void *dest, struct nlattr *src, int count)
+int nla_memcpy(void *dest, const struct nlattr *src, int count)
{
int minlen = min_t(int, count, nla_len(src));
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 9f1ea4a27b35..e9c05b8f4f45 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -609,7 +609,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
} else {
source = &addr->fsa_ax25.sax25_call;
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (user) {
nr->user_addr = user->call;
ax25_uid_put(user);
@@ -683,7 +683,7 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
}
source = (ax25_address *)dev->dev_addr;
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (user) {
nr->user_addr = user->call;
ax25_uid_put(user);
diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c
index 34c96c9674df..7b49591fe87c 100644
--- a/net/netrom/sysctl_net_netrom.c
+++ b/net/netrom/sysctl_net_netrom.c
@@ -41,8 +41,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_default_path_quality,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_quality,
.extra2 = &max_quality
},
@@ -52,8 +52,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_obsolescence_count_initialiser,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_obs,
.extra2 = &max_obs
},
@@ -63,8 +63,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_network_ttl_initialiser,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_ttl,
.extra2 = &max_ttl
},
@@ -74,8 +74,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t1,
.extra2 = &max_t1
},
@@ -85,8 +85,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_maximum_tries,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_n2,
.extra2 = &max_n2
},
@@ -96,8 +96,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_acknowledge_delay,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t2,
.extra2 = &max_t2
},
@@ -107,8 +107,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_busy_delay,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_t4,
.extra2 = &max_t4
},
@@ -118,8 +118,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_requested_window_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_window,
.extra2 = &max_window
},
@@ -129,8 +129,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_transport_no_activity_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_idle,
.extra2 = &max_idle
},
@@ -140,8 +140,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_routing_control,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_route,
.extra2 = &max_route
},
@@ -151,8 +151,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_link_fails_count,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_fails,
.extra2 = &max_fails
},
@@ -162,8 +162,8 @@ static ctl_table nr_table[] = {
.data = &sysctl_netrom_reset_circuit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_reset,
.extra2 = &max_reset
},
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index c718e7e3f7de..5f94db2f3e9e 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -872,6 +872,7 @@ static int packet_release(struct socket *sock)
write_lock_bh(&net->packet.sklist_lock);
sk_del_node_init(sk);
+ sock_prot_inuse_add(net, sk->sk_prot, -1);
write_unlock_bh(&net->packet.sklist_lock);
/*
@@ -1084,6 +1085,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol)
write_lock_bh(&net->packet.sklist_lock);
sk_add_node(sk, &net->packet.sklist);
+ sock_prot_inuse_add(net, &packet_proto, 1);
write_unlock_bh(&net->packet.sklist_lock);
return(0);
out:
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 9d211f12582b..13cb323f8c38 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -67,9 +67,6 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
struct phonet_protocol *pnp;
int err;
- if (net != &init_net)
- return -EAFNOSUPPORT;
-
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -352,9 +349,6 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
struct sockaddr_pn sa;
u16 len;
- if (dev_net(dev) != &init_net)
- goto out;
-
/* check we have at least a full Phonet header */
if (!pskb_pull(skb, sizeof(struct phonethdr)))
goto out;
@@ -373,7 +367,7 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
if (pn_sockaddr_get_addr(&sa) == 0)
goto out; /* currently, we cannot be device 0 */
- sk = pn_find_sock_by_sa(&sa);
+ sk = pn_find_sock_by_sa(dev_net(dev), &sa);
if (sk == NULL) {
if (can_respond(skb)) {
send_obj_unreachable(skb);
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
index 803eeef0aa85..b0ceac2d6cd1 100644
--- a/net/phonet/pep-gprs.c
+++ b/net/phonet/pep-gprs.c
@@ -40,23 +40,17 @@ struct gprs_dev {
void (*old_data_ready)(struct sock *, int);
void (*old_write_space)(struct sock *);
- struct net_device *net;
- struct net_device_stats stats;
-
- struct sk_buff_head tx_queue;
- struct work_struct tx_work;
- spinlock_t tx_lock;
- unsigned tx_max;
+ struct net_device *dev;
};
-static int gprs_type_trans(struct sk_buff *skb)
+static __be16 gprs_type_trans(struct sk_buff *skb)
{
const u8 *pvfc;
u8 buf;
pvfc = skb_header_pointer(skb, 0, 1, &buf);
if (!pvfc)
- return 0;
+ return htons(0);
/* Look at IP version field */
switch (*pvfc >> 4) {
case 4:
@@ -64,7 +58,15 @@ static int gprs_type_trans(struct sk_buff *skb)
case 6:
return htons(ETH_P_IPV6);
}
- return 0;
+ return htons(0);
+}
+
+static void gprs_writeable(struct gprs_dev *gp)
+{
+ struct net_device *dev = gp->dev;
+
+ if (pep_writeable(gp->sk))
+ netif_wake_queue(dev);
}
/*
@@ -73,18 +75,21 @@ static int gprs_type_trans(struct sk_buff *skb)
static void gprs_state_change(struct sock *sk)
{
- struct gprs_dev *dev = sk->sk_user_data;
+ struct gprs_dev *gp = sk->sk_user_data;
if (sk->sk_state == TCP_CLOSE_WAIT) {
- netif_stop_queue(dev->net);
- netif_carrier_off(dev->net);
+ struct net_device *dev = gp->dev;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
}
}
-static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
+static int gprs_recv(struct gprs_dev *gp, struct sk_buff *skb)
{
+ struct net_device *dev = gp->dev;
int err = 0;
- u16 protocol = gprs_type_trans(skb);
+ __be16 protocol = gprs_type_trans(skb);
if (!protocol) {
err = -EINVAL;
@@ -99,7 +104,7 @@ static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
* so wrap the IP packet as a single fragment of an head-less
* socket buffer. The network stack will pull what it needs,
* but at least, the whole IP payload is not memcpy'd. */
- rskb = netdev_alloc_skb(dev->net, 0);
+ rskb = netdev_alloc_skb(dev, 0);
if (!rskb) {
err = -ENOBUFS;
goto drop;
@@ -123,9 +128,9 @@ static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
skb->protocol = protocol;
skb_reset_mac_header(skb);
- skb->dev = dev->net;
+ skb->dev = dev;
- if (likely(dev->net->flags & IFF_UP)) {
+ if (likely(dev->flags & IFF_UP)) {
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
netif_rx(skb);
@@ -143,26 +148,21 @@ drop:
static void gprs_data_ready(struct sock *sk, int len)
{
- struct gprs_dev *dev = sk->sk_user_data;
+ struct gprs_dev *gp = sk->sk_user_data;
struct sk_buff *skb;
while ((skb = pep_read(sk)) != NULL) {
skb_orphan(skb);
- gprs_recv(dev, skb);
+ gprs_recv(gp, skb);
}
}
static void gprs_write_space(struct sock *sk)
{
- struct gprs_dev *dev = sk->sk_user_data;
- struct net_device *net = dev->net;
- unsigned credits = pep_writeable(sk);
-
- spin_lock_bh(&dev->tx_lock);
- dev->tx_max = credits;
- if (credits > skb_queue_len(&dev->tx_queue) && netif_running(net))
- netif_wake_queue(net);
- spin_unlock_bh(&dev->tx_lock);
+ struct gprs_dev *gp = sk->sk_user_data;
+
+ if (netif_running(gp->dev))
+ gprs_writeable(gp);
}
/*
@@ -173,22 +173,21 @@ static int gprs_open(struct net_device *dev)
{
struct gprs_dev *gp = netdev_priv(dev);
- gprs_write_space(gp->sk);
+ gprs_writeable(gp);
return 0;
}
static int gprs_close(struct net_device *dev)
{
- struct gprs_dev *gp = netdev_priv(dev);
-
netif_stop_queue(dev);
- flush_work(&gp->tx_work);
return 0;
}
-static int gprs_xmit(struct sk_buff *skb, struct net_device *net)
+static int gprs_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct gprs_dev *dev = netdev_priv(net);
+ struct gprs_dev *gp = netdev_priv(dev);
+ struct sock *sk = gp->sk;
+ int len, err;
switch (skb->protocol) {
case htons(ETH_P_IP):
@@ -199,84 +198,50 @@ static int gprs_xmit(struct sk_buff *skb, struct net_device *net)
return 0;
}
- spin_lock(&dev->tx_lock);
- if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) {
- skb_queue_tail(&dev->tx_queue, skb);
- skb = NULL;
- }
- if (skb_queue_len(&dev->tx_queue) >= dev->tx_max)
- netif_stop_queue(net);
- spin_unlock(&dev->tx_lock);
-
- schedule_work(&dev->tx_work);
- if (unlikely(skb))
+ skb_orphan(skb);
+ skb_set_owner_w(skb, sk);
+ len = skb->len;
+ err = pep_write(sk, skb);
+ if (err) {
+ LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n",
+ dev->name, err);
+ dev->stats.tx_aborted_errors++;
+ dev->stats.tx_errors++;
dev_kfree_skb(skb);
- return 0;
-}
-
-static void gprs_tx(struct work_struct *work)
-{
- struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work);
- struct sock *sk = dev->sk;
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) {
- int err;
-
- dev->stats.tx_bytes += skb->len;
+ } else {
dev->stats.tx_packets++;
-
- skb_orphan(skb);
- skb_set_owner_w(skb, sk);
-
- lock_sock(sk);
- err = pep_write(sk, skb);
- if (err) {
- LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n",
- dev->net->name, err);
- dev->stats.tx_aborted_errors++;
- dev->stats.tx_errors++;
- }
- release_sock(sk);
+ dev->stats.tx_bytes += len;
}
- lock_sock(sk);
- gprs_write_space(sk);
- release_sock(sk);
+ if (!pep_writeable(sk))
+ netif_stop_queue(dev);
+ return 0;
}
-static int gprs_set_mtu(struct net_device *net, int new_mtu)
+static int gprs_set_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11)))
return -EINVAL;
- net->mtu = new_mtu;
+ dev->mtu = new_mtu;
return 0;
}
-static struct net_device_stats *gprs_get_stats(struct net_device *net)
+static void gprs_setup(struct net_device *dev)
{
- struct gprs_dev *dev = netdev_priv(net);
-
- return &dev->stats;
-}
-
-static void gprs_setup(struct net_device *net)
-{
- net->features = NETIF_F_FRAGLIST;
- net->type = ARPHRD_NONE;
- net->flags = IFF_POINTOPOINT | IFF_NOARP;
- net->mtu = GPRS_DEFAULT_MTU;
- net->hard_header_len = 0;
- net->addr_len = 0;
- net->tx_queue_len = 10;
-
- net->destructor = free_netdev;
- net->open = gprs_open;
- net->stop = gprs_close;
- net->hard_start_xmit = gprs_xmit; /* mandatory */
- net->change_mtu = gprs_set_mtu;
- net->get_stats = gprs_get_stats;
+ dev->features = NETIF_F_FRAGLIST;
+ dev->type = ARPHRD_PHONET_PIPE;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = GPRS_DEFAULT_MTU;
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->destructor = free_netdev;
+ dev->open = gprs_open;
+ dev->stop = gprs_close;
+ dev->hard_start_xmit = gprs_xmit; /* mandatory */
+ dev->change_mtu = gprs_set_mtu;
}
/*
@@ -290,28 +255,25 @@ static void gprs_setup(struct net_device *net)
int gprs_attach(struct sock *sk)
{
static const char ifname[] = "gprs%d";
- struct gprs_dev *dev;
- struct net_device *net;
+ struct gprs_dev *gp;
+ struct net_device *dev;
int err;
if (unlikely(sk->sk_type == SOCK_STREAM))
return -EINVAL; /* need packet boundaries */
/* Create net device */
- net = alloc_netdev(sizeof(*dev), ifname, gprs_setup);
- if (!net)
+ dev = alloc_netdev(sizeof(*gp), ifname, gprs_setup);
+ if (!dev)
return -ENOMEM;
- dev = netdev_priv(net);
- dev->net = net;
- dev->tx_max = 0;
- spin_lock_init(&dev->tx_lock);
- skb_queue_head_init(&dev->tx_queue);
- INIT_WORK(&dev->tx_work, gprs_tx);
-
- netif_stop_queue(net);
- err = register_netdev(net);
+ gp = netdev_priv(dev);
+ gp->sk = sk;
+ gp->dev = dev;
+
+ netif_stop_queue(dev);
+ err = register_netdev(dev);
if (err) {
- free_netdev(net);
+ free_netdev(dev);
return err;
}
@@ -325,40 +287,38 @@ int gprs_attach(struct sock *sk)
err = -EINVAL;
goto out_rel;
}
- sk->sk_user_data = dev;
- dev->old_state_change = sk->sk_state_change;
- dev->old_data_ready = sk->sk_data_ready;
- dev->old_write_space = sk->sk_write_space;
+ sk->sk_user_data = gp;
+ gp->old_state_change = sk->sk_state_change;
+ gp->old_data_ready = sk->sk_data_ready;
+ gp->old_write_space = sk->sk_write_space;
sk->sk_state_change = gprs_state_change;
sk->sk_data_ready = gprs_data_ready;
sk->sk_write_space = gprs_write_space;
release_sock(sk);
-
sock_hold(sk);
- dev->sk = sk;
- printk(KERN_DEBUG"%s: attached\n", net->name);
- return net->ifindex;
+ printk(KERN_DEBUG"%s: attached\n", dev->name);
+ return dev->ifindex;
out_rel:
release_sock(sk);
- unregister_netdev(net);
+ unregister_netdev(dev);
return err;
}
void gprs_detach(struct sock *sk)
{
- struct gprs_dev *dev = sk->sk_user_data;
- struct net_device *net = dev->net;
+ struct gprs_dev *gp = sk->sk_user_data;
+ struct net_device *dev = gp->dev;
lock_sock(sk);
sk->sk_user_data = NULL;
- sk->sk_state_change = dev->old_state_change;
- sk->sk_data_ready = dev->old_data_ready;
- sk->sk_write_space = dev->old_write_space;
+ sk->sk_state_change = gp->old_state_change;
+ sk->sk_data_ready = gp->old_data_ready;
+ sk->sk_write_space = gp->old_write_space;
release_sock(sk);
- printk(KERN_DEBUG"%s: detached\n", net->name);
- unregister_netdev(net);
+ printk(KERN_DEBUG"%s: detached\n", dev->name);
+ unregister_netdev(dev);
sock_put(sk);
}
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index bc6d50f83249..bb3e67849b38 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -225,6 +225,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
{
struct pep_sock *pn = pep_sk(sk);
struct pnpipehdr *hdr = pnp_hdr(skb);
+ int wake = 0;
if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
return -EINVAL;
@@ -241,16 +242,16 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
case PN_LEGACY_FLOW_CONTROL:
switch (hdr->data[4]) {
case PEP_IND_BUSY:
- pn->tx_credits = 0;
+ atomic_set(&pn->tx_credits, 0);
break;
case PEP_IND_READY:
- pn->tx_credits = 1;
+ atomic_set(&pn->tx_credits, wake = 1);
break;
}
break;
case PN_ONE_CREDIT_FLOW_CONTROL:
if (hdr->data[4] == PEP_IND_READY)
- pn->tx_credits = 1;
+ atomic_set(&pn->tx_credits, wake = 1);
break;
}
break;
@@ -258,10 +259,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
break;
- if (pn->tx_credits + hdr->data[4] > 0xff)
- pn->tx_credits = 0xff;
- else
- pn->tx_credits += hdr->data[4];
+ atomic_add(wake = hdr->data[4], &pn->tx_credits);
break;
default:
@@ -269,7 +267,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
(unsigned)hdr->data[1]);
return -EOPNOTSUPP;
}
- if (pn->tx_credits)
+ if (wake)
sk->sk_write_space(sk);
return 0;
}
@@ -343,7 +341,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
}
/* fall through */
case PNS_PEP_DISABLE_REQ:
- pn->tx_credits = 0;
+ atomic_set(&pn->tx_credits, 0);
pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
break;
@@ -390,7 +388,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
/* fall through */
case PNS_PIPE_ENABLED_IND:
if (!pn_flow_safe(pn->tx_fc)) {
- pn->tx_credits = 1;
+ atomic_set(&pn->tx_credits, 1);
sk->sk_write_space(sk);
}
if (sk->sk_state == TCP_ESTABLISHED)
@@ -504,8 +502,9 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
newpn->pn_sk.resource = pn->pn_sk.resource;
skb_queue_head_init(&newpn->ctrlreq_queue);
newpn->pipe_handle = pipe_handle;
+ atomic_set(&newpn->tx_credits, 0);
newpn->peer_type = peer_type;
- newpn->rx_credits = newpn->tx_credits = 0;
+ newpn->rx_credits = 0;
newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
newpn->init_enable = enabled;
@@ -821,14 +820,18 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
struct pep_sock *pn = pep_sk(sk);
struct pnpipehdr *ph;
+ if (pn_flow_safe(pn->tx_fc) &&
+ !atomic_add_unless(&pn->tx_credits, -1, 0)) {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+
skb_push(skb, 3);
skb_reset_transport_header(skb);
ph = pnp_hdr(skb);
ph->utid = 0;
ph->message_id = PNS_PIPE_DATA;
ph->pipe_handle = pn->pipe_handle;
- if (pn_flow_safe(pn->tx_fc) && pn->tx_credits)
- pn->tx_credits--;
return pn_skb_send(sk, skb, &pipe_srv);
}
@@ -866,7 +869,7 @@ disabled:
BUG_ON(sk->sk_state != TCP_ESTABLISHED);
/* Wait until flow control allows TX */
- done = pn->tx_credits > 0;
+ done = atomic_read(&pn->tx_credits);
while (!done) {
DEFINE_WAIT(wait);
@@ -881,7 +884,7 @@ disabled:
prepare_to_wait(&sk->sk_socket->wait, &wait,
TASK_INTERRUPTIBLE);
- done = sk_wait_event(sk, &timeo, pn->tx_credits > 0);
+ done = sk_wait_event(sk, &timeo, atomic_read(&pn->tx_credits));
finish_wait(&sk->sk_socket->wait, &wait);
if (sk->sk_state != TCP_ESTABLISHED)
@@ -895,7 +898,8 @@ disabled:
goto out;
skb_reserve(skb, MAX_PHONET_HEADER + 3);
- if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits)
+ if (sk->sk_state != TCP_ESTABLISHED ||
+ !atomic_read(&pn->tx_credits))
goto disabled; /* sock_alloc_send_skb might sleep */
}
@@ -917,7 +921,7 @@ int pep_writeable(struct sock *sk)
{
struct pep_sock *pn = pep_sk(sk);
- return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0;
+ return atomic_read(&pn->tx_credits);
}
int pep_write(struct sock *sk, struct sk_buff *skb)
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index f93ff8ef47d0..5491bf5e354b 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -76,7 +76,7 @@ struct net_device *phonet_device_get(struct net *net)
dev = pnd->netdev;
BUG_ON(!dev);
- if (dev_net(dev) == net &&
+ if (net_eq(dev_net(dev), net) &&
(dev->reg_state == NETREG_REGISTERED) &&
((pnd->netdev->flags & IFF_UP)) == IFF_UP)
break;
@@ -140,12 +140,14 @@ u8 phonet_address_get(struct net_device *dev, u8 addr)
return addr;
}
-int phonet_address_lookup(u8 addr)
+int phonet_address_lookup(struct net *net, u8 addr)
{
struct phonet_device *pnd;
spin_lock_bh(&pndevs.lock);
list_for_each_entry(pnd, &pndevs.list, list) {
+ if (!net_eq(dev_net(pnd->netdev), net))
+ continue;
/* Don't allow unregistering devices! */
if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
((pnd->netdev->flags & IFF_UP)) != IFF_UP)
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index d81740187fb4..ada2a35bf7a2 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -57,7 +57,7 @@ static struct {
* Find address based on socket address, match only certain fields.
* Also grab sock if it was found. Remember to sock_put it later.
*/
-struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn)
+struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)
{
struct hlist_node *node;
struct sock *sknode;
@@ -71,6 +71,8 @@ struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn)
struct pn_sock *pn = pn_sk(sknode);
BUG_ON(!pn->sobject); /* unbound socket */
+ if (!net_eq(sock_net(sknode), net))
+ continue;
if (pn_port(obj)) {
/* Look up socket by port */
if (pn_port(pn->sobject) != pn_port(obj))
@@ -130,7 +132,7 @@ static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len)
handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr);
saddr = pn_addr(handle);
- if (saddr && phonet_address_lookup(saddr))
+ if (saddr && phonet_address_lookup(sock_net(sk), saddr))
return -EADDRNOTAVAIL;
lock_sock(sk);
@@ -225,7 +227,7 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
return POLLHUP;
- if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits)
+ if (sk->sk_state == TCP_ESTABLISHED && atomic_read(&pn->tx_credits))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
return mask;
@@ -361,6 +363,7 @@ static DEFINE_MUTEX(port_mutex);
int pn_sock_get_port(struct sock *sk, unsigned short sport)
{
static int port_cur;
+ struct net *net = sock_net(sk);
struct pn_sock *pn = pn_sk(sk);
struct sockaddr_pn try_sa;
struct sock *tmpsk;
@@ -381,7 +384,7 @@ int pn_sock_get_port(struct sock *sk, unsigned short sport)
port_cur = pmin;
pn_sockaddr_set_port(&try_sa, port_cur);
- tmpsk = pn_find_sock_by_sa(&try_sa);
+ tmpsk = pn_find_sock_by_sa(net, &try_sa);
if (tmpsk == NULL) {
sport = port_cur;
goto found;
@@ -391,7 +394,7 @@ int pn_sock_get_port(struct sock *sk, unsigned short sport)
} else {
/* try to find specific port */
pn_sockaddr_set_port(&try_sa, sport);
- tmpsk = pn_find_sock_by_sa(&try_sa);
+ tmpsk = pn_find_sock_by_sa(net, &try_sa);
if (tmpsk == NULL)
/* No sock there! We can use that port... */
goto found;
diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c
index 600a4309b8c8..7b5749ee2765 100644
--- a/net/phonet/sysctl.c
+++ b/net/phonet/sysctl.c
@@ -89,13 +89,13 @@ static struct ctl_table phonet_table[] = {
.data = &local_port_range,
.maxlen = sizeof(local_port_range),
.mode = 0644,
- .proc_handler = &proc_local_port_range,
+ .proc_handler = proc_local_port_range,
.strategy = NULL,
},
{ .ctl_name = 0 }
};
-struct ctl_path phonet_ctl_path[] = {
+static struct ctl_path phonet_ctl_path[] = {
{ .procname = "net", .ctl_name = CTL_NET, },
{ .procname = "phonet", .ctl_name = CTL_UNNUMBERED, },
{ },
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index bfdade72e066..84efde97c5a7 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -24,138 +24,318 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("Input layer to RF switch connector");
MODULE_LICENSE("GPL");
+enum rfkill_input_master_mode {
+ RFKILL_INPUT_MASTER_DONOTHING = 0,
+ RFKILL_INPUT_MASTER_RESTORE = 1,
+ RFKILL_INPUT_MASTER_UNBLOCKALL = 2,
+ RFKILL_INPUT_MASTER_MAX, /* marker */
+};
+
+/* Delay (in ms) between consecutive switch ops */
+#define RFKILL_OPS_DELAY 200
+
+static enum rfkill_input_master_mode rfkill_master_switch_mode =
+ RFKILL_INPUT_MASTER_UNBLOCKALL;
+module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0);
+MODULE_PARM_DESC(master_switch_mode,
+ "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all");
+
+enum rfkill_global_sched_op {
+ RFKILL_GLOBAL_OP_EPO = 0,
+ RFKILL_GLOBAL_OP_RESTORE,
+ RFKILL_GLOBAL_OP_UNLOCK,
+ RFKILL_GLOBAL_OP_UNBLOCK,
+};
+
+/*
+ * Currently, the code marked with RFKILL_NEED_SWSET is inactive.
+ * If handling of EV_SW SW_WLAN/WWAN/BLUETOOTH/etc is needed in the
+ * future, when such events are added, that code will be necessary.
+ */
+
struct rfkill_task {
- struct work_struct work;
- enum rfkill_type type;
- struct mutex mutex; /* ensures that task is serialized */
- spinlock_t lock; /* for accessing last and desired state */
- unsigned long last; /* last schedule */
- enum rfkill_state desired_state; /* on/off */
+ struct delayed_work dwork;
+
+ /* ensures that task is serialized */
+ struct mutex mutex;
+
+ /* protects everything below */
+ spinlock_t lock;
+
+ /* pending regular switch operations (1=pending) */
+ unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+#ifdef RFKILL_NEED_SWSET
+ /* set operation pending (1=pending) */
+ unsigned long sw_setpending[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ /* desired state for pending set operation (1=unblock) */
+ unsigned long sw_newstate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+#endif
+
+ /* should the state be complemented (1=yes) */
+ unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
+
+ bool global_op_pending;
+ enum rfkill_global_sched_op op;
+
+ /* last time it was scheduled */
+ unsigned long last_scheduled;
};
+static void __rfkill_handle_global_op(enum rfkill_global_sched_op op)
+{
+ unsigned int i;
+
+ switch (op) {
+ case RFKILL_GLOBAL_OP_EPO:
+ rfkill_epo();
+ break;
+ case RFKILL_GLOBAL_OP_RESTORE:
+ rfkill_restore_states();
+ break;
+ case RFKILL_GLOBAL_OP_UNLOCK:
+ rfkill_remove_epo_lock();
+ break;
+ case RFKILL_GLOBAL_OP_UNBLOCK:
+ rfkill_remove_epo_lock();
+ for (i = 0; i < RFKILL_TYPE_MAX; i++)
+ rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED);
+ break;
+ default:
+ /* memory corruption or bug, fail safely */
+ rfkill_epo();
+ WARN(1, "Unknown requested operation %d! "
+ "rfkill Emergency Power Off activated\n",
+ op);
+ }
+}
+
+#ifdef RFKILL_NEED_SWSET
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool sp, const bool s, const bool c)
+{
+ enum rfkill_state state;
+
+ if (sp)
+ state = (s) ? RFKILL_STATE_UNBLOCKED :
+ RFKILL_STATE_SOFT_BLOCKED;
+ else
+ state = rfkill_get_global_state(type);
+
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
+}
+#else
+static void __rfkill_handle_normal_op(const enum rfkill_type type,
+ const bool c)
+{
+ enum rfkill_state state;
+
+ state = rfkill_get_global_state(type);
+ if (c)
+ state = rfkill_state_complement(state);
+
+ rfkill_switch_all(type, state);
+}
+#endif
+
static void rfkill_task_handler(struct work_struct *work)
{
- struct rfkill_task *task = container_of(work, struct rfkill_task, work);
+ struct rfkill_task *task = container_of(work,
+ struct rfkill_task, dwork.work);
+ bool doit = true;
mutex_lock(&task->mutex);
- rfkill_switch_all(task->type, task->desired_state);
+ spin_lock_irq(&task->lock);
+ while (doit) {
+ if (task->global_op_pending) {
+ enum rfkill_global_sched_op op = task->op;
+ task->global_op_pending = false;
+ memset(task->sw_pending, 0, sizeof(task->sw_pending));
+ spin_unlock_irq(&task->lock);
+
+ __rfkill_handle_global_op(op);
+
+ /* make sure we do at least one pass with
+ * !task->global_op_pending */
+ spin_lock_irq(&task->lock);
+ continue;
+ } else if (!rfkill_is_epo_lock_active()) {
+ unsigned int i = 0;
+
+ while (!task->global_op_pending &&
+ i < RFKILL_TYPE_MAX) {
+ if (test_and_clear_bit(i, task->sw_pending)) {
+ bool c;
+#ifdef RFKILL_NEED_SWSET
+ bool sp, s;
+ sp = test_and_clear_bit(i,
+ task->sw_setpending);
+ s = test_bit(i, task->sw_newstate);
+#endif
+ c = test_and_clear_bit(i,
+ task->sw_togglestate);
+ spin_unlock_irq(&task->lock);
+
+#ifdef RFKILL_NEED_SWSET
+ __rfkill_handle_normal_op(i, sp, s, c);
+#else
+ __rfkill_handle_normal_op(i, c);
+#endif
+
+ spin_lock_irq(&task->lock);
+ }
+ i++;
+ }
+ }
+ doit = task->global_op_pending;
+ }
+ spin_unlock_irq(&task->lock);
mutex_unlock(&task->mutex);
}
-static void rfkill_task_epo_handler(struct work_struct *work)
+static struct rfkill_task rfkill_task = {
+ .dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork,
+ rfkill_task_handler),
+ .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex),
+ .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock),
+};
+
+static unsigned long rfkill_ratelimit(const unsigned long last)
{
- rfkill_epo();
+ const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY);
+ return (time_after(jiffies, last + delay)) ? 0 : delay;
}
-static DECLARE_WORK(epo_work, rfkill_task_epo_handler);
+static void rfkill_schedule_ratelimited(void)
+{
+ if (!delayed_work_pending(&rfkill_task.dwork)) {
+ schedule_delayed_work(&rfkill_task.dwork,
+ rfkill_ratelimit(rfkill_task.last_scheduled));
+ rfkill_task.last_scheduled = jiffies;
+ }
+}
-static void rfkill_schedule_epo(void)
+static void rfkill_schedule_global_op(enum rfkill_global_sched_op op)
{
- schedule_work(&epo_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ rfkill_task.op = op;
+ rfkill_task.global_op_pending = true;
+ if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) {
+ /* bypass the limiter for EPO */
+ cancel_delayed_work(&rfkill_task.dwork);
+ schedule_delayed_work(&rfkill_task.dwork, 0);
+ rfkill_task.last_scheduled = jiffies;
+ } else
+ rfkill_schedule_ratelimited();
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-static void rfkill_schedule_set(struct rfkill_task *task,
+#ifdef RFKILL_NEED_SWSET
+/* Use this if you need to add EV_SW SW_WLAN/WWAN/BLUETOOTH/etc handling */
+
+static void rfkill_schedule_set(enum rfkill_type type,
enum rfkill_state desired_state)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state = desired_state;
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ set_bit(type, rfkill_task.sw_setpending);
+ clear_bit(type, rfkill_task.sw_togglestate);
+ if (desired_state)
+ set_bit(type, rfkill_task.sw_newstate);
+ else
+ clear_bit(type, rfkill_task.sw_newstate);
+ rfkill_schedule_ratelimited();
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
+#endif
-static void rfkill_schedule_toggle(struct rfkill_task *task)
+static void rfkill_schedule_toggle(enum rfkill_type type)
{
unsigned long flags;
- if (unlikely(work_pending(&epo_work)))
+ if (rfkill_is_epo_lock_active())
return;
- spin_lock_irqsave(&task->lock, flags);
-
- if (time_after(jiffies, task->last + msecs_to_jiffies(200))) {
- task->desired_state =
- rfkill_state_complement(task->desired_state);
- task->last = jiffies;
- schedule_work(&task->work);
+ spin_lock_irqsave(&rfkill_task.lock, flags);
+ if (!rfkill_task.global_op_pending) {
+ set_bit(type, rfkill_task.sw_pending);
+ change_bit(type, rfkill_task.sw_togglestate);
+ rfkill_schedule_ratelimited();
}
-
- spin_unlock_irqrestore(&task->lock, flags);
+ spin_unlock_irqrestore(&rfkill_task.lock, flags);
}
-#define DEFINE_RFKILL_TASK(n, t) \
- struct rfkill_task n = { \
- .work = __WORK_INITIALIZER(n.work, \
- rfkill_task_handler), \
- .type = t, \
- .mutex = __MUTEX_INITIALIZER(n.mutex), \
- .lock = __SPIN_LOCK_UNLOCKED(n.lock), \
- .desired_state = RFKILL_STATE_UNBLOCKED, \
- }
-
-static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN);
-static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH);
-static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB);
-static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX);
-static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN);
-
static void rfkill_schedule_evsw_rfkillall(int state)
{
- /* EVERY radio type. state != 0 means radios ON */
- /* handle EPO (emergency power off) through shortcut */
if (state) {
- rfkill_schedule_set(&rfkill_wwan,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wimax,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_uwb,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_bt,
- RFKILL_STATE_UNBLOCKED);
- rfkill_schedule_set(&rfkill_wlan,
- RFKILL_STATE_UNBLOCKED);
+ switch (rfkill_master_switch_mode) {
+ case RFKILL_INPUT_MASTER_UNBLOCKALL:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK);
+ break;
+ case RFKILL_INPUT_MASTER_RESTORE:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE);
+ break;
+ case RFKILL_INPUT_MASTER_DONOTHING:
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK);
+ break;
+ default:
+ /* memory corruption or driver bug! fail safely */
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
+ WARN(1, "Unknown rfkill_master_switch_mode (%d), "
+ "driver bug or memory corruption detected!\n",
+ rfkill_master_switch_mode);
+ break;
+ }
} else
- rfkill_schedule_epo();
+ rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO);
}
static void rfkill_event(struct input_handle *handle, unsigned int type,
unsigned int code, int data)
{
if (type == EV_KEY && data == 1) {
+ enum rfkill_type t;
+
switch (code) {
case KEY_WLAN:
- rfkill_schedule_toggle(&rfkill_wlan);
+ t = RFKILL_TYPE_WLAN;
break;
case KEY_BLUETOOTH:
- rfkill_schedule_toggle(&rfkill_bt);
+ t = RFKILL_TYPE_BLUETOOTH;
break;
case KEY_UWB:
- rfkill_schedule_toggle(&rfkill_uwb);
+ t = RFKILL_TYPE_UWB;
break;
case KEY_WIMAX:
- rfkill_schedule_toggle(&rfkill_wimax);
+ t = RFKILL_TYPE_WIMAX;
break;
default:
- break;
+ return;
}
+ rfkill_schedule_toggle(t);
+ return;
} else if (type == EV_SW) {
switch (code) {
case SW_RFKILL_ALL:
rfkill_schedule_evsw_rfkillall(data);
- break;
+ return;
default:
- break;
+ return;
}
}
}
@@ -256,18 +436,23 @@ static struct input_handler rfkill_handler = {
static int __init rfkill_handler_init(void)
{
- unsigned long last_run = jiffies - msecs_to_jiffies(500);
- rfkill_wlan.last = last_run;
- rfkill_bt.last = last_run;
- rfkill_uwb.last = last_run;
- rfkill_wimax.last = last_run;
+ if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX)
+ return -EINVAL;
+
+ /*
+ * The penalty to not doing this is a possible RFKILL_OPS_DELAY delay
+ * at the first use. Acceptable, but if we can avoid it, why not?
+ */
+ rfkill_task.last_scheduled =
+ jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1;
return input_register_handler(&rfkill_handler);
}
static void __exit rfkill_handler_exit(void)
{
input_unregister_handler(&rfkill_handler);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rfkill_task.dwork);
+ rfkill_remove_epo_lock();
}
module_init(rfkill_handler_init);
diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h
index bbfa646157c6..fe8df6b5b935 100644
--- a/net/rfkill/rfkill-input.h
+++ b/net/rfkill/rfkill-input.h
@@ -14,5 +14,8 @@
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state);
void rfkill_epo(void);
void rfkill_restore_states(void);
+void rfkill_remove_epo_lock(void);
+bool rfkill_is_epo_lock_active(void);
+enum rfkill_state rfkill_get_global_state(const enum rfkill_type type);
#endif /* __RFKILL_INPUT_H */
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index 25ba3bd57e66..3c94f76d5525 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -51,51 +51,7 @@ struct rfkill_gsw_state {
static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX];
static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)];
-
-static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list);
-
-
-/**
- * register_rfkill_notifier - Add notifier to rfkill notifier chain
- * @nb: pointer to the new entry to add to the chain
- *
- * See blocking_notifier_chain_register() for return value and further
- * observations.
- *
- * Adds a notifier to the rfkill notifier chain. The chain will be
- * called with a pointer to the relevant rfkill structure as a parameter,
- * refer to include/linux/rfkill.h for the possible events.
- *
- * Notifiers added to this chain are to always return NOTIFY_DONE. This
- * chain is a blocking notifier chain: notifiers can sleep.
- *
- * Calls to this chain may have been done through a workqueue. One must
- * assume unordered asynchronous behaviour, there is no way to know if
- * actions related to the event that generated the notification have been
- * carried out already.
- */
-int register_rfkill_notifier(struct notifier_block *nb)
-{
- BUG_ON(!nb);
- return blocking_notifier_chain_register(&rfkill_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(register_rfkill_notifier);
-
-/**
- * unregister_rfkill_notifier - remove notifier from rfkill notifier chain
- * @nb: pointer to the entry to remove from the chain
- *
- * See blocking_notifier_chain_unregister() for return value and further
- * observations.
- *
- * Removes a notifier from the rfkill notifier chain.
- */
-int unregister_rfkill_notifier(struct notifier_block *nb)
-{
- BUG_ON(!nb);
- return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb);
-}
-EXPORT_SYMBOL_GPL(unregister_rfkill_notifier);
+static bool rfkill_epo_lock_active;
static void rfkill_led_trigger(struct rfkill *rfkill,
@@ -123,12 +79,9 @@ static void rfkill_led_trigger_activate(struct led_classdev *led)
}
#endif /* CONFIG_RFKILL_LEDS */
-static void notify_rfkill_state_change(struct rfkill *rfkill)
+static void rfkill_uevent(struct rfkill *rfkill)
{
- rfkill_led_trigger(rfkill, rfkill->state);
- blocking_notifier_call_chain(&rfkill_notifier_list,
- RFKILL_STATE_CHANGED,
- rfkill);
+ kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
}
static void update_rfkill_state(struct rfkill *rfkill)
@@ -141,7 +94,7 @@ static void update_rfkill_state(struct rfkill *rfkill)
oldstate = rfkill->state;
rfkill->state = newstate;
if (oldstate != newstate)
- notify_rfkill_state_change(rfkill);
+ rfkill_uevent(rfkill);
}
mutex_unlock(&rfkill->mutex);
}
@@ -219,7 +172,7 @@ static int rfkill_toggle_radio(struct rfkill *rfkill,
}
if (force || rfkill->state != oldstate)
- notify_rfkill_state_change(rfkill);
+ rfkill_uevent(rfkill);
return retval;
}
@@ -264,11 +217,14 @@ static void __rfkill_switch_all(const enum rfkill_type type,
*
* Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state).
* Please refer to __rfkill_switch_all() for details.
+ *
+ * Does nothing if the EPO lock is active.
*/
void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state)
{
mutex_lock(&rfkill_global_mutex);
- __rfkill_switch_all(type, state);
+ if (!rfkill_epo_lock_active)
+ __rfkill_switch_all(type, state);
mutex_unlock(&rfkill_global_mutex);
}
EXPORT_SYMBOL(rfkill_switch_all);
@@ -289,6 +245,7 @@ void rfkill_epo(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = true;
list_for_each_entry(rfkill, &rfkill_list, node) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1);
@@ -317,6 +274,7 @@ void rfkill_restore_states(void)
mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
for (i = 0; i < RFKILL_TYPE_MAX; i++)
__rfkill_switch_all(i, rfkill_global_states[i].default_state);
mutex_unlock(&rfkill_global_mutex);
@@ -324,6 +282,48 @@ void rfkill_restore_states(void)
EXPORT_SYMBOL_GPL(rfkill_restore_states);
/**
+ * rfkill_remove_epo_lock - unlock state changes
+ *
+ * Used by rfkill-input manually unlock state changes, when
+ * the EPO switch is deactivated.
+ */
+void rfkill_remove_epo_lock(void)
+{
+ mutex_lock(&rfkill_global_mutex);
+ rfkill_epo_lock_active = false;
+ mutex_unlock(&rfkill_global_mutex);
+}
+EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock);
+
+/**
+ * rfkill_is_epo_lock_active - returns true EPO is active
+ *
+ * Returns 0 (false) if there is NOT an active EPO contidion,
+ * and 1 (true) if there is an active EPO contition, which
+ * locks all radios in one of the BLOCKED states.
+ *
+ * Can be called in atomic context.
+ */
+bool rfkill_is_epo_lock_active(void)
+{
+ return rfkill_epo_lock_active;
+}
+EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active);
+
+/**
+ * rfkill_get_global_state - returns global state for a type
+ * @type: the type to get the global state of
+ *
+ * Returns the current global state for a given wireless
+ * device type.
+ */
+enum rfkill_state rfkill_get_global_state(const enum rfkill_type type)
+{
+ return rfkill_global_states[type].current_state;
+}
+EXPORT_SYMBOL_GPL(rfkill_get_global_state);
+
+/**
* rfkill_force_state - Force the internal rfkill radio state
* @rfkill: pointer to the rfkill class to modify.
* @state: the current radio state the class should be forced to.
@@ -357,7 +357,7 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state)
rfkill->state = state;
if (state != oldstate)
- notify_rfkill_state_change(rfkill);
+ rfkill_uevent(rfkill);
mutex_unlock(&rfkill->mutex);
@@ -431,9 +431,15 @@ static ssize_t rfkill_state_store(struct device *dev,
state != RFKILL_STATE_SOFT_BLOCKED)
return -EINVAL;
- if (mutex_lock_interruptible(&rfkill->mutex))
- return -ERESTARTSYS;
- error = rfkill_toggle_radio(rfkill, state, 0);
+ error = mutex_lock_killable(&rfkill->mutex);
+ if (error)
+ return error;
+
+ if (!rfkill_epo_lock_active)
+ error = rfkill_toggle_radio(rfkill, state, 0);
+ else
+ error = -EPERM;
+
mutex_unlock(&rfkill->mutex);
return error ? error : count;
@@ -472,12 +478,12 @@ static ssize_t rfkill_claim_store(struct device *dev,
* Take the global lock to make sure the kernel is not in
* the middle of rfkill_switch_all
*/
- error = mutex_lock_interruptible(&rfkill_global_mutex);
+ error = mutex_lock_killable(&rfkill_global_mutex);
if (error)
return error;
if (rfkill->user_claim != claim) {
- if (!claim) {
+ if (!claim && !rfkill_epo_lock_active) {
mutex_lock(&rfkill->mutex);
rfkill_toggle_radio(rfkill,
rfkill_global_states[rfkill->type].current_state,
@@ -511,24 +517,48 @@ static void rfkill_release(struct device *dev)
#ifdef CONFIG_PM
static int rfkill_suspend(struct device *dev, pm_message_t state)
{
+ struct rfkill *rfkill = to_rfkill(dev);
+
/* mark class device as suspended */
if (dev->power.power_state.event != state.event)
dev->power.power_state = state;
+ /* store state for the resume handler */
+ rfkill->state_for_resume = rfkill->state;
+
return 0;
}
static int rfkill_resume(struct device *dev)
{
struct rfkill *rfkill = to_rfkill(dev);
+ enum rfkill_state newstate;
if (dev->power.power_state.event != PM_EVENT_ON) {
mutex_lock(&rfkill->mutex);
dev->power.power_state.event = PM_EVENT_ON;
- /* restore radio state AND notify everybody */
- rfkill_toggle_radio(rfkill, rfkill->state, 1);
+ /*
+ * rfkill->state could have been modified before we got
+ * called, and won't be updated by rfkill_toggle_radio()
+ * in force mode. Sync it FIRST.
+ */
+ if (rfkill->get_state &&
+ !rfkill->get_state(rfkill->data, &newstate))
+ rfkill->state = newstate;
+
+ /*
+ * If we are under EPO, kick transmitter offline,
+ * otherwise restore to pre-suspend state.
+ *
+ * Issue a notification in any case
+ */
+ rfkill_toggle_radio(rfkill,
+ rfkill_epo_lock_active ?
+ RFKILL_STATE_SOFT_BLOCKED :
+ rfkill->state_for_resume,
+ 1);
mutex_unlock(&rfkill->mutex);
}
@@ -540,28 +570,6 @@ static int rfkill_resume(struct device *dev)
#define rfkill_resume NULL
#endif
-static int rfkill_blocking_uevent_notifier(struct notifier_block *nb,
- unsigned long eventid,
- void *data)
-{
- struct rfkill *rfkill = (struct rfkill *)data;
-
- switch (eventid) {
- case RFKILL_STATE_CHANGED:
- kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
- break;
- default:
- break;
- }
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block rfkill_blocking_uevent_nb = {
- .notifier_call = rfkill_blocking_uevent_notifier,
- .priority = 0,
-};
-
static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct rfkill *rfkill = to_rfkill(dev);
@@ -711,7 +719,7 @@ static void rfkill_led_trigger_register(struct rfkill *rfkill)
int error;
if (!rfkill->led_trigger.name)
- rfkill->led_trigger.name = rfkill->dev.bus_id;
+ rfkill->led_trigger.name = dev_name(&rfkill->dev);
if (!rfkill->led_trigger.activate)
rfkill->led_trigger.activate = rfkill_led_trigger_activate;
error = led_trigger_register(&rfkill->led_trigger);
@@ -752,8 +760,7 @@ int __must_check rfkill_register(struct rfkill *rfkill)
"badly initialized rfkill struct\n"))
return -EINVAL;
- snprintf(dev->bus_id, sizeof(dev->bus_id),
- "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1);
+ dev_set_name(dev, "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1);
rfkill_led_trigger_register(rfkill);
@@ -833,6 +840,7 @@ int rfkill_set_default(enum rfkill_type type, enum rfkill_state state)
if (!test_and_set_bit(type, rfkill_states_lockdflt)) {
rfkill_global_states[type].default_state = state;
+ rfkill_global_states[type].current_state = state;
error = 0;
} else
error = -EPERM;
@@ -864,14 +872,11 @@ static int __init rfkill_init(void)
return error;
}
- register_rfkill_notifier(&rfkill_blocking_uevent_nb);
-
return 0;
}
static void __exit rfkill_exit(void)
{
- unregister_rfkill_notifier(&rfkill_blocking_uevent_nb);
class_unregister(&rfkill_class);
}
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 0c1cc7612800..01392649b462 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -690,7 +690,7 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
source = &addr->srose_call;
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (user) {
rose->source_call = user->call;
ax25_uid_put(user);
@@ -791,7 +791,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
goto out_release;
}
- user = ax25_findbyuid(current->euid);
+ user = ax25_findbyuid(current_euid());
if (!user) {
err = -EINVAL;
goto out_release;
diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c
index 20be3485a97f..3bfe504faf86 100644
--- a/net/rose/sysctl_net_rose.c
+++ b/net/rose/sysctl_net_rose.c
@@ -31,8 +31,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_restart_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer
},
@@ -42,8 +42,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_call_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer
},
@@ -53,8 +53,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_reset_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer
},
@@ -64,8 +64,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_clear_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer
},
@@ -75,8 +75,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_no_activity_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_idle,
.extra2 = &max_idle
},
@@ -86,8 +86,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_ack_hold_back_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer
},
@@ -97,8 +97,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_routing_control,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_route,
.extra2 = &max_route
},
@@ -108,8 +108,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_link_fail_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_ftimer,
.extra2 = &max_ftimer
},
@@ -119,8 +119,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_maximum_vcs,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_maxvcs,
.extra2 = &max_maxvcs
},
@@ -130,8 +130,8 @@ static ctl_table rose_table[] = {
.data = &sysctl_rose_window_size,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_window,
.extra2 = &max_window
},
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 32e489118beb..d7d2bed7a699 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -96,9 +96,9 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
switch (srx->transport.family) {
case AF_INET:
- _debug("INET: %x @ %u.%u.%u.%u",
+ _debug("INET: %x @ %pI4",
ntohs(srx->transport.sin.sin_port),
- NIPQUAD(srx->transport.sin.sin_addr));
+ &srx->transport.sin.sin_addr);
if (srx->transport_len > 8)
memset((void *)&srx->transport + 8, 0,
srx->transport_len - 8);
diff --git a/net/rxrpc/ar-connection.c b/net/rxrpc/ar-connection.c
index 3869a5866752..0f1218b8d289 100644
--- a/net/rxrpc/ar-connection.c
+++ b/net/rxrpc/ar-connection.c
@@ -826,7 +826,7 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn)
/*
* reap dead connections
*/
-void rxrpc_connection_reaper(struct work_struct *work)
+static void rxrpc_connection_reaper(struct work_struct *work)
{
struct rxrpc_connection *conn, *_p;
unsigned long now, earliest, reap_time;
diff --git a/net/rxrpc/ar-connevent.c b/net/rxrpc/ar-connevent.c
index 1ada43d51165..dc5cb1e19509 100644
--- a/net/rxrpc/ar-connevent.c
+++ b/net/rxrpc/ar-connevent.c
@@ -126,7 +126,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
* mark a call as being on a now-secured channel
* - must be called with softirqs disabled
*/
-void rxrpc_call_is_secure(struct rxrpc_call *call)
+static void rxrpc_call_is_secure(struct rxrpc_call *call)
{
_enter("%p", call);
if (call) {
diff --git a/net/rxrpc/ar-error.c b/net/rxrpc/ar-error.c
index 6cb3e8890e7e..d4d1ae26d293 100644
--- a/net/rxrpc/ar-error.c
+++ b/net/rxrpc/ar-error.c
@@ -49,8 +49,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
port = serr->port;
- _net("Rx UDP Error from "NIPQUAD_FMT":%hu",
- NIPQUAD(addr), ntohs(port));
+ _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port));
_debug("Msg l:%d d:%d", skb->len, skb->data_len);
peer = rxrpc_find_peer(local, addr, port);
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index 9a8ff684da79..ad8c7a782da1 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -287,6 +287,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
time_t expiry,
u32 kvno)
{
+ const struct cred *cred = current_cred();
struct key *key;
int ret;
@@ -297,7 +298,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
_enter("");
- key = key_alloc(&key_type_rxrpc, "x", 0, 0, current, 0,
+ key = key_alloc(&key_type_rxrpc, "x", 0, 0, cred, 0,
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
_leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
@@ -340,10 +341,11 @@ EXPORT_SYMBOL(rxrpc_get_server_data_key);
*/
struct key *rxrpc_get_null_key(const char *keyname)
{
+ const struct cred *cred = current_cred();
struct key *key;
int ret;
- key = key_alloc(&key_type_rxrpc, keyname, 0, 0, current,
+ key = key_alloc(&key_type_rxrpc, keyname, 0, 0, cred,
KEY_POS_SEARCH, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key))
return key;
diff --git a/net/rxrpc/ar-local.c b/net/rxrpc/ar-local.c
index f3a2bd747a8f..807535ff29b5 100644
--- a/net/rxrpc/ar-local.c
+++ b/net/rxrpc/ar-local.c
@@ -131,10 +131,10 @@ struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
struct rxrpc_local *local;
int ret;
- _enter("{%d,%u,%u.%u.%u.%u+%hu}",
+ _enter("{%d,%u,%pI4+%hu}",
srx->transport_type,
srx->transport.family,
- NIPQUAD(srx->transport.sin.sin_addr),
+ &srx->transport.sin.sin_addr,
ntohs(srx->transport.sin.sin_port));
down_write(&rxrpc_local_sem);
@@ -143,10 +143,10 @@ struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
read_lock_bh(&rxrpc_local_lock);
list_for_each_entry(local, &rxrpc_locals, link) {
- _debug("CMP {%d,%u,%u.%u.%u.%u+%hu}",
+ _debug("CMP {%d,%u,%pI4+%hu}",
local->srx.transport_type,
local->srx.transport.family,
- NIPQUAD(local->srx.transport.sin.sin_addr),
+ &local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port));
if (local->srx.transport_type != srx->transport_type ||
@@ -188,11 +188,11 @@ struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *srx)
up_write(&rxrpc_local_sem);
- _net("LOCAL new %d {%d,%u,%u.%u.%u.%u+%hu}",
+ _net("LOCAL new %d {%d,%u,%pI4+%hu}",
local->debug_id,
local->srx.transport_type,
local->srx.transport.family,
- NIPQUAD(local->srx.transport.sin.sin_addr),
+ &local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port));
_leave(" = %p [new]", local);
@@ -203,11 +203,11 @@ found_local:
read_unlock_bh(&rxrpc_local_lock);
up_write(&rxrpc_local_sem);
- _net("LOCAL old %d {%d,%u,%u.%u.%u.%u+%hu}",
+ _net("LOCAL old %d {%d,%u,%pI4+%hu}",
local->debug_id,
local->srx.transport_type,
local->srx.transport.family,
- NIPQUAD(local->srx.transport.sin.sin_addr),
+ &local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port));
_leave(" = %p [reuse]", local);
diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c
index 2abe2081a5e8..edc026c1eb76 100644
--- a/net/rxrpc/ar-peer.c
+++ b/net/rxrpc/ar-peer.c
@@ -123,10 +123,10 @@ struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp)
const char *new = "old";
int usage;
- _enter("{%d,%d,%u.%u.%u.%u+%hu}",
+ _enter("{%d,%d,%pI4+%hu}",
srx->transport_type,
srx->transport_len,
- NIPQUAD(srx->transport.sin.sin_addr),
+ &srx->transport.sin.sin_addr,
ntohs(srx->transport.sin.sin_port));
/* search the peer list first */
@@ -177,12 +177,12 @@ struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp)
new = "new";
success:
- _net("PEER %s %d {%d,%u,%u.%u.%u.%u+%hu}",
+ _net("PEER %s %d {%d,%u,%pI4+%hu}",
new,
peer->debug_id,
peer->srx.transport_type,
peer->srx.transport.family,
- NIPQUAD(peer->srx.transport.sin.sin_addr),
+ &peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
_leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
diff --git a/net/rxrpc/ar-proc.c b/net/rxrpc/ar-proc.c
index 017322e2786d..38047f713f2c 100644
--- a/net/rxrpc/ar-proc.c
+++ b/net/rxrpc/ar-proc.c
@@ -61,12 +61,12 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
call = list_entry(v, struct rxrpc_call, link);
trans = call->conn->trans;
- sprintf(lbuff, NIPQUAD_FMT":%u",
- NIPQUAD(trans->local->srx.transport.sin.sin_addr),
+ sprintf(lbuff, "%pI4:%u",
+ &trans->local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port));
- sprintf(rbuff, NIPQUAD_FMT":%u",
- NIPQUAD(trans->peer->srx.transport.sin.sin_addr),
+ sprintf(rbuff, "%pI4:%u",
+ &trans->peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port));
seq_printf(seq,
@@ -144,12 +144,12 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
conn = list_entry(v, struct rxrpc_connection, link);
trans = conn->trans;
- sprintf(lbuff, NIPQUAD_FMT":%u",
- NIPQUAD(trans->local->srx.transport.sin.sin_addr),
+ sprintf(lbuff, "%pI4:%u",
+ &trans->local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port));
- sprintf(rbuff, NIPQUAD_FMT":%u",
- NIPQUAD(trans->peer->srx.transport.sin.sin_addr),
+ sprintf(rbuff, "%pI4:%u",
+ &trans->peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port));
seq_printf(seq,
diff --git a/net/rxrpc/ar-security.c b/net/rxrpc/ar-security.c
index 60d1d364430a..dc62920ee19a 100644
--- a/net/rxrpc/ar-security.c
+++ b/net/rxrpc/ar-security.c
@@ -40,7 +40,7 @@ static void rxrpc_security_put(struct rxrpc_security *sec)
/*
* look up an rxrpc security module
*/
-struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
+static struct rxrpc_security *rxrpc_security_lookup(u8 security_index)
{
struct rxrpc_security *sec = NULL;
diff --git a/net/rxrpc/ar-transport.c b/net/rxrpc/ar-transport.c
index 64069c8769a5..0936e1acc30e 100644
--- a/net/rxrpc/ar-transport.c
+++ b/net/rxrpc/ar-transport.c
@@ -78,10 +78,10 @@ struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local,
const char *new = "old";
int usage;
- _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},",
- NIPQUAD(local->srx.transport.sin.sin_addr),
+ _enter("{%pI4+%hu},{%pI4+%hu},",
+ &local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port),
- NIPQUAD(peer->srx.transport.sin.sin_addr),
+ &peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
/* search the transport list first */
@@ -149,10 +149,10 @@ struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local,
{
struct rxrpc_transport *trans;
- _enter("{%u.%u.%u.%u+%hu},{%u.%u.%u.%u+%hu},",
- NIPQUAD(local->srx.transport.sin.sin_addr),
+ _enter("{%pI4+%hu},{%pI4+%hu},",
+ &local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port),
- NIPQUAD(peer->srx.transport.sin.sin_addr),
+ &peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
/* search the transport list */
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index ba3f6e49fddc..ef8f91030a15 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -897,7 +897,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
/* get the IPv4 address of the entity that requested the ticket */
memcpy(&addr, p, sizeof(addr));
p += 4;
- _debug("KIV ADDR : "NIPQUAD_FMT, NIPQUAD(addr));
+ _debug("KIV ADDR : %pI4", &addr);
/* get the session key from the ticket */
memcpy(&key, p, sizeof(key));
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 6767e54155db..929218a47620 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -194,6 +194,17 @@ config NET_SCH_NETEM
If unsure, say N.
+config NET_SCH_DRR
+ tristate "Deficit Round Robin scheduler (DRR)"
+ help
+ Say Y here if you want to use the Deficit Round Robin (DRR) packet
+ scheduling algorithm.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sch_drr.
+
+ If unsure, say N.
+
config NET_SCH_INGRESS
tristate "Ingress Qdisc"
depends on NET_CLS_ACT
@@ -316,6 +327,14 @@ config NET_CLS_FLOW
To compile this code as a module, choose M here: the
module will be called cls_flow.
+config NET_CLS_CGROUP
+ bool "Control Group Classifier"
+ select NET_CLS
+ depends on CGROUPS
+ ---help---
+ Say Y here if you want to classify packets based on the control
+ cgroup of their process.
+
config NET_EMATCH
bool "Extended Matches"
select NET_CLS
diff --git a/net/sched/Makefile b/net/sched/Makefile
index e60c9925b269..54d950cd4b8d 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o
obj-$(CONFIG_NET_SCH_MULTIQ) += sch_multiq.o
obj-$(CONFIG_NET_SCH_ATM) += sch_atm.o
obj-$(CONFIG_NET_SCH_NETEM) += sch_netem.o
+obj-$(CONFIG_NET_SCH_DRR) += sch_drr.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
obj-$(CONFIG_NET_CLS_FW) += cls_fw.o
@@ -38,6 +39,7 @@ obj-$(CONFIG_NET_CLS_TCINDEX) += cls_tcindex.o
obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o
obj-$(CONFIG_NET_CLS_BASIC) += cls_basic.o
obj-$(CONFIG_NET_CLS_FLOW) += cls_flow.o
+obj-$(CONFIG_NET_CLS_CGROUP) += cls_cgroup.o
obj-$(CONFIG_NET_EMATCH) += ematch.o
obj-$(CONFIG_NET_EMATCH_CMP) += em_cmp.o
obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 8f457f1e0acf..9d03cc33b6cc 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -214,12 +214,14 @@ struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind,
}
EXPORT_SYMBOL(tcf_hash_check);
-struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a, int size, int bind, u32 *idx_gen, struct tcf_hashinfo *hinfo)
+struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
+ struct tc_action *a, int size, int bind,
+ u32 *idx_gen, struct tcf_hashinfo *hinfo)
{
struct tcf_common *p = kzalloc(size, GFP_KERNEL);
if (unlikely(!p))
- return p;
+ return ERR_PTR(-ENOMEM);
p->tcfc_refcnt = 1;
if (bind)
p->tcfc_bindcnt = 1;
@@ -228,9 +230,15 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, struct tc_acti
p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo);
p->tcfc_tm.install = jiffies;
p->tcfc_tm.lastuse = jiffies;
- if (est)
- gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
- &p->tcfc_lock, est);
+ if (est) {
+ int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
+ &p->tcfc_lock, est);
+ if (err) {
+ kfree(p);
+ return ERR_PTR(err);
+ }
+ }
+
a->priv = (void *) p;
return p;
}
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index ac04289da5d7..e7f796aec657 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -88,8 +88,8 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
if (!pc) {
pc = tcf_hash_create(parm->index, est, a, sizeof(*gact),
bind, &gact_idx_gen, &gact_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
ret = ACT_P_CREATED;
} else {
if (!ovr) {
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 0453d79ebf57..082c520b0def 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -136,8 +136,8 @@ static int tcf_ipt_init(struct nlattr *nla, struct nlattr *est,
if (!pc) {
pc = tcf_hash_create(index, est, a, sizeof(*ipt), bind,
&ipt_idx_gen, &ipt_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
ret = ACT_P_CREATED;
} else {
if (!ovr) {
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 70341c020b6d..b9aaab4e0354 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -105,8 +105,8 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est,
return -EINVAL;
pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind,
&mirred_idx_gen, &mirred_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
ret = ACT_P_CREATED;
} else {
if (!ovr) {
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
index 7b39ed485bca..d885ba311564 100644
--- a/net/sched/act_nat.c
+++ b/net/sched/act_nat.c
@@ -68,8 +68,8 @@ static int tcf_nat_init(struct nlattr *nla, struct nlattr *est,
if (!pc) {
pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind,
&nat_idx_gen, &nat_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
p = to_tcf_nat(pc);
ret = ACT_P_CREATED;
} else {
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index d5f4e3404864..96c0ed115e2a 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -68,8 +68,8 @@ static int tcf_pedit_init(struct nlattr *nla, struct nlattr *est,
return -EINVAL;
pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind,
&pedit_idx_gen, &pedit_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
p = to_pedit(pc);
keys = kmalloc(ksize, GFP_KERNEL);
if (keys == NULL) {
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 38015b493947..5c72a116b1a4 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -182,17 +182,32 @@ override:
R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE]);
if (R_tab == NULL)
goto failure;
+
+ if (!est && (ret == ACT_P_CREATED ||
+ !gen_estimator_active(&police->tcf_bstats,
+ &police->tcf_rate_est))) {
+ err = -EINVAL;
+ goto failure;
+ }
+
if (parm->peakrate.rate) {
P_tab = qdisc_get_rtab(&parm->peakrate,
tb[TCA_POLICE_PEAKRATE]);
- if (P_tab == NULL) {
- qdisc_put_rtab(R_tab);
+ if (P_tab == NULL)
goto failure;
- }
}
}
- /* No failure allowed after this point */
+
spin_lock_bh(&police->tcf_lock);
+ if (est) {
+ err = gen_replace_estimator(&police->tcf_bstats,
+ &police->tcf_rate_est,
+ &police->tcf_lock, est);
+ if (err)
+ goto failure_unlock;
+ }
+
+ /* No failure allowed after this point */
if (R_tab != NULL) {
qdisc_put_rtab(police->tcfp_R_tab);
police->tcfp_R_tab = R_tab;
@@ -217,10 +232,6 @@ override:
if (tb[TCA_POLICE_AVRATE])
police->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]);
- if (est)
- gen_replace_estimator(&police->tcf_bstats,
- &police->tcf_rate_est,
- &police->tcf_lock, est);
spin_unlock_bh(&police->tcf_lock);
if (ret != ACT_P_CREATED)
@@ -238,7 +249,13 @@ override:
a->priv = police;
return ret;
+failure_unlock:
+ spin_unlock_bh(&police->tcf_lock);
failure:
+ if (P_tab)
+ qdisc_put_rtab(P_tab);
+ if (R_tab)
+ qdisc_put_rtab(R_tab);
if (ret == ACT_P_CREATED)
kfree(police);
return err;
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index e7851ce92cfe..8daa1ebc7413 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -124,8 +124,8 @@ static int tcf_simp_init(struct nlattr *nla, struct nlattr *est,
if (!pc) {
pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind,
&simp_idx_gen, &simp_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
d = to_defact(pc);
ret = alloc_defdata(d, defdata);
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index fe9777e77f35..4ab916b8074b 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -104,8 +104,8 @@ static int tcf_skbedit_init(struct nlattr *nla, struct nlattr *est,
if (!pc) {
pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind,
&skbedit_idx_gen, &skbedit_hash_info);
- if (unlikely(!pc))
- return -ENOMEM;
+ if (IS_ERR(pc))
+ return PTR_ERR(pc);
d = to_skbedit(pc);
ret = ACT_P_CREATED;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 16e7ac9774e5..173fcc4b050d 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -531,7 +531,8 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
if (src->action) {
struct tc_action *act;
tcf_tree_lock(tp);
- act = xchg(&dst->action, src->action);
+ act = dst->action;
+ dst->action = src->action;
tcf_tree_unlock(tp);
if (act)
tcf_action_destroy(act, TCA_ACT_UNBIND);
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index 956915c217d6..4e2bda854119 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -102,7 +102,7 @@ static inline void basic_delete_filter(struct tcf_proto *tp,
static void basic_destroy(struct tcf_proto *tp)
{
- struct basic_head *head = (struct basic_head *) xchg(&tp->root, NULL);
+ struct basic_head *head = tp->root;
struct basic_filter *f, *n;
list_for_each_entry_safe(f, n, &head->flist, link) {
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
new file mode 100644
index 000000000000..91a3db4a76f8
--- /dev/null
+++ b/net/sched/cls_cgroup.c
@@ -0,0 +1,293 @@
+/*
+ * net/sched/cls_cgroup.c Control Group Classifier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/cgroup.h>
+#include <net/rtnetlink.h>
+#include <net/pkt_cls.h>
+
+struct cgroup_cls_state
+{
+ struct cgroup_subsys_state css;
+ u32 classid;
+};
+
+static inline struct cgroup_cls_state *cgrp_cls_state(struct cgroup *cgrp)
+{
+ return container_of(cgroup_subsys_state(cgrp, net_cls_subsys_id),
+ struct cgroup_cls_state, css);
+}
+
+static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p)
+{
+ return container_of(task_subsys_state(p, net_cls_subsys_id),
+ struct cgroup_cls_state, css);
+}
+
+static struct cgroup_subsys_state *cgrp_create(struct cgroup_subsys *ss,
+ struct cgroup *cgrp)
+{
+ struct cgroup_cls_state *cs;
+
+ if (!(cs = kzalloc(sizeof(*cs), GFP_KERNEL)))
+ return ERR_PTR(-ENOMEM);
+
+ if (cgrp->parent)
+ cs->classid = cgrp_cls_state(cgrp->parent)->classid;
+
+ return &cs->css;
+}
+
+static void cgrp_destroy(struct cgroup_subsys *ss, struct cgroup *cgrp)
+{
+ kfree(cgrp_cls_state(cgrp));
+}
+
+static u64 read_classid(struct cgroup *cgrp, struct cftype *cft)
+{
+ return cgrp_cls_state(cgrp)->classid;
+}
+
+static int write_classid(struct cgroup *cgrp, struct cftype *cft, u64 value)
+{
+ if (!cgroup_lock_live_group(cgrp))
+ return -ENODEV;
+
+ cgrp_cls_state(cgrp)->classid = (u32) value;
+
+ cgroup_unlock();
+
+ return 0;
+}
+
+static struct cftype ss_files[] = {
+ {
+ .name = "classid",
+ .read_u64 = read_classid,
+ .write_u64 = write_classid,
+ },
+};
+
+static int cgrp_populate(struct cgroup_subsys *ss, struct cgroup *cgrp)
+{
+ return cgroup_add_files(cgrp, ss, ss_files, ARRAY_SIZE(ss_files));
+}
+
+struct cgroup_subsys net_cls_subsys = {
+ .name = "net_cls",
+ .create = cgrp_create,
+ .destroy = cgrp_destroy,
+ .populate = cgrp_populate,
+ .subsys_id = net_cls_subsys_id,
+};
+
+struct cls_cgroup_head
+{
+ u32 handle;
+ struct tcf_exts exts;
+ struct tcf_ematch_tree ematches;
+};
+
+static int cls_cgroup_classify(struct sk_buff *skb, struct tcf_proto *tp,
+ struct tcf_result *res)
+{
+ struct cls_cgroup_head *head = tp->root;
+ struct cgroup_cls_state *cs;
+ int ret = 0;
+
+ /*
+ * Due to the nature of the classifier it is required to ignore all
+ * packets originating from softirq context as accessing `current'
+ * would lead to false results.
+ *
+ * This test assumes that all callers of dev_queue_xmit() explicitely
+ * disable bh. Knowing this, it is possible to detect softirq based
+ * calls by looking at the number of nested bh disable calls because
+ * softirqs always disables bh.
+ */
+ if (softirq_count() != SOFTIRQ_OFFSET)
+ return -1;
+
+ rcu_read_lock();
+ cs = task_cls_state(current);
+ if (cs->classid && tcf_em_tree_match(skb, &head->ematches, NULL)) {
+ res->classid = cs->classid;
+ res->class = 0;
+ ret = tcf_exts_exec(skb, &head->exts, res);
+ } else
+ ret = -1;
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+static unsigned long cls_cgroup_get(struct tcf_proto *tp, u32 handle)
+{
+ return 0UL;
+}
+
+static void cls_cgroup_put(struct tcf_proto *tp, unsigned long f)
+{
+}
+
+static int cls_cgroup_init(struct tcf_proto *tp)
+{
+ return 0;
+}
+
+static const struct tcf_ext_map cgroup_ext_map = {
+ .action = TCA_CGROUP_ACT,
+ .police = TCA_CGROUP_POLICE,
+};
+
+static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
+ [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED },
+};
+
+static int cls_cgroup_change(struct tcf_proto *tp, unsigned long base,
+ u32 handle, struct nlattr **tca,
+ unsigned long *arg)
+{
+ struct nlattr *tb[TCA_CGROUP_MAX+1];
+ struct cls_cgroup_head *head = tp->root;
+ struct tcf_ematch_tree t;
+ struct tcf_exts e;
+ int err;
+
+ if (head == NULL) {
+ if (!handle)
+ return -EINVAL;
+
+ head = kzalloc(sizeof(*head), GFP_KERNEL);
+ if (head == NULL)
+ return -ENOBUFS;
+
+ head->handle = handle;
+
+ tcf_tree_lock(tp);
+ tp->root = head;
+ tcf_tree_unlock(tp);
+ }
+
+ if (handle != head->handle)
+ return -ENOENT;
+
+ err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS],
+ cgroup_policy);
+ if (err < 0)
+ return err;
+
+ err = tcf_exts_validate(tp, tb, tca[TCA_RATE], &e, &cgroup_ext_map);
+ if (err < 0)
+ return err;
+
+ err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t);
+ if (err < 0)
+ return err;
+
+ tcf_exts_change(tp, &head->exts, &e);
+ tcf_em_tree_change(tp, &head->ematches, &t);
+
+ return 0;
+}
+
+static void cls_cgroup_destroy(struct tcf_proto *tp)
+{
+ struct cls_cgroup_head *head = tp->root;
+
+ if (head) {
+ tcf_exts_destroy(tp, &head->exts);
+ tcf_em_tree_destroy(tp, &head->ematches);
+ kfree(head);
+ }
+}
+
+static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg)
+{
+ return -EOPNOTSUPP;
+}
+
+static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+ struct cls_cgroup_head *head = tp->root;
+
+ if (arg->count < arg->skip)
+ goto skip;
+
+ if (arg->fn(tp, (unsigned long) head, arg) < 0) {
+ arg->stop = 1;
+ return;
+ }
+skip:
+ arg->count++;
+}
+
+static int cls_cgroup_dump(struct tcf_proto *tp, unsigned long fh,
+ struct sk_buff *skb, struct tcmsg *t)
+{
+ struct cls_cgroup_head *head = tp->root;
+ unsigned char *b = skb_tail_pointer(skb);
+ struct nlattr *nest;
+
+ t->tcm_handle = head->handle;
+
+ nest = nla_nest_start(skb, TCA_OPTIONS);
+ if (nest == NULL)
+ goto nla_put_failure;
+
+ if (tcf_exts_dump(skb, &head->exts, &cgroup_ext_map) < 0 ||
+ tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(skb, nest);
+
+ if (tcf_exts_dump_stats(skb, &head->exts, &cgroup_ext_map) < 0)
+ goto nla_put_failure;
+
+ return skb->len;
+
+nla_put_failure:
+ nlmsg_trim(skb, b);
+ return -1;
+}
+
+static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
+ .kind = "cgroup",
+ .init = cls_cgroup_init,
+ .change = cls_cgroup_change,
+ .classify = cls_cgroup_classify,
+ .destroy = cls_cgroup_destroy,
+ .get = cls_cgroup_get,
+ .put = cls_cgroup_put,
+ .delete = cls_cgroup_delete,
+ .walk = cls_cgroup_walk,
+ .dump = cls_cgroup_dump,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_cgroup_cls(void)
+{
+ return register_tcf_proto_ops(&cls_cgroup_ops);
+}
+
+static void __exit exit_cgroup_cls(void)
+{
+ unregister_tcf_proto_ops(&cls_cgroup_ops);
+}
+
+module_init(init_cgroup_cls);
+module_exit(exit_cgroup_cls);
+MODULE_LICENSE("GPL");
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 0ebaff637e31..0ef4e3065bcd 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -260,14 +260,14 @@ static u32 flow_get_rtclassid(const struct sk_buff *skb)
static u32 flow_get_skuid(const struct sk_buff *skb)
{
if (skb->sk && skb->sk->sk_socket && skb->sk->sk_socket->file)
- return skb->sk->sk_socket->file->f_uid;
+ return skb->sk->sk_socket->file->f_cred->fsuid;
return 0;
}
static u32 flow_get_skgid(const struct sk_buff *skb)
{
if (skb->sk && skb->sk->sk_socket && skb->sk->sk_socket->file)
- return skb->sk->sk_socket->file->f_gid;
+ return skb->sk->sk_socket->file->f_cred->fsgid;
return 0;
}
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index b0f90e593af0..6d6e87585fb1 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -148,7 +148,7 @@ fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f)
static void fw_destroy(struct tcf_proto *tp)
{
- struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
+ struct fw_head *head = tp->root;
struct fw_filter *f;
int h;
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index e3d8455eebc2..bdf1f4172eef 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -260,7 +260,7 @@ route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f)
static void route4_destroy(struct tcf_proto *tp)
{
- struct route4_head *head = xchg(&tp->root, NULL);
+ struct route4_head *head = tp->root;
int h1, h2;
if (head == NULL)
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 7a7bff5ded24..e806f2314b5e 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -13,12 +13,6 @@
#include <net/netlink.h>
#include <net/pkt_cls.h>
-
-/*
- * Not quite sure if we need all the xchgs Alexey uses when accessing things.
- * Can always add them later ... :)
- */
-
/*
* Passing parameters to the root seems to be done more awkwardly than really
* necessary. At least, u32 doesn't seem to use such dirty hacks. To be
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 246f9065ce34..05d178008cbc 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -387,7 +387,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
static void u32_destroy(struct tcf_proto *tp)
{
struct tc_u_common *tp_c = tp->data;
- struct tc_u_hnode *root_ht = xchg(&tp->root, NULL);
+ struct tc_u_hnode *root_ht = tp->root;
WARN_ON(root_ht == NULL);
@@ -479,7 +479,7 @@ static int u32_set_parms(struct tcf_proto *tp, unsigned long base,
err = -EINVAL;
if (tb[TCA_U32_LINK]) {
u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
- struct tc_u_hnode *ht_down = NULL;
+ struct tc_u_hnode *ht_down = NULL, *ht_old;
if (TC_U32_KEY(handle))
goto errout;
@@ -493,11 +493,12 @@ static int u32_set_parms(struct tcf_proto *tp, unsigned long base,
}
tcf_tree_lock(tp);
- ht_down = xchg(&n->ht_down, ht_down);
+ ht_old = n->ht_down;
+ n->ht_down = ht_down;
tcf_tree_unlock(tp);
- if (ht_down)
- ht_down->refcnt--;
+ if (ht_old)
+ ht_old->refcnt--;
}
if (tb[TCA_U32_CLASSID]) {
n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
diff --git a/net/sched/ematch.c b/net/sched/ematch.c
index e82519e548d7..aab59409728b 100644
--- a/net/sched/ematch.c
+++ b/net/sched/ematch.c
@@ -71,7 +71,7 @@
*
* static void __exit exit_my_ematch(void)
* {
- * return tcf_em_unregister(&my_ops);
+ * tcf_em_unregister(&my_ops);
* }
*
* module_init(init_my_ematch);
@@ -154,23 +154,11 @@ EXPORT_SYMBOL(tcf_em_register);
*
* Returns -ENOENT if no matching ematch was found.
*/
-int tcf_em_unregister(struct tcf_ematch_ops *ops)
+void tcf_em_unregister(struct tcf_ematch_ops *ops)
{
- int err = 0;
- struct tcf_ematch_ops *e;
-
write_lock(&ematch_mod_lock);
- list_for_each_entry(e, &ematch_ops, link) {
- if (e == ops) {
- list_del(&e->link);
- goto out;
- }
- }
-
- err = -ENOENT;
-out:
+ list_del(&ops->link);
write_unlock(&ematch_mod_lock);
- return err;
}
EXPORT_SYMBOL(tcf_em_unregister);
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 6ab4a2f92ca0..0fc4a18fd96f 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -97,10 +97,9 @@ static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n,
Auxiliary routines:
- ---requeue
+ ---peek
- requeues once dequeued packet. It is used for non-standard or
- just buggy devices, which can defer output even if netif_queue_stopped()=0.
+ like dequeue but without removing a packet from the queue
---reset
@@ -147,8 +146,14 @@ int register_qdisc(struct Qdisc_ops *qops)
if (qops->enqueue == NULL)
qops->enqueue = noop_qdisc_ops.enqueue;
- if (qops->requeue == NULL)
- qops->requeue = noop_qdisc_ops.requeue;
+ if (qops->peek == NULL) {
+ if (qops->dequeue == NULL) {
+ qops->peek = noop_qdisc_ops.peek;
+ } else {
+ rc = -EINVAL;
+ goto out;
+ }
+ }
if (qops->dequeue == NULL)
qops->dequeue = noop_qdisc_ops.dequeue;
@@ -184,7 +189,7 @@ EXPORT_SYMBOL(unregister_qdisc);
(root qdisc, all its children, children of children etc.)
*/
-struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
+static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
{
struct Qdisc *q;
@@ -199,28 +204,16 @@ struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
return NULL;
}
-/*
- * This lock is needed until some qdiscs stop calling qdisc_tree_decrease_qlen()
- * without rtnl_lock(); currently hfsc_dequeue(), netem_dequeue(), tbf_dequeue()
- */
-static DEFINE_SPINLOCK(qdisc_list_lock);
-
static void qdisc_list_add(struct Qdisc *q)
{
- if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
- spin_lock_bh(&qdisc_list_lock);
+ if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
list_add_tail(&q->list, &qdisc_root_sleeping(q)->list);
- spin_unlock_bh(&qdisc_list_lock);
- }
}
void qdisc_list_del(struct Qdisc *q)
{
- if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
- spin_lock_bh(&qdisc_list_lock);
+ if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
list_del(&q->list);
- spin_unlock_bh(&qdisc_list_lock);
- }
}
EXPORT_SYMBOL(qdisc_list_del);
@@ -229,22 +222,17 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle)
unsigned int i;
struct Qdisc *q;
- spin_lock_bh(&qdisc_list_lock);
-
for (i = 0; i < dev->num_tx_queues; i++) {
struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
struct Qdisc *txq_root = txq->qdisc_sleeping;
q = qdisc_match_from_root(txq_root, handle);
if (q)
- goto unlock;
+ goto out;
}
q = qdisc_match_from_root(dev->rx_queue.qdisc_sleeping, handle);
-
-unlock:
- spin_unlock_bh(&qdisc_list_lock);
-
+out:
return q;
}
@@ -462,7 +450,6 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer)
timer);
wd->qdisc->flags &= ~TCQ_F_THROTTLED;
- smp_wmb();
__netif_schedule(qdisc_root(wd->qdisc));
return HRTIMER_NORESTART;
@@ -892,9 +879,12 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
sch->stab = stab;
if (tca[TCA_RATE])
+ /* NB: ignores errors from replace_estimator
+ because change can't be undone. */
gen_replace_estimator(&sch->bstats, &sch->rate_est,
- qdisc_root_sleeping_lock(sch),
- tca[TCA_RATE]);
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+
return 0;
}
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 43d37256c15e..2a8b83af7c47 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -62,7 +62,7 @@ struct atm_qdisc_data {
struct atm_flow_data link; /* unclassified skbs go here */
struct atm_flow_data *flows; /* NB: "link" is also on this
list */
- struct tasklet_struct task; /* requeue tasklet */
+ struct tasklet_struct task; /* dequeue tasklet */
};
/* ------------------------- Class/flow operations ------------------------- */
@@ -102,7 +102,8 @@ static int atm_tc_graft(struct Qdisc *sch, unsigned long arg,
return -EINVAL;
if (!new)
new = &noop_qdisc;
- *old = xchg(&flow->q, new);
+ *old = flow->q;
+ flow->q = new;
if (*old)
qdisc_reset(*old);
return 0;
@@ -480,11 +481,14 @@ static void sch_atm_dequeue(unsigned long data)
* If traffic is properly shaped, this won't generate nasty
* little bursts. Otherwise, it may ... (but that's okay)
*/
- while ((skb = flow->q->dequeue(flow->q))) {
- if (!atm_may_send(flow->vcc, skb->truesize)) {
- (void)flow->q->ops->requeue(skb, flow->q);
+ while ((skb = flow->q->ops->peek(flow->q))) {
+ if (!atm_may_send(flow->vcc, skb->truesize))
break;
- }
+
+ skb = qdisc_dequeue_peeked(flow->q);
+ if (unlikely(!skb))
+ break;
+
pr_debug("atm_tc_dequeue: sending on class %p\n", flow);
/* remove any LL header somebody else has attached */
skb_pull(skb, skb_network_offset(skb));
@@ -516,27 +520,19 @@ static struct sk_buff *atm_tc_dequeue(struct Qdisc *sch)
pr_debug("atm_tc_dequeue(sch %p,[qdisc %p])\n", sch, p);
tasklet_schedule(&p->task);
- skb = p->link.q->dequeue(p->link.q);
+ skb = qdisc_dequeue_peeked(p->link.q);
if (skb)
sch->q.qlen--;
return skb;
}
-static int atm_tc_requeue(struct sk_buff *skb, struct Qdisc *sch)
+static struct sk_buff *atm_tc_peek(struct Qdisc *sch)
{
struct atm_qdisc_data *p = qdisc_priv(sch);
- int ret;
- pr_debug("atm_tc_requeue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
- ret = p->link.q->ops->requeue(skb, p->link.q);
- if (!ret) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- } else if (net_xmit_drop_count(ret)) {
- sch->qstats.drops++;
- p->link.qstats.drops++;
- }
- return ret;
+ pr_debug("atm_tc_peek(sch %p,[qdisc %p])\n", sch, p);
+
+ return p->link.q->ops->peek(p->link.q);
}
static unsigned int atm_tc_drop(struct Qdisc *sch)
@@ -694,7 +690,7 @@ static struct Qdisc_ops atm_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct atm_qdisc_data),
.enqueue = atm_tc_enqueue,
.dequeue = atm_tc_dequeue,
- .requeue = atm_tc_requeue,
+ .peek = atm_tc_peek,
.drop = atm_tc_drop,
.init = atm_tc_init,
.reset = atm_tc_reset,
diff --git a/net/sched/sch_blackhole.c b/net/sched/sch_blackhole.c
index 507fb488bc98..094a874b48bc 100644
--- a/net/sched/sch_blackhole.c
+++ b/net/sched/sch_blackhole.c
@@ -33,6 +33,7 @@ static struct Qdisc_ops blackhole_qdisc_ops __read_mostly = {
.priv_size = 0,
.enqueue = blackhole_enqueue,
.dequeue = blackhole_dequeue,
+ .peek = blackhole_dequeue,
.owner = THIS_MODULE,
};
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 03e389e8d945..9e43ed949167 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -405,40 +405,6 @@ cbq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return ret;
}
-static int
-cbq_requeue(struct sk_buff *skb, struct Qdisc *sch)
-{
- struct cbq_sched_data *q = qdisc_priv(sch);
- struct cbq_class *cl;
- int ret;
-
- if ((cl = q->tx_class) == NULL) {
- kfree_skb(skb);
- sch->qstats.drops++;
- return NET_XMIT_CN;
- }
- q->tx_class = NULL;
-
- cbq_mark_toplevel(q, cl);
-
-#ifdef CONFIG_NET_CLS_ACT
- q->rx_class = cl;
- cl->q->__parent = sch;
-#endif
- if ((ret = cl->q->ops->requeue(skb, cl->q)) == 0) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- if (!cl->next_alive)
- cbq_activate_class(cl);
- return 0;
- }
- if (net_xmit_drop_count(ret)) {
- sch->qstats.drops++;
- cl->qstats.drops++;
- }
- return ret;
-}
-
/* Overlimit actions */
/* TC_CBQ_OVL_CLASSIC: (default) penalize leaf class by adding offtime */
@@ -1669,7 +1635,8 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
#endif
}
sch_tree_lock(sch);
- *old = xchg(&cl->q, new);
+ *old = cl->q;
+ cl->q = new;
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
qdisc_reset(*old);
sch_tree_unlock(sch);
@@ -1798,11 +1765,23 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
}
if (tb[TCA_CBQ_RATE]) {
- rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]), tb[TCA_CBQ_RTAB]);
+ rtab = qdisc_get_rtab(nla_data(tb[TCA_CBQ_RATE]),
+ tb[TCA_CBQ_RTAB]);
if (rtab == NULL)
return -EINVAL;
}
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err) {
+ if (rtab)
+ qdisc_put_rtab(rtab);
+ return err;
+ }
+ }
+
/* Change class parameters */
sch_tree_lock(sch);
@@ -1810,8 +1789,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
cbq_deactivate_class(cl);
if (rtab) {
- rtab = xchg(&cl->R_tab, rtab);
- qdisc_put_rtab(rtab);
+ qdisc_put_rtab(cl->R_tab);
+ cl->R_tab = rtab;
}
if (tb[TCA_CBQ_LSSOPT])
@@ -1838,10 +1817,6 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
sch_tree_unlock(sch);
- if (tca[TCA_RATE])
- gen_replace_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch),
- tca[TCA_RATE]);
return 0;
}
@@ -1888,6 +1863,17 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
cl = kzalloc(sizeof(*cl), GFP_KERNEL);
if (cl == NULL)
goto failure;
+
+ if (tca[TCA_RATE]) {
+ err = gen_new_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err) {
+ kfree(cl);
+ goto failure;
+ }
+ }
+
cl->R_tab = rtab;
rtab = NULL;
cl->refcnt = 1;
@@ -1929,10 +1915,6 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
qdisc_class_hash_grow(sch, &q->clhash);
- if (tca[TCA_RATE])
- gen_new_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch), tca[TCA_RATE]);
-
*arg = (unsigned long)cl;
return 0;
@@ -2066,7 +2048,7 @@ static struct Qdisc_ops cbq_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct cbq_sched_data),
.enqueue = cbq_enqueue,
.dequeue = cbq_dequeue,
- .requeue = cbq_requeue,
+ .peek = qdisc_peek_dequeued,
.drop = cbq_drop,
.init = cbq_init,
.reset = cbq_reset,
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
new file mode 100644
index 000000000000..f6b4fa97df70
--- /dev/null
+++ b/net/sched/sch_drr.c
@@ -0,0 +1,519 @@
+/*
+ * net/sched/sch_drr.c Deficit Round Robin scheduler
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/pkt_sched.h>
+#include <net/sch_generic.h>
+#include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
+
+struct drr_class {
+ struct Qdisc_class_common common;
+ unsigned int refcnt;
+ unsigned int filter_cnt;
+
+ struct gnet_stats_basic bstats;
+ struct gnet_stats_queue qstats;
+ struct gnet_stats_rate_est rate_est;
+ struct list_head alist;
+ struct Qdisc *qdisc;
+
+ u32 quantum;
+ u32 deficit;
+};
+
+struct drr_sched {
+ struct list_head active;
+ struct tcf_proto *filter_list;
+ struct Qdisc_class_hash clhash;
+};
+
+static struct drr_class *drr_find_class(struct Qdisc *sch, u32 classid)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct Qdisc_class_common *clc;
+
+ clc = qdisc_class_find(&q->clhash, classid);
+ if (clc == NULL)
+ return NULL;
+ return container_of(clc, struct drr_class, common);
+}
+
+static void drr_purge_queue(struct drr_class *cl)
+{
+ unsigned int len = cl->qdisc->q.qlen;
+
+ qdisc_reset(cl->qdisc);
+ qdisc_tree_decrease_qlen(cl->qdisc, len);
+}
+
+static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
+ [TCA_DRR_QUANTUM] = { .type = NLA_U32 },
+};
+
+static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
+ struct nlattr **tca, unsigned long *arg)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl = (struct drr_class *)*arg;
+ struct nlattr *tb[TCA_DRR_MAX + 1];
+ u32 quantum;
+ int err;
+
+ err = nla_parse_nested(tb, TCA_DRR_MAX, tca[TCA_OPTIONS], drr_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_DRR_QUANTUM]) {
+ quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
+ if (quantum == 0)
+ return -EINVAL;
+ } else
+ quantum = psched_mtu(qdisc_dev(sch));
+
+ if (cl != NULL) {
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err)
+ return err;
+ }
+
+ sch_tree_lock(sch);
+ if (tb[TCA_DRR_QUANTUM])
+ cl->quantum = quantum;
+ sch_tree_unlock(sch);
+
+ return 0;
+ }
+
+ cl = kzalloc(sizeof(struct drr_class), GFP_KERNEL);
+ if (cl == NULL)
+ return -ENOBUFS;
+
+ cl->refcnt = 1;
+ cl->common.classid = classid;
+ cl->quantum = quantum;
+ cl->qdisc = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
+ &pfifo_qdisc_ops, classid);
+ if (cl->qdisc == NULL)
+ cl->qdisc = &noop_qdisc;
+
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err) {
+ qdisc_destroy(cl->qdisc);
+ kfree(cl);
+ return err;
+ }
+ }
+
+ sch_tree_lock(sch);
+ qdisc_class_hash_insert(&q->clhash, &cl->common);
+ sch_tree_unlock(sch);
+
+ qdisc_class_hash_grow(sch, &q->clhash);
+
+ *arg = (unsigned long)cl;
+ return 0;
+}
+
+static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
+{
+ gen_kill_estimator(&cl->bstats, &cl->rate_est);
+ qdisc_destroy(cl->qdisc);
+ kfree(cl);
+}
+
+static int drr_delete_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ if (cl->filter_cnt > 0)
+ return -EBUSY;
+
+ sch_tree_lock(sch);
+
+ drr_purge_queue(cl);
+ qdisc_class_hash_remove(&q->clhash, &cl->common);
+
+ if (--cl->refcnt == 0)
+ drr_destroy_class(sch, cl);
+
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static unsigned long drr_get_class(struct Qdisc *sch, u32 classid)
+{
+ struct drr_class *cl = drr_find_class(sch, classid);
+
+ if (cl != NULL)
+ cl->refcnt++;
+
+ return (unsigned long)cl;
+}
+
+static void drr_put_class(struct Qdisc *sch, unsigned long arg)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ if (--cl->refcnt == 0)
+ drr_destroy_class(sch, cl);
+}
+
+static struct tcf_proto **drr_tcf_chain(struct Qdisc *sch, unsigned long cl)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+
+ if (cl)
+ return NULL;
+
+ return &q->filter_list;
+}
+
+static unsigned long drr_bind_tcf(struct Qdisc *sch, unsigned long parent,
+ u32 classid)
+{
+ struct drr_class *cl = drr_find_class(sch, classid);
+
+ if (cl != NULL)
+ cl->filter_cnt++;
+
+ return (unsigned long)cl;
+}
+
+static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ cl->filter_cnt--;
+}
+
+static int drr_graft_class(struct Qdisc *sch, unsigned long arg,
+ struct Qdisc *new, struct Qdisc **old)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ if (new == NULL) {
+ new = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
+ &pfifo_qdisc_ops, cl->common.classid);
+ if (new == NULL)
+ new = &noop_qdisc;
+ }
+
+ sch_tree_lock(sch);
+ drr_purge_queue(cl);
+ *old = cl->qdisc;
+ cl->qdisc = new;
+ sch_tree_unlock(sch);
+ return 0;
+}
+
+static struct Qdisc *drr_class_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ return cl->qdisc;
+}
+
+static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+
+ if (cl->qdisc->q.qlen == 0)
+ list_del(&cl->alist);
+}
+
+static int drr_dump_class(struct Qdisc *sch, unsigned long arg,
+ struct sk_buff *skb, struct tcmsg *tcm)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+ struct nlattr *nest;
+
+ tcm->tcm_parent = TC_H_ROOT;
+ tcm->tcm_handle = cl->common.classid;
+ tcm->tcm_info = cl->qdisc->handle;
+
+ nest = nla_nest_start(skb, TCA_OPTIONS);
+ if (nest == NULL)
+ goto nla_put_failure;
+ NLA_PUT_U32(skb, TCA_DRR_QUANTUM, cl->quantum);
+ return nla_nest_end(skb, nest);
+
+nla_put_failure:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
+ struct gnet_dump *d)
+{
+ struct drr_class *cl = (struct drr_class *)arg;
+ struct tc_drr_stats xstats;
+
+ memset(&xstats, 0, sizeof(xstats));
+ if (cl->qdisc->q.qlen)
+ xstats.deficit = cl->deficit;
+
+ if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
+ gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
+ gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
+ return -1;
+
+ return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
+}
+
+static void drr_walk(struct Qdisc *sch, struct qdisc_walker *arg)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ struct hlist_node *n;
+ unsigned int i;
+
+ if (arg->stop)
+ return;
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) {
+ if (arg->count < arg->skip) {
+ arg->count++;
+ continue;
+ }
+ if (arg->fn(sch, (unsigned long)cl, arg) < 0) {
+ arg->stop = 1;
+ return;
+ }
+ arg->count++;
+ }
+ }
+}
+
+static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
+ int *qerr)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ struct tcf_result res;
+ int result;
+
+ if (TC_H_MAJ(skb->priority ^ sch->handle) == 0) {
+ cl = drr_find_class(sch, skb->priority);
+ if (cl != NULL)
+ return cl;
+ }
+
+ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
+ result = tc_classify(skb, q->filter_list, &res);
+ if (result >= 0) {
+#ifdef CONFIG_NET_CLS_ACT
+ switch (result) {
+ case TC_ACT_QUEUED:
+ case TC_ACT_STOLEN:
+ *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
+ case TC_ACT_SHOT:
+ return NULL;
+ }
+#endif
+ cl = (struct drr_class *)res.class;
+ if (cl == NULL)
+ cl = drr_find_class(sch, res.classid);
+ return cl;
+ }
+ return NULL;
+}
+
+static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ unsigned int len;
+ int err;
+
+ cl = drr_classify(skb, sch, &err);
+ if (cl == NULL) {
+ if (err & __NET_XMIT_BYPASS)
+ sch->qstats.drops++;
+ kfree_skb(skb);
+ return err;
+ }
+
+ len = qdisc_pkt_len(skb);
+ err = qdisc_enqueue(skb, cl->qdisc);
+ if (unlikely(err != NET_XMIT_SUCCESS)) {
+ if (net_xmit_drop_count(err)) {
+ cl->qstats.drops++;
+ sch->qstats.drops++;
+ }
+ return err;
+ }
+
+ if (cl->qdisc->q.qlen == 1) {
+ list_add_tail(&cl->alist, &q->active);
+ cl->deficit = cl->quantum;
+ }
+
+ cl->bstats.packets++;
+ cl->bstats.bytes += len;
+ sch->bstats.packets++;
+ sch->bstats.bytes += len;
+
+ sch->q.qlen++;
+ return err;
+}
+
+static struct sk_buff *drr_dequeue(struct Qdisc *sch)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ struct sk_buff *skb;
+ unsigned int len;
+
+ if (list_empty(&q->active))
+ goto out;
+ while (1) {
+ cl = list_first_entry(&q->active, struct drr_class, alist);
+ skb = cl->qdisc->ops->peek(cl->qdisc);
+ if (skb == NULL)
+ goto out;
+
+ len = qdisc_pkt_len(skb);
+ if (len <= cl->deficit) {
+ cl->deficit -= len;
+ skb = qdisc_dequeue_peeked(cl->qdisc);
+ if (cl->qdisc->q.qlen == 0)
+ list_del(&cl->alist);
+ sch->q.qlen--;
+ return skb;
+ }
+
+ cl->deficit += cl->quantum;
+ list_move_tail(&cl->alist, &q->active);
+ }
+out:
+ return NULL;
+}
+
+static unsigned int drr_drop(struct Qdisc *sch)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ unsigned int len;
+
+ list_for_each_entry(cl, &q->active, alist) {
+ if (cl->qdisc->ops->drop) {
+ len = cl->qdisc->ops->drop(cl->qdisc);
+ if (len > 0) {
+ sch->q.qlen--;
+ if (cl->qdisc->q.qlen == 0)
+ list_del(&cl->alist);
+ return len;
+ }
+ }
+ }
+ return 0;
+}
+
+static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ int err;
+
+ err = qdisc_class_hash_init(&q->clhash);
+ if (err < 0)
+ return err;
+ INIT_LIST_HEAD(&q->active);
+ return 0;
+}
+
+static void drr_reset_qdisc(struct Qdisc *sch)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ struct hlist_node *n;
+ unsigned int i;
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) {
+ if (cl->qdisc->q.qlen)
+ list_del(&cl->alist);
+ qdisc_reset(cl->qdisc);
+ }
+ }
+ sch->q.qlen = 0;
+}
+
+static void drr_destroy_qdisc(struct Qdisc *sch)
+{
+ struct drr_sched *q = qdisc_priv(sch);
+ struct drr_class *cl;
+ struct hlist_node *n, *next;
+ unsigned int i;
+
+ tcf_destroy_chain(&q->filter_list);
+
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry_safe(cl, n, next, &q->clhash.hash[i],
+ common.hnode)
+ drr_destroy_class(sch, cl);
+ }
+ qdisc_class_hash_destroy(&q->clhash);
+}
+
+static const struct Qdisc_class_ops drr_class_ops = {
+ .change = drr_change_class,
+ .delete = drr_delete_class,
+ .get = drr_get_class,
+ .put = drr_put_class,
+ .tcf_chain = drr_tcf_chain,
+ .bind_tcf = drr_bind_tcf,
+ .unbind_tcf = drr_unbind_tcf,
+ .graft = drr_graft_class,
+ .leaf = drr_class_leaf,
+ .qlen_notify = drr_qlen_notify,
+ .dump = drr_dump_class,
+ .dump_stats = drr_dump_class_stats,
+ .walk = drr_walk,
+};
+
+static struct Qdisc_ops drr_qdisc_ops __read_mostly = {
+ .cl_ops = &drr_class_ops,
+ .id = "drr",
+ .priv_size = sizeof(struct drr_sched),
+ .enqueue = drr_enqueue,
+ .dequeue = drr_dequeue,
+ .peek = qdisc_peek_dequeued,
+ .drop = drr_drop,
+ .init = drr_init_qdisc,
+ .reset = drr_reset_qdisc,
+ .destroy = drr_destroy_qdisc,
+ .owner = THIS_MODULE,
+};
+
+static int __init drr_init(void)
+{
+ return register_qdisc(&drr_qdisc_ops);
+}
+
+static void __exit drr_exit(void)
+{
+ unregister_qdisc(&drr_qdisc_ops);
+}
+
+module_init(drr_init);
+module_exit(drr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
index ba43aab3a851..d303daa45d49 100644
--- a/net/sched/sch_dsmark.c
+++ b/net/sched/sch_dsmark.c
@@ -68,7 +68,8 @@ static int dsmark_graft(struct Qdisc *sch, unsigned long arg,
}
sch_tree_lock(sch);
- *old = xchg(&p->q, new);
+ *old = p->q;
+ p->q = new;
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
qdisc_reset(*old);
sch_tree_unlock(sch);
@@ -313,24 +314,13 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
return skb;
}
-static int dsmark_requeue(struct sk_buff *skb, struct Qdisc *sch)
+static struct sk_buff *dsmark_peek(struct Qdisc *sch)
{
struct dsmark_qdisc_data *p = qdisc_priv(sch);
- int err;
-
- pr_debug("dsmark_requeue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
-
- err = p->q->ops->requeue(skb, p->q);
- if (err != NET_XMIT_SUCCESS) {
- if (net_xmit_drop_count(err))
- sch->qstats.drops++;
- return err;
- }
- sch->q.qlen++;
- sch->qstats.requeues++;
+ pr_debug("dsmark_peek(sch %p,[qdisc %p])\n", sch, p);
- return NET_XMIT_SUCCESS;
+ return p->q->ops->peek(p->q);
}
static unsigned int dsmark_drop(struct Qdisc *sch)
@@ -496,7 +486,7 @@ static struct Qdisc_ops dsmark_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct dsmark_qdisc_data),
.enqueue = dsmark_enqueue,
.dequeue = dsmark_dequeue,
- .requeue = dsmark_requeue,
+ .peek = dsmark_peek,
.drop = dsmark_drop,
.init = dsmark_init,
.reset = dsmark_reset,
diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c
index 23d258bfe8ac..92cfc9d7e3b9 100644
--- a/net/sched/sch_fifo.c
+++ b/net/sched/sch_fifo.c
@@ -83,7 +83,7 @@ struct Qdisc_ops pfifo_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct fifo_sched_data),
.enqueue = pfifo_enqueue,
.dequeue = qdisc_dequeue_head,
- .requeue = qdisc_requeue,
+ .peek = qdisc_peek_head,
.drop = qdisc_queue_drop,
.init = fifo_init,
.reset = qdisc_reset_queue,
@@ -98,7 +98,7 @@ struct Qdisc_ops bfifo_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct fifo_sched_data),
.enqueue = bfifo_enqueue,
.dequeue = qdisc_dequeue_head,
- .requeue = qdisc_requeue,
+ .peek = qdisc_peek_head,
.drop = qdisc_queue_drop,
.init = fifo_init,
.reset = qdisc_reset_queue,
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index cdcd16fcfeda..5f5efe4e6072 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -224,7 +224,7 @@ static void dev_watchdog(unsigned long arg)
char drivername[64];
WARN_ONCE(1, KERN_INFO "NETDEV WATCHDOG: %s (%s): transmit timed out\n",
dev->name, netdev_drivername(dev, drivername, 64));
- dev->tx_timeout(dev);
+ dev->netdev_ops->ndo_tx_timeout(dev);
}
if (!mod_timer(&dev->watchdog_timer,
round_jiffies(jiffies +
@@ -239,7 +239,7 @@ static void dev_watchdog(unsigned long arg)
void __netdev_watchdog_up(struct net_device *dev)
{
- if (dev->tx_timeout) {
+ if (dev->netdev_ops->ndo_tx_timeout) {
if (dev->watchdog_timeo <= 0)
dev->watchdog_timeo = 5*HZ;
if (!mod_timer(&dev->watchdog_timer,
@@ -311,21 +311,12 @@ static struct sk_buff *noop_dequeue(struct Qdisc * qdisc)
return NULL;
}
-static int noop_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
-{
- if (net_ratelimit())
- printk(KERN_DEBUG "%s deferred output. It is buggy.\n",
- skb->dev->name);
- kfree_skb(skb);
- return NET_XMIT_CN;
-}
-
struct Qdisc_ops noop_qdisc_ops __read_mostly = {
.id = "noop",
.priv_size = 0,
.enqueue = noop_enqueue,
.dequeue = noop_dequeue,
- .requeue = noop_requeue,
+ .peek = noop_dequeue,
.owner = THIS_MODULE,
};
@@ -340,7 +331,6 @@ struct Qdisc noop_qdisc = {
.flags = TCQ_F_BUILTIN,
.ops = &noop_qdisc_ops,
.list = LIST_HEAD_INIT(noop_qdisc.list),
- .requeue.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock),
.q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock),
.dev_queue = &noop_netdev_queue,
};
@@ -351,7 +341,7 @@ static struct Qdisc_ops noqueue_qdisc_ops __read_mostly = {
.priv_size = 0,
.enqueue = noop_enqueue,
.dequeue = noop_dequeue,
- .requeue = noop_requeue,
+ .peek = noop_dequeue,
.owner = THIS_MODULE,
};
@@ -367,7 +357,6 @@ static struct Qdisc noqueue_qdisc = {
.flags = TCQ_F_BUILTIN,
.ops = &noqueue_qdisc_ops,
.list = LIST_HEAD_INIT(noqueue_qdisc.list),
- .requeue.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock),
.q.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock),
.dev_queue = &noqueue_netdev_queue,
};
@@ -416,10 +405,17 @@ static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
return NULL;
}
-static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
+static struct sk_buff *pfifo_fast_peek(struct Qdisc* qdisc)
{
- qdisc->q.qlen++;
- return __qdisc_requeue(skb, qdisc, prio2list(skb, qdisc));
+ int prio;
+ struct sk_buff_head *list = qdisc_priv(qdisc);
+
+ for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) {
+ if (!skb_queue_empty(list + prio))
+ return skb_peek(list + prio);
+ }
+
+ return NULL;
}
static void pfifo_fast_reset(struct Qdisc* qdisc)
@@ -462,7 +458,7 @@ static struct Qdisc_ops pfifo_fast_ops __read_mostly = {
.priv_size = PFIFO_FAST_BANDS * sizeof(struct sk_buff_head),
.enqueue = pfifo_fast_enqueue,
.dequeue = pfifo_fast_dequeue,
- .requeue = pfifo_fast_requeue,
+ .peek = pfifo_fast_peek,
.init = pfifo_fast_init,
.reset = pfifo_fast_reset,
.dump = pfifo_fast_dump,
@@ -488,7 +484,6 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
sch->padded = (char *) sch - (char *) p;
INIT_LIST_HEAD(&sch->list);
- skb_queue_head_init(&sch->requeue);
skb_queue_head_init(&sch->q);
sch->ops = ops;
sch->enqueue = ops->enqueue;
@@ -531,6 +526,9 @@ void qdisc_reset(struct Qdisc *qdisc)
if (ops->reset)
ops->reset(qdisc);
+
+ kfree_skb(qdisc->gso_skb);
+ qdisc->gso_skb = NULL;
}
EXPORT_SYMBOL(qdisc_reset);
@@ -557,8 +555,6 @@ void qdisc_destroy(struct Qdisc *qdisc)
dev_put(qdisc_dev(qdisc));
kfree_skb(qdisc->gso_skb);
- __skb_queue_purge(&qdisc->requeue);
-
kfree((char *) qdisc - qdisc->padded);
}
EXPORT_SYMBOL(qdisc_destroy);
diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c
index c1ad6b8de105..40408d595c08 100644
--- a/net/sched/sch_gred.c
+++ b/net/sched/sch_gred.c
@@ -240,26 +240,6 @@ congestion_drop:
return NET_XMIT_CN;
}
-static int gred_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
- struct gred_sched *t = qdisc_priv(sch);
- struct gred_sched_data *q;
- u16 dp = tc_index_to_dp(skb);
-
- if (dp >= t->DPs || (q = t->tab[dp]) == NULL) {
- if (net_ratelimit())
- printk(KERN_WARNING "GRED: Unable to relocate VQ 0x%x "
- "for requeue, screwing up backlog.\n",
- tc_index_to_dp(skb));
- } else {
- if (red_is_idling(&q->parms))
- red_end_of_idle_period(&q->parms);
- q->backlog += qdisc_pkt_len(skb);
- }
-
- return qdisc_requeue(skb, sch);
-}
-
static struct sk_buff *gred_dequeue(struct Qdisc* sch)
{
struct sk_buff *skb;
@@ -602,7 +582,7 @@ static struct Qdisc_ops gred_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct gred_sched),
.enqueue = gred_enqueue,
.dequeue = gred_dequeue,
- .requeue = gred_requeue,
+ .peek = qdisc_peek_head,
.drop = gred_drop,
.init = gred_init,
.reset = gred_reset,
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index c1e77da8cd09..45c31b1a4e1d 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -184,7 +184,6 @@ struct hfsc_sched
struct rb_root eligible; /* eligible tree */
struct list_head droplist; /* active leaf class list (for
dropping) */
- struct sk_buff_head requeue; /* requeued packet */
struct qdisc_watchdog watchdog; /* watchdog timer */
};
@@ -880,28 +879,20 @@ set_passive(struct hfsc_class *cl)
*/
}
-/*
- * hack to get length of first packet in queue.
- */
static unsigned int
qdisc_peek_len(struct Qdisc *sch)
{
struct sk_buff *skb;
unsigned int len;
- skb = sch->dequeue(sch);
+ skb = sch->ops->peek(sch);
if (skb == NULL) {
if (net_ratelimit())
printk("qdisc_peek_len: non work-conserving qdisc ?\n");
return 0;
}
len = qdisc_pkt_len(skb);
- if (unlikely(sch->ops->requeue(skb, sch) != NET_XMIT_SUCCESS)) {
- if (net_ratelimit())
- printk("qdisc_peek_len: failed to requeue\n");
- qdisc_tree_decrease_qlen(sch, 1);
- return 0;
- }
+
return len;
}
@@ -1027,6 +1018,14 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
}
cur_time = psched_get_time();
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err)
+ return err;
+ }
+
sch_tree_lock(sch);
if (rsc != NULL)
hfsc_change_rsc(cl, rsc, cur_time);
@@ -1043,10 +1042,6 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
}
sch_tree_unlock(sch);
- if (tca[TCA_RATE])
- gen_replace_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch),
- tca[TCA_RATE]);
return 0;
}
@@ -1072,6 +1067,16 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
if (cl == NULL)
return -ENOBUFS;
+ if (tca[TCA_RATE]) {
+ err = gen_new_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err) {
+ kfree(cl);
+ return err;
+ }
+ }
+
if (rsc != NULL)
hfsc_change_rsc(cl, rsc, 0);
if (fsc != NULL)
@@ -1102,9 +1107,6 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
qdisc_class_hash_grow(sch, &q->clhash);
- if (tca[TCA_RATE])
- gen_new_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch), tca[TCA_RATE]);
*arg = (unsigned long)cl;
return 0;
}
@@ -1211,7 +1213,8 @@ hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
sch_tree_lock(sch);
hfsc_purge_queue(sch, cl);
- *old = xchg(&cl->qdisc, new);
+ *old = cl->qdisc;
+ cl->qdisc = new;
sch_tree_unlock(sch);
return 0;
}
@@ -1440,7 +1443,6 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
return err;
q->eligible = RB_ROOT;
INIT_LIST_HEAD(&q->droplist);
- skb_queue_head_init(&q->requeue);
q->root.cl_common.classid = sch->handle;
q->root.refcnt = 1;
@@ -1525,7 +1527,6 @@ hfsc_reset_qdisc(struct Qdisc *sch)
hlist_for_each_entry(cl, n, &q->clhash.hash[i], cl_common.hnode)
hfsc_reset_class(cl);
}
- __skb_queue_purge(&q->requeue);
q->eligible = RB_ROOT;
INIT_LIST_HEAD(&q->droplist);
qdisc_watchdog_cancel(&q->watchdog);
@@ -1550,7 +1551,6 @@ hfsc_destroy_qdisc(struct Qdisc *sch)
hfsc_destroy_class(sch, cl);
}
qdisc_class_hash_destroy(&q->clhash);
- __skb_queue_purge(&q->requeue);
qdisc_watchdog_cancel(&q->watchdog);
}
@@ -1574,7 +1574,7 @@ static int
hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct hfsc_class *cl;
- int err;
+ int uninitialized_var(err);
cl = hfsc_classify(skb, sch, &err);
if (cl == NULL) {
@@ -1617,8 +1617,6 @@ hfsc_dequeue(struct Qdisc *sch)
if (sch->q.qlen == 0)
return NULL;
- if ((skb = __skb_dequeue(&q->requeue)))
- goto out;
cur_time = psched_get_time();
@@ -1642,7 +1640,7 @@ hfsc_dequeue(struct Qdisc *sch)
}
}
- skb = cl->qdisc->dequeue(cl->qdisc);
+ skb = qdisc_dequeue_peeked(cl->qdisc);
if (skb == NULL) {
if (net_ratelimit())
printk("HFSC: Non-work-conserving qdisc ?\n");
@@ -1667,24 +1665,12 @@ hfsc_dequeue(struct Qdisc *sch)
set_passive(cl);
}
- out:
sch->flags &= ~TCQ_F_THROTTLED;
sch->q.qlen--;
return skb;
}
-static int
-hfsc_requeue(struct sk_buff *skb, struct Qdisc *sch)
-{
- struct hfsc_sched *q = qdisc_priv(sch);
-
- __skb_queue_head(&q->requeue, skb);
- sch->q.qlen++;
- sch->qstats.requeues++;
- return NET_XMIT_SUCCESS;
-}
-
static unsigned int
hfsc_drop(struct Qdisc *sch)
{
@@ -1735,7 +1721,7 @@ static struct Qdisc_ops hfsc_qdisc_ops __read_mostly = {
.dump = hfsc_dump_qdisc,
.enqueue = hfsc_enqueue,
.dequeue = hfsc_dequeue,
- .requeue = hfsc_requeue,
+ .peek = qdisc_peek_dequeued,
.drop = hfsc_drop,
.cl_ops = &hfsc_class_ops,
.priv_size = sizeof(struct hfsc_sched),
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index d14f02056ae6..5070643ce534 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -84,12 +84,12 @@ struct htb_class {
unsigned int children;
struct htb_class *parent; /* parent class */
+ int prio; /* these two are used only by leaves... */
+ int quantum; /* but stored for parent-to-leaf return */
+
union {
struct htb_class_leaf {
struct Qdisc *q;
- int prio;
- int aprio;
- int quantum;
int deficit[TC_HTB_MAXDEPTH];
struct list_head drop_list;
} leaf;
@@ -123,19 +123,8 @@ struct htb_class {
psched_tdiff_t mbuffer; /* max wait time */
long tokens, ctokens; /* current number of tokens */
psched_time_t t_c; /* checkpoint time */
-
- int prio; /* For parent to leaf return possible here */
- int quantum; /* we do backup. Finally full replacement */
- /* of un.leaf originals should be done. */
};
-static inline long L2T(struct htb_class *cl, struct qdisc_rate_table *rate,
- int size)
-{
- long result = qdisc_l2t(rate, size);
- return result;
-}
-
struct htb_sched {
struct Qdisc_class_hash clhash;
struct list_head drops[TC_HTB_NUMPRIO];/* active leaves (for drops) */
@@ -152,9 +141,6 @@ struct htb_sched {
/* time of nearest event per level (row) */
psched_time_t near_ev_cache[TC_HTB_MAXDEPTH];
- /* whether we hit non-work conserving class during this dequeue; we use */
- int nwc_hit; /* this to disable mindelay complaint in dequeue */
-
int defcls; /* class where unclassified flows go to */
/* filters for qdisc itself */
@@ -527,10 +513,10 @@ static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
WARN_ON(cl->level || !cl->un.leaf.q || !cl->un.leaf.q->q.qlen);
if (!cl->prio_activity) {
- cl->prio_activity = 1 << (cl->un.leaf.aprio = cl->un.leaf.prio);
+ cl->prio_activity = 1 << cl->prio;
htb_activate_prios(q, cl);
list_add_tail(&cl->un.leaf.drop_list,
- q->drops + cl->un.leaf.aprio);
+ q->drops + cl->prio);
}
}
@@ -551,7 +537,7 @@ static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl)
static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
- int ret;
+ int uninitialized_var(ret);
struct htb_sched *q = qdisc_priv(sch);
struct htb_class *cl = htb_classify(skb, sch, &ret);
@@ -591,45 +577,30 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return NET_XMIT_SUCCESS;
}
-/* TODO: requeuing packet charges it to policers again !! */
-static int htb_requeue(struct sk_buff *skb, struct Qdisc *sch)
+static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, long diff)
{
- int ret;
- struct htb_sched *q = qdisc_priv(sch);
- struct htb_class *cl = htb_classify(skb, sch, &ret);
- struct sk_buff *tskb;
+ long toks = diff + cl->tokens;
- if (cl == HTB_DIRECT) {
- /* enqueue to helper queue */
- if (q->direct_queue.qlen < q->direct_qlen) {
- __skb_queue_head(&q->direct_queue, skb);
- } else {
- __skb_queue_head(&q->direct_queue, skb);
- tskb = __skb_dequeue_tail(&q->direct_queue);
- kfree_skb(tskb);
- sch->qstats.drops++;
- return NET_XMIT_CN;
- }
-#ifdef CONFIG_NET_CLS_ACT
- } else if (!cl) {
- if (ret & __NET_XMIT_BYPASS)
- sch->qstats.drops++;
- kfree_skb(skb);
- return ret;
-#endif
- } else if ((ret = cl->un.leaf.q->ops->requeue(skb, cl->un.leaf.q)) !=
- NET_XMIT_SUCCESS) {
- if (net_xmit_drop_count(ret)) {
- sch->qstats.drops++;
- cl->qstats.drops++;
- }
- return ret;
- } else
- htb_activate(q, cl);
+ if (toks > cl->buffer)
+ toks = cl->buffer;
+ toks -= (long) qdisc_l2t(cl->rate, bytes);
+ if (toks <= -cl->mbuffer)
+ toks = 1 - cl->mbuffer;
- sch->q.qlen++;
- sch->qstats.requeues++;
- return NET_XMIT_SUCCESS;
+ cl->tokens = toks;
+}
+
+static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, long diff)
+{
+ long toks = diff + cl->ctokens;
+
+ if (toks > cl->cbuffer)
+ toks = cl->cbuffer;
+ toks -= (long) qdisc_l2t(cl->ceil, bytes);
+ if (toks <= -cl->mbuffer)
+ toks = 1 - cl->mbuffer;
+
+ cl->ctokens = toks;
}
/**
@@ -647,26 +618,20 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
int level, struct sk_buff *skb)
{
int bytes = qdisc_pkt_len(skb);
- long toks, diff;
enum htb_cmode old_mode;
-
-#define HTB_ACCNT(T,B,R) toks = diff + cl->T; \
- if (toks > cl->B) toks = cl->B; \
- toks -= L2T(cl, cl->R, bytes); \
- if (toks <= -cl->mbuffer) toks = 1-cl->mbuffer; \
- cl->T = toks
+ long diff;
while (cl) {
diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
if (cl->level >= level) {
if (cl->level == level)
cl->xstats.lends++;
- HTB_ACCNT(tokens, buffer, rate);
+ htb_accnt_tokens(cl, bytes, diff);
} else {
cl->xstats.borrows++;
cl->tokens += diff; /* we moved t_c; update tokens */
}
- HTB_ACCNT(ctokens, cbuffer, ceil);
+ htb_accnt_ctokens(cl, bytes, diff);
cl->t_c = q->now;
old_mode = cl->cmode;
@@ -733,14 +698,14 @@ static struct rb_node *htb_id_find_next_upper(int prio, struct rb_node *n,
while (n) {
struct htb_class *cl =
rb_entry(n, struct htb_class, node[prio]);
- if (id == cl->common.classid)
- return n;
if (id > cl->common.classid) {
n = n->rb_right;
- } else {
+ } else if (id < cl->common.classid) {
r = n;
n = n->rb_left;
+ } else {
+ return n;
}
}
return r;
@@ -761,7 +726,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
u32 *pid;
} stk[TC_HTB_MAXDEPTH], *sp = stk;
- WARN_ON(!tree->rb_node);
+ BUG_ON(!tree->rb_node);
sp->root = tree->rb_node;
sp->pptr = pptr;
sp->pid = pid;
@@ -781,9 +746,10 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
*sp->pptr = (*sp->pptr)->rb_left;
if (sp > stk) {
sp--;
- WARN_ON(!*sp->pptr);
- if (!*sp->pptr)
+ if (!*sp->pptr) {
+ WARN_ON(1);
return NULL;
+ }
htb_next_rb_node(sp->pptr);
}
} else {
@@ -814,8 +780,7 @@ static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, int prio,
do {
next:
- WARN_ON(!cl);
- if (!cl)
+ if (unlikely(!cl))
return NULL;
/* class can be empty - it is unlikely but can be true if leaf
@@ -849,7 +814,7 @@ next:
cl->common.classid);
cl->warned = 1;
}
- q->nwc_hit++;
+
htb_next_rb_node((level ? cl->parent->un.inner.ptr : q->
ptr[0]) + prio);
cl = htb_lookup_leaf(q->row[level] + prio, prio,
@@ -861,7 +826,7 @@ next:
if (likely(skb != NULL)) {
cl->un.leaf.deficit[level] -= qdisc_pkt_len(skb);
if (cl->un.leaf.deficit[level] < 0) {
- cl->un.leaf.deficit[level] += cl->un.leaf.quantum;
+ cl->un.leaf.deficit[level] += cl->quantum;
htb_next_rb_node((level ? cl->parent->un.inner.ptr : q->
ptr[0]) + prio);
}
@@ -894,7 +859,7 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch)
q->now = psched_get_time();
next_event = q->now + 5 * PSCHED_TICKS_PER_SEC;
- q->nwc_hit = 0;
+
for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
/* common case optimization - skip event handler quickly */
int m;
@@ -1095,8 +1060,8 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
opt.buffer = cl->buffer;
opt.ceil = cl->ceil->rate;
opt.cbuffer = cl->cbuffer;
- opt.quantum = cl->un.leaf.quantum;
- opt.prio = cl->un.leaf.prio;
+ opt.quantum = cl->quantum;
+ opt.prio = cl->prio;
opt.level = cl->level;
NLA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt);
@@ -1141,7 +1106,9 @@ static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
== NULL)
return -ENOBUFS;
sch_tree_lock(sch);
- if ((*old = xchg(&cl->un.leaf.q, new)) != NULL) {
+ *old = cl->un.leaf.q;
+ cl->un.leaf.q = new;
+ if (*old != NULL) {
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
qdisc_reset(*old);
}
@@ -1198,8 +1165,6 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
memset(&parent->un.inner, 0, sizeof(parent->un.inner));
INIT_LIST_HEAD(&parent->un.leaf.drop_list);
parent->un.leaf.q = new_q ? new_q : &noop_qdisc;
- parent->un.leaf.quantum = parent->quantum;
- parent->un.leaf.prio = parent->prio;
parent->tokens = parent->buffer;
parent->ctokens = parent->cbuffer;
parent->t_c = psched_get_time();
@@ -1371,9 +1336,14 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
if ((cl = kzalloc(sizeof(*cl), GFP_KERNEL)) == NULL)
goto failure;
- gen_new_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch),
- tca[TCA_RATE] ? : &est.nla);
+ err = gen_new_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE] ? : &est.nla);
+ if (err) {
+ kfree(cl);
+ goto failure;
+ }
+
cl->refcnt = 1;
cl->children = 0;
INIT_LIST_HEAD(&cl->un.leaf.drop_list);
@@ -1425,37 +1395,36 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
if (parent)
parent->children++;
} else {
- if (tca[TCA_RATE])
- gen_replace_estimator(&cl->bstats, &cl->rate_est,
- qdisc_root_sleeping_lock(sch),
- tca[TCA_RATE]);
+ if (tca[TCA_RATE]) {
+ err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
+ qdisc_root_sleeping_lock(sch),
+ tca[TCA_RATE]);
+ if (err)
+ return err;
+ }
sch_tree_lock(sch);
}
/* it used to be a nasty bug here, we have to check that node
is really leaf before changing cl->un.leaf ! */
if (!cl->level) {
- cl->un.leaf.quantum = rtab->rate.rate / q->rate2quantum;
- if (!hopt->quantum && cl->un.leaf.quantum < 1000) {
+ cl->quantum = rtab->rate.rate / q->rate2quantum;
+ if (!hopt->quantum && cl->quantum < 1000) {
printk(KERN_WARNING
"HTB: quantum of class %X is small. Consider r2q change.\n",
cl->common.classid);
- cl->un.leaf.quantum = 1000;
+ cl->quantum = 1000;
}
- if (!hopt->quantum && cl->un.leaf.quantum > 200000) {
+ if (!hopt->quantum && cl->quantum > 200000) {
printk(KERN_WARNING
"HTB: quantum of class %X is big. Consider r2q change.\n",
cl->common.classid);
- cl->un.leaf.quantum = 200000;
+ cl->quantum = 200000;
}
if (hopt->quantum)
- cl->un.leaf.quantum = hopt->quantum;
- if ((cl->un.leaf.prio = hopt->prio) >= TC_HTB_NUMPRIO)
- cl->un.leaf.prio = TC_HTB_NUMPRIO - 1;
-
- /* backup for htb_parent_to_leaf */
- cl->quantum = cl->un.leaf.quantum;
- cl->prio = cl->un.leaf.prio;
+ cl->quantum = hopt->quantum;
+ if ((cl->prio = hopt->prio) >= TC_HTB_NUMPRIO)
+ cl->prio = TC_HTB_NUMPRIO - 1;
}
cl->buffer = hopt->buffer;
@@ -1565,7 +1534,7 @@ static struct Qdisc_ops htb_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct htb_sched),
.enqueue = htb_enqueue,
.dequeue = htb_dequeue,
- .requeue = htb_requeue,
+ .peek = qdisc_peek_dequeued,
.drop = htb_drop,
.init = htb_init,
.reset = htb_reset,
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 915f3149dde2..7e151861794b 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -92,40 +92,6 @@ multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return ret;
}
-
-static int
-multiq_requeue(struct sk_buff *skb, struct Qdisc *sch)
-{
- struct Qdisc *qdisc;
- struct multiq_sched_data *q = qdisc_priv(sch);
- int ret;
-
- qdisc = multiq_classify(skb, sch, &ret);
-#ifdef CONFIG_NET_CLS_ACT
- if (qdisc == NULL) {
- if (ret & __NET_XMIT_BYPASS)
- sch->qstats.drops++;
- kfree_skb(skb);
- return ret;
- }
-#endif
-
- ret = qdisc->ops->requeue(skb, qdisc);
- if (ret == NET_XMIT_SUCCESS) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- if (q->curband)
- q->curband--;
- else
- q->curband = q->bands - 1;
- return NET_XMIT_SUCCESS;
- }
- if (net_xmit_drop_count(ret))
- sch->qstats.drops++;
- return ret;
-}
-
-
static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
{
struct multiq_sched_data *q = qdisc_priv(sch);
@@ -140,7 +106,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
q->curband = 0;
/* Check that target subqueue is available before
- * pulling an skb to avoid excessive requeues
+ * pulling an skb to avoid head-of-line blocking.
*/
if (!__netif_subqueue_stopped(qdisc_dev(sch), q->curband)) {
qdisc = q->queues[q->curband];
@@ -155,6 +121,34 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
}
+static struct sk_buff *multiq_peek(struct Qdisc *sch)
+{
+ struct multiq_sched_data *q = qdisc_priv(sch);
+ unsigned int curband = q->curband;
+ struct Qdisc *qdisc;
+ struct sk_buff *skb;
+ int band;
+
+ for (band = 0; band < q->bands; band++) {
+ /* cycle through bands to ensure fairness */
+ curband++;
+ if (curband >= q->bands)
+ curband = 0;
+
+ /* Check that target subqueue is available before
+ * pulling an skb to avoid head-of-line blocking.
+ */
+ if (!__netif_subqueue_stopped(qdisc_dev(sch), curband)) {
+ qdisc = q->queues[curband];
+ skb = qdisc->ops->peek(qdisc);
+ if (skb)
+ return skb;
+ }
+ }
+ return NULL;
+
+}
+
static unsigned int multiq_drop(struct Qdisc *sch)
{
struct multiq_sched_data *q = qdisc_priv(sch);
@@ -220,7 +214,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
q->bands = qopt->bands;
for (i = q->bands; i < q->max_bands; i++) {
if (q->queues[i] != &noop_qdisc) {
- struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc);
+ struct Qdisc *child = q->queues[i];
+ q->queues[i] = &noop_qdisc;
qdisc_tree_decrease_qlen(child, child->q.qlen);
qdisc_destroy(child);
}
@@ -230,7 +225,7 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
for (i = 0; i < q->bands; i++) {
if (q->queues[i] == &noop_qdisc) {
- struct Qdisc *child;
+ struct Qdisc *child, *old;
child = qdisc_create_dflt(qdisc_dev(sch),
sch->dev_queue,
&pfifo_qdisc_ops,
@@ -238,12 +233,13 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt)
i + 1));
if (child) {
sch_tree_lock(sch);
- child = xchg(&q->queues[i], child);
+ old = q->queues[i];
+ q->queues[i] = child;
- if (child != &noop_qdisc) {
- qdisc_tree_decrease_qlen(child,
- child->q.qlen);
- qdisc_destroy(child);
+ if (old != &noop_qdisc) {
+ qdisc_tree_decrease_qlen(old,
+ old->q.qlen);
+ qdisc_destroy(old);
}
sch_tree_unlock(sch);
}
@@ -451,7 +447,7 @@ static struct Qdisc_ops multiq_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct multiq_sched_data),
.enqueue = multiq_enqueue,
.dequeue = multiq_dequeue,
- .requeue = multiq_requeue,
+ .peek = multiq_peek,
.drop = multiq_drop,
.init = multiq_init,
.reset = multiq_reset,
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 98402f0efa47..d876b8734848 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -230,7 +230,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
*/
cb->time_to_send = psched_get_time();
q->counter = 0;
- ret = q->qdisc->ops->requeue(skb, q->qdisc);
+
+ __skb_queue_head(&q->qdisc->q, skb);
+ q->qdisc->qstats.backlog += qdisc_pkt_len(skb);
+ q->qdisc->qstats.requeues++;
+ ret = NET_XMIT_SUCCESS;
}
if (likely(ret == NET_XMIT_SUCCESS)) {
@@ -245,20 +249,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return ret;
}
-/* Requeue packets but don't change time stamp */
-static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
-{
- struct netem_sched_data *q = qdisc_priv(sch);
- int ret;
-
- if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- }
-
- return ret;
-}
-
static unsigned int netem_drop(struct Qdisc* sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
@@ -276,29 +266,25 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
struct netem_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
- smp_mb();
if (sch->flags & TCQ_F_THROTTLED)
return NULL;
- skb = q->qdisc->dequeue(q->qdisc);
+ skb = q->qdisc->ops->peek(q->qdisc);
if (skb) {
const struct netem_skb_cb *cb = netem_skb_cb(skb);
psched_time_t now = psched_get_time();
/* if more time remaining? */
if (cb->time_to_send <= now) {
+ skb = qdisc_dequeue_peeked(q->qdisc);
+ if (unlikely(!skb))
+ return NULL;
+
pr_debug("netem_dequeue: return skb=%p\n", skb);
sch->q.qlen--;
return skb;
}
- if (unlikely(q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS)) {
- qdisc_tree_decrease_qlen(q->qdisc, 1);
- sch->qstats.drops++;
- printk(KERN_ERR "netem: %s could not requeue\n",
- q->qdisc->ops->id);
- }
-
qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send);
}
@@ -341,14 +327,13 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr)
root_lock = qdisc_root_sleeping_lock(sch);
spin_lock_bh(root_lock);
- d = xchg(&q->delay_dist, d);
+ kfree(q->delay_dist);
+ q->delay_dist = d;
spin_unlock_bh(root_lock);
-
- kfree(d);
return 0;
}
-static int get_correlation(struct Qdisc *sch, const struct nlattr *attr)
+static void get_correlation(struct Qdisc *sch, const struct nlattr *attr)
{
struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_corr *c = nla_data(attr);
@@ -356,27 +341,24 @@ static int get_correlation(struct Qdisc *sch, const struct nlattr *attr)
init_crandom(&q->delay_cor, c->delay_corr);
init_crandom(&q->loss_cor, c->loss_corr);
init_crandom(&q->dup_cor, c->dup_corr);
- return 0;
}
-static int get_reorder(struct Qdisc *sch, const struct nlattr *attr)
+static void get_reorder(struct Qdisc *sch, const struct nlattr *attr)
{
struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_reorder *r = nla_data(attr);
q->reorder = r->probability;
init_crandom(&q->reorder_cor, r->correlation);
- return 0;
}
-static int get_corrupt(struct Qdisc *sch, const struct nlattr *attr)
+static void get_corrupt(struct Qdisc *sch, const struct nlattr *attr)
{
struct netem_sched_data *q = qdisc_priv(sch);
const struct tc_netem_corrupt *r = nla_data(attr);
q->corrupt = r->probability;
init_crandom(&q->corrupt_cor, r->correlation);
- return 0;
}
static const struct nla_policy netem_policy[TCA_NETEM_MAX + 1] = {
@@ -435,11 +417,8 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
if (q->gap)
q->reorder = ~0;
- if (tb[TCA_NETEM_CORR]) {
- ret = get_correlation(sch, tb[TCA_NETEM_CORR]);
- if (ret)
- return ret;
- }
+ if (tb[TCA_NETEM_CORR])
+ get_correlation(sch, tb[TCA_NETEM_CORR]);
if (tb[TCA_NETEM_DELAY_DIST]) {
ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST]);
@@ -447,17 +426,11 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt)
return ret;
}
- if (tb[TCA_NETEM_REORDER]) {
- ret = get_reorder(sch, tb[TCA_NETEM_REORDER]);
- if (ret)
- return ret;
- }
+ if (tb[TCA_NETEM_REORDER])
+ get_reorder(sch, tb[TCA_NETEM_REORDER]);
- if (tb[TCA_NETEM_CORRUPT]) {
- ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT]);
- if (ret)
- return ret;
- }
+ if (tb[TCA_NETEM_CORRUPT])
+ get_corrupt(sch, tb[TCA_NETEM_CORRUPT]);
return 0;
}
@@ -538,7 +511,7 @@ static struct Qdisc_ops tfifo_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct fifo_sched_data),
.enqueue = tfifo_enqueue,
.dequeue = qdisc_dequeue_head,
- .requeue = qdisc_requeue,
+ .peek = qdisc_peek_head,
.drop = qdisc_queue_drop,
.init = tfifo_init,
.reset = qdisc_reset_queue,
@@ -621,99 +594,12 @@ nla_put_failure:
return -1;
}
-static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
- struct sk_buff *skb, struct tcmsg *tcm)
-{
- struct netem_sched_data *q = qdisc_priv(sch);
-
- if (cl != 1) /* only one class */
- return -ENOENT;
-
- tcm->tcm_handle |= TC_H_MIN(1);
- tcm->tcm_info = q->qdisc->handle;
-
- return 0;
-}
-
-static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
- struct Qdisc **old)
-{
- struct netem_sched_data *q = qdisc_priv(sch);
-
- if (new == NULL)
- new = &noop_qdisc;
-
- sch_tree_lock(sch);
- *old = xchg(&q->qdisc, new);
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
-
- return 0;
-}
-
-static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg)
-{
- struct netem_sched_data *q = qdisc_priv(sch);
- return q->qdisc;
-}
-
-static unsigned long netem_get(struct Qdisc *sch, u32 classid)
-{
- return 1;
-}
-
-static void netem_put(struct Qdisc *sch, unsigned long arg)
-{
-}
-
-static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
- struct nlattr **tca, unsigned long *arg)
-{
- return -ENOSYS;
-}
-
-static int netem_delete(struct Qdisc *sch, unsigned long arg)
-{
- return -ENOSYS;
-}
-
-static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker)
-{
- if (!walker->stop) {
- if (walker->count >= walker->skip)
- if (walker->fn(sch, 1, walker) < 0) {
- walker->stop = 1;
- return;
- }
- walker->count++;
- }
-}
-
-static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl)
-{
- return NULL;
-}
-
-static const struct Qdisc_class_ops netem_class_ops = {
- .graft = netem_graft,
- .leaf = netem_leaf,
- .get = netem_get,
- .put = netem_put,
- .change = netem_change_class,
- .delete = netem_delete,
- .walk = netem_walk,
- .tcf_chain = netem_find_tcf,
- .dump = netem_dump_class,
-};
-
static struct Qdisc_ops netem_qdisc_ops __read_mostly = {
.id = "netem",
- .cl_ops = &netem_class_ops,
.priv_size = sizeof(struct netem_sched_data),
.enqueue = netem_enqueue,
.dequeue = netem_dequeue,
- .requeue = netem_requeue,
+ .peek = qdisc_peek_dequeued,
.drop = netem_drop,
.init = netem_init,
.reset = netem_reset,
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 504a78cdb718..94cecef70145 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -93,34 +93,20 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return ret;
}
-
-static int
-prio_requeue(struct sk_buff *skb, struct Qdisc* sch)
+static struct sk_buff *prio_peek(struct Qdisc *sch)
{
- struct Qdisc *qdisc;
- int ret;
-
- qdisc = prio_classify(skb, sch, &ret);
-#ifdef CONFIG_NET_CLS_ACT
- if (qdisc == NULL) {
- if (ret & __NET_XMIT_BYPASS)
- sch->qstats.drops++;
- kfree_skb(skb);
- return ret;
- }
-#endif
+ struct prio_sched_data *q = qdisc_priv(sch);
+ int prio;
- if ((ret = qdisc->ops->requeue(skb, qdisc)) == NET_XMIT_SUCCESS) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- return NET_XMIT_SUCCESS;
+ for (prio = 0; prio < q->bands; prio++) {
+ struct Qdisc *qdisc = q->queues[prio];
+ struct sk_buff *skb = qdisc->ops->peek(qdisc);
+ if (skb)
+ return skb;
}
- if (net_xmit_drop_count(ret))
- sch->qstats.drops++;
- return ret;
+ return NULL;
}
-
static struct sk_buff *prio_dequeue(struct Qdisc* sch)
{
struct prio_sched_data *q = qdisc_priv(sch);
@@ -201,7 +187,8 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
for (i=q->bands; i<TCQ_PRIO_BANDS; i++) {
- struct Qdisc *child = xchg(&q->queues[i], &noop_qdisc);
+ struct Qdisc *child = q->queues[i];
+ q->queues[i] = &noop_qdisc;
if (child != &noop_qdisc) {
qdisc_tree_decrease_qlen(child, child->q.qlen);
qdisc_destroy(child);
@@ -211,18 +198,19 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
for (i=0; i<q->bands; i++) {
if (q->queues[i] == &noop_qdisc) {
- struct Qdisc *child;
+ struct Qdisc *child, *old;
child = qdisc_create_dflt(qdisc_dev(sch), sch->dev_queue,
&pfifo_qdisc_ops,
TC_H_MAKE(sch->handle, i + 1));
if (child) {
sch_tree_lock(sch);
- child = xchg(&q->queues[i], child);
+ old = q->queues[i];
+ q->queues[i] = child;
- if (child != &noop_qdisc) {
- qdisc_tree_decrease_qlen(child,
- child->q.qlen);
- qdisc_destroy(child);
+ if (old != &noop_qdisc) {
+ qdisc_tree_decrease_qlen(old,
+ old->q.qlen);
+ qdisc_destroy(old);
}
sch_tree_unlock(sch);
}
@@ -421,7 +409,7 @@ static struct Qdisc_ops prio_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct prio_sched_data),
.enqueue = prio_enqueue,
.dequeue = prio_dequeue,
- .requeue = prio_requeue,
+ .peek = prio_peek,
.drop = prio_drop,
.init = prio_init,
.reset = prio_reset,
diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c
index 5da05839e225..2bdf241f6315 100644
--- a/net/sched/sch_red.c
+++ b/net/sched/sch_red.c
@@ -108,23 +108,6 @@ congestion_drop:
return NET_XMIT_CN;
}
-static int red_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
- struct red_sched_data *q = qdisc_priv(sch);
- struct Qdisc *child = q->qdisc;
- int ret;
-
- if (red_is_idling(&q->parms))
- red_end_of_idle_period(&q->parms);
-
- ret = child->ops->requeue(skb, child);
- if (likely(ret == NET_XMIT_SUCCESS)) {
- sch->qstats.requeues++;
- sch->q.qlen++;
- }
- return ret;
-}
-
static struct sk_buff * red_dequeue(struct Qdisc* sch)
{
struct sk_buff *skb;
@@ -140,6 +123,14 @@ static struct sk_buff * red_dequeue(struct Qdisc* sch)
return skb;
}
+static struct sk_buff * red_peek(struct Qdisc* sch)
+{
+ struct red_sched_data *q = qdisc_priv(sch);
+ struct Qdisc *child = q->qdisc;
+
+ return child->ops->peek(child);
+}
+
static unsigned int red_drop(struct Qdisc* sch)
{
struct red_sched_data *q = qdisc_priv(sch);
@@ -211,7 +202,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt)
q->limit = ctl->limit;
if (child) {
qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
- qdisc_destroy(xchg(&q->qdisc, child));
+ qdisc_destroy(q->qdisc);
+ q->qdisc = child;
}
red_set_parms(&q->parms, ctl->qth_min, ctl->qth_max, ctl->Wlog,
@@ -292,7 +284,8 @@ static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
new = &noop_qdisc;
sch_tree_lock(sch);
- *old = xchg(&q->qdisc, new);
+ *old = q->qdisc;
+ q->qdisc = new;
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
qdisc_reset(*old);
sch_tree_unlock(sch);
@@ -361,7 +354,7 @@ static struct Qdisc_ops red_qdisc_ops __read_mostly = {
.cl_ops = &red_class_ops,
.enqueue = red_enqueue,
.dequeue = red_dequeue,
- .requeue = red_requeue,
+ .peek = red_peek,
.drop = red_drop,
.init = red_init,
.reset = red_reset,
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index fe1508ef0d3d..f3965df00559 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -281,7 +281,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
struct sfq_sched_data *q = qdisc_priv(sch);
unsigned int hash;
sfq_index x;
- int ret;
+ int uninitialized_var(ret);
hash = sfq_classify(skb, sch, &ret);
if (hash == 0) {
@@ -329,71 +329,20 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return NET_XMIT_CN;
}
-static int
-sfq_requeue(struct sk_buff *skb, struct Qdisc *sch)
+static struct sk_buff *
+sfq_peek(struct Qdisc *sch)
{
struct sfq_sched_data *q = qdisc_priv(sch);
- unsigned int hash;
- sfq_index x;
- int ret;
-
- hash = sfq_classify(skb, sch, &ret);
- if (hash == 0) {
- if (ret & __NET_XMIT_BYPASS)
- sch->qstats.drops++;
- kfree_skb(skb);
- return ret;
- }
- hash--;
+ sfq_index a;
- x = q->ht[hash];
- if (x == SFQ_DEPTH) {
- q->ht[hash] = x = q->dep[SFQ_DEPTH].next;
- q->hash[x] = hash;
- }
-
- sch->qstats.backlog += qdisc_pkt_len(skb);
- __skb_queue_head(&q->qs[x], skb);
- /* If selected queue has length q->limit+1, this means that
- * all another queues are empty and we do simple tail drop.
- * This packet is still requeued at head of queue, tail packet
- * is dropped.
- */
- if (q->qs[x].qlen > q->limit) {
- skb = q->qs[x].prev;
- __skb_unlink(skb, &q->qs[x]);
- sch->qstats.drops++;
- sch->qstats.backlog -= qdisc_pkt_len(skb);
- kfree_skb(skb);
- return NET_XMIT_CN;
- }
-
- sfq_inc(q, x);
- if (q->qs[x].qlen == 1) { /* The flow is new */
- if (q->tail == SFQ_DEPTH) { /* It is the first flow */
- q->tail = x;
- q->next[x] = x;
- q->allot[x] = q->quantum;
- } else {
- q->next[x] = q->next[q->tail];
- q->next[q->tail] = x;
- q->tail = x;
- }
- }
-
- if (++sch->q.qlen <= q->limit) {
- sch->qstats.requeues++;
- return 0;
- }
+ /* No active slots */
+ if (q->tail == SFQ_DEPTH)
+ return NULL;
- sch->qstats.drops++;
- sfq_drop(sch);
- return NET_XMIT_CN;
+ a = q->next[q->tail];
+ return skb_peek(&q->qs[a]);
}
-
-
-
static struct sk_buff *
sfq_dequeue(struct Qdisc *sch)
{
@@ -624,7 +573,7 @@ static struct Qdisc_ops sfq_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct sfq_sched_data),
.enqueue = sfq_enqueue,
.dequeue = sfq_dequeue,
- .requeue = sfq_requeue,
+ .peek = sfq_peek,
.drop = sfq_drop,
.init = sfq_init,
.reset = sfq_reset,
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 94c61598b86a..a2f93c09f3cc 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -139,19 +139,6 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
return 0;
}
-static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
- struct tbf_sched_data *q = qdisc_priv(sch);
- int ret;
-
- if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
- sch->q.qlen++;
- sch->qstats.requeues++;
- }
-
- return ret;
-}
-
static unsigned int tbf_drop(struct Qdisc* sch)
{
struct tbf_sched_data *q = qdisc_priv(sch);
@@ -169,7 +156,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
struct tbf_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
- skb = q->qdisc->dequeue(q->qdisc);
+ skb = q->qdisc->ops->peek(q->qdisc);
if (skb) {
psched_time_t now;
@@ -192,6 +179,10 @@ static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
toks -= L2T(q, len);
if ((toks|ptoks) >= 0) {
+ skb = qdisc_dequeue_peeked(q->qdisc);
+ if (unlikely(!skb))
+ return NULL;
+
q->t_c = now;
q->tokens = toks;
q->ptokens = ptoks;
@@ -214,12 +205,6 @@ static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
(cf. CSZ, HPFQ, HFSC)
*/
- if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
- /* When requeue fails skb is dropped */
- qdisc_tree_decrease_qlen(q->qdisc, 1);
- sch->qstats.drops++;
- }
-
sch->qstats.overlimits++;
}
return NULL;
@@ -251,6 +236,7 @@ static int tbf_change(struct Qdisc* sch, struct nlattr *opt)
struct tc_tbf_qopt *qopt;
struct qdisc_rate_table *rtab = NULL;
struct qdisc_rate_table *ptab = NULL;
+ struct qdisc_rate_table *tmp;
struct Qdisc *child = NULL;
int max_size,n;
@@ -299,7 +285,8 @@ static int tbf_change(struct Qdisc* sch, struct nlattr *opt)
sch_tree_lock(sch);
if (child) {
qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
- qdisc_destroy(xchg(&q->qdisc, child));
+ qdisc_destroy(q->qdisc);
+ q->qdisc = child;
}
q->limit = qopt->limit;
q->mtu = qopt->mtu;
@@ -307,8 +294,14 @@ static int tbf_change(struct Qdisc* sch, struct nlattr *opt)
q->buffer = qopt->buffer;
q->tokens = q->buffer;
q->ptokens = q->mtu;
- rtab = xchg(&q->R_tab, rtab);
- ptab = xchg(&q->P_tab, ptab);
+
+ tmp = q->R_tab;
+ q->R_tab = rtab;
+ rtab = tmp;
+
+ tmp = q->P_tab;
+ q->P_tab = ptab;
+ ptab = tmp;
sch_tree_unlock(sch);
err = 0;
done:
@@ -398,7 +391,8 @@ static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
new = &noop_qdisc;
sch_tree_lock(sch);
- *old = xchg(&q->qdisc, new);
+ *old = q->qdisc;
+ q->qdisc = new;
qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
qdisc_reset(*old);
sch_tree_unlock(sch);
@@ -469,7 +463,7 @@ static struct Qdisc_ops tbf_qdisc_ops __read_mostly = {
.priv_size = sizeof(struct tbf_sched_data),
.enqueue = tbf_enqueue,
.dequeue = tbf_dequeue,
- .requeue = tbf_requeue,
+ .peek = qdisc_peek_dequeued,
.drop = tbf_drop,
.init = tbf_init,
.reset = tbf_reset,
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index d35ef059abb1..cfc8e7caba62 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -93,16 +93,6 @@ teql_enqueue(struct sk_buff *skb, struct Qdisc* sch)
return NET_XMIT_DROP;
}
-static int
-teql_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
- struct teql_sched_data *q = qdisc_priv(sch);
-
- __skb_queue_head(&q->q, skb);
- sch->qstats.requeues++;
- return 0;
-}
-
static struct sk_buff *
teql_dequeue(struct Qdisc* sch)
{
@@ -123,6 +113,13 @@ teql_dequeue(struct Qdisc* sch)
return skb;
}
+static struct sk_buff *
+teql_peek(struct Qdisc* sch)
+{
+ /* teql is meant to be used as root qdisc */
+ return NULL;
+}
+
static __inline__ void
teql_neigh_release(struct neighbour *n)
{
@@ -433,7 +430,7 @@ static __init void teql_master_setup(struct net_device *dev)
ops->enqueue = teql_enqueue;
ops->dequeue = teql_dequeue;
- ops->requeue = teql_requeue;
+ ops->peek = teql_peek;
ops->init = teql_qdisc_init;
ops->reset = teql_reset;
ops->destroy = teql_destroy;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 4124bbb99947..ceaa4aa066ea 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -223,10 +223,9 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
}
- SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
- "src:" NIP6_FMT " dst:" NIP6_FMT "\n",
+ SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n",
__func__, skb, skb->len,
- NIP6(fl.fl6_src), NIP6(fl.fl6_dst));
+ &fl.fl6_src, &fl.fl6_dst);
SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS);
@@ -252,23 +251,19 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
fl.oif = daddr->v6.sin6_scope_id;
- SCTP_DEBUG_PRINTK("%s: DST=" NIP6_FMT " ",
- __func__, NIP6(fl.fl6_dst));
+ SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl.fl6_dst);
if (saddr) {
ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr);
- SCTP_DEBUG_PRINTK(
- "SRC=" NIP6_FMT " - ",
- NIP6(fl.fl6_src));
+ SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl.fl6_src);
}
dst = ip6_route_output(&init_net, NULL, &fl);
if (!dst->error) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
- SCTP_DEBUG_PRINTK(
- "rt6_dst:" NIP6_FMT " rt6_src:" NIP6_FMT "\n",
- NIP6(rt->rt6i_dst.addr), NIP6(rt->rt6i_src.addr));
+ SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
+ &rt->rt6i_dst.addr, &rt->rt6i_src.addr);
return dst;
}
SCTP_DEBUG_PRINTK("NO ROUTE\n");
@@ -314,9 +309,8 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
__u8 matchlen = 0;
__u8 bmatchlen;
- SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p "
- "daddr:" NIP6_FMT " ",
- __func__, asoc, dst, NIP6(daddr->v6.sin6_addr));
+ SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ",
+ __func__, asoc, dst, &daddr->v6.sin6_addr);
if (!asoc) {
ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)),
@@ -324,8 +318,8 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
&daddr->v6.sin6_addr,
inet6_sk(&sk->inet.sk)->srcprefs,
&saddr->v6.sin6_addr);
- SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " NIP6_FMT "\n",
- NIP6(saddr->v6.sin6_addr));
+ SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n",
+ &saddr->v6.sin6_addr);
return;
}
@@ -353,12 +347,11 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
if (baddr) {
memcpy(saddr, baddr, sizeof(union sctp_addr));
- SCTP_DEBUG_PRINTK("saddr: " NIP6_FMT "\n",
- NIP6(saddr->v6.sin6_addr));
+ SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr);
} else {
printk(KERN_ERR "%s: asoc:%p Could not find a valid source "
- "address for the dest:" NIP6_FMT "\n",
- __func__, asoc, NIP6(daddr->v6.sin6_addr));
+ "address for the dest:%pI6\n",
+ __func__, asoc, &daddr->v6.sin6_addr);
}
rcu_read_unlock();
@@ -727,7 +720,7 @@ static int sctp_v6_is_ce(const struct sk_buff *skb)
/* Dump the v6 addr to the seq file. */
static void sctp_v6_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
{
- seq_printf(seq, NIP6_FMT " ", NIP6(addr->v6.sin6_addr));
+ seq_printf(seq, "%pI6 ", &addr->v6.sin6_addr);
}
static void sctp_v6_ecn_capable(struct sock *sk)
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 0b65354aaf64..b78e3be69013 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -102,6 +102,8 @@ struct sock *sctp_get_ctl_sock(void)
/* Set up the proc fs entry for the SCTP protocol. */
static __init int sctp_proc_init(void)
{
+ if (percpu_counter_init(&sctp_sockets_allocated, 0))
+ goto out_nomem;
#ifdef CONFIG_PROC_FS
if (!proc_net_sctp) {
struct proc_dir_entry *ent;
@@ -110,7 +112,7 @@ static __init int sctp_proc_init(void)
ent->owner = THIS_MODULE;
proc_net_sctp = ent;
} else
- goto out_nomem;
+ goto out_free_percpu;
}
if (sctp_snmp_proc_init())
@@ -135,11 +137,14 @@ out_snmp_proc_init:
proc_net_sctp = NULL;
remove_proc_entry("sctp", init_net.proc_net);
}
-out_nomem:
- return -ENOMEM;
+out_free_percpu:
+ percpu_counter_destroy(&sctp_sockets_allocated);
#else
return 0;
#endif /* CONFIG_PROC_FS */
+
+out_nomem:
+ return -ENOMEM;
}
/* Clean up the proc fs entry for the SCTP protocol.
@@ -482,9 +487,8 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
- SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
- __func__, NIPQUAD(fl.fl4_dst),
- NIPQUAD(fl.fl4_src));
+ SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ",
+ __func__, &fl.fl4_dst, &fl.fl4_src);
if (!ip_route_output_key(&init_net, &rt, &fl)) {
dst = &rt->u.dst;
@@ -540,8 +544,8 @@ out_unlock:
rcu_read_unlock();
out:
if (dst)
- SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
- NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_src));
+ SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n",
+ &rt->rt_dst, &rt->rt_src);
else
SCTP_DEBUG_PRINTK("NO ROUTE\n");
@@ -646,7 +650,7 @@ static void sctp_v4_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr)
/* Dump the v4 addr to the seq file. */
static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
{
- seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
+ seq_printf(seq, "%pI4 ", &addr->v4.sin_addr);
}
static void sctp_v4_ecn_capable(struct sock *sk)
@@ -866,11 +870,10 @@ static inline int sctp_v4_xmit(struct sk_buff *skb,
{
struct inet_sock *inet = inet_sk(skb->sk);
- SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
- "src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n",
+ SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n",
__func__, skb, skb->len,
- NIPQUAD(skb->rtable->rt_src),
- NIPQUAD(skb->rtable->rt_dst));
+ &skb->rtable->rt_src,
+ &skb->rtable->rt_dst);
inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ?
IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index a6a0ea71ae93..1c4e5d6c29c0 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -1123,19 +1123,17 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
if (from_addr.sa.sa_family == AF_INET6) {
if (net_ratelimit())
printk(KERN_WARNING
- "%s association %p could not find address "
- NIP6_FMT "\n",
+ "%s association %p could not find address %pI6\n",
__func__,
asoc,
- NIP6(from_addr.v6.sin6_addr));
+ &from_addr.v6.sin6_addr);
} else {
if (net_ratelimit())
printk(KERN_WARNING
- "%s association %p could not find address "
- NIPQUAD_FMT "\n",
+ "%s association %p could not find address %pI4\n",
__func__,
asoc,
- NIPQUAD(from_addr.v4.sin_addr.s_addr));
+ &from_addr.v4.sin_addr.s_addr);
}
return SCTP_DISPOSITION_DISCARD;
}
@@ -3691,6 +3689,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
{
struct sctp_chunk *chunk = arg;
struct sctp_fwdtsn_hdr *fwdtsn_hdr;
+ struct sctp_fwdtsn_skip *skip;
__u16 len;
__u32 tsn;
@@ -3720,6 +3719,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep,
if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0)
goto discard_noforce;
+ /* Silently discard the chunk if stream-id is not valid */
+ sctp_walk_fwdtsn(skip, chunk) {
+ if (ntohs(skip->stream) >= asoc->c.sinit_max_instreams)
+ goto discard_noforce;
+ }
+
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));
if (len > sizeof(struct sctp_fwdtsn_hdr))
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN,
@@ -3751,6 +3756,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
{
struct sctp_chunk *chunk = arg;
struct sctp_fwdtsn_hdr *fwdtsn_hdr;
+ struct sctp_fwdtsn_skip *skip;
__u16 len;
__u32 tsn;
@@ -3780,6 +3786,12 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0)
goto gen_shutdown;
+ /* Silently discard the chunk if stream-id is not valid */
+ sctp_walk_fwdtsn(skip, chunk) {
+ if (ntohs(skip->stream) >= asoc->c.sinit_max_instreams)
+ goto gen_shutdown;
+ }
+
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn));
if (len > sizeof(struct sctp_fwdtsn_hdr))
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index a1b904529d5e..b14a8f33e42d 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -114,7 +114,7 @@ extern int sysctl_sctp_wmem[3];
static int sctp_memory_pressure;
static atomic_t sctp_memory_allocated;
-static atomic_t sctp_sockets_allocated;
+struct percpu_counter sctp_sockets_allocated;
static void sctp_enter_memory_pressure(struct sock *sk)
{
@@ -2404,9 +2404,9 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk,
if (params.sack_delay == 0 && params.sack_freq == 0)
return 0;
} else if (optlen == sizeof(struct sctp_assoc_value)) {
- printk(KERN_WARNING "SCTP: Use of struct sctp_sack_info "
+ printk(KERN_WARNING "SCTP: Use of struct sctp_assoc_value "
"in delayed_ack socket option deprecated\n");
- printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
+ printk(KERN_WARNING "SCTP: Use struct sctp_sack_info instead\n");
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
@@ -2778,32 +2778,77 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, int op
}
/*
- * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
- *
- * This socket option specifies the maximum size to put in any outgoing
- * SCTP chunk. If a message is larger than this size it will be
+ * 8.1.16. Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
+ * This option will get or set the maximum size to put in any outgoing
+ * SCTP DATA chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
- * the user.
+ * the user. The default value for this option is '0' which indicates
+ * the user is NOT limiting fragmentation and only the PMTU will effect
+ * SCTP's choice of DATA chunk size. Note also that values set larger
+ * than the maximum size of an IP datagram will effectively let SCTP
+ * control fragmentation (i.e. the same as setting this option to 0).
+ *
+ * The following structure is used to access and modify this parameter:
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id: This parameter is ignored for one-to-one style sockets.
+ * For one-to-many style sockets this parameter indicates which
+ * association the user is performing an action upon. Note that if
+ * this field's value is zero then the endpoints default value is
+ * changed (effecting future associations only).
+ * assoc_value: This parameter specifies the maximum size in bytes.
*/
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optlen)
{
+ struct sctp_assoc_value params;
struct sctp_association *asoc;
struct sctp_sock *sp = sctp_sk(sk);
int val;
- if (optlen < sizeof(int))
+ if (optlen == sizeof(int)) {
+ printk(KERN_WARNING
+ "SCTP: Use of int in maxseg socket option deprecated\n");
+ printk(KERN_WARNING
+ "SCTP: Use struct sctp_assoc_value instead\n");
+ if (copy_from_user(&val, optval, optlen))
+ return -EFAULT;
+ params.assoc_id = 0;
+ } else if (optlen == sizeof(struct sctp_assoc_value)) {
+ if (copy_from_user(&params, optval, optlen))
+ return -EFAULT;
+ val = params.assoc_value;
+ } else
return -EINVAL;
- if (get_user(val, (int __user *)optval))
- return -EFAULT;
+
if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)))
return -EINVAL;
- sp->user_frag = val;
- /* Update the frag_point of the existing associations. */
- list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
- asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc) {
+ if (val == 0) {
+ val = asoc->pathmtu;
+ val -= sp->pf->af->net_header_len;
+ val -= sizeof(struct sctphdr) +
+ sizeof(struct sctp_data_chunk);
+ }
+
+ asoc->frag_point = val;
+ } else {
+ sp->user_frag = val;
+
+ /* Update the frag_point of the existing associations. */
+ list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
+ asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
+ }
}
return 0;
@@ -2965,14 +3010,21 @@ static int sctp_setsockopt_fragment_interleave(struct sock *sk,
}
/*
- * 7.1.25. Set or Get the sctp partial delivery point
+ * 8.1.21. Set or Get the SCTP Partial Delivery Point
* (SCTP_PARTIAL_DELIVERY_POINT)
+ *
* This option will set or get the SCTP partial delivery point. This
* point is the size of a message where the partial delivery API will be
* invoked to help free up rwnd space for the peer. Setting this to a
- * lower value will cause partial delivery's to happen more often. The
+ * lower value will cause partial deliveries to happen more often. The
* calls argument is an integer that sets or gets the partial delivery
- * point.
+ * point. Note also that the call will fail if the user attempts to set
+ * this value larger than the socket receive buffer size.
+ *
+ * Note that any single message having a length smaller than or equal to
+ * the SCTP partial delivery point will be delivered in one single read
+ * call as long as the user provided buffer is large enough to hold the
+ * message.
*/
static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
char __user *optval,
@@ -2985,6 +3037,12 @@ static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
if (get_user(val, (int __user *)optval))
return -EFAULT;
+ /* Note: We double the receive buffer from what the user sets
+ * it to be, also initial rwnd is based on rcvbuf/2.
+ */
+ if (val > (sk->sk_rcvbuf >> 1))
+ return -EINVAL;
+
sctp_sk(sk)->pd_point = val;
return 0; /* is this the right error code? */
@@ -3613,7 +3671,12 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->hmac = NULL;
SCTP_DBG_OBJCNT_INC(sock);
- atomic_inc(&sctp_sockets_allocated);
+ percpu_counter_inc(&sctp_sockets_allocated);
+
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ local_bh_enable();
+
return 0;
}
@@ -3627,7 +3690,10 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
- atomic_dec(&sctp_sockets_allocated);
+ percpu_counter_dec(&sctp_sockets_allocated);
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ local_bh_enable();
}
/* API 4.1.7 shutdown() - TCP Style Syntax
@@ -4168,9 +4234,9 @@ static int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
if (copy_from_user(&params, optval, len))
return -EFAULT;
} else if (len == sizeof(struct sctp_assoc_value)) {
- printk(KERN_WARNING "SCTP: Use of struct sctp_sack_info "
+ printk(KERN_WARNING "SCTP: Use of struct sctp_assoc_value "
"in delayed_ack socket option deprecated\n");
- printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
+ printk(KERN_WARNING "SCTP: Use struct sctp_sack_info instead\n");
if (copy_from_user(&params, optval, len))
return -EFAULT;
} else
@@ -5092,30 +5158,69 @@ static int sctp_getsockopt_context(struct sock *sk, int len,
}
/*
- * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
- *
- * This socket option specifies the maximum size to put in any outgoing
- * SCTP chunk. If a message is larger than this size it will be
+ * 8.1.16. Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
+ * This option will get or set the maximum size to put in any outgoing
+ * SCTP DATA chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
- * the user.
+ * the user. The default value for this option is '0' which indicates
+ * the user is NOT limiting fragmentation and only the PMTU will effect
+ * SCTP's choice of DATA chunk size. Note also that values set larger
+ * than the maximum size of an IP datagram will effectively let SCTP
+ * control fragmentation (i.e. the same as setting this option to 0).
+ *
+ * The following structure is used to access and modify this parameter:
+ *
+ * struct sctp_assoc_value {
+ * sctp_assoc_t assoc_id;
+ * uint32_t assoc_value;
+ * };
+ *
+ * assoc_id: This parameter is ignored for one-to-one style sockets.
+ * For one-to-many style sockets this parameter indicates which
+ * association the user is performing an action upon. Note that if
+ * this field's value is zero then the endpoints default value is
+ * changed (effecting future associations only).
+ * assoc_value: This parameter specifies the maximum size in bytes.
*/
static int sctp_getsockopt_maxseg(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
- int val;
+ struct sctp_assoc_value params;
+ struct sctp_association *asoc;
- if (len < sizeof(int))
+ if (len == sizeof(int)) {
+ printk(KERN_WARNING
+ "SCTP: Use of int in maxseg socket option deprecated\n");
+ printk(KERN_WARNING
+ "SCTP: Use struct sctp_assoc_value instead\n");
+ params.assoc_id = 0;
+ } else if (len >= sizeof(struct sctp_assoc_value)) {
+ len = sizeof(struct sctp_assoc_value);
+ if (copy_from_user(&params, optval, sizeof(params)))
+ return -EFAULT;
+ } else
return -EINVAL;
- len = sizeof(int);
+ asoc = sctp_id2assoc(sk, params.assoc_id);
+ if (!asoc && params.assoc_id && sctp_style(sk, UDP))
+ return -EINVAL;
+
+ if (asoc)
+ params.assoc_value = asoc->frag_point;
+ else
+ params.assoc_value = sctp_sk(sk)->user_frag;
- val = sctp_sk(sk)->user_frag;
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &val, len))
- return -EFAULT;
+ if (len == sizeof(int)) {
+ if (copy_to_user(optval, &params.assoc_value, len))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(optval, &params, len))
+ return -EFAULT;
+ }
return 0;
}
@@ -5368,6 +5473,38 @@ num:
return 0;
}
+/*
+ * 8.2.5. Get the Current Number of Associations (SCTP_GET_ASSOC_NUMBER)
+ * This option gets the current number of associations that are attached
+ * to a one-to-many style socket. The option value is an uint32_t.
+ */
+static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ struct sctp_sock *sp = sctp_sk(sk);
+ struct sctp_association *asoc;
+ u32 val = 0;
+
+ if (sctp_style(sk, TCP))
+ return -EOPNOTSUPP;
+
+ if (len < sizeof(u32))
+ return -EINVAL;
+
+ len = sizeof(u32);
+
+ list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
+ val++;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
@@ -5510,6 +5647,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
optlen);
break;
+ case SCTP_GET_ASSOC_NUMBER:
+ retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 52910697e104..f58e994e6852 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -63,8 +63,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rto_initial,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &timer_max
},
@@ -74,8 +74,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rto_min,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &timer_max
},
@@ -85,8 +85,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rto_max,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &timer_max
},
@@ -96,8 +96,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_valid_cookie_life,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &timer_max
},
@@ -107,8 +107,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_max_burst,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &zero,
.extra2 = &int_max
},
@@ -118,8 +118,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_max_retrans_association,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &int_max
},
@@ -129,8 +129,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_sndbuf_policy,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_RCVBUF_POLICY,
@@ -138,8 +138,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rcvbuf_policy,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_PATH_MAX_RETRANS,
@@ -147,8 +147,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_max_retrans_path,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &int_max
},
@@ -158,8 +158,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_max_retrans_init,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &int_max
},
@@ -169,8 +169,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_hb_interval,
.maxlen = sizeof(unsigned int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &one,
.extra2 = &timer_max
},
@@ -180,8 +180,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_cookie_preserve_enable,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_RTO_ALPHA,
@@ -189,8 +189,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rto_alpha,
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_RTO_BETA,
@@ -198,8 +198,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_rto_beta,
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_ADDIP_ENABLE,
@@ -207,8 +207,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_addip_enable,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_PRSCTP_ENABLE,
@@ -216,8 +216,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_prsctp_enable,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = NET_SCTP_SACK_TIMEOUT,
@@ -225,8 +225,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_sack_timeout,
.maxlen = sizeof(long),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &sack_timer_min,
.extra2 = &sack_timer_max,
},
@@ -236,7 +236,7 @@ static ctl_table sctp_table[] = {
.data = &sysctl_sctp_mem,
.maxlen = sizeof(sysctl_sctp_mem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -244,7 +244,7 @@ static ctl_table sctp_table[] = {
.data = &sysctl_sctp_rmem,
.maxlen = sizeof(sysctl_sctp_rmem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -252,7 +252,7 @@ static ctl_table sctp_table[] = {
.data = &sysctl_sctp_wmem,
.maxlen = sizeof(sysctl_sctp_wmem),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -260,8 +260,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_auth_enable,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{
.ctl_name = CTL_UNNUMBERED,
@@ -269,8 +269,8 @@ static ctl_table sctp_table[] = {
.data = &sctp_addip_noauth,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
- .strategy = &sysctl_intvec
+ .proc_handler = proc_dointvec,
+ .strategy = sysctl_intvec
},
{ .ctl_name = 0 }
};
diff --git a/net/socket.c b/net/socket.c
index 92764d836891..2c730fc718ab 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -69,7 +69,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
-#include <linux/thread_info.h>
#include <linux/wanrouter.h>
#include <linux/if_bridge.h>
#include <linux/if_frad.h>
@@ -491,8 +490,8 @@ static struct socket *sock_alloc(void)
sock = SOCKET_I(inode);
inode->i_mode = S_IFSOCK | S_IRWXUGO;
- inode->i_uid = current->fsuid;
- inode->i_gid = current->fsgid;
+ inode->i_uid = current_fsuid();
+ inode->i_gid = current_fsgid();
get_cpu_var(sockets_in_use)++;
put_cpu_var(sockets_in_use);
@@ -2307,6 +2306,7 @@ int kernel_accept(struct socket *sock, struct socket **newsock, int flags)
}
(*newsock)->ops = sock->ops;
+ __module_get((*newsock)->ops->owner);
done:
return err;
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index cb216b2df666..0c431c277af5 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -234,7 +234,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
/* Enforce a 60 second garbage collection moratorium */
- if (time_in_range(cred->cr_expire, expired, jiffies) &&
+ if (time_in_range_open(cred->cr_expire, expired, jiffies) &&
test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
continue;
@@ -350,16 +350,18 @@ EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
struct rpc_cred *
rpcauth_lookupcred(struct rpc_auth *auth, int flags)
{
- struct auth_cred acred = {
- .uid = current->fsuid,
- .gid = current->fsgid,
- .group_info = current->group_info,
- };
+ struct auth_cred acred;
struct rpc_cred *ret;
+ const struct cred *cred = current_cred();
dprintk("RPC: looking up %s cred\n",
auth->au_ops->au_name);
- get_group_info(acred.group_info);
+
+ memset(&acred, 0, sizeof(acred));
+ acred.uid = cred->fsuid;
+ acred.gid = cred->fsgid;
+ acred.group_info = get_group_info(((struct cred *)cred)->group_info);
+
ret = auth->au_ops->lookup_cred(auth, &acred, flags);
put_group_info(acred.group_info);
return ret;
@@ -513,7 +515,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
if (cred->cr_ops->crwrap_req)
return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
/* By default, we encode the arguments normally. */
- return rpc_call_xdrproc(encode, rqstp, data, obj);
+ return encode(rqstp, data, obj);
}
int
@@ -528,7 +530,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
data, obj);
/* By default, we decode the arguments normally. */
- return rpc_call_xdrproc(decode, rqstp, data, obj);
+ return decode(rqstp, data, obj);
}
int
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 853a4142cea1..e630b38a6047 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -72,11 +72,25 @@ struct gss_auth {
struct gss_api_mech *mech;
enum rpc_gss_svc service;
struct rpc_clnt *client;
- struct dentry *dentry;
+ /*
+ * There are two upcall pipes; dentry[1], named "gssd", is used
+ * for the new text-based upcall; dentry[0] is named after the
+ * mechanism (for example, "krb5") and exists for
+ * backwards-compatibility with older gssd's.
+ */
+ struct dentry *dentry[2];
};
+/* pipe_version >= 0 if and only if someone has a pipe open. */
+static int pipe_version = -1;
+static atomic_t pipe_users = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(pipe_version_lock);
+static struct rpc_wait_queue pipe_version_rpc_waitqueue;
+static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
+
static void gss_free_ctx(struct gss_cl_ctx *);
-static struct rpc_pipe_ops gss_upcall_ops;
+static struct rpc_pipe_ops gss_upcall_ops_v0;
+static struct rpc_pipe_ops gss_upcall_ops_v1;
static inline struct gss_cl_ctx *
gss_get_ctx(struct gss_cl_ctx *ctx)
@@ -220,6 +234,7 @@ err:
return p;
}
+#define UPCALL_BUF_LEN 128
struct gss_upcall_msg {
atomic_t count;
@@ -227,16 +242,41 @@ struct gss_upcall_msg {
struct rpc_pipe_msg msg;
struct list_head list;
struct gss_auth *auth;
+ struct rpc_inode *inode;
struct rpc_wait_queue rpc_waitqueue;
wait_queue_head_t waitqueue;
struct gss_cl_ctx *ctx;
+ char databuf[UPCALL_BUF_LEN];
};
+static int get_pipe_version(void)
+{
+ int ret;
+
+ spin_lock(&pipe_version_lock);
+ if (pipe_version >= 0) {
+ atomic_inc(&pipe_users);
+ ret = pipe_version;
+ } else
+ ret = -EAGAIN;
+ spin_unlock(&pipe_version_lock);
+ return ret;
+}
+
+static void put_pipe_version(void)
+{
+ if (atomic_dec_and_lock(&pipe_users, &pipe_version_lock)) {
+ pipe_version = -1;
+ spin_unlock(&pipe_version_lock);
+ }
+}
+
static void
gss_release_msg(struct gss_upcall_msg *gss_msg)
{
if (!atomic_dec_and_test(&gss_msg->count))
return;
+ put_pipe_version();
BUG_ON(!list_empty(&gss_msg->list));
if (gss_msg->ctx != NULL)
gss_put_ctx(gss_msg->ctx);
@@ -266,8 +306,8 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
static inline struct gss_upcall_msg *
gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg)
{
- struct inode *inode = gss_auth->dentry->d_inode;
- struct rpc_inode *rpci = RPC_I(inode);
+ struct rpc_inode *rpci = gss_msg->inode;
+ struct inode *inode = &rpci->vfs_inode;
struct gss_upcall_msg *old;
spin_lock(&inode->i_lock);
@@ -293,8 +333,7 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
static void
gss_unhash_msg(struct gss_upcall_msg *gss_msg)
{
- struct gss_auth *gss_auth = gss_msg->auth;
- struct inode *inode = gss_auth->dentry->d_inode;
+ struct inode *inode = &gss_msg->inode->vfs_inode;
if (list_empty(&gss_msg->list))
return;
@@ -310,7 +349,7 @@ gss_upcall_callback(struct rpc_task *task)
struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred,
struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
- struct inode *inode = gss_msg->auth->dentry->d_inode;
+ struct inode *inode = &gss_msg->inode->vfs_inode;
spin_lock(&inode->i_lock);
if (gss_msg->ctx)
@@ -323,22 +362,75 @@ gss_upcall_callback(struct rpc_task *task)
gss_release_msg(gss_msg);
}
+static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
+{
+ gss_msg->msg.data = &gss_msg->uid;
+ gss_msg->msg.len = sizeof(gss_msg->uid);
+}
+
+static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
+ struct rpc_clnt *clnt, int machine_cred)
+{
+ char *p = gss_msg->databuf;
+ int len = 0;
+
+ gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ",
+ gss_msg->auth->mech->gm_name,
+ gss_msg->uid);
+ p += gss_msg->msg.len;
+ if (clnt->cl_principal) {
+ len = sprintf(p, "target=%s ", clnt->cl_principal);
+ p += len;
+ gss_msg->msg.len += len;
+ }
+ if (machine_cred) {
+ len = sprintf(p, "service=* ");
+ p += len;
+ gss_msg->msg.len += len;
+ } else if (!strcmp(clnt->cl_program->name, "nfs4_cb")) {
+ len = sprintf(p, "service=nfs ");
+ p += len;
+ gss_msg->msg.len += len;
+ }
+ len = sprintf(p, "\n");
+ gss_msg->msg.len += len;
+
+ gss_msg->msg.data = gss_msg->databuf;
+ BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);
+}
+
+static void gss_encode_msg(struct gss_upcall_msg *gss_msg,
+ struct rpc_clnt *clnt, int machine_cred)
+{
+ if (pipe_version == 0)
+ gss_encode_v0_msg(gss_msg);
+ else /* pipe_version == 1 */
+ gss_encode_v1_msg(gss_msg, clnt, machine_cred);
+}
+
static inline struct gss_upcall_msg *
-gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid)
+gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid, struct rpc_clnt *clnt,
+ int machine_cred)
{
struct gss_upcall_msg *gss_msg;
+ int vers;
gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
- if (gss_msg != NULL) {
- INIT_LIST_HEAD(&gss_msg->list);
- rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
- init_waitqueue_head(&gss_msg->waitqueue);
- atomic_set(&gss_msg->count, 1);
- gss_msg->msg.data = &gss_msg->uid;
- gss_msg->msg.len = sizeof(gss_msg->uid);
- gss_msg->uid = uid;
- gss_msg->auth = gss_auth;
+ if (gss_msg == NULL)
+ return ERR_PTR(-ENOMEM);
+ vers = get_pipe_version();
+ if (vers < 0) {
+ kfree(gss_msg);
+ return ERR_PTR(vers);
}
+ gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode);
+ INIT_LIST_HEAD(&gss_msg->list);
+ rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
+ init_waitqueue_head(&gss_msg->waitqueue);
+ atomic_set(&gss_msg->count, 1);
+ gss_msg->uid = uid;
+ gss_msg->auth = gss_auth;
+ gss_encode_msg(gss_msg, clnt, machine_cred);
return gss_msg;
}
@@ -350,16 +442,13 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
struct gss_upcall_msg *gss_new, *gss_msg;
uid_t uid = cred->cr_uid;
- /* Special case: rpc.gssd assumes that uid == 0 implies machine creds */
- if (gss_cred->gc_machine_cred != 0)
- uid = 0;
-
- gss_new = gss_alloc_msg(gss_auth, uid);
- if (gss_new == NULL)
- return ERR_PTR(-ENOMEM);
+ gss_new = gss_alloc_msg(gss_auth, uid, clnt, gss_cred->gc_machine_cred);
+ if (IS_ERR(gss_new))
+ return gss_new;
gss_msg = gss_add_msg(gss_auth, gss_new);
if (gss_msg == gss_new) {
- int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg);
+ struct inode *inode = &gss_new->inode->vfs_inode;
+ int res = rpc_queue_upcall(inode, &gss_new->msg);
if (res) {
gss_unhash_msg(gss_new);
gss_msg = ERR_PTR(res);
@@ -369,6 +458,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
return gss_msg;
}
+static void warn_gssd(void)
+{
+ static unsigned long ratelimit;
+ unsigned long now = jiffies;
+
+ if (time_after(now, ratelimit)) {
+ printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
+ "Please check user daemon is running.\n");
+ ratelimit = now + 15*HZ;
+ }
+}
+
static inline int
gss_refresh_upcall(struct rpc_task *task)
{
@@ -378,16 +479,25 @@ gss_refresh_upcall(struct rpc_task *task)
struct gss_cred *gss_cred = container_of(cred,
struct gss_cred, gc_base);
struct gss_upcall_msg *gss_msg;
- struct inode *inode = gss_auth->dentry->d_inode;
+ struct inode *inode;
int err = 0;
dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid,
cred->cr_uid);
gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred);
+ if (IS_ERR(gss_msg) == -EAGAIN) {
+ /* XXX: warning on the first, under the assumption we
+ * shouldn't normally hit this case on a refresh. */
+ warn_gssd();
+ task->tk_timeout = 15*HZ;
+ rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL);
+ return 0;
+ }
if (IS_ERR(gss_msg)) {
err = PTR_ERR(gss_msg);
goto out;
}
+ inode = &gss_msg->inode->vfs_inode;
spin_lock(&inode->i_lock);
if (gss_cred->gc_upcall != NULL)
rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
@@ -414,18 +524,29 @@ out:
static inline int
gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
{
- struct inode *inode = gss_auth->dentry->d_inode;
+ struct inode *inode;
struct rpc_cred *cred = &gss_cred->gc_base;
struct gss_upcall_msg *gss_msg;
DEFINE_WAIT(wait);
int err = 0;
dprintk("RPC: gss_upcall for uid %u\n", cred->cr_uid);
+retry:
gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred);
+ if (PTR_ERR(gss_msg) == -EAGAIN) {
+ err = wait_event_interruptible_timeout(pipe_version_waitqueue,
+ pipe_version >= 0, 15*HZ);
+ if (err)
+ goto out;
+ if (pipe_version < 0)
+ warn_gssd();
+ goto retry;
+ }
if (IS_ERR(gss_msg)) {
err = PTR_ERR(gss_msg);
goto out;
}
+ inode = &gss_msg->inode->vfs_inode;
for (;;) {
prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE);
spin_lock(&inode->i_lock);
@@ -543,6 +664,38 @@ out:
return err;
}
+static int gss_pipe_open(struct inode *inode, int new_version)
+{
+ int ret = 0;
+
+ spin_lock(&pipe_version_lock);
+ if (pipe_version < 0) {
+ /* First open of any gss pipe determines the version: */
+ pipe_version = new_version;
+ rpc_wake_up(&pipe_version_rpc_waitqueue);
+ wake_up(&pipe_version_waitqueue);
+ } else if (pipe_version != new_version) {
+ /* Trying to open a pipe of a different version */
+ ret = -EBUSY;
+ goto out;
+ }
+ atomic_inc(&pipe_users);
+out:
+ spin_unlock(&pipe_version_lock);
+ return ret;
+
+}
+
+static int gss_pipe_open_v0(struct inode *inode)
+{
+ return gss_pipe_open(inode, 0);
+}
+
+static int gss_pipe_open_v1(struct inode *inode)
+{
+ return gss_pipe_open(inode, 1);
+}
+
static void
gss_pipe_release(struct inode *inode)
{
@@ -562,27 +715,22 @@ gss_pipe_release(struct inode *inode)
spin_lock(&inode->i_lock);
}
spin_unlock(&inode->i_lock);
+
+ put_pipe_version();
}
static void
gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
- static unsigned long ratelimit;
if (msg->errno < 0) {
dprintk("RPC: gss_pipe_destroy_msg releasing msg %p\n",
gss_msg);
atomic_inc(&gss_msg->count);
gss_unhash_msg(gss_msg);
- if (msg->errno == -ETIMEDOUT) {
- unsigned long now = jiffies;
- if (time_after(now, ratelimit)) {
- printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
- "Please check user daemon is running!\n");
- ratelimit = now + 15*HZ;
- }
- }
+ if (msg->errno == -ETIMEDOUT)
+ warn_gssd();
gss_release_msg(gss_msg);
}
}
@@ -623,20 +771,38 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
atomic_set(&auth->au_count, 1);
kref_init(&gss_auth->kref);
- gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
- clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
- if (IS_ERR(gss_auth->dentry)) {
- err = PTR_ERR(gss_auth->dentry);
+ /*
+ * Note: if we created the old pipe first, then someone who
+ * examined the directory at the right moment might conclude
+ * that we supported only the old pipe. So we instead create
+ * the new pipe first.
+ */
+ gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_dentry,
+ "gssd",
+ clnt, &gss_upcall_ops_v1,
+ RPC_PIPE_WAIT_FOR_OPEN);
+ if (IS_ERR(gss_auth->dentry[1])) {
+ err = PTR_ERR(gss_auth->dentry[1]);
goto err_put_mech;
}
+ gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_dentry,
+ gss_auth->mech->gm_name,
+ clnt, &gss_upcall_ops_v0,
+ RPC_PIPE_WAIT_FOR_OPEN);
+ if (IS_ERR(gss_auth->dentry[0])) {
+ err = PTR_ERR(gss_auth->dentry[0]);
+ goto err_unlink_pipe_1;
+ }
err = rpcauth_init_credcache(auth);
if (err)
- goto err_unlink_pipe;
+ goto err_unlink_pipe_0;
return auth;
-err_unlink_pipe:
- rpc_unlink(gss_auth->dentry);
+err_unlink_pipe_0:
+ rpc_unlink(gss_auth->dentry[0]);
+err_unlink_pipe_1:
+ rpc_unlink(gss_auth->dentry[1]);
err_put_mech:
gss_mech_put(gss_auth->mech);
err_free:
@@ -649,8 +815,8 @@ out_dec:
static void
gss_free(struct gss_auth *gss_auth)
{
- rpc_unlink(gss_auth->dentry);
- gss_auth->dentry = NULL;
+ rpc_unlink(gss_auth->dentry[1]);
+ rpc_unlink(gss_auth->dentry[0]);
gss_mech_put(gss_auth->mech);
kfree(gss_auth);
@@ -693,7 +859,7 @@ gss_destroying_context(struct rpc_cred *cred)
struct rpc_task *task;
if (gss_cred->gc_ctx == NULL ||
- test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
+ test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
return 0;
gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
@@ -757,14 +923,12 @@ gss_free_cred_callback(struct rcu_head *head)
}
static void
-gss_destroy_cred(struct rpc_cred *cred)
+gss_destroy_nullcred(struct rpc_cred *cred)
{
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
- if (gss_destroying_context(cred))
- return;
rcu_assign_pointer(gss_cred->gc_ctx, NULL);
call_rcu(&cred->cr_rcu, gss_free_cred_callback);
if (ctx)
@@ -772,6 +936,15 @@ gss_destroy_cred(struct rpc_cred *cred)
kref_put(&gss_auth->kref, gss_free_callback);
}
+static void
+gss_destroy_cred(struct rpc_cred *cred)
+{
+
+ if (gss_destroying_context(cred))
+ return;
+ gss_destroy_nullcred(cred);
+}
+
/*
* Lookup RPCSEC_GSS cred for the current process
*/
@@ -1017,7 +1190,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno);
- status = rpc_call_xdrproc(encode, rqstp, p, obj);
+ status = encode(rqstp, p, obj);
if (status)
return status;
@@ -1111,7 +1284,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
*p++ = htonl(rqstp->rq_seqno);
- status = rpc_call_xdrproc(encode, rqstp, p, obj);
+ status = encode(rqstp, p, obj);
if (status)
return status;
@@ -1170,12 +1343,12 @@ gss_wrap_req(struct rpc_task *task,
/* The spec seems a little ambiguous here, but I think that not
* wrapping context destruction requests makes the most sense.
*/
- status = rpc_call_xdrproc(encode, rqstp, p, obj);
+ status = encode(rqstp, p, obj);
goto out;
}
switch (gss_cred->gc_service) {
case RPC_GSS_SVC_NONE:
- status = rpc_call_xdrproc(encode, rqstp, p, obj);
+ status = encode(rqstp, p, obj);
break;
case RPC_GSS_SVC_INTEGRITY:
status = gss_wrap_req_integ(cred, ctx, encode,
@@ -1291,7 +1464,7 @@ gss_unwrap_resp(struct rpc_task *task,
cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
+ (savedlen - head->iov_len);
out_decode:
- status = rpc_call_xdrproc(decode, rqstp, p, obj);
+ status = decode(rqstp, p, obj);
out:
gss_put_ctx(ctx);
dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
@@ -1324,7 +1497,7 @@ static const struct rpc_credops gss_credops = {
static const struct rpc_credops gss_nullops = {
.cr_name = "AUTH_GSS",
- .crdestroy = gss_destroy_cred,
+ .crdestroy = gss_destroy_nullcred,
.crbind = rpcauth_generic_bind_cred,
.crmatch = gss_match,
.crmarshal = gss_marshal,
@@ -1334,10 +1507,19 @@ static const struct rpc_credops gss_nullops = {
.crunwrap_resp = gss_unwrap_resp,
};
-static struct rpc_pipe_ops gss_upcall_ops = {
+static struct rpc_pipe_ops gss_upcall_ops_v0 = {
+ .upcall = gss_pipe_upcall,
+ .downcall = gss_pipe_downcall,
+ .destroy_msg = gss_pipe_destroy_msg,
+ .open_pipe = gss_pipe_open_v0,
+ .release_pipe = gss_pipe_release,
+};
+
+static struct rpc_pipe_ops gss_upcall_ops_v1 = {
.upcall = gss_pipe_upcall,
.downcall = gss_pipe_downcall,
.destroy_msg = gss_pipe_destroy_msg,
+ .open_pipe = gss_pipe_open_v1,
.release_pipe = gss_pipe_release,
};
@@ -1354,6 +1536,7 @@ static int __init init_rpcsec_gss(void)
err = gss_svc_init();
if (err)
goto out_unregister;
+ rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
return 0;
out_unregister:
rpcauth_unregister(&authgss_ops);
diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c
index d83b881685fe..c0ba39c4f5f2 100644
--- a/net/sunrpc/auth_gss/gss_generic_token.c
+++ b/net/sunrpc/auth_gss/gss_generic_token.c
@@ -152,7 +152,7 @@ g_token_size(struct xdr_netobj *mech, unsigned int body_size)
return(1 + der_length_size(body_size) + body_size);
}
-EXPORT_SYMBOL(g_token_size);
+EXPORT_SYMBOL_GPL(g_token_size);
/* fills in a buffer with the token header. The buffer is assumed to
be the right size. buf is advanced past the token header */
@@ -167,7 +167,7 @@ g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf)
TWRITE_STR(*buf, mech->data, ((int) mech->len));
}
-EXPORT_SYMBOL(g_make_token_header);
+EXPORT_SYMBOL_GPL(g_make_token_header);
/*
* Given a buffer containing a token, reads and verifies the token,
@@ -231,5 +231,5 @@ g_verify_token_header(struct xdr_netobj *mech, int *body_size,
return(ret);
}
-EXPORT_SYMBOL(g_verify_token_header);
+EXPORT_SYMBOL_GPL(g_verify_token_header);
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index bce9d527af08..6efbb0cd3c7c 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -117,7 +117,7 @@ gss_mech_register(struct gss_api_mech *gm)
return 0;
}
-EXPORT_SYMBOL(gss_mech_register);
+EXPORT_SYMBOL_GPL(gss_mech_register);
void
gss_mech_unregister(struct gss_api_mech *gm)
@@ -129,7 +129,7 @@ gss_mech_unregister(struct gss_api_mech *gm)
gss_mech_free(gm);
}
-EXPORT_SYMBOL(gss_mech_unregister);
+EXPORT_SYMBOL_GPL(gss_mech_unregister);
struct gss_api_mech *
gss_mech_get(struct gss_api_mech *gm)
@@ -138,7 +138,7 @@ gss_mech_get(struct gss_api_mech *gm)
return gm;
}
-EXPORT_SYMBOL(gss_mech_get);
+EXPORT_SYMBOL_GPL(gss_mech_get);
struct gss_api_mech *
gss_mech_get_by_name(const char *name)
@@ -158,7 +158,7 @@ gss_mech_get_by_name(const char *name)
}
-EXPORT_SYMBOL(gss_mech_get_by_name);
+EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
static inline int
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -191,7 +191,7 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
return gm;
}
-EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
+EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
u32
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
@@ -205,7 +205,7 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
}
return RPC_AUTH_MAXFLAVOR; /* illegal value */
}
-EXPORT_SYMBOL(gss_svc_to_pseudoflavor);
+EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -219,7 +219,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
return 0;
}
-EXPORT_SYMBOL(gss_pseudoflavor_to_service);
+EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
char *
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
@@ -233,7 +233,7 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
return NULL;
}
-EXPORT_SYMBOL(gss_service_to_auth_domain_name);
+EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
void
gss_mech_put(struct gss_api_mech * gm)
@@ -242,7 +242,7 @@ gss_mech_put(struct gss_api_mech * gm)
module_put(gm->gm_owner);
}
-EXPORT_SYMBOL(gss_mech_put);
+EXPORT_SYMBOL_GPL(gss_mech_put);
/* The mech could probably be determined from the token instead, but it's just
* as easy for now to pass it in. */
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 81ae3d62a0cc..2278a50c6444 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -332,6 +332,7 @@ struct rsc {
struct svc_cred cred;
struct gss_svc_seq_data seqdata;
struct gss_ctx *mechctx;
+ char *client_name;
};
static struct cache_head *rsc_table[RSC_HASHMAX];
@@ -346,6 +347,7 @@ static void rsc_free(struct rsc *rsci)
gss_delete_sec_context(&rsci->mechctx);
if (rsci->cred.cr_group_info)
put_group_info(rsci->cred.cr_group_info);
+ kfree(rsci->client_name);
}
static void rsc_put(struct kref *ref)
@@ -383,6 +385,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
tmp->handle.data = NULL;
new->mechctx = NULL;
new->cred.cr_group_info = NULL;
+ new->client_name = NULL;
}
static void
@@ -397,6 +400,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
spin_lock_init(&new->seqdata.sd_lock);
new->cred = tmp->cred;
tmp->cred.cr_group_info = NULL;
+ new->client_name = tmp->client_name;
+ tmp->client_name = NULL;
}
static struct cache_head *
@@ -486,6 +491,15 @@ static int rsc_parse(struct cache_detail *cd,
status = gss_import_sec_context(buf, len, gm, &rsci.mechctx);
if (status)
goto out;
+
+ /* get client name */
+ len = qword_get(&mesg, buf, mlen);
+ if (len > 0) {
+ rsci.client_name = kstrdup(buf, GFP_KERNEL);
+ if (!rsci.client_name)
+ goto out;
+ }
+
}
rsci.h.expiry_time = expiry;
rscp = rsc_update(&rsci, rscp);
@@ -746,7 +760,7 @@ u32 svcauth_gss_flavor(struct auth_domain *dom)
return gd->pseudoflavor;
}
-EXPORT_SYMBOL(svcauth_gss_flavor);
+EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
int
svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
@@ -780,7 +794,7 @@ out:
return stat;
}
-EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
+EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
static inline int
read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
@@ -913,6 +927,16 @@ struct gss_svc_data {
struct rsc *rsci;
};
+char *svc_gss_principal(struct svc_rqst *rqstp)
+{
+ struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data;
+
+ if (gd && gd->rsci)
+ return gd->rsci->client_name;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(svc_gss_principal);
+
static int
svcauth_gss_set_client(struct svc_rqst *rqstp)
{
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 4895c341e46d..836f15c0c4a3 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -197,6 +197,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval);
+ clnt->cl_principal = NULL;
+ if (args->client_name) {
+ clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL);
+ if (!clnt->cl_principal)
+ goto out_no_principal;
+ }
kref_init(&clnt->cl_kref);
@@ -226,6 +232,8 @@ out_no_auth:
rpc_put_mount();
}
out_no_path:
+ kfree(clnt->cl_principal);
+out_no_principal:
rpc_free_iostats(clnt->cl_metrics);
out_no_stats:
if (clnt->cl_server != clnt->cl_inline_name)
@@ -271,15 +279,15 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
case AF_INET: {
struct sockaddr_in *sin =
(struct sockaddr_in *)args->address;
- snprintf(servername, sizeof(servername), NIPQUAD_FMT,
- NIPQUAD(sin->sin_addr.s_addr));
+ snprintf(servername, sizeof(servername), "%pI4",
+ &sin->sin_addr.s_addr);
break;
}
case AF_INET6: {
struct sockaddr_in6 *sin =
(struct sockaddr_in6 *)args->address;
- snprintf(servername, sizeof(servername), NIP6_FMT,
- NIP6(sin->sin6_addr));
+ snprintf(servername, sizeof(servername), "%pI6",
+ &sin->sin6_addr);
break;
}
default:
@@ -354,6 +362,11 @@ rpc_clone_client(struct rpc_clnt *clnt)
new->cl_metrics = rpc_alloc_iostats(clnt);
if (new->cl_metrics == NULL)
goto out_no_stats;
+ if (clnt->cl_principal) {
+ new->cl_principal = kstrdup(clnt->cl_principal, GFP_KERNEL);
+ if (new->cl_principal == NULL)
+ goto out_no_principal;
+ }
kref_init(&new->cl_kref);
err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
if (err != 0)
@@ -366,6 +379,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
rpciod_up();
return new;
out_no_path:
+ kfree(new->cl_principal);
+out_no_principal:
rpc_free_iostats(new->cl_metrics);
out_no_stats:
kfree(new);
@@ -417,6 +432,7 @@ rpc_free_client(struct kref *kref)
out_free:
rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics);
+ kfree(clnt->cl_principal);
clnt->cl_metrics = NULL;
xprt_put(clnt->cl_xprt);
rpciod_down();
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 23a2b8f6dc49..192453248870 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -113,7 +113,7 @@ out:
wake_up(&rpci->waitq);
return res;
}
-EXPORT_SYMBOL(rpc_queue_upcall);
+EXPORT_SYMBOL_GPL(rpc_queue_upcall);
static inline void
rpc_inode_setowner(struct inode *inode, void *private)
@@ -126,13 +126,14 @@ rpc_close_pipes(struct inode *inode)
{
struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_ops *ops;
+ int need_release;
mutex_lock(&inode->i_mutex);
ops = rpci->ops;
if (ops != NULL) {
LIST_HEAD(free_list);
-
spin_lock(&inode->i_lock);
+ need_release = rpci->nreaders != 0 || rpci->nwriters != 0;
rpci->nreaders = 0;
list_splice_init(&rpci->in_upcall, &free_list);
list_splice_init(&rpci->pipe, &free_list);
@@ -141,7 +142,7 @@ rpc_close_pipes(struct inode *inode)
spin_unlock(&inode->i_lock);
rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE);
rpci->nwriters = 0;
- if (ops->release_pipe)
+ if (need_release && ops->release_pipe)
ops->release_pipe(inode);
cancel_delayed_work_sync(&rpci->queue_timeout);
}
@@ -169,16 +170,24 @@ static int
rpc_pipe_open(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(inode);
+ int first_open;
int res = -ENXIO;
mutex_lock(&inode->i_mutex);
- if (rpci->ops != NULL) {
- if (filp->f_mode & FMODE_READ)
- rpci->nreaders ++;
- if (filp->f_mode & FMODE_WRITE)
- rpci->nwriters ++;
- res = 0;
+ if (rpci->ops == NULL)
+ goto out;
+ first_open = rpci->nreaders == 0 && rpci->nwriters == 0;
+ if (first_open && rpci->ops->open_pipe) {
+ res = rpci->ops->open_pipe(inode);
+ if (res)
+ goto out;
}
+ if (filp->f_mode & FMODE_READ)
+ rpci->nreaders++;
+ if (filp->f_mode & FMODE_WRITE)
+ rpci->nwriters++;
+ res = 0;
+out:
mutex_unlock(&inode->i_mutex);
return res;
}
@@ -188,6 +197,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_msg *msg;
+ int last_close;
mutex_lock(&inode->i_mutex);
if (rpci->ops == NULL)
@@ -214,7 +224,8 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
rpci->ops->destroy_msg, -EAGAIN);
}
}
- if (rpci->ops->release_pipe)
+ last_close = rpci->nwriters == 0 && rpci->nreaders == 0;
+ if (last_close && rpci->ops->release_pipe)
rpci->ops->release_pipe(inode);
out:
mutex_unlock(&inode->i_mutex);
@@ -396,6 +407,7 @@ enum {
RPCAUTH_nfs,
RPCAUTH_portmap,
RPCAUTH_statd,
+ RPCAUTH_nfsd4_cb,
RPCAUTH_RootEOF
};
@@ -429,6 +441,10 @@ static struct rpc_filelist files[] = {
.name = "statd",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
+ [RPCAUTH_nfsd4_cb] = {
+ .name = "nfsd4_cb",
+ .mode = S_IFDIR | S_IRUGO | S_IXUGO,
+ },
};
enum {
@@ -748,7 +764,7 @@ rpc_rmdir(struct dentry *dentry)
* @name: name of pipe
* @private: private data to associate with the pipe, for the caller's use
* @ops: operations defining the behavior of the pipe: upcall, downcall,
- * release_pipe, and destroy_msg.
+ * release_pipe, open_pipe, and destroy_msg.
* @flags: rpc_inode flags
*
* Data is made available for userspace to read by calls to
@@ -808,7 +824,7 @@ err_dput:
-ENOMEM);
goto out;
}
-EXPORT_SYMBOL(rpc_mkpipe);
+EXPORT_SYMBOL_GPL(rpc_mkpipe);
/**
* rpc_unlink - remove a pipe
@@ -839,7 +855,7 @@ rpc_unlink(struct dentry *dentry)
dput(parent);
return error;
}
-EXPORT_SYMBOL(rpc_unlink);
+EXPORT_SYMBOL_GPL(rpc_unlink);
/*
* populate the filesystem
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 41013dd66ac3..03ae007641e4 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -270,10 +270,9 @@ static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
char buf[32];
/* Construct AF_INET universal address */
- snprintf(buf, sizeof(buf),
- NIPQUAD_FMT".%u.%u",
- NIPQUAD(address_to_register->sin_addr.s_addr),
- port >> 8, port & 0xff);
+ snprintf(buf, sizeof(buf), "%pI4.%u.%u",
+ &address_to_register->sin_addr.s_addr,
+ port >> 8, port & 0xff);
map->r_addr = buf;
dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with "
@@ -305,9 +304,9 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
snprintf(buf, sizeof(buf), "::.%u.%u",
port >> 8, port & 0xff);
else
- snprintf(buf, sizeof(buf), NIP6_FMT".%u.%u",
- NIP6(address_to_register->sin6_addr),
- port >> 8, port & 0xff);
+ snprintf(buf, sizeof(buf), "%pI6.%u.%u",
+ &address_to_register->sin6_addr,
+ port >> 8, port & 0xff);
map->r_addr = buf;
dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with "
@@ -422,8 +421,8 @@ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot)
struct rpc_clnt *rpcb_clnt;
int status;
- dprintk("RPC: %s(" NIPQUAD_FMT ", %u, %u, %d)\n",
- __func__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
+ dprintk("RPC: %s(%pI4, %u, %u, %d)\n",
+ __func__, &sin->sin_addr.s_addr, prog, vers, prot);
rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin,
sizeof(*sin), prot, RPCBVERS_2);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index f24800f2c098..82240e6127b2 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -162,13 +162,9 @@ static void ip_map_request(struct cache_detail *cd,
struct ip_map *im = container_of(h, struct ip_map, h);
if (ipv6_addr_v4mapped(&(im->m_addr))) {
- snprintf(text_addr, 20, NIPQUAD_FMT,
- ntohl(im->m_addr.s6_addr32[3]) >> 24 & 0xff,
- ntohl(im->m_addr.s6_addr32[3]) >> 16 & 0xff,
- ntohl(im->m_addr.s6_addr32[3]) >> 8 & 0xff,
- ntohl(im->m_addr.s6_addr32[3]) >> 0 & 0xff);
+ snprintf(text_addr, 20, "%pI4", &im->m_addr.s6_addr32[3]);
} else {
- snprintf(text_addr, 40, NIP6_FMT, NIP6(im->m_addr));
+ snprintf(text_addr, 40, "%pI6", &im->m_addr);
}
qword_add(bpp, blen, im->m_class);
qword_add(bpp, blen, text_addr);
@@ -208,13 +204,13 @@ static int ip_map_parse(struct cache_detail *cd,
len = qword_get(&mesg, buf, mlen);
if (len <= 0) return -EINVAL;
- if (sscanf(buf, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) == 4) {
+ if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) == 4) {
addr.s6_addr32[0] = 0;
addr.s6_addr32[1] = 0;
addr.s6_addr32[2] = htonl(0xffff);
addr.s6_addr32[3] =
htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
- } else if (sscanf(buf, NIP6_FMT "%c",
+ } else if (sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x%c",
&b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &c) == 8) {
addr.s6_addr16[0] = htons(b1);
addr.s6_addr16[1] = htons(b2);
@@ -278,16 +274,10 @@ static int ip_map_show(struct seq_file *m,
dom = im->m_client->h.name;
if (ipv6_addr_v4mapped(&addr)) {
- seq_printf(m, "%s " NIPQUAD_FMT " %s\n",
- im->m_class,
- ntohl(addr.s6_addr32[3]) >> 24 & 0xff,
- ntohl(addr.s6_addr32[3]) >> 16 & 0xff,
- ntohl(addr.s6_addr32[3]) >> 8 & 0xff,
- ntohl(addr.s6_addr32[3]) >> 0 & 0xff,
- dom);
+ seq_printf(m, "%s %pI4 %s\n",
+ im->m_class, &addr.s6_addr32[3], dom);
} else {
- seq_printf(m, "%s " NIP6_FMT " %s\n",
- im->m_class, NIP6(addr), dom);
+ seq_printf(m, "%s %pI6 %s\n", im->m_class, &addr, dom);
}
return 0;
}
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index a1951dcc5776..ef3238d665ee 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -250,10 +250,10 @@ static int one_sock_name(char *buf, struct svc_sock *svsk)
switch(svsk->sk_sk->sk_family) {
case AF_INET:
- len = sprintf(buf, "ipv4 %s %u.%u.%u.%u %d\n",
- svsk->sk_sk->sk_protocol==IPPROTO_UDP?
+ len = sprintf(buf, "ipv4 %s %pI4 %d\n",
+ svsk->sk_sk->sk_protocol == IPPROTO_UDP ?
"udp" : "tcp",
- NIPQUAD(inet_sk(svsk->sk_sk)->rcv_saddr),
+ &inet_sk(svsk->sk_sk)->rcv_saddr,
inet_sk(svsk->sk_sk)->num);
break;
default:
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 79a55d56cc98..406e26de584e 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -28,7 +28,7 @@ xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
memcpy(p, obj->data, obj->len);
return p + XDR_QUADLEN(obj->len);
}
-EXPORT_SYMBOL(xdr_encode_netobj);
+EXPORT_SYMBOL_GPL(xdr_encode_netobj);
__be32 *
xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
@@ -41,7 +41,7 @@ xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
obj->data = (u8 *) p;
return p + XDR_QUADLEN(len);
}
-EXPORT_SYMBOL(xdr_decode_netobj);
+EXPORT_SYMBOL_GPL(xdr_decode_netobj);
/**
* xdr_encode_opaque_fixed - Encode fixed length opaque data
@@ -71,7 +71,7 @@ __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
}
return p;
}
-EXPORT_SYMBOL(xdr_encode_opaque_fixed);
+EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
/**
* xdr_encode_opaque - Encode variable length opaque data
@@ -86,14 +86,14 @@ __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
*p++ = htonl(nbytes);
return xdr_encode_opaque_fixed(p, ptr, nbytes);
}
-EXPORT_SYMBOL(xdr_encode_opaque);
+EXPORT_SYMBOL_GPL(xdr_encode_opaque);
__be32 *
xdr_encode_string(__be32 *p, const char *string)
{
return xdr_encode_array(p, string, strlen(string));
}
-EXPORT_SYMBOL(xdr_encode_string);
+EXPORT_SYMBOL_GPL(xdr_encode_string);
__be32 *
xdr_decode_string_inplace(__be32 *p, char **sp,
@@ -108,7 +108,7 @@ xdr_decode_string_inplace(__be32 *p, char **sp,
*sp = (char *) p;
return p + XDR_QUADLEN(len);
}
-EXPORT_SYMBOL(xdr_decode_string_inplace);
+EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
void
xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
@@ -136,7 +136,7 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
xdr->buflen += len;
xdr->len += len;
}
-EXPORT_SYMBOL(xdr_encode_pages);
+EXPORT_SYMBOL_GPL(xdr_encode_pages);
void
xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
@@ -158,7 +158,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
xdr->buflen += len;
}
-EXPORT_SYMBOL(xdr_inline_pages);
+EXPORT_SYMBOL_GPL(xdr_inline_pages);
/*
* Helper routines for doing 'memmove' like operations on a struct xdr_buf
@@ -428,7 +428,7 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
{
xdr_shrink_bufhead(buf, len);
}
-EXPORT_SYMBOL(xdr_shift_buf);
+EXPORT_SYMBOL_GPL(xdr_shift_buf);
/**
* xdr_init_encode - Initialize a struct xdr_stream for sending data.
@@ -465,7 +465,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
iov->iov_len += len;
}
}
-EXPORT_SYMBOL(xdr_init_encode);
+EXPORT_SYMBOL_GPL(xdr_init_encode);
/**
* xdr_reserve_space - Reserve buffer space for sending
@@ -492,7 +492,7 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
xdr->buf->len += nbytes;
return p;
}
-EXPORT_SYMBOL(xdr_reserve_space);
+EXPORT_SYMBOL_GPL(xdr_reserve_space);
/**
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending
@@ -527,7 +527,7 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
buf->buflen += len;
buf->len += len;
}
-EXPORT_SYMBOL(xdr_write_pages);
+EXPORT_SYMBOL_GPL(xdr_write_pages);
/**
* xdr_init_decode - Initialize an xdr_stream for decoding data.
@@ -547,7 +547,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
xdr->p = p;
xdr->end = (__be32 *)((char *)iov->iov_base + len);
}
-EXPORT_SYMBOL(xdr_init_decode);
+EXPORT_SYMBOL_GPL(xdr_init_decode);
/**
* xdr_inline_decode - Retrieve non-page XDR data to decode
@@ -569,7 +569,7 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
xdr->p = q;
return p;
}
-EXPORT_SYMBOL(xdr_inline_decode);
+EXPORT_SYMBOL_GPL(xdr_inline_decode);
/**
* xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
@@ -613,7 +613,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
xdr->p = (__be32 *)((char *)iov->iov_base + padding);
xdr->end = (__be32 *)((char *)iov->iov_base + end);
}
-EXPORT_SYMBOL(xdr_read_pages);
+EXPORT_SYMBOL_GPL(xdr_read_pages);
/**
* xdr_enter_page - decode data from the XDR page
@@ -638,7 +638,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
xdr->p = (__be32 *)(kaddr + xdr->buf->page_base);
xdr->end = (__be32 *)((char *)xdr->p + len);
}
-EXPORT_SYMBOL(xdr_enter_page);
+EXPORT_SYMBOL_GPL(xdr_enter_page);
static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
@@ -650,7 +650,7 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
buf->page_len = 0;
buf->buflen = buf->len = iov->iov_len;
}
-EXPORT_SYMBOL(xdr_buf_from_iov);
+EXPORT_SYMBOL_GPL(xdr_buf_from_iov);
/* Sets subbuf to the portion of buf of length len beginning base bytes
* from the start of buf. Returns -1 if base of length are out of bounds. */
@@ -699,7 +699,7 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf,
return -1;
return 0;
}
-EXPORT_SYMBOL(xdr_buf_subsegment);
+EXPORT_SYMBOL_GPL(xdr_buf_subsegment);
static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
{
@@ -730,7 +730,7 @@ int read_bytes_from_xdr_buf(struct xdr_buf *buf, unsigned int base, void *obj, u
__read_bytes_from_xdr_buf(&subbuf, obj, len);
return 0;
}
-EXPORT_SYMBOL(read_bytes_from_xdr_buf);
+EXPORT_SYMBOL_GPL(read_bytes_from_xdr_buf);
static void __write_bytes_to_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len)
{
@@ -774,7 +774,7 @@ xdr_decode_word(struct xdr_buf *buf, unsigned int base, u32 *obj)
*obj = ntohl(raw);
return 0;
}
-EXPORT_SYMBOL(xdr_decode_word);
+EXPORT_SYMBOL_GPL(xdr_decode_word);
int
xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
@@ -783,7 +783,7 @@ xdr_encode_word(struct xdr_buf *buf, unsigned int base, u32 obj)
return write_bytes_to_xdr_buf(buf, base, &raw, sizeof(obj));
}
-EXPORT_SYMBOL(xdr_encode_word);
+EXPORT_SYMBOL_GPL(xdr_encode_word);
/* If the netobj starting offset bytes from the start of xdr_buf is contained
* entirely in the head or the tail, set object to point to it; otherwise
@@ -821,7 +821,7 @@ int xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, unsigned in
__read_bytes_from_xdr_buf(&subbuf, obj->data, obj->len);
return 0;
}
-EXPORT_SYMBOL(xdr_buf_read_netobj);
+EXPORT_SYMBOL_GPL(xdr_buf_read_netobj);
/* Returns 0 on success, or else a negative error code. */
static int
@@ -1027,7 +1027,7 @@ xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
return xdr_xcode_array2(buf, base, desc, 0);
}
-EXPORT_SYMBOL(xdr_decode_array2);
+EXPORT_SYMBOL_GPL(xdr_decode_array2);
int
xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
@@ -1039,7 +1039,7 @@ xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
return xdr_xcode_array2(buf, base, desc, 1);
}
-EXPORT_SYMBOL(xdr_encode_array2);
+EXPORT_SYMBOL_GPL(xdr_encode_array2);
int
xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
@@ -1106,5 +1106,5 @@ xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len,
out:
return ret;
}
-EXPORT_SYMBOL(xdr_process_buf);
+EXPORT_SYMBOL_GPL(xdr_process_buf);
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index a4756576d687..629a28764da9 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -646,8 +646,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
ret = rdma_read_xdr(rdma_xprt, rmsgp, rqstp, ctxt);
if (ret > 0) {
/* read-list posted, defer until data received from client. */
- svc_xprt_received(xprt);
- return 0;
+ goto defer;
}
if (ret < 0) {
/* Post of read-list failed, free context. */
@@ -679,6 +678,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
* close bit and call svc_xprt_delete
*/
set_bit(XPT_CLOSE, &xprt->xpt_flags);
+defer:
svc_xprt_received(xprt);
return 0;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 9a7a8e7ae038..a3334e3b73cc 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -69,7 +69,7 @@
* array is only concerned with the reply we are assured that we have
* on extra page for the RPCRMDA header.
*/
-int fast_reg_xdr(struct svcxprt_rdma *xprt,
+static int fast_reg_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr,
struct svc_rdma_req_map *vec)
{
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 6fb493cbd29f..3d810e7df3fb 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -61,7 +61,7 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt);
static void rq_cq_reap(struct svcxprt_rdma *xprt);
static void sq_cq_reap(struct svcxprt_rdma *xprt);
-DECLARE_TASKLET(dto_tasklet, dto_tasklet_func, 0UL);
+static DECLARE_TASKLET(dto_tasklet, dto_tasklet_func, 0UL);
static DEFINE_SPINLOCK(dto_lock);
static LIST_HEAD(dto_xprt_q);
@@ -827,7 +827,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
struct rdma_conn_param conn_param;
struct ib_qp_init_attr qp_attr;
struct ib_device_attr devattr;
- int dma_mr_acc;
+ int uninitialized_var(dma_mr_acc);
int need_dma_mr;
int ret;
int i;
@@ -1048,21 +1048,21 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
dprintk("svcrdma: new connection %p accepted with the following "
"attributes:\n"
- " local_ip : %d.%d.%d.%d\n"
+ " local_ip : %pI4\n"
" local_port : %d\n"
- " remote_ip : %d.%d.%d.%d\n"
+ " remote_ip : %pI4\n"
" remote_port : %d\n"
" max_sge : %d\n"
" sq_depth : %d\n"
" max_requests : %d\n"
" ord : %d\n",
newxprt,
- NIPQUAD(((struct sockaddr_in *)&newxprt->sc_cm_id->
- route.addr.src_addr)->sin_addr.s_addr),
+ &((struct sockaddr_in *)&newxprt->sc_cm_id->
+ route.addr.src_addr)->sin_addr.s_addr,
ntohs(((struct sockaddr_in *)&newxprt->sc_cm_id->
route.addr.src_addr)->sin_port),
- NIPQUAD(((struct sockaddr_in *)&newxprt->sc_cm_id->
- route.addr.dst_addr)->sin_addr.s_addr),
+ &((struct sockaddr_in *)&newxprt->sc_cm_id->
+ route.addr.dst_addr)->sin_addr.s_addr,
ntohs(((struct sockaddr_in *)&newxprt->sc_cm_id->
route.addr.dst_addr)->sin_port),
newxprt->sc_max_sge,
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 9839c3d94145..1dd6123070e9 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -174,7 +174,7 @@ xprt_rdma_format_addresses(struct rpc_xprt *xprt)
buf = kzalloc(20, GFP_KERNEL);
if (buf)
- snprintf(buf, 20, NIPQUAD_FMT, NIPQUAD(addr->sin_addr.s_addr));
+ snprintf(buf, 20, "%pI4", &addr->sin_addr.s_addr);
xprt->address_strings[RPC_DISPLAY_ADDR] = buf;
buf = kzalloc(8, GFP_KERNEL);
@@ -186,8 +186,8 @@ xprt_rdma_format_addresses(struct rpc_xprt *xprt)
buf = kzalloc(48, GFP_KERNEL);
if (buf)
- snprintf(buf, 48, "addr="NIPQUAD_FMT" port=%u proto=%s",
- NIPQUAD(addr->sin_addr.s_addr),
+ snprintf(buf, 48, "addr=%pI4 port=%u proto=%s",
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port), "rdma");
xprt->address_strings[RPC_DISPLAY_ALL] = buf;
@@ -204,8 +204,8 @@ xprt_rdma_format_addresses(struct rpc_xprt *xprt)
buf = kzalloc(30, GFP_KERNEL);
if (buf)
- snprintf(buf, 30, NIPQUAD_FMT".%u.%u",
- NIPQUAD(addr->sin_addr.s_addr),
+ snprintf(buf, 30, "%pI4.%u.%u",
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port) >> 8,
ntohs(addr->sin_port) & 0xff);
xprt->address_strings[RPC_DISPLAY_UNIVERSAL_ADDR] = buf;
@@ -369,8 +369,8 @@ xprt_setup_rdma(struct xprt_create *args)
if (ntohs(sin->sin_port) != 0)
xprt_set_bound(xprt);
- dprintk("RPC: %s: %u.%u.%u.%u:%u\n", __func__,
- NIPQUAD(sin->sin_addr.s_addr), ntohs(sin->sin_port));
+ dprintk("RPC: %s: %pI4:%u\n",
+ __func__, &sin->sin_addr.s_addr, ntohs(sin->sin_port));
/* Set max requests */
cdata.max_requests = xprt->max_reqs;
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index a5fef5e6c323..3b21e0cc5e69 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -276,7 +276,9 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
struct rpcrdma_xprt *xprt = id->context;
struct rpcrdma_ia *ia = &xprt->rx_ia;
struct rpcrdma_ep *ep = &xprt->rx_ep;
+#ifdef RPC_DEBUG
struct sockaddr_in *addr = (struct sockaddr_in *) &ep->rep_remote_addr;
+#endif
struct ib_qp_attr attr;
struct ib_qp_init_attr iattr;
int connstate = 0;
@@ -323,12 +325,11 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
case RDMA_CM_EVENT_DEVICE_REMOVAL:
connstate = -ENODEV;
connected:
- dprintk("RPC: %s: %s: %u.%u.%u.%u:%u"
- " (ep 0x%p event 0x%x)\n",
+ dprintk("RPC: %s: %s: %pI4:%u (ep 0x%p event 0x%x)\n",
__func__,
(event->event <= 11) ? conn[event->event] :
"unknown connection error",
- NIPQUAD(addr->sin_addr.s_addr),
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port),
ep, event->event);
atomic_set(&rpcx_to_rdmax(ep->rep_xprt)->rx_buf.rb_credits, 1);
@@ -348,18 +349,17 @@ connected:
if (connstate == 1) {
int ird = attr.max_dest_rd_atomic;
int tird = ep->rep_remote_cma.responder_resources;
- printk(KERN_INFO "rpcrdma: connection to %u.%u.%u.%u:%u "
+ printk(KERN_INFO "rpcrdma: connection to %pI4:%u "
"on %s, memreg %d slots %d ird %d%s\n",
- NIPQUAD(addr->sin_addr.s_addr),
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port),
ia->ri_id->device->name,
ia->ri_memreg_strategy,
xprt->rx_buf.rb_max_requests,
ird, ird < 4 && ird < tird / 2 ? " (low!)" : "");
} else if (connstate < 0) {
- printk(KERN_INFO "rpcrdma: connection to %u.%u.%u.%u:%u "
- "closed (%d)\n",
- NIPQUAD(addr->sin_addr.s_addr),
+ printk(KERN_INFO "rpcrdma: connection to %pI4:%u closed (%d)\n",
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port),
connstate);
}
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 0a50361e3d83..5cbb404c4cdf 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -284,8 +284,7 @@ static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(20, GFP_KERNEL);
if (buf) {
- snprintf(buf, 20, NIPQUAD_FMT,
- NIPQUAD(addr->sin_addr.s_addr));
+ snprintf(buf, 20, "%pI4", &addr->sin_addr.s_addr);
}
xprt->address_strings[RPC_DISPLAY_ADDR] = buf;
@@ -300,8 +299,8 @@ static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(48, GFP_KERNEL);
if (buf) {
- snprintf(buf, 48, "addr="NIPQUAD_FMT" port=%u proto=%s",
- NIPQUAD(addr->sin_addr.s_addr),
+ snprintf(buf, 48, "addr=%pI4 port=%u proto=%s",
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port),
protocol);
}
@@ -323,8 +322,8 @@ static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(30, GFP_KERNEL);
if (buf) {
- snprintf(buf, 30, NIPQUAD_FMT".%u.%u",
- NIPQUAD(addr->sin_addr.s_addr),
+ snprintf(buf, 30, "%pI4.%u.%u",
+ &addr->sin_addr.s_addr,
ntohs(addr->sin_port) >> 8,
ntohs(addr->sin_port) & 0xff);
}
@@ -342,8 +341,7 @@ static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(40, GFP_KERNEL);
if (buf) {
- snprintf(buf, 40, NIP6_FMT,
- NIP6(addr->sin6_addr));
+ snprintf(buf, 40, "%pI6",&addr->sin6_addr);
}
xprt->address_strings[RPC_DISPLAY_ADDR] = buf;
@@ -358,18 +356,17 @@ static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(64, GFP_KERNEL);
if (buf) {
- snprintf(buf, 64, "addr="NIP6_FMT" port=%u proto=%s",
- NIP6(addr->sin6_addr),
+ snprintf(buf, 64, "addr=%pI6 port=%u proto=%s",
+ &addr->sin6_addr,
ntohs(addr->sin6_port),
protocol);
}
xprt->address_strings[RPC_DISPLAY_ALL] = buf;
buf = kzalloc(36, GFP_KERNEL);
- if (buf) {
- snprintf(buf, 36, NIP6_SEQFMT,
- NIP6(addr->sin6_addr));
- }
+ if (buf)
+ snprintf(buf, 36, "%pi6", &addr->sin6_addr);
+
xprt->address_strings[RPC_DISPLAY_HEX_ADDR] = buf;
buf = kzalloc(8, GFP_KERNEL);
@@ -381,10 +378,10 @@ static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt,
buf = kzalloc(50, GFP_KERNEL);
if (buf) {
- snprintf(buf, 50, NIP6_FMT".%u.%u",
- NIP6(addr->sin6_addr),
- ntohs(addr->sin6_port) >> 8,
- ntohs(addr->sin6_port) & 0xff);
+ snprintf(buf, 50, "%pI6.%u.%u",
+ &addr->sin6_addr,
+ ntohs(addr->sin6_port) >> 8,
+ ntohs(addr->sin6_port) & 0xff);
}
xprt->address_strings[RPC_DISPLAY_UNIVERSAL_ADDR] = buf;
@@ -1415,8 +1412,8 @@ static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
if (port > last)
nloop++;
} while (err == -EADDRINUSE && nloop != 2);
- dprintk("RPC: %s "NIPQUAD_FMT":%u: %s (%d)\n",
- __func__, NIPQUAD(myaddr.sin_addr),
+ dprintk("RPC: %s %pI4:%u: %s (%d)\n",
+ __func__, &myaddr.sin_addr,
port, err ? "failed" : "ok", err);
return err;
}
@@ -1448,8 +1445,8 @@ static int xs_bind6(struct sock_xprt *transport, struct socket *sock)
if (port > last)
nloop++;
} while (err == -EADDRINUSE && nloop != 2);
- dprintk("RPC: xs_bind6 "NIP6_FMT":%u: %s (%d)\n",
- NIP6(myaddr.sin6_addr), port, err ? "failed" : "ok", err);
+ dprintk("RPC: xs_bind6 %pI6:%u: %s (%d)\n",
+ &myaddr.sin6_addr, port, err ? "failed" : "ok", err);
return err;
}
diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c
index fe43ef7dd7e3..f72ba774c246 100644
--- a/net/tipc/eth_media.c
+++ b/net/tipc/eth_media.c
@@ -243,12 +243,11 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt,
static char *eth_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
{
unchar *addr = (unchar *)&a->dev_addr;
- DECLARE_MAC_BUF(mac);
if (str_size < 18)
*str_buf = '\0';
else
- sprintf(str_buf, "%s", print_mac(mac, addr));
+ sprintf(str_buf, "%pM", addr);
return str_buf;
}
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index cd72e22b132b..acab41a48d67 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -555,7 +555,7 @@ static struct name_seq *nametbl_find_seq(u32 type)
struct name_seq *ns;
dbg("find_seq %u,(%u,0x%x) table = %p, hash[type] = %u\n",
- type, ntohl(type), type, table.types, hash(type));
+ type, htonl(type), type, table.types, hash(type));
seq_head = &table.types[hash(type)];
hlist_for_each_entry(ns, seq_node, seq_head, ns_list) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 66d5ac4773ab..c6250d0055d2 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -164,7 +164,7 @@ static inline int unix_our_peer(struct sock *sk, struct sock *osk)
static inline int unix_may_send(struct sock *sk, struct sock *osk)
{
- return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
+ return unix_peer(osk) == NULL || unix_our_peer(sk, osk);
}
static inline int unix_recvq_full(struct sock const *sk)
@@ -197,7 +197,7 @@ static inline void unix_release_addr(struct unix_address *addr)
* - if started by zero, it is abstract name.
*/
-static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
+static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned *hashp)
{
if (len <= sizeof(short) || len > sizeof(*sunaddr))
return -EINVAL;
@@ -211,12 +211,12 @@ static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
* we are guaranteed that it is a valid memory location in our
* kernel address buffer.
*/
- ((char *)sunaddr)[len]=0;
+ ((char *)sunaddr)[len] = 0;
len = strlen(sunaddr->sun_path)+1+sizeof(short);
return len;
}
- *hashp = unix_hash_fold(csum_partial((char*)sunaddr, len, 0));
+ *hashp = unix_hash_fold(csum_partial(sunaddr, len, 0));
return len;
}
@@ -295,8 +295,7 @@ static struct sock *unix_find_socket_byinode(struct net *net, struct inode *i)
if (!net_eq(sock_net(s), net))
continue;
- if(dentry && dentry->d_inode == i)
- {
+ if (dentry && dentry->d_inode == i) {
sock_hold(s);
goto found;
}
@@ -354,7 +353,7 @@ static void unix_sock_destructor(struct sock *sk)
WARN_ON(!sk_unhashed(sk));
WARN_ON(sk->sk_socket);
if (!sock_flag(sk, SOCK_DEAD)) {
- printk("Attempt to release alive unix socket: %p\n", sk);
+ printk(KERN_INFO "Attempt to release alive unix socket: %p\n", sk);
return;
}
@@ -362,12 +361,16 @@ static void unix_sock_destructor(struct sock *sk)
unix_release_addr(u->addr);
atomic_dec(&unix_nr_socks);
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+ local_bh_enable();
#ifdef UNIX_REFCNT_DEBUG
- printk(KERN_DEBUG "UNIX %p is destroyed, %d are still alive.\n", sk, atomic_read(&unix_nr_socks));
+ printk(KERN_DEBUG "UNIX %p is destroyed, %d are still alive.\n", sk,
+ atomic_read(&unix_nr_socks));
#endif
}
-static int unix_release_sock (struct sock *sk, int embrion)
+static int unix_release_sock(struct sock *sk, int embrion)
{
struct unix_sock *u = unix_sk(sk);
struct dentry *dentry;
@@ -392,9 +395,9 @@ static int unix_release_sock (struct sock *sk, int embrion)
wake_up_interruptible_all(&u->peer_wait);
- skpair=unix_peer(sk);
+ skpair = unix_peer(sk);
- if (skpair!=NULL) {
+ if (skpair != NULL) {
if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) {
unix_state_lock(skpair);
/* No more writes */
@@ -414,7 +417,7 @@ static int unix_release_sock (struct sock *sk, int embrion)
/* Try to flush out this socket. Throw out buffers at least */
while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
- if (state==TCP_LISTEN)
+ if (state == TCP_LISTEN)
unix_release_sock(skb->sk, 1);
/* passed fds are erased in the kfree_skb hook */
kfree_skb(skb);
@@ -453,11 +456,11 @@ static int unix_listen(struct socket *sock, int backlog)
struct unix_sock *u = unix_sk(sk);
err = -EOPNOTSUPP;
- if (sock->type!=SOCK_STREAM && sock->type!=SOCK_SEQPACKET)
- goto out; /* Only stream/seqpacket sockets accept */
+ if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
+ goto out; /* Only stream/seqpacket sockets accept */
err = -EINVAL;
if (!u->addr)
- goto out; /* No listens on an unbound socket */
+ goto out; /* No listens on an unbound socket */
unix_state_lock(sk);
if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
goto out_unlock;
@@ -467,8 +470,7 @@ static int unix_listen(struct socket *sock, int backlog)
sk->sk_state = TCP_LISTEN;
/* set credentials so connect can copy them */
sk->sk_peercred.pid = task_tgid_vnr(current);
- sk->sk_peercred.uid = current->euid;
- sk->sk_peercred.gid = current->egid;
+ current_euid_egid(&sk->sk_peercred.uid, &sk->sk_peercred.gid);
err = 0;
out_unlock:
@@ -566,9 +568,9 @@ static const struct proto_ops unix_seqpacket_ops = {
};
static struct proto unix_proto = {
- .name = "UNIX",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct unix_sock),
+ .name = "UNIX",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct unix_sock),
};
/*
@@ -579,7 +581,7 @@ static struct proto unix_proto = {
*/
static struct lock_class_key af_unix_sk_receive_queue_lock_key;
-static struct sock * unix_create1(struct net *net, struct socket *sock)
+static struct sock *unix_create1(struct net *net, struct socket *sock)
{
struct sock *sk = NULL;
struct unix_sock *u;
@@ -592,7 +594,7 @@ static struct sock * unix_create1(struct net *net, struct socket *sock)
if (!sk)
goto out;
- sock_init_data(sock,sk);
+ sock_init_data(sock, sk);
lockdep_set_class(&sk->sk_receive_queue.lock,
&af_unix_sk_receive_queue_lock_key);
@@ -611,6 +613,11 @@ static struct sock * unix_create1(struct net *net, struct socket *sock)
out:
if (sk == NULL)
atomic_dec(&unix_nr_socks);
+ else {
+ local_bh_disable();
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ local_bh_enable();
+ }
return sk;
}
@@ -630,7 +637,7 @@ static int unix_create(struct net *net, struct socket *sock, int protocol)
* nothing uses it.
*/
case SOCK_RAW:
- sock->type=SOCK_DGRAM;
+ sock->type = SOCK_DGRAM;
case SOCK_DGRAM:
sock->ops = &unix_dgram_ops;
break;
@@ -653,7 +660,7 @@ static int unix_release(struct socket *sock)
sock->sk = NULL;
- return unix_release_sock (sk, 0);
+ return unix_release_sock(sk, 0);
}
static int unix_autobind(struct socket *sock)
@@ -662,7 +669,7 @@ static int unix_autobind(struct socket *sock)
struct net *net = sock_net(sk);
struct unix_sock *u = unix_sk(sk);
static u32 ordernum = 1;
- struct unix_address * addr;
+ struct unix_address *addr;
int err;
mutex_lock(&u->readlock);
@@ -681,7 +688,7 @@ static int unix_autobind(struct socket *sock)
retry:
addr->len = sprintf(addr->name->sun_path+1, "%05x", ordernum) + 1 + sizeof(short);
- addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
+ addr->hash = unix_hash_fold(csum_partial(addr->name, addr->len, 0));
spin_lock(&unix_table_lock);
ordernum = (ordernum+1)&0xFFFFF;
@@ -736,14 +743,14 @@ static struct sock *unix_find_other(struct net *net,
path_put(&path);
- err=-EPROTOTYPE;
+ err = -EPROTOTYPE;
if (u->sk_type != type) {
sock_put(u);
goto fail;
}
} else {
err = -ECONNREFUSED;
- u=unix_find_socket_byname(net, sunname, len, type, hash);
+ u = unix_find_socket_byname(net, sunname, len, type, hash);
if (u) {
struct dentry *dentry;
dentry = unix_sk(u)->dentry;
@@ -757,7 +764,7 @@ static struct sock *unix_find_other(struct net *net,
put_fail:
path_put(&path);
fail:
- *error=err;
+ *error = err;
return NULL;
}
@@ -767,8 +774,8 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct unix_sock *u = unix_sk(sk);
- struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
- struct dentry * dentry = NULL;
+ struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
+ struct dentry *dentry = NULL;
struct nameidata nd;
int err;
unsigned hash;
@@ -779,7 +786,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (sunaddr->sun_family != AF_UNIX)
goto out;
- if (addr_len==sizeof(short)) {
+ if (addr_len == sizeof(short)) {
err = unix_autobind(sock);
goto out;
}
@@ -875,8 +882,8 @@ out_mknod_unlock:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
path_put(&nd.path);
out_mknod_parent:
- if (err==-EEXIST)
- err=-EADDRINUSE;
+ if (err == -EEXIST)
+ err = -EADDRINUSE;
unix_release_addr(addr);
goto out_up;
}
@@ -911,7 +918,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
{
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
- struct sockaddr_un *sunaddr=(struct sockaddr_un*)addr;
+ struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
struct sock *other;
unsigned hash;
int err;
@@ -927,7 +934,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
goto out;
restart:
- other=unix_find_other(net, sunaddr, alen, sock->type, hash, &err);
+ other = unix_find_other(net, sunaddr, alen, sock->type, hash, &err);
if (!other)
goto out;
@@ -961,14 +968,14 @@ restart:
*/
if (unix_peer(sk)) {
struct sock *old_peer = unix_peer(sk);
- unix_peer(sk)=other;
+ unix_peer(sk) = other;
unix_state_double_unlock(sk, other);
if (other != old_peer)
unix_dgram_disconnected(sk, old_peer);
sock_put(old_peer);
} else {
- unix_peer(sk)=other;
+ unix_peer(sk) = other;
unix_state_double_unlock(sk, other);
}
return 0;
@@ -1004,7 +1011,7 @@ static long unix_wait_for_peer(struct sock *other, long timeo)
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
- struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
+ struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct unix_sock *u = unix_sk(sk), *newu, *otheru;
@@ -1126,8 +1133,7 @@ restart:
newsk->sk_state = TCP_ESTABLISHED;
newsk->sk_type = sk->sk_type;
newsk->sk_peercred.pid = task_tgid_vnr(current);
- newsk->sk_peercred.uid = current->euid;
- newsk->sk_peercred.gid = current->egid;
+ current_euid_egid(&newsk->sk_peercred.uid, &newsk->sk_peercred.gid);
newu = unix_sk(newsk);
newsk->sk_sleep = &newu->peer_wait;
otheru = unix_sk(other);
@@ -1179,16 +1185,17 @@ out:
static int unix_socketpair(struct socket *socka, struct socket *sockb)
{
- struct sock *ska=socka->sk, *skb = sockb->sk;
+ struct sock *ska = socka->sk, *skb = sockb->sk;
/* Join our sockets back to back */
sock_hold(ska);
sock_hold(skb);
- unix_peer(ska)=skb;
- unix_peer(skb)=ska;
+ unix_peer(ska) = skb;
+ unix_peer(skb) = ska;
ska->sk_peercred.pid = skb->sk_peercred.pid = task_tgid_vnr(current);
- ska->sk_peercred.uid = skb->sk_peercred.uid = current->euid;
- ska->sk_peercred.gid = skb->sk_peercred.gid = current->egid;
+ current_euid_egid(&skb->sk_peercred.uid, &skb->sk_peercred.gid);
+ ska->sk_peercred.uid = skb->sk_peercred.uid;
+ ska->sk_peercred.gid = skb->sk_peercred.gid;
if (ska->sk_type != SOCK_DGRAM) {
ska->sk_state = TCP_ESTABLISHED;
@@ -1207,7 +1214,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
int err;
err = -EOPNOTSUPP;
- if (sock->type!=SOCK_STREAM && sock->type!=SOCK_SEQPACKET)
+ if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
goto out;
err = -EINVAL;
@@ -1246,7 +1253,7 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_
{
struct sock *sk = sock->sk;
struct unix_sock *u;
- struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
+ struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
int err = 0;
if (peer) {
@@ -1286,7 +1293,7 @@ static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
skb->destructor = sock_wfree;
UNIXCB(skb).fp = NULL;
- for (i=scm->fp->count-1; i>=0; i--)
+ for (i = scm->fp->count-1; i >= 0; i--)
unix_notinflight(scm->fp->fp[i]);
}
@@ -1315,7 +1322,7 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
if (!UNIXCB(skb).fp)
return -ENOMEM;
- for (i=scm->fp->count-1; i>=0; i--)
+ for (i = scm->fp->count-1; i >= 0; i--)
unix_inflight(scm->fp->fp[i]);
skb->destructor = unix_destruct_fds;
return 0;
@@ -1332,7 +1339,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct unix_sock *u = unix_sk(sk);
- struct sockaddr_un *sunaddr=msg->msg_name;
+ struct sockaddr_un *sunaddr = msg->msg_name;
struct sock *other = NULL;
int namelen = 0; /* fake GCC */
int err;
@@ -1374,7 +1381,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto out;
skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
- if (skb==NULL)
+ if (skb == NULL)
goto out;
memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
@@ -1386,7 +1393,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
unix_get_secdata(siocb->scm, skb);
skb_reset_transport_header(skb);
- err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
if (err)
goto out_free;
@@ -1400,7 +1407,7 @@ restart:
other = unix_find_other(net, sunaddr, namelen, sk->sk_type,
hash, &err);
- if (other==NULL)
+ if (other == NULL)
goto out_free;
}
@@ -1420,7 +1427,7 @@ restart:
err = 0;
unix_state_lock(sk);
if (unix_peer(sk) == other) {
- unix_peer(sk)=NULL;
+ unix_peer(sk) = NULL;
unix_state_unlock(sk);
unix_dgram_disconnected(sk, other);
@@ -1486,10 +1493,10 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct sock_iocb *siocb = kiocb_to_siocb(kiocb);
struct sock *sk = sock->sk;
struct sock *other = NULL;
- struct sockaddr_un *sunaddr=msg->msg_name;
- int err,size;
+ struct sockaddr_un *sunaddr = msg->msg_name;
+ int err, size;
struct sk_buff *skb;
- int sent=0;
+ int sent = 0;
struct scm_cookie tmp_scm;
if (NULL == siocb->scm)
@@ -1517,8 +1524,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (sk->sk_shutdown & SEND_SHUTDOWN)
goto pipe_err;
- while(sent < len)
- {
+ while (sent < len) {
/*
* Optimisation for the fact that under 0.01% of X
* messages typically need breaking up.
@@ -1537,9 +1543,10 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
* Grab a buffer
*/
- skb=sock_alloc_send_skb(sk,size,msg->msg_flags&MSG_DONTWAIT, &err);
+ skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT,
+ &err);
- if (skb==NULL)
+ if (skb == NULL)
goto out_err;
/*
@@ -1560,7 +1567,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
}
}
- if ((err = memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size)) != 0) {
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ if (err) {
kfree_skb(skb);
goto out_err;
}
@@ -1574,7 +1582,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
skb_queue_tail(&other->sk_receive_queue, skb);
unix_state_unlock(other);
other->sk_data_ready(other, size);
- sent+=size;
+ sent += size;
}
scm_destroy(siocb->scm);
@@ -1586,8 +1594,8 @@ pipe_err_free:
unix_state_unlock(other);
kfree_skb(skb);
pipe_err:
- if (sent==0 && !(msg->msg_flags&MSG_NOSIGNAL))
- send_sig(SIGPIPE,current,0);
+ if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
err = -EPIPE;
out_err:
scm_destroy(siocb->scm);
@@ -1677,13 +1685,10 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
siocb->scm->creds = *UNIXCREDS(skb);
unix_set_secdata(siocb->scm, skb);
- if (!(flags & MSG_PEEK))
- {
+ if (!(flags & MSG_PEEK)) {
if (UNIXCB(skb).fp)
unix_detach_fds(siocb->scm, skb);
- }
- else
- {
+ } else {
/* It is questionable: on PEEK we could:
- do not return fds - good, but too simple 8)
- return fds, and do not return them on read (old strategy,
@@ -1704,7 +1709,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
scm_recv(sock, msg, siocb->scm, flags);
out_free:
- skb_free_datagram(sk,skb);
+ skb_free_datagram(sk, skb);
out_unlock:
mutex_unlock(&u->readlock);
out:
@@ -1715,7 +1720,7 @@ out:
* Sleep until data has arrive. But check for races..
*/
-static long unix_stream_data_wait(struct sock * sk, long timeo)
+static long unix_stream_data_wait(struct sock *sk, long timeo)
{
DEFINE_WAIT(wait);
@@ -1753,7 +1758,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
struct scm_cookie tmp_scm;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
- struct sockaddr_un *sunaddr=msg->msg_name;
+ struct sockaddr_un *sunaddr = msg->msg_name;
int copied = 0;
int check_creds = 0;
int target;
@@ -1784,15 +1789,13 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
mutex_lock(&u->readlock);
- do
- {
+ do {
int chunk;
struct sk_buff *skb;
unix_state_lock(sk);
skb = skb_dequeue(&sk->sk_receive_queue);
- if (skb==NULL)
- {
+ if (skb == NULL) {
if (copied >= target)
goto unlock;
@@ -1800,7 +1803,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
* POSIX 1003.1g mandates this order.
*/
- if ((err = sock_error(sk)) != 0)
+ err = sock_error(sk);
+ if (err)
goto unlock;
if (sk->sk_shutdown & RCV_SHUTDOWN)
goto unlock;
@@ -1827,7 +1831,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (check_creds) {
/* Never glue messages from different writers */
- if (memcmp(UNIXCREDS(skb), &siocb->scm->creds, sizeof(siocb->scm->creds)) != 0) {
+ if (memcmp(UNIXCREDS(skb), &siocb->scm->creds,
+ sizeof(siocb->scm->creds)) != 0) {
skb_queue_head(&sk->sk_receive_queue, skb);
break;
}
@@ -1838,8 +1843,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
}
/* Copy address just once */
- if (sunaddr)
- {
+ if (sunaddr) {
unix_copy_addr(msg, skb->sk);
sunaddr = NULL;
}
@@ -1855,16 +1859,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
size -= chunk;
/* Mark read part of skb as used */
- if (!(flags & MSG_PEEK))
- {
+ if (!(flags & MSG_PEEK)) {
skb_pull(skb, chunk);
if (UNIXCB(skb).fp)
unix_detach_fds(siocb->scm, skb);
/* put the skb back if we didn't use it up.. */
- if (skb->len)
- {
+ if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb);
break;
}
@@ -1873,9 +1875,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
if (siocb->scm->fp)
break;
- }
- else
- {
+ } else {
/* It is questionable, see note in unix_dgram_recvmsg.
*/
if (UNIXCB(skb).fp)
@@ -1903,7 +1903,7 @@ static int unix_shutdown(struct socket *sock, int mode)
if (mode) {
unix_state_lock(sk);
sk->sk_shutdown |= mode;
- other=unix_peer(sk);
+ other = unix_peer(sk);
if (other)
sock_hold(other);
unix_state_unlock(sk);
@@ -1938,16 +1938,15 @@ static int unix_shutdown(struct socket *sock, int mode)
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- long amount=0;
+ long amount = 0;
int err;
- switch(cmd)
- {
- case SIOCOUTQ:
- amount = atomic_read(&sk->sk_wmem_alloc);
- err = put_user(amount, (int __user *)arg);
- break;
- case SIOCINQ:
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = atomic_read(&sk->sk_wmem_alloc);
+ err = put_user(amount, (int __user *)arg);
+ break;
+ case SIOCINQ:
{
struct sk_buff *skb;
@@ -1964,21 +1963,21 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
} else {
skb = skb_peek(&sk->sk_receive_queue);
if (skb)
- amount=skb->len;
+ amount = skb->len;
}
spin_unlock(&sk->sk_receive_queue.lock);
err = put_user(amount, (int __user *)arg);
break;
}
- default:
- err = -ENOIOCTLCMD;
- break;
+ default:
+ err = -ENOIOCTLCMD;
+ break;
}
return err;
}
-static unsigned int unix_poll(struct file * file, struct socket *sock, poll_table *wait)
+static unsigned int unix_poll(struct file *file, struct socket *sock, poll_table *wait)
{
struct sock *sk = sock->sk;
unsigned int mask;
@@ -2000,7 +1999,8 @@ static unsigned int unix_poll(struct file * file, struct socket *sock, poll_tabl
mask |= POLLIN | POLLRDNORM;
/* Connection-based need to check for termination and startup */
- if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) && sk->sk_state == TCP_CLOSE)
+ if ((sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) &&
+ sk->sk_state == TCP_CLOSE)
mask |= POLLHUP;
/*
@@ -2096,6 +2096,7 @@ struct unix_iter_state {
struct seq_net_private p;
int i;
};
+
static struct sock *unix_seq_idx(struct seq_file *seq, loff_t pos)
{
struct unix_iter_state *iter = seq->private;
@@ -2112,7 +2113,6 @@ static struct sock *unix_seq_idx(struct seq_file *seq, loff_t pos)
return NULL;
}
-
static void *unix_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(unix_table_lock)
{
@@ -2192,7 +2192,6 @@ static const struct seq_operations unix_seq_ops = {
.show = unix_seq_show,
};
-
static int unix_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &unix_seq_ops,
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index abb3ab34cb1e..19c17e4a0c8b 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -106,8 +106,8 @@ static struct sock *unix_get_socket(struct file *filp)
* Socket ?
*/
if (S_ISSOCK(inode->i_mode)) {
- struct socket * sock = SOCKET_I(inode);
- struct sock * s = sock->sk;
+ struct socket *sock = SOCKET_I(inode);
+ struct sock *s = sock->sk;
/*
* PF_UNIX ?
@@ -126,7 +126,7 @@ static struct sock *unix_get_socket(struct file *filp)
void unix_inflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
- if(s) {
+ if (s) {
struct unix_sock *u = unix_sk(s);
spin_lock(&unix_gc_lock);
if (atomic_long_inc_return(&u->inflight) == 1) {
@@ -143,7 +143,7 @@ void unix_inflight(struct file *fp)
void unix_notinflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
- if(s) {
+ if (s) {
struct unix_sock *u = unix_sk(s);
spin_lock(&unix_gc_lock);
BUG_ON(list_empty(&u->link));
@@ -156,7 +156,7 @@ void unix_notinflight(struct file *fp)
static inline struct sk_buff *sock_queue_head(struct sock *sk)
{
- return (struct sk_buff *) &sk->sk_receive_queue;
+ return (struct sk_buff *)&sk->sk_receive_queue;
}
#define receive_queue_for_each_skb(sk, next, skb) \
@@ -370,7 +370,7 @@ void unix_gc(void)
*/
skb_queue_head_init(&hitlist);
list_for_each_entry(u, &gc_candidates, link)
- scan_children(&u->sk, inc_inflight, &hitlist);
+ scan_children(&u->sk, inc_inflight, &hitlist);
spin_unlock(&unix_gc_lock);
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index 77513d7e35f2..83c093077ebc 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -21,7 +21,7 @@ static ctl_table unix_table[] = {
.data = &init_net.unx.sysctl_max_dgram_qlen,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec
+ .proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
@@ -61,4 +61,3 @@ void unix_sysctl_unregister(struct net *net)
unregister_sysctl_table(net->unx.ctl);
kfree(table);
}
-
diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c
index 7f07152bc109..39701dec1dba 100644
--- a/net/wanrouter/wanmain.c
+++ b/net/wanrouter/wanmain.c
@@ -60,6 +60,8 @@
#define KMEM_SAFETYZONE 8
+#define DEV_TO_SLAVE(dev) (*((struct net_device **)netdev_priv(dev)))
+
/*
* Function Prototypes
*/
@@ -511,7 +513,7 @@ static int wanrouter_device_shutdown(struct wan_device *wandev)
if (err)
return err;
/* The above function deallocates the current dev
- * structure. Therefore, we cannot use dev->priv
+ * structure. Therefore, we cannot use netdev_priv(dev)
* as the next element: wandev->dev points to the
* next element */
dev = wandev->dev;
@@ -589,10 +591,6 @@ static int wanrouter_device_new_if(struct wan_device *wandev,
err = -EPROTONOSUPPORT;
goto out;
} else {
- dev = kzalloc(sizeof(struct net_device), GFP_KERNEL);
- err = -ENOBUFS;
- if (dev == NULL)
- goto out;
err = wandev->new_if(wandev, dev, cnf);
}
@@ -622,10 +620,9 @@ static int wanrouter_device_new_if(struct wan_device *wandev,
wandev->dev = dev;
} else {
for (slave=wandev->dev;
- *((struct net_device **)slave->priv);
- slave = *((struct net_device **)slave->priv));
-
- *((struct net_device **)slave->priv) = dev;
+ DEV_TO_SLAVE(slave);
+ slave = DEV_TO_SLAVE(slave))
+ DEV_TO_SLAVE(slave) = dev;
}
++wandev->ndev;
@@ -636,15 +633,9 @@ static int wanrouter_device_new_if(struct wan_device *wandev,
}
if (wandev->del_if)
wandev->del_if(wandev, dev);
+ free_netdev(dev);
}
- /* This code has moved from del_if() function */
- kfree(dev->priv);
- dev->priv = NULL;
-
- /* Sync PPP is disabled */
- if (cnf->config_id != WANCONFIG_MPPP)
- kfree(dev);
out:
kfree(cnf);
return err;
@@ -734,7 +725,7 @@ static int wanrouter_delete_interface(struct wan_device *wandev, char *name)
dev = wandev->dev;
prev = NULL;
while (dev && strcmp(name, dev->name)) {
- struct net_device **slave = dev->priv;
+ struct net_device **slave = netdev_priv(dev);
prev = dev;
dev = *slave;
}
@@ -751,12 +742,12 @@ static int wanrouter_delete_interface(struct wan_device *wandev, char *name)
lock_adapter_irq(&wandev->lock, &smp_flags);
if (prev) {
- struct net_device **prev_slave = prev->priv;
- struct net_device **slave = dev->priv;
+ struct net_device **prev_slave = netdev_priv(prev);
+ struct net_device **slave = netdev_priv(dev);
*prev_slave = *slave;
} else {
- struct net_device **slave = dev->priv;
+ struct net_device **slave = netdev_priv(dev);
wandev->dev = *slave;
}
--wandev->ndev;
@@ -764,11 +755,6 @@ static int wanrouter_delete_interface(struct wan_device *wandev, char *name)
printk(KERN_INFO "%s: unregistering '%s'\n", wandev->name, dev->name);
- /* Due to new interface linking method using dev->priv,
- * this code has moved from del_if() function.*/
- kfree(dev->priv);
- dev->priv=NULL;
-
unregister_netdev(dev);
free_netdev(dev);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 646c7121dbc0..e28e2b8fa436 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,6 +1,15 @@
config CFG80211
tristate "Improved wireless configuration API"
+config CFG80211_REG_DEBUG
+ bool "cfg80211 regulatory debugging"
+ depends on CFG80211
+ default n
+ ---help---
+ You can enable this if you want to debug regulatory changes.
+
+ If unsure, say N.
+
config NL80211
bool "nl80211 new netlink interface support"
depends on CFG80211
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY
ieee80211_regdom module parameter. This is being phased out and you
should stop using them ASAP.
+ Note: You will need CRDA if you want 802.11d support
+
Say Y unless you have installed a new userspace application.
Also say Y if have one currently depending on the ieee80211_regdom
module parameter and cannot port it to use the new userspace
@@ -72,3 +83,22 @@ config WIRELESS_EXT_SYSFS
Say Y if you have programs using it, like old versions of
hal.
+
+config LIB80211
+ tristate "Common routines for IEEE802.11 drivers"
+ default n
+ help
+ This options enables a library of common routines used
+ by IEEE802.11 wireless LAN drivers.
+
+ Drivers should select this themselves if needed. Say Y if
+ you want this built into your kernel.
+
+config LIB80211_CRYPT_WEP
+ tristate
+
+config LIB80211_CRYPT_CCMP
+ tristate
+
+config LIB80211_CRYPT_TKIP
+ tristate
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index b9f943c45f3b..938a334c8dbc 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -1,5 +1,12 @@
obj-$(CONFIG_WIRELESS_EXT) += wext.o
obj-$(CONFIG_CFG80211) += cfg80211.o
+obj-$(CONFIG_LIB80211) += lib80211.o
+obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
+obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
+obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o
+cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
cfg80211-$(CONFIG_NL80211) += nl80211.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 5031db7b275b..b96fc0c3f1c4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -19,7 +19,6 @@
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
-#include "reg.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@@ -236,8 +235,7 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
mutex_unlock(&cfg80211_drv_mutex);
/* give it a proper name */
- snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
- PHY_NAME "%d", drv->idx);
+ dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->idx);
mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx);
@@ -301,12 +299,10 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ mutex_lock(&cfg80211_drv_mutex);
+
/* set up regulatory info */
- mutex_lock(&cfg80211_reg_mutex);
wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
- mutex_unlock(&cfg80211_reg_mutex);
-
- mutex_lock(&cfg80211_drv_mutex);
res = device_add(&drv->wiphy.dev);
if (res)
@@ -351,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy)
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
+ /* If this device got a regulatory hint tell core its
+ * free to listen now to a new shiny device regulatory hint */
+ reg_device_remove(wiphy);
+
list_del(&drv->list);
device_del(&drv->wiphy.dev);
debugfs_remove(drv->wiphy.debugfsdir);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 771cc5cc7658..f7fb9f413028 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -11,6 +11,7 @@
#include <net/genetlink.h>
#include <net/wireless.h>
#include <net/cfg80211.h>
+#include "reg.h"
struct cfg80211_registered_device {
struct cfg80211_ops *ops;
@@ -21,6 +22,18 @@ struct cfg80211_registered_device {
* any call is in progress */
struct mutex mtx;
+ /* ISO / IEC 3166 alpha2 for which this device is receiving
+ * country IEs on, this can help disregard country IEs from APs
+ * on the same alpha2 quickly. The alpha2 may differ from
+ * cfg80211_regdomain's alpha2 when an intersection has occurred.
+ * If the AP is reconfigured this can also be used to tell us if
+ * the country on the country IE changed. */
+ char country_ie_alpha2[2];
+
+ /* If a Country IE has been received this tells us the environment
+ * which its telling us its in. This defaults to ENVIRON_ANY */
+ enum environment_cap env;
+
/* wiphy index, internal only */
int idx;
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c
new file mode 100644
index 000000000000..97d411f74507
--- /dev/null
+++ b/net/wireless/lib80211.c
@@ -0,0 +1,284 @@
+/*
+ * lib80211 -- common bits for IEEE802.11 drivers
+ *
+ * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
+ *
+ * Portions copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/ieee80211.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <net/lib80211.h>
+
+#define DRV_NAME "lib80211"
+
+#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
+MODULE_LICENSE("GPL");
+
+struct lib80211_crypto_alg {
+ struct list_head list;
+ struct lib80211_crypto_ops *ops;
+};
+
+static LIST_HEAD(lib80211_crypto_algs);
+static DEFINE_SPINLOCK(lib80211_crypto_lock);
+
+const char *print_ssid(char *buf, const char *ssid, u8 ssid_len)
+{
+ const char *s = ssid;
+ char *d = buf;
+
+ ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN);
+ while (ssid_len--) {
+ if (isprint(*s)) {
+ *d++ = *s++;
+ continue;
+ }
+
+ *d++ = '\\';
+ if (*s == '\0')
+ *d++ = '0';
+ else if (*s == '\n')
+ *d++ = 'n';
+ else if (*s == '\r')
+ *d++ = 'r';
+ else if (*s == '\t')
+ *d++ = 't';
+ else if (*s == '\\')
+ *d++ = '\\';
+ else
+ d += snprintf(d, 3, "%03o", *s);
+ s++;
+ }
+ *d = '\0';
+ return buf;
+}
+EXPORT_SYMBOL(print_ssid);
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->name = name;
+ info->lock = lock;
+
+ INIT_LIST_HEAD(&info->crypt_deinit_list);
+ setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
+ (unsigned long)info);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_crypt_info_init);
+
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
+{
+ int i;
+
+ lib80211_crypt_quiescing(info);
+ del_timer_sync(&info->crypt_deinit_timer);
+ lib80211_crypt_deinit_entries(info, 1);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ struct lib80211_crypt_data *crypt = info->crypt[i];
+ if (crypt) {
+ if (crypt->ops) {
+ crypt->ops->deinit(crypt->priv);
+ module_put(crypt->ops->owner);
+ }
+ kfree(crypt);
+ info->crypt[i] = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(lib80211_crypt_info_free);
+
+void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force)
+{
+ struct lib80211_crypt_data *entry, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(&entry->list);
+
+ if (entry->ops) {
+ entry->ops->deinit(entry->priv);
+ module_put(entry->ops->owner);
+ }
+ kfree(entry);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_entries);
+
+/* After this, crypt_deinit_list won't accept new members */
+void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ info->crypt_quiesced = 1;
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_quiescing);
+
+void lib80211_crypt_deinit_handler(unsigned long data)
+{
+ struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
+ unsigned long flags;
+
+ lib80211_crypt_deinit_entries(info, 0);
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
+ printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+ "deletion list\n", info->name);
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_handler);
+
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt)
+{
+ struct lib80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!info->crypt_quiesced) {
+ list_add(&tmp->list, &info->crypt_deinit_list);
+ if (!timer_pending(&info->crypt_deinit_timer)) {
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
+
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct lib80211_crypto_alg *alg;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_add(&alg->list, &lib80211_crypto_algs);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+
+ printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_register_crypto_ops);
+
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (alg->ops == ops)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return -EINVAL;
+
+ found:
+ printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm "
+ "'%s'\n", ops->name);
+ list_del(&alg->list);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ kfree(alg);
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
+
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (strcmp(alg->ops->name, name) == 0)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return NULL;
+
+ found:
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return alg->ops;
+}
+EXPORT_SYMBOL(lib80211_get_crypto_ops);
+
+static void *lib80211_crypt_null_init(int keyidx)
+{
+ return (void *)1;
+}
+
+static void lib80211_crypt_null_deinit(void *priv)
+{
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_null = {
+ .name = "NULL",
+ .init = lib80211_crypt_null_init,
+ .deinit = lib80211_crypt_null_deinit,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_init(void)
+{
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n");
+ return lib80211_register_crypto_ops(&lib80211_crypt_null);
+}
+
+static void __exit lib80211_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_null);
+ BUG_ON(!list_empty(&lib80211_crypto_algs));
+}
+
+module_init(lib80211_init);
+module_exit(lib80211_exit);
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
new file mode 100644
index 000000000000..db428194c16a
--- /dev/null
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -0,0 +1,492 @@
+/*
+ * lib80211 crypt: host-based CCMP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+#include <linux/wireless.h>
+
+#include <linux/ieee80211.h>
+
+#include <linux/crypto.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: CCMP");
+MODULE_LICENSE("GPL");
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct lib80211_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_cipher *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one(tfm, ct, pt);
+}
+
+static void *lib80211_ccmp_init(int key_idx)
+{
+ struct lib80211_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_ccmp: could not allocate "
+ "crypto API aes\n");
+ priv->tfm = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher(priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_ccmp_deinit(void *priv)
+{
+ struct lib80211_ccmp_data *_priv = priv;
+ if (_priv && _priv->tfm)
+ crypto_free_cipher(_priv->tfm);
+ kfree(priv);
+}
+
+static inline void xor_block(u8 * b, u8 * a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+static void ccmp_init_blocks(struct crypto_cipher *tfm,
+ struct ieee80211_hdr *hdr,
+ u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ a4_included = ieee80211_has_a4(hdr->frame_control);
+ qc_included = ieee80211_is_data_qos(hdr->frame_control);
+
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) & hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) & hdr->seq_ctrl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ lib80211_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ lib80211_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
+ u8 *aeskey, int keylen, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int i;
+ u8 *pos;
+
+ if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
+ return -1;
+
+ if (aeskey != NULL && keylen >= CCMP_TK_LEN)
+ memcpy(aeskey, key->key, CCMP_TK_LEN);
+
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+ return CCMP_HDR_LEN;
+}
+
+static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int data_len, i, blocks, last, len;
+ u8 *pos, *mic;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
+ if (len < 0)
+ return -1;
+
+ pos = skb->data + hdr_len + CCMP_HDR_LEN;
+ mic = skb_put(skb, CCMP_MIC_LEN);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+
+ return 0;
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
+{
+ u32 iv32_n, iv16_n;
+ u32 iv32_o, iv16_o;
+
+ iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
+ iv16_n = (pn_n[4] << 8) | pn_n[5];
+
+ iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
+ iv16_o = (pn_o[4] << 8) | pn_o[5];
+
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ u8 pn[6];
+ int i, blocks, last, len;
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!key->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+
+ if (ccmp_replay_check(pn, key->rx_pn)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: replay detected: STA=%pM "
+ "previous PN %02x%02x%02x%02x%02x%02x "
+ "received PN %02x%02x%02x%02x%02x%02x\n",
+ hdr->addr2,
+ key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
+ key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
+ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
+ }
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: decrypt failed: STA="
+ "%pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_cipher *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+static char *lib80211_ccmp_print_stats(char *p, void *priv)
+{
+ struct lib80211_ccmp_data *ccmp = priv;
+
+ p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
+ ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
+ ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
+ ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
+ .name = "CCMP",
+ .init = lib80211_ccmp_init,
+ .deinit = lib80211_ccmp_deinit,
+ .build_iv = lib80211_ccmp_hdr,
+ .encrypt_mpdu = lib80211_ccmp_encrypt,
+ .decrypt_mpdu = lib80211_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_ccmp_set_key,
+ .get_key = lib80211_ccmp_get_key,
+ .print_stats = lib80211_ccmp_print_stats,
+ .extra_mpdu_prefix_len = CCMP_HDR_LEN,
+ .extra_mpdu_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_ccmp_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+static void __exit lib80211_crypto_ccmp_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+module_init(lib80211_crypto_ccmp_init);
+module_exit(lib80211_crypto_ccmp_exit);
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
new file mode 100644
index 000000000000..7e8e22bfed90
--- /dev/null
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -0,0 +1,784 @@
+/*
+ * lib80211 crypt: host-based TKIP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/mm.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: TKIP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+
+ struct crypto_blkcipher *rx_tfm_arc4;
+ struct crypto_hash *rx_tfm_michael;
+ struct crypto_blkcipher *tx_tfm_arc4;
+ struct crypto_hash *tx_tfm_michael;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+
+ unsigned long flags;
+};
+
+static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ unsigned long old_flags = _priv->flags;
+ _priv->flags = flags;
+ return old_flags;
+}
+
+static unsigned long lib80211_tkip_get_flags(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ return _priv->flags;
+}
+
+static void *lib80211_tkip_init(int key_idx)
+{
+ struct lib80211_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+
+ priv->key_idx = key_idx;
+
+ priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm_michael)
+ crypto_free_hash(priv->tx_tfm_michael);
+ if (priv->tx_tfm_arc4)
+ crypto_free_blkcipher(priv->tx_tfm_arc4);
+ if (priv->rx_tfm_michael)
+ crypto_free_hash(priv->rx_tfm_michael);
+ if (priv->rx_tfm_arc4)
+ crypto_free_blkcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_tkip_deinit(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm_michael)
+ crypto_free_hash(_priv->tx_tfm_michael);
+ if (_priv->tx_tfm_arc4)
+ crypto_free_blkcipher(_priv->tx_tfm_arc4);
+ if (_priv->rx_tfm_michael)
+ crypto_free_hash(_priv->rx_tfm_michael);
+ if (_priv->rx_tfm_arc4)
+ crypto_free_blkcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+static inline u16 Mk16_le(__le16 * v)
+{
+ return le16_to_cpu(*v);
+}
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
+ u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) & WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
+ u8 * rc4key, int keylen, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int len;
+ u8 *pos;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (skb_headroom(skb) < 8 || skb->len < hdr_len)
+ return -1;
+
+ if (rc4key == NULL || keylen < 16)
+ return -1;
+
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 8);
+ memmove(pos, pos + 8, hdr_len);
+ pos += hdr_len;
+
+ *pos++ = *rc4key;
+ *pos++ = *(rc4key + 1);
+ *pos++ = *(rc4key + 2);
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ return 8;
+}
+
+static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
+ int len;
+ u8 rc4key[16], *pos, *icv;
+ u32 crc;
+ struct scatterlist sg;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "TX packet to %pM\n", hdr->addr1);
+ }
+ return -1;
+ }
+
+ if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb->data + hdr_len;
+
+ if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
+ return -1;
+
+ icv = skb_put(skb, 4);
+
+ crc = ~crc32_le(~0, pos, len);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
+ u32 iv32_o, u16 iv16_o)
+{
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
+ u8 rc4key[16];
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct ieee80211_hdr *hdr;
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "received packet from %pM\n", hdr->addr2);
+ }
+ return -1;
+ }
+
+ if (skb->len < hdr_len + 8 + 4)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += 8;
+
+ if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
+ " previous TSC %08x%04x received TSC "
+ "%08x%04x\n", hdr->addr2,
+ tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
+ }
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP: failed to decrypt "
+ "received packet from %pM\n",
+ hdr->addr2);
+ }
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already lost, so
+ * it needs to be recalculated for the next packet. */
+ tkey->rx_phase1_done = 0;
+ }
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: ICV error detected: STA="
+ "%pM\n", hdr->addr2);
+ }
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 8, skb->data, hdr_len);
+ skb_pull(skb, 8);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
+ u8 * data, size_t data_len, u8 * mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + 16, mic);
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
+{
+ struct ieee80211_hdr *hdr11;
+
+ hdr11 = (struct ieee80211_hdr *)skb->data;
+
+ switch (le16_to_cpu(hdr11->frame_control) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ case 0:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ if (ieee80211_is_data_qos(hdr11->frame_control)) {
+ hdr[12] = le16_to_cpu(*ieee80211_get_qos_ctl(hdr11))
+ & IEEE80211_QOS_CTL_TID_MASK;
+ } else
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
+ void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 *pos;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG "Invalid packet for Michael MIC add "
+ "(tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+ pos = skb_put(skb, 8);
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+static void lib80211_michael_mic_failure(struct net_device *dev,
+ struct ieee80211_hdr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
+}
+
+static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 mic[8];
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG "%s: Michael MIC verification failed for "
+ "MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ if (skb->dev)
+ lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed. */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_hash *tfm = tkey->tx_tfm_michael;
+ struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+static char *lib80211_tkip_print_stats(char *p, void *priv)
+{
+ struct lib80211_tkip_data *tkip = priv;
+ p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_tkip = {
+ .name = "TKIP",
+ .init = lib80211_tkip_init,
+ .deinit = lib80211_tkip_deinit,
+ .build_iv = lib80211_tkip_hdr,
+ .encrypt_mpdu = lib80211_tkip_encrypt,
+ .decrypt_mpdu = lib80211_tkip_decrypt,
+ .encrypt_msdu = lib80211_michael_mic_add,
+ .decrypt_msdu = lib80211_michael_mic_verify,
+ .set_key = lib80211_tkip_set_key,
+ .get_key = lib80211_tkip_get_key,
+ .print_stats = lib80211_tkip_print_stats,
+ .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .extra_msdu_postfix_len = 8, /* MIC */
+ .get_flags = lib80211_tkip_get_flags,
+ .set_flags = lib80211_tkip_set_flags,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_tkip_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
+}
+
+static void __exit lib80211_crypto_tkip_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
+}
+
+module_init(lib80211_crypto_tkip_init);
+module_exit(lib80211_crypto_tkip_exit);
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
new file mode 100644
index 000000000000..6d41e05ca33b
--- /dev/null
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -0,0 +1,296 @@
+/*
+ * lib80211 crypt: host-based WEP encryption implementation for lib80211
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <asm/string.h>
+
+#include <net/lib80211.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: WEP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_blkcipher *tx_tfm;
+ struct crypto_blkcipher *rx_tfm;
+};
+
+static void *lib80211_wep_init(int keyidx)
+{
+ struct lib80211_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm)
+ crypto_free_blkcipher(priv->tx_tfm);
+ if (priv->rx_tfm)
+ crypto_free_blkcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+ return NULL;
+}
+
+static void lib80211_wep_deinit(void *priv)
+{
+ struct lib80211_wep_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm)
+ crypto_free_blkcipher(_priv->tx_tfm);
+ if (_priv->rx_tfm)
+ crypto_free_blkcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
+static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
+ u8 *key, int keylen, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ u32 klen, len;
+ u8 *pos;
+
+ if (skb_headroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them. */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = (wep->iv >> 16) & 0xff;
+ *pos++ = (wep->iv >> 8) & 0xff;
+ *pos++ = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ return 0;
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
+ u32 crc, klen, len;
+ u8 *pos, *icv;
+ struct scatterlist sg;
+ u8 key[WEP_KEY_LEN + 3];
+
+ /* other checks are in lib80211_wep_build_iv */
+ if (skb_tailroom(skb) < 4)
+ return -1;
+
+ /* add the IV to the frame */
+ if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
+ return -1;
+
+ /* Copy the IV into the first 3 bytes of the key */
+ skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ len = skb->len - hdr_len - 4;
+ pos = skb->data + hdr_len + 4;
+ klen = 3 + wep->key_len;
+
+ /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
+ u32 crc, klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos, icv[4];
+ struct scatterlist sg;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
+ return -7;
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+static char *lib80211_wep_print_stats(char *p, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_wep = {
+ .name = "WEP",
+ .init = lib80211_wep_init,
+ .deinit = lib80211_wep_deinit,
+ .build_iv = lib80211_wep_build_iv,
+ .encrypt_mpdu = lib80211_wep_encrypt,
+ .decrypt_mpdu = lib80211_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_wep_set_key,
+ .get_key = lib80211_wep_get_key,
+ .print_stats = lib80211_wep_print_stats,
+ .extra_mpdu_prefix_len = 4, /* IV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_wep_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_wep);
+}
+
+static void __exit lib80211_crypto_wep_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
+}
+
+module_init(lib80211_crypto_wep_init);
+module_exit(lib80211_crypto_wep_exit);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 572793c8c7ab..1e728fff474e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = BUS_ID_SIZE-1 },
+ [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = NL80211_MAX_SUPP_RATES },
[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
- [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_MESH_ID_LEN },
[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+
+ [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (!nl_band)
goto nla_put_failure;
+ /* add HT info */
+ if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
+ NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+ sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
+ &dev->wiphy.bands[band]->ht_cap.mcs);
+ NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
+ dev->wiphy.bands[band]->ht_cap.cap);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ dev->wiphy.bands[band]->ht_cap.ampdu_factor);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+ dev->wiphy.bands[band]->ht_cap.ampdu_density);
+ }
+
/* add frequencies */
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (chan->flags & IEEE80211_CHAN_RADAR)
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power));
+
nla_nest_end(msg, nl_freq);
}
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
return -ENOBUFS;
}
+static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
+ [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
+ [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
+};
+
+static int parse_txq_params(struct nlattr *tb[],
+ struct ieee80211_txq_params *txq_params)
+{
+ if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
+ !tb[NL80211_TXQ_ATTR_AIFS])
+ return -EINVAL;
+
+ txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+ txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
+ txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
+ txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
+ txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+
+ return 0;
+}
+
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
- int result;
-
- if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
- return -EINVAL;
+ int result = 0, rem_txq_params = 0;
+ struct nlattr *nl_txq_params;
rdev = cfg80211_get_dev_from_info(info);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+ result = cfg80211_dev_rename(
+ rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (result)
+ goto bad_res;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
+ struct ieee80211_txq_params txq_params;
+ struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
+
+ if (!rdev->ops->set_txq_params) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ nla_for_each_nested(nl_txq_params,
+ info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
+ rem_txq_params) {
+ nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+ nla_data(nl_txq_params),
+ nla_len(nl_txq_params),
+ txq_params_policy);
+ result = parse_txq_params(tb, &txq_params);
+ if (result)
+ goto bad_res;
+
+ result = rdev->ops->set_txq_params(&rdev->wiphy,
+ &txq_params);
+ if (result)
+ goto bad_res;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ struct ieee80211_channel *chan;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ u32 freq, sec_freq;
+
+ if (!rdev->ops->set_channel) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ result = -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ channel_type = nla_get_u32(info->attrs[
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ channel_type != NL80211_CHAN_HT20 &&
+ channel_type != NL80211_CHAN_HT40PLUS &&
+ channel_type != NL80211_CHAN_HT40MINUS)
+ goto bad_res;
+ }
+
+ freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ chan = ieee80211_get_channel(&rdev->wiphy, freq);
+
+ /* Primary channel not allowed */
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+
+ if (channel_type == NL80211_CHAN_HT40MINUS)
+ sec_freq = freq - 20;
+ else if (channel_type == NL80211_CHAN_HT40PLUS)
+ sec_freq = freq + 20;
+ else
+ sec_freq = 0;
+
+ ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+ /* no HT capabilities */
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ !ht_cap->ht_supported)
+ goto bad_res;
+
+ if (sec_freq) {
+ struct ieee80211_channel *schan;
+
+ /* no 40 MHz capabilities */
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+ goto bad_res;
+
+ schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
+
+ /* Secondary channel not allowed */
+ if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+ }
+
+ result = rdev->ops->set_channel(&rdev->wiphy, chan,
+ channel_type);
+ if (result)
+ goto bad_res;
+ }
+
+
+ bad_res:
cfg80211_put_dev(rdev);
return result;
}
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
return 0;
}
+static u16 nl80211_calculate_bitrate(struct rate_info *rate)
+{
+ int modulation, streams, bitrate;
+
+ if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+ return rate->legacy;
+
+ /* the formula below does only work for MCS values smaller than 32 */
+ if (rate->mcs >= 32)
+ return 0;
+
+ modulation = rate->mcs & 7;
+ streams = (rate->mcs >> 3) + 1;
+
+ bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
+ 13500000 : 6500000;
+
+ if (modulation < 4)
+ bitrate *= (modulation + 1);
+ else if (modulation == 4)
+ bitrate *= (modulation + 2);
+ else
+ bitrate *= (modulation + 3);
+
+ bitrate *= streams;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
int flags, struct net_device *dev,
u8 *mac_addr, struct station_info *sinfo)
{
void *hdr;
- struct nlattr *sinfoattr;
+ struct nlattr *sinfoattr, *txrate;
+ u16 bitrate;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
if (!hdr)
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
if (sinfo->filled & STATION_INFO_PLINK_STATE)
NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
sinfo->plink_state);
+ if (sinfo->filled & STATION_INFO_SIGNAL)
+ NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
+ sinfo->signal);
+ if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+ txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
+ if (!txrate)
+ goto nla_put_failure;
+
+ /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
+ bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+ if (bitrate > 0)
+ NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
+
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
+ NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
+ sinfo->txrate.mcs);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
+ nla_nest_end(msg, txrate);
+ }
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
params.use_short_slot_time =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ params.basic_rates =
+ nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ params.basic_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ }
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
@@ -1680,11 +1887,188 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
#endif
mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
+ r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
return r;
}
+static int nl80211_get_mesh_params(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct mesh_config cur_params;
+ int err;
+ struct net_device *dev;
+ void *hdr;
+ struct nlattr *pinfoattr;
+ struct sk_buff *msg;
+
+ /* Look up our device */
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ /* Get the mesh params */
+ rtnl_lock();
+ err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
+ rtnl_unlock();
+ if (err)
+ goto out;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_MESH_PARAMS);
+ if (!hdr)
+ goto nla_put_failure;
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
+ if (!pinfoattr)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
+ cur_params.dot11MeshRetryTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ cur_params.dot11MeshConfirmTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
+ cur_params.dot11MeshHoldingTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ cur_params.dot11MeshMaxPeerLinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
+ cur_params.dot11MeshMaxRetries);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
+ cur_params.dot11MeshTTL);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ cur_params.auto_open_plinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ cur_params.dot11MeshHWMPmaxPREQretries);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
+ cur_params.path_refresh_time);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ cur_params.min_discovery_timeout);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ cur_params.dot11MeshHWMPactivePathTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ cur_params.dot11MeshHWMPpreqMinInterval);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+ nla_nest_end(msg, pinfoattr);
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ err = -EMSGSIZE;
+out:
+ /* Cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
+do {\
+ if (table[attr_num]) {\
+ cfg.param = nla_fn(table[attr_num]); \
+ mask |= (1 << (attr_num - 1)); \
+ } \
+} while (0);\
+
+static struct nla_policy
+nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
+ [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+
+ [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+};
+
+static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ u32 mask;
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct mesh_config cfg;
+ struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
+ struct nlattr *parent_attr;
+
+ parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
+ if (!parent_attr)
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
+ parent_attr, nl80211_meshconf_params_policy))
+ return -EINVAL;
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ /* This makes sure that there aren't more than 32 mesh config
+ * parameters (otherwise our bitfield scheme would not work.) */
+ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
+
+ /* Fill in the params struct */
+ mask = 0;
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
+ mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
+ mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
+ mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
+ mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
+ mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
+ mask, NL80211_MESHCONF_TTL, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
+ mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
+ mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
+ mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
+ mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+ mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
+ mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshHWMPnetDiameterTraversalTime,
+ mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ nla_get_u16);
+
+ /* Apply changes */
+ rtnl_lock();
+ err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
+ rtnl_unlock();
+
+ /* cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#undef FILL_IN_MESH_PARAM_IF_SET
+
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -1743,12 +2127,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
mutex_lock(&cfg80211_drv_mutex);
r = set_regdom(rd);
mutex_unlock(&cfg80211_drv_mutex);
- if (r)
- goto bad_reg;
-
return r;
-bad_reg:
+ bad_reg:
kfree(rd);
return -EINVAL;
}
@@ -1902,6 +2283,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_GET_MESH_PARAMS,
+ .doit = nl80211_get_mesh_params,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_MESH_PARAMS,
+ .doit = nl80211_set_mesh_params,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
/* multicast groups */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index eb3b1a9f9b12..4f877535e666 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -42,17 +42,40 @@
#include "core.h"
#include "reg.h"
-/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */
+/**
+ * struct regulatory_request - receipt of last regulatory request
+ *
+ * @wiphy: this is set if this request's initiator is
+ * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
+ * can be used by the wireless core to deal with conflicts
+ * and potentially inform users of which devices specifically
+ * cased the conflicts.
+ * @initiator: indicates who sent this request, could be any of
+ * of those set in reg_set_by, %REGDOM_SET_BY_*
+ * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested
+ * regulatory domain. We have a few special codes:
+ * 00 - World regulatory domain
+ * 99 - built by driver but a specific alpha2 cannot be determined
+ * 98 - result of an intersection between two regulatory domains
+ * @intersect: indicates whether the wireless core should intersect
+ * the requested regulatory domain with the presently set regulatory
+ * domain.
+ * @country_ie_checksum: checksum of the last processed and accepted
+ * country IE
+ * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+ * indoor, or if it doesn't matter
+ */
struct regulatory_request {
- struct list_head list;
struct wiphy *wiphy;
- int granted;
enum reg_set_by initiator;
char alpha2[2];
+ bool intersect;
+ u32 country_ie_checksum;
+ enum environment_cap country_ie_env;
};
-static LIST_HEAD(regulatory_requests);
-DEFINE_MUTEX(cfg80211_reg_mutex);
+/* Receipt of information from last regulatory request */
+static struct regulatory_request *last_request;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
@@ -63,13 +86,16 @@ static u32 supported_bandwidths[] = {
MHZ_TO_KHZ(20),
};
-static struct list_head regulatory_requests;
-
/* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2 */
static const struct ieee80211_regdomain *cfg80211_regdomain;
+/* We use this as a place for the rd structure built from the
+ * last parsed country IE to rest until CRDA gets back to us with
+ * what it thinks should apply for the same country */
+static const struct ieee80211_regdomain *country_ie_regdomain;
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 1,
@@ -204,7 +230,7 @@ static void reset_regdomains(void)
* core upon initialization */
static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{
- BUG_ON(list_empty(&regulatory_requests));
+ BUG_ON(!last_request);
reset_regdomains();
@@ -249,6 +275,18 @@ static bool is_unknown_alpha2(const char *alpha2)
return false;
}
+static bool is_intersected_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ /* Special case where regulatory domain is the
+ * result of an intersection between two regulatory domain
+ * structures */
+ if (alpha2[0] == '9' && alpha2[1] == '8')
+ return true;
+ return false;
+}
+
static bool is_an_alpha2(const char *alpha2)
{
if (!alpha2)
@@ -277,6 +315,25 @@ static bool regdom_changed(const char *alpha2)
return true;
}
+/**
+ * country_ie_integrity_changes - tells us if the country IE has changed
+ * @checksum: checksum of country IE of fields we are interested in
+ *
+ * If the country IE has not changed you can ignore it safely. This is
+ * useful to determine if two devices are seeing two different country IEs
+ * even on the same alpha2. Note that this will return false if no IE has
+ * been set on the wireless core yet.
+ */
+static bool country_ie_integrity_changes(u32 checksum)
+{
+ /* If no IE has been set then the checksum doesn't change */
+ if (unlikely(!last_request->country_ie_checksum))
+ return false;
+ if (unlikely(last_request->country_ie_checksum != checksum))
+ return true;
+ return false;
+}
+
/* This lets us keep regulatory code which is updated on a regulatory
* basis in userspace. */
static int call_crda(const char *alpha2)
@@ -300,121 +357,13 @@ static int call_crda(const char *alpha2)
return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, envp);
}
-/* This has the logic which determines when a new request
- * should be ignored. */
-static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
- char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *last_request = NULL;
-
- /* All initial requests are respected */
- if (list_empty(&regulatory_requests))
- return 0;
-
- last_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
- switch (set_by) {
- case REGDOM_SET_BY_INIT:
- return -EINVAL;
- case REGDOM_SET_BY_CORE:
- /* Always respect new wireless core hints, should only
- * come in for updating the world regulatory domain at init
- * anyway */
- return 0;
- case REGDOM_SET_BY_COUNTRY_IE:
- if (last_request->initiator == set_by) {
- if (last_request->wiphy != wiphy) {
- /* Two cards with two APs claiming different
- * different Country IE alpha2s!
- * You're special!! */
- if (!alpha2_equal(last_request->alpha2,
- cfg80211_regdomain->alpha2)) {
- /* XXX: Deal with conflict, consider
- * building a new one out of the
- * intersection */
- WARN_ON(1);
- return -EOPNOTSUPP;
- }
- return -EALREADY;
- }
- /* Two consecutive Country IE hints on the same wiphy */
- if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
- return 0;
- return -EALREADY;
- }
- if (WARN(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2),
- "Invalid Country IE regulatory hint passed "
- "to the wireless core\n"))
- return -EINVAL;
- /* We ignore Country IE hints for now, as we haven't yet
- * added the dot11MultiDomainCapabilityEnabled flag
- * for wiphys */
- return 1;
- case REGDOM_SET_BY_DRIVER:
- BUG_ON(!wiphy);
- if (last_request->initiator == set_by) {
- /* Two separate drivers hinting different things,
- * this is possible if you have two devices present
- * on a system with different EEPROM regulatory
- * readings. XXX: Do intersection, we support only
- * the first regulatory hint for now */
- if (last_request->wiphy != wiphy)
- return -EALREADY;
- if (rd)
- return -EALREADY;
- /* Driver should not be trying to hint different
- * regulatory domains! */
- BUG_ON(!alpha2_equal(alpha2,
- cfg80211_regdomain->alpha2));
- return -EALREADY;
- }
- if (last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* XXX: Handle intersection, and add the
- * dot11MultiDomainCapabilityEnabled flag to wiphy. For now
- * we assume the driver has this set to false, following the
- * 802.11d dot11MultiDomainCapabilityEnabled documentation */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return 0;
- return 0;
- case REGDOM_SET_BY_USER:
- if (last_request->initiator == set_by ||
- last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* Drivers can use their wiphy's reg_notifier()
- * to override any information */
- if (last_request->initiator == REGDOM_SET_BY_DRIVER)
- return 0;
- /* XXX: Handle intersection */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return -EOPNOTSUPP;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static bool __reg_is_valid_request(const char *alpha2,
- struct regulatory_request **request)
-{
- struct regulatory_request *req;
- if (list_empty(&regulatory_requests))
- return false;
- list_for_each_entry(req, &regulatory_requests, list) {
- if (alpha2_equal(req->alpha2, alpha2)) {
- *request = req;
- return true;
- }
- }
- return false;
-}
-
/* Used by nl80211 before kmalloc'ing our regulatory domain */
bool reg_is_valid_request(const char *alpha2)
{
- struct regulatory_request *request = NULL;
- return __reg_is_valid_request(alpha2, &request);
+ if (!last_request)
+ return false;
+
+ return alpha2_equal(last_request->alpha2, alpha2);
}
/* Sanity check on a regulatory rule */
@@ -423,7 +372,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
u32 freq_diff;
- if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
+ if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
return false;
if (freq_range->start_freq_khz > freq_range->end_freq_khz)
@@ -431,7 +380,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
- if (freq_range->max_bandwidth_khz > freq_diff)
+ if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff)
return false;
return true;
@@ -445,6 +394,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
if (!rd->n_reg_rules)
return false;
+ if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
+ return false;
+
for (i = 0; i < rd->n_reg_rules; i++) {
reg_rule = &rd->reg_rules[i];
if (!is_valid_reg_rule(reg_rule))
@@ -469,6 +421,311 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
return 0;
}
+/* Converts a country IE to a regulatory domain. A regulatory domain
+ * structure has a lot of information which the IE doesn't yet have,
+ * so for the other values we use upper max values as we will intersect
+ * with our userspace regulatory agent to get lower bounds. */
+static struct ieee80211_regdomain *country_ie_2_rd(
+ u8 *country_ie,
+ u8 country_ie_len,
+ u32 *checksum)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ unsigned int i = 0;
+ char alpha2[2];
+ u32 flags = 0;
+ u32 num_rules = 0, size_of_regd = 0;
+ u8 *triplets_start = NULL;
+ u8 len_at_triplet = 0;
+ /* the last channel we have registered in a subband (triplet) */
+ int last_sub_max_channel = 0;
+
+ *checksum = 0xDEADBEEF;
+
+ /* Country IE requirements */
+ BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN ||
+ country_ie_len & 0x01);
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ /*
+ * Third octet can be:
+ * 'I' - Indoor
+ * 'O' - Outdoor
+ *
+ * anything else we assume is no restrictions
+ */
+ if (country_ie[2] == 'I')
+ flags = NL80211_RRF_NO_OUTDOOR;
+ else if (country_ie[2] == 'O')
+ flags = NL80211_RRF_NO_INDOOR;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+
+ triplets_start = country_ie;
+ len_at_triplet = country_ie_len;
+
+ *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
+
+ /* We need to build a reg rule for each triplet, but first we must
+ * calculate the number of reg rules we will need. We will need one
+ * for each channel subband */
+ while (country_ie_len >= 3) {
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ int cur_sub_max_channel = 0, cur_channel = 0;
+
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ cur_channel = triplet->chans.first_channel;
+ cur_sub_max_channel = ieee80211_channel_to_frequency(
+ cur_channel + triplet->chans.num_channels);
+
+ /* Basic sanity check */
+ if (cur_sub_max_channel < cur_channel)
+ return NULL;
+
+ /* Do not allow overlapping channels. Also channels
+ * passed in each subband must be monotonically
+ * increasing */
+ if (last_sub_max_channel) {
+ if (cur_channel <= last_sub_max_channel)
+ return NULL;
+ if (cur_sub_max_channel <= last_sub_max_channel)
+ return NULL;
+ }
+
+ /* When dot11RegulatoryClassesRequired is supported
+ * we can throw ext triplets as part of this soup,
+ * for now we don't care when those change as we
+ * don't support them */
+ *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
+ ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
+ ((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
+
+ last_sub_max_channel = cur_sub_max_channel;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ num_rules++;
+
+ /* Note: this is not a IEEE requirement but
+ * simply a memory requirement */
+ if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+ return NULL;
+ }
+
+ country_ie = triplets_start;
+ country_ie_len = len_at_triplet;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ (num_rules * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = alpha2[0];
+ rd->alpha2[1] = alpha2[1];
+
+ /* This time around we fill in the rd */
+ while (country_ie_len >= 3) {
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ struct ieee80211_reg_rule *reg_rule = NULL;
+ struct ieee80211_freq_range *freq_range = NULL;
+ struct ieee80211_power_rule *power_rule = NULL;
+
+ /* Must parse if dot11RegulatoryClassesRequired is true,
+ * we don't support this yet */
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ reg_rule = &rd->reg_rules[i];
+ freq_range = &reg_rule->freq_range;
+ power_rule = &reg_rule->power_rule;
+
+ reg_rule->flags = flags;
+
+ /* The +10 is since the regulatory domain expects
+ * the actual band edge, not the center of freq for
+ * its start and end freqs, assuming 20 MHz bandwidth on
+ * the channels passed */
+ freq_range->start_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel) - 10);
+ freq_range->end_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel +
+ triplet->chans.num_channels) + 10);
+
+ /* Large arbitrary values, we intersect later */
+ /* Increment this if we ever support >= 40 MHz channels
+ * in IEEE 802.11 */
+ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+ power_rule->max_antenna_gain = DBI_TO_MBI(100);
+ power_rule->max_eirp = DBM_TO_MBM(100);
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ i++;
+
+ BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+ }
+
+ return rd;
+}
+
+
+/* Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun */
+static int reg_rules_intersect(
+ const struct ieee80211_reg_rule *rule1,
+ const struct ieee80211_reg_rule *rule2,
+ struct ieee80211_reg_rule *intersected_rule)
+{
+ const struct ieee80211_freq_range *freq_range1, *freq_range2;
+ struct ieee80211_freq_range *freq_range;
+ const struct ieee80211_power_rule *power_rule1, *power_rule2;
+ struct ieee80211_power_rule *power_rule;
+ u32 freq_diff;
+
+ freq_range1 = &rule1->freq_range;
+ freq_range2 = &rule2->freq_range;
+ freq_range = &intersected_rule->freq_range;
+
+ power_rule1 = &rule1->power_rule;
+ power_rule2 = &rule2->power_rule;
+ power_rule = &intersected_rule->power_rule;
+
+ freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
+ freq_range2->start_freq_khz);
+ freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
+ freq_range2->end_freq_khz);
+ freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
+ freq_range2->max_bandwidth_khz);
+
+ freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
+ if (freq_range->max_bandwidth_khz > freq_diff)
+ freq_range->max_bandwidth_khz = freq_diff;
+
+ power_rule->max_eirp = min(power_rule1->max_eirp,
+ power_rule2->max_eirp);
+ power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
+ power_rule2->max_antenna_gain);
+
+ intersected_rule->flags = (rule1->flags | rule2->flags);
+
+ if (!is_valid_reg_rule(intersected_rule))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * regdom_intersect - do the intersection between two regulatory domains
+ * @rd1: first regulatory domain
+ * @rd2: second regulatory domain
+ *
+ * Use this function to get the intersection between two regulatory domains.
+ * Once completed we will mark the alpha2 for the rd as intersected, "98",
+ * as no one single alpha2 can represent this regulatory domain.
+ *
+ * Returns a pointer to the regulatory domain structure which will hold the
+ * resulting intersection of rules between rd1 and rd2. We will
+ * kzalloc() this structure for you.
+ */
+static struct ieee80211_regdomain *regdom_intersect(
+ const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2)
+{
+ int r, size_of_regd;
+ unsigned int x, y;
+ unsigned int num_rules = 0, rule_idx = 0;
+ const struct ieee80211_reg_rule *rule1, *rule2;
+ struct ieee80211_reg_rule *intersected_rule;
+ struct ieee80211_regdomain *rd;
+ /* This is just a dummy holder to help us count */
+ struct ieee80211_reg_rule irule;
+
+ /* Uses the stack temporarily for counter arithmetic */
+ intersected_rule = &irule;
+
+ memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
+
+ if (!rd1 || !rd2)
+ return NULL;
+
+ /* First we get a count of the rules we'll need, then we actually
+ * build them. This is to so we can malloc() and free() a
+ * regdomain once. The reason we use reg_rules_intersect() here
+ * is it will return -EINVAL if the rule computed makes no sense.
+ * All rules that do check out OK are valid. */
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ if (!reg_rules_intersect(rule1, rule2,
+ intersected_rule))
+ num_rules++;
+ memset(intersected_rule, 0,
+ sizeof(struct ieee80211_reg_rule));
+ }
+ }
+
+ if (!num_rules)
+ return NULL;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ /* This time around instead of using the stack lets
+ * write to the target rule directly saving ourselves
+ * a memcpy() */
+ intersected_rule = &rd->reg_rules[rule_idx];
+ r = reg_rules_intersect(rule1, rule2,
+ intersected_rule);
+ /* No need to memset here the intersected rule here as
+ * we're not using the stack anymore */
+ if (r)
+ continue;
+ rule_idx++;
+ }
+ }
+
+ if (rule_idx != num_rules) {
+ kfree(rd);
+ return NULL;
+ }
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = '9';
+ rd->alpha2[1] = '8';
+
+ return rd;
+}
+
/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
* want to just have the channel structure use these */
static u32 map_regdom_flags(u32 rd_flags)
@@ -559,12 +816,23 @@ static void handle_band(struct ieee80211_supported_band *sband)
handle_channel(&sband->channels[i]);
}
+static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
+{
+ if (!last_request)
+ return true;
+ if (setby == REGDOM_SET_BY_CORE &&
+ wiphy->fw_handles_regulatory)
+ return true;
+ return false;
+}
+
static void update_all_wiphy_regulatory(enum reg_set_by setby)
{
struct cfg80211_registered_device *drv;
list_for_each_entry(drv, &cfg80211_drv_list, list)
- wiphy_update_regulatory(&drv->wiphy, setby);
+ if (!ignore_reg_update(&drv->wiphy, setby))
+ wiphy_update_regulatory(&drv->wiphy, setby);
}
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
@@ -578,78 +846,237 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
}
}
-/* Caller must hold &cfg80211_drv_mutex */
-int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *request;
- char *rd_alpha2;
- int r = 0;
-
- r = ignore_request(wiphy, set_by, (char *) alpha2, rd);
- if (r)
- return r;
+/* Return value which can be used by ignore_request() to indicate
+ * it has been determined we should intersect two regulatory domains */
+#define REG_INTERSECT 1
- if (rd)
- rd_alpha2 = rd->alpha2;
- else
- rd_alpha2 = (char *) alpha2;
+/* This has the logic which determines when a new request
+ * should be ignored. */
+static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2)
+{
+ /* All initial requests are respected */
+ if (!last_request)
+ return 0;
switch (set_by) {
+ case REGDOM_SET_BY_INIT:
+ return -EINVAL;
case REGDOM_SET_BY_CORE:
+ /*
+ * Always respect new wireless core hints, should only happen
+ * when updating the world regulatory domain at init.
+ */
+ return 0;
case REGDOM_SET_BY_COUNTRY_IE:
+ if (unlikely(!is_an_alpha2(alpha2)))
+ return -EINVAL;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy != wiphy) {
+ /*
+ * Two cards with two APs claiming different
+ * different Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
+ */
+ if (!alpha2_equal(alpha2,
+ cfg80211_regdomain->alpha2))
+ return -EOPNOTSUPP;
+ return -EALREADY;
+ }
+ /* Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack */
+ if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
+ alpha2)))
+ return 0;
+ return -EALREADY;
+ }
+ return REG_INTERSECT;
case REGDOM_SET_BY_DRIVER:
+ if (last_request->initiator == REGDOM_SET_BY_DRIVER)
+ return -EALREADY;
+ return 0;
case REGDOM_SET_BY_USER:
- request = kzalloc(sizeof(struct regulatory_request),
- GFP_KERNEL);
- if (!request)
- return -ENOMEM;
-
- request->alpha2[0] = rd_alpha2[0];
- request->alpha2[1] = rd_alpha2[1];
- request->initiator = set_by;
- request->wiphy = wiphy;
-
- list_add_tail(&request->list, &regulatory_requests);
- if (rd)
- break;
- r = call_crda(alpha2);
-#ifndef CONFIG_WIRELESS_OLD_REGULATORY
- if (r)
- printk(KERN_ERR "cfg80211: Failed calling CRDA\n");
-#endif
- break;
- default:
- r = -ENOTSUPP;
- break;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+ return REG_INTERSECT;
+ /* If the user knows better the user should set the regdom
+ * to their country before the IE is picked up */
+ if (last_request->initiator == REGDOM_SET_BY_USER &&
+ last_request->intersect)
+ return -EOPNOTSUPP;
+ return 0;
}
- return r;
+ return -EINVAL;
}
-/* If rd is not NULL and if this call fails the caller must free it */
-int regulatory_hint(struct wiphy *wiphy, const char *alpha2,
- struct ieee80211_regdomain *rd)
+/* Caller must hold &cfg80211_drv_mutex */
+int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2,
+ u32 country_ie_checksum,
+ enum environment_cap env)
{
- int r;
- BUG_ON(!rd && !alpha2);
+ struct regulatory_request *request;
+ bool intersect = false;
+ int r = 0;
- mutex_lock(&cfg80211_drv_mutex);
+ r = ignore_request(wiphy, set_by, alpha2);
- r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd);
- if (r || !rd)
- goto unlock_and_exit;
+ if (r == REG_INTERSECT)
+ intersect = true;
+ else if (r)
+ return r;
- /* If the driver passed a regulatory domain we skipped asking
- * userspace for one so we can now go ahead and set it */
- r = set_regdom(rd);
+ request = kzalloc(sizeof(struct regulatory_request),
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = set_by;
+ request->wiphy = wiphy;
+ request->intersect = intersect;
+ request->country_ie_checksum = country_ie_checksum;
+ request->country_ie_env = env;
+
+ kfree(last_request);
+ last_request = request;
+ /*
+ * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
+ * AND if CRDA is NOT present nothing will happen, if someone
+ * wants to bother with 11d with OLD_REG you can add a timer.
+ * If after x amount of time nothing happens you can call:
+ *
+ * return set_regdom(country_ie_regdomain);
+ *
+ * to intersect with the static rd
+ */
+ return call_crda(alpha2);
+}
+
+void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+{
+ BUG_ON(!alpha2);
-unlock_and_exit:
+ mutex_lock(&cfg80211_drv_mutex);
+ __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
- return r;
}
EXPORT_SYMBOL(regulatory_hint);
+static bool reg_same_country_ie_hint(struct wiphy *wiphy,
+ u32 country_ie_checksum)
+{
+ if (!last_request->wiphy)
+ return false;
+ if (likely(last_request->wiphy != wiphy))
+ return !country_ie_integrity_changes(country_ie_checksum);
+ /* We should not have let these through at this point, they
+ * should have been picked up earlier by the first alpha2 check
+ * on the device */
+ if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
+ return true;
+ return false;
+}
+
+void regulatory_hint_11d(struct wiphy *wiphy,
+ u8 *country_ie,
+ u8 country_ie_len)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ char alpha2[2];
+ u32 checksum = 0;
+ enum environment_cap env = ENVIRON_ANY;
+
+ if (!last_request)
+ return;
+
+ mutex_lock(&cfg80211_drv_mutex);
+
+ /* IE len must be evenly divisible by 2 */
+ if (country_ie_len & 0x01)
+ goto out;
+
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+ goto out;
+
+ /* Pending country IE processing, this can happen after we
+ * call CRDA and wait for a response if a beacon was received before
+ * we were able to process the last regulatory_hint_11d() call */
+ if (country_ie_regdomain)
+ goto out;
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ if (country_ie[2] == 'I')
+ env = ENVIRON_INDOOR;
+ else if (country_ie[2] == 'O')
+ env = ENVIRON_OUTDOOR;
+
+ /* We will run this for *every* beacon processed for the BSSID, so
+ * we optimize an early check to exit out early if we don't have to
+ * do anything */
+ if (likely(last_request->wiphy)) {
+ struct cfg80211_registered_device *drv_last_ie;
+
+ drv_last_ie = wiphy_to_dev(last_request->wiphy);
+
+ /* Lets keep this simple -- we trust the first AP
+ * after we intersect with CRDA */
+ if (likely(last_request->wiphy == wiphy)) {
+ /* Ignore IEs coming in on this wiphy with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* the wiphy moved on to another BSSID or the AP
+ * was reconfigured. XXX: We need to deal with the
+ * case where the user suspends and goes to goes
+ * to another country, and then gets IEs from an
+ * AP with different settings */
+ goto out;
+ } else {
+ /* Ignore IEs coming in on two separate wiphys with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* We could potentially intersect though */
+ goto out;
+ }
+ }
+
+ rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
+ if (!rd)
+ goto out;
+
+ /* This will not happen right now but we leave it here for the
+ * the future when we want to add suspend/resume support and having
+ * the user move to another country after doing so, or having the user
+ * move to another AP. Right now we just trust the first AP. This is why
+ * this is marked as likley(). If we hit this before we add this support
+ * we want to be informed of it as it would indicate a mistake in the
+ * current design */
+ if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
+ goto out;
+
+ /* We keep this around for when CRDA comes back with a response so
+ * we can intersect with that */
+ country_ie_regdomain = rd;
+
+ __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
+ country_ie_regdomain->alpha2, checksum, env);
+
+out:
+ mutex_unlock(&cfg80211_drv_mutex);
+}
+EXPORT_SYMBOL(regulatory_hint_11d);
static void print_rd_rules(const struct ieee80211_regdomain *rd)
{
@@ -689,7 +1116,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
- if (is_world_regdom(rd->alpha2))
+ if (is_intersected_alpha2(rd->alpha2)) {
+ struct wiphy *wiphy = NULL;
+ struct cfg80211_registered_device *drv;
+
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy) {
+ wiphy = last_request->wiphy;
+ drv = wiphy_to_dev(wiphy);
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain updated by AP to: %c%c\n",
+ drv->country_ie_alpha2[0],
+ drv->country_ie_alpha2[1]);
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain intersected: \n");
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "intersected: \n");
+ } else if (is_world_regdom(rd->alpha2))
printk(KERN_INFO "cfg80211: World regulatory "
"domain updated:\n");
else {
@@ -705,21 +1150,50 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-void print_regdomain_info(const struct ieee80211_regdomain *rd)
+static void print_regdomain_info(const struct ieee80211_regdomain *rd)
{
printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n",
rd->alpha2[0], rd->alpha2[1]);
print_rd_rules(rd);
}
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+#ifdef CONFIG_CFG80211_REG_DEBUG
+static void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
{
- struct regulatory_request *request = NULL;
+ printk(KERN_DEBUG "cfg80211: Received country IE:\n");
+ print_regdomain_info(country_ie_regdomain);
+ printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
+ print_regdomain_info(rd);
+ if (intersected_rd) {
+ printk(KERN_DEBUG "cfg80211: We intersect both of these "
+ "and get:\n");
+ print_regdomain_info(rd);
+ return;
+ }
+ printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
+}
+#else
+static inline void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
+{
+}
+#endif
+/* Takes ownership of rd only if it doesn't fail */
+static int __set_regdom(const struct ieee80211_regdomain *rd)
+{
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ struct cfg80211_registered_device *drv = NULL;
+ struct wiphy *wiphy = NULL;
/* Some basic sanity checks first */
if (is_world_regdom(rd->alpha2)) {
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
update_world_regdomain(rd);
return 0;
@@ -729,45 +1203,102 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
!is_unknown_alpha2(rd->alpha2))
return -EINVAL;
- if (list_empty(&regulatory_requests))
+ if (!last_request)
return -EINVAL;
- /* allow overriding the static definitions if CRDA is present */
- if (!is_old_static_regdom(cfg80211_regdomain) &&
- !regdom_changed(rd->alpha2))
- return -EINVAL;
+ /* Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE */
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+ /* If someone else asked us to change the rd lets only bother
+ * checking if the alpha2 changes if CRDA was already called */
+ if (!is_old_static_regdom(cfg80211_regdomain) &&
+ !regdom_changed(rd->alpha2))
+ return -EINVAL;
+ }
+
+ wiphy = last_request->wiphy;
/* Now lets set the regulatory domain, update all driver channels
* and finally inform them of what we have done, in case they want
* to review or adjust their own settings based on their own
* internal EEPROM data */
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
- reset_regdomains();
+ if (!is_valid_rd(rd)) {
+ printk(KERN_ERR "cfg80211: Invalid "
+ "regulatory domain detected:\n");
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
- /* Country IE parsing coming soon */
- switch (request->initiator) {
- case REGDOM_SET_BY_CORE:
- case REGDOM_SET_BY_DRIVER:
- case REGDOM_SET_BY_USER:
- if (!is_valid_rd(rd)) {
- printk(KERN_ERR "cfg80211: Invalid "
- "regulatory domain detected:\n");
- print_regdomain_info(rd);
+ if (!last_request->intersect) {
+ reset_regdomains();
+ cfg80211_regdomain = rd;
+ return 0;
+ }
+
+ /* Intersection requires a bit more work */
+
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ if (!intersected_rd)
return -EINVAL;
- }
- break;
- case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */
- WARN_ON(1);
- default:
- return -EOPNOTSUPP;
+
+ /* We can trash what CRDA provided now */
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
+
+ return 0;
}
- /* Tada! */
- cfg80211_regdomain = rd;
- request->granted = 1;
+ /*
+ * Country IE requests are handled a bit differently, we intersect
+ * the country IE rd with what CRDA believes that country should have
+ */
+
+ BUG_ON(!country_ie_regdomain);
+
+ if (rd != country_ie_regdomain) {
+ /* Intersect what CRDA returned and our what we
+ * had built from the Country IE received */
+
+ intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+
+ reg_country_ie_process_debug(rd, country_ie_regdomain,
+ intersected_rd);
+
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+ } else {
+ /* This would happen when CRDA was not present and
+ * OLD_REGULATORY was enabled. We intersect our Country
+ * IE rd and what was set on cfg80211 originally */
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ }
+
+ if (!intersected_rd)
+ return -EINVAL;
+
+ drv = wiphy_to_dev(wiphy);
+
+ drv->country_ie_alpha2[0] = rd->alpha2[0];
+ drv->country_ie_alpha2[1] = rd->alpha2[1];
+ drv->env = last_request->country_ie_env;
+
+ BUG_ON(intersected_rd == rd);
+
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
return 0;
}
@@ -775,52 +1306,41 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
/* Use this call to set the current regulatory domain. Conflicts with
* multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. If this calls fails you should kfree()
- * the passed rd. Caller must hold cfg80211_drv_mutex */
+ * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */
int set_regdom(const struct ieee80211_regdomain *rd)
{
- struct regulatory_request *this_request = NULL, *prev_request = NULL;
int r;
- if (!list_empty(&regulatory_requests))
- prev_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd);
- if (r)
+ if (r) {
+ kfree(rd);
return r;
-
- BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request)));
-
- /* The initial standard core update of the world regulatory domain, no
- * need to keep that request info around if it didn't fail. */
- if (is_world_regdom(rd->alpha2) &&
- this_request->initiator == REGDOM_SET_BY_CORE &&
- this_request->granted) {
- list_del(&this_request->list);
- kfree(this_request);
- this_request = NULL;
- }
-
- /* Remove old requests, we only leave behind the last one */
- if (prev_request) {
- list_del(&prev_request->list);
- kfree(prev_request);
- prev_request = NULL;
}
/* This would make this whole thing pointless */
- BUG_ON(rd != cfg80211_regdomain);
+ if (!last_request->intersect)
+ BUG_ON(rd != cfg80211_regdomain);
/* update all wiphys now with the new established regulatory domain */
- update_all_wiphy_regulatory(this_request->initiator);
+ update_all_wiphy_regulatory(last_request->initiator);
- print_regdomain(rd);
+ print_regdomain(cfg80211_regdomain);
return r;
}
+/* Caller must hold cfg80211_drv_mutex */
+void reg_device_remove(struct wiphy *wiphy)
+{
+ if (!last_request || !last_request->wiphy)
+ return;
+ if (last_request->wiphy != wiphy)
+ return;
+ last_request->wiphy = NULL;
+ last_request->country_ie_env = ENVIRON_ANY;
+}
+
int regulatory_init(void)
{
int err;
@@ -838,13 +1358,13 @@ int regulatory_init(void)
* you have CRDA you get it updated, otherwise you get
* stuck with the static values. We ignore "EU" code as
* that is not a valid ISO / IEC 3166 alpha2 */
- if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U')
+ if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
- ieee80211_regdom, NULL);
+ ieee80211_regdom, 0, ENVIRON_ANY);
#else
cfg80211_regdomain = cfg80211_world_regdom;
- err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL);
+ err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
if (err)
printk(KERN_ERR "cfg80211: calling CRDA failed - "
"unable to update world regulatory domain, "
@@ -856,16 +1376,15 @@ int regulatory_init(void)
void regulatory_exit(void)
{
- struct regulatory_request *req, *req_tmp;
-
mutex_lock(&cfg80211_drv_mutex);
reset_regdomains();
- list_for_each_entry_safe(req, req_tmp, &regulatory_requests, list) {
- list_del(&req->list);
- kfree(req);
- }
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+
+ kfree(last_request);
+
platform_device_unregister(reg_pdev);
mutex_unlock(&cfg80211_drv_mutex);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index a33362872f3c..a76ea3ff7cd6 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -1,13 +1,44 @@
#ifndef __NET_WIRELESS_REG_H
#define __NET_WIRELESS_REG_H
-extern struct mutex cfg80211_reg_mutex;
bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
+void reg_device_remove(struct wiphy *wiphy);
+
int regulatory_init(void);
void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd);
+enum environment_cap {
+ ENVIRON_ANY,
+ ENVIRON_INDOOR,
+ ENVIRON_OUTDOOR,
+};
+
+
+/**
+ * __regulatory_hint - hint to the wireless core a regulatory domain
+ * @wiphy: if the hint comes from country information from an AP, this
+ * is required to be set to the wiphy that received the information
+ * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
+ * should be in.
+ * @country_ie_checksum: checksum of processed country IE, set this to 0
+ * if the hint did not come from a country IE
+ * @country_ie_env: the environment the IE told us we are in, %ENVIRON_*
+ *
+ * The Wireless subsystem can use this function to hint to the wireless core
+ * what it believes should be the current regulatory domain by giving it an
+ * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be
+ * in.
+ *
+ * Returns zero if all went fine, %-EALREADY if a regulatory domain had
+ * already been set or other standard error codes.
+ *
+ */
+extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2, u32 country_ie_checksum,
+ enum environment_cap country_ie_env);
+
#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 29f820e18251..79a382877641 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -23,25 +23,20 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
}
-static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
}
-static ssize_t _show_permaddr(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- unsigned char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
-
- return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
-}
+SHOW_FMT(index, "%d", idx);
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
static struct device_attribute ieee80211_dev_attrs[] = {
- __ATTR(index, S_IRUGO, _show_index, NULL),
- __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
+ __ATTR_RO(index),
+ __ATTR_RO(macaddress),
{}
};
diff --git a/net/wireless/util.c b/net/wireless/util.c
index f54424693a38..e76cc28b0345 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -7,6 +7,25 @@
#include <asm/bitops.h>
#include "core.h"
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u64 basic_rates, int bitrate)
+{
+ struct ieee80211_rate *result = &sband->bitrates[0];
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (!(basic_rates & BIT(i)))
+ continue;
+ if (sband->bitrates[i].bitrate > bitrate)
+ continue;
+ result = &sband->bitrates[i];
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(ieee80211_get_response_rate);
+
int ieee80211_channel_to_frequency(int chan)
{
if (chan < 14)
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 000000000000..58e489fd4aed
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,139 @@
+/*
+ * cfg80211 - wext compat code
+ *
+ * This is temporary code until all wireless functionality is migrated
+ * into cfg80211, when that happens all the exports here go away and
+ * we directly assign the wireless handlers of wireless interfaces.
+ *
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <net/iw_handler.h>
+#include <net/wireless.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_supported_band *sband;
+ bool is_ht = false, is_a = false, is_b = false, is_g = false;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (sband) {
+ is_a = true;
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (sband) {
+ int i;
+ /* Check for mandatory rates */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 10)
+ is_b = true;
+ if (sband->bitrates[i].bitrate == 60)
+ is_g = true;
+ }
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ strcpy(name, "IEEE 802.11");
+ if (is_a)
+ strcat(name, "a");
+ if (is_b)
+ strcat(name, "b");
+ if (is_g)
+ strcat(name, "g");
+ if (is_ht)
+ strcat(name, "n");
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwname);
+
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ struct vif_params vifparams;
+ enum nl80211_iftype type;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_dev(wdev->wiphy);
+
+ if (!rdev->ops->change_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /* don't support changing VLANs, you just re-create them */
+ if (wdev->iftype == NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
+ switch (*mode) {
+ case IW_MODE_INFRA:
+ type = NL80211_IFTYPE_STATION;
+ break;
+ case IW_MODE_ADHOC:
+ type = NL80211_IFTYPE_ADHOC;
+ break;
+ case IW_MODE_REPEAT:
+ type = NL80211_IFTYPE_WDS;
+ break;
+ case IW_MODE_MONITOR:
+ type = NL80211_IFTYPE_MONITOR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ memset(&vifparams, 0, sizeof(vifparams));
+
+ return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
+ NULL, &vifparams);
+}
+EXPORT_SYMBOL(cfg80211_wext_siwmode);
+
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ *mode = IW_MODE_MASTER;
+ break;
+ case NL80211_IFTYPE_STATION:
+ *mode = IW_MODE_INFRA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ *mode = IW_MODE_ADHOC;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ *mode = IW_MODE_MONITOR;
+ break;
+ case NL80211_IFTYPE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ *mode = IW_MODE_SECOND; /* FIXME */
+ break;
+ default:
+ *mode = IW_MODE_AUTO;
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwmode);
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d98ffb75119a..e49a2d1ef1e4 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -64,7 +64,7 @@
* o Remove spy_offset from struct iw_handler_def
* o Start deprecating dev->get_wireless_stats, output a warning
* o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ * o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats)
*
* v8 - 17.02.06 - Jean II
* o RtNetlink requests support (SET/GET)
diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c
index 6ebda25c24e9..a5d3416522de 100644
--- a/net/x25/sysctl_net_x25.c
+++ b/net/x25/sysctl_net_x25.c
@@ -24,8 +24,8 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_restart_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer,
},
@@ -35,8 +35,8 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_call_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer,
},
@@ -46,8 +46,8 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_reset_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer,
},
@@ -57,8 +57,8 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_clear_request_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer,
},
@@ -68,8 +68,8 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_ack_holdback_timeout,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
.extra1 = &min_timer,
.extra2 = &max_timer,
},
@@ -79,7 +79,7 @@ static struct ctl_table x25_table[] = {
.data = &sysctl_x25_forward,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{ 0, },
};
diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile
index 0f439a72ccab..c631047e1b27 100644
--- a/net/xfrm/Makefile
+++ b/net/xfrm/Makefile
@@ -3,8 +3,8 @@
#
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
- xfrm_input.o xfrm_output.o xfrm_algo.o
+ xfrm_input.o xfrm_output.o xfrm_algo.o \
+ xfrm_sysctl.o
obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
obj-$(CONFIG_XFRM_USER) += xfrm_user.o
obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
-
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 75279402ccf4..b4a13178fb40 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -104,6 +104,7 @@ EXPORT_SYMBOL(xfrm_prepare_input);
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
{
+ struct net *net = dev_net(skb->dev);
int err;
__be32 seq;
struct xfrm_state *x;
@@ -127,7 +128,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
sp = secpath_dup(skb->sp);
if (!sp) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
goto drop;
}
if (skb->sp)
@@ -141,19 +142,19 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
seq = 0;
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
goto drop;
}
do {
if (skb->sp->len == XFRM_MAX_DEPTH) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop;
}
- x = xfrm_state_lookup(daddr, spi, nexthdr, family);
+ x = xfrm_state_lookup(net, daddr, spi, nexthdr, family);
if (x == NULL) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINNOSTATES);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
xfrm_audit_state_notfound(skb, family, spi, seq);
goto drop;
}
@@ -162,22 +163,22 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
spin_lock(&x->lock);
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEINVALID);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
goto drop_unlock;
}
if ((x->encap ? x->encap->encap_type : 0) != encap_type) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMISMATCH);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
goto drop_unlock;
}
if (x->props.replay_window && xfrm_replay_check(x, skb, seq)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATESEQERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
goto drop_unlock;
}
if (xfrm_state_check_expire(x)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEEXPIRED);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED);
goto drop_unlock;
}
@@ -198,7 +199,7 @@ resume:
x->type->proto);
x->stats.integrity_failed++;
}
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEPROTOERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
goto drop_unlock;
}
@@ -224,7 +225,7 @@ resume:
}
if (inner_mode->input(x, skb)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
@@ -242,7 +243,7 @@ resume:
err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
if (err < 0) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
goto drop;
}
} while (!err);
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index dc50f1e71f76..c235597ba8dd 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -41,6 +41,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
+ struct net *net = xs_net(x);
if (err <= 0)
goto resume;
@@ -48,33 +49,33 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
do {
err = xfrm_state_check_space(x, skb);
if (err) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
goto error_nolock;
}
err = x->outer_mode->output(x, skb);
if (err) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEMODEERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
goto error_nolock;
}
spin_lock_bh(&x->lock);
err = xfrm_state_check_expire(x);
if (err) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEEXPIRED);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED);
goto error;
}
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
XFRM_SKB_CB(skb)->seq.output = ++x->replay.oseq;
if (unlikely(x->replay.oseq == 0)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATESEQERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR);
x->replay.oseq--;
xfrm_audit_state_replay_overflow(x, skb);
err = -EOVERFLOW;
goto error;
}
- if (xfrm_aevent_is_on())
+ if (xfrm_aevent_is_on(net))
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
}
@@ -89,12 +90,12 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
resume:
if (err) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTSTATEPROTOERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
goto error_nolock;
}
if (!(skb->dst = dst_pop(dst))) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
err = -EHOSTUNREACH;
goto error_nolock;
}
@@ -178,6 +179,7 @@ static int xfrm_output_gso(struct sk_buff *skb)
int xfrm_output(struct sk_buff *skb)
{
+ struct net *net = dev_net(skb->dst->dev);
int err;
if (skb_is_gso(skb))
@@ -186,7 +188,7 @@ int xfrm_output(struct sk_buff *skb)
if (skb->ip_summed == CHECKSUM_PARTIAL) {
err = skb_checksum_help(skb);
if (err) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
kfree_skb(skb);
return err;
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index fb216c9adf86..9c068ab3a834 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -34,28 +34,16 @@
#include "xfrm_hash.h"
-int sysctl_xfrm_larval_drop __read_mostly = 1;
-
-#ifdef CONFIG_XFRM_STATISTICS
-DEFINE_SNMP_STAT(struct linux_xfrm_mib, xfrm_statistics) __read_mostly;
-EXPORT_SYMBOL(xfrm_statistics);
-#endif
-
DEFINE_MUTEX(xfrm_cfg_mutex);
EXPORT_SYMBOL(xfrm_cfg_mutex);
static DEFINE_RWLOCK(xfrm_policy_lock);
-static struct list_head xfrm_policy_all;
-unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
-EXPORT_SYMBOL(xfrm_policy_count);
-
static DEFINE_RWLOCK(xfrm_policy_afinfo_lock);
static struct xfrm_policy_afinfo *xfrm_policy_afinfo[NPROTO];
static struct kmem_cache *xfrm_dst_cache __read_mostly;
-static struct work_struct xfrm_policy_gc_work;
static HLIST_HEAD(xfrm_policy_gc_list);
static DEFINE_SPINLOCK(xfrm_policy_gc_lock);
@@ -63,6 +51,9 @@ static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family);
static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo);
static void xfrm_init_pmtu(struct dst_entry *dst);
+static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
+ int dir);
+
static inline int
__xfrm4_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
@@ -97,7 +88,7 @@ int xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl,
return 0;
}
-static inline struct dst_entry *__xfrm_dst_lookup(int tos,
+static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos,
xfrm_address_t *saddr,
xfrm_address_t *daddr,
int family)
@@ -109,7 +100,7 @@ static inline struct dst_entry *__xfrm_dst_lookup(int tos,
if (unlikely(afinfo == NULL))
return ERR_PTR(-EAFNOSUPPORT);
- dst = afinfo->dst_lookup(tos, saddr, daddr);
+ dst = afinfo->dst_lookup(net, tos, saddr, daddr);
xfrm_policy_put_afinfo(afinfo);
@@ -121,6 +112,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
xfrm_address_t *prev_daddr,
int family)
{
+ struct net *net = xs_net(x);
xfrm_address_t *saddr = &x->props.saddr;
xfrm_address_t *daddr = &x->id.daddr;
struct dst_entry *dst;
@@ -134,7 +126,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos,
daddr = x->coaddr;
}
- dst = __xfrm_dst_lookup(tos, saddr, daddr, family);
+ dst = __xfrm_dst_lookup(net, tos, saddr, daddr, family);
if (!IS_ERR(dst)) {
if (prev_saddr != saddr)
@@ -229,13 +221,14 @@ expired:
* SPD calls.
*/
-struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
+struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
{
struct xfrm_policy *policy;
policy = kzalloc(sizeof(struct xfrm_policy), gfp);
if (policy) {
+ write_pnet(&policy->xp_net, net);
INIT_LIST_HEAD(&policy->walk.all);
INIT_HLIST_NODE(&policy->bydst);
INIT_HLIST_NODE(&policy->byidx);
@@ -296,6 +289,7 @@ static void xfrm_policy_gc_task(struct work_struct *work)
hlist_for_each_entry_safe(policy, entry, tmp, &gc_list, bydst)
xfrm_policy_gc_kill(policy);
}
+static DECLARE_WORK(xfrm_policy_gc_work, xfrm_policy_gc_task);
/* Rule must be locked. Release descentant resources, announce
* entry dead. The rule must be unlinked from lists to the moment.
@@ -322,38 +316,29 @@ static void xfrm_policy_kill(struct xfrm_policy *policy)
schedule_work(&xfrm_policy_gc_work);
}
-struct xfrm_policy_hash {
- struct hlist_head *table;
- unsigned int hmask;
-};
-
-static struct hlist_head xfrm_policy_inexact[XFRM_POLICY_MAX*2];
-static struct xfrm_policy_hash xfrm_policy_bydst[XFRM_POLICY_MAX*2] __read_mostly;
-static struct hlist_head *xfrm_policy_byidx __read_mostly;
-static unsigned int xfrm_idx_hmask __read_mostly;
static unsigned int xfrm_policy_hashmax __read_mostly = 1 * 1024 * 1024;
-static inline unsigned int idx_hash(u32 index)
+static inline unsigned int idx_hash(struct net *net, u32 index)
{
- return __idx_hash(index, xfrm_idx_hmask);
+ return __idx_hash(index, net->xfrm.policy_idx_hmask);
}
-static struct hlist_head *policy_hash_bysel(struct xfrm_selector *sel, unsigned short family, int dir)
+static struct hlist_head *policy_hash_bysel(struct net *net, struct xfrm_selector *sel, unsigned short family, int dir)
{
- unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+ unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
unsigned int hash = __sel_hash(sel, family, hmask);
return (hash == hmask + 1 ?
- &xfrm_policy_inexact[dir] :
- xfrm_policy_bydst[dir].table + hash);
+ &net->xfrm.policy_inexact[dir] :
+ net->xfrm.policy_bydst[dir].table + hash);
}
-static struct hlist_head *policy_hash_direct(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, int dir)
+static struct hlist_head *policy_hash_direct(struct net *net, xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, int dir)
{
- unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+ unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
unsigned int hash = __addr_hash(daddr, saddr, family, hmask);
- return xfrm_policy_bydst[dir].table + hash;
+ return net->xfrm.policy_bydst[dir].table + hash;
}
static void xfrm_dst_hash_transfer(struct hlist_head *list,
@@ -408,12 +393,12 @@ static unsigned long xfrm_new_hash_mask(unsigned int old_hmask)
return ((old_hmask + 1) << 1) - 1;
}
-static void xfrm_bydst_resize(int dir)
+static void xfrm_bydst_resize(struct net *net, int dir)
{
- unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+ unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
- struct hlist_head *odst = xfrm_policy_bydst[dir].table;
+ struct hlist_head *odst = net->xfrm.policy_bydst[dir].table;
struct hlist_head *ndst = xfrm_hash_alloc(nsize);
int i;
@@ -425,20 +410,20 @@ static void xfrm_bydst_resize(int dir)
for (i = hmask; i >= 0; i--)
xfrm_dst_hash_transfer(odst + i, ndst, nhashmask);
- xfrm_policy_bydst[dir].table = ndst;
- xfrm_policy_bydst[dir].hmask = nhashmask;
+ net->xfrm.policy_bydst[dir].table = ndst;
+ net->xfrm.policy_bydst[dir].hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
xfrm_hash_free(odst, (hmask + 1) * sizeof(struct hlist_head));
}
-static void xfrm_byidx_resize(int total)
+static void xfrm_byidx_resize(struct net *net, int total)
{
- unsigned int hmask = xfrm_idx_hmask;
+ unsigned int hmask = net->xfrm.policy_idx_hmask;
unsigned int nhashmask = xfrm_new_hash_mask(hmask);
unsigned int nsize = (nhashmask + 1) * sizeof(struct hlist_head);
- struct hlist_head *oidx = xfrm_policy_byidx;
+ struct hlist_head *oidx = net->xfrm.policy_byidx;
struct hlist_head *nidx = xfrm_hash_alloc(nsize);
int i;
@@ -450,18 +435,18 @@ static void xfrm_byidx_resize(int total)
for (i = hmask; i >= 0; i--)
xfrm_idx_hash_transfer(oidx + i, nidx, nhashmask);
- xfrm_policy_byidx = nidx;
- xfrm_idx_hmask = nhashmask;
+ net->xfrm.policy_byidx = nidx;
+ net->xfrm.policy_idx_hmask = nhashmask;
write_unlock_bh(&xfrm_policy_lock);
xfrm_hash_free(oidx, (hmask + 1) * sizeof(struct hlist_head));
}
-static inline int xfrm_bydst_should_resize(int dir, int *total)
+static inline int xfrm_bydst_should_resize(struct net *net, int dir, int *total)
{
- unsigned int cnt = xfrm_policy_count[dir];
- unsigned int hmask = xfrm_policy_bydst[dir].hmask;
+ unsigned int cnt = net->xfrm.policy_count[dir];
+ unsigned int hmask = net->xfrm.policy_bydst[dir].hmask;
if (total)
*total += cnt;
@@ -473,9 +458,9 @@ static inline int xfrm_bydst_should_resize(int dir, int *total)
return 0;
}
-static inline int xfrm_byidx_should_resize(int total)
+static inline int xfrm_byidx_should_resize(struct net *net, int total)
{
- unsigned int hmask = xfrm_idx_hmask;
+ unsigned int hmask = net->xfrm.policy_idx_hmask;
if ((hmask + 1) < xfrm_policy_hashmax &&
total > hmask)
@@ -487,41 +472,40 @@ static inline int xfrm_byidx_should_resize(int total)
void xfrm_spd_getinfo(struct xfrmk_spdinfo *si)
{
read_lock_bh(&xfrm_policy_lock);
- si->incnt = xfrm_policy_count[XFRM_POLICY_IN];
- si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];
- si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];
- si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
- si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
- si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
- si->spdhcnt = xfrm_idx_hmask;
+ si->incnt = init_net.xfrm.policy_count[XFRM_POLICY_IN];
+ si->outcnt = init_net.xfrm.policy_count[XFRM_POLICY_OUT];
+ si->fwdcnt = init_net.xfrm.policy_count[XFRM_POLICY_FWD];
+ si->inscnt = init_net.xfrm.policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
+ si->outscnt = init_net.xfrm.policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
+ si->fwdscnt = init_net.xfrm.policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
+ si->spdhcnt = init_net.xfrm.policy_idx_hmask;
si->spdhmcnt = xfrm_policy_hashmax;
read_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_spd_getinfo);
static DEFINE_MUTEX(hash_resize_mutex);
-static void xfrm_hash_resize(struct work_struct *__unused)
+static void xfrm_hash_resize(struct work_struct *work)
{
+ struct net *net = container_of(work, struct net, xfrm.policy_hash_work);
int dir, total;
mutex_lock(&hash_resize_mutex);
total = 0;
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
- if (xfrm_bydst_should_resize(dir, &total))
- xfrm_bydst_resize(dir);
+ if (xfrm_bydst_should_resize(net, dir, &total))
+ xfrm_bydst_resize(net, dir);
}
- if (xfrm_byidx_should_resize(total))
- xfrm_byidx_resize(total);
+ if (xfrm_byidx_should_resize(net, total))
+ xfrm_byidx_resize(net, total);
mutex_unlock(&hash_resize_mutex);
}
-static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
-
/* Generate new index... KAME seems to generate them ordered by cost
* of an absolute inpredictability of ordering of rules. This will not pass. */
-static u32 xfrm_gen_index(u8 type, int dir)
+static u32 xfrm_gen_index(struct net *net, int dir)
{
static u32 idx_generator;
@@ -536,7 +520,7 @@ static u32 xfrm_gen_index(u8 type, int dir)
idx_generator += 8;
if (idx == 0)
idx = 8;
- list = xfrm_policy_byidx + idx_hash(idx);
+ list = net->xfrm.policy_byidx + idx_hash(net, idx);
found = 0;
hlist_for_each_entry(p, entry, list, byidx) {
if (p->index == idx) {
@@ -566,6 +550,7 @@ static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
{
+ struct net *net = xp_net(policy);
struct xfrm_policy *pol;
struct xfrm_policy *delpol;
struct hlist_head *chain;
@@ -573,7 +558,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
struct dst_entry *gc_list;
write_lock_bh(&xfrm_policy_lock);
- chain = policy_hash_bysel(&policy->selector, policy->family, dir);
+ chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
delpol = NULL;
newpos = NULL;
hlist_for_each_entry(pol, entry, chain, bydst) {
@@ -600,27 +585,23 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
else
hlist_add_head(&policy->bydst, chain);
xfrm_pol_hold(policy);
- xfrm_policy_count[dir]++;
+ net->xfrm.policy_count[dir]++;
atomic_inc(&flow_cache_genid);
- if (delpol) {
- hlist_del(&delpol->bydst);
- hlist_del(&delpol->byidx);
- list_del(&delpol->walk.all);
- xfrm_policy_count[dir]--;
- }
- policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
- hlist_add_head(&policy->byidx, xfrm_policy_byidx+idx_hash(policy->index));
+ if (delpol)
+ __xfrm_policy_unlink(delpol, dir);
+ policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
+ hlist_add_head(&policy->byidx, net->xfrm.policy_byidx+idx_hash(net, policy->index));
policy->curlft.add_time = get_seconds();
policy->curlft.use_time = 0;
if (!mod_timer(&policy->timer, jiffies + HZ))
xfrm_pol_hold(policy);
- list_add(&policy->walk.all, &xfrm_policy_all);
+ list_add(&policy->walk.all, &net->xfrm.policy_all);
write_unlock_bh(&xfrm_policy_lock);
if (delpol)
xfrm_policy_kill(delpol);
- else if (xfrm_bydst_should_resize(dir, NULL))
- schedule_work(&xfrm_hash_work);
+ else if (xfrm_bydst_should_resize(net, dir, NULL))
+ schedule_work(&net->xfrm.policy_hash_work);
read_lock_bh(&xfrm_policy_lock);
gc_list = NULL;
@@ -654,7 +635,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
}
EXPORT_SYMBOL(xfrm_policy_insert);
-struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u8 type, int dir,
struct xfrm_selector *sel,
struct xfrm_sec_ctx *ctx, int delete,
int *err)
@@ -665,7 +646,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
*err = 0;
write_lock_bh(&xfrm_policy_lock);
- chain = policy_hash_bysel(sel, sel->family, dir);
+ chain = policy_hash_bysel(net, sel, sel->family, dir);
ret = NULL;
hlist_for_each_entry(pol, entry, chain, bydst) {
if (pol->type == type &&
@@ -679,10 +660,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
write_unlock_bh(&xfrm_policy_lock);
return pol;
}
- hlist_del(&pol->bydst);
- hlist_del(&pol->byidx);
- list_del(&pol->walk.all);
- xfrm_policy_count[dir]--;
+ __xfrm_policy_unlink(pol, dir);
}
ret = pol;
break;
@@ -698,8 +676,8 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
}
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
-struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
- int *err)
+struct xfrm_policy *xfrm_policy_byid(struct net *net, u8 type, int dir, u32 id,
+ int delete, int *err)
{
struct xfrm_policy *pol, *ret;
struct hlist_head *chain;
@@ -711,7 +689,7 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
*err = 0;
write_lock_bh(&xfrm_policy_lock);
- chain = xfrm_policy_byidx + idx_hash(id);
+ chain = net->xfrm.policy_byidx + idx_hash(net, id);
ret = NULL;
hlist_for_each_entry(pol, entry, chain, byidx) {
if (pol->type == type && pol->index == id) {
@@ -723,10 +701,7 @@ struct xfrm_policy *xfrm_policy_byid(u8 type, int dir, u32 id, int delete,
write_unlock_bh(&xfrm_policy_lock);
return pol;
}
- hlist_del(&pol->bydst);
- hlist_del(&pol->byidx);
- list_del(&pol->walk.all);
- xfrm_policy_count[dir]--;
+ __xfrm_policy_unlink(pol, dir);
}
ret = pol;
break;
@@ -744,7 +719,7 @@ EXPORT_SYMBOL(xfrm_policy_byid);
#ifdef CONFIG_SECURITY_NETWORK_XFRM
static inline int
-xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
{
int dir, err = 0;
@@ -754,7 +729,7 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
int i;
hlist_for_each_entry(pol, entry,
- &xfrm_policy_inexact[dir], bydst) {
+ &net->xfrm.policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
err = security_xfrm_policy_delete(pol->security);
@@ -766,9 +741,9 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
return err;
}
}
- for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+ for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry,
- xfrm_policy_bydst[dir].table + i,
+ net->xfrm.policy_bydst[dir].table + i,
bydst) {
if (pol->type != type)
continue;
@@ -788,36 +763,33 @@ xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
}
#else
static inline int
-xfrm_policy_flush_secctx_check(u8 type, struct xfrm_audit *audit_info)
+xfrm_policy_flush_secctx_check(struct net *net, u8 type, struct xfrm_audit *audit_info)
{
return 0;
}
#endif
-int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
+int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info)
{
int dir, err = 0;
write_lock_bh(&xfrm_policy_lock);
- err = xfrm_policy_flush_secctx_check(type, audit_info);
+ err = xfrm_policy_flush_secctx_check(net, type, audit_info);
if (err)
goto out;
for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
struct xfrm_policy *pol;
struct hlist_node *entry;
- int i, killed;
+ int i;
- killed = 0;
again1:
hlist_for_each_entry(pol, entry,
- &xfrm_policy_inexact[dir], bydst) {
+ &net->xfrm.policy_inexact[dir], bydst) {
if (pol->type != type)
continue;
- hlist_del(&pol->bydst);
- hlist_del(&pol->byidx);
- list_del(&pol->walk.all);
+ __xfrm_policy_unlink(pol, dir);
write_unlock_bh(&xfrm_policy_lock);
xfrm_audit_policy_delete(pol, 1, audit_info->loginuid,
@@ -825,22 +797,19 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
audit_info->secid);
xfrm_policy_kill(pol);
- killed++;
write_lock_bh(&xfrm_policy_lock);
goto again1;
}
- for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+ for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
again2:
hlist_for_each_entry(pol, entry,
- xfrm_policy_bydst[dir].table + i,
+ net->xfrm.policy_bydst[dir].table + i,
bydst) {
if (pol->type != type)
continue;
- hlist_del(&pol->bydst);
- hlist_del(&pol->byidx);
- list_del(&pol->walk.all);
+ __xfrm_policy_unlink(pol, dir);
write_unlock_bh(&xfrm_policy_lock);
xfrm_audit_policy_delete(pol, 1,
@@ -848,14 +817,12 @@ int xfrm_policy_flush(u8 type, struct xfrm_audit *audit_info)
audit_info->sessionid,
audit_info->secid);
xfrm_policy_kill(pol);
- killed++;
write_lock_bh(&xfrm_policy_lock);
goto again2;
}
}
- xfrm_policy_count[dir] -= killed;
}
atomic_inc(&flow_cache_genid);
out:
@@ -864,7 +831,7 @@ out:
}
EXPORT_SYMBOL(xfrm_policy_flush);
-int xfrm_policy_walk(struct xfrm_policy_walk *walk,
+int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
int (*func)(struct xfrm_policy *, int, int, void*),
void *data)
{
@@ -881,10 +848,10 @@ int xfrm_policy_walk(struct xfrm_policy_walk *walk,
write_lock_bh(&xfrm_policy_lock);
if (list_empty(&walk->walk.all))
- x = list_first_entry(&xfrm_policy_all, struct xfrm_policy_walk_entry, all);
+ x = list_first_entry(&net->xfrm.policy_all, struct xfrm_policy_walk_entry, all);
else
x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all);
- list_for_each_entry_from(x, &xfrm_policy_all, all) {
+ list_for_each_entry_from(x, &net->xfrm.policy_all, all) {
if (x->dead)
continue;
pol = container_of(x, struct xfrm_policy, walk);
@@ -953,7 +920,8 @@ static int xfrm_policy_match(struct xfrm_policy *pol, struct flowi *fl,
return ret;
}
-static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
+static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
+ struct flowi *fl,
u16 family, u8 dir)
{
int err;
@@ -969,7 +937,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
return NULL;
read_lock_bh(&xfrm_policy_lock);
- chain = policy_hash_direct(daddr, saddr, family, dir);
+ chain = policy_hash_direct(net, daddr, saddr, family, dir);
ret = NULL;
hlist_for_each_entry(pol, entry, chain, bydst) {
err = xfrm_policy_match(pol, fl, type, family, dir);
@@ -986,7 +954,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(u8 type, struct flowi *fl,
break;
}
}
- chain = &xfrm_policy_inexact[dir];
+ chain = &net->xfrm.policy_inexact[dir];
hlist_for_each_entry(pol, entry, chain, bydst) {
err = xfrm_policy_match(pol, fl, type, family, dir);
if (err) {
@@ -1009,14 +977,14 @@ fail:
return ret;
}
-static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
- void **objp, atomic_t **obj_refp)
+static int xfrm_policy_lookup(struct net *net, struct flowi *fl, u16 family,
+ u8 dir, void **objp, atomic_t **obj_refp)
{
struct xfrm_policy *pol;
int err = 0;
#ifdef CONFIG_XFRM_SUB_POLICY
- pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_SUB, fl, family, dir);
+ pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
@@ -1024,7 +992,7 @@ static int xfrm_policy_lookup(struct flowi *fl, u16 family, u8 dir,
if (pol || err)
goto end;
#endif
- pol = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN, fl, family, dir);
+ pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
if (IS_ERR(pol)) {
err = PTR_ERR(pol);
pol = NULL;
@@ -1083,29 +1051,32 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struc
static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
{
- struct hlist_head *chain = policy_hash_bysel(&pol->selector,
+ struct net *net = xp_net(pol);
+ struct hlist_head *chain = policy_hash_bysel(net, &pol->selector,
pol->family, dir);
- list_add(&pol->walk.all, &xfrm_policy_all);
+ list_add(&pol->walk.all, &net->xfrm.policy_all);
hlist_add_head(&pol->bydst, chain);
- hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
- xfrm_policy_count[dir]++;
+ hlist_add_head(&pol->byidx, net->xfrm.policy_byidx+idx_hash(net, pol->index));
+ net->xfrm.policy_count[dir]++;
xfrm_pol_hold(pol);
- if (xfrm_bydst_should_resize(dir, NULL))
- schedule_work(&xfrm_hash_work);
+ if (xfrm_bydst_should_resize(net, dir, NULL))
+ schedule_work(&net->xfrm.policy_hash_work);
}
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
int dir)
{
+ struct net *net = xp_net(pol);
+
if (hlist_unhashed(&pol->bydst))
return NULL;
hlist_del(&pol->bydst);
hlist_del(&pol->byidx);
list_del(&pol->walk.all);
- xfrm_policy_count[dir]--;
+ net->xfrm.policy_count[dir]--;
return pol;
}
@@ -1127,6 +1098,7 @@ EXPORT_SYMBOL(xfrm_policy_delete);
int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
{
+ struct net *net = xp_net(pol);
struct xfrm_policy *old_pol;
#ifdef CONFIG_XFRM_SUB_POLICY
@@ -1139,7 +1111,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
sk->sk_policy[dir] = pol;
if (pol) {
pol->curlft.add_time = get_seconds();
- pol->index = xfrm_gen_index(pol->type, XFRM_POLICY_MAX+dir);
+ pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir);
__xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
}
if (old_pol)
@@ -1154,7 +1126,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
static struct xfrm_policy *clone_policy(struct xfrm_policy *old, int dir)
{
- struct xfrm_policy *newp = xfrm_policy_alloc(GFP_ATOMIC);
+ struct xfrm_policy *newp = xfrm_policy_alloc(xp_net(old), GFP_ATOMIC);
if (newp) {
newp->selector = old->selector;
@@ -1194,7 +1166,7 @@ int __xfrm_sk_clone_policy(struct sock *sk)
}
static int
-xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
+xfrm_get_saddr(struct net *net, xfrm_address_t *local, xfrm_address_t *remote,
unsigned short family)
{
int err;
@@ -1202,7 +1174,7 @@ xfrm_get_saddr(xfrm_address_t *local, xfrm_address_t *remote,
if (unlikely(afinfo == NULL))
return -EINVAL;
- err = afinfo->get_saddr(local, remote);
+ err = afinfo->get_saddr(net, local, remote);
xfrm_policy_put_afinfo(afinfo);
return err;
}
@@ -1214,6 +1186,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
struct xfrm_state **xfrm,
unsigned short family)
{
+ struct net *net = xp_net(policy);
int nx;
int i, error;
xfrm_address_t *daddr = xfrm_flowi_daddr(fl, family);
@@ -1232,7 +1205,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, struct flowi *fl,
local = &tmpl->saddr;
family = tmpl->encap_family;
if (xfrm_addr_any(local, family)) {
- error = xfrm_get_saddr(&tmp, remote, family);
+ error = xfrm_get_saddr(net, &tmp, remote, family);
if (error)
goto fail;
local = &tmp;
@@ -1546,7 +1519,7 @@ static int stale_bundle(struct dst_entry *dst);
* At the moment we eat a raw IP route. Mostly to speed up lookups
* on interfaces with disabled IPsec.
*/
-int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
+int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl,
struct sock *sk, int flags)
{
struct xfrm_policy *policy;
@@ -1576,7 +1549,7 @@ restart:
policy = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
err = PTR_ERR(policy);
if (IS_ERR(policy)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
goto dropdst;
}
}
@@ -1584,14 +1557,14 @@ restart:
if (!policy) {
/* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) ||
- !xfrm_policy_count[XFRM_POLICY_OUT])
+ !net->xfrm.policy_count[XFRM_POLICY_OUT])
goto nopol;
- policy = flow_cache_lookup(fl, dst_orig->ops->family,
+ policy = flow_cache_lookup(net, fl, dst_orig->ops->family,
dir, xfrm_policy_lookup);
err = PTR_ERR(policy);
if (IS_ERR(policy)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
goto dropdst;
}
}
@@ -1614,7 +1587,7 @@ restart:
default:
case XFRM_POLICY_BLOCK:
/* Prohibit the flow */
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
err = -EPERM;
goto error;
@@ -1634,7 +1607,7 @@ restart:
*/
dst = xfrm_find_bundle(fl, policy, family);
if (IS_ERR(dst)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
err = PTR_ERR(dst);
goto error;
}
@@ -1644,17 +1617,18 @@ restart:
#ifdef CONFIG_XFRM_SUB_POLICY
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
- pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
+ pols[1] = xfrm_policy_lookup_bytype(net,
+ XFRM_POLICY_TYPE_MAIN,
fl, family,
XFRM_POLICY_OUT);
if (pols[1]) {
if (IS_ERR(pols[1])) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR);
err = PTR_ERR(pols[1]);
goto error;
}
if (pols[1]->action == XFRM_POLICY_BLOCK) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLBLOCK);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLBLOCK);
err = -EPERM;
goto error;
}
@@ -1681,27 +1655,27 @@ restart:
if (unlikely(nx<0)) {
err = nx;
- if (err == -EAGAIN && sysctl_xfrm_larval_drop) {
+ if (err == -EAGAIN && net->xfrm.sysctl_larval_drop) {
/* EREMOTE tells the caller to generate
* a one-shot blackhole route.
*/
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
xfrm_pol_put(policy);
return -EREMOTE;
}
if (err == -EAGAIN && (flags & XFRM_LOOKUP_WAIT)) {
DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&km_waitq, &wait);
+ add_wait_queue(&net->xfrm.km_waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
- remove_wait_queue(&km_waitq, &wait);
+ remove_wait_queue(&net->xfrm.km_waitq, &wait);
nx = xfrm_tmpl_resolve(pols, npols, fl, xfrm, family);
if (nx == -EAGAIN && signal_pending(current)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
err = -ERESTART;
goto error;
}
@@ -1713,7 +1687,7 @@ restart:
err = nx;
}
if (err < 0) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTNOSTATES);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
goto error;
}
}
@@ -1726,7 +1700,7 @@ restart:
dst = xfrm_bundle_create(policy, xfrm, nx, fl, dst_orig);
err = PTR_ERR(dst);
if (IS_ERR(dst)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLEGENERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLEGENERROR);
goto error;
}
@@ -1747,9 +1721,9 @@ restart:
dst_free(dst);
if (pol_dead)
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTPOLDEAD);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLDEAD);
else
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
err = -EHOSTUNREACH;
goto error;
}
@@ -1761,7 +1735,7 @@ restart:
if (unlikely(err)) {
write_unlock_bh(&policy->lock);
dst_free(dst);
- XFRM_INC_STATS(LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTBUNDLECHECKERROR);
goto error;
}
@@ -1790,10 +1764,10 @@ nopol:
}
EXPORT_SYMBOL(__xfrm_lookup);
-int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
+int xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl,
struct sock *sk, int flags)
{
- int err = __xfrm_lookup(dst_p, fl, sk, flags);
+ int err = __xfrm_lookup(net, dst_p, fl, sk, flags);
if (err == -EREMOTE) {
dst_release(*dst_p);
@@ -1901,6 +1875,7 @@ static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp
int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
unsigned short family)
{
+ struct net *net = dev_net(skb->dev);
struct xfrm_policy *pol;
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
int npols = 0;
@@ -1916,7 +1891,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
fl_dir = policy_to_flow_dir(dir);
if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
return 0;
}
@@ -1929,7 +1904,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
for (i=skb->sp->len-1; i>=0; i--) {
struct xfrm_state *x = skb->sp->xvec[i];
if (!xfrm_selector_match(&x->sel, &fl, family)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMISMATCH);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
return 0;
}
}
@@ -1939,24 +1914,24 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
if (sk && sk->sk_policy[dir]) {
pol = xfrm_sk_policy_lookup(sk, dir, &fl);
if (IS_ERR(pol)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
}
}
if (!pol)
- pol = flow_cache_lookup(&fl, family, fl_dir,
+ pol = flow_cache_lookup(net, &fl, family, fl_dir,
xfrm_policy_lookup);
if (IS_ERR(pol)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
}
if (!pol) {
if (skb->sp && secpath_has_nontransport(skb->sp, 0, &xerr_idx)) {
xfrm_secpath_reject(xerr_idx, skb, &fl);
- XFRM_INC_STATS(LINUX_MIB_XFRMINNOPOLS);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
return 0;
}
return 1;
@@ -1968,12 +1943,12 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
npols ++;
#ifdef CONFIG_XFRM_SUB_POLICY
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
- pols[1] = xfrm_policy_lookup_bytype(XFRM_POLICY_TYPE_MAIN,
+ pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN,
&fl, family,
XFRM_POLICY_IN);
if (pols[1]) {
if (IS_ERR(pols[1])) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINPOLERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
return 0;
}
pols[1]->curlft.use_time = get_seconds();
@@ -1997,11 +1972,11 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
for (pi = 0; pi < npols; pi++) {
if (pols[pi] != pol &&
pols[pi]->action != XFRM_POLICY_ALLOW) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
goto reject;
}
if (ti + pols[pi]->xfrm_nr >= XFRM_MAX_DEPTH) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINBUFFERERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto reject_error;
}
for (i = 0; i < pols[pi]->xfrm_nr; i++)
@@ -2025,20 +2000,20 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
if (k < -1)
/* "-2 - errored_index" returned */
xerr_idx = -(2+k);
- XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
goto reject;
}
}
if (secpath_has_nontransport(sp, k, &xerr_idx)) {
- XFRM_INC_STATS(LINUX_MIB_XFRMINTMPLMISMATCH);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINTMPLMISMATCH);
goto reject;
}
xfrm_pols_put(pols, npols);
return 1;
}
- XFRM_INC_STATS(LINUX_MIB_XFRMINPOLBLOCK);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLBLOCK);
reject:
xfrm_secpath_reject(xerr_idx, skb, &fl);
@@ -2050,15 +2025,16 @@ EXPORT_SYMBOL(__xfrm_policy_check);
int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
{
+ struct net *net = dev_net(skb->dev);
struct flowi fl;
if (xfrm_decode_session(skb, &fl, family) < 0) {
/* XXX: we should have something like FWDHDRERROR here. */
- XFRM_INC_STATS(LINUX_MIB_XFRMINHDRERROR);
+ XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
return 0;
}
- return xfrm_lookup(&skb->dst, &fl, NULL, 0) == 0;
+ return xfrm_lookup(net, &skb->dst, &fl, NULL, 0) == 0;
}
EXPORT_SYMBOL(__xfrm_route_forward);
@@ -2142,7 +2118,7 @@ static void prune_one_bundle(struct xfrm_policy *pol, int (*func)(struct dst_ent
write_unlock(&pol->lock);
}
-static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
+static void xfrm_prune_bundles(struct net *net, int (*func)(struct dst_entry *))
{
struct dst_entry *gc_list = NULL;
int dir;
@@ -2155,11 +2131,11 @@ static void xfrm_prune_bundles(int (*func)(struct dst_entry *))
int i;
hlist_for_each_entry(pol, entry,
- &xfrm_policy_inexact[dir], bydst)
+ &net->xfrm.policy_inexact[dir], bydst)
prune_one_bundle(pol, func, &gc_list);
- table = xfrm_policy_bydst[dir].table;
- for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
+ table = net->xfrm.policy_bydst[dir].table;
+ for (i = net->xfrm.policy_bydst[dir].hmask; i >= 0; i--) {
hlist_for_each_entry(pol, entry, table + i, bydst)
prune_one_bundle(pol, func, &gc_list);
}
@@ -2178,14 +2154,14 @@ static int unused_bundle(struct dst_entry *dst)
return !atomic_read(&dst->__refcnt);
}
-static void __xfrm_garbage_collect(void)
+static void __xfrm_garbage_collect(struct net *net)
{
- xfrm_prune_bundles(unused_bundle);
+ xfrm_prune_bundles(net, unused_bundle);
}
-static int xfrm_flush_bundles(void)
+static int xfrm_flush_bundles(struct net *net)
{
- xfrm_prune_bundles(stale_bundle);
+ xfrm_prune_bundles(net, stale_bundle);
return 0;
}
@@ -2371,38 +2347,54 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void
{
struct net_device *dev = ptr;
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
switch (event) {
case NETDEV_DOWN:
- xfrm_flush_bundles();
+ xfrm_flush_bundles(dev_net(dev));
}
return NOTIFY_DONE;
}
static struct notifier_block xfrm_dev_notifier = {
- xfrm_dev_event,
- NULL,
- 0
+ .notifier_call = xfrm_dev_event,
};
#ifdef CONFIG_XFRM_STATISTICS
-static int __init xfrm_statistics_init(void)
+static int __net_init xfrm_statistics_init(struct net *net)
{
- if (snmp_mib_init((void **)xfrm_statistics,
+ int rv;
+
+ if (snmp_mib_init((void **)net->mib.xfrm_statistics,
sizeof(struct linux_xfrm_mib)) < 0)
return -ENOMEM;
+ rv = xfrm_proc_init(net);
+ if (rv < 0)
+ snmp_mib_free((void **)net->mib.xfrm_statistics);
+ return rv;
+}
+
+static void xfrm_statistics_fini(struct net *net)
+{
+ xfrm_proc_fini(net);
+ snmp_mib_free((void **)net->mib.xfrm_statistics);
+}
+#else
+static int __net_init xfrm_statistics_init(struct net *net)
+{
return 0;
}
+
+static void xfrm_statistics_fini(struct net *net)
+{
+}
#endif
-static void __init xfrm_policy_init(void)
+static int __net_init xfrm_policy_init(struct net *net)
{
unsigned int hmask, sz;
int dir;
- xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
+ if (net_eq(net, &init_net))
+ xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache",
sizeof(struct xfrm_dst),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
@@ -2410,39 +2402,124 @@ static void __init xfrm_policy_init(void)
hmask = 8 - 1;
sz = (hmask+1) * sizeof(struct hlist_head);
- xfrm_policy_byidx = xfrm_hash_alloc(sz);
- xfrm_idx_hmask = hmask;
- if (!xfrm_policy_byidx)
- panic("XFRM: failed to allocate byidx hash\n");
+ net->xfrm.policy_byidx = xfrm_hash_alloc(sz);
+ if (!net->xfrm.policy_byidx)
+ goto out_byidx;
+ net->xfrm.policy_idx_hmask = hmask;
for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
struct xfrm_policy_hash *htab;
- INIT_HLIST_HEAD(&xfrm_policy_inexact[dir]);
+ net->xfrm.policy_count[dir] = 0;
+ INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
- htab = &xfrm_policy_bydst[dir];
+ htab = &net->xfrm.policy_bydst[dir];
htab->table = xfrm_hash_alloc(sz);
- htab->hmask = hmask;
if (!htab->table)
- panic("XFRM: failed to allocate bydst hash\n");
+ goto out_bydst;
+ htab->hmask = hmask;
}
- INIT_LIST_HEAD(&xfrm_policy_all);
- INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
- register_netdevice_notifier(&xfrm_dev_notifier);
+ INIT_LIST_HEAD(&net->xfrm.policy_all);
+ INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize);
+ if (net_eq(net, &init_net))
+ register_netdevice_notifier(&xfrm_dev_notifier);
+ return 0;
+
+out_bydst:
+ for (dir--; dir >= 0; dir--) {
+ struct xfrm_policy_hash *htab;
+
+ htab = &net->xfrm.policy_bydst[dir];
+ xfrm_hash_free(htab->table, sz);
+ }
+ xfrm_hash_free(net->xfrm.policy_byidx, sz);
+out_byidx:
+ return -ENOMEM;
}
-void __init xfrm_init(void)
+static void xfrm_policy_fini(struct net *net)
{
-#ifdef CONFIG_XFRM_STATISTICS
- xfrm_statistics_init();
+ struct xfrm_audit audit_info;
+ unsigned int sz;
+ int dir;
+
+ flush_work(&net->xfrm.policy_hash_work);
+#ifdef CONFIG_XFRM_SUB_POLICY
+ audit_info.loginuid = -1;
+ audit_info.sessionid = -1;
+ audit_info.secid = 0;
+ xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, &audit_info);
#endif
- xfrm_state_init();
- xfrm_policy_init();
+ audit_info.loginuid = -1;
+ audit_info.sessionid = -1;
+ audit_info.secid = 0;
+ xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, &audit_info);
+ flush_work(&xfrm_policy_gc_work);
+
+ WARN_ON(!list_empty(&net->xfrm.policy_all));
+
+ for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ struct xfrm_policy_hash *htab;
+
+ WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
+
+ htab = &net->xfrm.policy_bydst[dir];
+ sz = (htab->hmask + 1);
+ WARN_ON(!hlist_empty(htab->table));
+ xfrm_hash_free(htab->table, sz);
+ }
+
+ sz = (net->xfrm.policy_idx_hmask + 1) * sizeof(struct hlist_head);
+ WARN_ON(!hlist_empty(net->xfrm.policy_byidx));
+ xfrm_hash_free(net->xfrm.policy_byidx, sz);
+}
+
+static int __net_init xfrm_net_init(struct net *net)
+{
+ int rv;
+
+ rv = xfrm_statistics_init(net);
+ if (rv < 0)
+ goto out_statistics;
+ rv = xfrm_state_init(net);
+ if (rv < 0)
+ goto out_state;
+ rv = xfrm_policy_init(net);
+ if (rv < 0)
+ goto out_policy;
+ rv = xfrm_sysctl_init(net);
+ if (rv < 0)
+ goto out_sysctl;
+ return 0;
+
+out_sysctl:
+ xfrm_policy_fini(net);
+out_policy:
+ xfrm_state_fini(net);
+out_state:
+ xfrm_statistics_fini(net);
+out_statistics:
+ return rv;
+}
+
+static void __net_exit xfrm_net_exit(struct net *net)
+{
+ xfrm_sysctl_fini(net);
+ xfrm_policy_fini(net);
+ xfrm_state_fini(net);
+ xfrm_statistics_fini(net);
+}
+
+static struct pernet_operations __net_initdata xfrm_net_ops = {
+ .init = xfrm_net_init,
+ .exit = xfrm_net_exit,
+};
+
+void __init xfrm_init(void)
+{
+ register_pernet_subsys(&xfrm_net_ops);
xfrm_input_init();
-#ifdef CONFIG_XFRM_STATISTICS
- xfrm_proc_init();
-#endif
}
#ifdef CONFIG_AUDITSYSCALL
@@ -2458,25 +2535,21 @@ static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
switch(sel->family) {
case AF_INET:
- audit_log_format(audit_buf, " src=" NIPQUAD_FMT,
- NIPQUAD(sel->saddr.a4));
+ audit_log_format(audit_buf, " src=%pI4", &sel->saddr.a4);
if (sel->prefixlen_s != 32)
audit_log_format(audit_buf, " src_prefixlen=%d",
sel->prefixlen_s);
- audit_log_format(audit_buf, " dst=" NIPQUAD_FMT,
- NIPQUAD(sel->daddr.a4));
+ audit_log_format(audit_buf, " dst=%pI4", &sel->daddr.a4);
if (sel->prefixlen_d != 32)
audit_log_format(audit_buf, " dst_prefixlen=%d",
sel->prefixlen_d);
break;
case AF_INET6:
- audit_log_format(audit_buf, " src=" NIP6_FMT,
- NIP6(*(struct in6_addr *)sel->saddr.a6));
+ audit_log_format(audit_buf, " src=%pI6", sel->saddr.a6);
if (sel->prefixlen_s != 128)
audit_log_format(audit_buf, " src_prefixlen=%d",
sel->prefixlen_s);
- audit_log_format(audit_buf, " dst=" NIP6_FMT,
- NIP6(*(struct in6_addr *)sel->daddr.a6));
+ audit_log_format(audit_buf, " dst=%pI6", sel->daddr.a6);
if (sel->prefixlen_d != 128)
audit_log_format(audit_buf, " dst_prefixlen=%d",
sel->prefixlen_d);
@@ -2546,7 +2619,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel,
u32 priority = ~0U;
read_lock_bh(&xfrm_policy_lock);
- chain = policy_hash_direct(&sel->daddr, &sel->saddr, sel->family, dir);
+ chain = policy_hash_direct(&init_net, &sel->daddr, &sel->saddr, sel->family, dir);
hlist_for_each_entry(pol, entry, chain, bydst) {
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type) {
@@ -2555,7 +2628,7 @@ static struct xfrm_policy * xfrm_migrate_policy_find(struct xfrm_selector *sel,
break;
}
}
- chain = &xfrm_policy_inexact[dir];
+ chain = &init_net.xfrm.policy_inexact[dir];
hlist_for_each_entry(pol, entry, chain, bydst) {
if (xfrm_migrate_selector_match(sel, &pol->selector) &&
pol->type == type &&
diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c
index 2b0db13f0cda..a2adb51849a9 100644
--- a/net/xfrm/xfrm_proc.c
+++ b/net/xfrm/xfrm_proc.c
@@ -44,32 +44,20 @@ static struct snmp_mib xfrm_mib_list[] = {
SNMP_MIB_SENTINEL
};
-static unsigned long
-fold_field(void *mib[], int offt)
-{
- unsigned long res = 0;
- int i;
-
- for_each_possible_cpu(i) {
- res += *(((unsigned long *)per_cpu_ptr(mib[0], i)) + offt);
- res += *(((unsigned long *)per_cpu_ptr(mib[1], i)) + offt);
- }
- return res;
-}
-
static int xfrm_statistics_seq_show(struct seq_file *seq, void *v)
{
+ struct net *net = seq->private;
int i;
for (i=0; xfrm_mib_list[i].name; i++)
seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name,
- fold_field((void **)xfrm_statistics,
- xfrm_mib_list[i].entry));
+ snmp_fold_field((void **)net->mib.xfrm_statistics,
+ xfrm_mib_list[i].entry));
return 0;
}
static int xfrm_statistics_seq_open(struct inode *inode, struct file *file)
{
- return single_open(file, xfrm_statistics_seq_show, NULL);
+ return single_open_net(inode, file, xfrm_statistics_seq_show);
}
static struct file_operations xfrm_statistics_seq_fops = {
@@ -77,21 +65,18 @@ static struct file_operations xfrm_statistics_seq_fops = {
.open = xfrm_statistics_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = single_release_net,
};
-int __init xfrm_proc_init(void)
+int __net_init xfrm_proc_init(struct net *net)
{
- int rc = 0;
-
- if (!proc_net_fops_create(&init_net, "xfrm_stat", S_IRUGO,
+ if (!proc_net_fops_create(net, "xfrm_stat", S_IRUGO,
&xfrm_statistics_seq_fops))
- goto stat_fail;
-
- out:
- return rc;
+ return -ENOMEM;
+ return 0;
+}
- stat_fail:
- rc = -ENOMEM;
- goto out;
+void xfrm_proc_fini(struct net *net)
+{
+ proc_net_remove(net, "xfrm_stat");
}
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 508337f97249..e25ff62ab2a6 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -24,17 +24,6 @@
#include "xfrm_hash.h"
-struct sock *xfrm_nl;
-EXPORT_SYMBOL(xfrm_nl);
-
-u32 sysctl_xfrm_aevent_etime __read_mostly = XFRM_AE_ETIME;
-EXPORT_SYMBOL(sysctl_xfrm_aevent_etime);
-
-u32 sysctl_xfrm_aevent_rseqth __read_mostly = XFRM_AE_SEQT_SIZE;
-EXPORT_SYMBOL(sysctl_xfrm_aevent_rseqth);
-
-u32 sysctl_xfrm_acq_expires __read_mostly = 30;
-
/* Each xfrm_state may be linked to two tables:
1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
@@ -44,19 +33,7 @@ u32 sysctl_xfrm_acq_expires __read_mostly = 30;
static DEFINE_SPINLOCK(xfrm_state_lock);
-/* Hash table to find appropriate SA towards given target (endpoint
- * of tunnel or destination of transport mode) allowed by selector.
- *
- * Main use is finding SA after policy selected tunnel or transport mode.
- * Also, it can be used by ah/esp icmp error handler to find offending SA.
- */
-static LIST_HEAD(xfrm_state_all);
-static struct hlist_head *xfrm_state_bydst __read_mostly;
-static struct hlist_head *xfrm_state_bysrc __read_mostly;
-static struct hlist_head *xfrm_state_byspi __read_mostly;
-static unsigned int xfrm_state_hmask __read_mostly;
static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
-static unsigned int xfrm_state_num;
static unsigned int xfrm_state_genid;
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
@@ -69,25 +46,27 @@ static void xfrm_audit_state_replay(struct xfrm_state *x,
#define xfrm_audit_state_replay(x, s, sq) do { ; } while (0)
#endif /* CONFIG_AUDITSYSCALL */
-static inline unsigned int xfrm_dst_hash(xfrm_address_t *daddr,
+static inline unsigned int xfrm_dst_hash(struct net *net,
+ xfrm_address_t *daddr,
xfrm_address_t *saddr,
u32 reqid,
unsigned short family)
{
- return __xfrm_dst_hash(daddr, saddr, reqid, family, xfrm_state_hmask);
+ return __xfrm_dst_hash(daddr, saddr, reqid, family, net->xfrm.state_hmask);
}
-static inline unsigned int xfrm_src_hash(xfrm_address_t *daddr,
+static inline unsigned int xfrm_src_hash(struct net *net,
+ xfrm_address_t *daddr,
xfrm_address_t *saddr,
unsigned short family)
{
- return __xfrm_src_hash(daddr, saddr, family, xfrm_state_hmask);
+ return __xfrm_src_hash(daddr, saddr, family, net->xfrm.state_hmask);
}
static inline unsigned int
-xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
+xfrm_spi_hash(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
{
- return __xfrm_spi_hash(daddr, spi, proto, family, xfrm_state_hmask);
+ return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
}
static void xfrm_hash_transfer(struct hlist_head *list,
@@ -121,16 +100,16 @@ static void xfrm_hash_transfer(struct hlist_head *list,
}
}
-static unsigned long xfrm_hash_new_size(void)
+static unsigned long xfrm_hash_new_size(unsigned int state_hmask)
{
- return ((xfrm_state_hmask + 1) << 1) *
- sizeof(struct hlist_head);
+ return ((state_hmask + 1) << 1) * sizeof(struct hlist_head);
}
static DEFINE_MUTEX(hash_resize_mutex);
-static void xfrm_hash_resize(struct work_struct *__unused)
+static void xfrm_hash_resize(struct work_struct *work)
{
+ struct net *net = container_of(work, struct net, xfrm.state_hash_work);
struct hlist_head *ndst, *nsrc, *nspi, *odst, *osrc, *ospi;
unsigned long nsize, osize;
unsigned int nhashmask, ohashmask;
@@ -138,7 +117,7 @@ static void xfrm_hash_resize(struct work_struct *__unused)
mutex_lock(&hash_resize_mutex);
- nsize = xfrm_hash_new_size();
+ nsize = xfrm_hash_new_size(net->xfrm.state_hmask);
ndst = xfrm_hash_alloc(nsize);
if (!ndst)
goto out_unlock;
@@ -157,19 +136,19 @@ static void xfrm_hash_resize(struct work_struct *__unused)
spin_lock_bh(&xfrm_state_lock);
nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
- for (i = xfrm_state_hmask; i >= 0; i--)
- xfrm_hash_transfer(xfrm_state_bydst+i, ndst, nsrc, nspi,
+ for (i = net->xfrm.state_hmask; i >= 0; i--)
+ xfrm_hash_transfer(net->xfrm.state_bydst+i, ndst, nsrc, nspi,
nhashmask);
- odst = xfrm_state_bydst;
- osrc = xfrm_state_bysrc;
- ospi = xfrm_state_byspi;
- ohashmask = xfrm_state_hmask;
+ odst = net->xfrm.state_bydst;
+ osrc = net->xfrm.state_bysrc;
+ ospi = net->xfrm.state_byspi;
+ ohashmask = net->xfrm.state_hmask;
- xfrm_state_bydst = ndst;
- xfrm_state_bysrc = nsrc;
- xfrm_state_byspi = nspi;
- xfrm_state_hmask = nhashmask;
+ net->xfrm.state_bydst = ndst;
+ net->xfrm.state_bysrc = nsrc;
+ net->xfrm.state_byspi = nspi;
+ net->xfrm.state_hmask = nhashmask;
spin_unlock_bh(&xfrm_state_lock);
@@ -182,16 +161,9 @@ out_unlock:
mutex_unlock(&hash_resize_mutex);
}
-static DECLARE_WORK(xfrm_hash_work, xfrm_hash_resize);
-
-DECLARE_WAIT_QUEUE_HEAD(km_waitq);
-EXPORT_SYMBOL(km_waitq);
-
static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
-static struct work_struct xfrm_state_gc_work;
-static HLIST_HEAD(xfrm_state_gc_list);
static DEFINE_SPINLOCK(xfrm_state_gc_lock);
int __xfrm_state_delete(struct xfrm_state *x);
@@ -401,20 +373,21 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
kfree(x);
}
-static void xfrm_state_gc_task(struct work_struct *data)
+static void xfrm_state_gc_task(struct work_struct *work)
{
+ struct net *net = container_of(work, struct net, xfrm.state_gc_work);
struct xfrm_state *x;
struct hlist_node *entry, *tmp;
struct hlist_head gc_list;
spin_lock_bh(&xfrm_state_gc_lock);
- hlist_move_list(&xfrm_state_gc_list, &gc_list);
+ hlist_move_list(&net->xfrm.state_gc_list, &gc_list);
spin_unlock_bh(&xfrm_state_gc_lock);
hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
xfrm_state_gc_destroy(x);
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
}
static inline unsigned long make_jiffies(long secs)
@@ -428,6 +401,7 @@ static inline unsigned long make_jiffies(long secs)
static void xfrm_timer_handler(unsigned long data)
{
struct xfrm_state *x = (struct xfrm_state*)data;
+ struct net *net = xs_net(x);
unsigned long now = get_seconds();
long next = LONG_MAX;
int warn = 0;
@@ -485,7 +459,7 @@ resched:
expired:
if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {
x->km.state = XFRM_STATE_EXPIRED;
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
next = 2;
goto resched;
}
@@ -504,13 +478,14 @@ out:
static void xfrm_replay_timer_handler(unsigned long data);
-struct xfrm_state *xfrm_state_alloc(void)
+struct xfrm_state *xfrm_state_alloc(struct net *net)
{
struct xfrm_state *x;
x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);
if (x) {
+ write_pnet(&x->xs_net, net);
atomic_set(&x->refcnt, 1);
atomic_set(&x->tunnel_users, 0);
INIT_LIST_HEAD(&x->km.all);
@@ -537,17 +512,20 @@ EXPORT_SYMBOL(xfrm_state_alloc);
void __xfrm_state_destroy(struct xfrm_state *x)
{
+ struct net *net = xs_net(x);
+
WARN_ON(x->km.state != XFRM_STATE_DEAD);
spin_lock_bh(&xfrm_state_gc_lock);
- hlist_add_head(&x->gclist, &xfrm_state_gc_list);
+ hlist_add_head(&x->gclist, &net->xfrm.state_gc_list);
spin_unlock_bh(&xfrm_state_gc_lock);
- schedule_work(&xfrm_state_gc_work);
+ schedule_work(&net->xfrm.state_gc_work);
}
EXPORT_SYMBOL(__xfrm_state_destroy);
int __xfrm_state_delete(struct xfrm_state *x)
{
+ struct net *net = xs_net(x);
int err = -ESRCH;
if (x->km.state != XFRM_STATE_DEAD) {
@@ -558,7 +536,7 @@ int __xfrm_state_delete(struct xfrm_state *x)
hlist_del(&x->bysrc);
if (x->id.spi)
hlist_del(&x->byspi);
- xfrm_state_num--;
+ net->xfrm.state_num--;
spin_unlock(&xfrm_state_lock);
/* All xfrm_state objects are created by xfrm_state_alloc.
@@ -587,15 +565,15 @@ EXPORT_SYMBOL(xfrm_state_delete);
#ifdef CONFIG_SECURITY_NETWORK_XFRM
static inline int
-xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
{
int i, err = 0;
- for (i = 0; i <= xfrm_state_hmask; i++) {
+ for (i = 0; i <= net->xfrm.state_hmask; i++) {
struct hlist_node *entry;
struct xfrm_state *x;
- hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
if (xfrm_id_proto_match(x->id.proto, proto) &&
(err = security_xfrm_state_delete(x)) != 0) {
xfrm_audit_state_delete(x, 0,
@@ -611,26 +589,26 @@ xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
}
#else
static inline int
-xfrm_state_flush_secctx_check(u8 proto, struct xfrm_audit *audit_info)
+xfrm_state_flush_secctx_check(struct net *net, u8 proto, struct xfrm_audit *audit_info)
{
return 0;
}
#endif
-int xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info)
+int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info)
{
int i, err = 0;
spin_lock_bh(&xfrm_state_lock);
- err = xfrm_state_flush_secctx_check(proto, audit_info);
+ err = xfrm_state_flush_secctx_check(net, proto, audit_info);
if (err)
goto out;
- for (i = 0; i <= xfrm_state_hmask; i++) {
+ for (i = 0; i <= net->xfrm.state_hmask; i++) {
struct hlist_node *entry;
struct xfrm_state *x;
restart:
- hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
if (!xfrm_state_kern(x) &&
xfrm_id_proto_match(x->id.proto, proto)) {
xfrm_state_hold(x);
@@ -652,7 +630,7 @@ restart:
out:
spin_unlock_bh(&xfrm_state_lock);
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
return err;
}
EXPORT_SYMBOL(xfrm_state_flush);
@@ -660,8 +638,8 @@ EXPORT_SYMBOL(xfrm_state_flush);
void xfrm_sad_getinfo(struct xfrmk_sadinfo *si)
{
spin_lock_bh(&xfrm_state_lock);
- si->sadcnt = xfrm_state_num;
- si->sadhcnt = xfrm_state_hmask;
+ si->sadcnt = init_net.xfrm.state_num;
+ si->sadhcnt = init_net.xfrm.state_hmask;
si->sadhmcnt = xfrm_state_hashmax;
spin_unlock_bh(&xfrm_state_lock);
}
@@ -681,13 +659,13 @@ xfrm_init_tempsel(struct xfrm_state *x, struct flowi *fl,
return 0;
}
-static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
+static struct xfrm_state *__xfrm_state_lookup(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
{
- unsigned int h = xfrm_spi_hash(daddr, spi, proto, family);
+ unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
struct xfrm_state *x;
struct hlist_node *entry;
- hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_byspi+h, byspi) {
if (x->props.family != family ||
x->id.spi != spi ||
x->id.proto != proto)
@@ -713,13 +691,13 @@ static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi,
return NULL;
}
-static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
+static struct xfrm_state *__xfrm_state_lookup_byaddr(struct net *net, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)
{
- unsigned int h = xfrm_src_hash(daddr, saddr, family);
+ unsigned int h = xfrm_src_hash(net, daddr, saddr, family);
struct xfrm_state *x;
struct hlist_node *entry;
- hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_bysrc+h, bysrc) {
if (x->props.family != family ||
x->id.proto != proto)
continue;
@@ -751,21 +729,23 @@ static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm
static inline struct xfrm_state *
__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)
{
+ struct net *net = xs_net(x);
+
if (use_spi)
- return __xfrm_state_lookup(&x->id.daddr, x->id.spi,
+ return __xfrm_state_lookup(net, &x->id.daddr, x->id.spi,
x->id.proto, family);
else
- return __xfrm_state_lookup_byaddr(&x->id.daddr,
+ return __xfrm_state_lookup_byaddr(net, &x->id.daddr,
&x->props.saddr,
x->id.proto, family);
}
-static void xfrm_hash_grow_check(int have_hash_collision)
+static void xfrm_hash_grow_check(struct net *net, int have_hash_collision)
{
if (have_hash_collision &&
- (xfrm_state_hmask + 1) < xfrm_state_hashmax &&
- xfrm_state_num > xfrm_state_hmask)
- schedule_work(&xfrm_hash_work);
+ (net->xfrm.state_hmask + 1) < xfrm_state_hashmax &&
+ net->xfrm.state_num > net->xfrm.state_hmask)
+ schedule_work(&net->xfrm.state_hash_work);
}
struct xfrm_state *
@@ -774,6 +754,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct xfrm_policy *pol, int *err,
unsigned short family)
{
+ struct net *net = xp_net(pol);
unsigned int h;
struct hlist_node *entry;
struct xfrm_state *x, *x0, *to_put;
@@ -784,8 +765,8 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
to_put = NULL;
spin_lock_bh(&xfrm_state_lock);
- h = xfrm_dst_hash(daddr, saddr, tmpl->reqid, family);
- hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
+ h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, family);
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
if (x->props.family == family &&
x->props.reqid == tmpl->reqid &&
!(x->props.flags & XFRM_STATE_WILDRECV) &&
@@ -829,13 +810,13 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
x = best;
if (!x && !error && !acquire_in_progress) {
if (tmpl->id.spi &&
- (x0 = __xfrm_state_lookup(daddr, tmpl->id.spi,
+ (x0 = __xfrm_state_lookup(net, daddr, tmpl->id.spi,
tmpl->id.proto, family)) != NULL) {
to_put = x0;
error = -EEXIST;
goto out;
}
- x = xfrm_state_alloc();
+ x = xfrm_state_alloc(net);
if (x == NULL) {
error = -ENOMEM;
goto out;
@@ -854,19 +835,19 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
if (km_query(x, tmpl, pol) == 0) {
x->km.state = XFRM_STATE_ACQ;
- list_add(&x->km.all, &xfrm_state_all);
- hlist_add_head(&x->bydst, xfrm_state_bydst+h);
- h = xfrm_src_hash(daddr, saddr, family);
- hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
+ list_add(&x->km.all, &net->xfrm.state_all);
+ hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
+ h = xfrm_src_hash(net, daddr, saddr, family);
+ hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
if (x->id.spi) {
- h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, family);
- hlist_add_head(&x->byspi, xfrm_state_byspi+h);
+ h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, family);
+ hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
}
- x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
- x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
+ x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+ x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
add_timer(&x->timer);
- xfrm_state_num++;
- xfrm_hash_grow_check(x->bydst.next != NULL);
+ net->xfrm.state_num++;
+ xfrm_hash_grow_check(net, x->bydst.next != NULL);
} else {
x->km.state = XFRM_STATE_DEAD;
to_put = x;
@@ -886,7 +867,8 @@ out:
}
struct xfrm_state *
-xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
+xfrm_stateonly_find(struct net *net,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
unsigned short family, u8 mode, u8 proto, u32 reqid)
{
unsigned int h;
@@ -894,8 +876,8 @@ xfrm_stateonly_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct hlist_node *entry;
spin_lock(&xfrm_state_lock);
- h = xfrm_dst_hash(daddr, saddr, reqid, family);
- hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
+ h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
if (x->props.family == family &&
x->props.reqid == reqid &&
!(x->props.flags & XFRM_STATE_WILDRECV) &&
@@ -919,48 +901,50 @@ EXPORT_SYMBOL(xfrm_stateonly_find);
static void __xfrm_state_insert(struct xfrm_state *x)
{
+ struct net *net = xs_net(x);
unsigned int h;
x->genid = ++xfrm_state_genid;
- list_add(&x->km.all, &xfrm_state_all);
+ list_add(&x->km.all, &net->xfrm.state_all);
- h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
+ h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
x->props.reqid, x->props.family);
- hlist_add_head(&x->bydst, xfrm_state_bydst+h);
+ hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
- h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);
- hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
+ h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
+ hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
if (x->id.spi) {
- h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,
+ h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
x->props.family);
- hlist_add_head(&x->byspi, xfrm_state_byspi+h);
+ hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
}
mod_timer(&x->timer, jiffies + HZ);
if (x->replay_maxage)
mod_timer(&x->rtimer, jiffies + x->replay_maxage);
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
- xfrm_state_num++;
+ net->xfrm.state_num++;
- xfrm_hash_grow_check(x->bydst.next != NULL);
+ xfrm_hash_grow_check(net, x->bydst.next != NULL);
}
/* xfrm_state_lock is held */
static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
{
+ struct net *net = xs_net(xnew);
unsigned short family = xnew->props.family;
u32 reqid = xnew->props.reqid;
struct xfrm_state *x;
struct hlist_node *entry;
unsigned int h;
- h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);
- hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
+ h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
if (x->props.family == family &&
x->props.reqid == reqid &&
!xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&
@@ -979,13 +963,13 @@ void xfrm_state_insert(struct xfrm_state *x)
EXPORT_SYMBOL(xfrm_state_insert);
/* xfrm_state_lock is held */
-static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
+static struct xfrm_state *__find_acq_core(struct net *net, unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)
{
- unsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);
+ unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family);
struct hlist_node *entry;
struct xfrm_state *x;
- hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+h, bydst) {
if (x->props.reqid != reqid ||
x->props.mode != mode ||
x->props.family != family ||
@@ -1017,7 +1001,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re
if (!create)
return NULL;
- x = xfrm_state_alloc();
+ x = xfrm_state_alloc(net);
if (likely(x)) {
switch (family) {
case AF_INET:
@@ -1048,27 +1032,28 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re
x->props.family = family;
x->props.mode = mode;
x->props.reqid = reqid;
- x->lft.hard_add_expires_seconds = sysctl_xfrm_acq_expires;
+ x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
xfrm_state_hold(x);
- x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
+ x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
add_timer(&x->timer);
- list_add(&x->km.all, &xfrm_state_all);
- hlist_add_head(&x->bydst, xfrm_state_bydst+h);
- h = xfrm_src_hash(daddr, saddr, family);
- hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
+ list_add(&x->km.all, &net->xfrm.state_all);
+ hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
+ h = xfrm_src_hash(net, daddr, saddr, family);
+ hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
- xfrm_state_num++;
+ net->xfrm.state_num++;
- xfrm_hash_grow_check(x->bydst.next != NULL);
+ xfrm_hash_grow_check(net, x->bydst.next != NULL);
}
return x;
}
-static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq);
+static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 seq);
int xfrm_state_add(struct xfrm_state *x)
{
+ struct net *net = xs_net(x);
struct xfrm_state *x1, *to_put;
int family;
int err;
@@ -1089,7 +1074,7 @@ int xfrm_state_add(struct xfrm_state *x)
}
if (use_spi && x->km.seq) {
- x1 = __xfrm_find_acq_byseq(x->km.seq);
+ x1 = __xfrm_find_acq_byseq(net, x->km.seq);
if (x1 && ((x1->id.proto != x->id.proto) ||
xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family))) {
to_put = x1;
@@ -1098,7 +1083,7 @@ int xfrm_state_add(struct xfrm_state *x)
}
if (use_spi && !x1)
- x1 = __find_acq_core(family, x->props.mode, x->props.reqid,
+ x1 = __find_acq_core(net, family, x->props.mode, x->props.reqid,
x->id.proto,
&x->id.daddr, &x->props.saddr, 0);
@@ -1124,8 +1109,9 @@ EXPORT_SYMBOL(xfrm_state_add);
#ifdef CONFIG_XFRM_MIGRATE
static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig, int *errp)
{
+ struct net *net = xs_net(orig);
int err = -ENOMEM;
- struct xfrm_state *x = xfrm_state_alloc();
+ struct xfrm_state *x = xfrm_state_alloc(net);
if (!x)
goto error;
@@ -1206,9 +1192,9 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
struct hlist_node *entry;
if (m->reqid) {
- h = xfrm_dst_hash(&m->old_daddr, &m->old_saddr,
+ h = xfrm_dst_hash(&init_net, &m->old_daddr, &m->old_saddr,
m->reqid, m->old_family);
- hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {
+ hlist_for_each_entry(x, entry, init_net.xfrm.state_bydst+h, bydst) {
if (x->props.mode != m->mode ||
x->id.proto != m->proto)
continue;
@@ -1223,9 +1209,9 @@ struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m)
return x;
}
} else {
- h = xfrm_src_hash(&m->old_daddr, &m->old_saddr,
+ h = xfrm_src_hash(&init_net, &m->old_daddr, &m->old_saddr,
m->old_family);
- hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {
+ hlist_for_each_entry(x, entry, init_net.xfrm.state_bysrc+h, bysrc) {
if (x->props.mode != m->mode ||
x->id.proto != m->proto)
continue;
@@ -1369,40 +1355,41 @@ int xfrm_state_check_expire(struct xfrm_state *x)
EXPORT_SYMBOL(xfrm_state_check_expire);
struct xfrm_state *
-xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,
+xfrm_state_lookup(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto,
unsigned short family)
{
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
- x = __xfrm_state_lookup(daddr, spi, proto, family);
+ x = __xfrm_state_lookup(net, daddr, spi, proto, family);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
EXPORT_SYMBOL(xfrm_state_lookup);
struct xfrm_state *
-xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,
+xfrm_state_lookup_byaddr(struct net *net,
+ xfrm_address_t *daddr, xfrm_address_t *saddr,
u8 proto, unsigned short family)
{
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
- x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);
+ x = __xfrm_state_lookup_byaddr(net, daddr, saddr, proto, family);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
struct xfrm_state *
-xfrm_find_acq(u8 mode, u32 reqid, u8 proto,
+xfrm_find_acq(struct net *net, u8 mode, u32 reqid, u8 proto,
xfrm_address_t *daddr, xfrm_address_t *saddr,
int create, unsigned short family)
{
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
- x = __find_acq_core(family, mode, reqid, proto, daddr, saddr, create);
+ x = __find_acq_core(net, family, mode, reqid, proto, daddr, saddr, create);
spin_unlock_bh(&xfrm_state_lock);
return x;
@@ -1449,15 +1436,15 @@ EXPORT_SYMBOL(xfrm_state_sort);
/* Silly enough, but I'm lazy to build resolution list */
-static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
+static struct xfrm_state *__xfrm_find_acq_byseq(struct net *net, u32 seq)
{
int i;
- for (i = 0; i <= xfrm_state_hmask; i++) {
+ for (i = 0; i <= net->xfrm.state_hmask; i++) {
struct hlist_node *entry;
struct xfrm_state *x;
- hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
+ hlist_for_each_entry(x, entry, net->xfrm.state_bydst+i, bydst) {
if (x->km.seq == seq &&
x->km.state == XFRM_STATE_ACQ) {
xfrm_state_hold(x);
@@ -1468,12 +1455,12 @@ static struct xfrm_state *__xfrm_find_acq_byseq(u32 seq)
return NULL;
}
-struct xfrm_state *xfrm_find_acq_byseq(u32 seq)
+struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 seq)
{
struct xfrm_state *x;
spin_lock_bh(&xfrm_state_lock);
- x = __xfrm_find_acq_byseq(seq);
+ x = __xfrm_find_acq_byseq(net, seq);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
@@ -1494,6 +1481,7 @@ EXPORT_SYMBOL(xfrm_get_acqseq);
int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
{
+ struct net *net = xs_net(x);
unsigned int h;
struct xfrm_state *x0;
int err = -ENOENT;
@@ -1511,7 +1499,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
err = -ENOENT;
if (minspi == maxspi) {
- x0 = xfrm_state_lookup(&x->id.daddr, minspi, x->id.proto, x->props.family);
+ x0 = xfrm_state_lookup(net, &x->id.daddr, minspi, x->id.proto, x->props.family);
if (x0) {
xfrm_state_put(x0);
goto unlock;
@@ -1521,7 +1509,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
u32 spi = 0;
for (h=0; h<high-low+1; h++) {
spi = low + net_random()%(high-low+1);
- x0 = xfrm_state_lookup(&x->id.daddr, htonl(spi), x->id.proto, x->props.family);
+ x0 = xfrm_state_lookup(net, &x->id.daddr, htonl(spi), x->id.proto, x->props.family);
if (x0 == NULL) {
x->id.spi = htonl(spi);
break;
@@ -1531,8 +1519,8 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
}
if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock);
- h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto, x->props.family);
- hlist_add_head(&x->byspi, xfrm_state_byspi+h);
+ h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+ hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
spin_unlock_bh(&xfrm_state_lock);
err = 0;
@@ -1545,7 +1533,7 @@ unlock:
}
EXPORT_SYMBOL(xfrm_alloc_spi);
-int xfrm_state_walk(struct xfrm_state_walk *walk,
+int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*),
void *data)
{
@@ -1558,10 +1546,10 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
spin_lock_bh(&xfrm_state_lock);
if (list_empty(&walk->all))
- x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all);
+ x = list_first_entry(&net->xfrm.state_all, struct xfrm_state_walk, all);
else
x = list_entry(&walk->all, struct xfrm_state_walk, all);
- list_for_each_entry_from(x, &xfrm_state_all, all) {
+ list_for_each_entry_from(x, &net->xfrm.state_all, all) {
if (x->state == XFRM_STATE_DEAD)
continue;
state = container_of(x, struct xfrm_state, km);
@@ -1660,7 +1648,7 @@ static void xfrm_replay_timer_handler(unsigned long data)
spin_lock(&x->lock);
if (x->km.state == XFRM_STATE_VALID) {
- if (xfrm_aevent_is_on())
+ if (xfrm_aevent_is_on(xs_net(x)))
xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);
else
x->xflags |= XFRM_TIME_DEFER;
@@ -1716,7 +1704,7 @@ void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
x->replay.bitmap |= (1U << diff);
}
- if (xfrm_aevent_is_on())
+ if (xfrm_aevent_is_on(xs_net(x)))
xfrm_replay_notify(x, XFRM_REPLAY_UPDATE);
}
@@ -1749,6 +1737,7 @@ EXPORT_SYMBOL(km_state_notify);
void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
{
+ struct net *net = xs_net(x);
struct km_event c;
c.data.hard = hard;
@@ -1757,7 +1746,7 @@ void km_state_expired(struct xfrm_state *x, int hard, u32 pid)
km_state_notify(x, &c);
if (hard)
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
}
EXPORT_SYMBOL(km_state_expired);
@@ -1800,6 +1789,7 @@ EXPORT_SYMBOL(km_new_mapping);
void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
{
+ struct net *net = xp_net(pol);
struct km_event c;
c.data.hard = hard;
@@ -1808,7 +1798,7 @@ void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid)
km_policy_notify(pol, dir, &c);
if (hard)
- wake_up(&km_waitq);
+ wake_up(&net->xfrm.km_waitq);
}
EXPORT_SYMBOL(km_policy_expired);
@@ -1835,7 +1825,7 @@ int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
EXPORT_SYMBOL(km_migrate);
#endif
-int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
+int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
{
int err = -EINVAL;
int ret;
@@ -1844,7 +1834,7 @@ int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr)
read_lock(&xfrm_km_lock);
list_for_each_entry(km, &xfrm_km_list, list) {
if (km->report) {
- ret = km->report(proto, sel, addr);
+ ret = km->report(net, proto, sel, addr);
if (!ret)
err = ret;
}
@@ -2032,8 +2022,9 @@ int xfrm_init_state(struct xfrm_state *x)
x->inner_mode = inner_mode;
} else {
struct xfrm_mode *inner_mode_iaf;
+ int iafamily = AF_INET;
- inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+ inner_mode = xfrm_get_mode(x->props.mode, x->props.family);
if (inner_mode == NULL)
goto error;
@@ -2041,22 +2032,17 @@ int xfrm_init_state(struct xfrm_state *x)
xfrm_put_mode(inner_mode);
goto error;
}
+ x->inner_mode = inner_mode;
- inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
- if (inner_mode_iaf == NULL)
- goto error;
-
- if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
- xfrm_put_mode(inner_mode_iaf);
- goto error;
- }
+ if (x->props.family == AF_INET)
+ iafamily = AF_INET6;
- if (x->props.family == AF_INET) {
- x->inner_mode = inner_mode;
- x->inner_mode_iaf = inner_mode_iaf;
- } else {
- x->inner_mode = inner_mode_iaf;
- x->inner_mode_iaf = inner_mode;
+ inner_mode_iaf = xfrm_get_mode(x->props.mode, iafamily);
+ if (inner_mode_iaf) {
+ if (inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)
+ x->inner_mode_iaf = inner_mode_iaf;
+ else
+ xfrm_put_mode(inner_mode_iaf);
}
}
@@ -2080,20 +2066,61 @@ error:
EXPORT_SYMBOL(xfrm_init_state);
-void __init xfrm_state_init(void)
+int __net_init xfrm_state_init(struct net *net)
{
unsigned int sz;
+ INIT_LIST_HEAD(&net->xfrm.state_all);
+
sz = sizeof(struct hlist_head) * 8;
- xfrm_state_bydst = xfrm_hash_alloc(sz);
- xfrm_state_bysrc = xfrm_hash_alloc(sz);
- xfrm_state_byspi = xfrm_hash_alloc(sz);
- if (!xfrm_state_bydst || !xfrm_state_bysrc || !xfrm_state_byspi)
- panic("XFRM: Cannot allocate bydst/bysrc/byspi hashes.");
- xfrm_state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
+ net->xfrm.state_bydst = xfrm_hash_alloc(sz);
+ if (!net->xfrm.state_bydst)
+ goto out_bydst;
+ net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
+ if (!net->xfrm.state_bysrc)
+ goto out_bysrc;
+ net->xfrm.state_byspi = xfrm_hash_alloc(sz);
+ if (!net->xfrm.state_byspi)
+ goto out_byspi;
+ net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
+
+ net->xfrm.state_num = 0;
+ INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
+ INIT_HLIST_HEAD(&net->xfrm.state_gc_list);
+ INIT_WORK(&net->xfrm.state_gc_work, xfrm_state_gc_task);
+ init_waitqueue_head(&net->xfrm.km_waitq);
+ return 0;
+
+out_byspi:
+ xfrm_hash_free(net->xfrm.state_bysrc, sz);
+out_bysrc:
+ xfrm_hash_free(net->xfrm.state_bydst, sz);
+out_bydst:
+ return -ENOMEM;
+}
+
+void xfrm_state_fini(struct net *net)
+{
+ struct xfrm_audit audit_info;
+ unsigned int sz;
+
+ flush_work(&net->xfrm.state_hash_work);
+ audit_info.loginuid = -1;
+ audit_info.sessionid = -1;
+ audit_info.secid = 0;
+ xfrm_state_flush(net, IPSEC_PROTO_ANY, &audit_info);
+ flush_work(&net->xfrm.state_gc_work);
+
+ WARN_ON(!list_empty(&net->xfrm.state_all));
- INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task);
+ sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
+ WARN_ON(!hlist_empty(net->xfrm.state_byspi));
+ xfrm_hash_free(net->xfrm.state_byspi, sz);
+ WARN_ON(!hlist_empty(net->xfrm.state_bysrc));
+ xfrm_hash_free(net->xfrm.state_bysrc, sz);
+ WARN_ON(!hlist_empty(net->xfrm.state_bydst));
+ xfrm_hash_free(net->xfrm.state_bydst, sz);
}
#ifdef CONFIG_AUDITSYSCALL
@@ -2109,16 +2136,12 @@ static void xfrm_audit_helper_sainfo(struct xfrm_state *x,
switch(x->props.family) {
case AF_INET:
- audit_log_format(audit_buf,
- " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
- NIPQUAD(x->props.saddr.a4),
- NIPQUAD(x->id.daddr.a4));
+ audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
+ &x->props.saddr.a4, &x->id.daddr.a4);
break;
case AF_INET6:
- audit_log_format(audit_buf,
- " src=" NIP6_FMT " dst=" NIP6_FMT,
- NIP6(*(struct in6_addr *)x->props.saddr.a6),
- NIP6(*(struct in6_addr *)x->id.daddr.a6));
+ audit_log_format(audit_buf, " src=%pI6 dst=%pI6",
+ x->props.saddr.a6, x->id.daddr.a6);
break;
}
@@ -2134,18 +2157,14 @@ static void xfrm_audit_helper_pktinfo(struct sk_buff *skb, u16 family,
switch (family) {
case AF_INET:
iph4 = ip_hdr(skb);
- audit_log_format(audit_buf,
- " src=" NIPQUAD_FMT " dst=" NIPQUAD_FMT,
- NIPQUAD(iph4->saddr),
- NIPQUAD(iph4->daddr));
+ audit_log_format(audit_buf, " src=%pI4 dst=%pI4",
+ &iph4->saddr, &iph4->daddr);
break;
case AF_INET6:
iph6 = ipv6_hdr(skb);
audit_log_format(audit_buf,
- " src=" NIP6_FMT " dst=" NIP6_FMT
- " flowlbl=0x%x%02x%02x",
- NIP6(iph6->saddr),
- NIP6(iph6->daddr),
+ " src=%pI6 dst=%pI6 flowlbl=0x%x%02x%02x",
+ &iph6->saddr,&iph6->daddr,
iph6->flow_lbl[0] & 0x0f,
iph6->flow_lbl[1],
iph6->flow_lbl[2]);
diff --git a/net/xfrm/xfrm_sysctl.c b/net/xfrm/xfrm_sysctl.c
new file mode 100644
index 000000000000..2e6ffb66f06f
--- /dev/null
+++ b/net/xfrm/xfrm_sysctl.c
@@ -0,0 +1,85 @@
+#include <linux/sysctl.h>
+#include <net/net_namespace.h>
+#include <net/xfrm.h>
+
+static void __xfrm_sysctl_init(struct net *net)
+{
+ net->xfrm.sysctl_aevent_etime = XFRM_AE_ETIME;
+ net->xfrm.sysctl_aevent_rseqth = XFRM_AE_SEQT_SIZE;
+ net->xfrm.sysctl_larval_drop = 1;
+ net->xfrm.sysctl_acq_expires = 30;
+}
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table xfrm_table[] = {
+ {
+ .ctl_name = NET_CORE_AEVENT_ETIME,
+ .procname = "xfrm_aevent_etime",
+ .maxlen = sizeof(u32),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .ctl_name = NET_CORE_AEVENT_RSEQTH,
+ .procname = "xfrm_aevent_rseqth",
+ .maxlen = sizeof(u32),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "xfrm_larval_drop",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "xfrm_acq_expires",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {}
+};
+
+int __net_init xfrm_sysctl_init(struct net *net)
+{
+ struct ctl_table *table;
+
+ __xfrm_sysctl_init(net);
+
+ table = kmemdup(xfrm_table, sizeof(xfrm_table), GFP_KERNEL);
+ if (!table)
+ goto out_kmemdup;
+ table[0].data = &net->xfrm.sysctl_aevent_etime;
+ table[1].data = &net->xfrm.sysctl_aevent_rseqth;
+ table[2].data = &net->xfrm.sysctl_larval_drop;
+ table[3].data = &net->xfrm.sysctl_acq_expires;
+
+ net->xfrm.sysctl_hdr = register_net_sysctl_table(net, net_core_path, table);
+ if (!net->xfrm.sysctl_hdr)
+ goto out_register;
+ return 0;
+
+out_register:
+ kfree(table);
+out_kmemdup:
+ return -ENOMEM;
+}
+
+void xfrm_sysctl_fini(struct net *net)
+{
+ struct ctl_table *table;
+
+ table = net->xfrm.sysctl_hdr->ctl_table_arg;
+ unregister_net_sysctl_table(net->xfrm.sysctl_hdr);
+ kfree(table);
+}
+#else
+int __net_init xfrm_sysctl_init(struct net *net)
+{
+ __xfrm_sysctl_init(net);
+ return 0;
+}
+#endif
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index a278a6f3b991..b95a2d64eb59 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -316,11 +316,12 @@ static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
x->replay_maxdiff = nla_get_u32(rt);
}
-static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
+static struct xfrm_state *xfrm_state_construct(struct net *net,
+ struct xfrm_usersa_info *p,
struct nlattr **attrs,
int *errp)
{
- struct xfrm_state *x = xfrm_state_alloc();
+ struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM;
if (!x)
@@ -367,9 +368,9 @@ static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,
goto error;
x->km.seq = p->seq;
- x->replay_maxdiff = sysctl_xfrm_aevent_rseqth;
+ x->replay_maxdiff = net->xfrm.sysctl_aevent_rseqth;
/* sysctl_xfrm_aevent_etime is in 100ms units */
- x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M;
+ x->replay_maxage = (net->xfrm.sysctl_aevent_etime*HZ)/XFRM_AE_ETH_M;
x->preplay.bitmap = 0;
x->preplay.seq = x->replay.seq+x->replay_maxdiff;
x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;
@@ -391,6 +392,7 @@ error_no_put:
static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_usersa_info *p = nlmsg_data(nlh);
struct xfrm_state *x;
int err;
@@ -403,7 +405,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
return err;
- x = xfrm_state_construct(p, attrs, &err);
+ x = xfrm_state_construct(net, p, attrs, &err);
if (!x)
return err;
@@ -431,7 +433,8 @@ out:
return err;
}
-static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,
+static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
+ struct xfrm_usersa_id *p,
struct nlattr **attrs,
int *errp)
{
@@ -440,7 +443,7 @@ static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,
if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {
err = -ESRCH;
- x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family);
+ x = xfrm_state_lookup(net, &p->daddr, p->spi, p->proto, p->family);
} else {
xfrm_address_t *saddr = NULL;
@@ -451,8 +454,8 @@ static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,
}
err = -ESRCH;
- x = xfrm_state_lookup_byaddr(&p->daddr, saddr, p->proto,
- p->family);
+ x = xfrm_state_lookup_byaddr(net, &p->daddr, saddr,
+ p->proto, p->family);
}
out:
@@ -464,6 +467,7 @@ static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,
static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
int err = -ESRCH;
struct km_event c;
@@ -472,7 +476,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
u32 sessionid = NETLINK_CB(skb).sessionid;
u32 sid = NETLINK_CB(skb).sid;
- x = xfrm_user_state_lookup(p, attrs, &err);
+ x = xfrm_user_state_lookup(net, p, attrs, &err);
if (x == NULL)
return err;
@@ -615,6 +619,7 @@ static int xfrm_dump_sa_done(struct netlink_callback *cb)
static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
struct xfrm_dump_info info;
@@ -631,7 +636,7 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
xfrm_state_walk_init(walk, 0);
}
- (void) xfrm_state_walk(walk, dump_one_state, &info);
+ (void) xfrm_state_walk(net, walk, dump_one_state, &info);
return skb->len;
}
@@ -703,6 +708,7 @@ nla_put_failure:
static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct sk_buff *r_skb;
u32 *flags = nlmsg_data(nlh);
u32 spid = NETLINK_CB(skb).pid;
@@ -715,7 +721,7 @@ static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
if (build_spdinfo(r_skb, spid, seq, *flags) < 0)
BUG();
- return nlmsg_unicast(xfrm_nl, r_skb, spid);
+ return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid);
}
static inline size_t xfrm_sadinfo_msgsize(void)
@@ -756,6 +762,7 @@ nla_put_failure:
static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct sk_buff *r_skb;
u32 *flags = nlmsg_data(nlh);
u32 spid = NETLINK_CB(skb).pid;
@@ -768,18 +775,19 @@ static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
if (build_sadinfo(r_skb, spid, seq, *flags) < 0)
BUG();
- return nlmsg_unicast(xfrm_nl, r_skb, spid);
+ return nlmsg_unicast(net->xfrm.nlsk, r_skb, spid);
}
static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_usersa_id *p = nlmsg_data(nlh);
struct xfrm_state *x;
struct sk_buff *resp_skb;
int err = -ESRCH;
- x = xfrm_user_state_lookup(p, attrs, &err);
+ x = xfrm_user_state_lookup(net, p, attrs, &err);
if (x == NULL)
goto out_noput;
@@ -787,7 +795,7 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
if (IS_ERR(resp_skb)) {
err = PTR_ERR(resp_skb);
} else {
- err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid);
+ err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid);
}
xfrm_state_put(x);
out_noput:
@@ -820,6 +828,7 @@ static int verify_userspi_info(struct xfrm_userspi_info *p)
static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
struct xfrm_userspi_info *p;
struct sk_buff *resp_skb;
@@ -837,7 +846,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
x = NULL;
if (p->info.seq) {
- x = xfrm_find_acq_byseq(p->info.seq);
+ x = xfrm_find_acq_byseq(net, p->info.seq);
if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) {
xfrm_state_put(x);
x = NULL;
@@ -845,7 +854,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (!x)
- x = xfrm_find_acq(p->info.mode, p->info.reqid,
+ x = xfrm_find_acq(net, p->info.mode, p->info.reqid,
p->info.id.proto, daddr,
&p->info.saddr, 1,
family);
@@ -863,7 +872,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
}
- err = nlmsg_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid);
+ err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).pid);
out:
xfrm_state_put(x);
@@ -1078,9 +1087,9 @@ static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_i
p->share = XFRM_SHARE_ANY; /* XXX xp->share */
}
-static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp)
+static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp)
{
- struct xfrm_policy *xp = xfrm_policy_alloc(GFP_KERNEL);
+ struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
int err;
if (!xp) {
@@ -1110,6 +1119,7 @@ static struct xfrm_policy *xfrm_policy_construct(struct xfrm_userpolicy_info *p,
static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
struct xfrm_policy *xp;
struct km_event c;
@@ -1126,7 +1136,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
return err;
- xp = xfrm_policy_construct(p, attrs, &err);
+ xp = xfrm_policy_construct(net, p, attrs, &err);
if (!xp)
return err;
@@ -1263,6 +1273,7 @@ static int xfrm_dump_policy_done(struct netlink_callback *cb)
static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
struct xfrm_dump_info info;
@@ -1279,7 +1290,7 @@ static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
}
- (void) xfrm_policy_walk(walk, dump_one_policy, &info);
+ (void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
return skb->len;
}
@@ -1311,6 +1322,7 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp;
struct xfrm_userpolicy_id *p;
u8 type = XFRM_POLICY_TYPE_MAIN;
@@ -1330,7 +1342,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
if (p->index)
- xp = xfrm_policy_byid(type, p->dir, p->index, delete, &err);
+ xp = xfrm_policy_byid(net, type, p->dir, p->index, delete, &err);
else {
struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_sec_ctx *ctx;
@@ -1347,7 +1359,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
return err;
}
- xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, ctx,
+ xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx,
delete, &err);
security_xfrm_policy_free(ctx);
}
@@ -1361,7 +1373,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
if (IS_ERR(resp_skb)) {
err = PTR_ERR(resp_skb);
} else {
- err = nlmsg_unicast(xfrm_nl, resp_skb,
+ err = nlmsg_unicast(net->xfrm.nlsk, resp_skb,
NETLINK_CB(skb).pid);
}
} else {
@@ -1390,6 +1402,7 @@ out:
static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct km_event c;
struct xfrm_usersa_flush *p = nlmsg_data(nlh);
struct xfrm_audit audit_info;
@@ -1398,13 +1411,14 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
audit_info.loginuid = NETLINK_CB(skb).loginuid;
audit_info.sessionid = NETLINK_CB(skb).sessionid;
audit_info.secid = NETLINK_CB(skb).sid;
- err = xfrm_state_flush(p->proto, &audit_info);
+ err = xfrm_state_flush(net, p->proto, &audit_info);
if (err)
return err;
c.data.proto = p->proto;
c.event = nlh->nlmsg_type;
c.seq = nlh->nlmsg_seq;
c.pid = nlh->nlmsg_pid;
+ c.net = net;
km_state_notify(NULL, &c);
return 0;
@@ -1457,6 +1471,7 @@ nla_put_failure:
static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
struct sk_buff *r_skb;
int err;
@@ -1468,7 +1483,7 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
if (r_skb == NULL)
return -ENOMEM;
- x = xfrm_state_lookup(&id->daddr, id->spi, id->proto, id->family);
+ x = xfrm_state_lookup(net, &id->daddr, id->spi, id->proto, id->family);
if (x == NULL) {
kfree_skb(r_skb);
return -ESRCH;
@@ -1486,7 +1501,7 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
if (build_aevent(r_skb, x, &c) < 0)
BUG();
- err = nlmsg_unicast(xfrm_nl, r_skb, NETLINK_CB(skb).pid);
+ err = nlmsg_unicast(net->xfrm.nlsk, r_skb, NETLINK_CB(skb).pid);
spin_unlock_bh(&x->lock);
xfrm_state_put(x);
return err;
@@ -1495,6 +1510,7 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
struct km_event c;
int err = - EINVAL;
@@ -1509,7 +1525,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
if (!(nlh->nlmsg_flags&NLM_F_REPLACE))
return err;
- x = xfrm_state_lookup(&p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
+ x = xfrm_state_lookup(net, &p->sa_id.daddr, p->sa_id.spi, p->sa_id.proto, p->sa_id.family);
if (x == NULL)
return -ESRCH;
@@ -1534,6 +1550,7 @@ out:
static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct km_event c;
u8 type = XFRM_POLICY_TYPE_MAIN;
int err;
@@ -1546,13 +1563,14 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
audit_info.loginuid = NETLINK_CB(skb).loginuid;
audit_info.sessionid = NETLINK_CB(skb).sessionid;
audit_info.secid = NETLINK_CB(skb).sid;
- err = xfrm_policy_flush(type, &audit_info);
+ err = xfrm_policy_flush(net, type, &audit_info);
if (err)
return err;
c.data.type = type;
c.event = nlh->nlmsg_type;
c.seq = nlh->nlmsg_seq;
c.pid = nlh->nlmsg_pid;
+ c.net = net;
km_policy_notify(NULL, 0, &c);
return 0;
}
@@ -1560,6 +1578,7 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp;
struct xfrm_user_polexpire *up = nlmsg_data(nlh);
struct xfrm_userpolicy_info *p = &up->pol;
@@ -1571,7 +1590,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
return err;
if (p->index)
- xp = xfrm_policy_byid(type, p->dir, p->index, 0, &err);
+ xp = xfrm_policy_byid(net, type, p->dir, p->index, 0, &err);
else {
struct nlattr *rt = attrs[XFRMA_SEC_CTX];
struct xfrm_sec_ctx *ctx;
@@ -1588,7 +1607,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
return err;
}
- xp = xfrm_policy_bysel_ctx(type, p->dir, &p->sel, ctx, 0, &err);
+ xp = xfrm_policy_bysel_ctx(net, type, p->dir, &p->sel, ctx, 0, &err);
security_xfrm_policy_free(ctx);
}
if (xp == NULL)
@@ -1623,12 +1642,13 @@ out:
static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_state *x;
int err;
struct xfrm_user_expire *ue = nlmsg_data(nlh);
struct xfrm_usersa_info *p = &ue->state;
- x = xfrm_state_lookup(&p->id.daddr, p->id.spi, p->id.proto, p->family);
+ x = xfrm_state_lookup(net, &p->id.daddr, p->id.spi, p->id.proto, p->family);
err = -ENOENT;
if (x == NULL)
@@ -1657,31 +1677,27 @@ out:
static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr **attrs)
{
+ struct net *net = sock_net(skb->sk);
struct xfrm_policy *xp;
struct xfrm_user_tmpl *ut;
int i;
struct nlattr *rt = attrs[XFRMA_TMPL];
struct xfrm_user_acquire *ua = nlmsg_data(nlh);
- struct xfrm_state *x = xfrm_state_alloc();
+ struct xfrm_state *x = xfrm_state_alloc(net);
int err = -ENOMEM;
if (!x)
- return err;
+ goto nomem;
err = verify_newpolicy_info(&ua->policy);
- if (err) {
- printk("BAD policy passed\n");
- kfree(x);
- return err;
- }
+ if (err)
+ goto bad_policy;
/* build an XP */
- xp = xfrm_policy_construct(&ua->policy, attrs, &err);
- if (!xp) {
- kfree(x);
- return err;
- }
+ xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+ if (!xp)
+ goto free_state;
memcpy(&x->id, &ua->id, sizeof(ua->id));
memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
@@ -1706,6 +1722,13 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
kfree(xp);
return 0;
+
+bad_policy:
+ printk("BAD policy passed\n");
+free_state:
+ kfree(x);
+nomem:
+ return err;
}
#ifdef CONFIG_XFRM_MIGRATE
@@ -1869,6 +1892,7 @@ static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
struct xfrm_migrate *m, int num_migrate,
struct xfrm_kmaddress *k)
{
+ struct net *net = &init_net;
struct sk_buff *skb;
skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC);
@@ -1879,7 +1903,7 @@ static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
if (build_migrate(skb, m, num_migrate, k, sel, dir, type) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MIGRATE, GFP_ATOMIC);
}
#else
static int xfrm_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type,
@@ -1968,6 +1992,7 @@ static struct xfrm_link {
static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
+ struct net *net = sock_net(skb->sk);
struct nlattr *attrs[XFRMA_MAX+1];
struct xfrm_link *link;
int type, err;
@@ -1989,7 +2014,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (link->dump == NULL)
return -EINVAL;
- return netlink_dump_start(xfrm_nl, skb, nlh, link->dump, link->done);
+ return netlink_dump_start(net->xfrm.nlsk, skb, nlh, link->dump, link->done);
}
err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, XFRMA_MAX,
@@ -2033,6 +2058,7 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, struct km_eve
static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
{
+ struct net *net = xs_net(x);
struct sk_buff *skb;
skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
@@ -2042,11 +2068,12 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, struct km_event *c)
if (build_expire(skb, x, c) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
}
static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c)
{
+ struct net *net = xs_net(x);
struct sk_buff *skb;
skb = nlmsg_new(xfrm_aevent_msgsize(), GFP_ATOMIC);
@@ -2056,11 +2083,12 @@ static int xfrm_aevent_state_notify(struct xfrm_state *x, struct km_event *c)
if (build_aevent(skb, x, c) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_AEVENTS, GFP_ATOMIC);
}
static int xfrm_notify_sa_flush(struct km_event *c)
{
+ struct net *net = c->net;
struct xfrm_usersa_flush *p;
struct nlmsghdr *nlh;
struct sk_buff *skb;
@@ -2081,7 +2109,7 @@ static int xfrm_notify_sa_flush(struct km_event *c)
nlmsg_end(skb, nlh);
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
}
static inline size_t xfrm_sa_len(struct xfrm_state *x)
@@ -2111,6 +2139,7 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c)
{
+ struct net *net = xs_net(x);
struct xfrm_usersa_info *p;
struct xfrm_usersa_id *id;
struct nlmsghdr *nlh;
@@ -2155,7 +2184,7 @@ static int xfrm_notify_sa(struct xfrm_state *x, struct km_event *c)
nlmsg_end(skb, nlh);
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_SA, GFP_ATOMIC);
nla_put_failure:
/* Somebody screwed up with xfrm_sa_len! */
@@ -2235,6 +2264,7 @@ nlmsg_failure:
static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
struct xfrm_policy *xp, int dir)
{
+ struct net *net = xs_net(x);
struct sk_buff *skb;
skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
@@ -2244,7 +2274,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
if (build_acquire(skb, x, xt, xp, dir) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_ACQUIRE, GFP_ATOMIC);
}
/* User gives us xfrm_user_policy_info followed by an array of 0
@@ -2253,6 +2283,7 @@ static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
u8 *data, int len, int *dir)
{
+ struct net *net = sock_net(sk);
struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data;
struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1);
struct xfrm_policy *xp;
@@ -2291,7 +2322,7 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
if (p->dir > XFRM_POLICY_OUT)
return NULL;
- xp = xfrm_policy_alloc(GFP_KERNEL);
+ xp = xfrm_policy_alloc(net, GFP_KERNEL);
if (xp == NULL) {
*dir = -ENOBUFS;
return NULL;
@@ -2344,6 +2375,7 @@ nlmsg_failure:
static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c)
{
+ struct net *net = xp_net(xp);
struct sk_buff *skb;
skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC);
@@ -2353,11 +2385,12 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, struct km_eve
if (build_polexpire(skb, xp, dir, c) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_EXPIRE, GFP_ATOMIC);
}
static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *c)
{
+ struct net *net = xp_net(xp);
struct xfrm_userpolicy_info *p;
struct xfrm_userpolicy_id *id;
struct nlmsghdr *nlh;
@@ -2408,7 +2441,7 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, struct km_event *
nlmsg_end(skb, nlh);
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
nlmsg_failure:
kfree_skb(skb);
@@ -2417,6 +2450,7 @@ nlmsg_failure:
static int xfrm_notify_policy_flush(struct km_event *c)
{
+ struct net *net = c->net;
struct nlmsghdr *nlh;
struct sk_buff *skb;
@@ -2432,7 +2466,7 @@ static int xfrm_notify_policy_flush(struct km_event *c)
nlmsg_end(skb, nlh);
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_POLICY, GFP_ATOMIC);
nlmsg_failure:
kfree_skb(skb);
@@ -2488,8 +2522,8 @@ nla_put_failure:
return -EMSGSIZE;
}
-static int xfrm_send_report(u8 proto, struct xfrm_selector *sel,
- xfrm_address_t *addr)
+static int xfrm_send_report(struct net *net, u8 proto,
+ struct xfrm_selector *sel, xfrm_address_t *addr)
{
struct sk_buff *skb;
@@ -2500,7 +2534,59 @@ static int xfrm_send_report(u8 proto, struct xfrm_selector *sel,
if (build_report(skb, proto, sel, addr) < 0)
BUG();
- return nlmsg_multicast(xfrm_nl, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC);
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_REPORT, GFP_ATOMIC);
+}
+
+static inline size_t xfrm_mapping_msgsize(void)
+{
+ return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping));
+}
+
+static int build_mapping(struct sk_buff *skb, struct xfrm_state *x,
+ xfrm_address_t *new_saddr, __be16 new_sport)
+{
+ struct xfrm_user_mapping *um;
+ struct nlmsghdr *nlh;
+
+ nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_MAPPING, sizeof(*um), 0);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ um = nlmsg_data(nlh);
+
+ memcpy(&um->id.daddr, &x->id.daddr, sizeof(um->id.daddr));
+ um->id.spi = x->id.spi;
+ um->id.family = x->props.family;
+ um->id.proto = x->id.proto;
+ memcpy(&um->new_saddr, new_saddr, sizeof(um->new_saddr));
+ memcpy(&um->old_saddr, &x->props.saddr, sizeof(um->old_saddr));
+ um->new_sport = new_sport;
+ um->old_sport = x->encap->encap_sport;
+ um->reqid = x->props.reqid;
+
+ return nlmsg_end(skb, nlh);
+}
+
+static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
+ __be16 sport)
+{
+ struct net *net = xs_net(x);
+ struct sk_buff *skb;
+
+ if (x->id.proto != IPPROTO_ESP)
+ return -EINVAL;
+
+ if (!x->encap)
+ return -EINVAL;
+
+ skb = nlmsg_new(xfrm_mapping_msgsize(), GFP_ATOMIC);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ if (build_mapping(skb, x, ipaddr, sport) < 0)
+ BUG();
+
+ return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
}
static struct xfrm_mgr netlink_mgr = {
@@ -2511,35 +2597,56 @@ static struct xfrm_mgr netlink_mgr = {
.notify_policy = xfrm_send_policy_notify,
.report = xfrm_send_report,
.migrate = xfrm_send_migrate,
+ .new_mapping = xfrm_send_mapping,
};
-static int __init xfrm_user_init(void)
+static int __net_init xfrm_user_net_init(struct net *net)
{
struct sock *nlsk;
- printk(KERN_INFO "Initializing XFRM netlink socket\n");
-
- nlsk = netlink_kernel_create(&init_net, NETLINK_XFRM, XFRMNLGRP_MAX,
+ nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX,
xfrm_netlink_rcv, NULL, THIS_MODULE);
if (nlsk == NULL)
return -ENOMEM;
- rcu_assign_pointer(xfrm_nl, nlsk);
-
- xfrm_register_km(&netlink_mgr);
-
+ rcu_assign_pointer(net->xfrm.nlsk, nlsk);
return 0;
}
-static void __exit xfrm_user_exit(void)
+static void __net_exit xfrm_user_net_exit(struct net *net)
{
- struct sock *nlsk = xfrm_nl;
+ struct sock *nlsk = net->xfrm.nlsk;
- xfrm_unregister_km(&netlink_mgr);
- rcu_assign_pointer(xfrm_nl, NULL);
+ rcu_assign_pointer(net->xfrm.nlsk, NULL);
synchronize_rcu();
netlink_kernel_release(nlsk);
}
+static struct pernet_operations xfrm_user_net_ops = {
+ .init = xfrm_user_net_init,
+ .exit = xfrm_user_net_exit,
+};
+
+static int __init xfrm_user_init(void)
+{
+ int rv;
+
+ printk(KERN_INFO "Initializing XFRM netlink socket\n");
+
+ rv = register_pernet_subsys(&xfrm_user_net_ops);
+ if (rv < 0)
+ return rv;
+ rv = xfrm_register_km(&netlink_mgr);
+ if (rv < 0)
+ unregister_pernet_subsys(&xfrm_user_net_ops);
+ return rv;
+}
+
+static void __exit xfrm_user_exit(void)
+{
+ xfrm_unregister_km(&netlink_mgr);
+ unregister_pernet_subsys(&xfrm_user_net_ops);
+}
+
module_init(xfrm_user_init);
module_exit(xfrm_user_exit);
MODULE_LICENSE("GPL");
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index 982dcae7bbe2..c29be8f90248 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -25,6 +25,13 @@ basetarget = $(basename $(notdir $@))
escsq = $(subst $(squote),'\$(squote)',$1)
###
+# Easy method for doing a status message
+ kecho := :
+ quiet_kecho := echo
+silent_kecho := :
+kecho := $($(quiet)kecho)
+
+###
# filechk is used to check if the content of a generated file is updated.
# Sample usage:
# define filechk_sample
@@ -39,22 +46,15 @@ escsq = $(subst $(squote),'\$(squote)',$1)
# - If they are equal no change, and no timestamp update
# - stdin is piped in from the first prerequisite ($<) so one has
# to specify a valid file as first prerequisite (often the kbuild file)
- chk_filechk = :
- quiet_chk_filechk = echo ' CHK $@'
-silent_chk_filechk = :
- upd_filechk = :
- quiet_upd_filechk = echo ' UPD $@'
-silent_upd_filechk = :
-
define filechk
$(Q)set -e; \
- $($(quiet)chk_filechk); \
+ $(kecho) ' CHK $@'; \
mkdir -p $(dir $@); \
$(filechk_$(1)) < $< > $@.tmp; \
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
rm -f $@.tmp; \
else \
- $($(quiet)upd_filechk); \
+ $(kecho) ' UPD $@'; \
mv -f $@.tmp $@; \
fi
endef
@@ -144,7 +144,9 @@ ld-option = $(call try-run,\
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
# Prefix -I with $(srctree) if it is not an absolute path.
-addtree = $(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1)
+# skip if -I has no parameter
+addtree = $(if $(patsubst -I%,%,$(1)), \
+$(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1))
# Find all -I options and call addtree
flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o)))
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 7a176773af85..5d900307de3e 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -151,14 +151,20 @@ cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $<
$(obj)/%.i: $(src)/%.c FORCE
$(call if_changed_dep,cc_i_c)
+cmd_genksyms = \
+ $(CPP) -D__GENKSYMS__ $(c_flags) $< | \
+ $(GENKSYMS) -T $@ -A -a $(ARCH) \
+ $(if $(KBUILD_PRESERVE),-p) \
+ $(if $(1),-r $(firstword $(wildcard $(@:.symtypes=.symref) /dev/null)))
+
quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@
-cmd_cc_symtypes_c = \
- $(CPP) -D__GENKSYMS__ $(c_flags) $< \
- | $(GENKSYMS) -T $@ >/dev/null; \
- test -s $@ || rm -f $@
+cmd_cc_symtypes_c = \
+ set -e; \
+ $(call cmd_genksyms, true) >/dev/null; \
+ test -s $@ || rm -f $@
$(obj)/%.symtypes : $(src)/%.c FORCE
- $(call if_changed_dep,cc_symtypes_c)
+ $(call cmd,cc_symtypes_c)
# C (.c) files
# The C file is compiled and updated dependency information is generated.
@@ -171,30 +177,38 @@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
else
# When module versioning is enabled the following steps are executed:
-# o compile a .tmp_<file>.o from <file>.c
-# o if .tmp_<file>.o doesn't contain a __ksymtab version, i.e. does
-# not export symbols, we just rename .tmp_<file>.o to <file>.o and
+# o compile a .tmp_<file>.s from <file>.c
+# o if .tmp_<file>.s doesn't contain a __ksymtab version, i.e. does
+# not export symbols, we just assemble .tmp_<file>.s to <file>.o and
# are done.
# o otherwise, we calculate symbol versions using the good old
# genksyms on the preprocessed source and postprocess them in a way
-# that they are usable as a linker script
-# o generate <file>.o from .tmp_<file>.o using the linker to
-# replace the unresolved symbols __crc_exported_symbol with
-# the actual value of the checksum generated by genksyms
+# that they are usable as assembly source
+# o assemble <file>.o from .tmp_<file>.s forcing inclusion of directives
+# defining the actual values of __crc_*, followed by objcopy-ing them
+# to force these symbols to be local to permit stripping them later.
+s_file = $(@D)/.tmp_$(@F:.o=.s)
+v_file = $(@D)/.tmp_$(@F:.o=.v)
+tmp_o_file = $(@D)/.tmp_$(@F)
+no_g_c_flags = $(filter-out -g%,$(c_flags))
+
+cmd_cc_o_c = $(CC) $(c_flags) -S -o $(s_file) $<
-cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
cmd_modversions = \
- if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
- $(CPP) -D__GENKSYMS__ $(c_flags) $< \
- | $(GENKSYMS) $(if $(KBUILD_SYMTYPES), \
- -T $(@D)/$(@F:.o=.symtypes)) -a $(ARCH) \
- > $(@D)/.tmp_$(@F:.o=.ver); \
- \
- $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \
- -T $(@D)/.tmp_$(@F:.o=.ver); \
- rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \
+ if grep -q __ksymtab $(s_file); then \
+ if $(call cmd_genksyms, $(KBUILD_SYMTYPES)) > $(v_file) \
+ && $(CC) $(no_g_c_flags) -c -Wa,$(v_file) \
+ -o $(tmp_o_file) $(s_file) \
+ && $(OBJCOPY) -L '__crc_*' -L '___crc_*' -w \
+ $(tmp_o_file) $@; \
+ then \
+ : ; \
+ else \
+ rm -f $@; exit 1; \
+ fi; \
else \
- mv -f $(@D)/.tmp_$(@F) $@; \
+ rm -f $(v_file); \
+ $(CC) $(no_g_c_flags) -c -o $@ $(s_file); \
fi;
endif
@@ -211,7 +225,12 @@ define rule_cc_o_c
$(cmd_record_mcount) \
scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > \
$(dot-target).tmp; \
- rm -f $(depfile); \
+ if [ -r $(@D)/.tmp_$(@F:.o=.v) ]; then \
+ echo >> $(dot-target).tmp; \
+ echo '$@: $(GENKSYMS)' >> $(dot-target).tmp; \
+ echo '$(GENKSYMS):: ;' >> $(dot-target).tmp; \
+ fi; \
+ rm -f $(depfile) $(@D)/.tmp_$(@F:.o=.?); \
mv -f $(dot-target).tmp $(dot-target).cmd
endef
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index b4ca38a21158..e06365775bdf 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -104,9 +104,11 @@ else
debug_flags =
endif
-orig_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o)
+orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
+ $(ccflags-y) $(CFLAGS_$(basetarget).o)
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
-_a_flags = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o)
+_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) \
+ $(asflags-y) $(AFLAGS_$(basetarget).o)
_cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(@F))
# If building the kernel in a separate objtree expand all occurrences
@@ -127,15 +129,16 @@ __a_flags = $(call flags,_a_flags)
__cpp_flags = $(call flags,_cpp_flags)
endif
-c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
- $(__c_flags) $(modkern_cflags) \
+c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
+ $(__c_flags) $(modkern_cflags) \
-D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags) \
$(debug_flags)
-a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(KBUILD_CPPFLAGS) \
+a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(__a_flags) $(modkern_aflags)
-cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags)
+cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
+ $(__cpp_flags)
ld_flags = $(LDFLAGS) $(ldflags-y)
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index efa5d940e632..a5122dce1264 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -17,7 +17,8 @@ __modinst: $(modules)
@:
quiet_cmd_modules_install = INSTALL $@
- cmd_modules_install = mkdir -p $(2); cp $@ $(2) ; $(mod_strip_cmd) $(2)/$(notdir $@)
+ cmd_modules_install = mkdir -p $(2); \
+ $(mod_strip_cmd) $@ $(2)/$(notdir $@) || cp $@ $(2)
# Modules built outside the kernel source tree go into extra by default
INSTALL_MOD_DIR ?= extra
diff --git a/scripts/decodecode b/scripts/decodecode
index 235d3938529d..4b00647814bc 100755
--- a/scripts/decodecode
+++ b/scripts/decodecode
@@ -7,7 +7,7 @@
# AFLAGS=--32 decodecode < 386.oops
cleanup() {
- rm -f $T $T.s $T.o
+ rm -f $T $T.s $T.o $T.oo $T.aa $T.aaa
exit 1
}
@@ -44,21 +44,33 @@ if [ $marker -eq 0 ]; then
marker=`expr index "$code" "\("`
fi
+touch $T.oo
if [ $marker -ne 0 ]; then
- beforemark=`echo "$code" | cut -c-$((${marker} - 1))`
+ echo All code >> $T.oo
+ echo ======== >> $T.oo
+ beforemark=`echo "$code"`
echo -n " .byte 0x" > $T.s
- echo $beforemark | sed -e 's/ /,0x/g' >> $T.s
- as $AFLAGS -o $T.o $T.s
- objdump -S $T.o
- rm $T.o $T.s
+ echo $beforemark | sed -e 's/ /,0x/g' | sed -e 's/<//g' | sed -e 's/>//g' >> $T.s
+ as $AFLAGS -o $T.o $T.s &> /dev/null
+ objdump -S $T.o | grep -v "/tmp" | grep -v "Disassembly" | grep -v "\.text" | grep -v "^$" &> $T.ooo
+ cat $T.ooo >> $T.oo
+ rm -f $T.o $T.s $T.ooo
# and fix code at-and-after marker
code=`echo "$code" | cut -c$((${marker} + 1))-`
fi
-
+echo Code starting with the faulting instruction > $T.aa
+echo =========================================== >> $T.aa
code=`echo $code | sed -e 's/ [<(]/ /;s/[>)] / /;s/ /,0x/g'`
echo -n " .byte 0x" > $T.s
echo $code >> $T.s
-as $AFLAGS -o $T.o $T.s
-objdump -S $T.o
-rm $T $T.s $T.o
+as $AFLAGS -o $T.o $T.s &> /dev/null
+objdump -S $T.o | grep -v "Disassembly" | grep -v "/tmp" | grep -v "\.text" | grep -v "^$" &> $T.aaa
+cat $T.aaa >> $T.aa
+
+faultline=`cat $T.aaa | head -1 | cut -d":" -f2`
+
+cat $T.oo | sed -e "s/\($faultline\)/\*\1 <-- trapping instruction/g"
+echo
+cat $T.aa
+cleanup
diff --git a/scripts/extract-ikconfig b/scripts/extract-ikconfig
index 8187e6f0dc2f..72997c353cb3 100755
--- a/scripts/extract-ikconfig
+++ b/scripts/extract-ikconfig
@@ -8,8 +8,8 @@ test -e $binoffset || cc -o $binoffset ./scripts/binoffset.c || exit 1
IKCFG_ST="0x49 0x4b 0x43 0x46 0x47 0x5f 0x53 0x54"
IKCFG_ED="0x49 0x4b 0x43 0x46 0x47 0x5f 0x45 0x44"
-function dump_config {
- typeset file="$1"
+dump_config() {
+ file="$1"
start=`$binoffset $file $IKCFG_ST 2>/dev/null`
[ "$?" != "0" ] && start="-1"
@@ -18,8 +18,8 @@ function dump_config {
fi
end=`$binoffset $file $IKCFG_ED 2>/dev/null`
- let start="$start + 8"
- let size="$end - $start"
+ start=`expr $start + 8`
+ size=`expr $end - $start`
dd if="$file" ibs=1 skip="$start" count="$size" 2>/dev/null | zcat
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c
index c249274e005a..f8bb4cabd62d 100644
--- a/scripts/genksyms/genksyms.c
+++ b/scripts/genksyms/genksyms.c
@@ -42,7 +42,8 @@ static FILE *debugfile;
int cur_line = 1;
char *cur_filename;
-static int flag_debug, flag_dump_defs, flag_dump_types, flag_warnings;
+static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
+ flag_preserve, flag_warnings, flag_asm;
static const char *arch = "";
static const char *mod_prefix = "";
@@ -58,6 +59,8 @@ static const char *const symbol_type_name[] = {
static int equal_list(struct string_list *a, struct string_list *b);
static void print_list(FILE * f, struct string_list *list);
+static void print_location(void);
+static void print_type_name(enum symbol_type type, const char *name);
/*----------------------------------------------------------------------*/
@@ -151,27 +154,83 @@ struct symbol *find_symbol(const char *name, enum symbol_type ns)
for (sym = symtab[h]; sym; sym = sym->hash_next)
if (map_to_ns(sym->type) == map_to_ns(ns) &&
- strcmp(name, sym->name) == 0)
+ strcmp(name, sym->name) == 0 &&
+ sym->is_declared)
break;
return sym;
}
-struct symbol *add_symbol(const char *name, enum symbol_type type,
- struct string_list *defn, int is_extern)
+static int is_unknown_symbol(struct symbol *sym)
+{
+ struct string_list *defn;
+
+ return ((sym->type == SYM_STRUCT ||
+ sym->type == SYM_UNION ||
+ sym->type == SYM_ENUM) &&
+ (defn = sym->defn) && defn->tag == SYM_NORMAL &&
+ strcmp(defn->string, "}") == 0 &&
+ (defn = defn->next) && defn->tag == SYM_NORMAL &&
+ strcmp(defn->string, "UNKNOWN") == 0 &&
+ (defn = defn->next) && defn->tag == SYM_NORMAL &&
+ strcmp(defn->string, "{") == 0);
+}
+
+struct symbol *__add_symbol(const char *name, enum symbol_type type,
+ struct string_list *defn, int is_extern,
+ int is_reference)
{
unsigned long h = crc32(name) % HASH_BUCKETS;
struct symbol *sym;
+ enum symbol_status status = STATUS_UNCHANGED;
for (sym = symtab[h]; sym; sym = sym->hash_next) {
- if (map_to_ns(sym->type) == map_to_ns(type)
- && strcmp(name, sym->name) == 0) {
- if (!equal_list(sym->defn, defn))
+ if (map_to_ns(sym->type) == map_to_ns(type) &&
+ strcmp(name, sym->name) == 0) {
+ if (is_reference)
+ /* fall through */ ;
+ else if (sym->type == type &&
+ equal_list(sym->defn, defn)) {
+ if (!sym->is_declared && sym->is_override) {
+ print_location();
+ print_type_name(type, name);
+ fprintf(stderr, " modversion is "
+ "unchanged\n");
+ }
+ sym->is_declared = 1;
+ return sym;
+ } else if (!sym->is_declared) {
+ if (sym->is_override && flag_preserve) {
+ print_location();
+ fprintf(stderr, "ignoring ");
+ print_type_name(type, name);
+ fprintf(stderr, " modversion change\n");
+ sym->is_declared = 1;
+ return sym;
+ } else {
+ status = is_unknown_symbol(sym) ?
+ STATUS_DEFINED : STATUS_MODIFIED;
+ }
+ } else {
error_with_pos("redefinition of %s", name);
- return sym;
+ return sym;
+ }
+ break;
}
}
+ if (sym) {
+ struct symbol **psym;
+
+ for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
+ if (*psym == sym) {
+ *psym = sym->hash_next;
+ break;
+ }
+ }
+ --nsyms;
+ }
+
sym = xmalloc(sizeof(*sym));
sym->name = name;
sym->type = type;
@@ -183,6 +242,10 @@ struct symbol *add_symbol(const char *name, enum symbol_type type,
sym->hash_next = symtab[h];
symtab[h] = sym;
+ sym->is_declared = !is_reference;
+ sym->status = status;
+ sym->is_override = 0;
+
if (flag_debug) {
fprintf(debugfile, "Defn for %s %s == <",
symbol_type_name[type], name);
@@ -196,6 +259,18 @@ struct symbol *add_symbol(const char *name, enum symbol_type type,
return sym;
}
+struct symbol *add_symbol(const char *name, enum symbol_type type,
+ struct string_list *defn, int is_extern)
+{
+ return __add_symbol(name, type, defn, is_extern, 0);
+}
+
+struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
+ struct string_list *defn, int is_extern)
+{
+ return __add_symbol(name, type, defn, is_extern, 1);
+}
+
/*----------------------------------------------------------------------*/
void free_node(struct string_list *node)
@@ -236,6 +311,90 @@ static int equal_list(struct string_list *a, struct string_list *b)
return !a && !b;
}
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct string_list *read_node(FILE *f)
+{
+ char buffer[256];
+ struct string_list node = {
+ .string = buffer,
+ .tag = SYM_NORMAL };
+ int c;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (c == ' ') {
+ if (node.string == buffer)
+ continue;
+ break;
+ } else if (c == '\n') {
+ if (node.string == buffer)
+ return NULL;
+ ungetc(c, f);
+ break;
+ }
+ if (node.string >= buffer + sizeof(buffer) - 1) {
+ fprintf(stderr, "Token too long\n");
+ exit(1);
+ }
+ *node.string++ = c;
+ }
+ if (node.string == buffer)
+ return NULL;
+ *node.string = 0;
+ node.string = buffer;
+
+ if (node.string[1] == '#') {
+ int n;
+
+ for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) {
+ if (node.string[0] == symbol_type_name[n][0]) {
+ node.tag = n;
+ node.string += 2;
+ return copy_node(&node);
+ }
+ }
+ fprintf(stderr, "Unknown type %c\n", node.string[0]);
+ exit(1);
+ }
+ return copy_node(&node);
+}
+
+static void read_reference(FILE *f)
+{
+ while (!feof(f)) {
+ struct string_list *defn = NULL;
+ struct string_list *sym, *def;
+ int is_extern = 0, is_override = 0;
+ struct symbol *subsym;
+
+ sym = read_node(f);
+ if (sym && sym->tag == SYM_NORMAL &&
+ !strcmp(sym->string, "override")) {
+ is_override = 1;
+ free_node(sym);
+ sym = read_node(f);
+ }
+ if (!sym)
+ continue;
+ def = read_node(f);
+ if (def && def->tag == SYM_NORMAL &&
+ !strcmp(def->string, "extern")) {
+ is_extern = 1;
+ free_node(def);
+ def = read_node(f);
+ }
+ while (def) {
+ def->next = defn;
+ defn = def;
+ def = read_node(f);
+ }
+ subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
+ defn, is_extern);
+ subsym->is_override = is_override;
+ free_node(sym);
+ }
+}
+
static void print_node(FILE * f, struct string_list *list)
{
if (list->tag != SYM_NORMAL) {
@@ -311,6 +470,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
case SYM_TYPEDEF:
subsym = find_symbol(cur->string, cur->tag);
+ /* FIXME: Bad reference files can segfault here. */
if (subsym->expansion_trail) {
if (flag_dump_defs)
fprintf(debugfile, "%s ", cur->string);
@@ -347,9 +507,22 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
t = n;
n = xmalloc(sizeof(*n));
- n->string = xstrdup("{ UNKNOWN }");
+ n->string = xstrdup("{");
+ n->tag = SYM_NORMAL;
+ n->next = t;
+ t = n;
+
+ n = xmalloc(sizeof(*n));
+ n->string = xstrdup("UNKNOWN");
+ n->tag = SYM_NORMAL;
+ n->next = t;
+ t = n;
+
+ n = xmalloc(sizeof(*n));
+ n->string = xstrdup("}");
n->tag = SYM_NORMAL;
n->next = t;
+ t = n;
subsym =
add_symbol(cur->string, cur->tag, n, 0);
@@ -397,37 +570,75 @@ void export_symbol(const char *name)
error_with_pos("export undefined symbol %s", name);
else {
unsigned long crc;
+ int has_changed = 0;
if (flag_dump_defs)
fprintf(debugfile, "Export %s == <", name);
expansion_trail = (struct symbol *)-1L;
+ sym->expansion_trail = expansion_trail;
+ expansion_trail = sym;
crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
sym = expansion_trail;
while (sym != (struct symbol *)-1L) {
struct symbol *n = sym->expansion_trail;
+
+ if (sym->status != STATUS_UNCHANGED) {
+ if (!has_changed) {
+ print_location();
+ fprintf(stderr, "%s: %s: modversion "
+ "changed because of changes "
+ "in ", flag_preserve ? "error" :
+ "warning", name);
+ } else
+ fprintf(stderr, ", ");
+ print_type_name(sym->type, sym->name);
+ if (sym->status == STATUS_DEFINED)
+ fprintf(stderr, " (became defined)");
+ has_changed = 1;
+ if (flag_preserve)
+ errors++;
+ }
sym->expansion_trail = 0;
sym = n;
}
+ if (has_changed)
+ fprintf(stderr, "\n");
if (flag_dump_defs)
fputs(">\n", debugfile);
- /* Used as a linker script. */
- printf("%s__crc_%s = 0x%08lx ;\n", mod_prefix, name, crc);
+ /* Used as assembly source or a linker script. */
+ printf(flag_asm
+ ? ".equiv %s__crc_%s, %#08lx\n"
+ : "%s__crc_%s = %#08lx ;\n",
+ mod_prefix, name, crc);
}
}
/*----------------------------------------------------------------------*/
+
+static void print_location(void)
+{
+ fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
+}
+
+static void print_type_name(enum symbol_type type, const char *name)
+{
+ if (type != SYM_NORMAL)
+ fprintf(stderr, "%s %s", symbol_type_name[type], name);
+ else
+ fprintf(stderr, "%s", name);
+}
+
void error_with_pos(const char *fmt, ...)
{
va_list args;
if (flag_warnings) {
- fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>",
- cur_line);
+ print_location();
va_start(args, fmt);
vfprintf(stderr, fmt, args);
@@ -440,21 +651,27 @@ void error_with_pos(const char *fmt, ...)
static void genksyms_usage(void)
{
- fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
+ fputs("Usage:\n" "genksyms [-aAdDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
#ifdef __GNU_LIBRARY__
" -a, --arch Select architecture\n"
+ " -A, --asm Generate assembly rather than linker script\n"
" -d, --debug Increment the debug level (repeatable)\n"
" -D, --dump Dump expanded symbol defs (for debugging only)\n"
- " -T, --dump-types file Dump expanded types into file (for debugging only)\n"
+ " -r, --reference file Read reference symbols from a file\n"
+ " -T, --dump-types file Dump expanded types into file\n"
+ " -p, --preserve Preserve reference modversions or fail\n"
" -w, --warnings Enable warnings\n"
" -q, --quiet Disable warnings (default)\n"
" -h, --help Print this message\n"
" -V, --version Print the release version\n"
#else /* __GNU_LIBRARY__ */
" -a Select architecture\n"
+ " -A Generate assembly rather than linker script\n"
" -d Increment the debug level (repeatable)\n"
" -D Dump expanded symbol defs (for debugging only)\n"
- " -T file Dump expanded types into file (for debugging only)\n"
+ " -r file Read reference symbols from a file\n"
+ " -T file Dump expanded types into file\n"
+ " -p Preserve reference modversions or fail\n"
" -w Enable warnings\n"
" -q Disable warnings (default)\n"
" -h Print this message\n"
@@ -465,26 +682,29 @@ static void genksyms_usage(void)
int main(int argc, char **argv)
{
- FILE *dumpfile = NULL;
+ FILE *dumpfile = NULL, *ref_file = NULL;
int o;
#ifdef __GNU_LIBRARY__
struct option long_opts[] = {
{"arch", 1, 0, 'a'},
+ {"asm", 0, 0, 'A'},
{"debug", 0, 0, 'd'},
{"warnings", 0, 0, 'w'},
{"quiet", 0, 0, 'q'},
{"dump", 0, 0, 'D'},
+ {"reference", 1, 0, 'r'},
{"dump-types", 1, 0, 'T'},
+ {"preserve", 0, 0, 'p'},
{"version", 0, 0, 'V'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
- while ((o = getopt_long(argc, argv, "a:dwqVDT:h",
+ while ((o = getopt_long(argc, argv, "a:dwqVADr:T:ph",
&long_opts[0], NULL)) != EOF)
#else /* __GNU_LIBRARY__ */
- while ((o = getopt(argc, argv, "a:dwqVDT:h")) != EOF)
+ while ((o = getopt(argc, argv, "a:dwqVADr:T:ph")) != EOF)
#endif /* __GNU_LIBRARY__ */
switch (o) {
case 'a':
@@ -502,9 +722,20 @@ int main(int argc, char **argv)
case 'V':
fputs("genksyms version 2.5.60\n", stderr);
break;
+ case 'A':
+ flag_asm = 1;
+ break;
case 'D':
flag_dump_defs = 1;
break;
+ case 'r':
+ flag_reference = 1;
+ ref_file = fopen(optarg, "r");
+ if (!ref_file) {
+ perror(optarg);
+ return 1;
+ }
+ break;
case 'T':
flag_dump_types = 1;
dumpfile = fopen(optarg, "w");
@@ -513,6 +744,9 @@ int main(int argc, char **argv)
return 1;
}
break;
+ case 'p':
+ flag_preserve = 1;
+ break;
case 'h':
genksyms_usage();
return 0;
@@ -533,12 +767,17 @@ int main(int argc, char **argv)
/* setlinebuf(debugfile); */
}
+ if (flag_reference)
+ read_reference(ref_file);
+
yyparse();
if (flag_dump_types && visited_symbols) {
while (visited_symbols != (struct symbol *)-1L) {
struct symbol *sym = visited_symbols;
+ if (sym->is_override)
+ fputs("override ", dumpfile);
if (sym->type != SYM_NORMAL) {
putc(symbol_type_name[sym->type][0], dumpfile);
putc('#', dumpfile);
diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h
index 2668287aa498..25c4d40cefc1 100644
--- a/scripts/genksyms/genksyms.h
+++ b/scripts/genksyms/genksyms.h
@@ -29,6 +29,10 @@ enum symbol_type {
SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION
};
+enum symbol_status {
+ STATUS_UNCHANGED, STATUS_DEFINED, STATUS_MODIFIED
+};
+
struct string_list {
struct string_list *next;
enum symbol_type tag;
@@ -43,6 +47,9 @@ struct symbol {
struct symbol *expansion_trail;
struct symbol *visited;
int is_extern;
+ int is_declared;
+ enum symbol_status status;
+ int is_override;
};
typedef struct string_list **yystype;
diff --git a/scripts/genksyms/keywords.c_shipped b/scripts/genksyms/keywords.c_shipped
index 971e0113ae7a..83484fe93ede 100644
--- a/scripts/genksyms/keywords.c_shipped
+++ b/scripts/genksyms/keywords.c_shipped
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.2 */
+/* ANSI-C code produced by gperf version 3.0.1 */
/* Command-line: gperf -L ANSI-C -a -C -E -g -H is_reserved_hash -k '1,3,$' -N is_reserved_word -p -t scripts/genksyms/keywords.gperf */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -32,7 +32,7 @@
#line 3 "scripts/genksyms/keywords.gperf"
struct resword { const char *name; int token; };
-/* maximum key range = 62, duplicates = 0 */
+/* maximum key range = 64, duplicates = 0 */
#ifdef __GNUC__
__inline
@@ -46,32 +46,32 @@ is_reserved_hash (register const char *str, register unsigned int len)
{
static const unsigned char asso_values[] =
{
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 5,
- 65, 65, 65, 65, 65, 65, 35, 65, 65, 65,
- 0, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 0, 65, 0, 65, 5,
- 20, 15, 10, 30, 65, 15, 65, 65, 20, 0,
- 10, 35, 20, 65, 10, 5, 0, 10, 5, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
- 65, 65, 65, 65, 65, 65
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 0,
+ 67, 67, 67, 67, 67, 67, 15, 67, 67, 67,
+ 0, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 0, 67, 0, 67, 5,
+ 25, 20, 15, 30, 67, 15, 67, 67, 10, 0,
+ 10, 40, 20, 67, 10, 5, 0, 10, 15, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67
};
return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[0]] + asso_values[(unsigned char)str[len - 1]];
}
@@ -84,116 +84,119 @@ is_reserved_word (register const char *str, register unsigned int len)
{
enum
{
- TOTAL_KEYWORDS = 43,
+ TOTAL_KEYWORDS = 45,
MIN_WORD_LENGTH = 3,
MAX_WORD_LENGTH = 24,
MIN_HASH_VALUE = 3,
- MAX_HASH_VALUE = 64
+ MAX_HASH_VALUE = 66
};
static const struct resword wordlist[] =
{
{""}, {""}, {""},
-#line 26 "scripts/genksyms/keywords.gperf"
+#line 28 "scripts/genksyms/keywords.gperf"
{"asm", ASM_KEYW},
{""},
-#line 8 "scripts/genksyms/keywords.gperf"
+#line 10 "scripts/genksyms/keywords.gperf"
{"__asm", ASM_KEYW},
{""},
-#line 9 "scripts/genksyms/keywords.gperf"
+#line 11 "scripts/genksyms/keywords.gperf"
{"__asm__", ASM_KEYW},
{""}, {""},
-#line 52 "scripts/genksyms/keywords.gperf"
+#line 54 "scripts/genksyms/keywords.gperf"
{"__typeof__", TYPEOF_KEYW},
{""},
-#line 12 "scripts/genksyms/keywords.gperf"
+#line 14 "scripts/genksyms/keywords.gperf"
{"__const", CONST_KEYW},
-#line 11 "scripts/genksyms/keywords.gperf"
- {"__attribute__", ATTRIBUTE_KEYW},
#line 13 "scripts/genksyms/keywords.gperf"
+ {"__attribute__", ATTRIBUTE_KEYW},
+#line 15 "scripts/genksyms/keywords.gperf"
{"__const__", CONST_KEYW},
-#line 18 "scripts/genksyms/keywords.gperf"
+#line 20 "scripts/genksyms/keywords.gperf"
{"__signed__", SIGNED_KEYW},
-#line 44 "scripts/genksyms/keywords.gperf"
+#line 46 "scripts/genksyms/keywords.gperf"
{"static", STATIC_KEYW},
-#line 20 "scripts/genksyms/keywords.gperf"
- {"__volatile__", VOLATILE_KEYW},
-#line 39 "scripts/genksyms/keywords.gperf"
+ {""},
+#line 41 "scripts/genksyms/keywords.gperf"
{"int", INT_KEYW},
-#line 32 "scripts/genksyms/keywords.gperf"
+#line 34 "scripts/genksyms/keywords.gperf"
{"char", CHAR_KEYW},
-#line 33 "scripts/genksyms/keywords.gperf"
+#line 35 "scripts/genksyms/keywords.gperf"
{"const", CONST_KEYW},
-#line 45 "scripts/genksyms/keywords.gperf"
+#line 47 "scripts/genksyms/keywords.gperf"
{"struct", STRUCT_KEYW},
-#line 24 "scripts/genksyms/keywords.gperf"
+#line 26 "scripts/genksyms/keywords.gperf"
{"__restrict__", RESTRICT_KEYW},
-#line 25 "scripts/genksyms/keywords.gperf"
+#line 27 "scripts/genksyms/keywords.gperf"
{"restrict", RESTRICT_KEYW},
-#line 23 "scripts/genksyms/keywords.gperf"
- {"_restrict", RESTRICT_KEYW},
-#line 16 "scripts/genksyms/keywords.gperf"
+#line 7 "scripts/genksyms/keywords.gperf"
+ {"EXPORT_SYMBOL_GPL_FUTURE", EXPORT_SYMBOL_KEYW},
+#line 18 "scripts/genksyms/keywords.gperf"
{"__inline__", INLINE_KEYW},
-#line 10 "scripts/genksyms/keywords.gperf"
- {"__attribute", ATTRIBUTE_KEYW},
{""},
-#line 14 "scripts/genksyms/keywords.gperf"
+#line 22 "scripts/genksyms/keywords.gperf"
+ {"__volatile__", VOLATILE_KEYW},
+#line 5 "scripts/genksyms/keywords.gperf"
+ {"EXPORT_SYMBOL", EXPORT_SYMBOL_KEYW},
+#line 25 "scripts/genksyms/keywords.gperf"
+ {"_restrict", RESTRICT_KEYW},
+ {""},
+#line 12 "scripts/genksyms/keywords.gperf"
+ {"__attribute", ATTRIBUTE_KEYW},
+#line 6 "scripts/genksyms/keywords.gperf"
+ {"EXPORT_SYMBOL_GPL", EXPORT_SYMBOL_KEYW},
+#line 16 "scripts/genksyms/keywords.gperf"
{"__extension__", EXTENSION_KEYW},
-#line 35 "scripts/genksyms/keywords.gperf"
+#line 37 "scripts/genksyms/keywords.gperf"
{"enum", ENUM_KEYW},
-#line 19 "scripts/genksyms/keywords.gperf"
- {"__volatile", VOLATILE_KEYW},
-#line 36 "scripts/genksyms/keywords.gperf"
+#line 8 "scripts/genksyms/keywords.gperf"
+ {"EXPORT_UNUSED_SYMBOL", EXPORT_SYMBOL_KEYW},
+#line 38 "scripts/genksyms/keywords.gperf"
{"extern", EXTERN_KEYW},
{""},
-#line 17 "scripts/genksyms/keywords.gperf"
+#line 19 "scripts/genksyms/keywords.gperf"
{"__signed", SIGNED_KEYW},
-#line 7 "scripts/genksyms/keywords.gperf"
- {"EXPORT_SYMBOL_GPL_FUTURE", EXPORT_SYMBOL_KEYW},
- {""},
-#line 51 "scripts/genksyms/keywords.gperf"
+#line 9 "scripts/genksyms/keywords.gperf"
+ {"EXPORT_UNUSED_SYMBOL_GPL", EXPORT_SYMBOL_KEYW},
+#line 49 "scripts/genksyms/keywords.gperf"
+ {"union", UNION_KEYW},
+#line 53 "scripts/genksyms/keywords.gperf"
{"typeof", TYPEOF_KEYW},
-#line 46 "scripts/genksyms/keywords.gperf"
+#line 48 "scripts/genksyms/keywords.gperf"
{"typedef", TYPEDEF_KEYW},
-#line 15 "scripts/genksyms/keywords.gperf"
+#line 17 "scripts/genksyms/keywords.gperf"
{"__inline", INLINE_KEYW},
-#line 31 "scripts/genksyms/keywords.gperf"
+#line 33 "scripts/genksyms/keywords.gperf"
{"auto", AUTO_KEYW},
-#line 47 "scripts/genksyms/keywords.gperf"
- {"union", UNION_KEYW},
- {""}, {""},
-#line 48 "scripts/genksyms/keywords.gperf"
- {"unsigned", UNSIGNED_KEYW},
-#line 49 "scripts/genksyms/keywords.gperf"
- {"void", VOID_KEYW},
-#line 42 "scripts/genksyms/keywords.gperf"
- {"short", SHORT_KEYW},
+#line 21 "scripts/genksyms/keywords.gperf"
+ {"__volatile", VOLATILE_KEYW},
{""}, {""},
#line 50 "scripts/genksyms/keywords.gperf"
- {"volatile", VOLATILE_KEYW},
- {""},
-#line 37 "scripts/genksyms/keywords.gperf"
- {"float", FLOAT_KEYW},
-#line 34 "scripts/genksyms/keywords.gperf"
- {"double", DOUBLE_KEYW},
+ {"unsigned", UNSIGNED_KEYW},
{""},
-#line 5 "scripts/genksyms/keywords.gperf"
- {"EXPORT_SYMBOL", EXPORT_SYMBOL_KEYW},
- {""}, {""},
-#line 38 "scripts/genksyms/keywords.gperf"
+#line 44 "scripts/genksyms/keywords.gperf"
+ {"short", SHORT_KEYW},
+#line 40 "scripts/genksyms/keywords.gperf"
{"inline", INLINE_KEYW},
-#line 6 "scripts/genksyms/keywords.gperf"
- {"EXPORT_SYMBOL_GPL", EXPORT_SYMBOL_KEYW},
-#line 41 "scripts/genksyms/keywords.gperf"
- {"register", REGISTER_KEYW},
{""},
-#line 22 "scripts/genksyms/keywords.gperf"
+#line 52 "scripts/genksyms/keywords.gperf"
+ {"volatile", VOLATILE_KEYW},
+#line 42 "scripts/genksyms/keywords.gperf"
+ {"long", LONG_KEYW},
+#line 24 "scripts/genksyms/keywords.gperf"
{"_Bool", BOOL_KEYW},
-#line 43 "scripts/genksyms/keywords.gperf"
- {"signed", SIGNED_KEYW},
{""}, {""},
-#line 40 "scripts/genksyms/keywords.gperf"
- {"long", LONG_KEYW}
+#line 43 "scripts/genksyms/keywords.gperf"
+ {"register", REGISTER_KEYW},
+#line 51 "scripts/genksyms/keywords.gperf"
+ {"void", VOID_KEYW},
+#line 39 "scripts/genksyms/keywords.gperf"
+ {"float", FLOAT_KEYW},
+#line 36 "scripts/genksyms/keywords.gperf"
+ {"double", DOUBLE_KEYW},
+ {""}, {""}, {""}, {""},
+#line 45 "scripts/genksyms/keywords.gperf"
+ {"signed", SIGNED_KEYW}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
diff --git a/scripts/genksyms/keywords.gperf b/scripts/genksyms/keywords.gperf
index 5ef3733225fb..8abe7ab8d88f 100644
--- a/scripts/genksyms/keywords.gperf
+++ b/scripts/genksyms/keywords.gperf
@@ -5,6 +5,8 @@ struct resword { const char *name; int token; }
EXPORT_SYMBOL, EXPORT_SYMBOL_KEYW
EXPORT_SYMBOL_GPL, EXPORT_SYMBOL_KEYW
EXPORT_SYMBOL_GPL_FUTURE, EXPORT_SYMBOL_KEYW
+EXPORT_UNUSED_SYMBOL, EXPORT_SYMBOL_KEYW
+EXPORT_UNUSED_SYMBOL_GPL, EXPORT_SYMBOL_KEYW
__asm, ASM_KEYW
__asm__, ASM_KEYW
__attribute, ATTRIBUTE_KEYW
diff --git a/scripts/headerdep.pl b/scripts/headerdep.pl
new file mode 100755
index 000000000000..97399da89ef2
--- /dev/null
+++ b/scripts/headerdep.pl
@@ -0,0 +1,193 @@
+#! /usr/bin/perl
+#
+# Detect cycles in the header file dependency graph
+# Vegard Nossum <vegardno@ifi.uio.no>
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+
+my $opt_all;
+my @opt_include;
+my $opt_graph;
+
+&Getopt::Long::Configure(qw(bundling pass_through));
+&GetOptions(
+ help => \&help,
+ version => \&version,
+
+ all => \$opt_all,
+ I => \@opt_include,
+ graph => \$opt_graph,
+);
+
+push @opt_include, 'include';
+my %deps = ();
+my %linenos = ();
+
+my @headers = grep { strip($_) } @ARGV;
+
+parse_all(@headers);
+
+if($opt_graph) {
+ graph();
+} else {
+ detect_cycles(@headers);
+}
+
+
+sub help {
+ print "Usage: $0 [options] file...\n";
+ print "\n";
+ print "Options:\n";
+ print " --all\n";
+ print " --graph\n";
+ print "\n";
+ print " -I includedir\n";
+ print "\n";
+ print "To make nice graphs, try:\n";
+ print " $0 --graph include/linux/kernel.h | dot -Tpng -o graph.png\n";
+ exit;
+}
+
+sub version {
+ print "headerdep version 2\n";
+ exit;
+}
+
+# Get a file name that is relative to our include paths
+sub strip {
+ my $filename = shift;
+
+ for my $i (@opt_include) {
+ my $stripped = $filename;
+ $stripped =~ s/^$i\///;
+
+ return $stripped if $stripped ne $filename;
+ }
+
+ return $filename;
+}
+
+# Search for the file name in the list of include paths
+sub search {
+ my $filename = shift;
+ return $filename if -f $filename;
+
+ for my $i (@opt_include) {
+ my $path = "$i/$filename";
+ return $path if -f $path;
+ }
+
+ return undef;
+}
+
+sub parse_all {
+ # Parse all the headers.
+ my @queue = @_;
+ while(@queue) {
+ my $header = pop @queue;
+ next if exists $deps{$header};
+
+ $deps{$header} = [] unless exists $deps{$header};
+
+ my $path = search($header);
+ next unless $path;
+
+ open(my $file, '<', $path) or die($!);
+ chomp(my @lines = <$file>);
+ close($file);
+
+ for my $i (0 .. $#lines) {
+ my $line = $lines[$i];
+ if(my($dep) = ($line =~ m/^#\s*include\s*<(.*?)>/)) {
+ push @queue, $dep;
+ push @{$deps{$header}}, [$i + 1, $dep];
+ }
+ }
+ }
+}
+
+sub print_cycle {
+ # $cycle[n] includes $cycle[n + 1];
+ # $cycle[-1] will be the culprit
+ my $cycle = shift;
+
+ # Adjust the line numbers
+ for my $i (0 .. $#$cycle - 1) {
+ $cycle->[$i]->[0] = $cycle->[$i + 1]->[0];
+ }
+ $cycle->[-1]->[0] = 0;
+
+ my $first = shift @$cycle;
+ my $last = pop @$cycle;
+
+ my $msg = "In file included";
+ printf "%s from %s,\n", $msg, $last->[1] if defined $last;
+
+ for my $header (reverse @$cycle) {
+ printf "%s from %s:%d%s\n",
+ " " x length $msg,
+ $header->[1], $header->[0],
+ $header->[1] eq $last->[1] ? ' <-- here' : '';
+ }
+
+ printf "%s:%d: warning: recursive header inclusion\n",
+ $first->[1], $first->[0];
+}
+
+# Find and print the smallest cycle starting in the specified node.
+sub detect_cycles {
+ my @queue = map { [[0, $_]] } @_;
+ while(@queue) {
+ my $top = pop @queue;
+ my $name = $top->[-1]->[1];
+
+ for my $dep (@{$deps{$name}}) {
+ my $chain = [@$top, [$dep->[0], $dep->[1]]];
+
+ # If the dep already exists in the chain, we have a
+ # cycle...
+ if(grep { $_->[1] eq $dep->[1] } @$top) {
+ print_cycle($chain);
+ next if $opt_all;
+ return;
+ }
+
+ push @queue, $chain;
+ }
+ }
+}
+
+sub mangle {
+ $_ = shift;
+ s/\//__/g;
+ s/\./_/g;
+ s/-/_/g;
+ $_;
+}
+
+# Output dependency graph in GraphViz language.
+sub graph {
+ print "digraph {\n";
+
+ print "\t/* vertices */\n";
+ for my $header (keys %deps) {
+ printf "\t%s [label=\"%s\"];\n",
+ mangle($header), $header;
+ }
+
+ print "\n";
+
+ print "\t/* edges */\n";
+ for my $header (keys %deps) {
+ for my $dep (@{$deps{$header}}) {
+ printf "\t%s -> %s;\n",
+ mangle($header), mangle($dep->[1]);
+ }
+ }
+
+ print "}\n";
+}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index ad2434b26970..92758120a767 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -130,18 +130,9 @@ static int read_symbol(FILE *in, struct sym_entry *s)
static int symbol_valid(struct sym_entry *s)
{
/* Symbols which vary between passes. Passes 1 and 2 must have
- * identical symbol lists. The kallsyms_* symbols below are only added
- * after pass 1, they would be included in pass 2 when --all-symbols is
- * specified so exclude them to get a stable symbol list.
+ * identical symbol lists.
*/
static char *special_symbols[] = {
- "kallsyms_addresses",
- "kallsyms_num_syms",
- "kallsyms_names",
- "kallsyms_markers",
- "kallsyms_token_table",
- "kallsyms_token_index",
-
/* Exclude linker generated symbols which vary between passes */
"_SDA_BASE_", /* ppc */
"_SDA2_BASE_", /* ppc */
@@ -173,7 +164,9 @@ static int symbol_valid(struct sym_entry *s)
}
/* Exclude symbols which vary between passes. */
- if (strstr((char *)s->sym + offset, "_compiled."))
+ if (strstr((char *)s->sym + offset, "_compiled.") ||
+ strncmp((char*)s->sym + offset, "__compound_literal.", 19) == 0 ||
+ strncmp((char*)s->sym + offset, "__compound_literal$", 19) == 0)
return 0;
for (i = 0; special_symbols[i]; i++)
@@ -550,8 +543,10 @@ int main(int argc, char **argv)
usage();
read_map(stdin);
- sort_symbols();
- optimize_token_table();
+ if (table_cnt) {
+ sort_symbols();
+ optimize_token_table();
+ }
write_src();
return 0;
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index 5552154cbedb..fcef0f59d553 100644
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -52,7 +52,7 @@ EOF
}
usage() {
- printf "Usage: $0 [-check compiler options|-header|-library]\n"
+ printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n"
}
if [ $# -eq 0 ]; then
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index d27aad78e1d8..8bb83a100edb 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -289,6 +289,8 @@ my %parameterdescs;
my @parameterlist;
my %sections;
my @sectionlist;
+my $sectcheck;
+my $struct_actual;
my $contents = "";
my $section_default = "Description"; # default section
@@ -378,10 +380,12 @@ sub dump_section {
# print STDERR "parameter def '$1' = '$contents'\n";
$name = $1;
$parameterdescs{$name} = $contents;
+ $sectcheck = $sectcheck . $name . " ";
} elsif ($name eq "@\.\.\.") {
# print STDERR "parameter def '...' = '$contents'\n";
$name = "...";
$parameterdescs{$name} = $contents;
+ $sectcheck = $sectcheck . $name . " ";
} else {
# print STDERR "other section '$name' = '$contents'\n";
if (defined($sections{$name}) && ($sections{$name} ne "")) {
@@ -1405,21 +1409,25 @@ sub dump_union($$) {
sub dump_struct($$) {
my $x = shift;
my $file = shift;
+ my $nested;
if ($x =~/(struct|union)\s+(\w+)\s*{(.*)}/) {
$declaration_name = $2;
my $members = $3;
# ignore embedded structs or unions
- $members =~ s/{.*}//g;
+ $members =~ s/({.*})//g;
+ $nested = $1;
# ignore members marked private:
$members =~ s/\/\*.*?private:.*?public:.*?\*\///gos;
$members =~ s/\/\*.*?private:.*//gos;
# strip comments:
$members =~ s/\/\*.*?\*\///gos;
+ $nested =~ s/\/\*.*?\*\///gos;
create_parameterlist($members, ';', $file);
+ check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested);
output_declaration($declaration_name,
'struct',
@@ -1505,6 +1513,14 @@ sub dump_typedef($$) {
}
}
+sub save_struct_actual($) {
+ my $actual = shift;
+
+ # strip all spaces from the actual param so that it looks like one string item
+ $actual =~ s/\s*//g;
+ $struct_actual = $struct_actual . $actual . " ";
+}
+
sub create_parameterlist($$$) {
my $args = shift;
my $splitter = shift;
@@ -1537,6 +1553,7 @@ sub create_parameterlist($$$) {
$param = $1;
$type = $arg;
$type =~ s/([^\(]+\(\*?)\s*$param/$1/;
+ save_struct_actual($param);
push_parameter($param, $type, $file);
} elsif ($arg) {
$arg =~ s/\s*:\s*/:/g;
@@ -1561,14 +1578,17 @@ sub create_parameterlist($$$) {
foreach $param (@args) {
if ($param =~ m/^(\*+)\s*(.*)/) {
+ save_struct_actual($2);
push_parameter($2, "$type $1", $file);
}
elsif ($param =~ m/(.*?):(\d+)/) {
if ($type ne "") { # skip unnamed bit-fields
+ save_struct_actual($1);
push_parameter($1, "$type:$2", $file)
}
}
else {
+ save_struct_actual($param);
push_parameter($param, $type, $file);
}
}
@@ -1634,6 +1654,46 @@ sub push_parameter($$$) {
$parametertypes{$param} = $type;
}
+sub check_sections($$$$$$) {
+ my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck, $nested) = @_;
+ my @sects = split ' ', $sectcheck;
+ my @prms = split ' ', $prmscheck;
+ my $err;
+ my ($px, $sx);
+ my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
+
+ foreach $sx (0 .. $#sects) {
+ $err = 1;
+ foreach $px (0 .. $#prms) {
+ $prm_clean = $prms[$px];
+ $prm_clean =~ s/\[.*\]//;
+ $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//;
+ ##$prm_clean =~ s/^\**//;
+ if ($prm_clean eq $sects[$sx]) {
+ $err = 0;
+ last;
+ }
+ }
+ if ($err) {
+ if ($decl_type eq "function") {
+ print STDERR "Warning(${file}:$.): " .
+ "Excess function parameter " .
+ "'$sects[$sx]' " .
+ "description in '$decl_name'\n";
+ ++$warnings;
+ } else {
+ if ($nested !~ m/\Q$sects[$sx]\E/) {
+ print STDERR "Warning(${file}:$.): " .
+ "Excess struct/union/enum/typedef member " .
+ "'$sects[$sx]' " .
+ "description in '$decl_name'\n";
+ ++$warnings;
+ }
+ }
+ }
+ }
+}
+
##
# takes a function prototype and the name of the current file being
# processed and spits out all the details stored in the global
@@ -1699,6 +1759,9 @@ sub dump_function($$) {
return;
}
+ my $prms = join " ", @parameterlist;
+ check_sections($file, $declaration_name, "function", $sectcheck, $prms, "");
+
output_declaration($declaration_name,
'function',
{'function' => $declaration_name,
@@ -1757,6 +1820,8 @@ sub reset_state {
@parameterlist = ();
%sections = ();
@sectionlist = ();
+ $sectcheck = "";
+ $struct_actual = "";
$prototype = "";
$state = 0;
diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h
index a8740df07b09..6a12dd9f1181 100755
--- a/scripts/mkcompile_h
+++ b/scripts/mkcompile_h
@@ -4,6 +4,8 @@ SMP=$3
PREEMPT=$4
CC=$5
+vecho() { [ "${quiet}" = "silent_" ] || echo "$@" ; }
+
# If compile.h exists already and we don't own autoconf.h
# (i.e. we're not the same user who did make *config), don't
# modify compile.h
@@ -11,7 +13,7 @@ CC=$5
# do "compiled by root"
if [ -r $TARGET -a ! -O include/linux/autoconf.h ]; then
- echo " SKIPPED $TARGET"
+ vecho " SKIPPED $TARGET"
exit 0
fi
@@ -89,7 +91,7 @@ if [ -r $TARGET ] && \
cmp -s .tmpver.1 .tmpver.2; then
rm -f .tmpcompile
else
- echo " UPD $TARGET"
+ vecho " UPD $TARGET"
mv -f .tmpcompile $TARGET
fi
rm -f .tmpver.1 .tmpver.2
diff --git a/scripts/mkmakefile b/scripts/mkmakefile
index e65d8b33faa4..67d59c7a18dc 100644
--- a/scripts/mkmakefile
+++ b/scripts/mkmakefile
@@ -17,7 +17,9 @@ if test -e $2/Makefile && ! grep -q Automatically $2/Makefile
then
exit 0
fi
-echo " GEN $2/Makefile"
+if [ "${quiet}" != "silent_" ]; then
+ echo " GEN $2/Makefile"
+fi
cat << EOF > $2/Makefile
# Automatically generated by $0: don't edit
diff --git a/scripts/mksysmap b/scripts/mksysmap
index 6e133a0bae7a..1db316a3712b 100644
--- a/scripts/mksysmap
+++ b/scripts/mksysmap
@@ -37,9 +37,6 @@
# readprofile starts reading symbols when _stext is found, and
# continue until it finds a symbol which is not either of 'T', 't',
-# 'W' or 'w'. __crc_ are 'A' and placed in the middle
-# so we just ignore them to let readprofile continue to work.
-# (At least sparc64 has __crc_ in the middle).
-
-$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2
+# 'W' or 'w'.
+$NM -n $1 | grep -v '\( [aNUw] \)\|\( \$[adt]\)' > $2
diff --git a/scripts/package/Makefile b/scripts/package/Makefile
index 5e326078a4a2..8c6b7b09606a 100644
--- a/scripts/package/Makefile
+++ b/scripts/package/Makefile
@@ -1,10 +1,6 @@
# Makefile for the different targets used to generate full packages of a kernel
# It uses the generic clean infrastructure of kbuild
-# Ignore the following files/directories during tar operation
-TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS
-
-
# RPM target
# ---------------------------------------------------------------------------
# The rpm target generates two rpm files:
@@ -47,7 +43,7 @@ rpm-pkg rpm: $(objtree)/kernel.spec FORCE
set -e; \
mv -f $(objtree)/.tmp_version $(objtree)/.version
- $(RPM) --target $(UTS_MACHINE) -ta ../$(KERNELPATH).tar.gz
+ $(RPM) $(RPMOPTS) --target $(UTS_MACHINE) -ta ../$(KERNELPATH).tar.gz
rm ../$(KERNELPATH).tar.gz
clean-files := $(objtree)/kernel.spec
@@ -64,7 +60,8 @@ binrpm-pkg: $(objtree)/binkernel.spec FORCE
set -e; \
mv -f $(objtree)/.tmp_version $(objtree)/.version
- $(RPM) --define "_builddir $(srctree)" --target $(UTS_MACHINE) -bb $<
+ $(RPM) $(RPMOPTS) --define "_builddir $(srctree)" --target \
+ $(UTS_MACHINE) -bb $<
clean-files += $(objtree)/binkernel.spec
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 0b1dc9f9bb06..fe831412bea9 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -114,6 +114,7 @@ my %text_sections = (
".text" => 1,
".sched.text" => 1,
".spinlock.text" => 1,
+ ".irqentry.text" => 1,
);
$objdump = "objdump" if ((length $objdump) == 0);
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 72d233528ade..f6946cf99ce1 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -19,6 +19,11 @@ if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
fi
fi
+ # Is this git on svn?
+ if git config --get svn-remote.svn.url >/dev/null; then
+ printf -- '-svn%s' "`git-svn find-rev $head`"
+ fi
+
# Are there uncommitted changes?
git update-index --refresh --unmerged > /dev/null
if git diff-index --name-only HEAD | grep -v "^scripts/package" \
@@ -51,7 +56,7 @@ if hgid=`hg id 2>/dev/null`; then
fi
# Check for svn and a svn repo.
-if rev=`svn info 2>/dev/null | grep '^Revision'`; then
+if rev=`svn info 2>/dev/null | grep '^Last Changed Rev'`; then
rev=`echo $rev | awk '{print $NF}'`
changes=`svn status 2>/dev/null | grep '^[AMD]' | wc -l`
diff --git a/scripts/strip-symbols b/scripts/strip-symbols
new file mode 100644
index 000000000000..29ee8c1a014b
--- /dev/null
+++ b/scripts/strip-symbols
@@ -0,0 +1,22 @@
+<*>
+*.h
+__compound_literal[$.][0-9]*
+__crc_[a-zA-Z_]*
+__exitcall_[a-zA-Z_]*
+__func__[$.][0-9]*
+__FUNCTION__[$.][0-9]*
+gcc[0-9]_compiled[$.]
+__initcall_[a-zA-Z_]*
+__kcrctab_[a-zA-Z_]*
+__kstrtab_[a-zA-Z_]*
+__ksymtab_[a-zA-Z_]*
+__mod_[a-zA-Z_]*[0-9]
+__module_depends
+__param_[a-zA-Z_]*
+__pci_fixup_*PCI_ANY_IDPCI_ANY_ID*
+__pci_fixup_*PCI_ANY_IDPCI_DEVICE_ID_*
+__pci_fixup_*PCI_VENDOR_ID_*PCI_ANY_ID*
+__pci_fixup_*PCI_VENDOR_ID_*PCI_DEVICE_ID_*
+__PRETTY_FUNCTION__[$.][0-9]*
+__setup_[a-zA-Z_]*
+____versions
diff --git a/scripts/tags.sh b/scripts/tags.sh
new file mode 100755
index 000000000000..4e7547209852
--- /dev/null
+++ b/scripts/tags.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Generate tags or cscope files
+# Usage tags.sh <mode>
+#
+# mode may be any of: tags, TAGS, cscope
+#
+# Uses the following environment variables:
+# ARCH, SUBARCH, srctree, src, obj
+
+if [ "$KBUILD_VERBOSE" = "1" ]; then
+ set -x
+fi
+
+# This is a duplicate of RCS_FIND_IGNORE without escaped '()'
+ignore="( -name SCCS -o -name BitKeeper -o -name .svn -o \
+ -name CVS -o -name .pc -o -name .hg -o \
+ -name .git ) \
+ -prune -o"
+
+# Do not use full path is we do not use O=.. builds
+if [ "${KBUILD_SRC}" = "" ]; then
+ tree=
+else
+ tree=${srctree}/
+fi
+
+# find sources in arch/$ARCH
+find_arch_sources()
+{
+ find ${tree}arch/$1 $ignore -name "$2" -print;
+}
+
+# find sources in arch/$1/include
+find_arch_include_sources()
+{
+ find ${tree}arch/$1/include $ignore -name "$2" -print;
+}
+
+# find sources in include/
+find_include_sources()
+{
+ find ${tree}include $ignore -name config -prune -o -name "$1" -print;
+}
+
+# find sources in rest of tree
+# we could benefit from a list of dirs to search in here
+find_other_sources()
+{
+ find ${tree}* $ignore \
+ \( -name include -o -name arch -o -name '.tmp_*' \) -prune -o \
+ -name "$1" -print;
+}
+
+find_sources()
+{
+ find_arch_sources $1 "$2"
+ find_include_sources "$2"
+ find_other_sources "$2"
+}
+
+all_sources()
+{
+ find_sources $SRCARCH '*.[chS]'
+ if [ ! -z "$archinclude" ]; then
+ find_arch_include_sources $archinclude '*.[chS]'
+ fi
+}
+
+all_kconfigs()
+{
+ find_sources $SRCARCH 'Kconfig*'
+}
+
+all_defconfigs()
+{
+ find_sources $SRCARCH "defconfig"
+}
+
+docscope()
+{
+ (echo \-k; echo \-q; all_sources) > cscope.files
+ cscope -b -f cscope.out
+}
+
+exuberant()
+{
+ all_sources > all
+ all_sources | xargs $1 -a \
+ -I __initdata,__exitdata,__acquires,__releases \
+ -I __read_mostly,____cacheline_aligned \
+ -I ____cacheline_aligned_in_smp \
+ -I ____cacheline_internodealigned_in_smp \
+ -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \
+ --extra=+f --c-kinds=+px \
+ --regex-asm='/^ENTRY\(([^)]*)\).*/\1/'
+
+ all_kconfigs | xargs $1 -a \
+ --langdef=kconfig --language-force=kconfig \
+ --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/'
+
+ all_kconfigs | xargs $1 -a \
+ --langdef=kconfig --language-force=kconfig \
+ --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/CONFIG_\2/'
+
+ all_defconfigs | xargs -r $1 -a \
+ --langdef=dotconfig --language-force=dotconfig \
+ --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'
+
+}
+
+emacs()
+{
+ all_sources | xargs $1 -a
+
+ all_kconfigs | xargs $1 -a \
+ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'
+
+ all_kconfigs | xargs $1 -a \
+ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/CONFIG_\3/'
+
+ all_defconfigs | xargs -r $1 -a \
+ --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'
+}
+
+xtags()
+{
+ if $1 --version 2>&1 | grep -iq exuberant; then
+ exuberant $1
+ elif $1 --version 2>&1 | grep -iq emacs; then
+ emacs $1
+ else
+ all_sources | xargs $1 -a
+ fi
+}
+
+
+# Support um (which uses SUBARCH)
+if [ "${ARCH}" = "um" ]; then
+ if [ "$SUBARCH" = "i386" ]; then
+ archinclude=x86
+ elif [ "$SUBARCH" = "x86_64" ]; then
+ archinclude=x86
+ else
+ archinclude=${SUBARCH}
+ fi
+fi
+
+case "$1" in
+ "cscope")
+ docscope
+ ;;
+
+ "tags")
+ xtags ctags
+ ;;
+
+ "TAGS")
+ xtags etags
+ ;;
+esac
diff --git a/security/capability.c b/security/capability.c
index 245874819036..2dce66fcb992 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -32,24 +32,19 @@ static int cap_quota_on(struct dentry *dentry)
return 0;
}
-static int cap_bprm_alloc_security(struct linux_binprm *bprm)
+static int cap_bprm_check_security (struct linux_binprm *bprm)
{
return 0;
}
-static void cap_bprm_free_security(struct linux_binprm *bprm)
+static void cap_bprm_committing_creds(struct linux_binprm *bprm)
{
}
-static void cap_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void cap_bprm_committed_creds(struct linux_binprm *bprm)
{
}
-static int cap_bprm_check_security(struct linux_binprm *bprm)
-{
- return 0;
-}
-
static int cap_sb_alloc_security(struct super_block *sb)
{
return 0;
@@ -64,7 +59,7 @@ static int cap_sb_copy_data(char *orig, char *copy)
return 0;
}
-static int cap_sb_kern_mount(struct super_block *sb, void *data)
+static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
return 0;
}
@@ -330,7 +325,7 @@ static int cap_file_receive(struct file *file)
return 0;
}
-static int cap_dentry_open(struct file *file)
+static int cap_dentry_open(struct file *file, const struct cred *cred)
{
return 0;
}
@@ -340,15 +335,29 @@ static int cap_task_create(unsigned long clone_flags)
return 0;
}
-static int cap_task_alloc_security(struct task_struct *p)
+static void cap_cred_free(struct cred *cred)
+{
+}
+
+static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
{
return 0;
}
-static void cap_task_free_security(struct task_struct *p)
+static void cap_cred_commit(struct cred *new, const struct cred *old)
{
}
+static int cap_kernel_act_as(struct cred *new, u32 secid)
+{
+ return 0;
+}
+
+static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+ return 0;
+}
+
static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{
return 0;
@@ -750,7 +759,7 @@ static void cap_release_secctx(char *secdata, u32 seclen)
}
#ifdef CONFIG_KEYS
-static int cap_key_alloc(struct key *key, struct task_struct *ctx,
+static int cap_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
return 0;
@@ -760,7 +769,7 @@ static void cap_key_free(struct key *key)
{
}
-static int cap_key_permission(key_ref_t key_ref, struct task_struct *context,
+static int cap_key_permission(key_ref_t key_ref, const struct cred *cred,
key_perm_t perm)
{
return 0;
@@ -814,8 +823,7 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, ptrace_may_access);
set_to_cap_if_null(ops, ptrace_traceme);
set_to_cap_if_null(ops, capget);
- set_to_cap_if_null(ops, capset_check);
- set_to_cap_if_null(ops, capset_set);
+ set_to_cap_if_null(ops, capset);
set_to_cap_if_null(ops, acct);
set_to_cap_if_null(ops, capable);
set_to_cap_if_null(ops, quotactl);
@@ -824,11 +832,9 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, syslog);
set_to_cap_if_null(ops, settime);
set_to_cap_if_null(ops, vm_enough_memory);
- set_to_cap_if_null(ops, bprm_alloc_security);
- set_to_cap_if_null(ops, bprm_free_security);
- set_to_cap_if_null(ops, bprm_apply_creds);
- set_to_cap_if_null(ops, bprm_post_apply_creds);
- set_to_cap_if_null(ops, bprm_set_security);
+ set_to_cap_if_null(ops, bprm_set_creds);
+ set_to_cap_if_null(ops, bprm_committing_creds);
+ set_to_cap_if_null(ops, bprm_committed_creds);
set_to_cap_if_null(ops, bprm_check_security);
set_to_cap_if_null(ops, bprm_secureexec);
set_to_cap_if_null(ops, sb_alloc_security);
@@ -890,10 +896,13 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, file_receive);
set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create);
- set_to_cap_if_null(ops, task_alloc_security);
- set_to_cap_if_null(ops, task_free_security);
+ set_to_cap_if_null(ops, cred_free);
+ set_to_cap_if_null(ops, cred_prepare);
+ set_to_cap_if_null(ops, cred_commit);
+ set_to_cap_if_null(ops, kernel_act_as);
+ set_to_cap_if_null(ops, kernel_create_files_as);
set_to_cap_if_null(ops, task_setuid);
- set_to_cap_if_null(ops, task_post_setuid);
+ set_to_cap_if_null(ops, task_fix_setuid);
set_to_cap_if_null(ops, task_setgid);
set_to_cap_if_null(ops, task_setpgid);
set_to_cap_if_null(ops, task_getpgid);
@@ -910,7 +919,6 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, task_wait);
set_to_cap_if_null(ops, task_kill);
set_to_cap_if_null(ops, task_prctl);
- set_to_cap_if_null(ops, task_reparent_to_init);
set_to_cap_if_null(ops, task_to_inode);
set_to_cap_if_null(ops, ipc_permission);
set_to_cap_if_null(ops, ipc_getsecid);
diff --git a/security/commoncap.c b/security/commoncap.c
index 3976613db829..79713545cd63 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -8,6 +8,7 @@
*/
#include <linux/capability.h>
+#include <linux/audit.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -29,7 +30,7 @@
int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- NETLINK_CB(skb).eff_cap = current->cap_effective;
+ NETLINK_CB(skb).eff_cap = current_cap();
return 0;
}
@@ -39,23 +40,41 @@ int cap_netlink_recv(struct sk_buff *skb, int cap)
return -EPERM;
return 0;
}
-
EXPORT_SYMBOL(cap_netlink_recv);
-/*
+/**
+ * cap_capable - Determine whether a task has a particular effective capability
+ * @tsk: The task to query
+ * @cap: The capability to check for
+ * @audit: Whether to write an audit message or not
+ *
+ * Determine whether the nominated task has the specified capability amongst
+ * its effective set, returning 0 if it does, -ve if it does not.
+ *
* NOTE WELL: cap_capable() cannot be used like the kernel's capable()
- * function. That is, it has the reverse semantics: cap_capable()
- * returns 0 when a task has a capability, but the kernel's capable()
- * returns 1 for this case.
+ * function. That is, it has the reverse semantics: cap_capable() returns 0
+ * when a task has a capability, but the kernel's capable() returns 1 for this
+ * case.
*/
-int cap_capable (struct task_struct *tsk, int cap)
+int cap_capable(struct task_struct *tsk, int cap, int audit)
{
+ __u32 cap_raised;
+
/* Derived from include/linux/sched.h:capable. */
- if (cap_raised(tsk->cap_effective, cap))
- return 0;
- return -EPERM;
+ rcu_read_lock();
+ cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap);
+ rcu_read_unlock();
+ return cap_raised ? 0 : -EPERM;
}
+/**
+ * cap_settime - Determine whether the current process may set the system clock
+ * @ts: The time to set
+ * @tz: The timezone to set
+ *
+ * Determine whether the current process may set the system clock and timezone
+ * information, returning 0 if permission granted, -ve if denied.
+ */
int cap_settime(struct timespec *ts, struct timezone *tz)
{
if (!capable(CAP_SYS_TIME))
@@ -63,121 +82,157 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
return 0;
}
+/**
+ * cap_ptrace_may_access - Determine whether the current process may access
+ * another
+ * @child: The process to be accessed
+ * @mode: The mode of attachment.
+ *
+ * Determine whether a process may access another, returning 0 if permission
+ * granted, -ve if denied.
+ */
int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
{
- /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
- if (cap_issubset(child->cap_permitted, current->cap_permitted))
- return 0;
- if (capable(CAP_SYS_PTRACE))
- return 0;
- return -EPERM;
+ int ret = 0;
+
+ rcu_read_lock();
+ if (!cap_issubset(__task_cred(child)->cap_permitted,
+ current_cred()->cap_permitted) &&
+ !capable(CAP_SYS_PTRACE))
+ ret = -EPERM;
+ rcu_read_unlock();
+ return ret;
}
+/**
+ * cap_ptrace_traceme - Determine whether another process may trace the current
+ * @parent: The task proposed to be the tracer
+ *
+ * Determine whether the nominated task is permitted to trace the current
+ * process, returning 0 if permission is granted, -ve if denied.
+ */
int cap_ptrace_traceme(struct task_struct *parent)
{
- /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
- if (cap_issubset(current->cap_permitted, parent->cap_permitted))
- return 0;
- if (has_capability(parent, CAP_SYS_PTRACE))
- return 0;
- return -EPERM;
+ int ret = 0;
+
+ rcu_read_lock();
+ if (!cap_issubset(current_cred()->cap_permitted,
+ __task_cred(parent)->cap_permitted) &&
+ !has_capability(parent, CAP_SYS_PTRACE))
+ ret = -EPERM;
+ rcu_read_unlock();
+ return ret;
}
-int cap_capget (struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
+/**
+ * cap_capget - Retrieve a task's capability sets
+ * @target: The task from which to retrieve the capability sets
+ * @effective: The place to record the effective set
+ * @inheritable: The place to record the inheritable set
+ * @permitted: The place to record the permitted set
+ *
+ * This function retrieves the capabilities of the nominated task and returns
+ * them to the caller.
+ */
+int cap_capget(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
+ const struct cred *cred;
+
/* Derived from kernel/capability.c:sys_capget. */
- *effective = target->cap_effective;
- *inheritable = target->cap_inheritable;
- *permitted = target->cap_permitted;
+ rcu_read_lock();
+ cred = __task_cred(target);
+ *effective = cred->cap_effective;
+ *inheritable = cred->cap_inheritable;
+ *permitted = cred->cap_permitted;
+ rcu_read_unlock();
return 0;
}
-#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
-
-static inline int cap_block_setpcap(struct task_struct *target)
-{
- /*
- * No support for remote process capability manipulation with
- * filesystem capability support.
- */
- return (target != current);
-}
-
+/*
+ * Determine whether the inheritable capabilities are limited to the old
+ * permitted set. Returns 1 if they are limited, 0 if they are not.
+ */
static inline int cap_inh_is_capped(void)
{
- /*
- * Return 1 if changes to the inheritable set are limited
- * to the old permitted set. That is, if the current task
- * does *not* possess the CAP_SETPCAP capability.
- */
- return (cap_capable(current, CAP_SETPCAP) != 0);
-}
-
-static inline int cap_limit_ptraced_target(void) { return 1; }
-
-#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
-static inline int cap_block_setpcap(struct task_struct *t) { return 0; }
-static inline int cap_inh_is_capped(void) { return 1; }
-static inline int cap_limit_ptraced_target(void)
-{
- return !capable(CAP_SETPCAP);
+ /* they are so limited unless the current task has the CAP_SETPCAP
+ * capability
+ */
+ if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
+ return 0;
+#endif
+ return 1;
}
-#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
-
-int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
- if (cap_block_setpcap(target)) {
- return -EPERM;
- }
- if (cap_inh_is_capped()
- && !cap_issubset(*inheritable,
- cap_combine(target->cap_inheritable,
- current->cap_permitted))) {
+/**
+ * cap_capset - Validate and apply proposed changes to current's capabilities
+ * @new: The proposed new credentials; alterations should be made here
+ * @old: The current task's current credentials
+ * @effective: A pointer to the proposed new effective capabilities set
+ * @inheritable: A pointer to the proposed new inheritable capabilities set
+ * @permitted: A pointer to the proposed new permitted capabilities set
+ *
+ * This function validates and applies a proposed mass change to the current
+ * process's capability sets. The changes are made to the proposed new
+ * credentials, and assuming no error, will be committed by the caller of LSM.
+ */
+int cap_capset(struct cred *new,
+ const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
+{
+ if (cap_inh_is_capped() &&
+ !cap_issubset(*inheritable,
+ cap_combine(old->cap_inheritable,
+ old->cap_permitted)))
/* incapable of using this inheritable set */
return -EPERM;
- }
+
if (!cap_issubset(*inheritable,
- cap_combine(target->cap_inheritable,
- current->cap_bset))) {
+ cap_combine(old->cap_inheritable,
+ old->cap_bset)))
/* no new pI capabilities outside bounding set */
return -EPERM;
- }
/* verify restrictions on target's new Permitted set */
- if (!cap_issubset (*permitted,
- cap_combine (target->cap_permitted,
- current->cap_permitted))) {
+ if (!cap_issubset(*permitted, old->cap_permitted))
return -EPERM;
- }
/* verify the _new_Effective_ is a subset of the _new_Permitted_ */
- if (!cap_issubset (*effective, *permitted)) {
+ if (!cap_issubset(*effective, *permitted))
return -EPERM;
- }
+ new->cap_effective = *effective;
+ new->cap_inheritable = *inheritable;
+ new->cap_permitted = *permitted;
return 0;
}
-void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
- target->cap_effective = *effective;
- target->cap_inheritable = *inheritable;
- target->cap_permitted = *permitted;
-}
-
+/*
+ * Clear proposed capability sets for execve().
+ */
static inline void bprm_clear_caps(struct linux_binprm *bprm)
{
- cap_clear(bprm->cap_post_exec_permitted);
+ cap_clear(bprm->cred->cap_permitted);
bprm->cap_effective = false;
}
#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+/**
+ * cap_inode_need_killpriv - Determine if inode change affects privileges
+ * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV
+ *
+ * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV
+ * affects the security markings on that inode, and if it is, should
+ * inode_killpriv() be invoked or the change rejected?
+ *
+ * Returns 0 if granted; +ve if granted, but inode_killpriv() is required; and
+ * -ve to deny the change.
+ */
int cap_inode_need_killpriv(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
@@ -192,6 +247,14 @@ int cap_inode_need_killpriv(struct dentry *dentry)
return 1;
}
+/**
+ * cap_inode_killpriv - Erase the security markings on an inode
+ * @dentry: The inode/dentry to alter
+ *
+ * Erase the privilege-enhancing security markings on an inode.
+ *
+ * Returns 0 if successful, -ve on error.
+ */
int cap_inode_killpriv(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
@@ -202,19 +265,75 @@ int cap_inode_killpriv(struct dentry *dentry)
return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
}
-static inline int cap_from_disk(struct vfs_cap_data *caps,
- struct linux_binprm *bprm, unsigned size)
+/*
+ * Calculate the new process capability sets from the capability sets attached
+ * to a file.
+ */
+static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
+ struct linux_binprm *bprm,
+ bool *effective)
+{
+ struct cred *new = bprm->cred;
+ unsigned i;
+ int ret = 0;
+
+ if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
+ *effective = true;
+
+ CAP_FOR_EACH_U32(i) {
+ __u32 permitted = caps->permitted.cap[i];
+ __u32 inheritable = caps->inheritable.cap[i];
+
+ /*
+ * pP' = (X & fP) | (pI & fI)
+ */
+ new->cap_permitted.cap[i] =
+ (new->cap_bset.cap[i] & permitted) |
+ (new->cap_inheritable.cap[i] & inheritable);
+
+ if (permitted & ~new->cap_permitted.cap[i])
+ /* insufficient to execute correctly */
+ ret = -EPERM;
+ }
+
+ /*
+ * For legacy apps, with no internal support for recognizing they
+ * do not have enough capabilities, we return an error if they are
+ * missing some "forced" (aka file-permitted) capabilities.
+ */
+ return *effective ? ret : 0;
+}
+
+/*
+ * Extract the on-exec-apply capability sets for an executable file.
+ */
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
{
+ struct inode *inode = dentry->d_inode;
__u32 magic_etc;
unsigned tocopy, i;
- int ret;
+ int size;
+ struct vfs_cap_data caps;
+
+ memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
+
+ if (!inode || !inode->i_op || !inode->i_op->getxattr)
+ return -ENODATA;
+
+ size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
+ XATTR_CAPS_SZ);
+ if (size == -ENODATA || size == -EOPNOTSUPP)
+ /* no data, that's ok */
+ return -ENODATA;
+ if (size < 0)
+ return size;
if (size < sizeof(magic_etc))
return -EINVAL;
- magic_etc = le32_to_cpu(caps->magic_etc);
+ cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
- switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
+ switch (magic_etc & VFS_CAP_REVISION_MASK) {
case VFS_CAP_REVISION_1:
if (size != XATTR_CAPS_SZ_1)
return -EINVAL;
@@ -229,77 +348,48 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
return -EINVAL;
}
- if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
- bprm->cap_effective = true;
- } else {
- bprm->cap_effective = false;
- }
-
- ret = 0;
-
CAP_FOR_EACH_U32(i) {
- __u32 value_cpu;
-
- if (i >= tocopy) {
- /*
- * Legacy capability sets have no upper bits
- */
- bprm->cap_post_exec_permitted.cap[i] = 0;
- continue;
- }
- /*
- * pP' = (X & fP) | (pI & fI)
- */
- value_cpu = le32_to_cpu(caps->data[i].permitted);
- bprm->cap_post_exec_permitted.cap[i] =
- (current->cap_bset.cap[i] & value_cpu) |
- (current->cap_inheritable.cap[i] &
- le32_to_cpu(caps->data[i].inheritable));
- if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
- /*
- * insufficient to execute correctly
- */
- ret = -EPERM;
- }
+ if (i >= tocopy)
+ break;
+ cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
+ cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
}
- /*
- * For legacy apps, with no internal support for recognizing they
- * do not have enough capabilities, we return an error if they are
- * missing some "forced" (aka file-permitted) capabilities.
- */
- return bprm->cap_effective ? ret : 0;
+ return 0;
}
-/* Locate any VFS capabilities: */
-static int get_file_caps(struct linux_binprm *bprm)
+/*
+ * Attempt to get the on-exec apply capability sets for an executable file from
+ * its xattrs and, if present, apply them to the proposed credentials being
+ * constructed by execve().
+ */
+static int get_file_caps(struct linux_binprm *bprm, bool *effective)
{
struct dentry *dentry;
int rc = 0;
- struct vfs_cap_data vcaps;
- struct inode *inode;
+ struct cpu_vfs_cap_data vcaps;
bprm_clear_caps(bprm);
+ if (!file_caps_enabled)
+ return 0;
+
if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
return 0;
dentry = dget(bprm->file->f_dentry);
- inode = dentry->d_inode;
- if (!inode->i_op || !inode->i_op->getxattr)
- goto out;
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps,
- XATTR_CAPS_SZ);
- if (rc == -ENODATA || rc == -EOPNOTSUPP) {
- /* no data, that's ok */
- rc = 0;
+ rc = get_vfs_caps_from_disk(dentry, &vcaps);
+ if (rc < 0) {
+ if (rc == -EINVAL)
+ printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
+ __func__, rc, bprm->filename);
+ else if (rc == -ENODATA)
+ rc = 0;
goto out;
}
- if (rc < 0)
- goto out;
- rc = cap_from_disk(&vcaps, bprm, rc);
+ rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective);
if (rc == -EINVAL)
printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
__func__, rc, bprm->filename);
@@ -323,18 +413,57 @@ int cap_inode_killpriv(struct dentry *dentry)
return 0;
}
-static inline int get_file_caps(struct linux_binprm *bprm)
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
+{
+ memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
+ return -ENODATA;
+}
+
+static inline int get_file_caps(struct linux_binprm *bprm, bool *effective)
{
bprm_clear_caps(bprm);
return 0;
}
#endif
-int cap_bprm_set_security (struct linux_binprm *bprm)
+/*
+ * Determine whether a exec'ing process's new permitted capabilities should be
+ * limited to just what it already has.
+ *
+ * This prevents processes that are being ptraced from gaining access to
+ * CAP_SETPCAP, unless the process they're tracing already has it, and the
+ * binary they're executing has filecaps that elevate it.
+ *
+ * Returns 1 if they should be limited, 0 if they are not.
+ */
+static inline int cap_limit_ptraced_target(void)
+{
+#ifndef CONFIG_SECURITY_FILE_CAPABILITIES
+ if (capable(CAP_SETPCAP))
+ return 0;
+#endif
+ return 1;
+}
+
+/**
+ * cap_bprm_set_creds - Set up the proposed credentials for execve().
+ * @bprm: The execution parameters, including the proposed creds
+ *
+ * Set up the proposed credentials for a new execution context being
+ * constructed by execve(). The proposed creds in @bprm->cred is altered,
+ * which won't take effect immediately. Returns 0 if successful, -ve on error.
+ */
+int cap_bprm_set_creds(struct linux_binprm *bprm)
{
+ const struct cred *old = current_cred();
+ struct cred *new = bprm->cred;
+ bool effective;
int ret;
- ret = get_file_caps(bprm);
+ effective = false;
+ ret = get_file_caps(bprm, &effective);
+ if (ret < 0)
+ return ret;
if (!issecure(SECURE_NOROOT)) {
/*
@@ -342,75 +471,113 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
* executables under compatibility mode, we override the
* capability sets for the file.
*
- * If only the real uid is 0, we do not set the effective
- * bit.
+ * If only the real uid is 0, we do not set the effective bit.
*/
- if (bprm->e_uid == 0 || current->uid == 0) {
+ if (new->euid == 0 || new->uid == 0) {
/* pP' = (cap_bset & ~0) | (pI & ~0) */
- bprm->cap_post_exec_permitted = cap_combine(
- current->cap_bset, current->cap_inheritable
- );
- bprm->cap_effective = (bprm->e_uid == 0);
- ret = 0;
+ new->cap_permitted = cap_combine(old->cap_bset,
+ old->cap_inheritable);
}
+ if (new->euid == 0)
+ effective = true;
}
- return ret;
-}
-
-void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
-{
- if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
- !cap_issubset(bprm->cap_post_exec_permitted,
- current->cap_permitted)) {
- set_dumpable(current->mm, suid_dumpable);
- current->pdeath_signal = 0;
-
- if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
- if (!capable(CAP_SETUID)) {
- bprm->e_uid = current->uid;
- bprm->e_gid = current->gid;
- }
- if (cap_limit_ptraced_target()) {
- bprm->cap_post_exec_permitted = cap_intersect(
- bprm->cap_post_exec_permitted,
- current->cap_permitted);
- }
+ /* Don't let someone trace a set[ug]id/setpcap binary with the revised
+ * credentials unless they have the appropriate permit
+ */
+ if ((new->euid != old->uid ||
+ new->egid != old->gid ||
+ !cap_issubset(new->cap_permitted, old->cap_permitted)) &&
+ bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
+ /* downgrade; they get no more than they had, and maybe less */
+ if (!capable(CAP_SETUID)) {
+ new->euid = new->uid;
+ new->egid = new->gid;
}
+ if (cap_limit_ptraced_target())
+ new->cap_permitted = cap_intersect(new->cap_permitted,
+ old->cap_permitted);
}
- current->suid = current->euid = current->fsuid = bprm->e_uid;
- current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ new->suid = new->fsuid = new->euid;
+ new->sgid = new->fsgid = new->egid;
- /* For init, we want to retain the capabilities set
- * in the init_task struct. Thus we skip the usual
- * capability rules */
+ /* For init, we want to retain the capabilities set in the initial
+ * task. Thus we skip the usual capability rules
+ */
if (!is_global_init(current)) {
- current->cap_permitted = bprm->cap_post_exec_permitted;
- if (bprm->cap_effective)
- current->cap_effective = bprm->cap_post_exec_permitted;
+ if (effective)
+ new->cap_effective = new->cap_permitted;
else
- cap_clear(current->cap_effective);
+ cap_clear(new->cap_effective);
}
+ bprm->cap_effective = effective;
- /* AUD: Audit candidate if current->cap_effective is set */
+ /*
+ * Audit candidate if current->cap_effective is set
+ *
+ * We do not bother to audit if 3 things are true:
+ * 1) cap_effective has all caps
+ * 2) we are root
+ * 3) root is supposed to have all caps (SECURE_NOROOT)
+ * Since this is just a normal root execing a process.
+ *
+ * Number 1 above might fail if you don't have a full bset, but I think
+ * that is interesting information to audit.
+ */
+ if (!cap_isclear(new->cap_effective)) {
+ if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
+ new->euid != 0 || new->uid != 0 ||
+ issecure(SECURE_NOROOT)) {
+ ret = audit_log_bprm_fcaps(bprm, new, old);
+ if (ret < 0)
+ return ret;
+ }
+ }
- current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ return 0;
}
-int cap_bprm_secureexec (struct linux_binprm *bprm)
+/**
+ * cap_bprm_secureexec - Determine whether a secure execution is required
+ * @bprm: The execution parameters
+ *
+ * Determine whether a secure execution is required, return 1 if it is, and 0
+ * if it is not.
+ *
+ * The credentials have been committed by this point, and so are no longer
+ * available through @bprm->cred.
+ */
+int cap_bprm_secureexec(struct linux_binprm *bprm)
{
- if (current->uid != 0) {
+ const struct cred *cred = current_cred();
+
+ if (cred->uid != 0) {
if (bprm->cap_effective)
return 1;
- if (!cap_isclear(bprm->cap_post_exec_permitted))
+ if (!cap_isclear(cred->cap_permitted))
return 1;
}
- return (current->euid != current->uid ||
- current->egid != current->gid);
+ return (cred->euid != cred->uid ||
+ cred->egid != cred->gid);
}
+/**
+ * cap_inode_setxattr - Determine whether an xattr may be altered
+ * @dentry: The inode/dentry being altered
+ * @name: The name of the xattr to be changed
+ * @value: The value that the xattr will be changed to
+ * @size: The size of value
+ * @flags: The replacement flag
+ *
+ * Determine whether an xattr may be altered or set on an inode, returning 0 if
+ * permission is granted, -ve if denied.
+ *
+ * This is used to make sure security xattrs don't get updated or set by those
+ * who aren't privileged to do so.
+ */
int cap_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
@@ -418,28 +585,42 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
if (!capable(CAP_SETFCAP))
return -EPERM;
return 0;
- } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ }
+
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
+/**
+ * cap_inode_removexattr - Determine whether an xattr may be removed
+ * @dentry: The inode/dentry being altered
+ * @name: The name of the xattr to be changed
+ *
+ * Determine whether an xattr may be removed from an inode, returning 0 if
+ * permission is granted, -ve if denied.
+ *
+ * This is used to make sure security xattrs don't get removed by those who
+ * aren't privileged to remove them.
+ */
int cap_inode_removexattr(struct dentry *dentry, const char *name)
{
if (!strcmp(name, XATTR_NAME_CAPS)) {
if (!capable(CAP_SETFCAP))
return -EPERM;
return 0;
- } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ }
+
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
-/* moved from kernel/sys.c. */
-/*
+/*
* cap_emulate_setxuid() fixes the effective / permitted capabilities of
* a process after a call to setuid, setreuid, or setresuid.
*
@@ -453,10 +634,10 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
* 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
* capabilities are set to the permitted capabilities.
*
- * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
+ * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
* never happen.
*
- * -astor
+ * -astor
*
* cevans - New behaviour, Oct '99
* A process may, via prctl(), elect to keep its capabilities when it
@@ -468,61 +649,60 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
* files..
* Thanks to Olaf Kirch and Peter Benie for spotting this.
*/
-static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
- int old_suid)
+static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
{
- if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
- (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+ if ((old->uid == 0 || old->euid == 0 || old->suid == 0) &&
+ (new->uid != 0 && new->euid != 0 && new->suid != 0) &&
!issecure(SECURE_KEEP_CAPS)) {
- cap_clear (current->cap_permitted);
- cap_clear (current->cap_effective);
- }
- if (old_euid == 0 && current->euid != 0) {
- cap_clear (current->cap_effective);
- }
- if (old_euid != 0 && current->euid == 0) {
- current->cap_effective = current->cap_permitted;
+ cap_clear(new->cap_permitted);
+ cap_clear(new->cap_effective);
}
+ if (old->euid == 0 && new->euid != 0)
+ cap_clear(new->cap_effective);
+ if (old->euid != 0 && new->euid == 0)
+ new->cap_effective = new->cap_permitted;
}
-int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
- int flags)
+/**
+ * cap_task_fix_setuid - Fix up the results of setuid() call
+ * @new: The proposed credentials
+ * @old: The current task's current credentials
+ * @flags: Indications of what has changed
+ *
+ * Fix up the results of setuid() call before the credential changes are
+ * actually applied, returning 0 to grant the changes, -ve to deny them.
+ */
+int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags)
{
switch (flags) {
case LSM_SETID_RE:
case LSM_SETID_ID:
case LSM_SETID_RES:
- /* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
- if (!issecure (SECURE_NO_SETUID_FIXUP)) {
- cap_emulate_setxuid (old_ruid, old_euid, old_suid);
- }
+ /* juggle the capabilities to follow [RES]UID changes unless
+ * otherwise suppressed */
+ if (!issecure(SECURE_NO_SETUID_FIXUP))
+ cap_emulate_setxuid(new, old);
break;
- case LSM_SETID_FS:
- {
- uid_t old_fsuid = old_ruid;
- /* Copied from kernel/sys.c:setfsuid. */
-
- /*
- * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
- * if not, we might be a bit too harsh here.
- */
-
- if (!issecure (SECURE_NO_SETUID_FIXUP)) {
- if (old_fsuid == 0 && current->fsuid != 0) {
- current->cap_effective =
- cap_drop_fs_set(
- current->cap_effective);
- }
- if (old_fsuid != 0 && current->fsuid == 0) {
- current->cap_effective =
- cap_raise_fs_set(
- current->cap_effective,
- current->cap_permitted);
- }
- }
- break;
+ case LSM_SETID_FS:
+ /* juggle the capabilties to follow FSUID changes, unless
+ * otherwise suppressed
+ *
+ * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
+ * if not, we might be a bit too harsh here.
+ */
+ if (!issecure(SECURE_NO_SETUID_FIXUP)) {
+ if (old->fsuid == 0 && new->fsuid != 0)
+ new->cap_effective =
+ cap_drop_fs_set(new->cap_effective);
+
+ if (old->fsuid != 0 && new->fsuid == 0)
+ new->cap_effective =
+ cap_raise_fs_set(new->cap_effective,
+ new->cap_permitted);
}
+ break;
+
default:
return -EINVAL;
}
@@ -543,42 +723,71 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
*/
static int cap_safe_nice(struct task_struct *p)
{
- if (!cap_issubset(p->cap_permitted, current->cap_permitted) &&
- !capable(CAP_SYS_NICE))
+ int is_subset;
+
+ rcu_read_lock();
+ is_subset = cap_issubset(__task_cred(p)->cap_permitted,
+ current_cred()->cap_permitted);
+ rcu_read_unlock();
+
+ if (!is_subset && !capable(CAP_SYS_NICE))
return -EPERM;
return 0;
}
-int cap_task_setscheduler (struct task_struct *p, int policy,
+/**
+ * cap_task_setscheduler - Detemine if scheduler policy change is permitted
+ * @p: The task to affect
+ * @policy: The policy to effect
+ * @lp: The parameters to the scheduling policy
+ *
+ * Detemine if the requested scheduler policy change is permitted for the
+ * specified task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setscheduler(struct task_struct *p, int policy,
struct sched_param *lp)
{
return cap_safe_nice(p);
}
-int cap_task_setioprio (struct task_struct *p, int ioprio)
+/**
+ * cap_task_ioprio - Detemine if I/O priority change is permitted
+ * @p: The task to affect
+ * @ioprio: The I/O priority to set
+ *
+ * Detemine if the requested I/O priority change is permitted for the specified
+ * task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setioprio(struct task_struct *p, int ioprio)
{
return cap_safe_nice(p);
}
-int cap_task_setnice (struct task_struct *p, int nice)
+/**
+ * cap_task_ioprio - Detemine if task priority change is permitted
+ * @p: The task to affect
+ * @nice: The nice value to set
+ *
+ * Detemine if the requested task priority change is permitted for the
+ * specified task, returning 0 if permission is granted, -ve if denied.
+ */
+int cap_task_setnice(struct task_struct *p, int nice)
{
return cap_safe_nice(p);
}
/*
- * called from kernel/sys.c for prctl(PR_CABSET_DROP)
- * done without task_capability_lock() because it introduces
- * no new races - i.e. only another task doing capget() on
- * this task could get inconsistent info. There can be no
- * racing writer bc a task can only change its own caps.
+ * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from
+ * the current task's bounding set. Returns 0 on success, -ve on error.
*/
-static long cap_prctl_drop(unsigned long cap)
+static long cap_prctl_drop(struct cred *new, unsigned long cap)
{
if (!capable(CAP_SETPCAP))
return -EPERM;
if (!cap_valid(cap))
return -EINVAL;
- cap_lower(current->cap_bset, cap);
+
+ cap_lower(new->cap_bset, cap);
return 0;
}
@@ -598,22 +807,42 @@ int cap_task_setnice (struct task_struct *p, int nice)
}
#endif
+/**
+ * cap_task_prctl - Implement process control functions for this security module
+ * @option: The process control function requested
+ * @arg2, @arg3, @arg4, @arg5: The argument data for this function
+ *
+ * Allow process control functions (sys_prctl()) to alter capabilities; may
+ * also deny access to other functions not otherwise implemented here.
+ *
+ * Returns 0 or +ve on success, -ENOSYS if this function is not implemented
+ * here, other -ve on error. If -ENOSYS is returned, sys_prctl() and other LSM
+ * modules will consider performing the function.
+ */
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p)
+ unsigned long arg4, unsigned long arg5)
{
+ struct cred *new;
long error = 0;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
switch (option) {
case PR_CAPBSET_READ:
+ error = -EINVAL;
if (!cap_valid(arg2))
- error = -EINVAL;
- else
- error = !!cap_raised(current->cap_bset, arg2);
- break;
+ goto error;
+ error = !!cap_raised(new->cap_bset, arg2);
+ goto no_change;
+
#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
case PR_CAPBSET_DROP:
- error = cap_prctl_drop(arg2);
- break;
+ error = cap_prctl_drop(new, arg2);
+ if (error < 0)
+ goto error;
+ goto changed;
/*
* The next four prctl's remain to assist with transitioning a
@@ -635,12 +864,12 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* capability-based-privilege environment.
*/
case PR_SET_SECUREBITS:
- if ((((current->securebits & SECURE_ALL_LOCKS) >> 1)
- & (current->securebits ^ arg2)) /*[1]*/
- || ((current->securebits & SECURE_ALL_LOCKS
- & ~arg2)) /*[2]*/
- || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
- || (cap_capable(current, CAP_SETPCAP) != 0)) { /*[4]*/
+ error = -EPERM;
+ if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
+ & (new->securebits ^ arg2)) /*[1]*/
+ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
+ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
+ || (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/
/*
* [1] no changing of bits that are locked
* [2] no unlocking of locks
@@ -648,65 +877,80 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* [4] doing anything requires privilege (go read about
* the "sendmail capabilities bug")
*/
- error = -EPERM; /* cannot change a locked bit */
- } else {
- current->securebits = arg2;
- }
- break;
+ )
+ /* cannot change a locked bit */
+ goto error;
+ new->securebits = arg2;
+ goto changed;
+
case PR_GET_SECUREBITS:
- error = current->securebits;
- break;
+ error = new->securebits;
+ goto no_change;
#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
case PR_GET_KEEPCAPS:
if (issecure(SECURE_KEEP_CAPS))
error = 1;
- break;
+ goto no_change;
+
case PR_SET_KEEPCAPS:
+ error = -EINVAL;
if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
- error = -EINVAL;
- else if (issecure(SECURE_KEEP_CAPS_LOCKED))
- error = -EPERM;
- else if (arg2)
- current->securebits |= issecure_mask(SECURE_KEEP_CAPS);
+ goto error;
+ error = -EPERM;
+ if (issecure(SECURE_KEEP_CAPS_LOCKED))
+ goto error;
+ if (arg2)
+ new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
else
- current->securebits &=
- ~issecure_mask(SECURE_KEEP_CAPS);
- break;
+ new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+ goto changed;
default:
/* No functionality available - continue with default */
- return 0;
+ error = -ENOSYS;
+ goto error;
}
/* Functionality provided */
- *rc_p = error;
- return 1;
-}
+changed:
+ return commit_creds(new);
-void cap_task_reparent_to_init (struct task_struct *p)
-{
- cap_set_init_eff(p->cap_effective);
- cap_clear(p->cap_inheritable);
- cap_set_full(p->cap_permitted);
- p->securebits = SECUREBITS_DEFAULT;
- return;
+no_change:
+ error = 0;
+error:
+ abort_creds(new);
+ return error;
}
-int cap_syslog (int type)
+/**
+ * cap_syslog - Determine whether syslog function is permitted
+ * @type: Function requested
+ *
+ * Determine whether the current process is permitted to use a particular
+ * syslog function, returning 0 if permission is granted, -ve if not.
+ */
+int cap_syslog(int type)
{
if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
+/**
+ * cap_vm_enough_memory - Determine whether a new virtual mapping is permitted
+ * @mm: The VM space in which the new mapping is to be made
+ * @pages: The size of the mapping
+ *
+ * Determine whether the allocation of a new virtual mapping by the current
+ * task is permitted, returning 0 if permission is granted, -ve if not.
+ */
int cap_vm_enough_memory(struct mm_struct *mm, long pages)
{
int cap_sys_admin = 0;
- if (cap_capable(current, CAP_SYS_ADMIN) == 0)
+ if (cap_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT) == 0)
cap_sys_admin = 1;
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
-
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 239098f0fd76..81932abefe7b 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -12,8 +12,8 @@
#ifndef _INTERNAL_H
#define _INTERNAL_H
+#include <linux/sched.h>
#include <linux/key-type.h>
-#include <linux/key-ui.h>
static inline __attribute__((format(printf, 1, 2)))
void no_printk(const char *fmt, ...)
@@ -26,7 +26,7 @@ void no_printk(const char *fmt, ...)
#define kleave(FMT, ...) \
printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
#define kdebug(FMT, ...) \
- printk(KERN_DEBUG "xxx" FMT"yyy\n", ##__VA_ARGS__)
+ printk(KERN_DEBUG " "FMT"\n", ##__VA_ARGS__)
#else
#define kenter(FMT, ...) \
no_printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
@@ -82,6 +82,9 @@ extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq;
+extern struct key_type *key_type_lookup(const char *type);
+extern void key_type_put(struct key_type *ktype);
+
extern int __key_link(struct key *keyring, struct key *key);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
@@ -95,7 +98,7 @@ extern struct key *keyring_search_instkey(struct key *keyring,
typedef int (*key_match_func_t)(const struct key *, const void *);
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *tsk,
+ const struct cred *cred,
struct key_type *type,
const void *description,
key_match_func_t match);
@@ -103,13 +106,13 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
extern key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *tsk);
+ const struct cred *cred);
extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
-extern int install_user_keyrings(struct task_struct *tsk);
-extern int install_thread_keyring(struct task_struct *tsk);
-extern int install_process_keyring(struct task_struct *tsk);
+extern int install_user_keyrings(void);
+extern int install_thread_keyring_to_cred(struct cred *);
+extern int install_process_keyring_to_cred(struct cred *);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
@@ -119,12 +122,39 @@ extern struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags);
+extern key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+ key_perm_t perm);
+
+extern long join_session_keyring(const char *name);
+
+/*
+ * check to see whether permission is granted to use a key in the desired way
+ */
+extern int key_task_permission(const key_ref_t key_ref,
+ const struct cred *cred,
+ key_perm_t perm);
+
+static inline int key_permission(const key_ref_t key_ref, key_perm_t perm)
+{
+ return key_task_permission(key_ref, current_cred(), perm);
+}
+
+/* required permissions */
+#define KEY_VIEW 0x01 /* require permission to view attributes */
+#define KEY_READ 0x02 /* require permission to read content */
+#define KEY_WRITE 0x04 /* require permission to update / modify */
+#define KEY_SEARCH 0x08 /* require permission to search (keyring) or find (key) */
+#define KEY_LINK 0x10 /* require permission to link */
+#define KEY_SETATTR 0x20 /* require permission to change attributes */
+#define KEY_ALL 0x3f /* all the above permissions */
+
/*
* request_key authorisation
*/
struct request_key_auth {
struct key *target_key;
- struct task_struct *context;
+ struct key *dest_keyring;
+ const struct cred *cred;
void *callout_info;
size_t callout_len;
pid_t pid;
@@ -133,7 +163,8 @@ struct request_key_auth {
extern struct key_type key_type_request_key_auth;
extern struct key *request_key_auth_new(struct key *target,
const void *callout_info,
- size_t callout_len);
+ size_t callout_len,
+ struct key *dest_keyring);
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
diff --git a/security/keys/key.c b/security/keys/key.c
index 14948cf83ef6..f76c8a546fd3 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -218,7 +218,7 @@ serial_exists:
* instantiate the key or discard it before returning
*/
struct key *key_alloc(struct key_type *type, const char *desc,
- uid_t uid, gid_t gid, struct task_struct *ctx,
+ uid_t uid, gid_t gid, const struct cred *cred,
key_perm_t perm, unsigned long flags)
{
struct key_user *user = NULL;
@@ -294,7 +294,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
#endif
/* let the security module know about the key */
- ret = security_key_alloc(key, ctx, flags);
+ ret = security_key_alloc(key, cred, flags);
if (ret < 0)
goto security_error;
@@ -391,7 +391,7 @@ static int __key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
int ret, awaken;
@@ -421,8 +421,8 @@ static int __key_instantiate_and_link(struct key *key,
ret = __key_link(keyring, key);
/* disable the authorisation key */
- if (instkey)
- key_revoke(instkey);
+ if (authkey)
+ key_revoke(authkey);
}
}
@@ -444,14 +444,14 @@ int key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
int ret;
if (keyring)
down_write(&keyring->sem);
- ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey);
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey);
if (keyring)
up_write(&keyring->sem);
@@ -469,7 +469,7 @@ EXPORT_SYMBOL(key_instantiate_and_link);
int key_negate_and_link(struct key *key,
unsigned timeout,
struct key *keyring,
- struct key *instkey)
+ struct key *authkey)
{
struct timespec now;
int ret, awaken;
@@ -504,8 +504,8 @@ int key_negate_and_link(struct key *key,
ret = __key_link(keyring, key);
/* disable the authorisation key */
- if (instkey)
- key_revoke(instkey);
+ if (authkey)
+ key_revoke(authkey);
}
mutex_unlock(&key_construction_mutex);
@@ -743,6 +743,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm,
unsigned long flags)
{
+ const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
key_ref_t key_ref;
@@ -802,8 +803,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
/* allocate a new key */
- key = key_alloc(ktype, description, current->fsuid, current->fsgid,
- current, perm, flags);
+ key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
+ perm, flags);
if (IS_ERR(key)) {
key_ref = ERR_CAST(key);
goto error_3;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index acc9c89e40a8..6688765bd8b9 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -103,7 +103,7 @@ asmlinkage long sys_add_key(const char __user *_type,
}
/* find the target keyring (which must be writable) */
- keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error3;
@@ -185,7 +185,7 @@ asmlinkage long sys_request_key(const char __user *_type,
/* get the destination keyring if specified */
dest_ref = NULL;
if (destringid) {
- dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+ dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref);
goto error3;
@@ -235,7 +235,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
key_ref_t key_ref;
long ret;
- key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH);
+ key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -308,7 +308,7 @@ long keyctl_update_key(key_serial_t id,
}
/* find the target key (which must be writable) */
- key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+ key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -336,7 +336,7 @@ long keyctl_revoke_key(key_serial_t id)
key_ref_t key_ref;
long ret;
- key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+ key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -362,7 +362,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
key_ref_t keyring_ref;
long ret;
- keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
@@ -388,13 +388,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref;
long ret;
- keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
}
- key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK);
+ key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -422,13 +422,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref;
long ret;
- keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE);
+ keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error;
}
- key_ref = lookup_user_key(NULL, id, 0, 0, 0);
+ key_ref = lookup_user_key(id, 0, 0, 0);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error2;
@@ -464,7 +464,7 @@ long keyctl_describe_key(key_serial_t keyid,
char *tmpbuf;
long ret;
- key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+ key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
if (IS_ERR(key_ref)) {
/* viewing a key under construction is permitted if we have the
* authorisation token handy */
@@ -472,7 +472,7 @@ long keyctl_describe_key(key_serial_t keyid,
instkey = key_get_instantiation_authkey(keyid);
if (!IS_ERR(instkey)) {
key_put(instkey);
- key_ref = lookup_user_key(NULL, keyid,
+ key_ref = lookup_user_key(keyid,
0, 1, 0);
if (!IS_ERR(key_ref))
goto okay;
@@ -557,7 +557,7 @@ long keyctl_keyring_search(key_serial_t ringid,
}
/* get the keyring at which to begin the search */
- keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH);
+ keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref);
goto error2;
@@ -566,7 +566,7 @@ long keyctl_keyring_search(key_serial_t ringid,
/* get the destination keyring if specified */
dest_ref = NULL;
if (destringid) {
- dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+ dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref);
goto error3;
@@ -636,7 +636,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
long ret;
/* find the key first */
- key_ref = lookup_user_key(NULL, keyid, 0, 0, 0);
+ key_ref = lookup_user_key(keyid, 0, 0, 0);
if (IS_ERR(key_ref)) {
ret = -ENOKEY;
goto error;
@@ -699,7 +699,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
if (uid == (uid_t) -1 && gid == (gid_t) -1)
goto error;
- key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -804,7 +804,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
goto error;
- key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -817,7 +817,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
down_write(&key->sem);
/* if we're not the sysadmin, we can only change a key that we own */
- if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) {
+ if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
key->perm = perm;
ret = 0;
}
@@ -829,6 +829,60 @@ error:
} /* end keyctl_setperm_key() */
+/*
+ * get the destination keyring for instantiation
+ */
+static long get_instantiation_keyring(key_serial_t ringid,
+ struct request_key_auth *rka,
+ struct key **_dest_keyring)
+{
+ key_ref_t dkref;
+
+ *_dest_keyring = NULL;
+
+ /* just return a NULL pointer if we weren't asked to make a link */
+ if (ringid == 0)
+ return 0;
+
+ /* if a specific keyring is nominated by ID, then use that */
+ if (ringid > 0) {
+ dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(dkref))
+ return PTR_ERR(dkref);
+ *_dest_keyring = key_ref_to_ptr(dkref);
+ return 0;
+ }
+
+ if (ringid == KEY_SPEC_REQKEY_AUTH_KEY)
+ return -EINVAL;
+
+ /* otherwise specify the destination keyring recorded in the
+ * authorisation key (any KEY_SPEC_*_KEYRING) */
+ if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
+ *_dest_keyring = rka->dest_keyring;
+ return 0;
+ }
+
+ return -ENOKEY;
+}
+
+/*
+ * change the request_key authorisation key on the current process
+ */
+static int keyctl_change_reqkey_auth(struct key *key)
+{
+ struct cred *new;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
+ key_put(new->request_key_auth);
+ new->request_key_auth = key_get(key);
+
+ return commit_creds(new);
+}
+
/*****************************************************************************/
/*
* instantiate the key with the specified payload, and, if one is given, link
@@ -839,13 +893,15 @@ long keyctl_instantiate_key(key_serial_t id,
size_t plen,
key_serial_t ringid)
{
+ const struct cred *cred = current_cred();
struct request_key_auth *rka;
- struct key *instkey;
- key_ref_t keyring_ref;
+ struct key *instkey, *dest_keyring;
void *payload;
long ret;
bool vm = false;
+ kenter("%d,,%zu,%d", id, plen, ringid);
+
ret = -EINVAL;
if (plen > 1024 * 1024 - 1)
goto error;
@@ -853,7 +909,7 @@ long keyctl_instantiate_key(key_serial_t id,
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
- instkey = current->request_key_auth;
+ instkey = cred->request_key_auth;
if (!instkey)
goto error;
@@ -883,28 +939,20 @@ long keyctl_instantiate_key(key_serial_t id,
/* find the destination keyring amongst those belonging to the
* requesting task */
- keyring_ref = NULL;
- if (ringid) {
- keyring_ref = lookup_user_key(rka->context, ringid, 1, 0,
- KEY_WRITE);
- if (IS_ERR(keyring_ref)) {
- ret = PTR_ERR(keyring_ref);
- goto error2;
- }
- }
+ ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
+ if (ret < 0)
+ goto error2;
/* instantiate the key and link it into a keyring */
ret = key_instantiate_and_link(rka->target_key, payload, plen,
- key_ref_to_ptr(keyring_ref), instkey);
+ dest_keyring, instkey);
- key_ref_put(keyring_ref);
+ key_put(dest_keyring);
/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
- if (ret == 0) {
- key_put(current->request_key_auth);
- current->request_key_auth = NULL;
- }
+ if (ret == 0)
+ keyctl_change_reqkey_auth(NULL);
error2:
if (!vm)
@@ -923,15 +971,17 @@ error:
*/
long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
{
+ const struct cred *cred = current_cred();
struct request_key_auth *rka;
- struct key *instkey;
- key_ref_t keyring_ref;
+ struct key *instkey, *dest_keyring;
long ret;
+ kenter("%d,%u,%d", id, timeout, ringid);
+
/* the appropriate instantiation authorisation key must have been
* assumed before calling this */
ret = -EPERM;
- instkey = current->request_key_auth;
+ instkey = cred->request_key_auth;
if (!instkey)
goto error;
@@ -941,27 +991,20 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
/* find the destination keyring if present (which must also be
* writable) */
- keyring_ref = NULL;
- if (ringid) {
- keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
- if (IS_ERR(keyring_ref)) {
- ret = PTR_ERR(keyring_ref);
- goto error;
- }
- }
+ ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
+ if (ret < 0)
+ goto error;
/* instantiate the key and link it into a keyring */
ret = key_negate_and_link(rka->target_key, timeout,
- key_ref_to_ptr(keyring_ref), instkey);
+ dest_keyring, instkey);
- key_ref_put(keyring_ref);
+ key_put(dest_keyring);
/* discard the assumed authority if it's just been disabled by
* instantiation of the key */
- if (ret == 0) {
- key_put(current->request_key_auth);
- current->request_key_auth = NULL;
- }
+ if (ret == 0)
+ keyctl_change_reqkey_auth(NULL);
error:
return ret;
@@ -975,35 +1018,56 @@ error:
*/
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
- int ret;
+ struct cred *new;
+ int ret, old_setting;
+
+ old_setting = current_cred_xxx(jit_keyring);
+
+ if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
+ return old_setting;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
switch (reqkey_defl) {
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- ret = install_thread_keyring(current);
+ ret = install_thread_keyring_to_cred(new);
if (ret < 0)
- return ret;
+ goto error;
goto set;
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- ret = install_process_keyring(current);
- if (ret < 0)
- return ret;
+ ret = install_process_keyring_to_cred(new);
+ if (ret < 0) {
+ if (ret != -EEXIST)
+ goto error;
+ ret = 0;
+ }
+ goto set;
case KEY_REQKEY_DEFL_DEFAULT:
case KEY_REQKEY_DEFL_SESSION_KEYRING:
case KEY_REQKEY_DEFL_USER_KEYRING:
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- set:
- current->jit_keyring = reqkey_defl;
+ case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+ goto set;
case KEY_REQKEY_DEFL_NO_CHANGE:
- return current->jit_keyring;
-
case KEY_REQKEY_DEFL_GROUP_KEYRING:
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
+set:
+ new->jit_keyring = reqkey_defl;
+ commit_creds(new);
+ return old_setting;
+error:
+ abort_creds(new);
+ return -EINVAL;
+
} /* end keyctl_set_reqkey_keyring() */
/*****************************************************************************/
@@ -1018,7 +1082,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
time_t expiry;
long ret;
- key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
goto error;
@@ -1062,9 +1126,7 @@ long keyctl_assume_authority(key_serial_t id)
/* we divest ourselves of authority if given an ID of 0 */
if (id == 0) {
- key_put(current->request_key_auth);
- current->request_key_auth = NULL;
- ret = 0;
+ ret = keyctl_change_reqkey_auth(NULL);
goto error;
}
@@ -1079,10 +1141,12 @@ long keyctl_assume_authority(key_serial_t id)
goto error;
}
- key_put(current->request_key_auth);
- current->request_key_auth = authkey;
- ret = authkey->serial;
+ ret = keyctl_change_reqkey_auth(authkey);
+ if (ret < 0)
+ goto error;
+ key_put(authkey);
+ ret = authkey->serial;
error:
return ret;
@@ -1105,7 +1169,7 @@ long keyctl_get_security(key_serial_t keyid,
char *context;
long ret;
- key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+ key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
if (IS_ERR(key_ref)) {
if (PTR_ERR(key_ref) != -EACCES)
return PTR_ERR(key_ref);
@@ -1117,7 +1181,7 @@ long keyctl_get_security(key_serial_t keyid,
return PTR_ERR(key_ref);
key_put(instkey);
- key_ref = lookup_user_key(NULL, keyid, 0, 1, 0);
+ key_ref = lookup_user_key(keyid, 0, 1, 0);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index a9ab8affc092..ed851574d073 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -16,6 +16,7 @@
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
+#include <keys/keyring-type.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -244,14 +245,14 @@ static long keyring_read(const struct key *keyring,
* allocate a keyring and link into the destination keyring
*/
struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
- struct task_struct *ctx, unsigned long flags,
+ const struct cred *cred, unsigned long flags,
struct key *dest)
{
struct key *keyring;
int ret;
keyring = key_alloc(&key_type_keyring, description,
- uid, gid, ctx,
+ uid, gid, cred,
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
flags);
@@ -280,7 +281,7 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
* - we propagate the possession attribute from the keyring ref to the key ref
*/
key_ref_t keyring_search_aux(key_ref_t keyring_ref,
- struct task_struct *context,
+ const struct cred *cred,
struct key_type *type,
const void *description,
key_match_func_t match)
@@ -303,7 +304,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_check(keyring);
/* top keyring must have search permission to begin the search */
- err = key_task_permission(keyring_ref, context, KEY_SEARCH);
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
@@ -376,7 +377,7 @@ descend:
/* key must have search permissions */
if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ cred, KEY_SEARCH) < 0)
continue;
/* we set a different error code if we pass a negative key */
@@ -403,7 +404,7 @@ ascend:
continue;
if (key_task_permission(make_key_ref(key, possessed),
- context, KEY_SEARCH) < 0)
+ cred, KEY_SEARCH) < 0)
continue;
/* stack the current position */
@@ -458,7 +459,7 @@ key_ref_t keyring_search(key_ref_t keyring,
if (!type->match)
return ERR_PTR(-ENOKEY);
- return keyring_search_aux(keyring, current,
+ return keyring_search_aux(keyring, current->cred,
type, description, type->match);
} /* end keyring_search() */
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 3b41f9b52537..5d9fc7b93f2e 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -14,12 +14,19 @@
#include "internal.h"
/*****************************************************************************/
-/*
- * check to see whether permission is granted to use a key in the desired way,
- * but permit the security modules to override
+/**
+ * key_task_permission - Check a key can be used
+ * @key_ref: The key to check
+ * @cred: The credentials to use
+ * @perm: The permissions to check for
+ *
+ * Check to see whether permission is granted to use a key in the desired way,
+ * but permit the security modules to override.
+ *
+ * The caller must hold either a ref on cred or must hold the RCU readlock or a
+ * spinlock.
*/
-int key_task_permission(const key_ref_t key_ref,
- struct task_struct *context,
+int key_task_permission(const key_ref_t key_ref, const struct cred *cred,
key_perm_t perm)
{
struct key *key;
@@ -29,7 +36,7 @@ int key_task_permission(const key_ref_t key_ref,
key = key_ref_to_ptr(key_ref);
/* use the second 8-bits of permissions for keys the caller owns */
- if (key->uid == context->fsuid) {
+ if (key->uid == cred->fsuid) {
kperm = key->perm >> 16;
goto use_these_perms;
}
@@ -37,15 +44,12 @@ int key_task_permission(const key_ref_t key_ref,
/* use the third 8-bits of permissions for keys the caller has a group
* membership in common with */
if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
- if (key->gid == context->fsgid) {
+ if (key->gid == cred->fsgid) {
kperm = key->perm >> 8;
goto use_these_perms;
}
- task_lock(context);
- ret = groups_search(context->group_info, key->gid);
- task_unlock(context);
-
+ ret = groups_search(cred->group_info, key->gid);
if (ret) {
kperm = key->perm >> 8;
goto use_these_perms;
@@ -56,6 +60,7 @@ int key_task_permission(const key_ref_t key_ref,
kperm = key->perm;
use_these_perms:
+
/* use the top 8-bits of permissions for keys the caller possesses
* - possessor permissions are additive with other permissions
*/
@@ -68,7 +73,7 @@ use_these_perms:
return -EACCES;
/* let LSM be the final arbiter */
- return security_key_permission(key_ref, context, perm);
+ return security_key_permission(key_ref, cred, perm);
} /* end key_task_permission() */
diff --git a/security/keys/proc.c b/security/keys/proc.c
index f619170da760..7f508def50e3 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -136,8 +136,12 @@ static int proc_keys_show(struct seq_file *m, void *v)
int rc;
/* check whether the current task is allowed to view the key (assuming
- * non-possession) */
- rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
+ * non-possession)
+ * - the caller holds a spinlock, and thus the RCU read lock, making our
+ * access to __current_cred() safe
+ */
+ rc = key_task_permission(make_key_ref(key, 0), current_cred(),
+ KEY_VIEW);
if (rc < 0)
return 0;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 45b240af6dbe..2f5d89e92b85 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -40,13 +40,17 @@ struct key_user root_key_user = {
/*
* install user and user session keyrings for a particular UID
*/
-int install_user_keyrings(struct task_struct *tsk)
+int install_user_keyrings(void)
{
- struct user_struct *user = tsk->user;
+ struct user_struct *user;
+ const struct cred *cred;
struct key *uid_keyring, *session_keyring;
char buf[20];
int ret;
+ cred = current_cred();
+ user = cred->user;
+
kenter("%p{%u}", user, user->uid);
if (user->uid_keyring) {
@@ -67,7 +71,7 @@ int install_user_keyrings(struct task_struct *tsk)
uid_keyring = find_keyring_by_name(buf, true);
if (IS_ERR(uid_keyring)) {
uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
- tsk, KEY_ALLOC_IN_QUOTA,
+ cred, KEY_ALLOC_IN_QUOTA,
NULL);
if (IS_ERR(uid_keyring)) {
ret = PTR_ERR(uid_keyring);
@@ -83,7 +87,7 @@ int install_user_keyrings(struct task_struct *tsk)
if (IS_ERR(session_keyring)) {
session_keyring =
keyring_alloc(buf, user->uid, (gid_t) -1,
- tsk, KEY_ALLOC_IN_QUOTA, NULL);
+ cred, KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(session_keyring)) {
ret = PTR_ERR(session_keyring);
goto error_release;
@@ -115,140 +119,128 @@ error:
return ret;
}
-/*****************************************************************************/
/*
- * deal with the UID changing
+ * install a fresh thread keyring directly to new credentials
*/
-void switch_uid_keyring(struct user_struct *new_user)
+int install_thread_keyring_to_cred(struct cred *new)
{
-#if 0 /* do nothing for now */
- struct key *old;
-
- /* switch to the new user's session keyring if we were running under
- * root's default session keyring */
- if (new_user->uid != 0 &&
- current->session_keyring == &root_session_keyring
- ) {
- atomic_inc(&new_user->session_keyring->usage);
-
- task_lock(current);
- old = current->session_keyring;
- current->session_keyring = new_user->session_keyring;
- task_unlock(current);
+ struct key *keyring;
- key_put(old);
- }
-#endif
+ keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
-} /* end switch_uid_keyring() */
+ new->thread_keyring = keyring;
+ return 0;
+}
-/*****************************************************************************/
/*
* install a fresh thread keyring, discarding the old one
*/
-int install_thread_keyring(struct task_struct *tsk)
+static int install_thread_keyring(void)
{
- struct key *keyring, *old;
- char buf[20];
+ struct cred *new;
int ret;
- sprintf(buf, "_tid.%u", tsk->pid);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
+ BUG_ON(new->thread_keyring);
+
+ ret = install_thread_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
}
- task_lock(tsk);
- old = tsk->thread_keyring;
- tsk->thread_keyring = keyring;
- task_unlock(tsk);
+ return commit_creds(new);
+}
- ret = 0;
+/*
+ * install a process keyring directly to a credentials struct
+ * - returns -EEXIST if there was already a process keyring, 0 if one installed,
+ * and other -ve on any other error
+ */
+int install_process_keyring_to_cred(struct cred *new)
+{
+ struct key *keyring;
+ int ret;
- key_put(old);
-error:
+ if (new->tgcred->process_keyring)
+ return -EEXIST;
+
+ keyring = keyring_alloc("_pid", new->uid, new->gid,
+ new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ spin_lock_irq(&new->tgcred->lock);
+ if (!new->tgcred->process_keyring) {
+ new->tgcred->process_keyring = keyring;
+ keyring = NULL;
+ ret = 0;
+ } else {
+ ret = -EEXIST;
+ }
+ spin_unlock_irq(&new->tgcred->lock);
+ key_put(keyring);
return ret;
+}
-} /* end install_thread_keyring() */
-
-/*****************************************************************************/
/*
* make sure a process keyring is installed
+ * - we
*/
-int install_process_keyring(struct task_struct *tsk)
+static int install_process_keyring(void)
{
- struct key *keyring;
- char buf[20];
+ struct cred *new;
int ret;
- might_sleep();
-
- if (!tsk->signal->process_keyring) {
- sprintf(buf, "_pid.%u", tsk->tgid);
-
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
- KEY_ALLOC_QUOTA_OVERRUN, NULL);
- if (IS_ERR(keyring)) {
- ret = PTR_ERR(keyring);
- goto error;
- }
-
- /* attach keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- if (!tsk->signal->process_keyring) {
- tsk->signal->process_keyring = keyring;
- keyring = NULL;
- }
- spin_unlock_irq(&tsk->sighand->siglock);
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
- key_put(keyring);
+ ret = install_process_keyring_to_cred(new);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret != -EEXIST ?: 0;
}
- ret = 0;
-error:
- return ret;
-
-} /* end install_process_keyring() */
+ return commit_creds(new);
+}
-/*****************************************************************************/
/*
- * install a session keyring, discarding the old one
- * - if a keyring is not supplied, an empty one is invented
+ * install a session keyring directly to a credentials struct
*/
-static int install_session_keyring(struct task_struct *tsk,
- struct key *keyring)
+static int install_session_keyring_to_cred(struct cred *cred,
+ struct key *keyring)
{
unsigned long flags;
struct key *old;
- char buf[20];
might_sleep();
/* create an empty session keyring */
if (!keyring) {
- sprintf(buf, "_ses.%u", tsk->tgid);
-
flags = KEY_ALLOC_QUOTA_OVERRUN;
- if (tsk->signal->session_keyring)
+ if (cred->tgcred->session_keyring)
flags = KEY_ALLOC_IN_QUOTA;
- keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
- flags, NULL);
+ keyring = keyring_alloc("_ses", cred->uid, cred->gid,
+ cred, flags, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
- }
- else {
+ } else {
atomic_inc(&keyring->usage);
}
/* install the keyring */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->session_keyring;
- rcu_assign_pointer(tsk->signal->session_keyring, keyring);
- spin_unlock_irq(&tsk->sighand->siglock);
+ spin_lock_irq(&cred->tgcred->lock);
+ old = cred->tgcred->session_keyring;
+ rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
+ spin_unlock_irq(&cred->tgcred->lock);
/* we're using RCU on the pointer, but there's no point synchronising
* on it if it didn't previously point to anything */
@@ -258,110 +250,29 @@ static int install_session_keyring(struct task_struct *tsk,
}
return 0;
+}
-} /* end install_session_keyring() */
-
-/*****************************************************************************/
-/*
- * copy the keys in a thread group for fork without CLONE_THREAD
- */
-int copy_thread_group_keys(struct task_struct *tsk)
-{
- key_check(current->thread_group->session_keyring);
- key_check(current->thread_group->process_keyring);
-
- /* no process keyring yet */
- tsk->signal->process_keyring = NULL;
-
- /* same session keyring */
- rcu_read_lock();
- tsk->signal->session_keyring =
- key_get(rcu_dereference(current->signal->session_keyring));
- rcu_read_unlock();
-
- return 0;
-
-} /* end copy_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * copy the keys for fork
- */
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
-{
- key_check(tsk->thread_keyring);
- key_check(tsk->request_key_auth);
-
- /* no thread keyring yet */
- tsk->thread_keyring = NULL;
-
- /* copy the request_key() authorisation for this thread */
- key_get(tsk->request_key_auth);
-
- return 0;
-
-} /* end copy_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of thread group keys upon thread group destruction
- */
-void exit_thread_group_keys(struct signal_struct *tg)
-{
- key_put(tg->session_keyring);
- key_put(tg->process_keyring);
-
-} /* end exit_thread_group_keys() */
-
-/*****************************************************************************/
-/*
- * dispose of per-thread keys upon thread exit
- */
-void exit_keys(struct task_struct *tsk)
-{
- key_put(tsk->thread_keyring);
- key_put(tsk->request_key_auth);
-
-} /* end exit_keys() */
-
-/*****************************************************************************/
/*
- * deal with execve()
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
*/
-int exec_keys(struct task_struct *tsk)
+static int install_session_keyring(struct key *keyring)
{
- struct key *old;
-
- /* newly exec'd tasks don't get a thread keyring */
- task_lock(tsk);
- old = tsk->thread_keyring;
- tsk->thread_keyring = NULL;
- task_unlock(tsk);
-
- key_put(old);
-
- /* discard the process keyring from a newly exec'd task */
- spin_lock_irq(&tsk->sighand->siglock);
- old = tsk->signal->process_keyring;
- tsk->signal->process_keyring = NULL;
- spin_unlock_irq(&tsk->sighand->siglock);
-
- key_put(old);
-
- return 0;
+ struct cred *new;
+ int ret;
-} /* end exec_keys() */
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
-/*****************************************************************************/
-/*
- * deal with SUID programs
- * - we might want to make this invent a new session keyring
- */
-int suid_keys(struct task_struct *tsk)
-{
- return 0;
+ ret = install_session_keyring_to_cred(new, NULL);
+ if (ret < 0) {
+ abort_creds(new);
+ return ret;
+ }
-} /* end suid_keys() */
+ return commit_creds(new);
+}
/*****************************************************************************/
/*
@@ -370,10 +281,11 @@ int suid_keys(struct task_struct *tsk)
void key_fsuid_changed(struct task_struct *tsk)
{
/* update the ownership of the thread keyring */
- if (tsk->thread_keyring) {
- down_write(&tsk->thread_keyring->sem);
- tsk->thread_keyring->uid = tsk->fsuid;
- up_write(&tsk->thread_keyring->sem);
+ BUG_ON(!tsk->cred);
+ if (tsk->cred->thread_keyring) {
+ down_write(&tsk->cred->thread_keyring->sem);
+ tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
+ up_write(&tsk->cred->thread_keyring->sem);
}
} /* end key_fsuid_changed() */
@@ -385,10 +297,11 @@ void key_fsuid_changed(struct task_struct *tsk)
void key_fsgid_changed(struct task_struct *tsk)
{
/* update the ownership of the thread keyring */
- if (tsk->thread_keyring) {
- down_write(&tsk->thread_keyring->sem);
- tsk->thread_keyring->gid = tsk->fsgid;
- up_write(&tsk->thread_keyring->sem);
+ BUG_ON(!tsk->cred);
+ if (tsk->cred->thread_keyring) {
+ down_write(&tsk->cred->thread_keyring->sem);
+ tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
+ up_write(&tsk->cred->thread_keyring->sem);
}
} /* end key_fsgid_changed() */
@@ -404,7 +317,7 @@ void key_fsgid_changed(struct task_struct *tsk)
key_ref_t search_process_keyrings(struct key_type *type,
const void *description,
key_match_func_t match,
- struct task_struct *context)
+ const struct cred *cred)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret, err;
@@ -423,10 +336,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
err = ERR_PTR(-EAGAIN);
/* search the thread keyring first */
- if (context->thread_keyring) {
+ if (cred->thread_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->thread_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->thread_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -444,10 +357,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
}
/* search the process keyring second */
- if (context->signal->process_keyring) {
+ if (cred->tgcred->process_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->signal->process_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->tgcred->process_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -465,13 +378,13 @@ key_ref_t search_process_keyrings(struct key_type *type,
}
/* search the session keyring */
- if (context->signal->session_keyring) {
+ if (cred->tgcred->session_keyring) {
rcu_read_lock();
key_ref = keyring_search_aux(
make_key_ref(rcu_dereference(
- context->signal->session_keyring),
+ cred->tgcred->session_keyring),
1),
- context, type, description, match);
+ cred, type, description, match);
rcu_read_unlock();
if (!IS_ERR(key_ref))
@@ -490,10 +403,10 @@ key_ref_t search_process_keyrings(struct key_type *type,
}
}
/* or search the user-session keyring */
- else if (context->user->session_keyring) {
+ else if (cred->user->session_keyring) {
key_ref = keyring_search_aux(
- make_key_ref(context->user->session_keyring, 1),
- context, type, description, match);
+ make_key_ref(cred->user->session_keyring, 1),
+ cred, type, description, match);
if (!IS_ERR(key_ref))
goto found;
@@ -514,20 +427,20 @@ key_ref_t search_process_keyrings(struct key_type *type,
* search the keyrings of the process mentioned there
* - we don't permit access to request_key auth keys via this method
*/
- if (context->request_key_auth &&
- context == current &&
+ if (cred->request_key_auth &&
+ cred == current_cred() &&
type != &key_type_request_key_auth
) {
/* defend against the auth key being revoked */
- down_read(&context->request_key_auth->sem);
+ down_read(&cred->request_key_auth->sem);
- if (key_validate(context->request_key_auth) == 0) {
- rka = context->request_key_auth->payload.data;
+ if (key_validate(cred->request_key_auth) == 0) {
+ rka = cred->request_key_auth->payload.data;
key_ref = search_process_keyrings(type, description,
- match, rka->context);
+ match, rka->cred);
- up_read(&context->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
if (!IS_ERR(key_ref))
goto found;
@@ -544,7 +457,7 @@ key_ref_t search_process_keyrings(struct key_type *type,
break;
}
} else {
- up_read(&context->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
}
}
@@ -572,93 +485,98 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
* - don't create special keyrings unless so requested
* - partially constructed keys aren't found unless requested
*/
-key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
- int create, int partial, key_perm_t perm)
+key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
+ key_perm_t perm)
{
- key_ref_t key_ref, skey_ref;
+ struct request_key_auth *rka;
+ const struct cred *cred;
struct key *key;
+ key_ref_t key_ref, skey_ref;
int ret;
- if (!context)
- context = current;
-
+try_again:
+ cred = get_current_cred();
key_ref = ERR_PTR(-ENOKEY);
switch (id) {
case KEY_SPEC_THREAD_KEYRING:
- if (!context->thread_keyring) {
+ if (!cred->thread_keyring) {
if (!create)
goto error;
- ret = install_thread_keyring(context);
+ ret = install_thread_keyring();
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
+ goto reget_creds;
}
- key = context->thread_keyring;
+ key = cred->thread_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_PROCESS_KEYRING:
- if (!context->signal->process_keyring) {
+ if (!cred->tgcred->process_keyring) {
if (!create)
goto error;
- ret = install_process_keyring(context);
+ ret = install_process_keyring();
if (ret < 0) {
key = ERR_PTR(ret);
goto error;
}
+ goto reget_creds;
}
- key = context->signal->process_keyring;
+ key = cred->tgcred->process_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_SESSION_KEYRING:
- if (!context->signal->session_keyring) {
+ if (!cred->tgcred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
- ret = install_user_keyrings(context);
+ ret = install_user_keyrings();
if (ret < 0)
goto error;
ret = install_session_keyring(
- context, context->user->session_keyring);
+ cred->user->session_keyring);
+
if (ret < 0)
goto error;
+ goto reget_creds;
}
rcu_read_lock();
- key = rcu_dereference(context->signal->session_keyring);
+ key = rcu_dereference(cred->tgcred->session_keyring);
atomic_inc(&key->usage);
rcu_read_unlock();
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_KEYRING:
- if (!context->user->uid_keyring) {
- ret = install_user_keyrings(context);
+ if (!cred->user->uid_keyring) {
+ ret = install_user_keyrings();
if (ret < 0)
goto error;
}
- key = context->user->uid_keyring;
+ key = cred->user->uid_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_SESSION_KEYRING:
- if (!context->user->session_keyring) {
- ret = install_user_keyrings(context);
+ if (!cred->user->session_keyring) {
+ ret = install_user_keyrings();
if (ret < 0)
goto error;
}
- key = context->user->session_keyring;
+ key = cred->user->session_keyring;
atomic_inc(&key->usage);
key_ref = make_key_ref(key, 1);
break;
@@ -669,7 +587,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
goto error;
case KEY_SPEC_REQKEY_AUTH_KEY:
- key = context->request_key_auth;
+ key = cred->request_key_auth;
if (!key)
goto error;
@@ -677,6 +595,25 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
key_ref = make_key_ref(key, 1);
break;
+ case KEY_SPEC_REQUESTOR_KEYRING:
+ if (!cred->request_key_auth)
+ goto error;
+
+ down_read(&cred->request_key_auth->sem);
+ if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
+ key_ref = ERR_PTR(-EKEYREVOKED);
+ key = NULL;
+ } else {
+ rka = cred->request_key_auth->payload.data;
+ key = rka->dest_keyring;
+ atomic_inc(&key->usage);
+ }
+ up_read(&cred->request_key_auth->sem);
+ if (!key)
+ goto error;
+ key_ref = make_key_ref(key, 1);
+ break;
+
default:
key_ref = ERR_PTR(-EINVAL);
if (id < 1)
@@ -693,7 +630,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
/* check to see if we possess the key */
skey_ref = search_process_keyrings(key->type, key,
lookup_user_key_possessed,
- current);
+ cred);
if (!IS_ERR(skey_ref)) {
key_put(key);
@@ -725,11 +662,12 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
goto invalid_key;
/* check the permissions */
- ret = key_task_permission(key_ref, context, perm);
+ ret = key_task_permission(key_ref, cred, perm);
if (ret < 0)
goto invalid_key;
error:
+ put_cred(cred);
return key_ref;
invalid_key:
@@ -737,6 +675,12 @@ invalid_key:
key_ref = ERR_PTR(ret);
goto error;
+ /* if we attempted to install a keyring, then it may have caused new
+ * creds to be installed */
+reget_creds:
+ put_cred(cred);
+ goto try_again;
+
} /* end lookup_user_key() */
/*****************************************************************************/
@@ -748,20 +692,33 @@ invalid_key:
*/
long join_session_keyring(const char *name)
{
- struct task_struct *tsk = current;
+ const struct cred *old;
+ struct cred *new;
struct key *keyring;
- long ret;
+ long ret, serial;
+
+ /* only permit this if there's a single thread in the thread group -
+ * this avoids us having to adjust the creds on all threads and risking
+ * ENOMEM */
+ if (!is_single_threaded(current))
+ return -EMLINK;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ old = current_cred();
/* if no name is provided, install an anonymous keyring */
if (!name) {
- ret = install_session_keyring(tsk, NULL);
+ ret = install_session_keyring_to_cred(new, NULL);
if (ret < 0)
goto error;
- rcu_read_lock();
- ret = rcu_dereference(tsk->signal->session_keyring)->serial;
- rcu_read_unlock();
- goto error;
+ serial = new->tgcred->session_keyring->serial;
+ ret = commit_creds(new);
+ if (ret == 0)
+ ret = serial;
+ goto okay;
}
/* allow the user to join or create a named keyring */
@@ -771,29 +728,33 @@ long join_session_keyring(const char *name)
keyring = find_keyring_by_name(name, false);
if (PTR_ERR(keyring) == -ENOKEY) {
/* not found - try and create a new one */
- keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
+ keyring = keyring_alloc(name, old->uid, old->gid, old,
KEY_ALLOC_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
- }
- else if (IS_ERR(keyring)) {
+ } else if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error2;
}
/* we've got a keyring - now to install it */
- ret = install_session_keyring(tsk, keyring);
+ ret = install_session_keyring_to_cred(new, keyring);
if (ret < 0)
goto error2;
+ commit_creds(new);
+ mutex_unlock(&key_session_mutex);
+
ret = keyring->serial;
key_put(keyring);
+okay:
+ return ret;
error2:
mutex_unlock(&key_session_mutex);
error:
+ abort_creds(new);
return ret;
-
-} /* end join_session_keyring() */
+}
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index abea08f87fe2..0e04f72ef2d4 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -19,6 +19,8 @@
#include <linux/slab.h>
#include "internal.h"
+#define key_negative_timeout 60 /* default timeout on a negative key's existence */
+
/*
* wait_on_bit() sleep function for uninterruptible waiting
*/
@@ -64,7 +66,7 @@ static int call_sbin_request_key(struct key_construction *cons,
const char *op,
void *aux)
{
- struct task_struct *tsk = current;
+ const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
struct key *key = cons->key, *authkey = cons->authkey, *keyring;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
@@ -74,15 +76,17 @@ static int call_sbin_request_key(struct key_construction *cons,
kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
- ret = install_user_keyrings(tsk);
+ ret = install_user_keyrings();
if (ret < 0)
goto error_alloc;
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
- keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current,
+ cred = get_current_cred();
+ keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ put_cred(cred);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto error_alloc;
@@ -94,29 +98,24 @@ static int call_sbin_request_key(struct key_construction *cons,
goto error_link;
/* record the UID and GID */
- sprintf(uid_str, "%d", current->fsuid);
- sprintf(gid_str, "%d", current->fsgid);
+ sprintf(uid_str, "%d", cred->fsuid);
+ sprintf(gid_str, "%d", cred->fsgid);
/* we say which key is under construction */
sprintf(key_str, "%d", key->serial);
/* we specify the process's default keyrings */
sprintf(keyring_str[0], "%d",
- tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+ cred->thread_keyring ? cred->thread_keyring->serial : 0);
prkey = 0;
- if (tsk->signal->process_keyring)
- prkey = tsk->signal->process_keyring->serial;
-
- sprintf(keyring_str[1], "%d", prkey);
+ if (cred->tgcred->process_keyring)
+ prkey = cred->tgcred->process_keyring->serial;
- if (tsk->signal->session_keyring) {
- rcu_read_lock();
- sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
- rcu_read_unlock();
- } else {
- sskey = tsk->user->session_keyring->serial;
- }
+ if (cred->tgcred->session_keyring)
+ sskey = rcu_dereference(cred->tgcred->session_keyring)->serial;
+ else
+ sskey = cred->user->session_keyring->serial;
sprintf(keyring_str[2], "%d", sskey);
@@ -157,8 +156,8 @@ error_link:
key_put(keyring);
error_alloc:
- kleave(" = %d", ret);
complete_request_key(cons, ret);
+ kleave(" = %d", ret);
return ret;
}
@@ -167,7 +166,8 @@ error_alloc:
* - we ignore program failure and go on key status instead
*/
static int construct_key(struct key *key, const void *callout_info,
- size_t callout_len, void *aux)
+ size_t callout_len, void *aux,
+ struct key *dest_keyring)
{
struct key_construction *cons;
request_key_actor_t actor;
@@ -181,7 +181,8 @@ static int construct_key(struct key *key, const void *callout_info,
return -ENOMEM;
/* allocate an authorisation key */
- authkey = request_key_auth_new(key, callout_info, callout_len);
+ authkey = request_key_auth_new(key, callout_info, callout_len,
+ dest_keyring);
if (IS_ERR(authkey)) {
kfree(cons);
ret = PTR_ERR(authkey);
@@ -209,46 +210,67 @@ static int construct_key(struct key *key, const void *callout_info,
}
/*
- * link a key to the appropriate destination keyring
- * - the caller must hold a write lock on the destination keyring
+ * get the appropriate destination keyring for the request
+ * - we return whatever keyring we select with an extra reference upon it which
+ * the caller must release
*/
-static void construct_key_make_link(struct key *key, struct key *dest_keyring)
+static void construct_get_dest_keyring(struct key **_dest_keyring)
{
- struct task_struct *tsk = current;
- struct key *drop = NULL;
+ struct request_key_auth *rka;
+ const struct cred *cred = current_cred();
+ struct key *dest_keyring = *_dest_keyring, *authkey;
- kenter("{%d},%p", key->serial, dest_keyring);
+ kenter("%p", dest_keyring);
/* find the appropriate keyring */
- if (!dest_keyring) {
- switch (tsk->jit_keyring) {
+ if (dest_keyring) {
+ /* the caller supplied one */
+ key_get(dest_keyring);
+ } else {
+ /* use a default keyring; falling through the cases until we
+ * find one that we actually have */
+ switch (cred->jit_keyring) {
case KEY_REQKEY_DEFL_DEFAULT:
+ case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
+ if (cred->request_key_auth) {
+ authkey = cred->request_key_auth;
+ down_read(&authkey->sem);
+ rka = authkey->payload.data;
+ if (!test_bit(KEY_FLAG_REVOKED,
+ &authkey->flags))
+ dest_keyring =
+ key_get(rka->dest_keyring);
+ up_read(&authkey->sem);
+ if (dest_keyring)
+ break;
+ }
+
case KEY_REQKEY_DEFL_THREAD_KEYRING:
- dest_keyring = tsk->thread_keyring;
+ dest_keyring = key_get(cred->thread_keyring);
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- dest_keyring = tsk->signal->process_keyring;
+ dest_keyring = key_get(cred->tgcred->process_keyring);
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_SESSION_KEYRING:
rcu_read_lock();
dest_keyring = key_get(
- rcu_dereference(tsk->signal->session_keyring));
+ rcu_dereference(cred->tgcred->session_keyring));
rcu_read_unlock();
- drop = dest_keyring;
if (dest_keyring)
break;
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- dest_keyring = tsk->user->session_keyring;
+ dest_keyring =
+ key_get(cred->user->session_keyring);
break;
case KEY_REQKEY_DEFL_USER_KEYRING:
- dest_keyring = tsk->user->uid_keyring;
+ dest_keyring = key_get(cred->user->uid_keyring);
break;
case KEY_REQKEY_DEFL_GROUP_KEYRING:
@@ -257,10 +279,9 @@ static void construct_key_make_link(struct key *key, struct key *dest_keyring)
}
}
- /* and attach the key to it */
- __key_link(dest_keyring, key);
- key_put(drop);
- kleave("");
+ *_dest_keyring = dest_keyring;
+ kleave(" [dk %d]", key_serial(dest_keyring));
+ return;
}
/*
@@ -275,6 +296,7 @@ static int construct_alloc_key(struct key_type *type,
struct key_user *user,
struct key **_key)
{
+ const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
@@ -282,33 +304,28 @@ static int construct_alloc_key(struct key_type *type,
mutex_lock(&user->cons_lock);
- key = key_alloc(type, description,
- current->fsuid, current->fsgid, current, KEY_POS_ALL,
- flags);
+ key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
+ KEY_POS_ALL, flags);
if (IS_ERR(key))
goto alloc_failed;
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
- if (dest_keyring)
- down_write(&dest_keyring->sem);
+ down_write(&dest_keyring->sem);
/* attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we
* waited for locks */
mutex_lock(&key_construction_mutex);
- key_ref = search_process_keyrings(type, description, type->match,
- current);
+ key_ref = search_process_keyrings(type, description, type->match, cred);
if (!IS_ERR(key_ref))
goto key_already_present;
- if (dest_keyring)
- construct_key_make_link(key, dest_keyring);
+ __key_link(dest_keyring, key);
mutex_unlock(&key_construction_mutex);
- if (dest_keyring)
- up_write(&dest_keyring->sem);
+ up_write(&dest_keyring->sem);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
@@ -346,25 +363,36 @@ static struct key *construct_key_and_link(struct key_type *type,
struct key *key;
int ret;
- user = key_user_lookup(current->fsuid);
+ kenter("");
+
+ user = key_user_lookup(current_fsuid());
if (!user)
return ERR_PTR(-ENOMEM);
+ construct_get_dest_keyring(&dest_keyring);
+
ret = construct_alloc_key(type, description, dest_keyring, flags, user,
&key);
key_user_put(user);
if (ret == 0) {
- ret = construct_key(key, callout_info, callout_len, aux);
- if (ret < 0)
+ ret = construct_key(key, callout_info, callout_len, aux,
+ dest_keyring);
+ if (ret < 0) {
+ kdebug("cons failed");
goto construction_failed;
+ }
}
+ key_put(dest_keyring);
+ kleave(" = key %d", key_serial(key));
return key;
construction_failed:
key_negate_and_link(key, key_negative_timeout, NULL, NULL);
key_put(key);
+ key_put(dest_keyring);
+ kleave(" = %d", ret);
return ERR_PTR(ret);
}
@@ -383,6 +411,7 @@ struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring,
unsigned long flags)
{
+ const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
@@ -392,7 +421,7 @@ struct key *request_key_and_link(struct key_type *type,
/* search all the process keyrings for a key */
key_ref = search_process_keyrings(type, description, type->match,
- current);
+ cred);
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index bd237b0a6331..86747151ee5b 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -105,9 +105,9 @@ static void request_key_auth_revoke(struct key *key)
kenter("{%d}", key->serial);
- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
+ if (rka->cred) {
+ put_cred(rka->cred);
+ rka->cred = NULL;
}
} /* end request_key_auth_revoke() */
@@ -122,12 +122,13 @@ static void request_key_auth_destroy(struct key *key)
kenter("{%d}", key->serial);
- if (rka->context) {
- put_task_struct(rka->context);
- rka->context = NULL;
+ if (rka->cred) {
+ put_cred(rka->cred);
+ rka->cred = NULL;
}
key_put(rka->target_key);
+ key_put(rka->dest_keyring);
kfree(rka->callout_info);
kfree(rka);
@@ -139,9 +140,10 @@ static void request_key_auth_destroy(struct key *key)
* access to the caller's security data
*/
struct key *request_key_auth_new(struct key *target, const void *callout_info,
- size_t callout_len)
+ size_t callout_len, struct key *dest_keyring)
{
struct request_key_auth *rka, *irka;
+ const struct cred *cred = current->cred;
struct key *authkey = NULL;
char desc[20];
int ret;
@@ -163,31 +165,29 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
/* see if the calling process is already servicing the key request of
* another process */
- if (current->request_key_auth) {
+ if (cred->request_key_auth) {
/* it is - use that instantiation context here too */
- down_read(&current->request_key_auth->sem);
+ down_read(&cred->request_key_auth->sem);
/* if the auth key has been revoked, then the key we're
* servicing is already instantiated */
- if (test_bit(KEY_FLAG_REVOKED,
- &current->request_key_auth->flags))
+ if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
goto auth_key_revoked;
- irka = current->request_key_auth->payload.data;
- rka->context = irka->context;
+ irka = cred->request_key_auth->payload.data;
+ rka->cred = get_cred(irka->cred);
rka->pid = irka->pid;
- get_task_struct(rka->context);
- up_read(&current->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
}
else {
/* it isn't - use this process as the context */
- rka->context = current;
+ rka->cred = get_cred(cred);
rka->pid = current->pid;
- get_task_struct(rka->context);
}
rka->target_key = key_get(target);
+ rka->dest_keyring = key_get(dest_keyring);
memcpy(rka->callout_info, callout_info, callout_len);
rka->callout_len = callout_len;
@@ -195,7 +195,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
sprintf(desc, "%x", target->serial);
authkey = key_alloc(&key_type_request_key_auth, desc,
- current->fsuid, current->fsgid, current,
+ cred->fsuid, cred->fsgid, cred,
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(authkey)) {
@@ -203,16 +203,16 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
goto error_alloc;
}
- /* construct and attach to the keyring */
+ /* construct the auth key */
ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
if (ret < 0)
goto error_inst;
- kleave(" = {%d}", authkey->serial);
+ kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
return authkey;
auth_key_revoked:
- up_read(&current->request_key_auth->sem);
+ up_read(&cred->request_key_auth->sem);
kfree(rka->callout_info);
kfree(rka);
kleave("= -EKEYREVOKED");
@@ -223,6 +223,7 @@ error_inst:
key_put(authkey);
error_alloc:
key_put(rka->target_key);
+ key_put(rka->dest_keyring);
kfree(rka->callout_info);
kfree(rka);
kleave("= %d", ret);
@@ -254,6 +255,7 @@ static int key_get_instantiation_authkey_match(const struct key *key,
*/
struct key *key_get_instantiation_authkey(key_serial_t target_id)
{
+ const struct cred *cred = current_cred();
struct key *authkey;
key_ref_t authkey_ref;
@@ -261,7 +263,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
&key_type_request_key_auth,
(void *) (unsigned long) target_id,
key_get_instantiation_authkey_match,
- current);
+ cred);
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
diff --git a/security/root_plug.c b/security/root_plug.c
index c3f68b5b372d..40fb4f15e27b 100644
--- a/security/root_plug.c
+++ b/security/root_plug.c
@@ -55,9 +55,9 @@ static int rootplug_bprm_check_security (struct linux_binprm *bprm)
struct usb_device *dev;
root_dbg("file %s, e_uid = %d, e_gid = %d\n",
- bprm->filename, bprm->e_uid, bprm->e_gid);
+ bprm->filename, bprm->cred->euid, bprm->cred->egid);
- if (bprm->e_gid == 0) {
+ if (bprm->cred->egid == 0) {
dev = usb_find_device(vendor_id, product_id);
if (!dev) {
root_dbg("e_gid = 0, and device not found, "
@@ -75,15 +75,12 @@ static struct security_operations rootplug_security_ops = {
.ptrace_may_access = cap_ptrace_may_access,
.ptrace_traceme = cap_ptrace_traceme,
.capget = cap_capget,
- .capset_check = cap_capset_check,
- .capset_set = cap_capset_set,
+ .capset = cap_capset,
.capable = cap_capable,
- .bprm_apply_creds = cap_bprm_apply_creds,
- .bprm_set_security = cap_bprm_set_security,
+ .bprm_set_creds = cap_bprm_set_creds,
- .task_post_setuid = cap_task_post_setuid,
- .task_reparent_to_init = cap_task_reparent_to_init,
+ .task_fix_setuid = cap_task_fix_setuid,
.task_prctl = cap_task_prctl,
.bprm_check_security = rootplug_bprm_check_security,
diff --git a/security/security.c b/security/security.c
index c0acfa7177e5..d85dbb37c972 100644
--- a/security/security.c
+++ b/security/security.c
@@ -145,25 +145,23 @@ int security_capget(struct task_struct *target,
return security_ops->capget(target, effective, inheritable, permitted);
}
-int security_capset_check(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
+int security_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
- return security_ops->capset_check(target, effective, inheritable, permitted);
+ return security_ops->capset(new, old,
+ effective, inheritable, permitted);
}
-void security_capset_set(struct task_struct *target,
- kernel_cap_t *effective,
- kernel_cap_t *inheritable,
- kernel_cap_t *permitted)
+int security_capable(struct task_struct *tsk, int cap)
{
- security_ops->capset_set(target, effective, inheritable, permitted);
+ return security_ops->capable(tsk, cap, SECURITY_CAP_AUDIT);
}
-int security_capable(struct task_struct *tsk, int cap)
+int security_capable_noaudit(struct task_struct *tsk, int cap)
{
- return security_ops->capable(tsk, cap);
+ return security_ops->capable(tsk, cap, SECURITY_CAP_NOAUDIT);
}
int security_acct(struct file *file)
@@ -215,34 +213,24 @@ int security_vm_enough_memory_kern(long pages)
return security_ops->vm_enough_memory(current->mm, pages);
}
-int security_bprm_alloc(struct linux_binprm *bprm)
-{
- return security_ops->bprm_alloc_security(bprm);
-}
-
-void security_bprm_free(struct linux_binprm *bprm)
-{
- security_ops->bprm_free_security(bprm);
-}
-
-void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+int security_bprm_set_creds(struct linux_binprm *bprm)
{
- security_ops->bprm_apply_creds(bprm, unsafe);
+ return security_ops->bprm_set_creds(bprm);
}
-void security_bprm_post_apply_creds(struct linux_binprm *bprm)
+int security_bprm_check(struct linux_binprm *bprm)
{
- security_ops->bprm_post_apply_creds(bprm);
+ return security_ops->bprm_check_security(bprm);
}
-int security_bprm_set(struct linux_binprm *bprm)
+void security_bprm_committing_creds(struct linux_binprm *bprm)
{
- return security_ops->bprm_set_security(bprm);
+ security_ops->bprm_committing_creds(bprm);
}
-int security_bprm_check(struct linux_binprm *bprm)
+void security_bprm_committed_creds(struct linux_binprm *bprm)
{
- return security_ops->bprm_check_security(bprm);
+ security_ops->bprm_committed_creds(bprm);
}
int security_bprm_secureexec(struct linux_binprm *bprm)
@@ -266,9 +254,9 @@ int security_sb_copy_data(char *orig, char *copy)
}
EXPORT_SYMBOL(security_sb_copy_data);
-int security_sb_kern_mount(struct super_block *sb, void *data)
+int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
- return security_ops->sb_kern_mount(sb, data);
+ return security_ops->sb_kern_mount(sb, flags, data);
}
int security_sb_show_options(struct seq_file *m, struct super_block *sb)
@@ -603,9 +591,9 @@ int security_file_receive(struct file *file)
return security_ops->file_receive(file);
}
-int security_dentry_open(struct file *file)
+int security_dentry_open(struct file *file, const struct cred *cred)
{
- return security_ops->dentry_open(file);
+ return security_ops->dentry_open(file, cred);
}
int security_task_create(unsigned long clone_flags)
@@ -613,14 +601,29 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(clone_flags);
}
-int security_task_alloc(struct task_struct *p)
+void security_cred_free(struct cred *cred)
{
- return security_ops->task_alloc_security(p);
+ security_ops->cred_free(cred);
}
-void security_task_free(struct task_struct *p)
+int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
{
- security_ops->task_free_security(p);
+ return security_ops->cred_prepare(new, old, gfp);
+}
+
+void security_commit_creds(struct cred *new, const struct cred *old)
+{
+ security_ops->cred_commit(new, old);
+}
+
+int security_kernel_act_as(struct cred *new, u32 secid)
+{
+ return security_ops->kernel_act_as(new, secid);
+}
+
+int security_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+ return security_ops->kernel_create_files_as(new, inode);
}
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -628,10 +631,10 @@ int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
return security_ops->task_setuid(id0, id1, id2, flags);
}
-int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
- uid_t old_suid, int flags)
+int security_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags)
{
- return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, flags);
+ return security_ops->task_fix_setuid(new, old, flags);
}
int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -713,14 +716,9 @@ int security_task_wait(struct task_struct *p)
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5, long *rc_p)
-{
- return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
-}
-
-void security_task_reparent_to_init(struct task_struct *p)
+ unsigned long arg4, unsigned long arg5)
{
- security_ops->task_reparent_to_init(p);
+ return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
}
void security_task_to_inode(struct task_struct *p, struct inode *inode)
@@ -1120,9 +1118,10 @@ EXPORT_SYMBOL(security_skb_classify_flow);
#ifdef CONFIG_KEYS
-int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags)
+int security_key_alloc(struct key *key, const struct cred *cred,
+ unsigned long flags)
{
- return security_ops->key_alloc(key, tsk, flags);
+ return security_ops->key_alloc(key, cred, flags);
}
void security_key_free(struct key *key)
@@ -1131,9 +1130,9 @@ void security_key_free(struct key *key)
}
int security_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm)
+ const struct cred *cred, key_perm_t perm)
{
- return security_ops->key_permission(key_ref, context, perm);
+ return security_ops->key_permission(key_ref, cred, perm);
}
int security_key_getsecurity(struct key *key, char **_buffer)
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index cb30c7e350b3..d43bd6baeeaa 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -495,7 +495,7 @@ static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
- audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr));
+ audit_log_format(ab, " %s=%pI6", name1, addr);
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
@@ -504,7 +504,7 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
__be16 port, char *name1, char *name2)
{
if (addr)
- audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr));
+ audit_log_format(ab, " %s=%pI4", name1, &addr);
if (port)
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
index 64af2d3409ef..c73aeaa008e8 100644
--- a/security/selinux/exports.c
+++ b/security/selinux/exports.c
@@ -39,9 +39,13 @@ EXPORT_SYMBOL_GPL(selinux_string_to_sid);
int selinux_secmark_relabel_packet_permission(u32 sid)
{
if (selinux_enabled) {
- struct task_security_struct *tsec = current->security;
+ const struct task_security_struct *__tsec;
+ u32 tsid;
- return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+ __tsec = current_security();
+ tsid = __tsec->sid;
+
+ return avc_has_perm(tsid, sid, SECCLASS_PACKET,
PACKET__RELABELTO, NULL);
}
return 0;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index f85597a4d733..dbeaa783b2a9 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -156,33 +156,62 @@ static int selinux_secmark_enabled(void)
return (atomic_read(&selinux_secmark_refcount) > 0);
}
-/* Allocate and free functions for each kind of security blob. */
-
-static int task_alloc_security(struct task_struct *task)
+/*
+ * initialise the security for the init task
+ */
+static void cred_init_security(void)
{
+ struct cred *cred = (struct cred *) current->real_cred;
struct task_security_struct *tsec;
tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
- return -ENOMEM;
+ panic("SELinux: Failed to initialize initial task.\n");
- tsec->osid = tsec->sid = SECINITSID_UNLABELED;
- task->security = tsec;
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ cred->security = tsec;
+}
- return 0;
+/*
+ * get the security ID of a set of credentials
+ */
+static inline u32 cred_sid(const struct cred *cred)
+{
+ const struct task_security_struct *tsec;
+
+ tsec = cred->security;
+ return tsec->sid;
}
-static void task_free_security(struct task_struct *task)
+/*
+ * get the objective security ID of a task
+ */
+static inline u32 task_sid(const struct task_struct *task)
{
- struct task_security_struct *tsec = task->security;
- task->security = NULL;
- kfree(tsec);
+ u32 sid;
+
+ rcu_read_lock();
+ sid = cred_sid(__task_cred(task));
+ rcu_read_unlock();
+ return sid;
+}
+
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+ const struct task_security_struct *tsec = current_cred()->security;
+
+ return tsec->sid;
}
+/* Allocate and free functions for each kind of security blob. */
+
static int inode_alloc_security(struct inode *inode)
{
- struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec;
+ u32 sid = current_sid();
isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
if (!isec)
@@ -193,7 +222,7 @@ static int inode_alloc_security(struct inode *inode)
isec->inode = inode;
isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE;
- isec->task_sid = tsec->sid;
+ isec->task_sid = sid;
inode->i_security = isec;
return 0;
@@ -215,15 +244,15 @@ static void inode_free_security(struct inode *inode)
static int file_alloc_security(struct file *file)
{
- struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec;
+ u32 sid = current_sid();
fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
if (!fsec)
return -ENOMEM;
- fsec->sid = tsec->sid;
- fsec->fown_sid = tsec->sid;
+ fsec->sid = sid;
+ fsec->fown_sid = sid;
file->f_security = fsec;
return 0;
@@ -338,8 +367,9 @@ static const match_table_t tokens = {
static int may_context_mount_sb_relabel(u32 sid,
struct superblock_security_struct *sbsec,
- struct task_security_struct *tsec)
+ const struct cred *cred)
{
+ const struct task_security_struct *tsec = cred->security;
int rc;
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -354,8 +384,9 @@ static int may_context_mount_sb_relabel(u32 sid,
static int may_context_mount_inode_relabel(u32 sid,
struct superblock_security_struct *sbsec,
- struct task_security_struct *tsec)
+ const struct cred *cred)
{
+ const struct task_security_struct *tsec = cred->security;
int rc;
rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
FILESYSTEM__RELABELFROM, NULL);
@@ -553,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
static int selinux_set_mnt_opts(struct super_block *sb,
struct security_mnt_opts *opts)
{
+ const struct cred *cred = current_cred();
int rc = 0, i;
- struct task_security_struct *tsec = current->security;
struct superblock_security_struct *sbsec = sb->s_security;
const char *name = sb->s_type->name;
struct inode *inode = sbsec->sb->s_root->d_inode;
@@ -671,7 +702,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
sbsec->proc = 1;
/* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
__func__, sb->s_type->name, rc);
@@ -680,8 +711,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
/* sets the context of the superblock for the fs being mounted. */
if (fscontext_sid) {
-
- rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
if (rc)
goto out;
@@ -695,12 +725,14 @@ static int selinux_set_mnt_opts(struct super_block *sb,
*/
if (context_sid) {
if (!fscontext_sid) {
- rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+ rc = may_context_mount_sb_relabel(context_sid, sbsec,
+ cred);
if (rc)
goto out;
sbsec->sid = context_sid;
} else {
- rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(context_sid, sbsec,
+ cred);
if (rc)
goto out;
}
@@ -712,7 +744,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
if (rootcontext_sid) {
- rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+ rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec,
+ cred);
if (rc)
goto out;
@@ -730,7 +763,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (defcontext_sid != sbsec->def_sid) {
rc = may_context_mount_inode_relabel(defcontext_sid,
- sbsec, tsec);
+ sbsec, cred);
if (rc)
goto out;
}
@@ -1345,18 +1378,53 @@ static inline u32 signal_to_av(int sig)
return perm;
}
-/* Check permission betweeen a pair of tasks, e.g. signal checks,
- fork check, ptrace check, etc. */
-static int task_has_perm(struct task_struct *tsk1,
- struct task_struct *tsk2,
+/*
+ * Check permission between a pair of credentials
+ * fork check, ptrace check, etc.
+ */
+static int cred_has_perm(const struct cred *actor,
+ const struct cred *target,
u32 perms)
{
- struct task_security_struct *tsec1, *tsec2;
+ u32 asid = cred_sid(actor), tsid = cred_sid(target);
- tsec1 = tsk1->security;
- tsec2 = tsk2->security;
- return avc_has_perm(tsec1->sid, tsec2->sid,
- SECCLASS_PROCESS, perms, NULL);
+ return avc_has_perm(asid, tsid, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between a pair of tasks, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * tsk1 is the actor and tsk2 is the target
+ * - this uses the default subjective creds of tsk1
+ */
+static int task_has_perm(const struct task_struct *tsk1,
+ const struct task_struct *tsk2,
+ u32 perms)
+{
+ const struct task_security_struct *__tsec1, *__tsec2;
+ u32 sid1, sid2;
+
+ rcu_read_lock();
+ __tsec1 = __task_cred(tsk1)->security; sid1 = __tsec1->sid;
+ __tsec2 = __task_cred(tsk2)->security; sid2 = __tsec2->sid;
+ rcu_read_unlock();
+ return avc_has_perm(sid1, sid2, SECCLASS_PROCESS, perms, NULL);
+}
+
+/*
+ * Check permission between current and another task, e.g. signal checks,
+ * fork check, ptrace check, etc.
+ * current is the actor and tsk2 is the target
+ * - this uses current's subjective creds
+ */
+static int current_has_perm(const struct task_struct *tsk,
+ u32 perms)
+{
+ u32 sid, tsid;
+
+ sid = current_sid();
+ tsid = task_sid(tsk);
+ return avc_has_perm(sid, tsid, SECCLASS_PROCESS, perms, NULL);
}
#if CAP_LAST_CAP > 63
@@ -1365,14 +1433,14 @@ static int task_has_perm(struct task_struct *tsk1,
/* Check whether a task is allowed to use a capability. */
static int task_has_capability(struct task_struct *tsk,
- int cap)
+ int cap, int audit)
{
- struct task_security_struct *tsec;
struct avc_audit_data ad;
+ struct av_decision avd;
u16 sclass;
+ u32 sid = task_sid(tsk);
u32 av = CAP_TO_MASK(cap);
-
- tsec = tsk->security;
+ int rc;
AVC_AUDIT_DATA_INIT(&ad, CAP);
ad.tsk = tsk;
@@ -1390,37 +1458,39 @@ static int task_has_capability(struct task_struct *tsk,
"SELinux: out of range capability %d\n", cap);
BUG();
}
- return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
+
+ rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
+ if (audit == SECURITY_CAP_AUDIT)
+ avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+ return rc;
}
/* Check whether a task is allowed to use a system operation. */
static int task_has_system(struct task_struct *tsk,
u32 perms)
{
- struct task_security_struct *tsec;
-
- tsec = tsk->security;
+ u32 sid = task_sid(tsk);
- return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+ return avc_has_perm(sid, SECINITSID_KERNEL,
SECCLASS_SYSTEM, perms, NULL);
}
/* Check whether a task has a particular permission to an inode.
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
-static int inode_has_perm(struct task_struct *tsk,
+static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
struct avc_audit_data *adp)
{
- struct task_security_struct *tsec;
struct inode_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid;
if (unlikely(IS_PRIVATE(inode)))
return 0;
- tsec = tsk->security;
+ sid = cred_sid(cred);
isec = inode->i_security;
if (!adp) {
@@ -1429,23 +1499,24 @@ static int inode_has_perm(struct task_struct *tsk,
ad.u.fs.inode = inode;
}
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+ return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
the dentry to help the auditing code to more easily generate the
pathname if needed. */
-static inline int dentry_has_perm(struct task_struct *tsk,
+static inline int dentry_has_perm(const struct cred *cred,
struct vfsmount *mnt,
struct dentry *dentry,
u32 av)
{
struct inode *inode = dentry->d_inode;
struct avc_audit_data ad;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.mnt = mnt;
ad.u.fs.path.dentry = dentry;
- return inode_has_perm(tsk, inode, av, &ad);
+ return inode_has_perm(cred, inode, av, &ad);
}
/* Check whether a task can use an open file descriptor to
@@ -1456,33 +1527,35 @@ static inline int dentry_has_perm(struct task_struct *tsk,
has the same SID as the process. If av is zero, then
access to the file is not checked, e.g. for cases
where only the descriptor is affected like seek. */
-static int file_has_perm(struct task_struct *tsk,
- struct file *file,
- u32 av)
+static int file_has_perm(const struct cred *cred,
+ struct file *file,
+ u32 av)
{
- struct task_security_struct *tsec = tsk->security;
struct file_security_struct *fsec = file->f_security;
struct inode *inode = file->f_path.dentry->d_inode;
struct avc_audit_data ad;
+ u32 sid = cred_sid(cred);
int rc;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path = file->f_path;
- if (tsec->sid != fsec->sid) {
- rc = avc_has_perm(tsec->sid, fsec->sid,
+ if (sid != fsec->sid) {
+ rc = avc_has_perm(sid, fsec->sid,
SECCLASS_FD,
FD__USE,
&ad);
if (rc)
- return rc;
+ goto out;
}
/* av is zero if only checking access to the descriptor. */
+ rc = 0;
if (av)
- return inode_has_perm(tsk, inode, av, &ad);
+ rc = inode_has_perm(cred, inode, av, &ad);
- return 0;
+out:
+ return rc;
}
/* Check whether a task can create a file. */
@@ -1490,36 +1563,36 @@ static int may_create(struct inode *dir,
struct dentry *dentry,
u16 tclass)
{
- struct task_security_struct *tsec;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
- u32 newsid;
+ u32 sid, newsid;
struct avc_audit_data ad;
int rc;
- tsec = current->security;
dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
+ sid = tsec->sid;
+ newsid = tsec->create_sid;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
DIR__ADD_NAME | DIR__SEARCH,
&ad);
if (rc)
return rc;
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
- &newsid);
+ if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
if (rc)
return rc;
}
- rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+ rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
return rc;
@@ -1532,11 +1605,9 @@ static int may_create(struct inode *dir,
static int may_create_key(u32 ksid,
struct task_struct *ctx)
{
- struct task_security_struct *tsec;
-
- tsec = ctx->security;
+ u32 sid = task_sid(ctx);
- return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+ return avc_has_perm(sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
}
#define MAY_LINK 0
@@ -1549,13 +1620,12 @@ static int may_link(struct inode *dir,
int kind)
{
- struct task_security_struct *tsec;
struct inode_security_struct *dsec, *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
u32 av;
int rc;
- tsec = current->security;
dsec = dir->i_security;
isec = dentry->d_inode->i_security;
@@ -1564,7 +1634,7 @@ static int may_link(struct inode *dir,
av = DIR__SEARCH;
av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
- rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+ rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
@@ -1584,7 +1654,7 @@ static int may_link(struct inode *dir,
return 0;
}
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+ rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad);
return rc;
}
@@ -1593,14 +1663,13 @@ static inline int may_rename(struct inode *old_dir,
struct inode *new_dir,
struct dentry *new_dentry)
{
- struct task_security_struct *tsec;
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
u32 av;
int old_is_dir, new_is_dir;
int rc;
- tsec = current->security;
old_dsec = old_dir->i_security;
old_isec = old_dentry->d_inode->i_security;
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -1609,16 +1678,16 @@ static inline int may_rename(struct inode *old_dir,
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = old_dentry;
- rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+ rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
DIR__REMOVE_NAME | DIR__SEARCH, &ad);
if (rc)
return rc;
- rc = avc_has_perm(tsec->sid, old_isec->sid,
+ rc = avc_has_perm(sid, old_isec->sid,
old_isec->sclass, FILE__RENAME, &ad);
if (rc)
return rc;
if (old_is_dir && new_dir != old_dir) {
- rc = avc_has_perm(tsec->sid, old_isec->sid,
+ rc = avc_has_perm(sid, old_isec->sid,
old_isec->sclass, DIR__REPARENT, &ad);
if (rc)
return rc;
@@ -1628,13 +1697,13 @@ static inline int may_rename(struct inode *old_dir,
av = DIR__ADD_NAME | DIR__SEARCH;
if (new_dentry->d_inode)
av |= DIR__REMOVE_NAME;
- rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+ rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
new_isec = new_dentry->d_inode->i_security;
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
- rc = avc_has_perm(tsec->sid, new_isec->sid,
+ rc = avc_has_perm(sid, new_isec->sid,
new_isec->sclass,
(new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
if (rc)
@@ -1645,18 +1714,16 @@ static inline int may_rename(struct inode *old_dir,
}
/* Check whether a task can perform a filesystem operation. */
-static int superblock_has_perm(struct task_struct *tsk,
+static int superblock_has_perm(const struct cred *cred,
struct super_block *sb,
u32 perms,
struct avc_audit_data *ad)
{
- struct task_security_struct *tsec;
struct superblock_security_struct *sbsec;
+ u32 sid = cred_sid(cred);
- tsec = tsk->security;
sbsec = sb->s_security;
- return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
- perms, ad);
+ return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad);
}
/* Convert a Linux mode and permission mask to an access vector. */
@@ -1687,15 +1754,39 @@ static inline u32 file_mask_to_av(int mode, int mask)
return av;
}
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+ u32 av = 0;
+
+ if (file->f_mode & FMODE_READ)
+ av |= FILE__READ;
+ if (file->f_mode & FMODE_WRITE) {
+ if (file->f_flags & O_APPEND)
+ av |= FILE__APPEND;
+ else
+ av |= FILE__WRITE;
+ }
+ if (!av) {
+ /*
+ * Special file opened with flags 3 for ioctl-only use.
+ */
+ av = FILE__IOCTL;
+ }
+
+ return av;
+}
+
/*
- * Convert a file mask to an access vector and include the correct open
+ * Convert a file to an access vector and include the correct open
* open permission.
*/
-static inline u32 open_file_mask_to_av(int mode, int mask)
+static inline u32 open_file_to_av(struct file *file)
{
- u32 av = file_mask_to_av(mode, mask);
+ u32 av = file_to_av(file);
if (selinux_policycap_openperm) {
+ mode_t mode = file->f_path.dentry->d_inode->i_mode;
/*
* lnk files and socks do not really have an 'open'
*/
@@ -1711,31 +1802,8 @@ static inline u32 open_file_mask_to_av(int mode, int mask)
av |= DIR__OPEN;
else
printk(KERN_ERR "SELinux: WARNING: inside %s with "
- "unknown mode:%x\n", __func__, mode);
- }
- return av;
-}
-
-/* Convert a Linux file to an access vector. */
-static inline u32 file_to_av(struct file *file)
-{
- u32 av = 0;
-
- if (file->f_mode & FMODE_READ)
- av |= FILE__READ;
- if (file->f_mode & FMODE_WRITE) {
- if (file->f_flags & O_APPEND)
- av |= FILE__APPEND;
- else
- av |= FILE__WRITE;
+ "unknown mode:%o\n", __func__, mode);
}
- if (!av) {
- /*
- * Special file opened with flags 3 for ioctl-only use.
- */
- av = FILE__IOCTL;
- }
-
return av;
}
@@ -1751,13 +1819,12 @@ static int selinux_ptrace_may_access(struct task_struct *child,
return rc;
if (mode == PTRACE_MODE_READ) {
- struct task_security_struct *tsec = current->security;
- struct task_security_struct *csec = child->security;
- return avc_has_perm(tsec->sid, csec->sid,
- SECCLASS_FILE, FILE__READ, NULL);
+ u32 sid = current_sid();
+ u32 csid = task_sid(child);
+ return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, NULL);
}
- return task_has_perm(current, child, PROCESS__PTRACE);
+ return current_has_perm(child, PROCESS__PTRACE);
}
static int selinux_ptrace_traceme(struct task_struct *parent)
@@ -1776,40 +1843,37 @@ static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
{
int error;
- error = task_has_perm(current, target, PROCESS__GETCAP);
+ error = current_has_perm(target, PROCESS__GETCAP);
if (error)
return error;
return secondary_ops->capget(target, effective, inheritable, permitted);
}
-static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
+static int selinux_capset(struct cred *new, const struct cred *old,
+ const kernel_cap_t *effective,
+ const kernel_cap_t *inheritable,
+ const kernel_cap_t *permitted)
{
int error;
- error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+ error = secondary_ops->capset(new, old,
+ effective, inheritable, permitted);
if (error)
return error;
- return task_has_perm(current, target, PROCESS__SETCAP);
-}
-
-static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
-{
- secondary_ops->capset_set(target, effective, inheritable, permitted);
+ return cred_has_perm(old, new, PROCESS__SETCAP);
}
-static int selinux_capable(struct task_struct *tsk, int cap)
+static int selinux_capable(struct task_struct *tsk, int cap, int audit)
{
int rc;
- rc = secondary_ops->capable(tsk, cap);
+ rc = secondary_ops->capable(tsk, cap, audit);
if (rc)
return rc;
- return task_has_capability(tsk, cap);
+ return task_has_capability(tsk, cap, audit);
}
static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
@@ -1857,15 +1921,14 @@ static int selinux_sysctl(ctl_table *table, int op)
{
int error = 0;
u32 av;
- struct task_security_struct *tsec;
- u32 tsid;
+ u32 tsid, sid;
int rc;
rc = secondary_ops->sysctl(table, op);
if (rc)
return rc;
- tsec = current->security;
+ sid = current_sid();
rc = selinux_sysctl_get_sid(table, (op == 0001) ?
SECCLASS_DIR : SECCLASS_FILE, &tsid);
@@ -1877,7 +1940,7 @@ static int selinux_sysctl(ctl_table *table, int op)
/* The op values are "defined" in sysctl.c, thereby creating
* a bad coupling between this module and sysctl.c */
if (op == 001) {
- error = avc_has_perm(tsec->sid, tsid,
+ error = avc_has_perm(sid, tsid,
SECCLASS_DIR, DIR__SEARCH, NULL);
} else {
av = 0;
@@ -1886,7 +1949,7 @@ static int selinux_sysctl(ctl_table *table, int op)
if (op & 002)
av |= FILE__WRITE;
if (av)
- error = avc_has_perm(tsec->sid, tsid,
+ error = avc_has_perm(sid, tsid,
SECCLASS_FILE, av, NULL);
}
@@ -1895,6 +1958,7 @@ static int selinux_sysctl(ctl_table *table, int op)
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
{
+ const struct cred *cred = current_cred();
int rc = 0;
if (!sb)
@@ -1906,14 +1970,12 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
case Q_QUOTAOFF:
case Q_SETINFO:
case Q_SETQUOTA:
- rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD,
- NULL);
+ rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL);
break;
case Q_GETFMT:
case Q_GETINFO:
case Q_GETQUOTA:
- rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET,
- NULL);
+ rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL);
break;
default:
rc = 0; /* let the kernel handle invalid cmds */
@@ -1924,7 +1986,9 @@ static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
static int selinux_quota_on(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
}
static int selinux_syslog(int type)
@@ -1972,16 +2036,8 @@ static int selinux_syslog(int type)
static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
{
int rc, cap_sys_admin = 0;
- struct task_security_struct *tsec = current->security;
-
- rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
- if (rc == 0)
- rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY,
- CAP_TO_MASK(CAP_SYS_ADMIN),
- 0,
- NULL);
+ rc = selinux_capable(current, CAP_SYS_ADMIN, SECURITY_CAP_NOAUDIT);
if (rc == 0)
cap_sys_admin = 1;
@@ -1990,59 +2046,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
-static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
+static int selinux_bprm_set_creds(struct linux_binprm *bprm)
{
- struct bprm_security_struct *bsec;
-
- bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
- if (!bsec)
- return -ENOMEM;
-
- bsec->sid = SECINITSID_UNLABELED;
- bsec->set = 0;
-
- bprm->security = bsec;
- return 0;
-}
-
-static int selinux_bprm_set_security(struct linux_binprm *bprm)
-{
- struct task_security_struct *tsec;
- struct inode *inode = bprm->file->f_path.dentry->d_inode;
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *new_tsec;
struct inode_security_struct *isec;
- struct bprm_security_struct *bsec;
- u32 newsid;
struct avc_audit_data ad;
+ struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
- rc = secondary_ops->bprm_set_security(bprm);
+ rc = secondary_ops->bprm_set_creds(bprm);
if (rc)
return rc;
- bsec = bprm->security;
-
- if (bsec->set)
+ /* SELinux context only depends on initial program or script and not
+ * the script interpreter */
+ if (bprm->cred_prepared)
return 0;
- tsec = current->security;
+ old_tsec = current_security();
+ new_tsec = bprm->cred->security;
isec = inode->i_security;
/* Default to the current task SID. */
- bsec->sid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
+ new_tsec->osid = old_tsec->sid;
/* Reset fs, key, and sock SIDs on execve. */
- tsec->create_sid = 0;
- tsec->keycreate_sid = 0;
- tsec->sockcreate_sid = 0;
+ new_tsec->create_sid = 0;
+ new_tsec->keycreate_sid = 0;
+ new_tsec->sockcreate_sid = 0;
- if (tsec->exec_sid) {
- newsid = tsec->exec_sid;
+ if (old_tsec->exec_sid) {
+ new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
- tsec->exec_sid = 0;
+ new_tsec->exec_sid = 0;
} else {
/* Check for a default transition on this program. */
- rc = security_transition_sid(tsec->sid, isec->sid,
- SECCLASS_PROCESS, &newsid);
+ rc = security_transition_sid(old_tsec->sid, isec->sid,
+ SECCLASS_PROCESS, &new_tsec->sid);
if (rc)
return rc;
}
@@ -2051,33 +2093,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
ad.u.fs.path = bprm->file->f_path;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
- newsid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
- if (tsec->sid == newsid) {
- rc = avc_has_perm(tsec->sid, isec->sid,
+ if (new_tsec->sid == old_tsec->sid) {
+ rc = avc_has_perm(old_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
- rc = avc_has_perm(tsec->sid, newsid,
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
- rc = avc_has_perm(newsid, isec->sid,
+ rc = avc_has_perm(new_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
- /* Clear any possibly unsafe personality bits on exec: */
- current->personality &= ~PER_CLEAR_ON_SETID;
+ /* Check for shared state */
+ if (bprm->unsafe & LSM_UNSAFE_SHARE) {
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ SECCLASS_PROCESS, PROCESS__SHARE,
+ NULL);
+ if (rc)
+ return -EPERM;
+ }
+
+ /* Make sure that anyone attempting to ptrace over a task that
+ * changes its SID has the appropriate permit */
+ if (bprm->unsafe &
+ (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ struct task_struct *tracer;
+ struct task_security_struct *sec;
+ u32 ptsid = 0;
- /* Set the security field to the new SID. */
- bsec->sid = newsid;
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(current);
+ if (likely(tracer != NULL)) {
+ sec = __task_cred(tracer)->security;
+ ptsid = sec->sid;
+ }
+ rcu_read_unlock();
+
+ if (ptsid != 0) {
+ rc = avc_has_perm(ptsid, new_tsec->sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc)
+ return -EPERM;
+ }
+ }
+
+ /* Clear any possibly unsafe personality bits on exec: */
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
}
- bsec->set = 1;
return 0;
}
@@ -2086,35 +2158,34 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)
return secondary_ops->bprm_check_security(bprm);
}
-
static int selinux_bprm_secureexec(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec = current->security;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
+ u32 sid, osid;
int atsecure = 0;
- if (tsec->osid != tsec->sid) {
+ sid = tsec->sid;
+ osid = tsec->osid;
+
+ if (osid != sid) {
/* Enable secure mode for SIDs transitions unless
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
- atsecure = avc_has_perm(tsec->osid, tsec->sid,
- SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL);
+ atsecure = avc_has_perm(osid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__NOATSECURE, NULL);
}
return (atsecure || secondary_ops->bprm_secureexec(bprm));
}
-static void selinux_bprm_free_security(struct linux_binprm *bprm)
-{
- kfree(bprm->security);
- bprm->security = NULL;
-}
-
extern struct vfsmount *selinuxfs_mount;
extern struct dentry *selinux_null;
/* Derived from fs/exec.c:flush_old_files. */
-static inline void flush_unauthorized_files(struct files_struct *files)
+static inline void flush_unauthorized_files(const struct cred *cred,
+ struct files_struct *files)
{
struct avc_audit_data ad;
struct file *file, *devnull = NULL;
@@ -2136,7 +2207,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
interested in the inode-based check here. */
file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
inode = file->f_path.dentry->d_inode;
- if (inode_has_perm(current, inode,
+ if (inode_has_perm(cred, inode,
FILE__READ | FILE__WRITE, NULL)) {
drop_tty = 1;
}
@@ -2171,7 +2242,7 @@ static inline void flush_unauthorized_files(struct files_struct *files)
file = fget(i);
if (!file)
continue;
- if (file_has_perm(current,
+ if (file_has_perm(cred,
file,
file_to_av(file))) {
sys_close(i);
@@ -2185,7 +2256,10 @@ static inline void flush_unauthorized_files(struct files_struct *files)
if (devnull) {
get_file(devnull);
} else {
- devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+ devnull = dentry_open(
+ dget(selinux_null),
+ mntget(selinuxfs_mount),
+ O_RDWR, cred);
if (IS_ERR(devnull)) {
devnull = NULL;
put_unused_fd(fd);
@@ -2204,94 +2278,78 @@ static inline void flush_unauthorized_files(struct files_struct *files)
spin_unlock(&files->file_lock);
}
-static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+/*
+ * Prepare a process for imminent new credential changes due to exec
+ */
+static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec;
- struct bprm_security_struct *bsec;
- u32 sid;
- int rc;
-
- secondary_ops->bprm_apply_creds(bprm, unsafe);
-
- tsec = current->security;
+ struct task_security_struct *new_tsec;
+ struct rlimit *rlim, *initrlim;
+ int rc, i;
- bsec = bprm->security;
- sid = bsec->sid;
+ secondary_ops->bprm_committing_creds(bprm);
- tsec->osid = tsec->sid;
- bsec->unsafe = 0;
- if (tsec->sid != sid) {
- /* Check for shared state. If not ok, leave SID
- unchanged and kill. */
- if (unsafe & LSM_UNSAFE_SHARE) {
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
+ new_tsec = bprm->cred->security;
+ if (new_tsec->sid == new_tsec->osid)
+ return;
- /* Check for ptracing, and update the task SID if ok.
- Otherwise, leave SID unchanged and kill. */
- if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- struct task_struct *tracer;
- struct task_security_struct *sec;
- u32 ptsid = 0;
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(bprm->cred, current->files);
- rcu_read_lock();
- tracer = tracehook_tracer_task(current);
- if (likely(tracer != NULL)) {
- sec = tracer->security;
- ptsid = sec->sid;
- }
- rcu_read_unlock();
+ /* Always clear parent death signal on SID transitions. */
+ current->pdeath_signal = 0;
- if (ptsid != 0) {
- rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- return;
- }
- }
+ /* Check whether the new SID can inherit resource limits from the old
+ * SID. If not, reset all soft limits to the lower of the current
+ * task's hard limit and the init task's soft limit.
+ *
+ * Note that the setting of hard limits (even to lower them) can be
+ * controlled by the setrlimit check. The inclusion of the init task's
+ * soft limit into the computation is to avoid resetting soft limits
+ * higher than the default soft limit for cases where the default is
+ * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK.
+ */
+ rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
+ PROCESS__RLIMITINH, NULL);
+ if (rc) {
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim + i;
+ rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
}
- tsec->sid = sid;
+ update_rlimit_cpu(rlim->rlim_cur);
}
}
/*
- * called after apply_creds without the task lock held
+ * Clean up the process immediately after the installation of new credentials
+ * due to exec
*/
-static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec;
- struct rlimit *rlim, *initrlim;
+ const struct task_security_struct *tsec = current_security();
struct itimerval itimer;
- struct bprm_security_struct *bsec;
+ struct sighand_struct *psig;
+ u32 osid, sid;
int rc, i;
+ unsigned long flags;
- tsec = current->security;
- bsec = bprm->security;
+ secondary_ops->bprm_committed_creds(bprm);
- if (bsec->unsafe) {
- force_sig_specific(SIGKILL, current);
- return;
- }
- if (tsec->osid == tsec->sid)
+ osid = tsec->osid;
+ sid = tsec->sid;
+
+ if (sid == osid)
return;
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(current->files);
-
- /* Check whether the new SID can inherit signal state
- from the old SID. If not, clear itimers to avoid
- subsequent signal generation and flush and unblock
- signals. This must occur _after_ the task SID has
- been updated so that any kill done after the flush
- will be checked against the new SID. */
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__SIGINH, NULL);
+ /* Check whether the new SID can inherit signal state from the old SID.
+ * If not, clear itimers to avoid subsequent signal generation and
+ * flush and unblock signals.
+ *
+ * This must occur _after_ the task SID has been updated so that any
+ * kill done after the flush will be checked against the new SID.
+ */
+ rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
if (rc) {
memset(&itimer, 0, sizeof itimer);
for (i = 0; i < 3; i++)
@@ -2304,33 +2362,14 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
spin_unlock_irq(&current->sighand->siglock);
}
- /* Always clear parent death signal on SID transitions. */
- current->pdeath_signal = 0;
-
- /* Check whether the new SID can inherit resource limits
- from the old SID. If not, reset all soft limits to
- the lower of the current task's hard limit and the init
- task's soft limit. Note that the setting of hard limits
- (even to lower them) can be controlled by the setrlimit
- check. The inclusion of the init task's soft limit into
- the computation is to avoid resetting soft limits higher
- than the default soft limit for cases where the default
- is lower than the hard limit, e.g. RLIMIT_CORE or
- RLIMIT_STACK.*/
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__RLIMITINH, NULL);
- if (rc) {
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim = current->signal->rlim + i;
- initrlim = init_task.signal->rlim+i;
- rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
- }
- update_rlimit_cpu(rlim->rlim_cur);
- }
-
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
+ /* Wake up the parent if it is waiting so that it can recheck
+ * wait permission to the new task SID. */
+ read_lock_irq(&tasklist_lock);
+ psig = current->parent->sighand;
+ spin_lock_irqsave(&psig->siglock, flags);
wake_up_interruptible(&current->parent->signal->wait_chldexit);
+ spin_unlock_irqrestore(&psig->siglock, flags);
+ read_unlock_irq(&tasklist_lock);
}
/* superblock security operations */
@@ -2435,8 +2474,9 @@ out:
return rc;
}
-static int selinux_sb_kern_mount(struct super_block *sb, void *data)
+static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
+ const struct cred *cred = current_cred();
struct avc_audit_data ad;
int rc;
@@ -2444,18 +2484,23 @@ static int selinux_sb_kern_mount(struct super_block *sb, void *data)
if (rc)
return rc;
+ /* Allow all mounts performed by the kernel */
+ if (flags & MS_KERNMOUNT)
+ return 0;
+
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = sb->s_root;
- return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
+ return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
}
static int selinux_sb_statfs(struct dentry *dentry)
{
+ const struct cred *cred = current_cred();
struct avc_audit_data ad;
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry->d_sb->s_root;
- return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
+ return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
}
static int selinux_mount(char *dev_name,
@@ -2464,6 +2509,7 @@ static int selinux_mount(char *dev_name,
unsigned long flags,
void *data)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
@@ -2471,22 +2517,23 @@ static int selinux_mount(char *dev_name,
return rc;
if (flags & MS_REMOUNT)
- return superblock_has_perm(current, path->mnt->mnt_sb,
+ return superblock_has_perm(cred, path->mnt->mnt_sb,
FILESYSTEM__REMOUNT, NULL);
else
- return dentry_has_perm(current, path->mnt, path->dentry,
+ return dentry_has_perm(cred, path->mnt, path->dentry,
FILE__MOUNTON);
}
static int selinux_umount(struct vfsmount *mnt, int flags)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->sb_umount(mnt, flags);
if (rc)
return rc;
- return superblock_has_perm(current, mnt->mnt_sb,
+ return superblock_has_perm(cred, mnt->mnt_sb,
FILESYSTEM__UNMOUNT, NULL);
}
@@ -2506,21 +2553,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value,
size_t *len)
{
- struct task_security_struct *tsec;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
- u32 newsid, clen;
+ u32 sid, newsid, clen;
int rc;
char *namep = NULL, *context;
- tsec = current->security;
dsec = dir->i_security;
sbsec = dir->i_sb->s_security;
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid,
+ sid = tsec->sid;
+ newsid = tsec->create_sid;
+
+ if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ rc = security_transition_sid(sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
if (rc) {
@@ -2623,21 +2671,25 @@ static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dent
static int selinux_inode_readlink(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_follow_link(dentry, nameidata);
if (rc)
return rc;
- return dentry_has_perm(current, NULL, dentry, FILE__READ);
+ return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_permission(inode, mask);
@@ -2649,12 +2701,13 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return 0;
}
- return inode_has_perm(current, inode,
- open_file_mask_to_av(inode->i_mode, mask), NULL);
+ return inode_has_perm(cred, inode,
+ file_mask_to_av(inode->i_mode, mask), NULL);
}
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->inode_setattr(dentry, iattr);
@@ -2666,18 +2719,22 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET))
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+ return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
- return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
+ return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
}
static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
- return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, mnt, dentry, FILE__GETATTR);
}
static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
{
+ const struct cred *cred = current_cred();
+
if (!strncmp(name, XATTR_SECURITY_PREFIX,
sizeof XATTR_SECURITY_PREFIX - 1)) {
if (!strcmp(name, XATTR_NAME_CAPS)) {
@@ -2692,18 +2749,17 @@ static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
/* Not an attribute we recognize, so just check the
ordinary setattr permission. */
- return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+ return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
}
static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- struct task_security_struct *tsec = current->security;
struct inode *inode = dentry->d_inode;
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec;
struct avc_audit_data ad;
- u32 newsid;
+ u32 newsid, sid = current_sid();
int rc = 0;
if (strcmp(name, XATTR_NAME_SELINUX))
@@ -2719,7 +2775,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
AVC_AUDIT_DATA_INIT(&ad, FS);
ad.u.fs.path.dentry = dentry;
- rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+ rc = avc_has_perm(sid, isec->sid, isec->sclass,
FILE__RELABELFROM, &ad);
if (rc)
return rc;
@@ -2733,12 +2789,12 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
if (rc)
return rc;
- rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+ rc = avc_has_perm(sid, newsid, isec->sclass,
FILE__RELABELTO, &ad);
if (rc)
return rc;
- rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ rc = security_validate_transition(isec->sid, newsid, sid,
isec->sclass);
if (rc)
return rc;
@@ -2778,12 +2834,16 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
{
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_listxattr(struct dentry *dentry)
{
- return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+ const struct cred *cred = current_cred();
+
+ return dentry_has_perm(cred, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
@@ -2806,7 +2866,6 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
u32 size;
int error;
char *context = NULL;
- struct task_security_struct *tsec = current->security;
struct inode_security_struct *isec = inode->i_security;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
@@ -2821,13 +2880,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
* and lack of permission just means that we fall back to the
* in-core context value, not a denial.
*/
- error = secondary_ops->capable(current, CAP_MAC_ADMIN);
- if (!error)
- error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
- SECCLASS_CAPABILITY2,
- CAPABILITY2__MAC_ADMIN,
- 0,
- NULL);
+ error = selinux_capable(current, CAP_MAC_ADMIN, SECURITY_CAP_NOAUDIT);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
&size);
@@ -2894,6 +2947,7 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
static int selinux_revalidate_file_permission(struct file *file, int mask)
{
+ const struct cred *cred = current_cred();
int rc;
struct inode *inode = file->f_path.dentry->d_inode;
@@ -2906,7 +2960,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
mask |= MAY_APPEND;
- rc = file_has_perm(current, file,
+ rc = file_has_perm(cred, file,
file_mask_to_av(inode->i_mode, mask));
if (rc)
return rc;
@@ -2917,16 +2971,16 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file->f_path.dentry->d_inode;
- struct task_security_struct *tsec = current->security;
struct file_security_struct *fsec = file->f_security;
struct inode_security_struct *isec = inode->i_security;
+ u32 sid = current_sid();
if (!mask) {
/* No permission to check. Existence test. */
return 0;
}
- if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+ if (sid == fsec->sid && fsec->isid == isec->sid
&& fsec->pseqno == avc_policy_seqno())
return selinux_netlbl_inode_permission(inode, mask);
@@ -2946,6 +3000,7 @@ static void selinux_file_free_security(struct file *file)
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ const struct cred *cred = current_cred();
u32 av = 0;
if (_IOC_DIR(cmd) & _IOC_WRITE)
@@ -2955,11 +3010,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
if (!av)
av = FILE__IOCTL;
- return file_has_perm(current, file, av);
+ return file_has_perm(cred, file, av);
}
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
#ifndef CONFIG_PPC32
if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
/*
@@ -2967,9 +3025,9 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
* private file mapping that will also be writable.
* This has an additional check.
*/
- int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ rc = cred_has_perm(cred, cred, PROCESS__EXECMEM);
if (rc)
- return rc;
+ goto error;
}
#endif
@@ -2984,9 +3042,11 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
if (prot & PROT_EXEC)
av |= FILE__EXECUTE;
- return file_has_perm(current, file, av);
+ return file_has_perm(cred, file, av);
}
- return 0;
+
+error:
+ return rc;
}
static int selinux_file_mmap(struct file *file, unsigned long reqprot,
@@ -2994,7 +3054,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot,
unsigned long addr, unsigned long addr_only)
{
int rc = 0;
- u32 sid = ((struct task_security_struct *)(current->security))->sid;
+ u32 sid = current_sid();
if (addr < mmap_min_addr)
rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
@@ -3013,6 +3073,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long reqprot,
unsigned long prot)
{
+ const struct cred *cred = current_cred();
int rc;
rc = secondary_ops->file_mprotect(vma, reqprot, prot);
@@ -3027,12 +3088,11 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
- rc = task_has_perm(current, current,
- PROCESS__EXECHEAP);
+ rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
} else if (!vma->vm_file &&
vma->vm_start <= vma->vm_mm->start_stack &&
vma->vm_end >= vma->vm_mm->start_stack) {
- rc = task_has_perm(current, current, PROCESS__EXECSTACK);
+ rc = current_has_perm(current, PROCESS__EXECSTACK);
} else if (vma->vm_file && vma->anon_vma) {
/*
* We are making executable a file mapping that has
@@ -3041,8 +3101,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
* modified content. This typically should only
* occur for text relocations.
*/
- rc = file_has_perm(current, vma->vm_file,
- FILE__EXECMOD);
+ rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
}
if (rc)
return rc;
@@ -3054,12 +3113,15 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
static int selinux_file_lock(struct file *file, unsigned int cmd)
{
- return file_has_perm(current, file, FILE__LOCK);
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, FILE__LOCK);
}
static int selinux_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ const struct cred *cred = current_cred();
int err = 0;
switch (cmd) {
@@ -3070,7 +3132,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
}
if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
- err = file_has_perm(current, file, FILE__WRITE);
+ err = file_has_perm(cred, file, FILE__WRITE);
break;
}
/* fall through */
@@ -3080,7 +3142,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
case F_GETOWN:
case F_GETSIG:
/* Just check FD__USE permission */
- err = file_has_perm(current, file, 0);
+ err = file_has_perm(cred, file, 0);
break;
case F_GETLK:
case F_SETLK:
@@ -3094,7 +3156,7 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
err = -EINVAL;
break;
}
- err = file_has_perm(current, file, FILE__LOCK);
+ err = file_has_perm(cred, file, FILE__LOCK);
break;
}
@@ -3103,12 +3165,10 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
static int selinux_file_set_fowner(struct file *file)
{
- struct task_security_struct *tsec;
struct file_security_struct *fsec;
- tsec = current->security;
fsec = file->f_security;
- fsec->fown_sid = tsec->sid;
+ fsec->fown_sid = current_sid();
return 0;
}
@@ -3117,14 +3177,13 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
struct file *file;
+ u32 sid = current_sid();
u32 perm;
- struct task_security_struct *tsec;
struct file_security_struct *fsec;
/* struct fown_struct is never outside the context of a struct file */
file = container_of(fown, struct file, f_owner);
- tsec = tsk->security;
fsec = file->f_security;
if (!signum)
@@ -3132,20 +3191,23 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
else
perm = signal_to_av(signum);
- return avc_has_perm(fsec->fown_sid, tsec->sid,
+ return avc_has_perm(fsec->fown_sid, sid,
SECCLASS_PROCESS, perm, NULL);
}
static int selinux_file_receive(struct file *file)
{
- return file_has_perm(current, file, file_to_av(file));
+ const struct cred *cred = current_cred();
+
+ return file_has_perm(cred, file, file_to_av(file));
}
-static int selinux_dentry_open(struct file *file)
+static int selinux_dentry_open(struct file *file, const struct cred *cred)
{
struct file_security_struct *fsec;
struct inode *inode;
struct inode_security_struct *isec;
+
inode = file->f_path.dentry->d_inode;
fsec = file->f_security;
isec = inode->i_security;
@@ -3166,7 +3228,7 @@ static int selinux_dentry_open(struct file *file)
* new inode label or new policy.
* This check is not redundant - do not remove.
*/
- return inode_has_perm(current, inode, file_to_av(file), NULL);
+ return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
}
/* task security operations */
@@ -3179,36 +3241,88 @@ static int selinux_task_create(unsigned long clone_flags)
if (rc)
return rc;
- return task_has_perm(current, current, PROCESS__FORK);
+ return current_has_perm(current, PROCESS__FORK);
}
-static int selinux_task_alloc_security(struct task_struct *tsk)
+/*
+ * detach and free the LSM part of a set of credentials
+ */
+static void selinux_cred_free(struct cred *cred)
{
- struct task_security_struct *tsec1, *tsec2;
- int rc;
-
- tsec1 = current->security;
+ struct task_security_struct *tsec = cred->security;
+ cred->security = NULL;
+ kfree(tsec);
+}
- rc = task_alloc_security(tsk);
- if (rc)
- return rc;
- tsec2 = tsk->security;
+/*
+ * prepare a new set of credentials for modification
+ */
+static int selinux_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *tsec;
- tsec2->osid = tsec1->osid;
- tsec2->sid = tsec1->sid;
+ old_tsec = old->security;
- /* Retain the exec, fs, key, and sock SIDs across fork */
- tsec2->exec_sid = tsec1->exec_sid;
- tsec2->create_sid = tsec1->create_sid;
- tsec2->keycreate_sid = tsec1->keycreate_sid;
- tsec2->sockcreate_sid = tsec1->sockcreate_sid;
+ tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
+ if (!tsec)
+ return -ENOMEM;
+ new->security = tsec;
return 0;
}
-static void selinux_task_free_security(struct task_struct *tsk)
+/*
+ * commit new credentials
+ */
+static void selinux_cred_commit(struct cred *new, const struct cred *old)
+{
+ secondary_ops->cred_commit(new, old);
+}
+
+/*
+ * set the security data for a kernel service
+ * - all the creation contexts are set to unlabelled
+ */
+static int selinux_kernel_act_as(struct cred *new, u32 secid)
{
- task_free_security(tsk);
+ struct task_security_struct *tsec = new->security;
+ u32 sid = current_sid();
+ int ret;
+
+ ret = avc_has_perm(sid, secid,
+ SECCLASS_KERNEL_SERVICE,
+ KERNEL_SERVICE__USE_AS_OVERRIDE,
+ NULL);
+ if (ret == 0) {
+ tsec->sid = secid;
+ tsec->create_sid = 0;
+ tsec->keycreate_sid = 0;
+ tsec->sockcreate_sid = 0;
+ }
+ return ret;
+}
+
+/*
+ * set the file creation context in a security record to the same as the
+ * objective context of the specified inode
+ */
+static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ struct task_security_struct *tsec = new->security;
+ u32 sid = current_sid();
+ int ret;
+
+ ret = avc_has_perm(sid, isec->sid,
+ SECCLASS_KERNEL_SERVICE,
+ KERNEL_SERVICE__CREATE_FILES_AS,
+ NULL);
+
+ if (ret == 0)
+ tsec->create_sid = isec->sid;
+ return 0;
}
static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
@@ -3222,9 +3336,10 @@ static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
return 0;
}
-static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
+ int flags)
{
- return secondary_ops->task_post_setuid(id0, id1, id2, flags);
+ return secondary_ops->task_fix_setuid(new, old, flags);
}
static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
@@ -3235,23 +3350,22 @@ static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return task_has_perm(current, p, PROCESS__SETPGID);
+ return current_has_perm(p, PROCESS__SETPGID);
}
static int selinux_task_getpgid(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETPGID);
+ return current_has_perm(p, PROCESS__GETPGID);
}
static int selinux_task_getsid(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSESSION);
+ return current_has_perm(p, PROCESS__GETSESSION);
}
static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
{
- struct task_security_struct *tsec = p->security;
- *secid = tsec->sid;
+ *secid = task_sid(p);
}
static int selinux_task_setgroups(struct group_info *group_info)
@@ -3268,7 +3382,7 @@ static int selinux_task_setnice(struct task_struct *p, int nice)
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_setioprio(struct task_struct *p, int ioprio)
@@ -3279,12 +3393,12 @@ static int selinux_task_setioprio(struct task_struct *p, int ioprio)
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_getioprio(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSCHED);
+ return current_has_perm(p, PROCESS__GETSCHED);
}
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
@@ -3299,9 +3413,9 @@ static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
later be used as a safe reset point for the soft limit
- upon context transitions. See selinux_bprm_apply_creds. */
+ upon context transitions. See selinux_bprm_committing_creds. */
if (old_rlim->rlim_max != new_rlim->rlim_max)
- return task_has_perm(current, current, PROCESS__SETRLIMIT);
+ return current_has_perm(current, PROCESS__SETRLIMIT);
return 0;
}
@@ -3314,17 +3428,17 @@ static int selinux_task_setscheduler(struct task_struct *p, int policy, struct s
if (rc)
return rc;
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_getscheduler(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__GETSCHED);
+ return current_has_perm(p, PROCESS__GETSCHED);
}
static int selinux_task_movememory(struct task_struct *p)
{
- return task_has_perm(current, p, PROCESS__SETSCHED);
+ return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
@@ -3332,7 +3446,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
{
u32 perm;
int rc;
- struct task_security_struct *tsec;
rc = secondary_ops->task_kill(p, info, sig, secid);
if (rc)
@@ -3342,11 +3455,11 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
perm = signal_to_av(sig);
- tsec = p->security;
if (secid)
- rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+ rc = avc_has_perm(secid, task_sid(p),
+ SECCLASS_PROCESS, perm, NULL);
else
- rc = task_has_perm(current, p, perm);
+ rc = current_has_perm(p, perm);
return rc;
}
@@ -3354,13 +3467,12 @@ static int selinux_task_prctl(int option,
unsigned long arg2,
unsigned long arg3,
unsigned long arg4,
- unsigned long arg5,
- long *rc_p)
+ unsigned long arg5)
{
/* The current prctl operations do not appear to require
any SELinux controls since they merely observe or modify
the state of the current process. */
- return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+ return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
}
static int selinux_task_wait(struct task_struct *p)
@@ -3368,27 +3480,14 @@ static int selinux_task_wait(struct task_struct *p)
return task_has_perm(p, current, PROCESS__SIGCHLD);
}
-static void selinux_task_reparent_to_init(struct task_struct *p)
-{
- struct task_security_struct *tsec;
-
- secondary_ops->task_reparent_to_init(p);
-
- tsec = p->security;
- tsec->osid = tsec->sid;
- tsec->sid = SECINITSID_KERNEL;
- return;
-}
-
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct task_security_struct *tsec = p->security;
struct inode_security_struct *isec = inode->i_security;
+ u32 sid = task_sid(p);
- isec->sid = tsec->sid;
+ isec->sid = sid;
isec->initialized = 1;
- return;
}
/* Returns error only if unable to parse addresses */
@@ -3627,19 +3726,19 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
u32 perms)
{
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct avc_audit_data ad;
+ u32 sid;
int err = 0;
- tsec = task->security;
isec = SOCK_INODE(sock)->i_security;
if (isec->sid == SECINITSID_KERNEL)
goto out;
+ sid = task_sid(task);
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
- err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+ err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
out:
return err;
@@ -3648,18 +3747,20 @@ out:
static int selinux_socket_create(int family, int type,
int protocol, int kern)
{
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
+ u32 sid, newsid;
+ u16 secclass;
int err = 0;
- struct task_security_struct *tsec;
- u32 newsid;
if (kern)
goto out;
- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
- err = avc_has_perm(tsec->sid, newsid,
- socket_type_to_security_class(family, type,
- protocol), SOCKET__CREATE, NULL);
+ sid = tsec->sid;
+ newsid = tsec->sockcreate_sid ?: sid;
+
+ secclass = socket_type_to_security_class(family, type, protocol);
+ err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL);
out:
return err;
@@ -3668,18 +3769,26 @@ out:
static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- int err = 0;
+ const struct cred *cred = current_cred();
+ const struct task_security_struct *tsec = cred->security;
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct sk_security_struct *sksec;
- u32 newsid;
+ u32 sid, newsid;
+ int err = 0;
+
+ sid = tsec->sid;
+ newsid = tsec->sockcreate_sid;
isec = SOCK_INODE(sock)->i_security;
- tsec = current->security;
- newsid = tsec->sockcreate_sid ? : tsec->sid;
+ if (kern)
+ isec->sid = SECINITSID_KERNEL;
+ else if (newsid)
+ isec->sid = newsid;
+ else
+ isec->sid = sid;
+
isec->sclass = socket_type_to_security_class(family, type, protocol);
- isec->sid = kern ? SECINITSID_KERNEL : newsid;
isec->initialized = 1;
if (sock->sk) {
@@ -3714,7 +3823,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
if (family == PF_INET || family == PF_INET6) {
char *addrp;
struct inode_security_struct *isec;
- struct task_security_struct *tsec;
struct avc_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3722,7 +3830,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
struct sock *sk = sock->sk;
u32 sid, node_perm;
- tsec = current->security;
isec = SOCK_INODE(sock)->i_security;
if (family == PF_INET) {
@@ -4387,7 +4494,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
"SELinux: unrecognized netlink message"
" type=%hu for sclass=%hu\n",
nlh->nlmsg_type, isec->sclass);
- if (!selinux_enforcing)
+ if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
@@ -4628,7 +4735,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
* as fast and as clean as possible. */
if (selinux_compat_net || !selinux_policycap_netpeer)
return selinux_ip_postroute_compat(skb, ifindex, family);
-
+#ifdef CONFIG_XFRM
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
* packet transformation so allow the packet to pass without any checks
* since we'll have another chance to perform access control checks
@@ -4637,7 +4744,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
* is NULL, in this case go ahead and apply access control. */
if (skb->dst != NULL && skb->dst->xfrm != NULL)
return NF_ACCEPT;
-
+#endif
secmark_active = selinux_secmark_enabled();
peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
@@ -4763,15 +4870,16 @@ static int ipc_alloc_security(struct task_struct *task,
struct kern_ipc_perm *perm,
u16 sclass)
{
- struct task_security_struct *tsec = task->security;
struct ipc_security_struct *isec;
+ u32 sid;
isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
if (!isec)
return -ENOMEM;
+ sid = task_sid(task);
isec->sclass = sclass;
- isec->sid = tsec->sid;
+ isec->sid = sid;
perm->security = isec;
return 0;
@@ -4809,17 +4917,16 @@ static void msg_msg_free_security(struct msg_msg *msg)
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
u32 perms)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = ipc_perms->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = ipc_perms->key;
- return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+ return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
}
static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
@@ -4835,22 +4942,21 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
/* message queue security operations */
static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
if (rc)
return rc;
- tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &ad);
if (rc) {
ipc_free_security(&msq->q_perm);
@@ -4866,17 +4972,16 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = msq->q_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__ASSOCIATE, &ad);
}
@@ -4910,13 +5015,12 @@ static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
- tsec = current->security;
isec = msq->q_perm.security;
msec = msg->security;
@@ -4928,9 +5032,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
* Compute new sid based on current process and
* message queue this message will be stored in
*/
- rc = security_transition_sid(tsec->sid,
- isec->sid,
- SECCLASS_MSG,
+ rc = security_transition_sid(sid, isec->sid, SECCLASS_MSG,
&msec->sid);
if (rc)
return rc;
@@ -4940,16 +5042,16 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
ad.u.ipc_id = msq->q_perm.key;
/* Can this process write to the queue? */
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
MSGQ__WRITE, &ad);
if (!rc)
/* Can this process send the message */
- rc = avc_has_perm(tsec->sid, msec->sid,
- SECCLASS_MSG, MSG__SEND, &ad);
+ rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG,
+ MSG__SEND, &ad);
if (!rc)
/* Can the message be put in the queue? */
- rc = avc_has_perm(msec->sid, isec->sid,
- SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
+ rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__ENQUEUE, &ad);
return rc;
}
@@ -4958,23 +5060,22 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
struct task_struct *target,
long type, int mode)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct msg_security_struct *msec;
struct avc_audit_data ad;
+ u32 sid = task_sid(target);
int rc;
- tsec = target->security;
isec = msq->q_perm.security;
msec = msg->security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = msq->q_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid,
+ rc = avc_has_perm(sid, isec->sid,
SECCLASS_MSGQ, MSGQ__READ, &ad);
if (!rc)
- rc = avc_has_perm(tsec->sid, msec->sid,
+ rc = avc_has_perm(sid, msec->sid,
SECCLASS_MSG, MSG__RECEIVE, &ad);
return rc;
}
@@ -4982,22 +5083,21 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
/* Shared Memory security operations */
static int selinux_shm_alloc_security(struct shmid_kernel *shp)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
if (rc)
return rc;
- tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &ad);
if (rc) {
ipc_free_security(&shp->shm_perm);
@@ -5013,17 +5113,16 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = shp->shm_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = shp->shm_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
SHM__ASSOCIATE, &ad);
}
@@ -5081,22 +5180,21 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
/* Semaphore security operations */
static int selinux_sem_alloc_security(struct sem_array *sma)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
int rc;
rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
if (rc)
return rc;
- tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
- rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &ad);
if (rc) {
ipc_free_security(&sma->sem_perm);
@@ -5112,17 +5210,16 @@ static void selinux_sem_free_security(struct sem_array *sma)
static int selinux_sem_associate(struct sem_array *sma, int semflg)
{
- struct task_security_struct *tsec;
struct ipc_security_struct *isec;
struct avc_audit_data ad;
+ u32 sid = current_sid();
- tsec = current->security;
isec = sma->sem_perm.security;
AVC_AUDIT_DATA_INIT(&ad, IPC);
ad.u.ipc_id = sma->sem_perm.key;
- return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
SEM__ASSOCIATE, &ad);
}
@@ -5212,33 +5309,35 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
static int selinux_getprocattr(struct task_struct *p,
char *name, char **value)
{
- struct task_security_struct *tsec;
+ const struct task_security_struct *__tsec;
u32 sid;
int error;
unsigned len;
if (current != p) {
- error = task_has_perm(current, p, PROCESS__GETATTR);
+ error = current_has_perm(p, PROCESS__GETATTR);
if (error)
return error;
}
- tsec = p->security;
+ rcu_read_lock();
+ __tsec = __task_cred(p)->security;
if (!strcmp(name, "current"))
- sid = tsec->sid;
+ sid = __tsec->sid;
else if (!strcmp(name, "prev"))
- sid = tsec->osid;
+ sid = __tsec->osid;
else if (!strcmp(name, "exec"))
- sid = tsec->exec_sid;
+ sid = __tsec->exec_sid;
else if (!strcmp(name, "fscreate"))
- sid = tsec->create_sid;
+ sid = __tsec->create_sid;
else if (!strcmp(name, "keycreate"))
- sid = tsec->keycreate_sid;
+ sid = __tsec->keycreate_sid;
else if (!strcmp(name, "sockcreate"))
- sid = tsec->sockcreate_sid;
+ sid = __tsec->sockcreate_sid;
else
- return -EINVAL;
+ goto invalid;
+ rcu_read_unlock();
if (!sid)
return 0;
@@ -5247,6 +5346,10 @@ static int selinux_getprocattr(struct task_struct *p,
if (error)
return error;
return len;
+
+invalid:
+ rcu_read_unlock();
+ return -EINVAL;
}
static int selinux_setprocattr(struct task_struct *p,
@@ -5254,7 +5357,8 @@ static int selinux_setprocattr(struct task_struct *p,
{
struct task_security_struct *tsec;
struct task_struct *tracer;
- u32 sid = 0;
+ struct cred *new;
+ u32 sid = 0, ptsid;
int error;
char *str = value;
@@ -5270,15 +5374,15 @@ static int selinux_setprocattr(struct task_struct *p,
* above restriction is ever removed.
*/
if (!strcmp(name, "exec"))
- error = task_has_perm(current, p, PROCESS__SETEXEC);
+ error = current_has_perm(p, PROCESS__SETEXEC);
else if (!strcmp(name, "fscreate"))
- error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ error = current_has_perm(p, PROCESS__SETFSCREATE);
else if (!strcmp(name, "keycreate"))
- error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+ error = current_has_perm(p, PROCESS__SETKEYCREATE);
else if (!strcmp(name, "sockcreate"))
- error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+ error = current_has_perm(p, PROCESS__SETSOCKCREATE);
else if (!strcmp(name, "current"))
- error = task_has_perm(current, p, PROCESS__SETCURRENT);
+ error = current_has_perm(p, PROCESS__SETCURRENT);
else
error = -EINVAL;
if (error)
@@ -5301,87 +5405,75 @@ static int selinux_setprocattr(struct task_struct *p,
return error;
}
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+
/* Permission checking based on the specified context is
performed during the actual operation (execve,
open/mkdir/...), when we know the full context of the
- operation. See selinux_bprm_set_security for the execve
+ operation. See selinux_bprm_set_creds for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = p->security;
- if (!strcmp(name, "exec"))
+ tsec = new->security;
+ if (!strcmp(name, "exec")) {
tsec->exec_sid = sid;
- else if (!strcmp(name, "fscreate"))
+ } else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
- else if (!strcmp(name, "keycreate")) {
+ } else if (!strcmp(name, "keycreate")) {
error = may_create_key(sid, p);
if (error)
- return error;
+ goto abort_change;
tsec->keycreate_sid = sid;
- } else if (!strcmp(name, "sockcreate"))
+ } else if (!strcmp(name, "sockcreate")) {
tsec->sockcreate_sid = sid;
- else if (!strcmp(name, "current")) {
- struct av_decision avd;
-
+ } else if (!strcmp(name, "current")) {
+ error = -EINVAL;
if (sid == 0)
- return -EINVAL;
- /*
- * SELinux allows to change context in the following case only.
- * - Single threaded processes.
- * - Multi threaded processes intend to change its context into
- * more restricted domain (defined by TYPEBOUNDS statement).
- */
- if (atomic_read(&p->mm->mm_users) != 1) {
- struct task_struct *g, *t;
- struct mm_struct *mm = p->mm;
- read_lock(&tasklist_lock);
- do_each_thread(g, t) {
- if (t->mm == mm && t != p) {
- read_unlock(&tasklist_lock);
- error = security_bounded_transition(tsec->sid, sid);
- if (!error)
- goto boundary_ok;
-
- return error;
- }
- } while_each_thread(g, t);
- read_unlock(&tasklist_lock);
+ goto abort_change;
+
+ /* Only allow single threaded processes to change context */
+ error = -EPERM;
+ if (!is_single_threaded(p)) {
+ error = security_bounded_transition(tsec->sid, sid);
+ if (error)
+ goto abort_change;
}
-boundary_ok:
/* Check permissions for the transition. */
error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__DYNTRANSITION, NULL);
if (error)
- return error;
+ goto abort_change;
/* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and fail. */
+ ptsid = 0;
task_lock(p);
- rcu_read_lock();
tracer = tracehook_tracer_task(p);
- if (tracer != NULL) {
- struct task_security_struct *ptsec = tracer->security;
- u32 ptsid = ptsec->sid;
- rcu_read_unlock();
- error = avc_has_perm_noaudit(ptsid, sid,
- SECCLASS_PROCESS,
- PROCESS__PTRACE, 0, &avd);
- if (!error)
- tsec->sid = sid;
- task_unlock(p);
- avc_audit(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, error, NULL);
+ if (tracer)
+ ptsid = task_sid(tracer);
+ task_unlock(p);
+
+ if (tracer) {
+ error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
if (error)
- return error;
- } else {
- rcu_read_unlock();
- tsec->sid = sid;
- task_unlock(p);
+ goto abort_change;
}
- } else
- return -EINVAL;
+ tsec->sid = sid;
+ } else {
+ error = -EINVAL;
+ goto abort_change;
+ }
+
+ commit_creds(new);
return size;
+
+abort_change:
+ abort_creds(new);
+ return error;
}
static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
@@ -5401,22 +5493,23 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
#ifdef CONFIG_KEYS
-static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+static int selinux_key_alloc(struct key *k, const struct cred *cred,
unsigned long flags)
{
- struct task_security_struct *tsec = tsk->security;
+ const struct task_security_struct *tsec;
struct key_security_struct *ksec;
ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
if (!ksec)
return -ENOMEM;
+ tsec = cred->security;
if (tsec->keycreate_sid)
ksec->sid = tsec->keycreate_sid;
else
ksec->sid = tsec->sid;
- k->security = ksec;
+ k->security = ksec;
return 0;
}
@@ -5429,17 +5522,12 @@ static void selinux_key_free(struct key *k)
}
static int selinux_key_permission(key_ref_t key_ref,
- struct task_struct *ctx,
- key_perm_t perm)
+ const struct cred *cred,
+ key_perm_t perm)
{
struct key *key;
- struct task_security_struct *tsec;
struct key_security_struct *ksec;
-
- key = key_ref_to_ptr(key_ref);
-
- tsec = ctx->security;
- ksec = key->security;
+ u32 sid;
/* if no specific permissions are requested, we skip the
permission check. No serious, additional covert channels
@@ -5447,8 +5535,12 @@ static int selinux_key_permission(key_ref_t key_ref,
if (perm == 0)
return 0;
- return avc_has_perm(tsec->sid, ksec->sid,
- SECCLASS_KEY, perm, NULL);
+ sid = cred_sid(cred);
+
+ key = key_ref_to_ptr(key_ref);
+ ksec = key->security;
+
+ return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
}
static int selinux_key_getsecurity(struct key *key, char **_buffer)
@@ -5473,8 +5565,7 @@ static struct security_operations selinux_ops = {
.ptrace_may_access = selinux_ptrace_may_access,
.ptrace_traceme = selinux_ptrace_traceme,
.capget = selinux_capget,
- .capset_check = selinux_capset_check,
- .capset_set = selinux_capset_set,
+ .capset = selinux_capset,
.sysctl = selinux_sysctl,
.capable = selinux_capable,
.quotactl = selinux_quotactl,
@@ -5485,12 +5576,10 @@ static struct security_operations selinux_ops = {
.netlink_send = selinux_netlink_send,
.netlink_recv = selinux_netlink_recv,
- .bprm_alloc_security = selinux_bprm_alloc_security,
- .bprm_free_security = selinux_bprm_free_security,
- .bprm_apply_creds = selinux_bprm_apply_creds,
- .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
- .bprm_set_security = selinux_bprm_set_security,
+ .bprm_set_creds = selinux_bprm_set_creds,
.bprm_check_security = selinux_bprm_check_security,
+ .bprm_committing_creds = selinux_bprm_committing_creds,
+ .bprm_committed_creds = selinux_bprm_committed_creds,
.bprm_secureexec = selinux_bprm_secureexec,
.sb_alloc_security = selinux_sb_alloc_security,
@@ -5549,10 +5638,13 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open,
.task_create = selinux_task_create,
- .task_alloc_security = selinux_task_alloc_security,
- .task_free_security = selinux_task_free_security,
+ .cred_free = selinux_cred_free,
+ .cred_prepare = selinux_cred_prepare,
+ .cred_commit = selinux_cred_commit,
+ .kernel_act_as = selinux_kernel_act_as,
+ .kernel_create_files_as = selinux_kernel_create_files_as,
.task_setuid = selinux_task_setuid,
- .task_post_setuid = selinux_task_post_setuid,
+ .task_fix_setuid = selinux_task_fix_setuid,
.task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
@@ -5569,7 +5661,6 @@ static struct security_operations selinux_ops = {
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
.task_prctl = selinux_task_prctl,
- .task_reparent_to_init = selinux_task_reparent_to_init,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
@@ -5665,8 +5756,6 @@ static struct security_operations selinux_ops = {
static __init int selinux_init(void)
{
- struct task_security_struct *tsec;
-
if (!security_module_enable(&selinux_ops)) {
selinux_enabled = 0;
return 0;
@@ -5680,10 +5769,7 @@ static __init int selinux_init(void)
printk(KERN_INFO "SELinux: Initializing.\n");
/* Set the security state for the initial task. */
- if (task_alloc_security(current))
- panic("SELinux: Failed to initialize initial task.\n");
- tsec = current->security;
- tsec->osid = tsec->sid = SECINITSID_KERNEL;
+ cred_init_security();
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
index 1223b4ff9bee..c0c885427b91 100644
--- a/security/selinux/include/av_perm_to_string.h
+++ b/security/selinux/include/av_perm_to_string.h
@@ -176,3 +176,5 @@
S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
S_(SECCLASS_PEER, PEER__RECV, "recv")
+ S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__USE_AS_OVERRIDE, "use_as_override")
+ S_(SECCLASS_KERNEL_SERVICE, KERNEL_SERVICE__CREATE_FILES_AS, "create_files_as")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
index c4c51165c505..0ba79fe00e11 100644
--- a/security/selinux/include/av_permissions.h
+++ b/security/selinux/include/av_permissions.h
@@ -841,3 +841,5 @@
#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
#define MEMPROTECT__MMAP_ZERO 0x00000001UL
#define PEER__RECV 0x00000001UL
+#define KERNEL_SERVICE__USE_AS_OVERRIDE 0x00000001UL
+#define KERNEL_SERVICE__CREATE_FILES_AS 0x00000002UL
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
index bd813c366e34..21ec786611d4 100644
--- a/security/selinux/include/class_to_string.h
+++ b/security/selinux/include/class_to_string.h
@@ -72,3 +72,8 @@
S_(NULL)
S_("peer")
S_("capability2")
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_("kernel_service")
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
index febf8868e852..882f27d66fac 100644
--- a/security/selinux/include/flask.h
+++ b/security/selinux/include/flask.h
@@ -52,6 +52,7 @@
#define SECCLASS_MEMPROTECT 61
#define SECCLASS_PEER 68
#define SECCLASS_CAPABILITY2 69
+#define SECCLASS_KERNEL_SERVICE 74
/*
* Security identifier indices for initial entities
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index f8be8d7fa26d..3cc45168f674 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -77,17 +77,6 @@ struct ipc_security_struct {
u32 sid; /* SID of IPC resource */
};
-struct bprm_security_struct {
- u32 sid; /* SID for transformed process */
- unsigned char set;
-
- /*
- * unsafe is used to share failure information from bprm_apply_creds()
- * to bprm_post_apply_creds().
- */
- char unsafe;
-};
-
struct netif_security_struct {
int ifindex; /* device index */
u32 sid; /* SID for this interface */
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index ff59c0c4804b..4ed7bab89c59 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -63,6 +63,9 @@ static struct nlmsg_perm nlmsg_route_perms[] =
{ RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
};
static struct nlmsg_perm nlmsg_firewall_perms[] =
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 69c9dccc8cf0..e5520996a75b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -95,13 +95,18 @@ extern void selnl_notify_setenforce(int val);
static int task_has_security(struct task_struct *tsk,
u32 perms)
{
- struct task_security_struct *tsec;
-
- tsec = tsk->security;
+ const struct task_security_struct *tsec;
+ u32 sid = 0;
+
+ rcu_read_lock();
+ tsec = __task_cred(tsk)->security;
+ if (tsec)
+ sid = tsec->sid;
+ rcu_read_unlock();
if (!tsec)
return -EACCES;
- return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+ return avc_has_perm(sid, SECINITSID_SECURITY,
SECCLASS_SECURITY, perms, NULL);
}
@@ -1206,7 +1211,7 @@ static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
{
int cpu;
- for (cpu = *idx; cpu < NR_CPUS; ++cpu) {
+ for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*idx = cpu + 1;
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 8f17f542a116..c0eb72013d67 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -197,7 +197,7 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *uctx, u32 sid)
{
int rc = 0;
- struct task_security_struct *tsec = current->security;
+ const struct task_security_struct *tsec = current_security();
struct xfrm_sec_ctx *ctx = NULL;
char *ctx_str = NULL;
u32 str_len;
@@ -333,7 +333,7 @@ void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
*/
int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
{
- struct task_security_struct *tsec = current->security;
+ const struct task_security_struct *tsec = current_security();
int rc = 0;
if (ctx) {
@@ -378,7 +378,7 @@ void selinux_xfrm_state_free(struct xfrm_state *x)
*/
int selinux_xfrm_state_delete(struct xfrm_state *x)
{
- struct task_security_struct *tsec = current->security;
+ const struct task_security_struct *tsec = current_security();
struct xfrm_sec_ctx *ctx = x->security;
int rc = 0;
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 79ff21ed4c3b..247cec3b5a43 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -164,7 +164,7 @@ int smk_curacc(char *obj_label, u32 mode)
{
int rc;
- rc = smk_access(current->security, obj_label, mode);
+ rc = smk_access(current_security(), obj_label, mode);
if (rc == 0)
return 0;
@@ -173,7 +173,7 @@ int smk_curacc(char *obj_label, u32 mode)
* only one that gets privilege and current does not
* have that label.
*/
- if (smack_onlycap != NULL && smack_onlycap != current->security)
+ if (smack_onlycap != NULL && smack_onlycap != current->cred->security)
return rc;
if (capable(CAP_MAC_OVERRIDE))
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 6e2dc0bab70d..1b5551dfc1f7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -30,6 +30,8 @@
#include "smack.h"
+#define task_security(task) (task_cred_xxx((task), security))
+
/*
* I hope these are the hokeyist lines of code in the module. Casey.
*/
@@ -102,7 +104,7 @@ static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
if (rc != 0)
return rc;
- rc = smk_access(current->security, ctp->security, MAY_READWRITE);
+ rc = smk_access(current_security(), task_security(ctp), MAY_READWRITE);
if (rc != 0 && capable(CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -124,7 +126,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
if (rc != 0)
return rc;
- rc = smk_access(ptp->security, current->security, MAY_READWRITE);
+ rc = smk_access(task_security(ptp), current_security(), MAY_READWRITE);
if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -141,7 +143,7 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
static int smack_syslog(int type)
{
int rc;
- char *sp = current->security;
+ char *sp = current_security();
rc = cap_syslog(type);
if (rc != 0)
@@ -248,11 +250,12 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
/**
* smack_sb_kern_mount - Smack specific mount processing
* @sb: the file system superblock
+ * @flags: the mount flags
* @data: the smack mount options
*
* Returns 0 on success, an error code on failure
*/
-static int smack_sb_kern_mount(struct super_block *sb, void *data)
+static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
struct dentry *root = sb->s_root;
struct inode *inode = root->d_inode;
@@ -373,7 +376,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
*/
static int smack_inode_alloc_security(struct inode *inode)
{
- inode->i_security = new_inode_smack(current->security);
+ inode->i_security = new_inode_smack(current_security());
if (inode->i_security == NULL)
return -ENOMEM;
return 0;
@@ -818,7 +821,7 @@ static int smack_file_permission(struct file *file, int mask)
*/
static int smack_file_alloc_security(struct file *file)
{
- file->f_security = current->security;
+ file->f_security = current_security();
return 0;
}
@@ -916,7 +919,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd,
*/
static int smack_file_set_fowner(struct file *file)
{
- file->f_security = current->security;
+ file->f_security = current_security();
return 0;
}
@@ -941,7 +944,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
* struct fown_struct is never outside the context of a struct file
*/
file = container_of(fown, struct file, f_owner);
- rc = smk_access(file->f_security, tsk->security, MAY_WRITE);
+ rc = smk_access(file->f_security, tsk->cred->security, MAY_WRITE);
if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
return 0;
return rc;
@@ -973,33 +976,75 @@ static int smack_file_receive(struct file *file)
*/
/**
- * smack_task_alloc_security - "allocate" a task blob
- * @tsk: the task in need of a blob
+ * smack_cred_free - "free" task-level security credentials
+ * @cred: the credentials in question
*
* Smack isn't using copies of blobs. Everyone
- * points to an immutable list. No alloc required.
- * No data copy required.
+ * points to an immutable list. The blobs never go away.
+ * There is no leak here.
+ */
+static void smack_cred_free(struct cred *cred)
+{
+ cred->security = NULL;
+}
+
+/**
+ * smack_cred_prepare - prepare new set of credentials for modification
+ * @new: the new credentials
+ * @old: the original credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a new set of credentials for modification.
+ */
+static int smack_cred_prepare(struct cred *new, const struct cred *old,
+ gfp_t gfp)
+{
+ new->security = old->security;
+ return 0;
+}
+
+/*
+ * commit new credentials
+ * @new: the new credentials
+ * @old: the original credentials
+ */
+static void smack_cred_commit(struct cred *new, const struct cred *old)
+{
+}
+
+/**
+ * smack_kernel_act_as - Set the subjective context in a set of credentials
+ * @new points to the set of credentials to be modified.
+ * @secid specifies the security ID to be set
*
- * Always returns 0
+ * Set the security data for a kernel service.
*/
-static int smack_task_alloc_security(struct task_struct *tsk)
+static int smack_kernel_act_as(struct cred *new, u32 secid)
{
- tsk->security = current->security;
+ char *smack = smack_from_secid(secid);
+
+ if (smack == NULL)
+ return -EINVAL;
+ new->security = smack;
return 0;
}
/**
- * smack_task_free_security - "free" a task blob
- * @task: the task with the blob
+ * smack_kernel_create_files_as - Set the file creation label in a set of creds
+ * @new points to the set of credentials to be modified
+ * @inode points to the inode to use as a reference
*
- * Smack isn't using copies of blobs. Everyone
- * points to an immutable list. The blobs never go away.
- * There is no leak here.
+ * Set the file creation context in a set of credentials to the same
+ * as the objective context of the specified inode
*/
-static void smack_task_free_security(struct task_struct *task)
+static int smack_kernel_create_files_as(struct cred *new,
+ struct inode *inode)
{
- task->security = NULL;
+ struct inode_smack *isp = inode->i_security;
+
+ new->security = isp->smk_inode;
+ return 0;
}
/**
@@ -1011,7 +1056,7 @@ static void smack_task_free_security(struct task_struct *task)
*/
static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return smk_curacc(p->security, MAY_WRITE);
+ return smk_curacc(task_security(p), MAY_WRITE);
}
/**
@@ -1022,7 +1067,7 @@ static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
*/
static int smack_task_getpgid(struct task_struct *p)
{
- return smk_curacc(p->security, MAY_READ);
+ return smk_curacc(task_security(p), MAY_READ);
}
/**
@@ -1033,7 +1078,7 @@ static int smack_task_getpgid(struct task_struct *p)
*/
static int smack_task_getsid(struct task_struct *p)
{
- return smk_curacc(p->security, MAY_READ);
+ return smk_curacc(task_security(p), MAY_READ);
}
/**
@@ -1045,7 +1090,7 @@ static int smack_task_getsid(struct task_struct *p)
*/
static void smack_task_getsecid(struct task_struct *p, u32 *secid)
{
- *secid = smack_to_secid(p->security);
+ *secid = smack_to_secid(task_security(p));
}
/**
@@ -1061,7 +1106,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
rc = cap_task_setnice(p, nice);
if (rc == 0)
- rc = smk_curacc(p->security, MAY_WRITE);
+ rc = smk_curacc(task_security(p), MAY_WRITE);
return rc;
}
@@ -1078,7 +1123,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
rc = cap_task_setioprio(p, ioprio);
if (rc == 0)
- rc = smk_curacc(p->security, MAY_WRITE);
+ rc = smk_curacc(task_security(p), MAY_WRITE);
return rc;
}
@@ -1090,7 +1135,7 @@ static int smack_task_setioprio(struct task_struct *p, int ioprio)
*/
static int smack_task_getioprio(struct task_struct *p)
{
- return smk_curacc(p->security, MAY_READ);
+ return smk_curacc(task_security(p), MAY_READ);
}
/**
@@ -1108,7 +1153,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
rc = cap_task_setscheduler(p, policy, lp);
if (rc == 0)
- rc = smk_curacc(p->security, MAY_WRITE);
+ rc = smk_curacc(task_security(p), MAY_WRITE);
return rc;
}
@@ -1120,7 +1165,7 @@ static int smack_task_setscheduler(struct task_struct *p, int policy,
*/
static int smack_task_getscheduler(struct task_struct *p)
{
- return smk_curacc(p->security, MAY_READ);
+ return smk_curacc(task_security(p), MAY_READ);
}
/**
@@ -1131,7 +1176,7 @@ static int smack_task_getscheduler(struct task_struct *p)
*/
static int smack_task_movememory(struct task_struct *p)
{
- return smk_curacc(p->security, MAY_WRITE);
+ return smk_curacc(task_security(p), MAY_WRITE);
}
/**
@@ -1154,13 +1199,13 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
* can write the receiver.
*/
if (secid == 0)
- return smk_curacc(p->security, MAY_WRITE);
+ return smk_curacc(task_security(p), MAY_WRITE);
/*
* If the secid isn't 0 we're dealing with some USB IO
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
- return smk_access(smack_from_secid(secid), p->security, MAY_WRITE);
+ return smk_access(smack_from_secid(secid), task_security(p), MAY_WRITE);
}
/**
@@ -1173,7 +1218,7 @@ static int smack_task_wait(struct task_struct *p)
{
int rc;
- rc = smk_access(current->security, p->security, MAY_WRITE);
+ rc = smk_access(current_security(), task_security(p), MAY_WRITE);
if (rc == 0)
return 0;
@@ -1204,7 +1249,7 @@ static int smack_task_wait(struct task_struct *p)
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
{
struct inode_smack *isp = inode->i_security;
- isp->smk_inode = p->security;
+ isp->smk_inode = task_security(p);
}
/*
@@ -1223,7 +1268,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
*/
static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
{
- char *csp = current->security;
+ char *csp = current_security();
struct socket_smack *ssp;
ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
@@ -1448,7 +1493,7 @@ static int smack_flags_to_may(int flags)
*/
static int smack_msg_msg_alloc_security(struct msg_msg *msg)
{
- msg->security = current->security;
+ msg->security = current_security();
return 0;
}
@@ -1484,7 +1529,7 @@ static int smack_shm_alloc_security(struct shmid_kernel *shp)
{
struct kern_ipc_perm *isp = &shp->shm_perm;
- isp->security = current->security;
+ isp->security = current_security();
return 0;
}
@@ -1593,7 +1638,7 @@ static int smack_sem_alloc_security(struct sem_array *sma)
{
struct kern_ipc_perm *isp = &sma->sem_perm;
- isp->security = current->security;
+ isp->security = current_security();
return 0;
}
@@ -1697,7 +1742,7 @@ static int smack_msg_queue_alloc_security(struct msg_queue *msq)
{
struct kern_ipc_perm *kisp = &msq->q_perm;
- kisp->security = current->security;
+ kisp->security = current_security();
return 0;
}
@@ -1852,7 +1897,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
struct super_block *sbp;
struct superblock_smack *sbsp;
struct inode_smack *isp;
- char *csp = current->security;
+ char *csp = current_security();
char *fetched;
char *final;
struct dentry *dp;
@@ -2009,7 +2054,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
if (strcmp(name, "current") != 0)
return -EINVAL;
- cp = kstrdup(p->security, GFP_KERNEL);
+ cp = kstrdup(task_security(p), GFP_KERNEL);
if (cp == NULL)
return -ENOMEM;
@@ -2033,6 +2078,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
+ struct cred *new;
char *newsmack;
/*
@@ -2055,7 +2101,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (newsmack == NULL)
return -EINVAL;
- p->security = newsmack;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ new->security = newsmack;
+ commit_creds(new);
return size;
}
@@ -2288,8 +2338,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
return;
ssp = sk->sk_security;
- ssp->smk_in = current->security;
- ssp->smk_out = current->security;
+ ssp->smk_in = ssp->smk_out = current_security();
ssp->smk_packet[0] = '\0';
rc = smack_netlabel(sk);
@@ -2352,17 +2401,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
/**
* smack_key_alloc - Set the key security blob
* @key: object
- * @tsk: the task associated with the key
+ * @cred: the credentials to use
* @flags: unused
*
* No allocation required
*
* Returns 0
*/
-static int smack_key_alloc(struct key *key, struct task_struct *tsk,
+static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- key->security = tsk->security;
+ key->security = cred->security;
return 0;
}
@@ -2380,14 +2429,14 @@ static void smack_key_free(struct key *key)
/*
* smack_key_permission - Smack access on a key
* @key_ref: gets to the object
- * @context: task involved
+ * @cred: the credentials to use
* @perm: unused
*
* Return 0 if the task has read and write to the object,
* an error code otherwise
*/
static int smack_key_permission(key_ref_t key_ref,
- struct task_struct *context, key_perm_t perm)
+ const struct cred *cred, key_perm_t perm)
{
struct key *keyp;
@@ -2403,10 +2452,10 @@ static int smack_key_permission(key_ref_t key_ref,
/*
* This should not occur
*/
- if (context->security == NULL)
+ if (cred->security == NULL)
return -EACCES;
- return smk_access(context->security, keyp->security, MAY_READWRITE);
+ return smk_access(cred->security, keyp->security, MAY_READWRITE);
}
#endif /* CONFIG_KEYS */
@@ -2577,15 +2626,13 @@ struct security_operations smack_ops = {
.ptrace_may_access = smack_ptrace_may_access,
.ptrace_traceme = smack_ptrace_traceme,
.capget = cap_capget,
- .capset_check = cap_capset_check,
- .capset_set = cap_capset_set,
+ .capset = cap_capset,
.capable = cap_capable,
.syslog = smack_syslog,
.settime = cap_settime,
.vm_enough_memory = cap_vm_enough_memory,
- .bprm_apply_creds = cap_bprm_apply_creds,
- .bprm_set_security = cap_bprm_set_security,
+ .bprm_set_creds = cap_bprm_set_creds,
.bprm_secureexec = cap_bprm_secureexec,
.sb_alloc_security = smack_sb_alloc_security,
@@ -2627,9 +2674,12 @@ struct security_operations smack_ops = {
.file_send_sigiotask = smack_file_send_sigiotask,
.file_receive = smack_file_receive,
- .task_alloc_security = smack_task_alloc_security,
- .task_free_security = smack_task_free_security,
- .task_post_setuid = cap_task_post_setuid,
+ .cred_free = smack_cred_free,
+ .cred_prepare = smack_cred_prepare,
+ .cred_commit = smack_cred_commit,
+ .kernel_act_as = smack_kernel_act_as,
+ .kernel_create_files_as = smack_kernel_create_files_as,
+ .task_fix_setuid = cap_task_fix_setuid,
.task_setpgid = smack_task_setpgid,
.task_getpgid = smack_task_getpgid,
.task_getsid = smack_task_getsid,
@@ -2642,7 +2692,6 @@ struct security_operations smack_ops = {
.task_movememory = smack_task_movememory,
.task_kill = smack_task_kill,
.task_wait = smack_task_wait,
- .task_reparent_to_init = cap_task_reparent_to_init,
.task_to_inode = smack_task_to_inode,
.task_prctl = cap_task_prctl,
@@ -2718,6 +2767,8 @@ struct security_operations smack_ops = {
*/
static __init int smack_init(void)
{
+ struct cred *cred;
+
if (!security_module_enable(&smack_ops))
return 0;
@@ -2726,7 +2777,8 @@ static __init int smack_init(void)
/*
* Set the security state for the initial task.
*/
- current->security = &smack_known_floor.smk_known;
+ cred = (struct cred *) current->cred;
+ cred->security = &smack_known_floor.smk_known;
/*
* Initialize locks
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index c21d8c8bf0c7..247dc9ebbc71 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -185,11 +185,15 @@ static int smk_open_load(struct inode *inode, struct file *file)
* the subject/object pair and replaces the access that was
* there. If the pair isn't found add it with the specified
* access.
+ *
+ * Returns 0 if nothing goes wrong or -ENOMEM if it fails
+ * during the allocation of the new pair to add.
*/
-static void smk_set_access(struct smack_rule *srp)
+static int smk_set_access(struct smack_rule *srp)
{
struct smk_list_entry *sp;
struct smk_list_entry *newp;
+ int ret = 0;
mutex_lock(&smack_list_lock);
@@ -202,14 +206,20 @@ static void smk_set_access(struct smack_rule *srp)
if (sp == NULL) {
newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL);
+ if (newp == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
newp->smk_rule = *srp;
newp->smk_next = smack_list;
smack_list = newp;
}
+out:
mutex_unlock(&smack_list_lock);
- return;
+ return ret;
}
/**
@@ -309,8 +319,10 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf,
goto out;
}
- smk_set_access(&rule);
- rc = count;
+ rc = smk_set_access(&rule);
+
+ if (!rc)
+ rc = count;
out:
kfree(data);
@@ -336,7 +348,7 @@ static void smk_cipso_doi(void)
audit_info.loginuid = audit_get_loginuid(current);
audit_info.sessionid = audit_get_sessionid(current);
- audit_info.secid = smack_to_secid(current->security);
+ audit_info.secid = smack_to_secid(current_security());
rc = netlbl_cfg_map_del(NULL, &audit_info);
if (rc != 0)
@@ -371,7 +383,7 @@ static void smk_unlbl_ambient(char *oldambient)
audit_info.loginuid = audit_get_loginuid(current);
audit_info.sessionid = audit_get_sessionid(current);
- audit_info.secid = smack_to_secid(current->security);
+ audit_info.secid = smack_to_secid(current_security());
if (oldambient != NULL) {
rc = netlbl_cfg_map_del(oldambient, &audit_info);
@@ -843,7 +855,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char in[SMK_LABELLEN];
- char *sp = current->security;
+ char *sp = current->cred->security;
if (!capable(CAP_MAC_ADMIN))
return -EPERM;
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index 7fa37e15f196..a351dd0a09c7 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
+#include <sound/ac97_codec.h>
/*
* Let drivers decide whether they want to support given codec from their
diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile
index 31cbe68fd42f..c3ee77fc4b2d 100644
--- a/sound/aoa/codecs/Makefile
+++ b/sound/aoa/codecs/Makefile
@@ -1,3 +1,7 @@
+snd-aoa-codec-onyx-objs := onyx.o
+snd-aoa-codec-tas-objs := tas.o
+snd-aoa-codec-toonie-objs := toonie.o
+
obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
new file mode 100644
index 000000000000..15500b9d2da0
--- /dev/null
+++ b/sound/aoa/codecs/onyx.c
@@ -0,0 +1,1118 @@
+/*
+ * Apple Onboard Audio driver for Onyx codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This is a driver for the pcm3052 codec chip (codenamed Onyx)
+ * that is present in newer Apple hardware (with digital output).
+ *
+ * The Onyx codec has the following connections (listed by the bit
+ * to be used in aoa_codec.connected):
+ * 0: analog output
+ * 1: digital output
+ * 2: line input
+ * 3: microphone input
+ * Note that even though I know of no machine that has for example
+ * the digital output connected but not the analog, I have handled
+ * all the different cases in the code so that this driver may serve
+ * as a good example of what to do.
+ *
+ * NOTE: This driver assumes that there's at most one chip to be
+ * used with one alsa card, in form of creating all kinds
+ * of mixer elements without regard for their existence.
+ * But snd-aoa assumes that there's at most one card, so
+ * this means you can only have one onyx on a system. This
+ * should probably be fixed by changing the assumption of
+ * having just a single card on a system, and making the
+ * 'card' pointer accessible to anyone who needs it instead
+ * of hiding it in the aoa_snd_* functions...
+ *
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
+
+#include "onyx.h"
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-onyx: "
+
+struct onyx {
+ /* cache registers 65 to 80, they are write-only! */
+ u8 cache[16];
+ struct i2c_client i2c;
+ struct aoa_codec codec;
+ u32 initialised:1,
+ spdif_locked:1,
+ analog_locked:1,
+ original_mute:2;
+ int open_count;
+ struct codec_info *codec_info;
+
+ /* mutex serializes concurrent access to the device
+ * and this structure.
+ */
+ struct mutex mutex;
+};
+#define codec_to_onyx(c) container_of(c, struct onyx, codec)
+
+/* both return 0 if all ok, else on error */
+static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
+{
+ s32 v;
+
+ if (reg != ONYX_REG_CONTROL) {
+ *value = onyx->cache[reg-FIRSTREGISTER];
+ return 0;
+ }
+ v = i2c_smbus_read_byte_data(&onyx->i2c, reg);
+ if (v < 0)
+ return -1;
+ *value = (u8)v;
+ onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
+ return 0;
+}
+
+static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value)
+{
+ int result;
+
+ result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value);
+ if (!result)
+ onyx->cache[reg-FIRSTREGISTER] = value;
+ return result;
+}
+
+/* alsa stuff */
+
+static int onyx_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = onyx_dev_register,
+};
+
+/* this is necessary because most alsa mixer programs
+ * can't properly handle the negative range */
+#define VOLUME_RANGE_SHIFT 128
+
+static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT;
+ uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT;
+ return 0;
+}
+
+static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 l, r;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
+ ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
+
+ return 0;
+}
+
+static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 l, r;
+
+ if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT ||
+ ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT)
+ return -EINVAL;
+ if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT ||
+ ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
+ return -EINVAL;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
+ onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
+
+ if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
+ r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&onyx->mutex);
+ return 0;
+ }
+
+ onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
+ ucontrol->value.integer.value[0]
+ - VOLUME_RANGE_SHIFT);
+ onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
+ ucontrol->value.integer.value[1]
+ - VOLUME_RANGE_SHIFT);
+ mutex_unlock(&onyx->mutex);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new volume_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_vol_info,
+ .get = onyx_snd_vol_get,
+ .put = onyx_snd_vol_put,
+};
+
+/* like above, this is necessary because a lot
+ * of alsa mixer programs don't handle ranges
+ * that don't start at 0 properly.
+ * even alsamixer is one of them... */
+#define INPUTGAIN_RANGE_SHIFT (-3)
+
+static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT;
+ uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT;
+ return 0;
+}
+
+static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 ig;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] =
+ (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
+
+ return 0;
+}
+
+static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v, n;
+
+ if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
+ ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
+ return -EINVAL;
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ n = v;
+ n &= ~ONYX_ADC_PGA_GAIN_MASK;
+ n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
+ & ONYX_ADC_PGA_GAIN_MASK;
+ onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
+ mutex_unlock(&onyx->mutex);
+
+ return n != v;
+}
+
+static struct snd_kcontrol_new inputgain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_inputgain_info,
+ .get = onyx_snd_inputgain_get,
+ .put = onyx_snd_inputgain_put,
+};
+
+static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Line-In", "Microphone" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ s8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
+
+ return 0;
+}
+
+static void onyx_set_capture_source(struct onyx *onyx, int mic)
+{
+ s8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
+ v &= ~ONYX_ADC_INPUT_MIC;
+ if (mic)
+ v |= ONYX_ADC_INPUT_MIC;
+ onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
+ mutex_unlock(&onyx->mutex);
+}
+
+static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
+ ucontrol->value.enumerated.item[0]);
+ return 1;
+}
+
+static struct snd_kcontrol_new capture_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* If we name this 'Input Source', it properly shows up in
+ * alsamixer as a selection, * but it's shown under the
+ * 'Playback' category.
+ * If I name it 'Capture Source', it shows up in strange
+ * ways (two bools of which one can be selected at a
+ * time) but at least it's shown in the 'Capture'
+ * category.
+ * I was told that this was due to backward compatibility,
+ * but I don't understand then why the mangling is *not*
+ * done when I name it "Input Source".....
+ */
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_capture_source_info,
+ .get = onyx_snd_capture_source_get,
+ .put = onyx_snd_capture_source_put,
+};
+
+#define onyx_snd_mute_info snd_ctl_boolean_stereo_info
+
+static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 c;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
+ ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
+
+ return 0;
+}
+
+static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v = 0, c = 0;
+ int err = -EBUSY;
+
+ mutex_lock(&onyx->mutex);
+ if (onyx->analog_locked)
+ goto out_unlock;
+
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ c = v;
+ c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT);
+ if (!ucontrol->value.integer.value[0])
+ c |= ONYX_MUTE_LEFT;
+ if (!ucontrol->value.integer.value[1])
+ c |= ONYX_MUTE_RIGHT;
+ err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return !err ? (v != c) : err;
+}
+
+static struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = onyx_snd_mute_info,
+ .get = onyx_snd_mute_get,
+ .put = onyx_snd_mute_put,
+};
+
+
+#define onyx_snd_single_bit_info snd_ctl_boolean_mono_info
+
+#define FLAG_POLARITY_INVERT 1
+#define FLAG_SPDIFLOCK 2
+
+static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 c;
+ long int pv = kcontrol->private_value;
+ u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
+ u8 address = (pv >> 8) & 0xff;
+ u8 mask = pv & 0xff;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, address, &c);
+ mutex_unlock(&onyx->mutex);
+
+ ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
+
+ return 0;
+}
+
+static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v = 0, c = 0;
+ int err;
+ long int pv = kcontrol->private_value;
+ u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
+ u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK;
+ u8 address = (pv >> 8) & 0xff;
+ u8 mask = pv & 0xff;
+
+ mutex_lock(&onyx->mutex);
+ if (spdiflock && onyx->spdif_locked) {
+ /* even if alsamixer doesn't care.. */
+ err = -EBUSY;
+ goto out_unlock;
+ }
+ onyx_read_register(onyx, address, &v);
+ c = v;
+ c &= ~(mask);
+ if (!!ucontrol->value.integer.value[0] ^ polarity)
+ c |= mask;
+ err = onyx_write_register(onyx, address, c);
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return !err ? (v != c) : err;
+}
+
+#define SINGLE_BIT(n, type, description, address, mask, flags) \
+static struct snd_kcontrol_new n##_control = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_##type, \
+ .name = description, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = onyx_snd_single_bit_info, \
+ .get = onyx_snd_single_bit_get, \
+ .put = onyx_snd_single_bit_put, \
+ .private_value = (flags << 16) | (address << 8) | mask \
+}
+
+SINGLE_BIT(spdif,
+ MIXER,
+ SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ ONYX_REG_DIG_INFO4,
+ ONYX_SPDIF_ENABLE,
+ FLAG_SPDIFLOCK);
+SINGLE_BIT(ovr1,
+ MIXER,
+ "Oversampling Rate",
+ ONYX_REG_DAC_CONTROL,
+ ONYX_OVR1,
+ 0);
+SINGLE_BIT(flt0,
+ MIXER,
+ "Fast Digital Filter Rolloff",
+ ONYX_REG_DAC_FILTER,
+ ONYX_ROLLOFF_FAST,
+ FLAG_POLARITY_INVERT);
+SINGLE_BIT(hpf,
+ MIXER,
+ "Highpass Filter",
+ ONYX_REG_ADC_HPF_BYPASS,
+ ONYX_HPF_DISABLE,
+ FLAG_POLARITY_INVERT);
+SINGLE_BIT(dm12,
+ MIXER,
+ "Digital De-Emphasis",
+ ONYX_REG_DAC_DEEMPH,
+ ONYX_DIGDEEMPH_CTRL,
+ 0);
+
+static int onyx_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* datasheet page 30, all others are 0 */
+ ucontrol->value.iec958.status[0] = 0x3e;
+ ucontrol->value.iec958.status[1] = 0xff;
+
+ ucontrol->value.iec958.status[3] = 0x3f;
+ ucontrol->value.iec958.status[4] = 0x0f;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new onyx_spdif_mask = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+ .info = onyx_spdif_info,
+ .get = onyx_spdif_mask_get,
+};
+
+static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
+ ucontrol->value.iec958.status[0] = v & 0x3e;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v);
+ ucontrol->value.iec958.status[1] = v;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
+ ucontrol->value.iec958.status[3] = v & 0x3f;
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ ucontrol->value.iec958.status[4] = v & 0x0f;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct onyx *onyx = snd_kcontrol_chip(kcontrol);
+ u8 v;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
+ v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
+
+ v = ucontrol->value.iec958.status[1];
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v);
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
+ v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v);
+
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
+ mutex_unlock(&onyx->mutex);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new onyx_spdif_ctrl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+ .info = onyx_spdif_info,
+ .get = onyx_spdif_get,
+ .put = onyx_spdif_put,
+};
+
+/* our registers */
+
+static u8 register_map[] = {
+ ONYX_REG_DAC_ATTEN_LEFT,
+ ONYX_REG_DAC_ATTEN_RIGHT,
+ ONYX_REG_CONTROL,
+ ONYX_REG_DAC_CONTROL,
+ ONYX_REG_DAC_DEEMPH,
+ ONYX_REG_DAC_FILTER,
+ ONYX_REG_DAC_OUTPHASE,
+ ONYX_REG_ADC_CONTROL,
+ ONYX_REG_ADC_HPF_BYPASS,
+ ONYX_REG_DIG_INFO1,
+ ONYX_REG_DIG_INFO2,
+ ONYX_REG_DIG_INFO3,
+ ONYX_REG_DIG_INFO4
+};
+
+static u8 initial_values[ARRAY_SIZE(register_map)] = {
+ 0x80, 0x80, /* muted */
+ ONYX_MRST | ONYX_SRST, /* but handled specially! */
+ ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
+ 0, /* no deemphasis */
+ ONYX_DAC_FILTER_ALWAYS,
+ ONYX_OUTPHASE_INVERTED,
+ (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/
+ ONYX_ADC_HPF_ALWAYS,
+ (1<<2), /* pcm audio */
+ 2, /* category: pcm coder */
+ 0, /* sampling frequency 44.1 kHz, clock accuracy level II */
+ 1 /* 24 bit depth */
+};
+
+/* reset registers of chip, either to initial or to previous values */
+static int onyx_register_init(struct onyx *onyx)
+{
+ int i;
+ u8 val;
+ u8 regs[sizeof(initial_values)];
+
+ if (!onyx->initialised) {
+ memcpy(regs, initial_values, sizeof(initial_values));
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val))
+ return -1;
+ val &= ~ONYX_SILICONVERSION;
+ val |= initial_values[3];
+ regs[3] = val;
+ } else {
+ for (i=0; i<sizeof(register_map); i++)
+ regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER];
+ }
+
+ for (i=0; i<sizeof(register_map); i++) {
+ if (onyx_write_register(onyx, register_map[i], regs[i]))
+ return -1;
+ }
+ onyx->initialised = 1;
+ return 0;
+}
+
+static struct transfer_info onyx_transfers[] = {
+ /* this is first so we can skip it if no input is present...
+ * No hardware exists with that, but it's here as an example
+ * of what to do :) */
+ {
+ /* analog input */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .transfer_in = 1,
+ .must_be_clock_source = 0,
+ .tag = 0,
+ },
+ {
+ /* if analog and digital are currently off, anything should go,
+ * so this entry describes everything we can do... */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+ | SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+#endif
+ ,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .tag = 0,
+ },
+ {
+ /* analog output */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .transfer_in = 0,
+ .must_be_clock_source = 0,
+ .tag = 1,
+ },
+ {
+ /* digital pcm output, also possible for analog out */
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .transfer_in = 0,
+ .must_be_clock_source = 0,
+ .tag = 2,
+ },
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+ /* Once alsa gets supports for this kind of thing we can add it... */
+ {
+ /* digital compressed output */
+ .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000,
+ .tag = 2,
+ },
+#endif
+ {}
+};
+
+static int onyx_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ u8 v;
+ struct onyx *onyx = cii->codec_data;
+ int spdif_enabled, analog_enabled;
+
+ mutex_lock(&onyx->mutex);
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ analog_enabled =
+ (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
+ != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
+ mutex_unlock(&onyx->mutex);
+
+ switch (ti->tag) {
+ case 0: return 1;
+ case 1: return analog_enabled;
+ case 2: return spdif_enabled;
+ }
+ return 1;
+}
+
+static int onyx_prepare(struct codec_info_item *cii,
+ struct bus_info *bi,
+ struct snd_pcm_substream *substream)
+{
+ u8 v;
+ struct onyx *onyx = cii->codec_data;
+ int err = -EBUSY;
+
+ mutex_lock(&onyx->mutex);
+
+#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+ if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
+ /* mute and lock analog output */
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+ if (onyx_write_register(onyx,
+ ONYX_REG_DAC_CONTROL,
+ v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
+ goto out_unlock;
+ onyx->analog_locked = 1;
+ err = 0;
+ goto out_unlock;
+ }
+#endif
+ switch (substream->runtime->rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ /* these rates are ok for all outputs */
+ /* FIXME: program spdif channel control bits here so that
+ * userspace doesn't have to if it only plays pcm! */
+ err = 0;
+ goto out_unlock;
+ default:
+ /* got some rate that the digital output can't do,
+ * so disable and lock it */
+ onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v);
+ if (onyx_write_register(onyx,
+ ONYX_REG_DIG_INFO4,
+ v & ~ONYX_SPDIF_ENABLE))
+ goto out_unlock;
+ onyx->spdif_locked = 1;
+ err = 0;
+ goto out_unlock;
+ }
+
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+static int onyx_open(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ onyx->open_count++;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_close(struct codec_info_item *cii,
+ struct snd_pcm_substream *substream)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ onyx->open_count--;
+ if (!onyx->open_count)
+ onyx->spdif_locked = onyx->analog_locked = 0;
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+static int onyx_switch_clock(struct codec_info_item *cii,
+ enum clock_switch what)
+{
+ struct onyx *onyx = cii->codec_data;
+
+ mutex_lock(&onyx->mutex);
+ /* this *MUST* be more elaborate later... */
+ switch (what) {
+ case CLOCK_SWITCH_PREPARE_SLAVE:
+ onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio);
+ break;
+ case CLOCK_SWITCH_SLAVE:
+ onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio);
+ break;
+ default: /* silence warning */
+ break;
+ }
+ mutex_unlock(&onyx->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ struct onyx *onyx = cii->codec_data;
+ u8 v;
+ int err = -ENXIO;
+
+ mutex_lock(&onyx->mutex);
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
+ goto out_unlock;
+ onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
+ /* Apple does a sleep here but the datasheet says to do it on resume */
+ err = 0;
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+static int onyx_resume(struct codec_info_item *cii)
+{
+ struct onyx *onyx = cii->codec_data;
+ u8 v;
+ int err = -ENXIO;
+
+ mutex_lock(&onyx->mutex);
+
+ /* reset codec */
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+
+ /* take codec out of suspend (if it still is after reset) */
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
+ goto out_unlock;
+ onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
+ /* FIXME: should divide by sample rate, but 8k is the lowest we go */
+ msleep(2205000/8000);
+ /* reset all values */
+ onyx_register_init(onyx);
+ err = 0;
+ out_unlock:
+ mutex_unlock(&onyx->mutex);
+
+ return err;
+}
+
+#endif /* CONFIG_PM */
+
+static struct codec_info onyx_codec_info = {
+ .transfers = onyx_transfers,
+ .sysclock_factor = 256,
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+ .usable = onyx_usable,
+ .prepare = onyx_prepare,
+ .open = onyx_open,
+ .close = onyx_close,
+ .switch_clock = onyx_switch_clock,
+#ifdef CONFIG_PM
+ .suspend = onyx_suspend,
+ .resume = onyx_resume,
+#endif
+};
+
+static int onyx_init_codec(struct aoa_codec *codec)
+{
+ struct onyx *onyx = codec_to_onyx(codec);
+ struct snd_kcontrol *ctl;
+ struct codec_info *ci = &onyx_codec_info;
+ u8 v;
+ int err;
+
+ if (!onyx->codec.gpio || !onyx->codec.gpio->methods) {
+ printk(KERN_ERR PFX "gpios not assigned!!\n");
+ return -EINVAL;
+ }
+
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
+ msleep(1);
+ onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
+ msleep(1);
+
+ if (onyx_register_init(onyx)) {
+ printk(KERN_ERR PFX "failed to initialise onyx registers\n");
+ return -ENODEV;
+ }
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) {
+ printk(KERN_ERR PFX "failed to create onyx snd device!\n");
+ return -ENODEV;
+ }
+
+ /* nothing connected? what a joke! */
+ if ((onyx->codec.connected & 0xF) == 0)
+ return -ENOTCONN;
+
+ /* if no inputs are present... */
+ if ((onyx->codec.connected & 0xC) == 0) {
+ if (!onyx->codec_info)
+ onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
+ if (!onyx->codec_info)
+ return -ENOMEM;
+ ci = onyx->codec_info;
+ *ci = onyx_codec_info;
+ ci->transfers++;
+ }
+
+ /* if no outputs are present... */
+ if ((onyx->codec.connected & 3) == 0) {
+ if (!onyx->codec_info)
+ onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
+ if (!onyx->codec_info)
+ return -ENOMEM;
+ ci = onyx->codec_info;
+ /* this is fine as there have to be inputs
+ * if we end up in this part of the code */
+ *ci = onyx_codec_info;
+ ci->transfers[1].formats = 0;
+ }
+
+ if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev,
+ aoa_get_card(),
+ ci, onyx)) {
+ printk(KERN_ERR PFX "error creating onyx pcm\n");
+ return -ENODEV;
+ }
+#define ADDCTL(n) \
+ do { \
+ ctl = snd_ctl_new1(&n, onyx); \
+ if (ctl) { \
+ ctl->id.device = \
+ onyx->codec.soundbus_dev->pcm->device; \
+ err = aoa_snd_ctl_add(ctl); \
+ if (err) \
+ goto error; \
+ } \
+ } while (0)
+
+ if (onyx->codec.soundbus_dev->pcm) {
+ /* give the user appropriate controls
+ * depending on what inputs are connected */
+ if ((onyx->codec.connected & 0xC) == 0xC)
+ ADDCTL(capture_source_control);
+ else if (onyx->codec.connected & 4)
+ onyx_set_capture_source(onyx, 0);
+ else
+ onyx_set_capture_source(onyx, 1);
+ if (onyx->codec.connected & 0xC)
+ ADDCTL(inputgain_control);
+
+ /* depending on what output is connected,
+ * give the user appropriate controls */
+ if (onyx->codec.connected & 1) {
+ ADDCTL(volume_control);
+ ADDCTL(mute_control);
+ ADDCTL(ovr1_control);
+ ADDCTL(flt0_control);
+ ADDCTL(hpf_control);
+ ADDCTL(dm12_control);
+ /* spdif control defaults to off */
+ }
+ if (onyx->codec.connected & 2) {
+ ADDCTL(onyx_spdif_mask);
+ ADDCTL(onyx_spdif_ctrl);
+ }
+ if ((onyx->codec.connected & 3) == 3)
+ ADDCTL(spdif_control);
+ /* if only S/PDIF is connected, enable it unconditionally */
+ if ((onyx->codec.connected & 3) == 2) {
+ onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
+ v |= ONYX_SPDIF_ENABLE;
+ onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
+ }
+ }
+#undef ADDCTL
+ printk(KERN_INFO PFX "attached to onyx codec via i2c\n");
+
+ return 0;
+ error:
+ onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
+ snd_device_free(aoa_get_card(), onyx);
+ return err;
+}
+
+static void onyx_exit_codec(struct aoa_codec *codec)
+{
+ struct onyx *onyx = codec_to_onyx(codec);
+
+ if (!onyx->codec.soundbus_dev) {
+ printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n");
+ return;
+ }
+ onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
+}
+
+static struct i2c_driver onyx_driver;
+
+static int onyx_create(struct i2c_adapter *adapter,
+ struct device_node *node,
+ int addr)
+{
+ struct onyx *onyx;
+ u8 dummy;
+
+ onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL);
+
+ if (!onyx)
+ return -ENOMEM;
+
+ mutex_init(&onyx->mutex);
+ onyx->i2c.driver = &onyx_driver;
+ onyx->i2c.adapter = adapter;
+ onyx->i2c.addr = addr & 0x7f;
+ strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE);
+
+ if (i2c_attach_client(&onyx->i2c)) {
+ printk(KERN_ERR PFX "failed to attach to i2c\n");
+ goto fail;
+ }
+
+ /* we try to read from register ONYX_REG_CONTROL
+ * to check if the codec is present */
+ if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) {
+ i2c_detach_client(&onyx->i2c);
+ printk(KERN_ERR PFX "failed to read control register\n");
+ goto fail;
+ }
+
+ strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
+ onyx->codec.owner = THIS_MODULE;
+ onyx->codec.init = onyx_init_codec;
+ onyx->codec.exit = onyx_exit_codec;
+ onyx->codec.node = of_node_get(node);
+
+ if (aoa_codec_register(&onyx->codec)) {
+ i2c_detach_client(&onyx->i2c);
+ goto fail;
+ }
+ printk(KERN_DEBUG PFX "created and attached onyx instance\n");
+ return 0;
+ fail:
+ kfree(onyx);
+ return -EINVAL;
+}
+
+static int onyx_i2c_attach(struct i2c_adapter *adapter)
+{
+ struct device_node *busnode, *dev = NULL;
+ struct pmac_i2c_bus *bus;
+
+ bus = pmac_i2c_adapter_to_bus(adapter);
+ if (bus == NULL)
+ return -ENODEV;
+ busnode = pmac_i2c_get_bus_node(bus);
+
+ while ((dev = of_get_next_child(busnode, dev)) != NULL) {
+ if (of_device_is_compatible(dev, "pcm3052")) {
+ const u32 *addr;
+ printk(KERN_DEBUG PFX "found pcm3052\n");
+ addr = of_get_property(dev, "reg", NULL);
+ if (!addr)
+ return -ENODEV;
+ return onyx_create(adapter, dev, (*addr)>>1);
+ }
+ }
+
+ /* if that didn't work, try desperate mode for older
+ * machines that have stuff missing from the device tree */
+
+ if (!of_device_is_compatible(busnode, "k2-i2c"))
+ return -ENODEV;
+
+ printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n");
+ /* probe both possible addresses for the onyx chip */
+ if (onyx_create(adapter, NULL, 0x46) == 0)
+ return 0;
+ return onyx_create(adapter, NULL, 0x47);
+}
+
+static int onyx_i2c_detach(struct i2c_client *client)
+{
+ struct onyx *onyx = container_of(client, struct onyx, i2c);
+ int err;
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+ aoa_codec_unregister(&onyx->codec);
+ of_node_put(onyx->codec.node);
+ if (onyx->codec_info)
+ kfree(onyx->codec_info);
+ kfree(onyx);
+ return 0;
+}
+
+static struct i2c_driver onyx_driver = {
+ .driver = {
+ .name = "aoa_codec_onyx",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = onyx_i2c_attach,
+ .detach_client = onyx_i2c_detach,
+};
+
+static int __init onyx_init(void)
+{
+ return i2c_add_driver(&onyx_driver);
+}
+
+static void __exit onyx_exit(void)
+{
+ i2c_del_driver(&onyx_driver);
+}
+
+module_init(onyx_init);
+module_exit(onyx_exit);
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.h b/sound/aoa/codecs/onyx.h
index ffd20254ff76..ffd20254ff76 100644
--- a/sound/aoa/codecs/snd-aoa-codec-onyx.h
+++ b/sound/aoa/codecs/onyx.h
diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c
deleted file mode 100644
index 6a3837d480e5..000000000000
--- a/sound/aoa/codecs/snd-aoa-codec-onyx.c
+++ /dev/null
@@ -1,1118 +0,0 @@
-/*
- * Apple Onboard Audio driver for Onyx codec
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- *
- *
- * This is a driver for the pcm3052 codec chip (codenamed Onyx)
- * that is present in newer Apple hardware (with digital output).
- *
- * The Onyx codec has the following connections (listed by the bit
- * to be used in aoa_codec.connected):
- * 0: analog output
- * 1: digital output
- * 2: line input
- * 3: microphone input
- * Note that even though I know of no machine that has for example
- * the digital output connected but not the analog, I have handled
- * all the different cases in the code so that this driver may serve
- * as a good example of what to do.
- *
- * NOTE: This driver assumes that there's at most one chip to be
- * used with one alsa card, in form of creating all kinds
- * of mixer elements without regard for their existence.
- * But snd-aoa assumes that there's at most one card, so
- * this means you can only have one onyx on a system. This
- * should probably be fixed by changing the assumption of
- * having just a single card on a system, and making the
- * 'card' pointer accessible to anyone who needs it instead
- * of hiding it in the aoa_snd_* functions...
- *
- */
-#include <linux/delay.h>
-#include <linux/module.h>
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa");
-
-#include "snd-aoa-codec-onyx.h"
-#include "../aoa.h"
-#include "../soundbus/soundbus.h"
-
-
-#define PFX "snd-aoa-codec-onyx: "
-
-struct onyx {
- /* cache registers 65 to 80, they are write-only! */
- u8 cache[16];
- struct i2c_client i2c;
- struct aoa_codec codec;
- u32 initialised:1,
- spdif_locked:1,
- analog_locked:1,
- original_mute:2;
- int open_count;
- struct codec_info *codec_info;
-
- /* mutex serializes concurrent access to the device
- * and this structure.
- */
- struct mutex mutex;
-};
-#define codec_to_onyx(c) container_of(c, struct onyx, codec)
-
-/* both return 0 if all ok, else on error */
-static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
-{
- s32 v;
-
- if (reg != ONYX_REG_CONTROL) {
- *value = onyx->cache[reg-FIRSTREGISTER];
- return 0;
- }
- v = i2c_smbus_read_byte_data(&onyx->i2c, reg);
- if (v < 0)
- return -1;
- *value = (u8)v;
- onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
- return 0;
-}
-
-static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value)
-{
- int result;
-
- result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value);
- if (!result)
- onyx->cache[reg-FIRSTREGISTER] = value;
- return result;
-}
-
-/* alsa stuff */
-
-static int onyx_dev_register(struct snd_device *dev)
-{
- return 0;
-}
-
-static struct snd_device_ops ops = {
- .dev_register = onyx_dev_register,
-};
-
-/* this is necessary because most alsa mixer programs
- * can't properly handle the negative range */
-#define VOLUME_RANGE_SHIFT 128
-
-static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT;
- uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT;
- return 0;
-}
-
-static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- s8 l, r;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
- onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
- mutex_unlock(&onyx->mutex);
-
- ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
- ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
-
- return 0;
-}
-
-static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- s8 l, r;
-
- if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT ||
- ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT)
- return -EINVAL;
- if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT ||
- ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
- return -EINVAL;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
- onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
-
- if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
- r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
- mutex_unlock(&onyx->mutex);
- return 0;
- }
-
- onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
- ucontrol->value.integer.value[0]
- - VOLUME_RANGE_SHIFT);
- onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
- ucontrol->value.integer.value[1]
- - VOLUME_RANGE_SHIFT);
- mutex_unlock(&onyx->mutex);
-
- return 1;
-}
-
-static struct snd_kcontrol_new volume_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = onyx_snd_vol_info,
- .get = onyx_snd_vol_get,
- .put = onyx_snd_vol_put,
-};
-
-/* like above, this is necessary because a lot
- * of alsa mixer programs don't handle ranges
- * that don't start at 0 properly.
- * even alsamixer is one of them... */
-#define INPUTGAIN_RANGE_SHIFT (-3)
-
-static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT;
- uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT;
- return 0;
-}
-
-static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 ig;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
- mutex_unlock(&onyx->mutex);
-
- ucontrol->value.integer.value[0] =
- (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
-
- return 0;
-}
-
-static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 v, n;
-
- if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
- ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
- return -EINVAL;
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
- n = v;
- n &= ~ONYX_ADC_PGA_GAIN_MASK;
- n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
- & ONYX_ADC_PGA_GAIN_MASK;
- onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
- mutex_unlock(&onyx->mutex);
-
- return n != v;
-}
-
-static struct snd_kcontrol_new inputgain_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Capture Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = onyx_snd_inputgain_info,
- .get = onyx_snd_inputgain_get,
- .put = onyx_snd_inputgain_put,
-};
-
-static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = { "Line-In", "Microphone" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- s8 v;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
- mutex_unlock(&onyx->mutex);
-
- ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
-
- return 0;
-}
-
-static void onyx_set_capture_source(struct onyx *onyx, int mic)
-{
- s8 v;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
- v &= ~ONYX_ADC_INPUT_MIC;
- if (mic)
- v |= ONYX_ADC_INPUT_MIC;
- onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
- mutex_unlock(&onyx->mutex);
-}
-
-static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- if (ucontrol->value.enumerated.item[0] > 1)
- return -EINVAL;
- onyx_set_capture_source(snd_kcontrol_chip(kcontrol),
- ucontrol->value.enumerated.item[0]);
- return 1;
-}
-
-static struct snd_kcontrol_new capture_source_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* If we name this 'Input Source', it properly shows up in
- * alsamixer as a selection, * but it's shown under the
- * 'Playback' category.
- * If I name it 'Capture Source', it shows up in strange
- * ways (two bools of which one can be selected at a
- * time) but at least it's shown in the 'Capture'
- * category.
- * I was told that this was due to backward compatibility,
- * but I don't understand then why the mangling is *not*
- * done when I name it "Input Source".....
- */
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = onyx_snd_capture_source_info,
- .get = onyx_snd_capture_source_get,
- .put = onyx_snd_capture_source_put,
-};
-
-#define onyx_snd_mute_info snd_ctl_boolean_stereo_info
-
-static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 c;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
- mutex_unlock(&onyx->mutex);
-
- ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
- ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
-
- return 0;
-}
-
-static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 v = 0, c = 0;
- int err = -EBUSY;
-
- mutex_lock(&onyx->mutex);
- if (onyx->analog_locked)
- goto out_unlock;
-
- onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
- c = v;
- c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT);
- if (!ucontrol->value.integer.value[0])
- c |= ONYX_MUTE_LEFT;
- if (!ucontrol->value.integer.value[1])
- c |= ONYX_MUTE_RIGHT;
- err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
-
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return !err ? (v != c) : err;
-}
-
-static struct snd_kcontrol_new mute_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = onyx_snd_mute_info,
- .get = onyx_snd_mute_get,
- .put = onyx_snd_mute_put,
-};
-
-
-#define onyx_snd_single_bit_info snd_ctl_boolean_mono_info
-
-#define FLAG_POLARITY_INVERT 1
-#define FLAG_SPDIFLOCK 2
-
-static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 c;
- long int pv = kcontrol->private_value;
- u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
- u8 address = (pv >> 8) & 0xff;
- u8 mask = pv & 0xff;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, address, &c);
- mutex_unlock(&onyx->mutex);
-
- ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
-
- return 0;
-}
-
-static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 v = 0, c = 0;
- int err;
- long int pv = kcontrol->private_value;
- u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT;
- u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK;
- u8 address = (pv >> 8) & 0xff;
- u8 mask = pv & 0xff;
-
- mutex_lock(&onyx->mutex);
- if (spdiflock && onyx->spdif_locked) {
- /* even if alsamixer doesn't care.. */
- err = -EBUSY;
- goto out_unlock;
- }
- onyx_read_register(onyx, address, &v);
- c = v;
- c &= ~(mask);
- if (!!ucontrol->value.integer.value[0] ^ polarity)
- c |= mask;
- err = onyx_write_register(onyx, address, c);
-
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return !err ? (v != c) : err;
-}
-
-#define SINGLE_BIT(n, type, description, address, mask, flags) \
-static struct snd_kcontrol_new n##_control = { \
- .iface = SNDRV_CTL_ELEM_IFACE_##type, \
- .name = description, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = onyx_snd_single_bit_info, \
- .get = onyx_snd_single_bit_get, \
- .put = onyx_snd_single_bit_put, \
- .private_value = (flags << 16) | (address << 8) | mask \
-}
-
-SINGLE_BIT(spdif,
- MIXER,
- SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
- ONYX_REG_DIG_INFO4,
- ONYX_SPDIF_ENABLE,
- FLAG_SPDIFLOCK);
-SINGLE_BIT(ovr1,
- MIXER,
- "Oversampling Rate",
- ONYX_REG_DAC_CONTROL,
- ONYX_OVR1,
- 0);
-SINGLE_BIT(flt0,
- MIXER,
- "Fast Digital Filter Rolloff",
- ONYX_REG_DAC_FILTER,
- ONYX_ROLLOFF_FAST,
- FLAG_POLARITY_INVERT);
-SINGLE_BIT(hpf,
- MIXER,
- "Highpass Filter",
- ONYX_REG_ADC_HPF_BYPASS,
- ONYX_HPF_DISABLE,
- FLAG_POLARITY_INVERT);
-SINGLE_BIT(dm12,
- MIXER,
- "Digital De-Emphasis",
- ONYX_REG_DAC_DEEMPH,
- ONYX_DIGDEEMPH_CTRL,
- 0);
-
-static int onyx_spdif_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
- uinfo->count = 1;
- return 0;
-}
-
-static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- /* datasheet page 30, all others are 0 */
- ucontrol->value.iec958.status[0] = 0x3e;
- ucontrol->value.iec958.status[1] = 0xff;
-
- ucontrol->value.iec958.status[3] = 0x3f;
- ucontrol->value.iec958.status[4] = 0x0f;
-
- return 0;
-}
-
-static struct snd_kcontrol_new onyx_spdif_mask = {
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
- .info = onyx_spdif_info,
- .get = onyx_spdif_mask_get,
-};
-
-static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 v;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
- ucontrol->value.iec958.status[0] = v & 0x3e;
-
- onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v);
- ucontrol->value.iec958.status[1] = v;
-
- onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
- ucontrol->value.iec958.status[3] = v & 0x3f;
-
- onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
- ucontrol->value.iec958.status[4] = v & 0x0f;
- mutex_unlock(&onyx->mutex);
-
- return 0;
-}
-
-static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct onyx *onyx = snd_kcontrol_chip(kcontrol);
- u8 v;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
- v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
- onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
-
- v = ucontrol->value.iec958.status[1];
- onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v);
-
- onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v);
- v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f);
- onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v);
-
- onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
- v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
- onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
- mutex_unlock(&onyx->mutex);
-
- return 1;
-}
-
-static struct snd_kcontrol_new onyx_spdif_ctrl = {
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
- .info = onyx_spdif_info,
- .get = onyx_spdif_get,
- .put = onyx_spdif_put,
-};
-
-/* our registers */
-
-static u8 register_map[] = {
- ONYX_REG_DAC_ATTEN_LEFT,
- ONYX_REG_DAC_ATTEN_RIGHT,
- ONYX_REG_CONTROL,
- ONYX_REG_DAC_CONTROL,
- ONYX_REG_DAC_DEEMPH,
- ONYX_REG_DAC_FILTER,
- ONYX_REG_DAC_OUTPHASE,
- ONYX_REG_ADC_CONTROL,
- ONYX_REG_ADC_HPF_BYPASS,
- ONYX_REG_DIG_INFO1,
- ONYX_REG_DIG_INFO2,
- ONYX_REG_DIG_INFO3,
- ONYX_REG_DIG_INFO4
-};
-
-static u8 initial_values[ARRAY_SIZE(register_map)] = {
- 0x80, 0x80, /* muted */
- ONYX_MRST | ONYX_SRST, /* but handled specially! */
- ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
- 0, /* no deemphasis */
- ONYX_DAC_FILTER_ALWAYS,
- ONYX_OUTPHASE_INVERTED,
- (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/
- ONYX_ADC_HPF_ALWAYS,
- (1<<2), /* pcm audio */
- 2, /* category: pcm coder */
- 0, /* sampling frequency 44.1 kHz, clock accuracy level II */
- 1 /* 24 bit depth */
-};
-
-/* reset registers of chip, either to initial or to previous values */
-static int onyx_register_init(struct onyx *onyx)
-{
- int i;
- u8 val;
- u8 regs[sizeof(initial_values)];
-
- if (!onyx->initialised) {
- memcpy(regs, initial_values, sizeof(initial_values));
- if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val))
- return -1;
- val &= ~ONYX_SILICONVERSION;
- val |= initial_values[3];
- regs[3] = val;
- } else {
- for (i=0; i<sizeof(register_map); i++)
- regs[i] = onyx->cache[register_map[i]-FIRSTREGISTER];
- }
-
- for (i=0; i<sizeof(register_map); i++) {
- if (onyx_write_register(onyx, register_map[i], regs[i]))
- return -1;
- }
- onyx->initialised = 1;
- return 0;
-}
-
-static struct transfer_info onyx_transfers[] = {
- /* this is first so we can skip it if no input is present...
- * No hardware exists with that, but it's here as an example
- * of what to do :) */
- {
- /* analog input */
- .formats = SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .transfer_in = 1,
- .must_be_clock_source = 0,
- .tag = 0,
- },
- {
- /* if analog and digital are currently off, anything should go,
- * so this entry describes everything we can do... */
- .formats = SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE
-#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
- | SNDRV_PCM_FMTBIT_COMPRESSED_16BE
-#endif
- ,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .tag = 0,
- },
- {
- /* analog output */
- .formats = SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_8000_96000,
- .transfer_in = 0,
- .must_be_clock_source = 0,
- .tag = 1,
- },
- {
- /* digital pcm output, also possible for analog out */
- .formats = SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000,
- .transfer_in = 0,
- .must_be_clock_source = 0,
- .tag = 2,
- },
-#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
- /* Once alsa gets supports for this kind of thing we can add it... */
- {
- /* digital compressed output */
- .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000,
- .tag = 2,
- },
-#endif
- {}
-};
-
-static int onyx_usable(struct codec_info_item *cii,
- struct transfer_info *ti,
- struct transfer_info *out)
-{
- u8 v;
- struct onyx *onyx = cii->codec_data;
- int spdif_enabled, analog_enabled;
-
- mutex_lock(&onyx->mutex);
- onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
- spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
- onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
- analog_enabled =
- (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
- != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
- mutex_unlock(&onyx->mutex);
-
- switch (ti->tag) {
- case 0: return 1;
- case 1: return analog_enabled;
- case 2: return spdif_enabled;
- }
- return 1;
-}
-
-static int onyx_prepare(struct codec_info_item *cii,
- struct bus_info *bi,
- struct snd_pcm_substream *substream)
-{
- u8 v;
- struct onyx *onyx = cii->codec_data;
- int err = -EBUSY;
-
- mutex_lock(&onyx->mutex);
-
-#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
- if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
- /* mute and lock analog output */
- onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
- if (onyx_write_register(onyx,
- ONYX_REG_DAC_CONTROL,
- v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
- goto out_unlock;
- onyx->analog_locked = 1;
- err = 0;
- goto out_unlock;
- }
-#endif
- switch (substream->runtime->rate) {
- case 32000:
- case 44100:
- case 48000:
- /* these rates are ok for all outputs */
- /* FIXME: program spdif channel control bits here so that
- * userspace doesn't have to if it only plays pcm! */
- err = 0;
- goto out_unlock;
- default:
- /* got some rate that the digital output can't do,
- * so disable and lock it */
- onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v);
- if (onyx_write_register(onyx,
- ONYX_REG_DIG_INFO4,
- v & ~ONYX_SPDIF_ENABLE))
- goto out_unlock;
- onyx->spdif_locked = 1;
- err = 0;
- goto out_unlock;
- }
-
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
-}
-
-static int onyx_open(struct codec_info_item *cii,
- struct snd_pcm_substream *substream)
-{
- struct onyx *onyx = cii->codec_data;
-
- mutex_lock(&onyx->mutex);
- onyx->open_count++;
- mutex_unlock(&onyx->mutex);
-
- return 0;
-}
-
-static int onyx_close(struct codec_info_item *cii,
- struct snd_pcm_substream *substream)
-{
- struct onyx *onyx = cii->codec_data;
-
- mutex_lock(&onyx->mutex);
- onyx->open_count--;
- if (!onyx->open_count)
- onyx->spdif_locked = onyx->analog_locked = 0;
- mutex_unlock(&onyx->mutex);
-
- return 0;
-}
-
-static int onyx_switch_clock(struct codec_info_item *cii,
- enum clock_switch what)
-{
- struct onyx *onyx = cii->codec_data;
-
- mutex_lock(&onyx->mutex);
- /* this *MUST* be more elaborate later... */
- switch (what) {
- case CLOCK_SWITCH_PREPARE_SLAVE:
- onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio);
- break;
- case CLOCK_SWITCH_SLAVE:
- onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio);
- break;
- default: /* silence warning */
- break;
- }
- mutex_unlock(&onyx->mutex);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
-{
- struct onyx *onyx = cii->codec_data;
- u8 v;
- int err = -ENXIO;
-
- mutex_lock(&onyx->mutex);
- if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
- onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
- /* Apple does a sleep here but the datasheet says to do it on resume */
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
-}
-
-static int onyx_resume(struct codec_info_item *cii)
-{
- struct onyx *onyx = cii->codec_data;
- u8 v;
- int err = -ENXIO;
-
- mutex_lock(&onyx->mutex);
-
- /* reset codec */
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
- msleep(1);
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
- msleep(1);
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
- msleep(1);
-
- /* take codec out of suspend (if it still is after reset) */
- if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
- onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
- /* FIXME: should divide by sample rate, but 8k is the lowest we go */
- msleep(2205000/8000);
- /* reset all values */
- onyx_register_init(onyx);
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
-}
-
-#endif /* CONFIG_PM */
-
-static struct codec_info onyx_codec_info = {
- .transfers = onyx_transfers,
- .sysclock_factor = 256,
- .bus_factor = 64,
- .owner = THIS_MODULE,
- .usable = onyx_usable,
- .prepare = onyx_prepare,
- .open = onyx_open,
- .close = onyx_close,
- .switch_clock = onyx_switch_clock,
-#ifdef CONFIG_PM
- .suspend = onyx_suspend,
- .resume = onyx_resume,
-#endif
-};
-
-static int onyx_init_codec(struct aoa_codec *codec)
-{
- struct onyx *onyx = codec_to_onyx(codec);
- struct snd_kcontrol *ctl;
- struct codec_info *ci = &onyx_codec_info;
- u8 v;
- int err;
-
- if (!onyx->codec.gpio || !onyx->codec.gpio->methods) {
- printk(KERN_ERR PFX "gpios not assigned!!\n");
- return -EINVAL;
- }
-
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
- msleep(1);
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1);
- msleep(1);
- onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
- msleep(1);
-
- if (onyx_register_init(onyx)) {
- printk(KERN_ERR PFX "failed to initialise onyx registers\n");
- return -ENODEV;
- }
-
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) {
- printk(KERN_ERR PFX "failed to create onyx snd device!\n");
- return -ENODEV;
- }
-
- /* nothing connected? what a joke! */
- if ((onyx->codec.connected & 0xF) == 0)
- return -ENOTCONN;
-
- /* if no inputs are present... */
- if ((onyx->codec.connected & 0xC) == 0) {
- if (!onyx->codec_info)
- onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
- if (!onyx->codec_info)
- return -ENOMEM;
- ci = onyx->codec_info;
- *ci = onyx_codec_info;
- ci->transfers++;
- }
-
- /* if no outputs are present... */
- if ((onyx->codec.connected & 3) == 0) {
- if (!onyx->codec_info)
- onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL);
- if (!onyx->codec_info)
- return -ENOMEM;
- ci = onyx->codec_info;
- /* this is fine as there have to be inputs
- * if we end up in this part of the code */
- *ci = onyx_codec_info;
- ci->transfers[1].formats = 0;
- }
-
- if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev,
- aoa_get_card(),
- ci, onyx)) {
- printk(KERN_ERR PFX "error creating onyx pcm\n");
- return -ENODEV;
- }
-#define ADDCTL(n) \
- do { \
- ctl = snd_ctl_new1(&n, onyx); \
- if (ctl) { \
- ctl->id.device = \
- onyx->codec.soundbus_dev->pcm->device; \
- err = aoa_snd_ctl_add(ctl); \
- if (err) \
- goto error; \
- } \
- } while (0)
-
- if (onyx->codec.soundbus_dev->pcm) {
- /* give the user appropriate controls
- * depending on what inputs are connected */
- if ((onyx->codec.connected & 0xC) == 0xC)
- ADDCTL(capture_source_control);
- else if (onyx->codec.connected & 4)
- onyx_set_capture_source(onyx, 0);
- else
- onyx_set_capture_source(onyx, 1);
- if (onyx->codec.connected & 0xC)
- ADDCTL(inputgain_control);
-
- /* depending on what output is connected,
- * give the user appropriate controls */
- if (onyx->codec.connected & 1) {
- ADDCTL(volume_control);
- ADDCTL(mute_control);
- ADDCTL(ovr1_control);
- ADDCTL(flt0_control);
- ADDCTL(hpf_control);
- ADDCTL(dm12_control);
- /* spdif control defaults to off */
- }
- if (onyx->codec.connected & 2) {
- ADDCTL(onyx_spdif_mask);
- ADDCTL(onyx_spdif_ctrl);
- }
- if ((onyx->codec.connected & 3) == 3)
- ADDCTL(spdif_control);
- /* if only S/PDIF is connected, enable it unconditionally */
- if ((onyx->codec.connected & 3) == 2) {
- onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
- v |= ONYX_SPDIF_ENABLE;
- onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
- }
- }
-#undef ADDCTL
- printk(KERN_INFO PFX "attached to onyx codec via i2c\n");
-
- return 0;
- error:
- onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
- snd_device_free(aoa_get_card(), onyx);
- return err;
-}
-
-static void onyx_exit_codec(struct aoa_codec *codec)
-{
- struct onyx *onyx = codec_to_onyx(codec);
-
- if (!onyx->codec.soundbus_dev) {
- printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n");
- return;
- }
- onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
-}
-
-static struct i2c_driver onyx_driver;
-
-static int onyx_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
-{
- struct onyx *onyx;
- u8 dummy;
-
- onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL);
-
- if (!onyx)
- return -ENOMEM;
-
- mutex_init(&onyx->mutex);
- onyx->i2c.driver = &onyx_driver;
- onyx->i2c.adapter = adapter;
- onyx->i2c.addr = addr & 0x7f;
- strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE);
-
- if (i2c_attach_client(&onyx->i2c)) {
- printk(KERN_ERR PFX "failed to attach to i2c\n");
- goto fail;
- }
-
- /* we try to read from register ONYX_REG_CONTROL
- * to check if the codec is present */
- if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) {
- i2c_detach_client(&onyx->i2c);
- printk(KERN_ERR PFX "failed to read control register\n");
- goto fail;
- }
-
- strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
- onyx->codec.owner = THIS_MODULE;
- onyx->codec.init = onyx_init_codec;
- onyx->codec.exit = onyx_exit_codec;
- onyx->codec.node = of_node_get(node);
-
- if (aoa_codec_register(&onyx->codec)) {
- i2c_detach_client(&onyx->i2c);
- goto fail;
- }
- printk(KERN_DEBUG PFX "created and attached onyx instance\n");
- return 0;
- fail:
- kfree(onyx);
- return -EINVAL;
-}
-
-static int onyx_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "pcm3052")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found pcm3052\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- return -ENODEV;
- return onyx_create(adapter, dev, (*addr)>>1);
- }
- }
-
- /* if that didn't work, try desperate mode for older
- * machines that have stuff missing from the device tree */
-
- if (!of_device_is_compatible(busnode, "k2-i2c"))
- return -ENODEV;
-
- printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n");
- /* probe both possible addresses for the onyx chip */
- if (onyx_create(adapter, NULL, 0x46) == 0)
- return 0;
- return onyx_create(adapter, NULL, 0x47);
-}
-
-static int onyx_i2c_detach(struct i2c_client *client)
-{
- struct onyx *onyx = container_of(client, struct onyx, i2c);
- int err;
-
- if ((err = i2c_detach_client(client)))
- return err;
- aoa_codec_unregister(&onyx->codec);
- of_node_put(onyx->codec.node);
- if (onyx->codec_info)
- kfree(onyx->codec_info);
- kfree(onyx);
- return 0;
-}
-
-static struct i2c_driver onyx_driver = {
- .driver = {
- .name = "aoa_codec_onyx",
- .owner = THIS_MODULE,
- },
- .attach_adapter = onyx_i2c_attach,
- .detach_client = onyx_i2c_detach,
-};
-
-static int __init onyx_init(void)
-{
- return i2c_add_driver(&onyx_driver);
-}
-
-static void __exit onyx_exit(void)
-{
- i2c_del_driver(&onyx_driver);
-}
-
-module_init(onyx_init);
-module_exit(onyx_exit);
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c
deleted file mode 100644
index 6c515b2b8bbd..000000000000
--- a/sound/aoa/codecs/snd-aoa-codec-tas.c
+++ /dev/null
@@ -1,1012 +0,0 @@
-/*
- * Apple Onboard Audio driver for tas codec
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- *
- * Open questions:
- * - How to distinguish between 3004 and versions?
- *
- * FIXMEs:
- * - This codec driver doesn't honour the 'connected'
- * property of the aoa_codec struct, hence if
- * it is used in machines where not everything is
- * connected it will display wrong mixer elements.
- * - Driver assumes that the microphone is always
- * monaureal and connected to the right channel of
- * the input. This should also be a codec-dependent
- * flag, maybe the codec should have 3 different
- * bits for the three different possibilities how
- * it can be hooked up...
- * But as long as I don't see any hardware hooked
- * up that way...
- * - As Apple notes in their code, the tas3004 seems
- * to delay the right channel by one sample. You can
- * see this when for example recording stereo in
- * audacity, or recording the tas output via cable
- * on another machine (use a sinus generator or so).
- * I tried programming the BiQuads but couldn't
- * make the delay work, maybe someone can read the
- * datasheet and fix it. The relevant Apple comment
- * is in AppleTAS3004Audio.cpp lines 1637 ff. Note
- * that their comment describing how they program
- * the filters sucks...
- *
- * Other things:
- * - this should actually register *two* aoa_codec
- * structs since it has two inputs. Then it must
- * use the prepare callback to forbid running the
- * secondary output on a different clock.
- * Also, whatever bus knows how to do this must
- * provide two soundbus_dev devices and the fabric
- * must be able to link them correctly.
- *
- * I don't even know if Apple ever uses the second
- * port on the tas3004 though, I don't think their
- * i2s controllers can even do it. OTOH, they all
- * derive the clocks from common clocks, so it
- * might just be possible. The framework allows the
- * codec to refine the transfer_info items in the
- * usable callback, so we can simply remove the
- * rates the second instance is not using when it
- * actually is in use.
- * Maybe we'll need to make the sound busses have
- * a 'clock group id' value so the codec can
- * determine if the two outputs can be driven at
- * the same time. But that is likely overkill, up
- * to the fabric to not link them up incorrectly,
- * and up to the hardware designer to not wire
- * them up in some weird unusable way.
- */
-#include <stddef.h>
-#include <linux/i2c.h>
-#include <asm/pmac_low_i2c.h>
-#include <asm/prom.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("tas codec driver for snd-aoa");
-
-#include "snd-aoa-codec-tas.h"
-#include "snd-aoa-codec-tas-gain-table.h"
-#include "snd-aoa-codec-tas-basstreble.h"
-#include "../aoa.h"
-#include "../soundbus/soundbus.h"
-
-#define PFX "snd-aoa-codec-tas: "
-
-
-struct tas {
- struct aoa_codec codec;
- struct i2c_client i2c;
- u32 mute_l:1, mute_r:1 ,
- controls_created:1 ,
- drc_enabled:1,
- hw_enabled:1;
- u8 cached_volume_l, cached_volume_r;
- u8 mixer_l[3], mixer_r[3];
- u8 bass, treble;
- u8 acr;
- int drc_range;
- /* protects hardware access against concurrency from
- * userspace when hitting controls and during
- * codec init/suspend/resume */
- struct mutex mtx;
-};
-
-static int tas_reset_init(struct tas *tas);
-
-static struct tas *codec_to_tas(struct aoa_codec *codec)
-{
- return container_of(codec, struct tas, codec);
-}
-
-static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data)
-{
- if (len == 1)
- return i2c_smbus_write_byte_data(&tas->i2c, reg, *data);
- else
- return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
-}
-
-static void tas3004_set_drc(struct tas *tas)
-{
- unsigned char val[6];
-
- if (tas->drc_enabled)
- val[0] = 0x50; /* 3:1 above threshold */
- else
- val[0] = 0x51; /* disabled */
- val[1] = 0x02; /* 1:1 below threshold */
- if (tas->drc_range > 0xef)
- val[2] = 0xef;
- else if (tas->drc_range < 0)
- val[2] = 0x00;
- else
- val[2] = tas->drc_range;
- val[3] = 0xb0;
- val[4] = 0x60;
- val[5] = 0xa0;
-
- tas_write_reg(tas, TAS_REG_DRC, 6, val);
-}
-
-static void tas_set_treble(struct tas *tas)
-{
- u8 tmp;
-
- tmp = tas3004_treble(tas->treble);
- tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
-}
-
-static void tas_set_bass(struct tas *tas)
-{
- u8 tmp;
-
- tmp = tas3004_bass(tas->bass);
- tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
-}
-
-static void tas_set_volume(struct tas *tas)
-{
- u8 block[6];
- int tmp;
- u8 left, right;
-
- left = tas->cached_volume_l;
- right = tas->cached_volume_r;
-
- if (left > 177) left = 177;
- if (right > 177) right = 177;
-
- if (tas->mute_l) left = 0;
- if (tas->mute_r) right = 0;
-
- /* analysing the volume and mixer tables shows
- * that they are similar enough when we shift
- * the mixer table down by 4 bits. The error
- * is miniscule, in just one item the error
- * is 1, at a value of 0x07f17b (mixer table
- * value is 0x07f17a) */
- tmp = tas_gaintable[left];
- block[0] = tmp>>20;
- block[1] = tmp>>12;
- block[2] = tmp>>4;
- tmp = tas_gaintable[right];
- block[3] = tmp>>20;
- block[4] = tmp>>12;
- block[5] = tmp>>4;
- tas_write_reg(tas, TAS_REG_VOL, 6, block);
-}
-
-static void tas_set_mixer(struct tas *tas)
-{
- u8 block[9];
- int tmp, i;
- u8 val;
-
- for (i=0;i<3;i++) {
- val = tas->mixer_l[i];
- if (val > 177) val = 177;
- tmp = tas_gaintable[val];
- block[3*i+0] = tmp>>16;
- block[3*i+1] = tmp>>8;
- block[3*i+2] = tmp;
- }
- tas_write_reg(tas, TAS_REG_LMIX, 9, block);
-
- for (i=0;i<3;i++) {
- val = tas->mixer_r[i];
- if (val > 177) val = 177;
- tmp = tas_gaintable[val];
- block[3*i+0] = tmp>>16;
- block[3*i+1] = tmp>>8;
- block[3*i+2] = tmp;
- }
- tas_write_reg(tas, TAS_REG_RMIX, 9, block);
-}
-
-/* alsa stuff */
-
-static int tas_dev_register(struct snd_device *dev)
-{
- return 0;
-}
-
-static struct snd_device_ops ops = {
- .dev_register = tas_dev_register,
-};
-
-static int tas_snd_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 177;
- return 0;
-}
-
-static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->cached_volume_l;
- ucontrol->value.integer.value[1] = tas->cached_volume_r;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- if (ucontrol->value.integer.value[0] < 0 ||
- ucontrol->value.integer.value[0] > 177)
- return -EINVAL;
- if (ucontrol->value.integer.value[1] < 0 ||
- ucontrol->value.integer.value[1] > 177)
- return -EINVAL;
-
- mutex_lock(&tas->mtx);
- if (tas->cached_volume_l == ucontrol->value.integer.value[0]
- && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->cached_volume_l = ucontrol->value.integer.value[0];
- tas->cached_volume_r = ucontrol->value.integer.value[1];
- if (tas->hw_enabled)
- tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new volume_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_vol_info,
- .get = tas_snd_vol_get,
- .put = tas_snd_vol_put,
-};
-
-#define tas_snd_mute_info snd_ctl_boolean_stereo_info
-
-static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = !tas->mute_l;
- ucontrol->value.integer.value[1] = !tas->mute_r;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- if (tas->mute_l == !ucontrol->value.integer.value[0]
- && tas->mute_r == !ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->mute_l = !ucontrol->value.integer.value[0];
- tas->mute_r = !ucontrol->value.integer.value[1];
- if (tas->hw_enabled)
- tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new mute_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_mute_info,
- .get = tas_snd_mute_get,
- .put = tas_snd_mute_put,
-};
-
-static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 177;
- return 0;
-}
-
-static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
- int idx = kcontrol->private_value;
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->mixer_l[idx];
- ucontrol->value.integer.value[1] = tas->mixer_r[idx];
- mutex_unlock(&tas->mtx);
-
- return 0;
-}
-
-static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
- int idx = kcontrol->private_value;
-
- mutex_lock(&tas->mtx);
- if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
- && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->mixer_l[idx] = ucontrol->value.integer.value[0];
- tas->mixer_r[idx] = ucontrol->value.integer.value[1];
-
- if (tas->hw_enabled)
- tas_set_mixer(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-#define MIXER_CONTROL(n,descr,idx) \
-static struct snd_kcontrol_new n##_control = { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = descr " Playback Volume", \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = tas_snd_mixer_info, \
- .get = tas_snd_mixer_get, \
- .put = tas_snd_mixer_put, \
- .private_value = idx, \
-}
-
-MIXER_CONTROL(pcm1, "PCM", 0);
-MIXER_CONTROL(monitor, "Monitor", 2);
-
-static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = TAS3004_DRC_MAX;
- return 0;
-}
-
-static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->drc_range;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- if (ucontrol->value.integer.value[0] < 0 ||
- ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
- return -EINVAL;
-
- mutex_lock(&tas->mtx);
- if (tas->drc_range == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->drc_range = ucontrol->value.integer.value[0];
- if (tas->hw_enabled)
- tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new drc_range_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DRC Range",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_drc_range_info,
- .get = tas_snd_drc_range_get,
- .put = tas_snd_drc_range_put,
-};
-
-#define tas_snd_drc_switch_info snd_ctl_boolean_mono_info
-
-static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->drc_enabled;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->drc_enabled = !!ucontrol->value.integer.value[0];
- if (tas->hw_enabled)
- tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new drc_switch_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DRC Range Switch",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_drc_switch_info,
- .get = tas_snd_drc_switch_get,
- .put = tas_snd_drc_switch_put,
-};
-
-static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = { "Line-In", "Microphone" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
- int oldacr;
-
- if (ucontrol->value.enumerated.item[0] > 1)
- return -EINVAL;
- mutex_lock(&tas->mtx);
- oldacr = tas->acr;
-
- /*
- * Despite what the data sheet says in one place, the
- * TAS_ACR_B_MONAUREAL bit forces mono output even when
- * input A (line in) is selected.
- */
- tas->acr &= ~(TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL);
- if (ucontrol->value.enumerated.item[0])
- tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL |
- TAS_ACR_B_MON_SEL_RIGHT;
- if (oldacr == tas->acr) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
- if (tas->hw_enabled)
- tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new capture_source_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* If we name this 'Input Source', it properly shows up in
- * alsamixer as a selection, * but it's shown under the
- * 'Playback' category.
- * If I name it 'Capture Source', it shows up in strange
- * ways (two bools of which one can be selected at a
- * time) but at least it's shown in the 'Capture'
- * category.
- * I was told that this was due to backward compatibility,
- * but I don't understand then why the mangling is *not*
- * done when I name it "Input Source".....
- */
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_capture_source_info,
- .get = tas_snd_capture_source_get,
- .put = tas_snd_capture_source_put,
-};
-
-static int tas_snd_treble_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = TAS3004_TREBLE_MIN;
- uinfo->value.integer.max = TAS3004_TREBLE_MAX;
- return 0;
-}
-
-static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->treble;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
- ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
- return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->treble == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->treble = ucontrol->value.integer.value[0];
- if (tas->hw_enabled)
- tas_set_treble(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new treble_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Treble",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_treble_info,
- .get = tas_snd_treble_get,
- .put = tas_snd_treble_put,
-};
-
-static int tas_snd_bass_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = TAS3004_BASS_MIN;
- uinfo->value.integer.max = TAS3004_BASS_MAX;
- return 0;
-}
-
-static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- mutex_lock(&tas->mtx);
- ucontrol->value.integer.value[0] = tas->bass;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct tas *tas = snd_kcontrol_chip(kcontrol);
-
- if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
- ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
- return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->bass == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
- return 0;
- }
-
- tas->bass = ucontrol->value.integer.value[0];
- if (tas->hw_enabled)
- tas_set_bass(tas);
- mutex_unlock(&tas->mtx);
- return 1;
-}
-
-static struct snd_kcontrol_new bass_control = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Bass",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = tas_snd_bass_info,
- .get = tas_snd_bass_get,
- .put = tas_snd_bass_put,
-};
-
-static struct transfer_info tas_transfers[] = {
- {
- /* input */
- .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
- .transfer_in = 1,
- },
- {
- /* output */
- .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
- .transfer_in = 0,
- },
- {}
-};
-
-static int tas_usable(struct codec_info_item *cii,
- struct transfer_info *ti,
- struct transfer_info *out)
-{
- return 1;
-}
-
-static int tas_reset_init(struct tas *tas)
-{
- u8 tmp;
-
- tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
- msleep(5);
- tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
- msleep(5);
- tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
- msleep(20);
- tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
- msleep(10);
- tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
-
- tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
- if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
- goto outerr;
-
- tas->acr |= TAS_ACR_ANALOG_PDOWN;
- if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
- goto outerr;
-
- tmp = 0;
- if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
- goto outerr;
-
- tas3004_set_drc(tas);
-
- /* Set treble & bass to 0dB */
- tas->treble = TAS3004_TREBLE_ZERO;
- tas->bass = TAS3004_BASS_ZERO;
- tas_set_treble(tas);
- tas_set_bass(tas);
-
- tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
- if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
- goto outerr;
-
- return 0;
- outerr:
- return -ENODEV;
-}
-
-static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
-{
- struct tas *tas = cii->codec_data;
-
- switch(clock) {
- case CLOCK_SWITCH_PREPARE_SLAVE:
- /* Clocks are going away, mute mute mute */
- tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
- tas->hw_enabled = 0;
- break;
- case CLOCK_SWITCH_SLAVE:
- /* Clocks are back, re-init the codec */
- mutex_lock(&tas->mtx);
- tas_reset_init(tas);
- tas_set_volume(tas);
- tas_set_mixer(tas);
- tas->hw_enabled = 1;
- tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
- mutex_unlock(&tas->mtx);
- break;
- default:
- /* doesn't happen as of now */
- return -EINVAL;
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-/* we are controlled via i2c and assume that is always up
- * If that wasn't the case, we'd have to suspend once
- * our i2c device is suspended, and then take note of that! */
-static int tas_suspend(struct tas *tas)
-{
- mutex_lock(&tas->mtx);
- tas->hw_enabled = 0;
- tas->acr |= TAS_ACR_ANALOG_PDOWN;
- tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int tas_resume(struct tas *tas)
-{
- /* reset codec */
- mutex_lock(&tas->mtx);
- tas_reset_init(tas);
- tas_set_volume(tas);
- tas_set_mixer(tas);
- tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
- return 0;
-}
-
-static int _tas_suspend(struct codec_info_item *cii, pm_message_t state)
-{
- return tas_suspend(cii->codec_data);
-}
-
-static int _tas_resume(struct codec_info_item *cii)
-{
- return tas_resume(cii->codec_data);
-}
-#else /* CONFIG_PM */
-#define _tas_suspend NULL
-#define _tas_resume NULL
-#endif /* CONFIG_PM */
-
-static struct codec_info tas_codec_info = {
- .transfers = tas_transfers,
- /* in theory, we can drive it at 512 too...
- * but so far the framework doesn't allow
- * for that and I don't see much point in it. */
- .sysclock_factor = 256,
- /* same here, could be 32 for just one 16 bit format */
- .bus_factor = 64,
- .owner = THIS_MODULE,
- .usable = tas_usable,
- .switch_clock = tas_switch_clock,
- .suspend = _tas_suspend,
- .resume = _tas_resume,
-};
-
-static int tas_init_codec(struct aoa_codec *codec)
-{
- struct tas *tas = codec_to_tas(codec);
- int err;
-
- if (!tas->codec.gpio || !tas->codec.gpio->methods) {
- printk(KERN_ERR PFX "gpios not assigned!!\n");
- return -EINVAL;
- }
-
- mutex_lock(&tas->mtx);
- if (tas_reset_init(tas)) {
- printk(KERN_ERR PFX "tas failed to initialise\n");
- mutex_unlock(&tas->mtx);
- return -ENXIO;
- }
- tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
-
- if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
- aoa_get_card(),
- &tas_codec_info, tas)) {
- printk(KERN_ERR PFX "error attaching tas to soundbus\n");
- return -ENODEV;
- }
-
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) {
- printk(KERN_ERR PFX "failed to create tas snd device!\n");
- return -ENODEV;
- }
- err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas));
- if (err)
- goto error;
-
- err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas));
- if (err)
- goto error;
-
- return 0;
- error:
- tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
- snd_device_free(aoa_get_card(), tas);
- return err;
-}
-
-static void tas_exit_codec(struct aoa_codec *codec)
-{
- struct tas *tas = codec_to_tas(codec);
-
- if (!tas->codec.soundbus_dev)
- return;
- tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
-}
-
-
-static struct i2c_driver tas_driver;
-
-static int tas_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
-{
- struct tas *tas;
-
- tas = kzalloc(sizeof(struct tas), GFP_KERNEL);
-
- if (!tas)
- return -ENOMEM;
-
- mutex_init(&tas->mtx);
- tas->i2c.driver = &tas_driver;
- tas->i2c.adapter = adapter;
- tas->i2c.addr = addr;
- /* seems that half is a saner default */
- tas->drc_range = TAS3004_DRC_MAX / 2;
- strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE);
-
- if (i2c_attach_client(&tas->i2c)) {
- printk(KERN_ERR PFX "failed to attach to i2c\n");
- goto fail;
- }
-
- strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN);
- tas->codec.owner = THIS_MODULE;
- tas->codec.init = tas_init_codec;
- tas->codec.exit = tas_exit_codec;
- tas->codec.node = of_node_get(node);
-
- if (aoa_codec_register(&tas->codec)) {
- goto detach;
- }
- printk(KERN_DEBUG
- "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
- addr, node->full_name);
- return 0;
- detach:
- i2c_detach_client(&tas->i2c);
- fail:
- mutex_destroy(&tas->mtx);
- kfree(tas);
- return -EINVAL;
-}
-
-static int tas_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "tas3004")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found tas3004\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- continue;
- return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
- }
- /* older machines have no 'codec' node with a 'compatible'
- * property that says 'tas3004', they just have a 'deq'
- * node without any such property... */
- if (strcmp(dev->name, "deq") == 0) {
- const u32 *_addr;
- u32 addr;
- printk(KERN_DEBUG PFX "found 'deq' node\n");
- _addr = of_get_property(dev, "i2c-address", NULL);
- if (!_addr)
- continue;
- addr = ((*_addr) >> 1) & 0x7f;
- /* now, if the address doesn't match any of the two
- * that a tas3004 can have, we cannot handle this.
- * I doubt it ever happens but hey. */
- if (addr != 0x34 && addr != 0x35)
- continue;
- return tas_create(adapter, dev, addr);
- }
- }
- return -ENODEV;
-}
-
-static int tas_i2c_detach(struct i2c_client *client)
-{
- struct tas *tas = container_of(client, struct tas, i2c);
- int err;
- u8 tmp = TAS_ACR_ANALOG_PDOWN;
-
- if ((err = i2c_detach_client(client)))
- return err;
- aoa_codec_unregister(&tas->codec);
- of_node_put(tas->codec.node);
-
- /* power down codec chip */
- tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
-
- mutex_destroy(&tas->mtx);
- kfree(tas);
- return 0;
-}
-
-static struct i2c_driver tas_driver = {
- .driver = {
- .name = "aoa_codec_tas",
- .owner = THIS_MODULE,
- },
- .attach_adapter = tas_i2c_attach,
- .detach_client = tas_i2c_detach,
-};
-
-static int __init tas_init(void)
-{
- return i2c_add_driver(&tas_driver);
-}
-
-static void __exit tas_exit(void)
-{
- i2c_del_driver(&tas_driver);
-}
-
-module_init(tas_init);
-module_exit(tas_exit);
diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/snd-aoa-codec-toonie.c
deleted file mode 100644
index 3c7d1d8a9a6f..000000000000
--- a/sound/aoa/codecs/snd-aoa-codec-toonie.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Apple Onboard Audio driver for Toonie codec
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- *
- *
- * This is a driver for the toonie codec chip. This chip is present
- * on the Mac Mini and is nothing but a DAC.
- */
-#include <linux/delay.h>
-#include <linux/module.h>
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("toonie codec driver for snd-aoa");
-
-#include "../aoa.h"
-#include "../soundbus/soundbus.h"
-
-
-#define PFX "snd-aoa-codec-toonie: "
-
-struct toonie {
- struct aoa_codec codec;
-};
-#define codec_to_toonie(c) container_of(c, struct toonie, codec)
-
-static int toonie_dev_register(struct snd_device *dev)
-{
- return 0;
-}
-
-static struct snd_device_ops ops = {
- .dev_register = toonie_dev_register,
-};
-
-static struct transfer_info toonie_transfers[] = {
- /* This thing *only* has analog output,
- * the rates are taken from Info.plist
- * from Darwin. */
- {
- .formats = SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_BE,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000,
- },
- {}
-};
-
-static int toonie_usable(struct codec_info_item *cii,
- struct transfer_info *ti,
- struct transfer_info *out)
-{
- return 1;
-}
-
-#ifdef CONFIG_PM
-static int toonie_suspend(struct codec_info_item *cii, pm_message_t state)
-{
- /* can we turn it off somehow? */
- return 0;
-}
-
-static int toonie_resume(struct codec_info_item *cii)
-{
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static struct codec_info toonie_codec_info = {
- .transfers = toonie_transfers,
- .sysclock_factor = 256,
- .bus_factor = 64,
- .owner = THIS_MODULE,
- .usable = toonie_usable,
-#ifdef CONFIG_PM
- .suspend = toonie_suspend,
- .resume = toonie_resume,
-#endif
-};
-
-static int toonie_init_codec(struct aoa_codec *codec)
-{
- struct toonie *toonie = codec_to_toonie(codec);
-
- /* nothing connected? what a joke! */
- if (toonie->codec.connected != 1)
- return -ENOTCONN;
-
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
- printk(KERN_ERR PFX "failed to create toonie snd device!\n");
- return -ENODEV;
- }
-
- if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev,
- aoa_get_card(),
- &toonie_codec_info, toonie)) {
- printk(KERN_ERR PFX "error creating toonie pcm\n");
- snd_device_free(aoa_get_card(), toonie);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void toonie_exit_codec(struct aoa_codec *codec)
-{
- struct toonie *toonie = codec_to_toonie(codec);
-
- if (!toonie->codec.soundbus_dev) {
- printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n");
- return;
- }
- toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie);
-}
-
-static struct toonie *toonie;
-
-static int __init toonie_init(void)
-{
- toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL);
-
- if (!toonie)
- return -ENOMEM;
-
- strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
- toonie->codec.owner = THIS_MODULE;
- toonie->codec.init = toonie_init_codec;
- toonie->codec.exit = toonie_exit_codec;
-
- if (aoa_codec_register(&toonie->codec)) {
- kfree(toonie);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void __exit toonie_exit(void)
-{
- aoa_codec_unregister(&toonie->codec);
- kfree(toonie);
-}
-
-module_init(toonie_init);
-module_exit(toonie_exit);
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h
index 69b61136fd54..69b61136fd54 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h
+++ b/sound/aoa/codecs/tas-basstreble.h
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h b/sound/aoa/codecs/tas-gain-table.h
index 4cfa6757715e..4cfa6757715e 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h
+++ b/sound/aoa/codecs/tas-gain-table.h
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
new file mode 100644
index 000000000000..008e0f85097d
--- /dev/null
+++ b/sound/aoa/codecs/tas.c
@@ -0,0 +1,1012 @@
+/*
+ * Apple Onboard Audio driver for tas codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ * Open questions:
+ * - How to distinguish between 3004 and versions?
+ *
+ * FIXMEs:
+ * - This codec driver doesn't honour the 'connected'
+ * property of the aoa_codec struct, hence if
+ * it is used in machines where not everything is
+ * connected it will display wrong mixer elements.
+ * - Driver assumes that the microphone is always
+ * monaureal and connected to the right channel of
+ * the input. This should also be a codec-dependent
+ * flag, maybe the codec should have 3 different
+ * bits for the three different possibilities how
+ * it can be hooked up...
+ * But as long as I don't see any hardware hooked
+ * up that way...
+ * - As Apple notes in their code, the tas3004 seems
+ * to delay the right channel by one sample. You can
+ * see this when for example recording stereo in
+ * audacity, or recording the tas output via cable
+ * on another machine (use a sinus generator or so).
+ * I tried programming the BiQuads but couldn't
+ * make the delay work, maybe someone can read the
+ * datasheet and fix it. The relevant Apple comment
+ * is in AppleTAS3004Audio.cpp lines 1637 ff. Note
+ * that their comment describing how they program
+ * the filters sucks...
+ *
+ * Other things:
+ * - this should actually register *two* aoa_codec
+ * structs since it has two inputs. Then it must
+ * use the prepare callback to forbid running the
+ * secondary output on a different clock.
+ * Also, whatever bus knows how to do this must
+ * provide two soundbus_dev devices and the fabric
+ * must be able to link them correctly.
+ *
+ * I don't even know if Apple ever uses the second
+ * port on the tas3004 though, I don't think their
+ * i2s controllers can even do it. OTOH, they all
+ * derive the clocks from common clocks, so it
+ * might just be possible. The framework allows the
+ * codec to refine the transfer_info items in the
+ * usable callback, so we can simply remove the
+ * rates the second instance is not using when it
+ * actually is in use.
+ * Maybe we'll need to make the sound busses have
+ * a 'clock group id' value so the codec can
+ * determine if the two outputs can be driven at
+ * the same time. But that is likely overkill, up
+ * to the fabric to not link them up incorrectly,
+ * and up to the hardware designer to not wire
+ * them up in some weird unusable way.
+ */
+#include <stddef.h>
+#include <linux/i2c.h>
+#include <asm/pmac_low_i2c.h>
+#include <asm/prom.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("tas codec driver for snd-aoa");
+
+#include "tas.h"
+#include "tas-gain-table.h"
+#include "tas-basstreble.h"
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+#define PFX "snd-aoa-codec-tas: "
+
+
+struct tas {
+ struct aoa_codec codec;
+ struct i2c_client i2c;
+ u32 mute_l:1, mute_r:1 ,
+ controls_created:1 ,
+ drc_enabled:1,
+ hw_enabled:1;
+ u8 cached_volume_l, cached_volume_r;
+ u8 mixer_l[3], mixer_r[3];
+ u8 bass, treble;
+ u8 acr;
+ int drc_range;
+ /* protects hardware access against concurrency from
+ * userspace when hitting controls and during
+ * codec init/suspend/resume */
+ struct mutex mtx;
+};
+
+static int tas_reset_init(struct tas *tas);
+
+static struct tas *codec_to_tas(struct aoa_codec *codec)
+{
+ return container_of(codec, struct tas, codec);
+}
+
+static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data)
+{
+ if (len == 1)
+ return i2c_smbus_write_byte_data(&tas->i2c, reg, *data);
+ else
+ return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);
+}
+
+static void tas3004_set_drc(struct tas *tas)
+{
+ unsigned char val[6];
+
+ if (tas->drc_enabled)
+ val[0] = 0x50; /* 3:1 above threshold */
+ else
+ val[0] = 0x51; /* disabled */
+ val[1] = 0x02; /* 1:1 below threshold */
+ if (tas->drc_range > 0xef)
+ val[2] = 0xef;
+ else if (tas->drc_range < 0)
+ val[2] = 0x00;
+ else
+ val[2] = tas->drc_range;
+ val[3] = 0xb0;
+ val[4] = 0x60;
+ val[5] = 0xa0;
+
+ tas_write_reg(tas, TAS_REG_DRC, 6, val);
+}
+
+static void tas_set_treble(struct tas *tas)
+{
+ u8 tmp;
+
+ tmp = tas3004_treble(tas->treble);
+ tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);
+}
+
+static void tas_set_bass(struct tas *tas)
+{
+ u8 tmp;
+
+ tmp = tas3004_bass(tas->bass);
+ tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);
+}
+
+static void tas_set_volume(struct tas *tas)
+{
+ u8 block[6];
+ int tmp;
+ u8 left, right;
+
+ left = tas->cached_volume_l;
+ right = tas->cached_volume_r;
+
+ if (left > 177) left = 177;
+ if (right > 177) right = 177;
+
+ if (tas->mute_l) left = 0;
+ if (tas->mute_r) right = 0;
+
+ /* analysing the volume and mixer tables shows
+ * that they are similar enough when we shift
+ * the mixer table down by 4 bits. The error
+ * is miniscule, in just one item the error
+ * is 1, at a value of 0x07f17b (mixer table
+ * value is 0x07f17a) */
+ tmp = tas_gaintable[left];
+ block[0] = tmp>>20;
+ block[1] = tmp>>12;
+ block[2] = tmp>>4;
+ tmp = tas_gaintable[right];
+ block[3] = tmp>>20;
+ block[4] = tmp>>12;
+ block[5] = tmp>>4;
+ tas_write_reg(tas, TAS_REG_VOL, 6, block);
+}
+
+static void tas_set_mixer(struct tas *tas)
+{
+ u8 block[9];
+ int tmp, i;
+ u8 val;
+
+ for (i=0;i<3;i++) {
+ val = tas->mixer_l[i];
+ if (val > 177) val = 177;
+ tmp = tas_gaintable[val];
+ block[3*i+0] = tmp>>16;
+ block[3*i+1] = tmp>>8;
+ block[3*i+2] = tmp;
+ }
+ tas_write_reg(tas, TAS_REG_LMIX, 9, block);
+
+ for (i=0;i<3;i++) {
+ val = tas->mixer_r[i];
+ if (val > 177) val = 177;
+ tmp = tas_gaintable[val];
+ block[3*i+0] = tmp>>16;
+ block[3*i+1] = tmp>>8;
+ block[3*i+2] = tmp;
+ }
+ tas_write_reg(tas, TAS_REG_RMIX, 9, block);
+}
+
+/* alsa stuff */
+
+static int tas_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = tas_dev_register,
+};
+
+static int tas_snd_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 177;
+ return 0;
+}
+
+static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->cached_volume_l;
+ ucontrol->value.integer.value[1] = tas->cached_volume_r;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > 177)
+ return -EINVAL;
+ if (ucontrol->value.integer.value[1] < 0 ||
+ ucontrol->value.integer.value[1] > 177)
+ return -EINVAL;
+
+ mutex_lock(&tas->mtx);
+ if (tas->cached_volume_l == ucontrol->value.integer.value[0]
+ && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->cached_volume_l = ucontrol->value.integer.value[0];
+ tas->cached_volume_r = ucontrol->value.integer.value[1];
+ if (tas->hw_enabled)
+ tas_set_volume(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new volume_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_vol_info,
+ .get = tas_snd_vol_get,
+ .put = tas_snd_vol_put,
+};
+
+#define tas_snd_mute_info snd_ctl_boolean_stereo_info
+
+static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = !tas->mute_l;
+ ucontrol->value.integer.value[1] = !tas->mute_r;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->mute_l == !ucontrol->value.integer.value[0]
+ && tas->mute_r == !ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->mute_l = !ucontrol->value.integer.value[0];
+ tas->mute_r = !ucontrol->value.integer.value[1];
+ if (tas->hw_enabled)
+ tas_set_volume(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_mute_info,
+ .get = tas_snd_mute_get,
+ .put = tas_snd_mute_put,
+};
+
+static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 177;
+ return 0;
+}
+
+static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->mixer_l[idx];
+ ucontrol->value.integer.value[1] = tas->mixer_r[idx];
+ mutex_unlock(&tas->mtx);
+
+ return 0;
+}
+
+static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+
+ mutex_lock(&tas->mtx);
+ if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
+ && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->mixer_l[idx] = ucontrol->value.integer.value[0];
+ tas->mixer_r[idx] = ucontrol->value.integer.value[1];
+
+ if (tas->hw_enabled)
+ tas_set_mixer(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+#define MIXER_CONTROL(n,descr,idx) \
+static struct snd_kcontrol_new n##_control = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = descr " Playback Volume", \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = tas_snd_mixer_info, \
+ .get = tas_snd_mixer_get, \
+ .put = tas_snd_mixer_put, \
+ .private_value = idx, \
+}
+
+MIXER_CONTROL(pcm1, "PCM", 0);
+MIXER_CONTROL(monitor, "Monitor", 2);
+
+static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = TAS3004_DRC_MAX;
+ return 0;
+}
+
+static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->drc_range;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
+ return -EINVAL;
+
+ mutex_lock(&tas->mtx);
+ if (tas->drc_range == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->drc_range = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas3004_set_drc(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new drc_range_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DRC Range",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_drc_range_info,
+ .get = tas_snd_drc_range_get,
+ .put = tas_snd_drc_range_put,
+};
+
+#define tas_snd_drc_switch_info snd_ctl_boolean_mono_info
+
+static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->drc_enabled;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->drc_enabled = !!ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas3004_set_drc(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new drc_switch_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DRC Range Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_drc_switch_info,
+ .get = tas_snd_drc_switch_get,
+ .put = tas_snd_drc_switch_put,
+};
+
+static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "Line-In", "Microphone" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item > 1)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+ int oldacr;
+
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
+ mutex_lock(&tas->mtx);
+ oldacr = tas->acr;
+
+ /*
+ * Despite what the data sheet says in one place, the
+ * TAS_ACR_B_MONAUREAL bit forces mono output even when
+ * input A (line in) is selected.
+ */
+ tas->acr &= ~(TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL);
+ if (ucontrol->value.enumerated.item[0])
+ tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL |
+ TAS_ACR_B_MON_SEL_RIGHT;
+ if (oldacr == tas->acr) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+ if (tas->hw_enabled)
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new capture_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* If we name this 'Input Source', it properly shows up in
+ * alsamixer as a selection, * but it's shown under the
+ * 'Playback' category.
+ * If I name it 'Capture Source', it shows up in strange
+ * ways (two bools of which one can be selected at a
+ * time) but at least it's shown in the 'Capture'
+ * category.
+ * I was told that this was due to backward compatibility,
+ * but I don't understand then why the mangling is *not*
+ * done when I name it "Input Source".....
+ */
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_capture_source_info,
+ .get = tas_snd_capture_source_get,
+ .put = tas_snd_capture_source_put,
+};
+
+static int tas_snd_treble_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS3004_TREBLE_MIN;
+ uinfo->value.integer.max = TAS3004_TREBLE_MAX;
+ return 0;
+}
+
+static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->treble;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
+ ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
+ return -EINVAL;
+ mutex_lock(&tas->mtx);
+ if (tas->treble == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->treble = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas_set_treble(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new treble_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Treble",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_treble_info,
+ .get = tas_snd_treble_get,
+ .put = tas_snd_treble_put,
+};
+
+static int tas_snd_bass_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS3004_BASS_MIN;
+ uinfo->value.integer.max = TAS3004_BASS_MAX;
+ return 0;
+}
+
+static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ mutex_lock(&tas->mtx);
+ ucontrol->value.integer.value[0] = tas->bass;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tas *tas = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
+ ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
+ return -EINVAL;
+ mutex_lock(&tas->mtx);
+ if (tas->bass == ucontrol->value.integer.value[0]) {
+ mutex_unlock(&tas->mtx);
+ return 0;
+ }
+
+ tas->bass = ucontrol->value.integer.value[0];
+ if (tas->hw_enabled)
+ tas_set_bass(tas);
+ mutex_unlock(&tas->mtx);
+ return 1;
+}
+
+static struct snd_kcontrol_new bass_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Bass",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = tas_snd_bass_info,
+ .get = tas_snd_bass_get,
+ .put = tas_snd_bass_put,
+};
+
+static struct transfer_info tas_transfers[] = {
+ {
+ /* input */
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .transfer_in = 1,
+ },
+ {
+ /* output */
+ .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .transfer_in = 0,
+ },
+ {}
+};
+
+static int tas_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ return 1;
+}
+
+static int tas_reset_init(struct tas *tas)
+{
+ u8 tmp;
+
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+ msleep(5);
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
+ msleep(5);
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1);
+ msleep(20);
+ tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0);
+ msleep(10);
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+
+ tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
+ if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
+ goto outerr;
+
+ tas->acr |= TAS_ACR_ANALOG_PDOWN;
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+ goto outerr;
+
+ tmp = 0;
+ if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
+ goto outerr;
+
+ tas3004_set_drc(tas);
+
+ /* Set treble & bass to 0dB */
+ tas->treble = TAS3004_TREBLE_ZERO;
+ tas->bass = TAS3004_BASS_ZERO;
+ tas_set_treble(tas);
+ tas_set_bass(tas);
+
+ tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
+ if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
+ goto outerr;
+
+ return 0;
+ outerr:
+ return -ENODEV;
+}
+
+static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
+{
+ struct tas *tas = cii->codec_data;
+
+ switch(clock) {
+ case CLOCK_SWITCH_PREPARE_SLAVE:
+ /* Clocks are going away, mute mute mute */
+ tas->codec.gpio->methods->all_amps_off(tas->codec.gpio);
+ tas->hw_enabled = 0;
+ break;
+ case CLOCK_SWITCH_SLAVE:
+ /* Clocks are back, re-init the codec */
+ mutex_lock(&tas->mtx);
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+ mutex_unlock(&tas->mtx);
+ break;
+ default:
+ /* doesn't happen as of now */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/* we are controlled via i2c and assume that is always up
+ * If that wasn't the case, we'd have to suspend once
+ * our i2c device is suspended, and then take note of that! */
+static int tas_suspend(struct tas *tas)
+{
+ mutex_lock(&tas->mtx);
+ tas->hw_enabled = 0;
+ tas->acr |= TAS_ACR_ANALOG_PDOWN;
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int tas_resume(struct tas *tas)
+{
+ /* reset codec */
+ mutex_lock(&tas->mtx);
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ mutex_unlock(&tas->mtx);
+ return 0;
+}
+
+static int _tas_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ return tas_suspend(cii->codec_data);
+}
+
+static int _tas_resume(struct codec_info_item *cii)
+{
+ return tas_resume(cii->codec_data);
+}
+#else /* CONFIG_PM */
+#define _tas_suspend NULL
+#define _tas_resume NULL
+#endif /* CONFIG_PM */
+
+static struct codec_info tas_codec_info = {
+ .transfers = tas_transfers,
+ /* in theory, we can drive it at 512 too...
+ * but so far the framework doesn't allow
+ * for that and I don't see much point in it. */
+ .sysclock_factor = 256,
+ /* same here, could be 32 for just one 16 bit format */
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+ .usable = tas_usable,
+ .switch_clock = tas_switch_clock,
+ .suspend = _tas_suspend,
+ .resume = _tas_resume,
+};
+
+static int tas_init_codec(struct aoa_codec *codec)
+{
+ struct tas *tas = codec_to_tas(codec);
+ int err;
+
+ if (!tas->codec.gpio || !tas->codec.gpio->methods) {
+ printk(KERN_ERR PFX "gpios not assigned!!\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&tas->mtx);
+ if (tas_reset_init(tas)) {
+ printk(KERN_ERR PFX "tas failed to initialise\n");
+ mutex_unlock(&tas->mtx);
+ return -ENXIO;
+ }
+ tas->hw_enabled = 1;
+ mutex_unlock(&tas->mtx);
+
+ if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
+ aoa_get_card(),
+ &tas_codec_info, tas)) {
+ printk(KERN_ERR PFX "error attaching tas to soundbus\n");
+ return -ENODEV;
+ }
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) {
+ printk(KERN_ERR PFX "failed to create tas snd device!\n");
+ return -ENODEV;
+ }
+ err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&drc_range_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&drc_switch_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&treble_control, tas));
+ if (err)
+ goto error;
+
+ err = aoa_snd_ctl_add(snd_ctl_new1(&bass_control, tas));
+ if (err)
+ goto error;
+
+ return 0;
+ error:
+ tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
+ snd_device_free(aoa_get_card(), tas);
+ return err;
+}
+
+static void tas_exit_codec(struct aoa_codec *codec)
+{
+ struct tas *tas = codec_to_tas(codec);
+
+ if (!tas->codec.soundbus_dev)
+ return;
+ tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas);
+}
+
+
+static struct i2c_driver tas_driver;
+
+static int tas_create(struct i2c_adapter *adapter,
+ struct device_node *node,
+ int addr)
+{
+ struct tas *tas;
+
+ tas = kzalloc(sizeof(struct tas), GFP_KERNEL);
+
+ if (!tas)
+ return -ENOMEM;
+
+ mutex_init(&tas->mtx);
+ tas->i2c.driver = &tas_driver;
+ tas->i2c.adapter = adapter;
+ tas->i2c.addr = addr;
+ /* seems that half is a saner default */
+ tas->drc_range = TAS3004_DRC_MAX / 2;
+ strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE);
+
+ if (i2c_attach_client(&tas->i2c)) {
+ printk(KERN_ERR PFX "failed to attach to i2c\n");
+ goto fail;
+ }
+
+ strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN);
+ tas->codec.owner = THIS_MODULE;
+ tas->codec.init = tas_init_codec;
+ tas->codec.exit = tas_exit_codec;
+ tas->codec.node = of_node_get(node);
+
+ if (aoa_codec_register(&tas->codec)) {
+ goto detach;
+ }
+ printk(KERN_DEBUG
+ "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
+ addr, node->full_name);
+ return 0;
+ detach:
+ i2c_detach_client(&tas->i2c);
+ fail:
+ mutex_destroy(&tas->mtx);
+ kfree(tas);
+ return -EINVAL;
+}
+
+static int tas_i2c_attach(struct i2c_adapter *adapter)
+{
+ struct device_node *busnode, *dev = NULL;
+ struct pmac_i2c_bus *bus;
+
+ bus = pmac_i2c_adapter_to_bus(adapter);
+ if (bus == NULL)
+ return -ENODEV;
+ busnode = pmac_i2c_get_bus_node(bus);
+
+ while ((dev = of_get_next_child(busnode, dev)) != NULL) {
+ if (of_device_is_compatible(dev, "tas3004")) {
+ const u32 *addr;
+ printk(KERN_DEBUG PFX "found tas3004\n");
+ addr = of_get_property(dev, "reg", NULL);
+ if (!addr)
+ continue;
+ return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
+ }
+ /* older machines have no 'codec' node with a 'compatible'
+ * property that says 'tas3004', they just have a 'deq'
+ * node without any such property... */
+ if (strcmp(dev->name, "deq") == 0) {
+ const u32 *_addr;
+ u32 addr;
+ printk(KERN_DEBUG PFX "found 'deq' node\n");
+ _addr = of_get_property(dev, "i2c-address", NULL);
+ if (!_addr)
+ continue;
+ addr = ((*_addr) >> 1) & 0x7f;
+ /* now, if the address doesn't match any of the two
+ * that a tas3004 can have, we cannot handle this.
+ * I doubt it ever happens but hey. */
+ if (addr != 0x34 && addr != 0x35)
+ continue;
+ return tas_create(adapter, dev, addr);
+ }
+ }
+ return -ENODEV;
+}
+
+static int tas_i2c_detach(struct i2c_client *client)
+{
+ struct tas *tas = container_of(client, struct tas, i2c);
+ int err;
+ u8 tmp = TAS_ACR_ANALOG_PDOWN;
+
+ if ((err = i2c_detach_client(client)))
+ return err;
+ aoa_codec_unregister(&tas->codec);
+ of_node_put(tas->codec.node);
+
+ /* power down codec chip */
+ tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
+
+ mutex_destroy(&tas->mtx);
+ kfree(tas);
+ return 0;
+}
+
+static struct i2c_driver tas_driver = {
+ .driver = {
+ .name = "aoa_codec_tas",
+ .owner = THIS_MODULE,
+ },
+ .attach_adapter = tas_i2c_attach,
+ .detach_client = tas_i2c_detach,
+};
+
+static int __init tas_init(void)
+{
+ return i2c_add_driver(&tas_driver);
+}
+
+static void __exit tas_exit(void)
+{
+ i2c_del_driver(&tas_driver);
+}
+
+module_init(tas_init);
+module_exit(tas_exit);
diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/tas.h
index ae177e3466e6..ae177e3466e6 100644
--- a/sound/aoa/codecs/snd-aoa-codec-tas.h
+++ b/sound/aoa/codecs/tas.h
diff --git a/sound/aoa/codecs/toonie.c b/sound/aoa/codecs/toonie.c
new file mode 100644
index 000000000000..f13827e17562
--- /dev/null
+++ b/sound/aoa/codecs/toonie.c
@@ -0,0 +1,150 @@
+/*
+ * Apple Onboard Audio driver for Toonie codec
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This is a driver for the toonie codec chip. This chip is present
+ * on the Mac Mini and is nothing but a DAC.
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("toonie codec driver for snd-aoa");
+
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+
+#define PFX "snd-aoa-codec-toonie: "
+
+struct toonie {
+ struct aoa_codec codec;
+};
+#define codec_to_toonie(c) container_of(c, struct toonie, codec)
+
+static int toonie_dev_register(struct snd_device *dev)
+{
+ return 0;
+}
+
+static struct snd_device_ops ops = {
+ .dev_register = toonie_dev_register,
+};
+
+static struct transfer_info toonie_transfers[] = {
+ /* This thing *only* has analog output,
+ * the rates are taken from Info.plist
+ * from Darwin. */
+ {
+ .formats = SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_BE,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ },
+ {}
+};
+
+static int toonie_usable(struct codec_info_item *cii,
+ struct transfer_info *ti,
+ struct transfer_info *out)
+{
+ return 1;
+}
+
+#ifdef CONFIG_PM
+static int toonie_suspend(struct codec_info_item *cii, pm_message_t state)
+{
+ /* can we turn it off somehow? */
+ return 0;
+}
+
+static int toonie_resume(struct codec_info_item *cii)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct codec_info toonie_codec_info = {
+ .transfers = toonie_transfers,
+ .sysclock_factor = 256,
+ .bus_factor = 64,
+ .owner = THIS_MODULE,
+ .usable = toonie_usable,
+#ifdef CONFIG_PM
+ .suspend = toonie_suspend,
+ .resume = toonie_resume,
+#endif
+};
+
+static int toonie_init_codec(struct aoa_codec *codec)
+{
+ struct toonie *toonie = codec_to_toonie(codec);
+
+ /* nothing connected? what a joke! */
+ if (toonie->codec.connected != 1)
+ return -ENOTCONN;
+
+ if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
+ printk(KERN_ERR PFX "failed to create toonie snd device!\n");
+ return -ENODEV;
+ }
+
+ if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev,
+ aoa_get_card(),
+ &toonie_codec_info, toonie)) {
+ printk(KERN_ERR PFX "error creating toonie pcm\n");
+ snd_device_free(aoa_get_card(), toonie);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void toonie_exit_codec(struct aoa_codec *codec)
+{
+ struct toonie *toonie = codec_to_toonie(codec);
+
+ if (!toonie->codec.soundbus_dev) {
+ printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n");
+ return;
+ }
+ toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie);
+}
+
+static struct toonie *toonie;
+
+static int __init toonie_init(void)
+{
+ toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL);
+
+ if (!toonie)
+ return -ENOMEM;
+
+ strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
+ toonie->codec.owner = THIS_MODULE;
+ toonie->codec.init = toonie_init_codec;
+ toonie->codec.exit = toonie_exit_codec;
+
+ if (aoa_codec_register(&toonie->codec)) {
+ kfree(toonie);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __exit toonie_exit(void)
+{
+ aoa_codec_unregister(&toonie->codec);
+ kfree(toonie);
+}
+
+module_init(toonie_init);
+module_exit(toonie_exit);
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile
index 62dc7287f663..a1596e88c718 100644
--- a/sound/aoa/core/Makefile
+++ b/sound/aoa/core/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_SND_AOA) += snd-aoa.o
-snd-aoa-objs := snd-aoa-core.o \
- snd-aoa-alsa.o \
- snd-aoa-gpio-pmf.o \
- snd-aoa-gpio-feature.o
+snd-aoa-objs := core.o \
+ alsa.o \
+ gpio-pmf.o \
+ gpio-feature.o
diff --git a/sound/aoa/core/alsa.c b/sound/aoa/core/alsa.c
new file mode 100644
index 000000000000..617850463582
--- /dev/null
+++ b/sound/aoa/core/alsa.c
@@ -0,0 +1,99 @@
+/*
+ * Apple Onboard Audio Alsa helpers
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+#include <linux/module.h>
+#include "alsa.h"
+
+static int index = -1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "index for AOA sound card.");
+
+static struct aoa_card *aoa_card;
+
+int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
+{
+ struct snd_card *alsa_card;
+ int err;
+
+ if (aoa_card)
+ /* cannot be EEXIST due to usage in aoa_fabric_register */
+ return -EBUSY;
+
+ alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
+ if (!alsa_card)
+ return -ENOMEM;
+ aoa_card = alsa_card->private_data;
+ aoa_card->alsa_card = alsa_card;
+ alsa_card->dev = dev;
+ strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
+ strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
+ strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
+ strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
+ err = snd_card_register(aoa_card->alsa_card);
+ if (err < 0) {
+ printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ return err;
+ }
+ return 0;
+}
+
+struct snd_card *aoa_get_card(void)
+{
+ if (aoa_card)
+ return aoa_card->alsa_card;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_get_card);
+
+void aoa_alsa_cleanup(void)
+{
+ if (aoa_card) {
+ snd_card_free(aoa_card->alsa_card);
+ aoa_card = NULL;
+ }
+}
+
+int aoa_snd_device_new(snd_device_type_t type,
+ void * device_data, struct snd_device_ops * ops)
+{
+ struct snd_card *card = aoa_get_card();
+ int err;
+
+ if (!card) return -ENOMEM;
+
+ err = snd_device_new(card, type, device_data, ops);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
+ return err;
+ }
+ err = snd_device_register(card, device_data);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: failed to register "
+ "snd device (%d)\n", err);
+ printk(KERN_ERR "snd-aoa: have you forgotten the "
+ "dev_register callback?\n");
+ snd_device_free(card, device_data);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_device_new);
+
+int aoa_snd_ctl_add(struct snd_kcontrol* control)
+{
+ int err;
+
+ if (!aoa_card) return -ENODEV;
+
+ err = snd_ctl_add(aoa_card->alsa_card, control);
+ if (err)
+ printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n",
+ err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/alsa.h
index 9669e4489cab..9669e4489cab 100644
--- a/sound/aoa/core/snd-aoa-alsa.h
+++ b/sound/aoa/core/alsa.h
diff --git a/sound/aoa/core/core.c b/sound/aoa/core/core.c
new file mode 100644
index 000000000000..10bec6c61382
--- /dev/null
+++ b/sound/aoa/core/core.c
@@ -0,0 +1,162 @@
+/*
+ * Apple Onboard Audio driver core
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include "../aoa.h"
+#include "alsa.h"
+
+MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+
+/* We allow only one fabric. This simplifies things,
+ * and more don't really make that much sense */
+static struct aoa_fabric *fabric;
+static LIST_HEAD(codec_list);
+
+static int attach_codec_to_fabric(struct aoa_codec *c)
+{
+ int err;
+
+ if (!try_module_get(c->owner))
+ return -EBUSY;
+ /* found_codec has to be assigned */
+ err = -ENOENT;
+ if (fabric->found_codec)
+ err = fabric->found_codec(c);
+ if (err) {
+ module_put(c->owner);
+ printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
+ c->name);
+ return err;
+ }
+ c->fabric = fabric;
+
+ err = 0;
+ if (c->init)
+ err = c->init(c);
+ if (err) {
+ printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
+ c->fabric = NULL;
+ if (fabric->remove_codec)
+ fabric->remove_codec(c);
+ module_put(c->owner);
+ return err;
+ }
+ if (fabric->attached_codec)
+ fabric->attached_codec(c);
+ return 0;
+}
+
+int aoa_codec_register(struct aoa_codec *codec)
+{
+ int err = 0;
+
+ /* if there's a fabric already, we can tell if we
+ * will want to have this codec, so propagate error
+ * through. Otherwise, this will happen later... */
+ if (fabric)
+ err = attach_codec_to_fabric(codec);
+ if (!err)
+ list_add(&codec->list, &codec_list);
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_codec_register);
+
+void aoa_codec_unregister(struct aoa_codec *codec)
+{
+ list_del(&codec->list);
+ if (codec->fabric && codec->exit)
+ codec->exit(codec);
+ if (fabric && fabric->remove_codec)
+ fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_codec_unregister);
+
+int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
+{
+ struct aoa_codec *c;
+ int err;
+
+ /* allow querying for presence of fabric
+ * (i.e. do this test first!) */
+ if (new_fabric == fabric) {
+ err = -EALREADY;
+ goto attach;
+ }
+ if (fabric)
+ return -EEXIST;
+ if (!new_fabric)
+ return -EINVAL;
+
+ err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
+ if (err)
+ return err;
+
+ fabric = new_fabric;
+
+ attach:
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric != fabric)
+ attach_codec_to_fabric(c);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_register);
+
+void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
+{
+ struct aoa_codec *c;
+
+ if (fabric != old_fabric)
+ return;
+
+ list_for_each_entry(c, &codec_list, list) {
+ if (c->fabric)
+ aoa_fabric_unlink_codec(c);
+ }
+
+ aoa_alsa_cleanup();
+
+ fabric = NULL;
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
+
+void aoa_fabric_unlink_codec(struct aoa_codec *codec)
+{
+ if (!codec->fabric) {
+ printk(KERN_ERR "snd-aoa: fabric unassigned "
+ "in aoa_fabric_unlink_codec\n");
+ dump_stack();
+ return;
+ }
+ if (codec->exit)
+ codec->exit(codec);
+ if (codec->fabric->remove_codec)
+ codec->fabric->remove_codec(codec);
+ codec->fabric = NULL;
+ module_put(codec->owner);
+}
+EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
+
+static int __init aoa_init(void)
+{
+ return 0;
+}
+
+static void __exit aoa_exit(void)
+{
+ aoa_alsa_cleanup();
+}
+
+module_init(aoa_init);
+module_exit(aoa_exit);
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c
new file mode 100644
index 000000000000..c93ad5dec66b
--- /dev/null
+++ b/sound/aoa/core/gpio-feature.c
@@ -0,0 +1,408 @@
+/*
+ * Apple Onboard Audio feature call GPIO control
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ * This file contains the GPIO control routines for
+ * direct (through feature calls) access to the GPIO
+ * registers.
+ */
+
+#include <asm/pmac_feature.h>
+#include <linux/interrupt.h>
+#include "../aoa.h"
+
+/* TODO: these are 20 global variables
+ * that aren't used on most machines...
+ * Move them into a dynamically allocated
+ * structure and use that.
+ */
+
+/* these are the GPIO numbers (register addresses as offsets into
+ * the GPIO space) */
+static int headphone_mute_gpio;
+static int amp_mute_gpio;
+static int lineout_mute_gpio;
+static int hw_reset_gpio;
+static int lineout_detect_gpio;
+static int headphone_detect_gpio;
+static int linein_detect_gpio;
+
+/* see the SWITCH_GPIO macro */
+static int headphone_mute_gpio_activestate;
+static int amp_mute_gpio_activestate;
+static int lineout_mute_gpio_activestate;
+static int hw_reset_gpio_activestate;
+static int lineout_detect_gpio_activestate;
+static int headphone_detect_gpio_activestate;
+static int linein_detect_gpio_activestate;
+
+/* node pointers that we save when getting the GPIO number
+ * to get the interrupt later */
+static struct device_node *lineout_detect_node;
+static struct device_node *linein_detect_node;
+static struct device_node *headphone_detect_node;
+
+static int lineout_detect_irq;
+static int linein_detect_irq;
+static int headphone_detect_irq;
+
+static struct device_node *get_gpio(char *name,
+ char *altname,
+ int *gpioptr,
+ int *gpioactiveptr)
+{
+ struct device_node *np, *gpio;
+ const u32 *reg;
+ const char *audio_gpio;
+
+ *gpioptr = -1;
+
+ /* check if we can get it the easy way ... */
+ np = of_find_node_by_name(NULL, name);
+ if (!np) {
+ /* some machines have only gpioX/extint-gpioX nodes,
+ * and an audio-gpio property saying what it is ...
+ * So what we have to do is enumerate all children
+ * of the gpio node and check them all. */
+ gpio = of_find_node_by_name(NULL, "gpio");
+ if (!gpio)
+ return NULL;
+ while ((np = of_get_next_child(gpio, np))) {
+ audio_gpio = of_get_property(np, "audio-gpio", NULL);
+ if (!audio_gpio)
+ continue;
+ if (strcmp(audio_gpio, name) == 0)
+ break;
+ if (altname && (strcmp(audio_gpio, altname) == 0))
+ break;
+ }
+ /* still not found, assume not there */
+ if (!np)
+ return NULL;
+ }
+
+ reg = of_get_property(np, "reg", NULL);
+ if (!reg)
+ return NULL;
+
+ *gpioptr = *reg;
+
+ /* this is a hack, usually the GPIOs 'reg' property
+ * should have the offset based from the GPIO space
+ * which is at 0x50, but apparently not always... */
+ if (*gpioptr < 0x50)
+ *gpioptr += 0x50;
+
+ reg = of_get_property(np, "audio-gpio-active-state", NULL);
+ if (!reg)
+ /* Apple seems to default to 1, but
+ * that doesn't seem right at least on most
+ * machines. So until proven that the opposite
+ * is necessary, we default to 0
+ * (which, incidentally, snd-powermac also does...) */
+ *gpioactiveptr = 0;
+ else
+ *gpioactiveptr = *reg;
+
+ return np;
+}
+
+static void get_irq(struct device_node * np, int *irqptr)
+{
+ if (np)
+ *irqptr = irq_of_parse_and_map(np, 0);
+ else
+ *irqptr = NO_IRQ;
+}
+
+/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
+#define SWITCH_GPIO(name, v, on) \
+ (((v)&~1) | ((on)? \
+ (name##_gpio_activestate==0?4:5): \
+ (name##_gpio_activestate==0?5:4)))
+
+#define FTR_GPIO(name, bit) \
+static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\
+{ \
+ int v; \
+ \
+ if (unlikely(!rt)) return; \
+ \
+ if (name##_mute_gpio < 0) \
+ return; \
+ \
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \
+ name##_mute_gpio, \
+ 0); \
+ \
+ /* muted = !on... */ \
+ v = SWITCH_GPIO(name##_mute, v, !on); \
+ \
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \
+ name##_mute_gpio, v); \
+ \
+ rt->implementation_private &= ~(1<<bit); \
+ rt->implementation_private |= (!!on << bit); \
+} \
+static int ftr_gpio_get_##name(struct gpio_runtime *rt) \
+{ \
+ if (unlikely(!rt)) return 0; \
+ return (rt->implementation_private>>bit)&1; \
+}
+
+FTR_GPIO(headphone, 0);
+FTR_GPIO(amp, 1);
+FTR_GPIO(lineout, 2);
+
+static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
+{
+ int v;
+
+ if (unlikely(!rt)) return;
+ if (hw_reset_gpio < 0)
+ return;
+
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
+ hw_reset_gpio, 0);
+ v = SWITCH_GPIO(hw_reset, v, on);
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,
+ hw_reset_gpio, v);
+}
+
+static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
+{
+ int saved;
+
+ if (unlikely(!rt)) return;
+ saved = rt->implementation_private;
+ ftr_gpio_set_headphone(rt, 0);
+ ftr_gpio_set_amp(rt, 0);
+ ftr_gpio_set_lineout(rt, 0);
+ rt->implementation_private = saved;
+}
+
+static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)
+{
+ int s;
+
+ if (unlikely(!rt)) return;
+ s = rt->implementation_private;
+ ftr_gpio_set_headphone(rt, (s>>0)&1);
+ ftr_gpio_set_amp(rt, (s>>1)&1);
+ ftr_gpio_set_lineout(rt, (s>>2)&1);
+}
+
+static void ftr_handle_notify(struct work_struct *work)
+{
+ struct gpio_notification *notif =
+ container_of(work, struct gpio_notification, work.work);
+
+ mutex_lock(&notif->mutex);
+ if (notif->notify)
+ notif->notify(notif->data);
+ mutex_unlock(&notif->mutex);
+}
+
+static void gpio_enable_dual_edge(int gpio)
+{
+ int v;
+
+ if (gpio == -1)
+ return;
+ v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
+ v |= 0x80; /* enable dual edge */
+ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);
+}
+
+static void ftr_gpio_init(struct gpio_runtime *rt)
+{
+ get_gpio("headphone-mute", NULL,
+ &headphone_mute_gpio,
+ &headphone_mute_gpio_activestate);
+ get_gpio("amp-mute", NULL,
+ &amp_mute_gpio,
+ &amp_mute_gpio_activestate);
+ get_gpio("lineout-mute", NULL,
+ &lineout_mute_gpio,
+ &lineout_mute_gpio_activestate);
+ get_gpio("hw-reset", "audio-hw-reset",
+ &hw_reset_gpio,
+ &hw_reset_gpio_activestate);
+
+ headphone_detect_node = get_gpio("headphone-detect", NULL,
+ &headphone_detect_gpio,
+ &headphone_detect_gpio_activestate);
+ /* go Apple, and thanks for giving these different names
+ * across the board... */
+ lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",
+ &lineout_detect_gpio,
+ &lineout_detect_gpio_activestate);
+ linein_detect_node = get_gpio("linein-detect", "line-input-detect",
+ &linein_detect_gpio,
+ &linein_detect_gpio_activestate);
+
+ gpio_enable_dual_edge(headphone_detect_gpio);
+ gpio_enable_dual_edge(lineout_detect_gpio);
+ gpio_enable_dual_edge(linein_detect_gpio);
+
+ get_irq(headphone_detect_node, &headphone_detect_irq);
+ get_irq(lineout_detect_node, &lineout_detect_irq);
+ get_irq(linein_detect_node, &linein_detect_irq);
+
+ ftr_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+ INIT_DELAYED_WORK(&rt->headphone_notify.work, ftr_handle_notify);
+ INIT_DELAYED_WORK(&rt->line_in_notify.work, ftr_handle_notify);
+ INIT_DELAYED_WORK(&rt->line_out_notify.work, ftr_handle_notify);
+ mutex_init(&rt->headphone_notify.mutex);
+ mutex_init(&rt->line_in_notify.mutex);
+ mutex_init(&rt->line_out_notify.mutex);
+}
+
+static void ftr_gpio_exit(struct gpio_runtime *rt)
+{
+ ftr_gpio_all_amps_off(rt);
+ rt->implementation_private = 0;
+ if (rt->headphone_notify.notify)
+ free_irq(headphone_detect_irq, &rt->headphone_notify);
+ if (rt->line_in_notify.gpio_private)
+ free_irq(linein_detect_irq, &rt->line_in_notify);
+ if (rt->line_out_notify.gpio_private)
+ free_irq(lineout_detect_irq, &rt->line_out_notify);
+ cancel_delayed_work(&rt->headphone_notify.work);
+ cancel_delayed_work(&rt->line_in_notify.work);
+ cancel_delayed_work(&rt->line_out_notify.work);
+ flush_scheduled_work();
+ mutex_destroy(&rt->headphone_notify.mutex);
+ mutex_destroy(&rt->line_in_notify.mutex);
+ mutex_destroy(&rt->line_out_notify.mutex);
+}
+
+static irqreturn_t ftr_handle_notify_irq(int xx, void *data)
+{
+ struct gpio_notification *notif = data;
+
+ schedule_delayed_work(&notif->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int ftr_set_notify(struct gpio_runtime *rt,
+ enum notify_type type,
+ notify_func_t notify,
+ void *data)
+{
+ struct gpio_notification *notif;
+ notify_func_t old;
+ int irq;
+ char *name;
+ int err = -EBUSY;
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ notif = &rt->headphone_notify;
+ name = "headphone-detect";
+ irq = headphone_detect_irq;
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ notif = &rt->line_in_notify;
+ name = "linein-detect";
+ irq = linein_detect_irq;
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ notif = &rt->line_out_notify;
+ name = "lineout-detect";
+ irq = lineout_detect_irq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (irq == NO_IRQ)
+ return -ENODEV;
+
+ mutex_lock(&notif->mutex);
+
+ old = notif->notify;
+
+ if (!old && !notify) {
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && notify) {
+ if (old == notify && notif->data == data)
+ err = 0;
+ goto out_unlock;
+ }
+
+ if (old && !notify)
+ free_irq(irq, notif);
+
+ if (!old && notify) {
+ err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
+ if (err)
+ goto out_unlock;
+ }
+
+ notif->notify = notify;
+ notif->data = data;
+
+ err = 0;
+ out_unlock:
+ mutex_unlock(&notif->mutex);
+ return err;
+}
+
+static int ftr_get_detect(struct gpio_runtime *rt,
+ enum notify_type type)
+{
+ int gpio, ret, active;
+
+ switch (type) {
+ case AOA_NOTIFY_HEADPHONE:
+ gpio = headphone_detect_gpio;
+ active = headphone_detect_gpio_activestate;
+ break;
+ case AOA_NOTIFY_LINE_IN:
+ gpio = linein_detect_gpio;
+ active = linein_detect_gpio_activestate;
+ break;
+ case AOA_NOTIFY_LINE_OUT:
+ gpio = lineout_detect_gpio;
+ active = lineout_detect_gpio_activestate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (gpio == -1)
+ return -ENODEV;
+
+ ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
+ if (ret < 0)
+ return ret;
+ return ((ret >> 1) & 1) == active;
+}
+
+static struct gpio_methods methods = {
+ .init = ftr_gpio_init,
+ .exit = ftr_gpio_exit,
+ .all_amps_off = ftr_gpio_all_amps_off,
+ .all_amps_restore = ftr_gpio_all_amps_restore,
+ .set_headphone = ftr_gpio_set_headphone,
+ .set_speakers = ftr_gpio_set_amp,
+ .set_lineout = ftr_gpio_set_lineout,
+ .set_hw_reset = ftr_gpio_set_hw_reset,
+ .get_headphone = ftr_gpio_get_headphone,
+ .get_speakers = ftr_gpio_get_amp,
+ .get_lineout = ftr_gpio_get_lineout,
+ .set_notify = ftr_set_notify,
+ .get_detect = ftr_get_detect,
+};
+
+struct gpio_methods *ftr_gpio_methods = &methods;
+EXPORT_SYMBOL_GPL(ftr_gpio_methods);
diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/gpio-pmf.c
index 5ca2220eac7d..5ca2220eac7d 100644
--- a/sound/aoa/core/snd-aoa-gpio-pmf.c
+++ b/sound/aoa/core/gpio-pmf.c
diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c
deleted file mode 100644
index 17fe689ed287..000000000000
--- a/sound/aoa/core/snd-aoa-alsa.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Apple Onboard Audio Alsa helpers
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
-#include <linux/module.h>
-#include "snd-aoa-alsa.h"
-
-static int index = -1;
-module_param(index, int, 0444);
-MODULE_PARM_DESC(index, "index for AOA sound card.");
-
-static struct aoa_card *aoa_card;
-
-int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
-{
- struct snd_card *alsa_card;
- int err;
-
- if (aoa_card)
- /* cannot be EEXIST due to usage in aoa_fabric_register */
- return -EBUSY;
-
- alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
- if (!alsa_card)
- return -ENOMEM;
- aoa_card = alsa_card->private_data;
- aoa_card->alsa_card = alsa_card;
- alsa_card->dev = dev;
- strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
- strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
- strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
- strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
- err = snd_card_register(aoa_card->alsa_card);
- if (err < 0) {
- printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
- snd_card_free(aoa_card->alsa_card);
- aoa_card = NULL;
- return err;
- }
- return 0;
-}
-
-struct snd_card *aoa_get_card(void)
-{
- if (aoa_card)
- return aoa_card->alsa_card;
- return NULL;
-}
-EXPORT_SYMBOL_GPL(aoa_get_card);
-
-void aoa_alsa_cleanup(void)
-{
- if (aoa_card) {
- snd_card_free(aoa_card->alsa_card);
- aoa_card = NULL;
- }
-}
-
-int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops)
-{
- struct snd_card *card = aoa_get_card();
- int err;
-
- if (!card) return -ENOMEM;
-
- err = snd_device_new(card, type, device_data, ops);
- if (err) {
- printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
- return err;
- }
- err = snd_device_register(card, device_data);
- if (err) {
- printk(KERN_ERR "snd-aoa: failed to register "
- "snd device (%d)\n", err);
- printk(KERN_ERR "snd-aoa: have you forgotten the "
- "dev_register callback?\n");
- snd_device_free(card, device_data);
- }
- return err;
-}
-EXPORT_SYMBOL_GPL(aoa_snd_device_new);
-
-int aoa_snd_ctl_add(struct snd_kcontrol* control)
-{
- int err;
-
- if (!aoa_card) return -ENODEV;
-
- err = snd_ctl_add(aoa_card->alsa_card, control);
- if (err)
- printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n",
- err);
- return err;
-}
-EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c
deleted file mode 100644
index 19fdae400687..000000000000
--- a/sound/aoa/core/snd-aoa-core.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Apple Onboard Audio driver core
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/list.h>
-#include "../aoa.h"
-#include "snd-aoa-alsa.h"
-
-MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_LICENSE("GPL");
-
-/* We allow only one fabric. This simplifies things,
- * and more don't really make that much sense */
-static struct aoa_fabric *fabric;
-static LIST_HEAD(codec_list);
-
-static int attach_codec_to_fabric(struct aoa_codec *c)
-{
- int err;
-
- if (!try_module_get(c->owner))
- return -EBUSY;
- /* found_codec has to be assigned */
- err = -ENOENT;
- if (fabric->found_codec)
- err = fabric->found_codec(c);
- if (err) {
- module_put(c->owner);
- printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
- c->name);
- return err;
- }
- c->fabric = fabric;
-
- err = 0;
- if (c->init)
- err = c->init(c);
- if (err) {
- printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
- c->fabric = NULL;
- if (fabric->remove_codec)
- fabric->remove_codec(c);
- module_put(c->owner);
- return err;
- }
- if (fabric->attached_codec)
- fabric->attached_codec(c);
- return 0;
-}
-
-int aoa_codec_register(struct aoa_codec *codec)
-{
- int err = 0;
-
- /* if there's a fabric already, we can tell if we
- * will want to have this codec, so propagate error
- * through. Otherwise, this will happen later... */
- if (fabric)
- err = attach_codec_to_fabric(codec);
- if (!err)
- list_add(&codec->list, &codec_list);
- return err;
-}
-EXPORT_SYMBOL_GPL(aoa_codec_register);
-
-void aoa_codec_unregister(struct aoa_codec *codec)
-{
- list_del(&codec->list);
- if (codec->fabric && codec->exit)
- codec->exit(codec);
- if (fabric && fabric->remove_codec)
- fabric->remove_codec(codec);
- codec->fabric = NULL;
- module_put(codec->owner);
-}
-EXPORT_SYMBOL_GPL(aoa_codec_unregister);
-
-int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev)
-{
- struct aoa_codec *c;
- int err;
-
- /* allow querying for presence of fabric
- * (i.e. do this test first!) */
- if (new_fabric == fabric) {
- err = -EALREADY;
- goto attach;
- }
- if (fabric)
- return -EEXIST;
- if (!new_fabric)
- return -EINVAL;
-
- err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev);
- if (err)
- return err;
-
- fabric = new_fabric;
-
- attach:
- list_for_each_entry(c, &codec_list, list) {
- if (c->fabric != fabric)
- attach_codec_to_fabric(c);
- }
- return err;
-}
-EXPORT_SYMBOL_GPL(aoa_fabric_register);
-
-void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
-{
- struct aoa_codec *c;
-
- if (fabric != old_fabric)
- return;
-
- list_for_each_entry(c, &codec_list, list) {
- if (c->fabric)
- aoa_fabric_unlink_codec(c);
- }
-
- aoa_alsa_cleanup();
-
- fabric = NULL;
-}
-EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
-
-void aoa_fabric_unlink_codec(struct aoa_codec *codec)
-{
- if (!codec->fabric) {
- printk(KERN_ERR "snd-aoa: fabric unassigned "
- "in aoa_fabric_unlink_codec\n");
- dump_stack();
- return;
- }
- if (codec->exit)
- codec->exit(codec);
- if (codec->fabric->remove_codec)
- codec->fabric->remove_codec(codec);
- codec->fabric = NULL;
- module_put(codec->owner);
-}
-EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
-
-static int __init aoa_init(void)
-{
- return 0;
-}
-
-static void __exit aoa_exit(void)
-{
- aoa_alsa_cleanup();
-}
-
-module_init(aoa_init);
-module_exit(aoa_exit);
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c
deleted file mode 100644
index 805dcbff2257..000000000000
--- a/sound/aoa/core/snd-aoa-gpio-feature.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Apple Onboard Audio feature call GPIO control
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- *
- * This file contains the GPIO control routines for
- * direct (through feature calls) access to the GPIO
- * registers.
- */
-
-#include <asm/pmac_feature.h>
-#include <linux/interrupt.h>
-#include "../aoa.h"
-
-/* TODO: these are 20 global variables
- * that aren't used on most machines...
- * Move them into a dynamically allocated
- * structure and use that.
- */
-
-/* these are the GPIO numbers (register addresses as offsets into
- * the GPIO space) */
-static int headphone_mute_gpio;
-static int amp_mute_gpio;
-static int lineout_mute_gpio;
-static int hw_reset_gpio;
-static int lineout_detect_gpio;
-static int headphone_detect_gpio;
-static int linein_detect_gpio;
-
-/* see the SWITCH_GPIO macro */
-static int headphone_mute_gpio_activestate;
-static int amp_mute_gpio_activestate;
-static int lineout_mute_gpio_activestate;
-static int hw_reset_gpio_activestate;
-static int lineout_detect_gpio_activestate;
-static int headphone_detect_gpio_activestate;
-static int linein_detect_gpio_activestate;
-
-/* node pointers that we save when getting the GPIO number
- * to get the interrupt later */
-static struct device_node *lineout_detect_node;
-static struct device_node *linein_detect_node;
-static struct device_node *headphone_detect_node;
-
-static int lineout_detect_irq;
-static int linein_detect_irq;
-static int headphone_detect_irq;
-
-static struct device_node *get_gpio(char *name,
- char *altname,
- int *gpioptr,
- int *gpioactiveptr)
-{
- struct device_node *np, *gpio;
- const u32 *reg;
- const char *audio_gpio;
-
- *gpioptr = -1;
-
- /* check if we can get it the easy way ... */
- np = of_find_node_by_name(NULL, name);
- if (!np) {
- /* some machines have only gpioX/extint-gpioX nodes,
- * and an audio-gpio property saying what it is ...
- * So what we have to do is enumerate all children
- * of the gpio node and check them all. */
- gpio = of_find_node_by_name(NULL, "gpio");
- if (!gpio)
- return NULL;
- while ((np = of_get_next_child(gpio, np))) {
- audio_gpio = of_get_property(np, "audio-gpio", NULL);
- if (!audio_gpio)
- continue;
- if (strcmp(audio_gpio, name) == 0)
- break;
- if (altname && (strcmp(audio_gpio, altname) == 0))
- break;
- }
- /* still not found, assume not there */
- if (!np)
- return NULL;
- }
-
- reg = of_get_property(np, "reg", NULL);
- if (!reg)
- return NULL;
-
- *gpioptr = *reg;
-
- /* this is a hack, usually the GPIOs 'reg' property
- * should have the offset based from the GPIO space
- * which is at 0x50, but apparently not always... */
- if (*gpioptr < 0x50)
- *gpioptr += 0x50;
-
- reg = of_get_property(np, "audio-gpio-active-state", NULL);
- if (!reg)
- /* Apple seems to default to 1, but
- * that doesn't seem right at least on most
- * machines. So until proven that the opposite
- * is necessary, we default to 0
- * (which, incidentally, snd-powermac also does...) */
- *gpioactiveptr = 0;
- else
- *gpioactiveptr = *reg;
-
- return np;
-}
-
-static void get_irq(struct device_node * np, int *irqptr)
-{
- if (np)
- *irqptr = irq_of_parse_and_map(np, 0);
- else
- *irqptr = NO_IRQ;
-}
-
-/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
-#define SWITCH_GPIO(name, v, on) \
- (((v)&~1) | ((on)? \
- (name##_gpio_activestate==0?4:5): \
- (name##_gpio_activestate==0?5:4)))
-
-#define FTR_GPIO(name, bit) \
-static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\
-{ \
- int v; \
- \
- if (unlikely(!rt)) return; \
- \
- if (name##_mute_gpio < 0) \
- return; \
- \
- v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \
- name##_mute_gpio, \
- 0); \
- \
- /* muted = !on... */ \
- v = SWITCH_GPIO(name##_mute, v, !on); \
- \
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \
- name##_mute_gpio, v); \
- \
- rt->implementation_private &= ~(1<<bit); \
- rt->implementation_private |= (!!on << bit); \
-} \
-static int ftr_gpio_get_##name(struct gpio_runtime *rt) \
-{ \
- if (unlikely(!rt)) return 0; \
- return (rt->implementation_private>>bit)&1; \
-}
-
-FTR_GPIO(headphone, 0);
-FTR_GPIO(amp, 1);
-FTR_GPIO(lineout, 2);
-
-static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
-{
- int v;
-
- if (unlikely(!rt)) return;
- if (hw_reset_gpio < 0)
- return;
-
- v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
- hw_reset_gpio, 0);
- v = SWITCH_GPIO(hw_reset, v, on);
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,
- hw_reset_gpio, v);
-}
-
-static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
-{
- int saved;
-
- if (unlikely(!rt)) return;
- saved = rt->implementation_private;
- ftr_gpio_set_headphone(rt, 0);
- ftr_gpio_set_amp(rt, 0);
- ftr_gpio_set_lineout(rt, 0);
- rt->implementation_private = saved;
-}
-
-static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)
-{
- int s;
-
- if (unlikely(!rt)) return;
- s = rt->implementation_private;
- ftr_gpio_set_headphone(rt, (s>>0)&1);
- ftr_gpio_set_amp(rt, (s>>1)&1);
- ftr_gpio_set_lineout(rt, (s>>2)&1);
-}
-
-static void ftr_handle_notify(struct work_struct *work)
-{
- struct gpio_notification *notif =
- container_of(work, struct gpio_notification, work.work);
-
- mutex_lock(&notif->mutex);
- if (notif->notify)
- notif->notify(notif->data);
- mutex_unlock(&notif->mutex);
-}
-
-static void gpio_enable_dual_edge(int gpio)
-{
- int v;
-
- if (gpio == -1)
- return;
- v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
- v |= 0x80; /* enable dual edge */
- pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v);
-}
-
-static void ftr_gpio_init(struct gpio_runtime *rt)
-{
- get_gpio("headphone-mute", NULL,
- &headphone_mute_gpio,
- &headphone_mute_gpio_activestate);
- get_gpio("amp-mute", NULL,
- &amp_mute_gpio,
- &amp_mute_gpio_activestate);
- get_gpio("lineout-mute", NULL,
- &lineout_mute_gpio,
- &lineout_mute_gpio_activestate);
- get_gpio("hw-reset", "audio-hw-reset",
- &hw_reset_gpio,
- &hw_reset_gpio_activestate);
-
- headphone_detect_node = get_gpio("headphone-detect", NULL,
- &headphone_detect_gpio,
- &headphone_detect_gpio_activestate);
- /* go Apple, and thanks for giving these different names
- * across the board... */
- lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",
- &lineout_detect_gpio,
- &lineout_detect_gpio_activestate);
- linein_detect_node = get_gpio("linein-detect", "line-input-detect",
- &linein_detect_gpio,
- &linein_detect_gpio_activestate);
-
- gpio_enable_dual_edge(headphone_detect_gpio);
- gpio_enable_dual_edge(lineout_detect_gpio);
- gpio_enable_dual_edge(linein_detect_gpio);
-
- get_irq(headphone_detect_node, &headphone_detect_irq);
- get_irq(lineout_detect_node, &lineout_detect_irq);
- get_irq(linein_detect_node, &linein_detect_irq);
-
- ftr_gpio_all_amps_off(rt);
- rt->implementation_private = 0;
- INIT_DELAYED_WORK(&rt->headphone_notify.work, ftr_handle_notify);
- INIT_DELAYED_WORK(&rt->line_in_notify.work, ftr_handle_notify);
- INIT_DELAYED_WORK(&rt->line_out_notify.work, ftr_handle_notify);
- mutex_init(&rt->headphone_notify.mutex);
- mutex_init(&rt->line_in_notify.mutex);
- mutex_init(&rt->line_out_notify.mutex);
-}
-
-static void ftr_gpio_exit(struct gpio_runtime *rt)
-{
- ftr_gpio_all_amps_off(rt);
- rt->implementation_private = 0;
- if (rt->headphone_notify.notify)
- free_irq(headphone_detect_irq, &rt->headphone_notify);
- if (rt->line_in_notify.gpio_private)
- free_irq(linein_detect_irq, &rt->line_in_notify);
- if (rt->line_out_notify.gpio_private)
- free_irq(lineout_detect_irq, &rt->line_out_notify);
- cancel_delayed_work(&rt->headphone_notify.work);
- cancel_delayed_work(&rt->line_in_notify.work);
- cancel_delayed_work(&rt->line_out_notify.work);
- flush_scheduled_work();
- mutex_destroy(&rt->headphone_notify.mutex);
- mutex_destroy(&rt->line_in_notify.mutex);
- mutex_destroy(&rt->line_out_notify.mutex);
-}
-
-static irqreturn_t ftr_handle_notify_irq(int xx, void *data)
-{
- struct gpio_notification *notif = data;
-
- schedule_delayed_work(&notif->work, 0);
-
- return IRQ_HANDLED;
-}
-
-static int ftr_set_notify(struct gpio_runtime *rt,
- enum notify_type type,
- notify_func_t notify,
- void *data)
-{
- struct gpio_notification *notif;
- notify_func_t old;
- int irq;
- char *name;
- int err = -EBUSY;
-
- switch (type) {
- case AOA_NOTIFY_HEADPHONE:
- notif = &rt->headphone_notify;
- name = "headphone-detect";
- irq = headphone_detect_irq;
- break;
- case AOA_NOTIFY_LINE_IN:
- notif = &rt->line_in_notify;
- name = "linein-detect";
- irq = linein_detect_irq;
- break;
- case AOA_NOTIFY_LINE_OUT:
- notif = &rt->line_out_notify;
- name = "lineout-detect";
- irq = lineout_detect_irq;
- break;
- default:
- return -EINVAL;
- }
-
- if (irq == NO_IRQ)
- return -ENODEV;
-
- mutex_lock(&notif->mutex);
-
- old = notif->notify;
-
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
-
- if (old && notify) {
- if (old == notify && notif->data == data)
- err = 0;
- goto out_unlock;
- }
-
- if (old && !notify)
- free_irq(irq, notif);
-
- if (!old && notify) {
- err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
- if (err)
- goto out_unlock;
- }
-
- notif->notify = notify;
- notif->data = data;
-
- err = 0;
- out_unlock:
- mutex_unlock(&notif->mutex);
- return err;
-}
-
-static int ftr_get_detect(struct gpio_runtime *rt,
- enum notify_type type)
-{
- int gpio, ret, active;
-
- switch (type) {
- case AOA_NOTIFY_HEADPHONE:
- gpio = headphone_detect_gpio;
- active = headphone_detect_gpio_activestate;
- break;
- case AOA_NOTIFY_LINE_IN:
- gpio = linein_detect_gpio;
- active = linein_detect_gpio_activestate;
- break;
- case AOA_NOTIFY_LINE_OUT:
- gpio = lineout_detect_gpio;
- active = lineout_detect_gpio_activestate;
- break;
- default:
- return -EINVAL;
- }
-
- if (gpio == -1)
- return -ENODEV;
-
- ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
- if (ret < 0)
- return ret;
- return ((ret >> 1) & 1) == active;
-}
-
-static struct gpio_methods methods = {
- .init = ftr_gpio_init,
- .exit = ftr_gpio_exit,
- .all_amps_off = ftr_gpio_all_amps_off,
- .all_amps_restore = ftr_gpio_all_amps_restore,
- .set_headphone = ftr_gpio_set_headphone,
- .set_speakers = ftr_gpio_set_amp,
- .set_lineout = ftr_gpio_set_lineout,
- .set_hw_reset = ftr_gpio_set_hw_reset,
- .get_headphone = ftr_gpio_get_headphone,
- .get_speakers = ftr_gpio_get_amp,
- .get_lineout = ftr_gpio_get_lineout,
- .set_notify = ftr_set_notify,
- .get_detect = ftr_get_detect,
-};
-
-struct gpio_methods *ftr_gpio_methods = &methods;
-EXPORT_SYMBOL_GPL(ftr_gpio_methods);
diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile
index 55fc5e7e52cf..da37c10eca51 100644
--- a/sound/aoa/fabrics/Makefile
+++ b/sound/aoa/fabrics/Makefile
@@ -1 +1,3 @@
+snd-aoa-fabric-layout-objs += layout.o
+
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
new file mode 100644
index 000000000000..ad60f5d10e82
--- /dev/null
+++ b/sound/aoa/fabrics/layout.c
@@ -0,0 +1,1120 @@
+/*
+ * Apple Onboard Audio driver -- layout fabric
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ *
+ *
+ * This fabric module looks for sound codecs
+ * based on the layout-id property in the device tree.
+ *
+ */
+
+#include <asm/prom.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include "../aoa.h"
+#include "../soundbus/soundbus.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
+
+#define MAX_CODECS_PER_BUS 2
+
+/* These are the connections the layout fabric
+ * knows about. It doesn't really care about the
+ * input ones, but I thought I'd separate them
+ * to give them proper names. The thing is that
+ * Apple usually will distinguish the active output
+ * by GPIOs, while the active input is set directly
+ * on the codec. Hence we here tell the codec what
+ * we think is connected. This information is hard-
+ * coded below ... */
+#define CC_SPEAKERS (1<<0)
+#define CC_HEADPHONE (1<<1)
+#define CC_LINEOUT (1<<2)
+#define CC_DIGITALOUT (1<<3)
+#define CC_LINEIN (1<<4)
+#define CC_MICROPHONE (1<<5)
+#define CC_DIGITALIN (1<<6)
+/* pretty bogus but users complain...
+ * This is a flag saying that the LINEOUT
+ * should be renamed to HEADPHONE.
+ * be careful with input detection! */
+#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7)
+
+struct codec_connection {
+ /* CC_ flags from above */
+ int connected;
+ /* codec dependent bit to be set in the aoa_codec.connected field.
+ * This intentionally doesn't have any generic flags because the
+ * fabric has to know the codec anyway and all codecs might have
+ * different connectors */
+ int codec_bit;
+};
+
+struct codec_connect_info {
+ char *name;
+ struct codec_connection *connections;
+};
+
+#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
+
+struct layout {
+ unsigned int layout_id;
+ struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
+ int flags;
+
+ /* if busname is not assigned, we use 'Master' below,
+ * so that our layout table doesn't need to be filled
+ * too much.
+ * We only assign these two if we expect to find more
+ * than one soundbus, i.e. on those machines with
+ * multiple layout-ids */
+ char *busname;
+ int pcmid;
+};
+
+MODULE_ALIAS("sound-layout-36");
+MODULE_ALIAS("sound-layout-41");
+MODULE_ALIAS("sound-layout-45");
+MODULE_ALIAS("sound-layout-47");
+MODULE_ALIAS("sound-layout-48");
+MODULE_ALIAS("sound-layout-49");
+MODULE_ALIAS("sound-layout-50");
+MODULE_ALIAS("sound-layout-51");
+MODULE_ALIAS("sound-layout-56");
+MODULE_ALIAS("sound-layout-57");
+MODULE_ALIAS("sound-layout-58");
+MODULE_ALIAS("sound-layout-60");
+MODULE_ALIAS("sound-layout-61");
+MODULE_ALIAS("sound-layout-62");
+MODULE_ALIAS("sound-layout-64");
+MODULE_ALIAS("sound-layout-65");
+MODULE_ALIAS("sound-layout-66");
+MODULE_ALIAS("sound-layout-67");
+MODULE_ALIAS("sound-layout-68");
+MODULE_ALIAS("sound-layout-69");
+MODULE_ALIAS("sound-layout-70");
+MODULE_ALIAS("sound-layout-72");
+MODULE_ALIAS("sound-layout-76");
+MODULE_ALIAS("sound-layout-80");
+MODULE_ALIAS("sound-layout-82");
+MODULE_ALIAS("sound-layout-84");
+MODULE_ALIAS("sound-layout-86");
+MODULE_ALIAS("sound-layout-90");
+MODULE_ALIAS("sound-layout-92");
+MODULE_ALIAS("sound-layout-94");
+MODULE_ALIAS("sound-layout-96");
+MODULE_ALIAS("sound-layout-98");
+MODULE_ALIAS("sound-layout-100");
+
+/* onyx with all but microphone connected */
+static struct codec_connection onyx_connections_nomic[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* onyx on machines without headphone */
+static struct codec_connection onyx_connections_noheadphones[] = {
+ {
+ .connected = CC_SPEAKERS | CC_LINEOUT |
+ CC_LINEOUT_LABELLED_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ /* FIXME: are these correct? probably not for all the machines
+ * below ... If not this will need separating. */
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* onyx on machines with real line-out */
+static struct codec_connection onyx_connections_reallineout[] = {
+ {
+ .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines without line out */
+static struct codec_connection tas_connections_nolineout[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines with neither line out nor line in */
+static struct codec_connection tas_connections_noline[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines without microphone */
+static struct codec_connection tas_connections_nomic[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+/* tas on machines with everything connected */
+static struct codec_connection tas_connections_all[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_LINEIN,
+ .codec_bit = 2,
+ },
+ {
+ .connected = CC_MICROPHONE,
+ .codec_bit = 3,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection toonie_connections[] = {
+ {
+ .connected = CC_SPEAKERS | CC_HEADPHONE,
+ .codec_bit = 0,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_input[] = {
+ {
+ .connected = CC_DIGITALIN,
+ .codec_bit = 0,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_output[] = {
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct codec_connection topaz_inout[] = {
+ {
+ .connected = CC_DIGITALIN,
+ .codec_bit = 0,
+ },
+ {
+ .connected = CC_DIGITALOUT,
+ .codec_bit = 1,
+ },
+ {} /* terminate array by .connected == 0 */
+};
+
+static struct layout layouts[] = {
+ /* last PowerBooks (15" Oct 2005) */
+ { .layout_id = 82,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 60,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_reallineout,
+ },
+ },
+ /* PowerMac9,1 */
+ { .layout_id = 61,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook5,7 */
+ { .layout_id = 64,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ /* PowerBook5,7 */
+ { .layout_id = 65,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook5,9 [17" Oct 2005] */
+ { .layout_id = 84,
+ .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerMac8,1 */
+ { .layout_id = 45,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* Quad PowerMac (analog in, analog/digital out) */
+ { .layout_id = 68,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ },
+ /* Quad PowerMac (digital in) */
+ { .layout_id = 69,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ .busname = "digital in", .pcmid = 1 },
+ /* Early 2005 PowerBook (PowerBook 5,6) */
+ { .layout_id = 70,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook 5,4 */
+ { .layout_id = 51,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook6,7 */
+ { .layout_id = 80,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_noline,
+ },
+ },
+ /* PowerBook6,8 */
+ { .layout_id = 72,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerMac8,2 */
+ { .layout_id = 86,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ /* PowerBook6,7 */
+ { .layout_id = 92,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerMac10,1 (Mac Mini) */
+ { .layout_id = 58,
+ .codecs[0] = {
+ .name = "toonie",
+ .connections = toonie_connections,
+ },
+ },
+ {
+ .layout_id = 96,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ /* unknown, untested, but this comes from Apple */
+ { .layout_id = 41,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
+ { .layout_id = 36,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_inout,
+ },
+ },
+ { .layout_id = 47,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 48,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 49,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_nomic,
+ },
+ },
+ { .layout_id = 50,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 56,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 57,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 62,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_output,
+ },
+ },
+ { .layout_id = 66,
+ .codecs[0] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 67,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ },
+ { .layout_id = 76,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nomic,
+ },
+ .codecs[1] = {
+ .name = "topaz",
+ .connections = topaz_inout,
+ },
+ },
+ { .layout_id = 90,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_noline,
+ },
+ },
+ { .layout_id = 94,
+ .codecs[0] = {
+ .name = "onyx",
+ /* but it has an external mic?? how to select? */
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ { .layout_id = 98,
+ .codecs[0] = {
+ .name = "toonie",
+ .connections = toonie_connections,
+ },
+ },
+ { .layout_id = 100,
+ .codecs[0] = {
+ .name = "topaz",
+ .connections = topaz_input,
+ },
+ .codecs[1] = {
+ .name = "onyx",
+ .connections = onyx_connections_noheadphones,
+ },
+ },
+ {}
+};
+
+static struct layout *find_layout_by_id(unsigned int id)
+{
+ struct layout *l;
+
+ l = layouts;
+ while (l->layout_id) {
+ if (l->layout_id == id)
+ return l;
+ l++;
+ }
+ return NULL;
+}
+
+static void use_layout(struct layout *l)
+{
+ int i;
+
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (l->codecs[i].name) {
+ request_module("snd-aoa-codec-%s", l->codecs[i].name);
+ }
+ }
+ /* now we wait for the codecs to call us back */
+}
+
+struct layout_dev;
+
+struct layout_dev_ptr {
+ struct layout_dev *ptr;
+};
+
+struct layout_dev {
+ struct list_head list;
+ struct soundbus_dev *sdev;
+ struct device_node *sound;
+ struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
+ struct layout *layout;
+ struct gpio_runtime gpio;
+
+ /* we need these for headphone/lineout detection */
+ struct snd_kcontrol *headphone_ctrl;
+ struct snd_kcontrol *lineout_ctrl;
+ struct snd_kcontrol *speaker_ctrl;
+ struct snd_kcontrol *headphone_detected_ctrl;
+ struct snd_kcontrol *lineout_detected_ctrl;
+
+ struct layout_dev_ptr selfptr_headphone;
+ struct layout_dev_ptr selfptr_lineout;
+
+ u32 have_lineout_detect:1,
+ have_headphone_detect:1,
+ switch_on_headphone:1,
+ switch_on_lineout:1;
+};
+
+static LIST_HEAD(layouts_list);
+static int layouts_list_items;
+/* this can go away but only if we allow multiple cards,
+ * make the fabric handle all the card stuff, etc... */
+static struct layout_dev *layout_device;
+
+#define control_info snd_ctl_boolean_mono_info
+
+#define AMP_CONTROL(n, description) \
+static int n##_control_get(struct snd_kcontrol *kcontrol, \
+ struct snd_ctl_elem_value *ucontrol) \
+{ \
+ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
+ if (gpio->methods && gpio->methods->get_##n) \
+ ucontrol->value.integer.value[0] = \
+ gpio->methods->get_##n(gpio); \
+ return 0; \
+} \
+static int n##_control_put(struct snd_kcontrol *kcontrol, \
+ struct snd_ctl_elem_value *ucontrol) \
+{ \
+ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
+ if (gpio->methods && gpio->methods->get_##n) \
+ gpio->methods->set_##n(gpio, \
+ !!ucontrol->value.integer.value[0]); \
+ return 1; \
+} \
+static struct snd_kcontrol_new n##_ctl = { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = description, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .info = control_info, \
+ .get = n##_control_get, \
+ .put = n##_control_put, \
+}
+
+AMP_CONTROL(headphone, "Headphone Switch");
+AMP_CONTROL(speakers, "Speakers Switch");
+AMP_CONTROL(lineout, "Line-Out Switch");
+
+static int detect_choice_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
+ break;
+ case 1:
+ ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int detect_choice_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+
+ switch (kcontrol->private_value) {
+ case 0:
+ ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
+ break;
+ case 1:
+ ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
+ break;
+ default:
+ return -ENODEV;
+ }
+ return 1;
+}
+
+static struct snd_kcontrol_new headphone_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detect_choice = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detect Autoswitch",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = detect_choice_get,
+ .put = detect_choice_put,
+ .private_value = 1,
+};
+
+static int detected_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
+ int v;
+
+ switch (kcontrol->private_value) {
+ case 0:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ break;
+ case 1:
+ v = ldev->gpio.methods->get_detect(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT);
+ break;
+ default:
+ return -ENODEV;
+ }
+ ucontrol->value.integer.value[0] = v;
+ return 0;
+}
+
+static struct snd_kcontrol_new headphone_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Headphone Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 0,
+};
+
+static struct snd_kcontrol_new lineout_detected = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-Out Detected",
+ .info = control_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .get = detected_get,
+ .private_value = 1,
+};
+
+static int check_codec(struct aoa_codec *codec,
+ struct layout_dev *ldev,
+ struct codec_connect_info *cci)
+{
+ const u32 *ref;
+ char propname[32];
+ struct codec_connection *cc;
+
+ /* if the codec has a 'codec' node, we require a reference */
+ if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
+ snprintf(propname, sizeof(propname),
+ "platform-%s-codec-ref", codec->name);
+ ref = of_get_property(ldev->sound, propname, NULL);
+ if (!ref) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "required property %s not present\n", propname);
+ return -ENODEV;
+ }
+ if (*ref != codec->node->linux_phandle) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "%s doesn't match!\n", propname);
+ return -ENODEV;
+ }
+ } else {
+ if (layouts_list_items != 1) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: "
+ "more than one soundbus, but no references.\n");
+ return -ENODEV;
+ }
+ }
+ codec->soundbus_dev = ldev->sdev;
+ codec->gpio = &ldev->gpio;
+
+ cc = cci->connections;
+ if (!cc)
+ return -EINVAL;
+
+ printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
+
+ codec->connected = 0;
+ codec->fabric_data = cc;
+
+ while (cc->connected) {
+ codec->connected |= 1<<cc->codec_bit;
+ cc++;
+ }
+
+ return 0;
+}
+
+static int layout_found_codec(struct aoa_codec *codec)
+{
+ struct layout_dev *ldev;
+ int i;
+
+ list_for_each_entry(ldev, &layouts_list, list) {
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (!ldev->layout->codecs[i].name)
+ continue;
+ if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
+ if (check_codec(codec,
+ ldev,
+ &ldev->layout->codecs[i]) == 0)
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+static void layout_remove_codec(struct aoa_codec *codec)
+{
+ int i;
+ /* here remove the codec from the layout dev's
+ * codec reference */
+
+ codec->soundbus_dev = NULL;
+ codec->gpio = NULL;
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ }
+}
+
+static void layout_notify(void *data)
+{
+ struct layout_dev_ptr *dptr = data;
+ struct layout_dev *ldev;
+ int v, update;
+ struct snd_kcontrol *detected, *c;
+ struct snd_card *card = aoa_get_card();
+
+ ldev = dptr->ptr;
+ if (data == &ldev->selfptr_headphone) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
+ detected = ldev->headphone_detected_ctrl;
+ update = ldev->switch_on_headphone;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, v);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
+ }
+ } else if (data == &ldev->selfptr_lineout) {
+ v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
+ detected = ldev->lineout_detected_ctrl;
+ update = ldev->switch_on_lineout;
+ if (update) {
+ ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
+ ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
+ ldev->gpio.methods->set_lineout(&ldev->gpio, v);
+ }
+ } else
+ return;
+
+ if (detected)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
+ if (update) {
+ c = ldev->headphone_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->speaker_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ c = ldev->lineout_ctrl;
+ if (c)
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
+ }
+}
+
+static void layout_attached_codec(struct aoa_codec *codec)
+{
+ struct codec_connection *cc;
+ struct snd_kcontrol *ctl;
+ int headphones, lineout;
+ struct layout_dev *ldev = layout_device;
+
+ /* need to add this codec to our codec array! */
+
+ cc = codec->fabric_data;
+
+ headphones = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_HEADPHONE);
+ lineout = codec->gpio->methods->get_detect(codec->gpio,
+ AOA_NOTIFY_LINE_OUT);
+
+ while (cc->connected) {
+ if (cc->connected & CC_SPEAKERS) {
+ if (headphones <= 0 && lineout <= 0)
+ ldev->gpio.methods->set_speakers(codec->gpio, 1);
+ ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
+ ldev->speaker_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ if (cc->connected & CC_HEADPHONE) {
+ if (headphones == 1)
+ ldev->gpio.methods->set_headphone(codec->gpio, 1);
+ ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
+ ldev->headphone_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_headphone_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ layout_notify,
+ &ldev->selfptr_headphone);
+ if (ldev->have_headphone_detect) {
+ ctl = snd_ctl_new1(&headphone_detect_choice,
+ ldev);
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&headphone_detected,
+ ldev);
+ ldev->headphone_detected_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ }
+ if (cc->connected & CC_LINEOUT) {
+ if (lineout == 1)
+ ldev->gpio.methods->set_lineout(codec->gpio, 1);
+ ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Switch", sizeof(ctl->id.name));
+ ldev->lineout_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ ldev->have_lineout_detect =
+ !ldev->gpio.methods
+ ->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ layout_notify,
+ &ldev->selfptr_lineout);
+ if (ldev->have_lineout_detect) {
+ ctl = snd_ctl_new1(&lineout_detect_choice,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detect Autoswitch",
+ sizeof(ctl->id.name));
+ aoa_snd_ctl_add(ctl);
+ ctl = snd_ctl_new1(&lineout_detected,
+ ldev);
+ if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
+ strlcpy(ctl->id.name,
+ "Headphone Detected",
+ sizeof(ctl->id.name));
+ ldev->lineout_detected_ctrl = ctl;
+ aoa_snd_ctl_add(ctl);
+ }
+ }
+ cc++;
+ }
+ /* now update initial state */
+ if (ldev->have_headphone_detect)
+ layout_notify(&ldev->selfptr_headphone);
+ if (ldev->have_lineout_detect)
+ layout_notify(&ldev->selfptr_lineout);
+}
+
+static struct aoa_fabric layout_fabric = {
+ .name = "SoundByLayout",
+ .owner = THIS_MODULE,
+ .found_codec = layout_found_codec,
+ .remove_codec = layout_remove_codec,
+ .attached_codec = layout_attached_codec,
+};
+
+static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
+{
+ struct device_node *sound = NULL;
+ const unsigned int *layout_id;
+ struct layout *layout;
+ struct layout_dev *ldev = NULL;
+ int err;
+
+ /* hm, currently we can only have one ... */
+ if (layout_device)
+ return -ENODEV;
+
+ /* by breaking out we keep a reference */
+ while ((sound = of_get_next_child(sdev->ofdev.node, sound))) {
+ if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
+ break;
+ }
+ if (!sound) return -ENODEV;
+
+ layout_id = of_get_property(sound, "layout-id", NULL);
+ if (!layout_id)
+ goto outnodev;
+ printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
+ *layout_id);
+
+ layout = find_layout_by_id(*layout_id);
+ if (!layout) {
+ printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
+ goto outnodev;
+ }
+
+ ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
+ if (!ldev)
+ goto outnodev;
+
+ layout_device = ldev;
+ ldev->sdev = sdev;
+ ldev->sound = sound;
+ ldev->layout = layout;
+ ldev->gpio.node = sound->parent;
+ switch (layout->layout_id) {
+ case 41: /* that unknown machine no one seems to have */
+ case 51: /* PowerBook5,4 */
+ case 58: /* Mac Mini */
+ ldev->gpio.methods = ftr_gpio_methods;
+ printk(KERN_DEBUG
+ "snd-aoa-fabric-layout: Using direct GPIOs\n");
+ break;
+ default:
+ ldev->gpio.methods = pmf_gpio_methods;
+ printk(KERN_DEBUG
+ "snd-aoa-fabric-layout: Using PMF GPIOs\n");
+ }
+ ldev->selfptr_headphone.ptr = ldev;
+ ldev->selfptr_lineout.ptr = ldev;
+ sdev->ofdev.dev.driver_data = ldev;
+ list_add(&ldev->list, &layouts_list);
+ layouts_list_items++;
+
+ /* assign these before registering ourselves, so
+ * callbacks that are done during registration
+ * already have the values */
+ sdev->pcmid = ldev->layout->pcmid;
+ if (ldev->layout->busname) {
+ sdev->pcmname = ldev->layout->busname;
+ } else {
+ sdev->pcmname = "Master";
+ }
+
+ ldev->gpio.methods->init(&ldev->gpio);
+
+ err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
+ if (err && err != -EALREADY) {
+ printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
+ " another fabric is active!\n");
+ goto outlistdel;
+ }
+
+ use_layout(layout);
+ ldev->switch_on_headphone = 1;
+ ldev->switch_on_lineout = 1;
+ return 0;
+ outlistdel:
+ /* we won't be using these then... */
+ ldev->gpio.methods->exit(&ldev->gpio);
+ /* reset if we didn't use it */
+ sdev->pcmname = NULL;
+ sdev->pcmid = -1;
+ list_del(&ldev->list);
+ layouts_list_items--;
+ outnodev:
+ of_node_put(sound);
+ layout_device = NULL;
+ kfree(ldev);
+ return -ENODEV;
+}
+
+static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+ int i;
+
+ for (i=0; i<MAX_CODECS_PER_BUS; i++) {
+ if (ldev->codecs[i]) {
+ aoa_fabric_unlink_codec(ldev->codecs[i]);
+ }
+ ldev->codecs[i] = NULL;
+ }
+ list_del(&ldev->list);
+ layouts_list_items--;
+ of_node_put(ldev->sound);
+
+ ldev->gpio.methods->set_notify(&ldev->gpio,
+ AOA_NOTIFY_HEADPHONE,
+ NULL,
+ NULL);
+ ldev->gpio.methods->set_notify(&ldev->gpio,
+ AOA_NOTIFY_LINE_OUT,
+ NULL,
+ NULL);
+
+ ldev->gpio.methods->exit(&ldev->gpio);
+ layout_device = NULL;
+ kfree(ldev);
+ sdev->pcmid = -1;
+ sdev->pcmname = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ ldev->gpio.methods->all_amps_off(&ldev->gpio);
+
+ return 0;
+}
+
+static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+{
+ struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ ldev->gpio.methods->all_amps_restore(&ldev->gpio);
+
+ return 0;
+}
+#endif
+
+static struct soundbus_driver aoa_soundbus_driver = {
+ .name = "snd_aoa_soundbus_drv",
+ .owner = THIS_MODULE,
+ .probe = aoa_fabric_layout_probe,
+ .remove = aoa_fabric_layout_remove,
+#ifdef CONFIG_PM
+ .suspend = aoa_fabric_layout_suspend,
+ .resume = aoa_fabric_layout_resume,
+#endif
+ .driver = {
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init aoa_fabric_layout_init(void)
+{
+ int err;
+
+ err = soundbus_register_driver(&aoa_soundbus_driver);
+ if (err)
+ return err;
+ return 0;
+}
+
+static void __exit aoa_fabric_layout_exit(void)
+{
+ soundbus_unregister_driver(&aoa_soundbus_driver);
+ aoa_fabric_unregister(&layout_fabric);
+}
+
+module_init(aoa_fabric_layout_init);
+module_exit(aoa_fabric_layout_exit);
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c
deleted file mode 100644
index dea7abb082cd..000000000000
--- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * Apple Onboard Audio driver -- layout fabric
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- *
- *
- * This fabric module looks for sound codecs
- * based on the layout-id property in the device tree.
- *
- */
-
-#include <asm/prom.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include "../aoa.h"
-#include "../soundbus/soundbus.h"
-
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
-
-#define MAX_CODECS_PER_BUS 2
-
-/* These are the connections the layout fabric
- * knows about. It doesn't really care about the
- * input ones, but I thought I'd separate them
- * to give them proper names. The thing is that
- * Apple usually will distinguish the active output
- * by GPIOs, while the active input is set directly
- * on the codec. Hence we here tell the codec what
- * we think is connected. This information is hard-
- * coded below ... */
-#define CC_SPEAKERS (1<<0)
-#define CC_HEADPHONE (1<<1)
-#define CC_LINEOUT (1<<2)
-#define CC_DIGITALOUT (1<<3)
-#define CC_LINEIN (1<<4)
-#define CC_MICROPHONE (1<<5)
-#define CC_DIGITALIN (1<<6)
-/* pretty bogus but users complain...
- * This is a flag saying that the LINEOUT
- * should be renamed to HEADPHONE.
- * be careful with input detection! */
-#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7)
-
-struct codec_connection {
- /* CC_ flags from above */
- int connected;
- /* codec dependent bit to be set in the aoa_codec.connected field.
- * This intentionally doesn't have any generic flags because the
- * fabric has to know the codec anyway and all codecs might have
- * different connectors */
- int codec_bit;
-};
-
-struct codec_connect_info {
- char *name;
- struct codec_connection *connections;
-};
-
-#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
-
-struct layout {
- unsigned int layout_id;
- struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
- int flags;
-
- /* if busname is not assigned, we use 'Master' below,
- * so that our layout table doesn't need to be filled
- * too much.
- * We only assign these two if we expect to find more
- * than one soundbus, i.e. on those machines with
- * multiple layout-ids */
- char *busname;
- int pcmid;
-};
-
-MODULE_ALIAS("sound-layout-36");
-MODULE_ALIAS("sound-layout-41");
-MODULE_ALIAS("sound-layout-45");
-MODULE_ALIAS("sound-layout-47");
-MODULE_ALIAS("sound-layout-48");
-MODULE_ALIAS("sound-layout-49");
-MODULE_ALIAS("sound-layout-50");
-MODULE_ALIAS("sound-layout-51");
-MODULE_ALIAS("sound-layout-56");
-MODULE_ALIAS("sound-layout-57");
-MODULE_ALIAS("sound-layout-58");
-MODULE_ALIAS("sound-layout-60");
-MODULE_ALIAS("sound-layout-61");
-MODULE_ALIAS("sound-layout-62");
-MODULE_ALIAS("sound-layout-64");
-MODULE_ALIAS("sound-layout-65");
-MODULE_ALIAS("sound-layout-66");
-MODULE_ALIAS("sound-layout-67");
-MODULE_ALIAS("sound-layout-68");
-MODULE_ALIAS("sound-layout-69");
-MODULE_ALIAS("sound-layout-70");
-MODULE_ALIAS("sound-layout-72");
-MODULE_ALIAS("sound-layout-76");
-MODULE_ALIAS("sound-layout-80");
-MODULE_ALIAS("sound-layout-82");
-MODULE_ALIAS("sound-layout-84");
-MODULE_ALIAS("sound-layout-86");
-MODULE_ALIAS("sound-layout-90");
-MODULE_ALIAS("sound-layout-92");
-MODULE_ALIAS("sound-layout-94");
-MODULE_ALIAS("sound-layout-96");
-MODULE_ALIAS("sound-layout-98");
-MODULE_ALIAS("sound-layout-100");
-
-/* onyx with all but microphone connected */
-static struct codec_connection onyx_connections_nomic[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
- .codec_bit = 0,
- },
- {
- .connected = CC_DIGITALOUT,
- .codec_bit = 1,
- },
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* onyx on machines without headphone */
-static struct codec_connection onyx_connections_noheadphones[] = {
- {
- .connected = CC_SPEAKERS | CC_LINEOUT |
- CC_LINEOUT_LABELLED_HEADPHONE,
- .codec_bit = 0,
- },
- {
- .connected = CC_DIGITALOUT,
- .codec_bit = 1,
- },
- /* FIXME: are these correct? probably not for all the machines
- * below ... If not this will need separating. */
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {
- .connected = CC_MICROPHONE,
- .codec_bit = 3,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* onyx on machines with real line-out */
-static struct codec_connection onyx_connections_reallineout[] = {
- {
- .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
- .codec_bit = 0,
- },
- {
- .connected = CC_DIGITALOUT,
- .codec_bit = 1,
- },
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* tas on machines without line out */
-static struct codec_connection tas_connections_nolineout[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE,
- .codec_bit = 0,
- },
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {
- .connected = CC_MICROPHONE,
- .codec_bit = 3,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* tas on machines with neither line out nor line in */
-static struct codec_connection tas_connections_noline[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE,
- .codec_bit = 0,
- },
- {
- .connected = CC_MICROPHONE,
- .codec_bit = 3,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* tas on machines without microphone */
-static struct codec_connection tas_connections_nomic[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
- .codec_bit = 0,
- },
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-/* tas on machines with everything connected */
-static struct codec_connection tas_connections_all[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
- .codec_bit = 0,
- },
- {
- .connected = CC_LINEIN,
- .codec_bit = 2,
- },
- {
- .connected = CC_MICROPHONE,
- .codec_bit = 3,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-static struct codec_connection toonie_connections[] = {
- {
- .connected = CC_SPEAKERS | CC_HEADPHONE,
- .codec_bit = 0,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-static struct codec_connection topaz_input[] = {
- {
- .connected = CC_DIGITALIN,
- .codec_bit = 0,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-static struct codec_connection topaz_output[] = {
- {
- .connected = CC_DIGITALOUT,
- .codec_bit = 1,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-static struct codec_connection topaz_inout[] = {
- {
- .connected = CC_DIGITALIN,
- .codec_bit = 0,
- },
- {
- .connected = CC_DIGITALOUT,
- .codec_bit = 1,
- },
- {} /* terminate array by .connected == 0 */
-};
-
-static struct layout layouts[] = {
- /* last PowerBooks (15" Oct 2005) */
- { .layout_id = 82,
- .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* PowerMac9,1 */
- { .layout_id = 60,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_reallineout,
- },
- },
- /* PowerMac9,1 */
- { .layout_id = 61,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* PowerBook5,7 */
- { .layout_id = 64,
- .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- /* PowerBook5,7 */
- { .layout_id = 65,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* PowerBook5,9 [17" Oct 2005] */
- { .layout_id = 84,
- .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* PowerMac8,1 */
- { .layout_id = 45,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* Quad PowerMac (analog in, analog/digital out) */
- { .layout_id = 68,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_nomic,
- },
- },
- /* Quad PowerMac (digital in) */
- { .layout_id = 69,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- .busname = "digital in", .pcmid = 1 },
- /* Early 2005 PowerBook (PowerBook 5,6) */
- { .layout_id = 70,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nolineout,
- },
- },
- /* PowerBook 5,4 */
- { .layout_id = 51,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nolineout,
- },
- },
- /* PowerBook6,7 */
- { .layout_id = 80,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_noline,
- },
- },
- /* PowerBook6,8 */
- { .layout_id = 72,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nolineout,
- },
- },
- /* PowerMac8,2 */
- { .layout_id = 86,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_nomic,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- /* PowerBook6,7 */
- { .layout_id = 92,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nolineout,
- },
- },
- /* PowerMac10,1 (Mac Mini) */
- { .layout_id = 58,
- .codecs[0] = {
- .name = "toonie",
- .connections = toonie_connections,
- },
- },
- {
- .layout_id = 96,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- /* unknown, untested, but this comes from Apple */
- { .layout_id = 41,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_all,
- },
- },
- { .layout_id = 36,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nomic,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_inout,
- },
- },
- { .layout_id = 47,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- { .layout_id = 48,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- { .layout_id = 49,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_nomic,
- },
- },
- { .layout_id = 50,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- { .layout_id = 56,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- { .layout_id = 57,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- { .layout_id = 62,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_output,
- },
- },
- { .layout_id = 66,
- .codecs[0] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- { .layout_id = 67,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- },
- { .layout_id = 76,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_nomic,
- },
- .codecs[1] = {
- .name = "topaz",
- .connections = topaz_inout,
- },
- },
- { .layout_id = 90,
- .codecs[0] = {
- .name = "tas",
- .connections = tas_connections_noline,
- },
- },
- { .layout_id = 94,
- .codecs[0] = {
- .name = "onyx",
- /* but it has an external mic?? how to select? */
- .connections = onyx_connections_noheadphones,
- },
- },
- { .layout_id = 98,
- .codecs[0] = {
- .name = "toonie",
- .connections = toonie_connections,
- },
- },
- { .layout_id = 100,
- .codecs[0] = {
- .name = "topaz",
- .connections = topaz_input,
- },
- .codecs[1] = {
- .name = "onyx",
- .connections = onyx_connections_noheadphones,
- },
- },
- {}
-};
-
-static struct layout *find_layout_by_id(unsigned int id)
-{
- struct layout *l;
-
- l = layouts;
- while (l->layout_id) {
- if (l->layout_id == id)
- return l;
- l++;
- }
- return NULL;
-}
-
-static void use_layout(struct layout *l)
-{
- int i;
-
- for (i=0; i<MAX_CODECS_PER_BUS; i++) {
- if (l->codecs[i].name) {
- request_module("snd-aoa-codec-%s", l->codecs[i].name);
- }
- }
- /* now we wait for the codecs to call us back */
-}
-
-struct layout_dev;
-
-struct layout_dev_ptr {
- struct layout_dev *ptr;
-};
-
-struct layout_dev {
- struct list_head list;
- struct soundbus_dev *sdev;
- struct device_node *sound;
- struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
- struct layout *layout;
- struct gpio_runtime gpio;
-
- /* we need these for headphone/lineout detection */
- struct snd_kcontrol *headphone_ctrl;
- struct snd_kcontrol *lineout_ctrl;
- struct snd_kcontrol *speaker_ctrl;
- struct snd_kcontrol *headphone_detected_ctrl;
- struct snd_kcontrol *lineout_detected_ctrl;
-
- struct layout_dev_ptr selfptr_headphone;
- struct layout_dev_ptr selfptr_lineout;
-
- u32 have_lineout_detect:1,
- have_headphone_detect:1,
- switch_on_headphone:1,
- switch_on_lineout:1;
-};
-
-static LIST_HEAD(layouts_list);
-static int layouts_list_items;
-/* this can go away but only if we allow multiple cards,
- * make the fabric handle all the card stuff, etc... */
-static struct layout_dev *layout_device;
-
-#define control_info snd_ctl_boolean_mono_info
-
-#define AMP_CONTROL(n, description) \
-static int n##_control_get(struct snd_kcontrol *kcontrol, \
- struct snd_ctl_elem_value *ucontrol) \
-{ \
- struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
- if (gpio->methods && gpio->methods->get_##n) \
- ucontrol->value.integer.value[0] = \
- gpio->methods->get_##n(gpio); \
- return 0; \
-} \
-static int n##_control_put(struct snd_kcontrol *kcontrol, \
- struct snd_ctl_elem_value *ucontrol) \
-{ \
- struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
- if (gpio->methods && gpio->methods->get_##n) \
- gpio->methods->set_##n(gpio, \
- !!ucontrol->value.integer.value[0]); \
- return 1; \
-} \
-static struct snd_kcontrol_new n##_ctl = { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = description, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = control_info, \
- .get = n##_control_get, \
- .put = n##_control_put, \
-}
-
-AMP_CONTROL(headphone, "Headphone Switch");
-AMP_CONTROL(speakers, "Speakers Switch");
-AMP_CONTROL(lineout, "Line-Out Switch");
-
-static int detect_choice_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
-
- switch (kcontrol->private_value) {
- case 0:
- ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
- break;
- case 1:
- ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
- break;
- default:
- return -ENODEV;
- }
- return 0;
-}
-
-static int detect_choice_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
-
- switch (kcontrol->private_value) {
- case 0:
- ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
- break;
- case 1:
- ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
- break;
- default:
- return -ENODEV;
- }
- return 1;
-}
-
-static struct snd_kcontrol_new headphone_detect_choice = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Headphone Detect Autoswitch",
- .info = control_info,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .get = detect_choice_get,
- .put = detect_choice_put,
- .private_value = 0,
-};
-
-static struct snd_kcontrol_new lineout_detect_choice = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line-Out Detect Autoswitch",
- .info = control_info,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .get = detect_choice_get,
- .put = detect_choice_put,
- .private_value = 1,
-};
-
-static int detected_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
- int v;
-
- switch (kcontrol->private_value) {
- case 0:
- v = ldev->gpio.methods->get_detect(&ldev->gpio,
- AOA_NOTIFY_HEADPHONE);
- break;
- case 1:
- v = ldev->gpio.methods->get_detect(&ldev->gpio,
- AOA_NOTIFY_LINE_OUT);
- break;
- default:
- return -ENODEV;
- }
- ucontrol->value.integer.value[0] = v;
- return 0;
-}
-
-static struct snd_kcontrol_new headphone_detected = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Headphone Detected",
- .info = control_info,
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .get = detected_get,
- .private_value = 0,
-};
-
-static struct snd_kcontrol_new lineout_detected = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line-Out Detected",
- .info = control_info,
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .get = detected_get,
- .private_value = 1,
-};
-
-static int check_codec(struct aoa_codec *codec,
- struct layout_dev *ldev,
- struct codec_connect_info *cci)
-{
- const u32 *ref;
- char propname[32];
- struct codec_connection *cc;
-
- /* if the codec has a 'codec' node, we require a reference */
- if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
- snprintf(propname, sizeof(propname),
- "platform-%s-codec-ref", codec->name);
- ref = of_get_property(ldev->sound, propname, NULL);
- if (!ref) {
- printk(KERN_INFO "snd-aoa-fabric-layout: "
- "required property %s not present\n", propname);
- return -ENODEV;
- }
- if (*ref != codec->node->linux_phandle) {
- printk(KERN_INFO "snd-aoa-fabric-layout: "
- "%s doesn't match!\n", propname);
- return -ENODEV;
- }
- } else {
- if (layouts_list_items != 1) {
- printk(KERN_INFO "snd-aoa-fabric-layout: "
- "more than one soundbus, but no references.\n");
- return -ENODEV;
- }
- }
- codec->soundbus_dev = ldev->sdev;
- codec->gpio = &ldev->gpio;
-
- cc = cci->connections;
- if (!cc)
- return -EINVAL;
-
- printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
-
- codec->connected = 0;
- codec->fabric_data = cc;
-
- while (cc->connected) {
- codec->connected |= 1<<cc->codec_bit;
- cc++;
- }
-
- return 0;
-}
-
-static int layout_found_codec(struct aoa_codec *codec)
-{
- struct layout_dev *ldev;
- int i;
-
- list_for_each_entry(ldev, &layouts_list, list) {
- for (i=0; i<MAX_CODECS_PER_BUS; i++) {
- if (!ldev->layout->codecs[i].name)
- continue;
- if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
- if (check_codec(codec,
- ldev,
- &ldev->layout->codecs[i]) == 0)
- return 0;
- }
- }
- }
- return -ENODEV;
-}
-
-static void layout_remove_codec(struct aoa_codec *codec)
-{
- int i;
- /* here remove the codec from the layout dev's
- * codec reference */
-
- codec->soundbus_dev = NULL;
- codec->gpio = NULL;
- for (i=0; i<MAX_CODECS_PER_BUS; i++) {
- }
-}
-
-static void layout_notify(void *data)
-{
- struct layout_dev_ptr *dptr = data;
- struct layout_dev *ldev;
- int v, update;
- struct snd_kcontrol *detected, *c;
- struct snd_card *card = aoa_get_card();
-
- ldev = dptr->ptr;
- if (data == &ldev->selfptr_headphone) {
- v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
- detected = ldev->headphone_detected_ctrl;
- update = ldev->switch_on_headphone;
- if (update) {
- ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
- ldev->gpio.methods->set_headphone(&ldev->gpio, v);
- ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
- }
- } else if (data == &ldev->selfptr_lineout) {
- v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
- detected = ldev->lineout_detected_ctrl;
- update = ldev->switch_on_lineout;
- if (update) {
- ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
- ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
- ldev->gpio.methods->set_lineout(&ldev->gpio, v);
- }
- } else
- return;
-
- if (detected)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
- if (update) {
- c = ldev->headphone_ctrl;
- if (c)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
- c = ldev->speaker_ctrl;
- if (c)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
- c = ldev->lineout_ctrl;
- if (c)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
- }
-}
-
-static void layout_attached_codec(struct aoa_codec *codec)
-{
- struct codec_connection *cc;
- struct snd_kcontrol *ctl;
- int headphones, lineout;
- struct layout_dev *ldev = layout_device;
-
- /* need to add this codec to our codec array! */
-
- cc = codec->fabric_data;
-
- headphones = codec->gpio->methods->get_detect(codec->gpio,
- AOA_NOTIFY_HEADPHONE);
- lineout = codec->gpio->methods->get_detect(codec->gpio,
- AOA_NOTIFY_LINE_OUT);
-
- while (cc->connected) {
- if (cc->connected & CC_SPEAKERS) {
- if (headphones <= 0 && lineout <= 0)
- ldev->gpio.methods->set_speakers(codec->gpio, 1);
- ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
- ldev->speaker_ctrl = ctl;
- aoa_snd_ctl_add(ctl);
- }
- if (cc->connected & CC_HEADPHONE) {
- if (headphones == 1)
- ldev->gpio.methods->set_headphone(codec->gpio, 1);
- ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
- ldev->headphone_ctrl = ctl;
- aoa_snd_ctl_add(ctl);
- ldev->have_headphone_detect =
- !ldev->gpio.methods
- ->set_notify(&ldev->gpio,
- AOA_NOTIFY_HEADPHONE,
- layout_notify,
- &ldev->selfptr_headphone);
- if (ldev->have_headphone_detect) {
- ctl = snd_ctl_new1(&headphone_detect_choice,
- ldev);
- aoa_snd_ctl_add(ctl);
- ctl = snd_ctl_new1(&headphone_detected,
- ldev);
- ldev->headphone_detected_ctrl = ctl;
- aoa_snd_ctl_add(ctl);
- }
- }
- if (cc->connected & CC_LINEOUT) {
- if (lineout == 1)
- ldev->gpio.methods->set_lineout(codec->gpio, 1);
- ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
- if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Switch", sizeof(ctl->id.name));
- ldev->lineout_ctrl = ctl;
- aoa_snd_ctl_add(ctl);
- ldev->have_lineout_detect =
- !ldev->gpio.methods
- ->set_notify(&ldev->gpio,
- AOA_NOTIFY_LINE_OUT,
- layout_notify,
- &ldev->selfptr_lineout);
- if (ldev->have_lineout_detect) {
- ctl = snd_ctl_new1(&lineout_detect_choice,
- ldev);
- if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Detect Autoswitch",
- sizeof(ctl->id.name));
- aoa_snd_ctl_add(ctl);
- ctl = snd_ctl_new1(&lineout_detected,
- ldev);
- if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Detected",
- sizeof(ctl->id.name));
- ldev->lineout_detected_ctrl = ctl;
- aoa_snd_ctl_add(ctl);
- }
- }
- cc++;
- }
- /* now update initial state */
- if (ldev->have_headphone_detect)
- layout_notify(&ldev->selfptr_headphone);
- if (ldev->have_lineout_detect)
- layout_notify(&ldev->selfptr_lineout);
-}
-
-static struct aoa_fabric layout_fabric = {
- .name = "SoundByLayout",
- .owner = THIS_MODULE,
- .found_codec = layout_found_codec,
- .remove_codec = layout_remove_codec,
- .attached_codec = layout_attached_codec,
-};
-
-static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
-{
- struct device_node *sound = NULL;
- const unsigned int *layout_id;
- struct layout *layout;
- struct layout_dev *ldev = NULL;
- int err;
-
- /* hm, currently we can only have one ... */
- if (layout_device)
- return -ENODEV;
-
- /* by breaking out we keep a reference */
- while ((sound = of_get_next_child(sdev->ofdev.node, sound))) {
- if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
- break;
- }
- if (!sound) return -ENODEV;
-
- layout_id = of_get_property(sound, "layout-id", NULL);
- if (!layout_id)
- goto outnodev;
- printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n",
- *layout_id);
-
- layout = find_layout_by_id(*layout_id);
- if (!layout) {
- printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
- goto outnodev;
- }
-
- ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
- if (!ldev)
- goto outnodev;
-
- layout_device = ldev;
- ldev->sdev = sdev;
- ldev->sound = sound;
- ldev->layout = layout;
- ldev->gpio.node = sound->parent;
- switch (layout->layout_id) {
- case 41: /* that unknown machine no one seems to have */
- case 51: /* PowerBook5,4 */
- case 58: /* Mac Mini */
- ldev->gpio.methods = ftr_gpio_methods;
- printk(KERN_DEBUG
- "snd-aoa-fabric-layout: Using direct GPIOs\n");
- break;
- default:
- ldev->gpio.methods = pmf_gpio_methods;
- printk(KERN_DEBUG
- "snd-aoa-fabric-layout: Using PMF GPIOs\n");
- }
- ldev->selfptr_headphone.ptr = ldev;
- ldev->selfptr_lineout.ptr = ldev;
- sdev->ofdev.dev.driver_data = ldev;
- list_add(&ldev->list, &layouts_list);
- layouts_list_items++;
-
- /* assign these before registering ourselves, so
- * callbacks that are done during registration
- * already have the values */
- sdev->pcmid = ldev->layout->pcmid;
- if (ldev->layout->busname) {
- sdev->pcmname = ldev->layout->busname;
- } else {
- sdev->pcmname = "Master";
- }
-
- ldev->gpio.methods->init(&ldev->gpio);
-
- err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
- if (err && err != -EALREADY) {
- printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
- " another fabric is active!\n");
- goto outlistdel;
- }
-
- use_layout(layout);
- ldev->switch_on_headphone = 1;
- ldev->switch_on_lineout = 1;
- return 0;
- outlistdel:
- /* we won't be using these then... */
- ldev->gpio.methods->exit(&ldev->gpio);
- /* reset if we didn't use it */
- sdev->pcmname = NULL;
- sdev->pcmid = -1;
- list_del(&ldev->list);
- layouts_list_items--;
- outnodev:
- of_node_put(sound);
- layout_device = NULL;
- kfree(ldev);
- return -ENODEV;
-}
-
-static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
-{
- struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
- int i;
-
- for (i=0; i<MAX_CODECS_PER_BUS; i++) {
- if (ldev->codecs[i]) {
- aoa_fabric_unlink_codec(ldev->codecs[i]);
- }
- ldev->codecs[i] = NULL;
- }
- list_del(&ldev->list);
- layouts_list_items--;
- of_node_put(ldev->sound);
-
- ldev->gpio.methods->set_notify(&ldev->gpio,
- AOA_NOTIFY_HEADPHONE,
- NULL,
- NULL);
- ldev->gpio.methods->set_notify(&ldev->gpio,
- AOA_NOTIFY_LINE_OUT,
- NULL,
- NULL);
-
- ldev->gpio.methods->exit(&ldev->gpio);
- layout_device = NULL;
- kfree(ldev);
- sdev->pcmid = -1;
- sdev->pcmname = NULL;
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
-{
- struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
-
- if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
- ldev->gpio.methods->all_amps_off(&ldev->gpio);
-
- return 0;
-}
-
-static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
-{
- struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
-
- if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
- ldev->gpio.methods->all_amps_restore(&ldev->gpio);
-
- return 0;
-}
-#endif
-
-static struct soundbus_driver aoa_soundbus_driver = {
- .name = "snd_aoa_soundbus_drv",
- .owner = THIS_MODULE,
- .probe = aoa_fabric_layout_probe,
- .remove = aoa_fabric_layout_remove,
-#ifdef CONFIG_PM
- .suspend = aoa_fabric_layout_suspend,
- .resume = aoa_fabric_layout_resume,
-#endif
- .driver = {
- .owner = THIS_MODULE,
- }
-};
-
-static int __init aoa_fabric_layout_init(void)
-{
- int err;
-
- err = soundbus_register_driver(&aoa_soundbus_driver);
- if (err)
- return err;
- return 0;
-}
-
-static void __exit aoa_fabric_layout_exit(void)
-{
- soundbus_unregister_driver(&aoa_soundbus_driver);
- aoa_fabric_unregister(&layout_fabric);
-}
-
-module_init(aoa_fabric_layout_init);
-module_exit(aoa_fabric_layout_exit);
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile
index e57a5cf65655..1b949b2a4028 100644
--- a/sound/aoa/soundbus/i2sbus/Makefile
+++ b/sound/aoa/soundbus/i2sbus/Makefile
@@ -1,2 +1,2 @@
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
-snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o
+snd-aoa-i2sbus-objs := core.o pcm.o control.o
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/control.c
index 87beb4ad4d63..87beb4ad4d63 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-control.c
+++ b/sound/aoa/soundbus/i2sbus/control.c
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
new file mode 100644
index 000000000000..be468edf3ecb
--- /dev/null
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -0,0 +1,450 @@
+/*
+ * i2sbus driver
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * GPL v2, can be found in COPYING.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+
+#include <asm/macio.h>
+#include <asm/dbdma.h>
+
+#include "../soundbus.h"
+#include "i2sbus.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_DESCRIPTION("Apple Soundbus: I2S support");
+
+static int force;
+module_param(force, int, 0444);
+MODULE_PARM_DESC(force, "Force loading i2sbus even when"
+ " no layout-id property is present");
+
+static struct of_device_id i2sbus_match[] = {
+ { .name = "i2s" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, i2sbus_match);
+
+static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
+ struct dbdma_command_mem *r,
+ int numcmds)
+{
+ /* one more for rounding, one for branch back, one for stop command */
+ r->size = (numcmds + 3) * sizeof(struct dbdma_cmd);
+ /* We use the PCI APIs for now until the generic one gets fixed
+ * enough or until we get some macio-specific versions
+ */
+ r->space = dma_alloc_coherent(
+ &macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size,
+ &r->bus_addr,
+ GFP_KERNEL);
+
+ if (!r->space) return -ENOMEM;
+
+ memset(r->space, 0, r->size);
+ r->cmds = (void*)DBDMA_ALIGN(r->space);
+ r->bus_cmd_start = r->bus_addr +
+ (dma_addr_t)((char*)r->cmds - (char*)r->space);
+
+ return 0;
+}
+
+static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
+ struct dbdma_command_mem *r)
+{
+ if (!r->space) return;
+
+ dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size, r->space, r->bus_addr);
+}
+
+static void i2sbus_release_dev(struct device *dev)
+{
+ struct i2sbus_dev *i2sdev;
+ int i;
+
+ i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
+
+ if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
+ if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
+ if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
+ if (i2sdev->allocated_resource[i])
+ release_and_free_resource(i2sdev->allocated_resource[i]);
+ free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
+ free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
+ free_irq(i2sdev->interrupts[i], i2sdev);
+ i2sbus_control_remove_dev(i2sdev->control, i2sdev);
+ mutex_destroy(&i2sdev->lock);
+ kfree(i2sdev);
+}
+
+static irqreturn_t i2sbus_bus_intr(int irq, void *devid)
+{
+ struct i2sbus_dev *dev = devid;
+ u32 intreg;
+
+ spin_lock(&dev->low_lock);
+ intreg = in_le32(&dev->intfregs->intr_ctl);
+
+ /* acknowledge interrupt reasons */
+ out_le32(&dev->intfregs->intr_ctl, intreg);
+
+ spin_unlock(&dev->low_lock);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * XXX FIXME: We test the layout_id's here to get the proper way of
+ * mapping in various registers, thanks to bugs in Apple device-trees.
+ * We could instead key off the machine model and the name of the i2s
+ * node (i2s-a). This we'll do when we move it all to macio_asic.c
+ * and have that export items for each sub-node too.
+ */
+static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
+ int layout, struct resource *res)
+{
+ struct device_node *parent;
+ int pindex, rc = -ENXIO;
+ const u32 *reg;
+
+ /* Machines with layout 76 and 36 (K2 based) have a weird device
+ * tree what we need to special case.
+ * Normal machines just fetch the resource from the i2s-X node.
+ * Darwin further divides normal machines into old and new layouts
+ * with a subtely different code path but that doesn't seem necessary
+ * in practice, they just bloated it. In addition, even on our K2
+ * case the i2s-modem node, if we ever want to handle it, uses the
+ * normal layout
+ */
+ if (layout != 76 && layout != 36)
+ return of_address_to_resource(np, index, res);
+
+ parent = of_get_parent(np);
+ pindex = (index == aoa_resource_i2smmio) ? 0 : 1;
+ rc = of_address_to_resource(parent, pindex, res);
+ if (rc)
+ goto bail;
+ reg = of_get_property(np, "reg", NULL);
+ if (reg == NULL) {
+ rc = -ENXIO;
+ goto bail;
+ }
+ res->start += reg[index * 2];
+ res->end = res->start + reg[index * 2 + 1] - 1;
+ bail:
+ of_node_put(parent);
+ return rc;
+}
+
+/* FIXME: look at device node refcounting */
+static int i2sbus_add_dev(struct macio_dev *macio,
+ struct i2sbus_control *control,
+ struct device_node *np)
+{
+ struct i2sbus_dev *dev;
+ struct device_node *child = NULL, *sound = NULL;
+ struct resource *r;
+ int i, layout = 0, rlen, ok = force;
+ static const char *rnames[] = { "i2sbus: %s (control)",
+ "i2sbus: %s (tx)",
+ "i2sbus: %s (rx)" };
+ static irq_handler_t ints[] = {
+ i2sbus_bus_intr,
+ i2sbus_tx_intr,
+ i2sbus_rx_intr
+ };
+
+ if (strlen(np->name) != 5)
+ return 0;
+ if (strncmp(np->name, "i2s-", 4))
+ return 0;
+
+ dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
+ if (!dev)
+ return 0;
+
+ i = 0;
+ while ((child = of_get_next_child(np, child))) {
+ if (strcmp(child->name, "sound") == 0) {
+ i++;
+ sound = child;
+ }
+ }
+ if (i == 1) {
+ const u32 *layout_id =
+ of_get_property(sound, "layout-id", NULL);
+ if (layout_id) {
+ layout = *layout_id;
+ snprintf(dev->sound.modalias, 32,
+ "sound-layout-%d", layout);
+ ok = 1;
+ }
+ }
+ /* for the time being, until we can handle non-layout-id
+ * things in some fabric, refuse to attach if there is no
+ * layout-id property or we haven't been forced to attach.
+ * When there are two i2s busses and only one has a layout-id,
+ * then this depends on the order, but that isn't important
+ * either as the second one in that case is just a modem. */
+ if (!ok) {
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->low_lock);
+ dev->sound.ofdev.node = np;
+ dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask;
+ dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask;
+ dev->sound.ofdev.dev.parent = &macio->ofdev.dev;
+ dev->sound.ofdev.dev.release = i2sbus_release_dev;
+ dev->sound.attach_codec = i2sbus_attach_codec;
+ dev->sound.detach_codec = i2sbus_detach_codec;
+ dev->sound.pcmid = -1;
+ dev->macio = macio;
+ dev->control = control;
+ dev->bus_number = np->name[4] - 'a';
+ INIT_LIST_HEAD(&dev->sound.codec_list);
+
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+ dev->interrupts[i] = -1;
+ snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
+ rnames[i], np->name);
+ }
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+ int irq = irq_of_parse_and_map(np, i);
+ if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
+ goto err;
+ dev->interrupts[i] = irq;
+ }
+
+
+ /* Resource handling is problematic as some device-trees contain
+ * useless crap (ugh ugh ugh). We work around that here by calling
+ * specific functions for calculating the appropriate resources.
+ *
+ * This will all be moved to macio_asic.c at one point
+ */
+ for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
+ if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i]))
+ goto err;
+ /* If only we could use our resource dev->resources[i]...
+ * but request_resource doesn't know about parents and
+ * contained resources...
+ */
+ dev->allocated_resource[i] =
+ request_mem_region(dev->resources[i].start,
+ dev->resources[i].end -
+ dev->resources[i].start + 1,
+ dev->rnames[i]);
+ if (!dev->allocated_resource[i]) {
+ printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
+ goto err;
+ }
+ }
+
+ r = &dev->resources[aoa_resource_i2smmio];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct i2s_interface_regs))
+ goto err;
+ dev->intfregs = ioremap(r->start, rlen);
+
+ r = &dev->resources[aoa_resource_txdbdma];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct dbdma_regs))
+ goto err;
+ dev->out.dbdma = ioremap(r->start, rlen);
+
+ r = &dev->resources[aoa_resource_rxdbdma];
+ rlen = r->end - r->start + 1;
+ if (rlen < sizeof(struct dbdma_regs))
+ goto err;
+ dev->in.dbdma = ioremap(r->start, rlen);
+
+ if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
+ goto err;
+
+ if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring,
+ MAX_DBDMA_COMMANDS))
+ goto err;
+ if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring,
+ MAX_DBDMA_COMMANDS))
+ goto err;
+
+ if (i2sbus_control_add_dev(dev->control, dev)) {
+ printk(KERN_ERR "i2sbus: control layer didn't like bus\n");
+ goto err;
+ }
+
+ if (soundbus_add_one(&dev->sound)) {
+ printk(KERN_DEBUG "i2sbus: device registration error!\n");
+ goto err;
+ }
+
+ /* enable this cell */
+ i2sbus_control_cell(dev->control, dev, 1);
+ i2sbus_control_enable(dev->control, dev);
+ i2sbus_control_clock(dev->control, dev, 1);
+
+ return 1;
+ err:
+ for (i=0;i<3;i++)
+ if (dev->interrupts[i] != -1)
+ free_irq(dev->interrupts[i], dev);
+ free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
+ free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
+ if (dev->intfregs) iounmap(dev->intfregs);
+ if (dev->out.dbdma) iounmap(dev->out.dbdma);
+ if (dev->in.dbdma) iounmap(dev->in.dbdma);
+ for (i=0;i<3;i++)
+ if (dev->allocated_resource[i])
+ release_and_free_resource(dev->allocated_resource[i]);
+ mutex_destroy(&dev->lock);
+ kfree(dev);
+ return 0;
+}
+
+static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
+{
+ struct device_node *np = NULL;
+ int got = 0, err;
+ struct i2sbus_control *control = NULL;
+
+ err = i2sbus_control_init(dev, &control);
+ if (err)
+ return err;
+ if (!control) {
+ printk(KERN_ERR "i2sbus_control_init API breakage\n");
+ return -ENODEV;
+ }
+
+ while ((np = of_get_next_child(dev->ofdev.node, np))) {
+ if (of_device_is_compatible(np, "i2sbus") ||
+ of_device_is_compatible(np, "i2s-modem")) {
+ got += i2sbus_add_dev(dev, control, np);
+ }
+ }
+
+ if (!got) {
+ /* found none, clean up */
+ i2sbus_control_destroy(control);
+ return -ENODEV;
+ }
+
+ dev->ofdev.dev.driver_data = control;
+
+ return 0;
+}
+
+static int i2sbus_remove(struct macio_dev* dev)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct i2sbus_dev *i2sdev, *tmp;
+
+ list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
+ soundbus_remove_one(&i2sdev->sound);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct codec_info_item *cii;
+ struct i2sbus_dev* i2sdev;
+ int err, ret = 0;
+
+ list_for_each_entry(i2sdev, &control->list, item) {
+ /* Notify Alsa */
+ if (i2sdev->sound.pcm) {
+ /* Suspend PCM streams */
+ snd_pcm_suspend_all(i2sdev->sound.pcm);
+ }
+
+ /* Notify codecs */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ err = 0;
+ if (cii->codec->suspend)
+ err = cii->codec->suspend(cii, state);
+ if (err)
+ ret = err;
+ }
+
+ /* wait until streams are stopped */
+ i2sbus_wait_for_stop_both(i2sdev);
+ }
+
+ return ret;
+}
+
+static int i2sbus_resume(struct macio_dev* dev)
+{
+ struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+ struct codec_info_item *cii;
+ struct i2sbus_dev* i2sdev;
+ int err, ret = 0;
+
+ list_for_each_entry(i2sdev, &control->list, item) {
+ /* reset i2s bus format etc. */
+ i2sbus_pcm_prepare_both(i2sdev);
+
+ /* Notify codecs so they can re-initialize */
+ list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
+ err = 0;
+ if (cii->codec->resume)
+ err = cii->codec->resume(cii);
+ if (err)
+ ret = err;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_PM */
+
+static int i2sbus_shutdown(struct macio_dev* dev)
+{
+ return 0;
+}
+
+static struct macio_driver i2sbus_drv = {
+ .name = "soundbus-i2s",
+ .owner = THIS_MODULE,
+ .match_table = i2sbus_match,
+ .probe = i2sbus_probe,
+ .remove = i2sbus_remove,
+#ifdef CONFIG_PM
+ .suspend = i2sbus_suspend,
+ .resume = i2sbus_resume,
+#endif
+ .shutdown = i2sbus_shutdown,
+};
+
+static int __init soundbus_i2sbus_init(void)
+{
+ return macio_register_driver(&i2sbus_drv);
+}
+
+static void __exit soundbus_i2sbus_exit(void)
+{
+ macio_unregister_driver(&i2sbus_drv);
+}
+
+module_init(soundbus_i2sbus_init);
+module_exit(soundbus_i2sbus_exit);
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c
deleted file mode 100644
index b4590df07466..000000000000
--- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * i2sbus driver
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-
-#include <sound/core.h>
-
-#include <asm/macio.h>
-#include <asm/dbdma.h>
-
-#include "../soundbus.h"
-#include "i2sbus.h"
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
-MODULE_DESCRIPTION("Apple Soundbus: I2S support");
-
-static int force;
-module_param(force, int, 0444);
-MODULE_PARM_DESC(force, "Force loading i2sbus even when"
- " no layout-id property is present");
-
-static struct of_device_id i2sbus_match[] = {
- { .name = "i2s" },
- { }
-};
-
-MODULE_DEVICE_TABLE(of, i2sbus_match);
-
-static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
- struct dbdma_command_mem *r,
- int numcmds)
-{
- /* one more for rounding, one for branch back, one for stop command */
- r->size = (numcmds + 3) * sizeof(struct dbdma_cmd);
- /* We use the PCI APIs for now until the generic one gets fixed
- * enough or until we get some macio-specific versions
- */
- r->space = dma_alloc_coherent(
- &macio_get_pci_dev(i2sdev->macio)->dev,
- r->size,
- &r->bus_addr,
- GFP_KERNEL);
-
- if (!r->space) return -ENOMEM;
-
- memset(r->space, 0, r->size);
- r->cmds = (void*)DBDMA_ALIGN(r->space);
- r->bus_cmd_start = r->bus_addr +
- (dma_addr_t)((char*)r->cmds - (char*)r->space);
-
- return 0;
-}
-
-static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
- struct dbdma_command_mem *r)
-{
- if (!r->space) return;
-
- dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
- r->size, r->space, r->bus_addr);
-}
-
-static void i2sbus_release_dev(struct device *dev)
-{
- struct i2sbus_dev *i2sdev;
- int i;
-
- i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
-
- if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
- if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
- if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
- for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
- if (i2sdev->allocated_resource[i])
- release_and_free_resource(i2sdev->allocated_resource[i]);
- free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
- free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
- for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
- free_irq(i2sdev->interrupts[i], i2sdev);
- i2sbus_control_remove_dev(i2sdev->control, i2sdev);
- mutex_destroy(&i2sdev->lock);
- kfree(i2sdev);
-}
-
-static irqreturn_t i2sbus_bus_intr(int irq, void *devid)
-{
- struct i2sbus_dev *dev = devid;
- u32 intreg;
-
- spin_lock(&dev->low_lock);
- intreg = in_le32(&dev->intfregs->intr_ctl);
-
- /* acknowledge interrupt reasons */
- out_le32(&dev->intfregs->intr_ctl, intreg);
-
- spin_unlock(&dev->low_lock);
-
- return IRQ_HANDLED;
-}
-
-
-/*
- * XXX FIXME: We test the layout_id's here to get the proper way of
- * mapping in various registers, thanks to bugs in Apple device-trees.
- * We could instead key off the machine model and the name of the i2s
- * node (i2s-a). This we'll do when we move it all to macio_asic.c
- * and have that export items for each sub-node too.
- */
-static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
- int layout, struct resource *res)
-{
- struct device_node *parent;
- int pindex, rc = -ENXIO;
- const u32 *reg;
-
- /* Machines with layout 76 and 36 (K2 based) have a weird device
- * tree what we need to special case.
- * Normal machines just fetch the resource from the i2s-X node.
- * Darwin further divides normal machines into old and new layouts
- * with a subtely different code path but that doesn't seem necessary
- * in practice, they just bloated it. In addition, even on our K2
- * case the i2s-modem node, if we ever want to handle it, uses the
- * normal layout
- */
- if (layout != 76 && layout != 36)
- return of_address_to_resource(np, index, res);
-
- parent = of_get_parent(np);
- pindex = (index == aoa_resource_i2smmio) ? 0 : 1;
- rc = of_address_to_resource(parent, pindex, res);
- if (rc)
- goto bail;
- reg = of_get_property(np, "reg", NULL);
- if (reg == NULL) {
- rc = -ENXIO;
- goto bail;
- }
- res->start += reg[index * 2];
- res->end = res->start + reg[index * 2 + 1] - 1;
- bail:
- of_node_put(parent);
- return rc;
-}
-
-/* FIXME: look at device node refcounting */
-static int i2sbus_add_dev(struct macio_dev *macio,
- struct i2sbus_control *control,
- struct device_node *np)
-{
- struct i2sbus_dev *dev;
- struct device_node *child = NULL, *sound = NULL;
- struct resource *r;
- int i, layout = 0, rlen, ok = force;
- static const char *rnames[] = { "i2sbus: %s (control)",
- "i2sbus: %s (tx)",
- "i2sbus: %s (rx)" };
- static irq_handler_t ints[] = {
- i2sbus_bus_intr,
- i2sbus_tx_intr,
- i2sbus_rx_intr
- };
-
- if (strlen(np->name) != 5)
- return 0;
- if (strncmp(np->name, "i2s-", 4))
- return 0;
-
- dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
- if (!dev)
- return 0;
-
- i = 0;
- while ((child = of_get_next_child(np, child))) {
- if (strcmp(child->name, "sound") == 0) {
- i++;
- sound = child;
- }
- }
- if (i == 1) {
- const u32 *layout_id =
- of_get_property(sound, "layout-id", NULL);
- if (layout_id) {
- layout = *layout_id;
- snprintf(dev->sound.modalias, 32,
- "sound-layout-%d", layout);
- ok = 1;
- }
- }
- /* for the time being, until we can handle non-layout-id
- * things in some fabric, refuse to attach if there is no
- * layout-id property or we haven't been forced to attach.
- * When there are two i2s busses and only one has a layout-id,
- * then this depends on the order, but that isn't important
- * either as the second one in that case is just a modem. */
- if (!ok) {
- kfree(dev);
- return -ENODEV;
- }
-
- mutex_init(&dev->lock);
- spin_lock_init(&dev->low_lock);
- dev->sound.ofdev.node = np;
- dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask;
- dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask;
- dev->sound.ofdev.dev.parent = &macio->ofdev.dev;
- dev->sound.ofdev.dev.release = i2sbus_release_dev;
- dev->sound.attach_codec = i2sbus_attach_codec;
- dev->sound.detach_codec = i2sbus_detach_codec;
- dev->sound.pcmid = -1;
- dev->macio = macio;
- dev->control = control;
- dev->bus_number = np->name[4] - 'a';
- INIT_LIST_HEAD(&dev->sound.codec_list);
-
- for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
- dev->interrupts[i] = -1;
- snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
- rnames[i], np->name);
- }
- for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
- int irq = irq_of_parse_and_map(np, i);
- if (request_irq(irq, ints[i], 0, dev->rnames[i], dev))
- goto err;
- dev->interrupts[i] = irq;
- }
-
-
- /* Resource handling is problematic as some device-trees contain
- * useless crap (ugh ugh ugh). We work around that here by calling
- * specific functions for calculating the appropriate resources.
- *
- * This will all be moved to macio_asic.c at one point
- */
- for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
- if (i2sbus_get_and_fixup_rsrc(np,i,layout,&dev->resources[i]))
- goto err;
- /* If only we could use our resource dev->resources[i]...
- * but request_resource doesn't know about parents and
- * contained resources...
- */
- dev->allocated_resource[i] =
- request_mem_region(dev->resources[i].start,
- dev->resources[i].end -
- dev->resources[i].start + 1,
- dev->rnames[i]);
- if (!dev->allocated_resource[i]) {
- printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
- goto err;
- }
- }
-
- r = &dev->resources[aoa_resource_i2smmio];
- rlen = r->end - r->start + 1;
- if (rlen < sizeof(struct i2s_interface_regs))
- goto err;
- dev->intfregs = ioremap(r->start, rlen);
-
- r = &dev->resources[aoa_resource_txdbdma];
- rlen = r->end - r->start + 1;
- if (rlen < sizeof(struct dbdma_regs))
- goto err;
- dev->out.dbdma = ioremap(r->start, rlen);
-
- r = &dev->resources[aoa_resource_rxdbdma];
- rlen = r->end - r->start + 1;
- if (rlen < sizeof(struct dbdma_regs))
- goto err;
- dev->in.dbdma = ioremap(r->start, rlen);
-
- if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma)
- goto err;
-
- if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring,
- MAX_DBDMA_COMMANDS))
- goto err;
- if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring,
- MAX_DBDMA_COMMANDS))
- goto err;
-
- if (i2sbus_control_add_dev(dev->control, dev)) {
- printk(KERN_ERR "i2sbus: control layer didn't like bus\n");
- goto err;
- }
-
- if (soundbus_add_one(&dev->sound)) {
- printk(KERN_DEBUG "i2sbus: device registration error!\n");
- goto err;
- }
-
- /* enable this cell */
- i2sbus_control_cell(dev->control, dev, 1);
- i2sbus_control_enable(dev->control, dev);
- i2sbus_control_clock(dev->control, dev, 1);
-
- return 1;
- err:
- for (i=0;i<3;i++)
- if (dev->interrupts[i] != -1)
- free_irq(dev->interrupts[i], dev);
- free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
- free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
- if (dev->intfregs) iounmap(dev->intfregs);
- if (dev->out.dbdma) iounmap(dev->out.dbdma);
- if (dev->in.dbdma) iounmap(dev->in.dbdma);
- for (i=0;i<3;i++)
- if (dev->allocated_resource[i])
- release_and_free_resource(dev->allocated_resource[i]);
- mutex_destroy(&dev->lock);
- kfree(dev);
- return 0;
-}
-
-static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
-{
- struct device_node *np = NULL;
- int got = 0, err;
- struct i2sbus_control *control = NULL;
-
- err = i2sbus_control_init(dev, &control);
- if (err)
- return err;
- if (!control) {
- printk(KERN_ERR "i2sbus_control_init API breakage\n");
- return -ENODEV;
- }
-
- while ((np = of_get_next_child(dev->ofdev.node, np))) {
- if (of_device_is_compatible(np, "i2sbus") ||
- of_device_is_compatible(np, "i2s-modem")) {
- got += i2sbus_add_dev(dev, control, np);
- }
- }
-
- if (!got) {
- /* found none, clean up */
- i2sbus_control_destroy(control);
- return -ENODEV;
- }
-
- dev->ofdev.dev.driver_data = control;
-
- return 0;
-}
-
-static int i2sbus_remove(struct macio_dev* dev)
-{
- struct i2sbus_control *control = dev->ofdev.dev.driver_data;
- struct i2sbus_dev *i2sdev, *tmp;
-
- list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
- soundbus_remove_one(&i2sdev->sound);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
-{
- struct i2sbus_control *control = dev->ofdev.dev.driver_data;
- struct codec_info_item *cii;
- struct i2sbus_dev* i2sdev;
- int err, ret = 0;
-
- list_for_each_entry(i2sdev, &control->list, item) {
- /* Notify Alsa */
- if (i2sdev->sound.pcm) {
- /* Suspend PCM streams */
- snd_pcm_suspend_all(i2sdev->sound.pcm);
- }
-
- /* Notify codecs */
- list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
- err = 0;
- if (cii->codec->suspend)
- err = cii->codec->suspend(cii, state);
- if (err)
- ret = err;
- }
-
- /* wait until streams are stopped */
- i2sbus_wait_for_stop_both(i2sdev);
- }
-
- return ret;
-}
-
-static int i2sbus_resume(struct macio_dev* dev)
-{
- struct i2sbus_control *control = dev->ofdev.dev.driver_data;
- struct codec_info_item *cii;
- struct i2sbus_dev* i2sdev;
- int err, ret = 0;
-
- list_for_each_entry(i2sdev, &control->list, item) {
- /* reset i2s bus format etc. */
- i2sbus_pcm_prepare_both(i2sdev);
-
- /* Notify codecs so they can re-initialize */
- list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
- err = 0;
- if (cii->codec->resume)
- err = cii->codec->resume(cii);
- if (err)
- ret = err;
- }
- }
-
- return ret;
-}
-#endif /* CONFIG_PM */
-
-static int i2sbus_shutdown(struct macio_dev* dev)
-{
- return 0;
-}
-
-static struct macio_driver i2sbus_drv = {
- .name = "soundbus-i2s",
- .owner = THIS_MODULE,
- .match_table = i2sbus_match,
- .probe = i2sbus_probe,
- .remove = i2sbus_remove,
-#ifdef CONFIG_PM
- .suspend = i2sbus_suspend,
- .resume = i2sbus_resume,
-#endif
- .shutdown = i2sbus_shutdown,
-};
-
-static int __init soundbus_i2sbus_init(void)
-{
- return macio_register_driver(&i2sbus_drv);
-}
-
-static void __exit soundbus_i2sbus_exit(void)
-{
- macio_unregister_driver(&i2sbus_drv);
-}
-
-module_init(soundbus_i2sbus_init);
-module_exit(soundbus_i2sbus_exit);
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h
index ff29654782c9..befefd99e271 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus.h
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -18,7 +18,7 @@
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
-#include "i2sbus-interface.h"
+#include "interface.h"
#include "../soundbus.h"
struct i2sbus_control {
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h b/sound/aoa/soundbus/i2sbus/interface.h
index c6b5f5452d20..c6b5f5452d20 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h
+++ b/sound/aoa/soundbus/i2sbus/interface.h
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index 59bacd365733..59bacd365733 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 34c1d94f921e..ef6539eea579 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -22,7 +22,7 @@
#include <asm/irq.h>
#include <mach/hardware.h>
-#include <mach/pxa-regs.h>
+#include <mach/regs-ac97.h>
#include <mach/pxa2xx-gpio.h>
#include <mach/audio.h>
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index c2635beb4c88..85cf591d4e11 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -22,6 +22,7 @@
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-ac97.h>
#include <mach/audio.h>
#include "pxa2xx-pcm.h"
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
index 5c4a4d38a083..65f86b56ba42 100644
--- a/sound/arm/pxa2xx-pcm.h
+++ b/sound/arm/pxa2xx-pcm.h
@@ -9,7 +9,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <asm/dma.h>
+#include <mach/dma.h>
struct pxa2xx_runtime_data {
int dma_ch;
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 66348c92f88d..7bbdda041a99 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -95,6 +95,26 @@ config SND_SEQUENCER_OSS
this will be compiled as a module. The module will be called
snd-seq-oss.
+config SND_HRTIMER
+ tristate "HR-timer backend support"
+ depends on HIGH_RES_TIMERS
+ select SND_TIMER
+ help
+ Say Y here to enable HR-timer backend for ALSA timer. ALSA uses
+ the hrtimer as a precise timing source. The ALSA sequencer code
+ also can use this timing source.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hrtimer.
+
+config SND_SEQ_HRTIMER_DEFAULT
+ bool "Use HR-timer as default sequencer timer"
+ depends on SND_HRTIMER && SND_SEQUENCER
+ default y
+ help
+ Say Y here to use the HR-timer backend as the default sequencer
+ timer.
+
config SND_RTCTIMER
tristate "RTC Timer support"
depends on RTC
@@ -114,6 +134,7 @@ config SND_RTCTIMER
config SND_SEQ_RTCTIMER_DEFAULT
bool "Use RTC as default sequencer timer"
depends on SND_RTCTIMER && SND_SEQUENCER
+ depends on !SND_SEQ_HRTIMER_DEFAULT
default y
help
Say Y here to use the RTC timer as the default sequencer
diff --git a/sound/core/Makefile b/sound/core/Makefile
index d57125a5687d..4229052e7b91 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -17,12 +17,14 @@ snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
+snd-hrtimer-objs := hrtimer.o
snd-rtctimer-objs := rtctimer.o
snd-hwdep-objs := hwdep.o
obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
+obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
diff --git a/sound/core/device.c b/sound/core/device.c
index c58d8227254c..a67dfac08c03 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -98,7 +98,7 @@ int snd_device_free(struct snd_card *card, void *device_data)
kfree(dev);
return 0;
}
- snd_printd("device free %p (from %p), not found\n", device_data,
+ snd_printd("device free %p (from %pF), not found\n", device_data,
__builtin_return_address(0));
return -ENXIO;
}
@@ -135,7 +135,7 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
}
return 0;
}
- snd_printd("device disconnect %p (from %p), not found\n", device_data,
+ snd_printd("device disconnect %p (from %pF), not found\n", device_data,
__builtin_return_address(0));
return -ENXIO;
}
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
new file mode 100644
index 000000000000..34c7d48f5061
--- /dev/null
+++ b/sound/core/hrtimer.c
@@ -0,0 +1,154 @@
+/*
+ * ALSA timer back-end using hrtimer
+ * Copyright (C) 2008 Takashi Iwai
+ *
+ * This program is free software; you can redistribute 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hrtimer.h>
+#include <sound/core.h>
+#include <sound/timer.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA hrtimer backend");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_HRTIMER));
+
+#define NANO_SEC 1000000000UL /* 10^9 in sec */
+static unsigned int resolution;
+
+struct snd_hrtimer {
+ struct snd_timer *timer;
+ struct hrtimer hrt;
+};
+
+static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
+{
+ struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
+ struct snd_timer *t = stime->timer;
+ hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
+ snd_timer_interrupt(stime->timer, t->sticks);
+ return HRTIMER_RESTART;
+}
+
+static int snd_hrtimer_open(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime;
+
+ stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ if (!stime)
+ return -ENOMEM;
+ hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ stime->timer = t;
+ stime->hrt.function = snd_hrtimer_callback;
+ t->private_data = stime;
+ return 0;
+}
+
+static int snd_hrtimer_close(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ if (stime) {
+ hrtimer_cancel(&stime->hrt);
+ kfree(stime);
+ t->private_data = NULL;
+ }
+ return 0;
+}
+
+static int snd_hrtimer_start(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
+ HRTIMER_MODE_REL);
+ return 0;
+}
+
+static int snd_hrtimer_stop(struct snd_timer *t)
+{
+ struct snd_hrtimer *stime = t->private_data;
+
+ hrtimer_cancel(&stime->hrt);
+ return 0;
+}
+
+static struct snd_timer_hardware hrtimer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO,
+ .open = snd_hrtimer_open,
+ .close = snd_hrtimer_close,
+ .start = snd_hrtimer_start,
+ .stop = snd_hrtimer_stop,
+};
+
+/*
+ * entry functions
+ */
+
+static struct snd_timer *mytimer;
+
+static int __init snd_hrtimer_init(void)
+{
+ struct snd_timer *timer;
+ struct timespec tp;
+ int err;
+
+ hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+ if (tp.tv_sec > 0 || !tp.tv_nsec) {
+ snd_printk(KERN_ERR
+ "snd-hrtimer: Invalid resolution %u.%09u",
+ (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
+ return -EINVAL;
+ }
+ resolution = tp.tv_nsec;
+
+ /* Create a new timer and set up the fields */
+ err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
+ &timer);
+ if (err < 0)
+ return err;
+
+ timer->module = THIS_MODULE;
+ strcpy(timer->name, "HR timer");
+ timer->hw = hrtimer_hw;
+ timer->hw.resolution = resolution;
+ timer->hw.ticks = NANO_SEC / resolution;
+
+ err = snd_timer_global_register(timer);
+ if (err < 0) {
+ snd_timer_global_free(timer);
+ return err;
+ }
+ mytimer = timer; /* remember this */
+
+ return 0;
+}
+
+static void __exit snd_hrtimer_exit(void)
+{
+ if (mytimer) {
+ snd_timer_global_free(mytimer);
+ mytimer = NULL;
+ }
+}
+
+module_init(snd_hrtimer_init);
+module_exit(snd_hrtimer_exit);
diff --git a/sound/core/info.c b/sound/core/info.c
index 527b207462b0..70fa87189f36 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -653,6 +653,23 @@ int snd_info_card_register(struct snd_card *card)
}
/*
+ * called on card->id change
+ */
+void snd_info_card_id_change(struct snd_card *card)
+{
+ mutex_lock(&info_mutex);
+ if (card->proc_root_link) {
+ snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+ card->proc_root_link = NULL;
+ }
+ if (strcmp(card->id, card->proc_root->name))
+ card->proc_root_link = proc_symlink(card->id,
+ snd_proc_root,
+ card->proc_root->name);
+ mutex_unlock(&info_mutex);
+}
+
+/*
* de-register the card proc file
* called from init.c
*/
diff --git a/sound/core/init.c b/sound/core/init.c
index b47ff8b44be8..0d5520c415d3 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -533,6 +533,65 @@ static void choose_default_id(struct snd_card *card)
}
}
+#ifndef CONFIG_SYSFS_DEPRECATED
+static ssize_t
+card_id_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)");
+}
+
+static ssize_t
+card_id_store_attr(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ char buf1[sizeof(card->id)];
+ size_t copy = count > sizeof(card->id) - 1 ?
+ sizeof(card->id) - 1 : count;
+ size_t idx;
+ int c;
+
+ for (idx = 0; idx < copy; idx++) {
+ c = buf[idx];
+ if (!isalnum(c) && c != '_' && c != '-')
+ return -EINVAL;
+ }
+ memcpy(buf1, buf, copy);
+ buf1[copy] = '\0';
+ mutex_lock(&snd_card_mutex);
+ if (!snd_info_check_reserved_words(buf1)) {
+ __exist:
+ mutex_unlock(&snd_card_mutex);
+ return -EEXIST;
+ }
+ for (idx = 0; idx < snd_ecards_limit; idx++) {
+ if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1))
+ goto __exist;
+ }
+ strcpy(card->id, buf1);
+ snd_info_card_id_change(card);
+ mutex_unlock(&snd_card_mutex);
+
+ return count;
+}
+
+static struct device_attribute card_id_attrs =
+ __ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
+
+static ssize_t
+card_number_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1);
+}
+
+static struct device_attribute card_number_attrs =
+ __ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+#endif /* CONFIG_SYSFS_DEPRECATED */
+
/**
* snd_card_register - register the soundcard
* @card: soundcard structure
@@ -553,7 +612,7 @@ int snd_card_register(struct snd_card *card)
#ifndef CONFIG_SYSFS_DEPRECATED
if (!card->card_dev) {
card->card_dev = device_create(sound_class, card->dev,
- MKDEV(0, 0), NULL,
+ MKDEV(0, 0), card,
"card%i", card->number);
if (IS_ERR(card->card_dev))
card->card_dev = NULL;
@@ -576,6 +635,16 @@ int snd_card_register(struct snd_card *card)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
+#ifndef CONFIG_SYSFS_DEPRECATED
+ if (card->card_dev) {
+ err = device_create_file(card->card_dev, &card_id_attrs);
+ if (err < 0)
+ return err;
+ err = device_create_file(card->card_dev, &card_number_attrs);
+ if (err < 0)
+ return err;
+ }
+#endif
return 0;
}
diff --git a/sound/core/jack.c b/sound/core/jack.c
index bd2d9e6b55e9..dd4a12dc09aa 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
else
input_free_device(jack->input_dev);
+ kfree(jack->id);
kfree(jack);
return 0;
@@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
if (jack == NULL)
return -ENOMEM;
- jack->id = id;
+ jack->id = kstrdup(id, GFP_KERNEL);
jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) {
@@ -102,9 +103,15 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
if (type & SND_JACK_HEADPHONE)
input_set_capability(jack->input_dev, EV_SW,
SW_HEADPHONE_INSERT);
+ if (type & SND_JACK_LINEOUT)
+ input_set_capability(jack->input_dev, EV_SW,
+ SW_LINEOUT_INSERT);
if (type & SND_JACK_MICROPHONE)
input_set_capability(jack->input_dev, EV_SW,
SW_MICROPHONE_INSERT);
+ if (type & SND_JACK_MECHANICAL)
+ input_set_capability(jack->input_dev, EV_SW,
+ SW_JACK_PHYSICAL_INSERT);
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
@@ -153,9 +160,15 @@ void snd_jack_report(struct snd_jack *jack, int status)
if (jack->type & SND_JACK_HEADPHONE)
input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
status & SND_JACK_HEADPHONE);
+ if (jack->type & SND_JACK_LINEOUT)
+ input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
+ status & SND_JACK_LINEOUT);
if (jack->type & SND_JACK_MICROPHONE)
input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
status & SND_JACK_MICROPHONE);
+ if (jack->type & SND_JACK_MECHANICAL)
+ input_report_switch(jack->input_dev, SW_JACK_PHYSICAL_INSERT,
+ status & SND_JACK_MECHANICAL);
input_sync(jack->input_dev);
}
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 39672f68ce5d..002777ba336a 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -151,7 +151,7 @@ static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *subs
if (!substream->opened)
return;
if (up) {
- tasklet_hi_schedule(&substream->runtime->tasklet);
+ tasklet_schedule(&substream->runtime->tasklet);
} else {
tasklet_kill(&substream->runtime->tasklet);
substream->ops->trigger(substream, 0);
@@ -908,7 +908,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
}
if (result > 0) {
if (runtime->event)
- tasklet_hi_schedule(&runtime->tasklet);
+ tasklet_schedule(&runtime->tasklet);
else if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
index 51e64e30dd3b..0851cd13e303 100644
--- a/sound/core/rtctimer.c
+++ b/sound/core/rtctimer.c
@@ -118,7 +118,7 @@ static void rtctimer_tasklet(unsigned long data)
*/
static void rtctimer_interrupt(void *private_data)
{
- tasklet_hi_schedule(private_data);
+ tasklet_schedule(private_data);
}
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index ee0f8405ab35..bf09a5ad1865 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -43,7 +43,9 @@ int seq_default_timer_class = SNDRV_TIMER_CLASS_GLOBAL;
int seq_default_timer_sclass = SNDRV_TIMER_SCLASS_NONE;
int seq_default_timer_card = -1;
int seq_default_timer_device =
-#ifdef CONFIG_SND_SEQ_RTCTIMER_DEFAULT
+#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
+ SNDRV_TIMER_GLOBAL_HRTIMER
+#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM
diff --git a/sound/core/timer.c b/sound/core/timer.c
index c584408c9f17..796532081e81 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -743,7 +743,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
spin_unlock_irqrestore(&timer->lock, flags);
if (use_tasklet)
- tasklet_hi_schedule(&timer->task_queue);
+ tasklet_schedule(&timer->task_queue);
}
/*
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 255fd18b9aec..0bcf14640fde 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -163,7 +163,7 @@ config SND_ML403_AC97CR
config SND_AC97_POWER_SAVE
bool "AC97 Power-Saving Mode"
- depends on SND_AC97_CODEC && EXPERIMENTAL
+ depends on SND_AC97_CODEC
default n
help
Say Y here to enable the aggressive power-saving support of
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index 1899cf0685bc..a4049eb94d35 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -96,7 +96,6 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
return -EINVAL;
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ;
pcsp_chip.timer.function = pcsp_do_timer;
card = snd_card_new(index, id, THIS_MODULE, 0);
@@ -188,10 +187,8 @@ static int __devexit pcsp_remove(struct platform_device *dev)
static void pcsp_stop_beep(struct snd_pcsp *chip)
{
- spin_lock_irq(&chip->substream_lock);
- if (!chip->playback_substream)
- pcspkr_stop_sound();
- spin_unlock_irq(&chip->substream_lock);
+ pcsp_sync_stop(chip);
+ pcspkr_stop_sound();
}
#ifdef CONFIG_PM
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 1d661f795e8c..cdef2664218f 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -62,6 +62,8 @@ struct snd_pcsp {
unsigned short port, irq, dma;
spinlock_t substream_lock;
struct snd_pcm_substream *playback_substream;
+ unsigned int fmt_size;
+ unsigned int is_signed;
size_t playback_ptr;
size_t period_ptr;
atomic_t timer_active;
@@ -77,6 +79,7 @@ struct snd_pcsp {
extern struct snd_pcsp pcsp_chip;
extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
+extern void pcsp_sync_stop(struct snd_pcsp *chip);
extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index 1f42e4063118..84cc2658c05b 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
#include <sound/pcm.h>
#include <asm/io.h>
#include "pcsp.h"
@@ -19,61 +20,57 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
#define DMIX_WANTS_S16 1
-enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
+/*
+ * Call snd_pcm_period_elapsed in a tasklet
+ * This avoids spinlock messes and long-running irq contexts
+ */
+static void pcsp_call_pcm_elapsed(unsigned long priv)
+{
+ if (atomic_read(&pcsp_chip.timer_active)) {
+ struct snd_pcm_substream *substream;
+ substream = pcsp_chip.playback_substream;
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
+static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
+
+/* write the port and returns the next expire time in ns;
+ * called at the trigger-start and in hrtimer callback
+ */
+static unsigned long pcsp_timer_update(struct hrtimer *handle)
{
unsigned char timer_cnt, val;
- int fmt_size, periods_elapsed;
u64 ns;
- size_t period_bytes, buffer_bytes;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
+ unsigned long flags;
if (chip->thalf) {
outb(chip->val61, 0x61);
chip->thalf = 0;
if (!atomic_read(&chip->timer_active))
- return HRTIMER_NORESTART;
- hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
- ktime_set(0, chip->ns_rem));
- return HRTIMER_RESTART;
+ return 0;
+ return chip->ns_rem;
}
- spin_lock_irq(&chip->substream_lock);
- /* Takashi Iwai says regarding this extra lock:
-
- If the irq handler handles some data on the DMA buffer, it should
- do snd_pcm_stream_lock().
- That protects basically against all races among PCM callbacks, yes.
- However, there are two remaining issues:
- 1. The substream pointer you try to lock isn't protected _before_
- this lock yet.
- 2. snd_pcm_period_elapsed() itself acquires the lock.
- The requirement of another lock is because of 1. When you get
- chip->playback_substream, it's not protected.
- Keeping this lock while snd_pcm_period_elapsed() assures the substream
- is still protected (at least, not released). And the other status is
- handled properly inside snd_pcm_stream_lock() in
- snd_pcm_period_elapsed().
-
- */
- if (!chip->playback_substream)
- goto exit_nr_unlock1;
- substream = chip->playback_substream;
- snd_pcm_stream_lock(substream);
if (!atomic_read(&chip->timer_active))
- goto exit_nr_unlock2;
+ return 0;
+ substream = chip->playback_substream;
+ if (!substream)
+ return 0;
runtime = substream->runtime;
- fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
/* assume it is mono! */
- val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
- if (snd_pcm_format_signed(runtime->format))
+ val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1];
+ if (chip->is_signed)
val ^= 0x80;
timer_cnt = val * CUR_DIV() / 256;
if (timer_cnt && chip->enable) {
- spin_lock(&i8253_lock);
+ spin_lock_irqsave(&i8253_lock, flags);
if (!nforce_wa) {
outb_p(chip->val61, 0x61);
outb_p(timer_cnt, 0x42);
@@ -82,12 +79,39 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
outb(chip->val61 ^ 2, 0x61);
chip->thalf = 1;
}
- spin_unlock(&i8253_lock);
+ spin_unlock_irqrestore(&i8253_lock, flags);
}
+ chip->ns_rem = PCSP_PERIOD_NS();
+ ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
+ chip->ns_rem -= ns;
+ return ns;
+}
+
+enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
+{
+ struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
+ struct snd_pcm_substream *substream;
+ int periods_elapsed, pointer_update;
+ size_t period_bytes, buffer_bytes;
+ unsigned long ns;
+ unsigned long flags;
+
+ pointer_update = !chip->thalf;
+ ns = pcsp_timer_update(handle);
+ if (!ns)
+ return HRTIMER_NORESTART;
+
+ /* update the playback position */
+ substream = chip->playback_substream;
+ if (!substream)
+ return HRTIMER_NORESTART;
+
period_bytes = snd_pcm_lib_period_bytes(substream);
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
+
+ spin_lock_irqsave(&chip->substream_lock, flags);
+ chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size;
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
#if PCSP_DEBUG
@@ -102,41 +126,30 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
* or ALSA will BUG on us. */
chip->playback_ptr %= buffer_bytes;
- snd_pcm_stream_unlock(substream);
-
if (periods_elapsed) {
- snd_pcm_period_elapsed(substream);
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
}
+ spin_unlock_irqrestore(&chip->substream_lock, flags);
- spin_unlock_irq(&chip->substream_lock);
+ if (periods_elapsed)
+ tasklet_schedule(&pcsp_pcm_tasklet);
- if (!atomic_read(&chip->timer_active))
- return HRTIMER_NORESTART;
+ hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
- chip->ns_rem = PCSP_PERIOD_NS();
- ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
- chip->ns_rem -= ns;
- hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
- ktime_set(0, ns));
return HRTIMER_RESTART;
-
-exit_nr_unlock2:
- snd_pcm_stream_unlock(substream);
-exit_nr_unlock1:
- spin_unlock_irq(&chip->substream_lock);
- return HRTIMER_NORESTART;
}
-static void pcsp_start_playing(struct snd_pcsp *chip)
+static int pcsp_start_playing(struct snd_pcsp *chip)
{
+ unsigned long ns;
+
#if PCSP_DEBUG
printk(KERN_INFO "PCSP: start_playing called\n");
#endif
if (atomic_read(&chip->timer_active)) {
printk(KERN_ERR "PCSP: Timer already active\n");
- return;
+ return -EIO;
}
spin_lock(&i8253_lock);
@@ -146,7 +159,12 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
atomic_set(&chip->timer_active, 1);
chip->thalf = 0;
- hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ ns = pcsp_timer_update(&pcsp_chip.timer);
+ if (!ns)
+ return -EIO;
+
+ hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL);
+ return 0;
}
static void pcsp_stop_playing(struct snd_pcsp *chip)
@@ -165,26 +183,35 @@ static void pcsp_stop_playing(struct snd_pcsp *chip)
spin_unlock(&i8253_lock);
}
+/*
+ * Force to stop and sync the stream
+ */
+void pcsp_sync_stop(struct snd_pcsp *chip)
+{
+ local_irq_disable();
+ pcsp_stop_playing(chip);
+ local_irq_enable();
+ hrtimer_cancel(&chip->timer);
+ tasklet_kill(&pcsp_pcm_tasklet);
+}
+
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
printk(KERN_INFO "PCSP: close called\n");
#endif
- if (atomic_read(&chip->timer_active)) {
- printk(KERN_ERR "PCSP: timer still active\n");
- pcsp_stop_playing(chip);
- }
- spin_lock_irq(&chip->substream_lock);
+ pcsp_sync_stop(chip);
chip->playback_substream = NULL;
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
int err;
+ pcsp_sync_stop(chip);
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
@@ -194,9 +221,11 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
{
+ struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
printk(KERN_INFO "PCSP: hw_free called\n");
#endif
+ pcsp_sync_stop(chip);
return snd_pcm_lib_free_pages(substream);
}
@@ -212,8 +241,12 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
snd_pcm_lib_period_bytes(substream),
substream->runtime->periods);
#endif
+ pcsp_sync_stop(chip);
chip->playback_ptr = 0;
chip->period_ptr = 0;
+ chip->fmt_size =
+ snd_pcm_format_physical_width(substream->runtime->format) >> 3;
+ chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
return 0;
}
@@ -226,8 +259,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- pcsp_start_playing(chip);
- break;
+ return pcsp_start_playing(chip);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pcsp_stop_playing(chip);
@@ -242,7 +274,11 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
*substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
- return bytes_to_frames(substream->runtime, chip->playback_ptr);
+ unsigned int pos;
+ spin_lock(&chip->substream_lock);
+ pos = chip->playback_ptr;
+ spin_unlock(&chip->substream_lock);
+ return bytes_to_frames(substream->runtime, pos);
}
static struct snd_pcm_hardware snd_pcsp_playback = {
@@ -279,9 +315,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
return -EBUSY;
}
runtime->hw = snd_pcsp_playback;
- spin_lock_irq(&chip->substream_lock);
chip->playback_substream = substream;
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 473b07f6ae85..14e3354be43a 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -548,7 +548,7 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
(chip->chip_status & VX_STAT_IS_STALE))
return IRQ_NONE;
if (! vx_test_and_ack(chip))
- tasklet_hi_schedule(&chip->tq);
+ tasklet_schedule(&chip->tq);
return IRQ_HANDLED;
}
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index 27de574c08f7..6644d0034fba 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -823,7 +823,7 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
* we trigger the pipe using tasklet, so that the interrupts are
* issued surely after the trigger is completed.
*/
- tasklet_hi_schedule(&pipe->start_tq);
+ tasklet_schedule(&pipe->start_tq);
chip->pcm_running++;
pipe->running = 1;
break;
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index c13a178383ba..549b4eba1496 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
#include <asm/io.h>
#include <linux/delay.h>
@@ -89,7 +89,7 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
{
struct snd_tea575x *tea = video_drvdata(file);
void __user *arg = (void __user *)data;
-
+
switch(cmd) {
case VIDIOCGCAP:
{
@@ -110,9 +110,9 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
case VIDIOCGTUNER:
{
struct video_tuner v;
- if (copy_from_user(&v, arg,sizeof(v))!=0)
+ if (copy_from_user(&v, arg,sizeof(v))!=0)
return -EFAULT;
- if (v.tuner) /* Only 1 tuner */
+ if (v.tuner) /* Only 1 tuner */
return -EINVAL;
v.rangelow = (87*16000);
v.rangehigh = (108*16000);
@@ -144,24 +144,24 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
snd_tea575x_set_freq(tea);
return 0;
case VIDIOCGAUDIO:
- {
+ {
struct video_audio v;
memset(&v, 0, sizeof(v));
strcpy(v.name, "Radio");
if(copy_to_user(arg,&v, sizeof(v)))
return -EFAULT;
- return 0;
+ return 0;
}
case VIDIOCSAUDIO:
{
struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
if (tea->ops->mute)
tea->ops->mute(tea,
(v.flags &
VIDEO_AUDIO_MUTE) ? 1 : 0);
- if(v.audio)
+ if(v.audio)
return -EINVAL;
return 0;
}
@@ -240,11 +240,11 @@ static int __init alsa_tea575x_module_init(void)
{
return 0;
}
-
+
static void __exit alsa_tea575x_module_exit(void)
{
}
-
+
module_init(alsa_tea575x_module_init)
module_exit(alsa_tea575x_module_exit)
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 667eccc676a4..ea06877be4b1 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -140,8 +140,10 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
break;
}
}
- if (i >= ARRAY_SIZE(possible_ports))
+ if (i >= ARRAY_SIZE(possible_ports)) {
+ err = -EINVAL;
goto _err;
+ }
}
acard->chip = chip;
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index c47842fad657..2c63bb9da74a 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -1483,16 +1483,14 @@ static void __exit unload_waveartist(struct address_info *hw)
#define VNC_HANDSET_DETECT 0x40
#define VNC_DISABLE_AUTOSWITCH 0x80
-extern spinlock_t gpio_lock;
-
static inline void
vnc_mute_spkr(wavnc_info *devc)
{
unsigned long flags;
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
static void
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 7003711f4fcc..6e3a1848447c 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -208,7 +208,8 @@ config SND_OXYGEN
* AuzenTech X-Meridian
* Bgears b-Enspirer
* Club3D Theatron DTS
- * HT-Omega Claro
+ * HT-Omega Claro (plus)
+ * HT-Omega Claro halo (XT)
* Razer Barracuda AC-1
* Sondigo Inferno
@@ -497,129 +498,7 @@ config SND_FM801_TEA575X
depends on SND_FM801_TEA575X_BOOL
default SND_FM801
-config SND_HDA_INTEL
- tristate "Intel HD Audio"
- select SND_PCM
- select SND_VMASTER
- help
- Say Y here to include support for Intel "High Definition
- Audio" (Azalia) motherboard devices.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-hda-intel.
-
-config SND_HDA_HWDEP
- bool "Build hwdep interface for HD-audio driver"
- depends on SND_HDA_INTEL
- select SND_HWDEP
- help
- Say Y here to build a hwdep interface for HD-audio driver.
- This interface can be used for out-of-band communication
- with codecs for debugging purposes.
-
-config SND_HDA_INPUT_BEEP
- bool "Support digital beep via input layer"
- depends on SND_HDA_INTEL
- depends on INPUT=y || INPUT=SND_HDA_INTEL
- help
- Say Y here to build a digital beep interface for HD-audio
- driver. This interface is used to generate digital beeps.
-
-config SND_HDA_CODEC_REALTEK
- bool "Build Realtek HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Realtek HD-audio codec support in
- snd-hda-intel driver, such as ALC880.
-
-config SND_HDA_CODEC_ANALOG
- bool "Build Analog Device HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Analog Device HD-audio codec support in
- snd-hda-intel driver, such as AD1986A.
-
-config SND_HDA_CODEC_SIGMATEL
- bool "Build IDT/Sigmatel HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include IDT (Sigmatel) HD-audio codec support in
- snd-hda-intel driver, such as STAC9200.
-
-config SND_HDA_CODEC_VIA
- bool "Build VIA HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include VIA HD-audio codec support in
- snd-hda-intel driver, such as VT1708.
-
-config SND_HDA_CODEC_ATIHDMI
- bool "Build ATI HDMI HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include ATI HDMI HD-audio codec support in
- snd-hda-intel driver, such as ATI RS600 HDMI.
-
-config SND_HDA_CODEC_NVHDMI
- bool "Build NVIDIA HDMI HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include NVIDIA HDMI HD-audio codec support in
- snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
-config SND_HDA_CODEC_CONEXANT
- bool "Build Conexant HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Conexant HD-audio codec support in
- snd-hda-intel driver, such as CX20549.
-
-config SND_HDA_CODEC_CMEDIA
- bool "Build C-Media HD-audio codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include C-Media HD-audio codec support in
- snd-hda-intel driver, such as CMI9880.
-
-config SND_HDA_CODEC_SI3054
- bool "Build Silicon Labs 3054 HD-modem codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Silicon Labs 3054 HD-modem codec
- (and compatibles) support in snd-hda-intel driver.
-
-config SND_HDA_GENERIC
- bool "Enable generic HD-audio codec parser"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to enable the generic HD-audio codec parser
- in snd-hda-intel driver.
-
-config SND_HDA_POWER_SAVE
- bool "Aggressive power-saving on HD-audio"
- depends on SND_HDA_INTEL && EXPERIMENTAL
- help
- Say Y here to enable more aggressive power-saving mode on
- HD-audio driver. The power-saving timeout can be configured
- via power_save option or over sysfs on-the-fly.
-
-config SND_HDA_POWER_SAVE_DEFAULT
- int "Default time-out for HD-audio power-save mode"
- depends on SND_HDA_POWER_SAVE
- default 0
- help
- The default time-out value in seconds for HD-audio automatic
- power-save mode. 0 means to disable the power-save mode.
+source "sound/pci/hda/Kconfig"
config SND_HDSP
tristate "RME Hammerfall DSP Audio"
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index bd510eceff1f..e2b843b4f9d0 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -175,7 +175,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x574d4C04, 0xffffffff, "WM9704M,WM9704Q", patch_wolfson04, NULL},
{ 0x574d4C05, 0xffffffff, "WM9705,WM9710", patch_wolfson05, NULL},
{ 0x574d4C09, 0xffffffff, "WM9709", NULL, NULL},
-{ 0x574d4C12, 0xffffffff, "WM9711,WM9712", patch_wolfson11, NULL},
+{ 0x574d4C12, 0xffffffff, "WM9711,WM9712,WM9715", patch_wolfson11, NULL},
{ 0x574d4c13, 0xffffffff, "WM9713,WM9714", patch_wolfson13, NULL, AC97_DEFAULT_POWER_OFF},
{ 0x594d4800, 0xffffffff, "YMF743", patch_yamaha_ymf743, NULL },
{ 0x594d4802, 0xffffffff, "YMF752", NULL, NULL },
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 6e831aff1bd0..81bc93e5f1e3 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -2054,8 +2054,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
.get = snd_ac97_ad1888_lohpsel_get,
.put = snd_ac97_ad1888_lohpsel_put
},
- AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
- AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+ AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
+ AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
+ AC97_AD_HPFD_SHIFT, 1, 1),
AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2832,6 +2833,8 @@ static int patch_alc655(struct snd_ac97 * ac97)
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
else
val |= (1 << 1); /* Pin 47 is spdif input pin */
+ /* this seems missing on some hardwares */
+ ac97->ext_id |= AC97_EI_SPDIF;
}
val &= ~(1 << 12); /* vref enable */
snd_ac97_write_cache(ac97, 0x7a, val);
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index 74175fc80c7f..14b8d9a91aae 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -664,10 +664,14 @@ struct snd_ca0106_pcm {
struct snd_ca0106_details {
u32 serial;
char * name;
- int ac97;
- int gpio_type;
- int i2c_adc;
- int spi_dac;
+ int ac97; /* ac97 = 0 -> Select MIC, Line in, TAD in, AUX in.
+ ac97 = 1 -> Default to AC97 in. */
+ int gpio_type; /* gpio_type = 1 -> shared mic-in/line-in
+ gpio_type = 2 -> shared side-out/line-in. */
+ int i2c_adc; /* with i2c_adc=1, the driver adds some capture volume
+ controls, phone, mic, line-in and aux. */
+ int spi_dac; /* spi_dac=1 adds the mute switch for each analog
+ output, front, rear, etc. */
};
// definition of the chip-specific record
@@ -686,11 +690,12 @@ struct snd_ca0106 {
spinlock_t emu_lock;
struct snd_ac97 *ac97;
- struct snd_pcm *pcm;
+ struct snd_pcm *pcm[4];
struct snd_ca0106_channel playback_channels[4];
struct snd_ca0106_channel capture_channels[4];
- u32 spdif_bits[4]; /* s/pdif out setup */
+ u32 spdif_bits[4]; /* s/pdif out default setup */
+ u32 spdif_str_bits[4]; /* s/pdif out per-stream setup */
int spdif_enable;
int capture_source;
int i2c_capture_source;
@@ -703,6 +708,11 @@ struct snd_ca0106 {
struct snd_ca_midi midi2;
u16 spi_dac_reg[16];
+
+#ifdef CONFIG_PM
+#define NUM_SAVED_VOLUMES 9
+ unsigned int saved_vol[NUM_SAVED_VOLUMES];
+#endif
};
int snd_ca0106_mixer(struct snd_ca0106 *emu);
@@ -721,3 +731,11 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
unsigned int data);
+
+#ifdef CONFIG_PM
+void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
+void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
+#else
+#define snd_ca0106_mixer_suspend(chip) do { } while (0)
+#define snd_ca0106_mixer_resume(chip) do { } while (0)
+#endif
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 88fbf285d2b7..0e62205d4081 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -254,7 +254,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
.name = "MSI K8N Diamond MB",
.gpio_type = 2,
.i2c_adc = 1,
- .spi_dac = 2 } ,
+ .spi_dac = 1 } ,
/* Shuttle XPC SD31P which has an onboard Creative Labs
* Sound Blaster Live! 24-bit EAX
* high-definition 7.1 audio processor".
@@ -305,9 +305,15 @@ static struct snd_pcm_hardware snd_ca0106_capture_hw = {
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+#if 0 /* FIXME: looks like 44.1kHz capture causes noisy output on 48kHz */
.rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
.rate_min = 44100,
+#else
+ .rates = (SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000),
+ .rate_min = 48000,
+#endif /* FIXME */
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
@@ -479,6 +485,15 @@ static const int spi_dacd_bit[] = {
[PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT,
};
+static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
+{
+ if (chip->spdif_str_bits[idx] != chip->spdif_bits[idx]) {
+ chip->spdif_str_bits[idx] = chip->spdif_bits[idx];
+ snd_ca0106_ptr_write(chip, SPCS0 + idx, 0,
+ chip->spdif_str_bits[idx]);
+ }
+}
+
/* open_playback callback */
static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
int channel_id)
@@ -524,6 +539,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
if (err < 0)
return err;
}
+
+ restore_spdif_bits(chip, channel_id);
+
return 0;
}
@@ -535,6 +553,8 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
struct snd_ca0106_pcm *epcm = runtime->private_data;
chip->playback_channels[epcm->channel_id].use = 0;
+ restore_spdif_bits(chip, epcm->channel_id);
+
if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
const int reg = spi_dacd_reg[epcm->channel_id];
@@ -847,15 +867,18 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
struct snd_pcm_substream *s;
u32 basic = 0;
u32 extended = 0;
- int running=0;
+ u32 bits;
+ int running = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- running=1;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
default:
- running=0;
+ running = 0;
break;
}
snd_pcm_group_for_each_entry(s, substream) {
@@ -865,22 +888,32 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
runtime = s->runtime;
epcm = runtime->private_data;
channel = epcm->channel_id;
- //snd_printk("channel=%d\n",channel);
+ /* snd_printk("channel=%d\n",channel); */
epcm->running = running;
- basic |= (0x1<<channel);
- extended |= (0x10<<channel);
+ basic |= (0x1 << channel);
+ extended |= (0x10 << channel);
snd_pcm_trigger_done(s, substream);
}
- //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+ /* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
- snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+ case SNDRV_PCM_TRIGGER_RESUME:
+ bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
+ bits |= extended;
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
+ bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
+ bits |= basic;
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
- snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
+ bits &= ~basic;
+ snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
+ bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
+ bits &= ~extended;
+ snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
break;
default:
result = -EINVAL;
@@ -1103,21 +1136,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
}
+static void ca0106_stop_chip(struct snd_ca0106 *chip);
+
static int snd_ca0106_free(struct snd_ca0106 *chip)
{
- if (chip->res_port != NULL) { /* avoid access to already used hardware */
- // disable interrupts
- snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
- outl(0, chip->port + INTE);
- snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
- udelay(1000);
- // disable audio
- //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
- outl(0, chip->port + HCFG);
- /* FIXME: We need to stop and DMA transfers here.
- * But as I am not sure how yet, we cannot from the dma pages.
- * So we can fix: snd-malloc: Memory leak? pages not freed = 8
- */
+ if (chip->res_port != NULL) {
+ /* avoid access to already used hardware */
+ ca0106_stop_chip(chip);
}
if (chip->irq >= 0)
free_irq(chip->irq, chip);
@@ -1203,15 +1228,14 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
+static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1238,7 +1262,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
strcpy(pcm->name, "CA0106");
- emu->pcm = pcm;
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream;
@@ -1260,8 +1283,7 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
return err;
}
- if (rpcm)
- *rpcm = pcm;
+ emu->pcm[device] = pcm;
return 0;
}
@@ -1301,89 +1323,10 @@ static unsigned int i2c_adc_init[][2] = {
{ 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */
};
-static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ca0106 **rchip)
+static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
{
- struct snd_ca0106 *chip;
- struct snd_ca0106_details *c;
- int err;
int ch;
- static struct snd_device_ops ops = {
- .dev_free = snd_ca0106_dev_free,
- };
-
- *rchip = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
- return err;
- if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
- printk(KERN_ERR "error to set 32bit mask DMA\n");
- pci_disable_device(pci);
- return -ENXIO;
- }
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
-
- spin_lock_init(&chip->emu_lock);
-
- chip->port = pci_resource_start(pci, 0);
- if ((chip->res_port = request_region(chip->port, 0x20,
- "snd_ca0106")) == NULL) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot allocate the port\n");
- return -EBUSY;
- }
-
- if (request_irq(pci->irq, snd_ca0106_interrupt,
- IRQF_SHARED, "snd_ca0106", chip)) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot grab irq\n");
- return -EBUSY;
- }
- chip->irq = pci->irq;
-
- /* This stores the periods table. */
- if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
- snd_ca0106_free(chip);
- return -ENOMEM;
- }
-
- pci_set_master(pci);
- /* read serial */
- pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
- pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
-#if 1
- printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model,
- pci->revision, chip->serial);
-#endif
- strcpy(card->driver, "CA0106");
- strcpy(card->shortname, "CA0106");
-
- for (c = ca0106_chip_details; c->serial; c++) {
- if (subsystem[dev]) {
- if (c->serial == subsystem[dev])
- break;
- } else if (c->serial == chip->serial)
- break;
- }
- chip->details = c;
- if (subsystem[dev]) {
- printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n",
- c->name, chip->serial, subsystem[dev]);
- }
-
- sprintf(card->longname, "%s at 0x%lx irq %i",
- c->name, chip->port, chip->irq);
+ unsigned int def_bits;
outl(0, chip->port + INTE);
@@ -1401,31 +1344,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
* AN = 0 (Audio data)
* P = 0 (Consumer)
*/
- snd_ca0106_ptr_write(chip, SPCS0, 0,
- chip->spdif_bits[0] =
- SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
- SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
- SPCS_GENERATIONSTATUS | 0x00001200 |
- 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ def_bits =
+ SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+ SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+ SPCS_GENERATIONSTATUS | 0x00001200 |
+ 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+ if (!resume) {
+ chip->spdif_str_bits[0] = chip->spdif_bits[0] = def_bits;
+ chip->spdif_str_bits[1] = chip->spdif_bits[1] = def_bits;
+ chip->spdif_str_bits[2] = chip->spdif_bits[2] = def_bits;
+ chip->spdif_str_bits[3] = chip->spdif_bits[3] = def_bits;
+ }
/* Only SPCS1 has been tested */
- snd_ca0106_ptr_write(chip, SPCS1, 0,
- chip->spdif_bits[1] =
- SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
- SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
- SPCS_GENERATIONSTATUS | 0x00001200 |
- 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
- snd_ca0106_ptr_write(chip, SPCS2, 0,
- chip->spdif_bits[2] =
- SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
- SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
- SPCS_GENERATIONSTATUS | 0x00001200 |
- 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
- snd_ca0106_ptr_write(chip, SPCS3, 0,
- chip->spdif_bits[3] =
- SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
- SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
- SPCS_GENERATIONSTATUS | 0x00001200 |
- 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+ snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_str_bits[1]);
+ snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_str_bits[0]);
+ snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_str_bits[2]);
+ snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_str_bits[3]);
snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
@@ -1433,92 +1367,124 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
/* Write 0x8000 to AC97_REC_GAIN to mute it. */
outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
outw(0x8000, chip->port + AC97DATA);
-#if 0
+#if 0 /* FIXME: what are these? */
snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
#endif
- //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+ /* OSS drivers set this. */
+ /* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */
+
/* Analog or Digital output */
snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
- snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
+ /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers.
+ * Use 0x000f0000 for surround71
+ */
+ snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000);
+
chip->spdif_enable = 0; /* Set digital SPDIF output off */
- //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
- //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+ /*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */
+ /*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */
+
+ /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000);
+ /* (Mute) CAPTURE feedback into PLAYBACK volume.
+ * Only lower 16 bits matter.
+ */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff);
+ /* SPDIF IN Volume */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000);
+ /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+ snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000);
- snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
- snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
- snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
- snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
- for(ch = 0; ch < 4; ch++) {
- snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+
+ for (ch = 0; ch < 4; ch++) {
+ /* Only high 16 bits matter */
+ snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030);
snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
- //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
- //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
- snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
- snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+#if 0 /* Mute */
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040);
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040);
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff);
+ snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff);
+#endif
}
if (chip->details->i2c_adc == 1) {
/* Select MIC, Line in, TAD in, AUX in */
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
/* Default to CAPTURE_SOURCE to i2s in */
- chip->capture_source = 3;
+ if (!resume)
+ chip->capture_source = 3;
} else if (chip->details->ac97 == 1) {
/* Default to AC97 in */
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
/* Default to CAPTURE_SOURCE to AC97 in */
- chip->capture_source = 4;
+ if (!resume)
+ chip->capture_source = 4;
} else {
/* Select MIC, Line in, TAD in, AUX in */
snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
/* Default to Set CAPTURE_SOURCE to i2s in */
- chip->capture_source = 3;
+ if (!resume)
+ chip->capture_source = 3;
}
- if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
- /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+ if (chip->details->gpio_type == 2) {
+ /* The SB0438 use GPIO differently. */
+ /* FIXME: Still need to find out what the other GPIO bits do.
+ * E.g. For digital spdif out.
+ */
outl(0x0, chip->port+GPIO);
- //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+ /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
outl(0x005f5301, chip->port+GPIO); /* Analog */
- } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
- /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+ } else if (chip->details->gpio_type == 1) {
+ /* The SB0410 and SB0413 use GPIO differently. */
+ /* FIXME: Still need to find out what the other GPIO bits do.
+ * E.g. For digital spdif out.
+ */
outl(0x0, chip->port+GPIO);
- //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+ /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
outl(0x005f5301, chip->port+GPIO); /* Analog */
} else {
outl(0x0, chip->port+GPIO);
outl(0x005f03a3, chip->port+GPIO); /* Analog */
- //outl(0x005f02a2, chip->port+GPIO); /* SPDIF */
+ /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
}
snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
- //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
- //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
- //outl(0x00000009, chip->port+HCFG);
- outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+ /* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
+ /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+ /* outl(0x00001409, chip->port+HCFG); */
+ /* outl(0x00000009, chip->port+HCFG); */
+ /* AC97 2.0, Enable outputs. */
+ outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
- if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+ if (chip->details->i2c_adc == 1) {
+ /* The SB0410 and SB0413 use I2C to control ADC. */
int size, n;
size = ARRAY_SIZE(i2c_adc_init);
- //snd_printk("I2C:array size=0x%x\n", size);
- for (n=0; n < size; n++) {
- snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
- }
- for (n=0; n < 4; n++) {
- chip->i2c_capture_volume[n][0]= 0xcf;
- chip->i2c_capture_volume[n][1]= 0xcf;
+ /* snd_printk("I2C:array size=0x%x\n", size); */
+ for (n = 0; n < size; n++)
+ snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
+ i2c_adc_init[n][1]);
+ for (n = 0; n < 4; n++) {
+ chip->i2c_capture_volume[n][0] = 0xcf;
+ chip->i2c_capture_volume[n][1] = 0xcf;
}
- chip->i2c_capture_source=2; /* Line in */
- //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+ chip->i2c_capture_source = 2; /* Line in */
+ /* Enable Line-in capture. MIC in currently untested. */
+ /* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
}
- if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */
+
+ if (chip->details->spi_dac == 1) {
+ /* The SB0570 use SPI to control DAC. */
int size, n;
size = ARRAY_SIZE(spi_dac_init);
@@ -1530,9 +1496,112 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
chip->spi_dac_reg[reg] = spi_dac_init[n];
}
}
+}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
+static void ca0106_stop_chip(struct snd_ca0106 *chip)
+{
+ /* disable interrupts */
+ snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+ outl(0, chip->port + INTE);
+ snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+ udelay(1000);
+ /* disable audio */
+ /* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
+ outl(0, chip->port + HCFG);
+ /* FIXME: We need to stop and DMA transfers here.
+ * But as I am not sure how yet, we cannot from the dma pages.
+ * So we can fix: snd-malloc: Memory leak? pages not freed = 8
+ */
+}
+
+static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
+ struct pci_dev *pci,
+ struct snd_ca0106 **rchip)
+{
+ struct snd_ca0106 *chip;
+ struct snd_ca0106_details *c;
+ int err;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_ca0106_dev_free,
+ };
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+ if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+ pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
+ printk(KERN_ERR "error to set 32bit mask DMA\n");
+ pci_disable_device(pci);
+ return -ENXIO;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+
+ spin_lock_init(&chip->emu_lock);
+
+ chip->port = pci_resource_start(pci, 0);
+ chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
+ if (!chip->res_port) {
+ snd_ca0106_free(chip);
+ printk(KERN_ERR "cannot allocate the port\n");
+ return -EBUSY;
+ }
+
+ if (request_irq(pci->irq, snd_ca0106_interrupt,
+ IRQF_SHARED, "snd_ca0106", chip)) {
+ snd_ca0106_free(chip);
+ printk(KERN_ERR "cannot grab irq\n");
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+
+ /* This stores the periods table. */
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+ 1024, &chip->buffer) < 0) {
+ snd_ca0106_free(chip);
+ return -ENOMEM;
+ }
+
+ pci_set_master(pci);
+ /* read serial */
+ pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+ pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+ printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
+ chip->model, pci->revision, chip->serial);
+ strcpy(card->driver, "CA0106");
+ strcpy(card->shortname, "CA0106");
+
+ for (c = ca0106_chip_details; c->serial; c++) {
+ if (subsystem[dev]) {
+ if (c->serial == subsystem[dev])
+ break;
+ } else if (c->serial == chip->serial)
+ break;
+ }
+ chip->details = c;
+ if (subsystem[dev]) {
+ printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
+ "subsystem=0x%x. Forced to subsystem=0x%x\n",
+ c->name, chip->serial, subsystem[dev]);
+ }
+
+ sprintf(card->longname, "%s at 0x%lx irq %i",
+ c->name, chip->port, chip->irq);
+
+ ca0106_init_chip(chip, 0);
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
snd_ca0106_free(chip);
return err;
}
@@ -1629,7 +1698,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
static int dev;
struct snd_card *card;
struct snd_ca0106 *chip;
- int err;
+ int i, err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -1642,44 +1711,31 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
if (card == NULL)
return -ENOMEM;
- if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_ca0106_create(dev, card, pci, &chip);
+ if (err < 0)
+ goto error;
+ card->private_data = chip;
- if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
- if ((err = snd_ca0106_ac97(chip)) < 0) {
- snd_card_free(card);
- return err;
- }
+ for (i = 0; i < 4; i++) {
+ err = snd_ca0106_pcm(chip, i);
+ if (err < 0)
+ goto error;
}
- if ((err = snd_ca0106_mixer(chip)) < 0) {
- snd_card_free(card);
- return err;
+
+ if (chip->details->ac97 == 1) {
+ /* The SB0410 and SB0413 do not have an AC97 chip. */
+ err = snd_ca0106_ac97(chip);
+ if (err < 0)
+ goto error;
}
+ err = snd_ca0106_mixer(chip);
+ if (err < 0)
+ goto error;
snd_printdd("ca0106: probe for MIDI channel A ...");
- if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
- snd_card_free(card);
- snd_printdd(" failed, err=0x%x\n",err);
- return err;
- }
+ err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
+ if (err < 0)
+ goto error;
snd_printdd(" done.\n");
#ifdef CONFIG_PROC_FS
@@ -1688,14 +1744,17 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
snd_card_set_dev(card, &pci->dev);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
+
+ error:
+ snd_card_free(card);
+ return err;
}
static void __devexit snd_ca0106_remove(struct pci_dev *pci)
@@ -1704,6 +1763,59 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL);
}
+#ifdef CONFIG_PM
+static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_ca0106 *chip = card->private_data;
+ int i;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ for (i = 0; i < 4; i++)
+ snd_pcm_suspend_all(chip->pcm[i]);
+ if (chip->details->ac97)
+ snd_ac97_suspend(chip->ac97);
+ snd_ca0106_mixer_suspend(chip);
+
+ ca0106_stop_chip(chip);
+
+ pci_disable_device(pci);
+ pci_save_state(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+ return 0;
+}
+
+static int snd_ca0106_resume(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_ca0106 *chip = card->private_data;
+ int i;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+
+ if (pci_enable_device(pci) < 0) {
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ pci_set_master(pci);
+
+ ca0106_init_chip(chip, 1);
+
+ if (chip->details->ac97)
+ snd_ac97_resume(chip->ac97);
+ snd_ca0106_mixer_resume(chip);
+ if (chip->details->spi_dac) {
+ for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++)
+ snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]);
+ }
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
// PCI IDs
static struct pci_device_id snd_ca0106_ids[] = {
{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */
@@ -1717,6 +1829,10 @@ static struct pci_driver driver = {
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
.remove = __devexit_p(snd_ca0106_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_ca0106_suspend,
+ .resume = snd_ca0106_resume,
+#endif
};
// initialization of the module
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 3025ed1b6e1e..ad2888705d2a 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -75,6 +75,84 @@
#include "ca0106.h"
+static void ca0106_spdif_enable(struct snd_ca0106 *emu)
+{
+ unsigned int val;
+
+ if (emu->spdif_enable) {
+ /* Digital */
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
+ val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
+ snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
+ val = inl(emu->port + GPIO) & ~0x101;
+ outl(val, emu->port + GPIO);
+
+ } else {
+ /* Analog */
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+ snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
+ val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
+ snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
+ val = inl(emu->port + GPIO) | 0x101;
+ outl(val, emu->port + GPIO);
+ }
+}
+
+static void ca0106_set_capture_source(struct snd_ca0106 *emu)
+{
+ unsigned int val = emu->capture_source;
+ unsigned int source, mask;
+ source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+ mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
+ snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+}
+
+static void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu,
+ unsigned int val, int force)
+{
+ unsigned int ngain, ogain;
+ u32 source;
+
+ snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+ ngain = emu->i2c_capture_volume[val][0]; /* Left */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+ if (force || ngain != ogain)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff);
+ ngain = emu->i2c_capture_volume[val][1]; /* Right */
+ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
+ if (force || ngain != ogain)
+ snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff);
+ source = 1 << val;
+ snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
+ emu->i2c_capture_source = val;
+}
+
+static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
+{
+ u32 tmp;
+
+ if (emu->capture_mic_line_in) {
+ /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
+ tmp = inl(emu->port+GPIO) & ~0x400;
+ tmp = tmp | 0x400;
+ outl(tmp, emu->port+GPIO);
+ /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
+ } else {
+ /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
+ tmp = inl(emu->port+GPIO) & ~0x400;
+ outl(tmp, emu->port+GPIO);
+ /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
+ }
+}
+
+static void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx)
+{
+ snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]);
+}
+
+/*
+ */
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
@@ -95,30 +173,12 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
int change = 0;
- u32 mask;
val = !!ucontrol->value.integer.value[0];
change = (emu->spdif_enable != val);
if (change) {
emu->spdif_enable = val;
- if (val) {
- /* Digital */
- snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
- snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
- snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
- snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
- mask = inl(emu->port + GPIO) & ~0x101;
- outl(mask, emu->port + GPIO);
-
- } else {
- /* Analog */
- snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
- snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
- snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
- snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
- mask = inl(emu->port + GPIO) | 0x101;
- outl(mask, emu->port + GPIO);
- }
+ ca0106_spdif_enable(emu);
}
return change;
}
@@ -154,8 +214,6 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
int change = 0;
- u32 mask;
- u32 source;
val = ucontrol->value.enumerated.item[0] ;
if (val >= 6)
@@ -163,9 +221,7 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
change = (emu->capture_source != val);
if (change) {
emu->capture_source = val;
- source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
- mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
- snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+ ca0106_set_capture_source(emu);
}
return change;
}
@@ -200,9 +256,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
{
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int source_id;
- unsigned int ngain, ogain;
int change = 0;
- u32 source;
/* If the capture source has changed,
* update the capture volume from the cached value
* for the particular source.
@@ -212,18 +266,7 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
change = (emu->i2c_capture_source != source_id);
if (change) {
- snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
- ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
- ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
- if (ngain != ogain)
- snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
- ngain = emu->i2c_capture_volume[source_id][1]; /* Left */
- ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */
- if (ngain != ogain)
- snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
- source = 1 << source_id;
- snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */
- emu->i2c_capture_source = source_id;
+ ca0106_set_i2c_capture_source(emu, source_id, 0);
}
return change;
}
@@ -271,7 +314,6 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
int change = 0;
- u32 tmp;
val = ucontrol->value.enumerated.item[0] ;
if (val > 1)
@@ -279,18 +321,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
change = (emu->capture_mic_line_in != val);
if (change) {
emu->capture_mic_line_in = val;
- if (val) {
- //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
- tmp = tmp | 0x400;
- outl(tmp, emu->port+GPIO);
- //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
- } else {
- //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
- outl(tmp, emu->port+GPIO);
- //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
- }
+ ca0106_set_capture_mic_line_in(emu);
}
return change;
}
@@ -322,16 +353,33 @@ static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
+static void decode_spdif_bits(unsigned char *status, unsigned int bits)
+{
+ status[0] = (bits >> 0) & 0xff;
+ status[1] = (bits >> 8) & 0xff;
+ status[2] = (bits >> 16) & 0xff;
+ status[3] = (bits >> 24) & 0xff;
+}
+
+static int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
- ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+ decode_spdif_bits(ucontrol->value.iec958.status,
+ emu->spdif_bits[idx]);
+ return 0;
+}
+
+static int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ decode_spdif_bits(ucontrol->value.iec958.status,
+ emu->spdif_str_bits[idx]);
return 0;
}
@@ -345,24 +393,48 @@ static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
+static unsigned int encode_spdif_bits(unsigned char *status)
+{
+ return ((unsigned int)status[0] << 0) |
+ ((unsigned int)status[1] << 8) |
+ ((unsigned int)status[2] << 16) |
+ ((unsigned int)status[3] << 24);
+}
+
+static int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int change;
unsigned int val;
- val = (ucontrol->value.iec958.status[0] << 0) |
- (ucontrol->value.iec958.status[1] << 8) |
- (ucontrol->value.iec958.status[2] << 16) |
- (ucontrol->value.iec958.status[3] << 24);
- change = val != emu->spdif_bits[idx];
- if (change) {
- snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
+ val = encode_spdif_bits(ucontrol->value.iec958.status);
+ if (val != emu->spdif_bits[idx]) {
emu->spdif_bits[idx] = val;
+ /* FIXME: this isn't safe, but needed to keep the compatibility
+ * with older alsa-lib config
+ */
+ emu->spdif_str_bits[idx] = val;
+ ca0106_set_spdif_bits(emu, idx);
+ return 1;
}
- return change;
+ return 0;
+}
+
+static int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int val;
+
+ val = encode_spdif_bits(ucontrol->value.iec958.status);
+ if (val != emu->spdif_str_bits[idx]) {
+ emu->spdif_str_bits[idx] = val;
+ ca0106_set_spdif_bits(emu, idx);
+ return 1;
+ }
+ return 0;
}
static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
@@ -573,8 +645,16 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.count = 4,
.info = snd_ca0106_spdif_info,
- .get = snd_ca0106_spdif_get,
- .put = snd_ca0106_spdif_put
+ .get = snd_ca0106_spdif_get_default,
+ .put = snd_ca0106_spdif_put_default
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
+ .count = 4,
+ .info = snd_ca0106_spdif_info,
+ .get = snd_ca0106_spdif_get_stream,
+ .put = snd_ca0106_spdif_put_stream
},
};
@@ -773,3 +853,50 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
return 0;
}
+#ifdef CONFIG_PM
+struct ca0106_vol_tbl {
+ unsigned int channel_id;
+ unsigned int reg;
+};
+
+static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
+ { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
+ { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
+ { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
+ { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 },
+ { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 },
+ { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 },
+ { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 },
+ { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 },
+ { 1, CAPTURE_CONTROL },
+};
+
+void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip)
+{
+ int i;
+
+ /* save volumes */
+ for (i = 0; i < NUM_SAVED_VOLUMES; i++)
+ chip->saved_vol[i] =
+ snd_ca0106_ptr_read(chip, saved_volumes[i].reg,
+ saved_volumes[i].channel_id);
+}
+
+void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
+{
+ int i;
+
+ for (i = 0; i < NUM_SAVED_VOLUMES; i++)
+ snd_ca0106_ptr_write(chip, saved_volumes[i].reg,
+ saved_volumes[i].channel_id,
+ chip->saved_vol[i]);
+
+ ca0106_spdif_enable(chip);
+ ca0106_set_capture_source(chip);
+ ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1);
+ for (i = 0; i < 4; i++)
+ ca0106_set_spdif_bits(chip, i);
+ if (chip->details->i2c_adc)
+ ca0106_set_capture_mic_line_in(chip);
+}
+#endif /* CONFIG_PM */
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index fb6dc3980257..8ab07aa63652 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -3640,7 +3640,10 @@ int snd_cs46xx_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_cs46xx *chip = card->private_data;
- int i, amp_saved;
+ int amp_saved;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ int i;
+#endif
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index bb3d57e6a3cb..fda7a94c992f 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -4,6 +4,9 @@
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
+ifdef CONFIG_MGEODE_LX
+snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
+endif
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index 1d8b16052535..826e6dec2e97 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -159,10 +159,14 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
return err;
memset(&ac97, 0, sizeof(ac97));
- ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM;
+ ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
+ | AC97_SCAP_POWER_SAVE;
ac97.private_data = cs5535au;
ac97.pci = cs5535au->pci;
+ /* set any OLPC-specific scaps */
+ olpc_prequirks(card, &ac97);
+
if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
snd_printk(KERN_ERR "mixer failed\n");
return err;
@@ -170,6 +174,12 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
+ err = olpc_quirks(card, cs5535au->ac97);
+ if (err < 0) {
+ snd_printk(KERN_ERR "olpc quirks failed\n");
+ return err;
+ }
+
return 0;
}
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 66bae7664193..7a298ac662e3 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -78,6 +78,7 @@ struct cs5535audio_dma {
unsigned int buf_addr, buf_bytes;
unsigned int period_bytes, periods;
u32 saved_prd;
+ int pcm_open_flag;
};
struct cs5535audio {
@@ -93,8 +94,46 @@ struct cs5535audio {
struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
};
+#ifdef CONFIG_PM
int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
int snd_cs5535audio_resume(struct pci_dev *pci);
+#endif
+
+#if defined(CONFIG_OLPC) && defined(CONFIG_MGEODE_LX)
+void __devinit olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97);
+int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void olpc_analog_input(struct snd_ac97 *ac97, int on);
+void olpc_mic_bias(struct snd_ac97 *ac97, int on);
+
+static inline void olpc_capture_open(struct snd_ac97 *ac97)
+{
+ /* default to Analog Input off */
+ olpc_analog_input(ac97, 0);
+ /* enable MIC Bias for recording */
+ olpc_mic_bias(ac97, 1);
+}
+
+static inline void olpc_capture_close(struct snd_ac97 *ac97)
+{
+ /* disable Analog Input */
+ olpc_analog_input(ac97, 0);
+ /* disable the MIC Bias (so the recording LED turns off) */
+ olpc_mic_bias(ac97, 0);
+}
+#else
+static inline void olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97) { }
+static inline int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+{
+ return 0;
+}
+static inline void olpc_analog_input(struct snd_ac97 *ac97, int on) { }
+static inline void olpc_mic_bias(struct snd_ac97 *ac97, int on) { }
+static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
+static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
+#endif
+
int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
#endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
new file mode 100644
index 000000000000..5c6814335cd7
--- /dev/null
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -0,0 +1,179 @@
+/*
+ * OLPC XO-1 additional sound features
+ *
+ * Copyright © 2006 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright © 2007-2008 Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+
+#include <asm/olpc.h>
+#include "cs5535audio.h"
+
+/*
+ * OLPC has an additional feature on top of the regular AD1888 codec features.
+ * It has an Analog Input mode that is switched into (after disabling the
+ * High Pass Filter) via GPIO. It is supported on B2 and later models.
+ */
+void olpc_analog_input(struct snd_ac97 *ac97, int on)
+{
+ int err;
+
+ if (!machine_is_olpc())
+ return;
+
+ /* update the High Pass Filter (via AC97_AD_TEST2) */
+ err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
+ 1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
+ if (err < 0) {
+ snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
+ return;
+ }
+
+ /* set Analog Input through GPIO */
+ if (on)
+ geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+ else
+ geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
+}
+
+/*
+ * OLPC XO-1's V_REFOUT is a mic bias enable.
+ */
+void olpc_mic_bias(struct snd_ac97 *ac97, int on)
+{
+ int err;
+
+ if (!machine_is_olpc())
+ return;
+
+ on = on ? 0 : 1;
+ err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
+ 1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
+ if (err < 0)
+ snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
+}
+
+static int olpc_dc_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+ v->value.integer.value[0] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
+ GPIO_OUTPUT_VAL);
+ return 0;
+}
+
+static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+ struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+
+ olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
+ return 1;
+}
+
+static int olpc_mic_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+ struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+ struct snd_ac97 *ac97 = cs5535au->ac97;
+ int i;
+
+ i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
+ v->value.integer.value[0] = i ? 0 : 1;
+ return 0;
+}
+
+static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
+{
+ struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
+
+ olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
+ return 1;
+}
+
+static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Mode Enable",
+ .info = olpc_dc_info,
+ .get = olpc_dc_get,
+ .put = olpc_dc_put,
+ .private_value = 0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "MIC Bias Enable",
+ .info = olpc_mic_info,
+ .get = olpc_mic_get,
+ .put = olpc_mic_put,
+ .private_value = 0,
+},
+};
+
+void __devinit olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97)
+{
+ if (!machine_is_olpc())
+ return;
+
+ /* invert EAPD if on an OLPC B3 or higher */
+ if (olpc_board_at_least(olpc_board_pre(0xb3)))
+ ac97->scaps |= AC97_SCAP_INV_EAPD;
+}
+
+int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+{
+ struct snd_ctl_elem_id elem;
+ int i, err;
+
+ if (!machine_is_olpc())
+ return 0;
+
+ /* drop the original AD1888 HPF control */
+ memset(&elem, 0, sizeof(elem));
+ elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
+ snd_ctl_remove_id(card, &elem);
+
+ /* drop the original V_REFOUT control */
+ memset(&elem, 0, sizeof(elem));
+ elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
+ snd_ctl_remove_id(card, &elem);
+
+ /* add the OLPC-specific controls */
+ for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
+ ac97->private_data));
+ if (err < 0)
+ return err;
+ }
+
+ /* turn off the mic by default */
+ olpc_mic_bias(ac97, 0);
+ return 0;
+}
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index cdcda87116c3..0f48a871f17b 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -260,6 +260,9 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
params_periods(hw_params),
params_period_bytes(hw_params));
+ if (!err)
+ dma->pcm_open_flag = 1;
+
return err;
}
@@ -268,6 +271,15 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
struct cs5535audio_dma *dma = substream->runtime->private_data;
+ if (dma->pcm_open_flag) {
+ if (substream == cs5535au->playback_substream)
+ snd_ac97_update_power(cs5535au->ac97,
+ AC97_PCM_FRONT_DAC_RATE, 0);
+ else
+ snd_ac97_update_power(cs5535au->ac97,
+ AC97_PCM_LR_ADC_RATE, 0);
+ dma->pcm_open_flag = 0;
+ }
cs5535audio_clear_dma_packets(cs5535au, dma, substream);
return snd_pcm_lib_free_pages(substream);
}
@@ -351,11 +363,14 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
if ((err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
+ olpc_capture_open(cs5535au->ac97);
return 0;
}
static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
{
+ struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
+ olpc_capture_close(cs5535au->ac97);
return 0;
}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index de5ee8f097f6..7958006a1d66 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -69,7 +69,7 @@ MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
* EMU10K1 init / done
*************************************************************************/
-void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch)
+void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
{
snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
snd_emu10k1_ptr_write(emu, IP, ch, 0);
@@ -151,9 +151,9 @@ static unsigned int i2c_adc_init[][2] = {
{ 0x12, 0x32 }, /* ALC Control 3 */
{ 0x13, 0x00 }, /* Noise gate control */
{ 0x14, 0xa6 }, /* Limiter control */
- { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */
+ { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for A2ZS Notebook */
};
-
+
static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
{
unsigned int silent_page;
@@ -161,8 +161,8 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
u32 tmp;
/* disable audio and lock cache */
- outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE,
- emu->port + HCFG);
+ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+ HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
/* reset recording buffers */
snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
@@ -179,7 +179,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
- if (emu->audigy){
+ if (emu->audigy) {
/* set SPDIF bypass mode */
snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
/* enable rear left + rear right AC97 slots */
@@ -197,12 +197,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Hacks for Alice3 to work independent of haP16V driver */
- //Setup SRCMulti_I2S SamplingRate
+ /* Setup SRCMulti_I2S SamplingRate */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp &= 0xfffff1ff;
tmp |= (0x2<<9);
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
-
+
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
/* Setup SRCMulti Input Audio Enable */
@@ -217,7 +217,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
/* Hacks for Alice3 to work independent of haP16V driver */
snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
- //Setup SRCMulti_I2S SamplingRate
+ /* Setup SRCMulti_I2S SamplingRate */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp &= 0xfffff1ff;
tmp |= (0x2<<9);
@@ -270,13 +270,13 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
size = ARRAY_SIZE(i2c_adc_init);
for (n = 0; n < size; n++)
snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
- for (n=0; n < 4; n++) {
- emu->i2c_capture_volume[n][0]= 0xcf;
- emu->i2c_capture_volume[n][1]= 0xcf;
+ for (n = 0; n < 4; n++) {
+ emu->i2c_capture_volume[n][0] = 0xcf;
+ emu->i2c_capture_volume[n][1] = 0xcf;
}
}
-
+
snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */
@@ -313,7 +313,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
(emu->model == 0x21 && emu->revision < 6))
outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
else
- // With on-chip joystick
+ /* With on-chip joystick */
outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
if (enable_ir) { /* enable IR for SB Live */
@@ -335,9 +335,9 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
udelay(100);
outl(reg, emu->port + HCFG);
- }
+ }
}
-
+
if (emu->card_capabilities->emu_model) {
; /* Disable all access to A_IOCFG for the emu1010 */
} else if (emu->card_capabilities->i2c_adc) {
@@ -364,7 +364,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
-
+
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz.
@@ -378,12 +378,12 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
}
}
-
+
#if 0
{
unsigned int tmp;
/* FIXME: the following routine disables LiveDrive-II !! */
- // TOSLink detection
+ /* TOSLink detection */
emu->tos_link = 0;
tmp = inl(emu->port + HCFG);
if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
@@ -400,7 +400,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
}
-int snd_emu10k1_done(struct snd_emu10k1 * emu)
+int snd_emu10k1_done(struct snd_emu10k1 *emu)
{
int ch;
@@ -495,7 +495,7 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
#define EC_LAST_PROMFILE_ADDR 0x2f
-#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The
+#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The
* can be up to 30 characters in length
* and is stored as a NULL-terminated
* ASCII string. Any unused bytes must be
@@ -503,8 +503,8 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */
-/* Most of this stuff is pretty self-evident. According to the hardware
- * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
+/* Most of this stuff is pretty self-evident. According to the hardware
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
* offset problem. Weird.
*/
#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
@@ -523,7 +523,7 @@ int snd_emu10k1_done(struct snd_emu10k1 * emu)
* register.
*/
-static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value)
+static void snd_emu10k1_ecard_write(struct snd_emu10k1 *emu, unsigned int value)
{
unsigned short count;
unsigned int data;
@@ -561,7 +561,7 @@ static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value
* channel.
*/
-static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
+static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 *emu,
unsigned short gain)
{
unsigned int bit;
@@ -574,7 +574,7 @@ static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
for (bit = (1 << 15); bit; bit >>= 1) {
unsigned int value;
-
+
value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
if (gain & bit)
@@ -589,7 +589,7 @@ static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu,
snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
}
-static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 *emu)
{
unsigned int hc_value;
@@ -598,7 +598,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
- /* Step 0: Set the codec type in the hardware control register
+ /* Step 0: Set the codec type in the hardware control register
* and enable audio output */
hc_value = inl(emu->port + HCFG);
outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
@@ -629,7 +629,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu)
return 0;
}
-static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
{
unsigned long special_port;
unsigned int value;
@@ -656,7 +656,7 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu)
return 0;
}
-static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename)
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, const char *filename)
{
int err;
int n, i;
@@ -666,11 +666,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
unsigned long flags;
const struct firmware *fw_entry;
- if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) {
- snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err);
+ err = request_firmware(&fw_entry, filename, &emu->pci->dev);
+ if (err != 0) {
+ snd_printk(KERN_ERR "firmware: %s not found. Err = %d\n", filename, err);
return err;
}
- snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+ snd_printk(KERN_INFO "firmware size = 0x%zx\n", fw_entry->size);
/* The FPGA is a Xilinx Spartan IIE XC2S50E */
/* GPIO7 -> FPGA PGMN
@@ -685,13 +686,13 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
write_post = inl(emu->port + A_IOCFG);
udelay(100); /* Allow FPGA memory to clean */
- for(n = 0; n < fw_entry->size; n++) {
- value=fw_entry->data[n];
- for(i = 0; i < 8; i++) {
+ for (n = 0; n < fw_entry->size; n++) {
+ value = fw_entry->data[n];
+ for (i = 0; i < 8; i++) {
reg = 0x80;
if (value & 0x1)
reg = reg | 0x20;
- value = value >> 1;
+ value = value >> 1;
outl(reg, emu->port + A_IOCFG);
write_post = inl(emu->port + A_IOCFG);
outl(reg | 0x40, emu->port + A_IOCFG);
@@ -703,14 +704,14 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
write_post = inl(emu->port + A_IOCFG);
spin_unlock_irqrestore(&emu->emu_lock, flags);
- release_firmware(fw_entry);
+ release_firmware(fw_entry);
return 0;
}
static int emu1010_firmware_thread(void *data)
{
- struct snd_emu10k1 * emu = data;
- int tmp,tmp2;
+ struct snd_emu10k1 *emu = data;
+ int tmp, tmp2;
int reg;
int err;
@@ -719,50 +720,50 @@ static int emu1010_firmware_thread(void *data)
msleep_interruptible(1000);
if (kthread_should_stop())
break;
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg ); /* OPTIONS: Which cards are attached to the EMU */
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
/* Audio Dock attached */
/* Return to Audio Dock programming mode */
snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK);
if (emu->card_capabilities->emu_model ==
EMU_MODEL_EMU1010) {
- if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+ err = snd_emu1010_load_firmware(emu, DOCK_FILENAME);
+ if (err != 0)
continue;
- }
} else if (emu->card_capabilities->emu_model ==
EMU_MODEL_EMU1010B) {
- if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+ err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
+ if (err != 0)
continue;
- }
} else if (emu->card_capabilities->emu_model ==
EMU_MODEL_EMU1616) {
- if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+ err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
+ if (err != 0)
continue;
- }
}
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 );
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg);
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", reg);
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg);
if ((reg & 0x1f) != 0x15) {
/* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", reg);
continue;
}
snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp );
- snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 );
- snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+ snd_printk("Audio Dock ver:%d.%d\n", tmp, tmp2);
/* Sync clocking between 1010 and Dock */
/* Allow DLL to settle */
msleep(10);
/* Unmute all. Default is muted after a firmware load */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
}
}
snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
@@ -800,10 +801,10 @@ static int emu1010_firmware_thread(void *data)
* 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
* 16 x 32-bit capture - snd_emu10k1_capture_efx_ops
*/
-static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
{
unsigned int i;
- int tmp,tmp2;
+ int tmp, tmp2;
int reg;
int err;
const char *filename = NULL;
@@ -818,7 +819,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
* Lock Tank Memory Cache,
* Mute all codecs.
*/
- outl(0x0005a004, emu->port + HCFG);
+ outl(0x0005a004, emu->port + HCFG);
/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
* Mute all codecs.
*/
@@ -829,25 +830,25 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
outl(0x0005a000, emu->port + HCFG);
/* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
- snd_printdd("reg1=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+ snd_printdd("reg1 = 0x%x\n", reg);
if ((reg & 0x3f) == 0x15) {
/* FPGA netlist already present so clear it */
/* Return to programming mode */
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02);
}
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
- snd_printdd("reg2=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+ snd_printdd("reg2 = 0x%x\n", reg);
if ((reg & 0x3f) == 0x15) {
/* FPGA failed to return to programming mode */
snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
return -ENODEV;
}
- snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+ snd_printk(KERN_INFO "emu1010: EMU_HANA_ID = 0x%x\n", reg);
switch (emu->card_capabilities->emu_model) {
case EMU_MODEL_EMU1010:
filename = HANA_FILENAME;
@@ -876,25 +877,25 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
}
/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
if ((reg & 0x3f) != 0x15) {
/* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg);
return -ENODEV;
}
snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp );
- snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 );
- snd_printk("Hana ver:%d.%d\n",tmp ,tmp2);
+ snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
+ snd_printk("emu1010: Hana version: %d.%d\n", tmp, tmp2);
/* Enable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
- snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
- snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp );
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+ snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+ snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
/* Optical -> ADAT I/O */
/* 0 : SPDIF
* 1 : ADAT
@@ -904,41 +905,42 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
tmp = 0;
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
- snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp );
- snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp );
+ snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
/* Set no attenuation on Audio Dock pads. */
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
emu->emu1010.adc_pads = 0x00;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
+ snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
/* Unmute Audio dock DACs, Headphone source DAC-4. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
- snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
+ snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
/* DAC PADs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
emu->emu1010.dac_pads = 0x0f;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp );
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 );
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
+ snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
/* SPDIF Format. Set Consumer mode, 24bit, copy enable */
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
/* MIDI routing */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
/* Unknown. */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c );
- /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
+ /* IRQ Enable: Alll on */
+ /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
/* IRQ Enable: All off */
- snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg );
- snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg);
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+ snd_printk(KERN_INFO "emu1010: Card options3 = 0x%x\n", reg);
/* Default WCLK set to 48kHz. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
/* Word Clock source, Internal 48kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
- //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+ /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
/* Audio Dock LEDs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
#if 0
/* For 96kHz */
@@ -992,7 +994,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
* Defaults only, users will set their own values anyways, let's
* just copy/paste.
*/
-
+
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
snd_emu1010_fpga_link_dst_src_write(emu,
@@ -1037,19 +1039,19 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
#endif
- for (i = 0;i < 0x20; i++ ) {
- /* AudioDock Elink <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE);
+ for (i = 0; i < 0x20; i++) {
+ /* AudioDock Elink <- Silence */
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0100 + i, EMU_SRC_SILENCE);
}
- for (i = 0;i < 4; i++) {
+ for (i = 0; i < 4; i++) {
/* Hana SPDIF Out <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE);
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0200 + i, EMU_SRC_SILENCE);
}
- for (i = 0;i < 7; i++) {
+ for (i = 0; i < 7; i++) {
/* Hamoa DAC <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE);
+ snd_emu1010_fpga_link_dst_src_write(emu, 0x0300 + i, EMU_SRC_SILENCE);
}
- for (i = 0;i < 7; i++) {
+ for (i = 0; i < 7; i++) {
/* Hana ADAT Out <- Silence */
snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
}
@@ -1065,30 +1067,30 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
snd_emu1010_fpga_link_dst_src_write(emu,
EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
-
/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
* Lock Sound Memory Cache, Lock Tank Memory Cache,
* Mute all codecs.
*/
- outl(0x0000a000, emu->port + HCFG);
+ outl(0x0000a000, emu->port + HCFG);
/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
* Lock Sound Memory Cache, Lock Tank Memory Cache,
* Un-Mute all codecs.
*/
outl(0x0000a001, emu->port + HCFG);
-
+
/* Initial boot complete. Now patches */
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp );
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp );
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
/* Start Micro/Audio Dock firmware loader thread */
if (!emu->emu1010.firmware_thread) {
@@ -1218,20 +1220,20 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
emu->emu1010.output_source[23] = 28;
}
/* TEMP: Select SPDIF in/out */
- //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */
+ /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
/* TEMP: Select 48kHz SPDIF out */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
/* Word Clock source, Internal 48kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K );
- //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X );
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+ /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
emu->emu1010.internal_clock = 1; /* 48000 */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
- //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */
- //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */
- //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */
+ /* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
+ /* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
+ /* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
return 0;
}
@@ -1247,13 +1249,13 @@ static void free_pm_buffer(struct snd_emu10k1 *emu);
static int snd_emu10k1_free(struct snd_emu10k1 *emu)
{
if (emu->port) { /* avoid access to already used hardware */
- snd_emu10k1_fx8010_tram_setup(emu, 0);
+ snd_emu10k1_fx8010_tram_setup(emu, 0);
snd_emu10k1_done(emu);
snd_emu10k1_free_efx(emu);
- }
+ }
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
/* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 );
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
}
if (emu->emu1010.firmware_thread)
kthread_stop(emu->emu1010.firmware_thread);
@@ -1278,7 +1280,7 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
#endif
if (emu->port)
pci_release_regions(emu->pci);
- if (emu->card_capabilities->ca0151_chip) /* P16V */
+ if (emu->card_capabilities->ca0151_chip) /* P16V */
snd_p16v_free(emu);
pci_disable_device(emu->pci);
kfree(emu);
@@ -1292,21 +1294,6 @@ static int snd_emu10k1_dev_free(struct snd_device *device)
}
static struct snd_emu_chip_details emu_chip_details[] = {
- /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
- /* Tested by James@superbug.co.uk 3rd July 2005 */
- /* DSP: CA0108-IAT
- * DAC: CS4382-KQ
- * ADC: Philips 1361T
- * AC97: STAC9750
- * CA0151: None
- */
- {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
- .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]",
- .id = "Audigy2",
- .emu10k2_chip = 1,
- .ca0108_chip = 1,
- .spk71 = 1,
- .ac97_chip = 1} ,
/* Audigy4 (Not PRO) SB0610 */
/* Tested by James@superbug.co.uk 4th April 2006 */
/* A_IOCFG bits
@@ -1346,20 +1333,37 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* CA0151: None
*/
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
- .driver = "Audigy2", .name = "Audigy 4 [SB0610]",
+ .driver = "Audigy2", .name = "SB Audigy 4 [SB0610]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.spk71 = 1,
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
+ /* Audigy 2 Value AC3 out does not work yet.
+ * Need to find out how to turn off interpolators.
+ */
+ /* Tested by James@superbug.co.uk 3rd July 2005 */
+ /* DSP: CA0108-IAT
+ * DAC: CS4382-KQ
+ * ADC: Philips 1361T
+ * AC97: STAC9750
+ * CA0151: None
+ */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+ .driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .ac97_chip = 1} ,
/* Audigy 2 ZS Notebook Cardbus card.*/
/* Tested by James@superbug.co.uk 6th November 2006 */
/* Audio output 7.1/Headphones working.
* Digital output working. (AC3 not checked, only PCM)
* Audio Mic/Line inputs working.
* Digital input not tested.
- */
+ */
/* DSP: Tina2
* DAC: Wolfson WM8768/WM8568
* ADC: Wolfson WM8775
@@ -1386,7 +1390,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
*
*/
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
- .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS Notebook [SB0530]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1396,7 +1400,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spk71 = 1} ,
/* Tested by James@superbug.co.uk 4th Nov 2007. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
- .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
+ .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1404,47 +1408,49 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spk71 = 1 ,
.emu_model = EMU_MODEL_EMU1616},
/* Tested by James@superbug.co.uk 4th Nov 2007. */
+ /* This is MAEM8960, 0202 is MAEM 8980 */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
- .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]",
+ .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.spk71 = 1,
- .emu_model = EMU_MODEL_EMU1010B},
+ .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
/* Tested by James@superbug.co.uk 8th July 2005. */
+ /* This is MAEM8810, 0202 is MAEM8820 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
- .driver = "Audigy2", .name = "E-mu 1010 [4001]",
+ .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.spk71 = 1,
- .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */
+ .emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
/* EMU0404b */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
- .driver = "Audigy2", .name = "E-mu 0404b [4002]",
+ .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.spk71 = 1,
- .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+ .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
/* Tested by James@superbug.co.uk 20-3-2007. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
- .driver = "Audigy2", .name = "E-mu 0404 [4002]",
+ .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
- /* Audigy4 (Not PRO) SB0610 */
- {.vendor = 0x1102, .device = 0x0008,
- .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
+ /* Note that all E-mu cards require kernel 2.6 or newer. */
+ {.vendor = 0x1102, .device = 0x0008,
+ .driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.ac97_chip = 1} ,
/* Tested by James@superbug.co.uk 3rd July 2005 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
- .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]",
+ .driver = "Audigy2", .name = "SB Audigy 4 PRO [SB0380]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1457,7 +1463,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* Just like 0x20021102
*/
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102,
- .driver = "Audigy2", .name = "Audigy 2 [SB0350b]",
+ .driver = "Audigy2", .name = "SB Audigy 2 [SB0350b]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1467,7 +1473,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
- .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]",
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1477,7 +1483,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
- .driver = "Audigy2", .name = "Audigy 2 ZS [2001]",
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0360]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1495,7 +1501,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* CA0151: Yes
*/
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
- .driver = "Audigy2", .name = "Audigy 2 [SB0240]",
+ .driver = "Audigy2", .name = "SB Audigy 2 [SB0240]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1505,7 +1511,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
- .driver = "Audigy2", .name = "Audigy 2 EX [1005]",
+ .driver = "Audigy2", .name = "SB Audigy 2 Platinum EX [SB0280]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1515,7 +1521,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
/* Dell OEM/Creative Labs Audigy 2 ZS */
/* See ALSA bug#1365 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
- .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]",
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0353]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1524,7 +1530,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
- .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]",
+ .driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1535,7 +1541,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.adc_1361t = 1, /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
- .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+ .driver = "Audigy2", .name = "SB Audigy 2 [Unknown]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1543,78 +1549,79 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
- .driver = "Audigy", .name = "Audigy 1 [SB0090]",
+ .driver = "Audigy", .name = "SB Audigy 1 [SB0092]",
.id = "Audigy",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
- .driver = "Audigy", .name = "Audigy 1 ES [SB0160]",
+ .driver = "Audigy", .name = "SB Audigy 1 ES [SB0160]",
.id = "Audigy",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.spdif_bug = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
- .driver = "Audigy", .name = "Audigy 1 [SB0090]",
+ .driver = "Audigy", .name = "SB Audigy 1 [SB0090]",
.id = "Audigy",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004,
- .driver = "Audigy", .name = "Audigy 1 [Unknown]",
+ .driver = "Audigy", .name = "Audigy 1 [Unknown]",
.id = "Audigy",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.ac97_chip = 1} ,
- {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
- .driver = "EMU10K1", .name = "SBLive! [SB0105]",
+ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
+ .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
+ .id = "Live",
+ .emu10k1_chip = 1,
+ .ac97_chip = 1,
+ .sblive51 = 1} ,
+ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806b1102,
+ .driver = "EMU10K1", .name = "SB Live! [SB0105]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
- {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102,
- .driver = "EMU10K1", .name = "SBLive! Value [SB0103]",
+ {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806a1102,
+ .driver = "EMU10K1", .name = "SB Live! Value [SB0103]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
- .driver = "EMU10K1", .name = "SBLive! Value [SB0101]",
+ .driver = "EMU10K1", .name = "SB Live! Value [SB0101]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
/* Tested by ALSA bug#1680 26th December 2005 */
- /* note: It really has SB0220 written on the card. */
+ /* note: It really has SB0220 written on the card, */
+ /* but it's SB0228 according to kx.inf */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102,
- .driver = "EMU10K1", .name = "SB Live 5.1 Dell OEM [SB0220]",
+ .driver = "EMU10K1", .name = "SB Live! 5.1 Dell OEM [SB0228]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
/* Tested by Thomas Zehetbauer 27th Aug 2005 */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102,
- .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]",
- .id = "Live",
- .emu10k1_chip = 1,
- .ac97_chip = 1,
- .sblive51 = 1} ,
- {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
- .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]",
+ .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
- .driver = "EMU10K1", .name = "SB Live 5.1",
+ .driver = "EMU10K1", .name = "SB Live! 5.1",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
/* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
- .driver = "EMU10K1", .name = "SBLive 5.1 [SB0060]",
+ .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0060]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum
@@ -1622,78 +1629,78 @@ static struct snd_emu_chip_details emu_chip_details[] = {
*/
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4850]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4850]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
- .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]",
+ .driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4871]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4871]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4831]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4831]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4870]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4870]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
/* Tested by James@superbug.co.uk 3rd July 2005 */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4832]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4832]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4830]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4830]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
- .driver = "EMU10K1", .name = "SB PCI512 [CT4790]",
+ .driver = "EMU10K1", .name = "SB PCI512 [CT4790]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4780]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4780]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
- .driver = "EMU10K1", .name = "E-mu APS [4001]",
+ .driver = "EMU10K1", .name = "E-mu APS [PC545]",
.id = "APS",
.emu10k1_chip = 1,
.ecard = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
- .driver = "EMU10K1", .name = "SBLive! [CT4620]",
+ .driver = "EMU10K1", .name = "SB Live! [CT4620]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
- .driver = "EMU10K1", .name = "SBLive! Value [CT4670]",
+ .driver = "EMU10K1", .name = "SB Live! Value [CT4670]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002,
- .driver = "EMU10K1", .name = "SB Live [Unknown]",
+ .driver = "EMU10K1", .name = "SB Live! [Unknown]",
.id = "Live",
.emu10k1_chip = 1,
.ac97_chip = 1,
@@ -1702,13 +1709,13 @@ static struct snd_emu_chip_details emu_chip_details[] = {
};
int __devinit snd_emu10k1_create(struct snd_card *card,
- struct pci_dev * pci,
+ struct pci_dev *pci,
unsigned short extin_mask,
unsigned short extout_mask,
long max_cache_bytes,
int enable_ir,
uint subsystem,
- struct snd_emu10k1 ** remu)
+ struct snd_emu10k1 **remu)
{
struct snd_emu10k1 *emu;
int idx, err;
@@ -1718,11 +1725,12 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
static struct snd_device_ops ops = {
.dev_free = snd_emu10k1_dev_free,
};
-
+
*remu = NULL;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
emu = kzalloc(sizeof(*emu), GFP_KERNEL);
@@ -1749,16 +1757,17 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
- snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
+ snd_printdd("vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n", pci->vendor, pci->device, emu->serial, emu->model);
for (c = emu_chip_details; c->vendor; c++) {
if (c->vendor == pci->vendor && c->device == pci->device) {
if (subsystem) {
- if (c->subsystem && (c->subsystem == subsystem) ) {
+ if (c->subsystem && (c->subsystem == subsystem))
break;
- } else continue;
+ else
+ continue;
} else {
- if (c->subsystem && (c->subsystem != emu->serial) )
+ if (c->subsystem && (c->subsystem != emu->serial))
continue;
if (c->revision && c->revision != emu->revision)
continue;
@@ -1774,14 +1783,18 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
}
emu->card_capabilities = c;
if (c->subsystem && !subsystem)
- snd_printdd("Sound card name=%s\n", c->name);
- else if (subsystem)
- snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n",
- c->name, pci->vendor, pci->device, emu->serial, c->subsystem);
- else
- snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n",
- c->name, pci->vendor, pci->device, emu->serial);
-
+ snd_printdd("Sound card name = %s\n", c->name);
+ else if (subsystem)
+ snd_printdd("Sound card name = %s, "
+ "vendor = 0x%x, device = 0x%x, subsystem = 0x%x. "
+ "Forced to subsytem = 0x%x\n", c->name,
+ pci->vendor, pci->device, emu->serial, c->subsystem);
+ else
+ snd_printdd("Sound card name = %s, "
+ "vendor = 0x%x, device = 0x%x, subsystem = 0x%x.\n",
+ c->name, pci->vendor, pci->device,
+ emu->serial);
+
if (!*card->id && c->id) {
int i, n = 0;
strlcpy(card->id, c->id, sizeof(card->id));
@@ -1815,7 +1828,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
else
emu->gpr_base = FXGPREGBASE;
- if ((err = pci_request_regions(pci, "EMU10K1")) < 0) {
+ err = pci_request_regions(pci, "EMU10K1");
+ if (err < 0) {
kfree(emu);
pci_disable_device(pci);
return err;
@@ -1862,21 +1876,25 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->enable_ir = enable_ir;
if (emu->card_capabilities->ca_cardbus_chip) {
- if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+ err = snd_emu10k1_cardbus_init(emu);
+ if (err < 0)
goto error;
}
if (emu->card_capabilities->ecard) {
- if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+ err = snd_emu10k1_ecard_init(emu);
+ if (err < 0)
goto error;
} else if (emu->card_capabilities->emu_model) {
- if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
- snd_emu10k1_free(emu);
- return err;
- }
+ err = snd_emu10k1_emu1010_init(emu);
+ if (err < 0) {
+ snd_emu10k1_free(emu);
+ return err;
+ }
} else {
/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
does not support this, it shouldn't do any harm */
- snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+ snd_emu10k1_ptr_write(emu, AC97SLOT, 0,
+ AC97SLOT_CNTR|AC97SLOT_LFE);
}
/* initialize TRAM setup */
@@ -1916,7 +1934,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
snd_emu10k1_synth_alloc(emu, 4096);
if (emu->reserved_page)
emu->reserved_page->map_locked = 1;
-
+
/* Clear silent pages and set up pointers */
memset(emu->silent_page.area, 0, PAGE_SIZE);
silent_page = emu->silent_page.addr << 1;
@@ -1929,19 +1947,23 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->voices[idx].number = idx;
}
- if ((err = snd_emu10k1_init(emu, enable_ir, 0)) < 0)
+ err = snd_emu10k1_init(emu, enable_ir, 0);
+ if (err < 0)
goto error;
#ifdef CONFIG_PM
- if ((err = alloc_pm_buffer(emu)) < 0)
+ err = alloc_pm_buffer(emu);
+ if (err < 0)
goto error;
#endif
/* Initialize the effect engine */
- if ((err = snd_emu10k1_init_efx(emu)) < 0)
+ err = snd_emu10k1_init_efx(emu);
+ if (err < 0)
goto error;
snd_emu10k1_audio_enable(emu);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops);
+ if (err < 0)
goto error;
#ifdef CONFIG_PROC_FS
@@ -1981,7 +2003,7 @@ static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
if (emu->audigy)
size += ARRAY_SIZE(saved_regs_audigy);
emu->saved_ptr = vmalloc(4 * NUM_G * size);
- if (! emu->saved_ptr)
+ if (!emu->saved_ptr)
return -ENOMEM;
if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
return -ENOMEM;
@@ -2026,7 +2048,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
if (emu->card_capabilities->ecard)
snd_emu10k1_ecard_init(emu);
else if (emu->card_capabilities->emu_model)
- snd_emu10k1_emu1010_init(emu);
+ snd_emu10k1_emu1010_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
snd_emu10k1_init(emu, emu->enable_ir, 1);
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index f34bbfb705f5..b0fb6c917c38 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1639,6 +1639,45 @@ static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
.put = snd_emu10k1_shared_spdif_put
};
+/* workaround for too low volume on Audigy due to 16bit/24bit conversion */
+
+#define snd_audigy_capture_boost_info snd_ctl_boolean_mono_info
+
+static int snd_audigy_capture_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ /* FIXME: better to use a cached version */
+ val = snd_ac97_read(emu->ac97, AC97_REC_GAIN);
+ ucontrol->value.integer.value[0] = !!val;
+ return 0;
+}
+
+static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+
+ if (ucontrol->value.integer.value[0])
+ val = 0x0f0f;
+ else
+ val = 0;
+ return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
+}
+
+static struct snd_kcontrol_new snd_audigy_capture_boost __devinitdata =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Capture Boost",
+ .info = snd_audigy_capture_boost_info,
+ .get = snd_audigy_capture_boost_get,
+ .put = snd_audigy_capture_boost_put
+};
+
+
/*
*/
static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
@@ -2087,5 +2126,12 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
}
}
+ if (emu->card_capabilities->ac97_chip && emu->audigy) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_capture_boost,
+ emu));
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 20ee7599600b..e9c3794bbcb8 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -1953,7 +1953,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
if (event & ESM_HWVOL_IRQ)
- tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+ tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
/* else ack 'em all, i imagine */
outb(0xFF, chip->io_port + 0x1A);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
new file mode 100644
index 000000000000..eb2a19b894a0
--- /dev/null
+++ b/sound/pci/hda/Kconfig
@@ -0,0 +1,188 @@
+menuconfig SND_HDA_INTEL
+ tristate "Intel HD Audio"
+ select SND_PCM
+ select SND_VMASTER
+ select SND_JACK if INPUT=y || INPUT=SND
+ help
+ Say Y here to include support for Intel "High Definition
+ Audio" (Azalia) and its compatible devices.
+
+ This option enables the HD-audio controller. Don't forget
+ to choose the appropriate codec options below.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-intel.
+
+if SND_HDA_INTEL
+
+config SND_HDA_HWDEP
+ bool "Build hwdep interface for HD-audio driver"
+ select SND_HWDEP
+ help
+ Say Y here to build a hwdep interface for HD-audio driver.
+ This interface can be used for out-of-band communication
+ with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+ bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
+ depends on SND_HDA_HWDEP && EXPERIMENTAL
+ help
+ Say Y here to enable the HD-audio codec re-configuration feature.
+ This adds the sysfs interfaces to allow user to clear the whole
+ codec configuration, change the codec setup, add extra verbs,
+ and re-configure the codec dynamically.
+
+config SND_HDA_INPUT_BEEP
+ bool "Support digital beep via input layer"
+ depends on INPUT=y || INPUT=SND_HDA_INTEL
+ help
+ Say Y here to build a digital beep interface for HD-audio
+ driver. This interface is used to generate digital beeps.
+
+config SND_HDA_CODEC_REALTEK
+ bool "Build Realtek HD-audio codec support"
+ default y
+ help
+ Say Y here to include Realtek HD-audio codec support in
+ snd-hda-intel driver, such as ALC880.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-realtek.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ANALOG
+ bool "Build Analog Device HD-audio codec support"
+ default y
+ help
+ Say Y here to include Analog Device HD-audio codec support in
+ snd-hda-intel driver, such as AD1986A.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-analog.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SIGMATEL
+ bool "Build IDT/Sigmatel HD-audio codec support"
+ default y
+ help
+ Say Y here to include IDT (Sigmatel) HD-audio codec support in
+ snd-hda-intel driver, such as STAC9200.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-idt.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_VIA
+ bool "Build VIA HD-audio codec support"
+ default y
+ help
+ Say Y here to include VIA HD-audio codec support in
+ snd-hda-intel driver, such as VT1708.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-via.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ATIHDMI
+ bool "Build ATI HDMI HD-audio codec support"
+ default y
+ help
+ Say Y here to include ATI HDMI HD-audio codec support in
+ snd-hda-intel driver, such as ATI RS600 HDMI.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-atihdmi.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_NVHDMI
+ bool "Build NVIDIA HDMI HD-audio codec support"
+ default y
+ help
+ Say Y here to include NVIDIA HDMI HD-audio codec support in
+ snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-nvhdmi.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_INTELHDMI
+ bool "Build INTEL HDMI HD-audio codec support"
+ default y
+ help
+ Say Y here to include INTEL HDMI HD-audio codec support in
+ snd-hda-intel driver, such as Eaglelake integrated HDMI.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-intelhdmi.
+ This module is automatically loaded at probing.
+
+config SND_HDA_ELD
+ def_bool y
+ depends on SND_HDA_CODEC_INTELHDMI
+
+config SND_HDA_CODEC_CONEXANT
+ bool "Build Conexant HD-audio codec support"
+ default y
+ help
+ Say Y here to include Conexant HD-audio codec support in
+ snd-hda-intel driver, such as CX20549.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-conexant.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_CMEDIA
+ bool "Build C-Media HD-audio codec support"
+ default y
+ help
+ Say Y here to include C-Media HD-audio codec support in
+ snd-hda-intel driver, such as CMI9880.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-cmedia.
+ This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SI3054
+ bool "Build Silicon Labs 3054 HD-modem codec support"
+ default y
+ help
+ Say Y here to include Silicon Labs 3054 HD-modem codec
+ (and compatibles) support in snd-hda-intel driver.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-si3054.
+ This module is automatically loaded at probing.
+
+config SND_HDA_GENERIC
+ bool "Enable generic HD-audio codec parser"
+ default y
+ help
+ Say Y here to enable the generic HD-audio codec parser
+ in snd-hda-intel driver.
+
+config SND_HDA_POWER_SAVE
+ bool "Aggressive power-saving on HD-audio"
+ help
+ Say Y here to enable more aggressive power-saving mode on
+ HD-audio driver. The power-saving timeout can be configured
+ via power_save option or over sysfs on-the-fly.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+ int "Default time-out for HD-audio power-save mode"
+ depends on SND_HDA_POWER_SAVE
+ default 0
+ help
+ The default time-out value in seconds for HD-audio automatic
+ power-save mode. 0 means to disable the power-save mode.
+
+endif
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 1980c6d207e7..50f9d0967251 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -1,20 +1,59 @@
-snd-hda-intel-y := hda_intel.o
-# since snd-hda-intel is the only driver using hda-codec,
-# merge it into a single module although it was originally
-# designed to be individual modules
-snd-hda-intel-y += hda_codec.o
-snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
-snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
-snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
+snd-hda-intel-objs := hda_intel.o
+snd-hda-codec-y := hda_codec.o
+snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
+snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
+# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+
+snd-hda-codec-realtek-objs := patch_realtek.o
+snd-hda-codec-cmedia-objs := patch_cmedia.o
+snd-hda-codec-analog-objs := patch_analog.o
+snd-hda-codec-idt-objs := patch_sigmatel.o
+snd-hda-codec-si3054-objs := patch_si3054.o
+snd-hda-codec-atihdmi-objs := patch_atihdmi.o
+snd-hda-codec-conexant-objs := patch_conexant.o
+snd-hda-codec-via-objs := patch_via.o
+snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
+snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o hda_eld.o
+
+# common driver
+obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
+
+# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
+ifdef CONFIG_SND_HDA_CODEC_REALTEK
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CMEDIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ANALOG
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SI3054
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CONEXANT
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_VIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+endif
+
+# this must be the last entry after codec drivers;
+# otherwise the codec patches won't be hooked before the PCI probe
+# when built in kernel
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 3ecd7e797dee..e00421c0d8ba 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -128,6 +128,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
void snd_hda_detach_beep_device(struct hda_codec *codec)
{
@@ -140,3 +141,4 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
kfree(beep);
}
}
+EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index eb9164176dab..e16cf63821ae 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -31,15 +31,6 @@
#include <sound/initval.h>
#include "hda_local.h"
#include <sound/hda_hwdep.h>
-#include "hda_patch.h" /* codec presets */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* define this option here to hide as static */
-static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
-MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
- "(in second, 0 = disable).");
-#endif
/*
* vendor / preset table
@@ -55,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1002, "ATI" },
{ 0x1057, "Motorola" },
{ 0x1095, "Silicon Image" },
+ { 0x10de, "Nvidia" },
{ 0x10ec, "Realtek" },
{ 0x1106, "VIA" },
{ 0x111d, "IDT" },
@@ -66,40 +58,31 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x1854, "LG" },
{ 0x1aec, "Wolfson Microelectronics" },
{ 0x434d, "C-Media" },
+ { 0x8086, "Intel" },
{ 0x8384, "SigmaTel" },
{} /* terminator */
};
-static const struct hda_codec_preset *hda_preset_tables[] = {
-#ifdef CONFIG_SND_HDA_CODEC_REALTEK
- snd_hda_preset_realtek,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
- snd_hda_preset_cmedia,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ANALOG
- snd_hda_preset_analog,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
- snd_hda_preset_sigmatel,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SI3054
- snd_hda_preset_si3054,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
- snd_hda_preset_atihdmi,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
- snd_hda_preset_conexant,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_VIA
- snd_hda_preset_via,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
- snd_hda_preset_nvhdmi,
-#endif
- NULL
-};
+static DEFINE_MUTEX(preset_mutex);
+static LIST_HEAD(hda_preset_tables);
+
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
+{
+ mutex_lock(&preset_mutex);
+ list_add_tail(&preset->list, &hda_preset_tables);
+ mutex_unlock(&preset_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
+
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
+{
+ mutex_lock(&preset_mutex);
+ list_del(&preset->list);
+ mutex_unlock(&preset_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
#ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_power_work(struct work_struct *work);
@@ -108,6 +91,72 @@ static void hda_keep_power_on(struct hda_codec *codec);
static inline void hda_keep_power_on(struct hda_codec *codec) {}
#endif
+const char *snd_hda_get_jack_location(u32 cfg)
+{
+ static char *bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static char *specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+
+const char *snd_hda_get_jack_connectivity(u32 cfg)
+{
+ static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+
+ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+
+const char *snd_hda_get_jack_type(u32 cfg)
+{
+ static char *jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digitial In", "Reserved", "Other"
+ };
+
+ return jack_types[(cfg & AC_DEFCFG_DEVICE)
+ >> AC_DEFCFG_DEVICE_SHIFT];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
+
+/*
+ * Compose a 32bit command word to be sent to the HD-audio controller
+ */
+static inline unsigned int
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+ unsigned int verb, unsigned int parm)
+{
+ u32 val;
+
+ val = (u32)(codec->addr & 0x0f) << 28;
+ val |= (u32)direct << 27;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= parm;
+ return val;
+}
+
/**
* snd_hda_codec_read - send a command and get the response
* @codec: the HDA codec
@@ -124,17 +173,21 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int direct,
unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
unsigned int res;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
- res = codec->bus->ops.get_response(codec);
+ mutex_lock(&bus->cmd_mutex);
+ if (!bus->ops.command(bus, res))
+ res = bus->ops.get_response(bus);
else
res = (unsigned int)-1;
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return res;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_read);
/**
* snd_hda_codec_write - send a single command without waiting for response
@@ -151,14 +204,19 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
+ unsigned int res;
int err;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- err = codec->bus->ops.command(codec, nid, direct, verb, parm);
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_lock(&bus->cmd_mutex);
+ err = bus->ops.command(bus, res);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_write);
/**
* snd_hda_sequence_write - sequence writes
@@ -173,6 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
for (; seq->nid; seq++)
snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
}
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
/**
* snd_hda_get_sub_nodes - get the range of sub nodes
@@ -194,6 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
*start_id = (parm >> 16) & 0x7fff;
return (int)(parm & 0x7fff);
}
+EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
/**
* snd_hda_get_connections - get connection list
@@ -282,6 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
}
return conns;
}
+EXPORT_SYMBOL_HDA(snd_hda_get_connections);
/**
@@ -316,6 +377,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
/*
* process queued unsolicited events
@@ -345,7 +407,7 @@ static void process_unsol_events(struct work_struct *work)
/*
* initialize unsolicited queue
*/
-static int __devinit init_unsol_queue(struct hda_bus *bus)
+static int init_unsol_queue(struct hda_bus *bus)
{
struct hda_bus_unsolicited *unsol;
@@ -391,9 +453,24 @@ static int snd_hda_bus_free(struct hda_bus *bus)
static int snd_hda_bus_dev_free(struct snd_device *device)
{
struct hda_bus *bus = device->device_data;
+ bus->shutdown = 1;
return snd_hda_bus_free(bus);
}
+#ifdef CONFIG_SND_HDA_HWDEP
+static int snd_hda_bus_dev_register(struct snd_device *device)
+{
+ struct hda_bus *bus = device->device_data;
+ struct hda_codec *codec;
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ snd_hda_hwdep_add_sysfs(codec);
+ }
+ return 0;
+}
+#else
+#define snd_hda_bus_dev_register NULL
+#endif
+
/**
* snd_hda_bus_new - create a HDA bus
* @card: the card entry
@@ -402,13 +479,14 @@ static int snd_hda_bus_dev_free(struct snd_device *device)
*
* Returns 0 if successful, or a negative error code.
*/
-int __devinit snd_hda_bus_new(struct snd_card *card,
+int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
const struct hda_bus_template *temp,
struct hda_bus **busp)
{
struct hda_bus *bus;
int err;
static struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_bus_dev_register,
.dev_free = snd_hda_bus_dev_free,
};
@@ -430,6 +508,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
bus->private_data = temp->private_data;
bus->pci = temp->pci;
bus->modelname = temp->modelname;
+ bus->power_save = temp->power_save;
bus->ops = temp->ops;
mutex_init(&bus->cmd_mutex);
@@ -444,27 +523,42 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
*busp = bus;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_bus_new);
#ifdef CONFIG_SND_HDA_GENERIC
#define is_generic_config(codec) \
- (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
#else
#define is_generic_config(codec) 0
#endif
+#ifdef MODULE
+#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */
+#else
+#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */
+#endif
+
/*
* find a matching codec preset
*/
-static const struct hda_codec_preset __devinit *
+static const struct hda_codec_preset *
find_codec_preset(struct hda_codec *codec)
{
- const struct hda_codec_preset **tbl, *preset;
+ struct hda_codec_preset_list *tbl;
+ const struct hda_codec_preset *preset;
+ int mod_requested = 0;
if (is_generic_config(codec))
return NULL; /* use the generic parser */
- for (tbl = hda_preset_tables; *tbl; tbl++) {
- for (preset = *tbl; preset->id; preset++) {
+ again:
+ mutex_lock(&preset_mutex);
+ list_for_each_entry(tbl, &hda_preset_tables, list) {
+ if (!try_module_get(tbl->owner)) {
+ snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
+ continue;
+ }
+ for (preset = tbl->preset; preset->id; preset++) {
u32 mask = preset->mask;
if (preset->afg && preset->afg != codec->afg)
continue;
@@ -474,23 +568,40 @@ find_codec_preset(struct hda_codec *codec)
mask = ~0;
if (preset->id == (codec->vendor_id & mask) &&
(!preset->rev ||
- preset->rev == codec->revision_id))
+ preset->rev == codec->revision_id)) {
+ mutex_unlock(&preset_mutex);
+ codec->owner = tbl->owner;
return preset;
+ }
}
+ module_put(tbl->owner);
+ }
+ mutex_unlock(&preset_mutex);
+
+ if (mod_requested < HDA_MODREQ_MAX_COUNT) {
+ char name[32];
+ if (!mod_requested)
+ snprintf(name, sizeof(name), "snd-hda-codec-id:%08x",
+ codec->vendor_id);
+ else
+ snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*",
+ (codec->vendor_id >> 16) & 0xffff);
+ request_module(name);
+ mod_requested++;
+ goto again;
}
return NULL;
}
/*
- * snd_hda_get_codec_name - store the codec name
+ * get_codec_name - store the codec name
*/
-void snd_hda_get_codec_name(struct hda_codec *codec,
- char *name, int namelen)
+static int get_codec_name(struct hda_codec *codec)
{
const struct hda_vendor_id *c;
const char *vendor = NULL;
u16 vendor_id = codec->vendor_id >> 16;
- char tmp[16];
+ char tmp[16], name[32];
for (c = hda_vendor_ids; c->id; c++) {
if (c->id == vendor_id) {
@@ -503,16 +614,21 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
vendor = tmp;
}
if (codec->preset && codec->preset->name)
- snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+ snprintf(name, sizeof(name), "%s %s", vendor,
+ codec->preset->name);
else
- snprintf(name, namelen, "%s ID %x", vendor,
+ snprintf(name, sizeof(name), "%s ID %x", vendor,
codec->vendor_id & 0xffff);
+ codec->name = kstrdup(name, GFP_KERNEL);
+ if (!codec->name)
+ return -ENOMEM;
+ return 0;
}
/*
* look for an AFG and MFG nodes
*/
-static void __devinit setup_fg_nodes(struct hda_codec *codec)
+static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
{
int i, total_nodes;
hda_nid_t nid;
@@ -571,11 +687,15 @@ static void snd_hda_codec_free(struct hda_codec *codec)
flush_scheduled_work();
#endif
list_del(&codec->list);
+ snd_array_free(&codec->mixers);
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ module_put(codec->owner);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
+ kfree(codec->name);
+ kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
}
@@ -588,8 +708,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
*
* Returns 0 if successful, or a negative error code.
*/
-int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- struct hda_codec **codecp)
+int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+ int do_init, struct hda_codec **codecp)
{
struct hda_codec *codec;
char component[31];
@@ -617,6 +737,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
mutex_init(&codec->spdif_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+ snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname) {
+ snd_hda_codec_free(codec);
+ return -ENODEV;
+ }
+ }
#ifdef CONFIG_SND_HDA_POWER_SAVE
INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -662,12 +790,44 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
+ if (bus->modelname)
+ codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
+
+ if (do_init) {
+ err = snd_hda_codec_configure(codec);
+ if (err < 0) {
+ snd_hda_codec_free(codec);
+ return err;
+ }
+ }
+ snd_hda_codec_proc_new(codec);
+
+ snd_hda_create_hwdep(codec);
+
+ sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
+ codec->subsystem_id, codec->revision_id);
+ snd_component_add(codec->bus->card, component);
+
+ if (codecp)
+ *codecp = codec;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+ int err;
codec->preset = find_codec_preset(codec);
+ if (!codec->name) {
+ err = get_codec_name(codec);
+ if (err < 0)
+ return err;
+ }
/* audio codec should override the mixer name */
- if (codec->afg || !*bus->card->mixername)
- snd_hda_get_codec_name(codec, bus->card->mixername,
- sizeof(bus->card->mixername));
+ if (codec->afg || !*codec->bus->card->mixername)
+ strlcpy(codec->bus->card->mixername, codec->name,
+ sizeof(codec->bus->card->mixername));
if (is_generic_config(codec)) {
err = snd_hda_parse_generic_codec(codec);
@@ -684,25 +844,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
printk(KERN_ERR "hda-codec: No codec parser is available\n");
patched:
- if (err < 0) {
- snd_hda_codec_free(codec);
- return err;
- }
-
- if (codec->patch_ops.unsol_event)
- init_unsol_queue(bus);
-
- snd_hda_codec_proc_new(codec);
-#ifdef CONFIG_SND_HDA_HWDEP
- snd_hda_create_hwdep(codec);
-#endif
-
- sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
- snd_component_add(codec->bus->card, component);
-
- if (codecp)
- *codecp = codec;
- return 0;
+ if (!err && codec->patch_ops.unsol_event)
+ err = init_unsol_queue(codec->bus);
+ return err;
}
/**
@@ -728,6 +872,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
msleep(1);
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
{
@@ -741,6 +886,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
#endif
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
/*
* amp access functions
@@ -752,17 +898,17 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
/* initialize the hash table */
-static void __devinit init_hda_cache(struct hda_cache_rec *cache,
+static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size)
{
memset(cache, 0, sizeof(*cache));
memset(cache->hash, 0xff, sizeof(cache->hash));
- cache->record_size = record_size;
+ snd_array_init(&cache->buf, record_size, 64);
}
static void free_hda_cache(struct hda_cache_rec *cache)
{
- kfree(cache->buffer);
+ snd_array_free(&cache->buf);
}
/* query the hash. allocate an entry if not found. */
@@ -774,35 +920,17 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
struct hda_cache_head *info;
while (cur != 0xffff) {
- info = (struct hda_cache_head *)(cache->buffer +
- cur * cache->record_size);
+ info = snd_array_elem(&cache->buf, cur);
if (info->key == key)
return info;
cur = info->next;
}
/* add a new hash entry */
- if (cache->num_entries >= cache->size) {
- /* reallocate the array */
- unsigned int new_size = cache->size + 64;
- void *new_buffer;
- new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
- if (!new_buffer) {
- snd_printk(KERN_ERR "hda_codec: "
- "can't malloc amp_info\n");
- return NULL;
- }
- if (cache->buffer) {
- memcpy(new_buffer, cache->buffer,
- cache->size * cache->record_size);
- kfree(cache->buffer);
- }
- cache->size = new_size;
- cache->buffer = new_buffer;
- }
- cur = cache->num_entries++;
- info = (struct hda_cache_head *)(cache->buffer +
- cur * cache->record_size);
+ info = snd_array_new(&cache->buf);
+ if (!info)
+ return NULL;
+ cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
info->next = cache->hash[idx];
@@ -840,6 +968,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
}
return info->amp_caps;
}
+EXPORT_SYMBOL_HDA(query_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
@@ -853,6 +982,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
info->head.val |= INFO_AMP_CAPS;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
/*
* read the current volume to info
@@ -906,6 +1036,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
return 0;
return get_vol_mute(codec, info, nid, ch, direction, index);
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
/*
* update the AMP value, mask = bit mask to set, val = the value
@@ -925,6 +1056,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
put_vol_mute(codec, info, nid, ch, direction, idx, val);
return 1;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
/*
* update the AMP stereo with the same mask and value
@@ -938,15 +1070,16 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
idx, mask, val);
return ret;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
#ifdef SND_HDA_NEEDS_RESUME
/* resume the all amp commands from the cache */
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
- struct hda_amp_info *buffer = codec->amp_cache.buffer;
+ struct hda_amp_info *buffer = codec->amp_cache.buf.list;
int i;
- for (i = 0; i < codec->amp_cache.size; i++, buffer++) {
+ for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
u32 key = buffer->head.key;
hda_nid_t nid;
unsigned int idx, dir, ch;
@@ -963,6 +1096,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
}
}
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
#endif /* SND_HDA_NEEDS_RESUME */
/* volume */
@@ -990,6 +1124,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = caps;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1009,6 +1144,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
& HDA_AMP_VOLMASK;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1033,6 +1169,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
snd_hda_power_down(codec);
return change;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *_tlv)
@@ -1059,6 +1196,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
/*
* set (static) TLV for virtual master volume; recalculated as max 0dB
@@ -1078,6 +1216,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
tlv[2] = -nums * step;
tlv[3] = step;
}
+EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
/* find a mixer control element with the given name */
static struct snd_kcontrol *
@@ -1097,6 +1236,69 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
{
return _snd_hda_find_mixer_ctl(codec, name, 0);
}
+EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
+
+/* Add a control element and assign to the codec */
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+{
+ int err;
+ struct snd_kcontrol **knewp;
+
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0)
+ return err;
+ knewp = snd_array_new(&codec->mixers);
+ if (!knewp)
+ return -ENOMEM;
+ *knewp = kctl;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+/* Clear all controls assigned to the given codec */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+ int i;
+ struct snd_kcontrol **kctls = codec->mixers.list;
+ for (i = 0; i < codec->mixers.used; i++)
+ snd_ctl_remove(codec->bus->card, kctls[i]);
+ snd_array_free(&codec->mixers);
+}
+
+void snd_hda_codec_reset(struct hda_codec *codec)
+{
+ int i;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ cancel_delayed_work(&codec->power_work);
+ flush_scheduled_work();
+#endif
+ snd_hda_ctls_clear(codec);
+ /* relase PCMs */
+ for (i = 0; i < codec->num_pcms; i++) {
+ if (codec->pcm_info[i].pcm) {
+ snd_device_free(codec->bus->card,
+ codec->pcm_info[i].pcm);
+ clear_bit(codec->pcm_info[i].device,
+ codec->bus->pcm_dev_bits);
+ }
+ }
+ if (codec->patch_ops.free)
+ codec->patch_ops.free(codec);
+ codec->proc_widget_hook = NULL;
+ codec->spec = NULL;
+ free_hda_cache(&codec->amp_cache);
+ free_hda_cache(&codec->cmd_cache);
+ init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
+ init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+ codec->num_pcms = 0;
+ codec->pcm_info = NULL;
+ codec->preset = NULL;
+ module_put(codec->owner);
+ codec->owner = NULL;
+}
+#endif /* CONFIG_SND_HDA_RECONFIG */
/* create a virtual master control and add slaves */
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -1115,7 +1317,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
kctl = snd_ctl_make_virtual_master(name, tlv);
if (!kctl)
return -ENOMEM;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
@@ -1133,6 +1335,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
/* switch */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
@@ -1146,6 +1349,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1165,6 +1369,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
HDA_AMP_MUTE) ? 0 : 1;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1195,6 +1400,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
snd_hda_power_down(codec);
return change;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
/*
* bound volume controls
@@ -1220,6 +1426,7 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->spdif_mutex);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1243,6 +1450,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->spdif_mutex);
return err < 0 ? err : change;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
/*
* generic bound volume/swtich controls
@@ -1262,6 +1470,7 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->spdif_mutex);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1278,6 +1487,7 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->spdif_mutex);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1300,6 +1510,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
mutex_unlock(&codec->spdif_mutex);
return err < 0 ? err : change;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
@@ -1316,6 +1527,7 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
mutex_unlock(&codec->spdif_mutex);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
struct hda_ctl_ops snd_hda_bind_vol = {
.info = snd_hda_mixer_amp_volume_info,
@@ -1323,6 +1535,7 @@ struct hda_ctl_ops snd_hda_bind_vol = {
.put = snd_hda_mixer_amp_volume_put,
.tlv = snd_hda_mixer_amp_tlv
};
+EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
struct hda_ctl_ops snd_hda_bind_sw = {
.info = snd_hda_mixer_amp_switch_info,
@@ -1330,6 +1543,7 @@ struct hda_ctl_ops snd_hda_bind_sw = {
.put = snd_hda_mixer_amp_switch_put,
.tlv = snd_hda_mixer_amp_tlv
};
+EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
/*
* SPDIF out controls
@@ -1577,9 +1791,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
}
for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
+ if (!kctl)
+ return -ENOMEM;
kctl->id.index = idx;
kctl->private_value = nid;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
@@ -1589,6 +1805,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
/*
* SPDIF sharing with analog output
@@ -1623,9 +1840,10 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
if (!mout->dig_out_nid)
return 0;
/* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_ctl_add(codec->bus->card,
+ return snd_hda_ctl_add(codec,
snd_ctl_new1(&spdif_share_sw, mout));
}
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
/*
* SPDIF input
@@ -1725,7 +1943,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
kctl = snd_ctl_new1(dig_mix, codec);
kctl->private_value = nid;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
@@ -1735,6 +1953,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
AC_DIG1_ENABLE;
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
#ifdef SND_HDA_NEEDS_RESUME
/*
@@ -1761,10 +1980,14 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
+ struct hda_bus *bus = codec->bus;
+ unsigned int res;
int err;
+
+ res = make_codec_cmd(codec, nid, direct, verb, parm);
snd_hda_power_up(codec);
- mutex_lock(&codec->bus->cmd_mutex);
- err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+ mutex_lock(&bus->cmd_mutex);
+ err = bus->ops.command(bus, res);
if (!err) {
struct hda_cache_head *c;
u32 key = build_cmd_cache_key(nid, verb);
@@ -1772,18 +1995,19 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
if (c)
c->val = parm;
}
- mutex_unlock(&codec->bus->cmd_mutex);
+ mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
return err;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
/* resume the all commands from the cache */
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
- struct hda_cache_head *buffer = codec->cmd_cache.buffer;
+ struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
int i;
- for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {
+ for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
u32 key = buffer->key;
if (!key)
continue;
@@ -1791,6 +2015,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec)
get_cmd_cache_cmd(key), buffer->val);
}
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
/**
* snd_hda_sequence_write_cache - sequence writes with caching
@@ -1808,6 +2033,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
seq->param);
}
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
#endif /* SND_HDA_NEEDS_RESUME */
/*
@@ -1868,6 +2094,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
}
}
+#ifdef CONFIG_SND_HDA_HWDEP
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+ if (codec->init_verbs.list)
+ snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
#ifdef SND_HDA_NEEDS_RESUME
/*
* call suspend and power-down; used both from PM and power-save
@@ -1894,6 +2131,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
AC_PWRST_D0);
+ hda_exec_init_verbs(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
@@ -1914,28 +2152,37 @@ static void hda_call_codec_resume(struct hda_codec *codec)
*
* Returns 0 if successful, otherwise a negative error code.
*/
-int __devinit snd_hda_build_controls(struct hda_bus *bus)
+int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
{
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
- int err = 0;
- /* fake as if already powered-on */
- hda_keep_power_on(codec);
- /* then fire up */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
- /* continue to initialize... */
- if (codec->patch_ops.init)
- err = codec->patch_ops.init(codec);
- if (!err && codec->patch_ops.build_controls)
- err = codec->patch_ops.build_controls(codec);
- snd_hda_power_down(codec);
+ int err = snd_hda_codec_build_controls(codec);
if (err < 0)
return err;
}
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_build_controls);
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+ int err = 0;
+ /* fake as if already powered-on */
+ hda_keep_power_on(codec);
+ /* then fire up */
+ hda_set_power_state(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_D0);
+ hda_exec_init_verbs(codec);
+ /* continue to initialize... */
+ if (codec->patch_ops.init)
+ err = codec->patch_ops.init(codec);
+ if (!err && codec->patch_ops.build_controls)
+ err = codec->patch_ops.build_controls(codec);
+ snd_hda_power_down(codec);
+ if (err < 0)
+ return err;
return 0;
}
@@ -2028,6 +2275,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
return val;
}
+EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
@@ -2042,7 +2290,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
*
* Returns 0 if successful, otherwise a negative error code.
*/
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
{
int i;
@@ -2207,6 +2455,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
return 1;
}
+EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
/*
* PCM stuff
@@ -2236,8 +2485,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
return 0;
}
-static int __devinit set_pcm_default_values(struct hda_codec *codec,
- struct hda_pcm_stream *info)
+static int set_pcm_default_values(struct hda_codec *codec,
+ struct hda_pcm_stream *info)
{
/* query support PCM information from the given NID */
if (info->nid && (!info->rates || !info->formats)) {
@@ -2263,6 +2512,110 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
return 0;
}
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, int type)
+{
+ static const char *dev_name[HDA_PCM_NTYPES] = {
+ "Audio", "SPDIF", "HDMI", "Modem"
+ };
+ /* starting device index for each PCM type */
+ static int dev_idx[HDA_PCM_NTYPES] = {
+ [HDA_PCM_TYPE_AUDIO] = 0,
+ [HDA_PCM_TYPE_SPDIF] = 1,
+ [HDA_PCM_TYPE_HDMI] = 3,
+ [HDA_PCM_TYPE_MODEM] = 6
+ };
+ /* normal audio device indices; not linear to keep compatibility */
+ static int audio_idx[4] = { 0, 2, 4, 5 };
+ int i, dev;
+
+ switch (type) {
+ case HDA_PCM_TYPE_AUDIO:
+ for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
+ dev = audio_idx[i];
+ if (!test_bit(dev, bus->pcm_dev_bits))
+ break;
+ }
+ if (i >= ARRAY_SIZE(audio_idx)) {
+ snd_printk(KERN_WARNING "Too many audio devices\n");
+ return -EAGAIN;
+ }
+ break;
+ case HDA_PCM_TYPE_SPDIF:
+ case HDA_PCM_TYPE_HDMI:
+ case HDA_PCM_TYPE_MODEM:
+ dev = dev_idx[type];
+ if (test_bit(dev, bus->pcm_dev_bits)) {
+ snd_printk(KERN_WARNING "%s already defined\n",
+ dev_name[type]);
+ return -EAGAIN;
+ }
+ break;
+ default:
+ snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+ return -EINVAL;
+ }
+ set_bit(dev, bus->pcm_dev_bits);
+ return dev;
+}
+
+/*
+ * attach a new PCM stream
+ */
+static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+{
+ struct hda_bus *bus = codec->bus;
+ struct hda_pcm_stream *info;
+ int stream, err;
+
+ if (snd_BUG_ON(!pcm->name))
+ return -EINVAL;
+ for (stream = 0; stream < 2; stream++) {
+ info = &pcm->stream[stream];
+ if (info->substreams) {
+ err = set_pcm_default_values(codec, info);
+ if (err < 0)
+ return err;
+ }
+ }
+ return bus->ops.attach_pcm(bus, codec, pcm);
+}
+
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+ unsigned int pcm;
+ int err;
+
+ if (!codec->num_pcms) {
+ if (!codec->patch_ops.build_pcms)
+ return 0;
+ err = codec->patch_ops.build_pcms(codec);
+ if (err < 0)
+ return err;
+ }
+ for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+ struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+ int dev;
+
+ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+ return 0; /* no substreams assigned */
+
+ if (!cpcm->pcm) {
+ dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
+ if (dev < 0)
+ return 0;
+ cpcm->device = dev;
+ err = snd_hda_attach_pcm(codec, cpcm);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
/**
* snd_hda_build_pcms - build PCM information
* @bus: the BUS
@@ -2294,27 +2647,13 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
- unsigned int pcm, s;
- int err;
- if (!codec->patch_ops.build_pcms)
- continue;
- err = codec->patch_ops.build_pcms(codec);
+ int err = snd_hda_codec_build_pcms(codec);
if (err < 0)
return err;
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- for (s = 0; s < 2; s++) {
- struct hda_pcm_stream *info;
- info = &codec->pcm_info[pcm].stream[s];
- if (!info->substreams)
- continue;
- err = set_pcm_default_values(codec, info);
- if (err < 0)
- return err;
- }
- }
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
/**
* snd_hda_check_board_config - compare the current codec with the config table
@@ -2333,11 +2672,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
int num_configs, const char **models,
const struct snd_pci_quirk *tbl)
{
- if (codec->bus->modelname && models) {
+ if (codec->modelname && models) {
int i;
for (i = 0; i < num_configs; i++) {
if (models[i] &&
- !strcmp(codec->bus->modelname, models[i])) {
+ !strcmp(codec->modelname, models[i])) {
snd_printd(KERN_INFO "hda_codec: model '%s' is "
"selected\n", models[i]);
return i;
@@ -2370,6 +2709,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
}
return -1;
}
+EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
/**
* snd_hda_add_new_ctls - create controls from the array
@@ -2390,7 +2730,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0) {
if (!codec->addr)
return err;
@@ -2398,13 +2738,14 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
if (!kctl)
return -ENOMEM;
kctl->id.device = codec->addr;
- err = snd_ctl_add(codec->bus->card, kctl);
+ err = snd_hda_ctl_add(codec, kctl);
if (err < 0)
return err;
}
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
#ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
@@ -2414,6 +2755,7 @@ static void hda_power_work(struct work_struct *work)
{
struct hda_codec *codec =
container_of(work, struct hda_codec, power_work.work);
+ struct hda_bus *bus = codec->bus;
if (!codec->power_on || codec->power_count) {
codec->power_transition = 0;
@@ -2421,8 +2763,8 @@ static void hda_power_work(struct work_struct *work)
}
hda_call_codec_suspend(codec);
- if (codec->bus->ops.pm_notify)
- codec->bus->ops.pm_notify(codec);
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus);
}
static void hda_keep_power_on(struct hda_codec *codec)
@@ -2433,29 +2775,39 @@ static void hda_keep_power_on(struct hda_codec *codec)
void snd_hda_power_up(struct hda_codec *codec)
{
+ struct hda_bus *bus = codec->bus;
+
codec->power_count++;
if (codec->power_on || codec->power_transition)
return;
codec->power_on = 1;
- if (codec->bus->ops.pm_notify)
- codec->bus->ops.pm_notify(codec);
+ if (bus->ops.pm_notify)
+ bus->ops.pm_notify(bus);
hda_call_codec_resume(codec);
cancel_delayed_work(&codec->power_work);
codec->power_transition = 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_power_up);
+
+#define power_save(codec) \
+ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
+
+#define power_save(codec) \
+ ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
void snd_hda_power_down(struct hda_codec *codec)
{
--codec->power_count;
if (!codec->power_on || codec->power_count || codec->power_transition)
return;
- if (power_save) {
+ if (power_save(codec)) {
codec->power_transition = 1; /* avoid reentrance */
schedule_delayed_work(&codec->power_work,
- msecs_to_jiffies(power_save * 1000));
+ msecs_to_jiffies(power_save(codec) * 1000));
}
}
+EXPORT_SYMBOL_HDA(snd_hda_power_down);
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
@@ -2492,6 +2844,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
#endif
/*
@@ -2511,6 +2864,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
chmode[uinfo->value.enumerated.item].channels);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
int snd_hda_ch_mode_get(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
@@ -2528,6 +2882,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
int snd_hda_ch_mode_put(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol,
@@ -2548,6 +2903,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
return 1;
}
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
/*
* input MUX helper
@@ -2568,6 +2924,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
strcpy(uinfo->value.enumerated.name, imux->items[index].label);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
@@ -2589,6 +2946,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
*cur_val = idx;
return 1;
}
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
/*
@@ -2641,6 +2999,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
struct hda_multi_out *mout,
@@ -2653,6 +3012,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
/*
* release the digital out
@@ -2665,6 +3025,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
/*
* set up more restrictions for analog out
@@ -2704,6 +3065,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
return snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
/*
* set up the i/o for analog out
@@ -2762,6 +3124,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
/*
* clean up the setting for analog out
@@ -2788,6 +3151,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
mutex_unlock(&codec->spdif_mutex);
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
/*
* Helper for automatic pin configuration
@@ -3073,11 +3437,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
/* labels for input pins */
const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
};
+EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
#ifdef CONFIG_PM
@@ -3105,11 +3471,11 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
}
return 0;
}
+EXPORT_SYMBOL_HDA(snd_hda_suspend);
/**
* snd_hda_resume - resume the codecs
* @bus: the HDA bus
- * @state: resume state
*
* Returns 0 if successful.
*
@@ -3126,16 +3492,79 @@ int snd_hda_resume(struct hda_bus *bus)
}
return 0;
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
- struct hda_codec *codec;
+EXPORT_SYMBOL_HDA(snd_hda_resume);
+#endif /* CONFIG_PM */
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- return 1;
+/*
+ * generic arrays
+ */
+
+/* get a new element from the given array
+ * if it exceeds the pre-allocated array size, re-allocate the array
+ */
+void *snd_array_new(struct snd_array *array)
+{
+ if (array->used >= array->alloced) {
+ int num = array->alloced + array->alloc_align;
+ void *nlist;
+ if (snd_BUG_ON(num >= 4096))
+ return NULL;
+ nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+ if (!nlist)
+ return NULL;
+ if (array->list) {
+ memcpy(nlist, array->list,
+ array->elem_size * array->alloced);
+ kfree(array->list);
+ }
+ array->list = nlist;
+ array->alloced = num;
}
- return 0;
+ return snd_array_elem(array, array->used++);
}
-#endif
-#endif
+EXPORT_SYMBOL_HDA(snd_array_new);
+
+/* free the given array elements */
+void snd_array_free(struct snd_array *array)
+{
+ kfree(array->list);
+ array->used = 0;
+ array->alloced = 0;
+ array->list = NULL;
+}
+EXPORT_SYMBOL_HDA(snd_array_free);
+
+/*
+ * used by hda_proc.c and hda_eld.c
+ */
+void snd_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+ static unsigned int rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 384000
+ };
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
+ if (pcm & (1 << i))
+ j += snprintf(buf + j, buflen - j, " %d", rates[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+
+void snd_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+ static unsigned int bits[] = { 8, 16, 20, 24, 32 };
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+ if (pcm & (AC_SUPPCM_BITS_8 << i))
+ j += snprintf(buf + j, buflen - j, " %d", bits[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
+
+MODULE_DESCRIPTION("HDA codec core");
+MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 60468f562400..729fc7642d7f 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -520,6 +520,36 @@ enum {
#define HDA_MAX_CODEC_ADDRESS 0x0f
/*
+ * generic arrays
+ */
+struct snd_array {
+ unsigned int used;
+ unsigned int alloced;
+ unsigned int elem_size;
+ unsigned int alloc_align;
+ void *list;
+};
+
+void *snd_array_new(struct snd_array *array);
+void snd_array_free(struct snd_array *array);
+static inline void snd_array_init(struct snd_array *array, unsigned int size,
+ unsigned int align)
+{
+ array->elem_size = size;
+ array->alloc_align = align;
+}
+
+static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
+{
+ return array->list + idx * array->elem_size;
+}
+
+static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
+{
+ return (unsigned long)(ptr - array->list) / array->elem_size;
+}
+
+/*
* Structures
*/
@@ -536,15 +566,17 @@ typedef u16 hda_nid_t;
/* bus operators */
struct hda_bus_ops {
/* send a single command */
- int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm);
+ int (*command)(struct hda_bus *bus, unsigned int cmd);
/* get a response from the last command */
- unsigned int (*get_response)(struct hda_codec *codec);
+ unsigned int (*get_response)(struct hda_bus *bus);
/* free the private data */
void (*private_free)(struct hda_bus *);
+ /* attach a PCM stream */
+ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *pcm);
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_codec *codec);
+ void (*pm_notify)(struct hda_bus *bus);
#endif
};
@@ -553,6 +585,7 @@ struct hda_bus_template {
void *private_data;
struct pci_dev *pci;
const char *modelname;
+ int *power_save;
struct hda_bus_ops ops;
};
@@ -569,6 +602,7 @@ struct hda_bus {
void *private_data;
struct pci_dev *pci;
const char *modelname;
+ int *power_save;
struct hda_bus_ops ops;
/* codec linked list */
@@ -581,10 +615,12 @@ struct hda_bus {
/* unsolicited event queue */
struct hda_bus_unsolicited *unsol;
- struct snd_info_entry *proc;
+ /* assigned PCMs */
+ DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
/* misc op flags */
unsigned int needs_damn_long_delay :1;
+ unsigned int shutdown :1; /* being unloaded */
};
/*
@@ -604,6 +640,16 @@ struct hda_codec_preset {
int (*patch)(struct hda_codec *codec);
};
+struct hda_codec_preset_list {
+ const struct hda_codec_preset *preset;
+ struct module *owner;
+ struct list_head list;
+};
+
+/* initial hook */
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
+
/* ops set by the preset patch */
struct hda_codec_ops {
int (*build_controls)(struct hda_codec *codec);
@@ -635,10 +681,7 @@ struct hda_amp_info {
struct hda_cache_rec {
u16 hash[64]; /* hash table for index */
- unsigned int num_entries; /* number of assigned entries */
- unsigned int size; /* allocated size */
- unsigned int record_size; /* record size (including header) */
- void *buffer; /* hash table entries */
+ struct snd_array buf; /* record entries */
};
/* PCM callbacks */
@@ -680,7 +723,8 @@ struct hda_pcm {
char *name;
struct hda_pcm_stream stream[2];
unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
- int device; /* assigned device number */
+ int device; /* device number to assign */
+ struct snd_pcm *pcm; /* assigned PCM instance */
};
/* codec information */
@@ -699,6 +743,9 @@ struct hda_codec {
/* detected preset */
const struct hda_codec_preset *preset;
+ struct module *owner;
+ const char *name; /* codec name */
+ const char *modelname; /* model name for preset */
/* set by patch */
struct hda_codec_ops patch_ops;
@@ -718,6 +765,8 @@ struct hda_codec {
hda_nid_t start_nid;
u32 *wcaps;
+ struct snd_array mixers; /* list of assigned mixer elements */
+
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
@@ -727,7 +776,11 @@ struct hda_codec {
unsigned int spdif_in_enable; /* SPDIF input enable? */
hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
+#ifdef CONFIG_SND_HDA_HWDEP
struct snd_hwdep *hwdep; /* assigned hwdep device */
+ struct snd_array init_verbs; /* additional init verbs */
+ struct snd_array hints; /* additional hints */
+#endif
/* misc flags */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
@@ -740,6 +793,10 @@ struct hda_codec {
int power_count; /* current (global) power refcount */
struct delayed_work power_work; /* delayed task for powerdown */
#endif
+
+ /* codec-specific additional proc output */
+ void (*proc_widget_hook)(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid);
};
/* direction */
@@ -754,7 +811,7 @@ enum {
int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
struct hda_bus **busp);
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- struct hda_codec **codecp);
+ int do_init, struct hda_codec **codecp);
/*
* low level functions
@@ -799,11 +856,13 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
* Mixer
*/
int snd_hda_build_controls(struct hda_bus *bus);
+int snd_hda_codec_build_controls(struct hda_codec *codec);
/*
* PCM
*/
int snd_hda_build_pcms(struct hda_bus *bus);
+int snd_hda_codec_build_pcms(struct hda_codec *codec);
void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag,
int channel_id, int format);
@@ -812,8 +871,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
unsigned int channels,
unsigned int format,
unsigned int maxbps);
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
unsigned int format);
@@ -831,18 +888,38 @@ int snd_hda_resume(struct hda_bus *bus);
#endif
/*
+ * get widget information
+ */
+const char *snd_hda_get_jack_connectivity(u32 cfg);
+const char *snd_hda_get_jack_type(u32 cfg);
+const char *snd_hda_get_jack_location(u32 cfg);
+
+/*
* power saving
*/
#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_power_up(struct hda_codec *codec);
void snd_hda_power_down(struct hda_codec *codec);
#define snd_hda_codec_needs_resume(codec) codec->power_count
-int snd_hda_codecs_inuse(struct hda_bus *bus);
#else
static inline void snd_hda_power_up(struct hda_codec *codec) {}
static inline void snd_hda_power_down(struct hda_codec *codec) {}
#define snd_hda_codec_needs_resume(codec) 1
-#define snd_hda_codecs_inuse(bus) 1
+#endif
+
+/*
+ * Codec modularization
+ */
+
+/* Export symbols only for communication with codec drivers;
+ * When built in kernel, all HD-audio drivers are supposed to be statically
+ * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be
+ * exported unless it's built as a module.
+ */
+#ifdef MODULE
+#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
+#else
+#define EXPORT_SYMBOL_HDA(sym)
#endif
#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
new file mode 100644
index 000000000000..fcad5ec31773
--- /dev/null
+++ b/sound/pci/hda/hda_eld.c
@@ -0,0 +1,590 @@
+/*
+ * Generic routines and proc interface for ELD(EDID Like Data) information
+ *
+ * Copyright(c) 2008 Intel Corporation.
+ *
+ * Authors:
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * This driver is free software; you can redistribute 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 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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 <sound/core.h>
+#include <asm/unaligned.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+enum eld_versions {
+ ELD_VER_CEA_861D = 2,
+ ELD_VER_PARTIAL = 31,
+};
+
+enum cea_edid_versions {
+ CEA_EDID_VER_NONE = 0,
+ CEA_EDID_VER_CEA861 = 1,
+ CEA_EDID_VER_CEA861A = 2,
+ CEA_EDID_VER_CEA861BCD = 3,
+ CEA_EDID_VER_RESERVED = 4,
+};
+
+static char *cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+static char *eld_connection_type_names[4] = {
+ "HDMI",
+ "DisplayPort",
+ "2-reserved",
+ "3-reserved"
+};
+
+enum cea_audio_coding_types {
+ AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
+ AUDIO_CODING_TYPE_LPCM = 1,
+ AUDIO_CODING_TYPE_AC3 = 2,
+ AUDIO_CODING_TYPE_MPEG1 = 3,
+ AUDIO_CODING_TYPE_MP3 = 4,
+ AUDIO_CODING_TYPE_MPEG2 = 5,
+ AUDIO_CODING_TYPE_AACLC = 6,
+ AUDIO_CODING_TYPE_DTS = 7,
+ AUDIO_CODING_TYPE_ATRAC = 8,
+ AUDIO_CODING_TYPE_SACD = 9,
+ AUDIO_CODING_TYPE_EAC3 = 10,
+ AUDIO_CODING_TYPE_DTS_HD = 11,
+ AUDIO_CODING_TYPE_MLP = 12,
+ AUDIO_CODING_TYPE_DST = 13,
+ AUDIO_CODING_TYPE_WMAPRO = 14,
+ AUDIO_CODING_TYPE_REF_CXT = 15,
+ /* also include valid xtypes below */
+ AUDIO_CODING_TYPE_HE_AAC = 15,
+ AUDIO_CODING_TYPE_HE_AAC2 = 16,
+ AUDIO_CODING_TYPE_MPEG_SURROUND = 17,
+};
+
+enum cea_audio_coding_xtypes {
+ AUDIO_CODING_XTYPE_HE_REF_CT = 0,
+ AUDIO_CODING_XTYPE_HE_AAC = 1,
+ AUDIO_CODING_XTYPE_HE_AAC2 = 2,
+ AUDIO_CODING_XTYPE_MPEG_SURROUND = 3,
+ AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
+};
+
+static char *cea_audio_coding_type_names[] = {
+ /* 0 */ "undefined",
+ /* 1 */ "LPCM",
+ /* 2 */ "AC-3",
+ /* 3 */ "MPEG1",
+ /* 4 */ "MP3",
+ /* 5 */ "MPEG2",
+ /* 6 */ "AAC-LC",
+ /* 7 */ "DTS",
+ /* 8 */ "ATRAC",
+ /* 9 */ "DSD (One Bit Audio)",
+ /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+ /* 11 */ "DTS-HD",
+ /* 12 */ "MLP (Dolby TrueHD)",
+ /* 13 */ "DST",
+ /* 14 */ "WMAPro",
+ /* 15 */ "HE-AAC",
+ /* 16 */ "HE-AACv2",
+ /* 17 */ "MPEG Surround",
+};
+
+/*
+ * The following two lists are shared between
+ * - HDMI audio InfoFrame (source to sink)
+ * - CEA E-EDID Extension (sink to source)
+ */
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static int cea_sample_sizes[4] = {
+ 0, /* 0: Refer to Stream Header */
+ AC_SUPPCM_BITS_16, /* 1: 16 bits */
+ AC_SUPPCM_BITS_20, /* 2: 20 bits */
+ AC_SUPPCM_BITS_24, /* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+static int cea_sampling_frequencies[8] = {
+ 0, /* 0: Refer to Stream Header */
+ SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
+ SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
+ SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
+ SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
+ SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
+ SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
+ SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
+};
+
+static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+ int byte_index)
+{
+ unsigned int val;
+
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_HDMI_ELDD, byte_index);
+
+#ifdef BE_PARANOID
+ printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+
+ if ((val & AC_ELDD_ELD_VALID) == 0) {
+ snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
+ byte_index);
+ val = 0;
+ }
+
+ return val & AC_ELDD_ELD_DATA;
+}
+
+#define GRAB_BITS(buf, byte, lowbit, bits) \
+({ \
+ BUILD_BUG_ON(lowbit > 7); \
+ BUILD_BUG_ON(bits > 8); \
+ BUILD_BUG_ON(bits <= 0); \
+ \
+ (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
+})
+
+static void hdmi_update_short_audio_desc(struct cea_sad *a,
+ const unsigned char *buf)
+{
+ int i;
+ int val;
+
+ val = GRAB_BITS(buf, 1, 0, 7);
+ a->rates = 0;
+ for (i = 0; i < 7; i++)
+ if (val & (1 << i))
+ a->rates |= cea_sampling_frequencies[i + 1];
+
+ a->channels = GRAB_BITS(buf, 0, 0, 3);
+ a->channels++;
+
+ a->format = GRAB_BITS(buf, 0, 3, 4);
+ switch (a->format) {
+ case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+ snd_printd(KERN_INFO
+ "HDMI: audio coding type 0 not expected\n");
+ break;
+
+ case AUDIO_CODING_TYPE_LPCM:
+ val = GRAB_BITS(buf, 2, 0, 3);
+ a->sample_bits = 0;
+ for (i = 0; i < 3; i++)
+ if (val & (1 << i))
+ a->sample_bits |= cea_sample_sizes[i + 1];
+ break;
+
+ case AUDIO_CODING_TYPE_AC3:
+ case AUDIO_CODING_TYPE_MPEG1:
+ case AUDIO_CODING_TYPE_MP3:
+ case AUDIO_CODING_TYPE_MPEG2:
+ case AUDIO_CODING_TYPE_AACLC:
+ case AUDIO_CODING_TYPE_DTS:
+ case AUDIO_CODING_TYPE_ATRAC:
+ a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+ a->max_bitrate *= 8000;
+ break;
+
+ case AUDIO_CODING_TYPE_SACD:
+ break;
+
+ case AUDIO_CODING_TYPE_EAC3:
+ break;
+
+ case AUDIO_CODING_TYPE_DTS_HD:
+ break;
+
+ case AUDIO_CODING_TYPE_MLP:
+ break;
+
+ case AUDIO_CODING_TYPE_DST:
+ break;
+
+ case AUDIO_CODING_TYPE_WMAPRO:
+ a->profile = GRAB_BITS(buf, 2, 0, 3);
+ break;
+
+ case AUDIO_CODING_TYPE_REF_CXT:
+ a->format = GRAB_BITS(buf, 2, 3, 5);
+ if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+ a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+ snd_printd(KERN_INFO
+ "HDMI: audio coding xtype %d not expected\n",
+ a->format);
+ a->format = 0;
+ } else
+ a->format += AUDIO_CODING_TYPE_HE_AAC -
+ AUDIO_CODING_XTYPE_HE_AAC;
+ break;
+ }
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+static int hdmi_update_eld(struct hdmi_eld *e,
+ const unsigned char *buf, int size)
+{
+ int mnl;
+ int i;
+
+ e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
+ if (e->eld_ver != ELD_VER_CEA_861D &&
+ e->eld_ver != ELD_VER_PARTIAL) {
+ snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
+ e->eld_ver);
+ goto out_fail;
+ }
+
+ e->eld_size = size;
+ e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
+ mnl = GRAB_BITS(buf, 4, 0, 5);
+ e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
+
+ e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
+ e->support_ai = GRAB_BITS(buf, 5, 1, 1);
+ e->conn_type = GRAB_BITS(buf, 5, 2, 2);
+ e->sad_count = GRAB_BITS(buf, 5, 4, 4);
+
+ e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+ e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
+
+ e->port_id = get_unaligned_le64(buf + 8);
+
+ /* not specified, but the spec's tendency is little endian */
+ e->manufacture_id = get_unaligned_le16(buf + 16);
+ e->product_id = get_unaligned_le16(buf + 18);
+
+ if (mnl > ELD_MAX_MNL) {
+ snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
+ goto out_fail;
+ } else if (ELD_FIXED_BYTES + mnl > size) {
+ snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
+ goto out_fail;
+ } else
+ strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+
+ for (i = 0; i < e->sad_count; i++) {
+ if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
+ snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
+ goto out_fail;
+ }
+ hdmi_update_short_audio_desc(e->sad + i,
+ buf + ELD_FIXED_BYTES + mnl + 3 * i);
+ }
+
+ return 0;
+
+out_fail:
+ e->eld_ver = 0;
+ return -EINVAL;
+}
+
+static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
+}
+
+static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
+{
+ int eldv;
+ int present;
+
+ present = hdmi_present_sense(codec, nid);
+ eldv = (present & AC_PINSENSE_ELDV);
+ present = (present & AC_PINSENSE_PRESENCE);
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
+ !!present, !!eldv);
+#endif
+
+ return eldv && present;
+}
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+ AC_DIPSIZE_ELD_BUF);
+}
+
+int snd_hdmi_get_eld(struct hdmi_eld *eld,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i;
+ int ret;
+ int size;
+ unsigned char *buf;
+
+ if (!hdmi_eld_valid(codec, nid))
+ return -ENOENT;
+
+ size = snd_hdmi_get_eld_size(codec, nid);
+ if (size == 0) {
+ /* wfg: workaround for ASUS P5E-VM HDMI board */
+ snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
+ size = 128;
+ }
+ if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
+ snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+ return -ERANGE;
+ }
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < size; i++)
+ buf[i] = hdmi_get_eld_byte(codec, nid, i);
+
+ ret = hdmi_update_eld(eld, buf, size);
+
+ kfree(buf);
+ return ret;
+}
+
+static void hdmi_show_short_audio_desc(struct cea_sad *a)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
+
+ if (!a->format)
+ return;
+
+ snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM)
+ snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8));
+ else if (a->max_bitrate)
+ snprintf(buf2, sizeof(buf2),
+ ", max bitrate = %d", a->max_bitrate);
+ else
+ buf2[0] = '\0';
+
+ printk(KERN_INFO "HDMI: supports coding type %s:"
+ " channels = %d, rates =%s%s\n",
+ cea_audio_coding_type_names[a->format],
+ a->channels,
+ buf,
+ buf2);
+}
+
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += snprintf(buf + j, buflen - j, " %s",
+ cea_speaker_allocation_names[i]);
+ }
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+void snd_hdmi_show_eld(struct hdmi_eld *e)
+{
+ int i;
+
+ printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
+ e->monitor_name,
+ eld_connection_type_names[e->conn_type]);
+
+ if (e->spk_alloc) {
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+ snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
+ }
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_show_short_audio_desc(e->sad + i);
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void hdmi_print_sad_info(int i, struct cea_sad *a,
+ struct snd_info_buffer *buffer)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+ snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+ i, a->format, cea_audio_coding_type_names[a->format]);
+ snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+ snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM) {
+ snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
+ i, a->sample_bits, buf);
+ }
+
+ if (a->max_bitrate)
+ snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+ i, a->max_bitrate);
+
+ if (a->profile)
+ snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+static void hdmi_print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_eld *e = entry->private_data;
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+ int i;
+ static char *eld_versoin_names[32] = {
+ "reserved",
+ "reserved",
+ "CEA-861D or below",
+ [3 ... 30] = "reserved",
+ [31] = "partial"
+ };
+ static char *cea_edid_version_names[8] = {
+ "no CEA EDID Timing Extension block present",
+ "CEA-861",
+ "CEA-861-A",
+ "CEA-861-B, C or D",
+ [4 ... 7] = "reserved"
+ };
+
+ snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
+ snd_iprintf(buffer, "connection_type\t\t%s\n",
+ eld_connection_type_names[e->conn_type]);
+ snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
+ eld_versoin_names[e->eld_ver]);
+ snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
+ cea_edid_version_names[e->cea_edid_ver]);
+ snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
+ snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+ snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+ snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+ snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
+ snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
+
+ snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
+
+ snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+
+static void hdmi_write_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_eld *e = entry->private_data;
+ char line[64];
+ char name[64];
+ char *sname;
+ long long val;
+ int n;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%s %llx", name, &val) != 2)
+ continue;
+ /*
+ * We don't allow modification to these fields:
+ * monitor_name manufacture_id product_id
+ * eld_version edid_version
+ */
+ if (!strcmp(name, "connection_type"))
+ e->conn_type = val;
+ else if (!strcmp(name, "port_id"))
+ e->port_id = val;
+ else if (!strcmp(name, "support_hdcp"))
+ e->support_hdcp = val;
+ else if (!strcmp(name, "support_ai"))
+ e->support_ai = val;
+ else if (!strcmp(name, "audio_sync_delay"))
+ e->aud_synch_delay = val;
+ else if (!strcmp(name, "speakers"))
+ e->spk_alloc = val;
+ else if (!strcmp(name, "sad_count"))
+ e->sad_count = val;
+ else if (!strncmp(name, "sad", 3)) {
+ sname = name + 4;
+ n = name[3] - '0';
+ if (name[4] >= '0' && name[4] <= '9') {
+ sname++;
+ n = 10 * n + name[4] - '0';
+ }
+ if (n < 0 || n > 31) /* double the CEA limit */
+ continue;
+ if (!strcmp(sname, "_coding_type"))
+ e->sad[n].format = val;
+ else if (!strcmp(sname, "_channels"))
+ e->sad[n].channels = val;
+ else if (!strcmp(sname, "_rates"))
+ e->sad[n].rates = val;
+ else if (!strcmp(sname, "_bits"))
+ e->sad[n].sample_bits = val;
+ else if (!strcmp(sname, "_max_bitrate"))
+ e->sad[n].max_bitrate = val;
+ else if (!strcmp(sname, "_profile"))
+ e->sad[n].profile = val;
+ if (n >= e->sad_count)
+ e->sad_count = n + 1;
+ }
+ }
+}
+
+
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+ char name[32];
+ struct snd_info_entry *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "eld#%d", codec->addr);
+ err = snd_card_proc_new(codec->bus->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
+ entry->c.text.write = hdmi_write_eld_info;
+ entry->mode |= S_IWUSR;
+ eld->proc_entry = entry;
+
+ return 0;
+}
+
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+ if (!codec->bus->shutdown && eld->proc_entry) {
+ snd_device_free(codec->bus->card, eld->proc_entry);
+ eld->proc_entry = NULL;
+ }
+}
+
+#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 0ca30894f7c6..65745e96dc70 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -723,7 +723,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_INPUT, index);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
@@ -732,7 +733,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
if (is_loopback)
add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
}
@@ -745,14 +747,16 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
(node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
(node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
created = 1;
}
@@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec)
}
/* create input MUX if multiple sources are available */
- if ((err = snd_ctl_add(codec->bus->card,
- snd_ctl_new1(&cap_sel, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+ if (err < 0)
return err;
/* no volume control? */
@@ -867,8 +871,8 @@ static int build_input_controls(struct hda_codec *codec)
HDA_CODEC_VOLUME(name, adc_node->nid,
spec->input_mux.items[i].index,
HDA_INPUT);
- if ((err = snd_ctl_add(codec->bus->card,
- snd_ctl_new1(&knew, codec))) < 0)
+ err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+ if (err < 0)
return err;
}
@@ -1097,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
snd_hda_generic_free(codec);
return err;
}
+EXPORT_SYMBOL(snd_hda_parse_generic_codec);
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 6e18a422d993..300ab407cf42 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -23,10 +23,12 @@
#include <linux/pci.h>
#include <linux/compat.h>
#include <linux/mutex.h>
+#include <linux/ctype.h>
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
/*
* write/read an out-of-bound verb
@@ -95,7 +97,26 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
return 0;
}
-int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
+static void clear_hwdep_elements(struct hda_codec *codec)
+{
+ char **head;
+ int i;
+
+ /* clear init verbs */
+ snd_array_free(&codec->init_verbs);
+ /* clear hints */
+ head = codec->hints.list;
+ for (i = 0; i < codec->hints.used; i++, head++)
+ kfree(*head);
+ snd_array_free(&codec->hints);
+}
+
+static void hwdep_free(struct snd_hwdep *hwdep)
+{
+ clear_hwdep_elements(hwdep->private_data);
+}
+
+int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
{
char hwname[16];
struct snd_hwdep *hwdep;
@@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
sprintf(hwdep->name, "HDA Codec %d", codec->addr);
hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
hwdep->private_data = codec;
+ hwdep->private_free = hwdep_free;
hwdep->exclusive = 1;
hwdep->ops.open = hda_hwdep_open;
@@ -117,5 +139,215 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
+ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+ snd_array_init(&codec->hints, sizeof(char *), 32);
+
return 0;
}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+ snd_hda_codec_reset(codec);
+ clear_hwdep_elements(codec);
+ return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+ int err;
+
+ snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
+ snd_hda_codec_reset(codec);
+ err = snd_hda_codec_configure(codec);
+ if (err < 0)
+ return err;
+ /* rebuild PCMs */
+ err = snd_hda_codec_build_pcms(codec);
+ if (err < 0)
+ return err;
+ /* rebuild mixers */
+ err = snd_hda_codec_build_controls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+ char *s = kstrndup(src, len, GFP_KERNEL);
+ char *p;
+ if (!s)
+ return NULL;
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+ return s;
+}
+
+#define CODEC_INFO_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ return sprintf(buf, "0x%x\n", codec->type); \
+}
+
+#define CODEC_INFO_STR_SHOW(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ return sprintf(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(modelname);
+
+#define CODEC_INFO_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ char *after; \
+ codec->type = simple_strtoul(buf, &after, 0); \
+ return count; \
+}
+
+#define CODEC_INFO_STR_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ char *s = kstrndup_noeol(buf, 64); \
+ if (!s) \
+ return -ENOMEM; \
+ kfree(codec->type); \
+ codec->type = s; \
+ return count; \
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
+ struct hda_codec *codec = hwdep->private_data; \
+ int err = 0; \
+ if (*buf) \
+ err = type##_codec(codec); \
+ return err < 0 ? err : count; \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ char *p;
+ struct hda_verb verb, *v;
+
+ verb.nid = simple_strtoul(buf, &p, 0);
+ verb.verb = simple_strtoul(p, &p, 0);
+ verb.param = simple_strtoul(p, &p, 0);
+ if (!verb.nid || !verb.verb || !verb.param)
+ return -EINVAL;
+ v = snd_array_new(&codec->init_verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = verb;
+ return count;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+ struct hda_codec *codec = hwdep->private_data;
+ char *p;
+ char **hint;
+
+ if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+ return count;
+ p = kstrndup_noeol(buf, 1024);
+ if (!p)
+ return -ENOMEM;
+ hint = snd_array_new(&codec->hints);
+ if (!hint) {
+ kfree(p);
+ return -ENOMEM;
+ }
+ *hint = p;
+ return count;
+}
+
+#define CODEC_ATTR_RW(type) \
+ __ATTR(type, 0644, type##_show, type##_store)
+#define CODEC_ATTR_RO(type) \
+ __ATTR_RO(type)
+#define CODEC_ATTR_WO(type) \
+ __ATTR(type, 0200, NULL, type##_store)
+
+static struct device_attribute codec_attrs[] = {
+ CODEC_ATTR_RW(vendor_id),
+ CODEC_ATTR_RW(subsystem_id),
+ CODEC_ATTR_RW(revision_id),
+ CODEC_ATTR_RO(afg),
+ CODEC_ATTR_RO(mfg),
+ CODEC_ATTR_RW(name),
+ CODEC_ATTR_RW(modelname),
+ CODEC_ATTR_WO(init_verbs),
+ CODEC_ATTR_WO(hints),
+ CODEC_ATTR_WO(reconfig),
+ CODEC_ATTR_WO(clear),
+};
+
+/*
+ * create sysfs files on hwdep directory
+ */
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+ struct snd_hwdep *hwdep = codec->hwdep;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
+ snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+ hwdep->device, &codec_attrs[i]);
+ return 0;
+}
+
+#endif /* CONFIG_SND_HDA_RECONFIG */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 35722ec920cb..f04de115ee11 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -58,6 +58,7 @@ static char *model[SNDRV_CARDS];
static int position_fix[SNDRV_CARDS];
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_only[SNDRV_CARDS];
static int single_cmd;
static int enable_msi;
@@ -76,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+module_param_array(probe_only, bool, NULL, 0444);
+MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
@@ -83,7 +86,10 @@ module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power_save option is defined in hda_codec.c */
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+ "(in second, 0 = disable).");
/* reset the HD-audio controller in power save mode.
* this may give more power-saving, but will take longer time to
@@ -292,6 +298,8 @@ enum {
/* Define VIA HD Audio Device ID*/
#define VIA_HDAC_DEVICE_ID 0x3288
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
/*
*/
@@ -392,6 +400,7 @@ struct azx {
unsigned int msi :1;
unsigned int irq_pending_warned :1;
unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+ unsigned int probing :1; /* codec probing phase */
/* for debugging */
unsigned int last_cmd; /* last issued command (to sync) */
@@ -414,6 +423,7 @@ enum {
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
+ AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -427,6 +437,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
+ [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
/*
@@ -527,9 +538,9 @@ static void azx_free_cmd_io(struct azx *chip)
}
/* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
unsigned int wp;
/* add command to corb */
@@ -577,9 +588,9 @@ static void azx_update_rirb(struct azx *chip)
}
/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
unsigned long timeout;
again:
@@ -596,7 +607,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
}
if (time_after(jiffies, timeout))
break;
- if (codec->bus->needs_damn_long_delay)
+ if (bus->needs_damn_long_delay)
msleep(2); /* temporary workaround */
else {
udelay(10);
@@ -624,6 +635,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
goto again;
}
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -1;
+ }
+
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd);
@@ -646,9 +665,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
*/
/* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
int timeout = 50;
while (timeout--) {
@@ -671,9 +690,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
}
/* receive a response */
-static unsigned int azx_single_get_response(struct hda_codec *codec)
+static unsigned int azx_single_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
int timeout = 50;
while (timeout--) {
@@ -696,38 +715,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
*/
/* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb,
- unsigned int para)
+static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
{
- struct azx *chip = codec->bus->private_data;
- u32 val;
-
- val = (u32)(codec->addr & 0x0f) << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= para;
- chip->last_cmd = val;
+ struct azx *chip = bus->private_data;
+ chip->last_cmd = val;
if (chip->single_cmd)
- return azx_single_send_cmd(codec, val);
+ return azx_single_send_cmd(bus, val);
else
- return azx_corb_send_cmd(codec, val);
+ return azx_corb_send_cmd(bus, val);
}
/* get a response */
-static unsigned int azx_get_response(struct hda_codec *codec)
+static unsigned int azx_get_response(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
if (chip->single_cmd)
- return azx_single_get_response(codec);
+ return azx_single_get_response(bus);
else
- return azx_rirb_get_response(codec);
+ return azx_rirb_get_response(bus);
}
#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_codec *codec);
+static void azx_power_notify(struct hda_bus *bus);
#endif
/* reset codec link */
@@ -1184,6 +1194,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ chip->probing = 1;
+ azx_send_cmd(chip->bus, cmd);
+ res = azx_get_response(chip->bus);
+ chip->probing = 0;
+ if (res == -1)
+ return -EIO;
+ snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+ return 0;
+}
+
+static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm);
+static void azx_stop_chip(struct azx *chip);
/*
* Codec initialization
@@ -1194,21 +1226,13 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
[AZX_DRIVER_TERA] = 1,
};
-/* number of slots to probe as default
- * this can be different from azx_max_codecs[] -- e.g. some boards
- * report wrongly the non-existing 4th slot availability
- */
-static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
- [AZX_DRIVER_ICH] = 3,
- [AZX_DRIVER_ATI] = 3,
-};
-
static int __devinit azx_codec_create(struct azx *chip, const char *model,
- unsigned int codec_probe_mask)
+ unsigned int codec_probe_mask,
+ int no_init)
{
struct hda_bus_template bus_temp;
- int c, codecs, audio_codecs, err;
- int def_slots, max_slots;
+ int c, codecs, err;
+ int max_slots;
memset(&bus_temp, 0, sizeof(bus_temp));
bus_temp.private_data = chip;
@@ -1216,7 +1240,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
bus_temp.pci = chip->pci;
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
+ bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
#ifdef CONFIG_SND_HDA_POWER_SAVE
+ bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify;
#endif
@@ -1227,33 +1253,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
if (chip->driver_type == AZX_DRIVER_NVIDIA)
chip->bus->needs_damn_long_delay = 1;
- codecs = audio_codecs = 0;
+ codecs = 0;
max_slots = azx_max_codecs[chip->driver_type];
if (!max_slots)
max_slots = AZX_MAX_CODECS;
- def_slots = azx_default_codecs[chip->driver_type];
- if (!def_slots)
- def_slots = max_slots;
- for (c = 0; c < def_slots; c++) {
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ snd_printk(KERN_WARNING
+ "hda_intel: Codec #%d probe error; "
+ "disabling it...\n", c);
+ chip->codec_mask &= ~(1 << c);
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and distrubs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip);
+ }
+ }
+ }
+
+ /* Then create codec instances */
+ for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
- err = snd_hda_codec_new(chip->bus, c, &codec);
+ err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
if (err < 0)
continue;
codecs++;
- if (codec->afg)
- audio_codecs++;
- }
- }
- if (!audio_codecs) {
- /* probe additional slots if no codec is found */
- for (; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
- err = snd_hda_codec_new(chip->bus, c, NULL);
- if (err < 0)
- continue;
- codecs++;
- }
}
}
if (!codecs) {
@@ -1722,111 +1758,59 @@ static struct snd_pcm_ops azx_pcm_ops = {
static void azx_pcm_free(struct snd_pcm *pcm)
{
- kfree(pcm->private_data);
+ struct azx_pcm *apcm = pcm->private_data;
+ if (apcm) {
+ apcm->chip->pcm[pcm->device] = NULL;
+ kfree(apcm);
+ }
}
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
- struct hda_pcm *cpcm)
+static int
+azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
{
- int err;
+ struct azx *chip = bus->private_data;
struct snd_pcm *pcm;
struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ int s, err;
- /* if no substreams are defined for both playback and capture,
- * it's just a placeholder. ignore it.
- */
- if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
- return 0;
-
- if (snd_BUG_ON(!cpcm->name))
+ if (pcm_dev >= AZX_MAX_PCMS) {
+ snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+ pcm_dev);
return -EINVAL;
-
- err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
- cpcm->stream[0].substreams,
- cpcm->stream[1].substreams,
+ }
+ if (chip->pcm[pcm_dev]) {
+ snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
+ return -EBUSY;
+ }
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
&pcm);
if (err < 0)
return err;
strcpy(pcm->name, cpcm->name);
- apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
if (apcm == NULL)
return -ENOMEM;
apcm->chip = chip;
apcm->codec = codec;
- apcm->hinfo[0] = &cpcm->stream[0];
- apcm->hinfo[1] = &cpcm->stream[1];
pcm->private_data = apcm;
pcm->private_free = azx_pcm_free;
- if (cpcm->stream[0].substreams)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
- if (cpcm->stream[1].substreams)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+ pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+ chip->pcm[pcm_dev] = pcm;
+ cpcm->pcm = pcm;
+ for (s = 0; s < 2; s++) {
+ apcm->hinfo[s] = &cpcm->stream[s];
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
+ /* buffer pre-allocation */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
snd_dma_pci_data(chip->pci),
1024 * 64, 32 * 1024 * 1024);
- chip->pcm[cpcm->device] = pcm;
- return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
- static const char *dev_name[HDA_PCM_NTYPES] = {
- "Audio", "SPDIF", "HDMI", "Modem"
- };
- /* starting device index for each PCM type */
- static int dev_idx[HDA_PCM_NTYPES] = {
- [HDA_PCM_TYPE_AUDIO] = 0,
- [HDA_PCM_TYPE_SPDIF] = 1,
- [HDA_PCM_TYPE_HDMI] = 3,
- [HDA_PCM_TYPE_MODEM] = 6
- };
- /* normal audio device indices; not linear to keep compatibility */
- static int audio_idx[4] = { 0, 2, 4, 5 };
- struct hda_codec *codec;
- int c, err;
- int num_devs[HDA_PCM_NTYPES];
-
- err = snd_hda_build_pcms(chip->bus);
- if (err < 0)
- return err;
-
- /* create audio PCMs */
- memset(num_devs, 0, sizeof(num_devs));
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- for (c = 0; c < codec->num_pcms; c++) {
- struct hda_pcm *cpcm = &codec->pcm_info[c];
- int type = cpcm->pcm_type;
- switch (type) {
- case HDA_PCM_TYPE_AUDIO:
- if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
- snd_printk(KERN_WARNING
- "Too many audio devices\n");
- continue;
- }
- cpcm->device = audio_idx[num_devs[type]];
- break;
- case HDA_PCM_TYPE_SPDIF:
- case HDA_PCM_TYPE_HDMI:
- case HDA_PCM_TYPE_MODEM:
- if (num_devs[type]) {
- snd_printk(KERN_WARNING
- "%s already defined\n",
- dev_name[type]);
- continue;
- }
- cpcm->device = dev_idx[type];
- break;
- default:
- snd_printk(KERN_WARNING
- "Invalid PCM type %d\n", type);
- continue;
- }
- num_devs[type]++;
- err = create_codec_pcm(chip, codec, cpcm);
- if (err < 0)
- return err;
- }
- }
return 0;
}
@@ -1903,13 +1887,13 @@ static void azx_stop_chip(struct azx *chip)
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* power-up/down the controller */
-static void azx_power_notify(struct hda_codec *codec)
+static void azx_power_notify(struct hda_bus *bus)
{
- struct azx *chip = codec->bus->private_data;
+ struct azx *chip = bus->private_data;
struct hda_codec *c;
int power_on = 0;
- list_for_each_entry(c, &codec->bus->codec_list, list) {
+ list_for_each_entry(c, &bus->codec_list, list) {
if (c->power_on) {
power_on = 1;
break;
@@ -1926,6 +1910,18 @@ static void azx_power_notify(struct hda_codec *codec)
/*
* power management
*/
+
+static int snd_hda_codecs_inuse(struct hda_bus *bus)
+{
+ struct hda_codec *codec;
+
+ list_for_each_entry(codec, &bus->codec_list, list) {
+ if (snd_hda_codec_needs_resume(codec))
+ return 1;
+ }
+ return 0;
+}
+
static int azx_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
@@ -1951,13 +1947,16 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
return 0;
}
+static int azx_resume_early(struct pci_dev *pci)
+{
+ return pci_restore_state(pci);
+}
+
static int azx_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
printk(KERN_ERR "hda-intel: pci_enable_device failed, "
"disabling device\n");
@@ -2095,6 +2094,10 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+ /* broken BIOS */
+ SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
+ /* including bogus ALC268 in slot#2 that conflicts with ALC888 */
+ SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
{}
};
@@ -2229,6 +2232,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
chip->capture_streams = ATIHDMI_NUM_CAPTURE;
break;
+ case AZX_DRIVER_GENERIC:
default:
chip->playback_streams = ICH6_NUM_PLAYBACK;
chip->capture_streams = ICH6_NUM_CAPTURE;
@@ -2338,40 +2342,31 @@ static int __devinit azx_probe(struct pci_dev *pci,
}
err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto out_free;
card->private_data = chip;
/* create codec instances */
- err = azx_codec_create(chip, model[dev], probe_mask[dev]);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ err = azx_codec_create(chip, model[dev], probe_mask[dev],
+ probe_only[dev]);
+ if (err < 0)
+ goto out_free;
/* create PCM streams */
- err = azx_pcm_create(chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_hda_build_pcms(chip->bus);
+ if (err < 0)
+ goto out_free;
/* create mixer controls */
err = azx_mixer_create(chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto out_free;
snd_card_set_dev(card, &pci->dev);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto out_free;
pci_set_drvdata(pci, card);
chip->running = 1;
@@ -2380,6 +2375,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
dev++;
return err;
+out_free:
+ snd_card_free(card);
+ return err;
}
static void __devexit azx_remove(struct pci_dev *pci)
@@ -2453,6 +2451,11 @@ static struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
/* Teradici */
{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+ /* AMD Generic, PCI class code and Vendor ID for HD Audio */
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GENERIC },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, azx_ids);
@@ -2465,6 +2468,7 @@ static struct pci_driver driver = {
.remove = __devexit_p(azx_remove),
#ifdef CONFIG_PM
.suspend = azx_suspend,
+ .resume_early = azx_resume_early,
.resume = azx_resume,
#endif
};
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7957fefda730..6f2fe0f9fdd8 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -96,6 +96,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves);
+void snd_hda_codec_reset(struct hda_codec *codec);
+int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
@@ -282,6 +284,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+void snd_print_pcm_rates(int pcm, char *buf, int buflen);
+
+#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
+void snd_print_pcm_bits(int pcm, char *buf, int buflen);
+
/*
* Misc
*/
@@ -364,17 +372,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
#define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE 0xb080
-#define AMP_OUT_UNMUTE 0xb000
-#define AMP_OUT_ZERO 0xb000
+#define AMP_OUT_MUTE 0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO 0xb000
/* pinctl values */
#define PIN_IN (AC_PINCTL_IN_EN)
-#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREFHIZ (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
#define PIN_VREF50 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
-#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREFGRD (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
#define PIN_VREF80 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
-#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
-#define PIN_OUT (AC_PINCTL_OUT_EN)
+#define PIN_VREF100 (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT (AC_PINCTL_OUT_EN)
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
@@ -393,10 +401,26 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+void snd_hda_ctls_clear(struct hda_codec *codec);
+
/*
* hwdep interface
*/
+#ifdef CONFIG_SND_HDA_HWDEP
int snd_hda_create_hwdep(struct hda_codec *codec);
+#else
+static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
+#endif
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+ return 0;
+}
+#endif
/*
* power-management
@@ -430,4 +454,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+/*
+ * CEA Short Audio Descriptor data
+ */
+struct cea_sad {
+ int channels;
+ int format; /* (format == 0) indicates invalid SAD */
+ int rates;
+ int sample_bits; /* for LPCM */
+ int max_bitrate; /* for AC3...ATRAC */
+ int profile; /* for WMAPRO */
+};
+
+#define ELD_FIXED_BYTES 20
+#define ELD_MAX_MNL 16
+#define ELD_MAX_SAD 16
+
+/*
+ * ELD: EDID Like Data
+ */
+struct hdmi_eld {
+ int eld_size;
+ int baseline_len;
+ int eld_ver; /* (eld_ver == 0) indicates invalid ELD */
+ int cea_edid_ver;
+ char monitor_name[ELD_MAX_MNL + 1];
+ int manufacture_id;
+ int product_id;
+ u64 port_id;
+ int support_hdcp;
+ int support_ai;
+ int conn_type;
+ int aud_synch_delay;
+ int spk_alloc;
+ int sad_count;
+ struct cea_sad sad[ELD_MAX_SAD];
+#ifdef CONFIG_PROC_FS
+ struct snd_info_entry *proc_entry;
+#endif
+};
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
+int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
+void snd_hdmi_show_eld(struct hdmi_eld *eld);
+
+#ifdef CONFIG_PROC_FS
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
+#else
+static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
+ struct hdmi_eld *eld)
+{
+ return 0;
+}
+static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
+ struct hdmi_eld *eld)
+{
+}
+#endif
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
deleted file mode 100644
index dfbcfa88da44..000000000000
--- a/sound/pci/hda/hda_patch.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * HDA Patches - included by hda_codec.c
- */
-
-/* Realtek codecs */
-extern struct hda_codec_preset snd_hda_preset_realtek[];
-/* C-Media codecs */
-extern struct hda_codec_preset snd_hda_preset_cmedia[];
-/* Analog Devices codecs */
-extern struct hda_codec_preset snd_hda_preset_analog[];
-/* SigmaTel codecs */
-extern struct hda_codec_preset snd_hda_preset_sigmatel[];
-/* SiLabs 3054/3055 modem codecs */
-extern struct hda_codec_preset snd_hda_preset_si3054[];
-/* ATI HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_atihdmi[];
-/* Conexant audio codec */
-extern struct hda_codec_preset snd_hda_preset_conexant[];
-/* VIA codecs */
-extern struct hda_codec_preset snd_hda_preset_via[];
-/* NVIDIA HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_nvhdmi[];
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index c39af986bff1..7ca66d654148 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
- static unsigned int rates[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
- 96000, 176400, 192000, 384000
- };
- int i;
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
- for (i = 0; i < ARRAY_SIZE(rates); i++)
- if (pcm & (1 << i))
- snd_iprintf(buffer, " %d", rates[i]);
- snd_iprintf(buffer, "\n");
+ snd_print_pcm_rates(pcm, buf, sizeof(buf));
+ snd_iprintf(buffer, "%s\n", buf);
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
{
- static unsigned int bits[] = { 8, 16, 20, 24, 32 };
- int i;
+ char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
- pcm = (pcm >> 16) & 0xff;
- snd_iprintf(buffer, " bits [0x%x]:", pcm);
- for (i = 0; i < ARRAY_SIZE(bits); i++)
- if (pcm & (1 << i))
- snd_iprintf(buffer, " %d", bits[i]);
- snd_iprintf(buffer, "\n");
+ snd_iprintf(buffer, " bits [0x%x]:", (pcm >> 16) & 0xff);
+ snd_print_pcm_bits(pcm, buf, sizeof(buf));
+ snd_iprintf(buffer, "%s\n", buf);
}
static void print_pcm_formats(struct snd_info_buffer *buffer,
@@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
print_pcm_formats(buffer, stream);
}
-static const char *get_jack_location(u32 cfg)
-{
- static char *bases[7] = {
- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
- };
- static unsigned char specials_idx[] = {
- 0x07, 0x08,
- 0x17, 0x18, 0x19,
- 0x37, 0x38
- };
- static char *specials[] = {
- "Rear Panel", "Drive Bar",
- "Riser", "HDMI", "ATAPI",
- "Mobile-In", "Mobile-Out"
- };
- int i;
- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
- if ((cfg & 0x0f) < 7)
- return bases[cfg & 0x0f];
- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
- if (cfg == specials_idx[i])
- return specials[i];
- }
- return "UNKNOWN";
-}
-
static const char *get_jack_connection(u32 cfg)
{
static char *names[16] = {
@@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
int *supports_vref)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
- static char *jack_types[16] = {
- "Line Out", "Speaker", "HP Out", "CD",
- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
- "Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digitial In", "Reserved", "Other"
- };
- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
@@ -274,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
- jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
- jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
- get_jack_location(caps));
+ snd_hda_get_jack_type(caps),
+ snd_hda_get_jack_connectivity(caps),
+ snd_hda_get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
@@ -457,17 +414,6 @@ static void print_conn_list(struct snd_info_buffer *buffer,
}
}
-static void print_realtek_coef(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
-{
- int coeff = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff);
- coeff = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_COEF_INDEX, 0);
- snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff);
-}
-
static void print_gpio(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
@@ -500,12 +446,13 @@ static void print_gpio(struct snd_info_buffer *buffer,
for (i = 0; i < max; ++i)
snd_iprintf(buffer,
" IO[%d]: enable=%d, dir=%d, wake=%d, "
- "sticky=%d, data=%d\n", i,
+ "sticky=%d, data=%d, unsol=%d\n", i,
(enable & (1<<i)) ? 1 : 0,
(direction & (1<<i)) ? 1 : 0,
(wake & (1<<i)) ? 1 : 0,
(sticky & (1<<i)) ? 1 : 0,
- (data & (1<<i)) ? 1 : 0);
+ (data & (1<<i)) ? 1 : 0,
+ (unsol & (1<<i)) ? 1 : 0);
/* FIXME: add GPO and GPI pin information */
}
@@ -513,12 +460,11 @@ static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct hda_codec *codec = entry->private_data;
- char buf[32];
hda_nid_t nid;
int i, nodes;
- snd_hda_get_codec_name(codec, buf, sizeof(buf));
- snd_iprintf(buffer, "Codec: %s\n", buf);
+ snd_iprintf(buffer, "Codec: %s\n",
+ codec->name ? codec->name : "Not Set");
snd_iprintf(buffer, "Address: %d\n", codec->addr);
snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
@@ -547,6 +493,8 @@ static void print_codec_info(struct snd_info_entry *entry,
}
print_gpio(buffer, codec, codec->afg);
+ if (codec->proc_widget_hook)
+ codec->proc_widget_hook(buffer, codec, codec->afg);
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
@@ -649,9 +597,8 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_caps & AC_WCAP_PROC_WID)
print_proc_caps(buffer, codec, nid);
- /* NID 0x20 == Realtek Define Registers */
- if (codec->vendor_id == 0x10ec && nid == 0x20)
- print_realtek_coef(buffer, codec, nid);
+ if (codec->proc_widget_hook)
+ codec->proc_widget_hook(buffer, codec, nid);
}
snd_hda_power_down(codec);
}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 686c77491dea..26247cfe749d 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -27,7 +27,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
struct ad198x_spec {
struct snd_kcontrol_new *mixers[5];
@@ -67,8 +66,7 @@ struct ad198x_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = {
NULL
};
+static void ad198x_free_kctls(struct hda_codec *codec);
+
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
return err;
}
+ ad198x_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
return 0;
}
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_free_kctls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
- unsigned int i;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
}
+ snd_array_free(&spec->kctls);
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (!spec)
+ return;
+
+ ad198x_free_kctls(codec);
kfree(codec->spec);
}
@@ -629,6 +641,36 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "External Amplifier",
+ .info = ad198x_eapd_info,
+ .get = ad198x_eapd_get,
+ .put = ad198x_eapd_put,
+ .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+ },
+ { } /* end */
+};
+
+static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
+ HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+ HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
@@ -917,6 +959,7 @@ enum {
AD1986A_LAPTOP_EAPD,
AD1986A_LAPTOP_AUTOMUTE,
AD1986A_ULTRA,
+ AD1986A_SAMSUNG,
AD1986A_MODELS
};
@@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
[AD1986A_LAPTOP_EAPD] = "laptop-eapd",
[AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
[AD1986A_ULTRA] = "ultra",
+ [AD1986A_SAMSUNG] = "samsung",
};
static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
@@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
@@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec)
break;
case AD1986A_LAPTOP_EAPD:
spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+ spec->num_init_verbs = 2;
+ spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+ if (!is_jack_available(codec, 0x25))
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+ break;
+ case AD1986A_SAMSUNG:
+ spec->mixers[0] = ad1986a_samsung_mixers;
spec->num_init_verbs = 3;
spec->init_verbs[1] = ad1986a_eapd_init_verbs;
spec->init_verbs[2] = ad1986a_automic_verbs;
@@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
* Automatic parse of I/O pins from the BIOS configuration
*/
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
enum {
AD_CTL_WIDGET_VOL,
AD_CTL_WIDGET_MUTE,
@@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
- if (! knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = ad1988_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (! knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = AD1988_SPDIF_IN;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
@@ -3861,6 +3901,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
+ SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
@@ -4267,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_analog[] = {
+static struct hda_codec_preset snd_hda_preset_analog[] = {
{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
@@ -4285,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:11d4*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_preset_list analog_list = {
+ .preset = snd_hda_preset_analog,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_analog_init(void)
+{
+ return snd_hda_add_codec_preset(&analog_list);
+}
+
+static void __exit patch_analog_exit(void)
+{
+ snd_hda_delete_codec_preset(&analog_list);
+}
+
+module_init(patch_analog_init)
+module_exit(patch_analog_exit)
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index ba61575983fd..233e4778bba9 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -27,7 +27,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
struct atihdmi_spec {
struct hda_multi_out multiout;
@@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_atihdmi[] = {
- { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+ { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
+ { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
- { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
+
+static struct hda_codec_preset_list atihdmi_list = {
+ .preset = snd_hda_preset_atihdmi,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_atihdmi_init(void)
+{
+ return snd_hda_add_codec_preset(&atihdmi_list);
+}
+
+static void __exit patch_atihdmi_exit(void)
+{
+ snd_hda_delete_codec_preset(&atihdmi_list);
+}
+
+module_init(patch_atihdmi_init)
+module_exit(patch_atihdmi_exit)
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 6ef57fbfb6eb..f3ebe837f2d5 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -28,7 +28,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
#define NUM_PINS 11
@@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_cmedia[] = {
+static struct hda_codec_preset snd_hda_preset_cmedia[] = {
{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:13f69880");
+MODULE_ALIAS("snd-hda-codec-id:434d4980");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("C-Media HD-audio codec");
+
+static struct hda_codec_preset_list cmedia_list = {
+ .preset = snd_hda_preset_cmedia,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_cmedia_init(void)
+{
+ return snd_hda_add_codec_preset(&cmedia_list);
+}
+
+static void __exit patch_cmedia_exit(void)
+{
+ snd_hda_delete_codec_preset(&cmedia_list);
+}
+
+module_init(patch_cmedia_init)
+module_exit(patch_cmedia_exit)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 7c1eb23f0cec..b20e1cede00b 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -27,7 +27,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
#define CXT_PIN_DIR_IN 0x00
#define CXT_PIN_DIR_OUT 0x01
@@ -86,8 +85,6 @@ struct conexant_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -344,15 +341,6 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
- struct conexant_spec *spec = codec->spec;
- unsigned int i;
-
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
kfree(codec->spec);
}
@@ -1782,7 +1770,7 @@ static int patch_cxt5051(struct hda_codec *codec)
/*
*/
-struct hda_codec_preset snd_hda_preset_conexant[] = {
+static struct hda_codec_preset snd_hda_preset_conexant[] = {
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
.patch = patch_cxt5045 },
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
@@ -1791,3 +1779,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5051 },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:14f15045");
+MODULE_ALIAS("snd-hda-codec-id:14f15047");
+MODULE_ALIAS("snd-hda-codec-id:14f15051");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Conexant HD-audio codec");
+
+static struct hda_codec_preset_list conexant_list = {
+ .preset = snd_hda_preset_conexant,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_conexant_init(void)
+{
+ return snd_hda_add_codec_preset(&conexant_list);
+}
+
+static void __exit patch_conexant_exit(void)
+{
+ snd_hda_delete_codec_preset(&conexant_list);
+}
+
+module_init(patch_conexant_init)
+module_exit(patch_conexant_exit)
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
new file mode 100644
index 000000000000..3564f4e4b74c
--- /dev/null
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -0,0 +1,711 @@
+/*
+ *
+ * patch_intelhdmi.c - Patch for Intel HDMI codecs
+ *
+ * Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ * Authors:
+ * Jiang Zhe <zhe.jiang@intel.com>
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * Maintained by:
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define CVT_NID 0x02 /* audio converter */
+#define PIN_NID 0x03 /* HDMI output pin */
+
+#define INTEL_HDMI_EVENT_TAG 0x08
+
+struct intel_hdmi_spec {
+ struct hda_multi_out multiout;
+ struct hda_pcm pcm_rec;
+ struct hdmi_eld sink_eld;
+};
+
+static struct hda_verb pinout_enable_verb[] = {
+ {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {} /* terminator */
+};
+
+static struct hda_verb pinout_disable_verb[] = {
+ {PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00},
+ {}
+};
+
+static struct hda_verb unsolicited_response_verb[] = {
+ {PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
+ INTEL_HDMI_EVENT_TAG},
+ {}
+};
+
+static struct hda_verb def_chan_map[] = {
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
+ {CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
+ {}
+};
+
+
+struct hdmi_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 ver; /* 0x01 */
+ u8 len; /* 0x0a */
+
+ u8 checksum; /* PB0 */
+ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+ u8 reserved[5]; /* PB6 - PB10 */
+};
+
+/*
+ * CEA speaker placement:
+ *
+ * FLH FCH FRH
+ * FLW FL FLC FC FRC FR FRW
+ *
+ * LFE
+ * TC
+ *
+ * RL RLC RC RRC RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+ FL = (1 << 0), /* Front Left */
+ FC = (1 << 1), /* Front Center */
+ FR = (1 << 2), /* Front Right */
+ FLC = (1 << 3), /* Front Left Center */
+ FRC = (1 << 4), /* Front Right Center */
+ RL = (1 << 5), /* Rear Left */
+ RC = (1 << 6), /* Rear Center */
+ RR = (1 << 7), /* Rear Right */
+ RLC = (1 << 8), /* Rear Left Center */
+ RRC = (1 << 9), /* Rear Right Center */
+ LFE = (1 << 10), /* Low Frequency Effect */
+ FLW = (1 << 11), /* Front Left Wide */
+ FRW = (1 << 12), /* Front Right Wide */
+ FLH = (1 << 13), /* Front Left High */
+ FCH = (1 << 14), /* Front Center High */
+ FRH = (1 << 15), /* Front Right High */
+ TC = (1 << 16), /* Top Center */
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+ [0] = FL | FR,
+ [1] = LFE,
+ [2] = FC,
+ [3] = RL | RR,
+ [4] = RC,
+ [5] = FLC | FRC,
+ [6] = RLC | RRC,
+ /* the following are not defined in ELD yet */
+ [7] = FLW | FRW,
+ [8] = FLH | FRH,
+ [9] = TC,
+ [10] = FCH,
+};
+
+struct cea_channel_speaker_allocation {
+ int ca_index;
+ int speakers[8];
+
+ /* derived values, just for convenience */
+ int channels;
+ int spk_mask;
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_setup_channel_allocation().
+ */
+static struct cea_channel_speaker_allocation channel_allocations[] = {
+/* channel: 8 7 6 5 4 3 2 1 */
+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
+ /* 2.1 */
+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
+ /* Dolby Surround */
+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
+ /* 5.1 */
+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
+ /* 6.1 */
+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
+ /* 7.1 */
+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
+};
+
+/*
+ * HDMI routines
+ */
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+ int *packet_index, int *byte_index)
+{
+ int val;
+
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+ *packet_index = val >> 5;
+ *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char val)
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec)
+{
+ /* Enable Audio InfoFrame Transmission */
+ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+ snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_BEST);
+ /* Unmute */
+ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, PIN_NID, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ /* Enable pin out */
+ snd_hda_sequence_write(codec, pinout_enable_verb);
+}
+
+static void hdmi_disable_output(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, pinout_disable_verb);
+ if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, PIN_NID, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ /*
+ * FIXME: noises may arise when playing music after reloading the
+ * kernel module, until the next X restart or monitor repower.
+ */
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec)
+{
+ return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+ AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+{
+ snd_hda_codec_write(codec, CVT_NID, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+
+ if (chs != hdmi_get_channel_count(codec))
+ snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
+ chs, hdmi_get_channel_count(codec));
+}
+
+static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int slot;
+
+ for (i = 0; i < 8; i++) {
+ slot = snd_hda_codec_read(codec, CVT_NID, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT, i);
+ printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
+ slot >> 4, slot & 0x7);
+ }
+#endif
+}
+
+static void hdmi_parse_eld(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->sink_eld;
+
+ if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+ snd_hdmi_show_eld(eld);
+}
+
+
+/*
+ * Audio InfoFrame routines
+ */
+
+static void hdmi_debug_dip_size(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int size;
+
+ size = snd_hdmi_get_eld_size(codec, PIN_NID);
+ printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, PIN_NID, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+ }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+{
+#ifdef BE_PARANOID
+ int i, j;
+ int size;
+ int pi, bi;
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, PIN_NID, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ if (size == 0)
+ continue;
+
+ hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+ for (j = 1; j < 1000; j++) {
+ hdmi_write_dip_byte(codec, PIN_NID, 0x0);
+ hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+ if (pi != i)
+ snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+ bi, pi, i);
+ if (bi == 0) /* byte index wrapped around */
+ break;
+ }
+ snd_printd(KERN_INFO
+ "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+ i, size, j);
+ }
+#endif
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_audio_infoframe *ai)
+{
+ u8 *params = (u8 *)ai;
+ int i;
+
+ hdmi_debug_dip_size(codec);
+ hdmi_clear_dip_buffers(codec); /* be paranoid */
+
+ hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+ for (i = 0; i < sizeof(ai); i++)
+ hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+}
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+ int i, j;
+ struct cea_channel_speaker_allocation *p;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ p = channel_allocations + i;
+ p->channels = 0;
+ p->spk_mask = 0;
+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+ if (p->speakers[j]) {
+ p->channels++;
+ p->spk_mask |= p->speakers[j];
+ }
+ }
+}
+
+/*
+ * The transformation takes two steps:
+ *
+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ * spk_mask => (channel_allocations[]) => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+ struct hdmi_audio_infoframe *ai)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->sink_eld;
+ int i;
+ int spk_mask = 0;
+ int channels = 1 + (ai->CC02_CT47 & 0x7);
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ /*
+ * CA defaults to 0 for basic stereo audio
+ */
+ if (!eld->eld_ver)
+ return 0;
+ if (!eld->spk_alloc)
+ return 0;
+ if (channels <= 2)
+ return 0;
+
+ /*
+ * expand ELD's speaker allocation mask
+ *
+ * ELD tells the speaker mask in a compact(paired) form,
+ * expand ELD's notions to match the ones used by Audio InfoFrame.
+ */
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (eld->spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ /* search for the first working match in the CA table */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask) {
+ ai->CA = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+
+ snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+ snd_printdd(KERN_INFO
+ "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+ ai->CA, channels, buf);
+
+ return ai->CA;
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+ struct hdmi_audio_infoframe *ai)
+{
+ if (!ai->CA)
+ return;
+
+ /*
+ * TODO: adjust channel mapping if necessary
+ * ALSA sequence is front/surr/clfe/side?
+ */
+
+ snd_hda_sequence_write(codec, def_chan_map);
+ hdmi_debug_channel_mapping(codec);
+}
+
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_audio_infoframe ai = {
+ .type = 0x84,
+ .ver = 0x01,
+ .len = 0x0a,
+ .CC02_CT47 = substream->runtime->channels - 1,
+ };
+
+ hdmi_setup_channel_allocation(codec, &ai);
+ hdmi_setup_channel_mapping(codec, &ai);
+
+ hdmi_fill_audio_infoframe(codec, &ai);
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int pind = !!(res & AC_UNSOL_RES_PD);
+ int eldv = !!(res & AC_UNSOL_RES_ELDV);
+
+ printk(KERN_INFO
+ "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
+ pind, eldv);
+
+ if (pind && eldv) {
+ hdmi_parse_eld(codec);
+ /* TODO: do real things about ELD */
+ }
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+ int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+ printk(KERN_INFO
+ "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ subtag,
+ cp_state,
+ cp_ready);
+
+ /* TODO */
+ if (cp_state)
+ ;
+ if (cp_ready)
+ ;
+}
+
+
+static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+ if (tag != INTEL_HDMI_EVENT_TAG) {
+ snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+ return;
+ }
+
+ if (subtag == 0)
+ hdmi_intrinsic_event(codec, res);
+ else
+ hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ hdmi_disable_output(codec);
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+
+ hdmi_set_channel_count(codec, substream->runtime->channels);
+
+ hdmi_setup_audio_infoframe(codec, substream);
+
+ hdmi_enable_output(codec);
+
+ return 0;
+}
+
+static struct hda_pcm_stream intel_hdmi_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = CVT_NID, /* NID to query formats and rates and setup streams */
+ .ops = {
+ .open = intel_hdmi_playback_pcm_open,
+ .close = intel_hdmi_playback_pcm_close,
+ .prepare = intel_hdmi_playback_pcm_prepare
+ },
+};
+
+static int intel_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "INTEL HDMI";
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+
+ return 0;
+}
+
+static int intel_hdmi_build_controls(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int intel_hdmi_init(struct hda_codec *codec)
+{
+ /* disable audio output as early as possible */
+ hdmi_disable_output(codec);
+
+ snd_hda_sequence_write(codec, unsolicited_response_verb);
+
+ return 0;
+}
+
+static void intel_hdmi_free(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec = codec->spec;
+
+ snd_hda_eld_proc_free(codec, &spec->sink_eld);
+ kfree(spec);
+}
+
+static struct hda_codec_ops intel_hdmi_patch_ops = {
+ .init = intel_hdmi_init,
+ .free = intel_hdmi_free,
+ .build_pcms = intel_hdmi_build_pcms,
+ .build_controls = intel_hdmi_build_controls,
+ .unsol_event = intel_hdmi_unsol_event,
+};
+
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+ struct intel_hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 8;
+ spec->multiout.dig_out_nid = CVT_NID;
+
+ codec->spec = spec;
+ codec->patch_ops = intel_hdmi_patch_ops;
+
+ snd_hda_eld_proc_new(codec, &spec->sink_eld);
+
+ init_channel_allocations();
+
+ return 0;
+}
+
+static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
+ { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi },
+ { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
+ { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
+ { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
+ { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
+ {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
+
+static struct hda_codec_preset_list intel_list = {
+ .preset = snd_hda_preset_intelhdmi,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_intelhdmi_init(void)
+{
+ return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_intelhdmi_exit(void)
+{
+ snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_intelhdmi_init)
+module_exit(patch_intelhdmi_exit)
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index 2eed2c8b98da..0270fda0bda5 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -158,8 +158,34 @@ static int patch_nvhdmi(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
- { .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
- { .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
+static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+ { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
+ { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
+ { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+
+static struct hda_codec_preset_list nvhdmi_list = {
+ .preset = snd_hda_preset_nvhdmi,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_nvhdmi_init(void)
+{
+ return snd_hda_add_codec_preset(&nvhdmi_list);
+}
+
+static void __exit patch_nvhdmi_exit(void)
+{
+ snd_hda_delete_codec_preset(&nvhdmi_list);
+}
+
+module_init(patch_nvhdmi_init)
+module_exit(patch_nvhdmi_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a378c0145125..0bd4e6bf354d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -30,7 +30,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
#define ALC880_FRONT_EVENT 0x01
#define ALC880_DCVOL_EVENT 0x02
@@ -114,6 +113,7 @@ enum {
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
+ ALC268_ACER_DMIC,
ALC268_ACER_ASPIRE_ONE,
ALC268_DELL,
ALC268_ZEPTO,
@@ -130,6 +130,8 @@ enum {
ALC269_QUANTA_FL1,
ALC269_ASUS_EEEPC_P703,
ALC269_ASUS_EEEPC_P901,
+ ALC269_FUJITSU,
+ ALC269_LIFEBOOK,
ALC269_AUTO,
ALC269_MODEL_LAST /* last tag */
};
@@ -152,6 +154,7 @@ enum {
enum {
ALC660VD_3ST,
ALC660VD_3ST_DIG,
+ ALC660VD_ASUS_V1S,
ALC861VD_3ST,
ALC861VD_3ST_DIG,
ALC861VD_6ST_DIG,
@@ -212,6 +215,7 @@ enum {
ALC883_TARGA_2ch_DIG,
ALC883_ACER,
ALC883_ACER_ASPIRE,
+ ALC888_ACER_ASPIRE_4930G,
ALC883_MEDION,
ALC883_MEDION_MD2,
ALC883_LAPTOP_EAPD,
@@ -225,9 +229,11 @@ enum {
ALC883_MITAC,
ALC883_CLEVO_M720,
ALC883_FUJITSU_PI2515,
+ ALC888_FUJITSU_XA3530,
ALC883_3ST_6ch_INTEL,
ALC888_ASUS_M90V,
ALC888_ASUS_EEE1601,
+ ALC1200_ASUS_P5Q,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -239,6 +245,7 @@ struct alc_spec {
/* codec parameterization */
struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
+ struct snd_kcontrol_new *cap_mixer; /* capture mixer */
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
@@ -268,6 +275,7 @@ struct alc_spec {
hda_nid_t *adc_nids;
hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ unsigned char is_mix_capture; /* matrix-style capture (non-mux) */
/* capture source */
unsigned int num_mux_defs;
@@ -284,8 +292,7 @@ struct alc_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -323,6 +330,7 @@ struct alc_config_preset {
struct snd_kcontrol_new *mixers[5]; /* should be identical size
* with spec
*/
+ struct snd_kcontrol_new *cap_mixer; /* capture mixer */
const struct hda_verb *init_verbs[5];
unsigned int num_dacs;
hda_nid_t *dac_nids;
@@ -375,14 +383,39 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+ unsigned int mux_idx;
hda_nid_t nid = spec->capsrc_nids ?
spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
- nid, &spec->cur_mux[adc_idx]);
-}
+ mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+ imux = &spec->input_mux[mux_idx];
+
+ if (spec->is_mix_capture) {
+ /* Matrix-mixer style (e.g. ALC882) */
+ unsigned int *cur_val = &spec->cur_mux[adc_idx];
+ unsigned int i, idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx)
+ return 0;
+ for (i = 0; i < imux->num_items; i++) {
+ unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
+ imux->items[i].index,
+ HDA_AMP_MUTE, v);
+ }
+ *cur_val = idx;
+ return 1;
+ } else {
+ /* MUX style (e.g. ALC880) */
+ return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
+ &spec->cur_mux[adc_idx]);
+ }
+}
/*
* channel mode setting
@@ -717,6 +750,43 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
#endif /* CONFIG_SND_DEBUG */
/*
+ */
+static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+{
+ if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
+ return;
+ spec->mixers[spec->num_mixers++] = mix;
+}
+
+static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
+{
+ if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
+ return;
+ spec->init_verbs[spec->num_init_verbs++] = verb;
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * hook for proc
+ */
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int coeff;
+
+ if (nid != 0x20)
+ return;
+ coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+ snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff);
+ coeff = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_COEF_INDEX, 0);
+ snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff);
+}
+#else
+#define print_realtek_coef NULL
+#endif
+
+/*
* set up from the preset table
*/
static void setup_preset(struct alc_spec *spec,
@@ -725,11 +795,11 @@ static void setup_preset(struct alc_spec *spec,
int i;
for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
- spec->mixers[spec->num_mixers++] = preset->mixers[i];
+ add_mixer(spec, preset->mixers[i]);
+ spec->cap_mixer = preset->cap_mixer;
for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
i++)
- spec->init_verbs[spec->num_init_verbs++] =
- preset->init_verbs[i];
+ add_verb(spec, preset->init_verbs[i]);
spec->channel_mode = preset->channel_mode;
spec->num_channel_mode = preset->num_channel_mode;
@@ -1107,6 +1177,226 @@ static void alc_fix_pincfg(struct hda_codec *codec,
}
/*
+ * ALC888
+ */
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc888_4ST_ch2_intel_init[] = {
+/* Mic-in jack as mic in */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Line in */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-Out as Front */
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc888_4ST_ch4_intel_init[] = {
+/* Mic-in jack as mic in */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Surround */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Front */
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc888_4ST_ch6_intel_init[] = {
+/* Mic-in jack as CLFE */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+ { } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc888_4ST_ch8_intel_init[] = {
+/* Mic-in jack as CLFE */
+ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Side */
+ { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+ { } /* end */
+};
+
+static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
+ { 2, alc888_4ST_ch2_intel_init },
+ { 4, alc888_4ST_ch4_intel_init },
+ { 6, alc888_4ST_ch6_intel_init },
+ { 8, alc888_4ST_ch8_intel_init },
+};
+
+/*
+ * ALC888 Fujitsu Siemens Amillo xa3530
+ */
+
+static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Connect Internal HP to Front */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Bass HP to Front */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Line-Out side jack (SPDIF) to Side */
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+/* Connect Mic jack to CLFE */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect Line-in jack to Surround */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect HP out jack to Front */
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Enable unsolicited event for HP jack and Line-out jack */
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {}
+};
+
+static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned int bits;
+ /* Line out presence */
+ present = snd_hda_codec_read(codec, 0x17, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ /* HP out presence */
+ present = present || snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? HDA_AMP_MUTE : 0;
+ /* Toggle internal speakers muting */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+ /* Toggle internal bass muting */
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if (res >> 26 == ALC880_HP_EVENT)
+ alc888_fujitsu_xa3530_automute(codec);
+}
+
+
+/*
+ * ALC888 Acer Aspire 4930G model
+ */
+
+static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Unselect Front Mic by default in input mixer 3 */
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
+/* Enable unsolicited event for HP jack */
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Connect Internal HP to front */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect HP out to front */
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ { }
+};
+
+static struct hda_input_mux alc888_2_capture_sources[2] = {
+ /* Front mic only available on one ADC */
+ {
+ .num_items = 4,
+ .items = {
+ { "Mic", 0x0 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ { "Front Mic", 0xb },
+ },
+ },
+ {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Line", 0x2 },
+ { "CD", 0x4 },
+ },
+ }
+};
+
+static struct snd_kcontrol_new alc888_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ { } /* end */
+};
+
+static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned int bits;
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, bits);
+}
+
+static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if (res >> 26 == ALC880_HP_EVENT)
+ alc888_acer_aspire_4930g_automute(codec);
+}
+
+/*
* ALC880 3-stack model
*
* DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
@@ -1205,49 +1495,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
};
/* capture mixer elements */
-static struct snd_kcontrol_new alc880_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
+static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int err;
-/* capture mixer elements (in case NID 0x07 not available) */
-static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+ HDA_INPUT);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int err;
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+ HDA_INPUT);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ getput_call_t func)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int err;
+
+ mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
+ 3, 0, HDA_INPUT);
+ err = func(kcontrol, ucontrol);
+ mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+ return err;
+}
+
+static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_get);
+}
+
+static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put);
+}
+
+/* capture mixer elements */
+#define alc_cap_sw_info snd_ctl_boolean_stereo_info
+
+static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_get);
+}
+
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put);
+}
+
+#define DEFINE_CAPMIX(num) \
+static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Switch", \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .count = num, \
+ .info = alc_cap_sw_info, \
+ .get = alc_cap_sw_get, \
+ .put = alc_cap_sw_put, \
+ }, \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Volume", \
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
+ .count = num, \
+ .info = alc_cap_vol_info, \
+ .get = alc_cap_vol_get, \
+ .put = alc_cap_vol_put, \
+ .tlv = { .c = alc_cap_vol_tlv }, \
+ }, \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ /* .name = "Capture Source", */ \
+ .name = "Input Source", \
+ .count = num, \
+ .info = alc_mux_enum_info, \
+ .get = alc_mux_enum_get, \
+ .put = alc_mux_enum_put, \
+ }, \
+ { } /* end */ \
+}
+
+/* up to three ADCs */
+DEFINE_CAPMIX(1);
+DEFINE_CAPMIX(2);
+DEFINE_CAPMIX(3);
/*
@@ -1533,18 +1900,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -1619,6 +1974,7 @@ static const char *alc_slave_vols[] = {
"Speaker Playback Volume",
"Mono Playback Volume",
"Line-Out Playback Volume",
+ "PCM Playback Volume",
NULL,
};
@@ -1638,6 +1994,9 @@ static const char *alc_slave_sws[] = {
/*
* build control elements
*/
+
+static void alc_free_kctls(struct hda_codec *codec);
+
static int alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -1649,7 +2008,11 @@ static int alc_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
-
+ if (spec->cap_mixer) {
+ err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
+ if (err < 0)
+ return err;
+ }
if (spec->multiout.dig_out_nid) {
err = snd_hda_create_spdif_out_ctls(codec,
spec->multiout.dig_out_nid);
@@ -1684,6 +2047,7 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
+ alc_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -2774,19 +3138,27 @@ static int alc_build_pcms(struct hda_codec *codec)
return 0;
}
+static void alc_free_kctls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
static void alc_free(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int i;
if (!spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
+ alc_free_kctls(codec);
kfree(spec);
codec->spec = NULL; /* to be sure */
}
@@ -3268,6 +3640,8 @@ static struct alc_config_preset alc880_presets[] = {
alc880_gpio2_init_verbs },
.num_dacs = ARRAY_SIZE(alc880_dac_nids),
.dac_nids = alc880_dac_nids,
+ .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
+ .num_adc_nids = 1, /* single ADC */
.hp_nid = 0x03,
.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
.channel_mode = alc880_2_jack_modes,
@@ -3532,9 +3906,6 @@ static struct alc_config_preset alc880_presets[] = {
* Automatic parse of I/O pins from the BIOS configuration
*/
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
enum {
ALC_CTL_WIDGET_VOL,
ALC_CTL_WIDGET_MUTE,
@@ -3552,29 +3923,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- /* array + terminator */
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
- if (!knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc,
- sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = alc880_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -3898,10 +4255,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC880_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
+ add_verb(spec, alc880_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -3925,6 +4282,17 @@ static void alc880_auto_init(struct hda_codec *codec)
* OK, here we have finally the patch for ALC880
*/
+static void set_capture_mixer(struct alc_spec *spec)
+{
+ static struct snd_kcontrol_new *caps[3] = {
+ alc_capture_mixer1,
+ alc_capture_mixer2,
+ alc_capture_mixer3,
+ };
+ if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3)
+ spec->cap_mixer = caps[spec->num_adc_nids - 1];
+}
+
static int patch_alc880(struct hda_codec *codec)
{
struct alc_spec *spec;
@@ -3980,16 +4348,12 @@ static int patch_alc880(struct hda_codec *codec)
if (wcap != AC_WID_AUD_IN) {
spec->adc_nids = alc880_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
- alc880_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc880_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
- spec->mixers[spec->num_mixers] = alc880_capture_mixer;
- spec->num_mixers++;
}
}
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -4000,6 +4364,7 @@ static int patch_alc880(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc880_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -4024,11 +4389,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = {
0x05,
};
-static hda_nid_t alc260_hp_adc_nids[2] = {
- /* ADC1, 0 */
- 0x05, 0x04
-};
-
/* NIDs used when simultaneous access to both ADCs makes sense. Note that
* alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
*/
@@ -4157,13 +4517,13 @@ static void alc260_hp_master_update(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
unsigned int val = spec->master_sw ? PIN_HP : 0;
/* change HP and line-out pins */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
/* mono (speaker) depending on the HP jack sense */
val = (val && !spec->jack_present) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
val);
}
@@ -4242,7 +4602,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
.info = snd_ctl_boolean_mono_info,
.get = alc260_hp_master_sw_get,
.put = alc260_hp_master_sw_put,
- .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+ .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
},
HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
@@ -4295,7 +4655,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec)
present = snd_hda_codec_read(codec, 0x15, 0,
AC_VERB_GET_PIN_SENSE, 0);
spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
- alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+ alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
}
static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
@@ -4427,45 +4787,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
{ } /* end */
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc260_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
/*
* initialization verbs
*/
@@ -5282,7 +5603,6 @@ static struct hda_verb alc260_volume_init_verbs[] = {
static int alc260_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- unsigned int wcap;
int err;
static hda_nid_t alc260_ignore[] = { 0x17, 0 };
@@ -5293,7 +5613,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- if (!spec->kctl_alloc)
+ if (!spec->kctls.list)
return 0; /* can't find valid BIOS pin config */
err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
if (err < 0)
@@ -5303,28 +5623,14 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+ add_verb(spec, alc260_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
- /* check whether NID 0x04 is valid */
- wcap = get_wcaps(codec, 0x04);
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc260_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
- spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
- } else {
- spec->adc_nids = alc260_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- spec->mixers[spec->num_mixers] = alc260_capture_mixer;
- }
- spec->num_mixers++;
-
store_pin_configs(codec);
return 1;
}
@@ -5394,12 +5700,11 @@ static struct alc_config_preset alc260_presets[] = {
[ALC260_BASIC] = {
.mixers = { alc260_base_output_mixer,
alc260_input_mixer,
- alc260_pc_beep_mixer,
- alc260_capture_mixer },
+ alc260_pc_beep_mixer },
.init_verbs = { alc260_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+ .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
.adc_nids = alc260_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
@@ -5407,14 +5712,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP] = {
.mixers = { alc260_hp_output_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_init_verbs,
alc260_hp_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5423,14 +5727,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP_DC7600] = {
.mixers = { alc260_hp_dc7600_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_init_verbs,
alc260_hp_dc7600_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5439,14 +5742,13 @@ static struct alc_config_preset alc260_presets[] = {
},
[ALC260_HP_3013] = {
.mixers = { alc260_hp_3013_mixer,
- alc260_input_mixer,
- alc260_capture_alt_mixer },
+ alc260_input_mixer },
.init_verbs = { alc260_hp_3013_init_verbs,
alc260_hp_3013_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
- .adc_nids = alc260_hp_adc_nids,
+ .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+ .adc_nids = alc260_adc_nids_alt,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
@@ -5454,8 +5756,7 @@ static struct alc_config_preset alc260_presets[] = {
.init_hook = alc260_hp_3013_automute,
},
[ALC260_FUJITSU_S702X] = {
- .mixers = { alc260_fujitsu_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_fujitsu_mixer },
.init_verbs = { alc260_fujitsu_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5467,8 +5768,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = alc260_fujitsu_capture_sources,
},
[ALC260_ACER] = {
- .mixers = { alc260_acer_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_acer_mixer },
.init_verbs = { alc260_acer_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5480,8 +5780,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = alc260_acer_capture_sources,
},
[ALC260_WILL] = {
- .mixers = { alc260_will_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_will_mixer },
.init_verbs = { alc260_init_verbs, alc260_will_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5493,8 +5792,7 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = &alc260_capture_source,
},
[ALC260_REPLACER_672V] = {
- .mixers = { alc260_replacer_672v_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_replacer_672v_mixer },
.init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
@@ -5509,8 +5807,7 @@ static struct alc_config_preset alc260_presets[] = {
},
#ifdef CONFIG_SND_DEBUG
[ALC260_TEST] = {
- .mixers = { alc260_test_mixer,
- alc260_capture_mixer },
+ .mixers = { alc260_test_mixer },
.init_verbs = { alc260_test_init_verbs },
.num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
.dac_nids = alc260_test_dac_nids,
@@ -5569,6 +5866,21 @@ static int patch_alc260(struct hda_codec *codec)
spec->stream_digital_playback = &alc260_pcm_digital_playback;
spec->stream_digital_capture = &alc260_pcm_digital_capture;
+ if (!spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x04 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x04);
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ /* get type */
+ if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+ spec->adc_nids = alc260_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+ } else {
+ spec->adc_nids = alc260_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+ }
+ }
+ set_capture_mixer(spec);
+
spec->vmaster_nid = 0x08;
codec->patch_ops = alc_patch_ops;
@@ -5578,6 +5890,7 @@ static int patch_alc260(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc260_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -5625,36 +5938,6 @@ static struct hda_input_mux alc882_capture_source = {
{ "CD", 0x4 },
},
};
-#define alc882_mux_enum_info alc_mux_enum_info
-#define alc882_mux_enum_get alc_mux_enum_get
-
-static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- hda_nid_t nid = spec->capsrc_nids ?
- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
-}
-
/*
* 2ch mode
*/
@@ -6337,49 +6620,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
{ }
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc882_loopbacks alc880_loopbacks
#endif
@@ -6508,8 +6748,7 @@ static struct alc_config_preset alc882_presets[] = {
.init_hook = alc885_imac24_init_hook,
},
[ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer,
- alc882_capture_mixer },
+ .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_targa_verbs},
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
@@ -6525,8 +6764,7 @@ static struct alc_config_preset alc882_presets[] = {
.init_hook = alc882_targa_automute,
},
[ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer,
- alc882_capture_mixer },
+ .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
.init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
.num_dacs = ARRAY_SIZE(alc882_dac_nids),
.dac_nids = alc882_dac_nids,
@@ -6831,6 +7069,7 @@ static int patch_alc882(struct hda_codec *codec)
spec->stream_digital_playback = &alc882_pcm_digital_playback;
spec->stream_digital_capture = &alc882_pcm_digital_capture;
+ spec->is_mix_capture = 1; /* matrix-style capture */
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -6840,17 +7079,13 @@ static int patch_alc882(struct hda_codec *codec)
spec->adc_nids = alc882_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
spec->capsrc_nids = alc882_capsrc_nids_alt;
- spec->mixers[spec->num_mixers] =
- alc882_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc882_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
spec->capsrc_nids = alc882_capsrc_nids;
- spec->mixers[spec->num_mixers] = alc882_capture_mixer;
- spec->num_mixers++;
}
}
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -6861,6 +7096,7 @@ static int patch_alc882(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc882_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -6879,6 +7115,8 @@ static int patch_alc882(struct hda_codec *codec)
#define ALC883_DIGOUT_NID 0x06
#define ALC883_DIGIN_NID 0x0a
+#define ALC1200_DIGOUT_NID 0x10
+
static hda_nid_t alc883_dac_nids[4] = {
/* front, rear, clfe, rear_surr */
0x02, 0x03, 0x04, 0x05
@@ -6889,8 +7127,20 @@ static hda_nid_t alc883_adc_nids[2] = {
0x08, 0x09,
};
+static hda_nid_t alc883_adc_nids_alt[1] = {
+ /* ADC1 */
+ 0x08,
+};
+
+static hda_nid_t alc883_adc_nids_rev[2] = {
+ /* ADC2-1 */
+ 0x09, 0x08
+};
+
static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+
/* input MUX */
/* FIXME: should be a matrix-type input source selection */
@@ -6957,11 +7207,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = {
},
};
-#define alc883_mux_enum_info alc_mux_enum_info
-#define alc883_mux_enum_get alc_mux_enum_get
-/* ALC883 has the ALC882-type input selection */
-#define alc883_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -7115,19 +7360,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7145,19 +7377,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = {
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7172,19 +7391,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7199,19 +7405,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7231,19 +7424,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7269,17 +7449,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7306,19 +7475,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7344,18 +7500,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7376,19 +7520,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7404,19 +7535,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7429,17 +7547,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7453,19 +7560,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7479,19 +7573,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7504,19 +7585,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7544,19 +7612,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
- },
{ } /* end */
};
@@ -7587,6 +7642,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
{
@@ -7594,9 +7653,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 1,
- .info = alc883_mux_enum_info,
- .get = alc883_mux_enum_get,
- .put = alc883_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -8251,27 +8310,6 @@ static struct hda_verb alc883_auto_init_verbs[] = {
{ }
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc883_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
- },
- { } /* end */
-};
-
static struct hda_verb alc888_asus_m90v_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -8394,6 +8432,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
[ALC883_ACER] = "acer",
[ALC883_ACER_ASPIRE] = "acer-aspire",
+ [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g",
[ALC883_MEDION] = "medion",
[ALC883_MEDION_MD2] = "medion-md2",
[ALC883_LAPTOP_EAPD] = "laptop-eapd",
@@ -8407,7 +8446,9 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_MITAC] = "mitac",
[ALC883_CLEVO_M720] = "clevo-m720",
[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
+ [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
[ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
+ [ALC1200_ASUS_P5Q] = "asus-p5q",
[ALC883_AUTO] = "auto",
};
@@ -8418,6 +8459,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+ ALC888_ACER_ASPIRE_4930G),
SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
@@ -8426,6 +8469,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
@@ -8452,6 +8496,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
@@ -8463,6 +8508,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515),
+ SND_PCI_QUIRK(0x1734, 0x113d, "Fujitsu AMILO Xa3530",
+ ALC888_FUJITSU_XA3530),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
@@ -8553,6 +8600,8 @@ static struct alc_config_preset alc883_presets[] = {
.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.dig_out_nid = ALC883_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
@@ -8586,6 +8635,26 @@ static struct alc_config_preset alc883_presets[] = {
.unsol_event = alc883_acer_aspire_unsol_event,
.init_hook = alc883_acer_aspire_automute,
},
+ [ALC888_ACER_ASPIRE_4930G] = {
+ .mixers = { alc888_base_mixer,
+ alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+ alc888_acer_aspire_4930g_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+ .adc_nids = alc883_adc_nids_rev,
+ .capsrc_nids = alc883_capsrc_nids_rev,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+ .channel_mode = alc883_3ST_6ch_modes,
+ .need_dac_fix = 1,
+ .num_mux_defs =
+ ARRAY_SIZE(alc888_2_capture_sources),
+ .input_mux = alc888_2_capture_sources,
+ .unsol_event = alc888_acer_aspire_4930g_unsol_event,
+ .init_hook = alc888_acer_aspire_4930g_automute,
+ },
[ALC883_MEDION] = {
.mixers = { alc883_fivestack_mixer,
alc883_chmode_mixer },
@@ -8593,6 +8662,8 @@ static struct alc_config_preset alc883_presets[] = {
alc883_medion_eapd_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.input_mux = &alc883_capture_source,
@@ -8635,6 +8706,8 @@ static struct alc_config_preset alc883_presets[] = {
.init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
+ .adc_nids = alc883_adc_nids_alt,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
.channel_mode = alc883_3ST_2ch_modes,
.input_mux = &alc883_lenovo_101e_capture_source,
@@ -8725,14 +8798,30 @@ static struct alc_config_preset alc883_presets[] = {
.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
.init_hook = alc883_2ch_fujitsu_pi2515_automute,
},
+ [ALC888_FUJITSU_XA3530] = {
+ .mixers = { alc888_base_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs,
+ alc888_fujitsu_xa3530_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+ .adc_nids = alc883_adc_nids_rev,
+ .capsrc_nids = alc883_capsrc_nids_rev,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes),
+ .channel_mode = alc888_4ST_8ch_intel_modes,
+ .num_mux_defs =
+ ARRAY_SIZE(alc888_2_capture_sources),
+ .input_mux = alc888_2_capture_sources,
+ .unsol_event = alc888_fujitsu_xa3530_unsol_event,
+ .init_hook = alc888_fujitsu_xa3530_automute,
+ },
[ALC888_LENOVO_SKY] = {
.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
.init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
.dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
.channel_mode = alc883_sixstack_modes,
.need_dac_fix = 1,
@@ -8756,6 +8845,7 @@ static struct alc_config_preset alc883_presets[] = {
},
[ALC888_ASUS_EEE1601] = {
.mixers = { alc883_asus_eee1601_mixer },
+ .cap_mixer = alc883_asus_eee1601_cap_mixer,
.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
.num_dacs = ARRAY_SIZE(alc883_dac_nids),
.dac_nids = alc883_dac_nids,
@@ -8768,6 +8858,17 @@ static struct alc_config_preset alc883_presets[] = {
.unsol_event = alc883_eee1601_unsol_event,
.init_hook = alc883_eee1601_inithook,
},
+ [ALC1200_ASUS_P5Q] = {
+ .mixers = { alc883_base_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC1200_DIGOUT_NID,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ },
};
@@ -8862,8 +8963,6 @@ static int alc883_parse_auto_config(struct hda_codec *codec)
/* hack - override the init verbs */
spec->init_verbs[0] = alc883_auto_init_verbs;
- spec->mixers[spec->num_mixers] = alc883_capture_mixer;
- spec->num_mixers++;
return 1; /* config found */
}
@@ -8946,9 +9045,15 @@ static int patch_alc883(struct hda_codec *codec)
spec->stream_digital_playback = &alc883_pcm_digital_playback;
spec->stream_digital_capture = &alc883_pcm_digital_capture;
- spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
- spec->adc_nids = alc883_adc_nids;
- spec->capsrc_nids = alc883_capsrc_nids;
+ if (!spec->num_adc_nids) {
+ spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
+ spec->adc_nids = alc883_adc_nids;
+ }
+ if (!spec->capsrc_nids)
+ spec->capsrc_nids = alc883_capsrc_nids;
+ spec->is_mix_capture = 1; /* matrix-style capture */
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -8960,6 +9065,7 @@ static int patch_alc883(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc883_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -9439,20 +9545,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -9969,7 +10061,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
struct alc_spec *spec = codec->spec;
int ret;
- ret = alc882_mux_enum_put(kcontrol, ucontrol);
+ ret = alc_mux_enum_put(kcontrol, ucontrol);
if (!ret)
return 0;
/* reprogram the HP pin as mic or HP according to the input source */
@@ -9986,8 +10078,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
.put = alc262_ultra_mux_enum_put,
},
{ } /* end */
@@ -10380,10 +10472,10 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = ALC262_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+ add_verb(spec, alc262_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -10466,6 +10558,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN",
+ ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
ALC262_TOSHIBA_RX1),
SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -10624,7 +10718,8 @@ static struct alc_config_preset alc262_presets[] = {
.init_hook = alc262_hippo_automute,
},
[ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
+ .mixers = { alc262_ultra_mixer },
+ .cap_mixer = alc262_ultra_capture_mixer,
.init_verbs = { alc262_ultra_verbs },
.num_dacs = ARRAY_SIZE(alc262_dac_nids),
.dac_nids = alc262_dac_nids,
@@ -10750,6 +10845,7 @@ static int patch_alc262(struct hda_codec *codec)
spec->stream_digital_playback = &alc262_pcm_digital_playback;
spec->stream_digital_capture = &alc262_pcm_digital_capture;
+ spec->is_mix_capture = 1;
if (!spec->adc_nids && spec->input_mux) {
/* check whether NID 0x07 is valid */
unsigned int wcap = get_wcaps(codec, 0x07);
@@ -10760,17 +10856,14 @@ static int patch_alc262(struct hda_codec *codec)
spec->adc_nids = alc262_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
spec->capsrc_nids = alc262_capsrc_nids_alt;
- spec->mixers[spec->num_mixers] =
- alc262_capture_alt_mixer;
- spec->num_mixers++;
} else {
spec->adc_nids = alc262_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
spec->capsrc_nids = alc262_capsrc_nids;
- spec->mixers[spec->num_mixers] = alc262_capture_mixer;
- spec->num_mixers++;
}
}
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x0c;
@@ -10781,6 +10874,7 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc262_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -10942,6 +11036,22 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
{ }
};
+static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+ { }
+};
+
static struct hda_verb alc268_acer_aspire_one_verbs[] = {
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -11218,10 +11328,6 @@ static struct hda_verb alc268_volume_init_verbs[] = {
{ }
};
-#define alc268_mux_enum_info alc_mux_enum_info
-#define alc268_mux_enum_get alc_mux_enum_get
-#define alc268_mux_enum_put alc_mux_enum_put
-
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
@@ -11233,9 +11339,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 1,
- .info = alc268_mux_enum_info,
- .get = alc268_mux_enum_get,
- .put = alc268_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -11253,9 +11359,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
/* .name = "Capture Source", */
.name = "Input Source",
.count = 2,
- .info = alc268_mux_enum_info,
- .get = alc268_mux_enum_get,
- .put = alc268_mux_enum_put,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
},
{ } /* end */
};
@@ -11274,6 +11380,15 @@ static struct hda_input_mux alc268_acer_capture_source = {
.num_items = 3,
.items = {
{ "Mic", 0x0 },
+ { "Internal Mic", 0x1 },
+ { "Line", 0x2 },
+ },
+};
+
+static struct hda_input_mux alc268_acer_dmic_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
{ "Internal Mic", 0x6 },
{ "Line", 0x2 },
},
@@ -11512,13 +11627,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
if (spec->autocfg.speaker_pins[0] != 0x1d)
- spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
+ add_mixer(spec, alc268_beep_mixer);
- spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+ add_verb(spec, alc268_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -11554,6 +11669,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
+ [ALC268_ACER_DMIC] = "acer-dmic",
[ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
[ALC268_DELL] = "dell",
[ALC268_ZEPTO] = "zepto",
@@ -11649,6 +11765,23 @@ static struct alc_config_preset alc268_presets[] = {
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
+ [ALC268_ACER_DMIC] = {
+ .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
+ alc268_beep_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_acer_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .capsrc_nids = alc268_capsrc_nids,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_acer_dmic_capture_source,
+ .unsol_event = alc268_acer_unsol_event,
+ .init_hook = alc268_acer_init_hook,
+ },
[ALC268_ACER_ASPIRE_ONE] = {
.mixers = { alc268_acer_aspire_one_mixer,
alc268_capture_alt_mixer },
@@ -11787,15 +11920,11 @@ static int patch_alc268(struct hda_codec *codec)
if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
spec->adc_nids = alc268_adc_nids_alt;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
- alc268_capture_alt_mixer;
- spec->num_mixers++;
+ add_mixer(spec, alc268_capture_alt_mixer);
} else {
spec->adc_nids = alc268_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
- spec->mixers[spec->num_mixers] =
- alc268_capture_mixer;
- spec->num_mixers++;
+ add_mixer(spec, alc268_capture_mixer);
}
spec->capsrc_nids = alc268_capsrc_nids;
/* set default input source */
@@ -11811,6 +11940,8 @@ static int patch_alc268(struct hda_codec *codec)
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
+ codec->proc_widget_hook = print_realtek_coef;
+
return 0;
}
@@ -11893,6 +12024,31 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
{ }
};
+static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
+ /* output mixer control */
+ HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_acer_master_sw_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+ },
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ { }
+};
+
/* bind volumes of both NID 0x0c and 0x0d */
static struct hda_bind_ctls alc269_epc_bind_vol = {
.ops = &snd_hda_bind_vol,
@@ -11911,28 +12067,18 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
};
/* capture mixer elements */
-static struct snd_kcontrol_new alc269_capture_mixer[] = {
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
{ } /* end */
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+/* FSC amilo */
+static struct snd_kcontrol_new alc269_fujitsu_mixer[] = {
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_BIND_VOL("PCM Playback Volume", &alc269_epc_bind_vol),
{ } /* end */
};
@@ -11953,6 +12099,20 @@ static struct hda_verb alc269_quanta_fl1_verbs[] = {
{ }
};
+static struct hda_verb alc269_lifebook_verbs[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ { }
+};
+
/* toggle speaker-output according to the hp-jack state */
static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
{
@@ -11978,6 +12138,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
AC_VERB_SET_PROC_COEF, 0x480);
}
+/* toggle speaker-output according to the hp-jacks state */
+static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ /* Check laptop headphone socket */
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ /* Check port replicator headphone socket */
+ present |= snd_hda_codec_read(codec, 0x1a, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ bits = present ? AMP_IN_MUTE(0) : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+ AMP_IN_MUTE(0), bits);
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+ AMP_IN_MUTE(0), bits);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x680);
+
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_COEF_INDEX, 0x0c);
+ snd_hda_codec_write(codec, 0x20, 0,
+ AC_VERB_SET_PROC_COEF, 0x480);
+}
+
static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
{
unsigned int present;
@@ -11988,6 +12179,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
}
+static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+{
+ unsigned int present_laptop;
+ unsigned int present_dock;
+
+ present_laptop = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ present_dock = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+ /* Laptop mic port overrides dock mic port, design decision */
+ if (present_dock)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x3);
+ if (present_laptop)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x0);
+ if (!present_dock && !present_laptop)
+ snd_hda_codec_write(codec, 0x23, 0,
+ AC_VERB_SET_CONNECT_SEL, 0x1);
+}
+
static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
unsigned int res)
{
@@ -11997,12 +12211,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
alc269_quanta_fl1_mic_automute(codec);
}
+static void alc269_lifebook_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc269_lifebook_speaker_automute(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc269_lifebook_mic_autoswitch(codec);
+}
+
static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
{
alc269_quanta_fl1_speaker_automute(codec);
alc269_quanta_fl1_mic_automute(codec);
}
+static void alc269_lifebook_init_hook(struct hda_codec *codec)
+{
+ alc269_lifebook_speaker_automute(codec);
+ alc269_lifebook_mic_autoswitch(codec);
+}
+
static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
@@ -12303,17 +12532,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
/* create a beep mixer control if the pin 0x1d isn't assigned */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
if (spec->autocfg.input_pins[i] == 0x1d)
break;
if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
- spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+ add_mixer(spec, alc269_beep_mixer);
- spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+ add_verb(spec, alc269_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
/* set default input source */
@@ -12325,8 +12554,8 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (err < 0)
return err;
- spec->mixers[spec->num_mixers] = alc269_capture_mixer;
- spec->num_mixers++;
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
store_pin_configs(codec);
return 1;
@@ -12355,7 +12584,9 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
[ALC269_BASIC] = "basic",
[ALC269_QUANTA_FL1] = "quanta",
[ALC269_ASUS_EEEPC_P703] = "eeepc-p703",
- [ALC269_ASUS_EEEPC_P901] = "eeepc-p901"
+ [ALC269_ASUS_EEEPC_P901] = "eeepc-p901",
+ [ALC269_FUJITSU] = "fujitsu",
+ [ALC269_LIFEBOOK] = "lifebook"
};
static struct snd_pci_quirk alc269_cfg_tbl[] = {
@@ -12366,12 +12597,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = {
ALC269_ASUS_EEEPC_P901),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
ALC269_ASUS_EEEPC_P901),
+ SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
{}
};
static struct alc_config_preset alc269_presets[] = {
[ALC269_BASIC] = {
- .mixers = { alc269_base_mixer, alc269_capture_mixer },
+ .mixers = { alc269_base_mixer },
.init_verbs = { alc269_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
.dac_nids = alc269_dac_nids,
@@ -12393,7 +12626,8 @@ static struct alc_config_preset alc269_presets[] = {
.init_hook = alc269_quanta_fl1_init_hook,
},
[ALC269_ASUS_EEEPC_P703] = {
- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+ .mixers = { alc269_eeepc_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
alc269_eeepc_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12406,7 +12640,8 @@ static struct alc_config_preset alc269_presets[] = {
.init_hook = alc269_eeepc_amic_inithook,
},
[ALC269_ASUS_EEEPC_P901] = {
- .mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+ .mixers = { alc269_eeepc_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
.init_verbs = { alc269_init_verbs,
alc269_eeepc_dmic_init_verbs },
.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12418,6 +12653,32 @@ static struct alc_config_preset alc269_presets[] = {
.unsol_event = alc269_eeepc_dmic_unsol_event,
.init_hook = alc269_eeepc_dmic_inithook,
},
+ [ALC269_FUJITSU] = {
+ .mixers = { alc269_fujitsu_mixer, alc269_beep_mixer },
+ .cap_mixer = alc269_epc_capture_mixer,
+ .init_verbs = { alc269_init_verbs,
+ alc269_eeepc_dmic_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_eeepc_dmic_capture_source,
+ .unsol_event = alc269_eeepc_dmic_unsol_event,
+ .init_hook = alc269_eeepc_dmic_inithook,
+ },
+ [ALC269_LIFEBOOK] = {
+ .mixers = { alc269_lifebook_mixer },
+ .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_capture_source,
+ .unsol_event = alc269_lifebook_unsol_event,
+ .init_hook = alc269_lifebook_init_hook,
+ },
};
static int patch_alc269(struct hda_codec *codec)
@@ -12472,6 +12733,8 @@ static int patch_alc269(struct hda_codec *codec)
spec->adc_nids = alc269_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
spec->capsrc_nids = alc269_capsrc_nids;
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
codec->patch_ops = alc_patch_ops;
if (board_config == ALC269_AUTO)
@@ -12480,6 +12743,7 @@ static int patch_alc269(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc269_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -12612,17 +12876,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -12646,17 +12899,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -12674,18 +12916,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- /*Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
-
{ } /* end */
};
@@ -12709,17 +12939,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -12751,17 +12970,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Channel Mode",
@@ -13293,25 +13501,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
return 0;
}
-static struct snd_kcontrol_new alc861_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t nid,
int pin_type, int dac_idx)
@@ -13402,18 +13591,17 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+ add_verb(spec, alc861_auto_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
spec->adc_nids = alc861_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- spec->mixers[spec->num_mixers] = alc861_capture_mixer;
- spec->num_mixers++;
+ set_capture_mixer(spec);
store_pin_configs(codec);
return 1;
@@ -13644,6 +13832,7 @@ static int patch_alc861(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc861_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -13709,11 +13898,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = {
},
};
-#define alc861vd_mux_enum_info alc_mux_enum_info
-#define alc861vd_mux_enum_get alc_mux_enum_get
-/* ALC861VD has the ALC882-type input selection (but has only one ADC) */
-#define alc861vd_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -13759,25 +13943,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
{ } /* end */
};
-static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc861vd_mux_enum_info,
- .get = alc861vd_mux_enum_get,
- .put = alc861vd_mux_enum_put,
- },
- { } /* end */
-};
-
/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
* Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
*/
@@ -14169,6 +14334,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re
static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
[ALC660VD_3ST] = "3stack-660",
[ALC660VD_3ST_DIG] = "3stack-660-digout",
+ [ALC660VD_ASUS_V1S] = "asus-v1s",
[ALC861VD_3ST] = "3stack",
[ALC861VD_3ST_DIG] = "3stack-digout",
[ALC861VD_6ST_DIG] = "6stack-digout",
@@ -14183,7 +14349,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
@@ -14290,6 +14456,21 @@ static struct alc_config_preset alc861vd_presets[] = {
.unsol_event = alc861vd_dallas_unsol_event,
.init_hook = alc861vd_dallas_automute,
},
+ [ALC660VD_ASUS_V1S] = {
+ .mixers = { alc861vd_lenovo_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+ alc861vd_3stack_init_verbs,
+ alc861vd_eapd_verbs,
+ alc861vd_lenovo_unsol_verbs },
+ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+ .dac_nids = alc660vd_dac_nids,
+ .dig_out_nid = ALC861VD_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ .unsol_event = alc861vd_lenovo_unsol_event,
+ .init_hook = alc861vd_lenovo_automute,
+ },
};
/*
@@ -14514,11 +14695,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
- spec->init_verbs[spec->num_init_verbs++]
- = alc861vd_volume_init_verbs;
+ add_verb(spec, alc861vd_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
@@ -14585,7 +14765,7 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->stream_name_analog = "ALC660-VD Analog";
spec->stream_name_digital = "ALC660-VD Digital";
/* always turn on EAPD */
- spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+ add_verb(spec, alc660vd_eapd_verbs);
} else {
spec->stream_name_analog = "ALC861VD Analog";
spec->stream_name_digital = "ALC861VD Digital";
@@ -14600,9 +14780,9 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->adc_nids = alc861vd_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
spec->capsrc_nids = alc861vd_capsrc_nids;
+ spec->is_mix_capture = 1;
- spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
- spec->num_mixers++;
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x02;
@@ -14614,6 +14794,7 @@ static int patch_alc861vd(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc861vd_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -14689,10 +14870,6 @@ static struct hda_input_mux alc663_m51va_capture_source = {
},
};
-#define alc662_mux_enum_info alc_mux_enum_info
-#define alc662_mux_enum_get alc_mux_enum_get
-#define alc662_mux_enum_put alc882_mux_enum_put
-
/*
* 2ch mode
*/
@@ -15278,25 +15455,6 @@ static struct hda_verb alc662_ecs_init_verbs[] = {
{}
};
-/* capture mixer elements */
-static struct snd_kcontrol_new alc662_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
- { } /* end */
-};
-
static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
@@ -15868,7 +16026,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
static struct alc_config_preset alc662_presets[] = {
[ALC662_3ST_2ch_DIG] = {
- .mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer },
+ .mixers = { alc662_3ST_2ch_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15879,8 +16037,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_3ST_6ch_DIG] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15892,8 +16049,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_3ST_6ch] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15903,8 +16059,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_5ST_DIG] = {
- .mixers = { alc662_base_mixer, alc662_chmode_mixer,
- alc662_capture_mixer },
+ .mixers = { alc662_base_mixer, alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15915,7 +16070,7 @@ static struct alc_config_preset alc662_presets[] = {
.input_mux = &alc662_capture_source,
},
[ALC662_LENOVO_101E] = {
- .mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer },
+ .mixers = { alc662_lenovo_101e_mixer },
.init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15926,7 +16081,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_lenovo_101e_all_automute,
},
[ALC662_ASUS_EEEPC_P701] = {
- .mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer },
+ .mixers = { alc662_eeepc_p701_mixer },
.init_verbs = { alc662_init_verbs,
alc662_eeepc_sue_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15938,7 +16093,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_inithook,
},
[ALC662_ASUS_EEEPC_EP20] = {
- .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+ .mixers = { alc662_eeepc_ep20_mixer,
alc662_chmode_mixer },
.init_verbs = { alc662_init_verbs,
alc662_eeepc_ep20_sue_init_verbs },
@@ -15951,7 +16106,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_ep20_inithook,
},
[ALC662_ECS] = {
- .mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+ .mixers = { alc662_ecs_mixer },
.init_verbs = { alc662_init_verbs,
alc662_ecs_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15963,7 +16118,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_eeepc_inithook,
},
[ALC663_ASUS_M51VA] = {
- .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .mixers = { alc663_m51va_mixer },
.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15975,7 +16130,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_m51va_inithook,
},
[ALC663_ASUS_G71V] = {
- .mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+ .mixers = { alc663_g71v_mixer },
.init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15987,7 +16142,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_g71v_inithook,
},
[ALC663_ASUS_H13] = {
- .mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+ .mixers = { alc663_m51va_mixer },
.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -15998,7 +16153,7 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_m51va_inithook,
},
[ALC663_ASUS_G50V] = {
- .mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+ .mixers = { alc663_g50v_mixer },
.init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
.dac_nids = alc662_dac_nids,
@@ -16010,7 +16165,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_g50v_inithook,
},
[ALC663_ASUS_MODE1] = {
- .mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_m51va_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_21jd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16024,7 +16180,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode1_inithook,
},
[ALC662_ASUS_MODE2] = {
- .mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc662_1bjd_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc662_1bjd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16037,7 +16194,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc662_mode2_inithook,
},
[ALC663_ASUS_MODE3] = {
- .mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_two_hp_m1_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_two_hp_amic_m1_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16051,8 +16209,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode3_inithook,
},
[ALC663_ASUS_MODE4] = {
- .mixers = { alc663_asus_21jd_clfe_mixer,
- alc662_auto_capture_mixer},
+ .mixers = { alc663_asus_21jd_clfe_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_21jd_amic_init_verbs},
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16066,8 +16224,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode4_inithook,
},
[ALC663_ASUS_MODE5] = {
- .mixers = { alc663_asus_15jd_clfe_mixer,
- alc662_auto_capture_mixer },
+ .mixers = { alc663_asus_15jd_clfe_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_15jd_amic_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16081,7 +16239,8 @@ static struct alc_config_preset alc662_presets[] = {
.init_hook = alc663_mode5_inithook,
},
[ALC663_ASUS_MODE6] = {
- .mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+ .mixers = { alc663_two_hp_m2_mixer },
+ .cap_mixer = alc662_auto_capture_mixer,
.init_verbs = { alc662_init_verbs,
alc663_two_hp_amic_m2_init_verbs },
.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16342,24 +16501,20 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_out_pin)
spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
- spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+ add_verb(spec, alc662_auto_init_verbs);
if (codec->vendor_id == 0x10ec0663)
- spec->init_verbs[spec->num_init_verbs++] =
- alc663_auto_init_verbs;
+ add_verb(spec, alc663_auto_init_verbs);
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
- spec->mixers[spec->num_mixers] = alc662_capture_mixer;
- spec->num_mixers++;
-
store_pin_configs(codec);
return 1;
}
@@ -16435,6 +16590,10 @@ static int patch_alc662(struct hda_codec *codec)
spec->adc_nids = alc662_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
spec->capsrc_nids = alc662_capsrc_nids;
+ spec->is_mix_capture = 1;
+
+ if (!spec->cap_mixer)
+ set_capture_mixer(spec);
spec->vmaster_nid = 0x02;
@@ -16445,6 +16604,7 @@ static int patch_alc662(struct hda_codec *codec)
if (!spec->loopback.amplist)
spec->loopback.amplist = alc662_loopbacks;
#endif
+ codec->proc_widget_hook = print_realtek_coef;
return 0;
}
@@ -16452,7 +16612,7 @@ static int patch_alc662(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_realtek[] = {
+static struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@@ -16484,3 +16644,26 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:10ec*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek HD-audio codec");
+
+static struct hda_codec_preset_list realtek_list = {
+ .preset = snd_hda_preset_realtek,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_realtek_init(void)
+{
+ return snd_hda_add_codec_preset(&realtek_list);
+}
+
+static void __exit patch_realtek_exit(void)
+{
+ snd_hda_delete_codec_preset(&realtek_list);
+}
+
+module_init(patch_realtek_init)
+module_exit(patch_realtek_exit)
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 9332b63e406c..43b436c5d01b 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -28,7 +28,6 @@
#include <sound/core.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
/* si3054 verbs */
#define SI3054_VERB_READ_NODE 0x900
@@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_si3054[] = {
+static struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
@@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
{}
};
+MODULE_ALIAS("snd-hda-codec-id:163c3055");
+MODULE_ALIAS("snd-hda-codec-id:163c3155");
+MODULE_ALIAS("snd-hda-codec-id:11c13026");
+MODULE_ALIAS("snd-hda-codec-id:11c13055");
+MODULE_ALIAS("snd-hda-codec-id:11c13155");
+MODULE_ALIAS("snd-hda-codec-id:10573055");
+MODULE_ALIAS("snd-hda-codec-id:10573057");
+MODULE_ALIAS("snd-hda-codec-id:10573155");
+MODULE_ALIAS("snd-hda-codec-id:11063288");
+MODULE_ALIAS("snd-hda-codec-id:15433155");
+MODULE_ALIAS("snd-hda-codec-id:18540018");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
+
+static struct hda_codec_preset_list si3054_list = {
+ .preset = snd_hda_preset_si3054,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_si3054_init(void)
+{
+ return snd_hda_add_codec_preset(&si3054_list);
+}
+
+static void __exit patch_si3054_exit(void)
+{
+ snd_hda_delete_codec_preset(&si3054_list);
+}
+
+module_init(patch_si3054_init)
+module_exit(patch_si3054_exit)
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 5dd3e89f620a..35b83dc6e19e 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -30,17 +30,17 @@
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/asoundef.h>
+#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
#include "hda_beep.h"
-#define NUM_CONTROL_ALLOC 32
-
-#define STAC_VREF_EVENT 0x00
-#define STAC_INSERT_EVENT 0x10
-#define STAC_PWR_EVENT 0x20
-#define STAC_HP_EVENT 0x30
+enum {
+ STAC_VREF_EVENT = 1,
+ STAC_INSERT_EVENT,
+ STAC_PWR_EVENT,
+ STAC_HP_EVENT,
+};
enum {
STAC_REF,
@@ -69,6 +69,7 @@ enum {
};
enum {
+ STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_DELL_M6_AMIC,
STAC_DELL_M6_DMIC,
@@ -127,6 +128,7 @@ enum {
};
enum {
+ STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
STAC_D965_5ST,
@@ -135,6 +137,19 @@ enum {
STAC_927X_MODELS
};
+struct sigmatel_event {
+ hda_nid_t nid;
+ unsigned char type;
+ unsigned char tag;
+ int data;
+};
+
+struct sigmatel_jack {
+ hda_nid_t nid;
+ int type;
+ struct snd_jack *jack;
+};
+
struct sigmatel_spec {
struct snd_kcontrol_new *mixers[4];
unsigned int num_mixers;
@@ -142,8 +157,6 @@ struct sigmatel_spec {
int board_config;
unsigned int eapd_switch: 1;
unsigned int surr_switch: 1;
- unsigned int line_switch: 1;
- unsigned int mic_switch: 1;
unsigned int alt_switch: 1;
unsigned int hp_detect: 1;
unsigned int spdif_mute: 1;
@@ -168,12 +181,20 @@ struct sigmatel_spec {
hda_nid_t *pwr_nids;
hda_nid_t *dac_list;
+ /* jack detection */
+ struct snd_array jacks;
+
+ /* events */
+ struct snd_array events;
+
/* playback */
struct hda_input_mux *mono_mux;
struct hda_input_mux *amp_mux;
unsigned int cur_mmux;
struct hda_multi_out multiout;
hda_nid_t dac_nids[5];
+ hda_nid_t hp_dacs[5];
+ hda_nid_t speaker_dacs[5];
/* capture */
hda_nid_t *adc_nids;
@@ -197,7 +218,6 @@ struct sigmatel_spec {
hda_nid_t *pin_nids;
unsigned int num_pins;
unsigned int *pin_configs;
- unsigned int *bios_pin_configs;
/* codec specific stuff */
struct hda_verb *init;
@@ -218,15 +238,16 @@ struct sigmatel_spec {
/* i/o switches */
unsigned int io_switch[2];
unsigned int clfe_swap;
- unsigned int hp_switch; /* NID of HP as line-out */
+ hda_nid_t line_switch; /* shared line-in for input and output */
+ hda_nid_t mic_switch; /* shared mic-in for input and output */
+ hda_nid_t hp_switch; /* NID of HP as line-out */
unsigned int aloopback;
struct hda_pcm pcm_rec[2]; /* PCM information */
/* dynamic controls and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_dimux;
struct hda_input_mux private_imux;
struct hda_input_mux private_smux;
@@ -270,9 +291,6 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
};
#define STAC92HD73_DAC_COUNT 5
-static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = {
- 0x15, 0x16, 0x17, 0x18, 0x19,
-};
static hda_nid_t stac92hd73xx_mux_nids[4] = {
0x28, 0x29, 0x2a, 0x2b,
@@ -291,11 +309,7 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
0x11, 0x12, 0
};
-#define STAC92HD81_DAC_COUNT 2
#define STAC92HD83_DAC_COUNT 3
-static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
- 0x13, 0x14, 0x22,
-};
static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
0x17, 0x18,
@@ -337,10 +351,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = {
0x24, 0x25,
};
-static hda_nid_t stac92hd71bxx_dac_nids[1] = {
- 0x10, /*0x11, */
-};
-
#define STAC92HD71BXX_NUM_DMICS 2
static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
0x18, 0x19, 0
@@ -572,12 +582,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
else
nid = codec->slave_dig_outs[smux_idx - 1];
if (spec->cur_smux[smux_idx] == smux->num_items - 1)
- val = AMP_OUT_MUTE;
+ val = HDA_AMP_MUTE;
else
- val = AMP_OUT_UNMUTE;
+ val = 0;
/* un/mute SPDIF out */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, val);
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, val);
}
return 0;
}
@@ -742,10 +752,6 @@ static struct hda_verb stac9200_eapd_init[] = {
static struct hda_verb stac92hd73xx_6ch_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup audio connections */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -764,10 +770,6 @@ static struct hda_verb dell_eq_core_init[] = {
/* set master volume to max value without distortion
* and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
- /* setup audio connections */
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -781,10 +783,6 @@ static struct hda_verb dell_eq_core_init[] = {
static struct hda_verb dell_m6_core_init[] = {
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup audio connections */
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -799,13 +797,6 @@ static struct hda_verb dell_m6_core_init[] = {
static struct hda_verb stac92hd73xx_8ch_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup audio connections */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* connect hp ports to dac3 */
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -823,15 +814,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = {
static struct hda_verb stac92hd73xx_10ch_core_init[] = {
/* set master volume and direct control */
{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* setup audio connections */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
/* dac3 is connected to import3 mux */
{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
- /* connect hp ports to dac4 */
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
/* setup adcs to point to mixer */
{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -857,17 +841,17 @@ static struct hda_verb stac92hd83xxx_core_init[] = {
/* power state controls amps */
{ 0x01, AC_VERB_SET_EAPD, 1 << 2},
+ {}
};
static struct hda_verb stac92hd71bxx_core_init[] = {
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* connect headphone jack to dac1 */
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {}
};
#define HD_DISABLE_PORTF 2
@@ -882,8 +866,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
/* set master volume and direct control */
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* connect headphone jack to dac1 */
- { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
/* unmute right and left channels for nodes 0x0a, 0xd */
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1083,21 +1065,21 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
- HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT),
/*
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT),
*/
{ } /* end */
};
@@ -1236,9 +1218,14 @@ static const char *slave_sws[] = {
NULL
};
+static void stac92xx_free_kctls(struct hda_codec *codec);
+static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
+
static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
int err;
int i;
@@ -1253,7 +1240,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
}
if (spec->num_dmuxes > 0) {
stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_ctl_add(codec->bus->card,
+ err = snd_hda_ctl_add(codec,
snd_ctl_new1(&stac_dmux_mixer, codec));
if (err < 0)
return err;
@@ -1269,7 +1256,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
- err = snd_ctl_add(codec->bus->card,
+ err = snd_hda_ctl_add(codec,
snd_ctl_new1(&stac_smux_mixer, codec));
if (err < 0)
return err;
@@ -1308,6 +1295,37 @@ static int stac92xx_build_controls(struct hda_codec *codec)
return err;
}
+ stac92xx_free_kctls(codec); /* no longer needed */
+
+ /* create jack input elements */
+ if (spec->hp_detect) {
+ for (i = 0; i < cfg->hp_outs; i++) {
+ int type = SND_JACK_HEADPHONE;
+ nid = cfg->hp_pins[i];
+ /* jack detection */
+ if (cfg->hp_outs == i)
+ type |= SND_JACK_LINEOUT;
+ err = stac92xx_add_jack(codec, nid, type);
+ if (err < 0)
+ return err;
+ }
+ }
+ for (i = 0; i < cfg->line_outs; i++) {
+ err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
+ SND_JACK_LINEOUT);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ nid = cfg->input_pins[i];
+ if (nid) {
+ err = stac92xx_add_jack(codec, nid,
+ SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
+ }
+ }
+
return 0;
}
@@ -1611,6 +1629,7 @@ static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_NO_JD] = "no-jd",
[STAC_92HD73XX_REF] = "ref",
[STAC_DELL_M6_AMIC] = "dell-m6-amic",
[STAC_DELL_M6_DMIC] = "dell-m6-dmic",
@@ -1640,6 +1659,8 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
"unknown Dell", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f,
"Dell Studio 1537", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
+ "Dell Studio 17", STAC_DELL_M6_DMIC),
{} /* terminator */
};
@@ -1662,6 +1683,7 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD71BXX_REF),
+ {} /* terminator */
};
static unsigned int ref92hd71bxx_pin_configs[11] = {
@@ -1712,6 +1734,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"HP dv5", STAC_HP_M4),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
"HP dv7", STAC_HP_M4),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc,
+ "HP dv7", STAC_HP_M4),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
"unknown HP", STAC_HP_M4),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
@@ -2027,6 +2051,7 @@ static unsigned int dell_3st_pin_configs[14] = {
};
static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+ [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
[STAC_D965_REF] = ref927x_pin_configs,
[STAC_D965_3ST] = d965_3st_pin_configs,
[STAC_D965_5ST] = d965_5st_pin_configs,
@@ -2035,6 +2060,7 @@ static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
};
static const char *stac927x_models[STAC_927X_MODELS] = {
+ [STAC_D965_REF_NO_JD] = "ref-no-jd",
[STAC_D965_REF] = "ref",
[STAC_D965_3ST] = "3stack",
[STAC_D965_5ST] = "5stack",
@@ -2193,12 +2219,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
int i;
struct sigmatel_spec *spec = codec->spec;
- if (! spec->bios_pin_configs) {
- spec->bios_pin_configs = kcalloc(spec->num_pins,
- sizeof(*spec->bios_pin_configs), GFP_KERNEL);
- if (! spec->bios_pin_configs)
- return -ENOMEM;
- }
+ kfree(spec->pin_configs);
+ spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
+ GFP_KERNEL);
+ if (!spec->pin_configs)
+ return -ENOMEM;
for (i = 0; i < spec->num_pins; i++) {
hda_nid_t nid = spec->pin_nids[i];
@@ -2208,7 +2233,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
AC_VERB_GET_CONFIG_DEFAULT, 0x00);
snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
nid, pin_cfg);
- spec->bios_pin_configs[i] = pin_cfg;
+ spec->pin_configs[i] = pin_cfg;
}
return 0;
@@ -2250,6 +2275,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
spec->pin_configs[i]);
}
+static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!pins)
+ return stac92xx_save_bios_config_regs(codec);
+
+ kfree(spec->pin_configs);
+ spec->pin_configs = kmemdup(pins,
+ spec->num_pins * sizeof(*pins),
+ GFP_KERNEL);
+ if (!spec->pin_configs)
+ return -ENOMEM;
+
+ stac92xx_set_config_regs(codec);
+ return 0;
+}
+
+static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ if (spec->pin_nids[i] == nid) {
+ spec->pin_configs[i] = cfg;
+ stac92xx_set_config_reg(codec, nid, cfg);
+ break;
+ }
+ }
+}
+
/*
* Analog playback callbacks
*/
@@ -2327,7 +2385,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
if (spec->powerdown_adcs) {
msleep(40);
- snd_hda_codec_write_cache(codec, nid, 0,
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
}
snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -2343,7 +2401,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
snd_hda_codec_cleanup_stream(codec, nid);
if (spec->powerdown_adcs)
- snd_hda_codec_write_cache(codec, nid, 0,
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
return 0;
}
@@ -2475,6 +2533,9 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char type);
+
static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2487,7 +2548,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
/* check to be sure that the ports are upto date with
* switch changes
*/
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
return 1;
}
@@ -2527,7 +2588,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
* appropriately according to the pin direction
*/
if (spec->hp_detect)
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
return 1;
}
@@ -2622,28 +2683,16 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
- if (! knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = *ktemp;
knew->index = idx;
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
@@ -2664,70 +2713,53 @@ static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
return stac92xx_add_control_idx(spec, type, 0, name, val);
}
-/* flag inputs as additional dynamic lineouts */
-static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+/* check whether the line-input can be used as line-out */
+static hda_nid_t check_line_out_switch(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- unsigned int wcaps, wtype;
- int i, num_dacs = 0;
-
- /* use the wcaps cache to count all DACs available for line-outs */
- for (i = 0; i < codec->num_nodes; i++) {
- wcaps = codec->wcaps[i];
- wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ unsigned int pincap;
- if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
- num_dacs++;
- }
+ if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+ return 0;
+ nid = cfg->input_pins[AUTO_PIN_LINE];
+ pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
+ return 0;
+}
- snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
-
- switch (cfg->line_outs) {
- case 3:
- /* add line-in as side */
- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
- cfg->line_out_pins[cfg->line_outs] =
- cfg->input_pins[AUTO_PIN_LINE];
- spec->line_switch = 1;
- cfg->line_outs++;
- }
- break;
- case 2:
- /* add line-in as clfe and mic as side */
- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
- cfg->line_out_pins[cfg->line_outs] =
- cfg->input_pins[AUTO_PIN_LINE];
- spec->line_switch = 1;
- cfg->line_outs++;
- }
- if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
- cfg->line_out_pins[cfg->line_outs] =
- cfg->input_pins[AUTO_PIN_MIC];
- spec->mic_switch = 1;
- cfg->line_outs++;
- }
- break;
- case 1:
- /* add line-in as surr and mic as clfe */
- if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
- cfg->line_out_pins[cfg->line_outs] =
- cfg->input_pins[AUTO_PIN_LINE];
- spec->line_switch = 1;
- cfg->line_outs++;
- }
- if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
- cfg->line_out_pins[cfg->line_outs] =
- cfg->input_pins[AUTO_PIN_MIC];
- spec->mic_switch = 1;
- cfg->line_outs++;
+/* check whether the mic-input can be used as line-out */
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int def_conf, pincap;
+ unsigned int mic_pin;
+
+ if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+ return 0;
+ mic_pin = AUTO_PIN_MIC;
+ for (;;) {
+ hda_nid_t nid = cfg->input_pins[mic_pin];
+ def_conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ /* some laptops have an internal analog microphone
+ * which can't be used as a output */
+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+ pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
}
- break;
+ if (mic_pin == AUTO_PIN_MIC)
+ mic_pin = AUTO_PIN_FRONT_MIC;
+ else
+ break;
}
-
return 0;
}
-
static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
{
int i;
@@ -2740,6 +2772,52 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
return 0;
}
+static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ int i;
+ if (is_in_dac_nids(spec, nid))
+ return 1;
+ for (i = 0; i < spec->autocfg.hp_outs; i++)
+ if (spec->hp_dacs[i] == nid)
+ return 1;
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ if (spec->speaker_dacs[i] == nid)
+ return 1;
+ return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int j, conn_len;
+ hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ unsigned int wcaps, wtype;
+
+ conn_len = snd_hda_get_connections(codec, nid, conn,
+ HDA_MAX_CONNECTIONS);
+ for (j = 0; j < conn_len; j++) {
+ wcaps = snd_hda_param_read(codec, conn[j],
+ AC_PAR_AUDIO_WIDGET_CAP);
+ wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ /* we check only analog outputs */
+ if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
+ continue;
+ /* if this route has a free DAC, assign it */
+ if (!check_all_dac_nids(spec, conn[j])) {
+ if (conn_len > 1) {
+ /* select this DAC in the pin's input mux */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ }
+ return conn[j];
+ }
+ }
+ return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+
/*
* Fill in the dac_nids table from the parsed pin configuration
* This function only works when every pin in line_out_pins[]
@@ -2747,31 +2825,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
* codecs are not connected directly to a DAC, such as the 9200
* and 9202/925x. For those, dac_nids[] must be hard-coded.
*/
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int i, j, conn_len = 0;
- hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
- unsigned int wcaps, wtype;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ hda_nid_t nid, dac;
for (i = 0; i < cfg->line_outs; i++) {
nid = cfg->line_out_pins[i];
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- for (j = 0; j < conn_len; j++) {
- wcaps = snd_hda_param_read(codec, conn[j],
- AC_PAR_AUDIO_WIDGET_CAP);
- wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wtype != AC_WID_AUD_OUT ||
- (wcaps & AC_WCAP_DIGITAL))
- continue;
- /* conn[j] is a DAC routed to this line-out */
- if (!is_in_dac_nids(spec, conn[j]))
- break;
- }
-
- if (j == conn_len) {
+ dac = get_unassigned_dac(codec, nid);
+ if (!dac) {
if (spec->multiout.num_dacs > 0) {
/* we have already working output pins,
* so let's drop the broken ones again
@@ -2785,24 +2849,64 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
__func__, nid);
return -ENODEV;
}
+ add_spec_dacs(spec, dac);
+ }
- spec->multiout.dac_nids[i] = conn[j];
- spec->multiout.num_dacs++;
- if (conn_len > 1) {
- /* select this DAC in the pin's input mux */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
+ /* add line-in as output */
+ nid = check_line_out_switch(codec);
+ if (nid) {
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ snd_printdd("STAC: Add line-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->line_switch = nid;
+ add_spec_dacs(spec, dac);
+ }
+ }
+ /* add mic as output */
+ nid = check_mic_out_switch(codec);
+ if (nid) {
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->mic_switch = nid;
+ add_spec_dacs(spec, dac);
+ }
+ }
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = dac;
+ else
+ add_spec_extra_dacs(spec, dac);
}
+ spec->hp_dacs[i] = dac;
+ }
+
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = cfg->speaker_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac)
+ add_spec_extra_dacs(spec, dac);
+ spec->speaker_dacs[i] = dac;
}
- snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
spec->multiout.num_dacs,
spec->multiout.dac_nids[0],
spec->multiout.dac_nids[1],
spec->multiout.dac_nids[2],
spec->multiout.dac_nids[3],
spec->multiout.dac_nids[4]);
+
return 0;
}
@@ -2827,9 +2931,7 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
{
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else if (spec->multiout.num_dacs > 4) {
+ if (spec->multiout.num_dacs > 4) {
printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
return 1;
} else {
@@ -2839,35 +2941,47 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
return 0;
}
-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
{
- if (is_in_dac_nids(spec, nid))
- return 1;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+ if (!spec->multiout.extra_out_nid[i]) {
+ spec->multiout.extra_out_nid[i] = nid;
+ return 0;
+ }
+ }
+ printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
+ return 1;
+}
+
+static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ int i;
+
+ if (spec->autocfg.line_outs != 1)
+ return 0;
if (spec->multiout.hp_nid == nid)
- return 1;
- return 0;
+ return 0;
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+ if (spec->multiout.extra_out_nid[i] == nid)
+ return 0;
+ return 1;
}
/* add playback controls from the parsed DAC table */
static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
+ struct sigmatel_spec *spec = codec->spec;
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
hda_nid_t nid = 0;
int i, err;
+ unsigned int wid_caps;
- struct sigmatel_spec *spec = codec->spec;
- unsigned int wid_caps, pincap;
-
-
- for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
-
+ for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) {
nid = spec->multiout.dac_nids[i];
-
if (i == 2) {
/* Center/LFE */
err = create_controls(spec, "Center", nid, 1);
@@ -2889,16 +3003,24 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
}
} else {
- err = create_controls(spec, chname[i], nid, 3);
+ const char *name = chname[i];
+ /* if it's a single DAC, assign a better name */
+ if (!i && is_unique_dac(spec, nid)) {
+ switch (cfg->line_out_type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ name = "Speaker";
+ break;
+ }
+ }
+ err = create_controls(spec, name, nid, 3);
if (err < 0)
return err;
}
}
- if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
- cfg->hp_outs && !spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
-
if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
err = stac92xx_add_control(spec,
STAC_CTL_WIDGET_HP_SWITCH,
@@ -2909,45 +3031,19 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
}
if (spec->line_switch) {
- nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_param_read(codec, nid,
- AC_PAR_PIN_CAP);
- if (pincap & AC_PINCAP_OUT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_IO_SWITCH,
- "Line In as Output Switch", nid << 8);
- if (err < 0)
- return err;
- }
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+ "Line In as Output Switch",
+ spec->line_switch << 8);
+ if (err < 0)
+ return err;
}
if (spec->mic_switch) {
- unsigned int def_conf;
- unsigned int mic_pin = AUTO_PIN_MIC;
-again:
- nid = cfg->input_pins[mic_pin];
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
- /* some laptops have an internal analog microphone
- * which can't be used as a output */
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
- pincap = snd_hda_param_read(codec, nid,
- AC_PAR_PIN_CAP);
- if (pincap & AC_PINCAP_OUT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_IO_SWITCH,
- "Mic as Output Switch", (nid << 8) | 1);
- nid = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- if (!check_in_dac_nids(spec, nid))
- add_spec_dacs(spec, nid);
- if (err < 0)
- return err;
- }
- } else if (mic_pin == AUTO_PIN_MIC) {
- mic_pin = AUTO_PIN_FRONT_MIC;
- goto again;
- }
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+ "Mic as Output Switch",
+ (spec->mic_switch << 8) | 1);
+ if (err < 0)
+ return err;
}
return 0;
@@ -2959,55 +3055,39 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
{
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid;
- int i, old_num_dacs, err;
+ int i, err, nums;
- old_num_dacs = spec->multiout.num_dacs;
+ nums = 0;
for (i = 0; i < cfg->hp_outs; i++) {
+ static const char *pfxs[] = {
+ "Headphone", "Headphone2", "Headphone3",
+ };
unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
if (wid_caps & AC_WCAP_UNSOL_CAP)
spec->hp_detect = 1;
- nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- if (check_in_dac_nids(spec, nid))
- nid = 0;
- if (! nid)
+ if (nums >= ARRAY_SIZE(pfxs))
continue;
- add_spec_dacs(spec, nid);
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- if (check_in_dac_nids(spec, nid))
- nid = 0;
- if (! nid)
- continue;
- add_spec_dacs(spec, nid);
- }
- for (i = 0; i < cfg->line_outs; i++) {
- nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
- AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- if (check_in_dac_nids(spec, nid))
- nid = 0;
- if (! nid)
+ nid = spec->hp_dacs[i];
+ if (!nid)
continue;
- add_spec_dacs(spec, nid);
+ err = create_controls(spec, pfxs[nums++], nid, 3);
+ if (err < 0)
+ return err;
}
- for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+ nums = 0;
+ for (i = 0; i < cfg->speaker_outs; i++) {
static const char *pfxs[] = {
"Speaker", "External Speaker", "Speaker2",
};
- err = create_controls(spec, pfxs[i - old_num_dacs],
- spec->multiout.dac_nids[i], 3);
- if (err < 0)
- return err;
- }
- if (spec->multiout.hp_nid) {
- err = create_controls(spec, "Headphone",
- spec->multiout.hp_nid, 3);
+ if (nums >= ARRAY_SIZE(pfxs))
+ continue;
+ nid = spec->speaker_dacs[i];
+ if (!nid)
+ continue;
+ err = create_controls(spec, pfxs[nums++], nid, 3);
if (err < 0)
return err;
}
-
return 0;
}
@@ -3345,7 +3425,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
{
struct sigmatel_spec *spec = codec->spec;
int err;
- int hp_speaker_swap = 0;
if ((err = snd_hda_parse_pin_def_config(codec,
&spec->autocfg,
@@ -3363,13 +3442,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
* speaker_outs so that the following routines can handle
* HP pins as primary outputs.
*/
+ snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
sizeof(spec->autocfg.line_out_pins));
spec->autocfg.speaker_outs = spec->autocfg.line_outs;
memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
sizeof(spec->autocfg.hp_pins));
spec->autocfg.line_outs = spec->autocfg.hp_outs;
- hp_speaker_swap = 1;
+ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+ spec->autocfg.hp_outs = 0;
}
if (spec->autocfg.mono_out_pin) {
int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
@@ -3421,11 +3502,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
AC_PINCTL_OUT_EN);
}
- if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
- return err;
- if (spec->multiout.num_dacs == 0)
- if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+ if (!spec->multiout.num_dacs) {
+ err = stac92xx_auto_fill_dac_nids(codec);
+ if (err < 0)
return err;
+ }
err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -3463,19 +3544,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
}
#endif
- if (hp_speaker_swap == 1) {
- /* Restore the hp_outs and line_outs */
- memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.line_out_pins));
- spec->autocfg.hp_outs = spec->autocfg.line_outs;
- memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
- sizeof(spec->autocfg.speaker_pins));
- spec->autocfg.line_outs = spec->autocfg.speaker_outs;
- memset(spec->autocfg.speaker_pins, 0,
- sizeof(spec->autocfg.speaker_pins));
- spec->autocfg.speaker_outs = 0;
- }
-
err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
if (err < 0)
@@ -3520,11 +3588,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
if (dig_in && spec->autocfg.dig_in_pin)
spec->dig_in_nid = dig_in;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux;
- spec->dinput_mux = &spec->private_dimux;
+ if (!spec->dinput_mux)
+ spec->dinput_mux = &spec->private_dimux;
spec->sinput_mux = &spec->private_smux;
spec->mono_mux = &spec->private_mono_mux;
spec->amp_mux = &spec->private_amp_mux;
@@ -3628,8 +3697,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = 0x04;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux;
spec->dinput_mux = &spec->private_dimux;
@@ -3673,13 +3742,101 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
}
+static int stac92xx_add_jack(struct hda_codec *codec,
+ hda_nid_t nid, int type)
+{
+#ifdef CONFIG_SND_JACK
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_jack *jack;
+ int def_conf = snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ int connectivity = get_defcfg_connect(def_conf);
+ char name[32];
+
+ if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+ return 0;
+
+ snd_array_init(&spec->jacks, sizeof(*jack), 32);
+ jack = snd_array_new(&spec->jacks);
+ if (!jack)
+ return -ENOMEM;
+ jack->nid = nid;
+ jack->type = type;
+
+ sprintf(name, "%s at %s %s Jack",
+ snd_hda_get_jack_type(def_conf),
+ snd_hda_get_jack_connectivity(def_conf),
+ snd_hda_get_jack_location(def_conf));
+
+ return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+#else
+ return 0;
+#endif
+}
+
+static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+ unsigned char type, int data)
+{
+ struct sigmatel_event *event;
+
+ snd_array_init(&spec->events, sizeof(*event), 32);
+ event = snd_array_new(&spec->events);
+ if (!event)
+ return -ENOMEM;
+ event->nid = nid;
+ event->type = type;
+ event->tag = spec->events.used;
+ event->data = data;
+
+ return event->tag;
+}
+
+static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
+ hda_nid_t nid, unsigned char type)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_event *event = spec->events.list;
+ int i;
+
+ for (i = 0; i < spec->events.used; i++, event++) {
+ if (event->nid == nid && event->type == type)
+ return event;
+ }
+ return NULL;
+}
+
+static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
+ unsigned char tag)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_event *event = spec->events.list;
+ int i;
+
+ for (i = 0; i < spec->events.used; i++, event++) {
+ if (event->tag == tag)
+ return event;
+ }
+ return NULL;
+}
+
static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
- unsigned int event)
+ unsigned int type)
{
- if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | event));
+ struct sigmatel_event *event;
+ int tag;
+
+ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
+ return;
+ event = stac_get_event(codec, nid, type);
+ if (event)
+ tag = event->tag;
+ else
+ tag = stac_add_event(codec->spec, nid, type, 0);
+ if (tag < 0)
+ return;
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | tag);
}
static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -3699,9 +3856,8 @@ static void stac92xx_power_down(struct hda_codec *codec)
/* power down inactive DACs */
hda_nid_t *dac;
for (dac = spec->dac_list; *dac; dac++)
- if (!is_in_dac_nids(spec, *dac) &&
- spec->multiout.hp_nid != *dac)
- snd_hda_codec_write_cache(codec, *dac, 0,
+ if (!check_all_dac_nids(spec, *dac))
+ snd_hda_codec_write(codec, *dac, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
}
@@ -3720,7 +3876,7 @@ static int stac92xx_init(struct hda_codec *codec)
/* power down adcs initially */
if (spec->powerdown_adcs)
for (i = 0; i < spec->num_adcs; i++)
- snd_hda_codec_write_cache(codec,
+ snd_hda_codec_write(codec,
spec->adc_nids[i], 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
@@ -3736,37 +3892,51 @@ static int stac92xx_init(struct hda_codec *codec)
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
- for (i = 0; i < cfg->hp_outs; i++)
- enable_pin_detect(codec, cfg->hp_pins[i],
- STAC_HP_EVENT);
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ enable_pin_detect(codec, nid, STAC_HP_EVENT);
+ }
/* force to enable the first line-out; the others are set up
* in unsol_event
*/
stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
- AC_PINCTL_OUT_EN);
- stac92xx_auto_init_hp_out(codec);
+ AC_PINCTL_OUT_EN);
/* fake event to set up pins */
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+ STAC_HP_EVENT);
} else {
stac92xx_auto_init_multi_out(codec);
stac92xx_auto_init_hp_out(codec);
+ for (i = 0; i < cfg->hp_outs; i++)
+ stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
}
for (i = 0; i < AUTO_PIN_LAST; i++) {
hda_nid_t nid = cfg->input_pins[i];
if (nid) {
- unsigned int pinctl;
+ unsigned int pinctl, conf;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
/* for mic pins, force to initialize */
pinctl = stac92xx_get_vref(codec, nid);
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
pinctl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
/* if PINCTL already set then skip */
- if (pinctl & AC_PINCTL_IN_EN)
- continue;
+ if (!(pinctl & AC_PINCTL_IN_EN)) {
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid,
+ pinctl);
+ }
+ }
+ conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+ enable_pin_detect(codec, nid,
+ STAC_INSERT_EVENT);
+ stac_issue_unsol_event(codec, nid,
+ STAC_INSERT_EVENT);
}
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
}
for (i = 0; i < spec->num_dmics; i++)
@@ -3781,9 +3951,14 @@ static int stac92xx_init(struct hda_codec *codec)
for (i = 0; i < spec->num_pwrs; i++) {
hda_nid_t nid = spec->pwr_nids[i];
int pinctl, def_conf;
- int event = STAC_PWR_EVENT;
- if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
+ /* power on when no jack detection is available */
+ if (!spec->hp_detect) {
+ stac_toggle_power_map(codec, nid, 1);
+ continue;
+ }
+
+ if (is_nid_hp_pin(cfg, nid))
continue; /* already has an unsol event */
pinctl = snd_hda_codec_read(codec, nid, 0,
@@ -3792,8 +3967,10 @@ static int stac92xx_init(struct hda_codec *codec)
* any attempts on powering down a input port cause the
* referenced VREF to act quirky.
*/
- if (pinctl & AC_PINCTL_IN_EN)
+ if (pinctl & AC_PINCTL_IN_EN) {
+ stac_toggle_power_map(codec, nid, 1);
continue;
+ }
def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
def_conf = get_defcfg_connect(def_conf);
@@ -3804,30 +3981,54 @@ static int stac92xx_init(struct hda_codec *codec)
stac_toggle_power_map(codec, nid, 1);
continue;
}
- enable_pin_detect(codec, spec->pwr_nids[i], event | i);
- codec->patch_ops.unsol_event(codec, (event | i) << 26);
+ if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
+ enable_pin_detect(codec, nid, STAC_PWR_EVENT);
+ stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
+ }
}
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;
}
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_JACK
+ /* free jack instances manually when clearing/reconfiguring */
+ struct sigmatel_spec *spec = codec->spec;
+ if (!codec->bus->shutdown && spec->jacks.list) {
+ struct sigmatel_jack *jacks = spec->jacks.list;
+ int i;
+ for (i = 0; i < spec->jacks.used; i++)
+ snd_device_free(codec->bus->card, &jacks[i].jack);
+ }
+ snd_array_free(&spec->jacks);
+#endif
+}
+
+static void stac92xx_free_kctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
static void stac92xx_free(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- int i;
if (! spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
- if (spec->bios_pin_configs)
- kfree(spec->bios_pin_configs);
+ kfree(spec->pin_configs);
+ stac92xx_free_jacks(codec);
+ snd_array_free(&spec->events);
kfree(spec);
snd_hda_detach_beep_device(codec);
@@ -3846,11 +4047,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
* "xxx as Output" mixer switch
*/
struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
- spec->line_switch) ||
- (nid == cfg->input_pins[AUTO_PIN_MIC] &&
- spec->mic_switch))
+ if (nid == spec->line_switch || nid == spec->mic_switch)
return;
}
@@ -3874,20 +4071,13 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
pin_ctl & ~flag);
}
-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
{
if (!nid)
return 0;
if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
- & (1 << 31)) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- return 0; /* mic- or line-input */
- else
- return 1; /* HP-output */
- }
+ & (1 << 31))
+ return 1;
return 0;
}
@@ -3899,11 +4089,9 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
struct auto_pin_cfg *cfg = &spec->autocfg;
/* ignore sensing of shared line and mic jacks */
- if (spec->line_switch &&
- cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
+ if (cfg->hp_pins[i] == spec->line_switch)
return 1;
- if (spec->mic_switch &&
- cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
+ if (cfg->hp_pins[i] == spec->mic_switch)
return 1;
/* ignore if the pin is set as line-out */
if (cfg->hp_pins[i] == spec->hp_switch)
@@ -3911,7 +4099,7 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
return 0;
}
-static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+static void stac92xx_hp_detect(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
@@ -3927,7 +4115,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
break;
if (no_hp_sensing(spec, i))
continue;
- presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+ presence = get_pin_presence(codec, cfg->hp_pins[i]);
+ if (presence) {
+ unsigned int pinctl;
+ pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl & AC_PINCTL_IN_EN)
+ presence = 0; /* mic- or line-input */
+ }
}
if (presence) {
@@ -4004,50 +4199,145 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
{
- stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid));
+ stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_jack *jacks = spec->jacks.list;
+
+ if (jacks) {
+ int i;
+ for (i = 0; i < spec->jacks.used; i++) {
+ if (jacks->nid == nid) {
+ unsigned int pin_ctl =
+ snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0x00);
+ int type = jacks->type;
+ if (type == (SND_JACK_LINEOUT
+ | SND_JACK_HEADPHONE))
+ type = (pin_ctl & AC_PINCTL_HP_EN)
+ ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+ snd_jack_report(jacks->jack,
+ get_pin_presence(codec, nid)
+ ? type : 0);
+ }
+ jacks++;
+ }
+ }
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char type)
+{
+ struct sigmatel_event *event = stac_get_event(codec, nid, type);
+ if (!event)
+ return;
+ codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
}
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
{
struct sigmatel_spec *spec = codec->spec;
- int idx = res >> 26 & 0x0f;
+ struct sigmatel_event *event;
+ int tag, data;
- switch ((res >> 26) & 0x70) {
+ tag = (res >> 26) & 0x7f;
+ event = stac_get_event_from_tag(codec, tag);
+ if (!event)
+ return;
+
+ switch (event->type) {
case STAC_HP_EVENT:
- stac92xx_hp_detect(codec, res);
+ stac92xx_hp_detect(codec);
/* fallthru */
+ case STAC_INSERT_EVENT:
case STAC_PWR_EVENT:
if (spec->num_pwrs > 0)
- stac92xx_pin_sense(codec, idx);
+ stac92xx_pin_sense(codec, event->nid);
+ stac92xx_report_jack(codec, event->nid);
break;
- case STAC_VREF_EVENT: {
- int data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
+ case STAC_VREF_EVENT:
+ data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
/* toggle VREF state based on GPIOx status */
snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
- !!(data & (1 << idx)));
+ !!(data & (1 << event->data)));
break;
- }
}
}
+#ifdef CONFIG_PROC_FS
+static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->afg)
+ snd_iprintf(buffer, "Power-Map: 0x%02x\n",
+ snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+}
+
+static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec,
+ unsigned int verb)
+{
+ snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
+ snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
+}
+
+/* stac92hd71bxx, stac92hd73xx */
+static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ stac92hd_proc_hook(buffer, codec, nid);
+ if (nid == codec->afg)
+ analog_loop_proc_hook(buffer, codec, 0xfa0);
+}
+
+static void stac9205_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->afg)
+ analog_loop_proc_hook(buffer, codec, 0xfe0);
+}
+
+static void stac927x_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->afg)
+ analog_loop_proc_hook(buffer, codec, 0xfeb);
+}
+#else
+#define stac92hd_proc_hook NULL
+#define stac92hd7x_proc_hook NULL
+#define stac9205_proc_hook NULL
+#define stac927x_proc_hook NULL
+#endif
+
#ifdef SND_HDA_NEEDS_RESUME
static int stac92xx_resume(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
stac92xx_set_config_regs(codec);
- snd_hda_sequence_write(codec, spec->init);
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data);
+ stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
- /* power down inactive DACs */
- if (spec->dac_list)
- stac92xx_power_down(codec);
- /* invoke unsolicited event to reset the HP state */
+ /* fake event to set up pins again to override cached values */
if (spec->hp_detect)
- codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+ STAC_HP_EVENT);
+ return 0;
+}
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
return 0;
}
#endif
@@ -4059,6 +4349,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
.free = stac92xx_free,
.unsol_event = stac92xx_unsol_event,
#ifdef SND_HDA_NEEDS_RESUME
+ .suspend = stac92xx_suspend,
.resume = stac92xx_resume,
#endif
};
@@ -4081,14 +4372,12 @@ static int patch_stac9200(struct hda_codec *codec)
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac9200_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac9200_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->multiout.max_channels = 2;
@@ -4144,14 +4433,12 @@ static int patch_stac925x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
"using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else if (stac925x_brd_tbl[spec->board_config] != NULL){
- spec->pin_configs = stac925x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac925x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->multiout.max_channels = 2;
@@ -4215,6 +4502,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
struct sigmatel_spec *spec;
hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
int err = 0;
+ int num_dacs;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -4233,26 +4521,23 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD73XX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd73xx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
- spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
+ num_dacs = snd_hda_get_connections(codec, 0x0a,
conn, STAC92HD73_DAC_COUNT + 2) - 1;
- if (spec->multiout.num_dacs < 0) {
+ if (num_dacs < 3 || num_dacs > 5) {
printk(KERN_WARNING "hda_codec: Could not determine "
"number of channels defaulting to DAC count\n");
- spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+ num_dacs = STAC92HD73_DAC_COUNT;
}
-
- switch (spec->multiout.num_dacs) {
+ switch (num_dacs) {
case 0x3: /* 6 Channel */
spec->mixer = stac92hd73xx_6ch_mixer;
spec->init = stac92hd73xx_6ch_core_init;
@@ -4264,9 +4549,9 @@ again:
case 0x5: /* 10 Channel */
spec->mixer = stac92hd73xx_10ch_mixer;
spec->init = stac92hd73xx_10ch_core_init;
- };
+ }
+ spec->multiout.dac_nids = spec->dac_nids;
- spec->multiout.dac_nids = stac92hd73xx_dac_nids;
spec->aloopback_mask = 0x01;
spec->aloopback_shift = 8;
@@ -4298,7 +4583,7 @@ again:
spec->eapd_switch = 0;
spec->num_amps = 1;
- if (!spec->init)
+ if (spec->board_config != STAC_DELL_EQ)
spec->init = dell_m6_core_init;
switch (spec->board_config) {
case STAC_DELL_M6_AMIC: /* Analog Mics */
@@ -4351,8 +4636,13 @@ again:
return err;
}
+ if (spec->board_config == STAC_92HD73XX_NO_JD)
+ spec->hp_detect = 0;
+
codec->patch_ops = stac92xx_patch_ops;
+ codec->proc_widget_hook = stac92hd7x_proc_hook;
+
return 0;
}
@@ -4384,17 +4674,15 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
spec->pwr_nids = stac92hd83xxx_pwr_nids;
spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
- spec->multiout.dac_nids = stac92hd83xxx_dac_nids;
+ spec->multiout.dac_nids = spec->dac_nids;
spec->init = stac92hd83xxx_core_init;
switch (codec->vendor_id) {
case 0x111d7605:
- spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
break;
default:
spec->num_pwrs--;
spec->init++; /* switch to config #2 */
- spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
}
spec->mixer = stac92hd83xxx_mixer;
@@ -4413,14 +4701,12 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD83XXX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd83xxx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4441,56 +4727,10 @@ again:
codec->patch_ops = stac92xx_patch_ops;
- return 0;
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_POWER_STATE, pwr);
-
- msleep(1);
- for (i = 0; i < spec->num_adcs; i++) {
- snd_hda_codec_write_cache(codec,
- spec->adc_nids[i], 0,
- AC_VERB_SET_POWER_STATE, pwr);
- }
-};
+ codec->proc_widget_hook = stac92hd_proc_hook;
-static int stac92hd71xx_resume(struct hda_codec *codec)
-{
- stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
- return stac92xx_resume(codec);
-}
-
-static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
return 0;
-};
-
-#endif
-
-static struct hda_codec_ops stac92hd71bxx_patch_ops = {
- .build_controls = stac92xx_build_controls,
- .build_pcms = stac92xx_build_pcms,
- .init = stac92xx_init,
- .free = stac92xx_free,
- .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
- .resume = stac92hd71xx_resume,
- .suspend = stac92hd71xx_suspend,
-#endif
-};
+}
static struct hda_input_mux stac92hd71bxx_dmux = {
.num_items = 4,
@@ -4527,14 +4767,12 @@ again:
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD71BXX, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac92hd71bxx_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
if (spec->board_config > STAC_92HD71BXX_REF) {
@@ -4557,21 +4795,21 @@ again:
switch (spec->board_config) {
case STAC_HP_M4:
/* Enable VREF power saving on GPIO1 detect */
+ err = stac_add_event(spec, codec->afg,
+ STAC_VREF_EVENT, 0x02);
+ if (err < 0)
+ return err;
snd_hda_codec_write_cache(codec, codec->afg, 0,
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | err);
spec->gpio_mask |= 0x02;
break;
}
if ((codec->revision_id & 0xf) == 0 ||
- (codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
- codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+ (codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- }
/* no output amps */
spec->num_pwrs = 0;
@@ -4580,15 +4818,11 @@ again:
/* disable VSW */
spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
- stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+ stac_change_pin_config(codec, 0xf, 0x40f000f0);
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
- if ((codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
- codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+ if ((codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
- }
/* no output amps */
spec->num_pwrs = 0;
@@ -4618,7 +4852,7 @@ again:
switch (spec->board_config) {
case STAC_HP_M4:
/* enable internal microphone */
- stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+ stac_change_pin_config(codec, 0x0e, 0x01813040);
stac92xx_auto_set_pinctl(codec, 0x0e,
AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
/* fallthru */
@@ -4639,9 +4873,7 @@ again:
spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
};
- spec->multiout.num_dacs = 1;
- spec->multiout.hp_nid = 0x11;
- spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+ spec->multiout.dac_nids = spec->dac_nids;
if (spec->dinput_mux)
spec->private_dimux.num_items +=
spec->num_dmics -
@@ -4663,6 +4895,8 @@ again:
return err;
}
+ codec->proc_widget_hook = stac92hd7x_proc_hook;
+
return 0;
};
@@ -4724,14 +4958,12 @@ static int patch_stac922x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
"using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else if (stac922x_brd_tbl[spec->board_config] != NULL) {
- spec->pin_configs = stac922x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac922x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->adc_nids = stac922x_adc_nids;
@@ -4794,14 +5026,12 @@ static int patch_stac927x(struct hda_codec *codec)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
"STAC927x, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac927x_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac927x_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->digbeep_nid = 0x23;
@@ -4831,15 +5061,15 @@ static int patch_stac927x(struct hda_codec *codec)
case 0x10280209:
case 0x1028022e:
/* correct the device field to SPDIF out */
- stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+ stac_change_pin_config(codec, 0x21, 0x01442070);
break;
};
/* configure the analog microphone on some laptops */
- stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
+ stac_change_pin_config(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */
- stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
+ stac_change_pin_config(codec, 0x0f, 0x0227011f);
/* correct the front input jack as a mic */
- stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+ stac_change_pin_config(codec, 0x0e, 0x02a79130);
/* fallthru */
case STAC_DELL_3ST:
/* GPIO2 High = Enable EAPD */
@@ -4887,6 +5117,8 @@ static int patch_stac927x(struct hda_codec *codec)
codec->patch_ops = stac92xx_patch_ops;
+ codec->proc_widget_hook = stac927x_proc_hook;
+
/*
* !!FIXME!!
* The STAC927x seem to require fairly long delays for certain
@@ -4899,6 +5131,10 @@ static int patch_stac927x(struct hda_codec *codec)
*/
codec->bus->needs_damn_long_delay = 1;
+ /* no jack detecion for ref-no-jd model */
+ if (spec->board_config == STAC_D965_REF_NO_JD)
+ spec->hp_detect = 0;
+
return 0;
}
@@ -4921,14 +5157,12 @@ static int patch_stac9205(struct hda_codec *codec)
if (spec->board_config < 0) {
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
err = stac92xx_save_bios_config_regs(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
- spec->pin_configs = spec->bios_pin_configs;
- } else {
- spec->pin_configs = stac9205_brd_tbl[spec->board_config];
- stac92xx_set_config_regs(codec);
+ } else
+ err = stac_save_pin_cfgs(codec,
+ stac9205_brd_tbl[spec->board_config]);
+ if (err < 0) {
+ stac92xx_free(codec);
+ return err;
}
spec->digbeep_nid = 0x23;
@@ -4955,15 +5189,18 @@ static int patch_stac9205(struct hda_codec *codec)
switch (spec->board_config){
case STAC_9205_DELL_M43:
/* Enable SPDIF in/out */
- stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
- stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+ stac_change_pin_config(codec, 0x1f, 0x01441030);
+ stac_change_pin_config(codec, 0x20, 0x1c410030);
/* Enable unsol response for GPIO4/Dock HP connection */
+ err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
+ if (err < 0)
+ return err;
snd_hda_codec_write_cache(codec, codec->afg, 0,
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
snd_hda_codec_write_cache(codec, codec->afg, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
- (AC_USRSP_EN | STAC_HP_EVENT));
+ AC_USRSP_EN | err);
spec->gpio_dir = 0x0b;
spec->eapd_mask = 0x01;
@@ -5001,6 +5238,8 @@ static int patch_stac9205(struct hda_codec *codec)
codec->patch_ops = stac92xx_patch_ops;
+ codec->proc_widget_hook = stac9205_proc_hook;
+
return 0;
}
@@ -5057,29 +5296,11 @@ static struct hda_verb vaio_ar_init[] = {
{}
};
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
static struct snd_kcontrol_new vaio_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5095,8 +5316,10 @@ static struct snd_kcontrol_new vaio_mixer[] = {
};
static struct snd_kcontrol_new vaio_ar_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5137,7 +5360,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
{
- if (get_hp_pin_presence(codec, 0x0a)) {
+ if (get_pin_presence(codec, 0x0a)) {
stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
} else {
@@ -5248,7 +5471,7 @@ static int patch_stac9872(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -5312,3 +5535,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:8384*");
+MODULE_ALIAS("snd-hda-codec-id:111d*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_preset_list sigmatel_list = {
+ .preset = snd_hda_preset_sigmatel,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_sigmatel_init(void)
+{
+ return snd_hda_add_codec_preset(&sigmatel_list);
+}
+
+static void __exit patch_sigmatel_exit(void)
+{
+ snd_hda_delete_codec_preset(&sigmatel_list);
+}
+
+module_init(patch_sigmatel_init)
+module_exit(patch_sigmatel_exit)
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 63e4871e5d8f..c761394cbe84 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -47,15 +47,11 @@
#include <sound/asoundef.h>
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_patch.h"
/* amp values */
#define AMP_VAL_IDX_SHIFT 19
#define AMP_VAL_IDX_MASK (0x0f<<19)
-#define NUM_CONTROL_ALLOC 32
-#define NUM_VERB_ALLOC 32
-
/* Pin Widget NID */
#define VT1708_HP_NID 0x13
#define VT1708_DIGOUT_NID 0x14
@@ -145,8 +141,6 @@ enum {
AUTO_SEQ_SIDE
};
-#define get_amp_nid(kc) ((kc)->private_value & 0xffff)
-
/* Some VT1708S based boards gets the micboost setting wrong, so we have
* to apply some brute-force and re-write the TLV's by software. */
static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
@@ -227,8 +221,7 @@ struct via_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
- unsigned int num_kctl_alloc, num_kctl_used;
- struct snd_kcontrol_new *kctl_alloc;
+ struct snd_array kctls;
struct hda_input_mux private_imux[2];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
@@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
{
struct snd_kcontrol_new *knew;
- if (spec->num_kctl_used >= spec->num_kctl_alloc) {
- int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
- /* array + terminator */
- knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
- if (!knew)
- return -ENOMEM;
- if (spec->kctl_alloc) {
- memcpy(knew, spec->kctl_alloc,
- sizeof(*knew) * spec->num_kctl_alloc);
- kfree(spec->kctl_alloc);
- }
- spec->kctl_alloc = knew;
- spec->num_kctl_alloc = num;
- }
-
- knew = &spec->kctl_alloc[spec->num_kctl_used];
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
*knew = vt1708_control_templates[type];
knew->name = kstrdup(name, GFP_KERNEL);
-
if (!knew->name)
return -ENOMEM;
knew->private_value = val;
- spec->num_kctl_used++;
return 0;
}
+static void via_free_kctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
/* create input playback/capture controls for the given pin */
static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
const char *ctlname, int idx, int mix_nid)
@@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
+ via_free_kctls(codec); /* no longer needed */
return 0;
}
@@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec)
static void via_free(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- unsigned int i;
if (!spec)
return;
- if (spec->kctl_alloc) {
- for (i = 0; i < spec->num_kctl_used; i++)
- kfree(spec->kctl_alloc[i].name);
- kfree(spec->kctl_alloc);
- }
-
+ via_free_kctls(codec);
kfree(codec->spec);
}
@@ -1373,8 +1359,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
@@ -1846,8 +1832,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1709_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -2390,8 +2376,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
if (spec->autocfg.dig_in_pin)
spec->dig_in_nid = VT1708B_DIGIN_NID;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -2855,8 +2841,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
spec->extra_dig_out_nid = 0x15;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -3174,8 +3160,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
spec->extra_dig_out_nid = 0x1B;
- if (spec->kctl_alloc)
- spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
spec->input_mux = &spec->private_imux[0];
@@ -3262,74 +3248,97 @@ static int patch_vt1702(struct hda_codec *codec)
/*
* patch entries
*/
-struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+static struct hda_codec_preset snd_hda_preset_via[] = {
+ { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
+ { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
+ { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
+ { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
+ { .id = 0x1106e710, .name = "VT1709 10-Ch",
.patch = patch_vt1709_10ch},
- { .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+ { .id = 0x1106e711, .name = "VT1709 10-Ch",
.patch = patch_vt1709_10ch},
- { .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+ { .id = 0x1106e712, .name = "VT1709 10-Ch",
.patch = patch_vt1709_10ch},
- { .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+ { .id = 0x1106e713, .name = "VT1709 10-Ch",
.patch = patch_vt1709_10ch},
- { .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+ { .id = 0x1106e714, .name = "VT1709 6-Ch",
.patch = patch_vt1709_6ch},
- { .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+ { .id = 0x1106e715, .name = "VT1709 6-Ch",
.patch = patch_vt1709_6ch},
- { .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+ { .id = 0x1106e716, .name = "VT1709 6-Ch",
.patch = patch_vt1709_6ch},
- { .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+ { .id = 0x1106e717, .name = "VT1709 6-Ch",
.patch = patch_vt1709_6ch},
- { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+ { .id = 0x1106e720, .name = "VT1708B 8-Ch",
.patch = patch_vt1708B_8ch},
- { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+ { .id = 0x1106e721, .name = "VT1708B 8-Ch",
.patch = patch_vt1708B_8ch},
- { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+ { .id = 0x1106e722, .name = "VT1708B 8-Ch",
.patch = patch_vt1708B_8ch},
- { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+ { .id = 0x1106e723, .name = "VT1708B 8-Ch",
.patch = patch_vt1708B_8ch},
- { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+ { .id = 0x1106e724, .name = "VT1708B 4-Ch",
.patch = patch_vt1708B_4ch},
- { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+ { .id = 0x1106e725, .name = "VT1708B 4-Ch",
.patch = patch_vt1708B_4ch},
- { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+ { .id = 0x1106e726, .name = "VT1708B 4-Ch",
.patch = patch_vt1708B_4ch},
- { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+ { .id = 0x1106e727, .name = "VT1708B 4-Ch",
.patch = patch_vt1708B_4ch},
- { .id = 0x11060397, .name = "VIA VT1708S",
+ { .id = 0x11060397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VIA VT1708S",
+ { .id = 0x11061397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VIA VT1708S",
+ { .id = 0x11062397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VIA VT1708S",
+ { .id = 0x11063397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VIA VT1708S",
+ { .id = 0x11064397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VIA VT1708S",
+ { .id = 0x11065397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VIA VT1708S",
+ { .id = 0x11066397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VIA VT1708S",
+ { .id = 0x11067397, .name = "VT1708S",
.patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VIA VT1702",
+ { .id = 0x11060398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11061398, .name = "VIA VT1702",
+ { .id = 0x11061398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11062398, .name = "VIA VT1702",
+ { .id = 0x11062398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11063398, .name = "VIA VT1702",
+ { .id = 0x11063398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11064398, .name = "VIA VT1702",
+ { .id = 0x11064398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11065398, .name = "VIA VT1702",
+ { .id = 0x11065398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11066398, .name = "VIA VT1702",
+ { .id = 0x11066398, .name = "VT1702",
.patch = patch_vt1702},
- { .id = 0x11067398, .name = "VIA VT1702",
+ { .id = 0x11067398, .name = "VT1702",
.patch = patch_vt1702},
{} /* terminator */
};
+
+MODULE_ALIAS("snd-hda-codec-id:1106*");
+
+static struct hda_codec_preset_list via_list = {
+ .preset = snd_hda_preset_via,
+ .owner = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VIA HD-audio codec");
+
+static int __init patch_via_init(void)
+{
+ return snd_hda_add_codec_preset(&via_list);
+}
+
+static void __exit patch_via_exit(void)
+{
+ snd_hda_delete_codec_preset(&via_list);
+}
+
+module_init(patch_via_init)
+module_exit(patch_via_exit)
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 1b3f11702713..0dfa0540ce2c 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -382,23 +382,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
unsigned char status_mask =
VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX | VT1724_IRQ_MTPCM;
int handled = 0;
-#ifdef CONFIG_SND_DEBUG
int timeout = 0;
-#endif
while (1) {
status = inb(ICEREG1724(ice, IRQSTAT));
status &= status_mask;
if (status == 0)
break;
-#ifdef CONFIG_SND_DEBUG
if (++timeout > 10) {
- printk(KERN_ERR
- "ice1724: Too long irq loop, status = 0x%x\n",
- status);
+ status = inb(ICEREG1724(ice, IRQSTAT));
+ printk(KERN_ERR "ice1724: Too long irq loop, "
+ "status = 0x%x\n", status);
+ if (status & VT1724_IRQ_MPU_TX) {
+ printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
+ outb(inb(ICEREG1724(ice, IRQMASK)) |
+ VT1724_IRQ_MPU_TX,
+ ICEREG1724(ice, IRQMASK));
+ }
break;
}
-#endif
handled = 1;
if (status & VT1724_IRQ_MPU_TX) {
spin_lock(&ice->reg_lock);
@@ -2351,7 +2353,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
{
struct snd_ice1712 *ice;
int err;
- unsigned char mask;
static struct snd_device_ops ops = {
.dev_free = snd_vt1724_dev_free,
};
@@ -2412,9 +2413,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
return -EIO;
}
- /* unmask used interrupts */
- mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
- outb(mask, ICEREG1724(ice, IRQMASK));
+ /* MPU_RX and TX irq masks are cleared later dynamically */
+ outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
+
/* don't handle FIFO overrun/underruns (just yet),
* since they cause machine lockups
*/
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 9ff3f9e34404..59bbaf8f3e5b 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1670,7 +1670,7 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (status & HV_INT_PENDING)
- tasklet_hi_schedule(&chip->hwvol_tq);
+ tasklet_schedule(&chip->hwvol_tq);
/*
* ack an assp int if its running
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index ae7601f353a7..f23a73577c22 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1010,7 +1010,7 @@ static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *
.dev_free = snd_mixart_chip_dev_free,
};
- mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (! chip) {
snd_printk(KERN_ERR "cannot allocate chip\n");
return -ENOMEM;
@@ -1025,6 +1025,7 @@ static int __devinit snd_mixart_create(struct mixart_mgr *mgr, struct snd_card *
return err;
}
+ mgr->chip[idx] = chip;
snd_card_set_dev(card, &mgr->pci->dev);
return 0;
@@ -1377,6 +1378,7 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
if ((err = snd_mixart_create(mgr, card, i)) < 0) {
+ snd_card_free(card);
snd_mixart_free(mgr);
return err;
}
diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c
index b9a06c279397..d3350f383966 100644
--- a/sound/pci/mixart/mixart_core.c
+++ b/sound/pci/mixart/mixart_core.c
@@ -550,7 +550,7 @@ irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)
mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
mgr->msg_fifo_writeptr++;
mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
- tasklet_hi_schedule(&mgr->msg_taskq);
+ tasklet_schedule(&mgr->msg_taskq);
}
spin_unlock(&mgr->msg_lock);
break;
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index b60f6212745a..de999c6d6dd3 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -61,6 +61,7 @@ MODULE_PARM_DESC(enable, "enable card");
enum {
MODEL_CMEDIA_REF, /* C-Media's reference design */
MODEL_MERIDIAN, /* AuzenTech X-Meridian */
+ MODEL_HALO, /* HT-Omega Claro halo */
};
static struct pci_device_id oxygen_ids[] __devinitdata = {
@@ -74,6 +75,7 @@ static struct pci_device_id oxygen_ids[] __devinitdata = {
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_HALO },
{ }
};
MODULE_DEVICE_TABLE(pci, oxygen_ids);
@@ -301,6 +303,8 @@ static int generic_probe(struct oxygen *chip, unsigned long driver_data)
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF;
+ }
+ if (driver_data == MODEL_MERIDIAN || driver_data == MODEL_HALO) {
chip->model.misc_flags = OXYGEN_MISC_MIDI;
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
}
diff --git a/sound/pci/pcxhr/Makefile b/sound/pci/pcxhr/Makefile
index 10473c05918d..b06128e918ca 100644
--- a/sound/pci/pcxhr/Makefile
+++ b/sound/pci/pcxhr/Makefile
@@ -1,2 +1,2 @@
-snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o
+snd-pcxhr-objs := pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o pcxhr_mix22.o
obj-$(CONFIG_SND_PCXHR) += snd-pcxhr.o
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 73de6e989b3d..27cf2c28d113 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -40,18 +40,20 @@
#include "pcxhr_mixer.h"
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
#define DRIVER_NAME "pcxhr"
-MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>");
+MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
+ "Marc Titinger <titinger@digigram.com>");
MODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int mono[SNDRV_CARDS]; /* capture in mono only */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static int mono[SNDRV_CARDS]; /* capture mono only */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
@@ -67,18 +69,58 @@ enum {
PCI_ID_PCX882HR,
PCI_ID_VX881HR,
PCI_ID_PCX881HR,
+ PCI_ID_VX882E,
+ PCI_ID_PCX882E,
+ PCI_ID_VX881E,
+ PCI_ID_PCX881E,
+ PCI_ID_VX1222HR,
PCI_ID_PCX1222HR,
+ PCI_ID_VX1221HR,
PCI_ID_PCX1221HR,
+ PCI_ID_VX1222E,
+ PCI_ID_PCX1222E,
+ PCI_ID_VX1221E,
+ PCI_ID_PCX1221E,
+ PCI_ID_VX222HR,
+ PCI_ID_VX222E,
+ PCI_ID_PCX22HR,
+ PCI_ID_PCX22E,
+ PCI_ID_VX222HRMIC,
+ PCI_ID_VX222E_MIC,
+ PCI_ID_PCX924HR,
+ PCI_ID_PCX924E,
+ PCI_ID_PCX924HRMIC,
+ PCI_ID_PCX924E_MIC,
PCI_ID_LAST
};
static struct pci_device_id pcxhr_ids[] = {
- { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, }, /* VX882HR */
- { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, }, /* PCX882HR */
- { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, }, /* VX881HR */
- { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, }, /* PCX881HR */
- { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, }, /* PCX1222HR */
- { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, }, /* PCX1221HR */
+ { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xb021, 0, 0, PCI_ID_VX882E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb121, 0, 0, PCI_ID_PCX882E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb221, 0, 0, PCI_ID_VX881E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb321, 0, 0, PCI_ID_PCX881E, },
+ { 0x10b5, 0x9656, 0x1369, 0xb401, 0, 0, PCI_ID_VX1222HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb601, 0, 0, PCI_ID_VX1221HR, },
+ { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xb421, 0, 0, PCI_ID_VX1222E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb521, 0, 0, PCI_ID_PCX1222E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb621, 0, 0, PCI_ID_VX1221E, },
+ { 0x10b5, 0x9056, 0x1369, 0xb721, 0, 0, PCI_ID_PCX1221E, },
+ { 0x10b5, 0x9056, 0x1369, 0xba01, 0, 0, PCI_ID_VX222HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xba21, 0, 0, PCI_ID_VX222E, },
+ { 0x10b5, 0x9056, 0x1369, 0xbd01, 0, 0, PCI_ID_PCX22HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xbd21, 0, 0, PCI_ID_PCX22E, },
+ { 0x10b5, 0x9056, 0x1369, 0xbc01, 0, 0, PCI_ID_VX222HRMIC, },
+ { 0x10b5, 0x9056, 0x1369, 0xbc21, 0, 0, PCI_ID_VX222E_MIC, },
+ { 0x10b5, 0x9056, 0x1369, 0xbb01, 0, 0, PCI_ID_PCX924HR, },
+ { 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
+ { 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
+ { 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
{ 0, }
};
@@ -88,27 +130,55 @@ struct board_parameters {
char* board_name;
short playback_chips;
short capture_chips;
+ short fw_file_set;
short firmware_num;
};
static struct board_parameters pcxhr_board_params[] = {
-[PCI_ID_VX882HR] = { "VX882HR", 4, 4, 41, },
-[PCI_ID_PCX882HR] = { "PCX882HR", 4, 4, 41, },
-[PCI_ID_VX881HR] = { "VX881HR", 4, 4, 41, },
-[PCI_ID_PCX881HR] = { "PCX881HR", 4, 4, 41, },
-[PCI_ID_PCX1222HR] = { "PCX1222HR", 6, 1, 42, },
-[PCI_ID_PCX1221HR] = { "PCX1221HR", 6, 1, 42, },
+[PCI_ID_VX882HR] = { "VX882HR", 4, 4, 0, 41 },
+[PCI_ID_PCX882HR] = { "PCX882HR", 4, 4, 0, 41 },
+[PCI_ID_VX881HR] = { "VX881HR", 4, 4, 0, 41 },
+[PCI_ID_PCX881HR] = { "PCX881HR", 4, 4, 0, 41 },
+[PCI_ID_VX882E] = { "VX882e", 4, 4, 1, 41 },
+[PCI_ID_PCX882E] = { "PCX882e", 4, 4, 1, 41 },
+[PCI_ID_VX881E] = { "VX881e", 4, 4, 1, 41 },
+[PCI_ID_PCX881E] = { "PCX881e", 4, 4, 1, 41 },
+[PCI_ID_VX1222HR] = { "VX1222HR", 6, 1, 2, 42 },
+[PCI_ID_PCX1222HR] = { "PCX1222HR", 6, 1, 2, 42 },
+[PCI_ID_VX1221HR] = { "VX1221HR", 6, 1, 2, 42 },
+[PCI_ID_PCX1221HR] = { "PCX1221HR", 6, 1, 2, 42 },
+[PCI_ID_VX1222E] = { "VX1222e", 6, 1, 3, 42 },
+[PCI_ID_PCX1222E] = { "PCX1222e", 6, 1, 3, 42 },
+[PCI_ID_VX1221E] = { "VX1221e", 6, 1, 3, 42 },
+[PCI_ID_PCX1221E] = { "PCX1221e", 6, 1, 3, 42 },
+[PCI_ID_VX222HR] = { "VX222HR", 1, 1, 4, 44 },
+[PCI_ID_VX222E] = { "VX222e", 1, 1, 4, 44 },
+[PCI_ID_PCX22HR] = { "PCX22HR", 1, 0, 4, 44 },
+[PCI_ID_PCX22E] = { "PCX22e", 1, 0, 4, 44 },
+[PCI_ID_VX222HRMIC] = { "VX222HR-Mic", 1, 1, 5, 44 },
+[PCI_ID_VX222E_MIC] = { "VX222e-Mic", 1, 1, 5, 44 },
+[PCI_ID_PCX924HR] = { "PCX924HR", 1, 1, 5, 44 },
+[PCI_ID_PCX924E] = { "PCX924e", 1, 1, 5, 44 },
+[PCI_ID_PCX924HRMIC] = { "PCX924HR-Mic", 1, 1, 5, 44 },
+[PCI_ID_PCX924E_MIC] = { "PCX924e-Mic", 1, 1, 5, 44 },
};
+/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
+/* VX222HR, VX222e, PCX22HR and PCX22e */
+#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
+/* some boards do not support 192kHz on digital AES input plugs */
+#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
+ (x->fw_file_set == 0) || \
+ (x->fw_file_set == 2))
static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
unsigned int* realfreq)
{
unsigned int reg;
- if (freq < 6900 || freq > 110250)
+ if (freq < 6900 || freq > 110000)
return -EINVAL;
- reg = (28224000 * 10) / freq;
- reg = (reg + 5) / 10;
+ reg = (28224000 * 2) / freq;
+ reg = (reg - 1) / 2;
if (reg < 0x200)
*pllreg = reg + 0x800;
else if (reg < 0x400)
@@ -121,7 +191,7 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
reg &= ~3;
}
if (realfreq)
- *realfreq = ((28224000 * 10) / reg + 5) / 10;
+ *realfreq = (28224000 / (reg + 1));
return 0;
}
@@ -151,11 +221,6 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
#define PCXHR_FREQ_AES_3 0x03
#define PCXHR_FREQ_AES_4 0x0d
-#define PCXHR_MODIFY_CLOCK_S_BIT 0x04
-
-#define PCXHR_IRQ_TIMER_FREQ 92000
-#define PCXHR_IRQ_TIMER_PERIOD 48
-
static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
unsigned int *reg, unsigned int *freq)
{
@@ -196,19 +261,32 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0) {
snd_printk(KERN_ERR
- "error CMD_ACCESS_IO_WRITE for PLL register : %x!\n",
- err );
+ "error CMD_ACCESS_IO_WRITE "
+ "for PLL register : %x!\n", err);
return err;
}
}
break;
- case PCXHR_CLOCK_TYPE_WORD_CLOCK : val = PCXHR_FREQ_WORD_CLOCK; break;
- case PCXHR_CLOCK_TYPE_AES_SYNC : val = PCXHR_FREQ_SYNC_AES; break;
- case PCXHR_CLOCK_TYPE_AES_1 : val = PCXHR_FREQ_AES_1; break;
- case PCXHR_CLOCK_TYPE_AES_2 : val = PCXHR_FREQ_AES_2; break;
- case PCXHR_CLOCK_TYPE_AES_3 : val = PCXHR_FREQ_AES_3; break;
- case PCXHR_CLOCK_TYPE_AES_4 : val = PCXHR_FREQ_AES_4; break;
- default : return -EINVAL;
+ case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+ val = PCXHR_FREQ_WORD_CLOCK;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_SYNC:
+ val = PCXHR_FREQ_SYNC_AES;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_1:
+ val = PCXHR_FREQ_AES_1;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_2:
+ val = PCXHR_FREQ_AES_2;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_3:
+ val = PCXHR_FREQ_AES_3;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_4:
+ val = PCXHR_FREQ_AES_4;
+ break;
+ default:
+ return -EINVAL;
}
*reg = val;
*freq = realfreq;
@@ -216,14 +294,13 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
}
-int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
+ unsigned int rate,
+ int *changed)
{
unsigned int val, realfreq, speed;
struct pcxhr_rmh rmh;
- int err, changed;
-
- if (rate == 0)
- return 0; /* nothing to do */
+ int err;
err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);
if (err)
@@ -237,13 +314,17 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
else
speed = 2; /* quad speed */
if (mgr->codec_speed != speed) {
- pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+ if (DSP_EXT_CMD_SET(mgr)) {
+ rmh.cmd[1] = 1;
+ rmh.cmd_len = 2;
+ }
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
rmh.cmd[0] |= IO_NUM_SPEED_RATIO;
rmh.cmd[1] = speed;
rmh.cmd_len = 2;
@@ -253,25 +334,57 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
}
/* set the new frequency */
snd_printdd("clock register : set %x\n", val);
- err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK, val, &changed);
+ err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
+ val, changed);
if (err)
return err;
+
mgr->sample_rate_real = realfreq;
mgr->cur_clock_type = mgr->use_clock_type;
/* unmute after codec speed modes */
if (mgr->codec_speed != speed) {
- pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+ if (DSP_EXT_CMD_SET(mgr)) {
+ rmh.cmd[1] = 1;
+ rmh.cmd_len = 2;
+ }
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- mgr->codec_speed = speed; /* save new codec speed */
+ mgr->codec_speed = speed; /* save new codec speed */
}
+ snd_printdd("pcxhr_sub_set_clock to %dHz (realfreq=%d)\n",
+ rate, realfreq);
+ return 0;
+}
+
+#define PCXHR_MODIFY_CLOCK_S_BIT 0x04
+
+#define PCXHR_IRQ_TIMER_FREQ 92000
+#define PCXHR_IRQ_TIMER_PERIOD 48
+
+int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+{
+ struct pcxhr_rmh rmh;
+ int err, changed;
+
+ if (rate == 0)
+ return 0; /* nothing to do */
+
+ if (mgr->is_hr_stereo)
+ err = hr222_sub_set_clock(mgr, rate, &changed);
+ else
+ err = pcxhr_sub_set_clock(mgr, rate, &changed);
+
+ if (err)
+ return err;
+
if (changed) {
pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);
- rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos */
+ rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos */
if (rate < PCXHR_IRQ_TIMER_FREQ)
rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;
else
@@ -282,26 +395,39 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
if (err)
return err;
}
- snd_printdd("pcxhr_set_clock to %dHz (realfreq=%d)\n", rate, realfreq);
return 0;
}
-int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type,
- int *sample_rate)
+static int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
+ enum pcxhr_clock_type clock_type,
+ int *sample_rate)
{
struct pcxhr_rmh rmh;
unsigned char reg;
int err, rate;
switch (clock_type) {
- case PCXHR_CLOCK_TYPE_WORD_CLOCK : reg = REG_STATUS_WORD_CLOCK; break;
- case PCXHR_CLOCK_TYPE_AES_SYNC : reg = REG_STATUS_AES_SYNC; break;
- case PCXHR_CLOCK_TYPE_AES_1 : reg = REG_STATUS_AES_1; break;
- case PCXHR_CLOCK_TYPE_AES_2 : reg = REG_STATUS_AES_2; break;
- case PCXHR_CLOCK_TYPE_AES_3 : reg = REG_STATUS_AES_3; break;
- case PCXHR_CLOCK_TYPE_AES_4 : reg = REG_STATUS_AES_4; break;
- default : return -EINVAL;
+ case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+ reg = REG_STATUS_WORD_CLOCK;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_SYNC:
+ reg = REG_STATUS_AES_SYNC;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_1:
+ reg = REG_STATUS_AES_1;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_2:
+ reg = REG_STATUS_AES_2;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_3:
+ reg = REG_STATUS_AES_3;
+ break;
+ case PCXHR_CLOCK_TYPE_AES_4:
+ reg = REG_STATUS_AES_4;
+ break;
+ default:
+ return -EINVAL;
}
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
rmh.cmd_len = 2;
@@ -311,7 +437,7 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- udelay(100); /* wait minimum 2 sample_frames at 32kHz ! */
+ udelay(100); /* wait minimum 2 sample_frames at 32kHz ! */
mgr->last_reg_stat = reg;
}
rmh.cmd[1] = REG_STATUS_CURRENT;
@@ -336,6 +462,18 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
}
+int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
+ enum pcxhr_clock_type clock_type,
+ int *sample_rate)
+{
+ if (mgr->is_hr_stereo)
+ return hr222_get_external_clock(mgr, clock_type,
+ sample_rate);
+ else
+ return pcxhr_sub_get_external_clock(mgr, clock_type,
+ sample_rate);
+}
+
/*
* start or stop playback/capture substream
*/
@@ -350,7 +488,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
start = 1;
else {
if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
- snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state CANNOT be stopped\n");
+ snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state "
+ "CANNOT be stopped\n");
return -EINVAL;
}
start = 0;
@@ -359,11 +498,12 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
return -EINVAL;
stream->timer_abs_periods = 0;
- stream->timer_period_frag = 0; /* reset theoretical stream pos */
+ stream->timer_period_frag = 0; /* reset theoretical stream pos */
stream->timer_buf_periods = 0;
stream->timer_is_synced = 0;
- stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
+ stream_mask =
+ stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);
pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
@@ -373,8 +513,10 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
- snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n", err);
- stream->status = start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
+ snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n",
+ err);
+ stream->status =
+ start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
return err;
}
@@ -399,13 +541,15 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
header = HEADER_FMT_BASE_LIN;
break;
case SNDRV_PCM_FORMAT_S16_LE:
- header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS | HEADER_FMT_INTEL;
+ header = HEADER_FMT_BASE_LIN |
+ HEADER_FMT_16BITS | HEADER_FMT_INTEL;
break;
case SNDRV_PCM_FORMAT_S16_BE:
header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;
break;
case SNDRV_PCM_FORMAT_S24_3LE:
- header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS | HEADER_FMT_INTEL;
+ header = HEADER_FMT_BASE_LIN |
+ HEADER_FMT_24BITS | HEADER_FMT_INTEL;
break;
case SNDRV_PCM_FORMAT_S24_3BE:
header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;
@@ -414,7 +558,8 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
break;
default:
- snd_printk(KERN_ERR "error pcxhr_set_format() : unknown format\n");
+ snd_printk(KERN_ERR
+ "error pcxhr_set_format() : unknown format\n");
return -EINVAL;
}
chip = snd_pcm_substream_chip(stream->substream);
@@ -432,14 +577,31 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
is_capture = stream->pipe->is_capture;
stream_num = is_capture ? 0 : stream->substream->number;
- pcxhr_init_rmh(&rmh, is_capture ? CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
- pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
- if (is_capture)
- rmh.cmd[0] |= 1<<12;
+ pcxhr_init_rmh(&rmh, is_capture ?
+ CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
+ pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+ stream_num, 0);
+ if (is_capture) {
+ /* bug with old dsp versions: */
+ /* bit 12 also sets the format of the playback stream */
+ if (DSP_EXT_CMD_SET(chip->mgr))
+ rmh.cmd[0] |= 1<<10;
+ else
+ rmh.cmd[0] |= 1<<12;
+ }
rmh.cmd[1] = 0;
- rmh.cmd[2] = header >> 8;
- rmh.cmd[3] = (header & 0xff) << 16;
- rmh.cmd_len = 4;
+ rmh.cmd_len = 2;
+ if (DSP_EXT_CMD_SET(chip->mgr)) {
+ /* add channels and set bit 19 if channels>2 */
+ rmh.cmd[1] = stream->channels;
+ if (!is_capture) {
+ /* playback : add channel mask to command */
+ rmh.cmd[2] = (stream->channels == 1) ? 0x01 : 0x03;
+ rmh.cmd_len = 3;
+ }
+ }
+ rmh.cmd[rmh.cmd_len++] = header >> 8;
+ rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
snd_printk(KERN_ERR "ERROR pcxhr_set_format err=%x;\n", err);
@@ -456,30 +618,38 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
stream_num = is_capture ? 0 : subs->number;
- snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n",
+ snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : "
+ "addr(%p) bytes(%zx) subs(%d)\n",
is_capture ? 'c' : 'p',
chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
subs->runtime->dma_bytes, subs->number);
pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
- pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
+ pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+ stream_num, 0);
/* max buffer size is 2 MByte */
snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
- rmh.cmd[1] = subs->runtime->dma_bytes * 8; /* size in bits */
- rmh.cmd[2] = subs->runtime->dma_addr >> 24; /* most significant byte */
- rmh.cmd[2] |= 1<<19; /* this is a circular buffer */
- rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD; /* least 3 significant bytes */
+ /* size in bits */
+ rmh.cmd[1] = subs->runtime->dma_bytes * 8;
+ /* most significant byte */
+ rmh.cmd[2] = subs->runtime->dma_addr >> 24;
+ /* this is a circular buffer */
+ rmh.cmd[2] |= 1<<19;
+ /* least 3 significant bytes */
+ rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;
rmh.cmd_len = 4;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
- snd_printk(KERN_ERR "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
+ snd_printk(KERN_ERR
+ "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
return err;
}
#if 0
-static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, snd_pcm_uframes_t *sample_count)
+static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
+ snd_pcm_uframes_t *sample_count)
{
struct pcxhr_rmh rmh;
int err;
@@ -533,8 +703,8 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
for (j = 0; j < chip->nb_streams_play; j++) {
if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) {
playback_mask |= (1 << pipe->first_audio);
- break; /* add only once, as all playback streams of
- * one chip use the same pipe
+ break; /* add only once, as all playback
+ * streams of one chip use the same pipe
*/
}
}
@@ -545,19 +715,21 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
return;
}
- snd_printdd("pcxhr_trigger_tasklet : playback_mask=%x capture_mask=%x\n",
+ snd_printdd("pcxhr_trigger_tasklet : "
+ "playback_mask=%x capture_mask=%x\n",
playback_mask, capture_mask);
/* synchronous stop of all the pipes concerned */
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 0);
if (err) {
mutex_unlock(&mgr->setup_mutex);
- snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error stop pipes (P%x C%x)\n",
+ snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+ "error stop pipes (P%x C%x)\n",
playback_mask, capture_mask);
return;
}
- /* unfortunately the dsp lost format and buffer info with the stop pipe */
+ /* the dsp lost format and buffer info with the stop pipe */
for (i = 0; i < mgr->num_cards; i++) {
struct pcxhr_stream *stream;
chip = mgr->chip[i];
@@ -596,12 +768,15 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
if (err) {
mutex_unlock(&mgr->setup_mutex);
- snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error start pipes (P%x C%x)\n",
+ snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+ "error start pipes (P%x C%x)\n",
playback_mask, capture_mask);
return;
}
- /* put the streams into the running state now (increment pointer by interrupt) */
+ /* put the streams into the running state now
+ * (increment pointer by interrupt)
+ */
spin_lock_irqsave(&mgr->lock, flags);
for ( i =0; i < mgr->num_cards; i++) {
struct pcxhr_stream *stream;
@@ -615,7 +790,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
stream = &chip->playback_stream[j];
if (stream->status == PCXHR_STREAM_STATUS_STARTED) {
/* playback will already have advanced ! */
- stream->timer_period_frag += PCXHR_GRANULARITY;
+ stream->timer_period_frag += mgr->granularity;
stream->status = PCXHR_STREAM_STATUS_RUNNING;
}
}
@@ -653,7 +828,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
PCXHR_STREAM_STATUS_SCHEDULE_RUN;
snd_pcm_trigger_done(s, subs);
}
- tasklet_hi_schedule(&chip->mgr->trigger_taskq);
+ tasklet_schedule(&chip->mgr->trigger_taskq);
} else {
stream = subs->runtime->private_data;
snd_printdd("Only one Substream %c %d\n",
@@ -697,12 +872,14 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT);
if (start) {
- mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID; /* last dsp time invalid */
- rmh.cmd[0] |= PCXHR_GRANULARITY;
+ /* last dsp time invalid */
+ mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+ rmh.cmd[0] |= mgr->granularity;
}
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
- snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n", err);
+ snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n",
+ err);
return err;
}
@@ -713,38 +890,16 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
{
struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
struct pcxhr_mgr *mgr = chip->mgr;
- /*
- struct pcxhr_stream *stream = (pcxhr_stream_t*)subs->runtime->private_data;
- */
int err = 0;
snd_printdd("pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
subs->runtime->period_size, subs->runtime->periods,
subs->runtime->buffer_size);
- /*
- if(subs->runtime->period_size <= PCXHR_GRANULARITY) {
- snd_printk(KERN_ERR "pcxhr_prepare : error period_size too small (%x)\n",
- (unsigned int)subs->runtime->period_size);
- return -EINVAL;
- }
- */
-
mutex_lock(&mgr->setup_mutex);
do {
- /* if the stream was stopped before, format and buffer were reset */
- /*
- if(stream->status == PCXHR_STREAM_STATUS_STOPPED) {
- err = pcxhr_set_format(stream);
- if(err) break;
- err = pcxhr_update_r_buffer(stream);
- if(err) break;
- }
- */
-
/* only the first stream can choose the sample rate */
- /* the further opened streams will be limited to its frequency (see open) */
/* set the clock only once (first stream) */
if (mgr->sample_rate != subs->runtime->rate) {
err = pcxhr_set_clock(mgr, subs->runtime->rate);
@@ -787,22 +942,9 @@ static int pcxhr_hw_params(struct snd_pcm_substream *subs,
stream->channels = channels;
stream->format = format;
- /* set the format to the board */
- /*
- err = pcxhr_set_format(stream);
- if(err) {
- mutex_unlock(&mgr->setup_mutex);
- return err;
- }
- */
/* allocate buffer */
err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
- /*
- if (err > 0) {
- err = pcxhr_update_r_buffer(stream);
- }
- */
mutex_unlock(&mgr->setup_mutex);
return err;
@@ -820,14 +962,18 @@ static int pcxhr_hw_free(struct snd_pcm_substream *subs)
*/
static struct snd_pcm_hardware pcxhr_caps =
{
- .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
- 0 /*SNDRV_PCM_INFO_PAUSE*/),
- .formats = ( SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
- SNDRV_PCM_FMTBIT_FLOAT_LE ),
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = (SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_3BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS |
+ SNDRV_PCM_RATE_8000_192000),
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
@@ -847,6 +993,7 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
struct pcxhr_mgr *mgr = chip->mgr;
struct snd_pcm_runtime *runtime = subs->runtime;
struct pcxhr_stream *stream;
+ int err;
mutex_lock(&mgr->setup_mutex);
@@ -874,6 +1021,18 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
return -EBUSY;
}
+ /* float format support is in some cases buggy on stereo cards */
+ if (mgr->is_hr_stereo)
+ runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_FLOAT_LE;
+
+ /* buffer-size should better be multiple of period-size */
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0) {
+ mutex_unlock(&mgr->setup_mutex);
+ return err;
+ }
+
/* if a sample rate is already used or fixed by external clock,
* the stream cannot change
*/
@@ -889,7 +1048,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
mutex_unlock(&mgr->setup_mutex);
return -EBUSY;
}
- runtime->hw.rate_min = runtime->hw.rate_max = external_rate;
+ runtime->hw.rate_min = external_rate;
+ runtime->hw.rate_max = external_rate;
}
}
@@ -899,9 +1059,11 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
runtime->private_data = stream;
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
-
+ /* better get a divisor of granularity values (96 or 192) */
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
snd_pcm_set_sync(subs);
mgr->ref_count_rate++;
@@ -919,11 +1081,12 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
mutex_lock(&mgr->setup_mutex);
- snd_printdd("pcxhr_close chip%d subs%d\n", chip->chip_idx, subs->number);
+ snd_printdd("pcxhr_close chip%d subs%d\n",
+ chip->chip_idx, subs->number);
/* sample rate released */
if (--mgr->ref_count_rate == 0) {
- mgr->sample_rate = 0; /* the sample rate is no more locked */
+ mgr->sample_rate = 0; /* the sample rate is no more locked */
pcxhr_hardware_timer(mgr, 0); /* stop the DSP-timer */
}
@@ -1016,7 +1179,8 @@ static int pcxhr_chip_dev_free(struct snd_device *device)
/*
*/
-static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card, int idx)
+static int __devinit pcxhr_create(struct pcxhr_mgr *mgr,
+ struct snd_card *card, int idx)
{
int err;
struct snd_pcxhr *chip;
@@ -1024,7 +1188,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
.dev_free = pcxhr_chip_dev_free,
};
- mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (! chip) {
snd_printk(KERN_ERR "cannot allocate chip\n");
return -ENOMEM;
@@ -1040,7 +1204,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
if (idx < mgr->capture_chips) {
if (mgr->mono_capture)
- chip->nb_streams_capt = 2; /* 2 mono streams (left+right) */
+ chip->nb_streams_capt = 2; /* 2 mono streams */
else
chip->nb_streams_capt = 1; /* or 1 stereo stream */
}
@@ -1050,13 +1214,15 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
return err;
}
+ mgr->chip[idx] = chip;
snd_card_set_dev(card, &mgr->pci->dev);
return 0;
}
/* proc interface */
-static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
struct snd_pcxhr *chip = entry->private_data;
struct pcxhr_mgr *mgr = chip->mgr;
@@ -1069,8 +1235,10 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
short ver_maj = (mgr->dsp_version >> 16) & 0xff;
short ver_min = (mgr->dsp_version >> 8) & 0xff;
short ver_build = mgr->dsp_version & 0xff;
- snd_iprintf(buffer, "module version %s\n", PCXHR_DRIVER_VERSION_STRING);
- snd_iprintf(buffer, "dsp version %d.%d.%d\n", ver_maj, ver_min, ver_build);
+ snd_iprintf(buffer, "module version %s\n",
+ PCXHR_DRIVER_VERSION_STRING);
+ snd_iprintf(buffer, "dsp version %d.%d.%d\n",
+ ver_maj, ver_min, ver_build);
if (mgr->board_has_analog)
snd_iprintf(buffer, "analog io available\n");
else
@@ -1084,18 +1252,22 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
if (ref > 0) {
if (mgr->sample_rate_real != 0 &&
mgr->sample_rate_real != 48000) {
- ref = (ref * 48000) / mgr->sample_rate_real;
- if (mgr->sample_rate_real >= PCXHR_IRQ_TIMER_FREQ)
+ ref = (ref * 48000) /
+ mgr->sample_rate_real;
+ if (mgr->sample_rate_real >=
+ PCXHR_IRQ_TIMER_FREQ)
ref *= 2;
}
cur = 100 - (100 * cur) / ref;
snd_iprintf(buffer, "cpu load %d%%\n", cur);
- snd_iprintf(buffer, "buffer pool %d/%d kWords\n",
+ snd_iprintf(buffer, "buffer pool %d/%d\n",
rmh.stat[2], rmh.stat[3]);
}
}
- snd_iprintf(buffer, "dma granularity : %d\n", PCXHR_GRANULARITY);
- snd_iprintf(buffer, "dsp time errors : %d\n", mgr->dsp_time_err);
+ snd_iprintf(buffer, "dma granularity : %d\n",
+ mgr->granularity);
+ snd_iprintf(buffer, "dsp time errors : %d\n",
+ mgr->dsp_time_err);
snd_iprintf(buffer, "dsp async pipe xrun errors : %d\n",
mgr->async_err_pipe_xrun);
snd_iprintf(buffer, "dsp async stream xrun errors : %d\n",
@@ -1110,33 +1282,52 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
rmh.cmd_idx = CMD_LAST_INDEX;
if( ! pcxhr_send_msg(mgr, &rmh) ) {
int i;
+ if (rmh.stat_len > 8)
+ rmh.stat_len = 8;
for (i = 0; i < rmh.stat_len; i++)
- snd_iprintf(buffer, "debug[%02d] = %06x\n", i, rmh.stat[i]);
+ snd_iprintf(buffer, "debug[%02d] = %06x\n",
+ i, rmh.stat[i]);
}
} else
snd_iprintf(buffer, "no firmware loaded\n");
snd_iprintf(buffer, "\n");
}
-static void pcxhr_proc_sync(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_sync(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
{
struct snd_pcxhr *chip = entry->private_data;
struct pcxhr_mgr *mgr = chip->mgr;
- static char *texts[7] = {
- "Internal", "Word", "AES Sync", "AES 1", "AES 2", "AES 3", "AES 4"
+ static const char *textsHR22[3] = {
+ "Internal", "AES Sync", "AES 1"
+ };
+ static const char *textsPCXHR[7] = {
+ "Internal", "Word", "AES Sync",
+ "AES 1", "AES 2", "AES 3", "AES 4"
};
+ const char **texts;
+ int max_clock;
+ if (mgr->is_hr_stereo) {
+ texts = textsHR22;
+ max_clock = HR22_CLOCK_TYPE_MAX;
+ } else {
+ texts = textsPCXHR;
+ max_clock = PCXHR_CLOCK_TYPE_MAX;
+ }
snd_iprintf(buffer, "\n%s\n", mgr->longname);
- snd_iprintf(buffer, "Current Sample Clock\t: %s\n", texts[mgr->cur_clock_type]);
- snd_iprintf(buffer, "Current Sample Rate\t= %d\n", mgr->sample_rate_real);
-
+ snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
+ texts[mgr->cur_clock_type]);
+ snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
+ mgr->sample_rate_real);
/* commands available when embedded DSP is running */
if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
int i, err, sample_rate;
- for (i = PCXHR_CLOCK_TYPE_WORD_CLOCK; i< (3 + mgr->capture_chips); i++) {
+ for (i = 1; i <= max_clock; i++) {
err = pcxhr_get_external_clock(mgr, i, &sample_rate);
if (err)
break;
- snd_iprintf(buffer, "%s Clock\t\t= %d\n", texts[i], sample_rate);
+ snd_iprintf(buffer, "%s Clock\t\t= %d\n",
+ texts[i], sample_rate);
}
} else
snd_iprintf(buffer, "no firmware loaded\n");
@@ -1194,7 +1385,8 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
/*
* probe function - creates the card manager
*/
-static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int __devinit pcxhr_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct pcxhr_mgr *mgr;
@@ -1217,7 +1409,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
/* check if we can restrict PCI DMA transfers to 32 bits */
if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0) {
- snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+ snd_printk(KERN_ERR "architecture does not support "
+ "32bit PCI busmaster DMA\n");
pci_disable_device(pci);
return -ENXIO;
}
@@ -1234,11 +1427,25 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
pci_disable_device(pci);
return -ENODEV;
}
- card_name = pcxhr_board_params[pci_id->driver_data].board_name;
- mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;
- mgr->capture_chips = pcxhr_board_params[pci_id->driver_data].capture_chips;
- mgr->firmware_num = pcxhr_board_params[pci_id->driver_data].firmware_num;
+ card_name =
+ pcxhr_board_params[pci_id->driver_data].board_name;
+ mgr->playback_chips =
+ pcxhr_board_params[pci_id->driver_data].playback_chips;
+ mgr->capture_chips =
+ pcxhr_board_params[pci_id->driver_data].capture_chips;
+ mgr->fw_file_set =
+ pcxhr_board_params[pci_id->driver_data].fw_file_set;
+ mgr->firmware_num =
+ pcxhr_board_params[pci_id->driver_data].firmware_num;
mgr->mono_capture = mono[dev];
+ mgr->is_hr_stereo = (mgr->playback_chips == 1);
+ mgr->board_has_aes1 = PCXHR_BOARD_HAS_AES1(mgr);
+ mgr->board_aes_in_192k = !PCXHR_BOARD_AESIN_NO_192K(mgr);
+
+ if (mgr->is_hr_stereo)
+ mgr->granularity = PCXHR_GRANULARITY_HR22;
+ else
+ mgr->granularity = PCXHR_GRANULARITY;
/* resource assignment */
if ((err = pci_request_regions(pci, card_name)) < 0) {
@@ -1261,7 +1468,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
mgr->irq = pci->irq;
sprintf(mgr->shortname, "Digigram %s", card_name);
- sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i", mgr->shortname,
+ sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i",
+ mgr->shortname,
mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
/* ISR spinlock */
@@ -1272,10 +1480,14 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
mutex_init(&mgr->setup_mutex);
/* init taslket */
- tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet, (unsigned long) mgr);
- tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet, (unsigned long) mgr);
+ tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
+ (unsigned long) mgr);
+ tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
+ (unsigned long) mgr);
+
mgr->prmh = kmalloc(sizeof(*mgr->prmh) +
- sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS),
+ sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
+ PCXHR_SIZE_MAX_STATUS),
GFP_KERNEL);
if (! mgr->prmh) {
pcxhr_free(mgr);
@@ -1296,7 +1508,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
else
idx = index[dev] + i;
- snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : card_name, i);
+ snprintf(tmpid, sizeof(tmpid), "%s-%d",
+ id[dev] ? id[dev] : card_name, i);
card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
if (! card) {
@@ -1310,6 +1523,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
if ((err = pcxhr_create(mgr, card, i)) < 0) {
+ snd_card_free(card);
pcxhr_free(mgr);
return err;
}
diff --git a/sound/pci/pcxhr/pcxhr.h b/sound/pci/pcxhr/pcxhr.h
index 652064787a55..84131a916c92 100644
--- a/sound/pci/pcxhr/pcxhr.h
+++ b/sound/pci/pcxhr/pcxhr.h
@@ -27,15 +27,18 @@
#include <linux/mutex.h>
#include <sound/pcm.h>
-#define PCXHR_DRIVER_VERSION 0x000804 /* 0.8.4 */
-#define PCXHR_DRIVER_VERSION_STRING "0.8.4" /* 0.8.4 */
+#define PCXHR_DRIVER_VERSION 0x000905 /* 0.9.5 */
+#define PCXHR_DRIVER_VERSION_STRING "0.9.5" /* 0.9.5 */
-#define PCXHR_MAX_CARDS 6
-#define PCXHR_PLAYBACK_STREAMS 4
+#define PCXHR_MAX_CARDS 6
+#define PCXHR_PLAYBACK_STREAMS 4
-#define PCXHR_GRANULARITY 96 /* transfer granularity (should be min 96 and multiple of 48) */
-#define PCXHR_GRANULARITY_MIN 96 /* transfer granularity of pipes and the dsp time (MBOX4) */
+#define PCXHR_GRANULARITY 96 /* min 96 and multiple of 48 */
+/* transfer granularity of pipes and the dsp time (MBOX4) */
+#define PCXHR_GRANULARITY_MIN 96
+/* TODO : granularity could be 64 or 128 */
+#define PCXHR_GRANULARITY_HR22 192 /* granularity for stereo cards */
struct snd_pcxhr;
struct pcxhr_mgr;
@@ -51,6 +54,11 @@ enum pcxhr_clock_type {
PCXHR_CLOCK_TYPE_AES_2,
PCXHR_CLOCK_TYPE_AES_3,
PCXHR_CLOCK_TYPE_AES_4,
+ PCXHR_CLOCK_TYPE_MAX = PCXHR_CLOCK_TYPE_AES_4,
+ HR22_CLOCK_TYPE_INTERNAL = PCXHR_CLOCK_TYPE_INTERNAL,
+ HR22_CLOCK_TYPE_AES_SYNC,
+ HR22_CLOCK_TYPE_AES_1,
+ HR22_CLOCK_TYPE_MAX = HR22_CLOCK_TYPE_AES_1,
};
struct pcxhr_mgr {
@@ -61,6 +69,8 @@ struct pcxhr_mgr {
int irq;
+ int granularity;
+
/* card access with 1 mem bar and 2 io bar's */
unsigned long port[3];
@@ -83,11 +93,16 @@ struct pcxhr_mgr {
/* hardware interface */
unsigned int dsp_loaded; /* bit flags of loaded dsp indices */
unsigned int dsp_version; /* read from embedded once firmware is loaded */
- int board_has_analog; /* if 0 the board is digital only */
- int mono_capture; /* if 1 the board does mono capture */
- int playback_chips; /* 4 or 6 */
- int capture_chips; /* 4 or 1 */
- int firmware_num; /* 41 or 42 */
+ int playback_chips;
+ int capture_chips;
+ int fw_file_set;
+ int firmware_num;
+ int is_hr_stereo:1;
+ int board_has_aes1:1; /* if 1 board has AES1 plug and SRC */
+ int board_has_analog:1; /* if 0 the board is digital only */
+ int board_has_mic:1; /* if 1 the board has microphone input */
+ int board_aes_in_192k:1;/* if 1 the aes input plugs do support 192kHz */
+ int mono_capture:1; /* if 1 the board does mono capture */
struct snd_dma_buffer hostport;
@@ -106,6 +121,9 @@ struct pcxhr_mgr {
int async_err_stream_xrun;
int async_err_pipe_xrun;
int async_err_other_last;
+
+ unsigned char xlx_cfg; /* copy of PCXHR_XLX_CFG register */
+ unsigned char xlx_selmic; /* copy of PCXHR_XLX_SELMIC register */
};
@@ -155,24 +173,30 @@ struct snd_pcxhr {
struct snd_pcm *pcm; /* PCM */
- struct pcxhr_pipe playback_pipe; /* 1 stereo pipe only */
- struct pcxhr_pipe capture_pipe[2]; /* 1 stereo pipe or 2 mono pipes */
+ struct pcxhr_pipe playback_pipe; /* 1 stereo pipe only */
+ struct pcxhr_pipe capture_pipe[2]; /* 1 stereo or 2 mono pipes */
struct pcxhr_stream playback_stream[PCXHR_PLAYBACK_STREAMS];
- struct pcxhr_stream capture_stream[2]; /* 1 stereo stream or 2 mono streams */
+ struct pcxhr_stream capture_stream[2]; /* 1 stereo or 2 mono streams */
int nb_streams_play;
int nb_streams_capt;
- int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */
- int analog_playback_volume[2]; /* Mixer : Master Playback Volume */
- int analog_capture_volume[2]; /* Mixer : Master Capture Volume */
- int digital_playback_active[PCXHR_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [streams][stereo]*/
- int digital_playback_volume[PCXHR_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [streams][stereo]*/
- int digital_capture_volume[2]; /* Mixer : Digital Capture Volume [stereo] */
- int monitoring_active[2]; /* Mixer : Monitoring Active */
- int monitoring_volume[2]; /* Mixer : Monitoring Volume */
- int audio_capture_source; /* Mixer : Audio Capture Source */
- unsigned char aes_bits[5]; /* Mixer : IEC958_AES bits */
+ int analog_playback_active[2]; /* Mixer : Master Playback !mute */
+ int analog_playback_volume[2]; /* Mixer : Master Playback Volume */
+ int analog_capture_volume[2]; /* Mixer : Master Capture Volume */
+ int digital_playback_active[PCXHR_PLAYBACK_STREAMS][2];
+ int digital_playback_volume[PCXHR_PLAYBACK_STREAMS][2];
+ int digital_capture_volume[2]; /* Mixer : Digital Capture Volume */
+ int monitoring_active[2]; /* Mixer : Monitoring Active */
+ int monitoring_volume[2]; /* Mixer : Monitoring Volume */
+ int audio_capture_source; /* Mixer : Audio Capture Source */
+ int mic_volume; /* used by cards with MIC only */
+ int mic_boost; /* used by cards with MIC only */
+ int mic_active; /* used by cards with MIC only */
+ int analog_capture_active; /* used by cards with MIC only */
+ int phantom_power; /* used by cards with MIC only */
+
+ unsigned char aes_bits[5]; /* Mixer : IEC958_AES bits */
};
struct pcxhr_hostport
@@ -184,6 +208,8 @@ struct pcxhr_hostport
/* exported */
int pcxhr_create_pcm(struct snd_pcxhr *chip);
int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate);
-int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type, int *sample_rate);
+int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
+ enum pcxhr_clock_type clock_type,
+ int *sample_rate);
#endif /* __SOUND_PCXHR_H */
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 7143259cfe34..833e7180ad2d 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -132,13 +132,15 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
*read = PCXHR_INPB(mgr, reg);
if ((*read & mask) == bit) {
if (i > 100)
- snd_printdd("ATTENTION! check_reg(%x) loopcount=%d\n",
+ snd_printdd("ATTENTION! check_reg(%x) "
+ "loopcount=%d\n",
reg, i);
return 0;
}
i++;
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=0x%x\n",
+ snd_printk(KERN_ERR
+ "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
reg, mask, *read);
return -EIO;
}
@@ -159,18 +161,22 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
#define PCXHR_IT_TEST_XILINX (0x0000003C | PCXHR_MASK_IT_HF1 | \
PCXHR_MASK_IT_MANAGE_HF5)
#define PCXHR_IT_DOWNLOAD_BOOT (0x0000000C | PCXHR_MASK_IT_HF1 | \
- PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
+ PCXHR_MASK_IT_MANAGE_HF5 | \
+ PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_RESET_BOARD_FUNC (0x0000000C | PCXHR_MASK_IT_HF0 | \
- PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT_EXTRA)
+ PCXHR_MASK_IT_MANAGE_HF5 | \
+ PCXHR_MASK_IT_WAIT_EXTRA)
#define PCXHR_IT_DOWNLOAD_DSP (0x0000000C | \
- PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
+ PCXHR_MASK_IT_MANAGE_HF5 | \
+ PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_DEBUG (0x0000005A | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_SEMAPHORE (0x0000005C | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_MESSAGE (0x00000074 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_CHK (0x00000076 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_UPDATE_RBUFFER (0x00000078 | PCXHR_MASK_IT_NO_HF0_HF1)
-static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atomic)
+static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
+ unsigned int itdsp, int atomic)
{
int err;
unsigned char reg;
@@ -178,17 +184,21 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
/* clear hf5 bit */
PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
- PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & ~PCXHR_MBOX0_HF5);
+ PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) &
+ ~PCXHR_MBOX0_HF5);
}
if ((itdsp & PCXHR_MASK_IT_NO_HF0_HF1) == 0) {
- reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
+ reg = (PCXHR_ICR_HI08_RREQ |
+ PCXHR_ICR_HI08_TREQ |
+ PCXHR_ICR_HI08_HDRQ);
if (itdsp & PCXHR_MASK_IT_HF0)
reg |= PCXHR_ICR_HI08_HF0;
if (itdsp & PCXHR_MASK_IT_HF1)
reg |= PCXHR_ICR_HI08_HF1;
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
}
- reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) | PCXHR_CVR_HI08_HC);
+ reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) |
+ PCXHR_CVR_HI08_HC);
PCXHR_OUTPB(mgr, PCXHR_DSP_CVR, reg);
if (itdsp & PCXHR_MASK_IT_WAIT) {
if (atomic)
@@ -211,10 +221,14 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
}
if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
/* wait for hf5 bit */
- err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0, PCXHR_MBOX0_HF5,
- PCXHR_MBOX0_HF5, PCXHR_TIMEOUT_DSP, &reg);
+ err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0,
+ PCXHR_MBOX0_HF5,
+ PCXHR_MBOX0_HF5,
+ PCXHR_TIMEOUT_DSP,
+ &reg);
if (err) {
- snd_printk(KERN_ERR "pcxhr_send_it_dsp : TIMEOUT HF5\n");
+ snd_printk(KERN_ERR
+ "pcxhr_send_it_dsp : TIMEOUT HF5\n");
return err;
}
}
@@ -263,7 +277,8 @@ void pcxhr_enable_dsp(struct pcxhr_mgr *mgr)
/*
* load the xilinx image
*/
-int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilinx, int second)
+int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr,
+ const struct firmware *xilinx, int second)
{
unsigned int i;
unsigned int chipsc;
@@ -274,7 +289,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
/* test first xilinx */
chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
/* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
- /* this bit will always be 1; no possibility to test presence of first xilinx */
+ /* this bit will always be 1;
+ * no possibility to test presence of first xilinx
+ */
if(second) {
if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
snd_printk(KERN_ERR "error loading first xilinx\n");
@@ -290,7 +307,8 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
data = *image;
mask = 0x80;
while (mask) {
- chipsc &= ~(PCXHR_CHIPSC_DATA_CLK | PCXHR_CHIPSC_DATA_IN);
+ chipsc &= ~(PCXHR_CHIPSC_DATA_CLK |
+ PCXHR_CHIPSC_DATA_IN);
if (data & mask)
chipsc |= PCXHR_CHIPSC_DATA_IN;
PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
@@ -330,15 +348,20 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
data = dsp->data + i;
if (i == 0) {
/* test data header consistency */
- len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]);
- if (len && dsp->size != (len + 2) * 3)
+ len = (unsigned int)((data[0]<<16) +
+ (data[1]<<8) +
+ data[2]);
+ if (len && (dsp->size != (len + 2) * 3))
return -EINVAL;
}
/* wait DSP ready for new transfer */
- err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
- PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &dummy);
+ err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+ PCXHR_ISR_HI08_TRDY,
+ PCXHR_ISR_HI08_TRDY,
+ PCXHR_TIMEOUT_DSP, &dummy);
if (err) {
- snd_printk(KERN_ERR "dsp loading error at position %d\n", i);
+ snd_printk(KERN_ERR
+ "dsp loading error at position %d\n", i);
return err;
}
/* send host data */
@@ -357,7 +380,8 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
/*
* load the eeprom image
*/
-int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eeprom)
+int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr,
+ const struct firmware *eeprom)
{
int err;
unsigned char reg;
@@ -365,7 +389,9 @@ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eepro
/* init value of the ICR register */
reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
if (PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & PCXHR_MBOX0_BOOT_HERE) {
- /* no need to load the eeprom binary, but init the HI08 interface */
+ /* no need to load the eeprom binary,
+ * but init the HI08 interface
+ */
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg | PCXHR_ICR_HI08_INIT);
msleep(PCXHR_WAIT_DEFAULT);
PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
@@ -429,8 +455,10 @@ int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, const struct firmware *dsp)
if (err)
return err;
/* wait for chk bit */
- return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
- PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, &dummy);
+ return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+ PCXHR_ISR_HI08_CHK,
+ PCXHR_ISR_HI08_CHK,
+ PCXHR_TIMEOUT_DSP, &dummy);
}
@@ -443,8 +471,8 @@ struct pcxhr_cmd_info {
/* RMH status type */
enum {
RMH_SSIZE_FIXED = 0, /* status size fix (st_length = 0..x) */
- RMH_SSIZE_ARG = 1, /* status size given in the LSB byte (used with st_length = 1) */
- RMH_SSIZE_MASK = 2, /* status size given in bitmask (used with st_length = 1) */
+ RMH_SSIZE_ARG = 1, /* status size given in the LSB byte */
+ RMH_SSIZE_MASK = 2, /* status size given in bitmask */
};
/*
@@ -474,7 +502,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[CMD_UPDATE_R_BUFFERS] = { 0x840000, 0, RMH_SSIZE_FIXED },
[CMD_FORMAT_STREAM_OUT] = { 0x860000, 0, RMH_SSIZE_FIXED },
[CMD_FORMAT_STREAM_IN] = { 0x870000, 0, RMH_SSIZE_FIXED },
-[CMD_STREAM_SAMPLE_COUNT] = { 0x902000, 2, RMH_SSIZE_FIXED }, /* stat_len = nb_streams * 2 */
+[CMD_STREAM_SAMPLE_COUNT] = { 0x902000, 2, RMH_SSIZE_FIXED },
[CMD_AUDIO_LEVEL_ADJUST] = { 0xc22000, 0, RMH_SSIZE_FIXED },
};
@@ -524,10 +552,13 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
for (i = 0; i < rmh->stat_len; i++) {
/* wait for receiver full */
- err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
- PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
+ err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+ PCXHR_ISR_HI08_RXDF,
+ PCXHR_ISR_HI08_RXDF,
+ PCXHR_TIMEOUT_DSP, &reg);
if (err) {
- snd_printk(KERN_ERR "ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )\n",
+ snd_printk(KERN_ERR "ERROR RMH stat: "
+ "ISR:RXDF=1 (ISR = %x; i=%d )\n",
reg, i);
return err;
}
@@ -537,10 +568,10 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
/* need to update rmh->stat_len on the fly ?? */
- if (i==0) {
+ if (!i) {
if (rmh->dsp_stat != RMH_SSIZE_FIXED) {
if (rmh->dsp_stat == RMH_SSIZE_ARG) {
- rmh->stat_len = (u16)(data & 0x0000ff) + 1;
+ rmh->stat_len = (data & 0x0000ff) + 1;
data &= 0xffff00;
} else {
/* rmh->dsp_stat == RMH_SSIZE_MASK */
@@ -562,7 +593,8 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
rmh->stat[i] = data;
}
if (rmh->stat_len > max_stat_len) {
- snd_printdd("PCXHR : rmh->stat_len=%x too big\n", rmh->stat_len);
+ snd_printdd("PCXHR : rmh->stat_len=%x too big\n",
+ rmh->stat_len);
rmh->stat_len = max_stat_len;
}
return 0;
@@ -605,7 +637,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data &= 0xff7fff; /* MASK_1_WORD_COMMAND */
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (rmh->cmd_idx < CMD_LAST_INDEX)
- snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]);
+ snd_printdd("MSG cmd[0]=%x (%s)\n",
+ data, cmd_names[rmh->cmd_idx]);
#endif
err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
@@ -619,8 +652,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
if (rmh->cmd_len > 1) {
/* send length */
data = rmh->cmd_len - 1;
- err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
- PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &reg);
+ err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+ PCXHR_ISR_HI08_TRDY,
+ PCXHR_ISR_HI08_TRDY,
+ PCXHR_TIMEOUT_DSP, &reg);
if (err)
return err;
PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
@@ -653,8 +688,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
/* test status ISR */
if (reg & PCXHR_ISR_HI08_ERR) {
/* ERROR, wait for receiver full */
- err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_RXDF,
- PCXHR_ISR_HI08_RXDF, PCXHR_TIMEOUT_DSP, &reg);
+ err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
+ PCXHR_ISR_HI08_RXDF,
+ PCXHR_ISR_HI08_RXDF,
+ PCXHR_TIMEOUT_DSP, &reg);
if (err) {
snd_printk(KERN_ERR "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
return err;
@@ -663,7 +700,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
- snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n", rmh->cmd_idx, data);
+ snd_printk(KERN_ERR "ERROR RMH(%d): 0x%x\n",
+ rmh->cmd_idx, data);
err = -EINVAL;
} else {
/* read the response data */
@@ -732,8 +770,9 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
{
int start_mask = PCXHR_INPL(mgr, PCXHR_PLX_MBOX2);
- /* least segnificant 12 bits are the pipe states for the playback audios */
- /* next 12 bits are the pipe states for the capture audios
+ /* least segnificant 12 bits are the pipe states
+ * for the playback audios
+ * next 12 bits are the pipe states for the capture audios
* (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
*/
start_mask &= 0xffffff;
@@ -744,7 +783,8 @@ static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
#define PCXHR_PIPE_STATE_CAPTURE_OFFSET 12
#define MAX_WAIT_FOR_DSP 20
-static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *retry)
+static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr,
+ int audio_mask, int *retry)
{
struct pcxhr_rmh rmh;
int err;
@@ -760,17 +800,20 @@ static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *
} else {
/* can start capture pipe */
pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
- PCXHR_PIPE_STATE_CAPTURE_OFFSET,
- 0, 0);
+ PCXHR_PIPE_STATE_CAPTURE_OFFSET,
+ 0, 0);
}
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
- "error pipe start (CMD_CAN_START_PIPE) err=%x!\n",
+ "error pipe start "
+ "(CMD_CAN_START_PIPE) err=%x!\n",
err);
return err;
}
- /* if the pipe couldn't be prepaired for start, retry it later */
+ /* if the pipe couldn't be prepaired for start,
+ * retry it later
+ */
if (rmh.stat[0] == 0)
*retry |= (1<<audio);
}
@@ -795,14 +838,14 @@ static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
} else {
/* stop capture pipe */
pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
- PCXHR_PIPE_STATE_CAPTURE_OFFSET,
- 0, 0);
+ PCXHR_PIPE_STATE_CAPTURE_OFFSET,
+ 0, 0);
}
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
- "error pipe stop (CMD_STOP_PIPE) err=%x!\n",
- err);
+ "error pipe stop "
+ "(CMD_STOP_PIPE) err=%x!\n", err);
return err;
}
}
@@ -822,15 +865,16 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
if (audio_mask & 1) {
pcxhr_init_rmh(&rmh, CMD_CONF_PIPE);
if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET)
- pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0, 1 << audio);
+ pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0,
+ 1 << audio);
else
pcxhr_set_pipe_cmd_params(&rmh, 1, 0, 0,
1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
snd_printk(KERN_ERR
- "error pipe start (CMD_CONF_PIPE) err=%x!\n",
- err);
+ "error pipe start "
+ "(CMD_CONF_PIPE) err=%x!\n", err);
return err;
}
}
@@ -841,7 +885,9 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);
err = pcxhr_send_msg(mgr, &rmh);
if (err) {
- snd_printk(KERN_ERR "error pipe start (CMD_SEND_IRQA) err=%x!\n", err );
+ snd_printk(KERN_ERR
+ "error pipe start (CMD_SEND_IRQA) err=%x!\n",
+ err);
return err;
}
return 0;
@@ -849,7 +895,8 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
-int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_mask, int start)
+int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
+ int capture_mask, int start)
{
int state, i, err;
int audio_mask;
@@ -858,21 +905,23 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
struct timeval my_tv1, my_tv2;
do_gettimeofday(&my_tv1);
#endif
- audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
+ audio_mask = (playback_mask |
+ (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
/* current pipe state (playback + record) */
state = pcxhr_pipes_running(mgr);
snd_printdd("pcxhr_set_pipe_state %s (mask %x current %x)\n",
start ? "START" : "STOP", audio_mask, state);
if (start) {
- audio_mask &= ~state; /* start only pipes that are not yet started */
+ /* start only pipes that are not yet started */
+ audio_mask &= ~state;
state = audio_mask;
for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {
err = pcxhr_prepair_pipe_start(mgr, state, &state);
if (err)
return err;
if (state == 0)
- break; /* success, all pipes prepaired for start */
- mdelay(1); /* otherwise wait 1 millisecond and retry */
+ break; /* success, all pipes prepaired */
+ mdelay(1); /* wait 1 millisecond and retry */
}
} else {
audio_mask &= state; /* stop only pipes that are started */
@@ -891,7 +940,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
if ((state & audio_mask) == (start ? audio_mask : 0))
break;
if (++i >= MAX_WAIT_FOR_DSP * 100) {
- snd_printk(KERN_ERR "error pipe start/stop (ED_NO_RESPONSE_AT_IRQA)\n");
+ snd_printk(KERN_ERR "error pipe start/stop\n");
return -EBUSY;
}
udelay(10); /* wait 10 microseconds */
@@ -918,7 +967,8 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
spin_lock_irqsave(&mgr->msg_lock, flags);
if ((mgr->io_num_reg_cont & mask) == value) {
- snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n", mask, value);
+ snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n",
+ mask, value);
if (changed)
*changed = 0;
spin_unlock_irqrestore(&mgr->msg_lock, flags);
@@ -971,7 +1021,8 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
err = ((err >> 12) & 0xfff);
if (!err)
return 0;
- snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n", err_src_name[err_src],
+ snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
+ err_src_name[err_src],
is_capture ? "Record" : "Play", pipe, err);
if (err == 0xe01)
mgr->async_err_stream_xrun++;
@@ -996,6 +1047,13 @@ void pcxhr_msg_tasklet(unsigned long arg)
snd_printdd("TASKLET : PCXHR_IRQ_TIME_CODE event occured\n");
if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
snd_printdd("TASKLET : PCXHR_IRQ_NOTIFY event occured\n");
+ if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
+ /* clear events FREQ_CHANGE and TIME_CODE */
+ pcxhr_init_rmh(prmh, CMD_TEST_IT);
+ err = pcxhr_send_msg(mgr, prmh);
+ snd_printdd("CMD_TEST_IT : err=%x, stat=%x\n",
+ err, prmh->stat[0]);
+ }
if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
snd_printdd("TASKLET : PCXHR_IRQ_ASYNC event occured\n");
@@ -1005,18 +1063,22 @@ void pcxhr_msg_tasklet(unsigned long arg)
prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
err = pcxhr_send_msg(mgr, prmh);
if (err)
- snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n", err);
+ snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n",
+ err);
i = 1;
while (i < prmh->stat_len) {
- int nb_audio = (prmh->stat[i] >> FIELD_SIZE) & MASK_FIRST_FIELD;
- int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD;
+ int nb_audio = ((prmh->stat[i] >> FIELD_SIZE) &
+ MASK_FIRST_FIELD);
+ int nb_stream = ((prmh->stat[i] >> (2*FIELD_SIZE)) &
+ MASK_FIRST_FIELD);
int pipe = prmh->stat[i] & MASK_FIRST_FIELD;
int is_capture = prmh->stat[i] & 0x400000;
u32 err2;
if (prmh->stat[i] & 0x800000) { /* if BIT_END */
snd_printdd("TASKLET : End%sPipe %d\n",
- is_capture ? "Record" : "Play", pipe);
+ is_capture ? "Record" : "Play",
+ pipe);
}
i++;
err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
@@ -1062,7 +1124,7 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
pcxhr_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);
pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
stream->pipe->first_audio, 0, stream_mask);
- /* rmh.stat_len = 2; */ /* 2 resp data for each stream of the pipe */
+ /* rmh.stat_len = 2; */ /* 2 resp data for each stream of the pipe */
err = pcxhr_send_msg(mgr, &rmh);
if (err)
@@ -1072,18 +1134,21 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
hw_sample_count += (u_int64_t)rmh.stat[1];
snd_printdd("stream %c%d : abs samples real(%ld) timer(%ld)\n",
- stream->pipe->is_capture ? 'C':'P', stream->substream->number,
+ stream->pipe->is_capture ? 'C' : 'P',
+ stream->substream->number,
(long unsigned int)hw_sample_count,
(long unsigned int)(stream->timer_abs_periods +
- stream->timer_period_frag + PCXHR_GRANULARITY));
-
+ stream->timer_period_frag +
+ mgr->granularity));
return hw_sample_count;
}
static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
- struct pcxhr_stream *stream, int samples_to_add)
+ struct pcxhr_stream *stream,
+ int samples_to_add)
{
- if (stream->substream && (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
+ if (stream->substream &&
+ (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
u_int64_t new_sample_count;
int elapsed = 0;
int hardware_read = 0;
@@ -1092,20 +1157,22 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
if (samples_to_add < 0) {
stream->timer_is_synced = 0;
/* add default if no hardware_read possible */
- samples_to_add = PCXHR_GRANULARITY;
+ samples_to_add = mgr->granularity;
}
if (!stream->timer_is_synced) {
- if (stream->timer_abs_periods != 0 ||
- stream->timer_period_frag + PCXHR_GRANULARITY >=
- runtime->period_size) {
- new_sample_count = pcxhr_stream_read_position(mgr, stream);
+ if ((stream->timer_abs_periods != 0) ||
+ ((stream->timer_period_frag + samples_to_add) >=
+ runtime->period_size)) {
+ new_sample_count =
+ pcxhr_stream_read_position(mgr, stream);
hardware_read = 1;
- if (new_sample_count >= PCXHR_GRANULARITY_MIN) {
- /* sub security offset because of jitter and
- * finer granularity of dsp time (MBOX4)
+ if (new_sample_count >= mgr->granularity) {
+ /* sub security offset because of
+ * jitter and finer granularity of
+ * dsp time (MBOX4)
*/
- new_sample_count -= PCXHR_GRANULARITY_MIN;
+ new_sample_count -= mgr->granularity;
stream->timer_is_synced = 1;
}
}
@@ -1128,12 +1195,15 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
stream->timer_buf_periods = 0;
stream->timer_abs_periods = new_elapse_pos;
}
- if (new_sample_count >= stream->timer_abs_periods)
- stream->timer_period_frag = (u_int32_t)(new_sample_count -
- stream->timer_abs_periods);
- else
- snd_printk(KERN_ERR "ERROR new_sample_count too small ??? %lx\n",
+ if (new_sample_count >= stream->timer_abs_periods) {
+ stream->timer_period_frag =
+ (u_int32_t)(new_sample_count -
+ stream->timer_abs_periods);
+ } else {
+ snd_printk(KERN_ERR
+ "ERROR new_sample_count too small ??? %ld\n",
(long unsigned int)new_sample_count);
+ }
if (elapsed) {
spin_unlock(&mgr->lock);
@@ -1143,7 +1213,6 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
}
}
-
irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
{
struct pcxhr_mgr *mgr = dev_id;
@@ -1156,7 +1225,8 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
spin_unlock(&mgr->lock);
- return IRQ_NONE; /* this device did not cause the interrupt */
+ /* this device did not cause the interrupt */
+ return IRQ_NONE;
}
/* clear interrupt */
@@ -1167,10 +1237,12 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
if (reg & PCXHR_IRQ_TIMER) {
int timer_toggle = reg & PCXHR_IRQ_TIMER;
/* is a 24 bit counter */
- int dsp_time_new = PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
+ int dsp_time_new =
+ PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
int dsp_time_diff = dsp_time_new - mgr->dsp_time_last;
- if (dsp_time_diff < 0 && mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID) {
+ if ((dsp_time_diff < 0) &&
+ (mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) {
snd_printdd("ERROR DSP TIME old(%d) new(%d) -> "
"resynchronize all streams\n",
mgr->dsp_time_last, dsp_time_new);
@@ -1178,42 +1250,51 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (dsp_time_diff == 0)
- snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);
- else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))
+ snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n",
+ dsp_time_new);
+ else if (dsp_time_diff >= (2*mgr->granularity))
snd_printdd("ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
- mgr->dsp_time_last, dsp_time_new - mgr->dsp_time_last);
+ mgr->dsp_time_last,
+ dsp_time_new - mgr->dsp_time_last);
+ else if (dsp_time_diff % mgr->granularity)
+ snd_printdd("ERROR DSP TIME increased by %d\n",
+ dsp_time_diff);
#endif
mgr->dsp_time_last = dsp_time_new;
- if (timer_toggle == mgr->timer_toggle)
+ if (timer_toggle == mgr->timer_toggle) {
snd_printdd("ERROR TIMER TOGGLE\n");
+ mgr->dsp_time_err++;
+ }
mgr->timer_toggle = timer_toggle;
reg &= ~PCXHR_IRQ_TIMER;
for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i];
for (j = 0; j < chip->nb_streams_capt; j++)
- pcxhr_update_timer_pos(mgr, &chip->capture_stream[j],
- dsp_time_diff);
+ pcxhr_update_timer_pos(mgr,
+ &chip->capture_stream[j],
+ dsp_time_diff);
}
for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i];
for (j = 0; j < chip->nb_streams_play; j++)
- pcxhr_update_timer_pos(mgr, &chip->playback_stream[j],
- dsp_time_diff);
+ pcxhr_update_timer_pos(mgr,
+ &chip->playback_stream[j],
+ dsp_time_diff);
}
}
/* other irq's handled in the tasklet */
if (reg & PCXHR_IRQ_MASK) {
-
- /* as we didn't request any notifications, some kind of xrun error
- * will probably occured
- */
- /* better resynchronize all streams next interrupt : */
- mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
-
+ if (reg & PCXHR_IRQ_ASYNC) {
+ /* as we didn't request any async notifications,
+ * some kind of xrun error will probably occured
+ */
+ /* better resynchronize all streams next interrupt : */
+ mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+ }
mgr->src_it_dsp = reg;
- tasklet_hi_schedule(&mgr->msg_taskq);
+ tasklet_schedule(&mgr->msg_taskq);
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if (reg & PCXHR_FATAL_DSP_ERR)
diff --git a/sound/pci/pcxhr/pcxhr_core.h b/sound/pci/pcxhr/pcxhr_core.h
index d9a4ab609875..bbbd66d13a64 100644
--- a/sound/pci/pcxhr/pcxhr_core.h
+++ b/sound/pci/pcxhr/pcxhr_core.h
@@ -65,7 +65,7 @@ enum {
CMD_RESYNC_AUDIO_INPUTS, /* cmd_len = 1 stat_len = 0 */
CMD_GET_DSP_RESOURCES, /* cmd_len = 1 stat_len = 4 */
CMD_SET_TIMER_INTERRUPT, /* cmd_len = 1 stat_len = 0 */
- CMD_RES_PIPE, /* cmd_len = 2 stat_len = 0 */
+ CMD_RES_PIPE, /* cmd_len >=2 stat_len = 0 */
CMD_FREE_PIPE, /* cmd_len = 1 stat_len = 0 */
CMD_CONF_PIPE, /* cmd_len = 2 stat_len = 0 */
CMD_STOP_PIPE, /* cmd_len = 1 stat_len = 0 */
@@ -96,6 +96,8 @@ void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd);
void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh* rmh, int capture, unsigned int param1,
unsigned int param2, unsigned int param3);
+#define DSP_EXT_CMD_SET(x) (x->dsp_version > 0x012800)
+
/*
send the rmh
*/
@@ -110,6 +112,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
#define IO_NUM_REG_STATUS 5
#define IO_NUM_REG_CUER 10
#define IO_NUM_UER_CHIP_REG 11
+#define IO_NUM_REG_CONFIG_SRC 12
#define IO_NUM_REG_OUT_ANA_LEVEL 20
#define IO_NUM_REG_IN_ANA_LEVEL 21
diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c
index 96640d9c227d..592743a298b0 100644
--- a/sound/pci/pcxhr/pcxhr_hwdep.c
+++ b/sound/pci/pcxhr/pcxhr_hwdep.c
@@ -31,6 +31,7 @@
#include "pcxhr_mixer.h"
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
@@ -40,10 +41,10 @@
#endif
+static int pcxhr_sub_init(struct pcxhr_mgr *mgr);
/*
* get basic information and init pcxhr card
*/
-
static int pcxhr_init_board(struct pcxhr_mgr *mgr)
{
int err;
@@ -68,7 +69,7 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2)
return -EINVAL;
/* test 8 or 2 phys in */
- if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) !=
+ if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) <
mgr->capture_chips * 2)
return -EINVAL;
/* test max nb substream per board */
@@ -77,20 +78,34 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
/* test max nb substream per pipe */
if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS)
return -EINVAL;
+ snd_printdd("supported formats : playback=%x capture=%x\n",
+ rmh.stat[2], rmh.stat[3]);
pcxhr_init_rmh(&rmh, CMD_VERSION);
/* firmware num for DSP */
rmh.cmd[0] |= mgr->firmware_num;
/* transfer granularity in samples (should be multiple of 48) */
- rmh.cmd[1] = (1<<23) + PCXHR_GRANULARITY;
+ rmh.cmd[1] = (1<<23) + mgr->granularity;
rmh.cmd_len = 2;
err = pcxhr_send_msg(mgr, &rmh);
if (err)
return err;
- snd_printdd("PCXHR DSP version is %d.%d.%d\n",
- (rmh.stat[0]>>16)&0xff, (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
+ snd_printdd("PCXHR DSP version is %d.%d.%d\n", (rmh.stat[0]>>16)&0xff,
+ (rmh.stat[0]>>8)&0xff, rmh.stat[0]&0xff);
mgr->dsp_version = rmh.stat[0];
+ if (mgr->is_hr_stereo)
+ err = hr222_sub_init(mgr);
+ else
+ err = pcxhr_sub_init(mgr);
+ return err;
+}
+
+static int pcxhr_sub_init(struct pcxhr_mgr *mgr)
+{
+ int err;
+ struct pcxhr_rmh rmh;
+
/* get options */
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
rmh.cmd[0] |= IO_NUM_REG_STATUS;
@@ -100,20 +115,22 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
if (err)
return err;
- if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) == REG_STATUS_OPT_ANALOG_BOARD)
- mgr->board_has_analog = 1; /* analog addon board available */
- else
- /* analog addon board not available -> no support for instance */
- return -EINVAL;
+ if ((rmh.stat[1] & REG_STATUS_OPT_DAUGHTER_MASK) ==
+ REG_STATUS_OPT_ANALOG_BOARD)
+ mgr->board_has_analog = 1; /* analog addon board found */
/* unmute inputs */
err = pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
REG_CONT_UNMUTE_INPUTS, NULL);
if (err)
return err;
- /* unmute outputs */
- pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* a write to IO_NUM_REG_MUTE_OUT mutes! */
+ /* unmute outputs (a write to IO_NUM_REG_MUTE_OUT mutes!) */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+ if (DSP_EXT_CMD_SET(mgr)) {
+ rmh.cmd[1] = 1; /* unmute digital plugs */
+ rmh.cmd_len = 2;
+ }
err = pcxhr_send_msg(mgr, &rmh);
return err;
}
@@ -124,19 +141,25 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
/* mute outputs */
+ if (!mgr->is_hr_stereo) {
/* a read to IO_NUM_REG_MUTE_OUT register unmutes! */
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
pcxhr_send_msg(mgr, &rmh);
/* mute inputs */
- pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS, 0, NULL);
+ pcxhr_write_io_num_reg_cont(mgr, REG_CONT_UNMUTE_INPUTS,
+ 0, NULL);
+ }
+ /* stereo cards mute with reset of dsp */
}
/* reset pcxhr dsp */
- if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
+ if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_EPRM_INDEX))
pcxhr_reset_dsp(mgr);
/* reset second xilinx */
- if (mgr->dsp_loaded & ( 1 << PCXHR_FIRMWARE_XLX_COM_INDEX))
+ if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_XLX_COM_INDEX)) {
pcxhr_reset_xilinx_com(mgr);
+ mgr->dsp_loaded = 1;
+ }
return;
}
@@ -144,8 +167,9 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
/*
* allocate a playback/capture pipe (pcmp0/pcmc0)
*/
-static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe,
- int is_capture, int pin)
+static int pcxhr_dsp_allocate_pipe(struct pcxhr_mgr *mgr,
+ struct pcxhr_pipe *pipe,
+ int is_capture, int pin)
{
int stream_count, audio_count;
int err;
@@ -161,15 +185,23 @@ static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pi
stream_count = PCXHR_PLAYBACK_STREAMS;
audio_count = 2; /* always stereo */
}
- snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n", pin, is_capture ? 'c' : 'p');
+ snd_printdd("snd_add_ref_pipe pin(%d) pcm%c0\n",
+ pin, is_capture ? 'c' : 'p');
pipe->is_capture = is_capture;
pipe->first_audio = pin;
/* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */
pcxhr_init_rmh(&rmh, CMD_RES_PIPE);
- pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin, audio_count, stream_count);
+ pcxhr_set_pipe_cmd_params(&rmh, is_capture, pin,
+ audio_count, stream_count);
+ rmh.cmd[1] |= 0x020000; /* add P_PCM_ONLY_MASK */
+ if (DSP_EXT_CMD_SET(mgr)) {
+ /* add channel mask to command */
+ rmh.cmd[rmh.cmd_len++] = (audio_count == 1) ? 0x01 : 0x03;
+ }
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_ERR "error pipe allocation (CMD_RES_PIPE) err=%x!\n", err );
+ snd_printk(KERN_ERR "error pipe allocation "
+ "(CMD_RES_PIPE) err=%x!\n", err);
return err;
}
pipe->status = PCXHR_PIPE_DEFINED;
@@ -199,10 +231,12 @@ static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
snd_printk(KERN_ERR "error stopping pipe!\n");
/* release the pipe */
pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
- pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio, 0, 0);
+ pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio,
+ 0, 0);
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
- snd_printk(KERN_ERR "error pipe release (CMD_FREE_PIPE) err(%x)\n", err);
+ snd_printk(KERN_ERR "error pipe release "
+ "(CMD_FREE_PIPE) err(%x)\n", err);
pipe->status = PCXHR_PIPE_UNDEFINED;
return err;
}
@@ -248,15 +282,16 @@ static int pcxhr_start_pipes(struct pcxhr_mgr *mgr)
for (i = 0; i < mgr->num_cards; i++) {
chip = mgr->chip[i];
if (chip->nb_streams_play)
- playback_mask |= (1 << chip->playback_pipe.first_audio);
+ playback_mask |= 1 << chip->playback_pipe.first_audio;
for (j = 0; j < chip->nb_streams_capt; j++)
- capture_mask |= (1 << chip->capture_pipe[j].first_audio);
+ capture_mask |= 1 << chip->capture_pipe[j].first_audio;
}
return pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
}
-static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmware *dsp)
+static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index,
+ const struct firmware *dsp)
{
int err, card_index;
@@ -330,22 +365,33 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmwar
int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
{
- static char *fw_files[5] = {
- "xi_1_882.dat",
- "xc_1_882.dat",
- "e321_512.e56",
- "b321_512.b56",
- "d321_512.d56"
+ static char *fw_files[][5] = {
+ [0] = { "xlxint.dat", "xlxc882hr.dat",
+ "dspe882.e56", "dspb882hr.b56", "dspd882.d56" },
+ [1] = { "xlxint.dat", "xlxc882e.dat",
+ "dspe882.e56", "dspb882e.b56", "dspd882.d56" },
+ [2] = { "xlxint.dat", "xlxc1222hr.dat",
+ "dspe882.e56", "dspb1222hr.b56", "dspd1222.d56" },
+ [3] = { "xlxint.dat", "xlxc1222e.dat",
+ "dspe882.e56", "dspb1222e.b56", "dspd1222.d56" },
+ [4] = { NULL, "xlxc222.dat",
+ "dspe924.e56", "dspb924.b56", "dspd222.d56" },
+ [5] = { NULL, "xlxc924.dat",
+ "dspe924.e56", "dspb924.b56", "dspd222.d56" },
};
char path[32];
const struct firmware *fw_entry;
int i, err;
+ int fw_set = mgr->fw_file_set;
- for (i = 0; i < ARRAY_SIZE(fw_files); i++) {
- sprintf(path, "pcxhr/%s", fw_files[i]);
+ for (i = 0; i < 5; i++) {
+ if (!fw_files[fw_set][i])
+ continue;
+ sprintf(path, "pcxhr/%s", fw_files[fw_set][i]);
if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
- snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n", path);
+ snd_printk(KERN_ERR "pcxhr: can't load firmware %s\n",
+ path);
return -ENOENT;
}
/* fake hwdep dsp record */
@@ -358,11 +404,26 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
return 0;
}
-MODULE_FIRMWARE("pcxhr/xi_1_882.dat");
-MODULE_FIRMWARE("pcxhr/xc_1_882.dat");
-MODULE_FIRMWARE("pcxhr/e321_512.e56");
-MODULE_FIRMWARE("pcxhr/b321_512.b56");
-MODULE_FIRMWARE("pcxhr/d321_512.d56");
+MODULE_FIRMWARE("pcxhr/xlxint.dat");
+MODULE_FIRMWARE("pcxhr/xlxc882hr.dat");
+MODULE_FIRMWARE("pcxhr/xlxc882e.dat");
+MODULE_FIRMWARE("pcxhr/dspe882.e56");
+MODULE_FIRMWARE("pcxhr/dspb882hr.b56");
+MODULE_FIRMWARE("pcxhr/dspb882e.b56");
+MODULE_FIRMWARE("pcxhr/dspd882.d56");
+
+MODULE_FIRMWARE("pcxhr/xlxc1222hr.dat");
+MODULE_FIRMWARE("pcxhr/xlxc1222e.dat");
+MODULE_FIRMWARE("pcxhr/dspb1222hr.b56");
+MODULE_FIRMWARE("pcxhr/dspb1222e.b56");
+MODULE_FIRMWARE("pcxhr/dspd1222.d56");
+
+MODULE_FIRMWARE("pcxhr/xlxc222.dat");
+MODULE_FIRMWARE("pcxhr/xlxc924.dat");
+MODULE_FIRMWARE("pcxhr/dspe924.e56");
+MODULE_FIRMWARE("pcxhr/dspb924.b56");
+MODULE_FIRMWARE("pcxhr/dspd222.d56");
+
#else /* old style firmware loading */
@@ -373,7 +434,8 @@ MODULE_FIRMWARE("pcxhr/d321_512.d56");
static int pcxhr_hwdep_dsp_status(struct snd_hwdep *hw,
struct snd_hwdep_dsp_status *info)
{
- strcpy(info->id, "pcxhr");
+ struct pcxhr_mgr *mgr = hw->private_data;
+ sprintf(info->id, "pcxhr%d", mgr->fw_file_set);
info->num_dsps = PCXHR_FIRMWARE_FILES_MAX_INDEX;
if (hw->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX))
@@ -393,8 +455,8 @@ static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
fw.size = dsp->length;
fw.data = vmalloc(fw.size);
if (! fw.data) {
- snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image (%lu bytes)\n",
- (unsigned long)fw.size);
+ snd_printk(KERN_ERR "pcxhr: cannot allocate dsp image "
+ "(%lu bytes)\n", (unsigned long)fw.size);
return -ENOMEM;
}
if (copy_from_user((void *)fw.data, dsp->image, dsp->length)) {
@@ -424,8 +486,11 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
int err;
struct snd_hwdep *hw;
- /* only create hwdep interface for first cardX (see "index" module parameter)*/
- if ((err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw)) < 0)
+ /* only create hwdep interface for first cardX
+ * (see "index" module parameter)
+ */
+ err = snd_hwdep_new(mgr->chip[0]->card, PCXHR_HWDEP_ID, 0, &hw);
+ if (err < 0)
return err;
hw->iface = SNDRV_HWDEP_IFACE_PCXHR;
@@ -435,10 +500,13 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
hw->ops.dsp_status = pcxhr_hwdep_dsp_status;
hw->ops.dsp_load = pcxhr_hwdep_dsp_load;
hw->exclusive = 1;
+ /* stereo cards don't need fw_file_0 -> dsp_loaded = 1 */
+ hw->dsp_loaded = mgr->is_hr_stereo ? 1 : 0;
mgr->dsp_loaded = 0;
sprintf(hw->name, PCXHR_HWDEP_ID);
- if ((err = snd_card_register(mgr->chip[0]->card)) < 0)
+ err = snd_card_register(mgr->chip[0]->card);
+ if (err < 0)
return err;
return 0;
}
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
new file mode 100644
index 000000000000..ff019126b672
--- /dev/null
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -0,0 +1,820 @@
+/*
+ * Driver for Digigram pcxhr compatible soundcards
+ *
+ * mixer interface for stereo cards
+ *
+ * Copyright (c) 2004 by Digigram <alsa@digigram.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/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/asoundef.h>
+#include "pcxhr.h"
+#include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
+
+
+/* registers used on the DSP and Xilinx (port 2) : HR stereo cards only */
+#define PCXHR_DSP_RESET 0x20
+#define PCXHR_XLX_CFG 0x24
+#define PCXHR_XLX_RUER 0x28
+#define PCXHR_XLX_DATA 0x2C
+#define PCXHR_XLX_STATUS 0x30
+#define PCXHR_XLX_LOFREQ 0x34
+#define PCXHR_XLX_HIFREQ 0x38
+#define PCXHR_XLX_CSUER 0x3C
+#define PCXHR_XLX_SELMIC 0x40
+
+#define PCXHR_DSP 2
+
+/* byte access only ! */
+#define PCXHR_INPB(mgr, x) inb((mgr)->port[PCXHR_DSP] + (x))
+#define PCXHR_OUTPB(mgr, x, data) outb((data), (mgr)->port[PCXHR_DSP] + (x))
+
+
+/* values for PCHR_DSP_RESET register */
+#define PCXHR_DSP_RESET_DSP 0x01
+#define PCXHR_DSP_RESET_MUTE 0x02
+#define PCXHR_DSP_RESET_CODEC 0x08
+
+/* values for PCHR_XLX_CFG register */
+#define PCXHR_CFG_SYNCDSP_MASK 0x80
+#define PCXHR_CFG_DEPENDENCY_MASK 0x60
+#define PCXHR_CFG_INDEPENDANT_SEL 0x00
+#define PCXHR_CFG_MASTER_SEL 0x40
+#define PCXHR_CFG_SLAVE_SEL 0x20
+#define PCXHR_CFG_DATA_UER1_SEL_MASK 0x10 /* 0 (UER0), 1(UER1) */
+#define PCXHR_CFG_DATAIN_SEL_MASK 0x08 /* 0 (ana), 1 (UER) */
+#define PCXHR_CFG_SRC_MASK 0x04 /* 0 (Bypass), 1 (SRC Actif) */
+#define PCXHR_CFG_CLOCK_UER1_SEL_MASK 0x02 /* 0 (UER0), 1(UER1) */
+#define PCXHR_CFG_CLOCKIN_SEL_MASK 0x01 /* 0 (internal), 1 (AES/EBU) */
+
+/* values for PCHR_XLX_DATA register */
+#define PCXHR_DATA_CODEC 0x80
+#define AKM_POWER_CONTROL_CMD 0xA007
+#define AKM_RESET_ON_CMD 0xA100
+#define AKM_RESET_OFF_CMD 0xA103
+#define AKM_CLOCK_INF_55K_CMD 0xA240
+#define AKM_CLOCK_SUP_55K_CMD 0xA24D
+#define AKM_MUTE_CMD 0xA38D
+#define AKM_UNMUTE_CMD 0xA30D
+#define AKM_LEFT_LEVEL_CMD 0xA600
+#define AKM_RIGHT_LEVEL_CMD 0xA700
+
+/* values for PCHR_XLX_STATUS register - READ */
+#define PCXHR_STAT_SRC_LOCK 0x01
+#define PCXHR_STAT_LEVEL_IN 0x02
+#define PCXHR_STAT_MIC_CAPS 0x10
+/* values for PCHR_XLX_STATUS register - WRITE */
+#define PCXHR_STAT_FREQ_SYNC_MASK 0x01
+#define PCXHR_STAT_FREQ_UER1_MASK 0x02
+#define PCXHR_STAT_FREQ_SAVE_MASK 0x80
+
+/* values for PCHR_XLX_CSUER register */
+#define PCXHR_SUER1_BIT_U_READ_MASK 0x80
+#define PCXHR_SUER1_BIT_C_READ_MASK 0x40
+#define PCXHR_SUER1_DATA_PRESENT_MASK 0x20
+#define PCXHR_SUER1_CLOCK_PRESENT_MASK 0x10
+#define PCXHR_SUER_BIT_U_READ_MASK 0x08
+#define PCXHR_SUER_BIT_C_READ_MASK 0x04
+#define PCXHR_SUER_DATA_PRESENT_MASK 0x02
+#define PCXHR_SUER_CLOCK_PRESENT_MASK 0x01
+
+#define PCXHR_SUER_BIT_U_WRITE_MASK 0x02
+#define PCXHR_SUER_BIT_C_WRITE_MASK 0x01
+
+/* values for PCXHR_XLX_SELMIC register - WRITE */
+#define PCXHR_SELMIC_PREAMPLI_OFFSET 2
+#define PCXHR_SELMIC_PREAMPLI_MASK 0x0C
+#define PCXHR_SELMIC_PHANTOM_ALIM 0x80
+
+
+static const unsigned char g_hr222_p_level[] = {
+ 0x00, /* [000] -49.5 dB: AKM[000] = -1.#INF dB (mute) */
+ 0x01, /* [001] -49.0 dB: AKM[001] = -48.131 dB (diff=0.86920 dB) */
+ 0x01, /* [002] -48.5 dB: AKM[001] = -48.131 dB (diff=0.36920 dB) */
+ 0x01, /* [003] -48.0 dB: AKM[001] = -48.131 dB (diff=0.13080 dB) */
+ 0x01, /* [004] -47.5 dB: AKM[001] = -48.131 dB (diff=0.63080 dB) */
+ 0x01, /* [005] -46.5 dB: AKM[001] = -48.131 dB (diff=1.63080 dB) */
+ 0x01, /* [006] -47.0 dB: AKM[001] = -48.131 dB (diff=1.13080 dB) */
+ 0x01, /* [007] -46.0 dB: AKM[001] = -48.131 dB (diff=2.13080 dB) */
+ 0x01, /* [008] -45.5 dB: AKM[001] = -48.131 dB (diff=2.63080 dB) */
+ 0x02, /* [009] -45.0 dB: AKM[002] = -42.110 dB (diff=2.88980 dB) */
+ 0x02, /* [010] -44.5 dB: AKM[002] = -42.110 dB (diff=2.38980 dB) */
+ 0x02, /* [011] -44.0 dB: AKM[002] = -42.110 dB (diff=1.88980 dB) */
+ 0x02, /* [012] -43.5 dB: AKM[002] = -42.110 dB (diff=1.38980 dB) */
+ 0x02, /* [013] -43.0 dB: AKM[002] = -42.110 dB (diff=0.88980 dB) */
+ 0x02, /* [014] -42.5 dB: AKM[002] = -42.110 dB (diff=0.38980 dB) */
+ 0x02, /* [015] -42.0 dB: AKM[002] = -42.110 dB (diff=0.11020 dB) */
+ 0x02, /* [016] -41.5 dB: AKM[002] = -42.110 dB (diff=0.61020 dB) */
+ 0x02, /* [017] -41.0 dB: AKM[002] = -42.110 dB (diff=1.11020 dB) */
+ 0x02, /* [018] -40.5 dB: AKM[002] = -42.110 dB (diff=1.61020 dB) */
+ 0x03, /* [019] -40.0 dB: AKM[003] = -38.588 dB (diff=1.41162 dB) */
+ 0x03, /* [020] -39.5 dB: AKM[003] = -38.588 dB (diff=0.91162 dB) */
+ 0x03, /* [021] -39.0 dB: AKM[003] = -38.588 dB (diff=0.41162 dB) */
+ 0x03, /* [022] -38.5 dB: AKM[003] = -38.588 dB (diff=0.08838 dB) */
+ 0x03, /* [023] -38.0 dB: AKM[003] = -38.588 dB (diff=0.58838 dB) */
+ 0x03, /* [024] -37.5 dB: AKM[003] = -38.588 dB (diff=1.08838 dB) */
+ 0x04, /* [025] -37.0 dB: AKM[004] = -36.090 dB (diff=0.91040 dB) */
+ 0x04, /* [026] -36.5 dB: AKM[004] = -36.090 dB (diff=0.41040 dB) */
+ 0x04, /* [027] -36.0 dB: AKM[004] = -36.090 dB (diff=0.08960 dB) */
+ 0x04, /* [028] -35.5 dB: AKM[004] = -36.090 dB (diff=0.58960 dB) */
+ 0x05, /* [029] -35.0 dB: AKM[005] = -34.151 dB (diff=0.84860 dB) */
+ 0x05, /* [030] -34.5 dB: AKM[005] = -34.151 dB (diff=0.34860 dB) */
+ 0x05, /* [031] -34.0 dB: AKM[005] = -34.151 dB (diff=0.15140 dB) */
+ 0x05, /* [032] -33.5 dB: AKM[005] = -34.151 dB (diff=0.65140 dB) */
+ 0x06, /* [033] -33.0 dB: AKM[006] = -32.568 dB (diff=0.43222 dB) */
+ 0x06, /* [034] -32.5 dB: AKM[006] = -32.568 dB (diff=0.06778 dB) */
+ 0x06, /* [035] -32.0 dB: AKM[006] = -32.568 dB (diff=0.56778 dB) */
+ 0x07, /* [036] -31.5 dB: AKM[007] = -31.229 dB (diff=0.27116 dB) */
+ 0x07, /* [037] -31.0 dB: AKM[007] = -31.229 dB (diff=0.22884 dB) */
+ 0x08, /* [038] -30.5 dB: AKM[008] = -30.069 dB (diff=0.43100 dB) */
+ 0x08, /* [039] -30.0 dB: AKM[008] = -30.069 dB (diff=0.06900 dB) */
+ 0x09, /* [040] -29.5 dB: AKM[009] = -29.046 dB (diff=0.45405 dB) */
+ 0x09, /* [041] -29.0 dB: AKM[009] = -29.046 dB (diff=0.04595 dB) */
+ 0x0a, /* [042] -28.5 dB: AKM[010] = -28.131 dB (diff=0.36920 dB) */
+ 0x0a, /* [043] -28.0 dB: AKM[010] = -28.131 dB (diff=0.13080 dB) */
+ 0x0b, /* [044] -27.5 dB: AKM[011] = -27.303 dB (diff=0.19705 dB) */
+ 0x0b, /* [045] -27.0 dB: AKM[011] = -27.303 dB (diff=0.30295 dB) */
+ 0x0c, /* [046] -26.5 dB: AKM[012] = -26.547 dB (diff=0.04718 dB) */
+ 0x0d, /* [047] -26.0 dB: AKM[013] = -25.852 dB (diff=0.14806 dB) */
+ 0x0e, /* [048] -25.5 dB: AKM[014] = -25.208 dB (diff=0.29176 dB) */
+ 0x0e, /* [049] -25.0 dB: AKM[014] = -25.208 dB (diff=0.20824 dB) */
+ 0x0f, /* [050] -24.5 dB: AKM[015] = -24.609 dB (diff=0.10898 dB) */
+ 0x10, /* [051] -24.0 dB: AKM[016] = -24.048 dB (diff=0.04840 dB) */
+ 0x11, /* [052] -23.5 dB: AKM[017] = -23.522 dB (diff=0.02183 dB) */
+ 0x12, /* [053] -23.0 dB: AKM[018] = -23.025 dB (diff=0.02535 dB) */
+ 0x13, /* [054] -22.5 dB: AKM[019] = -22.556 dB (diff=0.05573 dB) */
+ 0x14, /* [055] -22.0 dB: AKM[020] = -22.110 dB (diff=0.11020 dB) */
+ 0x15, /* [056] -21.5 dB: AKM[021] = -21.686 dB (diff=0.18642 dB) */
+ 0x17, /* [057] -21.0 dB: AKM[023] = -20.896 dB (diff=0.10375 dB) */
+ 0x18, /* [058] -20.5 dB: AKM[024] = -20.527 dB (diff=0.02658 dB) */
+ 0x1a, /* [059] -20.0 dB: AKM[026] = -19.831 dB (diff=0.16866 dB) */
+ 0x1b, /* [060] -19.5 dB: AKM[027] = -19.504 dB (diff=0.00353 dB) */
+ 0x1d, /* [061] -19.0 dB: AKM[029] = -18.883 dB (diff=0.11716 dB) */
+ 0x1e, /* [062] -18.5 dB: AKM[030] = -18.588 dB (diff=0.08838 dB) */
+ 0x20, /* [063] -18.0 dB: AKM[032] = -18.028 dB (diff=0.02780 dB) */
+ 0x22, /* [064] -17.5 dB: AKM[034] = -17.501 dB (diff=0.00123 dB) */
+ 0x24, /* [065] -17.0 dB: AKM[036] = -17.005 dB (diff=0.00475 dB) */
+ 0x26, /* [066] -16.5 dB: AKM[038] = -16.535 dB (diff=0.03513 dB) */
+ 0x28, /* [067] -16.0 dB: AKM[040] = -16.090 dB (diff=0.08960 dB) */
+ 0x2b, /* [068] -15.5 dB: AKM[043] = -15.461 dB (diff=0.03857 dB) */
+ 0x2d, /* [069] -15.0 dB: AKM[045] = -15.067 dB (diff=0.06655 dB) */
+ 0x30, /* [070] -14.5 dB: AKM[048] = -14.506 dB (diff=0.00598 dB) */
+ 0x33, /* [071] -14.0 dB: AKM[051] = -13.979 dB (diff=0.02060 dB) */
+ 0x36, /* [072] -13.5 dB: AKM[054] = -13.483 dB (diff=0.01707 dB) */
+ 0x39, /* [073] -13.0 dB: AKM[057] = -13.013 dB (diff=0.01331 dB) */
+ 0x3c, /* [074] -12.5 dB: AKM[060] = -12.568 dB (diff=0.06778 dB) */
+ 0x40, /* [075] -12.0 dB: AKM[064] = -12.007 dB (diff=0.00720 dB) */
+ 0x44, /* [076] -11.5 dB: AKM[068] = -11.481 dB (diff=0.01937 dB) */
+ 0x48, /* [077] -11.0 dB: AKM[072] = -10.984 dB (diff=0.01585 dB) */
+ 0x4c, /* [078] -10.5 dB: AKM[076] = -10.515 dB (diff=0.01453 dB) */
+ 0x51, /* [079] -10.0 dB: AKM[081] = -9.961 dB (diff=0.03890 dB) */
+ 0x55, /* [080] -9.5 dB: AKM[085] = -9.542 dB (diff=0.04243 dB) */
+ 0x5a, /* [081] -9.0 dB: AKM[090] = -9.046 dB (diff=0.04595 dB) */
+ 0x60, /* [082] -8.5 dB: AKM[096] = -8.485 dB (diff=0.01462 dB) */
+ 0x66, /* [083] -8.0 dB: AKM[102] = -7.959 dB (diff=0.04120 dB) */
+ 0x6c, /* [084] -7.5 dB: AKM[108] = -7.462 dB (diff=0.03767 dB) */
+ 0x72, /* [085] -7.0 dB: AKM[114] = -6.993 dB (diff=0.00729 dB) */
+ 0x79, /* [086] -6.5 dB: AKM[121] = -6.475 dB (diff=0.02490 dB) */
+ 0x80, /* [087] -6.0 dB: AKM[128] = -5.987 dB (diff=0.01340 dB) */
+ 0x87, /* [088] -5.5 dB: AKM[135] = -5.524 dB (diff=0.02413 dB) */
+ 0x8f, /* [089] -5.0 dB: AKM[143] = -5.024 dB (diff=0.02408 dB) */
+ 0x98, /* [090] -4.5 dB: AKM[152] = -4.494 dB (diff=0.00607 dB) */
+ 0xa1, /* [091] -4.0 dB: AKM[161] = -3.994 dB (diff=0.00571 dB) */
+ 0xaa, /* [092] -3.5 dB: AKM[170] = -3.522 dB (diff=0.02183 dB) */
+ 0xb5, /* [093] -3.0 dB: AKM[181] = -2.977 dB (diff=0.02277 dB) */
+ 0xbf, /* [094] -2.5 dB: AKM[191] = -2.510 dB (diff=0.01014 dB) */
+ 0xcb, /* [095] -2.0 dB: AKM[203] = -1.981 dB (diff=0.01912 dB) */
+ 0xd7, /* [096] -1.5 dB: AKM[215] = -1.482 dB (diff=0.01797 dB) */
+ 0xe3, /* [097] -1.0 dB: AKM[227] = -1.010 dB (diff=0.01029 dB) */
+ 0xf1, /* [098] -0.5 dB: AKM[241] = -0.490 dB (diff=0.00954 dB) */
+ 0xff, /* [099] +0.0 dB: AKM[255] = +0.000 dB (diff=0.00000 dB) */
+};
+
+
+static void hr222_config_akm(struct pcxhr_mgr *mgr, unsigned short data)
+{
+ unsigned short mask = 0x8000;
+ /* activate access to codec registers */
+ PCXHR_INPB(mgr, PCXHR_XLX_HIFREQ);
+
+ while (mask) {
+ PCXHR_OUTPB(mgr, PCXHR_XLX_DATA,
+ data & mask ? PCXHR_DATA_CODEC : 0);
+ mask >>= 1;
+ }
+ /* termiate access to codec registers */
+ PCXHR_INPB(mgr, PCXHR_XLX_RUER);
+}
+
+
+static int hr222_set_hw_playback_level(struct pcxhr_mgr *mgr,
+ int idx, int level)
+{
+ unsigned short cmd;
+ if (idx > 1 ||
+ level < 0 ||
+ level >= ARRAY_SIZE(g_hr222_p_level))
+ return -EINVAL;
+
+ if (idx == 0)
+ cmd = AKM_LEFT_LEVEL_CMD;
+ else
+ cmd = AKM_RIGHT_LEVEL_CMD;
+
+ /* conversion from PmBoardCodedLevel to AKM nonlinear programming */
+ cmd += g_hr222_p_level[level];
+
+ hr222_config_akm(mgr, cmd);
+ return 0;
+}
+
+
+static int hr222_set_hw_capture_level(struct pcxhr_mgr *mgr,
+ int level_l, int level_r, int level_mic)
+{
+ /* program all input levels at the same time */
+ unsigned int data;
+ int i;
+
+ if (!mgr->capture_chips)
+ return -EINVAL; /* no PCX22 */
+
+ data = ((level_mic & 0xff) << 24); /* micro is mono, but apply */
+ data |= ((level_mic & 0xff) << 16); /* level on both channels */
+ data |= ((level_r & 0xff) << 8); /* line input right channel */
+ data |= (level_l & 0xff); /* line input left channel */
+
+ PCXHR_INPB(mgr, PCXHR_XLX_DATA); /* activate input codec */
+ /* send 32 bits (4 x 8 bits) */
+ for (i = 0; i < 32; i++, data <<= 1) {
+ PCXHR_OUTPB(mgr, PCXHR_XLX_DATA,
+ (data & 0x80000000) ? PCXHR_DATA_CODEC : 0);
+ }
+ PCXHR_INPB(mgr, PCXHR_XLX_RUER); /* close input level codec */
+ return 0;
+}
+
+static void hr222_micro_boost(struct pcxhr_mgr *mgr, int level);
+
+int hr222_sub_init(struct pcxhr_mgr *mgr)
+{
+ unsigned char reg;
+
+ mgr->board_has_analog = 1; /* analog always available */
+ mgr->xlx_cfg = PCXHR_CFG_SYNCDSP_MASK;
+
+ reg = PCXHR_INPB(mgr, PCXHR_XLX_STATUS);
+ if (reg & PCXHR_STAT_MIC_CAPS)
+ mgr->board_has_mic = 1; /* microphone available */
+ snd_printdd("MIC input available = %d\n", mgr->board_has_mic);
+
+ /* reset codec */
+ PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
+ PCXHR_DSP_RESET_DSP);
+ msleep(5);
+ PCXHR_OUTPB(mgr, PCXHR_DSP_RESET,
+ PCXHR_DSP_RESET_DSP |
+ PCXHR_DSP_RESET_MUTE |
+ PCXHR_DSP_RESET_CODEC);
+ msleep(5);
+
+ /* config AKM */
+ hr222_config_akm(mgr, AKM_POWER_CONTROL_CMD);
+ hr222_config_akm(mgr, AKM_CLOCK_INF_55K_CMD);
+ hr222_config_akm(mgr, AKM_UNMUTE_CMD);
+ hr222_config_akm(mgr, AKM_RESET_OFF_CMD);
+
+ /* init micro boost */
+ hr222_micro_boost(mgr, 0);
+
+ return 0;
+}
+
+
+/* calc PLL register */
+/* TODO : there is a very similar fct in pcxhr.c */
+static int hr222_pll_freq_register(unsigned int freq,
+ unsigned int *pllreg,
+ unsigned int *realfreq)
+{
+ unsigned int reg;
+
+ if (freq < 6900 || freq > 219000)
+ return -EINVAL;
+ reg = (28224000 * 2) / freq;
+ reg = (reg - 1) / 2;
+ if (reg < 0x100)
+ *pllreg = reg + 0xC00;
+ else if (reg < 0x200)
+ *pllreg = reg + 0x800;
+ else if (reg < 0x400)
+ *pllreg = reg & 0x1ff;
+ else if (reg < 0x800) {
+ *pllreg = ((reg >> 1) & 0x1ff) + 0x200;
+ reg &= ~1;
+ } else {
+ *pllreg = ((reg >> 2) & 0x1ff) + 0x400;
+ reg &= ~3;
+ }
+ if (realfreq)
+ *realfreq = (28224000 / (reg + 1));
+ return 0;
+}
+
+int hr222_sub_set_clock(struct pcxhr_mgr *mgr,
+ unsigned int rate,
+ int *changed)
+{
+ unsigned int speed, pllreg = 0;
+ int err;
+ unsigned realfreq = rate;
+
+ switch (mgr->use_clock_type) {
+ case HR22_CLOCK_TYPE_INTERNAL:
+ err = hr222_pll_freq_register(rate, &pllreg, &realfreq);
+ if (err)
+ return err;
+
+ mgr->xlx_cfg &= ~(PCXHR_CFG_CLOCKIN_SEL_MASK |
+ PCXHR_CFG_CLOCK_UER1_SEL_MASK);
+ break;
+ case HR22_CLOCK_TYPE_AES_SYNC:
+ mgr->xlx_cfg |= PCXHR_CFG_CLOCKIN_SEL_MASK;
+ mgr->xlx_cfg &= ~PCXHR_CFG_CLOCK_UER1_SEL_MASK;
+ break;
+ case HR22_CLOCK_TYPE_AES_1:
+ if (!mgr->board_has_aes1)
+ return -EINVAL;
+
+ mgr->xlx_cfg |= (PCXHR_CFG_CLOCKIN_SEL_MASK |
+ PCXHR_CFG_CLOCK_UER1_SEL_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ hr222_config_akm(mgr, AKM_MUTE_CMD);
+
+ if (mgr->use_clock_type == HR22_CLOCK_TYPE_INTERNAL) {
+ PCXHR_OUTPB(mgr, PCXHR_XLX_HIFREQ, pllreg >> 8);
+ PCXHR_OUTPB(mgr, PCXHR_XLX_LOFREQ, pllreg & 0xff);
+ }
+
+ /* set clock source */
+ PCXHR_OUTPB(mgr, PCXHR_XLX_CFG, mgr->xlx_cfg);
+
+ /* codec speed modes */
+ speed = rate < 55000 ? 0 : 1;
+ if (mgr->codec_speed != speed) {
+ mgr->codec_speed = speed;
+ if (speed == 0)
+ hr222_config_akm(mgr, AKM_CLOCK_INF_55K_CMD);
+ else
+ hr222_config_akm(mgr, AKM_CLOCK_SUP_55K_CMD);
+ }
+
+ mgr->sample_rate_real = realfreq;
+ mgr->cur_clock_type = mgr->use_clock_type;
+
+ if (changed)
+ *changed = 1;
+
+ hr222_config_akm(mgr, AKM_UNMUTE_CMD);
+
+ snd_printdd("set_clock to %dHz (realfreq=%d pllreg=%x)\n",
+ rate, realfreq, pllreg);
+ return 0;
+}
+
+int hr222_get_external_clock(struct pcxhr_mgr *mgr,
+ enum pcxhr_clock_type clock_type,
+ int *sample_rate)
+{
+ int rate, calc_rate = 0;
+ unsigned int ticks;
+ unsigned char mask, reg;
+
+ if (clock_type == HR22_CLOCK_TYPE_AES_SYNC) {
+
+ mask = (PCXHR_SUER_CLOCK_PRESENT_MASK |
+ PCXHR_SUER_DATA_PRESENT_MASK);
+ reg = PCXHR_STAT_FREQ_SYNC_MASK;
+
+ } else if (clock_type == HR22_CLOCK_TYPE_AES_1 && mgr->board_has_aes1) {
+
+ mask = (PCXHR_SUER1_CLOCK_PRESENT_MASK |
+ PCXHR_SUER1_DATA_PRESENT_MASK);
+ reg = PCXHR_STAT_FREQ_UER1_MASK;
+
+ } else {
+ snd_printdd("get_external_clock : type %d not supported\n",
+ clock_type);
+ return -EINVAL; /* other clocks not supported */
+ }
+
+ if ((PCXHR_INPB(mgr, PCXHR_XLX_CSUER) & mask) != mask) {
+ snd_printdd("get_external_clock(%d) = 0 Hz\n", clock_type);
+ *sample_rate = 0;
+ return 0; /* no external clock locked */
+ }
+
+ PCXHR_OUTPB(mgr, PCXHR_XLX_STATUS, reg); /* calculate freq */
+
+ /* save the measured clock frequency */
+ reg |= PCXHR_STAT_FREQ_SAVE_MASK;
+
+ if (mgr->last_reg_stat != reg) {
+ udelay(500); /* wait min 2 cycles of lowest freq (8000) */
+ mgr->last_reg_stat = reg;
+ }
+
+ PCXHR_OUTPB(mgr, PCXHR_XLX_STATUS, reg); /* save */
+
+ /* get the frequency */
+ ticks = (unsigned int)PCXHR_INPB(mgr, PCXHR_XLX_CFG);
+ ticks = (ticks & 0x03) << 8;
+ ticks |= (unsigned int)PCXHR_INPB(mgr, PCXHR_DSP_RESET);
+
+ if (ticks != 0)
+ calc_rate = 28224000 / ticks;
+ /* rounding */
+ if (calc_rate > 184200)
+ rate = 192000;
+ else if (calc_rate > 152200)
+ rate = 176400;
+ else if (calc_rate > 112000)
+ rate = 128000;
+ else if (calc_rate > 92100)
+ rate = 96000;
+ else if (calc_rate > 76100)
+ rate = 88200;
+ else if (calc_rate > 56000)
+ rate = 64000;
+ else if (calc_rate > 46050)
+ rate = 48000;
+ else if (calc_rate > 38050)
+ rate = 44100;
+ else if (calc_rate > 28000)
+ rate = 32000;
+ else if (calc_rate > 23025)
+ rate = 24000;
+ else if (calc_rate > 19025)
+ rate = 22050;
+ else if (calc_rate > 14000)
+ rate = 16000;
+ else if (calc_rate > 11512)
+ rate = 12000;
+ else if (calc_rate > 9512)
+ rate = 11025;
+ else if (calc_rate > 7000)
+ rate = 8000;
+ else
+ rate = 0;
+
+ snd_printdd("External clock is at %d Hz (measured %d Hz)\n",
+ rate, calc_rate);
+ *sample_rate = rate;
+ return 0;
+}
+
+
+int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
+ int is_capture, int channel)
+{
+ snd_printdd("hr222_update_analog_audio_level(%s chan=%d)\n",
+ is_capture ? "capture" : "playback", channel);
+ if (is_capture) {
+ int level_l, level_r, level_mic;
+ /* we have to update all levels */
+ if (chip->analog_capture_active) {
+ level_l = chip->analog_capture_volume[0];
+ level_r = chip->analog_capture_volume[1];
+ } else {
+ level_l = HR222_LINE_CAPTURE_LEVEL_MIN;
+ level_r = HR222_LINE_CAPTURE_LEVEL_MIN;
+ }
+ if (chip->mic_active)
+ level_mic = chip->mic_volume;
+ else
+ level_mic = HR222_MICRO_CAPTURE_LEVEL_MIN;
+ return hr222_set_hw_capture_level(chip->mgr,
+ level_l, level_r, level_mic);
+ } else {
+ int vol;
+ if (chip->analog_playback_active[channel])
+ vol = chip->analog_playback_volume[channel];
+ else
+ vol = HR222_LINE_PLAYBACK_LEVEL_MIN;
+ return hr222_set_hw_playback_level(chip->mgr, channel, vol);
+ }
+}
+
+
+/*texts[5] = {"Line", "Digital", "Digi+SRC", "Mic", "Line+Mic"}*/
+#define SOURCE_LINE 0
+#define SOURCE_DIGITAL 1
+#define SOURCE_DIGISRC 2
+#define SOURCE_MIC 3
+#define SOURCE_LINEMIC 4
+
+int hr222_set_audio_source(struct snd_pcxhr *chip)
+{
+ int digital = 0;
+ /* default analog source */
+ chip->mgr->xlx_cfg &= ~(PCXHR_CFG_SRC_MASK |
+ PCXHR_CFG_DATAIN_SEL_MASK |
+ PCXHR_CFG_DATA_UER1_SEL_MASK);
+
+ if (chip->audio_capture_source == SOURCE_DIGISRC) {
+ chip->mgr->xlx_cfg |= PCXHR_CFG_SRC_MASK;
+ digital = 1;
+ } else {
+ if (chip->audio_capture_source == SOURCE_DIGITAL)
+ digital = 1;
+ }
+ if (digital) {
+ chip->mgr->xlx_cfg |= PCXHR_CFG_DATAIN_SEL_MASK;
+ if (chip->mgr->board_has_aes1) {
+ /* get data from the AES1 plug */
+ chip->mgr->xlx_cfg |= PCXHR_CFG_DATA_UER1_SEL_MASK;
+ }
+ /* chip->mic_active = 0; */
+ /* chip->analog_capture_active = 0; */
+ } else {
+ int update_lvl = 0;
+ chip->analog_capture_active = 0;
+ chip->mic_active = 0;
+ if (chip->audio_capture_source == SOURCE_LINE ||
+ chip->audio_capture_source == SOURCE_LINEMIC) {
+ if (chip->analog_capture_active == 0)
+ update_lvl = 1;
+ chip->analog_capture_active = 1;
+ }
+ if (chip->audio_capture_source == SOURCE_MIC ||
+ chip->audio_capture_source == SOURCE_LINEMIC) {
+ if (chip->mic_active == 0)
+ update_lvl = 1;
+ chip->mic_active = 1;
+ }
+ if (update_lvl) {
+ /* capture: update all 3 mutes/unmutes with one call */
+ hr222_update_analog_audio_level(chip, 1, 0);
+ }
+ }
+ /* set the source infos (max 3 bits modified) */
+ PCXHR_OUTPB(chip->mgr, PCXHR_XLX_CFG, chip->mgr->xlx_cfg);
+ return 0;
+}
+
+
+int hr222_iec958_capture_byte(struct snd_pcxhr *chip,
+ int aes_idx, unsigned char *aes_bits)
+{
+ unsigned char idx = (unsigned char)(aes_idx * 8);
+ unsigned char temp = 0;
+ unsigned char mask = chip->mgr->board_has_aes1 ?
+ PCXHR_SUER1_BIT_C_READ_MASK : PCXHR_SUER_BIT_C_READ_MASK;
+ int i;
+ for (i = 0; i < 8; i++) {
+ PCXHR_OUTPB(chip->mgr, PCXHR_XLX_RUER, idx++); /* idx < 192 */
+ temp <<= 1;
+ if (PCXHR_INPB(chip->mgr, PCXHR_XLX_CSUER) & mask)
+ temp |= 1;
+ }
+ snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+ chip->chip_idx, aes_idx, temp);
+ *aes_bits = temp;
+ return 0;
+}
+
+
+int hr222_iec958_update_byte(struct snd_pcxhr *chip,
+ int aes_idx, unsigned char aes_bits)
+{
+ int i;
+ unsigned char new_bits = aes_bits;
+ unsigned char old_bits = chip->aes_bits[aes_idx];
+ unsigned char idx = (unsigned char)(aes_idx * 8);
+ for (i = 0; i < 8; i++) {
+ if ((old_bits & 0x01) != (new_bits & 0x01)) {
+ /* idx < 192 */
+ PCXHR_OUTPB(chip->mgr, PCXHR_XLX_RUER, idx);
+ /* write C and U bit */
+ PCXHR_OUTPB(chip->mgr, PCXHR_XLX_CSUER, new_bits&0x01 ?
+ PCXHR_SUER_BIT_C_WRITE_MASK : 0);
+ }
+ idx++;
+ old_bits >>= 1;
+ new_bits >>= 1;
+ }
+ chip->aes_bits[aes_idx] = aes_bits;
+ return 0;
+}
+
+static void hr222_micro_boost(struct pcxhr_mgr *mgr, int level)
+{
+ unsigned char boost_mask;
+ boost_mask = (unsigned char) (level << PCXHR_SELMIC_PREAMPLI_OFFSET);
+ if (boost_mask & (~PCXHR_SELMIC_PREAMPLI_MASK))
+ return; /* only values form 0 to 3 accepted */
+
+ mgr->xlx_selmic &= ~PCXHR_SELMIC_PREAMPLI_MASK;
+ mgr->xlx_selmic |= boost_mask;
+
+ PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
+
+ snd_printdd("hr222_micro_boost : set %x\n", boost_mask);
+}
+
+static void hr222_phantom_power(struct pcxhr_mgr *mgr, int power)
+{
+ if (power)
+ mgr->xlx_selmic |= PCXHR_SELMIC_PHANTOM_ALIM;
+ else
+ mgr->xlx_selmic &= ~PCXHR_SELMIC_PHANTOM_ALIM;
+
+ PCXHR_OUTPB(mgr, PCXHR_XLX_SELMIC, mgr->xlx_selmic);
+
+ snd_printdd("hr222_phantom_power : set %d\n", power);
+}
+
+
+/* mic level */
+static const DECLARE_TLV_DB_SCALE(db_scale_mic_hr222, -9850, 50, 650);
+
+static int hr222_mic_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = HR222_MICRO_CAPTURE_LEVEL_MIN; /* -98 dB */
+ /* gains from 9 dB to 31.5 dB not recommended; use micboost instead */
+ uinfo->value.integer.max = HR222_MICRO_CAPTURE_LEVEL_MAX; /* +7 dB */
+ return 0;
+}
+
+static int hr222_mic_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->mic_volume;
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if (chip->mic_volume != ucontrol->value.integer.value[0]) {
+ changed = 1;
+ chip->mic_volume = ucontrol->value.integer.value[0];
+ hr222_update_analog_audio_level(chip, 1, 0);
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static struct snd_kcontrol_new hr222_control_mic_level = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Mic Capture Volume",
+ .info = hr222_mic_vol_info,
+ .get = hr222_mic_vol_get,
+ .put = hr222_mic_vol_put,
+ .tlv = { .p = db_scale_mic_hr222 },
+};
+
+
+/* mic boost level */
+static const DECLARE_TLV_DB_SCALE(db_scale_micboost_hr222, 0, 1800, 5400);
+
+static int hr222_mic_boost_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0; /* 0 dB */
+ uinfo->value.integer.max = 3; /* 54 dB */
+ return 0;
+}
+
+static int hr222_mic_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->mic_boost;
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ int changed = 0;
+ mutex_lock(&chip->mgr->mixer_mutex);
+ if (chip->mic_boost != ucontrol->value.integer.value[0]) {
+ changed = 1;
+ chip->mic_boost = ucontrol->value.integer.value[0];
+ hr222_micro_boost(chip->mgr, chip->mic_boost);
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static struct snd_kcontrol_new hr222_control_mic_boost = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "MicBoost Capture Volume",
+ .info = hr222_mic_boost_info,
+ .get = hr222_mic_boost_get,
+ .put = hr222_mic_boost_put,
+ .tlv = { .p = db_scale_micboost_hr222 },
+};
+
+
+/******************* Phantom power switch *******************/
+#define hr222_phantom_power_info snd_ctl_boolean_mono_info
+
+static int hr222_phantom_power_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ mutex_lock(&chip->mgr->mixer_mutex);
+ ucontrol->value.integer.value[0] = chip->phantom_power;
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return 0;
+}
+
+static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ int power, changed = 0;
+
+ mutex_lock(&chip->mgr->mixer_mutex);
+ power = !!ucontrol->value.integer.value[0];
+ if (chip->phantom_power != power) {
+ hr222_phantom_power(chip->mgr, power);
+ chip->phantom_power = power;
+ changed = 1;
+ }
+ mutex_unlock(&chip->mgr->mixer_mutex);
+ return changed;
+}
+
+static struct snd_kcontrol_new hr222_phantom_power_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Phantom Power Switch",
+ .info = hr222_phantom_power_info,
+ .get = hr222_phantom_power_get,
+ .put = hr222_phantom_power_put,
+};
+
+
+int hr222_add_mic_controls(struct snd_pcxhr *chip)
+{
+ int err;
+ if (!chip->mgr->board_has_mic)
+ return 0;
+
+ /* controls */
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_control_mic_level,
+ chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_control_mic_boost,
+ chip));
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&hr222_phantom_power_switch,
+ chip));
+ return err;
+}
diff --git a/sound/pci/pcxhr/pcxhr_mix22.h b/sound/pci/pcxhr/pcxhr_mix22.h
new file mode 100644
index 000000000000..6b318b2f0100
--- /dev/null
+++ b/sound/pci/pcxhr/pcxhr_mix22.h
@@ -0,0 +1,56 @@
+/*
+ * Driver for Digigram pcxhr compatible soundcards
+ *
+ * low level interface with interrupt ans message handling
+ *
+ * Copyright (c) 2004 by Digigram <alsa@digigram.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 __SOUND_PCXHR_MIX22_H
+#define __SOUND_PCXHR_MIX22_H
+
+struct pcxhr_mgr;
+
+int hr222_sub_init(struct pcxhr_mgr *mgr);
+int hr222_sub_set_clock(struct pcxhr_mgr *mgr, unsigned int rate,
+ int *changed);
+int hr222_get_external_clock(struct pcxhr_mgr *mgr,
+ enum pcxhr_clock_type clock_type,
+ int *sample_rate);
+
+#define HR222_LINE_PLAYBACK_LEVEL_MIN 0 /* -25.5 dB */
+#define HR222_LINE_PLAYBACK_ZERO_LEVEL 51 /* 0.0 dB */
+#define HR222_LINE_PLAYBACK_LEVEL_MAX 99 /* +24.0 dB */
+
+#define HR222_LINE_CAPTURE_LEVEL_MIN 0 /* -111.5 dB */
+#define HR222_LINE_CAPTURE_ZERO_LEVEL 223 /* 0.0 dB */
+#define HR222_LINE_CAPTURE_LEVEL_MAX 255 /* +16 dB */
+#define HR222_MICRO_CAPTURE_LEVEL_MIN 0 /* -98.5 dB */
+#define HR222_MICRO_CAPTURE_LEVEL_MAX 210 /* +6.5 dB */
+
+int hr222_update_analog_audio_level(struct snd_pcxhr *chip,
+ int is_capture,
+ int channel);
+int hr222_set_audio_source(struct snd_pcxhr *chip);
+int hr222_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx,
+ unsigned char *aes_bits);
+int hr222_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx,
+ unsigned char aes_bits);
+
+int hr222_add_mic_controls(struct snd_pcxhr *chip);
+
+#endif /* __SOUND_PCXHR_MIX22_H */
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index aabc7bc5321e..2436e374586f 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -33,20 +33,24 @@
#include <sound/tlv.h>
#include <sound/asoundef.h>
#include "pcxhr_mixer.h"
+#include "pcxhr_mix22.h"
+#define PCXHR_LINE_CAPTURE_LEVEL_MIN 0 /* -112.0 dB */
+#define PCXHR_LINE_CAPTURE_LEVEL_MAX 255 /* +15.5 dB */
+#define PCXHR_LINE_CAPTURE_ZERO_LEVEL 224 /* 0.0 dB ( 0 dBu -> 0 dBFS ) */
-#define PCXHR_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB */
-#define PCXHR_ANALOG_CAPTURE_LEVEL_MAX 255 /* +31.5 dB */
-#define PCXHR_ANALOG_CAPTURE_ZERO_LEVEL 224 /* +16.0 dB ( +31.5 dB - fix level +15.5 dB ) */
+#define PCXHR_LINE_PLAYBACK_LEVEL_MIN 0 /* -104.0 dB */
+#define PCXHR_LINE_PLAYBACK_LEVEL_MAX 128 /* +24.0 dB */
+#define PCXHR_LINE_PLAYBACK_ZERO_LEVEL 104 /* 0.0 dB ( 0 dBFS -> 0 dBu ) */
-#define PCXHR_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -128.0 dB */
-#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128 /* 0.0 dB */
-#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104 /* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
-
-static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -9600, 50, 3150);
+static const DECLARE_TLV_DB_SCALE(db_scale_analog_capture, -11200, 50, 1550);
static const DECLARE_TLV_DB_SCALE(db_scale_analog_playback, -10400, 100, 2400);
-static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_capture, int channel)
+static const DECLARE_TLV_DB_SCALE(db_scale_a_hr222_capture, -11150, 50, 1600);
+static const DECLARE_TLV_DB_SCALE(db_scale_a_hr222_playback, -2550, 50, 2400);
+
+static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip,
+ int is_capture, int channel)
{
int err, vol;
struct pcxhr_rmh rmh;
@@ -60,15 +64,17 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
if (chip->analog_playback_active[channel])
vol = chip->analog_playback_volume[channel];
else
- vol = PCXHR_ANALOG_PLAYBACK_LEVEL_MIN;
- rmh.cmd[2] = PCXHR_ANALOG_PLAYBACK_LEVEL_MAX - vol; /* playback analog levels are inversed */
+ vol = PCXHR_LINE_PLAYBACK_LEVEL_MIN;
+ /* playback analog levels are inversed */
+ rmh.cmd[2] = PCXHR_LINE_PLAYBACK_LEVEL_MAX - vol;
}
rmh.cmd[1] = 1 << ((2 * chip->chip_idx) + channel); /* audio mask */
rmh.cmd_len = 3;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err < 0) {
- snd_printk(KERN_DEBUG "error update_analog_audio_level card(%d) "
- "is_capture(%d) err(%x)\n", chip->chip_idx, is_capture, err);
+ snd_printk(KERN_DEBUG "error update_analog_audio_level card(%d)"
+ " is_capture(%d) err(%x)\n",
+ chip->chip_idx, is_capture, err);
return -EINVAL;
}
return 0;
@@ -80,14 +86,34 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
static int pcxhr_analog_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
if (kcontrol->private_value == 0) { /* playback */
- uinfo->value.integer.min = PCXHR_ANALOG_PLAYBACK_LEVEL_MIN; /* -128 dB */
- uinfo->value.integer.max = PCXHR_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */
+ if (chip->mgr->is_hr_stereo) {
+ uinfo->value.integer.min =
+ HR222_LINE_PLAYBACK_LEVEL_MIN; /* -25 dB */
+ uinfo->value.integer.max =
+ HR222_LINE_PLAYBACK_LEVEL_MAX; /* +24 dB */
+ } else {
+ uinfo->value.integer.min =
+ PCXHR_LINE_PLAYBACK_LEVEL_MIN; /*-104 dB */
+ uinfo->value.integer.max =
+ PCXHR_LINE_PLAYBACK_LEVEL_MAX; /* +24 dB */
+ }
} else { /* capture */
- uinfo->value.integer.min = PCXHR_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */
- uinfo->value.integer.max = PCXHR_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */
+ if (chip->mgr->is_hr_stereo) {
+ uinfo->value.integer.min =
+ HR222_LINE_CAPTURE_LEVEL_MIN; /*-112 dB */
+ uinfo->value.integer.max =
+ HR222_LINE_CAPTURE_LEVEL_MAX; /* +15.5 dB */
+ } else {
+ uinfo->value.integer.min =
+ PCXHR_LINE_CAPTURE_LEVEL_MIN; /*-112 dB */
+ uinfo->value.integer.max =
+ PCXHR_LINE_CAPTURE_LEVEL_MAX; /* +15.5 dB */
+ }
}
return 0;
}
@@ -98,11 +124,11 @@ static int pcxhr_analog_vol_get(struct snd_kcontrol *kcontrol,
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
mutex_lock(&chip->mgr->mixer_mutex);
if (kcontrol->private_value == 0) { /* playback */
- ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
- ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
+ ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
} else { /* capture */
- ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
- ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
+ ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
+ ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
}
mutex_unlock(&chip->mgr->mixer_mutex);
return 0;
@@ -123,18 +149,35 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
&chip->analog_capture_volume[i] :
&chip->analog_playback_volume[i];
if (is_capture) {
- if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN ||
- new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX)
- continue;
+ if (chip->mgr->is_hr_stereo) {
+ if (new_volume < HR222_LINE_CAPTURE_LEVEL_MIN ||
+ new_volume > HR222_LINE_CAPTURE_LEVEL_MAX)
+ continue;
+ } else {
+ if (new_volume < PCXHR_LINE_CAPTURE_LEVEL_MIN ||
+ new_volume > PCXHR_LINE_CAPTURE_LEVEL_MAX)
+ continue;
+ }
} else {
- if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN ||
- new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX)
- continue;
+ if (chip->mgr->is_hr_stereo) {
+ if (new_volume < HR222_LINE_PLAYBACK_LEVEL_MIN ||
+ new_volume > HR222_LINE_PLAYBACK_LEVEL_MAX)
+ continue;
+ } else {
+ if (new_volume < PCXHR_LINE_PLAYBACK_LEVEL_MIN ||
+ new_volume > PCXHR_LINE_PLAYBACK_LEVEL_MAX)
+ continue;
+ }
}
if (*stored_volume != new_volume) {
*stored_volume = new_volume;
changed = 1;
- pcxhr_update_analog_audio_level(chip, is_capture, i);
+ if (chip->mgr->is_hr_stereo)
+ hr222_update_analog_audio_level(chip,
+ is_capture, i);
+ else
+ pcxhr_update_analog_audio_level(chip,
+ is_capture, i);
}
}
mutex_unlock(&chip->mgr->mixer_mutex);
@@ -153,6 +196,7 @@ static struct snd_kcontrol_new pcxhr_control_analog_level = {
};
/* shared */
+
#define pcxhr_sw_info snd_ctl_boolean_stereo_info
static int pcxhr_audio_sw_get(struct snd_kcontrol *kcontrol,
@@ -180,7 +224,10 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
!!ucontrol->value.integer.value[i];
changed = 1;
/* update playback levels */
- pcxhr_update_analog_audio_level(chip, 0, i);
+ if (chip->mgr->is_hr_stereo)
+ hr222_update_analog_audio_level(chip, 0, i);
+ else
+ pcxhr_update_analog_audio_level(chip, 0, i);
}
}
mutex_unlock(&chip->mgr->mixer_mutex);
@@ -251,7 +298,8 @@ static int pcxhr_update_playback_stream_level(struct snd_pcxhr* chip, int idx)
#define VALID_AUDIO_IO_MUTE_LEVEL 0x000004
#define VALID_AUDIO_IO_MUTE_MONITOR_1 0x000008
-static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, int channel)
+static int pcxhr_update_audio_pipe_level(struct snd_pcxhr *chip,
+ int capture, int channel)
{
int err;
struct pcxhr_rmh rmh;
@@ -264,18 +312,20 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
pcxhr_init_rmh(&rmh, CMD_AUDIO_LEVEL_ADJUST);
/* add channel mask */
- pcxhr_set_pipe_cmd_params(&rmh, capture, 0, 0, 1 << (channel + pipe->first_audio));
- /* TODO : if mask (3 << pipe->first_audio) is used, left and right channel
- * will be programmed to the same params
- */
+ pcxhr_set_pipe_cmd_params(&rmh, capture, 0, 0,
+ 1 << (channel + pipe->first_audio));
+ /* TODO : if mask (3 << pipe->first_audio) is used, left and right
+ * channel will be programmed to the same params */
if (capture) {
rmh.cmd[0] |= VALID_AUDIO_IO_DIGITAL_LEVEL;
- /* VALID_AUDIO_IO_MUTE_LEVEL not yet handled (capture pipe level) */
+ /* VALID_AUDIO_IO_MUTE_LEVEL not yet handled
+ * (capture pipe level) */
rmh.cmd[2] = chip->digital_capture_volume[channel];
} else {
- rmh.cmd[0] |= VALID_AUDIO_IO_MONITOR_LEVEL | VALID_AUDIO_IO_MUTE_MONITOR_1;
- /* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL not yet
- * handled (playback pipe level)
+ rmh.cmd[0] |= VALID_AUDIO_IO_MONITOR_LEVEL |
+ VALID_AUDIO_IO_MUTE_MONITOR_1;
+ /* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL
+ * not yet handled (playback pipe level)
*/
rmh.cmd[2] = chip->monitoring_volume[channel] << 10;
if (chip->monitoring_active[channel] == 0)
@@ -284,8 +334,8 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
rmh.cmd_len = 3;
err = pcxhr_send_msg(chip->mgr, &rmh);
- if(err<0) {
- snd_printk(KERN_DEBUG "error update_audio_level card(%d) err(%x)\n",
+ if (err < 0) {
+ snd_printk(KERN_DEBUG "error update_audio_level(%d) err=%x\n",
chip->chip_idx, err);
return -EINVAL;
}
@@ -309,15 +359,15 @@ static int pcxhr_pcm_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int *stored_volume;
int is_capture = kcontrol->private_value;
mutex_lock(&chip->mgr->mixer_mutex);
- if (is_capture)
- stored_volume = chip->digital_capture_volume; /* digital capture */
- else
- stored_volume = chip->digital_playback_volume[idx]; /* digital playback */
+ if (is_capture) /* digital capture */
+ stored_volume = chip->digital_capture_volume;
+ else /* digital playback */
+ stored_volume = chip->digital_playback_volume[idx];
ucontrol->value.integer.value[0] = stored_volume[0];
ucontrol->value.integer.value[1] = stored_volume[1];
mutex_unlock(&chip->mgr->mixer_mutex);
@@ -328,7 +378,7 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
- int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
+ int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int changed = 0;
int is_capture = kcontrol->private_value;
int *stored_volume;
@@ -384,7 +434,8 @@ static int pcxhr_pcm_sw_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
@@ -444,8 +495,8 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
if (chip->monitoring_volume[i] !=
ucontrol->value.integer.value[i]) {
chip->monitoring_volume[i] =
- !!ucontrol->value.integer.value[i];
- if(chip->monitoring_active[i])
+ ucontrol->value.integer.value[i];
+ if (chip->monitoring_active[i])
/* update monitoring volume and mute */
/* do only when monitoring is unmuted */
pcxhr_update_audio_pipe_level(chip, 0, i);
@@ -460,7 +511,7 @@ static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
- .name = "Monitoring Volume",
+ .name = "Monitoring Playback Volume",
.info = pcxhr_digital_vol_info, /* shared */
.get = pcxhr_monitor_vol_get,
.put = pcxhr_monitor_vol_put,
@@ -511,7 +562,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
static struct snd_kcontrol_new pcxhr_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Monitoring Switch",
+ .name = "Monitoring Playback Switch",
.info = pcxhr_sw_info, /* shared */
.get = pcxhr_monitor_sw_get,
.put = pcxhr_monitor_sw_put
@@ -533,7 +584,7 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
struct pcxhr_rmh rmh;
unsigned int mask, reg;
unsigned int codec;
- int err, use_src, changed;
+ int err, changed;
switch (chip->chip_idx) {
case 0 : mask = PCXHR_SOURCE_AUDIO01_UER; codec = CS8420_01_CS; break;
@@ -542,13 +593,10 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
case 3 : mask = PCXHR_SOURCE_AUDIO67_UER; codec = CS8420_67_CS; break;
default: return -EINVAL;
}
- reg = 0; /* audio source from analog plug */
- use_src = 0; /* do not activate codec SRC */
-
if (chip->audio_capture_source != 0) {
reg = mask; /* audio source from digital plug */
- if (chip->audio_capture_source == 2)
- use_src = 1;
+ } else {
+ reg = 0; /* audio source from analog plug */
}
/* set the input source */
pcxhr_write_io_num_reg_cont(chip->mgr, mask, reg, &changed);
@@ -560,29 +608,61 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
if (err)
return err;
}
- pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set codec SRC on off */
- rmh.cmd_len = 3;
- rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
- rmh.cmd[1] = codec;
- rmh.cmd[2] = (CS8420_DATA_FLOW_CTL & CHIP_SIG_AND_MAP_SPI) | (use_src ? 0x41 : 0x54);
- err = pcxhr_send_msg(chip->mgr, &rmh);
- if(err)
- return err;
- rmh.cmd[2] = (CS8420_CLOCK_SRC_CTL & CHIP_SIG_AND_MAP_SPI) | (use_src ? 0x41 : 0x49);
- err = pcxhr_send_msg(chip->mgr, &rmh);
+ if (chip->mgr->board_aes_in_192k) {
+ int i;
+ unsigned int src_config = 0xC0;
+ /* update all src configs with one call */
+ for (i = 0; (i < 4) && (i < chip->mgr->capture_chips); i++) {
+ if (chip->mgr->chip[i]->audio_capture_source == 2)
+ src_config |= (1 << (3 - i));
+ }
+ /* set codec SRC on off */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
+ rmh.cmd_len = 2;
+ rmh.cmd[0] |= IO_NUM_REG_CONFIG_SRC;
+ rmh.cmd[1] = src_config;
+ err = pcxhr_send_msg(chip->mgr, &rmh);
+ } else {
+ int use_src = 0;
+ if (chip->audio_capture_source == 2)
+ use_src = 1;
+ /* set codec SRC on off */
+ pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
+ rmh.cmd_len = 3;
+ rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
+ rmh.cmd[1] = codec;
+ rmh.cmd[2] = ((CS8420_DATA_FLOW_CTL & CHIP_SIG_AND_MAP_SPI) |
+ (use_src ? 0x41 : 0x54));
+ err = pcxhr_send_msg(chip->mgr, &rmh);
+ if (err)
+ return err;
+ rmh.cmd[2] = ((CS8420_CLOCK_SRC_CTL & CHIP_SIG_AND_MAP_SPI) |
+ (use_src ? 0x41 : 0x49));
+ err = pcxhr_send_msg(chip->mgr, &rmh);
+ }
return err;
}
static int pcxhr_audio_src_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Analog", "Digital", "Digi+SRC"};
+ static const char *texts[5] = {
+ "Line", "Digital", "Digi+SRC", "Mic", "Line+Mic"
+ };
+ int i;
+ struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
+ i = 2; /* no SRC, no Mic available */
+ if (chip->mgr->board_has_aes1) {
+ i = 3; /* SRC available */
+ if (chip->mgr->board_has_mic)
+ i = 5; /* Mic and MicroMix available */
+ }
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
+ uinfo->value.enumerated.items = i;
+ if (uinfo->value.enumerated.item > (i-1))
+ uinfo->value.enumerated.item = i-1;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
@@ -601,13 +681,21 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
int ret = 0;
-
- if (ucontrol->value.enumerated.item[0] >= 3)
+ int i = 2; /* no SRC, no Mic available */
+ if (chip->mgr->board_has_aes1) {
+ i = 3; /* SRC available */
+ if (chip->mgr->board_has_mic)
+ i = 5; /* Mic and MicroMix available */
+ }
+ if (ucontrol->value.enumerated.item[0] >= i)
return -EINVAL;
mutex_lock(&chip->mgr->mixer_mutex);
if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) {
chip->audio_capture_source = ucontrol->value.enumerated.item[0];
- pcxhr_set_audio_source(chip);
+ if (chip->mgr->is_hr_stereo)
+ hr222_set_audio_source(chip);
+ else
+ pcxhr_set_audio_source(chip);
ret = 1;
}
mutex_unlock(&chip->mgr->mixer_mutex);
@@ -626,25 +714,46 @@ static struct snd_kcontrol_new pcxhr_control_audio_src = {
/*
* clock type selection
* enum pcxhr_clock_type {
- * PCXHR_CLOCK_TYPE_INTERNAL = 0,
- * PCXHR_CLOCK_TYPE_WORD_CLOCK,
- * PCXHR_CLOCK_TYPE_AES_SYNC,
- * PCXHR_CLOCK_TYPE_AES_1,
- * PCXHR_CLOCK_TYPE_AES_2,
- * PCXHR_CLOCK_TYPE_AES_3,
- * PCXHR_CLOCK_TYPE_AES_4,
- * };
+ * PCXHR_CLOCK_TYPE_INTERNAL = 0,
+ * PCXHR_CLOCK_TYPE_WORD_CLOCK,
+ * PCXHR_CLOCK_TYPE_AES_SYNC,
+ * PCXHR_CLOCK_TYPE_AES_1,
+ * PCXHR_CLOCK_TYPE_AES_2,
+ * PCXHR_CLOCK_TYPE_AES_3,
+ * PCXHR_CLOCK_TYPE_AES_4,
+ * PCXHR_CLOCK_TYPE_MAX = PCXHR_CLOCK_TYPE_AES_4,
+ * HR22_CLOCK_TYPE_INTERNAL = PCXHR_CLOCK_TYPE_INTERNAL,
+ * HR22_CLOCK_TYPE_AES_SYNC,
+ * HR22_CLOCK_TYPE_AES_1,
+ * HR22_CLOCK_TYPE_MAX = HR22_CLOCK_TYPE_AES_1,
+ * };
*/
static int pcxhr_clock_type_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[7] = {
- "Internal", "WordClock", "AES Sync", "AES 1", "AES 2", "AES 3", "AES 4"
+ static const char *textsPCXHR[7] = {
+ "Internal", "WordClock", "AES Sync",
+ "AES 1", "AES 2", "AES 3", "AES 4"
+ };
+ static const char *textsHR22[3] = {
+ "Internal", "AES Sync", "AES 1"
};
+ const char **texts;
struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
- int clock_items = 3 + mgr->capture_chips;
-
+ int clock_items = 2; /* at least Internal and AES Sync clock */
+ if (mgr->board_has_aes1) {
+ clock_items += mgr->capture_chips; /* add AES x */
+ if (!mgr->is_hr_stereo)
+ clock_items += 1; /* add word clock */
+ }
+ if (mgr->is_hr_stereo) {
+ texts = textsHR22;
+ snd_BUG_ON(clock_items > (HR22_CLOCK_TYPE_MAX+1));
+ } else {
+ texts = textsPCXHR;
+ snd_BUG_ON(clock_items > (PCXHR_CLOCK_TYPE_MAX+1));
+ }
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = clock_items;
@@ -667,9 +776,13 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol);
- unsigned int clock_items = 3 + mgr->capture_chips;
int rate, ret = 0;
-
+ unsigned int clock_items = 2; /* at least Internal and AES Sync clock */
+ if (mgr->board_has_aes1) {
+ clock_items += mgr->capture_chips; /* add AES x */
+ if (!mgr->is_hr_stereo)
+ clock_items += 1; /* add word clock */
+ }
if (ucontrol->value.enumerated.item[0] >= clock_items)
return -EINVAL;
mutex_lock(&mgr->mixer_mutex);
@@ -677,7 +790,8 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
mutex_lock(&mgr->setup_mutex);
mgr->use_clock_type = ucontrol->value.enumerated.item[0];
if (mgr->use_clock_type)
- pcxhr_get_external_clock(mgr, mgr->use_clock_type, &rate);
+ pcxhr_get_external_clock(mgr, mgr->use_clock_type,
+ &rate);
else
rate = mgr->sample_rate;
if (rate) {
@@ -686,7 +800,7 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
mgr->sample_rate = rate;
}
mutex_unlock(&mgr->setup_mutex);
- ret = 1; /* return 1 even if the set was not done. ok ? */
+ ret = 1; /* return 1 even if the set was not done. ok ? */
}
mutex_unlock(&mgr->mixer_mutex);
return ret;
@@ -747,14 +861,16 @@ static struct snd_kcontrol_new pcxhr_control_clock_rate = {
/*
* IEC958 status bits
*/
-static int pcxhr_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+static int pcxhr_iec958_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
-static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx, unsigned char* aes_bits)
+static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip,
+ int aes_idx, unsigned char *aes_bits)
{
int i, err;
unsigned char temp;
@@ -763,39 +879,61 @@ static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx, unsign
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
rmh.cmd[0] |= IO_NUM_UER_CHIP_REG;
switch (chip->chip_idx) {
- case 0: rmh.cmd[1] = CS8420_01_CS; break; /* use CS8416_01_CS for AES SYNC plug */
+ /* instead of CS8420_01_CS use CS8416_01_CS for AES SYNC plug */
+ case 0: rmh.cmd[1] = CS8420_01_CS; break;
case 1: rmh.cmd[1] = CS8420_23_CS; break;
case 2: rmh.cmd[1] = CS8420_45_CS; break;
case 3: rmh.cmd[1] = CS8420_67_CS; break;
default: return -EINVAL;
}
- switch (aes_idx) {
- case 0: rmh.cmd[2] = CS8420_CSB0; break; /* use CS8416_CSBx for AES SYNC plug */
- case 1: rmh.cmd[2] = CS8420_CSB1; break;
- case 2: rmh.cmd[2] = CS8420_CSB2; break;
- case 3: rmh.cmd[2] = CS8420_CSB3; break;
- case 4: rmh.cmd[2] = CS8420_CSB4; break;
- default: return -EINVAL;
+ if (chip->mgr->board_aes_in_192k) {
+ switch (aes_idx) {
+ case 0: rmh.cmd[2] = CS8416_CSB0; break;
+ case 1: rmh.cmd[2] = CS8416_CSB1; break;
+ case 2: rmh.cmd[2] = CS8416_CSB2; break;
+ case 3: rmh.cmd[2] = CS8416_CSB3; break;
+ case 4: rmh.cmd[2] = CS8416_CSB4; break;
+ default: return -EINVAL;
+ }
+ } else {
+ switch (aes_idx) {
+ /* instead of CS8420_CSB0 use CS8416_CSBx for AES SYNC plug */
+ case 0: rmh.cmd[2] = CS8420_CSB0; break;
+ case 1: rmh.cmd[2] = CS8420_CSB1; break;
+ case 2: rmh.cmd[2] = CS8420_CSB2; break;
+ case 3: rmh.cmd[2] = CS8420_CSB3; break;
+ case 4: rmh.cmd[2] = CS8420_CSB4; break;
+ default: return -EINVAL;
+ }
}
- rmh.cmd[1] &= 0x0fffff; /* size and code the chip id for the fpga */
- rmh.cmd[2] &= CHIP_SIG_AND_MAP_SPI; /* chip signature + map for spi read */
+ /* size and code the chip id for the fpga */
+ rmh.cmd[1] &= 0x0fffff;
+ /* chip signature + map for spi read */
+ rmh.cmd[2] &= CHIP_SIG_AND_MAP_SPI;
rmh.cmd_len = 3;
err = pcxhr_send_msg(chip->mgr, &rmh);
if (err)
return err;
- temp = 0;
- for (i = 0; i < 8; i++) {
- /* attention : reversed bit order (not with CS8416_01_CS) */
- temp <<= 1;
- if (rmh.stat[1] & (1 << i))
- temp |= 1;
+
+ if (chip->mgr->board_aes_in_192k) {
+ temp = (unsigned char)rmh.stat[1];
+ } else {
+ temp = 0;
+ /* reversed bit order (not with CS8416_01_CS) */
+ for (i = 0; i < 8; i++) {
+ temp <<= 1;
+ if (rmh.stat[1] & (1 << i))
+ temp |= 1;
+ }
}
- snd_printdd("read iec958 AES %d byte %d = 0x%x\n", chip->chip_idx, aes_idx, temp);
+ snd_printdd("read iec958 AES %d byte %d = 0x%x\n",
+ chip->chip_idx, aes_idx, temp);
*aes_bits = temp;
return 0;
}
-static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol);
unsigned char aes_bits;
@@ -806,7 +944,12 @@ static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if (kcontrol->private_value == 0) /* playback */
aes_bits = chip->aes_bits[i];
else { /* capture */
- err = pcxhr_iec958_capture_byte(chip, i, &aes_bits);
+ if (chip->mgr->is_hr_stereo)
+ err = hr222_iec958_capture_byte(chip, i,
+ &aes_bits);
+ else
+ err = pcxhr_iec958_capture_byte(chip, i,
+ &aes_bits);
if (err)
break;
}
@@ -825,7 +968,8 @@ static int pcxhr_iec958_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx, unsigned char aes_bits)
+static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip,
+ int aes_idx, unsigned char aes_bits)
{
int i, err, cmd;
unsigned char new_bits = aes_bits;
@@ -834,12 +978,12 @@ static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx, unsigne
for (i = 0; i < 8; i++) {
if ((old_bits & 0x01) != (new_bits & 0x01)) {
- cmd = chip->chip_idx & 0x03; /* chip index 0..3 */
- if(chip->chip_idx > 3)
+ cmd = chip->chip_idx & 0x03; /* chip index 0..3 */
+ if (chip->chip_idx > 3)
/* new bit used if chip_idx>3 (PCX1222HR) */
cmd |= 1 << 22;
- cmd |= ((aes_idx << 3) + i) << 2; /* add bit offset */
- cmd |= (new_bits & 0x01) << 23; /* add bit value */
+ cmd |= ((aes_idx << 3) + i) << 2; /* add bit offset */
+ cmd |= (new_bits & 0x01) << 23; /* add bit value */
pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
rmh.cmd[0] |= IO_NUM_REG_CUER;
rmh.cmd[1] = cmd;
@@ -867,7 +1011,12 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
mutex_lock(&chip->mgr->mixer_mutex);
for (i = 0; i < 5; i++) {
if (ucontrol->value.iec958.status[i] != chip->aes_bits[i]) {
- pcxhr_iec958_update_byte(chip, i, ucontrol->value.iec958.status[i]);
+ if (chip->mgr->is_hr_stereo)
+ hr222_iec958_update_byte(chip, i,
+ ucontrol->value.iec958.status[i]);
+ else
+ pcxhr_iec958_update_byte(chip, i,
+ ucontrol->value.iec958.status[i]);
changed = 1;
}
}
@@ -917,29 +1066,53 @@ static void pcxhr_init_audio_levels(struct snd_pcxhr *chip)
/* at boot time the digital volumes are unmuted 0dB */
for (j = 0; j < PCXHR_PLAYBACK_STREAMS; j++) {
chip->digital_playback_active[j][i] = 1;
- chip->digital_playback_volume[j][i] = PCXHR_DIGITAL_ZERO_LEVEL;
+ chip->digital_playback_volume[j][i] =
+ PCXHR_DIGITAL_ZERO_LEVEL;
}
- /* after boot, only two bits are set on the uer interface */
- chip->aes_bits[0] = IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_FS_48000;
-/* only for test purpose, remove later */
+ /* after boot, only two bits are set on the uer
+ * interface
+ */
+ chip->aes_bits[0] = (IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_PRO_FS_48000);
#ifdef CONFIG_SND_DEBUG
- /* analog volumes for playback (is LEVEL_MIN after boot) */
+ /* analog volumes for playback
+ * (is LEVEL_MIN after boot)
+ */
chip->analog_playback_active[i] = 1;
- chip->analog_playback_volume[i] = PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL;
- pcxhr_update_analog_audio_level(chip, 0, i);
+ if (chip->mgr->is_hr_stereo)
+ chip->analog_playback_volume[i] =
+ HR222_LINE_PLAYBACK_ZERO_LEVEL;
+ else {
+ chip->analog_playback_volume[i] =
+ PCXHR_LINE_PLAYBACK_ZERO_LEVEL;
+ pcxhr_update_analog_audio_level(chip, 0, i);
+ }
#endif
-/* test end */
+ /* stereo cards need to be initialised after boot */
+ if (chip->mgr->is_hr_stereo)
+ hr222_update_analog_audio_level(chip, 0, i);
}
if (chip->nb_streams_capt) {
/* at boot time the digital volumes are unmuted 0dB */
- chip->digital_capture_volume[i] = PCXHR_DIGITAL_ZERO_LEVEL;
-/* only for test purpose, remove later */
+ chip->digital_capture_volume[i] =
+ PCXHR_DIGITAL_ZERO_LEVEL;
+ chip->analog_capture_active = 1;
#ifdef CONFIG_SND_DEBUG
- /* analog volumes for playback (is LEVEL_MIN after boot) */
- chip->analog_capture_volume[i] = PCXHR_ANALOG_CAPTURE_ZERO_LEVEL;
- pcxhr_update_analog_audio_level(chip, 1, i);
+ /* analog volumes for playback
+ * (is LEVEL_MIN after boot)
+ */
+ if (chip->mgr->is_hr_stereo)
+ chip->analog_capture_volume[i] =
+ HR222_LINE_CAPTURE_ZERO_LEVEL;
+ else {
+ chip->analog_capture_volume[i] =
+ PCXHR_LINE_CAPTURE_ZERO_LEVEL;
+ pcxhr_update_analog_audio_level(chip, 1, i);
+ }
#endif
-/* test end */
+ /* stereo cards need to be initialised after boot */
+ if (chip->mgr->is_hr_stereo)
+ hr222_update_analog_audio_level(chip, 1, i);
}
}
@@ -963,90 +1136,125 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp = pcxhr_control_analog_level;
temp.name = "Master Playback Volume";
temp.private_value = 0; /* playback */
- temp.tlv.p = db_scale_analog_playback;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ if (mgr->is_hr_stereo)
+ temp.tlv.p = db_scale_a_hr222_playback;
+ else
+ temp.tlv.p = db_scale_analog_playback;
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
+
/* output mute controls */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_output_switch,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_output_switch,
+ chip));
+ if (err < 0)
return err;
-
+
temp = snd_pcxhr_pcm_vol;
temp.name = "PCM Playback Volume";
temp.count = PCXHR_PLAYBACK_STREAMS;
temp.private_value = 0; /* playback */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_pcm_switch,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_pcm_switch, chip));
+ if (err < 0)
return err;
/* IEC958 controls */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_playback_iec958_mask,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_playback_iec958_mask,
+ chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_playback_iec958,
- chip))) < 0)
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_playback_iec958,
+ chip));
+ if (err < 0)
return err;
}
if (chip->nb_streams_capt) {
- /* analog input level control only on first two chips !*/
+ /* analog input level control */
temp = pcxhr_control_analog_level;
- temp.name = "Master Capture Volume";
+ temp.name = "Line Capture Volume";
temp.private_value = 1; /* capture */
- temp.tlv.p = db_scale_analog_capture;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+ if (mgr->is_hr_stereo)
+ temp.tlv.p = db_scale_a_hr222_capture;
+ else
+ temp.tlv.p = db_scale_analog_capture;
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = snd_pcxhr_pcm_vol;
temp.name = "PCM Capture Volume";
temp.count = 1;
temp.private_value = 1; /* capture */
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
+
/* Audio source */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_audio_src,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_audio_src, chip));
+ if (err < 0)
return err;
+
/* IEC958 controls */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_capture_iec958_mask,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_capture_iec958_mask,
+ chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_capture_iec958,
- chip))) < 0)
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_capture_iec958,
+ chip));
+ if (err < 0)
return err;
+
+ if (mgr->is_hr_stereo) {
+ err = hr222_add_mic_controls(chip);
+ if (err < 0)
+ return err;
+ }
}
/* monitoring only if playback and capture device available */
if (chip->nb_streams_capt > 0 && chip->nb_streams_play > 0) {
/* monitoring */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_monitor_vol,
- chip))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_monitor_vol, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_monitor_sw,
- chip))) < 0)
+
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_monitor_sw, chip));
+ if (err < 0)
return err;
}
if (i == 0) {
/* clock mode only one control per pcxhr */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_clock_type,
- mgr))) < 0)
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_clock_type, mgr));
+ if (err < 0)
return err;
- /* non standard control used to scan the external clock presence/frequencies */
- if ((err = snd_ctl_add(chip->card,
- snd_ctl_new1(&pcxhr_control_clock_rate,
- mgr))) < 0)
+ /* non standard control used to scan
+ * the external clock presence/frequencies
+ */
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&pcxhr_control_clock_rate, mgr));
+ if (err < 0)
return err;
}
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index e9f0706ed3e4..3caacfb9d8e0 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -172,7 +172,7 @@ MODULE_PARM_DESC(opl3_port, "OPL3 port # for Riptide driver.");
#define MAX_WRITE_RETRY 10 /* cmd interface limits */
#define MAX_ERROR_COUNT 10
-#define CMDIF_TIMEOUT 500000
+#define CMDIF_TIMEOUT 50000
#define RESET_TRIES 5
#define READ_PORT_ULONG(p) inl((unsigned long)&(p))
@@ -1754,7 +1754,7 @@ snd_riptide_interrupt(int irq, void *dev_id)
if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) ||
IS_EOCIRQ(cif->hwport)) {
chip->handled_irqs++;
- tasklet_hi_schedule(&chip->riptide_tq);
+ tasklet_schedule(&chip->riptide_tq);
}
if (chip->rmidi && IS_MPUIRQ(cif->hwport)) {
chip->handled_irqs++;
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 736246f98acc..44d0c15e2b71 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -1452,7 +1452,7 @@ static int snd_hdsp_create_midi (struct snd_card *card, struct hdsp *hdsp, int i
if (snd_rawmidi_new (card, buf, id, 1, 1, &hdsp->midi[id].rmidi) < 0)
return -1;
- sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+ sprintf(hdsp->midi[id].rmidi->name, "HDSP MIDI %d", id+1);
hdsp->midi[id].rmidi->private_data = &hdsp->midi[id];
snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output);
@@ -3750,7 +3750,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id)
}
}
if (hdsp->use_midi_tasklet && schedule)
- tasklet_hi_schedule(&hdsp->midi_tasklet);
+ tasklet_schedule(&hdsp->midi_tasklet);
return IRQ_HANDLED;
}
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 98762f909d64..71231cf1b2b0 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -1293,7 +1293,7 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card,
if (err < 0)
return err;
- sprintf (hdspm->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+ sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1);
hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
@@ -3476,7 +3476,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id)
schedule = 1;
}
if (schedule)
- tasklet_hi_schedule(&hdspm->midi_tasklet);
+ tasklet_schedule(&hdspm->midi_tasklet);
return IRQ_HANDLED;
}
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
index fa4b11398b1f..ea903c8e90dd 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c
@@ -41,7 +41,7 @@ irqreturn_t pdacf_interrupt(int irq, void *dev)
if (stat & PDAUDIOCF_IRQOVR) /* should never happen */
snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n");
if (chip->pcm_substream)
- tasklet_hi_schedule(&chip->tq);
+ tasklet_schedule(&chip->tq);
if (!(stat & PDAUDIOCF_IRQAKM))
stat |= PDAUDIOCF_IRQAKM; /* check rate */
}
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index a38c0c790d2b..af76ee862d27 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -1033,7 +1033,7 @@ static int __init snd_pmac_detect(struct snd_pmac *chip)
}
if (of_device_is_compatible(sound, "tumbler")) {
chip->model = PMAC_TUMBLER;
- chip->can_capture = 0; /* no capture */
+ chip->can_capture = machine_is_compatible("PowerMac4,2");
chip->can_duplex = 0;
// chip->can_byte_swap = 0; /* FIXME: check this */
chip->num_freqs = ARRAY_SIZE(tumbler_freqs);
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index f746e15b8481..3eb223385416 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -875,7 +875,8 @@ static struct snd_kcontrol_new snapper_mixers[] __initdata = {
.put = tumbler_put_master_switch
},
DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM),
- DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2),
+ /* Alternative PCM is assigned to Mic analog loopback on iBook G4 */
+ DEFINE_SNAPPER_MIX("Mic Playback Volume", 0, VOL_IDX_PCM2),
DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC),
DEFINE_SNAPPER_MONO("Tone Control - Bass", bass),
DEFINE_SNAPPER_MONO("Tone Control - Treble", treble),
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 4dfda6674bec..ef025c66cc66 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -22,17 +22,16 @@ if SND_SOC
config SND_SOC_AC97_BUS
bool
-# All the supported Soc's
-source "sound/soc/at32/Kconfig"
-source "sound/soc/at91/Kconfig"
+# All the supported SoCs
+source "sound/soc/atmel/Kconfig"
source "sound/soc/au1x/Kconfig"
+source "sound/soc/blackfin/Kconfig"
+source "sound/soc/davinci/Kconfig"
+source "sound/soc/fsl/Kconfig"
+source "sound/soc/omap/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
-source "sound/soc/fsl/Kconfig"
-source "sound/soc/davinci/Kconfig"
-source "sound/soc/omap/Kconfig"
-source "sound/soc/blackfin/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index d849349f2c66..86a9b1f5b0f3 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,13 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
-obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
-obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/
+obj-$(CONFIG_SND_SOC) += codecs/
+obj-$(CONFIG_SND_SOC) += atmel/
+obj-$(CONFIG_SND_SOC) += au1x/
+obj-$(CONFIG_SND_SOC) += blackfin/
+obj-$(CONFIG_SND_SOC) += davinci/
+obj-$(CONFIG_SND_SOC) += fsl/
+obj-$(CONFIG_SND_SOC) += omap/
+obj-$(CONFIG_SND_SOC) += pxa/
+obj-$(CONFIG_SND_SOC) += s3c24xx/
+obj-$(CONFIG_SND_SOC) += sh/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
deleted file mode 100644
index b0765e86c085..000000000000
--- a/sound/soc/at32/Kconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-config SND_AT32_SOC
- tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
- depends on AVR32 && SND_SOC
- help
- Say Y or M if you want to add support for codecs attached to
- the AT32 SSC interface. You will also need to
- to select the audio interfaces to support below.
-
-
-config SND_AT32_SOC_SSC
- tristate
-
-
-
-config SND_AT32_SOC_PLAYPAQ
- tristate "SoC Audio support for PlayPaq with WM8510"
- depends on SND_AT32_SOC && BOARD_PLAYPAQ
- select SND_AT32_SOC_SSC
- select SND_SOC_WM8510
- help
- Say Y or M here if you want to add support for SoC audio
- on the LRS PlayPaq.
-
-
-
-config SND_AT32_SOC_PLAYPAQ_SLAVE
- bool "Run CODEC on PlayPaq in slave mode"
- depends on SND_AT32_SOC_PLAYPAQ
- default n
- help
- Say Y if you want to run with the AT32 SSC generating the BCLK
- and FRAME signals on the PlayPaq. Unless you want to play
- with the AT32 as the SSC master, you probably want to say N here,
- as this will give you better sound quality.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
deleted file mode 100644
index c03e55ececeb..000000000000
--- a/sound/soc/at32/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# AT32 Platform Support
-snd-soc-at32-objs := at32-pcm.o
-snd-soc-at32-ssc-objs := at32-ssc.o
-
-obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
-obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
-
-# AT32 Machine Support
-snd-soc-playpaq-objs := playpaq_wm8510.o
-
-obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c
deleted file mode 100644
index c83584f989a9..000000000000
--- a/sound/soc/at32/at32-pcm.c
+++ /dev/null
@@ -1,492 +0,0 @@
-/* sound/soc/at32/at32-pcm.c
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- * Geoffrey Wossum <gwossum@acm.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.
- *
- * Note that this is basically a port of the sound/soc/at91-pcm.c to
- * the AVR32 kernel. Thanks to Frank Mandarino for that code.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-
-
-
-/*--------------------------------------------------------------------------*\
- * Hardware definition
-\*--------------------------------------------------------------------------*/
-/* TODO: These values were taken from the AT91 platform driver, check
- * them against real values for AT32
- */
-static const struct snd_pcm_hardware at32_pcm_hardware = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE),
-
- .formats = SNDRV_PCM_FMTBIT_S16,
- .period_bytes_min = 32,
- .period_bytes_max = 8192, /* 512 frames * 16 bytes / frame */
- .periods_min = 2,
- .periods_max = 1024,
- .buffer_bytes_max = 32 * 1024,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Data types
-\*--------------------------------------------------------------------------*/
-struct at32_runtime_data {
- struct at32_pcm_dma_params *params;
- dma_addr_t dma_buffer; /* physical address of DMA buffer */
- dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
- size_t period_size;
-
- dma_addr_t period_ptr; /* physical address of next period */
- int periods; /* period index of period_ptr */
-
- /* Save PDC registers (for power management) */
- u32 pdc_xpr_save;
- u32 pdc_xcr_save;
- u32 pdc_xnpr_save;
- u32 pdc_xncr_save;
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
- size_t size = at32_pcm_hardware.buffer_bytes_max;
-
- dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
- dmabuf->dev.dev = pcm->card->dev;
- dmabuf->private_data = NULL;
- dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
- &dmabuf->addr, GFP_KERNEL);
- pr_debug("at32_pcm: preallocate_dma_buffer: "
- "area=%p, addr=%p, size=%ld\n",
- (void *)dmabuf->area, (void *)dmabuf->addr, size);
-
- if (!dmabuf->area)
- return -ENOMEM;
-
- dmabuf->bytes = size;
- return 0;
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * ISR
-\*--------------------------------------------------------------------------*/
-static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *rtd = substream->runtime;
- struct at32_runtime_data *prtd = rtd->private_data;
- struct at32_pcm_dma_params *params = prtd->params;
- static int count;
-
- count++;
- if (ssc_sr & params->mask->ssc_endbuf) {
- pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
- "underrun" : "overrun", params->name, ssc_sr, count);
-
- /* re-start the PDC */
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end)
- prtd->period_ptr = prtd->dma_buffer;
-
-
- ssc_writex(params->ssc->regs, params->pdc->xpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_enable);
- }
-
-
- if (ssc_sr & params->mask->ssc_endx) {
- /* Load the PDC next pointer and counter registers */
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end)
- prtd->period_ptr = prtd->dma_buffer;
- ssc_writex(params->ssc->regs, params->pdc->xnpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
- }
-
-
- snd_pcm_period_elapsed(substream);
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * PCM operations
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at32_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* this may get called several times by oss emulation
- * with different params
- */
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->params = rtd->dai->cpu_dai->dma_data;
- prtd->params->dma_intr_handler = at32_pcm_dma_irq;
-
- prtd->dma_buffer = runtime->dma_addr;
- prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
- prtd->period_size = params_period_bytes(params);
-
- pr_debug("hw_params: DMA for %s initialized "
- "(dma_bytes=%ld, period_size=%ld)\n",
- prtd->params->name, runtime->dma_bytes, prtd->period_size);
-
- return 0;
-}
-
-
-
-static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct at32_runtime_data *prtd = substream->runtime->private_data;
- struct at32_pcm_dma_params *params = prtd->params;
-
- if (params != NULL) {
- ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
- params->mask->pdc_disable);
- prtd->params->dma_intr_handler = NULL;
- }
-
- return 0;
-}
-
-
-
-static int at32_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct at32_runtime_data *prtd = substream->runtime->private_data;
- struct at32_pcm_dma_params *params = prtd->params;
-
- ssc_writex(params->ssc->regs, SSC_IDR,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
-
- return 0;
-}
-
-
-static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *rtd = substream->runtime;
- struct at32_runtime_data *prtd = rtd->private_data;
- struct at32_pcm_dma_params *params = prtd->params;
- int ret = 0;
-
- pr_debug("at32_pcm_trigger: buffer_size = %ld, "
- "dma_area = %p, dma_bytes = %ld\n",
- rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- prtd->period_ptr = prtd->dma_buffer;
-
- ssc_writex(params->ssc->regs, params->pdc->xpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
-
- prtd->period_ptr += prtd->period_size;
- ssc_writex(params->ssc->regs, params->pdc->xnpr,
- prtd->period_ptr);
- ssc_writex(params->ssc->regs, params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
-
- pr_debug("trigger: period_ptr=%lx, xpr=%x, "
- "xcr=%d, xnpr=%x, xncr=%d\n",
- (unsigned long)prtd->period_ptr,
- ssc_readx(params->ssc->regs, params->pdc->xpr),
- ssc_readx(params->ssc->regs, params->pdc->xcr),
- ssc_readx(params->ssc->regs, params->pdc->xnpr),
- ssc_readx(params->ssc->regs, params->pdc->xncr));
-
- ssc_writex(params->ssc->regs, SSC_IER,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
- ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
- params->mask->pdc_enable);
-
- pr_debug("sr=%x, imr=%x\n",
- ssc_readx(params->ssc->regs, SSC_SR),
- ssc_readx(params->ssc->regs, SSC_IER));
- break; /* SNDRV_PCM_TRIGGER_START */
-
-
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
- break;
-
-
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_enable);
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-
-
-static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at32_runtime_data *prtd = runtime->private_data;
- struct at32_pcm_dma_params *params = prtd->params;
- dma_addr_t ptr;
- snd_pcm_uframes_t x;
-
- ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
- x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
- if (x == runtime->buffer_size)
- x = 0;
-
- return x;
-}
-
-
-
-static int at32_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at32_runtime_data *prtd;
- int ret = 0;
-
- snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
-
- /* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- runtime->private_data = prtd;
-
-
-out:
- return ret;
-}
-
-
-
-static int at32_pcm_close(struct snd_pcm_substream *substream)
-{
- struct at32_runtime_data *prtd = substream->runtime->private_data;
-
- kfree(prtd);
- return 0;
-}
-
-
-static int at32_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return remap_pfn_range(vma, vma->vm_start,
- substream->dma_buffer.addr >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-
-
-static struct snd_pcm_ops at32_pcm_ops = {
- .open = at32_pcm_open,
- .close = at32_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = at32_pcm_hw_params,
- .hw_free = at32_pcm_hw_free,
- .prepare = at32_pcm_prepare,
- .trigger = at32_pcm_trigger,
- .pointer = at32_pcm_pointer,
- .mmap = at32_pcm_mmap,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
-static u64 at32_pcm_dmamask = 0xffffffff;
-
-static int at32_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai,
- struct snd_pcm *pcm)
-{
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &at32_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (dai->playback.channels_min) {
- ret = at32_pcm_preallocate_dma_buffer(
- pcm, SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->capture.channels_min) {
- pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
- ret = at32_pcm_preallocate_dma_buffer(
- pcm, SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
-
-
-out:
- return ret;
-}
-
-
-
-static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (substream == NULL)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_coherent(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_pcm_suspend(struct platform_device *pdev,
- struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct at32_runtime_data *prtd;
- struct at32_pcm_dma_params *params;
-
- if (runtime == NULL)
- return 0;
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* Disable the PDC and save the PDC registers */
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
- params->mask->pdc_disable);
-
- prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
- prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
- prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
- prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
- return 0;
-}
-
-
-
-static int at32_pcm_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct at32_runtime_data *prtd;
- struct at32_pcm_dma_params *params;
-
- if (runtime == NULL)
- return 0;
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* Restore the PDC registers and enable the PDC */
- ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
- ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
- ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
- ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
- ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
- return 0;
-}
-#else /* CONFIG_PM */
-# define at32_pcm_suspend NULL
-# define at32_pcm_resume NULL
-#endif /* CONFIG_PM */
-
-
-
-struct snd_soc_platform at32_soc_platform = {
- .name = "at32-audio",
- .pcm_ops = &at32_pcm_ops,
- .pcm_new = at32_pcm_new,
- .pcm_free = at32_pcm_free_dma_buffers,
- .suspend = at32_pcm_suspend,
- .resume = at32_pcm_resume,
-};
-EXPORT_SYMBOL_GPL(at32_soc_platform);
-
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("Atmel AT32 PCM module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
deleted file mode 100644
index 2a52430417da..000000000000
--- a/sound/soc/at32/at32-pcm.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* sound/soc/at32/at32-pcm.h
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- * Geoffrey Wossum <gwossum@acm.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SOC_AT32_AT32_PCM_H
-#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
-
-#include <linux/atmel-ssc.h>
-
-
-/*
- * Registers and status bits that are required by the PCM driver
- * TODO: Is ptcr really used?
- */
-struct at32_pdc_regs {
- u32 xpr; /* PDC RX/TX pointer */
- u32 xcr; /* PDC RX/TX counter */
- u32 xnpr; /* PDC next RX/TX pointer */
- u32 xncr; /* PDC next RX/TX counter */
- u32 ptcr; /* PDC transfer control */
-};
-
-
-
-/*
- * SSC mask info
- */
-struct at32_ssc_mask {
- u32 ssc_enable; /* SSC RX/TX enable */
- u32 ssc_disable; /* SSC RX/TX disable */
- u32 ssc_endx; /* SSC ENDTX or ENDRX */
- u32 ssc_endbuf; /* SSC TXBUFF or RXBUFF */
- u32 pdc_enable; /* PDC RX/TX enable */
- u32 pdc_disable; /* PDC RX/TX disable */
-};
-
-
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation. All fields except dma_intr_handler() are initialized
- * by the interface. The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at32_pcm_dma_params {
- char *name; /* stream identifier */
- int pdc_xfer_size; /* PDC counter increment in bytes */
- struct ssc_device *ssc; /* SSC device for stream */
- struct at32_pdc_regs *pdc; /* PDC register info */
- struct at32_ssc_mask *mask; /* SSC mask info */
- struct snd_pcm_substream *substream;
- void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
-};
-
-
-
-/*
- * The AT32 ASoC platform driver
- */
-extern struct snd_soc_platform at32_soc_platform;
-
-
-
-/*
- * SSC register access (since ssc_writel() / ssc_readl() require literal name)
- */
-#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
-#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
-
-#endif /* __SOUND_SOC_AT32_AT32_PCM_H */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
deleted file mode 100644
index 4ef6492c902e..000000000000
--- a/sound/soc/at32/at32-ssc.c
+++ /dev/null
@@ -1,849 +0,0 @@
-/* sound/soc/at32/at32-ssc.c
- * ASoC platform driver for AT32 using SSC as DAI
- *
- * Copyright (C) 2008 Long Range Systems
- * Geoffrey Wossum <gwossum@acm.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.
- *
- * Note that this is basically a port of the sound/soc/at91-ssc.c to
- * the AVR32 kernel. Thanks to Frank Mandarino for that code.
- */
-
-/* #define DEBUG */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/atmel_pdc.h>
-#include <linux/atmel-ssc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-#include "at32-ssc.h"
-
-
-
-/*-------------------------------------------------------------------------*\
- * Constants
-\*-------------------------------------------------------------------------*/
-#define NUM_SSC_DEVICES 3
-
-/*
- * SSC direction masks
- */
-#define SSC_DIR_MASK_UNUSED 0
-#define SSC_DIR_MASK_PLAYBACK 1
-#define SSC_DIR_MASK_CAPTURE 2
-
-/*
- * SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
- * are expected to be used with SSC_BF
- */
-/* START bit field values */
-#define SSC_START_CONTINUOUS 0
-#define SSC_START_TX_RX 1
-#define SSC_START_LOW_RF 2
-#define SSC_START_HIGH_RF 3
-#define SSC_START_FALLING_RF 4
-#define SSC_START_RISING_RF 5
-#define SSC_START_LEVEL_RF 6
-#define SSC_START_EDGE_RF 7
-#define SSS_START_COMPARE_0 8
-
-/* CKI bit field values */
-#define SSC_CKI_FALLING 0
-#define SSC_CKI_RISING 1
-
-/* CKO bit field values */
-#define SSC_CKO_NONE 0
-#define SSC_CKO_CONTINUOUS 1
-#define SSC_CKO_TRANSFER 2
-
-/* CKS bit field values */
-#define SSC_CKS_DIV 0
-#define SSC_CKS_CLOCK 1
-#define SSC_CKS_PIN 2
-
-/* FSEDGE bit field values */
-#define SSC_FSEDGE_POSITIVE 0
-#define SSC_FSEDGE_NEGATIVE 1
-
-/* FSOS bit field values */
-#define SSC_FSOS_NONE 0
-#define SSC_FSOS_NEGATIVE 1
-#define SSC_FSOS_POSITIVE 2
-#define SSC_FSOS_LOW 3
-#define SSC_FSOS_HIGH 4
-#define SSC_FSOS_TOGGLE 5
-
-#define START_DELAY 1
-
-
-
-/*-------------------------------------------------------------------------*\
- * Module data
-\*-------------------------------------------------------------------------*/
-/*
- * SSC PDC registered required by the PCM DMA engine
- */
-static struct at32_pdc_regs pdc_tx_reg = {
- .xpr = SSC_PDC_TPR,
- .xcr = SSC_PDC_TCR,
- .xnpr = SSC_PDC_TNPR,
- .xncr = SSC_PDC_TNCR,
-};
-
-
-
-static struct at32_pdc_regs pdc_rx_reg = {
- .xpr = SSC_PDC_RPR,
- .xcr = SSC_PDC_RCR,
- .xnpr = SSC_PDC_RNPR,
- .xncr = SSC_PDC_RNCR,
-};
-
-
-
-/*
- * SSC and PDC status bits for transmit and receive
- */
-static struct at32_ssc_mask ssc_tx_mask = {
- .ssc_enable = SSC_BIT(CR_TXEN),
- .ssc_disable = SSC_BIT(CR_TXDIS),
- .ssc_endx = SSC_BIT(SR_ENDTX),
- .ssc_endbuf = SSC_BIT(SR_TXBUFE),
- .pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
- .pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
-};
-
-
-
-static struct at32_ssc_mask ssc_rx_mask = {
- .ssc_enable = SSC_BIT(CR_RXEN),
- .ssc_disable = SSC_BIT(CR_RXDIS),
- .ssc_endx = SSC_BIT(SR_ENDRX),
- .ssc_endbuf = SSC_BIT(SR_RXBUFF),
- .pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
- .pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
-};
-
-
-
-/*
- * DMA parameters for each SSC
- */
-static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
- {
- {
- .name = "SSC0 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC0 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- },
- },
- {
- {
- .name = "SSC1 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC1 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- },
- },
- {
- {
- .name = "SSC2 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC2 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- },
- },
-};
-
-
-
-static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
- {
- .name = "ssc0",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
- .dir_mask = SSC_DIR_MASK_UNUSED,
- .initialized = 0,
- },
- {
- .name = "ssc1",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
- .dir_mask = SSC_DIR_MASK_UNUSED,
- .initialized = 0,
- },
- {
- .name = "ssc2",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
- .dir_mask = SSC_DIR_MASK_UNUSED,
- .initialized = 0,
- },
-};
-
-
-
-
-/*-------------------------------------------------------------------------*\
- * ISR
-\*-------------------------------------------------------------------------*/
-/*
- * SSC interrupt handler. Passes PDC interrupts to the DMA interrupt
- * handler in the PCM driver.
- */
-static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
-{
- struct at32_ssc_info *ssc_p = dev_id;
- struct at32_pcm_dma_params *dma_params;
- u32 ssc_sr;
- u32 ssc_substream_mask;
- int i;
-
- ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
- ssc_readl(ssc_p->ssc->regs, IMR));
-
- /*
- * Loop through substreams attached to this SSC. If a DMA-related
- * interrupt occured on that substream, call the DMA interrupt
- * handler function, if one has been registered in the dma_param
- * structure by the PCM driver.
- */
- for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
- dma_params = ssc_p->dma_params[i];
-
- if ((dma_params != NULL) &&
- (dma_params->dma_intr_handler != NULL)) {
- ssc_substream_mask = (dma_params->mask->ssc_endx |
- dma_params->mask->ssc_endbuf);
- if (ssc_sr & ssc_substream_mask) {
- dma_params->dma_intr_handler(ssc_sr,
- dma_params->
- substream);
- }
- }
- }
-
-
- return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*\
- * DAI functions
-\*-------------------------------------------------------------------------*/
-/*
- * Startup. Only that one substream allowed in each direction.
- */
-static int at32_ssc_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- int dir_mask;
-
- dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
-
- spin_lock_irq(&ssc_p->lock);
- if (ssc_p->dir_mask & dir_mask) {
- spin_unlock_irq(&ssc_p->lock);
- return -EBUSY;
- }
- ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
-
- return 0;
-}
-
-
-
-/*
- * Shutdown. Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- struct at32_pcm_dma_params *dma_params;
- int dir_mask;
-
- dma_params = ssc_p->dma_params[substream->stream];
-
- if (dma_params != NULL) {
- ssc_writel(dma_params->ssc->regs, CR,
- dma_params->mask->ssc_disable);
- pr_debug("%s disabled SSC_SR=0x%08x\n",
- (substream->stream ? "receiver" : "transmit"),
- ssc_readl(ssc_p->ssc->regs, SR));
-
- dma_params->ssc = NULL;
- dma_params->substream = NULL;
- ssc_p->dma_params[substream->stream] = NULL;
- }
-
-
- dir_mask = 1 << substream->stream;
- spin_lock_irq(&ssc_p->lock);
- ssc_p->dir_mask &= ~dir_mask;
- if (!ssc_p->dir_mask) {
- /* Shutdown the SSC clock */
- pr_debug("at32-ssc: Stopping user %d clock\n",
- ssc_p->ssc->user);
- clk_disable(ssc_p->ssc->clk);
-
- if (ssc_p->initialized) {
- free_irq(ssc_p->ssc->irq, ssc_p);
- ssc_p->initialized = 0;
- }
-
- /* Reset the SSC */
- ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
- /* clear the SSC dividers */
- ssc_p->cmr_div = 0;
- ssc_p->tcmr_period = 0;
- ssc_p->rcmr_period = 0;
- }
- spin_unlock_irq(&ssc_p->lock);
-}
-
-
-
-/*
- * Set the SSC system clock rate
- */
-static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- /* TODO: What the heck do I do here? */
- return 0;
-}
-
-
-
-/*
- * Record DAI format for use by hw_params()
- */
-static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
- ssc_p->daifmt = fmt;
- return 0;
-}
-
-
-
-/*
- * Record SSC clock dividers for use in hw_params()
- */
-static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
- switch (div_id) {
- case AT32_SSC_CMR_DIV:
- /*
- * The same master clock divider is used for both
- * transmit and receive, so if a value has already
- * been set, it must match this value
- */
- if (ssc_p->cmr_div == 0)
- ssc_p->cmr_div = div;
- else if (div != ssc_p->cmr_div)
- return -EBUSY;
- break;
-
- case AT32_SSC_TCMR_PERIOD:
- ssc_p->tcmr_period = div;
- break;
-
- case AT32_SSC_RCMR_PERIOD:
- ssc_p->rcmr_period = div;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-
-
-/*
- * Configure the SSC
- */
-static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int id = rtd->dai->cpu_dai->id;
- struct at32_ssc_info *ssc_p = &ssc_info[id];
- struct at32_pcm_dma_params *dma_params;
- int channels, bits;
- u32 tfmr, rfmr, tcmr, rcmr;
- int start_event;
- int ret;
-
-
- /*
- * Currently, there is only one set of dma_params for each direction.
- * If more are added, this code will have to be changed to select
- * the proper set
- */
- dma_params = &ssc_dma_params[id][substream->stream];
- dma_params->ssc = ssc_p->ssc;
- dma_params->substream = substream;
-
- ssc_p->dma_params[substream->stream] = dma_params;
-
-
- /*
- * The cpu_dai->dma_data field is only used to communicate the
- * appropriate DMA parameters to the PCM driver's hw_params()
- * function. It should not be used for other purposes as it
- * is common to all substreams.
- */
- rtd->dai->cpu_dai->dma_data = dma_params;
-
- channels = params_channels(params);
-
-
- /*
- * Determine sample size in bits and the PDC increment
- */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- bits = 8;
- dma_params->pdc_xfer_size = 1;
- break;
-
- case SNDRV_PCM_FORMAT_S16:
- bits = 16;
- dma_params->pdc_xfer_size = 2;
- break;
-
- case SNDRV_PCM_FORMAT_S24:
- bits = 24;
- dma_params->pdc_xfer_size = 4;
- break;
-
- case SNDRV_PCM_FORMAT_S32:
- bits = 32;
- dma_params->pdc_xfer_size = 4;
- break;
-
- default:
- pr_warning("at32-ssc: Unsupported PCM format %d",
- params_format(params));
- return -EINVAL;
- }
- pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
- bits, dma_params->pdc_xfer_size, channels);
-
-
- /*
- * The SSC only supports up to 16-bit samples in I2S format, due
- * to the size of the Frame Mode Register FSLEN field.
- */
- if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
- if (bits > 16) {
- pr_warning("at32-ssc: "
- "sample size %d is too large for I2S\n",
- bits);
- return -EINVAL;
- }
-
-
- /*
- * Compute the SSC register settings
- */
- switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
- SND_SOC_DAIFMT_MASTER_MASK)) {
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * I2S format, SSC provides BCLK and LRS clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output on the SSC TK line
- */
- pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
- rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
- SSC_BF(RCMR_STTDLY, START_DELAY) |
- SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
- SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
- SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
- SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
- rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
- SSC_BF(RFMR_FSLEN, bits - 1) |
- SSC_BF(RFMR_DATNB, channels - 1) |
- SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
- tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
- SSC_BF(TCMR_STTDLY, START_DELAY) |
- SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
- SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
- SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
- SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
- tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
- SSC_BF(TFMR_FSLEN, bits - 1) |
- SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
- SSC_BF(TFMR_DATLEN, bits - 1));
- break;
-
-
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
- /*
- * I2S format, CODEC supplies BCLK and LRC clock.
- *
- * The SSC transmit clock is obtained from the BCLK signal
- * on the TK line, and the SSC receive clock is generated from
- * the transmit clock.
- *
- * For single channel data, one sample is transferred on the
- * falling edge of the LRC clock. For two channel data, one
- * sample is transferred on both edges of the LRC clock.
- */
- pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
- start_event = ((channels == 1) ?
- SSC_START_FALLING_RF : SSC_START_EDGE_RF);
-
- rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
- SSC_BF(RCMR_START, start_event) |
- SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
- SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
- SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
-
- rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
- SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
- tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
- SSC_BF(TCMR_START, start_event) |
- SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
- SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
- SSC_BF(TCMR_CKS, SSC_CKS_PIN));
-
- tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
- SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
- break;
-
-
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output on the SSC TK line
- */
- pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
- rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
- SSC_BF(RCMR_STTDLY, 1) |
- SSC_BF(RCMR_START, SSC_START_RISING_RF) |
- SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
- SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
- SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
- rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
- SSC_BF(RFMR_DATNB, channels - 1) |
- SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
- tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
- SSC_BF(TCMR_STTDLY, 1) |
- SSC_BF(TCMR_START, SSC_START_RISING_RF) |
- SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
- SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
- SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
- tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
- SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
- SSC_BF(TFMR_DATNB, channels - 1) |
- SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
- break;
-
-
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
- default:
- pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
- ssc_p->daifmt);
- return -EINVAL;
- break;
- }
- pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
- rcmr, rfmr, tcmr, tfmr);
-
-
- if (!ssc_p->initialized) {
- /* enable peripheral clock */
- pr_debug("at32-ssc: Starting clock\n");
- clk_enable(ssc_p->ssc->clk);
-
- /* Reset the SSC and its PDC registers */
- ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
- ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
-
- ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
- ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
-
- ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
- ssc_p->name, ssc_p);
- if (ret < 0) {
- pr_warning("at32-ssc: request irq failed (%d)\n", ret);
- pr_debug("at32-ssc: Stopping clock\n");
- clk_disable(ssc_p->ssc->clk);
- return ret;
- }
-
- ssc_p->initialized = 1;
- }
-
- /* Set SSC clock mode register */
- ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
-
- /* set receive clock mode and format */
- ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
- ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
-
- /* set transmit clock mode and format */
- ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
- ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
-
- pr_debug("at32-ssc: SSC initialized\n");
- return 0;
-}
-
-
-
-static int at32_ssc_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- struct at32_pcm_dma_params *dma_params;
-
- dma_params = ssc_p->dma_params[substream->stream];
-
- ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
-
- return 0;
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_ssc_suspend(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
-{
- struct at32_ssc_info *ssc_p;
-
- if (!cpu_dai->active)
- return 0;
-
- ssc_p = &ssc_info[cpu_dai->id];
-
- /* Save the status register before disabling transmit and receive */
- ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
- ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
-
- /* Save the current interrupt mask, then disable unmasked interrupts */
- ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
- ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
-
- ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
- ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
- ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
- ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
- ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
-
- return 0;
-}
-
-
-
-static int at32_ssc_resume(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
-{
- struct at32_ssc_info *ssc_p;
- u32 cr;
-
- if (!cpu_dai->active)
- return 0;
-
- ssc_p = &ssc_info[cpu_dai->id];
-
- /* restore SSC register settings */
- ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
- ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
- ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
- ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
- ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
-
- /* re-enable interrupts */
- ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
-
- /* Re-enable recieve and transmit as appropriate */
- cr = 0;
- cr |=
- (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
- cr |=
- (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
- ssc_writel(ssc_p->ssc->regs, CR, cr);
-
- return 0;
-}
-#else /* CONFIG_PM */
-# define at32_ssc_suspend NULL
-# define at32_ssc_resume NULL
-#endif /* CONFIG_PM */
-
-
-#define AT32_SSC_RATES \
- (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-
-#define AT32_SSC_FORMATS \
- (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16 | \
- SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
-
-
-struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
- {
- .name = "at32-ssc0",
- .id = 0,
- .type = SND_SOC_DAI_PCM,
- .suspend = at32_ssc_suspend,
- .resume = at32_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .ops = {
- .startup = at32_ssc_startup,
- .shutdown = at32_ssc_shutdown,
- .prepare = at32_ssc_prepare,
- .hw_params = at32_ssc_hw_params,
- },
- .dai_ops = {
- .set_sysclk = at32_ssc_set_dai_sysclk,
- .set_fmt = at32_ssc_set_dai_fmt,
- .set_clkdiv = at32_ssc_set_dai_clkdiv,
- },
- .private_data = &ssc_info[0],
- },
- {
- .name = "at32-ssc1",
- .id = 1,
- .type = SND_SOC_DAI_PCM,
- .suspend = at32_ssc_suspend,
- .resume = at32_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .ops = {
- .startup = at32_ssc_startup,
- .shutdown = at32_ssc_shutdown,
- .prepare = at32_ssc_prepare,
- .hw_params = at32_ssc_hw_params,
- },
- .dai_ops = {
- .set_sysclk = at32_ssc_set_dai_sysclk,
- .set_fmt = at32_ssc_set_dai_fmt,
- .set_clkdiv = at32_ssc_set_dai_clkdiv,
- },
- .private_data = &ssc_info[1],
- },
- {
- .name = "at32-ssc2",
- .id = 2,
- .type = SND_SOC_DAI_PCM,
- .suspend = at32_ssc_suspend,
- .resume = at32_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT32_SSC_RATES,
- .formats = AT32_SSC_FORMATS,
- },
- .ops = {
- .startup = at32_ssc_startup,
- .shutdown = at32_ssc_shutdown,
- .prepare = at32_ssc_prepare,
- .hw_params = at32_ssc_hw_params,
- },
- .dai_ops = {
- .set_sysclk = at32_ssc_set_dai_sysclk,
- .set_fmt = at32_ssc_set_dai_fmt,
- .set_clkdiv = at32_ssc_set_dai_clkdiv,
- },
- .private_data = &ssc_info[2],
- },
-};
-EXPORT_SYMBOL_GPL(at32_ssc_dai);
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
deleted file mode 100644
index 3c052dbbe460..000000000000
--- a/sound/soc/at32/at32-ssc.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* sound/soc/at32/at32-ssc.h
- * ASoC SSC interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- * Geoffrey Wossum <gwossum@acm.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SOC_AT32_AT32_SSC_H
-#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
-
-#include <linux/types.h>
-#include <linux/atmel-ssc.h>
-
-#include "at32-pcm.h"
-
-
-
-struct at32_ssc_state {
- u32 ssc_cmr;
- u32 ssc_rcmr;
- u32 ssc_rfmr;
- u32 ssc_tcmr;
- u32 ssc_tfmr;
- u32 ssc_sr;
- u32 ssc_imr;
-};
-
-
-
-struct at32_ssc_info {
- char *name;
- struct ssc_device *ssc;
- spinlock_t lock; /* lock for dir_mask */
- unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
- unsigned short initialized; /* true if SSC has been initialized */
- unsigned short daifmt;
- unsigned short cmr_div;
- unsigned short tcmr_period;
- unsigned short rcmr_period;
- struct at32_pcm_dma_params *dma_params[2];
- struct at32_ssc_state ssc_state;
-};
-
-
-/* SSC divider ids */
-#define AT32_SSC_CMR_DIV 0 /* MCK divider for BCLK */
-#define AT32_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
-#define AT32_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
-
-
-extern struct snd_soc_dai at32_ssc_dai[];
-
-
-
-#endif /* __SOUND_SOC_AT32_AT32_SSC_H */
diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c
deleted file mode 100644
index b1966e4dfcd3..000000000000
--- a/sound/soc/at32/playpaq_wm8510.c
+++ /dev/null
@@ -1,513 +0,0 @@
-/* sound/soc/at32/playpaq_wm8510.c
- * ASoC machine driver for PlayPaq using WM8510 codec
- *
- * Copyright (C) 2008 Long Range Systems
- * Geoffrey Wossum <gwossum@acm.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.
- *
- * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
- *
- * NOTE: If you don't have the AT32 enhanced portmux configured (which
- * isn't currently in the mainline or Atmel patched kernel), you will
- * need to set the MCLK pin (PA30) to peripheral A in your board initialization
- * code. Something like:
- * at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
- *
- */
-
-/* #define DEBUG */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <mach/at32ap700x.h>
-#include <mach/portmux.h>
-
-#include "../codecs/wm8510.h"
-#include "at32-pcm.h"
-#include "at32-ssc.h"
-
-
-/*-------------------------------------------------------------------------*\
- * constants
-\*-------------------------------------------------------------------------*/
-#define MCLK_PIN GPIO_PIN_PA(30)
-#define MCLK_PERIPH GPIO_PERIPH_A
-
-
-/*-------------------------------------------------------------------------*\
- * data types
-\*-------------------------------------------------------------------------*/
-/* SSC clocking data */
-struct ssc_clock_data {
- /* CMR div */
- unsigned int cmr_div;
-
- /* Frame period (as needed by xCMR.PERIOD) */
- unsigned int period;
-
- /* The SSC clock rate these settings where calculated for */
- unsigned long ssc_rate;
-};
-
-
-/*-------------------------------------------------------------------------*\
- * module data
-\*-------------------------------------------------------------------------*/
-static struct clk *_gclk0;
-static struct clk *_pll0;
-
-#define CODEC_CLK (_gclk0)
-
-
-/*-------------------------------------------------------------------------*\
- * Sound SOC operations
-\*-------------------------------------------------------------------------*/
-#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
-static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct at32_ssc_info *ssc_p = cpu_dai->private_data;
- struct ssc_device *ssc = ssc_p->ssc;
- struct ssc_clock_data cd;
- unsigned int rate, width_bits, channels;
- unsigned int bitrate, ssc_div;
- unsigned actual_rate;
-
-
- /*
- * Figure out required bitrate
- */
- rate = params_rate(params);
- channels = params_channels(params);
- width_bits = snd_pcm_format_physical_width(params_format(params));
- bitrate = rate * width_bits * channels;
-
-
- /*
- * Figure out required SSC divider and period for required bitrate
- */
- cd.ssc_rate = clk_get_rate(ssc->clk);
- ssc_div = cd.ssc_rate / bitrate;
- cd.cmr_div = ssc_div / 2;
- if (ssc_div & 1) {
- /* round cmr_div up */
- cd.cmr_div++;
- }
- cd.period = width_bits - 1;
-
-
- /*
- * Find actual rate, compare to requested rate
- */
- actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
- pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
- rate, actual_rate);
-
-
- return cd;
-}
-#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
-
-
-
-static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- struct at32_ssc_info *ssc_p = cpu_dai->private_data;
- struct ssc_device *ssc = ssc_p->ssc;
- unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
- int ret;
-
-
- /* Due to difficulties with getting the correct clocks from the AT32's
- * PLL0, we're going to let the CODEC be in charge of all the clocks
- */
-#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
- const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
-#else
- struct ssc_clock_data cd;
- const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
-#endif
-
- if (ssc == NULL) {
- pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
- return -EINVAL;
- }
-
-
- /*
- * Figure out PLL and BCLK dividers for WM8510
- */
- switch (params_rate(params)) {
- case 48000:
- pll_out = 12288000;
- mclk_div = WM8510_MCLKDIV_1;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 44100:
- pll_out = 11289600;
- mclk_div = WM8510_MCLKDIV_1;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 22050:
- pll_out = 11289600;
- mclk_div = WM8510_MCLKDIV_2;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 16000:
- pll_out = 12288000;
- mclk_div = WM8510_MCLKDIV_3;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 11025:
- pll_out = 11289600;
- mclk_div = WM8510_MCLKDIV_4;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- case 8000:
- pll_out = 12288000;
- mclk_div = WM8510_MCLKDIV_6;
- bclk = WM8510_BCLKDIV_8;
- break;
-
- default:
- pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
- params_rate(params));
- return -EINVAL;
- }
-
-
- /*
- * set CPU and CODEC DAI configuration
- */
- ret = snd_soc_dai_set_fmt(codec_dai, fmt);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: "
- "Failed to set CODEC DAI format (%d)\n",
- ret);
- return ret;
- }
- ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: "
- "Failed to set CPU DAI format (%d)\n",
- ret);
- return ret;
- }
-
-
- /*
- * Set CPU clock configuration
- */
-#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
- cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
- pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
- cd.cmr_div, cd.period);
- ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
- ret);
- return ret;
- }
- ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
- cd.period);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: "
- "Failed to set CPU transmit period (%d)\n",
- ret);
- return ret;
- }
-#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
-
-
- /*
- * Set CODEC clock configuration
- */
- pr_debug("playpaq_wm8510: "
- "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
- clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
-
-
-#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
- if (ret < 0) {
- pr_warning
- ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
- ret);
- return ret;
- }
-#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
-
-
- ret = snd_soc_dai_set_pll(codec_dai, 0,
- clk_get_rate(CODEC_CLK), pll_out);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
- ret);
- return ret;
- }
-
-
- ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
- if (ret < 0) {
- pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
- ret);
- return ret;
- }
-
-
- return 0;
-}
-
-
-
-static struct snd_soc_ops playpaq_wm8510_ops = {
- .hw_params = playpaq_wm8510_hw_params,
-};
-
-
-
-static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-
-
-static const struct snd_soc_dapm_route intercon[] = {
- /* speaker connected to SPKOUT */
- {"Ext Spk", NULL, "SPKOUTP"},
- {"Ext Spk", NULL, "SPKOUTN"},
-
- {"Mic Bias", NULL, "Int Mic"},
- {"MICN", NULL, "Mic Bias"},
- {"MICP", NULL, "Mic Bias"},
-};
-
-
-
-static int playpaq_wm8510_init(struct snd_soc_codec *codec)
-{
- int i;
-
- /*
- * Add DAPM widgets
- */
- for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
- snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
-
-
-
- /*
- * Setup audio path interconnects
- */
- snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
-
-
-
- /* always connected pins */
- snd_soc_dapm_enable_pin(codec, "Int Mic");
- snd_soc_dapm_enable_pin(codec, "Ext Spk");
- snd_soc_dapm_sync(codec);
-
-
-
- /* Make CSB show PLL rate */
- snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
- WM8510_OPCLKDIV_1 | 4);
-
- return 0;
-}
-
-
-
-static struct snd_soc_dai_link playpaq_wm8510_dai = {
- .name = "WM8510",
- .stream_name = "WM8510 PCM",
- .cpu_dai = &at32_ssc_dai[0],
- .codec_dai = &wm8510_dai,
- .init = playpaq_wm8510_init,
- .ops = &playpaq_wm8510_ops,
-};
-
-
-
-static struct snd_soc_machine snd_soc_machine_playpaq = {
- .name = "LRS_PlayPaq_WM8510",
- .dai_link = &playpaq_wm8510_dai,
- .num_links = 1,
-};
-
-
-
-static struct wm8510_setup_data playpaq_wm8510_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1a,
-};
-
-
-
-static struct snd_soc_device playpaq_wm8510_snd_devdata = {
- .machine = &snd_soc_machine_playpaq,
- .platform = &at32_soc_platform,
- .codec_dev = &soc_codec_dev_wm8510,
- .codec_data = &playpaq_wm8510_setup,
-};
-
-static struct platform_device *playpaq_snd_device;
-
-
-static int __init playpaq_asoc_init(void)
-{
- int ret = 0;
- struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
- struct ssc_device *ssc = NULL;
-
-
- /*
- * Request SSC device
- */
- ssc = ssc_request(0);
- if (IS_ERR(ssc)) {
- ret = PTR_ERR(ssc);
- goto err_ssc;
- }
- ssc_p->ssc = ssc;
-
-
- /*
- * Configure MCLK for WM8510
- */
- _gclk0 = clk_get(NULL, "gclk0");
- if (IS_ERR(_gclk0)) {
- _gclk0 = NULL;
- goto err_gclk0;
- }
- _pll0 = clk_get(NULL, "pll0");
- if (IS_ERR(_pll0)) {
- _pll0 = NULL;
- goto err_pll0;
- }
- if (clk_set_parent(_gclk0, _pll0)) {
- pr_warning("snd-soc-playpaq: "
- "Failed to set PLL0 as parent for DAC clock\n");
- goto err_set_clk;
- }
- clk_set_rate(CODEC_CLK, 12000000);
- clk_enable(CODEC_CLK);
-
-#if defined CONFIG_AT32_ENHANCED_PORTMUX
- at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
-#endif
-
-
- /*
- * Create and register platform device
- */
- playpaq_snd_device = platform_device_alloc("soc-audio", 0);
- if (playpaq_snd_device == NULL) {
- ret = -ENOMEM;
- goto err_device_alloc;
- }
-
- platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
- playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
-
- ret = platform_device_add(playpaq_snd_device);
- if (ret) {
- pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
- ret);
- goto err_device_add;
- }
-
- return 0;
-
-
-err_device_add:
- if (playpaq_snd_device != NULL) {
- platform_device_put(playpaq_snd_device);
- playpaq_snd_device = NULL;
- }
-err_device_alloc:
-err_set_clk:
- if (_pll0 != NULL) {
- clk_put(_pll0);
- _pll0 = NULL;
- }
-err_pll0:
- if (_gclk0 != NULL) {
- clk_put(_gclk0);
- _gclk0 = NULL;
- }
-err_gclk0:
- ssc_free(ssc);
-err_ssc:
- return ret;
-}
-
-
-static void __exit playpaq_asoc_exit(void)
-{
- struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
- struct ssc_device *ssc;
-
- if (ssc_p != NULL) {
- ssc = ssc_p->ssc;
- if (ssc != NULL)
- ssc_free(ssc);
- ssc_p->ssc = NULL;
- }
-
- if (_gclk0 != NULL) {
- clk_put(_gclk0);
- _gclk0 = NULL;
- }
- if (_pll0 != NULL) {
- clk_put(_pll0);
- _pll0 = NULL;
- }
-
-#if defined CONFIG_AT32_ENHANCED_PORTMUX
- at32_free_pin(MCLK_PIN);
-#endif
-
- platform_device_unregister(playpaq_snd_device);
- playpaq_snd_device = NULL;
-}
-
-module_init(playpaq_asoc_init);
-module_exit(playpaq_asoc_exit);
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
deleted file mode 100644
index 85a883299c2e..000000000000
--- a/sound/soc/at91/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-config SND_AT91_SOC
- tristate "SoC Audio for the Atmel AT91 System-on-Chip"
- depends on ARCH_AT91
- help
- Say Y or M if you want to add support for codecs attached to
- the AT91 SSC interface. You will also need
- to select the audio interfaces to support below.
-
-config SND_AT91_SOC_SSC
- tristate
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
deleted file mode 100644
index b817f11df286..000000000000
--- a/sound/soc/at91/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# AT91 Platform Support
-snd-soc-at91-objs := at91-pcm.o
-snd-soc-at91-ssc-objs := at91-ssc.o
-
-obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
-obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
deleted file mode 100644
index 7ab48bd25e4c..000000000000
--- a/sound/soc/at91/at91-pcm.c
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- * Created: Mar 3, 2006
- *
- * Based on pxa2xx-pcm.c by:
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * Copyright: (C) 2004 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/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-
-#if 0
-#define DBG(x...) printk(KERN_INFO "at91-pcm: " x)
-#else
-#define DBG(x...)
-#endif
-
-static const struct snd_pcm_hardware at91_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 2,
- .periods_max = 1024,
- .buffer_bytes_max = 32 * 1024,
-};
-
-struct at91_runtime_data {
- struct at91_pcm_dma_params *params;
- dma_addr_t dma_buffer; /* physical address of dma buffer */
- dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
- size_t period_size;
- dma_addr_t period_ptr; /* physical address of next period */
- u32 pdc_xpr_save; /* PDC register save */
- u32 pdc_xcr_save;
- u32 pdc_xnpr_save;
- u32 pdc_xncr_save;
-};
-
-static void at91_pcm_dma_irq(u32 ssc_sr,
- struct snd_pcm_substream *substream)
-{
- struct at91_runtime_data *prtd = substream->runtime->private_data;
- struct at91_pcm_dma_params *params = prtd->params;
- static int count = 0;
-
- count++;
-
- if (ssc_sr & params->mask->ssc_endbuf) {
-
- printk(KERN_WARNING
- "at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
- substream->stream == SNDRV_PCM_STREAM_PLAYBACK
- ? "underrun" : "overrun",
- params->name, ssc_sr, count);
-
- /* re-start the PDC */
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end) {
- prtd->period_ptr = prtd->dma_buffer;
- }
-
- at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
- at91_ssc_write(params->ssc_base + params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
-
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
- }
-
- if (ssc_sr & params->mask->ssc_endx) {
-
- /* Load the PDC next pointer and counter registers */
- prtd->period_ptr += prtd->period_size;
- if (prtd->period_ptr >= prtd->dma_buffer_end) {
- prtd->period_ptr = prtd->dma_buffer;
- }
- at91_ssc_write(params->ssc_base + params->pdc->xnpr,
- prtd->period_ptr);
- at91_ssc_write(params->ssc_base + params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
- }
-
- snd_pcm_period_elapsed(substream);
-}
-
-static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at91_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- /* this may get called several times by oss emulation
- * with different params */
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->params = rtd->dai->cpu_dai->dma_data;
- prtd->params->dma_intr_handler = at91_pcm_dma_irq;
-
- prtd->dma_buffer = runtime->dma_addr;
- prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
- prtd->period_size = params_period_bytes(params);
-
- DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
- prtd->params->name, runtime->dma_bytes, prtd->period_size);
- return 0;
-}
-
-static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct at91_runtime_data *prtd = substream->runtime->private_data;
- struct at91_pcm_dma_params *params = prtd->params;
-
- if (params != NULL) {
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
- prtd->params->dma_intr_handler = NULL;
- }
-
- return 0;
-}
-
-static int at91_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct at91_runtime_data *prtd = substream->runtime->private_data;
- struct at91_pcm_dma_params *params = prtd->params;
-
- at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
-
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
- return 0;
-}
-
-static int at91_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct at91_runtime_data *prtd = substream->runtime->private_data;
- struct at91_pcm_dma_params *params = prtd->params;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- prtd->period_ptr = prtd->dma_buffer;
-
- at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
- at91_ssc_write(params->ssc_base + params->pdc->xcr,
- prtd->period_size / params->pdc_xfer_size);
-
- prtd->period_ptr += prtd->period_size;
- at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
- at91_ssc_write(params->ssc_base + params->pdc->xncr,
- prtd->period_size / params->pdc_xfer_size);
-
- DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
- (unsigned long) prtd->period_ptr,
- at91_ssc_read(params->ssc_base + params->pdc->xpr),
- at91_ssc_read(params->ssc_base + params->pdc->xcr),
- at91_ssc_read(params->ssc_base + params->pdc->xnpr),
- at91_ssc_read(params->ssc_base + params->pdc->xncr));
-
- at91_ssc_write(params->ssc_base + AT91_SSC_IER,
- params->mask->ssc_endx | params->mask->ssc_endbuf);
-
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR,
- params->mask->pdc_enable);
-
- DBG("sr=%lx imr=%lx\n",
- at91_ssc_read(params->ssc_base + AT91_SSC_SR),
- at91_ssc_read(params->ssc_base + AT91_SSC_IMR));
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static snd_pcm_uframes_t at91_pcm_pointer(
- struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at91_runtime_data *prtd = runtime->private_data;
- struct at91_pcm_dma_params *params = prtd->params;
- dma_addr_t ptr;
- snd_pcm_uframes_t x;
-
- ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
- x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
-}
-
-static int at91_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct at91_runtime_data *prtd;
- int ret = 0;
-
- snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
-
- /* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- goto out;
-
- prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
- if (prtd == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- runtime->private_data = prtd;
-
- out:
- return ret;
-}
-
-static int at91_pcm_close(struct snd_pcm_substream *substream)
-{
- struct at91_runtime_data *prtd = substream->runtime->private_data;
-
- kfree(prtd);
- return 0;
-}
-
-static int at91_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-struct snd_pcm_ops at91_pcm_ops = {
- .open = at91_pcm_open,
- .close = at91_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = at91_pcm_hw_params,
- .hw_free = at91_pcm_hw_free,
- .prepare = at91_pcm_prepare,
- .trigger = at91_pcm_trigger,
- .pointer = at91_pcm_pointer,
- .mmap = at91_pcm_mmap,
-};
-
-static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
- int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = at91_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
-
- DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- (void *) buf->area,
- (void *) buf->addr,
- size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
-
-static u64 at91_pcm_dmamask = 0xffffffff;
-
-static int at91_pcm_new(struct snd_card *card,
- struct snd_soc_dai *dai, struct snd_pcm *pcm)
-{
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &at91_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (dai->playback.channels_min) {
- ret = at91_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto out;
- }
-
- if (dai->capture.channels_min) {
- ret = at91_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto out;
- }
- out:
- return ret;
-}
-
-static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
-}
-
-#ifdef CONFIG_PM
-static int at91_pcm_suspend(struct platform_device *pdev,
- struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct at91_runtime_data *prtd;
- struct at91_pcm_dma_params *params;
-
- if (!runtime)
- return 0;
-
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* disable the PDC and save the PDC registers */
-
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
- prtd->pdc_xpr_save = at91_ssc_read(params->ssc_base + params->pdc->xpr);
- prtd->pdc_xcr_save = at91_ssc_read(params->ssc_base + params->pdc->xcr);
- prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
- prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
-
- return 0;
-}
-
-static int at91_pcm_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
-{
- struct snd_pcm_runtime *runtime = dai->runtime;
- struct at91_runtime_data *prtd;
- struct at91_pcm_dma_params *params;
-
- if (!runtime)
- return 0;
-
- prtd = runtime->private_data;
- params = prtd->params;
-
- /* restore the PDC registers and enable the PDC */
- at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->pdc_xpr_save);
- at91_ssc_write(params->ssc_base + params->pdc->xcr, prtd->pdc_xcr_save);
- at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
- at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
-
- at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
- return 0;
-}
-#else
-#define at91_pcm_suspend NULL
-#define at91_pcm_resume NULL
-#endif
-
-struct snd_soc_platform at91_soc_platform = {
- .name = "at91-audio",
- .pcm_ops = &at91_pcm_ops,
- .pcm_new = at91_pcm_new,
- .pcm_free = at91_pcm_free_dma_buffers,
- .suspend = at91_pcm_suspend,
- .resume = at91_pcm_resume,
-};
-
-EXPORT_SYMBOL_GPL(at91_soc_platform);
-
-MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
-MODULE_DESCRIPTION("Atmel AT91 PCM module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h
deleted file mode 100644
index e5aada2cb102..000000000000
--- a/sound/soc/at91/at91-pcm.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- * Created: Mar 3, 2006
- *
- * Based on pxa2xx-pcm.h by:
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * 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.
- */
-
-#ifndef _AT91_PCM_H
-#define _AT91_PCM_H
-
-#include <mach/hardware.h>
-
-struct at91_ssc_periph {
- void __iomem *base;
- u32 pid;
-};
-
-/*
- * Registers and status bits that are required by the PCM driver.
- */
-struct at91_pdc_regs {
- unsigned int xpr; /* PDC recv/trans pointer */
- unsigned int xcr; /* PDC recv/trans counter */
- unsigned int xnpr; /* PDC next recv/trans pointer */
- unsigned int xncr; /* PDC next recv/trans counter */
- unsigned int ptcr; /* PDC transfer control */
-};
-
-struct at91_ssc_mask {
- u32 ssc_enable; /* SSC recv/trans enable */
- u32 ssc_disable; /* SSC recv/trans disable */
- u32 ssc_endx; /* SSC ENDTX or ENDRX */
- u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
- u32 pdc_enable; /* PDC recv/trans enable */
- u32 pdc_disable; /* PDC recv/trans disable */
-};
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation. All fields except dma_intr_handler() are initialized
- * by the interface. The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at91_pcm_dma_params {
- char *name; /* stream identifier */
- int pdc_xfer_size; /* PDC counter increment in bytes */
- void __iomem *ssc_base; /* SSC base address */
- struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */
- struct at91_ssc_mask *mask;/* SSC & PDC status bits */
- struct snd_pcm_substream *substream;
- void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
-};
-
-extern struct snd_soc_platform at91_soc_platform;
-
-#define at91_ssc_read(a) ((unsigned long) __raw_readl(a))
-#define at91_ssc_write(a,v) __raw_writel((v),(a))
-
-#endif /* _AT91_PCM_H */
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
deleted file mode 100644
index 1b61cc461261..000000000000
--- a/sound/soc/at91/at91-ssc.c
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * at91-ssc.c -- ALSA SoC AT91 SSC Audio Layer Platform driver
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- *
- * Based on pxa2xx Platform drivers by
- * Liam Girdwood <lrg@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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_pmc.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-#include "at91-ssc.h"
-
-#if 0
-#define DBG(x...) printk(KERN_DEBUG "at91-ssc:" x)
-#else
-#define DBG(x...)
-#endif
-
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES 1
-#else
-#define NUM_SSC_DEVICES 3
-#endif
-
-
-/*
- * SSC PDC registers required by the PCM DMA engine.
- */
-static struct at91_pdc_regs pdc_tx_reg = {
- .xpr = ATMEL_PDC_TPR,
- .xcr = ATMEL_PDC_TCR,
- .xnpr = ATMEL_PDC_TNPR,
- .xncr = ATMEL_PDC_TNCR,
-};
-
-static struct at91_pdc_regs pdc_rx_reg = {
- .xpr = ATMEL_PDC_RPR,
- .xcr = ATMEL_PDC_RCR,
- .xnpr = ATMEL_PDC_RNPR,
- .xncr = ATMEL_PDC_RNCR,
-};
-
-/*
- * SSC & PDC status bits for transmit and receive.
- */
-static struct at91_ssc_mask ssc_tx_mask = {
- .ssc_enable = AT91_SSC_TXEN,
- .ssc_disable = AT91_SSC_TXDIS,
- .ssc_endx = AT91_SSC_ENDTX,
- .ssc_endbuf = AT91_SSC_TXBUFE,
- .pdc_enable = ATMEL_PDC_TXTEN,
- .pdc_disable = ATMEL_PDC_TXTDIS,
-};
-
-static struct at91_ssc_mask ssc_rx_mask = {
- .ssc_enable = AT91_SSC_RXEN,
- .ssc_disable = AT91_SSC_RXDIS,
- .ssc_endx = AT91_SSC_ENDRX,
- .ssc_endbuf = AT91_SSC_RXBUFF,
- .pdc_enable = ATMEL_PDC_RXTEN,
- .pdc_disable = ATMEL_PDC_RXTDIS,
-};
-
-
-/*
- * DMA parameters.
- */
-static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
- {{
- .name = "SSC0 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC0 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- }},
-#if NUM_SSC_DEVICES == 3
- {{
- .name = "SSC1 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC1 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- }},
- {{
- .name = "SSC2 PCM out",
- .pdc = &pdc_tx_reg,
- .mask = &ssc_tx_mask,
- },
- {
- .name = "SSC2 PCM in",
- .pdc = &pdc_rx_reg,
- .mask = &ssc_rx_mask,
- }},
-#endif
-};
-
-struct at91_ssc_state {
- u32 ssc_cmr;
- u32 ssc_rcmr;
- u32 ssc_rfmr;
- u32 ssc_tcmr;
- u32 ssc_tfmr;
- u32 ssc_sr;
- u32 ssc_imr;
-};
-
-static struct at91_ssc_info {
- char *name;
- struct at91_ssc_periph ssc;
- spinlock_t lock; /* lock for dir_mask */
- unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
- unsigned short initialized; /* 1=SSC has been initialized */
- unsigned short daifmt;
- unsigned short cmr_div;
- unsigned short tcmr_period;
- unsigned short rcmr_period;
- struct at91_pcm_dma_params *dma_params[2];
- struct at91_ssc_state ssc_state;
-
-} ssc_info[NUM_SSC_DEVICES] = {
- {
- .name = "ssc0",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
- .dir_mask = 0,
- .initialized = 0,
- },
-#if NUM_SSC_DEVICES == 3
- {
- .name = "ssc1",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
- .dir_mask = 0,
- .initialized = 0,
- },
- {
- .name = "ssc2",
- .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
- .dir_mask = 0,
- .initialized = 0,
- },
-#endif
-};
-
-static unsigned int at91_ssc_sysclk;
-
-/*
- * SSC interrupt handler. Passes PDC interrupts to the DMA
- * interrupt handler in the PCM driver.
- */
-static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id)
-{
- struct at91_ssc_info *ssc_p = dev_id;
- struct at91_pcm_dma_params *dma_params;
- u32 ssc_sr;
- int i;
-
- ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)
- & at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
-
- /*
- * Loop through the substreams attached to this SSC. If
- * a DMA-related interrupt occurred on that substream, call
- * the DMA interrupt handler function, if one has been
- * registered in the dma_params structure by the PCM driver.
- */
- for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
- dma_params = ssc_p->dma_params[i];
-
- if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
- (ssc_sr &
- (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
-
- dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * Startup. Only that one substream allowed in each direction.
- */
-static int at91_ssc_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- int dir_mask;
-
- DBG("ssc_startup: SSC_SR=0x%08lx\n",
- at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
- dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
-
- spin_lock_irq(&ssc_p->lock);
- if (ssc_p->dir_mask & dir_mask) {
- spin_unlock_irq(&ssc_p->lock);
- return -EBUSY;
- }
- ssc_p->dir_mask |= dir_mask;
- spin_unlock_irq(&ssc_p->lock);
-
- return 0;
-}
-
-/*
- * Shutdown. Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- struct at91_pcm_dma_params *dma_params;
- int dir, dir_mask;
-
- dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
- dma_params = ssc_p->dma_params[dir];
-
- if (dma_params != NULL) {
- at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
- dma_params->mask->ssc_disable);
- DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
- at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
-
- dma_params->ssc_base = NULL;
- dma_params->substream = NULL;
- ssc_p->dma_params[dir] = NULL;
- }
-
- dir_mask = 1 << dir;
-
- spin_lock_irq(&ssc_p->lock);
- ssc_p->dir_mask &= ~dir_mask;
- if (!ssc_p->dir_mask) {
- /* Shutdown the SSC clock. */
- DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
- at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
-
- if (ssc_p->initialized) {
- free_irq(ssc_p->ssc.pid, ssc_p);
- ssc_p->initialized = 0;
- }
-
- /* Reset the SSC */
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
- /* Clear the SSC dividers */
- ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
- }
- spin_unlock_irq(&ssc_p->lock);
-}
-
-/*
- * Record the SSC system clock rate.
- */
-static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq, int dir)
-{
- /*
- * The only clock supplied to the SSC is the AT91 master clock,
- * which is only used if the SSC is generating BCLK and/or
- * LRC clocks.
- */
- switch (clk_id) {
- case AT91_SYSCLK_MCK:
- at91_ssc_sysclk = freq;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * Record the DAI format for use in hw_params().
- */
-static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
- ssc_p->daifmt = fmt;
- return 0;
-}
-
-/*
- * Record SSC clock dividers for use in hw_params().
- */
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
- switch (div_id) {
- case AT91SSC_CMR_DIV:
- /*
- * The same master clock divider is used for both
- * transmit and receive, so if a value has already
- * been set, it must match this value.
- */
- if (ssc_p->cmr_div == 0)
- ssc_p->cmr_div = div;
- else
- if (div != ssc_p->cmr_div)
- return -EBUSY;
- break;
-
- case AT91SSC_TCMR_PERIOD:
- ssc_p->tcmr_period = div;
- break;
-
- case AT91SSC_RCMR_PERIOD:
- ssc_p->rcmr_period = div;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * Configure the SSC.
- */
-static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int id = rtd->dai->cpu_dai->id;
- struct at91_ssc_info *ssc_p = &ssc_info[id];
- struct at91_pcm_dma_params *dma_params;
- int dir, channels, bits;
- u32 tfmr, rfmr, tcmr, rcmr;
- int start_event;
- int ret;
-
- /*
- * Currently, there is only one set of dma params for
- * each direction. If more are added, this code will
- * have to be changed to select the proper set.
- */
- dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-
- dma_params = &ssc_dma_params[id][dir];
- dma_params->ssc_base = ssc_p->ssc.base;
- dma_params->substream = substream;
-
- ssc_p->dma_params[dir] = dma_params;
-
- /*
- * The cpu_dai->dma_data field is only used to communicate the
- * appropriate DMA parameters to the pcm driver hw_params()
- * function. It should not be used for other purposes
- * as it is common to all substreams.
- */
- rtd->dai->cpu_dai->dma_data = dma_params;
-
- channels = params_channels(params);
-
- /*
- * Determine sample size in bits and the PDC increment.
- */
- switch(params_format(params)) {
- case SNDRV_PCM_FORMAT_S8:
- bits = 8;
- dma_params->pdc_xfer_size = 1;
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- bits = 16;
- dma_params->pdc_xfer_size = 2;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- bits = 24;
- dma_params->pdc_xfer_size = 4;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- bits = 32;
- dma_params->pdc_xfer_size = 4;
- break;
- default:
- printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
- return -EINVAL;
- }
-
- /*
- * The SSC only supports up to 16-bit samples in I2S format, due
- * to the size of the Frame Mode Register FSLEN field.
- */
- if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
- && bits > 16) {
- printk(KERN_WARNING
- "at91-ssc: sample size %d is too large for I2S\n", bits);
- return -EINVAL;
- }
-
- /*
- * Compute SSC register settings.
- */
- switch (ssc_p->daifmt
- & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
-
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * I2S format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output on the SSC TK line.
- */
- rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
- | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
-
- rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
- | (((bits - 1) << 16) & AT91_SSC_FSLEN)
- | (((channels - 1) << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_LOOP)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
-
- tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
- | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
-
- tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( 0 << 23) & AT91_SSC_FSDEN)
- | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
- | (((bits - 1) << 16) & AT91_SSC_FSLEN)
- | (((channels - 1) << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_DATDEF)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
- break;
-
- case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
- /*
- * I2S format, CODEC supplies BCLK and LRC clocks.
- *
- * The SSC transmit clock is obtained from the BCLK signal on
- * on the TK line, and the SSC receive clock is generated from the
- * transmit clock.
- *
- * For single channel data, one sample is transferred on the falling
- * edge of the LRC clock. For two channel data, one sample is
- * transferred on both edges of the LRC clock.
- */
- start_event = channels == 1
- ? AT91_SSC_START_FALLING_RF
- : AT91_SSC_START_EDGE_RF;
-
- rcmr = (( 0 << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( start_event ) & AT91_SSC_START)
- | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS);
-
- rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
- | (( 0 << 16) & AT91_SSC_FSLEN)
- | (( 0 << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_LOOP)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
-
- tcmr = (( 0 << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( start_event ) & AT91_SSC_START)
- | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_PIN ) & AT91_SSC_CKS);
-
- tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( 0 << 23) & AT91_SSC_FSDEN)
- | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
- | (( 0 << 16) & AT91_SSC_FSLEN)
- | (( 0 << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_DATDEF)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
- break;
-
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
- /*
- * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
- *
- * The SSC transmit and receive clocks are generated from the
- * MCK divider, and the BCLK signal is output on the SSC TK line.
- */
- rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
- | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
-
- rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
- | (( 0 << 16) & AT91_SSC_FSLEN)
- | (((channels - 1) << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_LOOP)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
-
- tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
- | (( 1 << 16) & AT91_SSC_STTDLY)
- | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
- | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
- | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
- | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
-
- tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
- | (( 0 << 23) & AT91_SSC_FSDEN)
- | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
- | (( 0 << 16) & AT91_SSC_FSLEN)
- | (((channels - 1) << 8) & AT91_SSC_DATNB)
- | (( 1 << 7) & AT91_SSC_MSBF)
- | (( 0 << 5) & AT91_SSC_DATDEF)
- | (((bits - 1) << 0) & AT91_SSC_DATALEN);
-
-
-
- break;
-
- case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
- default:
- printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n",
- ssc_p->daifmt);
- return -EINVAL;
- break;
- }
- DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr);
-
- if (!ssc_p->initialized) {
-
- /* Enable PMC peripheral clock for this SSC */
- DBG("Starting pid %d clock\n", ssc_p->ssc.pid);
- at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
-
- /* Reset the SSC and its PDC registers */
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0);
- at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0);
-
- if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt,
- 0, ssc_p->name, ssc_p)) < 0) {
- printk(KERN_WARNING "at91-ssc: request_irq failure\n");
-
- DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
- at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
- return ret;
- }
-
- ssc_p->initialized = 1;
- }
-
- /* set SSC clock mode register */
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div);
-
- /* set receive clock mode and format */
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr);
-
- /* set transmit clock mode and format */
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr);
-
- DBG("hw_params: SSC initialized\n");
- return 0;
-}
-
-
-static int at91_ssc_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
- struct at91_pcm_dma_params *dma_params;
- int dir;
-
- dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
- dma_params = ssc_p->dma_params[dir];
-
- at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
- dma_params->mask->ssc_enable);
-
- DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit",
- at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR));
- return 0;
-}
-
-
-#ifdef CONFIG_PM
-static int at91_ssc_suspend(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
-{
- struct at91_ssc_info *ssc_p;
-
- if(!cpu_dai->active)
- return 0;
-
- ssc_p = &ssc_info[cpu_dai->id];
-
- /* Save the status register before disabling transmit and receive. */
- ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
- AT91_SSC_TXDIS | AT91_SSC_RXDIS);
-
- /* Save the current interrupt mask, then disable unmasked interrupts. */
- ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr);
-
- ssc_p->ssc_state.ssc_cmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR);
- ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR);
- ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR);
- ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR);
- ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR);
-
- return 0;
-}
-
-static int at91_ssc_resume(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
-{
- struct at91_ssc_info *ssc_p;
-
- if(!cpu_dai->active)
- return 0;
-
- ssc_p = &ssc_info[cpu_dai->id];
-
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr);
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->ssc_state.ssc_cmr);
-
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER, ssc_p->ssc_state.ssc_imr);
-
- at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
- ((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
- ((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
-
- return 0;
-}
-
-#else
-#define at91_ssc_suspend NULL
-#define at91_ssc_resume NULL
-#endif
-
-#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
- SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
- SNDRV_PCM_RATE_96000)
-
-#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
- { .name = "at91-ssc0",
- .id = 0,
- .type = SND_SOC_DAI_PCM,
- .suspend = at91_ssc_suspend,
- .resume = at91_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .ops = {
- .startup = at91_ssc_startup,
- .shutdown = at91_ssc_shutdown,
- .prepare = at91_ssc_prepare,
- .hw_params = at91_ssc_hw_params,},
- .dai_ops = {
- .set_sysclk = at91_ssc_set_dai_sysclk,
- .set_fmt = at91_ssc_set_dai_fmt,
- .set_clkdiv = at91_ssc_set_dai_clkdiv,},
- .private_data = &ssc_info[0].ssc,
- },
-#if NUM_SSC_DEVICES == 3
- { .name = "at91-ssc1",
- .id = 1,
- .type = SND_SOC_DAI_PCM,
- .suspend = at91_ssc_suspend,
- .resume = at91_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .ops = {
- .startup = at91_ssc_startup,
- .shutdown = at91_ssc_shutdown,
- .prepare = at91_ssc_prepare,
- .hw_params = at91_ssc_hw_params,},
- .dai_ops = {
- .set_sysclk = at91_ssc_set_dai_sysclk,
- .set_fmt = at91_ssc_set_dai_fmt,
- .set_clkdiv = at91_ssc_set_dai_clkdiv,},
- .private_data = &ssc_info[1].ssc,
- },
- { .name = "at91-ssc2",
- .id = 2,
- .type = SND_SOC_DAI_PCM,
- .suspend = at91_ssc_suspend,
- .resume = at91_ssc_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = AT91_SSC_RATES,
- .formats = AT91_SSC_FORMATS,},
- .ops = {
- .startup = at91_ssc_startup,
- .shutdown = at91_ssc_shutdown,
- .prepare = at91_ssc_prepare,
- .hw_params = at91_ssc_hw_params,},
- .dai_ops = {
- .set_sysclk = at91_ssc_set_dai_sysclk,
- .set_fmt = at91_ssc_set_dai_fmt,
- .set_clkdiv = at91_ssc_set_dai_clkdiv,},
- .private_data = &ssc_info[2].ssc,
- },
-#endif
-};
-
-EXPORT_SYMBOL_GPL(at91_ssc_dai);
-
-/* Module information */
-MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
-MODULE_DESCRIPTION("AT91 SSC ASoC Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
deleted file mode 100644
index 6b7bf382d06f..000000000000
--- a/sound/soc/at91/at91-ssc.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- * Created: Jan 9, 2007
- *
- * This program is free software; you can 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 _AT91_SSC_H
-#define _AT91_SSC_H
-
-/* SSC system clock ids */
-#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
-
-/* SSC divider ids */
-#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */
-#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
-#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
-
-extern struct snd_soc_dai at91_ssc_dai[];
-
-#endif /* _AT91_SSC_H */
-
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
new file mode 100644
index 000000000000..a608d7009dbd
--- /dev/null
+++ b/sound/soc/atmel/Kconfig
@@ -0,0 +1,43 @@
+config SND_ATMEL_SOC
+ tristate "SoC Audio for the Atmel System-on-Chip"
+ depends on ARCH_AT91 || AVR32
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the ATMEL SSC interface. You will also need
+ to select the audio interfaces to support below.
+
+config SND_ATMEL_SOC_SSC
+ tristate
+ depends on SND_ATMEL_SOC
+ help
+ Say Y or M if you want to add support for codecs the
+ ATMEL SSC interface. You will also needs to select the individual
+ machine drivers to support below.
+
+config SND_AT91_SOC_SAM9G20_WM8731
+ tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
+ depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC
+ select SND_ATMEL_SOC_SSC
+ select SND_SOC_WM8731
+ help
+ Say Y if you want to add support for SoC audio on WM8731-based
+ AT91sam9g20 evaluation board.
+
+config SND_AT32_SOC_PLAYPAQ
+ tristate "SoC Audio support for PlayPaq with WM8510"
+ depends on SND_ATMEL_SOC && BOARD_PLAYPAQ
+ select SND_ATMEL_SOC_SSC
+ select SND_SOC_WM8510
+ help
+ Say Y or M here if you want to add support for SoC audio
+ on the LRS PlayPaq.
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+ bool "Run CODEC on PlayPaq in slave mode"
+ depends on SND_AT32_SOC_PLAYPAQ
+ default n
+ help
+ Say Y if you want to run with the AT32 SSC generating the BCLK
+ and FRAME signals on the PlayPaq. Unless you want to play
+ with the AT32 as the SSC master, you probably want to say N here,
+ as this will give you better sound quality.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
new file mode 100644
index 000000000000..f54a7cc68e66
--- /dev/null
+++ b/sound/soc/atmel/Makefile
@@ -0,0 +1,15 @@
+# AT91 Platform Support
+snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+
+obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+
+# AT91 Machine Support
+snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
new file mode 100644
index 000000000000..1fac5efd285b
--- /dev/null
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -0,0 +1,494 @@
+/*
+ * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 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 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/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ * them against real values for AT32
+ */
+static const struct snd_pcm_hardware atmel_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 2,
+ .periods_max = 1024,
+ .buffer_bytes_max = 32 * 1024,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct atmel_runtime_data {
+ struct atmel_pcm_dma_params *params;
+ dma_addr_t dma_buffer; /* physical address of dma buffer */
+ dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
+ size_t period_size;
+
+ dma_addr_t period_ptr; /* physical address of next period */
+ int periods; /* period index of period_ptr */
+
+ /* PDC register save */
+ u32 pdc_xpr_save;
+ u32 pdc_xcr_save;
+ u32 pdc_xnpr_save;
+ u32 pdc_xncr_save;
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size = atmel_pcm_hardware.buffer_bytes_max;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_coherent(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ pr_debug("atmel-pcm:"
+ "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+ (void *) buf->area,
+ (void *) buf->addr,
+ size);
+
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+ struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ static int count;
+
+ count++;
+
+ if (ssc_sr & params->mask->ssc_endbuf) {
+ pr_warning("atmel-pcm: buffer %s on %s"
+ " (SSC_SR=%#x, count=%d)\n",
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+ ? "underrun" : "overrun",
+ params->name, ssc_sr, count);
+
+ /* re-start the PDC */
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ }
+
+ if (ssc_sr & params->mask->ssc_endx) {
+ /* Load the PDC next pointer and counter registers */
+ prtd->period_ptr += prtd->period_size;
+ if (prtd->period_ptr >= prtd->dma_buffer_end)
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+ }
+
+ snd_pcm_period_elapsed(substream);
+}
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ /* this may get called several times by oss emulation
+ * with different params */
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ prtd->params = rtd->dai->cpu_dai->dma_data;
+ prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
+
+ prtd->dma_buffer = runtime->dma_addr;
+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+ prtd->period_size = params_period_bytes(params);
+
+ pr_debug("atmel-pcm: "
+ "hw_params: DMA for %s initialized "
+ "(dma_bytes=%u, period_size=%u)\n",
+ prtd->params->name,
+ runtime->dma_bytes,
+ prtd->period_size);
+ return 0;
+}
+
+static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+
+ if (params != NULL) {
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_disable);
+ prtd->params->dma_intr_handler = NULL;
+ }
+
+ return 0;
+}
+
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+
+ ssc_writex(params->ssc->regs, SSC_IDR,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ return 0;
+}
+
+static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_pcm_runtime *rtd = substream->runtime;
+ struct atmel_runtime_data *prtd = rtd->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ int ret = 0;
+
+ pr_debug("atmel-pcm:buffer_size = %ld,"
+ "dma_area = %p, dma_bytes = %u\n",
+ rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ prtd->period_ptr = prtd->dma_buffer;
+
+ ssc_writex(params->ssc->regs, params->pdc->xpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xcr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ prtd->period_ptr += prtd->period_size;
+ ssc_writex(params->ssc->regs, params->pdc->xnpr,
+ prtd->period_ptr);
+ ssc_writex(params->ssc->regs, params->pdc->xncr,
+ prtd->period_size / params->pdc_xfer_size);
+
+ pr_debug("atmel-pcm: trigger: "
+ "period_ptr=%lx, xpr=%u, "
+ "xcr=%u, xnpr=%u, xncr=%u\n",
+ (unsigned long)prtd->period_ptr,
+ ssc_readx(params->ssc->regs, params->pdc->xpr),
+ ssc_readx(params->ssc->regs, params->pdc->xcr),
+ ssc_readx(params->ssc->regs, params->pdc->xnpr),
+ ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+ ssc_writex(params->ssc->regs, SSC_IER,
+ params->mask->ssc_endx | params->mask->ssc_endbuf);
+ ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+ params->mask->pdc_enable);
+
+ pr_debug("sr=%u imr=%u\n",
+ ssc_readx(params->ssc->regs, SSC_SR),
+ ssc_readx(params->ssc->regs, SSC_IER));
+ break; /* SNDRV_PCM_TRIGGER_START */
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_disable);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+ params->mask->pdc_enable);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t atmel_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd = runtime->private_data;
+ struct atmel_pcm_dma_params *params = prtd->params;
+ dma_addr_t ptr;
+ snd_pcm_uframes_t x;
+
+ ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+ x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+ if (x == runtime->buffer_size)
+ x = 0;
+
+ return x;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct atmel_runtime_data *prtd;
+ int ret = 0;
+
+ snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
+
+ /* ensure that buffer size is a multiple of period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ goto out;
+
+ prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
+ if (prtd == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ runtime->private_data = prtd;
+
+ out:
+ return ret;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct atmel_runtime_data *prtd = substream->runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ return remap_pfn_range(vma, vma->vm_start,
+ substream->dma_buffer.addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct snd_pcm_ops atmel_pcm_ops = {
+ .open = atmel_pcm_open,
+ .close = atmel_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = atmel_pcm_hw_params,
+ .hw_free = atmel_pcm_hw_free,
+ .prepare = atmel_pcm_prepare,
+ .trigger = atmel_pcm_trigger,
+ .pointer = atmel_pcm_pointer,
+ .mmap = atmel_pcm_mmap,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 atmel_pcm_dmamask = 0xffffffff;
+
+static int atmel_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &atmel_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = atmel_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ pr_debug("at32-pcm:"
+ "Allocating PCM capture DMA buffer\n");
+ ret = atmel_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+#ifdef CONFIG_PM
+static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct atmel_runtime_data *prtd;
+ struct atmel_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* disable the PDC and save the PDC registers */
+
+ ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+ prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+ prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+ prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+ prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+ return 0;
+}
+
+static int atmel_pcm_resume(struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = dai->runtime;
+ struct atmel_runtime_data *prtd;
+ struct atmel_pcm_dma_params *params;
+
+ if (!runtime)
+ return 0;
+
+ prtd = runtime->private_data;
+ params = prtd->params;
+
+ /* restore the PDC registers and enable the PDC */
+ ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+ ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+ ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+ return 0;
+}
+#else
+#define atmel_pcm_suspend NULL
+#define atmel_pcm_resume NULL
+#endif
+
+struct snd_soc_platform atmel_soc_platform = {
+ .name = "atmel-audio",
+ .pcm_ops = &atmel_pcm_ops,
+ .pcm_new = atmel_pcm_new,
+ .pcm_free = atmel_pcm_free_dma_buffers,
+ .suspend = atmel_pcm_suspend,
+ .resume = atmel_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(atmel_soc_platform);
+
+static int __init atmel_pcm_modinit(void)
+{
+ return snd_soc_register_platform(&atmel_soc_platform);
+}
+module_init(atmel_pcm_modinit);
+
+static void __exit atmel_pcm_modexit(void)
+{
+ snd_soc_unregister_platform(&atmel_soc_platform);
+}
+module_exit(atmel_pcm_modexit);
+
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
new file mode 100644
index 000000000000..ec9b2824b663
--- /dev/null
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -0,0 +1,86 @@
+/*
+ * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author: Nicolas Pitre
+ * Created: Nov 30, 2004
+ * Copyright: (C) 2004 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 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 _ATMEL_PCM_H
+#define _ATMEL_PCM_H
+
+#include <linux/atmel-ssc.h>
+
+/*
+ * Registers and status bits that are required by the PCM driver.
+ */
+struct atmel_pdc_regs {
+ unsigned int xpr; /* PDC recv/trans pointer */
+ unsigned int xcr; /* PDC recv/trans counter */
+ unsigned int xnpr; /* PDC next recv/trans pointer */
+ unsigned int xncr; /* PDC next recv/trans counter */
+ unsigned int ptcr; /* PDC transfer control */
+};
+
+struct atmel_ssc_mask {
+ u32 ssc_enable; /* SSC recv/trans enable */
+ u32 ssc_disable; /* SSC recv/trans disable */
+ u32 ssc_endx; /* SSC ENDTX or ENDRX */
+ u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
+ u32 pdc_enable; /* PDC recv/trans enable */
+ u32 pdc_disable; /* PDC recv/trans disable */
+};
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation. All fields except dma_intr_handler() are initialized
+ * by the interface. The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct atmel_pcm_dma_params {
+ char *name; /* stream identifier */
+ int pdc_xfer_size; /* PDC counter increment in bytes */
+ struct ssc_device *ssc; /* SSC device for stream */
+ struct atmel_pdc_regs *pdc; /* PDC receive or transmit registers */
+ struct atmel_ssc_mask *mask; /* SSC & PDC status bits */
+ struct snd_pcm_substream *substream;
+ void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
+};
+
+extern struct snd_soc_platform atmel_soc_platform;
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
+
+#endif /* _ATMEL_PCM_H */
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
new file mode 100644
index 000000000000..c5d67900d666
--- /dev/null
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -0,0 +1,790 @@
+/*
+ * atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ * ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <linux/atmel-ssc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
+#define NUM_SSC_DEVICES 1
+#else
+#define NUM_SSC_DEVICES 3
+#endif
+
+/*
+ * SSC PDC registers required by the PCM DMA engine.
+ */
+static struct atmel_pdc_regs pdc_tx_reg = {
+ .xpr = ATMEL_PDC_TPR,
+ .xcr = ATMEL_PDC_TCR,
+ .xnpr = ATMEL_PDC_TNPR,
+ .xncr = ATMEL_PDC_TNCR,
+};
+
+static struct atmel_pdc_regs pdc_rx_reg = {
+ .xpr = ATMEL_PDC_RPR,
+ .xcr = ATMEL_PDC_RCR,
+ .xnpr = ATMEL_PDC_RNPR,
+ .xncr = ATMEL_PDC_RNCR,
+};
+
+/*
+ * SSC & PDC status bits for transmit and receive.
+ */
+static struct atmel_ssc_mask ssc_tx_mask = {
+ .ssc_enable = SSC_BIT(CR_TXEN),
+ .ssc_disable = SSC_BIT(CR_TXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDTX),
+ .ssc_endbuf = SSC_BIT(SR_TXBUFE),
+ .pdc_enable = ATMEL_PDC_TXTEN,
+ .pdc_disable = ATMEL_PDC_TXTDIS,
+};
+
+static struct atmel_ssc_mask ssc_rx_mask = {
+ .ssc_enable = SSC_BIT(CR_RXEN),
+ .ssc_disable = SSC_BIT(CR_RXDIS),
+ .ssc_endx = SSC_BIT(SR_ENDRX),
+ .ssc_endbuf = SSC_BIT(SR_RXBUFF),
+ .pdc_enable = ATMEL_PDC_RXTEN,
+ .pdc_disable = ATMEL_PDC_RXTDIS,
+};
+
+
+/*
+ * DMA parameters.
+ */
+static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+ {{
+ .name = "SSC0 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC0 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ } },
+#if NUM_SSC_DEVICES == 3
+ {{
+ .name = "SSC1 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC1 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ } },
+ {{
+ .name = "SSC2 PCM out",
+ .pdc = &pdc_tx_reg,
+ .mask = &ssc_tx_mask,
+ },
+ {
+ .name = "SSC2 PCM in",
+ .pdc = &pdc_rx_reg,
+ .mask = &ssc_rx_mask,
+ } },
+#endif
+};
+
+
+static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+ {
+ .name = "ssc0",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+#if NUM_SSC_DEVICES == 3
+ {
+ .name = "ssc1",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+ {
+ .name = "ssc2",
+ .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+ .dir_mask = SSC_DIR_MASK_UNUSED,
+ .initialized = 0,
+ },
+#endif
+};
+
+
+/*
+ * SSC interrupt handler. Passes PDC interrupts to the DMA
+ * interrupt handler in the PCM driver.
+ */
+static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
+{
+ struct atmel_ssc_info *ssc_p = dev_id;
+ struct atmel_pcm_dma_params *dma_params;
+ u32 ssc_sr;
+ u32 ssc_substream_mask;
+ int i;
+
+ ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR)
+ & (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR);
+
+ /*
+ * Loop through the substreams attached to this SSC. If
+ * a DMA-related interrupt occurred on that substream, call
+ * the DMA interrupt handler function, if one has been
+ * registered in the dma_params structure by the PCM driver.
+ */
+ for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+ dma_params = ssc_p->dma_params[i];
+
+ if ((dma_params != NULL) &&
+ (dma_params->dma_intr_handler != NULL)) {
+ ssc_substream_mask = (dma_params->mask->ssc_endx |
+ dma_params->mask->ssc_endbuf);
+ if (ssc_sr & ssc_substream_mask) {
+ dma_params->dma_intr_handler(ssc_sr,
+ dma_params->
+ substream);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup. Only that one substream allowed in each direction.
+ */
+static int atmel_ssc_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ int dir_mask;
+
+ pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
+ ssc_readl(ssc_p->ssc->regs, SR));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir_mask = SSC_DIR_MASK_PLAYBACK;
+ else
+ dir_mask = SSC_DIR_MASK_CAPTURE;
+
+ spin_lock_irq(&ssc_p->lock);
+ if (ssc_p->dir_mask & dir_mask) {
+ spin_unlock_irq(&ssc_p->lock);
+ return -EBUSY;
+ }
+ ssc_p->dir_mask |= dir_mask;
+ spin_unlock_irq(&ssc_p->lock);
+
+ return 0;
+}
+
+/*
+ * Shutdown. Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir, dir_mask;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 0;
+ else
+ dir = 1;
+
+ dma_params = ssc_p->dma_params[dir];
+
+ if (dma_params != NULL) {
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+ pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n",
+ (dir ? "receive" : "transmit"),
+ ssc_readl(ssc_p->ssc->regs, SR));
+
+ dma_params->ssc = NULL;
+ dma_params->substream = NULL;
+ ssc_p->dma_params[dir] = NULL;
+ }
+
+ dir_mask = 1 << dir;
+
+ spin_lock_irq(&ssc_p->lock);
+ ssc_p->dir_mask &= ~dir_mask;
+ if (!ssc_p->dir_mask) {
+ if (ssc_p->initialized) {
+ /* Shutdown the SSC clock. */
+ pr_debug("atmel_ssc_dau: Stopping clock\n");
+ clk_disable(ssc_p->ssc->clk);
+
+ free_irq(ssc_p->ssc->irq, ssc_p);
+ ssc_p->initialized = 0;
+ }
+
+ /* Reset the SSC */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+ /* Clear the SSC dividers */
+ ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+ }
+ spin_unlock_irq(&ssc_p->lock);
+}
+
+
+/*
+ * Record the DAI format for use in hw_params().
+ */
+static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ ssc_p->daifmt = fmt;
+ return 0;
+}
+
+/*
+ * Record SSC clock dividers for use in hw_params().
+ */
+static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+ switch (div_id) {
+ case ATMEL_SSC_CMR_DIV:
+ /*
+ * The same master clock divider is used for both
+ * transmit and receive, so if a value has already
+ * been set, it must match this value.
+ */
+ if (ssc_p->cmr_div == 0)
+ ssc_p->cmr_div = div;
+ else
+ if (div != ssc_p->cmr_div)
+ return -EBUSY;
+ break;
+
+ case ATMEL_SSC_TCMR_PERIOD:
+ ssc_p->tcmr_period = div;
+ break;
+
+ case ATMEL_SSC_RCMR_PERIOD:
+ ssc_p->rcmr_period = div;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Configure the SSC.
+ */
+static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ int id = rtd->dai->cpu_dai->id;
+ struct atmel_ssc_info *ssc_p = &ssc_info[id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir, channels, bits;
+ u32 tfmr, rfmr, tcmr, rcmr;
+ int start_event;
+ int ret;
+
+ /*
+ * Currently, there is only one set of dma params for
+ * each direction. If more are added, this code will
+ * have to be changed to select the proper set.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 0;
+ else
+ dir = 1;
+
+ dma_params = &ssc_dma_params[id][dir];
+ dma_params->ssc = ssc_p->ssc;
+ dma_params->substream = substream;
+
+ ssc_p->dma_params[dir] = dma_params;
+
+ /*
+ * The cpu_dai->dma_data field is only used to communicate the
+ * appropriate DMA parameters to the pcm driver hw_params()
+ * function. It should not be used for other purposes
+ * as it is common to all substreams.
+ */
+ rtd->dai->cpu_dai->dma_data = dma_params;
+
+ channels = params_channels(params);
+
+ /*
+ * Determine sample size in bits and the PDC increment.
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ bits = 8;
+ dma_params->pdc_xfer_size = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits = 16;
+ dma_params->pdc_xfer_size = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits = 24;
+ dma_params->pdc_xfer_size = 4;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits = 32;
+ dma_params->pdc_xfer_size = 4;
+ break;
+ default:
+ printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ /*
+ * The SSC only supports up to 16-bit samples in I2S format, due
+ * to the size of the Frame Mode Register FSLEN field.
+ */
+ if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
+ && bits > 16) {
+ printk(KERN_WARNING
+ "atmel_ssc_dai: sample size %d"
+ "is too large for I2S\n", bits);
+ return -EINVAL;
+ }
+
+ /*
+ * Compute SSC register settings.
+ */
+ switch (ssc_p->daifmt
+ & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * I2S format, SSC provides BCLK and LRC clocks.
+ *
+ * The SSC transmit and receive clocks are generated
+ * from the MCK divider, and the BCLK signal
+ * is output on the SSC TK line.
+ */
+ rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+ | SSC_BF(RCMR_STTDLY, START_DELAY)
+ | SSC_BF(RCMR_START, SSC_START_FALLING_RF)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+ rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
+ | SSC_BF(RFMR_FSLEN, (bits - 1))
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+ | SSC_BF(TCMR_STTDLY, START_DELAY)
+ | SSC_BF(TCMR_START, SSC_START_FALLING_RF)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+ | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+ | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+ tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
+ | SSC_BF(TFMR_FSLEN, (bits - 1))
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+ break;
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ /*
+ * I2S format, CODEC supplies BCLK and LRC clocks.
+ *
+ * The SSC transmit clock is obtained from the BCLK signal on
+ * on the TK line, and the SSC receive clock is
+ * generated from the transmit clock.
+ *
+ * For single channel data, one sample is transferred
+ * on the falling edge of the LRC clock.
+ * For two channel data, one sample is
+ * transferred on both edges of the LRC clock.
+ */
+ start_event = ((channels == 1)
+ ? SSC_START_FALLING_RF
+ : SSC_START_EDGE_RF);
+
+ rcmr = SSC_BF(RCMR_PERIOD, 0)
+ | SSC_BF(RCMR_STTDLY, START_DELAY)
+ | SSC_BF(RCMR_START, start_event)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(RCMR_CKS, SSC_CKS_CLOCK);
+
+ rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
+ | SSC_BF(RFMR_FSLEN, 0)
+ | SSC_BF(RFMR_DATNB, 0)
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tcmr = SSC_BF(TCMR_PERIOD, 0)
+ | SSC_BF(TCMR_STTDLY, START_DELAY)
+ | SSC_BF(TCMR_START, start_event)
+ | SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+ | SSC_BF(TCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+
+ tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
+ | SSC_BF(TFMR_FSLEN, 0)
+ | SSC_BF(TFMR_DATNB, 0)
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+ /*
+ * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+ *
+ * The SSC transmit and receive clocks are generated from the
+ * MCK divider, and the BCLK signal is output
+ * on the SSC TK line.
+ */
+ rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+ | SSC_BF(RCMR_STTDLY, 1)
+ | SSC_BF(RCMR_START, SSC_START_RISING_RF)
+ | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+ | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+ | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+ rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
+ | SSC_BF(RFMR_FSLEN, 0)
+ | SSC_BF(RFMR_DATNB, (channels - 1))
+ | SSC_BIT(RFMR_MSBF)
+ | SSC_BF(RFMR_LOOP, 0)
+ | SSC_BF(RFMR_DATLEN, (bits - 1));
+
+ tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+ | SSC_BF(TCMR_STTDLY, 1)
+ | SSC_BF(TCMR_START, SSC_START_RISING_RF)
+ | SSC_BF(TCMR_CKI, SSC_CKI_RISING)
+ | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+ | SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+ tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ | SSC_BF(TFMR_FSDEN, 0)
+ | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
+ | SSC_BF(TFMR_FSLEN, 0)
+ | SSC_BF(TFMR_DATNB, (channels - 1))
+ | SSC_BIT(TFMR_MSBF)
+ | SSC_BF(TFMR_DATDEF, 0)
+ | SSC_BF(TFMR_DATLEN, (bits - 1));
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+ default:
+ printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
+ ssc_p->daifmt);
+ return -EINVAL;
+ break;
+ }
+ pr_debug("atmel_ssc_hw_params: "
+ "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+ rcmr, rfmr, tcmr, tfmr);
+
+ if (!ssc_p->initialized) {
+
+ /* Enable PMC peripheral clock for this SSC */
+ pr_debug("atmel_ssc_dai: Starting clock\n");
+ clk_enable(ssc_p->ssc->clk);
+
+ /* Reset the SSC and its PDC registers */
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+ ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+ ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+ ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+ ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0,
+ ssc_p->name, ssc_p);
+ if (ret < 0) {
+ printk(KERN_WARNING
+ "atmel_ssc_dai: request_irq failure\n");
+ pr_debug("Atmel_ssc_dai: Stoping clock\n");
+ clk_disable(ssc_p->ssc->clk);
+ return ret;
+ }
+
+ ssc_p->initialized = 1;
+ }
+
+ /* set SSC clock mode register */
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+ /* set receive clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+ /* set transmit clock mode and format */
+ ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+ ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+ pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n");
+ return 0;
+}
+
+
+static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+ struct atmel_pcm_dma_params *dma_params;
+ int dir;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = 0;
+ else
+ dir = 1;
+
+ dma_params = ssc_p->dma_params[dir];
+
+ ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+ pr_debug("%s enabled SSC_SR=0x%08x\n",
+ dir ? "receive" : "transmit",
+ ssc_readl(ssc_p->ssc->regs, SR));
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+{
+ struct atmel_ssc_info *ssc_p;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* Save the status register before disabling transmit and receive */
+ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+ /* Save the current interrupt mask, then disable unmasked interrupts */
+ ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+ ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+ ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+ ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+ ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+ ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+ ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+ return 0;
+}
+
+
+
+static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct atmel_ssc_info *ssc_p;
+ u32 cr;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssc_p = &ssc_info[cpu_dai->id];
+
+ /* restore SSC register settings */
+ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+ ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+ ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+ ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+ /* re-enable interrupts */
+ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+ /* Re-enable recieve and transmit as appropriate */
+ cr = 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+ cr |=
+ (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+ ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+ return 0;
+}
+#else /* CONFIG_PM */
+# define atmel_ssc_suspend NULL
+# define atmel_ssc_resume NULL
+#endif /* CONFIG_PM */
+
+
+#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
+ { .name = "atmel-ssc0",
+ .id = 0,
+ .suspend = atmel_ssc_suspend,
+ .resume = atmel_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .ops = {
+ .startup = atmel_ssc_startup,
+ .shutdown = atmel_ssc_shutdown,
+ .prepare = atmel_ssc_prepare,
+ .hw_params = atmel_ssc_hw_params,
+ .set_fmt = atmel_ssc_set_dai_fmt,
+ .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .private_data = &ssc_info[0],
+ },
+#if NUM_SSC_DEVICES == 3
+ { .name = "atmel-ssc1",
+ .id = 1,
+ .suspend = atmel_ssc_suspend,
+ .resume = atmel_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .ops = {
+ .startup = atmel_ssc_startup,
+ .shutdown = atmel_ssc_shutdown,
+ .prepare = atmel_ssc_prepare,
+ .hw_params = atmel_ssc_hw_params,
+ .set_fmt = atmel_ssc_set_dai_fmt,
+ .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .private_data = &ssc_info[1],
+ },
+ { .name = "atmel-ssc2",
+ .id = 2,
+ .suspend = atmel_ssc_suspend,
+ .resume = atmel_ssc_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ATMEL_SSC_RATES,
+ .formats = ATMEL_SSC_FORMATS,},
+ .ops = {
+ .startup = atmel_ssc_startup,
+ .shutdown = atmel_ssc_shutdown,
+ .prepare = atmel_ssc_prepare,
+ .hw_params = atmel_ssc_hw_params,
+ .set_fmt = atmel_ssc_set_dai_fmt,
+ .set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+ .private_data = &ssc_info[2],
+ },
+#endif
+};
+EXPORT_SYMBOL_GPL(atmel_ssc_dai);
+
+static int __init atmel_ssc_modinit(void)
+{
+ return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_init(atmel_ssc_modinit);
+
+static void __exit atmel_ssc_modexit(void)
+{
+ snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_exit(atmel_ssc_modexit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
+MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
new file mode 100644
index 000000000000..a828746e8a2f
--- /dev/null
+++ b/sound/soc/atmel/atmel_ssc_dai.h
@@ -0,0 +1,121 @@
+/*
+ * atmel_ssc_dai.h - ALSA SSC interface for the Atmel SoC
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ * ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.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 _ATMEL_SSC_DAI_H
+#define _ATMEL_SSC_DAI_H
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "atmel-pcm.h"
+
+/* SSC system clock ids */
+#define ATMEL_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
+
+/* SSC divider ids */
+#define ATMEL_SSC_CMR_DIV 0 /* MCK divider for BCLK */
+#define ATMEL_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
+#define ATMEL_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED 0
+#define SSC_DIR_MASK_PLAYBACK 1
+#define SSC_DIR_MASK_CAPTURE 2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>. These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS 0
+#define SSC_START_TX_RX 1
+#define SSC_START_LOW_RF 2
+#define SSC_START_HIGH_RF 3
+#define SSC_START_FALLING_RF 4
+#define SSC_START_RISING_RF 5
+#define SSC_START_LEVEL_RF 6
+#define SSC_START_EDGE_RF 7
+#define SSS_START_COMPARE_0 8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING 0
+#define SSC_CKI_RISING 1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE 0
+#define SSC_CKO_CONTINUOUS 1
+#define SSC_CKO_TRANSFER 2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV 0
+#define SSC_CKS_CLOCK 1
+#define SSC_CKS_PIN 2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE 0
+#define SSC_FSEDGE_NEGATIVE 1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE 0
+#define SSC_FSOS_NEGATIVE 1
+#define SSC_FSOS_POSITIVE 2
+#define SSC_FSOS_LOW 3
+#define SSC_FSOS_HIGH 4
+#define SSC_FSOS_TOGGLE 5
+
+#define START_DELAY 1
+
+struct atmel_ssc_state {
+ u32 ssc_cmr;
+ u32 ssc_rcmr;
+ u32 ssc_rfmr;
+ u32 ssc_tcmr;
+ u32 ssc_tfmr;
+ u32 ssc_sr;
+ u32 ssc_imr;
+};
+
+
+struct atmel_ssc_info {
+ char *name;
+ struct ssc_device *ssc;
+ spinlock_t lock; /* lock for dir_mask */
+ unsigned short dir_mask; /* 0=unused, 1=playback, 2=capture */
+ unsigned short initialized; /* true if SSC has been initialized */
+ unsigned short daifmt;
+ unsigned short cmr_div;
+ unsigned short tcmr_period;
+ unsigned short rcmr_period;
+ struct atmel_pcm_dma_params *dma_params[2];
+ struct atmel_ssc_state ssc_state;
+};
+extern struct snd_soc_dai atmel_ssc_dai[];
+
+#endif /* _AT91_SSC_DAI_H */
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
new file mode 100644
index 000000000000..43dd8cee83c6
--- /dev/null
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -0,0 +1,512 @@
+/* sound/soc/at32/playpaq_wm8510.c
+ * ASoC machine driver for PlayPaq using WM8510 codec
+ *
+ * Copyright (C) 2008 Long Range Systems
+ * Geoffrey Wossum <gwossum@acm.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.
+ *
+ * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
+ *
+ * NOTE: If you don't have the AT32 enhanced portmux configured (which
+ * isn't currently in the mainline or Atmel patched kernel), you will
+ * need to set the MCLK pin (PA30) to peripheral A in your board initialization
+ * code. Something like:
+ * at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/at32ap700x.h>
+#include <mach/portmux.h>
+
+#include "../codecs/wm8510.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+/*-------------------------------------------------------------------------*\
+ * constants
+\*-------------------------------------------------------------------------*/
+#define MCLK_PIN GPIO_PIN_PA(30)
+#define MCLK_PERIPH GPIO_PERIPH_A
+
+
+/*-------------------------------------------------------------------------*\
+ * data types
+\*-------------------------------------------------------------------------*/
+/* SSC clocking data */
+struct ssc_clock_data {
+ /* CMR div */
+ unsigned int cmr_div;
+
+ /* Frame period (as needed by xCMR.PERIOD) */
+ unsigned int period;
+
+ /* The SSC clock rate these settings where calculated for */
+ unsigned long ssc_rate;
+};
+
+
+/*-------------------------------------------------------------------------*\
+ * module data
+\*-------------------------------------------------------------------------*/
+static struct clk *_gclk0;
+static struct clk *_pll0;
+
+#define CODEC_CLK (_gclk0)
+
+
+/*-------------------------------------------------------------------------*\
+ * Sound SOC operations
+\*-------------------------------------------------------------------------*/
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ struct ssc_clock_data cd;
+ unsigned int rate, width_bits, channels;
+ unsigned int bitrate, ssc_div;
+ unsigned actual_rate;
+
+
+ /*
+ * Figure out required bitrate
+ */
+ rate = params_rate(params);
+ channels = params_channels(params);
+ width_bits = snd_pcm_format_physical_width(params_format(params));
+ bitrate = rate * width_bits * channels;
+
+
+ /*
+ * Figure out required SSC divider and period for required bitrate
+ */
+ cd.ssc_rate = clk_get_rate(ssc->clk);
+ ssc_div = cd.ssc_rate / bitrate;
+ cd.cmr_div = ssc_div / 2;
+ if (ssc_div & 1) {
+ /* round cmr_div up */
+ cd.cmr_div++;
+ }
+ cd.period = width_bits - 1;
+
+
+ /*
+ * Find actual rate, compare to requested rate
+ */
+ actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
+ pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+ rate, actual_rate);
+
+
+ return cd;
+}
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+
+static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct at32_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
+ int ret;
+
+
+ /* Due to difficulties with getting the correct clocks from the AT32's
+ * PLL0, we're going to let the CODEC be in charge of all the clocks
+ */
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+#else
+ struct ssc_clock_data cd;
+ const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+#endif
+
+ if (ssc == NULL) {
+ pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
+ return -EINVAL;
+ }
+
+
+ /*
+ * Figure out PLL and BCLK dividers for WM8510
+ */
+ switch (params_rate(params)) {
+ case 48000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 44100:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_1;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 22050:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_2;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 16000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_3;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 11025:
+ pll_out = 11289600;
+ mclk_div = WM8510_MCLKDIV_4;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ case 8000:
+ pll_out = 12288000;
+ mclk_div = WM8510_MCLKDIV_6;
+ bclk = WM8510_BCLKDIV_8;
+ break;
+
+ default:
+ pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+
+ /*
+ * set CPU and CODEC DAI configuration
+ */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CODEC DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU DAI format (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ /*
+ * Set CPU clock configuration
+ */
+#if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
+ pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
+ cd.cmr_div, cd.period);
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
+ ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
+ cd.period);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: "
+ "Failed to set CPU transmit period (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ /*
+ * Set CODEC clock configuration
+ */
+ pr_debug("playpaq_wm8510: "
+ "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
+ clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
+
+
+#if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
+ if (ret < 0) {
+ pr_warning
+ ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+#endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
+
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ clk_get_rate(CODEC_CLK), pll_out);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
+ if (ret < 0) {
+ pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
+ ret);
+ return ret;
+ }
+
+
+ return 0;
+}
+
+
+
+static struct snd_soc_ops playpaq_wm8510_ops = {
+ .hw_params = playpaq_wm8510_hw_params,
+};
+
+
+
+static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* speaker connected to SPKOUT */
+ {"Ext Spk", NULL, "SPKOUTP"},
+ {"Ext Spk", NULL, "SPKOUTN"},
+
+ {"Mic Bias", NULL, "Int Mic"},
+ {"MICN", NULL, "Mic Bias"},
+ {"MICP", NULL, "Mic Bias"},
+};
+
+
+
+static int playpaq_wm8510_init(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /*
+ * Add DAPM widgets
+ */
+ for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
+ snd_soc_dapm_new_control(codec, &playpaq_dapm_widgets[i]);
+
+
+
+ /*
+ * Setup audio path interconnects
+ */
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+
+
+ /* always connected pins */
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ snd_soc_dapm_sync(codec);
+
+
+
+ /* Make CSB show PLL rate */
+ snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
+ WM8510_OPCLKDIV_1 | 4);
+
+ return 0;
+}
+
+
+
+static struct snd_soc_dai_link playpaq_wm8510_dai = {
+ .name = "WM8510",
+ .stream_name = "WM8510 PCM",
+ .cpu_dai = &at32_ssc_dai[0],
+ .codec_dai = &wm8510_dai,
+ .init = playpaq_wm8510_init,
+ .ops = &playpaq_wm8510_ops,
+};
+
+
+
+static struct snd_soc_card snd_soc_playpaq = {
+ .name = "LRS_PlayPaq_WM8510",
+ .platform = &at32_soc_platform,
+ .dai_link = &playpaq_wm8510_dai,
+ .num_links = 1,
+};
+
+
+
+static struct wm8510_setup_data playpaq_wm8510_setup = {
+ .i2c_bus = 0,
+ .i2c_address = 0x1a,
+};
+
+
+
+static struct snd_soc_device playpaq_wm8510_snd_devdata = {
+ .card = &snd_soc_playpaq,
+ .codec_dev = &soc_codec_dev_wm8510,
+ .codec_data = &playpaq_wm8510_setup,
+};
+
+static struct platform_device *playpaq_snd_device;
+
+
+static int __init playpaq_asoc_init(void)
+{
+ int ret = 0;
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc = NULL;
+
+
+ /*
+ * Request SSC device
+ */
+ ssc = ssc_request(0);
+ if (IS_ERR(ssc)) {
+ ret = PTR_ERR(ssc);
+ goto err_ssc;
+ }
+ ssc_p->ssc = ssc;
+
+
+ /*
+ * Configure MCLK for WM8510
+ */
+ _gclk0 = clk_get(NULL, "gclk0");
+ if (IS_ERR(_gclk0)) {
+ _gclk0 = NULL;
+ goto err_gclk0;
+ }
+ _pll0 = clk_get(NULL, "pll0");
+ if (IS_ERR(_pll0)) {
+ _pll0 = NULL;
+ goto err_pll0;
+ }
+ if (clk_set_parent(_gclk0, _pll0)) {
+ pr_warning("snd-soc-playpaq: "
+ "Failed to set PLL0 as parent for DAC clock\n");
+ goto err_set_clk;
+ }
+ clk_set_rate(CODEC_CLK, 12000000);
+ clk_enable(CODEC_CLK);
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
+#endif
+
+
+ /*
+ * Create and register platform device
+ */
+ playpaq_snd_device = platform_device_alloc("soc-audio", 0);
+ if (playpaq_snd_device == NULL) {
+ ret = -ENOMEM;
+ goto err_device_alloc;
+ }
+
+ platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
+ playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
+
+ ret = platform_device_add(playpaq_snd_device);
+ if (ret) {
+ pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
+ ret);
+ goto err_device_add;
+ }
+
+ return 0;
+
+
+err_device_add:
+ if (playpaq_snd_device != NULL) {
+ platform_device_put(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+ }
+err_device_alloc:
+err_set_clk:
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+err_pll0:
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+err_gclk0:
+ ssc_free(ssc);
+err_ssc:
+ return ret;
+}
+
+
+static void __exit playpaq_asoc_exit(void)
+{
+ struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
+ struct ssc_device *ssc;
+
+ if (ssc_p != NULL) {
+ ssc = ssc_p->ssc;
+ if (ssc != NULL)
+ ssc_free(ssc);
+ ssc_p->ssc = NULL;
+ }
+
+ if (_gclk0 != NULL) {
+ clk_put(_gclk0);
+ _gclk0 = NULL;
+ }
+ if (_pll0 != NULL) {
+ clk_put(_pll0);
+ _pll0 = NULL;
+ }
+
+#if defined CONFIG_AT32_ENHANCED_PORTMUX
+ at32_free_pin(MCLK_PIN);
+#endif
+
+ platform_device_unregister(playpaq_snd_device);
+ playpaq_snd_device = NULL;
+}
+
+module_init(playpaq_asoc_init);
+module_exit(playpaq_asoc_exit);
+
+MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
+MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
new file mode 100644
index 000000000000..1fb59a9d3719
--- /dev/null
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -0,0 +1,328 @@
+/*
+ * sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based
+ * ATMEL AT91SAM9G20ek board.
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on ati_b1_wm8731.c by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ * Based on corgi.c by:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ int ret;
+
+ /* codec system clock is supplied by PCK0, set to 12MHz */
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+ 12000000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+ dev_dbg(rtd->socdev->dev, "shutdown");
+}
+
+static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
+ struct ssc_device *ssc = ssc_p->ssc;
+ int ret;
+
+ unsigned int rate;
+ int cmr_div, period;
+
+ if (ssc == NULL) {
+ printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
+ return -EINVAL;
+ }
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The SSC clock dividers depend on the sample rate. The CMR.DIV
+ * field divides the system master clock MCK to drive the SSC TK
+ * signal which provides the codec BCLK. The TCMR.PERIOD and
+ * RCMR.PERIOD fields further divide the BCLK signal to drive
+ * the SSC TF and RF signals which provide the codec DACLRC and
+ * ADCLRC clocks.
+ *
+ * The dividers were determined through trial and error, where a
+ * CMR.DIV value is chosen such that the resulting BCLK value is
+ * divisible, or almost divisible, by (2 * sample rate), and then
+ * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
+ */
+ rate = params_rate(params);
+
+ switch (rate) {
+ case 8000:
+ cmr_div = 55; /* BCLK = 133MHz/(2*55) = 1.209MHz */
+ period = 74; /* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
+ break;
+ case 11025:
+ cmr_div = 67; /* BCLK = 133MHz/(2*60) = 1.108MHz */
+ period = 45; /* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
+ break;
+ case 16000:
+ cmr_div = 63; /* BCLK = 133MHz/(2*63) = 1.055MHz */
+ period = 32; /* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
+ break;
+ case 22050:
+ cmr_div = 52; /* BCLK = 133MHz/(2*52) = 1.278MHz */
+ period = 28; /* LRC = BCLK/(2*(28+1)) = 22049Hz */
+ break;
+ case 32000:
+ cmr_div = 66; /* BCLK = 133MHz/(2*66) = 1.007MHz */
+ period = 15; /* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
+ break;
+ case 44100:
+ cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
+ period = 25; /* LRC = BCLK/(2*(25+1)) = 44098Hz */
+ break;
+ case 48000:
+ cmr_div = 33; /* BCLK = 133MHz/(2*33) = 2.015MHz */
+ period = 20; /* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
+ break;
+ case 88200:
+ cmr_div = 29; /* BCLK = 133MHz/(2*29) = 2.293MHz */
+ period = 12; /* LRC = BCLK/(2*(12+1)) = 88196Hz */
+ break;
+ case 96000:
+ cmr_div = 23; /* BCLK = 133MHz/(2*23) = 2.891MHz */
+ period = 14; /* LRC = BCLK/(2*(14+1)) = 96376Hz */
+ break;
+ default:
+ printk(KERN_WARNING "unsupported rate %d"
+ " on at91sam9g20ek board\n", rate);
+ return -EINVAL;
+ }
+
+ /* set the MCK divider for BCLK */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* set the BCLK divider for DACLRC */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai,
+ ATMEL_SSC_TCMR_PERIOD, period);
+ } else {
+ /* set the BCLK divider for ADCLRC */
+ ret = snd_soc_dai_set_clkdiv(cpu_dai,
+ ATMEL_SSC_RCMR_PERIOD, period);
+ }
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops at91sam9g20ek_ops = {
+ .startup = at91sam9g20ek_startup,
+ .hw_params = at91sam9g20ek_hw_params,
+ .shutdown = at91sam9g20ek_shutdown,
+};
+
+
+static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+ /* speaker connected to LHPOUT */
+ {"Ext Spk", NULL, "LHPOUT"},
+
+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
+ {"MICIN", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Int Mic"},
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9g20ek board.
+ */
+static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
+{
+ printk(KERN_DEBUG
+ "at91sam9g20ek_wm8731 "
+ ": at91sam9g20ek_wm8731_init() called\n");
+
+ /* Add specific widgets */
+ snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
+ ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
+ /* Set up specific audio path interconnects */
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ /* not connected */
+ snd_soc_dapm_disable_pin(codec, "RLINEIN");
+ snd_soc_dapm_disable_pin(codec, "LLINEIN");
+
+ /* always connected */
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link at91sam9g20ek_dai = {
+ .name = "WM8731",
+ .stream_name = "WM8731 PCM",
+ .cpu_dai = &atmel_ssc_dai[0],
+ .codec_dai = &wm8731_dai,
+ .init = at91sam9g20ek_wm8731_init,
+ .ops = &at91sam9g20ek_ops,
+};
+
+static struct snd_soc_card snd_soc_at91sam9g20ek = {
+ .name = "WM8731",
+ .platform = &atmel_soc_platform,
+ .dai_link = &at91sam9g20ek_dai,
+ .num_links = 1,
+};
+
+static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
+ .i2c_bus = 0,
+ .i2c_address = 0x1b,
+};
+
+static struct snd_soc_device at91sam9g20ek_snd_devdata = {
+ .card = &snd_soc_at91sam9g20ek,
+ .codec_dev = &soc_codec_dev_wm8731,
+ .codec_data = &at91sam9g20ek_wm8731_setup,
+};
+
+static struct platform_device *at91sam9g20ek_snd_device;
+
+static int __init at91sam9g20ek_init(void)
+{
+ struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+ struct ssc_device *ssc = NULL;
+ int ret;
+
+ /*
+ * Request SSC device
+ */
+ ssc = ssc_request(0);
+ if (IS_ERR(ssc)) {
+ ret = PTR_ERR(ssc);
+ ssc = NULL;
+ goto err_ssc;
+ }
+ ssc_p->ssc = ssc;
+
+ at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!at91sam9g20ek_snd_device) {
+ printk(KERN_DEBUG
+ "platform device allocation failed\n");
+ ret = -ENOMEM;
+ }
+
+ platform_set_drvdata(at91sam9g20ek_snd_device,
+ &at91sam9g20ek_snd_devdata);
+ at91sam9g20ek_snd_devdata.dev = &at91sam9g20ek_snd_device->dev;
+
+ ret = platform_device_add(at91sam9g20ek_snd_device);
+ if (ret) {
+ printk(KERN_DEBUG
+ "platform device allocation failed\n");
+ platform_device_put(at91sam9g20ek_snd_device);
+ }
+
+ return ret;
+
+err_ssc:
+ return ret;
+}
+
+static void __exit at91sam9g20ek_exit(void)
+{
+ struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+ struct ssc_device *ssc;
+
+ if (ssc_p != NULL) {
+ ssc = ssc_p->ssc;
+ if (ssc != NULL)
+ ssc_free(ssc);
+ ssc_p->ssc = NULL;
+ }
+
+ platform_device_unregister(at91sam9g20ek_snd_device);
+ at91sam9g20ek_snd_device = NULL;
+}
+
+module_init(at91sam9g20ek_init);
+module_exit(at91sam9g20ek_exit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 1466d9328800..74c823d60f91 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -406,11 +406,12 @@ static int __init au1xpsc_audio_dbdma_init(void)
{
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
- return 0;
+ return snd_soc_register_platform(&au1xpsc_soc_platform);
}
static void __exit au1xpsc_audio_dbdma_exit(void)
{
+ snd_soc_unregister_platform(&au1xpsc_soc_platform);
}
module_init(au1xpsc_audio_dbdma_init);
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 57facbad6825..f0e30aec7f23 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -160,7 +160,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -210,7 +211,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
}
static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
- int cmd)
+ int cmd, struct snd_soc_dai *dai)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -313,8 +314,7 @@ static void au1xpsc_ac97_remove(struct platform_device *pdev,
au1xpsc_ac97_workdata = NULL;
}
-static int au1xpsc_ac97_suspend(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
{
/* save interesting registers and disable PSC */
au1xpsc_ac97_workdata->pm[0] =
@@ -328,8 +328,7 @@ static int au1xpsc_ac97_suspend(struct platform_device *pdev,
return 0;
}
-static int au1xpsc_ac97_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
{
/* restore PSC clock config */
au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
@@ -345,7 +344,7 @@ static int au1xpsc_ac97_resume(struct platform_device *pdev,
struct snd_soc_dai au1xpsc_ac97_dai = {
.name = "au1xpsc_ac97",
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.probe = au1xpsc_ac97_probe,
.remove = au1xpsc_ac97_remove,
.suspend = au1xpsc_ac97_suspend,
@@ -372,11 +371,12 @@ EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
static int __init au1xpsc_ac97_init(void)
{
au1xpsc_ac97_workdata = NULL;
- return 0;
+ return snd_soc_register_dai(&au1xpsc_ac97_dai);
}
static void __exit au1xpsc_ac97_exit(void)
{
+ snd_soc_unregister_dai(&au1xpsc_ac97_dai);
}
module_init(au1xpsc_ac97_init);
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index 9384702c7ebd..f916de4400ed 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -116,7 +116,8 @@ out:
}
static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
@@ -240,7 +241,8 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
return 0;
}
-static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
int ret, stype = SUBSTREAM_TYPE(substream);
@@ -337,8 +339,7 @@ static void au1xpsc_i2s_remove(struct platform_device *pdev,
au1xpsc_i2s_workdata = NULL;
}
-static int au1xpsc_i2s_suspend(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
/* save interesting register and disable PSC */
au1xpsc_i2s_workdata->pm[0] =
@@ -352,8 +353,7 @@ static int au1xpsc_i2s_suspend(struct platform_device *pdev,
return 0;
}
-static int au1xpsc_i2s_resume(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
{
/* select I2S mode and PSC clock */
au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
@@ -369,7 +369,6 @@ static int au1xpsc_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai au1xpsc_i2s_dai = {
.name = "au1xpsc_i2s",
- .type = SND_SOC_DAI_I2S,
.probe = au1xpsc_i2s_probe,
.remove = au1xpsc_i2s_remove,
.suspend = au1xpsc_i2s_suspend,
@@ -389,8 +388,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
.ops = {
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
- },
- .dai_ops = {
.set_fmt = au1xpsc_i2s_set_fmt,
},
};
@@ -399,11 +396,12 @@ EXPORT_SYMBOL(au1xpsc_i2s_dai);
static int __init au1xpsc_i2s_init(void)
{
au1xpsc_i2s_workdata = NULL;
- return 0;
+ return snd_soc_register_dai(&au1xpsc_i2s_dai);
}
static void __exit au1xpsc_i2s_exit(void)
{
+ snd_soc_unregister_dai(&au1xpsc_i2s_dai);
}
module_init(au1xpsc_i2s_init);
diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c
index f75ae7f62c3d..27683eb7905e 100644
--- a/sound/soc/au1x/sample-ac97.c
+++ b/sound/soc/au1x/sample-ac97.c
@@ -42,14 +42,14 @@ static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
.ops = NULL,
};
-static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+static struct snd_soc_card au1xpsc_sample_ac97_machine = {
.name = "Au1xxx PSC AC97 Audio",
.dai_link = &au1xpsc_sample_ac97_dai,
.num_links = 1,
};
static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
- .machine = &au1xpsc_sample_ac97_machine,
+ .card = &au1xpsc_sample_ac97_machine,
.platform = &au1xpsc_soc_platform, /* see dbdma2.c */
.codec_dev = &soc_codec_dev_ac97,
};
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index dc006206f622..0a2f8f9eff53 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -1,6 +1,6 @@
config SND_BF5XX_I2S
tristate "SoC I2S Audio for the ADI BF5xx chip"
- depends on BLACKFIN && SND_SOC
+ depends on BLACKFIN
help
Say Y or M if you want to add support for codecs attached to
the Blackfin SPORT (synchronous serial ports) interface in I2S
@@ -13,7 +13,6 @@ config SND_BF5XX_SOC_SSM2602
select SND_BF5XX_SOC_I2S
select SND_SOC_SSM2602
select I2C
- select I2C_BLACKFIN_TWI
help
Say Y if you want to add support for SoC audio on BF527-EZKIT.
@@ -35,7 +34,7 @@ config SND_BFIN_AD73311_SE
config SND_BF5XX_AC97
tristate "SoC AC97 Audio for the ADI BF5xx chip"
- depends on BLACKFIN && SND_SOC
+ depends on BLACKFIN
help
Say Y or M if you want to add support for codecs attached to
the Blackfin SPORT (synchronous serial ports) interface in slot 16
@@ -47,7 +46,7 @@ config SND_BF5XX_AC97
properly with this driver. This driver is known to work with the
Analog Devices line of AC97 codecs.
-config SND_MMAP_SUPPORT
+config SND_BF5XX_MMAP_SUPPORT
bool "Enable MMAP Support"
depends on SND_BF5XX_AC97
default y
@@ -55,9 +54,17 @@ config SND_MMAP_SUPPORT
Say y if you want AC97 driver to support mmap mode.
We introduce an intermediate buffer to simulate mmap.
+config SND_BF5XX_MULTICHAN_SUPPORT
+ bool "Enable Multichannel Support"
+ depends on SND_BF5XX_AC97
+ default n
+ help
+ Say y if you want AC97 driver to support up to 5.1 channel audio.
+ this mode will consume much more memory for DMA.
+
config SND_BF5XX_SOC_SPORT
tristate
-
+
config SND_BF5XX_SOC_I2S
tristate
select SND_BF5XX_SOC_SPORT
@@ -80,7 +87,7 @@ config SND_BF5XX_SPORT_NUM
int "Set a SPORT for Sound chip"
depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
range 0 3 if BF54x
- range 0 1 if (BF53x || BF561)
+ range 0 1 if !BF54x
default 0
help
Set the correct SPORT for sound chip.
@@ -90,12 +97,13 @@ config SND_BF5XX_HAVE_COLD_RESET
depends on SND_BF5XX_AC97
default y if BFIN548_EZKIT
default n if !BFIN548_EZKIT
-
+
config SND_BF5XX_RESET_GPIO_NUM
int "Set a GPIO for cold reset"
depends on SND_BF5XX_HAVE_COLD_RESET
range 0 159
default 19 if BFIN548_EZKIT
default 5 if BFIN537_STAMP
+ default 0
help
Set the correct GPIO for RESET the sound chip.
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 25e50d2ea1ec..8067cfafa3a7 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -43,24 +43,34 @@
#include "bf5xx-ac97.h"
#include "bf5xx-sport.h"
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+static unsigned int ac97_chan_mask[] = {
+ SP_FL, /* Mono */
+ SP_STEREO, /* Stereo */
+ SP_2DOT1, /* 2.1*/
+ SP_QUAD,/*Quadraquic*/
+ SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
+ SP_5DOT1, /* 5.1 */
+};
+
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct sport_device *sport = runtime->private_data;
+ unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- bf5xx_pcm_to_ac97(
- (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
- (__u32 *)runtime->dma_area + sport->tx_pos, count);
+ bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
+ sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
+ runtime->channels, count, chan_mask);
sport->tx_pos += runtime->period_size;
if (sport->tx_pos >= runtime->buffer_size)
sport->tx_pos %= runtime->buffer_size;
sport->tx_delay_pos = sport->tx_pos;
} else {
- bf5xx_ac97_to_pcm(
- (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
- (__u32 *)runtime->dma_area + sport->rx_pos, count);
+ bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
+ sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
+ runtime->channels, count);
sport->rx_pos += runtime->period_size;
if (sport->rx_pos >= runtime->buffer_size)
sport->rx_pos %= runtime->buffer_size;
@@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
static void bf5xx_dma_irq(void *data)
{
struct snd_pcm_substream *pcm = data;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
struct snd_pcm_runtime *runtime = pcm->runtime;
struct sport_device *sport = runtime->private_data;
bf5xx_mmap_copy(pcm, runtime->period_size);
@@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data)
* The total rx/tx buffer is for ac97 frame to hold all pcm data
* is 0x20000 * sizeof(struct ac97_frame) / 4.
*/
-#ifdef CONFIG_SND_MMAP_SUPPORT
static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
-#else
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
#endif
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.period_bytes_min = 32,
.period_bytes_max = 0x10000,
@@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- memset(runtime->dma_area, 0, runtime->buffer_size);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport->once = 0;
+ if (runtime->dma_area)
+ memset(runtime->dma_area, 0, runtime->buffer_size);
+ memset(sport->tx_dma_buf, 0, runtime->buffer_size *
+ sizeof(struct ac97_frame));
+ } else
+ memset(sport->rx_dma_buf, 0, runtime->buffer_size *
+ sizeof(struct ac97_frame));
+#endif
snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
/* An intermediate buffer is introduced for implementing mmap for
* SPORT working in TMD mode(include AC97).
*/
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
@@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
bf5xx_mmap_copy(substream, runtime->period_size);
- snd_pcm_period_elapsed(substream);
sport->tx_delay_pos = 0;
+#endif
sport_tx_start(sport);
- }
- else
+ } else
sport_rx_start(sport);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
sport->tx_pos = 0;
#endif
sport_tx_stop(sport);
} else {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
sport->rx_pos = 0;
#endif
sport_rx_stop(sport);
@@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
struct sport_device *sport = runtime->private_data;
unsigned int curr;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
curr = sport->tx_delay_pos;
else
@@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
return ret;
}
-static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sport_device *sport = runtime->private_data;
-
- pr_debug("%s enter\n", __func__);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- sport->once = 0;
- memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
- } else
- memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
-
- return 0;
-}
-
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
@@ -281,32 +283,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
void __user *buf, snd_pcm_uframes_t count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
-
+ unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
pr_debug("%s copy pos:0x%lx count:0x%lx\n",
substream->stream ? "Capture" : "Playback", pos, count);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- bf5xx_pcm_to_ac97(
- (struct ac97_frame *)runtime->dma_area + pos,
- buf, count);
+ bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
+ (__u16 *)buf, count, chan_mask);
else
- bf5xx_ac97_to_pcm(
- (struct ac97_frame *)runtime->dma_area + pos,
- buf, count);
+ bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
+ (__u16 *)buf, count);
return 0;
}
#endif
struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
.open = bf5xx_pcm_open,
- .close = bf5xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
.hw_free = bf5xx_pcm_hw_free,
.prepare = bf5xx_pcm_prepare,
.trigger = bf5xx_pcm_trigger,
.pointer = bf5xx_pcm_pointer,
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
.mmap = bf5xx_pcm_mmap,
#else
.copy = bf5xx_pcm_copy,
@@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
* Need to allocate local buffer when enable
* MMAP for SPORT working in TMD mode (include AC97).
*/
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (!sport_handle->tx_dma_buf) {
sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
@@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
sizeof(struct ac97_frame) / 4;
#endif
@@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
continue;
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
buf->area = NULL;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (sport_handle->tx_dma_buf)
dma_free_coherent(NULL, size, \
@@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = {
};
EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
+static int __init bfin_ac97_init(void)
+{
+ return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+ snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
+}
+module_exit(bfin_ac97_exit);
+
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 5e5aafb6485f..3be2be60576d 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -54,71 +54,103 @@
static int *cmd_count;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
-#if defined(CONFIG_BF54x)
+static u16 sport_req[][7] = {
+ PIN_REQ_SPORT_0,
+#ifdef PIN_REQ_SPORT_1
+ PIN_REQ_SPORT_1,
+#endif
+#ifdef PIN_REQ_SPORT_2
+ PIN_REQ_SPORT_2,
+#endif
+#ifdef PIN_REQ_SPORT_3
+ PIN_REQ_SPORT_3,
+#endif
+ };
+
static struct sport_param sport_params[4] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERR,
+ .err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
+#ifdef PIN_REQ_SPORT_1
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERR,
+ .err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
},
+#endif
+#ifdef PIN_REQ_SPORT_2
{
.dma_rx_chan = CH_SPORT2_RX,
.dma_tx_chan = CH_SPORT2_TX,
- .err_irq = IRQ_SPORT2_ERR,
+ .err_irq = IRQ_SPORT2_ERROR,
.regs = (struct sport_register *)SPORT2_TCR1,
},
+#endif
+#ifdef PIN_REQ_SPORT_3
{
.dma_rx_chan = CH_SPORT3_RX,
.dma_tx_chan = CH_SPORT3_TX,
- .err_irq = IRQ_SPORT3_ERR,
+ .err_irq = IRQ_SPORT3_ERROR,
.regs = (struct sport_register *)SPORT3_TCR1,
}
-};
-#else
-static struct sport_param sport_params[2] = {
- {
- .dma_rx_chan = CH_SPORT0_RX,
- .dma_tx_chan = CH_SPORT0_TX,
- .err_irq = IRQ_SPORT0_ERROR,
- .regs = (struct sport_register *)SPORT0_TCR1,
- },
- {
- .dma_rx_chan = CH_SPORT1_RX,
- .dma_tx_chan = CH_SPORT1_TX,
- .err_irq = IRQ_SPORT1_ERROR,
- .regs = (struct sport_register *)SPORT1_TCR1,
- }
-};
#endif
+};
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
- size_t count)
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
+ size_t count, unsigned int chan_mask)
{
while (count--) {
- dst->ac97_tag = TAG_VALID | TAG_PCM;
- (dst++)->ac97_pcm = *src++;
+ dst->ac97_tag = TAG_VALID;
+ if (chan_mask & SP_FL) {
+ dst->ac97_pcm_r = *src++;
+ dst->ac97_tag |= TAG_PCM_RIGHT;
+ }
+ if (chan_mask & SP_FR) {
+ dst->ac97_pcm_l = *src++;
+ dst->ac97_tag |= TAG_PCM_LEFT;
+
+ }
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ if (chan_mask & SP_SR) {
+ dst->ac97_sl = *src++;
+ dst->ac97_tag |= TAG_PCM_SL;
+ }
+ if (chan_mask & SP_SL) {
+ dst->ac97_sr = *src++;
+ dst->ac97_tag |= TAG_PCM_SR;
+ }
+ if (chan_mask & SP_LFE) {
+ dst->ac97_lfe = *src++;
+ dst->ac97_tag |= TAG_PCM_LFE;
+ }
+ if (chan_mask & SP_FC) {
+ dst->ac97_center = *src++;
+ dst->ac97_tag |= TAG_PCM_CENTER;
+ }
+#endif
+ dst++;
}
}
EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
size_t count)
{
- while (count--)
- *(dst++) = (src++)->ac97_pcm;
+ while (count--) {
+ *(dst++) = src->ac97_pcm_l;
+ *(dst++) = src->ac97_pcm_r;
+ src++;
+ }
}
EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
static unsigned int sport_tx_curr_frag(struct sport_device *sport)
{
- return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
+ return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
sport->tx_fragsize;
}
@@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
sport_incfrag(sport, &nextfrag, 1);
- nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+ nextwrite = (struct ac97_frame *)(sport->tx_buf +
nextfrag * sport->tx_fragsize);
pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
@@ -237,8 +269,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
EXPORT_SYMBOL_GPL(soc_ac97_ops);
#ifdef CONFIG_PM
-static int bf5xx_ac97_suspend(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport =
(struct sport_device *)dai->private_data;
@@ -253,8 +284,7 @@ static int bf5xx_ac97_suspend(struct platform_device *pdev,
return 0;
}
-static int bf5xx_ac97_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport =
@@ -297,20 +327,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev,
static int bf5xx_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- int ret;
-#if defined(CONFIG_BF54x)
- u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
- PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
-#else
- u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
-#endif
+ int ret = 0;
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
if (cmd_count == NULL)
return -ENOMEM;
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
- return -EFAULT;
+ ret = -EFAULT;
+ goto peripheral_err;
}
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
@@ -318,54 +343,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
pr_err("Failed to request GPIO_%d for reset\n",
CONFIG_SND_BF5XX_RESET_GPIO_NUM);
- peripheral_free_list(&sport_req[sport_num][0]);
- return -1;
+ ret = -1;
+ goto gpio_err;
}
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
#endif
sport_handle = sport_init(&sport_params[sport_num], 2, \
sizeof(struct ac97_frame), NULL);
if (!sport_handle) {
- peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
- return -ENODEV;
+ ret = -ENODEV;
+ goto sport_err;
}
/*SPORT works in TDM mode to simulate AC97 transfers*/
ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
if (ret) {
pr_err("SPORT is busy!\n");
- kfree(sport_handle);
- peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
- return -EBUSY;
+ ret = -EBUSY;
+ goto sport_config_err;
}
ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
- kfree(sport_handle);
- peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
- return -EBUSY;
+ ret = -EBUSY;
+ goto sport_config_err;
}
ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
if (ret) {
pr_err("SPORT is busy!\n");
- kfree(sport_handle);
- peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
- gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
- return -EBUSY;
+ ret = -EBUSY;
+ goto sport_config_err;
}
+
return 0;
+
+sport_config_err:
+ kfree(sport_handle);
+sport_err:
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+ gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+gpio_err:
+ peripheral_free_list(&sport_req[sport_num][0]);
+peripheral_err:
+ free_page((unsigned long)cmd_count);
+ cmd_count = NULL;
+
+ return ret;
}
static void bf5xx_ac97_remove(struct platform_device *pdev,
@@ -373,6 +398,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
{
free_page((unsigned long)cmd_count);
cmd_count = NULL;
+ peripheral_free_list(&sport_req[sport_num][0]);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
@@ -381,7 +407,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai bfin_ac97_dai = {
.name = "bf5xx-ac97",
.id = 0,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.probe = bf5xx_ac97_probe,
.remove = bf5xx_ac97_remove,
.suspend = bf5xx_ac97_suspend,
@@ -389,7 +415,11 @@ struct snd_soc_dai bfin_ac97_dai = {
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ .channels_max = 6,
+#else
.channels_max = 2,
+#endif
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
@@ -401,6 +431,18 @@ struct snd_soc_dai bfin_ac97_dai = {
};
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
+static int __init bfin_ac97_init(void)
+{
+ return snd_soc_register_dai(&bfin_ac97_dai);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+ snd_soc_unregister_dai(&bfin_ac97_dai);
+}
+module_exit(bfin_ac97_exit);
+
MODULE_AUTHOR("Roy Huang");
MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
index 3f77cc558dc0..3f2a911fe0cb 100644
--- a/sound/soc/blackfin/bf5xx-ac97.h
+++ b/sound/soc/blackfin/bf5xx-ac97.h
@@ -16,21 +16,46 @@ struct ac97_frame {
u16 ac97_tag; /* slot 0 */
u16 ac97_addr; /* slot 1 */
u16 ac97_data; /* slot 2 */
- u32 ac97_pcm; /* slot 3 and 4: left and right pcm data */
+ u16 ac97_pcm_l; /*slot 3:front left*/
+ u16 ac97_pcm_r; /*slot 4:front left*/
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+ u16 ac97_mdm_l1;
+ u16 ac97_center; /*slot 6:center*/
+ u16 ac97_sl; /*slot 7:surround left*/
+ u16 ac97_sr; /*slot 8:surround right*/
+ u16 ac97_lfe; /*slot 9:lfe*/
+#endif
} __attribute__ ((packed));
+/* Speaker location */
+#define SP_FL 0x0001
+#define SP_FR 0x0010
+#define SP_FC 0x0002
+#define SP_LFE 0x0020
+#define SP_SL 0x0004
+#define SP_SR 0x0040
+
+#define SP_STEREO (SP_FL | SP_FR)
+#define SP_2DOT1 (SP_FL | SP_FR | SP_LFE)
+#define SP_QUAD (SP_FL | SP_FR | SP_SL | SP_SR)
+#define SP_5DOT1 (SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
+
#define TAG_VALID 0x8000
#define TAG_CMD 0x6000
#define TAG_PCM_LEFT 0x1000
#define TAG_PCM_RIGHT 0x0800
-#define TAG_PCM (TAG_PCM_LEFT | TAG_PCM_RIGHT)
+#define TAG_PCM_MDM_L1 0x0400
+#define TAG_PCM_CENTER 0x0200
+#define TAG_PCM_SL 0x0100
+#define TAG_PCM_SR 0x0080
+#define TAG_PCM_LFE 0x0040
extern struct snd_soc_dai bfin_ac97_dai;
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
- size_t count);
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
+ size_t count, unsigned int chan_mask);
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
size_t count);
#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index 124425d22320..d8f591273778 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -43,7 +43,7 @@
#include "bf5xx-ac97-pcm.h"
#include "bf5xx-ac97.h"
-static struct snd_soc_machine bf5xx_board;
+static struct snd_soc_card bf5xx_board;
static int bf5xx_board_startup(struct snd_pcm_substream *substream)
{
@@ -67,15 +67,15 @@ static struct snd_soc_dai_link bf5xx_board_dai = {
.ops = &bf5xx_board_ops,
};
-static struct snd_soc_machine bf5xx_board = {
+static struct snd_soc_card bf5xx_board = {
.name = "bf5xx-board",
+ .platform = &bf5xx_ac97_soc_platform,
.dai_link = &bf5xx_board_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_board_snd_devdata = {
- .machine = &bf5xx_board,
- .platform = &bf5xx_ac97_soc_platform,
+ .card = &bf5xx_board,
.codec_dev = &soc_codec_dev_ad1980,
};
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index 622c9b909532..7f2a5e199075 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -65,7 +65,7 @@
#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
-static struct snd_soc_machine bf5xx_ad73311;
+static struct snd_soc_card bf5xx_ad73311;
static int snd_ad73311_startup(void)
{
@@ -168,7 +168,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
params_format(params));
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
@@ -190,16 +190,16 @@ static struct snd_soc_dai_link bf5xx_ad73311_dai = {
.ops = &bf5xx_ad73311_ops,
};
-static struct snd_soc_machine bf5xx_ad73311 = {
+static struct snd_soc_card bf5xx_ad73311 = {
.name = "bf5xx_ad73311",
+ .platform = &bf5xx_i2s_soc_platform,
.probe = bf5xx_probe,
.dai_link = &bf5xx_ad73311_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
- .machine = &bf5xx_ad73311,
- .platform = &bf5xx_i2s_soc_platform,
+ .card = &bf5xx_ad73311,
.codec_dev = &soc_codec_dev_ad73311,
};
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 61fccf925192..53d290b3ea47 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -283,6 +283,18 @@ struct snd_soc_platform bf5xx_i2s_soc_platform = {
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
+static int __init bfin_i2s_init(void)
+{
+ return snd_soc_register_platform(&bf5xx_i2s_soc_platform);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+ snd_soc_unregister_platform(&bf5xx_i2s_soc_platform);
+}
+module_exit(bfin_i2s_exit);
+
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index e020c160ee44..d1d95d2393fe 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -132,7 +132,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
return ret;
}
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
+static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
@@ -142,7 +143,8 @@ static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
}
static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
int ret = 0;
@@ -193,7 +195,8 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
bf5xx_i2s.counter--;
@@ -219,16 +222,14 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
return 0;
}
-static void bf5xx_i2s_remove(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
peripheral_free_list(&sport_req[sport_num][0]);
}
#ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct platform_device *dev,
- struct snd_soc_dai *dai)
+static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport =
(struct sport_device *)dai->private_data;
@@ -289,7 +290,6 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai bf5xx_i2s_dai = {
.name = "bf5xx-i2s",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.probe = bf5xx_i2s_probe,
.remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
@@ -307,13 +307,24 @@ struct snd_soc_dai bf5xx_i2s_dai = {
.ops = {
.startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
- .hw_params = bf5xx_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
},
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
+static int __init bfin_i2s_init(void)
+{
+ return snd_soc_register_dai(&bf5xx_i2s_dai);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+ snd_soc_unregister_dai(&bf5xx_i2s_dai);
+}
+module_exit(bfin_i2s_exit);
+
/* Module information */
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index fcadcc081f7f..2e63dea73e9c 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -116,7 +116,7 @@ struct sport_device {
void *err_data;
unsigned char *tx_dma_buf;
unsigned char *rx_dma_buf;
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#ifdef CONFIG_SND_BF5XX_MMAP_SUPPORT
dma_addr_t tx_dma_phy;
dma_addr_t rx_dma_phy;
int tx_pos;/*pcm sample count*/
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index e15f67fd7769..bc0cdded7116 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -44,7 +44,7 @@
#include "bf5xx-i2s-pcm.h"
#include "bf5xx-i2s.h"
-static struct snd_soc_machine bf5xx_ssm2602;
+static struct snd_soc_card bf5xx_ssm2602;
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
{
@@ -92,17 +92,17 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
*/
/* set codec DAI configuration */
- ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (ret < 0)
return ret;
- ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+ ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
@@ -135,15 +135,15 @@ static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
.i2c_address = 0x1b,
};
-static struct snd_soc_machine bf5xx_ssm2602 = {
+static struct snd_soc_card bf5xx_ssm2602 = {
.name = "bf5xx_ssm2602",
+ .platform = &bf5xx_i2s_soc_platform,
.dai_link = &bf5xx_ssm2602_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
- .machine = &bf5xx_ssm2602,
- .platform = &bf5xx_i2s_soc_platform,
+ .card = &bf5xx_ssm2602,
.codec_dev = &soc_codec_dev_ssm2602,
.codec_data = &bf5xx_ssm2602_setup,
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 38a0e3b620a7..c41289b5f586 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,31 +1,40 @@
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
- depends on I2C
- select SPI
- select SPI_MASTER
- select SND_SOC_AD73311
- select SND_SOC_AK4535
- select SND_SOC_CS4270
- select SND_SOC_SSM2602
- select SND_SOC_TLV320AIC23
- select SND_SOC_TLV320AIC26
- select SND_SOC_TLV320AIC3X
- select SND_SOC_UDA1380
- select SND_SOC_WM8510
- select SND_SOC_WM8580
- select SND_SOC_WM8731
- select SND_SOC_WM8750
- select SND_SOC_WM8753
- select SND_SOC_WM8900
- select SND_SOC_WM8903
- select SND_SOC_WM8971
- select SND_SOC_WM8990
+ select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+ select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+ select SND_SOC_AD73311 if I2C
+ select SND_SOC_AK4535 if I2C
+ select SND_SOC_CS4270 if I2C
+ select SND_SOC_PCM3008
+ select SND_SOC_SSM2602 if I2C
+ select SND_SOC_TLV320AIC23 if I2C
+ select SND_SOC_TLV320AIC26 if SPI_MASTER
+ select SND_SOC_TLV320AIC3X if I2C
+ select SND_SOC_TWL4030 if TWL4030_CORE
+ select SND_SOC_UDA134X
+ select SND_SOC_UDA1380 if I2C
+ select SND_SOC_WM8350 if MFD_WM8350
+ select SND_SOC_WM8510 if (I2C || SPI_MASTER)
+ select SND_SOC_WM8580 if I2C
+ select SND_SOC_WM8728 if (I2C || SPI_MASTER)
+ select SND_SOC_WM8731 if (I2C || SPI_MASTER)
+ select SND_SOC_WM8750 if (I2C || SPI_MASTER)
+ select SND_SOC_WM8753 if (I2C || SPI_MASTER)
+ select SND_SOC_WM8900 if I2C
+ select SND_SOC_WM8903 if I2C
+ select SND_SOC_WM8971 if I2C
+ select SND_SOC_WM8990 if I2C
+ select SND_SOC_WM9712 if SND_SOC_AC97_BUS
+ select SND_SOC_WM9713 if SND_SOC_AC97_BUS
help
Normally ASoC codec drivers are only built if a machine driver which
uses them is also built since they are only usable with a machine
driver. Selecting this option will allow these drivers to be built
without an explicit machine driver for test and development purposes.
+ Support for the bus types used to access the codecs to be built must
+ be selected separately.
+
If unsure select "N".
@@ -60,6 +69,12 @@ config SND_SOC_CS4270_VD33_ERRATA
bool
depends on SND_SOC_CS4270
+config SND_SOC_L3
+ tristate
+
+config SND_SOC_PCM3008
+ tristate
+
config SND_SOC_SSM2602
tristate
@@ -75,15 +90,29 @@ config SND_SOC_TLV320AIC3X
tristate
depends on I2C
+config SND_SOC_TWL4030
+ tristate
+ depends on TWL4030_CORE
+
+config SND_SOC_UDA134X
+ tristate
+ select SND_SOC_L3
+
config SND_SOC_UDA1380
tristate
+config SND_SOC_WM8350
+ tristate
+
config SND_SOC_WM8510
tristate
config SND_SOC_WM8580
tristate
+config SND_SOC_WM8728
+ tristate
+
config SND_SOC_WM8731
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 90f0a585fc70..c4ddc9aa2bbd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,13 +3,19 @@ snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-cs4270-objs := cs4270.o
+snd-soc-l3-objs := l3.o
+snd-soc-pcm3008-objs := pcm3008.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-twl4030-objs := twl4030.o
+snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8728-objs := wm8728.o
snd-soc-wm8731-objs := wm8731.o
snd-soc-wm8750-objs := wm8750.o
snd-soc-wm8753-objs := wm8753.o
@@ -25,13 +31,19 @@ obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
+obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index bd1ebdc6c86c..fb53e6511af2 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -24,7 +24,8 @@
#define AC97_VERSION "0.6"
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -42,7 +43,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
struct snd_soc_dai ac97_dai = {
.name = "AC97 HiFi",
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
@@ -113,7 +114,7 @@ static int ac97_soc_probe(struct platform_device *pdev)
if (ret < 0)
goto bus_err;
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0)
goto bus_err;
return 0;
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 1397b8e06c0b..73fdbb4d4a3d 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -85,6 +85,9 @@ SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
+SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
+
SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
@@ -142,10 +145,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
struct snd_soc_dai ad1980_dai = {
.name = "AC97",
+ .ac97_control = 1,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 6,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
.capture = {
@@ -192,6 +196,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
struct snd_soc_codec *codec;
int ret = 0;
u16 vendor_id2;
+ u16 ext_status;
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
@@ -234,7 +239,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
ret = ad1980_reset(codec, 0);
if (ret < 0) {
- printk(KERN_ERR "AC97 link error\n");
+ printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
goto reset_err;
}
@@ -253,12 +258,19 @@ static int ad1980_soc_probe(struct platform_device *pdev)
"supported\n");
}
- ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
- ac97_write(codec, AC97_PCM, 0x0000); /* unmute PCM out volume */
- ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
+ /* unmute captures and playbacks volume */
+ ac97_write(codec, AC97_MASTER, 0x0000);
+ ac97_write(codec, AC97_PCM, 0x0000);
+ ac97_write(codec, AC97_REC_GAIN, 0x0000);
+ ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+ ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+
+ /*power on LFE/CENTER/Surround DACs*/
+ ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
+ ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
ad1980_add_controls(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register card\n");
goto reset_err;
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index 37af8607b00a..b09289a1e55a 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -8,14 +8,10 @@
* 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.
- *
- * Revision history
- * 25th Sep 2008 Initial version.
*/
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -68,7 +64,7 @@ static int ad73311_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad73311: failed to register card\n");
goto register_err;
@@ -102,6 +98,18 @@ struct snd_soc_codec_device soc_codec_dev_ad73311 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
+static int __init ad73311_init(void)
+{
+ return snd_soc_register_dai(&ad73311_dai);
+}
+module_init(ad73311_init);
+
+static void __exit ad73311_exit(void)
+{
+ snd_soc_unregister_dai(&ad73311_dai);
+}
+module_exit(ad73311_exit);
+
MODULE_DESCRIPTION("ASoC ad73311 driver");
MODULE_AUTHOR("Cliff Cai ");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 2a89b5888e11..81300d8d42ca 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -339,7 +339,8 @@ static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
static int ak4535_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -451,8 +452,6 @@ struct snd_soc_dai ak4535_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = ak4535_hw_params,
- },
- .dai_ops = {
.set_fmt = ak4535_set_dai_fmt,
.digital_mute = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
@@ -513,7 +512,7 @@ static int ak4535_init(struct snd_soc_device *socdev)
ak4535_add_controls(codec);
ak4535_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ak4535: failed to register card\n");
goto card_err;
@@ -689,6 +688,18 @@ struct snd_soc_codec_device soc_codec_dev_ak4535 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
+static int __init ak4535_modinit(void)
+{
+ return snd_soc_register_dai(&ak4535_dai);
+}
+module_init(ak4535_modinit);
+
+static void __exit ak4535_exit(void)
+{
+ snd_soc_unregister_dai(&ak4535_dai);
+}
+module_exit(ak4535_exit);
+
MODULE_DESCRIPTION("Soc AK4535 driver");
MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 0bbd94501d7e..f1aa0c34421c 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -360,13 +360,14 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
/*
* Program the CS4270 with the given hardware parameters.
*
- * The .dai_ops functions are used to provide board-specific data, like
+ * The .ops functions are used to provide board-specific data, like
* input frequencies, to this driver. This function takes that information,
* combines it with the hardware parameters provided, and programs the
* hardware accordingly.
*/
static int cs4270_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -450,6 +451,19 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ /* Disable automatic volume control. It's enabled by default, and
+ * it causes volume change commands to be delayed, sometimes until
+ * after playback has started.
+ */
+
+ reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
+ reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
+ ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
+ if (ret < 0) {
+ printk(KERN_ERR "I2C write failed\n");
+ return ret;
+ }
+
/* Thaw and power-up the codec */
ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
@@ -697,10 +711,10 @@ static int cs4270_probe(struct platform_device *pdev)
if (codec->control_data) {
/* Initialize codec ops */
cs4270_dai.ops.hw_params = cs4270_hw_params;
- cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
- cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
+ cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
+ cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
- cs4270_dai.dai_ops.digital_mute = cs4270_mute;
+ cs4270_dai.ops.digital_mute = cs4270_mute;
#endif
} else
printk(KERN_INFO "cs4270: no I2C device found, "
@@ -709,7 +723,7 @@ static int cs4270_probe(struct platform_device *pdev)
printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
#endif
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to register card\n");
goto error_del_driver;
@@ -760,6 +774,18 @@ struct snd_soc_codec_device soc_codec_device_cs4270 = {
};
EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
+static int __init cs4270_init(void)
+{
+ return snd_soc_register_dai(&cs4270_dai);
+}
+module_init(cs4270_init);
+
+static void __exit cs4270_exit(void)
+{
+ snd_soc_unregister_dai(&cs4270_dai);
+}
+module_exit(cs4270_exit);
+
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
new file mode 100644
index 000000000000..5353af58862c
--- /dev/null
+++ b/sound/soc/codecs/l3.c
@@ -0,0 +1,91 @@
+/*
+ * L3 code
+ *
+ * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.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.
+ *
+ *
+ * based on:
+ *
+ * L3 bus algorithm module.
+ *
+ * Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <sound/l3.h>
+
+/*
+ * Send one byte of data to the chip. Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static void sendbyte(struct l3_pins *adap, unsigned int byte)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ adap->setclk(0);
+ udelay(adap->data_hold);
+ adap->setdat(byte & 1);
+ udelay(adap->data_setup);
+ adap->setclk(1);
+ udelay(adap->clock_high);
+ byte >>= 1;
+ }
+}
+
+/*
+ * Send a set of bytes to the chip. We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void sendbytes(struct l3_pins *adap, const u8 *buf,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i) {
+ udelay(adap->mode_hold);
+ adap->setmode(0);
+ udelay(adap->mode);
+ }
+ adap->setmode(1);
+ udelay(adap->mode_setup);
+ sendbyte(adap, buf[i]);
+ }
+}
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
+{
+ adap->setclk(1);
+ adap->setdat(1);
+ adap->setmode(1);
+ udelay(adap->mode);
+
+ adap->setmode(0);
+ udelay(adap->mode_setup);
+ sendbyte(adap, addr);
+ udelay(adap->mode_hold);
+
+ sendbytes(adap, data, len);
+
+ adap->setclk(1);
+ adap->setdat(1);
+ adap->setmode(0);
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(l3_write);
+
+MODULE_DESCRIPTION("L3 bit-banging driver");
+MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
new file mode 100644
index 000000000000..9a3e67e5319c
--- /dev/null
+++ b/sound/soc/codecs/pcm3008.c
@@ -0,0 +1,212 @@
+/*
+ * ALSA Soc PCM3008 codec support
+ *
+ * Author: Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on AC97 Soc codec, original copyright follow:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * Generic PCM3008 support.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "pcm3008.h"
+
+#define PCM3008_VERSION "0.2"
+
+#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai pcm3008_dai = {
+ .name = "PCM3008 HiFi",
+ .playback = {
+ .stream_name = "PCM3008 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM3008_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "PCM3008 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PCM3008_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+EXPORT_SYMBOL_GPL(pcm3008_dai);
+
+static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
+{
+ gpio_free(setup->dem0_pin);
+ gpio_free(setup->dem1_pin);
+ gpio_free(setup->pdad_pin);
+ gpio_free(setup->pdda_pin);
+}
+
+static int pcm3008_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct pcm3008_setup_data *setup = socdev->codec_data;
+ int ret = 0;
+
+ printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (!socdev->codec)
+ return -ENOMEM;
+
+ codec = socdev->codec;
+ mutex_init(&codec->mutex);
+
+ codec->name = "PCM3008";
+ codec->owner = THIS_MODULE;
+ codec->dai = &pcm3008_dai;
+ codec->num_dai = 1;
+ codec->write = NULL;
+ codec->read = NULL;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* Register PCMs. */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "pcm3008: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* Register Card. */
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "pcm3008: failed to register card\n");
+ goto card_err;
+ }
+
+ /* DEM1 DEM0 DE-EMPHASIS_MODE
+ * Low Low De-emphasis 44.1 kHz ON
+ * Low High De-emphasis OFF
+ * High Low De-emphasis 48 kHz ON
+ * High High De-emphasis 32 kHz ON
+ */
+
+ /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
+ ret = gpio_request(setup->dem0_pin, "codec_dem0");
+ if (ret == 0)
+ ret = gpio_direction_output(setup->dem0_pin, 1);
+ if (ret != 0)
+ goto gpio_err;
+
+ /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
+ ret = gpio_request(setup->dem1_pin, "codec_dem1");
+ if (ret == 0)
+ ret = gpio_direction_output(setup->dem1_pin, 0);
+ if (ret != 0)
+ goto gpio_err;
+
+ /* Configure PDAD GPIO. */
+ ret = gpio_request(setup->pdad_pin, "codec_pdad");
+ if (ret == 0)
+ ret = gpio_direction_output(setup->pdad_pin, 1);
+ if (ret != 0)
+ goto gpio_err;
+
+ /* Configure PDDA GPIO. */
+ ret = gpio_request(setup->pdda_pin, "codec_pdda");
+ if (ret == 0)
+ ret = gpio_direction_output(setup->pdda_pin, 1);
+ if (ret != 0)
+ goto gpio_err;
+
+ return ret;
+
+gpio_err:
+ pcm3008_gpio_free(setup);
+card_err:
+ snd_soc_free_pcms(socdev);
+pcm_err:
+ kfree(socdev->codec);
+
+ return ret;
+}
+
+static int pcm3008_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct pcm3008_setup_data *setup = socdev->codec_data;
+
+ if (!codec)
+ return 0;
+
+ pcm3008_gpio_free(setup);
+ snd_soc_free_pcms(socdev);
+ kfree(socdev->codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcm3008_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct pcm3008_setup_data *setup = socdev->codec_data;
+
+ gpio_set_value(setup->pdad_pin, 0);
+ gpio_set_value(setup->pdda_pin, 0);
+
+ return 0;
+}
+
+static int pcm3008_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct pcm3008_setup_data *setup = socdev->codec_data;
+
+ gpio_set_value(setup->pdad_pin, 1);
+ gpio_set_value(setup->pdda_pin, 1);
+
+ return 0;
+}
+#else
+#define pcm3008_soc_suspend NULL
+#define pcm3008_soc_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_pcm3008 = {
+ .probe = pcm3008_soc_probe,
+ .remove = pcm3008_soc_remove,
+ .suspend = pcm3008_soc_suspend,
+ .resume = pcm3008_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_pcm3008);
+
+static int __init pcm3008_init(void)
+{
+ return snd_soc_register_dai(&pcm3008_dai);
+}
+module_init(pcm3008_init);
+
+static void __exit pcm3008_exit(void)
+{
+ snd_soc_unregister_dai(&pcm3008_dai);
+}
+module_exit(pcm3008_exit);
+
+MODULE_DESCRIPTION("Soc PCM3008 driver");
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
new file mode 100644
index 000000000000..d04e87d3c060
--- /dev/null
+++ b/sound/soc/codecs/pcm3008.h
@@ -0,0 +1,25 @@
+/*
+ * PCM3008 ALSA SoC Layer
+ *
+ * Author: Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_SOC_PCM3008_H
+#define __LINUX_SND_SOC_PCM3008_H
+
+struct pcm3008_setup_data {
+ unsigned dem0_pin;
+ unsigned dem1_pin;
+ unsigned pdad_pin;
+ unsigned pdda_pin;
+};
+
+extern struct snd_soc_codec_device soc_codec_dev_pcm3008;
+extern struct snd_soc_dai pcm3008_dai;
+
+#endif
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 44ef0dacd564..cac373616768 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -285,16 +285,23 @@ static inline int get_coeff(int mclk, int rate)
}
static int ssm2602_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
int i = get_coeff(ssm2602->sysclk, params_rate(params));
+ if (substream == ssm2602->slave_substream) {
+ dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+ return 0;
+ }
+
/*no match is found*/
if (i == ARRAY_SIZE(coeff_div))
return -EINVAL;
@@ -324,19 +331,26 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_startup(struct snd_pcm_substream *substream)
+static int ssm2602_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
+ struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
/* The DAI has shared clocks so if we already have a playback or
* capture going then constrain this substream to match it.
+ * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
*/
if (ssm2602->master_substream) {
master_runtime = ssm2602->master_substream->runtime;
+ dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+ master_runtime->sample_bits,
+ master_runtime->rate);
+
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_RATE,
master_runtime->rate,
@@ -354,7 +368,8 @@ static int ssm2602_startup(struct snd_pcm_substream *substream)
return 0;
}
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
+static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -365,14 +380,21 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void ssm2602_shutdown(struct snd_pcm_substream *substream)
+static void ssm2602_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
+ struct ssm2602_priv *ssm2602 = codec->private_data;
/* deactivate */
if (!codec->active)
ssm2602_write(codec, SSM2602_ACTIVE, 0);
+
+ if (ssm2602->master_substream == substream)
+ ssm2602->master_substream = ssm2602->slave_substream;
+
+ ssm2602->slave_substream = NULL;
}
static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
@@ -432,10 +454,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
iface |= 0x0001;
break;
case SND_SOC_DAIFMT_DSP_A:
- iface |= 0x0003;
+ iface |= 0x0013;
break;
case SND_SOC_DAIFMT_DSP_B:
- iface |= 0x0013;
+ iface |= 0x0003;
break;
default:
return -EINVAL;
@@ -496,6 +518,9 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000)
+#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
struct snd_soc_dai ssm2602_dai = {
.name = "SSM2602",
.playback = {
@@ -503,20 +528,18 @@ struct snd_soc_dai ssm2602_dai = {
.channels_min = 2,
.channels_max = 2,
.rates = SSM2602_RATES,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .formats = SSM2602_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SSM2602_RATES,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+ .formats = SSM2602_FORMATS,},
.ops = {
.startup = ssm2602_startup,
.prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
- },
- .dai_ops = {
.digital_mute = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
@@ -601,7 +624,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
ssm2602_add_controls(codec);
ssm2602_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
pr_err("ssm2602: failed to register card\n");
goto card_err;
@@ -770,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
+static int __init ssm2602_modinit(void)
+{
+ return snd_soc_register_dai(&ssm2602_dai);
+}
+module_init(ssm2602_modinit);
+
+static void __exit ssm2602_exit(void)
+{
+ snd_soc_unregister_dai(&ssm2602_dai);
+}
+module_exit(ssm2602_exit);
+
MODULE_DESCRIPTION("ASoC ssm2602 driver");
MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 44308dac9e18..cfdea007c4cb 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -37,12 +37,6 @@
#define AIC23_VERSION "0.1"
-struct tlv320aic23_srate_reg_info {
- u32 sample_rate;
- u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
- u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
-};
-
/*
* AIC23 register cache
*/
@@ -261,20 +255,156 @@ static const struct snd_soc_dapm_route intercon[] = {
};
-/* tlv320aic23 related */
-static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
- {4000, 0x06, 1}, /* 4000 */
- {8000, 0x06, 0}, /* 8000 */
- {16000, 0x0C, 1}, /* 16000 */
- {22050, 0x11, 1}, /* 22050 */
- {24000, 0x00, 1}, /* 24000 */
- {32000, 0x0C, 0}, /* 32000 */
- {44100, 0x11, 0}, /* 44100 */
- {48000, 0x00, 0}, /* 48000 */
- {88200, 0x1F, 0}, /* 88200 */
- {96000, 0x0E, 0}, /* 96000 */
+/* AIC23 driver data */
+struct aic23 {
+ struct snd_soc_codec codec;
+ int mclk;
+ int requested_adc;
+ int requested_dac;
+};
+
+/*
+ * Common Crystals used
+ * 11.2896 Mhz /128 = *88.2k /192 = 58.8k
+ * 12.0000 Mhz /125 = *96k /136 = 88.235K
+ * 12.2880 Mhz /128 = *96k /192 = 64k
+ * 16.9344 Mhz /128 = 132.3k /192 = *88.2k
+ * 18.4320 Mhz /128 = 144k /192 = *96k
+ */
+
+/*
+ * Normal BOSR 0-256/2 = 128, 1-384/2 = 192
+ * USB BOSR 0-250/2 = 125, 1-272/2 = 136
+ */
+static const int bosr_usb_divisor_table[] = {
+ 128, 125, 192, 136
+};
+#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
+#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15))
+static const unsigned short sr_valid_mask[] = {
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/
+ LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/
+ LOWER_GROUP, /* Usb, bosr - 0*/
+ UPPER_GROUP, /* Usb, bosr - 1*/
+};
+/*
+ * Every divisor is a factor of 11*12
+ */
+#define SR_MULT (11*12)
+#define A(x) (x) ? (SR_MULT/x) : 0
+static const unsigned char sr_adc_mult_table[] = {
+ A(2), A(2), A(12), A(12), A(0), A(0), A(3), A(1),
+ A(2), A(2), A(11), A(11), A(0), A(0), A(0), A(1)
+};
+static const unsigned char sr_dac_mult_table[] = {
+ A(2), A(12), A(2), A(12), A(0), A(0), A(3), A(1),
+ A(2), A(11), A(2), A(11), A(0), A(0), A(0), A(1)
};
+static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
+ int dac, int dac_l, int dac_h, int need_dac)
+{
+ if ((adc >= adc_l) && (adc <= adc_h) &&
+ (dac >= dac_l) && (dac <= dac_h)) {
+ int diff_adc = need_adc - adc;
+ int diff_dac = need_dac - dac;
+ return abs(diff_adc) + abs(diff_dac);
+ }
+ return UINT_MAX;
+}
+
+static int find_rate(int mclk, u32 need_adc, u32 need_dac)
+{
+ int i, j;
+ int best_i = -1;
+ int best_j = -1;
+ int best_div = 0;
+ unsigned best_score = UINT_MAX;
+ int adc_l, adc_h, dac_l, dac_h;
+
+ need_adc *= SR_MULT;
+ need_dac *= SR_MULT;
+ /*
+ * rates given are +/- 1/32
+ */
+ adc_l = need_adc - (need_adc >> 5);
+ adc_h = need_adc + (need_adc >> 5);
+ dac_l = need_dac - (need_dac >> 5);
+ dac_h = need_dac + (need_dac >> 5);
+ for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
+ int base = mclk / bosr_usb_divisor_table[i];
+ int mask = sr_valid_mask[i];
+ for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
+ j++, mask >>= 1) {
+ int adc;
+ int dac;
+ int score;
+ if ((mask & 1) == 0)
+ continue;
+ adc = base * sr_adc_mult_table[j];
+ dac = base * sr_dac_mult_table[j];
+ score = get_score(adc, adc_l, adc_h, need_adc,
+ dac, dac_l, dac_h, need_dac);
+ if (best_score > score) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 0;
+ }
+ score = get_score((adc >> 1), adc_l, adc_h, need_adc,
+ (dac >> 1), dac_l, dac_h, need_dac);
+ /* prefer to have a /2 */
+ if ((score != UINT_MAX) && (best_score >= score)) {
+ best_score = score;
+ best_i = i;
+ best_j = j;
+ best_div = 1;
+ }
+ }
+ }
+ return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
+}
+
+#ifdef DEBUG
+static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+ u32 *sample_rate_adc, u32 *sample_rate_dac)
+{
+ int src = tlv320aic23_read_reg_cache(codec, TLV320AIC23_SRATE);
+ int sr = (src >> 2) & 0x0f;
+ int val = (mclk / bosr_usb_divisor_table[src & 3]);
+ int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
+ int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
+ if (src & TLV320AIC23_CLKIN_HALF) {
+ adc >>= 1;
+ dac >>= 1;
+ }
+ *sample_rate_adc = adc;
+ *sample_rate_dac = dac;
+}
+#endif
+
+static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+ u32 sample_rate_adc, u32 sample_rate_dac)
+{
+ /* Search for the right sample rate */
+ int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
+ if (data < 0) {
+ printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
+ __func__, sample_rate_adc, sample_rate_dac);
+ return -EINVAL;
+ }
+ tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
+#ifdef DEBUG
+ {
+ u32 adc, dac;
+ get_current_sample_rates(codec, mclk, &adc, &dac);
+ printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
+ adc, dac, data);
+ }
+#endif
+ return 0;
+}
+
static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
@@ -288,32 +418,36 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
}
static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
- u16 iface_reg, data;
- u8 count = 0;
+ u16 iface_reg;
+ int ret;
+ struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+ u32 sample_rate_adc = aic23->requested_adc;
+ u32 sample_rate_dac = aic23->requested_dac;
+ u32 sample_rate = params_rate(params);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aic23->requested_dac = sample_rate_dac = sample_rate;
+ if (!sample_rate_adc)
+ sample_rate_adc = sample_rate;
+ } else {
+ aic23->requested_adc = sample_rate_adc = sample_rate;
+ if (!sample_rate_dac)
+ sample_rate_dac = sample_rate;
+ }
+ ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+ sample_rate_dac);
+ if (ret < 0)
+ return ret;
iface_reg =
tlv320aic23_read_reg_cache(codec,
TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
-
- /* Search for the right sample rate */
- /* Verify what happens if the rate is not supported
- * now it goes to 96Khz */
- while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
- (count < ARRAY_SIZE(srate_reg_info))) {
- count++;
- }
-
- data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
- (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
- TLV320AIC23_USB_CLK_ON;
-
- tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
-
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
@@ -332,7 +466,8 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -344,17 +479,23 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
+ struct aic23 *aic23 = container_of(codec, struct aic23, codec);
/* deactivate */
if (!codec->active) {
udelay(50);
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
}
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ aic23->requested_dac = 0;
+ else
+ aic23->requested_adc = 0;
}
static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
@@ -400,7 +541,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
case SND_SOC_DAIFMT_I2S:
iface_reg |= TLV320AIC23_FOR_I2S;
break;
- case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
iface_reg |= TLV320AIC23_FOR_DSP;
break;
case SND_SOC_DAIFMT_RIGHT_J:
@@ -422,12 +563,9 @@ static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
-
- switch (freq) {
- case 12000000:
- return 0;
- }
- return -EINVAL;
+ struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+ aic23->mclk = freq;
+ return 0;
}
static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
@@ -478,12 +616,10 @@ struct snd_soc_dai tlv320aic23_dai = {
.prepare = tlv320aic23_pcm_prepare,
.hw_params = tlv320aic23_hw_params,
.shutdown = tlv320aic23_shutdown,
- },
- .dai_ops = {
- .digital_mute = tlv320aic23_mute,
- .set_fmt = tlv320aic23_set_dai_fmt,
- .set_sysclk = tlv320aic23_set_dai_sysclk,
- }
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+ }
};
EXPORT_SYMBOL_GPL(tlv320aic23_dai);
@@ -584,7 +720,7 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
tlv320aic23_add_controls(codec);
tlv320aic23_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "tlv320aic23: failed to register card\n");
goto card_err;
@@ -659,14 +795,15 @@ static int tlv320aic23_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
+ struct aic23 *aic23;
int ret = 0;
printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
+ aic23 = kzalloc(sizeof(struct aic23), GFP_KERNEL);
+ if (aic23 == NULL)
return -ENOMEM;
-
+ codec = &aic23->codec;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -687,6 +824,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
+ struct aic23 *aic23 = container_of(codec, struct aic23, codec);
if (codec->control_data)
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -697,7 +835,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
i2c_del_driver(&tlv320aic23_i2c_driver);
#endif
kfree(codec->reg_cache);
- kfree(codec);
+ kfree(aic23);
return 0;
}
@@ -709,6 +847,18 @@ struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
+static int __init tlv320aic23_modinit(void)
+{
+ return snd_soc_register_dai(&tlv320aic23_dai);
+}
+module_init(tlv320aic23_modinit);
+
+static void __exit tlv320aic23_exit(void)
+{
+ snd_soc_unregister_dai(&tlv320aic23_dai);
+}
+module_exit(tlv320aic23_exit);
+
MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index bed8a9e63ddc..29f2f1a017fd 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -125,7 +125,8 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
* Digital Audio Interface Operations
*/
static int aic26_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -287,8 +288,6 @@ struct snd_soc_dai aic26_dai = {
},
.ops = {
.hw_params = aic26_hw_params,
- },
- .dai_ops = {
.digital_mute = aic26_mute,
.set_sysclk = aic26_set_sysclk,
.set_fmt = aic26_set_fmt,
@@ -360,7 +359,7 @@ static int aic26_probe(struct platform_device *pdev)
/* CODEC is setup, we can register the card now */
dev_dbg(&pdev->dev, "Registering card\n");
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(&pdev->dev, "aic26: failed to register card\n");
goto card_err;
@@ -427,7 +426,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
static int aic26_spi_probe(struct spi_device *spi)
{
struct aic26 *aic26;
- int rc, i, reg;
+ int ret, i, reg;
dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
@@ -457,6 +456,14 @@ static int aic26_spi_probe(struct spi_device *spi)
aic26->codec.reg_cache_size = AIC26_NUM_REGS;
aic26->codec.reg_cache = aic26->reg_cache;
+ aic26_dai.dev = &spi->dev;
+ ret = snd_soc_register_dai(&aic26_dai);
+ if (ret != 0) {
+ dev_err(&spi->dev, "Failed to register DAI: %d\n", ret);
+ kfree(aic26);
+ return ret;
+ }
+
/* Reset the codec to power on defaults */
aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
@@ -475,8 +482,8 @@ static int aic26_spi_probe(struct spi_device *spi)
/* Register the sysfs files for debugging */
/* Create SysFS files */
- rc = device_create_file(&spi->dev, &dev_attr_keyclick);
- if (rc)
+ ret = device_create_file(&spi->dev, &dev_attr_keyclick);
+ if (ret)
dev_info(&spi->dev, "error creating sysfs files\n");
#if defined(CONFIG_SND_SOC_OF_SIMPLE)
@@ -493,6 +500,7 @@ static int aic26_spi_remove(struct spi_device *spi)
{
struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
+ snd_soc_unregister_dai(&aic26_dai);
kfree(aic26);
return 0;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index cff276ee261e..b47a749c5ea2 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -253,11 +253,17 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
- SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
- 0x01, 0),
- SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
- PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
- SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+ SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
+ SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
+ SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
+ DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
+ SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
+ 0, 0x7f, 1),
+ SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
+ 0, 0x7f, 1),
+ SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+ LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
+ SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
@@ -272,8 +278,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
0x01, 0),
- SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+ SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
+ SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+ 0, 0x7f, 1),
+ SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
+ 0, 0x7f, 1),
SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
@@ -281,8 +291,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
0x01, 0),
- SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
- PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
+ SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
+ 0, 0x7f, 1),
+ SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
+ 0, 0x7f, 1),
SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
@@ -333,7 +345,8 @@ SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
/* Left DAC_L1 Mixer */
static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", DACL1_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
@@ -341,7 +354,8 @@ static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
/* Right DAC_R1 Mixer */
static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", DACR1_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
@@ -350,14 +364,18 @@ static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
/* Left PGA Mixer */
static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
};
/* Right PGA Mixer */
static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
+ SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
};
@@ -379,34 +397,42 @@ SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
/* Left PGA Bypass Mixer */
static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", PGAL_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPL Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPR Switch", PGAL_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPLCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPRCOM Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0),
};
/* Right PGA Bypass Mixer */
static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", PGAR_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPL Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPR Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPLCOM Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPRCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
};
/* Left Line2 Bypass Mixer */
static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPLCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
};
/* Right Line2 Bypass Mixer */
static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
- SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineL Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("LineR Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
- SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
+ SOC_DAPM_SINGLE("HPRCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
};
static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
@@ -439,22 +465,26 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
/* Mono Output */
SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
- /* Left Inputs to Left ADC */
+ /* Inputs to Left ADC */
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
&aic3x_left_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line1_mux_controls),
+ SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_left_line1_mux_controls),
SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
&aic3x_left_line2_mux_controls),
- /* Right Inputs to Right ADC */
+ /* Inputs to Right ADC */
SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
LINE1R_2_RADC_CTRL, 2, 0),
SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
&aic3x_right_pga_mixer_controls[0],
ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
+ SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
+ &aic3x_right_line1_mux_controls),
SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
&aic3x_right_line1_mux_controls),
SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
@@ -531,7 +561,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left DAC Mux", "DAC_L2", "Left DAC"},
{"Left DAC Mux", "DAC_L3", "Left DAC"},
- {"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
+ {"Left DAC_L1 Mixer", "LineL Switch", "Left DAC Mux"},
+ {"Left DAC_L1 Mixer", "LineR Switch", "Left DAC Mux"},
{"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
{"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
{"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
@@ -557,7 +588,8 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Right DAC Mux", "DAC_R2", "Right DAC"},
{"Right DAC Mux", "DAC_R3", "Right DAC"},
- {"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
+ {"Right DAC_R1 Mixer", "LineL Switch", "Right DAC Mux"},
+ {"Right DAC_R1 Mixer", "LineR Switch", "Right DAC Mux"},
{"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
{"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
{"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
@@ -592,8 +624,10 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left Line2L Mux", "differential", "LINE2L"},
{"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
+ {"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"},
{"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
+ {"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
{"Left ADC", NULL, "Left PGA Mixer"},
{"Left ADC", NULL, "GPIO1 dmic modclk"},
@@ -605,18 +639,23 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Right Line2R Mux", "single-ended", "LINE2R"},
{"Right Line2R Mux", "differential", "LINE2R"},
+ {"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"},
{"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
{"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
+ {"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
{"Right ADC", NULL, "Right PGA Mixer"},
{"Right ADC", NULL, "GPIO1 dmic modclk"},
/* Left PGA Bypass */
- {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "LineL Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "LineR Switch", "Left PGA Mixer"},
{"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
- {"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
- {"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "HPL Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "HPR Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "HPLCOM Switch", "Left PGA Mixer"},
+ {"Left PGA Bypass Mixer", "HPRCOM Switch", "Left PGA Mixer"},
{"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
{"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
@@ -627,10 +666,13 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left HP Out", NULL, "Left PGA Bypass Mixer"},
/* Right PGA Bypass */
- {"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "LineL Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "LineR Switch", "Right PGA Mixer"},
{"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
- {"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
- {"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "HPL Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "HPR Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "HPLCOM Switch", "Right PGA Mixer"},
+ {"Right PGA Bypass Mixer", "HPRCOM Switch", "Right PGA Mixer"},
{"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
{"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
@@ -643,10 +685,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Right HP Out", NULL, "Right PGA Bypass Mixer"},
/* Left Line2 Bypass */
- {"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
+ {"Left Line2 Bypass Mixer", "LineL Switch", "Left Line2L Mux"},
+ {"Left Line2 Bypass Mixer", "LineR Switch", "Left Line2L Mux"},
{"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
{"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
- {"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
+ {"Left Line2 Bypass Mixer", "HPLCOM Switch", "Left Line2L Mux"},
{"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
{"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
@@ -657,10 +700,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
/* Right Line2 Bypass */
- {"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
+ {"Right Line2 Bypass Mixer", "LineL Switch", "Right Line2R Mux"},
+ {"Right Line2 Bypass Mixer", "LineR Switch", "Right Line2R Mux"},
{"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
{"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
- {"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
+ {"Right Line2 Bypass Mixer", "HPRCOM Switch", "Right Line2R Mux"},
{"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
{"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
@@ -694,7 +738,8 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
}
static int aic3x_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -846,6 +891,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
struct aic3x_priv *aic3x = codec->private_data;
u8 iface_areg, iface_breg;
+ int delay = 0;
iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -871,6 +917,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
SND_SOC_DAIFMT_INV_MASK)) {
case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
break;
+ case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+ delay = 1;
case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
iface_breg |= (0x01 << 6);
break;
@@ -887,6 +935,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* set iface */
aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+ aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
return 0;
}
@@ -981,14 +1030,41 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
}
EXPORT_SYMBOL_GPL(aic3x_get_gpio);
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+ int headset_debounce, int button_debounce)
+{
+ u8 val;
+
+ val = ((detect & AIC3X_HEADSET_DETECT_MASK)
+ << AIC3X_HEADSET_DETECT_SHIFT) |
+ ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
+ << AIC3X_HEADSET_DEBOUNCE_SHIFT) |
+ ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
+ << AIC3X_BUTTON_DEBOUNCE_SHIFT);
+
+ if (detect & AIC3X_HEADSET_DETECT_MASK)
+ val |= AIC3X_HEADSET_DETECT_ENABLED;
+
+ aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
+}
+EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
+
int aic3x_headset_detected(struct snd_soc_codec *codec)
{
u8 val;
- aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
- return (val >> 2) & 1;
+ aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+ return (val >> 4) & 1;
}
EXPORT_SYMBOL_GPL(aic3x_headset_detected);
+int aic3x_button_pressed(struct snd_soc_codec *codec)
+{
+ u8 val;
+ aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+ return (val >> 5) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_button_pressed);
+
#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1009,8 +1085,6 @@ struct snd_soc_dai aic3x_dai = {
.formats = AIC3X_FORMATS,},
.ops = {
.hw_params = aic3x_hw_params,
- },
- .dai_ops = {
.digital_mute = aic3x_mute,
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
@@ -1152,7 +1226,7 @@ static int aic3x_init(struct snd_soc_device *socdev)
aic3x_add_controls(codec);
aic3x_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "aic3x: failed to register card\n");
goto card_err;
@@ -1341,6 +1415,18 @@ struct snd_soc_codec_device soc_codec_dev_aic3x = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
+static int __init aic3x_modinit(void)
+{
+ return snd_soc_register_dai(&aic3x_dai);
+}
+module_init(aic3x_modinit);
+
+static void __exit aic3x_exit(void)
+{
+ snd_soc_unregister_dai(&aic3x_dai);
+}
+module_exit(aic3x_exit);
+
MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
MODULE_AUTHOR("Vladimir Barinov");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index 00a195aa02e4..ac827e578c4d 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -35,11 +35,15 @@
#define AIC3X_ASD_INTF_CTRLA 8
/* Audio serial data interface control register B */
#define AIC3X_ASD_INTF_CTRLB 9
+/* Audio serial data interface control register C */
+#define AIC3X_ASD_INTF_CTRLC 10
/* Audio overflow status and PLL R value programming register */
#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11
/* Audio codec digital filter control register */
#define AIC3X_CODEC_DFILT_CTRL 12
-
+/* Headset/button press detection register */
+#define AIC3X_HEADSET_DETECT_CTRL_A 13
+#define AIC3X_HEADSET_DETECT_CTRL_B 14
/* ADC PGA Gain control registers */
#define LADC_VOL 15
#define RADC_VOL 16
@@ -48,7 +52,9 @@
#define MIC3LR_2_RADC_CTRL 18
/* Line1 Input control registers */
#define LINE1L_2_LADC_CTRL 19
+#define LINE1R_2_LADC_CTRL 21
#define LINE1R_2_RADC_CTRL 22
+#define LINE1L_2_RADC_CTRL 24
/* Line2 Input control registers */
#define LINE2L_2_LADC_CTRL 20
#define LINE2R_2_RADC_CTRL 23
@@ -79,6 +85,8 @@
#define LINE2L_2_HPLOUT_VOL 45
#define LINE2R_2_HPROUT_VOL 62
#define PGAL_2_HPLOUT_VOL 46
+#define PGAL_2_HPROUT_VOL 60
+#define PGAR_2_HPLOUT_VOL 49
#define PGAR_2_HPROUT_VOL 63
#define DACL1_2_HPLOUT_VOL 47
#define DACR1_2_HPROUT_VOL 64
@@ -88,6 +96,8 @@
#define LINE2L_2_HPLCOM_VOL 52
#define LINE2R_2_HPRCOM_VOL 69
#define PGAL_2_HPLCOM_VOL 53
+#define PGAR_2_HPLCOM_VOL 56
+#define PGAL_2_HPRCOM_VOL 67
#define PGAR_2_HPRCOM_VOL 70
#define DACL1_2_HPLCOM_VOL 54
#define DACR1_2_HPRCOM_VOL 71
@@ -103,11 +113,17 @@
#define MONOLOPM_CTRL 79
/* Line Output Plus/Minus control registers */
#define LINE2L_2_LLOPM_VOL 80
+#define LINE2L_2_RLOPM_VOL 87
+#define LINE2R_2_LLOPM_VOL 83
#define LINE2R_2_RLOPM_VOL 90
#define PGAL_2_LLOPM_VOL 81
+#define PGAL_2_RLOPM_VOL 88
+#define PGAR_2_LLOPM_VOL 84
#define PGAR_2_RLOPM_VOL 91
#define DACL1_2_LLOPM_VOL 82
+#define DACL1_2_RLOPM_VOL 89
#define DACR1_2_RLOPM_VOL 92
+#define DACR1_2_LLOPM_VOL 85
#define LLOPM_CTRL 86
#define RLOPM_CTRL 93
/* GPIO/IRQ registers */
@@ -221,7 +237,49 @@ enum {
void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+
+/* headset detection / button API */
+
+/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
+ * and cellular headsets (GND + speaker output + microphone input).
+ * It is recommended to enable MIC bias for this function to work properly.
+ * For more information, please refer to the datasheet. */
+enum {
+ AIC3X_HEADSET_DETECT_OFF = 0,
+ AIC3X_HEADSET_DETECT_STEREO = 1,
+ AIC3X_HEADSET_DETECT_CELLULAR = 2,
+ AIC3X_HEADSET_DETECT_BOTH = 3
+};
+
+enum {
+ AIC3X_HEADSET_DEBOUNCE_16MS = 0,
+ AIC3X_HEADSET_DEBOUNCE_32MS = 1,
+ AIC3X_HEADSET_DEBOUNCE_64MS = 2,
+ AIC3X_HEADSET_DEBOUNCE_128MS = 3,
+ AIC3X_HEADSET_DEBOUNCE_256MS = 4,
+ AIC3X_HEADSET_DEBOUNCE_512MS = 5
+};
+
+enum {
+ AIC3X_BUTTON_DEBOUNCE_0MS = 0,
+ AIC3X_BUTTON_DEBOUNCE_8MS = 1,
+ AIC3X_BUTTON_DEBOUNCE_16MS = 2,
+ AIC3X_BUTTON_DEBOUNCE_32MS = 3
+};
+
+#define AIC3X_HEADSET_DETECT_ENABLED 0x80
+#define AIC3X_HEADSET_DETECT_SHIFT 5
+#define AIC3X_HEADSET_DETECT_MASK 3
+#define AIC3X_HEADSET_DEBOUNCE_SHIFT 2
+#define AIC3X_HEADSET_DEBOUNCE_MASK 7
+#define AIC3X_BUTTON_DEBOUNCE_SHIFT 0
+#define AIC3X_BUTTON_DEBOUNCE_MASK 3
+
+/* see the enums above for valid parameters to this function */
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+ int headset_debounce, int button_debounce);
int aic3x_headset_detected(struct snd_soc_codec *codec);
+int aic3x_button_pressed(struct snd_soc_codec *codec);
struct aic3x_setup_data {
int i2c_bus;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
new file mode 100644
index 000000000000..51848880504a
--- /dev/null
+++ b/sound/soc/codecs/twl4030.c
@@ -0,0 +1,1317 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman, <steve@sakoman.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "twl4030.h"
+
+/*
+ * twl4030 register cache & default register settings
+ */
+static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
+ 0x00, /* this register not used */
+ 0x93, /* REG_CODEC_MODE (0x1) */
+ 0xc3, /* REG_OPTION (0x2) */
+ 0x00, /* REG_UNKNOWN (0x3) */
+ 0x00, /* REG_MICBIAS_CTL (0x4) */
+ 0x20, /* REG_ANAMICL (0x5) */
+ 0x00, /* REG_ANAMICR (0x6) */
+ 0x00, /* REG_AVADC_CTL (0x7) */
+ 0x00, /* REG_ADCMICSEL (0x8) */
+ 0x00, /* REG_DIGMIXING (0x9) */
+ 0x0c, /* REG_ATXL1PGA (0xA) */
+ 0x0c, /* REG_ATXR1PGA (0xB) */
+ 0x00, /* REG_AVTXL2PGA (0xC) */
+ 0x00, /* REG_AVTXR2PGA (0xD) */
+ 0x01, /* REG_AUDIO_IF (0xE) */
+ 0x00, /* REG_VOICE_IF (0xF) */
+ 0x00, /* REG_ARXR1PGA (0x10) */
+ 0x00, /* REG_ARXL1PGA (0x11) */
+ 0x6c, /* REG_ARXR2PGA (0x12) */
+ 0x6c, /* REG_ARXL2PGA (0x13) */
+ 0x00, /* REG_VRXPGA (0x14) */
+ 0x00, /* REG_VSTPGA (0x15) */
+ 0x00, /* REG_VRX2ARXPGA (0x16) */
+ 0x0c, /* REG_AVDAC_CTL (0x17) */
+ 0x00, /* REG_ARX2VTXPGA (0x18) */
+ 0x00, /* REG_ARXL1_APGA_CTL (0x19) */
+ 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
+ 0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */
+ 0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */
+ 0x00, /* REG_ATX2ARXPGA (0x1D) */
+ 0x00, /* REG_BT_IF (0x1E) */
+ 0x00, /* REG_BTPGA (0x1F) */
+ 0x00, /* REG_BTSTPGA (0x20) */
+ 0x00, /* REG_EAR_CTL (0x21) */
+ 0x24, /* REG_HS_SEL (0x22) */
+ 0x0a, /* REG_HS_GAIN_SET (0x23) */
+ 0x00, /* REG_HS_POPN_SET (0x24) */
+ 0x00, /* REG_PREDL_CTL (0x25) */
+ 0x00, /* REG_PREDR_CTL (0x26) */
+ 0x00, /* REG_PRECKL_CTL (0x27) */
+ 0x00, /* REG_PRECKR_CTL (0x28) */
+ 0x00, /* REG_HFL_CTL (0x29) */
+ 0x00, /* REG_HFR_CTL (0x2A) */
+ 0x00, /* REG_ALC_CTL (0x2B) */
+ 0x00, /* REG_ALC_SET1 (0x2C) */
+ 0x00, /* REG_ALC_SET2 (0x2D) */
+ 0x00, /* REG_BOOST_CTL (0x2E) */
+ 0x00, /* REG_SOFTVOL_CTL (0x2F) */
+ 0x00, /* REG_DTMF_FREQSEL (0x30) */
+ 0x00, /* REG_DTMF_TONEXT1H (0x31) */
+ 0x00, /* REG_DTMF_TONEXT1L (0x32) */
+ 0x00, /* REG_DTMF_TONEXT2H (0x33) */
+ 0x00, /* REG_DTMF_TONEXT2L (0x34) */
+ 0x00, /* REG_DTMF_TONOFF (0x35) */
+ 0x00, /* REG_DTMF_WANONOFF (0x36) */
+ 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
+ 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
+ 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
+ 0x16, /* REG_APLL_CTL (0x3A) */
+ 0x00, /* REG_DTMF_CTL (0x3B) */
+ 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
+ 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
+ 0x00, /* REG_MISC_SET_1 (0x3E) */
+ 0x00, /* REG_PCMBTMUX (0x3F) */
+ 0x00, /* not used (0x40) */
+ 0x00, /* not used (0x41) */
+ 0x00, /* not used (0x42) */
+ 0x00, /* REG_RX_PATH_SEL (0x43) */
+ 0x00, /* REG_VDL_APGA_CTL (0x44) */
+ 0x00, /* REG_VIBRA_CTL (0x45) */
+ 0x00, /* REG_VIBRA_SET (0x46) */
+ 0x00, /* REG_VIBRA_PWM_SET (0x47) */
+ 0x00, /* REG_ANAMIC_GAIN (0x48) */
+ 0x00, /* REG_MISC_SET_2 (0x49) */
+};
+
+/*
+ * read twl4030 register cache
+ */
+static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+
+ return cache[reg];
+}
+
+/*
+ * write twl4030 register cache
+ */
+static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u8 value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= TWL4030_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the twl4030 register space
+ */
+static int twl4030_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ twl4030_write_reg_cache(codec, reg, value);
+ return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+}
+
+static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
+{
+ u8 mode;
+
+ mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+ mode & ~TWL4030_CODECPDZ);
+
+ /* REVISIT: this delay is present in TI sample drivers */
+ /* but there seems to be no TRM requirement for it */
+ udelay(10);
+}
+
+static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
+{
+ u8 mode;
+
+ mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+ mode | TWL4030_CODECPDZ);
+
+ /* REVISIT: this delay is present in TI sample drivers */
+ /* but there seems to be no TRM requirement for it */
+ udelay(10);
+}
+
+static void twl4030_init_chip(struct snd_soc_codec *codec)
+{
+ int i;
+
+ /* clear CODECPDZ prior to setting register defaults */
+ twl4030_clear_codecpdz(codec);
+
+ /* set all audio section registers to reasonable defaults */
+ for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
+ twl4030_write(codec, i, twl4030_reg[i]);
+
+}
+
+/* Earpiece */
+static const char *twl4030_earpiece_texts[] =
+ {"Off", "DACL1", "DACL2", "Invalid", "DACR1"};
+
+static const struct soc_enum twl4030_earpiece_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1,
+ ARRAY_SIZE(twl4030_earpiece_texts),
+ twl4030_earpiece_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
+SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
+
+/* PreDrive Left */
+static const char *twl4030_predrivel_texts[] =
+ {"Off", "DACL1", "DACL2", "Invalid", "DACR2"};
+
+static const struct soc_enum twl4030_predrivel_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1,
+ ARRAY_SIZE(twl4030_predrivel_texts),
+ twl4030_predrivel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
+SOC_DAPM_ENUM("Route", twl4030_predrivel_enum);
+
+/* PreDrive Right */
+static const char *twl4030_predriver_texts[] =
+ {"Off", "DACR1", "DACR2", "Invalid", "DACL2"};
+
+static const struct soc_enum twl4030_predriver_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1,
+ ARRAY_SIZE(twl4030_predriver_texts),
+ twl4030_predriver_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
+SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
+
+/* Headset Left */
+static const char *twl4030_hsol_texts[] =
+ {"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_hsol_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
+ ARRAY_SIZE(twl4030_hsol_texts),
+ twl4030_hsol_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
+SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
+
+/* Headset Right */
+static const char *twl4030_hsor_texts[] =
+ {"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_hsor_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
+ ARRAY_SIZE(twl4030_hsor_texts),
+ twl4030_hsor_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
+SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+
+/* Carkit Left */
+static const char *twl4030_carkitl_texts[] =
+ {"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_carkitl_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
+ ARRAY_SIZE(twl4030_carkitl_texts),
+ twl4030_carkitl_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
+
+/* Carkit Right */
+static const char *twl4030_carkitr_texts[] =
+ {"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_carkitr_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
+ ARRAY_SIZE(twl4030_carkitr_texts),
+ twl4030_carkitr_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+
+/* Handsfree Left */
+static const char *twl4030_handsfreel_texts[] =
+ {"Voice", "DACL1", "DACL2", "DACR2"};
+
+static const struct soc_enum twl4030_handsfreel_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
+ ARRAY_SIZE(twl4030_handsfreel_texts),
+ twl4030_handsfreel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
+
+/* Handsfree Right */
+static const char *twl4030_handsfreer_texts[] =
+ {"Voice", "DACR1", "DACR2", "DACL2"};
+
+static const struct soc_enum twl4030_handsfreer_enum =
+ SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
+ ARRAY_SIZE(twl4030_handsfreer_texts),
+ twl4030_handsfreer_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int ret = 0;
+ int val;
+
+ switch (e->reg) {
+ case TWL4030_REG_PREDL_CTL:
+ case TWL4030_REG_PREDR_CTL:
+ case TWL4030_REG_EAR_CTL:
+ val = w->value >> e->shift_l;
+ if (val == 3) {
+ printk(KERN_WARNING
+ "Invalid MUX setting for register 0x%02x (%d)\n",
+ e->reg, val);
+ ret = -1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static int handsfree_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
+ unsigned char hs_ctl;
+
+ hs_ctl = twl4030_read_reg_cache(w->codec, e->reg);
+
+ if (hs_ctl & TWL4030_HF_CTL_REF_EN) {
+ hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
+ twl4030_write(w->codec, e->reg, hs_ctl);
+ hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
+ twl4030_write(w->codec, e->reg, hs_ctl);
+ hs_ctl |= TWL4030_HF_CTL_HB_EN;
+ twl4030_write(w->codec, e->reg, hs_ctl);
+ } else {
+ hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN
+ | TWL4030_HF_CTL_HB_EN);
+ twl4030_write(w->codec, e->reg, hs_ctl);
+ }
+
+ return 0;
+}
+
+/*
+ * Some of the gain controls in TWL (mostly those which are associated with
+ * the outputs) are implemented in an interesting way:
+ * 0x0 : Power down (mute)
+ * 0x1 : 6dB
+ * 0x2 : 0 dB
+ * 0x3 : -6 dB
+ * Inverting not going to help with these.
+ * Custom volsw and volsw_2r get/put functions to handle these gain bits.
+ */
+#define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\
+ xinvert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw_twl4030, \
+ .put = snd_soc_put_volsw_twl4030, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = xreg, .shift = shift_left, .rshift = shift_right,\
+ .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\
+ xinvert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = snd_soc_get_volsw_r2_twl4030,\
+ .put = snd_soc_put_volsw_r2_twl4030, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .rshift = xshift, .max = xmax, .invert = xinvert} }
+#define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \
+ SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \
+ xinvert, tlv_array)
+
+static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ if (ucontrol->value.integer.value[0])
+ ucontrol->value.integer.value[0] =
+ max + 1 - ucontrol->value.integer.value[0];
+
+ if (shift != rshift) {
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg) >> rshift) & mask;
+ if (ucontrol->value.integer.value[1])
+ ucontrol->value.integer.value[1] =
+ max + 1 - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+
+static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+ unsigned short val, val2, val_mask;
+
+ val = (ucontrol->value.integer.value[0] & mask);
+
+ val_mask = mask << shift;
+ if (val)
+ val = max + 1 - val;
+ val = val << shift;
+ if (shift != rshift) {
+ val2 = (ucontrol->value.integer.value[1] & mask);
+ val_mask |= mask << rshift;
+ if (val2)
+ val2 = max + 1 - val2;
+ val |= val2 << rshift;
+ }
+ return snd_soc_update_bits(codec, reg, val_mask, val);
+}
+
+static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ int mask = (1<<fls(max))-1;
+
+ ucontrol->value.integer.value[0] =
+ (snd_soc_read(codec, reg) >> shift) & mask;
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, reg2) >> shift) & mask;
+
+ if (ucontrol->value.integer.value[0])
+ ucontrol->value.integer.value[0] =
+ max + 1 - ucontrol->value.integer.value[0];
+ if (ucontrol->value.integer.value[1])
+ ucontrol->value.integer.value[1] =
+ max + 1 - ucontrol->value.integer.value[1];
+
+ return 0;
+}
+
+static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ int max = mc->max;
+ int mask = (1 << fls(max)) - 1;
+ int err;
+ unsigned short val, val2, val_mask;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] & mask);
+ val2 = (ucontrol->value.integer.value[1] & mask);
+
+ if (val)
+ val = max + 1 - val;
+ if (val2)
+ val2 = max + 1 - val2;
+
+ val = val << shift;
+ val2 = val2 << shift;
+
+ err = snd_soc_update_bits(codec, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ return err;
+}
+
+static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = kcontrol->private_data;
+ u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+ int result = 0;
+
+ /* one bit must be set a time */
+ reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+ | TWL4030_MAINMIC_EN;
+ if (reg != 0) {
+ result++;
+ while ((reg & 1) == 0) {
+ result++;
+ reg >>= 1;
+ }
+ }
+
+ ucontrol->value.integer.value[0] = result;
+ return 0;
+}
+
+static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ u8 anamicl, micbias, avadc_ctl;
+
+ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+ anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+ | TWL4030_MAINMIC_EN);
+ micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+ micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
+ avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+ switch (value) {
+ case 1:
+ anamicl |= TWL4030_MAINMIC_EN;
+ micbias |= TWL4030_MICBIAS1_EN;
+ break;
+ case 2:
+ anamicl |= TWL4030_HSMIC_EN;
+ micbias |= TWL4030_HSMICBIAS_EN;
+ break;
+ case 3:
+ anamicl |= TWL4030_AUXL_EN;
+ break;
+ case 4:
+ anamicl |= TWL4030_CKMIC_EN;
+ break;
+ default:
+ break;
+ }
+
+ /* If some input is selected, enable amp and ADC */
+ if (value != 0) {
+ anamicl |= TWL4030_MICAMPL_EN;
+ avadc_ctl |= TWL4030_ADCL_EN;
+ } else {
+ anamicl &= ~TWL4030_MICAMPL_EN;
+ avadc_ctl &= ~TWL4030_ADCL_EN;
+ }
+
+ twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
+ twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+ twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+ return 1;
+}
+
+static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = kcontrol->private_data;
+ u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+ int value = 0;
+
+ reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
+ switch (reg) {
+ case TWL4030_SUBMIC_EN:
+ value = 1;
+ break;
+ case TWL4030_AUXR_EN:
+ value = 2;
+ break;
+ default:
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = value;
+ return 0;
+}
+
+static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = kcontrol->private_data;
+ int value = ucontrol->value.integer.value[0];
+ u8 anamicr, micbias, avadc_ctl;
+
+ anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+ anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
+ micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+ micbias &= ~TWL4030_MICBIAS2_EN;
+ avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+ switch (value) {
+ case 1:
+ anamicr |= TWL4030_SUBMIC_EN;
+ micbias |= TWL4030_MICBIAS2_EN;
+ break;
+ case 2:
+ anamicr |= TWL4030_AUXR_EN;
+ break;
+ default:
+ break;
+ }
+
+ if (value != 0) {
+ anamicr |= TWL4030_MICAMPR_EN;
+ avadc_ctl |= TWL4030_ADCR_EN;
+ } else {
+ anamicr &= ~TWL4030_MICAMPR_EN;
+ avadc_ctl &= ~TWL4030_ADCR_EN;
+ }
+
+ twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
+ twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+ twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+ return 1;
+}
+
+static const char *twl4030_left_in_sel[] = {
+ "None",
+ "Main Mic",
+ "Headset Mic",
+ "Line In",
+ "Carkit Mic",
+};
+
+static const char *twl4030_right_in_sel[] = {
+ "None",
+ "Sub Mic",
+ "Line In",
+};
+
+static const struct soc_enum twl4030_left_input_mux =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
+ twl4030_left_in_sel);
+
+static const struct soc_enum twl4030_right_input_mux =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
+ twl4030_right_in_sel);
+
+/*
+ * FGAIN volume control:
+ * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
+
+/*
+ * CGAIN volume control:
+ * 0 dB to 12 dB in 6 dB steps
+ * value 2 and 3 means 12 dB
+ */
+static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
+
+/*
+ * Analog playback gain
+ * -24 dB to 12 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
+
+/*
+ * Gain controls tied to outputs
+ * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
+ */
+static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
+
+/*
+ * Capture gain after the ADCs
+ * from 0 dB to 31 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
+
+/*
+ * Gain control for input amplifiers
+ * 0 dB to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+ /* Common playback gain controls */
+ SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
+ TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+ 0, 0x3f, 0, digital_fine_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
+ TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+ 0, 0x3f, 0, digital_fine_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
+ TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+ 6, 0x2, 0, digital_coarse_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
+ TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+ 6, 0x2, 0, digital_coarse_tlv),
+
+ SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
+ TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+ 3, 0x12, 1, analog_tlv),
+ SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
+ TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+ 3, 0x12, 1, analog_tlv),
+ SOC_DOUBLE_R("DAC1 Analog Playback Switch",
+ TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+ 1, 1, 0),
+ SOC_DOUBLE_R("DAC2 Analog Playback Switch",
+ TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+ 1, 1, 0),
+
+ /* Separate output gain controls */
+ SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
+ TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
+ 4, 3, 0, output_tvl),
+
+ SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume",
+ TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, output_tvl),
+
+ SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume",
+ TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
+ 4, 3, 0, output_tvl),
+
+ SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume",
+ TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),
+
+ /* Common capture gain controls */
+ SOC_DOUBLE_R_TLV("Capture Volume",
+ TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
+ 0, 0x1f, 0, digital_capture_tlv),
+
+ SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
+ 0, 3, 5, 0, input_gain_tlv),
+
+ /* Input source controls */
+ SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
+ twl4030_get_left_input, twl4030_put_left_input),
+ SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
+ twl4030_get_right_input, twl4030_put_right_input),
+};
+
+/* add non dapm controls */
+static int twl4030_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&twl4030_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_OUTPUT("EARPIECE"),
+ SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
+ SND_SOC_DAPM_OUTPUT("PREDRIVER"),
+ SND_SOC_DAPM_OUTPUT("HSOL"),
+ SND_SOC_DAPM_OUTPUT("HSOR"),
+ SND_SOC_DAPM_OUTPUT("CARKITL"),
+ SND_SOC_DAPM_OUTPUT("CARKITR"),
+ SND_SOC_DAPM_OUTPUT("HFL"),
+ SND_SOC_DAPM_OUTPUT("HFR"),
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
+ TWL4030_REG_AVDAC_CTL, 0, 0),
+ SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
+ TWL4030_REG_AVDAC_CTL, 1, 0),
+ SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
+ TWL4030_REG_AVDAC_CTL, 2, 0),
+ SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
+ TWL4030_REG_AVDAC_CTL, 3, 0),
+
+ /* Analog PGAs */
+ SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
+ 0, 0, NULL, 0),
+
+ /* Output MUX controls */
+ /* Earpiece */
+ SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_earpiece_control, outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+ /* PreDrivL/R */
+ SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_predrivel_control, outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+ SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_predriver_control, outmixer_event,
+ SND_SOC_DAPM_PRE_REG),
+ /* HeadsetL/R */
+ SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_hsol_control),
+ SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_hsor_control),
+ /* CarkitL/R */
+ SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_carkitl_control),
+ SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
+ &twl4030_dapm_carkitr_control),
+ /* HandsfreeL/R */
+ SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0,
+ &twl4030_dapm_handsfreel_control, handsfree_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0,
+ &twl4030_dapm_handsfreer_control, handsfree_event,
+ SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"ARXL1_APGA", NULL, "DAC Left1"},
+ {"ARXR1_APGA", NULL, "DAC Right1"},
+ {"ARXL2_APGA", NULL, "DAC Left2"},
+ {"ARXR2_APGA", NULL, "DAC Right2"},
+
+ /* Internal playback routings */
+ /* Earpiece */
+ {"Earpiece Mux", "DACL1", "ARXL1_APGA"},
+ {"Earpiece Mux", "DACL2", "ARXL2_APGA"},
+ {"Earpiece Mux", "DACR1", "ARXR1_APGA"},
+ /* PreDrivL */
+ {"PredriveL Mux", "DACL1", "ARXL1_APGA"},
+ {"PredriveL Mux", "DACL2", "ARXL2_APGA"},
+ {"PredriveL Mux", "DACR2", "ARXR2_APGA"},
+ /* PreDrivR */
+ {"PredriveR Mux", "DACR1", "ARXR1_APGA"},
+ {"PredriveR Mux", "DACR2", "ARXR2_APGA"},
+ {"PredriveR Mux", "DACL2", "ARXL2_APGA"},
+ /* HeadsetL */
+ {"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
+ {"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
+ /* HeadsetR */
+ {"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
+ {"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
+ /* CarkitL */
+ {"CarkitL Mux", "DACL1", "ARXL1_APGA"},
+ {"CarkitL Mux", "DACL2", "ARXL2_APGA"},
+ /* CarkitR */
+ {"CarkitR Mux", "DACR1", "ARXR1_APGA"},
+ {"CarkitR Mux", "DACR2", "ARXR2_APGA"},
+ /* HandsfreeL */
+ {"HandsfreeL Mux", "DACL1", "ARXL1_APGA"},
+ {"HandsfreeL Mux", "DACL2", "ARXL2_APGA"},
+ {"HandsfreeL Mux", "DACR2", "ARXR2_APGA"},
+ /* HandsfreeR */
+ {"HandsfreeR Mux", "DACR1", "ARXR1_APGA"},
+ {"HandsfreeR Mux", "DACR2", "ARXR2_APGA"},
+ {"HandsfreeR Mux", "DACL2", "ARXL2_APGA"},
+
+ /* outputs */
+ {"OUTL", NULL, "ARXL2_APGA"},
+ {"OUTR", NULL, "ARXR2_APGA"},
+ {"EARPIECE", NULL, "Earpiece Mux"},
+ {"PREDRIVEL", NULL, "PredriveL Mux"},
+ {"PREDRIVER", NULL, "PredriveR Mux"},
+ {"HSOL", NULL, "HeadsetL Mux"},
+ {"HSOR", NULL, "HeadsetR Mux"},
+ {"CARKITL", NULL, "CarkitL Mux"},
+ {"CARKITR", NULL, "CarkitR Mux"},
+ {"HFL", NULL, "HandsfreeL Mux"},
+ {"HFR", NULL, "HandsfreeR Mux"},
+
+ /* inputs */
+ {"ADCL", NULL, "INL"},
+ {"ADCR", NULL, "INR"},
+};
+
+static int twl4030_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets,
+ ARRAY_SIZE(twl4030_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static void twl4030_power_up(struct snd_soc_codec *codec)
+{
+ u8 anamicl, regmisc1, byte, popn;
+ int i = 0;
+
+ /* set CODECPDZ to turn on codec */
+ twl4030_set_codecpdz(codec);
+
+ /* initiate offset cancellation */
+ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+ twl4030_write(codec, TWL4030_REG_ANAMICL,
+ anamicl | TWL4030_CNCL_OFFSET_START);
+
+ /* wait for offset cancellation to complete */
+ do {
+ /* this takes a little while, so don't slam i2c */
+ udelay(2000);
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+ TWL4030_REG_ANAMICL);
+ } while ((i++ < 100) &&
+ ((byte & TWL4030_CNCL_OFFSET_START) ==
+ TWL4030_CNCL_OFFSET_START));
+
+ /* anti-pop when changing analog gain */
+ regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+ twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+ regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
+
+ /* toggle CODECPDZ as per TRM */
+ twl4030_clear_codecpdz(codec);
+ twl4030_set_codecpdz(codec);
+
+ /* program anti-pop with bias ramp delay */
+ popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+ popn &= TWL4030_RAMP_DELAY;
+ popn |= TWL4030_RAMP_DELAY_645MS;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+ popn |= TWL4030_VMID_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+ /* enable anti-pop ramp */
+ popn |= TWL4030_RAMP_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+}
+
+static void twl4030_power_down(struct snd_soc_codec *codec)
+{
+ u8 popn;
+
+ /* disable anti-pop ramp */
+ popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+ popn &= ~TWL4030_RAMP_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+ /* disable bias out */
+ popn &= ~TWL4030_VMID_EN;
+ twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+ /* power down */
+ twl4030_clear_codecpdz(codec);
+}
+
+static int twl4030_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ twl4030_power_up(codec);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* TODO: develop a twl4030_prepare function */
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* TODO: develop a twl4030_standby function */
+ twl4030_power_down(codec);
+ break;
+ case SND_SOC_BIAS_OFF:
+ twl4030_power_down(codec);
+ break;
+ }
+ codec->bias_level = level;
+
+ return 0;
+}
+
+static int twl4030_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u8 mode, old_mode, format, old_format;
+
+
+ /* bit rate */
+ old_mode = twl4030_read_reg_cache(codec,
+ TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+ mode = old_mode & ~TWL4030_APLL_RATE;
+
+ switch (params_rate(params)) {
+ case 8000:
+ mode |= TWL4030_APLL_RATE_8000;
+ break;
+ case 11025:
+ mode |= TWL4030_APLL_RATE_11025;
+ break;
+ case 12000:
+ mode |= TWL4030_APLL_RATE_12000;
+ break;
+ case 16000:
+ mode |= TWL4030_APLL_RATE_16000;
+ break;
+ case 22050:
+ mode |= TWL4030_APLL_RATE_22050;
+ break;
+ case 24000:
+ mode |= TWL4030_APLL_RATE_24000;
+ break;
+ case 32000:
+ mode |= TWL4030_APLL_RATE_32000;
+ break;
+ case 44100:
+ mode |= TWL4030_APLL_RATE_44100;
+ break;
+ case 48000:
+ mode |= TWL4030_APLL_RATE_48000;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (mode != old_mode) {
+ /* change rate and set CODECPDZ */
+ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+ twl4030_set_codecpdz(codec);
+ }
+
+ /* sample size */
+ old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+ format = old_format;
+ format &= ~TWL4030_DATA_WIDTH;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ format |= TWL4030_DATA_WIDTH_16S_16W;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ format |= TWL4030_DATA_WIDTH_32S_24W;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 hw params: unknown format %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ if (format != old_format) {
+
+ /* clear CODECPDZ before changing format (codec requirement) */
+ twl4030_clear_codecpdz(codec);
+
+ /* change format */
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+ /* set CODECPDZ afterwards */
+ twl4030_set_codecpdz(codec);
+ }
+ return 0;
+}
+
+static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 infreq;
+
+ switch (freq) {
+ case 19200000:
+ infreq = TWL4030_APLL_INFREQ_19200KHZ;
+ break;
+ case 26000000:
+ infreq = TWL4030_APLL_INFREQ_26000KHZ;
+ break;
+ case 38400000:
+ infreq = TWL4030_APLL_INFREQ_38400KHZ;
+ break;
+ default:
+ printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ infreq |= TWL4030_APLL_EN;
+ twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+ return 0;
+}
+
+static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 old_format, format;
+
+ /* get format */
+ old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+ format = old_format;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ format &= ~(TWL4030_AIF_SLAVE_EN);
+ format &= ~(TWL4030_CLK256FS_EN);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ format |= TWL4030_AIF_SLAVE_EN;
+ format |= TWL4030_CLK256FS_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ format &= ~TWL4030_AIF_FORMAT;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format |= TWL4030_AIF_FORMAT_CODEC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (format != old_format) {
+
+ /* clear CODECPDZ before changing format (codec requirement) */
+ twl4030_clear_codecpdz(codec);
+
+ /* change format */
+ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+ /* set CODECPDZ afterwards */
+ twl4030_set_codecpdz(codec);
+ }
+
+ return 0;
+}
+
+#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
+#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_dai twl4030_dai = {
+ .name = "twl4030",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TWL4030_RATES,
+ .formats = TWL4030_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TWL4030_RATES,
+ .formats = TWL4030_FORMATS,},
+ .ops = {
+ .hw_params = twl4030_hw_params,
+ .set_sysclk = twl4030_set_dai_sysclk,
+ .set_fmt = twl4030_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL_GPL(twl4030_dai);
+
+static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int twl4030_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ twl4030_set_bias_level(codec, codec->suspend_bias_level);
+ return 0;
+}
+
+/*
+ * initialize the driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+
+static int twl4030_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ printk(KERN_INFO "TWL4030 Audio Codec init \n");
+
+ codec->name = "twl4030";
+ codec->owner = THIS_MODULE;
+ codec->read = twl4030_read_reg_cache;
+ codec->write = twl4030_write;
+ codec->set_bias_level = twl4030_set_bias_level;
+ codec->dai = &twl4030_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(twl4030_reg);
+ codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ twl4030_init_chip(codec);
+
+ /* power on device */
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ twl4030_add_controls(codec);
+ twl4030_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "twl4030: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *twl4030_socdev;
+
+static int twl4030_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ twl4030_socdev = socdev;
+ twl4030_init(socdev);
+
+ return 0;
+}
+
+static int twl4030_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ printk(KERN_INFO "TWL4030 Audio Codec remove\n");
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+ .probe = twl4030_probe,
+ .remove = twl4030_remove,
+ .suspend = twl4030_suspend,
+ .resume = twl4030_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
+static int __init twl4030_modinit(void)
+{
+ return snd_soc_register_dai(&twl4030_dai);
+}
+module_init(twl4030_modinit);
+
+static void __exit twl4030_exit(void)
+{
+ snd_soc_unregister_dai(&twl4030_dai);
+}
+module_exit(twl4030_exit);
+
+MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
+MODULE_AUTHOR("Steve Sakoman");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
new file mode 100644
index 000000000000..54615c76802b
--- /dev/null
+++ b/sound/soc/codecs/twl4030.h
@@ -0,0 +1,219 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman <steve@sakoman.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_AUDIO_H__
+#define __TWL4030_AUDIO_H__
+
+#define TWL4030_REG_CODEC_MODE 0x1
+#define TWL4030_REG_OPTION 0x2
+#define TWL4030_REG_UNKNOWN 0x3
+#define TWL4030_REG_MICBIAS_CTL 0x4
+#define TWL4030_REG_ANAMICL 0x5
+#define TWL4030_REG_ANAMICR 0x6
+#define TWL4030_REG_AVADC_CTL 0x7
+#define TWL4030_REG_ADCMICSEL 0x8
+#define TWL4030_REG_DIGMIXING 0x9
+#define TWL4030_REG_ATXL1PGA 0xA
+#define TWL4030_REG_ATXR1PGA 0xB
+#define TWL4030_REG_AVTXL2PGA 0xC
+#define TWL4030_REG_AVTXR2PGA 0xD
+#define TWL4030_REG_AUDIO_IF 0xE
+#define TWL4030_REG_VOICE_IF 0xF
+#define TWL4030_REG_ARXR1PGA 0x10
+#define TWL4030_REG_ARXL1PGA 0x11
+#define TWL4030_REG_ARXR2PGA 0x12
+#define TWL4030_REG_ARXL2PGA 0x13
+#define TWL4030_REG_VRXPGA 0x14
+#define TWL4030_REG_VSTPGA 0x15
+#define TWL4030_REG_VRX2ARXPGA 0x16
+#define TWL4030_REG_AVDAC_CTL 0x17
+#define TWL4030_REG_ARX2VTXPGA 0x18
+#define TWL4030_REG_ARXL1_APGA_CTL 0x19
+#define TWL4030_REG_ARXR1_APGA_CTL 0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL 0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL 0x1C
+#define TWL4030_REG_ATX2ARXPGA 0x1D
+#define TWL4030_REG_BT_IF 0x1E
+#define TWL4030_REG_BTPGA 0x1F
+#define TWL4030_REG_BTSTPGA 0x20
+#define TWL4030_REG_EAR_CTL 0x21
+#define TWL4030_REG_HS_SEL 0x22
+#define TWL4030_REG_HS_GAIN_SET 0x23
+#define TWL4030_REG_HS_POPN_SET 0x24
+#define TWL4030_REG_PREDL_CTL 0x25
+#define TWL4030_REG_PREDR_CTL 0x26
+#define TWL4030_REG_PRECKL_CTL 0x27
+#define TWL4030_REG_PRECKR_CTL 0x28
+#define TWL4030_REG_HFL_CTL 0x29
+#define TWL4030_REG_HFR_CTL 0x2A
+#define TWL4030_REG_ALC_CTL 0x2B
+#define TWL4030_REG_ALC_SET1 0x2C
+#define TWL4030_REG_ALC_SET2 0x2D
+#define TWL4030_REG_BOOST_CTL 0x2E
+#define TWL4030_REG_SOFTVOL_CTL 0x2F
+#define TWL4030_REG_DTMF_FREQSEL 0x30
+#define TWL4030_REG_DTMF_TONEXT1H 0x31
+#define TWL4030_REG_DTMF_TONEXT1L 0x32
+#define TWL4030_REG_DTMF_TONEXT2H 0x33
+#define TWL4030_REG_DTMF_TONEXT2L 0x34
+#define TWL4030_REG_DTMF_TONOFF 0x35
+#define TWL4030_REG_DTMF_WANONOFF 0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39
+#define TWL4030_REG_APLL_CTL 0x3A
+#define TWL4030_REG_DTMF_CTL 0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2 0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1 0x3D
+#define TWL4030_REG_MISC_SET_1 0x3E
+#define TWL4030_REG_PCMBTMUX 0x3F
+#define TWL4030_REG_RX_PATH_SEL 0x43
+#define TWL4030_REG_VDL_APGA_CTL 0x44
+#define TWL4030_REG_VIBRA_CTL 0x45
+#define TWL4030_REG_VIBRA_SET 0x46
+#define TWL4030_REG_VIBRA_PWM_SET 0x47
+#define TWL4030_REG_ANAMIC_GAIN 0x48
+#define TWL4030_REG_MISC_SET_2 0x49
+
+#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+
+#define TWL4030_APLL_RATE 0xF0
+#define TWL4030_APLL_RATE_8000 0x00
+#define TWL4030_APLL_RATE_11025 0x10
+#define TWL4030_APLL_RATE_12000 0x20
+#define TWL4030_APLL_RATE_16000 0x40
+#define TWL4030_APLL_RATE_22050 0x50
+#define TWL4030_APLL_RATE_24000 0x60
+#define TWL4030_APLL_RATE_32000 0x80
+#define TWL4030_APLL_RATE_44100 0x90
+#define TWL4030_APLL_RATE_48000 0xA0
+#define TWL4030_SEL_16K 0x04
+#define TWL4030_CODECPDZ 0x02
+#define TWL4030_OPT_MODE 0x01
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+
+#define TWL4030_MICBIAS2_CTL 0x40
+#define TWL4030_MICBIAS1_CTL 0x20
+#define TWL4030_HSMICBIAS_EN 0x04
+#define TWL4030_MICBIAS2_EN 0x02
+#define TWL4030_MICBIAS1_EN 0x01
+
+/* ANAMICL (0x05) Fields */
+
+#define TWL4030_CNCL_OFFSET_START 0x80
+#define TWL4030_OFFSET_CNCL_SEL 0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60
+#define TWL4030_MICAMPL_EN 0x10
+#define TWL4030_CKMIC_EN 0x08
+#define TWL4030_AUXL_EN 0x04
+#define TWL4030_HSMIC_EN 0x02
+#define TWL4030_MAINMIC_EN 0x01
+
+/* ANAMICR (0x06) Fields */
+
+#define TWL4030_MICAMPR_EN 0x10
+#define TWL4030_AUXR_EN 0x04
+#define TWL4030_SUBMIC_EN 0x01
+
+/* AVADC_CTL (0x07) Fields */
+
+#define TWL4030_ADCL_EN 0x08
+#define TWL4030_AVADC_CLK_PRIORITY 0x04
+#define TWL4030_ADCR_EN 0x02
+
+/* AUDIO_IF (0x0E) Fields */
+
+#define TWL4030_AIF_SLAVE_EN 0x80
+#define TWL4030_DATA_WIDTH 0x60
+#define TWL4030_DATA_WIDTH_16S_16W 0x00
+#define TWL4030_DATA_WIDTH_32S_16W 0x40
+#define TWL4030_DATA_WIDTH_32S_24W 0x60
+#define TWL4030_AIF_FORMAT 0x18
+#define TWL4030_AIF_FORMAT_CODEC 0x00
+#define TWL4030_AIF_FORMAT_LEFT 0x08
+#define TWL4030_AIF_FORMAT_RIGHT 0x10
+#define TWL4030_AIF_FORMAT_TDM 0x18
+#define TWL4030_AIF_TRI_EN 0x04
+#define TWL4030_CLK256FS_EN 0x02
+#define TWL4030_AIF_EN 0x01
+
+/* HS_GAIN_SET (0x23) Fields */
+
+#define TWL4030_HSR_GAIN 0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB 0x04
+#define TWL4030_HSR_GAIN_0DB 0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C
+#define TWL4030_HSL_GAIN 0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN 0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB 0x01
+#define TWL4030_HSL_GAIN_0DB 0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB 0x03
+
+/* HS_POPN_SET (0x24) Fields */
+
+#define TWL4030_VMID_EN 0x40
+#define TWL4030_EXTMUTE 0x20
+#define TWL4030_RAMP_DELAY 0x1C
+#define TWL4030_RAMP_DELAY_20MS 0x00
+#define TWL4030_RAMP_DELAY_40MS 0x04
+#define TWL4030_RAMP_DELAY_81MS 0x08
+#define TWL4030_RAMP_DELAY_161MS 0x0C
+#define TWL4030_RAMP_DELAY_323MS 0x10
+#define TWL4030_RAMP_DELAY_645MS 0x14
+#define TWL4030_RAMP_DELAY_1291MS 0x18
+#define TWL4030_RAMP_DELAY_2581MS 0x1C
+#define TWL4030_RAMP_EN 0x02
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN 0x04
+#define TWL4030_HF_CTL_LOOP_EN 0x08
+#define TWL4030_HF_CTL_RAMP_EN 0x10
+#define TWL4030_HF_CTL_REF_EN 0x20
+
+/* APLL_CTL (0x3A) Fields */
+
+#define TWL4030_APLL_EN 0x10
+#define TWL4030_APLL_INFREQ 0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ 0x05
+#define TWL4030_APLL_INFREQ_26000KHZ 0x06
+#define TWL4030_APLL_INFREQ_38400KHZ 0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+
+#define TWL4030_CLK64_EN 0x80
+#define TWL4030_SCRAMBLE_EN 0x40
+#define TWL4030_FMLOOP_EN 0x20
+#define TWL4030_SMOOTH_ANAVOL_EN 0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
+
+extern struct snd_soc_dai twl4030_dai;
+extern struct snd_soc_codec_device soc_codec_dev_twl4030;
+
+#endif /* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
new file mode 100644
index 000000000000..a2c5064a774b
--- /dev/null
+++ b/sound/soc/codecs/uda134x.c
@@ -0,0 +1,668 @@
+/*
+ * uda134x.c -- UDA134X ALSA SoC Codec driver
+ *
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+ *
+ * This program is free software; you can 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/delay.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <sound/uda134x.h>
+#include <sound/l3.h>
+
+#include "uda134x.h"
+
+
+#define POWER_OFF_ON_STANDBY 1
+/*
+ ALSA SOC usually puts the device in standby mode when it's not used
+ for sometime. If you define POWER_OFF_ON_STANDBY the driver will
+ turn off the ADC/DAC when this callback is invoked and turn it back
+ on when needed. Unfortunately this will result in a very light bump
+ (it can be audible only with good earphones). If this bothers you
+ just comment this line, you will have slightly higher power
+ consumption . Please note that sending the L3 command for ADC is
+ enough to make the bump, so it doesn't make difference if you
+ completely take off power from the codec.
+ */
+
+#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
+#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda134x_priv {
+ int sysclk;
+ int dai_fmt;
+
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+/* In-data addresses are hard-coded into the reg-cache values */
+static const char uda134x_reg[UDA134X_REGS_NUM] = {
+ /* Extended address registers */
+ 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Status, data regs */
+ 0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
+};
+
+/*
+ * The codec has no support for reading its registers except for peak level...
+ */
+static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= UDA134X_REGS_NUM)
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * Write the register cache
+ */
+static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, unsigned int value)
+{
+ u8 *cache = codec->reg_cache;
+
+ if (reg >= UDA134X_REGS_NUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * Write to the uda134x registers
+ *
+ */
+static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ int ret;
+ u8 addr;
+ u8 data = value;
+ struct uda134x_platform_data *pd = codec->control_data;
+
+ pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
+
+ if (reg >= UDA134X_REGS_NUM) {
+ printk(KERN_ERR "%s unkown register: reg: %d",
+ __func__, reg);
+ return -EINVAL;
+ }
+
+ uda134x_write_reg_cache(codec, reg, value);
+
+ switch (reg) {
+ case UDA134X_STATUS0:
+ case UDA134X_STATUS1:
+ addr = UDA134X_STATUS_ADDR;
+ break;
+ case UDA134X_DATA000:
+ case UDA134X_DATA001:
+ case UDA134X_DATA010:
+ addr = UDA134X_DATA0_ADDR;
+ break;
+ case UDA134X_DATA1:
+ addr = UDA134X_DATA1_ADDR;
+ break;
+ default:
+ /* It's an extended address register */
+ addr = (reg | UDA134X_EXTADDR_PREFIX);
+
+ ret = l3_write(&pd->l3,
+ UDA134X_DATA0_ADDR, &addr, 1);
+ if (ret != 1)
+ return -EIO;
+
+ addr = UDA134X_DATA0_ADDR;
+ data = (value | UDA134X_EXTDATA_PREFIX);
+ break;
+ }
+
+ ret = l3_write(&pd->l3,
+ addr, &data, 1);
+ if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static inline void uda134x_reset(struct snd_soc_codec *codec)
+{
+ u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+ uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
+ msleep(1);
+ uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
+}
+
+static int uda134x_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
+
+ pr_debug("%s mute: %d\n", __func__, mute);
+
+ if (mute)
+ mute_reg |= (1<<2);
+ else
+ mute_reg &= ~(1<<2);
+
+ uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
+
+ return 0;
+}
+
+static int uda134x_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct uda134x_priv *uda134x = codec->private_data;
+ struct snd_pcm_runtime *master_runtime;
+
+ if (uda134x->master_substream) {
+ master_runtime = uda134x->master_substream->runtime;
+
+ pr_debug("%s constraining to %d bits at %d\n", __func__,
+ master_runtime->sample_bits,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ master_runtime->rate,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ master_runtime->sample_bits,
+ master_runtime->sample_bits);
+
+ uda134x->slave_substream = substream;
+ } else
+ uda134x->master_substream = substream;
+
+ return 0;
+}
+
+static void uda134x_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct uda134x_priv *uda134x = codec->private_data;
+
+ if (uda134x->master_substream == substream)
+ uda134x->master_substream = uda134x->slave_substream;
+
+ uda134x->slave_substream = NULL;
+}
+
+static int uda134x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct uda134x_priv *uda134x = codec->private_data;
+ u8 hw_params;
+
+ if (substream == uda134x->slave_substream) {
+ pr_debug("%s ignoring hw_params for slave substream\n",
+ __func__);
+ return 0;
+ }
+
+ hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+ hw_params &= STATUS0_SYSCLK_MASK;
+ hw_params &= STATUS0_DAIFMT_MASK;
+
+ pr_debug("%s sysclk: %d, rate:%d\n", __func__,
+ uda134x->sysclk, params_rate(params));
+
+ /* set SYSCLK / fs ratio */
+ switch (uda134x->sysclk / params_rate(params)) {
+ case 512:
+ break;
+ case 384:
+ hw_params |= (1<<4);
+ break;
+ case 256:
+ hw_params |= (1<<5);
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported fs\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
+ uda134x->dai_fmt, params_format(params));
+
+ /* set DAI format and word length */
+ switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hw_params |= (1<<1);
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ hw_params |= (1<<2);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ hw_params |= ((1<<2) | (1<<1));
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported format (right)\n",
+ __func__);
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ hw_params |= (1<<3);
+ break;
+ default:
+ printk(KERN_ERR "%s unsupported format\n", __func__);
+ return -EINVAL;
+ }
+
+ uda134x_write(codec, UDA134X_STATUS0, hw_params);
+
+ return 0;
+}
+
+static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct uda134x_priv *uda134x = codec->private_data;
+
+ pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
+ clk_id, freq, dir);
+
+ /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
+ because the codec is slave. Of course limitations of the clock
+ master (the IIS controller) apply.
+ We'll error out on set_hw_params if it's not OK */
+ if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+ uda134x->sysclk = freq;
+ return 0;
+ }
+
+ printk(KERN_ERR "%s unsupported sysclk\n", __func__);
+ return -EINVAL;
+}
+
+static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct uda134x_priv *uda134x = codec->private_data;
+
+ pr_debug("%s fmt: %08X\n", __func__, fmt);
+
+ /* codec supports only full slave mode */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+ printk(KERN_ERR "%s unsupported slave mode\n", __func__);
+ return -EINVAL;
+ }
+
+ /* no support for clock inversion */
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
+ return -EINVAL;
+ }
+
+ /* We can't setup DAI format here as it depends on the word bit num */
+ /* so let's just store the value for later */
+ uda134x->dai_fmt = fmt;
+
+ return 0;
+}
+
+static int uda134x_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 reg;
+ struct uda134x_platform_data *pd = codec->control_data;
+ int i;
+ u8 *cache = codec->reg_cache;
+
+ pr_debug("%s bias level %d\n", __func__, level);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* ADC, DAC on */
+ reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+ uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ /* power on */
+ if (pd->power) {
+ pd->power(1);
+ /* Sync reg_cache with the hardware */
+ for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
+ codec->write(codec, i, *cache++);
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* ADC, DAC power off */
+ reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+ uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power off */
+ if (pd->power)
+ pd->power(0);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
+ "Minimum2", "Maximum"};
+static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *uda134x_mixmode[] = {"Differential", "Analog1",
+ "Analog2", "Both"};
+
+static const struct soc_enum uda134x_mixer_enum[] = {
+SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
+SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
+SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
+};
+
+static const struct snd_kcontrol_new uda1341_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
+SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
+SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
+
+SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
+SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
+
+SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
+SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
+SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
+
+SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
+SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
+SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
+SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
+SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new uda1340_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static int uda134x_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i, n;
+ const struct snd_kcontrol_new *ctrls;
+ struct uda134x_platform_data *pd = codec->control_data;
+
+ switch (pd->model) {
+ case UDA134X_UDA1340:
+ case UDA134X_UDA1344:
+ n = ARRAY_SIZE(uda1340_snd_controls);
+ ctrls = uda1340_snd_controls;
+ break;
+ case UDA134X_UDA1341:
+ n = ARRAY_SIZE(uda1341_snd_controls);
+ ctrls = uda1341_snd_controls;
+ break;
+ default:
+ printk(KERN_ERR "%s unkown codec type: %d",
+ __func__, pd->model);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < n; i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ctrls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+struct snd_soc_dai uda134x_dai = {
+ .name = "UDA134X",
+ /* playback capabilities */
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA134X_RATES,
+ .formats = UDA134X_FORMATS,
+ },
+ /* capture capabilities */
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = UDA134X_RATES,
+ .formats = UDA134X_FORMATS,
+ },
+ /* pcm operations */
+ .ops = {
+ .startup = uda134x_startup,
+ .shutdown = uda134x_shutdown,
+ .hw_params = uda134x_hw_params,
+ .digital_mute = uda134x_mute,
+ .set_sysclk = uda134x_set_dai_sysclk,
+ .set_fmt = uda134x_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL(uda134x_dai);
+
+
+static int uda134x_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct uda134x_priv *uda134x;
+ void *codec_setup_data = socdev->codec_data;
+ int ret = -ENOMEM;
+ struct uda134x_platform_data *pd;
+
+ printk(KERN_INFO "UDA134X SoC Audio Codec\n");
+
+ if (!codec_setup_data) {
+ printk(KERN_ERR "UDA134X SoC codec: "
+ "missing L3 bitbang function\n");
+ return -ENODEV;
+ }
+
+ pd = codec_setup_data;
+ switch (pd->model) {
+ case UDA134X_UDA1340:
+ case UDA134X_UDA1341:
+ case UDA134X_UDA1344:
+ break;
+ default:
+ printk(KERN_ERR "UDA134X SoC codec: "
+ "unsupported model %d\n",
+ pd->model);
+ return -EINVAL;
+ }
+
+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (socdev->codec == NULL)
+ return ret;
+
+ codec = socdev->codec;
+
+ uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
+ if (uda134x == NULL)
+ goto priv_err;
+ codec->private_data = uda134x;
+
+ codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ goto reg_err;
+
+ mutex_init(&codec->mutex);
+
+ codec->reg_cache_size = sizeof(uda134x_reg);
+ codec->reg_cache_step = 1;
+
+ codec->name = "UDA134X";
+ codec->owner = THIS_MODULE;
+ codec->dai = &uda134x_dai;
+ codec->num_dai = 1;
+ codec->read = uda134x_read_reg_cache;
+ codec->write = uda134x_write;
+#ifdef POWER_OFF_ON_STANDBY
+ codec->set_bias_level = uda134x_set_bias_level;
+#endif
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->control_data = codec_setup_data;
+
+ if (pd->power)
+ pd->power(1);
+
+ uda134x_reset(codec);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "UDA134X: failed to register pcms\n");
+ goto pcm_err;
+ }
+
+ ret = uda134x_add_controls(codec);
+ if (ret < 0) {
+ printk(KERN_ERR "UDA134X: failed to register controls\n");
+ goto pcm_err;
+ }
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "UDA134X: failed to register card\n");
+ goto card_err;
+ }
+
+ return 0;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+reg_err:
+ kfree(codec->private_data);
+priv_err:
+ kfree(codec);
+ return ret;
+}
+
+/* power down chip */
+static int uda134x_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+
+ kfree(codec->private_data);
+ kfree(codec->reg_cache);
+ kfree(codec);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int uda134x_soc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int uda134x_soc_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
+ return 0;
+}
+#else
+#define uda134x_soc_suspend NULL
+#define uda134x_soc_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_device soc_codec_dev_uda134x = {
+ .probe = uda134x_soc_probe,
+ .remove = uda134x_soc_remove,
+ .suspend = uda134x_soc_suspend,
+ .resume = uda134x_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
+
+static int __init uda134x_init(void)
+{
+ return snd_soc_register_dai(&uda134x_dai);
+}
+module_init(uda134x_init);
+
+static void __exit uda134x_exit(void)
+{
+ snd_soc_unregister_dai(&uda134x_dai);
+}
+module_exit(uda134x_exit);
+
+MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
new file mode 100644
index 000000000000..94f440490b31
--- /dev/null
+++ b/sound/soc/codecs/uda134x.h
@@ -0,0 +1,36 @@
+#ifndef _UDA134X_CODEC_H
+#define _UDA134X_CODEC_H
+
+#define UDA134X_L3ADDR 5
+#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
+#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
+#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
+
+#define UDA134X_EXTADDR_PREFIX 0xC0
+#define UDA134X_EXTDATA_PREFIX 0xE0
+
+/* UDA134X registers */
+#define UDA134X_EA000 0
+#define UDA134X_EA001 1
+#define UDA134X_EA010 2
+#define UDA134X_EA011 3
+#define UDA134X_EA100 4
+#define UDA134X_EA101 5
+#define UDA134X_EA110 6
+#define UDA134X_EA111 7
+#define UDA134X_STATUS0 8
+#define UDA134X_STATUS1 9
+#define UDA134X_DATA000 10
+#define UDA134X_DATA001 11
+#define UDA134X_DATA010 12
+#define UDA134X_DATA1 13
+
+#define UDA134X_REGS_NUM 14
+
+#define STATUS0_DAIFMT_MASK (~(7<<1))
+#define STATUS0_SYSCLK_MASK (~(3<<4))
+
+extern struct snd_soc_dai uda134x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_uda134x;
+
+#endif
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index a69ee72a7af5..e6bf0844fbf3 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -407,7 +407,8 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
* when the DAI is being clocked by the CPU DAI. It's up to the
* machine and cpu DAI driver to do this before we are called.
*/
-static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -439,7 +440,8 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
}
static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -477,7 +479,8 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -560,8 +563,6 @@ struct snd_soc_dai uda1380_dai[] = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
- },
- .dai_ops = {
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
@@ -579,8 +580,6 @@ struct snd_soc_dai uda1380_dai[] = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
- },
- .dai_ops = {
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
@@ -598,8 +597,6 @@ struct snd_soc_dai uda1380_dai[] = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
- },
- .dai_ops = {
.set_fmt = uda1380_set_dai_fmt,
},
},
@@ -680,7 +677,7 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
/* uda1380 init */
uda1380_add_controls(codec);
uda1380_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
pr_err("uda1380: failed to register card\n");
goto card_err;
@@ -844,6 +841,18 @@ struct snd_soc_codec_device soc_codec_dev_uda1380 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+static int __init uda1380_modinit(void)
+{
+ return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_init(uda1380_modinit);
+
+static void __exit uda1380_exit(void)
+{
+ snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_exit(uda1380_exit);
+
MODULE_AUTHOR("Giorgio Padrin");
MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
new file mode 100644
index 000000000000..e3989d406f54
--- /dev/null
+++ b/sound/soc/codecs/wm8350.c
@@ -0,0 +1,1583 @@
+/*
+ * wm8350.c -- WM8350 ALSA SoC audio driver
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8350.h"
+
+#define WM8350_OUTn_0dB 0x39
+
+#define WM8350_RAMP_NONE 0
+#define WM8350_RAMP_UP 1
+#define WM8350_RAMP_DOWN 2
+
+/* We only include the analogue supplies here; the digital supplies
+ * need to be available well before this driver can be probed.
+ */
+static const char *supply_names[] = {
+ "AVDD",
+ "HPVDD",
+};
+
+struct wm8350_output {
+ u16 active;
+ u16 left_vol;
+ u16 right_vol;
+ u16 ramp;
+ u16 mute;
+};
+
+struct wm8350_data {
+ struct snd_soc_codec codec;
+ struct wm8350_output out1;
+ struct wm8350_output out2;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350->reg_cache[reg];
+}
+
+static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350_reg_read(wm8350, reg);
+}
+
+static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350_reg_write(wm8350, reg, value);
+}
+
+/*
+ * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
+{
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_data->out1;
+ struct wm8350 *wm8350 = codec->control_data;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
+ val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
+ val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
+{
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out2 = &wm8350_data->out2;
+ struct wm8350 *wm8350 = codec->control_data;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
+ val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
+ val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * This work ramps both output PGAs at stream start/stop time to
+ * minimise pop associated with DAPM power switching.
+ * It's best to enable Zero Cross when ramping occurs to minimise any
+ * zipper noises.
+ */
+static void wm8350_pga_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec =
+ container_of(work, struct snd_soc_codec, delayed_work.work);
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_data->out1,
+ *out2 = &wm8350_data->out2;
+ int i, out1_complete, out2_complete;
+
+ /* do we need to ramp at all ? */
+ if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
+ return;
+
+ /* PGA volumes have 6 bits of resolution to ramp */
+ for (i = 0; i <= 63; i++) {
+ out1_complete = 1, out2_complete = 1;
+ if (out1->ramp != WM8350_RAMP_NONE)
+ out1_complete = wm8350_out1_ramp_step(codec);
+ if (out2->ramp != WM8350_RAMP_NONE)
+ out2_complete = wm8350_out2_ramp_step(codec);
+
+ /* ramp finished ? */
+ if (out1_complete && out2_complete)
+ break;
+
+ /* we need to delay longer on the up ramp */
+ if (out1->ramp == WM8350_RAMP_UP ||
+ out2->ramp == WM8350_RAMP_UP) {
+ /* delay is longer over 0dB as increases are larger */
+ if (i >= WM8350_OUTn_0dB)
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (2));
+ else
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (1));
+ } else
+ udelay(50); /* doesn't matter if we delay longer */
+ }
+
+ out1->ramp = WM8350_RAMP_NONE;
+ out2->ramp = WM8350_RAMP_NONE;
+}
+
+/*
+ * WM8350 Controls
+ */
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ out = &wm8350_data->out1;
+ break;
+ case 2:
+ case 3:
+ out = &wm8350_data->out2;
+ break;
+
+ default:
+ BUG();
+ return -1;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ out->ramp = WM8350_RAMP_UP;
+ out->active = 1;
+
+ if (!delayed_work_pending(&codec->delayed_work))
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(1));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ out->ramp = WM8350_RAMP_DOWN;
+ out->active = 0;
+
+ if (!delayed_work_pending(&codec->delayed_work))
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(1));
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_output *out = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ret;
+ unsigned int reg = mc->reg;
+ u16 val;
+
+ /* For OUT1 and OUT2 we shadow the values and only actually write
+ * them out when active in order to ensure the amplifier comes on
+ * as quietly as possible. */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ out = &wm8350_priv->out1;
+ break;
+ case WM8350_LOUT2_VOLUME:
+ out = &wm8350_priv->out2;
+ break;
+ default:
+ break;
+ }
+
+ if (out) {
+ out->left_vol = ucontrol->value.integer.value[0];
+ out->right_vol = ucontrol->value.integer.value[1];
+ if (!out->active)
+ return 1;
+ }
+
+ ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = wm8350_codec_read(codec, reg);
+ wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+ return 1;
+}
+
+static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_priv->out1;
+ struct wm8350_output *out2 = &wm8350_priv->out2;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+
+ /* If these are cached registers use the cache */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ ucontrol->value.integer.value[0] = out1->left_vol;
+ ucontrol->value.integer.value[1] = out1->right_vol;
+ return 0;
+
+ case WM8350_LOUT2_VOLUME:
+ ucontrol->value.integer.value[0] = out2->left_vol;
+ ucontrol->value.integer.value[1] = out2->right_vol;
+ return 0;
+
+ default:
+ break;
+ }
+
+ return snd_soc_get_volsw_2r(kcontrol, ucontrol);
+}
+
+/* double control with volume update */
+#define SOC_WM8350_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \
+ xinvert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = wm8350_get_volsw_2r, .put = wm8350_put_volsw_2r_vu, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .rshift = xshift, .max = xmax, .invert = xinvert}, }
+
+static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
+static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
+static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
+static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
+static const char *wm8350_adcfilter[] = { "None", "High Pass" };
+static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
+static const char *wm8350_lr[] = { "Left", "Right" };
+
+static const struct soc_enum wm8350_enum[] = {
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
+};
+
+static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
+static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
+static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
+static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
+
+static const unsigned int capture_sd_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
+ 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm8350_snd_controls[] = {
+ SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
+ SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
+ SOC_WM8350_DOUBLE_R_TLV("Playback PCM Volume",
+ WM8350_DAC_DIGITAL_VOLUME_L,
+ WM8350_DAC_DIGITAL_VOLUME_R,
+ 0, 255, 0, dac_pcm_tlv),
+ SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
+ SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
+ SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
+ SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
+ SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
+ SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+ SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
+ WM8350_ADC_DIGITAL_VOLUME_L,
+ WM8350_ADC_DIGITAL_VOLUME_R,
+ 0, 255, 0, adc_pcm_tlv),
+ SOC_DOUBLE_TLV("Capture Sidetone Volume",
+ WM8350_ADC_DIVIDER,
+ 8, 4, 15, 1, capture_sd_tlv),
+ SOC_WM8350_DOUBLE_R_TLV("Capture Volume",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME,
+ 2, 63, 0, pre_amp_tlv),
+ SOC_DOUBLE_R("Capture ZC Switch",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
+ SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Bypass Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 9, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Bypass Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 13, 7, 0, out_mix_tlv),
+ SOC_SINGLE("Left Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
+ SOC_SINGLE("Right Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
+ SOC_SINGLE_TLV("Out4 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_WM8350_DOUBLE_R_TLV("Out1 Playback Volume",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 2, 63, 0, out_pga_tlv),
+ SOC_DOUBLE_R("Out1 Playback ZC Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME, 13, 1, 0),
+ SOC_WM8350_DOUBLE_R_TLV("Out2 Playback Volume",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 2, 63, 0, out_pga_tlv),
+ SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME, 13, 1, 0),
+ SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
+ SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+
+ SOC_DOUBLE_R("Out1 Playback Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 14, 1, 1),
+ SOC_DOUBLE_R("Out2 Playback Switch",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 14, 1, 1),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Playback Mixer */
+static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch",
+ WM8350_LEFT_MIXER_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Right Playback Mixer */
+static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Out4 Mixer */
+static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Right Capture Switch",
+ WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("Out3 Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Out3 Mixer */
+static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Capture Switch",
+ WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("Out4 Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Left Input Mixer */
+static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_LEFT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Right Input Mixer */
+static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_RIGHT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Left Mic Mixer */
+static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
+};
+
+/* Right Mic Mixer */
+static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
+};
+
+/* Beep Switch */
+static const struct snd_kcontrol_new wm8350_beep_switch_controls =
+SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
+
+/* Out4 Capture Mux */
+static const struct snd_kcontrol_new wm8350_out4_capture_controls =
+SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+
+static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
+
+ SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
+ 7, 0, &wm8350_right_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
+ 6, 0, &wm8350_left_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
+ &wm8350_out4_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out4_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
+ &wm8350_out3_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out3_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
+ &wm8350_right_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
+ &wm8350_left_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
+ &wm8350_left_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
+ &wm8350_right_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
+
+ /* virtual mixer for Beep and Out2R */
+ SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
+ &wm8350_beep_switch_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ WM8350_POWER_MGMT_4, 3, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+ WM8350_POWER_MGMT_4, 2, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+ WM8350_POWER_MGMT_4, 5, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+ WM8350_POWER_MGMT_4, 4, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
+
+ SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
+ &wm8350_out4_capture_controls),
+
+ SND_SOC_DAPM_OUTPUT("OUT1R"),
+ SND_SOC_DAPM_OUTPUT("OUT1L"),
+ SND_SOC_DAPM_OUTPUT("OUT2R"),
+ SND_SOC_DAPM_OUTPUT("OUT2L"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("IN1RN"),
+ SND_SOC_DAPM_INPUT("IN1RP"),
+ SND_SOC_DAPM_INPUT("IN2R"),
+ SND_SOC_DAPM_INPUT("IN1LP"),
+ SND_SOC_DAPM_INPUT("IN1LN"),
+ SND_SOC_DAPM_INPUT("IN2L"),
+ SND_SOC_DAPM_INPUT("IN3R"),
+ SND_SOC_DAPM_INPUT("IN3L"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* left playback mixer */
+ {"Left Playback Mixer", "Playback Switch", "Left DAC"},
+ {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
+ {"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* right playback mixer */
+ {"Right Playback Mixer", "Playback Switch", "Right DAC"},
+ {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
+ {"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* out4 playback mixer */
+ {"Out4 Mixer", "Right Playback Switch", "Right DAC"},
+ {"Out4 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
+ {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
+ {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
+ {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"OUT4", NULL, "Out4 Mixer"},
+
+ /* out3 playback mixer */
+ {"Out3 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
+ {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
+ {"OUT3", NULL, "Out3 Mixer"},
+
+ /* out2 */
+ {"Right Out2 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out2 PGA", NULL, "Left Playback Mixer"},
+ {"OUT2L", NULL, "Left Out2 PGA"},
+ {"OUT2R", NULL, "Right Out2 PGA"},
+
+ /* out1 */
+ {"Right Out1 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out1 PGA", NULL, "Left Playback Mixer"},
+ {"OUT1L", NULL, "Left Out1 PGA"},
+ {"OUT1R", NULL, "Right Out1 PGA"},
+
+ /* ADCs */
+ {"Left ADC", NULL, "Left Capture Mixer"},
+ {"Right ADC", NULL, "Right Capture Mixer"},
+
+ /* Left capture mixer */
+ {"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
+ {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
+ {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
+ {"Left Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* Right capture mixer */
+ {"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
+ {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
+ {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
+ {"Right Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* L3 Inputs */
+ {"IN3L PGA", NULL, "IN3L"},
+ {"IN3R PGA", NULL, "IN3R"},
+
+ /* Left Mic mixer */
+ {"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
+ {"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
+ {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
+
+ /* Right Mic mixer */
+ {"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
+ {"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
+ {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
+
+ /* out 4 capture */
+ {"Out4 Capture Channel", NULL, "Out4 Mixer"},
+
+ /* Beep */
+ {"Beep", NULL, "IN3R PGA"},
+};
+
+static int wm8350_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8350_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int wm8350_add_widgets(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(codec,
+ wm8350_dapm_widgets,
+ ARRAY_SIZE(wm8350_dapm_widgets));
+ if (ret != 0) {
+ dev_err(codec->dev, "dapm control register failed\n");
+ return ret;
+ }
+
+ /* set up audio paths */
+ ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ if (ret != 0) {
+ dev_err(codec->dev, "DAPM route register failed\n");
+ return ret;
+ }
+
+ return snd_soc_dapm_new_widgets(codec);
+}
+
+static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ u16 fll_4;
+
+ switch (clk_id) {
+ case WM8350_MCLK_SEL_MCLK:
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ break;
+ case WM8350_MCLK_SEL_PLL_MCLK:
+ case WM8350_MCLK_SEL_PLL_DAC:
+ case WM8350_MCLK_SEL_PLL_ADC:
+ case WM8350_MCLK_SEL_PLL_32K:
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ ~WM8350_FLL_CLK_SRC_MASK;
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+ break;
+ }
+
+ /* MCLK direction */
+ if (dir == WM8350_MCLK_DIR_OUT)
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+ else
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+
+ return 0;
+}
+
+static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 val;
+
+ switch (div_id) {
+ case WM8350_ADC_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+ ~WM8350_ADC_CLKDIV_MASK;
+ wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+ break;
+ case WM8350_DAC_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+ ~WM8350_DAC_CLKDIV_MASK;
+ wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+ break;
+ case WM8350_BCLK_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_BCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_OPCLK_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_OPCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_SYS_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_MCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_DACLR_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_RATE_MASK;
+ wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+ break;
+ case WM8350_ADCLR_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_RATE_MASK;
+ wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
+ u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+ ~WM8350_BCLK_MSTR;
+ u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_ENA;
+ u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_ENA;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master |= WM8350_BCLK_MSTR;
+ dac_lrc |= WM8350_DACLRC_ENA;
+ adc_lrc |= WM8350_ADCLRC_ENA;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x2 << 8;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x1 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x3 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x3 << 8; /* lg not sure which mode */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= WM8350_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+ wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
+ wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+ wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
+ return 0;
+}
+
+static int wm8350_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
+ WM8350_BCLK_MSTR;
+ int enabled = 0;
+
+ /* Check that the DACs or ADCs are enabled since they are
+ * required for LRC in master mode. The DACs or ADCs need a
+ * valid audio path i.e. pin -> ADC or DAC -> pin before
+ * the LRC will be enabled in master mode. */
+ if (!master && cmd != SNDRV_PCM_TRIGGER_START)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+ (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
+ } else {
+ enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+ (WM8350_DACR_ENA | WM8350_DACL_ENA);
+ }
+
+ if (!enabled) {
+ dev_err(codec->dev,
+ "%s: invalid audio path - no clocks available\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *codec_dai)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ ~WM8350_AIF_WL_MASK;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x1 << 10;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x2 << 10;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x3 << 10;
+ break;
+ }
+
+ wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+ return 0;
+}
+
+static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+
+ if (mute)
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ else
+ wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+ int div; /* FLL_OUTDIV */
+ int n;
+ int k;
+ int ratio; /* FLL_FRATIO */
+};
+
+/* The size in bits of the fll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
+ unsigned int output)
+{
+ u64 Kpart;
+ unsigned int t1, t2, K, Nmod;
+
+ if (output >= 2815250 && output <= 3125000)
+ fll_div->div = 0x4;
+ else if (output >= 5625000 && output <= 6250000)
+ fll_div->div = 0x3;
+ else if (output >= 11250000 && output <= 12500000)
+ fll_div->div = 0x2;
+ else if (output >= 22500000 && output <= 25000000)
+ fll_div->div = 0x1;
+ else {
+ printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
+ return -EINVAL;
+ }
+
+ if (input > 48000)
+ fll_div->ratio = 1;
+ else
+ fll_div->ratio = 8;
+
+ t1 = output * (1 << (fll_div->div + 1));
+ t2 = input * fll_div->ratio;
+
+ fll_div->n = t1 / t2;
+ Nmod = t1 % t2;
+
+ if (Nmod) {
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+ do_div(Kpart, t2);
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+ fll_div->k = K;
+ } else
+ fll_div->k = 0;
+
+ return 0;
+}
+
+static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ struct _fll_div fll_div;
+ int ret = 0;
+ u16 fll_1, fll_4;
+
+ /* power down FLL - we need to do this for reconfiguration */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
+
+ if (freq_out == 0 || freq_in == 0)
+ return ret;
+
+ ret = fll_factors(&fll_div, freq_in, freq_out);
+ if (ret < 0)
+ return ret;
+ dev_dbg(wm8350->dev,
+ "FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+ freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
+ fll_div.ratio);
+
+ /* set up N.K & dividers */
+ fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+ ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+ fll_1 | (fll_div.div << 8) | 0x50);
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+ (fll_div.ratio << 11) | (fll_div.
+ n & WM8350_FLL_N_MASK));
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+ fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+ fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
+ (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
+
+ /* power FLL on */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+
+ return 0;
+}
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *priv = codec->private_data;
+ struct wm8350_audio_platform_data *platform =
+ wm8350->codec.platform_data;
+ u16 pm1;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K |
+ platform->codec_current_on << 14);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
+ pm1 &= ~WM8350_VMID_MASK;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* Enable the system clock */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ /* mute DAC & outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
+ WM8350_DAC_MUTE_ENA);
+
+ /* discharge cap memory */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* wait for discharge */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ cap_discharge_msecs));
+
+ /* enable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* ramp up vmid */
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ (platform->
+ codec_current_charge << 14) |
+ WM8350_VMID_5K | WM8350_VMIDEN |
+ WM8350_VBUFEN);
+
+ /* wait for vmid */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_charge_msecs));
+
+ /* turn on vmid 300k */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ pm1 |= WM8350_VMID_300K |
+ (platform->codec_current_standby << 14);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1);
+
+
+ /* enable analogue bias */
+ pm1 |= WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ } else {
+ /* turn on vmid 300k and reduce current */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_300K |
+ (platform->
+ codec_current_standby << 14));
+
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+
+ /* mute DAC & enable outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
+ WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
+ WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
+
+ /* enable anti pop S curve */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* turn off vmid */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~WM8350_VMIDEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_discharge_msecs));
+
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8) |
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* turn off VBuf and drain */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VBUFEN | WM8350_VMID_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_OUTPUT_DRAIN_EN);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->drain_msecs));
+
+ pm1 &= ~WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable anti-pop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
+ WM8350_OUT1L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
+ WM8350_OUT1R_ENA);
+ wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
+ WM8350_OUT2L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
+ WM8350_OUT2R_ENA);
+
+ /* disable clock gen */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8350_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+ return 0;
+}
+
+static struct snd_soc_codec *wm8350_codec;
+
+static int wm8350_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct wm8350 *wm8350;
+ struct wm8350_data *priv;
+ int ret;
+ struct wm8350_output *out1;
+ struct wm8350_output *out2;
+
+ BUG_ON(!wm8350_codec);
+
+ socdev->codec = wm8350_codec;
+ codec = socdev->codec;
+ wm8350 = codec->control_data;
+ priv = codec->private_data;
+
+ /* Enable the codec */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ /* Enable robust clocking mode in ADC */
+ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
+ wm8350_codec_write(codec, 0xde, 0x13);
+ wm8350_codec_write(codec, WM8350_SECURITY, 0);
+
+ /* read OUT1 & OUT2 volumes */
+ out1 = &priv->out1;
+ out2 = &priv->out2;
+ out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
+ WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
+ WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
+ WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
+ WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
+
+ /* Latch VU bits & mute */
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME,
+ WM8350_OUT1_VU | WM8350_OUT1L_MUTE);
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME,
+ WM8350_OUT2_VU | WM8350_OUT2L_MUTE);
+ wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME,
+ WM8350_OUT1_VU | WM8350_OUT1R_MUTE);
+ wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
+ WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ return ret;
+ }
+
+ wm8350_add_controls(codec);
+ wm8350_add_widgets(codec);
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register card\n");
+ goto card_err;
+ }
+
+ return 0;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+}
+
+static int wm8350_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ int ret;
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(&codec->delayed_work);
+
+ /* if there was any work waiting then we run it now and
+ * wait for its completion */
+ if (ret) {
+ schedule_delayed_work(&codec->delayed_work, 0);
+ flush_scheduled_work();
+ }
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ return 0;
+}
+
+#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8350_dai = {
+ .name = "WM8350",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8350_pcm_hw_params,
+ .digital_mute = wm8350_mute,
+ .trigger = wm8350_pcm_trigger,
+ .set_fmt = wm8350_set_dai_fmt,
+ .set_sysclk = wm8350_set_dai_sysclk,
+ .set_pll = wm8350_set_fll,
+ .set_clkdiv = wm8350_set_clkdiv,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8350_dai);
+
+struct snd_soc_codec_device soc_codec_dev_wm8350 = {
+ .probe = wm8350_probe,
+ .remove = wm8350_remove,
+ .suspend = wm8350_suspend,
+ .resume = wm8350_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
+
+static int wm8350_codec_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+ struct wm8350_data *priv;
+ struct snd_soc_codec *codec;
+ int ret, i;
+
+ if (wm8350->codec.platform_data == NULL) {
+ dev_err(&pdev->dev, "No audio platform data supplied\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ goto err_priv;
+
+ codec = &priv->codec;
+ wm8350->codec.codec = codec;
+
+ wm8350_dai.dev = &pdev->dev;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->dev = &pdev->dev;
+ codec->name = "WM8350";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8350_codec_read;
+ codec->write = wm8350_codec_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8350_set_bias_level;
+ codec->dai = &wm8350_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8350_MAX_REGISTER;
+ codec->private_data = priv;
+ codec->control_data = wm8350;
+
+ /* Put the codec into reset if it wasn't already */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work);
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0)
+ goto err_supply;
+
+ wm8350_codec = codec;
+
+ ret = snd_soc_register_dai(&wm8350_dai);
+ if (ret != 0)
+ goto err_codec;
+ return 0;
+
+err_codec:
+ snd_soc_unregister_codec(codec);
+err_supply:
+ regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+err_priv:
+ kfree(priv);
+ wm8350_codec = NULL;
+ return ret;
+}
+
+static int __devexit wm8350_codec_remove(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = wm8350->codec.codec;
+ struct wm8350_data *priv = codec->private_data;
+
+ snd_soc_unregister_dai(&wm8350_dai);
+ snd_soc_unregister_codec(codec);
+ regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+ kfree(priv);
+ wm8350_codec = NULL;
+ return 0;
+}
+
+static struct platform_driver wm8350_codec_driver = {
+ .driver = {
+ .name = "wm8350-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8350_codec_probe,
+ .remove = __devexit_p(wm8350_codec_remove),
+};
+
+static __init int wm8350_init(void)
+{
+ return platform_driver_register(&wm8350_codec_driver);
+}
+module_init(wm8350_init);
+
+static __exit void wm8350_exit(void)
+{
+ platform_driver_unregister(&wm8350_codec_driver);
+}
+module_exit(wm8350_exit);
+
+MODULE_DESCRIPTION("ASoC WM8350 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-codec");
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
new file mode 100644
index 000000000000..cc2887aa6c38
--- /dev/null
+++ b/sound/soc/codecs/wm8350.h
@@ -0,0 +1,20 @@
+/*
+ * wm8350.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute 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 _WM8350_H
+#define _WM8350_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm8350_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8350;
+
+#endif
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index d8ca2da8d634..40f8238df717 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -463,7 +463,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -585,8 +586,6 @@ struct snd_soc_dai wm8510_dai = {
.formats = WM8510_FORMATS,},
.ops = {
.hw_params = wm8510_pcm_hw_params,
- },
- .dai_ops = {
.digital_mute = wm8510_mute,
.set_fmt = wm8510_set_dai_fmt,
.set_clkdiv = wm8510_set_dai_clkdiv,
@@ -659,7 +658,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8510_add_controls(codec);
wm8510_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8510: failed to register card\n");
goto card_err;
@@ -890,6 +889,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8510 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
+static int __init wm8510_modinit(void)
+{
+ return snd_soc_register_dai(&wm8510_dai);
+}
+module_init(wm8510_modinit);
+
+static void __exit wm8510_exit(void)
+{
+ snd_soc_unregister_dai(&wm8510_dai);
+}
+module_exit(wm8510_exit);
+
MODULE_DESCRIPTION("ASoC WM8510 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 627ebfb4209b..d004e5845298 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -548,13 +548,13 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
* Set PCM DAI bit size and sample rate.
*/
static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai_link *dai = rtd->dai;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
- u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
+ u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
paifb &= ~WM8580_AIF_LENGTH_MASK;
/* bit size */
@@ -574,7 +574,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+ wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
return 0;
}
@@ -798,8 +798,6 @@ struct snd_soc_dai wm8580_dai[] = {
},
.ops = {
.hw_params = wm8580_paif_hw_params,
- },
- .dai_ops = {
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
@@ -818,8 +816,6 @@ struct snd_soc_dai wm8580_dai[] = {
},
.ops = {
.hw_params = wm8580_paif_hw_params,
- },
- .dai_ops = {
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
@@ -873,7 +869,7 @@ static int wm8580_init(struct snd_soc_device *socdev)
wm8580_add_controls(codec);
wm8580_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8580: failed to register card\n");
goto card_err;
@@ -900,85 +896,85 @@ static struct snd_soc_device *wm8580_socdev;
* low = 0x1a
* high = 0x1b
*/
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-static struct i2c_driver wm8580_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8580_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8580_socdev;
- struct wm8580_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c;
int ret;
- if (addr != setup->i2c_address)
- return -ENODEV;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
- if (i2c == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
- goto err;
- }
-
ret = wm8580_init(socdev);
- if (ret < 0) {
+ if (ret < 0)
dev_err(&i2c->dev, "failed to initialise WM8580\n");
- goto err;
- }
-
- return ret;
-
-err:
- kfree(codec);
- kfree(i2c);
return ret;
}
-static int wm8580_i2c_detach(struct i2c_client *client)
+static int wm8580_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
- i2c_detach_client(client);
kfree(codec->reg_cache);
- kfree(client);
return 0;
}
-static int wm8580_i2c_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, wm8580_codec_probe);
-}
+static const struct i2c_device_id wm8580_i2c_id[] = {
+ { "wm8580", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
-/* corgi i2c codec control layer */
static struct i2c_driver wm8580_i2c_driver = {
.driver = {
.name = "WM8580 I2C Codec",
.owner = THIS_MODULE,
},
- .attach_adapter = wm8580_i2c_attach,
- .detach_client = wm8580_i2c_detach,
- .command = NULL,
+ .probe = wm8580_i2c_probe,
+ .remove = wm8580_i2c_remove,
+ .id_table = wm8580_i2c_id,
};
-static struct i2c_client client_template = {
- .name = "WM8580",
- .driver = &wm8580_i2c_driver,
-};
+static int wm8580_add_i2c_device(struct platform_device *pdev,
+ const struct wm8580_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8580_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8580_i2c_driver);
+ return -ENODEV;
+}
#endif
static int wm8580_probe(struct platform_device *pdev)
@@ -1011,11 +1007,8 @@ static int wm8580_probe(struct platform_device *pdev)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
- normal_i2c[0] = setup->i2c_address;
codec->hw_write = (hw_write_t)i2c_master_send;
- ret = i2c_add_driver(&wm8580_i2c_driver);
- if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ ret = wm8580_add_i2c_device(pdev, setup);
}
#else
/* Add other interfaces here */
@@ -1034,6 +1027,7 @@ static int wm8580_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8580_i2c_driver);
#endif
kfree(codec->private_data);
@@ -1048,6 +1042,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
+static int __init wm8580_modinit(void)
+{
+ return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_init(wm8580_modinit);
+
+static void __exit wm8580_exit(void)
+{
+ snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_exit(wm8580_exit);
+
MODULE_DESCRIPTION("ASoC WM8580 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
index 589ddaba21d7..09e4422f6f2f 100644
--- a/sound/soc/codecs/wm8580.h
+++ b/sound/soc/codecs/wm8580.h
@@ -29,6 +29,7 @@
#define WM8580_CLKSRC_NONE 5
struct wm8580_setup_data {
+ int i2c_bus;
unsigned short i2c_address;
};
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
new file mode 100644
index 000000000000..80b11983e137
--- /dev/null
+++ b/sound/soc/codecs/wm8728.c
@@ -0,0 +1,585 @@
+/*
+ * wm8728.c -- WM8728 ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8728.h"
+
+struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+/*
+ * We can't read the WM8728 register space so we cache them instead.
+ * Note that the defaults here aren't the physical defaults, we latch
+ * the volume update bits, mute the output and enable infinite zero
+ * detect.
+ */
+static const u16 wm8728_reg_defaults[] = {
+ 0x1ff,
+ 0x1ff,
+ 0x001,
+ 0x100,
+};
+
+static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+ return cache[reg];
+}
+
+static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+ cache[reg] = value;
+}
+
+/*
+ * write to the WM8728 register space
+ */
+static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ /* data is
+ * D15..D9 WM8728 register offset
+ * D8...D0 register data
+ */
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+
+ wm8728_write_reg_cache(codec, reg, value);
+
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new wm8728_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
+ 0, 255, 0, wm8728_tlv),
+
+SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
+};
+
+static int wm8728_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8728_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"VOUTL", NULL, "DAC"},
+ {"VOUTR", NULL, "DAC"},
+};
+
+static int wm8728_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
+ ARRAY_SIZE(wm8728_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+
+ return 0;
+}
+
+static int wm8728_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+ if (mute)
+ wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+ else
+ wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+
+ return 0;
+}
+
+static int wm8728_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+ dac &= ~0x18;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ dac |= 0x10;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dac |= 0x08;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8728_write(codec, WM8728_DACCTL, dac);
+
+ return 0;
+}
+
+static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+
+ /* Currently only I2S is supported by the driver, though the
+ * hardware is more flexible.
+ */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* The hardware only support full slave mode */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= ~0x22;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ iface &= ~0x02;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= 0x02;
+ iface &= ~0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x22;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8728_write(codec, WM8728_IFCTL, iface);
+ return 0;
+}
+
+static int wm8728_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg;
+ int i;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Power everything up... */
+ reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+
+ /* ..then sync in the register cache. */
+ for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
+ wm8728_write(codec, i,
+ wm8728_read_reg_cache(codec, i));
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+ wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8728_dai = {
+ .name = "WM8728",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = WM8728_RATES,
+ .formats = WM8728_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8728_hw_params,
+ .digital_mute = wm8728_mute,
+ .set_fmt = wm8728_set_dai_fmt,
+ }
+};
+EXPORT_SYMBOL_GPL(wm8728_dai);
+
+static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int wm8728_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8728_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+/*
+ * initialise the WM8728 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8728_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "WM8728";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8728_read_reg_cache;
+ codec->write = wm8728_write;
+ codec->set_bias_level = wm8728_set_bias_level;
+ codec->dai = &wm8728_dai;
+ codec->num_dai = 1;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
+ codec->reg_cache = kmemdup(wm8728_reg_defaults,
+ sizeof(wm8728_reg_defaults),
+ GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8728: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ wm8728_add_controls(codec);
+ wm8728_add_widgets(codec);
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "wm8728: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *wm8728_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8728 2 wire address is determined by GPIO5
+ * state during powerup.
+ * low = 0x1a
+ * high = 0x1b
+ */
+
+static int wm8728_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = wm8728_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = wm8728_init(socdev);
+ if (ret < 0)
+ pr_err("failed to initialise WM8728\n");
+
+ return ret;
+}
+
+static int wm8728_i2c_remove(struct i2c_client *client)
+{
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
+ kfree(codec->reg_cache);
+ return 0;
+}
+
+static const struct i2c_device_id wm8728_i2c_id[] = {
+ { "wm8728", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
+
+static struct i2c_driver wm8728_i2c_driver = {
+ .driver = {
+ .name = "WM8728 I2C Codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8728_i2c_probe,
+ .remove = wm8728_i2c_remove,
+ .id_table = wm8728_i2c_id,
+};
+
+static int wm8728_add_i2c_device(struct platform_device *pdev,
+ const struct wm8728_setup_data *setup)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ int ret;
+
+ ret = i2c_add_driver(&wm8728_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ return ret;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = setup->i2c_address;
+ strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
+
+ adapter = i2c_get_adapter(setup->i2c_bus);
+ if (!adapter) {
+ dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+ setup->i2c_bus);
+ goto err_driver;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ i2c_put_adapter(adapter);
+ if (!client) {
+ dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+ (unsigned int)info.addr);
+ goto err_driver;
+ }
+
+ return 0;
+
+err_driver:
+ i2c_del_driver(&wm8728_i2c_driver);
+ return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8728_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8728_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8728_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8728\n");
+
+ return ret;
+}
+
+static int __devexit wm8728_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8728_spi_driver = {
+ .driver = {
+ .name = "wm8728",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8728_spi_probe,
+ .remove = __devexit_p(wm8728_spi_remove),
+};
+
+static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
+static int wm8728_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8728_setup_data *setup;
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ wm8728_socdev = socdev;
+ ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ if (setup->i2c_address) {
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ ret = wm8728_add_i2c_device(pdev, setup);
+ }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8728_spi_write;
+ ret = spi_register_driver(&wm8728_spi_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add spi driver");
+ }
+#endif
+
+ if (ret != 0)
+ kfree(codec);
+
+ return ret;
+}
+
+/* power down chip */
+static int wm8728_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&wm8728_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8728_spi_driver);
+#endif
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8728 = {
+ .probe = wm8728_probe,
+ .remove = wm8728_remove,
+ .suspend = wm8728_suspend,
+ .resume = wm8728_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
+
+static int __init wm8728_modinit(void)
+{
+ return snd_soc_register_dai(&wm8728_dai);
+}
+module_init(wm8728_modinit);
+
+static void __exit wm8728_exit(void)
+{
+ snd_soc_unregister_dai(&wm8728_dai);
+}
+module_exit(wm8728_exit);
+
+MODULE_DESCRIPTION("ASoC WM8728 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8728.h b/sound/soc/codecs/wm8728.h
new file mode 100644
index 000000000000..d269c132474b
--- /dev/null
+++ b/sound/soc/codecs/wm8728.h
@@ -0,0 +1,30 @@
+/*
+ * wm8728.h -- WM8728 ASoC codec driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8728_H
+#define _WM8728_H
+
+#define WM8728_DACLVOL 0x00
+#define WM8728_DACRVOL 0x01
+#define WM8728_DACCTL 0x02
+#define WM8728_IFCTL 0x03
+
+struct wm8728_setup_data {
+ int spi;
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8728_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 7f8a7e36b33e..c444b9f2701e 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -264,7 +264,8 @@ static inline int get_coeff(int mclk, int rate)
}
static int wm8731_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -293,7 +294,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
+static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -305,7 +307,8 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void wm8731_shutdown(struct snd_pcm_substream *substream)
+static void wm8731_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -461,8 +464,6 @@ struct snd_soc_dai wm8731_dai = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
- },
- .dai_ops = {
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
@@ -544,7 +545,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
wm8731_add_controls(codec);
wm8731_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8731: failed to register card\n");
goto card_err;
@@ -792,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
+static int __init wm8731_modinit(void)
+{
+ return snd_soc_register_dai(&wm8731_dai);
+}
+module_init(wm8731_modinit);
+
+static void __exit wm8731_exit(void)
+{
+ snd_soc_unregister_dai(&wm8731_dai);
+}
+module_exit(wm8731_exit);
+
MODULE_DESCRIPTION("ASoC WM8731 driver");
MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 9b7296ee5b08..5997fa68e0d5 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -614,7 +614,8 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -709,8 +710,6 @@ struct snd_soc_dai wm8750_dai = {
.formats = WM8750_FORMATS,},
.ops = {
.hw_params = wm8750_pcm_hw_params,
- },
- .dai_ops = {
.digital_mute = wm8750_mute,
.set_fmt = wm8750_set_dai_fmt,
.set_sysclk = wm8750_set_dai_sysclk,
@@ -819,7 +818,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
wm8750_add_controls(codec);
wm8750_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8750: failed to register card\n");
goto card_err;
@@ -1086,6 +1085,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
+static int __init wm8750_modinit(void)
+{
+ return snd_soc_register_dai(&wm8750_dai);
+}
+module_init(wm8750_modinit);
+
+static void __exit wm8750_exit(void)
+{
+ snd_soc_unregister_dai(&wm8750_dai);
+}
+module_exit(wm8750_exit);
+
MODULE_DESCRIPTION("ASoC WM8750 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d426eaa22185..6c21b50c9375 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -922,7 +922,8 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
* Set PCM DAI bit size and sample rate.
*/
static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1155,7 +1156,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
* Set PCM DAI bit size and sample rate.
*/
static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1323,16 +1325,15 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
- .formats = WM8753_FORMATS,},
+ .formats = WM8753_FORMATS},
.capture = { /* dummy for fast DAI switching */
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8753_RATES,
- .formats = WM8753_FORMATS,},
+ .formats = WM8753_FORMATS},
.ops = {
- .hw_params = wm8753_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1h_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1356,8 +1357,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
- .hw_params = wm8753_pcm_hw_params,},
- .dai_ops = {
+ .hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1v_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1385,8 +1385,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
- .hw_params = wm8753_pcm_hw_params,},
- .dai_ops = {
+ .hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode2_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1410,8 +1409,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
- .hw_params = wm8753_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1439,8 +1437,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
- .hw_params = wm8753_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1608,7 +1605,7 @@ static int wm8753_init(struct snd_soc_device *socdev)
wm8753_add_controls(codec);
wm8753_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8753: failed to register card\n");
goto card_err;
@@ -1877,6 +1874,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8753 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
+static int __init wm8753_modinit(void)
+{
+ return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_init(wm8753_modinit);
+
+static void __exit wm8753_exit(void)
+{
+ snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_exit(wm8753_exit);
+
MODULE_DESCRIPTION("ASoC WM8753 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 3b326c9b5586..6767de10ded0 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -138,6 +138,10 @@
struct snd_soc_codec_device soc_codec_dev_wm8900;
struct wm8900_priv {
+ struct snd_soc_codec codec;
+
+ u16 reg_cache[WM8900_MAXREG];
+
u32 fll_in; /* FLL input frequency */
u32 fll_out; /* FLL output frequency */
};
@@ -727,7 +731,8 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
}
static int wm8900_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1117,8 +1122,6 @@ struct snd_soc_dai wm8900_dai = {
},
.ops = {
.hw_params = wm8900_hw_params,
- },
- .dai_ops = {
.set_clkdiv = wm8900_set_dai_clkdiv,
.set_pll = wm8900_set_dai_pll,
.set_fmt = wm8900_set_dai_fmt,
@@ -1283,16 +1286,28 @@ static int wm8900_resume(struct platform_device *pdev)
return 0;
}
-/*
- * initialise the WM8900 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8900_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8900_codec;
+
+static int wm8900_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
- struct snd_soc_codec *codec = socdev->codec;
- int ret = 0;
+ struct wm8900_priv *wm8900;
+ struct snd_soc_codec *codec;
unsigned int reg;
- struct i2c_client *i2c_client = socdev->codec->control_data;
+ int ret;
+
+ wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+ if (wm8900 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8900->codec;
+ codec->private_data = wm8900;
+ codec->reg_cache = &wm8900->reg_cache[0];
+ codec->reg_cache_size = WM8900_MAXREG;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "WM8900";
codec->owner = THIS_MODULE;
@@ -1300,33 +1315,28 @@ static int wm8900_init(struct snd_soc_device *socdev)
codec->write = wm8900_write;
codec->dai = &wm8900_dai;
codec->num_dai = 1;
- codec->reg_cache_size = WM8900_MAXREG;
- codec->reg_cache = kmemdup(wm8900_reg_defaults,
- sizeof(wm8900_reg_defaults), GFP_KERNEL);
-
- if (codec->reg_cache == NULL)
- return -ENOMEM;
+ codec->hw_write = (hw_write_t)i2c_master_send;
+ codec->control_data = i2c;
+ codec->set_bias_level = wm8900_set_bias_level;
+ codec->dev = &i2c->dev;
reg = wm8900_read(codec, WM8900_REG_ID);
if (reg != 0x8900) {
- dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
- reg);
- return -ENODEV;
- }
-
- codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
- if (codec->private_data == NULL) {
- ret = -ENOMEM;
- goto priv_err;
+ dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
+ ret = -ENODEV;
+ goto err;
}
/* Read back from the chip */
reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
reg = (reg >> 12) & 0xf;
- dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
+ dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
wm8900_reset(codec);
+ /* Turn the chip on */
+ wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
/* Latch the volume update bits */
wm8900_write(codec, WM8900_REG_LINVOL,
wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
@@ -1352,160 +1362,98 @@ static int wm8900_init(struct snd_soc_device *socdev)
/* Set the DAC and mixer output bias */
wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
- /* Register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
- goto pcm_err;
- }
-
- /* Turn the chip on */
- codec->bias_level = SND_SOC_BIAS_OFF;
- wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- wm8900_add_controls(codec);
- wm8900_add_widgets(codec);
-
- ret = snd_soc_register_card(socdev);
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to register card\n");
- goto card_err;
- }
- return ret;
-
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-pcm_err:
- kfree(codec->reg_cache);
-priv_err:
- kfree(codec->private_data);
- return ret;
-}
+ wm8900_dai.dev = &i2c->dev;
-static struct snd_soc_device *wm8900_socdev;
+ wm8900_codec = codec;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8900_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-{
- struct snd_soc_device *socdev = wm8900_socdev;
- struct wm8900_setup_data *setup = socdev->codec_data;
- struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c;
- int ret;
-
- if (addr != setup->i2c_address)
- return -ENODEV;
-
- dev_err(&adap->dev, "Probe on %x\n", addr);
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
- if (i2c == NULL) {
- kfree(codec);
- return -ENOMEM;
- }
- i2c_set_clientdata(i2c, codec);
- codec->control_data = i2c;
-
- ret = i2c_attach_client(i2c);
- if (ret < 0) {
- dev_err(&adap->dev,
- "failed to attach codec at addr %x\n", addr);
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
goto err;
}
- ret = wm8900_init(socdev);
- if (ret < 0) {
- dev_err(&adap->dev, "failed to initialise WM8900\n");
- goto err;
+ ret = snd_soc_register_dai(&wm8900_dai);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
}
+
return ret;
+err_codec:
+ snd_soc_unregister_codec(codec);
err:
- kfree(codec);
- kfree(i2c);
+ kfree(wm8900);
+ wm8900_codec = NULL;
return ret;
}
-static int wm8900_i2c_detach(struct i2c_client *client)
+static int wm8900_i2c_remove(struct i2c_client *client)
{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- i2c_detach_client(client);
- kfree(codec->reg_cache);
- kfree(client);
+ snd_soc_unregister_dai(&wm8900_dai);
+ snd_soc_unregister_codec(wm8900_codec);
+
+ wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
+
+ wm8900_dai.dev = NULL;
+ kfree(wm8900_codec->private_data);
+ wm8900_codec = NULL;
+
return 0;
}
-static int wm8900_i2c_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, wm8900_codec_probe);
-}
+static const struct i2c_device_id wm8900_i2c_id[] = {
+ { "wm8900", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
-/* corgi i2c codec control layer */
static struct i2c_driver wm8900_i2c_driver = {
.driver = {
- .name = "WM8900 I2C codec",
+ .name = "WM8900",
.owner = THIS_MODULE,
},
- .attach_adapter = wm8900_i2c_attach,
- .detach_client = wm8900_i2c_detach,
- .command = NULL,
-};
-
-static struct i2c_client client_template = {
- .name = "WM8900",
- .driver = &wm8900_i2c_driver,
+ .probe = wm8900_i2c_probe,
+ .remove = wm8900_i2c_remove,
+ .id_table = wm8900_i2c_id,
};
-#endif
static int wm8900_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8900_setup_data *setup;
struct snd_soc_codec *codec;
int ret = 0;
- dev_info(&pdev->dev, "WM8900 Audio Codec\n");
-
- setup = socdev->codec_data;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
-
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
+ if (!wm8900_codec) {
+ dev_err(&pdev->dev, "I2C client not yet instantiated\n");
+ return -ENODEV;
+ }
+ codec = wm8900_codec;
socdev->codec = codec;
- codec->set_bias_level = wm8900_set_bias_level;
+ /* Register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register new PCMs\n");
+ goto pcm_err;
+ }
- wm8900_socdev = socdev;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- if (setup->i2c_address) {
- normal_i2c[0] = setup->i2c_address;
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = i2c_add_driver(&wm8900_i2c_driver);
- if (ret != 0)
- printk(KERN_ERR "can't add i2c driver");
+ wm8900_add_controls(codec);
+ wm8900_add_widgets(codec);
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register card\n");
+ goto card_err;
}
-#else
-#error Non-I2C interfaces not yet supported
-#endif
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
return ret;
}
@@ -1513,17 +1461,9 @@ static int wm8900_probe(struct platform_device *pdev)
static int wm8900_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
-
- if (codec->control_data)
- wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&wm8900_i2c_driver);
-#endif
- kfree(codec);
return 0;
}
@@ -1536,6 +1476,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8900 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
+static int __init wm8900_modinit(void)
+{
+ return i2c_add_driver(&wm8900_i2c_driver);
+}
+module_init(wm8900_modinit);
+
+static void __exit wm8900_exit(void)
+{
+ i2c_del_driver(&wm8900_i2c_driver);
+}
+module_exit(wm8900_exit);
+
MODULE_DESCRIPTION("ASoC WM8900 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h
index ba450d99e902..fd15007d10c7 100644
--- a/sound/soc/codecs/wm8900.h
+++ b/sound/soc/codecs/wm8900.h
@@ -52,12 +52,6 @@
#define WM8900_DAC_CLKDIV_5_5 0x14
#define WM8900_DAC_CLKDIV_6 0x18
-#define WM8900_
-
-struct wm8900_setup_data {
- unsigned short i2c_address;
-};
-
extern struct snd_soc_dai wm8900_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8900;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index ce40d7877605..bde74546db4a 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -33,19 +33,6 @@
#include "wm8903.h"
-struct wm8903_priv {
- int sysclk;
-
- /* Reference counts */
- int charge_pump_users;
- int class_w_users;
- int playback_active;
- int capture_active;
-
- struct snd_pcm_substream *master_substream;
- struct snd_pcm_substream *slave_substream;
-};
-
/* Register defaults at reset */
static u16 wm8903_reg_defaults[] = {
0x8903, /* R0 - SW Reset and ID */
@@ -223,6 +210,23 @@ static u16 wm8903_reg_defaults[] = {
0x0000, /* R172 - Analogue Output Bias 0 */
};
+struct wm8903_priv {
+ struct snd_soc_codec codec;
+ u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)];
+
+ int sysclk;
+
+ /* Reference counts */
+ int charge_pump_users;
+ int class_w_users;
+ int playback_active;
+ int capture_active;
+
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+
static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -360,6 +364,8 @@ static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
static void wm8903_reset(struct snd_soc_codec *codec)
{
wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+ memcpy(codec->reg_cache, wm8903_reg_defaults,
+ sizeof(wm8903_reg_defaults));
}
#define WM8903_OUTPUT_SHORT 0x8
@@ -392,6 +398,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
break;
default:
BUG();
+ return -EINVAL; /* Spurious warning from some compilers */
}
switch (w->shift) {
@@ -403,6 +410,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
break;
default:
BUG();
+ return -EINVAL; /* Spurious warning from some compilers */
}
if (event & SND_SOC_DAPM_PRE_PMU) {
@@ -773,14 +781,14 @@ static const struct snd_kcontrol_new left_output_mixer[] = {
SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0),
};
static const struct snd_kcontrol_new right_output_mixer[] = {
SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0),
};
static const struct snd_kcontrol_new left_speaker_mixer[] = {
@@ -788,7 +796,7 @@ SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
- 1, 1, 0),
+ 0, 1, 0),
};
static const struct snd_kcontrol_new right_speaker_mixer[] = {
@@ -797,7 +805,7 @@ SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
1, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
- 1, 1, 0),
+ 0, 1, 0),
};
static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
@@ -989,6 +997,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ wm8903_write(codec, WM8903_CLOCK_RATES_2,
+ WM8903_CLK_SYS_ENA);
+
wm8903_run_sequence(codec, 0);
wm8903_sync_reg_cache(codec, codec->reg_cache);
@@ -1019,6 +1030,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
wm8903_run_sequence(codec, 32);
+ reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
+ reg &= ~WM8903_CLK_SYS_ENA;
+ wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
break;
}
@@ -1257,7 +1271,8 @@ static struct {
{ 0, 0 },
};
-static int wm8903_startup(struct snd_pcm_substream *substream)
+static int wm8903_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1298,7 +1313,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream)
return 0;
}
-static void wm8903_shutdown(struct snd_pcm_substream *substream)
+static void wm8903_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1317,7 +1333,8 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream)
}
static int wm8903_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1515,8 +1532,6 @@ struct snd_soc_dai wm8903_dai = {
.startup = wm8903_startup,
.shutdown = wm8903_shutdown,
.hw_params = wm8903_hw_params,
- },
- .dai_ops = {
.digital_mute = wm8903_digital_mute,
.set_fmt = wm8903_set_dai_fmt,
.set_sysclk = wm8903_set_dai_sysclk
@@ -1560,39 +1575,48 @@ static int wm8903_resume(struct platform_device *pdev)
return 0;
}
-/*
- * initialise the WM8903 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8903_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8903_codec;
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
- struct snd_soc_codec *codec = socdev->codec;
- struct i2c_client *i2c = codec->control_data;
- int ret = 0;
+ struct wm8903_priv *wm8903;
+ struct snd_soc_codec *codec;
+ int ret;
u16 val;
- val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
- if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
- dev_err(&i2c->dev,
- "Device with ID register %x is not a WM8903\n", val);
- return -ENODEV;
- }
+ wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
+ if (wm8903 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8903->codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ codec->dev = &i2c->dev;
codec->name = "WM8903";
codec->owner = THIS_MODULE;
codec->read = wm8903_read;
codec->write = wm8903_write;
+ codec->hw_write = (hw_write_t)i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8903_set_bias_level;
codec->dai = &wm8903_dai;
codec->num_dai = 1;
- codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
- codec->reg_cache = kmemdup(wm8903_reg_defaults,
- sizeof(wm8903_reg_defaults),
- GFP_KERNEL);
- if (codec->reg_cache == NULL) {
- dev_err(&i2c->dev, "Failed to allocate register cache\n");
- return -ENOMEM;
+ codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
+ codec->reg_cache = &wm8903->reg_cache[0];
+ codec->private_data = wm8903;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+ if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not a WM8903\n", val);
+ return -ENODEV;
}
val = wm8903_read(codec, WM8903_REVISION_NUMBER);
@@ -1601,16 +1625,6 @@ static int wm8903_init(struct snd_soc_device *socdev)
wm8903_reset(codec);
- /* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0) {
- dev_err(&i2c->dev, "failed to create pcms\n");
- goto pcm_err;
- }
-
- /* SYSCLK is required for pretty much anything */
- wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
-
/* power on device */
wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1645,47 +1659,45 @@ static int wm8903_init(struct snd_soc_device *socdev)
val |= WM8903_DAC_MUTEMODE;
wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
- wm8903_add_controls(codec);
- wm8903_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
- if (ret < 0) {
- dev_err(&i2c->dev, "wm8903: failed to register card\n");
- goto card_err;
+ wm8903_dai.dev = &i2c->dev;
+ wm8903_codec = codec;
+
+ ret = snd_soc_register_codec(codec);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ ret = snd_soc_register_dai(&wm8903_dai);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+ goto err_codec;
}
return ret;
-card_err:
- snd_soc_free_pcms(socdev);
- snd_soc_dapm_free(socdev);
-pcm_err:
- kfree(codec->reg_cache);
+err_codec:
+ snd_soc_unregister_codec(codec);
+err:
+ wm8903_codec = NULL;
+ kfree(wm8903);
return ret;
}
-static struct snd_soc_device *wm8903_socdev;
-
-static int wm8903_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int wm8903_i2c_remove(struct i2c_client *client)
{
- struct snd_soc_device *socdev = wm8903_socdev;
- struct snd_soc_codec *codec = socdev->codec;
- int ret;
+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
- i2c_set_clientdata(i2c, codec);
- codec->control_data = i2c;
+ snd_soc_unregister_dai(&wm8903_dai);
+ snd_soc_unregister_codec(codec);
- ret = wm8903_init(socdev);
- if (ret < 0)
- dev_err(&i2c->dev, "Device initialisation failed\n");
+ wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return ret;
-}
+ kfree(codec->private_data);
+
+ wm8903_codec = NULL;
+ wm8903_dai.dev = NULL;
-static int wm8903_i2c_remove(struct i2c_client *client)
-{
- struct snd_soc_codec *codec = i2c_get_clientdata(client);
- kfree(codec->reg_cache);
return 0;
}
@@ -1709,75 +1721,37 @@ static struct i2c_driver wm8903_i2c_driver = {
static int wm8903_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct wm8903_setup_data *setup;
- struct snd_soc_codec *codec;
- struct wm8903_priv *wm8903;
- struct i2c_board_info board_info;
- struct i2c_adapter *adapter;
- struct i2c_client *i2c_client;
int ret = 0;
- setup = socdev->codec_data;
-
- if (!setup->i2c_address) {
- dev_err(&pdev->dev, "No codec address provided\n");
- return -ENODEV;
+ if (!wm8903_codec) {
+ dev_err(&pdev->dev, "I2C device not yet probed\n");
+ goto err;
}
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
- if (codec == NULL)
- return -ENOMEM;
+ socdev->codec = wm8903_codec;
- wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
- if (wm8903 == NULL) {
- ret = -ENOMEM;
- goto err_codec;
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create pcms\n");
+ goto err;
}
- codec->private_data = wm8903;
- socdev->codec = codec;
- mutex_init(&codec->mutex);
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
-
- wm8903_socdev = socdev;
+ wm8903_add_controls(socdev->codec);
+ wm8903_add_widgets(socdev->codec);
- codec->hw_write = (hw_write_t)i2c_master_send;
- ret = i2c_add_driver(&wm8903_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add i2c driver\n");
- goto err_priv;
- } else {
- memset(&board_info, 0, sizeof(board_info));
- strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
- board_info.addr = setup->i2c_address;
-
- adapter = i2c_get_adapter(setup->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "Can't get I2C bus %d\n",
- setup->i2c_bus);
- ret = -ENODEV;
- goto err_adapter;
- }
-
- i2c_client = i2c_new_device(adapter, &board_info);
- i2c_put_adapter(adapter);
- if (i2c_client == NULL) {
- dev_err(&pdev->dev,
- "I2C driver registration failed\n");
- ret = -ENODEV;
- goto err_adapter;
- }
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "wm8903: failed to register card\n");
+ goto card_err;
}
return ret;
-err_adapter:
- i2c_del_driver(&wm8903_i2c_driver);
-err_priv:
- kfree(codec->private_data);
-err_codec:
- kfree(codec);
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+err:
return ret;
}
@@ -1792,10 +1766,6 @@ static int wm8903_remove(struct platform_device *pdev)
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
- i2c_unregister_device(socdev->codec->control_data);
- i2c_del_driver(&wm8903_i2c_driver);
- kfree(codec->private_data);
- kfree(codec);
return 0;
}
@@ -1808,6 +1778,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8903 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
+static int __init wm8903_modinit(void)
+{
+ return i2c_add_driver(&wm8903_i2c_driver);
+}
+module_init(wm8903_modinit);
+
+static void __exit wm8903_exit(void)
+{
+ i2c_del_driver(&wm8903_i2c_driver);
+}
+module_exit(wm8903_exit);
+
MODULE_DESCRIPTION("ASoC WM8903 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h
index cec622f2f660..0ea27e2b9963 100644
--- a/sound/soc/codecs/wm8903.h
+++ b/sound/soc/codecs/wm8903.h
@@ -18,11 +18,6 @@
extern struct snd_soc_dai wm8903_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8903;
-struct wm8903_setup_data {
- int i2c_bus;
- int i2c_address;
-};
-
#define WM8903_MCLK_DIV_2 1
#define WM8903_CLK_SYS 2
#define WM8903_BCLK 3
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index f41a578ddd4f..88ead7f8dd98 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -541,7 +541,8 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -634,8 +635,6 @@ struct snd_soc_dai wm8971_dai = {
.formats = WM8971_FORMATS,},
.ops = {
.hw_params = wm8971_pcm_hw_params,
- },
- .dai_ops = {
.digital_mute = wm8971_mute,
.set_fmt = wm8971_set_dai_fmt,
.set_sysclk = wm8971_set_dai_sysclk,
@@ -748,7 +747,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
wm8971_add_controls(codec);
wm8971_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8971: failed to register card\n");
goto card_err;
@@ -936,6 +935,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8971 = {
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
+static int __init wm8971_modinit(void)
+{
+ return snd_soc_register_dai(&wm8971_dai);
+}
+module_init(wm8971_modinit);
+
+static void __exit wm8971_exit(void)
+{
+ snd_soc_unregister_dai(&wm8971_dai);
+}
+module_exit(wm8971_exit);
+
MODULE_DESCRIPTION("ASoC WM8971 driver");
MODULE_AUTHOR("Lab126");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 572d22b0880b..5b5afc144478 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -106,6 +106,7 @@ static const u16 wm8990_reg[] = {
0x0008, /* R60 - PLL1 */
0x0031, /* R61 - PLL2 */
0x0026, /* R62 - PLL3 */
+ 0x0000, /* R63 - Driver internal */
};
/*
@@ -126,10 +127,9 @@ static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
- BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
- /* Reset register is uncached */
- if (reg == 0)
+ /* Reset register and reserved registers are uncached */
+ if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
return;
cache[reg] = value;
@@ -1172,7 +1172,8 @@ static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
* Set PCM DAI bit size and sample rate.
*/
static int wm8990_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
@@ -1222,8 +1223,14 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
break;
+
case SND_SOC_BIAS_PREPARE:
+ /* VMID=2*50k */
+ val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+ ~WM8990_VMID_MODE_MASK;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
break;
+
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Enable all output discharge bits */
@@ -1272,10 +1279,17 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
- } else {
- /* ON -> standby */
+ /* Enable workaround for ADC clocking issue. */
+ wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+ wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
+ wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
}
+
+ /* VMID=2*250k */
+ val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+ ~WM8990_VMID_MODE_MASK;
+ wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
break;
case SND_SOC_BIAS_OFF:
@@ -1349,8 +1363,7 @@ struct snd_soc_dai wm8990_dai = {
.rates = WM8990_RATES,
.formats = WM8990_FORMATS,},
.ops = {
- .hw_params = wm8990_hw_params,},
- .dai_ops = {
+ .hw_params = wm8990_hw_params,
.digital_mute = wm8990_mute,
.set_fmt = wm8990_set_dai_fmt,
.set_clkdiv = wm8990_set_dai_clkdiv,
@@ -1449,7 +1462,7 @@ static int wm8990_init(struct snd_soc_device *socdev)
wm8990_add_controls(codec);
wm8990_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8990: failed to register card\n");
goto card_err;
@@ -1630,6 +1643,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8990 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
+static int __init wm8990_modinit(void)
+{
+ return snd_soc_register_dai(&wm8990_dai);
+}
+module_init(wm8990_modinit);
+
+static void __exit wm8990_exit(void)
+{
+ snd_soc_unregister_dai(&wm8990_dai);
+}
+module_exit(wm8990_exit);
+
MODULE_DESCRIPTION("ASoC WM8990 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h
index 0e192f3b0788..7114ddc88b4b 100644
--- a/sound/soc/codecs/wm8990.h
+++ b/sound/soc/codecs/wm8990.h
@@ -80,8 +80,8 @@
#define WM8990_PLL3 0x3E
#define WM8990_INTDRIVBITS 0x3F
-#define WM8990_REGISTER_COUNT 60
-#define WM8990_MAX_REGISTER 0x3F
+#define WM8990_EXT_ACCESS_ENA 0x75
+#define WM8990_EXT_CTL1 0x7a
/*
* Field Definitions.
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index ffb471e420e2..af83d629078a 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -487,7 +487,8 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -507,7 +508,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
return ac97_write(codec, reg, runtime->rate);
}
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -533,7 +535,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
struct snd_soc_dai wm9712_dai[] = {
{
.name = "AC97 HiFi",
- .type = SND_SOC_DAI_AC97_BUS,
+ .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -688,7 +690,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
ret = wm9712_reset(codec, 0);
if (ret < 0) {
- printk(KERN_ERR "AC97 link error\n");
+ printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
goto reset_err;
}
@@ -698,7 +700,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm9712_add_controls(codec);
wm9712_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register card\n");
goto reset_err;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 945b32ed9884..f3ca8aaf0139 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -928,11 +928,10 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
}
static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = dai->codec;
u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
switch (params_format(params)) {
@@ -954,11 +953,10 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
+static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_codec *codec = dai->codec;
u16 status;
/* Gracefully shut down the voice interface. */
@@ -969,12 +967,11 @@ static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
ac97_write(codec, AC97_EXTENDED_MID, status);
}
-static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
+static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
+ struct snd_soc_codec *codec = dai->codec;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
int reg;
u16 vra;
@@ -989,12 +986,11 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
return ac97_write(codec, reg, runtime->rate);
}
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
+ struct snd_soc_codec *codec = dai->codec;
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_device *socdev = rtd->socdev;
- struct snd_soc_codec *codec = socdev->codec;
u16 vra, xsle;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
@@ -1028,7 +1024,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
struct snd_soc_dai wm9713_dai[] = {
{
.name = "AC97 HiFi",
- .type = SND_SOC_DAI_AC97_BUS,
+ .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -1042,8 +1038,7 @@ struct snd_soc_dai wm9713_dai[] = {
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
- .prepare = ac97_hifi_prepare,},
- .dai_ops = {
+ .prepare = ac97_hifi_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,},
},
@@ -1056,8 +1051,7 @@ struct snd_soc_dai wm9713_dai[] = {
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
- .prepare = ac97_aux_prepare,},
- .dai_ops = {
+ .prepare = ac97_aux_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,},
},
@@ -1077,8 +1071,7 @@ struct snd_soc_dai wm9713_dai[] = {
.formats = WM9713_PCM_FORMATS,},
.ops = {
.hw_params = wm9713_pcm_hw_params,
- .shutdown = wm9713_voiceshutdown,},
- .dai_ops = {
+ .shutdown = wm9713_voiceshutdown,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
.set_fmt = wm9713_set_dai_fmt,
@@ -1097,6 +1090,8 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
}
soc_ac97_ops.reset(codec->ac97);
+ if (soc_ac97_ops.warm_reset)
+ soc_ac97_ops.warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
@@ -1240,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
wm9713_reset(codec, 0);
ret = wm9713_reset(codec, 1);
if (ret < 0) {
- printk(KERN_ERR "AC97 link error\n");
+ printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
goto reset_err;
}
@@ -1252,7 +1247,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
wm9713_add_controls(codec);
wm9713_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
+ ret = snd_soc_init_card(socdev);
if (ret < 0)
goto reset_err;
return 0;
@@ -1288,7 +1283,6 @@ static int wm9713_soc_remove(struct platform_device *pdev)
snd_soc_free_ac97_codec(codec);
kfree(codec->private_data);
kfree(codec->reg_cache);
- kfree(codec->dai);
kfree(codec);
return 0;
}
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 8f7e33834902..b502741692d6 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -17,3 +17,13 @@ config SND_DAVINCI_SOC_EVM
help
Say Y if you want to add support for SoC audio on TI
DaVinci EVM platform.
+
+config SND_DAVINCI_SOC_SFFSDR
+ tristate "SoC Audio support for SFFSDR"
+ depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
+ select SND_DAVINCI_SOC_I2S
+ select SND_SOC_PCM3008
+ select SFFSDR_FPGA
+ help
+ Say Y if you want to add support for SoC audio on
+ Lyrtech SFFSDR board.
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index ca772e5b4637..ca8bae1fc3f6 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
# DAVINCI Machine Support
snd-soc-evm-objs := davinci-evm.o
+snd-soc-sffsdr-objs := davinci-sffsdr.o
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 9e6062cd6b59..01b948bb55a1 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -28,6 +28,8 @@
#define EVM_CODEC_CLOCK 22579200
+#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
static int evm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -37,14 +39,12 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
int ret = 0;
/* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_CBM_CFM);
+ ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF);
+ ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
@@ -128,8 +128,9 @@ static struct snd_soc_dai_link evm_dai = {
};
/* davinci-evm audio machine driver */
-static struct snd_soc_machine snd_soc_machine_evm = {
+static struct snd_soc_card snd_soc_card_evm = {
.name = "DaVinci EVM",
+ .platform = &davinci_soc_platform,
.dai_link = &evm_dai,
.num_links = 1,
};
@@ -142,8 +143,7 @@ static struct aic3x_setup_data evm_aic3x_setup = {
/* evm audio subsystem */
static struct snd_soc_device evm_snd_devdata = {
- .machine = &snd_soc_machine_evm,
- .platform = &davinci_soc_platform,
+ .card = &snd_soc_card_evm,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &evm_aic3x_setup,
};
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index abb5fedb0b1e..0fee779e3c76 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -59,6 +59,7 @@
#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
+#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
@@ -110,16 +111,59 @@ static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_platform *platform = socdev->card->platform;
u32 w;
+ int ret;
/* Start the sample generator and enable transmitter/receiver */
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Stop the DMA to avoid data loss */
+ /* while the transmitter is out of reset to handle XSYNCERR */
+ if (platform->pcm_ops->trigger) {
+ ret = platform->pcm_ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ printk(KERN_DEBUG "Playback DMA stop failed\n");
+ }
+
+ /* Enable the transmitter */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
- else
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+ /* wait for any unexpected frame sync error to occur */
+ udelay(100);
+
+ /* Disable the transmitter to clear any outstanding XSYNCERR */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+ /* Restart the DMA */
+ if (platform->pcm_ops->trigger) {
+ ret = platform->pcm_ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_START);
+ if (ret < 0)
+ printk(KERN_DEBUG "Playback DMA start failed\n");
+ }
+ /* Enable the transmitter */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+ } else {
+
+ /* Enable the reciever */
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ }
+
/* Start frame sync */
w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -144,7 +188,8 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
}
-static int davinci_i2s_startup(struct snd_pcm_substream *substream)
+static int davinci_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -155,61 +200,138 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
return 0;
}
+#define DEFAULT_BITPERSAMPLE 16
+
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
- u32 w;
+ unsigned int pcr;
+ unsigned int srgr;
+ unsigned int rcr;
+ unsigned int xcr;
+ srgr = DAVINCI_MCBSP_SRGR_FSGM |
+ DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
+ DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
- DAVINCI_MCBSP_PCR_FSXM |
- DAVINCI_MCBSP_PCR_FSRM |
- DAVINCI_MCBSP_PCR_CLKXM |
- DAVINCI_MCBSP_PCR_CLKRM);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
- DAVINCI_MCBSP_SRGR_FSGM);
+ /* cpu is master */
+ pcr = DAVINCI_MCBSP_PCR_FSXM |
+ DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* McBSP CLKR pin is the input for the Sample Rate Generator.
+ * McBSP FSR and FSX are driven by the Sample Rate Generator. */
+ pcr = DAVINCI_MCBSP_PCR_SCLKME |
+ DAVINCI_MCBSP_PCR_FSXM |
+ DAVINCI_MCBSP_PCR_FSRM;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
+ /* codec is master */
+ pcr = 0;
break;
default:
+ printk(KERN_ERR "%s:bad master\n", __func__);
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_NF:
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
- DAVINCI_MCBSP_PCR_CLKRP, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
+ xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_B:
break;
- case SND_SOC_DAIFMT_NB_IF:
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP |
- DAVINCI_MCBSP_PCR_FSRP, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ case SND_SOC_DAIFMT_I2S:
+ /* Davinci doesn't support TRUE I2S, but some codecs will have
+ * the left and right channels contiguous. This allows
+ * dsp_a mode to be used with an inverted normal frame clk.
+ * If your codec is master and does not have contiguous
+ * channels, then you will have sound on only one channel.
+ * Try using a different mode, or codec as slave.
+ *
+ * The TLV320AIC33 is an example of a codec where this works.
+ * It has a variable bit clock frequency allowing it to have
+ * valid data on every bit clock.
+ *
+ * The TLV320AIC23 is an example of a codec where this does not
+ * work. It has a fixed bit clock frequency with progressively
+ * more empty bit clock slots between channels as the sample
+ * rate is lowered.
+ */
+ fmt ^= SND_SOC_DAIFMT_NB_IF;
+ case SND_SOC_DAIFMT_DSP_A:
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ break;
+ default:
+ printk(KERN_ERR "%s:bad format\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* CLKRP Receive clock polarity,
+ * 1 - sampled on rising edge of CLKR
+ * valid on rising edge
+ * CLKXP Transmit clock polarity,
+ * 1 - clocked on falling edge of CLKX
+ * valid on rising edge
+ * FSRP Receive frame sync pol, 0 - active high
+ * FSXP Transmit frame sync pol, 0 - active high
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
break;
case SND_SOC_DAIFMT_IB_IF:
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
- DAVINCI_MCBSP_PCR_CLKRP |
- DAVINCI_MCBSP_PCR_FSXP |
- DAVINCI_MCBSP_PCR_FSRP, 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+ /* CLKRP Receive clock polarity,
+ * 0 - sampled on falling edge of CLKR
+ * valid on falling edge
+ * CLKXP Transmit clock polarity,
+ * 0 - clocked on rising edge of CLKX
+ * valid on falling edge
+ * FSRP Receive frame sync pol, 1 - active low
+ * FSXP Transmit frame sync pol, 1 - active low
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
break;
- case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ /* CLKRP Receive clock polarity,
+ * 1 - sampled on rising edge of CLKR
+ * valid on rising edge
+ * CLKXP Transmit clock polarity,
+ * 1 - clocked on falling edge of CLKX
+ * valid on rising edge
+ * FSRP Receive frame sync pol, 1 - active low
+ * FSXP Transmit frame sync pol, 1 - active low
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
+ DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* CLKRP Receive clock polarity,
+ * 0 - sampled on falling edge of CLKR
+ * valid on falling edge
+ * CLKXP Transmit clock polarity,
+ * 0 - clocked on rising edge of CLKX
+ * valid on falling edge
+ * FSRP Receive frame sync pol, 0 - active high
+ * FSXP Transmit frame sync pol, 0 - active high
+ */
break;
default:
return -EINVAL;
}
-
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
return 0;
}
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
@@ -219,25 +341,20 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
u32 w;
/* general line settings */
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
- DAVINCI_MCBSP_SPCR_RINTM(3) |
- DAVINCI_MCBSP_SPCR_XINTM(3) |
- DAVINCI_MCBSP_SPCR_FREE);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
- DAVINCI_MCBSP_RCR_RFRLEN1(1) |
- DAVINCI_MCBSP_RCR_RDATDLY(1));
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
- DAVINCI_MCBSP_XCR_XFRLEN1(1) |
- DAVINCI_MCBSP_XCR_XDATDLY(1) |
- DAVINCI_MCBSP_XCR_XFIG);
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ } else {
+ w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+ }
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+ w = DAVINCI_MCBSP_SRGR_FSGM;
MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
@@ -260,20 +377,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
- w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
- MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+ } else {
+ w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
+ MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+ }
return 0;
}
-static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
int ret = 0;
@@ -299,8 +420,8 @@ static int davinci_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev;
struct resource *mem, *ioarea;
struct evm_snd_platform_data *pdata;
@@ -361,8 +482,8 @@ static void davinci_i2s_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
struct resource *mem;
@@ -381,7 +502,6 @@ static void davinci_i2s_remove(struct platform_device *pdev,
struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.probe = davinci_i2s_probe,
.remove = davinci_i2s_remove,
.playback = {
@@ -397,13 +517,24 @@ struct snd_soc_dai davinci_i2s_dai = {
.ops = {
.startup = davinci_i2s_startup,
.trigger = davinci_i2s_trigger,
- .hw_params = davinci_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt,
},
};
EXPORT_SYMBOL_GPL(davinci_i2s_dai);
+static int __init davinci_i2s_init(void)
+{
+ return snd_soc_register_dai(&davinci_i2s_dai);
+}
+module_init(davinci_i2s_init);
+
+static void __exit davinci_i2s_exit(void)
+{
+ snd_soc_unregister_dai(&davinci_i2s_dai);
+}
+module_exit(davinci_i2s_exit);
+
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index 76feaa657375..74abc9b4f1cc 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -24,13 +25,6 @@
#include "davinci-pcm.h"
-#define DAVINCI_PCM_DEBUG 0
-#if DAVINCI_PCM_DEBUG
-#define DPRINTK(x...) printk(KERN_DEBUG x)
-#else
-#define DPRINTK(x...)
-#endif
-
static struct snd_pcm_hardware davinci_pcm_hardware = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
@@ -78,8 +72,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
dma_offset = prtd->period * period_size;
dma_pos = runtime->dma_addr + dma_offset;
- DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x "
- "period_size=%x\n", lch, dma_pos, period_size);
+ pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
+ "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
data_type = prtd->params->data_type;
count = period_size / data_type;
@@ -112,7 +106,7 @@ static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data)
struct snd_pcm_substream *substream = data;
struct davinci_runtime_data *prtd = substream->runtime->private_data;
- DPRINTK("lch=%d, status=0x%x\n", lch, ch_status);
+ pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
if (unlikely(ch_status != DMA_COMPLETE))
return;
@@ -316,8 +310,8 @@ static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
- DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
- (void *) buf->area, (void *) buf->addr, size);
+ pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
+ "size=%d\n", (void *) buf->area, (void *) buf->addr, size);
if (!buf->area)
return -ENOMEM;
@@ -384,6 +378,18 @@ struct snd_soc_platform davinci_soc_platform = {
};
EXPORT_SYMBOL_GPL(davinci_soc_platform);
+static int __init davinci_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&davinci_soc_platform);
+}
+module_init(davinci_soc_platform_init);
+
+static void __exit davinci_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&davinci_soc_platform);
+}
+module_exit(davinci_soc_platform_exit);
+
MODULE_AUTHOR("Vladimir Barinov");
MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
new file mode 100644
index 000000000000..f67579d52765
--- /dev/null
+++ b/sound/soc/davinci/davinci-sffsdr.c
@@ -0,0 +1,157 @@
+/*
+ * ASoC driver for Lyrtech SFFSDR board.
+ *
+ * Author: Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/dma.h>
+#include <asm/plat-sffsdr/sffsdr-fpga.h>
+
+#include <mach/mcbsp.h>
+#include <mach/edma.h>
+
+#include "../codecs/pcm3008.h"
+#include "davinci-pcm.h"
+#include "davinci-i2s.h"
+
+static int sffsdr_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int fs;
+ int ret = 0;
+
+ /* Set cpu DAI configuration:
+ * CLKX and CLKR are the inputs for the Sample Rate Generator.
+ * FSX and FSR are outputs, driven by the sample Rate Generator. */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_RIGHT_J |
+ SND_SOC_DAIFMT_CBM_CFS |
+ SND_SOC_DAIFMT_IB_NF);
+ if (ret < 0)
+ return ret;
+
+ /* Fsref can be 32000, 44100 or 48000. */
+ fs = params_rate(params);
+
+ pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
+
+ return sffsdr_fpga_set_codec_fs(fs);
+}
+
+static struct snd_soc_ops sffsdr_ops = {
+ .hw_params = sffsdr_hw_params,
+};
+
+/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sffsdr_dai = {
+ .name = "PCM3008", /* Codec name */
+ .stream_name = "PCM3008 HiFi",
+ .cpu_dai = &davinci_i2s_dai,
+ .codec_dai = &pcm3008_dai,
+ .ops = &sffsdr_ops,
+};
+
+/* davinci-sffsdr audio machine driver */
+static struct snd_soc_card snd_soc_sffsdr = {
+ .name = "DaVinci SFFSDR",
+ .platform = &davinci_soc_platform,
+ .dai_link = &sffsdr_dai,
+ .num_links = 1,
+};
+
+/* sffsdr audio private data */
+static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
+ .dem0_pin = GPIO(45),
+ .dem1_pin = GPIO(46),
+ .pdad_pin = GPIO(47),
+ .pdda_pin = GPIO(38),
+};
+
+/* sffsdr audio subsystem */
+static struct snd_soc_device sffsdr_snd_devdata = {
+ .card = &snd_soc_sffsdr,
+ .codec_dev = &soc_codec_dev_pcm3008,
+ .codec_data = &sffsdr_pcm3008_setup,
+};
+
+static struct resource sffsdr_snd_resources[] = {
+ {
+ .start = DAVINCI_MCBSP_BASE,
+ .end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct evm_snd_platform_data sffsdr_snd_data = {
+ .tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
+ .rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
+};
+
+static struct platform_device *sffsdr_snd_device;
+
+static int __init sffsdr_init(void)
+{
+ int ret;
+
+ sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!sffsdr_snd_device) {
+ printk(KERN_ERR "platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
+ sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
+ sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
+
+ ret = platform_device_add_resources(sffsdr_snd_device,
+ sffsdr_snd_resources,
+ ARRAY_SIZE(sffsdr_snd_resources));
+ if (ret) {
+ printk(KERN_ERR "platform device add ressources failed\n");
+ goto error;
+ }
+
+ ret = platform_device_add(sffsdr_snd_device);
+ if (ret)
+ goto error;
+
+ return ret;
+
+error:
+ platform_device_put(sffsdr_snd_device);
+ return ret;
+}
+
+static void __exit sffsdr_exit(void)
+{
+ platform_device_unregister(sffsdr_snd_device);
+}
+
+module_init(sffsdr_init);
+module_exit(sffsdr_exit);
+
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 8d73edc56102..95c12b26fe37 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -20,7 +20,7 @@ config SND_SOC_MPC8610_HPCD
config SND_SOC_MPC5200_I2S
tristate "Freescale MPC5200 PSC in I2S mode driver"
- depends on SND_SOC && PPC_MPC52xx && PPC_BESTCOMM
+ depends on PPC_MPC52xx && PPC_BESTCOMM
select SND_SOC_OF_SIMPLE
select PPC_BESTCOMM_GEN_BD
help
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index d2d3da9729f2..64993eda5679 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -284,7 +284,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
* fsl_dma_new: initialize this PCM driver.
*
* This function is called when the codec driver calls snd_soc_new_pcms(),
- * once for each .dai_link in the machine driver's snd_soc_machine
+ * once for each .dai_link in the machine driver's snd_soc_card
* structure.
*/
static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
@@ -853,6 +853,18 @@ int fsl_dma_configure(struct fsl_dma_info *dma_info)
}
EXPORT_SYMBOL_GPL(fsl_dma_configure);
+static int __init fsl_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&fsl_soc_platform);
+}
+module_init(fsl_soc_platform_init);
+
+static void __exit fsl_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&fsl_soc_platform);
+}
+module_exit(fsl_soc_platform_exit);
+
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 157a7895ffa1..c6d6eb71dc1d 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -266,7 +266,8 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
* If this is the first stream open, then grab the IRQ and program most of
* the SSI registers.
*/
-static int fsl_ssi_startup(struct snd_pcm_substream *substream)
+static int fsl_ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -411,7 +412,8 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
* Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
* clock master.
*/
-static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
+static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -441,7 +443,8 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
* The DMA channel is in external master start and pause mode, which
* means the SSI completely controls the flow of data.
*/
-static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -490,7 +493,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
*
* Shutdown the SSI if there are no other substreams open.
*/
-static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
+static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -578,8 +582,6 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
.prepare = fsl_ssi_prepare,
.shutdown = fsl_ssi_shutdown,
.trigger = fsl_ssi_trigger,
- },
- .dai_ops = {
.set_sysclk = fsl_ssi_set_sysclk,
.set_fmt = fsl_ssi_set_fmt,
},
@@ -671,6 +673,14 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
fsl_ssi_dai->private_data = ssi_private;
fsl_ssi_dai->name = ssi_private->name;
fsl_ssi_dai->id = ssi_info->id;
+ fsl_ssi_dai->dev = ssi_info->dev;
+
+ ret = snd_soc_register_dai(fsl_ssi_dai);
+ if (ret != 0) {
+ dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret);
+ kfree(fsl_ssi_dai);
+ return NULL;
+ }
return fsl_ssi_dai;
}
@@ -688,6 +698,8 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
+ snd_soc_unregister_dai(&ssi_private->cpu_dai);
+
kfree(ssi_private);
}
EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index 94a02eaa4825..9eb1ce185bd0 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -187,7 +187,8 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
* If this is the first stream open, then grab the IRQ and program most of
* the PSC registers.
*/
-static int psc_i2s_startup(struct snd_pcm_substream *substream)
+static int psc_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -220,7 +221,8 @@ static int psc_i2s_startup(struct snd_pcm_substream *substream)
}
static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -256,7 +258,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
@@ -268,7 +271,8 @@ static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
* This function is called by ALSA to start, stop, pause, and resume the DMA
* transfer of data.
*/
-static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -383,7 +387,8 @@ static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
*
* Shutdown the PSC if there are no other substreams open.
*/
-static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -464,7 +469,6 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
* psc_i2s_dai_template: template CPU Digital Audio Interface
*/
static struct snd_soc_dai psc_i2s_dai_template = {
- .type = SND_SOC_DAI_I2S,
.playback = {
.channels_min = 2,
.channels_max = 2,
@@ -483,8 +487,6 @@ static struct snd_soc_dai psc_i2s_dai_template = {
.hw_free = psc_i2s_hw_free,
.shutdown = psc_i2s_shutdown,
.trigger = psc_i2s_trigger,
- },
- .dai_ops = {
.set_sysclk = psc_i2s_set_sysclk,
.set_fmt = psc_i2s_set_fmt,
},
@@ -826,6 +828,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op,
if (rc)
dev_info(psc_i2s->dev, "error creating sysfs files\n");
+ snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
+
/* Tell the ASoC OF helpers about it */
of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
&psc_i2s->dai);
@@ -839,6 +843,8 @@ static int __devexit psc_i2s_of_remove(struct of_device *op)
dev_dbg(&op->dev, "psc_i2s_remove()\n");
+ snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
+
bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 94f89debde1f..bcec3f60bad9 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -29,7 +29,7 @@
struct mpc8610_hpcd_data {
struct snd_soc_device sound_devdata;
struct snd_soc_dai_link dai;
- struct snd_soc_machine machine;
+ struct snd_soc_card machine;
unsigned int dai_format;
unsigned int codec_clk_direction;
unsigned int cpu_clk_direction;
@@ -185,7 +185,7 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
/**
* mpc8610_hpcd_machine: ASoC machine data
*/
-static struct snd_soc_machine mpc8610_hpcd_machine = {
+static struct snd_soc_card mpc8610_hpcd_machine = {
.probe = mpc8610_hpcd_machine_probe,
.remove = mpc8610_hpcd_machine_remove,
.name = "MPC8610 HPCD",
@@ -465,9 +465,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
goto error;
}
- machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
+ machine_data->sound_devdata.card = &mpc8610_hpcd_machine;
machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
- machine_data->sound_devdata.platform = &fsl_soc_platform;
+ machine_data->machine.platform = &fsl_soc_platform;
sound_device->dev.platform_data = machine_data;
diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c
index 0382fdac51cd..8bc5cd9e972f 100644
--- a/sound/soc/fsl/soc-of-simple.c
+++ b/sound/soc/fsl/soc-of-simple.c
@@ -31,7 +31,7 @@ struct of_snd_soc_device {
int id;
struct list_head list;
struct snd_soc_device device;
- struct snd_soc_machine machine;
+ struct snd_soc_card card;
struct snd_soc_dai_link dai_link;
struct platform_device *pdev;
struct device_node *platform_node;
@@ -58,9 +58,9 @@ of_snd_soc_get_device(struct device_node *codec_node)
/* Initialize the structure and add it to the global list */
of_soc->codec_node = codec_node;
of_soc->id = of_snd_soc_next_index++;
- of_soc->machine.dai_link = &of_soc->dai_link;
- of_soc->machine.num_links = 1;
- of_soc->device.machine = &of_soc->machine;
+ of_soc->card.dai_link = &of_soc->dai_link;
+ of_soc->card.num_links = 1;
+ of_soc->device.card = &of_soc->card;
of_soc->dai_link.ops = &of_snd_soc_ops;
list_add(&of_soc->list, &of_snd_soc_device_list);
@@ -158,8 +158,8 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform,
of_soc->platform_node = node;
of_soc->dai_link.cpu_dai = cpu_dai;
- of_soc->device.platform = platform;
- of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+ of_soc->card.platform = platform;
+ of_soc->card.name = of_soc->dai_link.cpu_dai->name;
/* Now try to register the SoC device */
of_snd_soc_register_device(of_soc);
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 8b7766b998d7..a7b1d77b2105 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -1,6 +1,6 @@
config SND_OMAP_SOC
tristate "SoC Audio for the Texas Instruments OMAP chips"
- depends on ARCH_OMAP && SND_SOC
+ depends on ARCH_OMAP
config SND_OMAP_SOC_MCBSP
tristate
@@ -21,3 +21,36 @@ config SND_OMAP_SOC_OSK5912
select SND_SOC_TLV320AIC23
help
Say Y if you want to add support for SoC audio on osk5912.
+
+config SND_OMAP_SOC_OVERO
+ tristate "SoC Audio support for Gumstix Overo"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on the Gumstix Overo.
+
+config SND_OMAP_SOC_OMAP2EVM
+ tristate "SoC Audio support for OMAP2EVM board"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP2EVM
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on the omap2evm board.
+
+config SND_OMAP_SOC_SDP3430
+ tristate "SoC Audio support for Texas Instruments SDP3430"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on Texas Instruments
+ SDP3430.
+
+config SND_OMAP_SOC_OMAP3_PANDORA
+ tristate "SoC Audio support for OMAP3 Pandora"
+ depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index e09d1f297f64..76fedd96e365 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -8,6 +8,14 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
snd-soc-osk5912-objs := osk5912.o
+snd-soc-overo-objs := overo.o
+snd-soc-omap2evm-objs := omap2evm.o
+snd-soc-sdp3430-objs := sdp3430.o
+snd-soc-omap3pandora-objs := omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
+obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
+obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index fae3ad36e0bf..25593fee9121 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -70,9 +70,13 @@ static void n810_ext_control(struct snd_soc_codec *codec)
static int n810_startup(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
+ snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+
n810_ext_control(codec);
return clk_enable(sys_clkout2);
}
@@ -282,8 +286,9 @@ static struct snd_soc_dai_link n810_dai = {
};
/* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_n810 = {
+static struct snd_soc_card snd_soc_n810 = {
.name = "N810",
+ .platform = &omap_soc_platform,
.dai_link = &n810_dai,
.num_links = 1,
};
@@ -298,8 +303,7 @@ static struct aic3x_setup_data n810_aic33_setup = {
/* Audio subsystem */
static struct snd_soc_device n810_snd_devdata = {
- .machine = &snd_soc_machine_n810,
- .platform = &omap_soc_platform,
+ .card = &snd_soc_n810,
.codec_dev = &soc_codec_dev_aic3x,
.codec_data = &n810_aic33_setup,
};
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 8485a8a9d0ff..ec5e18a78758 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -36,9 +36,7 @@
#include "omap-mcbsp.h"
#include "omap-pcm.h"
-#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000 | \
- SNDRV_PCM_RATE_KNOT)
+#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
struct omap_mcbsp_data {
unsigned int bus_id;
@@ -140,7 +138,8 @@ static const unsigned long omap34xx_mcbsp_port[][2] = {
static const unsigned long omap34xx_mcbsp_port[][2] = {};
#endif
-static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
+static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -153,7 +152,8 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
return err;
}
-static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
+static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -165,7 +165,8 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
}
}
-static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -194,14 +195,15 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
}
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
- int wlen;
+ int wlen, channels;
unsigned long port;
if (cpu_class_is_omap1()) {
@@ -230,12 +232,17 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
- switch (params_channels(params)) {
+ channels = params_channels(params);
+ switch (channels) {
case 2:
- /* Set 1 word per (McBPSP) frame and use dual-phase frames */
- regs->rcr2 |= RFRLEN2(1 - 1) | RPHASE;
+ /* Use dual-phase frames */
+ regs->rcr2 |= RPHASE;
+ regs->xcr2 |= XPHASE;
+ case 1:
+ /* Set 1 word per (McBSP) frame */
+ regs->rcr2 |= RFRLEN2(1 - 1);
regs->rcr1 |= RFRLEN1(1 - 1);
- regs->xcr2 |= XFRLEN2(1 - 1) | XPHASE;
+ regs->xcr2 |= XFRLEN2(1 - 1);
regs->xcr1 |= XFRLEN1(1 - 1);
break;
default:
@@ -263,9 +270,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
regs->srgr2 |= FPER(wlen * 2 - 1);
regs->srgr1 |= FWID(wlen - 1);
break;
- case SND_SOC_DAIFMT_DSP_A:
- regs->srgr2 |= FPER(wlen * 2 - 1);
- regs->srgr1 |= FWID(wlen * 2 - 2);
+ case SND_SOC_DAIFMT_DSP_B:
+ regs->srgr2 |= FPER(wlen * channels - 1);
+ regs->srgr1 |= FWID(wlen * channels - 2);
break;
}
@@ -302,7 +309,7 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
regs->rcr2 |= RDATDLY(1);
regs->xcr2 |= XDATDLY(1);
break;
- case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
/* 0-bit data delay */
regs->rcr2 |= RDATDLY(0);
regs->xcr2 |= XDATDLY(0);
@@ -452,17 +459,16 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
#define OMAP_MCBSP_DAI_BUILDER(link_id) \
{ \
- .name = "omap-mcbsp-dai-(link_id)", \
+ .name = "omap-mcbsp-dai-"#link_id, \
.id = (link_id), \
- .type = SND_SOC_DAI_I2S, \
.playback = { \
- .channels_min = 2, \
+ .channels_min = 1, \
.channels_max = 2, \
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.capture = { \
- .channels_min = 2, \
+ .channels_min = 1, \
.channels_max = 2, \
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
@@ -472,8 +478,6 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
.shutdown = omap_mcbsp_dai_shutdown, \
.trigger = omap_mcbsp_dai_trigger, \
.hw_params = omap_mcbsp_dai_hw_params, \
- }, \
- .dai_ops = { \
.set_fmt = omap_mcbsp_dai_set_dai_fmt, \
.set_clkdiv = omap_mcbsp_dai_set_clkdiv, \
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \
@@ -495,6 +499,19 @@ struct snd_soc_dai omap_mcbsp_dai[] = {
EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
+static int __init snd_omap_mcbsp_init(void)
+{
+ return snd_soc_register_dais(omap_mcbsp_dai,
+ ARRAY_SIZE(omap_mcbsp_dai));
+}
+module_init(snd_omap_mcbsp_init);
+
+static void __exit snd_omap_mcbsp_exit(void)
+{
+ snd_soc_unregister_dais(omap_mcbsp_dai, ARRAY_SIZE(omap_mcbsp_dai));
+}
+module_exit(snd_omap_mcbsp_exit);
+
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index e9084fdd2082..b0362dfd5b71 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
prtd->dma_data = dma_data;
err = omap_request_dma(dma_data->dma_req, dma_data->name,
omap_pcm_dma_irq, substream, &prtd->dma_ch);
- if (!err & !cpu_is_omap1510()) {
+ if (!err && !cpu_is_omap1510()) {
/*
* Link channel with itself so DMA doesn't need any
* reprogramming while looping the buffer
@@ -233,7 +233,7 @@ static int omap_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto out;
- prtd = kzalloc(sizeof(prtd), GFP_KERNEL);
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
if (prtd == NULL) {
ret = -ENOMEM;
goto out;
@@ -354,6 +354,18 @@ struct snd_soc_platform omap_soc_platform = {
};
EXPORT_SYMBOL_GPL(omap_soc_platform);
+static int __init omap_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&omap_soc_platform);
+}
+module_init(omap_soc_platform_init);
+
+static void __exit omap_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&omap_soc_platform);
+}
+module_exit(omap_soc_platform_exit);
+
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
MODULE_DESCRIPTION("OMAP PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
new file mode 100644
index 000000000000..0c2322dcf02a
--- /dev/null
+++ b/sound/soc/omap/omap2evm.c
@@ -0,0 +1,151 @@
+/*
+ * omap2evm.c -- SoC audio machine driver for omap2evm board
+ *
+ * Author: Arun KS <arunks@mistralsolutions.com>
+ *
+ * Based on sound/soc/omap/overo.c by Steve Sakoman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap2evm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops omap2evm_ops = {
+ .hw_params = omap2evm_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap2evm_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai,
+ .ops = &omap2evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap2evm = {
+ .name = "omap2evm",
+ .platform = &omap_soc_platform,
+ .dai_link = &omap2evm_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap2evm_snd_devdata = {
+ .card = &snd_soc_omap2evm,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap2evm_snd_device;
+
+static int __init omap2evm_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap2evm()) {
+ pr_debug("Not omap2evm!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "omap2evm SoC init\n");
+
+ omap2evm_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!omap2evm_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap2evm_snd_device, &omap2evm_snd_devdata);
+ omap2evm_snd_devdata.dev = &omap2evm_snd_device->dev;
+ *(unsigned int *)omap2evm_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+ ret = platform_device_add(omap2evm_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(omap2evm_snd_device);
+
+ return ret;
+}
+module_init(omap2evm_soc_init);
+
+static void __exit omap2evm_soc_exit(void)
+{
+ platform_device_unregister(omap2evm_snd_device);
+}
+module_exit(omap2evm_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC omap2evm");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
new file mode 100644
index 000000000000..fd24a4acd2f5
--- /dev/null
+++ b/sound/soc/omap/omap3beagle.c
@@ -0,0 +1,149 @@
+/*
+ * omap3beagle.c -- SoC audio for OMAP3 Beagle
+ *
+ * Author: Steve Sakoman <steve@sakoman.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap3beagle_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops omap3beagle_ops = {
+ .hw_params = omap3beagle_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3beagle_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai,
+ .ops = &omap3beagle_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3beagle = {
+ .name = "omap3beagle",
+ .platform = &omap_soc_platform,
+ .dai_link = &omap3beagle_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3beagle_snd_devdata = {
+ .card = &snd_soc_omap3beagle,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3beagle_snd_device;
+
+static int __init omap3beagle_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap3_beagle()) {
+ pr_debug("Not OMAP3 Beagle!\n");
+ return -ENODEV;
+ }
+ pr_info("OMAP3 Beagle SoC init\n");
+
+ omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!omap3beagle_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata);
+ omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev;
+ *(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+ ret = platform_device_add(omap3beagle_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(omap3beagle_snd_device);
+
+ return ret;
+}
+
+static void __exit omap3beagle_soc_exit(void)
+{
+ platform_device_unregister(omap3beagle_snd_device);
+}
+
+module_init(omap3beagle_soc_init);
+module_exit(omap3beagle_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
new file mode 100644
index 000000000000..bd91594496b1
--- /dev/null
+++ b/sound/soc/omap/omap3pandora.c
@@ -0,0 +1,311 @@
+/*
+ * omap3pandora.c -- SoC audio for Pandora Handheld Console
+ *
+ * Author: Gražvydas Ignotas <notasas@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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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/clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+#define OMAP3_PANDORA_DAC_POWER_GPIO 118
+#define OMAP3_PANDORA_AMP_POWER_GPIO 14
+
+#define PREFIX "ASoC omap3pandora: "
+
+static int omap3pandora_cmn_hw_params(struct snd_soc_dai *codec_dai,
+ struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set codec system clock\n");
+ return ret;
+ }
+
+ /* Set McBSP clock to external */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set cpu system clock\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set SRG clock divider\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int omap3pandora_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int omap3pandora_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ return omap3pandora_cmn_hw_params(codec_dai, cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+}
+
+static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
+ } else {
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+ mdelay(1);
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Audio paths on Pandora board:
+ *
+ * |O| ---> PCM DAC +-> AMP -> Headphone Jack
+ * |M| A +--------> Line Out
+ * |A| <~~clk~~+
+ * |P| <--- TWL4030 <--------- Line In and MICs
+ */
+static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("PCM DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
+ 0, 0, NULL, 0, omap3pandora_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+};
+
+static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
+ SND_SOC_DAPM_MIC("Mic (external)", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route omap3pandora_out_map[] = {
+ {"Headphone Amplifier", NULL, "PCM DAC"},
+ {"Line Out", NULL, "PCM DAC"},
+ {"Headphone Jack", NULL, "Headphone Amplifier"},
+};
+
+static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
+ {"INL", NULL, "Line In"},
+ {"INR", NULL, "Line In"},
+ {"INL", NULL, "Mic (Internal)"},
+ {"INR", NULL, "Mic (external)"},
+};
+
+static int omap3pandora_out_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets,
+ ARRAY_SIZE(omap3pandora_out_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dapm_add_routes(codec, omap3pandora_out_map,
+ ARRAY_SIZE(omap3pandora_out_map));
+
+ return snd_soc_dapm_sync(codec);
+}
+
+static int omap3pandora_in_init(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets,
+ ARRAY_SIZE(omap3pandora_in_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dapm_add_routes(codec, omap3pandora_in_map,
+ ARRAY_SIZE(omap3pandora_in_map));
+
+ return snd_soc_dapm_sync(codec);
+}
+
+static struct snd_soc_ops omap3pandora_out_ops = {
+ .hw_params = omap3pandora_out_hw_params,
+};
+
+static struct snd_soc_ops omap3pandora_in_ops = {
+ .hw_params = omap3pandora_in_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3pandora_dai[] = {
+ {
+ .name = "PCM1773",
+ .stream_name = "HiFi Out",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai,
+ .ops = &omap3pandora_out_ops,
+ .init = omap3pandora_out_init,
+ }, {
+ .name = "TWL4030",
+ .stream_name = "Line/Mic In",
+ .cpu_dai = &omap_mcbsp_dai[1],
+ .codec_dai = &twl4030_dai,
+ .ops = &omap3pandora_in_ops,
+ .init = omap3pandora_in_init,
+ }
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_omap3pandora = {
+ .name = "omap3pandora",
+ .platform = &omap_soc_platform,
+ .dai_link = omap3pandora_dai,
+ .num_links = ARRAY_SIZE(omap3pandora_dai),
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3pandora_snd_data = {
+ .card = &snd_soc_card_omap3pandora,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3pandora_snd_device;
+
+static int __init omap3pandora_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap3_pandora()) {
+ pr_debug(PREFIX "Not OMAP3 Pandora\n");
+ return -ENODEV;
+ }
+ pr_info("OMAP3 Pandora SoC init\n");
+
+ ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
+ if (ret) {
+ pr_err(PREFIX "Failed to get DAC power GPIO\n");
+ return ret;
+ }
+
+ ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+ if (ret) {
+ pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
+ goto fail0;
+ }
+
+ ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
+ if (ret) {
+ pr_err(PREFIX "Failed to get amp power GPIO\n");
+ goto fail0;
+ }
+
+ ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+ if (ret) {
+ pr_err(PREFIX "Failed to set amp power GPIO direction\n");
+ goto fail1;
+ }
+
+ omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
+ if (omap3pandora_snd_device == NULL) {
+ pr_err(PREFIX "Platform device allocation failed\n");
+ ret = -ENOMEM;
+ goto fail1;
+ }
+
+ platform_set_drvdata(omap3pandora_snd_device, &omap3pandora_snd_data);
+ omap3pandora_snd_data.dev = &omap3pandora_snd_device->dev;
+ *(unsigned int *)omap_mcbsp_dai[0].private_data = 1; /* McBSP2 */
+ *(unsigned int *)omap_mcbsp_dai[1].private_data = 3; /* McBSP4 */
+
+ ret = platform_device_add(omap3pandora_snd_device);
+ if (ret) {
+ pr_err(PREFIX "Unable to add platform device\n");
+ goto fail2;
+ }
+
+ return 0;
+
+fail2:
+ platform_device_put(omap3pandora_snd_device);
+fail1:
+ gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+fail0:
+ gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+ return ret;
+}
+module_init(omap3pandora_soc_init);
+
+static void __exit omap3pandora_soc_exit(void)
+{
+ platform_device_unregister(omap3pandora_snd_device);
+ gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+ gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+}
+module_exit(omap3pandora_soc_exit);
+
+MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
index 0fe733796898..cd41a948df7b 100644
--- a/sound/soc/omap/osk5912.c
+++ b/sound/soc/omap/osk5912.c
@@ -61,7 +61,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
/* Set codec DAI configuration */
err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBM_CFM);
if (err < 0) {
@@ -71,7 +71,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
/* Set cpu DAI configuration */
err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_DSP_B |
SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_CBM_CFM);
if (err < 0) {
@@ -143,16 +143,16 @@ static struct snd_soc_dai_link osk_dai = {
};
/* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_osk = {
+static struct snd_soc_card snd_soc_card_osk = {
.name = "OSK5912",
+ .platform = &omap_soc_platform,
.dai_link = &osk_dai,
.num_links = 1,
};
/* Audio subsystem */
static struct snd_soc_device osk_snd_devdata = {
- .machine = &snd_soc_machine_osk,
- .platform = &omap_soc_platform,
+ .card = &snd_soc_card_osk,
.codec_dev = &soc_codec_dev_tlv320aic23,
};
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
new file mode 100644
index 000000000000..a72dc4e159e5
--- /dev/null
+++ b/sound/soc/omap/overo.c
@@ -0,0 +1,148 @@
+/*
+ * overo.c -- SoC audio for Gumstix Overo
+ *
+ * Author: Steve Sakoman <steve@sakoman.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int overo_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops overo_ops = {
+ .hw_params = overo_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link overo_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai,
+ .ops = &overo_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_overo = {
+ .name = "overo",
+ .platform = &omap_soc_platform,
+ .dai_link = &overo_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device overo_snd_devdata = {
+ .card = &snd_soc_card_overo,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *overo_snd_device;
+
+static int __init overo_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_overo()) {
+ pr_debug("Not Overo!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "overo SoC init\n");
+
+ overo_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!overo_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(overo_snd_device, &overo_snd_devdata);
+ overo_snd_devdata.dev = &overo_snd_device->dev;
+ *(unsigned int *)overo_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+ ret = platform_device_add(overo_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(overo_snd_device);
+
+ return ret;
+}
+module_init(overo_soc_init);
+
+static void __exit overo_soc_exit(void)
+{
+ platform_device_unregister(overo_snd_device);
+}
+module_exit(overo_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC overo");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
new file mode 100644
index 000000000000..ad97836818b1
--- /dev/null
+++ b/sound/soc/omap/sdp3430.c
@@ -0,0 +1,152 @@
+/*
+ * sdp3430.c -- SoC audio for TI OMAP3430 SDP
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * Based on:
+ * Author: Steve Sakoman <steve@sakoman.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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int sdp3430_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret;
+
+ /* Set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return ret;
+ }
+
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return ret;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops sdp3430_ops = {
+ .hw_params = sdp3430_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sdp3430_dai = {
+ .name = "TWL4030",
+ .stream_name = "TWL4030",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &twl4030_dai,
+ .ops = &sdp3430_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_machine snd_soc_machine_sdp3430 = {
+ .name = "SDP3430",
+ .platform = &omap_soc_platform,
+ .dai_link = &sdp3430_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device sdp3430_snd_devdata = {
+ .machine = &snd_soc_machine_sdp3430,
+ .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *sdp3430_snd_device;
+
+static int __init sdp3430_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap_3430sdp()) {
+ pr_debug("Not SDP3430!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "SDP3430 SoC init\n");
+
+ sdp3430_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!sdp3430_snd_device) {
+ printk(KERN_ERR "Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata);
+ sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev;
+ *(unsigned int *)sdp3430_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+ ret = platform_device_add(sdp3430_snd_device);
+ if (ret)
+ goto err1;
+
+ return 0;
+
+err1:
+ printk(KERN_ERR "Unable to add platform device\n");
+ platform_device_put(sdp3430_snd_device);
+
+ return ret;
+}
+module_init(sdp3430_soc_init);
+
+static void __exit sdp3430_soc_exit(void)
+{
+ platform_device_unregister(sdp3430_snd_device);
+}
+module_exit(sdp3430_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC SDP3430");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index f8c1cdd940ac..f82e10699471 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -21,6 +21,9 @@ config SND_PXA2XX_SOC_AC97
config SND_PXA2XX_SOC_I2S
tristate
+config SND_PXA_SOC_SSP
+ tristate
+
config SND_PXA2XX_SOC_CORGI
tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
@@ -75,3 +78,22 @@ config SND_PXA2XX_SOC_EM_X270
help
Say Y if you want to add support for SoC audio on
CompuLab EM-x270.
+
+config SND_PXA2XX_SOC_PALM27X
+ bool "SoC Audio support for Palm T|X, T5 and LifeDrive"
+ depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5)
+ select SND_PXA2XX_SOC_AC97
+ select SND_SOC_WM9712
+ help
+ Say Y if you want to add support for SoC audio on
+ Palm T|X, T5 or LifeDrive handheld computer.
+
+config SND_SOC_ZYLONITE
+ tristate "SoC Audio support for Marvell Zylonite"
+ depends on SND_PXA2XX_SOC && MACH_ZYLONITE
+ select SND_PXA2XX_SOC_AC97
+ select SND_PXA_SOC_SSP
+ select SND_SOC_WM9713
+ help
+ Say Y if you want to add support for SoC audio on the
+ Marvell Zylonite reference platform.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 5bc8edf9dca9..08a9f2797729 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -2,10 +2,12 @@
snd-soc-pxa2xx-objs := pxa2xx-pcm.o
snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
+snd-soc-pxa-ssp-objs := pxa-ssp.o
obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
# PXA Machine Support
snd-soc-corgi-objs := corgi.o
@@ -14,6 +16,8 @@ snd-soc-tosa-objs := tosa.o
snd-soc-e800-objs := e800_wm9712.o
snd-soc-spitz-objs := spitz.o
snd-soc-em-x270-objs := em-x270.o
+snd-soc-palm27x-objs := palm27x.o
+snd-soc-zylonite-objs := zylonite.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -21,3 +25,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
+obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 2718eaf7895f..1ba25a559524 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -108,15 +108,11 @@ static int corgi_startup(struct snd_pcm_substream *substream)
}
/* we need to unmute the HP at shutdown as the mute burns power on corgi */
-static int corgi_shutdown(struct snd_pcm_substream *substream)
+static void corgi_shutdown(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->socdev->codec;
-
/* set = unmute headphone */
gpio_set_value(CORGI_GPIO_MUTE_L, 1);
gpio_set_value(CORGI_GPIO_MUTE_R, 1);
- return 0;
}
static int corgi_hw_params(struct snd_pcm_substream *substream,
@@ -314,8 +310,9 @@ static struct snd_soc_dai_link corgi_dai = {
};
/* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
.name = "Corgi",
+ .platform = &pxa2xx_soc_platform,
.dai_link = &corgi_dai,
.num_links = 1,
};
@@ -328,8 +325,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
/* corgi audio subsystem */
static struct snd_soc_device corgi_snd_devdata = {
- .machine = &snd_soc_machine_corgi,
- .platform = &pxa2xx_soc_platform,
+ .card = &snd_soc_corgi,
.codec_dev = &soc_codec_dev_wm8731,
.codec_data = &corgi_wm8731_setup,
};
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 6781c5be242f..2e3386dfa0f0 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -29,7 +29,7 @@
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
-static struct snd_soc_machine e800;
+static struct snd_soc_card e800;
static struct snd_soc_dai_link e800_dai[] = {
{
@@ -40,15 +40,15 @@ static struct snd_soc_dai_link e800_dai[] = {
},
};
-static struct snd_soc_machine e800 = {
+static struct snd_soc_card e800 = {
.name = "Toshiba e800",
+ .platform = &pxa2xx_soc_platform,
.dai_link = e800_dai,
.num_links = ARRAY_SIZE(e800_dai),
};
static struct snd_soc_device e800_snd_devdata = {
- .machine = &e800,
- .platform = &pxa2xx_soc_platform,
+ .card = &e800,
.codec_dev = &soc_codec_dev_wm9712,
};
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index e6ff6929ab4b..fe4a729ea648 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -23,7 +23,6 @@
#include <linux/moduleparam.h>
#include <linux/device.h>
-#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@@ -53,15 +52,15 @@ static struct snd_soc_dai_link em_x270_dai[] = {
},
};
-static struct snd_soc_machine em_x270 = {
+static struct snd_soc_card em_x270 = {
.name = "EM-X270",
+ .platform = &pxa2xx_soc_platform,
.dai_link = em_x270_dai,
.num_links = ARRAY_SIZE(em_x270_dai),
};
static struct snd_soc_device em_x270_snd_devdata = {
- .machine = &em_x270,
- .platform = &pxa2xx_soc_platform,
+ .card = &em_x270,
.codec_dev = &soc_codec_dev_wm9712,
};
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
new file mode 100644
index 000000000000..4a9cf3083af0
--- /dev/null
+++ b/sound/soc/pxa/palm27x.c
@@ -0,0 +1,269 @@
+/*
+ * linux/sound/soc/pxa/palm27x.c
+ *
+ * SoC Audio driver for Palm T|X, T5 and LifeDrive
+ *
+ * based on tosa.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+#include <mach/palmasoc.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static int palm27x_jack_func = 1;
+static int palm27x_spk_func = 1;
+static int palm27x_ep_gpio = -1;
+
+static void palm27x_ext_control(struct snd_soc_codec *codec)
+{
+ if (!palm27x_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Speaker");
+ else
+ snd_soc_dapm_disable_pin(codec, "Speaker");
+
+ if (!palm27x_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+}
+
+static int palm27x_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+
+ /* check the jack status at stream startup */
+ palm27x_ext_control(codec);
+ return 0;
+}
+
+static struct snd_soc_ops palm27x_ops = {
+ .startup = palm27x_startup,
+};
+
+static irqreturn_t palm27x_interrupt(int irq, void *v)
+{
+ palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
+ palm27x_jack_func = !palm27x_spk_func;
+ return IRQ_HANDLED;
+}
+
+static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = palm27x_jack_func;
+ return 0;
+}
+
+static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (palm27x_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ palm27x_jack_func = ucontrol->value.integer.value[0];
+ palm27x_ext_control(codec);
+ return 1;
+}
+
+static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = palm27x_spk_func;
+ return 0;
+}
+
+static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (palm27x_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ palm27x_spk_func = ucontrol->value.integer.value[0];
+ palm27x_ext_control(codec);
+ return 1;
+}
+
+/* PalmTX machine dapm widgets */
+static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* PalmTX audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* headphone connected to HPOUTL, HPOUTR */
+ {"Headphone Jack", NULL, "HPOUTL"},
+ {"Headphone Jack", NULL, "HPOUTR"},
+
+ /* ext speaker connected to ROUT2, LOUT2 */
+ {"Speaker", NULL, "LOUT2"},
+ {"Speaker", NULL, "ROUT2"},
+};
+
+static const char *jack_function[] = {"Headphone", "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum palm27x_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new palm27x_controls[] = {
+ SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
+ palm27x_set_jack),
+ SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
+ palm27x_set_spk),
+};
+
+static int palm27x_ac97_init(struct snd_soc_codec *codec)
+{
+ int i, err;
+
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONOOUT");
+
+ /* add palm27x specific controls */
+ for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&palm27x_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ /* add palm27x specific widgets */
+ snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
+ ARRAY_SIZE(palm27x_dapm_widgets));
+
+ /* set up palm27x specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link palm27x_dai[] = {
+{
+ .name = "AC97 HiFi",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+ .init = palm27x_ac97_init,
+ .ops = &palm27x_ops,
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+ .ops = &palm27x_ops,
+},
+};
+
+static struct snd_soc_card palm27x_asoc = {
+ .name = "Palm/PXA27x",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = palm27x_dai,
+ .num_links = ARRAY_SIZE(palm27x_dai),
+};
+
+static struct snd_soc_device palm27x_snd_devdata = {
+ .card = &palm27x_asoc,
+ .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *palm27x_snd_device;
+
+static int __init palm27x_asoc_init(void)
+{
+ int ret;
+
+ if (!(machine_is_palmtx() || machine_is_palmt5() ||
+ machine_is_palmld()))
+ return -ENODEV;
+
+ ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
+ if (ret)
+ return ret;
+ ret = gpio_direction_input(palm27x_ep_gpio);
+ if (ret)
+ goto err_alloc;
+
+ if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Headphone jack", NULL))
+ goto err_alloc;
+
+ palm27x_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!palm27x_snd_device) {
+ ret = -ENOMEM;
+ goto err_dev;
+ }
+
+ platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
+ palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
+ ret = platform_device_add(palm27x_snd_device);
+
+ if (ret != 0)
+ goto put_device;
+
+ return 0;
+
+put_device:
+ platform_device_put(palm27x_snd_device);
+err_dev:
+ free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+err_alloc:
+ gpio_free(palm27x_ep_gpio);
+
+ return ret;
+}
+
+static void __exit palm27x_asoc_exit(void)
+{
+ free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+ gpio_free(palm27x_ep_gpio);
+ platform_device_unregister(palm27x_snd_device);
+}
+
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data)
+{
+ palm27x_ep_gpio = data->jack_gpio;
+}
+
+module_init(palm27x_asoc_init);
+module_exit(palm27x_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index 4d9930c52789..6e9827189fff 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -276,8 +276,9 @@ static struct snd_soc_dai_link poodle_dai = {
};
/* poodle audio machine driver */
-static struct snd_soc_machine snd_soc_machine_poodle = {
+static struct snd_soc_card snd_soc_poodle = {
.name = "Poodle",
+ .platform = &pxa2xx_soc_platform,
.dai_link = &poodle_dai,
.num_links = 1,
};
@@ -290,8 +291,7 @@ static struct wm8731_setup_data poodle_wm8731_setup = {
/* poodle audio subsystem */
static struct snd_soc_device poodle_snd_devdata = {
- .machine = &snd_soc_machine_poodle,
- .platform = &pxa2xx_soc_platform,
+ .card = &snd_soc_poodle,
.codec_dev = &soc_codec_dev_wm8731,
.codec_data = &poodle_wm8731_setup,
};
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
new file mode 100644
index 000000000000..73cb6b4c2f2d
--- /dev/null
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -0,0 +1,931 @@
+#define DEBUG
+/*
+ * pxa-ssp.c -- ALSA Soc Audio Layer
+ *
+ * Copyright 2005,2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * TODO:
+ * o Test network mode for > 16bit sample size
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/regs-ssp.h>
+#include <mach/audio.h>
+#include <mach/ssp.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa-ssp.h"
+
+/*
+ * SSP audio private data
+ */
+struct ssp_priv {
+ struct ssp_dev dev;
+ unsigned int sysclk;
+ int dai_fmt;
+#ifdef CONFIG_PM
+ struct ssp_state state;
+#endif
+};
+
+#define PXA2xx_SSP1_BASE 0x41000000
+#define PXA27x_SSP2_BASE 0x41700000
+#define PXA27x_SSP3_BASE 0x41900000
+#define PXA3xx_SSP4_BASE 0x41a00000
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = {
+ .name = "SSP1 PCM Mono out",
+ .dev_addr = PXA2xx_SSP1_BASE + SSDR,
+ .drcmr = &DRCMR(14),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = {
+ .name = "SSP1 PCM Mono in",
+ .dev_addr = PXA2xx_SSP1_BASE + SSDR,
+ .drcmr = &DRCMR(13),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = {
+ .name = "SSP1 PCM Stereo out",
+ .dev_addr = PXA2xx_SSP1_BASE + SSDR,
+ .drcmr = &DRCMR(14),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = {
+ .name = "SSP1 PCM Stereo in",
+ .dev_addr = PXA2xx_SSP1_BASE + SSDR,
+ .drcmr = &DRCMR(13),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = {
+ .name = "SSP2 PCM Mono out",
+ .dev_addr = PXA27x_SSP2_BASE + SSDR,
+ .drcmr = &DRCMR(16),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = {
+ .name = "SSP2 PCM Mono in",
+ .dev_addr = PXA27x_SSP2_BASE + SSDR,
+ .drcmr = &DRCMR(15),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = {
+ .name = "SSP2 PCM Stereo out",
+ .dev_addr = PXA27x_SSP2_BASE + SSDR,
+ .drcmr = &DRCMR(16),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = {
+ .name = "SSP2 PCM Stereo in",
+ .dev_addr = PXA27x_SSP2_BASE + SSDR,
+ .drcmr = &DRCMR(15),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = {
+ .name = "SSP3 PCM Mono out",
+ .dev_addr = PXA27x_SSP3_BASE + SSDR,
+ .drcmr = &DRCMR(67),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = {
+ .name = "SSP3 PCM Mono in",
+ .dev_addr = PXA27x_SSP3_BASE + SSDR,
+ .drcmr = &DRCMR(66),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = {
+ .name = "SSP3 PCM Stereo out",
+ .dev_addr = PXA27x_SSP3_BASE + SSDR,
+ .drcmr = &DRCMR(67),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = {
+ .name = "SSP3 PCM Stereo in",
+ .dev_addr = PXA27x_SSP3_BASE + SSDR,
+ .drcmr = &DRCMR(66),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = {
+ .name = "SSP4 PCM Mono out",
+ .dev_addr = PXA3xx_SSP4_BASE + SSDR,
+ .drcmr = &DRCMR(67),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = {
+ .name = "SSP4 PCM Mono in",
+ .dev_addr = PXA3xx_SSP4_BASE + SSDR,
+ .drcmr = &DRCMR(66),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = {
+ .name = "SSP4 PCM Stereo out",
+ .dev_addr = PXA3xx_SSP4_BASE + SSDR,
+ .drcmr = &DRCMR(67),
+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = {
+ .name = "SSP4 PCM Stereo in",
+ .dev_addr = PXA3xx_SSP4_BASE + SSDR,
+ .drcmr = &DRCMR(66),
+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+ DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static void dump_registers(struct ssp_device *ssp)
+{
+ dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
+ ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1),
+ ssp_read_reg(ssp, SSTO));
+
+ dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
+ ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR),
+ ssp_read_reg(ssp, SSACD));
+}
+
+static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = {
+ {
+ &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in,
+ &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in,
+ },
+ {
+ &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in,
+ &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in,
+ },
+ {
+ &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in,
+ &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in,
+ },
+ {
+ &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in,
+ &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in,
+ },
+};
+
+static int pxa_ssp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct ssp_priv *priv = cpu_dai->private_data;
+ int ret = 0;
+
+ if (!cpu_dai->active) {
+ ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ);
+ if (ret < 0)
+ return ret;
+ ssp_disable(&priv->dev);
+ }
+ return ret;
+}
+
+static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct ssp_priv *priv = cpu_dai->private_data;
+
+ if (!cpu_dai->active) {
+ ssp_disable(&priv->dev);
+ ssp_exit(&priv->dev);
+ }
+}
+
+#ifdef CONFIG_PM
+
+static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ ssp_save_state(&priv->dev, &priv->state);
+ clk_disable(priv->dev.ssp->clk);
+ return 0;
+}
+
+static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+
+ if (!cpu_dai->active)
+ return 0;
+
+ clk_enable(priv->dev.ssp->clk);
+ ssp_restore_state(&priv->dev, &priv->state);
+ ssp_enable(&priv->dev);
+
+ return 0;
+}
+
+#else
+#define pxa_ssp_suspend NULL
+#define pxa_ssp_resume NULL
+#endif
+
+/**
+ * ssp_set_clkdiv - set SSP clock divider
+ * @div: serial clock rate divider
+ */
+static void ssp_set_scr(struct ssp_dev *dev, u32 div)
+{
+ struct ssp_device *ssp = dev->ssp;
+ u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR;
+
+ ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div)));
+}
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ int val;
+
+ u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
+ ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+
+ dev_dbg(&ssp->pdev->dev,
+ "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
+ cpu_dai->id, clk_id, freq);
+
+ switch (clk_id) {
+ case PXA_SSP_CLK_NET_PLL:
+ sscr0 |= SSCR0_MOD;
+ break;
+ case PXA_SSP_CLK_PLL:
+ /* Internal PLL is fixed */
+ if (cpu_is_pxa25x())
+ priv->sysclk = 1843200;
+ else
+ priv->sysclk = 13000000;
+ break;
+ case PXA_SSP_CLK_EXT:
+ priv->sysclk = freq;
+ sscr0 |= SSCR0_ECS;
+ break;
+ case PXA_SSP_CLK_NET:
+ priv->sysclk = freq;
+ sscr0 |= SSCR0_NCS | SSCR0_MOD;
+ break;
+ case PXA_SSP_CLK_AUDIO:
+ priv->sysclk = 0;
+ ssp_set_scr(&priv->dev, 1);
+ sscr0 |= SSCR0_ADC;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* The SSP clock must be disabled when changing SSP clock mode
+ * on PXA2xx. On PXA3xx it must be enabled when doing so. */
+ if (!cpu_is_pxa3xx())
+ clk_disable(priv->dev.ssp->clk);
+ val = ssp_read_reg(ssp, SSCR0) | sscr0;
+ ssp_write_reg(ssp, SSCR0, val);
+ if (!cpu_is_pxa3xx())
+ clk_enable(priv->dev.ssp->clk);
+
+ return 0;
+}
+
+/*
+ * Set the SSP clock dividers.
+ */
+static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ int val;
+
+ switch (div_id) {
+ case PXA_SSP_AUDIO_DIV_ACDS:
+ val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div);
+ ssp_write_reg(ssp, SSACD, val);
+ break;
+ case PXA_SSP_AUDIO_DIV_SCDB:
+ val = ssp_read_reg(ssp, SSACD);
+ val &= ~SSACD_SCDB;
+#if defined(CONFIG_PXA3xx)
+ if (cpu_is_pxa3xx())
+ val &= ~SSACD_SCDX8;
+#endif
+ switch (div) {
+ case PXA_SSP_CLK_SCDB_1:
+ val |= SSACD_SCDB;
+ break;
+ case PXA_SSP_CLK_SCDB_4:
+ break;
+#if defined(CONFIG_PXA3xx)
+ case PXA_SSP_CLK_SCDB_8:
+ if (cpu_is_pxa3xx())
+ val |= SSACD_SCDX8;
+ else
+ return -EINVAL;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+ ssp_write_reg(ssp, SSACD, val);
+ break;
+ case PXA_SSP_DIV_SCR:
+ ssp_set_scr(&priv->dev, div);
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
+ */
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
+ int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70;
+
+#if defined(CONFIG_PXA3xx)
+ if (cpu_is_pxa3xx())
+ ssp_write_reg(ssp, SSACDD, 0);
+#endif
+
+ switch (freq_out) {
+ case 5622000:
+ break;
+ case 11345000:
+ ssacd |= (0x1 << 4);
+ break;
+ case 12235000:
+ ssacd |= (0x2 << 4);
+ break;
+ case 14857000:
+ ssacd |= (0x3 << 4);
+ break;
+ case 32842000:
+ ssacd |= (0x4 << 4);
+ break;
+ case 48000000:
+ ssacd |= (0x5 << 4);
+ break;
+ case 0:
+ /* Disable */
+ break;
+
+ default:
+#ifdef CONFIG_PXA3xx
+ /* PXA3xx has a clock ditherer which can be used to generate
+ * a wider range of frequencies - calculate a value for it.
+ */
+ if (cpu_is_pxa3xx()) {
+ u32 val;
+ u64 tmp = 19968;
+ tmp *= 1000000;
+ do_div(tmp, freq_out);
+ val = tmp;
+
+ val = (val << 16) | 64;;
+ ssp_write_reg(ssp, SSACDD, val);
+
+ ssacd |= (0x6 << 4);
+
+ dev_dbg(&ssp->pdev->dev,
+ "Using SSACDD %x to supply %dHz\n",
+ val, freq_out);
+ break;
+ }
+#endif
+
+ return -EINVAL;
+ }
+
+ ssp_write_reg(ssp, SSACD, ssacd);
+
+ return 0;
+}
+
+/*
+ * Set the active slots in TDM/Network mode
+ */
+static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int mask, int slots)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ u32 sscr0;
+
+ sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
+
+ /* set number of active slots */
+ sscr0 |= SSCR0_SlotsPerFrm(slots);
+ ssp_write_reg(ssp, SSCR0, sscr0);
+
+ /* set active slot mask */
+ ssp_write_reg(ssp, SSTSA, mask);
+ ssp_write_reg(ssp, SSRSA, mask);
+ return 0;
+}
+
+/*
+ * Tristate the SSP DAI lines
+ */
+static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+ int tristate)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ u32 sscr1;
+
+ sscr1 = ssp_read_reg(ssp, SSCR1);
+ if (tristate)
+ sscr1 &= ~SSCR1_TTE;
+ else
+ sscr1 |= SSCR1_TTE;
+ ssp_write_reg(ssp, SSCR1, sscr1);
+
+ return 0;
+}
+
+/*
+ * Set up the SSP DAI format.
+ * The SSP Port must be inactive before calling this function as the
+ * physical interface format is changed.
+ */
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ u32 sscr0;
+ u32 sscr1;
+ u32 sspsp;
+
+ /* reset port settings */
+ sscr0 = ssp_read_reg(ssp, SSCR0) &
+ (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+ sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
+ sspsp = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ sscr1 |= SSCR1_SCLKDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ssp_write_reg(ssp, SSCR0, sscr0);
+ ssp_write_reg(ssp, SSCR1, sscr1);
+ ssp_write_reg(ssp, SSPSP, sspsp);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sscr0 |= SSCR0_MOD | SSCR0_PSP;
+ sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sspsp |= SSPSP_FSRT;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sspsp |= SSPSP_SFRMP | SSPSP_FSRT;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sspsp |= SSPSP_SFRMP;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ sspsp |= SSPSP_FSRT;
+ case SND_SOC_DAIFMT_DSP_B:
+ sscr0 |= SSCR0_MOD | SSCR0_PSP;
+ sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sspsp |= SSPSP_SFRMP;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ssp_write_reg(ssp, SSCR0, sscr0);
+ ssp_write_reg(ssp, SSCR1, sscr1);
+ ssp_write_reg(ssp, SSPSP, sspsp);
+
+ dump_registers(ssp);
+
+ /* Since we are configuring the timings for the format by hand
+ * we have to defer some things until hw_params() where we
+ * know parameters like the sample size.
+ */
+ priv->dai_fmt = fmt;
+
+ return 0;
+}
+
+/*
+ * Set the SSP audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ int dma = 0, chn = params_channels(params);
+ u32 sscr0;
+ u32 sspsp;
+ int width = snd_pcm_format_physical_width(params_format(params));
+
+ /* select correct DMA params */
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ dma = 1; /* capture DMA offset is 1,3 */
+ if (chn == 2)
+ dma += 2; /* stereo DMA offset is 2, mono is 0 */
+ cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
+
+ dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
+
+ /* we can only change the settings if the port is not in use */
+ if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
+ return 0;
+
+ /* clear selected SSP bits */
+ sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
+ ssp_write_reg(ssp, SSCR0, sscr0);
+
+ /* bit size */
+ sscr0 = ssp_read_reg(ssp, SSCR0);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+#ifdef CONFIG_PXA3xx
+ if (cpu_is_pxa3xx())
+ sscr0 |= SSCR0_FPCKE;
+#endif
+ sscr0 |= SSCR0_DataSize(16);
+ if (params_channels(params) > 1)
+ sscr0 |= SSCR0_EDSS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
+ /* we must be in network mode (2 slots) for 24 bit stereo */
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
+ /* we must be in network mode (2 slots) for 32 bit stereo */
+ break;
+ }
+ ssp_write_reg(ssp, SSCR0, sscr0);
+
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Cleared when the DAI format is set */
+ sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width);
+ ssp_write_reg(ssp, SSPSP, sspsp);
+ break;
+ default:
+ break;
+ }
+
+ /* We always use a network mode so we always require TDM slots
+ * - complain loudly and fail if they've not been set up yet.
+ */
+ if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) {
+ dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
+ return -EINVAL;
+ }
+
+ dump_registers(ssp);
+
+ return 0;
+}
+
+static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+ struct ssp_priv *priv = cpu_dai->private_data;
+ struct ssp_device *ssp = priv->dev.ssp;
+ int val;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ssp_enable(&priv->dev);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ val = ssp_read_reg(ssp, SSCR1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val |= SSCR1_TSRE;
+ else
+ val |= SSCR1_RSRE;
+ ssp_write_reg(ssp, SSCR1, val);
+ val = ssp_read_reg(ssp, SSSR);
+ ssp_write_reg(ssp, SSSR, val);
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ val = ssp_read_reg(ssp, SSCR1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val |= SSCR1_TSRE;
+ else
+ val |= SSCR1_RSRE;
+ ssp_write_reg(ssp, SSCR1, val);
+ ssp_enable(&priv->dev);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ val = ssp_read_reg(ssp, SSCR1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val &= ~SSCR1_TSRE;
+ else
+ val &= ~SSCR1_RSRE;
+ ssp_write_reg(ssp, SSCR1, val);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ssp_disable(&priv->dev);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ val = ssp_read_reg(ssp, SSCR1);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ val &= ~SSCR1_TSRE;
+ else
+ val &= ~SSCR1_RSRE;
+ ssp_write_reg(ssp, SSCR1, val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ dump_registers(ssp);
+
+ return ret;
+}
+
+static int pxa_ssp_probe(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct ssp_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev.ssp = ssp_request(dai->id, "SoC audio");
+ if (priv->dev.ssp == NULL) {
+ ret = -ENODEV;
+ goto err_priv;
+ }
+
+ dai->private_data = priv;
+
+ return 0;
+
+err_priv:
+ kfree(priv);
+ return ret;
+}
+
+static void pxa_ssp_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ struct ssp_priv *priv = dai->private_data;
+ ssp_free(priv->dev.ssp);
+}
+
+#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai pxa_ssp_dai[] = {
+ {
+ .name = "pxa2xx-ssp1",
+ .id = 0,
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+ },
+ },
+ { .name = "pxa2xx-ssp2",
+ .id = 1,
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+ },
+ },
+ {
+ .name = "pxa2xx-ssp3",
+ .id = 2,
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+ },
+ },
+ {
+ .name = "pxa2xx-ssp4",
+ .id = 3,
+ .probe = pxa_ssp_probe,
+ .remove = pxa_ssp_remove,
+ .suspend = pxa_ssp_suspend,
+ .resume = pxa_ssp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = PXA_SSP_RATES,
+ .formats = PXA_SSP_FORMATS,
+ },
+ .ops = {
+ .startup = pxa_ssp_startup,
+ .shutdown = pxa_ssp_shutdown,
+ .trigger = pxa_ssp_trigger,
+ .hw_params = pxa_ssp_hw_params,
+ .set_sysclk = pxa_ssp_set_dai_sysclk,
+ .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+ .set_pll = pxa_ssp_set_dai_pll,
+ .set_fmt = pxa_ssp_set_dai_fmt,
+ .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+ .set_tristate = pxa_ssp_set_dai_tristate,
+ },
+ },
+};
+EXPORT_SYMBOL_GPL(pxa_ssp_dai);
+
+static int __init pxa_ssp_init(void)
+{
+ return snd_soc_register_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai));
+}
+module_init(pxa_ssp_init);
+
+static void __exit pxa_ssp_exit(void)
+{
+ snd_soc_unregister_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai));
+}
+module_exit(pxa_ssp_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h
new file mode 100644
index 000000000000..91deadd55675
--- /dev/null
+++ b/sound/soc/pxa/pxa-ssp.h
@@ -0,0 +1,47 @@
+/*
+ * ASoC PXA SSP port support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _PXA_SSP_H
+#define _PXA_SSP_H
+
+/* pxa DAI SSP IDs */
+#define PXA_DAI_SSP1 0
+#define PXA_DAI_SSP2 1
+#define PXA_DAI_SSP3 2
+#define PXA_DAI_SSP4 3
+
+/* SSP clock sources */
+#define PXA_SSP_CLK_PLL 0
+#define PXA_SSP_CLK_EXT 1
+#define PXA_SSP_CLK_NET 2
+#define PXA_SSP_CLK_AUDIO 3
+#define PXA_SSP_CLK_NET_PLL 4
+
+/* SSP audio dividers */
+#define PXA_SSP_AUDIO_DIV_ACDS 0
+#define PXA_SSP_AUDIO_DIV_SCDB 1
+#define PXA_SSP_DIV_SCR 2
+
+/* SSP ACDS audio dividers values */
+#define PXA_SSP_CLK_AUDIO_DIV_1 0
+#define PXA_SSP_CLK_AUDIO_DIV_2 1
+#define PXA_SSP_CLK_AUDIO_DIV_4 2
+#define PXA_SSP_CLK_AUDIO_DIV_8 3
+#define PXA_SSP_CLK_AUDIO_DIV_16 4
+#define PXA_SSP_CLK_AUDIO_DIV_32 5
+
+/* SSP divider bypass */
+#define PXA_SSP_CLK_SCDB_4 0
+#define PXA_SSP_CLK_SCDB_1 1
+#define PXA_SSP_CLK_SCDB_8 2
+
+#define PXA_SSP_PLL_OUT 0
+
+extern struct snd_soc_dai pxa_ssp_dai[4];
+
+#endif
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index a7a3a9c5c6ff..812c2b4d3e07 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -21,6 +21,7 @@
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
+#include <mach/regs-ac97.h>
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
@@ -87,14 +88,12 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
};
#ifdef CONFIG_PM
-static int pxa2xx_ac97_suspend(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai)
{
return pxa2xx_ac97_hw_suspend();
}
-static int pxa2xx_ac97_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int pxa2xx_ac97_resume(struct snd_soc_dai *dai)
{
return pxa2xx_ac97_hw_resume();
}
@@ -117,7 +116,8 @@ static void pxa2xx_ac97_remove(struct platform_device *pdev,
}
static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -131,7 +131,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
}
static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -145,7 +146,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
}
static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -170,7 +172,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
{
.name = "pxa2xx-ac97",
.id = 0,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.probe = pxa2xx_ac97_probe,
.remove = pxa2xx_ac97_remove,
.suspend = pxa2xx_ac97_suspend,
@@ -193,7 +195,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
{
.name = "pxa2xx-ac97-aux",
.id = 1,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
@@ -212,7 +214,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
{
.name = "pxa2xx-ac97-mic",
.id = 2,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
@@ -227,6 +229,18 @@ struct snd_soc_dai pxa_ac97_dai[] = {
EXPORT_SYMBOL_GPL(pxa_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
+static int __init pxa_ac97_init(void)
+{
+ return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+}
+module_init(pxa_ac97_init);
+
+static void __exit pxa_ac97_exit(void)
+{
+ snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
+}
+module_exit(pxa_ac97_exit);
+
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index e758034db5c3..517991fb1099 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -121,7 +121,8 @@ static struct pxa2xx_gpio gpio_bus[] = {
},
};
-static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -187,7 +188,8 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
}
static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -248,7 +250,8 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
int ret = 0;
@@ -269,7 +272,8 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
return ret;
}
-static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
SACR1 |= SACR1_DRPL;
@@ -289,8 +293,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
}
#ifdef CONFIG_PM
-static int pxa2xx_i2s_suspend(struct platform_device *dev,
- struct snd_soc_dai *dai)
+static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -307,8 +310,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev,
return 0;
}
-static int pxa2xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
{
if (!dai->active)
return 0;
@@ -336,7 +338,6 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai pxa_i2s_dai = {
.name = "pxa2xx-i2s",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.suspend = pxa2xx_i2s_suspend,
.resume = pxa2xx_i2s_resume,
.playback = {
@@ -353,8 +354,7 @@ struct snd_soc_dai pxa_i2s_dai = {
.startup = pxa2xx_i2s_startup,
.shutdown = pxa2xx_i2s_shutdown,
.trigger = pxa2xx_i2s_trigger,
- .hw_params = pxa2xx_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = pxa2xx_i2s_hw_params,
.set_fmt = pxa2xx_i2s_set_dai_fmt,
.set_sysclk = pxa2xx_i2s_set_dai_sysclk,
},
@@ -364,12 +364,23 @@ EXPORT_SYMBOL_GPL(pxa_i2s_dai);
static int pxa2xx_i2s_probe(struct platform_device *dev)
{
+ int ret;
+
clk_i2s = clk_get(&dev->dev, "I2SCLK");
- return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0;
+ if (IS_ERR(clk_i2s))
+ return PTR_ERR(clk_i2s);
+
+ pxa_i2s_dai.dev = &dev->dev;
+ ret = snd_soc_register_dai(&pxa_i2s_dai);
+ if (ret != 0)
+ clk_put(clk_i2s);
+
+ return ret;
}
static int __devexit pxa2xx_i2s_remove(struct platform_device *dev)
{
+ snd_soc_unregister_dai(&pxa_i2s_dai);
clk_put(clk_i2s);
clk_i2s = ERR_PTR(-ENOENT);
return 0;
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index afcd892cd2fa..c670d08e7c9e 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -69,7 +69,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-struct snd_pcm_ops pxa2xx_pcm_ops = {
+static struct snd_pcm_ops pxa2xx_pcm_ops = {
.open = __pxa2xx_pcm_open,
.close = __pxa2xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -118,6 +118,18 @@ struct snd_soc_platform pxa2xx_soc_platform = {
};
EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
+static int __init pxa2xx_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&pxa2xx_soc_platform);
+}
+module_init(pxa2xx_soc_platform_init);
+
+static void __exit pxa2xx_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&pxa2xx_soc_platform);
+}
+module_exit(pxa2xx_soc_platform_exit);
+
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index d307b6757e95..a3b9e6bdf979 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -319,8 +319,9 @@ static struct snd_soc_dai_link spitz_dai = {
};
/* spitz audio machine driver */
-static struct snd_soc_machine snd_soc_machine_spitz = {
+static struct snd_soc_card snd_soc_spitz = {
.name = "Spitz",
+ .platform = &pxa2xx_soc_platform,
.dai_link = &spitz_dai,
.num_links = 1,
};
@@ -333,8 +334,7 @@ static struct wm8750_setup_data spitz_wm8750_setup = {
/* spitz audio subsystem */
static struct snd_soc_device spitz_snd_devdata = {
- .machine = &snd_soc_machine_spitz,
- .platform = &pxa2xx_soc_platform,
+ .card = &snd_soc_spitz,
.codec_dev = &soc_codec_dev_wm8750,
.codec_data = &spitz_wm8750_setup,
};
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index afefe41b8c46..c77194f74c9b 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -38,7 +38,7 @@
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
-static struct snd_soc_machine tosa;
+static struct snd_soc_card tosa;
#define TOSA_HP 0
#define TOSA_MIC_INT 1
@@ -230,15 +230,37 @@ static struct snd_soc_dai_link tosa_dai[] = {
},
};
-static struct snd_soc_machine tosa = {
+static int tosa_probe(struct platform_device *dev)
+{
+ int ret;
+
+ ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
+ if (ret)
+ return ret;
+ ret = gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
+ if (ret)
+ gpio_free(TOSA_GPIO_L_MUTE);
+
+ return ret;
+}
+
+static int tosa_remove(struct platform_device *dev)
+{
+ gpio_free(TOSA_GPIO_L_MUTE);
+ return 0;
+}
+
+static struct snd_soc_card tosa = {
.name = "Tosa",
+ .platform = &pxa2xx_soc_platform,
.dai_link = tosa_dai,
.num_links = ARRAY_SIZE(tosa_dai),
+ .probe = tosa_probe,
+ .remove = tosa_remove,
};
static struct snd_soc_device tosa_snd_devdata = {
- .machine = &tosa,
- .platform = &pxa2xx_soc_platform,
+ .card = &tosa,
.codec_dev = &soc_codec_dev_wm9712,
};
@@ -251,11 +273,6 @@ static int __init tosa_init(void)
if (!machine_is_tosa())
return -ENODEV;
- ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
- if (ret)
- return ret;
- gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
-
tosa_snd_device = platform_device_alloc("soc-audio", -1);
if (!tosa_snd_device) {
ret = -ENOMEM;
@@ -272,15 +289,12 @@ static int __init tosa_init(void)
platform_device_put(tosa_snd_device);
err_alloc:
- gpio_free(TOSA_GPIO_L_MUTE);
-
return ret;
}
static void __exit tosa_exit(void)
{
platform_device_unregister(tosa_snd_device);
- gpio_free(TOSA_GPIO_L_MUTE);
}
module_init(tosa_init);
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
new file mode 100644
index 000000000000..f8e9ecd589d3
--- /dev/null
+++ b/sound/soc/pxa/zylonite.c
@@ -0,0 +1,219 @@
+/*
+ * zylonite.c -- SoC audio for Zylonite
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm9713.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+#include "pxa-ssp.h"
+
+static struct snd_soc_card zylonite;
+
+static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Microphone", NULL),
+ SND_SOC_DAPM_MIC("Handset Microphone", NULL),
+ SND_SOC_DAPM_SPK("Multiactor", NULL),
+ SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
+};
+
+/* Currently supported audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Headphone output connected to HPL/HPR */
+ { "Headphone", NULL, "HPL" },
+ { "Headphone", NULL, "HPR" },
+
+ /* On-board earpiece */
+ { "Headset Earpiece", NULL, "OUT3" },
+
+ /* Headphone mic */
+ { "MIC2A", NULL, "Mic Bias" },
+ { "Mic Bias", NULL, "Headset Microphone" },
+
+ /* On-board mic */
+ { "MIC1", NULL, "Mic Bias" },
+ { "Mic Bias", NULL, "Handset Microphone" },
+
+ /* Multiactor differentially connected over SPKL/SPKR */
+ { "Multiactor", NULL, "SPKL" },
+ { "Multiactor", NULL, "SPKR" },
+};
+
+static int zylonite_wm9713_init(struct snd_soc_codec *codec)
+{
+ /* Currently we only support use of the AC97 clock here. If
+ * CLK_POUT is selected by SW15 then the clock API will need
+ * to be used to request and enable it here.
+ */
+
+ snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
+ ARRAY_SIZE(zylonite_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ /* Static setup for now */
+ snd_soc_dapm_enable_pin(codec, "Headphone");
+ snd_soc_dapm_enable_pin(codec, "Headset Earpiece");
+
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int pll_out = 0;
+ unsigned int acds = 0;
+ unsigned int wm9713_div = 0;
+ int ret = 0;
+
+ switch (params_rate(params)) {
+ case 8000:
+ wm9713_div = 12;
+ pll_out = 2048000;
+ break;
+ case 16000:
+ wm9713_div = 6;
+ pll_out = 4096000;
+ break;
+ case 48000:
+ default:
+ wm9713_div = 2;
+ pll_out = 12288000;
+ acds = 1;
+ break;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+ params_channels(params),
+ params_channels(params));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs
+ * to be set instead.
+ */
+ ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+ WM9713_PCMDIV(wm9713_div));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops zylonite_voice_ops = {
+ .hw_params = zylonite_voice_hw_params,
+};
+
+static struct snd_soc_dai_link zylonite_dai[] = {
+{
+ .name = "AC97",
+ .stream_name = "AC97 HiFi",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+ .init = zylonite_wm9713_init,
+},
+{
+ .name = "AC97 Aux",
+ .stream_name = "AC97 Aux",
+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
+},
+{
+ .name = "WM9713 Voice",
+ .stream_name = "WM9713 Voice",
+ .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3],
+ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
+ .ops = &zylonite_voice_ops,
+},
+};
+
+static struct snd_soc_card zylonite = {
+ .name = "Zylonite",
+ .platform = &pxa2xx_soc_platform,
+ .dai_link = zylonite_dai,
+ .num_links = ARRAY_SIZE(zylonite_dai),
+};
+
+static struct snd_soc_device zylonite_snd_ac97_devdata = {
+ .card = &zylonite,
+ .codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *zylonite_snd_ac97_device;
+
+static int __init zylonite_init(void)
+{
+ int ret;
+
+ zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+ if (!zylonite_snd_ac97_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(zylonite_snd_ac97_device,
+ &zylonite_snd_ac97_devdata);
+ zylonite_snd_ac97_devdata.dev = &zylonite_snd_ac97_device->dev;
+
+ ret = platform_device_add(zylonite_snd_ac97_device);
+ if (ret != 0)
+ platform_device_put(zylonite_snd_ac97_device);
+
+ return ret;
+}
+
+static void __exit zylonite_exit(void)
+{
+ platform_device_unregister(zylonite_snd_ac97_device);
+}
+
+module_init(zylonite_init);
+module_exit(zylonite_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index b9f2353effeb..fcd03acf10f6 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -44,3 +44,8 @@ config SND_S3C24XX_SOC_LN2440SBC_ALC650
Say Y if you want to add support for SoC audio on ln2440sbc
with the ALC650.
+config SND_S3C24XX_SOC_S3C24XX_UDA134X
+ tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
+ depends on SND_S3C24XX_SOC
+ select SND_S3C24XX_SOC_I2S
+ select SND_SOC_UDA134X
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 0aa5fb0b9700..96b3f3f617d4 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -13,7 +13,9 @@ obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
+snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
+obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c
index 4eab2c19c454..12c71482d258 100644
--- a/sound/soc/s3c24xx/ln2440sbc_alc650.c
+++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c
@@ -27,7 +27,7 @@
#include "s3c24xx-pcm.h"
#include "s3c24xx-ac97.h"
-static struct snd_soc_machine ln2440sbc;
+static struct snd_soc_card ln2440sbc;
static struct snd_soc_dai_link ln2440sbc_dai[] = {
{
@@ -38,15 +38,15 @@ static struct snd_soc_dai_link ln2440sbc_dai[] = {
},
};
-static struct snd_soc_machine ln2440sbc = {
+static struct snd_soc_card ln2440sbc = {
.name = "LN2440SBC",
+ .platform = &s3c24xx_soc_platform,
.dai_link = ln2440sbc_dai,
.num_links = ARRAY_SIZE(ln2440sbc_dai),
};
static struct snd_soc_device ln2440sbc_snd_ac97_devdata = {
- .machine = &ln2440sbc,
- .platform = &s3c24xx_soc_platform,
+ .card = &ln2440sbc,
.codec_dev = &soc_codec_dev_ac97,
};
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 87ddfefcc2fb..45bb12e8ea44 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -59,7 +59,7 @@
#define NEO_CAPTURE_HEADSET 7
#define NEO_CAPTURE_BLUETOOTH 8
-static struct snd_soc_machine neo1973;
+static struct snd_soc_card neo1973;
static struct i2c_client *i2c;
static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
@@ -548,7 +548,6 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
static struct snd_soc_dai bt_dai = {
.name = "Bluetooth",
.id = 0,
- .type = SND_SOC_DAI_PCM,
.playback = {
.channels_min = 1,
.channels_max = 1,
@@ -579,8 +578,9 @@ static struct snd_soc_dai_link neo1973_dai[] = {
},
};
-static struct snd_soc_machine neo1973 = {
+static struct snd_soc_card neo1973 = {
.name = "neo1973",
+ .platform = &s3c24xx_soc_platform,
.dai_link = neo1973_dai,
.num_links = ARRAY_SIZE(neo1973_dai),
};
@@ -591,8 +591,7 @@ static struct wm8753_setup_data neo1973_wm8753_setup = {
};
static struct snd_soc_device neo1973_snd_devdata = {
- .machine = &neo1973,
- .platform = &s3c24xx_soc_platform,
+ .card = &neo1973,
.codec_dev = &soc_codec_dev_wm8753,
.codec_data = &neo1973_wm8753_setup,
};
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index ded7d995a922..f3fc0aba0aaf 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -343,7 +343,8 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
}
static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
u32 iismod;
@@ -373,7 +374,8 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
unsigned long irqs;
@@ -647,8 +649,7 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
}
#ifdef CONFIG_PM
-static int s3c2412_i2s_suspend(struct platform_device *dev,
- struct snd_soc_dai *dai)
+static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
u32 iismod;
@@ -663,25 +664,24 @@ static int s3c2412_i2s_suspend(struct platform_device *dev,
iismod = readl(i2s->regs + S3C2412_IISMOD);
if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
- dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__);
+ pr_warning("%s: RXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
- dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__);
+ pr_warning("%s: TXDMA active?\n", __func__);
if (iismod & S3C2412_IISCON_IIS_ACTIVE)
- dev_warn(&dev->dev, "%s: IIS active\n", __func__);
+ pr_warning("%s: IIS active\n", __func__);
}
return 0;
}
-static int s3c2412_i2s_resume(struct platform_device *pdev,
- struct snd_soc_dai *dai)
+static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
{
struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
- dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n",
- dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
+ pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
+ dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
if (dai->active) {
writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
@@ -711,7 +711,6 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai s3c2412_i2s_dai = {
.name = "s3c2412-i2s",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.probe = s3c2412_i2s_probe,
.suspend = s3c2412_i2s_suspend,
.resume = s3c2412_i2s_resume,
@@ -730,8 +729,6 @@ struct snd_soc_dai s3c2412_i2s_dai = {
.ops = {
.trigger = s3c2412_i2s_trigger,
.hw_params = s3c2412_i2s_hw_params,
- },
- .dai_ops = {
.set_fmt = s3c2412_i2s_set_fmt,
.set_clkdiv = s3c2412_i2s_set_clkdiv,
.set_sysclk = s3c2412_i2s_set_sysclk,
@@ -739,6 +736,19 @@ struct snd_soc_dai s3c2412_i2s_dai = {
};
EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
+static int __init s3c2412_i2s_init(void)
+{
+ return snd_soc_register_dai(&s3c2412_i2s_dai);
+}
+module_init(s3c2412_i2s_init);
+
+static void __exit s3c2412_i2s_exit(void)
+{
+ snd_soc_unregister_dai(&s3c2412_i2s_dai);
+}
+module_exit(s3c2412_i2s_exit);
+
+
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index 19c5c3cf5d8c..5822d2dd49ba 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -28,7 +28,7 @@
#include <sound/soc.h>
#include <mach/hardware.h>
-#include <asm/plat-s3c/regs-ac97.h>
+#include <plat/regs-ac97.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
#include <mach/audio.h>
@@ -271,7 +271,8 @@ static void s3c2443_ac97_remove(struct platform_device *pdev,
}
static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -284,7 +285,8 @@ static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
u32 ac_glbctrl;
@@ -313,7 +315,8 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
}
static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -327,7 +330,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
}
static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
- int cmd)
+ int cmd, struct snd_soc_dai *dai)
{
u32 ac_glbctrl;
@@ -356,7 +359,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
{
.name = "s3c2443-ac97",
.id = 0,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.probe = s3c2443_ac97_probe,
.remove = s3c2443_ac97_remove,
.playback = {
@@ -378,7 +381,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
{
.name = "pxa2xx-ac97-mic",
.id = 1,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
@@ -393,6 +396,21 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
EXPORT_SYMBOL_GPL(soc_ac97_ops);
+static int __init s3c2443_ac97_init(void)
+{
+ return snd_soc_register_dais(s3c2443_ac97_dai,
+ ARRAY_SIZE(s3c2443_ac97_dai));
+}
+module_init(s3c2443_ac97_init);
+
+static void __exit s3c2443_ac97_exit(void)
+{
+ snd_soc_unregister_dais(s3c2443_ac97_dai,
+ ARRAY_SIZE(s3c2443_ac97_dai));
+}
+module_exit(s3c2443_ac97_exit);
+
+
MODULE_AUTHOR("Graeme Gregory");
MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index ba4476b55fbc..6f4d439b57aa 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -243,7 +243,8 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
}
static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
u32 iismod;
@@ -261,10 +262,17 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
+ iismod &= ~S3C2410_IISMOD_16BIT;
+ ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->dma_size = 1;
break;
case SNDRV_PCM_FORMAT_S16_LE:
iismod |= S3C2410_IISMOD_16BIT;
+ ((struct s3c24xx_pcm_dma_params *)
+ rtd->dai->cpu_dai->dma_data)->dma_size = 2;
break;
+ default:
+ return -EINVAL;
}
writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -272,7 +280,8 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
int ret = 0;
@@ -410,8 +419,7 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev,
}
#ifdef CONFIG_PM
-static int s3c24xx_i2s_suspend(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
+static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
@@ -425,8 +433,7 @@ static int s3c24xx_i2s_suspend(struct platform_device *pdev,
return 0;
}
-static int s3c24xx_i2s_resume(struct platform_device *pdev,
- struct snd_soc_dai *cpu_dai)
+static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
{
DBG("Entered %s\n", __func__);
clk_enable(s3c24xx_i2s.iis_clk);
@@ -452,7 +459,6 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
struct snd_soc_dai s3c24xx_i2s_dai = {
.name = "s3c24xx-i2s",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.probe = s3c24xx_i2s_probe,
.suspend = s3c24xx_i2s_suspend,
.resume = s3c24xx_i2s_resume,
@@ -468,8 +474,7 @@ struct snd_soc_dai s3c24xx_i2s_dai = {
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.trigger = s3c24xx_i2s_trigger,
- .hw_params = s3c24xx_i2s_hw_params,},
- .dai_ops = {
+ .hw_params = s3c24xx_i2s_hw_params,
.set_fmt = s3c24xx_i2s_set_fmt,
.set_clkdiv = s3c24xx_i2s_set_clkdiv,
.set_sysclk = s3c24xx_i2s_set_sysclk,
@@ -477,6 +482,18 @@ struct snd_soc_dai s3c24xx_i2s_dai = {
};
EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
+static int __init s3c24xx_i2s_init(void)
+{
+ return snd_soc_register_dai(&s3c24xx_i2s_dai);
+}
+module_init(s3c24xx_i2s_init);
+
+static void __exit s3c24xx_i2s_exit(void)
+{
+ snd_soc_unregister_dai(&s3c24xx_i2s_dai);
+}
+module_exit(s3c24xx_i2s_exit);
+
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index e13e614bada9..7c64d31d067e 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -465,6 +465,18 @@ struct snd_soc_platform s3c24xx_soc_platform = {
};
EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
+static int __init s3c24xx_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&s3c24xx_soc_platform);
+}
+module_init(s3c24xx_soc_platform_init);
+
+static void __exit s3c24xx_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&s3c24xx_soc_platform);
+}
+module_exit(s3c24xx_soc_platform_exit);
+
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c
new file mode 100644
index 000000000000..a0a4d1832a14
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c
@@ -0,0 +1,373 @@
+/*
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * s3c24xx_uda134x.c -- S3C24XX_UDA134X ALSA SoC Audio board driver
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * This program is free software; you can 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/clk.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/s3c24xx_uda134x.h>
+#include <sound/uda134x.h>
+
+#include <asm/plat-s3c24xx/regs-iis.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "../codecs/uda134x.h"
+
+
+/* #define ENFORCE_RATES 1 */
+/*
+ Unfortunately the S3C24XX in master mode has a limited capacity of
+ generating the clock for the codec. If you define this only rates
+ that are really available will be enforced. But be careful, most
+ user level application just want the usual sampling frequencies (8,
+ 11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
+ operation for embedded systems. So if you aren't very lucky or your
+ hardware engineer wasn't very forward-looking it's better to leave
+ this undefined. If you do so an approximate value for the requested
+ sampling rate in the range -/+ 5% will be chosen. If this in not
+ possible an error will be returned.
+*/
+
+static struct clk *xtal;
+static struct clk *pclk;
+/* this is need because we don't have a place where to keep the
+ * pointers to the clocks in each substream. We get the clocks only
+ * when we are actually using them so we don't block stuff like
+ * frequency change or oscillator power-off */
+static int clk_users;
+static DEFINE_MUTEX(clk_lock);
+
+static unsigned int rates[33 * 2];
+#ifdef ENFORCE_RATES
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+#endif
+
+static struct platform_device *s3c24xx_uda134x_snd_device;
+
+static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+#ifdef ENFORCE_RATES
+ struct snd_pcm_runtime *runtime = substream->runtime;;
+#endif
+
+ mutex_lock(&clk_lock);
+ pr_debug("%s %d\n", __func__, clk_users);
+ if (clk_users == 0) {
+ xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
+ if (!xtal) {
+ printk(KERN_ERR "%s cannot get xtal\n", __func__);
+ ret = -EBUSY;
+ } else {
+ pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
+ "pclk");
+ if (!pclk) {
+ printk(KERN_ERR "%s cannot get pclk\n",
+ __func__);
+ clk_put(xtal);
+ ret = -EBUSY;
+ }
+ }
+ if (!ret) {
+ int i, j;
+
+ for (i = 0; i < 2; i++) {
+ int fs = i ? 256 : 384;
+
+ rates[i*33] = clk_get_rate(xtal) / fs;
+ for (j = 1; j < 33; j++)
+ rates[i*33 + j] = clk_get_rate(pclk) /
+ (j * fs);
+ }
+ }
+ }
+ clk_users += 1;
+ mutex_unlock(&clk_lock);
+ if (!ret) {
+#ifdef ENFORCE_RATES
+ ret = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_rates);
+ if (ret < 0)
+ printk(KERN_ERR "%s cannot set constraints\n",
+ __func__);
+#endif
+ }
+ return ret;
+}
+
+static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
+{
+ mutex_lock(&clk_lock);
+ pr_debug("%s %d\n", __func__, clk_users);
+ clk_users -= 1;
+ if (clk_users == 0) {
+ clk_put(xtal);
+ xtal = NULL;
+ clk_put(pclk);
+ pclk = NULL;
+ }
+ mutex_unlock(&clk_lock);
+}
+
+static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int clk = 0;
+ int ret = 0;
+ int clk_source, fs_mode;
+ unsigned long rate = params_rate(params);
+ long err, cerr;
+ unsigned int div;
+ int i, bi;
+
+ err = 999999;
+ bi = 0;
+ for (i = 0; i < 2*33; i++) {
+ cerr = rates[i] - rate;
+ if (cerr < 0)
+ cerr = -cerr;
+ if (cerr < err) {
+ err = cerr;
+ bi = i;
+ }
+ }
+ if (bi / 33 == 1)
+ fs_mode = S3C2410_IISMOD_256FS;
+ else
+ fs_mode = S3C2410_IISMOD_384FS;
+ if (bi % 33 == 0) {
+ clk_source = S3C24XX_CLKSRC_MPLL;
+ div = 1;
+ } else {
+ clk_source = S3C24XX_CLKSRC_PCLK;
+ div = bi % 33;
+ }
+ pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
+
+ clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
+ pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
+ fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
+ clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
+ div, clk, err);
+
+ if ((err * 100 / rate) > 5) {
+ printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
+ "too different from desired (%ld%%)\n",
+ err * 100 / rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
+ S3C2410_IISMOD_32FS);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+ S3C24XX_PRESCALE(div, div));
+ if (ret < 0)
+ return ret;
+
+ /* set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops s3c24xx_uda134x_ops = {
+ .startup = s3c24xx_uda134x_startup,
+ .shutdown = s3c24xx_uda134x_shutdown,
+ .hw_params = s3c24xx_uda134x_hw_params,
+};
+
+static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
+ .name = "UDA134X",
+ .stream_name = "UDA134X",
+ .codec_dai = &uda134x_dai,
+ .cpu_dai = &s3c24xx_i2s_dai,
+ .ops = &s3c24xx_uda134x_ops,
+};
+
+static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
+ .name = "S3C24XX_UDA134X",
+ .platform = &s3c24xx_soc_platform,
+ .dai_link = &s3c24xx_uda134x_dai_link,
+ .num_links = 1,
+};
+
+static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
+
+static void setdat(int v)
+{
+ gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
+}
+
+static void setclk(int v)
+{
+ gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
+}
+
+static void setmode(int v)
+{
+ gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
+}
+
+static struct uda134x_platform_data s3c24xx_uda134x = {
+ .l3 = {
+ .setdat = setdat,
+ .setclk = setclk,
+ .setmode = setmode,
+ .data_hold = 1,
+ .data_setup = 1,
+ .clock_high = 1,
+ .mode_hold = 1,
+ .mode = 1,
+ .mode_setup = 1,
+ },
+};
+
+static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
+ .card = &snd_soc_s3c24xx_uda134x,
+ .codec_dev = &soc_codec_dev_uda134x,
+ .codec_data = &s3c24xx_uda134x,
+};
+
+static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
+{
+ if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
+ printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+ "l3 %s pin already in use", fun);
+ return -EBUSY;
+ }
+ gpio_direction_output(pin, 0);
+ return 0;
+}
+
+static int s3c24xx_uda134x_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
+
+ s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
+ if (s3c24xx_uda134x_l3_pins == NULL) {
+ printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+ "unable to find platform data\n");
+ return -ENODEV;
+ }
+ s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
+ s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
+
+ if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
+ "data") < 0)
+ return -EBUSY;
+ if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
+ "clk") < 0) {
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+ return -EBUSY;
+ }
+ if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
+ "mode") < 0) {
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+ return -EBUSY;
+ }
+
+ s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!s3c24xx_uda134x_snd_device) {
+ printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+ "Unable to register\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(s3c24xx_uda134x_snd_device,
+ &s3c24xx_uda134x_snd_devdata);
+ s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
+ ret = platform_device_add(s3c24xx_uda134x_snd_device);
+ if (ret) {
+ printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
+ platform_device_put(s3c24xx_uda134x_snd_device);
+ }
+
+ return ret;
+}
+
+static int s3c24xx_uda134x_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(s3c24xx_uda134x_snd_device);
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+ gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
+ return 0;
+}
+
+static struct platform_driver s3c24xx_uda134x_driver = {
+ .probe = s3c24xx_uda134x_probe,
+ .remove = s3c24xx_uda134x_remove,
+ .driver = {
+ .name = "s3c24xx_uda134x",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c24xx_uda134x_init(void)
+{
+ return platform_driver_register(&s3c24xx_uda134x_driver);
+}
+
+static void __exit s3c24xx_uda134x_exit(void)
+{
+ platform_driver_unregister(&s3c24xx_uda134x_driver);
+}
+
+
+module_init(s3c24xx_uda134x_init);
+module_exit(s3c24xx_uda134x_exit);
+
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index 8515d6ff03f2..a2a4f5323c17 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -23,7 +23,7 @@
#include "s3c24xx-pcm.h"
#include "s3c24xx-ac97.h"
-static struct snd_soc_machine smdk2443;
+static struct snd_soc_card smdk2443;
static struct snd_soc_dai_link smdk2443_dai[] = {
{
@@ -34,15 +34,15 @@ static struct snd_soc_dai_link smdk2443_dai[] = {
},
};
-static struct snd_soc_machine smdk2443 = {
+static struct snd_soc_card smdk2443 = {
.name = "SMDK2443",
+ .platform = &s3c24xx_soc_platform,
.dai_link = smdk2443_dai,
.num_links = ARRAY_SIZE(smdk2443_dai),
};
static struct snd_soc_device smdk2443_snd_ac97_devdata = {
- .machine = &smdk2443,
- .platform = &s3c24xx_soc_platform,
+ .card = &smdk2443,
.codec_dev = &soc_codec_dev_ac97,
};
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c
index 9faa12622d09..0dad3a0bb920 100644
--- a/sound/soc/sh/dma-sh7760.c
+++ b/sound/soc/sh/dma-sh7760.c
@@ -348,6 +348,18 @@ struct snd_soc_platform sh7760_soc_platform = {
};
EXPORT_SYMBOL_GPL(sh7760_soc_platform);
+static int __init sh7760_soc_platform_init(void)
+{
+ return snd_soc_register_platform(&sh7760_soc_platform);
+}
+module_init(sh7760_soc_platform_init);
+
+static void __exit sh7760_soc_platform_exit(void)
+{
+ snd_soc_unregister_platform(&sh7760_soc_platform);
+}
+module_exit(sh7760_soc_platform_exit);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index df7bc345c320..eab31838badf 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -236,7 +236,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
EXPORT_SYMBOL_GPL(soc_ac97_ops);
static int hac_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
@@ -270,7 +271,7 @@ struct snd_soc_dai sh4_hac_dai[] = {
{
.name = "HAC0",
.id = 0,
- .type = SND_SOC_DAI_AC97,
+ .ac97_control = 1,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -290,8 +291,8 @@ struct snd_soc_dai sh4_hac_dai[] = {
#ifdef CONFIG_CPU_SUBTYPE_SH7760
{
.name = "HAC1",
+ .ac97_control = 1,
.id = 1,
- .type = SND_SOC_DAI_AC97,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@@ -313,6 +314,18 @@ struct snd_soc_dai sh4_hac_dai[] = {
};
EXPORT_SYMBOL_GPL(sh4_hac_dai);
+static int __init sh4_hac_init(void)
+{
+ return snd_soc_register_dais(sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
+}
+module_init(sh4_hac_init);
+
+static void __exit sh4_hac_exit(void)
+{
+ snd_soc_unregister_dais(sh4_hac_dai, ARRAY_SIZE(sh4_hac_dai));
+}
+module_exit(sh4_hac_exit);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c
index 92bfaf4774a7..ce7f95b59de3 100644
--- a/sound/soc/sh/sh7760-ac97.c
+++ b/sound/soc/sh/sh7760-ac97.c
@@ -38,15 +38,15 @@ static struct snd_soc_dai_link sh7760_ac97_dai = {
.ops = NULL,
};
-static struct snd_soc_machine sh7760_ac97_soc_machine = {
+static struct snd_soc_card sh7760_ac97_soc_machine = {
.name = "SH7760 AC97",
+ .platform = &sh7760_soc_platform,
.dai_link = &sh7760_ac97_dai,
.num_links = 1,
};
static struct snd_soc_device sh7760_ac97_snd_devdata = {
- .machine = &sh7760_ac97_soc_machine,
- .platform = &sh7760_soc_platform,
+ .card = &sh7760_ac97_soc_machine,
.codec_dev = &soc_codec_dev_ac97,
};
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 55c3464163ab..d1e5390fddeb 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -89,7 +89,8 @@ struct ssi_priv {
* track usage of the SSI; it is simplex-only so prevent attempts of
* concurrent playback + capture. FIXME: any locking required?
*/
-static int ssi_startup(struct snd_pcm_substream *substream)
+static int ssi_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -101,7 +102,8 @@ static int ssi_startup(struct snd_pcm_substream *substream)
return 0;
}
-static void ssi_shutdown(struct snd_pcm_substream *substream)
+static void ssi_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -109,7 +111,8 @@ static void ssi_shutdown(struct snd_pcm_substream *substream)
ssi->inuse = 0;
}
-static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -129,7 +132,8 @@ static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
}
static int ssi_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -336,7 +340,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
{
.name = "SSI0",
.id = 0,
- .type = SND_SOC_DAI_I2S,
.playback = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
@@ -354,8 +357,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
.shutdown = ssi_shutdown,
.trigger = ssi_trigger,
.hw_params = ssi_hw_params,
- },
- .dai_ops = {
.set_sysclk = ssi_set_sysclk,
.set_clkdiv = ssi_set_clkdiv,
.set_fmt = ssi_set_fmt,
@@ -365,7 +366,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
{
.name = "SSI1",
.id = 1,
- .type = SND_SOC_DAI_I2S,
.playback = {
.rates = SSI_RATES,
.formats = SSI_FMTS,
@@ -383,8 +383,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
.shutdown = ssi_shutdown,
.trigger = ssi_trigger,
.hw_params = ssi_hw_params,
- },
- .dai_ops = {
.set_sysclk = ssi_set_sysclk,
.set_clkdiv = ssi_set_clkdiv,
.set_fmt = ssi_set_fmt,
@@ -394,6 +392,18 @@ struct snd_soc_dai sh4_ssi_dai[] = {
};
EXPORT_SYMBOL_GPL(sh4_ssi_dai);
+static int __init sh4_ssi_init(void)
+{
+ return snd_soc_register_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
+}
+module_init(sh4_ssi_init);
+
+static void __exit sh4_ssi_exit(void)
+{
+ snd_soc_unregister_dais(sh4_ssi_dai, ARRAY_SIZE(sh4_ssi_dai));
+}
+module_exit(sh4_ssi_exit);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 16c7453f4946..b098c0b4c584 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/bitops.h>
+#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -34,18 +35,23 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
-/* debug */
-#define SOC_DEBUG 0
-#if SOC_DEBUG
-#define dbg(format, arg...) printk(format, ## arg)
-#else
-#define dbg(format, arg...)
-#endif
-
static DEFINE_MUTEX(pcm_mutex);
static DEFINE_MUTEX(io_mutex);
static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_root;
+#endif
+
+static DEFINE_MUTEX(client_mutex);
+static LIST_HEAD(card_list);
+static LIST_HEAD(dai_list);
+static LIST_HEAD(platform_list);
+static LIST_HEAD(codec_list);
+
+static int snd_soc_register_card(struct snd_soc_card *card);
+static int snd_soc_unregister_card(struct snd_soc_card *card);
+
/*
* This is a timeout to do a DAPM powerdown after a stream is closed().
* It can be used to eliminate pops between different playback streams, e.g.
@@ -107,20 +113,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
}
#endif
-static inline const char *get_dai_name(int type)
-{
- switch (type) {
- case SND_SOC_DAI_AC97_BUS:
- case SND_SOC_DAI_AC97:
- return "AC97";
- case SND_SOC_DAI_I2S:
- return "I2S";
- case SND_SOC_DAI_PCM:
- return "PCM";
- }
- return NULL;
-}
-
/*
* Called by ALSA when a PCM substream is opened, the runtime->hw record is
* then initialized and any private data can be allocated. This also calls
@@ -130,9 +122,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_card *card = socdev->card;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
@@ -141,7 +134,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
/* startup the audio subsystem */
if (cpu_dai->ops.startup) {
- ret = cpu_dai->ops.startup(substream);
+ ret = cpu_dai->ops.startup(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open interface %s\n",
cpu_dai->name);
@@ -158,7 +151,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
}
if (codec_dai->ops.startup) {
- ret = codec_dai->ops.startup(substream);
+ ret = codec_dai->ops.startup(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't open codec %s\n",
codec_dai->name);
@@ -228,12 +221,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
goto machine_err;
}
- dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
- dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
- dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
- runtime->hw.channels_max);
- dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
- runtime->hw.rate_max);
+ pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
+ pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
+ pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+ runtime->hw.channels_max);
+ pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
+ runtime->hw.rate_max);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
cpu_dai->playback.active = codec_dai->playback.active = 1;
@@ -255,7 +248,7 @@ codec_dai_err:
platform_err:
if (cpu_dai->ops.shutdown)
- cpu_dai->ops.shutdown(substream);
+ cpu_dai->ops.shutdown(substream, cpu_dai);
out:
mutex_unlock(&pcm_mutex);
return ret;
@@ -268,8 +261,9 @@ out:
*/
static void close_delayed_work(struct work_struct *work)
{
- struct snd_soc_device *socdev =
- container_of(work, struct snd_soc_device, delayed_work.work);
+ struct snd_soc_card *card = container_of(work, struct snd_soc_card,
+ delayed_work.work);
+ struct snd_soc_device *socdev = card->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_dai *codec_dai;
int i;
@@ -278,18 +272,18 @@ static void close_delayed_work(struct work_struct *work)
for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
- dbg("pop wq checking: %s status: %s waiting: %s\n",
- codec_dai->playback.stream_name,
- codec_dai->playback.active ? "active" : "inactive",
- codec_dai->pop_wait ? "yes" : "no");
+ pr_debug("pop wq checking: %s status: %s waiting: %s\n",
+ codec_dai->playback.stream_name,
+ codec_dai->playback.active ? "active" : "inactive",
+ codec_dai->pop_wait ? "yes" : "no");
/* are we waiting on this codec DAI stream */
if (codec_dai->pop_wait == 1) {
/* Reduce power if no longer active */
if (codec->active == 0) {
- dbg("pop wq D1 %s %s\n", codec->name,
- codec_dai->playback.stream_name);
+ pr_debug("pop wq D1 %s %s\n", codec->name,
+ codec_dai->playback.stream_name);
snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_PREPARE);
}
@@ -301,8 +295,8 @@ static void close_delayed_work(struct work_struct *work)
/* Fall into standby if no longer active */
if (codec->active == 0) {
- dbg("pop wq D3 %s %s\n", codec->name,
- codec_dai->playback.stream_name);
+ pr_debug("pop wq D3 %s %s\n", codec->name,
+ codec_dai->playback.stream_name);
snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_STANDBY);
}
@@ -320,8 +314,9 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_card *card = socdev->card;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
@@ -346,10 +341,10 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
snd_soc_dai_digital_mute(codec_dai, 1);
if (cpu_dai->ops.shutdown)
- cpu_dai->ops.shutdown(substream);
+ cpu_dai->ops.shutdown(substream, cpu_dai);
if (codec_dai->ops.shutdown)
- codec_dai->ops.shutdown(substream);
+ codec_dai->ops.shutdown(substream, codec_dai);
if (machine->ops && machine->ops->shutdown)
machine->ops->shutdown(substream);
@@ -361,7 +356,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* start delayed pop wq here for playback streams */
codec_dai->pop_wait = 1;
- schedule_delayed_work(&socdev->delayed_work,
+ schedule_delayed_work(&card->delayed_work,
msecs_to_jiffies(pmdown_time));
} else {
/* capture streams can be powered down now */
@@ -387,8 +382,9 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_card *card = socdev->card;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
@@ -413,7 +409,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
if (codec_dai->ops.prepare) {
- ret = codec_dai->ops.prepare(substream);
+ ret = codec_dai->ops.prepare(substream, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: codec DAI prepare error\n");
goto out;
@@ -421,58 +417,49 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
if (cpu_dai->ops.prepare) {
- ret = cpu_dai->ops.prepare(substream);
+ ret = cpu_dai->ops.prepare(substream, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: cpu DAI prepare error\n");
goto out;
}
}
- /* we only want to start a DAPM playback stream if we are not waiting
- * on an existing one stopping */
- if (codec_dai->pop_wait) {
- /* we are waiting for the delayed work to start */
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- snd_soc_dapm_stream_event(socdev->codec,
- codec_dai->capture.stream_name,
- SND_SOC_DAPM_STREAM_START);
- else {
- codec_dai->pop_wait = 0;
- cancel_delayed_work(&socdev->delayed_work);
- snd_soc_dai_digital_mute(codec_dai, 0);
- }
- } else {
- /* no delayed work - do we need to power up codec */
- if (codec->bias_level != SND_SOC_BIAS_ON) {
+ /* cancel any delayed stream shutdown that is pending */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ codec_dai->pop_wait) {
+ codec_dai->pop_wait = 0;
+ cancel_delayed_work(&card->delayed_work);
+ }
- snd_soc_dapm_set_bias_level(socdev,
- SND_SOC_BIAS_PREPARE);
+ /* do we need to power up codec */
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
+ snd_soc_dapm_set_bias_level(socdev,
+ SND_SOC_BIAS_PREPARE);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dapm_stream_event(codec,
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(codec,
codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(codec,
+ else
+ snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
- snd_soc_dai_digital_mute(codec_dai, 0);
+ snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+ snd_soc_dai_digital_mute(codec_dai, 0);
- } else {
- /* codec already powered - power on widgets */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dapm_stream_event(codec,
+ } else {
+ /* codec already powered - power on widgets */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_stream_event(codec,
codec_dai->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(codec,
+ else
+ snd_soc_dapm_stream_event(codec,
codec_dai->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
- snd_soc_dai_digital_mute(codec_dai, 0);
- }
+ snd_soc_dai_digital_mute(codec_dai, 0);
}
out:
@@ -491,7 +478,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret = 0;
@@ -507,7 +495,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
if (codec_dai->ops.hw_params) {
- ret = codec_dai->ops.hw_params(substream, params);
+ ret = codec_dai->ops.hw_params(substream, params, codec_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: can't set codec %s hw params\n",
codec_dai->name);
@@ -516,7 +504,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
if (cpu_dai->ops.hw_params) {
- ret = cpu_dai->ops.hw_params(substream, params);
+ ret = cpu_dai->ops.hw_params(substream, params, cpu_dai);
if (ret < 0) {
printk(KERN_ERR "asoc: interface %s hw params failed\n",
cpu_dai->name);
@@ -539,11 +527,11 @@ out:
platform_err:
if (cpu_dai->ops.hw_free)
- cpu_dai->ops.hw_free(substream);
+ cpu_dai->ops.hw_free(substream, cpu_dai);
interface_err:
if (codec_dai->ops.hw_free)
- codec_dai->ops.hw_free(substream);
+ codec_dai->ops.hw_free(substream, codec_dai);
codec_err:
if (machine->ops && machine->ops->hw_free)
@@ -561,7 +549,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
struct snd_soc_codec *codec = socdev->codec;
@@ -582,10 +571,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
/* now free hw params for the DAI's */
if (codec_dai->ops.hw_free)
- codec_dai->ops.hw_free(substream);
+ codec_dai->ops.hw_free(substream, codec_dai);
if (cpu_dai->ops.hw_free)
- cpu_dai->ops.hw_free(substream);
+ cpu_dai->ops.hw_free(substream, cpu_dai);
mutex_unlock(&pcm_mutex);
return 0;
@@ -595,14 +584,15 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_card *card= socdev->card;
struct snd_soc_dai_link *machine = rtd->dai;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *cpu_dai = machine->cpu_dai;
struct snd_soc_dai *codec_dai = machine->codec_dai;
int ret;
if (codec_dai->ops.trigger) {
- ret = codec_dai->ops.trigger(substream, cmd);
+ ret = codec_dai->ops.trigger(substream, cmd, codec_dai);
if (ret < 0)
return ret;
}
@@ -614,7 +604,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
if (cpu_dai->ops.trigger) {
- ret = cpu_dai->ops.trigger(substream, cmd);
+ ret = cpu_dai->ops.trigger(substream, cmd, cpu_dai);
if (ret < 0)
return ret;
}
@@ -636,8 +626,8 @@ static struct snd_pcm_ops soc_pcm_ops = {
static int soc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
int i;
@@ -653,29 +643,29 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
/* mute any active DAC's */
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
- if (dai->dai_ops.digital_mute && dai->playback.active)
- dai->dai_ops.digital_mute(dai, 1);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+ if (dai->ops.digital_mute && dai->playback.active)
+ dai->ops.digital_mute(dai, 1);
}
/* suspend all pcms */
- for (i = 0; i < machine->num_links; i++)
- snd_pcm_suspend_all(machine->dai_link[i].pcm);
+ for (i = 0; i < card->num_links; i++)
+ snd_pcm_suspend_all(card->dai_link[i].pcm);
- if (machine->suspend_pre)
- machine->suspend_pre(pdev, state);
+ if (card->suspend_pre)
+ card->suspend_pre(pdev, state);
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
- if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
- cpu_dai->suspend(pdev, cpu_dai);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+ if (cpu_dai->suspend && !cpu_dai->ac97_control)
+ cpu_dai->suspend(cpu_dai);
if (platform->suspend)
- platform->suspend(pdev, cpu_dai);
+ platform->suspend(cpu_dai);
}
/* close any waiting streams and save state */
- run_delayed_work(&socdev->delayed_work);
+ run_delayed_work(&card->delayed_work);
codec->suspend_bias_level = codec->bias_level;
for (i = 0; i < codec->num_dai; i++) {
@@ -692,14 +682,14 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
if (codec_dev->suspend)
codec_dev->suspend(pdev, state);
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
- if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
- cpu_dai->suspend(pdev, cpu_dai);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+ if (cpu_dai->suspend && cpu_dai->ac97_control)
+ cpu_dai->suspend(cpu_dai);
}
- if (machine->suspend_post)
- machine->suspend_post(pdev, state);
+ if (card->suspend_post)
+ card->suspend_post(pdev, state);
return 0;
}
@@ -709,11 +699,11 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
*/
static void soc_resume_deferred(struct work_struct *work)
{
- struct snd_soc_device *socdev = container_of(work,
- struct snd_soc_device,
- deferred_resume_work);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_card *card = container_of(work,
+ struct snd_soc_card,
+ deferred_resume_work);
+ struct snd_soc_device *socdev = card->socdev;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
struct snd_soc_codec *codec = socdev->codec;
struct platform_device *pdev = to_platform_device(socdev->dev);
@@ -723,15 +713,15 @@ static void soc_resume_deferred(struct work_struct *work)
* so userspace apps are blocked from touching us
*/
- dev_info(socdev->dev, "starting resume work\n");
+ dev_dbg(socdev->dev, "starting resume work\n");
- if (machine->resume_pre)
- machine->resume_pre(pdev);
+ if (card->resume_pre)
+ card->resume_pre(pdev);
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
- if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
- cpu_dai->resume(pdev, cpu_dai);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+ if (cpu_dai->resume && cpu_dai->ac97_control)
+ cpu_dai->resume(cpu_dai);
}
if (codec_dev->resume)
@@ -749,24 +739,24 @@ static void soc_resume_deferred(struct work_struct *work)
}
/* unmute any active DACs */
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
- if (dai->dai_ops.digital_mute && dai->playback.active)
- dai->dai_ops.digital_mute(dai, 0);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+ if (dai->ops.digital_mute && dai->playback.active)
+ dai->ops.digital_mute(dai, 0);
}
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
- if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
- cpu_dai->resume(pdev, cpu_dai);
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+ if (cpu_dai->resume && !cpu_dai->ac97_control)
+ cpu_dai->resume(cpu_dai);
if (platform->resume)
- platform->resume(pdev, cpu_dai);
+ platform->resume(cpu_dai);
}
- if (machine->resume_post)
- machine->resume_post(pdev);
+ if (card->resume_post)
+ card->resume_post(pdev);
- dev_info(socdev->dev, "resume work completed\n");
+ dev_dbg(socdev->dev, "resume work completed\n");
/* userspace can access us now we are back as we were before */
snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
@@ -776,11 +766,12 @@ static void soc_resume_deferred(struct work_struct *work)
static int soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_card *card = socdev->card;
- dev_info(socdev->dev, "scheduling resume work\n");
+ dev_dbg(socdev->dev, "scheduling resume work\n");
- if (!schedule_work(&socdev->deferred_resume_work))
- dev_err(socdev->dev, "work item may be lost\n");
+ if (!schedule_work(&card->deferred_resume_work))
+ dev_err(socdev->dev, "resume work item may be lost\n");
return 0;
}
@@ -790,23 +781,83 @@ static int soc_resume(struct platform_device *pdev)
#define soc_resume NULL
#endif
-/* probes a new socdev */
-static int soc_probe(struct platform_device *pdev)
+static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
- int ret = 0, i;
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
- struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+ struct platform_device *pdev = container_of(card->dev,
+ struct platform_device,
+ dev);
+ struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+ struct snd_soc_platform *platform;
+ struct snd_soc_dai *dai;
+ int i, found, ret, ac97;
+
+ if (card->instantiated)
+ return;
+
+ found = 0;
+ list_for_each_entry(platform, &platform_list, list)
+ if (card->platform == platform) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "Platform %s not registered\n",
+ card->platform->name);
+ return;
+ }
+
+ ac97 = 0;
+ for (i = 0; i < card->num_links; i++) {
+ found = 0;
+ list_for_each_entry(dai, &dai_list, list)
+ if (card->dai_link[i].cpu_dai == dai) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "DAI %s not registered\n",
+ card->dai_link[i].cpu_dai->name);
+ return;
+ }
- if (machine->probe) {
- ret = machine->probe(pdev);
+ if (card->dai_link[i].cpu_dai->ac97_control)
+ ac97 = 1;
+ }
+
+ /* If we have AC97 in the system then don't wait for the
+ * codec. This will need revisiting if we have to handle
+ * systems with mixed AC97 and non-AC97 parts. Only check for
+ * DAIs currently; we can't do this per link since some AC97
+ * codecs have non-AC97 DAIs.
+ */
+ if (!ac97)
+ for (i = 0; i < card->num_links; i++) {
+ found = 0;
+ list_for_each_entry(dai, &dai_list, list)
+ if (card->dai_link[i].codec_dai == dai) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ dev_dbg(card->dev, "DAI %s not registered\n",
+ card->dai_link[i].codec_dai->name);
+ return;
+ }
+ }
+
+ /* Note that we do not current check for codec components */
+
+ dev_dbg(card->dev, "All components present, instantiating\n");
+
+ /* Found everything, bring it up */
+ if (card->probe) {
+ ret = card->probe(pdev);
if (ret < 0)
- return ret;
+ return;
}
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->probe) {
ret = cpu_dai->probe(pdev, cpu_dai);
if (ret < 0)
@@ -827,13 +878,15 @@ static int soc_probe(struct platform_device *pdev)
}
/* DAPM stream work */
- INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work);
+ INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
/* deferred resume work */
- INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred);
+ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
- return 0;
+ card->instantiated = 1;
+
+ return;
platform_err:
if (codec_dev->remove)
@@ -841,15 +894,45 @@ platform_err:
cpu_dai_err:
for (i--; i >= 0; i--) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
- if (machine->remove)
- machine->remove(pdev);
+ if (card->remove)
+ card->remove(pdev);
+}
- return ret;
+/*
+ * Attempt to initialise any uninitalised cards. Must be called with
+ * client_mutex.
+ */
+static void snd_soc_instantiate_cards(void)
+{
+ struct snd_soc_card *card;
+ list_for_each_entry(card, &card_list, list)
+ snd_soc_instantiate_card(card);
+}
+
+/* probes a new socdev */
+static int soc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_card *card = socdev->card;
+
+ /* Bodge while we push things out of socdev */
+ card->socdev = socdev;
+
+ /* Bodge while we unpick instantiation */
+ card->dev = &pdev->dev;
+ ret = snd_soc_register_card(card);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register card\n");
+ return ret;
+ }
+
+ return 0;
}
/* removes a socdev */
@@ -857,11 +940,11 @@ static int soc_remove(struct platform_device *pdev)
{
int i;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_machine *machine = socdev->machine;
- struct snd_soc_platform *platform = socdev->platform;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
- run_delayed_work(&socdev->delayed_work);
+ run_delayed_work(&card->delayed_work);
if (platform->remove)
platform->remove(pdev);
@@ -869,14 +952,16 @@ static int soc_remove(struct platform_device *pdev)
if (codec_dev->remove)
codec_dev->remove(pdev);
- for (i = 0; i < machine->num_links; i++) {
- struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+ for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
if (cpu_dai->remove)
cpu_dai->remove(pdev, cpu_dai);
}
- if (machine->remove)
- machine->remove(pdev);
+ if (card->remove)
+ card->remove(pdev);
+
+ snd_soc_unregister_card(card);
return 0;
}
@@ -898,6 +983,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)
{
struct snd_soc_codec *codec = socdev->codec;
+ struct snd_soc_card *card = socdev->card;
+ struct snd_soc_platform *platform = card->platform;
struct snd_soc_dai *codec_dai = dai_link->codec_dai;
struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
struct snd_soc_pcm_runtime *rtd;
@@ -914,8 +1001,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
codec_dai->codec = socdev->codec;
/* check client and interface hw capabilities */
- sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
- get_dai_name(cpu_dai->type), num);
+ sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
+ num);
if (codec_dai->playback.channels_min)
playback = 1;
@@ -933,13 +1020,13 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
dai_link->pcm = pcm;
pcm->private_data = rtd;
- soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
- soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
- soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl;
- soc_pcm_ops.copy = socdev->platform->pcm_ops->copy;
- soc_pcm_ops.silence = socdev->platform->pcm_ops->silence;
- soc_pcm_ops.ack = socdev->platform->pcm_ops->ack;
- soc_pcm_ops.page = socdev->platform->pcm_ops->page;
+ soc_pcm_ops.mmap = platform->pcm_ops->mmap;
+ soc_pcm_ops.pointer = platform->pcm_ops->pointer;
+ soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
+ soc_pcm_ops.copy = platform->pcm_ops->copy;
+ soc_pcm_ops.silence = platform->pcm_ops->silence;
+ soc_pcm_ops.ack = platform->pcm_ops->ack;
+ soc_pcm_ops.page = platform->pcm_ops->page;
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
@@ -947,24 +1034,22 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
- ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm);
+ ret = platform->pcm_new(codec->card, codec_dai, pcm);
if (ret < 0) {
printk(KERN_ERR "asoc: platform pcm constructor failed\n");
kfree(rtd);
return ret;
}
- pcm->private_free = socdev->platform->pcm_free;
+ pcm->private_free = platform->pcm_free;
printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
}
/* codec register dump */
-static ssize_t codec_reg_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t soc_codec_reg_show(struct snd_soc_device *devdata, char *buf)
{
- struct snd_soc_device *devdata = dev_get_drvdata(dev);
struct snd_soc_codec *codec = devdata->codec;
int i, step = 1, count = 0;
@@ -1001,8 +1086,110 @@ static ssize_t codec_reg_show(struct device *dev,
return count;
}
+static ssize_t codec_reg_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
+ return soc_codec_reg_show(devdata, buf);
+}
+
static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ struct snd_soc_codec *codec = file->private_data;
+ struct device *card_dev = codec->card->dev;
+ struct snd_soc_device *devdata = card_dev->driver_data;
+ char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ ret = soc_codec_reg_show(devdata, buf);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ char *start = buf;
+ unsigned long reg, value;
+ int step = 1;
+ struct snd_soc_codec *codec = file->private_data;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ if (codec->reg_cache_step)
+ step = codec->reg_cache_step;
+
+ while (*start == ' ')
+ start++;
+ reg = simple_strtoul(start, &start, 16);
+ if ((reg >= codec->reg_cache_size) || (reg % step))
+ return -EINVAL;
+ while (*start == ' ')
+ start++;
+ if (strict_strtoul(start, 16, &value))
+ return -EINVAL;
+ codec->write(codec, reg, value);
+ return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+ .open = codec_reg_open_file,
+ .read = codec_reg_read_file,
+ .write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+ codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+ debugfs_root, codec,
+ &codec_reg_fops);
+ if (!codec->debugfs_reg)
+ printk(KERN_WARNING
+ "ASoC: Failed to create codec register debugfs file\n");
+
+ codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+ debugfs_root,
+ &codec->pop_time);
+ if (!codec->debugfs_pop_time)
+ printk(KERN_WARNING
+ "Failed to create pop time debugfs file\n");
+}
+
+static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+ debugfs_remove(codec->debugfs_pop_time);
+ debugfs_remove(codec->debugfs_reg);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @codec: audio codec
@@ -1121,7 +1308,7 @@ EXPORT_SYMBOL_GPL(snd_soc_test_bits);
int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
{
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_card *card = socdev->card;
int ret = 0, i;
mutex_lock(&codec->mutex);
@@ -1140,11 +1327,11 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
/* create the pcms */
- for (i = 0; i < machine->num_links; i++) {
- ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
+ for (i = 0; i < card->num_links; i++) {
+ ret = soc_new_pcm(socdev, &card->dai_link[i], i);
if (ret < 0) {
printk(KERN_ERR "asoc: can't create pcm %s\n",
- machine->dai_link[i].stream_name);
+ card->dai_link[i].stream_name);
mutex_unlock(&codec->mutex);
return ret;
}
@@ -1156,7 +1343,7 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
/**
- * snd_soc_register_card - register sound card
+ * snd_soc_init_card - register sound card
* @socdev: the SoC audio device
*
* Register a SoC sound card. Also registers an AC97 device if the
@@ -1164,29 +1351,28 @@ EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
*
* Returns 0 for success, else error.
*/
-int snd_soc_register_card(struct snd_soc_device *socdev)
+int snd_soc_init_card(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_card *card = socdev->card;
int ret = 0, i, ac97 = 0, err = 0;
- for (i = 0; i < machine->num_links; i++) {
- if (socdev->machine->dai_link[i].init) {
- err = socdev->machine->dai_link[i].init(codec);
+ for (i = 0; i < card->num_links; i++) {
+ if (card->dai_link[i].init) {
+ err = card->dai_link[i].init(codec);
if (err < 0) {
printk(KERN_ERR "asoc: failed to init %s\n",
- socdev->machine->dai_link[i].stream_name);
+ card->dai_link[i].stream_name);
continue;
}
}
- if (socdev->machine->dai_link[i].codec_dai->type ==
- SND_SOC_DAI_AC97_BUS)
+ if (card->dai_link[i].codec_dai->ac97_control)
ac97 = 1;
}
snprintf(codec->card->shortname, sizeof(codec->card->shortname),
- "%s", machine->name);
+ "%s", card->name);
snprintf(codec->card->longname, sizeof(codec->card->longname),
- "%s (%s)", machine->name, codec->name);
+ "%s (%s)", card->name, codec->name);
ret = snd_card_register(codec->card);
if (ret < 0) {
@@ -1216,12 +1402,13 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
if (err < 0)
printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
+ soc_init_codec_debugfs(socdev->codec);
mutex_unlock(&codec->mutex);
out:
return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_register_card);
+EXPORT_SYMBOL_GPL(snd_soc_init_card);
/**
* snd_soc_free_pcms - free sound card and pcms
@@ -1239,10 +1426,11 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
#endif
mutex_lock(&codec->mutex);
+ soc_cleanup_codec_debugfs(socdev->codec);
#ifdef CONFIG_SND_SOC_AC97_BUS
for (i = 0; i < codec->num_dai; i++) {
codec_dai = &codec->dai[i];
- if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
+ if (codec_dai->ac97_control && codec->ac97) {
soc_ac97_dev_unregister(codec);
goto free_card;
}
@@ -1756,8 +1944,8 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- if (dai->dai_ops.set_sysclk)
- return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+ if (dai->ops.set_sysclk)
+ return dai->ops.set_sysclk(dai, clk_id, freq, dir);
else
return -EINVAL;
}
@@ -1776,8 +1964,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div)
{
- if (dai->dai_ops.set_clkdiv)
- return dai->dai_ops.set_clkdiv(dai, div_id, div);
+ if (dai->ops.set_clkdiv)
+ return dai->ops.set_clkdiv(dai, div_id, div);
else
return -EINVAL;
}
@@ -1795,8 +1983,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
int pll_id, unsigned int freq_in, unsigned int freq_out)
{
- if (dai->dai_ops.set_pll)
- return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+ if (dai->ops.set_pll)
+ return dai->ops.set_pll(dai, pll_id, freq_in, freq_out);
else
return -EINVAL;
}
@@ -1805,15 +1993,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
/**
* snd_soc_dai_set_fmt - configure DAI hardware audio format.
* @dai: DAI
- * @clk_id: DAI specific clock ID
* @fmt: SND_SOC_DAIFMT_ format value.
*
* Configures the DAI hardware format and clocking.
*/
int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- if (dai->dai_ops.set_fmt)
- return dai->dai_ops.set_fmt(dai, fmt);
+ if (dai->ops.set_fmt)
+ return dai->ops.set_fmt(dai, fmt);
else
return -EINVAL;
}
@@ -1831,8 +2018,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
unsigned int mask, int slots)
{
- if (dai->dai_ops.set_sysclk)
- return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+ if (dai->ops.set_sysclk)
+ return dai->ops.set_tdm_slot(dai, mask, slots);
else
return -EINVAL;
}
@@ -1847,8 +2034,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
*/
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- if (dai->dai_ops.set_sysclk)
- return dai->dai_ops.set_tristate(dai, tristate);
+ if (dai->ops.set_sysclk)
+ return dai->ops.set_tristate(dai, tristate);
else
return -EINVAL;
}
@@ -1863,21 +2050,242 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
*/
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
{
- if (dai->dai_ops.digital_mute)
- return dai->dai_ops.digital_mute(dai, mute);
+ if (dai->ops.digital_mute)
+ return dai->ops.digital_mute(dai, mute);
else
return -EINVAL;
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
-static int __devinit snd_soc_init(void)
+/**
+ * snd_soc_register_card - Register a card with the ASoC core
+ *
+ * @param card Card to register
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_register_card(struct snd_soc_card *card)
+{
+ if (!card->name || !card->dev)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&card->list);
+ card->instantiated = 0;
+
+ mutex_lock(&client_mutex);
+ list_add(&card->list, &card_list);
+ snd_soc_instantiate_cards();
+ mutex_unlock(&client_mutex);
+
+ dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+
+ return 0;
+}
+
+/**
+ * snd_soc_unregister_card - Unregister a card with the ASoC core
+ *
+ * @param card Card to unregister
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_unregister_card(struct snd_soc_card *card)
+{
+ mutex_lock(&client_mutex);
+ list_del(&card->list);
+ mutex_unlock(&client_mutex);
+
+ dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
+
+ return 0;
+}
+
+/**
+ * snd_soc_register_dai - Register a DAI with the ASoC core
+ *
+ * @param dai DAI to register
+ */
+int snd_soc_register_dai(struct snd_soc_dai *dai)
+{
+ if (!dai->name)
+ return -EINVAL;
+
+ /* The device should become mandatory over time */
+ if (!dai->dev)
+ printk(KERN_WARNING "No device for DAI %s\n", dai->name);
+
+ INIT_LIST_HEAD(&dai->list);
+
+ mutex_lock(&client_mutex);
+ list_add(&dai->list, &dai_list);
+ snd_soc_instantiate_cards();
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Registered DAI '%s'\n", dai->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
+
+/**
+ * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
+ *
+ * @param dai DAI to unregister
+ */
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
+{
+ mutex_lock(&client_mutex);
+ list_del(&dai->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered DAI '%s'\n", dai->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
+
+/**
+ * snd_soc_register_dais - Register multiple DAIs with the ASoC core
+ *
+ * @param dai Array of DAIs to register
+ * @param count Number of DAIs
+ */
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ ret = snd_soc_register_dai(&dai[i]);
+ if (ret != 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ for (i--; i >= 0; i--)
+ snd_soc_unregister_dai(&dai[i]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dais);
+
+/**
+ * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
+ *
+ * @param dai Array of DAIs to unregister
+ * @param count Number of DAIs
+ */
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ snd_soc_unregister_dai(&dai[i]);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
+
+/**
+ * snd_soc_register_platform - Register a platform with the ASoC core
+ *
+ * @param platform platform to register
+ */
+int snd_soc_register_platform(struct snd_soc_platform *platform)
+{
+ if (!platform->name)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&platform->list);
+
+ mutex_lock(&client_mutex);
+ list_add(&platform->list, &platform_list);
+ snd_soc_instantiate_cards();
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Registered platform '%s'\n", platform->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+
+/**
+ * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+ *
+ * @param platform platform to unregister
+ */
+void snd_soc_unregister_platform(struct snd_soc_platform *platform)
{
- printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
+ mutex_lock(&client_mutex);
+ list_del(&platform->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered platform '%s'\n", platform->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
+
+/**
+ * snd_soc_register_codec - Register a codec with the ASoC core
+ *
+ * @param codec codec to register
+ */
+int snd_soc_register_codec(struct snd_soc_codec *codec)
+{
+ if (!codec->name)
+ return -EINVAL;
+
+ /* The device should become mandatory over time */
+ if (!codec->dev)
+ printk(KERN_WARNING "No device for codec %s\n", codec->name);
+
+ INIT_LIST_HEAD(&codec->list);
+
+ mutex_lock(&client_mutex);
+ list_add(&codec->list, &codec_list);
+ snd_soc_instantiate_cards();
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Registered codec '%s'\n", codec->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_codec);
+
+/**
+ * snd_soc_unregister_codec - Unregister a codec from the ASoC core
+ *
+ * @param codec codec to unregister
+ */
+void snd_soc_unregister_codec(struct snd_soc_codec *codec)
+{
+ mutex_lock(&client_mutex);
+ list_del(&codec->list);
+ mutex_unlock(&client_mutex);
+
+ pr_debug("Unregistered codec '%s'\n", codec->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+
+static int __init snd_soc_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_root = debugfs_create_dir("asoc", NULL);
+ if (IS_ERR(debugfs_root) || !debugfs_root) {
+ printk(KERN_WARNING
+ "ASoC: Failed to create debugfs directory\n");
+ debugfs_root = NULL;
+ }
+#endif
+
return platform_driver_register(&soc_driver);
}
-static void snd_soc_exit(void)
+static void __exit snd_soc_exit(void)
{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(debugfs_root);
+#endif
platform_driver_unregister(&soc_driver);
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7351db9606e4..8863eddbac02 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -37,7 +37,6 @@
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
-#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -67,17 +66,13 @@ static int dapm_status = 1;
module_param(dapm_status, int, 0);
MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
-static struct dentry *asoc_debugfs;
-
-static u32 pop_time;
-
-static void pop_wait(void)
+static void pop_wait(u32 pop_time)
{
if (pop_time)
schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
}
-static void pop_dbg(const char *fmt, ...)
+static void pop_dbg(u32 pop_time, const char *fmt, ...)
{
va_list args;
@@ -85,7 +80,7 @@ static void pop_dbg(const char *fmt, ...)
if (pop_time) {
vprintk(fmt, args);
- pop_wait();
+ pop_wait(pop_time);
}
va_end(args);
@@ -230,10 +225,11 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
change = old != new;
if (change) {
- pop_dbg("pop test %s : %s in %d ms\n", widget->name,
- widget->power ? "on" : "off", pop_time);
+ pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
+ widget->name, widget->power ? "on" : "off",
+ codec->pop_time);
snd_soc_write(codec, widget->reg, new);
- pop_wait();
+ pop_wait(codec->pop_time);
}
pr_debug("reg %x old %x new %x change %d\n", widget->reg,
old, new, change);
@@ -293,7 +289,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
struct snd_soc_dapm_widget *w)
{
int i, ret = 0;
- char name[32];
+ size_t name_len;
struct snd_soc_dapm_path *path;
/* add kcontrol */
@@ -307,11 +303,16 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
continue;
/* add dapm control with long name */
- snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
- path->long_name = kstrdup (name, GFP_KERNEL);
+ name_len = 2 + strlen(w->name)
+ + strlen(w->kcontrols[i].name);
+ path->long_name = kmalloc(name_len, GFP_KERNEL);
if (path->long_name == NULL)
return -ENOMEM;
+ snprintf(path->long_name, name_len, "%s %s",
+ w->name, w->kcontrols[i].name);
+ path->long_name[name_len - 1] = '\0';
+
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
path->long_name);
ret = snd_ctl_add(codec->card, path->kcontrol);
@@ -821,23 +822,9 @@ static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
int snd_soc_dapm_sys_add(struct device *dev)
{
- int ret = 0;
-
if (!dapm_status)
return 0;
-
- ret = device_create_file(dev, &dev_attr_dapm_widget);
- if (ret != 0)
- return ret;
-
- asoc_debugfs = debugfs_create_dir("asoc", NULL);
- if (!IS_ERR(asoc_debugfs) && asoc_debugfs)
- debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs,
- &pop_time);
- else
- asoc_debugfs = NULL;
-
- return 0;
+ return device_create_file(dev, &dev_attr_dapm_widget);
}
static void snd_soc_dapm_sys_remove(struct device *dev)
@@ -845,9 +832,6 @@ static void snd_soc_dapm_sys_remove(struct device *dev)
if (dapm_status) {
device_remove_file(dev, &dev_attr_dapm_widget);
}
-
- if (asoc_debugfs)
- debugfs_remove_recursive(asoc_debugfs);
}
/* free all dapm widgets and resources */
@@ -1007,28 +991,6 @@ err:
}
/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * This function has been deprecated in favour of snd_soc_dapm_add_routes().
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
- const char *control, const char *source)
-{
- return snd_soc_dapm_add_route(codec, sink, control, source);
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
-
-/**
* snd_soc_dapm_add_routes - Add routes between DAPM widgets
* @codec: codec
* @route: audio routes
@@ -1358,8 +1320,12 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
for (i = 0; i < num; i++) {
ret = snd_soc_dapm_new_control(codec, widget);
- if (ret < 0)
+ if (ret < 0) {
+ printk(KERN_ERR
+ "ASoC: Failed to create DAPM control %s: %d\n",
+ widget->name, ret);
return ret;
+ }
widget++;
}
return 0;
@@ -1440,11 +1406,11 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
enum snd_soc_bias_level level)
{
struct snd_soc_codec *codec = socdev->codec;
- struct snd_soc_machine *machine = socdev->machine;
+ struct snd_soc_card *card = socdev->card;
int ret = 0;
- if (machine->set_bias_level)
- ret = machine->set_bias_level(machine, level);
+ if (card->set_bias_level)
+ ret = card->set_bias_level(card, level);
if (ret == 0 && codec->set_bias_level)
ret = codec->set_bias_level(codec, level);
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 10ba4218161b..2b302bbffe73 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <sound/core.h>
#ifdef CONFIG_SOUND_OSS_CORE
static int __init init_oss_soundcore(void);
diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c
index 798ca124da58..ccd763dd7167 100644
--- a/sound/usb/caiaq/caiaq-control.c
+++ b/sound/usb/caiaq/caiaq-control.c
@@ -247,69 +247,56 @@ static struct caiaq_controller a8dj_controller[] = {
{ "Software lock", 40 }
};
-int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+static int __devinit add_controls(struct caiaq_controller *c, int num,
+ struct snd_usb_caiaqdev *dev)
{
- int i;
+ int i, ret;
struct snd_kcontrol *kc;
+ for (i = 0; i < num; i++, c++) {
+ kcontrol_template.name = c->name;
+ kcontrol_template.private_value = c->index;
+ kc = snd_ctl_new1(&kcontrol_template, dev);
+ ret = snd_ctl_add(dev->chip.card, kc);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
+{
+ int ret = 0;
+
switch (dev->chip.usb_id) {
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1):
- for (i = 0; i < ARRAY_SIZE(ak1_controller); i++) {
- struct caiaq_controller *c = ak1_controller + i;
- kcontrol_template.name = c->name;
- kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- snd_ctl_add(dev->chip.card, kc);
- }
-
+ ret = add_controls(ak1_controller,
+ ARRAY_SIZE(ak1_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
- for (i = 0; i < ARRAY_SIZE(rk2_controller); i++) {
- struct caiaq_controller *c = rk2_controller + i;
- kcontrol_template.name = c->name;
- kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- snd_ctl_add(dev->chip.card, kc);
- }
-
+ ret = add_controls(rk2_controller,
+ ARRAY_SIZE(rk2_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3):
- for (i = 0; i < ARRAY_SIZE(rk3_controller); i++) {
- struct caiaq_controller *c = rk3_controller + i;
- kcontrol_template.name = c->name;
- kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- snd_ctl_add(dev->chip.card, kc);
- }
-
+ ret = add_controls(rk3_controller,
+ ARRAY_SIZE(rk3_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
- for (i = 0; i < ARRAY_SIZE(kore_controller); i++) {
- struct caiaq_controller *c = kore_controller + i;
- kcontrol_template.name = c->name;
- kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- snd_ctl_add(dev->chip.card, kc);
- }
-
+ ret = add_controls(kore_controller,
+ ARRAY_SIZE(kore_controller), dev);
break;
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
- for (i = 0; i < ARRAY_SIZE(a8dj_controller); i++) {
- struct caiaq_controller *c = a8dj_controller + i;
- kcontrol_template.name = c->name;
- kcontrol_template.private_value = c->index;
- kc = snd_ctl_new1(&kcontrol_template, dev);
- snd_ctl_add(dev->chip.card, kc);
- }
-
+ ret = add_controls(a8dj_controller,
+ ARRAY_SIZE(a8dj_controller), dev);
break;
}
- return 0;
+ return ret;
}
diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c
index 83175083e50f..b143ef7152f7 100644
--- a/sound/usb/caiaq/caiaq-device.c
+++ b/sound/usb/caiaq/caiaq-device.c
@@ -42,7 +42,7 @@
#endif
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.8");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.9");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, RigKontrol3},"
diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
index 5962e4b84423..6d9f9b135c62 100644
--- a/sound/usb/usbmidi.c
+++ b/sound/usb/usbmidi.c
@@ -880,7 +880,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream,
snd_rawmidi_transmit_ack(substream, 1);
return;
}
- tasklet_hi_schedule(&port->ep->tasklet);
+ tasklet_schedule(&port->ep->tasklet);
}
}
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index ff23cc1ce3b9..70b96355ca4c 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -276,7 +276,8 @@ static void subs_set_complete(struct urb **urbs, void (*complete)(struct urb *))
}
}
-int usb_stream_prepare_playback(struct usb_stream_kernel *sk, struct urb *inurb)
+static int usb_stream_prepare_playback(struct usb_stream_kernel *sk,
+ struct urb *inurb)
{
struct usb_stream *s = sk->s;
struct urb *io;
diff --git a/usr/gen_init_cpio.c b/usr/gen_init_cpio.c
index 7abc07f0fcd2..f1d3fe34176a 100644
--- a/usr/gen_init_cpio.c
+++ b/usr/gen_init_cpio.c
@@ -370,6 +370,30 @@ error:
return rc;
}
+static char *cpio_replace_env(char *new_location)
+{
+ char expanded[PATH_MAX + 1];
+ char env_var[PATH_MAX + 1];
+ char *start;
+ char *end;
+
+ for (start = NULL; (start = strstr(new_location, "${")); ) {
+ end = strchr(start, '}');
+ if (start < end) {
+ *env_var = *expanded = '\0';
+ strncat(env_var, start + 2, end - start - 2);
+ strncat(expanded, new_location, start - new_location);
+ strncat(expanded, getenv(env_var), PATH_MAX);
+ strncat(expanded, end + 1, PATH_MAX);
+ strncpy(new_location, expanded, PATH_MAX);
+ } else
+ break;
+ }
+
+ return new_location;
+}
+
+
static int cpio_mkfile_line(const char *line)
{
char name[PATH_MAX + 1];
@@ -415,7 +439,8 @@ static int cpio_mkfile_line(const char *line)
} else {
dname = name;
}
- rc = cpio_mkfile(dname, location, mode, uid, gid, nlinks);
+ rc = cpio_mkfile(dname, cpio_replace_env(location),
+ mode, uid, gid, nlinks);
fail:
if (dname_len) free(dname);
return rc;
@@ -439,6 +464,7 @@ void usage(const char *prog)
"\n"
"<name> name of the file/dir/nod/etc in the archive\n"
"<location> location of the file in the current filesystem\n"
+ " expands shell variables quoted with ${}\n"
"<target> link target\n"
"<mode> mode/permissions of the file\n"
"<uid> user id (0=root)\n"